From 3c9dcaf742cfa3e3c2b0843c8752cf7713aaf1fc Mon Sep 17 00:00:00 2001
From: lefengyang <lefengyang@tencent.com>
Date: Wed, 10 Sep 2025 19:38:41 +0800
Subject: [PATCH] init

---
 vendor/pimple/pimple/ext/pimple/tests/014.phpt                                                                      |   30 
 vendor/symfony/var-dumper/Caster/ConstStub.php                                                                      |   33 
 vendor/topthink/framework/src/think/route/Resource.php                                                              |  251 
 vendor/symfony/translation/Tests/fixtures/resources-2.0-multi-segment-unit.xlf                                      |   17 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/UpdateDenyConfigRequest.php                          |  170 
 vendor/symfony/translation/Tests/TranslatorCacheTest.php                                                            |  318 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/UpdatePullToPushTaskResponse.php                    |   75 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/no/functions                                  |  416 
 vendor/symfony/translation/Tests/Formatter/MessageFormatterTest.php                                                 |   83 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnData.php                                                 |   27 
 vendor/topthink/framework/tests/ContainerTest.php                                                                   |  314 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/br/functions                               |  408 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Sample.php                                                |  229 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodSpace.php                                      |    0 
 vendor/songshenzong/support/LICENSE.txt                                                                             |   19 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/RelaySourceConfigList.php                           |   65 
 vendor/psr/http-client/LICENSE                                                                                      |   19 
 vendor/topthink/framework/src/think/cache/TagSet.php                                                                |  132 
 vendor/topthink/think-migration/src/Migrator.php                                                                    |   27 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoCreateCdnPreloadTask.php                                            |   28 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php                                       |  138 
 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.php                          |   12 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/ListDomainDetailRequest.php                          |  309 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoStartWorkflow.php                                                   |   31 
 vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Client.php                                                                  |  250 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/hu/config                                     |   23 
 vendor/qcloud/cos-sdk-v5/sample/deleteBucketCors.php                                                                |   25 
 vendor/qiniu/php-sdk/examples/put_bucketAccessStyleMode.php                                                         |   21 
 vendor/topthink/framework/src/think/console/command/make/stubs/event.stub                                           |    8 
 vendor/topthink/think-orm/src/db/builder/Sqlsrv.php                                                                 |  184 
 vendor/xaboy/form-builder/src/UI/Elm/Traits/CascaderFactoryTrait.php                                                |   80 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateMediaPublishStatus.php                                        |   28 
 vendor/symfony/translation/LICENSE                                                                                  |   19 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListCdnAccessLogResponse.php                      |  119 
 vendor/symfony/translation/Tests/fixtures/non-valid.yml                                                             |    1 
 vendor/topthink/framework/tests/AppTest.php                                                                         |  215 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeCDNSnapshotHistory.php                                     |   34 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoQueryUploadTaskInfo.php                                             |   27 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoDrm.php                                                             |   50 
 workerman.bat                                                                                                       |   11 
 vendor/symfony/translation/MessageCatalogue.php                                                                     |  301 
 vendor/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php                                   |  168 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowIterator.php                                        |  167 
 vendor/symfony/var-dumper/Cloner/ClonerInterface.php                                                                |   27 
 vendor/topthink/think-migration/src/helper.php                                                                      |   40 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php                               |  200 
 vendor/topthink/think-orm/src/db/builder/Sqlite.php                                                                 |   97 
 vendor/qcloud/cos-sdk-v5/sample/listObjects.php                                                                     |   30 
 vendor/symfony/event-dispatcher/CHANGELOG.md                                                                        |   23 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS.php                                               |  236 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/DenyConfigDetail.php                                |  337 
 vendor/topthink/think-migration/src/command/migrate/Breakpoint.php                                                  |   92 
 vendor/topthink/think-queue/LICENSE                                                                                 |  201 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/GetDirectEditResult.php                              |  269 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnUpperIp.php                                              |   19 
 vendor/symfony/http-foundation/Tests/Session/Attribute/AttributeBagTest.php                                         |  186 
 vendor/symfony/translation/Loader/JsonFileLoader.php                                                                |   64 
 vendor/topthink/framework/src/think/console/command/make/stubs/service.stub                                         |   27 
 vendor/topthink/think-multi-app/src/MultiApp.php                                                                    |  245 
 vendor/symfony/translation/Tests/Catalogue/AbstractOperationTest.php                                                |   74 
 vendor/symfony/http-foundation/HeaderBag.php                                                                        |  331 
 vendor/topthink/think-captcha/src/Captcha.php                                                                       |  339 
 vendor/symfony/process/CHANGELOG.md                                                                                 |   90 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoBase.php                                                           |   12 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoCreatePullToPushTask.php                                           |   35 
 vendor/topthink/framework/src/think/console/output/question/Choice.php                                              |  163 
 vendor/xaboy/form-builder/src/components/FormStyle.php                                                              |   78 
 vendor/volcengine/volc-sdk-php/examples/Imp/job/DemoRetrieveJob.php                                                 |   32 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDomainInstanceInfo.php                            |  167 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetAudioInfoForAudit.php                                            |   28 
 vendor/psr/simple-cache/.editorconfig                                                                               |   12 
 vendor/topthink/think-orm/src/model/concern/ModelEvent.php                                                          |   88 
 vendor/topthink/framework/src/think/console/command/optimize/Route.php                                              |   66 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/ForbidStreamRequest.php                              |  217 
 vendor/topthink/framework/src/think/exception/InvalidArgumentException.php                                          |   22 
 vendor/workerman/workerman/composer.json                                                                            |   38 
 vendor/xaboy/form-builder/src/UI/Iview/Components/Option.php                                                        |   56 
 vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php                             |  154 
 vendor/topthink/framework/src/think/facade/Event.php                                                                |   42 
 vendor/symfony/http-foundation/File/MimeType/ExtensionGuesser.php                                                   |   94 
 vendor/topthink/framework/src/think/facade/Validate.php                                                             |   95 
 vendor/topthink/framework/src/think/Facade.php                                                                      |   98 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCommitData.php                                    |   75 
 vendor/symfony/polyfill-php70/Resources/stubs/TypeError.php                                                         |    5 
 vendor/xaboy/form-builder/composer.json                                                                             |   36 
 vendor/psr/cache/src/InvalidArgumentException.php                                                                   |   13 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/WriterPart.php                                        |   33 
 vendor/qiniu/php-sdk/src/Qiniu/Storage/ArgusManager.php                                                             |   73 
 vendor/topthink/think-queue/src/queue/command/Work.php                                                              |  151 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/RelaySourceGroupItemV2.php                          |  133 
 vendor/symfony/event-dispatcher/.gitignore                                                                          |    3 
 vendor/psr/http-client/README.md                                                                                    |   12 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeEdgeTopNrtData.php                                          |   25 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateSubtitleInfoResponse.php                    |   75 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetAuditFramesForAuditResponse.php                |  119 
 vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/NoSuchUploadException.php                                         |    8 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetPlayInfoWithLiveTimeShiftSceneResult.php       |   65 
 vendor/symfony/translation/IdentityTranslator.php                                                                   |   61 
 vendor/topthink/framework/src/think/console/command/make/Validate.php                                               |   39 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetDirectEditProgressResponse.php                 |  119 
 vendor/symfony/polyfill-intl-normalizer/composer.json                                                               |   39 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php                                              |  106 
 vendor/topthink/think-queue/src/queue/connector/Redis.php                                                           |  298 
 vendor/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php                                          |   72 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeriesValues.php                                       |  397 
 vendor/xaboy/form-builder/src/Annotation/ClassName.php                                                              |   39 
 vendor/qiniu/php-sdk/examples/put_referAntiLeech.php                                                                |   23 
 vendor/qcloud/cos-sdk-v5/sample/upload.php                                                                          |   49 
 vendor/topthink/framework/src/think/Middleware.php                                                                  |  257 
 vendor/topthink/framework/src/think/facade/Lang.php                                                                 |   41 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCallbackAuthType.php                              |   52 
 vendor/xaboy/form-builder/src/UI/Elm/Traits/DatePickerFactoryTrait.php                                              |  141 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAdaptiveInfo.php                                  |  133 
 vendor/topthink/think-orm/src/db/builder/Pgsql.php                                                                  |  118 
 vendor/topthink/think-orm/src/db/BaseQuery.php                                                                      | 1282 
 vendor/topthink/framework/composer.json                                                                             |   54 
 vendor/symfony/http-foundation/composer.json                                                                        |   38 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer.php                   |   34 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/Matrix.php                                           | 1202 
 vendor/symfony/translation/DependencyInjection/TranslationExtractorPass.php                                         |   49 
 vendor/songshenzong/support/CHANGELOG.md                                                                            |   24 
 vendor/topthink/think-migration/phinx/src/Phinx/Migration/CreationInterface.php                                     |   94 
 vendor/symfony/var-dumper/Tests/Caster/MemcachedCasterTest.php                                                      |   93 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoListSnapshots.php                                                   |   27 
 vendor/symfony/http-foundation/Tests/RequestMatcherTest.php                                                         |  151 
 vendor/topthink/think-filesystem/tests/bootstrap.php                                                                |    2 
 vendor/pimple/pimple/ext/pimple/pimple_compat.h                                                                     |   81 
 vendor/topthink/think-queue/src/common.php                                                                          |   31 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDomainoInfo.php                                   |  347 
 vendor/topthink/framework/src/think/validate/ValidateRule.php                                                       |  172 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodCreateCdnRefreshTaskRequest.php                    |  133 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUploadMaterialRequest.php                          |  235 
 vendor/xaboy/form-builder/src/Annotation/Validate/Required.php                                                      |   38 
 vendor/topthink/framework/src/think/console/command/Lists.php                                                       |   74 
 vendor/topthink/framework/src/think/console/command/Make.php                                                        |   99 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/cs/config                                     |   23 
 vendor/topthink/framework/src/think/console/LICENSE                                                                 |   19 
 vendor/topthink/think-captcha/assets/ttfs/4.ttf                                                                     |    0 
 vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoUnbindAXN.php                                              |   17 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListCdnUsageDataRequest.php                        |  235 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/it/functions                                  |  416 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Date.php                                                  |  483 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodPoint.php                                         |   99 
 vendor/pimple/pimple/ext/pimple/tests/005.phpt                                                                      |   27 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUrlUploadJsonRequest.php                           |   99 
 vendor/symfony/var-dumper/Caster/CutStub.php                                                                        |   59 
 vendor/qiniu/php-sdk/docs/rtc/README.md                                                                             |   71 
 vendor/symfony/translation/Tests/DataCollectorTranslatorTest.php                                                    |  116 
 vendor/xaboy/form-builder/src/UI/Elm/Traits/CheckBoxFactoryTrait.php                                                |   31 
 vendor/symfony/translation-contracts/TranslatorInterface.php                                                        |   65 
 vendor/symfony/var-dumper/Caster/PgSqlCaster.php                                                                    |  154 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/StringHelper.php                                          |  722 
 vendor/qiniu/php-sdk/examples/rs_batch_move.php                                                                     |   36 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListCdnTasksRequest.php                            |  337 
 vendor/topthink/think-captcha/README.md                                                                             |   57 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/WorkflowExecution.php                                |  581 
 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/StrictSessionHandlerTest.php                           |  189 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoSnapshotAuditPreset.php                                            |   51 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoStopCdnDomain.php                                                   |   19 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/cs/functions                                  |  416 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php                                      |  169 
 vendor/topthink/framework/src/think/route/RuleName.php                                                              |  211 
 vendor/psr/http-message/src/RequestInterface.php                                                                    |  129 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Layout.php                                                 |  481 
 vendor/qcloud/cos-sdk-v5/sample/catchException.php                                                                  |   30 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeCDNSnapshotHistoryResponse.php              |  119 
 vendor/psr/http-client/src/ClientExceptionInterface.php                                                             |   10 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx.php                                                  |  538 
 vendor/topthink/think-captcha/src/CaptchaService.php                                                                |   21 
 vendor/xaboy/form-builder/src/Json.php                                                                              |   38 
 vendor/xaboy/form-builder/src/UI/Elm/Traits/InputNumberFactoryTrait.php                                             |   32 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/Root.php                                          |  426 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDeleteVideoClassificationRequest.php               |   85 
 vendor/symfony/translation/MessageSelector.php                                                                      |   98 
 vendor/topthink/think-image/src/Image.php                                                                           |  610 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoListPullToPushTask.php                                             |   29 
 vendor/symfony/translation/Tests/fixtures/resources-target-attributes.xlf                                           |   14 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeInfo.php                                                   |  198 
 vendor/qiniu/php-sdk/src/Qiniu/Storage/FormUploader.php                                                             |  137 
 vendor/xaboy/form-builder/src/UI/Elm/Traits/TreeFactoryTrait.php                                                    |   74 
 vendor/workerman/workerman/Protocols/Websocket.php                                                                  |  504 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Memory.php                                            |   79 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetPrivateDrmPlayAuthResponse.php                 |  119 
 vendor/topthink/framework/src/think/Lang.php                                                                        |  290 
 vendor/symfony/http-foundation/Tests/RedirectResponseTest.php                                                       |   97 
 vendor/symfony/translation/Dumper/IcuResFileDumper.php                                                              |  106 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Html.php                                                  | 1846 
 vendor/topthink/think-migration/phinx/src/Phinx/Migration/AbstractMigration.php                                     |  273 
 vendor/symfony/process/Exception/RuntimeException.php                                                               |   21 
 vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php                                            |   23 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF8.php                                       |   81 
 vendor/songshenzong/support/src/BashEcho.php                                                                        |   72 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/LogoOverride.php                                     |   99 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/ListVQScoreTaskRequest.php                           |  201 
 vendor/symfony/translation/Tests/fixtures/resources-2.0.xlf                                                         |   25 
 vendor/xaboy/form-builder/src/Rule/ChildrenRule.php                                                                 |   97 
 vendor/symfony/var-dumper/Tests/Dumper/HtmlDumperTest.php                                                           |  163 
 vendor/topthink/think-orm/src/db/Fetch.php                                                                          |  494 
 vendor/xaboy/form-builder/src/UI/Elm/Components/Popover.php                                                         |   55 
 vendor/topthink/framework/src/think/console/command/make/Model.php                                                  |   36 
 vendor/xaboy/form-builder/src/Annotation/Col.php                                                                    |   45 
 vendor/qiniu/php-sdk/examples/upload_and_callback.php                                                               |   34 
 vendor/qiniu/php-sdk/examples/pulpvideo.php                                                                         |   55 
 vendor/topthink/think-queue/src/queue/CallQueuedHandler.php                                                         |   36 
 vendor/psr/http-client/src/ClientInterface.php                                                                      |   20 
 vendor/symfony/psr-http-message-bridge/composer.json                                                                |   42 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/JpGraph.php                                       |  870 
 vendor/topthink/framework/src/think/console/command/make/Middleware.php                                             |   36 
 vendor/xaboy/form-builder/src/Factory/Elm.php                                                                       |  154 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodRetrieveTranscodeResultRequest.php                 |   99 
 vendor/xaboy/form-builder/src/traits/form/FormInputTrait.php                                                        |  113 
 vendor/symfony/translation/Tests/fixtures/resources.yml                                                             |    1 
 vendor/symfony/polyfill-php70/Resources/stubs/AssertionError.php                                                    |    5 
 vendor/xaboy/form-builder/src/components/InputNumber.php                                                            |   84 
 vendor/symfony/translation/Tests/LoggingTranslatorTest.php                                                          |   68 
 vendor/volcengine/volc-sdk-php/examples/ImageX/DemoDeleteImages.php                                                 |   16 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateMediaInfoRequest.php                         |  420 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDeleteMediaResponse.php                           |  119 
 vendor/xaboy/form-builder/src/UI/Elm/Components/Button.php                                                          |   51 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/WriterPart.php                                       |   33 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDeleteTranscodesData.php                          |   65 
 vendor/psr/container/README.md                                                                                      |    5 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetAppInfo.php                                                      |   27 
 vendor/topthink/think-orm/src/model/relation/HasOne.php                                                             |  300 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoUpdateCdnConfig.php                                                 |   36 
 vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php                                               |  398 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoPlay.php                                                            |   47 
 vendor/symfony/translation/Tests/fixtures/extractor/translation.html.php                                            |   59 
 vendor/topthink/think-queue/.gitignore                                                                              |    4 
 vendor/topthink/think-image/tests/InfoTest.php                                                                      |   60 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeDenyConfigResponse.php                      |  119 
 vendor/xaboy/form-builder/src/UI/Iview/Components/Upload.php                                                        |  119 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoDeleteTranscodes.php                                                |   29 
 vendor/qiniu/php-sdk/examples/delete_bucketEvent.php                                                                |   21 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fi/functions                                  |  416 
 vendor/symfony/http-foundation/Tests/AcceptHeaderTest.php                                                           |  103 
 vendor/pimple/pimple/ext/pimple/config.m4                                                                           |   63 
 vendor/psr/log/Psr/Log/InvalidArgumentException.php                                                                 |    7 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoKillStream.php                                                     |   29 
 vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoUpgradeAXToAXB.php                                         |   19 
 vendor/ralouphie/getallheaders/src/getallheaders.php                                                                |   46 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageSetup.php                                          |  835 
 vendor/topthink/framework/src/think/Log.php                                                                         |  342 
 vendor/topthink/framework/src/think/Response.php                                                                    |  413 
 vendor/volcengine/volc-sdk-php/examples/.gitignore                                                                  |    1 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoSubmitPreloadTask.php                                               |   19 
 vendor/volcengine/volc-sdk-php/README.md                                                                            |   49 
 vendor/qiniu/php-sdk/examples/cdn_get_flux.php                                                                      |   34 
 vendor/topthink/think-queue/src/queue/Worker.php                                                                    |  414 
 vendor/xaboy/form-builder/src/UI/Iview/Components/Checkbox.php                                                      |   49 
 vendor/topthink/framework/src/think/service/PaginatorService.php                                                    |   52 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodApplyUploadInfoData.php                           |   75 
 vendor/symfony/translation/Tests/fixtures/messages.yml                                                              |    3 
 vendor/volcengine/volc-sdk-php/src/Service/Base/Models/Base/ResponseMetadata.php                                    |  245 
 vendor/workerman/workerman/MIT-LICENSE.txt                                                                          |   21 
 vendor/xaboy/form-builder/src/components/Checkbox.php                                                               |   91 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListCdnAccessLogRequest.php                        |  167 
 vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/SQLiteAdapter.php                                        | 1136 
 vendor/workerman/channel/composer.json                                                                              |   12 
 vendor/topthink/think-captcha/assets/ttfs/2.ttf                                                                     |    0 
 vendor/topthink/framework/src/think/exception/ErrorException.php                                                    |   57 
 vendor/xaboy/form-builder/src/Contract/CustomComponentInterface.php                                                 |   25 
 vendor/symfony/translation/Writer/TranslationWriter.php                                                             |   90 
 vendor/symfony/var-dumper/Resources/js/htmlDescriptor.js                                                            |   10 
 vendor/topthink/think-image/src/image/gif/Gif.php                                                                   |   88 
 vendor/xaboy/form-builder/src/UI/Elm/Components/TimePicker.php                                                      |  111 
 vendor/xaboy/form-builder/src/UI/Elm/Style/Col.php                                                                  |  166 
 vendor/symfony/http-foundation/File/Stream.php                                                                      |   28 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/ListPullToPushTaskResponse.php                      |  119 
 vendor/symfony/psr-http-message-bridge/.travis.yml                                                                  |   47 
 vendor/symfony/var-dumper/Caster/ReflectionCaster.php                                                               |  388 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeVQScoreTask.php                                            |   27 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/config                                     |   24 
 vendor/topthink/think-view/README.md                                                                                |   36 
 vendor/xaboy/form-builder/src/Annotation/Validate/Enum.php                                                          |   40 
 vendor/symfony/translation/Tests/fixtures/resources-notes-meta.xlf                                                  |   26 
 vendor/xaboy/form-builder/src/UI/Iview/Components/TimePicker.php                                                    |  115 
 vendor/topthink/think-migration/src/command/seed/Run.php                                                            |  107 
 vendor/qcloud/cos-sdk-v5/sample/deleteBucket.php                                                                    |   25 
 vendor/xaboy/form-builder/src/traits/form/FormColorPickerTrait.php                                                  |   32 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/Quality.php                                          |  119 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnRegionAndIsp.php                                         |   19 
 vendor/symfony/polyfill-intl-idn/Resources/unidata/disallowed_STD3_valid.php                                        |   71 
 vendor/symfony/translation/Extractor/ChainExtractor.php                                                             |   60 
 vendor/topthink/framework/src/think/initializer/Error.php                                                           |  117 
 vendor/psr/http-factory/README.md                                                                                   |   10 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodMediaInfo.php                                     |  153 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Xlfn.php                                             |  159 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AdvancedValueBinder.php                                     |  174 
 vendor/topthink/framework/src/think/facade/Env.php                                                                  |   44 
 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php                        |  145 
 vendor/volcengine/volc-sdk-php/examples/Rtc/rtc-openapi-demo/Rtc/Rtc.php                                            |   83 
 vendor/spatie/macroable/README.md                                                                                   |  130 
 vendor/symfony/translation/Tests/fixtures/resources.po                                                              |   24 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/SnapshotManage.php                               |    0 
 vendor/pimple/pimple/src/Pimple/Exception/ExpectedInvokableException.php                                            |   38 
 vendor/topthink/think-captcha/assets/bgs/7.jpg                                                                      |    0 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/ListPullToPushTaskRequest.php                        |  133 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Upload/OptionInfo.php                                                |   14 
 vendor/volcengine/volc-sdk-php/src/Service/Cdn.php                                                                  |  733 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateSpaceResponse.php                           |   75 
 vendor/xaboy/form-builder/src/UI/Elm/Traits/RadioFactoryTrait.php                                                   |   32 
 vendor/symfony/http-foundation/File/File.php                                                                        |  138 
 vendor/topthink/framework/src/think/route/Dispatch.php                                                              |  264 
 vendor/xaboy/form-builder/src/components/Row.php                                                                    |   67 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/CreateDomainRequest.php                              |  133 
 vendor/xaboy/form-builder/src/UI/Elm/Traits/ColorPickerFactoryTrait.php                                             |   32 
 vendor/xaboy/form-builder/src/traits/form/FormHiddenTrait.php                                                       |   31 
 vendor/symfony/translation/Loader/YamlFileLoader.php                                                                |   50 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/BaseDrawing.php                                        |  532 
 vendor/symfony/translation/Tests/DependencyInjection/fixtures/ServiceProperties.php                                 |    8 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls.php                                                   |  946 
 vendor/topthink/think-migration/phinx/src/Phinx/Seed/SeedInterface.php                                              |  166 
 vendor/topthink/framework/src/think/console/command/optimize/Schema.php                                             |  104 
 vendor/xaboy/form-builder/src/Driver/FormComponent.php                                                              |   69 
 vendor/pimple/pimple/ext/pimple/tests/012.phpt                                                                      |   28 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeRecordTaskFileHistoryResponse.php           |  119 
 vendor/symfony/http-foundation/IpUtils.php                                                                          |  156 
 vendor/volcengine/volc-sdk-php/src/Service/Live/LiveOption.php                                                      |  272 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateSubtitleInfoRequest.php                      |  309 
 vendor/xaboy/form-builder/src/UI/Elm/Components/Select.php                                                          |   99 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/SingularValueDecomposition.php                       |  528 
 vendor/symfony/polyfill-intl-idn/Resources/unidata/Regex.php                                                        |   24 
 vendor/symfony/translation/Tests/Dumper/QtFileDumperTest.php                                                        |   48 
 vendor/topthink/think-migration/src/command/migrate/Create.php                                                      |   55 
 vendor/qcloud_sts/qcloud-sts-sdk/LICENSE                                                                            |   21 
 vendor/psr/http-message/CHANGELOG.md                                                                                |   36 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Iterator.php                                           |   85 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataType.php                                                |   85 
 vendor/symfony/http-foundation/File/MimeType/ExtensionGuesserInterface.php                                          |   27 
 vendor/symfony/http-foundation/Exception/ConflictingHeadersException.php                                            |   21 
 vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/SqlServerAdapter.php                                     | 1165 
 vendor/topthink/think-orm/README.md                                                                                 |   27 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Thumbnails.php                                        |   20 
 vendor/topthink/framework/src/think/Http.php                                                                        |  288 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodCallback.php                                   |    0 
 vendor/psr/http-client/src/RequestExceptionInterface.php                                                            |   24 
 vendor/xaboy/form-builder/src/FormHandle.php                                                                        |  153 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/ValuePair.php                                        |   99 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Vod.php                                                              | 2208 
 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_max_age.php                                |   10 
 vendor/topthink/think-image/tests/autoload.php                                                                      |   15 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetAppInfoResult.php                              |  167 
 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/common.inc                                    |  151 
 vendor/symfony/translation/Tests/fixtures/resources.dump.json                                                       |    1 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/PlotArea.php                                               |  111 
 vendor/topthink/framework/src/think/console/command/make/Service.php                                                |   36 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/GeneratePlayURLResult.php                           |   65 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodSetCallbackEventRequest.php                        |  167 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetDirectEditProgressRequest.php                   |   58 
 vendor/xaboy/form-builder/src/UI/Iview/Components/DatePicker.php                                                    |  125 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/SheetView.php                                          |  193 
 vendor/symfony/event-dispatcher/EventSubscriberInterface.php                                                        |   46 
 vendor/volcengine/volc-sdk-php/examples/ImageX/DemoUpdateImageUrls.php                                              |   16 
 vendor/volcengine/volc-sdk-php/examples/VoiceNotify/CreateTtsResource.php                                           |   23 
 vendor/symfony/var-dumper/Cloner/Stub.php                                                                           |   67 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Chart.php                                                  |  663 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeOriginNrtDataSummary.php                                    |   24 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSnapshot.php                                      |  201 
 vendor/topthink/think-view/src/Think.php                                                                            |  263 
 vendor/xaboy/form-builder/images/demo03.jpg                                                                         |    0 
 vendor/qiniu/php-sdk/autoload.php                                                                                   |   14 
 vendor/topthink/think-orm/.gitignore                                                                                |    3 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListWorkflowExecutionRequest.php                   |  507 
 vendor/symfony/var-dumper/Caster/AmqpCaster.php                                                                     |  210 
 vendor/symfony/translation/Formatter/MessageFormatter.php                                                           |   79 
 vendor/topthink/framework/CONTRIBUTING.md                                                                           |  119 
 vendor/symfony/translation/MetadataAwareInterface.php                                                               |   54 
 vendor/symfony/psr-http-message-bridge/Tests/Functional/CovertTest.php                                              |  239 
 vendor/pimple/pimple/ext/pimple/tests/016.phpt                                                                      |   24 
 vendor/qiniu/php-sdk/examples/update_bucketEvent.php                                                                |   25 
 vendor/topthink/framework/src/think/response/View.php                                                               |  151 
 vendor/qcloud/cos-sdk-v5/sample/getBucketAcl.php                                                                    |   25 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoUpdatePullToPushTask.php                                           |   36 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoStopPullToPushTask.php                                             |   27 
 vendor/symfony/http-foundation/Tests/Session/Flash/FlashBagTest.php                                                 |  157 
 vendor/topthink/think-orm/src/paginator/driver/Bootstrap.php                                                        |  209 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoGeneratePlayURL.php                                                |   34 
 vendor/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php                                         |  159 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListCdnStatusDataRequest.php                       |  235 
 vendor/qcloud/cos-sdk-v5/phpunit.xml                                                                                |    7 
 vendor/symfony/var-dumper/composer.json                                                                             |   54 
 vendor/symfony/var-dumper/README.md                                                                                 |   15 
 vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php                                                         |   99 
 vendor/psr/container/src/ContainerExceptionInterface.php                                                            |   13 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/AddrScoreInfo.php                                   |   99 
 vendor/symfony/http-foundation/Request.php                                                                          | 2191 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/ResponseLive.php                                 |  120 
 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/invalid_cookie_name.php                           |   11 
 vendor/xaboy/form-builder/src/traits/form/FormTreeTrait.php                                                         |   73 
 vendor/symfony/var-dumper/Tests/Caster/SplCasterTest.php                                                            |  207 
 vendor/symfony/translation/Dumper/JsonFileDumper.php                                                                |   40 
 vendor/xaboy/form-builder/src/UI/Elm/Components/Group.php                                                           |   77 
 vendor/qiniu/php-sdk/examples/pfop_mkzip.php                                                                        |   43 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/CDNSnapshotHistory.php                              |  371 
 vendor/symfony/var-dumper/Tests/Fixtures/GeneratorDemo.php                                                          |   21 
 vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/UploadBodyListener.php                                                      |   79 
 vendor/symfony/var-dumper/Caster/TraceStub.php                                                                      |   36 
 vendor/symfony/http-foundation/RedirectResponse.php                                                                 |  109 
 vendor/topthink/think-queue/src/queue/job/Database.php                                                              |   91 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeClosedStreamInfoByPageResponse.php          |  119 
 vendor/psr/log/Psr/Log/AbstractLogger.php                                                                           |  128 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php                              |   90 
 vendor/volcengine/volc-sdk-php/examples/Sms/DemoSendSms.php                                                         |   26 
 vendor/symfony/translation/Tests/Catalogue/MergeOperationTest.php                                                   |   97 
 vendor/symfony/process/Exception/ExceptionInterface.php                                                             |   21 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListWorkflowExecutionResponse.php                 |  119 
 vendor/symfony/translation/Tests/Dumper/JsonFileDumperTest.php                                                      |   39 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter.php                                         |  857 
 vendor/volcengine/volc-sdk-php/Changelog                                                                            |  211 
 vendor/qiniu/php-sdk/src/Qiniu/Processing/PersistentFop.php                                                         |   94 
 vendor/volcengine/volc-sdk-php/examples/VEdit/DemoSubmitTemplateTask.php                                            |   38 
 vendor/symfony/var-dumper/Caster/FrameStub.php                                                                      |   30 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/VodOption.php                                                        |  630 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/ForbidStreamResponse.php                            |   75 
 vendor/topthink/think-orm/src/db/concern/AggregateQuery.php                                                         |  107 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetAutomaticSpeechRecognitionForAuditRequest.php   |   99 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetHlsDecryptionKeyResponse.php                   |  119 
 vendor/psr/log/Psr/Log/LoggerInterface.php                                                                          |  125 
 vendor/symfony/polyfill-php70/README.md                                                                             |   28 
 vendor/qiniu/php-sdk/examples/persistent_fop_init.php                                                               |   19 
 vendor/volcengine/volc-sdk-php/src/Service/Live/LiveUtils.php                                                       |   86 
 vendor/symfony/http-foundation/Tests/HeaderBagTest.php                                                              |  205 
 vendor/symfony/translation/Tests/fixtures/resources.php                                                             |    5 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php                                                  | 2054 
 vendor/symfony/http-foundation/Response.php                                                                         | 1308 
 vendor/symfony/translation-contracts/LocaleAwareInterface.php                                                       |   31 
 vendor/topthink/think-captcha/src/helper.php                                                                        |   52 
 vendor/xaboy/form-builder/src/UI/Elm/Components/Radio.php                                                           |   69 
 vendor/symfony/var-dumper/Caster/Caster.php                                                                         |  157 
 vendor/symfony/translation/Tests/Loader/QtFileLoaderTest.php                                                        |   79 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListVideoClassificationsRequest.php                |   85 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedRange.php                                                   |  235 
 vendor/workerman/workerman/Events/React/Base.php                                                                    |  262 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Styles.php                                            |   68 
 vendor/symfony/translation/Tests/fixtures/resources.mo                                                              |    0 
 vendor/topthink/think-queue/src/queue/InteractsWithTime.php                                                         |   65 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetHlsDecryptionKeyResult.php                     |   65 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSpriteSnapshot.php                                |  337 
 vendor/topthink/framework/src/think/cache/Driver.php                                                                |  359 
 vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php                                                   |  183 
 vendor/workerman/workerman/Events/React/ExtLibEventLoop.php                                                         |   27 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/StopPullToPushTaskResponse.php                      |   75 
 vendor/qiniu/php-sdk/examples/qetag.php                                                                             |   11 
 vendor/xaboy/form-builder/src/Form.php                                                                              |  535 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoSubmitUnblockTask.php                                               |   19 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoUploadSts2.php                                                      |   21 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAllPlayInfoModel.php                              |  415 
 vendor/workerman/workerman/Worker.php                                                                               | 2490 
 vendor/topthink/think-queue/src/queue/Service.php                                                                   |   50 
 vendor/spatie/macroable/config/.gitkeep                                                                             |    0 
 vendor/topthink/framework/src/think/Exception.php                                                                   |   60 
 vendor/workerman/workerman/Protocols/ProtocolInterface.php                                                          |   52 
 vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Copy.php                                                                    |  140 
 vendor/symfony/var-dumper/Resources/css/htmlDescriptor.css                                                          |  130 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IOFactory.php                                                    |  217 
 vendor/symfony/var-dumper/Dumper/ContextProvider/ContextProviderInterface.php                                       |   25 
 vendor/volcengine/volc-sdk-php/examples/BusinessSecurity/DemoRiskDetect.php                                         |   36 
 vendor/symfony/polyfill-intl-normalizer/bootstrap.php                                                               |   19 
 vendor/topthink/think-captcha/assets/bgs/5.jpg                                                                      |    0 
 vendor/symfony/psr-http-message-bridge/phpunit.xml.dist                                                             |   30 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetRecPosterData.php                              |   99 
 vendor/topthink/think-queue/src/facade/Queue.php                                                                    |   18 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodUpload.php                                     |    0 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodUrlUploadURLSet.php                               |  371 
 vendor/xaboy/form-builder/src/UI/Iview/Traits/ColorPickerFactoryTrait.php                                           |   32 
 vendor/topthink/framework/src/think/response/Html.php                                                               |   34 
 vendor/topthink/framework/src/think/route/dispatch/Url.php                                                          |  118 
 vendor/xaboy/form-builder/src/UI/Elm/Style/Row.php                                                                  |   90 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/da/config                                     |   25 
 vendor/qiniu/php-sdk/examples/rsf_v2list_bucket.php                                                                 |   33 
 vendor/topthink/framework/src/think/console/output/formatter/Stack.php                                              |  116 
 vendor/topthink/framework/src/think/exception/HttpException.php                                                     |   42 
 vendor/topthink/think-migration/src/db/Table.php                                                                    |  135 
 vendor/topthink/think-migration/phinx/CHANGELOG.md                                                                  |  370 
 vendor/symfony/translation/Dumper/DumperInterface.php                                                               |   31 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Parser.php                                            | 1438 
 vendor/workerman/workerman/Protocols/Text.php                                                                       |   70 
 vendor/symfony/http-foundation/File/Exception/FileNotFoundException.php                                             |   28 
 vendor/xaboy/form-builder/src/FormComponentDriver.php                                                               |  267 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnIterator.php                                     |  172 
 vendor/qiniu/php-sdk/examples/pfop_video_avthumb.php                                                                |   47 
 vendor/symfony/psr-http-message-bridge/Tests/Fixtures/ServerRequest.php                                             |  141 
 vendor/symfony/http-foundation/Tests/CookieTest.php                                                                 |  235 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsVBA.php                                          |   40 
 vendor/symfony/translation/LoggingTranslator.php                                                                    |  157 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php                                           |  282 
 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.expected                     |   10 
 vendor/xaboy/form-builder/src/Contract/FormHandleInterface.php                                                      |   11 
 vendor/qiniu/php-sdk/examples/rs_delete.php                                                                         |   17 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateMediaPublishStatusResponse.php              |   75 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/ForbiddenStreamInfoList.php                         |  277 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListCdnTopAccessUrlResponse.php                   |  119 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateVideoClassification.php                                       |   29 
 vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/BucketAlreadyExistsException.php                                  |    5 
 vendor/topthink/framework/src/think/console/output/Descriptor.php                                                   |  323 
 vendor/xaboy/form-builder/src/components/Slider.php                                                                 |  103 
 vendor/symfony/polyfill-mbstring/bootstrap.php                                                                      |   58 
 vendor/symfony/var-dumper/Caster/DOMCaster.php                                                                      |  302 
 vendor/topthink/think-migration/phinx/src/Phinx/Migration/MigrationInterface.php                                    |  215 
 vendor/workerman/workerman.log                                                                                      |  740 
 vendor/topthink/think-orm/src/model/concern/TimeStamp.php                                                           |  208 
 vendor/symfony/translation/Tests/fixtures/resources.csv                                                             |    4 
 vendor/volcengine/volc-sdk-php/src/Service/Visual.php                                                               |  173 
 vendor/topthink/framework/src/helper.php                                                                            |  664 
 vendor/songshenzong/support/phpunit.xml                                                                             |   35 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetWorkflowExecutionDetailResult.php              |  392 
 vendor/psr/log/Psr/Log/LogLevel.php                                                                                 |   18 
 vendor/qiniu/php-sdk/.travis.yml                                                                                    |   28 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoDescribeVodDomainTrafficData.php                                    |   31 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodCreateCdnPreloadTaskResponse.php                  |  119 
 vendor/qiniu/php-sdk/src/Qiniu/functions.php                                                                        |  264 
 vendor/xaboy/form-builder/src/Form/ElmForm.php                                                                      |   24 
 vendor/symfony/translation/Tests/DependencyInjection/fixtures/ServiceMethodCalls.php                                |   12 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/functions                                  |  408 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/hu/functions                                  |  416 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCommitUploadInfoResult.php                        |   75 
 vendor/xaboy/form-builder/src/UI/Iview/Style/Row.php                                                                |   90 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetSmartStrategyLitePlayInfoRequest.php            |  417 
 vendor/symfony/psr-http-message-bridge/LICENSE                                                                      |   19 
 vendor/pimple/pimple/ext/pimple/tests/019.phpt                                                                      |   18 
 vendor/symfony/polyfill-intl-idn/LICENSE                                                                            |   19 
 vendor/symfony/var-dumper/Caster/ResourceCaster.php                                                                 |   98 
 vendor/xaboy/form-builder/src/exception/FormBuilderException.php                                                    |   18 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/ClosedStreamInfo.php                                |   99 
 vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php                                                        |   91 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoRestartPullToPushTask.php                                          |   27 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoUpdateDenyConfig.php                                               |   30 
 vendor/volcengine/volc-sdk-php/examples/VoiceNotify/FetchResource.php                                               |   20 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Theme.php                                            |  837 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodURLSet.php                                        |  313 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeDomainRequest.php                            |   65 
 vendor/symfony/http-foundation/Tests/AcceptHeaderItemTest.php                                                       |  113 
 vendor/symfony/translation/Tests/fixtures/empty.mo                                                                  |    0 
 vendor/xaboy/form-builder/src/Annotation/Validate/Pattern.php                                                       |   40 
 vendor/topthink/think-migration/src/command/stubs/migrate.stub                                                      |   33 
 vendor/topthink/framework/src/think/route/RuleGroup.php                                                             |  523 
 vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoClick2Call.php                                             |   19 
 vendor/psr/http-factory/.gitignore                                                                                  |    2 
 vendor/psr/log/composer.json                                                                                        |   26 
 vendor/topthink/framework/src/think/console/command/make/stubs/subscribe.stub                                       |    8 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/GeneratePushURLRequest.php                           |  235 
 vendor/workerman/crontab/src/Crontab.php                                                                            |  177 
 vendor/topthink/think-queue/src/queue/job/Sync.php                                                                  |   66 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetPlayInfoWithLiveTimeShiftScene.php                               |   31 
 vendor/qiniu/php-sdk/src/Qiniu/Auth.php                                                                             |  187 
 vendor/symfony/process/Tests/PhpProcessTest.php                                                                     |   63 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetWorkflowExecutionDetailResponse.php            |  119 
 vendor/topthink/think-queue/tests/ListenerTest.php                                                                  |   80 
 vendor/xaboy/form-builder/src/UI/Iview/Components/Tooltip.php                                                       |   47 
 vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/ExceptionParser.php                                                         |  109 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Axis.php                                                   |  556 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/CellIterator.php                                       |   57 
 vendor/workerman/workerman/Lib/Constants.php                                                                        |   40 
 vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/fr.res                                                 |    0 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/GeneratePushURLResult.php                           |  201 
 vendor/topthink/think-orm/src/db/concern/ResultOperation.php                                                        |  247 
 vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/en.res                                                 |    0 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetMediaListRequest.php                            |  371 
 vendor/qcloud/cos-sdk-v5/sample/getBucketCors.php                                                                   |   25 
 vendor/symfony/translation/MessageCatalogueInterface.php                                                            |  138 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDeleteMediaData.php                               |   65 
 vendor/ralouphie/getallheaders/.travis.yml                                                                          |   18 
 vendor/symfony/translation/CHANGELOG.md                                                                             |  126 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer.php                                   |  175 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Escher.php                                            |  677 
 vendor/topthink/think-migration/phinx/src/Phinx/Util/Util.php                                                       |  190 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetSubtitleAuthToken.php                                            |   26 
 vendor/symfony/translation/Tests/fixtures/empty.po                                                                  |    0 
 vendor/topthink/framework/src/think/console/command/make/stubs/controller.api.stub                                  |   64 
 vendor/topthink/think-orm/src/db/connector/Sqlite.php                                                               |   96 
 vendor/topthink/think-queue/src/queue/command/Retry.php                                                             |   83 
 vendor/symfony/http-foundation/Session/Storage/Handler/WriteCheckSessionHandler.php                                 |   92 
 vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Response/ImpRetrieveJobResponse.php                           |   92 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Supervisor.php                                             |  117 
 vendor/qiniu/php-sdk/examples/upload_simple_file.php                                                                |   37 
 vendor/topthink/think-template/src/template/taglib/Cx.php                                                           |  715 
 vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/TablePrefixAdapter.php                                   |  272 
 vendor/qiniu/php-sdk/tests/Qiniu/Tests/ResumeUpTest.php                                                             |   60 
 vendor/symfony/translation/Extractor/AbstractFileExtractor.php                                                      |   80 
 vendor/volcengine/volc-sdk-php/examples/Visual/DemoOCR.php                                                          |   21 
 vendor/psr/log/Psr/Log/LoggerAwareTrait.php                                                                         |   26 
 vendor/symfony/var-dumper/Caster/ExceptionCaster.php                                                                |  357 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodUpdateSubtitleStatusData.php                      |   65 
 vendor/topthink/framework/src/think/console/command/make/Command.php                                                |   55 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodBetterFrameDataForAudit.php                       |  269 
 vendor/symfony/translation/Tests/PluralizationRulesTest.php                                                         |  124 
 vendor/symfony/http-foundation/Tests/JsonResponseTest.php                                                           |  266 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoListCdnTopAccessUrl.php                                             |   30 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDescribeVodDomainTrafficDataResult.php            |  220 
 vendor/xaboy/form-builder/src/UI/Elm/Traits/FrameFactoryTrait.php                                                   |  126 
 vendor/ralouphie/getallheaders/tests/GetAllHeadersTest.php                                                          |  121 
 vendor/topthink/think-orm/src/model/relation/HasManyThrough.php                                                     |  382 
 vendor/symfony/var-dumper/Tests/Dumper/FunctionsTest.php                                                            |   57 
 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.php                                   |   24 
 vendor/xaboy/form-builder/src/Response.php                                                                          |  107 
 vendor/xaboy/form-builder/src/UI/Iview/Traits/SelectFactoryTrait.php                                                |   48 
 vendor/volcengine/volc-sdk-php/.gitignore                                                                           |    9 
 vendor/qiniu/php-sdk/examples/README.md                                                                             |   10 
 vendor/symfony/var-dumper/Tests/Fixtures/FooInterface.php                                                           |   11 
 vendor/psr/log/LICENSE                                                                                              |   19 
 vendor/qcloud/cos-sdk-v5/sample/createBucket.php                                                                    |   25 
 vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php                                                                 |  146 
 vendor/qcloud_sts/qcloud-sts-sdk/composer.json                                                                      |   23 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/StageStatus.php                                      |   94 
 vendor/symfony/psr-http-message-bridge/CHANGELOG.md                                                                 |   18 
 vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/CurlException.php                                                 |    5 
 vendor/symfony/var-dumper/Dumper/CliDumper.php                                                                      |  643 
 vendor/topthink/think-migration/phinx/CONTRIBUTING.md                                                               |   66 
 vendor/symfony/translation/Tests/Loader/CsvFileLoaderTest.php                                                       |   61 
 vendor/topthink/framework/src/think/contract/ModelRelationInterface.php                                             |   99 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Exception.php                                                    |    7 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDescribeIpInfoResult.php                          |   58 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetAllPlayInfo.php                                                  |   44 
 vendor/xaboy/form-builder/README.md                                                                                 |  105 
 vendor/songshenzong/support/tests/bootstrap.php                                                                     |   11 
 vendor/xaboy/form-builder/src/traits/form/FormTimePickerTrait.php                                                   |   60 
 vendor/qiniu/php-sdk/examples/prefop.php                                                                            |   29 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/CreatePullToPushTaskRequest.php                      |  337 
 vendor/xaboy/form-builder/src/components/Cascader.php                                                               |  151 
 vendor/topthink/think-helper/src/contract/Arrayable.php                                                             |    8 
 vendor/xaboy/form-builder/src/UI/Elm/Components/Checkbox.php                                                        |   79 
 vendor/topthink/framework/tests/DbTest.php                                                                          |   49 
 vendor/psr/simple-cache/src/InvalidArgumentException.php                                                            |   13 
 vendor/topthink/framework/tests/HttpTest.php                                                                        |  155 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodCommitUploadInfoRequest.php                        |  167 
 vendor/qcloud/cos-sdk-v5/sample/createMultipartUpload.php                                                           |   39 
 vendor/symfony/var-dumper/Tests/Caster/PdoCasterTest.php                                                            |   64 
 vendor/symfony/http-foundation/Session/Flash/FlashBag.php                                                           |  152 
 vendor/symfony/polyfill-mbstring/LICENSE                                                                            |   19 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php                                       |  146 
 vendor/symfony/translation/phpunit.xml.dist                                                                         |   30 
 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.expected                       |   17 
 vendor/topthink/framework/src/think/exception/ClassNotFoundException.php                                            |   39 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeRelaySourceV2.php                                          |   27 
 vendor/volcengine/volc-sdk-php/examples/Visual/DemoGeneralSegment.php                                               |   13 
 vendor/topthink/think-image/tests/RotateTest.php                                                                    |   42 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnIpInfo.php                                     |  167 
 vendor/symfony/translation/Resources/schemas/xml.xsd                                                                |  309 
 vendor/pimple/pimple/ext/pimple/tests/009.phpt                                                                      |   13 
 vendor/topthink/framework/src/think/cache/driver/Wincache.php                                                       |  175 
 vendor/songshenzong/support/tests/Core/EchoBashTest.php                                                             |   29 
 vendor/symfony/psr-http-message-bridge/Tests/Fixtures/Uri.php                                                       |  135 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodTrafficData.php                                   |   92 
 vendor/symfony/translation/Tests/fixtures/empty-translation.mo                                                      |    0 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodVideoClassificationsData.php                      |   58 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnStatisticsCommonResult.php                     |   85 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Hyperlink.php                                               |  113 
 vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/GPBMetadata/ImpCommon.php                                     |   24 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/tr/functions                                  |  416 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Exception.php                                             |    9 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/CategoryTagInfo.php                                  |  201 
 vendor/symfony/var-dumper/Caster/DsPairStub.php                                                                     |   28 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/VodUtils.php                                                         |   56 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/ListDomainDetailResponse.php                        |  119 
 vendor/symfony/http-foundation/Tests/ResponseTestCase.php                                                           |   89 
 vendor/xaboy/form-builder/src/Annotation/Emit.php                                                                   |   47 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetSubtitleInfoList.php                                             |   38 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSourceInfo.php                                    |  541 
 vendor/pimple/pimple/ext/pimple/tests/010.phpt                                                                      |   45 
 vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/NoSuchKeyException.php                                            |    6 
 vendor/volcengine/volc-sdk-php/examples/Rtc/rtc-openapi-demo/README.md                                              |   34 
 vendor/symfony/translation/Extractor/PhpStringTokenParser.php                                                       |  142 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Column.php                                             |   64 
 vendor/topthink/think-image/src/image/gif/Encoder.php                                                               |  222 
 vendor/psr/http-client/src/NetworkExceptionInterface.php                                                            |   24 
 vendor/symfony/http-foundation/Tests/ParameterBagTest.php                                                           |  194 
 vendor/xaboy/form-builder/src/Template/form.php                                                                     |   16 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php                           |  205 
 vendor/xaboy/form-builder/src/UI/Iview/Traits/SwitchesFactoryTrait.php                                              |   32 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoDomain.php                                                         |   61 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetPlayInfoWithLiveTimeShiftSceneResponse.php     |  119 
 vendor/xaboy/form-builder/src/UI/Elm/Traits/GroupFactoryTrait.php                                                   |   33 
 vendor/symfony/var-dumper/Caster/PdoCaster.php                                                                      |  120 
 vendor/qcloud/cos-sdk-v5/sample/getObject.php                                                                       |   36 
 vendor/topthink/framework/src/think/event/LogWrite.php                                                              |   31 
 vendor/pimple/pimple/src/Pimple/Container.php                                                                       |  298 
 vendor/volcengine/volc-sdk-php/src/Service/Imp/ImpOption.php                                                        |   70 
 vendor/volcengine/volc-sdk-php/examples/VoiceNotify/CreateTask.php                                                  |   30 
 vendor/symfony/http-foundation/Tests/File/Fixtures/other-file.example                                               |    0 
 vendor/symfony/http-foundation/Tests/ServerBagTest.php                                                              |  170 
 vendor/xaboy/form-builder/src/UI/Iview/Traits/InputFactoryTrait.php                                                 |  112 
 vendor/psr/http-message/src/MessageInterface.php                                                                    |  187 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php                             |  445 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/KillStreamResponse.php                              |   75 
 vendor/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php                                    |  255 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoDescribeIpInfo.php                                                  |   27 
 vendor/qiniu/php-sdk/src/Qiniu/Http/Error.php                                                                       |   35 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetPlayInfoRequest.php                             |  587 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher.php                                                |   64 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/CreateDomainResponse.php                            |   75 
 vendor/symfony/translation/Tests/fixtures/resources-multi-files.xlf                                                 |   27 
 vendor/topthink/think-orm/src/db/connector/Mysql.php                                                                |  162 
 vendor/workerman/crontab/README.md                                                                                  |   29 
 vendor/xaboy/form-builder/src/UI/Elm/Components/Option.php                                                          |   56 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/CreateVQScoreTaskResponse.php                       |  119 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Alignment.php                                              |  462 
 vendor/symfony/translation/Dumper/PhpFileDumper.php                                                                 |   38 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetAudioEventDetectionForAuditResult.php          |   68 
 vendor/symfony/translation/Tests/fixtures/escaped-id-plurals.po                                                     |   10 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/DataValidations.php                                  |   51 
 vendor/symfony/http-foundation/File/MimeType/MimeTypeExtensionGuesser.php                                           |  819 
 vendor/symfony/http-foundation/Tests/Session/SessionTest.php                                                        |  263 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php                                    |  114 
 vendor/topthink/think-migration/phinx/src/Phinx/Db/Table/Index.php                                                  |  185 
 vendor/symfony/var-dumper/Tests/Caster/GmpCasterTest.php                                                            |   48 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DeleteDomainResponse.php                            |   75 
 vendor/volcengine/volc-sdk-php/src/Service/Imp/ImpUtils.php                                                         |   56 
 vendor/workerman/workerman/WebServer.php                                                                            |  311 
 vendor/topthink/think-orm/src/db/exception/DataNotFoundException.php                                                |   43 
 vendor/symfony/http-foundation/Tests/ExpressionRequestMatcherTest.php                                               |   69 
 vendor/topthink/think-view/LICENSE                                                                                  |  201 
 vendor/topthink/framework/src/think/console/command/Version.php                                                     |   33 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetPlayInfoWithLiveTimeShiftSceneRequest.php       |  201 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DeleteRelaySourceRequest.php                         |   99 
 vendor/topthink/framework/src/think/route/Rule.php                                                                  |  923 
 vendor/xaboy/form-builder/src/Contract/ColComponentInterface.php                                                    |   19 
 vendor/qiniu/php-sdk/tests/Qiniu/Tests/ZoneTest.php                                                                 |   59 
 vendor/topthink/framework/src/think/route/Url.php                                                                   |  517 
 vendor/xaboy/form-builder/src/UI/Elm/Traits/ValidateFactoryTrait.php                                                |   73 
 vendor/xaboy/form-builder/src/UI/Iview/Validate.php                                                                 |  225 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodListWorkflowExecutionResult.php                   |  167 
 vendor/symfony/var-dumper/Tests/Caster/StubCasterTest.php                                                           |  213 
 vendor/topthink/framework/src/think/Manager.php                                                                     |  177 
 vendor/topthink/think-migration/src/db/Column.php                                                                   |  171 
 vendor/topthink/framework/tests/CacheTest.php                                                                       |  149 
 vendor/topthink/think-captcha/assets/bgs/4.jpg                                                                      |    0 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/VQScore.php                                      |    0 
 vendor/topthink/framework/src/think/contract/LogHandlerInterface.php                                                |   28 
 vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/AdapterWrapper.php                                       |  507 
 vendor/xaboy/form-builder/src/Annotation/Info.php                                                                   |   40 
 vendor/xaboy/form-builder/src/UI/Elm/Traits/InputFactoryTrait.php                                                   |  112 
 vendor/symfony/translation/Tests/Loader/JsonFileLoaderTest.php                                                      |   62 
 vendor/topthink/think-image/tests/WaterTest.php                                                                     |   58 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetDirectEditResultRequest.php                     |   58 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/de/functions                                  |  416 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/DocumentGenerator.php                                            |   97 
 vendor/qcloud/cos-sdk-v5/sample/completeMultipartUpload.php                                                         |   37 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetAuditFramesForAudit.php                                          |   30 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetAuditFramesForAuditRequest.php                  |  167 
 vendor/topthink/framework/src/think/Request.php                                                                     | 2178 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Upload/FunctionInner.php                                             |   28 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Comments.php                                         |  232 
 vendor/symfony/translation/Tests/DependencyInjection/TranslationDumperPassTest.php                                  |   48 
 vendor/symfony/polyfill-intl-idn/Resources/unidata/mapped.php                                                       | 5778 
 vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/ExceptionListener.php                                                       |   69 
 vendor/symfony/var-dumper/Tests/Test/VarDumperTestTraitTest.php                                                     |   46 
 vendor/topthink/framework/src/think/Container.php                                                                   |  561 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeRecordTaskFileHistory.php                                  |   34 
 vendor/psr/container/LICENSE                                                                                        |   21 
 vendor/symfony/var-dumper/Caster/GmpCaster.php                                                                      |   30 
 vendor/symfony/translation/Tests/Dumper/IcuResFileDumperTest.php                                                    |   29 
 vendor/topthink/think-multi-app/LICENSE                                                                             |  201 
 vendor/topthink/think-queue/src/queue/command/stubs/failed_jobs.stub                                                |   18 
 vendor/symfony/psr-http-message-bridge/.php_cs.dist                                                                 |   24 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDescribeVodSpaceStorageDataResult.php             |  269 
 vendor/symfony/translation/DependencyInjection/TranslatorPass.php                                                   |   89 
 vendor/symfony/process/Exception/InvalidArgumentException.php                                                       |   21 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Upload/WorkflowInput.php                                             |   18 
 vendor/topthink/think-image/tests/images/test.bmp                                                                   |    0 
 vendor/xaboy/form-builder/src/Rule/OptionsRule.php                                                                  |  109 
 vendor/workerman/workerman/Events/Ev.php                                                                            |  194 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DeletePullToPushTaskResponse.php                    |   75 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetAudioInfoForAuditResult.php                    |   68 
 vendor/qiniu/php-sdk/LICENSE                                                                                        |   22 
 vendor/symfony/var-dumper/Caster/MemcachedCaster.php                                                                |   79 
 vendor/symfony/event-dispatcher/Event.php                                                                           |  120 
 vendor/xaboy/form-builder/src/traits/form/FormInputNumberTrait.php                                                  |   33 
 vendor/topthink/framework/src/think/event/AppInit.php                                                               |   19 
 vendor/volcengine/volc-sdk-php/examples/Live/DescribeClosedStreamInfoByPage.php                                     |   36 
 vendor/psr/cache/src/CacheException.php                                                                             |   10 
 vendor/symfony/translation/Tests/fixtures/with-attributes.xlf                                                       |   21 
 vendor/songshenzong/support/tests/Core/StrTest.php                                                                  |  108 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/RestartPullToPushTaskResponse.php                   |   75 
 vendor/topthink/think-orm/src/db/exception/BindParamException.php                                                   |   35 
 vendor/topthink/think-orm/src/db/connector/Oracle.php                                                               |  117 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCommitUploadInfoData.php                          |  177 
 vendor/psr/container/src/ContainerInterface.php                                                                     |   37 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListCdnTopAccessUrlRequest.php                     |  167 
 vendor/qiniu/php-sdk/examples/rs_batch_change_mime.php                                                              |   28 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoApplyUploadInfo.php                                                 |   31 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/ReferenceHelper.php                                              |  898 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/WorkflowParams.php                                   |  109 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetMLFramesForAudit.php                                             |   38 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Workbook.php                                          | 1147 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/ru/config                                     |   24 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoListCdnAccessLog.php                                                |   30 
 vendor/qcloud_sts/qcloud-sts-sdk/src/Scope.php                                                                      |   56 
 vendor/topthink/think-migration/src/command/stubs/seed.stub                                                         |   19 
 vendor/workerman/channel/README.md                                                                                  |   49 
 vendor/symfony/var-dumper/Command/Descriptor/CliDescriptor.php                                                      |   88 
 vendor/xaboy/form-builder/demo/iview.php                                                                            |  140 
 vendor/topthink/framework/src/think/console/command/make/stubs/controller.plain.stub                                |    9 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDeleteTranscodesRequest.php                        |  133 
 vendor/ralouphie/getallheaders/phpunit.xml                                                                          |   22 
 vendor/volcengine/volc-sdk-php/examples/VoiceNotify/GetResourceUploadUrl.php                                        |   20 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodPlayInfoModelVersion.php                          |   67 
 vendor/topthink/think-queue/src/queue/event/JobExceptionOccurred.php                                                |   45 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodApplyUploadInfoResponse.php                       |  119 
 vendor/workerman/workerman/Protocols/Ws.php                                                                         |  472 
 vendor/symfony/var-dumper/Tests/Caster/ReflectionCasterTest.php                                                     |  251 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/da/functions                                  |  416 
 vendor/volcengine/volc-sdk-php/examples/Visual/DemoConvertPhoto.php                                                 |   13 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/StreamStateInfo.php                                 |  119 
 vendor/symfony/translation/Tests/Loader/YamlFileLoaderTest.php                                                      |   71 
 vendor/xaboy/form-builder/src/Factory/Iview.php                                                                     |  169 
 vendor/pimple/pimple/ext/pimple/tests/003.phpt                                                                      |   16 
 vendor/symfony/translation/PluralizationRules.php                                                                   |  218 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoStream.php                                                         |   25 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoCreateCdnRefreshTask.php                                            |   29 
 vendor/workerman/workerman/Events/Swoole.php                                                                        |  221 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/ManagerPullPushDomainBindRequest.php                 |   99 
 vendor/pimple/pimple/.travis.yml                                                                                    |   40 
 vendor/symfony/http-foundation/Session/Storage/MetadataBag.php                                                      |  168 
 vendor/symfony/translation/Tests/Catalogue/TargetOperationTest.php                                                  |   96 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color.php                                             |   36 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/ScoreInfo.php                                       |   99 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ErrorCode.php                                         |   32 
 vendor/xaboy/form-builder/src/Contract/BootstrapInterface.php                                                       |   27 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php                                                   |  858 
 vendor/xaboy/form-builder/src/UI/Iview/Components/Radio.php                                                         |   65 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoCreateSpace.php                                                     |   31 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/FillPattern.php                                 |   47 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE/Blip.php          |   60 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/TaskInfoItem.php                                    |  439 
 vendor/symfony/http-foundation/RequestStack.php                                                                     |  103 
 vendor/symfony/polyfill-mbstring/Mbstring.php                                                                       |  800 
 vendor/symfony/polyfill-php72/bootstrap.php                                                                         |   36 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSpaceInfo.php                                     |  371 
 vendor/symfony/translation/Tests/fixtures/resources.ini                                                             |    1 
 vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoUnBindAXB.php                                              |   17 
 vendor/symfony/translation/Exception/InvalidResourceException.php                                                   |   21 
 vendor/symfony/process/Exception/ProcessSignaledException.php                                                       |   41 
 vendor/xaboy/form-builder/src/UI/Elm/Traits/SliderFactoryTrait.php                                                  |   50 
 vendor/qiniu/php-sdk/examples/upload_mgr_init.php                                                                   |   18 
 vendor/topthink/think-filesystem/src/Filesystem.php                                                                 |   89 
 vendor/songshenzong/support/src/Arrays.php                                                                          |   84 
 vendor/xaboy/form-builder/src/UI/Elm/Components/Switches.php                                                        |   75 
 vendor/symfony/translation/Tests/fixtures/resources.ts                                                              |   21 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Color.php                                                  |  407 
 vendor/topthink/think-orm/src/db/concern/Transaction.php                                                            |  117 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetDirectEditProgress.php                                           |   26 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/Pagination.php                                      |  167 
 vendor/xaboy/form-builder/src/UI/Elm/Components/Upload.php                                                          |  106 
 vendor/volcengine/volc-sdk-php/src/Base/SignatureV4.php                                                             |  316 
 vendor/topthink/think-migration/src/command/migrate/Status.php                                                      |  126 
 vendor/topthink/think-migration/phinx/src/Phinx/Seed/Seed.template.php.dist                                         |   19 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListSpaceResponse.php                             |  109 
 vendor/xaboy/form-builder/src/UI/Elm/Components/Frame.php                                                           |   84 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Exception.php                                             |    9 
 vendor/psr/http-message/README.md                                                                                   |   13 
 vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php                                                     |  524 
 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php                              |  411 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Coordinate.php                                              |  512 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodListCdnAccessLogResult.php                        |   65 
 vendor/xaboy/form-builder/src/UI/Elm/Components/InputNumber.php                                                     |   57 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/DocProps.php                                         |  239 
 vendor/topthink/think-orm/LICENSE                                                                                   |  201 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeLiveStreamInfoByPageRequest.php              |  347 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodStorageData.php                                   |   85 
 vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/GPBMetadata/ResponseImp.php                                   |   26 
 vendor/topthink/think-helper/src/helper.php                                                                         |  279 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDeleteMediaRequest.php                             |   99 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Comment.php                                                      |  329 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/QRDecomposition.php                                  |  249 
 vendor/qiniu/php-sdk/tests/Qiniu/Tests/Base64Test.php                                                               |   14 
 vendor/symfony/translation/.gitignore                                                                               |    3 
 vendor/qiniu/php-sdk/examples/get_bucketEvents.php                                                                  |   20 
 vendor/symfony/var-dumper/Tests/Cloner/DataTest.php                                                                 |  115 
 vendor/symfony/var-dumper/Tests/Command/Descriptor/HtmlDescriptorTest.php                                           |  195 
 vendor/symfony/translation/Dumper/PoFileDumper.php                                                                  |   84 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/ExecutionStage.php                                   |  187 
 vendor/topthink/framework/src/think/cache/driver/Redis.php                                                          |  249 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoCreateVQScoreTask.php                                              |   31 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Meta.php                                              |   75 
 vendor/topthink/think-queue/tests/DatabaseConnectorTest.php                                                         |  123 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php                                  |  380 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php                                 |  125 
 vendor/symfony/process/README.md                                                                                    |   13 
 vendor/volcengine/volc-sdk-php/src/Service/VEdit.php                                                                |   84 
 vendor/qiniu/php-sdk/tests/Qiniu/Tests/FormUpTest.php                                                               |   59 
 vendor/symfony/var-dumper/Cloner/DumperInterface.php                                                                |   60 
 vendor/symfony/http-foundation/Session/SessionBagProxy.php                                                          |   89 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnService.php                                              |   15 
 vendor/symfony/translation/Tests/Loader/MoFileLoaderTest.php                                                        |   72 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnAccessLog.php                                            |   23 
 vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php                                         |   34 
 vendor/qiniu/php-sdk/examples/upload_multi_demos.php                                                                |   85 
 vendor/xaboy/form-builder/src/UI/Elm/Components/Hidden.php                                                          |   57 
 vendor/topthink/think-captcha/assets/bgs/2.jpg                                                                      |    0 
 vendor/topthink/framework/src/think/console/output/driver/Nothing.php                                               |   33 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetPrivateDrmPlayAuthResult.php                   |   65 
 vendor/volcengine/volc-sdk-php/examples/Visual/DemoJPCartoon.php                                                    |   14 
 vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Signature.php                                                               |   50 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php                                |  196 
 vendor/pimple/pimple/ext/pimple/tests/018.phpt                                                                      |   23 
 vendor/qiniu/php-sdk/src/Qiniu/Http/Client.php                                                                      |  160 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetMediaListResponse.php                          |  119 
 vendor/symfony/var-dumper/Tests/Server/ConnectionTest.php                                                           |   88 
 vendor/workerman/workerman/Protocols/Http/mime.types                                                                |   80 
 vendor/topthink/framework/src/think/console/command/VendorPublish.php                                               |   69 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Exception.php                                              |    9 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodSetCallbackEventResponse.php                      |   75 
 vendor/workerman/workerman/README.md                                                                                |  603 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCommitUploadInfoResponseData.php                  |  211 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodStoreInfo.php                                     |   99 
 vendor/symfony/translation/Loader/PoFileLoader.php                                                                  |  148 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoCommitUploadInfo.php                                                |   30 
 vendor/xaboy/form-builder/src/traits/form/FormValidateTrait.php                                                     |   84 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateSubtitleStatus.php                                            |   31 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeEdgeNrtDataSummary.php                                      |   29 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeVQScoreTaskResponse.php                     |  119 
 vendor/symfony/process/Tests/PhpExecutableFinderTest.php                                                            |   49 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/Border.php                                      |   42 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoListResourceTags.php                                                |   15 
 vendor/xaboy/form-builder/src/traits/form/FormSelectTrait.php                                                       |   61 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php          |  369 
 vendor/symfony/polyfill-intl-normalizer/LICENSE                                                                     |   19 
 vendor/symfony/translation/Loader/QtFileLoader.php                                                                  |   77 
 vendor/pimple/pimple/ext/pimple/tests/008.phpt                                                                      |   29 
 vendor/songshenzong/support/src/Traits/Str.php                                                                      |  295 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListSpaceRequest.php                               |   99 
 vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php                                           |   61 
 vendor/xaboy/form-builder/src/UI/Iview/Components/Group.php                                                         |   77 
 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeSessionHandlerTest.php                           |   38 
 vendor/topthink/framework/tests/EnvTest.php                                                                         |   80 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateSubtitleStatusResponse.php                  |  119 
 vendor/topthink/think-captcha/assets/ttfs/6.ttf                                                                     |    0 
 vendor/xaboy/form-builder/src/traits/form/FormStyleTrait.php                                                        |   63 
 vendor/qiniu/php-sdk/examples/cdn_get_log_list.php                                                                  |   29 
 vendor/topthink/think-orm/src/db/Raw.php                                                                            |   71 
 vendor/volcengine/volc-sdk-php/src/Service/Rtc.php                                                                  |   78 
 vendor/topthink/think-image/tests/FlipTest.php                                                                      |   43 
 vendor/qcloud/cos-sdk-v5/README.md                                                                                  |  264 
 vendor/pimple/pimple/ext/pimple/tests/017_1.phpt                                                                    |   17 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Worksheet.php                                          | 3013 
 vendor/symfony/translation/Translator.php                                                                           |  510 
 vendor/topthink/think-migration/src/command/factory/Create.php                                                      |   82 
 vendor/qiniu/php-sdk/tests/Qiniu/Tests/PfopTest.php                                                                 |   66 
 vendor/topthink/framework/src/think/middleware/FormTokenCheck.php                                                   |   45 
 vendor/volcengine/volc-sdk-php/examples/ImageX/DemoRequest.php                                                      |   19 
 vendor/qcloud/cos-sdk-v5/sample/abortMultipartUpload.php                                                            |   27 
 vendor/qcloud/cos-sdk-v5/sample/putBucketCors.php                                                                   |   33 
 vendor/topthink/think-orm/src/model/Relation.php                                                                    |  258 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeOriginTopStatusCode.php                                     |   20 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/BIFFwriter.php                                        |  224 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/ru/functions                                  |  416 
 vendor/topthink/think-orm/src/db/exception/ModelEventException.php                                                  |   19 
 vendor/topthink/think-queue/src/Queue.php                                                                           |   77 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php                                       |   92 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/XMLWriter.php                                             |   92 
 vendor/topthink/framework/tests/bootstrap.php                                                                       |    3 
 vendor/services.php                                                                                                 |    9 
 vendor/topthink/framework/src/think/console/command/Help.php                                                        |   70 
 vendor/symfony/translation/Dumper/QtFileDumper.php                                                                  |   61 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pl/functions                                  |  416 
 vendor/topthink/framework/src/think/middleware/AllowCrossDomain.php                                                 |   63 
 vendor/topthink/think-orm/src/db/Query.php                                                                          |  451 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/RelaySourceConfig.php                               |  133 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidation.php                                          |  481 
 vendor/volcengine/volc-sdk-php/src/Service/Billing.php                                                              |   76 
 vendor/pimple/pimple/src/Pimple/Tests/Psr11/ServiceLocatorTest.php                                                  |  134 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/PushURLItem.php                                     |  133 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeContentQuota.php                                            |   15 
 vendor/symfony/http-foundation/Session/SessionInterface.php                                                         |  180 
 vendor/topthink/think-queue/src/queue/command/ListFailed.php                                                        |  110 
 vendor/volcengine/volc-sdk-php/examples/GameProtect/DemoRiskResult.php                                              |   14 
 vendor/psr/http-factory/LICENSE                                                                                     |   21 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DeletePullToPushTaskRequest.php                      |   65 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Drawing.php                                               |  249 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowDimension.php                                       |  115 
 vendor/topthink/think-migration/src/command/migrate/Run.php                                                         |  140 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/Domain.php                                       |    0 
 vendor/topthink/think-queue/src/queue/failed/None.php                                                               |   63 
 vendor/qiniu/php-sdk/examples/php-logo.png                                                                          |    0 
 vendor/topthink/think-helper/src/helper/Arr.php                                                                     |  634 
 vendor/xaboy/form-builder/src/Rule/BaseRule.php                                                                     |  323 
 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php                          |  333 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Protection.php                                         |  698 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUrlUploadResponse.php                             |  119 
 vendor/symfony/translation/Tests/fixtures/messages_linear.yml                                                       |    2 
 vendor/topthink/think-image/tests/TestCase.php                                                                      |   33 
 vendor/symfony/translation/Formatter/MessageFormatterInterface.php                                                  |   30 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodWorkflow.php                                   |    0 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeRelaySourceResponse.php                     |  119 
 vendor/xaboy/form-builder/src/Rule/EmitRule.php                                                                     |   68 
 vendor/symfony/var-dumper/Tests/Cloner/VarClonerTest.php                                                            |  439 
 vendor/qiniu/php-sdk/examples/rs_copy.php                                                                           |   22 
 vendor/xaboy/form-builder/src/UI/Iview/Traits/DatePickerFactoryTrait.php                                            |  128 
 vendor/symfony/var-dumper/Command/Descriptor/DumpDescriptorInterface.php                                            |   23 
 vendor/topthink/think-orm/src/db/exception/DbException.php                                                          |   81 
 vendor/qiniu/php-sdk/examples/rs_bucket_domains.php                                                                 |   19 
 vendor/topthink/framework/src/think/exception/FuncNotFoundException.php                                             |   30 
 vendor/topthink/framework/src/think/console/output/Formatter.php                                                    |  198 
 vendor/topthink/think-orm/src/db/connector/Pgsql.php                                                                |  108 
 vendor/psr/http-client/CHANGELOG.md                                                                                 |   23 
 vendor/qiniu/php-sdk/docs/rtc/example.php                                                                           |   42 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeForbiddenStreamInfoByPage.php                              |   32 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSmartStrategyDataStoreStatus.php                  |   68 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fi/config                                     |   24 
 vendor/symfony/translation/Tests/fixtures/extractor/resource.format.engine                                          |    0 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetMediaInfosResponse.php                         |  119 
 vendor/symfony/process/Tests/PipeStdinInStdoutStdErrStreamSelect.php                                                |   72 
 vendor/xaboy/form-builder/src/components/Option.php                                                                 |   76 
 vendor/xaboy/form-builder/src/UI/Iview/Traits/FormStyleFactoryTrait.php                                             |   55 
 vendor/symfony/process/phpunit.xml.dist                                                                             |   30 
 vendor/ralouphie/getallheaders/.gitignore                                                                           |    5 
 vendor/symfony/http-foundation/Tests/File/Fixtures/directory/.empty                                                 |    0 
 vendor/symfony/psr-http-message-bridge/Tests/Factory/HttpFoundationFactoryTest.php                                  |  250 
 vendor/symfony/process/Tests/ProcessTest.php                                                                        | 1557 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDomainConfigInfo.php                              |  153 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoCallback.php                                                       |   33 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodQueryUploadTaskInfoResponse.php                   |  119 
 vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/NativeProxyTest.php                                      |   38 
 vendor/symfony/psr-http-message-bridge/Tests/Factory/AbstractHttpMessageFactoryTest.php                             |  229 
 vendor/symfony/translation/Tests/Dumper/PoFileDumperTest.php                                                        |   48 
 vendor/topthink/think-filesystem/.gitignore                                                                         |    3 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetRecommendedPosterRequest.php                    |   65 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php                                   |   81 
 vendor/qcloud/cos-sdk-v5/sample/headObject.php                                                                      |   26 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/it/config                                     |   24 
 vendor/qiniu/php-sdk/tests/Qiniu/Tests/Crc32Test.php                                                                |   21 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetSmartStrategyLitePlayInfo.php                                    |   37 
 vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/BucketStyleListener.php                                                     |  107 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodAddCallbackSubscriptionResponse.php               |   79 
 vendor/symfony/var-dumper/Test/VarDumperTestTrait.php                                                               |   58 
 vendor/qcloud/cos-sdk-v5/sample/listParts.php                                                                       |   29 
 vendor/topthink/framework/src/think/facade/Log.php                                                                  |   58 
 vendor/symfony/translation/Tests/Dumper/CsvFileDumperTest.php                                                       |   30 
 vendor/topthink/think-queue/composer.json                                                                           |   47 
 vendor/symfony/translation/Tests/Loader/IniFileLoaderTest.php                                                       |   51 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/StreamInfoList.php                                  |  405 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSnapshotData.php                                  |  303 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetAudioEventDetectionForAuditRequest.php          |   99 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/BestFit.php                                         |  463 
 vendor/topthink/framework/tests/SessionTest.php                                                                     |  225 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Properties.php                                             |  369 
 vendor/xaboy/form-builder/src/traits/component/CallPropsTrait.php                                                   |   52 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodApplyUploadInfoRequest.php                         |  201 
 vendor/xaboy/form-builder/src/UI/Elm/Traits/HiddenFactoryTrait.php                                                  |   31 
 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_lax.php                           |    8 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Border.php                                                 |  231 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/CellsFactory.php                                      |   21 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnDataDetail.php                                           |   27 
 vendor/topthink/framework/src/tpl/think_exception.tpl                                                               |  502 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodQueryUploadResult.php                             |   99 
 vendor/symfony/var-dumper/Dumper/ServerDumper.php                                                                   |   53 
 vendor/topthink/think-migration/LICENSE                                                                             |   32 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Upload/VodUpload.php                                                 |  266 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php                                        |  136 
 vendor/volcengine/volc-sdk-php/examples/Visual/DemoEnhancePhoto.php                                                 |   13 
 vendor/symfony/http-foundation/FileBag.php                                                                          |  144 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetSubtitleInfoListResponse.php                   |  119 
 vendor/symfony/process/Pipes/WindowsPipes.php                                                                       |  191 
 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.php                         |  135 
 vendor/symfony/translation/Tests/fixtures/withnote.xlf                                                              |   22 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/ResumeStreamRequest.php                              |  179 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php                                       |   59 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Chart.php                                            | 1516 
 vendor/topthink/think-queue/src/queue/command/ForgetFailed.php                                                      |   25 
 vendor/topthink/think-filesystem/src/filesystem/Driver.php                                                          |  130 
 vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/SignatureListener.php                                                       |   45 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/GetDirectEditProgress.php                            |   58 
 vendor/symfony/translation-contracts/LICENSE                                                                        |   19 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/StringValueBinder.php                                       |   29 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/ListPullToPushTaskResult.php                        |  109 
 vendor/topthink/framework/LICENSE.txt                                                                               |   32 
 vendor/symfony/var-dumper/Tests/Dumper/ServerDumperTest.php                                                         |   95 
 vendor/symfony/polyfill-intl-normalizer/Resources/unidata/compatibilityDecomposition.php                            | 3695 
 vendor/topthink/framework/src/think/log/driver/File.php                                                             |  207 
 vendor/symfony/var-dumper/LICENSE                                                                                   |   19 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php                                        |  151 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Style.php                                                  |  639 
 vendor/qiniu/php-sdk/examples/rs_delete_after_days.php                                                              |   20 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeRecordTaskFileHistoryRequest.php             |  327 
 vendor/symfony/http-foundation/Session/Session.php                                                                  |  280 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoAddCdnDomain.php                                                    |   37 
 vendor/pimple/pimple/src/Pimple/Exception/UnknownIdentifierException.php                                            |   45 
 vendor/symfony/http-foundation/Session/Storage/Handler/MemcacheSessionHandler.php                                   |  118 
 vendor/psr/simple-cache/src/CacheException.php                                                                      |   10 
 vendor/qiniu/php-sdk/src/Qiniu/Processing/Operation.php                                                             |   60 
 vendor/symfony/translation/Loader/MoFileLoader.php                                                                  |  145 
 vendor/workerman/crontab/example/test.php                                                                           |   16 
 vendor/psr/log/.gitignore                                                                                           |    1 
 vendor/topthink/framework/src/think/log/ChannelSet.php                                                              |   39 
 vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Service.php                                                                 | 4360 
 vendor/symfony/var-dumper/Tests/Caster/IntlCasterTest.php                                                           |  297 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSamplePosterSnapshot.php                          |  303 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReadFilter.php                                           |   17 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodContentInfo.php                                   |  235 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeOriginTopNrtData.php                                        |   22 
 vendor/xaboy/form-builder/src/UI/Iview/Components/Cascader.php                                                      |  122 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodPlayInfoWithLiveTimeShiftScene.php                |  167 
 vendor/songshenzong/support/.travis.yml                                                                             |   22 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodCreateVideoClassificationRequest.php               |  139 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoListCdnStatusData.php                                               |   32 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDescribeVodSpaceStorageDataRequest.php             |  201 
 vendor/xaboy/form-builder/src/UI/Elm/Components/TreeData.php                                                        |  106 
 vendor/topthink/think-queue/src/queue/job/Redis.php                                                                 |  121 
 vendor/symfony/translation/Tests/fixtures/resources-tool-info.xlf                                                   |   14 
 vendor/topthink/think-queue/src/queue/event/JobProcessed.php                                                        |   20 
 vendor/topthink/think-queue/src/queue/command/Table.php                                                             |   46 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/BaseWriter.php                                            |  131 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoListCdnPvData.php                                                   |   31 
 vendor/topthink/framework/src/think/log/Channel.php                                                                 |  286 
 vendor/topthink/framework/tests/EventTest.php                                                                       |  134 
 vendor/symfony/translation/Exception/InvalidArgumentException.php                                                   |   21 
 vendor/symfony/event-dispatcher/Tests/EventTest.php                                                                 |   97 
 vendor/topthink/think-queue/src/queue/connector/Sync.php                                                            |   74 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodCommon.php                                     |    0 
 vendor/topthink/framework/tests/RouteTest.php                                                                       |  291 
 vendor/symfony/process/Tests/ExecutableFinderTest.php                                                               |  178 
 vendor/symfony/translation/Command/XliffLintCommand.php                                                             |  279 
 vendor/symfony/translation/composer.json                                                                            |   60 
 vendor/xaboy/form-builder/src/traits/form/FormRateTrait.php                                                         |   32 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/TimeZone.php                                              |   81 
 vendor/symfony/translation/Dumper/MoFileDumper.php                                                                  |   82 
 vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php                                              |   88 
 vendor/xaboy/form-builder/src/UI/Elm/Components/Rate.php                                                            |   69 
 vendor/qiniu/php-sdk/examples/rs_change_mime.php                                                                    |   20 
 vendor/topthink/framework/src/think/exception/ValidateException.php                                                 |   37 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer.php                                    |   52 
 vendor/symfony/polyfill-php72/LICENSE                                                                               |   19 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodFrameExtractingOptionForAudit.php                 |   82 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetBetterFramesForAuditRequest.php                 |  133 
 vendor/topthink/framework/src/think/Session.php                                                                     |   65 
 vendor/symfony/translation/Loader/IniFileLoader.php                                                                 |   28 
 vendor/topthink/framework/src/think/console/input/Option.php                                                        |  221 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Cell.php                                                    |  676 
 vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/CosException.php                                                  |    7 
 vendor/symfony/http-foundation/Tests/File/UploadedFileTest.php                                                      |  286 
 vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Response/ImpKillJobResponse.php                               |   65 
 vendor/symfony/translation/Resources/bin/translation-status.php                                                     |  200 
 vendor/qiniu/php-sdk/tests/Qiniu/Tests/AuthTest.php                                                                 |   71 
 vendor/volcengine/volc-sdk-php/src/Service/Base/Models/Base/ResponseError.php                                       |   99 
 vendor/symfony/http-foundation/ParameterBag.php                                                                     |  234 
 vendor/topthink/framework/src/think/console/command/make/stubs/validate.stub                                        |   25 
 vendor/xaboy/form-builder/src/UI/Elm/Components/Tree.php                                                            |  108 
 vendor/symfony/translation/Tests/DataCollector/TranslationDataCollectorTest.php                                     |  150 
 vendor/xaboy/form-builder/src/UI/Iview/Components/Poptip.php                                                        |   57 
 vendor/symfony/translation-contracts/TranslatorTrait.php                                                            |  255 
 vendor/symfony/http-foundation/Tests/ApacheRequestTest.php                                                          |   93 
 vendor/volcengine/volc-sdk-php/examples/VoiceNotify/SingleBatchAppend.php                                           |   28 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodFrameDataForAudit.php                             |  201 
 vendor/qiniu/php-sdk/tests/bootstrap.php                                                                            |   43 
 vendor/xaboy/form-builder/src/UI/Iview/Style/FormStyle.php                                                          |  117 
 vendor/symfony/psr-http-message-bridge/Tests/Fixtures/Stream.php                                                    |   99 
 vendor/topthink/framework/src/think/facade/Cache.php                                                                |   48 
 vendor/symfony/translation/Tests/IdentityTranslatorTest.php                                                         |   23 
 vendor/symfony/translation/Tests/fixtures/empty.ini                                                                 |    0 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeRelaySourceRequest.php                       |   65 
 vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Request/ImpKillJobRequest.php                                 |   65 
 vendor/symfony/var-dumper/Dumper/AbstractDumper.php                                                                 |  211 
 vendor/topthink/framework/src/think/facade/Console.php                                                              |   56 
 vendor/qiniu/php-sdk/examples/pfop_watermark.php                                                                    |   52 
 vendor/topthink/framework/src/think/log/driver/Socket.php                                                           |  311 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDomainCertificateInfo.php                         |  235 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoRetrieveTranscodeResult.php                                         |   28 
 vendor/xaboy/form-builder/src/Rule/PropsRule.php                                                                    |  132 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodPlay.php                                       |    0 
 vendor/xaboy/form-builder/src/Annotation/Validate/Range.php                                                         |   40 
 vendor/qcloud/cos-sdk-v5/.travis.yml                                                                                |    6 
 vendor/symfony/translation/DependencyInjection/TranslationDumperPass.php                                            |   44 
 vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/InvalidArgumentException.php                                      |    5 
 vendor/topthink/think-queue/src/queue/exception/MaxAttemptsExceededException.php                                    |   10 
 vendor/qiniu/php-sdk/examples/rs_batch_change_type.php                                                              |   34 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetAutomaticSpeechRecognitionForAudit.php                           |   28 
 vendor/psr/http-message/src/ResponseInterface.php                                                                   |   68 
 vendor/volcengine/volc-sdk-php/examples/VEdit/DemoSubmitDirectEditTask.php                                          |   45 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnDimension.php                                    |  115 
 vendor/workerman/workerman/Events/EventInterface.php                                                                |  107 
 vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php                                                         |  214 
 vendor/xaboy/form-builder/src/traits/form/FormCheckBoxTrait.php                                                     |   31 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Html.php                                                  |  952 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoListDomain.php                                                      |   27 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetFramesForAuditResult.php                       |   65 
 vendor/symfony/http-foundation/Tests/RequestTest.php                                                                | 2446 
 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.expected                            |   14 
 vendor/symfony/translation/Tests/fixtures/resources-2.0-clean.xlf                                                   |   23 
 vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php                                                    | 1104 
 vendor/xaboy/form-builder/src/components/Hidden.php                                                                 |   55 
 vendor/symfony/polyfill-intl-idn/Info.php                                                                           |   23 
 vendor/symfony/var-dumper/Caster/ProxyManagerCaster.php                                                             |   31 
 vendor/topthink/think-image/tests/images/test.jpg                                                                   |    0 
 vendor/xaboy/form-builder/src/components/Upload.php                                                                 |  156 
 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php                         |   61 
 vendor/symfony/psr-http-message-bridge/Tests/Fixtures/UploadedFile.php                                              |   65 
 vendor/topthink/framework/src/think/console/output/Ask.php                                                          |  336 
 vendor/symfony/psr-http-message-bridge/Factory/PsrHttpFactory.php                                                   |  172 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateSpaceUploadConfigResponse.php               |   75 
 vendor/qiniu/php-sdk/tests/Qiniu/Tests/ImageUrlBuilderTest.php                                                      |  261 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowCellIterator.php                                    |  195 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Cells.php                                             |  498 
 vendor/topthink/think-migration/composer.json                                                                       |   34 
 vendor/topthink/framework/src/think/response/Jsonp.php                                                              |   74 
 vendor/xaboy/form-builder/src/UI/Iview/Traits/InputNumberFactoryTrait.php                                           |   32 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/GeneratePlayURLRequest.php                           |  303 
 vendor/symfony/translation/Tests/fixtures/resources-2.0+intl-icu.xlf                                                |   11 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodVideoStreamMeta.php                               |  269 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php                                                   |  489 
 vendor/qiniu/php-sdk/examples/put_bucketAccessMode.php                                                              |   21 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pl/config                                     |   24 
 vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php                                        |  254 
 vendor/symfony/process/LICENSE                                                                                      |   19 
 vendor/symfony/translation/Dumper/YamlFileDumper.php                                                                |   62 
 vendor/xaboy/form-builder/src/traits/form/FormRadioTrait.php                                                        |   32 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DisableDomainRequest.php                             |   65 
 vendor/topthink/framework/src/think/Event.php                                                                       |  272 
 vendor/symfony/translation/Tests/fixtures/empty-translation.po                                                      |    3 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoDeletePullToPushTask.php                                           |   27 
 vendor/symfony/polyfill-intl-normalizer/Normalizer.php                                                              |  308 
 vendor/volcengine/volc-sdk-php/src/Service/Sms.php                                                                  |   60 
 vendor/topthink/think-orm/composer.json                                                                             |   28 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetPrivateDrmPlayAuthRequest.php                   |  167 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/VQScoreID.php                                       |   65 
 vendor/topthink/think-image/tests/TextTest.php                                                                      |   58 
 vendor/topthink/think-queue/src/queue/Connector.php                                                                 |  163 
 vendor/symfony/polyfill-intl-idn/bootstrap.php                                                                      |  141 
 vendor/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php                                 |   55 
 vendor/symfony/http-foundation/Tests/File/Fixtures/test                                                             |    0 
 vendor/workerman/workerman/Events/React/ExtEventLoop.php                                                            |   27 
 vendor/topthink/think-orm/src/db/Builder.php                                                                        | 1303 
 vendor/symfony/translation/Tests/Util/ArrayConverterTest.php                                                        |   74 
 vendor/symfony/var-dumper/Caster/ClassStub.php                                                                      |  106 
 vendor/topthink/think-orm/src/db/Mongo.php                                                                          |  712 
 vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Invokable.php                                                        |   38 
 vendor/qiniu/php-sdk/examples/rsf_list_bucket.php                                                                   |   46 
 vendor/topthink/think-queue/README.md                                                                               |  118 
 vendor/xaboy/form-builder/src/traits/form/FormUploadTrait.php                                                       |  103 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php                                                   | 7933 +
 vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/resources.dat                                          |    0 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/SubmitDirectEditTaskAsyncResult.php                  |   65 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLERead.php                                               |  350 
 vendor/topthink/framework/src/think/response/Redirect.php                                                           |   98 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateMediaInfo.php                                                 |   32 
 vendor/volcengine/volc-sdk-php/examples/ImageX/DemoGetImageOCR.php                                                  |   17 
 vendor/qcloud/cos-sdk-v5/sample/copyObject.php                                                                      |   28 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeEdgeTopStatusCode.php                                       |   20 
 vendor/xaboy/form-builder/src/UI/Iview/Bootstrap.php                                                                |   53 
 vendor/topthink/framework/src/think/session/Store.php                                                               |  340 
 vendor/ralouphie/getallheaders/composer.json                                                                        |   21 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Mimetype.php                                          |   20 
 vendor/symfony/process/composer.json                                                                                |   33 
 vendor/workerman/workerman/Events/Event.php                                                                         |  219 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetMediaInfosData.php                             |   99 
 vendor/volcengine/volc-sdk-php/examples/VoiceNotify/SingleInfo.php                                                  |   16 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoListCdnTasks.php                                                    |   35 
 vendor/xaboy/form-builder/src/Factory/Base.php                                                                      |   30 
 vendor/qcloud/cos-sdk-v5/sample/deleteBucketLifecycle.php                                                           |   25 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/VhostWithDenyConfig.php                             |  167 
 vendor/qiniu/php-sdk/examples/rs_pub_domain.php                                                                     |   19 
 vendor/topthink/framework/src/think/console/bin/README.md                                                           |    1 
 vendor/topthink/think-queue/src/queue/command/Restart.php                                                           |   33 
 vendor/topthink/framework/src/think/response/File.php                                                               |  160 
 vendor/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php                                  |  122 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoListCdnUsageData.php                                                |   32 
 vendor/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php                                     |  103 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/BaseReader.php                                            |  158 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Upload/Functions.php                                                 |   36 
 vendor/volcengine/volc-sdk-php/examples/Visual/DemoFaceSwap.php                                                     |   14 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodUrlResponseData.php                               |   65 
 vendor/topthink/think-queue/src/queue/command/Listen.php                                                            |   63 
 vendor/xaboy/form-builder/LICENSE                                                                                   |   21 
 vendor/xaboy/form-builder/src/Handle/IviewFormHandle.php                                                            |   33 
 vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/WrapperInterface.php                                     |   60 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/PullToPush.php                                   |    0 
 vendor/symfony/translation/Tests/fixtures/resources-clean.xlf                                                       |   25 
 vendor/symfony/translation-contracts/README.md                                                                      |    9 
 vendor/volcengine/volc-sdk-php/src/Service/Live.php                                                                 |  871 
 vendor/xaboy/form-builder/src/UI/Iview/Components/Input.php                                                         |   91 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetAppInfoResponse.php                            |  123 
 vendor/topthink/think-migration/phinx/src/Phinx/Db/Table.php                                                        |  674 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/File.php                                          |   64 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/TextElement.php                                         |   86 
 vendor/psr/log/README.md                                                                                            |   58 
 vendor/symfony/polyfill-intl-normalizer/README.md                                                                   |   14 
 vendor/topthink/framework/src/think/console/command/make/Controller.php                                             |   56 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoAddCallbackSubscription.php                                         |   29 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetMediaListData.php                              |  201 
 vendor/xaboy/form-builder/src/Annotation/Validate/Min.php                                                           |   39 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDeleteResourceTags.php                                              |   22 
 vendor/xaboy/form-builder/src/Contract/OptionComponentInterface.php                                                 |   19 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/RecordManage.php                                 |    0 
 vendor/qiniu/php-sdk/examples/pfop_vframe.php                                                                       |   46 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Font.php                                              |  147 
 vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoBindAXB.php                                                |   22 
 vendor/qiniu/php-sdk/examples/saveas.php                                                                            |   28 
 vendor/xaboy/form-builder/src/UI/Elm/Traits/FormStyleFactoryTrait.php                                               |   55 
 vendor/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php                                              |   17 
 vendor/psr/http-factory/src/StreamFactoryInterface.php                                                              |   45 
 vendor/xaboy/form-builder/src/components/Select.php                                                                 |  129 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsRibbon.php                                       |   45 
 vendor/symfony/http-foundation/Session/Attribute/AttributeBag.php                                                   |  148 
 vendor/topthink/think-captcha/assets/ttfs/3.ttf                                                                     |    0 
 vendor/symfony/process/Tests/KillableProcessWithOutput.php                                                          |   25 
 vendor/symfony/polyfill-intl-idn/Resources/unidata/disallowed.php                                                   | 2638 
 vendor/topthink/think-captcha/LICENSE                                                                               |   32 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodQueryUploadTaskInfoRequest.php                     |   65 
 vendor/qcloud/cos-sdk-v5/sample/putBucketLifecycle.php                                                              |   35 
 vendor/qiniu/php-sdk/examples/get_bucketLifecycleRules.php                                                          |   20 
 vendor/topthink/think-filesystem/src/filesystem/driver/Local.php                                                    |   98 
 vendor/topthink/think-template/src/template/exception/TemplateNotFoundException.php                                 |   33 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDeleteTranscodesResponse.php                      |  119 
 vendor/symfony/http-foundation/StreamedResponse.php                                                                 |  146 
 vendor/pimple/pimple/ext/pimple/tests/006.phpt                                                                      |   51 
 vendor/symfony/http-foundation/Tests/schema/iana-registry.rng                                                       |  198 
 vendor/topthink/think-orm/src/model/relation/OneToOne.php                                                           |  332 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodCreateCdnRefreshTaskResponse.php                  |  119 
 vendor/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php                                                   |    5 
 vendor/volcengine/volc-sdk-php/examples/VoiceNotify/DeleteResource.php                                              |   12 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateMediaInfoResponse.php                       |   75 
 vendor/topthink/framework/src/think/Config.php                                                                      |  197 
 vendor/topthink/think-orm/src/db/CacheItem.php                                                                      |  209 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php                                         |  406 
 vendor/symfony/translation/Tests/Writer/TranslationWriterTest.php                                                   |   69 
 vendor/topthink/framework/.travis.yml                                                                               |   35 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/ForbiddenStreamInfo.php                             |   99 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods.php                                                   |  699 
 vendor/pimple/pimple/src/Pimple/Tests/Fixtures/PimpleServiceProvider.php                                            |   54 
 vendor/volcengine/volc-sdk-php/examples/VoiceNotify/SingleCancel.php                                                |   16 
 vendor/topthink/think-orm/src/model/relation/BelongsToMany.php                                                      |  677 
 vendor/topthink/framework/src/think/console/output/question/Confirmation.php                                        |   57 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetBetterFramesForAudit.php                                         |   29 
 vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/packagelist.txt                                        |    2 
 vendor/topthink/framework/src/think/facade/Session.php                                                              |   35 
 vendor/pimple/pimple/src/Pimple/Exception/InvalidServiceIdentifierException.php                                     |   45 
 vendor/psr/log/Psr/Log/NullLogger.php                                                                               |   30 
 vendor/topthink/think-image/README.md                                                                               |   29 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnTaskResult.php                                 |  167 
 vendor/topthink/framework/src/think/exception/FileException.php                                                     |   17 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodCdn.php                                        |    0 
 vendor/topthink/think-multi-app/src/command/Build.php                                                               |  180 
 vendor/symfony/var-dumper/Tests/Caster/XmlReaderCasterTest.php                                                      |  248 
 vendor/topthink/framework/src/think/Validate.php                                                                    | 1688 
 vendor/symfony/translation/Catalogue/TargetOperation.php                                                            |   72 
 vendor/topthink/framework/src/think/console/Output.php                                                              |  231 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateSubtitleStatusRequest.php                    |  201 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetHlsDecryptionKey.php                                             |   29 
 vendor/xaboy/form-builder/src/components/Switches.php                                                               |   98 
 vendor/spatie/macroable/LICENSE.md                                                                                  |   21 
 vendor/xaboy/form-builder/src/UI/Elm/Config.php                                                                     |  218 
 vendor/qiniu/php-sdk/examples/upload_tokens.php                                                                     |   68 
 vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php                                             |  436 
 vendor/symfony/translation/Loader/CsvFileLoader.php                                                                 |   65 
 vendor/topthink/think-queue/src/queue/event/JobFailed.php                                                           |   24 
 vendor/xaboy/form-builder/src/Rule/CallPropsRule.php                                                                |   45 
 vendor/topthink/think-filesystem/src/facade/Filesystem.php                                                          |   33 
 vendor/qcloud/cos-sdk-v5/sample/listBucket.php                                                                      |   24 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/UpdatePullToPushTaskRequest.php                      |  379 
 vendor/topthink/framework/src/think/console/output/driver/Buffer.php                                                |   52 
 vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Md5Listener.php                                                             |   57 
 vendor/symfony/translation/Tests/Dumper/IniFileDumperTest.php                                                       |   29 
 vendor/pimple/pimple/src/Pimple/Psr11/Container.php                                                                 |   55 
 vendor/topthink/framework/src/think/facade/Route.php                                                                |   84 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoUploadMedia.php                                                     |   49 
 vendor/xaboy/form-builder/src/UI/Elm/Components/Cascader.php                                                        |  126 
 vendor/workerman/crontab/composer.json                                                                              |   34 
 vendor/qiniu/php-sdk/.gitignore                                                                                     |   12 
 vendor/xaboy/form-builder/src/UI/Elm/Validate.php                                                                   |   20 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/Run.php                                                 |   65 
 vendor/workerman/channel/src/Server.php                                                                             |  108 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php                                |   95 
 vendor/volcengine/volc-sdk-php/examples/Rtc/rtc-openapi-demo/composer.json                                          |   19 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooterDrawing.php                                |   24 
 vendor/xaboy/form-builder/src/UI/Iview/Components/Switches.php                                                      |   93 
 vendor/qiniu/php-sdk/phpunit.xml.dist                                                                               |   18 
 vendor/symfony/translation/Dumper/CsvFileDumper.php                                                                 |   63 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoCreateVideoClassification.php                                       |   30 
 vendor/symfony/polyfill-intl-idn/Resources/unidata/virama.php                                                       |   65 
 vendor/xaboy/form-builder/src/UI/Elm/Traits/RateFactoryTrait.php                                                    |   32 
 vendor/symfony/http-foundation/Tests/Session/Storage/MockFileSessionStorageTest.php                                 |  127 
 vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Request/ImpRetrieveJobRequest.php                             |   65 
 vendor/topthink/think-orm/src/db/concern/ParamsBind.php                                                             |  106 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListSnapshotsResponse.php                         |  119 
 vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php                                                  |  375 
 vendor/xaboy/form-builder/src/UI/Iview/Traits/CheckBoxFactoryTrait.php                                              |   31 
 vendor/topthink/think-migration/src/Factory.php                                                                     |  313 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetAutomaticSpeechRecognitionForAuditResult.php   |   68 
 vendor/topthink/framework/src/think/console/input/Argument.php                                                      |  138 
 vendor/symfony/process/InputStream.php                                                                              |   90 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodSubmitDirectEditTaskAsyncRequest.php               |  239 
 vendor/psr/container/src/NotFoundExceptionInterface.php                                                             |   13 
 vendor/topthink/framework/src/think/middleware/SessionInit.php                                                      |   80 
 vendor/topthink/think-migration/src/Command.php                                                                     |   89 
 vendor/symfony/translation/Tests/DependencyInjection/fixtures/ControllerArguments.php                               |   16 
 vendor/symfony/event-dispatcher/EventDispatcher.php                                                                 |  198 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoAuth.php                                                           |   66 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoDeleteVideoClassification.php                                       |   28 
 vendor/qiniu/php-sdk/examples/put_bucketQuota.php                                                                   |   22 
 vendor/topthink/think-migration/src/command/Migrate.php                                                             |  146 
 vendor/xaboy/form-builder/src/view/formScript.php                                                                   |   72 
 vendor/symfony/var-dumper/Tests/Caster/DateCasterTest.php                                                           |  390 
 vendor/pimple/pimple/ext/pimple/pimple.c                                                                            | 1114 
 vendor/topthink/think-migration/phinx/LICENSE                                                                       |    9 
 vendor/volcengine/volc-sdk-php/examples/Imp/job/DemoSubmitJob.php                                                   |   41 
 vendor/symfony/http-foundation/File/UploadedFile.php                                                                |  268 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeLiveStreamState.php                                        |   30 
 vendor/symfony/translation/Loader/LoaderInterface.php                                                               |   38 
 vendor/qiniu/php-sdk/tests/Qiniu/Tests/HttpTest.php                                                                 |   43 
 vendor/volcengine/volc-sdk-php/examples/Sts/DemoAssumeRole.php                                                      |   19 
 vendor/topthink/think-queue/src/queue/event/JobProcessing.php                                                       |   20 
 vendor/topthink/think-view/composer.json                                                                            |   20 
 vendor/topthink/think-captcha/composer.json                                                                         |   30 
 vendor/topthink/framework/src/think/service/ValidateService.php                                                     |   31 
 vendor/symfony/var-dumper/Caster/EnumStub.php                                                                       |   30 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Title.php                                                  |   65 
 vendor/topthink/framework/src/think/console/Input.php                                                               |  465 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeLiveStreamInfoByPage.php                                   |   34 
 vendor/topthink/framework/src/think/console/command/make/stubs/controller.stub                                      |   85 
 vendor/symfony/process/ProcessUtils.php                                                                             |   69 
 vendor/qiniu/php-sdk/examples/rsf_list_files.php                                                                    |   38 
 vendor/workerman/workerman/Lib/Timer.php                                                                            |  179 
 vendor/songshenzong/support/.gitignore                                                                              |    7 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Style.php                                            |  668 
 vendor/symfony/http-foundation/JsonResponse.php                                                                     |  220 
 vendor/xaboy/form-builder/src/UI/Elm/Components/Input.php                                                           |  111 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetMLFramesForAuditResponse.php                   |  119 
 vendor/topthink/framework/src/think/console/command/RunServer.php                                                   |   72 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/VQScoreTaskInfo.php                                 |  167 
 vendor/psr/http-client/composer.json                                                                                |   27 
 vendor/topthink/think-helper/src/Collection.php                                                                     |  651 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodApplyUploadInfoResult.php                         |   75 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/UpdateRelaySourceRequest.php                         |  201 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetWorkflowExecutionStatusResponse.php            |  119 
 vendor/xaboy/form-builder/src/UI/Elm/Components/Slider.php                                                          |   75 
 vendor/songshenzong/support/src/StringsServiceProvider.php                                                          |   37 
 vendor/psr/http-factory/src/ResponseFactoryInterface.php                                                            |   18 
 vendor/xaboy/form-builder/src/components/Radio.php                                                                  |   77 
 vendor/symfony/translation/Interval.php                                                                             |  112 
 vendor/qiniu/php-sdk/examples/get_corsRules.php                                                                     |   20 
 vendor/symfony/translation/Exception/ExceptionInterface.php                                                         |   21 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetSmartStrategyLitePlayInfoResult.php            |  177 
 vendor/symfony/var-dumper/Tests/Dumper/CliDumperTest.php                                                            |  534 
 vendor/topthink/think-captcha/assets/ttfs/1.ttf                                                                     |    0 
 vendor/topthink/think-captcha/assets/zhttfs/1.ttf                                                                   |    0 
 vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php                                       |  100 
 vendor/topthink/think-orm/src/db/Where.php                                                                          |  182 
 vendor/topthink/framework/src/think/cache/driver/File.php                                                           |  304 
 vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Command.php                                                                 |   39 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php                                     |  240 
 vendor/topthink/framework/src/think/Db.php                                                                          |  117 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoUpdateResourceTags.php                                              |   23 
 vendor/symfony/http-foundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php                                |   96 
 vendor/symfony/translation/Tests/fixtures/empty.csv                                                                 |    0 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeClosedStreamInfoByPageRequest.php            |  411 
 vendor/symfony/translation/Extractor/PhpExtractor.php                                                               |  278 
 vendor/pimple/pimple/README.rst                                                                                     |  326 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/PasswordHasher.php                                        |  100 
 vendor/topthink/think-factory/.gitignore                                                                            |    4 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/ListVQScoreTaskResponse.php                         |  119 
 vendor/topthink/framework/src/think/middleware/LoadLangPack.php                                                     |  118 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetWorkflowExecutionStatusRequest.php              |   65 
 vendor/symfony/translation/Loader/IcuResFileLoader.php                                                              |   91 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/Inspection.php                                       |  109 
 vendor/songshenzong/support/src/Strings.php                                                                         |   16 
 vendor/xaboy/form-builder/src/Contract/FormComponentInterface.php                                                   |   20 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeLiveStreamInfoByPageResponse.php            |  119 
 vendor/qiniu/php-sdk/examples/rs_batch_delete.php                                                                   |   28 
 vendor/symfony/translation/Tests/fixtures/resname.xlf                                                               |   22 
 vendor/pimple/pimple/ext/pimple/.gitignore                                                                          |   30 
 vendor/symfony/var-dumper/Tests/Caster/ExceptionCasterTest.php                                                      |  247 
 vendor/xaboy/form-builder/src/UI/Iview/Components/InputNumber.php                                                   |   51 
 vendor/symfony/polyfill-intl-normalizer/Resources/unidata/combiningClass.php                                        |  876 
 vendor/symfony/translation/Tests/fixtures/resourcebundle/corrupted/resources.dat                                    |    1 
 vendor/topthink/think-image/LICENSE                                                                                 |  201 
 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_lax.expected                      |    9 
 vendor/topthink/think-captcha/.gitignore                                                                            |    3 
 vendor/xaboy/form-builder/src/components/Rate.php                                                                   |   63 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/StageDetail.php                                      |  315 
 vendor/symfony/polyfill-intl-idn/Resources/unidata/DisallowedRanges.php                                             |  375 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/ClosedStream.php                                    |  277 
 vendor/pimple/pimple/ext/pimple/tests/013.phpt                                                                      |   33 
 vendor/spatie/macroable/composer.json                                                                               |   40 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetWorkflowExecution                                                |   26 
 vendor/qiniu/php-sdk/examples/get_bucketinfo.php                                                                    |   20 
 vendor/symfony/translation/Tests/DependencyInjection/TranslationPathsPassTest.php                                   |   89 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodSubmitDirectEditTaskAsyncResponse.php             |  123 
 vendor/symfony/var-dumper/Caster/DsCaster.php                                                                       |   68 
 vendor/topthink/think-orm/src/facade/Db.php                                                                         |   86 
 vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/ServiceResponseException.php                                      |  189 
 vendor/qiniu/php-sdk/examples/upload_and_pfop.php                                                                   |   38 
 vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/en.txt                                                 |    3 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DefaultValueBinder.php                                      |   82 
 vendor/psr/simple-cache/src/CacheInterface.php                                                                      |  114 
 vendor/psr/http-factory/composer.json                                                                               |   35 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetPlayInfoResponse.php                           |  119 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCreateVideoClassificationData.php                 |   58 
 vendor/pimple/pimple/src/Pimple/Exception/FrozenServiceException.php                                                |   45 
 vendor/topthink/think-filesystem/composer.json                                                                      |   33 
 vendor/symfony/var-dumper/Caster/DoctrineCaster.php                                                                 |   60 
 vendor/xaboy/form-builder/src/UI/Elm/Traits/TimePickerFactoryTrait.php                                              |   61 
 vendor/psr/cache/composer.json                                                                                      |   25 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/VQScoreTaskListInfo.php                             |  167 
 vendor/symfony/translation/Util/ArrayConverter.php                                                                  |   99 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Cell/Comment.php                                      |   30 
 vendor/volcengine/volc-sdk-php/examples/Imp/job/DemoKillJob.php                                                     |   31 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodCreateSpaceRequest.php                             |  205 
 vendor/symfony/translation/README.md                                                                                |   13 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeIPInfo.php                                                  |   19 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/Clip.php                                             |   99 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Properties.php                                          |  629 
 vendor/symfony/http-foundation/Session/Storage/Proxy/NativeProxy.php                                                |   40 
 vendor/symfony/translation-contracts/composer.json                                                                  |   33 
 vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php                                                          |   35 
 vendor/qcloud/cos-sdk-v5/sample/putBucketAcl.php                                                                    |   41 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Conditional.php                                            |  274 
 vendor/qiniu/php-sdk/src/Qiniu/Config.php                                                                           |  140 
 vendor/volcengine/volc-sdk-php/composer.json                                                                        |   25 
 vendor/topthink/think-image/tests/images/test.ttf                                                                   |    0 
 vendor/symfony/http-foundation/Tests/File/Fixtures/case-sensitive-mime-type.xlsm                                    |    0 
 vendor/symfony/polyfill-intl-idn/Idn.php                                                                            |  915 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Upload/SnapshotTimeInput.php                                         |   18 
 vendor/qiniu/php-sdk/src/Qiniu/Storage/ResumeUploader.php                                                           |  169 
 vendor/symfony/event-dispatcher/README.md                                                                           |   15 
 vendor/topthink/framework/src/think/console/input/Definition.php                                                    |  375 
 vendor/topthink/think-orm/src/model/concern/OptimLock.php                                                           |   85 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListCdnTasksResponse.php                          |  119 
 vendor/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php                                        |  101 
 vendor/qiniu/php-sdk/CONTRIBUTING.md                                                                                |   30 
 vendor/volcengine/volc-sdk-php/examples/VoiceNotify/CommitResourceUpload.php                                        |   19 
 vendor/symfony/translation/Exception/LogicException.php                                                             |   21 
 vendor/xaboy/form-builder/src/UI/Iview/Traits/CascaderFactoryTrait.php                                              |   80 
 vendor/topthink/think-migration/phinx/src/Phinx/Seed/AbstractSeed.php                                               |  215 
 vendor/xaboy/form-builder/images/demo02.jpg                                                                         |    0 
 vendor/topthink/framework/src/think/route/Domain.php                                                                |  183 
 vendor/qiniu/php-sdk/examples/rs_buckets.php                                                                        |   19 
 vendor/xaboy/form-builder/src/Template/createScript.min.php                                                         |    1 
 vendor/topthink/framework/src/think/console/command/Clear.php                                                       |   85 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDescribeIPInfoRequest.php                          |   65 
 vendor/pimple/pimple/src/Pimple/ServiceProviderInterface.php                                                        |   46 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodRetrieveTranscodeResultResponse.php               |  119 
 vendor/qcloud_sts/qcloud-sts-sdk/README.md                                                                          |   61 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/RestartPullToPushTaskRequest.php                     |   65 
 vendor/topthink/framework/tests/MiddlewareTest.php                                                                  |  108 
 vendor/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php                                                 |  161 
 vendor/topthink/think-helper/src/contract/Jsonable.php                                                              |    8 
 vendor/xaboy/form-builder/demo/demo.php                                                                             |   34 
 vendor/topthink/think-image/tests/images/test.png                                                                   |    0 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnConfig.php                                               |   19 
 vendor/symfony/var-dumper/Tests/Fixtures/xml_reader.xml                                                             |   10 
 vendor/qcloud/cos-sdk-v5/sample/listMultipartUploads.php                                                            |   31 
 vendor/workerman/channel/test/server.php                                                                            |   22 
 vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/MysqlAdapter.php                                         | 1145 
 vendor/topthink/think-orm/src/db/concern/WhereQuery.php                                                             |  532 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCommonConfigInfo.php                              |  133 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Font.php                                                  |  759 
 vendor/qiniu/php-sdk/examples/rs_batch_copy.php                                                                     |   36 
 vendor/symfony/http-foundation/Session/Storage/Proxy/AbstractProxy.php                                              |  122 
 vendor/qiniu/php-sdk/examples/get_bucketList.php                                                                    |   20 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetPlayInfo.php                                                     |   42 
 vendor/psr/http-factory/.pullapprove.yml                                                                            |    7 
 vendor/xaboy/form-builder/src/Contract/AnnotationInterface.php                                                      |   29 
 vendor/symfony/http-foundation/Tests/BinaryFileResponseTest.php                                                     |  365 
 vendor/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php                            |   23 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateVideoClassificationResponse.php             |   75 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoRecordPreset.php                                                   |   59 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/RequestVod.php                                    |  398 
 vendor/qiniu/php-sdk/src/Qiniu/Rtc/AppClient.php                                                                    |  209 
 vendor/symfony/process/Tests/SignalListener.php                                                                     |   21 
 vendor/topthink/framework/src/think/facade/Cookie.php                                                               |   40 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodCreateVideoClassificationResponse.php             |  119 
 vendor/topthink/framework/tests/DispatchTest.php                                                                    |   32 
 vendor/xaboy/form-builder/src/Contract/ConfigInterface.php                                                          |   44 
 vendor/symfony/process/Pipes/UnixPipes.php                                                                          |  153 
 vendor/topthink/think-queue/src/config.php                                                                          |   38 
 vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoQuerySubscriptionForList.php                               |   20 
 vendor/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php                                               |    5 
 vendor/xaboy/form-builder/src/UI/Iview/Components/Hidden.php                                                        |   57 
 vendor/xaboy/form-builder/src/Handle/ElmFormHandle.php                                                              |   30 
 vendor/topthink/framework/src/think/view/driver/Php.php                                                             |  191 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Security.php                                            |  205 
 vendor/topthink/framework/src/think/console/command/make/stubs/model.stub                                           |   14 
 vendor/qiniu/php-sdk/examples/rs_change_type.php                                                                    |   20 
 vendor/symfony/var-dumper/Caster/SplCaster.php                                                                      |  220 
 vendor/symfony/translation/Tests/Dumper/YamlFileDumperTest.php                                                      |   45 
 vendor/topthink/framework/src/think/console/command/make/stubs/middleware.stub                                      |   19 
 vendor/volcengine/volc-sdk-php/examples/Billing/DemoListBillOverviewByProd.php                                      |   23 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/KillStreamRequest.php                                |  145 
 vendor/topthink/think-orm/src/db/Connection.php                                                                     |  332 
 vendor/qcloud_sts/qcloud-sts-sdk/src/Sts.php                                                                        |  166 
 vendor/xaboy/form-builder/src/view/form.php                                                                         |   17 
 vendor/volcengine/volc-sdk-php/examples/DemoSign.php                                                                |   23 
 vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoSelectNumberAndBindAXB.php                                 |   19 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodStartWorkflowResponse.php                         |  123 
 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.expected                           |   24 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/es/config                                     |   24 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoDeleteRelaySourceV2.php                                            |   28 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Theme.php                                            |   93 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Font.php                                                   |  548 
 vendor/qiniu/php-sdk/src/Qiniu/Http/Response.php                                                                    |  176 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeDomainResponse.php                          |  119 
 vendor/topthink/think-orm/src/db/builder/Mysql.php                                                                  |  421 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnOriginData.php                                           |   23 
 vendor/qcloud/cos-sdk-v5/sample/putObject.php                                                                       |   41 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoUploadMediaByUrl.php                                                |   27 
 vendor/volcengine/volc-sdk-php/examples/ImageX/DemoGetUploadToken.php                                               |   20 
 vendor/qiniu/php-sdk/examples/put_bucketMaxAge.php                                                                  |   21 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoDescribeVodSpaceStorageData.php                                     |   31 
 vendor/qiniu/php-sdk/examples/update_bucketLifecycleRule.php                                                        |   30 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/RC4.php                                               |   61 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Fill.php                                                   |  314 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/RichText.php                                            |  170 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer.php                      |   79 
 vendor/symfony/translation/Resources/data/parents.json                                                              |  136 
 vendor/topthink/framework/src/think/service/ModelService.php                                                        |   53 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php                                        | 1275 
 vendor/xaboy/form-builder/src/traits/component/ComponentOptionsTrait.php                                            |   81 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/MetaInf.php                                           |   60 
 vendor/topthink/think-image/tests/tmp/.gitignore                                                                    |    2 
 vendor/qiniu/php-sdk/examples/rs_change_status.php                                                                  |   20 
 vendor/topthink/think-orm/src/model/concern/Attribute.php                                                           |  651 
 vendor/symfony/translation/Resources/schemas/xliff-core-2.0.xsd                                                     |  411 
 vendor/topthink/think-orm/src/db/builder/Mongo.php                                                                  |  675 
 vendor/pimple/pimple/ext/pimple/tests/bench.phpb                                                                    |   51 
 vendor/symfony/polyfill-php72/README.md                                                                             |   27 
 vendor/psr/cache/README.md                                                                                          |    9 
 vendor/symfony/polyfill-intl-idn/Resources/unidata/deviation.php                                                    |    8 
 vendor/qiniu/php-sdk/tests/Qiniu/Tests/BucketTest.php                                                               |  227 
 vendor/volcengine/volc-sdk-php/src/Service/Imp/Imp.php                                                              |  146 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/Trend.php                                           |  120 
 vendor/xaboy/form-builder/src/UI/Iview/Traits/UploadFactoryTrait.php                                                |  110 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooter.php                                       |  490 
 vendor/topthink/think-queue/src/queue/connector/Database.php                                                        |  220 
 vendor/topthink/think-orm/src/model/relation/MorphOne.php                                                           |  280 
 vendor/symfony/http-foundation/LICENSE                                                                              |   19 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodASRInfoForAudit.php                               |  235 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetSpaceDetailResponse.php                        |  119 
 vendor/xaboy/form-builder/src/Contract/StyleInterface.php                                                           |   19 
 vendor/workerman/workerman/Connection/AsyncUdpConnection.php                                                        |  209 
 vendor/symfony/var-dumper/Cloner/Cursor.php                                                                         |   43 
 vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoClick2CallLite.php                                         |   18 
 vendor/qiniu/php-sdk/src/Qiniu/Zone.php                                                                             |   47 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/nl/functions                                  |  416 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/CodePage.php                                              |   92 
 vendor/pimple/pimple/src/Pimple/Tests/ServiceIteratorTest.php                                                       |   52 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF5.php                                       |   81 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnTopAccessUrlElement.php                        |  133 
 vendor/psr/http-factory/src/ServerRequestFactoryInterface.php                                                       |   24 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE.php                                                   |  567 
 vendor/topthink/think-orm/src/model/Pivot.php                                                                       |   53 
 vendor/psr/log/Psr/Log/LoggerAwareInterface.php                                                                     |   18 
 vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/MultipartUpload.php                                                         |  132 
 vendor/workerman/workerman/.gitignore                                                                               |    6 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAudioInfoForAudit.php                             |  133 
 vendor/symfony/http-foundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php                               |  204 
 vendor/topthink/think-captcha/assets/bgs/8.jpg                                                                      |    0 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetSmartStrategyLitePlayInfoResponse.php          |  123 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Escher.php                                            |  510 
 vendor/volcengine/volc-sdk-php/src/Service/AdBlocker.php                                                            |   68 
 vendor/qiniu/php-sdk/src/Qiniu/Etag.php                                                                             |   76 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAEDEventItemForAudit.php                          |  133 
 vendor/symfony/translation/Tests/fixtures/invalid-xml-resources.xlf                                                 |   23 
 vendor/topthink/framework/tests/ViewTest.php                                                                        |  127 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetSubtitleInfoListData.php                       |  235 
 vendor/symfony/translation/Tests/TranslatorTest.php                                                                 |  612 
 vendor/topthink/think-queue/src/queue/event/WorkerStopping.php                                                      |   24 
 vendor/qiniu/php-sdk/src/Qiniu/Storage/BucketManager.php                                                            | 1107 
 vendor/topthink/think-queue/tests/TestCase.php                                                                      |   23 
 vendor/psr/http-message/composer.json                                                                               |   26 
 vendor/xaboy/form-builder/src/UI/Iview/Components/Select.php                                                        |   73 
 vendor/topthink/framework/README.md                                                                                 |   84 
 vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php                                                       |   22 
 vendor/topthink/framework/src/think/event/HttpEnd.php                                                               |   19 
 vendor/topthink/think-multi-app/src/command/Clear.php                                                               |   66 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSpaceUploadConfigKey.php                          |   59 
 vendor/topthink/think-orm/src/Paginator.php                                                                         |  518 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodSmartStrategy.php                              |    0 
 vendor/pimple/pimple/ext/pimple/php_pimple.h                                                                        |  137 
 vendor/qiniu/php-sdk/examples/delete_bucketLifecycleRule.php                                                        |   21 
 vendor/topthink/think-migration/src/command/migrate/Rollback.php                                                    |  146 
 vendor/psr/http-factory/src/UploadedFileFactoryInterface.php                                                        |   34 
 vendor/symfony/polyfill-php72/composer.json                                                                         |   31 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodASRUtteranceWordForAudit.php                      |  167 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoCert.php                                                           |   58 
 vendor/topthink/think-migration/src/command/seed/Create.php                                                         |   83 
 vendor/xaboy/form-builder/src/Helper.php                                                                            |   64 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VisualQuality.php                                    |  235 
 vendor/symfony/var-dumper/Caster/ArgsStub.php                                                                       |   80 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/sv/functions                                  |  408 
 vendor/volcengine/volc-sdk-php/examples/VoiceNotify/PauseTask.php                                                   |   14 
 vendor/symfony/translation/Tests/Loader/LocalizedTestCase.php                                                       |   24 
 vendor/qiniu/php-sdk/src/Qiniu/Cdn/CdnManager.php                                                                   |  192 
 vendor/pimple/pimple/ext/pimple/README.md                                                                           |   12 
 vendor/symfony/var-dumper/Caster/SymfonyCaster.php                                                                  |   51 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDescribeVodDomainBandwidthDataResponse.php        |  119 
 vendor/symfony/http-foundation/ServerBag.php                                                                        |  102 
 vendor/topthink/think-factory/LICENSE                                                                               |  201 
 vendor/pimple/pimple/src/Pimple/ServiceIterator.php                                                                 |   69 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListVideoClassificationsResponse.php              |  119 
 vendor/topthink/think-migration/phinx/src/Phinx/Migration/Migration.template.php.dist                               |   32 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetAudioEventDetectionForAudit.php                                  |   28 
 vendor/qiniu/php-sdk/tests/Qiniu/Tests/EtagTest.php                                                                 |   52 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoDeleteMedia.php                                                     |   28 
 vendor/topthink/framework/src/think/facade/Middleware.php                                                           |   42 
 vendor/symfony/translation/Extractor/ExtractorInterface.php                                                         |   38 
 vendor/workerman/workerman/Events/Libevent.php                                                                      |  227 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoUpdateRelaySourceV2.php                                            |   31 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetAudioEventDetectionForAuditResponse.php        |  119 
 vendor/xaboy/form-builder/src/UI/Elm/Traits/SelectFactoryTrait.php                                                  |   48 
 vendor/volcengine/volc-sdk-php/examples/Billing/DemoListBill.php                                                    |   24 
 vendor/xaboy/form-builder/images/demo04.jpg                                                                         |    0 
 vendor/psr/simple-cache/composer.json                                                                               |   25 
 vendor/symfony/var-dumper/Tests/Fixtures/NotLoadableClass.php                                                       |    7 
 vendor/symfony/event-dispatcher/LICENSE                                                                             |   19 
 vendor/topthink/think-image/composer.json                                                                           |   26 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE.php               |   89 
 vendor/pimple/pimple/composer.json                                                                                  |   29 
 vendor/symfony/translation/Tests/fixtures/empty.yml                                                                 |    0 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodListCdnTopAccessUrlResult.php                     |   65 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetSpaceDetailRequest.php                          |   65 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Legend.php                                                 |  157 
 vendor/symfony/translation/Tests/fixtures/malformed.json                                                            |    3 
 vendor/topthink/framework/src/think/cache/driver/Memcached.php                                                      |  221 
 vendor/topthink/think-orm/src/model/Collection.php                                                                  |  250 
 vendor/symfony/translation/Tests/MessageSelectorTest.php                                                            |  140 
 vendor/volcengine/volc-sdk-php/examples/VoiceNotify/StopTask.php                                                    |   14 
 vendor/volcengine/volc-sdk-php/src/Service/Notify.php                                                               |  324 
 vendor/topthink/think-queue/phpunit.xml.dist                                                                        |   30 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Rels.php                                             |  449 
 vendor/symfony/http-foundation/ExpressionRequestMatcher.php                                                         |   47 
 vendor/topthink/think-orm/src/db/connector/Mongo.php                                                                | 1167 
 vendor/pimple/pimple/src/Pimple/Tests/Psr11/ContainerTest.php                                                       |   77 
 vendor/topthink/think-migration/src/command/Seed.php                                                                |   73 
 vendor/xaboy/form-builder/src/interfaces/FormComponentInterFace.php                                                 |   25 
 vendor/topthink/think-migration/src/FactoryBuilder.php                                                              |  437 
 vendor/topthink/think-image/src/image/gif/Decoder.php                                                               |  207 
 vendor/symfony/translation/Tests/MessageCatalogueTest.php                                                           |  243 
 vendor/symfony/translation/Tests/Extractor/PhpExtractorTest.php                                                     |  101 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetOriginalPlayInfoResponse.php                   |  119 
 vendor/volcengine/volc-sdk-php/src/Service/ImageX.php                                                               |  433 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnAccessLogInfo.php                              |   99 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoListVideoClassifications.php                                        |   28 
 vendor/qiniu/php-sdk/examples/rs_move.php                                                                           |   22 
 vendor/xaboy/form-builder/src/components/Tree.php                                                                   |  124 
 vendor/pimple/pimple/ext/pimple/tests/015.phpt                                                                      |   17 
 vendor/symfony/process/PhpProcess.php                                                                               |   75 
 vendor/symfony/psr-http-message-bridge/HttpFoundationFactoryInterface.php                                           |   43 
 vendor/topthink/think-queue/src/queue/Queueable.php                                                                 |   46 
 vendor/xaboy/form-builder/src/Driver/FormOptionsComponent.php                                                       |   29 
 vendor/symfony/translation/Tests/Loader/XliffFileLoaderTest.php                                                     |  331 
 vendor/symfony/http-foundation/Tests/Session/Storage/MockArraySessionStorageTest.php                                |  131 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnStatisticsData.php                             |  139 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeEdgeStatisticalData.php                                     |   26 
 vendor/symfony/psr-http-message-bridge/.gitignore                                                                   |    4 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetAudioInfoForAuditResponse.php                  |  119 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodThumbInfo.php                                     |  303 
 vendor/symfony/translation/Tests/Command/XliffLintCommandTest.php                                                   |  218 
 vendor/topthink/think-image/phpunit.xml                                                                             |   20 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListDomainResponse.php                            |  123 
 vendor/xaboy/form-builder/src/UI/Iview/Components/Frame.php                                                         |   83 
 vendor/workerman/workerman/Events/Select.php                                                                        |  340 
 vendor/topthink/framework/src/think/File.php                                                                        |  204 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUrlUploadRequest.php                               |   99 
 vendor/xaboy/form-builder/src/traits/form/FormFrameTrait.php                                                        |  127 
 vendor/psr/http-factory/src/UriFactoryInterface.php                                                                 |   17 
 vendor/xaboy/form-builder/src/UI/Elm/Components/ColorPicker.php                                                     |   47 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoListSpace.php                                                       |   28 
 vendor/qcloud/cos-sdk-v5/sample/getBucketLifecycle.php                                                              |   25 
 vendor/symfony/var-dumper/Cloner/VarCloner.php                                                                      |  295 
 vendor/symfony/var-dumper/Exception/ThrowingCasterException.php                                                     |   26 
 vendor/topthink/think-migration/src/Service.php                                                                     |   51 
 vendor/symfony/translation/Catalogue/AbstractOperation.php                                                          |  157 
 vendor/pimple/pimple/LICENSE                                                                                        |   19 
 vendor/topthink/framework/src/think/session/driver/Cache.php                                                        |   50 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodPrivateDrmPlayAuthInfo.php                        |   99 
 vendor/qiniu/php-sdk/examples/delete_bucket.php                                                                     |   20 
 vendor/symfony/psr-http-message-bridge/README.md                                                                    |   20 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReader.php                                               |  133 
 vendor/topthink/think-image/tests/images/test.gif                                                                   |    0 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeAccountingData.php                                          |   22 
 vendor/symfony/translation/Tests/DependencyInjection/fixtures/ServiceArguments.php                                  |   12 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetAutomaticSpeechRecognitionForAuditResponse.php |  119 
 vendor/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php                                        |  918 
 vendor/topthink/think-orm/src/model/relation/MorphToMany.php                                                        |  458 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetSpaceDetail.php                                                  |   27 
 vendor/topthink/think-orm/src/db/ConnectionInterface.php                                                            |  190 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf.php                                                   |  253 
 vendor/topthink/framework/src/think/exception/HttpResponseException.php                                             |   37 
 vendor/xaboy/form-builder/src/UI/Iview/Traits/TimePickerFactoryTrait.php                                            |   61 
 vendor/qiniu/php-sdk/src/Qiniu/Sms/Sms.php                                                                          |  337 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/ResumeStreamResponse.php                            |   75 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDomainInstanceInfos.php                           |   99 
 vendor/volcengine/volc-sdk-php/src/Service/BusinessSecurity.php                                                     |  489 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/DescribeDenyConfigResult.php                        |   65 
 vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoUpdateAXN.php                                              |   19 
 vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/AdapterFactory.php                                       |  181 
 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.php                            |    8 
 vendor/symfony/polyfill-intl-idn/README.md                                                                          |   12 
 vendor/volcengine/volc-sdk-php/examples/AdBlocker/DemoAdBlock.php                                                   |   12 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateSpace.php                                                     |   30 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodFileSubtitleInfo.php                              |   99 
 vendor/qcloud/cos-sdk-v5/sample/headBucket.php                                                                      |   25 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetAllPlayInfoResponse.php                        |  119 
 vendor/songshenzong/support/src/StringsFacade.php                                                                   |   21 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/PHP Charting Libraries.txt                        |   20 
 vendor/workerman/crontab/src/Parser.php                                                                             |  171 
 vendor/symfony/var-dumper/Tests/Command/Descriptor/CliDescriptorTest.php                                            |  173 
 vendor/symfony/translation/Formatter/ChoiceMessageFormatterInterface.php                                            |   32 
 vendor/volcengine/volc-sdk-php/examples/Visual/DemoHumanSegment.php                                                 |   13 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/CDNSnapshotHistoryInfo.php                          |  109 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnAccessLogElement.php                           |  201 
 vendor/topthink/think-captcha/assets/bgs/6.jpg                                                                      |    0 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodMedia.php                                      |    0 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/TranscodeAudioOverride.php                           |  136 
 vendor/symfony/http-foundation/Session/Storage/SessionStorageInterface.php                                          |  137 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateSpaceUploadConfig.php                                         |   29 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fr/config                                     |   24 
 vendor/symfony/var-dumper/.gitignore                                                                                |    3 
 vendor/topthink/framework/src/think/console/command/make/stubs/listener.stub                                        |   17 
 vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/AbstractProxyTest.php                                    |  113 
 vendor/xaboy/form-builder/src/UI/Iview/Traits/HiddenFactoryTrait.php                                                |   31 
 vendor/xaboy/form-builder/src/Annotation/AnnotationReader.php                                                       |  122 
 vendor/symfony/http-foundation/Tests/RequestStackTest.php                                                           |   70 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DeleteDenyConfigResponse.php                        |   75 
 vendor/topthink/framework/src/think/console/command/ServiceDiscover.php                                             |   52 
 vendor/spatie/macroable/CONTRIBUTING.md                                                                             |   55 
 vendor/symfony/var-dumper/Dumper/ContextProvider/SourceContextProvider.php                                          |  126 
 vendor/topthink/think-filesystem/phpunit.xml.dist                                                                   |   25 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat.php                                           |  873 
 vendor/symfony/var-dumper/Caster/DateCaster.php                                                                     |  122 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php                                                     |  159 
 vendor/symfony/var-dumper/Tests/Fixtures/dump_server.php                                                            |   38 
 vendor/symfony/translation/Formatter/IntlFormatterInterface.php                                                     |   27 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListSnapshotsRequest.php                           |   58 
 vendor/topthink/think-queue/tests/QueueTest.php                                                                     |   49 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/IValueBinder.php                                            |   16 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodClassification.php                                |  257 
 vendor/volcengine/volc-sdk-php/src/Service/GameProduct.php                                                          |   73 
 vendor/qiniu/php-sdk/examples/image_url_builder.php                                                                 |   74 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/PlayURL.php                                         |  167 
 vendor/workerman/channel/src/Client.php                                                                             |  275 
 vendor/xaboy/form-builder/src/UI/Iview/Components/TreeData.php                                                      |  106 
 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_urlencode.php                              |   12 
 vendor/symfony/process/Tests/ErrorProcessInitiator.php                                                              |   36 
 vendor/symfony/polyfill-intl-idn/composer.json                                                                      |   45 
 vendor/symfony/translation/Tests/Loader/IcuDatFileLoaderTest.php                                                    |   64 
 vendor/topthink/think-orm/src/model/relation/BelongsTo.php                                                          |  331 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Dimension.php                                          |  163 
 vendor/topthink/think-captcha/src/facade/Captcha.php                                                                |   18 
 vendor/xaboy/form-builder/src/components/DatePicker.php                                                             |  172 
 vendor/topthink/think-helper/README.md                                                                              |   33 
 vendor/symfony/translation/Tests/fixtures/escaped-id.po                                                             |    8 
 vendor/topthink/think-template/README.md                                                                            |   70 
 vendor/symfony/process/ExecutableFinder.php                                                                         |   88 
 vendor/volcengine/volc-sdk-php/src/Base/V4Curl.php                                                                  |  330 
 vendor/spatie/macroable/.styleci.yml                                                                                |    4 
 vendor/symfony/translation/Tests/DependencyInjection/TranslationExtractorPassTest.php                               |   66 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodMediaBasicInfo.php                                |  347 
 vendor/xaboy/form-builder/src/Annotation/Validate/Len.php                                                           |   39 
 vendor/topthink/think-queue/src/queue/Listener.php                                                                  |  162 
 vendor/songshenzong/support/composer.json                                                                           |   60 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDescribeVodSpaceStorageDataResponse.php           |  105 
 vendor/qiniu/php-sdk/examples/rs_fetch.php                                                                          |   34 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeForbiddenStreamInfoByPageRequest.php         |  255 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoSubmitBlockTask.php                                                 |   20 
 vendor/xaboy/form-builder/src/components/Frame.php                                                                  |  119 
 vendor/topthink/think-queue/src/queue/command/stubs/jobs.stub                                                       |   20 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php                                             |  104 
 vendor/pimple/pimple/ext/pimple/tests/002.phpt                                                                      |   15 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateVideoClassificationRequest.php               |  112 
 vendor/symfony/var-dumper/Caster/IntlCaster.php                                                                     |  170 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoStartCdnDomain.php                                                  |   19 
 vendor/volcengine/volc-sdk-php/src/Base/Model/SignParam.php                                                         |  150 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateMediaPublishStatusRequest.php                |   99 
 vendor/topthink/framework/src/think/console/Table.php                                                               |  300 
 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_urlencode.expected                         |   10 
 vendor/topthink/framework/src/think/facade/App.php                                                                  |   59 
 vendor/songshenzong/support/src/Time.php                                                                            |   72 
 vendor/symfony/http-foundation/Session/Flash/FlashBagInterface.php                                                  |   93 
 vendor/qiniu/php-sdk/CHANGELOG.md                                                                                   |  105 
 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.php                                 |    8 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoSubmitRefreshTask.php                                               |   20 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/VQScoreInfo.php                                     |  337 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/en/uk/config                                  |    8 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing/Shadow.php                                     |  289 
 vendor/symfony/event-dispatcher/GenericEvent.php                                                                    |  175 
 vendor/symfony/psr-http-message-bridge/Tests/Factory/PsrHttpFactoryTest.php                                         |   29 
 vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Request/ImpSubmitJobRequest.php                               |  167 
 vendor/symfony/translation/Catalogue/OperationInterface.php                                                         |   77 
 vendor/symfony/http-foundation/phpunit.xml.dist                                                                     |   31 
 vendor/qiniu/php-sdk/examples/persistent_fop_status.php                                                             |   18 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/IRenderer.php                                     |   22 
 vendor/symfony/translation/Exception/RuntimeException.php                                                           |   21 
 vendor/psr/log/Psr/Log/LoggerTrait.php                                                                              |  142 
 vendor/symfony/translation/Writer/TranslationWriterInterface.php                                                    |   34 
 vendor/volcengine/volc-sdk-php/examples/Cdn/init.php                                                                |   21 
 vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/AdapterInterface.php                                     |  510 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodCreateSpaceResponse.php                           |   79 
 vendor/symfony/translation/DependencyInjection/TranslatorPathsPass.php                                              |  144 
 vendor/topthink/think-orm/src/db/exception/ModelNotFoundException.php                                               |   44 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidator.php                                           |   77 
 vendor/psr/http-message/src/StreamInterface.php                                                                     |  158 
 vendor/symfony/http-foundation/AcceptHeader.php                                                                     |  168 
 vendor/topthink/think-template/LICENSE                                                                              |  201 
 vendor/topthink/think-queue/src/queue/command/FlushFailed.php                                                       |   21 
 vendor/pimple/pimple/CHANGELOG                                                                                      |   59 
 vendor/volcengine/volc-sdk-php/src/Service/Iam.php                                                                  |   54 
 vendor/xaboy/form-builder/src/Template/createScript.php                                                             |   94 
 vendor/songshenzong/support/tests/OSTest.php                                                                        |   46 
 vendor/topthink/think-migration/phinx/src/Phinx/Db/Table/ForeignKey.php                                             |  252 
 vendor/topthink/framework/src/think/Env.php                                                                         |  201 
 vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoUpdateAXB.php                                              |   19 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/RequestLive.php                                  |  212 
 vendor/topthink/framework/src/think/facade/Config.php                                                               |   37 
 vendor/volcengine/volc-sdk-php/examples/VoiceNotify/ResumeTask.php                                                  |   13 
 vendor/symfony/http-foundation/RequestMatcherInterface.php                                                          |   27 
 vendor/topthink/framework/tests/ConfigTest.php                                                                      |   46 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/DefaultReadFilter.php                                     |   20 
 vendor/topthink/framework/src/think/console/Command.php                                                             |  504 
 vendor/symfony/translation/Tests/Loader/IcuResFileLoaderTest.php                                                    |   51 
 vendor/symfony/http-foundation/CHANGELOG.md                                                                         |  165 
 vendor/qiniu/php-sdk/src/Qiniu/Http/Request.php                                                                     |   18 
 vendor/topthink/think-migration/phinx/src/Phinx/Db/Table/Column.php                                                 |  550 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/TranscodeResult.php                                  |  143 
 vendor/symfony/translation/Tests/DependencyInjection/TranslationPassTest.php                                        |  122 
 vendor/topthink/think-migration/phinx/src/Phinx/Migration/IrreversibleMigrationException.php                        |   39 
 vendor/topthink/think-captcha/src/CaptchaController.php                                                             |   20 
 vendor/topthink/think-helper/.gitignore                                                                             |    3 
 vendor/symfony/var-dumper/Caster/LinkStub.php                                                                       |  108 
 vendor/xaboy/form-builder/src/Form/IviewForm.php                                                                    |   24 
 vendor/qiniu/php-sdk/composer.json                                                                                  |   26 
 vendor/psr/http-message/LICENSE                                                                                     |   19 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodStartWorkflowRequest.php                           |  249 
 vendor/symfony/var-dumper/Tests/Caster/RedisCasterTest.php                                                          |   70 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodAddCallbackSubscriptionRequest.php                 |  145 
 vendor/psr/simple-cache/README.md                                                                                   |    8 
 vendor/xaboy/form-builder/src/UI/Iview/Traits/TreeFactoryTrait.php                                                  |   74 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoSubmitDirectEditTaskAsync.php                                       |   33 
 vendor/psr/http-factory/src/RequestFactoryInterface.php                                                             |   18 
 vendor/qiniu/php-sdk/examples/rs_prefetch.php                                                                       |   17 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListDomainRequest.php                              |   69 
 vendor/qiniu/php-sdk/docs/sms/example.php                                                                           |   70 
 vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php                                                    | 1096 
 vendor/topthink/framework/src/think/initializer/BootService.php                                                     |   26 
 vendor/topthink/framework/src/think/middleware/CheckRequestCache.php                                                |  183 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Row.php                                                |   74 
 vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php                                                       |  229 
 vendor/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php                                                     |   34 
 vendor/topthink/think-orm/src/model/relation/HasMany.php                                                            |  367 
 vendor/symfony/process/.gitignore                                                                                   |    3 
 vendor/symfony/psr-http-message-bridge/Tests/Fixtures/Response.php                                                  |   45 
 vendor/symfony/var-dumper/Resources/bin/var-dump-server                                                             |   63 
 vendor/qiniu/php-sdk/README.md                                                                                      |   75 
 vendor/symfony/http-foundation/Tests/File/MimeType/MimeTypeTest.php                                                 |   90 
 vendor/topthink/think-captcha/assets/bgs/3.jpg                                                                      |    0 
 vendor/xaboy/form-builder/src/Annotation/Validate/Max.php                                                           |   39 
 vendor/topthink/think-factory/src/Factory.php                                                                       |  115 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateSubtitleInfo.php                                              |   32 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodUploadAddress.php                                 |  167 
 vendor/symfony/process/PhpExecutableFinder.php                                                                      |  101 
 vendor/symfony/var-dumper/Caster/RedisCaster.php                                                                    |  150 
 vendor/xaboy/form-builder/src/components/TimePicker.php                                                             |  155 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fr/functions                                  |  416 
 vendor/topthink/think-queue/src/queue/FailedJob.php                                                                 |   47 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetMediaList.php                                                    |   36 
 vendor/symfony/http-foundation/Tests/File/FileTest.php                                                              |  180 
 vendor/symfony/var-dumper/Dumper/ContextProvider/RequestContextProvider.php                                         |   51 
 vendor/symfony/http-foundation/Tests/schema/http-status-codes.rng                                                   |   31 
 vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Tests/TestHelper.php                                                        |   48 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Xls.php                                                   |  279 
 vendor/xaboy/form-builder/src/UI/Iview/Traits/RateFactoryTrait.php                                                  |   32 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/DeLogoInfo.php                                       |  235 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/DomainListInfo.php                                  |   99 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodHeaderPair.php                                    |   99 
 vendor/symfony/http-foundation/Tests/File/FakeFile.php                                                              |   45 
 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_strict.expected                   |    9 
 vendor/symfony/polyfill-php70/composer.json                                                                         |   33 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateSpaceUploadConfigRequest.php                 |  141 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/br/config                                  |   24 
 vendor/symfony/psr-http-message-bridge/Tests/Fixtures/Message.php                                                   |   93 
 vendor/topthink/think-template/src/facade/Template.php                                                              |   83 
 vendor/xaboy/form-builder/src/traits/form/FormDatePickerTrait.php                                                   |  129 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DeleteRelaySourceResponse.php                       |   75 
 vendor/xaboy/form-builder/src/UI/Iview/Components/Tree.php                                                          |   87 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeDenyConfig.php                                             |   29 
 vendor/pimple/pimple/.gitignore                                                                                     |    3 
 vendor/topthink/think-orm/src/model/relation/HasOneThrough.php                                                      |  163 
 vendor/topthink/think-migration/src/command/stubs/factory.stub                                                      |   11 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/LiveStreamInfo.php                                  |   99 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeries.php                                             |  394 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeContentTasks.php                                            |   20 
 vendor/qiniu/php-sdk/examples/get_bucketQuota.php                                                                   |   20 
 vendor/qiniu/php-sdk/examples/cdn_refresh_urls_dirs.php                                                             |   52 
 vendor/symfony/translation/Tests/DependencyInjection/fixtures/ServiceSubscriber.php                                 |   19 
 vendor/topthink/think-helper/composer.json                                                                          |   22 
 vendor/qiniu/php-sdk/examples/rs_batch_stat.php                                                                     |   28 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Worksheet.php                                         | 4484 
 vendor/symfony/polyfill-php70/Php70.php                                                                             |   74 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnDomainTopData.php                                        |   23 
 vendor/symfony/http-foundation/Tests/FileBagTest.php                                                                |  175 
 vendor/topthink/think-orm/src/db/PDOConnection.php                                                                  | 1746 
 vendor/symfony/translation/Tests/fixtures/non-valid.xlf                                                             |   11 
 vendor/symfony/translation/Util/XliffUtils.php                                                                      |  163 
 vendor/symfony/var-dumper/Command/Descriptor/HtmlDescriptor.php                                                     |  119 
 vendor/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalComposition.php                                  |  945 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetAudioInfoForAuditRequest.php                    |   99 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodASRUtteranceForAudit.php                          |  235 
 vendor/xaboy/form-builder/src/traits/form/FormSwitchesTrait.php                                                     |   32 
 vendor/symfony/translation/Reader/TranslationReaderInterface.php                                                    |   30 
 vendor/psr/cache/LICENSE.txt                                                                                        |   19 
 vendor/symfony/http-foundation/Tests/Session/Flash/AutoExpireFlashBagTest.php                                       |  161 
 vendor/symfony/var-dumper/CHANGELOG.md                                                                              |   42 
 vendor/symfony/psr-http-message-bridge/HttpMessageFactoryInterface.php                                              |   43 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUploadMediaResponse.php                           |  119 
 vendor/topthink/framework/src/think/App.php                                                                         |  620 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUploadMediaRequest.php                             |  201 
 vendor/topthink/framework/logo.png                                                                                  |    0 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetMLFramesForAuditRequest.php                     |  439 
 vendor/symfony/var-dumper/Tests/Fixtures/dumb-var.php                                                               |   40 
 vendor/topthink/think-migration/src/Seeder.php                                                                      |   24 
 vendor/workerman/workerman/Events/React/StreamSelectLoop.php                                                        |   26 
 vendor/xaboy/form-builder/src/traits/form/FormCascaderTrait.php                                                     |   80 
 vendor/topthink/think-orm/src/model/concern/Conversion.php                                                          |  278 
 vendor/topthink/think-orm/src/model/relation/MorphMany.php                                                          |  353 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/es/functions                                  |  416 
 vendor/topthink/framework/src/think/route/dispatch/Controller.php                                                   |  194 
 vendor/topthink/think-queue/tests/WorkerTest.php                                                                    |  420 
 vendor/topthink/framework/src/think/event/HttpRun.php                                                               |   19 
 vendor/topthink/think-orm/src/db/exception/InvalidArgumentException.php                                             |   21 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodCdnStatisticsCommonResponse.php                   |  119 
 vendor/qiniu/php-sdk/examples/get_bucketinfos.php                                                                   |   20 
 vendor/psr/http-message/src/UploadedFileInterface.php                                                               |  123 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodQueryData.php                                     |   75 
 vendor/qiniu/php-sdk/test-env.sh                                                                                    |    4 
 vendor/symfony/var-dumper/Command/ServerDumpCommand.php                                                             |   99 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetWorkflowExecutionDetailRequest.php              |   65 
 vendor/psr/http-message/src/UriInterface.php                                                                        |  323 
 vendor/topthink/think-orm/src/Model.php                                                                             | 1060 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodEdit.php                                       |   39 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAEDTimeRangeForAudit.php                          |  133 
 vendor/xaboy/form-builder/src/UI/Elm/Components/Tooltip.php                                                         |   54 
 vendor/symfony/translation/Tests/fixtures/resources.json                                                            |    3 
 vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Business/JobExecution.php                                     |  323 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDescribeIPInfoResponse.php                        |  109 
 vendor/volcengine/volc-sdk-php/examples/Billing/DemoListBillDetail.php                                              |   26 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/CHANGELOG.TXT                                        |   16 
 vendor/symfony/translation/Loader/IcuDatFileLoader.php                                                              |   61 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/sv/config                                     |   24 
 vendor/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php                                          |   59 
 vendor/topthink/framework/src/think/initializer/RegisterService.php                                                 |   48 
 vendor/topthink/think-image/.travis.yml                                                                             |   22 
 vendor/symfony/http-foundation/Tests/ResponseTest.php                                                               | 1014 
 vendor/qiniu/php-sdk/examples/cdn_get_bandwidth.php                                                                 |   40 
 vendor/songshenzong/support/src/BashEchoHelpers.php                                                                 |   64 
 vendor/topthink/framework/src/think/Service.php                                                                     |   66 
 vendor/topthink/think-queue/src/queue/ShouldQueue.php                                                               |   17 
 vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/NoSuchBucketException.php                                         |    6 
 vendor/symfony/process/Exception/LogicException.php                                                                 |   21 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListCdnPvDataRequest.php                           |  201 
 vendor/symfony/translation-contracts/Test/TranslatorTest.php                                                        |  353 
 vendor/symfony/var-dumper/Resources/functions/dump.php                                                              |   43 
 vendor/qiniu/php-sdk/examples/bucket_lifecycleRule.php                                                              |   30 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetRecommendedPosterResponse.php                  |  119 
 vendor/pimple/pimple/ext/pimple/tests/001.phpt                                                                      |   45 
 vendor/symfony/http-foundation/Tests/Session/Storage/MetadataBagTest.php                                            |  139 
 vendor/topthink/think-image/.gitignore                                                                              |    4 
 vendor/workerman/workerman/Connection/ConnectionInterface.php                                                       |  125 
 vendor/topthink/think-factory/composer.json                                                                         |   22 
 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.expected              |   24 
 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_max_age.expected                           |   11 
 vendor/symfony/translation/TranslatorInterface.php                                                                  |   70 
 vendor/topthink/framework/src/think/route/RuleItem.php                                                              |  330 
 vendor/symfony/http-foundation/ApacheRequest.php                                                                    |   43 
 vendor/xaboy/form-builder/src/traits/form/FormSliderTrait.php                                                       |   48 
 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.expected                          |   15 
 vendor/symfony/translation/Tests/Dumper/XliffFileDumperTest.php                                                     |  130 
 vendor/symfony/translation/Exception/NotFoundResourceException.php                                                  |   21 
 vendor/symfony/http-foundation/File/Exception/FileException.php                                                     |   21 
 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.php                                |   10 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodCreateCdnPreloadTaskRequest.php                    |   99 
 vendor/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php                                       |   76 
 vendor/topthink/framework/tests/InteractsWithApp.php                                                                |   30 
 vendor/volcengine/volc-sdk-php/src/Service/Base/Models/GPBMetadata/Base.php                                         |    0 
 vendor/symfony/translation/Tests/fixtures/resources.xlf                                                             |   23 
 vendor/symfony/event-dispatcher/Debug/WrappedListener.php                                                           |   71 
 vendor/volcengine/volc-sdk-php/src/Base/SignatureTrait.php                                                          |   49 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/StreamManage.php                                 |    0 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDescribeVodDomainTrafficDataRequest.php            |  201 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Csv.php                                                   |  334 
 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php                             |   59 
 vendor/symfony/http-foundation/Tests/File/Fixtures/.unknownextension                                                |    1 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php                              |  122 
 vendor/symfony/http-foundation/Tests/IpUtilsTest.php                                                                |  104 
 vendor/topthink/think-captcha/assets/bgs/1.jpg                                                                      |    0 
 vendor/qcloud/cos-sdk-v5/sample/uploadPart.php                                                                      |   31 
 vendor/pimple/pimple/ext/pimple/tests/bench_shared.phpb                                                             |   25 
 vendor/qcloud/cos-sdk-v5/.gitignore                                                                                 |    2 
 vendor/topthink/think-queue/tests/bootstrap.php                                                                     |    3 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/nl/config                                     |   24 
 vendor/topthink/framework/src/think/console/command/RouteList.php                                                   |  128 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetHlsDecryptionKeyRequest.php                     |  133 
 vendor/qcloud/cos-sdk-v5/sample/restoreObject.php                                                                   |   30 
 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.php                   |   13 
 vendor/symfony/polyfill-php70/Resources/stubs/ParseError.php                                                        |    5 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Live.php                                                            | 1232 
 vendor/topthink/framework/tests/LogTest.php                                                                         |  130 
 vendor/topthink/think-queue/src/queue/command/FailedTable.php                                                       |   46 
 vendor/symfony/http-foundation/Tests/ResponseHeaderBagTest.php                                                      |  363 
 vendor/symfony/psr-http-message-bridge/Tests/Factory/DiactorosFactoryTest.php                                       |   32 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/RecordTaskFile.php                                  |  398 
 vendor/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalDecomposition.php                                | 2065 
 vendor/symfony/translation/Tests/fixtures/plurals.mo                                                                |    0 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDescribeVodDomainTrafficDataResponse.php          |  105 
 vendor/qiniu/php-sdk/examples/rs_stat.php                                                                           |   19 
 vendor/symfony/var-dumper/Caster/StubCaster.php                                                                     |   82 
 vendor/qcloud/cos-sdk-v5/LICENSE                                                                                    |   21 
 vendor/topthink/think-filesystem/tests/FilesystemTest.php                                                           |   66 
 vendor/topthink/think-migration/phinx/src/Phinx/Migration/AbstractTemplateCreation.php                              |   97 
 vendor/symfony/var-dumper/Caster/XmlResourceCaster.php                                                              |   61 
 vendor/symfony/var-dumper/phpunit.xml.dist                                                                          |   33 
 vendor/volcengine/volc-sdk-php/src/Base/Model/SignResult.php                                                        |  136 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Html.php                                                  |  839 
 vendor/symfony/translation/Tests/fixtures/withdoctype.xlf                                                           |   12 
 vendor/workerman/workerman/Connection/AsyncTcpConnection.php                                                        |  369 
 vendor/symfony/translation/Tests/Dumper/PhpFileDumperTest.php                                                       |   29 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/ITextElement.php                                        |   36 
 vendor/symfony/psr-http-message-bridge/Factory/HttpFoundationFactory.php                                            |  280 
 vendor/qiniu/php-sdk/src/Qiniu/Storage/UploadManager.php                                                            |  144 
 vendor/topthink/framework/src/think/console/command/make/Event.php                                                  |   35 
 vendor/topthink/framework/src/think/console/command/make/Subscribe.php                                              |   35 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDescribeVodDomainBandwidthDataRequest.php          |  235 
 vendor/symfony/polyfill-php70/Resources/stubs/Error.php                                                             |    5 
 vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/GPBMetadata/RequestImp.php                                    |   25 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodTranscodeInfo.php                                 |  459 
 vendor/qcloud_sts/qcloud-sts-sdk/.gitignore                                                                         |    6 
 vendor/symfony/var-dumper/Cloner/Data.php                                                                           |  423 
 vendor/xaboy/form-builder/src/UI/Elm/Components/DatePicker.php                                                      |  153 
 vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoQuerySubscription.php                                      |   17 
 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.php                               |    8 
 vendor/symfony/translation/Loader/FileLoader.php                                                                    |   65 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoListCdnDomains.php                                                  |   22 
 vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php                                              |    5 
 vendor/xaboy/form-builder/src/UI/Iview/Components/Button.php                                                        |   58 
 vendor/workerman/workerman/Protocols/Frame.php                                                                      |   61 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoSetCallbackEvent.php                                                |   30 
 vendor/symfony/translation/Tests/fixtures/fuzzy-translations.po                                                     |   10 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/MD5.php                                               |  184 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/EigenvalueDecomposition.php                          |  861 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetMediaInfosRequest.php                           |   65 
 vendor/songshenzong/support/src/StringsHelpers.php                                                                  |   13 
 vendor/topthink/framework/src/think/file/UploadedFile.php                                                           |  143 
 vendor/pimple/pimple/ext/pimple/tests/011.phpt                                                                      |   19 
 vendor/spatie/macroable/CHANGELOG.md                                                                                |    7 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/ResponseVod.php                                   |  208 
 vendor/topthink/think-migration/README.md                                                                           |    6 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/GeneratePlayURLResponse.php                         |  119 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/SnapshotOverride.php                                 |  133 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodStoreUriGroup.php                                 |   99 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Content.php                                           |  367 
 vendor/symfony/translation/Tests/fixtures/plurals.po                                                                |    5 
 vendor/symfony/http-foundation/AcceptHeaderItem.php                                                                 |  209 
 vendor/symfony/translation/Tests/Formatter/IntlFormatterTest.php                                                    |   96 
 vendor/workerman/workerman/Connection/UdpConnection.php                                                             |  191 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetPrivateDrmPlayAuth.php                                           |   30 
 vendor/qcloud/cos-sdk-v5/sample/deleteObject.php                                                                    |   26 
 vendor/symfony/translation/Tests/Loader/PhpFileLoaderTest.php                                                       |   50 
 vendor/topthink/framework/src/think/response/Xml.php                                                                |  127 
 vendor/xaboy/form-builder/src/components/Col.php                                                                    |   85 
 vendor/symfony/translation/Dumper/FileDumper.php                                                                    |  132 
 vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/fr.txt                                                 |    3 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Chart.php                                            |  567 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php                                            |   72 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAudioStreamMeta.php                               |  201 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DisableDomainResponse.php                           |   75 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php                                          |  505 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodBandwidthData.php                                 |   99 
 vendor/workerman/workerman/Protocols/Http.php                                                                       |  701 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeContentBlockTasks.php                                       |   21 
 vendor/qiniu/php-sdk/src/Qiniu/Processing/ImageUrlBuilder.php                                                       |  282 
 vendor/pimple/pimple/src/Pimple/Tests/PimpleServiceProviderInterfaceTest.php                                        |   76 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDeleteCdnDomain.php                                                 |   19 
 vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesser.php                                                    |  133 
 vendor/topthink/think-helper/LICENSE                                                                                |  201 
 vendor/pimple/pimple/src/Pimple/Psr11/ServiceLocator.php                                                            |   75 
 vendor/topthink/framework/src/think/Cache.php                                                                       |  197 
 vendor/topthink/think-image/tests/CropTest.php                                                                      |   67 
 vendor/symfony/event-dispatcher/phpunit.xml.dist                                                                    |   31 
 vendor/topthink/framework/src/think/exception/Handle.php                                                            |  332 
 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.expected                              |   20 
 vendor/symfony/var-dumper/Dumper/DataDumperInterface.php                                                            |   24 
 vendor/symfony/translation/TranslatorBagInterface.php                                                               |   33 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetAllPlayInfoRequest.php                          |  667 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DeleteDenyConfigRequest.php                          |  137 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetMediaInfos.php                                                   |   27 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeForbiddenStreamInfoByPageResponse.php       |  119 
 vendor/xaboy/form-builder/src/UI/Elm/Bootstrap.php                                                                  |   33 
 vendor/xaboy/form-builder/src/UI/Iview/Config.php                                                                   |  218 
 vendor/xaboy/form-builder/src/components/Input.php                                                                  |  143 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAEDInfoForAudit.php                               |   65 
 vendor/xaboy/form-builder/src/UI/Iview/Components/Slider.php                                                        |   61 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/IWriter.php                                               |   87 
 vendor/symfony/polyfill-php72/Php72.php                                                                             |  216 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/DenyConfig.php                                   |    0 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCreateCdnTaskResult.php                           |   65 
 vendor/symfony/var-dumper/Server/Connection.php                                                                     |   95 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoTransPreset.php                                                    |   54 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/EnableDomainRequest.php                              |   65 
 vendor/topthink/framework/src/think/Console.php                                                                     |  786 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetRecommendedPoster.php                                            |   27 
 vendor/xaboy/form-builder/src/traits/form/FormOptionTrait.php                                                       |   33 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetDirectEditResultResponse.php                   |  109 
 vendor/symfony/var-dumper/Caster/XmlReaderCaster.php                                                                |   77 
 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_strict.php                        |    8 
 vendor/symfony/process/Tests/ProcessFailedExceptionTest.php                                                         |  137 
 vendor/volcengine/volc-sdk-php/examples/VoiceNotify/BatchAppend.php                                                 |   24 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/HashTable.php                                                    |  175 
 vendor/symfony/translation/Tests/fixtures/valid.csv                                                                 |    4 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeEdgeTopStatisticalData.php                                  |   24 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/RelaySource.php                                  |    0 
 vendor/topthink/think-template/src/template/driver/File.php                                                         |   83 
 vendor/qiniu/php-sdk/examples/rs_batch_delete_after_days.php                                                        |   34 
 vendor/spatie/macroable/src/Macroable.php                                                                           |   75 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetDirectEditResult.php                                             |   29 
 vendor/symfony/translation/Tests/Loader/PoFileLoaderTest.php                                                        |  109 
 vendor/volcengine/volc-sdk-php/examples/Cdn/DemoAddResourceTags.php                                                 |   22 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/TranscodeVideoOverride.php                           |  177 
 vendor/symfony/http-foundation/BinaryFileResponse.php                                                               |  359 
 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php                       |   77 
 vendor/topthink/think-orm/src/db/connector/Sqlsrv.php                                                               |  122 
 vendor/symfony/polyfill-php70/bootstrap.php                                                                         |   27 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetBetterFramesForAuditResponse.php               |  119 
 vendor/symfony/var-dumper/Caster/CutArrayStub.php                                                                   |   30 
 vendor/symfony/var-dumper/Tests/Caster/CasterTest.php                                                               |  178 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/tr/config                                     |   24 
 vendor/topthink/framework/src/think/event/LogRecord.php                                                             |   29 
 vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoBindAXN.php                                                |   21 
 vendor/symfony/translation/Reader/TranslationReader.php                                                             |   63 
 vendor/pimple/pimple/src/Pimple/Tests/PimpleTest.php                                                                |  589 
 vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/ProxyAdapter.php                                         |  325 
 vendor/symfony/translation/Loader/ArrayLoader.php                                                                   |   66 
 vendor/symfony/translation/Dumper/IniFileDumper.php                                                                 |   45 
 vendor/topthink/framework/src/think/console/command/make/stubs/command.stub                                         |   26 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodVolumeInfo.php                                    |   99 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeCDNSnapshotHistoryRequest.php                |  323 
 vendor/symfony/process/Exception/ProcessTimedOutException.php                                                       |   69 
 vendor/topthink/framework/src/think/Pipeline.php                                                                    |  107 
 vendor/topthink/think-template/.gitignore                                                                           |    1 
 vendor/symfony/http-foundation/Session/Storage/Handler/NativeSessionHandler.php                                     |   24 
 vendor/psr/cache/src/CacheItemInterface.php                                                                         |  105 
 vendor/topthink/think-multi-app/src/Url.php                                                                         |  232 
 vendor/qiniu/php-sdk/examples/cdn_timestamp_antileech.php                                                           |   19 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/UpdateRelaySourceResponse.php                       |   75 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetBetterFramesForAuditResult.php                 |   65 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoDescribeVodDomainBandwidthData.php                                  |   32 
 vendor/songshenzong/support/src/Env.php                                                                             |  101 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetSubtitleInfoListRequest.php                     |  439 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateSpaceRequest.php                             |  167 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/ManagerPullPushDomainBindResponse.php               |   75 
 vendor/topthink/framework/src/think/facade/View.php                                                                 |   42 
 vendor/topthink/think-filesystem/README.md                                                                          |    5 
 vendor/symfony/http-foundation/Tests/ResponseFunctionalTest.php                                                     |   58 
 vendor/symfony/var-dumper/Dumper/ContextProvider/CliContextProvider.php                                             |   32 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageMargins.php                                        |  214 
 vendor/qiniu/php-sdk/tests/Qiniu/Tests/CdnManagerTest.php                                                           |   41 
 vendor/symfony/http-foundation/Cookie.php                                                                           |  292 
 vendor/symfony/process/Exception/ProcessFailedException.php                                                         |   54 
 vendor/symfony/translation/Loader/XliffFileLoader.php                                                               |  212 
 vendor/topthink/framework/phpunit.xml.dist                                                                          |   25 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeDenyConfigRequest.php                        |  137 
 vendor/xaboy/form-builder/src/Util.php                                                                              |   25 
 vendor/topthink/think-orm/src/db/connector/pgsql.sql                                                                |  117 
 vendor/symfony/polyfill-mbstring/README.md                                                                          |   13 
 vendor/xaboy/form-builder/src/UI/Iview/Traits/FrameFactoryTrait.php                                                 |  126 
 vendor/symfony/http-foundation/Exception/SuspiciousOperationException.php                                           |   20 
 vendor/symfony/var-dumper/VarDumper.php                                                                             |   56 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDeleteVideoClassificationResponse.php             |   75 
 vendor/psr/cache/src/CacheItemPoolInterface.php                                                                     |  138 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodStartWorkflowResult.php                           |   65 
 vendor/volcengine/volc-sdk-php/examples/VoiceNotify/UpdateTask.php                                                  |   21 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/LUDecomposition.php                                  |  282 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VolumeInfo.php                                       |  167 
 vendor/xaboy/form-builder/src/UI/Iview/Traits/SliderFactoryTrait.php                                                |   50 
 vendor/symfony/var-dumper/Tests/Fixtures/Twig.php                                                                   |   38 
 vendor/xaboy/form-builder/src/Annotation/Validate/ValidateAnnotation.php                                            |   44 
 vendor/psr/container/.gitignore                                                                                     |    3 
 vendor/topthink/think-view/.gitignore                                                                               |    1 
 vendor/topthink/think-captcha/assets/ttfs/5.ttf                                                                     |    0 
 vendor/pimple/pimple/phpunit.xml.dist                                                                               |   14 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodAppsManage.php                                 |    0 
 vendor/symfony/psr-http-message-bridge/Factory/DiactorosFactory.php                                                 |  175 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodASRLanguageDetailForAudit.php                     |   99 
 vendor/qiniu/php-sdk/examples/rs_download_urls.php                                                                  |   17 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeLiveStreamStateResponse.php                 |  119 
 vendor/symfony/translation/Tests/Dumper/MoFileDumperTest.php                                                        |   29 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods.php                                                   |  156 
 vendor/xaboy/form-builder/src/UI/Elm/Traits/SwitchesFactoryTrait.php                                                |   32 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoListVQScoreTask.php                                                |   31 
 vendor/topthink/framework/src/think/cache/driver/Memcache.php                                                       |  209 
 vendor/pimple/pimple/ext/pimple/tests/004.phpt                                                                      |   30 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/StopPullToPushTaskRequest.php                        |   65 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetOriginalPlayInfoResult.php                     |  405 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Security/XmlScanner.php                                   |  150 
 vendor/symfony/process/Pipes/AbstractPipes.php                                                                      |  178 
 vendor/qiniu/php-sdk/tests/Qiniu/Tests/DownloadTest.php                                                             |   25 
 vendor/pimple/pimple/ext/pimple/config.w32                                                                          |   13 
 vendor/topthink/framework/src/think/Cookie.php                                                                      |  232 
 vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php                                              |  106 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoSnapshotPreset.php                                                 |   47 
 vendor/psr/log/Psr/Log/Test/TestLogger.php                                                                          |  147 
 vendor/qiniu/php-sdk/examples/upload_verify_callback.php                                                            |   32 
 vendor/topthink/framework/src/think/route/dispatch/Callback.php                                                     |   30 
 vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/TokenListener.php                                                           |   48 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Xf.php                                                |  548 
 vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/BucketNotEmptyException.php                                       |    6 
 vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesserInterface.php                                           |   35 
 vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/PostgresAdapter.php                                      | 1182 
 vendor/symfony/translation/DataCollector/TranslationDataCollector.php                                               |  169 
 vendor/xaboy/form-builder/src/UI/Iview/Traits/RadioFactoryTrait.php                                                 |   32 
 vendor/symfony/event-dispatcher/Tests/GenericEventTest.php                                                          |  136 
 vendor/symfony/process/Process.php                                                                                  | 1653 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoPublish.php                                                         |   32 
 vendor/symfony/polyfill-php70/LICENSE                                                                               |   19 
 vendor/topthink/framework/src/think/session/driver/File.php                                                         |  249 
 vendor/topthink/think-image/src/image/Exception.php                                                                 |   18 
 vendor/volcengine/volc-sdk-php/examples/ImageX/DemoGetImageSegment.php                                              |   26 
 vendor/xaboy/form-builder/src/Annotation/Group.php                                                                  |   86 
 vendor/topthink/think-orm/src/db/builder/Oracle.php                                                                 |   95 
 vendor/songshenzong/support/src/Traits/Uri.php                                                                      |   73 
 vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php                              |  157 
 vendor/xaboy/form-builder/src/UI/Iview/Style/Col.php                                                                |  178 
 vendor/xaboy/form-builder/src/UI/Iview/Traits/ValidateFactoryTrait.php                                              |   73 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv.php                                                   |  533 
 vendor/songshenzong/support/src/OS.php                                                                              |   75 
 vendor/topthink/think-migration/src/Creator.php                                                                     |   77 
 vendor/symfony/translation/Tests/Dumper/FileDumperTest.php                                                          |   90 
 vendor/topthink/think-template/src/template/TagLib.php                                                              |  349 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeLiveStreamStateRequest.php                   |  175 
 vendor/symfony/http-foundation/.gitignore                                                                           |    3 
 vendor/topthink/think-multi-app/composer.json                                                                       |   28 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/File.php                                                  |  142 
 vendor/songshenzong/support/README.md                                                                               |   32 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/UpdateDenyConfigResponse.php                        |   75 
 vendor/symfony/process/Tests/NonStopableProcess.php                                                                 |   47 
 vendor/symfony/translation/Dumper/XliffFileDumper.php                                                               |  209 
 vendor/volcengine/volc-sdk-php/src/Base/Singleton.php                                                               |   30 
 vendor/topthink/framework/src/think/console/command/make/Listener.php                                               |   35 
 vendor/symfony/http-foundation/Exception/RequestExceptionInterface.php                                              |   21 
 vendor/symfony/http-foundation/File/Exception/AccessDeniedException.php                                             |   28 
 vendor/symfony/translation/Tests/fixtures/empty.json                                                                |    0 
 vendor/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php                                          |  256 
 vendor/topthink/think-template/composer.json                                                                        |   20 
 vendor/xaboy/form-builder/src/components/ColorPicker.php                                                            |   86 
 vendor/volcengine/volc-sdk-php/examples/DemoIam.php                                                                 |   21 
 vendor/topthink/think-helper/src/helper/Str.php                                                                     |  234 
 vendor/xaboy/form-builder/src/Contract/ValidateInterface.php                                                        |   25 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DeleteDomainRequest.php                              |   65 
 vendor/topthink/framework/src/think/facade/Request.php                                                              |  134 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodPlayInfoModel.php                                 |  483 
 vendor/symfony/http-foundation/README.md                                                                            |   14 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/CholeskyDecomposition.php                            |  147 
 vendor/symfony/http-foundation/File/MimeType/FileinfoMimeTypeGuesser.php                                            |   69 
 vendor/symfony/http-foundation/RequestMatcher.php                                                                   |  178 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/GridLines.php                                              |  454 
 vendor/xaboy/form-builder/src/UI/Iview/Components/ColorPicker.php                                                   |   49 
 vendor/symfony/http-foundation/Session/SessionBagInterface.php                                                      |   46 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSmartStrategyResponseStreamType.php               |   68 
 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/invalid_cookie_name.expected                      |    6 
 vendor/xaboy/form-builder/src/components/TreeData.php                                                               |  102 
 vendor/topthink/framework/src/think/console/output/formatter/Style.php                                              |  190 
 vendor/symfony/event-dispatcher/composer.json                                                                       |   44 
 vendor/topthink/framework/.gitignore                                                                                |   10 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeVQScoreTaskRequest.php                       |   65 
 vendor/topthink/think-multi-app/src/Service.php                                                                     |   32 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric.php                                              |  882 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php                                      |  278 
 vendor/topthink/think-orm/src/model/relation/MorphTo.php                                                            |  332 
 vendor/workerman/workerman/Autoloader.php                                                                           |   69 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/de/config                                     |   24 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/BaseParserClass.php                                  |   19 
 vendor/topthink/think-queue/src/queue/Job.php                                                                       |  282 
 vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Business/JobOutput.php                                        |  329 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BuiltIn.php                                     |   35 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Settings.php                                          |   52 
 vendor/xaboy/form-builder/src/UI/Elm/Style/FormStyle.php                                                            |   86 
 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/WriteCheckSessionHandlerTest.php                       |   97 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AddressHelper.php                                           |   88 
 vendor/volcengine/volc-sdk-php/src/Service/SecretNumber.php                                                         |  282 
 vendor/xaboy/form-builder/src/components/Validate.php                                                               |  207 
 vendor/volcengine/volc-sdk-php/examples/ImageX/DemoUpload.php                                                       |   17 
 vendor/topthink/framework/src/think/event/RouteLoaded.php                                                           |   21 
 vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Business/InputPath.php                                        |  146 
 vendor/topthink/framework/src/lang/zh-cn.php                                                                        |  148 
 vendor/topthink/think-queue/src/queue/failed/Database.php                                                           |   99 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSubtitleInfo.php                                  |  507 
 vendor/symfony/process/Pipes/PipesInterface.php                                                                     |   67 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodCommitUploadInfoResponse.php                      |  119 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnCellIterator.php                                 |  189 
 vendor/xaboy/form-builder/src/UI/Iview/Traits/GroupFactoryTrait.php                                                 |   33 
 vendor/xaboy/form-builder/src/UI/Iview/Components/Rate.php                                                          |   47 
 vendor/topthink/think-migration/phinx/README.md                                                                     |  128 
 vendor/topthink/framework/src/think/exception/RouteNotFoundException.php                                            |   26 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/CreatePullToPushTaskResult.php                      |   65 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/GeneratePushURLResponse.php                         |  119 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Borders.php                                                |  411 
 vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Tests/Test.php                                                              | 1347 
 vendor/symfony/translation/Loader/PhpFileLoader.php                                                                 |   28 
 vendor/volcengine/volc-sdk-php/examples/Vod/DemoUploadMaterial.php                                                  |  148 
 vendor/psr/simple-cache/LICENSE.md                                                                                  |   21 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing.php                                            |  114 
 vendor/topthink/framework/src/think/console/output/Question.php                                                     |  211 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/CreateVQScoreTaskRequest.php                         |  213 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/utils/Maths.php                                      |   30 
 vendor/symfony/http-foundation/ResponseHeaderBag.php                                                                |  340 
 vendor/topthink/think-orm/src/DbManager.php                                                                         |  396 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/Polyfill.php                                      |    9 
 vendor/ralouphie/getallheaders/LICENSE                                                                              |   21 
 vendor/symfony/translation/Tests/fixtures/empty.xlf                                                                 |    0 
 vendor/symfony/translation/Tests/IntervalTest.php                                                                   |   52 
 vendor/topthink/think-migration/.gitignore                                                                          |    3 
 vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/PdoAdapter.php                                           |  587 
 vendor/volcengine/volc-sdk-php/examples/Live/DemoGeneratePushURL.php                                                |   32 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetAppInfoRequest.php                              |   65 
 vendor/xaboy/form-builder/src/UI/Elm/Traits/UploadFactoryTrait.php                                                  |   93 
 vendor/topthink/framework/src/think/View.php                                                                        |  191 
 vendor/symfony/translation/Resources/schemas/xliff-core-1.2-strict.xsd                                              | 2223 
 vendor/symfony/event-dispatcher/Tests/ContainerAwareEventDispatcherTest.php                                         |  277 
 vendor/symfony/translation/Formatter/IntlFormatter.php                                                              |   55 
 vendor/volcengine/volc-sdk-php/src/Service/Sts.php                                                                  |   45 
 vendor/topthink/framework/src/think/console/bin/hiddeninput.exe                                                     |    0 
 vendor/symfony/http-foundation/Tests/File/Fixtures/test.gif                                                         |    0 
 vendor/qcloud/cos-sdk-v5/composer.json                                                                              |   27 
 vendor/volcengine/volc-sdk-php/examples/Rtc/rtc-openapi-demo/Rtc/DemoRequest.php                                    |   59 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/EnableDomainResponse.php                            |   75 
 vendor/symfony/var-dumper/Server/DumpServer.php                                                                     |  107 
 vendor/psr/cache/CHANGELOG.md                                                                                       |   16 
 vendor/symfony/var-dumper/Cloner/AbstractCloner.php                                                                 |  360 
 vendor/psr/http-message/src/ServerRequestInterface.php                                                              |  261 
 vendor/symfony/polyfill-mbstring/composer.json                                                                      |   34 
 vendor/symfony/http-foundation/File/Exception/UnexpectedTypeException.php                                           |   20 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/no/config                                     |   24 
 vendor/pimple/pimple/ext/pimple/tests/007.phpt                                                                      |   22 
 vendor/symfony/http-foundation/File/Exception/UploadException.php                                                   |   21 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDescribeVodDomainBandwidthDataResult.php          |  303 
 vendor/topthink/framework/src/think/console/output/driver/Console.php                                               |  369 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/RecordHistoryInfo.php                               |  109 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Protection.php                                             |  186 
 vendor/topthink/think-orm/src/db/exception/PDOException.php                                                         |   41 
 vendor/topthink/think-orm/src/model/concern/SoftDelete.php                                                          |  248 
 vendor/xaboy/form-builder/src/Driver/CustomComponent.php                                                            |  121 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/Addr.php                                         |    0 
 vendor/volcengine/volc-sdk-php/VERSION                                                                              |    1 
 vendor/xaboy/form-builder/src/Rule/ValidateRule.php                                                                 |   70 
 vendor/topthink/framework/src/think/response/Json.php                                                               |   62 
 vendor/psr/container/composer.json                                                                                  |   27 
 vendor/volcengine/volc-sdk-php/examples/ImageX/DemoFetchUrl.php                                                     |   20 
 vendor/symfony/polyfill-intl-idn/Resources/unidata/disallowed_STD3_mapped.php                                       |  308 
 vendor/xaboy/form-builder/src/Contract/FormOptionsComponentInterface.php                                            |   19 
 vendor/topthink/framework/src/think/Route.php                                                                       |  939 
 vendor/topthink/think-orm/src/model/concern/RelationShip.php                                                        |  841 
 vendor/qiniu/php-sdk/.scrutinizer.yml                                                                               |   35 
 vendor/symfony/var-dumper/Dumper/HtmlDumper.php                                                                     |  969 
 vendor/symfony/translation/DataCollectorTranslator.php                                                              |  188 
 vendor/topthink/think-multi-app/src/command/stubs/controller.stub                                                   |   12 
 vendor/xaboy/form-builder/demo/elm.php                                                                              |  162 
 vendor/ralouphie/getallheaders/README.md                                                                            |   19 
 vendor/volcengine/volc-sdk-php/examples/Rtc/rtc-openapi-demo/VERSION                                                |    1 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodPlayInfo.php                                      |  823 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Spreadsheet.php                                                  | 1443 
 vendor/qiniu/php-sdk/src/Qiniu/Region.php                                                                           |  196 
 vendor/symfony/translation/Tests/fixtures/resourcebundle/res/en.res                                                 |    0 
 vendor/symfony/http-foundation/Tests/Session/Storage/NativeSessionStorageTest.php                                   |  296 
 vendor/pimple/pimple/ext/pimple/tests/017.phpt                                                                      |   17 
 vendor/symfony/http-foundation/Tests/StreamedResponseTest.php                                                       |  144 
 vendor/symfony/translation/Tests/fixtures/encoding.xlf                                                              |   16 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAllPlayInfoResult.php                             |  126 
 vendor/topthink/think-multi-app/README.md                                                                           |   14 
 vendor/xaboy/form-builder/images/components.png                                                                     |    0 
 vendor/xaboy/form-builder/src/Rule/ControlRule.php                                                                  |   80 
 vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Response/ImpSubmitJobResponse.php                             |   92 
 vendor/qiniu/php-sdk/tests/Qiniu/Tests/FopTest.php                                                                  |   37 
 vendor/volcengine/volc-sdk-php/examples/VoiceNotify/QueryResource.php                                               |   18 
 vendor/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.php                                          |   99 
 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/common.inc                                        |   43 
 vendor/topthink/think-template/src/Template.php                                                                     | 1320 
 vendor/qiniu/php-sdk/examples/put_bucketEvent.php                                                                   |   25 
 vendor/songshenzong/support/tests/TimeTest.php                                                                      |   22 
 vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/OverrideParams.php                                   |  167 
 vendor/qcloud/cos-sdk-v5/sample.php                                                                                 |  824 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/DomainList.php                                      |  567 
 vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/CreatePullToPushTaskResponse.php                    |  119 
 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IComparable.php                                                  |   13 
 vendor/topthink/framework/src/think/console/output/descriptor/Console.php                                           |  153 
 vendor/topthink/think-image/tests/ThumbTest.php                                                                     |  284 
 vendor/symfony/polyfill-intl-idn/Resources/unidata/ignored.php                                                      |  273 
 vendor/symfony/event-dispatcher/EventDispatcherInterface.php                                                        |   81 
 vendor/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php                                           |  152 
 vendor/symfony/translation/Catalogue/MergeOperation.php                                                             |   58 
 vendor/workerman/workerman/Connection/TcpConnection.php                                                             | 1001 
 vendor/symfony/translation/Tests/fixtures/extractor/this.is.a.template.format.engine                                |    0 
 2,489 files changed, 378,510 insertions(+), 0 deletions(-)

diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/cs/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/cs/config
new file mode 100755
index 0000000..df51373
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/cs/config
@@ -0,0 +1,23 @@
+##
+## PhpSpreadsheet
+##
+
+ArgumentSeparator	= ;
+
+
+##
+##	(For future use)
+##
+currencySymbol	= Kč
+
+
+##
+##	Excel Error Codes	(For future use)
+##
+NULL	= #NULL!
+DIV0	= #DIV/0!
+VALUE	= #HODNOTA!
+REF	= #REF!
+NAME	= #NÁZEV?
+NUM	= #NUM!
+NA	= #N/A
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/cs/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/cs/functions
new file mode 100755
index 0000000..733d406
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/cs/functions
@@ -0,0 +1,416 @@
+##
+## PhpSpreadsheet
+##
+## Data in this file derived from https://www.excel-function-translation.com/
+##
+##
+
+
+##
+##	Add-in and Automation functions		Funkce doplňků a automatizace
+##
+GETPIVOTDATA		= ZÍSKATKONTDATA	##	Vrátí data uložená v kontingenční tabulce. Pomocí funkce ZÍSKATKONTDATA můžete načíst souhrnná data z kontingenční tabulky, pokud jsou tato data v kontingenční sestavě zobrazena.
+
+
+##
+##	Cube functions				Funkce pro práci s krychlemi
+##
+CUBEKPIMEMBER		= CUBEKPIMEMBER		##	Vrátí název, vlastnost a velikost klíčového ukazatele výkonu (KUV) a zobrazí v buňce název a vlastnost. Klíčový ukazatel výkonu je kvantifikovatelná veličina, například hrubý měsíční zisk nebo čtvrtletní obrat na zaměstnance, která se používá pro sledování výkonnosti organizace.
+CUBEMEMBER		= CUBEMEMBER		##	Vrátí člen nebo n-tici v hierarchii krychle. Slouží k ověření, zda v krychli existuje člen nebo n-tice.
+CUBEMEMBERPROPERTY	= CUBEMEMBERPROPERTY	##	Vrátí hodnotu vlastnosti člena v krychli. Slouží k ověření, zda v krychli existuje člen s daným názvem, a k vrácení konkrétní vlastnosti tohoto člena.
+CUBERANKEDMEMBER	= CUBERANKEDMEMBER	##	Vrátí n-tý nebo pořadový člen sady. Použijte ji pro vrácení jednoho nebo více prvků sady, například obchodníka s nejvyšším obratem nebo deseti nejlepších studentů.
+CUBESET			= CUBESET		##	Definuje vypočtenou sadu členů nebo n-tic odesláním výrazu sady do krychle na serveru, který vytvoří sadu a potom ji vrátí do aplikace Microsoft Office Excel.
+CUBESETCOUNT		= CUBESETCOUNT		##	Vrátí počet položek v množině
+CUBEVALUE		= CUBEVALUE		##	Vrátí úhrnnou hodnotu z krychle.
+
+
+##
+##	Database functions			Funkce databáze
+##
+DAVERAGE		= DPRŮMĚR		##	Vrátí průměr vybraných položek databáze.
+DCOUNT			= DPOČET		##	Spočítá buňky databáze obsahující čísla.
+DCOUNTA			= DPOČET2		##	Spočítá buňky databáze, které nejsou prázdné.
+DGET			= DZÍSKAT		##	Extrahuje z databáze jeden záznam splňující zadaná kritéria.
+DMAX			= DMAX			##	Vrátí maximální hodnotu z vybraných položek databáze.
+DMIN			= DMIN			##	Vrátí minimální hodnotu z vybraných položek databáze.
+DPRODUCT		= DSOUČIN		##	Vynásobí hodnoty určitého pole záznamů v databázi, které splňují daná kritéria.
+DSTDEV			= DSMODCH.VÝBĚR		##	Odhadne směrodatnou odchylku výběru vybraných položek databáze.
+DSTDEVP			= DSMODCH		##	Vypočte směrodatnou odchylku základního souboru vybraných položek databáze.
+DSUM			= DSUMA			##	Sečte čísla ve sloupcovém poli záznamů databáze, která splňují daná kritéria.
+DVAR			= DVAR.VÝBĚR		##	Odhadne rozptyl výběru vybraných položek databáze.
+DVARP			= DVAR			##	Vypočte rozptyl základního souboru vybraných položek databáze.
+
+
+##
+##	Date and time functions			Funkce data a času
+##
+DATE			= DATUM			##	Vrátí pořadové číslo určitého data.
+DATEVALUE		= DATUMHODN		##	Převede datum ve formě textu na pořadové číslo.
+DAY			= DEN			##	Převede pořadové číslo na den v měsíci.
+DAYS360			= ROK360		##	Vrátí počet dní mezi dvěma daty na základě roku s 360 dny.
+EDATE			= EDATE			##	Vrátí pořadové číslo data, které označuje určený počet měsíců před nebo po počátečním datu.
+EOMONTH			= EOMONTH		##	Vrátí pořadové číslo posledního dne měsíce před nebo po zadaném počtu měsíců.
+HOUR			= HODINA		##	Převede pořadové číslo na hodinu.
+MINUTE			= MINUTA		##	Převede pořadové číslo na minutu.
+MONTH			= MĚSÍC			##	Převede pořadové číslo na měsíc.
+NETWORKDAYS		= NETWORKDAYS		##	Vrátí počet celých pracovních dní mezi dvěma daty.
+NOW			= NYNÍ			##	Vrátí pořadové číslo aktuálního data a času.
+SECOND			= SEKUNDA		##	Převede pořadové číslo na sekundu.
+TIME			= ČAS			##	Vrátí pořadové číslo určitého času.
+TIMEVALUE		= ČASHODN		##	Převede čas ve formě textu na pořadové číslo.
+TODAY			= DNES			##	Vrátí pořadové číslo dnešního data.
+WEEKDAY			= DENTÝDNE		##	Převede pořadové číslo na den v týdnu.
+WEEKNUM			= WEEKNUM		##	Převede pořadové číslo na číslo představující číselnou pozici týdne v roce.
+WORKDAY			= WORKDAY		##	Vrátí pořadové číslo data před nebo po zadaném počtu pracovních dní.
+YEAR			= ROK			##	Převede pořadové číslo na rok.
+YEARFRAC		= YEARFRAC		##	Vrátí část roku vyjádřenou zlomkem a představující počet celých dní mezi počátečním a koncovým datem.
+
+
+##
+##	Engineering functions			Inženýrské funkce (Technické funkce)
+##
+BESSELI			= BESSELI		##	Vrátí modifikovanou Besselovu funkci In(x).
+BESSELJ			= BESSELJ		##	Vrátí modifikovanou Besselovu funkci Jn(x).
+BESSELK			= BESSELK		##	Vrátí modifikovanou Besselovu funkci Kn(x).
+BESSELY			= BESSELY		##	Vrátí Besselovu funkci Yn(x).
+BIN2DEC			= BIN2DEC		##	Převede binární číslo na desítkové.
+BIN2HEX			= BIN2HEX		##	Převede binární číslo na šestnáctkové.
+BIN2OCT			= BIN2OCT		##	Převede binární číslo na osmičkové.
+COMPLEX			= COMPLEX		##	Převede reálnou a imaginární část na komplexní číslo.
+CONVERT			= CONVERT		##	Převede číslo do jiného jednotkového měrného systému.
+DEC2BIN			= DEC2BIN		##	Převede desítkového čísla na dvojkové
+DEC2HEX			= DEC2HEX		##	Převede desítkové číslo na šestnáctkové.
+DEC2OCT			= DEC2OCT		##	Převede desítkové číslo na osmičkové.
+DELTA			= DELTA			##	Testuje rovnost dvou hodnot.
+ERF			= ERF			##	Vrátí chybovou funkci.
+ERFC			= ERFC			##	Vrátí doplňkovou chybovou funkci.
+GESTEP			= GESTEP		##	Testuje, zda je číslo větší než mezní hodnota.
+HEX2BIN			= HEX2BIN		##	Převede šestnáctkové číslo na binární.
+HEX2DEC			= HEX2DEC		##	Převede šestnáctkové číslo na desítkové.
+HEX2OCT			= HEX2OCT		##	Převede šestnáctkové číslo na osmičkové.
+IMABS			= IMABS			##	Vrátí absolutní hodnotu (modul) komplexního čísla.
+IMAGINARY		= IMAGINARY		##	Vrátí imaginární část komplexního čísla.
+IMARGUMENT		= IMARGUMENT		##	Vrátí argument théta, úhel vyjádřený v radiánech.
+IMCONJUGATE		= IMCONJUGATE		##	Vrátí komplexně sdružené číslo ke komplexnímu číslu.
+IMCOS			= IMCOS			##	Vrátí kosinus komplexního čísla.
+IMDIV			= IMDIV			##	Vrátí podíl dvou komplexních čísel.
+IMEXP			= IMEXP			##	Vrátí exponenciální tvar komplexního čísla.
+IMLN			= IMLN			##	Vrátí přirozený logaritmus komplexního čísla.
+IMLOG10			= IMLOG10		##	Vrátí dekadický logaritmus komplexního čísla.
+IMLOG2			= IMLOG2		##	Vrátí logaritmus komplexního čísla při základu 2.
+IMPOWER			= IMPOWER		##	Vrátí komplexní číslo umocněné na celé číslo.
+IMPRODUCT		= IMPRODUCT		##	Vrátí součin komplexních čísel.
+IMREAL			= IMREAL		##	Vrátí reálnou část komplexního čísla.
+IMSIN			= IMSIN			##	Vrátí sinus komplexního čísla.
+IMSQRT			= IMSQRT		##	Vrátí druhou odmocninu komplexního čísla.
+IMSUB			= IMSUB			##	Vrátí rozdíl mezi dvěma komplexními čísly.
+IMSUM			= IMSUM			##	Vrátí součet dvou komplexních čísel.
+OCT2BIN			= OCT2BIN		##	Převede osmičkové číslo na binární.
+OCT2DEC			= OCT2DEC		##	Převede osmičkové číslo na desítkové.
+OCT2HEX			= OCT2HEX		##	Převede osmičkové číslo na šestnáctkové.
+
+
+##
+##	Financial functions			Finanční funkce
+##
+ACCRINT			= ACCRINT		##	Vrátí nahromaděný úrok z cenného papíru, ze kterého je úrok placen v pravidelných termínech.
+ACCRINTM		= ACCRINTM		##	Vrátí nahromaděný úrok z cenného papíru, ze kterého je úrok placen k datu splatnosti.
+AMORDEGRC		= AMORDEGRC		##	Vrátí lineární amortizaci v každém účetním období pomocí koeficientu amortizace.
+AMORLINC		= AMORLINC		##	Vrátí lineární amortizaci v každém účetním období.
+COUPDAYBS		= COUPDAYBS		##	Vrátí počet dnů od začátku období placení kupónů do data splatnosti.
+COUPDAYS		= COUPDAYS		##	Vrátí počet dnů v období placení kupónů, které obsahuje den zúčtování.
+COUPDAYSNC		= COUPDAYSNC		##	Vrátí počet dnů od data zúčtování do následujícího data placení kupónu.
+COUPNCD			= COUPNCD		##	Vrátí následující datum placení kupónu po datu zúčtování.
+COUPNUM			= COUPNUM		##	Vrátí počet kupónů splatných mezi datem zúčtování a datem splatnosti.
+COUPPCD			= COUPPCD		##	Vrátí předchozí datum placení kupónu před datem zúčtování.
+CUMIPMT			= CUMIPMT		##	Vrátí kumulativní úrok splacený mezi dvěma obdobími.
+CUMPRINC		= CUMPRINC		##	Vrátí kumulativní jistinu splacenou mezi dvěma obdobími půjčky.
+DB			= ODPIS.ZRYCH		##	Vrátí odpis aktiva za určité období pomocí degresivní metody odpisu s pevným zůstatkem.
+DDB			= ODPIS.ZRYCH2		##	Vrátí odpis aktiva za určité období pomocí dvojité degresivní metody odpisu nebo jiné metody, kterou zadáte.
+DISC			= DISC			##	Vrátí diskontní sazbu cenného papíru.
+DOLLARDE		= DOLLARDE		##	Převede částku v korunách vyjádřenou zlomkem na částku v korunách vyjádřenou desetinným číslem.
+DOLLARFR		= DOLLARFR		##	Převede částku v korunách vyjádřenou desetinným číslem na částku v korunách vyjádřenou zlomkem.
+DURATION		= DURATION		##	Vrátí roční dobu cenného papíru s pravidelnými úrokovými sazbami.
+EFFECT			= EFFECT		##	Vrátí efektivní roční úrokovou sazbu.
+FV			= BUDHODNOTA		##	Vrátí budoucí hodnotu investice.
+FVSCHEDULE		= FVSCHEDULE		##	Vrátí budoucí hodnotu počáteční jistiny po použití série sazeb složitého úroku.
+INTRATE			= INTRATE		##	Vrátí úrokovou sazbu plně investovaného cenného papíru.
+IPMT			= PLATBA.ÚROK		##	Vrátí výšku úroku investice za dané období.
+IRR			= MÍRA.VÝNOSNOSTI	##	Vrátí vnitřní výnosové procento série peněžních toků.
+ISPMT			= ISPMT			##	Vypočte výši úroku z investice zaplaceného během určitého období.
+MDURATION		= MDURATION		##	Vrátí Macauleyho modifikovanou dobu cenného papíru o nominální hodnotě 100 Kč.
+MIRR			= MOD.MÍRA.VÝNOSNOSTI	##	Vrátí vnitřní sazbu výnosu, přičemž kladné a záporné hodnoty peněžních prostředků jsou financovány podle různých sazeb.
+NOMINAL			= NOMINAL		##	Vrátí nominální roční úrokovou sazbu.
+NPER			= POČET.OBDOBÍ		##	Vrátí počet období pro investici.
+NPV			= ČISTÁ.SOUČHODNOTA	##	Vrátí čistou současnou hodnotu investice vypočítanou na základě série pravidelných peněžních toků a diskontní sazby.
+ODDFPRICE		= ODDFPRICE		##	Vrátí cenu cenného papíru o nominální hodnotě 100 Kč s odlišným prvním obdobím.
+ODDFYIELD		= ODDFYIELD		##	Vrátí výnos cenného papíru s odlišným prvním obdobím.
+ODDLPRICE		= ODDLPRICE		##	Vrátí cenu cenného papíru o nominální hodnotě 100 Kč s odlišným posledním obdobím.
+ODDLYIELD		= ODDLYIELD		##	Vrátí výnos cenného papíru s odlišným posledním obdobím.
+PMT			= PLATBA		##	Vrátí hodnotu pravidelné splátky anuity.
+PPMT			= PLATBA.ZÁKLAD		##	Vrátí hodnotu splátky jistiny pro zadanou investici za dané období.
+PRICE			= PRICE			##	Vrátí cenu cenného papíru o nominální hodnotě 100 Kč, ze kterého je úrok placen v pravidelných termínech.
+PRICEDISC		= PRICEDISC		##	Vrátí cenu diskontního cenného papíru o nominální hodnotě 100 Kč.
+PRICEMAT		= PRICEMAT		##	Vrátí cenu cenného papíru o nominální hodnotě 100 Kč, ze kterého je úrok placen k datu splatnosti.
+PV			= SOUČHODNOTA		##	Vrátí současnou hodnotu investice.
+RATE			= ÚROKOVÁ.MÍRA		##	Vrátí úrokovou sazbu vztaženou na období anuity.
+RECEIVED		= RECEIVED		##	Vrátí částku obdrženou k datu splatnosti plně investovaného cenného papíru.
+SLN			= ODPIS.LIN		##	Vrátí přímé odpisy aktiva pro jedno období.
+SYD			= ODPIS.NELIN		##	Vrátí směrné číslo ročních odpisů aktiva pro zadané období.
+TBILLEQ			= TBILLEQ		##	Vrátí výnos směnky státní pokladny ekvivalentní výnosu obligace.
+TBILLPRICE		= TBILLPRICE		##	Vrátí cenu směnky státní pokladny o nominální hodnotě 100 Kč.
+TBILLYIELD		= TBILLYIELD		##	Vrátí výnos směnky státní pokladny.
+VDB			= ODPIS.ZA.INT		##	Vrátí odpis aktiva pro určité období nebo část období pomocí degresivní metody odpisu.
+XIRR			= XIRR			##	Vrátí vnitřní výnosnost pro harmonogram peněžních toků, který nemusí být nutně periodický.
+XNPV			= XNPV			##	Vrátí čistou současnou hodnotu pro harmonogram peněžních toků, který nemusí být nutně periodický.
+YIELD			= YIELD			##	Vrátí výnos cenného papíru, ze kterého je úrok placen v pravidelných termínech.
+YIELDDISC		= YIELDDISC		##	Vrátí roční výnos diskontního cenného papíru, například směnky státní pokladny.
+YIELDMAT		= YIELDMAT		##	Vrátí roční výnos cenného papíru, ze kterého je úrok placen k datu splatnosti.
+
+
+##
+##	Information functions			Informační funkce
+##
+CELL			= POLÍČKO		##	Vrátí informace o formátování, umístění nebo obsahu buňky.
+ERROR.TYPE		= CHYBA.TYP		##	Vrátí číslo odpovídající typu chyby.
+INFO			= O.PROSTŘEDÍ		##	Vrátí informace o aktuálním pracovním prostředí.
+ISBLANK			= JE.PRÁZDNÉ		##	Vrátí hodnotu PRAVDA, pokud se argument hodnota odkazuje na prázdnou buňku.
+ISERR			= JE.CHYBA		##	Vrátí hodnotu PRAVDA, pokud je argument hodnota libovolná chybová hodnota (kromě #N/A).
+ISERROR			= JE.CHYBHODN		##	Vrátí hodnotu PRAVDA, pokud je argument hodnota libovolná chybová hodnota.
+ISEVEN			= ISEVEN		##	Vrátí hodnotu PRAVDA, pokud je číslo sudé.
+ISLOGICAL		= JE.LOGHODN		##	Vrátí hodnotu PRAVDA, pokud je argument hodnota logická hodnota.
+ISNA			= JE.NEDEF		##	Vrátí hodnotu PRAVDA, pokud je argument hodnota chybová hodnota #N/A.
+ISNONTEXT		= JE.NETEXT		##	Vrátí hodnotu PRAVDA, pokud argument hodnota není text.
+ISNUMBER		= JE.ČÍSLO		##	Vrátí hodnotu PRAVDA, pokud je argument hodnota číslo.
+ISODD			= ISODD			##	Vrátí hodnotu PRAVDA, pokud je číslo liché.
+ISREF			= JE.ODKAZ		##	Vrátí hodnotu PRAVDA, pokud je argument hodnota odkaz.
+ISTEXT			= JE.TEXT		##	Vrátí hodnotu PRAVDA, pokud je argument hodnota text.
+N			= N			##	Vrátí hodnotu převedenou na číslo.
+NA			= NEDEF			##	Vrátí chybovou hodnotu #N/A.
+TYPE			= TYP			##	Vrátí číslo označující datový typ hodnoty.
+
+
+##
+##	Logical functions			Logické funkce
+##
+AND			= A			##	Vrátí hodnotu PRAVDA, mají-li všechny argumenty hodnotu PRAVDA.
+FALSE			= NEPRAVDA		##	Vrátí logickou hodnotu NEPRAVDA.
+IF			= KDYŽ			##	Určí, který logický test má proběhnout.
+IFERROR			= IFERROR		##	Pokud je vzorec vyhodnocen jako chyba, vrátí zadanou hodnotu. V opačném případě vrátí výsledek vzorce.
+NOT			= NE			##	Provede logickou negaci argumentu funkce.
+OR			= NEBO			##	Vrátí hodnotu PRAVDA, je-li alespoň jeden argument roven hodnotě PRAVDA.
+TRUE			= PRAVDA		##	Vrátí logickou hodnotu PRAVDA.
+
+
+##
+##	Lookup and reference functions		Vyhledávací funkce
+##
+ADDRESS			= ODKAZ			##	Vrátí textový odkaz na jednu buňku listu.
+AREAS			= POČET.BLOKŮ		##	Vrátí počet oblastí v odkazu.
+CHOOSE			= ZVOLIT		##	Zvolí hodnotu ze seznamu hodnot.
+COLUMN			= SLOUPEC		##	Vrátí číslo sloupce odkazu.
+COLUMNS			= SLOUPCE		##	Vrátí počet sloupců v odkazu.
+HLOOKUP			= VVYHLEDAT		##	Prohledá horní řádek matice a vrátí hodnotu určené buňky.
+HYPERLINK		= HYPERTEXTOVÝ.ODKAZ	##	Vytvoří zástupce nebo odkaz, který otevře dokument uložený na síťovém serveru, v síti intranet nebo Internet.
+INDEX			= INDEX			##	Pomocí rejstříku zvolí hodnotu z odkazu nebo matice.
+INDIRECT		= NEPŘÍMÝ.ODKAZ		##	Vrátí odkaz určený textovou hodnotou.
+LOOKUP			= VYHLEDAT		##	Vyhledá hodnoty ve vektoru nebo matici.
+MATCH			= POZVYHLEDAT		##	Vyhledá hodnoty v odkazu nebo matici.
+OFFSET			= POSUN			##	Vrátí posun odkazu od zadaného odkazu.
+ROW			= ŘÁDEK			##	Vrátí číslo řádku odkazu.
+ROWS			= ŘÁDKY			##	Vrátí počet řádků v odkazu.
+RTD			= RTD			##	Načte data reálného času z programu, který podporuje automatizaci modelu COM (Automatizace: Způsob práce s objekty určité aplikace z jiné aplikace nebo nástroje pro vývoj. Automatizace (dříve nazývaná automatizace OLE) je počítačovým standardem a je funkcí modelu COM (Component Object Model).).
+TRANSPOSE		= TRANSPOZICE		##	Vrátí transponovanou matici.
+VLOOKUP			= SVYHLEDAT		##	Prohledá první sloupec matice, přesune kurzor v řádku a vrátí hodnotu buňky.
+
+
+##
+##	Math and trigonometry functions		Matematické a trigonometrické funkce
+##
+ABS			= ABS			##	Vrátí absolutní hodnotu čísla.
+ACOS			= ARCCOS		##	Vrátí arkuskosinus čísla.
+ACOSH			= ARCCOSH		##	Vrátí hyperbolický arkuskosinus čísla.
+ASIN			= ARCSIN		##	Vrátí arkussinus čísla.
+ASINH			= ARCSINH		##	Vrátí hyperbolický arkussinus čísla.
+ATAN			= ARCTG			##	Vrátí arkustangens čísla.
+ATAN2			= ARCTG2		##	Vrátí arkustangens x-ové a y-ové souřadnice.
+ATANH			= ARCTGH		##	Vrátí hyperbolický arkustangens čísla.
+CEILING			= ZAOKR.NAHORU		##	Zaokrouhlí číslo na nejbližší celé číslo nebo na nejbližší násobek zadané hodnoty.
+COMBIN			= KOMBINACE		##	Vrátí počet kombinací pro daný počet položek.
+COS			= COS			##	Vrátí kosinus čísla.
+COSH			= COSH			##	Vrátí hyperbolický kosinus čísla.
+DEGREES			= DEGREES		##	Převede radiány na stupně.
+EVEN			= ZAOKROUHLIT.NA.SUDÉ	##	Zaokrouhlí číslo nahoru na nejbližší celé sudé číslo.
+EXP			= EXP			##	Vrátí základ přirozeného logaritmu e umocněný na zadané číslo.
+FACT			= FAKTORIÁL		##	Vrátí faktoriál čísla.
+FACTDOUBLE		= FACTDOUBLE		##	Vrátí dvojitý faktoriál čísla.
+FLOOR			= ZAOKR.DOLŮ		##	Zaokrouhlí číslo dolů, směrem k nule.
+GCD			= GCD			##	Vrátí největší společný dělitel.
+INT			= CELÁ.ČÁST		##	Zaokrouhlí číslo dolů na nejbližší celé číslo.
+LCM			= LCM			##	Vrátí nejmenší společný násobek.
+LN			= LN			##	Vrátí přirozený logaritmus čísla.
+LOG			= LOGZ			##	Vrátí logaritmus čísla při zadaném základu.
+LOG10			= LOG			##	Vrátí dekadický logaritmus čísla.
+MDETERM			= DETERMINANT		##	Vrátí determinant matice.
+MINVERSE		= INVERZE		##	Vrátí inverzní matici.
+MMULT			= SOUČIN.MATIC		##	Vrátí součin dvou matic.
+MOD			= MOD			##	Vrátí zbytek po dělení.
+MROUND			= MROUND		##	Vrátí číslo zaokrouhlené na požadovaný násobek.
+MULTINOMIAL		= MULTINOMIAL		##	Vrátí mnohočlen z množiny čísel.
+ODD			= ZAOKROUHLIT.NA.LICHÉ	##	Zaokrouhlí číslo nahoru na nejbližší celé liché číslo.
+PI			= PI			##	Vrátí hodnotu čísla pí.
+POWER			= POWER			##	Umocní číslo na zadanou mocninu.
+PRODUCT			= SOUČIN		##	Vynásobí argumenty funkce.
+QUOTIENT		= QUOTIENT		##	Vrátí celou část dělení.
+RADIANS			= RADIANS		##	Převede stupně na radiány.
+RAND			= NÁHČÍSLO		##	Vrátí náhodné číslo mezi 0 a 1.
+RANDBETWEEN		= RANDBETWEEN		##	Vrátí náhodné číslo mezi zadanými čísly.
+ROMAN			= ROMAN			##	Převede arabskou číslici na římskou ve formátu textu.
+ROUND			= ZAOKROUHLIT		##	Zaokrouhlí číslo na zadaný počet číslic.
+ROUNDDOWN		= ROUNDDOWN		##	Zaokrouhlí číslo dolů, směrem k nule.
+ROUNDUP			= ROUNDUP		##	Zaokrouhlí číslo nahoru, směrem od nuly.
+SERIESSUM		= SERIESSUM		##	Vrátí součet mocninné řady určené podle vzorce.
+SIGN			= SIGN			##	Vrátí znaménko čísla.
+SIN			= SIN			##	Vrátí sinus daného úhlu.
+SINH			= SINH			##	Vrátí hyperbolický sinus čísla.
+SQRT			= ODMOCNINA		##	Vrátí kladnou druhou odmocninu.
+SQRTPI			= SQRTPI		##	Vrátí druhou odmocninu výrazu (číslo * pí).
+SUBTOTAL		= SUBTOTAL		##	Vrátí souhrn v seznamu nebo databázi.
+SUM			= SUMA			##	Sečte argumenty funkce.
+SUMIF			= SUMIF			##	Sečte buňky vybrané podle zadaných kritérií.
+SUMIFS			= SUMIFS		##	Sečte buňky určené více zadanými podmínkami.
+SUMPRODUCT		= SOUČIN.SKALÁRNÍ	##	Vrátí součet součinů odpovídajících prvků matic.
+SUMSQ			= SUMA.ČTVERCŮ		##	Vrátí součet čtverců argumentů.
+SUMX2MY2		= SUMX2MY2		##	Vrátí součet rozdílu čtverců odpovídajících hodnot ve dvou maticích.
+SUMX2PY2		= SUMX2PY2		##	Vrátí součet součtu čtverců odpovídajících hodnot ve dvou maticích.
+SUMXMY2			= SUMXMY2		##	Vrátí součet čtverců rozdílů odpovídajících hodnot ve dvou maticích.
+TAN			= TGTG			##	Vrátí tangens čísla.
+TANH			= TGH			##	Vrátí hyperbolický tangens čísla.
+TRUNC			= USEKNOUT		##	Zkrátí číslo na celé číslo.
+
+
+##
+##	Statistical functions			Statistické funkce
+##
+AVEDEV			= PRŮMODCHYLKA		##	Vrátí průměrnou hodnotu absolutních odchylek datových bodů od jejich střední hodnoty.
+AVERAGE			= PRŮMĚR		##	Vrátí průměrnou hodnotu argumentů.
+AVERAGEA		= AVERAGEA		##	Vrátí průměrnou hodnotu argumentů včetně čísel, textu a logických hodnot.
+AVERAGEIF		= AVERAGEIF		##	Vrátí průměrnou hodnotu (aritmetický průměr) všech buněk v oblasti, které vyhovují příslušné podmínce.
+AVERAGEIFS		= AVERAGEIFS		##	Vrátí průměrnou hodnotu (aritmetický průměr) všech buněk vyhovujících několika podmínkám.
+BETADIST		= BETADIST		##	Vrátí hodnotu součtového rozdělení beta.
+BETAINV			= BETAINV		##	Vrátí inverzní hodnotu součtového rozdělení pro zadané rozdělení beta.
+BINOMDIST		= BINOMDIST		##	Vrátí hodnotu binomického rozdělení pravděpodobnosti jednotlivých veličin.
+CHIDIST			= CHIDIST		##	Vrátí jednostrannou pravděpodobnost rozdělení chí-kvadrát.
+CHIINV			= CHIINV		##	Vrátí hodnotu funkce inverzní k distribuční funkci jednostranné pravděpodobnosti rozdělení chí-kvadrát.
+CHITEST			= CHITEST		##	Vrátí test nezávislosti.
+CONFIDENCE		= CONFIDENCE		##	Vrátí interval spolehlivosti pro střední hodnotu základního souboru.
+CORREL			= CORREL		##	Vrátí korelační koeficient mezi dvěma množinami dat.
+COUNT			= POČET			##	Vrátí počet čísel v seznamu argumentů.
+COUNTA			= POČET2		##	Vrátí počet hodnot v seznamu argumentů.
+COUNTBLANK		= COUNTBLANK		##	Spočítá počet prázdných buněk v oblasti.
+COUNTIF			= COUNTIF		##	Spočítá buňky v oblasti, které odpovídají zadaným kritériím.
+COUNTIFS		= COUNTIFS		##	Spočítá buňky v oblasti, které odpovídají více kritériím.
+COVAR			= COVAR			##	Vrátí hodnotu kovariance, průměrnou hodnotu součinů párových odchylek
+CRITBINOM		= CRITBINOM		##	Vrátí nejmenší hodnotu, pro kterou má součtové binomické rozdělení hodnotu větší nebo rovnu hodnotě kritéria.
+DEVSQ			= DEVSQ			##	Vrátí součet čtverců odchylek.
+EXPONDIST		= EXPONDIST		##	Vrátí hodnotu exponenciálního rozdělení.
+FDIST			= FDIST			##	Vrátí hodnotu rozdělení pravděpodobnosti F.
+FINV			= FINV			##	Vrátí hodnotu inverzní funkce k distribuční funkci rozdělení F.
+FISHER			= FISHER		##	Vrátí hodnotu Fisherovy transformace.
+FISHERINV		= FISHERINV		##	Vrátí hodnotu inverzní funkce k Fisherově transformaci.
+FORECAST		= FORECAST		##	Vrátí hodnotu lineárního trendu.
+FREQUENCY		= ČETNOSTI		##	Vrátí četnost rozdělení jako svislou matici.
+FTEST			= FTEST			##	Vrátí výsledek F-testu.
+GAMMADIST		= GAMMADIST		##	Vrátí hodnotu rozdělení gama.
+GAMMAINV		= GAMMAINV		##	Vrátí hodnotu inverzní funkce k distribuční funkci součtového rozdělení gama.
+GAMMALN			= GAMMALN		##	Vrátí přirozený logaritmus funkce gama, Γ(x).
+GEOMEAN			= GEOMEAN		##	Vrátí geometrický průměr.
+GROWTH			= LOGLINTREND		##	Vrátí hodnoty exponenciálního trendu.
+HARMEAN			= HARMEAN		##	Vrátí harmonický průměr.
+HYPGEOMDIST		= HYPGEOMDIST		##	Vrátí hodnotu hypergeometrického rozdělení.
+INTERCEPT		= INTERCEPT		##	Vrátí úsek lineární regresní čáry.
+KURT			= KURT			##	Vrátí hodnotu excesu množiny dat.
+LARGE			= LARGE			##	Vrátí k-tou největší hodnotu množiny dat.
+LINEST			= LINREGRESE		##	Vrátí parametry lineárního trendu.
+LOGEST			= LOGLINREGRESE		##	Vrátí parametry exponenciálního trendu.
+LOGINV			= LOGINV		##	Vrátí inverzní funkci k distribuční funkci logaritmicko-normálního rozdělení.
+LOGNORMDIST		= LOGNORMDIST		##	Vrátí hodnotu součtového logaritmicko-normálního rozdělení.
+MAX			= MAX			##	Vrátí maximální hodnotu seznamu argumentů.
+MAXA			= MAXA			##	Vrátí maximální hodnotu seznamu argumentů včetně čísel, textu a logických hodnot.
+MEDIAN			= MEDIAN		##	Vrátí střední hodnotu zadaných čísel.
+MIN			= MIN			##	Vrátí minimální hodnotu seznamu argumentů.
+MINA			= MINA			##	Vrátí nejmenší hodnotu v seznamu argumentů včetně čísel, textu a logických hodnot.
+MODE			= MODE			##	Vrátí hodnotu, která se v množině dat vyskytuje nejčastěji.
+NEGBINOMDIST		= NEGBINOMDIST		##	Vrátí hodnotu negativního binomického rozdělení.
+NORMDIST		= NORMDIST		##	Vrátí hodnotu normálního součtového rozdělení.
+NORMINV			= NORMINV		##	Vrátí inverzní funkci k funkci normálního součtového rozdělení.
+NORMSDIST		= NORMSDIST		##	Vrátí hodnotu standardního normálního součtového rozdělení.
+NORMSINV		= NORMSINV		##	Vrátí inverzní funkci k funkci standardního normálního součtového rozdělení.
+PEARSON			= PEARSON		##	Vrátí Pearsonův výsledný momentový korelační koeficient.
+PERCENTILE		= PERCENTIL		##	Vrátí hodnotu k-tého percentilu hodnot v oblasti.
+PERCENTRANK		= PERCENTRANK		##	Vrátí pořadí hodnoty v množině dat vyjádřené procentuální částí množiny dat.
+PERMUT			= PERMUTACE		##	Vrátí počet permutací pro zadaný počet objektů.
+POISSON			= POISSON		##	Vrátí hodnotu distribuční funkce Poissonova rozdělení.
+PROB			= PROB			##	Vrátí pravděpodobnost výskytu hodnot v oblasti mezi dvěma mezními hodnotami.
+QUARTILE		= QUARTIL		##	Vrátí hodnotu kvartilu množiny dat.
+RANK			= RANK			##	Vrátí pořadí čísla v seznamu čísel.
+RSQ			= RKQ			##	Vrátí druhou mocninu Pearsonova výsledného momentového korelačního koeficientu.
+SKEW			= SKEW			##	Vrátí zešikmení rozdělení.
+SLOPE			= SLOPE			##	Vrátí směrnici lineární regresní čáry.
+SMALL			= SMALL			##	Vrátí k-tou nejmenší hodnotu množiny dat.
+STANDARDIZE		= STANDARDIZE		##	Vrátí normalizovanou hodnotu.
+STDEV			= SMODCH.VÝBĚR		##	Vypočte směrodatnou odchylku výběru.
+STDEVA			= STDEVA		##	Vypočte směrodatnou odchylku výběru včetně čísel, textu a logických hodnot.
+STDEVP			= SMODCH		##	Vypočte směrodatnou odchylku základního souboru.
+STDEVPA			= STDEVPA		##	Vypočte směrodatnou odchylku základního souboru včetně čísel, textu a logických hodnot.
+STEYX			= STEYX			##	Vrátí standardní chybu předpovězené hodnoty y pro každou hodnotu x v regresi.
+TDIST			= TDIST			##	Vrátí hodnotu Studentova t-rozdělení.
+TINV			= TINV			##	Vrátí inverzní funkci k distribuční funkci Studentova t-rozdělení.
+TREND			= LINTREND		##	Vrátí hodnoty lineárního trendu.
+TRIMMEAN		= TRIMMEAN		##	Vrátí střední hodnotu vnitřní části množiny dat.
+TTEST			= TTEST			##	Vrátí pravděpodobnost spojenou se Studentovým t-testem.
+VAR			= VAR.VÝBĚR		##	Vypočte rozptyl výběru.
+VARA			= VARA			##	Vypočte rozptyl výběru včetně čísel, textu a logických hodnot.
+VARP			= VAR			##	Vypočte rozptyl základního souboru.
+VARPA			= VARPA			##	Vypočte rozptyl základního souboru včetně čísel, textu a logických hodnot.
+WEIBULL			= WEIBULL		##	Vrátí hodnotu Weibullova rozdělení.
+ZTEST			= ZTEST			##	Vrátí jednostrannou P-hodnotu z-testu.
+
+
+##
+##	Text functions				Textové funkce
+##
+ASC			= ASC			##	Změní znaky s plnou šířkou (dvoubajtové)v řetězci znaků na znaky s poloviční šířkou (jednobajtové).
+BAHTTEXT		= BAHTTEXT		##	Převede číslo na text ve formátu, měny ß (baht).
+CHAR			= ZNAK			##	Vrátí znak určený číslem kódu.
+CLEAN			= VYČISTIT		##	Odebere z textu všechny netisknutelné znaky.
+CODE			= KÓD			##	Vrátí číselný kód prvního znaku zadaného textového řetězce.
+CONCATENATE		= CONCATENATE		##	Spojí několik textových položek do jedné.
+DOLLAR			= KČ			##	Převede číslo na text ve formátu měny Kč (česká koruna).
+EXACT			= STEJNÉ		##	Zkontroluje, zda jsou dvě textové hodnoty shodné.
+FIND			= NAJÍT			##	Nalezne textovou hodnotu uvnitř jiné (rozlišuje malá a velká písmena).
+FINDB			= FINDB			##	Nalezne textovou hodnotu uvnitř jiné (rozlišuje malá a velká písmena).
+FIXED			= ZAOKROUHLIT.NA.TEXT	##	Zformátuje číslo jako text s pevným počtem desetinných míst.
+JIS			= JIS			##	Změní znaky s poloviční šířkou (jednobajtové) v řetězci znaků na znaky s plnou šířkou (dvoubajtové).
+LEFT			= ZLEVA			##	Vrátí první znaky textové hodnoty umístěné nejvíce vlevo.
+LEFTB			= LEFTB			##	Vrátí první znaky textové hodnoty umístěné nejvíce vlevo.
+LEN			= DÉLKA			##	Vrátí počet znaků textového řetězce.
+LENB			= LENB			##	Vrátí počet znaků textového řetězce.
+LOWER			= MALÁ			##	Převede text na malá písmena.
+MID			= ČÁST			##	Vrátí určitý počet znaků textového řetězce počínaje zadaným místem.
+MIDB			= MIDB			##	Vrátí určitý počet znaků textového řetězce počínaje zadaným místem.
+PHONETIC		= ZVUKOVÉ		##	Extrahuje fonetické znaky (furigana) z textového řetězce.
+PROPER			= VELKÁ2		##	Převede první písmeno každého slova textové hodnoty na velké.
+REPLACE			= NAHRADIT		##	Nahradí znaky uvnitř textu.
+REPLACEB		= NAHRADITB		##	Nahradí znaky uvnitř textu.
+REPT			= OPAKOVAT		##	Zopakuje text podle zadaného počtu opakování.
+RIGHT			= ZPRAVA		##	Vrátí první znaky textové hodnoty umístěné nejvíce vpravo.
+RIGHTB			= RIGHTB		##	Vrátí první znaky textové hodnoty umístěné nejvíce vpravo.
+SEARCH			= HLEDAT		##	Nalezne textovou hodnotu uvnitř jiné (malá a velká písmena nejsou rozlišována).
+SEARCHB			= SEARCHB		##	Nalezne textovou hodnotu uvnitř jiné (malá a velká písmena nejsou rozlišována).
+SUBSTITUTE		= DOSADIT		##	V textovém řetězci nahradí starý text novým.
+T			= T			##	Převede argumenty na text.
+TEXT			= HODNOTA.NA.TEXT	##	Zformátuje číslo a převede ho na text.
+TRIM			= PROČISTIT		##	Odstraní z textu mezery.
+UPPER			= VELKÁ			##	Převede text na velká písmena.
+VALUE			= HODNOTA		##	Převede textový argument na číslo.
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/da/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/da/config
new file mode 100755
index 0000000..a7aa8fe
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/da/config
@@ -0,0 +1,25 @@
+##
+## PhpSpreadsheet
+##
+
+ArgumentSeparator	= ;
+
+
+##
+##	(For future use)
+##
+currencySymbol	= kr
+
+
+
+##
+##	Excel Error Codes	(For future use)
+
+##
+NULL	= #NUL!
+DIV0	= #DIVISION/0!
+VALUE	= #VÆRDI!
+REF	= #REFERENCE!
+NAME	= #NAVN?
+NUM	= #NUM!
+NA	= #I/T
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/da/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/da/functions
new file mode 100755
index 0000000..d02aa2e
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/da/functions
@@ -0,0 +1,416 @@
+##
+## PhpSpreadsheet
+##
+## Data in this file derived from https://www.excel-function-translation.com/
+##
+##
+
+
+##
+##	Add-in and Automation functions			Tilføjelsesprogram- og automatiseringsfunktioner
+##
+GETPIVOTDATA		= HENTPIVOTDATA			##	Returnerer data, der er lagret i en pivottabelrapport
+
+
+##
+##	Cube functions					Kubefunktioner
+##
+CUBEKPIMEMBER		= KUBE.KPI.MEDLEM		##	Returnerer navn, egenskab og mål for en KPI-indikator og viser navnet og egenskaben i cellen. En KPI-indikator er en målbar størrelse, f.eks. bruttooverskud pr. måned eller personaleudskiftning pr. kvartal, der bruges til at overvåge en organisations præstationer.
+CUBEMEMBER		= KUBE.MEDLEM			##	Returnerer et medlem eller en tupel fra kubehierarkiet. Bruges til at validere, om et medlem eller en tupel findes i kuben.
+CUBEMEMBERPROPERTY	= KUBEMEDLEM.EGENSKAB		##	Returnerer værdien af en egenskab for et medlem i kuben. Bruges til at validere, om et medlemsnavn findes i kuben, og returnere den angivne egenskab for medlemmet.
+CUBERANKEDMEMBER	= KUBEMEDLEM.RANG		##	Returnerer det n'te eller rangordnede medlem i et sæt. Bruges til at returnere et eller flere elementer i et sæt, f.eks. topsælgere eller de 10 bedste elever.
+CUBESET			= KUBESÆT			##	Definerer et beregnet sæt medlemmer eller tupler ved at sende et sætudtryk til kuben på serveren, som opretter sættet og returnerer det til Microsoft Office Excel.
+CUBESETCOUNT		= KUBESÆT.TÆL			##	Returnerer antallet af elementer i et sæt.
+CUBEVALUE		= KUBEVÆRDI			##	Returnerer en sammenlagt (aggregeret) værdi fra en kube.
+
+
+##
+##	Database functions				Databasefunktioner
+##
+DAVERAGE		= DMIDDEL			##	Returnerer gennemsnittet af markerede databaseposter
+DCOUNT			= DTÆL				##	Tæller de celler, der indeholder tal, i en database
+DCOUNTA			= DTÆLV				##	Tæller udfyldte celler i en database
+DGET			= DHENT				##	Uddrager en enkelt post, der opfylder de angivne kriterier, fra en database
+DMAX			= DMAKS				##	Returnerer den største værdi blandt markerede databaseposter
+DMIN			= DMIN				##	Returnerer den mindste værdi blandt markerede databaseposter
+DPRODUCT		= DPRODUKT			##	Ganger værdierne i et bestemt felt med poster, der opfylder kriterierne i en database
+DSTDEV			= DSTDAFV			##	Beregner et skøn over standardafvigelsen baseret på en stikprøve af markerede databaseposter
+DSTDEVP			= DSTDAFVP			##	Beregner standardafvigelsen baseret på hele populationen af markerede databaseposter
+DSUM			= DSUM				##	Sammenlægger de tal i feltkolonnen i databasen, der opfylder kriterierne
+DVAR			= DVARIANS			##	Beregner varians baseret på en stikprøve af markerede databaseposter
+DVARP			= DVARIANSP			##	Beregner varians baseret på hele populationen af markerede databaseposter
+
+
+##
+##	Date and time functions				Dato- og klokkeslætsfunktioner
+##
+DATE			= DATO				##	Returnerer serienummeret for en bestemt dato
+DATEVALUE		= DATOVÆRDI			##	Konverterer en dato i form af tekst til et serienummer
+DAY			= DAG				##	Konverterer et serienummer til en dag i måneden
+DAYS360			= DAGE360			##	Beregner antallet af dage mellem to datoer på grundlag af et år med 360 dage
+EDATE			= EDATO				##	Returnerer serienummeret for den dato, der ligger det angivne antal måneder før eller efter startdatoen
+EOMONTH			= SLUT.PÅ.MÅNED			##	Returnerer serienummeret på den sidste dag i måneden før eller efter et angivet antal måneder
+HOUR			= TIME				##	Konverterer et serienummer til en time
+MINUTE			= MINUT				##	Konverterer et serienummer til et minut
+MONTH			= MÅNED				##	Konverterer et serienummer til en måned
+NETWORKDAYS		= ANTAL.ARBEJDSDAGE		##	Returnerer antallet af hele arbejdsdage mellem to datoer
+NOW			= NU				##	Returnerer serienummeret for den aktuelle dato eller det aktuelle klokkeslæt
+SECOND			= SEKUND			##	Konverterer et serienummer til et sekund
+TIME			= KLOKKESLÆT			##	Returnerer serienummeret for et bestemt klokkeslæt
+TIMEVALUE		= TIDSVÆRDI			##	Konverterer et klokkeslæt i form af tekst til et serienummer
+TODAY			= IDAG				##	Returnerer serienummeret for dags dato
+WEEKDAY			= UGEDAG			##	Konverterer et serienummer til en ugedag
+WEEKNUM			= UGE.NR			##	Konverterer et serienummer til et tal, der angiver ugenummeret i året
+WORKDAY			= ARBEJDSDAG			##	Returnerer serienummeret for dagen før eller efter det angivne antal arbejdsdage
+YEAR			= ÅR				##	Konverterer et serienummer til et år
+YEARFRAC		= ÅR.BRØK			##	Returnerer årsbrøken, der repræsenterer antallet af hele dage mellem startdato og slutdato
+
+
+##
+##	Engineering functions				Tekniske funktioner
+##
+BESSELI			= BESSELI			##	Returnerer den modificerede Bessel-funktion In(x)
+BESSELJ			= BESSELJ			##	Returnerer Bessel-funktionen Jn(x)
+BESSELK			= BESSELK			##	Returnerer den modificerede Bessel-funktion Kn(x)
+BESSELY			= BESSELY			##	Returnerer Bessel-funktionen Yn(x)
+BIN2DEC			= BIN.TIL.DEC			##	Konverterer et binært tal til et decimaltal
+BIN2HEX			= BIN.TIL.HEX			##	Konverterer et binært tal til et heksadecimalt tal
+BIN2OCT			= BIN.TIL.OKT			##	Konverterer et binært tal til et oktaltal.
+COMPLEX			= KOMPLEKS			##	Konverterer reelle og imaginære koefficienter til et komplekst tal
+CONVERT			= KONVERTER			##	Konverterer et tal fra én måleenhed til en anden
+DEC2BIN			= DEC.TIL.BIN			##	Konverterer et decimaltal til et binært tal
+DEC2HEX			= DEC.TIL.HEX			##	Konverterer et decimaltal til et heksadecimalt tal
+DEC2OCT			= DEC.TIL.OKT			##	Konverterer et decimaltal til et oktaltal
+DELTA			= DELTA				##	Tester, om to værdier er ens
+ERF			= FEJLFUNK			##	Returner fejlfunktionen
+ERFC			= FEJLFUNK.KOMP			##	Returnerer den komplementære fejlfunktion
+GESTEP			= GETRIN			##	Tester, om et tal er større end en grænseværdi
+HEX2BIN			= HEX.TIL.BIN			##	Konverterer et heksadecimalt tal til et binært tal
+HEX2DEC			= HEX.TIL.DEC			##	Konverterer et decimaltal til et heksadecimalt tal
+HEX2OCT			= HEX.TIL.OKT			##	Konverterer et heksadecimalt tal til et oktaltal
+IMABS			= IMAGABS			##	Returnerer den absolutte værdi (modulus) for et komplekst tal
+IMAGINARY		= IMAGINÆR			##	Returnerer den imaginære koefficient for et komplekst tal
+IMARGUMENT		= IMAGARGUMENT			##	Returnerer argumentet theta, en vinkel udtrykt i radianer
+IMCONJUGATE		= IMAGKONJUGERE			##	Returnerer den komplekse konjugation af et komplekst tal
+IMCOS			= IMAGCOS			##	Returnerer et komplekst tals cosinus
+IMDIV			= IMAGDIV			##	Returnerer kvotienten for to komplekse tal
+IMEXP			= IMAGEKSP			##	Returnerer et komplekst tals eksponentialfunktion
+IMLN			= IMAGLN			##	Returnerer et komplekst tals naturlige logaritme
+IMLOG10			= IMAGLOG10			##	Returnerer et komplekst tals sædvanlige logaritme (titalslogaritme)
+IMLOG2			= IMAGLOG2			##	Returnerer et komplekst tals sædvanlige logaritme (totalslogaritme)
+IMPOWER			= IMAGPOTENS			##	Returnerer et komplekst tal opløftet i en heltalspotens
+IMPRODUCT		= IMAGPRODUKT			##	Returnerer produktet af komplekse tal
+IMREAL			= IMAGREELT			##	Returnerer den reelle koefficient for et komplekst tal
+IMSIN			= IMAGSIN			##	Returnerer et komplekst tals sinus
+IMSQRT			= IMAGKVROD			##	Returnerer et komplekst tals kvadratrod
+IMSUB			= IMAGSUB			##	Returnerer forskellen mellem to komplekse tal
+IMSUM			= IMAGSUM			##	Returnerer summen af komplekse tal
+OCT2BIN			= OKT.TIL.BIN			##	Konverterer et oktaltal til et binært tal
+OCT2DEC			= OKT.TIL.DEC			##	Konverterer et oktaltal til et decimaltal
+OCT2HEX			= OKT.TIL.HEX			##	Konverterer et oktaltal til et heksadecimalt tal
+
+
+##
+##	Financial functions				Finansielle funktioner
+##
+ACCRINT			= PÅLØBRENTE			##	Returnerer den påløbne rente for et værdipapir med periodiske renteudbetalinger
+ACCRINTM		= PÅLØBRENTE.UDLØB		##	Returnerer den påløbne rente for et værdipapir, hvor renteudbetalingen finder sted ved papirets udløb
+AMORDEGRC		= AMORDEGRC			##	Returnerer afskrivningsbeløbet for hver regnskabsperiode ved hjælp af en afskrivningskoefficient
+AMORLINC		= AMORLINC			##	Returnerer afskrivningsbeløbet for hver regnskabsperiode
+COUPDAYBS		= KUPONDAGE.SA			##	Returnerer antallet af dage fra starten af kuponperioden til afregningsdatoen
+COUPDAYS		= KUPONDAGE.A			##	Returnerer antallet af dage fra begyndelsen af kuponperioden til afregningsdatoen
+COUPDAYSNC		= KUPONDAGE.ANK			##	Returnerer antallet af dage i den kuponperiode, der indeholder afregningsdatoen
+COUPNCD			= KUPONDAG.NÆSTE		##	Returnerer den næste kupondato efter afregningsdatoen
+COUPNUM			= KUPONBETALINGER		##	Returnerer antallet af kuponudbetalinger mellem afregnings- og udløbsdatoen
+COUPPCD			= KUPONDAG.FORRIGE		##	Returnerer den forrige kupondato før afregningsdatoen
+CUMIPMT			= AKKUM.RENTE			##	Returnerer den akkumulerede rente, der betales på et lån mellem to perioder
+CUMPRINC		= AKKUM.HOVEDSTOL		##	Returnerer den akkumulerede nedbringelse af hovedstol mellem to perioder
+DB			= DB				##	Returnerer afskrivningen på et aktiv i en angivet periode ved anvendelse af saldometoden
+DDB			= DSA				##	Returnerer afskrivningsbeløbet for et aktiv over en bestemt periode ved anvendelse af dobbeltsaldometoden eller en anden afskrivningsmetode, som du angiver
+DISC			= DISKONTO			##	Returnerer et værdipapirs diskonto
+DOLLARDE		= KR.DECIMAL			##	Konverterer en kronepris udtrykt som brøk til en kronepris udtrykt som decimaltal
+DOLLARFR		= KR.BRØK			##	Konverterer en kronepris udtrykt som decimaltal til en kronepris udtrykt som brøk
+DURATION		= VARIGHED			##	Returnerer den årlige løbetid for et værdipapir med periodiske renteudbetalinger
+EFFECT			= EFFEKTIV.RENTE		##	Returnerer den årlige effektive rente
+FV			= FV				##	Returnerer fremtidsværdien af en investering
+FVSCHEDULE		= FVTABEL			##	Returnerer den fremtidige værdi af en hovedstol, når der er tilskrevet rente og rentes rente efter forskellige rentesatser
+INTRATE			= RENTEFOD			##	Returnerer renten på et fuldt ud investeret værdipapir
+IPMT			= R.YDELSE			##	Returnerer renten fra en investering for en given periode
+IRR			= IA				##	Returnerer den interne rente for en række pengestrømme
+ISPMT			= ISPMT				##	Beregner den betalte rente i løbet af en bestemt investeringsperiode
+MDURATION		= MVARIGHED			##	Returnerer Macauleys modificerede løbetid for et værdipapir med en formodet pari på kr. 100
+MIRR			= MIA				##	Returnerer den interne forrentning, hvor positive og negative pengestrømme finansieres til forskellig rente
+NOMINAL			= NOMINEL			##	Returnerer den årlige nominelle rente
+NPER			= NPER				##	Returnerer antallet af perioder for en investering
+NPV			= NUTIDSVÆRDI			##	Returnerer nettonutidsværdien for en investering baseret på en række periodiske pengestrømme og en diskonteringssats
+ODDFPRICE		= ULIGE.KURS.PÅLYDENDE		##	Returnerer kursen pr. kr. 100 nominel værdi for et værdipapir med en ulige (kort eller lang) første periode
+ODDFYIELD		= ULIGE.FØRSTE.AFKAST		##	Returnerer afkastet for et værdipapir med ulige første periode
+ODDLPRICE		= ULIGE.SIDSTE.KURS		##	Returnerer kursen pr. kr. 100 nominel værdi for et værdipapir med ulige sidste periode
+ODDLYIELD		= ULIGE.SIDSTE.AFKAST		##	Returnerer afkastet for et værdipapir med ulige sidste periode
+PMT			= YDELSE			##	Returnerer renten fra en investering for en given periode
+PPMT			= H.YDELSE			##	Returnerer ydelsen på hovedstolen for en investering i en given periode
+PRICE			= KURS				##	Returnerer kursen pr. kr 100 nominel værdi for et værdipapir med periodiske renteudbetalinger
+PRICEDISC		= KURS.DISKONTO			##	Returnerer kursen pr. kr 100 nominel værdi for et diskonteret værdipapir
+PRICEMAT		= KURS.UDLØB			##	Returnerer kursen pr. kr 100 nominel værdi for et værdipapir, hvor renten udbetales ved papirets udløb
+PV			= NV				##	Returnerer den nuværende værdi af en investering
+RATE			= RENTE				##	Returnerer renten i hver periode for en annuitet
+RECEIVED		= MODTAGET.VED.UDLØB		##	Returnerer det beløb, der modtages ved udløbet af et fuldt ud investeret værdipapir
+SLN			= LA				##	Returnerer den lineære afskrivning for et aktiv i en enkelt periode
+SYD			= ÅRSAFSKRIVNING		##	Returnerer den årlige afskrivning på et aktiv i en bestemt periode
+TBILLEQ			= STATSOBLIGATION		##	Returnerer det obligationsækvivalente afkast for en statsobligation
+TBILLPRICE		= STATSOBLIGATION.KURS		##	Returnerer kursen pr. kr 100 nominel værdi for en statsobligation
+TBILLYIELD		= STATSOBLIGATION.AFKAST	##	Returnerer en afkastet på en statsobligation
+VDB			= VSA				##	Returnerer afskrivningen på et aktiv i en angivet periode, herunder delperioder, ved brug af dobbeltsaldometoden
+XIRR			= INTERN.RENTE			##	Returnerer den interne rente for en plan over pengestrømme, der ikke behøver at være periodiske
+XNPV			= NETTO.NUTIDSVÆRDI		##	Returnerer nutidsværdien for en plan over pengestrømme, der ikke behøver at være periodiske
+YIELD			= AFKAST			##	Returnerer afkastet for et værdipapir med periodiske renteudbetalinger
+YIELDDISC		= AFKAST.DISKONTO		##	Returnerer det årlige afkast for et diskonteret værdipapir, f.eks. en statsobligation
+YIELDMAT		= AFKAST.UDLØBSDATO		##	Returnerer det årlige afkast for et værdipapir, hvor renten udbetales ved papirets udløb
+
+
+##
+##	Information functions				Informationsfunktioner
+##
+CELL			= CELLE				##	Returnerer oplysninger om formatering, placering eller indhold af en celle
+ERROR.TYPE		= FEJLTYPE			##	Returnerer et tal, der svarer til en fejltype
+INFO			= INFO				##	Returnerer oplysninger om det aktuelle operativmiljø
+ISBLANK			= ER.TOM			##	Returnerer SAND, hvis værdien er tom
+ISERR			= ER.FJL			##	Returnerer SAND, hvis værdien er en fejlværdi undtagen #I/T
+ISERROR			= ER.FEJL			##	Returnerer SAND, hvis værdien er en fejlværdi
+ISEVEN			= ER.LIGE			##	Returnerer SAND, hvis tallet er lige
+ISLOGICAL		= ER.LOGISK			##	Returnerer SAND, hvis værdien er en logisk værdi
+ISNA			= ER.IKKE.TILGÆNGELIG		##	Returnerer SAND, hvis værdien er fejlværdien #I/T
+ISNONTEXT		= ER.IKKE.TEKST			##	Returnerer SAND, hvis værdien ikke er tekst
+ISNUMBER		= ER.TAL			##	Returnerer SAND, hvis værdien er et tal
+ISODD			= ER.ULIGE			##	Returnerer SAND, hvis tallet er ulige
+ISREF			= ER.REFERENCE			##	Returnerer SAND, hvis værdien er en reference
+ISTEXT			= ER.TEKST			##	Returnerer SAND, hvis værdien er tekst
+N			= TAL				##	Returnerer en værdi konverteret til et tal
+NA			= IKKE.TILGÆNGELIG		##	Returnerer fejlværdien #I/T
+TYPE			= VÆRDITYPE			##	Returnerer et tal, der angiver datatypen for en værdi
+
+
+##
+##	Logical functions				Logiske funktioner
+##
+AND			= OG				##	Returnerer SAND, hvis alle argumenterne er sande
+FALSE			= FALSK				##	Returnerer den logiske værdi FALSK
+IF			= HVIS				##	Angiver en logisk test, der skal udføres
+IFERROR			= HVIS.FEJL			##	Returnerer en værdi, du angiver, hvis en formel evauleres som en fejl. Returnerer i modsat fald resultatet af formlen
+NOT			= IKKE				##	Vender argumentets logik om
+OR			= ELLER				##	Returneret værdien SAND, hvis mindst ét argument er sandt
+TRUE			= SAND				##	Returnerer den logiske værdi SAND
+
+
+##
+##	Lookup and reference functions			Opslags- og referencefunktioner
+##
+ADDRESS			= ADRESSE			##	Returnerer en reference som tekst til en enkelt celle i et regneark
+AREAS			= OMRÅDER			##	Returnerer antallet af områder i en reference
+CHOOSE			= VÆLG				##	Vælger en værdi på en liste med værdier
+COLUMN			= KOLONNE			##	Returnerer kolonnenummeret i en reference
+COLUMNS			= KOLONNER			##	Returnerer antallet af kolonner i en reference
+HLOOKUP			= VOPSLAG			##	Søger i den øverste række af en matrix og returnerer værdien af den angivne celle
+HYPERLINK		= HYPERLINK			##	Opretter en genvej kaldet et hyperlink, der åbner et dokument, som er lagret på en netværksserver, på et intranet eller på internettet
+INDEX			= INDEKS			##	Anvender et indeks til at vælge en værdi fra en reference eller en matrix
+INDIRECT		= INDIREKTE			##	Returnerer en reference, der er angivet af en tekstværdi
+LOOKUP			= SLÅ.OP			##	Søger værdier i en vektor eller en matrix
+MATCH			= SAMMENLIGN			##	Søger værdier i en reference eller en matrix
+OFFSET			= FORSKYDNING			##	Returnerer en reference forskudt i forhold til en given reference
+ROW			= RÆKKE				##	Returnerer rækkenummeret for en reference
+ROWS			= RÆKKER			##	Returnerer antallet af rækker i en reference
+RTD			= RTD				##	Henter realtidsdata fra et program, der understøtter COM-automatisering (Automation: En metode til at arbejde med objekter fra et andet program eller udviklingsværktøj. Automation, som tidligere blev kaldt OLE Automation, er en industristandard og en funktion i COM (Component Object Model).)
+TRANSPOSE		= TRANSPONER			##	Returnerer en transponeret matrix
+VLOOKUP			= LOPSLAG			##	Søger i øverste række af en matrix og flytter på tværs af rækken for at returnere en celleværdi
+
+
+##
+##	Math and trigonometry functions			Matematiske og trigonometriske funktioner
+##
+ABS			= ABS				##	Returnerer den absolutte værdi af et tal
+ACOS			= ARCCOS			##	Returnerer et tals arcus cosinus
+ACOSH			= ARCCOSH			##	Returnerer den inverse hyperbolske cosinus af tal
+ASIN			= ARCSIN			##	Returnerer et tals arcus sinus
+ASINH			= ARCSINH			##	Returnerer den inverse hyperbolske sinus for tal
+ATAN			= ARCTAN			##	Returnerer et tals arcus tangens
+ATAN2			= ARCTAN2			##	Returnerer de angivne x- og y-koordinaters arcus tangens
+ATANH			= ARCTANH			##	Returnerer et tals inverse hyperbolske tangens
+CEILING			= AFRUND.LOFT			##	Afrunder et tal til nærmeste heltal eller til nærmeste multiplum af betydning
+COMBIN			= KOMBIN			##	Returnerer antallet af kombinationer for et givet antal objekter
+COS			= COS				##	Returnerer et tals cosinus
+COSH			= COSH				##	Returnerer den inverse hyperbolske cosinus af et tal
+DEGREES			= GRADER			##	Konverterer radianer til grader
+EVEN			= LIGE				##	Runder et tal op til nærmeste lige heltal
+EXP			= EKSP				##	Returnerer e opløftet til en potens af et angivet tal
+FACT			= FAKULTET			##	Returnerer et tals fakultet
+FACTDOUBLE		= DOBBELT.FAKULTET		##	Returnerer et tals dobbelte fakultet
+FLOOR			= AFRUND.GULV			##	Runder et tal ned mod nul
+GCD			= STØRSTE.FÆLLES.DIVISOR	##	Returnerer den største fælles divisor
+INT			= HELTAL			##	Nedrunder et tal til det nærmeste heltal
+LCM			= MINDSTE.FÆLLES.MULTIPLUM	##	Returnerer det mindste fælles multiplum
+LN			= LN				##	Returnerer et tals naturlige logaritme
+LOG			= LOG				##	Returnerer logaritmen for et tal på grundlag af et angivet grundtal
+LOG10			= LOG10				##	Returnerer titalslogaritmen af et tal
+MDETERM			= MDETERM			##	Returnerer determinanten for en matrix
+MINVERSE		= MINVERT			##	Returnerer den inverse matrix for en matrix
+MMULT			= MPRODUKT			##	Returnerer matrixproduktet af to matrixer
+MOD			= REST				##	Returnerer restværdien fra division
+MROUND			= MAFRUND			##	Returnerer et tal afrundet til det ønskede multiplum
+MULTINOMIAL		= MULTINOMIAL			##	Returnerer et multinomialt talsæt
+ODD			= ULIGE				##	Runder et tal op til nærmeste ulige heltal
+PI			= PI				##	Returnerer værdien af pi
+POWER			= POTENS			##	Returnerer resultatet af et tal opløftet til en potens
+PRODUCT			= PRODUKT			##	Multiplicerer argumenterne
+QUOTIENT		= KVOTIENT			##	Returnerer heltalsdelen ved division
+RADIANS			= RADIANER			##	Konverterer grader til radianer
+RAND			= SLUMP				##	Returnerer et tilfældigt tal mellem 0 og 1
+RANDBETWEEN		= SLUMP.MELLEM			##	Returnerer et tilfældigt tal mellem de tal, der angives
+ROMAN			= ROMERTAL			##	Konverterer et arabertal til romertal som tekst
+ROUND			= AFRUND			##	Afrunder et tal til et angivet antal decimaler
+ROUNDDOWN		= RUND.NED			##	Runder et tal ned mod nul
+ROUNDUP			= RUND.OP			##	Runder et tal op, væk fra 0 (nul)
+SERIESSUM		= SERIESUM			##	Returnerer summen af en potensserie baseret på en formel
+SIGN			= FORTEGN			##	Returnerer et tals fortegn
+SIN			= SIN				##	Returnerer en given vinkels sinusværdi
+SINH			= SINH				##	Returnerer den hyperbolske sinus af et tal
+SQRT			= KVROD				##	Returnerer en positiv kvadratrod
+SQRTPI			= KVRODPI			##	Returnerer kvadratroden af (tal * pi;)
+SUBTOTAL		= SUBTOTAL			##	Returnerer en subtotal på en liste eller i en database
+SUM			= SUM				##	Lægger argumenterne sammen
+SUMIF			= SUM.HVIS			##	Lægger de celler sammen, der er specificeret af et givet kriterium.
+SUMIFS			= SUM.HVISER			##	Lægger de celler i et område sammen, der opfylder flere kriterier.
+SUMPRODUCT		= SUMPRODUKT			##	Returnerer summen af produkter af ens matrixkomponenter
+SUMSQ			= SUMKV				##	Returnerer summen af argumenternes kvadrater
+SUMX2MY2		= SUMX2MY2			##	Returnerer summen af differensen mellem kvadrater af ens værdier i to matrixer
+SUMX2PY2		= SUMX2PY2			##	Returnerer summen af summen af kvadrater af tilsvarende værdier i to matrixer
+SUMXMY2			= SUMXMY2			##	Returnerer summen af kvadrater af differenser mellem ens værdier i to matrixer
+TAN			= TAN				##	Returnerer et tals tangens
+TANH			= TANH				##	Returnerer et tals hyperbolske tangens
+TRUNC			= AFKORT			##	Afkorter et tal til et heltal
+
+
+##
+##	Statistical functions				Statistiske funktioner
+##
+AVEDEV			= MAD				##	Returnerer den gennemsnitlige numeriske afvigelse fra stikprøvens middelværdi
+AVERAGE			= MIDDEL			##	Returnerer middelværdien af argumenterne
+AVERAGEA		= MIDDELV			##	Returnerer middelværdien af argumenterne og medtager tal, tekst og logiske værdier
+AVERAGEIF		= MIDDEL.HVIS			##	Returnerer gennemsnittet (den aritmetiske middelværdi) af alle de celler, der opfylder et givet kriterium, i et område
+AVERAGEIFS		= MIDDEL.HVISER			##	Returnerer gennemsnittet (den aritmetiske middelværdi) af alle de celler, der opfylder flere kriterier.
+BETADIST		= BETAFORDELING			##	Returnerer den kumulative betafordelingsfunktion
+BETAINV			= BETAINV			##	Returnerer den inverse kumulative fordelingsfunktion for en angivet betafordeling
+BINOMDIST		= BINOMIALFORDELING		##	Returnerer punktsandsynligheden for binomialfordelingen
+CHIDIST			= CHIFORDELING			##	Returnerer fraktilsandsynligheden for en chi2-fordeling
+CHIINV			= CHIINV			##	Returnerer den inverse fraktilsandsynlighed for en chi2-fordeling
+CHITEST			= CHITEST			##	Foretager en test for uafhængighed
+CONFIDENCE		= KONFIDENSINTERVAL		##	Returnerer et konfidensinterval for en population
+CORREL			= KORRELATION			##	Returnerer korrelationskoefficienten mellem to datasæt
+COUNT			= TÆL				##	Tæller antallet af tal på en liste med argumenter
+COUNTA			= TÆLV				##	Tæller antallet af værdier på en liste med argumenter
+COUNTBLANK		= ANTAL.BLANKE			##	Tæller antallet af tomme celler i et område
+COUNTIF			= TÆLHVIS			##	Tæller antallet af celler, som opfylder de givne kriterier, i et område
+COUNTIFS		= TÆL.HVISER			##	Tæller antallet af de celler, som opfylder flere kriterier, i et område
+COVAR			= KOVARIANS			##	Beregner kovariansen mellem to stokastiske variabler
+CRITBINOM		= KRITBINOM			##	Returnerer den mindste værdi for x, for hvilken det gælder, at fordelingsfunktionen er mindre end eller lig med kriterieværdien.
+DEVSQ			= SAK				##	Returnerer summen af de kvadrerede afvigelser fra middelværdien
+EXPONDIST		= EKSPFORDELING			##	Returnerer eksponentialfordelingen
+FDIST			= FFORDELING			##	Returnerer fraktilsandsynligheden for F-fordelingen
+FINV			= FINV				##	Returnerer den inverse fraktilsandsynlighed for F-fordelingen
+FISHER			= FISHER			##	Returnerer Fisher-transformationen
+FISHERINV		= FISHERINV			##	Returnerer den inverse Fisher-transformation
+FORECAST		= PROGNOSE			##	Returnerer en prognoseværdi baseret på lineær tendens
+FREQUENCY		= FREKVENS			##	Returnerer en frekvensfordeling i en søjlevektor
+FTEST			= FTEST				##	Returnerer resultatet af en F-test til sammenligning af varians
+GAMMADIST		= GAMMAFORDELING		##	Returnerer fordelingsfunktionen for gammafordelingen
+GAMMAINV		= GAMMAINV			##	Returnerer den inverse fordelingsfunktion for gammafordelingen
+GAMMALN			= GAMMALN			##	Returnerer den naturlige logaritme til gammafordelingen, G(x)
+GEOMEAN			= GEOMIDDELVÆRDI		##	Returnerer det geometriske gennemsnit
+GROWTH			= FORØGELSE			##	Returnerer værdier langs en eksponentiel tendens
+HARMEAN			= HARMIDDELVÆRDI		##	Returnerer det harmoniske gennemsnit
+HYPGEOMDIST		= HYPGEOFORDELING		##	Returnerer punktsandsynligheden i en hypergeometrisk fordeling
+INTERCEPT		= SKÆRING			##	Returnerer afskæringsværdien på y-aksen i en lineær regression
+KURT			= TOPSTEJL			##	Returnerer kurtosisværdien for en stokastisk variabel
+LARGE			= STOR				##	Returnerer den k'te største værdi i et datasæt
+LINEST			= LINREGR			##	Returnerer parameterestimaterne for en lineær tendens
+LOGEST			= LOGREGR			##	Returnerer parameterestimaterne for en eksponentiel tendens
+LOGINV			= LOGINV			##	Returnerer den inverse fordelingsfunktion for lognormalfordelingen
+LOGNORMDIST		= LOGNORMFORDELING		##	Returnerer fordelingsfunktionen for lognormalfordelingen
+MAX			= MAKS				##	Returnerer den maksimale værdi på en liste med argumenter.
+MAXA			= MAKSV				##	Returnerer den maksimale værdi på en liste med argumenter og medtager tal, tekst og logiske værdier
+MEDIAN			= MEDIAN			##	Returnerer medianen for de angivne tal
+MIN			= MIN				##	Returnerer den mindste værdi på en liste med argumenter.
+MINA			= MINV				##	Returnerer den mindste værdi på en liste med argumenter og medtager tal, tekst og logiske værdier
+MODE			= HYPPIGST			##	Returnerer den hyppigste værdi i et datasæt
+NEGBINOMDIST		= NEGBINOMFORDELING		##	Returnerer den negative binomialfordeling
+NORMDIST		= NORMFORDELING			##	Returnerer fordelingsfunktionen for normalfordelingen
+NORMINV			= NORMINV			##	Returnerer den inverse fordelingsfunktion for normalfordelingen
+NORMSDIST		= STANDARDNORMFORDELING		##	Returnerer fordelingsfunktionen for standardnormalfordelingen
+NORMSINV		= STANDARDNORMINV		##	Returnerer den inverse fordelingsfunktion for standardnormalfordelingen
+PEARSON			= PEARSON			##	Returnerer Pearsons korrelationskoefficient
+PERCENTILE		= FRAKTIL			##	Returnerer den k'te fraktil for datasættet
+PERCENTRANK		= PROCENTPLADS			##	Returnerer den procentuelle rang for en given værdi i et datasæt
+PERMUT			= PERMUT			##	Returnerer antallet af permutationer for et givet sæt objekter
+POISSON			= POISSON			##	Returnerer fordelingsfunktionen for en Poisson-fordeling
+PROB			= SANDSYNLIGHED			##	Returnerer intervalsandsynligheden
+QUARTILE		= KVARTIL			##	Returnerer kvartilen i et givet datasæt
+RANK			= PLADS				##	Returnerer rangen for et tal på en liste med tal
+RSQ			= FORKLARINGSGRAD		##	Returnerer R2-værdien fra en simpel lineær regression
+SKEW			= SKÆVHED			##	Returnerer skævheden for en stokastisk variabel
+SLOPE			= HÆLDNING			##	Returnerer estimatet på hældningen fra en simpel lineær regression
+SMALL			= MINDSTE			##	Returnerer den k'te mindste værdi i datasættet
+STANDARDIZE		= STANDARDISER			##	Returnerer en standardiseret værdi
+STDEV			= STDAFV			##	Estimerer standardafvigelsen på basis af en stikprøve
+STDEVA			= STDAFVV			##	Beregner standardafvigelsen på basis af en prøve og medtager tal, tekst og logiske værdier
+STDEVP			= STDAFVP			##	Beregner standardafvigelsen på basis af en hel population
+STDEVPA			= STDAFVPV			##	Beregner standardafvigelsen på basis af en hel population og medtager tal, tekst og logiske værdier
+STEYX			= STFYX				##	Returnerer standardafvigelsen for de estimerede y-værdier i den simple lineære regression
+TDIST			= TFORDELING			##	Returnerer fordelingsfunktionen for Student's t-fordeling
+TINV			= TINV				##	Returnerer den inverse fordelingsfunktion for Student's t-fordeling
+TREND			= TENDENS			##	Returnerer værdi under antagelse af en lineær tendens
+TRIMMEAN		= TRIMMIDDELVÆRDI		##	Returnerer den trimmede middelværdi for datasættet
+TTEST			= TTEST				##	Returnerer den sandsynlighed, der er forbundet med Student's t-test
+VAR			= VARIANS			##	Beregner variansen på basis af en prøve
+VARA			= VARIANSV			##	Beregner variansen på basis af en prøve og medtager tal, tekst og logiske værdier
+VARP			= VARIANSP			##	Beregner variansen på basis af hele populationen
+VARPA			= VARIANSPV			##	Beregner variansen på basis af hele populationen og medtager tal, tekst og logiske værdier
+WEIBULL			= WEIBULL			##	Returnerer fordelingsfunktionen for Weibull-fordelingen
+ZTEST			= ZTEST				##	Returnerer sandsynlighedsværdien ved en en-sidet z-test
+
+
+##
+##	Text functions					Tekstfunktioner
+##
+ASC			= ASC				##	Ændrer engelske tegn i fuld bredde (dobbelt-byte) eller katakana i en tegnstreng til tegn i halv bredde (enkelt-byte)
+BAHTTEXT		= BAHTTEKST			##	Konverterer et tal til tekst ved hjælp af valutaformatet ß (baht)
+CHAR			= TEGN				##	Returnerer det tegn, der svarer til kodenummeret
+CLEAN			= RENS				##	Fjerner alle tegn, der ikke kan udskrives, fra tekst
+CODE			= KODE				##	Returnerer en numerisk kode for det første tegn i en tekststreng
+CONCATENATE		= SAMMENKÆDNING			##	Sammenkæder adskillige tekstelementer til ét tekstelement
+DOLLAR			= KR				##	Konverterer et tal til tekst ved hjælp af valutaformatet kr. (kroner)
+EXACT			= EKSAKT			##	Kontrollerer, om to tekstværdier er identiske
+FIND			= FIND				##	Søger efter en tekstværdi i en anden tekstværdi (der skelnes mellem store og små bogstaver)
+FINDB			= FINDB				##	Søger efter en tekstværdi i en anden tekstværdi (der skelnes mellem store og små bogstaver)
+FIXED			= FAST				##	Formaterer et tal som tekst med et fast antal decimaler
+JIS			= JIS				##	Ændrer engelske tegn i halv bredde (enkelt-byte) eller katakana i en tegnstreng til tegn i fuld bredde (dobbelt-byte)
+LEFT			= VENSTRE			##	Returnerer tegnet længst til venstre i en tekstværdi
+LEFTB			= VENSTREB			##	Returnerer tegnet længst til venstre i en tekstværdi
+LEN			= LÆNGDE			##	Returnerer antallet af tegn i en tekststreng
+LENB			= LÆNGDEB			##	Returnerer antallet af tegn i en tekststreng
+LOWER			= SMÅ.BOGSTAVER			##	Konverterer tekst til små bogstaver
+MID			= MIDT				##	Returnerer et bestemt antal tegn fra en tekststreng fra og med den angivne startposition
+MIDB			= MIDTB				##	Returnerer et bestemt antal tegn fra en tekststreng fra og med den angivne startposition
+PHONETIC		= FONETISK			##	Uddrager de fonetiske (furigana) tegn fra en tekststreng
+PROPER			= STORT.FORBOGSTAV		##	Konverterer første bogstav i hvert ord i teksten til stort bogstav
+REPLACE			= ERSTAT			##	Erstatter tegn i tekst
+REPLACEB		= ERSTATB			##	Erstatter tegn i tekst
+REPT			= GENTAG			##	Gentager tekst et givet antal gange
+RIGHT			= HØJRE				##	Returnerer tegnet længste til højre i en tekstværdi
+RIGHTB			= HØJREB			##	Returnerer tegnet længste til højre i en tekstværdi
+SEARCH			= SØG				##	Søger efter en tekstværdi i en anden tekstværdi (der skelnes ikke mellem store og små bogstaver)
+SEARCHB			= SØGB				##	Søger efter en tekstværdi i en anden tekstværdi (der skelnes ikke mellem store og små bogstaver)
+SUBSTITUTE		= UDSKIFT			##	Udskifter gammel tekst med ny tekst i en tekststreng
+T			= T				##	Konverterer argumenterne til tekst
+TEXT			= TEKST				##	Formaterer et tal og konverterer det til tekst
+TRIM			= FJERN.OVERFLØDIGE.BLANKE	##	Fjerner mellemrum fra tekst
+UPPER			= STORE.BOGSTAVER		##	Konverterer tekst til store bogstaver
+VALUE			= VÆRDI				##	Konverterer et tekstargument til et tal
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/de/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/de/config
new file mode 100755
index 0000000..9751c4b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/de/config
@@ -0,0 +1,24 @@
+##
+## PhpSpreadsheet
+##
+
+ArgumentSeparator	= ;
+
+
+##
+##	(For future use)
+##
+currencySymbol	= €
+
+
+##
+##	Excel Error Codes	(For future use)
+
+##
+NULL	= #NULL!
+DIV0	= #DIV/0!
+VALUE	= #WERT!
+REF	= #BEZUG!
+NAME	= #NAME?
+NUM	= #ZAHL!
+NA	= #NV
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/de/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/de/functions
new file mode 100755
index 0000000..01df42f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/de/functions
@@ -0,0 +1,416 @@
+##
+## PhpSpreadsheet
+##
+## Data in this file derived from https://www.excel-function-translation.com/
+##
+##
+
+
+##
+##	Add-in and Automation functions			Add-In- und Automatisierungsfunktionen
+##
+GETPIVOTDATA	= PIVOTDATENZUORDNEN			##	In einem PivotTable-Bericht gespeicherte Daten werden zurückgegeben.
+
+
+##
+##	Cube functions					Cubefunktionen
+##
+CUBEKPIMEMBER		= CUBEKPIELEMENT		##	Gibt Name, Eigenschaft und Measure eines Key Performance Indicators (KPI) zurück und zeigt den Namen und die Eigenschaft in der Zelle an. Ein KPI ist ein quantifizierbares Maß, wie z. B. der monatliche Bruttogewinn oder die vierteljährliche Mitarbeiterfluktuation, mit dessen Hilfe das Leistungsverhalten eines Unternehmens überwacht werden kann.
+CUBEMEMBER		= CUBEELEMENT			##	Gibt ein Element oder ein Tuple in einer Cubehierarchie zurück. Wird verwendet, um zu überprüfen, ob das Element oder Tuple im Cube vorhanden ist.
+CUBEMEMBERPROPERTY	= CUBEELEMENTEIGENSCHAFT	##	Gibt den Wert einer Elementeigenschaft im Cube zurück. Wird verwendet, um zu überprüfen, ob ein Elementname im Cube vorhanden ist, und um die für dieses Element angegebene Eigenschaft zurückzugeben.
+CUBERANKEDMEMBER	= CUBERANGELEMENT		##	Gibt das n-te oder n-rangige Element in einer Menge zurück. Wird verwendet, um mindestens ein Element in einer Menge zurückzugeben, wie z. B. bester Vertriebsmitarbeiter oder 10 beste Kursteilnehmer.
+CUBESET			= CUBEMENGE			##	Definiert eine berechnete Menge Elemente oder Tuples durch Senden eines Mengenausdrucks an den Cube auf dem Server, der die Menge erstellt und an Microsoft Office Excel zurückgibt.
+CUBESETCOUNT		= CUBEMENGENANZAHL		##	Gibt die Anzahl der Elemente in einer Menge zurück.
+CUBEVALUE		= CUBEWERT			##	Gibt einen Aggregatwert aus einem Cube zurück.
+
+
+##
+##	Database functions				Datenbankfunktionen
+##
+DAVERAGE		= DBMITTELWERT			##	Gibt den Mittelwert der ausgewählten Datenbankeinträge zurück
+DCOUNT			= DBANZAHL			##	Zählt die Zellen mit Zahlen in einer Datenbank
+DCOUNTA			= DBANZAHL2			##	Zählt nicht leere Zellen in einer Datenbank
+DGET			= DBAUSZUG			##	Extrahiert aus einer Datenbank einen einzelnen Datensatz, der den angegebenen Kriterien entspricht
+DMAX			= DBMAX				##	Gibt den größten Wert aus ausgewählten Datenbankeinträgen zurück
+DMIN			= DBMIN				##	Gibt den kleinsten Wert aus ausgewählten Datenbankeinträgen zurück
+DPRODUCT		= DBPRODUKT			##	Multipliziert die Werte in einem bestimmten Feld mit Datensätzen, die den Kriterien in einer Datenbank entsprechen
+DSTDEV			= DBSTDABW			##	Schätzt die Standardabweichung auf der Grundlage einer Stichprobe aus ausgewählten Datenbankeinträgen
+DSTDEVP			= DBSTDABWN			##	Berechnet die Standardabweichung auf der Grundlage der Grundgesamtheit ausgewählter Datenbankeinträge
+DSUM			= DBSUMME			##	Addiert die Zahlen in der Feldspalte mit Datensätzen in der Datenbank, die den Kriterien entsprechen
+DVAR			= DBVARIANZ			##	Schätzt die Varianz auf der Grundlage ausgewählter Datenbankeinträge
+DVARP			= DBVARIANZEN			##	Berechnet die Varianz auf der Grundlage der Grundgesamtheit ausgewählter Datenbankeinträge
+
+
+##
+##	Date and time functions				Datums- und Zeitfunktionen
+##
+DATE			= DATUM				##	Gibt die fortlaufende Zahl eines bestimmten Datums zurück
+DATEVALUE		= DATWERT			##	Wandelt ein Datum in Form von Text in eine fortlaufende Zahl um
+DAY			= TAG				##	Wandelt eine fortlaufende Zahl in den Tag des Monats um
+DAYS360			= TAGE360			##	Berechnet die Anzahl der Tage zwischen zwei Datumsangaben ausgehend von einem Jahr, das 360 Tage hat
+EDATE			= EDATUM			##	Gibt die fortlaufende Zahl des Datums zurück, bei dem es sich um die angegebene Anzahl von Monaten vor oder nach dem Anfangstermin handelt
+EOMONTH			= MONATSENDE			##	Gibt die fortlaufende Zahl des letzten Tags des Monats vor oder nach einer festgelegten Anzahl von Monaten zurück
+HOUR			= STUNDE			##	Wandelt eine fortlaufende Zahl in eine Stunde um
+MINUTE			= MINUTE			##	Wandelt eine fortlaufende Zahl in eine Minute um
+MONTH			= MONAT				##	Wandelt eine fortlaufende Zahl in einen Monat um
+NETWORKDAYS		= NETTOARBEITSTAGE		##	Gibt die Anzahl von ganzen Arbeitstagen zwischen zwei Datumswerten zurück
+NOW			= JETZT				##	Gibt die fortlaufende Zahl des aktuellen Datums und der aktuellen Uhrzeit zurück
+SECOND			= SEKUNDE			##	Wandelt eine fortlaufende Zahl in eine Sekunde um
+TIME			= ZEIT				##	Gibt die fortlaufende Zahl einer bestimmten Uhrzeit zurück
+TIMEVALUE		= ZEITWERT			##	Wandelt eine Uhrzeit in Form von Text in eine fortlaufende Zahl um
+TODAY			= HEUTE				##	Gibt die fortlaufende Zahl des heutigen Datums zurück
+WEEKDAY			= WOCHENTAG			##	Wandelt eine fortlaufende Zahl in den Wochentag um
+WEEKNUM			= KALENDERWOCHE			##	Wandelt eine fortlaufende Zahl in eine Zahl um, die angibt, in welche Woche eines Jahres das angegebene Datum fällt
+WORKDAY			= ARBEITSTAG			##	Gibt die fortlaufende Zahl des Datums vor oder nach einer bestimmten Anzahl von Arbeitstagen zurück
+YEAR			= JAHR				##	Wandelt eine fortlaufende Zahl in ein Jahr um
+YEARFRAC		= BRTEILJAHRE			##	Gibt die Anzahl der ganzen Tage zwischen Ausgangsdatum und Enddatum in Bruchteilen von Jahren zurück
+
+
+##
+##	Engineering functions				Konstruktionsfunktionen
+##
+BESSELI			= BESSELI			##	Gibt die geänderte Besselfunktion In(x) zurück
+BESSELJ			= BESSELJ			##	Gibt die Besselfunktion Jn(x) zurück
+BESSELK			= BESSELK			##	Gibt die geänderte Besselfunktion Kn(x) zurück
+BESSELY			= BESSELY			##	Gibt die Besselfunktion Yn(x) zurück
+BIN2DEC			= BININDEZ			##	Wandelt eine binäre Zahl (Dualzahl) in eine dezimale Zahl um
+BIN2HEX			= BININHEX			##	Wandelt eine binäre Zahl (Dualzahl) in eine hexadezimale Zahl um
+BIN2OCT			= BININOKT			##	Wandelt eine binäre Zahl (Dualzahl) in eine oktale Zahl um
+COMPLEX			= KOMPLEXE			##	Wandelt den Real- und Imaginärteil in eine komplexe Zahl um
+CONVERT			= UMWANDELN			##	Wandelt eine Zahl von einem Maßsystem in ein anderes um
+DEC2BIN			= DEZINBIN			##	Wandelt eine dezimale Zahl in eine binäre Zahl (Dualzahl) um
+DEC2HEX			= DEZINHEX			##	Wandelt eine dezimale Zahl in eine hexadezimale Zahl um
+DEC2OCT			= DEZINOKT			##	Wandelt eine dezimale Zahl in eine oktale Zahl um
+DELTA			= DELTA				##	Überprüft, ob zwei Werte gleich sind
+ERF			= GAUSSFEHLER			##	Gibt die Gauss'sche Fehlerfunktion zurück
+ERFC			= GAUSSFKOMPL			##	Gibt das Komplement zur Gauss'schen Fehlerfunktion zurück
+GESTEP			= GGANZZAHL			##	Überprüft, ob eine Zahl größer als ein gegebener Schwellenwert ist
+HEX2BIN			= HEXINBIN			##	Wandelt eine hexadezimale Zahl in eine Binärzahl um
+HEX2DEC			= HEXINDEZ			##	Wandelt eine hexadezimale Zahl in eine dezimale Zahl um
+HEX2OCT			= HEXINOKT			##	Wandelt eine hexadezimale Zahl in eine Oktalzahl um
+IMABS			= IMABS				##	Gibt den Absolutbetrag (Modulo) einer komplexen Zahl zurück
+IMAGINARY		= IMAGINÄRTEIL			##	Gibt den Imaginärteil einer komplexen Zahl zurück
+IMARGUMENT		= IMARGUMENT			##	Gibt das Argument Theta zurück, einen Winkel, der als Bogenmaß ausgedrückt wird
+IMCONJUGATE		= IMKONJUGIERTE			##	Gibt die konjugierte komplexe Zahl zu einer komplexen Zahl zurück
+IMCOS			= IMCOS				##	Gibt den Kosinus einer komplexen Zahl zurück
+IMDIV			= IMDIV				##	Gibt den Quotienten zweier komplexer Zahlen zurück
+IMEXP			= IMEXP				##	Gibt die algebraische Form einer in exponentieller Schreibweise vorliegenden komplexen Zahl zurück
+IMLN			= IMLN				##	Gibt den natürlichen Logarithmus einer komplexen Zahl zurück
+IMLOG10			= IMLOG10			##	Gibt den Logarithmus einer komplexen Zahl zur Basis 10 zurück
+IMLOG2			= IMLOG2			##	Gibt den Logarithmus einer komplexen Zahl zur Basis 2 zurück
+IMPOWER			= IMAPOTENZ			##	Potenziert eine komplexe Zahl mit einer ganzen Zahl
+IMPRODUCT		= IMPRODUKT			##	Gibt das Produkt von komplexen Zahlen zurück
+IMREAL			= IMREALTEIL			##	Gibt den Realteil einer komplexen Zahl zurück
+IMSIN			= IMSIN				##	Gibt den Sinus einer komplexen Zahl zurück
+IMSQRT			= IMWURZEL			##	Gibt die Quadratwurzel einer komplexen Zahl zurück
+IMSUB			= IMSUB				##	Gibt die Differenz zwischen zwei komplexen Zahlen zurück
+IMSUM			= IMSUMME			##	Gibt die Summe von komplexen Zahlen zurück
+OCT2BIN			= OKTINBIN			##	Wandelt eine oktale Zahl in eine binäre Zahl (Dualzahl) um
+OCT2DEC			= OKTINDEZ			##	Wandelt eine oktale Zahl in eine dezimale Zahl um
+OCT2HEX			= OKTINHEX			##	Wandelt eine oktale Zahl in eine hexadezimale Zahl um
+
+
+##
+##	Financial functions				Finanzmathematische Funktionen
+##
+ACCRINT			= AUFGELZINS			##	Gibt die aufgelaufenen Zinsen (Stückzinsen) eines Wertpapiers mit periodischen Zinszahlungen zurück
+ACCRINTM		= AUFGELZINSF			##	Gibt die aufgelaufenen Zinsen (Stückzinsen) eines Wertpapiers zurück, die bei Fälligkeit ausgezahlt werden
+AMORDEGRC		= AMORDEGRK			##	Gibt die Abschreibung für die einzelnen Abschreibungszeiträume mithilfe eines Abschreibungskoeffizienten zurück
+AMORLINC		= AMORLINEARK			##	Gibt die Abschreibung für die einzelnen Abschreibungszeiträume zurück
+COUPDAYBS		= ZINSTERMTAGVA			##	Gibt die Anzahl der Tage vom Anfang des Zinstermins bis zum Abrechnungstermin zurück
+COUPDAYS		= ZINSTERMTAGE			##	Gibt die Anzahl der Tage der Zinsperiode zurück, die den Abrechnungstermin einschließt
+COUPDAYSNC		= ZINSTERMTAGNZ			##	Gibt die Anzahl der Tage vom Abrechnungstermin bis zum nächsten Zinstermin zurück
+COUPNCD			= ZINSTERMNZ			##	Gibt das Datum des ersten Zinstermins nach dem Abrechnungstermin zurück
+COUPNUM			= ZINSTERMZAHL			##	Gibt die Anzahl der Zinstermine zwischen Abrechnungs- und Fälligkeitsdatum zurück
+COUPPCD			= ZINSTERMVZ			##	Gibt das Datum des letzten Zinstermins vor dem Abrechnungstermin zurück
+CUMIPMT			= KUMZINSZ			##	Berechnet die kumulierten Zinsen, die zwischen zwei Perioden zu zahlen sind
+CUMPRINC		= KUMKAPITAL			##	Berechnet die aufgelaufene Tilgung eines Darlehens, die zwischen zwei Perioden zu zahlen ist
+DB			= GDA2				##	Gibt die geometrisch-degressive Abschreibung eines Wirtschaftsguts für eine bestimmte Periode zurück
+DDB			= GDA				##	Gibt die Abschreibung eines Anlageguts für einen angegebenen Zeitraum unter Verwendung der degressiven Doppelraten-Abschreibung oder eines anderen von Ihnen angegebenen Abschreibungsverfahrens zurück
+DISC			= DISAGIO			##	Gibt den in Prozent ausgedrückten Abzinsungssatz eines Wertpapiers zurück
+DOLLARDE		= NOTIERUNGDEZ			##	Wandelt eine Notierung, die als Dezimalbruch ausgedrückt wurde, in eine Dezimalzahl um
+DOLLARFR		= NOTIERUNGBRU			##	Wandelt eine Notierung, die als Dezimalzahl ausgedrückt wurde, in einen Dezimalbruch um
+DURATION		= DURATION			##	Gibt die jährliche Duration eines Wertpapiers mit periodischen Zinszahlungen zurück
+EFFECT			= EFFEKTIV			##	Gibt die jährliche Effektivverzinsung zurück
+FV			= ZW				##	Gibt den zukünftigen Wert (Endwert) einer Investition zurück
+FVSCHEDULE		= ZW2				##	Gibt den aufgezinsten Wert des Anfangskapitals für eine Reihe periodisch unterschiedlicher Zinssätze zurück
+INTRATE			= ZINSSATZ			##	Gibt den Zinssatz eines voll investierten Wertpapiers zurück
+IPMT			= ZINSZ				##	Gibt die Zinszahlung einer Investition für die angegebene Periode zurück
+IRR			= IKV				##	Gibt den internen Zinsfuß einer Investition ohne Finanzierungskosten oder Reinvestitionsgewinne zurück
+ISPMT			= ISPMT				##	Berechnet die während eines bestimmten Zeitraums für eine Investition gezahlten Zinsen
+MDURATION		= MDURATION			##	Gibt die geänderte Dauer für ein Wertpapier mit einem angenommenen Nennwert von 100 € zurück
+MIRR			= QIKV				##	Gibt den internen Zinsfuß zurück, wobei positive und negative Zahlungen zu unterschiedlichen Sätzen finanziert werden
+NOMINAL			= NOMINAL			##	Gibt die jährliche Nominalverzinsung zurück
+NPER			= ZZR				##	Gibt die Anzahl der Zahlungsperioden einer Investition zurück
+NPV			= NBW				##	Gibt den Nettobarwert einer Investition auf Basis periodisch anfallender Zahlungen und eines Abzinsungsfaktors zurück
+ODDFPRICE		= UNREGER.KURS			##	Gibt den Kurs pro 100 € Nennwert eines Wertpapiers mit einem unregelmäßigen ersten Zinstermin zurück
+ODDFYIELD		= UNREGER.REND			##	Gibt die Rendite eines Wertpapiers mit einem unregelmäßigen ersten Zinstermin zurück
+ODDLPRICE		= UNREGLE.KURS			##	Gibt den Kurs pro 100 € Nennwert eines Wertpapiers mit einem unregelmäßigen letzten Zinstermin zurück
+ODDLYIELD		= UNREGLE.REND			##	Gibt die Rendite eines Wertpapiers mit einem unregelmäßigen letzten Zinstermin zurück
+PMT			= RMZ				##	Gibt die periodische Zahlung für eine Annuität zurück
+PPMT			= KAPZ				##	Gibt die Kapitalrückzahlung einer Investition für eine angegebene Periode zurück
+PRICE			= KURS				##	Gibt den Kurs pro 100 € Nennwert eines Wertpapiers zurück, das periodisch Zinsen auszahlt
+PRICEDISC		= KURSDISAGIO			##	Gibt den Kurs pro 100 € Nennwert eines unverzinslichen Wertpapiers zurück
+PRICEMAT		= KURSFÄLLIG			##	Gibt den Kurs pro 100 € Nennwert eines Wertpapiers zurück, das Zinsen am Fälligkeitsdatum auszahlt
+PV			= BW				##	Gibt den Barwert einer Investition zurück
+RATE			= ZINS				##	Gibt den Zinssatz pro Zeitraum einer Annuität zurück
+RECEIVED		= AUSZAHLUNG			##	Gibt den Auszahlungsbetrag eines voll investierten Wertpapiers am Fälligkeitstermin zurück
+SLN			= LIA				##	Gibt die lineare Abschreibung eines Wirtschaftsguts pro Periode zurück
+SYD			= DIA				##	Gibt die arithmetisch-degressive Abschreibung eines Wirtschaftsguts für eine bestimmte Periode zurück
+TBILLEQ			= TBILLÄQUIV			##	Gibt die Rendite für ein Wertpapier zurück
+TBILLPRICE		= TBILLKURS			##	Gibt den Kurs pro 100 € Nennwert eines Wertpapiers zurück
+TBILLYIELD		= TBILLRENDITE			##	Gibt die Rendite für ein Wertpapier zurück
+VDB			= VDB				##	Gibt die degressive Abschreibung eines Wirtschaftsguts für eine bestimmte Periode oder Teilperiode zurück
+XIRR			= XINTZINSFUSS			##	Gibt den internen Zinsfuß einer Reihe nicht periodisch anfallender Zahlungen zurück
+XNPV			= XKAPITALWERT			##	Gibt den Nettobarwert (Kapitalwert) einer Reihe nicht periodisch anfallender Zahlungen zurück
+YIELD			= RENDITE			##	Gibt die Rendite eines Wertpapiers zurück, das periodisch Zinsen auszahlt
+YIELDDISC		= RENDITEDIS			##	Gibt die jährliche Rendite eines unverzinslichen Wertpapiers zurück
+YIELDMAT		= RENDITEFÄLL			##	Gibt die jährliche Rendite eines Wertpapiers zurück, das Zinsen am Fälligkeitsdatum auszahlt
+
+
+##
+##	Information functions				Informationsfunktionen
+##
+CELL			= ZELLE				##	Gibt Informationen zu Formatierung, Position oder Inhalt einer Zelle zurück
+ERROR.TYPE		= FEHLER.TYP			##	Gibt eine Zahl zurück, die einem Fehlertyp entspricht
+INFO			= INFO				##	Gibt Informationen zur aktuellen Betriebssystemumgebung zurück
+ISBLANK			= ISTLEER			##	Gibt WAHR zurück, wenn der Wert leer ist
+ISERR			= ISTFEHL			##	Gibt WAHR zurück, wenn der Wert ein beliebiger Fehlerwert außer #N/V ist
+ISERROR			= ISTFEHLER			##	Gibt WAHR zurück, wenn der Wert ein beliebiger Fehlerwert ist
+ISEVEN			= ISTGERADE			##	Gibt WAHR zurück, wenn es sich um eine gerade Zahl handelt
+ISLOGICAL		= ISTLOG			##	Gibt WAHR zurück, wenn der Wert ein Wahrheitswert ist
+ISNA			= ISTNV				##	Gibt WAHR zurück, wenn der Wert der Fehlerwert #N/V ist
+ISNONTEXT		= ISTKTEXT			##	Gibt WAHR zurück, wenn der Wert ein Element ist, das keinen Text enthält
+ISNUMBER		= ISTZAHL			##	Gibt WAHR zurück, wenn der Wert eine Zahl ist
+ISODD			= ISTUNGERADE			##	Gibt WAHR zurück, wenn es sich um eine ungerade Zahl handelt
+ISREF			= ISTBEZUG			##	Gibt WAHR zurück, wenn der Wert ein Bezug ist
+ISTEXT			= ISTTEXT			##	Gibt WAHR zurück, wenn der Wert ein Element ist, das Text enthält
+N			= N				##	Gibt den in eine Zahl umgewandelten Wert zurück
+NA			= NV				##	Gibt den Fehlerwert #NV zurück
+TYPE			= TYP				##	Gibt eine Zahl zurück, die den Datentyp des angegebenen Werts anzeigt
+
+
+##
+##	Logical functions				Logische Funktionen
+##
+AND			= UND				##	Gibt WAHR zurück, wenn alle zugehörigen Argumente WAHR sind
+FALSE			= FALSCH			##	Gibt den Wahrheitswert FALSCH zurück
+IF			= WENN				##	Gibt einen logischen Test zum Ausführen an
+IFERROR			= WENNFEHLER			##	Gibt einen von Ihnen festgelegten Wert zurück, wenn die Auswertung der Formel zu einem Fehler führt; andernfalls wird das Ergebnis der Formel zurückgegeben
+NOT			= NICHT				##	Kehrt den Wahrheitswert der zugehörigen Argumente um
+OR			= ODER				##	Gibt WAHR zurück, wenn ein Argument WAHR ist
+TRUE			= WAHR				##	Gibt den Wahrheitswert WAHR zurück
+
+
+##
+##	Lookup and reference functions			Nachschlage- und Verweisfunktionen
+##
+ADDRESS			= ADRESSE			##	Gibt einen Bezug auf eine einzelne Zelle in einem Tabellenblatt als Text zurück
+AREAS			= BEREICHE			##	Gibt die Anzahl der innerhalb eines Bezugs aufgeführten Bereiche zurück
+CHOOSE			= WAHL				##	Wählt einen Wert aus eine Liste mit Werten aus
+COLUMN			= SPALTE			##	Gibt die Spaltennummer eines Bezugs zurück
+COLUMNS			= SPALTEN			##	Gibt die Anzahl der Spalten in einem Bezug zurück
+HLOOKUP			= HVERWEIS			##	Sucht in der obersten Zeile einer Matrix und gibt den Wert der angegebenen Zelle zurück
+HYPERLINK		= HYPERLINK			##	Erstellt eine Verknüpfung, über die ein auf einem Netzwerkserver, in einem Intranet oder im Internet gespeichertes Dokument geöffnet wird
+INDEX			= INDEX				##	Verwendet einen Index, um einen Wert aus einem Bezug oder einer Matrix auszuwählen
+INDIRECT		= INDIREKT			##	Gibt einen Bezug zurück, der von einem Textwert angegeben wird
+LOOKUP			= LOOKUP			##	Sucht Werte in einem Vektor oder einer Matrix
+MATCH			= VERGLEICH			##	Sucht Werte in einem Bezug oder einer Matrix
+OFFSET			= BEREICH.VERSCHIEBEN		##	Gibt einen Bezugoffset aus einem gegebenen Bezug zurück
+ROW			= ZEILE				##	Gibt die Zeilennummer eines Bezugs zurück
+ROWS			= ZEILEN			##	Gibt die Anzahl der Zeilen in einem Bezug zurück
+RTD			= RTD				##	Ruft Echtzeitdaten von einem Programm ab, das die COM-Automatisierung (Automatisierung: Ein Verfahren, bei dem aus einer Anwendung oder einem Entwicklungstool heraus mit den Objekten einer anderen Anwendung gearbeitet wird. Die früher als OLE-Automatisierung bezeichnete Automatisierung ist ein Industriestandard und eine Funktion von COM (Component Object Model).) unterstützt
+TRANSPOSE		= MTRANS			##	Gibt die transponierte Matrix einer Matrix zurück
+VLOOKUP			= SVERWEIS			##	Sucht in der ersten Spalte einer Matrix und arbeitet sich durch die Zeile, um den Wert einer Zelle zurückzugeben
+
+
+##
+##	Math and trigonometry functions			Mathematische und trigonometrische Funktionen
+##
+ABS			= ABS				##	Gibt den Absolutwert einer Zahl zurück
+ACOS			= ARCCOS			##	Gibt den Arkuskosinus einer Zahl zurück
+ACOSH			= ARCCOSHYP			##	Gibt den umgekehrten hyperbolischen Kosinus einer Zahl zurück
+ASIN			= ARCSIN			##	Gibt den Arkussinus einer Zahl zurück
+ASINH			= ARCSINHYP			##	Gibt den umgekehrten hyperbolischen Sinus einer Zahl zurück
+ATAN			= ARCTAN			##	Gibt den Arkustangens einer Zahl zurück
+ATAN2			= ARCTAN2			##	Gibt den Arkustangens einer x- und einer y-Koordinate zurück
+ATANH			= ARCTANHYP			##	Gibt den umgekehrten hyperbolischen Tangens einer Zahl zurück
+CEILING			= OBERGRENZE			##	Rundet eine Zahl auf die nächste ganze Zahl oder das nächste Vielfache von Schritt
+COMBIN			= KOMBINATIONEN			##	Gibt die Anzahl der Kombinationen für eine bestimmte Anzahl von Objekten zurück
+COS			= COS				##	Gibt den Kosinus einer Zahl zurück
+COSH			= COSHYP			##	Gibt den hyperbolischen Kosinus einer Zahl zurück
+DEGREES			= GRAD				##	Wandelt Bogenmaß (Radiant) in Grad um
+EVEN			= GERADE			##	Rundet eine Zahl auf die nächste gerade ganze Zahl auf
+EXP			= EXP				##	Potenziert die Basis e mit der als Argument angegebenen Zahl
+FACT			= FAKULTÄT			##	Gibt die Fakultät einer Zahl zurück
+FACTDOUBLE		= ZWEIFAKULTÄT			##	Gibt die Fakultät zu Zahl mit Schrittlänge 2 zurück
+FLOOR			= UNTERGRENZE			##	Rundet die Zahl auf Anzahl_Stellen ab
+GCD			= GGT				##	Gibt den größten gemeinsamen Teiler zurück
+INT			= GANZZAHL			##	Rundet eine Zahl auf die nächstkleinere ganze Zahl ab
+LCM			= KGV				##	Gibt das kleinste gemeinsame Vielfache zurück
+LN			= LN				##	Gibt den natürlichen Logarithmus einer Zahl zurück
+LOG			= LOG				##	Gibt den Logarithmus einer Zahl zu der angegebenen Basis zurück
+LOG10			= LOG10				##	Gibt den Logarithmus einer Zahl zur Basis 10 zurück
+MDETERM			= MDET				##	Gibt die Determinante einer Matrix zurück
+MINVERSE		= MINV				##	Gibt die inverse Matrix einer Matrix zurück
+MMULT			= MMULT				##	Gibt das Produkt zweier Matrizen zurück
+MOD			= REST				##	Gibt den Rest einer Division zurück
+MROUND			= VRUNDEN			##	Gibt eine auf das gewünschte Vielfache gerundete Zahl zurück
+MULTINOMIAL		= POLYNOMIAL			##	Gibt den Polynomialkoeffizienten einer Gruppe von Zahlen zurück
+ODD			= UNGERADE			##	Rundet eine Zahl auf die nächste ungerade ganze Zahl auf
+PI			= PI				##	Gibt den Wert Pi zurück
+POWER			= POTENZ			##	Gibt als Ergebnis eine potenzierte Zahl zurück
+PRODUCT			= PRODUKT			##	Multipliziert die zugehörigen Argumente
+QUOTIENT		= QUOTIENT			##	Gibt den ganzzahligen Anteil einer Division zurück
+RADIANS			= BOGENMASS			##	Wandelt Grad in Bogenmaß (Radiant) um
+RAND			= ZUFALLSZAHL			##	Gibt eine Zufallszahl zwischen 0 und 1 zurück
+RANDBETWEEN		= ZUFALLSBEREICH		##	Gibt eine Zufallszahl aus dem festgelegten Bereich zurück
+ROMAN			= RÖMISCH			##	Wandelt eine arabische Zahl in eine römische Zahl als Text um
+ROUND			= RUNDEN			##	Rundet eine Zahl auf eine bestimmte Anzahl von Dezimalstellen
+ROUNDDOWN		= ABRUNDEN			##	Rundet die Zahl auf Anzahl_Stellen ab
+ROUNDUP			= AUFRUNDEN			##	Rundet die Zahl auf Anzahl_Stellen auf
+SERIESSUM		= POTENZREIHE			##	Gibt die Summe von Potenzen (zur Berechnung von Potenzreihen und dichotomen Wahrscheinlichkeiten) zurück
+SIGN			= VORZEICHEN			##	Gibt das Vorzeichen einer Zahl zurück
+SIN			= SIN				##	Gibt den Sinus einer Zahl zurück
+SINH			= SINHYP			##	Gibt den hyperbolischen Sinus einer Zahl zurück
+SQRT			= WURZEL			##	Gibt die Quadratwurzel einer Zahl zurück
+SQRTPI			= WURZELPI			##	Gibt die Wurzel aus der mit Pi (pi) multiplizierten Zahl zurück
+SUBTOTAL		= TEILERGEBNIS			##	Gibt ein Teilergebnis in einer Liste oder Datenbank zurück
+SUM			= SUMME				##	Addiert die zugehörigen Argumente
+SUMIF			= SUMMEWENN			##	Addiert Zahlen, die mit den Suchkriterien übereinstimmen
+SUMIFS			= SUMMEWENNS			##	Die Zellen, die mehrere Kriterien erfüllen, werden in einem Bereich hinzugefügt
+SUMPRODUCT		= SUMMENPRODUKT			##	Gibt die Summe der Produkte zusammengehöriger Matrixkomponenten zurück
+SUMSQ			= QUADRATESUMME			##	Gibt die Summe der quadrierten Argumente zurück
+SUMX2MY2		= SUMMEX2MY2			##	Gibt die Summe der Differenzen der Quadrate für zusammengehörige Komponenten zweier Matrizen zurück
+SUMX2PY2		= SUMMEX2PY2			##	Gibt die Summe der Quadrate für zusammengehörige Komponenten zweier Matrizen zurück
+SUMXMY2			= SUMMEXMY2			##	Gibt die Summe der quadrierten Differenzen für zusammengehörige Komponenten zweier Matrizen zurück
+TAN			= TAN				##	Gibt den Tangens einer Zahl zurück
+TANH			= TANHYP			##	Gibt den hyperbolischen Tangens einer Zahl zurück
+TRUNC			= KÜRZEN			##	Schneidet die Kommastellen einer Zahl ab und gibt als Ergebnis eine ganze Zahl zurück
+
+
+##
+##	Statistical functions				Statistische Funktionen
+##
+AVEDEV			= MITTELABW			##	Gibt die durchschnittliche absolute Abweichung einer Reihe von Merkmalsausprägungen und ihrem Mittelwert zurück
+AVERAGE			= MITTELWERT			##	Gibt den Mittelwert der zugehörigen Argumente zurück
+AVERAGEA		= MITTELWERTA			##	Gibt den Mittelwert der zugehörigen Argumente, die Zahlen, Text und Wahrheitswerte enthalten, zurück
+AVERAGEIF		= MITTELWERTWENN		##	Der Durchschnittswert (arithmetisches Mittel) für alle Zellen in einem Bereich, die einem angegebenen Kriterium entsprechen, wird zurückgegeben
+AVERAGEIFS		= MITTELWERTWENNS		##	Gibt den Durchschnittswert (arithmetisches Mittel) aller Zellen zurück, die mehreren Kriterien entsprechen
+BETADIST		= BETAVERT			##	Gibt die Werte der kumulierten Betaverteilungsfunktion zurück
+BETAINV			= BETAINV			##	Gibt das Quantil der angegebenen Betaverteilung zurück
+BINOMDIST		= BINOMVERT			##	Gibt Wahrscheinlichkeiten einer binomialverteilten Zufallsvariablen zurück
+CHIDIST			= CHIVERT			##	Gibt Werte der Verteilungsfunktion (1-Alpha) einer Chi-Quadrat-verteilten Zufallsgröße zurück
+CHIINV			= CHIINV			##	Gibt Quantile der Verteilungsfunktion (1-Alpha) der Chi-Quadrat-Verteilung zurück
+CHITEST			= CHITEST			##	Gibt die Teststatistik eines Unabhängigkeitstests zurück
+CONFIDENCE		= KONFIDENZ			##	Ermöglicht die Berechnung des 1-Alpha Konfidenzintervalls für den Erwartungswert einer Zufallsvariablen
+CORREL			= KORREL			##	Gibt den Korrelationskoeffizienten zweier Reihen von Merkmalsausprägungen zurück
+COUNT			= ANZAHL			##	Gibt die Anzahl der Zahlen in der Liste mit Argumenten an
+COUNTA			= ANZAHL2			##	Gibt die Anzahl der Werte in der Liste mit Argumenten an
+COUNTBLANK		= ANZAHLLEEREZELLEN		##	Gibt die Anzahl der leeren Zellen in einem Bereich an
+COUNTIF			= ZÄHLENWENN			##	Gibt die Anzahl der Zellen in einem Bereich an, deren Inhalte mit den Suchkriterien übereinstimmen
+COUNTIFS		= ZÄHLENWENNS			##	Gibt die Anzahl der Zellen in einem Bereich an, deren Inhalte mit mehreren Suchkriterien übereinstimmen
+COVAR			= KOVAR				##	Gibt die Kovarianz zurück, den Mittelwert der für alle Datenpunktpaare gebildeten Produkte der Abweichungen
+CRITBINOM		= KRITBINOM			##	Gibt den kleinsten Wert zurück, für den die kumulierten Wahrscheinlichkeiten der Binomialverteilung kleiner oder gleich einer Grenzwahrscheinlichkeit sind
+DEVSQ			= SUMQUADABW			##	Gibt die Summe der quadrierten Abweichungen der Datenpunkte von ihrem Stichprobenmittelwert zurück
+EXPONDIST		= EXPONVERT			##	Gibt Wahrscheinlichkeiten einer exponential verteilten Zufallsvariablen zurück
+FDIST			= FVERT				##	Gibt Werte der Verteilungsfunktion (1-Alpha) einer F-verteilten Zufallsvariablen zurück
+FINV			= FINV				##	Gibt Quantile der F-Verteilung zurück
+FISHER			= FISHER			##	Gibt die Fisher-Transformation zurück
+FISHERINV		= FISHERINV			##	Gibt die Umkehrung der Fisher-Transformation zurück
+FORECAST		= PROGNOSE			##	Gibt einen Wert zurück, der sich aus einem linearen Trend ergibt
+FREQUENCY		= HÄUFIGKEIT			##	Gibt eine Häufigkeitsverteilung als vertikale Matrix zurück
+FTEST			= FTEST				##	Gibt die Teststatistik eines F-Tests zurück
+GAMMADIST		= GAMMAVERT			##	Gibt Wahrscheinlichkeiten einer gammaverteilten Zufallsvariablen zurück
+GAMMAINV		= GAMMAINV			##	Gibt Quantile der Gammaverteilung zurück
+GAMMALN			= GAMMALN			##	Gibt den natürlichen Logarithmus der Gammafunktion zurück, Γ(x)
+GEOMEAN			= GEOMITTEL			##	Gibt das geometrische Mittel zurück
+GROWTH			= VARIATION			##	Gibt Werte zurück, die sich aus einem exponentiellen Trend ergeben
+HARMEAN			= HARMITTEL			##	Gibt das harmonische Mittel zurück
+HYPGEOMDIST		= HYPGEOMVERT			##	Gibt Wahrscheinlichkeiten einer hypergeometrisch-verteilten Zufallsvariablen zurück
+INTERCEPT		= ACHSENABSCHNITT		##	Gibt den Schnittpunkt der Regressionsgeraden zurück
+KURT			= KURT				##	Gibt die Kurtosis (Exzess) einer Datengruppe zurück
+LARGE			= KGRÖSSTE			##	Gibt den k-größten Wert einer Datengruppe zurück
+LINEST			= RGP				##	Gibt die Parameter eines linearen Trends zurück
+LOGEST			= RKP				##	Gibt die Parameter eines exponentiellen Trends zurück
+LOGINV			= LOGINV			##	Gibt Quantile der Lognormalverteilung zurück
+LOGNORMDIST		= LOGNORMVERT			##	Gibt Werte der Verteilungsfunktion einer lognormalverteilten Zufallsvariablen zurück
+MAX			= MAX				##	Gibt den Maximalwert einer Liste mit Argumenten zurück
+MAXA			= MAXA				##	Gibt den Maximalwert einer Liste mit Argumenten zurück, die Zahlen, Text und Wahrheitswerte enthalten
+MEDIAN			= MEDIAN			##	Gibt den Median der angegebenen Zahlen zurück
+MIN			= MIN				##	Gibt den Minimalwert einer Liste mit Argumenten zurück
+MINA			= MINA				##	Gibt den kleinsten Wert einer Liste mit Argumenten zurück, die Zahlen, Text und Wahrheitswerte enthalten
+MODE			= MODALWERT			##	Gibt den am häufigsten vorkommenden Wert in einer Datengruppe zurück
+NEGBINOMDIST		= NEGBINOMVERT			##	Gibt Wahrscheinlichkeiten einer negativen, binominal verteilten Zufallsvariablen zurück
+NORMDIST		= NORMVERT			##	Gibt Wahrscheinlichkeiten einer normal verteilten Zufallsvariablen zurück
+NORMINV			= NORMINV			##	Gibt Quantile der Normalverteilung zurück
+NORMSDIST		= STANDNORMVERT			##	Gibt Werte der Verteilungsfunktion einer standardnormalverteilten Zufallsvariablen zurück
+NORMSINV		= STANDNORMINV			##	Gibt Quantile der Standardnormalverteilung zurück
+PEARSON			= PEARSON			##	Gibt den Pearsonschen Korrelationskoeffizienten zurück
+PERCENTILE		= QUANTIL			##	Gibt das Alpha-Quantil einer Gruppe von Daten zurück
+PERCENTRANK		= QUANTILSRANG			##	Gibt den prozentualen Rang (Alpha) eines Werts in einer Datengruppe zurück
+PERMUT			= VARIATIONEN			##	Gibt die Anzahl der Möglichkeiten zurück, um k Elemente aus einer Menge von n Elementen ohne Zurücklegen zu ziehen
+POISSON			= POISSON			##	Gibt Wahrscheinlichkeiten einer poissonverteilten Zufallsvariablen zurück
+PROB			= WAHRSCHBEREICH		##	Gibt die Wahrscheinlichkeit für ein von zwei Werten eingeschlossenes Intervall zurück
+QUARTILE		= QUARTILE			##	Gibt die Quartile der Datengruppe zurück
+RANK			= RANG				##	Gibt den Rang zurück, den eine Zahl innerhalb einer Liste von Zahlen einnimmt
+RSQ			= BESTIMMTHEITSMASS		##	Gibt das Quadrat des Pearsonschen Korrelationskoeffizienten zurück
+SKEW			= SCHIEFE			##	Gibt die Schiefe einer Verteilung zurück
+SLOPE			= STEIGUNG			##	Gibt die Steigung der Regressionsgeraden zurück
+SMALL			= KKLEINSTE			##	Gibt den k-kleinsten Wert einer Datengruppe zurück
+STANDARDIZE		= STANDARDISIERUNG		##	Gibt den standardisierten Wert zurück
+STDEV			= STABW				##	Schätzt die Standardabweichung ausgehend von einer Stichprobe
+STDEVA			= STABWA			##	Schätzt die Standardabweichung ausgehend von einer Stichprobe, die Zahlen, Text und Wahrheitswerte enthält
+STDEVP			= STABWN			##	Berechnet die Standardabweichung ausgehend von der Grundgesamtheit
+STDEVPA			= STABWNA			##	Berechnet die Standardabweichung ausgehend von der Grundgesamtheit, die Zahlen, Text und Wahrheitswerte enthält
+STEYX			= STFEHLERYX			##	Gibt den Standardfehler der geschätzten y-Werte für alle x-Werte der Regression zurück
+TDIST			= TVERT				##	Gibt Werte der Verteilungsfunktion (1-Alpha) einer (Student) t-verteilten Zufallsvariablen zurück
+TINV			= TINV				##	Gibt Quantile der t-Verteilung zurück
+TREND			= TREND				##	Gibt Werte zurück, die sich aus einem linearen Trend ergeben
+TRIMMEAN		= GESTUTZTMITTEL		##	Gibt den Mittelwert einer Datengruppe zurück, ohne die Randwerte zu berücksichtigen
+TTEST			= TTEST				##	Gibt die Teststatistik eines Student'schen t-Tests zurück
+VAR			= VARIANZ			##	Schätzt die Varianz ausgehend von einer Stichprobe
+VARA			= VARIANZA			##	Schätzt die Varianz ausgehend von einer Stichprobe, die Zahlen, Text und Wahrheitswerte enthält
+VARP			= VARIANZEN			##	Berechnet die Varianz ausgehend von der Grundgesamtheit
+VARPA			= VARIANZENA			##	Berechnet die Varianz ausgehend von der Grundgesamtheit, die Zahlen, Text und Wahrheitswerte enthält
+WEIBULL			= WEIBULL			##	Gibt Wahrscheinlichkeiten einer weibullverteilten Zufallsvariablen zurück
+ZTEST			= GTEST				##	Gibt den einseitigen Wahrscheinlichkeitswert für einen Gausstest (Normalverteilung) zurück
+
+
+##
+##	Text functions					Textfunktionen
+##
+ASC			= ASC				##	Konvertiert DB-Text in einer Zeichenfolge (lateinische Buchstaben oder Katakana) in SB-Text
+BAHTTEXT		= BAHTTEXT			##	Wandelt eine Zahl in Text im Währungsformat ß (Baht) um
+CHAR			= ZEICHEN			##	Gibt das der Codezahl entsprechende Zeichen zurück
+CLEAN			= SÄUBERN			##	Löscht alle nicht druckbaren Zeichen aus einem Text
+CODE			= CODE				##	Gibt die Codezahl des ersten Zeichens in einem Text zurück
+CONCATENATE		= VERKETTEN			##	Verknüpft mehrere Textelemente zu einem Textelement
+DOLLAR			= DM				##	Wandelt eine Zahl in Text im Währungsformat € (Euro) um
+EXACT			= IDENTISCH			##	Prüft, ob zwei Textwerte identisch sind
+FIND			= FINDEN			##	Sucht nach einem Textwert, der in einem anderen Textwert enthalten ist (Groß-/Kleinschreibung wird unterschieden)
+FINDB			= FINDENB			##	Sucht nach einem Textwert, der in einem anderen Textwert enthalten ist (Groß-/Kleinschreibung wird unterschieden)
+FIXED			= FEST				##	Formatiert eine Zahl als Text mit einer festen Anzahl von Dezimalstellen
+JIS			= JIS				##	Konvertiert SB-Text in einer Zeichenfolge (lateinische Buchstaben oder Katakana) in DB-Text
+LEFT			= LINKS				##	Gibt die Zeichen ganz links in einem Textwert zurück
+LEFTB			= LINKSB			##	Gibt die Zeichen ganz links in einem Textwert zurück
+LEN			= LÄNGE				##	Gibt die Anzahl der Zeichen in einer Zeichenfolge zurück
+LENB			= LÄNGEB			##	Gibt die Anzahl der Zeichen in einer Zeichenfolge zurück
+LOWER			= KLEIN				##	Wandelt Text in Kleinbuchstaben um
+MID			= TEIL				##	Gibt eine bestimmte Anzahl Zeichen aus einer Zeichenfolge ab der von Ihnen angegebenen Stelle zurück
+MIDB			= TEILB				##	Gibt eine bestimmte Anzahl Zeichen aus einer Zeichenfolge ab der von Ihnen angegebenen Stelle zurück
+PHONETIC		= PHONETIC			##	Extrahiert die phonetischen (Furigana-)Zeichen aus einer Textzeichenfolge
+PROPER			= GROSS2			##	Wandelt den ersten Buchstaben aller Wörter eines Textwerts in Großbuchstaben um
+REPLACE			= ERSETZEN			##	Ersetzt Zeichen in Text
+REPLACEB		= ERSETZENB			##	Ersetzt Zeichen in Text
+REPT			= WIEDERHOLEN			##	Wiederholt einen Text so oft wie angegeben
+RIGHT			= RECHTS			##	Gibt die Zeichen ganz rechts in einem Textwert zurück
+RIGHTB			= RECHTSB			##	Gibt die Zeichen ganz rechts in einem Textwert zurück
+SEARCH			= SUCHEN			##	Sucht nach einem Textwert, der in einem anderen Textwert enthalten ist (Groß-/Kleinschreibung wird nicht unterschieden)
+SEARCHB			= SUCHENB			##	Sucht nach einem Textwert, der in einem anderen Textwert enthalten ist (Groß-/Kleinschreibung wird nicht unterschieden)
+SUBSTITUTE		= WECHSELN			##	Ersetzt in einer Zeichenfolge neuen Text gegen alten
+T			= T				##	Wandelt die zugehörigen Argumente in Text um
+TEXT			= TEXT				##	Formatiert eine Zahl und wandelt sie in Text um
+TRIM			= GLÄTTEN			##	Entfernt Leerzeichen aus Text
+UPPER			= GROSS				##	Wandelt Text in Großbuchstaben um
+VALUE			= WERT				##	Wandelt ein Textargument in eine Zahl um
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/en/uk/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/en/uk/config
new file mode 100755
index 0000000..859e4be
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/en/uk/config
@@ -0,0 +1,8 @@
+##
+## PhpSpreadsheet
+##
+
+##
+##	(For future use)
+##
+currencySymbol	= £
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/es/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/es/config
new file mode 100755
index 0000000..5b9b948
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/es/config
@@ -0,0 +1,24 @@
+##
+## PhpSpreadsheet
+##
+
+ArgumentSeparator	= ;
+
+
+##
+##	(For future use)
+##
+currencySymbol	= $	##	I'm surprised that the Excel Documentation suggests $ rather than €
+
+
+##
+##	Excel Error Codes	(For future use)
+
+##
+NULL	= #¡NULO!
+DIV0	= #¡DIV/0!
+VALUE	= #¡VALOR!
+REF	= #¡REF!
+NAME	= #¿NOMBRE?
+NUM	= #¡NÚM!
+NA	= #N/A
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/es/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/es/functions
new file mode 100755
index 0000000..ac1ac86
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/es/functions
@@ -0,0 +1,416 @@
+##
+## PhpSpreadsheet
+##
+## Data in this file derived from https://www.excel-function-translation.com/
+##
+##
+
+
+##
+##	Add-in and Automation functions				Funciones de complementos y automatización
+##
+GETPIVOTDATA		= IMPORTARDATOSDINAMICOS		##	Devuelve los datos almacenados en un informe de tabla dinámica.
+
+
+##
+##	Cube functions						Funciones de cubo
+##
+CUBEKPIMEMBER		= MIEMBROKPICUBO			##	Devuelve un nombre, propiedad y medida de indicador de rendimiento clave (KPI) y muestra el nombre y la propiedad en la celda. Un KPI es una medida cuantificable, como los beneficios brutos mensuales o la facturación trimestral por empleado, que se usa para supervisar el rendimiento de una organización.
+CUBEMEMBER		= MIEMBROCUBO				##	Devuelve un miembro o tupla en una jerarquía de cubo. Se usa para validar la existencia del miembro o la tupla en el cubo.
+CUBEMEMBERPROPERTY	= PROPIEDADMIEMBROCUBO			##	Devuelve el valor de una propiedad de miembro del cubo Se usa para validar la existencia de un nombre de miembro en el cubo y para devolver la propiedad especificada para este miembro.
+CUBERANKEDMEMBER	= MIEMBRORANGOCUBO			##	Devuelve el miembro n, o clasificado, de un conjunto. Se usa para devolver uno o más elementos de un conjunto, por ejemplo, el representante con mejores ventas o los diez mejores alumnos.
+CUBESET			= CONJUNTOCUBO				##	Define un conjunto calculado de miembros o tuplas mediante el envío de una expresión de conjunto al cubo en el servidor, lo que crea el conjunto y, después, devuelve dicho conjunto a Microsoft Office Excel.
+CUBESETCOUNT		= RECUENTOCONJUNTOCUBO			##	Devuelve el número de elementos de un conjunto.
+CUBEVALUE		= VALORCUBO				##	Devuelve un valor agregado de un cubo.
+
+
+##
+##	Database functions					Funciones de base de datos
+##
+DAVERAGE		= BDPROMEDIO				##	Devuelve el promedio de las entradas seleccionadas en la base de datos.
+DCOUNT			= BDCONTAR				##	Cuenta el número de celdas que contienen números en una base de datos.
+DCOUNTA			= BDCONTARA				##	Cuenta el número de celdas no vacías en una base de datos.
+DGET			= BDEXTRAER				##	Extrae de una base de datos un único registro que cumple los criterios especificados.
+DMAX			= BDMAX					##	Devuelve el valor máximo de las entradas seleccionadas de la base de datos.
+DMIN			= BDMIN					##	Devuelve el valor mínimo de las entradas seleccionadas de la base de datos.
+DPRODUCT		= BDPRODUCTO				##	Multiplica los valores de un campo concreto de registros de una base de datos que cumplen los criterios especificados.
+DSTDEV			= BDDESVEST				##	Calcula la desviación estándar a partir de una muestra de entradas seleccionadas en la base de datos.
+DSTDEVP			= BDDESVESTP				##	Calcula la desviación estándar en función de la población total de las entradas seleccionadas de la base de datos.
+DSUM			= BDSUMA				##	Suma los números de la columna de campo de los registros de la base de datos que cumplen los criterios.
+DVAR			= BDVAR					##	Calcula la varianza a partir de una muestra de entradas seleccionadas de la base de datos.
+DVARP			= BDVARP				##	Calcula la varianza a partir de la población total de entradas seleccionadas de la base de datos.
+
+
+##
+##	Date and time functions					Funciones de fecha y hora
+##
+DATE			= FECHA					##	Devuelve el número de serie correspondiente a una fecha determinada.
+DATEVALUE		= FECHANUMERO				##	Convierte una fecha con formato de texto en un valor de número de serie.
+DAY			= DIA					##	Convierte un número de serie en un valor de día del mes.
+DAYS360			= DIAS360				##	Calcula el número de días entre dos fechas a partir de un año de 360 días.
+EDATE			= FECHA.MES				##	Devuelve el número de serie de la fecha equivalente al número indicado de meses anteriores o posteriores a la fecha inicial.
+EOMONTH			= FIN.MES				##	Devuelve el número de serie correspondiente al último día del mes anterior o posterior a un número de meses especificado.
+HOUR			= HORA					##	Convierte un número de serie en un valor de hora.
+MINUTE			= MINUTO				##	Convierte un número de serie en un valor de minuto.
+MONTH			= MES					##	Convierte un número de serie en un valor de mes.
+NETWORKDAYS		= DIAS.LAB				##	Devuelve el número de todos los días laborables existentes entre dos fechas.
+NOW			= AHORA					##	Devuelve el número de serie correspondiente a la fecha y hora actuales.
+SECOND			= SEGUNDO				##	Convierte un número de serie en un valor de segundo.
+TIME			= HORA					##	Devuelve el número de serie correspondiente a una hora determinada.
+TIMEVALUE		= HORANUMERO				##	Convierte una hora con formato de texto en un valor de número de serie.
+TODAY			= HOY					##	Devuelve el número de serie correspondiente al día actual.
+WEEKDAY			= DIASEM				##	Convierte un número de serie en un valor de día de la semana.
+WEEKNUM			= NUM.DE.SEMANA				##	Convierte un número de serie en un número que representa el lugar numérico correspondiente a una semana de un año.
+WORKDAY			= DIA.LAB				##	Devuelve el número de serie de la fecha que tiene lugar antes o después de un número determinado de días laborables.
+YEAR			= AÑO					##	Convierte un número de serie en un valor de año.
+YEARFRAC		= FRAC.AÑO				##	Devuelve la fracción de año que representa el número total de días existentes entre el valor de fecha_inicial y el de fecha_final.
+
+
+##
+##	Engineering functions					Funciones de ingeniería
+##
+BESSELI			= BESSELI				##	Devuelve la función Bessel In(x) modificada.
+BESSELJ			= BESSELJ				##	Devuelve la función Bessel Jn(x).
+BESSELK			= BESSELK				##	Devuelve la función Bessel Kn(x) modificada.
+BESSELY			= BESSELY				##	Devuelve la función Bessel Yn(x).
+BIN2DEC			= BIN.A.DEC				##	Convierte un número binario en decimal.
+BIN2HEX			= BIN.A.HEX				##	Convierte un número binario en hexadecimal.
+BIN2OCT			= BIN.A.OCT				##	Convierte un número binario en octal.
+COMPLEX			= COMPLEJO				##	Convierte coeficientes reales e imaginarios en un número complejo.
+CONVERT			= CONVERTIR				##	Convierte un número de un sistema de medida a otro.
+DEC2BIN			= DEC.A.BIN				##	Convierte un número decimal en binario.
+DEC2HEX			= DEC.A.HEX				##	Convierte un número decimal en hexadecimal.
+DEC2OCT			= DEC.A.OCT				##	Convierte un número decimal en octal.
+DELTA			= DELTA					##	Comprueba si dos valores son iguales.
+ERF			= FUN.ERROR				##	Devuelve la función de error.
+ERFC			= FUN.ERROR.COMPL			##	Devuelve la función de error complementario.
+GESTEP			= MAYOR.O.IGUAL				##	Comprueba si un número es mayor que un valor de umbral.
+HEX2BIN			= HEX.A.BIN				##	Convierte un número hexadecimal en binario.
+HEX2DEC			= HEX.A.DEC				##	Convierte un número hexadecimal en decimal.
+HEX2OCT			= HEX.A.OCT				##	Convierte un número hexadecimal en octal.
+IMABS			= IM.ABS				##	Devuelve el valor absoluto (módulo) de un número complejo.
+IMAGINARY		= IMAGINARIO				##	Devuelve el coeficiente imaginario de un número complejo.
+IMARGUMENT		= IM.ANGULO				##	Devuelve el argumento theta, un ángulo expresado en radianes.
+IMCONJUGATE		= IM.CONJUGADA				##	Devuelve la conjugada compleja de un número complejo.
+IMCOS			= IM.COS				##	Devuelve el coseno de un número complejo.
+IMDIV			= IM.DIV				##	Devuelve el cociente de dos números complejos.
+IMEXP			= IM.EXP				##	Devuelve el valor exponencial de un número complejo.
+IMLN			= IM.LN					##	Devuelve el logaritmo natural (neperiano) de un número complejo.
+IMLOG10			= IM.LOG10				##	Devuelve el logaritmo en base 10 de un número complejo.
+IMLOG2			= IM.LOG2				##	Devuelve el logaritmo en base 2 de un número complejo.
+IMPOWER			= IM.POT				##	Devuelve un número complejo elevado a una potencia entera.
+IMPRODUCT		= IM.PRODUCT				##	Devuelve el producto de números complejos.
+IMREAL			= IM.REAL				##	Devuelve el coeficiente real de un número complejo.
+IMSIN			= IM.SENO				##	Devuelve el seno de un número complejo.
+IMSQRT			= IM.RAIZ2				##	Devuelve la raíz cuadrada de un número complejo.
+IMSUB			= IM.SUSTR				##	Devuelve la diferencia entre dos números complejos.
+IMSUM			= IM.SUM				##	Devuelve la suma de números complejos.
+OCT2BIN			= OCT.A.BIN				##	Convierte un número octal en binario.
+OCT2DEC			= OCT.A.DEC				##	Convierte un número octal en decimal.
+OCT2HEX			= OCT.A.HEX				##	Convierte un número octal en hexadecimal.
+
+
+##
+##	Financial functions					Funciones financieras
+##
+ACCRINT			= INT.ACUM				##	Devuelve el interés acumulado de un valor bursátil con pagos de interés periódicos.
+ACCRINTM		= INT.ACUM.V				##	Devuelve el interés acumulado de un valor bursátil con pagos de interés al vencimiento.
+AMORDEGRC		= AMORTIZ.PROGRE			##	Devuelve la amortización de cada período contable mediante el uso de un coeficiente de amortización.
+AMORLINC		= AMORTIZ.LIN				##	Devuelve la amortización de cada uno de los períodos contables.
+COUPDAYBS		= CUPON.DIAS.L1				##	Devuelve el número de días desde el principio del período de un cupón hasta la fecha de liquidación.
+COUPDAYS		= CUPON.DIAS				##	Devuelve el número de días del período (entre dos cupones) donde se encuentra la fecha de liquidación.
+COUPDAYSNC		= CUPON.DIAS.L2				##	Devuelve el número de días desde la fecha de liquidación hasta la fecha del próximo cupón.
+COUPNCD			= CUPON.FECHA.L2			##	Devuelve la fecha del próximo cupón después de la fecha de liquidación.
+COUPNUM			= CUPON.NUM				##	Devuelve el número de pagos de cupón entre la fecha de liquidación y la fecha de vencimiento.
+COUPPCD			= CUPON.FECHA.L1			##	Devuelve la fecha de cupón anterior a la fecha de liquidación.
+CUMIPMT			= PAGO.INT.ENTRE			##	Devuelve el interés acumulado pagado entre dos períodos.
+CUMPRINC		= PAGO.PRINC.ENTRE			##	Devuelve el capital acumulado pagado de un préstamo entre dos períodos.
+DB			= DB					##	Devuelve la amortización de un bien durante un período específico a través del método de amortización de saldo fijo.
+DDB			= DDB					##	Devuelve la amortización de un bien durante un período específico a través del método de amortización por doble disminución de saldo u otro método que se especifique.
+DISC			= TASA.DESC				##	Devuelve la tasa de descuento de un valor bursátil.
+DOLLARDE		= MONEDA.DEC				##	Convierte una cotización de un valor bursátil expresada en forma fraccionaria en una cotización de un valor bursátil expresada en forma decimal.
+DOLLARFR		= MONEDA.FRAC				##	Convierte una cotización de un valor bursátil expresada en forma decimal en una cotización de un valor bursátil expresada en forma fraccionaria.
+DURATION		= DURACION				##	Devuelve la duración anual de un valor bursátil con pagos de interés periódico.
+EFFECT			= INT.EFECTIVO				##	Devuelve la tasa de interés anual efectiva.
+FV			= VF					##	Devuelve el valor futuro de una inversión.
+FVSCHEDULE		= VF.PLAN				##	Devuelve el valor futuro de un capital inicial después de aplicar una serie de tasas de interés compuesto.
+INTRATE			= TASA.INT				##	Devuelve la tasa de interés para la inversión total de un valor bursátil.
+IPMT			= PAGOINT				##	Devuelve el pago de intereses de una inversión durante un período determinado.
+IRR			= TIR					##	Devuelve la tasa interna de retorno para una serie de flujos de efectivo periódicos.
+ISPMT			= INT.PAGO.DIR				##	Calcula el interés pagado durante un período específico de una inversión.
+MDURATION		= DURACION.MODIF			##	Devuelve la duración de Macauley modificada de un valor bursátil con un valor nominal supuesto de 100 $.
+MIRR			= TIRM					##	Devuelve la tasa interna de retorno donde se financian flujos de efectivo positivos y negativos a tasas diferentes.
+NOMINAL			= TASA.NOMINAL				##	Devuelve la tasa nominal de interés anual.
+NPER			= NPER					##	Devuelve el número de períodos de una inversión.
+NPV			= VNA					##	Devuelve el valor neto actual de una inversión en función de una serie de flujos periódicos de efectivo y una tasa de descuento.
+ODDFPRICE		= PRECIO.PER.IRREGULAR.1		##	Devuelve el precio por un valor nominal de 100 $ de un valor bursátil con un primer período impar.
+ODDFYIELD		= RENDTO.PER.IRREGULAR.1		##	Devuelve el rendimiento de un valor bursátil con un primer período impar.
+ODDLPRICE		= PRECIO.PER.IRREGULAR.2		##	Devuelve el precio por un valor nominal de 100 $ de un valor bursátil con un último período impar.
+ODDLYIELD		= RENDTO.PER.IRREGULAR.2		##	Devuelve el rendimiento de un valor bursátil con un último período impar.
+PMT			= PAGO					##	Devuelve el pago periódico de una anualidad.
+PPMT			= PAGOPRIN				##	Devuelve el pago de capital de una inversión durante un período determinado.
+PRICE			= PRECIO				##	Devuelve el precio por un valor nominal de 100 $ de un valor bursátil que paga una tasa de interés periódico.
+PRICEDISC		= PRECIO.DESCUENTO			##	Devuelve el precio por un valor nominal de 100 $ de un valor bursátil con descuento.
+PRICEMAT		= PRECIO.VENCIMIENTO			##	Devuelve el precio por un valor nominal de 100 $ de un valor bursátil que paga interés a su vencimiento.
+PV			= VALACT				##	Devuelve el valor actual de una inversión.
+RATE			= TASA					##	Devuelve la tasa de interés por período de una anualidad.
+RECEIVED		= CANTIDAD.RECIBIDA			##	Devuelve la cantidad recibida al vencimiento de un valor bursátil completamente invertido.
+SLN			= SLN					##	Devuelve la amortización por método directo de un bien en un período dado.
+SYD			= SYD					##	Devuelve la amortización por suma de dígitos de los años de un bien durante un período especificado.
+TBILLEQ			= LETRA.DE.TES.EQV.A.BONO		##	Devuelve el rendimiento de un bono equivalente a una letra del Tesoro (de EE.UU.)
+TBILLPRICE		= LETRA.DE.TES.PRECIO			##	Devuelve el precio por un valor nominal de 100 $ de una letra del Tesoro (de EE.UU.)
+TBILLYIELD		= LETRA.DE.TES.RENDTO			##	Devuelve el rendimiento de una letra del Tesoro (de EE.UU.)
+VDB			= DVS					##	Devuelve la amortización de un bien durante un período específico o parcial a través del método de cálculo del saldo en disminución.
+XIRR			= TIR.NO.PER				##	Devuelve la tasa interna de retorno para un flujo de efectivo que no es necesariamente periódico.
+XNPV			= VNA.NO.PER				##	Devuelve el valor neto actual para un flujo de efectivo que no es necesariamente periódico.
+YIELD			= RENDTO				##	Devuelve el rendimiento de un valor bursátil que paga intereses periódicos.
+YIELDDISC		= RENDTO.DESC				##	Devuelve el rendimiento anual de un valor bursátil con descuento; por ejemplo, una letra del Tesoro (de EE.UU.)
+YIELDMAT		= RENDTO.VENCTO				##	Devuelve el rendimiento anual de un valor bursátil que paga intereses al vencimiento.
+
+
+##
+##	Information functions					Funciones de información
+##
+CELL			= CELDA					##	Devuelve información acerca del formato, la ubicación o el contenido de una celda.
+ERROR.TYPE		= TIPO.DE.ERROR				##	Devuelve un número que corresponde a un tipo de error.
+INFO			= INFO					##	Devuelve información acerca del entorno operativo en uso.
+ISBLANK			= ESBLANCO				##	Devuelve VERDADERO si el valor está en blanco.
+ISERR			= ESERR					##	Devuelve VERDADERO si el valor es cualquier valor de error excepto #N/A.
+ISERROR			= ESERROR				##	Devuelve VERDADERO si el valor es cualquier valor de error.
+ISEVEN			= ES.PAR				##	Devuelve VERDADERO si el número es par.
+ISLOGICAL		= ESLOGICO				##	Devuelve VERDADERO si el valor es un valor lógico.
+ISNA			= ESNOD					##	Devuelve VERDADERO si el valor es el valor de error #N/A.
+ISNONTEXT		= ESNOTEXTO				##	Devuelve VERDADERO si el valor no es texto.
+ISNUMBER		= ESNUMERO				##	Devuelve VERDADERO si el valor es un número.
+ISODD			= ES.IMPAR				##	Devuelve VERDADERO si el número es impar.
+ISREF			= ESREF					##	Devuelve VERDADERO si el valor es una referencia.
+ISTEXT			= ESTEXTO				##	Devuelve VERDADERO si el valor es texto.
+N			= N					##	Devuelve un valor convertido en un número.
+NA			= ND					##	Devuelve el valor de error #N/A.
+TYPE			= TIPO					##	Devuelve un número que indica el tipo de datos de un valor.
+
+
+##
+##	Logical functions					Funciones lógicas
+##
+AND			= Y					##	Devuelve VERDADERO si todos sus argumentos son VERDADERO.
+FALSE			= FALSO					##	Devuelve el valor lógico FALSO.
+IF			= SI					##	Especifica una prueba lógica que realizar.
+IFERROR			= SI.ERROR				##	Devuelve un valor que se especifica si una fórmula lo evalúa como un error; de lo contrario, devuelve el resultado de la fórmula.
+NOT			= NO					##	Invierte el valor lógico del argumento.
+OR			= O					##	Devuelve VERDADERO si cualquier argumento es VERDADERO.
+TRUE			= VERDADERO				##	Devuelve el valor lógico VERDADERO.
+
+
+##
+##	Lookup and reference functions				Funciones de búsqueda y referencia
+##
+ADDRESS			= DIRECCION				##	Devuelve una referencia como texto a una sola celda de una hoja de cálculo.
+AREAS			= AREAS					##	Devuelve el número de áreas de una referencia.
+CHOOSE			= ELEGIR				##	Elige un valor de una lista de valores.
+COLUMN			= COLUMNA				##	Devuelve el número de columna de una referencia.
+COLUMNS			= COLUMNAS				##	Devuelve el número de columnas de una referencia.
+HLOOKUP			= BUSCARH				##	Busca en la fila superior de una matriz y devuelve el valor de la celda indicada.
+HYPERLINK		= HIPERVINCULO				##	Crea un acceso directo o un salto que abre un documento almacenado en un servidor de red, en una intranet o en Internet.
+INDEX			= INDICE				##	Usa un índice para elegir un valor de una referencia o matriz.
+INDIRECT		= INDIRECTO				##	Devuelve una referencia indicada por un valor de texto.
+LOOKUP			= BUSCAR				##	Busca valores de un vector o una matriz.
+MATCH			= COINCIDIR				##	Busca valores de una referencia o matriz.
+OFFSET			= DESREF				##	Devuelve un desplazamiento de referencia respecto a una referencia dada.
+ROW			= FILA					##	Devuelve el número de fila de una referencia.
+ROWS			= FILAS					##	Devuelve el número de filas de una referencia.
+RTD			= RDTR					##	Recupera datos en tiempo real desde un programa compatible con la automatización COM (automatización: modo de trabajar con los objetos de una aplicación desde otra aplicación o herramienta de entorno. La automatización, antes denominada automatización OLE, es un estándar de la industria y una función del Modelo de objetos componentes (COM).).
+TRANSPOSE		= TRANSPONER				##	Devuelve la transposición de una matriz.
+VLOOKUP			= BUSCARV				##	Busca en la primera columna de una matriz y se mueve en horizontal por la fila para devolver el valor de una celda.
+
+
+##
+##	Math and trigonometry functions				Funciones matemáticas y trigonométricas
+##
+ABS			= ABS					##	Devuelve el valor absoluto de un número.
+ACOS			= ACOS					##	Devuelve el arcocoseno de un número.
+ACOSH			= ACOSH					##	Devuelve el coseno hiperbólico inverso de un número.
+ASIN			= ASENO					##	Devuelve el arcoseno de un número.
+ASINH			= ASENOH				##	Devuelve el seno hiperbólico inverso de un número.
+ATAN			= ATAN					##	Devuelve la arcotangente de un número.
+ATAN2			= ATAN2					##	Devuelve la arcotangente de las coordenadas "x" e "y".
+ATANH			= ATANH					##	Devuelve la tangente hiperbólica inversa de un número.
+CEILING			= MULTIPLO.SUPERIOR			##	Redondea un número al entero más próximo o al múltiplo significativo más cercano.
+COMBIN			= COMBINAT				##	Devuelve el número de combinaciones para un número determinado de objetos.
+COS			= COS					##	Devuelve el coseno de un número.
+COSH			= COSH					##	Devuelve el coseno hiperbólico de un número.
+DEGREES			= GRADOS				##	Convierte radianes en grados.
+EVEN			= REDONDEA.PAR				##	Redondea un número hasta el entero par más próximo.
+EXP			= EXP					##	Devuelve e elevado a la potencia de un número dado.
+FACT			= FACT					##	Devuelve el factorial de un número.
+FACTDOUBLE		= FACT.DOBLE				##	Devuelve el factorial doble de un número.
+FLOOR			= MULTIPLO.INFERIOR			##	Redondea un número hacia abajo, en dirección hacia cero.
+GCD			= M.C.D					##	Devuelve el máximo común divisor.
+INT			= ENTERO				##	Redondea un número hacia abajo hasta el entero más próximo.
+LCM			= M.C.M					##	Devuelve el mínimo común múltiplo.
+LN			= LN					##	Devuelve el logaritmo natural (neperiano) de un número.
+LOG			= LOG					##	Devuelve el logaritmo de un número en una base especificada.
+LOG10			= LOG10					##	Devuelve el logaritmo en base 10 de un número.
+MDETERM			= MDETERM				##	Devuelve la determinante matricial de una matriz.
+MINVERSE		= MINVERSA				##	Devuelve la matriz inversa de una matriz.
+MMULT			= MMULT					##	Devuelve el producto de matriz de dos matrices.
+MOD			= RESIDUO				##	Devuelve el resto de la división.
+MROUND			= REDOND.MULT				##	Devuelve un número redondeado al múltiplo deseado.
+MULTINOMIAL		= MULTINOMIAL				##	Devuelve el polinomio de un conjunto de números.
+ODD			= REDONDEA.IMPAR			##	Redondea un número hacia arriba hasta el entero impar más próximo.
+PI			= PI					##	Devuelve el valor de pi.
+POWER			= POTENCIA				##	Devuelve el resultado de elevar un número a una potencia.
+PRODUCT			= PRODUCTO				##	Multiplica sus argumentos.
+QUOTIENT		= COCIENTE				##	Devuelve la parte entera de una división.
+RADIANS			= RADIANES				##	Convierte grados en radianes.
+RAND			= ALEATORIO				##	Devuelve un número aleatorio entre 0 y 1.
+RANDBETWEEN		= ALEATORIO.ENTRE			##	Devuelve un número aleatorio entre los números que especifique.
+ROMAN			= NUMERO.ROMANO				##	Convierte un número arábigo en número romano, con formato de texto.
+ROUND			= REDONDEAR				##	Redondea un número al número de decimales especificado.
+ROUNDDOWN		= REDONDEAR.MENOS			##	Redondea un número hacia abajo, en dirección hacia cero.
+ROUNDUP			= REDONDEAR.MAS				##	Redondea un número hacia arriba, en dirección contraria a cero.
+SERIESSUM		= SUMA.SERIES				##	Devuelve la suma de una serie de potencias en función de la fórmula.
+SIGN			= SIGNO					##	Devuelve el signo de un número.
+SIN			= SENO					##	Devuelve el seno de un ángulo determinado.
+SINH			= SENOH					##	Devuelve el seno hiperbólico de un número.
+SQRT			= RAIZ					##	Devuelve la raíz cuadrada positiva de un número.
+SQRTPI			= RAIZ2PI				##	Devuelve la raíz cuadrada de un número multiplicado por PI (número * pi).
+SUBTOTAL		= SUBTOTALES				##	Devuelve un subtotal en una lista o base de datos.
+SUM			= SUMA					##	Suma sus argumentos.
+SUMIF			= SUMAR.SI				##	Suma las celdas especificadas que cumplen unos criterios determinados.
+SUMIFS			= SUMAR.SI.CONJUNTO			##	Suma las celdas de un rango que cumplen varios criterios.
+SUMPRODUCT		= SUMAPRODUCTO				##	Devuelve la suma de los productos de los correspondientes componentes de matriz.
+SUMSQ			= SUMA.CUADRADOS			##	Devuelve la suma de los cuadrados de los argumentos.
+SUMX2MY2		= SUMAX2MENOSY2				##	Devuelve la suma de la diferencia de los cuadrados de los valores correspondientes de dos matrices.
+SUMX2PY2		= SUMAX2MASY2				##	Devuelve la suma de la suma de los cuadrados de los valores correspondientes de dos matrices.
+SUMXMY2			= SUMAXMENOSY2				##	Devuelve la suma de los cuadrados de las diferencias de los valores correspondientes de dos matrices.
+TAN			= TAN					##	Devuelve la tangente de un número.
+TANH			= TANH					##	Devuelve la tangente hiperbólica de un número.
+TRUNC			= TRUNCAR				##	Trunca un número a un entero.
+
+
+##
+##	Statistical functions					Funciones estadísticas
+##
+AVEDEV			= DESVPROM				##	Devuelve el promedio de las desviaciones absolutas de la media de los puntos de datos.
+AVERAGE			= PROMEDIO				##	Devuelve el promedio de sus argumentos.
+AVERAGEA		= PROMEDIOA				##	Devuelve el promedio de sus argumentos, incluidos números, texto y valores lógicos.
+AVERAGEIF		= PROMEDIO.SI				##	Devuelve el promedio (media aritmética) de todas las celdas de un rango que cumplen unos criterios determinados.
+AVERAGEIFS		= PROMEDIO.SI.CONJUNTO			##	Devuelve el promedio (media aritmética) de todas las celdas que cumplen múltiples criterios.
+BETADIST		= DISTR.BETA				##	Devuelve la función de distribución beta acumulativa.
+BETAINV			= DISTR.BETA.INV			##	Devuelve la función inversa de la función de distribución acumulativa de una distribución beta especificada.
+BINOMDIST		= DISTR.BINOM				##	Devuelve la probabilidad de una variable aleatoria discreta siguiendo una distribución binomial.
+CHIDIST			= DISTR.CHI				##	Devuelve la probabilidad de una variable aleatoria continua siguiendo una distribución chi cuadrado de una sola cola.
+CHIINV			= PRUEBA.CHI.INV			##	Devuelve la función inversa de la probabilidad de una variable aleatoria continua siguiendo una distribución chi cuadrado de una sola cola.
+CHITEST			= PRUEBA.CHI				##	Devuelve la prueba de independencia.
+CONFIDENCE		= INTERVALO.CONFIANZA			##	Devuelve el intervalo de confianza de la media de una población.
+CORREL			= COEF.DE.CORREL			##	Devuelve el coeficiente de correlación entre dos conjuntos de datos.
+COUNT			= CONTAR				##	Cuenta cuántos números hay en la lista de argumentos.
+COUNTA			= CONTARA				##	Cuenta cuántos valores hay en la lista de argumentos.
+COUNTBLANK		= CONTAR.BLANCO				##	Cuenta el número de celdas en blanco de un rango.
+COUNTIF			= CONTAR.SI				##	Cuenta el número de celdas, dentro del rango, que cumplen el criterio especificado.
+COUNTIFS		= CONTAR.SI.CONJUNTO			##	Cuenta el número de celdas, dentro del rango, que cumplen varios criterios.
+COVAR			= COVAR					##	Devuelve la covarianza, que es el promedio de los productos de las desviaciones para cada pareja de puntos de datos.
+CRITBINOM		= BINOM.CRIT				##	Devuelve el menor valor cuya distribución binomial acumulativa es menor o igual a un valor de criterio.
+DEVSQ			= DESVIA2				##	Devuelve la suma de los cuadrados de las desviaciones.
+EXPONDIST		= DISTR.EXP				##	Devuelve la distribución exponencial.
+FDIST			= DISTR.F				##	Devuelve la distribución de probabilidad F.
+FINV			= DISTR.F.INV				##	Devuelve la función inversa de la distribución de probabilidad F.
+FISHER			= FISHER				##	Devuelve la transformación Fisher.
+FISHERINV		= PRUEBA.FISHER.INV			##	Devuelve la función inversa de la transformación Fisher.
+FORECAST		= PRONOSTICO				##	Devuelve un valor en una tendencia lineal.
+FREQUENCY		= FRECUENCIA				##	Devuelve una distribución de frecuencia como una matriz vertical.
+FTEST			= PRUEBA.F				##	Devuelve el resultado de una prueba F.
+GAMMADIST		= DISTR.GAMMA				##	Devuelve la distribución gamma.
+GAMMAINV		= DISTR.GAMMA.INV			##	Devuelve la función inversa de la distribución gamma acumulativa.
+GAMMALN			= GAMMA.LN				##	Devuelve el logaritmo natural de la función gamma, G(x).
+GEOMEAN			= MEDIA.GEOM				##	Devuelve la media geométrica.
+GROWTH			= CRECIMIENTO				##	Devuelve valores en una tendencia exponencial.
+HARMEAN			= MEDIA.ARMO				##	Devuelve la media armónica.
+HYPGEOMDIST		= DISTR.HIPERGEOM			##	Devuelve la distribución hipergeométrica.
+INTERCEPT		= INTERSECCION.EJE			##	Devuelve la intersección de la línea de regresión lineal.
+KURT			= CURTOSIS				##	Devuelve la curtosis de un conjunto de datos.
+LARGE			= K.ESIMO.MAYOR				##	Devuelve el k-ésimo mayor valor de un conjunto de datos.
+LINEST			= ESTIMACION.LINEAL			##	Devuelve los parámetros de una tendencia lineal.
+LOGEST			= ESTIMACION.LOGARITMICA		##	Devuelve los parámetros de una tendencia exponencial.
+LOGINV			= DISTR.LOG.INV				##	Devuelve la función inversa de la distribución logarítmico-normal.
+LOGNORMDIST		= DISTR.LOG.NORM			##	Devuelve la distribución logarítmico-normal acumulativa.
+MAX			= MAX					##	Devuelve el valor máximo de una lista de argumentos.
+MAXA			= MAXA					##	Devuelve el valor máximo de una lista de argumentos, incluidos números, texto y valores lógicos.
+MEDIAN			= MEDIANA				##	Devuelve la mediana de los números dados.
+MIN			= MIN					##	Devuelve el valor mínimo de una lista de argumentos.
+MINA			= MINA					##	Devuelve el valor mínimo de una lista de argumentos, incluidos números, texto y valores lógicos.
+MODE			= MODA					##	Devuelve el valor más común de un conjunto de datos.
+NEGBINOMDIST		= NEGBINOMDIST				##	Devuelve la distribución binomial negativa.
+NORMDIST		= DISTR.NORM				##	Devuelve la distribución normal acumulativa.
+NORMINV			= DISTR.NORM.INV			##	Devuelve la función inversa de la distribución normal acumulativa.
+NORMSDIST		= DISTR.NORM.ESTAND			##	Devuelve la distribución normal estándar acumulativa.
+NORMSINV		= DISTR.NORM.ESTAND.INV			##	Devuelve la función inversa de la distribución normal estándar acumulativa.
+PEARSON			= PEARSON				##	Devuelve el coeficiente de momento de correlación de producto Pearson.
+PERCENTILE		= PERCENTIL				##	Devuelve el k-ésimo percentil de los valores de un rango.
+PERCENTRANK		= RANGO.PERCENTIL			##	Devuelve el rango porcentual de un valor de un conjunto de datos.
+PERMUT			= PERMUTACIONES				##	Devuelve el número de permutaciones de un número determinado de objetos.
+POISSON			= POISSON				##	Devuelve la distribución de Poisson.
+PROB			= PROBABILIDAD				##	Devuelve la probabilidad de que los valores de un rango se encuentren entre dos límites.
+QUARTILE		= CUARTIL				##	Devuelve el cuartil de un conjunto de datos.
+RANK			= JERARQUIA				##	Devuelve la jerarquía de un número en una lista de números.
+RSQ			= COEFICIENTE.R2			##	Devuelve el cuadrado del coeficiente de momento de correlación de producto Pearson.
+SKEW			= COEFICIENTE.ASIMETRIA			##	Devuelve la asimetría de una distribución.
+SLOPE			= PENDIENTE				##	Devuelve la pendiente de la línea de regresión lineal.
+SMALL			= K.ESIMO.MENOR				##	Devuelve el k-ésimo menor valor de un conjunto de datos.
+STANDARDIZE		= NORMALIZACION				##	Devuelve un valor normalizado.
+STDEV			= DESVEST				##	Calcula la desviación estándar a partir de una muestra.
+STDEVA			= DESVESTA				##	Calcula la desviación estándar a partir de una muestra, incluidos números, texto y valores lógicos.
+STDEVP			= DESVESTP				##	Calcula la desviación estándar en función de toda la población.
+STDEVPA			= DESVESTPA				##	Calcula la desviación estándar en función de toda la población, incluidos números, texto y valores lógicos.
+STEYX			= ERROR.TIPICO.XY			##	Devuelve el error estándar del valor de "y" previsto para cada "x" de la regresión.
+TDIST			= DISTR.T				##	Devuelve la distribución de t de Student.
+TINV			= DISTR.T.INV				##	Devuelve la función inversa de la distribución de t de Student.
+TREND			= TENDENCIA				##	Devuelve valores en una tendencia lineal.
+TRIMMEAN		= MEDIA.ACOTADA				##	Devuelve la media del interior de un conjunto de datos.
+TTEST			= PRUEBA.T				##	Devuelve la probabilidad asociada a una prueba t de Student.
+VAR			= VAR					##	Calcula la varianza en función de una muestra.
+VARA			= VARA					##	Calcula la varianza en función de una muestra, incluidos números, texto y valores lógicos.
+VARP			= VARP					##	Calcula la varianza en función de toda la población.
+VARPA			= VARPA					##	Calcula la varianza en función de toda la población, incluidos números, texto y valores lógicos.
+WEIBULL			= DIST.WEIBULL				##	Devuelve la distribución de Weibull.
+ZTEST			= PRUEBA.Z				##	Devuelve el valor de una probabilidad de una cola de una prueba z.
+
+
+##
+##	Text functions						Funciones de texto
+##
+ASC			= ASC					##	Convierte las letras inglesas o katakana de ancho completo (de dos bytes) dentro de una cadena de caracteres en caracteres de ancho medio (de un byte).
+BAHTTEXT		= TEXTOBAHT				##	Convierte un número en texto, con el formato de moneda ß (Baht).
+CHAR			= CARACTER				##	Devuelve el carácter especificado por el número de código.
+CLEAN			= LIMPIAR				##	Quita del texto todos los caracteres no imprimibles.
+CODE			= CODIGO				##	Devuelve un código numérico del primer carácter de una cadena de texto.
+CONCATENATE		= CONCATENAR				##	Concatena varios elementos de texto en uno solo.
+DOLLAR			= MONEDA				##	Convierte un número en texto, con el formato de moneda $ (dólar).
+EXACT			= IGUAL					##	Comprueba si dos valores de texto son idénticos.
+FIND			= ENCONTRAR				##	Busca un valor de texto dentro de otro (distingue mayúsculas de minúsculas).
+FINDB			= ENCONTRARB				##	Busca un valor de texto dentro de otro (distingue mayúsculas de minúsculas).
+FIXED			= DECIMAL				##	Da formato a un número como texto con un número fijo de decimales.
+JIS			= JIS					##	Convierte las letras inglesas o katakana de ancho medio (de un byte) dentro de una cadena de caracteres en caracteres de ancho completo (de dos bytes).
+LEFT			= IZQUIERDA				##	Devuelve los caracteres del lado izquierdo de un valor de texto.
+LEFTB			= IZQUIERDAB				##	Devuelve los caracteres del lado izquierdo de un valor de texto.
+LEN			= LARGO					##	Devuelve el número de caracteres de una cadena de texto.
+LENB			= LARGOB				##	Devuelve el número de caracteres de una cadena de texto.
+LOWER			= MINUSC				##	Pone el texto en minúsculas.
+MID			= EXTRAE				##	Devuelve un número específico de caracteres de una cadena de texto que comienza en la posición que se especifique.
+MIDB			= EXTRAEB				##	Devuelve un número específico de caracteres de una cadena de texto que comienza en la posición que se especifique.
+PHONETIC		= FONETICO				##	Extrae los caracteres fonéticos (furigana) de una cadena de texto.
+PROPER			= NOMPROPIO				##	Pone en mayúscula la primera letra de cada palabra de un valor de texto.
+REPLACE			= REEMPLAZAR				##	Reemplaza caracteres de texto.
+REPLACEB		= REEMPLAZARB				##	Reemplaza caracteres de texto.
+REPT			= REPETIR				##	Repite el texto un número determinado de veces.
+RIGHT			= DERECHA				##	Devuelve los caracteres del lado derecho de un valor de texto.
+RIGHTB			= DERECHAB				##	Devuelve los caracteres del lado derecho de un valor de texto.
+SEARCH			= HALLAR				##	Busca un valor de texto dentro de otro (no distingue mayúsculas de minúsculas).
+SEARCHB			= HALLARB				##	Busca un valor de texto dentro de otro (no distingue mayúsculas de minúsculas).
+SUBSTITUTE		= SUSTITUIR				##	Sustituye texto nuevo por texto antiguo en una cadena de texto.
+T			= T					##	Convierte sus argumentos a texto.
+TEXT			= TEXTO					##	Da formato a un número y lo convierte en texto.
+TRIM			= ESPACIOS				##	Quita los espacios del texto.
+UPPER			= MAYUSC				##	Pone el texto en mayúsculas.
+VALUE			= VALOR					##	Convierte un argumento de texto en un número.
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fi/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fi/config
new file mode 100755
index 0000000..22aaf58
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fi/config
@@ -0,0 +1,24 @@
+##
+## PhpSpreadsheet
+##
+
+ArgumentSeparator	= ;
+
+
+##
+##	(For future use)
+##
+currencySymbol	= $	#	Symbol not known, should it be a € (Euro)?
+
+
+##
+##	Excel Error Codes	(For future use)
+
+##
+NULL	= #TYHJÄ!
+DIV0	= #JAKO/0!
+VALUE	= #ARVO!
+REF	= #VIITTAUS!
+NAME	= #NIMI?
+NUM	= #LUKU!
+NA	= #PUUTTUU
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fi/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fi/functions
new file mode 100755
index 0000000..289e0ea
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fi/functions
@@ -0,0 +1,416 @@
+##
+## PhpSpreadsheet
+##
+## Data in this file derived from https://www.excel-function-translation.com/
+##
+##
+
+
+##
+##	Add-in and Automation functions			Apuohjelma- ja automaatiofunktiot
+##
+GETPIVOTDATA		= NOUDA.PIVOT.TIEDOT		##	Palauttaa pivot-taulukkoraporttiin tallennettuja tietoja.
+
+
+##
+##	Cube functions					Kuutiofunktiot
+##
+CUBEKPIMEMBER		= KUUTIOKPIJÄSEN		##	Palauttaa suorituskykyilmaisimen (KPI) nimen, ominaisuuden sekä mitan ja näyttää nimen sekä ominaisuuden solussa. KPI on mitattavissa oleva suure, kuten kuukauden bruttotuotto tai vuosineljänneksen työntekijäkohtainen liikevaihto, joiden avulla tarkkaillaan organisaation suorituskykyä.
+CUBEMEMBER		= KUUTIONJÄSEN			##	Palauttaa kuutiohierarkian jäsenen tai monikon. Tällä funktiolla voit tarkistaa, että jäsen tai monikko on olemassa kuutiossa.
+CUBEMEMBERPROPERTY	= KUUTIONJÄSENENOMINAISUUS	##	Palauttaa kuution jäsenominaisuuden arvon. Tällä funktiolla voit tarkistaa, että nimi on olemassa kuutiossa, ja palauttaa tämän jäsenen määritetyn ominaisuuden.
+CUBERANKEDMEMBER	= KUUTIONLUOKITELTUJÄSEN	##	Palauttaa joukon n:nnen jäsenen. Tällä funktiolla voit palauttaa joukosta elementtejä, kuten parhaan myyjän tai 10 parasta opiskelijaa.
+CUBESET			= KUUTIOJOUKKO			##	Määrittää lasketun jäsen- tai monikkojoukon lähettämällä joukon lausekkeita palvelimessa olevalle kuutiolle. Palvelin luo joukon ja palauttaa sen Microsoft Office Excelille.
+CUBESETCOUNT		= KUUTIOJOUKKOJENMÄÄRÄ		##	Palauttaa joukon kohteiden määrän.
+CUBEVALUE		= KUUTIONARVO			##	Palauttaa koostetun arvon kuutiosta.
+
+
+##
+##	Database functions				Tietokantafunktiot
+##
+DAVERAGE		= TKESKIARVO			##	Palauttaa valittujen tietokantamerkintöjen keskiarvon.
+DCOUNT			= TLASKE			##	Laskee tietokannan lukuja sisältävien solujen määrän.
+DCOUNTA			= TLASKEA			##	Laskee tietokannan tietoja sisältävien solujen määrän.
+DGET			= TNOUDA			##	Hakee määritettyjä ehtoja vastaavan tietueen tietokannasta.
+DMAX			= TMAKS				##	Palauttaa suurimman arvon tietokannasta valittujen arvojen joukosta.
+DMIN			= TMIN				##	Palauttaa pienimmän arvon tietokannasta valittujen arvojen joukosta.
+DPRODUCT		= TTULO				##	Kertoo määritetyn ehdon täyttävien tietokannan tietueiden tietyssä kentässä olevat arvot.
+DSTDEV			= TKESKIHAJONTA			##	Laskee keskihajonnan tietokannasta valituista arvoista muodostuvan otoksen perusteella.
+DSTDEVP			= TKESKIHAJONTAP		##	Laskee keskihajonnan tietokannasta valittujen arvojen koko populaation perusteella.
+DSUM			= TSUMMA			##	Lisää luvut määritetyn ehdon täyttävien tietokannan tietueiden kenttäsarakkeeseen.
+DVAR			= TVARIANSSI			##	Laskee varianssin tietokannasta valittujen arvojen otoksen perusteella.
+DVARP			= TVARIANSSIP			##	Laskee varianssin tietokannasta valittujen arvojen koko populaation perusteella.
+
+
+##
+##	Date and time functions				Päivämäärä- ja aikafunktiot
+##
+DATE			= PÄIVÄYS			##	Palauttaa annetun päivämäärän järjestysluvun.
+DATEVALUE		= PÄIVÄYSARVO			##	Muuntaa tekstimuodossa olevan päivämäärän järjestysluvuksi.
+DAY			= PÄIVÄ				##	Muuntaa järjestysluvun kuukauden päiväksi.
+DAYS360			= PÄIVÄT360			##	Laskee kahden päivämäärän välisten päivien määrän käyttäen perustana 360-päiväistä vuotta.
+EDATE			= PÄIVÄ.KUUKAUSI		##	Palauttaa järjestyslukuna päivämäärän, joka poikkeaa aloituspäivän päivämäärästä annetun kuukausimäärän verran joko eteen- tai taaksepäin.
+EOMONTH			= KUUKAUSI.LOPPU		##	Palauttaa järjestyslukuna sen kuukauden viimeisen päivämäärän, joka poikkeaa annetun kuukausimäärän verran eteen- tai taaksepäin.
+HOUR			= TUNNIT			##	Muuntaa järjestysluvun tunneiksi.
+MINUTE			= MINUUTIT			##	Muuntaa järjestysluvun minuuteiksi.
+MONTH			= KUUKAUSI			##	Muuntaa järjestysluvun kuukausiksi.
+NETWORKDAYS		= TYÖPÄIVÄT			##	Palauttaa kahden päivämäärän välissä olevien täysien työpäivien määrän.
+NOW			= NYT				##	Palauttaa kuluvan päivämäärän ja ajan järjestysnumeron.
+SECOND			= SEKUNNIT			##	Muuntaa järjestysluvun sekunneiksi.
+TIME			= AIKA				##	Palauttaa annetun kellonajan järjestysluvun.
+TIMEVALUE		= AIKA_ARVO			##	Muuntaa tekstimuodossa olevan kellonajan järjestysluvuksi.
+TODAY			= TÄMÄ.PÄIVÄ			##	Palauttaa kuluvan päivän päivämäärän järjestysluvun.
+WEEKDAY			= VIIKONPÄIVÄ			##	Muuntaa järjestysluvun viikonpäiväksi.
+WEEKNUM			= VIIKKO.NRO			##	Muuntaa järjestysluvun luvuksi, joka ilmaisee viikon järjestysluvun vuoden alusta laskettuna.
+WORKDAY			= TYÖPÄIVÄ			##	Palauttaa järjestysluvun päivämäärälle, joka sijaitsee annettujen työpäivien verran eteen tai taaksepäin.
+YEAR			= VUOSI				##	Muuntaa järjestysluvun vuosiksi.
+YEARFRAC		= VUOSI.OSA			##	Palauttaa määritettyjen päivämäärien (aloituspäivä ja lopetuspäivä) välisen osan vuodesta.
+
+
+##
+##	Engineering functions				Tekniset funktiot
+##
+BESSELI			= BESSELI			##	Palauttaa muunnetun Bessel-funktion In(x).
+BESSELJ			= BESSELJ			##	Palauttaa Bessel-funktion Jn(x).
+BESSELK			= BESSELK			##	Palauttaa muunnetun Bessel-funktion Kn(x).
+BESSELY			= BESSELY			##	Palauttaa Bessel-funktion Yn(x).
+BIN2DEC			= BINDES			##	Muuntaa binaariluvun desimaaliluvuksi.
+BIN2HEX			= BINHEKSA			##	Muuntaa binaariluvun heksadesimaaliluvuksi.
+BIN2OCT			= BINOKT			##	Muuntaa binaariluvun oktaaliluvuksi.
+COMPLEX			= KOMPLEKSI			##	Muuntaa reaali- ja imaginaariosien kertoimet kompleksiluvuksi.
+CONVERT			= MUUNNA			##	Muuntaa luvun toisen mittajärjestelmän mukaiseksi.
+DEC2BIN			= DESBIN			##	Muuntaa desimaaliluvun binaariluvuksi.
+DEC2HEX			= DESHEKSA			##	Muuntaa kymmenjärjestelmän luvun heksadesimaaliluvuksi.
+DEC2OCT			= DESOKT			##	Muuntaa kymmenjärjestelmän luvun oktaaliluvuksi.
+DELTA			= SAMA.ARVO			##	Tarkistaa, ovatko kaksi arvoa yhtä suuria.
+ERF			= VIRHEFUNKTIO			##	Palauttaa virhefunktion.
+ERFC			= VIRHEFUNKTIO.KOMPLEMENTTI	##	Palauttaa komplementtivirhefunktion.
+GESTEP			= RAJA				##	Testaa, onko luku suurempi kuin kynnysarvo.
+HEX2BIN			= HEKSABIN			##	Muuntaa heksadesimaaliluvun binaariluvuksi.
+HEX2DEC			= HEKSADES			##	Muuntaa heksadesimaaliluvun desimaaliluvuksi.
+HEX2OCT			= HEKSAOKT			##	Muuntaa heksadesimaaliluvun oktaaliluvuksi.
+IMABS			= KOMPLEKSI.ITSEISARVO		##	Palauttaa kompleksiluvun itseisarvon (moduluksen).
+IMAGINARY		= KOMPLEKSI.IMAG		##	Palauttaa kompleksiluvun imaginaariosan kertoimen.
+IMARGUMENT		= KOMPLEKSI.ARG			##	Palauttaa theeta-argumentin, joka on radiaaneina annettu kulma.
+IMCONJUGATE		= KOMPLEKSI.KONJ		##	Palauttaa kompleksiluvun konjugaattiluvun.
+IMCOS			= KOMPLEKSI.COS			##	Palauttaa kompleksiluvun kosinin.
+IMDIV			= KOMPLEKSI.OSAM		##	Palauttaa kahden kompleksiluvun osamäärän.
+IMEXP			= KOMPLEKSI.EKSP		##	Palauttaa kompleksiluvun eksponentin.
+IMLN			= KOMPLEKSI.LN			##	Palauttaa kompleksiluvun luonnollisen logaritmin.
+IMLOG10			= KOMPLEKSI.LOG10		##	Palauttaa kompleksiluvun kymmenkantaisen logaritmin.
+IMLOG2			= KOMPLEKSI.LOG2		##	Palauttaa kompleksiluvun kaksikantaisen logaritmin.
+IMPOWER			= KOMPLEKSI.POT			##	Palauttaa kokonaislukupotenssiin korotetun kompleksiluvun.
+IMPRODUCT		= KOMPLEKSI.TULO		##	Palauttaa kompleksilukujen tulon.
+IMREAL			= KOMPLEKSI.REAALI		##	Palauttaa kompleksiluvun reaaliosan kertoimen.
+IMSIN			= KOMPLEKSI.SIN			##	Palauttaa kompleksiluvun sinin.
+IMSQRT			= KOMPLEKSI.NELIÖJ		##	Palauttaa kompleksiluvun neliöjuuren.
+IMSUB			= KOMPLEKSI.EROTUS		##	Palauttaa kahden kompleksiluvun erotuksen.
+IMSUM			= KOMPLEKSI.SUM			##	Palauttaa kompleksilukujen summan.
+OCT2BIN			= OKTBIN			##	Muuntaa oktaaliluvun binaariluvuksi.
+OCT2DEC			= OKTDES			##	Muuntaa oktaaliluvun desimaaliluvuksi.
+OCT2HEX			= OKTHEKSA			##	Muuntaa oktaaliluvun heksadesimaaliluvuksi.
+
+
+##
+##	Financial functions				Rahoitusfunktiot
+##
+ACCRINT			= KERTYNYT.KORKO		##	Laskee arvopaperille kertyneen koron, kun korko kertyy säännöllisin väliajoin.
+ACCRINTM		= KERTYNYT.KORKO.LOPUSSA	##	Laskee arvopaperille kertyneen koron, kun korko maksetaan eräpäivänä.
+AMORDEGRC		= AMORDEGRC			##	Laskee kunkin laskentakauden poiston poistokerrointa käyttämällä.
+AMORLINC		= AMORLINC			##	Palauttaa kunkin laskentakauden poiston.
+COUPDAYBS		= KORKOPÄIVÄT.ALUSTA		##	Palauttaa koronmaksukauden aloituspäivän ja tilityspäivän välisen ajanjakson päivien määrän.
+COUPDAYS		= KORKOPÄIVÄT			##	Palauttaa päivien määrän koronmaksukaudelta, johon tilityspäivä kuuluu.
+COUPDAYSNC		= KORKOPÄIVÄT.SEURAAVA		##	Palauttaa tilityspäivän ja seuraavan koronmaksupäivän välisen ajanjakson päivien määrän.
+COUPNCD			= KORKOMAKSU.SEURAAVA		##	Palauttaa tilityspäivän jälkeisen seuraavan koronmaksupäivän.
+COUPNUM			= KORKOPÄIVÄJAKSOT		##	Palauttaa arvopaperin ostopäivän ja erääntymispäivän välisten koronmaksupäivien määrän.
+COUPPCD			= KORKOPÄIVÄ.EDELLINEN		##	Palauttaa tilityspäivää edeltävän koronmaksupäivän.
+CUMIPMT			= MAKSETTU.KORKO		##	Palauttaa kahden jakson välisenä aikana kertyneen koron.
+CUMPRINC		= MAKSETTU.LYHENNYS		##	Palauttaa lainalle kahden jakson välisenä aikana kertyneen lyhennyksen.
+DB			= DB				##	Palauttaa kauden kirjanpidollisen poiston amerikkalaisen DB-menetelmän (Fixed-declining balance) mukaan.
+DDB			= DDB				##	Palauttaa kauden kirjanpidollisen poiston amerikkalaisen DDB-menetelmän (Double-Declining Balance) tai jonkin muun määrittämäsi menetelmän mukaan.
+DISC			= DISKONTTOKORKO		##	Palauttaa arvopaperin diskonttokoron.
+DOLLARDE		= VALUUTTA.DES			##	Muuntaa murtolukuna ilmoitetun valuuttamäärän desimaaliluvuksi.
+DOLLARFR		= VALUUTTA.MURTO		##	Muuntaa desimaalilukuna ilmaistun valuuttamäärän murtoluvuksi.
+DURATION		= KESTO				##	Palauttaa keston arvopaperille, jonka koronmaksu tapahtuu säännöllisesti.
+EFFECT			= KORKO.EFEKT			##	Palauttaa todellisen vuosikoron.
+FV			= TULEVA.ARVO			##	Palauttaa sijoituksen tulevan arvon.
+FVSCHEDULE		= TULEVA.ARVO.ERIKORKO		##	Palauttaa pääoman tulevan arvon, kun pääomalle on kertynyt korkoa vaihtelevasti.
+INTRATE			= KORKO.ARVOPAPERI		##	Palauttaa arvopaperin korkokannan täysin sijoitetulle arvopaperille.
+IPMT			= IPMT				##	Laskee sijoitukselle tai lainalle tiettynä ajanjaksona kertyvän koron.
+IRR			= SISÄINEN.KORKO		##	Laskee sisäisen korkokannan kassavirrasta muodostuvalle sarjalle.
+ISPMT			= ONMAKSU			##	Laskee sijoituksen maksetun koron tietyllä jaksolla.
+MDURATION		= KESTO.MUUNN			##	Palauttaa muunnetun Macauley-keston arvopaperille, jonka oletettu nimellisarvo on 100 euroa.
+MIRR			= MSISÄINEN			##	Palauttaa sisäisen korkokannan, kun positiivisten ja negatiivisten kassavirtojen rahoituskorko on erilainen.
+NOMINAL			= KORKO.VUOSI			##	Palauttaa vuosittaisen nimelliskoron.
+NPER			= NJAKSO			##	Palauttaa sijoituksen jaksojen määrän.
+NPV			= NNA				##	Palauttaa sijoituksen nykyarvon toistuvista kassavirroista muodostuvan sarjan ja diskonttokoron perusteella.
+ODDFPRICE		= PARITON.ENS.NIMELLISARVO	##	Palauttaa arvopaperin hinnan tilanteessa, jossa ensimmäinen jakso on pariton.
+ODDFYIELD		= PARITON.ENS.TUOTTO		##	Palauttaa arvopaperin tuoton tilanteessa, jossa ensimmäinen jakso on pariton.
+ODDLPRICE		= PARITON.VIIM.NIMELLISARVO	##	Palauttaa arvopaperin hinnan tilanteessa, jossa viimeinen jakso on pariton.
+ODDLYIELD		= PARITON.VIIM.TUOTTO		##	Palauttaa arvopaperin tuoton tilanteessa, jossa viimeinen jakso on pariton.
+PMT			= MAKSU				##	Palauttaa annuiteetin kausittaisen maksuerän.
+PPMT			= PPMT				##	Laskee sijoitukselle tai lainalle tiettynä ajanjaksona maksettavan lyhennyksen.
+PRICE			= HINTA				##	Palauttaa hinnan 100 euron nimellisarvoa kohden arvopaperille, jonka korko maksetaan säännöllisin väliajoin.
+PRICEDISC		= HINTA.DISK			##	Palauttaa diskontatun arvopaperin hinnan 100 euron nimellisarvoa kohden.
+PRICEMAT		= HINTA.LUNASTUS		##	Palauttaa hinnan 100 euron nimellisarvoa kohden arvopaperille, jonka korko maksetaan erääntymispäivänä.
+PV			= NA				##	Palauttaa sijoituksen nykyarvon.
+RATE			= KORKO				##	Palauttaa annuiteetin kausittaisen korkokannan.
+RECEIVED		= SAATU.HINTA			##	Palauttaa arvopaperin tuoton erääntymispäivänä kokonaan maksetulle sijoitukselle.
+SLN			= STP				##	Palauttaa sijoituksen tasapoiston yhdeltä jaksolta.
+SYD			= VUOSIPOISTO			##	Palauttaa sijoituksen vuosipoiston annettuna kautena amerikkalaisen SYD-menetelmän (Sum-of-Year's Digits) avulla.
+TBILLEQ			= OBLIG.TUOTTOPROS		##	Palauttaa valtion obligaation tuoton vastaavana joukkovelkakirjan tuottona.
+TBILLPRICE		= OBLIG.HINTA			##	Palauttaa obligaation hinnan 100 euron nimellisarvoa kohden.
+TBILLYIELD		= OBLIG.TUOTTO			##	Palauttaa obligaation tuoton.
+VDB			= VDB				##	Palauttaa annetun kauden tai kauden osan kirjanpidollisen poiston amerikkalaisen DB-menetelmän (Fixed-declining balance) mukaan.
+XIRR			= SISÄINEN.KORKO.JAKSOTON	##	Palauttaa sisäisen korkokannan kassavirtojen sarjoille, jotka eivät välttämättä ole säännöllisiä.
+XNPV			= NNA.JAKSOTON			##	Palauttaa nettonykyarvon kassavirtasarjalle, joka ei välttämättä ole kausittainen.
+YIELD			= TUOTTO			##	Palauttaa tuoton arvopaperille, jonka korko maksetaan säännöllisin väliajoin.
+YIELDDISC		= TUOTTO.DISK			##	Palauttaa diskontatun arvopaperin, kuten obligaation, vuosittaisen tuoton.
+YIELDMAT		= TUOTTO.ERÄP			##	Palauttaa erääntymispäivänään korkoa tuottavan arvopaperin vuosittaisen tuoton.
+
+
+##
+##	Information functions				Erikoisfunktiot
+##
+CELL			= SOLU				##	Palauttaa tietoja solun muotoilusta, sijainnista ja sisällöstä.
+ERROR.TYPE		= VIRHEEN.LAJI			##	Palauttaa virhetyyppiä vastaavan luvun.
+INFO			= KUVAUS			##	Palauttaa tietoja nykyisestä käyttöympäristöstä.
+ISBLANK			= ONTYHJÄ			##	Palauttaa arvon TOSI, jos arvo on tyhjä.
+ISERR			= ONVIRH			##	Palauttaa arvon TOSI, jos arvo on mikä tahansa virhearvo paitsi arvo #PUUTTUU!.
+ISERROR			= ONVIRHE			##	Palauttaa arvon TOSI, jos arvo on mikä tahansa virhearvo.
+ISEVEN			= ONPARILLINEN			##	Palauttaa arvon TOSI, jos arvo on parillinen.
+ISLOGICAL		= ONTOTUUS			##	Palauttaa arvon TOSI, jos arvo on mikä tahansa looginen arvo.
+ISNA			= ONPUUTTUU			##	Palauttaa arvon TOSI, jos virhearvo on #PUUTTUU!.
+ISNONTEXT		= ONEI_TEKSTI			##	Palauttaa arvon TOSI, jos arvo ei ole teksti.
+ISNUMBER		= ONLUKU			##	Palauttaa arvon TOSI, jos arvo on luku.
+ISODD			= ONPARITON			##	Palauttaa arvon TOSI, jos arvo on pariton.
+ISREF			= ONVIITT			##	Palauttaa arvon TOSI, jos arvo on viittaus.
+ISTEXT			= ONTEKSTI			##	Palauttaa arvon TOSI, jos arvo on teksti.
+N			= N				##	Palauttaa arvon luvuksi muunnettuna.
+NA			= PUUTTUU			##	Palauttaa virhearvon #PUUTTUU!.
+TYPE			= TYYPPI			##	Palauttaa luvun, joka ilmaisee arvon tietotyypin.
+
+
+##
+##	Logical functions				Loogiset funktiot
+##
+AND			= JA				##	Palauttaa arvon TOSI, jos kaikkien argumenttien arvo on TOSI.
+FALSE			= EPÄTOSI			##	Palauttaa totuusarvon EPÄTOSI.
+IF			= JOS				##	Määrittää suoritettavan loogisen testin.
+IFERROR			= JOSVIRHE			##	Palauttaa määrittämäsi arvon, jos kaavan tulos on virhe; muussa tapauksessa palauttaa kaavan tuloksen.
+NOT			= EI				##	Kääntää argumentin loogisen arvon.
+OR			= TAI				##	Palauttaa arvon TOSI, jos minkä tahansa argumentin arvo on TOSI.
+TRUE			= TOSI				##	Palauttaa totuusarvon TOSI.
+
+
+##
+##	Lookup and reference functions			Haku- ja viitefunktiot
+##
+ADDRESS			= OSOITE			##	Palauttaa laskentataulukon soluun osoittavan viittauksen tekstinä.
+AREAS			= ALUEET			##	Palauttaa viittauksessa olevien alueiden määrän.
+CHOOSE			= VALITSE.INDEKSI		##	Valitsee arvon arvoluettelosta.
+COLUMN			= SARAKE			##	Palauttaa viittauksen sarakenumeron.
+COLUMNS			= SARAKKEET			##	Palauttaa viittauksessa olevien sarakkeiden määrän.
+HLOOKUP			= VHAKU				##	Suorittaa haun matriisin ylimmältä riviltä ja palauttaa määritetyn solun arvon.
+HYPERLINK		= HYPERLINKKI			##	Luo pikakuvakkeen tai tekstin, joka avaa verkkopalvelimeen, intranetiin tai Internetiin tallennetun tiedoston.
+INDEX			= INDEKSI			##	Valitsee arvon viittauksesta tai matriisista indeksin mukaan.
+INDIRECT		= EPÄSUORA			##	Palauttaa tekstiarvona ilmaistun viittauksen.
+LOOKUP			= HAKU				##	Etsii arvoja vektorista tai matriisista.
+MATCH			= VASTINE			##	Etsii arvoja viittauksesta tai matriisista.
+OFFSET			= SIIRTYMÄ			##	Palauttaa annetun viittauksen siirtymän.
+ROW			= RIVI				##	Palauttaa viittauksen rivinumeron.
+ROWS			= RIVIT				##	Palauttaa viittauksessa olevien rivien määrän.
+RTD			= RTD				##	Noutaa COM-automaatiota (automaatio: Tapa käsitellä sovelluksen objekteja toisesta sovelluksesta tai kehitystyökalusta. Automaatio, jota aiemmin kutsuttiin OLE-automaatioksi, on teollisuusstandardi ja COM-mallin (Component Object Model) ominaisuus.) tukevasta ohjelmasta reaaliaikaisia tietoja.
+TRANSPOSE		= TRANSPONOI			##	Palauttaa matriisin käänteismatriisin.
+VLOOKUP			= PHAKU				##	Suorittaa haun matriisin ensimmäisestä sarakkeesta ja palauttaa rivillä olevan solun arvon.
+
+
+##
+##	Math and trigonometry functions			Matemaattiset ja trigonometriset funktiot
+##
+ABS			= ITSEISARVO			##	Palauttaa luvun itseisarvon.
+ACOS			= ACOS				##	Palauttaa luvun arkuskosinin.
+ACOSH			= ACOSH				##	Palauttaa luvun käänteisen hyperbolisen kosinin.
+ASIN			= ASIN				##	Palauttaa luvun arkussinin.
+ASINH			= ASINH				##	Palauttaa luvun käänteisen hyperbolisen sinin.
+ATAN			= ATAN				##	Palauttaa luvun arkustangentin.
+ATAN2			= ATAN2				##	Palauttaa arkustangentin x- ja y-koordinaatin perusteella.
+ATANH			= ATANH				##	Palauttaa luvun käänteisen hyperbolisen tangentin.
+CEILING			= PYÖRISTÄ.KERR.YLÖS		##	Pyöristää luvun lähimpään kokonaislukuun tai tarkkuusargumentin lähimpään kerrannaiseen.
+COMBIN			= KOMBINAATIO			##	Palauttaa mahdollisten kombinaatioiden määrän annetulle objektien määrälle.
+COS			= COS				##	Palauttaa luvun kosinin.
+COSH			= COSH				##	Palauttaa luvun hyperbolisen kosinin.
+DEGREES			= ASTEET			##	Muuntaa radiaanit asteiksi.
+EVEN			= PARILLINEN			##	Pyöristää luvun ylöspäin lähimpään parilliseen kokonaislukuun.
+EXP			= EKSPONENTTI			##	Palauttaa e:n korotettuna annetun luvun osoittamaan potenssiin.
+FACT			= KERTOMA			##	Palauttaa luvun kertoman.
+FACTDOUBLE		= KERTOMA.OSA			##	Palauttaa luvun osakertoman.
+FLOOR			= PYÖRISTÄ.KERR.ALAS		##	Pyöristää luvun alaspäin (nollaa kohti).
+GCD			= SUURIN.YHT.TEKIJÄ		##	Palauttaa suurimman yhteisen tekijän.
+INT			= KOKONAISLUKU			##	Pyöristää luvun alaspäin lähimpään kokonaislukuun.
+LCM			= PIENIN.YHT.JAETTAVA		##	Palauttaa pienimmän yhteisen tekijän.
+LN			= LUONNLOG			##	Palauttaa luvun luonnollisen logaritmin.
+LOG			= LOG				##	Laskee luvun logaritmin käyttämällä annettua kantalukua.
+LOG10			= LOG10				##	Palauttaa luvun kymmenkantaisen logaritmin.
+MDETERM			= MDETERM			##	Palauttaa matriisin matriisideterminantin.
+MINVERSE		= MKÄÄNTEINEN			##	Palauttaa matriisin käänteismatriisin.
+MMULT			= MKERRO			##	Palauttaa kahden matriisin tulon.
+MOD			= JAKOJ				##	Palauttaa jakolaskun jäännöksen.
+MROUND			= PYÖRISTÄ.KERR			##	Palauttaa luvun pyöristettynä annetun luvun kerrannaiseen.
+MULTINOMIAL		= MULTINOMI			##	Palauttaa lukujoukon multinomin.
+ODD			= PARITON			##	Pyöristää luvun ylöspäin lähimpään parittomaan kokonaislukuun.
+PI			= PII				##	Palauttaa piin arvon.
+POWER			= POTENSSI			##	Palauttaa luvun korotettuna haluttuun potenssiin.
+PRODUCT			= TULO				##	Kertoo annetut argumentit.
+QUOTIENT		= OSAMÄÄRÄ			##	Palauttaa osamäärän kokonaislukuosan.
+RADIANS			= RADIAANIT			##	Muuntaa asteet radiaaneiksi.
+RAND			= SATUNNAISLUKU			##	Palauttaa satunnaisluvun väliltä 0–1.
+RANDBETWEEN		= SATUNNAISLUKU.VÄLILTÄ		##	Palauttaa satunnaisluvun määritettyjen lukujen väliltä.
+ROMAN			= ROMAN				##	Muuntaa arabialaisen numeron tekstimuotoiseksi roomalaiseksi numeroksi.
+ROUND			= PYÖRISTÄ			##	Pyöristää luvun annettuun määrään desimaaleja.
+ROUNDDOWN		= PYÖRISTÄ.DES.ALAS		##	Pyöristää luvun alaspäin (nollaa kohti).
+ROUNDUP			= PYÖRISTÄ.DES.YLÖS		##	Pyöristää luvun ylöspäin (poispäin nollasta).
+SERIESSUM		= SARJA.SUMMA			##	Palauttaa kaavaan perustuvan potenssisarjan arvon.
+SIGN			= ETUMERKKI			##	Palauttaa luvun etumerkin.
+SIN			= SIN				##	Palauttaa annetun kulman sinin.
+SINH			= SINH				##	Palauttaa luvun hyperbolisen sinin.
+SQRT			= NELIÖJUURI			##	Palauttaa positiivisen neliöjuuren.
+SQRTPI			= NELIÖJUURI.PII		##	Palauttaa tulon (luku * pii) neliöjuuren.
+SUBTOTAL		= VÄLISUMMA			##	Palauttaa luettelon tai tietokannan välisumman.
+SUM			= SUMMA				##	Laskee yhteen annetut argumentit.
+SUMIF			= SUMMA.JOS			##	Laskee ehdot täyttävien solujen summan.
+SUMIFS			= SUMMA.JOS.JOUKKO		##	Laskee yhteen solualueen useita ehtoja vastaavat solut.
+SUMPRODUCT		= TULOJEN.SUMMA			##	Palauttaa matriisin toisiaan vastaavien osien tulojen summan.
+SUMSQ			= NELIÖSUMMA			##	Palauttaa argumenttien neliöiden summan.
+SUMX2MY2		= NELIÖSUMMIEN.EROTUS		##	Palauttaa kahden matriisin toisiaan vastaavien arvojen laskettujen neliösummien erotuksen.
+SUMX2PY2		= NELIÖSUMMIEN.SUMMA		##	Palauttaa kahden matriisin toisiaan vastaavien arvojen neliösummien summan.
+SUMXMY2			= EROTUSTEN.NELIÖSUMMA		##	Palauttaa kahden matriisin toisiaan vastaavien arvojen erotusten neliösumman.
+TAN			= TAN				##	Palauttaa luvun tangentin.
+TANH			= TANH				##	Palauttaa luvun hyperbolisen tangentin.
+TRUNC			= KATKAISE			##	Katkaisee luvun kokonaisluvuksi.
+
+
+##
+##	Statistical functions				Tilastolliset funktiot
+##
+AVEDEV			= KESKIPOIKKEAMA		##	Palauttaa hajontojen itseisarvojen keskiarvon.
+AVERAGE			= KESKIARVO			##	Palauttaa argumenttien keskiarvon.
+AVERAGEA		= KESKIARVOA			##	Palauttaa argumenttien, mukaan lukien lukujen, tekstin ja loogisten arvojen, keskiarvon.
+AVERAGEIF		= KESKIARVO.JOS			##	Palauttaa alueen niiden solujen keskiarvon (aritmeettisen keskiarvon), jotka täyttävät annetut ehdot.
+AVERAGEIFS		= KESKIARVO.JOS.JOUKKO		##	Palauttaa niiden solujen keskiarvon (aritmeettisen keskiarvon), jotka vastaavat useita ehtoja.
+BETADIST		= BEETAJAKAUMA			##	Palauttaa kumulatiivisen beetajakaumafunktion arvon.
+BETAINV			= BEETAJAKAUMA.KÄÄNT		##	Palauttaa määritetyn beetajakauman käänteisen kumulatiivisen jakaumafunktion arvon.
+BINOMDIST		= BINOMIJAKAUMA			##	Palauttaa yksittäisen termin binomijakaumatodennäköisyyden.
+CHIDIST			= CHIJAKAUMA			##	Palauttaa yksisuuntaisen chi-neliön jakauman todennäköisyyden.
+CHIINV			= CHIJAKAUMA.KÄÄNT		##	Palauttaa yksisuuntaisen chi-neliön jakauman todennäköisyyden käänteisarvon.
+CHITEST			= CHITESTI			##	Palauttaa riippumattomuustestin tuloksen.
+CONFIDENCE		= LUOTTAMUSVÄLI			##	Palauttaa luottamusvälin populaation keskiarvolle.
+CORREL			= KORRELAATIO			##	Palauttaa kahden arvojoukon korrelaatiokertoimen.
+COUNT			= LASKE				##	Laskee argumenttiluettelossa olevien lukujen määrän.
+COUNTA			= LASKE.A			##	Laskee argumenttiluettelossa olevien arvojen määrän.
+COUNTBLANK		= LASKE.TYHJÄT			##	Laskee alueella olevien tyhjien solujen määrän.
+COUNTIF			= LASKE.JOS			##	Laskee alueella olevien sellaisten solujen määrän, joiden sisältö vastaa annettuja ehtoja.
+COUNTIFS		= LASKE.JOS.JOUKKO		##	Laskee alueella olevien sellaisten solujen määrän, joiden sisältö vastaa useita ehtoja.
+COVAR			= KOVARIANSSI			##	Palauttaa kovarianssin, joka on keskiarvo havaintoaineiston kunkin pisteparin poikkeamien tuloista.
+CRITBINOM		= BINOMIJAKAUMA.KRIT		##	Palauttaa pienimmän arvon, jossa binomijakauman kertymäfunktion arvo on pienempi tai yhtä suuri kuin vertailuarvo.
+DEVSQ			= OIKAISTU.NELIÖSUMMA		##	Palauttaa keskipoikkeamien neliösumman.
+EXPONDIST		= EKSPONENTIAALIJAKAUMA		##	Palauttaa eksponentiaalijakauman.
+FDIST			= FJAKAUMA			##	Palauttaa F-todennäköisyysjakauman.
+FINV			= FJAKAUMA.KÄÄNT		##	Palauttaa F-todennäköisyysjakauman käänteisfunktion.
+FISHER			= FISHER			##	Palauttaa Fisher-muunnoksen.
+FISHERINV		= FISHER.KÄÄNT			##	Palauttaa käänteisen Fisher-muunnoksen.
+FORECAST		= ENNUSTE			##	Palauttaa lineaarisen trendin arvon.
+FREQUENCY		= TAAJUUS			##	Palauttaa frekvenssijakautuman pystysuuntaisena matriisina.
+FTEST			= FTESTI			##	Palauttaa F-testin tuloksen.
+GAMMADIST		= GAMMAJAKAUMA			##	Palauttaa gammajakauman.
+GAMMAINV		= GAMMAJAKAUMA.KÄÄNT		##	Palauttaa käänteisen gammajakauman kertymäfunktion.
+GAMMALN			= GAMMALN			##	Palauttaa gammafunktion luonnollisen logaritmin G(x).
+GEOMEAN			= KESKIARVO.GEOM		##	Palauttaa geometrisen keskiarvon.
+GROWTH			= KASVU				##	Palauttaa eksponentiaalisen trendin arvon.
+HARMEAN			= KESKIARVO.HARM		##	Palauttaa harmonisen keskiarvon.
+HYPGEOMDIST		= HYPERGEOM.JAKAUMA		##	Palauttaa hypergeometrisen jakauman.
+INTERCEPT		= LEIKKAUSPISTE			##	Palauttaa lineaarisen regressiosuoran leikkauspisteen.
+KURT			= KURT				##	Palauttaa tietoalueen vinous-arvon eli huipukkuuden.
+LARGE			= SUURI				##	Palauttaa tietojoukon k:nneksi suurimman arvon.
+LINEST			= LINREGR			##	Palauttaa lineaarisen trendin parametrit.
+LOGEST			= LOGREGR			##	Palauttaa eksponentiaalisen trendin parametrit.
+LOGINV			= LOGNORM.JAKAUMA.KÄÄNT		##	Palauttaa lognormeeratun jakauman käänteisfunktion.
+LOGNORMDIST		= LOGNORM.JAKAUMA		##	Palauttaa lognormaalisen jakauman kertymäfunktion.
+MAX			= MAKS				##	Palauttaa suurimman arvon argumenttiluettelosta.
+MAXA			= MAKSA				##	Palauttaa argumenttien, mukaan lukien lukujen, tekstin ja loogisten arvojen, suurimman arvon.
+MEDIAN			= MEDIAANI			##	Palauttaa annettujen lukujen mediaanin.
+MIN			= MIN				##	Palauttaa pienimmän arvon argumenttiluettelosta.
+MINA			= MINA				##	Palauttaa argumenttien, mukaan lukien lukujen, tekstin ja loogisten arvojen, pienimmän arvon.
+MODE			= MOODI				##	Palauttaa tietojoukossa useimmin esiintyvän arvon.
+NEGBINOMDIST		= BINOMIJAKAUMA.NEG		##	Palauttaa negatiivisen binomijakauman.
+NORMDIST		= NORM.JAKAUMA			##	Palauttaa normaalijakauman kertymäfunktion.
+NORMINV			= NORM.JAKAUMA.KÄÄNT		##	Palauttaa käänteisen normaalijakauman kertymäfunktion.
+NORMSDIST		= NORM.JAKAUMA.NORMIT		##	Palauttaa normitetun normaalijakauman kertymäfunktion.
+NORMSINV		= NORM.JAKAUMA.NORMIT.KÄÄNT	##	Palauttaa normitetun normaalijakauman kertymäfunktion käänteisarvon.
+PEARSON			= PEARSON			##	Palauttaa Pearsonin tulomomenttikorrelaatiokertoimen.
+PERCENTILE		= PROSENTTIPISTE		##	Palauttaa alueen arvojen k:nnen prosenttipisteen.
+PERCENTRANK		= PROSENTTIJÄRJESTYS		##	Palauttaa tietojoukon arvon prosentuaalisen järjestysluvun.
+PERMUT			= PERMUTAATIO			##	Palauttaa mahdollisten permutaatioiden määrän annetulle objektien määrälle.
+POISSON			= POISSON			##	Palauttaa Poissonin todennäköisyysjakauman.
+PROB			= TODENNÄKÖISYYS		##	Palauttaa todennäköisyyden sille, että arvot ovat tietyltä väliltä.
+QUARTILE		= NELJÄNNES			##	Palauttaa tietoalueen neljänneksen.
+RANK			= ARVON.MUKAAN			##	Palauttaa luvun paikan lukuarvoluettelossa.
+RSQ			= PEARSON.NELIÖ			##	Palauttaa Pearsonin tulomomenttikorrelaatiokertoimen neliön.
+SKEW			= JAKAUMAN.VINOUS		##	Palauttaa jakauman vinouden.
+SLOPE			= KULMAKERROIN			##	Palauttaa lineaarisen regressiosuoran kulmakertoimen.
+SMALL			= PIENI				##	Palauttaa tietojoukon k:nneksi pienimmän arvon.
+STANDARDIZE		= NORMITA			##	Palauttaa normitetun arvon.
+STDEV			= KESKIHAJONTA			##	Laskee populaation keskihajonnan otoksen perusteella.
+STDEVA			= KESKIHAJONTAA			##	Laskee populaation keskihajonnan otoksen perusteella, mukaan lukien luvut, tekstin ja loogiset arvot.
+STDEVP			= KESKIHAJONTAP			##	Laskee normaalijakautuman koko populaation perusteella.
+STDEVPA			= KESKIHAJONTAPA		##	Laskee populaation keskihajonnan koko populaation perusteella, mukaan lukien luvut, tekstin ja totuusarvot.
+STEYX			= KESKIVIRHE			##	Palauttaa regression kutakin x-arvoa vastaavan ennustetun y-arvon keskivirheen.
+TDIST			= TJAKAUMA			##	Palauttaa t-jakautuman.
+TINV			= TJAKAUMA.KÄÄNT		##	Palauttaa käänteisen t-jakauman.
+TREND			= SUUNTAUS			##	Palauttaa lineaarisen trendin arvoja.
+TRIMMEAN		= KESKIARVO.TASATTU		##	Palauttaa tietojoukon tasatun keskiarvon.
+TTEST			= TTESTI			##	Palauttaa t-testiin liittyvän todennäköisyyden.
+VAR			= VAR				##	Arvioi populaation varianssia otoksen perusteella.
+VARA			= VARA				##	Laskee populaation varianssin otoksen perusteella, mukaan lukien luvut, tekstin ja loogiset arvot.
+VARP			= VARP				##	Laskee varianssin koko populaation perusteella.
+VARPA			= VARPA				##	Laskee populaation varianssin koko populaation perusteella, mukaan lukien luvut, tekstin ja totuusarvot.
+WEIBULL			= WEIBULL			##	Palauttaa Weibullin jakauman.
+ZTEST			= ZTESTI			##	Palauttaa z-testin yksisuuntaisen todennäköisyysarvon.
+
+
+##
+##	Text functions					Tekstifunktiot
+##
+ASC			= ASC				##	Muuntaa merkkijonossa olevat englanninkieliset DBCS- tai katakana-merkit SBCS-merkeiksi.
+BAHTTEXT		= BAHTTEKSTI			##	Muuntaa luvun tekstiksi ß (baht) -valuuttamuotoa käyttämällä.
+CHAR			= MERKKI			##	Palauttaa koodin lukua vastaavan merkin.
+CLEAN			= SIIVOA			##	Poistaa tekstistä kaikki tulostumattomat merkit.
+CODE			= KOODI				##	Palauttaa tekstimerkkijonon ensimmäisen merkin numerokoodin.
+CONCATENATE		= KETJUTA			##	Yhdistää useat merkkijonot yhdeksi merkkijonoksi.
+DOLLAR			= VALUUTTA			##	Muuntaa luvun tekstiksi $ (dollari) -valuuttamuotoa käyttämällä.
+EXACT			= VERTAA			##	Tarkistaa, ovatko kaksi tekstiarvoa samanlaiset.
+FIND			= ETSI				##	Etsii tekstiarvon toisen tekstin sisältä (tunnistaa isot ja pienet kirjaimet).
+FINDB			= ETSIB				##	Etsii tekstiarvon toisen tekstin sisältä (tunnistaa isot ja pienet kirjaimet).
+FIXED			= KIINTEÄ			##	Muotoilee luvun tekstiksi, jossa on kiinteä määrä desimaaleja.
+JIS			= JIS				##	Muuntaa merkkijonossa olevat englanninkieliset SBCS- tai katakana-merkit DBCS-merkeiksi.
+LEFT			= VASEN				##	Palauttaa tekstiarvon vasemmanpuoliset merkit.
+LEFTB			= VASENB			##	Palauttaa tekstiarvon vasemmanpuoliset merkit.
+LEN			= PITUUS			##	Palauttaa tekstimerkkijonon merkkien määrän.
+LENB			= PITUUSB			##	Palauttaa tekstimerkkijonon merkkien määrän.
+LOWER			= PIENET			##	Muuntaa tekstin pieniksi kirjaimiksi.
+MID			= POIMI.TEKSTI			##	Palauttaa määritetyn määrän merkkejä merkkijonosta alkaen annetusta kohdasta.
+MIDB			= POIMI.TEKSTIB			##	Palauttaa määritetyn määrän merkkejä merkkijonosta alkaen annetusta kohdasta.
+PHONETIC		= FONEETTINEN			##	Hakee foneettiset (furigana) merkit merkkijonosta.
+PROPER			= ERISNIMI			##	Muuttaa merkkijonon kunkin sanan ensimmäisen kirjaimen isoksi.
+REPLACE			= KORVAA			##	Korvaa tekstissä olevat merkit.
+REPLACEB		= KORVAAB			##	Korvaa tekstissä olevat merkit.
+REPT			= TOISTA			##	Toistaa tekstin annetun määrän kertoja.
+RIGHT			= OIKEA				##	Palauttaa tekstiarvon oikeanpuoliset merkit.
+RIGHTB			= OIKEAB			##	Palauttaa tekstiarvon oikeanpuoliset merkit.
+SEARCH			= KÄY.LÄPI			##	Etsii tekstiarvon toisen tekstin sisältä (isot ja pienet kirjaimet tulkitaan samoiksi merkeiksi).
+SEARCHB			= KÄY.LÄPIB			##	Etsii tekstiarvon toisen tekstin sisältä (isot ja pienet kirjaimet tulkitaan samoiksi merkeiksi).
+SUBSTITUTE		= VAIHDA			##	Korvaa merkkijonossa olevan tekstin toisella.
+T			= T				##	Muuntaa argumentit tekstiksi.
+TEXT			= TEKSTI			##	Muotoilee luvun ja muuntaa sen tekstiksi.
+TRIM			= POISTA.VÄLIT			##	Poistaa välilyönnit tekstistä.
+UPPER			= ISOT				##	Muuntaa tekstin isoiksi kirjaimiksi.
+VALUE			= ARVO				##	Muuntaa tekstiargumentin luvuksi.
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fr/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fr/config
new file mode 100755
index 0000000..8189598
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fr/config
@@ -0,0 +1,24 @@
+##
+## PhpSpreadsheet
+##
+
+ArgumentSeparator	= ;
+
+
+##
+##	(For future use)
+##
+currencySymbol	= €
+
+
+##
+##	Excel Error Codes	(For future use)
+
+##
+NULL	= #NUL!
+DIV0	= #DIV/0!
+VALUE	= #VALEUR!
+REF	= #REF!
+NAME	= #NOM?
+NUM	= #NOMBRE!
+NA	= #N/A
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fr/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fr/functions
new file mode 100755
index 0000000..7f40d5f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fr/functions
@@ -0,0 +1,416 @@
+##
+## PhpSpreadsheet
+##
+## Data in this file derived from https://www.excel-function-translation.com/
+##
+##
+
+
+##
+##	Add-in and Automation functions			Fonctions de complément et d’automatisation
+##
+GETPIVOTDATA		= LIREDONNEESTABCROISDYNAMIQUE	##	Renvoie les données stockées dans un rapport de tableau croisé dynamique.
+
+
+##
+##	Cube functions					Fonctions Cube
+##
+CUBEKPIMEMBER		= MEMBREKPICUBE			##	Renvoie un nom, une propriété et une mesure d’indicateur de performance clé et affiche le nom et la propriété dans la cellule. Un indicateur de performance clé est une mesure quantifiable, telle que la marge bénéficiaire brute mensuelle ou la rotation trimestrielle du personnel, utilisée pour évaluer les performances d’une entreprise.
+CUBEMEMBER		= MEMBRECUBE			##	Renvoie un membre ou un uplet dans une hiérarchie de cubes. Utilisez cette fonction pour valider l’existence du membre ou de l’uplet dans le cube.
+CUBEMEMBERPROPERTY	= PROPRIETEMEMBRECUBE		##	Renvoie la valeur d’une propriété de membre du cube. Utilisez cette fonction pour valider l’existence d’un nom de membre dans le cube et pour renvoyer la propriété spécifiée pour ce membre.
+CUBERANKEDMEMBER	= RANGMEMBRECUBE		##	Renvoie le nième membre ou le membre placé à un certain rang dans un ensemble. Utilisez cette fonction pour renvoyer un ou plusieurs éléments d’un ensemble, tels que les meilleurs vendeurs ou les 10 meilleurs étudiants.
+CUBESET			= JEUCUBE			##	Définit un ensemble calculé de membres ou d’uplets en envoyant une expression définie au cube sur le serveur qui crée l’ensemble et le renvoie à Microsoft Office Excel.
+CUBESETCOUNT		= NBJEUCUBE			##	Renvoie le nombre d’éléments dans un jeu.
+CUBEVALUE		= VALEURCUBE			##	Renvoie une valeur d’agrégation issue d’un cube.
+
+
+##
+##	Database functions				Fonctions de base de données
+##
+DAVERAGE		= BDMOYENNE			##	Renvoie la moyenne des entrées de base de données sélectionnées.
+DCOUNT			= BCOMPTE			##	Compte le nombre de cellules d’une base de données qui contiennent des nombres.
+DCOUNTA			= BDNBVAL			##	Compte les cellules non vides d’une base de données.
+DGET			= BDLIRE			##	Extrait d’une base de données un enregistrement unique répondant aux critères spécifiés.
+DMAX			= BDMAX				##	Renvoie la valeur maximale des entrées de base de données sélectionnées.
+DMIN			= BDMIN				##	Renvoie la valeur minimale des entrées de base de données sélectionnées.
+DPRODUCT		= BDPRODUIT			##	Multiplie les valeurs d’un champ particulier des enregistrements d’une base de données, qui répondent aux critères spécifiés.
+DSTDEV			= BDECARTYPE			##	Calcule l’écart type pour un échantillon d’entrées de base de données sélectionnées.
+DSTDEVP			= BDECARTYPEP			##	Calcule l’écart type pour l’ensemble d’une population d’entrées de base de données sélectionnées.
+DSUM			= BDSOMME			##	Ajoute les nombres dans la colonne de champ des enregistrements de la base de données, qui répondent aux critères.
+DVAR			= BDVAR				##	Calcule la variance pour un échantillon d’entrées de base de données sélectionnées.
+DVARP			= BDVARP			##	Calcule la variance pour l’ensemble d’une population d’entrées de base de données sélectionnées.
+
+
+##
+##	Date and time functions				Fonctions de date et d’heure
+##
+DATE			= DATE				##	Renvoie le numéro de série d’une date précise.
+DATEVALUE		= DATEVAL			##	Convertit une date représentée sous forme de texte en numéro de série.
+DAY			= JOUR				##	Convertit un numéro de série en jour du mois.
+DAYS360			= JOURS360			##	Calcule le nombre de jours qui séparent deux dates sur la base d’une année de 360 jours.
+EDATE			= MOIS.DECALER			##	Renvoie le numéro séquentiel de la date qui représente une date spécifiée (l’argument date_départ), corrigée en plus ou en moins du nombre de mois indiqué.
+EOMONTH			= FIN.MOIS			##	Renvoie le numéro séquentiel de la date du dernier jour du mois précédant ou suivant la date_départ du nombre de mois indiqué.
+HOUR			= HEURE				##	Convertit un numéro de série en heure.
+MINUTE			= MINUTE			##	Convertit un numéro de série en minute.
+MONTH			= MOIS				##	Convertit un numéro de série en mois.
+NETWORKDAYS		= NB.JOURS.OUVRES		##	Renvoie le nombre de jours ouvrés entiers compris entre deux dates.
+NOW			= MAINTENANT			##	Renvoie le numéro de série de la date et de l’heure du jour.
+SECOND			= SECONDE			##	Convertit un numéro de série en seconde.
+TIME			= TEMPS				##	Renvoie le numéro de série d’une heure précise.
+TIMEVALUE		= TEMPSVAL			##	Convertit une date représentée sous forme de texte en numéro de série.
+TODAY			= AUJOURDHUI			##	Renvoie le numéro de série de la date du jour.
+WEEKDAY			= JOURSEM			##	Convertit un numéro de série en jour de la semaine.
+WEEKNUM			= NO.SEMAINE			##	Convertit un numéro de série en un numéro représentant l’ordre de la semaine dans l’année.
+WORKDAY			= SERIE.JOUR.OUVRE		##	Renvoie le numéro de série de la date avant ou après le nombre de jours ouvrés spécifiés.
+YEAR			= ANNEE				##	Convertit un numéro de série en année.
+YEARFRAC		= FRACTION.ANNEE		##	Renvoie la fraction de l’année représentant le nombre de jours entre la date de début et la date de fin.
+
+
+##
+##	Engineering functions				Fonctions d’ingénierie
+##
+BESSELI			= BESSELI			##	Renvoie la fonction Bessel modifiée In(x).
+BESSELJ			= BESSELJ			##	Renvoie la fonction Bessel Jn(x).
+BESSELK			= BESSELK			##	Renvoie la fonction Bessel modifiée Kn(x).
+BESSELY			= BESSELY			##	Renvoie la fonction Bessel Yn(x).
+BIN2DEC			= BINDEC			##	Convertit un nombre binaire en nombre décimal.
+BIN2HEX			= BINHEX			##	Convertit un nombre binaire en nombre hexadécimal.
+BIN2OCT			= BINOCT			##	Convertit un nombre binaire en nombre octal.
+COMPLEX			= COMPLEXE			##	Convertit des coefficients réel et imaginaire en un nombre complexe.
+CONVERT			= CONVERT			##	Convertit un nombre d’une unité de mesure à une autre.
+DEC2BIN			= DECBIN			##	Convertit un nombre décimal en nombre binaire.
+DEC2HEX			= DECHEX			##	Convertit un nombre décimal en nombre hexadécimal.
+DEC2OCT			= DECOCT			##	Convertit un nombre décimal en nombre octal.
+DELTA			= DELTA				##	Teste l’égalité de deux nombres.
+ERF			= ERF				##	Renvoie la valeur de la fonction d’erreur.
+ERFC			= ERFC				##	Renvoie la valeur de la fonction d’erreur complémentaire.
+GESTEP			= SUP.SEUIL			##	Teste si un nombre est supérieur à une valeur de seuil.
+HEX2BIN			= HEXBIN			##	Convertit un nombre hexadécimal en nombre binaire.
+HEX2DEC			= HEXDEC			##	Convertit un nombre hexadécimal en nombre décimal.
+HEX2OCT			= HEXOCT			##	Convertit un nombre hexadécimal en nombre octal.
+IMABS			= COMPLEXE.MODULE		##	Renvoie la valeur absolue (module) d’un nombre complexe.
+IMAGINARY		= COMPLEXE.IMAGINAIRE		##	Renvoie le coefficient imaginaire d’un nombre complexe.
+IMARGUMENT		= COMPLEXE.ARGUMENT		##	Renvoie l’argument thêta, un angle exprimé en radians.
+IMCONJUGATE		= COMPLEXE.CONJUGUE		##	Renvoie le nombre complexe conjugué d’un nombre complexe.
+IMCOS			= IMCOS				##	Renvoie le cosinus d’un nombre complexe.
+IMDIV			= COMPLEXE.DIV			##	Renvoie le quotient de deux nombres complexes.
+IMEXP			= COMPLEXE.EXP			##	Renvoie la fonction exponentielle d’un nombre complexe.
+IMLN			= COMPLEXE.LN			##	Renvoie le logarithme népérien d’un nombre complexe.
+IMLOG10			= COMPLEXE.LOG10		##	Calcule le logarithme en base 10 d’un nombre complexe.
+IMLOG2			= COMPLEXE.LOG2			##	Calcule le logarithme en base 2 d’un nombre complexe.
+IMPOWER			= COMPLEXE.PUISSANCE		##	Renvoie un nombre complexe élevé à une puissance entière.
+IMPRODUCT		= COMPLEXE.PRODUIT		##	Renvoie le produit de plusieurs nombres complexes.
+IMREAL			= COMPLEXE.REEL			##	Renvoie le coefficient réel d’un nombre complexe.
+IMSIN			= COMPLEXE.SIN			##	Renvoie le sinus d’un nombre complexe.
+IMSQRT			= COMPLEXE.RACINE		##	Renvoie la racine carrée d’un nombre complexe.
+IMSUB			= COMPLEXE.DIFFERENCE		##	Renvoie la différence entre deux nombres complexes.
+IMSUM			= COMPLEXE.SOMME		##	Renvoie la somme de plusieurs nombres complexes.
+OCT2BIN			= OCTBIN			##	Convertit un nombre octal en nombre binaire.
+OCT2DEC			= OCTDEC			##	Convertit un nombre octal en nombre décimal.
+OCT2HEX			= OCTHEX			##	Convertit un nombre octal en nombre hexadécimal.
+
+
+##
+##	Financial functions				Fonctions financières
+##
+ACCRINT			= INTERET.ACC			##	Renvoie l’intérêt couru non échu d’un titre dont l’intérêt est perçu périodiquement.
+ACCRINTM		= INTERET.ACC.MAT		##	Renvoie l’intérêt couru non échu d’un titre dont l’intérêt est perçu à l’échéance.
+AMORDEGRC		= AMORDEGRC			##	Renvoie l’amortissement correspondant à chaque période comptable en utilisant un coefficient d’amortissement.
+AMORLINC		= AMORLINC			##	Renvoie l’amortissement d’un bien à la fin d’une période fiscale donnée.
+COUPDAYBS		= NB.JOURS.COUPON.PREC		##	Renvoie le nombre de jours entre le début de la période de coupon et la date de liquidation.
+COUPDAYS		= NB.JOURS.COUPONS		##	Renvoie le nombre de jours pour la période du coupon contenant la date de liquidation.
+COUPDAYSNC		= NB.JOURS.COUPON.SUIV		##	Renvoie le nombre de jours entre la date de liquidation et la date du coupon suivant la date de liquidation.
+COUPNCD			= DATE.COUPON.SUIV		##	Renvoie la première date de coupon ultérieure à la date de règlement.
+COUPNUM			= NB.COUPONS			##	Renvoie le nombre de coupons dus entre la date de règlement et la date d’échéance.
+COUPPCD			= DATE.COUPON.PREC		##	Renvoie la date de coupon précédant la date de règlement.
+CUMIPMT			= CUMUL.INTER			##	Renvoie l’intérêt cumulé payé sur un emprunt entre deux périodes.
+CUMPRINC		= CUMUL.PRINCPER		##	Renvoie le montant cumulé des remboursements du capital d’un emprunt effectués entre deux périodes.
+DB			= DB				##	Renvoie l’amortissement d’un bien pour une période spécifiée en utilisant la méthode de l’amortissement dégressif à taux fixe.
+DDB			= DDB				##	Renvoie l’amortissement d’un bien pour toute période spécifiée, en utilisant la méthode de l’amortissement dégressif à taux double ou selon un coefficient à spécifier.
+DISC			= TAUX.ESCOMPTE			##	Calcule le taux d’escompte d’une transaction.
+DOLLARDE		= PRIX.DEC			##	Convertit un prix en euros, exprimé sous forme de fraction, en un prix en euros exprimé sous forme de nombre décimal.
+DOLLARFR		= PRIX.FRAC			##	Convertit un prix en euros, exprimé sous forme de nombre décimal, en un prix en euros exprimé sous forme de fraction.
+DURATION		= DUREE				##	Renvoie la durée, en années, d’un titre dont l’intérêt est perçu périodiquement.
+EFFECT			= TAUX.EFFECTIF			##	Renvoie le taux d’intérêt annuel effectif.
+FV			= VC				##	Renvoie la valeur future d’un investissement.
+FVSCHEDULE		= VC.PAIEMENTS			##	Calcule la valeur future d’un investissement en appliquant une série de taux d’intérêt composites.
+INTRATE			= TAUX.INTERET			##	Affiche le taux d’intérêt d’un titre totalement investi.
+IPMT			= INTPER			##	Calcule le montant des intérêts d’un investissement pour une période donnée.
+IRR			= TRI				##	Calcule le taux de rentabilité interne d’un investissement pour une succession de trésoreries.
+ISPMT			= ISPMT				##	Calcule le montant des intérêts d’un investissement pour une période donnée.
+MDURATION		= DUREE.MODIFIEE		##	Renvoie la durée de Macauley modifiée pour un titre ayant une valeur nominale hypothétique de 100_euros.
+MIRR			= TRIM				##	Calcule le taux de rentabilité interne lorsque les paiements positifs et négatifs sont financés à des taux différents.
+NOMINAL			= TAUX.NOMINAL			##	Calcule le taux d’intérêt nominal annuel.
+NPER			= NPM				##	Renvoie le nombre de versements nécessaires pour rembourser un emprunt.
+NPV			= VAN				##	Calcule la valeur actuelle nette d’un investissement basé sur une série de décaissements et un taux d’escompte.
+ODDFPRICE		= PRIX.PCOUPON.IRREG		##	Renvoie le prix par tranche de valeur nominale de 100 euros d’un titre dont la première période de coupon est irrégulière.
+ODDFYIELD		= REND.PCOUPON.IRREG		##	Renvoie le taux de rendement d’un titre dont la première période de coupon est irrégulière.
+ODDLPRICE		= PRIX.DCOUPON.IRREG		##	Renvoie le prix par tranche de valeur nominale de 100 euros d’un titre dont la première période de coupon est irrégulière.
+ODDLYIELD		= REND.DCOUPON.IRREG		##	Renvoie le taux de rendement d’un titre dont la dernière période de coupon est irrégulière.
+PMT			= VPM				##	Calcule le paiement périodique d’un investissement donné.
+PPMT			= PRINCPER			##	Calcule, pour une période donnée, la part de remboursement du principal d’un investissement.
+PRICE			= PRIX.TITRE			##	Renvoie le prix d’un titre rapportant des intérêts périodiques, pour une valeur nominale de 100 euros.
+PRICEDISC		= VALEUR.ENCAISSEMENT		##	Renvoie la valeur d’encaissement d’un escompte commercial, pour une valeur nominale de 100 euros.
+PRICEMAT		= PRIX.TITRE.ECHEANCE		##	Renvoie le prix d’un titre dont la valeur nominale est 100 euros et qui rapporte des intérêts à l’échéance.
+PV			= PV				##	Calcule la valeur actuelle d’un investissement.
+RATE			= TAUX				##	Calcule le taux d’intérêt par période pour une annuité.
+RECEIVED		= VALEUR.NOMINALE		##	Renvoie la valeur nominale à échéance d’un effet de commerce.
+SLN			= AMORLIN			##	Calcule l’amortissement linéaire d’un bien pour une période donnée.
+SYD			= SYD				##	Calcule l’amortissement d’un bien pour une période donnée sur la base de la méthode américaine Sum-of-Years Digits (amortissement dégressif à taux décroissant appliqué à une valeur constante).
+TBILLEQ			= TAUX.ESCOMPTE.R		##	Renvoie le taux d’escompte rationnel d’un bon du Trésor.
+TBILLPRICE		= PRIX.BON.TRESOR		##	Renvoie le prix d’un bon du Trésor d’une valeur nominale de 100 euros.
+TBILLYIELD		= RENDEMENT.BON.TRESOR		##	Calcule le taux de rendement d’un bon du Trésor.
+VDB			= VDB				##	Renvoie l’amortissement d’un bien pour une période spécifiée ou partielle en utilisant une méthode de l’amortissement dégressif à taux fixe.
+XIRR			= TRI.PAIEMENTS			##	Calcule le taux de rentabilité interne d’un ensemble de paiements non périodiques.
+XNPV			= VAN.PAIEMENTS			##	Renvoie la valeur actuelle nette d’un ensemble de paiements non périodiques.
+YIELD			= RENDEMENT.TITRE		##	Calcule le rendement d’un titre rapportant des intérêts périodiquement.
+YIELDDISC		= RENDEMENT.SIMPLE		##	Calcule le taux de rendement d’un emprunt à intérêt simple (par exemple, un bon du Trésor).
+YIELDMAT		= RENDEMENT.TITRE.ECHEANCE	##	Renvoie le rendement annuel d’un titre qui rapporte des intérêts à l’échéance.
+
+
+##
+##	Information functions				Fonctions d’information
+##
+CELL			= CELLULE			##	Renvoie des informations sur la mise en forme, l’emplacement et le contenu d’une cellule.
+ERROR.TYPE		= TYPE.ERREUR			##	Renvoie un nombre correspondant à un type d’erreur.
+INFO			= INFORMATIONS			##	Renvoie des informations sur l’environnement d’exploitation actuel.
+ISBLANK			= ESTVIDE			##	Renvoie VRAI si l’argument valeur est vide.
+ISERR			= ESTERR			##	Renvoie VRAI si l’argument valeur fait référence à une valeur d’erreur, sauf #N/A.
+ISERROR			= ESTERREUR			##	Renvoie VRAI si l’argument valeur fait référence à une valeur d’erreur.
+ISEVEN			= EST.PAIR			##	Renvoie VRAI si le chiffre est pair.
+ISLOGICAL		= ESTLOGIQUE			##	Renvoie VRAI si l’argument valeur fait référence à une valeur logique.
+ISNA			= ESTNA				##	Renvoie VRAI si l’argument valeur fait référence à la valeur d’erreur #N/A.
+ISNONTEXT		= ESTNONTEXTE			##	Renvoie VRAI si l’argument valeur ne se présente pas sous forme de texte.
+ISNUMBER		= ESTNUM			##	Renvoie VRAI si l’argument valeur représente un nombre.
+ISODD			= EST.IMPAIR			##	Renvoie VRAI si le chiffre est impair.
+ISREF			= ESTREF			##	Renvoie VRAI si l’argument valeur est une référence.
+ISTEXT			= ESTTEXTE			##	Renvoie VRAI si l’argument valeur se présente sous forme de texte.
+N			= N				##	Renvoie une valeur convertie en nombre.
+NA			= NA				##	Renvoie la valeur d’erreur #N/A.
+TYPE			= TYPE				##	Renvoie un nombre indiquant le type de données d’une valeur.
+
+
+##
+##	Logical functions				Fonctions logiques
+##
+AND			= ET				##	Renvoie VRAI si tous ses arguments sont VRAI.
+FALSE			= FAUX				##	Renvoie la valeur logique FAUX.
+IF			= SI				##	Spécifie un test logique à effectuer.
+IFERROR			= SIERREUR			##	Renvoie une valeur que vous spécifiez si une formule génère une erreur ; sinon, elle renvoie le résultat de la formule.
+NOT			= NON				##	Inverse la logique de cet argument.
+OR			= OU				##	Renvoie VRAI si un des arguments est VRAI.
+TRUE			= VRAI				##	Renvoie la valeur logique VRAI.
+
+
+##
+##	Lookup and reference functions			Fonctions de recherche et de référence
+##
+ADDRESS			= ADRESSE			##	Renvoie une référence sous forme de texte à une seule cellule d’une feuille de calcul.
+AREAS			= ZONES				##	Renvoie le nombre de zones dans une référence.
+CHOOSE			= CHOISIR			##	Choisit une valeur dans une liste.
+COLUMN			= COLONNE			##	Renvoie le numéro de colonne d’une référence.
+COLUMNS			= COLONNES			##	Renvoie le nombre de colonnes dans une référence.
+HLOOKUP			= RECHERCHEH			##	Effectue une recherche dans la première ligne d’une matrice et renvoie la valeur de la cellule indiquée.
+HYPERLINK		= LIEN_HYPERTEXTE		##	Crée un raccourci ou un renvoi qui ouvre un document stocké sur un serveur réseau, sur un réseau Intranet ou sur Internet.
+INDEX			= INDEX				##	Utilise un index pour choisir une valeur provenant d’une référence ou d’une matrice.
+INDIRECT		= INDIRECT			##	Renvoie une référence indiquée par une valeur de texte.
+LOOKUP			= RECHERCHE			##	Recherche des valeurs dans un vecteur ou une matrice.
+MATCH			= EQUIV				##	Recherche des valeurs dans une référence ou une matrice.
+OFFSET			= DECALER			##	Renvoie une référence décalée par rapport à une référence donnée.
+ROW			= LIGNE				##	Renvoie le numéro de ligne d’une référence.
+ROWS			= LIGNES			##	Renvoie le nombre de lignes dans une référence.
+RTD			= RTD				##	Extrait les données en temps réel à partir d’un programme prenant en charge l’automation COM (Automation : utilisation des objets d'une application à partir d'une autre application ou d'un autre outil de développement. Autrefois appelée OLE Automation, Automation est une norme industrielle et une fonctionnalité du modèle d'objet COM (Component Object Model).).
+TRANSPOSE		= TRANSPOSE			##	Renvoie la transposition d’une matrice.
+VLOOKUP			= RECHERCHEV			##	Effectue une recherche dans la première colonne d’une matrice et se déplace sur la ligne pour renvoyer la valeur d’une cellule.
+
+
+##
+##	Math and trigonometry functions			Fonctions mathématiques et trigonométriques
+##
+ABS			= ABS				##	Renvoie la valeur absolue d’un nombre.
+ACOS			= ACOS				##	Renvoie l’arccosinus d’un nombre.
+ACOSH			= ACOSH				##	Renvoie le cosinus hyperbolique inverse d’un nombre.
+ASIN			= ASIN				##	Renvoie l’arcsinus d’un nombre.
+ASINH			= ASINH				##	Renvoie le sinus hyperbolique inverse d’un nombre.
+ATAN			= ATAN				##	Renvoie l’arctangente d’un nombre.
+ATAN2			= ATAN2				##	Renvoie l’arctangente des coordonnées x et y.
+ATANH			= ATANH				##	Renvoie la tangente hyperbolique inverse d’un nombre.
+CEILING			= PLAFOND			##	Arrondit un nombre au nombre entier le plus proche ou au multiple le plus proche de l’argument précision en s’éloignant de zéro.
+COMBIN			= COMBIN			##	Renvoie le nombre de combinaisons que l’on peut former avec un nombre donné d’objets.
+COS			= COS				##	Renvoie le cosinus d’un nombre.
+COSH			= COSH				##	Renvoie le cosinus hyperbolique d’un nombre.
+DEGREES			= DEGRES			##	Convertit des radians en degrés.
+EVEN			= PAIR				##	Arrondit un nombre au nombre entier pair le plus proche en s’éloignant de zéro.
+EXP			= EXP				##	Renvoie e élevé à la puissance d’un nombre donné.
+FACT			= FACT				##	Renvoie la factorielle d’un nombre.
+FACTDOUBLE		= FACTDOUBLE			##	Renvoie la factorielle double d’un nombre.
+FLOOR			= PLANCHER			##	Arrondit un nombre en tendant vers 0 (zéro).
+GCD			= PGCD				##	Renvoie le plus grand commun diviseur.
+INT			= ENT				##	Arrondit un nombre à l’entier immédiatement inférieur.
+LCM			= PPCM				##	Renvoie le plus petit commun multiple.
+LN			= LN				##	Renvoie le logarithme népérien d’un nombre.
+LOG			= LOG				##	Renvoie le logarithme d’un nombre dans la base spécifiée.
+LOG10			= LOG10				##	Calcule le logarithme en base 10 d’un nombre.
+MDETERM			= DETERMAT			##	Renvoie le déterminant d’une matrice.
+MINVERSE		= INVERSEMAT			##	Renvoie la matrice inverse d’une matrice.
+MMULT			= PRODUITMAT			##	Renvoie le produit de deux matrices.
+MOD			= MOD				##	Renvoie le reste d’une division.
+MROUND			= ARRONDI.AU.MULTIPLE		##	Donne l’arrondi d’un nombre au multiple spécifié.
+MULTINOMIAL		= MULTINOMIALE			##	Calcule la multinomiale d’un ensemble de nombres.
+ODD			= IMPAIR			##	Renvoie le nombre, arrondi à la valeur du nombre entier impair le plus proche en s’éloignant de zéro.
+PI			= PI				##	Renvoie la valeur de pi.
+POWER			= PUISSANCE			##	Renvoie la valeur du nombre élevé à une puissance.
+PRODUCT			= PRODUIT			##	Multiplie ses arguments.
+QUOTIENT		= QUOTIENT			##	Renvoie la partie entière du résultat d’une division.
+RADIANS			= RADIANS			##	Convertit des degrés en radians.
+RAND			= ALEA				##	Renvoie un nombre aléatoire compris entre 0 et 1.
+RANDBETWEEN		= ALEA.ENTRE.BORNES		##	Renvoie un nombre aléatoire entre les nombres que vous spécifiez.
+ROMAN			= ROMAIN			##	Convertit des chiffres arabes en chiffres romains, sous forme de texte.
+ROUND			= ARRONDI			##	Arrondit un nombre au nombre de chiffres indiqué.
+ROUNDDOWN		= ARRONDI.INF			##	Arrondit un nombre en tendant vers 0 (zéro).
+ROUNDUP			= ARRONDI.SUP			##	Arrondit un nombre à l’entier supérieur, en s’éloignant de zéro.
+SERIESSUM		= SOMME.SERIES			##	Renvoie la somme d’une série géométrique en s’appuyant sur la formule suivante :
+SIGN			= SIGNE				##	Renvoie le signe d’un nombre.
+SIN			= SIN				##	Renvoie le sinus d’un angle donné.
+SINH			= SINH				##	Renvoie le sinus hyperbolique d’un nombre.
+SQRT			= RACINE			##	Renvoie la racine carrée d’un nombre.
+SQRTPI			= RACINE.PI			##	Renvoie la racine carrée de (nombre * pi).
+SUBTOTAL		= SOUS.TOTAL			##	Renvoie un sous-total dans une liste ou une base de données.
+SUM			= SOMME				##	Calcule la somme de ses arguments.
+SUMIF			= SOMME.SI			##	Additionne les cellules spécifiées si elles répondent à un critère donné.
+SUMIFS			= SOMME.SI.ENS			##	Ajoute les cellules d’une plage qui répondent à plusieurs critères.
+SUMPRODUCT		= SOMMEPROD			##	Multiplie les valeurs correspondantes des matrices spécifiées et calcule la somme de ces produits.
+SUMSQ			= SOMME.CARRES			##	Renvoie la somme des carrés des arguments.
+SUMX2MY2		= SOMME.X2MY2			##	Renvoie la somme de la différence des carrés des valeurs correspondantes de deux matrices.
+SUMX2PY2		= SOMME.X2PY2			##	Renvoie la somme de la somme des carrés des valeurs correspondantes de deux matrices.
+SUMXMY2			= SOMME.XMY2			##	Renvoie la somme des carrés des différences entre les valeurs correspondantes de deux matrices.
+TAN			= TAN				##	Renvoie la tangente d’un nombre.
+TANH			= TANH				##	Renvoie la tangente hyperbolique d’un nombre.
+TRUNC			= TRONQUE			##	Renvoie la partie entière d’un nombre.
+
+
+##
+##	Statistical functions				Fonctions statistiques
+##
+AVEDEV			= ECART.MOYEN			##	Renvoie la moyenne des écarts absolus observés dans la moyenne des points de données.
+AVERAGE			= MOYENNE			##	Renvoie la moyenne de ses arguments.
+AVERAGEA		= AVERAGEA			##	Renvoie la moyenne de ses arguments, nombres, texte et valeurs logiques inclus.
+AVERAGEIF		= MOYENNE.SI			##	Renvoie la moyenne (arithmétique) de toutes les cellules d’une plage qui répondent à des critères donnés.
+AVERAGEIFS		= MOYENNE.SI.ENS		##	Renvoie la moyenne (arithmétique) de toutes les cellules qui répondent à plusieurs critères.
+BETADIST		= LOI.BETA			##	Renvoie la fonction de distribution cumulée.
+BETAINV			= BETA.INVERSE			##	Renvoie l’inverse de la fonction de distribution cumulée pour une distribution bêta spécifiée.
+BINOMDIST		= LOI.BINOMIALE			##	Renvoie la probabilité d’une variable aléatoire discrète suivant la loi binomiale.
+CHIDIST			= LOI.KHIDEUX			##	Renvoie la probabilité unilatérale de la distribution khi-deux.
+CHIINV			= KHIDEUX.INVERSE		##	Renvoie l’inverse de la probabilité unilatérale de la distribution khi-deux.
+CHITEST			= TEST.KHIDEUX			##	Renvoie le test d’indépendance.
+CONFIDENCE		= INTERVALLE.CONFIANCE		##	Renvoie l’intervalle de confiance pour une moyenne de population.
+CORREL			= COEFFICIENT.CORRELATION	##	Renvoie le coefficient de corrélation entre deux séries de données.
+COUNT			= NB				##	Détermine les nombres compris dans la liste des arguments.
+COUNTA			= NBVAL				##	Détermine le nombre de valeurs comprises dans la liste des arguments.
+COUNTBLANK		= NB.VIDE			##	Compte le nombre de cellules vides dans une plage.
+COUNTIF			= NB.SI				##	Compte le nombre de cellules qui répondent à un critère donné dans une plage.
+COUNTIFS		= NB.SI.ENS			##	Compte le nombre de cellules à l’intérieur d’une plage qui répondent à plusieurs critères.
+COVAR			= COVARIANCE			##	Renvoie la covariance, moyenne des produits des écarts pour chaque série d’observations.
+CRITBINOM		= CRITERE.LOI.BINOMIALE		##	Renvoie la plus petite valeur pour laquelle la distribution binomiale cumulée est inférieure ou égale à une valeur de critère.
+DEVSQ			= SOMME.CARRES.ECARTS		##	Renvoie la somme des carrés des écarts.
+EXPONDIST		= LOI.EXPONENTIELLE		##	Renvoie la distribution exponentielle.
+FDIST			= LOI.F				##	Renvoie la distribution de probabilité F.
+FINV			= INVERSE.LOI.F			##	Renvoie l’inverse de la distribution de probabilité F.
+FISHER			= FISHER			##	Renvoie la transformation de Fisher.
+FISHERINV		= FISHER.INVERSE		##	Renvoie l’inverse de la transformation de Fisher.
+FORECAST		= PREVISION			##	Calcule une valeur par rapport à une tendance linéaire.
+FREQUENCY		= FREQUENCE			##	Calcule la fréquence d’apparition des valeurs dans une plage de valeurs, puis renvoie des nombres sous forme de matrice verticale.
+FTEST			= TEST.F			##	Renvoie le résultat d’un test F.
+GAMMADIST		= LOI.GAMMA			##	Renvoie la probabilité d’une variable aléatoire suivant une loi Gamma.
+GAMMAINV		= LOI.GAMMA.INVERSE		##	Renvoie, pour une probabilité donnée, la valeur d’une variable aléatoire suivant une loi Gamma.
+GAMMALN			= LNGAMMA			##	Renvoie le logarithme népérien de la fonction Gamma, G(x)
+GEOMEAN			= MOYENNE.GEOMETRIQUE		##	Renvoie la moyenne géométrique.
+GROWTH			= CROISSANCE			##	Calcule des valeurs par rapport à une tendance exponentielle.
+HARMEAN			= MOYENNE.HARMONIQUE		##	Renvoie la moyenne harmonique.
+HYPGEOMDIST		= LOI.HYPERGEOMETRIQUE		##	Renvoie la probabilité d’une variable aléatoire discrète suivant une loi hypergéométrique.
+INTERCEPT		= ORDONNEE.ORIGINE		##	Renvoie l’ordonnée à l’origine d’une droite de régression linéaire.
+KURT			= KURTOSIS			##	Renvoie le kurtosis d’une série de données.
+LARGE			= GRANDE.VALEUR			##	Renvoie la k-ième plus grande valeur d’une série de données.
+LINEST			= DROITEREG			##	Renvoie les paramètres d’une tendance linéaire.
+LOGEST			= LOGREG			##	Renvoie les paramètres d’une tendance exponentielle.
+LOGINV			= LOI.LOGNORMALE.INVERSE	##	Renvoie l’inverse de la probabilité pour une variable aléatoire suivant la loi lognormale.
+LOGNORMDIST		= LOI.LOGNORMALE		##	Renvoie la probabilité d’une variable aléatoire continue suivant une loi lognormale.
+MAX			= MAX				##	Renvoie la valeur maximale contenue dans une liste d’arguments.
+MAXA			= MAXA				##	Renvoie la valeur maximale d’une liste d’arguments, nombres, texte et valeurs logiques inclus.
+MEDIAN			= MEDIANE			##	Renvoie la valeur médiane des nombres donnés.
+MIN			= MIN				##	Renvoie la valeur minimale contenue dans une liste d’arguments.
+MINA			= MINA				##	Renvoie la plus petite valeur d’une liste d’arguments, nombres, texte et valeurs logiques inclus.
+MODE			= MODE				##	Renvoie la valeur la plus courante d’une série de données.
+NEGBINOMDIST		= LOI.BINOMIALE.NEG		##	Renvoie la probabilité d’une variable aléatoire discrète suivant une loi binomiale négative.
+NORMDIST		= LOI.NORMALE			##	Renvoie la probabilité d’une variable aléatoire continue suivant une loi normale.
+NORMINV			= LOI.NORMALE.INVERSE		##	Renvoie, pour une probabilité donnée, la valeur d’une variable aléatoire suivant une loi normale standard.
+NORMSDIST		= LOI.NORMALE.STANDARD		##	Renvoie la probabilité d’une variable aléatoire continue suivant une loi normale standard.
+NORMSINV		= LOI.NORMALE.STANDARD.INVERSE	##	Renvoie l’inverse de la distribution cumulée normale standard.
+PEARSON			= PEARSON			##	Renvoie le coefficient de corrélation d’échantillonnage de Pearson.
+PERCENTILE		= CENTILE			##	Renvoie le k-ième centile des valeurs d’une plage.
+PERCENTRANK		= RANG.POURCENTAGE		##	Renvoie le rang en pourcentage d’une valeur d’une série de données.
+PERMUT			= PERMUTATION			##	Renvoie le nombre de permutations pour un nombre donné d’objets.
+POISSON			= LOI.POISSON			##	Renvoie la probabilité d’une variable aléatoire suivant une loi de Poisson.
+PROB			= PROBABILITE			##	Renvoie la probabilité que des valeurs d’une plage soient comprises entre deux limites.
+QUARTILE		= QUARTILE			##	Renvoie le quartile d’une série de données.
+RANK			= RANG				##	Renvoie le rang d’un nombre contenu dans une liste.
+RSQ			= COEFFICIENT.DETERMINATION	##	Renvoie la valeur du coefficient de détermination R^2 d’une régression linéaire.
+SKEW			= COEFFICIENT.ASYMETRIE		##	Renvoie l’asymétrie d’une distribution.
+SLOPE			= PENTE				##	Renvoie la pente d’une droite de régression linéaire.
+SMALL			= PETITE.VALEUR			##	Renvoie la k-ième plus petite valeur d’une série de données.
+STANDARDIZE		= CENTREE.REDUITE		##	Renvoie une valeur centrée réduite.
+STDEV			= ECARTYPE			##	Évalue l’écart type d’une population en se basant sur un échantillon de cette population.
+STDEVA			= STDEVA			##	Évalue l’écart type d’une population en se basant sur un échantillon de cette population, nombres, texte et valeurs logiques inclus.
+STDEVP			= ECARTYPEP			##	Calcule l’écart type d’une population à partir de la population entière.
+STDEVPA			= STDEVPA			##	Calcule l’écart type d’une population à partir de l’ensemble de la population, nombres, texte et valeurs logiques inclus.
+STEYX			= ERREUR.TYPE.XY		##	Renvoie l’erreur type de la valeur y prévue pour chaque x de la régression.
+TDIST			= LOI.STUDENT			##	Renvoie la probabilité d’une variable aléatoire suivant une loi T de Student.
+TINV			= LOI.STUDENT.INVERSE		##	Renvoie, pour une probabilité donnée, la valeur d’une variable aléatoire suivant une loi T de Student.
+TREND			= TENDANCE			##	Renvoie des valeurs par rapport à une tendance linéaire.
+TRIMMEAN		= MOYENNE.REDUITE		##	Renvoie la moyenne de l’intérieur d’une série de données.
+TTEST			= TEST.STUDENT			##	Renvoie la probabilité associée à un test T de Student.
+VAR			= VAR				##	Calcule la variance sur la base d’un échantillon.
+VARA			= VARA				##	Estime la variance d’une population en se basant sur un échantillon de cette population, nombres, texte et valeurs logiques incluses.
+VARP			= VAR.P				##	Calcule la variance sur la base de l’ensemble de la population.
+VARPA			= VARPA				##	Calcule la variance d’une population en se basant sur la population entière, nombres, texte et valeurs logiques inclus.
+WEIBULL			= LOI.WEIBULL			##	Renvoie la probabilité d’une variable aléatoire suivant une loi de Weibull.
+ZTEST			= TEST.Z			##	Renvoie la valeur de probabilité unilatérale d’un test z.
+
+
+##
+##	Text functions					Fonctions de texte
+##
+ASC			= ASC				##	Change les caractères anglais ou katakana à pleine chasse (codés sur deux octets) à l’intérieur d’une chaîne de caractères en caractères à demi-chasse (codés sur un octet).
+BAHTTEXT		= BAHTTEXT			##	Convertit un nombre en texte en utilisant le format monétaire ß (baht).
+CHAR			= CAR				##	Renvoie le caractère spécifié par le code numérique.
+CLEAN			= EPURAGE			##	Supprime tous les caractères de contrôle du texte.
+CODE			= CODE				##	Renvoie le numéro de code du premier caractère du texte.
+CONCATENATE		= CONCATENER			##	Assemble plusieurs éléments textuels de façon à n’en former qu’un seul.
+DOLLAR			= EURO				##	Convertit un nombre en texte en utilisant le format monétaire € (euro).
+EXACT			= EXACT				##	Vérifie si deux valeurs de texte sont identiques.
+FIND			= TROUVE			##	Trouve un valeur textuelle dans une autre, en respectant la casse.
+FINDB			= TROUVERB			##	Trouve un valeur textuelle dans une autre, en respectant la casse.
+FIXED			= CTXT				##	Convertit un nombre au format texte avec un nombre de décimales spécifié.
+JIS			= JIS				##	Change les caractères anglais ou katakana à demi-chasse (codés sur un octet) à l’intérieur d’une chaîne de caractères en caractères à à pleine chasse (codés sur deux octets).
+LEFT			= GAUCHE			##	Renvoie des caractères situés à l’extrême gauche d’une chaîne de caractères.
+LEFTB			= GAUCHEB			##	Renvoie des caractères situés à l’extrême gauche d’une chaîne de caractères.
+LEN			= NBCAR				##	Renvoie le nombre de caractères contenus dans une chaîne de texte.
+LENB			= LENB				##	Renvoie le nombre de caractères contenus dans une chaîne de texte.
+LOWER			= MINUSCULE			##	Convertit le texte en minuscules.
+MID			= STXT				##	Renvoie un nombre déterminé de caractères d’une chaîne de texte à partir de la position que vous indiquez.
+MIDB			= STXTB				##	Renvoie un nombre déterminé de caractères d’une chaîne de texte à partir de la position que vous indiquez.
+PHONETIC		= PHONETIQUE			##	Extrait les caractères phonétiques (furigana) d’une chaîne de texte.
+PROPER			= NOMPROPRE			##	Met en majuscules la première lettre de chaque mot dans une chaîne textuelle.
+REPLACE			= REMPLACER			##	Remplace des caractères dans un texte.
+REPLACEB		= REMPLACERB			##	Remplace des caractères dans un texte.
+REPT			= REPT				##	Répète un texte un certain nombre de fois.
+RIGHT			= DROITE			##	Renvoie des caractères situés à l’extrême droite d’une chaîne de caractères.
+RIGHTB			= DROITEB			##	Renvoie des caractères situés à l’extrême droite d’une chaîne de caractères.
+SEARCH			= CHERCHE			##	Trouve un texte dans un autre texte (sans respecter la casse).
+SEARCHB			= CHERCHERB			##	Trouve un texte dans un autre texte (sans respecter la casse).
+SUBSTITUTE		= SUBSTITUE			##	Remplace l’ancien texte d’une chaîne de caractères par un nouveau.
+T			= T				##	Convertit ses arguments en texte.
+TEXT			= TEXTE				##	Convertit un nombre au format texte.
+TRIM			= SUPPRESPACE			##	Supprime les espaces du texte.
+UPPER			= MAJUSCULE			##	Convertit le texte en majuscules.
+VALUE			= CNUM				##	Convertit un argument textuel en nombre
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/hu/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/hu/config
new file mode 100755
index 0000000..db61436
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/hu/config
@@ -0,0 +1,23 @@
+##
+## PhpSpreadsheet
+##
+
+ArgumentSeparator	= ;
+
+
+##
+##	(For future use)
+##
+currencySymbol	= Ft
+
+
+##
+##	Excel Error Codes	(For future use)
+##
+NULL	= #NULLA!
+DIV0	= #ZÉRÓOSZTÓ!
+VALUE	= #ÉRTÉK!
+REF	= #HIV!
+NAME	= #NÉV?
+NUM	= #SZÁM!
+NA	= #HIÁNYZIK
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/hu/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/hu/functions
new file mode 100755
index 0000000..3adffeb
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/hu/functions
@@ -0,0 +1,416 @@
+##
+## PhpSpreadsheet
+##
+## Data in this file derived from https://www.excel-function-translation.com/
+##
+##
+
+
+##
+##	Add-in and Automation functions			Bővítmények és automatizálási függvények
+##
+GETPIVOTDATA		= KIMUTATÁSADATOT.VESZ		##	A kimutatásokban tárolt adatok visszaadására használható.
+
+
+##
+##	Cube functions Kockafüggvények
+##
+CUBEKPIMEMBER		= KOCKA.FŐTELJMUT		##	Egy fő teljesítménymutató (KPI) nevét, tulajdonságát és mértékegységét adja eredményül, a nevet és a tulajdonságot megjeleníti a cellában. A KPI-k számszerűsíthető mérési lehetőséget jelentenek – ilyen mutató például a havi bruttó nyereség vagy az egy alkalmazottra jutó negyedéves forgalom –, egy szervezet teljesítményének nyomonkövetésére használhatók.
+CUBEMEMBER		= KOCKA.TAG			##	Kockahierachia tagját vagy rekordját adja eredményül. Ellenőrizhető vele, hogy szerepel-e a kockában az adott tag vagy rekord.
+CUBEMEMBERPROPERTY	= KOCKA.TAG.TUL			##	A kocka egyik tagtulajdonságának értékét adja eredményül. Használatával ellenőrizhető, hogy szerepel-e egy tagnév a kockában, eredménye pedig az erre a tagra vonatkozó, megadott tulajdonság.
+CUBERANKEDMEMBER	= KOCKA.HALM.ELEM		##	Egy halmaz rangsor szerinti n-edik tagját adja eredményül. Használatával egy halmaz egy vagy több elemét kaphatja meg, például a legnagyobb teljesítményű üzletkötőt vagy a 10 legjobb tanulót.
+CUBESET			= KOCKA.HALM			##	Számított tagok vagy rekordok halmazát adja eredményül, ehhez egy beállított kifejezést elküld a kiszolgálón található kockának, majd ezt a halmazt adja vissza a Microsoft Office Excel alkalmazásnak.
+CUBESETCOUNT		= KOCKA.HALM.DB			##	Egy halmaz elemszámát adja eredményül.
+CUBEVALUE		= KOCKA.ÉRTÉK			##	Kockából összesített értéket ad eredményül.
+
+
+##
+##	Database functions				Adatbázis-kezelő függvények
+##
+DAVERAGE		= AB.ÁTLAG			##	A kijelölt adatbáziselemek átlagát számítja ki.
+DCOUNT			= AB.DARAB			##	Megszámolja, hogy az adatbázisban hány cella tartalmaz számokat.
+DCOUNTA			= AB.DARAB2			##	Megszámolja az adatbázisban lévő nem üres cellákat.
+DGET			= AB.MEZŐ			##	Egy adatbázisból egyetlen olyan rekordot ad vissza, amely megfelel a megadott feltételeknek.
+DMAX			= AB.MAX			##	A kiválasztott adatbáziselemek közül a legnagyobb értéket adja eredményül.
+DMIN			= AB.MIN			##	A kijelölt adatbáziselemek közül a legkisebb értéket adja eredményül.
+DPRODUCT		= AB.SZORZAT			##	Az adatbázis megadott feltételeknek eleget tevő rekordjaira összeszorozza a megadott mezőben található számértékeket, és eredményül ezt a szorzatot adja.
+DSTDEV			= AB.SZÓRÁS			##	A kijelölt adatbáziselemek egy mintája alapján megbecsüli a szórást.
+DSTDEVP			= AB.SZÓRÁS2			##	A kijelölt adatbáziselemek teljes sokasága alapján kiszámítja a szórást.
+DSUM			= AB.SZUM			##	Összeadja a feltételnek megfelelő adatbázisrekordok mezőoszlopában a számokat.
+DVAR			= AB.VAR			##	A kijelölt adatbáziselemek mintája alapján becslést ad a szórásnégyzetre.
+DVARP			= AB.VAR2			##	A kijelölt adatbáziselemek teljes sokasága alapján kiszámítja a szórásnégyzetet.
+
+
+##
+##	Date and time functions				Dátumfüggvények
+##
+DATE			= DÁTUM				##	Adott dátum dátumértékét adja eredményül.
+DATEVALUE		= DÁTUMÉRTÉK			##	Szövegként megadott dátumot dátumértékké alakít át.
+DAY			= NAP				##	Dátumértéket a hónap egy napjává (0-31) alakít.
+DAYS360			= NAP360			##	Két dátum közé eső napok számát számítja ki a 360 napos év alapján.
+EDATE			= EDATE				##	Adott dátumnál adott számú hónappal korábbi vagy későbbi dátum dátumértékét adja eredményül.
+EOMONTH			= EOMONTH			##	Adott dátumnál adott számú hónappal korábbi vagy későbbi hónap utolsó napjának dátumértékét adja eredményül.
+HOUR			= ÓRA				##	Időértéket órákká alakít.
+MINUTE			= PERC				##	Időértéket percekké alakít.
+MONTH			= HÓNAP				##	Időértéket hónapokká alakít.
+NETWORKDAYS		= NETWORKDAYS			##	Két dátum között a teljes munkanapok számát adja meg.
+NOW			= MOST				##	A napi dátum dátumértékét és a pontos idő időértékét adja eredményül.
+SECOND			= MPERC				##	Időértéket másodpercekké alakít át.
+TIME			= IDŐ				##	Adott időpont időértékét adja meg.
+TIMEVALUE		= IDŐÉRTÉK			##	Szövegként megadott időpontot időértékké alakít át.
+TODAY			= MA				##	A napi dátum dátumértékét adja eredményül.
+WEEKDAY			= HÉT.NAPJA			##	Dátumértéket a hét napjává alakítja át.
+WEEKNUM			= WEEKNUM			##	Visszatérési értéke egy szám, amely azt mutatja meg, hogy a megadott dátum az év hányadik hetére esik.
+WORKDAY			= WORKDAY			##	Adott dátumnál adott munkanappal korábbi vagy későbbi dátum dátumértékét adja eredményül.
+YEAR			= ÉV				##	Sorszámot évvé alakít át.
+YEARFRAC		= YEARFRAC			##	Az adott dátumok közötti teljes napok számát törtévként adja meg.
+
+
+##
+##	Engineering functions				Mérnöki függvények
+##
+BESSELI			= BESSELI			##	Az In(x) módosított Bessel-függvény értékét adja eredményül.
+BESSELJ			= BESSELJ			##	A Jn(x) Bessel-függvény értékét adja eredményül.
+BESSELK			= BESSELK			##	A Kn(x) módosított Bessel-függvény értékét adja eredményül.
+BESSELY			= BESSELY			##	Az Yn(x) módosított Bessel-függvény értékét adja eredményül.
+BIN2DEC			= BIN2DEC			##	Bináris számot decimálissá alakít át.
+BIN2HEX			= BIN2HEX			##	Bináris számot hexadecimálissá alakít át.
+BIN2OCT			= BIN2OCT			##	Bináris számot oktálissá alakít át.
+COMPLEX			= COMPLEX			##	Valós és képzetes részből komplex számot képez.
+CONVERT			= CONVERT			##	Mértékegységeket vált át.
+DEC2BIN			= DEC2BIN			##	Decimális számot binárissá alakít át.
+DEC2HEX			= DEC2HEX			##	Decimális számot hexadecimálissá alakít át.
+DEC2OCT			= DEC2OCT			##	Decimális számot oktálissá alakít át.
+DELTA			= DELTA				##	Azt vizsgálja, hogy két érték egyenlő-e.
+ERF			= ERF				##	A hibafüggvény értékét adja eredményül.
+ERFC			= ERFC				##	A kiegészített hibafüggvény értékét adja eredményül.
+GESTEP			= GESTEP			##	Azt vizsgálja, hogy egy szám nagyobb-e adott küszöbértéknél.
+HEX2BIN			= HEX2BIN			##	Hexadecimális számot binárissá alakít át.
+HEX2DEC			= HEX2DEC			##	Hexadecimális számot decimálissá alakít át.
+HEX2OCT			= HEX2OCT			##	Hexadecimális számot oktálissá alakít át.
+IMABS			= IMABS				##	Komplex szám abszolút értékét (modulusát) adja eredményül.
+IMAGINARY		= IMAGINARY			##	Komplex szám képzetes részét adja eredményül.
+IMARGUMENT		= IMARGUMENT			##	A komplex szám radiánban kifejezett théta argumentumát adja eredményül.
+IMCONJUGATE		= IMCONJUGATE			##	Komplex szám komplex konjugáltját adja eredményül.
+IMCOS			= IMCOS				##	Komplex szám koszinuszát adja eredményül.
+IMDIV			= IMDIV				##	Két komplex szám hányadosát adja eredményül.
+IMEXP			= IMEXP				##	Az e szám komplex kitevőjű hatványát adja eredményül.
+IMLN			= IMLN				##	Komplex szám természetes logaritmusát adja eredményül.
+IMLOG10			= IMLOG10			##	Komplex szám tízes alapú logaritmusát adja eredményül.
+IMLOG2			= IMLOG2			##	Komplex szám kettes alapú logaritmusát adja eredményül.
+IMPOWER			= IMPOWER			##	Komplex szám hatványát adja eredményül.
+IMPRODUCT		= IMPRODUCT			##	Komplex számok szorzatát adja eredményül.
+IMREAL			= IMREAL			##	Komplex szám valós részét adja eredményül.
+IMSIN			= IMSIN				##	Komplex szám szinuszát adja eredményül.
+IMSQRT			= IMSQRT			##	Komplex szám négyzetgyökét adja eredményül.
+IMSUB			= IMSUB				##	Két komplex szám különbségét adja eredményül.
+IMSUM			= IMSUM				##	Komplex számok összegét adja eredményül.
+OCT2BIN			= OCT2BIN			##	Oktális számot binárissá alakít át.
+OCT2DEC			= OCT2DEC			##	Oktális számot decimálissá alakít át.
+OCT2HEX			= OCT2HEX			##	Oktális számot hexadecimálissá alakít át.
+
+
+##
+##	Financial functions				Pénzügyi függvények
+##
+ACCRINT			= ACCRINT			##	Periodikusan kamatozó értékpapír felszaporodott kamatát adja eredményül.
+ACCRINTM		= ACCRINTM			##	Lejáratkor kamatozó értékpapír felszaporodott kamatát adja eredményül.
+AMORDEGRC		= AMORDEGRC			##	Állóeszköz lineáris értékcsökkenését adja meg az egyes könyvelési időszakokra vonatkozóan.
+AMORLINC		= AMORLINC			##	Az egyes könyvelési időszakokban az értékcsökkenést adja meg.
+COUPDAYBS		= COUPDAYBS			##	A szelvényidőszak kezdetétől a kifizetés időpontjáig eltelt napokat adja vissza.
+COUPDAYS		= COUPDAYS			##	A kifizetés időpontját magában foglaló szelvényperiódus hosszát adja meg napokban.
+COUPDAYSNC		= COUPDAYSNC			##	A kifizetés időpontja és a legközelebbi szelvénydátum közötti napok számát adja meg.
+COUPNCD			= COUPNCD			##	A kifizetést követő legelső szelvénydátumot adja eredményül.
+COUPNUM			= COUPNUM			##	A kifizetés és a lejárat időpontja között kifizetendő szelvények számát adja eredményül.
+COUPPCD			= COUPPCD			##	A kifizetés előtti utolsó szelvénydátumot adja eredményül.
+CUMIPMT			= CUMIPMT			##	Két fizetési időszak között kifizetett kamat halmozott értékét adja eredményül.
+CUMPRINC		= CUMPRINC			##	Két fizetési időszak között kifizetett részletek halmozott (kamatot nem tartalmazó) értékét adja eredményül.
+DB			= KCS2				##	Eszköz adott időszak alatti értékcsökkenését számítja ki a lineáris leírási modell alkalmazásával.
+DDB			= KCSA				##	Eszköz értékcsökkenését számítja ki adott időszakra vonatkozóan a progresszív vagy egyéb megadott leírási modell alkalmazásával.
+DISC			= DISC				##	Értékpapír leszámítolási kamatlábát adja eredményül.
+DOLLARDE		= DOLLARDE			##	Egy közönséges törtként megadott számot tizedes törtté alakít át.
+DOLLARFR		= DOLLARFR			##	Tizedes törtként megadott számot közönséges törtté alakít át.
+DURATION		= DURATION			##	Periodikus kamatfizetésű értékpapír éves kamatérzékenységét adja eredményül.
+EFFECT			= EFFECT			##	Az éves tényleges kamatláb értékét adja eredményül.
+FV			= JBÉ				##	Befektetés jövőbeli értékét számítja ki.
+FVSCHEDULE		= FVSCHEDULE			##	A kezdőtőke adott kamatlábak szerint megnövelt jövőbeli értékét adja eredményül.
+INTRATE			= INTRATE			##	A lejáratig teljesen lekötött értékpapír kamatrátáját adja eredményül.
+IPMT			= RRÉSZLET			##	Hiteltörlesztésen belül a tőketörlesztés nagyságát számítja ki adott időszakra.
+IRR			= BMR				##	A befektetés belső megtérülési rátáját számítja ki pénzáramláshoz.
+ISPMT			= LRÉSZLETKAMAT			##	A befektetés adott időszakára fizetett kamatot számítja ki.
+MDURATION		= MDURATION			##	Egy 100 Ft névértékű értékpapír Macauley-féle módosított kamatérzékenységét adja eredményül.
+MIRR			= MEGTÉRÜLÉS			##	A befektetés belső megtérülési rátáját számítja ki a költségek és a bevételek különböző kamatlába mellett.
+NOMINAL			= NOMINAL			##	Az éves névleges kamatláb értékét adja eredményül.
+NPER			= PER.SZÁM			##	A törlesztési időszakok számát adja meg.
+NPV			= NMÉ				##	Befektetéshez kapcsolódó pénzáramlás nettó jelenértékét számítja ki ismert pénzáramlás és kamatláb mellett.
+ODDFPRICE		= ODDFPRICE			##	Egy 100 Ft névértékű, a futamidő elején töredék-időszakos értékpapír árát adja eredményül.
+ODDFYIELD		= ODDFYIELD			##	A futamidő elején töredék-időszakos értékpapír hozamát adja eredményül.
+ODDLPRICE		= ODDLPRICE			##	Egy 100 Ft névértékű, a futamidő végén töredék-időszakos értékpapír árát adja eredményül.
+ODDLYIELD		= ODDLYIELD			##	A futamidő végén töredék-időszakos értékpapír hozamát adja eredményül.
+PMT			= RÉSZLET			##	A törlesztési időszakra vonatkozó törlesztési összeget számítja ki.
+PPMT			= PRÉSZLET			##	Hiteltörlesztésen belül a tőketörlesztés nagyságát számítja ki adott időszakra.
+PRICE			= PRICE				##	Egy 100 Ft névértékű, periodikusan kamatozó értékpapír árát adja eredményül.
+PRICEDISC		= PRICEDISC			##	Egy 100 Ft névértékű leszámítolt értékpapír árát adja eredményül.
+PRICEMAT		= PRICEMAT			##	Egy 100 Ft névértékű, a lejáratkor kamatozó értékpapír árát adja eredményül.
+PV			= MÉ				##	Befektetés jelenlegi értékét számítja ki.
+RATE			= RÁTA				##	Egy törlesztési időszakban az egy időszakra eső kamatláb nagyságát számítja ki.
+RECEIVED		= RECEIVED			##	A lejáratig teljesen lekötött értékpapír lejáratakor kapott összegét adja eredményül.
+SLN			= LCSA				##	Tárgyi eszköz egy időszakra eső amortizációját adja meg bruttó érték szerinti lineáris leírási kulcsot alkalmazva.
+SYD			= SYD				##	Tárgyi eszköz értékcsökkenését számítja ki adott időszakra az évek számjegyösszegével dolgozó módszer alapján.
+TBILLEQ			= TBILLEQ			##	Kincstárjegy kötvény-egyenértékű hozamát adja eredményül.
+TBILLPRICE		= TBILLPRICE			##	Egy 100 Ft névértékű kincstárjegy árát adja eredményül.
+TBILLYIELD		= TBILLYIELD			##	Kincstárjegy hozamát adja eredményül.
+VDB			= ÉCSRI				##	Tárgyi eszköz amortizációját számítja ki megadott vagy részidőszakra a csökkenő egyenleg módszerének alkalmazásával.
+XIRR			= XIRR				##	Ütemezett készpénzforgalom (cash flow) belső megtérülési kamatrátáját adja eredményül.
+XNPV			= XNPV				##	Ütemezett készpénzforgalom (cash flow) nettó jelenlegi értékét adja eredményül.
+YIELD			= YIELD				##	Periodikusan kamatozó értékpapír hozamát adja eredményül.
+YIELDDISC		= YIELDDISC			##	Leszámítolt értékpapír (például kincstárjegy) éves hozamát adja eredményül.
+YIELDMAT		= YIELDMAT			##	Lejáratkor kamatozó értékpapír éves hozamát adja eredményül.
+
+
+##
+##	Information functions				Információs függvények
+##
+CELL			= CELLA				##	Egy cella formátumára, elhelyezkedésére vagy tartalmára vonatkozó adatokat ad eredményül.
+ERROR.TYPE		= HIBA.TÍPUS			##	Egy hibatípushoz tartozó számot ad eredményül.
+INFO			= INFÓ				##	A rendszer- és munkakörnyezet pillanatnyi állapotáról ad felvilágosítást.
+ISBLANK			= ÜRES				##	Eredménye IGAZ, ha az érték üres.
+ISERR			= HIBA				##	Eredménye IGAZ, ha az érték valamelyik hibaérték a #HIÁNYZIK kivételével.
+ISERROR			= HIBÁS				##	Eredménye IGAZ, ha az érték valamelyik hibaérték.
+ISEVEN			= ISEVEN			##	Eredménye IGAZ, ha argumentuma páros szám.
+ISLOGICAL		= LOGIKAI			##	Eredménye IGAZ, ha az érték logikai érték.
+ISNA			= NINCS				##	Eredménye IGAZ, ha az érték a #HIÁNYZIK hibaérték.
+ISNONTEXT		= NEM.SZÖVEG			##	Eredménye IGAZ, ha az érték nem szöveg.
+ISNUMBER		= SZÁM				##	Eredménye IGAZ, ha az érték szám.
+ISODD			= ISODD				##	Eredménye IGAZ, ha argumentuma páratlan szám.
+ISREF			= HIVATKOZÁS			##	Eredménye IGAZ, ha az érték hivatkozás.
+ISTEXT			= SZÖVEG.E			##	Eredménye IGAZ, ha az érték szöveg.
+N			= N				##	Argumentumának értékét számmá alakítja.
+NA			= HIÁNYZIK			##	Eredménye a #HIÁNYZIK hibaérték.
+TYPE			= TÍPUS				##	Érték adattípusának azonosítószámát adja eredményül.
+
+
+##
+##	Logical functions				Logikai függvények
+##
+AND			= ÉS				##	Eredménye IGAZ, ha minden argumentuma IGAZ.
+FALSE			= HAMIS				##	A HAMIS logikai értéket adja eredményül.
+IF			= HA				##	Logikai vizsgálatot hajt végre.
+IFERROR			= HAHIBA			##	A megadott értéket adja vissza, ha egy képlet hibához vezet; más esetben a képlet értékét adja eredményül.
+NOT			= NEM				##	Argumentuma értékének ellentettjét adja eredményül.
+OR			= VAGY				##	Eredménye IGAZ, ha bármely argumentuma IGAZ.
+TRUE			= IGAZ				##	Az IGAZ logikai értéket adja eredményül.
+
+
+##
+##	Lookup and reference functions			Keresési és hivatkozási függvények
+##
+ADDRESS			= CÍM				##	A munkalap egy cellájára való hivatkozást adja szövegként eredményül.
+AREAS			= TERÜLET			##	Hivatkozásban a területek számát adja eredményül.
+CHOOSE			= VÁLASZT			##	Értékek listájából választ ki egy elemet.
+COLUMN			= OSZLOP			##	Egy hivatkozás oszlopszámát adja eredményül.
+COLUMNS			= OSZLOPOK			##	A hivatkozásban található oszlopok számát adja eredményül.
+HLOOKUP			= VKERES			##	A megadott tömb felső sorában adott értékű elemet keres, és a megtalált elem oszlopából adott sorban elhelyezkedő értékkel tér vissza.
+HYPERLINK		= HIPERHIVATKOZÁS		##	Hálózati kiszolgálón, intraneten vagy az interneten tárolt dokumentumot megnyitó parancsikont vagy hivatkozást hoz létre.
+INDEX			= INDEX				##	Tömb- vagy hivatkozás indexszel megadott értékét adja vissza.
+INDIRECT		= INDIREKT			##	Szöveg megadott hivatkozást ad eredményül.
+LOOKUP			= KERES				##	Vektorban vagy tömbben keres meg értékeket.
+MATCH			= HOL.VAN			##	Hivatkozásban vagy tömbben értékeket keres.
+OFFSET			= OFSZET			##	Hivatkozás egy másik hivatkozástól számított távolságát adja meg.
+ROW			= SOR				##	Egy hivatkozás sorának számát adja meg.
+ROWS			= SOROK				##	Egy hivatkozás sorainak számát adja meg.
+RTD			= RTD				##	Valós idejű adatokat keres vissza a COM automatizmust (automatizálás: Egy alkalmazás objektumaival való munka másik alkalmazásból vagy fejlesztőeszközből. A korábban OLE automatizmusnak nevezett automatizálás iparági szabvány, a Component Object Model (COM) szolgáltatása.) támogató programból.
+TRANSPOSE		= TRANSZPONÁLÁS			##	Egy tömb transzponáltját adja eredményül.
+VLOOKUP			= FKERES			##	A megadott tömb bal szélső oszlopában megkeres egy értéket, majd annak sora és a megadott oszlop metszéspontjában levő értéked adja eredményül.
+
+
+##
+##	Math and trigonometry functions			Matematikai és trigonometrikus függvények
+##
+ABS			= ABS				##	Egy szám abszolút értékét adja eredményül.
+ACOS			= ARCCOS			##	Egy szám arkusz koszinuszát számítja ki.
+ACOSH			= ACOSH				##	Egy szám inverz koszinusz hiperbolikuszát számítja ki.
+ASIN			= ARCSIN			##	Egy szám arkusz szinuszát számítja ki.
+ASINH			= ASINH				##	Egy szám inverz szinusz hiperbolikuszát számítja ki.
+ATAN			= ARCTAN			##	Egy szám arkusz tangensét számítja ki.
+ATAN2			= ARCTAN2			##	X és y koordináták alapján számítja ki az arkusz tangens értéket.
+ATANH			= ATANH				##	A szám inverz tangens hiperbolikuszát számítja ki.
+CEILING			= PLAFON			##	Egy számot a legközelebbi egészre vagy a pontosságként megadott érték legközelebb eső többszörösére kerekít.
+COMBIN			= KOMBINÁCIÓK			##	Adott számú objektum összes lehetséges kombinációinak számát számítja ki.
+COS			= COS				##	Egy szám koszinuszát számítja ki.
+COSH			= COSH				##	Egy szám koszinusz hiperbolikuszát számítja ki.
+DEGREES			= FOK				##	Radiánt fokká alakít át.
+EVEN			= PÁROS				##	Egy számot a legközelebbi páros egész számra kerekít.
+EXP			= KITEVŐ			##	Az e adott kitevőjű hatványát adja eredményül.
+FACT			= FAKT				##	Egy szám faktoriálisát számítja ki.
+FACTDOUBLE		= FACTDOUBLE			##	Egy szám dupla faktoriálisát adja eredményül.
+FLOOR			= PADLÓ				##	Egy számot lefelé, a nulla felé kerekít.
+GCD			= GCD				##	A legnagyobb közös osztót adja eredményül.
+INT			= INT				##	Egy számot lefelé kerekít a legközelebbi egészre.
+LCM			= LCM				##	A legkisebb közös többszöröst adja eredményül.
+LN			= LN				##	Egy szám természetes logaritmusát számítja ki.
+LOG			= LOG				##	Egy szám adott alapú logaritmusát számítja ki.
+LOG10			= LOG10				##	Egy szám 10-es alapú logaritmusát számítja ki.
+MDETERM			= MDETERM			##	Egy tömb mátrix-determinánsát számítja ki.
+MINVERSE		= INVERZ.MÁTRIX			##	Egy tömb mátrix inverzét adja eredményül.
+MMULT			= MSZORZAT			##	Két tömb mátrix-szorzatát adja meg.
+MOD			= MARADÉK			##	Egy szám osztási maradékát adja eredményül.
+MROUND			= MROUND			##	A kívánt többszörösére kerekített értéket ad eredményül.
+MULTINOMIAL		= MULTINOMIAL			##	Számhalmaz multinomiálisát adja eredményül.
+ODD			= PÁRATLAN			##	Egy számot a legközelebbi páratlan számra kerekít.
+PI			= PI				##	A pi matematikai állandót adja vissza.
+POWER			= HATVÁNY			##	Egy szám adott kitevőjű hatványát számítja ki.
+PRODUCT			= SZORZAT			##	Argumentumai szorzatát számítja ki.
+QUOTIENT		= QUOTIENT			##	Egy hányados egész részét adja eredményül.
+RADIANS			= RADIÁN			##	Fokot radiánná alakít át.
+RAND			= VÉL				##	Egy 0 és 1 közötti véletlen számot ad eredményül.
+RANDBETWEEN		= RANDBETWEEN			##	Megadott számok közé eső véletlen számot állít elő.
+ROMAN			= RÓMAI				##	Egy számot római számokkal kifejezve szövegként ad eredményül.
+ROUND			= KEREKÍTÉS			##	Egy számot adott számú számjegyre kerekít.
+ROUNDDOWN		= KEREKÍTÉS.LE			##	Egy számot lefelé, a nulla felé kerekít.
+ROUNDUP			= KEREKÍTÉS.FEL			##	Egy számot felfelé, a nullától távolabbra kerekít.
+SERIESSUM		= SERIESSUM			##	Hatványsor összegét adja eredményül.
+SIGN			= ELŐJEL			##	Egy szám előjelét adja meg.
+SIN			= SIN				##	Egy szög szinuszát számítja ki.
+SINH			= SINH				##	Egy szám szinusz hiperbolikuszát számítja ki.
+SQRT			= GYÖK				##	Egy szám pozitív négyzetgyökét számítja ki.
+SQRTPI			= SQRTPI			##	A (szám*pi) négyzetgyökét adja eredményül.
+SUBTOTAL		= RÉSZÖSSZEG			##	Lista vagy adatbázis részösszegét adja eredményül.
+SUM			= SZUM				##	Összeadja az argumentumlistájában lévő számokat.
+SUMIF			= SZUMHA			##	A megadott feltételeknek eleget tevő cellákban található értékeket adja össze.
+SUMIFS			= SZUMHATÖBB			##	Több megadott feltételnek eleget tévő tartománycellák összegét adja eredményül.
+SUMPRODUCT		= SZORZATÖSSZEG			##	A megfelelő tömbelemek szorzatának összegét számítja ki.
+SUMSQ			= NÉGYZETÖSSZEG			##	Argumentumai négyzetének összegét számítja ki.
+SUMX2MY2		= SZUMX2BŐLY2			##	Két tömb megfelelő elemei négyzetének különbségét összegzi.
+SUMX2PY2		= SZUMX2MEGY2			##	Két tömb megfelelő elemei négyzetének összegét összegzi.
+SUMXMY2			= SZUMXBŐLY2			##	Két tömb megfelelő elemei különbségének négyzetösszegét számítja ki.
+TAN			= TAN				##	Egy szám tangensét számítja ki.
+TANH			= TANH				##	Egy szám tangens hiperbolikuszát számítja ki.
+TRUNC			= CSONK				##	Egy számot egésszé csonkít.
+
+
+##
+##	Statistical functions				Statisztikai függvények
+##
+AVEDEV			= ÁTL.ELTÉRÉS			##	Az adatpontoknak átlaguktól való átlagos abszolút eltérését számítja ki.
+AVERAGE			= ÁTLAG				##	Argumentumai átlagát számítja ki.
+AVERAGEA		= ÁTLAGA			##	Argumentumai átlagát számítja ki (beleértve a számokat, szöveget és logikai értékeket).
+AVERAGEIF		= ÁTLAGHA			##	A megadott feltételnek eleget tévő tartomány celláinak átlagát (számtani közepét) adja eredményül.
+AVERAGEIFS		= ÁTLAGHATÖBB			##	A megadott feltételeknek eleget tévő cellák átlagát (számtani közepét) adja eredményül.
+BETADIST		= BÉTA.ELOSZLÁS			##	A béta-eloszlás függvényt számítja ki.
+BETAINV			= INVERZ.BÉTA			##	Adott béta-eloszláshoz kiszámítja a béta eloszlásfüggvény inverzét.
+BINOMDIST		= BINOM.ELOSZLÁS		##	A diszkrét binomiális eloszlás valószínűségértékét számítja ki.
+CHIDIST			= KHI.ELOSZLÁS			##	A khi-négyzet-eloszlás egyszélű valószínűségértékét számítja ki.
+CHIINV			= INVERZ.KHI			##	A khi-négyzet-eloszlás egyszélű valószínűségértékének inverzét számítja ki.
+CHITEST			= KHI.PRÓBA			##	Függetlenségvizsgálatot hajt végre.
+CONFIDENCE		= MEGBÍZHATÓSÁG			##	Egy statisztikai sokaság várható értékének megbízhatósági intervallumát adja eredményül.
+CORREL			= KORREL			##	Két adathalmaz korrelációs együtthatóját számítja ki.
+COUNT			= DARAB				##	Megszámolja, hogy argumentumlistájában hány szám található.
+COUNTA			= DARAB2			##	Megszámolja, hogy argumentumlistájában hány érték található.
+COUNTBLANK		= DARABÜRES			##	Egy tartományban összeszámolja az üres cellákat.
+COUNTIF			= DARABTELI			##	Egy tartományban összeszámolja azokat a cellákat, amelyek eleget tesznek a megadott feltételnek.
+COUNTIFS		= DARABHATÖBB			##	Egy tartományban összeszámolja azokat a cellákat, amelyek eleget tesznek több feltételnek.
+COVAR			= KOVAR				##	A kovarianciát, azaz a páronkénti eltérések szorzatának átlagát számítja ki.
+CRITBINOM		= KRITBINOM			##	Azt a legkisebb számot adja eredményül, amelyre a binomiális eloszlásfüggvény értéke nem kisebb egy adott határértéknél.
+DEVSQ			= SQ				##	Az átlagtól való eltérések négyzetének összegét számítja ki.
+EXPONDIST		= EXP.ELOSZLÁS			##	Az exponenciális eloszlás értékét számítja ki.
+FDIST			= F.ELOSZLÁS			##	Az F-eloszlás értékét számítja ki.
+FINV			= INVERZ.F			##	Az F-eloszlás inverzének értékét számítja ki.
+FISHER			= FISHER			##	Fisher-transzformációt hajt végre.
+FISHERINV		= INVERZ.FISHER			##	A Fisher-transzformáció inverzét hajtja végre.
+FORECAST		= ELŐREJELZÉS			##	Az ismert értékek alapján lineáris regresszióval becsült értéket ad eredményül.
+FREQUENCY		= GYAKORISÁG			##	A gyakorisági vagy empirikus eloszlás értékét függőleges tömbként adja eredményül.
+FTEST			= F.PRÓBA			##	Az F-próba értékét adja eredményül.
+GAMMADIST		= GAMMA.ELOSZLÁS		##	A gamma-eloszlás értékét számítja ki.
+GAMMAINV		= INVERZ.GAMMA			##	A gamma-eloszlás eloszlásfüggvénye inverzének értékét számítja ki.
+GAMMALN			= GAMMALN			##	A gamma-függvény természetes logaritmusát számítja ki.
+GEOMEAN			= MÉRTANI.KÖZÉP			##	Argumentumai mértani középértékét számítja ki.
+GROWTH			= NÖV				##	Exponenciális regresszió alapján ad becslést.
+HARMEAN			= HARM.KÖZÉP			##	Argumentumai harmonikus átlagát számítja ki.
+HYPGEOMDIST		= HIPERGEOM.ELOSZLÁS		##	A hipergeometriai eloszlás értékét számítja ki.
+INTERCEPT		= METSZ				##	A regressziós egyenes y tengellyel való metszéspontját határozza meg.
+KURT			= CSÚCSOSSÁG			##	Egy adathalmaz csúcsosságát számítja ki.
+LARGE			= NAGY				##	Egy adathalmaz k-adik legnagyobb elemét adja eredményül.
+LINEST			= LIN.ILL			##	A legkisebb négyzetek módszerével az adatokra illesztett egyenes paramétereit határozza meg.
+LOGEST			= LOG.ILL			##	Az adatokra illesztett exponenciális görbe paramétereit határozza meg.
+LOGINV			= INVERZ.LOG.ELOSZLÁS		##	A lognormális eloszlás inverzét számítja ki.
+LOGNORMDIST		= LOG.ELOSZLÁS			##	A lognormális eloszlásfüggvény értékét számítja ki.
+MAX			= MAX				##	Az argumentumai között szereplő legnagyobb számot adja meg.
+MAXA			= MAX2				##	Az argumentumai között szereplő legnagyobb számot adja meg (beleértve a számokat, szöveget és logikai értékeket).
+MEDIAN			= MEDIÁN			##	Adott számhalmaz mediánját számítja ki.
+MIN			= MIN				##	Az argumentumai között szereplő legkisebb számot adja meg.
+MINA			= MIN2				##	Az argumentumai között szereplő legkisebb számot adja meg, beleértve a számokat, szöveget és logikai értékeket.
+MODE			= MÓDUSZ			##	Egy adathalmazból kiválasztja a leggyakrabban előforduló számot.
+NEGBINOMDIST		= NEGBINOM.ELOSZL		##	A negatív binomiális eloszlás értékét számítja ki.
+NORMDIST		= NORM.ELOSZL			##	A normális eloszlás értékét számítja ki.
+NORMINV			= INVERZ.NORM			##	A normális eloszlás eloszlásfüggvénye inverzének értékét számítja ki.
+NORMSDIST		= STNORMELOSZL			##	A standard normális eloszlás eloszlásfüggvényének értékét számítja ki.
+NORMSINV		= INVERZ.STNORM			##	A standard normális eloszlás eloszlásfüggvénye inverzének értékét számítja ki.
+PEARSON			= PEARSON			##	A Pearson-féle korrelációs együtthatót számítja ki.
+PERCENTILE		= PERCENTILIS			##	Egy tartományban található értékek k-adik percentilisét, azaz százalékosztályát adja eredményül.
+PERCENTRANK		= SZÁZALÉKRANG			##	Egy értéknek egy adathalmazon belül vett százalékos rangját (elhelyezkedését) számítja ki.
+PERMUT			= VARIÁCIÓK			##	Adott számú objektum k-ad osztályú ismétlés nélküli variációinak számát számítja ki.
+POISSON			= POISSON			##	A Poisson-eloszlás értékét számítja ki.
+PROB			= VALÓSZÍNŰSÉG			##	Annak valószínűségét számítja ki, hogy adott értékek két határérték közé esnek.
+QUARTILE		= KVARTILIS			##	Egy adathalmaz kvartilisét (negyedszintjét) számítja ki.
+RANK			= SORSZÁM			##	Kiszámítja, hogy egy szám hányadik egy számsorozatban.
+RSQ			= RNÉGYZET			##	Kiszámítja a Pearson-féle szorzatmomentum korrelációs együtthatójának négyzetét.
+SKEW			= FERDESÉG			##	Egy eloszlás ferdeségét határozza meg.
+SLOPE			= MEREDEKSÉG			##	Egy lineáris regressziós egyenes meredekségét számítja ki.
+SMALL			= KICSI				##	Egy adathalmaz k-adik legkisebb elemét adja meg.
+STANDARDIZE		= NORMALIZÁLÁS			##	Normalizált értéket ad eredményül.
+STDEV			= SZÓRÁS			##	Egy statisztikai sokaság mintájából kiszámítja annak szórását.
+STDEVA			= SZÓRÁSA			##	Egy statisztikai sokaság mintájából kiszámítja annak szórását (beleértve a számokat, szöveget és logikai értékeket).
+STDEVP			= SZÓRÁSP			##	Egy statisztikai sokaság egészéből kiszámítja annak szórását.
+STDEVPA			= SZÓRÁSPA			##	Egy statisztikai sokaság egészéből kiszámítja annak szórását (beleértve számokat, szöveget és logikai értékeket).
+STEYX			= STHIBAYX			##	Egy regresszió esetén az egyes x-értékek alapján meghatározott y-értékek standard hibáját számítja ki.
+TDIST			= T.ELOSZLÁS			##	A Student-féle t-eloszlás értékét számítja ki.
+TINV			= INVERZ.T			##	A Student-féle t-eloszlás inverzét számítja ki.
+TREND			= TREND				##	Lineáris trend értékeit számítja ki.
+TRIMMEAN		= RÉSZÁTLAG			##	Egy adathalmaz középső részének átlagát számítja ki.
+TTEST			= T.PRÓBA			##	A Student-féle t-próbához tartozó valószínűséget számítja ki.
+VAR			= VAR				##	Minta alapján becslést ad a varianciára.
+VARA			= VARA				##	Minta alapján becslést ad a varianciára (beleértve számokat, szöveget és logikai értékeket).
+VARP			= VARP				##	Egy statisztikai sokaság varianciáját számítja ki.
+VARPA			= VARPA				##	Egy statisztikai sokaság varianciáját számítja ki (beleértve számokat, szöveget és logikai értékeket).
+WEIBULL			= WEIBULL			##	A Weibull-féle eloszlás értékét számítja ki.
+ZTEST			= Z.PRÓBA			##	Az egyszélű z-próbával kapott valószínűségértéket számítja ki.
+
+
+##
+##	Text functions					Szövegműveletekhez használható függvények
+##
+ASC			= ASC				##	Szöveg teljes szélességű (kétbájtos) latin és katakana karaktereit félszélességű (egybájtos) karakterekké alakítja.
+BAHTTEXT		= BAHTSZÖVEG			##	Számot szöveggé alakít a ß (baht) pénznemformátum használatával.
+CHAR			= KARAKTER			##	A kódszámmal meghatározott karaktert adja eredményül.
+CLEAN			= TISZTÍT			##	A szövegből eltávolítja az összes nem nyomtatható karaktert.
+CODE			= KÓD				##	Karaktersorozat első karakterének numerikus kódját adja eredményül.
+CONCATENATE		= ÖSSZEFŰZ			##	Több szövegelemet egyetlen szöveges elemmé fűz össze.
+DOLLAR			= FORINT			##	Számot pénznem formátumú szöveggé alakít át.
+EXACT			= AZONOS			##	Megvizsgálja, hogy két érték azonos-e.
+FIND			= SZÖVEG.TALÁL			##	Karaktersorozatot keres egy másikban (a kis- és nagybetűk megkülönböztetésével).
+FINDB			= SZÖVEG.TALÁL2			##	Karaktersorozatot keres egy másikban (a kis- és nagybetűk megkülönböztetésével).
+FIXED			= FIX				##	Számot szöveges formátumúra alakít adott számú tizedesjegyre kerekítve.
+JIS			= JIS				##	A félszélességű (egybájtos) latin és a katakana karaktereket teljes szélességű (kétbájtos) karakterekké alakítja.
+LEFT			= BAL				##	Szöveg bal szélső karaktereit adja eredményül.
+LEFTB			= BAL2				##	Szöveg bal szélső karaktereit adja eredményül.
+LEN			= HOSSZ				##	Szöveg karakterekben mért hosszát adja eredményül.
+LENB			= HOSSZ2			##	Szöveg karakterekben mért hosszát adja eredményül.
+LOWER			= KISBETŰ			##	Szöveget kisbetűssé alakít át.
+MID			= KÖZÉP				##	A szöveg adott pozíciójától kezdve megadott számú karaktert ad vissza eredményként.
+MIDB			= KÖZÉP2			##	A szöveg adott pozíciójától kezdve megadott számú karaktert ad vissza eredményként.
+PHONETIC		= PHONETIC			##	Szöveg furigana (fonetikus) karaktereit adja vissza.
+PROPER			= TNÉV				##	Szöveg minden szavának kezdőbetűjét nagybetűsre cseréli.
+REPLACE			= CSERE				##	A szövegen belül karaktereket cserél.
+REPLACEB		= CSERE2			##	A szövegen belül karaktereket cserél.
+REPT			= SOKSZOR			##	Megadott számú alkalommal megismétel egy szövegrészt.
+RIGHT			= JOBB				##	Szövegrész jobb szélső karaktereit adja eredményül.
+RIGHTB			= JOBB2				##	Szövegrész jobb szélső karaktereit adja eredményül.
+SEARCH			= SZÖVEG.KERES			##	Karaktersorozatot keres egy másikban (a kis- és nagybetűk között nem tesz különbséget).
+SEARCHB			= SZÖVEG.KERES2			##	Karaktersorozatot keres egy másikban (a kis- és nagybetűk között nem tesz különbséget).
+SUBSTITUTE		= HELYETTE			##	Szövegben adott karaktereket másikra cserél.
+T			= T				##	Argumentumát szöveggé alakítja át.
+TEXT			= SZÖVEG			##	Számértéket alakít át adott számformátumú szöveggé.
+TRIM			= TRIM				##	A szövegből eltávolítja a szóközöket.
+UPPER			= NAGYBETŰS			##	Szöveget nagybetűssé alakít át.
+VALUE			= ÉRTÉK				##	Szöveget számmá alakít át.
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/it/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/it/config
new file mode 100755
index 0000000..6cc013a
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/it/config
@@ -0,0 +1,24 @@
+##
+## PhpSpreadsheet
+##
+
+ArgumentSeparator	= ;
+
+
+##
+##	(For future use)
+##
+currencySymbol	= €
+
+
+##
+##	Excel Error Codes	(For future use)
+
+##
+NULL	= #NULLO!
+DIV0	= #DIV/0!
+VALUE	= #VALORE!
+REF	= #RIF!
+NAME	= #NOME?
+NUM	= #NUM!
+NA	= #N/D
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/it/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/it/functions
new file mode 100755
index 0000000..1901baf
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/it/functions
@@ -0,0 +1,416 @@
+##
+## PhpSpreadsheet
+##
+## Data in this file derived from https://www.excel-function-translation.com/
+##
+##
+
+
+##
+##	Add-in and Automation functions			Funzioni di automazione e dei componenti aggiuntivi
+##
+GETPIVOTDATA		= INFO.DATI.TAB.PIVOT		##	Restituisce i dati memorizzati in un rapporto di tabella pivot
+
+
+##
+##	Cube functions					Funzioni cubo
+##
+CUBEKPIMEMBER		= MEMBRO.KPI.CUBO		##	Restituisce il nome, la proprietà e la misura di un indicatore di prestazioni chiave (KPI) e visualizza il nome e la proprietà nella cella. Un KPI è una misura quantificabile, ad esempio l'utile lordo mensile o il fatturato trimestrale dei dipendenti, utilizzata per il monitoraggio delle prestazioni di un'organizzazione.
+CUBEMEMBER		= MEMBRO.CUBO			##	Restituisce un membro o una tupla in una gerarchia di cubi. Consente di verificare l'esistenza del membro o della tupla nel cubo.
+CUBEMEMBERPROPERTY	= PROPRIETÀ.MEMBRO.CUBO		##	Restituisce il valore di una proprietà di un membro del cubo. Consente di verificare l'esistenza di un nome di membro all'interno del cubo e di restituire la proprietà specificata per tale membro.
+CUBERANKEDMEMBER	= MEMBRO.CUBO.CON.RANGO		##	Restituisce l'n-esimo membro o il membro ordinato di un insieme. Consente di restituire uno o più elementi in un insieme, ad esempio l'agente di vendita migliore o i primi 10 studenti.
+CUBESET			= SET.CUBO			##	Definisce un insieme di tuple o membri calcolati mediante l'invio di un'espressione di insieme al cubo sul server. In questo modo l'insieme viene creato e restituito a Microsoft Office Excel.
+CUBESETCOUNT		= CONTA.SET.CUBO		##	Restituisce il numero di elementi di un insieme.
+CUBEVALUE		= VALORE.CUBO			##	Restituisce un valore aggregato da un cubo.
+
+
+##
+##	Database functions				Funzioni di database
+##
+DAVERAGE		= DB.MEDIA			##	Restituisce la media di voci del database selezionate
+DCOUNT			= DB.CONTA.NUMERI		##	Conta le celle di un database contenenti numeri
+DCOUNTA			= DB.CONTA.VALORI		##	Conta le celle non vuote in un database
+DGET			= DB.VALORI			##	Estrae da un database un singolo record che soddisfa i criteri specificati
+DMAX			= DB.MAX			##	Restituisce il valore massimo dalle voci selezionate in un database
+DMIN			= DB.MIN			##	Restituisce il valore minimo dalle voci di un database selezionate
+DPRODUCT		= DB.PRODOTTO			##	Moltiplica i valori in un determinato campo di record che soddisfano i criteri del database
+DSTDEV			= DB.DEV.ST			##	Restituisce una stima della deviazione standard sulla base di un campione di voci di un database selezionate
+DSTDEVP			= DB.DEV.ST.POP			##	Calcola la deviazione standard sulla base di tutte le voci di un database selezionate
+DSUM			= DB.SOMMA			##	Aggiunge i numeri nel campo colonna di record del database che soddisfa determinati criteri
+DVAR			= DB.VAR			##	Restituisce una stima della varianza sulla base di un campione da voci di un database selezionate
+DVARP			= DB.VAR.POP			##	Calcola la varianza sulla base di tutte le voci di un database selezionate
+
+
+##
+##	Date and time functions				Funzioni data e ora
+##
+DATE			= DATA				##	Restituisce il numero seriale di una determinata data
+DATEVALUE		= DATA.VALORE			##	Converte una data sotto forma di testo in un numero seriale
+DAY			= GIORNO			##	Converte un numero seriale in un giorno del mese
+DAYS360			= GIORNO360			##	Calcola il numero di giorni compreso tra due date basandosi su un anno di 360 giorni
+EDATE			= DATA.MESE			##	Restituisce il numero seriale della data che rappresenta il numero di mesi prima o dopo la data di inizio
+EOMONTH			= FINE.MESE			##	Restituisce il numero seriale dell'ultimo giorno del mese, prima o dopo un determinato numero di mesi
+HOUR			= ORA				##	Converte un numero seriale in un'ora
+MINUTE			= MINUTO			##	Converte un numero seriale in un minuto
+MONTH			= MESE				##	Converte un numero seriale in un mese
+NETWORKDAYS		= GIORNI.LAVORATIVI.TOT		##	Restituisce il numero di tutti i giorni lavorativi compresi fra due date
+NOW			= ADESSO			##	Restituisce il numero seriale della data e dell'ora corrente
+SECOND			= SECONDO			##	Converte un numero seriale in un secondo
+TIME			= ORARIO			##	Restituisce il numero seriale di una determinata ora
+TIMEVALUE		= ORARIO.VALORE			##	Converte un orario in forma di testo in un numero seriale
+TODAY			= OGGI				##	Restituisce il numero seriale relativo alla data odierna
+WEEKDAY			= GIORNO.SETTIMANA		##	Converte un numero seriale in un giorno della settimana
+WEEKNUM			= NUM.SETTIMANA			##	Converte un numero seriale in un numero che rappresenta la posizione numerica di una settimana nell'anno
+WORKDAY			= GIORNO.LAVORATIVO		##	Restituisce il numero della data prima o dopo un determinato numero di giorni lavorativi
+YEAR			= ANNO				##	Converte un numero seriale in un anno
+YEARFRAC		= FRAZIONE.ANNO			##	Restituisce la frazione dell'anno che rappresenta il numero dei giorni compresi tra una data_ iniziale e una data_finale
+
+
+##
+##	Engineering functions				Funzioni ingegneristiche
+##
+BESSELI			= BESSEL.I			##	Restituisce la funzione di Bessel modificata In(x)
+BESSELJ			= BESSEL.J			##	Restituisce la funzione di Bessel Jn(x)
+BESSELK			= BESSEL.K			##	Restituisce la funzione di Bessel modificata Kn(x)
+BESSELY			= BESSEL.Y			##	Restituisce la funzione di Bessel Yn(x)
+BIN2DEC			= BINARIO.DECIMALE		##	Converte un numero binario in decimale
+BIN2HEX			= BINARIO.HEX			##	Converte un numero binario in esadecimale
+BIN2OCT			= BINARIO.OCT			##	Converte un numero binario in ottale
+COMPLEX			= COMPLESSO			##	Converte i coefficienti reali e immaginari in numeri complessi
+CONVERT			= CONVERTI			##	Converte un numero da un sistema di misura in un altro
+DEC2BIN			= DECIMALE.BINARIO		##	Converte un numero decimale in binario
+DEC2HEX			= DECIMALE.HEX			##	Converte un numero decimale in esadecimale
+DEC2OCT			= DECIMALE.OCT			##	Converte un numero decimale in ottale
+DELTA			= DELTA				##	Verifica se due valori sono uguali
+ERF			= FUNZ.ERRORE			##	Restituisce la funzione di errore
+ERFC			= FUNZ.ERRORE.COMP		##	Restituisce la funzione di errore complementare
+GESTEP			= SOGLIA			##	Verifica se un numero è maggiore del valore di soglia
+HEX2BIN			= HEX.BINARIO			##	Converte un numero esadecimale in binario
+HEX2DEC			= HEX.DECIMALE			##	Converte un numero esadecimale in decimale
+HEX2OCT			= HEX.OCT			##	Converte un numero esadecimale in ottale
+IMABS			= COMP.MODULO			##	Restituisce il valore assoluto (modulo) di un numero complesso
+IMAGINARY		= COMP.IMMAGINARIO		##	Restituisce il coefficiente immaginario di un numero complesso
+IMARGUMENT		= COMP.ARGOMENTO		##	Restituisce l'argomento theta, un angolo espresso in radianti
+IMCONJUGATE		= COMP.CONIUGATO		##	Restituisce il complesso coniugato del numero complesso
+IMCOS			= COMP.COS			##	Restituisce il coseno di un numero complesso
+IMDIV			= COMP.DIV			##	Restituisce il quoziente di due numeri complessi
+IMEXP			= COMP.EXP			##	Restituisce il valore esponenziale di un numero complesso
+IMLN			= COMP.LN			##	Restituisce il logaritmo naturale di un numero complesso
+IMLOG10			= COMP.LOG10			##	Restituisce il logaritmo in base 10 di un numero complesso
+IMLOG2			= COMP.LOG2			##	Restituisce un logaritmo in base 2 di un numero complesso
+IMPOWER			= COMP.POTENZA			##	Restituisce il numero complesso elevato a una potenza intera
+IMPRODUCT		= COMP.PRODOTTO			##	Restituisce il prodotto di numeri complessi compresi tra 2 e 29
+IMREAL			= COMP.PARTE.REALE		##	Restituisce il coefficiente reale di un numero complesso
+IMSIN			= COMP.SEN			##	Restituisce il seno di un numero complesso
+IMSQRT			= COMP.RADQ			##	Restituisce la radice quadrata di un numero complesso
+IMSUB			= COMP.DIFF			##	Restituisce la differenza fra due numeri complessi
+IMSUM			= COMP.SOMMA			##	Restituisce la somma di numeri complessi
+OCT2BIN			= OCT.BINARIO			##	Converte un numero ottale in binario
+OCT2DEC			= OCT.DECIMALE			##	Converte un numero ottale in decimale
+OCT2HEX			= OCT.HEX			##	Converte un numero ottale in esadecimale
+
+
+##
+##	Financial functions				Funzioni finanziarie
+##
+ACCRINT			= INT.MATURATO.PER		##	Restituisce l'interesse maturato di un titolo che paga interessi periodici
+ACCRINTM		= INT.MATURATO.SCAD		##	Restituisce l'interesse maturato di un titolo che paga interessi alla scadenza
+AMORDEGRC		= AMMORT.DEGR			##	Restituisce l'ammortamento per ogni periodo contabile utilizzando un coefficiente di ammortamento
+AMORLINC		= AMMORT.PER			##	Restituisce l'ammortamento per ogni periodo contabile
+COUPDAYBS		= GIORNI.CED.INIZ.LIQ		##	Restituisce il numero dei giorni che vanno dall'inizio del periodo di durata della cedola alla data di liquidazione
+COUPDAYS		= GIORNI.CED			##	Restituisce il numero dei giorni relativi al periodo della cedola che contiene la data di liquidazione
+COUPDAYSNC		= GIORNI.CED.NUOVA		##	Restituisce il numero di giorni che vanno dalla data di liquidazione alla data della cedola successiva
+COUPNCD			= DATA.CED.SUCC			##	Restituisce un numero che rappresenta la data della cedola successiva alla data di liquidazione
+COUPNUM			= NUM.CED			##	Restituisce il numero di cedole pagabili fra la data di liquidazione e la data di scadenza
+COUPPCD			= DATA.CED.PREC			##	Restituisce un numero che rappresenta la data della cedola precedente alla data di liquidazione
+CUMIPMT			= INT.CUMUL			##	Restituisce l'interesse cumulativo pagato fra due periodi
+CUMPRINC		= CAP.CUM			##	Restituisce il capitale cumulativo pagato per estinguere un debito fra due periodi
+DB			= DB				##	Restituisce l'ammortamento di un bene per un periodo specificato utilizzando il metodo di ammortamento a quote fisse decrescenti
+DDB			= AMMORT			##	Restituisce l'ammortamento di un bene per un periodo specificato utilizzando il metodo di ammortamento a doppie quote decrescenti o altri metodi specificati
+DISC			= TASSO.SCONTO			##	Restituisce il tasso di sconto per un titolo
+DOLLARDE		= VALUTA.DEC			##	Converte un prezzo valuta, espresso come frazione, in prezzo valuta, espresso come numero decimale
+DOLLARFR		= VALUTA.FRAZ			##	Converte un prezzo valuta, espresso come numero decimale, in prezzo valuta, espresso come frazione
+DURATION		= DURATA			##	Restituisce la durata annuale di un titolo con i pagamenti di interesse periodico
+EFFECT			= EFFETTIVO			##	Restituisce l'effettivo tasso di interesse annuo
+FV			= VAL.FUT			##	Restituisce il valore futuro di un investimento
+FVSCHEDULE		= VAL.FUT.CAPITALE		##	Restituisce il valore futuro di un capitale iniziale dopo aver applicato una serie di tassi di interesse composti
+INTRATE			= TASSO.INT			##	Restituisce il tasso di interesse per un titolo interamente investito
+IPMT			= INTERESSI			##	Restituisce il valore degli interessi per un investimento relativo a un periodo specifico
+IRR			= TIR.COST			##	Restituisce il tasso di rendimento interno per una serie di flussi di cassa
+ISPMT			= INTERESSE.RATA		##	Calcola l'interesse di un investimento pagato durante un periodo specifico
+MDURATION		= DURATA.M			##	Restituisce la durata Macauley modificata per un titolo con un valore presunto di € 100
+MIRR			= TIR.VAR			##	Restituisce il tasso di rendimento interno in cui i flussi di cassa positivi e negativi sono finanziati a tassi differenti
+NOMINAL			= NOMINALE			##	Restituisce il tasso di interesse nominale annuale
+NPER			= NUM.RATE			##	Restituisce un numero di periodi relativi a un investimento
+NPV			= VAN				##	Restituisce il valore attuale netto di un investimento basato su una serie di flussi di cassa periodici e sul tasso di sconto
+ODDFPRICE		= PREZZO.PRIMO.IRR		##	Restituisce il prezzo di un titolo dal valore nominale di € 100 avente il primo periodo di durata irregolare
+ODDFYIELD		= REND.PRIMO.IRR		##	Restituisce il rendimento di un titolo avente il primo periodo di durata irregolare
+ODDLPRICE		= PREZZO.ULTIMO.IRR		##	Restituisce il prezzo di un titolo dal valore nominale di € 100 avente l'ultimo periodo di durata irregolare
+ODDLYIELD		= REND.ULTIMO.IRR		##	Restituisce il rendimento di un titolo avente l'ultimo periodo di durata irregolare
+PMT			= RATA				##	Restituisce il pagamento periodico di una rendita annua
+PPMT			= P.RATA			##	Restituisce il pagamento sul capitale di un investimento per un dato periodo
+PRICE			= PREZZO			##	Restituisce il prezzo di un titolo dal valore nominale di € 100 che paga interessi periodici
+PRICEDISC		= PREZZO.SCONT			##	Restituisce il prezzo di un titolo scontato dal valore nominale di € 100
+PRICEMAT		= PREZZO.SCAD			##	Restituisce il prezzo di un titolo dal valore nominale di € 100 che paga gli interessi alla scadenza
+PV			= VA				##	Restituisce il valore attuale di un investimento
+RATE			= TASSO				##	Restituisce il tasso di interesse per un periodo di un'annualità
+RECEIVED		= RICEV.SCAD			##	Restituisce l'ammontare ricevuto alla scadenza di un titolo interamente investito
+SLN			= AMMORT.COST			##	Restituisce l'ammortamento a quote costanti di un bene per un singolo periodo
+SYD			= AMMORT.ANNUO			##	Restituisce l'ammortamento a somma degli anni di un bene per un periodo specificato
+TBILLEQ			= BOT.EQUIV			##	Restituisce il rendimento equivalente ad un'obbligazione per un Buono ordinario del Tesoro
+TBILLPRICE		= BOT.PREZZO			##	Restituisce il prezzo di un Buono del Tesoro dal valore nominale di € 100
+TBILLYIELD		= BOT.REND			##	Restituisce il rendimento di un Buono del Tesoro
+VDB			= AMMORT.VAR			##	Restituisce l'ammortamento di un bene per un periodo specificato o parziale utilizzando il metodo a doppie quote proporzionali ai valori residui
+XIRR			= TIR.X				##	Restituisce il tasso di rendimento interno di un impiego di flussi di cassa
+XNPV			= VAN.X				##	Restituisce il valore attuale netto di un impiego di flussi di cassa non necessariamente periodici
+YIELD			= REND				##	Restituisce il rendimento di un titolo che frutta interessi periodici
+YIELDDISC		= REND.TITOLI.SCONT		##	Restituisce il rendimento annuale di un titolo scontato, ad esempio un Buono del Tesoro
+YIELDMAT		= REND.SCAD			##	Restituisce il rendimento annuo di un titolo che paga interessi alla scadenza
+
+
+##
+##	Information functions				Funzioni relative alle informazioni
+##
+CELL			= CELLA				##	Restituisce le informazioni sulla formattazione, la posizione o i contenuti di una cella
+ERROR.TYPE		= ERRORE.TIPO			##	Restituisce un numero che corrisponde a un tipo di errore
+INFO			= INFO				##	Restituisce le informazioni sull'ambiente operativo corrente
+ISBLANK			= VAL.VUOTO			##	Restituisce VERO se il valore è vuoto
+ISERR			= VAL.ERR			##	Restituisce VERO se il valore è un valore di errore qualsiasi tranne #N/D
+ISERROR			= VAL.ERRORE			##	Restituisce VERO se il valore è un valore di errore qualsiasi
+ISEVEN			= VAL.PARI			##	Restituisce VERO se il numero è pari
+ISLOGICAL		= VAL.LOGICO			##	Restituisce VERO se il valore è un valore logico
+ISNA			= VAL.NON.DISP			##	Restituisce VERO se il valore è un valore di errore #N/D
+ISNONTEXT		= VAL.NON.TESTO			##	Restituisce VERO se il valore non è in formato testo
+ISNUMBER		= VAL.NUMERO			##	Restituisce VERO se il valore è un numero
+ISODD			= VAL.DISPARI			##	Restituisce VERO se il numero è dispari
+ISREF			= VAL.RIF			##	Restituisce VERO se il valore è un riferimento
+ISTEXT			= VAL.TESTO			##	Restituisce VERO se il valore è in formato testo
+N			= NUM				##	Restituisce un valore convertito in numero
+NA			= NON.DISP			##	Restituisce il valore di errore #N/D
+TYPE			= TIPO				##	Restituisce un numero che indica il tipo di dati relativi a un valore
+
+
+##
+##	Logical functions				Funzioni logiche
+##
+AND			= E				##	Restituisce VERO se tutti gli argomenti sono VERO
+FALSE			= FALSO				##	Restituisce il valore logico FALSO
+IF			= SE				##	Specifica un test logico da eseguire
+IFERROR			= SE.ERRORE			##	Restituisce un valore specificato se una formula fornisce un errore come risultato; in caso contrario, restituisce il risultato della formula
+NOT			= NON				##	Inverte la logica degli argomenti
+OR			= O				##	Restituisce VERO se un argomento qualsiasi è VERO
+TRUE			= VERO				##	Restituisce il valore logico VERO
+
+
+##
+##	Lookup and reference functions			Funzioni di ricerca e di riferimento
+##
+ADDRESS			= INDIRIZZO			##	Restituisce un riferimento come testo in una singola cella di un foglio di lavoro
+AREAS			= AREE				##	Restituisce il numero di aree in un riferimento
+CHOOSE			= SCEGLI			##	Sceglie un valore da un elenco di valori
+COLUMN			= RIF.COLONNA			##	Restituisce il numero di colonna di un riferimento
+COLUMNS			= COLONNE			##	Restituisce il numero di colonne in un riferimento
+HLOOKUP			= CERCA.ORIZZ			##	Effettua una ricerca nella riga superiore di una matrice e restituisce il valore della cella specificata
+HYPERLINK		= COLLEG.IPERTESTUALE		##	Crea un collegamento che apre un documento memorizzato in un server di rete, una rete Intranet o Internet
+INDEX			= INDICE			##	Utilizza un indice per scegliere un valore da un riferimento o da una matrice
+INDIRECT		= INDIRETTO			##	Restituisce un riferimento specificato da un valore testo
+LOOKUP			= CERCA				##	Ricerca i valori in un vettore o in una matrice
+MATCH			= CONFRONTA			##	Ricerca i valori in un riferimento o in una matrice
+OFFSET			= SCARTO			##	Restituisce uno scarto di riferimento da un riferimento dato
+ROW			= RIF.RIGA			##	Restituisce il numero di riga di un riferimento
+ROWS			= RIGHE				##	Restituisce il numero delle righe in un riferimento
+RTD			= DATITEMPOREALE		##	Recupera dati in tempo reale da un programma che supporta l'automazione COM (automazione: Metodo per utilizzare gli oggetti di un'applicazione da un'altra applicazione o da un altro strumento di sviluppo. Precedentemente nota come automazione OLE, l'automazione è uno standard del settore e una caratteristica del modello COM (Component Object Model).)
+TRANSPOSE		= MATR.TRASPOSTA		##	Restituisce la trasposizione di una matrice
+VLOOKUP			= CERCA.VERT			##	Effettua una ricerca nella prima colonna di una matrice e si sposta attraverso la riga per restituire il valore di una cella
+
+
+##
+##	Math and trigonometry functions			Funzioni matematiche e trigonometriche
+##
+ABS			= ASS				##	Restituisce il valore assoluto di un numero.
+ACOS			= ARCCOS			##	Restituisce l'arcocoseno di un numero
+ACOSH			= ARCCOSH			##	Restituisce l'inverso del coseno iperbolico di un numero
+ASIN			= ARCSEN			##	Restituisce l'arcoseno di un numero
+ASINH			= ARCSENH			##	Restituisce l'inverso del seno iperbolico di un numero
+ATAN			= ARCTAN			##	Restituisce l'arcotangente di un numero
+ATAN2			= ARCTAN.2			##	Restituisce l'arcotangente delle coordinate x e y specificate
+ATANH			= ARCTANH			##	Restituisce l'inverso della tangente iperbolica di un numero
+CEILING			= ARROTONDA.ECCESSO		##	Arrotonda un numero per eccesso all'intero più vicino o al multiplo più vicino a peso
+COMBIN			= COMBINAZIONE			##	Restituisce il numero di combinazioni possibili per un numero assegnato di elementi
+COS			= COS				##	Restituisce il coseno dell'angolo specificato
+COSH			= COSH				##	Restituisce il coseno iperbolico di un numero
+DEGREES			= GRADI				##	Converte i radianti in gradi
+EVEN			= PARI				##	Arrotonda il valore assoluto di un numero per eccesso al più vicino intero pari
+EXP			= ESP				##	Restituisce il numero e elevato alla potenza di num
+FACT			= FATTORIALE			##	Restituisce il fattoriale di un numero
+FACTDOUBLE		= FATT.DOPPIO			##	Restituisce il fattoriale doppio di un numero
+FLOOR			= ARROTONDA.DIFETTO		##	Arrotonda un numero per difetto al multiplo più vicino a zero
+GCD			= MCD				##	Restituisce il massimo comune divisore
+INT			= INT				##	Arrotonda un numero per difetto al numero intero più vicino
+LCM			= MCM				##	Restituisce il minimo comune multiplo
+LN			= LN				##	Restituisce il logaritmo naturale di un numero
+LOG			= LOG				##	Restituisce il logaritmo di un numero in una specificata base
+LOG10			= LOG10				##	Restituisce il logaritmo in base 10 di un numero
+MDETERM			= MATR.DETERM			##	Restituisce il determinante di una matrice
+MINVERSE		= MATR.INVERSA			##	Restituisce l'inverso di una matrice
+MMULT			= MATR.PRODOTTO			##	Restituisce il prodotto di due matrici
+MOD			= RESTO				##	Restituisce il resto della divisione
+MROUND			= ARROTONDA.MULTIPLO		##	Restituisce un numero arrotondato al multiplo desiderato
+MULTINOMIAL		= MULTINOMIALE			##	Restituisce il multinomiale di un insieme di numeri
+ODD			= DISPARI			##	Arrotonda un numero per eccesso al più vicino intero dispari
+PI			= PI.GRECO			##	Restituisce il valore di pi greco
+POWER			= POTENZA			##	Restituisce il risultato di un numero elevato a potenza
+PRODUCT			= PRODOTTO			##	Moltiplica i suoi argomenti
+QUOTIENT		= QUOZIENTE			##	Restituisce la parte intera di una divisione
+RADIANS			= RADIANTI			##	Converte i gradi in radianti
+RAND			= CASUALE			##	Restituisce un numero casuale compreso tra 0 e 1
+RANDBETWEEN		= CASUALE.TRA			##	Restituisce un numero casuale compreso tra i numeri specificati
+ROMAN			= ROMANO			##	Restituisce il numero come numero romano sotto forma di testo
+ROUND			= ARROTONDA			##	Arrotonda il numero al numero di cifre specificato
+ROUNDDOWN		= ARROTONDA.PER.DIF		##	Arrotonda il valore assoluto di un numero per difetto
+ROUNDUP			= ARROTONDA.PER.ECC		##	Arrotonda il valore assoluto di un numero per eccesso
+SERIESSUM		= SOMMA.SERIE			##	Restituisce la somma di una serie di potenze in base alla formula
+SIGN			= SEGNO				##	Restituisce il segno di un numero
+SIN			= SEN				##	Restituisce il seno di un dato angolo
+SINH			= SENH				##	Restituisce il seno iperbolico di un numero
+SQRT			= RADQ				##	Restituisce una radice quadrata
+SQRTPI			= RADQ.PI.GRECO			##	Restituisce la radice quadrata di un numero (numero * pi greco)
+SUBTOTAL		= SUBTOTALE			##	Restituisce un subtotale in un elenco o in un database
+SUM			= SOMMA				##	Somma i suoi argomenti
+SUMIF			= SOMMA.SE			##	Somma le celle specificate da un dato criterio
+SUMIFS			= SOMMA.PIÙ.SE			##	Somma le celle in un intervallo che soddisfano più criteri
+SUMPRODUCT		= MATR.SOMMA.PRODOTTO		##	Restituisce la somma dei prodotti dei componenti corrispondenti della matrice
+SUMSQ			= SOMMA.Q			##	Restituisce la somma dei quadrati degli argomenti
+SUMX2MY2		= SOMMA.DIFF.Q			##	Restituisce la somma della differenza dei quadrati dei corrispondenti elementi in due matrici
+SUMX2PY2		= SOMMA.SOMMA.Q			##	Restituisce la somma della somma dei quadrati dei corrispondenti elementi in due matrici
+SUMXMY2			= SOMMA.Q.DIFF			##	Restituisce la somma dei quadrati delle differenze dei corrispondenti elementi in due matrici
+TAN			= TAN				##	Restituisce la tangente di un numero
+TANH			= TANH				##	Restituisce la tangente iperbolica di un numero
+TRUNC			= TRONCA			##	Tronca la parte decimale di un numero
+
+
+##
+##	Statistical functions				Funzioni statistiche
+##
+AVEDEV			= MEDIA.DEV			##	Restituisce la media delle deviazioni assolute delle coordinate rispetto alla loro media
+AVERAGE			= MEDIA				##	Restituisce la media degli argomenti
+AVERAGEA		= MEDIA.VALORI			##	Restituisce la media degli argomenti, inclusi i numeri, il testo e i valori logici
+AVERAGEIF		= MEDIA.SE			##	Restituisce la media aritmetica di tutte le celle in un intervallo che soddisfano un determinato criterio
+AVERAGEIFS		= MEDIA.PIÙ.SE			##	Restituisce la media aritmetica di tutte le celle che soddisfano più criteri
+BETADIST		= DISTRIB.BETA			##	Restituisce la funzione di distribuzione cumulativa beta
+BETAINV			= INV.BETA			##	Restituisce l'inverso della funzione di distribuzione cumulativa per una distribuzione beta specificata
+BINOMDIST		= DISTRIB.BINOM			##	Restituisce la distribuzione binomiale per il termine individuale
+CHIDIST			= DISTRIB.CHI			##	Restituisce la probabilità a una coda per la distribuzione del chi quadrato
+CHIINV			= INV.CHI			##	Restituisce l'inverso della probabilità ad una coda per la distribuzione del chi quadrato
+CHITEST			= TEST.CHI			##	Restituisce il test per l'indipendenza
+CONFIDENCE		= CONFIDENZA			##	Restituisce l'intervallo di confidenza per una popolazione
+CORREL			= CORRELAZIONE			##	Restituisce il coefficiente di correlazione tra due insiemi di dati
+COUNT			= CONTA.NUMERI			##	Conta la quantità di numeri nell'elenco di argomenti
+COUNTA			= CONTA.VALORI			##	Conta il numero di valori nell'elenco di argomenti
+COUNTBLANK		= CONTA.VUOTE			##	Conta il numero di celle vuote all'interno di un intervallo
+COUNTIF			= CONTA.SE			##	Conta il numero di celle all'interno di un intervallo che soddisfa i criteri specificati
+COUNTIFS		= CONTA.PIÙ.SE			##	Conta il numero di celle in un intervallo che soddisfano più criteri.
+COVAR			= COVARIANZA			##	Calcola la covarianza, la media dei prodotti delle deviazioni accoppiate
+CRITBINOM		= CRIT.BINOM			##	Restituisce il più piccolo valore per il quale la distribuzione cumulativa binomiale risulta maggiore o uguale ad un valore di criterio
+DEVSQ			= DEV.Q				##	Restituisce la somma dei quadrati delle deviazioni
+EXPONDIST		= DISTRIB.EXP			##	Restituisce la distribuzione esponenziale
+FDIST			= DISTRIB.F			##	Restituisce la distribuzione di probabilità F
+FINV			= INV.F				##	Restituisce l'inverso della distribuzione della probabilità F
+FISHER			= FISHER			##	Restituisce la trasformazione di Fisher
+FISHERINV		= INV.FISHER			##	Restituisce l'inverso della trasformazione di Fisher
+FORECAST		= PREVISIONE			##	Restituisce i valori lungo una tendenza lineare
+FREQUENCY		= FREQUENZA			##	Restituisce la distribuzione di frequenza come matrice verticale
+FTEST			= TEST.F			##	Restituisce il risultato di un test F
+GAMMADIST		= DISTRIB.GAMMA			##	Restituisce la distribuzione gamma
+GAMMAINV		= INV.GAMMA			##	Restituisce l'inverso della distribuzione cumulativa gamma
+GAMMALN			= LN.GAMMA			##	Restituisce il logaritmo naturale della funzione gamma, G(x)
+GEOMEAN			= MEDIA.GEOMETRICA		##	Restituisce la media geometrica
+GROWTH			= CRESCITA			##	Restituisce i valori lungo una linea di tendenza esponenziale
+HARMEAN			= MEDIA.ARMONICA		##	Restituisce la media armonica
+HYPGEOMDIST		= DISTRIB.IPERGEOM		##	Restituisce la distribuzione ipergeometrica
+INTERCEPT		= INTERCETTA			##	Restituisce l'intercetta della retta di regressione lineare
+KURT			= CURTOSI			##	Restituisce la curtosi di un insieme di dati
+LARGE			= GRANDE			##	Restituisce il k-esimo valore più grande in un insieme di dati
+LINEST			= REGR.LIN			##	Restituisce i parametri di una tendenza lineare
+LOGEST			= REGR.LOG			##	Restituisce i parametri di una linea di tendenza esponenziale
+LOGINV			= INV.LOGNORM			##	Restituisce l'inverso di una distribuzione lognormale
+LOGNORMDIST		= DISTRIB.LOGNORM		##	Restituisce la distribuzione lognormale cumulativa
+MAX			= MAX				##	Restituisce il valore massimo in un elenco di argomenti
+MAXA			= MAX.VALORI			##	Restituisce il valore massimo in un elenco di argomenti, inclusi i numeri, il testo e i valori logici
+MEDIAN			= MEDIANA			##	Restituisce la mediana dei numeri specificati
+MIN			= MIN				##	Restituisce il valore minimo in un elenco di argomenti
+MINA			= MIN.VALORI			##	Restituisce il più piccolo valore in un elenco di argomenti, inclusi i numeri, il testo e i valori logici
+MODE			= MODA				##	Restituisce il valore più comune in un insieme di dati
+NEGBINOMDIST		= DISTRIB.BINOM.NEG		##	Restituisce la distribuzione binomiale negativa
+NORMDIST		= DISTRIB.NORM			##	Restituisce la distribuzione cumulativa normale
+NORMINV			= INV.NORM			##	Restituisce l'inverso della distribuzione cumulativa normale standard
+NORMSDIST		= DISTRIB.NORM.ST		##	Restituisce la distribuzione cumulativa normale standard
+NORMSINV		= INV.NORM.ST			##	Restituisce l'inverso della distribuzione cumulativa normale
+PEARSON			= PEARSON			##	Restituisce il coefficiente del momento di correlazione di Pearson
+PERCENTILE		= PERCENTILE			##	Restituisce il k-esimo dato percentile di valori in un intervallo
+PERCENTRANK		= PERCENT.RANGO			##	Restituisce il rango di un valore in un insieme di dati come percentuale
+PERMUT			= PERMUTAZIONE			##	Restituisce il numero delle permutazioni per un determinato numero di oggetti
+POISSON			= POISSON			##	Restituisce la distribuzione di Poisson
+PROB			= PROBABILITÀ			##	Calcola la probabilità che dei valori in un intervallo siano compresi tra due limiti
+QUARTILE		= QUARTILE			##	Restituisce il quartile di un insieme di dati
+RANK			= RANGO				##	Restituisce il rango di un numero in un elenco di numeri
+RSQ			= RQ				##	Restituisce la radice quadrata del coefficiente di momento di correlazione di Pearson
+SKEW			= ASIMMETRIA			##	Restituisce il grado di asimmetria di una distribuzione
+SLOPE			= PENDENZA			##	Restituisce la pendenza di una retta di regressione lineare
+SMALL			= PICCOLO			##	Restituisce il k-esimo valore più piccolo in un insieme di dati
+STANDARDIZE		= NORMALIZZA			##	Restituisce un valore normalizzato
+STDEV			= DEV.ST			##	Restituisce una stima della deviazione standard sulla base di un campione
+STDEVA			= DEV.ST.VALORI			##	Restituisce una stima della deviazione standard sulla base di un campione, inclusi i numeri, il testo e i valori logici
+STDEVP			= DEV.ST.POP			##	Calcola la deviazione standard sulla base di un'intera popolazione
+STDEVPA			= DEV.ST.POP.VALORI		##	Calcola la deviazione standard sulla base sull'intera popolazione, inclusi i numeri, il testo e i valori logici
+STEYX			= ERR.STD.YX			##	Restituisce l'errore standard del valore previsto per y per ogni valore x nella regressione
+TDIST			= DISTRIB.T			##	Restituisce la distribuzione t di Student
+TINV			= INV.T				##	Restituisce l'inversa della distribuzione t di Student
+TREND			= TENDENZA			##	Restituisce i valori lungo una linea di tendenza lineare
+TRIMMEAN		= MEDIA.TRONCATA		##	Restituisce la media della parte interna di un insieme di dati
+TTEST			= TEST.T			##	Restituisce la probabilità associata ad un test t di Student
+VAR			= VAR				##	Stima la varianza sulla base di un campione
+VARA			= VAR.VALORI			##	Stima la varianza sulla base di un campione, inclusi i numeri, il testo e i valori logici
+VARP			= VAR.POP			##	Calcola la varianza sulla base dell'intera popolazione
+VARPA			= VAR.POP.VALORI		##	Calcola la deviazione standard sulla base sull'intera popolazione, inclusi i numeri, il testo e i valori logici
+WEIBULL			= WEIBULL			##	Restituisce la distribuzione di Weibull
+ZTEST			= TEST.Z			##	Restituisce il valore di probabilità a una coda per un test z
+
+
+##
+##	Text functions					Funzioni di testo
+##
+ASC			= ASC				##	Modifica le lettere inglesi o il katakana a doppio byte all'interno di una stringa di caratteri in caratteri a singolo byte
+BAHTTEXT		= BAHTTESTO			##	Converte un numero in testo, utilizzando il formato valuta ß (baht)
+CHAR			= CODICE.CARATT			##	Restituisce il carattere specificato dal numero di codice
+CLEAN			= LIBERA			##	Elimina dal testo tutti i caratteri che non è possibile stampare
+CODE			= CODICE			##	Restituisce il codice numerico del primo carattere di una stringa di testo
+CONCATENATE		= CONCATENA			##	Unisce diversi elementi di testo in un unico elemento di testo
+DOLLAR			= VALUTA			##	Converte un numero in testo, utilizzando il formato valuta € (euro)
+EXACT			= IDENTICO			##	Verifica se due valori di testo sono uguali
+FIND			= TROVA				##	Rileva un valore di testo all'interno di un altro (distinzione tra maiuscole e minuscole)
+FINDB			= TROVA.B			##	Rileva un valore di testo all'interno di un altro (distinzione tra maiuscole e minuscole)
+FIXED			= FISSO				##	Formatta un numero come testo con un numero fisso di decimali
+JIS			= ORDINAMENTO.JIS		##	Modifica le lettere inglesi o i caratteri katakana a byte singolo all'interno di una stringa di caratteri in caratteri a byte doppio.
+LEFT			= SINISTRA			##	Restituisce il carattere più a sinistra di un valore di testo
+LEFTB			= SINISTRA.B			##	Restituisce il carattere più a sinistra di un valore di testo
+LEN			= LUNGHEZZA			##	Restituisce il numero di caratteri di una stringa di testo
+LENB			= LUNB				##	Restituisce il numero di caratteri di una stringa di testo
+LOWER			= MINUSC			##	Converte il testo in lettere minuscole
+MID			= MEDIA				##	Restituisce un numero specifico di caratteri di una stringa di testo a partire dalla posizione specificata
+MIDB			= MEDIA.B			##	Restituisce un numero specifico di caratteri di una stringa di testo a partire dalla posizione specificata
+PHONETIC		= FURIGANA			##	Estrae i caratteri fonetici (furigana) da una stringa di testo.
+PROPER			= MAIUSC.INIZ			##	Converte in maiuscolo la prima lettera di ogni parola di un valore di testo
+REPLACE			= RIMPIAZZA			##	Sostituisce i caratteri all'interno di un testo
+REPLACEB		= SOSTITUISCI.B			##	Sostituisce i caratteri all'interno di un testo
+REPT			= RIPETI			##	Ripete un testo per un dato numero di volte
+RIGHT			= DESTRA			##	Restituisce il carattere più a destra di un valore di testo
+RIGHTB			= DESTRA.B			##	Restituisce il carattere più a destra di un valore di testo
+SEARCH			= RICERCA			##	Rileva un valore di testo all'interno di un altro (non è sensibile alle maiuscole e minuscole)
+SEARCHB			= CERCA.B			##	Rileva un valore di testo all'interno di un altro (non è sensibile alle maiuscole e minuscole)
+SUBSTITUTE		= SOSTITUISCI			##	Sostituisce il nuovo testo al testo contenuto in una stringa
+T			= T				##	Converte gli argomenti in testo
+TEXT			= TESTO				##	Formatta un numero e lo converte in testo
+TRIM			= ANNULLA.SPAZI			##	Elimina gli spazi dal testo
+UPPER			= MAIUSC			##	Converte il testo in lettere maiuscole
+VALUE			= VALORE			##	Converte un argomento di testo in numero
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/nl/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/nl/config
new file mode 100755
index 0000000..8376022
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/nl/config
@@ -0,0 +1,24 @@
+##
+## PhpSpreadsheet
+##
+
+ArgumentSeparator	= ;
+
+
+##
+##	(For future use)
+##
+currencySymbol	= €
+
+
+##
+##	Excel Error Codes	(For future use)
+
+##
+NULL	= #LEEG!
+DIV0	= #DEEL/0!
+VALUE	= #WAARDE!
+REF	= #VERW!
+NAME	= #NAAM?
+NUM	= #GETAL!
+NA	= #N/B
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/nl/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/nl/functions
new file mode 100755
index 0000000..2518f42
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/nl/functions
@@ -0,0 +1,416 @@
+##
+## PhpSpreadsheet
+##
+## Data in this file derived from https://www.excel-function-translation.com/
+##
+##
+
+
+##
+##	Add-in and Automation functions			Automatiseringsfuncties en functies in invoegtoepassingen
+##
+GETPIVOTDATA		= DRAAITABEL.OPHALEN		##	Geeft gegevens uit een draaitabelrapport als resultaat
+
+
+##
+##	Cube functions					Kubusfuncties
+##
+CUBEKPIMEMBER		= KUBUSKPILID			##	Retourneert de naam, eigenschap en waarde van een KPI (prestatie-indicator) en geeft de naam en de eigenschap in de cel weer. Een KPI is een meetbare waarde, zoals de maandelijkse brutowinst of de omzet per kwartaal per werknemer, die wordt gebruikt om de prestaties van een organisatie te bewaken
+CUBEMEMBER		= KUBUSLID			##	Retourneert een lid of tupel in een kubushiërarchie. Wordt gebruikt om te controleren of het lid of de tupel in de kubus aanwezig is
+CUBEMEMBERPROPERTY	= KUBUSLIDEIGENSCHAP		##	Retourneert de waarde van een lideigenschap in de kubus. Wordt gebruikt om te controleren of de lidnaam in de kubus bestaat en retourneert de opgegeven eigenschap voor dit lid
+CUBERANKEDMEMBER	= KUBUSGERANGCHIKTLID		##	Retourneert het zoveelste, gerangschikte lid in een set. Wordt gebruikt om een of meer elementen in een set te retourneren, zoals de tien beste verkopers of de tien beste studenten
+CUBESET			= KUBUSSET			##	Definieert een berekende set leden of tupels door een ingestelde expressie naar de kubus op de server te sturen, alwaar de set wordt gemaakt en vervolgens wordt geretourneerd naar Microsoft Office Excel
+CUBESETCOUNT		= KUBUSSETAANTAL		##	Retourneert het aantal onderdelen in een set
+CUBEVALUE		= KUBUSWAARDE			##	Retourneert een samengestelde waarde van een kubus
+
+
+##
+##	Database functions				Databasefuncties
+##
+DAVERAGE		= DBGEMIDDELDE			##	Berekent de gemiddelde waarde in geselecteerde databasegegevens
+DCOUNT			= DBAANTAL			##	Telt de cellen met getallen in een database
+DCOUNTA			= DBAANTALC			##	Telt de niet-lege cellen in een database
+DGET			= DBLEZEN			##	Retourneert één record dat voldoet aan de opgegeven criteria uit een database
+DMAX			= DBMAX				##	Retourneert de maximumwaarde in de geselecteerde databasegegevens
+DMIN			= DBMIN				##	Retourneert de minimumwaarde in de geselecteerde databasegegevens
+DPRODUCT		= DBPRODUCT			##	Vermenigvuldigt de waarden in een bepaald veld van de records die voldoen aan de criteria in een database
+DSTDEV			= DBSTDEV			##	Maakt een schatting van de standaarddeviatie op basis van een steekproef uit geselecteerde databasegegevens
+DSTDEVP			= DBSTDEVP			##	Berekent de standaarddeviatie op basis van de volledige populatie van geselecteerde databasegegevens
+DSUM			= DBSOM				##	Telt de getallen uit een kolom records in de database op die voldoen aan de criteria
+DVAR			= DBVAR				##	Maakt een schatting van de variantie op basis van een steekproef uit geselecteerde databasegegevens
+DVARP			= DBVARP			##	Berekent de variantie op basis van de volledige populatie van geselecteerde databasegegevens
+
+
+##
+##	Date and time functions				Datum- en tijdfuncties
+##
+DATE			= DATUM				##	Geeft als resultaat het seriële getal van een opgegeven datum
+DATEVALUE		= DATUMWAARDE			##	Converteert een datum in de vorm van tekst naar een serieel getal
+DAY			= DAG				##	Converteert een serieel getal naar een dag van de maand
+DAYS360			= DAGEN360			##	Berekent het aantal dagen tussen twee datums op basis van een jaar met 360 dagen
+EDATE			= ZELFDE.DAG			##	Geeft als resultaat het seriële getal van een datum die het opgegeven aantal maanden voor of na de begindatum ligt
+EOMONTH			= LAATSTE.DAG			##	Geeft als resultaat het seriële getal van de laatste dag van de maand voor of na het opgegeven aantal maanden
+HOUR			= UUR				##	Converteert een serieel getal naar uren
+MINUTE			= MINUUT			##	Converteert een serieel naar getal minuten
+MONTH			= MAAND				##	Converteert een serieel getal naar een maand
+NETWORKDAYS		= NETTO.WERKDAGEN		##	Geeft als resultaat het aantal hele werkdagen tussen twee datums
+NOW			= NU				##	Geeft als resultaat het seriële getal van de huidige datum en tijd
+SECOND			= SECONDE			##	Converteert een serieel getal naar seconden
+TIME			= TIJD				##	Geeft als resultaat het seriële getal van een bepaald tijdstip
+TIMEVALUE		= TIJDWAARDE			##	Converteert de tijd in de vorm van tekst naar een serieel getal
+TODAY			= VANDAAG			##	Geeft als resultaat het seriële getal van de huidige datum
+WEEKDAY			= WEEKDAG			##	Converteert een serieel getal naar een weekdag
+WEEKNUM			= WEEKNUMMER			##	Converteert een serieel getal naar een weeknummer
+WORKDAY			= WERKDAG			##	Geeft als resultaat het seriële getal van de datum voor of na een bepaald aantal werkdagen
+YEAR			= JAAR				##	Converteert een serieel getal naar een jaar
+YEARFRAC		= JAAR.DEEL			##	Geeft als resultaat het gedeelte van het jaar, uitgedrukt in het aantal hele dagen tussen begindatum en einddatum
+
+
+##
+##	Engineering functions				Technische functies
+##
+BESSELI			= BESSEL.Y			##	Geeft als resultaat de gewijzigde Bessel-functie In(x)
+BESSELJ			= BESSEL.J			##	Geeft als resultaat de Bessel-functie Jn(x)
+BESSELK			= BESSEL.K			##	Geeft als resultaat de gewijzigde Bessel-functie Kn(x)
+BESSELY			= BESSEL.Y			##	Geeft als resultaat de gewijzigde Bessel-functie Yn(x)
+BIN2DEC			= BIN.N.DEC			##	Converteert een binair getal naar een decimaal getal
+BIN2HEX			= BIN.N.HEX			##	Converteert een binair getal naar een hexadecimaal getal
+BIN2OCT			= BIN.N.OCT			##	Converteert een binair getal naar een octaal getal
+COMPLEX			= COMPLEX			##	Converteert reële en imaginaire coëfficiënten naar een complex getal
+CONVERT			= CONVERTEREN			##	Converteert een getal in de ene maateenheid naar een getal in een andere maateenheid
+DEC2BIN			= DEC.N.BIN			##	Converteert een decimaal getal naar een binair getal
+DEC2HEX			= DEC.N.HEX			##	Converteert een decimaal getal naar een hexadecimaal getal
+DEC2OCT			= DEC.N.OCT			##	Converteert een decimaal getal naar een octaal getal
+DELTA			= DELTA				##	Test of twee waarden gelijk zijn
+ERF			= FOUTFUNCTIE			##	Geeft als resultaat de foutfunctie
+ERFC			= FOUT.COMPLEMENT		##	Geeft als resultaat de complementaire foutfunctie
+GESTEP			= GROTER.DAN			##	Test of een getal groter is dan de drempelwaarde
+HEX2BIN			= HEX.N.BIN			##	Converteert een hexadecimaal getal naar een binair getal
+HEX2DEC			= HEX.N.DEC			##	Converteert een hexadecimaal getal naar een decimaal getal
+HEX2OCT			= HEX.N.OCT			##	Converteert een hexadecimaal getal naar een octaal getal
+IMABS			= C.ABS				##	Geeft als resultaat de absolute waarde (modulus) van een complex getal
+IMAGINARY		= C.IM.DEEL			##	Geeft als resultaat de imaginaire coëfficiënt van een complex getal
+IMARGUMENT		= C.ARGUMENT			##	Geeft als resultaat het argument thèta, een hoek uitgedrukt in radialen
+IMCONJUGATE		= C.TOEGEVOEGD			##	Geeft als resultaat het complexe toegevoegde getal van een complex getal
+IMCOS			= C.COS				##	Geeft als resultaat de cosinus van een complex getal
+IMDIV			= C.QUOTIENT			##	Geeft als resultaat het quotiënt van twee complexe getallen
+IMEXP			= C.EXP				##	Geeft als resultaat de exponent van een complex getal
+IMLN			= C.LN				##	Geeft als resultaat de natuurlijke logaritme van een complex getal
+IMLOG10			= C.LOG10			##	Geeft als resultaat de logaritme met grondtal 10 van een complex getal
+IMLOG2			= C.LOG2			##	Geeft als resultaat de logaritme met grondtal 2 van een complex getal
+IMPOWER			= C.MACHT			##	Geeft als resultaat een complex getal dat is verheven tot de macht van een geheel getal
+IMPRODUCT		= C.PRODUCT			##	Geeft als resultaat het product van complexe getallen
+IMREAL			= C.REEEL.DEEL			##	Geeft als resultaat de reële coëfficiënt van een complex getal
+IMSIN			= C.SIN				##	Geeft als resultaat de sinus van een complex getal
+IMSQRT			= C.WORTEL			##	Geeft als resultaat de vierkantswortel van een complex getal
+IMSUB			= C.VERSCHIL			##	Geeft als resultaat het verschil tussen twee complexe getallen
+IMSUM			= C.SOM				##	Geeft als resultaat de som van complexe getallen
+OCT2BIN			= OCT.N.BIN			##	Converteert een octaal getal naar een binair getal
+OCT2DEC			= OCT.N.DEC			##	Converteert een octaal getal naar een decimaal getal
+OCT2HEX			= OCT.N.HEX			##	Converteert een octaal getal naar een hexadecimaal getal
+
+
+##
+##	Financial functions				Financiële functies
+##
+ACCRINT			= SAMENG.RENTE			##	Berekent de opgelopen rente voor een waardepapier waarvan de rente periodiek wordt uitgekeerd
+ACCRINTM		= SAMENG.RENTE.V		##	Berekent de opgelopen rente voor een waardepapier waarvan de rente op de vervaldatum wordt uitgekeerd
+AMORDEGRC		= AMORDEGRC			##	Geeft als resultaat de afschrijving voor elke boekingsperiode door een afschrijvingscoëfficiënt toe te passen
+AMORLINC		= AMORLINC			##	Berekent de afschrijving voor elke boekingsperiode
+COUPDAYBS		= COUP.DAGEN.BB			##	Berekent het aantal dagen vanaf het begin van de coupontermijn tot de stortingsdatum
+COUPDAYS		= COUP.DAGEN			##	Geeft als resultaat het aantal dagen in de coupontermijn waarin de stortingsdatum valt
+COUPDAYSNC		= COUP.DAGEN.VV			##	Geeft als resultaat het aantal dagen vanaf de stortingsdatum tot de volgende couponvervaldatum
+COUPNCD			= COUP.DATUM.NB			##	Geeft als resultaat de volgende coupondatum na de stortingsdatum
+COUPNUM			= COUP.AANTAL			##	Geeft als resultaat het aantal coupons dat nog moet worden uitbetaald tussen de stortingsdatum en de vervaldatum
+COUPPCD			= COUP.DATUM.VB			##	Geeft als resultaat de vorige couponvervaldatum vóór de stortingsdatum
+CUMIPMT			= CUM.RENTE			##	Geeft als resultaat de cumulatieve rente die tussen twee termijnen is uitgekeerd
+CUMPRINC		= CUM.HOOFDSOM			##	Geeft als resultaat de cumulatieve hoofdsom van een lening die tussen twee termijnen is terugbetaald
+DB			= DB				##	Geeft als resultaat de afschrijving van activa voor een bepaalde periode met behulp van de 'fixed declining balance'-methode
+DDB			= DDB				##	Geeft als resultaat de afschrijving van activa over een bepaalde termijn met behulp van de 'double declining balance'-methode of een andere methode die u opgeeft
+DISC			= DISCONTO			##	Geeft als resultaat het discontopercentage voor een waardepapier
+DOLLARDE		= EURO.DE			##	Converteert een prijs in euro's, uitgedrukt in een breuk, naar een prijs in euro's, uitgedrukt in een decimaal getal
+DOLLARFR		= EURO.BR			##	Converteert een prijs in euro's, uitgedrukt in een decimaal getal, naar een prijs in euro's, uitgedrukt in een breuk
+DURATION		= DUUR				##	Geeft als resultaat de gewogen gemiddelde looptijd voor een waardepapier met periodieke rentebetalingen
+EFFECT			= EFFECT.RENTE			##	Geeft als resultaat het effectieve jaarlijkse rentepercentage
+FV			= TW				##	Geeft als resultaat de toekomstige waarde van een investering
+FVSCHEDULE		= TOEK.WAARDE2			##	Geeft als resultaat de toekomstige waarde van een bepaalde hoofdsom na het toepassen van een reeks samengestelde rentepercentages
+INTRATE			= RENTEPERCENTAGE		##	Geeft als resultaat het rentepercentage voor een volgestort waardepapier
+IPMT			= IBET				##	Geeft als resultaat de te betalen rente voor een investering over een bepaalde termijn
+IRR			= IR				##	Geeft als resultaat de interne rentabiliteit voor een reeks cashflows
+ISPMT			= ISBET				##	Geeft als resultaat de rente die is betaald tijdens een bepaalde termijn van een investering
+MDURATION		= AANG.DUUR			##	Geeft als resultaat de aangepaste Macauley-looptijd voor een waardepapier, aangenomen dat de nominale waarde € 100 bedraagt
+MIRR			= GIR				##	Geeft als resultaat de interne rentabiliteit voor een serie cashflows, waarbij voor betalingen een ander rentepercentage geldt dan voor inkomsten
+NOMINAL			= NOMINALE.RENTE		##	Geeft als resultaat het nominale jaarlijkse rentepercentage
+NPER			= NPER				##	Geeft als resultaat het aantal termijnen van een investering
+NPV			= NHW				##	Geeft als resultaat de netto huidige waarde van een investering op basis van een reeks periodieke cashflows en een discontopercentage
+ODDFPRICE		= AFW.ET.PRIJS			##	Geeft als resultaat de prijs per € 100 nominale waarde voor een waardepapier met een afwijkende eerste termijn
+ODDFYIELD		= AFW.ET.REND			##	Geeft als resultaat het rendement voor een waardepapier met een afwijkende eerste termijn
+ODDLPRICE		= AFW.LT.PRIJS			##	Geeft als resultaat de prijs per € 100 nominale waarde voor een waardepapier met een afwijkende laatste termijn
+ODDLYIELD		= AFW.LT.REND			##	Geeft als resultaat het rendement voor een waardepapier met een afwijkende laatste termijn
+PMT			= BET				##	Geeft als resultaat de periodieke betaling voor een annuïteit
+PPMT			= PBET				##	Geeft als resultaat de afbetaling op de hoofdsom voor een bepaalde termijn
+PRICE			= PRIJS.NOM			##	Geeft als resultaat de prijs per € 100 nominale waarde voor een waardepapier waarvan de rente periodiek wordt uitgekeerd
+PRICEDISC		= PRIJS.DISCONTO		##	Geeft als resultaat de prijs per € 100 nominale waarde voor een verdisconteerd waardepapier
+PRICEMAT		= PRIJS.VERVALDAG		##	Geeft als resultaat de prijs per € 100 nominale waarde voor een waardepapier waarvan de rente wordt uitgekeerd op de vervaldatum
+PV			= HW				##	Geeft als resultaat de huidige waarde van een investering
+RATE			= RENTE				##	Geeft als resultaat het periodieke rentepercentage voor een annuïteit
+RECEIVED		= OPBRENGST			##	Geeft als resultaat het bedrag dat op de vervaldatum wordt uitgekeerd voor een volgestort waardepapier
+SLN			= LIN.AFSCHR			##	Geeft als resultaat de lineaire afschrijving van activa over één termijn
+SYD			= SYD				##	Geeft als resultaat de afschrijving van activa over een bepaalde termijn met behulp van de 'Sum-Of-Years-Digits'-methode
+TBILLEQ			= SCHATK.OBL			##	Geeft als resultaat het rendement op schatkistpapier, dat op dezelfde manier wordt berekend als het rendement op obligaties
+TBILLPRICE		= SCHATK.PRIJS			##	Bepaalt de prijs per € 100 nominale waarde voor schatkistpapier
+TBILLYIELD		= SCHATK.REND			##	Berekent het rendement voor schatkistpapier
+VDB			= VDB				##	Geeft als resultaat de afschrijving van activa over een gehele of gedeeltelijke termijn met behulp van de 'declining balance'-methode
+XIRR			= IR.SCHEMA			##	Berekent de interne rentabiliteit voor een betalingsschema van cashflows
+XNPV			= NHW2				##	Berekent de huidige nettowaarde voor een betalingsschema van cashflows
+YIELD			= RENDEMENT			##	Geeft als resultaat het rendement voor een waardepapier waarvan de rente periodiek wordt uitgekeerd
+YIELDDISC		= REND.DISCONTO			##	Geeft als resultaat het jaarlijkse rendement voor een verdisconteerd waardepapier, bijvoorbeeld schatkistpapier
+YIELDMAT		= REND.VERVAL			##	Geeft als resultaat het jaarlijkse rendement voor een waardepapier waarvan de rente wordt uitgekeerd op de vervaldatum
+
+
+##
+##	Information functions				Informatiefuncties
+##
+CELL			= CEL				##	Geeft als resultaat informatie over de opmaak, locatie of inhoud van een cel
+ERROR.TYPE		= TYPE.FOUT			##	Geeft als resultaat een getal dat overeenkomt met een van de foutwaarden van Microsoft Excel
+INFO			= INFO				##	Geeft als resultaat informatie over de huidige besturingsomgeving
+ISBLANK			= ISLEEG			##	Geeft als resultaat WAAR als de waarde leeg is
+ISERR			= ISFOUT2			##	Geeft als resultaat WAAR als de waarde een foutwaarde is, met uitzondering van #N/B
+ISERROR			= ISFOUT			##	Geeft als resultaat WAAR als de waarde een foutwaarde is
+ISEVEN			= IS.EVEN			##	Geeft als resultaat WAAR als het getal even is
+ISLOGICAL		= ISLOGISCH			##	Geeft als resultaat WAAR als de waarde een logische waarde is
+ISNA			= ISNB				##	Geeft als resultaat WAAR als de waarde de foutwaarde #N/B is
+ISNONTEXT		= ISGEENTEKST			##	Geeft als resultaat WAAR als de waarde geen tekst is
+ISNUMBER		= ISGETAL			##	Geeft als resultaat WAAR als de waarde een getal is
+ISODD			= IS.ONEVEN			##	Geeft als resultaat WAAR als het getal oneven is
+ISREF			= ISVERWIJZING			##	Geeft als resultaat WAAR als de waarde een verwijzing is
+ISTEXT			= ISTEKST			##	Geeft als resultaat WAAR als de waarde tekst is
+N			= N				##	Geeft als resultaat een waarde die is geconverteerd naar een getal
+NA			= NB				##	Geeft als resultaat de foutwaarde #N/B
+TYPE			= TYPE				##	Geeft als resultaat een getal dat het gegevenstype van een waarde aangeeft
+
+
+##
+##	Logical functions				Logische functies
+##
+AND			= EN				##	Geeft als resultaat WAAR als alle argumenten WAAR zijn
+FALSE			= ONWAAR			##	Geeft als resultaat de logische waarde ONWAAR
+IF			= ALS				##	Geeft een logische test aan
+IFERROR			= ALS.FOUT			##	Retourneert een waarde die u opgeeft als een formule een fout oplevert, anders wordt het resultaat van de formule geretourneerd
+NOT			= NIET				##	Keert de logische waarde van het argument om
+OR			= OF				##	Geeft als resultaat WAAR als minimaal een van de argumenten WAAR is
+TRUE			= WAAR				##	Geeft als resultaat de logische waarde WAAR
+
+
+##
+##	Lookup and reference functions			Zoek- en verwijzingsfuncties
+##
+ADDRESS			= ADRES				##	Geeft als resultaat een verwijzing, in de vorm van tekst, naar één bepaalde cel in een werkblad
+AREAS			= BEREIKEN			##	Geeft als resultaat het aantal bereiken in een verwijzing
+CHOOSE			= KIEZEN			##	Kiest een waarde uit een lijst met waarden
+COLUMN			= KOLOM				##	Geeft als resultaat het kolomnummer van een verwijzing
+COLUMNS			= KOLOMMEN			##	Geeft als resultaat het aantal kolommen in een verwijzing
+HLOOKUP			= HORIZ.ZOEKEN			##	Zoekt in de bovenste rij van een matrix naar een bepaalde waarde en geeft als resultaat de gevonden waarde in de opgegeven cel
+HYPERLINK		= HYPERLINK			##	Maakt een snelkoppeling of een sprong waarmee een document wordt geopend dat is opgeslagen op een netwerkserver, een intranet of op internet
+INDEX			= INDEX				##	Kiest met een index een waarde uit een verwijzing of een matrix
+INDIRECT		= INDIRECT			##	Geeft als resultaat een verwijzing die wordt aangegeven met een tekstwaarde
+LOOKUP			= ZOEKEN			##	Zoekt naar bepaalde waarden in een vector of een matrix
+MATCH			= VERGELIJKEN			##	Zoekt naar bepaalde waarden in een verwijzing of een matrix
+OFFSET			= VERSCHUIVING			##	Geeft als resultaat een nieuwe verwijzing die is verschoven ten opzichte van een bepaalde verwijzing
+ROW			= RIJ				##	Geeft als resultaat het rijnummer van een verwijzing
+ROWS			= RIJEN				##	Geeft als resultaat het aantal rijen in een verwijzing
+RTD			= RTG				##	Haalt realtimegegevens op uit een programma dat COM-automatisering (automatisering: een methode waarmee de ene toepassing objecten van een andere toepassing of ontwikkelprogramma kan besturen. Automatisering werd vroeger OLE-automatisering genoemd. Automatisering is een industrienorm die deel uitmaakt van het Component Object Model (COM).) ondersteunt
+TRANSPOSE		= TRANSPONEREN			##	Geeft als resultaat de getransponeerde van een matrix
+VLOOKUP			= VERT.ZOEKEN			##	Zoekt in de meest linkse kolom van een matrix naar een bepaalde waarde en geeft als resultaat de waarde in de opgegeven cel
+
+
+##
+##	Math and trigonometry functions			Wiskundige en trigonometrische functies
+##
+ABS			= ABS				##	Geeft als resultaat de absolute waarde van een getal
+ACOS			= BOOGCOS			##	Geeft als resultaat de boogcosinus van een getal
+ACOSH			= BOOGCOSH			##	Geeft als resultaat de inverse cosinus hyperbolicus van een getal
+ASIN			= BOOGSIN			##	Geeft als resultaat de boogsinus van een getal
+ASINH			= BOOGSINH			##	Geeft als resultaat de inverse sinus hyperbolicus van een getal
+ATAN			= BOOGTAN			##	Geeft als resultaat de boogtangens van een getal
+ATAN2			= BOOGTAN2			##	Geeft als resultaat de boogtangens van de x- en y-coördinaten
+ATANH			= BOOGTANH			##	Geeft als resultaat de inverse tangens hyperbolicus van een getal
+CEILING			= AFRONDEN.BOVEN		##	Rondt de absolute waarde van een getal naar boven af op het dichtstbijzijnde gehele getal of het dichtstbijzijnde significante veelvoud
+COMBIN			= COMBINATIES			##	Geeft als resultaat het aantal combinaties voor een bepaald aantal objecten
+COS			= COS				##	Geeft als resultaat de cosinus van een getal
+COSH			= COSH				##	Geeft als resultaat de cosinus hyperbolicus van een getal
+DEGREES			= GRADEN			##	Converteert radialen naar graden
+EVEN			= EVEN				##	Rondt het getal af op het dichtstbijzijnde gehele even getal
+EXP			= EXP				##	Verheft e tot de macht van een bepaald getal
+FACT			= FACULTEIT			##	Geeft als resultaat de faculteit van een getal
+FACTDOUBLE		= DUBBELE.FACULTEIT		##	Geeft als resultaat de dubbele faculteit van een getal
+FLOOR			= AFRONDEN.BENEDEN		##	Rondt de absolute waarde van een getal naar beneden af
+GCD			= GGD				##	Geeft als resultaat de grootste gemene deler
+INT			= INTEGER			##	Rondt een getal naar beneden af op het dichtstbijzijnde gehele getal
+LCM			= KGV				##	Geeft als resultaat het kleinste gemene veelvoud
+LN			= LN				##	Geeft als resultaat de natuurlijke logaritme van een getal
+LOG			= LOG				##	Geeft als resultaat de logaritme met het opgegeven grondtal van een getal
+LOG10			= LOG10				##	Geeft als resultaat de logaritme met grondtal 10 van een getal
+MDETERM			= DETERMINANTMAT		##	Geeft als resultaat de determinant van een matrix
+MINVERSE		= INVERSEMAT			##	Geeft als resultaat de inverse van een matrix
+MMULT			= PRODUCTMAT			##	Geeft als resultaat het product van twee matrices
+MOD			= REST				##	Geeft als resultaat het restgetal van een deling
+MROUND			= AFRONDEN.N.VEELVOUD		##	Geeft als resultaat een getal afgerond op het gewenste veelvoud
+MULTINOMIAL		= MULTINOMIAAL			##	Geeft als resultaat de multinomiaalcoëfficiënt van een reeks getallen
+ODD			= ONEVEN			##	Rondt de absolute waarde van het getal naar boven af op het dichtstbijzijnde gehele oneven getal
+PI			= PI				##	Geeft als resultaat de waarde van pi
+POWER			= MACHT				##	Verheft een getal tot een macht
+PRODUCT			= PRODUCT			##	Vermenigvuldigt de argumenten met elkaar
+QUOTIENT		= QUOTIENT			##	Geeft als resultaat de uitkomst van een deling als geheel getal
+RADIANS			= RADIALEN			##	Converteert graden naar radialen
+RAND			= ASELECT			##	Geeft als resultaat een willekeurig getal tussen 0 en 1
+RANDBETWEEN		= ASELECTTUSSEN			##	Geeft een willekeurig getal tussen de getallen die u hebt opgegeven
+ROMAN			= ROMEINS			##	Converteert een Arabisch getal naar een Romeins getal en geeft het resultaat weer in de vorm van tekst
+ROUND			= AFRONDEN			##	Rondt een getal af op het opgegeven aantal decimalen
+ROUNDDOWN		= AFRONDEN.NAAR.BENEDEN		##	Rondt de absolute waarde van een getal naar beneden af
+ROUNDUP			= AFRONDEN.NAAR.BOVEN		##	Rondt de absolute waarde van een getal naar boven af
+SERIESSUM		= SOM.MACHTREEKS		##	Geeft als resultaat de som van een machtreeks die is gebaseerd op de formule
+SIGN			= POS.NEG			##	Geeft als resultaat het teken van een getal
+SIN			= SIN				##	Geeft als resultaat de sinus van de opgegeven hoek
+SINH			= SINH				##	Geeft als resultaat de sinus hyperbolicus van een getal
+SQRT			= WORTEL			##	Geeft als resultaat de positieve vierkantswortel van een getal
+SQRTPI			= WORTEL.PI			##	Geeft als resultaat de vierkantswortel van (getal * pi)
+SUBTOTAL		= SUBTOTAAL			##	Geeft als resultaat een subtotaal voor een bereik
+SUM			= SOM				##	Telt de argumenten op
+SUMIF			= SOM.ALS			##	Telt de getallen bij elkaar op die voldoen aan een bepaald criterium
+SUMIFS			= SOMMEN.ALS			##	Telt de cellen in een bereik op die aan meerdere criteria voldoen
+SUMPRODUCT		= SOMPRODUCT			##	Geeft als resultaat de som van de producten van de corresponderende matrixelementen
+SUMSQ			= KWADRATENSOM			##	Geeft als resultaat de som van de kwadraten van de argumenten
+SUMX2MY2		= SOM.X2MINY2			##	Geeft als resultaat de som van het verschil tussen de kwadraten van corresponderende waarden in twee matrices
+SUMX2PY2		= SOM.X2PLUSY2			##	Geeft als resultaat de som van de kwadratensom van corresponderende waarden in twee matrices
+SUMXMY2			= SOM.XMINY.2			##	Geeft als resultaat de som van de kwadraten van de verschillen tussen de corresponderende waarden in twee matrices
+TAN			= TAN				##	Geeft als resultaat de tangens van een getal
+TANH			= TANH				##	Geeft als resultaat de tangens hyperbolicus van een getal
+TRUNC			= GEHEEL			##	Kapt een getal af tot een geheel getal
+
+
+##
+##	Statistical functions				Statistische functies
+##
+AVEDEV			= GEM.DEVIATIE			##	Geeft als resultaat het gemiddelde van de absolute deviaties van gegevenspunten ten opzichte van hun gemiddelde waarde
+AVERAGE			= GEMIDDELDE			##	Geeft als resultaat het gemiddelde van de argumenten
+AVERAGEA		= GEMIDDELDEA			##	Geeft als resultaat het gemiddelde van de argumenten, inclusief getallen, tekst en logische waarden
+AVERAGEIF		= GEMIDDELDE.ALS		##	Geeft het gemiddelde (rekenkundig gemiddelde) als resultaat van alle cellen in een bereik die voldoen aan de opgegeven criteria
+AVERAGEIFS		= GEMIDDELDEN.ALS		##	Geeft het gemiddelde (rekenkundig gemiddelde) als resultaat van alle cellen die aan meerdere criteria voldoen
+BETADIST		= BETA.VERD			##	Geeft als resultaat de cumulatieve bèta-verdelingsfunctie
+BETAINV			= BETA.INV			##	Geeft als resultaat de inverse van de cumulatieve verdelingsfunctie voor een gegeven bèta-verdeling
+BINOMDIST		= BINOMIALE.VERD		##	Geeft als resultaat de binomiale verdeling
+CHIDIST			= CHI.KWADRAAT			##	Geeft als resultaat de eenzijdige kans van de chi-kwadraatverdeling
+CHIINV			= CHI.KWADRAAT.INV		##	Geeft als resultaat de inverse van een eenzijdige kans van de chi-kwadraatverdeling
+CHITEST			= CHI.TOETS			##	Geeft als resultaat de onafhankelijkheidstoets
+CONFIDENCE		= BETROUWBAARHEID		##	Geeft als resultaat het betrouwbaarheidsinterval van een gemiddelde waarde voor de elementen van een populatie
+CORREL			= CORRELATIE			##	Geeft als resultaat de correlatiecoëfficiënt van twee gegevensverzamelingen
+COUNT			= AANTAL			##	Telt het aantal getallen in de argumentenlijst
+COUNTA			= AANTALARG			##	Telt het aantal waarden in de argumentenlijst
+COUNTBLANK		= AANTAL.LEGE.CELLEN		##	Telt het aantal lege cellen in een bereik
+COUNTIF			= AANTAL.ALS			##	Telt in een bereik het aantal cellen die voldoen aan een bepaald criterium
+COUNTIFS		= AANTALLEN.ALS			##	Telt in een bereik het aantal cellen die voldoen aan meerdere criteria
+COVAR			= COVARIANTIE			##	Geeft als resultaat de covariantie, het gemiddelde van de producten van de gepaarde deviaties
+CRITBINOM		= CRIT.BINOM			##	Geeft als resultaat de kleinste waarde waarvoor de binomiale verdeling kleiner is dan of gelijk is aan het criterium
+DEVSQ			= DEV.KWAD			##	Geeft als resultaat de som van de deviaties in het kwadraat
+EXPONDIST		= EXPON.VERD			##	Geeft als resultaat de exponentiële verdeling
+FDIST			= F.VERDELING			##	Geeft als resultaat de F-verdeling
+FINV			= F.INVERSE			##	Geeft als resultaat de inverse van de F-verdeling
+FISHER			= FISHER			##	Geeft als resultaat de Fisher-transformatie
+FISHERINV		= FISHER.INV			##	Geeft als resultaat de inverse van de Fisher-transformatie
+FORECAST		= VOORSPELLEN			##	Geeft als resultaat een waarde op basis van een lineaire trend
+FREQUENCY		= FREQUENTIE			##	Geeft als resultaat een frequentieverdeling in de vorm van een verticale matrix
+FTEST			= F.TOETS			##	Geeft als resultaat een F-toets
+GAMMADIST		= GAMMA.VERD			##	Geeft als resultaat de gamma-verdeling
+GAMMAINV		= GAMMA.INV			##	Geeft als resultaat de inverse van de cumulatieve gamma-verdeling
+GAMMALN			= GAMMA.LN			##	Geeft als resultaat de natuurlijke logaritme van de gamma-functie, G(x)
+GEOMEAN			= MEETK.GEM			##	Geeft als resultaat het meetkundige gemiddelde
+GROWTH			= GROEI				##	Geeft als resultaat de waarden voor een exponentiële trend
+HARMEAN			= HARM.GEM			##	Geeft als resultaat het harmonische gemiddelde
+HYPGEOMDIST		= HYPERGEO.VERD			##	Geeft als resultaat de hypergeometrische verdeling
+INTERCEPT		= SNIJPUNT			##	Geeft als resultaat het snijpunt van de lineaire regressielijn met de y-as
+KURT			= KURTOSIS			##	Geeft als resultaat de kurtosis van een gegevensverzameling
+LARGE			= GROOTSTE			##	Geeft als resultaat de op k-1 na grootste waarde in een gegevensverzameling
+LINEST			= LIJNSCH			##	Geeft als resultaat de parameters van een lineaire trend
+LOGEST			= LOGSCH			##	Geeft als resultaat de parameters van een exponentiële trend
+LOGINV			= LOG.NORM.INV			##	Geeft als resultaat de inverse van de logaritmische normale verdeling
+LOGNORMDIST		= LOG.NORM.VERD			##	Geeft als resultaat de cumulatieve logaritmische normale verdeling
+MAX			= MAX				##	Geeft als resultaat de maximumwaarde in een lijst met argumenten
+MAXA			= MAXA				##	Geeft als resultaat de maximumwaarde in een lijst met argumenten, inclusief getallen, tekst en logische waarden
+MEDIAN			= MEDIAAN			##	Geeft als resultaat de mediaan van de opgegeven getallen
+MIN			= MIN				##	Geeft als resultaat de minimumwaarde in een lijst met argumenten
+MINA			= MINA				##	Geeft als resultaat de minimumwaarde in een lijst met argumenten, inclusief getallen, tekst en logische waarden
+MODE			= MODUS				##	Geeft als resultaat de meest voorkomende waarde in een gegevensverzameling
+NEGBINOMDIST		= NEG.BINOM.VERD		##	Geeft als resultaat de negatieve binomiaalverdeling
+NORMDIST		= NORM.VERD			##	Geeft als resultaat de cumulatieve normale verdeling
+NORMINV			= NORM.INV			##	Geeft als resultaat de inverse van de cumulatieve standaardnormale verdeling
+NORMSDIST		= STAND.NORM.VERD		##	Geeft als resultaat de cumulatieve standaardnormale verdeling
+NORMSINV		= STAND.NORM.INV		##	Geeft als resultaat de inverse van de cumulatieve normale verdeling
+PEARSON			= PEARSON			##	Geeft als resultaat de correlatiecoëfficiënt van Pearson
+PERCENTILE		= PERCENTIEL			##	Geeft als resultaat het k-de percentiel van waarden in een bereik
+PERCENTRANK		= PERCENT.RANG			##	Geeft als resultaat de positie, in procenten uitgedrukt, van een waarde in de rangorde van een gegevensverzameling
+PERMUT			= PERMUTATIES			##	Geeft als resultaat het aantal permutaties voor een gegeven aantal objecten
+POISSON			= POISSON			##	Geeft als resultaat de Poisson-verdeling
+PROB			= KANS				##	Geeft als resultaat de kans dat waarden zich tussen twee grenzen bevinden
+QUARTILE		= KWARTIEL			##	Geeft als resultaat het kwartiel van een gegevensverzameling
+RANK			= RANG				##	Geeft als resultaat het rangnummer van een getal in een lijst getallen
+RSQ			= R.KWADRAAT			##	Geeft als resultaat het kwadraat van de Pearson-correlatiecoëfficiënt
+SKEW			= SCHEEFHEID			##	Geeft als resultaat de mate van asymmetrie van een verdeling
+SLOPE			= RICHTING			##	Geeft als resultaat de richtingscoëfficiënt van een lineaire regressielijn
+SMALL			= KLEINSTE			##	Geeft als resultaat de op k-1 na kleinste waarde in een gegevensverzameling
+STANDARDIZE		= NORMALISEREN			##	Geeft als resultaat een genormaliseerde waarde
+STDEV			= STDEV				##	Maakt een schatting van de standaarddeviatie op basis van een steekproef
+STDEVA			= STDEVA			##	Maakt een schatting van de standaarddeviatie op basis van een steekproef, inclusief getallen, tekst en logische waarden
+STDEVP			= STDEVP			##	Berekent de standaarddeviatie op basis van de volledige populatie
+STDEVPA			= STDEVPA			##	Berekent de standaarddeviatie op basis van de volledige populatie, inclusief getallen, tekst en logische waarden
+STEYX			= STAND.FOUT.YX			##	Geeft als resultaat de standaardfout in de voorspelde y-waarde voor elke x in een regressie
+TDIST			= T.VERD			##	Geeft als resultaat de Student T-verdeling
+TINV			= T.INV				##	Geeft als resultaat de inverse van de Student T-verdeling
+TREND			= TREND				##	Geeft als resultaat de waarden voor een lineaire trend
+TRIMMEAN		= GETRIMD.GEM			##	Geeft als resultaat het gemiddelde van waarden in een gegevensverzameling
+TTEST			= T.TOETS			##	Geeft als resultaat de kans met behulp van de Student T-toets
+VAR			= VAR				##	Maakt een schatting van de variantie op basis van een steekproef
+VARA			= VARA				##	Maakt een schatting van de variantie op basis van een steekproef, inclusief getallen, tekst en logische waarden
+VARP			= VARP				##	Berekent de variantie op basis van de volledige populatie
+VARPA			= VARPA				##	Berekent de standaarddeviatie op basis van de volledige populatie, inclusief getallen, tekst en logische waarden
+WEIBULL			= WEIBULL			##	Geeft als resultaat de Weibull-verdeling
+ZTEST			= Z.TOETS			##	Geeft als resultaat de eenzijdige kanswaarde van een Z-toets
+
+
+##
+##	Text functions					Tekstfuncties
+##
+ASC			= ASC				##	Wijzigt Nederlandse letters of katakanatekens over de volle breedte (dubbel-bytetekens) binnen een tekenreeks in tekens over de halve breedte (enkel-bytetekens)
+BAHTTEXT		= BAHT.TEKST			##	Converteert een getal naar tekst met de valutanotatie ß (baht)
+CHAR			= TEKEN				##	Geeft als resultaat het teken dat hoort bij de opgegeven code
+CLEAN			= WISSEN.CONTROL		##	Verwijdert alle niet-afdrukbare tekens uit een tekst
+CODE			= CODE				##	Geeft als resultaat de numerieke code voor het eerste teken in een tekenreeks
+CONCATENATE		= TEKST.SAMENVOEGEN		##	Voegt verschillende tekstfragmenten samen tot één tekstfragment
+DOLLAR			= EURO				##	Converteert een getal naar tekst met de valutanotatie € (euro)
+EXACT			= GELIJK			##	Controleert of twee tekenreeksen identiek zijn
+FIND			= VIND.ALLES			##	Zoekt een bepaalde tekenreeks in een tekst (waarbij onderscheid wordt gemaakt tussen hoofdletters en kleine letters)
+FINDB			= VIND.ALLES.B			##	Zoekt een bepaalde tekenreeks in een tekst (waarbij onderscheid wordt gemaakt tussen hoofdletters en kleine letters)
+FIXED			= VAST				##	Maakt een getal als tekst met een vast aantal decimalen op
+JIS			= JIS				##	Wijzigt Nederlandse letters of katakanatekens over de halve breedte (enkel-bytetekens) binnen een tekenreeks in tekens over de volle breedte (dubbel-bytetekens)
+LEFT			= LINKS				##	Geeft als resultaat de meest linkse tekens in een tekenreeks
+LEFTB			= LINKSB			##	Geeft als resultaat de meest linkse tekens in een tekenreeks
+LEN			= LENGTE			##	Geeft als resultaat het aantal tekens in een tekenreeks
+LENB			= LENGTEB			##	Geeft als resultaat het aantal tekens in een tekenreeks
+LOWER			= KLEINE.LETTERS		##	Zet tekst om in kleine letters
+MID			= MIDDEN			##	Geeft als resultaat een bepaald aantal tekens van een tekenreeks vanaf de positie die u opgeeft
+MIDB			= DEELB				##	Geeft als resultaat een bepaald aantal tekens van een tekenreeks vanaf de positie die u opgeeft
+PHONETIC		= FONETISCH			##	Haalt de fonetische tekens (furigana) uit een tekenreeks op
+PROPER			= BEGINLETTERS			##	Zet de eerste letter van elk woord in een tekst om in een hoofdletter
+REPLACE			= VERVANG			##	Vervangt tekens binnen een tekst
+REPLACEB		= VERVANGENB			##	Vervangt tekens binnen een tekst
+REPT			= HERHALING			##	Herhaalt een tekst een aantal malen
+RIGHT			= RECHTS			##	Geeft als resultaat de meest rechtse tekens in een tekenreeks
+RIGHTB			= RECHTSB			##	Geeft als resultaat de meest rechtse tekens in een tekenreeks
+SEARCH			= VIND.SPEC			##	Zoekt een bepaalde tekenreeks in een tekst (waarbij geen onderscheid wordt gemaakt tussen hoofdletters en kleine letters)
+SEARCHB			= VIND.SPEC.B			##	Zoekt een bepaalde tekenreeks in een tekst (waarbij geen onderscheid wordt gemaakt tussen hoofdletters en kleine letters)
+SUBSTITUTE		= SUBSTITUEREN			##	Vervangt oude tekst door nieuwe tekst in een tekenreeks
+T			= T				##	Converteert de argumenten naar tekst
+TEXT			= TEKST				##	Maakt een getal op en converteert het getal naar tekst
+TRIM			= SPATIES.WISSEN		##	Verwijdert de spaties uit een tekst
+UPPER			= HOOFDLETTERS			##	Zet tekst om in hoofdletters
+VALUE			= WAARDE			##	Converteert tekst naar een getal
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/no/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/no/config
new file mode 100755
index 0000000..c7f4152
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/no/config
@@ -0,0 +1,24 @@
+##
+## PhpSpreadsheet
+##
+
+ArgumentSeparator	= ;
+
+
+##
+##	(For future use)
+##
+currencySymbol	= kr
+
+
+##
+##	Excel Error Codes	(For future use)
+
+##
+NULL	= #NULL!
+DIV0	= #DIV/0!
+VALUE	= #VERDI!
+REF	= #REF!
+NAME	= #NAVN?
+NUM	= #NUM!
+NA	= #I/T
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/no/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/no/functions
new file mode 100755
index 0000000..ab2a379
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/no/functions
@@ -0,0 +1,416 @@
+##
+## PhpSpreadsheet
+##
+## Data in this file derived from https://www.excel-function-translation.com/
+##
+##
+
+
+##
+##	Add-in and Automation functions				Funksjonene Tillegg og Automatisering
+##
+GETPIVOTDATA		= HENTPIVOTDATA				##	Returnerer data som er lagret i en pivottabellrapport
+
+
+##
+##	Cube functions						Kubefunksjoner
+##
+CUBEKPIMEMBER		= KUBEKPIMEDLEM				##	Returnerer navnet, egenskapen og målet for en viktig ytelsesindikator (KPI), og viser navnet og egenskapen i cellen. En KPI er en målbar enhet, for eksempel månedlig bruttoinntjening eller kvartalsvis inntjening per ansatt, og brukes til å overvåke ytelsen i en organisasjon.
+CUBEMEMBER		= KUBEMEDLEM				##	Returnerer et medlem eller en tuppel i et kubehierarki. Brukes til å validere at medlemmet eller tuppelen finnes i kuben.
+CUBEMEMBERPROPERTY	= KUBEMEDLEMEGENSKAP			##	Returnerer verdien til en medlemsegenskap i kuben. Brukes til å validere at et medlemsnavn finnes i kuben, og til å returnere den angitte egenskapen for dette medlemmet.
+CUBERANKEDMEMBER	= KUBERANGERTMEDLEM			##	Returnerer det n-te, eller rangerte, medlemmet i et sett. Brukes til å returnere ett eller flere elementer i et sett, for eksempel de 10 beste studentene.
+CUBESET			= KUBESETT				##	Definerer et beregnet sett av medlemmer eller tuppeler ved å sende et settuttrykk til kuben på serveren, noe som oppretter settet og deretter returnerer dette settet til Microsoft Office Excel.
+CUBESETCOUNT		= KUBESETTANTALL			##	Returnerer antallet elementer i et sett.
+CUBEVALUE		= KUBEVERDI				##	Returnerer en aggregert verdi fra en kube.
+
+
+##
+##	Database functions					Databasefunksjoner
+##
+DAVERAGE		= DGJENNOMSNITT				##	Returnerer gjennomsnittet av merkede databaseposter
+DCOUNT			= DANTALL				##	Teller celler som inneholder tall i en database
+DCOUNTA			= DANTALLA				##	Teller celler som ikke er tomme i en database
+DGET			= DHENT					##	Trekker ut fra en database en post som oppfyller angitte vilkår
+DMAX			= DMAKS					##	Returnerer maksimumsverdien fra merkede databaseposter
+DMIN			= DMIN					##	Returnerer minimumsverdien fra merkede databaseposter
+DPRODUCT		= DPRODUKT				##	Multipliserer verdiene i et bestemt felt med poster som oppfyller vilkårene i en database
+DSTDEV			= DSTDAV				##	Estimerer standardavviket basert på et utvalg av merkede databaseposter
+DSTDEVP			= DSTAVP				##	Beregner standardavviket basert på at merkede databaseposter utgjør hele populasjonen
+DSUM			= DSUMMER				##	Legger til tallene i feltkolonnen med poster, i databasen som oppfyller vilkårene
+DVAR			= DVARIANS				##	Estimerer variansen basert på et utvalg av merkede databaseposter
+DVARP			= DVARIANSP				##	Beregner variansen basert på at merkede databaseposter utgjør hele populasjonen
+
+
+##
+##	Date and time functions					Dato- og tidsfunksjoner
+##
+DATE			= DATO					##	Returnerer serienummeret som svarer til en bestemt dato
+DATEVALUE		= DATOVERDI				##	Konverterer en dato med tekstformat til et serienummer
+DAY			= DAG					##	Konverterer et serienummer til en dag i måneden
+DAYS360			= DAGER360				##	Beregner antall dager mellom to datoer basert på et år med 360 dager
+EDATE			= DAG.ETTER				##	Returnerer serienummeret som svarer til datoen som er det indikerte antall måneder før eller etter startdatoen
+EOMONTH			= MÅNEDSSLUTT				##	Returnerer serienummeret som svarer til siste dag i måneden, før eller etter et angitt antall måneder
+HOUR			= TIME					##	Konverterer et serienummer til en time
+MINUTE			= MINUTT				##	Konverterer et serienummer til et minutt
+MONTH			= MÅNED					##	Konverterer et serienummer til en måned
+NETWORKDAYS		= NETT.ARBEIDSDAGER			##	Returnerer antall hele arbeidsdager mellom to datoer
+NOW			= NÅ					##	Returnerer serienummeret som svarer til gjeldende dato og klokkeslett
+SECOND			= SEKUND				##	Konverterer et serienummer til et sekund
+TIME			= TID					##	Returnerer serienummeret som svarer til et bestemt klokkeslett
+TIMEVALUE		= TIDSVERDI				##	Konverterer et klokkeslett i tekstformat til et serienummer
+TODAY			= IDAG					##	Returnerer serienummeret som svarer til dagens dato
+WEEKDAY			= UKEDAG				##	Konverterer et serienummer til en ukedag
+WEEKNUM			= UKENR					##	Konverterer et serienummer til et tall som representerer hvilket nummer uken har i et år
+WORKDAY			= ARBEIDSDAG				##	Returnerer serienummeret som svarer til datoen før eller etter et angitt antall arbeidsdager
+YEAR			= ÅR					##	Konverterer et serienummer til et år
+YEARFRAC		= ÅRDEL					##	Returnerer brøkdelen for året, som svarer til antall hele dager mellom startdato og sluttdato
+
+
+##
+##	Engineering functions					Tekniske funksjoner
+##
+BESSELI			= BESSELI				##	Returnerer den endrede Bessel-funksjonen In(x)
+BESSELJ			= BESSELJ				##	Returnerer Bessel-funksjonen Jn(x)
+BESSELK			= BESSELK				##	Returnerer den endrede Bessel-funksjonen Kn(x)
+BESSELY			= BESSELY				##	Returnerer Bessel-funksjonen Yn(x)
+BIN2DEC			= BINTILDES				##	Konverterer et binært tall til et desimaltall
+BIN2HEX			= BINTILHEKS				##	Konverterer et binært tall til et heksadesimaltall
+BIN2OCT			= BINTILOKT				##	Konverterer et binært tall til et oktaltall
+COMPLEX			= KOMPLEKS				##	Konverterer reelle og imaginære koeffisienter til et komplekst tall
+CONVERT			= KONVERTER				##	Konverterer et tall fra ett målsystem til et annet
+DEC2BIN			= DESTILBIN				##	Konverterer et desimaltall til et binærtall
+DEC2HEX			= DESTILHEKS				##	Konverterer et heltall i 10-tallsystemet til et heksadesimalt tall
+DEC2OCT			= DESTILOKT				##	Konverterer et heltall i 10-tallsystemet til et oktaltall
+DELTA			= DELTA					##	Undersøker om to verdier er like
+ERF			= FEILF					##	Returnerer feilfunksjonen
+ERFC			= FEILFK				##	Returnerer den komplementære feilfunksjonen
+GESTEP			= GRENSEVERDI				##	Tester om et tall er større enn en terskelverdi
+HEX2BIN			= HEKSTILBIN				##	Konverterer et heksadesimaltall til et binært tall
+HEX2DEC			= HEKSTILDES				##	Konverterer et heksadesimalt tall til et heltall i 10-tallsystemet
+HEX2OCT			= HEKSTILOKT				##	Konverterer et heksadesimalt tall til et oktaltall
+IMABS			= IMABS					##	Returnerer absoluttverdien (koeffisienten) til et komplekst tall
+IMAGINARY		= IMAGINÆR				##	Returnerer den imaginære koeffisienten til et komplekst tall
+IMARGUMENT		= IMARGUMENT				##	Returnerer argumentet theta, som er en vinkel uttrykt i radianer
+IMCONJUGATE		= IMKONJUGERT				##	Returnerer den komplekse konjugaten til et komplekst tall
+IMCOS			= IMCOS					##	Returnerer cosinus til et komplekst tall
+IMDIV			= IMDIV					##	Returnerer kvotienten til to komplekse tall
+IMEXP			= IMEKSP				##	Returnerer eksponenten til et komplekst tall
+IMLN			= IMLN					##	Returnerer den naturlige logaritmen for et komplekst tall
+IMLOG10			= IMLOG10				##	Returnerer logaritmen med grunntall 10 for et komplekst tall
+IMLOG2			= IMLOG2				##	Returnerer logaritmen med grunntall 2 for et komplekst tall
+IMPOWER			= IMOPPHØY				##	Returnerer et komplekst tall opphøyd til en heltallspotens
+IMPRODUCT		= IMPRODUKT				##	Returnerer produktet av komplekse tall
+IMREAL			= IMREELL				##	Returnerer den reelle koeffisienten til et komplekst tall
+IMSIN			= IMSIN					##	Returnerer sinus til et komplekst tall
+IMSQRT			= IMROT					##	Returnerer kvadratroten av et komplekst tall
+IMSUB			= IMSUB					##	Returnerer differansen mellom to komplekse tall
+IMSUM			= IMSUMMER				##	Returnerer summen av komplekse tall
+OCT2BIN			= OKTTILBIN				##	Konverterer et oktaltall til et binært tall
+OCT2DEC			= OKTTILDES				##	Konverterer et oktaltall til et desimaltall
+OCT2HEX			= OKTTILHEKS				##	Konverterer et oktaltall til et heksadesimaltall
+
+
+##
+##	Financial functions					Økonomiske funksjoner
+##
+ACCRINT			= PÅLØPT.PERIODISK.RENTE		##	Returnerer påløpte renter for et verdipapir som betaler periodisk rente
+ACCRINTM		= PÅLØPT.FORFALLSRENTE			##	Returnerer den påløpte renten for et verdipapir som betaler rente ved forfall
+AMORDEGRC		= AMORDEGRC				##	Returnerer avskrivningen for hver regnskapsperiode ved hjelp av en avskrivingskoeffisient
+AMORLINC		= AMORLINC				##	Returnerer avskrivingen for hver regnskapsperiode
+COUPDAYBS		= OBLIG.DAGER.FF			##	Returnerer antall dager fra begynnelsen av den rentebærende perioden til innløsningsdatoen
+COUPDAYS		= OBLIG.DAGER				##	Returnerer antall dager i den rentebærende perioden som inneholder innløsningsdatoen
+COUPDAYSNC		= OBLIG.DAGER.NF			##	Returnerer antall dager fra betalingsdato til neste renteinnbetalingsdato
+COUPNCD			= OBLIG.DAGER.EF			##	Returnerer obligasjonsdatoen som kommer etter oppgjørsdatoen
+COUPNUM			= OBLIG.ANTALL				##	Returnerer antall obligasjoner som skal betales mellom oppgjørsdatoen og forfallsdatoen
+COUPPCD			= OBLIG.DAG.FORRIGE			##	Returnerer obligasjonsdatoen som kommer før oppgjørsdatoen
+CUMIPMT			= SAMLET.RENTE				##	Returnerer den kumulative renten som er betalt mellom to perioder
+CUMPRINC		= SAMLET.HOVEDSTOL			##	Returnerer den kumulative hovedstolen som er betalt for et lån mellom to perioder
+DB			= DAVSKR				##	Returnerer avskrivningen for et aktivum i en angitt periode, foretatt med fast degressiv avskrivning
+DDB			= DEGRAVS				##	Returnerer avskrivningen for et aktivum for en gitt periode, ved hjelp av dobbel degressiv avskrivning eller en metode som du selv angir
+DISC			= DISKONTERT				##	Returnerer diskonteringsraten for et verdipapir
+DOLLARDE		= DOLLARDE				##	Konverterer en valutapris uttrykt som en brøk, til en valutapris uttrykt som et desimaltall
+DOLLARFR		= DOLLARBR				##	Konverterer en valutapris uttrykt som et desimaltall, til en valutapris uttrykt som en brøk
+DURATION		= VARIGHET				##	Returnerer årlig varighet for et verdipapir med renter som betales periodisk
+EFFECT			= EFFEKTIV.RENTE			##	Returnerer den effektive årlige rentesatsen
+FV			= SLUTTVERDI				##	Returnerer fremtidig verdi for en investering
+FVSCHEDULE		= SVPLAN				##	Returnerer den fremtidige verdien av en inngående hovedstol etter å ha anvendt en serie med sammensatte rentesatser
+INTRATE			= RENTESATS				##	Returnerer rentefoten av et fullfinansiert verdipapir
+IPMT			= RAVDRAG				##	Returnerer betalte renter på en investering for en gitt periode
+IRR			= IR					##	Returnerer internrenten for en serie kontantstrømmer
+ISPMT			= ER.AVDRAG				##	Beregner renten som er betalt for en investering i løpet av en bestemt periode
+MDURATION		= MVARIGHET				##	Returnerer Macauleys modifiserte varighet for et verdipapir med en antatt pålydende verdi på kr 100,00
+MIRR			= MODIR					##	Returnerer internrenten der positive og negative kontantstrømmer finansieres med forskjellige satser
+NOMINAL			= NOMINELL				##	Returnerer årlig nominell rentesats
+NPER			= PERIODER				##	Returnerer antall perioder for en investering
+NPV			= NNV					##	Returnerer netto nåverdi for en investering, basert på en serie periodiske kontantstrømmer og en rentesats
+ODDFPRICE		= AVVIKFP.PRIS				##	Returnerer pris pålydende kr 100 for et verdipapir med en odde første periode
+ODDFYIELD		= AVVIKFP.AVKASTNING			##	Returnerer avkastingen for et verdipapir med en odde første periode
+ODDLPRICE		= AVVIKSP.PRIS				##	Returnerer pris pålydende kr 100 for et verdipapir med en odde siste periode
+ODDLYIELD		= AVVIKSP.AVKASTNING			##	Returnerer avkastingen for et verdipapir med en odde siste periode
+PMT			= AVDRAG				##	Returnerer periodisk betaling for en annuitet
+PPMT			= AMORT					##	Returnerer betalingen på hovedstolen for en investering i en gitt periode
+PRICE			= PRIS					##	Returnerer prisen per pålydende kr 100 for et verdipapir som gir periodisk avkastning
+PRICEDISC		= PRIS.DISKONTERT			##	Returnerer prisen per pålydende kr 100 for et diskontert verdipapir
+PRICEMAT		= PRIS.FORFALL				##	Returnerer prisen per pålydende kr 100 av et verdipapir som betaler rente ved forfall
+PV			= NÅVERDI				##	Returnerer nåverdien av en investering
+RATE			= RENTE					##	Returnerer rentesatsen per periode for en annuitet
+RECEIVED		= MOTTATT.AVKAST			##	Returnerer summen som mottas ved forfallsdato for et fullinvestert verdipapir
+SLN			= LINAVS				##	Returnerer den lineære avskrivningen for et aktivum i én periode
+SYD			= ÅRSAVS				##	Returnerer årsavskrivningen for et aktivum i en angitt periode
+TBILLEQ			= TBILLEKV				##	Returnerer den obligasjonsekvivalente avkastningen for en statsobligasjon
+TBILLPRICE		= TBILLPRIS				##	Returnerer prisen per pålydende kr 100 for en statsobligasjon
+TBILLYIELD		= TBILLAVKASTNING			##	Returnerer avkastningen til en statsobligasjon
+VDB			= VERDIAVS				##	Returnerer avskrivningen for et aktivum i en angitt periode eller delperiode, ved hjelp av degressiv avskrivning
+XIRR			= XIR					##	Returnerer internrenten for en serie kontantstrømmer som ikke nødvendigvis er periodiske
+XNPV			= XNNV					##	Returnerer netto nåverdi for en serie kontantstrømmer som ikke nødvendigvis er periodiske
+YIELD			= AVKAST				##	Returnerer avkastningen på et verdipapir som betaler periodisk rente
+YIELDDISC		= AVKAST.DISKONTERT			##	Returnerer årlig avkastning for et diskontert verdipapir, for eksempel en statskasseveksel
+YIELDMAT		= AVKAST.FORFALL			##	Returnerer den årlige avkastningen for et verdipapir som betaler rente ved forfallsdato
+
+
+##
+##	Information functions					Informasjonsfunksjoner
+##
+CELL			= CELLE					##	Returnerer informasjon om formatering, plassering eller innholdet til en celle
+ERROR.TYPE		= FEIL.TYPE				##	Returnerer et tall som svarer til en feiltype
+INFO			= INFO					##	Returnerer informasjon om gjeldende operativmiljø
+ISBLANK			= ERTOM					##	Returnerer SANN hvis verdien er tom
+ISERR			= ERFEIL				##	Returnerer SANN hvis verdien er en hvilken som helst annen feilverdi enn #I/T
+ISERROR			= ERFEIL				##	Returnerer SANN hvis verdien er en hvilken som helst feilverdi
+ISEVEN			= ERPARTALL				##	Returnerer SANN hvis tallet er et partall
+ISLOGICAL		= ERLOGISK				##	Returnerer SANN hvis verdien er en logisk verdi
+ISNA			= ERIT					##	Returnerer SANN hvis verdien er feilverdien #I/T
+ISNONTEXT		= ERIKKETEKST				##	Returnerer SANN hvis verdien ikke er tekst
+ISNUMBER		= ERTALL				##	Returnerer SANN hvis verdien er et tall
+ISODD			= ERODDETALL				##	Returnerer SANN hvis tallet er et oddetall
+ISREF			= ERREF					##	Returnerer SANN hvis verdien er en referanse
+ISTEXT			= ERTEKST				##	Returnerer SANN hvis verdien er tekst
+N			= N					##	Returnerer en verdi som er konvertert til et tall
+NA			= IT					##	Returnerer feilverdien #I/T
+TYPE			= VERDITYPE				##	Returnerer et tall som indikerer datatypen til en verdi
+
+
+##
+##	Logical functions					Logiske funksjoner
+##
+AND			= OG					##	Returnerer SANN hvis alle argumentene er lik SANN
+FALSE			= USANN					##	Returnerer den logiske verdien USANN
+IF			= HVIS					##	Angir en logisk test som skal utføres
+IFERROR			= HVISFEIL				##	Returnerer en verdi du angir hvis en formel evaluerer til en feil. Ellers returnerer den resultatet av formelen.
+NOT			= IKKE					##	Reverserer logikken til argumentet
+OR			= ELLER					##	Returnerer SANN hvis ett eller flere argumenter er lik SANN
+TRUE			= SANN					##	Returnerer den logiske verdien SANN
+
+
+##
+##	Lookup and reference functions				Oppslag- og referansefunksjoner
+##
+ADDRESS			= ADRESSE				##	Returnerer en referanse som tekst til en enkelt celle i et regneark
+AREAS			= OMRÅDER				##	Returnerer antall områder i en referanse
+CHOOSE			= VELG					##	Velger en verdi fra en liste med verdier
+COLUMN			= KOLONNE				##	Returnerer kolonnenummeret for en referanse
+COLUMNS			= KOLONNER				##	Returnerer antall kolonner i en referanse
+HLOOKUP			= FINN.KOLONNE				##	Leter i den øverste raden i en matrise og returnerer verdien for den angitte cellen
+HYPERLINK		= HYPERKOBLING				##	Oppretter en snarvei eller et hopp som åpner et dokument som er lagret på en nettverksserver, et intranett eller Internett
+INDEX			= INDEKS				##	Bruker en indeks til å velge en verdi fra en referanse eller matrise
+INDIRECT		= INDIREKTE				##	Returnerer en referanse angitt av en tekstverdi
+LOOKUP			= SLÅ.OPP				##	Slår opp verdier i en vektor eller matrise
+MATCH			= SAMMENLIGNE				##	Slår opp verdier i en referanse eller matrise
+OFFSET			= FORSKYVNING				##	Returnerer en referanseforskyvning fra en gitt referanse
+ROW			= RAD					##	Returnerer radnummeret for en referanse
+ROWS			= RADER					##	Returnerer antall rader i en referanse
+RTD			= RTD					##	Henter sanntidsdata fra et program som støtter COM-automatisering (automatisering: En måte å arbeide på med programobjekter fra et annet program- eller utviklingsverktøy. Tidligere kalt OLE-automatisering. Automatisering er en bransjestandard og en funksjon i Component Object Model (COM).)
+TRANSPOSE		= TRANSPONER				##	Returnerer transponeringen av en matrise
+VLOOKUP			= FINN.RAD				##	Leter i den første kolonnen i en matrise og flytter bortover raden for å returnere verdien til en celle
+
+
+##
+##	Math and trigonometry functions				Matematikk- og trigonometrifunksjoner
+##
+ABS			= ABS					##	Returnerer absoluttverdien til et tall
+ACOS			= ARCCOS				##	Returnerer arcus cosinus til et tall
+ACOSH			= ARCCOSH				##	Returnerer den inverse hyperbolske cosinus til et tall
+ASIN			= ARCSIN				##	Returnerer arcus sinus til et tall
+ASINH			= ARCSINH				##	Returnerer den inverse hyperbolske sinus til et tall
+ATAN			= ARCTAN				##	Returnerer arcus tangens til et tall
+ATAN2			= ARCTAN2				##	Returnerer arcus tangens fra x- og y-koordinater
+ATANH			= ARCTANH				##	Returnerer den inverse hyperbolske tangens til et tall
+CEILING			= AVRUND.GJELDENDE.MULTIPLUM		##	Runder av et tall til nærmeste heltall eller til nærmeste signifikante multiplum
+COMBIN			= KOMBINASJON				##	Returnerer antall kombinasjoner for ett gitt antall objekter
+COS			= COS					##	Returnerer cosinus til et tall
+COSH			= COSH					##	Returnerer den hyperbolske cosinus til et tall
+DEGREES			= GRADER				##	Konverterer radianer til grader
+EVEN			= AVRUND.TIL.PARTALL			##	Runder av et tall oppover til nærmeste heltall som er et partall
+EXP			= EKSP					##	Returnerer e opphøyd i en angitt potens
+FACT			= FAKULTET				##	Returnerer fakultet til et tall
+FACTDOUBLE		= DOBBELFAKT				##	Returnerer et talls doble fakultet
+FLOOR			= AVRUND.GJELDENDE.MULTIPLUM.NED	##	Avrunder et tall nedover, mot null
+GCD			= SFF					##	Returnerer høyeste felles divisor
+INT			= HELTALL				##	Avrunder et tall nedover til nærmeste heltall
+LCM			= MFM					##	Returnerer minste felles multiplum
+LN			= LN					##	Returnerer den naturlige logaritmen til et tall
+LOG			= LOG					##	Returnerer logaritmen for et tall til et angitt grunntall
+LOG10			= LOG10					##	Returnerer logaritmen med grunntall 10 for et tall
+MDETERM			= MDETERM				##	Returnerer matrisedeterminanten til en matrise
+MINVERSE		= MINVERS				##	Returnerer den inverse matrisen til en matrise
+MMULT			= MMULT					##	Returnerer matriseproduktet av to matriser
+MOD			= REST					##	Returnerer resten fra en divisjon
+MROUND			= MRUND					##	Returnerer et tall avrundet til det ønskede multiplum
+MULTINOMIAL		= MULTINOMINELL				##	Returnerer det multinominelle for et sett med tall
+ODD			= AVRUND.TIL.ODDETALL			##	Runder av et tall oppover til nærmeste heltall som er et oddetall
+PI			= PI					##	Returnerer verdien av pi
+POWER			= OPPHØYD.I				##	Returnerer resultatet av et tall opphøyd i en potens
+PRODUCT			= PRODUKT				##	Multipliserer argumentene
+QUOTIENT		= KVOTIENT				##	Returnerer heltallsdelen av en divisjon
+RADIANS			= RADIANER				##	Konverterer grader til radianer
+RAND			= TILFELDIG				##	Returnerer et tilfeldig tall mellom 0 og 1
+RANDBETWEEN		= TILFELDIGMELLOM			##	Returnerer et tilfeldig tall innenfor et angitt område
+ROMAN			= ROMERTALL				##	Konverterer vanlige tall til romertall, som tekst
+ROUND			= AVRUND				##	Avrunder et tall til et angitt antall sifre
+ROUNDDOWN		= AVRUND.NED				##	Avrunder et tall nedover, mot null
+ROUNDUP			= AVRUND.OPP				##	Runder av et tall oppover, bort fra null
+SERIESSUM		= SUMMER.REKKE				##	Returnerer summen av en geometrisk rekke, basert på formelen
+SIGN			= FORTEGN				##	Returnerer fortegnet for et tall
+SIN			= SIN					##	Returnerer sinus til en gitt vinkel
+SINH			= SINH					##	Returnerer den hyperbolske sinus til et tall
+SQRT			= ROT					##	Returnerer en positiv kvadratrot
+SQRTPI			= ROTPI					##	Returnerer kvadratroten av (tall * pi)
+SUBTOTAL		= DELSUM				##	Returnerer en delsum i en liste eller database
+SUM			= SUMMER				##	Legger sammen argumentene
+SUMIF			= SUMMERHVIS				##	Legger sammen cellene angitt ved et gitt vilkår
+SUMIFS			= SUMMER.HVIS.SETT			##	Legger sammen cellene i et område som oppfyller flere vilkår
+SUMPRODUCT		= SUMMERPRODUKT				##	Returnerer summen av produktene av tilsvarende matrisekomponenter
+SUMSQ			= SUMMERKVADRAT				##	Returnerer kvadratsummen av argumentene
+SUMX2MY2		= SUMMERX2MY2				##	Returnerer summen av differansen av kvadratene for tilsvarende verdier i to matriser
+SUMX2PY2		= SUMMERX2PY2				##	Returnerer summen av kvadratsummene for tilsvarende verdier i to matriser
+SUMXMY2			= SUMMERXMY2				##	Returnerer summen av kvadratene av differansen for tilsvarende verdier i to matriser
+TAN			= TAN					##	Returnerer tangens for et tall
+TANH			= TANH					##	Returnerer den hyperbolske tangens for et tall
+TRUNC			= AVKORT				##	Korter av et tall til et heltall
+
+
+##
+##	Statistical functions					Statistiske funksjoner
+##
+AVEDEV			= GJENNOMSNITTSAVVIK			##	Returnerer datapunktenes gjennomsnittlige absoluttavvik fra middelverdien
+AVERAGE			= GJENNOMSNITT				##	Returnerer gjennomsnittet for argumentene
+AVERAGEA		= GJENNOMSNITTA				##	Returnerer gjennomsnittet for argumentene, inkludert tall, tekst og logiske verdier
+AVERAGEIF		= GJENNOMSNITTHVIS			##	Returnerer gjennomsnittet (aritmetisk gjennomsnitt) av alle cellene i et område som oppfyller et bestemt vilkår
+AVERAGEIFS		= GJENNOMSNITT.HVIS.SETT		##	Returnerer gjennomsnittet (aritmetisk middelverdi) av alle celler som oppfyller flere vilkår.
+BETADIST		= BETA.FORDELING			##	Returnerer den kumulative betafordelingsfunksjonen
+BETAINV			= INVERS.BETA.FORDELING			##	Returnerer den inverse verdien til fordelingsfunksjonen for en angitt betafordeling
+BINOMDIST		= BINOM.FORDELING			##	Returnerer den individuelle binomiske sannsynlighetsfordelingen
+CHIDIST			= KJI.FORDELING				##	Returnerer den ensidige sannsynligheten for en kjikvadrert fordeling
+CHIINV			= INVERS.KJI.FORDELING			##	Returnerer den inverse av den ensidige sannsynligheten for den kjikvadrerte fordelingen
+CHITEST			= KJI.TEST				##	Utfører testen for uavhengighet
+CONFIDENCE		= KONFIDENS				##	Returnerer konfidensintervallet til gjennomsnittet for en populasjon
+CORREL			= KORRELASJON				##	Returnerer korrelasjonskoeffisienten mellom to datasett
+COUNT			= ANTALL				##	Teller hvor mange tall som er i argumentlisten
+COUNTA			= ANTALLA				##	Teller hvor mange verdier som er i argumentlisten
+COUNTBLANK		= TELLBLANKE				##	Teller antall tomme celler i et område.
+COUNTIF			= ANTALL.HVIS				##	Teller antall celler i et område som oppfyller gitte vilkår
+COUNTIFS		= ANTALL.HVIS.SETT			##	Teller antallet ikke-tomme celler i et område som oppfyller flere vilkår
+COVAR			= KOVARIANS				##	Returnerer kovariansen, gjennomsnittet av produktene av parvise avvik
+CRITBINOM		= GRENSE.BINOM				##	Returnerer den minste verdien der den kumulative binomiske fordelingen er mindre enn eller lik en vilkårsverdi
+DEVSQ			= AVVIK.KVADRERT			##	Returnerer summen av kvadrerte avvik
+EXPONDIST		= EKSP.FORDELING			##	Returnerer eksponentialfordelingen
+FDIST			= FFORDELING				##	Returnerer F-sannsynlighetsfordelingen
+FINV			= FFORDELING.INVERS			##	Returnerer den inverse av den sannsynlige F-fordelingen
+FISHER			= FISHER				##	Returnerer Fisher-transformasjonen
+FISHERINV		= FISHERINV				##	Returnerer den inverse av Fisher-transformasjonen
+FORECAST		= PROGNOSE				##	Returnerer en verdi langs en lineær trend
+FREQUENCY		= FREKVENS				##	Returnerer en frekvensdistribusjon som en loddrett matrise
+FTEST			= FTEST					##	Returnerer resultatet av en F-test
+GAMMADIST		= GAMMAFORDELING			##	Returnerer gammafordelingen
+GAMMAINV		= GAMMAINV				##	Returnerer den inverse av den gammakumulative fordelingen
+GAMMALN			= GAMMALN				##	Returnerer den naturlige logaritmen til gammafunksjonen G(x)
+GEOMEAN			= GJENNOMSNITT.GEOMETRISK		##	Returnerer den geometriske middelverdien
+GROWTH			= VEKST					##	Returnerer verdier langs en eksponentiell trend
+HARMEAN			= GJENNOMSNITT.HARMONISK		##	Returnerer den harmoniske middelverdien
+HYPGEOMDIST		= HYPGEOM.FORDELING			##	Returnerer den hypergeometriske fordelingen
+INTERCEPT		= SKJÆRINGSPUNKT			##	Returnerer skjæringspunktet til den lineære regresjonslinjen
+KURT			= KURT					##	Returnerer kurtosen til et datasett
+LARGE			= N.STØRST				##	Returnerer den n-te største verdien i et datasett
+LINEST			= RETTLINJE				##	Returnerer parameterne til en lineær trend
+LOGEST			= KURVE					##	Returnerer parameterne til en eksponentiell trend
+LOGINV			= LOGINV				##	Returnerer den inverse lognormale fordelingen
+LOGNORMDIST		= LOGNORMFORD				##	Returnerer den kumulative lognormale fordelingen
+MAX			= STØRST				##	Returnerer maksimumsverdien i en argumentliste
+MAXA			= MAKSA					##	Returnerer maksimumsverdien i en argumentliste, inkludert tall, tekst og logiske verdier
+MEDIAN			= MEDIAN				##	Returnerer medianen til tallene som er gitt
+MIN			= MIN					##	Returnerer minimumsverdien i en argumentliste
+MINA			= MINA					##	Returnerer den minste verdien i en argumentliste, inkludert tall, tekst og logiske verdier
+MODE			= MODUS					##	Returnerer den vanligste verdien i et datasett
+NEGBINOMDIST		= NEGBINOM.FORDELING			##	Returnerer den negative binomiske fordelingen
+NORMDIST		= NORMALFORDELING			##	Returnerer den kumulative normalfordelingen
+NORMINV			= NORMINV				##	Returnerer den inverse kumulative normalfordelingen
+NORMSDIST		= NORMSFORDELING			##	Returnerer standard kumulativ normalfordeling
+NORMSINV		= NORMSINV				##	Returnerer den inverse av den den kumulative standard normalfordelingen
+PEARSON			= PEARSON				##	Returnerer produktmomentkorrelasjonskoeffisienten, Pearson
+PERCENTILE		= PERSENTIL				##	Returnerer den n-te persentil av verdiene i et område
+PERCENTRANK		= PROSENTDEL				##	Returnerer prosentrangeringen av en verdi i et datasett
+PERMUT			= PERMUTER				##	Returnerer antall permutasjoner for et gitt antall objekter
+POISSON			= POISSON				##	Returnerer Poissons sannsynlighetsfordeling
+PROB			= SANNSYNLIG				##	Returnerer sannsynligheten for at verdier i et område ligger mellom to grenser
+QUARTILE		= KVARTIL				##	Returnerer kvartilen til et datasett
+RANK			= RANG					##	Returnerer rangeringen av et tall, eller plassen tallet har i en rekke
+RSQ			= RKVADRAT				##	Returnerer kvadratet av produktmomentkorrelasjonskoeffisienten (Pearsons r)
+SKEW			= SKJEVFORDELING			##	Returnerer skjevheten i en fordeling
+SLOPE			= STIGNINGSTALL				##	Returnerer stigningtallet for den lineære regresjonslinjen
+SMALL			= N.MINST				##	Returnerer den n-te minste verdien i et datasett
+STANDARDIZE		= NORMALISER				##	Returnerer en normalisert verdi
+STDEV			= STDAV					##	Estimere standardavvik på grunnlag av et utvalg
+STDEVA			= STDAVVIKA				##	Estimerer standardavvik basert på et utvalg, inkludert tall, tekst og logiske verdier
+STDEVP			= STDAVP				##	Beregner standardavvik basert på hele populasjonen
+STDEVPA			= STDAVVIKPA				##	Beregner standardavvik basert på hele populasjonen, inkludert tall, tekst og logiske verdier
+STEYX			= STANDARDFEIL				##	Returnerer standardfeilen for den predikerte y-verdien for hver x i regresjonen
+TDIST			= TFORDELING				##	Returnerer en Student t-fordeling
+TINV			= TINV					##	Returnerer den inverse Student t-fordelingen
+TREND			= TREND					##	Returnerer verdier langs en lineær trend
+TRIMMEAN		= TRIMMET.GJENNOMSNITT			##	Returnerer den interne middelverdien til et datasett
+TTEST			= TTEST					##	Returnerer sannsynligheten assosiert med en Student t-test
+VAR			= VARIANS				##	Estimerer varians basert på et utvalg
+VARA			= VARIANSA				##	Estimerer varians basert på et utvalg, inkludert tall, tekst og logiske verdier
+VARP			= VARIANSP				##	Beregner varians basert på hele populasjonen
+VARPA			= VARIANSPA				##	Beregner varians basert på hele populasjonen, inkludert tall, tekst og logiske verdier
+WEIBULL			= WEIBULL.FORDELING			##	Returnerer Weibull-fordelingen
+ZTEST			= ZTEST					##	Returnerer den ensidige sannsynlighetsverdien for en z-test
+
+
+##
+##	Text functions						Tekstfunksjoner
+##
+ASC			= STIGENDE				##	Endrer fullbreddes (dobbeltbyte) engelske bokstaver eller katakana i en tegnstreng, til halvbreddes (enkeltbyte) tegn
+BAHTTEXT		= BAHTTEKST				##	Konverterer et tall til tekst, og bruker valutaformatet ß (baht)
+CHAR			= TEGNKODE				##	Returnerer tegnet som svarer til kodenummeret
+CLEAN			= RENSK					##	Fjerner alle tegn som ikke kan skrives ut, fra teksten
+CODE			= KODE					##	Returnerer en numerisk kode for det første tegnet i en tekststreng
+CONCATENATE		= KJEDE.SAMMEN				##	Slår sammen flere tekstelementer til ett tekstelement
+DOLLAR			= VALUTA				##	Konverterer et tall til tekst, og bruker valutaformatet $ (dollar)
+EXACT			= EKSAKT				##	Kontrollerer om to tekstverdier er like
+FIND			= FINN					##	Finner en tekstverdi inne i en annen (skiller mellom store og små bokstaver)
+FINDB			= FINNB					##	Finner en tekstverdi inne i en annen (skiller mellom store og små bokstaver)
+FIXED			= FASTSATT				##	Formaterer et tall som tekst med et bestemt antall desimaler
+JIS			= JIS					##	Endrer halvbreddes (enkeltbyte) engelske bokstaver eller katakana i en tegnstreng, til fullbreddes (dobbeltbyte) tegn
+LEFT			= VENSTRE				##	Returnerer tegnene lengst til venstre i en tekstverdi
+LEFTB			= VENSTREB				##	Returnerer tegnene lengst til venstre i en tekstverdi
+LEN			= LENGDE				##	Returnerer antall tegn i en tekststreng
+LENB			= LENGDEB				##	Returnerer antall tegn i en tekststreng
+LOWER			= SMÅ					##	Konverterer tekst til små bokstaver
+MID			= DELTEKST				##	Returnerer et angitt antall tegn fra en tekststreng, og begynner fra posisjonen du angir
+MIDB			= DELTEKSTB				##	Returnerer et angitt antall tegn fra en tekststreng, og begynner fra posisjonen du angir
+PHONETIC		= FURIGANA				##	Trekker ut fonetiske tegn (furigana) fra en tekststreng
+PROPER			= STOR.FORBOKSTAV			##	Gir den første bokstaven i hvert ord i en tekstverdi stor forbokstav
+REPLACE			= ERSTATT				##	Erstatter tegn i en tekst
+REPLACEB		= ERSTATTB				##	Erstatter tegn i en tekst
+REPT			= GJENTA				##	Gjentar tekst et gitt antall ganger
+RIGHT			= HØYRE					##	Returnerer tegnene lengst til høyre i en tekstverdi
+RIGHTB			= HØYREB				##	Returnerer tegnene lengst til høyre i en tekstverdi
+SEARCH			= SØK					##	Finner en tekstverdi inne i en annen (skiller ikke mellom store og små bokstaver)
+SEARCHB			= SØKB					##	Finner en tekstverdi inne i en annen (skiller ikke mellom store og små bokstaver)
+SUBSTITUTE		= BYTT.UT				##	Bytter ut gammel tekst med ny tekst i en tekststreng
+T			= T					##	Konverterer argumentene til tekst
+TEXT			= TEKST					##	Formaterer et tall og konverterer det til tekst
+TRIM			= TRIMME				##	Fjerner mellomrom fra tekst
+UPPER			= STORE					##	Konverterer tekst til store bokstaver
+VALUE			= VERDI					##	Konverterer et tekstargument til et tall
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pl/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pl/config
new file mode 100755
index 0000000..00f8b9a
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pl/config
@@ -0,0 +1,24 @@
+##
+## PhpSpreadsheet
+##
+
+ArgumentSeparator	= ;
+
+
+##
+##	(For future use)
+##
+currencySymbol	= zł
+
+
+##
+##	Excel Error Codes	(For future use)
+
+##
+NULL	= #ZERO!
+DIV0	= #DZIEL/0!
+VALUE	= #ARG!
+REF	= #ADR!
+NAME	= #NAZWA?
+NUM	= #LICZBA!
+NA	= #N/D!
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pl/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pl/functions
new file mode 100755
index 0000000..907a4ff
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pl/functions
@@ -0,0 +1,416 @@
+##
+## PhpSpreadsheet
+##
+## Data in this file derived from https://www.excel-function-translation.com/
+##
+##
+
+
+##
+##	Add-in and Automation functions			Funkcje dodatków i automatyzacji
+##
+GETPIVOTDATA		= WEŹDANETABELI			##	Zwraca dane przechowywane w raporcie tabeli przestawnej.
+
+
+##
+##	Cube functions					Funkcje modułów
+##
+CUBEKPIMEMBER		= ELEMENT.KPI.MODUŁU		##	Zwraca nazwę, właściwość i miarę kluczowego wskaźnika wydajności (KPI) oraz wyświetla nazwę i właściwość w komórce. Wskaźnik KPI jest miarą ilościową, taką jak miesięczny zysk brutto lub kwartalna fluktuacja pracowników, używaną do monitorowania wydajności organizacji.
+CUBEMEMBER		= ELEMENT.MODUŁU		##	Zwraca element lub krotkę z hierarchii modułu. Służy do sprawdzania, czy element lub krotka istnieje w module.
+CUBEMEMBERPROPERTY	= WŁAŚCIWOŚĆ.ELEMENTU.MODUŁU	##	Zwraca wartość właściwości elementu w module. Służy do sprawdzania, czy nazwa elementu istnieje w module, i zwracania określonej właściwości dla tego elementu.
+CUBERANKEDMEMBER	= USZEREGOWANY.ELEMENT.MODUŁU	##	Zwraca n-ty (albo uszeregowany) element zestawu. Służy do zwracania elementu lub elementów zestawu, na przykład najlepszego sprzedawcy lub 10 najlepszych studentów.
+CUBESET			= ZESTAW.MODUŁÓW		##	Definiuje obliczony zestaw elementów lub krotek, wysyłając wyrażenie zestawu do serwera modułu, który tworzy zestaw i zwraca go do programu Microsoft Office Excel.
+CUBESETCOUNT		= LICZNIK.MODUŁÓW.ZESTAWU	##	Zwraca liczbę elementów zestawu.
+CUBEVALUE		= WARTOŚĆ.MODUŁU		##	Zwraca zagregowaną wartość z modułu.
+
+
+##
+##	Database functions				Funkcje baz danych
+##
+DAVERAGE		= BD.ŚREDNIA			##	Zwraca wartość średniej wybranych wpisów bazy danych.
+DCOUNT			= BD.ILE.REKORDÓW		##	Zlicza komórki zawierające liczby w bazie danych.
+DCOUNTA			= BD.ILE.REKORDÓW.A		##	Zlicza niepuste komórki w bazie danych.
+DGET			= BD.POLE			##	Wyodrębnia z bazy danych jeden rekord spełniający określone kryteria.
+DMAX			= BD.MAX			##	Zwraca wartość maksymalną z wybranych wpisów bazy danych.
+DMIN			= BD.MIN			##	Zwraca wartość minimalną z wybranych wpisów bazy danych.
+DPRODUCT		= BD.ILOCZYN			##	Mnoży wartości w konkretnym, spełniającym kryteria polu rekordów bazy danych.
+DSTDEV			= BD.ODCH.STANDARD		##	Szacuje odchylenie standardowe na podstawie próbki z wybranych wpisów bazy danych.
+DSTDEVP			= BD.ODCH.STANDARD.POPUL	##	Oblicza odchylenie standardowe na podstawie całej populacji wybranych wpisów bazy danych.
+DSUM			= BD.SUMA			##	Dodaje liczby w kolumnie pól rekordów bazy danych, które spełniają kryteria.
+DVAR			= BD.WARIANCJA			##	Szacuje wariancję na podstawie próbki z wybranych wpisów bazy danych.
+DVARP			= BD.WARIANCJA.POPUL		##	Oblicza wariancję na podstawie całej populacji wybranych wpisów bazy danych.
+
+
+##
+##	Date and time functions				Funkcje dat, godzin i czasu
+##
+DATE			= DATA				##	Zwraca liczbę seryjną dla wybranej daty.
+DATEVALUE		= DATA.WARTOŚĆ			##	Konwertuje datę w formie tekstu na liczbę seryjną.
+DAY			= DZIEŃ				##	Konwertuje liczbę seryjną na dzień miesiąca.
+DAYS360			= DNI.360			##	Oblicza liczbę dni między dwiema datami na podstawie roku 360-dniowego.
+EDATE			= UPŁDNI			##	Zwraca liczbę seryjną daty jako wskazaną liczbę miesięcy przed określoną datą początkową lub po niej.
+EOMONTH			= EOMONTH			##	Zwraca liczbę seryjną ostatniego dnia miesiąca przed określoną liczbą miesięcy lub po niej.
+HOUR			= GODZINA			##	Konwertuje liczbę seryjną na godzinę.
+MINUTE			= MINUTA			##	Konwertuje liczbę seryjną na minutę.
+MONTH			= MIESIĄC			##	Konwertuje liczbę seryjną na miesiąc.
+NETWORKDAYS		= NETWORKDAYS			##	Zwraca liczbę pełnych dni roboczych między dwiema datami.
+NOW			= TERAZ				##	Zwraca liczbę seryjną bieżącej daty i godziny.
+SECOND			= SEKUNDA			##	Konwertuje liczbę seryjną na sekundę.
+TIME			= CZAS				##	Zwraca liczbę seryjną określonego czasu.
+TIMEVALUE		= CZAS.WARTOŚĆ			##	Konwertuje czas w formie tekstu na liczbę seryjną.
+TODAY			= DZIŚ				##	Zwraca liczbę seryjną dla daty bieżącej.
+WEEKDAY			= DZIEŃ.TYG			##	Konwertuje liczbę seryjną na dzień tygodnia.
+WEEKNUM			= WEEKNUM			##	Konwertuje liczbę seryjną na liczbę reprezentującą numer tygodnia w roku.
+WORKDAY			= WORKDAY			##	Zwraca liczbę seryjną dla daty przed określoną liczbą dni roboczych lub po niej.
+YEAR			= ROK				##	Konwertuje liczbę seryjną na rok.
+YEARFRAC		= YEARFRAC			##	Zwraca część roku reprezentowaną przez pełną liczbę dni między datą początkową a datą końcową.
+
+
+##
+##	Engineering functions				Funkcje inżynierskie
+##
+BESSELI			= BESSELI			##	Zwraca wartość zmodyfikowanej funkcji Bessela In(x).
+BESSELJ			= BESSELJ			##	Zwraca wartość funkcji Bessela Jn(x).
+BESSELK			= BESSELK			##	Zwraca wartość zmodyfikowanej funkcji Bessela Kn(x).
+BESSELY			= BESSELY			##	Zwraca wartość funkcji Bessela Yn(x).
+BIN2DEC			= BIN2DEC			##	Konwertuje liczbę w postaci dwójkowej na liczbę w postaci dziesiętnej.
+BIN2HEX			= BIN2HEX			##	Konwertuje liczbę w postaci dwójkowej na liczbę w postaci szesnastkowej.
+BIN2OCT			= BIN2OCT			##	Konwertuje liczbę w postaci dwójkowej na liczbę w postaci ósemkowej.
+COMPLEX			= COMPLEX			##	Konwertuje część rzeczywistą i urojoną na liczbę zespoloną.
+CONVERT			= CONVERT			##	Konwertuje liczbę z jednego systemu miar na inny.
+DEC2BIN			= DEC2BIN			##	Konwertuje liczbę w postaci dziesiętnej na postać dwójkową.
+DEC2HEX			= DEC2HEX			##	Konwertuje liczbę w postaci dziesiętnej na liczbę w postaci szesnastkowej.
+DEC2OCT			= DEC2OCT			##	Konwertuje liczbę w postaci dziesiętnej na liczbę w postaci ósemkowej.
+DELTA			= DELTA				##	Sprawdza, czy dwie wartości są równe.
+ERF			= ERF				##	Zwraca wartość funkcji błędu.
+ERFC			= ERFC				##	Zwraca wartość komplementarnej funkcji błędu.
+GESTEP			= GESTEP			##	Sprawdza, czy liczba jest większa niż wartość progowa.
+HEX2BIN			= HEX2BIN			##	Konwertuje liczbę w postaci szesnastkowej na liczbę w postaci dwójkowej.
+HEX2DEC			= HEX2DEC			##	Konwertuje liczbę w postaci szesnastkowej na liczbę w postaci dziesiętnej.
+HEX2OCT			= HEX2OCT			##	Konwertuje liczbę w postaci szesnastkowej na liczbę w postaci ósemkowej.
+IMABS			= IMABS				##	Zwraca wartość bezwzględną (moduł) liczby zespolonej.
+IMAGINARY		= IMAGINARY			##	Zwraca wartość części urojonej liczby zespolonej.
+IMARGUMENT		= IMARGUMENT			##	Zwraca wartość argumentu liczby zespolonej, przy czym kąt wyrażony jest w radianach.
+IMCONJUGATE		= IMCONJUGATE			##	Zwraca wartość liczby sprzężonej danej liczby zespolonej.
+IMCOS			= IMCOS				##	Zwraca wartość cosinusa liczby zespolonej.
+IMDIV			= IMDIV				##	Zwraca wartość ilorazu dwóch liczb zespolonych.
+IMEXP			= IMEXP				##	Zwraca postać wykładniczą liczby zespolonej.
+IMLN			= IMLN				##	Zwraca wartość logarytmu naturalnego liczby zespolonej.
+IMLOG10			= IMLOG10			##	Zwraca wartość logarytmu dziesiętnego liczby zespolonej.
+IMLOG2			= IMLOG2			##	Zwraca wartość logarytmu liczby zespolonej przy podstawie 2.
+IMPOWER			= IMPOWER			##	Zwraca wartość liczby zespolonej podniesionej do potęgi całkowitej.
+IMPRODUCT		= IMPRODUCT			##	Zwraca wartość iloczynu liczb zespolonych.
+IMREAL			= IMREAL			##	Zwraca wartość części rzeczywistej liczby zespolonej.
+IMSIN			= IMSIN				##	Zwraca wartość sinusa liczby zespolonej.
+IMSQRT			= IMSQRT			##	Zwraca wartość pierwiastka kwadratowego z liczby zespolonej.
+IMSUB			= IMSUB				##	Zwraca wartość różnicy dwóch liczb zespolonych.
+IMSUM			= IMSUM				##	Zwraca wartość sumy liczb zespolonych.
+OCT2BIN			= OCT2BIN			##	Konwertuje liczbę w postaci ósemkowej na liczbę w postaci dwójkowej.
+OCT2DEC			= OCT2DEC			##	Konwertuje liczbę w postaci ósemkowej na liczbę w postaci dziesiętnej.
+OCT2HEX			= OCT2HEX			##	Konwertuje liczbę w postaci ósemkowej na liczbę w postaci szesnastkowej.
+
+
+##
+##	Financial functions				Funkcje finansowe
+##
+ACCRINT			= ACCRINT			##	Zwraca narosłe odsetki dla papieru wartościowego z oprocentowaniem okresowym.
+ACCRINTM		= ACCRINTM			##	Zwraca narosłe odsetki dla papieru wartościowego z oprocentowaniem w terminie wykupu.
+AMORDEGRC		= AMORDEGRC			##	Zwraca amortyzację dla każdego okresu rozliczeniowego z wykorzystaniem współczynnika amortyzacji.
+AMORLINC		= AMORLINC			##	Zwraca amortyzację dla każdego okresu rozliczeniowego.
+COUPDAYBS		= COUPDAYBS			##	Zwraca liczbę dni od początku okresu dywidendy do dnia rozliczeniowego.
+COUPDAYS		= COUPDAYS			##	Zwraca liczbę dni w okresie dywidendy, z uwzględnieniem dnia rozliczeniowego.
+COUPDAYSNC		= COUPDAYSNC			##	Zwraca liczbę dni od dnia rozliczeniowego do daty następnego dnia dywidendy.
+COUPNCD			= COUPNCD			##	Zwraca dzień następnej dywidendy po dniu rozliczeniowym.
+COUPNUM			= COUPNUM			##	Zwraca liczbę dywidend płatnych między dniem rozliczeniowym a dniem wykupu.
+COUPPCD			= COUPPCD			##	Zwraca dzień poprzedniej dywidendy przed dniem rozliczeniowym.
+CUMIPMT			= CUMIPMT			##	Zwraca wartość procentu składanego płatnego między dwoma okresami.
+CUMPRINC		= CUMPRINC			##	Zwraca wartość kapitału skumulowanego spłaty pożyczki między dwoma okresami.
+DB			= DB				##	Zwraca amortyzację środka trwałego w danym okresie metodą degresywną z zastosowaniem stałej bazowej.
+DDB			= DDB				##	Zwraca amortyzację środka trwałego za podany okres metodą degresywną z zastosowaniem podwójnej bazowej lub metodą określoną przez użytkownika.
+DISC			= DISC				##	Zwraca wartość stopy dyskontowej papieru wartościowego.
+DOLLARDE		= DOLLARDE			##	Konwertuje cenę w postaci ułamkowej na cenę wyrażoną w postaci dziesiętnej.
+DOLLARFR		= DOLLARFR			##	Konwertuje cenę wyrażoną w postaci dziesiętnej na cenę wyrażoną w postaci ułamkowej.
+DURATION		= DURATION			##	Zwraca wartość rocznego przychodu z papieru wartościowego o okresowych wypłatach oprocentowania.
+EFFECT			= EFFECT			##	Zwraca wartość efektywnej rocznej stopy procentowej.
+FV			= FV				##	Zwraca przyszłą wartość lokaty.
+FVSCHEDULE		= FVSCHEDULE			##	Zwraca przyszłą wartość kapitału początkowego wraz z szeregiem procentów składanych.
+INTRATE			= INTRATE			##	Zwraca wartość stopy procentowej papieru wartościowego całkowicie ulokowanego.
+IPMT			= IPMT				##	Zwraca wysokość spłaty oprocentowania lokaty za dany okres.
+IRR			= IRR				##	Zwraca wartość wewnętrznej stopy zwrotu dla serii przepływów gotówkowych.
+ISPMT			= ISPMT				##	Oblicza wysokość spłaty oprocentowania za dany okres lokaty.
+MDURATION		= MDURATION			##	Zwraca wartość zmodyfikowanego okresu Macauleya dla papieru wartościowego o założonej wartości nominalnej 100 zł.
+MIRR			= MIRR				##	Zwraca wartość wewnętrznej stopy zwrotu dla przypadku, gdy dodatnie i ujemne przepływy gotówkowe mają różne stopy.
+NOMINAL			= NOMINAL			##	Zwraca wysokość nominalnej rocznej stopy procentowej.
+NPER			= NPER				##	Zwraca liczbę okresów dla lokaty.
+NPV			= NPV				##	Zwraca wartość bieżącą netto lokaty na podstawie szeregu okresowych przepływów gotówkowych i stopy dyskontowej.
+ODDFPRICE		= ODDFPRICE			##	Zwraca cenę za 100 zł wartości nominalnej papieru wartościowego z nietypowym pierwszym okresem.
+ODDFYIELD		= ODDFYIELD			##	Zwraca rentowność papieru wartościowego z nietypowym pierwszym okresem.
+ODDLPRICE		= ODDLPRICE			##	Zwraca cenę za 100 zł wartości nominalnej papieru wartościowego z nietypowym ostatnim okresem.
+ODDLYIELD		= ODDLYIELD			##	Zwraca rentowność papieru wartościowego z nietypowym ostatnim okresem.
+PMT			= PMT				##	Zwraca wartość okresowej płatności raty rocznej.
+PPMT			= PPMT				##	Zwraca wysokość spłaty kapitału w przypadku lokaty dla danego okresu.
+PRICE			= PRICE				##	Zwraca cenę za 100 zł wartości nominalnej papieru wartościowego z oprocentowaniem okresowym.
+PRICEDISC		= PRICEDISC			##	Zwraca cenę za 100 zł wartości nominalnej papieru wartościowego zdyskontowanego.
+PRICEMAT		= PRICEMAT			##	Zwraca cenę za 100 zł wartości nominalnej papieru wartościowego z oprocentowaniem w terminie wykupu.
+PV			= PV				##	Zwraca wartość bieżącą lokaty.
+RATE			= RATE				##	Zwraca wysokość stopy procentowej w okresie raty rocznej.
+RECEIVED		= RECEIVED			##	Zwraca wartość kapitału otrzymanego przy wykupie papieru wartościowego całkowicie ulokowanego.
+SLN			= SLN				##	Zwraca amortyzację środka trwałego za jeden okres metodą liniową.
+SYD			= SYD				##	Zwraca amortyzację środka trwałego za dany okres metodą sumy cyfr lat amortyzacji.
+TBILLEQ			= TBILLEQ			##	Zwraca rentowność ekwiwalentu obligacji dla bonu skarbowego.
+TBILLPRICE		= TBILLPRICE			##	Zwraca cenę za 100 zł wartości nominalnej bonu skarbowego.
+TBILLYIELD		= TBILLYIELD			##	Zwraca rentowność bonu skarbowego.
+VDB			= VDB				##	Oblicza amortyzację środka trwałego w danym okresie lub jego części metodą degresywną.
+XIRR			= XIRR				##	Zwraca wartość wewnętrznej stopy zwrotu dla serii rozłożonych w czasie przepływów gotówkowych, niekoniecznie okresowych.
+XNPV			= XNPV				##	Zwraca wartość bieżącą netto dla serii rozłożonych w czasie przepływów gotówkowych, niekoniecznie okresowych.
+YIELD			= YIELD				##	Zwraca rentowność papieru wartościowego z oprocentowaniem okresowym.
+YIELDDISC		= YIELDDISC			##	Zwraca roczną rentowność zdyskontowanego papieru wartościowego, na przykład bonu skarbowego.
+YIELDMAT		= YIELDMAT			##	Zwraca roczną rentowność papieru wartościowego oprocentowanego przy wykupie.
+
+
+##
+##	Information functions				Funkcje informacyjne
+##
+CELL			= KOMÓRKA			##	Zwraca informacje o formacie, położeniu lub zawartości komórki.
+ERROR.TYPE		= NR.BŁĘDU			##	Zwraca liczbę odpowiadającą typowi błędu.
+INFO			= INFO				##	Zwraca informację o aktualnym środowisku pracy.
+ISBLANK			= CZY.PUSTA			##	Zwraca wartość PRAWDA, jeśli wartość jest pusta.
+ISERR			= CZY.BŁ			##	Zwraca wartość PRAWDA, jeśli wartość jest dowolną wartością błędu, z wyjątkiem #N/D!.
+ISERROR			= CZY.BŁĄD			##	Zwraca wartość PRAWDA, jeśli wartość jest dowolną wartością błędu.
+ISEVEN			= ISEVEN			##	Zwraca wartość PRAWDA, jeśli liczba jest parzysta.
+ISLOGICAL		= CZY.LOGICZNA			##	Zwraca wartość PRAWDA, jeśli wartość jest wartością logiczną.
+ISNA			= CZY.BRAK			##	Zwraca wartość PRAWDA, jeśli wartość jest wartością błędu #N/D!.
+ISNONTEXT		= CZY.NIE.TEKST			##	Zwraca wartość PRAWDA, jeśli wartość nie jest tekstem.
+ISNUMBER		= CZY.LICZBA			##	Zwraca wartość PRAWDA, jeśli wartość jest liczbą.
+ISODD			= ISODD				##	Zwraca wartość PRAWDA, jeśli liczba jest nieparzysta.
+ISREF			= CZY.ADR			##	Zwraca wartość PRAWDA, jeśli wartość jest odwołaniem.
+ISTEXT			= CZY.TEKST			##	Zwraca wartość PRAWDA, jeśli wartość jest tekstem.
+N			= L				##	Zwraca wartość przekonwertowaną na postać liczbową.
+NA			= BRAK				##	Zwraca wartość błędu #N/D!.
+TYPE			= TYP				##	Zwraca liczbę wskazującą typ danych wartości.
+
+
+##
+##	Logical functions				Funkcje logiczne
+##
+AND			= ORAZ				##	Zwraca wartość PRAWDA, jeśli wszystkie argumenty mają wartość PRAWDA.
+FALSE			= FAŁSZ				##	Zwraca wartość logiczną FAŁSZ.
+IF			= JEŻELI			##	Określa warunek logiczny do sprawdzenia.
+IFERROR			= JEŻELI.BŁĄD			##	Zwraca określoną wartość, jeśli wynikiem obliczenia formuły jest błąd; w przeciwnym przypadku zwraca wynik formuły.
+NOT			= NIE				##	Odwraca wartość logiczną argumentu.
+OR			= LUB				##	Zwraca wartość PRAWDA, jeśli co najmniej jeden z argumentów ma wartość PRAWDA.
+TRUE			= PRAWDA			##	Zwraca wartość logiczną PRAWDA.
+
+
+##
+##	Lookup and reference functions			Funkcje wyszukiwania i odwołań
+##
+ADDRESS			= ADRES				##	Zwraca odwołanie do jednej komórki w arkuszu jako wartość tekstową.
+AREAS			= OBSZARY			##	Zwraca liczbę obszarów występujących w odwołaniu.
+CHOOSE			= WYBIERZ			##	Wybiera wartość z listy wartości.
+COLUMN			= NR.KOLUMNY			##	Zwraca numer kolumny z odwołania.
+COLUMNS			= LICZBA.KOLUMN			##	Zwraca liczbę kolumn dla danego odwołania.
+HLOOKUP			= WYSZUKAJ.POZIOMO		##	Przegląda górny wiersz tablicy i zwraca wartość wskazanej komórki.
+HYPERLINK		= HIPERŁĄCZE			##	Tworzy skrót lub skok, który pozwala otwierać dokument przechowywany na serwerze sieciowym, w sieci intranet lub w Internecie.
+INDEX			= INDEKS			##	Używa indeksu do wybierania wartości z odwołania lub tablicy.
+INDIRECT		= ADR.POŚR			##	Zwraca odwołanie określone przez wartość tekstową.
+LOOKUP			= WYSZUKAJ			##	Wyszukuje wartości w wektorze lub tablicy.
+MATCH			= PODAJ.POZYCJĘ			##	Wyszukuje wartości w odwołaniu lub w tablicy.
+OFFSET			= PRZESUNIĘCIE			##	Zwraca adres przesunięty od danego odwołania.
+ROW			= WIERSZ			##	Zwraca numer wiersza odwołania.
+ROWS			= ILE.WIERSZY			##	Zwraca liczbę wierszy dla danego odwołania.
+RTD			= RTD				##	Pobiera dane w czasie rzeczywistym z programu obsługującego automatyzację COM (Automatyzacja: Sposób pracy z obiektami aplikacji pochodzącymi z innej aplikacji lub narzędzia projektowania. Nazywana wcześniej Automatyzacją OLE, Automatyzacja jest standardem przemysłowym i funkcją obiektowego modelu składników (COM, Component Object Model).).
+TRANSPOSE		= TRANSPONUJ			##	Zwraca transponowaną tablicę.
+VLOOKUP			= WYSZUKAJ.PIONOWO		##	Przeszukuje pierwszą kolumnę tablicy i przechodzi wzdłuż wiersza, aby zwrócić wartość komórki.
+
+
+##
+##	Math and trigonometry functions			Funkcje matematyczne i trygonometryczne
+##
+ABS			= MODUŁ.LICZBY			##	Zwraca wartość absolutną liczby.
+ACOS			= ACOS				##	Zwraca arcus cosinus liczby.
+ACOSH			= ACOSH				##	Zwraca arcus cosinus hiperboliczny liczby.
+ASIN			= ASIN				##	Zwraca arcus sinus liczby.
+ASINH			= ASINH				##	Zwraca arcus sinus hiperboliczny liczby.
+ATAN			= ATAN				##	Zwraca arcus tangens liczby.
+ATAN2			= ATAN2				##	Zwraca arcus tangens liczby na podstawie współrzędnych x i y.
+ATANH			= ATANH				##	Zwraca arcus tangens hiperboliczny liczby.
+CEILING			= ZAOKR.W.GÓRĘ			##	Zaokrągla liczbę do najbliższej liczby całkowitej lub do najbliższej wielokrotności dokładności.
+COMBIN			= KOMBINACJE			##	Zwraca liczbę kombinacji dla danej liczby obiektów.
+COS			= COS				##	Zwraca cosinus liczby.
+COSH			= COSH				##	Zwraca cosinus hiperboliczny liczby.
+DEGREES			= STOPNIE			##	Konwertuje radiany na stopnie.
+EVEN			= ZAOKR.DO.PARZ			##	Zaokrągla liczbę w górę do najbliższej liczby parzystej.
+EXP			= EXP				##	Zwraca wartość liczby e podniesionej do potęgi określonej przez podaną liczbę.
+FACT			= SILNIA			##	Zwraca silnię liczby.
+FACTDOUBLE		= FACTDOUBLE			##	Zwraca podwójną silnię liczby.
+FLOOR			= ZAOKR.W.DÓŁ			##	Zaokrągla liczbę w dół, w kierunku zera.
+GCD			= GCD				##	Zwraca największy wspólny dzielnik.
+INT			= ZAOKR.DO.CAŁK			##	Zaokrągla liczbę w dół do najbliższej liczby całkowitej.
+LCM			= LCM				##	Zwraca najmniejszą wspólną wielokrotność.
+LN			= LN				##	Zwraca logarytm naturalny podanej liczby.
+LOG			= LOG				##	Zwraca logarytm danej liczby przy zadanej podstawie.
+LOG10			= LOG10				##	Zwraca logarytm dziesiętny liczby.
+MDETERM			= WYZNACZNIK.MACIERZY		##	Zwraca wyznacznik macierzy tablicy.
+MINVERSE		= MACIERZ.ODW			##	Zwraca odwrotność macierzy tablicy.
+MMULT			= MACIERZ.ILOCZYN		##	Zwraca iloczyn macierzy dwóch tablic.
+MOD			= MOD				##	Zwraca resztę z dzielenia.
+MROUND			= MROUND			##	Zwraca liczbę zaokrągloną do żądanej wielokrotności.
+MULTINOMIAL		= MULTINOMIAL			##	Zwraca wielomian dla zbioru liczb.
+ODD			= ZAOKR.DO.NPARZ		##	Zaokrągla liczbę w górę do najbliższej liczby nieparzystej.
+PI			= PI				##	Zwraca wartość liczby Pi.
+POWER			= POTĘGA			##	Zwraca liczbę podniesioną do potęgi.
+PRODUCT			= ILOCZYN			##	Mnoży argumenty.
+QUOTIENT		= QUOTIENT			##	Zwraca iloraz (całkowity).
+RADIANS			= RADIANY			##	Konwertuje stopnie na radiany.
+RAND			= LOS				##	Zwraca liczbę pseudolosową z zakresu od 0 do 1.
+RANDBETWEEN		= RANDBETWEEN			##	Zwraca liczbę pseudolosową z zakresu określonego przez podane argumenty.
+ROMAN			= RZYMSKIE			##	Konwertuje liczbę arabską na rzymską jako tekst.
+ROUND			= ZAOKR				##	Zaokrągla liczbę do określonej liczby cyfr.
+ROUNDDOWN		= ZAOKR.DÓŁ			##	Zaokrągla liczbę w dół, w kierunku zera.
+ROUNDUP			= ZAOKR.GÓRA			##	Zaokrągla liczbę w górę, w kierunku od zera.
+SERIESSUM		= SERIESSUM			##	Zwraca sumę szeregu potęgowego na podstawie wzoru.
+SIGN			= ZNAK.LICZBY			##	Zwraca znak liczby.
+SIN			= SIN				##	Zwraca sinus danego kąta.
+SINH			= SINH				##	Zwraca sinus hiperboliczny liczby.
+SQRT			= PIERWIASTEK			##	Zwraca dodatni pierwiastek kwadratowy.
+SQRTPI			= SQRTPI			##	Zwraca pierwiastek kwadratowy iloczynu (liczba * Pi).
+SUBTOTAL		= SUMY.POŚREDNIE		##	Zwraca sumę częściową listy lub bazy danych.
+SUM			= SUMA				##	Dodaje argumenty.
+SUMIF			= SUMA.JEŻELI			##	Dodaje komórki określone przez podane kryterium.
+SUMIFS			= SUMA.WARUNKÓW			##	Dodaje komórki w zakresie, które spełniają wiele kryteriów.
+SUMPRODUCT		= SUMA.ILOCZYNÓW		##	Zwraca sumę iloczynów odpowiednich elementów tablicy.
+SUMSQ			= SUMA.KWADRATÓW		##	Zwraca sumę kwadratów argumentów.
+SUMX2MY2		= SUMA.X2.M.Y2			##	Zwraca sumę różnic kwadratów odpowiednich wartości w dwóch tablicach.
+SUMX2PY2		= SUMA.X2.P.Y2			##	Zwraca sumę sum kwadratów odpowiednich wartości w dwóch tablicach.
+SUMXMY2			= SUMA.XMY.2			##	Zwraca sumę kwadratów różnic odpowiednich wartości w dwóch tablicach.
+TAN			= TAN				##	Zwraca tangens liczby.
+TANH			= TANH				##	Zwraca tangens hiperboliczny liczby.
+TRUNC			= LICZBA.CAŁK			##	Przycina liczbę do wartości całkowitej.
+
+
+##
+##	Statistical functions				Funkcje statystyczne
+##
+AVEDEV			= ODCH.ŚREDNIE			##	Zwraca średnią wartość odchyleń absolutnych punktów danych od ich wartości średniej.
+AVERAGE			= ŚREDNIA			##	Zwraca wartość średnią argumentów.
+AVERAGEA		= ŚREDNIA.A			##	Zwraca wartość średnią argumentów, z uwzględnieniem liczb, tekstów i wartości logicznych.
+AVERAGEIF		= ŚREDNIA.JEŻELI		##	Zwraca średnią (średnią arytmetyczną) wszystkich komórek w zakresie, które spełniają podane kryteria.
+AVERAGEIFS		= ŚREDNIA.WARUNKÓW		##	Zwraca średnią (średnią arytmetyczną) wszystkich komórek, które spełniają jedno lub więcej kryteriów.
+BETADIST		= ROZKŁAD.BETA			##	Zwraca skumulowaną funkcję gęstości prawdopodobieństwa beta.
+BETAINV			= ROZKŁAD.BETA.ODW		##	Zwraca odwrotność skumulowanej funkcji gęstości prawdopodobieństwa beta.
+BINOMDIST		= ROZKŁAD.DWUM			##	Zwraca pojedynczy składnik dwumianowego rozkładu prawdopodobieństwa.
+CHIDIST			= ROZKŁAD.CHI			##	Zwraca wartość jednostronnego prawdopodobieństwa rozkładu chi-kwadrat.
+CHIINV			= ROZKŁAD.CHI.ODW		##	Zwraca odwrotność wartości jednostronnego prawdopodobieństwa rozkładu chi-kwadrat.
+CHITEST			= TEST.CHI			##	Zwraca test niezależności.
+CONFIDENCE		= UFNOŚĆ			##	Zwraca interwał ufności dla średniej populacji.
+CORREL			= WSP.KORELACJI			##	Zwraca współczynnik korelacji dwóch zbiorów danych.
+COUNT			= ILE.LICZB			##	Zlicza liczby znajdujące się na liście argumentów.
+COUNTA			= ILE.NIEPUSTYCH		##	Zlicza wartości znajdujące się na liście argumentów.
+COUNTBLANK		= LICZ.PUSTE			##	Zwraca liczbę pustych komórek w pewnym zakresie.
+COUNTIF			= LICZ.JEŻELI			##	Zlicza komórki wewnątrz zakresu, które spełniają podane kryteria.
+COUNTIFS		= LICZ.WARUNKI			##	Zlicza komórki wewnątrz zakresu, które spełniają wiele kryteriów.
+COVAR			= KOWARIANCJA			##	Zwraca kowariancję, czyli średnią wartość iloczynów odpowiednich odchyleń.
+CRITBINOM		= PRÓG.ROZKŁAD.DWUM		##	Zwraca najmniejszą wartość, dla której skumulowany rozkład dwumianowy jest mniejszy niż wartość kryterium lub równy jej.
+DEVSQ			= ODCH.KWADRATOWE		##	Zwraca sumę kwadratów odchyleń.
+EXPONDIST		= ROZKŁAD.EXP			##	Zwraca rozkład wykładniczy.
+FDIST			= ROZKŁAD.F			##	Zwraca rozkład prawdopodobieństwa F.
+FINV			= ROZKŁAD.F.ODW			##	Zwraca odwrotność rozkładu prawdopodobieństwa F.
+FISHER			= ROZKŁAD.FISHER		##	Zwraca transformację Fishera.
+FISHERINV		= ROZKŁAD.FISHER.ODW		##	Zwraca odwrotność transformacji Fishera.
+FORECAST		= REGLINX			##	Zwraca wartość trendu liniowego.
+FREQUENCY		= CZĘSTOŚĆ			##	Zwraca rozkład częstotliwości jako tablicę pionową.
+FTEST			= TEST.F			##	Zwraca wynik testu F.
+GAMMADIST		= ROZKŁAD.GAMMA			##	Zwraca rozkład gamma.
+GAMMAINV		= ROZKŁAD.GAMMA.ODW		##	Zwraca odwrotność skumulowanego rozkładu gamma.
+GAMMALN			= ROZKŁAD.LIN.GAMMA		##	Zwraca logarytm naturalny funkcji gamma, Γ(x).
+GEOMEAN			= ŚREDNIA.GEOMETRYCZNA		##	Zwraca średnią geometryczną.
+GROWTH			= REGEXPW			##	Zwraca wartości trendu wykładniczego.
+HARMEAN			= ŚREDNIA.HARMONICZNA		##	Zwraca średnią harmoniczną.
+HYPGEOMDIST		= ROZKŁAD.HIPERGEOM		##	Zwraca rozkład hipergeometryczny.
+INTERCEPT		= ODCIĘTA			##	Zwraca punkt przecięcia osi pionowej z linią regresji liniowej.
+KURT			= KURTOZA			##	Zwraca kurtozę zbioru danych.
+LARGE			= MAX.K				##	Zwraca k-tą największą wartość ze zbioru danych.
+LINEST			= REGLINP			##	Zwraca parametry trendu liniowego.
+LOGEST			= REGEXPP			##	Zwraca parametry trendu wykładniczego.
+LOGINV			= ROZKŁAD.LOG.ODW		##	Zwraca odwrotność rozkładu logarytmu naturalnego.
+LOGNORMDIST		= ROZKŁAD.LOG			##	Zwraca skumulowany rozkład logarytmu naturalnego.
+MAX			= MAX				##	Zwraca maksymalną wartość listy argumentów.
+MAXA			= MAX.A				##	Zwraca maksymalną wartość listy argumentów, z uwzględnieniem liczb, tekstów i wartości logicznych.
+MEDIAN			= MEDIANA			##	Zwraca medianę podanych liczb.
+MIN			= MIN				##	Zwraca minimalną wartość listy argumentów.
+MINA			= MIN.A				##	Zwraca najmniejszą wartość listy argumentów, z uwzględnieniem liczb, tekstów i wartości logicznych.
+MODE			= WYST.NAJCZĘŚCIEJ		##	Zwraca wartość najczęściej występującą w zbiorze danych.
+NEGBINOMDIST		= ROZKŁAD.DWUM.PRZEC		##	Zwraca ujemny rozkład dwumianowy.
+NORMDIST		= ROZKŁAD.NORMALNY		##	Zwraca rozkład normalny skumulowany.
+NORMINV			= ROZKŁAD.NORMALNY.ODW		##	Zwraca odwrotność rozkładu normalnego skumulowanego.
+NORMSDIST		= ROZKŁAD.NORMALNY.S		##	Zwraca standardowy rozkład normalny skumulowany.
+NORMSINV		= ROZKŁAD.NORMALNY.S.ODW	##	Zwraca odwrotność standardowego rozkładu normalnego skumulowanego.
+PEARSON			= PEARSON			##	Zwraca współczynnik korelacji momentu iloczynu Pearsona.
+PERCENTILE		= PERCENTYL			##	Wyznacza k-ty percentyl wartości w zakresie.
+PERCENTRANK		= PROCENT.POZYCJA		##	Zwraca procentową pozycję wartości w zbiorze danych.
+PERMUT			= PERMUTACJE			##	Zwraca liczbę permutacji dla danej liczby obiektów.
+POISSON			= ROZKŁAD.POISSON		##	Zwraca rozkład Poissona.
+PROB			= PRAWDPD			##	Zwraca prawdopodobieństwo, że wartości w zakresie leżą pomiędzy dwiema granicami.
+QUARTILE		= KWARTYL			##	Wyznacza kwartyl zbioru danych.
+RANK			= POZYCJA			##	Zwraca pozycję liczby na liście liczb.
+RSQ			= R.KWADRAT			##	Zwraca kwadrat współczynnika korelacji momentu iloczynu Pearsona.
+SKEW			= SKOŚNOŚĆ			##	Zwraca skośność rozkładu.
+SLOPE			= NACHYLENIE			##	Zwraca nachylenie linii regresji liniowej.
+SMALL			= MIN.K				##	Zwraca k-tą najmniejszą wartość ze zbioru danych.
+STANDARDIZE		= NORMALIZUJ			##	Zwraca wartość znormalizowaną.
+STDEV			= ODCH.STANDARDOWE		##	Szacuje odchylenie standardowe na podstawie próbki.
+STDEVA			= ODCH.STANDARDOWE.A		##	Szacuje odchylenie standardowe na podstawie próbki, z uwzględnieniem liczb, tekstów i wartości logicznych.
+STDEVP			= ODCH.STANDARD.POPUL		##	Oblicza odchylenie standardowe na podstawie całej populacji.
+STDEVPA			= ODCH.STANDARD.POPUL.A		##	Oblicza odchylenie standardowe na podstawie całej populacji, z uwzględnieniem liczb, teksów i wartości logicznych.
+STEYX			= REGBŁSTD			##	Zwraca błąd standardowy przewidzianej wartości y dla każdej wartości x w regresji.
+TDIST			= ROZKŁAD.T			##	Zwraca rozkład t-Studenta.
+TINV			= ROZKŁAD.T.ODW			##	Zwraca odwrotność rozkładu t-Studenta.
+TREND			= REGLINW			##	Zwraca wartości trendu liniowego.
+TRIMMEAN		= ŚREDNIA.WEWN			##	Zwraca średnią wartość dla wnętrza zbioru danych.
+TTEST			= TEST.T			##	Zwraca prawdopodobieństwo związane z testem t-Studenta.
+VAR			= WARIANCJA			##	Szacuje wariancję na podstawie próbki.
+VARA			= WARIANCJA.A			##	Szacuje wariancję na podstawie próbki, z uwzględnieniem liczb, tekstów i wartości logicznych.
+VARP			= WARIANCJA.POPUL		##	Oblicza wariancję na podstawie całej populacji.
+VARPA			= WARIANCJA.POPUL.A		##	Oblicza wariancję na podstawie całej populacji, z uwzględnieniem liczb, tekstów i wartości logicznych.
+WEIBULL			= ROZKŁAD.WEIBULL		##	Zwraca rozkład Weibulla.
+ZTEST			= TEST.Z			##	Zwraca wartość jednostronnego prawdopodobieństwa testu z.
+
+
+##
+##	Text functions					Funkcje tekstowe
+##
+ASC			= ASC				##	Zamienia litery angielskie lub katakana o pełnej szerokości (dwubajtowe) w ciągu znaków na znaki o szerokości połówkowej (jednobajtowe).
+BAHTTEXT		= BAHTTEXT			##	Konwertuje liczbę na tekst, stosując format walutowy ß (baht).
+CHAR			= ZNAK				##	Zwraca znak o podanym numerze kodu.
+CLEAN			= OCZYŚĆ			##	Usuwa z tekstu wszystkie znaki, które nie mogą być drukowane.
+CODE			= KOD				##	Zwraca kod numeryczny pierwszego znaku w ciągu tekstowym.
+CONCATENATE		= ZŁĄCZ.TEKSTY			##	Łączy kilka oddzielnych tekstów w jeden tekst.
+DOLLAR			= KWOTA				##	Konwertuje liczbę na tekst, stosując format walutowy $ (dolar).
+EXACT			= PORÓWNAJ			##	Sprawdza identyczność dwóch wartości tekstowych.
+FIND			= ZNAJDŹ			##	Znajduje jedną wartość tekstową wewnątrz innej (z uwzględnieniem wielkich i małych liter).
+FINDB			= ZNAJDŹB			##	Znajduje jedną wartość tekstową wewnątrz innej (z uwzględnieniem wielkich i małych liter).
+FIXED			= ZAOKR.DO.TEKST		##	Formatuje liczbę jako tekst przy stałej liczbie miejsc dziesiętnych.
+JIS			= JIS				##	Zmienia litery angielskie lub katakana o szerokości połówkowej (jednobajtowe) w ciągu znaków na znaki o pełnej szerokości (dwubajtowe).
+LEFT			= LEWY				##	Zwraca skrajne lewe znaki z wartości tekstowej.
+LEFTB			= LEWYB				##	Zwraca skrajne lewe znaki z wartości tekstowej.
+LEN			= DŁ				##	Zwraca liczbę znaków ciągu tekstowego.
+LENB			= DŁ.B				##	Zwraca liczbę znaków ciągu tekstowego.
+LOWER			= LITERY.MAŁE			##	Konwertuje wielkie litery tekstu na małe litery.
+MID			= FRAGMENT.TEKSTU		##	Zwraca określoną liczbę znaków z ciągu tekstowego, zaczynając od zadanej pozycji.
+MIDB			= FRAGMENT.TEKSTU.B		##	Zwraca określoną liczbę znaków z ciągu tekstowego, zaczynając od zadanej pozycji.
+PHONETIC		= PHONETIC			##	Wybiera znaki fonetyczne (furigana) z ciągu tekstowego.
+PROPER			= Z.WIELKIEJ.LITERY		##	Zastępuje pierwszą literę każdego wyrazu tekstu wielką literą.
+REPLACE			= ZASTĄP			##	Zastępuje znaki w tekście.
+REPLACEB		= ZASTĄP.B			##	Zastępuje znaki w tekście.
+REPT			= POWT				##	Powiela tekst daną liczbę razy.
+RIGHT			= PRAWY				##	Zwraca skrajne prawe znaki z wartości tekstowej.
+RIGHTB			= PRAWYB			##	Zwraca skrajne prawe znaki z wartości tekstowej.
+SEARCH			= SZUKAJ.TEKST			##	Wyszukuje jedną wartość tekstową wewnątrz innej (bez uwzględniania wielkości liter).
+SEARCHB			= SZUKAJ.TEKST.B		##	Wyszukuje jedną wartość tekstową wewnątrz innej (bez uwzględniania wielkości liter).
+SUBSTITUTE		= PODSTAW			##	Podstawia nowy tekst w miejsce poprzedniego tekstu w ciągu tekstowym.
+T			= T				##	Konwertuje argumenty na tekst.
+TEXT			= TEKST				##	Formatuje liczbę i konwertuje ją na tekst.
+TRIM			= USUŃ.ZBĘDNE.ODSTĘPY		##	Usuwa spacje z tekstu.
+UPPER			= LITERY.WIELKIE		##	Konwertuje znaki tekstu na wielkie litery.
+VALUE			= WARTOŚĆ			##	Konwertuje argument tekstowy na liczbę.
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/br/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/br/config
new file mode 100755
index 0000000..904f99f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/br/config
@@ -0,0 +1,24 @@
+##
+## PhpSpreadsheet
+##
+
+ArgumentSeparator	= ;
+
+
+##
+##	(For future use)
+##
+currencySymbol	= R$
+
+
+##
+##	Excel Error Codes	(For future use)
+
+##
+NULL	= #NULO!
+DIV0	= #DIV/0!
+VALUE	= #VALOR!
+REF	= #REF!
+NAME	= #NOME?
+NUM	= #NÚM!
+NA	= #N/D
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/br/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/br/functions
new file mode 100755
index 0000000..a062a7f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/br/functions
@@ -0,0 +1,408 @@
+##
+##	Add-in and Automation functions			Funções Suplemento e Automação
+##
+GETPIVOTDATA		= INFODADOSTABELADINÂMICA	##	Retorna os dados armazenados em um relatório de tabela dinâmica
+
+
+##
+##	Cube functions					Funções de Cubo
+##
+CUBEKPIMEMBER		= MEMBROKPICUBO			##	Retorna o nome de um KPI (indicador de desempenho-chave), uma propriedade e uma medida e exibe o nome e a propriedade na célula. Um KPI é uma medida quantificável, como o lucro bruto mensal ou a rotatividade trimestral dos funcionários, usada para monitorar o desempenho de uma organização.
+CUBEMEMBER		= MEMBROCUBO			##	Retorna um membro ou tupla em uma hierarquia de cubo. Use para validar se o membro ou tupla existe no cubo.
+CUBEMEMBERPROPERTY	= PROPRIEDADEMEMBROCUBO		##	Retorna o valor da propriedade de um membro no cubo. Usada para validar a existência do nome do membro no cubo e para retornar a propriedade especificada para esse membro.
+CUBERANKEDMEMBER	= MEMBROCLASSIFICADOCUBO	##	Retorna o enésimo membro, ou o membro ordenado, em um conjunto. Use para retornar um ou mais elementos em um conjunto, assim como o melhor vendedor ou os dez melhores alunos.
+CUBESET			= CONJUNTOCUBO			##	Define um conjunto calculado de membros ou tuplas enviando uma expressão do conjunto para o cubo no servidor, que cria o conjunto e o retorna para o Microsoft Office Excel.
+CUBESETCOUNT		= CONTAGEMCONJUNTOCUBO		##	Retorna o número de itens em um conjunto.
+CUBEVALUE		= VALORCUBO			##	Retorna um valor agregado de um cubo.
+
+
+##
+##	Database functions				Funções de banco de dados
+##
+DAVERAGE		= BDMÉDIA			##	Retorna a média das entradas selecionadas de um banco de dados
+DCOUNT			= BDCONTAR			##	Conta as células que contêm números em um banco de dados
+DCOUNTA			= BDCONTARA			##	Conta células não vazias em um banco de dados
+DGET			= BDEXTRAIR			##	Extrai de um banco de dados um único registro que corresponde a um critério específico
+DMAX			= BDMÁX				##	Retorna o valor máximo de entradas selecionadas de um banco de dados
+DMIN			= BDMÍN				##	Retorna o valor mínimo de entradas selecionadas de um banco de dados
+DPRODUCT		= BDMULTIPL			##	Multiplica os valores em um campo específico de registros que correspondem ao critério em um banco de dados
+DSTDEV			= BDEST				##	Estima o desvio padrão com base em uma amostra de entradas selecionadas de um banco de dados
+DSTDEVP			= BDDESVPA			##	Calcula o desvio padrão com base na população inteira de entradas selecionadas de um banco de dados
+DSUM			= BDSOMA			##	Adiciona os números à coluna de campos de registros do banco de dados que correspondem ao critério
+DVAR			= BDVAREST			##	Estima a variância com base em uma amostra de entradas selecionadas de um banco de dados
+DVARP			= BDVARP			##	Calcula a variância com base na população inteira de entradas selecionadas de um banco de dados
+
+
+##
+##	Date and time functions				Funções de data e hora
+##
+DATE			= DATA				##	Retorna o número de série de uma data específica
+DATEVALUE		= DATA.VALOR			##	Converte uma data na forma de texto para um número de série
+DAY			= DIA				##	Converte um número de série em um dia do mês
+DAYS360			= DIAS360			##	Calcula o número de dias entre duas datas com base em um ano de 360 dias
+EDATE			= DATAM				##	Retorna o número de série da data que é o número indicado de meses antes ou depois da data inicial
+EOMONTH			= FIMMÊS			##	Retorna o número de série do último dia do mês antes ou depois de um número especificado de meses
+HOUR			= HORA				##	Converte um número de série em uma hora
+MINUTE			= MINUTO			##	Converte um número de série em um minuto
+MONTH			= MÊS				##	Converte um número de série em um mês
+NETWORKDAYS		= DIATRABALHOTOTAL		##	Retorna o número de dias úteis inteiros entre duas datas
+NOW			= AGORA				##	Retorna o número de série seqüencial da data e hora atuais
+SECOND			= SEGUNDO			##	Converte um número de série em um segundo
+TIME			= HORA				##	Retorna o número de série de uma hora específica
+TIMEVALUE		= VALOR.TEMPO			##	Converte um horário na forma de texto para um número de série
+TODAY			= HOJE				##	Retorna o número de série da data de hoje
+WEEKDAY			= DIA.DA.SEMANA			##	Converte um número de série em um dia da semana
+WEEKNUM			= NÚMSEMANA			##	Converte um número de série em um número que representa onde a semana cai numericamente em um ano
+WORKDAY			= DIATRABALHO			##	Retorna o número de série da data antes ou depois de um número específico de dias úteis
+YEAR			= ANO				##	Converte um número de série em um ano
+YEARFRAC		= FRAÇÃOANO			##	Retorna a fração do ano que representa o número de dias entre data_inicial e data_final
+
+
+##
+##	Engineering functions				Funções de engenharia
+##
+BESSELI			= BESSELI			##	Retorna a função de Bessel In(x) modificada
+BESSELJ			= BESSELJ			##	Retorna a função de Bessel Jn(x)
+BESSELK			= BESSELK			##	Retorna a função de Bessel Kn(x) modificada
+BESSELY			= BESSELY			##	Retorna a função de Bessel Yn(x)
+BIN2DEC			= BIN2DEC			##	Converte um número binário em decimal
+BIN2HEX			= BIN2HEX			##	Converte um número binário em hexadecimal
+BIN2OCT			= BIN2OCT			##	Converte um número binário em octal
+COMPLEX			= COMPLEX			##	Converte coeficientes reais e imaginários e um número complexo
+CONVERT			= CONVERTER			##	Converte um número de um sistema de medida para outro
+DEC2BIN			= DECABIN			##	Converte um número decimal em binário
+DEC2HEX			= DECAHEX			##	Converte um número decimal em hexadecimal
+DEC2OCT			= DECAOCT			##	Converte um número decimal em octal
+DELTA			= DELTA				##	Testa se dois valores são iguais
+ERF			= FUNERRO			##	Retorna a função de erro
+ERFC			= FUNERROCOMPL			##	Retorna a função de erro complementar
+GESTEP			= DEGRAU			##	Testa se um número é maior do que um valor limite
+HEX2BIN			= HEXABIN			##	Converte um número hexadecimal em binário
+HEX2DEC			= HEXADEC			##	Converte um número hexadecimal em decimal
+HEX2OCT			= HEXAOCT			##	Converte um número hexadecimal em octal
+IMABS			= IMABS				##	Retorna o valor absoluto (módulo) de um número complexo
+IMAGINARY		= IMAGINÁRIO			##	Retorna o coeficiente imaginário de um número complexo
+IMARGUMENT		= IMARG				##	Retorna o argumento teta, um ângulo expresso em radianos
+IMCONJUGATE		= IMCONJ			##	Retorna o conjugado complexo de um número complexo
+IMCOS			= IMCOS				##	Retorna o cosseno de um número complexo
+IMDIV			= IMDIV				##	Retorna o quociente de dois números complexos
+IMEXP			= IMEXP				##	Retorna o exponencial de um número complexo
+IMLN			= IMLN				##	Retorna o logaritmo natural de um número complexo
+IMLOG10			= IMLOG10			##	Retorna o logaritmo de base 10 de um número complexo
+IMLOG2			= IMLOG2			##	Retorna o logaritmo de base 2 de um número complexo
+IMPOWER			= IMPOT				##	Retorna um número complexo elevado a uma potência inteira
+IMPRODUCT		= IMPROD			##	Retorna o produto de números complexos
+IMREAL			= IMREAL			##	Retorna o coeficiente real de um número complexo
+IMSIN			= IMSENO			##	Retorna o seno de um número complexo
+IMSQRT			= IMRAIZ			##	Retorna a raiz quadrada de um número complexo
+IMSUB			= IMSUBTR			##	Retorna a diferença entre dois números complexos
+IMSUM			= IMSOMA			##	Retorna a soma de números complexos
+OCT2BIN			= OCTABIN			##	Converte um número octal em binário
+OCT2DEC			= OCTADEC			##	Converte um número octal em decimal
+OCT2HEX			= OCTAHEX			##	Converte um número octal em hexadecimal
+
+
+##
+##	Financial functions				Funções financeiras
+##
+ACCRINT			= JUROSACUM			##	Retorna a taxa de juros acumulados de um título que paga uma taxa periódica de juros
+ACCRINTM		= JUROSACUMV			##	Retorna os juros acumulados de um título que paga juros no vencimento
+AMORDEGRC		= AMORDEGRC			##	Retorna a depreciação para cada período contábil usando o coeficiente de depreciação
+AMORLINC		= AMORLINC			##	Retorna a depreciação para cada período contábil
+COUPDAYBS		= CUPDIASINLIQ			##	Retorna o número de dias do início do período de cupom até a data de liquidação
+COUPDAYS		= CUPDIAS			##	Retorna o número de dias no período de cupom que contém a data de quitação
+COUPDAYSNC		= CUPDIASPRÓX			##	Retorna o número de dias da data de liquidação até a data do próximo cupom
+COUPNCD			= CUPDATAPRÓX			##	Retorna a próxima data de cupom após a data de quitação
+COUPNUM			= CUPNÚM			##	Retorna o número de cupons pagáveis entre as datas de quitação e vencimento
+COUPPCD			= CUPDATAANT			##	Retorna a data de cupom anterior à data de quitação
+CUMIPMT			= PGTOJURACUM			##	Retorna os juros acumulados pagos entre dois períodos
+CUMPRINC		= PGTOCAPACUM			##	Retorna o capital acumulado pago sobre um empréstimo entre dois períodos
+DB			= BD				##	Retorna a depreciação de um ativo para um período especificado, usando o método de balanço de declínio fixo
+DDB			= BDD				##	Retorna a depreciação de um ativo com relação a um período especificado usando o método de saldos decrescentes duplos ou qualquer outro método especificado por você
+DISC			= DESC				##	Retorna a taxa de desconto de um título
+DOLLARDE		= MOEDADEC			##	Converte um preço em formato de moeda, na forma fracionária, em um preço na forma decimal
+DOLLARFR		= MOEDAFRA			##	Converte um preço, apresentado na forma decimal, em um preço apresentado na forma fracionária
+DURATION		= DURAÇÃO			##	Retorna a duração anual de um título com pagamentos de juros periódicos
+EFFECT			= EFETIVA			##	Retorna a taxa de juros anual efetiva
+FV			= VF				##	Retorna o valor futuro de um investimento
+FVSCHEDULE		= VFPLANO			##	Retorna o valor futuro de um capital inicial após a aplicação de uma série de taxas de juros compostas
+INTRATE			= TAXAJUROS			##	Retorna a taxa de juros de um título totalmente investido
+IPMT			= IPGTO				##	Retorna o pagamento de juros para um investimento em um determinado período
+IRR			= TIR				##	Retorna a taxa interna de retorno de uma série de fluxos de caixa
+ISPMT			= ÉPGTO				##	Calcula os juros pagos durante um período específico de um investimento
+MDURATION		= MDURAÇÃO			##	Retorna a duração de Macauley modificada para um título com um valor de paridade equivalente a R$ 100
+MIRR			= MTIR				##	Calcula a taxa interna de retorno em que fluxos de caixa positivos e negativos são financiados com diferentes taxas
+NOMINAL			= NOMINAL			##	Retorna a taxa de juros nominal anual
+NPER			= NPER				##	Retorna o número de períodos de um investimento
+NPV			= VPL				##	Retorna o valor líquido atual de um investimento com base em uma série de fluxos de caixa periódicos e em uma taxa de desconto
+ODDFPRICE		= PREÇOPRIMINC			##	Retorna o preço por R$ 100 de valor nominal de um título com um primeiro período indefinido
+ODDFYIELD		= LUCROPRIMINC			##	Retorna o rendimento de um título com um primeiro período indefinido
+ODDLPRICE		= PREÇOÚLTINC			##	Retorna o preço por R$ 100 de valor nominal de um título com um último período de cupom indefinido
+ODDLYIELD		= LUCROÚLTINC			##	Retorna o rendimento de um título com um último período indefinido
+PMT			= PGTO				##	Retorna o pagamento periódico de uma anuidade
+PPMT			= PPGTO				##	Retorna o pagamento de capital para determinado período de investimento
+PRICE			= PREÇO				##	Retorna a preço por R$ 100,00 de valor nominal de um título que paga juros periódicos
+PRICEDISC		= PREÇODESC			##	Retorna o preço por R$ 100,00 de valor nominal de um título descontado
+PRICEMAT		= PREÇOVENC			##	Retorna o preço por R$ 100,00 de valor nominal de um título que paga juros no vencimento
+PV			= VP				##	Retorna o valor presente de um investimento
+RATE			= TAXA				##	Retorna a taxa de juros por período de uma anuidade
+RECEIVED		= RECEBER			##	Retorna a quantia recebida no vencimento de um título totalmente investido
+SLN			= DPD				##	Retorna a depreciação em linha reta de um ativo durante um período
+SYD			= SDA				##	Retorna a depreciação dos dígitos da soma dos anos de um ativo para um período especificado
+TBILLEQ			= OTN				##	Retorna o rendimento de um título equivalente a uma obrigação do Tesouro
+TBILLPRICE		= OTNVALOR			##	Retorna o preço por R$ 100,00 de valor nominal de uma obrigação do Tesouro
+TBILLYIELD		= OTNLUCRO			##	Retorna o rendimento de uma obrigação do Tesouro
+VDB			= BDV				##	Retorna a depreciação de um ativo para um período especificado ou parcial usando um método de balanço declinante
+XIRR			= XTIR				##	Fornece a taxa interna de retorno para um programa de fluxos de caixa que não é necessariamente periódico
+XNPV			= XVPL				##	Retorna o valor presente líquido de um programa de fluxos de caixa que não é necessariamente periódico
+YIELD			= LUCRO				##	Retorna o lucro de um título que paga juros periódicos
+YIELDDISC		= LUCRODESC			##	Retorna o rendimento anual de um título descontado. Por exemplo, uma obrigação do Tesouro
+YIELDMAT		= LUCROVENC			##	Retorna o lucro anual de um título que paga juros no vencimento
+
+
+##
+##	Information functions				Funções de informação
+##
+CELL			= CÉL				##	Retorna informações sobre formatação, localização ou conteúdo de uma célula
+ERROR.TYPE		= TIPO.ERRO			##	Retorna um número correspondente a um tipo de erro
+INFO			= INFORMAÇÃO			##	Retorna informações sobre o ambiente operacional atual
+ISBLANK			= ÉCÉL.VAZIA			##	Retorna VERDADEIRO se o valor for vazio
+ISERR			= ÉERRO				##	Retorna VERDADEIRO se o valor for um valor de erro diferente de #N/D
+ISERROR			= ÉERROS			##	Retorna VERDADEIRO se o valor for um valor de erro
+ISEVEN			= ÉPAR				##	Retorna VERDADEIRO se o número for par
+ISLOGICAL		= ÉLÓGICO			##	Retorna VERDADEIRO se o valor for um valor lógico
+ISNA			= É.NÃO.DISP			##	Retorna VERDADEIRO se o valor for o valor de erro #N/D
+ISNONTEXT		= É.NÃO.TEXTO			##	Retorna VERDADEIRO se o valor for diferente de texto
+ISNUMBER		= ÉNÚM				##	Retorna VERDADEIRO se o valor for um número
+ISODD			= ÉIMPAR			##	Retorna VERDADEIRO se o número for ímpar
+ISREF			= ÉREF				##	Retorna VERDADEIRO se o valor for uma referência
+ISTEXT			= ÉTEXTO			##	Retorna VERDADEIRO se o valor for texto
+N			= N				##	Retorna um valor convertido em um número
+NA			= NÃO.DISP			##	Retorna o valor de erro #N/D
+TYPE			= TIPO				##	Retorna um número indicando o tipo de dados de um valor
+
+
+##
+##	Logical functions				Funções lógicas
+##
+AND			= E				##	Retorna VERDADEIRO se todos os seus argumentos forem VERDADEIROS
+FALSE			= FALSO				##	Retorna o valor lógico FALSO
+IF			= SE				##	Especifica um teste lógico a ser executado
+IFERROR			= SEERRO			##	Retornará um valor que você especifica se uma fórmula for avaliada para um erro; do contrário, retornará o resultado da fórmula
+NOT			= NÃO				##	Inverte o valor lógico do argumento
+OR			= OU				##	Retorna VERDADEIRO se um dos argumentos for VERDADEIRO
+TRUE			= VERDADEIRO			##	Retorna o valor lógico VERDADEIRO
+
+
+##
+##	Lookup and reference functions			Funções de pesquisa e referência
+##
+ADDRESS			= ENDEREÇO			##	Retorna uma referência como texto para uma única célula em uma planilha
+AREAS			= ÁREAS				##	Retorna o número de áreas em uma referência
+CHOOSE			= ESCOLHER			##	Escolhe um valor a partir de uma lista de valores
+COLUMN			= COL				##	Retorna o número da coluna de uma referência
+COLUMNS			= COLS				##	Retorna o número de colunas em uma referência
+HLOOKUP			= PROCH				##	Procura na linha superior de uma matriz e retorna o valor da célula especificada
+HYPERLINK		= HYPERLINK			##	Cria um atalho ou salto que abre um documento armazenado em um servidor de rede, uma intranet ou na Internet
+INDEX			= ÍNDICE			##	Usa um índice para escolher um valor de uma referência ou matriz
+INDIRECT		= INDIRETO			##	Retorna uma referência indicada por um valor de texto
+LOOKUP			= PROC				##	Procura valores em um vetor ou em uma matriz
+MATCH			= CORRESP			##	Procura valores em uma referência ou em uma matriz
+OFFSET			= DESLOC			##	Retorna um deslocamento de referência com base em uma determinada referência
+ROW			= LIN				##	Retorna o número da linha de uma referência
+ROWS			= LINS				##	Retorna o número de linhas em uma referência
+RTD			= RTD				##	Recupera dados em tempo real de um programa que ofereça suporte a automação COM (automação: uma forma de trabalhar com objetos de um aplicativo a partir de outro aplicativo ou ferramenta de desenvolvimento. Chamada inicialmente de automação OLE, a automação é um padrão industrial e um recurso do modelo de objeto componente (COM).)
+TRANSPOSE		= TRANSPOR			##	Retorna a transposição de uma matriz
+VLOOKUP			= PROCV				##	Procura na primeira coluna de uma matriz e move ao longo da linha para retornar o valor de uma célula
+
+
+##
+##	Math and trigonometry functions			Funções matemáticas e trigonométricas
+##
+ABS			= ABS				##	Retorna o valor absoluto de um número
+ACOS			= ACOS				##	Retorna o arco cosseno de um número
+ACOSH			= ACOSH				##	Retorna o cosseno hiperbólico inverso de um número
+ASIN			= ASEN				##	Retorna o arco seno de um número
+ASINH			= ASENH				##	Retorna o seno hiperbólico inverso de um número
+ATAN			= ATAN				##	Retorna o arco tangente de um número
+ATAN2			= ATAN2				##	Retorna o arco tangente das coordenadas x e y especificadas
+ATANH			= ATANH				##	Retorna a tangente hiperbólica inversa de um número
+CEILING			= TETO				##	Arredonda um número para o inteiro mais próximo ou para o múltiplo mais próximo de significância
+COMBIN			= COMBIN			##	Retorna o número de combinações de um determinado número de objetos
+COS			= COS				##	Retorna o cosseno de um número
+COSH			= COSH				##	Retorna o cosseno hiperbólico de um número
+DEGREES			= GRAUS				##	Converte radianos em graus
+EVEN			= PAR				##	Arredonda um número para cima até o inteiro par mais próximo
+EXP			= EXP				##	Retorna e elevado à potência de um número especificado
+FACT			= FATORIAL			##	Retorna o fatorial de um número
+FACTDOUBLE		= FATDUPLO			##	Retorna o fatorial duplo de um número
+FLOOR			= ARREDMULTB			##	Arredonda um número para baixo até zero
+GCD			= MDC				##	Retorna o máximo divisor comum
+INT			= INT				##	Arredonda um número para baixo até o número inteiro mais próximo
+LCM			= MMC				##	Retorna o mínimo múltiplo comum
+LN			= LN				##	Retorna o logaritmo natural de um número
+LOG			= LOG				##	Retorna o logaritmo de um número de uma base especificada
+LOG10			= LOG10				##	Retorna o logaritmo de base 10 de um número
+MDETERM			= MATRIZ.DETERM			##	Retorna o determinante de uma matriz de uma variável do tipo matriz
+MINVERSE		= MATRIZ.INVERSO		##	Retorna a matriz inversa de uma matriz
+MMULT			= MATRIZ.MULT			##	Retorna o produto de duas matrizes
+MOD			= RESTO				##	Retorna o resto da divisão
+MROUND			= MARRED			##	Retorna um número arredondado ao múltiplo desejado
+MULTINOMIAL		= MULTINOMIAL			##	Retorna o multinomial de um conjunto de números
+ODD			= ÍMPAR				##	Arredonda um número para cima até o inteiro ímpar mais próximo
+PI			= PI				##	Retorna o valor de Pi
+POWER			= POTÊNCIA			##	Fornece o resultado de um número elevado a uma potência
+PRODUCT			= MULT				##	Multiplica seus argumentos
+QUOTIENT		= QUOCIENTE			##	Retorna a parte inteira de uma divisão
+RADIANS			= RADIANOS			##	Converte graus em radianos
+RAND			= ALEATÓRIO			##	Retorna um número aleatório entre 0 e 1
+RANDBETWEEN		= ALEATÓRIOENTRE		##	Retorna um número aleatório entre os números especificados
+ROMAN			= ROMANO			##	Converte um algarismo arábico em romano, como texto
+ROUND			= ARRED				##	Arredonda um número até uma quantidade especificada de dígitos
+ROUNDDOWN		= ARREDONDAR.PARA.BAIXO		##	Arredonda um número para baixo até zero
+ROUNDUP			= ARREDONDAR.PARA.CIMA		##	Arredonda um número para cima, afastando-o de zero
+SERIESSUM		= SOMASEQÜÊNCIA			##	Retorna a soma de uma série polinomial baseada na fórmula
+SIGN			= SINAL				##	Retorna o sinal de um número
+SIN			= SEN				##	Retorna o seno de um ângulo dado
+SINH			= SENH				##	Retorna o seno hiperbólico de um número
+SQRT			= RAIZ				##	Retorna uma raiz quadrada positiva
+SQRTPI			= RAIZPI			##	Retorna a raiz quadrada de (núm* pi)
+SUBTOTAL		= SUBTOTAL			##	Retorna um subtotal em uma lista ou em um banco de dados
+SUM			= SOMA				##	Soma seus argumentos
+SUMIF			= SOMASE			##	Adiciona as células especificadas por um determinado critério
+SUMIFS			= SOMASE			##	Adiciona as células em um intervalo que atende a vários critérios
+SUMPRODUCT		= SOMARPRODUTO			##	Retorna a soma dos produtos de componentes correspondentes de matrizes
+SUMSQ			= SOMAQUAD			##	Retorna a soma dos quadrados dos argumentos
+SUMX2MY2		= SOMAX2DY2			##	Retorna a soma da diferença dos quadrados dos valores correspondentes em duas matrizes
+SUMX2PY2		= SOMAX2SY2			##	Retorna a soma da soma dos quadrados dos valores correspondentes em duas matrizes
+SUMXMY2			= SOMAXMY2			##	Retorna a soma dos quadrados das diferenças dos valores correspondentes em duas matrizes
+TAN			= TAN				##	Retorna a tangente de um número
+TANH			= TANH				##	Retorna a tangente hiperbólica de um número
+TRUNC			= TRUNCAR			##	Trunca um número para um inteiro
+
+
+##
+##	Statistical functions				Funções estatísticas
+##
+AVEDEV			= DESV.MÉDIO			##	Retorna a média aritmética dos desvios médios dos pontos de dados a partir de sua média
+AVERAGE			= MÉDIA				##	Retorna a média dos argumentos
+AVERAGEA		= MÉDIAA			##	Retorna a média dos argumentos, inclusive números, texto e valores lógicos
+AVERAGEIF		= MÉDIASE			##	Retorna a média (média aritmética) de todas as células em um intervalo que atendem a um determinado critério
+AVERAGEIFS		= MÉDIASES			##	Retorna a média (média aritmética) de todas as células que atendem a múltiplos critérios.
+BETADIST		= DISTBETA			##	Retorna a função de distribuição cumulativa beta
+BETAINV			= BETA.ACUM.INV			##	Retorna o inverso da função de distribuição cumulativa para uma distribuição beta especificada
+BINOMDIST		= DISTRBINOM			##	Retorna a probabilidade de distribuição binomial do termo individual
+CHIDIST			= DIST.QUI			##	Retorna a probabilidade unicaudal da distribuição qui-quadrada
+CHIINV			= INV.QUI			##	Retorna o inverso da probabilidade uni-caudal da distribuição qui-quadrada
+CHITEST			= TESTE.QUI			##	Retorna o teste para independência
+CONFIDENCE		= INT.CONFIANÇA			##	Retorna o intervalo de confiança para uma média da população
+CORREL			= CORREL			##	Retorna o coeficiente de correlação entre dois conjuntos de dados
+COUNT			= CONT.NÚM			##	Calcula quantos números há na lista de argumentos
+COUNTA			= CONT.VALORES			##	Calcula quantos valores há na lista de argumentos
+COUNTBLANK		= CONTAR.VAZIO			##	Conta o número de células vazias no intervalo especificado
+COUNTIF			= CONT.SE			##	Calcula o número de células não vazias em um intervalo que corresponde a determinados critérios
+COUNTIFS		= CONT.SES			##	Conta o número de células dentro de um intervalo que atende a múltiplos critérios
+COVAR			= COVAR				##	Retorna a covariância, a média dos produtos dos desvios pares
+CRITBINOM		= CRIT.BINOM			##	Retorna o menor valor para o qual a distribuição binomial cumulativa é menor ou igual ao valor padrão
+DEVSQ			= DESVQ				##	Retorna a soma dos quadrados dos desvios
+EXPONDIST		= DISTEXPON			##	Retorna a distribuição exponencial
+FDIST			= DISTF				##	Retorna a distribuição de probabilidade F
+FINV			= INVF				##	Retorna o inverso da distribuição de probabilidades F
+FISHER			= FISHER			##	Retorna a transformação Fisher
+FISHERINV		= FISHERINV			##	Retorna o inverso da transformação Fisher
+FORECAST		= PREVISÃO			##	Retorna um valor ao longo de uma linha reta
+FREQUENCY		= FREQÜÊNCIA			##	Retorna uma distribuição de freqüência como uma matriz vertical
+FTEST			= TESTEF			##	Retorna o resultado de um teste F
+GAMMADIST		= DISTGAMA			##	Retorna a distribuição gama
+GAMMAINV		= INVGAMA			##	Retorna o inverso da distribuição cumulativa gama
+GAMMALN			= LNGAMA			##	Retorna o logaritmo natural da função gama, G(x)
+GEOMEAN			= MÉDIA.GEOMÉTRICA		##	Retorna a média geométrica
+GROWTH			= CRESCIMENTO			##	Retorna valores ao longo de uma tendência exponencial
+HARMEAN			= MÉDIA.HARMÔNICA		##	Retorna a média harmônica
+HYPGEOMDIST		= DIST.HIPERGEOM		##	Retorna a distribuição hipergeométrica
+INTERCEPT		= INTERCEPÇÃO			##	Retorna a intercepção da linha de regressão linear
+KURT			= CURT				##	Retorna a curtose de um conjunto de dados
+LARGE			= MAIOR				##	Retorna o maior valor k-ésimo de um conjunto de dados
+LINEST			= PROJ.LIN			##	Retorna os parâmetros de uma tendência linear
+LOGEST			= PROJ.LOG			##	Retorna os parâmetros de uma tendência exponencial
+LOGINV			= INVLOG			##	Retorna o inverso da distribuição lognormal
+LOGNORMDIST		= DIST.LOGNORMAL		##	Retorna a distribuição lognormal cumulativa
+MAX			= MÁXIMO			##	Retorna o valor máximo em uma lista de argumentos
+MAXA			= MÁXIMOA			##	Retorna o maior valor em uma lista de argumentos, inclusive números, texto e valores lógicos
+MEDIAN			= MED				##	Retorna a mediana dos números indicados
+MIN			= MÍNIMO			##	Retorna o valor mínimo em uma lista de argumentos
+MINA			= MÍNIMOA			##	Retorna o menor valor em uma lista de argumentos, inclusive números, texto e valores lógicos
+MODE			= MODO				##	Retorna o valor mais comum em um conjunto de dados
+NEGBINOMDIST		= DIST.BIN.NEG			##	Retorna a distribuição binomial negativa
+NORMDIST		= DIST.NORM			##	Retorna a distribuição cumulativa normal
+NORMINV			= INV.NORM			##	Retorna o inverso da distribuição cumulativa normal
+NORMSDIST		= DIST.NORMP			##	Retorna a distribuição cumulativa normal padrão
+NORMSINV		= INV.NORMP			##	Retorna o inverso da distribuição cumulativa normal padrão
+PEARSON			= PEARSON			##	Retorna o coeficiente de correlação do momento do produto Pearson
+PERCENTILE		= PERCENTIL			##	Retorna o k-ésimo percentil de valores em um intervalo
+PERCENTRANK		= ORDEM.PORCENTUAL		##	Retorna a ordem percentual de um valor em um conjunto de dados
+PERMUT			= PERMUT			##	Retorna o número de permutações de um determinado número de objetos
+POISSON			= POISSON			##	Retorna a distribuição Poisson
+PROB			= PROB				##	Retorna a probabilidade de valores em um intervalo estarem entre dois limites
+QUARTILE		= QUARTIL			##	Retorna o quartil do conjunto de dados
+RANK			= ORDEM				##	Retorna a posição de um número em uma lista de números
+RSQ			= RQUAD				##	Retorna o quadrado do coeficiente de correlação do momento do produto de Pearson
+SKEW			= DISTORÇÃO			##	Retorna a distorção de uma distribuição
+SLOPE			= INCLINAÇÃO			##	Retorna a inclinação da linha de regressão linear
+SMALL			= MENOR				##	Retorna o menor valor k-ésimo do conjunto de dados
+STANDARDIZE		= PADRONIZAR			##	Retorna um valor normalizado
+STDEV			= DESVPAD			##	Estima o desvio padrão com base em uma amostra
+STDEVA			= DESVPADA			##	Estima o desvio padrão com base em uma amostra, inclusive números, texto e valores lógicos
+STDEVP			= DESVPADP			##	Calcula o desvio padrão com base na população total
+STDEVPA			= DESVPADPA			##	Calcula o desvio padrão com base na população total, inclusive números, texto e valores lógicos
+STEYX			= EPADYX			##	Retorna o erro padrão do valor-y previsto para cada x da regressão
+TDIST			= DISTT				##	Retorna a distribuição t de Student
+TINV			= INVT				##	Retorna o inverso da distribuição t de Student
+TREND			= TENDÊNCIA			##	Retorna valores ao longo de uma tendência linear
+TRIMMEAN		= MÉDIA.INTERNA			##	Retorna a média do interior de um conjunto de dados
+TTEST			= TESTET			##	Retorna a probabilidade associada ao teste t de Student
+VAR			= VAR				##	Estima a variância com base em uma amostra
+VARA			= VARA				##	Estima a variância com base em uma amostra, inclusive números, texto e valores lógicos
+VARP			= VARP				##	Calcula a variância com base na população inteira
+VARPA			= VARPA				##	Calcula a variância com base na população total, inclusive números, texto e valores lógicos
+WEIBULL			= WEIBULL			##	Retorna a distribuição Weibull
+ZTEST			= TESTEZ			##	Retorna o valor de probabilidade uni-caudal de um teste-z
+
+
+##
+##	Text functions			Funções de texto
+##
+ASC			= ASC				##	Altera letras do inglês ou katakana de largura total (bytes duplos) dentro de uma seqüência de caracteres para caracteres de meia largura (byte único)
+BAHTTEXT		= BAHTTEXT			##	Converte um número em um texto, usando o formato de moeda ß (baht)
+CHAR			= CARACT			##	Retorna o caractere especificado pelo número de código
+CLEAN			= TIRAR				##	Remove todos os caracteres do texto que não podem ser impressos
+CODE			= CÓDIGO			##	Retorna um código numérico para o primeiro caractere de uma seqüência de caracteres de texto
+CONCATENATE		= CONCATENAR			##	Agrupa vários itens de texto em um único item de texto
+DOLLAR			= MOEDA				##	Converte um número em texto, usando o formato de moeda $ (dólar)
+EXACT			= EXATO				##	Verifica se dois valores de texto são idênticos
+FIND			= PROCURAR			##	Procura um valor de texto dentro de outro (diferencia maiúsculas de minúsculas)
+FINDB			= PROCURARB			##	Procura um valor de texto dentro de outro (diferencia maiúsculas de minúsculas)
+FIXED			= DEF.NÚM.DEC			##	Formata um número como texto com um número fixo de decimais
+JIS			= JIS				##	Altera letras do inglês ou katakana de meia largura (byte único) dentro de uma seqüência de caracteres para caracteres de largura total (bytes duplos)
+LEFT			= ESQUERDA			##	Retorna os caracteres mais à esquerda de um valor de texto
+LEFTB			= ESQUERDAB			##	Retorna os caracteres mais à esquerda de um valor de texto
+LEN			= NÚM.CARACT			##	Retorna o número de caracteres em uma seqüência de texto
+LENB			= NÚM.CARACTB			##	Retorna o número de caracteres em uma seqüência de texto
+LOWER			= MINÚSCULA			##	Converte texto para minúsculas
+MID			= EXT.TEXTO			##	Retorna um número específico de caracteres de uma seqüência de texto começando na posição especificada
+MIDB			= EXT.TEXTOB			##	Retorna um número específico de caracteres de uma seqüência de texto começando na posição especificada
+PHONETIC		= FONÉTICA			##	Extrai os caracteres fonéticos (furigana) de uma seqüência de caracteres de texto
+PROPER			= PRI.MAIÚSCULA			##	Coloca a primeira letra de cada palavra em maiúscula em um valor de texto
+REPLACE			= MUDAR				##	Muda os caracteres dentro do texto
+REPLACEB		= MUDARB			##	Muda os caracteres dentro do texto
+REPT			= REPT				##	Repete o texto um determinado número de vezes
+RIGHT			= DIREITA			##	Retorna os caracteres mais à direita de um valor de texto
+RIGHTB			= DIREITAB			##	Retorna os caracteres mais à direita de um valor de texto
+SEARCH			= LOCALIZAR			##	Localiza um valor de texto dentro de outro (não diferencia maiúsculas de minúsculas)
+SEARCHB			= LOCALIZARB			##	Localiza um valor de texto dentro de outro (não diferencia maiúsculas de minúsculas)
+SUBSTITUTE		= SUBSTITUIR			##	Substitui um novo texto por um texto antigo em uma seqüência de texto
+T			= T				##	Converte os argumentos em texto
+TEXT			= TEXTO				##	Formata um número e o converte em texto
+TRIM			= ARRUMAR			##	Remove espaços do texto
+UPPER			= MAIÚSCULA			##	Converte o texto em maiúsculas
+VALUE			= VALOR				##	Converte um argumento de texto em um número
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/config
new file mode 100755
index 0000000..cd85c17
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/config
@@ -0,0 +1,24 @@
+##
+## PhpSpreadsheet
+##
+
+ArgumentSeparator	= ;
+
+
+##
+##	(For future use)
+##
+currencySymbol	= €
+
+
+##
+##	Excel Error Codes	(For future use)
+
+##
+NULL	= #NULO!
+DIV0	= #DIV/0!
+VALUE	= #VALOR!
+REF	= #REF!
+NAME	= #NOME?
+NUM	= #NÚM!
+NA	= #N/D
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/functions
new file mode 100755
index 0000000..ba4eb47
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/functions
@@ -0,0 +1,408 @@
+##
+##	Add-in and Automation functions			Funções de Suplemento e Automatização
+##
+GETPIVOTDATA		= OBTERDADOSDIN			##	Devolve dados armazenados num relatório de Tabela Dinâmica
+
+
+##
+##	Cube functions					Funções de cubo
+##
+CUBEKPIMEMBER		= MEMBROKPICUBO			##	Devolve o nome, propriedade e medição de um KPI (key performance indicator) e apresenta o nome e a propriedade na célula. Um KPI é uma medida quantificável, como, por exemplo, o lucro mensal bruto ou a rotatividade trimestral de pessoal, utilizada para monitorizar o desempenho de uma organização.
+CUBEMEMBER		= MEMBROCUBO			##	Devolve um membro ou cadeia de identificação numa hierarquia de cubo. Utilizada para validar a existência do membro ou cadeia de identificação no cubo.
+CUBEMEMBERPROPERTY	= PROPRIEDADEMEMBROCUBO		##	Devolve o valor de uma propriedade de membro no cubo. Utilizada para validar a existência de um nome de membro no cubo e para devolver a propriedade especificada para esse membro.
+CUBERANKEDMEMBER	= MEMBROCLASSIFICADOCUBO	##	Devolve o enésimo ou a classificação mais alta num conjunto. Utilizada para devolver um ou mais elementos num conjunto, tal como o melhor vendedor ou os 10 melhores alunos.
+CUBESET			= CONJUNTOCUBO			##	Define um conjunto calculado de membros ou cadeias de identificação enviando uma expressão de conjunto para o cubo no servidor, que cria o conjunto e, em seguida, devolve o conjunto ao Microsoft Office Excel.
+CUBESETCOUNT		= CONTARCONJUNTOCUBO		##	Devolve o número de itens num conjunto.
+CUBEVALUE		= VALORCUBO			##	Devolve um valor agregado do cubo.
+
+
+##
+##	Database functions				Funções de base de dados
+##
+DAVERAGE		= BDMÉDIA			##	Devolve a média das entradas da base de dados seleccionadas
+DCOUNT			= BDCONTAR			##	Conta as células que contêm números numa base de dados
+DCOUNTA			= BDCONTAR.VAL			##	Conta as células que não estejam em branco numa base de dados
+DGET			= BDOBTER			##	Extrai de uma base de dados um único registo que corresponde aos critérios especificados
+DMAX			= BDMÁX				##	Devolve o valor máximo das entradas da base de dados seleccionadas
+DMIN			= BDMÍN				##	Devolve o valor mínimo das entradas da base de dados seleccionadas
+DPRODUCT		= BDMULTIPL			##	Multiplica os valores de um determinado campo de registos que correspondem aos critérios numa base de dados
+DSTDEV			= BDDESVPAD			##	Calcula o desvio-padrão com base numa amostra de entradas da base de dados seleccionadas
+DSTDEVP			= BDDESVPADP			##	Calcula o desvio-padrão com base na população total das entradas da base de dados seleccionadas
+DSUM			= BDSOMA			##	Adiciona os números na coluna de campo dos registos de base de dados que correspondem aos critérios
+DVAR			= BDVAR				##	Calcula a variância com base numa amostra das entradas de base de dados seleccionadas
+DVARP			= BDVARP			##	Calcula a variância com base na população total das entradas de base de dados seleccionadas
+
+
+##
+##	Date and time functions				Funções de data e hora
+##
+DATE			= DATA				##	Devolve o número de série de uma determinada data
+DATEVALUE		= DATA.VALOR			##	Converte uma data em forma de texto num número de série
+DAY			= DIA				##	Converte um número de série num dia do mês
+DAYS360			= DIAS360			##	Calcula o número de dias entre duas datas com base num ano com 360 dias
+EDATE			= DATAM				##	Devolve um número de série de data que corresponde ao número de meses indicado antes ou depois da data de início
+EOMONTH			= FIMMÊS			##	Devolve o número de série do último dia do mês antes ou depois de um número de meses especificado
+HOUR			= HORA				##	Converte um número de série numa hora
+MINUTE			= MINUTO			##	Converte um número de série num minuto
+MONTH			= MÊS				##	Converte um número de série num mês
+NETWORKDAYS		= DIATRABALHOTOTAL		##	Devolve o número total de dias úteis entre duas datas
+NOW			= AGORA				##	Devolve o número de série da data e hora actuais
+SECOND			= SEGUNDO			##	Converte um número de série num segundo
+TIME			= TEMPO				##	Devolve o número de série de um determinado tempo
+TIMEVALUE		= VALOR.TEMPO			##	Converte um tempo em forma de texto num número de série
+TODAY			= HOJE				##	Devolve o número de série da data actual
+WEEKDAY			= DIA.SEMANA			##	Converte um número de série num dia da semana
+WEEKNUM			= NÚMSEMANA			##	Converte um número de série num número que representa o número da semana num determinado ano
+WORKDAY			= DIA.TRABALHO			##	Devolve o número de série da data antes ou depois de um número de dias úteis especificado
+YEAR			= ANO				##	Converte um número de série num ano
+YEARFRAC		= FRACÇÃOANO			##	Devolve a fracção de ano que representa o número de dias inteiros entre a data_de_início e a data_de_fim
+
+
+##
+##	Engineering functions				Funções de engenharia
+##
+BESSELI			= BESSELI			##	Devolve a função de Bessel modificada In(x)
+BESSELJ			= BESSELJ			##	Devolve a função de Bessel Jn(x)
+BESSELK			= BESSELK			##	Devolve a função de Bessel modificada Kn(x)
+BESSELY			= BESSELY			##	Devolve a função de Bessel Yn(x)
+BIN2DEC			= BINADEC			##	Converte um número binário em decimal
+BIN2HEX			= BINAHEX			##	Converte um número binário em hexadecimal
+BIN2OCT			= BINAOCT			##	Converte um número binário em octal
+COMPLEX			= COMPLEXO			##	Converte coeficientes reais e imaginários num número complexo
+CONVERT			= CONVERTER			##	Converte um número de um sistema de medida noutro
+DEC2BIN			= DECABIN			##	Converte um número decimal em binário
+DEC2HEX			= DECAHEX			##	Converte um número decimal em hexadecimal
+DEC2OCT			= DECAOCT			##	Converte um número decimal em octal
+DELTA			= DELTA				##	Testa se dois valores são iguais
+ERF			= FUNCERRO			##	Devolve a função de erro
+ERFC			= FUNCERROCOMPL			##	Devolve a função de erro complementar
+GESTEP			= DEGRAU			##	Testa se um número é maior do que um valor limite
+HEX2BIN			= HEXABIN			##	Converte um número hexadecimal em binário
+HEX2DEC			= HEXADEC			##	Converte um número hexadecimal em decimal
+HEX2OCT			= HEXAOCT			##	Converte um número hexadecimal em octal
+IMABS			= IMABS				##	Devolve o valor absoluto (módulo) de um número complexo
+IMAGINARY		= IMAGINÁRIO			##	Devolve o coeficiente imaginário de um número complexo
+IMARGUMENT		= IMARG				##	Devolve o argumento Teta, um ângulo expresso em radianos
+IMCONJUGATE		= IMCONJ			##	Devolve o conjugado complexo de um número complexo
+IMCOS			= IMCOS				##	Devolve o co-seno de um número complexo
+IMDIV			= IMDIV				##	Devolve o quociente de dois números complexos
+IMEXP			= IMEXP				##	Devolve o exponencial de um número complexo
+IMLN			= IMLN				##	Devolve o logaritmo natural de um número complexo
+IMLOG10			= IMLOG10			##	Devolve o logaritmo de base 10 de um número complexo
+IMLOG2			= IMLOG2			##	Devolve o logaritmo de base 2 de um número complexo
+IMPOWER			= IMPOT				##	Devolve um número complexo elevado a uma potência inteira
+IMPRODUCT		= IMPROD			##	Devolve o produto de números complexos
+IMREAL			= IMREAL			##	Devolve o coeficiente real de um número complexo
+IMSIN			= IMSENO			##	Devolve o seno de um número complexo
+IMSQRT			= IMRAIZ			##	Devolve a raiz quadrada de um número complexo
+IMSUB			= IMSUBTR			##	Devolve a diferença entre dois números complexos
+IMSUM			= IMSOMA			##	Devolve a soma de números complexos
+OCT2BIN			= OCTABIN			##	Converte um número octal em binário
+OCT2DEC			= OCTADEC			##	Converte um número octal em decimal
+OCT2HEX			= OCTAHEX			##	Converte um número octal em hexadecimal
+
+
+##
+##	Financial functions				Funções financeiras
+##
+ACCRINT			= JUROSACUM			##	Devolve os juros acumulados de um título que paga juros periódicos
+ACCRINTM		= JUROSACUMV			##	Devolve os juros acumulados de um título que paga juros no vencimento
+AMORDEGRC		= AMORDEGRC			##	Devolve a depreciação correspondente a cada período contabilístico utilizando um coeficiente de depreciação
+AMORLINC		= AMORLINC			##	Devolve a depreciação correspondente a cada período contabilístico
+COUPDAYBS		= CUPDIASINLIQ			##	Devolve o número de dias entre o início do período do cupão e a data de regularização
+COUPDAYS		= CUPDIAS			##	Devolve o número de dias no período do cupão que contém a data de regularização
+COUPDAYSNC		= CUPDIASPRÓX			##	Devolve o número de dias entre a data de regularização e a data do cupão seguinte
+COUPNCD			= CUPDATAPRÓX			##	Devolve a data do cupão seguinte após a data de regularização
+COUPNUM			= CUPNÚM			##	Devolve o número de cupões a serem pagos entre a data de regularização e a data de vencimento
+COUPPCD			= CUPDATAANT			##	Devolve a data do cupão anterior antes da data de regularização
+CUMIPMT			= PGTOJURACUM			##	Devolve os juros cumulativos pagos entre dois períodos
+CUMPRINC		= PGTOCAPACUM			##	Devolve o capital cumulativo pago a título de empréstimo entre dois períodos
+DB			= BD				##	Devolve a depreciação de um activo relativo a um período especificado utilizando o método das quotas degressivas fixas
+DDB			= BDD				##	Devolve a depreciação de um activo relativo a um período especificado utilizando o método das quotas degressivas duplas ou qualquer outro método especificado
+DISC			= DESC				##	Devolve a taxa de desconto de um título
+DOLLARDE		= MOEDADEC			##	Converte um preço em unidade monetária, expresso como uma fracção, num preço em unidade monetária, expresso como um número decimal
+DOLLARFR		= MOEDAFRA			##	Converte um preço em unidade monetária, expresso como um número decimal, num preço em unidade monetária, expresso como uma fracção
+DURATION		= DURAÇÃO			##	Devolve a duração anual de um título com pagamentos de juros periódicos
+EFFECT			= EFECTIVA			##	Devolve a taxa de juros anual efectiva
+FV			= VF				##	Devolve o valor futuro de um investimento
+FVSCHEDULE		= VFPLANO			##	Devolve o valor futuro de um capital inicial após a aplicação de uma série de taxas de juro compostas
+INTRATE			= TAXAJUROS			##	Devolve a taxa de juros de um título investido na totalidade
+IPMT			= IPGTO				##	Devolve o pagamento dos juros de um investimento durante um determinado período
+IRR			= TIR				##	Devolve a taxa de rentabilidade interna para uma série de fluxos monetários
+ISPMT			= É.PGTO			##	Calcula os juros pagos durante um período específico de um investimento
+MDURATION		= MDURAÇÃO			##	Devolve a duração modificada de Macauley de um título com um valor de paridade equivalente a € 100
+MIRR			= MTIR				##	Devolve a taxa interna de rentabilidade em que os fluxos monetários positivos e negativos são financiados com taxas diferentes
+NOMINAL			= NOMINAL			##	Devolve a taxa de juros nominal anual
+NPER			= NPER				##	Devolve o número de períodos de um investimento
+NPV			= VAL				##	Devolve o valor actual líquido de um investimento com base numa série de fluxos monetários periódicos e numa taxa de desconto
+ODDFPRICE		= PREÇOPRIMINC			##	Devolve o preço por € 100 do valor nominal de um título com um período inicial incompleto
+ODDFYIELD		= LUCROPRIMINC			##	Devolve o lucro de um título com um período inicial incompleto
+ODDLPRICE		= PREÇOÚLTINC			##	Devolve o preço por € 100 do valor nominal de um título com um período final incompleto
+ODDLYIELD		= LUCROÚLTINC			##	Devolve o lucro de um título com um período final incompleto
+PMT			= PGTO				##	Devolve o pagamento periódico de uma anuidade
+PPMT			= PPGTO				##	Devolve o pagamento sobre o capital de um investimento num determinado período
+PRICE			= PREÇO				##	Devolve o preço por € 100 do valor nominal de um título que paga juros periódicos
+PRICEDISC		= PREÇODESC			##	Devolve o preço por € 100 do valor nominal de um título descontado
+PRICEMAT		= PREÇOVENC			##	Devolve o preço por € 100 do valor nominal de um título que paga juros no vencimento
+PV			= VA				##	Devolve o valor actual de um investimento
+RATE			= TAXA				##	Devolve a taxa de juros por período de uma anuidade
+RECEIVED		= RECEBER			##	Devolve o montante recebido no vencimento de um título investido na totalidade
+SLN			= AMORT				##	Devolve uma depreciação linear de um activo durante um período
+SYD			= AMORTD			##	Devolve a depreciação por algarismos da soma dos anos de um activo durante um período especificado
+TBILLEQ			= OTN				##	Devolve o lucro de um título equivalente a uma Obrigação do Tesouro
+TBILLPRICE		= OTNVALOR			##	Devolve o preço por € 100 de valor nominal de uma Obrigação do Tesouro
+TBILLYIELD		= OTNLUCRO			##	Devolve o lucro de uma Obrigação do Tesouro
+VDB			= BDV				##	Devolve a depreciação de um activo relativo a um período específico ou parcial utilizando um método de quotas degressivas
+XIRR			= XTIR				##	Devolve a taxa interna de rentabilidade de um plano de fluxos monetários que não seja necessariamente periódica
+XNPV			= XVAL				##	Devolve o valor actual líquido de um plano de fluxos monetários que não seja necessariamente periódico
+YIELD			= LUCRO				##	Devolve o lucro de um título que paga juros periódicos
+YIELDDISC		= LUCRODESC			##	Devolve o lucro anual de um título emitido abaixo do valor nominal, por exemplo, uma Obrigação do Tesouro
+YIELDMAT		= LUCROVENC			##	Devolve o lucro anual de um título que paga juros na data de vencimento
+
+
+##
+##	Information functions				Funções de informação
+##
+CELL			= CÉL				##	Devolve informações sobre a formatação, localização ou conteúdo de uma célula
+ERROR.TYPE		= TIPO.ERRO			##	Devolve um número correspondente a um tipo de erro
+INFO			= INFORMAÇÃO			##	Devolve informações sobre o ambiente de funcionamento actual
+ISBLANK			= É.CÉL.VAZIA			##	Devolve VERDADEIRO se o valor estiver em branco
+ISERR			= É.ERROS			##	Devolve VERDADEIRO se o valor for um valor de erro diferente de #N/D
+ISERROR			= É.ERRO			##	Devolve VERDADEIRO se o valor for um valor de erro
+ISEVEN			= ÉPAR				##	Devolve VERDADEIRO se o número for par
+ISLOGICAL		= É.LÓGICO			##	Devolve VERDADEIRO se o valor for lógico
+ISNA			= É.NÃO.DISP			##	Devolve VERDADEIRO se o valor for o valor de erro #N/D
+ISNONTEXT		= É.NÃO.TEXTO			##	Devolve VERDADEIRO se o valor não for texto
+ISNUMBER		= É.NÚM				##	Devolve VERDADEIRO se o valor for um número
+ISODD			= ÉÍMPAR			##	Devolve VERDADEIRO se o número for ímpar
+ISREF			= É.REF				##	Devolve VERDADEIRO se o valor for uma referência
+ISTEXT			= É.TEXTO			##	Devolve VERDADEIRO se o valor for texto
+N			= N				##	Devolve um valor convertido num número
+NA			= NÃO.DISP			##	Devolve o valor de erro #N/D
+TYPE			= TIPO				##	Devolve um número que indica o tipo de dados de um valor
+
+
+##
+##	Logical functions				Funções lógicas
+##
+AND			= E				##	Devolve VERDADEIRO se todos os respectivos argumentos corresponderem a VERDADEIRO
+FALSE			= FALSO				##	Devolve o valor lógico FALSO
+IF			= SE				##	Especifica um teste lógico a ser executado
+IFERROR			= SE.ERRO			##	Devolve um valor definido pelo utilizador se ocorrer um erro na fórmula, e devolve o resultado da fórmula se não ocorrer nenhum erro
+NOT			= NÃO				##	Inverte a lógica do respectivo argumento
+OR			= OU				##	Devolve VERDADEIRO se qualquer argumento for VERDADEIRO
+TRUE			= VERDADEIRO			##	Devolve o valor lógico VERDADEIRO
+
+
+##
+##	Lookup and reference functions			Funções de pesquisa e referência
+##
+ADDRESS			= ENDEREÇO			##	Devolve uma referência a uma única célula numa folha de cálculo como texto
+AREAS			= ÁREAS				##	Devolve o número de áreas numa referência
+CHOOSE			= SELECCIONAR			##	Selecciona um valor a partir de uma lista de valores
+COLUMN			= COL				##	Devolve o número da coluna de uma referência
+COLUMNS			= COLS				##	Devolve o número de colunas numa referência
+HLOOKUP			= PROCH				##	Procura na linha superior de uma matriz e devolve o valor da célula indicada
+HYPERLINK		= HIPERLIGAÇÃO			##	Cria um atalho ou hiperligação que abre um documento armazenado num servidor de rede, numa intranet ou na Internet
+INDEX			= ÍNDICE			##	Utiliza um índice para escolher um valor de uma referência ou de uma matriz
+INDIRECT		= INDIRECTO			##	Devolve uma referência indicada por um valor de texto
+LOOKUP			= PROC				##	Procura valores num vector ou numa matriz
+MATCH			= CORRESP			##	Procura valores numa referência ou numa matriz
+OFFSET			= DESLOCAMENTO			##	Devolve o deslocamento de referência de uma determinada referência
+ROW			= LIN				##	Devolve o número da linha de uma referência
+ROWS			= LINS				##	Devolve o número de linhas numa referência
+RTD			= RTD				##	Obtém dados em tempo real a partir de um programa que suporte automatização COM (automatização: modo de trabalhar com objectos de uma aplicação a partir de outra aplicação ou ferramenta de desenvolvimento. Anteriormente conhecida como automatização OLE, a automatização é uma norma da indústria de software e uma funcionalidade COM (Component Object Model).)
+TRANSPOSE		= TRANSPOR			##	Devolve a transposição de uma matriz
+VLOOKUP			= PROCV				##	Procura na primeira coluna de uma matriz e percorre a linha para devolver o valor de uma célula
+
+
+##
+##	Math and trigonometry functions			Funções matemáticas e trigonométricas
+##
+ABS			= ABS				##	Devolve o valor absoluto de um número
+ACOS			= ACOS				##	Devolve o arco de co-seno de um número
+ACOSH			= ACOSH				##	Devolve o co-seno hiperbólico inverso de um número
+ASIN			= ASEN				##	Devolve o arco de seno de um número
+ASINH			= ASENH				##	Devolve o seno hiperbólico inverso de um número
+ATAN			= ATAN				##	Devolve o arco de tangente de um número
+ATAN2			= ATAN2				##	Devolve o arco de tangente das coordenadas x e y
+ATANH			= ATANH				##	Devolve a tangente hiperbólica inversa de um número
+CEILING			= ARRED.EXCESSO			##	Arredonda um número para o número inteiro mais próximo ou para o múltiplo de significância mais próximo
+COMBIN			= COMBIN			##	Devolve o número de combinações de um determinado número de objectos
+COS			= COS				##	Devolve o co-seno de um número
+COSH			= COSH				##	Devolve o co-seno hiperbólico de um número
+DEGREES			= GRAUS				##	Converte radianos em graus
+EVEN			= PAR				##	Arredonda um número por excesso para o número inteiro mais próximo
+EXP			= EXP				##	Devolve e elevado à potência de um determinado número
+FACT			= FACTORIAL			##	Devolve o factorial de um número
+FACTDOUBLE		= FACTDUPLO			##	Devolve o factorial duplo de um número
+FLOOR			= ARRED.DEFEITO			##	Arredonda um número por defeito até zero
+GCD			= MDC				##	Devolve o maior divisor comum
+INT			= INT				##	Arredonda um número por defeito para o número inteiro mais próximo
+LCM			= MMC				##	Devolve o mínimo múltiplo comum
+LN			= LN				##	Devolve o logaritmo natural de um número
+LOG			= LOG				##	Devolve o logaritmo de um número com uma base especificada
+LOG10			= LOG10				##	Devolve o logaritmo de base 10 de um número
+MDETERM			= MATRIZ.DETERM			##	Devolve o determinante matricial de uma matriz
+MINVERSE		= MATRIZ.INVERSA		##	Devolve o inverso matricial de uma matriz
+MMULT			= MATRIZ.MULT			##	Devolve o produto matricial de duas matrizes
+MOD			= RESTO				##	Devolve o resto da divisão
+MROUND			= MARRED			##	Devolve um número arredondado para o múltiplo pretendido
+MULTINOMIAL		= POLINOMIAL			##	Devolve o polinomial de um conjunto de números
+ODD			= ÍMPAR				##	Arredonda por excesso um número para o número inteiro ímpar mais próximo
+PI			= PI				##	Devolve o valor de pi
+POWER			= POTÊNCIA			##	Devolve o resultado de um número elevado a uma potência
+PRODUCT			= PRODUTO			##	Multiplica os respectivos argumentos
+QUOTIENT		= QUOCIENTE			##	Devolve a parte inteira de uma divisão
+RADIANS			= RADIANOS			##	Converte graus em radianos
+RAND			= ALEATÓRIO			##	Devolve um número aleatório entre 0 e 1
+RANDBETWEEN		= ALEATÓRIOENTRE		##	Devolve um número aleatório entre os números especificados
+ROMAN			= ROMANO			##	Converte um número árabe em romano, como texto
+ROUND			= ARRED				##	Arredonda um número para um número de dígitos especificado
+ROUNDDOWN		= ARRED.PARA.BAIXO		##	Arredonda um número por defeito até zero
+ROUNDUP			= ARRED.PARA.CIMA		##	Arredonda um número por excesso, afastando-o de zero
+SERIESSUM		= SOMASÉRIE			##	Devolve a soma de uma série de potências baseada na fórmula
+SIGN			= SINAL				##	Devolve o sinal de um número
+SIN			= SEN				##	Devolve o seno de um determinado ângulo
+SINH			= SENH				##	Devolve o seno hiperbólico de um número
+SQRT			= RAIZQ				##	Devolve uma raiz quadrada positiva
+SQRTPI			= RAIZPI			##	Devolve a raiz quadrada de (núm * pi)
+SUBTOTAL		= SUBTOTAL			##	Devolve um subtotal numa lista ou base de dados
+SUM			= SOMA				##	Adiciona os respectivos argumentos
+SUMIF			= SOMA.SE			##	Adiciona as células especificadas por um determinado critério
+SUMIFS			= SOMA.SE.S			##	Adiciona as células num intervalo que cumpre vários critérios
+SUMPRODUCT		= SOMARPRODUTO			##	Devolve a soma dos produtos de componentes de matrizes correspondentes
+SUMSQ			= SOMARQUAD			##	Devolve a soma dos quadrados dos argumentos
+SUMX2MY2		= SOMAX2DY2			##	Devolve a soma da diferença dos quadrados dos valores correspondentes em duas matrizes
+SUMX2PY2		= SOMAX2SY2			##	Devolve a soma da soma dos quadrados dos valores correspondentes em duas matrizes
+SUMXMY2			= SOMAXMY2			##	Devolve a soma dos quadrados da diferença dos valores correspondentes em duas matrizes
+TAN			= TAN				##	Devolve a tangente de um número
+TANH			= TANH				##	Devolve a tangente hiperbólica de um número
+TRUNC			= TRUNCAR			##	Trunca um número para um número inteiro
+
+
+##
+##	Statistical functions				Funções estatísticas
+##
+AVEDEV			= DESV.MÉDIO			##	Devolve a média aritmética dos desvios absolutos à média dos pontos de dados
+AVERAGE			= MÉDIA				##	Devolve a média dos respectivos argumentos
+AVERAGEA		= MÉDIAA			##	Devolve uma média dos respectivos argumentos, incluindo números, texto e valores lógicos
+AVERAGEIF		= MÉDIA.SE			##	Devolve a média aritmética de todas as células num intervalo que cumprem determinado critério
+AVERAGEIFS		= MÉDIA.SE.S			##	Devolve a média aritmética de todas as células que cumprem múltiplos critérios
+BETADIST		= DISTBETA			##	Devolve a função de distribuição cumulativa beta
+BETAINV			= BETA.ACUM.INV			##	Devolve o inverso da função de distribuição cumulativa relativamente a uma distribuição beta específica
+BINOMDIST		= DISTRBINOM			##	Devolve a probabilidade de distribuição binomial de termo individual
+CHIDIST			= DIST.CHI			##	Devolve a probabilidade unicaudal da distribuição qui-quadrada
+CHIINV			= INV.CHI			##	Devolve o inverso da probabilidade unicaudal da distribuição qui-quadrada
+CHITEST			= TESTE.CHI			##	Devolve o teste para independência
+CONFIDENCE		= INT.CONFIANÇA			##	Devolve o intervalo de confiança correspondente a uma média de população
+CORREL			= CORREL			##	Devolve o coeficiente de correlação entre dois conjuntos de dados
+COUNT			= CONTAR			##	Conta os números que existem na lista de argumentos
+COUNTA			= CONTAR.VAL			##	Conta os valores que existem na lista de argumentos
+COUNTBLANK		= CONTAR.VAZIO			##	Conta o número de células em branco num intervalo
+COUNTIF			= CONTAR.SE			##	Calcula o número de células num intervalo que corresponde aos critérios determinados
+COUNTIFS		= CONTAR.SE.S			##	Conta o número de células num intervalo que cumprem múltiplos critérios
+COVAR			= COVAR				##	Devolve a covariância, que é a média dos produtos de desvios de pares
+CRITBINOM		= CRIT.BINOM			##	Devolve o menor valor em que a distribuição binomial cumulativa é inferior ou igual a um valor de critério
+DEVSQ			= DESVQ				##	Devolve a soma dos quadrados dos desvios
+EXPONDIST		= DISTEXPON			##	Devolve a distribuição exponencial
+FDIST			= DISTF				##	Devolve a distribuição da probabilidade F
+FINV			= INVF				##	Devolve o inverso da distribuição da probabilidade F
+FISHER			= FISHER			##	Devolve a transformação Fisher
+FISHERINV		= FISHERINV			##	Devolve o inverso da transformação Fisher
+FORECAST		= PREVISÃO			##	Devolve um valor ao longo de uma tendência linear
+FREQUENCY		= FREQUÊNCIA			##	Devolve uma distribuição de frequência como uma matriz vertical
+FTEST			= TESTEF			##	Devolve o resultado de um teste F
+GAMMADIST		= DISTGAMA			##	Devolve a distribuição gama
+GAMMAINV		= INVGAMA			##	Devolve o inverso da distribuição gama cumulativa
+GAMMALN			= LNGAMA			##	Devolve o logaritmo natural da função gama, Γ(x)
+GEOMEAN			= MÉDIA.GEOMÉTRICA		##	Devolve a média geométrica
+GROWTH			= CRESCIMENTO			##	Devolve valores ao longo de uma tendência exponencial
+HARMEAN			= MÉDIA.HARMÓNICA		##	Devolve a média harmónica
+HYPGEOMDIST		= DIST.HIPERGEOM		##	Devolve a distribuição hipergeométrica
+INTERCEPT		= INTERCEPTAR			##	Devolve a intercepção da linha de regressão linear
+KURT			= CURT				##	Devolve a curtose de um conjunto de dados
+LARGE			= MAIOR				##	Devolve o maior valor k-ésimo de um conjunto de dados
+LINEST			= PROJ.LIN			##	Devolve os parâmetros de uma tendência linear
+LOGEST			= PROJ.LOG			##	Devolve os parâmetros de uma tendência exponencial
+LOGINV			= INVLOG			##	Devolve o inverso da distribuição normal logarítmica
+LOGNORMDIST		= DIST.NORMALLOG		##	Devolve a distribuição normal logarítmica cumulativa
+MAX			= MÁXIMO			##	Devolve o valor máximo numa lista de argumentos
+MAXA			= MÁXIMOA			##	Devolve o valor máximo numa lista de argumentos, incluindo números, texto e valores lógicos
+MEDIAN			= MED				##	Devolve a mediana dos números indicados
+MIN			= MÍNIMO			##	Devolve o valor mínimo numa lista de argumentos
+MINA			= MÍNIMOA			##	Devolve o valor mínimo numa lista de argumentos, incluindo números, texto e valores lógicos
+MODE			= MODA				##	Devolve o valor mais comum num conjunto de dados
+NEGBINOMDIST		= DIST.BIN.NEG			##	Devolve a distribuição binominal negativa
+NORMDIST		= DIST.NORM			##	Devolve a distribuição cumulativa normal
+NORMINV			= INV.NORM			##	Devolve o inverso da distribuição cumulativa normal
+NORMSDIST		= DIST.NORMP			##	Devolve a distribuição cumulativa normal padrão
+NORMSINV		= INV.NORMP			##	Devolve o inverso da distribuição cumulativa normal padrão
+PEARSON			= PEARSON			##	Devolve o coeficiente de correlação momento/produto de Pearson
+PERCENTILE		= PERCENTIL			##	Devolve o k-ésimo percentil de valores num intervalo
+PERCENTRANK		= ORDEM.PERCENTUAL		##	Devolve a ordem percentual de um valor num conjunto de dados
+PERMUT			= PERMUTAR			##	Devolve o número de permutações de um determinado número de objectos
+POISSON			= POISSON			##	Devolve a distribuição de Poisson
+PROB			= PROB				##	Devolve a probabilidade dos valores num intervalo se encontrarem entre dois limites
+QUARTILE		= QUARTIL			##	Devolve o quartil de um conjunto de dados
+RANK			= ORDEM				##	Devolve a ordem de um número numa lista numérica
+RSQ			= RQUAD				##	Devolve o quadrado do coeficiente de correlação momento/produto de Pearson
+SKEW			= DISTORÇÃO			##	Devolve a distorção de uma distribuição
+SLOPE			= DECLIVE			##	Devolve o declive da linha de regressão linear
+SMALL			= MENOR				##	Devolve o menor valor de k-ésimo de um conjunto de dados
+STANDARDIZE		= NORMALIZAR			##	Devolve um valor normalizado
+STDEV			= DESVPAD			##	Calcula o desvio-padrão com base numa amostra
+STDEVA			= DESVPADA			##	Calcula o desvio-padrão com base numa amostra, incluindo números, texto e valores lógicos
+STDEVP			= DESVPADP			##	Calcula o desvio-padrão com base na população total
+STDEVPA			= DESVPADPA			##	Calcula o desvio-padrão com base na população total, incluindo números, texto e valores lógicos
+STEYX			= EPADYX			##	Devolve o erro-padrão do valor de y previsto para cada x na regressão
+TDIST			= DISTT				##	Devolve a distribuição t de Student
+TINV			= INVT				##	Devolve o inverso da distribuição t de Student
+TREND			= TENDÊNCIA			##	Devolve valores ao longo de uma tendência linear
+TRIMMEAN		= MÉDIA.INTERNA			##	Devolve a média do interior de um conjunto de dados
+TTEST			= TESTET			##	Devolve a probabilidade associada ao teste t de Student
+VAR			= VAR				##	Calcula a variância com base numa amostra
+VARA			= VARA				##	Calcula a variância com base numa amostra, incluindo números, texto e valores lógicos
+VARP			= VARP				##	Calcula a variância com base na população total
+VARPA			= VARPA				##	Calcula a variância com base na população total, incluindo números, texto e valores lógicos
+WEIBULL			= WEIBULL			##	Devolve a distribuição Weibull
+ZTEST			= TESTEZ			##	Devolve o valor de probabilidade unicaudal de um teste-z
+
+
+##
+##	Text functions					Funções de texto
+##
+ASC			= ASC				##	Altera letras ou katakana de largura total (byte duplo) numa cadeia de caracteres para caracteres de largura média (byte único)
+BAHTTEXT		= TEXTO.BAHT			##	Converte um número em texto, utilizando o formato monetário ß (baht)
+CHAR			= CARÁCT			##	Devolve o carácter especificado pelo número de código
+CLEAN			= LIMPAR			##	Remove do texto todos os caracteres não imprimíveis
+CODE			= CÓDIGO			##	Devolve um código numérico correspondente ao primeiro carácter numa cadeia de texto
+CONCATENATE		= CONCATENAR			##	Agrupa vários itens de texto num único item de texto
+DOLLAR			= MOEDA				##	Converte um número em texto, utilizando o formato monetário € (Euro)
+EXACT			= EXACTO			##	Verifica se dois valores de texto são idênticos
+FIND			= LOCALIZAR			##	Localiza um valor de texto dentro de outro (sensível às maiúsculas e minúsculas)
+FINDB			= LOCALIZARB			##	Localiza um valor de texto dentro de outro (sensível às maiúsculas e minúsculas)
+FIXED			= FIXA				##	Formata um número como texto com um número fixo de decimais
+JIS			= JIS				##	Altera letras ou katakana de largura média (byte único) numa cadeia de caracteres para caracteres de largura total (byte duplo)
+LEFT			= ESQUERDA			##	Devolve os caracteres mais à esquerda de um valor de texto
+LEFTB			= ESQUERDAB			##	Devolve os caracteres mais à esquerda de um valor de texto
+LEN			= NÚM.CARACT			##	Devolve o número de caracteres de uma cadeia de texto
+LENB			= NÚM.CARACTB			##	Devolve o número de caracteres de uma cadeia de texto
+LOWER			= MINÚSCULAS			##	Converte o texto em minúsculas
+MID			= SEG.TEXTO			##	Devolve um número específico de caracteres de uma cadeia de texto, a partir da posição especificada
+MIDB			= SEG.TEXTOB			##	Devolve um número específico de caracteres de uma cadeia de texto, a partir da posição especificada
+PHONETIC		= FONÉTICA			##	Retira os caracteres fonéticos (furigana) de uma cadeia de texto
+PROPER			= INICIAL.MAIÚSCULA		##	Coloca em maiúsculas a primeira letra de cada palavra de um valor de texto
+REPLACE			= SUBSTITUIR			##	Substitui caracteres no texto
+REPLACEB		= SUBSTITUIRB			##	Substitui caracteres no texto
+REPT			= REPETIR			##	Repete texto um determinado número de vezes
+RIGHT			= DIREITA			##	Devolve os caracteres mais à direita de um valor de texto
+RIGHTB			= DIREITAB			##	Devolve os caracteres mais à direita de um valor de texto
+SEARCH			= PROCURAR			##	Localiza um valor de texto dentro de outro (não sensível a maiúsculas e minúsculas)
+SEARCHB			= PROCURARB			##	Localiza um valor de texto dentro de outro (não sensível a maiúsculas e minúsculas)
+SUBSTITUTE		= SUBST				##	Substitui texto novo por texto antigo numa cadeia de texto
+T			= T				##	Converte os respectivos argumentos em texto
+TEXT			= TEXTO				##	Formata um número e converte-o em texto
+TRIM			= COMPACTAR			##	Remove espaços do texto
+UPPER			= MAIÚSCULAS			##	Converte texto em maiúsculas
+VALUE			= VALOR				##	Converte um argumento de texto num número
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/ru/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/ru/config
new file mode 100755
index 0000000..9ee9e6c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/ru/config
@@ -0,0 +1,24 @@
+##
+## PhpSpreadsheet
+##
+
+ArgumentSeparator	= ;
+
+
+##
+##	(For future use)
+##
+currencySymbol	= р
+
+
+##
+##	Excel Error Codes	(For future use)
+
+##
+NULL	= #ПУСТО!
+DIV0	= #ДЕЛ/0!
+VALUE	= #ЗНАЧ!
+REF	= #ССЫЛ!
+NAME	= #ИМЯ?
+NUM	= #ЧИСЛО!
+NA	= #Н/Д
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/ru/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/ru/functions
new file mode 100755
index 0000000..3597dbf
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/ru/functions
@@ -0,0 +1,416 @@
+##
+## PhpSpreadsheet
+##
+## Data in this file derived from information provided by web-junior (http://www.web-junior.net/)
+##
+##
+
+
+##
+##	Add-in and Automation functions				Функции надстроек и автоматизации
+##
+GETPIVOTDATA		= ПОЛУЧИТЬ.ДАННЫЕ.СВОДНОЙ.ТАБЛИЦЫ	##	Возвращает данные, хранящиеся в отчете сводной таблицы.
+
+
+##
+##	Cube functions						Функции Куб
+##
+CUBEKPIMEMBER		= КУБЭЛЕМЕНТКИП				##	Возвращает свойство ключевого индикатора производительности «(КИП)» и отображает имя «КИП» в ячейке. «КИП» представляет собой количественную величину, такую как ежемесячная валовая прибыль или ежеквартальная текучесть кадров, используемой для контроля эффективности работы организации.
+CUBEMEMBER		= КУБЭЛЕМЕНТ				##	Возвращает элемент или кортеж из куба. Используется для проверки существования элемента или кортежа в кубе.
+CUBEMEMBERPROPERTY	= КУБСВОЙСТВОЭЛЕМЕНТА			##	Возвращает значение свойства элемента из куба. Используется для проверки существования имени элемента в кубе и возвращает указанное свойство для этого элемента.
+CUBERANKEDMEMBER	= КУБПОРЭЛЕМЕНТ				##	Возвращает n-ый или ранжированный элемент в множество. Используется для возвращения одного или нескольких элементов в множество, например, лучшего продавца или 10 лучших студентов.
+CUBESET			= КУБМНОЖ				##	Определяет вычислительное множество элементов или кортежей, отправляя на сервер выражение, которое создает множество, а затем возвращает его в Microsoft Office Excel.
+CUBESETCOUNT		= КУБЧИСЛОЭЛМНОЖ			##	Возвращает число элементов множества.
+CUBEVALUE		= КУБЗНАЧЕНИЕ				##	Возвращает обобщенное значение из куба.
+
+
+##
+##	Database functions					Функции для работы с базами данных
+##
+DAVERAGE		= ДСРЗНАЧ				##	Возвращает среднее значение выбранных записей базы данных.
+DCOUNT			= БСЧЁТ					##	Подсчитывает количество числовых ячеек в базе данных.
+DCOUNTA			= БСЧЁТА				##	Подсчитывает количество непустых ячеек в базе данных.
+DGET			= БИЗВЛЕЧЬ				##	Извлекает из базы данных одну запись, удовлетворяющую заданному условию.
+DMAX			= ДМАКС					##	Возвращает максимальное значение среди выделенных записей базы данных.
+DMIN			= ДМИН					##	Возвращает минимальное значение среди выделенных записей базы данных.
+DPRODUCT		= БДПРОИЗВЕД				##	Перемножает значения определенного поля в записях базы данных, удовлетворяющих условию.
+DSTDEV			= ДСТАНДОТКЛ				##	Оценивает стандартное отклонение по выборке для выделенных записей базы данных.
+DSTDEVP			= ДСТАНДОТКЛП				##	Вычисляет стандартное отклонение по генеральной совокупности для выделенных записей базы данных
+DSUM			= БДСУММ				##	Суммирует числа в поле для записей базы данных, удовлетворяющих условию.
+DVAR			= БДДИСП				##	Оценивает дисперсию по выборке из выделенных записей базы данных
+DVARP			= БДДИСПП				##	Вычисляет дисперсию по генеральной совокупности для выделенных записей базы данных
+
+
+##
+##	Date and time functions					Функции даты и времени
+##
+DATE			= ДАТА					##	Возвращает заданную дату в числовом формате.
+DATEVALUE		= ДАТАЗНАЧ				##	Преобразует дату из текстового формата в числовой формат.
+DAY			= ДЕНЬ					##	Преобразует дату в числовом формате в день месяца.
+DAYS360			= ДНЕЙ360				##	Вычисляет количество дней между двумя датами на основе 360-дневного года.
+EDATE			= ДАТАМЕС				##	Возвращает дату в числовом формате, отстоящую на заданное число месяцев вперед или назад от начальной даты.
+EOMONTH			= КОНМЕСЯЦА				##	Возвращает дату в числовом формате для последнего дня месяца, отстоящего вперед или назад на заданное число месяцев.
+HOUR			= ЧАС					##	Преобразует дату в числовом формате в часы.
+MINUTE			= МИНУТЫ				##	Преобразует дату в числовом формате в минуты.
+MONTH			= МЕСЯЦ					##	Преобразует дату в числовом формате в месяцы.
+NETWORKDAYS		= ЧИСТРАБДНИ				##	Возвращает количество рабочих дней между двумя датами.
+NOW			= ТДАТА					##	Возвращает текущую дату и время в числовом формате.
+SECOND			= СЕКУНДЫ				##	Преобразует дату в числовом формате в секунды.
+TIME			= ВРЕМЯ					##	Возвращает заданное время в числовом формате.
+TIMEVALUE		= ВРЕМЗНАЧ				##	Преобразует время из текстового формата в числовой формат.
+TODAY			= СЕГОДНЯ				##	Возвращает текущую дату в числовом формате.
+WEEKDAY			= ДЕНЬНЕД				##	Преобразует дату в числовом формате в день недели.
+WEEKNUM			= НОМНЕДЕЛИ				##	Преобразует числовое представление в число, которое указывает, на какую неделю года приходится указанная дата.
+WORKDAY			= РАБДЕНЬ				##	Возвращает дату в числовом формате, отстоящую вперед или назад на заданное количество рабочих дней.
+YEAR			= ГОД					##	Преобразует дату в числовом формате в год.
+YEARFRAC		= ДОЛЯГОДА				##	Возвращает долю года, которую составляет количество дней между начальной и конечной датами.
+
+
+##
+##	Engineering functions					Инженерные функции
+##
+BESSELI			= БЕССЕЛЬ.I	 			##	Возвращает модифицированную функцию Бесселя In(x).
+BESSELJ			= БЕССЕЛЬ.J				##	Возвращает функцию Бесселя Jn(x).
+BESSELK			= БЕССЕЛЬ.K				##	Возвращает модифицированную функцию Бесселя Kn(x).
+BESSELY			= БЕССЕЛЬ.Y				##	Возвращает функцию Бесселя Yn(x).
+BIN2DEC			= ДВ.В.ДЕС				##	Преобразует двоичное число в десятичное.
+BIN2HEX			= ДВ.В.ШЕСТН				##	Преобразует двоичное число в шестнадцатеричное.
+BIN2OCT			= ДВ.В.ВОСЬМ				##	Преобразует двоичное число в восьмеричное.
+COMPLEX			= КОМПЛЕКСН				##	Преобразует коэффициенты при вещественной и мнимой частях комплексного числа в комплексное число.
+CONVERT			= ПРЕОБР				##	Преобразует число из одной системы единиц измерения в другую.
+DEC2BIN			= ДЕС.В.ДВ				##	Преобразует десятичное число в двоичное.
+DEC2HEX			= ДЕС.В.ШЕСТН				##	Преобразует десятичное число в шестнадцатеричное.
+DEC2OCT			= ДЕС.В.ВОСЬМ				##	Преобразует десятичное число в восьмеричное.
+DELTA			= ДЕЛЬТА				##	Проверяет равенство двух значений.
+ERF			= ФОШ					##	Возвращает функцию ошибки.
+ERFC			= ДФОШ					##	Возвращает дополнительную функцию ошибки.
+GESTEP			= ПОРОГ					##	Проверяет, не превышает ли данное число порогового значения.
+HEX2BIN			= ШЕСТН.В.ДВ				##	Преобразует шестнадцатеричное число в двоичное.
+HEX2DEC			= ШЕСТН.В.ДЕС				##	Преобразует шестнадцатеричное число в десятичное.
+HEX2OCT			= ШЕСТН.В.ВОСЬМ				##	Преобразует шестнадцатеричное число в восьмеричное.
+IMABS			= МНИМ.ABS				##	Возвращает абсолютную величину (модуль) комплексного числа.
+IMAGINARY		= МНИМ.ЧАСТЬ				##	Возвращает коэффициент при мнимой части комплексного числа.
+IMARGUMENT		= МНИМ.АРГУМЕНТ				##	Возвращает значение аргумента комплексного числа (тета) — угол, выраженный в радианах.
+IMCONJUGATE		= МНИМ.СОПРЯЖ				##	Возвращает комплексно-сопряженное комплексное число.
+IMCOS			= МНИМ.COS				##	Возвращает косинус комплексного числа.
+IMDIV			= МНИМ.ДЕЛ				##	Возвращает частное от деления двух комплексных чисел.
+IMEXP			= МНИМ.EXP				##	Возвращает экспоненту комплексного числа.
+IMLN			= МНИМ.LN				##	Возвращает натуральный логарифм комплексного числа.
+IMLOG10			= МНИМ.LOG10				##	Возвращает обычный (десятичный) логарифм комплексного числа.
+IMLOG2			= МНИМ.LOG2				##	Возвращает двоичный логарифм комплексного числа.
+IMPOWER			= МНИМ.СТЕПЕНЬ				##	Возвращает комплексное число, возведенное в целую степень.
+IMPRODUCT		= МНИМ.ПРОИЗВЕД				##	Возвращает произведение от 2 до 29 комплексных чисел.
+IMREAL			= МНИМ.ВЕЩ				##	Возвращает коэффициент при вещественной части комплексного числа.
+IMSIN			= МНИМ.SIN				##	Возвращает синус комплексного числа.
+IMSQRT			= МНИМ.КОРЕНЬ				##	Возвращает значение квадратного корня из комплексного числа.
+IMSUB			= МНИМ.РАЗН				##	Возвращает разность двух комплексных чисел.
+IMSUM			= МНИМ.СУММ				##	Возвращает сумму комплексных чисел.
+OCT2BIN			= ВОСЬМ.В.ДВ				##	Преобразует восьмеричное число в двоичное.
+OCT2DEC			= ВОСЬМ.В.ДЕС				##	Преобразует восьмеричное число в десятичное.
+OCT2HEX			= ВОСЬМ.В.ШЕСТН				##	Преобразует восьмеричное число в шестнадцатеричное.
+
+
+##
+##	Financial functions					Финансовые функции
+##
+ACCRINT			= НАКОПДОХОД				##	Возвращает накопленный процент по ценным бумагам с периодической выплатой процентов.
+ACCRINTM		= НАКОПДОХОДПОГАШ			##	Возвращает накопленный процент по ценным бумагам, проценты по которым выплачиваются в срок погашения.
+AMORDEGRC		= АМОРУМ				##	Возвращает величину амортизации для каждого периода, используя коэффициент амортизации.
+AMORLINC		= АМОРУВ				##	Возвращает величину амортизации для каждого периода.
+COUPDAYBS		= ДНЕЙКУПОНДО				##	Возвращает количество дней от начала действия купона до даты соглашения.
+COUPDAYS		= ДНЕЙКУПОН				##	Возвращает число дней в периоде купона, содержащем дату соглашения.
+COUPDAYSNC		= ДНЕЙКУПОНПОСЛЕ			##	Возвращает число дней от даты соглашения до срока следующего купона.
+COUPNCD			= ДАТАКУПОНПОСЛЕ			##	Возвращает следующую дату купона после даты соглашения.
+COUPNUM			= ЧИСЛКУПОН				##	Возвращает количество купонов, которые могут быть оплачены между датой соглашения и сроком вступления в силу.
+COUPPCD			= ДАТАКУПОНДО				##	Возвращает предыдущую дату купона перед датой соглашения.
+CUMIPMT			= ОБЩПЛАТ				##	Возвращает общую выплату, произведенную между двумя периодическими выплатами.
+CUMPRINC		= ОБЩДОХОД				##	Возвращает общую выплату по займу между двумя периодами.
+DB			= ФУО					##	Возвращает величину амортизации актива для заданного периода, рассчитанную методом фиксированного уменьшения остатка.
+DDB			= ДДОБ					##	Возвращает величину амортизации актива за данный период, используя метод двойного уменьшения остатка или иной явно указанный метод.
+DISC			= СКИДКА				##	Возвращает норму скидки для ценных бумаг.
+DOLLARDE		= РУБЛЬ.ДЕС				##	Преобразует цену в рублях, выраженную в виде дроби, в цену в рублях, выраженную десятичным числом.
+DOLLARFR		= РУБЛЬ.ДРОБЬ				##	Преобразует цену в рублях, выраженную десятичным числом, в цену в рублях, выраженную в виде дроби.
+DURATION		= ДЛИТ					##	Возвращает ежегодную продолжительность действия ценных бумаг с периодическими выплатами по процентам.
+EFFECT			= ЭФФЕКТ				##	Возвращает действующие ежегодные процентные ставки.
+FV			= БС					##	Возвращает будущую стоимость инвестиции.
+FVSCHEDULE		= БЗРАСПИС				##	Возвращает будущую стоимость первоначальной основной суммы после начисления ряда сложных процентов.
+INTRATE			= ИНОРМА				##	Возвращает процентную ставку для полностью инвестированных ценных бумаг.
+IPMT			= ПРПЛТ					##	Возвращает величину выплаты прибыли на вложения за данный период.
+IRR			= ВСД					##	Возвращает внутреннюю ставку доходности для ряда потоков денежных средств.
+ISPMT			= ПРОЦПЛАТ				##	Вычисляет выплаты за указанный период инвестиции.
+MDURATION		= МДЛИТ					##	Возвращает модифицированную длительность Маколея для ценных бумаг с предполагаемой номинальной стоимостью 100 рублей.
+MIRR			= МВСД					##	Возвращает внутреннюю ставку доходности, при которой положительные и отрицательные денежные потоки имеют разные значения ставки.
+NOMINAL			= НОМИНАЛ				##	Возвращает номинальную годовую процентную ставку.
+NPER			= КПЕР					##	Возвращает общее количество периодов выплаты для данного вклада.
+NPV			= ЧПС					##	Возвращает чистую приведенную стоимость инвестиции, основанной на серии периодических денежных потоков и ставке дисконтирования.
+ODDFPRICE		= ЦЕНАПЕРВНЕРЕГ				##	Возвращает цену за 100 рублей нарицательной стоимости ценных бумаг с нерегулярным первым периодом.
+ODDFYIELD		= ДОХОДПЕРВНЕРЕГ			##	Возвращает доход по ценным бумагам с нерегулярным первым периодом.
+ODDLPRICE		= ЦЕНАПОСЛНЕРЕГ				##	Возвращает цену за 100 рублей нарицательной стоимости ценных бумаг с нерегулярным последним периодом.
+ODDLYIELD		= ДОХОДПОСЛНЕРЕГ			##	Возвращает доход по ценным бумагам с нерегулярным последним периодом.
+PMT			= ПЛТ					##	Возвращает величину выплаты за один период аннуитета.
+PPMT			= ОСПЛТ					##	Возвращает величину выплат в погашение основной суммы по инвестиции за заданный период.
+PRICE			= ЦЕНА					##	Возвращает цену за 100 рублей нарицательной стоимости ценных бумаг, по которым производится периодическая выплата процентов.
+PRICEDISC		= ЦЕНАСКИДКА				##	Возвращает цену за 100 рублей номинальной стоимости ценных бумаг, на которые сделана скидка.
+PRICEMAT		= ЦЕНАПОГАШ				##	Возвращает цену за 100 рублей номинальной стоимости ценных бумаг, проценты по которым выплачиваются в срок погашения.
+PV			= ПС					##	Возвращает приведенную (к текущему моменту) стоимость инвестиции.
+RATE			= СТАВКА				##	Возвращает процентную ставку по аннуитету за один период.
+RECEIVED		= ПОЛУЧЕНО				##	Возвращает сумму, полученную к сроку погашения полностью обеспеченных ценных бумаг.
+SLN			= АПЛ					##	Возвращает величину линейной амортизации актива за один период.
+SYD			= АСЧ					##	Возвращает величину амортизации актива за данный период, рассчитанную методом суммы годовых чисел.
+TBILLEQ			= РАВНОКЧЕК				##	Возвращает эквивалентный облигации доход по казначейскому чеку.
+TBILLPRICE		= ЦЕНАКЧЕК				##	Возвращает цену за 100 рублей нарицательной стоимости для казначейского чека.
+TBILLYIELD		= ДОХОДКЧЕК				##	Возвращает доход по казначейскому чеку.
+VDB			= ПУО					##	Возвращает величину амортизации актива для указанного или частичного периода при использовании метода сокращающегося баланса.
+XIRR			= ЧИСТВНДОХ				##	Возвращает внутреннюю ставку доходности для графика денежных потоков, которые не обязательно носят периодический характер.
+XNPV			= ЧИСТНЗ				##	Возвращает чистую приведенную стоимость для денежных потоков, которые не обязательно являются периодическими.
+YIELD			= ДОХОД					##	Возвращает доход от ценных бумаг, по которым производятся периодические выплаты процентов.
+YIELDDISC		= ДОХОДСКИДКА				##	Возвращает годовой доход по ценным бумагам, на которые сделана скидка (пример — казначейские чеки).
+YIELDMAT		= ДОХОДПОГАШ				##	Возвращает годовой доход от ценных бумаг, проценты по которым выплачиваются в срок погашения.
+
+
+##
+##	Information functions					Информационные функции
+##
+CELL			= ЯЧЕЙКА				##	Возвращает информацию о формате, расположении или содержимом ячейки.
+ERROR.TYPE		= ТИП.ОШИБКИ				##	Возвращает числовой код, соответствующий типу ошибки.
+INFO			= ИНФОРМ				##	Возвращает информацию о текущей операционной среде.
+ISBLANK			= ЕПУСТО				##	Возвращает значение ИСТИНА, если аргумент является ссылкой на пустую ячейку.
+ISERR			= ЕОШ					##	Возвращает значение ИСТИНА, если аргумент ссылается на любое значение ошибки, кроме #Н/Д.
+ISERROR			= ЕОШИБКА				##	Возвращает значение ИСТИНА, если аргумент ссылается на любое значение ошибки.
+ISEVEN			= ЕЧЁТН					##	Возвращает значение ИСТИНА, если значение аргумента является четным числом.
+ISLOGICAL		= ЕЛОГИЧ				##	Возвращает значение ИСТИНА, если аргумент ссылается на логическое значение.
+ISNA			= ЕНД					##	Возвращает значение ИСТИНА, если аргумент ссылается на значение ошибки #Н/Д.
+ISNONTEXT		= ЕНЕТЕКСТ				##	Возвращает значение ИСТИНА, если значение аргумента не является текстом.
+ISNUMBER		= ЕЧИСЛО				##	Возвращает значение ИСТИНА, если аргумент ссылается на число.
+ISODD			= ЕНЕЧЁТ				##	Возвращает значение ИСТИНА, если значение аргумента является нечетным числом.
+ISREF			= ЕССЫЛКА				##	Возвращает значение ИСТИНА, если значение аргумента является ссылкой.
+ISTEXT			= ЕТЕКСТ				##	Возвращает значение ИСТИНА, если значение аргумента является текстом.
+N			= Ч					##	Возвращает значение, преобразованное в число.
+NA			= НД					##	Возвращает значение ошибки #Н/Д.
+TYPE			= ТИП					##	Возвращает число, обозначающее тип данных значения.
+
+
+##
+##	Logical functions					Логические функции
+##
+AND			= И					##	Renvoie VRAI si tous ses arguments sont VRAI.
+FALSE			= ЛОЖЬ					##	Возвращает логическое значение ЛОЖЬ.
+IF			= ЕСЛИ					##	Выполняет проверку условия.
+IFERROR			= ЕСЛИОШИБКА				##	Возвращает введённое значение, если вычисление по формуле вызывает ошибку; в противном случае функция возвращает результат вычисления.
+NOT			= НЕ					##	Меняет логическое значение своего аргумента на противоположное.
+OR			= ИЛИ					##	Возвращает значение ИСТИНА, если хотя бы один аргумент имеет значение ИСТИНА.
+TRUE			= ИСТИНА				##	Возвращает логическое значение ИСТИНА.
+
+
+##
+##	Lookup and reference functions				Функции ссылки и поиска
+##
+ADDRESS			= АДРЕС					##	Возвращает ссылку на отдельную ячейку листа в виде текста.
+AREAS			= ОБЛАСТИ				##	Возвращает количество областей в ссылке.
+CHOOSE			= ВЫБОР					##	Выбирает значение из списка значений по индексу.
+COLUMN			= СТОЛБЕЦ				##	Возвращает номер столбца, на который указывает ссылка.
+COLUMNS			= ЧИСЛСТОЛБ				##	Возвращает количество столбцов в ссылке.
+HLOOKUP			= ГПР					##	Ищет в первой строке массива и возвращает значение отмеченной ячейки
+HYPERLINK		= ГИПЕРССЫЛКА				##	Создает ссылку, открывающую документ, который находится на сервере сети, в интрасети или в Интернете.
+INDEX			= ИНДЕКС				##	Использует индекс для выбора значения из ссылки или массива.
+INDIRECT		= ДВССЫЛ				##	Возвращает ссылку, заданную текстовым значением.
+LOOKUP			= ПРОСМОТР				##	Ищет значения в векторе или массиве.
+MATCH			= ПОИСКПОЗ				##	Ищет значения в ссылке или массиве.
+OFFSET			= СМЕЩ					##	Возвращает смещение ссылки относительно заданной ссылки.
+ROW			= СТРОКА				##	Возвращает номер строки, определяемой ссылкой.
+ROWS			= ЧСТРОК				##	Возвращает количество строк в ссылке.
+RTD			= ДРВ					##	Извлекает данные реального времени из программ, поддерживающих автоматизацию COM (Программирование объектов. Стандартное средство для работы с объектами некоторого приложения из другого приложения или средства разработки. Программирование объектов (ранее называемое программированием OLE) является функцией модели COM (Component Object Model, модель компонентных объектов).).
+TRANSPOSE		= ТРАНСП				##	Возвращает транспонированный массив.
+VLOOKUP			= ВПР					##	Ищет значение в первом столбце массива и возвращает значение из ячейки в найденной строке и указанном столбце.
+
+
+##
+##	Math and trigonometry functions				Математические и тригонометрические функции
+##
+ABS			= ABS					##	Возвращает модуль (абсолютную величину) числа.
+ACOS			= ACOS					##	Возвращает арккосинус числа.
+ACOSH			= ACOSH					##	Возвращает гиперболический арккосинус числа.
+ASIN			= ASIN					##	Возвращает арксинус числа.
+ASINH			= ASINH					##	Возвращает гиперболический арксинус числа.
+ATAN			= ATAN					##	Возвращает арктангенс числа.
+ATAN2			= ATAN2					##	Возвращает арктангенс для заданных координат x и y.
+ATANH			= ATANH					##	Возвращает гиперболический арктангенс числа.
+CEILING			= ОКРВВЕРХ				##	Округляет число до ближайшего целого или до ближайшего кратного указанному значению.
+COMBIN			= ЧИСЛКОМБ				##	Возвращает количество комбинаций для заданного числа объектов.
+COS			= COS					##	Возвращает косинус числа.
+COSH			= COSH					##	Возвращает гиперболический косинус числа.
+DEGREES			= ГРАДУСЫ				##	Преобразует радианы в градусы.
+EVEN			= ЧЁТН					##	Округляет число до ближайшего четного целого.
+EXP			= EXP					##	Возвращает число e, возведенное в указанную степень.
+FACT			= ФАКТР					##	Возвращает факториал числа.
+FACTDOUBLE		= ДВФАКТР				##	Возвращает двойной факториал числа.
+FLOOR			= ОКРВНИЗ				##	Округляет число до ближайшего меньшего по модулю значения.
+GCD			= НОД					##	Возвращает наибольший общий делитель.
+INT			= ЦЕЛОЕ					##	Округляет число до ближайшего меньшего целого.
+LCM			= НОК					##	Возвращает наименьшее общее кратное.
+LN			= LN					##	Возвращает натуральный логарифм числа.
+LOG			= LOG					##	Возвращает логарифм числа по заданному основанию.
+LOG10			= LOG10					##	Возвращает десятичный логарифм числа.
+MDETERM			= МОПРЕД				##	Возвращает определитель матрицы массива.
+MINVERSE		= МОБР					##	Возвращает обратную матрицу массива.
+MMULT			= МУМНОЖ				##	Возвращает произведение матриц двух массивов.
+MOD			= ОСТАТ					##	Возвращает остаток от деления.
+MROUND			= ОКРУГЛТ				##	Возвращает число, округленное с требуемой точностью.
+MULTINOMIAL		= МУЛЬТИНОМ				##	Возвращает мультиномиальный коэффициент множества чисел.
+ODD			= НЕЧЁТ					##	Округляет число до ближайшего нечетного целого.
+PI			= ПИ					##	Возвращает число пи.
+POWER			= СТЕПЕНЬ				##	Возвращает результат возведения числа в степень.
+PRODUCT			= ПРОИЗВЕД				##	Возвращает произведение аргументов.
+QUOTIENT		= ЧАСТНОЕ				##	Возвращает целую часть частного при делении.
+RADIANS			= РАДИАНЫ				##	Преобразует градусы в радианы.
+RAND			= СЛЧИС					##	Возвращает случайное число в интервале от 0 до 1.
+RANDBETWEEN		= СЛУЧМЕЖДУ				##	Возвращает случайное число в интервале между двумя заданными числами.
+ROMAN			= РИМСКОЕ				##	Преобразует арабские цифры в римские в виде текста.
+ROUND			= ОКРУГЛ				##	Округляет число до указанного количества десятичных разрядов.
+ROUNDDOWN		= ОКРУГЛВНИЗ				##	Округляет число до ближайшего меньшего по модулю значения.
+ROUNDUP			= ОКРУГЛВВЕРХ				##	Округляет число до ближайшего большего по модулю значения.
+SERIESSUM		= РЯД.СУММ				##	Возвращает сумму степенного ряда, вычисленную по формуле.
+SIGN			= ЗНАК					##	Возвращает знак числа.
+SIN			= SIN					##	Возвращает синус заданного угла.
+SINH			= SINH					##	Возвращает гиперболический синус числа.
+SQRT			= КОРЕНЬ				##	Возвращает положительное значение квадратного корня.
+SQRTPI			= КОРЕНЬПИ				##	Возвращает квадратный корень из значения выражения (число * ПИ).
+SUBTOTAL		= ПРОМЕЖУТОЧНЫЕ.ИТОГИ			##	Возвращает промежуточный итог в списке или базе данных.
+SUM			= СУММ					##	Суммирует аргументы.
+SUMIF			= СУММЕСЛИ				##	Суммирует ячейки, удовлетворяющие заданному условию.
+SUMIFS			= СУММЕСЛИМН				##	Суммирует диапазон ячеек, удовлетворяющих нескольким условиям.
+SUMPRODUCT		= СУММПРОИЗВ				##	Возвращает сумму произведений соответствующих элементов массивов.
+SUMSQ			= СУММКВ				##	Возвращает сумму квадратов аргументов.
+SUMX2MY2		= СУММРАЗНКВ				##	Возвращает сумму разностей квадратов соответствующих значений в двух массивах.
+SUMX2PY2		= СУММСУММКВ				##	Возвращает сумму сумм квадратов соответствующих элементов двух массивов.
+SUMXMY2			= СУММКВРАЗН				##	Возвращает сумму квадратов разностей соответствующих значений в двух массивах.
+TAN			= TAN					##	Возвращает тангенс числа.
+TANH			= TANH					##	Возвращает гиперболический тангенс числа.
+TRUNC			= ОТБР					##	Отбрасывает дробную часть числа.
+
+
+##
+##	Statistical functions					Статистические функции
+##
+AVEDEV			= СРОТКЛ				##	Возвращает среднее арифметическое абсолютных значений отклонений точек данных от среднего.
+AVERAGE			= СРЗНАЧ				##	Возвращает среднее арифметическое аргументов.
+AVERAGEA		= СРЗНАЧА				##	Возвращает среднее арифметическое аргументов, включая числа, текст и логические значения.
+AVERAGEIF		= СРЗНАЧЕСЛИ 				##	Возвращает среднее значение (среднее арифметическое) всех ячеек в диапазоне, которые удовлетворяют данному условию.
+AVERAGEIFS		= СРЗНАЧЕСЛИМН 				##	Возвращает среднее значение (среднее арифметическое) всех ячеек, которые удовлетворяют нескольким условиям.
+BETADIST		= БЕТАРАСП				##	Возвращает интегральную функцию бета-распределения.
+BETAINV			= БЕТАОБР				##	Возвращает обратную интегральную функцию указанного бета-распределения.
+BINOMDIST		= БИНОМРАСП				##	Возвращает отдельное значение биномиального распределения.
+CHIDIST			= ХИ2РАСП				##	Возвращает одностороннюю вероятность распределения хи-квадрат.
+CHIINV			= ХИ2ОБР				##	Возвращает обратное значение односторонней вероятности распределения хи-квадрат.
+CHITEST			= ХИ2ТЕСТ				##	Возвращает тест на независимость.
+CONFIDENCE		= ДОВЕРИТ				##	Возвращает доверительный интервал для среднего значения по генеральной совокупности.
+CORREL			= КОРРЕЛ				##	Возвращает коэффициент корреляции между двумя множествами данных.
+COUNT			= СЧЁТ					##	Подсчитывает количество чисел в списке аргументов.
+COUNTA			= СЧЁТЗ					##	Подсчитывает количество значений в списке аргументов.
+COUNTBLANK		= СЧИТАТЬПУСТОТЫ			##	Подсчитывает количество пустых ячеек в диапазоне
+COUNTIF			= СЧЁТЕСЛИ 				##	Подсчитывает количество ячеек в диапазоне, удовлетворяющих заданному условию
+COUNTIFS		= СЧЁТЕСЛИМН				##	Подсчитывает количество ячеек внутри диапазона, удовлетворяющих нескольким условиям.
+COVAR			= КОВАР					##	Возвращает ковариацию, среднее произведений парных отклонений
+CRITBINOM		= КРИТБИНОМ				##	Возвращает наименьшее значение, для которого интегральное биномиальное распределение меньше или равно заданному критерию.
+DEVSQ			= КВАДРОТКЛ				##	Возвращает сумму квадратов отклонений.
+EXPONDIST		= ЭКСПРАСП				##	Возвращает экспоненциальное распределение.
+FDIST			= FРАСП					##	Возвращает F-распределение вероятности.
+FINV			= FРАСПОБР				##	Возвращает обратное значение для F-распределения вероятности.
+FISHER			= ФИШЕР					##	Возвращает преобразование Фишера.
+FISHERINV		= ФИШЕРОБР				##	Возвращает обратное преобразование Фишера.
+FORECAST		= ПРЕДСКАЗ				##	Возвращает значение линейного тренда.
+FREQUENCY		= ЧАСТОТА				##	Возвращает распределение частот в виде вертикального массива.
+FTEST			= ФТЕСТ					##	Возвращает результат F-теста.
+GAMMADIST		= ГАММАРАСП				##	Возвращает гамма-распределение.
+GAMMAINV		= ГАММАОБР				##	Возвращает обратное гамма-распределение.
+GAMMALN			= ГАММАНЛОГ				##	Возвращает натуральный логарифм гамма функции, Γ(x).
+GEOMEAN			= СРГЕОМ				##	Возвращает среднее геометрическое.
+GROWTH			= РОСТ					##	Возвращает значения в соответствии с экспоненциальным трендом.
+HARMEAN			= СРГАРМ				##	Возвращает среднее гармоническое.
+HYPGEOMDIST		= ГИПЕРГЕОМЕТ				##	Возвращает гипергеометрическое распределение.
+INTERCEPT		= ОТРЕЗОК				##	Возвращает отрезок, отсекаемый на оси линией линейной регрессии.
+KURT			= ЭКСЦЕСС				##	Возвращает эксцесс множества данных.
+LARGE			= НАИБОЛЬШИЙ				##	Возвращает k-ое наибольшее значение в множестве данных.
+LINEST			= ЛИНЕЙН				##	Возвращает параметры линейного тренда.
+LOGEST			= ЛГРФПРИБЛ				##	Возвращает параметры экспоненциального тренда.
+LOGINV			= ЛОГНОРМОБР				##	Возвращает обратное логарифмическое нормальное распределение.
+LOGNORMDIST		= ЛОГНОРМРАСП				##	Возвращает интегральное логарифмическое нормальное распределение.
+MAX			= МАКС					##	Возвращает наибольшее значение в списке аргументов.
+MAXA			= МАКСА					##	Возвращает наибольшее значение в списке аргументов, включая числа, текст и логические значения.
+MEDIAN			= МЕДИАНА				##	Возвращает медиану заданных чисел.
+MIN			= МИН					##	Возвращает наименьшее значение в списке аргументов.
+MINA			= МИНА					##	Возвращает наименьшее значение в списке аргументов, включая числа, текст и логические значения.
+MODE			= МОДА					##	Возвращает значение моды множества данных.
+NEGBINOMDIST		= ОТРБИНОМРАСП				##	Возвращает отрицательное биномиальное распределение.
+NORMDIST		= НОРМРАСП				##	Возвращает нормальную функцию распределения.
+NORMINV			= НОРМОБР				##	Возвращает обратное нормальное распределение.
+NORMSDIST		= НОРМСТРАСП				##	Возвращает стандартное нормальное интегральное распределение.
+NORMSINV		= НОРМСТОБР				##	Возвращает обратное значение стандартного нормального распределения.
+PEARSON			= ПИРСОН				##	Возвращает коэффициент корреляции Пирсона.
+PERCENTILE		= ПЕРСЕНТИЛЬ				##	Возвращает k-ую персентиль для значений диапазона.
+PERCENTRANK		= ПРОЦЕНТРАНГ				##	Возвращает процентную норму значения в множестве данных.
+PERMUT			= ПЕРЕСТ				##	Возвращает количество перестановок для заданного числа объектов.
+POISSON			= ПУАССОН				##	Возвращает распределение Пуассона.
+PROB			= ВЕРОЯТНОСТЬ				##	Возвращает вероятность того, что значение из диапазона находится внутри заданных пределов.
+QUARTILE		= КВАРТИЛЬ				##	Возвращает квартиль множества данных.
+RANK			= РАНГ					##	Возвращает ранг числа в списке чисел.
+RSQ			= КВПИРСОН				##	Возвращает квадрат коэффициента корреляции Пирсона.
+SKEW			= СКОС					##	Возвращает асимметрию распределения.
+SLOPE			= НАКЛОН				##	Возвращает наклон линии линейной регрессии.
+SMALL			= НАИМЕНЬШИЙ				##	Возвращает k-ое наименьшее значение в множестве данных.
+STANDARDIZE		= НОРМАЛИЗАЦИЯ				##	Возвращает нормализованное значение.
+STDEV			= СТАНДОТКЛОН				##	Оценивает стандартное отклонение по выборке.
+STDEVA			= СТАНДОТКЛОНА				##	Оценивает стандартное отклонение по выборке, включая числа, текст и логические значения.
+STDEVP			= СТАНДОТКЛОНП				##	Вычисляет стандартное отклонение по генеральной совокупности.
+STDEVPA			= СТАНДОТКЛОНПА				##	Вычисляет стандартное отклонение по генеральной совокупности, включая числа, текст и логические значения.
+STEYX			= СТОШYX				##	Возвращает стандартную ошибку предсказанных значений y для каждого значения x в регрессии.
+TDIST			= СТЬЮДРАСП				##	Возвращает t-распределение Стьюдента.
+TINV			= СТЬЮДРАСПОБР				##	Возвращает обратное t-распределение Стьюдента.
+TREND			= ТЕНДЕНЦИЯ				##	Возвращает значения в соответствии с линейным трендом.
+TRIMMEAN		= УРЕЗСРЕДНЕЕ				##	Возвращает среднее внутренности множества данных.
+TTEST			= ТТЕСТ					##	Возвращает вероятность, соответствующую критерию Стьюдента.
+VAR			= ДИСП					##	Оценивает дисперсию по выборке.
+VARA			= ДИСПА					##	Оценивает дисперсию по выборке, включая числа, текст и логические значения.
+VARP			= ДИСПР					##	Вычисляет дисперсию для генеральной совокупности.
+VARPA			= ДИСПРА				##	Вычисляет дисперсию для генеральной совокупности, включая числа, текст и логические значения.
+WEIBULL			= ВЕЙБУЛЛ				##	Возвращает распределение Вейбулла.
+ZTEST			= ZТЕСТ					##	Возвращает двустороннее P-значение z-теста.
+
+
+##
+##	Text functions						Текстовые функции
+##
+ASC			= ASC					##	Для языков с двухбайтовыми наборами знаков (например, катакана) преобразует полноширинные (двухбайтовые) знаки в полуширинные (однобайтовые).
+BAHTTEXT		= БАТТЕКСТ				##	Преобразует число в текст, используя денежный формат ß (БАТ).
+CHAR			= СИМВОЛ				##	Возвращает знак с заданным кодом.
+CLEAN			= ПЕЧСИМВ				##	Удаляет все непечатаемые знаки из текста.
+CODE			= КОДСИМВ				##	Возвращает числовой код первого знака в текстовой строке.
+CONCATENATE		= СЦЕПИТЬ				##	Объединяет несколько текстовых элементов в один.
+DOLLAR			= РУБЛЬ					##	Преобразует число в текст, используя денежный формат.
+EXACT			= СОВПАД				##	Проверяет идентичность двух текстовых значений.
+FIND			= НАЙТИ					##	Ищет вхождения одного текстового значения в другом (с учетом регистра).
+FINDB			= НАЙТИБ				##	Ищет вхождения одного текстового значения в другом (с учетом регистра).
+FIXED			= ФИКСИРОВАННЫЙ				##	Форматирует число и преобразует его в текст с заданным числом десятичных знаков.
+JIS			= JIS					##	Для языков с двухбайтовыми наборами знаков (например, катакана) преобразует полуширинные (однобайтовые) знаки в текстовой строке в полноширинные (двухбайтовые).
+LEFT			= ЛЕВСИМВ				##	Возвращает крайние слева знаки текстового значения.
+LEFTB			= ЛЕВБ					##	Возвращает крайние слева знаки текстового значения.
+LEN			= ДЛСТР					##	Возвращает количество знаков в текстовой строке.
+LENB			= ДЛИНБ					##	Возвращает количество знаков в текстовой строке.
+LOWER			= СТРОЧН				##	Преобразует все буквы текста в строчные.
+MID			= ПСТР					##	Возвращает заданное число знаков из строки текста, начиная с указанной позиции.
+MIDB			= ПСТРБ					##	Возвращает заданное число знаков из строки текста, начиная с указанной позиции.
+PHONETIC		= PHONETIC				##	Извлекает фонетические (фуригана) знаки из текстовой строки.
+PROPER			= ПРОПНАЧ				##	Преобразует первую букву в каждом слове текста в прописную.
+REPLACE			= ЗАМЕНИТЬ				##	Заменяет знаки в тексте.
+REPLACEB		= ЗАМЕНИТЬБ				##	Заменяет знаки в тексте.
+REPT			= ПОВТОР				##	Повторяет текст заданное число раз.
+RIGHT			= ПРАВСИМВ				##	Возвращает крайние справа знаки текстовой строки.
+RIGHTB			= ПРАВБ					##	Возвращает крайние справа знаки текстовой строки.
+SEARCH			= ПОИСК					##	Ищет вхождения одного текстового значения в другом (без учета регистра).
+SEARCHB			= ПОИСКБ				##	Ищет вхождения одного текстового значения в другом (без учета регистра).
+SUBSTITUTE		= ПОДСТАВИТЬ				##	Заменяет в текстовой строке старый текст новым.
+T			= Т					##	Преобразует аргументы в текст.
+TEXT			= ТЕКСТ					##	Форматирует число и преобразует его в текст.
+TRIM			= СЖПРОБЕЛЫ				##	Удаляет из текста пробелы.
+UPPER			= ПРОПИСН				##	Преобразует все буквы текста в прописные.
+VALUE			= ЗНАЧЕН				##	Преобразует текстовый аргумент в число.
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/sv/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/sv/config
new file mode 100755
index 0000000..bf72cc4
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/sv/config
@@ -0,0 +1,24 @@
+##
+## PhpSpreadsheet
+##
+
+ArgumentSeparator	= ;
+
+
+##
+##	(For future use)
+##
+currencySymbol	= kr
+
+
+##
+##	Excel Error Codes	(For future use)
+
+##
+NULL	= #Skärning!
+DIV0	= #Division/0!
+VALUE	= #Värdefel!
+REF	= #Referens!
+NAME	= #Namn?
+NUM	= #Ogiltigt!
+NA	= #Saknas!
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/sv/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/sv/functions
new file mode 100755
index 0000000..73b2deb
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/sv/functions
@@ -0,0 +1,408 @@
+##
+##	Add-in and Automation functions			Tilläggs- och automatiseringsfunktioner
+##
+GETPIVOTDATA		= HÄMTA.PIVOTDATA		##	Returnerar data som lagrats i en pivottabellrapport
+
+
+##
+##	Cube functions					Kubfunktioner
+##
+CUBEKPIMEMBER		= KUBKPIMEDLEM			##	Returnerar namn, egenskap och mått för en KPI och visar namnet och egenskapen i cellen. En KPI, eller prestandaindikator, är ett kvantifierbart mått, t.ex. månatlig bruttovinst eller personalomsättning per kvartal, som används för att analysera ett företags resultat.
+CUBEMEMBER		= KUBMEDLEM			##	Returnerar en medlem eller ett par i en kubhierarki. Används för att verifiera att medlemmen eller paret finns i kuben.
+CUBEMEMBERPROPERTY	= KUBMEDLEMSEGENSKAP		##	Returnerar värdet för en medlemsegenskap i kuben. Används för att verifiera att ett medlemsnamn finns i kuben, samt för att returnera den angivna egenskapen för medlemmen.
+CUBERANKEDMEMBER	= KUBRANGORDNADMEDLEM		##	Returnerar den n:te, eller rangordnade, medlemmen i en uppsättning. Används för att returnera ett eller flera element i en uppsättning, till exempelvis den bästa försäljaren eller de tio bästa eleverna.
+CUBESET			= KUBINSTÄLLNING		##	Definierar en beräknad uppsättning medlemmar eller par genom att skicka ett bestämt uttryck till kuben på servern, som skapar uppsättningen och sedan returnerar den till Microsoft Office Excel.
+CUBESETCOUNT		= KUBINSTÄLLNINGANTAL		##	Returnerar antalet objekt i en uppsättning.
+CUBEVALUE		= KUBVÄRDE			##	Returnerar ett mängdvärde från en kub.
+
+
+##
+##	Database functions				Databasfunktioner
+##
+DAVERAGE		= DMEDEL			##	Returnerar medelvärdet av databasposterna
+DCOUNT			= DANTAL			##	Räknar antalet celler som innehåller tal i en databas
+DCOUNTA			= DANTALV			##	Räknar ifyllda celler i en databas
+DGET			= DHÄMTA			##	Hämtar en enstaka post från en databas som uppfyller de angivna villkoren
+DMAX			= DMAX				##	Returnerar det största värdet från databasposterna
+DMIN			= DMIN				##	Returnerar det minsta värdet från databasposterna
+DPRODUCT		= DPRODUKT			##	Multiplicerar värdena i ett visst fält i poster som uppfyller villkoret
+DSTDEV			= DSTDAV			##	Uppskattar standardavvikelsen baserat på ett urval av databasposterna
+DSTDEVP			= DSTDAVP			##	Beräknar standardavvikelsen utifrån hela populationen av valda databasposter
+DSUM			= DSUMMA			##	Summerar talen i kolumnfält i databasposter som uppfyller villkoret
+DVAR			= DVARIANS			##	Uppskattar variansen baserat på ett urval av databasposterna
+DVARP			= DVARIANSP			##	Beräknar variansen utifrån hela populationen av valda databasposter
+
+
+##
+##	Date and time functions				Tid- och datumfunktioner
+##
+DATE			= DATUM				##	Returnerar ett serienummer för ett visst datum
+DATEVALUE		= DATUMVÄRDE			##	Konverterar ett datum i textformat till ett serienummer
+DAY			= DAG				##	Konverterar ett serienummer till dag i månaden
+DAYS360			= DAGAR360			##	Beräknar antalet dagar mellan två datum baserat på ett 360-dagarsår
+EDATE			= EDATUM			##	Returnerar serienumret för ett datum som infaller ett visst antal månader före eller efter startdatumet
+EOMONTH			= SLUTMÅNAD			##	Returnerar serienumret för sista dagen i månaden ett visst antal månader tidigare eller senare
+HOUR			= TIMME				##	Konverterar ett serienummer till en timme
+MINUTE			= MINUT				##	Konverterar ett serienummer till en minut
+MONTH			= MÅNAD				##	Konverterar ett serienummer till en månad
+NETWORKDAYS		= NETTOARBETSDAGAR		##	Returnerar antalet hela arbetsdagar mellan två datum
+NOW			= NU				##	Returnerar serienumret för dagens datum och aktuell tid
+SECOND			= SEKUND			##	Konverterar ett serienummer till en sekund
+TIME			= KLOCKSLAG			##	Returnerar serienumret för en viss tid
+TIMEVALUE		= TIDVÄRDE			##	Konverterar en tid i textformat till ett serienummer
+TODAY			= IDAG				##	Returnerar serienumret för dagens datum
+WEEKDAY			= VECKODAG			##	Konverterar ett serienummer till en dag i veckan
+WEEKNUM			= VECKONR			##	Konverterar ett serienummer till ett veckonummer
+WORKDAY			= ARBETSDAGAR			##	Returnerar serienumret för ett datum ett visst antal arbetsdagar tidigare eller senare
+YEAR			= ÅR				##	Konverterar ett serienummer till ett år
+YEARFRAC		= ÅRDEL				##	Returnerar en del av ett år som representerar antalet hela dagar mellan start- och slutdatum
+
+
+##
+##	Engineering functions				Tekniska funktioner
+##
+BESSELI			= BESSELI			##	Returnerar den modifierade Bessel-funktionen In(x)
+BESSELJ			= BESSELJ			##	Returnerar Bessel-funktionen Jn(x)
+BESSELK			= BESSELK			##	Returnerar den modifierade Bessel-funktionen Kn(x)
+BESSELY			= BESSELY			##	Returnerar Bessel-funktionen Yn(x)
+BIN2DEC			= BIN.TILL.DEC			##	Omvandlar ett binärt tal till decimalt
+BIN2HEX			= BIN.TILL.HEX			##	Omvandlar ett binärt tal till hexadecimalt
+BIN2OCT			= BIN.TILL.OKT			##	Omvandlar ett binärt tal till oktalt
+COMPLEX			= KOMPLEX			##	Omvandlar reella och imaginära koefficienter till ett komplext tal
+CONVERT			= KONVERTERA			##	Omvandlar ett tal från ett måttsystem till ett annat
+DEC2BIN			= DEC.TILL.BIN			##	Omvandlar ett decimalt tal till binärt
+DEC2HEX			= DEC.TILL.HEX			##	Omvandlar ett decimalt tal till hexadecimalt
+DEC2OCT			= DEC.TILL.OKT			##	Omvandlar ett decimalt tal till oktalt
+DELTA			= DELTA				##	Testar om två värden är lika
+ERF			= FELF				##	Returnerar felfunktionen
+ERFC			= FELFK				##	Returnerar den komplementära felfunktionen
+GESTEP			= SLSTEG			##	Testar om ett tal är större än ett tröskelvärde
+HEX2BIN			= HEX.TILL.BIN			##	Omvandlar ett hexadecimalt tal till binärt
+HEX2DEC			= HEX.TILL.DEC			##	Omvandlar ett hexadecimalt tal till decimalt
+HEX2OCT			= HEX.TILL.OKT			##	Omvandlar ett hexadecimalt tal till oktalt
+IMABS			= IMABS				##	Returnerar absolutvärdet (modulus) för ett komplext tal
+IMAGINARY		= IMAGINÄR			##	Returnerar den imaginära koefficienten för ett komplext tal
+IMARGUMENT		= IMARGUMENT			##	Returnerar det komplexa talets argument, en vinkel uttryckt i radianer
+IMCONJUGATE		= IMKONJUGAT			##	Returnerar det komplexa talets konjugat
+IMCOS			= IMCOS				##	Returnerar cosinus för ett komplext tal
+IMDIV			= IMDIV				##	Returnerar kvoten för två komplexa tal
+IMEXP			= IMEUPPHÖJT			##	Returnerar exponenten för ett komplext tal
+IMLN			= IMLN				##	Returnerar den naturliga logaritmen för ett komplext tal
+IMLOG10			= IMLOG10			##	Returnerar 10-logaritmen för ett komplext tal
+IMLOG2			= IMLOG2			##	Returnerar 2-logaritmen för ett komplext tal
+IMPOWER			= IMUPPHÖJT			##	Returnerar ett komplext tal upphöjt till en exponent
+IMPRODUCT		= IMPRODUKT			##	Returnerar produkten av komplexa tal
+IMREAL			= IMREAL			##	Returnerar den reella koefficienten för ett komplext tal
+IMSIN			= IMSIN				##	Returnerar sinus för ett komplext tal
+IMSQRT			= IMROT				##	Returnerar kvadratroten av ett komplext tal
+IMSUB			= IMDIFF			##	Returnerar differensen mellan två komplexa tal
+IMSUM			= IMSUM				##	Returnerar summan av komplexa tal
+OCT2BIN			= OKT.TILL.BIN			##	Omvandlar ett oktalt tal till binärt
+OCT2DEC			= OKT.TILL.DEC			##	Omvandlar ett oktalt tal till decimalt
+OCT2HEX			= OKT.TILL.HEX			##	Omvandlar ett oktalt tal till hexadecimalt
+
+
+##
+##	Financial functions				Finansiella funktioner
+##
+ACCRINT			= UPPLRÄNTA			##	Returnerar den upplupna räntan för värdepapper med periodisk ränta
+ACCRINTM		= UPPLOBLRÄNTA			##	Returnerar den upplupna räntan för ett värdepapper som ger avkastning på förfallodagen
+AMORDEGRC		= AMORDEGRC			##	Returnerar avskrivningen för varje redovisningsperiod med hjälp av en avskrivningskoefficient
+AMORLINC		= AMORLINC			##	Returnerar avskrivningen för varje redovisningsperiod
+COUPDAYBS		= KUPDAGBB			##	Returnerar antal dagar från början av kupongperioden till likviddagen
+COUPDAYS		= KUPDAGARS			##	Returnerar antalet dagar i kupongperioden som innehåller betalningsdatumet
+COUPDAYSNC		= KUPDAGNK			##	Returnerar antalet dagar från betalningsdatumet till nästa kupongdatum
+COUPNCD			= KUPNKD			##	Returnerar nästa kupongdatum efter likviddagen
+COUPNUM			= KUPANT			##	Returnerar kuponger som förfaller till betalning mellan likviddagen och förfallodagen
+COUPPCD			= KUPFKD			##	Returnerar föregående kupongdatum före likviddagen
+CUMIPMT			= KUMRÄNTA			##	Returnerar den ackumulerade räntan som betalats mellan två perioder
+CUMPRINC		= KUMPRIS			##	Returnerar det ackumulerade kapitalbeloppet som betalats på ett lån mellan två perioder
+DB			= DB				##	Returnerar avskrivningen för en tillgång under en angiven tid enligt metoden för fast degressiv avskrivning
+DDB			= DEGAVSKR			##	Returnerar en tillgångs värdeminskning under en viss period med hjälp av dubbel degressiv avskrivning eller någon annan metod som du anger
+DISC			= DISK				##	Returnerar diskonteringsräntan för ett värdepapper
+DOLLARDE		= DECTAL			##	Omvandlar ett pris uttryckt som ett bråk till ett decimaltal
+DOLLARFR		= BRÅK				##	Omvandlar ett pris i kronor uttryckt som ett decimaltal till ett bråk
+DURATION		= LÖPTID			##	Returnerar den årliga löptiden för en säkerhet med periodiska räntebetalningar
+EFFECT			= EFFRÄNTA			##	Returnerar den årliga effektiva räntesatsen
+FV			= SLUTVÄRDE			##	Returnerar det framtida värdet på en investering
+FVSCHEDULE		= FÖRRÄNTNING			##	Returnerar det framtida värdet av ett begynnelsekapital beräknat på olika räntenivåer
+INTRATE			= ÅRSRÄNTA			##	Returnerar räntesatsen för ett betalt värdepapper
+IPMT			= RBETALNING			##	Returnerar räntedelen av en betalning för en given period
+IRR			= IR				##	Returnerar internräntan för en serie betalningar
+ISPMT			= RALÅN				##	Beräknar räntan som har betalats under en specifik betalningsperiod
+MDURATION		= MLÖPTID			##	Returnerar den modifierade Macauley-löptiden för ett värdepapper med det antagna nominella värdet 100 kr
+MIRR			= MODIR				##	Returnerar internräntan där positiva och negativa betalningar finansieras med olika räntor
+NOMINAL			= NOMRÄNTA			##	Returnerar den årliga nominella räntesatsen
+NPER			= PERIODER			##	Returnerar antalet perioder för en investering
+NPV			= NETNUVÄRDE			##	Returnerar nuvärdet av en serie periodiska betalningar vid en given diskonteringsränta
+ODDFPRICE		= UDDAFPRIS			##	Returnerar priset per 100 kr nominellt värde för ett värdepapper med en udda första period
+ODDFYIELD		= UDDAFAVKASTNING		##	Returnerar avkastningen för en säkerhet med en udda första period
+ODDLPRICE		= UDDASPRIS			##	Returnerar priset per 100 kr nominellt värde för ett värdepapper med en udda sista period
+ODDLYIELD		= UDDASAVKASTNING		##	Returnerar avkastningen för en säkerhet med en udda sista period
+PMT			= BETALNING			##	Returnerar den periodiska betalningen för en annuitet
+PPMT			= AMORT				##	Returnerar amorteringsdelen av en annuitetsbetalning för en given period
+PRICE			= PRIS				##	Returnerar priset per 100 kr nominellt värde för ett värdepapper som ger periodisk ränta
+PRICEDISC		= PRISDISK			##	Returnerar priset per 100 kr nominellt värde för ett diskonterat värdepapper
+PRICEMAT		= PRISFÖRF			##	Returnerar priset per 100 kr nominellt värde för ett värdepapper som ger ränta på förfallodagen
+PV			= PV				##	Returnerar nuvärdet av en serie lika stora periodiska betalningar
+RATE			= RÄNTA				##	Returnerar räntesatsen per period i en annuitet
+RECEIVED		= BELOPP			##	Returnerar beloppet som utdelas på förfallodagen för ett betalat värdepapper
+SLN			= LINAVSKR			##	Returnerar den linjära avskrivningen för en tillgång under en period
+SYD			= ÅRSAVSKR			##	Returnerar den årliga avskrivningssumman för en tillgång under en angiven period
+TBILLEQ			= SSVXEKV			##	Returnerar avkastningen motsvarande en obligation för en statsskuldväxel
+TBILLPRICE		= SSVXPRIS			##	Returnerar priset per 100 kr nominellt värde för en statsskuldväxel
+TBILLYIELD		= SSVXRÄNTA			##	Returnerar avkastningen för en statsskuldväxel
+VDB			= VDEGRAVSKR			##	Returnerar avskrivningen för en tillgång under en angiven period (med degressiv avskrivning)
+XIRR			= XIRR				##	Returnerar internräntan för en serie betalningar som inte nödvändigtvis är periodiska
+XNPV			= XNUVÄRDE			##	Returnerar det nuvarande nettovärdet för en serie betalningar som inte nödvändigtvis är periodiska
+YIELD			= NOMAVK			##	Returnerar avkastningen för ett värdepapper som ger periodisk ränta
+YIELDDISC		= NOMAVKDISK			##	Returnerar den årliga avkastningen för diskonterade värdepapper, exempelvis en statsskuldväxel
+YIELDMAT		= NOMAVKFÖRF			##	Returnerar den årliga avkastningen för ett värdepapper som ger ränta på förfallodagen
+
+
+##
+##	Information functions				Informationsfunktioner
+##
+CELL			= CELL				##	Returnerar information om formatering, plats och innehåll i en cell
+ERROR.TYPE		= FEL.TYP			##	Returnerar ett tal som motsvarar ett felvärde
+INFO			= INFO				##	Returnerar information om operativsystemet
+ISBLANK			= ÄRREF				##	Returnerar SANT om värdet är tomt
+ISERR			= Ä				##	Returnerar SANT om värdet är ett felvärde annat än #SAKNAS!
+ISERROR			= ÄRFEL				##	Returnerar SANT om värdet är ett felvärde
+ISEVEN			= ÄRJÄMN			##	Returnerar SANT om talet är jämnt
+ISLOGICAL		= ÄREJTEXT			##	Returnerar SANT om värdet är ett logiskt värde
+ISNA			= ÄRLOGISK			##	Returnerar SANT om värdet är felvärdet #SAKNAS!
+ISNONTEXT		= ÄRSAKNAD			##	Returnerar SANT om värdet inte är text
+ISNUMBER		= ÄRTAL				##	Returnerar SANT om värdet är ett tal
+ISODD			= ÄRUDDA			##	Returnerar SANT om talet är udda
+ISREF			= ÄRTOM				##	Returnerar SANT om värdet är en referens
+ISTEXT			= ÄRTEXT			##	Returnerar SANT om värdet är text
+N			= N				##	Returnerar ett värde omvandlat till ett tal
+NA			= SAKNAS			##	Returnerar felvärdet #SAKNAS!
+TYPE			= VÄRDETYP			##	Returnerar ett tal som anger värdets datatyp
+
+
+##
+##	Logical functions				Logiska funktioner
+##
+AND			= OCH				##	Returnerar SANT om alla argument är sanna
+FALSE			= FALSKT			##	Returnerar det logiska värdet FALSKT
+IF			= OM				##	Anger vilket logiskt test som ska utföras
+IFERROR			= OMFEL				##	Returnerar ett värde som du anger om en formel utvärderar till ett fel; annars returneras resultatet av formeln
+NOT			= ICKE				##	Inverterar logiken för argumenten
+OR			= ELLER				##	Returnerar SANT om något argument är SANT
+TRUE			= SANT				##	Returnerar det logiska värdet SANT
+
+
+##
+##	Lookup and reference functions			Sök- och referensfunktioner
+##
+ADDRESS			= ADRESS			##	Returnerar en referens som text till en enstaka cell i ett kalkylblad
+AREAS			= OMRÅDEN			##	Returnerar antalet områden i en referens
+CHOOSE			= VÄLJ				##	Väljer ett värde i en lista över värden
+COLUMN			= KOLUMN			##	Returnerar kolumnnumret för en referens
+COLUMNS			= KOLUMNER			##	Returnerar antalet kolumner i en referens
+HLOOKUP			= LETAKOLUMN			##	Söker i den översta raden i en matris och returnerar värdet för angiven cell
+HYPERLINK		= HYPERLÄNK			##	Skapar en genväg eller ett hopp till ett dokument i nätverket, i ett intranät eller på Internet
+INDEX			= INDEX				##	Använder ett index för ett välja ett värde i en referens eller matris
+INDIRECT		= INDIREKT			##	Returnerar en referens som anges av ett textvärde
+LOOKUP			= LETAUPP			##	Letar upp värden i en vektor eller matris
+MATCH			= PASSA				##	Letar upp värden i en referens eller matris
+OFFSET			= FÖRSKJUTNING			##	Returnerar en referens förskjuten i förhållande till en given referens
+ROW			= RAD				##	Returnerar radnumret för en referens
+ROWS			= RADER				##	Returnerar antalet rader i en referens
+RTD			= RTD				##	Hämtar realtidsdata från ett program som stöder COM-automation (Automation: Ett sätt att arbeta med ett programs objekt från ett annat program eller utvecklingsverktyg. Detta kallades tidigare för OLE Automation, och är en branschstandard och ingår i Component Object Model (COM).)
+TRANSPOSE		= TRANSPONERA			##	Transponerar en matris
+VLOOKUP			= LETARAD			##	Letar i den första kolumnen i en matris och flyttar över raden för att returnera värdet för en cell
+
+
+##
+##	Math and trigonometry functions			Matematiska och trigonometriska funktioner
+##
+ABS			= ABS				##	Returnerar absolutvärdet av ett tal
+ACOS			= ARCCOS			##	Returnerar arcus cosinus för ett tal
+ACOSH			= ARCCOSH			##	Returnerar inverterad hyperbolisk cosinus för ett tal
+ASIN			= ARCSIN			##	Returnerar arcus cosinus för ett tal
+ASINH			= ARCSINH			##	Returnerar hyperbolisk arcus sinus för ett tal
+ATAN			= ARCTAN			##	Returnerar arcus tangens för ett tal
+ATAN2			= ARCTAN2			##	Returnerar arcus tangens för en x- och en y- koordinat
+ATANH			= ARCTANH			##	Returnerar hyperbolisk arcus tangens för ett tal
+CEILING			= RUNDA.UPP			##	Avrundar ett tal till närmaste heltal eller närmaste signifikanta multipel
+COMBIN			= KOMBIN			##	Returnerar antalet kombinationer för ett givet antal objekt
+COS			= COS				##	Returnerar cosinus för ett tal
+COSH			= COSH				##	Returnerar hyperboliskt cosinus för ett tal
+DEGREES			= GRADER			##	Omvandlar radianer till grader
+EVEN			= JÄMN				##	Avrundar ett tal uppåt till närmaste heltal
+EXP			= EXP				##	Returnerar e upphöjt till ett givet tal
+FACT			= FAKULTET			##	Returnerar fakulteten för ett tal
+FACTDOUBLE		= DUBBELFAKULTET		##	Returnerar dubbelfakulteten för ett tal
+FLOOR			= RUNDA.NED			##	Avrundar ett tal nedåt mot noll
+GCD			= SGD				##	Returnerar den största gemensamma nämnaren
+INT			= HELTAL			##	Avrundar ett tal nedåt till närmaste heltal
+LCM			= MGM				##	Returnerar den minsta gemensamma multipeln
+LN			= LN				##	Returnerar den naturliga logaritmen för ett tal
+LOG			= LOG				##	Returnerar logaritmen för ett tal för en given bas
+LOG10			= LOG10				##	Returnerar 10-logaritmen för ett tal
+MDETERM			= MDETERM			##	Returnerar matrisen som är avgörandet av en matris
+MINVERSE		= MINVERT			##	Returnerar matrisinversen av en matris
+MMULT			= MMULT				##	Returnerar matrisprodukten av två matriser
+MOD			= REST				##	Returnerar resten vid en division
+MROUND			= MAVRUNDA			##	Returnerar ett tal avrundat till en given multipel
+MULTINOMIAL		= MULTINOMIAL			##	Returnerar multinomialen för en uppsättning tal
+ODD			= UDDA				##	Avrundar ett tal uppåt till närmaste udda heltal
+PI			= PI				##	Returnerar värdet pi
+POWER			= UPPHÖJT.TILL			##	Returnerar resultatet av ett tal upphöjt till en exponent
+PRODUCT			= PRODUKT			##	Multiplicerar argumenten
+QUOTIENT		= KVOT				##	Returnerar heltalsdelen av en division
+RADIANS			= RADIANER			##	Omvandlar grader till radianer
+RAND			= SLUMP				##	Returnerar ett slumptal mellan 0 och 1
+RANDBETWEEN		= SLUMP.MELLAN			##	Returnerar ett slumptal mellan de tal som du anger
+ROMAN			= ROMERSK			##	Omvandlar vanliga (arabiska) siffror till romerska som text
+ROUND			= AVRUNDA			##	Avrundar ett tal till ett angivet antal siffror
+ROUNDDOWN		= AVRUNDA.NEDÅT			##	Avrundar ett tal nedåt mot noll
+ROUNDUP			= AVRUNDA.UPPÅT			##	Avrundar ett tal uppåt, från noll
+SERIESSUM		= SERIESUMMA			##	Returnerar summan av en potensserie baserat på formeln
+SIGN			= TECKEN			##	Returnerar tecknet för ett tal
+SIN			= SIN				##	Returnerar sinus för en given vinkel
+SINH			= SINH				##	Returnerar hyperbolisk sinus för ett tal
+SQRT			= ROT				##	Returnerar den positiva kvadratroten
+SQRTPI			= ROTPI				##	Returnerar kvadratroten för (tal * pi)
+SUBTOTAL		= DELSUMMA			##	Returnerar en delsumma i en lista eller databas
+SUM			= SUMMA				##	Summerar argumenten
+SUMIF			= SUMMA.OM			##	Summerar celler enligt ett angivet villkor
+SUMIFS			= SUMMA.OMF			##	Lägger till cellerna i ett område som uppfyller flera kriterier
+SUMPRODUCT		= PRODUKTSUMMA			##	Returnerar summan av produkterna i motsvarande matriskomponenter
+SUMSQ			= KVADRATSUMMA			##	Returnerar summan av argumentens kvadrater
+SUMX2MY2		= SUMMAX2MY2			##	Returnerar summan av differensen mellan kvadraterna för motsvarande värden i två matriser
+SUMX2PY2		= SUMMAX2PY2			##	Returnerar summan av summan av kvadraterna av motsvarande värden i två matriser
+SUMXMY2			= SUMMAXMY2			##	Returnerar summan av kvadraten av skillnaden mellan motsvarande värden i två matriser
+TAN			= TAN				##	Returnerar tangens för ett tal
+TANH			= TANH				##	Returnerar hyperbolisk tangens för ett tal
+TRUNC			= AVKORTA			##	Avkortar ett tal till ett heltal
+
+
+##
+##	Statistical functions				Statistiska funktioner
+##
+AVEDEV			= MEDELAVV			##	Returnerar medelvärdet för datapunkters absoluta avvikelse från deras medelvärde
+AVERAGE			= MEDEL				##	Returnerar medelvärdet av argumenten
+AVERAGEA		= AVERAGEA			##	Returnerar medelvärdet av argumenten, inklusive tal, text och logiska värden
+AVERAGEIF		= MEDELOM			##	Returnerar medelvärdet (aritmetiskt medelvärde) för alla celler i ett område som uppfyller ett givet kriterium
+AVERAGEIFS		= MEDELOMF			##	Returnerar medelvärdet (det aritmetiska medelvärdet) för alla celler som uppfyller flera villkor.
+BETADIST		= BETAFÖRD			##	Returnerar den kumulativa betafördelningsfunktionen
+BETAINV			= BETAINV			##	Returnerar inversen till den kumulativa fördelningsfunktionen för en viss betafördelning
+BINOMDIST		= BINOMFÖRD			##	Returnerar den individuella binomialfördelningen
+CHIDIST			= CHI2FÖRD			##	Returnerar den ensidiga sannolikheten av c2-fördelningen
+CHIINV			= CHI2INV			##	Returnerar inversen av chi2-fördelningen
+CHITEST			= CHI2TEST			##	Returnerar oberoendetesten
+CONFIDENCE		= KONFIDENS			##	Returnerar konfidensintervallet för en populations medelvärde
+CORREL			= KORREL			##	Returnerar korrelationskoefficienten mellan två datamängder
+COUNT			= ANTAL				##	Räknar hur många tal som finns bland argumenten
+COUNTA			= ANTALV			##	Räknar hur många värden som finns bland argumenten
+COUNTBLANK		= ANTAL.TOMMA			##	Räknar antalet tomma celler i ett område
+COUNTIF			= ANTAL.OM			##	Räknar antalet celler i ett område som uppfyller angivna villkor.
+COUNTIFS		= ANTAL.OMF			##	Räknar antalet celler i ett område som uppfyller flera villkor.
+COVAR			= KOVAR				##	Returnerar kovariansen, d.v.s. medelvärdet av produkterna för parade avvikelser
+CRITBINOM		= KRITBINOM			##	Returnerar det minsta värdet för vilket den kumulativa binomialfördelningen är mindre än eller lika med ett villkorsvärde
+DEVSQ			= KVADAVV			##	Returnerar summan av kvadrater på avvikelser
+EXPONDIST		= EXPONFÖRD			##	Returnerar exponentialfördelningen
+FDIST			= FFÖRD				##	Returnerar F-sannolikhetsfördelningen
+FINV			= FINV				##	Returnerar inversen till F-sannolikhetsfördelningen
+FISHER			= FISHER			##	Returnerar Fisher-transformationen
+FISHERINV		= FISHERINV			##	Returnerar inversen till Fisher-transformationen
+FORECAST		= PREDIKTION			##	Returnerar ett värde längs en linjär trendlinje
+FREQUENCY		= FREKVENS			##	Returnerar en frekvensfördelning som en lodrät matris
+FTEST			= FTEST				##	Returnerar resultatet av en F-test
+GAMMADIST		= GAMMAFÖRD			##	Returnerar gammafördelningen
+GAMMAINV		= GAMMAINV			##	Returnerar inversen till den kumulativa gammafördelningen
+GAMMALN			= GAMMALN			##	Returnerar den naturliga logaritmen för gammafunktionen, G(x)
+GEOMEAN			= GEOMEDEL			##	Returnerar det geometriska medelvärdet
+GROWTH			= EXPTREND			##	Returnerar värden längs en exponentiell trend
+HARMEAN			= HARMMEDEL			##	Returnerar det harmoniska medelvärdet
+HYPGEOMDIST		= HYPGEOMFÖRD			##	Returnerar den hypergeometriska fördelningen
+INTERCEPT		= SKÄRNINGSPUNKT		##	Returnerar skärningspunkten för en linjär regressionslinje
+KURT			= TOPPIGHET			##	Returnerar toppigheten av en mängd data
+LARGE			= STÖRSTA			##	Returnerar det n:te största värdet i en mängd data
+LINEST			= REGR				##	Returnerar parametrar till en linjär trendlinje
+LOGEST			= EXPREGR			##	Returnerar parametrarna i en exponentiell trend
+LOGINV			= LOGINV			##	Returnerar inversen till den lognormala fördelningen
+LOGNORMDIST		= LOGNORMFÖRD			##	Returnerar den kumulativa lognormala fördelningen
+MAX			= MAX				##	Returnerar det största värdet i en lista av argument
+MAXA			= MAXA				##	Returnerar det största värdet i en lista av argument, inklusive tal, text och logiska värden
+MEDIAN			= MEDIAN			##	Returnerar medianen för angivna tal
+MIN			= MIN				##	Returnerar det minsta värdet i en lista med argument
+MINA			= MINA				##	Returnerar det minsta värdet i en lista över argument, inklusive tal, text och logiska värden
+MODE			= TYPVÄRDE			##	Returnerar det vanligaste värdet i en datamängd
+NEGBINOMDIST		= NEGBINOMFÖRD			##	Returnerar den negativa binomialfördelningen
+NORMDIST		= NORMFÖRD			##	Returnerar den kumulativa normalfördelningen
+NORMINV			= NORMINV			##	Returnerar inversen till den kumulativa normalfördelningen
+NORMSDIST		= NORMSFÖRD			##	Returnerar den kumulativa standardnormalfördelningen
+NORMSINV		= NORMSINV			##	Returnerar inversen till den kumulativa standardnormalfördelningen
+PEARSON			= PEARSON			##	Returnerar korrelationskoefficienten till Pearsons momentprodukt
+PERCENTILE		= PERCENTIL			##	Returnerar den n:te percentilen av värden i ett område
+PERCENTRANK		= PROCENTRANG			##	Returnerar procentrangen för ett värde i en datamängd
+PERMUT			= PERMUT			##	Returnerar antal permutationer för ett givet antal objekt
+POISSON			= POISSON			##	Returnerar Poisson-fördelningen
+PROB			= SANNOLIKHET			##	Returnerar sannolikheten att värden i ett område ligger mellan två gränser
+QUARTILE		= KVARTIL			##	Returnerar kvartilen av en mängd data
+RANK			= RANG				##	Returnerar rangordningen för ett tal i en lista med tal
+RSQ			= RKV				##	Returnerar kvadraten av Pearsons produktmomentkorrelationskoefficient
+SKEW			= SNEDHET			##	Returnerar snedheten för en fördelning
+SLOPE			= LUTNING			##	Returnerar lutningen på en linjär regressionslinje
+SMALL			= MINSTA			##	Returnerar det n:te minsta värdet i en mängd data
+STANDARDIZE		= STANDARDISERA			##	Returnerar ett normaliserat värde
+STDEV			= STDAV				##	Uppskattar standardavvikelsen baserat på ett urval
+STDEVA			= STDEVA			##	Uppskattar standardavvikelsen baserat på ett urval, inklusive tal, text och logiska värden
+STDEVP			= STDAVP			##	Beräknar standardavvikelsen baserat på hela populationen
+STDEVPA			= STDEVPA			##	Beräknar standardavvikelsen baserat på hela populationen, inklusive tal, text och logiska värden
+STEYX			= STDFELYX			##	Returnerar standardfelet för ett förutspått y-värde för varje x-värde i regressionen
+TDIST			= TFÖRD				##	Returnerar Students t-fördelning
+TINV			= TINV				##	Returnerar inversen till Students t-fördelning
+TREND			= TREND				##	Returnerar värden längs en linjär trend
+TRIMMEAN		= TRIMMEDEL			##	Returnerar medelvärdet av mittpunkterna i en datamängd
+TTEST			= TTEST				##	Returnerar sannolikheten beräknad ur Students t-test
+VAR			= VARIANS			##	Uppskattar variansen baserat på ett urval
+VARA			= VARA				##	Uppskattar variansen baserat på ett urval, inklusive tal, text och logiska värden
+VARP			= VARIANSP			##	Beräknar variansen baserat på hela populationen
+VARPA			= VARPA				##	Beräknar variansen baserat på hela populationen, inklusive tal, text och logiska värden
+WEIBULL			= WEIBULL			##	Returnerar Weibull-fördelningen
+ZTEST			= ZTEST				##	Returnerar det ensidiga sannolikhetsvärdet av ett z-test
+
+
+##
+##	Text functions					Textfunktioner
+##
+ASC			= ASC				##	Ändrar helbredds (dubbel byte) engelska bokstäver eller katakana inom en teckensträng till tecken med halvt breddsteg (enkel byte)
+BAHTTEXT		= BAHTTEXT			##	Omvandlar ett tal till text med valutaformatet ß (baht)
+CHAR			= TECKENKOD			##	Returnerar tecknet som anges av kod
+CLEAN			= STÄDA				##	Tar bort alla icke utskrivbara tecken i en text
+CODE			= KOD				##	Returnerar en numerisk kod för det första tecknet i en textsträng
+CONCATENATE		= SAMMANFOGA			##	Sammanfogar flera textdelar till en textsträng
+DOLLAR			= VALUTA			##	Omvandlar ett tal till text med valutaformat
+EXACT			= EXAKT				##	Kontrollerar om två textvärden är identiska
+FIND			= HITTA				##	Hittar en text i en annan (skiljer på gemener och versaler)
+FINDB			= HITTAB			##	Hittar en text i en annan (skiljer på gemener och versaler)
+FIXED			= FASTTAL			##	Formaterar ett tal som text med ett fast antal decimaler
+JIS			= JIS				##	Ändrar halvbredds (enkel byte) engelska bokstäver eller katakana inom en teckensträng till tecken med helt breddsteg (dubbel byte)
+LEFT			= VÄNSTER			##	Returnerar tecken längst till vänster i en sträng
+LEFTB			= VÄNSTERB			##	Returnerar tecken längst till vänster i en sträng
+LEN			= LÄNGD				##	Returnerar antalet tecken i en textsträng
+LENB			= LÄNGDB			##	Returnerar antalet tecken i en textsträng
+LOWER			= GEMENER			##	Omvandlar text till gemener
+MID			= EXTEXT			##	Returnerar angivet antal tecken från en text med början vid den position som du anger
+MIDB			= EXTEXTB			##	Returnerar angivet antal tecken från en text med början vid den position som du anger
+PHONETIC		= PHONETIC			##	Returnerar de fonetiska (furigana) tecknen i en textsträng
+PROPER			= INITIAL			##	Ändrar första bokstaven i varje ord i ett textvärde till versal
+REPLACE			= ERSÄTT			##	Ersätter tecken i text
+REPLACEB		= ERSÄTTB			##	Ersätter tecken i text
+REPT			= REP				##	Upprepar en text ett bestämt antal gånger
+RIGHT			= HÖGER				##	Returnerar tecken längst till höger i en sträng
+RIGHTB			= HÖGERB			##	Returnerar tecken längst till höger i en sträng
+SEARCH			= SÖK				##	Hittar ett textvärde i ett annat (skiljer inte på gemener och versaler)
+SEARCHB			= SÖKB				##	Hittar ett textvärde i ett annat (skiljer inte på gemener och versaler)
+SUBSTITUTE		= BYT.UT			##	Ersätter gammal text med ny text i en textsträng
+T			= T				##	Omvandlar argumenten till text
+TEXT			= TEXT				##	Formaterar ett tal och omvandlar det till text
+TRIM			= RENSA				##	Tar bort blanksteg från text
+UPPER			= VERSALER			##	Omvandlar text till versaler
+VALUE			= TEXTNUM			##	Omvandlar ett textargument till ett tal
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/tr/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/tr/config
new file mode 100755
index 0000000..266e000
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/tr/config
@@ -0,0 +1,24 @@
+##
+## PhpSpreadsheet
+##
+
+ArgumentSeparator	= ;
+
+
+##
+##	(For future use)
+##
+currencySymbol	= YTL
+
+
+##
+##	Excel Error Codes	(For future use)
+
+##
+NULL	= #BOŞ!
+DIV0	= #SAYI/0!
+VALUE	= #DEĞER!
+REF	= #BAŞV!
+NAME	= #AD?
+NUM	= #SAYI!
+NA	= #YOK
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/tr/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/tr/functions
new file mode 100755
index 0000000..f03563a
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/tr/functions
@@ -0,0 +1,416 @@
+##
+## PhpSpreadsheet
+##
+## Data in this file derived from https://www.excel-function-translation.com/
+##
+##
+
+
+##
+##	Add-in and Automation functions			Eklenti ve Otomasyon fonksiyonları
+##
+GETPIVOTDATA		= ÖZETVERİAL	##	Bir Özet Tablo raporunda saklanan verileri verir.
+
+
+##
+##	Cube functions					Küp işlevleri
+##
+CUBEKPIMEMBER		= KÜPKPIÜYE			##	Kilit performans göstergesi (KPI-Key Performance Indicator) adını, özelliğini ve ölçüsünü verir ve hücredeki ad ve özelliği gösterir. KPI, bir kurumun performansını izlemek için kullanılan aylık brüt kâr ya da üç aylık çalışan giriş çıkışları gibi ölçülebilen bir birimdir.
+CUBEMEMBER			= KÜPÜYE			##	Bir küp hiyerarşisinde bir üyeyi veya kaydı verir. Üye veya kaydın küpte varolduğunu doğrulamak için kullanılır.
+CUBEMEMBERPROPERTY	= KÜPÜYEÖZELLİĞİ	##	Bir küpte bir üyenin özelliğinin değerini verir. Küp içinde üye adının varlığını doğrulamak ve bu üyenin belli özelliklerini getirmek için kullanılır.
+CUBERANKEDMEMBER	= KÜPÜYESIRASI 		##	Bir küme içindeki üyenin derecesini veya kaçıncı olduğunu verir. En iyi satış elemanı, veya en iyi on öğrenci gibi bir kümedeki bir veya daha fazla öğeyi getirmek için kullanılır.
+CUBESET				= KÜPKÜME			##	Kümeyi oluşturan ve ardından bu kümeyi Microsoft Office Excel'e getiren sunucudaki küpe küme ifadelerini göndererek hesaplanan üye veya kayıt kümesini tanımlar.
+CUBESETCOUNT		= KÜPKÜMESAY		##	Bir kümedeki öğelerin sayısını getirir.
+CUBEVALUE			= KÜPDEĞER 			##	Bir küpten toplam değeri getirir.
+
+
+##
+##	Database functions				Veritabanı işlevleri
+##
+DAVERAGE		= VSEÇORT			##	Seçili veritabanı girdilerinin ortalamasını verir.
+DCOUNT			= VSEÇSAY			##	Veritabanında sayı içeren hücre sayısını hesaplar.
+DCOUNTA			= VSEÇSAYDOLU		##	Veritabanındaki boş olmayan hücreleri sayar.
+DGET			= VAL				##	Veritabanından, belirtilen ölçütlerle eşleşen tek bir rapor çıkarır.
+DMAX			= VSEÇMAK			##	Seçili veritabanı girişlerinin en yüksek değerini verir.
+DMIN			= VSEÇMİN			##	Seçili veritabanı girişlerinin en düşük değerini verir.
+DPRODUCT		= VSEÇÇARP			##	Kayıtların belli bir alanında bulunan, bir veritabanındaki ölçütlerle eşleşen değerleri çarpar.
+DSTDEV			= VSEÇSTDSAPMA		##	Seçili veritabanı girişlerinden oluşan bir örneğe dayanarak, standart sapmayı tahmin eder.
+DSTDEVP			= VSEÇSTDSAPMAS		##	Standart sapmayı, seçili veritabanı girişlerinin tüm popülasyonunu esas alarak hesaplar.
+DSUM			= VSEÇTOPLA			##	Kayıtların alan sütununda bulunan, ölçütle eşleşen sayıları toplar.
+DVAR			= VSEÇVAR			##	Seçili veritabanı girişlerinden oluşan bir örneği esas alarak farkı tahmin eder.
+DVARP			= VSEÇVARS			##	Seçili veritabanı girişlerinin tüm popülasyonunu esas alarak farkı hesaplar.
+
+
+##
+##	Date and time functions				Tarih ve saat işlevleri
+##
+DATE			= TARİH				##	Belirli bir tarihin seri numarasını verir.
+DATEVALUE		= TARİHSAYISI		##	Metin biçimindeki bir tarihi seri numarasına dönüştürür.
+DAY				= GÜN				##	Seri numarasını, ayın bir gününe dönüştürür.
+DAYS360			= GÜN360			##	İki tarih arasındaki gün sayısını, 360 günlük yılı esas alarak hesaplar.
+EDATE			= SERİTARİH			##	Başlangıç tarihinden itibaren, belirtilen ay sayısından önce veya sonraki tarihin seri numarasını verir.
+EOMONTH			= SERİAY			##	Belirtilen sayıda ay önce veya sonraki ayın son gününün seri numarasını verir.
+HOUR			= SAAT				##	Bir seri numarasını saate dönüştürür.
+MINUTE			= DAKİKA			##	Bir seri numarasını dakikaya dönüştürür.
+MONTH			= AY				##	Bir seri numarasını aya dönüştürür.
+NETWORKDAYS		= TAMİŞGÜNÜ			##	İki tarih arasındaki tam çalışma günlerinin sayısını verir.
+NOW				= ŞİMDİ				##	Geçerli tarihin ve saatin seri numarasını verir.
+SECOND			= SANİYE			##	Bir seri numarasını saniyeye dönüştürür.
+TIME			= ZAMAN				##	Belirli bir zamanın seri numarasını verir.
+TIMEVALUE		= ZAMANSAYISI		##	Metin biçimindeki zamanı seri numarasına dönüştürür.
+TODAY			= BUGÜN				##	Bugünün tarihini seri numarasına dönüştürür.
+WEEKDAY			= HAFTANINGÜNÜ		##	Bir seri numarasını, haftanın gününe dönüştürür.
+WEEKNUM			= HAFTASAY			##	Dizisel değerini, haftanın yıl içinde bulunduğu konumu sayısal olarak gösteren sayıya dönüştürür.
+WORKDAY			= İŞGÜNÜ			##	Belirtilen sayıda çalışma günü öncesinin ya da sonrasının tarihinin seri numarasını verir.
+YEAR			= YIL				##	Bir seri numarasını yıla dönüştürür.
+YEARFRAC		= YILORAN			##	Başlangıç_tarihi ve bitiş_tarihi arasındaki tam günleri gösteren yıl kesrini verir.
+
+
+##
+##	Engineering functions				Mühendislik işlevleri
+##
+BESSELI			= BESSELI			##	Değiştirilmiş Bessel fonksiyonu In(x)'i verir.
+BESSELJ			= BESSELJ			##	Bessel fonksiyonu Jn(x)'i verir.
+BESSELK			= BESSELK			##	Değiştirilmiş Bessel fonksiyonu Kn(x)'i verir.
+BESSELY			= BESSELY			##	Bessel fonksiyonu Yn(x)'i verir.
+BIN2DEC			= BIN2DEC			##	İkili bir sayıyı, ondalık sayıya dönüştürür.
+BIN2HEX			= BIN2HEX			##	İkili bir sayıyı, onaltılıya dönüştürür.
+BIN2OCT			= BIN2OCT			##	İkili bir sayıyı, sekizliye dönüştürür.
+COMPLEX			= KARMAŞIK			##	Gerçek ve sanal katsayıları, karmaşık sayıya dönüştürür.
+CONVERT			= ÇEVİR				##	Bir sayıyı, bir ölçüm sisteminden bir başka ölçüm sistemine dönüştürür.
+DEC2BIN			= DEC2BIN			##	Ondalık bir sayıyı, ikiliye dönüştürür.
+DEC2HEX			= DEC2HEX			##	Ondalık bir sayıyı, onaltılıya dönüştürür.
+DEC2OCT			= DEC2OCT			##	Ondalık bir sayıyı sekizliğe dönüştürür.
+DELTA			= DELTA				##	İki değerin eşit olup olmadığını sınar.
+ERF				= HATAİŞLEV			##	Hata işlevini verir.
+ERFC			= TÜMHATAİŞLEV		##	Tümleyici hata işlevini verir.
+GESTEP			= BESINIR			##	Bir sayının eşik değerinden büyük olup olmadığını sınar.
+HEX2BIN			= HEX2BIN			##	Onaltılı bir sayıyı ikiliye dönüştürür.
+HEX2DEC			= HEX2DEC			##	Onaltılı bir sayıyı ondalığa dönüştürür.
+HEX2OCT			= HEX2OCT			##	Onaltılı bir sayıyı sekizliğe dönüştürür.
+IMABS			= SANMUTLAK			##	Karmaşık bir sayının mutlak değerini (modül) verir.
+IMAGINARY		= SANAL				##	Karmaşık bir sayının sanal katsayısını verir.
+IMARGUMENT		= SANBAĞ_DEĞİŞKEN	##	Radyanlarla belirtilen bir açı olan teta bağımsız değişkenini verir.
+IMCONJUGATE		= SANEŞLENEK		##	Karmaşık bir sayının karmaşık eşleniğini verir.
+IMCOS			= SANCOS			##	Karmaşık bir sayının kosinüsünü verir.
+IMDIV			= SANBÖL			##	İki karmaşık sayının bölümünü verir.
+IMEXP			= SANÜS				##	Karmaşık bir sayının üssünü verir.
+IMLN			= SANLN				##	Karmaşık bir sayının doğal logaritmasını verir.
+IMLOG10			= SANLOG10			##	Karmaşık bir sayının, 10 tabanında logaritmasını verir.
+IMLOG2			= SANLOG2			##	Karmaşık bir sayının 2 tabanında logaritmasını verir.
+IMPOWER			= SANÜSSÜ			##	Karmaşık bir sayıyı, bir tamsayı üssüne yükseltilmiş olarak verir.
+IMPRODUCT		= SANÇARP			##	Karmaşık sayıların çarpımını verir.
+IMREAL			= SANGERÇEK			##	Karmaşık bir sayının, gerçek katsayısını verir.
+IMSIN			= SANSIN			##	Karmaşık bir sayının sinüsünü verir.
+IMSQRT			= SANKAREKÖK		##	Karmaşık bir sayının karekökünü verir.
+IMSUB			= SANÇIKAR			##	İki karmaşık sayının farkını verir.
+IMSUM			= SANTOPLA			##	Karmaşık sayıların toplamını verir.
+OCT2BIN			= OCT2BIN			##	Sekizli bir sayıyı ikiliye dönüştürür.
+OCT2DEC			= OCT2DEC			##	Sekizli bir sayıyı ondalığa dönüştürür.
+OCT2HEX			= OCT2HEX			##	Sekizli bir sayıyı onaltılıya dönüştürür.
+
+
+##
+##	Financial functions				Finansal fonksiyonlar
+##
+ACCRINT			= GERÇEKFAİZ		##	Dönemsel faiz ödeyen hisse senedine ilişkin tahakkuk eden faizi getirir.
+ACCRINTM		= GERÇEKFAİZV		##	Vadesinde ödeme yapan bir tahvilin tahakkuk etmiş faizini verir.
+AMORDEGRC		= AMORDEGRC			##	Yıpranma katsayısı kullanarak her hesap döneminin değer kaybını verir.
+AMORLINC		= AMORLINC			##	Her hesap dönemi içindeki yıpranmayı verir.
+COUPDAYBS		= KUPONGÜNBD		##	Kupon süresinin başlangıcından alış tarihine kadar olan süredeki gün sayısını verir.
+COUPDAYS		= KUPONGÜN			##	Kupon süresindeki, gün sayısını, alış tarihini de içermek üzere, verir.
+COUPDAYSNC		= KUPONGÜNDSK		##	Alış tarihinden bir sonraki kupon tarihine kadar olan gün sayısını verir.
+COUPNCD			= KUPONGÜNSKT		##	Alış tarihinden bir sonraki kupon tarihini verir.
+COUPNUM			= KUPONSAYI			##	Alış tarihiyle vade tarihi arasında ödenecek kuponların sayısını verir.
+COUPPCD			= KUPONGÜNÖKT		##	Alış tarihinden bir önceki kupon tarihini verir.
+CUMIPMT			= AİÇVERİMORANI		##	İki dönem arasında ödenen kümülatif faizi verir.
+CUMPRINC		= ANA_PARA_ÖDEMESİ	##	İki dönem arasında bir borç üzerine ödenen birikimli temeli verir.
+DB				= AZALANBAKİYE		##	Bir malın belirtilen bir süre içindeki yıpranmasını, sabit azalan bakiye yöntemini kullanarak verir.
+DDB				= ÇİFTAZALANBAKİYE	##	Bir malın belirtilen bir süre içindeki yıpranmasını, çift azalan bakiye yöntemi ya da sizin belirttiğiniz başka bir yöntemi kullanarak verir.
+DISC			= İNDİRİM			##	Bir tahvilin indirim oranını verir.
+DOLLARDE		= LİRAON			##	Kesir olarak tanımlanmış lira fiyatını, ondalık sayı olarak tanımlanmış lira fiyatına dönüştürür.
+DOLLARFR		= LİRAKES			##	Ondalık sayı olarak tanımlanmış lira fiyatını, kesir olarak tanımlanmış lira fiyatına dönüştürür.
+DURATION		= SÜRE				##	Belli aralıklarla faiz ödemesi yapan bir tahvilin yıllık süresini verir.
+EFFECT			= ETKİN				##	Efektif yıllık faiz oranını verir.
+FV				= ANBD				##	Bir yatırımın gelecekteki değerini verir.
+FVSCHEDULE		= GDPROGRAM			##	Bir seri birleşik faiz oranı uyguladıktan sonra, bir başlangıçtaki anaparanın gelecekteki değerini verir.
+INTRATE			= FAİZORANI			##	Tam olarak yatırım yapılmış bir tahvilin faiz oranını verir.
+IPMT			= FAİZTUTARI		##	Bir yatırımın verilen bir süre için faiz ödemesini verir.
+IRR				= İÇ_VERİM_ORANI	##	Bir para akışı serisi için, iç verim oranını verir.
+ISPMT			= ISPMT				##	Yatırımın belirli bir dönemi boyunca ödenen faizi hesaplar.
+MDURATION		= MSÜRE				##	Varsayılan par değeri 10.000.000 lira olan bir tahvil için Macauley değiştirilmiş süreyi verir.
+MIRR			= D_İÇ_VERİM_ORANI	##	Pozitif ve negatif para akışlarının farklı oranlarda finanse edildiği durumlarda, iç verim oranını verir.
+NOMINAL			= NOMİNAL			##	Yıllık nominal faiz oranını verir.
+NPER			= DÖNEM_SAYISI		##	Bir yatırımın dönem sayısını verir.
+NPV				= NBD				##	Bir yatırımın bugünkü net değerini, bir dönemsel para akışları serisine ve bir indirim oranına bağlı olarak verir.
+ODDFPRICE		= TEKYDEĞER			##	Tek bir ilk dönemi olan bir tahvilin değerini, her 100.000.000 lirada bir verir.
+ODDFYIELD		= TEKYÖDEME			##	Tek bir ilk dönemi olan bir tahvilin ödemesini verir.
+ODDLPRICE		= TEKSDEĞER			##	Tek bir son dönemi olan bir tahvilin fiyatını her 10.000.000 lirada bir verir.
+ODDLYIELD		= TEKSÖDEME			##	Tek bir son dönemi olan bir tahvilin ödemesini verir.
+PMT				= DEVRESEL_ÖDEME	##	Bir yıllık dönemsel ödemeyi verir.
+PPMT			= ANA_PARA_ÖDEMESİ	##	Verilen bir süre için, bir yatırımın anaparasına dayanan ödemeyi verir.
+PRICE			= DEĞER				##	Dönemsel faiz ödeyen bir tahvilin fiyatını 10.000.00 liralık değer başına verir.
+PRICEDISC		= DEĞERİND			##	İndirimli bir tahvilin fiyatını 10.000.000 liralık nominal değer başına verir.
+PRICEMAT		= DEĞERVADE			##	Faizini vade sonunda ödeyen bir tahvilin fiyatını 10.000.000 nominal değer başına verir.
+PV				= BD				##	Bir yatırımın bugünkü değerini verir.
+RATE			= FAİZ_ORANI		##	Bir yıllık dönem başına düşen faiz oranını verir.
+RECEIVED		= GETİRİ			##	Tam olarak yatırılmış bir tahvilin vadesinin bitiminde alınan miktarı verir.
+SLN				= DA				##	Bir malın bir dönem içindeki doğrusal yıpranmasını verir.
+SYD				= YAT				##	Bir malın belirli bir dönem için olan amortismanını verir.
+TBILLEQ			= HTAHEŞ			##	Bir Hazine bonosunun bono eşdeğeri ödemesini verir.
+TBILLPRICE		= HTAHDEĞER			##	Bir Hazine bonosunun değerini, 10.000.000 liralık nominal değer başına verir.
+TBILLYIELD		= HTAHÖDEME			##	Bir Hazine bonosunun ödemesini verir.
+VDB				= DAB				##	Bir malın amortismanını, belirlenmiş ya da kısmi bir dönem için, bir azalan bakiye yöntemi kullanarak verir.
+XIRR			= AİÇVERİMORANI		##	Dönemsel olması gerekmeyen bir para akışları programı için, iç verim oranını verir.
+XNPV			= ANBD				##	Dönemsel olması gerekmeyen bir para akışları programı için, bugünkü net değeri verir.
+YIELD			= ÖDEME				##	Belirli aralıklarla faiz ödeyen bir tahvilin ödemesini verir.
+YIELDDISC		= ÖDEMEİND			##	İndirimli bir tahvilin yıllık ödemesini verir; örneğin, bir Hazine bonosunun.
+YIELDMAT		= ÖDEMEVADE			##	Vadesinin bitiminde faiz ödeyen bir tahvilin yıllık ödemesini verir.
+
+
+##
+##	Information functions				Bilgi fonksiyonları
+##
+CELL			= HÜCRE			##	Bir hücrenin biçimlendirmesi, konumu ya da içeriği hakkında bilgi verir.
+ERROR.TYPE		= HATA.TİPİ		##	Bir hata türüne ilişkin sayıları verir.
+INFO			= BİLGİ			##	Geçerli işletim ortamı hakkında bilgi verir.
+ISBLANK			= EBOŞSA		##	Değer boşsa, DOĞRU verir.
+ISERR			= EHATA			##	Değer, #YOK dışındaki bir hata değeriyse, DOĞRU verir.
+ISERROR			= EHATALIYSA	##	Değer, herhangi bir hata değeriyse, DOĞRU verir.
+ISEVEN			= ÇİFTTİR		##	Sayı çiftse, DOĞRU verir.
+ISLOGICAL		= EMANTIKSALSA	##	Değer, mantıksal bir değerse, DOĞRU verir.
+ISNA			= EYOKSA		##	Değer, #YOK hata değeriyse, DOĞRU verir.
+ISNONTEXT		= EMETİNDEĞİLSE	##	Değer, metin değilse, DOĞRU verir.
+ISNUMBER		= ESAYIYSA		##	Değer, bir sayıysa, DOĞRU verir.
+ISODD			= TEKTİR		##	Sayı tekse, DOĞRU verir.
+ISREF			= EREFSE		##	Değer bir başvuruysa, DOĞRU verir.
+ISTEXT			= EMETİNSE		##	Değer bir metinse DOĞRU verir.
+N				= N				##	Sayıya dönüştürülmüş bir değer verir.
+NA				= YOKSAY		##	#YOK hata değerini verir.
+TYPE			= TİP			##	Bir değerin veri türünü belirten bir sayı verir.
+
+
+##
+##	Logical functions				Mantıksal fonksiyonlar
+##
+AND				= VE			##	Bütün bağımsız değişkenleri DOĞRU ise, DOĞRU verir.
+FALSE			= YANLIŞ		##	YANLIŞ mantıksal değerini verir.
+IF				= EĞER			##	Gerçekleştirilecek bir mantıksal sınama belirtir.
+IFERROR			= EĞERHATA		##	Formül hatalıysa belirttiğiniz değeri verir; bunun dışındaki durumlarda formülün sonucunu verir.
+NOT				= DEĞİL			##	Bağımsız değişkeninin mantığını tersine çevirir.
+OR				= YADA			##	Bağımsız değişkenlerden herhangi birisi DOĞRU ise, DOĞRU verir.
+TRUE			= DOĞRU			##	DOĞRU mantıksal değerini verir.
+
+
+##
+##	Lookup and reference functions			Arama ve Başvuru fonksiyonları
+##
+ADDRESS			= ADRES				##	Bir başvuruyu, çalışma sayfasındaki tek bir hücreye metin olarak verir.
+AREAS			= ALANSAY			##	Renvoie le nombre de zones dans une référence.
+CHOOSE			= ELEMAN			##	Değerler listesinden bir değer seçer.
+COLUMN			= SÜTUN				##	Bir başvurunun sütun sayısını verir.
+COLUMNS			= SÜTUNSAY			##	Bir başvurudaki sütunların sayısını verir.
+HLOOKUP			= YATAYARA			##	Bir dizinin en üst satırına bakar ve belirtilen hücrenin değerini verir.
+HYPERLINK		= KÖPRÜ				##	Bir ağ sunucusunda, bir intranette ya da Internet'te depolanan bir belgeyi açan bir kısayol ya da atlama oluşturur.
+INDEX			= İNDİS				##	Başvurudan veya diziden bir değer seçmek için, bir dizin kullanır.
+INDIRECT		= DOLAYLI			##	Metin değeriyle belirtilen bir başvuru verir.
+LOOKUP			= ARA				##	Bir vektördeki veya dizideki değerleri arar.
+MATCH			= KAÇINCI			##	Bir başvurudaki veya dizideki değerleri arar.
+OFFSET			= KAYDIR			##	Verilen bir başvurudan, bir başvuru kaydırmayı verir.
+ROW				= SATIR				##	Bir başvurunun satır sayısını verir.
+ROWS			= SATIRSAY			##	Bir başvurudaki satırların sayısını verir.
+RTD				= RTD				##	COM otomasyonunu destekleyen programdan gerçek zaman verileri alır.
+TRANSPOSE		= DEVRİK_DÖNÜŞÜM	##	Bir dizinin devrik dönüşümünü verir.
+VLOOKUP			= DÜŞEYARA			##	Bir dizinin ilk sütununa bakar ve bir hücrenin değerini vermek için satır boyunca hareket eder.
+
+
+##
+##	Math and trigonometry functions			Matematik ve trigonometri fonksiyonları
+##
+ABS				= MUTLAK			##	Bir sayının mutlak değerini verir.
+ACOS			= ACOS				##	Bir sayının ark kosinüsünü verir.
+ACOSH			= ACOSH				##	Bir sayının ters hiperbolik kosinüsünü verir.
+ASIN			= ASİN				##	Bir sayının ark sinüsünü verir.
+ASINH			= ASİNH				##	Bir sayının ters hiperbolik sinüsünü verir.
+ATAN			= ATAN				##	Bir sayının ark tanjantını verir.
+ATAN2			= ATAN2				##	Ark tanjantı, x- ve y- koordinatlarından verir.
+ATANH			= ATANH				##	Bir sayının ters hiperbolik tanjantını verir.
+CEILING			= TAVANAYUVARLA		##	Bir sayıyı, en yakın tamsayıya ya da en yakın katına yuvarlar.
+COMBIN			= KOMBİNASYON		##	Verilen sayıda öğenin kombinasyon sayısını verir.
+COS				= COS				##	Bir sayının kosinüsünü verir.
+COSH			= COSH				##	Bir sayının hiperbolik kosinüsünü verir.
+DEGREES			= DERECE			##	Radyanları dereceye dönüştürür.
+EVEN			= ÇİFT				##	Bir sayıyı, en yakın daha büyük çift tamsayıya yuvarlar.
+EXP				= ÜS				##	e'yi, verilen bir sayının üssüne yükseltilmiş olarak verir.
+FACT			= ÇARPINIM			##	Bir sayının faktörünü verir.
+FACTDOUBLE		= ÇİFTFAKTÖR		##	Bir sayının çift çarpınımını verir.
+FLOOR			= TABANAYUVARLA		##	Bir sayıyı, daha küçük sayıya, sıfıra yakınsayarak yuvarlar.
+GCD				= OBEB				##	En büyük ortak böleni verir.
+INT				= TAMSAYI			##	Bir sayıyı aşağıya doğru en yakın tamsayıya yuvarlar.
+LCM				= OKEK				##	En küçük ortak katı verir.
+LN				= LN				##	Bir sayının doğal logaritmasını verir.
+LOG				= LOG				##	Bir sayının, belirtilen bir tabandaki logaritmasını verir.
+LOG10			= LOG10				##	Bir sayının 10 tabanında logaritmasını verir.
+MDETERM			= DETERMİNANT		##	Bir dizinin dizey determinantını verir.
+MINVERSE		= DİZEY_TERS		##	Bir dizinin dizey tersini verir.
+MMULT			= DÇARP				##	İki dizinin dizey çarpımını verir.
+MOD				= MODÜLO			##	Bölmeden kalanı verir.
+MROUND			= KYUVARLA			##	İstenen kata yuvarlanmış bir sayı verir.
+MULTINOMIAL		= ÇOKTERİMLİ		##	Bir sayılar kümesinin çok terimlisini verir.
+ODD				= TEK				##	Bir sayıyı en yakın daha büyük tek sayıya yuvarlar.
+PI				= Pİ				##	Pi değerini verir.
+POWER			= KUVVET			##	Bir üsse yükseltilmiş sayının sonucunu verir.
+PRODUCT			= ÇARPIM			##	Bağımsız değişkenlerini çarpar.
+QUOTIENT		= BÖLÜM				##	Bir bölme işleminin tamsayı kısmını verir.
+RADIANS			= RADYAN			##	Dereceleri radyanlara dönüştürür.
+RAND			= S_SAYI_ÜRET		##	0 ile 1 arasında rastgele bir sayı verir.
+RANDBETWEEN		= RASTGELEARALIK	##	Belirttiğiniz sayılar arasında rastgele bir sayı verir.
+ROMAN			= ROMEN				##	Bir normal rakamı, metin olarak, romen rakamına çevirir.
+ROUND			= YUVARLA			##	Bir sayıyı, belirtilen basamak sayısına yuvarlar.
+ROUNDDOWN		= AŞAĞIYUVARLA		##	Bir sayıyı, daha küçük sayıya, sıfıra yakınsayarak yuvarlar.
+ROUNDUP			= YUKARIYUVARLA		##	Bir sayıyı daha büyük sayıya, sıfırdan ıraksayarak yuvarlar.
+SERIESSUM		= SERİTOPLA			##	Bir üs serisinin toplamını, formüle bağlı olarak verir.
+SIGN			= İŞARET			##	Bir sayının işaretini verir.
+SIN				= SİN				##	Verilen bir açının sinüsünü verir.
+SINH			= SİNH				##	Bir sayının hiperbolik sinüsünü verir.
+SQRT			= KAREKÖK			##	Pozitif bir karekök verir.
+SQRTPI			= KAREKÖKPİ			##	(* Pi sayısının) kare kökünü verir.
+SUBTOTAL		= ALTTOPLAM			##	Bir listedeki ya da veritabanındaki bir alt toplamı verir.
+SUM				= TOPLA				##	Bağımsız değişkenlerini toplar.
+SUMIF			= ETOPLA			##	Verilen ölçütle belirlenen hücreleri toplar.
+SUMIFS			= SUMIFS			##	Bir aralıktaki, birden fazla ölçüte uyan hücreleri ekler.
+SUMPRODUCT		= TOPLA.ÇARPIM		##	İlişkili dizi bileşenlerinin çarpımlarının toplamını verir.
+SUMSQ			= TOPKARE			##	Bağımsız değişkenlerin karelerinin toplamını verir.
+SUMX2MY2		= TOPX2EY2			##	İki dizideki ilişkili değerlerin farkının toplamını verir.
+SUMX2PY2		= TOPX2AY2			##	İki dizideki ilişkili değerlerin karelerinin toplamının toplamını verir.
+SUMXMY2			= TOPXEY2			##	İki dizideki ilişkili değerlerin farklarının karelerinin toplamını verir.
+TAN				= TAN				##	Bir sayının tanjantını verir.
+TANH			= TANH				##	Bir sayının hiperbolik tanjantını verir.
+TRUNC			= NSAT				##	Bir sayının, tamsayı durumuna gelecek şekilde, fazlalıklarını atar.
+
+
+##
+##	Statistical functions				İstatistiksel fonksiyonlar
+##
+AVEDEV			= ORTSAP			##	Veri noktalarının ortalamalarından mutlak sapmalarının ortalamasını verir.
+AVERAGE			= ORTALAMA			##	Bağımsız değişkenlerinin ortalamasını verir.
+AVERAGEA		= ORTALAMAA			##	Bağımsız değişkenlerinin, sayılar, metin ve mantıksal değerleri içermek üzere ortalamasını verir.
+AVERAGEIF		= EĞERORTALAMA 		##	Verili ölçütü karşılayan bir aralıktaki bütün hücrelerin ortalamasını (aritmetik ortalama) hesaplar.
+AVERAGEIFS		= EĞERLERORTALAMA 	##	Birden çok ölçüte uyan tüm hücrelerin ortalamasını (aritmetik ortalama) hesaplar.
+BETADIST		= BETADAĞ			##	Beta birikimli dağılım fonksiyonunu verir.
+BETAINV			= BETATERS			##	Belirli bir beta dağılımı için birikimli dağılım fonksiyonunun tersini verir.
+BINOMDIST		= BİNOMDAĞ			##	Tek terimli binom dağılımı olasılığını verir.
+CHIDIST			= KİKAREDAĞ			##	Kikare dağılımın tek kuyruklu olasılığını verir.
+CHIINV			= KİKARETERS		##	Kikare dağılımın kuyruklu olasılığının tersini verir.
+CHITEST			= KİKARETEST		##	Bağımsızlık sınamalarını verir.
+CONFIDENCE		= GÜVENİRLİK		##	Bir popülasyon ortalaması için güvenirlik aralığını verir.
+CORREL			= KORELASYON		##	İki veri kümesi arasındaki bağlantı katsayısını verir.
+COUNT			= BAĞ_DEĞ_SAY		##	Bağımsız değişkenler listesinde kaç tane sayı bulunduğunu sayar.
+COUNTA			= BAĞ_DEĞ_DOLU_SAY	##	Bağımsız değişkenler listesinde kaç tane değer bulunduğunu sayar.
+COUNTBLANK		= BOŞLUKSAY 		##	Aralıktaki boş hücre sayısını hesaplar.
+COUNTIF			= EĞERSAY 			##	Verilen ölçütlere uyan bir aralık içindeki hücreleri sayar.
+COUNTIFS		= ÇOKEĞERSAY 		##	Birden çok ölçüte uyan bir aralık içindeki hücreleri sayar.
+COVAR			= KOVARYANS 		##	Eşleştirilmiş sapmaların ortalaması olan kovaryansı verir.
+CRITBINOM		= KRİTİKBİNOM		##	Birikimli binom dağılımının bir ölçüt değerinden küçük veya ölçüt değerine eşit olduğu en küçük değeri verir.
+DEVSQ			= SAPKARE			##	Sapmaların karelerinin toplamını verir.
+EXPONDIST		= ÜSTELDAĞ			##	Üstel dağılımı verir.
+FDIST			= FDAĞ				##	F olasılık dağılımını verir.
+FINV			= FTERS				##	F olasılık dağılımının tersini verir.
+FISHER			= FISHER			##	Fisher dönüşümünü verir.
+FISHERINV		= FISHERTERS		##	Fisher dönüşümünün tersini verir.
+FORECAST		= TAHMİN			##	Bir doğrusal eğilim boyunca bir değer verir.
+FREQUENCY		= SIKLIK			##	Bir sıklık dağılımını, dikey bir dizi olarak verir.
+FTEST			= FTEST				##	Bir F-test'in sonucunu verir.
+GAMMADIST		= GAMADAĞ			##	Gama dağılımını verir.
+GAMMAINV		= GAMATERS			##	Gama kümülatif dağılımının tersini verir.
+GAMMALN			= GAMALN			##	Gama fonksiyonunun (?(x)) doğal logaritmasını verir.
+GEOMEAN			= GEOORT			##	Geometrik ortayı verir.
+GROWTH			= BÜYÜME			##	Üstel bir eğilim boyunca değerler verir.
+HARMEAN			= HARORT			##	Harmonik ortayı verir.
+HYPGEOMDIST		= HİPERGEOMDAĞ		##	Hipergeometrik dağılımı verir.
+INTERCEPT		= KESMENOKTASI		##	Doğrusal çakıştırma çizgisinin kesişme noktasını verir.
+KURT			= BASIKLIK			##	Bir veri kümesinin basıklığını verir.
+LARGE			= BÜYÜK				##	Bir veri kümesinde k. en büyük değeri verir.
+LINEST			= DOT				##	Doğrusal bir eğilimin parametrelerini verir.
+LOGEST			= LOT				##	Üstel bir eğilimin parametrelerini verir.
+LOGINV			= LOGTERS			##	Bir lognormal dağılımının tersini verir.
+LOGNORMDIST		= LOGNORMDAĞ		##	Birikimli lognormal dağılımını verir.
+MAX				= MAK				##	Bir bağımsız değişkenler listesindeki en büyük değeri verir.
+MAXA			= MAKA				##	Bir bağımsız değişkenler listesindeki, sayılar, metin ve mantıksal değerleri içermek üzere, en büyük değeri verir.
+MEDIAN			= ORTANCA			##	Belirtilen sayıların orta değerini verir.
+MIN				= MİN				##	Bir bağımsız değişkenler listesindeki en küçük değeri verir.
+MINA			= MİNA				##	Bir bağımsız değişkenler listesindeki, sayılar, metin ve mantıksal değerleri de içermek üzere, en küçük değeri verir.
+MODE			= ENÇOK_OLAN		##	Bir veri kümesindeki en sık rastlanan değeri verir.
+NEGBINOMDIST	= NEGBİNOMDAĞ		##	Negatif binom dağılımını verir.
+NORMDIST		= NORMDAĞ			##	Normal birikimli dağılımı verir.
+NORMINV			= NORMTERS			##	Normal kümülatif dağılımın tersini verir.
+NORMSDIST		= NORMSDAĞ			##	Standart normal birikimli dağılımı verir.
+NORMSINV		= NORMSTERS			##	Standart normal birikimli dağılımın tersini verir.
+PEARSON			= PEARSON			##	Pearson çarpım moment korelasyon katsayısını verir.
+PERCENTILE		= YÜZDEBİRLİK		##	Bir aralık içerisinde bulunan değerlerin k. frekans toplamını verir.
+PERCENTRANK		= YÜZDERANK			##	Bir veri kümesindeki bir değerin yüzde mertebesini verir.
+PERMUT			= PERMÜTASYON		##	Verilen sayıda nesne için permütasyon sayısını verir.
+POISSON			= POISSON			##	Poisson dağılımını verir.
+PROB			= OLASILIK			##	Bir aralıktaki değerlerin iki sınır arasında olması olasılığını verir.
+QUARTILE		= DÖRTTEBİRLİK		##	Bir veri kümesinin dörtte birliğini verir.
+RANK			= RANK				##	Bir sayılar listesinde bir sayının mertebesini verir.
+RSQ				= RKARE				##	Pearson çarpım moment korelasyon katsayısının karesini verir.
+SKEW			= ÇARPIKLIK			##	Bir dağılımın çarpıklığını verir.
+SLOPE			= EĞİM				##	Doğrusal çakışma çizgisinin eğimini verir.
+SMALL			= KÜÇÜK				##	Bir veri kümesinde k. en küçük değeri verir.
+STANDARDIZE		= STANDARTLAŞTIRMA	##	Normalleştirilmiş bir değer verir.
+STDEV			= STDSAPMA			##	Bir örneğe dayanarak standart sapmayı tahmin eder.
+STDEVA			= STDSAPMAA			##	Standart sapmayı, sayılar, metin ve mantıksal değerleri içermek üzere, bir örneğe bağlı olarak tahmin eder.
+STDEVP			= STDSAPMAS			##	Standart sapmayı, tüm popülasyona bağlı olarak hesaplar.
+STDEVPA			= STDSAPMASA		##	Standart sapmayı, sayılar, metin ve mantıksal değerleri içermek üzere, tüm popülasyona bağlı olarak hesaplar.
+STEYX			= STHYX				##	Regresyondaki her x için tahmini y değerinin standart hatasını verir.
+TDIST			= TDAĞ				##	T-dağılımını verir.
+TINV			= TTERS				##	T-dağılımının tersini verir.
+TREND			= EĞİLİM			##	Doğrusal bir eğilim boyunca değerler verir.
+TRIMMEAN		= KIRPORTALAMA		##	Bir veri kümesinin içinin ortalamasını verir.
+TTEST			= TTEST				##	T-test'le ilişkilendirilmiş olasılığı verir.
+VAR				= VAR				##	Varyansı, bir örneğe bağlı olarak tahmin eder.
+VARA			= VARA				##	Varyansı, sayılar, metin ve mantıksal değerleri içermek üzere, bir örneğe bağlı olarak tahmin eder.
+VARP			= VARS				##	Varyansı, tüm popülasyona dayanarak hesaplar.
+VARPA			= VARSA				##	Varyansı, sayılar, metin ve mantıksal değerleri içermek üzere, tüm popülasyona bağlı olarak hesaplar.
+WEIBULL			= WEIBULL			##	Weibull dağılımını hesaplar.
+ZTEST			= ZTEST				##	Z-testinin tek kuyruklu olasılık değerini hesaplar.
+
+
+##
+##	Text functions					Metin fonksiyonları
+##
+ASC				= ASC 				##	Bir karakter dizesindeki çift enli (iki bayt) İngilizce harfleri veya katakanayı yarım enli (tek bayt) karakterlerle değiştirir.
+BAHTTEXT		= BAHTTEXT 			##	Sayıyı, ß (baht) para birimi biçimini kullanarak metne dönüştürür.
+CHAR			= DAMGA 			##	Kod sayısıyla belirtilen karakteri verir.
+CLEAN			= TEMİZ				##	Metindeki bütün yazdırılamaz karakterleri kaldırır.
+CODE			= KOD				##	Bir metin dizesindeki ilk karakter için sayısal bir kod verir.
+CONCATENATE		= BİRLEŞTİR			##	Pek çok metin öğesini bir metin öğesi olarak birleştirir.
+DOLLAR			= LİRA				##	Bir sayıyı YTL (yeni Türk lirası) para birimi biçimini kullanarak metne dönüştürür.
+EXACT			= ÖZDEŞ				##	İki metin değerinin özdeş olup olmadığını anlamak için, değerleri denetler.
+FIND			= BUL				##	Bir metin değerini, bir başkasının içinde bulur (büyük küçük harf duyarlıdır).
+FINDB			= BULB				##	Bir metin değerini, bir başkasının içinde bulur (büyük küçük harf duyarlıdır).
+FIXED			= SAYIDÜZENLE		##	Bir sayıyı, sabit sayıda ondalıkla, metin olarak biçimlendirir.
+JIS				= JIS				##	Bir karakter dizesindeki tek enli (tek bayt) İngilizce harfleri veya katakanayı çift enli (iki bayt) karakterlerle değiştirir.
+LEFT			= SOL				##	Bir metin değerinden en soldaki karakterleri verir.
+LEFTB			= SOLB				##	Bir metin değerinden en soldaki karakterleri verir.
+LEN				= UZUNLUK			##	Bir metin dizesindeki karakter sayısını verir.
+LENB			= UZUNLUKB			##	Bir metin dizesindeki karakter sayısını verir.
+LOWER			= KÜÇÜKHARF			##	Metni küçük harfe çevirir.
+MID				= ORTA				##	Bir metin dizesinden belirli sayıda karakteri, belirttiğiniz konumdan başlamak üzere verir.
+MIDB			= ORTAB				##	Bir metin dizesinden belirli sayıda karakteri, belirttiğiniz konumdan başlamak üzere verir.
+PHONETIC		= SES				##	Metin dizesinden ses (furigana) karakterlerini ayıklar.
+PROPER			= YAZIM.DÜZENİ		##	Bir metin değerinin her bir sözcüğünün ilk harfini büyük harfe çevirir.
+REPLACE			= DEĞİŞTİR			##	Metnin içindeki karakterleri değiştirir.
+REPLACEB		= DEĞİŞTİRB			##	Metnin içindeki karakterleri değiştirir.
+REPT			= YİNELE			##	Metni belirtilen sayıda yineler.
+RIGHT			= SAĞ				##	Bir metin değerinden en sağdaki karakterleri verir.
+RIGHTB			= SAĞB				##	Bir metin değerinden en sağdaki karakterleri verir.
+SEARCH			= BUL				##	Bir metin değerini, bir başkasının içinde bulur (büyük küçük harf duyarlı değildir).
+SEARCHB			= BULB				##	Bir metin değerini, bir başkasının içinde bulur (büyük küçük harf duyarlı değildir).
+SUBSTITUTE		= YERİNEKOY			##	Bir metin dizesinde, eski metnin yerine yeni metin koyar.
+T				= M					##	Bağımsız değerlerini metne dönüştürür.
+TEXT			= METNEÇEVİR		##	Bir sayıyı biçimlendirir ve metne dönüştürür.
+TRIM			= KIRP				##	Metindeki boşlukları kaldırır.
+UPPER			= BÜYÜKHARF			##	Metni büyük harfe çevirir.
+VALUE			= SAYIYAÇEVİR		##	Bir metin bağımsız değişkenini sayıya dönüştürür.
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AddressHelper.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AddressHelper.php
new file mode 100755
index 0000000..77a521b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AddressHelper.php
@@ -0,0 +1,88 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Cell;
+
+use PhpOffice\PhpSpreadsheet\Exception;
+
+class AddressHelper
+{
+    /**
+     * Converts an R1C1 format cell address to an A1 format cell address.
+     */
+    public static function convertToA1(
+        string $address,
+        int $currentRowNumber = 1,
+        int $currentColumnNumber = 1
+    ): string {
+        $validityCheck = preg_match('/^(R(\[?-?\d*\]?))(C(\[?-?\d*\]?))$/i', $address, $cellReference);
+
+        if ($validityCheck === 0) {
+            throw new Exception('Invalid R1C1-format Cell Reference');
+        }
+
+        $rowReference = $cellReference[2];
+        //    Empty R reference is the current row
+        if ($rowReference === '') {
+            $rowReference = (string) $currentRowNumber;
+        }
+        //    Bracketed R references are relative to the current row
+        if ($rowReference[0] === '[') {
+            $rowReference = $currentRowNumber + trim($rowReference, '[]');
+        }
+        $columnReference = $cellReference[4];
+        //    Empty C reference is the current column
+        if ($columnReference === '') {
+            $columnReference = (string) $currentColumnNumber;
+        }
+        //    Bracketed C references are relative to the current column
+        if ($columnReference[0] === '[') {
+            $columnReference = $currentColumnNumber + trim($columnReference, '[]');
+        }
+
+        if ($columnReference <= 0 || $rowReference <= 0) {
+            throw new Exception('Invalid R1C1-format Cell Reference, Value out of range');
+        }
+        $A1CellReference = Coordinate::stringFromColumnIndex($columnReference) . $rowReference;
+
+        return $A1CellReference;
+    }
+
+    /**
+     * Converts an A1 format cell address to an R1C1 format cell address.
+     * If $currentRowNumber or $currentColumnNumber are provided, then the R1C1 address will be formatted as a relative address.
+     */
+    public static function convertToR1C1(
+        string $address,
+        ?int $currentRowNumber = null,
+        ?int $currentColumnNumber = null
+    ): string {
+        $validityCheck = preg_match('/^\$?([A-Z]{1,3})\$?(\d{1,7})$/i', $address, $cellReference);
+
+        if ($validityCheck === 0) {
+            throw new Exception('Invalid A1-format Cell Reference');
+        }
+
+        $columnId = Coordinate::columnIndexFromString($cellReference[1]);
+        $rowId = (int) $cellReference[2];
+
+        if ($currentRowNumber !== null) {
+            if ($rowId === $currentRowNumber) {
+                $rowId = '';
+            } else {
+                $rowId = '[' . ($rowId - $currentRowNumber) . ']';
+            }
+        }
+
+        if ($currentColumnNumber !== null) {
+            if ($columnId === $currentColumnNumber) {
+                $columnId = '';
+            } else {
+                $columnId = '[' . ($columnId - $currentColumnNumber) . ']';
+            }
+        }
+
+        $R1C1Address = "R{$rowId}C{$columnId}";
+
+        return $R1C1Address;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AdvancedValueBinder.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AdvancedValueBinder.php
new file mode 100755
index 0000000..cf638d0
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AdvancedValueBinder.php
@@ -0,0 +1,174 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Cell;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
+use PhpOffice\PhpSpreadsheet\RichText\RichText;
+use PhpOffice\PhpSpreadsheet\Shared\Date;
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
+
+class AdvancedValueBinder extends DefaultValueBinder implements IValueBinder
+{
+    /**
+     * Bind value to a cell.
+     *
+     * @param Cell $cell Cell to bind value to
+     * @param mixed $value Value to bind in cell
+     *
+     * @return bool
+     */
+    public function bindValue(Cell $cell, $value = null)
+    {
+        // sanitize UTF-8 strings
+        if (is_string($value)) {
+            $value = StringHelper::sanitizeUTF8($value);
+        }
+
+        // Find out data type
+        $dataType = parent::dataTypeForValue($value);
+
+        // Style logic - strings
+        if ($dataType === DataType::TYPE_STRING && !$value instanceof RichText) {
+            //    Test for booleans using locale-setting
+            if ($value == Calculation::getTRUE()) {
+                $cell->setValueExplicit(true, DataType::TYPE_BOOL);
+
+                return true;
+            } elseif ($value == Calculation::getFALSE()) {
+                $cell->setValueExplicit(false, DataType::TYPE_BOOL);
+
+                return true;
+            }
+
+            // Check for number in scientific format
+            if (preg_match('/^' . Calculation::CALCULATION_REGEXP_NUMBER . '$/', $value)) {
+                $cell->setValueExplicit((float) $value, DataType::TYPE_NUMERIC);
+
+                return true;
+            }
+
+            // Check for fraction
+            if (preg_match('/^([+-]?)\s*(\d+)\s?\/\s*(\d+)$/', $value, $matches)) {
+                // Convert value to number
+                $value = $matches[2] / $matches[3];
+                if ($matches[1] == '-') {
+                    $value = 0 - $value;
+                }
+                $cell->setValueExplicit((float) $value, DataType::TYPE_NUMERIC);
+                // Set style
+                $cell->getWorksheet()->getStyle($cell->getCoordinate())
+                    ->getNumberFormat()->setFormatCode('??/??');
+
+                return true;
+            } elseif (preg_match('/^([+-]?)(\d*) +(\d*)\s?\/\s*(\d*)$/', $value, $matches)) {
+                // Convert value to number
+                $value = $matches[2] + ($matches[3] / $matches[4]);
+                if ($matches[1] == '-') {
+                    $value = 0 - $value;
+                }
+                $cell->setValueExplicit((float) $value, DataType::TYPE_NUMERIC);
+                // Set style
+                $cell->getWorksheet()->getStyle($cell->getCoordinate())
+                    ->getNumberFormat()->setFormatCode('# ??/??');
+
+                return true;
+            }
+
+            // Check for percentage
+            if (preg_match('/^\-?\d*\.?\d*\s?\%$/', $value)) {
+                // Convert value to number
+                $value = (float) str_replace('%', '', $value) / 100;
+                $cell->setValueExplicit($value, DataType::TYPE_NUMERIC);
+                // Set style
+                $cell->getWorksheet()->getStyle($cell->getCoordinate())
+                    ->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_PERCENTAGE_00);
+
+                return true;
+            }
+
+            // Check for currency
+            $currencyCode = StringHelper::getCurrencyCode();
+            $decimalSeparator = StringHelper::getDecimalSeparator();
+            $thousandsSeparator = StringHelper::getThousandsSeparator();
+            if (preg_match('/^' . preg_quote($currencyCode, '/') . ' *(\d{1,3}(' . preg_quote($thousandsSeparator, '/') . '\d{3})*|(\d+))(' . preg_quote($decimalSeparator, '/') . '\d{2})?$/', $value)) {
+                // Convert value to number
+                $value = (float) trim(str_replace([$currencyCode, $thousandsSeparator, $decimalSeparator], ['', '', '.'], $value));
+                $cell->setValueExplicit($value, DataType::TYPE_NUMERIC);
+                // Set style
+                $cell->getWorksheet()->getStyle($cell->getCoordinate())
+                    ->getNumberFormat()->setFormatCode(
+                        str_replace('$', $currencyCode, NumberFormat::FORMAT_CURRENCY_USD_SIMPLE)
+                    );
+
+                return true;
+            } elseif (preg_match('/^\$ *(\d{1,3}(\,\d{3})*|(\d+))(\.\d{2})?$/', $value)) {
+                // Convert value to number
+                $value = (float) trim(str_replace(['$', ','], '', $value));
+                $cell->setValueExplicit($value, DataType::TYPE_NUMERIC);
+                // Set style
+                $cell->getWorksheet()->getStyle($cell->getCoordinate())
+                    ->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_CURRENCY_USD_SIMPLE);
+
+                return true;
+            }
+
+            // Check for time without seconds e.g. '9:45', '09:45'
+            if (preg_match('/^(\d|[0-1]\d|2[0-3]):[0-5]\d$/', $value)) {
+                // Convert value to number
+                [$h, $m] = explode(':', $value);
+                $days = $h / 24 + $m / 1440;
+                $cell->setValueExplicit($days, DataType::TYPE_NUMERIC);
+                // Set style
+                $cell->getWorksheet()->getStyle($cell->getCoordinate())
+                    ->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_DATE_TIME3);
+
+                return true;
+            }
+
+            // Check for time with seconds '9:45:59', '09:45:59'
+            if (preg_match('/^(\d|[0-1]\d|2[0-3]):[0-5]\d:[0-5]\d$/', $value)) {
+                // Convert value to number
+                [$h, $m, $s] = explode(':', $value);
+                $days = $h / 24 + $m / 1440 + $s / 86400;
+                // Convert value to number
+                $cell->setValueExplicit($days, DataType::TYPE_NUMERIC);
+                // Set style
+                $cell->getWorksheet()->getStyle($cell->getCoordinate())
+                    ->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_DATE_TIME4);
+
+                return true;
+            }
+
+            // Check for datetime, e.g. '2008-12-31', '2008-12-31 15:59', '2008-12-31 15:59:10'
+            if (($d = Date::stringToExcel($value)) !== false) {
+                // Convert value to number
+                $cell->setValueExplicit($d, DataType::TYPE_NUMERIC);
+                // Determine style. Either there is a time part or not. Look for ':'
+                if (strpos($value, ':') !== false) {
+                    $formatCode = 'yyyy-mm-dd h:mm';
+                } else {
+                    $formatCode = 'yyyy-mm-dd';
+                }
+                $cell->getWorksheet()->getStyle($cell->getCoordinate())
+                    ->getNumberFormat()->setFormatCode($formatCode);
+
+                return true;
+            }
+
+            // Check for newline character "\n"
+            if (strpos($value, "\n") !== false) {
+                $value = StringHelper::sanitizeUTF8($value);
+                $cell->setValueExplicit($value, DataType::TYPE_STRING);
+                // Set style
+                $cell->getWorksheet()->getStyle($cell->getCoordinate())
+                    ->getAlignment()->setWrapText(true);
+
+                return true;
+            }
+        }
+
+        // Not bound yet? Use parent...
+        return parent::bindValue($cell, $value);
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Cell.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Cell.php
new file mode 100755
index 0000000..0bca2fc
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Cell.php
@@ -0,0 +1,676 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Cell;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
+use PhpOffice\PhpSpreadsheet\Collection\Cells;
+use PhpOffice\PhpSpreadsheet\Exception;
+use PhpOffice\PhpSpreadsheet\RichText\RichText;
+use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
+use PhpOffice\PhpSpreadsheet\Style\Style;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+
+class Cell
+{
+    /**
+     * Value binder to use.
+     *
+     * @var IValueBinder
+     */
+    private static $valueBinder;
+
+    /**
+     * Value of the cell.
+     *
+     * @var mixed
+     */
+    private $value;
+
+    /**
+     *    Calculated value of the cell (used for caching)
+     *    This returns the value last calculated by MS Excel or whichever spreadsheet program was used to
+     *        create the original spreadsheet file.
+     *    Note that this value is not guaranteed to reflect the actual calculated value because it is
+     *        possible that auto-calculation was disabled in the original spreadsheet, and underlying data
+     *        values used by the formula have changed since it was last calculated.
+     *
+     * @var mixed
+     */
+    private $calculatedValue;
+
+    /**
+     * Type of the cell data.
+     *
+     * @var string
+     */
+    private $dataType;
+
+    /**
+     * Collection of cells.
+     *
+     * @var Cells
+     */
+    private $parent;
+
+    /**
+     * Index to cellXf.
+     *
+     * @var int
+     */
+    private $xfIndex = 0;
+
+    /**
+     * Attributes of the formula.
+     */
+    private $formulaAttributes;
+
+    /**
+     * Update the cell into the cell collection.
+     *
+     * @return $this
+     */
+    public function updateInCollection()
+    {
+        $this->parent->update($this);
+
+        return $this;
+    }
+
+    public function detach(): void
+    {
+        $this->parent = null;
+    }
+
+    public function attach(Cells $parent): void
+    {
+        $this->parent = $parent;
+    }
+
+    /**
+     * Create a new Cell.
+     *
+     * @param mixed $pValue
+     * @param string $pDataType
+     */
+    public function __construct($pValue, $pDataType, Worksheet $pSheet)
+    {
+        // Initialise cell value
+        $this->value = $pValue;
+
+        // Set worksheet cache
+        $this->parent = $pSheet->getCellCollection();
+
+        // Set datatype?
+        if ($pDataType !== null) {
+            if ($pDataType == DataType::TYPE_STRING2) {
+                $pDataType = DataType::TYPE_STRING;
+            }
+            $this->dataType = $pDataType;
+        } elseif (!self::getValueBinder()->bindValue($this, $pValue)) {
+            throw new Exception('Value could not be bound to cell.');
+        }
+    }
+
+    /**
+     * Get cell coordinate column.
+     *
+     * @return string
+     */
+    public function getColumn()
+    {
+        return $this->parent->getCurrentColumn();
+    }
+
+    /**
+     * Get cell coordinate row.
+     *
+     * @return int
+     */
+    public function getRow()
+    {
+        return $this->parent->getCurrentRow();
+    }
+
+    /**
+     * Get cell coordinate.
+     *
+     * @return string
+     */
+    public function getCoordinate()
+    {
+        return $this->parent->getCurrentCoordinate();
+    }
+
+    /**
+     * Get cell value.
+     *
+     * @return mixed
+     */
+    public function getValue()
+    {
+        return $this->value;
+    }
+
+    /**
+     * Get cell value with formatting.
+     *
+     * @return string
+     */
+    public function getFormattedValue()
+    {
+        return (string) NumberFormat::toFormattedString(
+            $this->getCalculatedValue(),
+            $this->getStyle()
+                ->getNumberFormat()->getFormatCode()
+        );
+    }
+
+    /**
+     * Set cell value.
+     *
+     *    Sets the value for a cell, automatically determining the datatype using the value binder
+     *
+     * @param mixed $pValue Value
+     *
+     * @return $this
+     */
+    public function setValue($pValue)
+    {
+        if (!self::getValueBinder()->bindValue($this, $pValue)) {
+            throw new Exception('Value could not be bound to cell.');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Set the value for a cell, with the explicit data type passed to the method (bypassing any use of the value binder).
+     *
+     * @param mixed $pValue Value
+     * @param string $pDataType Explicit data type, see DataType::TYPE_*
+     *
+     * @return Cell
+     */
+    public function setValueExplicit($pValue, $pDataType)
+    {
+        // set the value according to data type
+        switch ($pDataType) {
+            case DataType::TYPE_NULL:
+                $this->value = $pValue;
+
+                break;
+            case DataType::TYPE_STRING2:
+                $pDataType = DataType::TYPE_STRING;
+                // no break
+            case DataType::TYPE_STRING:
+                // Synonym for string
+            case DataType::TYPE_INLINE:
+                // Rich text
+                $this->value = DataType::checkString($pValue);
+
+                break;
+            case DataType::TYPE_NUMERIC:
+                if (is_string($pValue) && !is_numeric($pValue)) {
+                    throw new Exception('Invalid numeric value for datatype Numeric');
+                }
+                $this->value = 0 + $pValue;
+
+                break;
+            case DataType::TYPE_FORMULA:
+                $this->value = (string) $pValue;
+
+                break;
+            case DataType::TYPE_BOOL:
+                $this->value = (bool) $pValue;
+
+                break;
+            case DataType::TYPE_ERROR:
+                $this->value = DataType::checkErrorCode($pValue);
+
+                break;
+            default:
+                throw new Exception('Invalid datatype: ' . $pDataType);
+
+                break;
+        }
+
+        // set the datatype
+        $this->dataType = $pDataType;
+
+        return $this->updateInCollection();
+    }
+
+    /**
+     * Get calculated cell value.
+     *
+     * @param bool $resetLog Whether the calculation engine logger should be reset or not
+     *
+     * @return mixed
+     */
+    public function getCalculatedValue($resetLog = true)
+    {
+        if ($this->dataType == DataType::TYPE_FORMULA) {
+            try {
+                $result = Calculation::getInstance(
+                    $this->getWorksheet()->getParent()
+                )->calculateCellValue($this, $resetLog);
+                //    We don't yet handle array returns
+                if (is_array($result)) {
+                    while (is_array($result)) {
+                        $result = array_shift($result);
+                    }
+                }
+            } catch (Exception $ex) {
+                if (($ex->getMessage() === 'Unable to access External Workbook') && ($this->calculatedValue !== null)) {
+                    return $this->calculatedValue; // Fallback for calculations referencing external files.
+                }
+
+                throw new \PhpOffice\PhpSpreadsheet\Calculation\Exception(
+                    $this->getWorksheet()->getTitle() . '!' . $this->getCoordinate() . ' -> ' . $ex->getMessage()
+                );
+            }
+
+            if ($result === '#Not Yet Implemented') {
+                return $this->calculatedValue; // Fallback if calculation engine does not support the formula.
+            }
+
+            return $result;
+        } elseif ($this->value instanceof RichText) {
+            return $this->value->getPlainText();
+        }
+
+        return $this->value;
+    }
+
+    /**
+     * Set old calculated value (cached).
+     *
+     * @param mixed $pValue Value
+     *
+     * @return Cell
+     */
+    public function setCalculatedValue($pValue)
+    {
+        if ($pValue !== null) {
+            $this->calculatedValue = (is_numeric($pValue)) ? (float) $pValue : $pValue;
+        }
+
+        return $this->updateInCollection();
+    }
+
+    /**
+     *    Get old calculated value (cached)
+     *    This returns the value last calculated by MS Excel or whichever spreadsheet program was used to
+     *        create the original spreadsheet file.
+     *    Note that this value is not guaranteed to reflect the actual calculated value because it is
+     *        possible that auto-calculation was disabled in the original spreadsheet, and underlying data
+     *        values used by the formula have changed since it was last calculated.
+     *
+     * @return mixed
+     */
+    public function getOldCalculatedValue()
+    {
+        return $this->calculatedValue;
+    }
+
+    /**
+     * Get cell data type.
+     *
+     * @return string
+     */
+    public function getDataType()
+    {
+        return $this->dataType;
+    }
+
+    /**
+     * Set cell data type.
+     *
+     * @param string $pDataType see DataType::TYPE_*
+     *
+     * @return Cell
+     */
+    public function setDataType($pDataType)
+    {
+        if ($pDataType == DataType::TYPE_STRING2) {
+            $pDataType = DataType::TYPE_STRING;
+        }
+        $this->dataType = $pDataType;
+
+        return $this->updateInCollection();
+    }
+
+    /**
+     * Identify if the cell contains a formula.
+     *
+     * @return bool
+     */
+    public function isFormula()
+    {
+        return $this->dataType == DataType::TYPE_FORMULA;
+    }
+
+    /**
+     *    Does this cell contain Data validation rules?
+     *
+     * @return bool
+     */
+    public function hasDataValidation()
+    {
+        if (!isset($this->parent)) {
+            throw new Exception('Cannot check for data validation when cell is not bound to a worksheet');
+        }
+
+        return $this->getWorksheet()->dataValidationExists($this->getCoordinate());
+    }
+
+    /**
+     * Get Data validation rules.
+     *
+     * @return DataValidation
+     */
+    public function getDataValidation()
+    {
+        if (!isset($this->parent)) {
+            throw new Exception('Cannot get data validation for cell that is not bound to a worksheet');
+        }
+
+        return $this->getWorksheet()->getDataValidation($this->getCoordinate());
+    }
+
+    /**
+     * Set Data validation rules.
+     *
+     * @param DataValidation $pDataValidation
+     *
+     * @return Cell
+     */
+    public function setDataValidation(?DataValidation $pDataValidation = null)
+    {
+        if (!isset($this->parent)) {
+            throw new Exception('Cannot set data validation for cell that is not bound to a worksheet');
+        }
+
+        $this->getWorksheet()->setDataValidation($this->getCoordinate(), $pDataValidation);
+
+        return $this->updateInCollection();
+    }
+
+    /**
+     * Does this cell contain valid value?
+     *
+     * @return bool
+     */
+    public function hasValidValue()
+    {
+        $validator = new DataValidator();
+
+        return $validator->isValid($this);
+    }
+
+    /**
+     * Does this cell contain a Hyperlink?
+     *
+     * @return bool
+     */
+    public function hasHyperlink()
+    {
+        if (!isset($this->parent)) {
+            throw new Exception('Cannot check for hyperlink when cell is not bound to a worksheet');
+        }
+
+        return $this->getWorksheet()->hyperlinkExists($this->getCoordinate());
+    }
+
+    /**
+     * Get Hyperlink.
+     *
+     * @return Hyperlink
+     */
+    public function getHyperlink()
+    {
+        if (!isset($this->parent)) {
+            throw new Exception('Cannot get hyperlink for cell that is not bound to a worksheet');
+        }
+
+        return $this->getWorksheet()->getHyperlink($this->getCoordinate());
+    }
+
+    /**
+     * Set Hyperlink.
+     *
+     * @param Hyperlink $pHyperlink
+     *
+     * @return Cell
+     */
+    public function setHyperlink(?Hyperlink $pHyperlink = null)
+    {
+        if (!isset($this->parent)) {
+            throw new Exception('Cannot set hyperlink for cell that is not bound to a worksheet');
+        }
+
+        $this->getWorksheet()->setHyperlink($this->getCoordinate(), $pHyperlink);
+
+        return $this->updateInCollection();
+    }
+
+    /**
+     * Get cell collection.
+     *
+     * @return Cells
+     */
+    public function getParent()
+    {
+        return $this->parent;
+    }
+
+    /**
+     * Get parent worksheet.
+     *
+     * @return Worksheet
+     */
+    public function getWorksheet()
+    {
+        return $this->parent->getParent();
+    }
+
+    /**
+     * Is this cell in a merge range.
+     *
+     * @return bool
+     */
+    public function isInMergeRange()
+    {
+        return (bool) $this->getMergeRange();
+    }
+
+    /**
+     * Is this cell the master (top left cell) in a merge range (that holds the actual data value).
+     *
+     * @return bool
+     */
+    public function isMergeRangeValueCell()
+    {
+        if ($mergeRange = $this->getMergeRange()) {
+            $mergeRange = Coordinate::splitRange($mergeRange);
+            [$startCell] = $mergeRange[0];
+            if ($this->getCoordinate() === $startCell) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * If this cell is in a merge range, then return the range.
+     *
+     * @return false|string
+     */
+    public function getMergeRange()
+    {
+        foreach ($this->getWorksheet()->getMergeCells() as $mergeRange) {
+            if ($this->isInRange($mergeRange)) {
+                return $mergeRange;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Get cell style.
+     *
+     * @return Style
+     */
+    public function getStyle()
+    {
+        return $this->getWorksheet()->getStyle($this->getCoordinate());
+    }
+
+    /**
+     * Re-bind parent.
+     *
+     * @return Cell
+     */
+    public function rebindParent(Worksheet $parent)
+    {
+        $this->parent = $parent->getCellCollection();
+
+        return $this->updateInCollection();
+    }
+
+    /**
+     *    Is cell in a specific range?
+     *
+     * @param string $pRange Cell range (e.g. A1:A1)
+     *
+     * @return bool
+     */
+    public function isInRange($pRange)
+    {
+        [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($pRange);
+
+        // Translate properties
+        $myColumn = Coordinate::columnIndexFromString($this->getColumn());
+        $myRow = $this->getRow();
+
+        // Verify if cell is in range
+        return ($rangeStart[0] <= $myColumn) && ($rangeEnd[0] >= $myColumn) &&
+                ($rangeStart[1] <= $myRow) && ($rangeEnd[1] >= $myRow);
+    }
+
+    /**
+     * Compare 2 cells.
+     *
+     * @param Cell $a Cell a
+     * @param Cell $b Cell b
+     *
+     * @return int Result of comparison (always -1 or 1, never zero!)
+     */
+    public static function compareCells(self $a, self $b)
+    {
+        if ($a->getRow() < $b->getRow()) {
+            return -1;
+        } elseif ($a->getRow() > $b->getRow()) {
+            return 1;
+        } elseif (Coordinate::columnIndexFromString($a->getColumn()) < Coordinate::columnIndexFromString($b->getColumn())) {
+            return -1;
+        }
+
+        return 1;
+    }
+
+    /**
+     * Get value binder to use.
+     *
+     * @return IValueBinder
+     */
+    public static function getValueBinder()
+    {
+        if (self::$valueBinder === null) {
+            self::$valueBinder = new DefaultValueBinder();
+        }
+
+        return self::$valueBinder;
+    }
+
+    /**
+     * Set value binder to use.
+     */
+    public static function setValueBinder(IValueBinder $binder): void
+    {
+        self::$valueBinder = $binder;
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $vars = get_object_vars($this);
+        foreach ($vars as $key => $value) {
+            if ((is_object($value)) && ($key != 'parent')) {
+                $this->$key = clone $value;
+            } else {
+                $this->$key = $value;
+            }
+        }
+    }
+
+    /**
+     * Get index to cellXf.
+     *
+     * @return int
+     */
+    public function getXfIndex()
+    {
+        return $this->xfIndex;
+    }
+
+    /**
+     * Set index to cellXf.
+     *
+     * @param int $pValue
+     *
+     * @return Cell
+     */
+    public function setXfIndex($pValue)
+    {
+        $this->xfIndex = $pValue;
+
+        return $this->updateInCollection();
+    }
+
+    /**
+     * Set the formula attributes.
+     *
+     * @param mixed $pAttributes
+     *
+     * @return $this
+     */
+    public function setFormulaAttributes($pAttributes)
+    {
+        $this->formulaAttributes = $pAttributes;
+
+        return $this;
+    }
+
+    /**
+     * Get the formula attributes.
+     */
+    public function getFormulaAttributes()
+    {
+        return $this->formulaAttributes;
+    }
+
+    /**
+     * Convert to string.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return (string) $this->getValue();
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Coordinate.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Coordinate.php
new file mode 100755
index 0000000..8c67991
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Coordinate.php
@@ -0,0 +1,512 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Cell;
+
+use PhpOffice\PhpSpreadsheet\Exception;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+
+/**
+ * Helper class to manipulate cell coordinates.
+ *
+ * Columns indexes and rows are always based on 1, **not** on 0. This match the behavior
+ * that Excel users are used to, and also match the Excel functions `COLUMN()` and `ROW()`.
+ */
+abstract class Coordinate
+{
+    /**
+     * Default range variable constant.
+     *
+     * @var string
+     */
+    const DEFAULT_RANGE = 'A1:A1';
+
+    /**
+     * Coordinate from string.
+     *
+     * @param string $pCoordinateString eg: 'A1'
+     *
+     * @return string[] Array containing column and row (indexes 0 and 1)
+     */
+    public static function coordinateFromString($pCoordinateString)
+    {
+        if (preg_match('/^([$]?[A-Z]{1,3})([$]?\\d{1,7})$/', $pCoordinateString, $matches)) {
+            return [$matches[1], $matches[2]];
+        } elseif (self::coordinateIsRange($pCoordinateString)) {
+            throw new Exception('Cell coordinate string can not be a range of cells');
+        } elseif ($pCoordinateString == '') {
+            throw new Exception('Cell coordinate can not be zero-length string');
+        }
+
+        throw new Exception('Invalid cell coordinate ' . $pCoordinateString);
+    }
+
+    /**
+     * Checks if a coordinate represents a range of cells.
+     *
+     * @param string $coord eg: 'A1' or 'A1:A2' or 'A1:A2,C1:C2'
+     *
+     * @return bool Whether the coordinate represents a range of cells
+     */
+    public static function coordinateIsRange($coord)
+    {
+        return (strpos($coord, ':') !== false) || (strpos($coord, ',') !== false);
+    }
+
+    /**
+     * Make string row, column or cell coordinate absolute.
+     *
+     * @param string $pCoordinateString e.g. 'A' or '1' or 'A1'
+     *                    Note that this value can be a row or column reference as well as a cell reference
+     *
+     * @return string Absolute coordinate        e.g. '$A' or '$1' or '$A$1'
+     */
+    public static function absoluteReference($pCoordinateString)
+    {
+        if (self::coordinateIsRange($pCoordinateString)) {
+            throw new Exception('Cell coordinate string can not be a range of cells');
+        }
+
+        // Split out any worksheet name from the reference
+        [$worksheet, $pCoordinateString] = Worksheet::extractSheetTitle($pCoordinateString, true);
+        if ($worksheet > '') {
+            $worksheet .= '!';
+        }
+
+        // Create absolute coordinate
+        if (ctype_digit($pCoordinateString)) {
+            return $worksheet . '$' . $pCoordinateString;
+        } elseif (ctype_alpha($pCoordinateString)) {
+            return $worksheet . '$' . strtoupper($pCoordinateString);
+        }
+
+        return $worksheet . self::absoluteCoordinate($pCoordinateString);
+    }
+
+    /**
+     * Make string coordinate absolute.
+     *
+     * @param string $pCoordinateString e.g. 'A1'
+     *
+     * @return string Absolute coordinate        e.g. '$A$1'
+     */
+    public static function absoluteCoordinate($pCoordinateString)
+    {
+        if (self::coordinateIsRange($pCoordinateString)) {
+            throw new Exception('Cell coordinate string can not be a range of cells');
+        }
+
+        // Split out any worksheet name from the coordinate
+        [$worksheet, $pCoordinateString] = Worksheet::extractSheetTitle($pCoordinateString, true);
+        if ($worksheet > '') {
+            $worksheet .= '!';
+        }
+
+        // Create absolute coordinate
+        [$column, $row] = self::coordinateFromString($pCoordinateString);
+        $column = ltrim($column, '$');
+        $row = ltrim($row, '$');
+
+        return $worksheet . '$' . $column . '$' . $row;
+    }
+
+    /**
+     * Split range into coordinate strings.
+     *
+     * @param string $pRange e.g. 'B4:D9' or 'B4:D9,H2:O11' or 'B4'
+     *
+     * @return array Array containing one or more arrays containing one or two coordinate strings
+     *                                e.g. ['B4','D9'] or [['B4','D9'], ['H2','O11']]
+     *                                        or ['B4']
+     */
+    public static function splitRange($pRange)
+    {
+        // Ensure $pRange is a valid range
+        if (empty($pRange)) {
+            $pRange = self::DEFAULT_RANGE;
+        }
+
+        $exploded = explode(',', $pRange);
+        $counter = count($exploded);
+        for ($i = 0; $i < $counter; ++$i) {
+            $exploded[$i] = explode(':', $exploded[$i]);
+        }
+
+        return $exploded;
+    }
+
+    /**
+     * Build range from coordinate strings.
+     *
+     * @param array $pRange Array containg one or more arrays containing one or two coordinate strings
+     *
+     * @return string String representation of $pRange
+     */
+    public static function buildRange(array $pRange)
+    {
+        // Verify range
+        if (empty($pRange) || !is_array($pRange[0])) {
+            throw new Exception('Range does not contain any information');
+        }
+
+        // Build range
+        $counter = count($pRange);
+        for ($i = 0; $i < $counter; ++$i) {
+            $pRange[$i] = implode(':', $pRange[$i]);
+        }
+
+        return implode(',', $pRange);
+    }
+
+    /**
+     * Calculate range boundaries.
+     *
+     * @param string $pRange Cell range (e.g. A1:A1)
+     *
+     * @return array Range coordinates [Start Cell, End Cell]
+     *                    where Start Cell and End Cell are arrays (Column Number, Row Number)
+     */
+    public static function rangeBoundaries($pRange)
+    {
+        // Ensure $pRange is a valid range
+        if (empty($pRange)) {
+            $pRange = self::DEFAULT_RANGE;
+        }
+
+        // Uppercase coordinate
+        $pRange = strtoupper($pRange);
+
+        // Extract range
+        if (strpos($pRange, ':') === false) {
+            $rangeA = $rangeB = $pRange;
+        } else {
+            [$rangeA, $rangeB] = explode(':', $pRange);
+        }
+
+        // Calculate range outer borders
+        $rangeStart = self::coordinateFromString($rangeA);
+        $rangeEnd = self::coordinateFromString($rangeB);
+
+        // Translate column into index
+        $rangeStart[0] = self::columnIndexFromString($rangeStart[0]);
+        $rangeEnd[0] = self::columnIndexFromString($rangeEnd[0]);
+
+        return [$rangeStart, $rangeEnd];
+    }
+
+    /**
+     * Calculate range dimension.
+     *
+     * @param string $pRange Cell range (e.g. A1:A1)
+     *
+     * @return array Range dimension (width, height)
+     */
+    public static function rangeDimension($pRange)
+    {
+        // Calculate range outer borders
+        [$rangeStart, $rangeEnd] = self::rangeBoundaries($pRange);
+
+        return [($rangeEnd[0] - $rangeStart[0] + 1), ($rangeEnd[1] - $rangeStart[1] + 1)];
+    }
+
+    /**
+     * Calculate range boundaries.
+     *
+     * @param string $pRange Cell range (e.g. A1:A1)
+     *
+     * @return array Range coordinates [Start Cell, End Cell]
+     *                    where Start Cell and End Cell are arrays [Column ID, Row Number]
+     */
+    public static function getRangeBoundaries($pRange)
+    {
+        // Ensure $pRange is a valid range
+        if (empty($pRange)) {
+            $pRange = self::DEFAULT_RANGE;
+        }
+
+        // Uppercase coordinate
+        $pRange = strtoupper($pRange);
+
+        // Extract range
+        if (strpos($pRange, ':') === false) {
+            $rangeA = $rangeB = $pRange;
+        } else {
+            [$rangeA, $rangeB] = explode(':', $pRange);
+        }
+
+        return [self::coordinateFromString($rangeA), self::coordinateFromString($rangeB)];
+    }
+
+    /**
+     * Column index from string.
+     *
+     * @param string $pString eg 'A'
+     *
+     * @return int Column index (A = 1)
+     */
+    public static function columnIndexFromString($pString)
+    {
+        //    Using a lookup cache adds a slight memory overhead, but boosts speed
+        //    caching using a static within the method is faster than a class static,
+        //        though it's additional memory overhead
+        static $indexCache = [];
+
+        if (isset($indexCache[$pString])) {
+            return $indexCache[$pString];
+        }
+        //    It's surprising how costly the strtoupper() and ord() calls actually are, so we use a lookup array rather than use ord()
+        //        and make it case insensitive to get rid of the strtoupper() as well. Because it's a static, there's no significant
+        //        memory overhead either
+        static $columnLookup = [
+            'A' => 1, 'B' => 2, 'C' => 3, 'D' => 4, 'E' => 5, 'F' => 6, 'G' => 7, 'H' => 8, 'I' => 9, 'J' => 10, 'K' => 11, 'L' => 12, 'M' => 13,
+            'N' => 14, 'O' => 15, 'P' => 16, 'Q' => 17, 'R' => 18, 'S' => 19, 'T' => 20, 'U' => 21, 'V' => 22, 'W' => 23, 'X' => 24, 'Y' => 25, 'Z' => 26,
+            'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5, 'f' => 6, 'g' => 7, 'h' => 8, 'i' => 9, 'j' => 10, 'k' => 11, 'l' => 12, 'm' => 13,
+            'n' => 14, 'o' => 15, 'p' => 16, 'q' => 17, 'r' => 18, 's' => 19, 't' => 20, 'u' => 21, 'v' => 22, 'w' => 23, 'x' => 24, 'y' => 25, 'z' => 26,
+        ];
+
+        //    We also use the language construct isset() rather than the more costly strlen() function to match the length of $pString
+        //        for improved performance
+        if (isset($pString[0])) {
+            if (!isset($pString[1])) {
+                $indexCache[$pString] = $columnLookup[$pString];
+
+                return $indexCache[$pString];
+            } elseif (!isset($pString[2])) {
+                $indexCache[$pString] = $columnLookup[$pString[0]] * 26 + $columnLookup[$pString[1]];
+
+                return $indexCache[$pString];
+            } elseif (!isset($pString[3])) {
+                $indexCache[$pString] = $columnLookup[$pString[0]] * 676 + $columnLookup[$pString[1]] * 26 + $columnLookup[$pString[2]];
+
+                return $indexCache[$pString];
+            }
+        }
+
+        throw new Exception('Column string index can not be ' . ((isset($pString[0])) ? 'longer than 3 characters' : 'empty'));
+    }
+
+    /**
+     * String from column index.
+     *
+     * @param int $columnIndex Column index (A = 1)
+     *
+     * @return string
+     */
+    public static function stringFromColumnIndex($columnIndex)
+    {
+        static $indexCache = [];
+
+        if (!isset($indexCache[$columnIndex])) {
+            $indexValue = $columnIndex;
+            $base26 = null;
+            do {
+                $characterValue = ($indexValue % 26) ?: 26;
+                $indexValue = ($indexValue - $characterValue) / 26;
+                $base26 = chr($characterValue + 64) . ($base26 ?: '');
+            } while ($indexValue > 0);
+            $indexCache[$columnIndex] = $base26;
+        }
+
+        return $indexCache[$columnIndex];
+    }
+
+    /**
+     * Extract all cell references in range, which may be comprised of multiple cell ranges.
+     *
+     * @param string $pRange Range (e.g. A1 or A1:C10 or A1:E10 A20:E25)
+     *
+     * @return array Array containing single cell references
+     */
+    public static function extractAllCellReferencesInRange($pRange)
+    {
+        $returnValue = [];
+
+        // Explode spaces
+        $cellBlocks = self::getCellBlocksFromRangeString($pRange);
+        foreach ($cellBlocks as $cellBlock) {
+            $returnValue = array_merge($returnValue, self::getReferencesForCellBlock($cellBlock));
+        }
+
+        //    Sort the result by column and row
+        $sortKeys = [];
+        foreach (array_unique($returnValue) as $coord) {
+            $column = '';
+            $row = 0;
+
+            sscanf($coord, '%[A-Z]%d', $column, $row);
+            $sortKeys[sprintf('%3s%09d', $column, $row)] = $coord;
+        }
+        ksort($sortKeys);
+
+        // Return value
+        return array_values($sortKeys);
+    }
+
+    /**
+     * Get all cell references for an individual cell block.
+     *
+     * @param string $cellBlock A cell range e.g. A4:B5
+     *
+     * @return array All individual cells in that range
+     */
+    private static function getReferencesForCellBlock($cellBlock)
+    {
+        $returnValue = [];
+
+        // Single cell?
+        if (!self::coordinateIsRange($cellBlock)) {
+            return (array) $cellBlock;
+        }
+
+        // Range...
+        $ranges = self::splitRange($cellBlock);
+        foreach ($ranges as $range) {
+            // Single cell?
+            if (!isset($range[1])) {
+                $returnValue[] = $range[0];
+
+                continue;
+            }
+
+            // Range...
+            [$rangeStart, $rangeEnd] = $range;
+            [$startColumn, $startRow] = self::coordinateFromString($rangeStart);
+            [$endColumn, $endRow] = self::coordinateFromString($rangeEnd);
+            $startColumnIndex = self::columnIndexFromString($startColumn);
+            $endColumnIndex = self::columnIndexFromString($endColumn);
+            ++$endColumnIndex;
+
+            // Current data
+            $currentColumnIndex = $startColumnIndex;
+            $currentRow = $startRow;
+
+            self::validateRange($cellBlock, $startColumnIndex, $endColumnIndex, $currentRow, $endRow);
+
+            // Loop cells
+            while ($currentColumnIndex < $endColumnIndex) {
+                while ($currentRow <= $endRow) {
+                    $returnValue[] = self::stringFromColumnIndex($currentColumnIndex) . $currentRow;
+                    ++$currentRow;
+                }
+                ++$currentColumnIndex;
+                $currentRow = $startRow;
+            }
+        }
+
+        return $returnValue;
+    }
+
+    /**
+     * Convert an associative array of single cell coordinates to values to an associative array
+     * of cell ranges to values.  Only adjacent cell coordinates with the same
+     * value will be merged.  If the value is an object, it must implement the method getHashCode().
+     *
+     * For example, this function converts:
+     *
+     *    [ 'A1' => 'x', 'A2' => 'x', 'A3' => 'x', 'A4' => 'y' ]
+     *
+     * to:
+     *
+     *    [ 'A1:A3' => 'x', 'A4' => 'y' ]
+     *
+     * @param array $pCoordCollection associative array mapping coordinates to values
+     *
+     * @return array associative array mapping coordinate ranges to valuea
+     */
+    public static function mergeRangesInCollection(array $pCoordCollection)
+    {
+        $hashedValues = [];
+        $mergedCoordCollection = [];
+
+        foreach ($pCoordCollection as $coord => $value) {
+            if (self::coordinateIsRange($coord)) {
+                $mergedCoordCollection[$coord] = $value;
+
+                continue;
+            }
+
+            [$column, $row] = self::coordinateFromString($coord);
+            $row = (int) (ltrim($row, '$'));
+            $hashCode = $column . '-' . (is_object($value) ? $value->getHashCode() : $value);
+
+            if (!isset($hashedValues[$hashCode])) {
+                $hashedValues[$hashCode] = (object) [
+                    'value' => $value,
+                    'col' => $column,
+                    'rows' => [$row],
+                ];
+            } else {
+                $hashedValues[$hashCode]->rows[] = $row;
+            }
+        }
+
+        ksort($hashedValues);
+
+        foreach ($hashedValues as $hashedValue) {
+            sort($hashedValue->rows);
+            $rowStart = null;
+            $rowEnd = null;
+            $ranges = [];
+
+            foreach ($hashedValue->rows as $row) {
+                if ($rowStart === null) {
+                    $rowStart = $row;
+                    $rowEnd = $row;
+                } elseif ($rowEnd === $row - 1) {
+                    $rowEnd = $row;
+                } else {
+                    if ($rowStart == $rowEnd) {
+                        $ranges[] = $hashedValue->col . $rowStart;
+                    } else {
+                        $ranges[] = $hashedValue->col . $rowStart . ':' . $hashedValue->col . $rowEnd;
+                    }
+
+                    $rowStart = $row;
+                    $rowEnd = $row;
+                }
+            }
+
+            if ($rowStart !== null) {
+                if ($rowStart == $rowEnd) {
+                    $ranges[] = $hashedValue->col . $rowStart;
+                } else {
+                    $ranges[] = $hashedValue->col . $rowStart . ':' . $hashedValue->col . $rowEnd;
+                }
+            }
+
+            foreach ($ranges as $range) {
+                $mergedCoordCollection[$range] = $hashedValue->value;
+            }
+        }
+
+        return $mergedCoordCollection;
+    }
+
+    /**
+     * Get the individual cell blocks from a range string, splitting by space and removing any $ characters.
+     *
+     * @param string $pRange
+     *
+     * @return string[]
+     */
+    private static function getCellBlocksFromRangeString($pRange)
+    {
+        return explode(' ', str_replace('$', '', strtoupper($pRange)));
+    }
+
+    /**
+     * Check that the given range is valid, i.e. that the start column and row are not greater than the end column and
+     * row.
+     *
+     * @param string $cellBlock The original range, for displaying a meaningful error message
+     * @param int $startColumnIndex
+     * @param int $endColumnIndex
+     * @param int $currentRow
+     * @param int $endRow
+     */
+    private static function validateRange($cellBlock, $startColumnIndex, $endColumnIndex, $currentRow, $endRow): void
+    {
+        if ($startColumnIndex >= $endColumnIndex || $currentRow > $endRow) {
+            throw new Exception('Invalid range: "' . $cellBlock . '"');
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataType.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataType.php
new file mode 100755
index 0000000..ba03579
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataType.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Cell;
+
+use PhpOffice\PhpSpreadsheet\RichText\RichText;
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+
+class DataType
+{
+    // Data types
+    const TYPE_STRING2 = 'str';
+    const TYPE_STRING = 's';
+    const TYPE_FORMULA = 'f';
+    const TYPE_NUMERIC = 'n';
+    const TYPE_BOOL = 'b';
+    const TYPE_NULL = 'null';
+    const TYPE_INLINE = 'inlineStr';
+    const TYPE_ERROR = 'e';
+
+    /**
+     * List of error codes.
+     *
+     * @var array
+     */
+    private static $errorCodes = [
+        '#NULL!' => 0,
+        '#DIV/0!' => 1,
+        '#VALUE!' => 2,
+        '#REF!' => 3,
+        '#NAME?' => 4,
+        '#NUM!' => 5,
+        '#N/A' => 6,
+    ];
+
+    /**
+     * Get list of error codes.
+     *
+     * @return array
+     */
+    public static function getErrorCodes()
+    {
+        return self::$errorCodes;
+    }
+
+    /**
+     * Check a string that it satisfies Excel requirements.
+     *
+     * @param null|RichText|string $pValue Value to sanitize to an Excel string
+     *
+     * @return null|RichText|string Sanitized value
+     */
+    public static function checkString($pValue)
+    {
+        if ($pValue instanceof RichText) {
+            // TODO: Sanitize Rich-Text string (max. character count is 32,767)
+            return $pValue;
+        }
+
+        // string must never be longer than 32,767 characters, truncate if necessary
+        $pValue = StringHelper::substring($pValue, 0, 32767);
+
+        // we require that newline is represented as "\n" in core, not as "\r\n" or "\r"
+        $pValue = str_replace(["\r\n", "\r"], "\n", $pValue);
+
+        return $pValue;
+    }
+
+    /**
+     * Check a value that it is a valid error code.
+     *
+     * @param mixed $pValue Value to sanitize to an Excel error code
+     *
+     * @return string Sanitized value
+     */
+    public static function checkErrorCode($pValue)
+    {
+        $pValue = (string) $pValue;
+
+        if (!isset(self::$errorCodes[$pValue])) {
+            $pValue = '#NULL!';
+        }
+
+        return $pValue;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidation.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidation.php
new file mode 100755
index 0000000..dfeb024
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidation.php
@@ -0,0 +1,481 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Cell;
+
+class DataValidation
+{
+    // Data validation types
+    const TYPE_NONE = 'none';
+    const TYPE_CUSTOM = 'custom';
+    const TYPE_DATE = 'date';
+    const TYPE_DECIMAL = 'decimal';
+    const TYPE_LIST = 'list';
+    const TYPE_TEXTLENGTH = 'textLength';
+    const TYPE_TIME = 'time';
+    const TYPE_WHOLE = 'whole';
+
+    // Data validation error styles
+    const STYLE_STOP = 'stop';
+    const STYLE_WARNING = 'warning';
+    const STYLE_INFORMATION = 'information';
+
+    // Data validation operators
+    const OPERATOR_BETWEEN = 'between';
+    const OPERATOR_EQUAL = 'equal';
+    const OPERATOR_GREATERTHAN = 'greaterThan';
+    const OPERATOR_GREATERTHANOREQUAL = 'greaterThanOrEqual';
+    const OPERATOR_LESSTHAN = 'lessThan';
+    const OPERATOR_LESSTHANOREQUAL = 'lessThanOrEqual';
+    const OPERATOR_NOTBETWEEN = 'notBetween';
+    const OPERATOR_NOTEQUAL = 'notEqual';
+
+    /**
+     * Formula 1.
+     *
+     * @var string
+     */
+    private $formula1 = '';
+
+    /**
+     * Formula 2.
+     *
+     * @var string
+     */
+    private $formula2 = '';
+
+    /**
+     * Type.
+     *
+     * @var string
+     */
+    private $type = self::TYPE_NONE;
+
+    /**
+     * Error style.
+     *
+     * @var string
+     */
+    private $errorStyle = self::STYLE_STOP;
+
+    /**
+     * Operator.
+     *
+     * @var string
+     */
+    private $operator = self::OPERATOR_BETWEEN;
+
+    /**
+     * Allow Blank.
+     *
+     * @var bool
+     */
+    private $allowBlank = false;
+
+    /**
+     * Show DropDown.
+     *
+     * @var bool
+     */
+    private $showDropDown = false;
+
+    /**
+     * Show InputMessage.
+     *
+     * @var bool
+     */
+    private $showInputMessage = false;
+
+    /**
+     * Show ErrorMessage.
+     *
+     * @var bool
+     */
+    private $showErrorMessage = false;
+
+    /**
+     * Error title.
+     *
+     * @var string
+     */
+    private $errorTitle = '';
+
+    /**
+     * Error.
+     *
+     * @var string
+     */
+    private $error = '';
+
+    /**
+     * Prompt title.
+     *
+     * @var string
+     */
+    private $promptTitle = '';
+
+    /**
+     * Prompt.
+     *
+     * @var string
+     */
+    private $prompt = '';
+
+    /**
+     * Create a new DataValidation.
+     */
+    public function __construct()
+    {
+    }
+
+    /**
+     * Get Formula 1.
+     *
+     * @return string
+     */
+    public function getFormula1()
+    {
+        return $this->formula1;
+    }
+
+    /**
+     * Set Formula 1.
+     *
+     * @param string $value
+     *
+     * @return $this
+     */
+    public function setFormula1($value)
+    {
+        $this->formula1 = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get Formula 2.
+     *
+     * @return string
+     */
+    public function getFormula2()
+    {
+        return $this->formula2;
+    }
+
+    /**
+     * Set Formula 2.
+     *
+     * @param string $value
+     *
+     * @return $this
+     */
+    public function setFormula2($value)
+    {
+        $this->formula2 = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get Type.
+     *
+     * @return string
+     */
+    public function getType()
+    {
+        return $this->type;
+    }
+
+    /**
+     * Set Type.
+     *
+     * @param string $value
+     *
+     * @return $this
+     */
+    public function setType($value)
+    {
+        $this->type = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get Error style.
+     *
+     * @return string
+     */
+    public function getErrorStyle()
+    {
+        return $this->errorStyle;
+    }
+
+    /**
+     * Set Error style.
+     *
+     * @param string $value see self::STYLE_*
+     *
+     * @return $this
+     */
+    public function setErrorStyle($value)
+    {
+        $this->errorStyle = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get Operator.
+     *
+     * @return string
+     */
+    public function getOperator()
+    {
+        return $this->operator;
+    }
+
+    /**
+     * Set Operator.
+     *
+     * @param string $value
+     *
+     * @return $this
+     */
+    public function setOperator($value)
+    {
+        $this->operator = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get Allow Blank.
+     *
+     * @return bool
+     */
+    public function getAllowBlank()
+    {
+        return $this->allowBlank;
+    }
+
+    /**
+     * Set Allow Blank.
+     *
+     * @param bool $value
+     *
+     * @return $this
+     */
+    public function setAllowBlank($value)
+    {
+        $this->allowBlank = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get Show DropDown.
+     *
+     * @return bool
+     */
+    public function getShowDropDown()
+    {
+        return $this->showDropDown;
+    }
+
+    /**
+     * Set Show DropDown.
+     *
+     * @param bool $value
+     *
+     * @return $this
+     */
+    public function setShowDropDown($value)
+    {
+        $this->showDropDown = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get Show InputMessage.
+     *
+     * @return bool
+     */
+    public function getShowInputMessage()
+    {
+        return $this->showInputMessage;
+    }
+
+    /**
+     * Set Show InputMessage.
+     *
+     * @param bool $value
+     *
+     * @return $this
+     */
+    public function setShowInputMessage($value)
+    {
+        $this->showInputMessage = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get Show ErrorMessage.
+     *
+     * @return bool
+     */
+    public function getShowErrorMessage()
+    {
+        return $this->showErrorMessage;
+    }
+
+    /**
+     * Set Show ErrorMessage.
+     *
+     * @param bool $value
+     *
+     * @return $this
+     */
+    public function setShowErrorMessage($value)
+    {
+        $this->showErrorMessage = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get Error title.
+     *
+     * @return string
+     */
+    public function getErrorTitle()
+    {
+        return $this->errorTitle;
+    }
+
+    /**
+     * Set Error title.
+     *
+     * @param string $value
+     *
+     * @return $this
+     */
+    public function setErrorTitle($value)
+    {
+        $this->errorTitle = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get Error.
+     *
+     * @return string
+     */
+    public function getError()
+    {
+        return $this->error;
+    }
+
+    /**
+     * Set Error.
+     *
+     * @param string $value
+     *
+     * @return $this
+     */
+    public function setError($value)
+    {
+        $this->error = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get Prompt title.
+     *
+     * @return string
+     */
+    public function getPromptTitle()
+    {
+        return $this->promptTitle;
+    }
+
+    /**
+     * Set Prompt title.
+     *
+     * @param string $value
+     *
+     * @return $this
+     */
+    public function setPromptTitle($value)
+    {
+        $this->promptTitle = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get Prompt.
+     *
+     * @return string
+     */
+    public function getPrompt()
+    {
+        return $this->prompt;
+    }
+
+    /**
+     * Set Prompt.
+     *
+     * @param string $value
+     *
+     * @return $this
+     */
+    public function setPrompt($value)
+    {
+        $this->prompt = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get hash code.
+     *
+     * @return string Hash code
+     */
+    public function getHashCode()
+    {
+        return md5(
+            $this->formula1 .
+            $this->formula2 .
+            $this->type .
+            $this->errorStyle .
+            $this->operator .
+            ($this->allowBlank ? 't' : 'f') .
+            ($this->showDropDown ? 't' : 'f') .
+            ($this->showInputMessage ? 't' : 'f') .
+            ($this->showErrorMessage ? 't' : 'f') .
+            $this->errorTitle .
+            $this->error .
+            $this->promptTitle .
+            $this->prompt .
+            __CLASS__
+        );
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $vars = get_object_vars($this);
+        foreach ($vars as $key => $value) {
+            if (is_object($value)) {
+                $this->$key = clone $value;
+            } else {
+                $this->$key = $value;
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidator.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidator.php
new file mode 100755
index 0000000..430d81b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidator.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Cell;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
+use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Exception;
+
+/**
+ * Validate a cell value according to its validation rules.
+ */
+class DataValidator
+{
+    /**
+     * Does this cell contain valid value?
+     *
+     * @param Cell $cell Cell to check the value
+     *
+     * @return bool
+     */
+    public function isValid(Cell $cell)
+    {
+        if (!$cell->hasDataValidation()) {
+            return true;
+        }
+
+        $cellValue = $cell->getValue();
+        $dataValidation = $cell->getDataValidation();
+
+        if (!$dataValidation->getAllowBlank() && ($cellValue === null || $cellValue === '')) {
+            return false;
+        }
+
+        // TODO: write check on all cases
+        switch ($dataValidation->getType()) {
+            case DataValidation::TYPE_LIST:
+                return $this->isValueInList($cell);
+        }
+
+        return false;
+    }
+
+    /**
+     * Does this cell contain valid value, based on list?
+     *
+     * @param Cell $cell Cell to check the value
+     *
+     * @return bool
+     */
+    private function isValueInList(Cell $cell)
+    {
+        $cellValue = $cell->getValue();
+        $dataValidation = $cell->getDataValidation();
+
+        $formula1 = $dataValidation->getFormula1();
+        if (!empty($formula1)) {
+            // inline values list
+            if ($formula1[0] === '"') {
+                return in_array(strtolower($cellValue), explode(',', strtolower(trim($formula1, '"'))), true);
+            } elseif (strpos($formula1, ':') > 0) {
+                // values list cells
+                $matchFormula = '=MATCH(' . $cell->getCoordinate() . ', ' . $formula1 . ', 0)';
+                $calculation = Calculation::getInstance($cell->getWorksheet()->getParent());
+
+                try {
+                    $result = $calculation->calculateFormula($matchFormula, $cell->getCoordinate(), $cell);
+
+                    return $result !== Functions::NA();
+                } catch (Exception $ex) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DefaultValueBinder.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DefaultValueBinder.php
new file mode 100755
index 0000000..693446e
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DefaultValueBinder.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Cell;
+
+use DateTimeInterface;
+use PhpOffice\PhpSpreadsheet\RichText\RichText;
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+
+class DefaultValueBinder implements IValueBinder
+{
+    /**
+     * Bind value to a cell.
+     *
+     * @param Cell $cell Cell to bind value to
+     * @param mixed $value Value to bind in cell
+     *
+     * @return bool
+     */
+    public function bindValue(Cell $cell, $value)
+    {
+        // sanitize UTF-8 strings
+        if (is_string($value)) {
+            $value = StringHelper::sanitizeUTF8($value);
+        } elseif (is_object($value)) {
+            // Handle any objects that might be injected
+            if ($value instanceof DateTimeInterface) {
+                $value = $value->format('Y-m-d H:i:s');
+            } elseif (!($value instanceof RichText)) {
+                $value = (string) $value;
+            }
+        }
+
+        // Set value explicit
+        $cell->setValueExplicit($value, static::dataTypeForValue($value));
+
+        // Done!
+        return true;
+    }
+
+    /**
+     * DataType for value.
+     *
+     * @param mixed $pValue
+     *
+     * @return string
+     */
+    public static function dataTypeForValue($pValue)
+    {
+        // Match the value against a few data types
+        if ($pValue === null) {
+            return DataType::TYPE_NULL;
+        } elseif (is_float($pValue) || is_int($pValue)) {
+            return DataType::TYPE_NUMERIC;
+        } elseif (is_bool($pValue)) {
+            return DataType::TYPE_BOOL;
+        } elseif ($pValue === '') {
+            return DataType::TYPE_STRING;
+        } elseif ($pValue instanceof RichText) {
+            return DataType::TYPE_INLINE;
+        } elseif (is_string($pValue) && $pValue[0] === '=' && strlen($pValue) > 1) {
+            return DataType::TYPE_FORMULA;
+        } elseif (preg_match('/^[\+\-]?(\d+\\.?\d*|\d*\\.?\d+)([Ee][\-\+]?[0-2]?\d{1,3})?$/', $pValue)) {
+            $tValue = ltrim($pValue, '+-');
+            if (is_string($pValue) && $tValue[0] === '0' && strlen($tValue) > 1 && $tValue[1] !== '.') {
+                return DataType::TYPE_STRING;
+            } elseif ((strpos($pValue, '.') === false) && ($pValue > PHP_INT_MAX)) {
+                return DataType::TYPE_STRING;
+            } elseif (!is_numeric($pValue)) {
+                return DataType::TYPE_STRING;
+            }
+
+            return DataType::TYPE_NUMERIC;
+        } elseif (is_string($pValue)) {
+            $errorCodes = DataType::getErrorCodes();
+            if (isset($errorCodes[$pValue])) {
+                return DataType::TYPE_ERROR;
+            }
+        }
+
+        return DataType::TYPE_STRING;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Hyperlink.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Hyperlink.php
new file mode 100755
index 0000000..003d510
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Hyperlink.php
@@ -0,0 +1,113 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Cell;
+
+class Hyperlink
+{
+    /**
+     * URL to link the cell to.
+     *
+     * @var string
+     */
+    private $url;
+
+    /**
+     * Tooltip to display on the hyperlink.
+     *
+     * @var string
+     */
+    private $tooltip;
+
+    /**
+     * Create a new Hyperlink.
+     *
+     * @param string $pUrl Url to link the cell to
+     * @param string $pTooltip Tooltip to display on the hyperlink
+     */
+    public function __construct($pUrl = '', $pTooltip = '')
+    {
+        // Initialise member variables
+        $this->url = $pUrl;
+        $this->tooltip = $pTooltip;
+    }
+
+    /**
+     * Get URL.
+     *
+     * @return string
+     */
+    public function getUrl()
+    {
+        return $this->url;
+    }
+
+    /**
+     * Set URL.
+     *
+     * @param string $value
+     *
+     * @return $this
+     */
+    public function setUrl($value)
+    {
+        $this->url = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get tooltip.
+     *
+     * @return string
+     */
+    public function getTooltip()
+    {
+        return $this->tooltip;
+    }
+
+    /**
+     * Set tooltip.
+     *
+     * @param string $value
+     *
+     * @return $this
+     */
+    public function setTooltip($value)
+    {
+        $this->tooltip = $value;
+
+        return $this;
+    }
+
+    /**
+     * Is this hyperlink internal? (to another worksheet).
+     *
+     * @return bool
+     */
+    public function isInternal()
+    {
+        return strpos($this->url, 'sheet://') !== false;
+    }
+
+    /**
+     * @return string
+     */
+    public function getTypeHyperlink()
+    {
+        return $this->isInternal() ? '' : 'External';
+    }
+
+    /**
+     * Get hash code.
+     *
+     * @return string Hash code
+     */
+    public function getHashCode()
+    {
+        return md5(
+            $this->url .
+            $this->tooltip .
+            __CLASS__
+        );
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/IValueBinder.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/IValueBinder.php
new file mode 100755
index 0000000..5af9f5f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/IValueBinder.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Cell;
+
+interface IValueBinder
+{
+    /**
+     * Bind value to a cell.
+     *
+     * @param Cell $cell Cell to bind value to
+     * @param mixed $value Value to bind in cell
+     *
+     * @return bool
+     */
+    public function bindValue(Cell $cell, $value);
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/StringValueBinder.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/StringValueBinder.php
new file mode 100755
index 0000000..346d025
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/StringValueBinder.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Cell;
+
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+
+class StringValueBinder implements IValueBinder
+{
+    /**
+     * Bind value to a cell.
+     *
+     * @param Cell $cell Cell to bind value to
+     * @param mixed $value Value to bind in cell
+     *
+     * @return bool
+     */
+    public function bindValue(Cell $cell, $value)
+    {
+        // sanitize UTF-8 strings
+        if (is_string($value)) {
+            $value = StringHelper::sanitizeUTF8($value);
+        }
+
+        $cell->setValueExplicit((string) $value, DataType::TYPE_STRING);
+
+        // Done!
+        return true;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Axis.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Axis.php
new file mode 100755
index 0000000..7995c3b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Axis.php
@@ -0,0 +1,556 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Chart;
+
+/**
+ * Created by PhpStorm.
+ * User: Wiktor Trzonkowski
+ * Date: 6/17/14
+ * Time: 12:11 PM.
+ */
+class Axis extends Properties
+{
+    /**
+     * Axis Number.
+     *
+     * @var array of mixed
+     */
+    private $axisNumber = [
+        'format' => self::FORMAT_CODE_GENERAL,
+        'source_linked' => 1,
+    ];
+
+    /**
+     * Axis Options.
+     *
+     * @var array of mixed
+     */
+    private $axisOptions = [
+        'minimum' => null,
+        'maximum' => null,
+        'major_unit' => null,
+        'minor_unit' => null,
+        'orientation' => self::ORIENTATION_NORMAL,
+        'minor_tick_mark' => self::TICK_MARK_NONE,
+        'major_tick_mark' => self::TICK_MARK_NONE,
+        'axis_labels' => self::AXIS_LABELS_NEXT_TO,
+        'horizontal_crosses' => self::HORIZONTAL_CROSSES_AUTOZERO,
+        'horizontal_crosses_value' => null,
+    ];
+
+    /**
+     * Fill Properties.
+     *
+     * @var array of mixed
+     */
+    private $fillProperties = [
+        'type' => self::EXCEL_COLOR_TYPE_ARGB,
+        'value' => null,
+        'alpha' => 0,
+    ];
+
+    /**
+     * Line Properties.
+     *
+     * @var array of mixed
+     */
+    private $lineProperties = [
+        'type' => self::EXCEL_COLOR_TYPE_ARGB,
+        'value' => null,
+        'alpha' => 0,
+    ];
+
+    /**
+     * Line Style Properties.
+     *
+     * @var array of mixed
+     */
+    private $lineStyleProperties = [
+        'width' => '9525',
+        'compound' => self::LINE_STYLE_COMPOUND_SIMPLE,
+        'dash' => self::LINE_STYLE_DASH_SOLID,
+        'cap' => self::LINE_STYLE_CAP_FLAT,
+        'join' => self::LINE_STYLE_JOIN_BEVEL,
+        'arrow' => [
+            'head' => [
+                'type' => self::LINE_STYLE_ARROW_TYPE_NOARROW,
+                'size' => self::LINE_STYLE_ARROW_SIZE_5,
+            ],
+            'end' => [
+                'type' => self::LINE_STYLE_ARROW_TYPE_NOARROW,
+                'size' => self::LINE_STYLE_ARROW_SIZE_8,
+            ],
+        ],
+    ];
+
+    /**
+     * Shadow Properties.
+     *
+     * @var array of mixed
+     */
+    private $shadowProperties = [
+        'presets' => self::SHADOW_PRESETS_NOSHADOW,
+        'effect' => null,
+        'color' => [
+            'type' => self::EXCEL_COLOR_TYPE_STANDARD,
+            'value' => 'black',
+            'alpha' => 40,
+        ],
+        'size' => [
+            'sx' => null,
+            'sy' => null,
+            'kx' => null,
+        ],
+        'blur' => null,
+        'direction' => null,
+        'distance' => null,
+        'algn' => null,
+        'rotWithShape' => null,
+    ];
+
+    /**
+     * Glow Properties.
+     *
+     * @var array of mixed
+     */
+    private $glowProperties = [
+        'size' => null,
+        'color' => [
+            'type' => self::EXCEL_COLOR_TYPE_STANDARD,
+            'value' => 'black',
+            'alpha' => 40,
+        ],
+    ];
+
+    /**
+     * Soft Edge Properties.
+     *
+     * @var array of mixed
+     */
+    private $softEdges = [
+        'size' => null,
+    ];
+
+    /**
+     * Get Series Data Type.
+     *
+     * @param mixed $format_code
+     *
+     * @return string
+     */
+    public function setAxisNumberProperties($format_code)
+    {
+        $this->axisNumber['format'] = (string) $format_code;
+        $this->axisNumber['source_linked'] = 0;
+    }
+
+    /**
+     * Get Axis Number Format Data Type.
+     *
+     * @return string
+     */
+    public function getAxisNumberFormat()
+    {
+        return $this->axisNumber['format'];
+    }
+
+    /**
+     * Get Axis Number Source Linked.
+     *
+     * @return string
+     */
+    public function getAxisNumberSourceLinked()
+    {
+        return (string) $this->axisNumber['source_linked'];
+    }
+
+    /**
+     * Set Axis Options Properties.
+     *
+     * @param string $axis_labels
+     * @param string $horizontal_crosses_value
+     * @param string $horizontal_crosses
+     * @param string $axis_orientation
+     * @param string $major_tmt
+     * @param string $minor_tmt
+     * @param string $minimum
+     * @param string $maximum
+     * @param string $major_unit
+     * @param string $minor_unit
+     */
+    public function setAxisOptionsProperties($axis_labels, $horizontal_crosses_value = null, $horizontal_crosses = null, $axis_orientation = null, $major_tmt = null, $minor_tmt = null, $minimum = null, $maximum = null, $major_unit = null, $minor_unit = null): void
+    {
+        $this->axisOptions['axis_labels'] = (string) $axis_labels;
+        ($horizontal_crosses_value !== null) ? $this->axisOptions['horizontal_crosses_value'] = (string) $horizontal_crosses_value : null;
+        ($horizontal_crosses !== null) ? $this->axisOptions['horizontal_crosses'] = (string) $horizontal_crosses : null;
+        ($axis_orientation !== null) ? $this->axisOptions['orientation'] = (string) $axis_orientation : null;
+        ($major_tmt !== null) ? $this->axisOptions['major_tick_mark'] = (string) $major_tmt : null;
+        ($minor_tmt !== null) ? $this->axisOptions['minor_tick_mark'] = (string) $minor_tmt : null;
+        ($minor_tmt !== null) ? $this->axisOptions['minor_tick_mark'] = (string) $minor_tmt : null;
+        ($minimum !== null) ? $this->axisOptions['minimum'] = (string) $minimum : null;
+        ($maximum !== null) ? $this->axisOptions['maximum'] = (string) $maximum : null;
+        ($major_unit !== null) ? $this->axisOptions['major_unit'] = (string) $major_unit : null;
+        ($minor_unit !== null) ? $this->axisOptions['minor_unit'] = (string) $minor_unit : null;
+    }
+
+    /**
+     * Get Axis Options Property.
+     *
+     * @param string $property
+     *
+     * @return string
+     */
+    public function getAxisOptionsProperty($property)
+    {
+        return $this->axisOptions[$property];
+    }
+
+    /**
+     * Set Axis Orientation Property.
+     *
+     * @param string $orientation
+     */
+    public function setAxisOrientation($orientation): void
+    {
+        $this->axisOptions['orientation'] = (string) $orientation;
+    }
+
+    /**
+     * Set Fill Property.
+     *
+     * @param string $color
+     * @param int $alpha
+     * @param string $type
+     */
+    public function setFillParameters($color, $alpha = 0, $type = self::EXCEL_COLOR_TYPE_ARGB): void
+    {
+        $this->fillProperties = $this->setColorProperties($color, $alpha, $type);
+    }
+
+    /**
+     * Set Line Property.
+     *
+     * @param string $color
+     * @param int $alpha
+     * @param string $type
+     */
+    public function setLineParameters($color, $alpha = 0, $type = self::EXCEL_COLOR_TYPE_ARGB): void
+    {
+        $this->lineProperties = $this->setColorProperties($color, $alpha, $type);
+    }
+
+    /**
+     * Get Fill Property.
+     *
+     * @param string $property
+     *
+     * @return string
+     */
+    public function getFillProperty($property)
+    {
+        return $this->fillProperties[$property];
+    }
+
+    /**
+     * Get Line Property.
+     *
+     * @param string $property
+     *
+     * @return string
+     */
+    public function getLineProperty($property)
+    {
+        return $this->lineProperties[$property];
+    }
+
+    /**
+     * Set Line Style Properties.
+     *
+     * @param float $line_width
+     * @param string $compound_type
+     * @param string $dash_type
+     * @param string $cap_type
+     * @param string $join_type
+     * @param string $head_arrow_type
+     * @param string $head_arrow_size
+     * @param string $end_arrow_type
+     * @param string $end_arrow_size
+     */
+    public function setLineStyleProperties($line_width = null, $compound_type = null, $dash_type = null, $cap_type = null, $join_type = null, $head_arrow_type = null, $head_arrow_size = null, $end_arrow_type = null, $end_arrow_size = null): void
+    {
+        ($line_width !== null) ? $this->lineStyleProperties['width'] = $this->getExcelPointsWidth((float) $line_width) : null;
+        ($compound_type !== null) ? $this->lineStyleProperties['compound'] = (string) $compound_type : null;
+        ($dash_type !== null) ? $this->lineStyleProperties['dash'] = (string) $dash_type : null;
+        ($cap_type !== null) ? $this->lineStyleProperties['cap'] = (string) $cap_type : null;
+        ($join_type !== null) ? $this->lineStyleProperties['join'] = (string) $join_type : null;
+        ($head_arrow_type !== null) ? $this->lineStyleProperties['arrow']['head']['type'] = (string) $head_arrow_type : null;
+        ($head_arrow_size !== null) ? $this->lineStyleProperties['arrow']['head']['size'] = (string) $head_arrow_size : null;
+        ($end_arrow_type !== null) ? $this->lineStyleProperties['arrow']['end']['type'] = (string) $end_arrow_type : null;
+        ($end_arrow_size !== null) ? $this->lineStyleProperties['arrow']['end']['size'] = (string) $end_arrow_size : null;
+    }
+
+    /**
+     * Get Line Style Property.
+     *
+     * @param array|string $elements
+     *
+     * @return string
+     */
+    public function getLineStyleProperty($elements)
+    {
+        return $this->getArrayElementsValue($this->lineStyleProperties, $elements);
+    }
+
+    /**
+     * Get Line Style Arrow Excel Width.
+     *
+     * @param string $arrow
+     *
+     * @return string
+     */
+    public function getLineStyleArrowWidth($arrow)
+    {
+        return $this->getLineStyleArrowSize($this->lineStyleProperties['arrow'][$arrow]['size'], 'w');
+    }
+
+    /**
+     * Get Line Style Arrow Excel Length.
+     *
+     * @param string $arrow
+     *
+     * @return string
+     */
+    public function getLineStyleArrowLength($arrow)
+    {
+        return $this->getLineStyleArrowSize($this->lineStyleProperties['arrow'][$arrow]['size'], 'len');
+    }
+
+    /**
+     * Set Shadow Properties.
+     *
+     * @param int $sh_presets
+     * @param string $sh_color_value
+     * @param string $sh_color_type
+     * @param string $sh_color_alpha
+     * @param float $sh_blur
+     * @param int $sh_angle
+     * @param float $sh_distance
+     */
+    public function setShadowProperties($sh_presets, $sh_color_value = null, $sh_color_type = null, $sh_color_alpha = null, $sh_blur = null, $sh_angle = null, $sh_distance = null): void
+    {
+        $this->setShadowPresetsProperties((int) $sh_presets)
+            ->setShadowColor(
+                $sh_color_value === null ? $this->shadowProperties['color']['value'] : $sh_color_value,
+                $sh_color_alpha === null ? (int) $this->shadowProperties['color']['alpha'] : $sh_color_alpha,
+                $sh_color_type === null ? $this->shadowProperties['color']['type'] : $sh_color_type
+            )
+            ->setShadowBlur($sh_blur)
+            ->setShadowAngle($sh_angle)
+            ->setShadowDistance($sh_distance);
+    }
+
+    /**
+     * Set Shadow Color.
+     *
+     * @param int $shadow_presets
+     *
+     * @return $this
+     */
+    private function setShadowPresetsProperties($shadow_presets)
+    {
+        $this->shadowProperties['presets'] = $shadow_presets;
+        $this->setShadowProperiesMapValues($this->getShadowPresetsMap($shadow_presets));
+
+        return $this;
+    }
+
+    /**
+     * Set Shadow Properties from Mapped Values.
+     *
+     * @param mixed &$reference
+     *
+     * @return $this
+     */
+    private function setShadowProperiesMapValues(array $properties_map, &$reference = null)
+    {
+        $base_reference = $reference;
+        foreach ($properties_map as $property_key => $property_val) {
+            if (is_array($property_val)) {
+                if ($reference === null) {
+                    $reference = &$this->shadowProperties[$property_key];
+                } else {
+                    $reference = &$reference[$property_key];
+                }
+                $this->setShadowProperiesMapValues($property_val, $reference);
+            } else {
+                if ($base_reference === null) {
+                    $this->shadowProperties[$property_key] = $property_val;
+                } else {
+                    $reference[$property_key] = $property_val;
+                }
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Set Shadow Color.
+     *
+     * @param string $color
+     * @param int $alpha
+     * @param string $type
+     *
+     * @return $this
+     */
+    private function setShadowColor($color, $alpha, $type)
+    {
+        $this->shadowProperties['color'] = $this->setColorProperties($color, $alpha, $type);
+
+        return $this;
+    }
+
+    /**
+     * Set Shadow Blur.
+     *
+     * @param float $blur
+     *
+     * @return $this
+     */
+    private function setShadowBlur($blur)
+    {
+        if ($blur !== null) {
+            $this->shadowProperties['blur'] = (string) $this->getExcelPointsWidth($blur);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Set Shadow Angle.
+     *
+     * @param int $angle
+     *
+     * @return $this
+     */
+    private function setShadowAngle($angle)
+    {
+        if ($angle !== null) {
+            $this->shadowProperties['direction'] = (string) $this->getExcelPointsAngle($angle);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Set Shadow Distance.
+     *
+     * @param float $distance
+     *
+     * @return $this
+     */
+    private function setShadowDistance($distance)
+    {
+        if ($distance !== null) {
+            $this->shadowProperties['distance'] = (string) $this->getExcelPointsWidth($distance);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Shadow Property.
+     *
+     * @param string|string[] $elements
+     *
+     * @return null|array|int|string
+     */
+    public function getShadowProperty($elements)
+    {
+        return $this->getArrayElementsValue($this->shadowProperties, $elements);
+    }
+
+    /**
+     * Set Glow Properties.
+     *
+     * @param float $size
+     * @param string $color_value
+     * @param int $color_alpha
+     * @param string $color_type
+     */
+    public function setGlowProperties($size, $color_value = null, $color_alpha = null, $color_type = null): void
+    {
+        $this->setGlowSize($size)
+            ->setGlowColor(
+                $color_value === null ? $this->glowProperties['color']['value'] : $color_value,
+                $color_alpha === null ? (int) $this->glowProperties['color']['alpha'] : $color_alpha,
+                $color_type === null ? $this->glowProperties['color']['type'] : $color_type
+            );
+    }
+
+    /**
+     * Get Glow Property.
+     *
+     * @param array|string $property
+     *
+     * @return string
+     */
+    public function getGlowProperty($property)
+    {
+        return $this->getArrayElementsValue($this->glowProperties, $property);
+    }
+
+    /**
+     * Set Glow Color.
+     *
+     * @param float $size
+     *
+     * @return $this
+     */
+    private function setGlowSize($size)
+    {
+        if ($size !== null) {
+            $this->glowProperties['size'] = $this->getExcelPointsWidth($size);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Set Glow Color.
+     *
+     * @param string $color
+     * @param int $alpha
+     * @param string $type
+     *
+     * @return $this
+     */
+    private function setGlowColor($color, $alpha, $type)
+    {
+        $this->glowProperties['color'] = $this->setColorProperties($color, $alpha, $type);
+
+        return $this;
+    }
+
+    /**
+     * Set Soft Edges Size.
+     *
+     * @param float $size
+     */
+    public function setSoftEdges($size): void
+    {
+        if ($size !== null) {
+            $softEdges['size'] = (string) $this->getExcelPointsWidth($size);
+        }
+    }
+
+    /**
+     * Get Soft Edges Size.
+     *
+     * @return string
+     */
+    public function getSoftEdgesSize()
+    {
+        return $this->softEdges['size'];
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Chart.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Chart.php
new file mode 100755
index 0000000..20eb2ae
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Chart.php
@@ -0,0 +1,663 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Chart;
+
+use PhpOffice\PhpSpreadsheet\Settings;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+
+class Chart
+{
+    /**
+     * Chart Name.
+     *
+     * @var string
+     */
+    private $name = '';
+
+    /**
+     * Worksheet.
+     *
+     * @var Worksheet
+     */
+    private $worksheet;
+
+    /**
+     * Chart Title.
+     *
+     * @var Title
+     */
+    private $title;
+
+    /**
+     * Chart Legend.
+     *
+     * @var Legend
+     */
+    private $legend;
+
+    /**
+     * X-Axis Label.
+     *
+     * @var Title
+     */
+    private $xAxisLabel;
+
+    /**
+     * Y-Axis Label.
+     *
+     * @var Title
+     */
+    private $yAxisLabel;
+
+    /**
+     * Chart Plot Area.
+     *
+     * @var PlotArea
+     */
+    private $plotArea;
+
+    /**
+     * Plot Visible Only.
+     *
+     * @var bool
+     */
+    private $plotVisibleOnly = true;
+
+    /**
+     * Display Blanks as.
+     *
+     * @var string
+     */
+    private $displayBlanksAs = DataSeries::EMPTY_AS_GAP;
+
+    /**
+     * Chart Asix Y as.
+     *
+     * @var Axis
+     */
+    private $yAxis;
+
+    /**
+     * Chart Asix X as.
+     *
+     * @var Axis
+     */
+    private $xAxis;
+
+    /**
+     * Chart Major Gridlines as.
+     *
+     * @var GridLines
+     */
+    private $majorGridlines;
+
+    /**
+     * Chart Minor Gridlines as.
+     *
+     * @var GridLines
+     */
+    private $minorGridlines;
+
+    /**
+     * Top-Left Cell Position.
+     *
+     * @var string
+     */
+    private $topLeftCellRef = 'A1';
+
+    /**
+     * Top-Left X-Offset.
+     *
+     * @var int
+     */
+    private $topLeftXOffset = 0;
+
+    /**
+     * Top-Left Y-Offset.
+     *
+     * @var int
+     */
+    private $topLeftYOffset = 0;
+
+    /**
+     * Bottom-Right Cell Position.
+     *
+     * @var string
+     */
+    private $bottomRightCellRef = 'A1';
+
+    /**
+     * Bottom-Right X-Offset.
+     *
+     * @var int
+     */
+    private $bottomRightXOffset = 10;
+
+    /**
+     * Bottom-Right Y-Offset.
+     *
+     * @var int
+     */
+    private $bottomRightYOffset = 10;
+
+    /**
+     * Create a new Chart.
+     *
+     * @param mixed $name
+     * @param mixed $plotVisibleOnly
+     * @param string $displayBlanksAs
+     */
+    public function __construct($name, ?Title $title = null, ?Legend $legend = null, ?PlotArea $plotArea = null, $plotVisibleOnly = true, $displayBlanksAs = DataSeries::EMPTY_AS_GAP, ?Title $xAxisLabel = null, ?Title $yAxisLabel = null, ?Axis $xAxis = null, ?Axis $yAxis = null, ?GridLines $majorGridlines = null, ?GridLines $minorGridlines = null)
+    {
+        $this->name = $name;
+        $this->title = $title;
+        $this->legend = $legend;
+        $this->xAxisLabel = $xAxisLabel;
+        $this->yAxisLabel = $yAxisLabel;
+        $this->plotArea = $plotArea;
+        $this->plotVisibleOnly = $plotVisibleOnly;
+        $this->displayBlanksAs = $displayBlanksAs;
+        $this->xAxis = $xAxis;
+        $this->yAxis = $yAxis;
+        $this->majorGridlines = $majorGridlines;
+        $this->minorGridlines = $minorGridlines;
+    }
+
+    /**
+     * Get Name.
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * Get Worksheet.
+     *
+     * @return Worksheet
+     */
+    public function getWorksheet()
+    {
+        return $this->worksheet;
+    }
+
+    /**
+     * Set Worksheet.
+     *
+     * @param Worksheet $pValue
+     *
+     * @return $this
+     */
+    public function setWorksheet(?Worksheet $pValue = null)
+    {
+        $this->worksheet = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Title.
+     *
+     * @return Title
+     */
+    public function getTitle()
+    {
+        return $this->title;
+    }
+
+    /**
+     * Set Title.
+     *
+     * @return $this
+     */
+    public function setTitle(Title $title)
+    {
+        $this->title = $title;
+
+        return $this;
+    }
+
+    /**
+     * Get Legend.
+     *
+     * @return Legend
+     */
+    public function getLegend()
+    {
+        return $this->legend;
+    }
+
+    /**
+     * Set Legend.
+     *
+     * @return $this
+     */
+    public function setLegend(Legend $legend)
+    {
+        $this->legend = $legend;
+
+        return $this;
+    }
+
+    /**
+     * Get X-Axis Label.
+     *
+     * @return Title
+     */
+    public function getXAxisLabel()
+    {
+        return $this->xAxisLabel;
+    }
+
+    /**
+     * Set X-Axis Label.
+     *
+     * @return $this
+     */
+    public function setXAxisLabel(Title $label)
+    {
+        $this->xAxisLabel = $label;
+
+        return $this;
+    }
+
+    /**
+     * Get Y-Axis Label.
+     *
+     * @return Title
+     */
+    public function getYAxisLabel()
+    {
+        return $this->yAxisLabel;
+    }
+
+    /**
+     * Set Y-Axis Label.
+     *
+     * @return $this
+     */
+    public function setYAxisLabel(Title $label)
+    {
+        $this->yAxisLabel = $label;
+
+        return $this;
+    }
+
+    /**
+     * Get Plot Area.
+     *
+     * @return PlotArea
+     */
+    public function getPlotArea()
+    {
+        return $this->plotArea;
+    }
+
+    /**
+     * Get Plot Visible Only.
+     *
+     * @return bool
+     */
+    public function getPlotVisibleOnly()
+    {
+        return $this->plotVisibleOnly;
+    }
+
+    /**
+     * Set Plot Visible Only.
+     *
+     * @param bool $plotVisibleOnly
+     *
+     * @return $this
+     */
+    public function setPlotVisibleOnly($plotVisibleOnly)
+    {
+        $this->plotVisibleOnly = $plotVisibleOnly;
+
+        return $this;
+    }
+
+    /**
+     * Get Display Blanks as.
+     *
+     * @return string
+     */
+    public function getDisplayBlanksAs()
+    {
+        return $this->displayBlanksAs;
+    }
+
+    /**
+     * Set Display Blanks as.
+     *
+     * @param string $displayBlanksAs
+     *
+     * @return $this
+     */
+    public function setDisplayBlanksAs($displayBlanksAs)
+    {
+        $this->displayBlanksAs = $displayBlanksAs;
+
+        return $this;
+    }
+
+    /**
+     * Get yAxis.
+     *
+     * @return Axis
+     */
+    public function getChartAxisY()
+    {
+        if ($this->yAxis !== null) {
+            return $this->yAxis;
+        }
+
+        return new Axis();
+    }
+
+    /**
+     * Get xAxis.
+     *
+     * @return Axis
+     */
+    public function getChartAxisX()
+    {
+        if ($this->xAxis !== null) {
+            return $this->xAxis;
+        }
+
+        return new Axis();
+    }
+
+    /**
+     * Get Major Gridlines.
+     *
+     * @return GridLines
+     */
+    public function getMajorGridlines()
+    {
+        if ($this->majorGridlines !== null) {
+            return $this->majorGridlines;
+        }
+
+        return new GridLines();
+    }
+
+    /**
+     * Get Minor Gridlines.
+     *
+     * @return GridLines
+     */
+    public function getMinorGridlines()
+    {
+        if ($this->minorGridlines !== null) {
+            return $this->minorGridlines;
+        }
+
+        return new GridLines();
+    }
+
+    /**
+     * Set the Top Left position for the chart.
+     *
+     * @param string $cell
+     * @param int $xOffset
+     * @param int $yOffset
+     *
+     * @return $this
+     */
+    public function setTopLeftPosition($cell, $xOffset = null, $yOffset = null)
+    {
+        $this->topLeftCellRef = $cell;
+        if ($xOffset !== null) {
+            $this->setTopLeftXOffset($xOffset);
+        }
+        if ($yOffset !== null) {
+            $this->setTopLeftYOffset($yOffset);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get the top left position of the chart.
+     *
+     * @return array an associative array containing the cell address, X-Offset and Y-Offset from the top left of that cell
+     */
+    public function getTopLeftPosition()
+    {
+        return [
+            'cell' => $this->topLeftCellRef,
+            'xOffset' => $this->topLeftXOffset,
+            'yOffset' => $this->topLeftYOffset,
+        ];
+    }
+
+    /**
+     * Get the cell address where the top left of the chart is fixed.
+     *
+     * @return string
+     */
+    public function getTopLeftCell()
+    {
+        return $this->topLeftCellRef;
+    }
+
+    /**
+     * Set the Top Left cell position for the chart.
+     *
+     * @param string $cell
+     *
+     * @return $this
+     */
+    public function setTopLeftCell($cell)
+    {
+        $this->topLeftCellRef = $cell;
+
+        return $this;
+    }
+
+    /**
+     * Set the offset position within the Top Left cell for the chart.
+     *
+     * @param int $xOffset
+     * @param int $yOffset
+     *
+     * @return $this
+     */
+    public function setTopLeftOffset($xOffset, $yOffset)
+    {
+        if ($xOffset !== null) {
+            $this->setTopLeftXOffset($xOffset);
+        }
+
+        if ($yOffset !== null) {
+            $this->setTopLeftYOffset($yOffset);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get the offset position within the Top Left cell for the chart.
+     *
+     * @return int[]
+     */
+    public function getTopLeftOffset()
+    {
+        return [
+            'X' => $this->topLeftXOffset,
+            'Y' => $this->topLeftYOffset,
+        ];
+    }
+
+    public function setTopLeftXOffset($xOffset)
+    {
+        $this->topLeftXOffset = $xOffset;
+
+        return $this;
+    }
+
+    public function getTopLeftXOffset()
+    {
+        return $this->topLeftXOffset;
+    }
+
+    public function setTopLeftYOffset($yOffset)
+    {
+        $this->topLeftYOffset = $yOffset;
+
+        return $this;
+    }
+
+    public function getTopLeftYOffset()
+    {
+        return $this->topLeftYOffset;
+    }
+
+    /**
+     * Set the Bottom Right position of the chart.
+     *
+     * @param string $cell
+     * @param int $xOffset
+     * @param int $yOffset
+     *
+     * @return $this
+     */
+    public function setBottomRightPosition($cell, $xOffset = null, $yOffset = null)
+    {
+        $this->bottomRightCellRef = $cell;
+        if ($xOffset !== null) {
+            $this->setBottomRightXOffset($xOffset);
+        }
+        if ($yOffset !== null) {
+            $this->setBottomRightYOffset($yOffset);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get the bottom right position of the chart.
+     *
+     * @return array an associative array containing the cell address, X-Offset and Y-Offset from the top left of that cell
+     */
+    public function getBottomRightPosition()
+    {
+        return [
+            'cell' => $this->bottomRightCellRef,
+            'xOffset' => $this->bottomRightXOffset,
+            'yOffset' => $this->bottomRightYOffset,
+        ];
+    }
+
+    public function setBottomRightCell($cell)
+    {
+        $this->bottomRightCellRef = $cell;
+
+        return $this;
+    }
+
+    /**
+     * Get the cell address where the bottom right of the chart is fixed.
+     *
+     * @return string
+     */
+    public function getBottomRightCell()
+    {
+        return $this->bottomRightCellRef;
+    }
+
+    /**
+     * Set the offset position within the Bottom Right cell for the chart.
+     *
+     * @param int $xOffset
+     * @param int $yOffset
+     *
+     * @return $this
+     */
+    public function setBottomRightOffset($xOffset, $yOffset)
+    {
+        if ($xOffset !== null) {
+            $this->setBottomRightXOffset($xOffset);
+        }
+
+        if ($yOffset !== null) {
+            $this->setBottomRightYOffset($yOffset);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get the offset position within the Bottom Right cell for the chart.
+     *
+     * @return int[]
+     */
+    public function getBottomRightOffset()
+    {
+        return [
+            'X' => $this->bottomRightXOffset,
+            'Y' => $this->bottomRightYOffset,
+        ];
+    }
+
+    public function setBottomRightXOffset($xOffset)
+    {
+        $this->bottomRightXOffset = $xOffset;
+
+        return $this;
+    }
+
+    public function getBottomRightXOffset()
+    {
+        return $this->bottomRightXOffset;
+    }
+
+    public function setBottomRightYOffset($yOffset)
+    {
+        $this->bottomRightYOffset = $yOffset;
+
+        return $this;
+    }
+
+    public function getBottomRightYOffset()
+    {
+        return $this->bottomRightYOffset;
+    }
+
+    public function refresh(): void
+    {
+        if ($this->worksheet !== null) {
+            $this->plotArea->refresh($this->worksheet);
+        }
+    }
+
+    /**
+     * Render the chart to given file (or stream).
+     *
+     * @param string $outputDestination Name of the file render to
+     *
+     * @return bool true on success
+     */
+    public function render($outputDestination = null)
+    {
+        if ($outputDestination == 'php://output') {
+            $outputDestination = null;
+        }
+
+        $libraryName = Settings::getChartRenderer();
+        if ($libraryName === null) {
+            return false;
+        }
+
+        // Ensure that data series values are up-to-date before we render
+        $this->refresh();
+
+        $renderer = new $libraryName($this);
+
+        return $renderer->render($outputDestination);
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeries.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeries.php
new file mode 100755
index 0000000..3a44b33
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeries.php
@@ -0,0 +1,394 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Chart;
+
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+
+class DataSeries
+{
+    const TYPE_BARCHART = 'barChart';
+    const TYPE_BARCHART_3D = 'bar3DChart';
+    const TYPE_LINECHART = 'lineChart';
+    const TYPE_LINECHART_3D = 'line3DChart';
+    const TYPE_AREACHART = 'areaChart';
+    const TYPE_AREACHART_3D = 'area3DChart';
+    const TYPE_PIECHART = 'pieChart';
+    const TYPE_PIECHART_3D = 'pie3DChart';
+    const TYPE_DOUGHNUTCHART = 'doughnutChart';
+    const TYPE_DONUTCHART = self::TYPE_DOUGHNUTCHART; // Synonym
+    const TYPE_SCATTERCHART = 'scatterChart';
+    const TYPE_SURFACECHART = 'surfaceChart';
+    const TYPE_SURFACECHART_3D = 'surface3DChart';
+    const TYPE_RADARCHART = 'radarChart';
+    const TYPE_BUBBLECHART = 'bubbleChart';
+    const TYPE_STOCKCHART = 'stockChart';
+    const TYPE_CANDLECHART = self::TYPE_STOCKCHART; // Synonym
+
+    const GROUPING_CLUSTERED = 'clustered';
+    const GROUPING_STACKED = 'stacked';
+    const GROUPING_PERCENT_STACKED = 'percentStacked';
+    const GROUPING_STANDARD = 'standard';
+
+    const DIRECTION_BAR = 'bar';
+    const DIRECTION_HORIZONTAL = self::DIRECTION_BAR;
+    const DIRECTION_COL = 'col';
+    const DIRECTION_COLUMN = self::DIRECTION_COL;
+    const DIRECTION_VERTICAL = self::DIRECTION_COL;
+
+    const STYLE_LINEMARKER = 'lineMarker';
+    const STYLE_SMOOTHMARKER = 'smoothMarker';
+    const STYLE_MARKER = 'marker';
+    const STYLE_FILLED = 'filled';
+
+    const EMPTY_AS_GAP = 'gap';
+    const EMPTY_AS_ZERO = 'zero';
+    const EMPTY_AS_SPAN = 'span';
+
+    /**
+     * Series Plot Type.
+     *
+     * @var string
+     */
+    private $plotType;
+
+    /**
+     * Plot Grouping Type.
+     *
+     * @var string
+     */
+    private $plotGrouping;
+
+    /**
+     * Plot Direction.
+     *
+     * @var string
+     */
+    private $plotDirection;
+
+    /**
+     * Plot Style.
+     *
+     * @var null|string
+     */
+    private $plotStyle;
+
+    /**
+     * Order of plots in Series.
+     *
+     * @var array of integer
+     */
+    private $plotOrder = [];
+
+    /**
+     * Plot Label.
+     *
+     * @var array of DataSeriesValues
+     */
+    private $plotLabel = [];
+
+    /**
+     * Plot Category.
+     *
+     * @var array of DataSeriesValues
+     */
+    private $plotCategory = [];
+
+    /**
+     * Smooth Line.
+     *
+     * @var bool
+     */
+    private $smoothLine;
+
+    /**
+     * Plot Values.
+     *
+     * @var array of DataSeriesValues
+     */
+    private $plotValues = [];
+
+    /**
+     * Create a new DataSeries.
+     *
+     * @param null|mixed $plotType
+     * @param null|mixed $plotGrouping
+     * @param int[] $plotOrder
+     * @param DataSeriesValues[] $plotLabel
+     * @param DataSeriesValues[] $plotCategory
+     * @param DataSeriesValues[] $plotValues
+     * @param null|string $plotDirection
+     * @param bool $smoothLine
+     * @param null|string $plotStyle
+     */
+    public function __construct($plotType = null, $plotGrouping = null, array $plotOrder = [], array $plotLabel = [], array $plotCategory = [], array $plotValues = [], $plotDirection = null, $smoothLine = false, $plotStyle = null)
+    {
+        $this->plotType = $plotType;
+        $this->plotGrouping = $plotGrouping;
+        $this->plotOrder = $plotOrder;
+        $keys = array_keys($plotValues);
+        $this->plotValues = $plotValues;
+        if ((count($plotLabel) == 0) || ($plotLabel[$keys[0]] === null)) {
+            $plotLabel[$keys[0]] = new DataSeriesValues();
+        }
+        $this->plotLabel = $plotLabel;
+
+        if ((count($plotCategory) == 0) || ($plotCategory[$keys[0]] === null)) {
+            $plotCategory[$keys[0]] = new DataSeriesValues();
+        }
+        $this->plotCategory = $plotCategory;
+
+        $this->smoothLine = $smoothLine;
+        $this->plotStyle = $plotStyle;
+
+        if ($plotDirection === null) {
+            $plotDirection = self::DIRECTION_COL;
+        }
+        $this->plotDirection = $plotDirection;
+    }
+
+    /**
+     * Get Plot Type.
+     *
+     * @return string
+     */
+    public function getPlotType()
+    {
+        return $this->plotType;
+    }
+
+    /**
+     * Set Plot Type.
+     *
+     * @param string $plotType
+     *
+     * @return $this
+     */
+    public function setPlotType($plotType)
+    {
+        $this->plotType = $plotType;
+
+        return $this;
+    }
+
+    /**
+     * Get Plot Grouping Type.
+     *
+     * @return string
+     */
+    public function getPlotGrouping()
+    {
+        return $this->plotGrouping;
+    }
+
+    /**
+     * Set Plot Grouping Type.
+     *
+     * @param string $groupingType
+     *
+     * @return $this
+     */
+    public function setPlotGrouping($groupingType)
+    {
+        $this->plotGrouping = $groupingType;
+
+        return $this;
+    }
+
+    /**
+     * Get Plot Direction.
+     *
+     * @return string
+     */
+    public function getPlotDirection()
+    {
+        return $this->plotDirection;
+    }
+
+    /**
+     * Set Plot Direction.
+     *
+     * @param string $plotDirection
+     *
+     * @return $this
+     */
+    public function setPlotDirection($plotDirection)
+    {
+        $this->plotDirection = $plotDirection;
+
+        return $this;
+    }
+
+    /**
+     * Get Plot Order.
+     *
+     * @return int[]
+     */
+    public function getPlotOrder()
+    {
+        return $this->plotOrder;
+    }
+
+    /**
+     * Get Plot Labels.
+     *
+     * @return array of DataSeriesValues
+     */
+    public function getPlotLabels()
+    {
+        return $this->plotLabel;
+    }
+
+    /**
+     * Get Plot Label by Index.
+     *
+     * @param mixed $index
+     *
+     * @return DataSeriesValues
+     */
+    public function getPlotLabelByIndex($index)
+    {
+        $keys = array_keys($this->plotLabel);
+        if (in_array($index, $keys)) {
+            return $this->plotLabel[$index];
+        } elseif (isset($keys[$index])) {
+            return $this->plotLabel[$keys[$index]];
+        }
+
+        return false;
+    }
+
+    /**
+     * Get Plot Categories.
+     *
+     * @return array of DataSeriesValues
+     */
+    public function getPlotCategories()
+    {
+        return $this->plotCategory;
+    }
+
+    /**
+     * Get Plot Category by Index.
+     *
+     * @param mixed $index
+     *
+     * @return DataSeriesValues
+     */
+    public function getPlotCategoryByIndex($index)
+    {
+        $keys = array_keys($this->plotCategory);
+        if (in_array($index, $keys)) {
+            return $this->plotCategory[$index];
+        } elseif (isset($keys[$index])) {
+            return $this->plotCategory[$keys[$index]];
+        }
+
+        return false;
+    }
+
+    /**
+     * Get Plot Style.
+     *
+     * @return null|string
+     */
+    public function getPlotStyle()
+    {
+        return $this->plotStyle;
+    }
+
+    /**
+     * Set Plot Style.
+     *
+     * @param null|string $plotStyle
+     *
+     * @return $this
+     */
+    public function setPlotStyle($plotStyle)
+    {
+        $this->plotStyle = $plotStyle;
+
+        return $this;
+    }
+
+    /**
+     * Get Plot Values.
+     *
+     * @return array of DataSeriesValues
+     */
+    public function getPlotValues()
+    {
+        return $this->plotValues;
+    }
+
+    /**
+     * Get Plot Values by Index.
+     *
+     * @param mixed $index
+     *
+     * @return DataSeriesValues
+     */
+    public function getPlotValuesByIndex($index)
+    {
+        $keys = array_keys($this->plotValues);
+        if (in_array($index, $keys)) {
+            return $this->plotValues[$index];
+        } elseif (isset($keys[$index])) {
+            return $this->plotValues[$keys[$index]];
+        }
+
+        return false;
+    }
+
+    /**
+     * Get Number of Plot Series.
+     *
+     * @return int
+     */
+    public function getPlotSeriesCount()
+    {
+        return count($this->plotValues);
+    }
+
+    /**
+     * Get Smooth Line.
+     *
+     * @return bool
+     */
+    public function getSmoothLine()
+    {
+        return $this->smoothLine;
+    }
+
+    /**
+     * Set Smooth Line.
+     *
+     * @param bool $smoothLine
+     *
+     * @return $this
+     */
+    public function setSmoothLine($smoothLine)
+    {
+        $this->smoothLine = $smoothLine;
+
+        return $this;
+    }
+
+    public function refresh(Worksheet $worksheet): void
+    {
+        foreach ($this->plotValues as $plotValues) {
+            if ($plotValues !== null) {
+                $plotValues->refresh($worksheet, true);
+            }
+        }
+        foreach ($this->plotLabel as $plotValues) {
+            if ($plotValues !== null) {
+                $plotValues->refresh($worksheet, true);
+            }
+        }
+        foreach ($this->plotCategory as $plotValues) {
+            if ($plotValues !== null) {
+                $plotValues->refresh($worksheet, false);
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeriesValues.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeriesValues.php
new file mode 100755
index 0000000..c1bd973
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeriesValues.php
@@ -0,0 +1,397 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Chart;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
+use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+
+class DataSeriesValues
+{
+    const DATASERIES_TYPE_STRING = 'String';
+    const DATASERIES_TYPE_NUMBER = 'Number';
+
+    private static $dataTypeValues = [
+        self::DATASERIES_TYPE_STRING,
+        self::DATASERIES_TYPE_NUMBER,
+    ];
+
+    /**
+     * Series Data Type.
+     *
+     * @var string
+     */
+    private $dataType;
+
+    /**
+     * Series Data Source.
+     *
+     * @var string
+     */
+    private $dataSource;
+
+    /**
+     * Format Code.
+     *
+     * @var string
+     */
+    private $formatCode;
+
+    /**
+     * Series Point Marker.
+     *
+     * @var string
+     */
+    private $pointMarker;
+
+    /**
+     * Point Count (The number of datapoints in the dataseries).
+     *
+     * @var int
+     */
+    private $pointCount = 0;
+
+    /**
+     * Data Values.
+     *
+     * @var array of mixed
+     */
+    private $dataValues = [];
+
+    /**
+     * Fill color (can be array with colors if dataseries have custom colors).
+     *
+     * @var string|string[]
+     */
+    private $fillColor;
+
+    /**
+     * Line Width.
+     *
+     * @var int
+     */
+    private $lineWidth = 12700;
+
+    /**
+     * Create a new DataSeriesValues object.
+     *
+     * @param string $dataType
+     * @param string $dataSource
+     * @param null|mixed $formatCode
+     * @param int $pointCount
+     * @param mixed $dataValues
+     * @param null|mixed $marker
+     * @param null|string|string[] $fillColor
+     */
+    public function __construct($dataType = self::DATASERIES_TYPE_NUMBER, $dataSource = null, $formatCode = null, $pointCount = 0, $dataValues = [], $marker = null, $fillColor = null)
+    {
+        $this->setDataType($dataType);
+        $this->dataSource = $dataSource;
+        $this->formatCode = $formatCode;
+        $this->pointCount = $pointCount;
+        $this->dataValues = $dataValues;
+        $this->pointMarker = $marker;
+        $this->fillColor = $fillColor;
+    }
+
+    /**
+     * Get Series Data Type.
+     *
+     * @return string
+     */
+    public function getDataType()
+    {
+        return $this->dataType;
+    }
+
+    /**
+     * Set Series Data Type.
+     *
+     * @param string $dataType Datatype of this data series
+     *                                Typical values are:
+     *                                    DataSeriesValues::DATASERIES_TYPE_STRING
+     *                                        Normally used for axis point values
+     *                                    DataSeriesValues::DATASERIES_TYPE_NUMBER
+     *                                        Normally used for chart data values
+     *
+     * @return $this
+     */
+    public function setDataType($dataType)
+    {
+        if (!in_array($dataType, self::$dataTypeValues)) {
+            throw new Exception('Invalid datatype for chart data series values');
+        }
+        $this->dataType = $dataType;
+
+        return $this;
+    }
+
+    /**
+     * Get Series Data Source (formula).
+     *
+     * @return string
+     */
+    public function getDataSource()
+    {
+        return $this->dataSource;
+    }
+
+    /**
+     * Set Series Data Source (formula).
+     *
+     * @param string $dataSource
+     *
+     * @return $this
+     */
+    public function setDataSource($dataSource)
+    {
+        $this->dataSource = $dataSource;
+
+        return $this;
+    }
+
+    /**
+     * Get Point Marker.
+     *
+     * @return string
+     */
+    public function getPointMarker()
+    {
+        return $this->pointMarker;
+    }
+
+    /**
+     * Set Point Marker.
+     *
+     * @param string $marker
+     *
+     * @return $this
+     */
+    public function setPointMarker($marker)
+    {
+        $this->pointMarker = $marker;
+
+        return $this;
+    }
+
+    /**
+     * Get Series Format Code.
+     *
+     * @return string
+     */
+    public function getFormatCode()
+    {
+        return $this->formatCode;
+    }
+
+    /**
+     * Set Series Format Code.
+     *
+     * @param string $formatCode
+     *
+     * @return $this
+     */
+    public function setFormatCode($formatCode)
+    {
+        $this->formatCode = $formatCode;
+
+        return $this;
+    }
+
+    /**
+     * Get Series Point Count.
+     *
+     * @return int
+     */
+    public function getPointCount()
+    {
+        return $this->pointCount;
+    }
+
+    /**
+     * Get fill color.
+     *
+     * @return string|string[] HEX color or array with HEX colors
+     */
+    public function getFillColor()
+    {
+        return $this->fillColor;
+    }
+
+    /**
+     * Set fill color for series.
+     *
+     * @param string|string[] $color HEX color or array with HEX colors
+     *
+     * @return   DataSeriesValues
+     */
+    public function setFillColor($color)
+    {
+        if (is_array($color)) {
+            foreach ($color as $colorValue) {
+                $this->validateColor($colorValue);
+            }
+        } else {
+            $this->validateColor($color);
+        }
+        $this->fillColor = $color;
+
+        return $this;
+    }
+
+    /**
+     * Method for validating hex color.
+     *
+     * @param string $color value for color
+     *
+     * @return bool true if validation was successful
+     */
+    private function validateColor($color)
+    {
+        if (!preg_match('/^[a-f0-9]{6}$/i', $color)) {
+            throw new Exception(sprintf('Invalid hex color for chart series (color: "%s")', $color));
+        }
+
+        return true;
+    }
+
+    /**
+     * Get line width for series.
+     *
+     * @return int
+     */
+    public function getLineWidth()
+    {
+        return $this->lineWidth;
+    }
+
+    /**
+     * Set line width for the series.
+     *
+     * @param int $width
+     *
+     * @return $this
+     */
+    public function setLineWidth($width)
+    {
+        $minWidth = 12700;
+        $this->lineWidth = max($minWidth, $width);
+
+        return $this;
+    }
+
+    /**
+     * Identify if the Data Series is a multi-level or a simple series.
+     *
+     * @return null|bool
+     */
+    public function isMultiLevelSeries()
+    {
+        if (count($this->dataValues) > 0) {
+            return is_array(array_values($this->dataValues)[0]);
+        }
+
+        return null;
+    }
+
+    /**
+     * Return the level count of a multi-level Data Series.
+     *
+     * @return int
+     */
+    public function multiLevelCount()
+    {
+        $levelCount = 0;
+        foreach ($this->dataValues as $dataValueSet) {
+            $levelCount = max($levelCount, count($dataValueSet));
+        }
+
+        return $levelCount;
+    }
+
+    /**
+     * Get Series Data Values.
+     *
+     * @return array of mixed
+     */
+    public function getDataValues()
+    {
+        return $this->dataValues;
+    }
+
+    /**
+     * Get the first Series Data value.
+     *
+     * @return mixed
+     */
+    public function getDataValue()
+    {
+        $count = count($this->dataValues);
+        if ($count == 0) {
+            return null;
+        } elseif ($count == 1) {
+            return $this->dataValues[0];
+        }
+
+        return $this->dataValues;
+    }
+
+    /**
+     * Set Series Data Values.
+     *
+     * @param array $dataValues
+     *
+     * @return $this
+     */
+    public function setDataValues($dataValues)
+    {
+        $this->dataValues = Functions::flattenArray($dataValues);
+        $this->pointCount = count($dataValues);
+
+        return $this;
+    }
+
+    public function refresh(Worksheet $worksheet, $flatten = true): void
+    {
+        if ($this->dataSource !== null) {
+            $calcEngine = Calculation::getInstance($worksheet->getParent());
+            $newDataValues = Calculation::unwrapResult(
+                $calcEngine->_calculateFormulaValue(
+                    '=' . $this->dataSource,
+                    null,
+                    $worksheet->getCell('A1')
+                )
+            );
+            if ($flatten) {
+                $this->dataValues = Functions::flattenArray($newDataValues);
+                foreach ($this->dataValues as &$dataValue) {
+                    if (is_string($dataValue) && !empty($dataValue) && $dataValue[0] == '#') {
+                        $dataValue = 0.0;
+                    }
+                }
+                unset($dataValue);
+            } else {
+                [$worksheet, $cellRange] = Worksheet::extractSheetTitle($this->dataSource, true);
+                $dimensions = Coordinate::rangeDimension(str_replace('$', '', $cellRange));
+                if (($dimensions[0] == 1) || ($dimensions[1] == 1)) {
+                    $this->dataValues = Functions::flattenArray($newDataValues);
+                } else {
+                    $newArray = array_values(array_shift($newDataValues));
+                    foreach ($newArray as $i => $newDataSet) {
+                        $newArray[$i] = [$newDataSet];
+                    }
+
+                    foreach ($newDataValues as $newDataSet) {
+                        $i = 0;
+                        foreach ($newDataSet as $newDataVal) {
+                            array_unshift($newArray[$i++], $newDataVal);
+                        }
+                    }
+                    $this->dataValues = $newArray;
+                }
+            }
+            $this->pointCount = count($this->dataValues);
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Exception.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Exception.php
new file mode 100755
index 0000000..3f95b59
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Exception.php
@@ -0,0 +1,9 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Chart;
+
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+
+class Exception extends PhpSpreadsheetException
+{
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/GridLines.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/GridLines.php
new file mode 100755
index 0000000..2e424bc
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/GridLines.php
@@ -0,0 +1,454 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Chart;
+
+/**
+ * Created by PhpStorm.
+ * User: Wiktor Trzonkowski
+ * Date: 7/2/14
+ * Time: 2:36 PM.
+ */
+class GridLines extends Properties
+{
+    /**
+     * Properties of Class:
+     * Object State (State for Minor Tick Mark) @var bool
+     * Line Properties @var  array of mixed
+     * Shadow Properties @var  array of mixed
+     * Glow Properties @var  array of mixed
+     * Soft Properties @var  array of mixed.
+     */
+    private $objectState = false;
+
+    private $lineProperties = [
+        'color' => [
+            'type' => self::EXCEL_COLOR_TYPE_STANDARD,
+            'value' => null,
+            'alpha' => 0,
+        ],
+        'style' => [
+            'width' => '9525',
+            'compound' => self::LINE_STYLE_COMPOUND_SIMPLE,
+            'dash' => self::LINE_STYLE_DASH_SOLID,
+            'cap' => self::LINE_STYLE_CAP_FLAT,
+            'join' => self::LINE_STYLE_JOIN_BEVEL,
+            'arrow' => [
+                'head' => [
+                    'type' => self::LINE_STYLE_ARROW_TYPE_NOARROW,
+                    'size' => self::LINE_STYLE_ARROW_SIZE_5,
+                ],
+                'end' => [
+                    'type' => self::LINE_STYLE_ARROW_TYPE_NOARROW,
+                    'size' => self::LINE_STYLE_ARROW_SIZE_8,
+                ],
+            ],
+        ],
+    ];
+
+    private $shadowProperties = [
+        'presets' => self::SHADOW_PRESETS_NOSHADOW,
+        'effect' => null,
+        'color' => [
+            'type' => self::EXCEL_COLOR_TYPE_STANDARD,
+            'value' => 'black',
+            'alpha' => 85,
+        ],
+        'size' => [
+            'sx' => null,
+            'sy' => null,
+            'kx' => null,
+        ],
+        'blur' => null,
+        'direction' => null,
+        'distance' => null,
+        'algn' => null,
+        'rotWithShape' => null,
+    ];
+
+    private $glowProperties = [
+        'size' => null,
+        'color' => [
+            'type' => self::EXCEL_COLOR_TYPE_STANDARD,
+            'value' => 'black',
+            'alpha' => 40,
+        ],
+    ];
+
+    private $softEdges = [
+        'size' => null,
+    ];
+
+    /**
+     * Get Object State.
+     *
+     * @return bool
+     */
+    public function getObjectState()
+    {
+        return $this->objectState;
+    }
+
+    /**
+     * Change Object State to True.
+     *
+     * @return $this
+     */
+    private function activateObject()
+    {
+        $this->objectState = true;
+
+        return $this;
+    }
+
+    /**
+     * Set Line Color Properties.
+     *
+     * @param string $value
+     * @param int $alpha
+     * @param string $type
+     */
+    public function setLineColorProperties($value, $alpha = 0, $type = self::EXCEL_COLOR_TYPE_STANDARD): void
+    {
+        $this->activateObject()
+            ->lineProperties['color'] = $this->setColorProperties(
+                $value,
+                $alpha,
+                $type
+            );
+    }
+
+    /**
+     * Set Line Color Properties.
+     *
+     * @param float $line_width
+     * @param string $compound_type
+     * @param string $dash_type
+     * @param string $cap_type
+     * @param string $join_type
+     * @param string $head_arrow_type
+     * @param string $head_arrow_size
+     * @param string $end_arrow_type
+     * @param string $end_arrow_size
+     */
+    public function setLineStyleProperties($line_width = null, $compound_type = null, $dash_type = null, $cap_type = null, $join_type = null, $head_arrow_type = null, $head_arrow_size = null, $end_arrow_type = null, $end_arrow_size = null): void
+    {
+        $this->activateObject();
+        ($line_width !== null)
+                ? $this->lineProperties['style']['width'] = $this->getExcelPointsWidth((float) $line_width)
+                : null;
+        ($compound_type !== null)
+                ? $this->lineProperties['style']['compound'] = (string) $compound_type
+                : null;
+        ($dash_type !== null)
+                ? $this->lineProperties['style']['dash'] = (string) $dash_type
+                : null;
+        ($cap_type !== null)
+                ? $this->lineProperties['style']['cap'] = (string) $cap_type
+                : null;
+        ($join_type !== null)
+                ? $this->lineProperties['style']['join'] = (string) $join_type
+                : null;
+        ($head_arrow_type !== null)
+                ? $this->lineProperties['style']['arrow']['head']['type'] = (string) $head_arrow_type
+                : null;
+        ($head_arrow_size !== null)
+                ? $this->lineProperties['style']['arrow']['head']['size'] = (string) $head_arrow_size
+                : null;
+        ($end_arrow_type !== null)
+                ? $this->lineProperties['style']['arrow']['end']['type'] = (string) $end_arrow_type
+                : null;
+        ($end_arrow_size !== null)
+                ? $this->lineProperties['style']['arrow']['end']['size'] = (string) $end_arrow_size
+                : null;
+    }
+
+    /**
+     * Get Line Color Property.
+     *
+     * @param string $parameter
+     *
+     * @return string
+     */
+    public function getLineColorProperty($parameter)
+    {
+        return $this->lineProperties['color'][$parameter];
+    }
+
+    /**
+     * Get Line Style Property.
+     *
+     * @param array|string $elements
+     *
+     * @return string
+     */
+    public function getLineStyleProperty($elements)
+    {
+        return $this->getArrayElementsValue($this->lineProperties['style'], $elements);
+    }
+
+    /**
+     * Set Glow Properties.
+     *
+     * @param float $size
+     * @param string $color_value
+     * @param int $color_alpha
+     * @param string $color_type
+     */
+    public function setGlowProperties($size, $color_value = null, $color_alpha = null, $color_type = null): void
+    {
+        $this
+            ->activateObject()
+            ->setGlowSize($size)
+            ->setGlowColor($color_value, $color_alpha, $color_type);
+    }
+
+    /**
+     * Get Glow Color Property.
+     *
+     * @param string $property
+     *
+     * @return string
+     */
+    public function getGlowColor($property)
+    {
+        return $this->glowProperties['color'][$property];
+    }
+
+    /**
+     * Get Glow Size.
+     *
+     * @return string
+     */
+    public function getGlowSize()
+    {
+        return $this->glowProperties['size'];
+    }
+
+    /**
+     * Set Glow Size.
+     *
+     * @param float $size
+     *
+     * @return $this
+     */
+    private function setGlowSize($size)
+    {
+        $this->glowProperties['size'] = $this->getExcelPointsWidth((float) $size);
+
+        return $this;
+    }
+
+    /**
+     * Set Glow Color.
+     *
+     * @param string $color
+     * @param int $alpha
+     * @param string $type
+     *
+     * @return $this
+     */
+    private function setGlowColor($color, $alpha, $type)
+    {
+        if ($color !== null) {
+            $this->glowProperties['color']['value'] = (string) $color;
+        }
+        if ($alpha !== null) {
+            $this->glowProperties['color']['alpha'] = $this->getTrueAlpha((int) $alpha);
+        }
+        if ($type !== null) {
+            $this->glowProperties['color']['type'] = (string) $type;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Line Style Arrow Parameters.
+     *
+     * @param string $arrow_selector
+     * @param string $property_selector
+     *
+     * @return string
+     */
+    public function getLineStyleArrowParameters($arrow_selector, $property_selector)
+    {
+        return $this->getLineStyleArrowSize($this->lineProperties['style']['arrow'][$arrow_selector]['size'], $property_selector);
+    }
+
+    /**
+     * Set Shadow Properties.
+     *
+     * @param int $sh_presets
+     * @param string $sh_color_value
+     * @param string $sh_color_type
+     * @param int $sh_color_alpha
+     * @param string $sh_blur
+     * @param int $sh_angle
+     * @param float $sh_distance
+     */
+    public function setShadowProperties($sh_presets, $sh_color_value = null, $sh_color_type = null, $sh_color_alpha = null, $sh_blur = null, $sh_angle = null, $sh_distance = null): void
+    {
+        $this->activateObject()
+            ->setShadowPresetsProperties((int) $sh_presets)
+            ->setShadowColor(
+                $sh_color_value === null ? $this->shadowProperties['color']['value'] : $sh_color_value,
+                $sh_color_alpha === null ? (int) $this->shadowProperties['color']['alpha'] : $this->getTrueAlpha($sh_color_alpha),
+                $sh_color_type === null ? $this->shadowProperties['color']['type'] : $sh_color_type
+            )
+            ->setShadowBlur($sh_blur)
+            ->setShadowAngle($sh_angle)
+            ->setShadowDistance($sh_distance);
+    }
+
+    /**
+     * Set Shadow Presets Properties.
+     *
+     * @param int $shadow_presets
+     *
+     * @return $this
+     */
+    private function setShadowPresetsProperties($shadow_presets)
+    {
+        $this->shadowProperties['presets'] = $shadow_presets;
+        $this->setShadowProperiesMapValues($this->getShadowPresetsMap($shadow_presets));
+
+        return $this;
+    }
+
+    /**
+     * Set Shadow Properties Values.
+     *
+     * @param mixed &$reference
+     *
+     * @return $this
+     */
+    private function setShadowProperiesMapValues(array $properties_map, &$reference = null)
+    {
+        $base_reference = $reference;
+        foreach ($properties_map as $property_key => $property_val) {
+            if (is_array($property_val)) {
+                if ($reference === null) {
+                    $reference = &$this->shadowProperties[$property_key];
+                } else {
+                    $reference = &$reference[$property_key];
+                }
+                $this->setShadowProperiesMapValues($property_val, $reference);
+            } else {
+                if ($base_reference === null) {
+                    $this->shadowProperties[$property_key] = $property_val;
+                } else {
+                    $reference[$property_key] = $property_val;
+                }
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Set Shadow Color.
+     *
+     * @param string $color
+     * @param int $alpha
+     * @param string $type
+     *
+     * @return $this
+     */
+    private function setShadowColor($color, $alpha, $type)
+    {
+        if ($color !== null) {
+            $this->shadowProperties['color']['value'] = (string) $color;
+        }
+        if ($alpha !== null) {
+            $this->shadowProperties['color']['alpha'] = $this->getTrueAlpha((int) $alpha);
+        }
+        if ($type !== null) {
+            $this->shadowProperties['color']['type'] = (string) $type;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Set Shadow Blur.
+     *
+     * @param float $blur
+     *
+     * @return $this
+     */
+    private function setShadowBlur($blur)
+    {
+        if ($blur !== null) {
+            $this->shadowProperties['blur'] = (string) $this->getExcelPointsWidth($blur);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Set Shadow Angle.
+     *
+     * @param int $angle
+     *
+     * @return $this
+     */
+    private function setShadowAngle($angle)
+    {
+        if ($angle !== null) {
+            $this->shadowProperties['direction'] = (string) $this->getExcelPointsAngle($angle);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Set Shadow Distance.
+     *
+     * @param float $distance
+     *
+     * @return $this
+     */
+    private function setShadowDistance($distance)
+    {
+        if ($distance !== null) {
+            $this->shadowProperties['distance'] = (string) $this->getExcelPointsWidth($distance);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Shadow Property.
+     *
+     * @param string|string[] $elements
+     *
+     * @return string
+     */
+    public function getShadowProperty($elements)
+    {
+        return $this->getArrayElementsValue($this->shadowProperties, $elements);
+    }
+
+    /**
+     * Set Soft Edges Size.
+     *
+     * @param float $size
+     */
+    public function setSoftEdgesSize($size): void
+    {
+        if ($size !== null) {
+            $this->activateObject();
+            $this->softEdges['size'] = (string) $this->getExcelPointsWidth($size);
+        }
+    }
+
+    /**
+     * Get Soft Edges Size.
+     *
+     * @return string
+     */
+    public function getSoftEdgesSize()
+    {
+        return $this->softEdges['size'];
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Layout.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Layout.php
new file mode 100755
index 0000000..51c8995
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Layout.php
@@ -0,0 +1,481 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Chart;
+
+class Layout
+{
+    /**
+     * layoutTarget.
+     *
+     * @var string
+     */
+    private $layoutTarget;
+
+    /**
+     * X Mode.
+     *
+     * @var string
+     */
+    private $xMode;
+
+    /**
+     * Y Mode.
+     *
+     * @var string
+     */
+    private $yMode;
+
+    /**
+     * X-Position.
+     *
+     * @var float
+     */
+    private $xPos;
+
+    /**
+     * Y-Position.
+     *
+     * @var float
+     */
+    private $yPos;
+
+    /**
+     * width.
+     *
+     * @var float
+     */
+    private $width;
+
+    /**
+     * height.
+     *
+     * @var float
+     */
+    private $height;
+
+    /**
+     * show legend key
+     * Specifies that legend keys should be shown in data labels.
+     *
+     * @var bool
+     */
+    private $showLegendKey;
+
+    /**
+     * show value
+     * Specifies that the value should be shown in a data label.
+     *
+     * @var bool
+     */
+    private $showVal;
+
+    /**
+     * show category name
+     * Specifies that the category name should be shown in the data label.
+     *
+     * @var bool
+     */
+    private $showCatName;
+
+    /**
+     * show data series name
+     * Specifies that the series name should be shown in the data label.
+     *
+     * @var bool
+     */
+    private $showSerName;
+
+    /**
+     * show percentage
+     * Specifies that the percentage should be shown in the data label.
+     *
+     * @var bool
+     */
+    private $showPercent;
+
+    /**
+     * show bubble size.
+     *
+     * @var bool
+     */
+    private $showBubbleSize;
+
+    /**
+     * show leader lines
+     * Specifies that leader lines should be shown for the data label.
+     *
+     * @var bool
+     */
+    private $showLeaderLines;
+
+    /**
+     * Create a new Layout.
+     */
+    public function __construct(array $layout = [])
+    {
+        if (isset($layout['layoutTarget'])) {
+            $this->layoutTarget = $layout['layoutTarget'];
+        }
+        if (isset($layout['xMode'])) {
+            $this->xMode = $layout['xMode'];
+        }
+        if (isset($layout['yMode'])) {
+            $this->yMode = $layout['yMode'];
+        }
+        if (isset($layout['x'])) {
+            $this->xPos = (float) $layout['x'];
+        }
+        if (isset($layout['y'])) {
+            $this->yPos = (float) $layout['y'];
+        }
+        if (isset($layout['w'])) {
+            $this->width = (float) $layout['w'];
+        }
+        if (isset($layout['h'])) {
+            $this->height = (float) $layout['h'];
+        }
+    }
+
+    /**
+     * Get Layout Target.
+     *
+     * @return string
+     */
+    public function getLayoutTarget()
+    {
+        return $this->layoutTarget;
+    }
+
+    /**
+     * Set Layout Target.
+     *
+     * @param string $value
+     *
+     * @return $this
+     */
+    public function setLayoutTarget($value)
+    {
+        $this->layoutTarget = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get X-Mode.
+     *
+     * @return string
+     */
+    public function getXMode()
+    {
+        return $this->xMode;
+    }
+
+    /**
+     * Set X-Mode.
+     *
+     * @param string $value
+     *
+     * @return $this
+     */
+    public function setXMode($value)
+    {
+        $this->xMode = (string) $value;
+
+        return $this;
+    }
+
+    /**
+     * Get Y-Mode.
+     *
+     * @return string
+     */
+    public function getYMode()
+    {
+        return $this->yMode;
+    }
+
+    /**
+     * Set Y-Mode.
+     *
+     * @param string $value
+     *
+     * @return $this
+     */
+    public function setYMode($value)
+    {
+        $this->yMode = (string) $value;
+
+        return $this;
+    }
+
+    /**
+     * Get X-Position.
+     *
+     * @return number
+     */
+    public function getXPosition()
+    {
+        return $this->xPos;
+    }
+
+    /**
+     * Set X-Position.
+     *
+     * @param float $value
+     *
+     * @return $this
+     */
+    public function setXPosition($value)
+    {
+        $this->xPos = (float) $value;
+
+        return $this;
+    }
+
+    /**
+     * Get Y-Position.
+     *
+     * @return number
+     */
+    public function getYPosition()
+    {
+        return $this->yPos;
+    }
+
+    /**
+     * Set Y-Position.
+     *
+     * @param float $value
+     *
+     * @return $this
+     */
+    public function setYPosition($value)
+    {
+        $this->yPos = (float) $value;
+
+        return $this;
+    }
+
+    /**
+     * Get Width.
+     *
+     * @return number
+     */
+    public function getWidth()
+    {
+        return $this->width;
+    }
+
+    /**
+     * Set Width.
+     *
+     * @param float $value
+     *
+     * @return $this
+     */
+    public function setWidth($value)
+    {
+        $this->width = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get Height.
+     *
+     * @return number
+     */
+    public function getHeight()
+    {
+        return $this->height;
+    }
+
+    /**
+     * Set Height.
+     *
+     * @param float $value
+     *
+     * @return $this
+     */
+    public function setHeight($value)
+    {
+        $this->height = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get show legend key.
+     *
+     * @return bool
+     */
+    public function getShowLegendKey()
+    {
+        return $this->showLegendKey;
+    }
+
+    /**
+     * Set show legend key
+     * Specifies that legend keys should be shown in data labels.
+     *
+     * @param bool $value Show legend key
+     *
+     * @return $this
+     */
+    public function setShowLegendKey($value)
+    {
+        $this->showLegendKey = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get show value.
+     *
+     * @return bool
+     */
+    public function getShowVal()
+    {
+        return $this->showVal;
+    }
+
+    /**
+     * Set show val
+     * Specifies that the value should be shown in data labels.
+     *
+     * @param bool $value Show val
+     *
+     * @return $this
+     */
+    public function setShowVal($value)
+    {
+        $this->showVal = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get show category name.
+     *
+     * @return bool
+     */
+    public function getShowCatName()
+    {
+        return $this->showCatName;
+    }
+
+    /**
+     * Set show cat name
+     * Specifies that the category name should be shown in data labels.
+     *
+     * @param bool $value Show cat name
+     *
+     * @return $this
+     */
+    public function setShowCatName($value)
+    {
+        $this->showCatName = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get show data series name.
+     *
+     * @return bool
+     */
+    public function getShowSerName()
+    {
+        return $this->showSerName;
+    }
+
+    /**
+     * Set show ser name
+     * Specifies that the series name should be shown in data labels.
+     *
+     * @param bool $value Show series name
+     *
+     * @return $this
+     */
+    public function setShowSerName($value)
+    {
+        $this->showSerName = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get show percentage.
+     *
+     * @return bool
+     */
+    public function getShowPercent()
+    {
+        return $this->showPercent;
+    }
+
+    /**
+     * Set show percentage
+     * Specifies that the percentage should be shown in data labels.
+     *
+     * @param bool $value Show percentage
+     *
+     * @return $this
+     */
+    public function setShowPercent($value)
+    {
+        $this->showPercent = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get show bubble size.
+     *
+     * @return bool
+     */
+    public function getShowBubbleSize()
+    {
+        return $this->showBubbleSize;
+    }
+
+    /**
+     * Set show bubble size
+     * Specifies that the bubble size should be shown in data labels.
+     *
+     * @param bool $value Show bubble size
+     *
+     * @return $this
+     */
+    public function setShowBubbleSize($value)
+    {
+        $this->showBubbleSize = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get show leader lines.
+     *
+     * @return bool
+     */
+    public function getShowLeaderLines()
+    {
+        return $this->showLeaderLines;
+    }
+
+    /**
+     * Set show leader lines
+     * Specifies that leader lines should be shown in data labels.
+     *
+     * @param bool $value Show leader lines
+     *
+     * @return $this
+     */
+    public function setShowLeaderLines($value)
+    {
+        $this->showLeaderLines = $value;
+
+        return $this;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Legend.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Legend.php
new file mode 100755
index 0000000..fc0ed14
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Legend.php
@@ -0,0 +1,157 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Chart;
+
+class Legend
+{
+    /** Legend positions */
+    const XL_LEGEND_POSITION_BOTTOM = -4107; //    Below the chart.
+    const XL_LEGEND_POSITION_CORNER = 2; //    In the upper right-hand corner of the chart border.
+    const XL_LEGEND_POSITION_CUSTOM = -4161; //    A custom position.
+    const XL_LEGEND_POSITION_LEFT = -4131; //    Left of the chart.
+    const XL_LEGEND_POSITION_RIGHT = -4152; //    Right of the chart.
+    const XL_LEGEND_POSITION_TOP = -4160; //    Above the chart.
+
+    const POSITION_RIGHT = 'r';
+    const POSITION_LEFT = 'l';
+    const POSITION_BOTTOM = 'b';
+    const POSITION_TOP = 't';
+    const POSITION_TOPRIGHT = 'tr';
+
+    private static $positionXLref = [
+        self::XL_LEGEND_POSITION_BOTTOM => self::POSITION_BOTTOM,
+        self::XL_LEGEND_POSITION_CORNER => self::POSITION_TOPRIGHT,
+        self::XL_LEGEND_POSITION_CUSTOM => '??',
+        self::XL_LEGEND_POSITION_LEFT => self::POSITION_LEFT,
+        self::XL_LEGEND_POSITION_RIGHT => self::POSITION_RIGHT,
+        self::XL_LEGEND_POSITION_TOP => self::POSITION_TOP,
+    ];
+
+    /**
+     * Legend position.
+     *
+     * @var string
+     */
+    private $position = self::POSITION_RIGHT;
+
+    /**
+     * Allow overlay of other elements?
+     *
+     * @var bool
+     */
+    private $overlay = true;
+
+    /**
+     * Legend Layout.
+     *
+     * @var Layout
+     */
+    private $layout;
+
+    /**
+     * Create a new Legend.
+     *
+     * @param string $position
+     * @param bool $overlay
+     */
+    public function __construct($position = self::POSITION_RIGHT, ?Layout $layout = null, $overlay = false)
+    {
+        $this->setPosition($position);
+        $this->layout = $layout;
+        $this->setOverlay($overlay);
+    }
+
+    /**
+     * Get legend position as an excel string value.
+     *
+     * @return string
+     */
+    public function getPosition()
+    {
+        return $this->position;
+    }
+
+    /**
+     * Get legend position using an excel string value.
+     *
+     * @param string $position see self::POSITION_*
+     *
+     * @return bool
+     */
+    public function setPosition($position)
+    {
+        if (!in_array($position, self::$positionXLref)) {
+            return false;
+        }
+
+        $this->position = $position;
+
+        return true;
+    }
+
+    /**
+     * Get legend position as an Excel internal numeric value.
+     *
+     * @return int
+     */
+    public function getPositionXL()
+    {
+        return array_search($this->position, self::$positionXLref);
+    }
+
+    /**
+     * Set legend position using an Excel internal numeric value.
+     *
+     * @param int $positionXL see self::XL_LEGEND_POSITION_*
+     *
+     * @return bool
+     */
+    public function setPositionXL($positionXL)
+    {
+        if (!isset(self::$positionXLref[$positionXL])) {
+            return false;
+        }
+
+        $this->position = self::$positionXLref[$positionXL];
+
+        return true;
+    }
+
+    /**
+     * Get allow overlay of other elements?
+     *
+     * @return bool
+     */
+    public function getOverlay()
+    {
+        return $this->overlay;
+    }
+
+    /**
+     * Set allow overlay of other elements?
+     *
+     * @param bool $overlay
+     *
+     * @return bool
+     */
+    public function setOverlay($overlay)
+    {
+        if (!is_bool($overlay)) {
+            return false;
+        }
+
+        $this->overlay = $overlay;
+
+        return true;
+    }
+
+    /**
+     * Get Layout.
+     *
+     * @return Layout
+     */
+    public function getLayout()
+    {
+        return $this->layout;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/PlotArea.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/PlotArea.php
new file mode 100755
index 0000000..954777c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/PlotArea.php
@@ -0,0 +1,111 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Chart;
+
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+
+class PlotArea
+{
+    /**
+     * PlotArea Layout.
+     *
+     * @var Layout
+     */
+    private $layout;
+
+    /**
+     * Plot Series.
+     *
+     * @var DataSeries[]
+     */
+    private $plotSeries = [];
+
+    /**
+     * Create a new PlotArea.
+     *
+     * @param DataSeries[] $plotSeries
+     */
+    public function __construct(?Layout $layout = null, array $plotSeries = [])
+    {
+        $this->layout = $layout;
+        $this->plotSeries = $plotSeries;
+    }
+
+    /**
+     * Get Layout.
+     *
+     * @return Layout
+     */
+    public function getLayout()
+    {
+        return $this->layout;
+    }
+
+    /**
+     * Get Number of Plot Groups.
+     *
+     * @return array of DataSeries
+     */
+    public function getPlotGroupCount()
+    {
+        return count($this->plotSeries);
+    }
+
+    /**
+     * Get Number of Plot Series.
+     *
+     * @return int
+     */
+    public function getPlotSeriesCount()
+    {
+        $seriesCount = 0;
+        foreach ($this->plotSeries as $plot) {
+            $seriesCount += $plot->getPlotSeriesCount();
+        }
+
+        return $seriesCount;
+    }
+
+    /**
+     * Get Plot Series.
+     *
+     * @return array of DataSeries
+     */
+    public function getPlotGroup()
+    {
+        return $this->plotSeries;
+    }
+
+    /**
+     * Get Plot Series by Index.
+     *
+     * @param mixed $index
+     *
+     * @return DataSeries
+     */
+    public function getPlotGroupByIndex($index)
+    {
+        return $this->plotSeries[$index];
+    }
+
+    /**
+     * Set Plot Series.
+     *
+     * @param DataSeries[] $plotSeries
+     *
+     * @return $this
+     */
+    public function setPlotSeries(array $plotSeries)
+    {
+        $this->plotSeries = $plotSeries;
+
+        return $this;
+    }
+
+    public function refresh(Worksheet $worksheet): void
+    {
+        foreach ($this->plotSeries as $plotSeries) {
+            $plotSeries->refresh($worksheet);
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Properties.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Properties.php
new file mode 100755
index 0000000..98095f0
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Properties.php
@@ -0,0 +1,369 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Chart;
+
+/**
+ * Created by PhpStorm.
+ * User: nhw2h8s
+ * Date: 7/2/14
+ * Time: 5:45 PM.
+ */
+abstract class Properties
+{
+    const
+        EXCEL_COLOR_TYPE_STANDARD = 'prstClr';
+    const EXCEL_COLOR_TYPE_SCHEME = 'schemeClr';
+    const EXCEL_COLOR_TYPE_ARGB = 'srgbClr';
+
+    const
+        AXIS_LABELS_LOW = 'low';
+    const AXIS_LABELS_HIGH = 'high';
+    const AXIS_LABELS_NEXT_TO = 'nextTo';
+    const AXIS_LABELS_NONE = 'none';
+
+    const
+        TICK_MARK_NONE = 'none';
+    const TICK_MARK_INSIDE = 'in';
+    const TICK_MARK_OUTSIDE = 'out';
+    const TICK_MARK_CROSS = 'cross';
+
+    const
+        HORIZONTAL_CROSSES_AUTOZERO = 'autoZero';
+    const HORIZONTAL_CROSSES_MAXIMUM = 'max';
+
+    const
+        FORMAT_CODE_GENERAL = 'General';
+    const FORMAT_CODE_NUMBER = '#,##0.00';
+    const FORMAT_CODE_CURRENCY = '$#,##0.00';
+    const FORMAT_CODE_ACCOUNTING = '_($* #,##0.00_);_($* (#,##0.00);_($* "-"??_);_(@_)';
+    const FORMAT_CODE_DATE = 'm/d/yyyy';
+    const FORMAT_CODE_TIME = '[$-F400]h:mm:ss AM/PM';
+    const FORMAT_CODE_PERCENTAGE = '0.00%';
+    const FORMAT_CODE_FRACTION = '# ?/?';
+    const FORMAT_CODE_SCIENTIFIC = '0.00E+00';
+    const FORMAT_CODE_TEXT = '@';
+    const FORMAT_CODE_SPECIAL = '00000';
+
+    const
+        ORIENTATION_NORMAL = 'minMax';
+    const ORIENTATION_REVERSED = 'maxMin';
+
+    const
+        LINE_STYLE_COMPOUND_SIMPLE = 'sng';
+    const LINE_STYLE_COMPOUND_DOUBLE = 'dbl';
+    const LINE_STYLE_COMPOUND_THICKTHIN = 'thickThin';
+    const LINE_STYLE_COMPOUND_THINTHICK = 'thinThick';
+    const LINE_STYLE_COMPOUND_TRIPLE = 'tri';
+    const LINE_STYLE_DASH_SOLID = 'solid';
+    const LINE_STYLE_DASH_ROUND_DOT = 'sysDot';
+    const LINE_STYLE_DASH_SQUERE_DOT = 'sysDash';
+    const LINE_STYPE_DASH_DASH = 'dash';
+    const LINE_STYLE_DASH_DASH_DOT = 'dashDot';
+    const LINE_STYLE_DASH_LONG_DASH = 'lgDash';
+    const LINE_STYLE_DASH_LONG_DASH_DOT = 'lgDashDot';
+    const LINE_STYLE_DASH_LONG_DASH_DOT_DOT = 'lgDashDotDot';
+    const LINE_STYLE_CAP_SQUARE = 'sq';
+    const LINE_STYLE_CAP_ROUND = 'rnd';
+    const LINE_STYLE_CAP_FLAT = 'flat';
+    const LINE_STYLE_JOIN_ROUND = 'bevel';
+    const LINE_STYLE_JOIN_MITER = 'miter';
+    const LINE_STYLE_JOIN_BEVEL = 'bevel';
+    const LINE_STYLE_ARROW_TYPE_NOARROW = null;
+    const LINE_STYLE_ARROW_TYPE_ARROW = 'triangle';
+    const LINE_STYLE_ARROW_TYPE_OPEN = 'arrow';
+    const LINE_STYLE_ARROW_TYPE_STEALTH = 'stealth';
+    const LINE_STYLE_ARROW_TYPE_DIAMOND = 'diamond';
+    const LINE_STYLE_ARROW_TYPE_OVAL = 'oval';
+    const LINE_STYLE_ARROW_SIZE_1 = 1;
+    const LINE_STYLE_ARROW_SIZE_2 = 2;
+    const LINE_STYLE_ARROW_SIZE_3 = 3;
+    const LINE_STYLE_ARROW_SIZE_4 = 4;
+    const LINE_STYLE_ARROW_SIZE_5 = 5;
+    const LINE_STYLE_ARROW_SIZE_6 = 6;
+    const LINE_STYLE_ARROW_SIZE_7 = 7;
+    const LINE_STYLE_ARROW_SIZE_8 = 8;
+    const LINE_STYLE_ARROW_SIZE_9 = 9;
+
+    const
+        SHADOW_PRESETS_NOSHADOW = null;
+    const SHADOW_PRESETS_OUTER_BOTTTOM_RIGHT = 1;
+    const SHADOW_PRESETS_OUTER_BOTTOM = 2;
+    const SHADOW_PRESETS_OUTER_BOTTOM_LEFT = 3;
+    const SHADOW_PRESETS_OUTER_RIGHT = 4;
+    const SHADOW_PRESETS_OUTER_CENTER = 5;
+    const SHADOW_PRESETS_OUTER_LEFT = 6;
+    const SHADOW_PRESETS_OUTER_TOP_RIGHT = 7;
+    const SHADOW_PRESETS_OUTER_TOP = 8;
+    const SHADOW_PRESETS_OUTER_TOP_LEFT = 9;
+    const SHADOW_PRESETS_INNER_BOTTTOM_RIGHT = 10;
+    const SHADOW_PRESETS_INNER_BOTTOM = 11;
+    const SHADOW_PRESETS_INNER_BOTTOM_LEFT = 12;
+    const SHADOW_PRESETS_INNER_RIGHT = 13;
+    const SHADOW_PRESETS_INNER_CENTER = 14;
+    const SHADOW_PRESETS_INNER_LEFT = 15;
+    const SHADOW_PRESETS_INNER_TOP_RIGHT = 16;
+    const SHADOW_PRESETS_INNER_TOP = 17;
+    const SHADOW_PRESETS_INNER_TOP_LEFT = 18;
+    const SHADOW_PRESETS_PERSPECTIVE_BELOW = 19;
+    const SHADOW_PRESETS_PERSPECTIVE_UPPER_RIGHT = 20;
+    const SHADOW_PRESETS_PERSPECTIVE_UPPER_LEFT = 21;
+    const SHADOW_PRESETS_PERSPECTIVE_LOWER_RIGHT = 22;
+    const SHADOW_PRESETS_PERSPECTIVE_LOWER_LEFT = 23;
+
+    /**
+     * @param float $width
+     *
+     * @return float
+     */
+    protected function getExcelPointsWidth($width)
+    {
+        return $width * 12700;
+    }
+
+    /**
+     * @param float $angle
+     *
+     * @return float
+     */
+    protected function getExcelPointsAngle($angle)
+    {
+        return $angle * 60000;
+    }
+
+    protected function getTrueAlpha($alpha)
+    {
+        return (string) 100 - $alpha . '000';
+    }
+
+    protected function setColorProperties($color, $alpha, $type)
+    {
+        return [
+            'type' => (string) $type,
+            'value' => (string) $color,
+            'alpha' => (string) $this->getTrueAlpha($alpha),
+        ];
+    }
+
+    protected function getLineStyleArrowSize($array_selector, $array_kay_selector)
+    {
+        $sizes = [
+            1 => ['w' => 'sm', 'len' => 'sm'],
+            2 => ['w' => 'sm', 'len' => 'med'],
+            3 => ['w' => 'sm', 'len' => 'lg'],
+            4 => ['w' => 'med', 'len' => 'sm'],
+            5 => ['w' => 'med', 'len' => 'med'],
+            6 => ['w' => 'med', 'len' => 'lg'],
+            7 => ['w' => 'lg', 'len' => 'sm'],
+            8 => ['w' => 'lg', 'len' => 'med'],
+            9 => ['w' => 'lg', 'len' => 'lg'],
+        ];
+
+        return $sizes[$array_selector][$array_kay_selector];
+    }
+
+    protected function getShadowPresetsMap($shadow_presets_option)
+    {
+        $presets_options = [
+            //OUTER
+            1 => [
+                'effect' => 'outerShdw',
+                'blur' => '50800',
+                'distance' => '38100',
+                'direction' => '2700000',
+                'algn' => 'tl',
+                'rotWithShape' => '0',
+            ],
+            2 => [
+                'effect' => 'outerShdw',
+                'blur' => '50800',
+                'distance' => '38100',
+                'direction' => '5400000',
+                'algn' => 't',
+                'rotWithShape' => '0',
+            ],
+            3 => [
+                'effect' => 'outerShdw',
+                'blur' => '50800',
+                'distance' => '38100',
+                'direction' => '8100000',
+                'algn' => 'tr',
+                'rotWithShape' => '0',
+            ],
+            4 => [
+                'effect' => 'outerShdw',
+                'blur' => '50800',
+                'distance' => '38100',
+                'algn' => 'l',
+                'rotWithShape' => '0',
+            ],
+            5 => [
+                'effect' => 'outerShdw',
+                'size' => [
+                    'sx' => '102000',
+                    'sy' => '102000',
+                ],
+                'blur' => '63500',
+                'distance' => '38100',
+                'algn' => 'ctr',
+                'rotWithShape' => '0',
+            ],
+            6 => [
+                'effect' => 'outerShdw',
+                'blur' => '50800',
+                'distance' => '38100',
+                'direction' => '10800000',
+                'algn' => 'r',
+                'rotWithShape' => '0',
+            ],
+            7 => [
+                'effect' => 'outerShdw',
+                'blur' => '50800',
+                'distance' => '38100',
+                'direction' => '18900000',
+                'algn' => 'bl',
+                'rotWithShape' => '0',
+            ],
+            8 => [
+                'effect' => 'outerShdw',
+                'blur' => '50800',
+                'distance' => '38100',
+                'direction' => '16200000',
+                'rotWithShape' => '0',
+            ],
+            9 => [
+                'effect' => 'outerShdw',
+                'blur' => '50800',
+                'distance' => '38100',
+                'direction' => '13500000',
+                'algn' => 'br',
+                'rotWithShape' => '0',
+            ],
+            //INNER
+            10 => [
+                'effect' => 'innerShdw',
+                'blur' => '63500',
+                'distance' => '50800',
+                'direction' => '2700000',
+            ],
+            11 => [
+                'effect' => 'innerShdw',
+                'blur' => '63500',
+                'distance' => '50800',
+                'direction' => '5400000',
+            ],
+            12 => [
+                'effect' => 'innerShdw',
+                'blur' => '63500',
+                'distance' => '50800',
+                'direction' => '8100000',
+            ],
+            13 => [
+                'effect' => 'innerShdw',
+                'blur' => '63500',
+                'distance' => '50800',
+            ],
+            14 => [
+                'effect' => 'innerShdw',
+                'blur' => '114300',
+            ],
+            15 => [
+                'effect' => 'innerShdw',
+                'blur' => '63500',
+                'distance' => '50800',
+                'direction' => '10800000',
+            ],
+            16 => [
+                'effect' => 'innerShdw',
+                'blur' => '63500',
+                'distance' => '50800',
+                'direction' => '18900000',
+            ],
+            17 => [
+                'effect' => 'innerShdw',
+                'blur' => '63500',
+                'distance' => '50800',
+                'direction' => '16200000',
+            ],
+            18 => [
+                'effect' => 'innerShdw',
+                'blur' => '63500',
+                'distance' => '50800',
+                'direction' => '13500000',
+            ],
+            //perspective
+            19 => [
+                'effect' => 'outerShdw',
+                'blur' => '152400',
+                'distance' => '317500',
+                'size' => [
+                    'sx' => '90000',
+                    'sy' => '-19000',
+                ],
+                'direction' => '5400000',
+                'rotWithShape' => '0',
+            ],
+            20 => [
+                'effect' => 'outerShdw',
+                'blur' => '76200',
+                'direction' => '18900000',
+                'size' => [
+                    'sy' => '23000',
+                    'kx' => '-1200000',
+                ],
+                'algn' => 'bl',
+                'rotWithShape' => '0',
+            ],
+            21 => [
+                'effect' => 'outerShdw',
+                'blur' => '76200',
+                'direction' => '13500000',
+                'size' => [
+                    'sy' => '23000',
+                    'kx' => '1200000',
+                ],
+                'algn' => 'br',
+                'rotWithShape' => '0',
+            ],
+            22 => [
+                'effect' => 'outerShdw',
+                'blur' => '76200',
+                'distance' => '12700',
+                'direction' => '2700000',
+                'size' => [
+                    'sy' => '-23000',
+                    'kx' => '-800400',
+                ],
+                'algn' => 'bl',
+                'rotWithShape' => '0',
+            ],
+            23 => [
+                'effect' => 'outerShdw',
+                'blur' => '76200',
+                'distance' => '12700',
+                'direction' => '8100000',
+                'size' => [
+                    'sy' => '-23000',
+                    'kx' => '800400',
+                ],
+                'algn' => 'br',
+                'rotWithShape' => '0',
+            ],
+        ];
+
+        return $presets_options[$shadow_presets_option];
+    }
+
+    protected function getArrayElementsValue($properties, $elements)
+    {
+        $reference = &$properties;
+        if (!is_array($elements)) {
+            return $reference[$elements];
+        }
+
+        foreach ($elements as $keys) {
+            $reference = &$reference[$keys];
+        }
+
+        return $reference;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/IRenderer.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/IRenderer.php
new file mode 100755
index 0000000..3032f6b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/IRenderer.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Chart\Renderer;
+
+use PhpOffice\PhpSpreadsheet\Chart\Chart;
+
+interface IRenderer
+{
+    /**
+     * IRenderer constructor.
+     */
+    public function __construct(Chart $chart);
+
+    /**
+     * Render the chart to given file (or stream).
+     *
+     * @param string $filename Name of the file render to
+     *
+     * @return bool true on success
+     */
+    public function render($filename);
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/JpGraph.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/JpGraph.php
new file mode 100755
index 0000000..bd931c6
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/JpGraph.php
@@ -0,0 +1,870 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Chart\Renderer;
+
+use AccBarPlot;
+use AccLinePlot;
+use BarPlot;
+use ContourPlot;
+use Graph;
+use GroupBarPlot;
+use LinePlot;
+use PhpOffice\PhpSpreadsheet\Chart\Chart;
+use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
+use PieGraph;
+use PiePlot;
+use PiePlot3D;
+use PiePlotC;
+use RadarGraph;
+use RadarPlot;
+use ScatterPlot;
+use Spline;
+use StockPlot;
+
+require_once __DIR__ . '/Polyfill.php';
+
+class JpGraph implements IRenderer
+{
+    private static $width = 640;
+
+    private static $height = 480;
+
+    private static $colourSet = [
+        'mediumpurple1', 'palegreen3', 'gold1', 'cadetblue1',
+        'darkmagenta', 'coral', 'dodgerblue3', 'eggplant',
+        'mediumblue', 'magenta', 'sandybrown', 'cyan',
+        'firebrick1', 'forestgreen', 'deeppink4', 'darkolivegreen',
+        'goldenrod2',
+    ];
+
+    private static $markSet;
+
+    private $chart;
+
+    private $graph;
+
+    private static $plotColour = 0;
+
+    private static $plotMark = 0;
+
+    /**
+     * Create a new jpgraph.
+     */
+    public function __construct(Chart $chart)
+    {
+        self::init();
+        $this->graph = null;
+        $this->chart = $chart;
+    }
+
+    private static function init(): void
+    {
+        static $loaded = false;
+        if ($loaded) {
+            return;
+        }
+
+        \JpGraph\JpGraph::load();
+        \JpGraph\JpGraph::module('bar');
+        \JpGraph\JpGraph::module('contour');
+        \JpGraph\JpGraph::module('line');
+        \JpGraph\JpGraph::module('pie');
+        \JpGraph\JpGraph::module('pie3d');
+        \JpGraph\JpGraph::module('radar');
+        \JpGraph\JpGraph::module('regstat');
+        \JpGraph\JpGraph::module('scatter');
+        \JpGraph\JpGraph::module('stock');
+
+        self::$markSet = [
+            'diamond' => MARK_DIAMOND,
+            'square' => MARK_SQUARE,
+            'triangle' => MARK_UTRIANGLE,
+            'x' => MARK_X,
+            'star' => MARK_STAR,
+            'dot' => MARK_FILLEDCIRCLE,
+            'dash' => MARK_DTRIANGLE,
+            'circle' => MARK_CIRCLE,
+            'plus' => MARK_CROSS,
+        ];
+
+        $loaded = true;
+    }
+
+    private function formatPointMarker($seriesPlot, $markerID)
+    {
+        $plotMarkKeys = array_keys(self::$markSet);
+        if ($markerID === null) {
+            //    Use default plot marker (next marker in the series)
+            self::$plotMark %= count(self::$markSet);
+            $seriesPlot->mark->SetType(self::$markSet[$plotMarkKeys[self::$plotMark++]]);
+        } elseif ($markerID !== 'none') {
+            //    Use specified plot marker (if it exists)
+            if (isset(self::$markSet[$markerID])) {
+                $seriesPlot->mark->SetType(self::$markSet[$markerID]);
+            } else {
+                //    If the specified plot marker doesn't exist, use default plot marker (next marker in the series)
+                self::$plotMark %= count(self::$markSet);
+                $seriesPlot->mark->SetType(self::$markSet[$plotMarkKeys[self::$plotMark++]]);
+            }
+        } else {
+            //    Hide plot marker
+            $seriesPlot->mark->Hide();
+        }
+        $seriesPlot->mark->SetColor(self::$colourSet[self::$plotColour]);
+        $seriesPlot->mark->SetFillColor(self::$colourSet[self::$plotColour]);
+        $seriesPlot->SetColor(self::$colourSet[self::$plotColour++]);
+
+        return $seriesPlot;
+    }
+
+    private function formatDataSetLabels($groupID, $datasetLabels, $labelCount, $rotation = '')
+    {
+        $datasetLabelFormatCode = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getFormatCode();
+        if ($datasetLabelFormatCode !== null) {
+            //    Retrieve any label formatting code
+            $datasetLabelFormatCode = stripslashes($datasetLabelFormatCode);
+        }
+
+        $testCurrentIndex = 0;
+        foreach ($datasetLabels as $i => $datasetLabel) {
+            if (is_array($datasetLabel)) {
+                if ($rotation == 'bar') {
+                    $datasetLabels[$i] = implode(' ', $datasetLabel);
+                } else {
+                    $datasetLabel = array_reverse($datasetLabel);
+                    $datasetLabels[$i] = implode("\n", $datasetLabel);
+                }
+            } else {
+                //    Format labels according to any formatting code
+                if ($datasetLabelFormatCode !== null) {
+                    $datasetLabels[$i] = NumberFormat::toFormattedString($datasetLabel, $datasetLabelFormatCode);
+                }
+            }
+            ++$testCurrentIndex;
+        }
+
+        return $datasetLabels;
+    }
+
+    private function percentageSumCalculation($groupID, $seriesCount)
+    {
+        $sumValues = [];
+        //    Adjust our values to a percentage value across all series in the group
+        for ($i = 0; $i < $seriesCount; ++$i) {
+            if ($i == 0) {
+                $sumValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues();
+            } else {
+                $nextValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues();
+                foreach ($nextValues as $k => $value) {
+                    if (isset($sumValues[$k])) {
+                        $sumValues[$k] += $value;
+                    } else {
+                        $sumValues[$k] = $value;
+                    }
+                }
+            }
+        }
+
+        return $sumValues;
+    }
+
+    private function percentageAdjustValues($dataValues, $sumValues)
+    {
+        foreach ($dataValues as $k => $dataValue) {
+            $dataValues[$k] = $dataValue / $sumValues[$k] * 100;
+        }
+
+        return $dataValues;
+    }
+
+    private function getCaption($captionElement)
+    {
+        //    Read any caption
+        $caption = ($captionElement !== null) ? $captionElement->getCaption() : null;
+        //    Test if we have a title caption to display
+        if ($caption !== null) {
+            //    If we do, it could be a plain string or an array
+            if (is_array($caption)) {
+                //    Implode an array to a plain string
+                $caption = implode('', $caption);
+            }
+        }
+
+        return $caption;
+    }
+
+    private function renderTitle(): void
+    {
+        $title = $this->getCaption($this->chart->getTitle());
+        if ($title !== null) {
+            $this->graph->title->Set($title);
+        }
+    }
+
+    private function renderLegend(): void
+    {
+        $legend = $this->chart->getLegend();
+        if ($legend !== null) {
+            $legendPosition = $legend->getPosition();
+            switch ($legendPosition) {
+                case 'r':
+                    $this->graph->legend->SetPos(0.01, 0.5, 'right', 'center'); //    right
+                    $this->graph->legend->SetColumns(1);
+
+                    break;
+                case 'l':
+                    $this->graph->legend->SetPos(0.01, 0.5, 'left', 'center'); //    left
+                    $this->graph->legend->SetColumns(1);
+
+                    break;
+                case 't':
+                    $this->graph->legend->SetPos(0.5, 0.01, 'center', 'top'); //    top
+                    break;
+                case 'b':
+                    $this->graph->legend->SetPos(0.5, 0.99, 'center', 'bottom'); //    bottom
+                    break;
+                default:
+                    $this->graph->legend->SetPos(0.01, 0.01, 'right', 'top'); //    top-right
+                    $this->graph->legend->SetColumns(1);
+
+                    break;
+            }
+        } else {
+            $this->graph->legend->Hide();
+        }
+    }
+
+    private function renderCartesianPlotArea($type = 'textlin'): void
+    {
+        $this->graph = new Graph(self::$width, self::$height);
+        $this->graph->SetScale($type);
+
+        $this->renderTitle();
+
+        //    Rotate for bar rather than column chart
+        $rotation = $this->chart->getPlotArea()->getPlotGroupByIndex(0)->getPlotDirection();
+        $reverse = $rotation == 'bar';
+
+        $xAxisLabel = $this->chart->getXAxisLabel();
+        if ($xAxisLabel !== null) {
+            $title = $this->getCaption($xAxisLabel);
+            if ($title !== null) {
+                $this->graph->xaxis->SetTitle($title, 'center');
+                $this->graph->xaxis->title->SetMargin(35);
+                if ($reverse) {
+                    $this->graph->xaxis->title->SetAngle(90);
+                    $this->graph->xaxis->title->SetMargin(90);
+                }
+            }
+        }
+
+        $yAxisLabel = $this->chart->getYAxisLabel();
+        if ($yAxisLabel !== null) {
+            $title = $this->getCaption($yAxisLabel);
+            if ($title !== null) {
+                $this->graph->yaxis->SetTitle($title, 'center');
+                if ($reverse) {
+                    $this->graph->yaxis->title->SetAngle(0);
+                    $this->graph->yaxis->title->SetMargin(-55);
+                }
+            }
+        }
+    }
+
+    private function renderPiePlotArea(): void
+    {
+        $this->graph = new PieGraph(self::$width, self::$height);
+
+        $this->renderTitle();
+    }
+
+    private function renderRadarPlotArea(): void
+    {
+        $this->graph = new RadarGraph(self::$width, self::$height);
+        $this->graph->SetScale('lin');
+
+        $this->renderTitle();
+    }
+
+    private function renderPlotLine($groupID, $filled = false, $combination = false, $dimensions = '2d'): void
+    {
+        $grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping();
+
+        $labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(0)->getPointCount();
+        if ($labelCount > 0) {
+            $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues();
+            $datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels, $labelCount);
+            $this->graph->xaxis->SetTickLabels($datasetLabels);
+        }
+
+        $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
+        $seriesPlots = [];
+        if ($grouping == 'percentStacked') {
+            $sumValues = $this->percentageSumCalculation($groupID, $seriesCount);
+        }
+
+        //    Loop through each data series in turn
+        for ($i = 0; $i < $seriesCount; ++$i) {
+            $dataValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues();
+            $marker = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getPointMarker();
+
+            if ($grouping == 'percentStacked') {
+                $dataValues = $this->percentageAdjustValues($dataValues, $sumValues);
+            }
+
+            //    Fill in any missing values in the $dataValues array
+            $testCurrentIndex = 0;
+            foreach ($dataValues as $k => $dataValue) {
+                while ($k != $testCurrentIndex) {
+                    $dataValues[$testCurrentIndex] = null;
+                    ++$testCurrentIndex;
+                }
+                ++$testCurrentIndex;
+            }
+
+            $seriesPlot = new LinePlot($dataValues);
+            if ($combination) {
+                $seriesPlot->SetBarCenter();
+            }
+
+            if ($filled) {
+                $seriesPlot->SetFilled(true);
+                $seriesPlot->SetColor('black');
+                $seriesPlot->SetFillColor(self::$colourSet[self::$plotColour++]);
+            } else {
+                //    Set the appropriate plot marker
+                $this->formatPointMarker($seriesPlot, $marker);
+            }
+            $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($i)->getDataValue();
+            $seriesPlot->SetLegend($dataLabel);
+
+            $seriesPlots[] = $seriesPlot;
+        }
+
+        if ($grouping == 'standard') {
+            $groupPlot = $seriesPlots;
+        } else {
+            $groupPlot = new AccLinePlot($seriesPlots);
+        }
+        $this->graph->Add($groupPlot);
+    }
+
+    private function renderPlotBar($groupID, $dimensions = '2d'): void
+    {
+        $rotation = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotDirection();
+        //    Rotate for bar rather than column chart
+        if (($groupID == 0) && ($rotation == 'bar')) {
+            $this->graph->Set90AndMargin();
+        }
+        $grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping();
+
+        $labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(0)->getPointCount();
+        if ($labelCount > 0) {
+            $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues();
+            $datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels, $labelCount, $rotation);
+            //    Rotate for bar rather than column chart
+            if ($rotation == 'bar') {
+                $datasetLabels = array_reverse($datasetLabels);
+                $this->graph->yaxis->SetPos('max');
+                $this->graph->yaxis->SetLabelAlign('center', 'top');
+                $this->graph->yaxis->SetLabelSide(SIDE_RIGHT);
+            }
+            $this->graph->xaxis->SetTickLabels($datasetLabels);
+        }
+
+        $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
+        $seriesPlots = [];
+        if ($grouping == 'percentStacked') {
+            $sumValues = $this->percentageSumCalculation($groupID, $seriesCount);
+        }
+
+        //    Loop through each data series in turn
+        for ($j = 0; $j < $seriesCount; ++$j) {
+            $dataValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($j)->getDataValues();
+            if ($grouping == 'percentStacked') {
+                $dataValues = $this->percentageAdjustValues($dataValues, $sumValues);
+            }
+
+            //    Fill in any missing values in the $dataValues array
+            $testCurrentIndex = 0;
+            foreach ($dataValues as $k => $dataValue) {
+                while ($k != $testCurrentIndex) {
+                    $dataValues[$testCurrentIndex] = null;
+                    ++$testCurrentIndex;
+                }
+                ++$testCurrentIndex;
+            }
+
+            //    Reverse the $dataValues order for bar rather than column chart
+            if ($rotation == 'bar') {
+                $dataValues = array_reverse($dataValues);
+            }
+            $seriesPlot = new BarPlot($dataValues);
+            $seriesPlot->SetColor('black');
+            $seriesPlot->SetFillColor(self::$colourSet[self::$plotColour++]);
+            if ($dimensions == '3d') {
+                $seriesPlot->SetShadow();
+            }
+            if (!$this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($j)) {
+                $dataLabel = '';
+            } else {
+                $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($j)->getDataValue();
+            }
+            $seriesPlot->SetLegend($dataLabel);
+
+            $seriesPlots[] = $seriesPlot;
+        }
+        //    Reverse the plot order for bar rather than column chart
+        if (($rotation == 'bar') && ($grouping != 'percentStacked')) {
+            $seriesPlots = array_reverse($seriesPlots);
+        }
+
+        if ($grouping == 'clustered') {
+            $groupPlot = new GroupBarPlot($seriesPlots);
+        } elseif ($grouping == 'standard') {
+            $groupPlot = new GroupBarPlot($seriesPlots);
+        } else {
+            $groupPlot = new AccBarPlot($seriesPlots);
+            if ($dimensions == '3d') {
+                $groupPlot->SetShadow();
+            }
+        }
+
+        $this->graph->Add($groupPlot);
+    }
+
+    private function renderPlotScatter($groupID, $bubble): void
+    {
+        $grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping();
+        $scatterStyle = $bubbleSize = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle();
+
+        $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
+        $seriesPlots = [];
+
+        //    Loop through each data series in turn
+        for ($i = 0; $i < $seriesCount; ++$i) {
+            $dataValuesY = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex($i)->getDataValues();
+            $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues();
+
+            foreach ($dataValuesY as $k => $dataValueY) {
+                $dataValuesY[$k] = $k;
+            }
+
+            $seriesPlot = new ScatterPlot($dataValuesX, $dataValuesY);
+            if ($scatterStyle == 'lineMarker') {
+                $seriesPlot->SetLinkPoints();
+                $seriesPlot->link->SetColor(self::$colourSet[self::$plotColour]);
+            } elseif ($scatterStyle == 'smoothMarker') {
+                $spline = new Spline($dataValuesY, $dataValuesX);
+                [$splineDataY, $splineDataX] = $spline->Get(count($dataValuesX) * self::$width / 20);
+                $lplot = new LinePlot($splineDataX, $splineDataY);
+                $lplot->SetColor(self::$colourSet[self::$plotColour]);
+
+                $this->graph->Add($lplot);
+            }
+
+            if ($bubble) {
+                $this->formatPointMarker($seriesPlot, 'dot');
+                $seriesPlot->mark->SetColor('black');
+                $seriesPlot->mark->SetSize($bubbleSize);
+            } else {
+                $marker = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getPointMarker();
+                $this->formatPointMarker($seriesPlot, $marker);
+            }
+            $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($i)->getDataValue();
+            $seriesPlot->SetLegend($dataLabel);
+
+            $this->graph->Add($seriesPlot);
+        }
+    }
+
+    private function renderPlotRadar($groupID): void
+    {
+        $radarStyle = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle();
+
+        $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
+        $seriesPlots = [];
+
+        //    Loop through each data series in turn
+        for ($i = 0; $i < $seriesCount; ++$i) {
+            $dataValuesY = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex($i)->getDataValues();
+            $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues();
+            $marker = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getPointMarker();
+
+            $dataValues = [];
+            foreach ($dataValuesY as $k => $dataValueY) {
+                $dataValues[$k] = implode(' ', array_reverse($dataValueY));
+            }
+            $tmp = array_shift($dataValues);
+            $dataValues[] = $tmp;
+            $tmp = array_shift($dataValuesX);
+            $dataValuesX[] = $tmp;
+
+            $this->graph->SetTitles(array_reverse($dataValues));
+
+            $seriesPlot = new RadarPlot(array_reverse($dataValuesX));
+
+            $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($i)->getDataValue();
+            $seriesPlot->SetColor(self::$colourSet[self::$plotColour++]);
+            if ($radarStyle == 'filled') {
+                $seriesPlot->SetFillColor(self::$colourSet[self::$plotColour]);
+            }
+            $this->formatPointMarker($seriesPlot, $marker);
+            $seriesPlot->SetLegend($dataLabel);
+
+            $this->graph->Add($seriesPlot);
+        }
+    }
+
+    private function renderPlotContour($groupID): void
+    {
+        $contourStyle = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle();
+
+        $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
+        $seriesPlots = [];
+
+        $dataValues = [];
+        //    Loop through each data series in turn
+        for ($i = 0; $i < $seriesCount; ++$i) {
+            $dataValuesY = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex($i)->getDataValues();
+            $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues();
+
+            $dataValues[$i] = $dataValuesX;
+        }
+        $seriesPlot = new ContourPlot($dataValues);
+
+        $this->graph->Add($seriesPlot);
+    }
+
+    private function renderPlotStock($groupID): void
+    {
+        $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
+        $plotOrder = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotOrder();
+
+        $dataValues = [];
+        //    Loop through each data series in turn and build the plot arrays
+        foreach ($plotOrder as $i => $v) {
+            $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($v)->getDataValues();
+            foreach ($dataValuesX as $j => $dataValueX) {
+                $dataValues[$plotOrder[$i]][$j] = $dataValueX;
+            }
+        }
+        if (empty($dataValues)) {
+            return;
+        }
+
+        $dataValuesPlot = [];
+        // Flatten the plot arrays to a single dimensional array to work with jpgraph
+        $jMax = count($dataValues[0]);
+        for ($j = 0; $j < $jMax; ++$j) {
+            for ($i = 0; $i < $seriesCount; ++$i) {
+                $dataValuesPlot[] = $dataValues[$i][$j];
+            }
+        }
+
+        // Set the x-axis labels
+        $labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(0)->getPointCount();
+        if ($labelCount > 0) {
+            $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues();
+            $datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels, $labelCount);
+            $this->graph->xaxis->SetTickLabels($datasetLabels);
+        }
+
+        $seriesPlot = new StockPlot($dataValuesPlot);
+        $seriesPlot->SetWidth(20);
+
+        $this->graph->Add($seriesPlot);
+    }
+
+    private function renderAreaChart($groupCount, $dimensions = '2d'): void
+    {
+        $this->renderCartesianPlotArea();
+
+        for ($i = 0; $i < $groupCount; ++$i) {
+            $this->renderPlotLine($i, true, false, $dimensions);
+        }
+    }
+
+    private function renderLineChart($groupCount, $dimensions = '2d'): void
+    {
+        $this->renderCartesianPlotArea();
+
+        for ($i = 0; $i < $groupCount; ++$i) {
+            $this->renderPlotLine($i, false, false, $dimensions);
+        }
+    }
+
+    private function renderBarChart($groupCount, $dimensions = '2d'): void
+    {
+        $this->renderCartesianPlotArea();
+
+        for ($i = 0; $i < $groupCount; ++$i) {
+            $this->renderPlotBar($i, $dimensions);
+        }
+    }
+
+    private function renderScatterChart($groupCount): void
+    {
+        $this->renderCartesianPlotArea('linlin');
+
+        for ($i = 0; $i < $groupCount; ++$i) {
+            $this->renderPlotScatter($i, false);
+        }
+    }
+
+    private function renderBubbleChart($groupCount): void
+    {
+        $this->renderCartesianPlotArea('linlin');
+
+        for ($i = 0; $i < $groupCount; ++$i) {
+            $this->renderPlotScatter($i, true);
+        }
+    }
+
+    private function renderPieChart($groupCount, $dimensions = '2d', $doughnut = false, $multiplePlots = false): void
+    {
+        $this->renderPiePlotArea();
+
+        $iLimit = ($multiplePlots) ? $groupCount : 1;
+        for ($groupID = 0; $groupID < $iLimit; ++$groupID) {
+            $grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping();
+            $exploded = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle();
+            $datasetLabels = [];
+            if ($groupID == 0) {
+                $labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(0)->getPointCount();
+                if ($labelCount > 0) {
+                    $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues();
+                    $datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels, $labelCount);
+                }
+            }
+
+            $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
+            $seriesPlots = [];
+            //    For pie charts, we only display the first series: doughnut charts generally display all series
+            $jLimit = ($multiplePlots) ? $seriesCount : 1;
+            //    Loop through each data series in turn
+            for ($j = 0; $j < $jLimit; ++$j) {
+                $dataValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($j)->getDataValues();
+
+                //    Fill in any missing values in the $dataValues array
+                $testCurrentIndex = 0;
+                foreach ($dataValues as $k => $dataValue) {
+                    while ($k != $testCurrentIndex) {
+                        $dataValues[$testCurrentIndex] = null;
+                        ++$testCurrentIndex;
+                    }
+                    ++$testCurrentIndex;
+                }
+
+                if ($dimensions == '3d') {
+                    $seriesPlot = new PiePlot3D($dataValues);
+                } else {
+                    if ($doughnut) {
+                        $seriesPlot = new PiePlotC($dataValues);
+                    } else {
+                        $seriesPlot = new PiePlot($dataValues);
+                    }
+                }
+
+                if ($multiplePlots) {
+                    $seriesPlot->SetSize(($jLimit - $j) / ($jLimit * 4));
+                }
+
+                if ($doughnut) {
+                    $seriesPlot->SetMidColor('white');
+                }
+
+                $seriesPlot->SetColor(self::$colourSet[self::$plotColour++]);
+                if (count($datasetLabels) > 0) {
+                    $seriesPlot->SetLabels(array_fill(0, count($datasetLabels), ''));
+                }
+                if ($dimensions != '3d') {
+                    $seriesPlot->SetGuideLines(false);
+                }
+                if ($j == 0) {
+                    if ($exploded) {
+                        $seriesPlot->ExplodeAll();
+                    }
+                    $seriesPlot->SetLegends($datasetLabels);
+                }
+
+                $this->graph->Add($seriesPlot);
+            }
+        }
+    }
+
+    private function renderRadarChart($groupCount): void
+    {
+        $this->renderRadarPlotArea();
+
+        for ($groupID = 0; $groupID < $groupCount; ++$groupID) {
+            $this->renderPlotRadar($groupID);
+        }
+    }
+
+    private function renderStockChart($groupCount): void
+    {
+        $this->renderCartesianPlotArea('intint');
+
+        for ($groupID = 0; $groupID < $groupCount; ++$groupID) {
+            $this->renderPlotStock($groupID);
+        }
+    }
+
+    private function renderContourChart($groupCount, $dimensions): void
+    {
+        $this->renderCartesianPlotArea('intint');
+
+        for ($i = 0; $i < $groupCount; ++$i) {
+            $this->renderPlotContour($i);
+        }
+    }
+
+    private function renderCombinationChart($groupCount, $dimensions, $outputDestination)
+    {
+        $this->renderCartesianPlotArea();
+
+        for ($i = 0; $i < $groupCount; ++$i) {
+            $dimensions = null;
+            $chartType = $this->chart->getPlotArea()->getPlotGroupByIndex($i)->getPlotType();
+            switch ($chartType) {
+                case 'area3DChart':
+                    $dimensions = '3d';
+                // no break
+                case 'areaChart':
+                    $this->renderPlotLine($i, true, true, $dimensions);
+
+                    break;
+                case 'bar3DChart':
+                    $dimensions = '3d';
+                // no break
+                case 'barChart':
+                    $this->renderPlotBar($i, $dimensions);
+
+                    break;
+                case 'line3DChart':
+                    $dimensions = '3d';
+                // no break
+                case 'lineChart':
+                    $this->renderPlotLine($i, false, true, $dimensions);
+
+                    break;
+                case 'scatterChart':
+                    $this->renderPlotScatter($i, false);
+
+                    break;
+                case 'bubbleChart':
+                    $this->renderPlotScatter($i, true);
+
+                    break;
+                default:
+                    $this->graph = null;
+
+                    return false;
+            }
+        }
+
+        $this->renderLegend();
+
+        $this->graph->Stroke($outputDestination);
+
+        return true;
+    }
+
+    public function render($outputDestination)
+    {
+        self::$plotColour = 0;
+
+        $groupCount = $this->chart->getPlotArea()->getPlotGroupCount();
+
+        $dimensions = null;
+        if ($groupCount == 1) {
+            $chartType = $this->chart->getPlotArea()->getPlotGroupByIndex(0)->getPlotType();
+        } else {
+            $chartTypes = [];
+            for ($i = 0; $i < $groupCount; ++$i) {
+                $chartTypes[] = $this->chart->getPlotArea()->getPlotGroupByIndex($i)->getPlotType();
+            }
+            $chartTypes = array_unique($chartTypes);
+            if (count($chartTypes) == 1) {
+                $chartType = array_pop($chartTypes);
+            } elseif (count($chartTypes) == 0) {
+                echo 'Chart is not yet implemented<br />';
+
+                return false;
+            } else {
+                return $this->renderCombinationChart($groupCount, $dimensions, $outputDestination);
+            }
+        }
+
+        switch ($chartType) {
+            case 'area3DChart':
+                $dimensions = '3d';
+            // no break
+            case 'areaChart':
+                $this->renderAreaChart($groupCount, $dimensions);
+
+                break;
+            case 'bar3DChart':
+                $dimensions = '3d';
+            // no break
+            case 'barChart':
+                $this->renderBarChart($groupCount, $dimensions);
+
+                break;
+            case 'line3DChart':
+                $dimensions = '3d';
+            // no break
+            case 'lineChart':
+                $this->renderLineChart($groupCount, $dimensions);
+
+                break;
+            case 'pie3DChart':
+                $dimensions = '3d';
+            // no break
+            case 'pieChart':
+                $this->renderPieChart($groupCount, $dimensions, false, false);
+
+                break;
+            case 'doughnut3DChart':
+                $dimensions = '3d';
+            // no break
+            case 'doughnutChart':
+                $this->renderPieChart($groupCount, $dimensions, true, true);
+
+                break;
+            case 'scatterChart':
+                $this->renderScatterChart($groupCount);
+
+                break;
+            case 'bubbleChart':
+                $this->renderBubbleChart($groupCount);
+
+                break;
+            case 'radarChart':
+                $this->renderRadarChart($groupCount);
+
+                break;
+            case 'surface3DChart':
+                $dimensions = '3d';
+            // no break
+            case 'surfaceChart':
+                $this->renderContourChart($groupCount, $dimensions);
+
+                break;
+            case 'stockChart':
+                $this->renderStockChart($groupCount);
+
+                break;
+            default:
+                echo $chartType . ' is not yet implemented<br />';
+
+                return false;
+        }
+        $this->renderLegend();
+
+        $this->graph->Stroke($outputDestination);
+
+        return true;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/PHP Charting Libraries.txt b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/PHP Charting Libraries.txt
new file mode 100755
index 0000000..4abab7a
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/PHP Charting Libraries.txt
@@ -0,0 +1,20 @@
+ChartDirector
+	https://www.advsofteng.com/cdphp.html
+
+GraPHPite
+	http://graphpite.sourceforge.net/
+
+JpGraph
+	http://www.aditus.nu/jpgraph/
+
+LibChart
+	https://naku.dohcrew.com/libchart/pages/introduction/
+
+pChart
+	http://pchart.sourceforge.net/
+
+TeeChart
+	https://www.steema.com/
+
+PHPGraphLib
+    http://www.ebrueggeman.com/phpgraphlib
\ No newline at end of file
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/Polyfill.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/Polyfill.php
new file mode 100755
index 0000000..5e233ab
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/Polyfill.php
@@ -0,0 +1,9 @@
+<?php
+
+// This is a dirty workaround to output JpGraph charts even when antialiasing is not available
+if (!function_exists('imageantialias')) {
+    function imageantialias(...$args): void
+    {
+        // Do nothing
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Title.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Title.php
new file mode 100755
index 0000000..af9fa08
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Title.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Chart;
+
+class Title
+{
+    /**
+     * Title Caption.
+     *
+     * @var string
+     */
+    private $caption;
+
+    /**
+     * Title Layout.
+     *
+     * @var Layout
+     */
+    private $layout;
+
+    /**
+     * Create a new Title.
+     *
+     * @param null|mixed $caption
+     */
+    public function __construct($caption = null, ?Layout $layout = null)
+    {
+        $this->caption = $caption;
+        $this->layout = $layout;
+    }
+
+    /**
+     * Get caption.
+     *
+     * @return string
+     */
+    public function getCaption()
+    {
+        return $this->caption;
+    }
+
+    /**
+     * Set caption.
+     *
+     * @param string $caption
+     *
+     * @return $this
+     */
+    public function setCaption($caption)
+    {
+        $this->caption = $caption;
+
+        return $this;
+    }
+
+    /**
+     * Get Layout.
+     *
+     * @return Layout
+     */
+    public function getLayout()
+    {
+        return $this->layout;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Cells.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Cells.php
new file mode 100755
index 0000000..48f34f4
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Cells.php
@@ -0,0 +1,498 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Collection;
+
+use Generator;
+use PhpOffice\PhpSpreadsheet\Cell\Cell;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use Psr\SimpleCache\CacheInterface;
+
+class Cells
+{
+    /**
+     * @var \Psr\SimpleCache\CacheInterface
+     */
+    private $cache;
+
+    /**
+     * Parent worksheet.
+     *
+     * @var Worksheet
+     */
+    private $parent;
+
+    /**
+     * The currently active Cell.
+     *
+     * @var Cell
+     */
+    private $currentCell;
+
+    /**
+     * Coordinate of the currently active Cell.
+     *
+     * @var string
+     */
+    private $currentCoordinate;
+
+    /**
+     * Flag indicating whether the currently active Cell requires saving.
+     *
+     * @var bool
+     */
+    private $currentCellIsDirty = false;
+
+    /**
+     * An index of existing cells. Booleans indexed by their coordinate.
+     *
+     * @var bool[]
+     */
+    private $index = [];
+
+    /**
+     * Prefix used to uniquely identify cache data for this worksheet.
+     *
+     * @var string
+     */
+    private $cachePrefix;
+
+    /**
+     * Initialise this new cell collection.
+     *
+     * @param Worksheet $parent The worksheet for this cell collection
+     */
+    public function __construct(Worksheet $parent, CacheInterface $cache)
+    {
+        // Set our parent worksheet.
+        // This is maintained here to facilitate re-attaching it to Cell objects when
+        // they are woken from a serialized state
+        $this->parent = $parent;
+        $this->cache = $cache;
+        $this->cachePrefix = $this->getUniqueID();
+    }
+
+    /**
+     * Return the parent worksheet for this cell collection.
+     *
+     * @return Worksheet
+     */
+    public function getParent()
+    {
+        return $this->parent;
+    }
+
+    /**
+     * Whether the collection holds a cell for the given coordinate.
+     *
+     * @param string $pCoord Coordinate of the cell to check
+     *
+     * @return bool
+     */
+    public function has($pCoord)
+    {
+        if ($pCoord === $this->currentCoordinate) {
+            return true;
+        }
+
+        // Check if the requested entry exists in the index
+        return isset($this->index[$pCoord]);
+    }
+
+    /**
+     * Add or update a cell in the collection.
+     *
+     * @param Cell $cell Cell to update
+     *
+     * @return Cell
+     */
+    public function update(Cell $cell)
+    {
+        return $this->add($cell->getCoordinate(), $cell);
+    }
+
+    /**
+     * Delete a cell in cache identified by coordinate.
+     *
+     * @param string $pCoord Coordinate of the cell to delete
+     */
+    public function delete($pCoord): void
+    {
+        if ($pCoord === $this->currentCoordinate && $this->currentCell !== null) {
+            $this->currentCell->detach();
+            $this->currentCoordinate = null;
+            $this->currentCell = null;
+            $this->currentCellIsDirty = false;
+        }
+
+        unset($this->index[$pCoord]);
+
+        // Delete the entry from cache
+        $this->cache->delete($this->cachePrefix . $pCoord);
+    }
+
+    /**
+     * Get a list of all cell coordinates currently held in the collection.
+     *
+     * @return string[]
+     */
+    public function getCoordinates()
+    {
+        return array_keys($this->index);
+    }
+
+    /**
+     * Get a sorted list of all cell coordinates currently held in the collection by row and column.
+     *
+     * @return string[]
+     */
+    public function getSortedCoordinates()
+    {
+        $sortKeys = [];
+        foreach ($this->getCoordinates() as $coord) {
+            $column = '';
+            $row = 0;
+            sscanf($coord, '%[A-Z]%d', $column, $row);
+            $sortKeys[sprintf('%09d%3s', $row, $column)] = $coord;
+        }
+        ksort($sortKeys);
+
+        return array_values($sortKeys);
+    }
+
+    /**
+     * Get highest worksheet column and highest row that have cell records.
+     *
+     * @return array Highest column name and highest row number
+     */
+    public function getHighestRowAndColumn()
+    {
+        // Lookup highest column and highest row
+        $col = ['A' => '1A'];
+        $row = [1];
+        foreach ($this->getCoordinates() as $coord) {
+            $c = '';
+            $r = 0;
+            sscanf($coord, '%[A-Z]%d', $c, $r);
+            $row[$r] = $r;
+            $col[$c] = strlen($c) . $c;
+        }
+
+        // Determine highest column and row
+        $highestRow = max($row);
+        $highestColumn = substr(max($col), 1);
+
+        return [
+            'row' => $highestRow,
+            'column' => $highestColumn,
+        ];
+    }
+
+    /**
+     * Return the cell coordinate of the currently active cell object.
+     *
+     * @return string
+     */
+    public function getCurrentCoordinate()
+    {
+        return $this->currentCoordinate;
+    }
+
+    /**
+     * Return the column coordinate of the currently active cell object.
+     *
+     * @return string
+     */
+    public function getCurrentColumn()
+    {
+        $column = '';
+        $row = 0;
+
+        sscanf($this->currentCoordinate, '%[A-Z]%d', $column, $row);
+
+        return $column;
+    }
+
+    /**
+     * Return the row coordinate of the currently active cell object.
+     *
+     * @return int
+     */
+    public function getCurrentRow()
+    {
+        $column = '';
+        $row = 0;
+
+        sscanf($this->currentCoordinate, '%[A-Z]%d', $column, $row);
+
+        return (int) $row;
+    }
+
+    /**
+     * Get highest worksheet column.
+     *
+     * @param string $row Return the highest column for the specified row,
+     *                    or the highest column of any row if no row number is passed
+     *
+     * @return string Highest column name
+     */
+    public function getHighestColumn($row = null)
+    {
+        if ($row === null) {
+            $colRow = $this->getHighestRowAndColumn();
+
+            return $colRow['column'];
+        }
+
+        $columnList = [1];
+        foreach ($this->getCoordinates() as $coord) {
+            $c = '';
+            $r = 0;
+
+            sscanf($coord, '%[A-Z]%d', $c, $r);
+            if ($r != $row) {
+                continue;
+            }
+            $columnList[] = Coordinate::columnIndexFromString($c);
+        }
+
+        return Coordinate::stringFromColumnIndex(max($columnList));
+    }
+
+    /**
+     * Get highest worksheet row.
+     *
+     * @param string $column Return the highest row for the specified column,
+     *                       or the highest row of any column if no column letter is passed
+     *
+     * @return int Highest row number
+     */
+    public function getHighestRow($column = null)
+    {
+        if ($column === null) {
+            $colRow = $this->getHighestRowAndColumn();
+
+            return $colRow['row'];
+        }
+
+        $rowList = [0];
+        foreach ($this->getCoordinates() as $coord) {
+            $c = '';
+            $r = 0;
+
+            sscanf($coord, '%[A-Z]%d', $c, $r);
+            if ($c != $column) {
+                continue;
+            }
+            $rowList[] = $r;
+        }
+
+        return max($rowList);
+    }
+
+    /**
+     * Generate a unique ID for cache referencing.
+     *
+     * @return string Unique Reference
+     */
+    private function getUniqueID()
+    {
+        return uniqid('phpspreadsheet.', true) . '.';
+    }
+
+    /**
+     * Clone the cell collection.
+     *
+     * @param Worksheet $parent The new worksheet that we're copying to
+     *
+     * @return self
+     */
+    public function cloneCellCollection(Worksheet $parent)
+    {
+        $this->storeCurrentCell();
+        $newCollection = clone $this;
+
+        $newCollection->parent = $parent;
+        if (($newCollection->currentCell !== null) && (is_object($newCollection->currentCell))) {
+            $newCollection->currentCell->attach($this);
+        }
+
+        // Get old values
+        $oldKeys = $newCollection->getAllCacheKeys();
+        $oldValues = $newCollection->cache->getMultiple($oldKeys);
+        $newValues = [];
+        $oldCachePrefix = $newCollection->cachePrefix;
+
+        // Change prefix
+        $newCollection->cachePrefix = $newCollection->getUniqueID();
+        foreach ($oldValues as $oldKey => $value) {
+            $newValues[str_replace($oldCachePrefix, $newCollection->cachePrefix, $oldKey)] = clone $value;
+        }
+
+        // Store new values
+        $stored = $newCollection->cache->setMultiple($newValues);
+        if (!$stored) {
+            $newCollection->__destruct();
+
+            throw new PhpSpreadsheetException('Failed to copy cells in cache');
+        }
+
+        return $newCollection;
+    }
+
+    /**
+     * Remove a row, deleting all cells in that row.
+     *
+     * @param string $row Row number to remove
+     */
+    public function removeRow($row): void
+    {
+        foreach ($this->getCoordinates() as $coord) {
+            $c = '';
+            $r = 0;
+
+            sscanf($coord, '%[A-Z]%d', $c, $r);
+            if ($r == $row) {
+                $this->delete($coord);
+            }
+        }
+    }
+
+    /**
+     * Remove a column, deleting all cells in that column.
+     *
+     * @param string $column Column ID to remove
+     */
+    public function removeColumn($column): void
+    {
+        foreach ($this->getCoordinates() as $coord) {
+            $c = '';
+            $r = 0;
+
+            sscanf($coord, '%[A-Z]%d', $c, $r);
+            if ($c == $column) {
+                $this->delete($coord);
+            }
+        }
+    }
+
+    /**
+     * Store cell data in cache for the current cell object if it's "dirty",
+     * and the 'nullify' the current cell object.
+     */
+    private function storeCurrentCell(): void
+    {
+        if ($this->currentCellIsDirty && !empty($this->currentCoordinate)) {
+            $this->currentCell->detach();
+
+            $stored = $this->cache->set($this->cachePrefix . $this->currentCoordinate, $this->currentCell);
+            if (!$stored) {
+                $this->__destruct();
+
+                throw new PhpSpreadsheetException("Failed to store cell {$this->currentCoordinate} in cache");
+            }
+            $this->currentCellIsDirty = false;
+        }
+
+        $this->currentCoordinate = null;
+        $this->currentCell = null;
+    }
+
+    /**
+     * Add or update a cell identified by its coordinate into the collection.
+     *
+     * @param string $pCoord Coordinate of the cell to update
+     * @param Cell $cell Cell to update
+     *
+     * @return \PhpOffice\PhpSpreadsheet\Cell\Cell
+     */
+    public function add($pCoord, Cell $cell)
+    {
+        if ($pCoord !== $this->currentCoordinate) {
+            $this->storeCurrentCell();
+        }
+        $this->index[$pCoord] = true;
+
+        $this->currentCoordinate = $pCoord;
+        $this->currentCell = $cell;
+        $this->currentCellIsDirty = true;
+
+        return $cell;
+    }
+
+    /**
+     * Get cell at a specific coordinate.
+     *
+     * @param string $pCoord Coordinate of the cell
+     *
+     * @return null|\PhpOffice\PhpSpreadsheet\Cell\Cell Cell that was found, or null if not found
+     */
+    public function get($pCoord)
+    {
+        if ($pCoord === $this->currentCoordinate) {
+            return $this->currentCell;
+        }
+        $this->storeCurrentCell();
+
+        // Return null if requested entry doesn't exist in collection
+        if (!$this->has($pCoord)) {
+            return null;
+        }
+
+        // Check if the entry that has been requested actually exists
+        $cell = $this->cache->get($this->cachePrefix . $pCoord);
+        if ($cell === null) {
+            throw new PhpSpreadsheetException("Cell entry {$pCoord} no longer exists in cache. This probably means that the cache was cleared by someone else.");
+        }
+
+        // Set current entry to the requested entry
+        $this->currentCoordinate = $pCoord;
+        $this->currentCell = $cell;
+        // Re-attach this as the cell's parent
+        $this->currentCell->attach($this);
+
+        // Return requested entry
+        return $this->currentCell;
+    }
+
+    /**
+     * Clear the cell collection and disconnect from our parent.
+     */
+    public function unsetWorksheetCells(): void
+    {
+        if ($this->currentCell !== null) {
+            $this->currentCell->detach();
+            $this->currentCell = null;
+            $this->currentCoordinate = null;
+        }
+
+        // Flush the cache
+        $this->__destruct();
+
+        $this->index = [];
+
+        // detach ourself from the worksheet, so that it can then delete this object successfully
+        $this->parent = null;
+    }
+
+    /**
+     * Destroy this cell collection.
+     */
+    public function __destruct()
+    {
+        $this->cache->deleteMultiple($this->getAllCacheKeys());
+    }
+
+    /**
+     * Returns all known cache keys.
+     *
+     * @return Generator|string[]
+     */
+    private function getAllCacheKeys()
+    {
+        foreach ($this->getCoordinates() as $coordinate) {
+            yield $this->cachePrefix . $coordinate;
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/CellsFactory.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/CellsFactory.php
new file mode 100755
index 0000000..7f34c23
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/CellsFactory.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Collection;
+
+use PhpOffice\PhpSpreadsheet\Settings;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+
+abstract class CellsFactory
+{
+    /**
+     * Initialise the cache storage.
+     *
+     * @param Worksheet $parent Enable cell caching for this worksheet
+     *
+     * @return Cells
+     * */
+    public static function getInstance(Worksheet $parent)
+    {
+        return new Cells($parent, Settings::getCache());
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Memory.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Memory.php
new file mode 100755
index 0000000..a32551b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Memory.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Collection;
+
+use Psr\SimpleCache\CacheInterface;
+
+/**
+ * This is the default implementation for in-memory cell collection.
+ *
+ * Alternatives implementation should leverage off-memory, non-volatile storage
+ * to reduce overall memory usage.
+ */
+class Memory implements CacheInterface
+{
+    private $cache = [];
+
+    public function clear()
+    {
+        $this->cache = [];
+
+        return true;
+    }
+
+    public function delete($key)
+    {
+        unset($this->cache[$key]);
+
+        return true;
+    }
+
+    public function deleteMultiple($keys)
+    {
+        foreach ($keys as $key) {
+            $this->delete($key);
+        }
+
+        return true;
+    }
+
+    public function get($key, $default = null)
+    {
+        if ($this->has($key)) {
+            return $this->cache[$key];
+        }
+
+        return $default;
+    }
+
+    public function getMultiple($keys, $default = null)
+    {
+        $results = [];
+        foreach ($keys as $key) {
+            $results[$key] = $this->get($key, $default);
+        }
+
+        return $results;
+    }
+
+    public function has($key)
+    {
+        return array_key_exists($key, $this->cache);
+    }
+
+    public function set($key, $value, $ttl = null)
+    {
+        $this->cache[$key] = $value;
+
+        return true;
+    }
+
+    public function setMultiple($values, $ttl = null)
+    {
+        foreach ($values as $key => $value) {
+            $this->set($key, $value);
+        }
+
+        return true;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Comment.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Comment.php
new file mode 100755
index 0000000..31f7664
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Comment.php
@@ -0,0 +1,329 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet;
+
+use PhpOffice\PhpSpreadsheet\RichText\RichText;
+
+class Comment implements IComparable
+{
+    /**
+     * Author.
+     *
+     * @var string
+     */
+    private $author;
+
+    /**
+     * Rich text comment.
+     *
+     * @var RichText
+     */
+    private $text;
+
+    /**
+     * Comment width (CSS style, i.e. XXpx or YYpt).
+     *
+     * @var string
+     */
+    private $width = '96pt';
+
+    /**
+     * Left margin (CSS style, i.e. XXpx or YYpt).
+     *
+     * @var string
+     */
+    private $marginLeft = '59.25pt';
+
+    /**
+     * Top margin (CSS style, i.e. XXpx or YYpt).
+     *
+     * @var string
+     */
+    private $marginTop = '1.5pt';
+
+    /**
+     * Visible.
+     *
+     * @var bool
+     */
+    private $visible = false;
+
+    /**
+     * Comment height (CSS style, i.e. XXpx or YYpt).
+     *
+     * @var string
+     */
+    private $height = '55.5pt';
+
+    /**
+     * Comment fill color.
+     *
+     * @var Style\Color
+     */
+    private $fillColor;
+
+    /**
+     * Alignment.
+     *
+     * @var string
+     */
+    private $alignment;
+
+    /**
+     * Create a new Comment.
+     */
+    public function __construct()
+    {
+        // Initialise variables
+        $this->author = 'Author';
+        $this->text = new RichText();
+        $this->fillColor = new Style\Color('FFFFFFE1');
+        $this->alignment = Style\Alignment::HORIZONTAL_GENERAL;
+    }
+
+    /**
+     * Get Author.
+     *
+     * @return string
+     */
+    public function getAuthor()
+    {
+        return $this->author;
+    }
+
+    /**
+     * Set Author.
+     *
+     * @param string $author
+     *
+     * @return $this
+     */
+    public function setAuthor($author)
+    {
+        $this->author = $author;
+
+        return $this;
+    }
+
+    /**
+     * Get Rich text comment.
+     *
+     * @return RichText
+     */
+    public function getText()
+    {
+        return $this->text;
+    }
+
+    /**
+     * Set Rich text comment.
+     *
+     * @return $this
+     */
+    public function setText(RichText $pValue)
+    {
+        $this->text = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get comment width (CSS style, i.e. XXpx or YYpt).
+     *
+     * @return string
+     */
+    public function getWidth()
+    {
+        return $this->width;
+    }
+
+    /**
+     * Set comment width (CSS style, i.e. XXpx or YYpt).
+     *
+     * @param string $width
+     *
+     * @return $this
+     */
+    public function setWidth($width)
+    {
+        $this->width = $width;
+
+        return $this;
+    }
+
+    /**
+     * Get comment height (CSS style, i.e. XXpx or YYpt).
+     *
+     * @return string
+     */
+    public function getHeight()
+    {
+        return $this->height;
+    }
+
+    /**
+     * Set comment height (CSS style, i.e. XXpx or YYpt).
+     *
+     * @param string $value
+     *
+     * @return $this
+     */
+    public function setHeight($value)
+    {
+        $this->height = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get left margin (CSS style, i.e. XXpx or YYpt).
+     *
+     * @return string
+     */
+    public function getMarginLeft()
+    {
+        return $this->marginLeft;
+    }
+
+    /**
+     * Set left margin (CSS style, i.e. XXpx or YYpt).
+     *
+     * @param string $value
+     *
+     * @return $this
+     */
+    public function setMarginLeft($value)
+    {
+        $this->marginLeft = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get top margin (CSS style, i.e. XXpx or YYpt).
+     *
+     * @return string
+     */
+    public function getMarginTop()
+    {
+        return $this->marginTop;
+    }
+
+    /**
+     * Set top margin (CSS style, i.e. XXpx or YYpt).
+     *
+     * @param string $value
+     *
+     * @return $this
+     */
+    public function setMarginTop($value)
+    {
+        $this->marginTop = $value;
+
+        return $this;
+    }
+
+    /**
+     * Is the comment visible by default?
+     *
+     * @return bool
+     */
+    public function getVisible()
+    {
+        return $this->visible;
+    }
+
+    /**
+     * Set comment default visibility.
+     *
+     * @param bool $value
+     *
+     * @return $this
+     */
+    public function setVisible($value)
+    {
+        $this->visible = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get fill color.
+     *
+     * @return Style\Color
+     */
+    public function getFillColor()
+    {
+        return $this->fillColor;
+    }
+
+    /**
+     * Set Alignment.
+     *
+     * @param string $alignment see Style\Alignment::HORIZONTAL_*
+     *
+     * @return $this
+     */
+    public function setAlignment($alignment)
+    {
+        $this->alignment = $alignment;
+
+        return $this;
+    }
+
+    /**
+     * Get Alignment.
+     *
+     * @return string
+     */
+    public function getAlignment()
+    {
+        return $this->alignment;
+    }
+
+    /**
+     * Get hash code.
+     *
+     * @return string Hash code
+     */
+    public function getHashCode()
+    {
+        return md5(
+            $this->author .
+            $this->text->getHashCode() .
+            $this->width .
+            $this->height .
+            $this->marginLeft .
+            $this->marginTop .
+            ($this->visible ? 1 : 0) .
+            $this->fillColor->getHashCode() .
+            $this->alignment .
+            __CLASS__
+        );
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $vars = get_object_vars($this);
+        foreach ($vars as $key => $value) {
+            if (is_object($value)) {
+                $this->$key = clone $value;
+            } else {
+                $this->$key = $value;
+            }
+        }
+    }
+
+    /**
+     * Convert to string.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return $this->text->getPlainText();
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Properties.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Properties.php
new file mode 100755
index 0000000..cbaae2d
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Properties.php
@@ -0,0 +1,629 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Document;
+
+class Properties
+{
+    /** constants */
+    const PROPERTY_TYPE_BOOLEAN = 'b';
+    const PROPERTY_TYPE_INTEGER = 'i';
+    const PROPERTY_TYPE_FLOAT = 'f';
+    const PROPERTY_TYPE_DATE = 'd';
+    const PROPERTY_TYPE_STRING = 's';
+    const PROPERTY_TYPE_UNKNOWN = 'u';
+
+    /**
+     * Creator.
+     *
+     * @var string
+     */
+    private $creator = 'Unknown Creator';
+
+    /**
+     * LastModifiedBy.
+     *
+     * @var string
+     */
+    private $lastModifiedBy;
+
+    /**
+     * Created.
+     *
+     * @var int
+     */
+    private $created;
+
+    /**
+     * Modified.
+     *
+     * @var int
+     */
+    private $modified;
+
+    /**
+     * Title.
+     *
+     * @var string
+     */
+    private $title = 'Untitled Spreadsheet';
+
+    /**
+     * Description.
+     *
+     * @var string
+     */
+    private $description = '';
+
+    /**
+     * Subject.
+     *
+     * @var string
+     */
+    private $subject = '';
+
+    /**
+     * Keywords.
+     *
+     * @var string
+     */
+    private $keywords = '';
+
+    /**
+     * Category.
+     *
+     * @var string
+     */
+    private $category = '';
+
+    /**
+     * Manager.
+     *
+     * @var string
+     */
+    private $manager = '';
+
+    /**
+     * Company.
+     *
+     * @var string
+     */
+    private $company = 'Microsoft Corporation';
+
+    /**
+     * Custom Properties.
+     *
+     * @var string
+     */
+    private $customProperties = [];
+
+    /**
+     * Create a new Document Properties instance.
+     */
+    public function __construct()
+    {
+        // Initialise values
+        $this->lastModifiedBy = $this->creator;
+        $this->created = time();
+        $this->modified = time();
+    }
+
+    /**
+     * Get Creator.
+     *
+     * @return string
+     */
+    public function getCreator()
+    {
+        return $this->creator;
+    }
+
+    /**
+     * Set Creator.
+     *
+     * @param string $creator
+     *
+     * @return $this
+     */
+    public function setCreator($creator)
+    {
+        $this->creator = $creator;
+
+        return $this;
+    }
+
+    /**
+     * Get Last Modified By.
+     *
+     * @return string
+     */
+    public function getLastModifiedBy()
+    {
+        return $this->lastModifiedBy;
+    }
+
+    /**
+     * Set Last Modified By.
+     *
+     * @param string $pValue
+     *
+     * @return $this
+     */
+    public function setLastModifiedBy($pValue)
+    {
+        $this->lastModifiedBy = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Created.
+     *
+     * @return int
+     */
+    public function getCreated()
+    {
+        return $this->created;
+    }
+
+    /**
+     * Set Created.
+     *
+     * @param int|string $time
+     *
+     * @return $this
+     */
+    public function setCreated($time)
+    {
+        if ($time === null) {
+            $time = time();
+        } elseif (is_string($time)) {
+            if (is_numeric($time)) {
+                $time = (int) $time;
+            } else {
+                $time = strtotime($time);
+            }
+        }
+
+        $this->created = $time;
+
+        return $this;
+    }
+
+    /**
+     * Get Modified.
+     *
+     * @return int
+     */
+    public function getModified()
+    {
+        return $this->modified;
+    }
+
+    /**
+     * Set Modified.
+     *
+     * @param int|string $time
+     *
+     * @return $this
+     */
+    public function setModified($time)
+    {
+        if ($time === null) {
+            $time = time();
+        } elseif (is_string($time)) {
+            if (is_numeric($time)) {
+                $time = (int) $time;
+            } else {
+                $time = strtotime($time);
+            }
+        }
+
+        $this->modified = $time;
+
+        return $this;
+    }
+
+    /**
+     * Get Title.
+     *
+     * @return string
+     */
+    public function getTitle()
+    {
+        return $this->title;
+    }
+
+    /**
+     * Set Title.
+     *
+     * @param string $title
+     *
+     * @return $this
+     */
+    public function setTitle($title)
+    {
+        $this->title = $title;
+
+        return $this;
+    }
+
+    /**
+     * Get Description.
+     *
+     * @return string
+     */
+    public function getDescription()
+    {
+        return $this->description;
+    }
+
+    /**
+     * Set Description.
+     *
+     * @param string $description
+     *
+     * @return $this
+     */
+    public function setDescription($description)
+    {
+        $this->description = $description;
+
+        return $this;
+    }
+
+    /**
+     * Get Subject.
+     *
+     * @return string
+     */
+    public function getSubject()
+    {
+        return $this->subject;
+    }
+
+    /**
+     * Set Subject.
+     *
+     * @param string $subject
+     *
+     * @return $this
+     */
+    public function setSubject($subject)
+    {
+        $this->subject = $subject;
+
+        return $this;
+    }
+
+    /**
+     * Get Keywords.
+     *
+     * @return string
+     */
+    public function getKeywords()
+    {
+        return $this->keywords;
+    }
+
+    /**
+     * Set Keywords.
+     *
+     * @param string $keywords
+     *
+     * @return $this
+     */
+    public function setKeywords($keywords)
+    {
+        $this->keywords = $keywords;
+
+        return $this;
+    }
+
+    /**
+     * Get Category.
+     *
+     * @return string
+     */
+    public function getCategory()
+    {
+        return $this->category;
+    }
+
+    /**
+     * Set Category.
+     *
+     * @param string $category
+     *
+     * @return $this
+     */
+    public function setCategory($category)
+    {
+        $this->category = $category;
+
+        return $this;
+    }
+
+    /**
+     * Get Company.
+     *
+     * @return string
+     */
+    public function getCompany()
+    {
+        return $this->company;
+    }
+
+    /**
+     * Set Company.
+     *
+     * @param string $company
+     *
+     * @return $this
+     */
+    public function setCompany($company)
+    {
+        $this->company = $company;
+
+        return $this;
+    }
+
+    /**
+     * Get Manager.
+     *
+     * @return string
+     */
+    public function getManager()
+    {
+        return $this->manager;
+    }
+
+    /**
+     * Set Manager.
+     *
+     * @param string $manager
+     *
+     * @return $this
+     */
+    public function setManager($manager)
+    {
+        $this->manager = $manager;
+
+        return $this;
+    }
+
+    /**
+     * Get a List of Custom Property Names.
+     *
+     * @return array of string
+     */
+    public function getCustomProperties()
+    {
+        return array_keys($this->customProperties);
+    }
+
+    /**
+     * Check if a Custom Property is defined.
+     *
+     * @param string $propertyName
+     *
+     * @return bool
+     */
+    public function isCustomPropertySet($propertyName)
+    {
+        return isset($this->customProperties[$propertyName]);
+    }
+
+    /**
+     * Get a Custom Property Value.
+     *
+     * @param string $propertyName
+     *
+     * @return mixed
+     */
+    public function getCustomPropertyValue($propertyName)
+    {
+        if (isset($this->customProperties[$propertyName])) {
+            return $this->customProperties[$propertyName]['value'];
+        }
+    }
+
+    /**
+     * Get a Custom Property Type.
+     *
+     * @param string $propertyName
+     *
+     * @return string
+     */
+    public function getCustomPropertyType($propertyName)
+    {
+        if (isset($this->customProperties[$propertyName])) {
+            return $this->customProperties[$propertyName]['type'];
+        }
+    }
+
+    /**
+     * Set a Custom Property.
+     *
+     * @param string $propertyName
+     * @param mixed $propertyValue
+     * @param string $propertyType
+     *      'i'    : Integer
+     *   'f' : Floating Point
+     *   's' : String
+     *   'd' : Date/Time
+     *   'b' : Boolean
+     *
+     * @return $this
+     */
+    public function setCustomProperty($propertyName, $propertyValue = '', $propertyType = null)
+    {
+        if (($propertyType === null) || (!in_array($propertyType, [self::PROPERTY_TYPE_INTEGER,
+            self::PROPERTY_TYPE_FLOAT,
+            self::PROPERTY_TYPE_STRING,
+            self::PROPERTY_TYPE_DATE,
+            self::PROPERTY_TYPE_BOOLEAN, ]))) {
+            if ($propertyValue === null) {
+                $propertyType = self::PROPERTY_TYPE_STRING;
+            } elseif (is_float($propertyValue)) {
+                $propertyType = self::PROPERTY_TYPE_FLOAT;
+            } elseif (is_int($propertyValue)) {
+                $propertyType = self::PROPERTY_TYPE_INTEGER;
+            } elseif (is_bool($propertyValue)) {
+                $propertyType = self::PROPERTY_TYPE_BOOLEAN;
+            } else {
+                $propertyType = self::PROPERTY_TYPE_STRING;
+            }
+        }
+
+        $this->customProperties[$propertyName] = [
+            'value' => $propertyValue,
+            'type' => $propertyType,
+        ];
+
+        return $this;
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $vars = get_object_vars($this);
+        foreach ($vars as $key => $value) {
+            if (is_object($value)) {
+                $this->$key = clone $value;
+            } else {
+                $this->$key = $value;
+            }
+        }
+    }
+
+    public static function convertProperty($propertyValue, $propertyType)
+    {
+        switch ($propertyType) {
+            case 'empty':     //    Empty
+                return '';
+
+                break;
+            case 'null':      //    Null
+                return null;
+
+                break;
+            case 'i1':        //    1-Byte Signed Integer
+            case 'i2':        //    2-Byte Signed Integer
+            case 'i4':        //    4-Byte Signed Integer
+            case 'i8':        //    8-Byte Signed Integer
+            case 'int':       //    Integer
+                return (int) $propertyValue;
+
+                break;
+            case 'ui1':       //    1-Byte Unsigned Integer
+            case 'ui2':       //    2-Byte Unsigned Integer
+            case 'ui4':       //    4-Byte Unsigned Integer
+            case 'ui8':       //    8-Byte Unsigned Integer
+            case 'uint':      //    Unsigned Integer
+                return abs((int) $propertyValue);
+
+                break;
+            case 'r4':        //    4-Byte Real Number
+            case 'r8':        //    8-Byte Real Number
+            case 'decimal':   //    Decimal
+                return (float) $propertyValue;
+
+                break;
+            case 'lpstr':     //    LPSTR
+            case 'lpwstr':    //    LPWSTR
+            case 'bstr':      //    Basic String
+                return $propertyValue;
+
+                break;
+            case 'date':      //    Date and Time
+            case 'filetime':  //    File Time
+                return strtotime($propertyValue);
+
+                break;
+            case 'bool':     //    Boolean
+                return $propertyValue == 'true';
+
+                break;
+            case 'cy':       //    Currency
+            case 'error':    //    Error Status Code
+            case 'vector':   //    Vector
+            case 'array':    //    Array
+            case 'blob':     //    Binary Blob
+            case 'oblob':    //    Binary Blob Object
+            case 'stream':   //    Binary Stream
+            case 'ostream':  //    Binary Stream Object
+            case 'storage':  //    Binary Storage
+            case 'ostorage': //    Binary Storage Object
+            case 'vstream':  //    Binary Versioned Stream
+            case 'clsid':    //    Class ID
+            case 'cf':       //    Clipboard Data
+                return $propertyValue;
+
+                break;
+        }
+
+        return $propertyValue;
+    }
+
+    public static function convertPropertyType($propertyType)
+    {
+        switch ($propertyType) {
+            case 'i1':       //    1-Byte Signed Integer
+            case 'i2':       //    2-Byte Signed Integer
+            case 'i4':       //    4-Byte Signed Integer
+            case 'i8':       //    8-Byte Signed Integer
+            case 'int':      //    Integer
+            case 'ui1':      //    1-Byte Unsigned Integer
+            case 'ui2':      //    2-Byte Unsigned Integer
+            case 'ui4':      //    4-Byte Unsigned Integer
+            case 'ui8':      //    8-Byte Unsigned Integer
+            case 'uint':     //    Unsigned Integer
+                return self::PROPERTY_TYPE_INTEGER;
+
+                break;
+            case 'r4':       //    4-Byte Real Number
+            case 'r8':       //    8-Byte Real Number
+            case 'decimal':  //    Decimal
+                return self::PROPERTY_TYPE_FLOAT;
+
+                break;
+            case 'empty':    //    Empty
+            case 'null':     //    Null
+            case 'lpstr':    //    LPSTR
+            case 'lpwstr':   //    LPWSTR
+            case 'bstr':     //    Basic String
+                return self::PROPERTY_TYPE_STRING;
+
+                break;
+            case 'date':     //    Date and Time
+            case 'filetime': //    File Time
+                return self::PROPERTY_TYPE_DATE;
+
+                break;
+            case 'bool':     //    Boolean
+                return self::PROPERTY_TYPE_BOOLEAN;
+
+                break;
+            case 'cy':       //    Currency
+            case 'error':    //    Error Status Code
+            case 'vector':   //    Vector
+            case 'array':    //    Array
+            case 'blob':     //    Binary Blob
+            case 'oblob':    //    Binary Blob Object
+            case 'stream':   //    Binary Stream
+            case 'ostream':  //    Binary Stream Object
+            case 'storage':  //    Binary Storage
+            case 'ostorage': //    Binary Storage Object
+            case 'vstream':  //    Binary Versioned Stream
+            case 'clsid':    //    Class ID
+            case 'cf':       //    Clipboard Data
+                return self::PROPERTY_TYPE_UNKNOWN;
+
+                break;
+        }
+
+        return self::PROPERTY_TYPE_UNKNOWN;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Security.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Security.php
new file mode 100755
index 0000000..cef3db8
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Security.php
@@ -0,0 +1,205 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Document;
+
+use PhpOffice\PhpSpreadsheet\Shared\PasswordHasher;
+
+class Security
+{
+    /**
+     * LockRevision.
+     *
+     * @var bool
+     */
+    private $lockRevision = false;
+
+    /**
+     * LockStructure.
+     *
+     * @var bool
+     */
+    private $lockStructure = false;
+
+    /**
+     * LockWindows.
+     *
+     * @var bool
+     */
+    private $lockWindows = false;
+
+    /**
+     * RevisionsPassword.
+     *
+     * @var string
+     */
+    private $revisionsPassword = '';
+
+    /**
+     * WorkbookPassword.
+     *
+     * @var string
+     */
+    private $workbookPassword = '';
+
+    /**
+     * Create a new Document Security instance.
+     */
+    public function __construct()
+    {
+    }
+
+    /**
+     * Is some sort of document security enabled?
+     *
+     * @return bool
+     */
+    public function isSecurityEnabled()
+    {
+        return  $this->lockRevision ||
+                $this->lockStructure ||
+                $this->lockWindows;
+    }
+
+    /**
+     * Get LockRevision.
+     *
+     * @return bool
+     */
+    public function getLockRevision()
+    {
+        return $this->lockRevision;
+    }
+
+    /**
+     * Set LockRevision.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setLockRevision($pValue)
+    {
+        $this->lockRevision = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get LockStructure.
+     *
+     * @return bool
+     */
+    public function getLockStructure()
+    {
+        return $this->lockStructure;
+    }
+
+    /**
+     * Set LockStructure.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setLockStructure($pValue)
+    {
+        $this->lockStructure = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get LockWindows.
+     *
+     * @return bool
+     */
+    public function getLockWindows()
+    {
+        return $this->lockWindows;
+    }
+
+    /**
+     * Set LockWindows.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setLockWindows($pValue)
+    {
+        $this->lockWindows = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get RevisionsPassword (hashed).
+     *
+     * @return string
+     */
+    public function getRevisionsPassword()
+    {
+        return $this->revisionsPassword;
+    }
+
+    /**
+     * Set RevisionsPassword.
+     *
+     * @param string $pValue
+     * @param bool $pAlreadyHashed If the password has already been hashed, set this to true
+     *
+     * @return $this
+     */
+    public function setRevisionsPassword($pValue, $pAlreadyHashed = false)
+    {
+        if (!$pAlreadyHashed) {
+            $pValue = PasswordHasher::hashPassword($pValue);
+        }
+        $this->revisionsPassword = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get WorkbookPassword (hashed).
+     *
+     * @return string
+     */
+    public function getWorkbookPassword()
+    {
+        return $this->workbookPassword;
+    }
+
+    /**
+     * Set WorkbookPassword.
+     *
+     * @param string $pValue
+     * @param bool $pAlreadyHashed If the password has already been hashed, set this to true
+     *
+     * @return $this
+     */
+    public function setWorkbookPassword($pValue, $pAlreadyHashed = false)
+    {
+        if (!$pAlreadyHashed) {
+            $pValue = PasswordHasher::hashPassword($pValue);
+        }
+        $this->workbookPassword = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $vars = get_object_vars($this);
+        foreach ($vars as $key => $value) {
+            if (is_object($value)) {
+                $this->$key = clone $value;
+            } else {
+                $this->$key = $value;
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/DocumentGenerator.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/DocumentGenerator.php
new file mode 100755
index 0000000..5e06af9
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/DocumentGenerator.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Category;
+use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use ReflectionClass;
+use UnexpectedValueException;
+
+class DocumentGenerator
+{
+    /**
+     * @param array[] $phpSpreadsheetFunctions
+     */
+    public static function generateFunctionListByCategory(array $phpSpreadsheetFunctions): string
+    {
+        $result = "# Function list by category\n";
+        foreach (self::getCategories() as $categoryConstant => $category) {
+            $result .= "\n";
+            $result .= "## {$categoryConstant}\n";
+            $result .= "\n";
+            $lengths = [20, 42];
+            $result .= self::tableRow($lengths, ['Excel Function', 'PhpSpreadsheet Function']) . "\n";
+            $result .= self::tableRow($lengths, null) . "\n";
+            foreach ($phpSpreadsheetFunctions as $excelFunction => $functionInfo) {
+                if ($category === $functionInfo['category']) {
+                    $phpFunction = self::getPhpSpreadsheetFunctionText($functionInfo['functionCall']);
+                    $result .= self::tableRow($lengths, [$excelFunction, $phpFunction]) . "\n";
+                }
+            }
+        }
+
+        return $result;
+    }
+
+    private static function getCategories(): array
+    {
+        return (new ReflectionClass(Category::class))->getConstants();
+    }
+
+    private static function tableRow(array $lengths, ?array $values = null): string
+    {
+        $result = '';
+        foreach (array_map(null, $lengths, $values ?? []) as $i => [$length, $value]) {
+            $pad = $value === null ? '-' : ' ';
+            if ($i > 0) {
+                $result .= '|' . $pad;
+            }
+            $result .= str_pad($value ?? '', $length, $pad);
+        }
+
+        return rtrim($result, ' ');
+    }
+
+    private static function getPhpSpreadsheetFunctionText($functionCall): string
+    {
+        if (is_string($functionCall)) {
+            return $functionCall;
+        }
+        if ($functionCall === [Functions::class, 'DUMMY']) {
+            return '**Not yet Implemented**';
+        }
+        if (is_array($functionCall)) {
+            return "\\{$functionCall[0]}::{$functionCall[1]}";
+        }
+
+        throw new UnexpectedValueException(
+            '$functionCall is of type ' . gettype($functionCall) . '. string or array expected'
+        );
+    }
+
+    /**
+     * @param array[] $phpSpreadsheetFunctions
+     */
+    public static function generateFunctionListByName(array $phpSpreadsheetFunctions): string
+    {
+        $categoryConstants = array_flip(self::getCategories());
+        $result = "# Function list by name\n";
+        $lastAlphabet = null;
+        foreach ($phpSpreadsheetFunctions as $excelFunction => $functionInfo) {
+            $lengths = [20, 31, 42];
+            if ($lastAlphabet !== $excelFunction[0]) {
+                $lastAlphabet = $excelFunction[0];
+                $result .= "\n";
+                $result .= "## {$lastAlphabet}\n";
+                $result .= "\n";
+                $result .= self::tableRow($lengths, ['Excel Function', 'Category', 'PhpSpreadsheet Function']) . "\n";
+                $result .= self::tableRow($lengths, null) . "\n";
+            }
+            $category = $categoryConstants[$functionInfo['category']];
+            $phpFunction = self::getPhpSpreadsheetFunctionText($functionInfo['functionCall']);
+            $result .= self::tableRow($lengths, [$excelFunction, $category, $phpFunction]) . "\n";
+        }
+
+        return $result;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Exception.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Exception.php
new file mode 100755
index 0000000..9c5ab30
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Exception.php
@@ -0,0 +1,7 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet;
+
+class Exception extends \Exception
+{
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/HashTable.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/HashTable.php
new file mode 100755
index 0000000..90ea806
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/HashTable.php
@@ -0,0 +1,175 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet;
+
+class HashTable
+{
+    /**
+     * HashTable elements.
+     *
+     * @var IComparable[]
+     */
+    protected $items = [];
+
+    /**
+     * HashTable key map.
+     *
+     * @var string[]
+     */
+    protected $keyMap = [];
+
+    /**
+     * Create a new \PhpOffice\PhpSpreadsheet\HashTable.
+     *
+     * @param IComparable[] $pSource Optional source array to create HashTable from
+     */
+    public function __construct($pSource = null)
+    {
+        if ($pSource !== null) {
+            // Create HashTable
+            $this->addFromSource($pSource);
+        }
+    }
+
+    /**
+     * Add HashTable items from source.
+     *
+     * @param IComparable[] $pSource Source array to create HashTable from
+     */
+    public function addFromSource(?array $pSource = null): void
+    {
+        // Check if an array was passed
+        if ($pSource == null) {
+            return;
+        }
+
+        foreach ($pSource as $item) {
+            $this->add($item);
+        }
+    }
+
+    /**
+     * Add HashTable item.
+     *
+     * @param IComparable $pSource Item to add
+     */
+    public function add(IComparable $pSource): void
+    {
+        $hash = $pSource->getHashCode();
+        if (!isset($this->items[$hash])) {
+            $this->items[$hash] = $pSource;
+            $this->keyMap[count($this->items) - 1] = $hash;
+        }
+    }
+
+    /**
+     * Remove HashTable item.
+     *
+     * @param IComparable $pSource Item to remove
+     */
+    public function remove(IComparable $pSource): void
+    {
+        $hash = $pSource->getHashCode();
+        if (isset($this->items[$hash])) {
+            unset($this->items[$hash]);
+
+            $deleteKey = -1;
+            foreach ($this->keyMap as $key => $value) {
+                if ($deleteKey >= 0) {
+                    $this->keyMap[$key - 1] = $value;
+                }
+
+                if ($value == $hash) {
+                    $deleteKey = $key;
+                }
+            }
+            unset($this->keyMap[count($this->keyMap) - 1]);
+        }
+    }
+
+    /**
+     * Clear HashTable.
+     */
+    public function clear(): void
+    {
+        $this->items = [];
+        $this->keyMap = [];
+    }
+
+    /**
+     * Count.
+     *
+     * @return int
+     */
+    public function count()
+    {
+        return count($this->items);
+    }
+
+    /**
+     * Get index for hash code.
+     *
+     * @param string $pHashCode
+     *
+     * @return int Index
+     */
+    public function getIndexForHashCode($pHashCode)
+    {
+        return array_search($pHashCode, $this->keyMap);
+    }
+
+    /**
+     * Get by index.
+     *
+     * @param int $pIndex
+     *
+     * @return IComparable
+     */
+    public function getByIndex($pIndex)
+    {
+        if (isset($this->keyMap[$pIndex])) {
+            return $this->getByHashCode($this->keyMap[$pIndex]);
+        }
+
+        return null;
+    }
+
+    /**
+     * Get by hashcode.
+     *
+     * @param string $pHashCode
+     *
+     * @return IComparable
+     */
+    public function getByHashCode($pHashCode)
+    {
+        if (isset($this->items[$pHashCode])) {
+            return $this->items[$pHashCode];
+        }
+
+        return null;
+    }
+
+    /**
+     * HashTable to array.
+     *
+     * @return IComparable[]
+     */
+    public function toArray()
+    {
+        return $this->items;
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $vars = get_object_vars($this);
+        foreach ($vars as $key => $value) {
+            if (is_object($value)) {
+                $this->$key = clone $value;
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Html.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Html.php
new file mode 100755
index 0000000..252e14a
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Html.php
@@ -0,0 +1,839 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Helper;
+
+use DOMDocument;
+use DOMElement;
+use DOMNode;
+use DOMText;
+use PhpOffice\PhpSpreadsheet\RichText\RichText;
+use PhpOffice\PhpSpreadsheet\Style\Color;
+use PhpOffice\PhpSpreadsheet\Style\Font;
+
+class Html
+{
+    protected static $colourMap = [
+        'aliceblue' => 'f0f8ff',
+        'antiquewhite' => 'faebd7',
+        'antiquewhite1' => 'ffefdb',
+        'antiquewhite2' => 'eedfcc',
+        'antiquewhite3' => 'cdc0b0',
+        'antiquewhite4' => '8b8378',
+        'aqua' => '00ffff',
+        'aquamarine1' => '7fffd4',
+        'aquamarine2' => '76eec6',
+        'aquamarine4' => '458b74',
+        'azure1' => 'f0ffff',
+        'azure2' => 'e0eeee',
+        'azure3' => 'c1cdcd',
+        'azure4' => '838b8b',
+        'beige' => 'f5f5dc',
+        'bisque1' => 'ffe4c4',
+        'bisque2' => 'eed5b7',
+        'bisque3' => 'cdb79e',
+        'bisque4' => '8b7d6b',
+        'black' => '000000',
+        'blanchedalmond' => 'ffebcd',
+        'blue' => '0000ff',
+        'blue1' => '0000ff',
+        'blue2' => '0000ee',
+        'blue4' => '00008b',
+        'blueviolet' => '8a2be2',
+        'brown' => 'a52a2a',
+        'brown1' => 'ff4040',
+        'brown2' => 'ee3b3b',
+        'brown3' => 'cd3333',
+        'brown4' => '8b2323',
+        'burlywood' => 'deb887',
+        'burlywood1' => 'ffd39b',
+        'burlywood2' => 'eec591',
+        'burlywood3' => 'cdaa7d',
+        'burlywood4' => '8b7355',
+        'cadetblue' => '5f9ea0',
+        'cadetblue1' => '98f5ff',
+        'cadetblue2' => '8ee5ee',
+        'cadetblue3' => '7ac5cd',
+        'cadetblue4' => '53868b',
+        'chartreuse1' => '7fff00',
+        'chartreuse2' => '76ee00',
+        'chartreuse3' => '66cd00',
+        'chartreuse4' => '458b00',
+        'chocolate' => 'd2691e',
+        'chocolate1' => 'ff7f24',
+        'chocolate2' => 'ee7621',
+        'chocolate3' => 'cd661d',
+        'coral' => 'ff7f50',
+        'coral1' => 'ff7256',
+        'coral2' => 'ee6a50',
+        'coral3' => 'cd5b45',
+        'coral4' => '8b3e2f',
+        'cornflowerblue' => '6495ed',
+        'cornsilk1' => 'fff8dc',
+        'cornsilk2' => 'eee8cd',
+        'cornsilk3' => 'cdc8b1',
+        'cornsilk4' => '8b8878',
+        'cyan1' => '00ffff',
+        'cyan2' => '00eeee',
+        'cyan3' => '00cdcd',
+        'cyan4' => '008b8b',
+        'darkgoldenrod' => 'b8860b',
+        'darkgoldenrod1' => 'ffb90f',
+        'darkgoldenrod2' => 'eead0e',
+        'darkgoldenrod3' => 'cd950c',
+        'darkgoldenrod4' => '8b6508',
+        'darkgreen' => '006400',
+        'darkkhaki' => 'bdb76b',
+        'darkolivegreen' => '556b2f',
+        'darkolivegreen1' => 'caff70',
+        'darkolivegreen2' => 'bcee68',
+        'darkolivegreen3' => 'a2cd5a',
+        'darkolivegreen4' => '6e8b3d',
+        'darkorange' => 'ff8c00',
+        'darkorange1' => 'ff7f00',
+        'darkorange2' => 'ee7600',
+        'darkorange3' => 'cd6600',
+        'darkorange4' => '8b4500',
+        'darkorchid' => '9932cc',
+        'darkorchid1' => 'bf3eff',
+        'darkorchid2' => 'b23aee',
+        'darkorchid3' => '9a32cd',
+        'darkorchid4' => '68228b',
+        'darksalmon' => 'e9967a',
+        'darkseagreen' => '8fbc8f',
+        'darkseagreen1' => 'c1ffc1',
+        'darkseagreen2' => 'b4eeb4',
+        'darkseagreen3' => '9bcd9b',
+        'darkseagreen4' => '698b69',
+        'darkslateblue' => '483d8b',
+        'darkslategray' => '2f4f4f',
+        'darkslategray1' => '97ffff',
+        'darkslategray2' => '8deeee',
+        'darkslategray3' => '79cdcd',
+        'darkslategray4' => '528b8b',
+        'darkturquoise' => '00ced1',
+        'darkviolet' => '9400d3',
+        'deeppink1' => 'ff1493',
+        'deeppink2' => 'ee1289',
+        'deeppink3' => 'cd1076',
+        'deeppink4' => '8b0a50',
+        'deepskyblue1' => '00bfff',
+        'deepskyblue2' => '00b2ee',
+        'deepskyblue3' => '009acd',
+        'deepskyblue4' => '00688b',
+        'dimgray' => '696969',
+        'dodgerblue1' => '1e90ff',
+        'dodgerblue2' => '1c86ee',
+        'dodgerblue3' => '1874cd',
+        'dodgerblue4' => '104e8b',
+        'firebrick' => 'b22222',
+        'firebrick1' => 'ff3030',
+        'firebrick2' => 'ee2c2c',
+        'firebrick3' => 'cd2626',
+        'firebrick4' => '8b1a1a',
+        'floralwhite' => 'fffaf0',
+        'forestgreen' => '228b22',
+        'fuchsia' => 'ff00ff',
+        'gainsboro' => 'dcdcdc',
+        'ghostwhite' => 'f8f8ff',
+        'gold1' => 'ffd700',
+        'gold2' => 'eec900',
+        'gold3' => 'cdad00',
+        'gold4' => '8b7500',
+        'goldenrod' => 'daa520',
+        'goldenrod1' => 'ffc125',
+        'goldenrod2' => 'eeb422',
+        'goldenrod3' => 'cd9b1d',
+        'goldenrod4' => '8b6914',
+        'gray' => 'bebebe',
+        'gray1' => '030303',
+        'gray10' => '1a1a1a',
+        'gray11' => '1c1c1c',
+        'gray12' => '1f1f1f',
+        'gray13' => '212121',
+        'gray14' => '242424',
+        'gray15' => '262626',
+        'gray16' => '292929',
+        'gray17' => '2b2b2b',
+        'gray18' => '2e2e2e',
+        'gray19' => '303030',
+        'gray2' => '050505',
+        'gray20' => '333333',
+        'gray21' => '363636',
+        'gray22' => '383838',
+        'gray23' => '3b3b3b',
+        'gray24' => '3d3d3d',
+        'gray25' => '404040',
+        'gray26' => '424242',
+        'gray27' => '454545',
+        'gray28' => '474747',
+        'gray29' => '4a4a4a',
+        'gray3' => '080808',
+        'gray30' => '4d4d4d',
+        'gray31' => '4f4f4f',
+        'gray32' => '525252',
+        'gray33' => '545454',
+        'gray34' => '575757',
+        'gray35' => '595959',
+        'gray36' => '5c5c5c',
+        'gray37' => '5e5e5e',
+        'gray38' => '616161',
+        'gray39' => '636363',
+        'gray4' => '0a0a0a',
+        'gray40' => '666666',
+        'gray41' => '696969',
+        'gray42' => '6b6b6b',
+        'gray43' => '6e6e6e',
+        'gray44' => '707070',
+        'gray45' => '737373',
+        'gray46' => '757575',
+        'gray47' => '787878',
+        'gray48' => '7a7a7a',
+        'gray49' => '7d7d7d',
+        'gray5' => '0d0d0d',
+        'gray50' => '7f7f7f',
+        'gray51' => '828282',
+        'gray52' => '858585',
+        'gray53' => '878787',
+        'gray54' => '8a8a8a',
+        'gray55' => '8c8c8c',
+        'gray56' => '8f8f8f',
+        'gray57' => '919191',
+        'gray58' => '949494',
+        'gray59' => '969696',
+        'gray6' => '0f0f0f',
+        'gray60' => '999999',
+        'gray61' => '9c9c9c',
+        'gray62' => '9e9e9e',
+        'gray63' => 'a1a1a1',
+        'gray64' => 'a3a3a3',
+        'gray65' => 'a6a6a6',
+        'gray66' => 'a8a8a8',
+        'gray67' => 'ababab',
+        'gray68' => 'adadad',
+        'gray69' => 'b0b0b0',
+        'gray7' => '121212',
+        'gray70' => 'b3b3b3',
+        'gray71' => 'b5b5b5',
+        'gray72' => 'b8b8b8',
+        'gray73' => 'bababa',
+        'gray74' => 'bdbdbd',
+        'gray75' => 'bfbfbf',
+        'gray76' => 'c2c2c2',
+        'gray77' => 'c4c4c4',
+        'gray78' => 'c7c7c7',
+        'gray79' => 'c9c9c9',
+        'gray8' => '141414',
+        'gray80' => 'cccccc',
+        'gray81' => 'cfcfcf',
+        'gray82' => 'd1d1d1',
+        'gray83' => 'd4d4d4',
+        'gray84' => 'd6d6d6',
+        'gray85' => 'd9d9d9',
+        'gray86' => 'dbdbdb',
+        'gray87' => 'dedede',
+        'gray88' => 'e0e0e0',
+        'gray89' => 'e3e3e3',
+        'gray9' => '171717',
+        'gray90' => 'e5e5e5',
+        'gray91' => 'e8e8e8',
+        'gray92' => 'ebebeb',
+        'gray93' => 'ededed',
+        'gray94' => 'f0f0f0',
+        'gray95' => 'f2f2f2',
+        'gray97' => 'f7f7f7',
+        'gray98' => 'fafafa',
+        'gray99' => 'fcfcfc',
+        'green' => '00ff00',
+        'green1' => '00ff00',
+        'green2' => '00ee00',
+        'green3' => '00cd00',
+        'green4' => '008b00',
+        'greenyellow' => 'adff2f',
+        'honeydew1' => 'f0fff0',
+        'honeydew2' => 'e0eee0',
+        'honeydew3' => 'c1cdc1',
+        'honeydew4' => '838b83',
+        'hotpink' => 'ff69b4',
+        'hotpink1' => 'ff6eb4',
+        'hotpink2' => 'ee6aa7',
+        'hotpink3' => 'cd6090',
+        'hotpink4' => '8b3a62',
+        'indianred' => 'cd5c5c',
+        'indianred1' => 'ff6a6a',
+        'indianred2' => 'ee6363',
+        'indianred3' => 'cd5555',
+        'indianred4' => '8b3a3a',
+        'ivory1' => 'fffff0',
+        'ivory2' => 'eeeee0',
+        'ivory3' => 'cdcdc1',
+        'ivory4' => '8b8b83',
+        'khaki' => 'f0e68c',
+        'khaki1' => 'fff68f',
+        'khaki2' => 'eee685',
+        'khaki3' => 'cdc673',
+        'khaki4' => '8b864e',
+        'lavender' => 'e6e6fa',
+        'lavenderblush1' => 'fff0f5',
+        'lavenderblush2' => 'eee0e5',
+        'lavenderblush3' => 'cdc1c5',
+        'lavenderblush4' => '8b8386',
+        'lawngreen' => '7cfc00',
+        'lemonchiffon1' => 'fffacd',
+        'lemonchiffon2' => 'eee9bf',
+        'lemonchiffon3' => 'cdc9a5',
+        'lemonchiffon4' => '8b8970',
+        'light' => 'eedd82',
+        'lightblue' => 'add8e6',
+        'lightblue1' => 'bfefff',
+        'lightblue2' => 'b2dfee',
+        'lightblue3' => '9ac0cd',
+        'lightblue4' => '68838b',
+        'lightcoral' => 'f08080',
+        'lightcyan1' => 'e0ffff',
+        'lightcyan2' => 'd1eeee',
+        'lightcyan3' => 'b4cdcd',
+        'lightcyan4' => '7a8b8b',
+        'lightgoldenrod1' => 'ffec8b',
+        'lightgoldenrod2' => 'eedc82',
+        'lightgoldenrod3' => 'cdbe70',
+        'lightgoldenrod4' => '8b814c',
+        'lightgoldenrodyellow' => 'fafad2',
+        'lightgray' => 'd3d3d3',
+        'lightpink' => 'ffb6c1',
+        'lightpink1' => 'ffaeb9',
+        'lightpink2' => 'eea2ad',
+        'lightpink3' => 'cd8c95',
+        'lightpink4' => '8b5f65',
+        'lightsalmon1' => 'ffa07a',
+        'lightsalmon2' => 'ee9572',
+        'lightsalmon3' => 'cd8162',
+        'lightsalmon4' => '8b5742',
+        'lightseagreen' => '20b2aa',
+        'lightskyblue' => '87cefa',
+        'lightskyblue1' => 'b0e2ff',
+        'lightskyblue2' => 'a4d3ee',
+        'lightskyblue3' => '8db6cd',
+        'lightskyblue4' => '607b8b',
+        'lightslateblue' => '8470ff',
+        'lightslategray' => '778899',
+        'lightsteelblue' => 'b0c4de',
+        'lightsteelblue1' => 'cae1ff',
+        'lightsteelblue2' => 'bcd2ee',
+        'lightsteelblue3' => 'a2b5cd',
+        'lightsteelblue4' => '6e7b8b',
+        'lightyellow1' => 'ffffe0',
+        'lightyellow2' => 'eeeed1',
+        'lightyellow3' => 'cdcdb4',
+        'lightyellow4' => '8b8b7a',
+        'lime' => '00ff00',
+        'limegreen' => '32cd32',
+        'linen' => 'faf0e6',
+        'magenta' => 'ff00ff',
+        'magenta2' => 'ee00ee',
+        'magenta3' => 'cd00cd',
+        'magenta4' => '8b008b',
+        'maroon' => 'b03060',
+        'maroon1' => 'ff34b3',
+        'maroon2' => 'ee30a7',
+        'maroon3' => 'cd2990',
+        'maroon4' => '8b1c62',
+        'medium' => '66cdaa',
+        'mediumaquamarine' => '66cdaa',
+        'mediumblue' => '0000cd',
+        'mediumorchid' => 'ba55d3',
+        'mediumorchid1' => 'e066ff',
+        'mediumorchid2' => 'd15fee',
+        'mediumorchid3' => 'b452cd',
+        'mediumorchid4' => '7a378b',
+        'mediumpurple' => '9370db',
+        'mediumpurple1' => 'ab82ff',
+        'mediumpurple2' => '9f79ee',
+        'mediumpurple3' => '8968cd',
+        'mediumpurple4' => '5d478b',
+        'mediumseagreen' => '3cb371',
+        'mediumslateblue' => '7b68ee',
+        'mediumspringgreen' => '00fa9a',
+        'mediumturquoise' => '48d1cc',
+        'mediumvioletred' => 'c71585',
+        'midnightblue' => '191970',
+        'mintcream' => 'f5fffa',
+        'mistyrose1' => 'ffe4e1',
+        'mistyrose2' => 'eed5d2',
+        'mistyrose3' => 'cdb7b5',
+        'mistyrose4' => '8b7d7b',
+        'moccasin' => 'ffe4b5',
+        'navajowhite1' => 'ffdead',
+        'navajowhite2' => 'eecfa1',
+        'navajowhite3' => 'cdb38b',
+        'navajowhite4' => '8b795e',
+        'navy' => '000080',
+        'navyblue' => '000080',
+        'oldlace' => 'fdf5e6',
+        'olive' => '808000',
+        'olivedrab' => '6b8e23',
+        'olivedrab1' => 'c0ff3e',
+        'olivedrab2' => 'b3ee3a',
+        'olivedrab4' => '698b22',
+        'orange' => 'ffa500',
+        'orange1' => 'ffa500',
+        'orange2' => 'ee9a00',
+        'orange3' => 'cd8500',
+        'orange4' => '8b5a00',
+        'orangered1' => 'ff4500',
+        'orangered2' => 'ee4000',
+        'orangered3' => 'cd3700',
+        'orangered4' => '8b2500',
+        'orchid' => 'da70d6',
+        'orchid1' => 'ff83fa',
+        'orchid2' => 'ee7ae9',
+        'orchid3' => 'cd69c9',
+        'orchid4' => '8b4789',
+        'pale' => 'db7093',
+        'palegoldenrod' => 'eee8aa',
+        'palegreen' => '98fb98',
+        'palegreen1' => '9aff9a',
+        'palegreen2' => '90ee90',
+        'palegreen3' => '7ccd7c',
+        'palegreen4' => '548b54',
+        'paleturquoise' => 'afeeee',
+        'paleturquoise1' => 'bbffff',
+        'paleturquoise2' => 'aeeeee',
+        'paleturquoise3' => '96cdcd',
+        'paleturquoise4' => '668b8b',
+        'palevioletred' => 'db7093',
+        'palevioletred1' => 'ff82ab',
+        'palevioletred2' => 'ee799f',
+        'palevioletred3' => 'cd6889',
+        'palevioletred4' => '8b475d',
+        'papayawhip' => 'ffefd5',
+        'peachpuff1' => 'ffdab9',
+        'peachpuff2' => 'eecbad',
+        'peachpuff3' => 'cdaf95',
+        'peachpuff4' => '8b7765',
+        'pink' => 'ffc0cb',
+        'pink1' => 'ffb5c5',
+        'pink2' => 'eea9b8',
+        'pink3' => 'cd919e',
+        'pink4' => '8b636c',
+        'plum' => 'dda0dd',
+        'plum1' => 'ffbbff',
+        'plum2' => 'eeaeee',
+        'plum3' => 'cd96cd',
+        'plum4' => '8b668b',
+        'powderblue' => 'b0e0e6',
+        'purple' => 'a020f0',
+        'rebeccapurple' => '663399',
+        'purple1' => '9b30ff',
+        'purple2' => '912cee',
+        'purple3' => '7d26cd',
+        'purple4' => '551a8b',
+        'red' => 'ff0000',
+        'red1' => 'ff0000',
+        'red2' => 'ee0000',
+        'red3' => 'cd0000',
+        'red4' => '8b0000',
+        'rosybrown' => 'bc8f8f',
+        'rosybrown1' => 'ffc1c1',
+        'rosybrown2' => 'eeb4b4',
+        'rosybrown3' => 'cd9b9b',
+        'rosybrown4' => '8b6969',
+        'royalblue' => '4169e1',
+        'royalblue1' => '4876ff',
+        'royalblue2' => '436eee',
+        'royalblue3' => '3a5fcd',
+        'royalblue4' => '27408b',
+        'saddlebrown' => '8b4513',
+        'salmon' => 'fa8072',
+        'salmon1' => 'ff8c69',
+        'salmon2' => 'ee8262',
+        'salmon3' => 'cd7054',
+        'salmon4' => '8b4c39',
+        'sandybrown' => 'f4a460',
+        'seagreen1' => '54ff9f',
+        'seagreen2' => '4eee94',
+        'seagreen3' => '43cd80',
+        'seagreen4' => '2e8b57',
+        'seashell1' => 'fff5ee',
+        'seashell2' => 'eee5de',
+        'seashell3' => 'cdc5bf',
+        'seashell4' => '8b8682',
+        'sienna' => 'a0522d',
+        'sienna1' => 'ff8247',
+        'sienna2' => 'ee7942',
+        'sienna3' => 'cd6839',
+        'sienna4' => '8b4726',
+        'silver' => 'c0c0c0',
+        'skyblue' => '87ceeb',
+        'skyblue1' => '87ceff',
+        'skyblue2' => '7ec0ee',
+        'skyblue3' => '6ca6cd',
+        'skyblue4' => '4a708b',
+        'slateblue' => '6a5acd',
+        'slateblue1' => '836fff',
+        'slateblue2' => '7a67ee',
+        'slateblue3' => '6959cd',
+        'slateblue4' => '473c8b',
+        'slategray' => '708090',
+        'slategray1' => 'c6e2ff',
+        'slategray2' => 'b9d3ee',
+        'slategray3' => '9fb6cd',
+        'slategray4' => '6c7b8b',
+        'snow1' => 'fffafa',
+        'snow2' => 'eee9e9',
+        'snow3' => 'cdc9c9',
+        'snow4' => '8b8989',
+        'springgreen1' => '00ff7f',
+        'springgreen2' => '00ee76',
+        'springgreen3' => '00cd66',
+        'springgreen4' => '008b45',
+        'steelblue' => '4682b4',
+        'steelblue1' => '63b8ff',
+        'steelblue2' => '5cacee',
+        'steelblue3' => '4f94cd',
+        'steelblue4' => '36648b',
+        'tan' => 'd2b48c',
+        'tan1' => 'ffa54f',
+        'tan2' => 'ee9a49',
+        'tan3' => 'cd853f',
+        'tan4' => '8b5a2b',
+        'teal' => '008080',
+        'thistle' => 'd8bfd8',
+        'thistle1' => 'ffe1ff',
+        'thistle2' => 'eed2ee',
+        'thistle3' => 'cdb5cd',
+        'thistle4' => '8b7b8b',
+        'tomato1' => 'ff6347',
+        'tomato2' => 'ee5c42',
+        'tomato3' => 'cd4f39',
+        'tomato4' => '8b3626',
+        'turquoise' => '40e0d0',
+        'turquoise1' => '00f5ff',
+        'turquoise2' => '00e5ee',
+        'turquoise3' => '00c5cd',
+        'turquoise4' => '00868b',
+        'violet' => 'ee82ee',
+        'violetred' => 'd02090',
+        'violetred1' => 'ff3e96',
+        'violetred2' => 'ee3a8c',
+        'violetred3' => 'cd3278',
+        'violetred4' => '8b2252',
+        'wheat' => 'f5deb3',
+        'wheat1' => 'ffe7ba',
+        'wheat2' => 'eed8ae',
+        'wheat3' => 'cdba96',
+        'wheat4' => '8b7e66',
+        'white' => 'ffffff',
+        'whitesmoke' => 'f5f5f5',
+        'yellow' => 'ffff00',
+        'yellow1' => 'ffff00',
+        'yellow2' => 'eeee00',
+        'yellow3' => 'cdcd00',
+        'yellow4' => '8b8b00',
+        'yellowgreen' => '9acd32',
+    ];
+
+    protected $face;
+
+    protected $size;
+
+    protected $color;
+
+    protected $bold = false;
+
+    protected $italic = false;
+
+    protected $underline = false;
+
+    protected $superscript = false;
+
+    protected $subscript = false;
+
+    protected $strikethrough = false;
+
+    protected $startTagCallbacks = [
+        'font' => 'startFontTag',
+        'b' => 'startBoldTag',
+        'strong' => 'startBoldTag',
+        'i' => 'startItalicTag',
+        'em' => 'startItalicTag',
+        'u' => 'startUnderlineTag',
+        'ins' => 'startUnderlineTag',
+        'del' => 'startStrikethruTag',
+        'sup' => 'startSuperscriptTag',
+        'sub' => 'startSubscriptTag',
+    ];
+
+    protected $endTagCallbacks = [
+        'font' => 'endFontTag',
+        'b' => 'endBoldTag',
+        'strong' => 'endBoldTag',
+        'i' => 'endItalicTag',
+        'em' => 'endItalicTag',
+        'u' => 'endUnderlineTag',
+        'ins' => 'endUnderlineTag',
+        'del' => 'endStrikethruTag',
+        'sup' => 'endSuperscriptTag',
+        'sub' => 'endSubscriptTag',
+        'br' => 'breakTag',
+        'p' => 'breakTag',
+        'h1' => 'breakTag',
+        'h2' => 'breakTag',
+        'h3' => 'breakTag',
+        'h4' => 'breakTag',
+        'h5' => 'breakTag',
+        'h6' => 'breakTag',
+    ];
+
+    protected $stack = [];
+
+    protected $stringData = '';
+
+    /**
+     * @var RichText
+     */
+    protected $richTextObject;
+
+    protected function initialise(): void
+    {
+        $this->face = $this->size = $this->color = null;
+        $this->bold = $this->italic = $this->underline = $this->superscript = $this->subscript = $this->strikethrough = false;
+
+        $this->stack = [];
+
+        $this->stringData = '';
+    }
+
+    /**
+     * Parse HTML formatting and return the resulting RichText.
+     *
+     * @param string $html
+     *
+     * @return RichText
+     */
+    public function toRichTextObject($html)
+    {
+        $this->initialise();
+
+        //    Create a new DOM object
+        $dom = new DOMDocument();
+        //    Load the HTML file into the DOM object
+        //  Note the use of error suppression, because typically this will be an html fragment, so not fully valid markup
+        $prefix = '<?xml encoding="UTF-8">';
+        @$dom->loadHTML($prefix . $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
+        //    Discard excess white space
+        $dom->preserveWhiteSpace = false;
+
+        $this->richTextObject = new RichText();
+        $this->parseElements($dom);
+
+        // Clean any further spurious whitespace
+        $this->cleanWhitespace();
+
+        return $this->richTextObject;
+    }
+
+    protected function cleanWhitespace(): void
+    {
+        foreach ($this->richTextObject->getRichTextElements() as $key => $element) {
+            $text = $element->getText();
+            // Trim any leading spaces on the first run
+            if ($key == 0) {
+                $text = ltrim($text);
+            }
+            // Trim any spaces immediately after a line break
+            $text = preg_replace('/\n */mu', "\n", $text);
+            $element->setText($text);
+        }
+    }
+
+    protected function buildTextRun(): void
+    {
+        $text = $this->stringData;
+        if (trim($text) === '') {
+            return;
+        }
+
+        $richtextRun = $this->richTextObject->createTextRun($this->stringData);
+        if ($this->face) {
+            $richtextRun->getFont()->setName($this->face);
+        }
+        if ($this->size) {
+            $richtextRun->getFont()->setSize($this->size);
+        }
+        if ($this->color) {
+            $richtextRun->getFont()->setColor(new Color('ff' . $this->color));
+        }
+        if ($this->bold) {
+            $richtextRun->getFont()->setBold(true);
+        }
+        if ($this->italic) {
+            $richtextRun->getFont()->setItalic(true);
+        }
+        if ($this->underline) {
+            $richtextRun->getFont()->setUnderline(Font::UNDERLINE_SINGLE);
+        }
+        if ($this->superscript) {
+            $richtextRun->getFont()->setSuperscript(true);
+        }
+        if ($this->subscript) {
+            $richtextRun->getFont()->setSubscript(true);
+        }
+        if ($this->strikethrough) {
+            $richtextRun->getFont()->setStrikethrough(true);
+        }
+        $this->stringData = '';
+    }
+
+    protected function rgbToColour($rgb)
+    {
+        preg_match_all('/\d+/', $rgb, $values);
+        foreach ($values[0] as &$value) {
+            $value = str_pad(dechex($value), 2, '0', STR_PAD_LEFT);
+        }
+
+        return implode('', $values[0]);
+    }
+
+    protected function colourNameLookup($rgb)
+    {
+        return self::$colourMap[$rgb];
+    }
+
+    protected function startFontTag($tag): void
+    {
+        foreach ($tag->attributes as $attribute) {
+            $attributeName = strtolower($attribute->name);
+            $attributeValue = $attribute->value;
+
+            if ($attributeName == 'color') {
+                if (preg_match('/rgb\s*\(/', $attributeValue)) {
+                    $this->$attributeName = $this->rgbToColour($attributeValue);
+                } elseif (strpos(trim($attributeValue), '#') === 0) {
+                    $this->$attributeName = ltrim($attributeValue, '#');
+                } else {
+                    $this->$attributeName = $this->colourNameLookup($attributeValue);
+                }
+            } else {
+                $this->$attributeName = $attributeValue;
+            }
+        }
+    }
+
+    protected function endFontTag(): void
+    {
+        $this->face = $this->size = $this->color = null;
+    }
+
+    protected function startBoldTag(): void
+    {
+        $this->bold = true;
+    }
+
+    protected function endBoldTag(): void
+    {
+        $this->bold = false;
+    }
+
+    protected function startItalicTag(): void
+    {
+        $this->italic = true;
+    }
+
+    protected function endItalicTag(): void
+    {
+        $this->italic = false;
+    }
+
+    protected function startUnderlineTag(): void
+    {
+        $this->underline = true;
+    }
+
+    protected function endUnderlineTag(): void
+    {
+        $this->underline = false;
+    }
+
+    protected function startSubscriptTag(): void
+    {
+        $this->subscript = true;
+    }
+
+    protected function endSubscriptTag(): void
+    {
+        $this->subscript = false;
+    }
+
+    protected function startSuperscriptTag(): void
+    {
+        $this->superscript = true;
+    }
+
+    protected function endSuperscriptTag(): void
+    {
+        $this->superscript = false;
+    }
+
+    protected function startStrikethruTag(): void
+    {
+        $this->strikethrough = true;
+    }
+
+    protected function endStrikethruTag(): void
+    {
+        $this->strikethrough = false;
+    }
+
+    protected function breakTag(): void
+    {
+        $this->stringData .= "\n";
+    }
+
+    protected function parseTextNode(DOMText $textNode): void
+    {
+        $domText = preg_replace(
+            '/\s+/u',
+            ' ',
+            str_replace(["\r", "\n"], ' ', $textNode->nodeValue)
+        );
+        $this->stringData .= $domText;
+        $this->buildTextRun();
+    }
+
+    /**
+     * @param string $callbackTag
+     */
+    protected function handleCallback(DOMElement $element, $callbackTag, array $callbacks): void
+    {
+        if (isset($callbacks[$callbackTag])) {
+            $elementHandler = $callbacks[$callbackTag];
+            if (method_exists($this, $elementHandler)) {
+                call_user_func([$this, $elementHandler], $element);
+            }
+        }
+    }
+
+    protected function parseElementNode(DOMElement $element): void
+    {
+        $callbackTag = strtolower($element->nodeName);
+        $this->stack[] = $callbackTag;
+
+        $this->handleCallback($element, $callbackTag, $this->startTagCallbacks);
+
+        $this->parseElements($element);
+        array_pop($this->stack);
+
+        $this->handleCallback($element, $callbackTag, $this->endTagCallbacks);
+    }
+
+    protected function parseElements(DOMNode $element): void
+    {
+        foreach ($element->childNodes as $child) {
+            if ($child instanceof DOMText) {
+                $this->parseTextNode($child);
+            } elseif ($child instanceof DOMElement) {
+                $this->parseElementNode($child);
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Sample.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Sample.php
new file mode 100755
index 0000000..a91b195
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Sample.php
@@ -0,0 +1,229 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Helper;
+
+use PhpOffice\PhpSpreadsheet\IOFactory;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Writer\IWriter;
+use PhpOffice\PhpSpreadsheet\Writer\Pdf;
+use RecursiveDirectoryIterator;
+use RecursiveIteratorIterator;
+use RecursiveRegexIterator;
+use ReflectionClass;
+use RegexIterator;
+use RuntimeException;
+
+/**
+ * Helper class to be used in sample code.
+ */
+class Sample
+{
+    /**
+     * Returns whether we run on CLI or browser.
+     *
+     * @return bool
+     */
+    public function isCli()
+    {
+        return PHP_SAPI === 'cli';
+    }
+
+    /**
+     * Return the filename currently being executed.
+     *
+     * @return string
+     */
+    public function getScriptFilename()
+    {
+        return basename($_SERVER['SCRIPT_FILENAME'], '.php');
+    }
+
+    /**
+     * Whether we are executing the index page.
+     *
+     * @return bool
+     */
+    public function isIndex()
+    {
+        return $this->getScriptFilename() === 'index';
+    }
+
+    /**
+     * Return the page title.
+     *
+     * @return string
+     */
+    public function getPageTitle()
+    {
+        return $this->isIndex() ? 'PHPSpreadsheet' : $this->getScriptFilename();
+    }
+
+    /**
+     * Return the page heading.
+     *
+     * @return string
+     */
+    public function getPageHeading()
+    {
+        return $this->isIndex() ? '' : '<h1>' . str_replace('_', ' ', $this->getScriptFilename()) . '</h1>';
+    }
+
+    /**
+     * Returns an array of all known samples.
+     *
+     * @return string[] [$name => $path]
+     */
+    public function getSamples()
+    {
+        // Populate samples
+        $baseDir = realpath(__DIR__ . '/../../../samples');
+        $directory = new RecursiveDirectoryIterator($baseDir);
+        $iterator = new RecursiveIteratorIterator($directory);
+        $regex = new RegexIterator($iterator, '/^.+\.php$/', RecursiveRegexIterator::GET_MATCH);
+
+        $files = [];
+        foreach ($regex as $file) {
+            $file = str_replace(str_replace('\\', '/', $baseDir) . '/', '', str_replace('\\', '/', $file[0]));
+            $info = pathinfo($file);
+            $category = str_replace('_', ' ', $info['dirname']);
+            $name = str_replace('_', ' ', preg_replace('/(|\.php)/', '', $info['filename']));
+            if (!in_array($category, ['.', 'boostrap', 'templates'])) {
+                if (!isset($files[$category])) {
+                    $files[$category] = [];
+                }
+                $files[$category][$name] = $file;
+            }
+        }
+
+        // Sort everything
+        ksort($files);
+        foreach ($files as &$f) {
+            asort($f);
+        }
+
+        return $files;
+    }
+
+    /**
+     * Write documents.
+     *
+     * @param string $filename
+     * @param string[] $writers
+     */
+    public function write(Spreadsheet $spreadsheet, $filename, array $writers = ['Xlsx', 'Xls']): void
+    {
+        // Set active sheet index to the first sheet, so Excel opens this as the first sheet
+        $spreadsheet->setActiveSheetIndex(0);
+
+        // Write documents
+        foreach ($writers as $writerType) {
+            $path = $this->getFilename($filename, mb_strtolower($writerType));
+            $writer = IOFactory::createWriter($spreadsheet, $writerType);
+            if ($writer instanceof Pdf) {
+                // PDF writer needs temporary directory
+                $tempDir = $this->getTemporaryFolder();
+                $writer->setTempDir($tempDir);
+            }
+            $callStartTime = microtime(true);
+            $writer->save($path);
+            $this->logWrite($writer, $path, $callStartTime);
+        }
+
+        $this->logEndingNotes();
+    }
+
+    /**
+     * Returns the temporary directory and make sure it exists.
+     *
+     * @return string
+     */
+    private function getTemporaryFolder()
+    {
+        $tempFolder = sys_get_temp_dir() . '/phpspreadsheet';
+        if (!is_dir($tempFolder)) {
+            if (!mkdir($tempFolder) && !is_dir($tempFolder)) {
+                throw new RuntimeException(sprintf('Directory "%s" was not created', $tempFolder));
+            }
+        }
+
+        return $tempFolder;
+    }
+
+    /**
+     * Returns the filename that should be used for sample output.
+     *
+     * @param string $filename
+     * @param string $extension
+     *
+     * @return string
+     */
+    public function getFilename($filename, $extension = 'xlsx')
+    {
+        $originalExtension = pathinfo($filename, PATHINFO_EXTENSION);
+
+        return $this->getTemporaryFolder() . '/' . str_replace('.' . $originalExtension, '.' . $extension, basename($filename));
+    }
+
+    /**
+     * Return a random temporary file name.
+     *
+     * @param string $extension
+     *
+     * @return string
+     */
+    public function getTemporaryFilename($extension = 'xlsx')
+    {
+        $temporaryFilename = tempnam($this->getTemporaryFolder(), 'phpspreadsheet-');
+        unlink($temporaryFilename);
+
+        return $temporaryFilename . '.' . $extension;
+    }
+
+    public function log($message): void
+    {
+        $eol = $this->isCli() ? PHP_EOL : '<br />';
+        echo date('H:i:s ') . $message . $eol;
+    }
+
+    /**
+     * Log ending notes.
+     */
+    public function logEndingNotes(): void
+    {
+        // Do not show execution time for index
+        $this->log('Peak memory usage: ' . (memory_get_peak_usage(true) / 1024 / 1024) . 'MB');
+    }
+
+    /**
+     * Log a line about the write operation.
+     *
+     * @param string $path
+     * @param float $callStartTime
+     */
+    public function logWrite(IWriter $writer, $path, $callStartTime): void
+    {
+        $callEndTime = microtime(true);
+        $callTime = $callEndTime - $callStartTime;
+        $reflection = new ReflectionClass($writer);
+        $format = $reflection->getShortName();
+        $message = "Write {$format} format to <code>{$path}</code>  in " . sprintf('%.4f', $callTime) . ' seconds';
+
+        $this->log($message);
+    }
+
+    /**
+     * Log a line about the read operation.
+     *
+     * @param string $format
+     * @param string $path
+     * @param float $callStartTime
+     */
+    public function logRead($format, $path, $callStartTime): void
+    {
+        $callEndTime = microtime(true);
+        $callTime = $callEndTime - $callStartTime;
+        $message = "Read {$format} format from <code>{$path}</code>  in " . sprintf('%.4f', $callTime) . ' seconds';
+
+        $this->log($message);
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IComparable.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IComparable.php
new file mode 100755
index 0000000..c215847
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IComparable.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet;
+
+interface IComparable
+{
+    /**
+     * Get hash code.
+     *
+     * @return string Hash code
+     */
+    public function getHashCode();
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IOFactory.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IOFactory.php
new file mode 100755
index 0000000..ab04e96
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IOFactory.php
@@ -0,0 +1,217 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet;
+
+use PhpOffice\PhpSpreadsheet\Shared\File;
+
+/**
+ * Factory to create readers and writers easily.
+ *
+ * It is not required to use this class, but it should make it easier to read and write files.
+ * Especially for reading files with an unknown format.
+ */
+abstract class IOFactory
+{
+    private static $readers = [
+        'Xlsx' => Reader\Xlsx::class,
+        'Xls' => Reader\Xls::class,
+        'Xml' => Reader\Xml::class,
+        'Ods' => Reader\Ods::class,
+        'Slk' => Reader\Slk::class,
+        'Gnumeric' => Reader\Gnumeric::class,
+        'Html' => Reader\Html::class,
+        'Csv' => Reader\Csv::class,
+    ];
+
+    private static $writers = [
+        'Xls' => Writer\Xls::class,
+        'Xlsx' => Writer\Xlsx::class,
+        'Ods' => Writer\Ods::class,
+        'Csv' => Writer\Csv::class,
+        'Html' => Writer\Html::class,
+        'Tcpdf' => Writer\Pdf\Tcpdf::class,
+        'Dompdf' => Writer\Pdf\Dompdf::class,
+        'Mpdf' => Writer\Pdf\Mpdf::class,
+    ];
+
+    /**
+     * Create Writer\IWriter.
+     *
+     * @param string $writerType Example: Xlsx
+     *
+     * @return Writer\IWriter
+     */
+    public static function createWriter(Spreadsheet $spreadsheet, $writerType)
+    {
+        if (!isset(self::$writers[$writerType])) {
+            throw new Writer\Exception("No writer found for type $writerType");
+        }
+
+        // Instantiate writer
+        $className = self::$writers[$writerType];
+
+        return new $className($spreadsheet);
+    }
+
+    /**
+     * Create Reader\IReader.
+     *
+     * @param string $readerType Example: Xlsx
+     *
+     * @return Reader\IReader
+     */
+    public static function createReader($readerType)
+    {
+        if (!isset(self::$readers[$readerType])) {
+            throw new Reader\Exception("No reader found for type $readerType");
+        }
+
+        // Instantiate reader
+        $className = self::$readers[$readerType];
+
+        return new $className();
+    }
+
+    /**
+     * Loads Spreadsheet from file using automatic Reader\IReader resolution.
+     *
+     * @param string $pFilename The name of the spreadsheet file
+     *
+     * @return Spreadsheet
+     */
+    public static function load($pFilename)
+    {
+        $reader = self::createReaderForFile($pFilename);
+
+        return $reader->load($pFilename);
+    }
+
+    /**
+     * Identify file type using automatic Reader\IReader resolution.
+     *
+     * @param string $pFilename The name of the spreadsheet file to identify
+     *
+     * @return string
+     */
+    public static function identify($pFilename)
+    {
+        $reader = self::createReaderForFile($pFilename);
+        $className = get_class($reader);
+        $classType = explode('\\', $className);
+        unset($reader);
+
+        return array_pop($classType);
+    }
+
+    /**
+     * Create Reader\IReader for file using automatic Reader\IReader resolution.
+     *
+     * @param string $filename The name of the spreadsheet file
+     *
+     * @return Reader\IReader
+     */
+    public static function createReaderForFile($filename)
+    {
+        File::assertFile($filename);
+
+        // First, lucky guess by inspecting file extension
+        $guessedReader = self::getReaderTypeFromExtension($filename);
+        if ($guessedReader !== null) {
+            $reader = self::createReader($guessedReader);
+
+            // Let's see if we are lucky
+            if (isset($reader) && $reader->canRead($filename)) {
+                return $reader;
+            }
+        }
+
+        // If we reach here then "lucky guess" didn't give any result
+        // Try walking through all the options in self::$autoResolveClasses
+        foreach (self::$readers as $type => $class) {
+            //    Ignore our original guess, we know that won't work
+            if ($type !== $guessedReader) {
+                $reader = self::createReader($type);
+                if ($reader->canRead($filename)) {
+                    return $reader;
+                }
+            }
+        }
+
+        throw new Reader\Exception('Unable to identify a reader for this file');
+    }
+
+    /**
+     * Guess a reader type from the file extension, if any.
+     *
+     * @param string $filename
+     *
+     * @return null|string
+     */
+    private static function getReaderTypeFromExtension($filename)
+    {
+        $pathinfo = pathinfo($filename);
+        if (!isset($pathinfo['extension'])) {
+            return null;
+        }
+
+        switch (strtolower($pathinfo['extension'])) {
+            case 'xlsx': // Excel (OfficeOpenXML) Spreadsheet
+            case 'xlsm': // Excel (OfficeOpenXML) Macro Spreadsheet (macros will be discarded)
+            case 'xltx': // Excel (OfficeOpenXML) Template
+            case 'xltm': // Excel (OfficeOpenXML) Macro Template (macros will be discarded)
+                return 'Xlsx';
+            case 'xls': // Excel (BIFF) Spreadsheet
+            case 'xlt': // Excel (BIFF) Template
+                return 'Xls';
+            case 'ods': // Open/Libre Offic Calc
+            case 'ots': // Open/Libre Offic Calc Template
+                return 'Ods';
+            case 'slk':
+                return 'Slk';
+            case 'xml': // Excel 2003 SpreadSheetML
+                return 'Xml';
+            case 'gnumeric':
+                return 'Gnumeric';
+            case 'htm':
+            case 'html':
+                return 'Html';
+            case 'csv':
+                // Do nothing
+                // We must not try to use CSV reader since it loads
+                // all files including Excel files etc.
+                return null;
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * Register a writer with its type and class name.
+     *
+     * @param string $writerType
+     * @param string $writerClass
+     */
+    public static function registerWriter($writerType, $writerClass): void
+    {
+        if (!is_a($writerClass, Writer\IWriter::class, true)) {
+            throw new Writer\Exception('Registered writers must implement ' . Writer\IWriter::class);
+        }
+
+        self::$writers[$writerType] = $writerClass;
+    }
+
+    /**
+     * Register a reader with its type and class name.
+     *
+     * @param string $readerType
+     * @param string $readerClass
+     */
+    public static function registerReader($readerType, $readerClass): void
+    {
+        if (!is_a($readerClass, Reader\IReader::class, true)) {
+            throw new Reader\Exception('Registered readers must implement ' . Reader\IReader::class);
+        }
+
+        self::$readers[$readerType] = $readerClass;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedRange.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedRange.php
new file mode 100755
index 0000000..94fe819
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedRange.php
@@ -0,0 +1,235 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet;
+
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+
+class NamedRange
+{
+    /**
+     * Range name.
+     *
+     * @var string
+     */
+    private $name;
+
+    /**
+     * Worksheet on which the named range can be resolved.
+     *
+     * @var Worksheet
+     */
+    private $worksheet;
+
+    /**
+     * Range of the referenced cells.
+     *
+     * @var string
+     */
+    private $range;
+
+    /**
+     * Is the named range local? (i.e. can only be used on $this->worksheet).
+     *
+     * @var bool
+     */
+    private $localOnly;
+
+    /**
+     * Scope.
+     *
+     * @var Worksheet
+     */
+    private $scope;
+
+    /**
+     * Create a new NamedRange.
+     *
+     * @param string $pName
+     * @param string $pRange
+     * @param bool $pLocalOnly
+     * @param null|Worksheet $pScope Scope. Only applies when $pLocalOnly = true. Null for global scope.
+     */
+    public function __construct($pName, Worksheet $pWorksheet, $pRange = 'A1', $pLocalOnly = false, $pScope = null)
+    {
+        // Validate data
+        if (($pName === null) || ($pWorksheet === null) || ($pRange === null)) {
+            throw new Exception('Parameters can not be null.');
+        }
+
+        // Set local members
+        $this->name = $pName;
+        $this->worksheet = $pWorksheet;
+        $this->range = $pRange;
+        $this->localOnly = $pLocalOnly;
+        $this->scope = ($pLocalOnly == true) ? (($pScope == null) ? $pWorksheet : $pScope) : null;
+    }
+
+    /**
+     * Get name.
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * Set name.
+     *
+     * @param string $value
+     *
+     * @return $this
+     */
+    public function setName($value)
+    {
+        if ($value !== null) {
+            // Old title
+            $oldTitle = $this->name;
+
+            // Re-attach
+            if ($this->worksheet !== null) {
+                $this->worksheet->getParent()->removeNamedRange($this->name, $this->worksheet);
+            }
+            $this->name = $value;
+
+            if ($this->worksheet !== null) {
+                $this->worksheet->getParent()->addNamedRange($this);
+            }
+
+            // New title
+            $newTitle = $this->name;
+            ReferenceHelper::getInstance()->updateNamedFormulas($this->worksheet->getParent(), $oldTitle, $newTitle);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get worksheet.
+     *
+     * @return Worksheet
+     */
+    public function getWorksheet()
+    {
+        return $this->worksheet;
+    }
+
+    /**
+     * Set worksheet.
+     *
+     * @param Worksheet $value
+     *
+     * @return $this
+     */
+    public function setWorksheet(?Worksheet $value = null)
+    {
+        if ($value !== null) {
+            $this->worksheet = $value;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get range.
+     *
+     * @return string
+     */
+    public function getRange()
+    {
+        return $this->range;
+    }
+
+    /**
+     * Set range.
+     *
+     * @param string $value
+     *
+     * @return $this
+     */
+    public function setRange($value)
+    {
+        if ($value !== null) {
+            $this->range = $value;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get localOnly.
+     *
+     * @return bool
+     */
+    public function getLocalOnly()
+    {
+        return $this->localOnly;
+    }
+
+    /**
+     * Set localOnly.
+     *
+     * @param bool $value
+     *
+     * @return $this
+     */
+    public function setLocalOnly($value)
+    {
+        $this->localOnly = $value;
+        $this->scope = $value ? $this->worksheet : null;
+
+        return $this;
+    }
+
+    /**
+     * Get scope.
+     *
+     * @return null|Worksheet
+     */
+    public function getScope()
+    {
+        return $this->scope;
+    }
+
+    /**
+     * Set scope.
+     *
+     * @return $this
+     */
+    public function setScope(?Worksheet $value = null)
+    {
+        $this->scope = $value;
+        $this->localOnly = $value != null;
+
+        return $this;
+    }
+
+    /**
+     * Resolve a named range to a regular cell range.
+     *
+     * @param string $pNamedRange Named range
+     * @param null|Worksheet $pSheet Scope. Use null for global scope
+     *
+     * @return NamedRange
+     */
+    public static function resolveRange($pNamedRange, Worksheet $pSheet)
+    {
+        return $pSheet->getParent()->getNamedRange($pNamedRange, $pSheet);
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $vars = get_object_vars($this);
+        foreach ($vars as $key => $value) {
+            if (is_object($value)) {
+                $this->$key = clone $value;
+            } else {
+                $this->$key = $value;
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/BaseReader.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/BaseReader.php
new file mode 100755
index 0000000..77a6421
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/BaseReader.php
@@ -0,0 +1,158 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader;
+
+use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
+use PhpOffice\PhpSpreadsheet\Shared\File;
+
+abstract class BaseReader implements IReader
+{
+    /**
+     * Read data only?
+     * Identifies whether the Reader should only read data values for cells, and ignore any formatting information;
+     *        or whether it should read both data and formatting.
+     *
+     * @var bool
+     */
+    protected $readDataOnly = false;
+
+    /**
+     * Read empty cells?
+     * Identifies whether the Reader should read data values for cells all cells, or should ignore cells containing
+     *         null value or empty string.
+     *
+     * @var bool
+     */
+    protected $readEmptyCells = true;
+
+    /**
+     * Read charts that are defined in the workbook?
+     * Identifies whether the Reader should read the definitions for any charts that exist in the workbook;.
+     *
+     * @var bool
+     */
+    protected $includeCharts = false;
+
+    /**
+     * Restrict which sheets should be loaded?
+     * This property holds an array of worksheet names to be loaded. If null, then all worksheets will be loaded.
+     *
+     * @var array of string
+     */
+    protected $loadSheetsOnly;
+
+    /**
+     * IReadFilter instance.
+     *
+     * @var IReadFilter
+     */
+    protected $readFilter;
+
+    protected $fileHandle;
+
+    /**
+     * @var XmlScanner
+     */
+    protected $securityScanner;
+
+    public function __construct()
+    {
+        $this->readFilter = new DefaultReadFilter();
+    }
+
+    public function getReadDataOnly()
+    {
+        return $this->readDataOnly;
+    }
+
+    public function setReadDataOnly($pValue)
+    {
+        $this->readDataOnly = (bool) $pValue;
+
+        return $this;
+    }
+
+    public function getReadEmptyCells()
+    {
+        return $this->readEmptyCells;
+    }
+
+    public function setReadEmptyCells($pValue)
+    {
+        $this->readEmptyCells = (bool) $pValue;
+
+        return $this;
+    }
+
+    public function getIncludeCharts()
+    {
+        return $this->includeCharts;
+    }
+
+    public function setIncludeCharts($pValue)
+    {
+        $this->includeCharts = (bool) $pValue;
+
+        return $this;
+    }
+
+    public function getLoadSheetsOnly()
+    {
+        return $this->loadSheetsOnly;
+    }
+
+    public function setLoadSheetsOnly($value)
+    {
+        if ($value === null) {
+            return $this->setLoadAllSheets();
+        }
+
+        $this->loadSheetsOnly = is_array($value) ? $value : [$value];
+
+        return $this;
+    }
+
+    public function setLoadAllSheets()
+    {
+        $this->loadSheetsOnly = null;
+
+        return $this;
+    }
+
+    public function getReadFilter()
+    {
+        return $this->readFilter;
+    }
+
+    public function setReadFilter(IReadFilter $pValue)
+    {
+        $this->readFilter = $pValue;
+
+        return $this;
+    }
+
+    public function getSecurityScanner()
+    {
+        if (property_exists($this, 'securityScanner')) {
+            return $this->securityScanner;
+        }
+
+        return null;
+    }
+
+    /**
+     * Open file for reading.
+     *
+     * @param string $pFilename
+     */
+    protected function openFile($pFilename): void
+    {
+        File::assertFile($pFilename);
+
+        // Open file
+        $this->fileHandle = fopen($pFilename, 'rb');
+        if ($this->fileHandle === false) {
+            throw new Exception('Could not open file ' . $pFilename . ' for reading.');
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv.php
new file mode 100755
index 0000000..db2b50b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv.php
@@ -0,0 +1,533 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader;
+
+use InvalidArgumentException;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+
+class Csv extends BaseReader
+{
+    /**
+     * Input encoding.
+     *
+     * @var string
+     */
+    private $inputEncoding = 'UTF-8';
+
+    /**
+     * Delimiter.
+     *
+     * @var string
+     */
+    private $delimiter;
+
+    /**
+     * Enclosure.
+     *
+     * @var string
+     */
+    private $enclosure = '"';
+
+    /**
+     * Sheet index to read.
+     *
+     * @var int
+     */
+    private $sheetIndex = 0;
+
+    /**
+     * Load rows contiguously.
+     *
+     * @var bool
+     */
+    private $contiguous = false;
+
+    /**
+     * The character that can escape the enclosure.
+     *
+     * @var string
+     */
+    private $escapeCharacter = '\\';
+
+    /**
+     * Create a new CSV Reader instance.
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Set input encoding.
+     *
+     * @param string $pValue Input encoding, eg: 'UTF-8'
+     *
+     * @return $this
+     */
+    public function setInputEncoding($pValue)
+    {
+        $this->inputEncoding = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get input encoding.
+     *
+     * @return string
+     */
+    public function getInputEncoding()
+    {
+        return $this->inputEncoding;
+    }
+
+    /**
+     * Move filepointer past any BOM marker.
+     */
+    protected function skipBOM(): void
+    {
+        rewind($this->fileHandle);
+
+        switch ($this->inputEncoding) {
+            case 'UTF-8':
+                fgets($this->fileHandle, 4) == "\xEF\xBB\xBF" ?
+                    fseek($this->fileHandle, 3) : fseek($this->fileHandle, 0);
+
+                break;
+        }
+    }
+
+    /**
+     * Identify any separator that is explicitly set in the file.
+     */
+    protected function checkSeparator(): void
+    {
+        $line = fgets($this->fileHandle);
+        if ($line === false) {
+            return;
+        }
+
+        if ((strlen(trim($line, "\r\n")) == 5) && (stripos($line, 'sep=') === 0)) {
+            $this->delimiter = substr($line, 4, 1);
+
+            return;
+        }
+
+        $this->skipBOM();
+    }
+
+    /**
+     * Infer the separator if it isn't explicitly set in the file or specified by the user.
+     */
+    protected function inferSeparator(): void
+    {
+        if ($this->delimiter !== null) {
+            return;
+        }
+
+        $potentialDelimiters = [',', ';', "\t", '|', ':', ' ', '~'];
+        $counts = [];
+        foreach ($potentialDelimiters as $delimiter) {
+            $counts[$delimiter] = [];
+        }
+
+        // Count how many times each of the potential delimiters appears in each line
+        $numberLines = 0;
+        while (($line = $this->getNextLine()) !== false && (++$numberLines < 1000)) {
+            $countLine = [];
+            for ($i = strlen($line) - 1; $i >= 0; --$i) {
+                $char = $line[$i];
+                if (isset($counts[$char])) {
+                    if (!isset($countLine[$char])) {
+                        $countLine[$char] = 0;
+                    }
+                    ++$countLine[$char];
+                }
+            }
+            foreach ($potentialDelimiters as $delimiter) {
+                $counts[$delimiter][] = $countLine[$delimiter]
+                    ?? 0;
+            }
+        }
+
+        // If number of lines is 0, nothing to infer : fall back to the default
+        if ($numberLines === 0) {
+            $this->delimiter = reset($potentialDelimiters);
+            $this->skipBOM();
+
+            return;
+        }
+
+        // Calculate the mean square deviations for each delimiter (ignoring delimiters that haven't been found consistently)
+        $meanSquareDeviations = [];
+        $middleIdx = floor(($numberLines - 1) / 2);
+
+        foreach ($potentialDelimiters as $delimiter) {
+            $series = $counts[$delimiter];
+            sort($series);
+
+            $median = ($numberLines % 2)
+                ? $series[$middleIdx]
+                : ($series[$middleIdx] + $series[$middleIdx + 1]) / 2;
+
+            if ($median === 0) {
+                continue;
+            }
+
+            $meanSquareDeviations[$delimiter] = array_reduce(
+                $series,
+                function ($sum, $value) use ($median) {
+                    return $sum + ($value - $median) ** 2;
+                }
+            ) / count($series);
+        }
+
+        // ... and pick the delimiter with the smallest mean square deviation (in case of ties, the order in potentialDelimiters is respected)
+        $min = INF;
+        foreach ($potentialDelimiters as $delimiter) {
+            if (!isset($meanSquareDeviations[$delimiter])) {
+                continue;
+            }
+
+            if ($meanSquareDeviations[$delimiter] < $min) {
+                $min = $meanSquareDeviations[$delimiter];
+                $this->delimiter = $delimiter;
+            }
+        }
+
+        // If no delimiter could be detected, fall back to the default
+        if ($this->delimiter === null) {
+            $this->delimiter = reset($potentialDelimiters);
+        }
+
+        $this->skipBOM();
+    }
+
+    /**
+     * Get the next full line from the file.
+     *
+     * @return false|string
+     */
+    private function getNextLine()
+    {
+        $line = '';
+        $enclosure = '(?<!' . preg_quote($this->escapeCharacter, '/') . ')' . preg_quote($this->enclosure, '/');
+
+        do {
+            // Get the next line in the file
+            $newLine = fgets($this->fileHandle);
+
+            // Return false if there is no next line
+            if ($newLine === false) {
+                return false;
+            }
+
+            // Add the new line to the line passed in
+            $line = $line . $newLine;
+
+            // Drop everything that is enclosed to avoid counting false positives in enclosures
+            $line = preg_replace('/(' . $enclosure . '.*' . $enclosure . ')/Us', '', $line);
+
+            // See if we have any enclosures left in the line
+            // if we still have an enclosure then we need to read the next line as well
+        } while (preg_match('/(' . $enclosure . ')/', $line) > 0);
+
+        return $line;
+    }
+
+    /**
+     * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
+     *
+     * @param string $pFilename
+     *
+     * @return array
+     */
+    public function listWorksheetInfo($pFilename)
+    {
+        // Open file
+        $this->openFileOrMemory($pFilename);
+        $fileHandle = $this->fileHandle;
+
+        // Skip BOM, if any
+        $this->skipBOM();
+        $this->checkSeparator();
+        $this->inferSeparator();
+
+        $worksheetInfo = [];
+        $worksheetInfo[0]['worksheetName'] = 'Worksheet';
+        $worksheetInfo[0]['lastColumnLetter'] = 'A';
+        $worksheetInfo[0]['lastColumnIndex'] = 0;
+        $worksheetInfo[0]['totalRows'] = 0;
+        $worksheetInfo[0]['totalColumns'] = 0;
+
+        // Loop through each line of the file in turn
+        while (($rowData = fgetcsv($fileHandle, 0, $this->delimiter, $this->enclosure, $this->escapeCharacter)) !== false) {
+            ++$worksheetInfo[0]['totalRows'];
+            $worksheetInfo[0]['lastColumnIndex'] = max($worksheetInfo[0]['lastColumnIndex'], count($rowData) - 1);
+        }
+
+        $worksheetInfo[0]['lastColumnLetter'] = Coordinate::stringFromColumnIndex($worksheetInfo[0]['lastColumnIndex'] + 1);
+        $worksheetInfo[0]['totalColumns'] = $worksheetInfo[0]['lastColumnIndex'] + 1;
+
+        // Close file
+        fclose($fileHandle);
+
+        return $worksheetInfo;
+    }
+
+    /**
+     * Loads Spreadsheet from file.
+     *
+     * @param string $pFilename
+     *
+     * @return Spreadsheet
+     */
+    public function load($pFilename)
+    {
+        // Create new Spreadsheet
+        $spreadsheet = new Spreadsheet();
+
+        // Load into this instance
+        return $this->loadIntoExisting($pFilename, $spreadsheet);
+    }
+
+    private function openFileOrMemory($pFilename): void
+    {
+        // Open file
+        $fhandle = $this->canRead($pFilename);
+        if (!$fhandle) {
+            throw new Exception($pFilename . ' is an Invalid Spreadsheet file.');
+        }
+        $this->openFile($pFilename);
+        if ($this->inputEncoding !== 'UTF-8') {
+            fclose($this->fileHandle);
+            $entireFile = file_get_contents($pFilename);
+            $this->fileHandle = fopen('php://memory', 'r+b');
+            $data = StringHelper::convertEncoding($entireFile, 'UTF-8', $this->inputEncoding);
+            fwrite($this->fileHandle, $data);
+            rewind($this->fileHandle);
+        }
+    }
+
+    /**
+     * Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
+     *
+     * @param string $pFilename
+     *
+     * @return Spreadsheet
+     */
+    public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet)
+    {
+        $lineEnding = ini_get('auto_detect_line_endings');
+        ini_set('auto_detect_line_endings', true);
+
+        // Open file
+        $this->openFileOrMemory($pFilename);
+        $fileHandle = $this->fileHandle;
+
+        // Skip BOM, if any
+        $this->skipBOM();
+        $this->checkSeparator();
+        $this->inferSeparator();
+
+        // Create new PhpSpreadsheet object
+        while ($spreadsheet->getSheetCount() <= $this->sheetIndex) {
+            $spreadsheet->createSheet();
+        }
+        $sheet = $spreadsheet->setActiveSheetIndex($this->sheetIndex);
+
+        // Set our starting row based on whether we're in contiguous mode or not
+        $currentRow = 1;
+        $outRow = 0;
+
+        // Loop through each line of the file in turn
+        while (($rowData = fgetcsv($fileHandle, 0, $this->delimiter, $this->enclosure, $this->escapeCharacter)) !== false) {
+            $noOutputYet = true;
+            $columnLetter = 'A';
+            foreach ($rowData as $rowDatum) {
+                if ($rowDatum != '' && $this->readFilter->readCell($columnLetter, $currentRow)) {
+                    if ($this->contiguous) {
+                        if ($noOutputYet) {
+                            $noOutputYet = false;
+                            ++$outRow;
+                        }
+                    } else {
+                        $outRow = $currentRow;
+                    }
+                    // Set cell value
+                    $sheet->getCell($columnLetter . $outRow)->setValue($rowDatum);
+                }
+                ++$columnLetter;
+            }
+            ++$currentRow;
+        }
+
+        // Close file
+        fclose($fileHandle);
+
+        ini_set('auto_detect_line_endings', $lineEnding);
+
+        // Return
+        return $spreadsheet;
+    }
+
+    /**
+     * Get delimiter.
+     *
+     * @return string
+     */
+    public function getDelimiter()
+    {
+        return $this->delimiter;
+    }
+
+    /**
+     * Set delimiter.
+     *
+     * @param string $delimiter Delimiter, eg: ','
+     *
+     * @return $this
+     */
+    public function setDelimiter($delimiter)
+    {
+        $this->delimiter = $delimiter;
+
+        return $this;
+    }
+
+    /**
+     * Get enclosure.
+     *
+     * @return string
+     */
+    public function getEnclosure()
+    {
+        return $this->enclosure;
+    }
+
+    /**
+     * Set enclosure.
+     *
+     * @param string $enclosure Enclosure, defaults to "
+     *
+     * @return $this
+     */
+    public function setEnclosure($enclosure)
+    {
+        if ($enclosure == '') {
+            $enclosure = '"';
+        }
+        $this->enclosure = $enclosure;
+
+        return $this;
+    }
+
+    /**
+     * Get sheet index.
+     *
+     * @return int
+     */
+    public function getSheetIndex()
+    {
+        return $this->sheetIndex;
+    }
+
+    /**
+     * Set sheet index.
+     *
+     * @param int $pValue Sheet index
+     *
+     * @return $this
+     */
+    public function setSheetIndex($pValue)
+    {
+        $this->sheetIndex = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Set Contiguous.
+     *
+     * @param bool $contiguous
+     *
+     * @return $this
+     */
+    public function setContiguous($contiguous)
+    {
+        $this->contiguous = (bool) $contiguous;
+
+        return $this;
+    }
+
+    /**
+     * Get Contiguous.
+     *
+     * @return bool
+     */
+    public function getContiguous()
+    {
+        return $this->contiguous;
+    }
+
+    /**
+     * Set escape backslashes.
+     *
+     * @param string $escapeCharacter
+     *
+     * @return $this
+     */
+    public function setEscapeCharacter($escapeCharacter)
+    {
+        $this->escapeCharacter = $escapeCharacter;
+
+        return $this;
+    }
+
+    /**
+     * Get escape backslashes.
+     *
+     * @return string
+     */
+    public function getEscapeCharacter()
+    {
+        return $this->escapeCharacter;
+    }
+
+    /**
+     * Can the current IReader read the file?
+     *
+     * @param string $pFilename
+     *
+     * @return bool
+     */
+    public function canRead($pFilename)
+    {
+        // Check if file exists
+        try {
+            $this->openFile($pFilename);
+        } catch (InvalidArgumentException $e) {
+            return false;
+        }
+
+        fclose($this->fileHandle);
+
+        // Trust file extension if any
+        $extension = strtolower(pathinfo($pFilename, PATHINFO_EXTENSION));
+        if (in_array($extension, ['csv', 'tsv'])) {
+            return true;
+        }
+
+        // Attempt to guess mimetype
+        $type = mime_content_type($pFilename);
+        $supportedTypes = [
+            'text/csv',
+            'text/plain',
+            'inode/x-empty',
+        ];
+
+        return in_array($type, $supportedTypes, true);
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/DefaultReadFilter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/DefaultReadFilter.php
new file mode 100755
index 0000000..e104186
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/DefaultReadFilter.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader;
+
+class DefaultReadFilter implements IReadFilter
+{
+    /**
+     * Should this cell be read?
+     *
+     * @param string $column Column address (as a string value like "A", or "IV")
+     * @param int $row Row number
+     * @param string $worksheetName Optional worksheet name
+     *
+     * @return bool
+     */
+    public function readCell($column, $row, $worksheetName = '')
+    {
+        return true;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Exception.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Exception.php
new file mode 100755
index 0000000..ddbc9b5
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Exception.php
@@ -0,0 +1,9 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader;
+
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+
+class Exception extends PhpSpreadsheetException
+{
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric.php
new file mode 100755
index 0000000..f976802
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric.php
@@ -0,0 +1,882 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Cell\DataType;
+use PhpOffice\PhpSpreadsheet\NamedRange;
+use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
+use PhpOffice\PhpSpreadsheet\ReferenceHelper;
+use PhpOffice\PhpSpreadsheet\RichText\RichText;
+use PhpOffice\PhpSpreadsheet\Settings;
+use PhpOffice\PhpSpreadsheet\Shared\Date;
+use PhpOffice\PhpSpreadsheet\Shared\File;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Style\Alignment;
+use PhpOffice\PhpSpreadsheet\Style\Border;
+use PhpOffice\PhpSpreadsheet\Style\Borders;
+use PhpOffice\PhpSpreadsheet\Style\Fill;
+use PhpOffice\PhpSpreadsheet\Style\Font;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use XMLReader;
+
+class Gnumeric extends BaseReader
+{
+    /**
+     * Shared Expressions.
+     *
+     * @var array
+     */
+    private $expressions = [];
+
+    private $referenceHelper;
+
+    /**
+     * Create a new Gnumeric.
+     */
+    public function __construct()
+    {
+        parent::__construct();
+        $this->referenceHelper = ReferenceHelper::getInstance();
+        $this->securityScanner = XmlScanner::getInstance($this);
+    }
+
+    /**
+     * Can the current IReader read the file?
+     *
+     * @param string $pFilename
+     *
+     * @return bool
+     */
+    public function canRead($pFilename)
+    {
+        File::assertFile($pFilename);
+
+        // Check if gzlib functions are available
+        if (!function_exists('gzread')) {
+            throw new Exception('gzlib library is not enabled');
+        }
+
+        // Read signature data (first 3 bytes)
+        $fh = fopen($pFilename, 'rb');
+        $data = fread($fh, 2);
+        fclose($fh);
+
+        return $data == chr(0x1F) . chr(0x8B);
+    }
+
+    /**
+     * Reads names of the worksheets from a file, without parsing the whole file to a Spreadsheet object.
+     *
+     * @param string $pFilename
+     *
+     * @return array
+     */
+    public function listWorksheetNames($pFilename)
+    {
+        File::assertFile($pFilename);
+
+        $xml = new XMLReader();
+        $xml->xml($this->securityScanner->scanFile('compress.zlib://' . realpath($pFilename)), null, Settings::getLibXmlLoaderOptions());
+        $xml->setParserProperty(2, true);
+
+        $worksheetNames = [];
+        while ($xml->read()) {
+            if ($xml->name == 'gnm:SheetName' && $xml->nodeType == XMLReader::ELEMENT) {
+                $xml->read(); //    Move onto the value node
+                $worksheetNames[] = (string) $xml->value;
+            } elseif ($xml->name == 'gnm:Sheets') {
+                //    break out of the loop once we've got our sheet names rather than parse the entire file
+                break;
+            }
+        }
+
+        return $worksheetNames;
+    }
+
+    /**
+     * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
+     *
+     * @param string $pFilename
+     *
+     * @return array
+     */
+    public function listWorksheetInfo($pFilename)
+    {
+        File::assertFile($pFilename);
+
+        $xml = new XMLReader();
+        $xml->xml($this->securityScanner->scanFile('compress.zlib://' . realpath($pFilename)), null, Settings::getLibXmlLoaderOptions());
+        $xml->setParserProperty(2, true);
+
+        $worksheetInfo = [];
+        while ($xml->read()) {
+            if ($xml->name == 'gnm:Sheet' && $xml->nodeType == XMLReader::ELEMENT) {
+                $tmpInfo = [
+                    'worksheetName' => '',
+                    'lastColumnLetter' => 'A',
+                    'lastColumnIndex' => 0,
+                    'totalRows' => 0,
+                    'totalColumns' => 0,
+                ];
+
+                while ($xml->read()) {
+                    if ($xml->name == 'gnm:Name' && $xml->nodeType == XMLReader::ELEMENT) {
+                        $xml->read(); //    Move onto the value node
+                        $tmpInfo['worksheetName'] = (string) $xml->value;
+                    } elseif ($xml->name == 'gnm:MaxCol' && $xml->nodeType == XMLReader::ELEMENT) {
+                        $xml->read(); //    Move onto the value node
+                        $tmpInfo['lastColumnIndex'] = (int) $xml->value;
+                        $tmpInfo['totalColumns'] = (int) $xml->value + 1;
+                    } elseif ($xml->name == 'gnm:MaxRow' && $xml->nodeType == XMLReader::ELEMENT) {
+                        $xml->read(); //    Move onto the value node
+                        $tmpInfo['totalRows'] = (int) $xml->value + 1;
+
+                        break;
+                    }
+                }
+                $tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1);
+                $worksheetInfo[] = $tmpInfo;
+            }
+        }
+
+        return $worksheetInfo;
+    }
+
+    /**
+     * @param string $filename
+     *
+     * @return string
+     */
+    private function gzfileGetContents($filename)
+    {
+        $file = @gzopen($filename, 'rb');
+        $data = '';
+        if ($file !== false) {
+            while (!gzeof($file)) {
+                $data .= gzread($file, 1024);
+            }
+            gzclose($file);
+        }
+
+        return $data;
+    }
+
+    /**
+     * Loads Spreadsheet from file.
+     *
+     * @param string $pFilename
+     *
+     * @return Spreadsheet
+     */
+    public function load($pFilename)
+    {
+        // Create new Spreadsheet
+        $spreadsheet = new Spreadsheet();
+
+        // Load into this instance
+        return $this->loadIntoExisting($pFilename, $spreadsheet);
+    }
+
+    /**
+     * Loads from file into Spreadsheet instance.
+     *
+     * @param string $pFilename
+     *
+     * @return Spreadsheet
+     */
+    public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet)
+    {
+        File::assertFile($pFilename);
+
+        $gFileData = $this->gzfileGetContents($pFilename);
+
+        $xml = simplexml_load_string($this->securityScanner->scan($gFileData), 'SimpleXMLElement', Settings::getLibXmlLoaderOptions());
+        $namespacesMeta = $xml->getNamespaces(true);
+
+        $gnmXML = $xml->children($namespacesMeta['gnm']);
+
+        $docProps = $spreadsheet->getProperties();
+        //    Document Properties are held differently, depending on the version of Gnumeric
+        if (isset($namespacesMeta['office'])) {
+            $officeXML = $xml->children($namespacesMeta['office']);
+            $officeDocXML = $officeXML->{'document-meta'};
+            $officeDocMetaXML = $officeDocXML->meta;
+
+            foreach ($officeDocMetaXML as $officePropertyData) {
+                $officePropertyDC = [];
+                if (isset($namespacesMeta['dc'])) {
+                    $officePropertyDC = $officePropertyData->children($namespacesMeta['dc']);
+                }
+                foreach ($officePropertyDC as $propertyName => $propertyValue) {
+                    $propertyValue = (string) $propertyValue;
+                    switch ($propertyName) {
+                        case 'title':
+                            $docProps->setTitle(trim($propertyValue));
+
+                            break;
+                        case 'subject':
+                            $docProps->setSubject(trim($propertyValue));
+
+                            break;
+                        case 'creator':
+                            $docProps->setCreator(trim($propertyValue));
+                            $docProps->setLastModifiedBy(trim($propertyValue));
+
+                            break;
+                        case 'date':
+                            $creationDate = strtotime(trim($propertyValue));
+                            $docProps->setCreated($creationDate);
+                            $docProps->setModified($creationDate);
+
+                            break;
+                        case 'description':
+                            $docProps->setDescription(trim($propertyValue));
+
+                            break;
+                    }
+                }
+                $officePropertyMeta = [];
+                if (isset($namespacesMeta['meta'])) {
+                    $officePropertyMeta = $officePropertyData->children($namespacesMeta['meta']);
+                }
+                foreach ($officePropertyMeta as $propertyName => $propertyValue) {
+                    $attributes = $propertyValue->attributes($namespacesMeta['meta']);
+                    $propertyValue = (string) $propertyValue;
+                    switch ($propertyName) {
+                        case 'keyword':
+                            $docProps->setKeywords(trim($propertyValue));
+
+                            break;
+                        case 'initial-creator':
+                            $docProps->setCreator(trim($propertyValue));
+                            $docProps->setLastModifiedBy(trim($propertyValue));
+
+                            break;
+                        case 'creation-date':
+                            $creationDate = strtotime(trim($propertyValue));
+                            $docProps->setCreated($creationDate);
+                            $docProps->setModified($creationDate);
+
+                            break;
+                        case 'user-defined':
+                            [, $attrName] = explode(':', $attributes['name']);
+                            switch ($attrName) {
+                                case 'publisher':
+                                    $docProps->setCompany(trim($propertyValue));
+
+                                    break;
+                                case 'category':
+                                    $docProps->setCategory(trim($propertyValue));
+
+                                    break;
+                                case 'manager':
+                                    $docProps->setManager(trim($propertyValue));
+
+                                    break;
+                            }
+
+                            break;
+                    }
+                }
+            }
+        } elseif (isset($gnmXML->Summary)) {
+            foreach ($gnmXML->Summary->Item as $summaryItem) {
+                $propertyName = $summaryItem->name;
+                $propertyValue = $summaryItem->{'val-string'};
+                switch ($propertyName) {
+                    case 'title':
+                        $docProps->setTitle(trim($propertyValue));
+
+                        break;
+                    case 'comments':
+                        $docProps->setDescription(trim($propertyValue));
+
+                        break;
+                    case 'keywords':
+                        $docProps->setKeywords(trim($propertyValue));
+
+                        break;
+                    case 'category':
+                        $docProps->setCategory(trim($propertyValue));
+
+                        break;
+                    case 'manager':
+                        $docProps->setManager(trim($propertyValue));
+
+                        break;
+                    case 'author':
+                        $docProps->setCreator(trim($propertyValue));
+                        $docProps->setLastModifiedBy(trim($propertyValue));
+
+                        break;
+                    case 'company':
+                        $docProps->setCompany(trim($propertyValue));
+
+                        break;
+                }
+            }
+        }
+
+        $worksheetID = 0;
+        foreach ($gnmXML->Sheets->Sheet as $sheet) {
+            $worksheetName = (string) $sheet->Name;
+            if ((isset($this->loadSheetsOnly)) && (!in_array($worksheetName, $this->loadSheetsOnly))) {
+                continue;
+            }
+
+            $maxRow = $maxCol = 0;
+
+            // Create new Worksheet
+            $spreadsheet->createSheet();
+            $spreadsheet->setActiveSheetIndex($worksheetID);
+            //    Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in formula
+            //        cells... during the load, all formulae should be correct, and we're simply bringing the worksheet
+            //        name in line with the formula, not the reverse
+            $spreadsheet->getActiveSheet()->setTitle($worksheetName, false, false);
+
+            if ((!$this->readDataOnly) && (isset($sheet->PrintInformation))) {
+                if (isset($sheet->PrintInformation->Margins)) {
+                    foreach ($sheet->PrintInformation->Margins->children('gnm', true) as $key => $margin) {
+                        $marginAttributes = $margin->attributes();
+                        $marginSize = 72 / 100; //    Default
+                        switch ($marginAttributes['PrefUnit']) {
+                            case 'mm':
+                                $marginSize = (int) ($marginAttributes['Points']) / 100;
+
+                                break;
+                        }
+                        switch ($key) {
+                            case 'top':
+                                $spreadsheet->getActiveSheet()->getPageMargins()->setTop($marginSize);
+
+                                break;
+                            case 'bottom':
+                                $spreadsheet->getActiveSheet()->getPageMargins()->setBottom($marginSize);
+
+                                break;
+                            case 'left':
+                                $spreadsheet->getActiveSheet()->getPageMargins()->setLeft($marginSize);
+
+                                break;
+                            case 'right':
+                                $spreadsheet->getActiveSheet()->getPageMargins()->setRight($marginSize);
+
+                                break;
+                            case 'header':
+                                $spreadsheet->getActiveSheet()->getPageMargins()->setHeader($marginSize);
+
+                                break;
+                            case 'footer':
+                                $spreadsheet->getActiveSheet()->getPageMargins()->setFooter($marginSize);
+
+                                break;
+                        }
+                    }
+                }
+            }
+
+            foreach ($sheet->Cells->Cell as $cell) {
+                $cellAttributes = $cell->attributes();
+                $row = (int) $cellAttributes->Row + 1;
+                $column = (int) $cellAttributes->Col;
+
+                if ($row > $maxRow) {
+                    $maxRow = $row;
+                }
+                if ($column > $maxCol) {
+                    $maxCol = $column;
+                }
+
+                $column = Coordinate::stringFromColumnIndex($column + 1);
+
+                // Read cell?
+                if ($this->getReadFilter() !== null) {
+                    if (!$this->getReadFilter()->readCell($column, $row, $worksheetName)) {
+                        continue;
+                    }
+                }
+
+                $ValueType = $cellAttributes->ValueType;
+                $ExprID = (string) $cellAttributes->ExprID;
+                $type = DataType::TYPE_FORMULA;
+                if ($ExprID > '') {
+                    if (((string) $cell) > '') {
+                        $this->expressions[$ExprID] = [
+                            'column' => $cellAttributes->Col,
+                            'row' => $cellAttributes->Row,
+                            'formula' => (string) $cell,
+                        ];
+                    } else {
+                        $expression = $this->expressions[$ExprID];
+
+                        $cell = $this->referenceHelper->updateFormulaReferences(
+                            $expression['formula'],
+                            'A1',
+                            $cellAttributes->Col - $expression['column'],
+                            $cellAttributes->Row - $expression['row'],
+                            $worksheetName
+                        );
+                    }
+                    $type = DataType::TYPE_FORMULA;
+                } else {
+                    switch ($ValueType) {
+                        case '10':        //    NULL
+                            $type = DataType::TYPE_NULL;
+
+                            break;
+                        case '20':        //    Boolean
+                            $type = DataType::TYPE_BOOL;
+                            $cell = $cell == 'TRUE';
+
+                            break;
+                        case '30':        //    Integer
+                            $cell = (int) $cell;
+                            // Excel 2007+ doesn't differentiate between integer and float, so set the value and dropthru to the next (numeric) case
+                            // no break
+                        case '40':        //    Float
+                            $type = DataType::TYPE_NUMERIC;
+
+                            break;
+                        case '50':        //    Error
+                            $type = DataType::TYPE_ERROR;
+
+                            break;
+                        case '60':        //    String
+                            $type = DataType::TYPE_STRING;
+
+                            break;
+                        case '70':        //    Cell Range
+                        case '80':        //    Array
+                    }
+                }
+                $spreadsheet->getActiveSheet()->getCell($column . $row)->setValueExplicit($cell, $type);
+            }
+
+            if ((!$this->readDataOnly) && (isset($sheet->Objects))) {
+                foreach ($sheet->Objects->children('gnm', true) as $key => $comment) {
+                    $commentAttributes = $comment->attributes();
+                    //    Only comment objects are handled at the moment
+                    if ($commentAttributes->Text) {
+                        $spreadsheet->getActiveSheet()->getComment((string) $commentAttributes->ObjectBound)->setAuthor((string) $commentAttributes->Author)->setText($this->parseRichText((string) $commentAttributes->Text));
+                    }
+                }
+            }
+            foreach ($sheet->Styles->StyleRegion as $styleRegion) {
+                $styleAttributes = $styleRegion->attributes();
+                if (($styleAttributes['startRow'] <= $maxRow) &&
+                    ($styleAttributes['startCol'] <= $maxCol)) {
+                    $startColumn = Coordinate::stringFromColumnIndex((int) $styleAttributes['startCol'] + 1);
+                    $startRow = $styleAttributes['startRow'] + 1;
+
+                    $endColumn = ($styleAttributes['endCol'] > $maxCol) ? $maxCol : (int) $styleAttributes['endCol'];
+                    $endColumn = Coordinate::stringFromColumnIndex($endColumn + 1);
+                    $endRow = ($styleAttributes['endRow'] > $maxRow) ? $maxRow : $styleAttributes['endRow'];
+                    ++$endRow;
+                    $cellRange = $startColumn . $startRow . ':' . $endColumn . $endRow;
+
+                    $styleAttributes = $styleRegion->Style->attributes();
+
+                    //    We still set the number format mask for date/time values, even if readDataOnly is true
+                    if ((!$this->readDataOnly) ||
+                        (Date::isDateTimeFormatCode((string) $styleAttributes['Format']))) {
+                        $styleArray = [];
+                        $styleArray['numberFormat']['formatCode'] = (string) $styleAttributes['Format'];
+                        //    If readDataOnly is false, we set all formatting information
+                        if (!$this->readDataOnly) {
+                            switch ($styleAttributes['HAlign']) {
+                                case '1':
+                                    $styleArray['alignment']['horizontal'] = Alignment::HORIZONTAL_GENERAL;
+
+                                    break;
+                                case '2':
+                                    $styleArray['alignment']['horizontal'] = Alignment::HORIZONTAL_LEFT;
+
+                                    break;
+                                case '4':
+                                    $styleArray['alignment']['horizontal'] = Alignment::HORIZONTAL_RIGHT;
+
+                                    break;
+                                case '8':
+                                    $styleArray['alignment']['horizontal'] = Alignment::HORIZONTAL_CENTER;
+
+                                    break;
+                                case '16':
+                                case '64':
+                                    $styleArray['alignment']['horizontal'] = Alignment::HORIZONTAL_CENTER_CONTINUOUS;
+
+                                    break;
+                                case '32':
+                                    $styleArray['alignment']['horizontal'] = Alignment::HORIZONTAL_JUSTIFY;
+
+                                    break;
+                            }
+
+                            switch ($styleAttributes['VAlign']) {
+                                case '1':
+                                    $styleArray['alignment']['vertical'] = Alignment::VERTICAL_TOP;
+
+                                    break;
+                                case '2':
+                                    $styleArray['alignment']['vertical'] = Alignment::VERTICAL_BOTTOM;
+
+                                    break;
+                                case '4':
+                                    $styleArray['alignment']['vertical'] = Alignment::VERTICAL_CENTER;
+
+                                    break;
+                                case '8':
+                                    $styleArray['alignment']['vertical'] = Alignment::VERTICAL_JUSTIFY;
+
+                                    break;
+                            }
+
+                            $styleArray['alignment']['wrapText'] = $styleAttributes['WrapText'] == '1';
+                            $styleArray['alignment']['shrinkToFit'] = $styleAttributes['ShrinkToFit'] == '1';
+                            $styleArray['alignment']['indent'] = ((int) ($styleAttributes['Indent']) > 0) ? $styleAttributes['indent'] : 0;
+
+                            $RGB = self::parseGnumericColour($styleAttributes['Fore']);
+                            $styleArray['font']['color']['rgb'] = $RGB;
+                            $RGB = self::parseGnumericColour($styleAttributes['Back']);
+                            $shade = $styleAttributes['Shade'];
+                            if (($RGB != '000000') || ($shade != '0')) {
+                                $styleArray['fill']['color']['rgb'] = $styleArray['fill']['startColor']['rgb'] = $RGB;
+                                $RGB2 = self::parseGnumericColour($styleAttributes['PatternColor']);
+                                $styleArray['fill']['endColor']['rgb'] = $RGB2;
+                                switch ($shade) {
+                                    case '1':
+                                        $styleArray['fill']['fillType'] = Fill::FILL_SOLID;
+
+                                        break;
+                                    case '2':
+                                        $styleArray['fill']['fillType'] = Fill::FILL_GRADIENT_LINEAR;
+
+                                        break;
+                                    case '3':
+                                        $styleArray['fill']['fillType'] = Fill::FILL_GRADIENT_PATH;
+
+                                        break;
+                                    case '4':
+                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKDOWN;
+
+                                        break;
+                                    case '5':
+                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKGRAY;
+
+                                        break;
+                                    case '6':
+                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKGRID;
+
+                                        break;
+                                    case '7':
+                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKHORIZONTAL;
+
+                                        break;
+                                    case '8':
+                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKTRELLIS;
+
+                                        break;
+                                    case '9':
+                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKUP;
+
+                                        break;
+                                    case '10':
+                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKVERTICAL;
+
+                                        break;
+                                    case '11':
+                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_GRAY0625;
+
+                                        break;
+                                    case '12':
+                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_GRAY125;
+
+                                        break;
+                                    case '13':
+                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTDOWN;
+
+                                        break;
+                                    case '14':
+                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTGRAY;
+
+                                        break;
+                                    case '15':
+                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTGRID;
+
+                                        break;
+                                    case '16':
+                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTHORIZONTAL;
+
+                                        break;
+                                    case '17':
+                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTTRELLIS;
+
+                                        break;
+                                    case '18':
+                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTUP;
+
+                                        break;
+                                    case '19':
+                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTVERTICAL;
+
+                                        break;
+                                    case '20':
+                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_MEDIUMGRAY;
+
+                                        break;
+                                }
+                            }
+
+                            $fontAttributes = $styleRegion->Style->Font->attributes();
+                            $styleArray['font']['name'] = (string) $styleRegion->Style->Font;
+                            $styleArray['font']['size'] = (int) ($fontAttributes['Unit']);
+                            $styleArray['font']['bold'] = $fontAttributes['Bold'] == '1';
+                            $styleArray['font']['italic'] = $fontAttributes['Italic'] == '1';
+                            $styleArray['font']['strikethrough'] = $fontAttributes['StrikeThrough'] == '1';
+                            switch ($fontAttributes['Underline']) {
+                                case '1':
+                                    $styleArray['font']['underline'] = Font::UNDERLINE_SINGLE;
+
+                                    break;
+                                case '2':
+                                    $styleArray['font']['underline'] = Font::UNDERLINE_DOUBLE;
+
+                                    break;
+                                case '3':
+                                    $styleArray['font']['underline'] = Font::UNDERLINE_SINGLEACCOUNTING;
+
+                                    break;
+                                case '4':
+                                    $styleArray['font']['underline'] = Font::UNDERLINE_DOUBLEACCOUNTING;
+
+                                    break;
+                                default:
+                                    $styleArray['font']['underline'] = Font::UNDERLINE_NONE;
+
+                                    break;
+                            }
+                            switch ($fontAttributes['Script']) {
+                                case '1':
+                                    $styleArray['font']['superscript'] = true;
+
+                                    break;
+                                case '-1':
+                                    $styleArray['font']['subscript'] = true;
+
+                                    break;
+                            }
+
+                            if (isset($styleRegion->Style->StyleBorder)) {
+                                if (isset($styleRegion->Style->StyleBorder->Top)) {
+                                    $styleArray['borders']['top'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->Top->attributes());
+                                }
+                                if (isset($styleRegion->Style->StyleBorder->Bottom)) {
+                                    $styleArray['borders']['bottom'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->Bottom->attributes());
+                                }
+                                if (isset($styleRegion->Style->StyleBorder->Left)) {
+                                    $styleArray['borders']['left'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->Left->attributes());
+                                }
+                                if (isset($styleRegion->Style->StyleBorder->Right)) {
+                                    $styleArray['borders']['right'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->Right->attributes());
+                                }
+                                if ((isset($styleRegion->Style->StyleBorder->Diagonal)) && (isset($styleRegion->Style->StyleBorder->{'Rev-Diagonal'}))) {
+                                    $styleArray['borders']['diagonal'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->Diagonal->attributes());
+                                    $styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_BOTH;
+                                } elseif (isset($styleRegion->Style->StyleBorder->Diagonal)) {
+                                    $styleArray['borders']['diagonal'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->Diagonal->attributes());
+                                    $styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_UP;
+                                } elseif (isset($styleRegion->Style->StyleBorder->{'Rev-Diagonal'})) {
+                                    $styleArray['borders']['diagonal'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->{'Rev-Diagonal'}->attributes());
+                                    $styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_DOWN;
+                                }
+                            }
+                            if (isset($styleRegion->Style->HyperLink)) {
+                                //    TO DO
+                                $hyperlink = $styleRegion->Style->HyperLink->attributes();
+                            }
+                        }
+                        $spreadsheet->getActiveSheet()->getStyle($cellRange)->applyFromArray($styleArray);
+                    }
+                }
+            }
+
+            if ((!$this->readDataOnly) && (isset($sheet->Cols))) {
+                //    Column Widths
+                $columnAttributes = $sheet->Cols->attributes();
+                $defaultWidth = $columnAttributes['DefaultSizePts'] / 5.4;
+                $c = 0;
+                foreach ($sheet->Cols->ColInfo as $columnOverride) {
+                    $columnAttributes = $columnOverride->attributes();
+                    $column = $columnAttributes['No'];
+                    $columnWidth = $columnAttributes['Unit'] / 5.4;
+                    $hidden = (isset($columnAttributes['Hidden'])) && ($columnAttributes['Hidden'] == '1');
+                    $columnCount = (isset($columnAttributes['Count'])) ? $columnAttributes['Count'] : 1;
+                    while ($c < $column) {
+                        $spreadsheet->getActiveSheet()->getColumnDimension(Coordinate::stringFromColumnIndex($c + 1))->setWidth($defaultWidth);
+                        ++$c;
+                    }
+                    while (($c < ($column + $columnCount)) && ($c <= $maxCol)) {
+                        $spreadsheet->getActiveSheet()->getColumnDimension(Coordinate::stringFromColumnIndex($c + 1))->setWidth($columnWidth);
+                        if ($hidden) {
+                            $spreadsheet->getActiveSheet()->getColumnDimension(Coordinate::stringFromColumnIndex($c + 1))->setVisible(false);
+                        }
+                        ++$c;
+                    }
+                }
+                while ($c <= $maxCol) {
+                    $spreadsheet->getActiveSheet()->getColumnDimension(Coordinate::stringFromColumnIndex($c + 1))->setWidth($defaultWidth);
+                    ++$c;
+                }
+            }
+
+            if ((!$this->readDataOnly) && (isset($sheet->Rows))) {
+                //    Row Heights
+                $rowAttributes = $sheet->Rows->attributes();
+                $defaultHeight = $rowAttributes['DefaultSizePts'];
+                $r = 0;
+
+                foreach ($sheet->Rows->RowInfo as $rowOverride) {
+                    $rowAttributes = $rowOverride->attributes();
+                    $row = $rowAttributes['No'];
+                    $rowHeight = $rowAttributes['Unit'];
+                    $hidden = (isset($rowAttributes['Hidden'])) && ($rowAttributes['Hidden'] == '1');
+                    $rowCount = (isset($rowAttributes['Count'])) ? $rowAttributes['Count'] : 1;
+                    while ($r < $row) {
+                        ++$r;
+                        $spreadsheet->getActiveSheet()->getRowDimension($r)->setRowHeight($defaultHeight);
+                    }
+                    while (($r < ($row + $rowCount)) && ($r < $maxRow)) {
+                        ++$r;
+                        $spreadsheet->getActiveSheet()->getRowDimension($r)->setRowHeight($rowHeight);
+                        if ($hidden) {
+                            $spreadsheet->getActiveSheet()->getRowDimension($r)->setVisible(false);
+                        }
+                    }
+                }
+                while ($r < $maxRow) {
+                    ++$r;
+                    $spreadsheet->getActiveSheet()->getRowDimension($r)->setRowHeight($defaultHeight);
+                }
+            }
+
+            //    Handle Merged Cells in this worksheet
+            if (isset($sheet->MergedRegions)) {
+                foreach ($sheet->MergedRegions->Merge as $mergeCells) {
+                    if (strpos($mergeCells, ':') !== false) {
+                        $spreadsheet->getActiveSheet()->mergeCells($mergeCells);
+                    }
+                }
+            }
+
+            ++$worksheetID;
+        }
+
+        //    Loop through definedNames (global named ranges)
+        if (isset($gnmXML->Names)) {
+            foreach ($gnmXML->Names->Name as $namedRange) {
+                $name = (string) $namedRange->name;
+                $range = (string) $namedRange->value;
+                if (stripos($range, '#REF!') !== false) {
+                    continue;
+                }
+
+                $range = Worksheet::extractSheetTitle($range, true);
+                $range[0] = trim($range[0], "'");
+                if ($worksheet = $spreadsheet->getSheetByName($range[0])) {
+                    $extractedRange = str_replace('$', '', $range[1]);
+                    $spreadsheet->addNamedRange(new NamedRange($name, $worksheet, $extractedRange));
+                }
+            }
+        }
+
+        // Return
+        return $spreadsheet;
+    }
+
+    private static function parseBorderAttributes($borderAttributes)
+    {
+        $styleArray = [];
+        if (isset($borderAttributes['Color'])) {
+            $styleArray['color']['rgb'] = self::parseGnumericColour($borderAttributes['Color']);
+        }
+
+        switch ($borderAttributes['Style']) {
+            case '0':
+                $styleArray['borderStyle'] = Border::BORDER_NONE;
+
+                break;
+            case '1':
+                $styleArray['borderStyle'] = Border::BORDER_THIN;
+
+                break;
+            case '2':
+                $styleArray['borderStyle'] = Border::BORDER_MEDIUM;
+
+                break;
+            case '3':
+                $styleArray['borderStyle'] = Border::BORDER_SLANTDASHDOT;
+
+                break;
+            case '4':
+                $styleArray['borderStyle'] = Border::BORDER_DASHED;
+
+                break;
+            case '5':
+                $styleArray['borderStyle'] = Border::BORDER_THICK;
+
+                break;
+            case '6':
+                $styleArray['borderStyle'] = Border::BORDER_DOUBLE;
+
+                break;
+            case '7':
+                $styleArray['borderStyle'] = Border::BORDER_DOTTED;
+
+                break;
+            case '8':
+                $styleArray['borderStyle'] = Border::BORDER_MEDIUMDASHED;
+
+                break;
+            case '9':
+                $styleArray['borderStyle'] = Border::BORDER_DASHDOT;
+
+                break;
+            case '10':
+                $styleArray['borderStyle'] = Border::BORDER_MEDIUMDASHDOT;
+
+                break;
+            case '11':
+                $styleArray['borderStyle'] = Border::BORDER_DASHDOTDOT;
+
+                break;
+            case '12':
+                $styleArray['borderStyle'] = Border::BORDER_MEDIUMDASHDOTDOT;
+
+                break;
+            case '13':
+                $styleArray['borderStyle'] = Border::BORDER_MEDIUMDASHDOTDOT;
+
+                break;
+        }
+
+        return $styleArray;
+    }
+
+    private function parseRichText($is)
+    {
+        $value = new RichText();
+        $value->createText($is);
+
+        return $value;
+    }
+
+    private static function parseGnumericColour($gnmColour)
+    {
+        [$gnmR, $gnmG, $gnmB] = explode(':', $gnmColour);
+        $gnmR = substr(str_pad($gnmR, 4, '0', STR_PAD_RIGHT), 0, 2);
+        $gnmG = substr(str_pad($gnmG, 4, '0', STR_PAD_RIGHT), 0, 2);
+        $gnmB = substr(str_pad($gnmB, 4, '0', STR_PAD_RIGHT), 0, 2);
+
+        return $gnmR . $gnmG . $gnmB;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Html.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Html.php
new file mode 100755
index 0000000..2fe85b6
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Html.php
@@ -0,0 +1,952 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader;
+
+use DOMDocument;
+use DOMElement;
+use DOMNode;
+use DOMText;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Style\Border;
+use PhpOffice\PhpSpreadsheet\Style\Color;
+use PhpOffice\PhpSpreadsheet\Style\Fill;
+use PhpOffice\PhpSpreadsheet\Style\Font;
+use PhpOffice\PhpSpreadsheet\Style\Style;
+use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+
+/** PhpSpreadsheet root directory */
+class Html extends BaseReader
+{
+    /**
+     * Sample size to read to determine if it's HTML or not.
+     */
+    const TEST_SAMPLE_SIZE = 2048;
+
+    /**
+     * Input encoding.
+     *
+     * @var string
+     */
+    protected $inputEncoding = 'ANSI';
+
+    /**
+     * Sheet index to read.
+     *
+     * @var int
+     */
+    protected $sheetIndex = 0;
+
+    /**
+     * Formats.
+     *
+     * @var array
+     */
+    protected $formats = [
+        'h1' => [
+            'font' => [
+                'bold' => true,
+                'size' => 24,
+            ],
+        ], //    Bold, 24pt
+        'h2' => [
+            'font' => [
+                'bold' => true,
+                'size' => 18,
+            ],
+        ], //    Bold, 18pt
+        'h3' => [
+            'font' => [
+                'bold' => true,
+                'size' => 13.5,
+            ],
+        ], //    Bold, 13.5pt
+        'h4' => [
+            'font' => [
+                'bold' => true,
+                'size' => 12,
+            ],
+        ], //    Bold, 12pt
+        'h5' => [
+            'font' => [
+                'bold' => true,
+                'size' => 10,
+            ],
+        ], //    Bold, 10pt
+        'h6' => [
+            'font' => [
+                'bold' => true,
+                'size' => 7.5,
+            ],
+        ], //    Bold, 7.5pt
+        'a' => [
+            'font' => [
+                'underline' => true,
+                'color' => [
+                    'argb' => Color::COLOR_BLUE,
+                ],
+            ],
+        ], //    Blue underlined
+        'hr' => [
+            'borders' => [
+                'bottom' => [
+                    'borderStyle' => Border::BORDER_THIN,
+                    'color' => [
+                        Color::COLOR_BLACK,
+                    ],
+                ],
+            ],
+        ], //    Bottom border
+        'strong' => [
+            'font' => [
+                'bold' => true,
+            ],
+        ], //    Bold
+        'b' => [
+            'font' => [
+                'bold' => true,
+            ],
+        ], //    Bold
+        'i' => [
+            'font' => [
+                'italic' => true,
+            ],
+        ], //    Italic
+        'em' => [
+            'font' => [
+                'italic' => true,
+            ],
+        ], //    Italic
+    ];
+
+    protected $rowspan = [];
+
+    /**
+     * Create a new HTML Reader instance.
+     */
+    public function __construct()
+    {
+        parent::__construct();
+        $this->securityScanner = XmlScanner::getInstance($this);
+    }
+
+    /**
+     * Validate that the current file is an HTML file.
+     *
+     * @param string $pFilename
+     *
+     * @return bool
+     */
+    public function canRead($pFilename)
+    {
+        // Check if file exists
+        try {
+            $this->openFile($pFilename);
+        } catch (Exception $e) {
+            return false;
+        }
+
+        $beginning = $this->readBeginning();
+        $startWithTag = self::startsWithTag($beginning);
+        $containsTags = self::containsTags($beginning);
+        $endsWithTag = self::endsWithTag($this->readEnding());
+
+        fclose($this->fileHandle);
+
+        return $startWithTag && $containsTags && $endsWithTag;
+    }
+
+    private function readBeginning()
+    {
+        fseek($this->fileHandle, 0);
+
+        return fread($this->fileHandle, self::TEST_SAMPLE_SIZE);
+    }
+
+    private function readEnding()
+    {
+        $meta = stream_get_meta_data($this->fileHandle);
+        $filename = $meta['uri'];
+
+        $size = filesize($filename);
+        if ($size === 0) {
+            return '';
+        }
+
+        $blockSize = self::TEST_SAMPLE_SIZE;
+        if ($size < $blockSize) {
+            $blockSize = $size;
+        }
+
+        fseek($this->fileHandle, $size - $blockSize);
+
+        return fread($this->fileHandle, $blockSize);
+    }
+
+    private static function startsWithTag($data)
+    {
+        return '<' === substr(trim($data), 0, 1);
+    }
+
+    private static function endsWithTag($data)
+    {
+        return '>' === substr(trim($data), -1, 1);
+    }
+
+    private static function containsTags($data)
+    {
+        return strlen($data) !== strlen(strip_tags($data));
+    }
+
+    /**
+     * Loads Spreadsheet from file.
+     *
+     * @param string $pFilename
+     *
+     * @return Spreadsheet
+     */
+    public function load($pFilename)
+    {
+        // Create new Spreadsheet
+        $spreadsheet = new Spreadsheet();
+
+        // Load into this instance
+        return $this->loadIntoExisting($pFilename, $spreadsheet);
+    }
+
+    /**
+     * Set input encoding.
+     *
+     * @param string $pValue Input encoding, eg: 'ANSI'
+     *
+     * @return $this
+     */
+    public function setInputEncoding($pValue)
+    {
+        $this->inputEncoding = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get input encoding.
+     *
+     * @return string
+     */
+    public function getInputEncoding()
+    {
+        return $this->inputEncoding;
+    }
+
+    //    Data Array used for testing only, should write to Spreadsheet object on completion of tests
+    protected $dataArray = [];
+
+    protected $tableLevel = 0;
+
+    protected $nestedColumn = ['A'];
+
+    protected function setTableStartColumn($column)
+    {
+        if ($this->tableLevel == 0) {
+            $column = 'A';
+        }
+        ++$this->tableLevel;
+        $this->nestedColumn[$this->tableLevel] = $column;
+
+        return $this->nestedColumn[$this->tableLevel];
+    }
+
+    protected function getTableStartColumn()
+    {
+        return $this->nestedColumn[$this->tableLevel];
+    }
+
+    protected function releaseTableStartColumn()
+    {
+        --$this->tableLevel;
+
+        return array_pop($this->nestedColumn);
+    }
+
+    protected function flushCell(Worksheet $sheet, $column, $row, &$cellContent): void
+    {
+        if (is_string($cellContent)) {
+            //    Simple String content
+            if (trim($cellContent) > '') {
+                //    Only actually write it if there's content in the string
+                //    Write to worksheet to be done here...
+                //    ... we return the cell so we can mess about with styles more easily
+                $sheet->setCellValue($column . $row, $cellContent);
+                $this->dataArray[$row][$column] = $cellContent;
+            }
+        } else {
+            //    We have a Rich Text run
+            //    TODO
+            $this->dataArray[$row][$column] = 'RICH TEXT: ' . $cellContent;
+        }
+        $cellContent = (string) '';
+    }
+
+    /**
+     * @param int $row
+     * @param string $column
+     * @param string $cellContent
+     */
+    protected function processDomElement(DOMNode $element, Worksheet $sheet, &$row, &$column, &$cellContent): void
+    {
+        foreach ($element->childNodes as $child) {
+            if ($child instanceof DOMText) {
+                $domText = preg_replace('/\s+/u', ' ', trim($child->nodeValue));
+                if (is_string($cellContent)) {
+                    //    simply append the text if the cell content is a plain text string
+                    $cellContent .= $domText;
+                }
+                //    but if we have a rich text run instead, we need to append it correctly
+                    //    TODO
+            } elseif ($child instanceof DOMElement) {
+                $attributeArray = [];
+                foreach ($child->attributes as $attribute) {
+                    $attributeArray[$attribute->name] = $attribute->value;
+                }
+
+                switch ($child->nodeName) {
+                    case 'meta':
+                        foreach ($attributeArray as $attributeName => $attributeValue) {
+                            // Extract character set, so we can convert to UTF-8 if required
+                            if ($attributeName === 'charset') {
+                                $this->setInputEncoding($attributeValue);
+                            }
+                        }
+                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+
+                        break;
+                    case 'title':
+                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+                        $sheet->setTitle($cellContent, true, false);
+                        $cellContent = '';
+
+                        break;
+                    case 'span':
+                    case 'div':
+                    case 'font':
+                    case 'i':
+                    case 'em':
+                    case 'strong':
+                    case 'b':
+                        if (isset($attributeArray['class']) && $attributeArray['class'] === 'comment') {
+                            $sheet->getComment($column . $row)
+                                ->getText()
+                                ->createTextRun($child->textContent);
+
+                            break;
+                        }
+
+                        if ($cellContent > '') {
+                            $cellContent .= ' ';
+                        }
+                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+                        if ($cellContent > '') {
+                            $cellContent .= ' ';
+                        }
+
+                        if (isset($this->formats[$child->nodeName])) {
+                            $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
+                        }
+
+                        break;
+                    case 'hr':
+                        $this->flushCell($sheet, $column, $row, $cellContent);
+                        ++$row;
+                        if (isset($this->formats[$child->nodeName])) {
+                            $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
+                        } else {
+                            $cellContent = '----------';
+                            $this->flushCell($sheet, $column, $row, $cellContent);
+                        }
+                        ++$row;
+                        // Add a break after a horizontal rule, simply by allowing the code to dropthru
+                        // no break
+                    case 'br':
+                        if ($this->tableLevel > 0) {
+                            //    If we're inside a table, replace with a \n and set the cell to wrap
+                            $cellContent .= "\n";
+                            $sheet->getStyle($column . $row)->getAlignment()->setWrapText(true);
+                        } else {
+                            //    Otherwise flush our existing content and move the row cursor on
+                            $this->flushCell($sheet, $column, $row, $cellContent);
+                            ++$row;
+                        }
+
+                        break;
+                    case 'a':
+                        foreach ($attributeArray as $attributeName => $attributeValue) {
+                            switch ($attributeName) {
+                                case 'href':
+                                    $sheet->getCell($column . $row)->getHyperlink()->setUrl($attributeValue);
+                                    if (isset($this->formats[$child->nodeName])) {
+                                        $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
+                                    }
+
+                                    break;
+                                case 'class':
+                                    if ($attributeValue === 'comment-indicator') {
+                                        break; // Ignore - it's just a red square.
+                                    }
+                            }
+                        }
+                        $cellContent .= ' ';
+                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+
+                        break;
+                    case 'h1':
+                    case 'h2':
+                    case 'h3':
+                    case 'h4':
+                    case 'h5':
+                    case 'h6':
+                    case 'ol':
+                    case 'ul':
+                    case 'p':
+                        if ($this->tableLevel > 0) {
+                            //    If we're inside a table, replace with a \n
+                            $cellContent .= "\n";
+                            $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+                        } else {
+                            if ($cellContent > '') {
+                                $this->flushCell($sheet, $column, $row, $cellContent);
+                                ++$row;
+                            }
+                            $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+                            $this->flushCell($sheet, $column, $row, $cellContent);
+
+                            if (isset($this->formats[$child->nodeName])) {
+                                $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
+                            }
+
+                            ++$row;
+                            $column = 'A';
+                        }
+
+                        break;
+                    case 'li':
+                        if ($this->tableLevel > 0) {
+                            //    If we're inside a table, replace with a \n
+                            $cellContent .= "\n";
+                            $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+                        } else {
+                            if ($cellContent > '') {
+                                $this->flushCell($sheet, $column, $row, $cellContent);
+                            }
+                            ++$row;
+                            $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+                            $this->flushCell($sheet, $column, $row, $cellContent);
+                            $column = 'A';
+                        }
+
+                        break;
+                    case 'img':
+                        $this->insertImage($sheet, $column, $row, $attributeArray);
+
+                        break;
+                    case 'table':
+                        $this->flushCell($sheet, $column, $row, $cellContent);
+                        $column = $this->setTableStartColumn($column);
+                        if ($this->tableLevel > 1) {
+                            --$row;
+                        }
+                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+                        $column = $this->releaseTableStartColumn();
+                        if ($this->tableLevel > 1) {
+                            ++$column;
+                        } else {
+                            ++$row;
+                        }
+
+                        break;
+                    case 'thead':
+                    case 'tbody':
+                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+
+                        break;
+                    case 'tr':
+                        $column = $this->getTableStartColumn();
+                        $cellContent = '';
+                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+
+                        if (isset($attributeArray['height'])) {
+                            $sheet->getRowDimension($row)->setRowHeight($attributeArray['height']);
+                        }
+
+                        ++$row;
+
+                        break;
+                    case 'th':
+                    case 'td':
+                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+
+                        while (isset($this->rowspan[$column . $row])) {
+                            ++$column;
+                        }
+
+                        // apply inline style
+                        $this->applyInlineStyle($sheet, $row, $column, $attributeArray);
+
+                        $this->flushCell($sheet, $column, $row, $cellContent);
+
+                        if (isset($attributeArray['rowspan'], $attributeArray['colspan'])) {
+                            //create merging rowspan and colspan
+                            $columnTo = $column;
+                            for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) {
+                                ++$columnTo;
+                            }
+                            $range = $column . $row . ':' . $columnTo . ($row + (int) $attributeArray['rowspan'] - 1);
+                            foreach (Coordinate::extractAllCellReferencesInRange($range) as $value) {
+                                $this->rowspan[$value] = true;
+                            }
+                            $sheet->mergeCells($range);
+                            $column = $columnTo;
+                        } elseif (isset($attributeArray['rowspan'])) {
+                            //create merging rowspan
+                            $range = $column . $row . ':' . $column . ($row + (int) $attributeArray['rowspan'] - 1);
+                            foreach (Coordinate::extractAllCellReferencesInRange($range) as $value) {
+                                $this->rowspan[$value] = true;
+                            }
+                            $sheet->mergeCells($range);
+                        } elseif (isset($attributeArray['colspan'])) {
+                            //create merging colspan
+                            $columnTo = $column;
+                            for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) {
+                                ++$columnTo;
+                            }
+                            $sheet->mergeCells($column . $row . ':' . $columnTo . $row);
+                            $column = $columnTo;
+                        } elseif (isset($attributeArray['bgcolor'])) {
+                            $sheet->getStyle($column . $row)->applyFromArray(
+                                [
+                                    'fill' => [
+                                        'fillType' => Fill::FILL_SOLID,
+                                        'color' => ['rgb' => $attributeArray['bgcolor']],
+                                    ],
+                                ]
+                            );
+                        }
+
+                        if (isset($attributeArray['width'])) {
+                            $sheet->getColumnDimension($column)->setWidth($attributeArray['width']);
+                        }
+
+                        if (isset($attributeArray['height'])) {
+                            $sheet->getRowDimension($row)->setRowHeight($attributeArray['height']);
+                        }
+
+                        if (isset($attributeArray['align'])) {
+                            $sheet->getStyle($column . $row)->getAlignment()->setHorizontal($attributeArray['align']);
+                        }
+
+                        if (isset($attributeArray['valign'])) {
+                            $sheet->getStyle($column . $row)->getAlignment()->setVertical($attributeArray['valign']);
+                        }
+
+                        if (isset($attributeArray['data-format'])) {
+                            $sheet->getStyle($column . $row)->getNumberFormat()->setFormatCode($attributeArray['data-format']);
+                        }
+
+                        ++$column;
+
+                        break;
+                    case 'body':
+                        $row = 1;
+                        $column = 'A';
+                        $cellContent = '';
+                        $this->tableLevel = 0;
+                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+
+                        break;
+                    default:
+                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+                }
+            }
+        }
+    }
+
+    /**
+     * Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
+     *
+     * @param string $pFilename
+     *
+     * @return Spreadsheet
+     */
+    public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet)
+    {
+        // Validate
+        if (!$this->canRead($pFilename)) {
+            throw new Exception($pFilename . ' is an Invalid HTML file.');
+        }
+
+        // Create a new DOM object
+        $dom = new DOMDocument();
+        // Reload the HTML file into the DOM object
+        $loaded = $dom->loadHTML(mb_convert_encoding($this->securityScanner->scanFile($pFilename), 'HTML-ENTITIES', 'UTF-8'));
+        if ($loaded === false) {
+            throw new Exception('Failed to load ' . $pFilename . ' as a DOM Document');
+        }
+
+        return $this->loadDocument($dom, $spreadsheet);
+    }
+
+    /**
+     * Spreadsheet from content.
+     *
+     * @param string $content
+     */
+    public function loadFromString($content, ?Spreadsheet $spreadsheet = null): Spreadsheet
+    {
+        //    Create a new DOM object
+        $dom = new DOMDocument();
+        //    Reload the HTML file into the DOM object
+        $loaded = $dom->loadHTML(mb_convert_encoding($this->securityScanner->scan($content), 'HTML-ENTITIES', 'UTF-8'));
+        if ($loaded === false) {
+            throw new Exception('Failed to load content as a DOM Document');
+        }
+
+        return $this->loadDocument($dom, $spreadsheet ?? new Spreadsheet());
+    }
+
+    /**
+     * Loads PhpSpreadsheet from DOMDocument into PhpSpreadsheet instance.
+     */
+    private function loadDocument(DOMDocument $document, Spreadsheet $spreadsheet): Spreadsheet
+    {
+        while ($spreadsheet->getSheetCount() <= $this->sheetIndex) {
+            $spreadsheet->createSheet();
+        }
+        $spreadsheet->setActiveSheetIndex($this->sheetIndex);
+
+        // Discard white space
+        $document->preserveWhiteSpace = false;
+
+        $row = 0;
+        $column = 'A';
+        $content = '';
+        $this->rowspan = [];
+        $this->processDomElement($document, $spreadsheet->getActiveSheet(), $row, $column, $content);
+
+        // Return
+        return $spreadsheet;
+    }
+
+    /**
+     * Get sheet index.
+     *
+     * @return int
+     */
+    public function getSheetIndex()
+    {
+        return $this->sheetIndex;
+    }
+
+    /**
+     * Set sheet index.
+     *
+     * @param int $pValue Sheet index
+     *
+     * @return $this
+     */
+    public function setSheetIndex($pValue)
+    {
+        $this->sheetIndex = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Apply inline css inline style.
+     *
+     * NOTES :
+     * Currently only intended for td & th element,
+     * and only takes 'background-color' and 'color'; property with HEX color
+     *
+     * TODO :
+     * - Implement to other propertie, such as border
+     *
+     * @param Worksheet $sheet
+     * @param int $row
+     * @param string $column
+     * @param array $attributeArray
+     */
+    private function applyInlineStyle(&$sheet, $row, $column, $attributeArray): void
+    {
+        if (!isset($attributeArray['style'])) {
+            return;
+        }
+
+        $cellStyle = $sheet->getStyle($column . $row);
+
+        // add color styles (background & text) from dom element,currently support : td & th, using ONLY inline css style with RGB color
+        $styles = explode(';', $attributeArray['style']);
+        foreach ($styles as $st) {
+            $value = explode(':', $st);
+            $styleName = isset($value[0]) ? trim($value[0]) : null;
+            $styleValue = isset($value[1]) ? trim($value[1]) : null;
+
+            if (!$styleName) {
+                continue;
+            }
+
+            switch ($styleName) {
+                case 'background':
+                case 'background-color':
+                    $styleColor = $this->getStyleColor($styleValue);
+
+                    if (!$styleColor) {
+                        continue 2;
+                    }
+
+                    $cellStyle->applyFromArray(['fill' => ['fillType' => Fill::FILL_SOLID, 'color' => ['rgb' => $styleColor]]]);
+
+                    break;
+                case 'color':
+                    $styleColor = $this->getStyleColor($styleValue);
+
+                    if (!$styleColor) {
+                        continue 2;
+                    }
+
+                    $cellStyle->applyFromArray(['font' => ['color' => ['rgb' => $styleColor]]]);
+
+                    break;
+
+                case 'border':
+                    $this->setBorderStyle($cellStyle, $styleValue, 'allBorders');
+
+                    break;
+
+                case 'border-top':
+                    $this->setBorderStyle($cellStyle, $styleValue, 'top');
+
+                    break;
+
+                case 'border-bottom':
+                    $this->setBorderStyle($cellStyle, $styleValue, 'bottom');
+
+                    break;
+
+                case 'border-left':
+                    $this->setBorderStyle($cellStyle, $styleValue, 'left');
+
+                    break;
+
+                case 'border-right':
+                    $this->setBorderStyle($cellStyle, $styleValue, 'right');
+
+                    break;
+
+                case 'font-size':
+                    $cellStyle->getFont()->setSize(
+                        (float) $styleValue
+                    );
+
+                    break;
+
+                case 'font-weight':
+                    if ($styleValue === 'bold' || $styleValue >= 500) {
+                        $cellStyle->getFont()->setBold(true);
+                    }
+
+                    break;
+
+                case 'font-style':
+                    if ($styleValue === 'italic') {
+                        $cellStyle->getFont()->setItalic(true);
+                    }
+
+                    break;
+
+                case 'font-family':
+                    $cellStyle->getFont()->setName(str_replace('\'', '', $styleValue));
+
+                    break;
+
+                case 'text-decoration':
+                    switch ($styleValue) {
+                        case 'underline':
+                            $cellStyle->getFont()->setUnderline(Font::UNDERLINE_SINGLE);
+
+                            break;
+                        case 'line-through':
+                            $cellStyle->getFont()->setStrikethrough(true);
+
+                            break;
+                    }
+
+                    break;
+
+                case 'text-align':
+                    $cellStyle->getAlignment()->setHorizontal($styleValue);
+
+                    break;
+
+                case 'vertical-align':
+                    $cellStyle->getAlignment()->setVertical($styleValue);
+
+                    break;
+
+                case 'width':
+                    $sheet->getColumnDimension($column)->setWidth(
+                        str_replace('px', '', $styleValue)
+                    );
+
+                    break;
+
+                case 'height':
+                    $sheet->getRowDimension($row)->setRowHeight(
+                        str_replace('px', '', $styleValue)
+                    );
+
+                    break;
+
+                case 'word-wrap':
+                    $cellStyle->getAlignment()->setWrapText(
+                        $styleValue === 'break-word'
+                    );
+
+                    break;
+
+                case 'text-indent':
+                    $cellStyle->getAlignment()->setIndent(
+                        (int) str_replace(['px'], '', $styleValue)
+                    );
+
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Check if has #, so we can get clean hex.
+     *
+     * @param $value
+     *
+     * @return null|string
+     */
+    public function getStyleColor($value)
+    {
+        if (strpos($value, '#') === 0) {
+            return substr($value, 1);
+        }
+
+        return null;
+    }
+
+    /**
+     * @param string    $column
+     * @param int       $row
+     */
+    private function insertImage(Worksheet $sheet, $column, $row, array $attributes): void
+    {
+        if (!isset($attributes['src'])) {
+            return;
+        }
+
+        $src = urldecode($attributes['src']);
+        $width = isset($attributes['width']) ? (float) $attributes['width'] : null;
+        $height = isset($attributes['height']) ? (float) $attributes['height'] : null;
+        $name = isset($attributes['alt']) ? (float) $attributes['alt'] : null;
+
+        $drawing = new Drawing();
+        $drawing->setPath($src);
+        $drawing->setWorksheet($sheet);
+        $drawing->setCoordinates($column . $row);
+        $drawing->setOffsetX(0);
+        $drawing->setOffsetY(10);
+        $drawing->setResizeProportional(true);
+
+        if ($name) {
+            $drawing->setName($name);
+        }
+
+        if ($width) {
+            $drawing->setWidth((int) $width);
+        }
+
+        if ($height) {
+            $drawing->setHeight((int) $height);
+        }
+
+        $sheet->getColumnDimension($column)->setWidth(
+            $drawing->getWidth() / 6
+        );
+
+        $sheet->getRowDimension($row)->setRowHeight(
+            $drawing->getHeight() * 0.9
+        );
+    }
+
+    /**
+     * Map html border style to PhpSpreadsheet border style.
+     *
+     * @param  string $style
+     *
+     * @return null|string
+     */
+    public function getBorderStyle($style)
+    {
+        switch ($style) {
+            case 'solid':
+                return Border::BORDER_THIN;
+            case 'dashed':
+                return Border::BORDER_DASHED;
+            case 'dotted':
+                return Border::BORDER_DOTTED;
+            case 'medium':
+                return Border::BORDER_MEDIUM;
+            case 'thick':
+                return Border::BORDER_THICK;
+            case 'none':
+                return Border::BORDER_NONE;
+            case 'dash-dot':
+                return Border::BORDER_DASHDOT;
+            case 'dash-dot-dot':
+                return Border::BORDER_DASHDOTDOT;
+            case 'double':
+                return Border::BORDER_DOUBLE;
+            case 'hair':
+                return Border::BORDER_HAIR;
+            case 'medium-dash-dot':
+                return Border::BORDER_MEDIUMDASHDOT;
+            case 'medium-dash-dot-dot':
+                return Border::BORDER_MEDIUMDASHDOTDOT;
+            case 'medium-dashed':
+                return Border::BORDER_MEDIUMDASHED;
+            case 'slant-dash-dot':
+                return Border::BORDER_SLANTDASHDOT;
+        }
+
+        return null;
+    }
+
+    /**
+     * @param string $styleValue
+     * @param string $type
+     */
+    private function setBorderStyle(Style $cellStyle, $styleValue, $type): void
+    {
+        if (trim($styleValue) === Border::BORDER_NONE) {
+            $borderStyle = Border::BORDER_NONE;
+            $color = null;
+        } else {
+            [, $borderStyle, $color] = explode(' ', $styleValue);
+        }
+
+        $cellStyle->applyFromArray([
+            'borders' => [
+                $type => [
+                    'borderStyle' => $this->getBorderStyle($borderStyle),
+                    'color' => ['rgb' => $this->getStyleColor($color)],
+                ],
+            ],
+        ]);
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReadFilter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReadFilter.php
new file mode 100755
index 0000000..ccfe05a
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReadFilter.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader;
+
+interface IReadFilter
+{
+    /**
+     * Should this cell be read?
+     *
+     * @param string $column Column address (as a string value like "A", or "IV")
+     * @param int $row Row number
+     * @param string $worksheetName Optional worksheet name
+     *
+     * @return bool
+     */
+    public function readCell($column, $row, $worksheetName = '');
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReader.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReader.php
new file mode 100755
index 0000000..a8bd360
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReader.php
@@ -0,0 +1,133 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader;
+
+interface IReader
+{
+    /**
+     * IReader constructor.
+     */
+    public function __construct();
+
+    /**
+     * Can the current IReader read the file?
+     *
+     * @param string $pFilename
+     *
+     * @return bool
+     */
+    public function canRead($pFilename);
+
+    /**
+     * Read data only?
+     *        If this is true, then the Reader will only read data values for cells, it will not read any formatting information.
+     *        If false (the default) it will read data and formatting.
+     *
+     * @return bool
+     */
+    public function getReadDataOnly();
+
+    /**
+     * Set read data only
+     *        Set to true, to advise the Reader only to read data values for cells, and to ignore any formatting information.
+     *        Set to false (the default) to advise the Reader to read both data and formatting for cells.
+     *
+     * @param bool $pValue
+     *
+     * @return IReader
+     */
+    public function setReadDataOnly($pValue);
+
+    /**
+     * Read empty cells?
+     *        If this is true (the default), then the Reader will read data values for all cells, irrespective of value.
+     *        If false it will not read data for cells containing a null value or an empty string.
+     *
+     * @return bool
+     */
+    public function getReadEmptyCells();
+
+    /**
+     * Set read empty cells
+     *        Set to true (the default) to advise the Reader read data values for all cells, irrespective of value.
+     *        Set to false to advise the Reader to ignore cells containing a null value or an empty string.
+     *
+     * @param bool $pValue
+     *
+     * @return IReader
+     */
+    public function setReadEmptyCells($pValue);
+
+    /**
+     * Read charts in workbook?
+     *        If this is true, then the Reader will include any charts that exist in the workbook.
+     *      Note that a ReadDataOnly value of false overrides, and charts won't be read regardless of the IncludeCharts value.
+     *        If false (the default) it will ignore any charts defined in the workbook file.
+     *
+     * @return bool
+     */
+    public function getIncludeCharts();
+
+    /**
+     * Set read charts in workbook
+     *        Set to true, to advise the Reader to include any charts that exist in the workbook.
+     *      Note that a ReadDataOnly value of false overrides, and charts won't be read regardless of the IncludeCharts value.
+     *        Set to false (the default) to discard charts.
+     *
+     * @param bool $pValue
+     *
+     * @return IReader
+     */
+    public function setIncludeCharts($pValue);
+
+    /**
+     * Get which sheets to load
+     * Returns either an array of worksheet names (the list of worksheets that should be loaded), or a null
+     *        indicating that all worksheets in the workbook should be loaded.
+     *
+     * @return mixed
+     */
+    public function getLoadSheetsOnly();
+
+    /**
+     * Set which sheets to load.
+     *
+     * @param mixed $value
+     *        This should be either an array of worksheet names to be loaded, or a string containing a single worksheet name.
+     *        If NULL, then it tells the Reader to read all worksheets in the workbook
+     *
+     * @return IReader
+     */
+    public function setLoadSheetsOnly($value);
+
+    /**
+     * Set all sheets to load
+     *        Tells the Reader to load all worksheets from the workbook.
+     *
+     * @return IReader
+     */
+    public function setLoadAllSheets();
+
+    /**
+     * Read filter.
+     *
+     * @return IReadFilter
+     */
+    public function getReadFilter();
+
+    /**
+     * Set read filter.
+     *
+     * @return IReader
+     */
+    public function setReadFilter(IReadFilter $pValue);
+
+    /**
+     * Loads PhpSpreadsheet from file.
+     *
+     * @param string $pFilename
+     *
+     * @return \PhpOffice\PhpSpreadsheet\Spreadsheet
+     */
+    public function load($pFilename);
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods.php
new file mode 100755
index 0000000..d638d1f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods.php
@@ -0,0 +1,699 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader;
+
+use DateTime;
+use DateTimeZone;
+use DOMAttr;
+use DOMDocument;
+use DOMElement;
+use DOMNode;
+use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Cell\DataType;
+use PhpOffice\PhpSpreadsheet\Reader\Ods\Properties as DocumentProperties;
+use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
+use PhpOffice\PhpSpreadsheet\RichText\RichText;
+use PhpOffice\PhpSpreadsheet\Settings;
+use PhpOffice\PhpSpreadsheet\Shared\Date;
+use PhpOffice\PhpSpreadsheet\Shared\File;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
+use XMLReader;
+use ZipArchive;
+
+class Ods extends BaseReader
+{
+    /**
+     * Create a new Ods Reader instance.
+     */
+    public function __construct()
+    {
+        parent::__construct();
+        $this->securityScanner = XmlScanner::getInstance($this);
+    }
+
+    /**
+     * Can the current IReader read the file?
+     *
+     * @param string $pFilename
+     *
+     * @return bool
+     */
+    public function canRead($pFilename)
+    {
+        File::assertFile($pFilename);
+
+        $mimeType = 'UNKNOWN';
+
+        // Load file
+
+        $zip = new ZipArchive();
+        if ($zip->open($pFilename) === true) {
+            // check if it is an OOXML archive
+            $stat = $zip->statName('mimetype');
+            if ($stat && ($stat['size'] <= 255)) {
+                $mimeType = $zip->getFromName($stat['name']);
+            } elseif ($zip->statName('META-INF/manifest.xml')) {
+                $xml = simplexml_load_string(
+                    $this->securityScanner->scan($zip->getFromName('META-INF/manifest.xml')),
+                    'SimpleXMLElement',
+                    Settings::getLibXmlLoaderOptions()
+                );
+                $namespacesContent = $xml->getNamespaces(true);
+                if (isset($namespacesContent['manifest'])) {
+                    $manifest = $xml->children($namespacesContent['manifest']);
+                    foreach ($manifest as $manifestDataSet) {
+                        $manifestAttributes = $manifestDataSet->attributes($namespacesContent['manifest']);
+                        if ($manifestAttributes->{'full-path'} == '/') {
+                            $mimeType = (string) $manifestAttributes->{'media-type'};
+
+                            break;
+                        }
+                    }
+                }
+            }
+
+            $zip->close();
+
+            return $mimeType === 'application/vnd.oasis.opendocument.spreadsheet';
+        }
+
+        return false;
+    }
+
+    /**
+     * Reads names of the worksheets from a file, without parsing the whole file to a PhpSpreadsheet object.
+     *
+     * @param string $pFilename
+     *
+     * @return string[]
+     */
+    public function listWorksheetNames($pFilename)
+    {
+        File::assertFile($pFilename);
+
+        $zip = new ZipArchive();
+        if (!$zip->open($pFilename)) {
+            throw new Exception('Could not open ' . $pFilename . ' for reading! Error opening file.');
+        }
+
+        $worksheetNames = [];
+
+        $xml = new XMLReader();
+        $xml->xml(
+            $this->securityScanner->scanFile('zip://' . realpath($pFilename) . '#content.xml'),
+            null,
+            Settings::getLibXmlLoaderOptions()
+        );
+        $xml->setParserProperty(2, true);
+
+        // Step into the first level of content of the XML
+        $xml->read();
+        while ($xml->read()) {
+            // Quickly jump through to the office:body node
+            while ($xml->name !== 'office:body') {
+                if ($xml->isEmptyElement) {
+                    $xml->read();
+                } else {
+                    $xml->next();
+                }
+            }
+            // Now read each node until we find our first table:table node
+            while ($xml->read()) {
+                if ($xml->name == 'table:table' && $xml->nodeType == XMLReader::ELEMENT) {
+                    // Loop through each table:table node reading the table:name attribute for each worksheet name
+                    do {
+                        $worksheetNames[] = $xml->getAttribute('table:name');
+                        $xml->next();
+                    } while ($xml->name == 'table:table' && $xml->nodeType == XMLReader::ELEMENT);
+                }
+            }
+        }
+
+        return $worksheetNames;
+    }
+
+    /**
+     * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
+     *
+     * @param string $pFilename
+     *
+     * @return array
+     */
+    public function listWorksheetInfo($pFilename)
+    {
+        File::assertFile($pFilename);
+
+        $worksheetInfo = [];
+
+        $zip = new ZipArchive();
+        if (!$zip->open($pFilename)) {
+            throw new Exception('Could not open ' . $pFilename . ' for reading! Error opening file.');
+        }
+
+        $xml = new XMLReader();
+        $xml->xml(
+            $this->securityScanner->scanFile('zip://' . realpath($pFilename) . '#content.xml'),
+            null,
+            Settings::getLibXmlLoaderOptions()
+        );
+        $xml->setParserProperty(2, true);
+
+        // Step into the first level of content of the XML
+        $xml->read();
+        while ($xml->read()) {
+            // Quickly jump through to the office:body node
+            while ($xml->name !== 'office:body') {
+                if ($xml->isEmptyElement) {
+                    $xml->read();
+                } else {
+                    $xml->next();
+                }
+            }
+            // Now read each node until we find our first table:table node
+            while ($xml->read()) {
+                if ($xml->name == 'table:table' && $xml->nodeType == XMLReader::ELEMENT) {
+                    $worksheetNames[] = $xml->getAttribute('table:name');
+
+                    $tmpInfo = [
+                        'worksheetName' => $xml->getAttribute('table:name'),
+                        'lastColumnLetter' => 'A',
+                        'lastColumnIndex' => 0,
+                        'totalRows' => 0,
+                        'totalColumns' => 0,
+                    ];
+
+                    // Loop through each child node of the table:table element reading
+                    $currCells = 0;
+                    do {
+                        $xml->read();
+                        if ($xml->name == 'table:table-row' && $xml->nodeType == XMLReader::ELEMENT) {
+                            $rowspan = $xml->getAttribute('table:number-rows-repeated');
+                            $rowspan = empty($rowspan) ? 1 : $rowspan;
+                            $tmpInfo['totalRows'] += $rowspan;
+                            $tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells);
+                            $currCells = 0;
+                            // Step into the row
+                            $xml->read();
+                            do {
+                                if ($xml->name == 'table:table-cell' && $xml->nodeType == XMLReader::ELEMENT) {
+                                    if (!$xml->isEmptyElement) {
+                                        ++$currCells;
+                                        $xml->next();
+                                    } else {
+                                        $xml->read();
+                                    }
+                                } elseif ($xml->name == 'table:covered-table-cell' && $xml->nodeType == XMLReader::ELEMENT) {
+                                    $mergeSize = $xml->getAttribute('table:number-columns-repeated');
+                                    $currCells += (int) $mergeSize;
+                                    $xml->read();
+                                } else {
+                                    $xml->read();
+                                }
+                            } while ($xml->name != 'table:table-row');
+                        }
+                    } while ($xml->name != 'table:table');
+
+                    $tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells);
+                    $tmpInfo['lastColumnIndex'] = $tmpInfo['totalColumns'] - 1;
+                    $tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1);
+                    $worksheetInfo[] = $tmpInfo;
+                }
+            }
+        }
+
+        return $worksheetInfo;
+    }
+
+    /**
+     * Loads PhpSpreadsheet from file.
+     *
+     * @param string $pFilename
+     *
+     * @return Spreadsheet
+     */
+    public function load($pFilename)
+    {
+        // Create new Spreadsheet
+        $spreadsheet = new Spreadsheet();
+
+        // Load into this instance
+        return $this->loadIntoExisting($pFilename, $spreadsheet);
+    }
+
+    /**
+     * Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
+     *
+     * @param string $pFilename
+     *
+     * @return Spreadsheet
+     */
+    public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet)
+    {
+        File::assertFile($pFilename);
+
+        $timezoneObj = new DateTimeZone('Europe/London');
+        $GMT = new DateTimeZone('UTC');
+
+        $zip = new ZipArchive();
+        if (!$zip->open($pFilename)) {
+            throw new Exception("Could not open {$pFilename} for reading! Error opening file.");
+        }
+
+        // Meta
+
+        $xml = simplexml_load_string(
+            $this->securityScanner->scan($zip->getFromName('meta.xml')),
+            'SimpleXMLElement',
+            Settings::getLibXmlLoaderOptions()
+        );
+        if ($xml === false) {
+            throw new Exception('Unable to read data from {$pFilename}');
+        }
+
+        $namespacesMeta = $xml->getNamespaces(true);
+
+        (new DocumentProperties($spreadsheet))->load($xml, $namespacesMeta);
+
+        // Content
+
+        $dom = new DOMDocument('1.01', 'UTF-8');
+        $dom->loadXML(
+            $this->securityScanner->scan($zip->getFromName('content.xml')),
+            Settings::getLibXmlLoaderOptions()
+        );
+
+        $officeNs = $dom->lookupNamespaceUri('office');
+        $tableNs = $dom->lookupNamespaceUri('table');
+        $textNs = $dom->lookupNamespaceUri('text');
+        $xlinkNs = $dom->lookupNamespaceUri('xlink');
+
+        $spreadsheets = $dom->getElementsByTagNameNS($officeNs, 'body')
+            ->item(0)
+            ->getElementsByTagNameNS($officeNs, 'spreadsheet');
+
+        foreach ($spreadsheets as $workbookData) {
+            /** @var DOMElement $workbookData */
+            $tables = $workbookData->getElementsByTagNameNS($tableNs, 'table');
+
+            $worksheetID = 0;
+            foreach ($tables as $worksheetDataSet) {
+                /** @var DOMElement $worksheetDataSet */
+                $worksheetName = $worksheetDataSet->getAttributeNS($tableNs, 'name');
+
+                // Check loadSheetsOnly
+                if (isset($this->loadSheetsOnly)
+                    && $worksheetName
+                    && !in_array($worksheetName, $this->loadSheetsOnly)) {
+                    continue;
+                }
+
+                // Create sheet
+                if ($worksheetID > 0) {
+                    $spreadsheet->createSheet(); // First sheet is added by default
+                }
+                $spreadsheet->setActiveSheetIndex($worksheetID);
+
+                if ($worksheetName) {
+                    // Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in
+                    // formula cells... during the load, all formulae should be correct, and we're simply
+                    // bringing the worksheet name in line with the formula, not the reverse
+                    $spreadsheet->getActiveSheet()->setTitle($worksheetName, false, false);
+                }
+
+                // Go through every child of table element
+                $rowID = 1;
+                foreach ($worksheetDataSet->childNodes as $childNode) {
+                    /** @var DOMElement $childNode */
+
+                    // Filter elements which are not under the "table" ns
+                    if ($childNode->namespaceURI != $tableNs) {
+                        continue;
+                    }
+
+                    $key = $childNode->nodeName;
+
+                    // Remove ns from node name
+                    if (strpos($key, ':') !== false) {
+                        $keyChunks = explode(':', $key);
+                        $key = array_pop($keyChunks);
+                    }
+
+                    switch ($key) {
+                        case 'table-header-rows':
+                            /// TODO :: Figure this out. This is only a partial implementation I guess.
+                            //          ($rowData it's not used at all and I'm not sure that PHPExcel
+                            //          has an API for this)
+
+//                            foreach ($rowData as $keyRowData => $cellData) {
+//                                $rowData = $cellData;
+//                                break;
+//                            }
+                            break;
+                        case 'table-row':
+                            if ($childNode->hasAttributeNS($tableNs, 'number-rows-repeated')) {
+                                $rowRepeats = $childNode->getAttributeNS($tableNs, 'number-rows-repeated');
+                            } else {
+                                $rowRepeats = 1;
+                            }
+
+                            $columnID = 'A';
+                            foreach ($childNode->childNodes as $key => $cellData) {
+                                // @var \DOMElement $cellData
+
+                                if ($this->getReadFilter() !== null) {
+                                    if (!$this->getReadFilter()->readCell($columnID, $rowID, $worksheetName)) {
+                                        ++$columnID;
+
+                                        continue;
+                                    }
+                                }
+
+                                // Initialize variables
+                                $formatting = $hyperlink = null;
+                                $hasCalculatedValue = false;
+                                $cellDataFormula = '';
+
+                                if ($cellData->hasAttributeNS($tableNs, 'formula')) {
+                                    $cellDataFormula = $cellData->getAttributeNS($tableNs, 'formula');
+                                    $hasCalculatedValue = true;
+                                }
+
+                                // Annotations
+                                $annotation = $cellData->getElementsByTagNameNS($officeNs, 'annotation');
+
+                                if ($annotation->length > 0) {
+                                    $textNode = $annotation->item(0)->getElementsByTagNameNS($textNs, 'p');
+
+                                    if ($textNode->length > 0) {
+                                        $text = $this->scanElementForText($textNode->item(0));
+
+                                        $spreadsheet->getActiveSheet()
+                                            ->getComment($columnID . $rowID)
+                                            ->setText($this->parseRichText($text));
+//                                                                    ->setAuthor( $author )
+                                    }
+                                }
+
+                                // Content
+
+                                /** @var DOMElement[] $paragraphs */
+                                $paragraphs = [];
+
+                                foreach ($cellData->childNodes as $item) {
+                                    /** @var DOMElement $item */
+
+                                    // Filter text:p elements
+                                    if ($item->nodeName == 'text:p') {
+                                        $paragraphs[] = $item;
+                                    }
+                                }
+
+                                if (count($paragraphs) > 0) {
+                                    // Consolidate if there are multiple p records (maybe with spans as well)
+                                    $dataArray = [];
+
+                                    // Text can have multiple text:p and within those, multiple text:span.
+                                    // text:p newlines, but text:span does not.
+                                    // Also, here we assume there is no text data is span fields are specified, since
+                                    // we have no way of knowing proper positioning anyway.
+
+                                    foreach ($paragraphs as $pData) {
+                                        $dataArray[] = $this->scanElementForText($pData);
+                                    }
+                                    $allCellDataText = implode("\n", $dataArray);
+
+                                    $type = $cellData->getAttributeNS($officeNs, 'value-type');
+
+                                    switch ($type) {
+                                        case 'string':
+                                            $type = DataType::TYPE_STRING;
+                                            $dataValue = $allCellDataText;
+
+                                            foreach ($paragraphs as $paragraph) {
+                                                $link = $paragraph->getElementsByTagNameNS($textNs, 'a');
+                                                if ($link->length > 0) {
+                                                    $hyperlink = $link->item(0)->getAttributeNS($xlinkNs, 'href');
+                                                }
+                                            }
+
+                                            break;
+                                        case 'boolean':
+                                            $type = DataType::TYPE_BOOL;
+                                            $dataValue = ($allCellDataText == 'TRUE') ? true : false;
+
+                                            break;
+                                        case 'percentage':
+                                            $type = DataType::TYPE_NUMERIC;
+                                            $dataValue = (float) $cellData->getAttributeNS($officeNs, 'value');
+
+                                            if (floor($dataValue) == $dataValue) {
+                                                $dataValue = (int) $dataValue;
+                                            }
+                                            $formatting = NumberFormat::FORMAT_PERCENTAGE_00;
+
+                                            break;
+                                        case 'currency':
+                                            $type = DataType::TYPE_NUMERIC;
+                                            $dataValue = (float) $cellData->getAttributeNS($officeNs, 'value');
+
+                                            if (floor($dataValue) == $dataValue) {
+                                                $dataValue = (int) $dataValue;
+                                            }
+                                            $formatting = NumberFormat::FORMAT_CURRENCY_USD_SIMPLE;
+
+                                            break;
+                                        case 'float':
+                                            $type = DataType::TYPE_NUMERIC;
+                                            $dataValue = (float) $cellData->getAttributeNS($officeNs, 'value');
+
+                                            if (floor($dataValue) == $dataValue) {
+                                                if ($dataValue == (int) $dataValue) {
+                                                    $dataValue = (int) $dataValue;
+                                                } else {
+                                                    $dataValue = (float) $dataValue;
+                                                }
+                                            }
+
+                                            break;
+                                        case 'date':
+                                            $type = DataType::TYPE_NUMERIC;
+                                            $value = $cellData->getAttributeNS($officeNs, 'date-value');
+
+                                            $dateObj = new DateTime($value, $GMT);
+                                            $dateObj->setTimeZone($timezoneObj);
+                                            [$year, $month, $day, $hour, $minute, $second] = explode(
+                                                ' ',
+                                                $dateObj->format('Y m d H i s')
+                                            );
+
+                                            $dataValue = Date::formattedPHPToExcel(
+                                                (int) $year,
+                                                (int) $month,
+                                                (int) $day,
+                                                (int) $hour,
+                                                (int) $minute,
+                                                (int) $second
+                                            );
+
+                                            if ($dataValue != floor($dataValue)) {
+                                                $formatting = NumberFormat::FORMAT_DATE_XLSX15
+                                                    . ' '
+                                                    . NumberFormat::FORMAT_DATE_TIME4;
+                                            } else {
+                                                $formatting = NumberFormat::FORMAT_DATE_XLSX15;
+                                            }
+
+                                            break;
+                                        case 'time':
+                                            $type = DataType::TYPE_NUMERIC;
+
+                                            $timeValue = $cellData->getAttributeNS($officeNs, 'time-value');
+
+                                            $dataValue = Date::PHPToExcel(
+                                                strtotime(
+                                                    '01-01-1970 ' . implode(':', sscanf($timeValue, 'PT%dH%dM%dS'))
+                                                )
+                                            );
+                                            $formatting = NumberFormat::FORMAT_DATE_TIME4;
+
+                                            break;
+                                        default:
+                                            $dataValue = null;
+                                    }
+                                } else {
+                                    $type = DataType::TYPE_NULL;
+                                    $dataValue = null;
+                                }
+
+                                if ($hasCalculatedValue) {
+                                    $type = DataType::TYPE_FORMULA;
+                                    $cellDataFormula = substr($cellDataFormula, strpos($cellDataFormula, ':=') + 1);
+                                    $temp = explode('"', $cellDataFormula);
+                                    $tKey = false;
+                                    foreach ($temp as &$value) {
+                                        // Only replace in alternate array entries (i.e. non-quoted blocks)
+                                        if ($tKey = !$tKey) {
+                                            // Cell range reference in another sheet
+                                            $value = preg_replace('/\[([^\.]+)\.([^\.]+):\.([^\.]+)\]/U', '$1!$2:$3', $value);
+
+                                            // Cell reference in another sheet
+                                            $value = preg_replace('/\[([^\.]+)\.([^\.]+)\]/U', '$1!$2', $value);
+
+                                            // Cell range reference
+                                            $value = preg_replace('/\[\.([^\.]+):\.([^\.]+)\]/U', '$1:$2', $value);
+
+                                            // Simple cell reference
+                                            $value = preg_replace('/\[\.([^\.]+)\]/U', '$1', $value);
+
+                                            $value = Calculation::translateSeparator(';', ',', $value, $inBraces);
+                                        }
+                                    }
+                                    unset($value);
+
+                                    // Then rebuild the formula string
+                                    $cellDataFormula = implode('"', $temp);
+                                }
+
+                                if ($cellData->hasAttributeNS($tableNs, 'number-columns-repeated')) {
+                                    $colRepeats = (int) $cellData->getAttributeNS($tableNs, 'number-columns-repeated');
+                                } else {
+                                    $colRepeats = 1;
+                                }
+
+                                if ($type !== null) {
+                                    for ($i = 0; $i < $colRepeats; ++$i) {
+                                        if ($i > 0) {
+                                            ++$columnID;
+                                        }
+
+                                        if ($type !== DataType::TYPE_NULL) {
+                                            for ($rowAdjust = 0; $rowAdjust < $rowRepeats; ++$rowAdjust) {
+                                                $rID = $rowID + $rowAdjust;
+
+                                                $cell = $spreadsheet->getActiveSheet()
+                                                    ->getCell($columnID . $rID);
+
+                                                // Set value
+                                                if ($hasCalculatedValue) {
+                                                    $cell->setValueExplicit($cellDataFormula, $type);
+                                                } else {
+                                                    $cell->setValueExplicit($dataValue, $type);
+                                                }
+
+                                                if ($hasCalculatedValue) {
+                                                    $cell->setCalculatedValue($dataValue);
+                                                }
+
+                                                // Set other properties
+                                                if ($formatting !== null) {
+                                                    $spreadsheet->getActiveSheet()
+                                                        ->getStyle($columnID . $rID)
+                                                        ->getNumberFormat()
+                                                        ->setFormatCode($formatting);
+                                                } else {
+                                                    $spreadsheet->getActiveSheet()
+                                                        ->getStyle($columnID . $rID)
+                                                        ->getNumberFormat()
+                                                        ->setFormatCode(NumberFormat::FORMAT_GENERAL);
+                                                }
+
+                                                if ($hyperlink !== null) {
+                                                    $cell->getHyperlink()
+                                                        ->setUrl($hyperlink);
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+
+                                // Merged cells
+                                if ($cellData->hasAttributeNS($tableNs, 'number-columns-spanned')
+                                    || $cellData->hasAttributeNS($tableNs, 'number-rows-spanned')
+                                ) {
+                                    if (($type !== DataType::TYPE_NULL) || (!$this->readDataOnly)) {
+                                        $columnTo = $columnID;
+
+                                        if ($cellData->hasAttributeNS($tableNs, 'number-columns-spanned')) {
+                                            $columnIndex = Coordinate::columnIndexFromString($columnID);
+                                            $columnIndex += (int) $cellData->getAttributeNS($tableNs, 'number-columns-spanned');
+                                            $columnIndex -= 2;
+
+                                            $columnTo = Coordinate::stringFromColumnIndex($columnIndex + 1);
+                                        }
+
+                                        $rowTo = $rowID;
+
+                                        if ($cellData->hasAttributeNS($tableNs, 'number-rows-spanned')) {
+                                            $rowTo = $rowTo + (int) $cellData->getAttributeNS($tableNs, 'number-rows-spanned') - 1;
+                                        }
+
+                                        $cellRange = $columnID . $rowID . ':' . $columnTo . $rowTo;
+                                        $spreadsheet->getActiveSheet()->mergeCells($cellRange);
+                                    }
+                                }
+
+                                ++$columnID;
+                            }
+                            $rowID += $rowRepeats;
+
+                            break;
+                    }
+                }
+                ++$worksheetID;
+            }
+        }
+
+        // Return
+        return $spreadsheet;
+    }
+
+    /**
+     * Recursively scan element.
+     *
+     * @return string
+     */
+    protected function scanElementForText(DOMNode $element)
+    {
+        $str = '';
+        foreach ($element->childNodes as $child) {
+            /** @var DOMNode $child */
+            if ($child->nodeType == XML_TEXT_NODE) {
+                $str .= $child->nodeValue;
+            } elseif ($child->nodeType == XML_ELEMENT_NODE && $child->nodeName == 'text:s') {
+                // It's a space
+
+                // Multiple spaces?
+                /** @var DOMAttr $cAttr */
+                $cAttr = $child->attributes->getNamedItem('c');
+                if ($cAttr) {
+                    $multiplier = (int) $cAttr->nodeValue;
+                } else {
+                    $multiplier = 1;
+                }
+
+                $str .= str_repeat(' ', $multiplier);
+            }
+
+            if ($child->hasChildNodes()) {
+                $str .= $this->scanElementForText($child);
+            }
+        }
+
+        return $str;
+    }
+
+    /**
+     * @param string $is
+     *
+     * @return RichText
+     */
+    private function parseRichText($is)
+    {
+        $value = new RichText();
+        $value->createText($is);
+
+        return $value;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php
new file mode 100755
index 0000000..c0b309a
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php
@@ -0,0 +1,136 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Ods;
+
+use PhpOffice\PhpSpreadsheet\Document\Properties as DocumentProperties;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use SimpleXMLElement;
+
+class Properties
+{
+    private $spreadsheet;
+
+    public function __construct(Spreadsheet $spreadsheet)
+    {
+        $this->spreadsheet = $spreadsheet;
+    }
+
+    public function load(SimpleXMLElement $xml, $namespacesMeta): void
+    {
+        $docProps = $this->spreadsheet->getProperties();
+        $officeProperty = $xml->children($namespacesMeta['office']);
+        foreach ($officeProperty as $officePropertyData) {
+            // @var \SimpleXMLElement $officePropertyData
+            if (isset($namespacesMeta['dc'])) {
+                $officePropertiesDC = $officePropertyData->children($namespacesMeta['dc']);
+                $this->setCoreProperties($docProps, $officePropertiesDC);
+            }
+
+            $officePropertyMeta = (object) [];
+            if (isset($namespacesMeta['dc'])) {
+                $officePropertyMeta = $officePropertyData->children($namespacesMeta['meta']);
+            }
+            foreach ($officePropertyMeta as $propertyName => $propertyValue) {
+                $this->setMetaProperties($namespacesMeta, $propertyValue, $propertyName, $docProps);
+            }
+        }
+    }
+
+    private function setCoreProperties(DocumentProperties $docProps, SimpleXMLElement $officePropertyDC): void
+    {
+        foreach ($officePropertyDC as $propertyName => $propertyValue) {
+            $propertyValue = (string) $propertyValue;
+            switch ($propertyName) {
+                case 'title':
+                    $docProps->setTitle($propertyValue);
+
+                    break;
+                case 'subject':
+                    $docProps->setSubject($propertyValue);
+
+                    break;
+                case 'creator':
+                    $docProps->setCreator($propertyValue);
+                    $docProps->setLastModifiedBy($propertyValue);
+
+                    break;
+                case 'creation-date':
+                    $creationDate = strtotime($propertyValue);
+                    $docProps->setCreated($creationDate);
+                    $docProps->setModified($creationDate);
+
+                    break;
+                case 'keyword':
+                    $docProps->setKeywords($propertyValue);
+
+                    break;
+                case 'description':
+                    $docProps->setDescription($propertyValue);
+
+                    break;
+            }
+        }
+    }
+
+    private function setMetaProperties(
+        $namespacesMeta,
+        SimpleXMLElement $propertyValue,
+        $propertyName,
+        DocumentProperties $docProps
+    ): void {
+        $propertyValueAttributes = $propertyValue->attributes($namespacesMeta['meta']);
+        $propertyValue = (string) $propertyValue;
+        switch ($propertyName) {
+            case 'initial-creator':
+                $docProps->setCreator($propertyValue);
+
+                break;
+            case 'keyword':
+                $docProps->setKeywords($propertyValue);
+
+                break;
+            case 'creation-date':
+                $creationDate = strtotime($propertyValue);
+                $docProps->setCreated($creationDate);
+
+                break;
+            case 'user-defined':
+                $this->setUserDefinedProperty($propertyValueAttributes, $propertyValue, $docProps);
+
+                break;
+        }
+    }
+
+    private function setUserDefinedProperty($propertyValueAttributes, $propertyValue, DocumentProperties $docProps): void
+    {
+        $propertyValueName = '';
+        $propertyValueType = DocumentProperties::PROPERTY_TYPE_STRING;
+        foreach ($propertyValueAttributes as $key => $value) {
+            if ($key == 'name') {
+                $propertyValueName = (string) $value;
+            } elseif ($key == 'value-type') {
+                switch ($value) {
+                    case 'date':
+                        $propertyValue = DocumentProperties::convertProperty($propertyValue, 'date');
+                        $propertyValueType = DocumentProperties::PROPERTY_TYPE_DATE;
+
+                        break;
+                    case 'boolean':
+                        $propertyValue = DocumentProperties::convertProperty($propertyValue, 'bool');
+                        $propertyValueType = DocumentProperties::PROPERTY_TYPE_BOOLEAN;
+
+                        break;
+                    case 'float':
+                        $propertyValue = DocumentProperties::convertProperty($propertyValue, 'r4');
+                        $propertyValueType = DocumentProperties::PROPERTY_TYPE_FLOAT;
+
+                        break;
+                    default:
+                        $propertyValueType = DocumentProperties::PROPERTY_TYPE_STRING;
+                }
+            }
+        }
+
+        $docProps->setCustomProperty($propertyValueName, $propertyValue, $propertyValueType);
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Security/XmlScanner.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Security/XmlScanner.php
new file mode 100755
index 0000000..55bba63
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Security/XmlScanner.php
@@ -0,0 +1,150 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Security;
+
+use PhpOffice\PhpSpreadsheet\Reader;
+use PhpOffice\PhpSpreadsheet\Settings;
+
+class XmlScanner
+{
+    /**
+     * String used to identify risky xml elements.
+     *
+     * @var string
+     */
+    private $pattern;
+
+    private $callback;
+
+    private static $libxmlDisableEntityLoaderValue;
+
+    public function __construct($pattern = '<!DOCTYPE')
+    {
+        $this->pattern = $pattern;
+
+        $this->disableEntityLoaderCheck();
+
+        // A fatal error will bypass the destructor, so we register a shutdown here
+        register_shutdown_function([__CLASS__, 'shutdown']);
+    }
+
+    public static function getInstance(Reader\IReader $reader)
+    {
+        switch (true) {
+            case $reader instanceof Reader\Html:
+                return new self('<!ENTITY');
+            case $reader instanceof Reader\Xlsx:
+            case $reader instanceof Reader\Xml:
+            case $reader instanceof Reader\Ods:
+            case $reader instanceof Reader\Gnumeric:
+                return new self('<!DOCTYPE');
+            default:
+                return new self('<!DOCTYPE');
+        }
+    }
+
+    public static function threadSafeLibxmlDisableEntityLoaderAvailability()
+    {
+        if (PHP_MAJOR_VERSION == 7) {
+            switch (PHP_MINOR_VERSION) {
+                case 2:
+                    return PHP_RELEASE_VERSION >= 1;
+                case 1:
+                    return PHP_RELEASE_VERSION >= 13;
+                case 0:
+                    return PHP_RELEASE_VERSION >= 27;
+            }
+
+            return true;
+        }
+
+        return false;
+    }
+
+    private function disableEntityLoaderCheck(): void
+    {
+        if (Settings::getLibXmlDisableEntityLoader()) {
+            $libxmlDisableEntityLoaderValue = libxml_disable_entity_loader(true);
+
+            if (self::$libxmlDisableEntityLoaderValue === null) {
+                self::$libxmlDisableEntityLoaderValue = $libxmlDisableEntityLoaderValue;
+            }
+        }
+    }
+
+    public static function shutdown(): void
+    {
+        if (self::$libxmlDisableEntityLoaderValue !== null) {
+            libxml_disable_entity_loader(self::$libxmlDisableEntityLoaderValue);
+            self::$libxmlDisableEntityLoaderValue = null;
+        }
+    }
+
+    public function __destruct()
+    {
+        self::shutdown();
+    }
+
+    public function setAdditionalCallback(callable $callback): void
+    {
+        $this->callback = $callback;
+    }
+
+    private function toUtf8($xml)
+    {
+        $pattern = '/encoding="(.*?)"/';
+        $result = preg_match($pattern, $xml, $matches);
+        $charset = strtoupper($result ? $matches[1] : 'UTF-8');
+
+        if ($charset !== 'UTF-8') {
+            $xml = mb_convert_encoding($xml, 'UTF-8', $charset);
+
+            $result = preg_match($pattern, $xml, $matches);
+            $charset = strtoupper($result ? $matches[1] : 'UTF-8');
+            if ($charset !== 'UTF-8') {
+                throw new Reader\Exception('Suspicious Double-encoded XML, spreadsheet file load() aborted to prevent XXE/XEE attacks');
+            }
+        }
+
+        return $xml;
+    }
+
+    /**
+     * Scan the XML for use of <!ENTITY to prevent XXE/XEE attacks.
+     *
+     * @param mixed $xml
+     *
+     * @return string
+     */
+    public function scan($xml)
+    {
+        $this->disableEntityLoaderCheck();
+
+        $xml = $this->toUtf8($xml);
+
+        // Don't rely purely on libxml_disable_entity_loader()
+        $pattern = '/\\0?' . implode('\\0?', str_split($this->pattern)) . '\\0?/';
+
+        if (preg_match($pattern, $xml)) {
+            throw new Reader\Exception('Detected use of ENTITY in XML, spreadsheet file load() aborted to prevent XXE/XEE attacks');
+        }
+
+        if ($this->callback !== null && is_callable($this->callback)) {
+            $xml = call_user_func($this->callback, $xml);
+        }
+
+        return $xml;
+    }
+
+    /**
+     * Scan theXML for use of <!ENTITY to prevent XXE/XEE attacks.
+     *
+     * @param string $filestream
+     *
+     * @return string
+     */
+    public function scanFile($filestream)
+    {
+        return $this->scan(file_get_contents($filestream));
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php
new file mode 100755
index 0000000..f40eba7
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php
@@ -0,0 +1,489 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Style\Border;
+
+class Slk extends BaseReader
+{
+    /**
+     * Input encoding.
+     *
+     * @var string
+     */
+    private $inputEncoding = 'ANSI';
+
+    /**
+     * Sheet index to read.
+     *
+     * @var int
+     */
+    private $sheetIndex = 0;
+
+    /**
+     * Formats.
+     *
+     * @var array
+     */
+    private $formats = [];
+
+    /**
+     * Format Count.
+     *
+     * @var int
+     */
+    private $format = 0;
+
+    /**
+     * Create a new SYLK Reader instance.
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Validate that the current file is a SYLK file.
+     *
+     * @param string $pFilename
+     *
+     * @return bool
+     */
+    public function canRead($pFilename)
+    {
+        // Check if file exists
+        try {
+            $this->openFile($pFilename);
+        } catch (Exception $e) {
+            return false;
+        }
+
+        // Read sample data (first 2 KB will do)
+        $data = fread($this->fileHandle, 2048);
+
+        // Count delimiters in file
+        $delimiterCount = substr_count($data, ';');
+        $hasDelimiter = $delimiterCount > 0;
+
+        // Analyze first line looking for ID; signature
+        $lines = explode("\n", $data);
+        $hasId = substr($lines[0], 0, 4) === 'ID;P';
+
+        fclose($this->fileHandle);
+
+        return $hasDelimiter && $hasId;
+    }
+
+    /**
+     * Set input encoding.
+     *
+     * @param string $pValue Input encoding, eg: 'ANSI'
+     *
+     * @return $this
+     */
+    public function setInputEncoding($pValue)
+    {
+        $this->inputEncoding = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get input encoding.
+     *
+     * @return string
+     */
+    public function getInputEncoding()
+    {
+        return $this->inputEncoding;
+    }
+
+    /**
+     * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
+     *
+     * @param string $pFilename
+     *
+     * @return array
+     */
+    public function listWorksheetInfo($pFilename)
+    {
+        // Open file
+        if (!$this->canRead($pFilename)) {
+            throw new Exception($pFilename . ' is an Invalid Spreadsheet file.');
+        }
+        $this->openFile($pFilename);
+        $fileHandle = $this->fileHandle;
+        rewind($fileHandle);
+
+        $worksheetInfo = [];
+        $worksheetInfo[0]['worksheetName'] = 'Worksheet';
+        $worksheetInfo[0]['lastColumnLetter'] = 'A';
+        $worksheetInfo[0]['lastColumnIndex'] = 0;
+        $worksheetInfo[0]['totalRows'] = 0;
+        $worksheetInfo[0]['totalColumns'] = 0;
+
+        // loop through one row (line) at a time in the file
+        $rowIndex = 0;
+        while (($rowData = fgets($fileHandle)) !== false) {
+            $columnIndex = 0;
+
+            // convert SYLK encoded $rowData to UTF-8
+            $rowData = StringHelper::SYLKtoUTF8($rowData);
+
+            // explode each row at semicolons while taking into account that literal semicolon (;)
+            // is escaped like this (;;)
+            $rowData = explode("\t", str_replace('¤', ';', str_replace(';', "\t", str_replace(';;', '¤', rtrim($rowData)))));
+
+            $dataType = array_shift($rowData);
+            if ($dataType == 'C') {
+                //  Read cell value data
+                foreach ($rowData as $rowDatum) {
+                    switch ($rowDatum[0]) {
+                        case 'C':
+                        case 'X':
+                            $columnIndex = substr($rowDatum, 1) - 1;
+
+                            break;
+                        case 'R':
+                        case 'Y':
+                            $rowIndex = substr($rowDatum, 1);
+
+                            break;
+                    }
+
+                    $worksheetInfo[0]['totalRows'] = max($worksheetInfo[0]['totalRows'], $rowIndex);
+                    $worksheetInfo[0]['lastColumnIndex'] = max($worksheetInfo[0]['lastColumnIndex'], $columnIndex);
+                }
+            }
+        }
+
+        $worksheetInfo[0]['lastColumnLetter'] = Coordinate::stringFromColumnIndex($worksheetInfo[0]['lastColumnIndex'] + 1);
+        $worksheetInfo[0]['totalColumns'] = $worksheetInfo[0]['lastColumnIndex'] + 1;
+
+        // Close file
+        fclose($fileHandle);
+
+        return $worksheetInfo;
+    }
+
+    /**
+     * Loads PhpSpreadsheet from file.
+     *
+     * @param string $pFilename
+     *
+     * @return Spreadsheet
+     */
+    public function load($pFilename)
+    {
+        // Create new Spreadsheet
+        $spreadsheet = new Spreadsheet();
+
+        // Load into this instance
+        return $this->loadIntoExisting($pFilename, $spreadsheet);
+    }
+
+    /**
+     * Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
+     *
+     * @param string $pFilename
+     *
+     * @return Spreadsheet
+     */
+    public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet)
+    {
+        // Open file
+        if (!$this->canRead($pFilename)) {
+            throw new Exception($pFilename . ' is an Invalid Spreadsheet file.');
+        }
+        $this->openFile($pFilename);
+        $fileHandle = $this->fileHandle;
+        rewind($fileHandle);
+
+        // Create new Worksheets
+        while ($spreadsheet->getSheetCount() <= $this->sheetIndex) {
+            $spreadsheet->createSheet();
+        }
+        $spreadsheet->setActiveSheetIndex($this->sheetIndex);
+
+        $fromFormats = ['\-', '\ '];
+        $toFormats = ['-', ' '];
+
+        // Loop through file
+        $column = $row = '';
+
+        // loop through one row (line) at a time in the file
+        while (($rowData = fgets($fileHandle)) !== false) {
+            // convert SYLK encoded $rowData to UTF-8
+            $rowData = StringHelper::SYLKtoUTF8($rowData);
+
+            // explode each row at semicolons while taking into account that literal semicolon (;)
+            // is escaped like this (;;)
+            $rowData = explode("\t", str_replace('¤', ';', str_replace(';', "\t", str_replace(';;', '¤', rtrim($rowData)))));
+
+            $dataType = array_shift($rowData);
+            //    Read shared styles
+            if ($dataType == 'P') {
+                $formatArray = [];
+                foreach ($rowData as $rowDatum) {
+                    switch ($rowDatum[0]) {
+                        case 'P':
+                            $formatArray['numberFormat']['formatCode'] = str_replace($fromFormats, $toFormats, substr($rowDatum, 1));
+
+                            break;
+                        case 'E':
+                        case 'F':
+                            $formatArray['font']['name'] = substr($rowDatum, 1);
+
+                            break;
+                        case 'L':
+                            $formatArray['font']['size'] = substr($rowDatum, 1);
+
+                            break;
+                        case 'S':
+                            $styleSettings = substr($rowDatum, 1);
+                            $iMax = strlen($styleSettings);
+                            for ($i = 0; $i < $iMax; ++$i) {
+                                switch ($styleSettings[$i]) {
+                                    case 'I':
+                                        $formatArray['font']['italic'] = true;
+
+                                        break;
+                                    case 'D':
+                                        $formatArray['font']['bold'] = true;
+
+                                        break;
+                                    case 'T':
+                                        $formatArray['borders']['top']['borderStyle'] = Border::BORDER_THIN;
+
+                                        break;
+                                    case 'B':
+                                        $formatArray['borders']['bottom']['borderStyle'] = Border::BORDER_THIN;
+
+                                        break;
+                                    case 'L':
+                                        $formatArray['borders']['left']['borderStyle'] = Border::BORDER_THIN;
+
+                                        break;
+                                    case 'R':
+                                        $formatArray['borders']['right']['borderStyle'] = Border::BORDER_THIN;
+
+                                        break;
+                                }
+                            }
+
+                            break;
+                    }
+                }
+                $this->formats['P' . $this->format++] = $formatArray;
+            //    Read cell value data
+            } elseif ($dataType == 'C') {
+                $hasCalculatedValue = false;
+                $cellData = $cellDataFormula = '';
+                foreach ($rowData as $rowDatum) {
+                    switch ($rowDatum[0]) {
+                        case 'C':
+                        case 'X':
+                            $column = substr($rowDatum, 1);
+
+                            break;
+                        case 'R':
+                        case 'Y':
+                            $row = substr($rowDatum, 1);
+
+                            break;
+                        case 'K':
+                            $cellData = substr($rowDatum, 1);
+
+                            break;
+                        case 'E':
+                            $cellDataFormula = '=' . substr($rowDatum, 1);
+                            //    Convert R1C1 style references to A1 style references (but only when not quoted)
+                            $temp = explode('"', $cellDataFormula);
+                            $key = false;
+                            foreach ($temp as &$value) {
+                                //    Only count/replace in alternate array entries
+                                if ($key = !$key) {
+                                    preg_match_all('/(R(\[?-?\d*\]?))(C(\[?-?\d*\]?))/', $value, $cellReferences, PREG_SET_ORDER + PREG_OFFSET_CAPTURE);
+                                    //    Reverse the matches array, otherwise all our offsets will become incorrect if we modify our way
+                                    //        through the formula from left to right. Reversing means that we work right to left.through
+                                    //        the formula
+                                    $cellReferences = array_reverse($cellReferences);
+                                    //    Loop through each R1C1 style reference in turn, converting it to its A1 style equivalent,
+                                    //        then modify the formula to use that new reference
+                                    foreach ($cellReferences as $cellReference) {
+                                        $rowReference = $cellReference[2][0];
+                                        //    Empty R reference is the current row
+                                        if ($rowReference == '') {
+                                            $rowReference = $row;
+                                        }
+                                        //    Bracketed R references are relative to the current row
+                                        if ($rowReference[0] == '[') {
+                                            $rowReference = $row + trim($rowReference, '[]');
+                                        }
+                                        $columnReference = $cellReference[4][0];
+                                        //    Empty C reference is the current column
+                                        if ($columnReference == '') {
+                                            $columnReference = $column;
+                                        }
+                                        //    Bracketed C references are relative to the current column
+                                        if ($columnReference[0] == '[') {
+                                            $columnReference = $column + trim($columnReference, '[]');
+                                        }
+                                        $A1CellReference = Coordinate::stringFromColumnIndex($columnReference) . $rowReference;
+
+                                        $value = substr_replace($value, $A1CellReference, $cellReference[0][1], strlen($cellReference[0][0]));
+                                    }
+                                }
+                            }
+                            unset($value);
+                            //    Then rebuild the formula string
+                            $cellDataFormula = implode('"', $temp);
+                            $hasCalculatedValue = true;
+
+                            break;
+                    }
+                }
+                $columnLetter = Coordinate::stringFromColumnIndex($column);
+                $cellData = Calculation::unwrapResult($cellData);
+
+                // Set cell value
+                $spreadsheet->getActiveSheet()->getCell($columnLetter . $row)->setValue(($hasCalculatedValue) ? $cellDataFormula : $cellData);
+                if ($hasCalculatedValue) {
+                    $cellData = Calculation::unwrapResult($cellData);
+                    $spreadsheet->getActiveSheet()->getCell($columnLetter . $row)->setCalculatedValue($cellData);
+                }
+                //    Read cell formatting
+            } elseif ($dataType == 'F') {
+                $formatStyle = $columnWidth = $styleSettings = '';
+                $styleData = [];
+                foreach ($rowData as $rowDatum) {
+                    switch ($rowDatum[0]) {
+                        case 'C':
+                        case 'X':
+                            $column = substr($rowDatum, 1);
+
+                            break;
+                        case 'R':
+                        case 'Y':
+                            $row = substr($rowDatum, 1);
+
+                            break;
+                        case 'P':
+                            $formatStyle = $rowDatum;
+
+                            break;
+                        case 'W':
+                            [$startCol, $endCol, $columnWidth] = explode(' ', substr($rowDatum, 1));
+
+                            break;
+                        case 'S':
+                            $styleSettings = substr($rowDatum, 1);
+                            $iMax = strlen($styleSettings);
+                            for ($i = 0; $i < $iMax; ++$i) {
+                                switch ($styleSettings[$i]) {
+                                    case 'I':
+                                        $styleData['font']['italic'] = true;
+
+                                        break;
+                                    case 'D':
+                                        $styleData['font']['bold'] = true;
+
+                                        break;
+                                    case 'T':
+                                        $styleData['borders']['top']['borderStyle'] = Border::BORDER_THIN;
+
+                                        break;
+                                    case 'B':
+                                        $styleData['borders']['bottom']['borderStyle'] = Border::BORDER_THIN;
+
+                                        break;
+                                    case 'L':
+                                        $styleData['borders']['left']['borderStyle'] = Border::BORDER_THIN;
+
+                                        break;
+                                    case 'R':
+                                        $styleData['borders']['right']['borderStyle'] = Border::BORDER_THIN;
+
+                                        break;
+                                }
+                            }
+
+                            break;
+                    }
+                }
+                if (($formatStyle > '') && ($column > '') && ($row > '')) {
+                    $columnLetter = Coordinate::stringFromColumnIndex($column);
+                    if (isset($this->formats[$formatStyle])) {
+                        $spreadsheet->getActiveSheet()->getStyle($columnLetter . $row)->applyFromArray($this->formats[$formatStyle]);
+                    }
+                }
+                if ((!empty($styleData)) && ($column > '') && ($row > '')) {
+                    $columnLetter = Coordinate::stringFromColumnIndex($column);
+                    $spreadsheet->getActiveSheet()->getStyle($columnLetter . $row)->applyFromArray($styleData);
+                }
+                if ($columnWidth > '') {
+                    if ($startCol == $endCol) {
+                        $startCol = Coordinate::stringFromColumnIndex($startCol);
+                        $spreadsheet->getActiveSheet()->getColumnDimension($startCol)->setWidth($columnWidth);
+                    } else {
+                        $startCol = Coordinate::stringFromColumnIndex($startCol);
+                        $endCol = Coordinate::stringFromColumnIndex($endCol);
+                        $spreadsheet->getActiveSheet()->getColumnDimension($startCol)->setWidth($columnWidth);
+                        do {
+                            $spreadsheet->getActiveSheet()->getColumnDimension(++$startCol)->setWidth($columnWidth);
+                        } while ($startCol != $endCol);
+                    }
+                }
+            } else {
+                foreach ($rowData as $rowDatum) {
+                    switch ($rowDatum[0]) {
+                        case 'C':
+                        case 'X':
+                            $column = substr($rowDatum, 1);
+
+                            break;
+                        case 'R':
+                        case 'Y':
+                            $row = substr($rowDatum, 1);
+
+                            break;
+                    }
+                }
+            }
+        }
+
+        // Close file
+        fclose($fileHandle);
+
+        // Return
+        return $spreadsheet;
+    }
+
+    /**
+     * Get sheet index.
+     *
+     * @return int
+     */
+    public function getSheetIndex()
+    {
+        return $this->sheetIndex;
+    }
+
+    /**
+     * Set sheet index.
+     *
+     * @param int $pValue Sheet index
+     *
+     * @return $this
+     */
+    public function setSheetIndex($pValue)
+    {
+        $this->sheetIndex = $pValue;
+
+        return $this;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php
new file mode 100755
index 0000000..b206f8a
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php
@@ -0,0 +1,7933 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Cell\DataType;
+use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+use PhpOffice\PhpSpreadsheet\NamedRange;
+use PhpOffice\PhpSpreadsheet\RichText\RichText;
+use PhpOffice\PhpSpreadsheet\Shared\CodePage;
+use PhpOffice\PhpSpreadsheet\Shared\Date;
+use PhpOffice\PhpSpreadsheet\Shared\Escher;
+use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE;
+use PhpOffice\PhpSpreadsheet\Shared\File;
+use PhpOffice\PhpSpreadsheet\Shared\OLE;
+use PhpOffice\PhpSpreadsheet\Shared\OLERead;
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Style\Alignment;
+use PhpOffice\PhpSpreadsheet\Style\Borders;
+use PhpOffice\PhpSpreadsheet\Style\Font;
+use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
+use PhpOffice\PhpSpreadsheet\Style\Protection;
+use PhpOffice\PhpSpreadsheet\Style\Style;
+use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
+use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup;
+use PhpOffice\PhpSpreadsheet\Worksheet\SheetView;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+
+// Original file header of ParseXL (used as the base for this class):
+// --------------------------------------------------------------------------------
+// Adapted from Excel_Spreadsheet_Reader developed by users bizon153,
+// trex005, and mmp11 (SourceForge.net)
+// https://sourceforge.net/projects/phpexcelreader/
+// Primary changes made by canyoncasa (dvc) for ParseXL 1.00 ...
+//     Modelled moreso after Perl Excel Parse/Write modules
+//     Added Parse_Excel_Spreadsheet object
+//         Reads a whole worksheet or tab as row,column array or as
+//         associated hash of indexed rows and named column fields
+//     Added variables for worksheet (tab) indexes and names
+//     Added an object call for loading individual woorksheets
+//     Changed default indexing defaults to 0 based arrays
+//     Fixed date/time and percent formats
+//     Includes patches found at SourceForge...
+//         unicode patch by nobody
+//         unpack("d") machine depedency patch by matchy
+//         boundsheet utf16 patch by bjaenichen
+//     Renamed functions for shorter names
+//     General code cleanup and rigor, including <80 column width
+//     Included a testcase Excel file and PHP example calls
+//     Code works for PHP 5.x
+
+// Primary changes made by canyoncasa (dvc) for ParseXL 1.10 ...
+// http://sourceforge.net/tracker/index.php?func=detail&aid=1466964&group_id=99160&atid=623334
+//     Decoding of formula conditions, results, and tokens.
+//     Support for user-defined named cells added as an array "namedcells"
+//         Patch code for user-defined named cells supports single cells only.
+//         NOTE: this patch only works for BIFF8 as BIFF5-7 use a different
+//         external sheet reference structure
+class Xls extends BaseReader
+{
+    // ParseXL definitions
+    const XLS_BIFF8 = 0x0600;
+    const XLS_BIFF7 = 0x0500;
+    const XLS_WORKBOOKGLOBALS = 0x0005;
+    const XLS_WORKSHEET = 0x0010;
+
+    // record identifiers
+    const XLS_TYPE_FORMULA = 0x0006;
+    const XLS_TYPE_EOF = 0x000a;
+    const XLS_TYPE_PROTECT = 0x0012;
+    const XLS_TYPE_OBJECTPROTECT = 0x0063;
+    const XLS_TYPE_SCENPROTECT = 0x00dd;
+    const XLS_TYPE_PASSWORD = 0x0013;
+    const XLS_TYPE_HEADER = 0x0014;
+    const XLS_TYPE_FOOTER = 0x0015;
+    const XLS_TYPE_EXTERNSHEET = 0x0017;
+    const XLS_TYPE_DEFINEDNAME = 0x0018;
+    const XLS_TYPE_VERTICALPAGEBREAKS = 0x001a;
+    const XLS_TYPE_HORIZONTALPAGEBREAKS = 0x001b;
+    const XLS_TYPE_NOTE = 0x001c;
+    const XLS_TYPE_SELECTION = 0x001d;
+    const XLS_TYPE_DATEMODE = 0x0022;
+    const XLS_TYPE_EXTERNNAME = 0x0023;
+    const XLS_TYPE_LEFTMARGIN = 0x0026;
+    const XLS_TYPE_RIGHTMARGIN = 0x0027;
+    const XLS_TYPE_TOPMARGIN = 0x0028;
+    const XLS_TYPE_BOTTOMMARGIN = 0x0029;
+    const XLS_TYPE_PRINTGRIDLINES = 0x002b;
+    const XLS_TYPE_FILEPASS = 0x002f;
+    const XLS_TYPE_FONT = 0x0031;
+    const XLS_TYPE_CONTINUE = 0x003c;
+    const XLS_TYPE_PANE = 0x0041;
+    const XLS_TYPE_CODEPAGE = 0x0042;
+    const XLS_TYPE_DEFCOLWIDTH = 0x0055;
+    const XLS_TYPE_OBJ = 0x005d;
+    const XLS_TYPE_COLINFO = 0x007d;
+    const XLS_TYPE_IMDATA = 0x007f;
+    const XLS_TYPE_SHEETPR = 0x0081;
+    const XLS_TYPE_HCENTER = 0x0083;
+    const XLS_TYPE_VCENTER = 0x0084;
+    const XLS_TYPE_SHEET = 0x0085;
+    const XLS_TYPE_PALETTE = 0x0092;
+    const XLS_TYPE_SCL = 0x00a0;
+    const XLS_TYPE_PAGESETUP = 0x00a1;
+    const XLS_TYPE_MULRK = 0x00bd;
+    const XLS_TYPE_MULBLANK = 0x00be;
+    const XLS_TYPE_DBCELL = 0x00d7;
+    const XLS_TYPE_XF = 0x00e0;
+    const XLS_TYPE_MERGEDCELLS = 0x00e5;
+    const XLS_TYPE_MSODRAWINGGROUP = 0x00eb;
+    const XLS_TYPE_MSODRAWING = 0x00ec;
+    const XLS_TYPE_SST = 0x00fc;
+    const XLS_TYPE_LABELSST = 0x00fd;
+    const XLS_TYPE_EXTSST = 0x00ff;
+    const XLS_TYPE_EXTERNALBOOK = 0x01ae;
+    const XLS_TYPE_DATAVALIDATIONS = 0x01b2;
+    const XLS_TYPE_TXO = 0x01b6;
+    const XLS_TYPE_HYPERLINK = 0x01b8;
+    const XLS_TYPE_DATAVALIDATION = 0x01be;
+    const XLS_TYPE_DIMENSION = 0x0200;
+    const XLS_TYPE_BLANK = 0x0201;
+    const XLS_TYPE_NUMBER = 0x0203;
+    const XLS_TYPE_LABEL = 0x0204;
+    const XLS_TYPE_BOOLERR = 0x0205;
+    const XLS_TYPE_STRING = 0x0207;
+    const XLS_TYPE_ROW = 0x0208;
+    const XLS_TYPE_INDEX = 0x020b;
+    const XLS_TYPE_ARRAY = 0x0221;
+    const XLS_TYPE_DEFAULTROWHEIGHT = 0x0225;
+    const XLS_TYPE_WINDOW2 = 0x023e;
+    const XLS_TYPE_RK = 0x027e;
+    const XLS_TYPE_STYLE = 0x0293;
+    const XLS_TYPE_FORMAT = 0x041e;
+    const XLS_TYPE_SHAREDFMLA = 0x04bc;
+    const XLS_TYPE_BOF = 0x0809;
+    const XLS_TYPE_SHEETPROTECTION = 0x0867;
+    const XLS_TYPE_RANGEPROTECTION = 0x0868;
+    const XLS_TYPE_SHEETLAYOUT = 0x0862;
+    const XLS_TYPE_XFEXT = 0x087d;
+    const XLS_TYPE_PAGELAYOUTVIEW = 0x088b;
+    const XLS_TYPE_UNKNOWN = 0xffff;
+
+    // Encryption type
+    const MS_BIFF_CRYPTO_NONE = 0;
+    const MS_BIFF_CRYPTO_XOR = 1;
+    const MS_BIFF_CRYPTO_RC4 = 2;
+
+    // Size of stream blocks when using RC4 encryption
+    const REKEY_BLOCK = 0x400;
+
+    /**
+     * Summary Information stream data.
+     *
+     * @var string
+     */
+    private $summaryInformation;
+
+    /**
+     * Extended Summary Information stream data.
+     *
+     * @var string
+     */
+    private $documentSummaryInformation;
+
+    /**
+     * Workbook stream data. (Includes workbook globals substream as well as sheet substreams).
+     *
+     * @var string
+     */
+    private $data;
+
+    /**
+     * Size in bytes of $this->data.
+     *
+     * @var int
+     */
+    private $dataSize;
+
+    /**
+     * Current position in stream.
+     *
+     * @var int
+     */
+    private $pos;
+
+    /**
+     * Workbook to be returned by the reader.
+     *
+     * @var Spreadsheet
+     */
+    private $spreadsheet;
+
+    /**
+     * Worksheet that is currently being built by the reader.
+     *
+     * @var Worksheet
+     */
+    private $phpSheet;
+
+    /**
+     * BIFF version.
+     *
+     * @var int
+     */
+    private $version;
+
+    /**
+     * Codepage set in the Excel file being read. Only important for BIFF5 (Excel 5.0 - Excel 95)
+     * For BIFF8 (Excel 97 - Excel 2003) this will always have the value 'UTF-16LE'.
+     *
+     * @var string
+     */
+    private $codepage;
+
+    /**
+     * Shared formats.
+     *
+     * @var array
+     */
+    private $formats;
+
+    /**
+     * Shared fonts.
+     *
+     * @var array
+     */
+    private $objFonts;
+
+    /**
+     * Color palette.
+     *
+     * @var array
+     */
+    private $palette;
+
+    /**
+     * Worksheets.
+     *
+     * @var array
+     */
+    private $sheets;
+
+    /**
+     * External books.
+     *
+     * @var array
+     */
+    private $externalBooks;
+
+    /**
+     * REF structures. Only applies to BIFF8.
+     *
+     * @var array
+     */
+    private $ref;
+
+    /**
+     * External names.
+     *
+     * @var array
+     */
+    private $externalNames;
+
+    /**
+     * Defined names.
+     *
+     * @var array
+     */
+    private $definedname;
+
+    /**
+     * Shared strings. Only applies to BIFF8.
+     *
+     * @var array
+     */
+    private $sst;
+
+    /**
+     * Panes are frozen? (in sheet currently being read). See WINDOW2 record.
+     *
+     * @var bool
+     */
+    private $frozen;
+
+    /**
+     * Fit printout to number of pages? (in sheet currently being read). See SHEETPR record.
+     *
+     * @var bool
+     */
+    private $isFitToPages;
+
+    /**
+     * Objects. One OBJ record contributes with one entry.
+     *
+     * @var array
+     */
+    private $objs;
+
+    /**
+     * Text Objects. One TXO record corresponds with one entry.
+     *
+     * @var array
+     */
+    private $textObjects;
+
+    /**
+     * Cell Annotations (BIFF8).
+     *
+     * @var array
+     */
+    private $cellNotes;
+
+    /**
+     * The combined MSODRAWINGGROUP data.
+     *
+     * @var string
+     */
+    private $drawingGroupData;
+
+    /**
+     * The combined MSODRAWING data (per sheet).
+     *
+     * @var string
+     */
+    private $drawingData;
+
+    /**
+     * Keep track of XF index.
+     *
+     * @var int
+     */
+    private $xfIndex;
+
+    /**
+     * Mapping of XF index (that is a cell XF) to final index in cellXf collection.
+     *
+     * @var array
+     */
+    private $mapCellXfIndex;
+
+    /**
+     * Mapping of XF index (that is a style XF) to final index in cellStyleXf collection.
+     *
+     * @var array
+     */
+    private $mapCellStyleXfIndex;
+
+    /**
+     * The shared formulas in a sheet. One SHAREDFMLA record contributes with one value.
+     *
+     * @var array
+     */
+    private $sharedFormulas;
+
+    /**
+     * The shared formula parts in a sheet. One FORMULA record contributes with one value if it
+     * refers to a shared formula.
+     *
+     * @var array
+     */
+    private $sharedFormulaParts;
+
+    /**
+     * The type of encryption in use.
+     *
+     * @var int
+     */
+    private $encryption = 0;
+
+    /**
+     * The position in the stream after which contents are encrypted.
+     *
+     * @var int
+     */
+    private $encryptionStartPos = false;
+
+    /**
+     * The current RC4 decryption object.
+     *
+     * @var Xls\RC4
+     */
+    private $rc4Key;
+
+    /**
+     * The position in the stream that the RC4 decryption object was left at.
+     *
+     * @var int
+     */
+    private $rc4Pos = 0;
+
+    /**
+     * The current MD5 context state.
+     *
+     * @var string
+     */
+    private $md5Ctxt;
+
+    /**
+     * @var int
+     */
+    private $textObjRef;
+
+    /**
+     * @var string
+     */
+    private $baseCell;
+
+    /**
+     * Create a new Xls Reader instance.
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Can the current IReader read the file?
+     *
+     * @param string $pFilename
+     *
+     * @return bool
+     */
+    public function canRead($pFilename)
+    {
+        File::assertFile($pFilename);
+
+        try {
+            // Use ParseXL for the hard work.
+            $ole = new OLERead();
+
+            // get excel data
+            $ole->read($pFilename);
+
+            return true;
+        } catch (PhpSpreadsheetException $e) {
+            return false;
+        }
+    }
+
+    /**
+     * Reads names of the worksheets from a file, without parsing the whole file to a PhpSpreadsheet object.
+     *
+     * @param string $pFilename
+     *
+     * @return array
+     */
+    public function listWorksheetNames($pFilename)
+    {
+        File::assertFile($pFilename);
+
+        $worksheetNames = [];
+
+        // Read the OLE file
+        $this->loadOLE($pFilename);
+
+        // total byte size of Excel data (workbook global substream + sheet substreams)
+        $this->dataSize = strlen($this->data);
+
+        $this->pos = 0;
+        $this->sheets = [];
+
+        // Parse Workbook Global Substream
+        while ($this->pos < $this->dataSize) {
+            $code = self::getUInt2d($this->data, $this->pos);
+
+            switch ($code) {
+                case self::XLS_TYPE_BOF:
+                    $this->readBof();
+
+                    break;
+                case self::XLS_TYPE_SHEET:
+                    $this->readSheet();
+
+                    break;
+                case self::XLS_TYPE_EOF:
+                    $this->readDefault();
+
+                    break 2;
+                default:
+                    $this->readDefault();
+
+                    break;
+            }
+        }
+
+        foreach ($this->sheets as $sheet) {
+            if ($sheet['sheetType'] != 0x00) {
+                // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
+                continue;
+            }
+
+            $worksheetNames[] = $sheet['name'];
+        }
+
+        return $worksheetNames;
+    }
+
+    /**
+     * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
+     *
+     * @param string $pFilename
+     *
+     * @return array
+     */
+    public function listWorksheetInfo($pFilename)
+    {
+        File::assertFile($pFilename);
+
+        $worksheetInfo = [];
+
+        // Read the OLE file
+        $this->loadOLE($pFilename);
+
+        // total byte size of Excel data (workbook global substream + sheet substreams)
+        $this->dataSize = strlen($this->data);
+
+        // initialize
+        $this->pos = 0;
+        $this->sheets = [];
+
+        // Parse Workbook Global Substream
+        while ($this->pos < $this->dataSize) {
+            $code = self::getUInt2d($this->data, $this->pos);
+
+            switch ($code) {
+                case self::XLS_TYPE_BOF:
+                    $this->readBof();
+
+                    break;
+                case self::XLS_TYPE_SHEET:
+                    $this->readSheet();
+
+                    break;
+                case self::XLS_TYPE_EOF:
+                    $this->readDefault();
+
+                    break 2;
+                default:
+                    $this->readDefault();
+
+                    break;
+            }
+        }
+
+        // Parse the individual sheets
+        foreach ($this->sheets as $sheet) {
+            if ($sheet['sheetType'] != 0x00) {
+                // 0x00: Worksheet
+                // 0x02: Chart
+                // 0x06: Visual Basic module
+                continue;
+            }
+
+            $tmpInfo = [];
+            $tmpInfo['worksheetName'] = $sheet['name'];
+            $tmpInfo['lastColumnLetter'] = 'A';
+            $tmpInfo['lastColumnIndex'] = 0;
+            $tmpInfo['totalRows'] = 0;
+            $tmpInfo['totalColumns'] = 0;
+
+            $this->pos = $sheet['offset'];
+
+            while ($this->pos <= $this->dataSize - 4) {
+                $code = self::getUInt2d($this->data, $this->pos);
+
+                switch ($code) {
+                    case self::XLS_TYPE_RK:
+                    case self::XLS_TYPE_LABELSST:
+                    case self::XLS_TYPE_NUMBER:
+                    case self::XLS_TYPE_FORMULA:
+                    case self::XLS_TYPE_BOOLERR:
+                    case self::XLS_TYPE_LABEL:
+                        $length = self::getUInt2d($this->data, $this->pos + 2);
+                        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+                        // move stream pointer to next record
+                        $this->pos += 4 + $length;
+
+                        $rowIndex = self::getUInt2d($recordData, 0) + 1;
+                        $columnIndex = self::getUInt2d($recordData, 2);
+
+                        $tmpInfo['totalRows'] = max($tmpInfo['totalRows'], $rowIndex);
+                        $tmpInfo['lastColumnIndex'] = max($tmpInfo['lastColumnIndex'], $columnIndex);
+
+                        break;
+                    case self::XLS_TYPE_BOF:
+                        $this->readBof();
+
+                        break;
+                    case self::XLS_TYPE_EOF:
+                        $this->readDefault();
+
+                        break 2;
+                    default:
+                        $this->readDefault();
+
+                        break;
+                }
+            }
+
+            $tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1);
+            $tmpInfo['totalColumns'] = $tmpInfo['lastColumnIndex'] + 1;
+
+            $worksheetInfo[] = $tmpInfo;
+        }
+
+        return $worksheetInfo;
+    }
+
+    /**
+     * Loads PhpSpreadsheet from file.
+     *
+     * @param string $pFilename
+     *
+     * @return Spreadsheet
+     */
+    public function load($pFilename)
+    {
+        // Read the OLE file
+        $this->loadOLE($pFilename);
+
+        // Initialisations
+        $this->spreadsheet = new Spreadsheet();
+        $this->spreadsheet->removeSheetByIndex(0); // remove 1st sheet
+        if (!$this->readDataOnly) {
+            $this->spreadsheet->removeCellStyleXfByIndex(0); // remove the default style
+            $this->spreadsheet->removeCellXfByIndex(0); // remove the default style
+        }
+
+        // Read the summary information stream (containing meta data)
+        $this->readSummaryInformation();
+
+        // Read the Additional document summary information stream (containing application-specific meta data)
+        $this->readDocumentSummaryInformation();
+
+        // total byte size of Excel data (workbook global substream + sheet substreams)
+        $this->dataSize = strlen($this->data);
+
+        // initialize
+        $this->pos = 0;
+        $this->codepage = 'CP1252';
+        $this->formats = [];
+        $this->objFonts = [];
+        $this->palette = [];
+        $this->sheets = [];
+        $this->externalBooks = [];
+        $this->ref = [];
+        $this->definedname = [];
+        $this->sst = [];
+        $this->drawingGroupData = '';
+        $this->xfIndex = '';
+        $this->mapCellXfIndex = [];
+        $this->mapCellStyleXfIndex = [];
+
+        // Parse Workbook Global Substream
+        while ($this->pos < $this->dataSize) {
+            $code = self::getUInt2d($this->data, $this->pos);
+
+            switch ($code) {
+                case self::XLS_TYPE_BOF:
+                    $this->readBof();
+
+                    break;
+                case self::XLS_TYPE_FILEPASS:
+                    $this->readFilepass();
+
+                    break;
+                case self::XLS_TYPE_CODEPAGE:
+                    $this->readCodepage();
+
+                    break;
+                case self::XLS_TYPE_DATEMODE:
+                    $this->readDateMode();
+
+                    break;
+                case self::XLS_TYPE_FONT:
+                    $this->readFont();
+
+                    break;
+                case self::XLS_TYPE_FORMAT:
+                    $this->readFormat();
+
+                    break;
+                case self::XLS_TYPE_XF:
+                    $this->readXf();
+
+                    break;
+                case self::XLS_TYPE_XFEXT:
+                    $this->readXfExt();
+
+                    break;
+                case self::XLS_TYPE_STYLE:
+                    $this->readStyle();
+
+                    break;
+                case self::XLS_TYPE_PALETTE:
+                    $this->readPalette();
+
+                    break;
+                case self::XLS_TYPE_SHEET:
+                    $this->readSheet();
+
+                    break;
+                case self::XLS_TYPE_EXTERNALBOOK:
+                    $this->readExternalBook();
+
+                    break;
+                case self::XLS_TYPE_EXTERNNAME:
+                    $this->readExternName();
+
+                    break;
+                case self::XLS_TYPE_EXTERNSHEET:
+                    $this->readExternSheet();
+
+                    break;
+                case self::XLS_TYPE_DEFINEDNAME:
+                    $this->readDefinedName();
+
+                    break;
+                case self::XLS_TYPE_MSODRAWINGGROUP:
+                    $this->readMsoDrawingGroup();
+
+                    break;
+                case self::XLS_TYPE_SST:
+                    $this->readSst();
+
+                    break;
+                case self::XLS_TYPE_EOF:
+                    $this->readDefault();
+
+                    break 2;
+                default:
+                    $this->readDefault();
+
+                    break;
+            }
+        }
+
+        // Resolve indexed colors for font, fill, and border colors
+        // Cannot be resolved already in XF record, because PALETTE record comes afterwards
+        if (!$this->readDataOnly) {
+            foreach ($this->objFonts as $objFont) {
+                if (isset($objFont->colorIndex)) {
+                    $color = Xls\Color::map($objFont->colorIndex, $this->palette, $this->version);
+                    $objFont->getColor()->setRGB($color['rgb']);
+                }
+            }
+
+            foreach ($this->spreadsheet->getCellXfCollection() as $objStyle) {
+                // fill start and end color
+                $fill = $objStyle->getFill();
+
+                if (isset($fill->startcolorIndex)) {
+                    $startColor = Xls\Color::map($fill->startcolorIndex, $this->palette, $this->version);
+                    $fill->getStartColor()->setRGB($startColor['rgb']);
+                }
+                if (isset($fill->endcolorIndex)) {
+                    $endColor = Xls\Color::map($fill->endcolorIndex, $this->palette, $this->version);
+                    $fill->getEndColor()->setRGB($endColor['rgb']);
+                }
+
+                // border colors
+                $top = $objStyle->getBorders()->getTop();
+                $right = $objStyle->getBorders()->getRight();
+                $bottom = $objStyle->getBorders()->getBottom();
+                $left = $objStyle->getBorders()->getLeft();
+                $diagonal = $objStyle->getBorders()->getDiagonal();
+
+                if (isset($top->colorIndex)) {
+                    $borderTopColor = Xls\Color::map($top->colorIndex, $this->palette, $this->version);
+                    $top->getColor()->setRGB($borderTopColor['rgb']);
+                }
+                if (isset($right->colorIndex)) {
+                    $borderRightColor = Xls\Color::map($right->colorIndex, $this->palette, $this->version);
+                    $right->getColor()->setRGB($borderRightColor['rgb']);
+                }
+                if (isset($bottom->colorIndex)) {
+                    $borderBottomColor = Xls\Color::map($bottom->colorIndex, $this->palette, $this->version);
+                    $bottom->getColor()->setRGB($borderBottomColor['rgb']);
+                }
+                if (isset($left->colorIndex)) {
+                    $borderLeftColor = Xls\Color::map($left->colorIndex, $this->palette, $this->version);
+                    $left->getColor()->setRGB($borderLeftColor['rgb']);
+                }
+                if (isset($diagonal->colorIndex)) {
+                    $borderDiagonalColor = Xls\Color::map($diagonal->colorIndex, $this->palette, $this->version);
+                    $diagonal->getColor()->setRGB($borderDiagonalColor['rgb']);
+                }
+            }
+        }
+
+        // treat MSODRAWINGGROUP records, workbook-level Escher
+        if (!$this->readDataOnly && $this->drawingGroupData) {
+            $escherWorkbook = new Escher();
+            $reader = new Xls\Escher($escherWorkbook);
+            $escherWorkbook = $reader->load($this->drawingGroupData);
+        }
+
+        // Parse the individual sheets
+        foreach ($this->sheets as $sheet) {
+            if ($sheet['sheetType'] != 0x00) {
+                // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
+                continue;
+            }
+
+            // check if sheet should be skipped
+            if (isset($this->loadSheetsOnly) && !in_array($sheet['name'], $this->loadSheetsOnly)) {
+                continue;
+            }
+
+            // add sheet to PhpSpreadsheet object
+            $this->phpSheet = $this->spreadsheet->createSheet();
+            //    Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in formula
+            //        cells... during the load, all formulae should be correct, and we're simply bringing the worksheet
+            //        name in line with the formula, not the reverse
+            $this->phpSheet->setTitle($sheet['name'], false, false);
+            $this->phpSheet->setSheetState($sheet['sheetState']);
+
+            $this->pos = $sheet['offset'];
+
+            // Initialize isFitToPages. May change after reading SHEETPR record.
+            $this->isFitToPages = false;
+
+            // Initialize drawingData
+            $this->drawingData = '';
+
+            // Initialize objs
+            $this->objs = [];
+
+            // Initialize shared formula parts
+            $this->sharedFormulaParts = [];
+
+            // Initialize shared formulas
+            $this->sharedFormulas = [];
+
+            // Initialize text objs
+            $this->textObjects = [];
+
+            // Initialize cell annotations
+            $this->cellNotes = [];
+            $this->textObjRef = -1;
+
+            while ($this->pos <= $this->dataSize - 4) {
+                $code = self::getUInt2d($this->data, $this->pos);
+
+                switch ($code) {
+                    case self::XLS_TYPE_BOF:
+                        $this->readBof();
+
+                        break;
+                    case self::XLS_TYPE_PRINTGRIDLINES:
+                        $this->readPrintGridlines();
+
+                        break;
+                    case self::XLS_TYPE_DEFAULTROWHEIGHT:
+                        $this->readDefaultRowHeight();
+
+                        break;
+                    case self::XLS_TYPE_SHEETPR:
+                        $this->readSheetPr();
+
+                        break;
+                    case self::XLS_TYPE_HORIZONTALPAGEBREAKS:
+                        $this->readHorizontalPageBreaks();
+
+                        break;
+                    case self::XLS_TYPE_VERTICALPAGEBREAKS:
+                        $this->readVerticalPageBreaks();
+
+                        break;
+                    case self::XLS_TYPE_HEADER:
+                        $this->readHeader();
+
+                        break;
+                    case self::XLS_TYPE_FOOTER:
+                        $this->readFooter();
+
+                        break;
+                    case self::XLS_TYPE_HCENTER:
+                        $this->readHcenter();
+
+                        break;
+                    case self::XLS_TYPE_VCENTER:
+                        $this->readVcenter();
+
+                        break;
+                    case self::XLS_TYPE_LEFTMARGIN:
+                        $this->readLeftMargin();
+
+                        break;
+                    case self::XLS_TYPE_RIGHTMARGIN:
+                        $this->readRightMargin();
+
+                        break;
+                    case self::XLS_TYPE_TOPMARGIN:
+                        $this->readTopMargin();
+
+                        break;
+                    case self::XLS_TYPE_BOTTOMMARGIN:
+                        $this->readBottomMargin();
+
+                        break;
+                    case self::XLS_TYPE_PAGESETUP:
+                        $this->readPageSetup();
+
+                        break;
+                    case self::XLS_TYPE_PROTECT:
+                        $this->readProtect();
+
+                        break;
+                    case self::XLS_TYPE_SCENPROTECT:
+                        $this->readScenProtect();
+
+                        break;
+                    case self::XLS_TYPE_OBJECTPROTECT:
+                        $this->readObjectProtect();
+
+                        break;
+                    case self::XLS_TYPE_PASSWORD:
+                        $this->readPassword();
+
+                        break;
+                    case self::XLS_TYPE_DEFCOLWIDTH:
+                        $this->readDefColWidth();
+
+                        break;
+                    case self::XLS_TYPE_COLINFO:
+                        $this->readColInfo();
+
+                        break;
+                    case self::XLS_TYPE_DIMENSION:
+                        $this->readDefault();
+
+                        break;
+                    case self::XLS_TYPE_ROW:
+                        $this->readRow();
+
+                        break;
+                    case self::XLS_TYPE_DBCELL:
+                        $this->readDefault();
+
+                        break;
+                    case self::XLS_TYPE_RK:
+                        $this->readRk();
+
+                        break;
+                    case self::XLS_TYPE_LABELSST:
+                        $this->readLabelSst();
+
+                        break;
+                    case self::XLS_TYPE_MULRK:
+                        $this->readMulRk();
+
+                        break;
+                    case self::XLS_TYPE_NUMBER:
+                        $this->readNumber();
+
+                        break;
+                    case self::XLS_TYPE_FORMULA:
+                        $this->readFormula();
+
+                        break;
+                    case self::XLS_TYPE_SHAREDFMLA:
+                        $this->readSharedFmla();
+
+                        break;
+                    case self::XLS_TYPE_BOOLERR:
+                        $this->readBoolErr();
+
+                        break;
+                    case self::XLS_TYPE_MULBLANK:
+                        $this->readMulBlank();
+
+                        break;
+                    case self::XLS_TYPE_LABEL:
+                        $this->readLabel();
+
+                        break;
+                    case self::XLS_TYPE_BLANK:
+                        $this->readBlank();
+
+                        break;
+                    case self::XLS_TYPE_MSODRAWING:
+                        $this->readMsoDrawing();
+
+                        break;
+                    case self::XLS_TYPE_OBJ:
+                        $this->readObj();
+
+                        break;
+                    case self::XLS_TYPE_WINDOW2:
+                        $this->readWindow2();
+
+                        break;
+                    case self::XLS_TYPE_PAGELAYOUTVIEW:
+                        $this->readPageLayoutView();
+
+                        break;
+                    case self::XLS_TYPE_SCL:
+                        $this->readScl();
+
+                        break;
+                    case self::XLS_TYPE_PANE:
+                        $this->readPane();
+
+                        break;
+                    case self::XLS_TYPE_SELECTION:
+                        $this->readSelection();
+
+                        break;
+                    case self::XLS_TYPE_MERGEDCELLS:
+                        $this->readMergedCells();
+
+                        break;
+                    case self::XLS_TYPE_HYPERLINK:
+                        $this->readHyperLink();
+
+                        break;
+                    case self::XLS_TYPE_DATAVALIDATIONS:
+                        $this->readDataValidations();
+
+                        break;
+                    case self::XLS_TYPE_DATAVALIDATION:
+                        $this->readDataValidation();
+
+                        break;
+                    case self::XLS_TYPE_SHEETLAYOUT:
+                        $this->readSheetLayout();
+
+                        break;
+                    case self::XLS_TYPE_SHEETPROTECTION:
+                        $this->readSheetProtection();
+
+                        break;
+                    case self::XLS_TYPE_RANGEPROTECTION:
+                        $this->readRangeProtection();
+
+                        break;
+                    case self::XLS_TYPE_NOTE:
+                        $this->readNote();
+
+                        break;
+                    case self::XLS_TYPE_TXO:
+                        $this->readTextObject();
+
+                        break;
+                    case self::XLS_TYPE_CONTINUE:
+                        $this->readContinue();
+
+                        break;
+                    case self::XLS_TYPE_EOF:
+                        $this->readDefault();
+
+                        break 2;
+                    default:
+                        $this->readDefault();
+
+                        break;
+                }
+            }
+
+            // treat MSODRAWING records, sheet-level Escher
+            if (!$this->readDataOnly && $this->drawingData) {
+                $escherWorksheet = new Escher();
+                $reader = new Xls\Escher($escherWorksheet);
+                $escherWorksheet = $reader->load($this->drawingData);
+
+                // get all spContainers in one long array, so they can be mapped to OBJ records
+                $allSpContainers = $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers();
+            }
+
+            // treat OBJ records
+            foreach ($this->objs as $n => $obj) {
+                // the first shape container never has a corresponding OBJ record, hence $n + 1
+                if (isset($allSpContainers[$n + 1]) && is_object($allSpContainers[$n + 1])) {
+                    $spContainer = $allSpContainers[$n + 1];
+
+                    // we skip all spContainers that are a part of a group shape since we cannot yet handle those
+                    if ($spContainer->getNestingLevel() > 1) {
+                        continue;
+                    }
+
+                    // calculate the width and height of the shape
+                    [$startColumn, $startRow] = Coordinate::coordinateFromString($spContainer->getStartCoordinates());
+                    [$endColumn, $endRow] = Coordinate::coordinateFromString($spContainer->getEndCoordinates());
+
+                    $startOffsetX = $spContainer->getStartOffsetX();
+                    $startOffsetY = $spContainer->getStartOffsetY();
+                    $endOffsetX = $spContainer->getEndOffsetX();
+                    $endOffsetY = $spContainer->getEndOffsetY();
+
+                    $width = \PhpOffice\PhpSpreadsheet\Shared\Xls::getDistanceX($this->phpSheet, $startColumn, $startOffsetX, $endColumn, $endOffsetX);
+                    $height = \PhpOffice\PhpSpreadsheet\Shared\Xls::getDistanceY($this->phpSheet, $startRow, $startOffsetY, $endRow, $endOffsetY);
+
+                    // calculate offsetX and offsetY of the shape
+                    $offsetX = $startOffsetX * \PhpOffice\PhpSpreadsheet\Shared\Xls::sizeCol($this->phpSheet, $startColumn) / 1024;
+                    $offsetY = $startOffsetY * \PhpOffice\PhpSpreadsheet\Shared\Xls::sizeRow($this->phpSheet, $startRow) / 256;
+
+                    switch ($obj['otObjType']) {
+                        case 0x19:
+                            // Note
+                            if (isset($this->cellNotes[$obj['idObjID']])) {
+                                $cellNote = $this->cellNotes[$obj['idObjID']];
+
+                                if (isset($this->textObjects[$obj['idObjID']])) {
+                                    $textObject = $this->textObjects[$obj['idObjID']];
+                                    $this->cellNotes[$obj['idObjID']]['objTextData'] = $textObject;
+                                }
+                            }
+
+                            break;
+                        case 0x08:
+                            // picture
+                            // get index to BSE entry (1-based)
+                            $BSEindex = $spContainer->getOPT(0x0104);
+
+                            // If there is no BSE Index, we will fail here and other fields are not read.
+                            // Fix by checking here.
+                            // TODO: Why is there no BSE Index? Is this a new Office Version? Password protected field?
+                            // More likely : a uncompatible picture
+                            if (!$BSEindex) {
+                                continue 2;
+                            }
+
+                            $BSECollection = $escherWorkbook->getDggContainer()->getBstoreContainer()->getBSECollection();
+                            $BSE = $BSECollection[$BSEindex - 1];
+                            $blipType = $BSE->getBlipType();
+
+                            // need check because some blip types are not supported by Escher reader such as EMF
+                            if ($blip = $BSE->getBlip()) {
+                                $ih = imagecreatefromstring($blip->getData());
+                                $drawing = new MemoryDrawing();
+                                $drawing->setImageResource($ih);
+
+                                // width, height, offsetX, offsetY
+                                $drawing->setResizeProportional(false);
+                                $drawing->setWidth($width);
+                                $drawing->setHeight($height);
+                                $drawing->setOffsetX($offsetX);
+                                $drawing->setOffsetY($offsetY);
+
+                                switch ($blipType) {
+                                    case BSE::BLIPTYPE_JPEG:
+                                        $drawing->setRenderingFunction(MemoryDrawing::RENDERING_JPEG);
+                                        $drawing->setMimeType(MemoryDrawing::MIMETYPE_JPEG);
+
+                                        break;
+                                    case BSE::BLIPTYPE_PNG:
+                                        $drawing->setRenderingFunction(MemoryDrawing::RENDERING_PNG);
+                                        $drawing->setMimeType(MemoryDrawing::MIMETYPE_PNG);
+
+                                        break;
+                                }
+
+                                $drawing->setWorksheet($this->phpSheet);
+                                $drawing->setCoordinates($spContainer->getStartCoordinates());
+                            }
+
+                            break;
+                        default:
+                            // other object type
+                            break;
+                    }
+                }
+            }
+
+            // treat SHAREDFMLA records
+            if ($this->version == self::XLS_BIFF8) {
+                foreach ($this->sharedFormulaParts as $cell => $baseCell) {
+                    [$column, $row] = Coordinate::coordinateFromString($cell);
+                    if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($column, $row, $this->phpSheet->getTitle())) {
+                        $formula = $this->getFormulaFromStructure($this->sharedFormulas[$baseCell], $cell);
+                        $this->phpSheet->getCell($cell)->setValueExplicit('=' . $formula, DataType::TYPE_FORMULA);
+                    }
+                }
+            }
+
+            if (!empty($this->cellNotes)) {
+                foreach ($this->cellNotes as $note => $noteDetails) {
+                    if (!isset($noteDetails['objTextData'])) {
+                        if (isset($this->textObjects[$note])) {
+                            $textObject = $this->textObjects[$note];
+                            $noteDetails['objTextData'] = $textObject;
+                        } else {
+                            $noteDetails['objTextData']['text'] = '';
+                        }
+                    }
+                    $cellAddress = str_replace('$', '', $noteDetails['cellRef']);
+                    $this->phpSheet->getComment($cellAddress)->setAuthor($noteDetails['author'])->setText($this->parseRichText($noteDetails['objTextData']['text']));
+                }
+            }
+        }
+
+        // add the named ranges (defined names)
+        foreach ($this->definedname as $definedName) {
+            if ($definedName['isBuiltInName']) {
+                switch ($definedName['name']) {
+                    case pack('C', 0x06):
+                        // print area
+                        //    in general, formula looks like this: Foo!$C$7:$J$66,Bar!$A$1:$IV$2
+                        $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
+
+                        $extractedRanges = [];
+                        foreach ($ranges as $range) {
+                            // $range should look like one of these
+                            //        Foo!$C$7:$J$66
+                            //        Bar!$A$1:$IV$2
+                            $explodes = Worksheet::extractSheetTitle($range, true);
+                            $sheetName = trim($explodes[0], "'");
+                            if (count($explodes) == 2) {
+                                if (strpos($explodes[1], ':') === false) {
+                                    $explodes[1] = $explodes[1] . ':' . $explodes[1];
+                                }
+                                $extractedRanges[] = str_replace('$', '', $explodes[1]); // C7:J66
+                            }
+                        }
+                        if ($docSheet = $this->spreadsheet->getSheetByName($sheetName)) {
+                            $docSheet->getPageSetup()->setPrintArea(implode(',', $extractedRanges)); // C7:J66,A1:IV2
+                        }
+
+                        break;
+                    case pack('C', 0x07):
+                        // print titles (repeating rows)
+                        // Assuming BIFF8, there are 3 cases
+                        // 1. repeating rows
+                        //        formula looks like this: Sheet!$A$1:$IV$2
+                        //        rows 1-2 repeat
+                        // 2. repeating columns
+                        //        formula looks like this: Sheet!$A$1:$B$65536
+                        //        columns A-B repeat
+                        // 3. both repeating rows and repeating columns
+                        //        formula looks like this: Sheet!$A$1:$B$65536,Sheet!$A$1:$IV$2
+                        $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
+                        foreach ($ranges as $range) {
+                            // $range should look like this one of these
+                            //        Sheet!$A$1:$B$65536
+                            //        Sheet!$A$1:$IV$2
+                            if (strpos($range, '!') !== false) {
+                                $explodes = Worksheet::extractSheetTitle($range, true);
+                                if ($docSheet = $this->spreadsheet->getSheetByName($explodes[0])) {
+                                    $extractedRange = $explodes[1];
+                                    $extractedRange = str_replace('$', '', $extractedRange);
+
+                                    $coordinateStrings = explode(':', $extractedRange);
+                                    if (count($coordinateStrings) == 2) {
+                                        [$firstColumn, $firstRow] = Coordinate::coordinateFromString($coordinateStrings[0]);
+                                        [$lastColumn, $lastRow] = Coordinate::coordinateFromString($coordinateStrings[1]);
+
+                                        if ($firstColumn == 'A' && $lastColumn == 'IV') {
+                                            // then we have repeating rows
+                                            $docSheet->getPageSetup()->setRowsToRepeatAtTop([$firstRow, $lastRow]);
+                                        } elseif ($firstRow == 1 && $lastRow == 65536) {
+                                            // then we have repeating columns
+                                            $docSheet->getPageSetup()->setColumnsToRepeatAtLeft([$firstColumn, $lastColumn]);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+
+                        break;
+                }
+            } else {
+                // Extract range
+                if (strpos($definedName['formula'], '!') !== false) {
+                    $explodes = Worksheet::extractSheetTitle($definedName['formula'], true);
+                    if (($docSheet = $this->spreadsheet->getSheetByName($explodes[0])) ||
+                        ($docSheet = $this->spreadsheet->getSheetByName(trim($explodes[0], "'")))) {
+                        $extractedRange = $explodes[1];
+                        $extractedRange = str_replace('$', '', $extractedRange);
+
+                        $localOnly = ($definedName['scope'] == 0) ? false : true;
+
+                        $scope = ($definedName['scope'] == 0) ? null : $this->spreadsheet->getSheetByName($this->sheets[$definedName['scope'] - 1]['name']);
+
+                        $this->spreadsheet->addNamedRange(new NamedRange((string) $definedName['name'], $docSheet, $extractedRange, $localOnly, $scope));
+                    }
+                }
+                //    Named Value
+                    //    TODO Provide support for named values
+            }
+        }
+        $this->data = null;
+
+        return $this->spreadsheet;
+    }
+
+    /**
+     * Read record data from stream, decrypting as required.
+     *
+     * @param string $data Data stream to read from
+     * @param int $pos Position to start reading from
+     * @param int $len Record data length
+     *
+     * @return string Record data
+     */
+    private function readRecordData($data, $pos, $len)
+    {
+        $data = substr($data, $pos, $len);
+
+        // File not encrypted, or record before encryption start point
+        if ($this->encryption == self::MS_BIFF_CRYPTO_NONE || $pos < $this->encryptionStartPos) {
+            return $data;
+        }
+
+        $recordData = '';
+        if ($this->encryption == self::MS_BIFF_CRYPTO_RC4) {
+            $oldBlock = floor($this->rc4Pos / self::REKEY_BLOCK);
+            $block = floor($pos / self::REKEY_BLOCK);
+            $endBlock = floor(($pos + $len) / self::REKEY_BLOCK);
+
+            // Spin an RC4 decryptor to the right spot. If we have a decryptor sitting
+            // at a point earlier in the current block, re-use it as we can save some time.
+            if ($block != $oldBlock || $pos < $this->rc4Pos || !$this->rc4Key) {
+                $this->rc4Key = $this->makeKey($block, $this->md5Ctxt);
+                $step = $pos % self::REKEY_BLOCK;
+            } else {
+                $step = $pos - $this->rc4Pos;
+            }
+            $this->rc4Key->RC4(str_repeat("\0", $step));
+
+            // Decrypt record data (re-keying at the end of every block)
+            while ($block != $endBlock) {
+                $step = self::REKEY_BLOCK - ($pos % self::REKEY_BLOCK);
+                $recordData .= $this->rc4Key->RC4(substr($data, 0, $step));
+                $data = substr($data, $step);
+                $pos += $step;
+                $len -= $step;
+                ++$block;
+                $this->rc4Key = $this->makeKey($block, $this->md5Ctxt);
+            }
+            $recordData .= $this->rc4Key->RC4(substr($data, 0, $len));
+
+            // Keep track of the position of this decryptor.
+            // We'll try and re-use it later if we can to speed things up
+            $this->rc4Pos = $pos + $len;
+        } elseif ($this->encryption == self::MS_BIFF_CRYPTO_XOR) {
+            throw new Exception('XOr encryption not supported');
+        }
+
+        return $recordData;
+    }
+
+    /**
+     * Use OLE reader to extract the relevant data streams from the OLE file.
+     *
+     * @param string $pFilename
+     */
+    private function loadOLE($pFilename): void
+    {
+        // OLE reader
+        $ole = new OLERead();
+        // get excel data,
+        $ole->read($pFilename);
+        // Get workbook data: workbook stream + sheet streams
+        $this->data = $ole->getStream($ole->wrkbook);
+        // Get summary information data
+        $this->summaryInformation = $ole->getStream($ole->summaryInformation);
+        // Get additional document summary information data
+        $this->documentSummaryInformation = $ole->getStream($ole->documentSummaryInformation);
+    }
+
+    /**
+     * Read summary information.
+     */
+    private function readSummaryInformation(): void
+    {
+        if (!isset($this->summaryInformation)) {
+            return;
+        }
+
+        // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
+        // offset: 2; size: 2;
+        // offset: 4; size: 2; OS version
+        // offset: 6; size: 2; OS indicator
+        // offset: 8; size: 16
+        // offset: 24; size: 4; section count
+        $secCount = self::getInt4d($this->summaryInformation, 24);
+
+        // offset: 28; size: 16; first section's class id: e0 85 9f f2 f9 4f 68 10 ab 91 08 00 2b 27 b3 d9
+        // offset: 44; size: 4
+        $secOffset = self::getInt4d($this->summaryInformation, 44);
+
+        // section header
+        // offset: $secOffset; size: 4; section length
+        $secLength = self::getInt4d($this->summaryInformation, $secOffset);
+
+        // offset: $secOffset+4; size: 4; property count
+        $countProperties = self::getInt4d($this->summaryInformation, $secOffset + 4);
+
+        // initialize code page (used to resolve string values)
+        $codePage = 'CP1252';
+
+        // offset: ($secOffset+8); size: var
+        // loop through property decarations and properties
+        for ($i = 0; $i < $countProperties; ++$i) {
+            // offset: ($secOffset+8) + (8 * $i); size: 4; property ID
+            $id = self::getInt4d($this->summaryInformation, ($secOffset + 8) + (8 * $i));
+
+            // Use value of property id as appropriate
+            // offset: ($secOffset+12) + (8 * $i); size: 4; offset from beginning of section (48)
+            $offset = self::getInt4d($this->summaryInformation, ($secOffset + 12) + (8 * $i));
+
+            $type = self::getInt4d($this->summaryInformation, $secOffset + $offset);
+
+            // initialize property value
+            $value = null;
+
+            // extract property value based on property type
+            switch ($type) {
+                case 0x02: // 2 byte signed integer
+                    $value = self::getUInt2d($this->summaryInformation, $secOffset + 4 + $offset);
+
+                    break;
+                case 0x03: // 4 byte signed integer
+                    $value = self::getInt4d($this->summaryInformation, $secOffset + 4 + $offset);
+
+                    break;
+                case 0x13: // 4 byte unsigned integer
+                    // not needed yet, fix later if necessary
+                    break;
+                case 0x1E: // null-terminated string prepended by dword string length
+                    $byteLength = self::getInt4d($this->summaryInformation, $secOffset + 4 + $offset);
+                    $value = substr($this->summaryInformation, $secOffset + 8 + $offset, $byteLength);
+                    $value = StringHelper::convertEncoding($value, 'UTF-8', $codePage);
+                    $value = rtrim($value);
+
+                    break;
+                case 0x40: // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
+                    // PHP-time
+                    $value = OLE::OLE2LocalDate(substr($this->summaryInformation, $secOffset + 4 + $offset, 8));
+
+                    break;
+                case 0x47: // Clipboard format
+                    // not needed yet, fix later if necessary
+                    break;
+            }
+
+            switch ($id) {
+                case 0x01:    //    Code Page
+                    $codePage = CodePage::numberToName($value);
+
+                    break;
+                case 0x02:    //    Title
+                    $this->spreadsheet->getProperties()->setTitle($value);
+
+                    break;
+                case 0x03:    //    Subject
+                    $this->spreadsheet->getProperties()->setSubject($value);
+
+                    break;
+                case 0x04:    //    Author (Creator)
+                    $this->spreadsheet->getProperties()->setCreator($value);
+
+                    break;
+                case 0x05:    //    Keywords
+                    $this->spreadsheet->getProperties()->setKeywords($value);
+
+                    break;
+                case 0x06:    //    Comments (Description)
+                    $this->spreadsheet->getProperties()->setDescription($value);
+
+                    break;
+                case 0x07:    //    Template
+                    //    Not supported by PhpSpreadsheet
+                    break;
+                case 0x08:    //    Last Saved By (LastModifiedBy)
+                    $this->spreadsheet->getProperties()->setLastModifiedBy($value);
+
+                    break;
+                case 0x09:    //    Revision
+                    //    Not supported by PhpSpreadsheet
+                    break;
+                case 0x0A:    //    Total Editing Time
+                    //    Not supported by PhpSpreadsheet
+                    break;
+                case 0x0B:    //    Last Printed
+                    //    Not supported by PhpSpreadsheet
+                    break;
+                case 0x0C:    //    Created Date/Time
+                    $this->spreadsheet->getProperties()->setCreated($value);
+
+                    break;
+                case 0x0D:    //    Modified Date/Time
+                    $this->spreadsheet->getProperties()->setModified($value);
+
+                    break;
+                case 0x0E:    //    Number of Pages
+                    //    Not supported by PhpSpreadsheet
+                    break;
+                case 0x0F:    //    Number of Words
+                    //    Not supported by PhpSpreadsheet
+                    break;
+                case 0x10:    //    Number of Characters
+                    //    Not supported by PhpSpreadsheet
+                    break;
+                case 0x11:    //    Thumbnail
+                    //    Not supported by PhpSpreadsheet
+                    break;
+                case 0x12:    //    Name of creating application
+                    //    Not supported by PhpSpreadsheet
+                    break;
+                case 0x13:    //    Security
+                    //    Not supported by PhpSpreadsheet
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Read additional document summary information.
+     */
+    private function readDocumentSummaryInformation(): void
+    {
+        if (!isset($this->documentSummaryInformation)) {
+            return;
+        }
+
+        //    offset: 0;    size: 2;    must be 0xFE 0xFF (UTF-16 LE byte order mark)
+        //    offset: 2;    size: 2;
+        //    offset: 4;    size: 2;    OS version
+        //    offset: 6;    size: 2;    OS indicator
+        //    offset: 8;    size: 16
+        //    offset: 24;    size: 4;    section count
+        $secCount = self::getInt4d($this->documentSummaryInformation, 24);
+
+        // offset: 28;    size: 16;    first section's class id: 02 d5 cd d5 9c 2e 1b 10 93 97 08 00 2b 2c f9 ae
+        // offset: 44;    size: 4;    first section offset
+        $secOffset = self::getInt4d($this->documentSummaryInformation, 44);
+
+        //    section header
+        //    offset: $secOffset;    size: 4;    section length
+        $secLength = self::getInt4d($this->documentSummaryInformation, $secOffset);
+
+        //    offset: $secOffset+4;    size: 4;    property count
+        $countProperties = self::getInt4d($this->documentSummaryInformation, $secOffset + 4);
+
+        // initialize code page (used to resolve string values)
+        $codePage = 'CP1252';
+
+        //    offset: ($secOffset+8);    size: var
+        //    loop through property decarations and properties
+        for ($i = 0; $i < $countProperties; ++$i) {
+            //    offset: ($secOffset+8) + (8 * $i);    size: 4;    property ID
+            $id = self::getInt4d($this->documentSummaryInformation, ($secOffset + 8) + (8 * $i));
+
+            // Use value of property id as appropriate
+            // offset: 60 + 8 * $i;    size: 4;    offset from beginning of section (48)
+            $offset = self::getInt4d($this->documentSummaryInformation, ($secOffset + 12) + (8 * $i));
+
+            $type = self::getInt4d($this->documentSummaryInformation, $secOffset + $offset);
+
+            // initialize property value
+            $value = null;
+
+            // extract property value based on property type
+            switch ($type) {
+                case 0x02:    //    2 byte signed integer
+                    $value = self::getUInt2d($this->documentSummaryInformation, $secOffset + 4 + $offset);
+
+                    break;
+                case 0x03:    //    4 byte signed integer
+                    $value = self::getInt4d($this->documentSummaryInformation, $secOffset + 4 + $offset);
+
+                    break;
+                case 0x0B:  // Boolean
+                    $value = self::getUInt2d($this->documentSummaryInformation, $secOffset + 4 + $offset);
+                    $value = ($value == 0 ? false : true);
+
+                    break;
+                case 0x13:    //    4 byte unsigned integer
+                    // not needed yet, fix later if necessary
+                    break;
+                case 0x1E:    //    null-terminated string prepended by dword string length
+                    $byteLength = self::getInt4d($this->documentSummaryInformation, $secOffset + 4 + $offset);
+                    $value = substr($this->documentSummaryInformation, $secOffset + 8 + $offset, $byteLength);
+                    $value = StringHelper::convertEncoding($value, 'UTF-8', $codePage);
+                    $value = rtrim($value);
+
+                    break;
+                case 0x40:    //    Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
+                    // PHP-Time
+                    $value = OLE::OLE2LocalDate(substr($this->documentSummaryInformation, $secOffset + 4 + $offset, 8));
+
+                    break;
+                case 0x47:    //    Clipboard format
+                    // not needed yet, fix later if necessary
+                    break;
+            }
+
+            switch ($id) {
+                case 0x01:    //    Code Page
+                    $codePage = CodePage::numberToName($value);
+
+                    break;
+                case 0x02:    //    Category
+                    $this->spreadsheet->getProperties()->setCategory($value);
+
+                    break;
+                case 0x03:    //    Presentation Target
+                    //    Not supported by PhpSpreadsheet
+                    break;
+                case 0x04:    //    Bytes
+                    //    Not supported by PhpSpreadsheet
+                    break;
+                case 0x05:    //    Lines
+                    //    Not supported by PhpSpreadsheet
+                    break;
+                case 0x06:    //    Paragraphs
+                    //    Not supported by PhpSpreadsheet
+                    break;
+                case 0x07:    //    Slides
+                    //    Not supported by PhpSpreadsheet
+                    break;
+                case 0x08:    //    Notes
+                    //    Not supported by PhpSpreadsheet
+                    break;
+                case 0x09:    //    Hidden Slides
+                    //    Not supported by PhpSpreadsheet
+                    break;
+                case 0x0A:    //    MM Clips
+                    //    Not supported by PhpSpreadsheet
+                    break;
+                case 0x0B:    //    Scale Crop
+                    //    Not supported by PhpSpreadsheet
+                    break;
+                case 0x0C:    //    Heading Pairs
+                    //    Not supported by PhpSpreadsheet
+                    break;
+                case 0x0D:    //    Titles of Parts
+                    //    Not supported by PhpSpreadsheet
+                    break;
+                case 0x0E:    //    Manager
+                    $this->spreadsheet->getProperties()->setManager($value);
+
+                    break;
+                case 0x0F:    //    Company
+                    $this->spreadsheet->getProperties()->setCompany($value);
+
+                    break;
+                case 0x10:    //    Links up-to-date
+                    //    Not supported by PhpSpreadsheet
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Reads a general type of BIFF record. Does nothing except for moving stream pointer forward to next record.
+     */
+    private function readDefault(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+    }
+
+    /**
+     *    The NOTE record specifies a comment associated with a particular cell. In Excel 95 (BIFF7) and earlier versions,
+     *        this record stores a note (cell note). This feature was significantly enhanced in Excel 97.
+     */
+    private function readNote(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if ($this->readDataOnly) {
+            return;
+        }
+
+        $cellAddress = $this->readBIFF8CellAddress(substr($recordData, 0, 4));
+        if ($this->version == self::XLS_BIFF8) {
+            $noteObjID = self::getUInt2d($recordData, 6);
+            $noteAuthor = self::readUnicodeStringLong(substr($recordData, 8));
+            $noteAuthor = $noteAuthor['value'];
+            $this->cellNotes[$noteObjID] = [
+                'cellRef' => $cellAddress,
+                'objectID' => $noteObjID,
+                'author' => $noteAuthor,
+            ];
+        } else {
+            $extension = false;
+            if ($cellAddress == '$B$65536') {
+                //    If the address row is -1 and the column is 0, (which translates as $B$65536) then this is a continuation
+                //        note from the previous cell annotation. We're not yet handling this, so annotations longer than the
+                //        max 2048 bytes will probably throw a wobbly.
+                $row = self::getUInt2d($recordData, 0);
+                $extension = true;
+                $cellAddress = array_pop(array_keys($this->phpSheet->getComments()));
+            }
+
+            $cellAddress = str_replace('$', '', $cellAddress);
+            $noteLength = self::getUInt2d($recordData, 4);
+            $noteText = trim(substr($recordData, 6));
+
+            if ($extension) {
+                //    Concatenate this extension with the currently set comment for the cell
+                $comment = $this->phpSheet->getComment($cellAddress);
+                $commentText = $comment->getText()->getPlainText();
+                $comment->setText($this->parseRichText($commentText . $noteText));
+            } else {
+                //    Set comment for the cell
+                $this->phpSheet->getComment($cellAddress)->setText($this->parseRichText($noteText));
+//                                                    ->setAuthor($author)
+            }
+        }
+    }
+
+    /**
+     * The TEXT Object record contains the text associated with a cell annotation.
+     */
+    private function readTextObject(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if ($this->readDataOnly) {
+            return;
+        }
+
+        // recordData consists of an array of subrecords looking like this:
+        //    grbit: 2 bytes; Option Flags
+        //    rot: 2 bytes; rotation
+        //    cchText: 2 bytes; length of the text (in the first continue record)
+        //    cbRuns: 2 bytes; length of the formatting (in the second continue record)
+        // followed by the continuation records containing the actual text and formatting
+        $grbitOpts = self::getUInt2d($recordData, 0);
+        $rot = self::getUInt2d($recordData, 2);
+        $cchText = self::getUInt2d($recordData, 10);
+        $cbRuns = self::getUInt2d($recordData, 12);
+        $text = $this->getSplicedRecordData();
+
+        $textByte = $text['spliceOffsets'][1] - $text['spliceOffsets'][0] - 1;
+        $textStr = substr($text['recordData'], $text['spliceOffsets'][0] + 1, $textByte);
+        // get 1 byte
+        $is16Bit = ord($text['recordData'][0]);
+        // it is possible to use a compressed format,
+        // which omits the high bytes of all characters, if they are all zero
+        if (($is16Bit & 0x01) === 0) {
+            $textStr = StringHelper::ConvertEncoding($textStr, 'UTF-8', 'ISO-8859-1');
+        } else {
+            $textStr = $this->decodeCodepage($textStr);
+        }
+
+        $this->textObjects[$this->textObjRef] = [
+            'text' => $textStr,
+            'format' => substr($text['recordData'], $text['spliceOffsets'][1], $cbRuns),
+            'alignment' => $grbitOpts,
+            'rotation' => $rot,
+        ];
+    }
+
+    /**
+     * Read BOF.
+     */
+    private function readBof(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = substr($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        // offset: 2; size: 2; type of the following data
+        $substreamType = self::getUInt2d($recordData, 2);
+
+        switch ($substreamType) {
+            case self::XLS_WORKBOOKGLOBALS:
+                $version = self::getUInt2d($recordData, 0);
+                if (($version != self::XLS_BIFF8) && ($version != self::XLS_BIFF7)) {
+                    throw new Exception('Cannot read this Excel file. Version is too old.');
+                }
+                $this->version = $version;
+
+                break;
+            case self::XLS_WORKSHEET:
+                // do not use this version information for anything
+                // it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream
+                break;
+            default:
+                // substream, e.g. chart
+                // just skip the entire substream
+                do {
+                    $code = self::getUInt2d($this->data, $this->pos);
+                    $this->readDefault();
+                } while ($code != self::XLS_TYPE_EOF && $this->pos < $this->dataSize);
+
+                break;
+        }
+    }
+
+    /**
+     * FILEPASS.
+     *
+     * This record is part of the File Protection Block. It
+     * contains information about the read/write password of the
+     * file. All record contents following this record will be
+     * encrypted.
+     *
+     * --    "OpenOffice.org's Documentation of the Microsoft
+     *         Excel File Format"
+     *
+     * The decryption functions and objects used from here on in
+     * are based on the source of Spreadsheet-ParseExcel:
+     * https://metacpan.org/release/Spreadsheet-ParseExcel
+     */
+    private function readFilepass(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+
+        if ($length != 54) {
+            throw new Exception('Unexpected file pass record length');
+        }
+
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if (!$this->verifyPassword('VelvetSweatshop', substr($recordData, 6, 16), substr($recordData, 22, 16), substr($recordData, 38, 16), $this->md5Ctxt)) {
+            throw new Exception('Decryption password incorrect');
+        }
+
+        $this->encryption = self::MS_BIFF_CRYPTO_RC4;
+
+        // Decryption required from the record after next onwards
+        $this->encryptionStartPos = $this->pos + self::getUInt2d($this->data, $this->pos + 2);
+    }
+
+    /**
+     * Make an RC4 decryptor for the given block.
+     *
+     * @param int $block Block for which to create decrypto
+     * @param string $valContext MD5 context state
+     *
+     * @return Xls\RC4
+     */
+    private function makeKey($block, $valContext)
+    {
+        $pwarray = str_repeat("\0", 64);
+
+        for ($i = 0; $i < 5; ++$i) {
+            $pwarray[$i] = $valContext[$i];
+        }
+
+        $pwarray[5] = chr($block & 0xff);
+        $pwarray[6] = chr(($block >> 8) & 0xff);
+        $pwarray[7] = chr(($block >> 16) & 0xff);
+        $pwarray[8] = chr(($block >> 24) & 0xff);
+
+        $pwarray[9] = "\x80";
+        $pwarray[56] = "\x48";
+
+        $md5 = new Xls\MD5();
+        $md5->add($pwarray);
+
+        $s = $md5->getContext();
+
+        return new Xls\RC4($s);
+    }
+
+    /**
+     * Verify RC4 file password.
+     *
+     * @param string $password Password to check
+     * @param string $docid Document id
+     * @param string $salt_data Salt data
+     * @param string $hashedsalt_data Hashed salt data
+     * @param string $valContext Set to the MD5 context of the value
+     *
+     * @return bool Success
+     */
+    private function verifyPassword($password, $docid, $salt_data, $hashedsalt_data, &$valContext)
+    {
+        $pwarray = str_repeat("\0", 64);
+
+        $iMax = strlen($password);
+        for ($i = 0; $i < $iMax; ++$i) {
+            $o = ord(substr($password, $i, 1));
+            $pwarray[2 * $i] = chr($o & 0xff);
+            $pwarray[2 * $i + 1] = chr(($o >> 8) & 0xff);
+        }
+        $pwarray[2 * $i] = chr(0x80);
+        $pwarray[56] = chr(($i << 4) & 0xff);
+
+        $md5 = new Xls\MD5();
+        $md5->add($pwarray);
+
+        $mdContext1 = $md5->getContext();
+
+        $offset = 0;
+        $keyoffset = 0;
+        $tocopy = 5;
+
+        $md5->reset();
+
+        while ($offset != 16) {
+            if ((64 - $offset) < 5) {
+                $tocopy = 64 - $offset;
+            }
+            for ($i = 0; $i <= $tocopy; ++$i) {
+                $pwarray[$offset + $i] = $mdContext1[$keyoffset + $i];
+            }
+            $offset += $tocopy;
+
+            if ($offset == 64) {
+                $md5->add($pwarray);
+                $keyoffset = $tocopy;
+                $tocopy = 5 - $tocopy;
+                $offset = 0;
+
+                continue;
+            }
+
+            $keyoffset = 0;
+            $tocopy = 5;
+            for ($i = 0; $i < 16; ++$i) {
+                $pwarray[$offset + $i] = $docid[$i];
+            }
+            $offset += 16;
+        }
+
+        $pwarray[16] = "\x80";
+        for ($i = 0; $i < 47; ++$i) {
+            $pwarray[17 + $i] = "\0";
+        }
+        $pwarray[56] = "\x80";
+        $pwarray[57] = "\x0a";
+
+        $md5->add($pwarray);
+        $valContext = $md5->getContext();
+
+        $key = $this->makeKey(0, $valContext);
+
+        $salt = $key->RC4($salt_data);
+        $hashedsalt = $key->RC4($hashedsalt_data);
+
+        $salt .= "\x80" . str_repeat("\0", 47);
+        $salt[56] = "\x80";
+
+        $md5->reset();
+        $md5->add($salt);
+        $mdContext2 = $md5->getContext();
+
+        return $mdContext2 == $hashedsalt;
+    }
+
+    /**
+     * CODEPAGE.
+     *
+     * This record stores the text encoding used to write byte
+     * strings, stored as MS Windows code page identifier.
+     *
+     * --    "OpenOffice.org's Documentation of the Microsoft
+     *         Excel File Format"
+     */
+    private function readCodepage(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        // offset: 0; size: 2; code page identifier
+        $codepage = self::getUInt2d($recordData, 0);
+
+        $this->codepage = CodePage::numberToName($codepage);
+    }
+
+    /**
+     * DATEMODE.
+     *
+     * This record specifies the base date for displaying date
+     * values. All dates are stored as count of days past this
+     * base date. In BIFF2-BIFF4 this record is part of the
+     * Calculation Settings Block. In BIFF5-BIFF8 it is
+     * stored in the Workbook Globals Substream.
+     *
+     * --    "OpenOffice.org's Documentation of the Microsoft
+     *         Excel File Format"
+     */
+    private function readDateMode(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        // offset: 0; size: 2; 0 = base 1900, 1 = base 1904
+        Date::setExcelCalendar(Date::CALENDAR_WINDOWS_1900);
+        if (ord($recordData[0]) == 1) {
+            Date::setExcelCalendar(Date::CALENDAR_MAC_1904);
+        }
+    }
+
+    /**
+     * Read a FONT record.
+     */
+    private function readFont(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if (!$this->readDataOnly) {
+            $objFont = new Font();
+
+            // offset: 0; size: 2; height of the font (in twips = 1/20 of a point)
+            $size = self::getUInt2d($recordData, 0);
+            $objFont->setSize($size / 20);
+
+            // offset: 2; size: 2; option flags
+            // bit: 0; mask 0x0001; bold (redundant in BIFF5-BIFF8)
+            // bit: 1; mask 0x0002; italic
+            $isItalic = (0x0002 & self::getUInt2d($recordData, 2)) >> 1;
+            if ($isItalic) {
+                $objFont->setItalic(true);
+            }
+
+            // bit: 2; mask 0x0004; underlined (redundant in BIFF5-BIFF8)
+            // bit: 3; mask 0x0008; strikethrough
+            $isStrike = (0x0008 & self::getUInt2d($recordData, 2)) >> 3;
+            if ($isStrike) {
+                $objFont->setStrikethrough(true);
+            }
+
+            // offset: 4; size: 2; colour index
+            $colorIndex = self::getUInt2d($recordData, 4);
+            $objFont->colorIndex = $colorIndex;
+
+            // offset: 6; size: 2; font weight
+            $weight = self::getUInt2d($recordData, 6);
+            switch ($weight) {
+                case 0x02BC:
+                    $objFont->setBold(true);
+
+                    break;
+            }
+
+            // offset: 8; size: 2; escapement type
+            $escapement = self::getUInt2d($recordData, 8);
+            switch ($escapement) {
+                case 0x0001:
+                    $objFont->setSuperscript(true);
+
+                    break;
+                case 0x0002:
+                    $objFont->setSubscript(true);
+
+                    break;
+            }
+
+            // offset: 10; size: 1; underline type
+            $underlineType = ord($recordData[10]);
+            switch ($underlineType) {
+                case 0x00:
+                    break; // no underline
+                case 0x01:
+                    $objFont->setUnderline(Font::UNDERLINE_SINGLE);
+
+                    break;
+                case 0x02:
+                    $objFont->setUnderline(Font::UNDERLINE_DOUBLE);
+
+                    break;
+                case 0x21:
+                    $objFont->setUnderline(Font::UNDERLINE_SINGLEACCOUNTING);
+
+                    break;
+                case 0x22:
+                    $objFont->setUnderline(Font::UNDERLINE_DOUBLEACCOUNTING);
+
+                    break;
+            }
+
+            // offset: 11; size: 1; font family
+            // offset: 12; size: 1; character set
+            // offset: 13; size: 1; not used
+            // offset: 14; size: var; font name
+            if ($this->version == self::XLS_BIFF8) {
+                $string = self::readUnicodeStringShort(substr($recordData, 14));
+            } else {
+                $string = $this->readByteStringShort(substr($recordData, 14));
+            }
+            $objFont->setName($string['value']);
+
+            $this->objFonts[] = $objFont;
+        }
+    }
+
+    /**
+     * FORMAT.
+     *
+     * This record contains information about a number format.
+     * All FORMAT records occur together in a sequential list.
+     *
+     * In BIFF2-BIFF4 other records referencing a FORMAT record
+     * contain a zero-based index into this list. From BIFF5 on
+     * the FORMAT record contains the index itself that will be
+     * used by other records.
+     *
+     * --    "OpenOffice.org's Documentation of the Microsoft
+     *         Excel File Format"
+     */
+    private function readFormat(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if (!$this->readDataOnly) {
+            $indexCode = self::getUInt2d($recordData, 0);
+
+            if ($this->version == self::XLS_BIFF8) {
+                $string = self::readUnicodeStringLong(substr($recordData, 2));
+            } else {
+                // BIFF7
+                $string = $this->readByteStringShort(substr($recordData, 2));
+            }
+
+            $formatString = $string['value'];
+            $this->formats[$indexCode] = $formatString;
+        }
+    }
+
+    /**
+     * XF - Extended Format.
+     *
+     * This record contains formatting information for cells, rows, columns or styles.
+     * According to https://support.microsoft.com/en-us/help/147732 there are always at least 15 cell style XF
+     * and 1 cell XF.
+     * Inspection of Excel files generated by MS Office Excel shows that XF records 0-14 are cell style XF
+     * and XF record 15 is a cell XF
+     * We only read the first cell style XF and skip the remaining cell style XF records
+     * We read all cell XF records.
+     *
+     * --    "OpenOffice.org's Documentation of the Microsoft
+     *         Excel File Format"
+     */
+    private function readXf(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        $objStyle = new Style();
+
+        if (!$this->readDataOnly) {
+            // offset:  0; size: 2; Index to FONT record
+            if (self::getUInt2d($recordData, 0) < 4) {
+                $fontIndex = self::getUInt2d($recordData, 0);
+            } else {
+                // this has to do with that index 4 is omitted in all BIFF versions for some strange reason
+                // check the OpenOffice documentation of the FONT record
+                $fontIndex = self::getUInt2d($recordData, 0) - 1;
+            }
+            $objStyle->setFont($this->objFonts[$fontIndex]);
+
+            // offset:  2; size: 2; Index to FORMAT record
+            $numberFormatIndex = self::getUInt2d($recordData, 2);
+            if (isset($this->formats[$numberFormatIndex])) {
+                // then we have user-defined format code
+                $numberFormat = ['formatCode' => $this->formats[$numberFormatIndex]];
+            } elseif (($code = NumberFormat::builtInFormatCode($numberFormatIndex)) !== '') {
+                // then we have built-in format code
+                $numberFormat = ['formatCode' => $code];
+            } else {
+                // we set the general format code
+                $numberFormat = ['formatCode' => 'General'];
+            }
+            $objStyle->getNumberFormat()->setFormatCode($numberFormat['formatCode']);
+
+            // offset:  4; size: 2; XF type, cell protection, and parent style XF
+            // bit 2-0; mask 0x0007; XF_TYPE_PROT
+            $xfTypeProt = self::getUInt2d($recordData, 4);
+            // bit 0; mask 0x01; 1 = cell is locked
+            $isLocked = (0x01 & $xfTypeProt) >> 0;
+            $objStyle->getProtection()->setLocked($isLocked ? Protection::PROTECTION_INHERIT : Protection::PROTECTION_UNPROTECTED);
+
+            // bit 1; mask 0x02; 1 = Formula is hidden
+            $isHidden = (0x02 & $xfTypeProt) >> 1;
+            $objStyle->getProtection()->setHidden($isHidden ? Protection::PROTECTION_PROTECTED : Protection::PROTECTION_UNPROTECTED);
+
+            // bit 2; mask 0x04; 0 = Cell XF, 1 = Cell Style XF
+            $isCellStyleXf = (0x04 & $xfTypeProt) >> 2;
+
+            // offset:  6; size: 1; Alignment and text break
+            // bit 2-0, mask 0x07; horizontal alignment
+            $horAlign = (0x07 & ord($recordData[6])) >> 0;
+            switch ($horAlign) {
+                case 0:
+                    $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_GENERAL);
+
+                    break;
+                case 1:
+                    $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT);
+
+                    break;
+                case 2:
+                    $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
+
+                    break;
+                case 3:
+                    $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_RIGHT);
+
+                    break;
+                case 4:
+                    $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_FILL);
+
+                    break;
+                case 5:
+                    $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_JUSTIFY);
+
+                    break;
+                case 6:
+                    $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER_CONTINUOUS);
+
+                    break;
+            }
+            // bit 3, mask 0x08; wrap text
+            $wrapText = (0x08 & ord($recordData[6])) >> 3;
+            switch ($wrapText) {
+                case 0:
+                    $objStyle->getAlignment()->setWrapText(false);
+
+                    break;
+                case 1:
+                    $objStyle->getAlignment()->setWrapText(true);
+
+                    break;
+            }
+            // bit 6-4, mask 0x70; vertical alignment
+            $vertAlign = (0x70 & ord($recordData[6])) >> 4;
+            switch ($vertAlign) {
+                case 0:
+                    $objStyle->getAlignment()->setVertical(Alignment::VERTICAL_TOP);
+
+                    break;
+                case 1:
+                    $objStyle->getAlignment()->setVertical(Alignment::VERTICAL_CENTER);
+
+                    break;
+                case 2:
+                    $objStyle->getAlignment()->setVertical(Alignment::VERTICAL_BOTTOM);
+
+                    break;
+                case 3:
+                    $objStyle->getAlignment()->setVertical(Alignment::VERTICAL_JUSTIFY);
+
+                    break;
+            }
+
+            if ($this->version == self::XLS_BIFF8) {
+                // offset:  7; size: 1; XF_ROTATION: Text rotation angle
+                $angle = ord($recordData[7]);
+                $rotation = 0;
+                if ($angle <= 90) {
+                    $rotation = $angle;
+                } elseif ($angle <= 180) {
+                    $rotation = 90 - $angle;
+                } elseif ($angle == 255) {
+                    $rotation = -165;
+                }
+                $objStyle->getAlignment()->setTextRotation($rotation);
+
+                // offset:  8; size: 1; Indentation, shrink to cell size, and text direction
+                // bit: 3-0; mask: 0x0F; indent level
+                $indent = (0x0F & ord($recordData[8])) >> 0;
+                $objStyle->getAlignment()->setIndent($indent);
+
+                // bit: 4; mask: 0x10; 1 = shrink content to fit into cell
+                $shrinkToFit = (0x10 & ord($recordData[8])) >> 4;
+                switch ($shrinkToFit) {
+                    case 0:
+                        $objStyle->getAlignment()->setShrinkToFit(false);
+
+                        break;
+                    case 1:
+                        $objStyle->getAlignment()->setShrinkToFit(true);
+
+                        break;
+                }
+
+                // offset:  9; size: 1; Flags used for attribute groups
+
+                // offset: 10; size: 4; Cell border lines and background area
+                // bit: 3-0; mask: 0x0000000F; left style
+                if ($bordersLeftStyle = Xls\Style\Border::lookup((0x0000000F & self::getInt4d($recordData, 10)) >> 0)) {
+                    $objStyle->getBorders()->getLeft()->setBorderStyle($bordersLeftStyle);
+                }
+                // bit: 7-4; mask: 0x000000F0; right style
+                if ($bordersRightStyle = Xls\Style\Border::lookup((0x000000F0 & self::getInt4d($recordData, 10)) >> 4)) {
+                    $objStyle->getBorders()->getRight()->setBorderStyle($bordersRightStyle);
+                }
+                // bit: 11-8; mask: 0x00000F00; top style
+                if ($bordersTopStyle = Xls\Style\Border::lookup((0x00000F00 & self::getInt4d($recordData, 10)) >> 8)) {
+                    $objStyle->getBorders()->getTop()->setBorderStyle($bordersTopStyle);
+                }
+                // bit: 15-12; mask: 0x0000F000; bottom style
+                if ($bordersBottomStyle = Xls\Style\Border::lookup((0x0000F000 & self::getInt4d($recordData, 10)) >> 12)) {
+                    $objStyle->getBorders()->getBottom()->setBorderStyle($bordersBottomStyle);
+                }
+                // bit: 22-16; mask: 0x007F0000; left color
+                $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & self::getInt4d($recordData, 10)) >> 16;
+
+                // bit: 29-23; mask: 0x3F800000; right color
+                $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & self::getInt4d($recordData, 10)) >> 23;
+
+                // bit: 30; mask: 0x40000000; 1 = diagonal line from top left to right bottom
+                $diagonalDown = (0x40000000 & self::getInt4d($recordData, 10)) >> 30 ? true : false;
+
+                // bit: 31; mask: 0x80000000; 1 = diagonal line from bottom left to top right
+                $diagonalUp = (0x80000000 & self::getInt4d($recordData, 10)) >> 31 ? true : false;
+
+                if ($diagonalUp == false && $diagonalDown == false) {
+                    $objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_NONE);
+                } elseif ($diagonalUp == true && $diagonalDown == false) {
+                    $objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_UP);
+                } elseif ($diagonalUp == false && $diagonalDown == true) {
+                    $objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_DOWN);
+                } elseif ($diagonalUp == true && $diagonalDown == true) {
+                    $objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_BOTH);
+                }
+
+                // offset: 14; size: 4;
+                // bit: 6-0; mask: 0x0000007F; top color
+                $objStyle->getBorders()->getTop()->colorIndex = (0x0000007F & self::getInt4d($recordData, 14)) >> 0;
+
+                // bit: 13-7; mask: 0x00003F80; bottom color
+                $objStyle->getBorders()->getBottom()->colorIndex = (0x00003F80 & self::getInt4d($recordData, 14)) >> 7;
+
+                // bit: 20-14; mask: 0x001FC000; diagonal color
+                $objStyle->getBorders()->getDiagonal()->colorIndex = (0x001FC000 & self::getInt4d($recordData, 14)) >> 14;
+
+                // bit: 24-21; mask: 0x01E00000; diagonal style
+                if ($bordersDiagonalStyle = Xls\Style\Border::lookup((0x01E00000 & self::getInt4d($recordData, 14)) >> 21)) {
+                    $objStyle->getBorders()->getDiagonal()->setBorderStyle($bordersDiagonalStyle);
+                }
+
+                // bit: 31-26; mask: 0xFC000000 fill pattern
+                if ($fillType = Xls\Style\FillPattern::lookup((0xFC000000 & self::getInt4d($recordData, 14)) >> 26)) {
+                    $objStyle->getFill()->setFillType($fillType);
+                }
+                // offset: 18; size: 2; pattern and background colour
+                // bit: 6-0; mask: 0x007F; color index for pattern color
+                $objStyle->getFill()->startcolorIndex = (0x007F & self::getUInt2d($recordData, 18)) >> 0;
+
+                // bit: 13-7; mask: 0x3F80; color index for pattern background
+                $objStyle->getFill()->endcolorIndex = (0x3F80 & self::getUInt2d($recordData, 18)) >> 7;
+            } else {
+                // BIFF5
+
+                // offset: 7; size: 1; Text orientation and flags
+                $orientationAndFlags = ord($recordData[7]);
+
+                // bit: 1-0; mask: 0x03; XF_ORIENTATION: Text orientation
+                $xfOrientation = (0x03 & $orientationAndFlags) >> 0;
+                switch ($xfOrientation) {
+                    case 0:
+                        $objStyle->getAlignment()->setTextRotation(0);
+
+                        break;
+                    case 1:
+                        $objStyle->getAlignment()->setTextRotation(-165);
+
+                        break;
+                    case 2:
+                        $objStyle->getAlignment()->setTextRotation(90);
+
+                        break;
+                    case 3:
+                        $objStyle->getAlignment()->setTextRotation(-90);
+
+                        break;
+                }
+
+                // offset: 8; size: 4; cell border lines and background area
+                $borderAndBackground = self::getInt4d($recordData, 8);
+
+                // bit: 6-0; mask: 0x0000007F; color index for pattern color
+                $objStyle->getFill()->startcolorIndex = (0x0000007F & $borderAndBackground) >> 0;
+
+                // bit: 13-7; mask: 0x00003F80; color index for pattern background
+                $objStyle->getFill()->endcolorIndex = (0x00003F80 & $borderAndBackground) >> 7;
+
+                // bit: 21-16; mask: 0x003F0000; fill pattern
+                $objStyle->getFill()->setFillType(Xls\Style\FillPattern::lookup((0x003F0000 & $borderAndBackground) >> 16));
+
+                // bit: 24-22; mask: 0x01C00000; bottom line style
+                $objStyle->getBorders()->getBottom()->setBorderStyle(Xls\Style\Border::lookup((0x01C00000 & $borderAndBackground) >> 22));
+
+                // bit: 31-25; mask: 0xFE000000; bottom line color
+                $objStyle->getBorders()->getBottom()->colorIndex = (0xFE000000 & $borderAndBackground) >> 25;
+
+                // offset: 12; size: 4; cell border lines
+                $borderLines = self::getInt4d($recordData, 12);
+
+                // bit: 2-0; mask: 0x00000007; top line style
+                $objStyle->getBorders()->getTop()->setBorderStyle(Xls\Style\Border::lookup((0x00000007 & $borderLines) >> 0));
+
+                // bit: 5-3; mask: 0x00000038; left line style
+                $objStyle->getBorders()->getLeft()->setBorderStyle(Xls\Style\Border::lookup((0x00000038 & $borderLines) >> 3));
+
+                // bit: 8-6; mask: 0x000001C0; right line style
+                $objStyle->getBorders()->getRight()->setBorderStyle(Xls\Style\Border::lookup((0x000001C0 & $borderLines) >> 6));
+
+                // bit: 15-9; mask: 0x0000FE00; top line color index
+                $objStyle->getBorders()->getTop()->colorIndex = (0x0000FE00 & $borderLines) >> 9;
+
+                // bit: 22-16; mask: 0x007F0000; left line color index
+                $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & $borderLines) >> 16;
+
+                // bit: 29-23; mask: 0x3F800000; right line color index
+                $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & $borderLines) >> 23;
+            }
+
+            // add cellStyleXf or cellXf and update mapping
+            if ($isCellStyleXf) {
+                // we only read one style XF record which is always the first
+                if ($this->xfIndex == 0) {
+                    $this->spreadsheet->addCellStyleXf($objStyle);
+                    $this->mapCellStyleXfIndex[$this->xfIndex] = 0;
+                }
+            } else {
+                // we read all cell XF records
+                $this->spreadsheet->addCellXf($objStyle);
+                $this->mapCellXfIndex[$this->xfIndex] = count($this->spreadsheet->getCellXfCollection()) - 1;
+            }
+
+            // update XF index for when we read next record
+            ++$this->xfIndex;
+        }
+    }
+
+    private function readXfExt(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if (!$this->readDataOnly) {
+            // offset: 0; size: 2; 0x087D = repeated header
+
+            // offset: 2; size: 2
+
+            // offset: 4; size: 8; not used
+
+            // offset: 12; size: 2; record version
+
+            // offset: 14; size: 2; index to XF record which this record modifies
+            $ixfe = self::getUInt2d($recordData, 14);
+
+            // offset: 16; size: 2; not used
+
+            // offset: 18; size: 2; number of extension properties that follow
+            $cexts = self::getUInt2d($recordData, 18);
+
+            // start reading the actual extension data
+            $offset = 20;
+            while ($offset < $length) {
+                // extension type
+                $extType = self::getUInt2d($recordData, $offset);
+
+                // extension length
+                $cb = self::getUInt2d($recordData, $offset + 2);
+
+                // extension data
+                $extData = substr($recordData, $offset + 4, $cb);
+
+                switch ($extType) {
+                    case 4:        // fill start color
+                        $xclfType = self::getUInt2d($extData, 0); // color type
+                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+                        if ($xclfType == 2) {
+                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
+
+                            // modify the relevant style property
+                            if (isset($this->mapCellXfIndex[$ixfe])) {
+                                $fill = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFill();
+                                $fill->getStartColor()->setRGB($rgb);
+                                $fill->startcolorIndex = null; // normal color index does not apply, discard
+                            }
+                        }
+
+                        break;
+                    case 5:        // fill end color
+                        $xclfType = self::getUInt2d($extData, 0); // color type
+                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+                        if ($xclfType == 2) {
+                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
+
+                            // modify the relevant style property
+                            if (isset($this->mapCellXfIndex[$ixfe])) {
+                                $fill = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFill();
+                                $fill->getEndColor()->setRGB($rgb);
+                                $fill->endcolorIndex = null; // normal color index does not apply, discard
+                            }
+                        }
+
+                        break;
+                    case 7:        // border color top
+                        $xclfType = self::getUInt2d($extData, 0); // color type
+                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+                        if ($xclfType == 2) {
+                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
+
+                            // modify the relevant style property
+                            if (isset($this->mapCellXfIndex[$ixfe])) {
+                                $top = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getTop();
+                                $top->getColor()->setRGB($rgb);
+                                $top->colorIndex = null; // normal color index does not apply, discard
+                            }
+                        }
+
+                        break;
+                    case 8:        // border color bottom
+                        $xclfType = self::getUInt2d($extData, 0); // color type
+                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+                        if ($xclfType == 2) {
+                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
+
+                            // modify the relevant style property
+                            if (isset($this->mapCellXfIndex[$ixfe])) {
+                                $bottom = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getBottom();
+                                $bottom->getColor()->setRGB($rgb);
+                                $bottom->colorIndex = null; // normal color index does not apply, discard
+                            }
+                        }
+
+                        break;
+                    case 9:        // border color left
+                        $xclfType = self::getUInt2d($extData, 0); // color type
+                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+                        if ($xclfType == 2) {
+                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
+
+                            // modify the relevant style property
+                            if (isset($this->mapCellXfIndex[$ixfe])) {
+                                $left = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getLeft();
+                                $left->getColor()->setRGB($rgb);
+                                $left->colorIndex = null; // normal color index does not apply, discard
+                            }
+                        }
+
+                        break;
+                    case 10:        // border color right
+                        $xclfType = self::getUInt2d($extData, 0); // color type
+                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+                        if ($xclfType == 2) {
+                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
+
+                            // modify the relevant style property
+                            if (isset($this->mapCellXfIndex[$ixfe])) {
+                                $right = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getRight();
+                                $right->getColor()->setRGB($rgb);
+                                $right->colorIndex = null; // normal color index does not apply, discard
+                            }
+                        }
+
+                        break;
+                    case 11:        // border color diagonal
+                        $xclfType = self::getUInt2d($extData, 0); // color type
+                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+                        if ($xclfType == 2) {
+                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
+
+                            // modify the relevant style property
+                            if (isset($this->mapCellXfIndex[$ixfe])) {
+                                $diagonal = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getDiagonal();
+                                $diagonal->getColor()->setRGB($rgb);
+                                $diagonal->colorIndex = null; // normal color index does not apply, discard
+                            }
+                        }
+
+                        break;
+                    case 13:    // font color
+                        $xclfType = self::getUInt2d($extData, 0); // color type
+                        $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+                        if ($xclfType == 2) {
+                            $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
+
+                            // modify the relevant style property
+                            if (isset($this->mapCellXfIndex[$ixfe])) {
+                                $font = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFont();
+                                $font->getColor()->setRGB($rgb);
+                                $font->colorIndex = null; // normal color index does not apply, discard
+                            }
+                        }
+
+                        break;
+                }
+
+                $offset += $cb;
+            }
+        }
+    }
+
+    /**
+     * Read STYLE record.
+     */
+    private function readStyle(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if (!$this->readDataOnly) {
+            // offset: 0; size: 2; index to XF record and flag for built-in style
+            $ixfe = self::getUInt2d($recordData, 0);
+
+            // bit: 11-0; mask 0x0FFF; index to XF record
+            $xfIndex = (0x0FFF & $ixfe) >> 0;
+
+            // bit: 15; mask 0x8000; 0 = user-defined style, 1 = built-in style
+            $isBuiltIn = (bool) ((0x8000 & $ixfe) >> 15);
+
+            if ($isBuiltIn) {
+                // offset: 2; size: 1; identifier for built-in style
+                $builtInId = ord($recordData[2]);
+
+                switch ($builtInId) {
+                    case 0x00:
+                        // currently, we are not using this for anything
+                        break;
+                    default:
+                        break;
+                }
+            }
+            // user-defined; not supported by PhpSpreadsheet
+        }
+    }
+
+    /**
+     * Read PALETTE record.
+     */
+    private function readPalette(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if (!$this->readDataOnly) {
+            // offset: 0; size: 2; number of following colors
+            $nm = self::getUInt2d($recordData, 0);
+
+            // list of RGB colors
+            for ($i = 0; $i < $nm; ++$i) {
+                $rgb = substr($recordData, 2 + 4 * $i, 4);
+                $this->palette[] = self::readRGB($rgb);
+            }
+        }
+    }
+
+    /**
+     * SHEET.
+     *
+     * This record is  located in the  Workbook Globals
+     * Substream  and represents a sheet inside the workbook.
+     * One SHEET record is written for each sheet. It stores the
+     * sheet name and a stream offset to the BOF record of the
+     * respective Sheet Substream within the Workbook Stream.
+     *
+     * --    "OpenOffice.org's Documentation of the Microsoft
+     *         Excel File Format"
+     */
+    private function readSheet(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // offset: 0; size: 4; absolute stream position of the BOF record of the sheet
+        // NOTE: not encrypted
+        $rec_offset = self::getInt4d($this->data, $this->pos + 4);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        // offset: 4; size: 1; sheet state
+        switch (ord($recordData[4])) {
+            case 0x00:
+                $sheetState = Worksheet::SHEETSTATE_VISIBLE;
+
+                break;
+            case 0x01:
+                $sheetState = Worksheet::SHEETSTATE_HIDDEN;
+
+                break;
+            case 0x02:
+                $sheetState = Worksheet::SHEETSTATE_VERYHIDDEN;
+
+                break;
+            default:
+                $sheetState = Worksheet::SHEETSTATE_VISIBLE;
+
+                break;
+        }
+
+        // offset: 5; size: 1; sheet type
+        $sheetType = ord($recordData[5]);
+
+        // offset: 6; size: var; sheet name
+        if ($this->version == self::XLS_BIFF8) {
+            $string = self::readUnicodeStringShort(substr($recordData, 6));
+            $rec_name = $string['value'];
+        } elseif ($this->version == self::XLS_BIFF7) {
+            $string = $this->readByteStringShort(substr($recordData, 6));
+            $rec_name = $string['value'];
+        }
+
+        $this->sheets[] = [
+            'name' => $rec_name,
+            'offset' => $rec_offset,
+            'sheetState' => $sheetState,
+            'sheetType' => $sheetType,
+        ];
+    }
+
+    /**
+     * Read EXTERNALBOOK record.
+     */
+    private function readExternalBook(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        // offset within record data
+        $offset = 0;
+
+        // there are 4 types of records
+        if (strlen($recordData) > 4) {
+            // external reference
+            // offset: 0; size: 2; number of sheet names ($nm)
+            $nm = self::getUInt2d($recordData, 0);
+            $offset += 2;
+
+            // offset: 2; size: var; encoded URL without sheet name (Unicode string, 16-bit length)
+            $encodedUrlString = self::readUnicodeStringLong(substr($recordData, 2));
+            $offset += $encodedUrlString['size'];
+
+            // offset: var; size: var; list of $nm sheet names (Unicode strings, 16-bit length)
+            $externalSheetNames = [];
+            for ($i = 0; $i < $nm; ++$i) {
+                $externalSheetNameString = self::readUnicodeStringLong(substr($recordData, $offset));
+                $externalSheetNames[] = $externalSheetNameString['value'];
+                $offset += $externalSheetNameString['size'];
+            }
+
+            // store the record data
+            $this->externalBooks[] = [
+                'type' => 'external',
+                'encodedUrl' => $encodedUrlString['value'],
+                'externalSheetNames' => $externalSheetNames,
+            ];
+        } elseif (substr($recordData, 2, 2) == pack('CC', 0x01, 0x04)) {
+            // internal reference
+            // offset: 0; size: 2; number of sheet in this document
+            // offset: 2; size: 2; 0x01 0x04
+            $this->externalBooks[] = [
+                'type' => 'internal',
+            ];
+        } elseif (substr($recordData, 0, 4) == pack('vCC', 0x0001, 0x01, 0x3A)) {
+            // add-in function
+            // offset: 0; size: 2; 0x0001
+            $this->externalBooks[] = [
+                'type' => 'addInFunction',
+            ];
+        } elseif (substr($recordData, 0, 2) == pack('v', 0x0000)) {
+            // DDE links, OLE links
+            // offset: 0; size: 2; 0x0000
+            // offset: 2; size: var; encoded source document name
+            $this->externalBooks[] = [
+                'type' => 'DDEorOLE',
+            ];
+        }
+    }
+
+    /**
+     * Read EXTERNNAME record.
+     */
+    private function readExternName(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        // external sheet references provided for named cells
+        if ($this->version == self::XLS_BIFF8) {
+            // offset: 0; size: 2; options
+            $options = self::getUInt2d($recordData, 0);
+
+            // offset: 2; size: 2;
+
+            // offset: 4; size: 2; not used
+
+            // offset: 6; size: var
+            $nameString = self::readUnicodeStringShort(substr($recordData, 6));
+
+            // offset: var; size: var; formula data
+            $offset = 6 + $nameString['size'];
+            $formula = $this->getFormulaFromStructure(substr($recordData, $offset));
+
+            $this->externalNames[] = [
+                'name' => $nameString['value'],
+                'formula' => $formula,
+            ];
+        }
+    }
+
+    /**
+     * Read EXTERNSHEET record.
+     */
+    private function readExternSheet(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        // external sheet references provided for named cells
+        if ($this->version == self::XLS_BIFF8) {
+            // offset: 0; size: 2; number of following ref structures
+            $nm = self::getUInt2d($recordData, 0);
+            for ($i = 0; $i < $nm; ++$i) {
+                $this->ref[] = [
+                    // offset: 2 + 6 * $i; index to EXTERNALBOOK record
+                    'externalBookIndex' => self::getUInt2d($recordData, 2 + 6 * $i),
+                    // offset: 4 + 6 * $i; index to first sheet in EXTERNALBOOK record
+                    'firstSheetIndex' => self::getUInt2d($recordData, 4 + 6 * $i),
+                    // offset: 6 + 6 * $i; index to last sheet in EXTERNALBOOK record
+                    'lastSheetIndex' => self::getUInt2d($recordData, 6 + 6 * $i),
+                ];
+            }
+        }
+    }
+
+    /**
+     * DEFINEDNAME.
+     *
+     * This record is part of a Link Table. It contains the name
+     * and the token array of an internal defined name. Token
+     * arrays of defined names contain tokens with aberrant
+     * token classes.
+     *
+     * --    "OpenOffice.org's Documentation of the Microsoft
+     *         Excel File Format"
+     */
+    private function readDefinedName(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if ($this->version == self::XLS_BIFF8) {
+            // retrieves named cells
+
+            // offset: 0; size: 2; option flags
+            $opts = self::getUInt2d($recordData, 0);
+
+            // bit: 5; mask: 0x0020; 0 = user-defined name, 1 = built-in-name
+            $isBuiltInName = (0x0020 & $opts) >> 5;
+
+            // offset: 2; size: 1; keyboard shortcut
+
+            // offset: 3; size: 1; length of the name (character count)
+            $nlen = ord($recordData[3]);
+
+            // offset: 4; size: 2; size of the formula data (it can happen that this is zero)
+            // note: there can also be additional data, this is not included in $flen
+            $flen = self::getUInt2d($recordData, 4);
+
+            // offset: 8; size: 2; 0=Global name, otherwise index to sheet (1-based)
+            $scope = self::getUInt2d($recordData, 8);
+
+            // offset: 14; size: var; Name (Unicode string without length field)
+            $string = self::readUnicodeString(substr($recordData, 14), $nlen);
+
+            // offset: var; size: $flen; formula data
+            $offset = 14 + $string['size'];
+            $formulaStructure = pack('v', $flen) . substr($recordData, $offset);
+
+            try {
+                $formula = $this->getFormulaFromStructure($formulaStructure);
+            } catch (PhpSpreadsheetException $e) {
+                $formula = '';
+            }
+
+            $this->definedname[] = [
+                'isBuiltInName' => $isBuiltInName,
+                'name' => $string['value'],
+                'formula' => $formula,
+                'scope' => $scope,
+            ];
+        }
+    }
+
+    /**
+     * Read MSODRAWINGGROUP record.
+     */
+    private function readMsoDrawingGroup(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+
+        // get spliced record data
+        $splicedRecordData = $this->getSplicedRecordData();
+        $recordData = $splicedRecordData['recordData'];
+
+        $this->drawingGroupData .= $recordData;
+    }
+
+    /**
+     * SST - Shared String Table.
+     *
+     * This record contains a list of all strings used anywhere
+     * in the workbook. Each string occurs only once. The
+     * workbook uses indexes into the list to reference the
+     * strings.
+     *
+     * --    "OpenOffice.org's Documentation of the Microsoft
+     *         Excel File Format"
+     */
+    private function readSst(): void
+    {
+        // offset within (spliced) record data
+        $pos = 0;
+
+        // get spliced record data
+        $splicedRecordData = $this->getSplicedRecordData();
+
+        $recordData = $splicedRecordData['recordData'];
+        $spliceOffsets = $splicedRecordData['spliceOffsets'];
+
+        // offset: 0; size: 4; total number of strings in the workbook
+        $pos += 4;
+
+        // offset: 4; size: 4; number of following strings ($nm)
+        $nm = self::getInt4d($recordData, 4);
+        $pos += 4;
+
+        // loop through the Unicode strings (16-bit length)
+        for ($i = 0; $i < $nm; ++$i) {
+            // number of characters in the Unicode string
+            $numChars = self::getUInt2d($recordData, $pos);
+            $pos += 2;
+
+            // option flags
+            $optionFlags = ord($recordData[$pos]);
+            ++$pos;
+
+            // bit: 0; mask: 0x01; 0 = compressed; 1 = uncompressed
+            $isCompressed = (($optionFlags & 0x01) == 0);
+
+            // bit: 2; mask: 0x02; 0 = ordinary; 1 = Asian phonetic
+            $hasAsian = (($optionFlags & 0x04) != 0);
+
+            // bit: 3; mask: 0x03; 0 = ordinary; 1 = Rich-Text
+            $hasRichText = (($optionFlags & 0x08) != 0);
+
+            if ($hasRichText) {
+                // number of Rich-Text formatting runs
+                $formattingRuns = self::getUInt2d($recordData, $pos);
+                $pos += 2;
+            }
+
+            if ($hasAsian) {
+                // size of Asian phonetic setting
+                $extendedRunLength = self::getInt4d($recordData, $pos);
+                $pos += 4;
+            }
+
+            // expected byte length of character array if not split
+            $len = ($isCompressed) ? $numChars : $numChars * 2;
+
+            // look up limit position
+            foreach ($spliceOffsets as $spliceOffset) {
+                // it can happen that the string is empty, therefore we need
+                // <= and not just <
+                if ($pos <= $spliceOffset) {
+                    $limitpos = $spliceOffset;
+
+                    break;
+                }
+            }
+
+            if ($pos + $len <= $limitpos) {
+                // character array is not split between records
+
+                $retstr = substr($recordData, $pos, $len);
+                $pos += $len;
+            } else {
+                // character array is split between records
+
+                // first part of character array
+                $retstr = substr($recordData, $pos, $limitpos - $pos);
+
+                $bytesRead = $limitpos - $pos;
+
+                // remaining characters in Unicode string
+                $charsLeft = $numChars - (($isCompressed) ? $bytesRead : ($bytesRead / 2));
+
+                $pos = $limitpos;
+
+                // keep reading the characters
+                while ($charsLeft > 0) {
+                    // look up next limit position, in case the string span more than one continue record
+                    foreach ($spliceOffsets as $spliceOffset) {
+                        if ($pos < $spliceOffset) {
+                            $limitpos = $spliceOffset;
+
+                            break;
+                        }
+                    }
+
+                    // repeated option flags
+                    // OpenOffice.org documentation 5.21
+                    $option = ord($recordData[$pos]);
+                    ++$pos;
+
+                    if ($isCompressed && ($option == 0)) {
+                        // 1st fragment compressed
+                        // this fragment compressed
+                        $len = min($charsLeft, $limitpos - $pos);
+                        $retstr .= substr($recordData, $pos, $len);
+                        $charsLeft -= $len;
+                        $isCompressed = true;
+                    } elseif (!$isCompressed && ($option != 0)) {
+                        // 1st fragment uncompressed
+                        // this fragment uncompressed
+                        $len = min($charsLeft * 2, $limitpos - $pos);
+                        $retstr .= substr($recordData, $pos, $len);
+                        $charsLeft -= $len / 2;
+                        $isCompressed = false;
+                    } elseif (!$isCompressed && ($option == 0)) {
+                        // 1st fragment uncompressed
+                        // this fragment compressed
+                        $len = min($charsLeft, $limitpos - $pos);
+                        for ($j = 0; $j < $len; ++$j) {
+                            $retstr .= $recordData[$pos + $j]
+                            . chr(0);
+                        }
+                        $charsLeft -= $len;
+                        $isCompressed = false;
+                    } else {
+                        // 1st fragment compressed
+                        // this fragment uncompressed
+                        $newstr = '';
+                        $jMax = strlen($retstr);
+                        for ($j = 0; $j < $jMax; ++$j) {
+                            $newstr .= $retstr[$j] . chr(0);
+                        }
+                        $retstr = $newstr;
+                        $len = min($charsLeft * 2, $limitpos - $pos);
+                        $retstr .= substr($recordData, $pos, $len);
+                        $charsLeft -= $len / 2;
+                        $isCompressed = false;
+                    }
+
+                    $pos += $len;
+                }
+            }
+
+            // convert to UTF-8
+            $retstr = self::encodeUTF16($retstr, $isCompressed);
+
+            // read additional Rich-Text information, if any
+            $fmtRuns = [];
+            if ($hasRichText) {
+                // list of formatting runs
+                for ($j = 0; $j < $formattingRuns; ++$j) {
+                    // first formatted character; zero-based
+                    $charPos = self::getUInt2d($recordData, $pos + $j * 4);
+
+                    // index to font record
+                    $fontIndex = self::getUInt2d($recordData, $pos + 2 + $j * 4);
+
+                    $fmtRuns[] = [
+                        'charPos' => $charPos,
+                        'fontIndex' => $fontIndex,
+                    ];
+                }
+                $pos += 4 * $formattingRuns;
+            }
+
+            // read additional Asian phonetics information, if any
+            if ($hasAsian) {
+                // For Asian phonetic settings, we skip the extended string data
+                $pos += $extendedRunLength;
+            }
+
+            // store the shared sting
+            $this->sst[] = [
+                'value' => $retstr,
+                'fmtRuns' => $fmtRuns,
+            ];
+        }
+
+        // getSplicedRecordData() takes care of moving current position in data stream
+    }
+
+    /**
+     * Read PRINTGRIDLINES record.
+     */
+    private function readPrintGridlines(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
+            // offset: 0; size: 2; 0 = do not print sheet grid lines; 1 = print sheet gridlines
+            $printGridlines = (bool) self::getUInt2d($recordData, 0);
+            $this->phpSheet->setPrintGridlines($printGridlines);
+        }
+    }
+
+    /**
+     * Read DEFAULTROWHEIGHT record.
+     */
+    private function readDefaultRowHeight(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        // offset: 0; size: 2; option flags
+        // offset: 2; size: 2; default height for unused rows, (twips 1/20 point)
+        $height = self::getUInt2d($recordData, 2);
+        $this->phpSheet->getDefaultRowDimension()->setRowHeight($height / 20);
+    }
+
+    /**
+     * Read SHEETPR record.
+     */
+    private function readSheetPr(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        // offset: 0; size: 2
+
+        // bit: 6; mask: 0x0040; 0 = outline buttons above outline group
+        $isSummaryBelow = (0x0040 & self::getUInt2d($recordData, 0)) >> 6;
+        $this->phpSheet->setShowSummaryBelow($isSummaryBelow);
+
+        // bit: 7; mask: 0x0080; 0 = outline buttons left of outline group
+        $isSummaryRight = (0x0080 & self::getUInt2d($recordData, 0)) >> 7;
+        $this->phpSheet->setShowSummaryRight($isSummaryRight);
+
+        // bit: 8; mask: 0x100; 0 = scale printout in percent, 1 = fit printout to number of pages
+        // this corresponds to radio button setting in page setup dialog in Excel
+        $this->isFitToPages = (bool) ((0x0100 & self::getUInt2d($recordData, 0)) >> 8);
+    }
+
+    /**
+     * Read HORIZONTALPAGEBREAKS record.
+     */
+    private function readHorizontalPageBreaks(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
+            // offset: 0; size: 2; number of the following row index structures
+            $nm = self::getUInt2d($recordData, 0);
+
+            // offset: 2; size: 6 * $nm; list of $nm row index structures
+            for ($i = 0; $i < $nm; ++$i) {
+                $r = self::getUInt2d($recordData, 2 + 6 * $i);
+                $cf = self::getUInt2d($recordData, 2 + 6 * $i + 2);
+                $cl = self::getUInt2d($recordData, 2 + 6 * $i + 4);
+
+                // not sure why two column indexes are necessary?
+                $this->phpSheet->setBreakByColumnAndRow($cf + 1, $r, Worksheet::BREAK_ROW);
+            }
+        }
+    }
+
+    /**
+     * Read VERTICALPAGEBREAKS record.
+     */
+    private function readVerticalPageBreaks(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
+            // offset: 0; size: 2; number of the following column index structures
+            $nm = self::getUInt2d($recordData, 0);
+
+            // offset: 2; size: 6 * $nm; list of $nm row index structures
+            for ($i = 0; $i < $nm; ++$i) {
+                $c = self::getUInt2d($recordData, 2 + 6 * $i);
+                $rf = self::getUInt2d($recordData, 2 + 6 * $i + 2);
+                $rl = self::getUInt2d($recordData, 2 + 6 * $i + 4);
+
+                // not sure why two row indexes are necessary?
+                $this->phpSheet->setBreakByColumnAndRow($c + 1, $rf, Worksheet::BREAK_COLUMN);
+            }
+        }
+    }
+
+    /**
+     * Read HEADER record.
+     */
+    private function readHeader(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if (!$this->readDataOnly) {
+            // offset: 0; size: var
+            // realized that $recordData can be empty even when record exists
+            if ($recordData) {
+                if ($this->version == self::XLS_BIFF8) {
+                    $string = self::readUnicodeStringLong($recordData);
+                } else {
+                    $string = $this->readByteStringShort($recordData);
+                }
+
+                $this->phpSheet->getHeaderFooter()->setOddHeader($string['value']);
+                $this->phpSheet->getHeaderFooter()->setEvenHeader($string['value']);
+            }
+        }
+    }
+
+    /**
+     * Read FOOTER record.
+     */
+    private function readFooter(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if (!$this->readDataOnly) {
+            // offset: 0; size: var
+            // realized that $recordData can be empty even when record exists
+            if ($recordData) {
+                if ($this->version == self::XLS_BIFF8) {
+                    $string = self::readUnicodeStringLong($recordData);
+                } else {
+                    $string = $this->readByteStringShort($recordData);
+                }
+                $this->phpSheet->getHeaderFooter()->setOddFooter($string['value']);
+                $this->phpSheet->getHeaderFooter()->setEvenFooter($string['value']);
+            }
+        }
+    }
+
+    /**
+     * Read HCENTER record.
+     */
+    private function readHcenter(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if (!$this->readDataOnly) {
+            // offset: 0; size: 2; 0 = print sheet left aligned, 1 = print sheet centered horizontally
+            $isHorizontalCentered = (bool) self::getUInt2d($recordData, 0);
+
+            $this->phpSheet->getPageSetup()->setHorizontalCentered($isHorizontalCentered);
+        }
+    }
+
+    /**
+     * Read VCENTER record.
+     */
+    private function readVcenter(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if (!$this->readDataOnly) {
+            // offset: 0; size: 2; 0 = print sheet aligned at top page border, 1 = print sheet vertically centered
+            $isVerticalCentered = (bool) self::getUInt2d($recordData, 0);
+
+            $this->phpSheet->getPageSetup()->setVerticalCentered($isVerticalCentered);
+        }
+    }
+
+    /**
+     * Read LEFTMARGIN record.
+     */
+    private function readLeftMargin(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if (!$this->readDataOnly) {
+            // offset: 0; size: 8
+            $this->phpSheet->getPageMargins()->setLeft(self::extractNumber($recordData));
+        }
+    }
+
+    /**
+     * Read RIGHTMARGIN record.
+     */
+    private function readRightMargin(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if (!$this->readDataOnly) {
+            // offset: 0; size: 8
+            $this->phpSheet->getPageMargins()->setRight(self::extractNumber($recordData));
+        }
+    }
+
+    /**
+     * Read TOPMARGIN record.
+     */
+    private function readTopMargin(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if (!$this->readDataOnly) {
+            // offset: 0; size: 8
+            $this->phpSheet->getPageMargins()->setTop(self::extractNumber($recordData));
+        }
+    }
+
+    /**
+     * Read BOTTOMMARGIN record.
+     */
+    private function readBottomMargin(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if (!$this->readDataOnly) {
+            // offset: 0; size: 8
+            $this->phpSheet->getPageMargins()->setBottom(self::extractNumber($recordData));
+        }
+    }
+
+    /**
+     * Read PAGESETUP record.
+     */
+    private function readPageSetup(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if (!$this->readDataOnly) {
+            // offset: 0; size: 2; paper size
+            $paperSize = self::getUInt2d($recordData, 0);
+
+            // offset: 2; size: 2; scaling factor
+            $scale = self::getUInt2d($recordData, 2);
+
+            // offset: 6; size: 2; fit worksheet width to this number of pages, 0 = use as many as needed
+            $fitToWidth = self::getUInt2d($recordData, 6);
+
+            // offset: 8; size: 2; fit worksheet height to this number of pages, 0 = use as many as needed
+            $fitToHeight = self::getUInt2d($recordData, 8);
+
+            // offset: 10; size: 2; option flags
+
+            // bit: 1; mask: 0x0002; 0=landscape, 1=portrait
+            $isPortrait = (0x0002 & self::getUInt2d($recordData, 10)) >> 1;
+
+            // bit: 2; mask: 0x0004; 1= paper size, scaling factor, paper orient. not init
+            // when this bit is set, do not use flags for those properties
+            $isNotInit = (0x0004 & self::getUInt2d($recordData, 10)) >> 2;
+
+            if (!$isNotInit) {
+                $this->phpSheet->getPageSetup()->setPaperSize($paperSize);
+                switch ($isPortrait) {
+                    case 0:
+                        $this->phpSheet->getPageSetup()->setOrientation(PageSetup::ORIENTATION_LANDSCAPE);
+
+                        break;
+                    case 1:
+                        $this->phpSheet->getPageSetup()->setOrientation(PageSetup::ORIENTATION_PORTRAIT);
+
+                        break;
+                }
+
+                $this->phpSheet->getPageSetup()->setScale($scale, false);
+                $this->phpSheet->getPageSetup()->setFitToPage((bool) $this->isFitToPages);
+                $this->phpSheet->getPageSetup()->setFitToWidth($fitToWidth, false);
+                $this->phpSheet->getPageSetup()->setFitToHeight($fitToHeight, false);
+            }
+
+            // offset: 16; size: 8; header margin (IEEE 754 floating-point value)
+            $marginHeader = self::extractNumber(substr($recordData, 16, 8));
+            $this->phpSheet->getPageMargins()->setHeader($marginHeader);
+
+            // offset: 24; size: 8; footer margin (IEEE 754 floating-point value)
+            $marginFooter = self::extractNumber(substr($recordData, 24, 8));
+            $this->phpSheet->getPageMargins()->setFooter($marginFooter);
+        }
+    }
+
+    /**
+     * PROTECT - Sheet protection (BIFF2 through BIFF8)
+     *   if this record is omitted, then it also means no sheet protection.
+     */
+    private function readProtect(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if ($this->readDataOnly) {
+            return;
+        }
+
+        // offset: 0; size: 2;
+
+        // bit 0, mask 0x01; 1 = sheet is protected
+        $bool = (0x01 & self::getUInt2d($recordData, 0)) >> 0;
+        $this->phpSheet->getProtection()->setSheet((bool) $bool);
+    }
+
+    /**
+     * SCENPROTECT.
+     */
+    private function readScenProtect(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if ($this->readDataOnly) {
+            return;
+        }
+
+        // offset: 0; size: 2;
+
+        // bit: 0, mask 0x01; 1 = scenarios are protected
+        $bool = (0x01 & self::getUInt2d($recordData, 0)) >> 0;
+
+        $this->phpSheet->getProtection()->setScenarios((bool) $bool);
+    }
+
+    /**
+     * OBJECTPROTECT.
+     */
+    private function readObjectProtect(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if ($this->readDataOnly) {
+            return;
+        }
+
+        // offset: 0; size: 2;
+
+        // bit: 0, mask 0x01; 1 = objects are protected
+        $bool = (0x01 & self::getUInt2d($recordData, 0)) >> 0;
+
+        $this->phpSheet->getProtection()->setObjects((bool) $bool);
+    }
+
+    /**
+     * PASSWORD - Sheet protection (hashed) password (BIFF2 through BIFF8).
+     */
+    private function readPassword(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if (!$this->readDataOnly) {
+            // offset: 0; size: 2; 16-bit hash value of password
+            $password = strtoupper(dechex(self::getUInt2d($recordData, 0))); // the hashed password
+            $this->phpSheet->getProtection()->setPassword($password, true);
+        }
+    }
+
+    /**
+     * Read DEFCOLWIDTH record.
+     */
+    private function readDefColWidth(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        // offset: 0; size: 2; default column width
+        $width = self::getUInt2d($recordData, 0);
+        if ($width != 8) {
+            $this->phpSheet->getDefaultColumnDimension()->setWidth($width);
+        }
+    }
+
+    /**
+     * Read COLINFO record.
+     */
+    private function readColInfo(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if (!$this->readDataOnly) {
+            // offset: 0; size: 2; index to first column in range
+            $firstColumnIndex = self::getUInt2d($recordData, 0);
+
+            // offset: 2; size: 2; index to last column in range
+            $lastColumnIndex = self::getUInt2d($recordData, 2);
+
+            // offset: 4; size: 2; width of the column in 1/256 of the width of the zero character
+            $width = self::getUInt2d($recordData, 4);
+
+            // offset: 6; size: 2; index to XF record for default column formatting
+            $xfIndex = self::getUInt2d($recordData, 6);
+
+            // offset: 8; size: 2; option flags
+            // bit: 0; mask: 0x0001; 1= columns are hidden
+            $isHidden = (0x0001 & self::getUInt2d($recordData, 8)) >> 0;
+
+            // bit: 10-8; mask: 0x0700; outline level of the columns (0 = no outline)
+            $level = (0x0700 & self::getUInt2d($recordData, 8)) >> 8;
+
+            // bit: 12; mask: 0x1000; 1 = collapsed
+            $isCollapsed = (0x1000 & self::getUInt2d($recordData, 8)) >> 12;
+
+            // offset: 10; size: 2; not used
+
+            for ($i = $firstColumnIndex + 1; $i <= $lastColumnIndex + 1; ++$i) {
+                if ($lastColumnIndex == 255 || $lastColumnIndex == 256) {
+                    $this->phpSheet->getDefaultColumnDimension()->setWidth($width / 256);
+
+                    break;
+                }
+                $this->phpSheet->getColumnDimensionByColumn($i)->setWidth($width / 256);
+                $this->phpSheet->getColumnDimensionByColumn($i)->setVisible(!$isHidden);
+                $this->phpSheet->getColumnDimensionByColumn($i)->setOutlineLevel($level);
+                $this->phpSheet->getColumnDimensionByColumn($i)->setCollapsed($isCollapsed);
+                $this->phpSheet->getColumnDimensionByColumn($i)->setXfIndex($this->mapCellXfIndex[$xfIndex]);
+            }
+        }
+    }
+
+    /**
+     * ROW.
+     *
+     * This record contains the properties of a single row in a
+     * sheet. Rows and cells in a sheet are divided into blocks
+     * of 32 rows.
+     *
+     * --    "OpenOffice.org's Documentation of the Microsoft
+     *         Excel File Format"
+     */
+    private function readRow(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if (!$this->readDataOnly) {
+            // offset: 0; size: 2; index of this row
+            $r = self::getUInt2d($recordData, 0);
+
+            // offset: 2; size: 2; index to column of the first cell which is described by a cell record
+
+            // offset: 4; size: 2; index to column of the last cell which is described by a cell record, increased by 1
+
+            // offset: 6; size: 2;
+
+            // bit: 14-0; mask: 0x7FFF; height of the row, in twips = 1/20 of a point
+            $height = (0x7FFF & self::getUInt2d($recordData, 6)) >> 0;
+
+            // bit: 15: mask: 0x8000; 0 = row has custom height; 1= row has default height
+            $useDefaultHeight = (0x8000 & self::getUInt2d($recordData, 6)) >> 15;
+
+            if (!$useDefaultHeight) {
+                $this->phpSheet->getRowDimension($r + 1)->setRowHeight($height / 20);
+            }
+
+            // offset: 8; size: 2; not used
+
+            // offset: 10; size: 2; not used in BIFF5-BIFF8
+
+            // offset: 12; size: 4; option flags and default row formatting
+
+            // bit: 2-0: mask: 0x00000007; outline level of the row
+            $level = (0x00000007 & self::getInt4d($recordData, 12)) >> 0;
+            $this->phpSheet->getRowDimension($r + 1)->setOutlineLevel($level);
+
+            // bit: 4; mask: 0x00000010; 1 = outline group start or ends here... and is collapsed
+            $isCollapsed = (0x00000010 & self::getInt4d($recordData, 12)) >> 4;
+            $this->phpSheet->getRowDimension($r + 1)->setCollapsed($isCollapsed);
+
+            // bit: 5; mask: 0x00000020; 1 = row is hidden
+            $isHidden = (0x00000020 & self::getInt4d($recordData, 12)) >> 5;
+            $this->phpSheet->getRowDimension($r + 1)->setVisible(!$isHidden);
+
+            // bit: 7; mask: 0x00000080; 1 = row has explicit format
+            $hasExplicitFormat = (0x00000080 & self::getInt4d($recordData, 12)) >> 7;
+
+            // bit: 27-16; mask: 0x0FFF0000; only applies when hasExplicitFormat = 1; index to XF record
+            $xfIndex = (0x0FFF0000 & self::getInt4d($recordData, 12)) >> 16;
+
+            if ($hasExplicitFormat && isset($this->mapCellXfIndex[$xfIndex])) {
+                $this->phpSheet->getRowDimension($r + 1)->setXfIndex($this->mapCellXfIndex[$xfIndex]);
+            }
+        }
+    }
+
+    /**
+     * Read RK record
+     * This record represents a cell that contains an RK value
+     * (encoded integer or floating-point value). If a
+     * floating-point value cannot be encoded to an RK value,
+     * a NUMBER record will be written. This record replaces the
+     * record INTEGER written in BIFF2.
+     *
+     * --    "OpenOffice.org's Documentation of the Microsoft
+     *         Excel File Format"
+     */
+    private function readRk(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        // offset: 0; size: 2; index to row
+        $row = self::getUInt2d($recordData, 0);
+
+        // offset: 2; size: 2; index to column
+        $column = self::getUInt2d($recordData, 2);
+        $columnString = Coordinate::stringFromColumnIndex($column + 1);
+
+        // Read cell?
+        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
+            // offset: 4; size: 2; index to XF record
+            $xfIndex = self::getUInt2d($recordData, 4);
+
+            // offset: 6; size: 4; RK value
+            $rknum = self::getInt4d($recordData, 6);
+            $numValue = self::getIEEE754($rknum);
+
+            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
+            if (!$this->readDataOnly) {
+                // add style information
+                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
+            }
+
+            // add cell
+            $cell->setValueExplicit($numValue, DataType::TYPE_NUMERIC);
+        }
+    }
+
+    /**
+     * Read LABELSST record
+     * This record represents a cell that contains a string. It
+     * replaces the LABEL record and RSTRING record used in
+     * BIFF2-BIFF5.
+     *
+     * --    "OpenOffice.org's Documentation of the Microsoft
+     *         Excel File Format"
+     */
+    private function readLabelSst(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        // offset: 0; size: 2; index to row
+        $row = self::getUInt2d($recordData, 0);
+
+        // offset: 2; size: 2; index to column
+        $column = self::getUInt2d($recordData, 2);
+        $columnString = Coordinate::stringFromColumnIndex($column + 1);
+
+        $emptyCell = true;
+        // Read cell?
+        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
+            // offset: 4; size: 2; index to XF record
+            $xfIndex = self::getUInt2d($recordData, 4);
+
+            // offset: 6; size: 4; index to SST record
+            $index = self::getInt4d($recordData, 6);
+
+            // add cell
+            if (($fmtRuns = $this->sst[$index]['fmtRuns']) && !$this->readDataOnly) {
+                // then we should treat as rich text
+                $richText = new RichText();
+                $charPos = 0;
+                $sstCount = count($this->sst[$index]['fmtRuns']);
+                for ($i = 0; $i <= $sstCount; ++$i) {
+                    if (isset($fmtRuns[$i])) {
+                        $text = StringHelper::substring($this->sst[$index]['value'], $charPos, $fmtRuns[$i]['charPos'] - $charPos);
+                        $charPos = $fmtRuns[$i]['charPos'];
+                    } else {
+                        $text = StringHelper::substring($this->sst[$index]['value'], $charPos, StringHelper::countCharacters($this->sst[$index]['value']));
+                    }
+
+                    if (StringHelper::countCharacters($text) > 0) {
+                        if ($i == 0) { // first text run, no style
+                            $richText->createText($text);
+                        } else {
+                            $textRun = $richText->createTextRun($text);
+                            if (isset($fmtRuns[$i - 1])) {
+                                if ($fmtRuns[$i - 1]['fontIndex'] < 4) {
+                                    $fontIndex = $fmtRuns[$i - 1]['fontIndex'];
+                                } else {
+                                    // this has to do with that index 4 is omitted in all BIFF versions for some strange reason
+                                    // check the OpenOffice documentation of the FONT record
+                                    $fontIndex = $fmtRuns[$i - 1]['fontIndex'] - 1;
+                                }
+                                $textRun->setFont(clone $this->objFonts[$fontIndex]);
+                            }
+                        }
+                    }
+                }
+                if ($this->readEmptyCells || trim($richText->getPlainText()) !== '') {
+                    $cell = $this->phpSheet->getCell($columnString . ($row + 1));
+                    $cell->setValueExplicit($richText, DataType::TYPE_STRING);
+                    $emptyCell = false;
+                }
+            } else {
+                if ($this->readEmptyCells || trim($this->sst[$index]['value']) !== '') {
+                    $cell = $this->phpSheet->getCell($columnString . ($row + 1));
+                    $cell->setValueExplicit($this->sst[$index]['value'], DataType::TYPE_STRING);
+                    $emptyCell = false;
+                }
+            }
+
+            if (!$this->readDataOnly && !$emptyCell && isset($this->mapCellXfIndex[$xfIndex])) {
+                // add style information
+                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
+            }
+        }
+    }
+
+    /**
+     * Read MULRK record
+     * This record represents a cell range containing RK value
+     * cells. All cells are located in the same row.
+     *
+     * --    "OpenOffice.org's Documentation of the Microsoft
+     *         Excel File Format"
+     */
+    private function readMulRk(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        // offset: 0; size: 2; index to row
+        $row = self::getUInt2d($recordData, 0);
+
+        // offset: 2; size: 2; index to first column
+        $colFirst = self::getUInt2d($recordData, 2);
+
+        // offset: var; size: 2; index to last column
+        $colLast = self::getUInt2d($recordData, $length - 2);
+        $columns = $colLast - $colFirst + 1;
+
+        // offset within record data
+        $offset = 4;
+
+        for ($i = 1; $i <= $columns; ++$i) {
+            $columnString = Coordinate::stringFromColumnIndex($colFirst + $i);
+
+            // Read cell?
+            if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
+                // offset: var; size: 2; index to XF record
+                $xfIndex = self::getUInt2d($recordData, $offset);
+
+                // offset: var; size: 4; RK value
+                $numValue = self::getIEEE754(self::getInt4d($recordData, $offset + 2));
+                $cell = $this->phpSheet->getCell($columnString . ($row + 1));
+                if (!$this->readDataOnly) {
+                    // add style
+                    $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
+                }
+
+                // add cell value
+                $cell->setValueExplicit($numValue, DataType::TYPE_NUMERIC);
+            }
+
+            $offset += 6;
+        }
+    }
+
+    /**
+     * Read NUMBER record
+     * This record represents a cell that contains a
+     * floating-point value.
+     *
+     * --    "OpenOffice.org's Documentation of the Microsoft
+     *         Excel File Format"
+     */
+    private function readNumber(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        // offset: 0; size: 2; index to row
+        $row = self::getUInt2d($recordData, 0);
+
+        // offset: 2; size 2; index to column
+        $column = self::getUInt2d($recordData, 2);
+        $columnString = Coordinate::stringFromColumnIndex($column + 1);
+
+        // Read cell?
+        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
+            // offset 4; size: 2; index to XF record
+            $xfIndex = self::getUInt2d($recordData, 4);
+
+            $numValue = self::extractNumber(substr($recordData, 6, 8));
+
+            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
+            if (!$this->readDataOnly) {
+                // add cell style
+                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
+            }
+
+            // add cell value
+            $cell->setValueExplicit($numValue, DataType::TYPE_NUMERIC);
+        }
+    }
+
+    /**
+     * Read FORMULA record + perhaps a following STRING record if formula result is a string
+     * This record contains the token array and the result of a
+     * formula cell.
+     *
+     * --    "OpenOffice.org's Documentation of the Microsoft
+     *         Excel File Format"
+     */
+    private function readFormula(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        // offset: 0; size: 2; row index
+        $row = self::getUInt2d($recordData, 0);
+
+        // offset: 2; size: 2; col index
+        $column = self::getUInt2d($recordData, 2);
+        $columnString = Coordinate::stringFromColumnIndex($column + 1);
+
+        // offset: 20: size: variable; formula structure
+        $formulaStructure = substr($recordData, 20);
+
+        // offset: 14: size: 2; option flags, recalculate always, recalculate on open etc.
+        $options = self::getUInt2d($recordData, 14);
+
+        // bit: 0; mask: 0x0001; 1 = recalculate always
+        // bit: 1; mask: 0x0002; 1 = calculate on open
+        // bit: 2; mask: 0x0008; 1 = part of a shared formula
+        $isPartOfSharedFormula = (bool) (0x0008 & $options);
+
+        // WARNING:
+        // We can apparently not rely on $isPartOfSharedFormula. Even when $isPartOfSharedFormula = true
+        // the formula data may be ordinary formula data, therefore we need to check
+        // explicitly for the tExp token (0x01)
+        $isPartOfSharedFormula = $isPartOfSharedFormula && ord($formulaStructure[2]) == 0x01;
+
+        if ($isPartOfSharedFormula) {
+            // part of shared formula which means there will be a formula with a tExp token and nothing else
+            // get the base cell, grab tExp token
+            $baseRow = self::getUInt2d($formulaStructure, 3);
+            $baseCol = self::getUInt2d($formulaStructure, 5);
+            $this->baseCell = Coordinate::stringFromColumnIndex($baseCol + 1) . ($baseRow + 1);
+        }
+
+        // Read cell?
+        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
+            if ($isPartOfSharedFormula) {
+                // formula is added to this cell after the sheet has been read
+                $this->sharedFormulaParts[$columnString . ($row + 1)] = $this->baseCell;
+            }
+
+            // offset: 16: size: 4; not used
+
+            // offset: 4; size: 2; XF index
+            $xfIndex = self::getUInt2d($recordData, 4);
+
+            // offset: 6; size: 8; result of the formula
+            if ((ord($recordData[6]) == 0) && (ord($recordData[12]) == 255) && (ord($recordData[13]) == 255)) {
+                // String formula. Result follows in appended STRING record
+                $dataType = DataType::TYPE_STRING;
+
+                // read possible SHAREDFMLA record
+                $code = self::getUInt2d($this->data, $this->pos);
+                if ($code == self::XLS_TYPE_SHAREDFMLA) {
+                    $this->readSharedFmla();
+                }
+
+                // read STRING record
+                $value = $this->readString();
+            } elseif ((ord($recordData[6]) == 1)
+                && (ord($recordData[12]) == 255)
+                && (ord($recordData[13]) == 255)) {
+                // Boolean formula. Result is in +2; 0=false, 1=true
+                $dataType = DataType::TYPE_BOOL;
+                $value = (bool) ord($recordData[8]);
+            } elseif ((ord($recordData[6]) == 2)
+                && (ord($recordData[12]) == 255)
+                && (ord($recordData[13]) == 255)) {
+                // Error formula. Error code is in +2
+                $dataType = DataType::TYPE_ERROR;
+                $value = Xls\ErrorCode::lookup(ord($recordData[8]));
+            } elseif ((ord($recordData[6]) == 3)
+                && (ord($recordData[12]) == 255)
+                && (ord($recordData[13]) == 255)) {
+                // Formula result is a null string
+                $dataType = DataType::TYPE_NULL;
+                $value = '';
+            } else {
+                // forumla result is a number, first 14 bytes like _NUMBER record
+                $dataType = DataType::TYPE_NUMERIC;
+                $value = self::extractNumber(substr($recordData, 6, 8));
+            }
+
+            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
+            if (!$this->readDataOnly) {
+                // add cell style
+                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
+            }
+
+            // store the formula
+            if (!$isPartOfSharedFormula) {
+                // not part of shared formula
+                // add cell value. If we can read formula, populate with formula, otherwise just used cached value
+                try {
+                    if ($this->version != self::XLS_BIFF8) {
+                        throw new Exception('Not BIFF8. Can only read BIFF8 formulas');
+                    }
+                    $formula = $this->getFormulaFromStructure($formulaStructure); // get formula in human language
+                    $cell->setValueExplicit('=' . $formula, DataType::TYPE_FORMULA);
+                } catch (PhpSpreadsheetException $e) {
+                    $cell->setValueExplicit($value, $dataType);
+                }
+            } else {
+                if ($this->version == self::XLS_BIFF8) {
+                    // do nothing at this point, formula id added later in the code
+                } else {
+                    $cell->setValueExplicit($value, $dataType);
+                }
+            }
+
+            // store the cached calculated value
+            $cell->setCalculatedValue($value);
+        }
+    }
+
+    /**
+     * Read a SHAREDFMLA record. This function just stores the binary shared formula in the reader,
+     * which usually contains relative references.
+     * These will be used to construct the formula in each shared formula part after the sheet is read.
+     */
+    private function readSharedFmla(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        // offset: 0, size: 6; cell range address of the area used by the shared formula, not used for anything
+        $cellRange = substr($recordData, 0, 6);
+        $cellRange = $this->readBIFF5CellRangeAddressFixed($cellRange); // note: even BIFF8 uses BIFF5 syntax
+
+        // offset: 6, size: 1; not used
+
+        // offset: 7, size: 1; number of existing FORMULA records for this shared formula
+        $no = ord($recordData[7]);
+
+        // offset: 8, size: var; Binary token array of the shared formula
+        $formula = substr($recordData, 8);
+
+        // at this point we only store the shared formula for later use
+        $this->sharedFormulas[$this->baseCell] = $formula;
+    }
+
+    /**
+     * Read a STRING record from current stream position and advance the stream pointer to next record
+     * This record is used for storing result from FORMULA record when it is a string, and
+     * it occurs directly after the FORMULA record.
+     *
+     * @return string The string contents as UTF-8
+     */
+    private function readString()
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if ($this->version == self::XLS_BIFF8) {
+            $string = self::readUnicodeStringLong($recordData);
+            $value = $string['value'];
+        } else {
+            $string = $this->readByteStringLong($recordData);
+            $value = $string['value'];
+        }
+
+        return $value;
+    }
+
+    /**
+     * Read BOOLERR record
+     * This record represents a Boolean value or error value
+     * cell.
+     *
+     * --    "OpenOffice.org's Documentation of the Microsoft
+     *         Excel File Format"
+     */
+    private function readBoolErr(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        // offset: 0; size: 2; row index
+        $row = self::getUInt2d($recordData, 0);
+
+        // offset: 2; size: 2; column index
+        $column = self::getUInt2d($recordData, 2);
+        $columnString = Coordinate::stringFromColumnIndex($column + 1);
+
+        // Read cell?
+        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
+            // offset: 4; size: 2; index to XF record
+            $xfIndex = self::getUInt2d($recordData, 4);
+
+            // offset: 6; size: 1; the boolean value or error value
+            $boolErr = ord($recordData[6]);
+
+            // offset: 7; size: 1; 0=boolean; 1=error
+            $isError = ord($recordData[7]);
+
+            $cell = $this->phpSheet->getCell($columnString . ($row + 1));
+            switch ($isError) {
+                case 0: // boolean
+                    $value = (bool) $boolErr;
+
+                    // add cell value
+                    $cell->setValueExplicit($value, DataType::TYPE_BOOL);
+
+                    break;
+                case 1: // error type
+                    $value = Xls\ErrorCode::lookup($boolErr);
+
+                    // add cell value
+                    $cell->setValueExplicit($value, DataType::TYPE_ERROR);
+
+                    break;
+            }
+
+            if (!$this->readDataOnly) {
+                // add cell style
+                $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
+            }
+        }
+    }
+
+    /**
+     * Read MULBLANK record
+     * This record represents a cell range of empty cells. All
+     * cells are located in the same row.
+     *
+     * --    "OpenOffice.org's Documentation of the Microsoft
+     *         Excel File Format"
+     */
+    private function readMulBlank(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        // offset: 0; size: 2; index to row
+        $row = self::getUInt2d($recordData, 0);
+
+        // offset: 2; size: 2; index to first column
+        $fc = self::getUInt2d($recordData, 2);
+
+        // offset: 4; size: 2 x nc; list of indexes to XF records
+        // add style information
+        if (!$this->readDataOnly && $this->readEmptyCells) {
+            for ($i = 0; $i < $length / 2 - 3; ++$i) {
+                $columnString = Coordinate::stringFromColumnIndex($fc + $i + 1);
+
+                // Read cell?
+                if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
+                    $xfIndex = self::getUInt2d($recordData, 4 + 2 * $i);
+                    $this->phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->mapCellXfIndex[$xfIndex]);
+                }
+            }
+        }
+
+        // offset: 6; size 2; index to last column (not needed)
+    }
+
+    /**
+     * Read LABEL record
+     * This record represents a cell that contains a string. In
+     * BIFF8 it is usually replaced by the LABELSST record.
+     * Excel still uses this record, if it copies unformatted
+     * text cells to the clipboard.
+     *
+     * --    "OpenOffice.org's Documentation of the Microsoft
+     *         Excel File Format"
+     */
+    private function readLabel(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        // offset: 0; size: 2; index to row
+        $row = self::getUInt2d($recordData, 0);
+
+        // offset: 2; size: 2; index to column
+        $column = self::getUInt2d($recordData, 2);
+        $columnString = Coordinate::stringFromColumnIndex($column + 1);
+
+        // Read cell?
+        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
+            // offset: 4; size: 2; XF index
+            $xfIndex = self::getUInt2d($recordData, 4);
+
+            // add cell value
+            // todo: what if string is very long? continue record
+            if ($this->version == self::XLS_BIFF8) {
+                $string = self::readUnicodeStringLong(substr($recordData, 6));
+                $value = $string['value'];
+            } else {
+                $string = $this->readByteStringLong(substr($recordData, 6));
+                $value = $string['value'];
+            }
+            if ($this->readEmptyCells || trim($value) !== '') {
+                $cell = $this->phpSheet->getCell($columnString . ($row + 1));
+                $cell->setValueExplicit($value, DataType::TYPE_STRING);
+
+                if (!$this->readDataOnly) {
+                    // add cell style
+                    $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
+                }
+            }
+        }
+    }
+
+    /**
+     * Read BLANK record.
+     */
+    private function readBlank(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        // offset: 0; size: 2; row index
+        $row = self::getUInt2d($recordData, 0);
+
+        // offset: 2; size: 2; col index
+        $col = self::getUInt2d($recordData, 2);
+        $columnString = Coordinate::stringFromColumnIndex($col + 1);
+
+        // Read cell?
+        if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
+            // offset: 4; size: 2; XF index
+            $xfIndex = self::getUInt2d($recordData, 4);
+
+            // add style information
+            if (!$this->readDataOnly && $this->readEmptyCells) {
+                $this->phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->mapCellXfIndex[$xfIndex]);
+            }
+        }
+    }
+
+    /**
+     * Read MSODRAWING record.
+     */
+    private function readMsoDrawing(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+
+        // get spliced record data
+        $splicedRecordData = $this->getSplicedRecordData();
+        $recordData = $splicedRecordData['recordData'];
+
+        $this->drawingData .= $recordData;
+    }
+
+    /**
+     * Read OBJ record.
+     */
+    private function readObj(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if ($this->readDataOnly || $this->version != self::XLS_BIFF8) {
+            return;
+        }
+
+        // recordData consists of an array of subrecords looking like this:
+        //    ft: 2 bytes; ftCmo type (0x15)
+        //    cb: 2 bytes; size in bytes of ftCmo data
+        //    ot: 2 bytes; Object Type
+        //    id: 2 bytes; Object id number
+        //    grbit: 2 bytes; Option Flags
+        //    data: var; subrecord data
+
+        // for now, we are just interested in the second subrecord containing the object type
+        $ftCmoType = self::getUInt2d($recordData, 0);
+        $cbCmoSize = self::getUInt2d($recordData, 2);
+        $otObjType = self::getUInt2d($recordData, 4);
+        $idObjID = self::getUInt2d($recordData, 6);
+        $grbitOpts = self::getUInt2d($recordData, 6);
+
+        $this->objs[] = [
+            'ftCmoType' => $ftCmoType,
+            'cbCmoSize' => $cbCmoSize,
+            'otObjType' => $otObjType,
+            'idObjID' => $idObjID,
+            'grbitOpts' => $grbitOpts,
+        ];
+        $this->textObjRef = $idObjID;
+    }
+
+    /**
+     * Read WINDOW2 record.
+     */
+    private function readWindow2(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        // offset: 0; size: 2; option flags
+        $options = self::getUInt2d($recordData, 0);
+
+        // offset: 2; size: 2; index to first visible row
+        $firstVisibleRow = self::getUInt2d($recordData, 2);
+
+        // offset: 4; size: 2; index to first visible colum
+        $firstVisibleColumn = self::getUInt2d($recordData, 4);
+        if ($this->version === self::XLS_BIFF8) {
+            // offset:  8; size: 2; not used
+            // offset: 10; size: 2; cached magnification factor in page break preview (in percent); 0 = Default (60%)
+            // offset: 12; size: 2; cached magnification factor in normal view (in percent); 0 = Default (100%)
+            // offset: 14; size: 4; not used
+            $zoomscaleInPageBreakPreview = self::getUInt2d($recordData, 10);
+            if ($zoomscaleInPageBreakPreview === 0) {
+                $zoomscaleInPageBreakPreview = 60;
+            }
+            $zoomscaleInNormalView = self::getUInt2d($recordData, 12);
+            if ($zoomscaleInNormalView === 0) {
+                $zoomscaleInNormalView = 100;
+            }
+        }
+
+        // bit: 1; mask: 0x0002; 0 = do not show gridlines, 1 = show gridlines
+        $showGridlines = (bool) ((0x0002 & $options) >> 1);
+        $this->phpSheet->setShowGridlines($showGridlines);
+
+        // bit: 2; mask: 0x0004; 0 = do not show headers, 1 = show headers
+        $showRowColHeaders = (bool) ((0x0004 & $options) >> 2);
+        $this->phpSheet->setShowRowColHeaders($showRowColHeaders);
+
+        // bit: 3; mask: 0x0008; 0 = panes are not frozen, 1 = panes are frozen
+        $this->frozen = (bool) ((0x0008 & $options) >> 3);
+
+        // bit: 6; mask: 0x0040; 0 = columns from left to right, 1 = columns from right to left
+        $this->phpSheet->setRightToLeft((bool) ((0x0040 & $options) >> 6));
+
+        // bit: 10; mask: 0x0400; 0 = sheet not active, 1 = sheet active
+        $isActive = (bool) ((0x0400 & $options) >> 10);
+        if ($isActive) {
+            $this->spreadsheet->setActiveSheetIndex($this->spreadsheet->getIndex($this->phpSheet));
+        }
+
+        // bit: 11; mask: 0x0800; 0 = normal view, 1 = page break view
+        $isPageBreakPreview = (bool) ((0x0800 & $options) >> 11);
+
+        //FIXME: set $firstVisibleRow and $firstVisibleColumn
+
+        if ($this->phpSheet->getSheetView()->getView() !== SheetView::SHEETVIEW_PAGE_LAYOUT) {
+            //NOTE: this setting is inferior to page layout view(Excel2007-)
+            $view = $isPageBreakPreview ? SheetView::SHEETVIEW_PAGE_BREAK_PREVIEW : SheetView::SHEETVIEW_NORMAL;
+            $this->phpSheet->getSheetView()->setView($view);
+            if ($this->version === self::XLS_BIFF8) {
+                $zoomScale = $isPageBreakPreview ? $zoomscaleInPageBreakPreview : $zoomscaleInNormalView;
+                $this->phpSheet->getSheetView()->setZoomScale($zoomScale);
+                $this->phpSheet->getSheetView()->setZoomScaleNormal($zoomscaleInNormalView);
+            }
+        }
+    }
+
+    /**
+     * Read PLV Record(Created by Excel2007 or upper).
+     */
+    private function readPageLayoutView(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        // offset: 0; size: 2; rt
+        //->ignore
+        $rt = self::getUInt2d($recordData, 0);
+        // offset: 2; size: 2; grbitfr
+        //->ignore
+        $grbitFrt = self::getUInt2d($recordData, 2);
+        // offset: 4; size: 8; reserved
+        //->ignore
+
+        // offset: 12; size 2; zoom scale
+        $wScalePLV = self::getUInt2d($recordData, 12);
+        // offset: 14; size 2; grbit
+        $grbit = self::getUInt2d($recordData, 14);
+
+        // decomprise grbit
+        $fPageLayoutView = $grbit & 0x01;
+        $fRulerVisible = ($grbit >> 1) & 0x01; //no support
+        $fWhitespaceHidden = ($grbit >> 3) & 0x01; //no support
+
+        if ($fPageLayoutView === 1) {
+            $this->phpSheet->getSheetView()->setView(SheetView::SHEETVIEW_PAGE_LAYOUT);
+            $this->phpSheet->getSheetView()->setZoomScale($wScalePLV); //set by Excel2007 only if SHEETVIEW_PAGE_LAYOUT
+        }
+        //otherwise, we cannot know whether SHEETVIEW_PAGE_LAYOUT or SHEETVIEW_PAGE_BREAK_PREVIEW.
+    }
+
+    /**
+     * Read SCL record.
+     */
+    private function readScl(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        // offset: 0; size: 2; numerator of the view magnification
+        $numerator = self::getUInt2d($recordData, 0);
+
+        // offset: 2; size: 2; numerator of the view magnification
+        $denumerator = self::getUInt2d($recordData, 2);
+
+        // set the zoom scale (in percent)
+        $this->phpSheet->getSheetView()->setZoomScale($numerator * 100 / $denumerator);
+    }
+
+    /**
+     * Read PANE record.
+     */
+    private function readPane(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if (!$this->readDataOnly) {
+            // offset: 0; size: 2; position of vertical split
+            $px = self::getUInt2d($recordData, 0);
+
+            // offset: 2; size: 2; position of horizontal split
+            $py = self::getUInt2d($recordData, 2);
+
+            // offset: 4; size: 2; top most visible row in the bottom pane
+            $rwTop = self::getUInt2d($recordData, 4);
+
+            // offset: 6; size: 2; first visible left column in the right pane
+            $colLeft = self::getUInt2d($recordData, 6);
+
+            if ($this->frozen) {
+                // frozen panes
+                $cell = Coordinate::stringFromColumnIndex($px + 1) . ($py + 1);
+                $topLeftCell = Coordinate::stringFromColumnIndex($colLeft + 1) . ($rwTop + 1);
+                $this->phpSheet->freezePane($cell, $topLeftCell);
+            }
+            // unfrozen panes; split windows; not supported by PhpSpreadsheet core
+        }
+    }
+
+    /**
+     * Read SELECTION record. There is one such record for each pane in the sheet.
+     */
+    private function readSelection(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if (!$this->readDataOnly) {
+            // offset: 0; size: 1; pane identifier
+            $paneId = ord($recordData[0]);
+
+            // offset: 1; size: 2; index to row of the active cell
+            $r = self::getUInt2d($recordData, 1);
+
+            // offset: 3; size: 2; index to column of the active cell
+            $c = self::getUInt2d($recordData, 3);
+
+            // offset: 5; size: 2; index into the following cell range list to the
+            //  entry that contains the active cell
+            $index = self::getUInt2d($recordData, 5);
+
+            // offset: 7; size: var; cell range address list containing all selected cell ranges
+            $data = substr($recordData, 7);
+            $cellRangeAddressList = $this->readBIFF5CellRangeAddressList($data); // note: also BIFF8 uses BIFF5 syntax
+
+            $selectedCells = $cellRangeAddressList['cellRangeAddresses'][0];
+
+            // first row '1' + last row '16384' indicates that full column is selected (apparently also in BIFF8!)
+            if (preg_match('/^([A-Z]+1\:[A-Z]+)16384$/', $selectedCells)) {
+                $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)16384$/', '${1}1048576', $selectedCells);
+            }
+
+            // first row '1' + last row '65536' indicates that full column is selected
+            if (preg_match('/^([A-Z]+1\:[A-Z]+)65536$/', $selectedCells)) {
+                $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)65536$/', '${1}1048576', $selectedCells);
+            }
+
+            // first column 'A' + last column 'IV' indicates that full row is selected
+            if (preg_match('/^(A\d+\:)IV(\d+)$/', $selectedCells)) {
+                $selectedCells = preg_replace('/^(A\d+\:)IV(\d+)$/', '${1}XFD${2}', $selectedCells);
+            }
+
+            $this->phpSheet->setSelectedCells($selectedCells);
+        }
+    }
+
+    private function includeCellRangeFiltered($cellRangeAddress)
+    {
+        $includeCellRange = true;
+        if ($this->getReadFilter() !== null) {
+            $includeCellRange = false;
+            $rangeBoundaries = Coordinate::getRangeBoundaries($cellRangeAddress);
+            ++$rangeBoundaries[1][0];
+            for ($row = $rangeBoundaries[0][1]; $row <= $rangeBoundaries[1][1]; ++$row) {
+                for ($column = $rangeBoundaries[0][0]; $column != $rangeBoundaries[1][0]; ++$column) {
+                    if ($this->getReadFilter()->readCell($column, $row, $this->phpSheet->getTitle())) {
+                        $includeCellRange = true;
+
+                        break 2;
+                    }
+                }
+            }
+        }
+
+        return $includeCellRange;
+    }
+
+    /**
+     * MERGEDCELLS.
+     *
+     * This record contains the addresses of merged cell ranges
+     * in the current sheet.
+     *
+     * --    "OpenOffice.org's Documentation of the Microsoft
+     *         Excel File Format"
+     */
+    private function readMergedCells(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
+            $cellRangeAddressList = $this->readBIFF8CellRangeAddressList($recordData);
+            foreach ($cellRangeAddressList['cellRangeAddresses'] as $cellRangeAddress) {
+                if ((strpos($cellRangeAddress, ':') !== false) &&
+                    ($this->includeCellRangeFiltered($cellRangeAddress))) {
+                    $this->phpSheet->mergeCells($cellRangeAddress);
+                }
+            }
+        }
+    }
+
+    /**
+     * Read HYPERLINK record.
+     */
+    private function readHyperLink(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer forward to next record
+        $this->pos += 4 + $length;
+
+        if (!$this->readDataOnly) {
+            // offset: 0; size: 8; cell range address of all cells containing this hyperlink
+            try {
+                $cellRange = $this->readBIFF8CellRangeAddressFixed($recordData);
+            } catch (PhpSpreadsheetException $e) {
+                return;
+            }
+
+            // offset: 8, size: 16; GUID of StdLink
+
+            // offset: 24, size: 4; unknown value
+
+            // offset: 28, size: 4; option flags
+            // bit: 0; mask: 0x00000001; 0 = no link or extant, 1 = file link or URL
+            $isFileLinkOrUrl = (0x00000001 & self::getUInt2d($recordData, 28)) >> 0;
+
+            // bit: 1; mask: 0x00000002; 0 = relative path, 1 = absolute path or URL
+            $isAbsPathOrUrl = (0x00000001 & self::getUInt2d($recordData, 28)) >> 1;
+
+            // bit: 2 (and 4); mask: 0x00000014; 0 = no description
+            $hasDesc = (0x00000014 & self::getUInt2d($recordData, 28)) >> 2;
+
+            // bit: 3; mask: 0x00000008; 0 = no text, 1 = has text
+            $hasText = (0x00000008 & self::getUInt2d($recordData, 28)) >> 3;
+
+            // bit: 7; mask: 0x00000080; 0 = no target frame, 1 = has target frame
+            $hasFrame = (0x00000080 & self::getUInt2d($recordData, 28)) >> 7;
+
+            // bit: 8; mask: 0x00000100; 0 = file link or URL, 1 = UNC path (inc. server name)
+            $isUNC = (0x00000100 & self::getUInt2d($recordData, 28)) >> 8;
+
+            // offset within record data
+            $offset = 32;
+
+            if ($hasDesc) {
+                // offset: 32; size: var; character count of description text
+                $dl = self::getInt4d($recordData, 32);
+                // offset: 36; size: var; character array of description text, no Unicode string header, always 16-bit characters, zero terminated
+                $desc = self::encodeUTF16(substr($recordData, 36, 2 * ($dl - 1)), false);
+                $offset += 4 + 2 * $dl;
+            }
+            if ($hasFrame) {
+                $fl = self::getInt4d($recordData, $offset);
+                $offset += 4 + 2 * $fl;
+            }
+
+            // detect type of hyperlink (there are 4 types)
+            $hyperlinkType = null;
+
+            if ($isUNC) {
+                $hyperlinkType = 'UNC';
+            } elseif (!$isFileLinkOrUrl) {
+                $hyperlinkType = 'workbook';
+            } elseif (ord($recordData[$offset]) == 0x03) {
+                $hyperlinkType = 'local';
+            } elseif (ord($recordData[$offset]) == 0xE0) {
+                $hyperlinkType = 'URL';
+            }
+
+            switch ($hyperlinkType) {
+                case 'URL':
+                    // section 5.58.2: Hyperlink containing a URL
+                    // e.g. http://example.org/index.php
+
+                    // offset: var; size: 16; GUID of URL Moniker
+                    $offset += 16;
+                    // offset: var; size: 4; size (in bytes) of character array of the URL including trailing zero word
+                    $us = self::getInt4d($recordData, $offset);
+                    $offset += 4;
+                    // offset: var; size: $us; character array of the URL, no Unicode string header, always 16-bit characters, zero-terminated
+                    $url = self::encodeUTF16(substr($recordData, $offset, $us - 2), false);
+                    $nullOffset = strpos($url, chr(0x00));
+                    if ($nullOffset) {
+                        $url = substr($url, 0, $nullOffset);
+                    }
+                    $url .= $hasText ? '#' : '';
+                    $offset += $us;
+
+                    break;
+                case 'local':
+                    // section 5.58.3: Hyperlink to local file
+                    // examples:
+                    //   mydoc.txt
+                    //   ../../somedoc.xls#Sheet!A1
+
+                    // offset: var; size: 16; GUI of File Moniker
+                    $offset += 16;
+
+                    // offset: var; size: 2; directory up-level count.
+                    $upLevelCount = self::getUInt2d($recordData, $offset);
+                    $offset += 2;
+
+                    // offset: var; size: 4; character count of the shortened file path and name, including trailing zero word
+                    $sl = self::getInt4d($recordData, $offset);
+                    $offset += 4;
+
+                    // offset: var; size: sl; character array of the shortened file path and name in 8.3-DOS-format (compressed Unicode string)
+                    $shortenedFilePath = substr($recordData, $offset, $sl);
+                    $shortenedFilePath = self::encodeUTF16($shortenedFilePath, true);
+                    $shortenedFilePath = substr($shortenedFilePath, 0, -1); // remove trailing zero
+
+                    $offset += $sl;
+
+                    // offset: var; size: 24; unknown sequence
+                    $offset += 24;
+
+                    // extended file path
+                    // offset: var; size: 4; size of the following file link field including string lenth mark
+                    $sz = self::getInt4d($recordData, $offset);
+                    $offset += 4;
+
+                    // only present if $sz > 0
+                    if ($sz > 0) {
+                        // offset: var; size: 4; size of the character array of the extended file path and name
+                        $xl = self::getInt4d($recordData, $offset);
+                        $offset += 4;
+
+                        // offset: var; size 2; unknown
+                        $offset += 2;
+
+                        // offset: var; size $xl; character array of the extended file path and name.
+                        $extendedFilePath = substr($recordData, $offset, $xl);
+                        $extendedFilePath = self::encodeUTF16($extendedFilePath, false);
+                        $offset += $xl;
+                    }
+
+                    // construct the path
+                    $url = str_repeat('..\\', $upLevelCount);
+                    $url .= ($sz > 0) ? $extendedFilePath : $shortenedFilePath; // use extended path if available
+                    $url .= $hasText ? '#' : '';
+
+                    break;
+                case 'UNC':
+                    // section 5.58.4: Hyperlink to a File with UNC (Universal Naming Convention) Path
+                    // todo: implement
+                    return;
+                case 'workbook':
+                    // section 5.58.5: Hyperlink to the Current Workbook
+                    // e.g. Sheet2!B1:C2, stored in text mark field
+                    $url = 'sheet://';
+
+                    break;
+                default:
+                    return;
+            }
+
+            if ($hasText) {
+                // offset: var; size: 4; character count of text mark including trailing zero word
+                $tl = self::getInt4d($recordData, $offset);
+                $offset += 4;
+                // offset: var; size: var; character array of the text mark without the # sign, no Unicode header, always 16-bit characters, zero-terminated
+                $text = self::encodeUTF16(substr($recordData, $offset, 2 * ($tl - 1)), false);
+                $url .= $text;
+            }
+
+            // apply the hyperlink to all the relevant cells
+            foreach (Coordinate::extractAllCellReferencesInRange($cellRange) as $coordinate) {
+                $this->phpSheet->getCell($coordinate)->getHyperLink()->setUrl($url);
+            }
+        }
+    }
+
+    /**
+     * Read DATAVALIDATIONS record.
+     */
+    private function readDataValidations(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer forward to next record
+        $this->pos += 4 + $length;
+    }
+
+    /**
+     * Read DATAVALIDATION record.
+     */
+    private function readDataValidation(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer forward to next record
+        $this->pos += 4 + $length;
+
+        if ($this->readDataOnly) {
+            return;
+        }
+
+        // offset: 0; size: 4; Options
+        $options = self::getInt4d($recordData, 0);
+
+        // bit: 0-3; mask: 0x0000000F; type
+        $type = (0x0000000F & $options) >> 0;
+        switch ($type) {
+            case 0x00:
+                $type = DataValidation::TYPE_NONE;
+
+                break;
+            case 0x01:
+                $type = DataValidation::TYPE_WHOLE;
+
+                break;
+            case 0x02:
+                $type = DataValidation::TYPE_DECIMAL;
+
+                break;
+            case 0x03:
+                $type = DataValidation::TYPE_LIST;
+
+                break;
+            case 0x04:
+                $type = DataValidation::TYPE_DATE;
+
+                break;
+            case 0x05:
+                $type = DataValidation::TYPE_TIME;
+
+                break;
+            case 0x06:
+                $type = DataValidation::TYPE_TEXTLENGTH;
+
+                break;
+            case 0x07:
+                $type = DataValidation::TYPE_CUSTOM;
+
+                break;
+        }
+
+        // bit: 4-6; mask: 0x00000070; error type
+        $errorStyle = (0x00000070 & $options) >> 4;
+        switch ($errorStyle) {
+            case 0x00:
+                $errorStyle = DataValidation::STYLE_STOP;
+
+                break;
+            case 0x01:
+                $errorStyle = DataValidation::STYLE_WARNING;
+
+                break;
+            case 0x02:
+                $errorStyle = DataValidation::STYLE_INFORMATION;
+
+                break;
+        }
+
+        // bit: 7; mask: 0x00000080; 1= formula is explicit (only applies to list)
+        // I have only seen cases where this is 1
+        $explicitFormula = (0x00000080 & $options) >> 7;
+
+        // bit: 8; mask: 0x00000100; 1= empty cells allowed
+        $allowBlank = (0x00000100 & $options) >> 8;
+
+        // bit: 9; mask: 0x00000200; 1= suppress drop down arrow in list type validity
+        $suppressDropDown = (0x00000200 & $options) >> 9;
+
+        // bit: 18; mask: 0x00040000; 1= show prompt box if cell selected
+        $showInputMessage = (0x00040000 & $options) >> 18;
+
+        // bit: 19; mask: 0x00080000; 1= show error box if invalid values entered
+        $showErrorMessage = (0x00080000 & $options) >> 19;
+
+        // bit: 20-23; mask: 0x00F00000; condition operator
+        $operator = (0x00F00000 & $options) >> 20;
+        switch ($operator) {
+            case 0x00:
+                $operator = DataValidation::OPERATOR_BETWEEN;
+
+                break;
+            case 0x01:
+                $operator = DataValidation::OPERATOR_NOTBETWEEN;
+
+                break;
+            case 0x02:
+                $operator = DataValidation::OPERATOR_EQUAL;
+
+                break;
+            case 0x03:
+                $operator = DataValidation::OPERATOR_NOTEQUAL;
+
+                break;
+            case 0x04:
+                $operator = DataValidation::OPERATOR_GREATERTHAN;
+
+                break;
+            case 0x05:
+                $operator = DataValidation::OPERATOR_LESSTHAN;
+
+                break;
+            case 0x06:
+                $operator = DataValidation::OPERATOR_GREATERTHANOREQUAL;
+
+                break;
+            case 0x07:
+                $operator = DataValidation::OPERATOR_LESSTHANOREQUAL;
+
+                break;
+        }
+
+        // offset: 4; size: var; title of the prompt box
+        $offset = 4;
+        $string = self::readUnicodeStringLong(substr($recordData, $offset));
+        $promptTitle = $string['value'] !== chr(0) ? $string['value'] : '';
+        $offset += $string['size'];
+
+        // offset: var; size: var; title of the error box
+        $string = self::readUnicodeStringLong(substr($recordData, $offset));
+        $errorTitle = $string['value'] !== chr(0) ? $string['value'] : '';
+        $offset += $string['size'];
+
+        // offset: var; size: var; text of the prompt box
+        $string = self::readUnicodeStringLong(substr($recordData, $offset));
+        $prompt = $string['value'] !== chr(0) ? $string['value'] : '';
+        $offset += $string['size'];
+
+        // offset: var; size: var; text of the error box
+        $string = self::readUnicodeStringLong(substr($recordData, $offset));
+        $error = $string['value'] !== chr(0) ? $string['value'] : '';
+        $offset += $string['size'];
+
+        // offset: var; size: 2; size of the formula data for the first condition
+        $sz1 = self::getUInt2d($recordData, $offset);
+        $offset += 2;
+
+        // offset: var; size: 2; not used
+        $offset += 2;
+
+        // offset: var; size: $sz1; formula data for first condition (without size field)
+        $formula1 = substr($recordData, $offset, $sz1);
+        $formula1 = pack('v', $sz1) . $formula1; // prepend the length
+        try {
+            $formula1 = $this->getFormulaFromStructure($formula1);
+
+            // in list type validity, null characters are used as item separators
+            if ($type == DataValidation::TYPE_LIST) {
+                $formula1 = str_replace(chr(0), ',', $formula1);
+            }
+        } catch (PhpSpreadsheetException $e) {
+            return;
+        }
+        $offset += $sz1;
+
+        // offset: var; size: 2; size of the formula data for the first condition
+        $sz2 = self::getUInt2d($recordData, $offset);
+        $offset += 2;
+
+        // offset: var; size: 2; not used
+        $offset += 2;
+
+        // offset: var; size: $sz2; formula data for second condition (without size field)
+        $formula2 = substr($recordData, $offset, $sz2);
+        $formula2 = pack('v', $sz2) . $formula2; // prepend the length
+        try {
+            $formula2 = $this->getFormulaFromStructure($formula2);
+        } catch (PhpSpreadsheetException $e) {
+            return;
+        }
+        $offset += $sz2;
+
+        // offset: var; size: var; cell range address list with
+        $cellRangeAddressList = $this->readBIFF8CellRangeAddressList(substr($recordData, $offset));
+        $cellRangeAddresses = $cellRangeAddressList['cellRangeAddresses'];
+
+        foreach ($cellRangeAddresses as $cellRange) {
+            $stRange = $this->phpSheet->shrinkRangeToFit($cellRange);
+            foreach (Coordinate::extractAllCellReferencesInRange($stRange) as $coordinate) {
+                $objValidation = $this->phpSheet->getCell($coordinate)->getDataValidation();
+                $objValidation->setType($type);
+                $objValidation->setErrorStyle($errorStyle);
+                $objValidation->setAllowBlank((bool) $allowBlank);
+                $objValidation->setShowInputMessage((bool) $showInputMessage);
+                $objValidation->setShowErrorMessage((bool) $showErrorMessage);
+                $objValidation->setShowDropDown(!$suppressDropDown);
+                $objValidation->setOperator($operator);
+                $objValidation->setErrorTitle($errorTitle);
+                $objValidation->setError($error);
+                $objValidation->setPromptTitle($promptTitle);
+                $objValidation->setPrompt($prompt);
+                $objValidation->setFormula1($formula1);
+                $objValidation->setFormula2($formula2);
+            }
+        }
+    }
+
+    /**
+     * Read SHEETLAYOUT record. Stores sheet tab color information.
+     */
+    private function readSheetLayout(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        // local pointer in record data
+        $offset = 0;
+
+        if (!$this->readDataOnly) {
+            // offset: 0; size: 2; repeated record identifier 0x0862
+
+            // offset: 2; size: 10; not used
+
+            // offset: 12; size: 4; size of record data
+            // Excel 2003 uses size of 0x14 (documented), Excel 2007 uses size of 0x28 (not documented?)
+            $sz = self::getInt4d($recordData, 12);
+
+            switch ($sz) {
+                case 0x14:
+                    // offset: 16; size: 2; color index for sheet tab
+                    $colorIndex = self::getUInt2d($recordData, 16);
+                    $color = Xls\Color::map($colorIndex, $this->palette, $this->version);
+                    $this->phpSheet->getTabColor()->setRGB($color['rgb']);
+
+                    break;
+                case 0x28:
+                    // TODO: Investigate structure for .xls SHEETLAYOUT record as saved by MS Office Excel 2007
+                    return;
+
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Read SHEETPROTECTION record (FEATHEADR).
+     */
+    private function readSheetProtection(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        if ($this->readDataOnly) {
+            return;
+        }
+
+        // offset: 0; size: 2; repeated record header
+
+        // offset: 2; size: 2; FRT cell reference flag (=0 currently)
+
+        // offset: 4; size: 8; Currently not used and set to 0
+
+        // offset: 12; size: 2; Shared feature type index (2=Enhanced Protetion, 4=SmartTag)
+        $isf = self::getUInt2d($recordData, 12);
+        if ($isf != 2) {
+            return;
+        }
+
+        // offset: 14; size: 1; =1 since this is a feat header
+
+        // offset: 15; size: 4; size of rgbHdrSData
+
+        // rgbHdrSData, assume "Enhanced Protection"
+        // offset: 19; size: 2; option flags
+        $options = self::getUInt2d($recordData, 19);
+
+        // bit: 0; mask 0x0001; 1 = user may edit objects, 0 = users must not edit objects
+        $bool = (0x0001 & $options) >> 0;
+        $this->phpSheet->getProtection()->setObjects(!$bool);
+
+        // bit: 1; mask 0x0002; edit scenarios
+        $bool = (0x0002 & $options) >> 1;
+        $this->phpSheet->getProtection()->setScenarios(!$bool);
+
+        // bit: 2; mask 0x0004; format cells
+        $bool = (0x0004 & $options) >> 2;
+        $this->phpSheet->getProtection()->setFormatCells(!$bool);
+
+        // bit: 3; mask 0x0008; format columns
+        $bool = (0x0008 & $options) >> 3;
+        $this->phpSheet->getProtection()->setFormatColumns(!$bool);
+
+        // bit: 4; mask 0x0010; format rows
+        $bool = (0x0010 & $options) >> 4;
+        $this->phpSheet->getProtection()->setFormatRows(!$bool);
+
+        // bit: 5; mask 0x0020; insert columns
+        $bool = (0x0020 & $options) >> 5;
+        $this->phpSheet->getProtection()->setInsertColumns(!$bool);
+
+        // bit: 6; mask 0x0040; insert rows
+        $bool = (0x0040 & $options) >> 6;
+        $this->phpSheet->getProtection()->setInsertRows(!$bool);
+
+        // bit: 7; mask 0x0080; insert hyperlinks
+        $bool = (0x0080 & $options) >> 7;
+        $this->phpSheet->getProtection()->setInsertHyperlinks(!$bool);
+
+        // bit: 8; mask 0x0100; delete columns
+        $bool = (0x0100 & $options) >> 8;
+        $this->phpSheet->getProtection()->setDeleteColumns(!$bool);
+
+        // bit: 9; mask 0x0200; delete rows
+        $bool = (0x0200 & $options) >> 9;
+        $this->phpSheet->getProtection()->setDeleteRows(!$bool);
+
+        // bit: 10; mask 0x0400; select locked cells
+        $bool = (0x0400 & $options) >> 10;
+        $this->phpSheet->getProtection()->setSelectLockedCells(!$bool);
+
+        // bit: 11; mask 0x0800; sort cell range
+        $bool = (0x0800 & $options) >> 11;
+        $this->phpSheet->getProtection()->setSort(!$bool);
+
+        // bit: 12; mask 0x1000; auto filter
+        $bool = (0x1000 & $options) >> 12;
+        $this->phpSheet->getProtection()->setAutoFilter(!$bool);
+
+        // bit: 13; mask 0x2000; pivot tables
+        $bool = (0x2000 & $options) >> 13;
+        $this->phpSheet->getProtection()->setPivotTables(!$bool);
+
+        // bit: 14; mask 0x4000; select unlocked cells
+        $bool = (0x4000 & $options) >> 14;
+        $this->phpSheet->getProtection()->setSelectUnlockedCells(!$bool);
+
+        // offset: 21; size: 2; not used
+    }
+
+    /**
+     * Read RANGEPROTECTION record
+     * Reading of this record is based on Microsoft Office Excel 97-2000 Binary File Format Specification,
+     * where it is referred to as FEAT record.
+     */
+    private function readRangeProtection(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+
+        // local pointer in record data
+        $offset = 0;
+
+        if (!$this->readDataOnly) {
+            $offset += 12;
+
+            // offset: 12; size: 2; shared feature type, 2 = enhanced protection, 4 = smart tag
+            $isf = self::getUInt2d($recordData, 12);
+            if ($isf != 2) {
+                // we only read FEAT records of type 2
+                return;
+            }
+            $offset += 2;
+
+            $offset += 5;
+
+            // offset: 19; size: 2; count of ref ranges this feature is on
+            $cref = self::getUInt2d($recordData, 19);
+            $offset += 2;
+
+            $offset += 6;
+
+            // offset: 27; size: 8 * $cref; list of cell ranges (like in hyperlink record)
+            $cellRanges = [];
+            for ($i = 0; $i < $cref; ++$i) {
+                try {
+                    $cellRange = $this->readBIFF8CellRangeAddressFixed(substr($recordData, 27 + 8 * $i, 8));
+                } catch (PhpSpreadsheetException $e) {
+                    return;
+                }
+                $cellRanges[] = $cellRange;
+                $offset += 8;
+            }
+
+            // offset: var; size: var; variable length of feature specific data
+            $rgbFeat = substr($recordData, $offset);
+            $offset += 4;
+
+            // offset: var; size: 4; the encrypted password (only 16-bit although field is 32-bit)
+            $wPassword = self::getInt4d($recordData, $offset);
+            $offset += 4;
+
+            // Apply range protection to sheet
+            if ($cellRanges) {
+                $this->phpSheet->protectCells(implode(' ', $cellRanges), strtoupper(dechex($wPassword)), true);
+            }
+        }
+    }
+
+    /**
+     * Read a free CONTINUE record. Free CONTINUE record may be a camouflaged MSODRAWING record
+     * When MSODRAWING data on a sheet exceeds 8224 bytes, CONTINUE records are used instead. Undocumented.
+     * In this case, we must treat the CONTINUE record as a MSODRAWING record.
+     */
+    private function readContinue(): void
+    {
+        $length = self::getUInt2d($this->data, $this->pos + 2);
+        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+        // check if we are reading drawing data
+        // this is in case a free CONTINUE record occurs in other circumstances we are unaware of
+        if ($this->drawingData == '') {
+            // move stream pointer to next record
+            $this->pos += 4 + $length;
+
+            return;
+        }
+
+        // check if record data is at least 4 bytes long, otherwise there is no chance this is MSODRAWING data
+        if ($length < 4) {
+            // move stream pointer to next record
+            $this->pos += 4 + $length;
+
+            return;
+        }
+
+        // dirty check to see if CONTINUE record could be a camouflaged MSODRAWING record
+        // look inside CONTINUE record to see if it looks like a part of an Escher stream
+        // we know that Escher stream may be split at least at
+        //        0xF003 MsofbtSpgrContainer
+        //        0xF004 MsofbtSpContainer
+        //        0xF00D MsofbtClientTextbox
+        $validSplitPoints = [0xF003, 0xF004, 0xF00D]; // add identifiers if we find more
+
+        $splitPoint = self::getUInt2d($recordData, 2);
+        if (in_array($splitPoint, $validSplitPoints)) {
+            // get spliced record data (and move pointer to next record)
+            $splicedRecordData = $this->getSplicedRecordData();
+            $this->drawingData .= $splicedRecordData['recordData'];
+
+            return;
+        }
+
+        // move stream pointer to next record
+        $this->pos += 4 + $length;
+    }
+
+    /**
+     * Reads a record from current position in data stream and continues reading data as long as CONTINUE
+     * records are found. Splices the record data pieces and returns the combined string as if record data
+     * is in one piece.
+     * Moves to next current position in data stream to start of next record different from a CONtINUE record.
+     *
+     * @return array
+     */
+    private function getSplicedRecordData()
+    {
+        $data = '';
+        $spliceOffsets = [];
+
+        $i = 0;
+        $spliceOffsets[0] = 0;
+
+        do {
+            ++$i;
+
+            // offset: 0; size: 2; identifier
+            $identifier = self::getUInt2d($this->data, $this->pos);
+            // offset: 2; size: 2; length
+            $length = self::getUInt2d($this->data, $this->pos + 2);
+            $data .= $this->readRecordData($this->data, $this->pos + 4, $length);
+
+            $spliceOffsets[$i] = $spliceOffsets[$i - 1] + $length;
+
+            $this->pos += 4 + $length;
+            $nextIdentifier = self::getUInt2d($this->data, $this->pos);
+        } while ($nextIdentifier == self::XLS_TYPE_CONTINUE);
+
+        return [
+            'recordData' => $data,
+            'spliceOffsets' => $spliceOffsets,
+        ];
+    }
+
+    /**
+     * Convert formula structure into human readable Excel formula like 'A3+A5*5'.
+     *
+     * @param string $formulaStructure The complete binary data for the formula
+     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
+     *
+     * @return string Human readable formula
+     */
+    private function getFormulaFromStructure($formulaStructure, $baseCell = 'A1')
+    {
+        // offset: 0; size: 2; size of the following formula data
+        $sz = self::getUInt2d($formulaStructure, 0);
+
+        // offset: 2; size: sz
+        $formulaData = substr($formulaStructure, 2, $sz);
+
+        // offset: 2 + sz; size: variable (optional)
+        if (strlen($formulaStructure) > 2 + $sz) {
+            $additionalData = substr($formulaStructure, 2 + $sz);
+        } else {
+            $additionalData = '';
+        }
+
+        return $this->getFormulaFromData($formulaData, $additionalData, $baseCell);
+    }
+
+    /**
+     * Take formula data and additional data for formula and return human readable formula.
+     *
+     * @param string $formulaData The binary data for the formula itself
+     * @param string $additionalData Additional binary data going with the formula
+     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
+     *
+     * @return string Human readable formula
+     */
+    private function getFormulaFromData($formulaData, $additionalData = '', $baseCell = 'A1')
+    {
+        // start parsing the formula data
+        $tokens = [];
+
+        while (strlen($formulaData) > 0 && $token = $this->getNextToken($formulaData, $baseCell)) {
+            $tokens[] = $token;
+            $formulaData = substr($formulaData, $token['size']);
+        }
+
+        $formulaString = $this->createFormulaFromTokens($tokens, $additionalData);
+
+        return $formulaString;
+    }
+
+    /**
+     * Take array of tokens together with additional data for formula and return human readable formula.
+     *
+     * @param array $tokens
+     * @param string $additionalData Additional binary data going with the formula
+     *
+     * @return string Human readable formula
+     */
+    private function createFormulaFromTokens($tokens, $additionalData)
+    {
+        // empty formula?
+        if (empty($tokens)) {
+            return '';
+        }
+
+        $formulaStrings = [];
+        foreach ($tokens as $token) {
+            // initialize spaces
+            $space0 = $space0 ?? ''; // spaces before next token, not tParen
+            $space1 = $space1 ?? ''; // carriage returns before next token, not tParen
+            $space2 = $space2 ?? ''; // spaces before opening parenthesis
+            $space3 = $space3 ?? ''; // carriage returns before opening parenthesis
+            $space4 = $space4 ?? ''; // spaces before closing parenthesis
+            $space5 = $space5 ?? ''; // carriage returns before closing parenthesis
+
+            switch ($token['name']) {
+                case 'tAdd': // addition
+                case 'tConcat': // addition
+                case 'tDiv': // division
+                case 'tEQ': // equality
+                case 'tGE': // greater than or equal
+                case 'tGT': // greater than
+                case 'tIsect': // intersection
+                case 'tLE': // less than or equal
+                case 'tList': // less than or equal
+                case 'tLT': // less than
+                case 'tMul': // multiplication
+                case 'tNE': // multiplication
+                case 'tPower': // power
+                case 'tRange': // range
+                case 'tSub': // subtraction
+                    $op2 = array_pop($formulaStrings);
+                    $op1 = array_pop($formulaStrings);
+                    $formulaStrings[] = "$op1$space1$space0{$token['data']}$op2";
+                    unset($space0, $space1);
+
+                    break;
+                case 'tUplus': // unary plus
+                case 'tUminus': // unary minus
+                    $op = array_pop($formulaStrings);
+                    $formulaStrings[] = "$space1$space0{$token['data']}$op";
+                    unset($space0, $space1);
+
+                    break;
+                case 'tPercent': // percent sign
+                    $op = array_pop($formulaStrings);
+                    $formulaStrings[] = "$op$space1$space0{$token['data']}";
+                    unset($space0, $space1);
+
+                    break;
+                case 'tAttrVolatile': // indicates volatile function
+                case 'tAttrIf':
+                case 'tAttrSkip':
+                case 'tAttrChoose':
+                    // token is only important for Excel formula evaluator
+                    // do nothing
+                    break;
+                case 'tAttrSpace': // space / carriage return
+                    // space will be used when next token arrives, do not alter formulaString stack
+                    switch ($token['data']['spacetype']) {
+                        case 'type0':
+                            $space0 = str_repeat(' ', $token['data']['spacecount']);
+
+                            break;
+                        case 'type1':
+                            $space1 = str_repeat("\n", $token['data']['spacecount']);
+
+                            break;
+                        case 'type2':
+                            $space2 = str_repeat(' ', $token['data']['spacecount']);
+
+                            break;
+                        case 'type3':
+                            $space3 = str_repeat("\n", $token['data']['spacecount']);
+
+                            break;
+                        case 'type4':
+                            $space4 = str_repeat(' ', $token['data']['spacecount']);
+
+                            break;
+                        case 'type5':
+                            $space5 = str_repeat("\n", $token['data']['spacecount']);
+
+                            break;
+                    }
+
+                    break;
+                case 'tAttrSum': // SUM function with one parameter
+                    $op = array_pop($formulaStrings);
+                    $formulaStrings[] = "{$space1}{$space0}SUM($op)";
+                    unset($space0, $space1);
+
+                    break;
+                case 'tFunc': // function with fixed number of arguments
+                case 'tFuncV': // function with variable number of arguments
+                    if ($token['data']['function'] != '') {
+                        // normal function
+                        $ops = []; // array of operators
+                        for ($i = 0; $i < $token['data']['args']; ++$i) {
+                            $ops[] = array_pop($formulaStrings);
+                        }
+                        $ops = array_reverse($ops);
+                        $formulaStrings[] = "$space1$space0{$token['data']['function']}(" . implode(',', $ops) . ')';
+                        unset($space0, $space1);
+                    } else {
+                        // add-in function
+                        $ops = []; // array of operators
+                        for ($i = 0; $i < $token['data']['args'] - 1; ++$i) {
+                            $ops[] = array_pop($formulaStrings);
+                        }
+                        $ops = array_reverse($ops);
+                        $function = array_pop($formulaStrings);
+                        $formulaStrings[] = "$space1$space0$function(" . implode(',', $ops) . ')';
+                        unset($space0, $space1);
+                    }
+
+                    break;
+                case 'tParen': // parenthesis
+                    $expression = array_pop($formulaStrings);
+                    $formulaStrings[] = "$space3$space2($expression$space5$space4)";
+                    unset($space2, $space3, $space4, $space5);
+
+                    break;
+                case 'tArray': // array constant
+                    $constantArray = self::readBIFF8ConstantArray($additionalData);
+                    $formulaStrings[] = $space1 . $space0 . $constantArray['value'];
+                    $additionalData = substr($additionalData, $constantArray['size']); // bite of chunk of additional data
+                    unset($space0, $space1);
+
+                    break;
+                case 'tMemArea':
+                    // bite off chunk of additional data
+                    $cellRangeAddressList = $this->readBIFF8CellRangeAddressList($additionalData);
+                    $additionalData = substr($additionalData, $cellRangeAddressList['size']);
+                    $formulaStrings[] = "$space1$space0{$token['data']}";
+                    unset($space0, $space1);
+
+                    break;
+                case 'tArea': // cell range address
+                case 'tBool': // boolean
+                case 'tErr': // error code
+                case 'tInt': // integer
+                case 'tMemErr':
+                case 'tMemFunc':
+                case 'tMissArg':
+                case 'tName':
+                case 'tNameX':
+                case 'tNum': // number
+                case 'tRef': // single cell reference
+                case 'tRef3d': // 3d cell reference
+                case 'tArea3d': // 3d cell range reference
+                case 'tRefN':
+                case 'tAreaN':
+                case 'tStr': // string
+                    $formulaStrings[] = "$space1$space0{$token['data']}";
+                    unset($space0, $space1);
+
+                    break;
+            }
+        }
+        $formulaString = $formulaStrings[0];
+
+        return $formulaString;
+    }
+
+    /**
+     * Fetch next token from binary formula data.
+     *
+     * @param string $formulaData Formula data
+     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
+     *
+     * @return array
+     */
+    private function getNextToken($formulaData, $baseCell = 'A1')
+    {
+        // offset: 0; size: 1; token id
+        $id = ord($formulaData[0]); // token id
+        $name = false; // initialize token name
+
+        switch ($id) {
+            case 0x03:
+                $name = 'tAdd';
+                $size = 1;
+                $data = '+';
+
+                break;
+            case 0x04:
+                $name = 'tSub';
+                $size = 1;
+                $data = '-';
+
+                break;
+            case 0x05:
+                $name = 'tMul';
+                $size = 1;
+                $data = '*';
+
+                break;
+            case 0x06:
+                $name = 'tDiv';
+                $size = 1;
+                $data = '/';
+
+                break;
+            case 0x07:
+                $name = 'tPower';
+                $size = 1;
+                $data = '^';
+
+                break;
+            case 0x08:
+                $name = 'tConcat';
+                $size = 1;
+                $data = '&';
+
+                break;
+            case 0x09:
+                $name = 'tLT';
+                $size = 1;
+                $data = '<';
+
+                break;
+            case 0x0A:
+                $name = 'tLE';
+                $size = 1;
+                $data = '<=';
+
+                break;
+            case 0x0B:
+                $name = 'tEQ';
+                $size = 1;
+                $data = '=';
+
+                break;
+            case 0x0C:
+                $name = 'tGE';
+                $size = 1;
+                $data = '>=';
+
+                break;
+            case 0x0D:
+                $name = 'tGT';
+                $size = 1;
+                $data = '>';
+
+                break;
+            case 0x0E:
+                $name = 'tNE';
+                $size = 1;
+                $data = '<>';
+
+                break;
+            case 0x0F:
+                $name = 'tIsect';
+                $size = 1;
+                $data = ' ';
+
+                break;
+            case 0x10:
+                $name = 'tList';
+                $size = 1;
+                $data = ',';
+
+                break;
+            case 0x11:
+                $name = 'tRange';
+                $size = 1;
+                $data = ':';
+
+                break;
+            case 0x12:
+                $name = 'tUplus';
+                $size = 1;
+                $data = '+';
+
+                break;
+            case 0x13:
+                $name = 'tUminus';
+                $size = 1;
+                $data = '-';
+
+                break;
+            case 0x14:
+                $name = 'tPercent';
+                $size = 1;
+                $data = '%';
+
+                break;
+            case 0x15:    //    parenthesis
+                $name = 'tParen';
+                $size = 1;
+                $data = null;
+
+                break;
+            case 0x16:    //    missing argument
+                $name = 'tMissArg';
+                $size = 1;
+                $data = '';
+
+                break;
+            case 0x17:    //    string
+                $name = 'tStr';
+                // offset: 1; size: var; Unicode string, 8-bit string length
+                $string = self::readUnicodeStringShort(substr($formulaData, 1));
+                $size = 1 + $string['size'];
+                $data = self::UTF8toExcelDoubleQuoted($string['value']);
+
+                break;
+            case 0x19:    //    Special attribute
+                // offset: 1; size: 1; attribute type flags:
+                switch (ord($formulaData[1])) {
+                    case 0x01:
+                        $name = 'tAttrVolatile';
+                        $size = 4;
+                        $data = null;
+
+                        break;
+                    case 0x02:
+                        $name = 'tAttrIf';
+                        $size = 4;
+                        $data = null;
+
+                        break;
+                    case 0x04:
+                        $name = 'tAttrChoose';
+                        // offset: 2; size: 2; number of choices in the CHOOSE function ($nc, number of parameters decreased by 1)
+                        $nc = self::getUInt2d($formulaData, 2);
+                        // offset: 4; size: 2 * $nc
+                        // offset: 4 + 2 * $nc; size: 2
+                        $size = 2 * $nc + 6;
+                        $data = null;
+
+                        break;
+                    case 0x08:
+                        $name = 'tAttrSkip';
+                        $size = 4;
+                        $data = null;
+
+                        break;
+                    case 0x10:
+                        $name = 'tAttrSum';
+                        $size = 4;
+                        $data = null;
+
+                        break;
+                    case 0x40:
+                    case 0x41:
+                        $name = 'tAttrSpace';
+                        $size = 4;
+                        // offset: 2; size: 2; space type and position
+                        switch (ord($formulaData[2])) {
+                            case 0x00:
+                                $spacetype = 'type0';
+
+                                break;
+                            case 0x01:
+                                $spacetype = 'type1';
+
+                                break;
+                            case 0x02:
+                                $spacetype = 'type2';
+
+                                break;
+                            case 0x03:
+                                $spacetype = 'type3';
+
+                                break;
+                            case 0x04:
+                                $spacetype = 'type4';
+
+                                break;
+                            case 0x05:
+                                $spacetype = 'type5';
+
+                                break;
+                            default:
+                                throw new Exception('Unrecognized space type in tAttrSpace token');
+
+                                break;
+                        }
+                        // offset: 3; size: 1; number of inserted spaces/carriage returns
+                        $spacecount = ord($formulaData[3]);
+
+                        $data = ['spacetype' => $spacetype, 'spacecount' => $spacecount];
+
+                        break;
+                    default:
+                        throw new Exception('Unrecognized attribute flag in tAttr token');
+
+                        break;
+                }
+
+                break;
+            case 0x1C:    //    error code
+                // offset: 1; size: 1; error code
+                $name = 'tErr';
+                $size = 2;
+                $data = Xls\ErrorCode::lookup(ord($formulaData[1]));
+
+                break;
+            case 0x1D:    //    boolean
+                // offset: 1; size: 1; 0 = false, 1 = true;
+                $name = 'tBool';
+                $size = 2;
+                $data = ord($formulaData[1]) ? 'TRUE' : 'FALSE';
+
+                break;
+            case 0x1E:    //    integer
+                // offset: 1; size: 2; unsigned 16-bit integer
+                $name = 'tInt';
+                $size = 3;
+                $data = self::getUInt2d($formulaData, 1);
+
+                break;
+            case 0x1F:    //    number
+                // offset: 1; size: 8;
+                $name = 'tNum';
+                $size = 9;
+                $data = self::extractNumber(substr($formulaData, 1));
+                $data = str_replace(',', '.', (string) $data); // in case non-English locale
+                break;
+            case 0x20:    //    array constant
+            case 0x40:
+            case 0x60:
+                // offset: 1; size: 7; not used
+                $name = 'tArray';
+                $size = 8;
+                $data = null;
+
+                break;
+            case 0x21:    //    function with fixed number of arguments
+            case 0x41:
+            case 0x61:
+                $name = 'tFunc';
+                $size = 3;
+                // offset: 1; size: 2; index to built-in sheet function
+                switch (self::getUInt2d($formulaData, 1)) {
+                    case 2:
+                        $function = 'ISNA';
+                        $args = 1;
+
+                        break;
+                    case 3:
+                        $function = 'ISERROR';
+                        $args = 1;
+
+                        break;
+                    case 10:
+                        $function = 'NA';
+                        $args = 0;
+
+                        break;
+                    case 15:
+                        $function = 'SIN';
+                        $args = 1;
+
+                        break;
+                    case 16:
+                        $function = 'COS';
+                        $args = 1;
+
+                        break;
+                    case 17:
+                        $function = 'TAN';
+                        $args = 1;
+
+                        break;
+                    case 18:
+                        $function = 'ATAN';
+                        $args = 1;
+
+                        break;
+                    case 19:
+                        $function = 'PI';
+                        $args = 0;
+
+                        break;
+                    case 20:
+                        $function = 'SQRT';
+                        $args = 1;
+
+                        break;
+                    case 21:
+                        $function = 'EXP';
+                        $args = 1;
+
+                        break;
+                    case 22:
+                        $function = 'LN';
+                        $args = 1;
+
+                        break;
+                    case 23:
+                        $function = 'LOG10';
+                        $args = 1;
+
+                        break;
+                    case 24:
+                        $function = 'ABS';
+                        $args = 1;
+
+                        break;
+                    case 25:
+                        $function = 'INT';
+                        $args = 1;
+
+                        break;
+                    case 26:
+                        $function = 'SIGN';
+                        $args = 1;
+
+                        break;
+                    case 27:
+                        $function = 'ROUND';
+                        $args = 2;
+
+                        break;
+                    case 30:
+                        $function = 'REPT';
+                        $args = 2;
+
+                        break;
+                    case 31:
+                        $function = 'MID';
+                        $args = 3;
+
+                        break;
+                    case 32:
+                        $function = 'LEN';
+                        $args = 1;
+
+                        break;
+                    case 33:
+                        $function = 'VALUE';
+                        $args = 1;
+
+                        break;
+                    case 34:
+                        $function = 'TRUE';
+                        $args = 0;
+
+                        break;
+                    case 35:
+                        $function = 'FALSE';
+                        $args = 0;
+
+                        break;
+                    case 38:
+                        $function = 'NOT';
+                        $args = 1;
+
+                        break;
+                    case 39:
+                        $function = 'MOD';
+                        $args = 2;
+
+                        break;
+                    case 40:
+                        $function = 'DCOUNT';
+                        $args = 3;
+
+                        break;
+                    case 41:
+                        $function = 'DSUM';
+                        $args = 3;
+
+                        break;
+                    case 42:
+                        $function = 'DAVERAGE';
+                        $args = 3;
+
+                        break;
+                    case 43:
+                        $function = 'DMIN';
+                        $args = 3;
+
+                        break;
+                    case 44:
+                        $function = 'DMAX';
+                        $args = 3;
+
+                        break;
+                    case 45:
+                        $function = 'DSTDEV';
+                        $args = 3;
+
+                        break;
+                    case 48:
+                        $function = 'TEXT';
+                        $args = 2;
+
+                        break;
+                    case 61:
+                        $function = 'MIRR';
+                        $args = 3;
+
+                        break;
+                    case 63:
+                        $function = 'RAND';
+                        $args = 0;
+
+                        break;
+                    case 65:
+                        $function = 'DATE';
+                        $args = 3;
+
+                        break;
+                    case 66:
+                        $function = 'TIME';
+                        $args = 3;
+
+                        break;
+                    case 67:
+                        $function = 'DAY';
+                        $args = 1;
+
+                        break;
+                    case 68:
+                        $function = 'MONTH';
+                        $args = 1;
+
+                        break;
+                    case 69:
+                        $function = 'YEAR';
+                        $args = 1;
+
+                        break;
+                    case 71:
+                        $function = 'HOUR';
+                        $args = 1;
+
+                        break;
+                    case 72:
+                        $function = 'MINUTE';
+                        $args = 1;
+
+                        break;
+                    case 73:
+                        $function = 'SECOND';
+                        $args = 1;
+
+                        break;
+                    case 74:
+                        $function = 'NOW';
+                        $args = 0;
+
+                        break;
+                    case 75:
+                        $function = 'AREAS';
+                        $args = 1;
+
+                        break;
+                    case 76:
+                        $function = 'ROWS';
+                        $args = 1;
+
+                        break;
+                    case 77:
+                        $function = 'COLUMNS';
+                        $args = 1;
+
+                        break;
+                    case 83:
+                        $function = 'TRANSPOSE';
+                        $args = 1;
+
+                        break;
+                    case 86:
+                        $function = 'TYPE';
+                        $args = 1;
+
+                        break;
+                    case 97:
+                        $function = 'ATAN2';
+                        $args = 2;
+
+                        break;
+                    case 98:
+                        $function = 'ASIN';
+                        $args = 1;
+
+                        break;
+                    case 99:
+                        $function = 'ACOS';
+                        $args = 1;
+
+                        break;
+                    case 105:
+                        $function = 'ISREF';
+                        $args = 1;
+
+                        break;
+                    case 111:
+                        $function = 'CHAR';
+                        $args = 1;
+
+                        break;
+                    case 112:
+                        $function = 'LOWER';
+                        $args = 1;
+
+                        break;
+                    case 113:
+                        $function = 'UPPER';
+                        $args = 1;
+
+                        break;
+                    case 114:
+                        $function = 'PROPER';
+                        $args = 1;
+
+                        break;
+                    case 117:
+                        $function = 'EXACT';
+                        $args = 2;
+
+                        break;
+                    case 118:
+                        $function = 'TRIM';
+                        $args = 1;
+
+                        break;
+                    case 119:
+                        $function = 'REPLACE';
+                        $args = 4;
+
+                        break;
+                    case 121:
+                        $function = 'CODE';
+                        $args = 1;
+
+                        break;
+                    case 126:
+                        $function = 'ISERR';
+                        $args = 1;
+
+                        break;
+                    case 127:
+                        $function = 'ISTEXT';
+                        $args = 1;
+
+                        break;
+                    case 128:
+                        $function = 'ISNUMBER';
+                        $args = 1;
+
+                        break;
+                    case 129:
+                        $function = 'ISBLANK';
+                        $args = 1;
+
+                        break;
+                    case 130:
+                        $function = 'T';
+                        $args = 1;
+
+                        break;
+                    case 131:
+                        $function = 'N';
+                        $args = 1;
+
+                        break;
+                    case 140:
+                        $function = 'DATEVALUE';
+                        $args = 1;
+
+                        break;
+                    case 141:
+                        $function = 'TIMEVALUE';
+                        $args = 1;
+
+                        break;
+                    case 142:
+                        $function = 'SLN';
+                        $args = 3;
+
+                        break;
+                    case 143:
+                        $function = 'SYD';
+                        $args = 4;
+
+                        break;
+                    case 162:
+                        $function = 'CLEAN';
+                        $args = 1;
+
+                        break;
+                    case 163:
+                        $function = 'MDETERM';
+                        $args = 1;
+
+                        break;
+                    case 164:
+                        $function = 'MINVERSE';
+                        $args = 1;
+
+                        break;
+                    case 165:
+                        $function = 'MMULT';
+                        $args = 2;
+
+                        break;
+                    case 184:
+                        $function = 'FACT';
+                        $args = 1;
+
+                        break;
+                    case 189:
+                        $function = 'DPRODUCT';
+                        $args = 3;
+
+                        break;
+                    case 190:
+                        $function = 'ISNONTEXT';
+                        $args = 1;
+
+                        break;
+                    case 195:
+                        $function = 'DSTDEVP';
+                        $args = 3;
+
+                        break;
+                    case 196:
+                        $function = 'DVARP';
+                        $args = 3;
+
+                        break;
+                    case 198:
+                        $function = 'ISLOGICAL';
+                        $args = 1;
+
+                        break;
+                    case 199:
+                        $function = 'DCOUNTA';
+                        $args = 3;
+
+                        break;
+                    case 207:
+                        $function = 'REPLACEB';
+                        $args = 4;
+
+                        break;
+                    case 210:
+                        $function = 'MIDB';
+                        $args = 3;
+
+                        break;
+                    case 211:
+                        $function = 'LENB';
+                        $args = 1;
+
+                        break;
+                    case 212:
+                        $function = 'ROUNDUP';
+                        $args = 2;
+
+                        break;
+                    case 213:
+                        $function = 'ROUNDDOWN';
+                        $args = 2;
+
+                        break;
+                    case 214:
+                        $function = 'ASC';
+                        $args = 1;
+
+                        break;
+                    case 215:
+                        $function = 'DBCS';
+                        $args = 1;
+
+                        break;
+                    case 221:
+                        $function = 'TODAY';
+                        $args = 0;
+
+                        break;
+                    case 229:
+                        $function = 'SINH';
+                        $args = 1;
+
+                        break;
+                    case 230:
+                        $function = 'COSH';
+                        $args = 1;
+
+                        break;
+                    case 231:
+                        $function = 'TANH';
+                        $args = 1;
+
+                        break;
+                    case 232:
+                        $function = 'ASINH';
+                        $args = 1;
+
+                        break;
+                    case 233:
+                        $function = 'ACOSH';
+                        $args = 1;
+
+                        break;
+                    case 234:
+                        $function = 'ATANH';
+                        $args = 1;
+
+                        break;
+                    case 235:
+                        $function = 'DGET';
+                        $args = 3;
+
+                        break;
+                    case 244:
+                        $function = 'INFO';
+                        $args = 1;
+
+                        break;
+                    case 252:
+                        $function = 'FREQUENCY';
+                        $args = 2;
+
+                        break;
+                    case 261:
+                        $function = 'ERROR.TYPE';
+                        $args = 1;
+
+                        break;
+                    case 271:
+                        $function = 'GAMMALN';
+                        $args = 1;
+
+                        break;
+                    case 273:
+                        $function = 'BINOMDIST';
+                        $args = 4;
+
+                        break;
+                    case 274:
+                        $function = 'CHIDIST';
+                        $args = 2;
+
+                        break;
+                    case 275:
+                        $function = 'CHIINV';
+                        $args = 2;
+
+                        break;
+                    case 276:
+                        $function = 'COMBIN';
+                        $args = 2;
+
+                        break;
+                    case 277:
+                        $function = 'CONFIDENCE';
+                        $args = 3;
+
+                        break;
+                    case 278:
+                        $function = 'CRITBINOM';
+                        $args = 3;
+
+                        break;
+                    case 279:
+                        $function = 'EVEN';
+                        $args = 1;
+
+                        break;
+                    case 280:
+                        $function = 'EXPONDIST';
+                        $args = 3;
+
+                        break;
+                    case 281:
+                        $function = 'FDIST';
+                        $args = 3;
+
+                        break;
+                    case 282:
+                        $function = 'FINV';
+                        $args = 3;
+
+                        break;
+                    case 283:
+                        $function = 'FISHER';
+                        $args = 1;
+
+                        break;
+                    case 284:
+                        $function = 'FISHERINV';
+                        $args = 1;
+
+                        break;
+                    case 285:
+                        $function = 'FLOOR';
+                        $args = 2;
+
+                        break;
+                    case 286:
+                        $function = 'GAMMADIST';
+                        $args = 4;
+
+                        break;
+                    case 287:
+                        $function = 'GAMMAINV';
+                        $args = 3;
+
+                        break;
+                    case 288:
+                        $function = 'CEILING';
+                        $args = 2;
+
+                        break;
+                    case 289:
+                        $function = 'HYPGEOMDIST';
+                        $args = 4;
+
+                        break;
+                    case 290:
+                        $function = 'LOGNORMDIST';
+                        $args = 3;
+
+                        break;
+                    case 291:
+                        $function = 'LOGINV';
+                        $args = 3;
+
+                        break;
+                    case 292:
+                        $function = 'NEGBINOMDIST';
+                        $args = 3;
+
+                        break;
+                    case 293:
+                        $function = 'NORMDIST';
+                        $args = 4;
+
+                        break;
+                    case 294:
+                        $function = 'NORMSDIST';
+                        $args = 1;
+
+                        break;
+                    case 295:
+                        $function = 'NORMINV';
+                        $args = 3;
+
+                        break;
+                    case 296:
+                        $function = 'NORMSINV';
+                        $args = 1;
+
+                        break;
+                    case 297:
+                        $function = 'STANDARDIZE';
+                        $args = 3;
+
+                        break;
+                    case 298:
+                        $function = 'ODD';
+                        $args = 1;
+
+                        break;
+                    case 299:
+                        $function = 'PERMUT';
+                        $args = 2;
+
+                        break;
+                    case 300:
+                        $function = 'POISSON';
+                        $args = 3;
+
+                        break;
+                    case 301:
+                        $function = 'TDIST';
+                        $args = 3;
+
+                        break;
+                    case 302:
+                        $function = 'WEIBULL';
+                        $args = 4;
+
+                        break;
+                    case 303:
+                        $function = 'SUMXMY2';
+                        $args = 2;
+
+                        break;
+                    case 304:
+                        $function = 'SUMX2MY2';
+                        $args = 2;
+
+                        break;
+                    case 305:
+                        $function = 'SUMX2PY2';
+                        $args = 2;
+
+                        break;
+                    case 306:
+                        $function = 'CHITEST';
+                        $args = 2;
+
+                        break;
+                    case 307:
+                        $function = 'CORREL';
+                        $args = 2;
+
+                        break;
+                    case 308:
+                        $function = 'COVAR';
+                        $args = 2;
+
+                        break;
+                    case 309:
+                        $function = 'FORECAST';
+                        $args = 3;
+
+                        break;
+                    case 310:
+                        $function = 'FTEST';
+                        $args = 2;
+
+                        break;
+                    case 311:
+                        $function = 'INTERCEPT';
+                        $args = 2;
+
+                        break;
+                    case 312:
+                        $function = 'PEARSON';
+                        $args = 2;
+
+                        break;
+                    case 313:
+                        $function = 'RSQ';
+                        $args = 2;
+
+                        break;
+                    case 314:
+                        $function = 'STEYX';
+                        $args = 2;
+
+                        break;
+                    case 315:
+                        $function = 'SLOPE';
+                        $args = 2;
+
+                        break;
+                    case 316:
+                        $function = 'TTEST';
+                        $args = 4;
+
+                        break;
+                    case 325:
+                        $function = 'LARGE';
+                        $args = 2;
+
+                        break;
+                    case 326:
+                        $function = 'SMALL';
+                        $args = 2;
+
+                        break;
+                    case 327:
+                        $function = 'QUARTILE';
+                        $args = 2;
+
+                        break;
+                    case 328:
+                        $function = 'PERCENTILE';
+                        $args = 2;
+
+                        break;
+                    case 331:
+                        $function = 'TRIMMEAN';
+                        $args = 2;
+
+                        break;
+                    case 332:
+                        $function = 'TINV';
+                        $args = 2;
+
+                        break;
+                    case 337:
+                        $function = 'POWER';
+                        $args = 2;
+
+                        break;
+                    case 342:
+                        $function = 'RADIANS';
+                        $args = 1;
+
+                        break;
+                    case 343:
+                        $function = 'DEGREES';
+                        $args = 1;
+
+                        break;
+                    case 346:
+                        $function = 'COUNTIF';
+                        $args = 2;
+
+                        break;
+                    case 347:
+                        $function = 'COUNTBLANK';
+                        $args = 1;
+
+                        break;
+                    case 350:
+                        $function = 'ISPMT';
+                        $args = 4;
+
+                        break;
+                    case 351:
+                        $function = 'DATEDIF';
+                        $args = 3;
+
+                        break;
+                    case 352:
+                        $function = 'DATESTRING';
+                        $args = 1;
+
+                        break;
+                    case 353:
+                        $function = 'NUMBERSTRING';
+                        $args = 2;
+
+                        break;
+                    case 360:
+                        $function = 'PHONETIC';
+                        $args = 1;
+
+                        break;
+                    case 368:
+                        $function = 'BAHTTEXT';
+                        $args = 1;
+
+                        break;
+                    default:
+                        throw new Exception('Unrecognized function in formula');
+
+                        break;
+                }
+                $data = ['function' => $function, 'args' => $args];
+
+                break;
+            case 0x22:    //    function with variable number of arguments
+            case 0x42:
+            case 0x62:
+                $name = 'tFuncV';
+                $size = 4;
+                // offset: 1; size: 1; number of arguments
+                $args = ord($formulaData[1]);
+                // offset: 2: size: 2; index to built-in sheet function
+                $index = self::getUInt2d($formulaData, 2);
+                switch ($index) {
+                    case 0:
+                        $function = 'COUNT';
+
+                        break;
+                    case 1:
+                        $function = 'IF';
+
+                        break;
+                    case 4:
+                        $function = 'SUM';
+
+                        break;
+                    case 5:
+                        $function = 'AVERAGE';
+
+                        break;
+                    case 6:
+                        $function = 'MIN';
+
+                        break;
+                    case 7:
+                        $function = 'MAX';
+
+                        break;
+                    case 8:
+                        $function = 'ROW';
+
+                        break;
+                    case 9:
+                        $function = 'COLUMN';
+
+                        break;
+                    case 11:
+                        $function = 'NPV';
+
+                        break;
+                    case 12:
+                        $function = 'STDEV';
+
+                        break;
+                    case 13:
+                        $function = 'DOLLAR';
+
+                        break;
+                    case 14:
+                        $function = 'FIXED';
+
+                        break;
+                    case 28:
+                        $function = 'LOOKUP';
+
+                        break;
+                    case 29:
+                        $function = 'INDEX';
+
+                        break;
+                    case 36:
+                        $function = 'AND';
+
+                        break;
+                    case 37:
+                        $function = 'OR';
+
+                        break;
+                    case 46:
+                        $function = 'VAR';
+
+                        break;
+                    case 49:
+                        $function = 'LINEST';
+
+                        break;
+                    case 50:
+                        $function = 'TREND';
+
+                        break;
+                    case 51:
+                        $function = 'LOGEST';
+
+                        break;
+                    case 52:
+                        $function = 'GROWTH';
+
+                        break;
+                    case 56:
+                        $function = 'PV';
+
+                        break;
+                    case 57:
+                        $function = 'FV';
+
+                        break;
+                    case 58:
+                        $function = 'NPER';
+
+                        break;
+                    case 59:
+                        $function = 'PMT';
+
+                        break;
+                    case 60:
+                        $function = 'RATE';
+
+                        break;
+                    case 62:
+                        $function = 'IRR';
+
+                        break;
+                    case 64:
+                        $function = 'MATCH';
+
+                        break;
+                    case 70:
+                        $function = 'WEEKDAY';
+
+                        break;
+                    case 78:
+                        $function = 'OFFSET';
+
+                        break;
+                    case 82:
+                        $function = 'SEARCH';
+
+                        break;
+                    case 100:
+                        $function = 'CHOOSE';
+
+                        break;
+                    case 101:
+                        $function = 'HLOOKUP';
+
+                        break;
+                    case 102:
+                        $function = 'VLOOKUP';
+
+                        break;
+                    case 109:
+                        $function = 'LOG';
+
+                        break;
+                    case 115:
+                        $function = 'LEFT';
+
+                        break;
+                    case 116:
+                        $function = 'RIGHT';
+
+                        break;
+                    case 120:
+                        $function = 'SUBSTITUTE';
+
+                        break;
+                    case 124:
+                        $function = 'FIND';
+
+                        break;
+                    case 125:
+                        $function = 'CELL';
+
+                        break;
+                    case 144:
+                        $function = 'DDB';
+
+                        break;
+                    case 148:
+                        $function = 'INDIRECT';
+
+                        break;
+                    case 167:
+                        $function = 'IPMT';
+
+                        break;
+                    case 168:
+                        $function = 'PPMT';
+
+                        break;
+                    case 169:
+                        $function = 'COUNTA';
+
+                        break;
+                    case 183:
+                        $function = 'PRODUCT';
+
+                        break;
+                    case 193:
+                        $function = 'STDEVP';
+
+                        break;
+                    case 194:
+                        $function = 'VARP';
+
+                        break;
+                    case 197:
+                        $function = 'TRUNC';
+
+                        break;
+                    case 204:
+                        $function = 'USDOLLAR';
+
+                        break;
+                    case 205:
+                        $function = 'FINDB';
+
+                        break;
+                    case 206:
+                        $function = 'SEARCHB';
+
+                        break;
+                    case 208:
+                        $function = 'LEFTB';
+
+                        break;
+                    case 209:
+                        $function = 'RIGHTB';
+
+                        break;
+                    case 216:
+                        $function = 'RANK';
+
+                        break;
+                    case 219:
+                        $function = 'ADDRESS';
+
+                        break;
+                    case 220:
+                        $function = 'DAYS360';
+
+                        break;
+                    case 222:
+                        $function = 'VDB';
+
+                        break;
+                    case 227:
+                        $function = 'MEDIAN';
+
+                        break;
+                    case 228:
+                        $function = 'SUMPRODUCT';
+
+                        break;
+                    case 247:
+                        $function = 'DB';
+
+                        break;
+                    case 255:
+                        $function = '';
+
+                        break;
+                    case 269:
+                        $function = 'AVEDEV';
+
+                        break;
+                    case 270:
+                        $function = 'BETADIST';
+
+                        break;
+                    case 272:
+                        $function = 'BETAINV';
+
+                        break;
+                    case 317:
+                        $function = 'PROB';
+
+                        break;
+                    case 318:
+                        $function = 'DEVSQ';
+
+                        break;
+                    case 319:
+                        $function = 'GEOMEAN';
+
+                        break;
+                    case 320:
+                        $function = 'HARMEAN';
+
+                        break;
+                    case 321:
+                        $function = 'SUMSQ';
+
+                        break;
+                    case 322:
+                        $function = 'KURT';
+
+                        break;
+                    case 323:
+                        $function = 'SKEW';
+
+                        break;
+                    case 324:
+                        $function = 'ZTEST';
+
+                        break;
+                    case 329:
+                        $function = 'PERCENTRANK';
+
+                        break;
+                    case 330:
+                        $function = 'MODE';
+
+                        break;
+                    case 336:
+                        $function = 'CONCATENATE';
+
+                        break;
+                    case 344:
+                        $function = 'SUBTOTAL';
+
+                        break;
+                    case 345:
+                        $function = 'SUMIF';
+
+                        break;
+                    case 354:
+                        $function = 'ROMAN';
+
+                        break;
+                    case 358:
+                        $function = 'GETPIVOTDATA';
+
+                        break;
+                    case 359:
+                        $function = 'HYPERLINK';
+
+                        break;
+                    case 361:
+                        $function = 'AVERAGEA';
+
+                        break;
+                    case 362:
+                        $function = 'MAXA';
+
+                        break;
+                    case 363:
+                        $function = 'MINA';
+
+                        break;
+                    case 364:
+                        $function = 'STDEVPA';
+
+                        break;
+                    case 365:
+                        $function = 'VARPA';
+
+                        break;
+                    case 366:
+                        $function = 'STDEVA';
+
+                        break;
+                    case 367:
+                        $function = 'VARA';
+
+                        break;
+                    default:
+                        throw new Exception('Unrecognized function in formula');
+
+                        break;
+                }
+                $data = ['function' => $function, 'args' => $args];
+
+                break;
+            case 0x23:    //    index to defined name
+            case 0x43:
+            case 0x63:
+                $name = 'tName';
+                $size = 5;
+                // offset: 1; size: 2; one-based index to definedname record
+                $definedNameIndex = self::getUInt2d($formulaData, 1) - 1;
+                // offset: 2; size: 2; not used
+                $data = $this->definedname[$definedNameIndex]['name'];
+
+                break;
+            case 0x24:    //    single cell reference e.g. A5
+            case 0x44:
+            case 0x64:
+                $name = 'tRef';
+                $size = 5;
+                $data = $this->readBIFF8CellAddress(substr($formulaData, 1, 4));
+
+                break;
+            case 0x25:    //    cell range reference to cells in the same sheet (2d)
+            case 0x45:
+            case 0x65:
+                $name = 'tArea';
+                $size = 9;
+                $data = $this->readBIFF8CellRangeAddress(substr($formulaData, 1, 8));
+
+                break;
+            case 0x26:    //    Constant reference sub-expression
+            case 0x46:
+            case 0x66:
+                $name = 'tMemArea';
+                // offset: 1; size: 4; not used
+                // offset: 5; size: 2; size of the following subexpression
+                $subSize = self::getUInt2d($formulaData, 5);
+                $size = 7 + $subSize;
+                $data = $this->getFormulaFromData(substr($formulaData, 7, $subSize));
+
+                break;
+            case 0x27:    //    Deleted constant reference sub-expression
+            case 0x47:
+            case 0x67:
+                $name = 'tMemErr';
+                // offset: 1; size: 4; not used
+                // offset: 5; size: 2; size of the following subexpression
+                $subSize = self::getUInt2d($formulaData, 5);
+                $size = 7 + $subSize;
+                $data = $this->getFormulaFromData(substr($formulaData, 7, $subSize));
+
+                break;
+            case 0x29:    //    Variable reference sub-expression
+            case 0x49:
+            case 0x69:
+                $name = 'tMemFunc';
+                // offset: 1; size: 2; size of the following sub-expression
+                $subSize = self::getUInt2d($formulaData, 1);
+                $size = 3 + $subSize;
+                $data = $this->getFormulaFromData(substr($formulaData, 3, $subSize));
+
+                break;
+            case 0x2C: // Relative 2d cell reference reference, used in shared formulas and some other places
+            case 0x4C:
+            case 0x6C:
+                $name = 'tRefN';
+                $size = 5;
+                $data = $this->readBIFF8CellAddressB(substr($formulaData, 1, 4), $baseCell);
+
+                break;
+            case 0x2D:    //    Relative 2d range reference
+            case 0x4D:
+            case 0x6D:
+                $name = 'tAreaN';
+                $size = 9;
+                $data = $this->readBIFF8CellRangeAddressB(substr($formulaData, 1, 8), $baseCell);
+
+                break;
+            case 0x39:    //    External name
+            case 0x59:
+            case 0x79:
+                $name = 'tNameX';
+                $size = 7;
+                // offset: 1; size: 2; index to REF entry in EXTERNSHEET record
+                // offset: 3; size: 2; one-based index to DEFINEDNAME or EXTERNNAME record
+                $index = self::getUInt2d($formulaData, 3);
+                // assume index is to EXTERNNAME record
+                $data = $this->externalNames[$index - 1]['name'];
+                // offset: 5; size: 2; not used
+                break;
+            case 0x3A:    //    3d reference to cell
+            case 0x5A:
+            case 0x7A:
+                $name = 'tRef3d';
+                $size = 7;
+
+                try {
+                    // offset: 1; size: 2; index to REF entry
+                    $sheetRange = $this->readSheetRangeByRefIndex(self::getUInt2d($formulaData, 1));
+                    // offset: 3; size: 4; cell address
+                    $cellAddress = $this->readBIFF8CellAddress(substr($formulaData, 3, 4));
+
+                    $data = "$sheetRange!$cellAddress";
+                } catch (PhpSpreadsheetException $e) {
+                    // deleted sheet reference
+                    $data = '#REF!';
+                }
+
+                break;
+            case 0x3B:    //    3d reference to cell range
+            case 0x5B:
+            case 0x7B:
+                $name = 'tArea3d';
+                $size = 11;
+
+                try {
+                    // offset: 1; size: 2; index to REF entry
+                    $sheetRange = $this->readSheetRangeByRefIndex(self::getUInt2d($formulaData, 1));
+                    // offset: 3; size: 8; cell address
+                    $cellRangeAddress = $this->readBIFF8CellRangeAddress(substr($formulaData, 3, 8));
+
+                    $data = "$sheetRange!$cellRangeAddress";
+                } catch (PhpSpreadsheetException $e) {
+                    // deleted sheet reference
+                    $data = '#REF!';
+                }
+
+                break;
+            // Unknown cases    // don't know how to deal with
+            default:
+                throw new Exception('Unrecognized token ' . sprintf('%02X', $id) . ' in formula');
+
+                break;
+        }
+
+        return [
+            'id' => $id,
+            'name' => $name,
+            'size' => $size,
+            'data' => $data,
+        ];
+    }
+
+    /**
+     * Reads a cell address in BIFF8 e.g. 'A2' or '$A$2'
+     * section 3.3.4.
+     *
+     * @param string $cellAddressStructure
+     *
+     * @return string
+     */
+    private function readBIFF8CellAddress($cellAddressStructure)
+    {
+        // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767))
+        $row = self::getUInt2d($cellAddressStructure, 0) + 1;
+
+        // offset: 2; size: 2; index to column or column offset + relative flags
+        // bit: 7-0; mask 0x00FF; column index
+        $column = Coordinate::stringFromColumnIndex((0x00FF & self::getUInt2d($cellAddressStructure, 2)) + 1);
+
+        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
+        if (!(0x4000 & self::getUInt2d($cellAddressStructure, 2))) {
+            $column = '$' . $column;
+        }
+        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
+        if (!(0x8000 & self::getUInt2d($cellAddressStructure, 2))) {
+            $row = '$' . $row;
+        }
+
+        return $column . $row;
+    }
+
+    /**
+     * Reads a cell address in BIFF8 for shared formulas. Uses positive and negative values for row and column
+     * to indicate offsets from a base cell
+     * section 3.3.4.
+     *
+     * @param string $cellAddressStructure
+     * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
+     *
+     * @return string
+     */
+    private function readBIFF8CellAddressB($cellAddressStructure, $baseCell = 'A1')
+    {
+        [$baseCol, $baseRow] = Coordinate::coordinateFromString($baseCell);
+        $baseCol = Coordinate::columnIndexFromString($baseCol) - 1;
+
+        // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767))
+        $rowIndex = self::getUInt2d($cellAddressStructure, 0);
+        $row = self::getUInt2d($cellAddressStructure, 0) + 1;
+
+        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
+        if (!(0x4000 & self::getUInt2d($cellAddressStructure, 2))) {
+            // offset: 2; size: 2; index to column or column offset + relative flags
+            // bit: 7-0; mask 0x00FF; column index
+            $colIndex = 0x00FF & self::getUInt2d($cellAddressStructure, 2);
+
+            $column = Coordinate::stringFromColumnIndex($colIndex + 1);
+            $column = '$' . $column;
+        } else {
+            // offset: 2; size: 2; index to column or column offset + relative flags
+            // bit: 7-0; mask 0x00FF; column index
+            $relativeColIndex = 0x00FF & self::getInt2d($cellAddressStructure, 2);
+            $colIndex = $baseCol + $relativeColIndex;
+            $colIndex = ($colIndex < 256) ? $colIndex : $colIndex - 256;
+            $colIndex = ($colIndex >= 0) ? $colIndex : $colIndex + 256;
+            $column = Coordinate::stringFromColumnIndex($colIndex + 1);
+        }
+
+        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
+        if (!(0x8000 & self::getUInt2d($cellAddressStructure, 2))) {
+            $row = '$' . $row;
+        } else {
+            $rowIndex = ($rowIndex <= 32767) ? $rowIndex : $rowIndex - 65536;
+            $row = $baseRow + $rowIndex;
+        }
+
+        return $column . $row;
+    }
+
+    /**
+     * Reads a cell range address in BIFF5 e.g. 'A2:B6' or 'A1'
+     * always fixed range
+     * section 2.5.14.
+     *
+     * @param string $subData
+     *
+     * @return string
+     */
+    private function readBIFF5CellRangeAddressFixed($subData)
+    {
+        // offset: 0; size: 2; index to first row
+        $fr = self::getUInt2d($subData, 0) + 1;
+
+        // offset: 2; size: 2; index to last row
+        $lr = self::getUInt2d($subData, 2) + 1;
+
+        // offset: 4; size: 1; index to first column
+        $fc = ord($subData[4]);
+
+        // offset: 5; size: 1; index to last column
+        $lc = ord($subData[5]);
+
+        // check values
+        if ($fr > $lr || $fc > $lc) {
+            throw new Exception('Not a cell range address');
+        }
+
+        // column index to letter
+        $fc = Coordinate::stringFromColumnIndex($fc + 1);
+        $lc = Coordinate::stringFromColumnIndex($lc + 1);
+
+        if ($fr == $lr && $fc == $lc) {
+            return "$fc$fr";
+        }
+
+        return "$fc$fr:$lc$lr";
+    }
+
+    /**
+     * Reads a cell range address in BIFF8 e.g. 'A2:B6' or 'A1'
+     * always fixed range
+     * section 2.5.14.
+     *
+     * @param string $subData
+     *
+     * @return string
+     */
+    private function readBIFF8CellRangeAddressFixed($subData)
+    {
+        // offset: 0; size: 2; index to first row
+        $fr = self::getUInt2d($subData, 0) + 1;
+
+        // offset: 2; size: 2; index to last row
+        $lr = self::getUInt2d($subData, 2) + 1;
+
+        // offset: 4; size: 2; index to first column
+        $fc = self::getUInt2d($subData, 4);
+
+        // offset: 6; size: 2; index to last column
+        $lc = self::getUInt2d($subData, 6);
+
+        // check values
+        if ($fr > $lr || $fc > $lc) {
+            throw new Exception('Not a cell range address');
+        }
+
+        // column index to letter
+        $fc = Coordinate::stringFromColumnIndex($fc + 1);
+        $lc = Coordinate::stringFromColumnIndex($lc + 1);
+
+        if ($fr == $lr && $fc == $lc) {
+            return "$fc$fr";
+        }
+
+        return "$fc$fr:$lc$lr";
+    }
+
+    /**
+     * Reads a cell range address in BIFF8 e.g. 'A2:B6' or '$A$2:$B$6'
+     * there are flags indicating whether column/row index is relative
+     * section 3.3.4.
+     *
+     * @param string $subData
+     *
+     * @return string
+     */
+    private function readBIFF8CellRangeAddress($subData)
+    {
+        // todo: if cell range is just a single cell, should this funciton
+        // not just return e.g. 'A1' and not 'A1:A1' ?
+
+        // offset: 0; size: 2; index to first row (0... 65535) (or offset (-32768... 32767))
+        $fr = self::getUInt2d($subData, 0) + 1;
+
+        // offset: 2; size: 2; index to last row (0... 65535) (or offset (-32768... 32767))
+        $lr = self::getUInt2d($subData, 2) + 1;
+
+        // offset: 4; size: 2; index to first column or column offset + relative flags
+
+        // bit: 7-0; mask 0x00FF; column index
+        $fc = Coordinate::stringFromColumnIndex((0x00FF & self::getUInt2d($subData, 4)) + 1);
+
+        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
+        if (!(0x4000 & self::getUInt2d($subData, 4))) {
+            $fc = '$' . $fc;
+        }
+
+        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
+        if (!(0x8000 & self::getUInt2d($subData, 4))) {
+            $fr = '$' . $fr;
+        }
+
+        // offset: 6; size: 2; index to last column or column offset + relative flags
+
+        // bit: 7-0; mask 0x00FF; column index
+        $lc = Coordinate::stringFromColumnIndex((0x00FF & self::getUInt2d($subData, 6)) + 1);
+
+        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
+        if (!(0x4000 & self::getUInt2d($subData, 6))) {
+            $lc = '$' . $lc;
+        }
+
+        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
+        if (!(0x8000 & self::getUInt2d($subData, 6))) {
+            $lr = '$' . $lr;
+        }
+
+        return "$fc$fr:$lc$lr";
+    }
+
+    /**
+     * Reads a cell range address in BIFF8 for shared formulas. Uses positive and negative values for row and column
+     * to indicate offsets from a base cell
+     * section 3.3.4.
+     *
+     * @param string $subData
+     * @param string $baseCell Base cell
+     *
+     * @return string Cell range address
+     */
+    private function readBIFF8CellRangeAddressB($subData, $baseCell = 'A1')
+    {
+        [$baseCol, $baseRow] = Coordinate::coordinateFromString($baseCell);
+        $baseCol = Coordinate::columnIndexFromString($baseCol) - 1;
+
+        // TODO: if cell range is just a single cell, should this funciton
+        // not just return e.g. 'A1' and not 'A1:A1' ?
+
+        // offset: 0; size: 2; first row
+        $frIndex = self::getUInt2d($subData, 0); // adjust below
+
+        // offset: 2; size: 2; relative index to first row (0... 65535) should be treated as offset (-32768... 32767)
+        $lrIndex = self::getUInt2d($subData, 2); // adjust below
+
+        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
+        if (!(0x4000 & self::getUInt2d($subData, 4))) {
+            // absolute column index
+            // offset: 4; size: 2; first column with relative/absolute flags
+            // bit: 7-0; mask 0x00FF; column index
+            $fcIndex = 0x00FF & self::getUInt2d($subData, 4);
+            $fc = Coordinate::stringFromColumnIndex($fcIndex + 1);
+            $fc = '$' . $fc;
+        } else {
+            // column offset
+            // offset: 4; size: 2; first column with relative/absolute flags
+            // bit: 7-0; mask 0x00FF; column index
+            $relativeFcIndex = 0x00FF & self::getInt2d($subData, 4);
+            $fcIndex = $baseCol + $relativeFcIndex;
+            $fcIndex = ($fcIndex < 256) ? $fcIndex : $fcIndex - 256;
+            $fcIndex = ($fcIndex >= 0) ? $fcIndex : $fcIndex + 256;
+            $fc = Coordinate::stringFromColumnIndex($fcIndex + 1);
+        }
+
+        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
+        if (!(0x8000 & self::getUInt2d($subData, 4))) {
+            // absolute row index
+            $fr = $frIndex + 1;
+            $fr = '$' . $fr;
+        } else {
+            // row offset
+            $frIndex = ($frIndex <= 32767) ? $frIndex : $frIndex - 65536;
+            $fr = $baseRow + $frIndex;
+        }
+
+        // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
+        if (!(0x4000 & self::getUInt2d($subData, 6))) {
+            // absolute column index
+            // offset: 6; size: 2; last column with relative/absolute flags
+            // bit: 7-0; mask 0x00FF; column index
+            $lcIndex = 0x00FF & self::getUInt2d($subData, 6);
+            $lc = Coordinate::stringFromColumnIndex($lcIndex + 1);
+            $lc = '$' . $lc;
+        } else {
+            // column offset
+            // offset: 4; size: 2; first column with relative/absolute flags
+            // bit: 7-0; mask 0x00FF; column index
+            $relativeLcIndex = 0x00FF & self::getInt2d($subData, 4);
+            $lcIndex = $baseCol + $relativeLcIndex;
+            $lcIndex = ($lcIndex < 256) ? $lcIndex : $lcIndex - 256;
+            $lcIndex = ($lcIndex >= 0) ? $lcIndex : $lcIndex + 256;
+            $lc = Coordinate::stringFromColumnIndex($lcIndex + 1);
+        }
+
+        // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
+        if (!(0x8000 & self::getUInt2d($subData, 6))) {
+            // absolute row index
+            $lr = $lrIndex + 1;
+            $lr = '$' . $lr;
+        } else {
+            // row offset
+            $lrIndex = ($lrIndex <= 32767) ? $lrIndex : $lrIndex - 65536;
+            $lr = $baseRow + $lrIndex;
+        }
+
+        return "$fc$fr:$lc$lr";
+    }
+
+    /**
+     * Read BIFF8 cell range address list
+     * section 2.5.15.
+     *
+     * @param string $subData
+     *
+     * @return array
+     */
+    private function readBIFF8CellRangeAddressList($subData)
+    {
+        $cellRangeAddresses = [];
+
+        // offset: 0; size: 2; number of the following cell range addresses
+        $nm = self::getUInt2d($subData, 0);
+
+        $offset = 2;
+        // offset: 2; size: 8 * $nm; list of $nm (fixed) cell range addresses
+        for ($i = 0; $i < $nm; ++$i) {
+            $cellRangeAddresses[] = $this->readBIFF8CellRangeAddressFixed(substr($subData, $offset, 8));
+            $offset += 8;
+        }
+
+        return [
+            'size' => 2 + 8 * $nm,
+            'cellRangeAddresses' => $cellRangeAddresses,
+        ];
+    }
+
+    /**
+     * Read BIFF5 cell range address list
+     * section 2.5.15.
+     *
+     * @param string $subData
+     *
+     * @return array
+     */
+    private function readBIFF5CellRangeAddressList($subData)
+    {
+        $cellRangeAddresses = [];
+
+        // offset: 0; size: 2; number of the following cell range addresses
+        $nm = self::getUInt2d($subData, 0);
+
+        $offset = 2;
+        // offset: 2; size: 6 * $nm; list of $nm (fixed) cell range addresses
+        for ($i = 0; $i < $nm; ++$i) {
+            $cellRangeAddresses[] = $this->readBIFF5CellRangeAddressFixed(substr($subData, $offset, 6));
+            $offset += 6;
+        }
+
+        return [
+            'size' => 2 + 6 * $nm,
+            'cellRangeAddresses' => $cellRangeAddresses,
+        ];
+    }
+
+    /**
+     * Get a sheet range like Sheet1:Sheet3 from REF index
+     * Note: If there is only one sheet in the range, one gets e.g Sheet1
+     * It can also happen that the REF structure uses the -1 (FFFF) code to indicate deleted sheets,
+     * in which case an Exception is thrown.
+     *
+     * @param int $index
+     *
+     * @return false|string
+     */
+    private function readSheetRangeByRefIndex($index)
+    {
+        if (isset($this->ref[$index])) {
+            $type = $this->externalBooks[$this->ref[$index]['externalBookIndex']]['type'];
+
+            switch ($type) {
+                case 'internal':
+                    // check if we have a deleted 3d reference
+                    if ($this->ref[$index]['firstSheetIndex'] == 0xFFFF || $this->ref[$index]['lastSheetIndex'] == 0xFFFF) {
+                        throw new Exception('Deleted sheet reference');
+                    }
+
+                    // we have normal sheet range (collapsed or uncollapsed)
+                    $firstSheetName = $this->sheets[$this->ref[$index]['firstSheetIndex']]['name'];
+                    $lastSheetName = $this->sheets[$this->ref[$index]['lastSheetIndex']]['name'];
+
+                    if ($firstSheetName == $lastSheetName) {
+                        // collapsed sheet range
+                        $sheetRange = $firstSheetName;
+                    } else {
+                        $sheetRange = "$firstSheetName:$lastSheetName";
+                    }
+
+                    // escape the single-quotes
+                    $sheetRange = str_replace("'", "''", $sheetRange);
+
+                    // if there are special characters, we need to enclose the range in single-quotes
+                    // todo: check if we have identified the whole set of special characters
+                    // it seems that the following characters are not accepted for sheet names
+                    // and we may assume that they are not present: []*/:\?
+                    if (preg_match("/[ !\"@#£$%&{()}<>=+'|^,;-]/u", $sheetRange)) {
+                        $sheetRange = "'$sheetRange'";
+                    }
+
+                    return $sheetRange;
+
+                    break;
+                default:
+                    // TODO: external sheet support
+                    throw new Exception('Xls reader only supports internal sheets in formulas');
+
+                    break;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * read BIFF8 constant value array from array data
+     * returns e.g. ['value' => '{1,2;3,4}', 'size' => 40]
+     * section 2.5.8.
+     *
+     * @param string $arrayData
+     *
+     * @return array
+     */
+    private static function readBIFF8ConstantArray($arrayData)
+    {
+        // offset: 0; size: 1; number of columns decreased by 1
+        $nc = ord($arrayData[0]);
+
+        // offset: 1; size: 2; number of rows decreased by 1
+        $nr = self::getUInt2d($arrayData, 1);
+        $size = 3; // initialize
+        $arrayData = substr($arrayData, 3);
+
+        // offset: 3; size: var; list of ($nc + 1) * ($nr + 1) constant values
+        $matrixChunks = [];
+        for ($r = 1; $r <= $nr + 1; ++$r) {
+            $items = [];
+            for ($c = 1; $c <= $nc + 1; ++$c) {
+                $constant = self::readBIFF8Constant($arrayData);
+                $items[] = $constant['value'];
+                $arrayData = substr($arrayData, $constant['size']);
+                $size += $constant['size'];
+            }
+            $matrixChunks[] = implode(',', $items); // looks like e.g. '1,"hello"'
+        }
+        $matrix = '{' . implode(';', $matrixChunks) . '}';
+
+        return [
+            'value' => $matrix,
+            'size' => $size,
+        ];
+    }
+
+    /**
+     * read BIFF8 constant value which may be 'Empty Value', 'Number', 'String Value', 'Boolean Value', 'Error Value'
+     * section 2.5.7
+     * returns e.g. ['value' => '5', 'size' => 9].
+     *
+     * @param string $valueData
+     *
+     * @return array
+     */
+    private static function readBIFF8Constant($valueData)
+    {
+        // offset: 0; size: 1; identifier for type of constant
+        $identifier = ord($valueData[0]);
+
+        switch ($identifier) {
+            case 0x00: // empty constant (what is this?)
+                $value = '';
+                $size = 9;
+
+                break;
+            case 0x01: // number
+                // offset: 1; size: 8; IEEE 754 floating-point value
+                $value = self::extractNumber(substr($valueData, 1, 8));
+                $size = 9;
+
+                break;
+            case 0x02: // string value
+                // offset: 1; size: var; Unicode string, 16-bit string length
+                $string = self::readUnicodeStringLong(substr($valueData, 1));
+                $value = '"' . $string['value'] . '"';
+                $size = 1 + $string['size'];
+
+                break;
+            case 0x04: // boolean
+                // offset: 1; size: 1; 0 = FALSE, 1 = TRUE
+                if (ord($valueData[1])) {
+                    $value = 'TRUE';
+                } else {
+                    $value = 'FALSE';
+                }
+                $size = 9;
+
+                break;
+            case 0x10: // error code
+                // offset: 1; size: 1; error code
+                $value = Xls\ErrorCode::lookup(ord($valueData[1]));
+                $size = 9;
+
+                break;
+        }
+
+        return [
+            'value' => $value,
+            'size' => $size,
+        ];
+    }
+
+    /**
+     * Extract RGB color
+     * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.4.
+     *
+     * @param string $rgb Encoded RGB value (4 bytes)
+     *
+     * @return array
+     */
+    private static function readRGB($rgb)
+    {
+        // offset: 0; size 1; Red component
+        $r = ord($rgb[0]);
+
+        // offset: 1; size: 1; Green component
+        $g = ord($rgb[1]);
+
+        // offset: 2; size: 1; Blue component
+        $b = ord($rgb[2]);
+
+        // HEX notation, e.g. 'FF00FC'
+        $rgb = sprintf('%02X%02X%02X', $r, $g, $b);
+
+        return ['rgb' => $rgb];
+    }
+
+    /**
+     * Read byte string (8-bit string length)
+     * OpenOffice documentation: 2.5.2.
+     *
+     * @param string $subData
+     *
+     * @return array
+     */
+    private function readByteStringShort($subData)
+    {
+        // offset: 0; size: 1; length of the string (character count)
+        $ln = ord($subData[0]);
+
+        // offset: 1: size: var; character array (8-bit characters)
+        $value = $this->decodeCodepage(substr($subData, 1, $ln));
+
+        return [
+            'value' => $value,
+            'size' => 1 + $ln, // size in bytes of data structure
+        ];
+    }
+
+    /**
+     * Read byte string (16-bit string length)
+     * OpenOffice documentation: 2.5.2.
+     *
+     * @param string $subData
+     *
+     * @return array
+     */
+    private function readByteStringLong($subData)
+    {
+        // offset: 0; size: 2; length of the string (character count)
+        $ln = self::getUInt2d($subData, 0);
+
+        // offset: 2: size: var; character array (8-bit characters)
+        $value = $this->decodeCodepage(substr($subData, 2));
+
+        //return $string;
+        return [
+            'value' => $value,
+            'size' => 2 + $ln, // size in bytes of data structure
+        ];
+    }
+
+    /**
+     * Extracts an Excel Unicode short string (8-bit string length)
+     * OpenOffice documentation: 2.5.3
+     * function will automatically find out where the Unicode string ends.
+     *
+     * @param string $subData
+     *
+     * @return array
+     */
+    private static function readUnicodeStringShort($subData)
+    {
+        $value = '';
+
+        // offset: 0: size: 1; length of the string (character count)
+        $characterCount = ord($subData[0]);
+
+        $string = self::readUnicodeString(substr($subData, 1), $characterCount);
+
+        // add 1 for the string length
+        ++$string['size'];
+
+        return $string;
+    }
+
+    /**
+     * Extracts an Excel Unicode long string (16-bit string length)
+     * OpenOffice documentation: 2.5.3
+     * this function is under construction, needs to support rich text, and Asian phonetic settings.
+     *
+     * @param string $subData
+     *
+     * @return array
+     */
+    private static function readUnicodeStringLong($subData)
+    {
+        $value = '';
+
+        // offset: 0: size: 2; length of the string (character count)
+        $characterCount = self::getUInt2d($subData, 0);
+
+        $string = self::readUnicodeString(substr($subData, 2), $characterCount);
+
+        // add 2 for the string length
+        $string['size'] += 2;
+
+        return $string;
+    }
+
+    /**
+     * Read Unicode string with no string length field, but with known character count
+     * this function is under construction, needs to support rich text, and Asian phonetic settings
+     * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.3.
+     *
+     * @param string $subData
+     * @param int $characterCount
+     *
+     * @return array
+     */
+    private static function readUnicodeString($subData, $characterCount)
+    {
+        $value = '';
+
+        // offset: 0: size: 1; option flags
+        // bit: 0; mask: 0x01; character compression (0 = compressed 8-bit, 1 = uncompressed 16-bit)
+        $isCompressed = !((0x01 & ord($subData[0])) >> 0);
+
+        // bit: 2; mask: 0x04; Asian phonetic settings
+        $hasAsian = (0x04) & ord($subData[0]) >> 2;
+
+        // bit: 3; mask: 0x08; Rich-Text settings
+        $hasRichText = (0x08) & ord($subData[0]) >> 3;
+
+        // offset: 1: size: var; character array
+        // this offset assumes richtext and Asian phonetic settings are off which is generally wrong
+        // needs to be fixed
+        $value = self::encodeUTF16(substr($subData, 1, $isCompressed ? $characterCount : 2 * $characterCount), $isCompressed);
+
+        return [
+            'value' => $value,
+            'size' => $isCompressed ? 1 + $characterCount : 1 + 2 * $characterCount, // the size in bytes including the option flags
+        ];
+    }
+
+    /**
+     * Convert UTF-8 string to string surounded by double quotes. Used for explicit string tokens in formulas.
+     * Example:  hello"world  -->  "hello""world".
+     *
+     * @param string $value UTF-8 encoded string
+     *
+     * @return string
+     */
+    private static function UTF8toExcelDoubleQuoted($value)
+    {
+        return '"' . str_replace('"', '""', $value) . '"';
+    }
+
+    /**
+     * Reads first 8 bytes of a string and return IEEE 754 float.
+     *
+     * @param string $data Binary string that is at least 8 bytes long
+     *
+     * @return float
+     */
+    private static function extractNumber($data)
+    {
+        $rknumhigh = self::getInt4d($data, 4);
+        $rknumlow = self::getInt4d($data, 0);
+        $sign = ($rknumhigh & 0x80000000) >> 31;
+        $exp = (($rknumhigh & 0x7ff00000) >> 20) - 1023;
+        $mantissa = (0x100000 | ($rknumhigh & 0x000fffff));
+        $mantissalow1 = ($rknumlow & 0x80000000) >> 31;
+        $mantissalow2 = ($rknumlow & 0x7fffffff);
+        $value = $mantissa / 2 ** (20 - $exp);
+
+        if ($mantissalow1 != 0) {
+            $value += 1 / 2 ** (21 - $exp);
+        }
+
+        $value += $mantissalow2 / 2 ** (52 - $exp);
+        if ($sign) {
+            $value *= -1;
+        }
+
+        return $value;
+    }
+
+    /**
+     * @param int $rknum
+     *
+     * @return float
+     */
+    private static function getIEEE754($rknum)
+    {
+        if (($rknum & 0x02) != 0) {
+            $value = $rknum >> 2;
+        } else {
+            // changes by mmp, info on IEEE754 encoding from
+            // research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html
+            // The RK format calls for using only the most significant 30 bits
+            // of the 64 bit floating point value. The other 34 bits are assumed
+            // to be 0 so we use the upper 30 bits of $rknum as follows...
+            $sign = ($rknum & 0x80000000) >> 31;
+            $exp = ($rknum & 0x7ff00000) >> 20;
+            $mantissa = (0x100000 | ($rknum & 0x000ffffc));
+            $value = $mantissa / 2 ** (20 - ($exp - 1023));
+            if ($sign) {
+                $value = -1 * $value;
+            }
+            //end of changes by mmp
+        }
+        if (($rknum & 0x01) != 0) {
+            $value /= 100;
+        }
+
+        return $value;
+    }
+
+    /**
+     * Get UTF-8 string from (compressed or uncompressed) UTF-16 string.
+     *
+     * @param string $string
+     * @param bool $compressed
+     *
+     * @return string
+     */
+    private static function encodeUTF16($string, $compressed = false)
+    {
+        if ($compressed) {
+            $string = self::uncompressByteString($string);
+        }
+
+        return StringHelper::convertEncoding($string, 'UTF-8', 'UTF-16LE');
+    }
+
+    /**
+     * Convert UTF-16 string in compressed notation to uncompressed form. Only used for BIFF8.
+     *
+     * @param string $string
+     *
+     * @return string
+     */
+    private static function uncompressByteString($string)
+    {
+        $uncompressedString = '';
+        $strLen = strlen($string);
+        for ($i = 0; $i < $strLen; ++$i) {
+            $uncompressedString .= $string[$i] . "\0";
+        }
+
+        return $uncompressedString;
+    }
+
+    /**
+     * Convert string to UTF-8. Only used for BIFF5.
+     *
+     * @param string $string
+     *
+     * @return string
+     */
+    private function decodeCodepage($string)
+    {
+        return StringHelper::convertEncoding($string, 'UTF-8', $this->codepage);
+    }
+
+    /**
+     * Read 16-bit unsigned integer.
+     *
+     * @param string $data
+     * @param int $pos
+     *
+     * @return int
+     */
+    public static function getUInt2d($data, $pos)
+    {
+        return ord($data[$pos]) | (ord($data[$pos + 1]) << 8);
+    }
+
+    /**
+     * Read 16-bit signed integer.
+     *
+     * @param string $data
+     * @param int $pos
+     *
+     * @return int
+     */
+    public static function getInt2d($data, $pos)
+    {
+        return unpack('s', $data[$pos] . $data[$pos + 1])[1];
+    }
+
+    /**
+     * Read 32-bit signed integer.
+     *
+     * @param string $data
+     * @param int $pos
+     *
+     * @return int
+     */
+    public static function getInt4d($data, $pos)
+    {
+        // FIX: represent numbers correctly on 64-bit system
+        // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
+        // Changed by Andreas Rehm 2006 to ensure correct result of the <<24 block on 32 and 64bit systems
+        $_or_24 = ord($data[$pos + 3]);
+        if ($_or_24 >= 128) {
+            // negative number
+            $_ord_24 = -abs((256 - $_or_24) << 24);
+        } else {
+            $_ord_24 = ($_or_24 & 127) << 24;
+        }
+
+        return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24;
+    }
+
+    private function parseRichText($is)
+    {
+        $value = new RichText();
+        $value->createText($is);
+
+        return $value;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color.php
new file mode 100755
index 0000000..c45f88c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xls;
+
+use PhpOffice\PhpSpreadsheet\Reader\Xls;
+
+class Color
+{
+    /**
+     * Read color.
+     *
+     * @param int $color Indexed color
+     * @param array $palette Color palette
+     * @param int $version
+     *
+     * @return array RGB color value, example: ['rgb' => 'FF0000']
+     */
+    public static function map($color, $palette, $version)
+    {
+        if ($color <= 0x07 || $color >= 0x40) {
+            // special built-in color
+            return Color\BuiltIn::lookup($color);
+        } elseif (isset($palette, $palette[$color - 8])) {
+            // palette color, color index 0x08 maps to pallete index 0
+            return $palette[$color - 8];
+        }
+
+        // default color table
+        if ($version == Xls::XLS_BIFF8) {
+            return Color\BIFF8::lookup($color);
+        }
+
+        // BIFF5
+        return Color\BIFF5::lookup($color);
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF5.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF5.php
new file mode 100755
index 0000000..743d938
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF5.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xls\Color;
+
+class BIFF5
+{
+    protected static $map = [
+        0x08 => '000000',
+        0x09 => 'FFFFFF',
+        0x0A => 'FF0000',
+        0x0B => '00FF00',
+        0x0C => '0000FF',
+        0x0D => 'FFFF00',
+        0x0E => 'FF00FF',
+        0x0F => '00FFFF',
+        0x10 => '800000',
+        0x11 => '008000',
+        0x12 => '000080',
+        0x13 => '808000',
+        0x14 => '800080',
+        0x15 => '008080',
+        0x16 => 'C0C0C0',
+        0x17 => '808080',
+        0x18 => '8080FF',
+        0x19 => '802060',
+        0x1A => 'FFFFC0',
+        0x1B => 'A0E0F0',
+        0x1C => '600080',
+        0x1D => 'FF8080',
+        0x1E => '0080C0',
+        0x1F => 'C0C0FF',
+        0x20 => '000080',
+        0x21 => 'FF00FF',
+        0x22 => 'FFFF00',
+        0x23 => '00FFFF',
+        0x24 => '800080',
+        0x25 => '800000',
+        0x26 => '008080',
+        0x27 => '0000FF',
+        0x28 => '00CFFF',
+        0x29 => '69FFFF',
+        0x2A => 'E0FFE0',
+        0x2B => 'FFFF80',
+        0x2C => 'A6CAF0',
+        0x2D => 'DD9CB3',
+        0x2E => 'B38FEE',
+        0x2F => 'E3E3E3',
+        0x30 => '2A6FF9',
+        0x31 => '3FB8CD',
+        0x32 => '488436',
+        0x33 => '958C41',
+        0x34 => '8E5E42',
+        0x35 => 'A0627A',
+        0x36 => '624FAC',
+        0x37 => '969696',
+        0x38 => '1D2FBE',
+        0x39 => '286676',
+        0x3A => '004500',
+        0x3B => '453E01',
+        0x3C => '6A2813',
+        0x3D => '85396A',
+        0x3E => '4A3285',
+        0x3F => '424242',
+    ];
+
+    /**
+     * Map color array from BIFF5 built-in color index.
+     *
+     * @param int $color
+     *
+     * @return array
+     */
+    public static function lookup($color)
+    {
+        if (isset(self::$map[$color])) {
+            return ['rgb' => self::$map[$color]];
+        }
+
+        return ['rgb' => '000000'];
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF8.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF8.php
new file mode 100755
index 0000000..5c109fb
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF8.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xls\Color;
+
+class BIFF8
+{
+    protected static $map = [
+        0x08 => '000000',
+        0x09 => 'FFFFFF',
+        0x0A => 'FF0000',
+        0x0B => '00FF00',
+        0x0C => '0000FF',
+        0x0D => 'FFFF00',
+        0x0E => 'FF00FF',
+        0x0F => '00FFFF',
+        0x10 => '800000',
+        0x11 => '008000',
+        0x12 => '000080',
+        0x13 => '808000',
+        0x14 => '800080',
+        0x15 => '008080',
+        0x16 => 'C0C0C0',
+        0x17 => '808080',
+        0x18 => '9999FF',
+        0x19 => '993366',
+        0x1A => 'FFFFCC',
+        0x1B => 'CCFFFF',
+        0x1C => '660066',
+        0x1D => 'FF8080',
+        0x1E => '0066CC',
+        0x1F => 'CCCCFF',
+        0x20 => '000080',
+        0x21 => 'FF00FF',
+        0x22 => 'FFFF00',
+        0x23 => '00FFFF',
+        0x24 => '800080',
+        0x25 => '800000',
+        0x26 => '008080',
+        0x27 => '0000FF',
+        0x28 => '00CCFF',
+        0x29 => 'CCFFFF',
+        0x2A => 'CCFFCC',
+        0x2B => 'FFFF99',
+        0x2C => '99CCFF',
+        0x2D => 'FF99CC',
+        0x2E => 'CC99FF',
+        0x2F => 'FFCC99',
+        0x30 => '3366FF',
+        0x31 => '33CCCC',
+        0x32 => '99CC00',
+        0x33 => 'FFCC00',
+        0x34 => 'FF9900',
+        0x35 => 'FF6600',
+        0x36 => '666699',
+        0x37 => '969696',
+        0x38 => '003366',
+        0x39 => '339966',
+        0x3A => '003300',
+        0x3B => '333300',
+        0x3C => '993300',
+        0x3D => '993366',
+        0x3E => '333399',
+        0x3F => '333333',
+    ];
+
+    /**
+     * Map color array from BIFF8 built-in color index.
+     *
+     * @param int $color
+     *
+     * @return array
+     */
+    public static function lookup($color)
+    {
+        if (isset(self::$map[$color])) {
+            return ['rgb' => self::$map[$color]];
+        }
+
+        return ['rgb' => '000000'];
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BuiltIn.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BuiltIn.php
new file mode 100755
index 0000000..90d50e3
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BuiltIn.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xls\Color;
+
+class BuiltIn
+{
+    protected static $map = [
+        0x00 => '000000',
+        0x01 => 'FFFFFF',
+        0x02 => 'FF0000',
+        0x03 => '00FF00',
+        0x04 => '0000FF',
+        0x05 => 'FFFF00',
+        0x06 => 'FF00FF',
+        0x07 => '00FFFF',
+        0x40 => '000000', // system window text color
+        0x41 => 'FFFFFF', // system window background color
+    ];
+
+    /**
+     * Map built-in color to RGB value.
+     *
+     * @param int $color Indexed color
+     *
+     * @return array
+     */
+    public static function lookup($color)
+    {
+        if (isset(self::$map[$color])) {
+            return ['rgb' => self::$map[$color]];
+        }
+
+        return ['rgb' => '000000'];
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ErrorCode.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ErrorCode.php
new file mode 100755
index 0000000..7daf723
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ErrorCode.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xls;
+
+class ErrorCode
+{
+    protected static $map = [
+        0x00 => '#NULL!',
+        0x07 => '#DIV/0!',
+        0x0F => '#VALUE!',
+        0x17 => '#REF!',
+        0x1D => '#NAME?',
+        0x24 => '#NUM!',
+        0x2A => '#N/A',
+    ];
+
+    /**
+     * Map error code, e.g. '#N/A'.
+     *
+     * @param int $code
+     *
+     * @return bool|string
+     */
+    public static function lookup($code)
+    {
+        if (isset(self::$map[$code])) {
+            return self::$map[$code];
+        }
+
+        return false;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Escher.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Escher.php
new file mode 100755
index 0000000..306fc8f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Escher.php
@@ -0,0 +1,677 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xls;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Reader\Xls;
+use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer;
+use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer;
+use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer\SpContainer;
+use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer;
+use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer;
+use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE;
+use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE\Blip;
+
+class Escher
+{
+    const DGGCONTAINER = 0xF000;
+    const BSTORECONTAINER = 0xF001;
+    const DGCONTAINER = 0xF002;
+    const SPGRCONTAINER = 0xF003;
+    const SPCONTAINER = 0xF004;
+    const DGG = 0xF006;
+    const BSE = 0xF007;
+    const DG = 0xF008;
+    const SPGR = 0xF009;
+    const SP = 0xF00A;
+    const OPT = 0xF00B;
+    const CLIENTTEXTBOX = 0xF00D;
+    const CLIENTANCHOR = 0xF010;
+    const CLIENTDATA = 0xF011;
+    const BLIPJPEG = 0xF01D;
+    const BLIPPNG = 0xF01E;
+    const SPLITMENUCOLORS = 0xF11E;
+    const TERTIARYOPT = 0xF122;
+
+    /**
+     * Escher stream data (binary).
+     *
+     * @var string
+     */
+    private $data;
+
+    /**
+     * Size in bytes of the Escher stream data.
+     *
+     * @var int
+     */
+    private $dataSize;
+
+    /**
+     * Current position of stream pointer in Escher stream data.
+     *
+     * @var int
+     */
+    private $pos;
+
+    /**
+     * The object to be returned by the reader. Modified during load.
+     *
+     * @var BSE|BstoreContainer|DgContainer|DggContainer|\PhpOffice\PhpSpreadsheet\Shared\Escher|SpContainer|SpgrContainer
+     */
+    private $object;
+
+    /**
+     * Create a new Escher instance.
+     *
+     * @param mixed $object
+     */
+    public function __construct($object)
+    {
+        $this->object = $object;
+    }
+
+    /**
+     * Load Escher stream data. May be a partial Escher stream.
+     *
+     * @param string $data
+     *
+     * @return BSE|BstoreContainer|DgContainer|DggContainer|\PhpOffice\PhpSpreadsheet\Shared\Escher|SpContainer|SpgrContainer
+     */
+    public function load($data)
+    {
+        $this->data = $data;
+
+        // total byte size of Excel data (workbook global substream + sheet substreams)
+        $this->dataSize = strlen($this->data);
+
+        $this->pos = 0;
+
+        // Parse Escher stream
+        while ($this->pos < $this->dataSize) {
+            // offset: 2; size: 2: Record Type
+            $fbt = Xls::getUInt2d($this->data, $this->pos + 2);
+
+            switch ($fbt) {
+                case self::DGGCONTAINER:
+                    $this->readDggContainer();
+
+                    break;
+                case self::DGG:
+                    $this->readDgg();
+
+                    break;
+                case self::BSTORECONTAINER:
+                    $this->readBstoreContainer();
+
+                    break;
+                case self::BSE:
+                    $this->readBSE();
+
+                    break;
+                case self::BLIPJPEG:
+                    $this->readBlipJPEG();
+
+                    break;
+                case self::BLIPPNG:
+                    $this->readBlipPNG();
+
+                    break;
+                case self::OPT:
+                    $this->readOPT();
+
+                    break;
+                case self::TERTIARYOPT:
+                    $this->readTertiaryOPT();
+
+                    break;
+                case self::SPLITMENUCOLORS:
+                    $this->readSplitMenuColors();
+
+                    break;
+                case self::DGCONTAINER:
+                    $this->readDgContainer();
+
+                    break;
+                case self::DG:
+                    $this->readDg();
+
+                    break;
+                case self::SPGRCONTAINER:
+                    $this->readSpgrContainer();
+
+                    break;
+                case self::SPCONTAINER:
+                    $this->readSpContainer();
+
+                    break;
+                case self::SPGR:
+                    $this->readSpgr();
+
+                    break;
+                case self::SP:
+                    $this->readSp();
+
+                    break;
+                case self::CLIENTTEXTBOX:
+                    $this->readClientTextbox();
+
+                    break;
+                case self::CLIENTANCHOR:
+                    $this->readClientAnchor();
+
+                    break;
+                case self::CLIENTDATA:
+                    $this->readClientData();
+
+                    break;
+                default:
+                    $this->readDefault();
+
+                    break;
+            }
+        }
+
+        return $this->object;
+    }
+
+    /**
+     * Read a generic record.
+     */
+    private function readDefault(): void
+    {
+        // offset 0; size: 2; recVer and recInstance
+        $verInstance = Xls::getUInt2d($this->data, $this->pos);
+
+        // offset: 2; size: 2: Record Type
+        $fbt = Xls::getUInt2d($this->data, $this->pos + 2);
+
+        // bit: 0-3; mask: 0x000F; recVer
+        $recVer = (0x000F & $verInstance) >> 0;
+
+        $length = Xls::getInt4d($this->data, $this->pos + 4);
+        $recordData = substr($this->data, $this->pos + 8, $length);
+
+        // move stream pointer to next record
+        $this->pos += 8 + $length;
+    }
+
+    /**
+     * Read DggContainer record (Drawing Group Container).
+     */
+    private function readDggContainer(): void
+    {
+        $length = Xls::getInt4d($this->data, $this->pos + 4);
+        $recordData = substr($this->data, $this->pos + 8, $length);
+
+        // move stream pointer to next record
+        $this->pos += 8 + $length;
+
+        // record is a container, read contents
+        $dggContainer = new DggContainer();
+        $this->object->setDggContainer($dggContainer);
+        $reader = new self($dggContainer);
+        $reader->load($recordData);
+    }
+
+    /**
+     * Read Dgg record (Drawing Group).
+     */
+    private function readDgg(): void
+    {
+        $length = Xls::getInt4d($this->data, $this->pos + 4);
+        $recordData = substr($this->data, $this->pos + 8, $length);
+
+        // move stream pointer to next record
+        $this->pos += 8 + $length;
+    }
+
+    /**
+     * Read BstoreContainer record (Blip Store Container).
+     */
+    private function readBstoreContainer(): void
+    {
+        $length = Xls::getInt4d($this->data, $this->pos + 4);
+        $recordData = substr($this->data, $this->pos + 8, $length);
+
+        // move stream pointer to next record
+        $this->pos += 8 + $length;
+
+        // record is a container, read contents
+        $bstoreContainer = new BstoreContainer();
+        $this->object->setBstoreContainer($bstoreContainer);
+        $reader = new self($bstoreContainer);
+        $reader->load($recordData);
+    }
+
+    /**
+     * Read BSE record.
+     */
+    private function readBSE(): void
+    {
+        // offset: 0; size: 2; recVer and recInstance
+
+        // bit: 4-15; mask: 0xFFF0; recInstance
+        $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
+
+        $length = Xls::getInt4d($this->data, $this->pos + 4);
+        $recordData = substr($this->data, $this->pos + 8, $length);
+
+        // move stream pointer to next record
+        $this->pos += 8 + $length;
+
+        // add BSE to BstoreContainer
+        $BSE = new BSE();
+        $this->object->addBSE($BSE);
+
+        $BSE->setBLIPType($recInstance);
+
+        // offset: 0; size: 1; btWin32 (MSOBLIPTYPE)
+        $btWin32 = ord($recordData[0]);
+
+        // offset: 1; size: 1; btWin32 (MSOBLIPTYPE)
+        $btMacOS = ord($recordData[1]);
+
+        // offset: 2; size: 16; MD4 digest
+        $rgbUid = substr($recordData, 2, 16);
+
+        // offset: 18; size: 2; tag
+        $tag = Xls::getUInt2d($recordData, 18);
+
+        // offset: 20; size: 4; size of BLIP in bytes
+        $size = Xls::getInt4d($recordData, 20);
+
+        // offset: 24; size: 4; number of references to this BLIP
+        $cRef = Xls::getInt4d($recordData, 24);
+
+        // offset: 28; size: 4; MSOFO file offset
+        $foDelay = Xls::getInt4d($recordData, 28);
+
+        // offset: 32; size: 1; unused1
+        $unused1 = ord($recordData[32]);
+
+        // offset: 33; size: 1; size of nameData in bytes (including null terminator)
+        $cbName = ord($recordData[33]);
+
+        // offset: 34; size: 1; unused2
+        $unused2 = ord($recordData[34]);
+
+        // offset: 35; size: 1; unused3
+        $unused3 = ord($recordData[35]);
+
+        // offset: 36; size: $cbName; nameData
+        $nameData = substr($recordData, 36, $cbName);
+
+        // offset: 36 + $cbName, size: var; the BLIP data
+        $blipData = substr($recordData, 36 + $cbName);
+
+        // record is a container, read contents
+        $reader = new self($BSE);
+        $reader->load($blipData);
+    }
+
+    /**
+     * Read BlipJPEG record. Holds raw JPEG image data.
+     */
+    private function readBlipJPEG(): void
+    {
+        // offset: 0; size: 2; recVer and recInstance
+
+        // bit: 4-15; mask: 0xFFF0; recInstance
+        $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
+
+        $length = Xls::getInt4d($this->data, $this->pos + 4);
+        $recordData = substr($this->data, $this->pos + 8, $length);
+
+        // move stream pointer to next record
+        $this->pos += 8 + $length;
+
+        $pos = 0;
+
+        // offset: 0; size: 16; rgbUid1 (MD4 digest of)
+        $rgbUid1 = substr($recordData, 0, 16);
+        $pos += 16;
+
+        // offset: 16; size: 16; rgbUid2 (MD4 digest), only if $recInstance = 0x46B or 0x6E3
+        if (in_array($recInstance, [0x046B, 0x06E3])) {
+            $rgbUid2 = substr($recordData, 16, 16);
+            $pos += 16;
+        }
+
+        // offset: var; size: 1; tag
+        $tag = ord($recordData[$pos]);
+        ++$pos;
+
+        // offset: var; size: var; the raw image data
+        $data = substr($recordData, $pos);
+
+        $blip = new Blip();
+        $blip->setData($data);
+
+        $this->object->setBlip($blip);
+    }
+
+    /**
+     * Read BlipPNG record. Holds raw PNG image data.
+     */
+    private function readBlipPNG(): void
+    {
+        // offset: 0; size: 2; recVer and recInstance
+
+        // bit: 4-15; mask: 0xFFF0; recInstance
+        $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
+
+        $length = Xls::getInt4d($this->data, $this->pos + 4);
+        $recordData = substr($this->data, $this->pos + 8, $length);
+
+        // move stream pointer to next record
+        $this->pos += 8 + $length;
+
+        $pos = 0;
+
+        // offset: 0; size: 16; rgbUid1 (MD4 digest of)
+        $rgbUid1 = substr($recordData, 0, 16);
+        $pos += 16;
+
+        // offset: 16; size: 16; rgbUid2 (MD4 digest), only if $recInstance = 0x46B or 0x6E3
+        if ($recInstance == 0x06E1) {
+            $rgbUid2 = substr($recordData, 16, 16);
+            $pos += 16;
+        }
+
+        // offset: var; size: 1; tag
+        $tag = ord($recordData[$pos]);
+        ++$pos;
+
+        // offset: var; size: var; the raw image data
+        $data = substr($recordData, $pos);
+
+        $blip = new Blip();
+        $blip->setData($data);
+
+        $this->object->setBlip($blip);
+    }
+
+    /**
+     * Read OPT record. This record may occur within DggContainer record or SpContainer.
+     */
+    private function readOPT(): void
+    {
+        // offset: 0; size: 2; recVer and recInstance
+
+        // bit: 4-15; mask: 0xFFF0; recInstance
+        $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
+
+        $length = Xls::getInt4d($this->data, $this->pos + 4);
+        $recordData = substr($this->data, $this->pos + 8, $length);
+
+        // move stream pointer to next record
+        $this->pos += 8 + $length;
+
+        $this->readOfficeArtRGFOPTE($recordData, $recInstance);
+    }
+
+    /**
+     * Read TertiaryOPT record.
+     */
+    private function readTertiaryOPT(): void
+    {
+        // offset: 0; size: 2; recVer and recInstance
+
+        // bit: 4-15; mask: 0xFFF0; recInstance
+        $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
+
+        $length = Xls::getInt4d($this->data, $this->pos + 4);
+        $recordData = substr($this->data, $this->pos + 8, $length);
+
+        // move stream pointer to next record
+        $this->pos += 8 + $length;
+    }
+
+    /**
+     * Read SplitMenuColors record.
+     */
+    private function readSplitMenuColors(): void
+    {
+        $length = Xls::getInt4d($this->data, $this->pos + 4);
+        $recordData = substr($this->data, $this->pos + 8, $length);
+
+        // move stream pointer to next record
+        $this->pos += 8 + $length;
+    }
+
+    /**
+     * Read DgContainer record (Drawing Container).
+     */
+    private function readDgContainer(): void
+    {
+        $length = Xls::getInt4d($this->data, $this->pos + 4);
+        $recordData = substr($this->data, $this->pos + 8, $length);
+
+        // move stream pointer to next record
+        $this->pos += 8 + $length;
+
+        // record is a container, read contents
+        $dgContainer = new DgContainer();
+        $this->object->setDgContainer($dgContainer);
+        $reader = new self($dgContainer);
+        $escher = $reader->load($recordData);
+    }
+
+    /**
+     * Read Dg record (Drawing).
+     */
+    private function readDg(): void
+    {
+        $length = Xls::getInt4d($this->data, $this->pos + 4);
+        $recordData = substr($this->data, $this->pos + 8, $length);
+
+        // move stream pointer to next record
+        $this->pos += 8 + $length;
+    }
+
+    /**
+     * Read SpgrContainer record (Shape Group Container).
+     */
+    private function readSpgrContainer(): void
+    {
+        // context is either context DgContainer or SpgrContainer
+
+        $length = Xls::getInt4d($this->data, $this->pos + 4);
+        $recordData = substr($this->data, $this->pos + 8, $length);
+
+        // move stream pointer to next record
+        $this->pos += 8 + $length;
+
+        // record is a container, read contents
+        $spgrContainer = new SpgrContainer();
+
+        if ($this->object instanceof DgContainer) {
+            // DgContainer
+            $this->object->setSpgrContainer($spgrContainer);
+        } else {
+            // SpgrContainer
+            $this->object->addChild($spgrContainer);
+        }
+
+        $reader = new self($spgrContainer);
+        $escher = $reader->load($recordData);
+    }
+
+    /**
+     * Read SpContainer record (Shape Container).
+     */
+    private function readSpContainer(): void
+    {
+        $length = Xls::getInt4d($this->data, $this->pos + 4);
+        $recordData = substr($this->data, $this->pos + 8, $length);
+
+        // add spContainer to spgrContainer
+        $spContainer = new SpContainer();
+        $this->object->addChild($spContainer);
+
+        // move stream pointer to next record
+        $this->pos += 8 + $length;
+
+        // record is a container, read contents
+        $reader = new self($spContainer);
+        $escher = $reader->load($recordData);
+    }
+
+    /**
+     * Read Spgr record (Shape Group).
+     */
+    private function readSpgr(): void
+    {
+        $length = Xls::getInt4d($this->data, $this->pos + 4);
+        $recordData = substr($this->data, $this->pos + 8, $length);
+
+        // move stream pointer to next record
+        $this->pos += 8 + $length;
+    }
+
+    /**
+     * Read Sp record (Shape).
+     */
+    private function readSp(): void
+    {
+        // offset: 0; size: 2; recVer and recInstance
+
+        // bit: 4-15; mask: 0xFFF0; recInstance
+        $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
+
+        $length = Xls::getInt4d($this->data, $this->pos + 4);
+        $recordData = substr($this->data, $this->pos + 8, $length);
+
+        // move stream pointer to next record
+        $this->pos += 8 + $length;
+    }
+
+    /**
+     * Read ClientTextbox record.
+     */
+    private function readClientTextbox(): void
+    {
+        // offset: 0; size: 2; recVer and recInstance
+
+        // bit: 4-15; mask: 0xFFF0; recInstance
+        $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
+
+        $length = Xls::getInt4d($this->data, $this->pos + 4);
+        $recordData = substr($this->data, $this->pos + 8, $length);
+
+        // move stream pointer to next record
+        $this->pos += 8 + $length;
+    }
+
+    /**
+     * Read ClientAnchor record. This record holds information about where the shape is anchored in worksheet.
+     */
+    private function readClientAnchor(): void
+    {
+        $length = Xls::getInt4d($this->data, $this->pos + 4);
+        $recordData = substr($this->data, $this->pos + 8, $length);
+
+        // move stream pointer to next record
+        $this->pos += 8 + $length;
+
+        // offset: 2; size: 2; upper-left corner column index (0-based)
+        $c1 = Xls::getUInt2d($recordData, 2);
+
+        // offset: 4; size: 2; upper-left corner horizontal offset in 1/1024 of column width
+        $startOffsetX = Xls::getUInt2d($recordData, 4);
+
+        // offset: 6; size: 2; upper-left corner row index (0-based)
+        $r1 = Xls::getUInt2d($recordData, 6);
+
+        // offset: 8; size: 2; upper-left corner vertical offset in 1/256 of row height
+        $startOffsetY = Xls::getUInt2d($recordData, 8);
+
+        // offset: 10; size: 2; bottom-right corner column index (0-based)
+        $c2 = Xls::getUInt2d($recordData, 10);
+
+        // offset: 12; size: 2; bottom-right corner horizontal offset in 1/1024 of column width
+        $endOffsetX = Xls::getUInt2d($recordData, 12);
+
+        // offset: 14; size: 2; bottom-right corner row index (0-based)
+        $r2 = Xls::getUInt2d($recordData, 14);
+
+        // offset: 16; size: 2; bottom-right corner vertical offset in 1/256 of row height
+        $endOffsetY = Xls::getUInt2d($recordData, 16);
+
+        // set the start coordinates
+        $this->object->setStartCoordinates(Coordinate::stringFromColumnIndex($c1 + 1) . ($r1 + 1));
+
+        // set the start offsetX
+        $this->object->setStartOffsetX($startOffsetX);
+
+        // set the start offsetY
+        $this->object->setStartOffsetY($startOffsetY);
+
+        // set the end coordinates
+        $this->object->setEndCoordinates(Coordinate::stringFromColumnIndex($c2 + 1) . ($r2 + 1));
+
+        // set the end offsetX
+        $this->object->setEndOffsetX($endOffsetX);
+
+        // set the end offsetY
+        $this->object->setEndOffsetY($endOffsetY);
+    }
+
+    /**
+     * Read ClientData record.
+     */
+    private function readClientData(): void
+    {
+        $length = Xls::getInt4d($this->data, $this->pos + 4);
+        $recordData = substr($this->data, $this->pos + 8, $length);
+
+        // move stream pointer to next record
+        $this->pos += 8 + $length;
+    }
+
+    /**
+     * Read OfficeArtRGFOPTE table of property-value pairs.
+     *
+     * @param string $data Binary data
+     * @param int $n Number of properties
+     */
+    private function readOfficeArtRGFOPTE($data, $n): void
+    {
+        $splicedComplexData = substr($data, 6 * $n);
+
+        // loop through property-value pairs
+        for ($i = 0; $i < $n; ++$i) {
+            // read 6 bytes at a time
+            $fopte = substr($data, 6 * $i, 6);
+
+            // offset: 0; size: 2; opid
+            $opid = Xls::getUInt2d($fopte, 0);
+
+            // bit: 0-13; mask: 0x3FFF; opid.opid
+            $opidOpid = (0x3FFF & $opid) >> 0;
+
+            // bit: 14; mask 0x4000; 1 = value in op field is BLIP identifier
+            $opidFBid = (0x4000 & $opid) >> 14;
+
+            // bit: 15; mask 0x8000; 1 = this is a complex property, op field specifies size of complex data
+            $opidFComplex = (0x8000 & $opid) >> 15;
+
+            // offset: 2; size: 4; the value for this property
+            $op = Xls::getInt4d($fopte, 2);
+
+            if ($opidFComplex) {
+                $complexData = substr($splicedComplexData, 0, $op);
+                $splicedComplexData = substr($splicedComplexData, $op);
+
+                // we store string value with complex data
+                $value = $complexData;
+            } else {
+                // we store integer value
+                $value = $op;
+            }
+
+            $this->object->setOPT($opidOpid, $value);
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/MD5.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/MD5.php
new file mode 100755
index 0000000..c0417ba
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/MD5.php
@@ -0,0 +1,184 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xls;
+
+class MD5
+{
+    // Context
+    private $a;
+
+    private $b;
+
+    private $c;
+
+    private $d;
+
+    /**
+     * MD5 stream constructor.
+     */
+    public function __construct()
+    {
+        $this->reset();
+    }
+
+    /**
+     * Reset the MD5 stream context.
+     */
+    public function reset(): void
+    {
+        $this->a = 0x67452301;
+        $this->b = 0xEFCDAB89;
+        $this->c = 0x98BADCFE;
+        $this->d = 0x10325476;
+    }
+
+    /**
+     * Get MD5 stream context.
+     *
+     * @return string
+     */
+    public function getContext()
+    {
+        $s = '';
+        foreach (['a', 'b', 'c', 'd'] as $i) {
+            $v = $this->{$i};
+            $s .= chr($v & 0xff);
+            $s .= chr(($v >> 8) & 0xff);
+            $s .= chr(($v >> 16) & 0xff);
+            $s .= chr(($v >> 24) & 0xff);
+        }
+
+        return $s;
+    }
+
+    /**
+     * Add data to context.
+     *
+     * @param string $data Data to add
+     */
+    public function add($data): void
+    {
+        $words = array_values(unpack('V16', $data));
+
+        $A = $this->a;
+        $B = $this->b;
+        $C = $this->c;
+        $D = $this->d;
+
+        $F = ['self', 'f'];
+        $G = ['self', 'g'];
+        $H = ['self', 'h'];
+        $I = ['self', 'i'];
+
+        // ROUND 1
+        self::step($F, $A, $B, $C, $D, $words[0], 7, 0xd76aa478);
+        self::step($F, $D, $A, $B, $C, $words[1], 12, 0xe8c7b756);
+        self::step($F, $C, $D, $A, $B, $words[2], 17, 0x242070db);
+        self::step($F, $B, $C, $D, $A, $words[3], 22, 0xc1bdceee);
+        self::step($F, $A, $B, $C, $D, $words[4], 7, 0xf57c0faf);
+        self::step($F, $D, $A, $B, $C, $words[5], 12, 0x4787c62a);
+        self::step($F, $C, $D, $A, $B, $words[6], 17, 0xa8304613);
+        self::step($F, $B, $C, $D, $A, $words[7], 22, 0xfd469501);
+        self::step($F, $A, $B, $C, $D, $words[8], 7, 0x698098d8);
+        self::step($F, $D, $A, $B, $C, $words[9], 12, 0x8b44f7af);
+        self::step($F, $C, $D, $A, $B, $words[10], 17, 0xffff5bb1);
+        self::step($F, $B, $C, $D, $A, $words[11], 22, 0x895cd7be);
+        self::step($F, $A, $B, $C, $D, $words[12], 7, 0x6b901122);
+        self::step($F, $D, $A, $B, $C, $words[13], 12, 0xfd987193);
+        self::step($F, $C, $D, $A, $B, $words[14], 17, 0xa679438e);
+        self::step($F, $B, $C, $D, $A, $words[15], 22, 0x49b40821);
+
+        // ROUND 2
+        self::step($G, $A, $B, $C, $D, $words[1], 5, 0xf61e2562);
+        self::step($G, $D, $A, $B, $C, $words[6], 9, 0xc040b340);
+        self::step($G, $C, $D, $A, $B, $words[11], 14, 0x265e5a51);
+        self::step($G, $B, $C, $D, $A, $words[0], 20, 0xe9b6c7aa);
+        self::step($G, $A, $B, $C, $D, $words[5], 5, 0xd62f105d);
+        self::step($G, $D, $A, $B, $C, $words[10], 9, 0x02441453);
+        self::step($G, $C, $D, $A, $B, $words[15], 14, 0xd8a1e681);
+        self::step($G, $B, $C, $D, $A, $words[4], 20, 0xe7d3fbc8);
+        self::step($G, $A, $B, $C, $D, $words[9], 5, 0x21e1cde6);
+        self::step($G, $D, $A, $B, $C, $words[14], 9, 0xc33707d6);
+        self::step($G, $C, $D, $A, $B, $words[3], 14, 0xf4d50d87);
+        self::step($G, $B, $C, $D, $A, $words[8], 20, 0x455a14ed);
+        self::step($G, $A, $B, $C, $D, $words[13], 5, 0xa9e3e905);
+        self::step($G, $D, $A, $B, $C, $words[2], 9, 0xfcefa3f8);
+        self::step($G, $C, $D, $A, $B, $words[7], 14, 0x676f02d9);
+        self::step($G, $B, $C, $D, $A, $words[12], 20, 0x8d2a4c8a);
+
+        // ROUND 3
+        self::step($H, $A, $B, $C, $D, $words[5], 4, 0xfffa3942);
+        self::step($H, $D, $A, $B, $C, $words[8], 11, 0x8771f681);
+        self::step($H, $C, $D, $A, $B, $words[11], 16, 0x6d9d6122);
+        self::step($H, $B, $C, $D, $A, $words[14], 23, 0xfde5380c);
+        self::step($H, $A, $B, $C, $D, $words[1], 4, 0xa4beea44);
+        self::step($H, $D, $A, $B, $C, $words[4], 11, 0x4bdecfa9);
+        self::step($H, $C, $D, $A, $B, $words[7], 16, 0xf6bb4b60);
+        self::step($H, $B, $C, $D, $A, $words[10], 23, 0xbebfbc70);
+        self::step($H, $A, $B, $C, $D, $words[13], 4, 0x289b7ec6);
+        self::step($H, $D, $A, $B, $C, $words[0], 11, 0xeaa127fa);
+        self::step($H, $C, $D, $A, $B, $words[3], 16, 0xd4ef3085);
+        self::step($H, $B, $C, $D, $A, $words[6], 23, 0x04881d05);
+        self::step($H, $A, $B, $C, $D, $words[9], 4, 0xd9d4d039);
+        self::step($H, $D, $A, $B, $C, $words[12], 11, 0xe6db99e5);
+        self::step($H, $C, $D, $A, $B, $words[15], 16, 0x1fa27cf8);
+        self::step($H, $B, $C, $D, $A, $words[2], 23, 0xc4ac5665);
+
+        // ROUND 4
+        self::step($I, $A, $B, $C, $D, $words[0], 6, 0xf4292244);
+        self::step($I, $D, $A, $B, $C, $words[7], 10, 0x432aff97);
+        self::step($I, $C, $D, $A, $B, $words[14], 15, 0xab9423a7);
+        self::step($I, $B, $C, $D, $A, $words[5], 21, 0xfc93a039);
+        self::step($I, $A, $B, $C, $D, $words[12], 6, 0x655b59c3);
+        self::step($I, $D, $A, $B, $C, $words[3], 10, 0x8f0ccc92);
+        self::step($I, $C, $D, $A, $B, $words[10], 15, 0xffeff47d);
+        self::step($I, $B, $C, $D, $A, $words[1], 21, 0x85845dd1);
+        self::step($I, $A, $B, $C, $D, $words[8], 6, 0x6fa87e4f);
+        self::step($I, $D, $A, $B, $C, $words[15], 10, 0xfe2ce6e0);
+        self::step($I, $C, $D, $A, $B, $words[6], 15, 0xa3014314);
+        self::step($I, $B, $C, $D, $A, $words[13], 21, 0x4e0811a1);
+        self::step($I, $A, $B, $C, $D, $words[4], 6, 0xf7537e82);
+        self::step($I, $D, $A, $B, $C, $words[11], 10, 0xbd3af235);
+        self::step($I, $C, $D, $A, $B, $words[2], 15, 0x2ad7d2bb);
+        self::step($I, $B, $C, $D, $A, $words[9], 21, 0xeb86d391);
+
+        $this->a = ($this->a + $A) & 0xffffffff;
+        $this->b = ($this->b + $B) & 0xffffffff;
+        $this->c = ($this->c + $C) & 0xffffffff;
+        $this->d = ($this->d + $D) & 0xffffffff;
+    }
+
+    private static function f($X, $Y, $Z)
+    {
+        return ($X & $Y) | ((~$X) & $Z); // X AND Y OR NOT X AND Z
+    }
+
+    private static function g($X, $Y, $Z)
+    {
+        return ($X & $Z) | ($Y & (~$Z)); // X AND Z OR Y AND NOT Z
+    }
+
+    private static function h($X, $Y, $Z)
+    {
+        return $X ^ $Y ^ $Z; // X XOR Y XOR Z
+    }
+
+    private static function i($X, $Y, $Z)
+    {
+        return $Y ^ ($X | (~$Z)); // Y XOR (X OR NOT Z)
+    }
+
+    private static function step($func, &$A, $B, $C, $D, $M, $s, $t): void
+    {
+        $A = ($A + call_user_func($func, $B, $C, $D) + $M + $t) & 0xffffffff;
+        $A = self::rotate($A, $s);
+        $A = ($B + $A) & 0xffffffff;
+    }
+
+    private static function rotate($decimal, $bits)
+    {
+        $binary = str_pad(decbin($decimal), 32, '0', STR_PAD_LEFT);
+
+        return bindec(substr($binary, $bits) . substr($binary, 0, $bits));
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/RC4.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/RC4.php
new file mode 100755
index 0000000..691aca7
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/RC4.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xls;
+
+class RC4
+{
+    // Context
+    protected $s = [];
+
+    protected $i = 0;
+
+    protected $j = 0;
+
+    /**
+     * RC4 stream decryption/encryption constrcutor.
+     *
+     * @param string $key Encryption key/passphrase
+     */
+    public function __construct($key)
+    {
+        $len = strlen($key);
+
+        for ($this->i = 0; $this->i < 256; ++$this->i) {
+            $this->s[$this->i] = $this->i;
+        }
+
+        $this->j = 0;
+        for ($this->i = 0; $this->i < 256; ++$this->i) {
+            $this->j = ($this->j + $this->s[$this->i] + ord($key[$this->i % $len])) % 256;
+            $t = $this->s[$this->i];
+            $this->s[$this->i] = $this->s[$this->j];
+            $this->s[$this->j] = $t;
+        }
+        $this->i = $this->j = 0;
+    }
+
+    /**
+     * Symmetric decryption/encryption function.
+     *
+     * @param string $data Data to encrypt/decrypt
+     *
+     * @return string
+     */
+    public function RC4($data)
+    {
+        $len = strlen($data);
+        for ($c = 0; $c < $len; ++$c) {
+            $this->i = ($this->i + 1) % 256;
+            $this->j = ($this->j + $this->s[$this->i]) % 256;
+            $t = $this->s[$this->i];
+            $this->s[$this->i] = $this->s[$this->j];
+            $this->s[$this->j] = $t;
+
+            $t = ($this->s[$this->i] + $this->s[$this->j]) % 256;
+
+            $data[$c] = chr(ord($data[$c]) ^ $this->s[$t]);
+        }
+
+        return $data;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/Border.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/Border.php
new file mode 100755
index 0000000..91cbe36
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/Border.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xls\Style;
+
+use PhpOffice\PhpSpreadsheet\Style\Border as StyleBorder;
+
+class Border
+{
+    protected static $map = [
+        0x00 => StyleBorder::BORDER_NONE,
+        0x01 => StyleBorder::BORDER_THIN,
+        0x02 => StyleBorder::BORDER_MEDIUM,
+        0x03 => StyleBorder::BORDER_DASHED,
+        0x04 => StyleBorder::BORDER_DOTTED,
+        0x05 => StyleBorder::BORDER_THICK,
+        0x06 => StyleBorder::BORDER_DOUBLE,
+        0x07 => StyleBorder::BORDER_HAIR,
+        0x08 => StyleBorder::BORDER_MEDIUMDASHED,
+        0x09 => StyleBorder::BORDER_DASHDOT,
+        0x0A => StyleBorder::BORDER_MEDIUMDASHDOT,
+        0x0B => StyleBorder::BORDER_DASHDOTDOT,
+        0x0C => StyleBorder::BORDER_MEDIUMDASHDOTDOT,
+        0x0D => StyleBorder::BORDER_SLANTDASHDOT,
+    ];
+
+    /**
+     * Map border style
+     * OpenOffice documentation: 2.5.11.
+     *
+     * @param int $index
+     *
+     * @return string
+     */
+    public static function lookup($index)
+    {
+        if (isset(self::$map[$index])) {
+            return self::$map[$index];
+        }
+
+        return StyleBorder::BORDER_NONE;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/FillPattern.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/FillPattern.php
new file mode 100755
index 0000000..7b85c08
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/FillPattern.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xls\Style;
+
+use PhpOffice\PhpSpreadsheet\Style\Fill;
+
+class FillPattern
+{
+    protected static $map = [
+        0x00 => Fill::FILL_NONE,
+        0x01 => Fill::FILL_SOLID,
+        0x02 => Fill::FILL_PATTERN_MEDIUMGRAY,
+        0x03 => Fill::FILL_PATTERN_DARKGRAY,
+        0x04 => Fill::FILL_PATTERN_LIGHTGRAY,
+        0x05 => Fill::FILL_PATTERN_DARKHORIZONTAL,
+        0x06 => Fill::FILL_PATTERN_DARKVERTICAL,
+        0x07 => Fill::FILL_PATTERN_DARKDOWN,
+        0x08 => Fill::FILL_PATTERN_DARKUP,
+        0x09 => Fill::FILL_PATTERN_DARKGRID,
+        0x0A => Fill::FILL_PATTERN_DARKTRELLIS,
+        0x0B => Fill::FILL_PATTERN_LIGHTHORIZONTAL,
+        0x0C => Fill::FILL_PATTERN_LIGHTVERTICAL,
+        0x0D => Fill::FILL_PATTERN_LIGHTDOWN,
+        0x0E => Fill::FILL_PATTERN_LIGHTUP,
+        0x0F => Fill::FILL_PATTERN_LIGHTGRID,
+        0x10 => Fill::FILL_PATTERN_LIGHTTRELLIS,
+        0x11 => Fill::FILL_PATTERN_GRAY125,
+        0x12 => Fill::FILL_PATTERN_GRAY0625,
+    ];
+
+    /**
+     * Get fill pattern from index
+     * OpenOffice documentation: 2.5.12.
+     *
+     * @param int $index
+     *
+     * @return string
+     */
+    public static function lookup($index)
+    {
+        if (isset(self::$map[$index])) {
+            return self::$map[$index];
+        }
+
+        return Fill::FILL_NONE;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php
new file mode 100755
index 0000000..fb7f064
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php
@@ -0,0 +1,2054 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Cell\Hyperlink;
+use PhpOffice\PhpSpreadsheet\NamedRange;
+use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx\AutoFilter;
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Chart;
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx\ColumnAndRowAttributes;
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx\ConditionalStyles;
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx\DataValidations;
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Hyperlinks;
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx\PageSetup;
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Properties as PropertyReader;
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx\SheetViewOptions;
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx\SheetViews;
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Styles;
+use PhpOffice\PhpSpreadsheet\ReferenceHelper;
+use PhpOffice\PhpSpreadsheet\RichText\RichText;
+use PhpOffice\PhpSpreadsheet\Settings;
+use PhpOffice\PhpSpreadsheet\Shared\Date;
+use PhpOffice\PhpSpreadsheet\Shared\Drawing;
+use PhpOffice\PhpSpreadsheet\Shared\File;
+use PhpOffice\PhpSpreadsheet\Shared\Font;
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Style\Border;
+use PhpOffice\PhpSpreadsheet\Style\Borders;
+use PhpOffice\PhpSpreadsheet\Style\Color;
+use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
+use PhpOffice\PhpSpreadsheet\Style\Protection;
+use PhpOffice\PhpSpreadsheet\Style\Style;
+use PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooterDrawing;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use SimpleXMLElement;
+use stdClass;
+use Throwable;
+use XMLReader;
+use ZipArchive;
+
+class Xlsx extends BaseReader
+{
+    /**
+     * ReferenceHelper instance.
+     *
+     * @var ReferenceHelper
+     */
+    private $referenceHelper;
+
+    /**
+     * Xlsx\Theme instance.
+     *
+     * @var Xlsx\Theme
+     */
+    private static $theme = null;
+
+    /**
+     * Create a new Xlsx Reader instance.
+     */
+    public function __construct()
+    {
+        parent::__construct();
+        $this->referenceHelper = ReferenceHelper::getInstance();
+        $this->securityScanner = XmlScanner::getInstance($this);
+    }
+
+    /**
+     * Can the current IReader read the file?
+     *
+     * @param string $pFilename
+     *
+     * @return bool
+     */
+    public function canRead($pFilename)
+    {
+        File::assertFile($pFilename);
+
+        $result = false;
+        $zip = new ZipArchive();
+
+        if ($zip->open($pFilename) === true) {
+            $workbookBasename = $this->getWorkbookBaseName($zip);
+            $result = !empty($workbookBasename);
+
+            $zip->close();
+        }
+
+        return $result;
+    }
+
+    /**
+     * Reads names of the worksheets from a file, without parsing the whole file to a Spreadsheet object.
+     *
+     * @param string $pFilename
+     *
+     * @return array
+     */
+    public function listWorksheetNames($pFilename)
+    {
+        File::assertFile($pFilename);
+
+        $worksheetNames = [];
+
+        $zip = new ZipArchive();
+        $zip->open($pFilename);
+
+        //    The files we're looking at here are small enough that simpleXML is more efficient than XMLReader
+        //~ http://schemas.openxmlformats.org/package/2006/relationships");
+        $rels = simplexml_load_string(
+            $this->securityScanner->scan($this->getFromZipArchive($zip, '_rels/.rels'))
+        );
+        foreach ($rels->Relationship as $rel) {
+            switch ($rel['Type']) {
+                case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument':
+                    //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
+                    $xmlWorkbook = simplexml_load_string(
+                        $this->securityScanner->scan($this->getFromZipArchive($zip, "{$rel['Target']}"))
+                    );
+
+                    if ($xmlWorkbook->sheets) {
+                        foreach ($xmlWorkbook->sheets->sheet as $eleSheet) {
+                            // Check if sheet should be skipped
+                            $worksheetNames[] = (string) $eleSheet['name'];
+                        }
+                    }
+            }
+        }
+
+        $zip->close();
+
+        return $worksheetNames;
+    }
+
+    /**
+     * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
+     *
+     * @param string $pFilename
+     *
+     * @return array
+     */
+    public function listWorksheetInfo($pFilename)
+    {
+        File::assertFile($pFilename);
+
+        $worksheetInfo = [];
+
+        $zip = new ZipArchive();
+        $zip->open($pFilename);
+
+        //~ http://schemas.openxmlformats.org/package/2006/relationships"
+        $rels = simplexml_load_string(
+            $this->securityScanner->scan($this->getFromZipArchive($zip, '_rels/.rels')),
+            'SimpleXMLElement',
+            Settings::getLibXmlLoaderOptions()
+        );
+        foreach ($rels->Relationship as $rel) {
+            if ($rel['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument') {
+                $dir = dirname($rel['Target']);
+
+                //~ http://schemas.openxmlformats.org/package/2006/relationships"
+                $relsWorkbook = simplexml_load_string(
+                    $this->securityScanner->scan(
+                        $this->getFromZipArchive($zip, "$dir/_rels/" . basename($rel['Target']) . '.rels')
+                    ),
+                    'SimpleXMLElement',
+                    Settings::getLibXmlLoaderOptions()
+                );
+                $relsWorkbook->registerXPathNamespace('rel', 'http://schemas.openxmlformats.org/package/2006/relationships');
+
+                $worksheets = [];
+                foreach ($relsWorkbook->Relationship as $ele) {
+                    if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet') {
+                        $worksheets[(string) $ele['Id']] = $ele['Target'];
+                    }
+                }
+
+                //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
+                $xmlWorkbook = simplexml_load_string(
+                    $this->securityScanner->scan(
+                        $this->getFromZipArchive($zip, "{$rel['Target']}")
+                    ),
+                    'SimpleXMLElement',
+                    Settings::getLibXmlLoaderOptions()
+                );
+                if ($xmlWorkbook->sheets) {
+                    $dir = dirname($rel['Target']);
+                    /** @var SimpleXMLElement $eleSheet */
+                    foreach ($xmlWorkbook->sheets->sheet as $eleSheet) {
+                        $tmpInfo = [
+                            'worksheetName' => (string) $eleSheet['name'],
+                            'lastColumnLetter' => 'A',
+                            'lastColumnIndex' => 0,
+                            'totalRows' => 0,
+                            'totalColumns' => 0,
+                        ];
+
+                        $fileWorksheet = $worksheets[(string) self::getArrayItem($eleSheet->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'), 'id')];
+
+                        $xml = new XMLReader();
+                        $xml->xml(
+                            $this->securityScanner->scanFile(
+                                'zip://' . File::realpath($pFilename) . '#' . "$dir/$fileWorksheet"
+                            ),
+                            null,
+                            Settings::getLibXmlLoaderOptions()
+                        );
+                        $xml->setParserProperty(2, true);
+
+                        $currCells = 0;
+                        while ($xml->read()) {
+                            if ($xml->name == 'row' && $xml->nodeType == XMLReader::ELEMENT) {
+                                $row = $xml->getAttribute('r');
+                                $tmpInfo['totalRows'] = $row;
+                                $tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells);
+                                $currCells = 0;
+                            } elseif ($xml->name == 'c' && $xml->nodeType == XMLReader::ELEMENT) {
+                                ++$currCells;
+                            }
+                        }
+                        $tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells);
+                        $xml->close();
+
+                        $tmpInfo['lastColumnIndex'] = $tmpInfo['totalColumns'] - 1;
+                        $tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1);
+
+                        $worksheetInfo[] = $tmpInfo;
+                    }
+                }
+            }
+        }
+
+        $zip->close();
+
+        return $worksheetInfo;
+    }
+
+    private static function castToBoolean($c)
+    {
+        $value = isset($c->v) ? (string) $c->v : null;
+        if ($value == '0') {
+            return false;
+        } elseif ($value == '1') {
+            return true;
+        }
+
+        return (bool) $c->v;
+    }
+
+    private static function castToError($c)
+    {
+        return isset($c->v) ? (string) $c->v : null;
+    }
+
+    private static function castToString($c)
+    {
+        return isset($c->v) ? (string) $c->v : null;
+    }
+
+    private function castToFormula($c, $r, &$cellDataType, &$value, &$calculatedValue, &$sharedFormulas, $castBaseType): void
+    {
+        $cellDataType = 'f';
+        $value = "={$c->f}";
+        $calculatedValue = self::$castBaseType($c);
+
+        // Shared formula?
+        if (isset($c->f['t']) && strtolower((string) $c->f['t']) == 'shared') {
+            $instance = (string) $c->f['si'];
+
+            if (!isset($sharedFormulas[(string) $c->f['si']])) {
+                $sharedFormulas[$instance] = ['master' => $r, 'formula' => $value];
+            } else {
+                $master = Coordinate::coordinateFromString($sharedFormulas[$instance]['master']);
+                $current = Coordinate::coordinateFromString($r);
+
+                $difference = [0, 0];
+                $difference[0] = Coordinate::columnIndexFromString($current[0]) - Coordinate::columnIndexFromString($master[0]);
+                $difference[1] = $current[1] - $master[1];
+
+                $value = $this->referenceHelper->updateFormulaReferences($sharedFormulas[$instance]['formula'], 'A1', $difference[0], $difference[1]);
+            }
+        }
+    }
+
+    /**
+     * @param string $fileName
+     *
+     * @return string
+     */
+    private function getFromZipArchive(ZipArchive $archive, $fileName = '')
+    {
+        // Root-relative paths
+        if (strpos($fileName, '//') !== false) {
+            $fileName = substr($fileName, strpos($fileName, '//') + 1);
+        }
+        $fileName = File::realpath($fileName);
+
+        // Sadly, some 3rd party xlsx generators don't use consistent case for filenaming
+        //    so we need to load case-insensitively from the zip file
+
+        // Apache POI fixes
+        $contents = $archive->getFromName($fileName, 0, ZipArchive::FL_NOCASE);
+        if ($contents === false) {
+            $contents = $archive->getFromName(substr($fileName, 1), 0, ZipArchive::FL_NOCASE);
+        }
+
+        return $contents;
+    }
+
+    /**
+     * Loads Spreadsheet from file.
+     *
+     * @param string $pFilename
+     *
+     * @return Spreadsheet
+     */
+    public function load($pFilename)
+    {
+        File::assertFile($pFilename);
+
+        // Initialisations
+        $excel = new Spreadsheet();
+        $excel->removeSheetByIndex(0);
+        if (!$this->readDataOnly) {
+            $excel->removeCellStyleXfByIndex(0); // remove the default style
+            $excel->removeCellXfByIndex(0); // remove the default style
+        }
+        $unparsedLoadedData = [];
+
+        $zip = new ZipArchive();
+        $zip->open($pFilename);
+
+        //    Read the theme first, because we need the colour scheme when reading the styles
+        //~ http://schemas.openxmlformats.org/package/2006/relationships"
+        $workbookBasename = $this->getWorkbookBaseName($zip);
+        $wbRels = simplexml_load_string(
+            $this->securityScanner->scan($this->getFromZipArchive($zip, "xl/_rels/${workbookBasename}.rels")),
+            'SimpleXMLElement',
+            Settings::getLibXmlLoaderOptions()
+        );
+        foreach ($wbRels->Relationship as $rel) {
+            switch ($rel['Type']) {
+                case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme':
+                    $themeOrderArray = ['lt1', 'dk1', 'lt2', 'dk2'];
+                    $themeOrderAdditional = count($themeOrderArray);
+
+                    $xmlTheme = simplexml_load_string(
+                        $this->securityScanner->scan($this->getFromZipArchive($zip, "xl/{$rel['Target']}")),
+                        'SimpleXMLElement',
+                        Settings::getLibXmlLoaderOptions()
+                    );
+                    if (is_object($xmlTheme)) {
+                        $xmlThemeName = $xmlTheme->attributes();
+                        $xmlTheme = $xmlTheme->children('http://schemas.openxmlformats.org/drawingml/2006/main');
+                        $themeName = (string) $xmlThemeName['name'];
+
+                        $colourScheme = $xmlTheme->themeElements->clrScheme->attributes();
+                        $colourSchemeName = (string) $colourScheme['name'];
+                        $colourScheme = $xmlTheme->themeElements->clrScheme->children('http://schemas.openxmlformats.org/drawingml/2006/main');
+
+                        $themeColours = [];
+                        foreach ($colourScheme as $k => $xmlColour) {
+                            $themePos = array_search($k, $themeOrderArray);
+                            if ($themePos === false) {
+                                $themePos = $themeOrderAdditional++;
+                            }
+                            if (isset($xmlColour->sysClr)) {
+                                $xmlColourData = $xmlColour->sysClr->attributes();
+                                $themeColours[$themePos] = $xmlColourData['lastClr'];
+                            } elseif (isset($xmlColour->srgbClr)) {
+                                $xmlColourData = $xmlColour->srgbClr->attributes();
+                                $themeColours[$themePos] = $xmlColourData['val'];
+                            }
+                        }
+                        self::$theme = new Xlsx\Theme($themeName, $colourSchemeName, $themeColours);
+                    }
+
+                    break;
+            }
+        }
+
+        //~ http://schemas.openxmlformats.org/package/2006/relationships"
+        $rels = simplexml_load_string(
+            $this->securityScanner->scan($this->getFromZipArchive($zip, '_rels/.rels')),
+            'SimpleXMLElement',
+            Settings::getLibXmlLoaderOptions()
+        );
+
+        $propertyReader = new PropertyReader($this->securityScanner, $excel->getProperties());
+        foreach ($rels->Relationship as $rel) {
+            switch ($rel['Type']) {
+                case 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties':
+                    $propertyReader->readCoreProperties($this->getFromZipArchive($zip, "{$rel['Target']}"));
+
+                    break;
+                case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties':
+                    $propertyReader->readExtendedProperties($this->getFromZipArchive($zip, "{$rel['Target']}"));
+
+                    break;
+                case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties':
+                    $propertyReader->readCustomProperties($this->getFromZipArchive($zip, "{$rel['Target']}"));
+
+                    break;
+                //Ribbon
+                case 'http://schemas.microsoft.com/office/2006/relationships/ui/extensibility':
+                    $customUI = $rel['Target'];
+                    if ($customUI !== null) {
+                        $this->readRibbon($excel, $customUI, $zip);
+                    }
+
+                    break;
+                case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument':
+                    $dir = dirname($rel['Target']);
+                    //~ http://schemas.openxmlformats.org/package/2006/relationships"
+                    $relsWorkbook = simplexml_load_string(
+                        $this->securityScanner->scan($this->getFromZipArchive($zip, "$dir/_rels/" . basename($rel['Target']) . '.rels')),
+                        'SimpleXMLElement',
+                        Settings::getLibXmlLoaderOptions()
+                    );
+                    $relsWorkbook->registerXPathNamespace('rel', 'http://schemas.openxmlformats.org/package/2006/relationships');
+
+                    $sharedStrings = [];
+                    $xpath = self::getArrayItem($relsWorkbook->xpath("rel:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings']"));
+                    if ($xpath) {
+                        //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
+                        $xmlStrings = simplexml_load_string(
+                            $this->securityScanner->scan($this->getFromZipArchive($zip, "$dir/$xpath[Target]")),
+                            'SimpleXMLElement',
+                            Settings::getLibXmlLoaderOptions()
+                        );
+                        if (isset($xmlStrings, $xmlStrings->si)) {
+                            foreach ($xmlStrings->si as $val) {
+                                if (isset($val->t)) {
+                                    $sharedStrings[] = StringHelper::controlCharacterOOXML2PHP((string) $val->t);
+                                } elseif (isset($val->r)) {
+                                    $sharedStrings[] = $this->parseRichText($val);
+                                }
+                            }
+                        }
+                    }
+
+                    $worksheets = [];
+                    $macros = $customUI = null;
+                    foreach ($relsWorkbook->Relationship as $ele) {
+                        switch ($ele['Type']) {
+                            case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet':
+                                $worksheets[(string) $ele['Id']] = $ele['Target'];
+
+                                break;
+                            // a vbaProject ? (: some macros)
+                            case 'http://schemas.microsoft.com/office/2006/relationships/vbaProject':
+                                $macros = $ele['Target'];
+
+                                break;
+                        }
+                    }
+
+                    if ($macros !== null) {
+                        $macrosCode = $this->getFromZipArchive($zip, 'xl/vbaProject.bin'); //vbaProject.bin always in 'xl' dir and always named vbaProject.bin
+                        if ($macrosCode !== false) {
+                            $excel->setMacrosCode($macrosCode);
+                            $excel->setHasMacros(true);
+                            //short-circuit : not reading vbaProject.bin.rel to get Signature =>allways vbaProjectSignature.bin in 'xl' dir
+                            $Certificate = $this->getFromZipArchive($zip, 'xl/vbaProjectSignature.bin');
+                            if ($Certificate !== false) {
+                                $excel->setMacrosCertificate($Certificate);
+                            }
+                        }
+                    }
+
+                    $xpath = self::getArrayItem($relsWorkbook->xpath("rel:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles']"));
+                    //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
+                    $xmlStyles = simplexml_load_string(
+                        $this->securityScanner->scan($this->getFromZipArchive($zip, "$dir/$xpath[Target]")),
+                        'SimpleXMLElement',
+                        Settings::getLibXmlLoaderOptions()
+                    );
+
+                    $styles = [];
+                    $cellStyles = [];
+                    $numFmts = null;
+                    if ($xmlStyles && $xmlStyles->numFmts[0]) {
+                        $numFmts = $xmlStyles->numFmts[0];
+                    }
+                    if (isset($numFmts) && ($numFmts !== null)) {
+                        $numFmts->registerXPathNamespace('sml', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
+                    }
+                    if (!$this->readDataOnly && $xmlStyles) {
+                        foreach ($xmlStyles->cellXfs->xf as $xf) {
+                            $numFmt = NumberFormat::FORMAT_GENERAL;
+
+                            if ($xf['numFmtId']) {
+                                if (isset($numFmts)) {
+                                    $tmpNumFmt = self::getArrayItem($numFmts->xpath("sml:numFmt[@numFmtId=$xf[numFmtId]]"));
+
+                                    if (isset($tmpNumFmt['formatCode'])) {
+                                        $numFmt = (string) $tmpNumFmt['formatCode'];
+                                    }
+                                }
+
+                                // We shouldn't override any of the built-in MS Excel values (values below id 164)
+                                //  But there's a lot of naughty homebrew xlsx writers that do use "reserved" id values that aren't actually used
+                                //  So we make allowance for them rather than lose formatting masks
+                                if ((int) $xf['numFmtId'] < 164 &&
+                                    NumberFormat::builtInFormatCode((int) $xf['numFmtId']) !== '') {
+                                    $numFmt = NumberFormat::builtInFormatCode((int) $xf['numFmtId']);
+                                }
+                            }
+                            $quotePrefix = false;
+                            if (isset($xf['quotePrefix'])) {
+                                $quotePrefix = (bool) $xf['quotePrefix'];
+                            }
+
+                            $style = (object) [
+                                'numFmt' => $numFmt,
+                                'font' => $xmlStyles->fonts->font[(int) ($xf['fontId'])],
+                                'fill' => $xmlStyles->fills->fill[(int) ($xf['fillId'])],
+                                'border' => $xmlStyles->borders->border[(int) ($xf['borderId'])],
+                                'alignment' => $xf->alignment,
+                                'protection' => $xf->protection,
+                                'quotePrefix' => $quotePrefix,
+                            ];
+                            $styles[] = $style;
+
+                            // add style to cellXf collection
+                            $objStyle = new Style();
+                            self::readStyle($objStyle, $style);
+                            $excel->addCellXf($objStyle);
+                        }
+
+                        foreach (isset($xmlStyles->cellStyleXfs->xf) ? $xmlStyles->cellStyleXfs->xf : [] as $xf) {
+                            $numFmt = NumberFormat::FORMAT_GENERAL;
+                            if ($numFmts && $xf['numFmtId']) {
+                                $tmpNumFmt = self::getArrayItem($numFmts->xpath("sml:numFmt[@numFmtId=$xf[numFmtId]]"));
+                                if (isset($tmpNumFmt['formatCode'])) {
+                                    $numFmt = (string) $tmpNumFmt['formatCode'];
+                                } elseif ((int) $xf['numFmtId'] < 165) {
+                                    $numFmt = NumberFormat::builtInFormatCode((int) $xf['numFmtId']);
+                                }
+                            }
+
+                            $cellStyle = (object) [
+                                'numFmt' => $numFmt,
+                                'font' => $xmlStyles->fonts->font[(int) ($xf['fontId'])],
+                                'fill' => $xmlStyles->fills->fill[(int) ($xf['fillId'])],
+                                'border' => $xmlStyles->borders->border[(int) ($xf['borderId'])],
+                                'alignment' => $xf->alignment,
+                                'protection' => $xf->protection,
+                                'quotePrefix' => $quotePrefix,
+                            ];
+                            $cellStyles[] = $cellStyle;
+
+                            // add style to cellStyleXf collection
+                            $objStyle = new Style();
+                            self::readStyle($objStyle, $cellStyle);
+                            $excel->addCellStyleXf($objStyle);
+                        }
+                    }
+
+                    $styleReader = new Styles($xmlStyles);
+                    $styleReader->setStyleBaseData(self::$theme, $styles, $cellStyles);
+                    $dxfs = $styleReader->dxfs($this->readDataOnly);
+                    $styles = $styleReader->styles();
+
+                    //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
+                    $xmlWorkbook = simplexml_load_string(
+                        $this->securityScanner->scan($this->getFromZipArchive($zip, "{$rel['Target']}")),
+                        'SimpleXMLElement',
+                        Settings::getLibXmlLoaderOptions()
+                    );
+
+                    // Set base date
+                    if ($xmlWorkbook->workbookPr) {
+                        Date::setExcelCalendar(Date::CALENDAR_WINDOWS_1900);
+                        if (isset($xmlWorkbook->workbookPr['date1904'])) {
+                            if (self::boolean((string) $xmlWorkbook->workbookPr['date1904'])) {
+                                Date::setExcelCalendar(Date::CALENDAR_MAC_1904);
+                            }
+                        }
+                    }
+
+                    // Set protection
+                    $this->readProtection($excel, $xmlWorkbook);
+
+                    $sheetId = 0; // keep track of new sheet id in final workbook
+                    $oldSheetId = -1; // keep track of old sheet id in final workbook
+                    $countSkippedSheets = 0; // keep track of number of skipped sheets
+                    $mapSheetId = []; // mapping of sheet ids from old to new
+
+                    $charts = $chartDetails = [];
+
+                    if ($xmlWorkbook->sheets) {
+                        /** @var SimpleXMLElement $eleSheet */
+                        foreach ($xmlWorkbook->sheets->sheet as $eleSheet) {
+                            ++$oldSheetId;
+
+                            // Check if sheet should be skipped
+                            if (isset($this->loadSheetsOnly) && !in_array((string) $eleSheet['name'], $this->loadSheetsOnly)) {
+                                ++$countSkippedSheets;
+                                $mapSheetId[$oldSheetId] = null;
+
+                                continue;
+                            }
+
+                            // Map old sheet id in original workbook to new sheet id.
+                            // They will differ if loadSheetsOnly() is being used
+                            $mapSheetId[$oldSheetId] = $oldSheetId - $countSkippedSheets;
+
+                            // Load sheet
+                            $docSheet = $excel->createSheet();
+                            //    Use false for $updateFormulaCellReferences to prevent adjustment of worksheet
+                            //        references in formula cells... during the load, all formulae should be correct,
+                            //        and we're simply bringing the worksheet name in line with the formula, not the
+                            //        reverse
+                            $docSheet->setTitle((string) $eleSheet['name'], false, false);
+                            $fileWorksheet = $worksheets[(string) self::getArrayItem($eleSheet->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'), 'id')];
+                            //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
+                            $xmlSheet = simplexml_load_string(
+                                $this->securityScanner->scan($this->getFromZipArchive($zip, "$dir/$fileWorksheet")),
+                                'SimpleXMLElement',
+                                Settings::getLibXmlLoaderOptions()
+                            );
+
+                            $sharedFormulas = [];
+
+                            if (isset($eleSheet['state']) && (string) $eleSheet['state'] != '') {
+                                $docSheet->setSheetState((string) $eleSheet['state']);
+                            }
+
+                            if ($xmlSheet) {
+                                if (isset($xmlSheet->sheetViews, $xmlSheet->sheetViews->sheetView)) {
+                                    $sheetViews = new SheetViews($xmlSheet->sheetViews->sheetView, $docSheet);
+                                    $sheetViews->load();
+                                }
+
+                                $sheetViewOptions = new SheetViewOptions($docSheet, $xmlSheet);
+                                $sheetViewOptions->load($this->getReadDataOnly());
+
+                                (new ColumnAndRowAttributes($docSheet, $xmlSheet))
+                                    ->load($this->getReadFilter(), $this->getReadDataOnly());
+                            }
+
+                            if ($xmlSheet && $xmlSheet->sheetData && $xmlSheet->sheetData->row) {
+                                $cIndex = 1; // Cell Start from 1
+                                foreach ($xmlSheet->sheetData->row as $row) {
+                                    $rowIndex = 1;
+                                    foreach ($row->c as $c) {
+                                        $r = (string) $c['r'];
+                                        if ($r == '') {
+                                            $r = Coordinate::stringFromColumnIndex($rowIndex) . $cIndex;
+                                        }
+                                        $cellDataType = (string) $c['t'];
+                                        $value = null;
+                                        $calculatedValue = null;
+
+                                        // Read cell?
+                                        if ($this->getReadFilter() !== null) {
+                                            $coordinates = Coordinate::coordinateFromString($r);
+
+                                            if (!$this->getReadFilter()->readCell($coordinates[0], (int) $coordinates[1], $docSheet->getTitle())) {
+                                                ++$rowIndex;
+
+                                                continue;
+                                            }
+                                        }
+
+                                        // Read cell!
+                                        switch ($cellDataType) {
+                                            case 's':
+                                                if ((string) $c->v != '') {
+                                                    $value = $sharedStrings[(int) ($c->v)];
+
+                                                    if ($value instanceof RichText) {
+                                                        $value = clone $value;
+                                                    }
+                                                } else {
+                                                    $value = '';
+                                                }
+
+                                                break;
+                                            case 'b':
+                                                if (!isset($c->f)) {
+                                                    $value = self::castToBoolean($c);
+                                                } else {
+                                                    // Formula
+                                                    $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, $sharedFormulas, 'castToBoolean');
+                                                    if (isset($c->f['t'])) {
+                                                        $att = $c->f;
+                                                        $docSheet->getCell($r)->setFormulaAttributes($att);
+                                                    }
+                                                }
+
+                                                break;
+                                            case 'inlineStr':
+                                                if (isset($c->f)) {
+                                                    $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, $sharedFormulas, 'castToError');
+                                                } else {
+                                                    $value = $this->parseRichText($c->is);
+                                                }
+
+                                                break;
+                                            case 'e':
+                                                if (!isset($c->f)) {
+                                                    $value = self::castToError($c);
+                                                } else {
+                                                    // Formula
+                                                    $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, $sharedFormulas, 'castToError');
+                                                }
+
+                                                break;
+                                            default:
+                                                if (!isset($c->f)) {
+                                                    $value = self::castToString($c);
+                                                } else {
+                                                    // Formula
+                                                    $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, $sharedFormulas, 'castToString');
+                                                }
+
+                                                break;
+                                        }
+
+                                        // read empty cells or the cells are not empty
+                                        if ($this->readEmptyCells || ($value !== null && $value !== '')) {
+                                            // Rich text?
+                                            if ($value instanceof RichText && $this->readDataOnly) {
+                                                $value = $value->getPlainText();
+                                            }
+
+                                            $cell = $docSheet->getCell($r);
+                                            // Assign value
+                                            if ($cellDataType != '') {
+                                                $cell->setValueExplicit($value, $cellDataType);
+                                            } else {
+                                                $cell->setValue($value);
+                                            }
+                                            if ($calculatedValue !== null) {
+                                                $cell->setCalculatedValue($calculatedValue);
+                                            }
+
+                                            // Style information?
+                                            if ($c['s'] && !$this->readDataOnly) {
+                                                // no style index means 0, it seems
+                                                $cell->setXfIndex(isset($styles[(int) ($c['s'])]) ?
+                                                    (int) ($c['s']) : 0);
+                                            }
+                                        }
+                                        ++$rowIndex;
+                                    }
+                                    ++$cIndex;
+                                }
+                            }
+
+                            if (!$this->readDataOnly && $xmlSheet && $xmlSheet->conditionalFormatting) {
+                                (new ConditionalStyles($docSheet, $xmlSheet, $dxfs))->load();
+                            }
+
+                            $aKeys = ['sheet', 'objects', 'scenarios', 'formatCells', 'formatColumns', 'formatRows', 'insertColumns', 'insertRows', 'insertHyperlinks', 'deleteColumns', 'deleteRows', 'selectLockedCells', 'sort', 'autoFilter', 'pivotTables', 'selectUnlockedCells'];
+                            if (!$this->readDataOnly && $xmlSheet && $xmlSheet->sheetProtection) {
+                                foreach ($aKeys as $key) {
+                                    $method = 'set' . ucfirst($key);
+                                    $docSheet->getProtection()->$method(self::boolean((string) $xmlSheet->sheetProtection[$key]));
+                                }
+                            }
+
+                            if ($xmlSheet) {
+                                $this->readSheetProtection($docSheet, $xmlSheet);
+                            }
+
+                            if ($xmlSheet && $xmlSheet->autoFilter && !$this->readDataOnly) {
+                                (new AutoFilter($docSheet, $xmlSheet))->load();
+                            }
+
+                            if ($xmlSheet && $xmlSheet->mergeCells && $xmlSheet->mergeCells->mergeCell && !$this->readDataOnly) {
+                                foreach ($xmlSheet->mergeCells->mergeCell as $mergeCell) {
+                                    $mergeRef = (string) $mergeCell['ref'];
+                                    if (strpos($mergeRef, ':') !== false) {
+                                        $docSheet->mergeCells((string) $mergeCell['ref']);
+                                    }
+                                }
+                            }
+
+                            if ($xmlSheet && !$this->readDataOnly) {
+                                $unparsedLoadedData = (new PageSetup($docSheet, $xmlSheet))->load($unparsedLoadedData);
+                            }
+
+                            if ($xmlSheet && $xmlSheet->dataValidations && !$this->readDataOnly) {
+                                (new DataValidations($docSheet, $xmlSheet))->load();
+                            }
+
+                            // unparsed sheet AlternateContent
+                            if ($xmlSheet && !$this->readDataOnly) {
+                                $mc = $xmlSheet->children('http://schemas.openxmlformats.org/markup-compatibility/2006');
+                                if ($mc->AlternateContent) {
+                                    foreach ($mc->AlternateContent as $alternateContent) {
+                                        $unparsedLoadedData['sheets'][$docSheet->getCodeName()]['AlternateContents'][] = $alternateContent->asXML();
+                                    }
+                                }
+                            }
+
+                            // Add hyperlinks
+                            if (!$this->readDataOnly) {
+                                $hyperlinkReader = new Hyperlinks($docSheet);
+                                // Locate hyperlink relations
+                                $relationsFileName = dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels';
+                                if ($zip->locateName($relationsFileName)) {
+                                    //~ http://schemas.openxmlformats.org/package/2006/relationships"
+                                    $relsWorksheet = simplexml_load_string(
+                                        $this->securityScanner->scan(
+                                            $this->getFromZipArchive($zip, $relationsFileName)
+                                        ),
+                                        'SimpleXMLElement',
+                                        Settings::getLibXmlLoaderOptions()
+                                    );
+                                    $hyperlinkReader->readHyperlinks($relsWorksheet);
+                                }
+
+                                // Loop through hyperlinks
+                                if ($xmlSheet && $xmlSheet->hyperlinks) {
+                                    $hyperlinkReader->setHyperlinks($xmlSheet->hyperlinks);
+                                }
+                            }
+
+                            // Add comments
+                            $comments = [];
+                            $vmlComments = [];
+                            if (!$this->readDataOnly) {
+                                // Locate comment relations
+                                if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
+                                    //~ http://schemas.openxmlformats.org/package/2006/relationships"
+                                    $relsWorksheet = simplexml_load_string(
+                                        $this->securityScanner->scan(
+                                            $this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')
+                                        ),
+                                        'SimpleXMLElement',
+                                        Settings::getLibXmlLoaderOptions()
+                                    );
+                                    foreach ($relsWorksheet->Relationship as $ele) {
+                                        if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments') {
+                                            $comments[(string) $ele['Id']] = (string) $ele['Target'];
+                                        }
+                                        if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing') {
+                                            $vmlComments[(string) $ele['Id']] = (string) $ele['Target'];
+                                        }
+                                    }
+                                }
+
+                                // Loop through comments
+                                foreach ($comments as $relName => $relPath) {
+                                    // Load comments file
+                                    $relPath = File::realpath(dirname("$dir/$fileWorksheet") . '/' . $relPath);
+                                    $commentsFile = simplexml_load_string(
+                                        $this->securityScanner->scan($this->getFromZipArchive($zip, $relPath)),
+                                        'SimpleXMLElement',
+                                        Settings::getLibXmlLoaderOptions()
+                                    );
+
+                                    // Utility variables
+                                    $authors = [];
+
+                                    // Loop through authors
+                                    foreach ($commentsFile->authors->author as $author) {
+                                        $authors[] = (string) $author;
+                                    }
+
+                                    // Loop through contents
+                                    foreach ($commentsFile->commentList->comment as $comment) {
+                                        if (!empty($comment['authorId'])) {
+                                            $docSheet->getComment((string) $comment['ref'])->setAuthor($authors[(string) $comment['authorId']]);
+                                        }
+                                        $docSheet->getComment((string) $comment['ref'])->setText($this->parseRichText($comment->text));
+                                    }
+                                }
+
+                                // later we will remove from it real vmlComments
+                                $unparsedVmlDrawings = $vmlComments;
+
+                                // Loop through VML comments
+                                foreach ($vmlComments as $relName => $relPath) {
+                                    // Load VML comments file
+                                    $relPath = File::realpath(dirname("$dir/$fileWorksheet") . '/' . $relPath);
+
+                                    try {
+                                        $vmlCommentsFile = simplexml_load_string(
+                                            $this->securityScanner->scan($this->getFromZipArchive($zip, $relPath)),
+                                            'SimpleXMLElement',
+                                            Settings::getLibXmlLoaderOptions()
+                                        );
+                                        $vmlCommentsFile->registerXPathNamespace('v', 'urn:schemas-microsoft-com:vml');
+                                    } catch (Throwable $ex) {
+                                        //Ignore unparsable vmlDrawings. Later they will be moved from $unparsedVmlDrawings to $unparsedLoadedData
+                                        continue;
+                                    }
+
+                                    $shapes = $vmlCommentsFile->xpath('//v:shape');
+                                    foreach ($shapes as $shape) {
+                                        $shape->registerXPathNamespace('v', 'urn:schemas-microsoft-com:vml');
+
+                                        if (isset($shape['style'])) {
+                                            $style = (string) $shape['style'];
+                                            $fillColor = strtoupper(substr((string) $shape['fillcolor'], 1));
+                                            $column = null;
+                                            $row = null;
+
+                                            $clientData = $shape->xpath('.//x:ClientData');
+                                            if (is_array($clientData) && !empty($clientData)) {
+                                                $clientData = $clientData[0];
+
+                                                if (isset($clientData['ObjectType']) && (string) $clientData['ObjectType'] == 'Note') {
+                                                    $temp = $clientData->xpath('.//x:Row');
+                                                    if (is_array($temp)) {
+                                                        $row = $temp[0];
+                                                    }
+
+                                                    $temp = $clientData->xpath('.//x:Column');
+                                                    if (is_array($temp)) {
+                                                        $column = $temp[0];
+                                                    }
+                                                }
+                                            }
+
+                                            if (($column !== null) && ($row !== null)) {
+                                                // Set comment properties
+                                                $comment = $docSheet->getCommentByColumnAndRow($column + 1, $row + 1);
+                                                $comment->getFillColor()->setRGB($fillColor);
+
+                                                // Parse style
+                                                $styleArray = explode(';', str_replace(' ', '', $style));
+                                                foreach ($styleArray as $stylePair) {
+                                                    $stylePair = explode(':', $stylePair);
+
+                                                    if ($stylePair[0] == 'margin-left') {
+                                                        $comment->setMarginLeft($stylePair[1]);
+                                                    }
+                                                    if ($stylePair[0] == 'margin-top') {
+                                                        $comment->setMarginTop($stylePair[1]);
+                                                    }
+                                                    if ($stylePair[0] == 'width') {
+                                                        $comment->setWidth($stylePair[1]);
+                                                    }
+                                                    if ($stylePair[0] == 'height') {
+                                                        $comment->setHeight($stylePair[1]);
+                                                    }
+                                                    if ($stylePair[0] == 'visibility') {
+                                                        $comment->setVisible($stylePair[1] == 'visible');
+                                                    }
+                                                }
+
+                                                unset($unparsedVmlDrawings[$relName]);
+                                            }
+                                        }
+                                    }
+                                }
+
+                                // unparsed vmlDrawing
+                                if ($unparsedVmlDrawings) {
+                                    foreach ($unparsedVmlDrawings as $rId => $relPath) {
+                                        $rId = substr($rId, 3); // rIdXXX
+                                        $unparsedVmlDrawing = &$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['vmlDrawings'];
+                                        $unparsedVmlDrawing[$rId] = [];
+                                        $unparsedVmlDrawing[$rId]['filePath'] = self::dirAdd("$dir/$fileWorksheet", $relPath);
+                                        $unparsedVmlDrawing[$rId]['relFilePath'] = $relPath;
+                                        $unparsedVmlDrawing[$rId]['content'] = $this->securityScanner->scan($this->getFromZipArchive($zip, $unparsedVmlDrawing[$rId]['filePath']));
+                                        unset($unparsedVmlDrawing);
+                                    }
+                                }
+
+                                // Header/footer images
+                                if ($xmlSheet && $xmlSheet->legacyDrawingHF && !$this->readDataOnly) {
+                                    if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
+                                        //~ http://schemas.openxmlformats.org/package/2006/relationships"
+                                        $relsWorksheet = simplexml_load_string(
+                                            $this->securityScanner->scan(
+                                                $this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')
+                                            ),
+                                            'SimpleXMLElement',
+                                            Settings::getLibXmlLoaderOptions()
+                                        );
+                                        $vmlRelationship = '';
+
+                                        foreach ($relsWorksheet->Relationship as $ele) {
+                                            if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing') {
+                                                $vmlRelationship = self::dirAdd("$dir/$fileWorksheet", $ele['Target']);
+                                            }
+                                        }
+
+                                        if ($vmlRelationship != '') {
+                                            // Fetch linked images
+                                            //~ http://schemas.openxmlformats.org/package/2006/relationships"
+                                            $relsVML = simplexml_load_string(
+                                                $this->securityScanner->scan(
+                                                    $this->getFromZipArchive($zip, dirname($vmlRelationship) . '/_rels/' . basename($vmlRelationship) . '.rels')
+                                                ),
+                                                'SimpleXMLElement',
+                                                Settings::getLibXmlLoaderOptions()
+                                            );
+                                            $drawings = [];
+                                            if (isset($relsVML->Relationship)) {
+                                                foreach ($relsVML->Relationship as $ele) {
+                                                    if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image') {
+                                                        $drawings[(string) $ele['Id']] = self::dirAdd($vmlRelationship, $ele['Target']);
+                                                    }
+                                                }
+                                            }
+                                            // Fetch VML document
+                                            $vmlDrawing = simplexml_load_string(
+                                                $this->securityScanner->scan($this->getFromZipArchive($zip, $vmlRelationship)),
+                                                'SimpleXMLElement',
+                                                Settings::getLibXmlLoaderOptions()
+                                            );
+                                            $vmlDrawing->registerXPathNamespace('v', 'urn:schemas-microsoft-com:vml');
+
+                                            $hfImages = [];
+
+                                            $shapes = $vmlDrawing->xpath('//v:shape');
+                                            foreach ($shapes as $idx => $shape) {
+                                                $shape->registerXPathNamespace('v', 'urn:schemas-microsoft-com:vml');
+                                                $imageData = $shape->xpath('//v:imagedata');
+
+                                                if (!$imageData) {
+                                                    continue;
+                                                }
+
+                                                $imageData = $imageData[$idx];
+
+                                                $imageData = $imageData->attributes('urn:schemas-microsoft-com:office:office');
+                                                $style = self::toCSSArray((string) $shape['style']);
+
+                                                $hfImages[(string) $shape['id']] = new HeaderFooterDrawing();
+                                                if (isset($imageData['title'])) {
+                                                    $hfImages[(string) $shape['id']]->setName((string) $imageData['title']);
+                                                }
+
+                                                $hfImages[(string) $shape['id']]->setPath('zip://' . File::realpath($pFilename) . '#' . $drawings[(string) $imageData['relid']], false);
+                                                $hfImages[(string) $shape['id']]->setResizeProportional(false);
+                                                $hfImages[(string) $shape['id']]->setWidth($style['width']);
+                                                $hfImages[(string) $shape['id']]->setHeight($style['height']);
+                                                if (isset($style['margin-left'])) {
+                                                    $hfImages[(string) $shape['id']]->setOffsetX($style['margin-left']);
+                                                }
+                                                $hfImages[(string) $shape['id']]->setOffsetY($style['margin-top']);
+                                                $hfImages[(string) $shape['id']]->setResizeProportional(true);
+                                            }
+
+                                            $docSheet->getHeaderFooter()->setImages($hfImages);
+                                        }
+                                    }
+                                }
+                            }
+
+                            // TODO: Autoshapes from twoCellAnchors!
+                            if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
+                                //~ http://schemas.openxmlformats.org/package/2006/relationships"
+                                $relsWorksheet = simplexml_load_string(
+                                    $this->securityScanner->scan(
+                                        $this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')
+                                    ),
+                                    'SimpleXMLElement',
+                                    Settings::getLibXmlLoaderOptions()
+                                );
+                                $drawings = [];
+                                foreach ($relsWorksheet->Relationship as $ele) {
+                                    if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing') {
+                                        $drawings[(string) $ele['Id']] = self::dirAdd("$dir/$fileWorksheet", $ele['Target']);
+                                    }
+                                }
+                                if ($xmlSheet->drawing && !$this->readDataOnly) {
+                                    $unparsedDrawings = [];
+                                    foreach ($xmlSheet->drawing as $drawing) {
+                                        $drawingRelId = (string) self::getArrayItem($drawing->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'), 'id');
+                                        $fileDrawing = $drawings[$drawingRelId];
+                                        //~ http://schemas.openxmlformats.org/package/2006/relationships"
+                                        $relsDrawing = simplexml_load_string(
+                                            $this->securityScanner->scan(
+                                                $this->getFromZipArchive($zip, dirname($fileDrawing) . '/_rels/' . basename($fileDrawing) . '.rels')
+                                            ),
+                                            'SimpleXMLElement',
+                                            Settings::getLibXmlLoaderOptions()
+                                        );
+                                        $images = [];
+                                        $hyperlinks = [];
+                                        if ($relsDrawing && $relsDrawing->Relationship) {
+                                            foreach ($relsDrawing->Relationship as $ele) {
+                                                if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink') {
+                                                    $hyperlinks[(string) $ele['Id']] = (string) $ele['Target'];
+                                                }
+                                                if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image') {
+                                                    $images[(string) $ele['Id']] = self::dirAdd($fileDrawing, $ele['Target']);
+                                                } elseif ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart') {
+                                                    if ($this->includeCharts) {
+                                                        $charts[self::dirAdd($fileDrawing, $ele['Target'])] = [
+                                                            'id' => (string) $ele['Id'],
+                                                            'sheet' => $docSheet->getTitle(),
+                                                        ];
+                                                    }
+                                                }
+                                            }
+                                        }
+                                        $xmlDrawing = simplexml_load_string(
+                                            $this->securityScanner->scan($this->getFromZipArchive($zip, $fileDrawing)),
+                                            'SimpleXMLElement',
+                                            Settings::getLibXmlLoaderOptions()
+                                        );
+                                        $xmlDrawingChildren = $xmlDrawing->children('http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing');
+
+                                        if ($xmlDrawingChildren->oneCellAnchor) {
+                                            foreach ($xmlDrawingChildren->oneCellAnchor as $oneCellAnchor) {
+                                                if ($oneCellAnchor->pic->blipFill) {
+                                                    /** @var SimpleXMLElement $blip */
+                                                    $blip = $oneCellAnchor->pic->blipFill->children('http://schemas.openxmlformats.org/drawingml/2006/main')->blip;
+                                                    /** @var SimpleXMLElement $xfrm */
+                                                    $xfrm = $oneCellAnchor->pic->spPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->xfrm;
+                                                    /** @var SimpleXMLElement $outerShdw */
+                                                    $outerShdw = $oneCellAnchor->pic->spPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->effectLst->outerShdw;
+                                                    /** @var SimpleXMLElement $hlinkClick */
+                                                    $hlinkClick = $oneCellAnchor->pic->nvPicPr->cNvPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->hlinkClick;
+
+                                                    $objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
+                                                    $objDrawing->setName((string) self::getArrayItem($oneCellAnchor->pic->nvPicPr->cNvPr->attributes(), 'name'));
+                                                    $objDrawing->setDescription((string) self::getArrayItem($oneCellAnchor->pic->nvPicPr->cNvPr->attributes(), 'descr'));
+                                                    $objDrawing->setPath(
+                                                        'zip://' . File::realpath($pFilename) . '#' .
+                                                        $images[(string) self::getArrayItem(
+                                                            $blip->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'),
+                                                            'embed'
+                                                        )],
+                                                        false
+                                                    );
+                                                    $objDrawing->setCoordinates(Coordinate::stringFromColumnIndex(((string) $oneCellAnchor->from->col) + 1) . ($oneCellAnchor->from->row + 1));
+                                                    $objDrawing->setOffsetX(Drawing::EMUToPixels($oneCellAnchor->from->colOff));
+                                                    $objDrawing->setOffsetY(Drawing::EMUToPixels($oneCellAnchor->from->rowOff));
+                                                    $objDrawing->setResizeProportional(false);
+                                                    $objDrawing->setWidth(Drawing::EMUToPixels(self::getArrayItem($oneCellAnchor->ext->attributes(), 'cx')));
+                                                    $objDrawing->setHeight(Drawing::EMUToPixels(self::getArrayItem($oneCellAnchor->ext->attributes(), 'cy')));
+                                                    if ($xfrm) {
+                                                        $objDrawing->setRotation(Drawing::angleToDegrees(self::getArrayItem($xfrm->attributes(), 'rot')));
+                                                    }
+                                                    if ($outerShdw) {
+                                                        $shadow = $objDrawing->getShadow();
+                                                        $shadow->setVisible(true);
+                                                        $shadow->setBlurRadius(Drawing::EMUToPixels(self::getArrayItem($outerShdw->attributes(), 'blurRad')));
+                                                        $shadow->setDistance(Drawing::EMUToPixels(self::getArrayItem($outerShdw->attributes(), 'dist')));
+                                                        $shadow->setDirection(Drawing::angleToDegrees(self::getArrayItem($outerShdw->attributes(), 'dir')));
+                                                        $shadow->setAlignment((string) self::getArrayItem($outerShdw->attributes(), 'algn'));
+                                                        $clr = isset($outerShdw->srgbClr) ? $outerShdw->srgbClr : $outerShdw->prstClr;
+                                                        $shadow->getColor()->setRGB(self::getArrayItem($clr->attributes(), 'val'));
+                                                        $shadow->setAlpha(self::getArrayItem($clr->alpha->attributes(), 'val') / 1000);
+                                                    }
+
+                                                    $this->readHyperLinkDrawing($objDrawing, $oneCellAnchor, $hyperlinks);
+
+                                                    $objDrawing->setWorksheet($docSheet);
+                                                } else {
+                                                    //    ? Can charts be positioned with a oneCellAnchor ?
+                                                    $coordinates = Coordinate::stringFromColumnIndex(((string) $oneCellAnchor->from->col) + 1) . ($oneCellAnchor->from->row + 1);
+                                                    $offsetX = Drawing::EMUToPixels($oneCellAnchor->from->colOff);
+                                                    $offsetY = Drawing::EMUToPixels($oneCellAnchor->from->rowOff);
+                                                    $width = Drawing::EMUToPixels(self::getArrayItem($oneCellAnchor->ext->attributes(), 'cx'));
+                                                    $height = Drawing::EMUToPixels(self::getArrayItem($oneCellAnchor->ext->attributes(), 'cy'));
+                                                }
+                                            }
+                                        }
+                                        if ($xmlDrawingChildren->twoCellAnchor) {
+                                            foreach ($xmlDrawingChildren->twoCellAnchor as $twoCellAnchor) {
+                                                if ($twoCellAnchor->pic->blipFill) {
+                                                    $blip = $twoCellAnchor->pic->blipFill->children('http://schemas.openxmlformats.org/drawingml/2006/main')->blip;
+                                                    $xfrm = $twoCellAnchor->pic->spPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->xfrm;
+                                                    $outerShdw = $twoCellAnchor->pic->spPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->effectLst->outerShdw;
+                                                    $hlinkClick = $twoCellAnchor->pic->nvPicPr->cNvPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->hlinkClick;
+                                                    $objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
+                                                    $objDrawing->setName((string) self::getArrayItem($twoCellAnchor->pic->nvPicPr->cNvPr->attributes(), 'name'));
+                                                    $objDrawing->setDescription((string) self::getArrayItem($twoCellAnchor->pic->nvPicPr->cNvPr->attributes(), 'descr'));
+                                                    $objDrawing->setPath(
+                                                        'zip://' . File::realpath($pFilename) . '#' .
+                                                        $images[(string) self::getArrayItem(
+                                                            $blip->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'),
+                                                            'embed'
+                                                        )],
+                                                        false
+                                                    );
+                                                    $objDrawing->setCoordinates(Coordinate::stringFromColumnIndex(((string) $twoCellAnchor->from->col) + 1) . ($twoCellAnchor->from->row + 1));
+                                                    $objDrawing->setOffsetX(Drawing::EMUToPixels($twoCellAnchor->from->colOff));
+                                                    $objDrawing->setOffsetY(Drawing::EMUToPixels($twoCellAnchor->from->rowOff));
+                                                    $objDrawing->setResizeProportional(false);
+
+                                                    if ($xfrm) {
+                                                        $objDrawing->setWidth(Drawing::EMUToPixels(self::getArrayItem($xfrm->ext->attributes(), 'cx')));
+                                                        $objDrawing->setHeight(Drawing::EMUToPixels(self::getArrayItem($xfrm->ext->attributes(), 'cy')));
+                                                        $objDrawing->setRotation(Drawing::angleToDegrees(self::getArrayItem($xfrm->attributes(), 'rot')));
+                                                    }
+                                                    if ($outerShdw) {
+                                                        $shadow = $objDrawing->getShadow();
+                                                        $shadow->setVisible(true);
+                                                        $shadow->setBlurRadius(Drawing::EMUToPixels(self::getArrayItem($outerShdw->attributes(), 'blurRad')));
+                                                        $shadow->setDistance(Drawing::EMUToPixels(self::getArrayItem($outerShdw->attributes(), 'dist')));
+                                                        $shadow->setDirection(Drawing::angleToDegrees(self::getArrayItem($outerShdw->attributes(), 'dir')));
+                                                        $shadow->setAlignment((string) self::getArrayItem($outerShdw->attributes(), 'algn'));
+                                                        $clr = isset($outerShdw->srgbClr) ? $outerShdw->srgbClr : $outerShdw->prstClr;
+                                                        $shadow->getColor()->setRGB(self::getArrayItem($clr->attributes(), 'val'));
+                                                        $shadow->setAlpha(self::getArrayItem($clr->alpha->attributes(), 'val') / 1000);
+                                                    }
+
+                                                    $this->readHyperLinkDrawing($objDrawing, $twoCellAnchor, $hyperlinks);
+
+                                                    $objDrawing->setWorksheet($docSheet);
+                                                } elseif (($this->includeCharts) && ($twoCellAnchor->graphicFrame)) {
+                                                    $fromCoordinate = Coordinate::stringFromColumnIndex(((string) $twoCellAnchor->from->col) + 1) . ($twoCellAnchor->from->row + 1);
+                                                    $fromOffsetX = Drawing::EMUToPixels($twoCellAnchor->from->colOff);
+                                                    $fromOffsetY = Drawing::EMUToPixels($twoCellAnchor->from->rowOff);
+                                                    $toCoordinate = Coordinate::stringFromColumnIndex(((string) $twoCellAnchor->to->col) + 1) . ($twoCellAnchor->to->row + 1);
+                                                    $toOffsetX = Drawing::EMUToPixels($twoCellAnchor->to->colOff);
+                                                    $toOffsetY = Drawing::EMUToPixels($twoCellAnchor->to->rowOff);
+                                                    $graphic = $twoCellAnchor->graphicFrame->children('http://schemas.openxmlformats.org/drawingml/2006/main')->graphic;
+                                                    /** @var SimpleXMLElement $chartRef */
+                                                    $chartRef = $graphic->graphicData->children('http://schemas.openxmlformats.org/drawingml/2006/chart')->chart;
+                                                    $thisChart = (string) $chartRef->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships');
+
+                                                    $chartDetails[$docSheet->getTitle() . '!' . $thisChart] = [
+                                                        'fromCoordinate' => $fromCoordinate,
+                                                        'fromOffsetX' => $fromOffsetX,
+                                                        'fromOffsetY' => $fromOffsetY,
+                                                        'toCoordinate' => $toCoordinate,
+                                                        'toOffsetX' => $toOffsetX,
+                                                        'toOffsetY' => $toOffsetY,
+                                                        'worksheetTitle' => $docSheet->getTitle(),
+                                                    ];
+                                                }
+                                            }
+                                        }
+                                        if ($relsDrawing === false && $xmlDrawing->count() == 0) {
+                                            // Save Drawing without rels and children as unparsed
+                                            $unparsedDrawings[$drawingRelId] = $xmlDrawing->asXML();
+                                        }
+                                    }
+
+                                    // store original rId of drawing files
+                                    $unparsedLoadedData['sheets'][$docSheet->getCodeName()]['drawingOriginalIds'] = [];
+                                    foreach ($relsWorksheet->Relationship as $ele) {
+                                        if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing') {
+                                            $drawingRelId = (string) $ele['Id'];
+                                            $unparsedLoadedData['sheets'][$docSheet->getCodeName()]['drawingOriginalIds'][(string) $ele['Target']] = $drawingRelId;
+                                            if (isset($unparsedDrawings[$drawingRelId])) {
+                                                $unparsedLoadedData['sheets'][$docSheet->getCodeName()]['Drawings'][$drawingRelId] = $unparsedDrawings[$drawingRelId];
+                                            }
+                                        }
+                                    }
+
+                                    // unparsed drawing AlternateContent
+                                    $xmlAltDrawing = simplexml_load_string(
+                                        $this->securityScanner->scan($this->getFromZipArchive($zip, $fileDrawing)),
+                                        'SimpleXMLElement',
+                                        Settings::getLibXmlLoaderOptions()
+                                    )->children('http://schemas.openxmlformats.org/markup-compatibility/2006');
+
+                                    if ($xmlAltDrawing->AlternateContent) {
+                                        foreach ($xmlAltDrawing->AlternateContent as $alternateContent) {
+                                            $unparsedLoadedData['sheets'][$docSheet->getCodeName()]['drawingAlternateContents'][] = $alternateContent->asXML();
+                                        }
+                                    }
+                                }
+                            }
+
+                            $this->readFormControlProperties($excel, $zip, $dir, $fileWorksheet, $docSheet, $unparsedLoadedData);
+                            $this->readPrinterSettings($excel, $zip, $dir, $fileWorksheet, $docSheet, $unparsedLoadedData);
+
+                            // Loop through definedNames
+                            if ($xmlWorkbook->definedNames) {
+                                foreach ($xmlWorkbook->definedNames->definedName as $definedName) {
+                                    // Extract range
+                                    $extractedRange = (string) $definedName;
+                                    if (($spos = strpos($extractedRange, '!')) !== false) {
+                                        $extractedRange = substr($extractedRange, 0, $spos) . str_replace('$', '', substr($extractedRange, $spos));
+                                    } else {
+                                        $extractedRange = str_replace('$', '', $extractedRange);
+                                    }
+
+                                    // Valid range?
+                                    if (stripos((string) $definedName, '#REF!') !== false || $extractedRange == '') {
+                                        continue;
+                                    }
+
+                                    // Some definedNames are only applicable if we are on the same sheet...
+                                    if ((string) $definedName['localSheetId'] != '' && (string) $definedName['localSheetId'] == $oldSheetId) {
+                                        // Switch on type
+                                        switch ((string) $definedName['name']) {
+                                            case '_xlnm._FilterDatabase':
+                                                if ((string) $definedName['hidden'] !== '1') {
+                                                    $extractedRange = explode(',', $extractedRange);
+                                                    foreach ($extractedRange as $range) {
+                                                        $autoFilterRange = $range;
+                                                        if (strpos($autoFilterRange, ':') !== false) {
+                                                            $docSheet->getAutoFilter()->setRange($autoFilterRange);
+                                                        }
+                                                    }
+                                                }
+
+                                                break;
+                                            case '_xlnm.Print_Titles':
+                                                // Split $extractedRange
+                                                $extractedRange = explode(',', $extractedRange);
+
+                                                // Set print titles
+                                                foreach ($extractedRange as $range) {
+                                                    $matches = [];
+                                                    $range = str_replace('$', '', $range);
+
+                                                    // check for repeating columns, e g. 'A:A' or 'A:D'
+                                                    if (preg_match('/!?([A-Z]+)\:([A-Z]+)$/', $range, $matches)) {
+                                                        $docSheet->getPageSetup()->setColumnsToRepeatAtLeft([$matches[1], $matches[2]]);
+                                                    } elseif (preg_match('/!?(\d+)\:(\d+)$/', $range, $matches)) {
+                                                        // check for repeating rows, e.g. '1:1' or '1:5'
+                                                        $docSheet->getPageSetup()->setRowsToRepeatAtTop([$matches[1], $matches[2]]);
+                                                    }
+                                                }
+
+                                                break;
+                                            case '_xlnm.Print_Area':
+                                                $rangeSets = preg_split("/('?(?:.*?)'?(?:![A-Z0-9]+:[A-Z0-9]+)),?/", $extractedRange, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
+                                                $newRangeSets = [];
+                                                foreach ($rangeSets as $rangeSet) {
+                                                    [$sheetName, $rangeSet] = Worksheet::extractSheetTitle($rangeSet, true);
+                                                    if (strpos($rangeSet, ':') === false) {
+                                                        $rangeSet = $rangeSet . ':' . $rangeSet;
+                                                    }
+                                                    $newRangeSets[] = str_replace('$', '', $rangeSet);
+                                                }
+                                                $docSheet->getPageSetup()->setPrintArea(implode(',', $newRangeSets));
+
+                                                break;
+                                            default:
+                                                break;
+                                        }
+                                    }
+                                }
+                            }
+
+                            // Next sheet id
+                            ++$sheetId;
+                        }
+
+                        // Loop through definedNames
+                        if ($xmlWorkbook->definedNames) {
+                            foreach ($xmlWorkbook->definedNames->definedName as $definedName) {
+                                // Extract range
+                                $extractedRange = (string) $definedName;
+                                if (($spos = strpos($extractedRange, '!')) !== false) {
+                                    $extractedRange = substr($extractedRange, 0, $spos) . str_replace('$', '', substr($extractedRange, $spos));
+                                } else {
+                                    $extractedRange = str_replace('$', '', $extractedRange);
+                                }
+
+                                // Valid range?
+                                if (stripos((string) $definedName, '#REF!') !== false || $extractedRange == '') {
+                                    continue;
+                                }
+
+                                // Some definedNames are only applicable if we are on the same sheet...
+                                if ((string) $definedName['localSheetId'] != '') {
+                                    // Local defined name
+                                    // Switch on type
+                                    switch ((string) $definedName['name']) {
+                                        case '_xlnm._FilterDatabase':
+                                        case '_xlnm.Print_Titles':
+                                        case '_xlnm.Print_Area':
+                                            break;
+                                        default:
+                                            if ($mapSheetId[(int) $definedName['localSheetId']] !== null) {
+                                                if (strpos((string) $definedName, '!') !== false) {
+                                                    $range = Worksheet::extractSheetTitle((string) $definedName, true);
+                                                    $range[0] = str_replace("''", "'", $range[0]);
+                                                    $range[0] = str_replace("'", '', $range[0]);
+                                                    if ($worksheet = $docSheet->getParent()->getSheetByName($range[0])) {
+                                                        $extractedRange = str_replace('$', '', $range[1]);
+                                                        $scope = $docSheet->getParent()->getSheet($mapSheetId[(int) $definedName['localSheetId']]);
+                                                        $excel->addNamedRange(new NamedRange((string) $definedName['name'], $worksheet, $extractedRange, true, $scope));
+                                                    }
+                                                }
+                                            }
+
+                                            break;
+                                    }
+                                } elseif (!isset($definedName['localSheetId'])) {
+                                    // "Global" definedNames
+                                    $locatedSheet = null;
+                                    $extractedSheetName = '';
+                                    if (strpos((string) $definedName, '!') !== false) {
+                                        // Extract sheet name
+                                        $extractedSheetName = Worksheet::extractSheetTitle((string) $definedName, true);
+                                        $extractedSheetName = trim($extractedSheetName[0], "'");
+
+                                        // Locate sheet
+                                        $locatedSheet = $excel->getSheetByName($extractedSheetName);
+
+                                        // Modify range
+                                        [$worksheetName, $extractedRange] = Worksheet::extractSheetTitle($extractedRange, true);
+                                    }
+
+                                    if ($locatedSheet !== null) {
+                                        $excel->addNamedRange(new NamedRange((string) $definedName['name'], $locatedSheet, $extractedRange, false));
+                                    }
+                                }
+                            }
+                        }
+                    }
+
+                    if ((!$this->readDataOnly || !empty($this->loadSheetsOnly)) && isset($xmlWorkbook->bookViews->workbookView)) {
+                        $workbookView = $xmlWorkbook->bookViews->workbookView;
+
+                        // active sheet index
+                        $activeTab = (int) ($workbookView['activeTab']); // refers to old sheet index
+
+                        // keep active sheet index if sheet is still loaded, else first sheet is set as the active
+                        if (isset($mapSheetId[$activeTab]) && $mapSheetId[$activeTab] !== null) {
+                            $excel->setActiveSheetIndex($mapSheetId[$activeTab]);
+                        } else {
+                            if ($excel->getSheetCount() == 0) {
+                                $excel->createSheet();
+                            }
+                            $excel->setActiveSheetIndex(0);
+                        }
+
+                        if (isset($workbookView['showHorizontalScroll'])) {
+                            $showHorizontalScroll = (string) $workbookView['showHorizontalScroll'];
+                            $excel->setShowHorizontalScroll($this->castXsdBooleanToBool($showHorizontalScroll));
+                        }
+
+                        if (isset($workbookView['showVerticalScroll'])) {
+                            $showVerticalScroll = (string) $workbookView['showVerticalScroll'];
+                            $excel->setShowVerticalScroll($this->castXsdBooleanToBool($showVerticalScroll));
+                        }
+
+                        if (isset($workbookView['showSheetTabs'])) {
+                            $showSheetTabs = (string) $workbookView['showSheetTabs'];
+                            $excel->setShowSheetTabs($this->castXsdBooleanToBool($showSheetTabs));
+                        }
+
+                        if (isset($workbookView['minimized'])) {
+                            $minimized = (string) $workbookView['minimized'];
+                            $excel->setMinimized($this->castXsdBooleanToBool($minimized));
+                        }
+
+                        if (isset($workbookView['autoFilterDateGrouping'])) {
+                            $autoFilterDateGrouping = (string) $workbookView['autoFilterDateGrouping'];
+                            $excel->setAutoFilterDateGrouping($this->castXsdBooleanToBool($autoFilterDateGrouping));
+                        }
+
+                        if (isset($workbookView['firstSheet'])) {
+                            $firstSheet = (string) $workbookView['firstSheet'];
+                            $excel->setFirstSheetIndex((int) $firstSheet);
+                        }
+
+                        if (isset($workbookView['visibility'])) {
+                            $visibility = (string) $workbookView['visibility'];
+                            $excel->setVisibility($visibility);
+                        }
+
+                        if (isset($workbookView['tabRatio'])) {
+                            $tabRatio = (string) $workbookView['tabRatio'];
+                            $excel->setTabRatio((int) $tabRatio);
+                        }
+                    }
+
+                    break;
+            }
+        }
+
+        if (!$this->readDataOnly) {
+            $contentTypes = simplexml_load_string(
+                $this->securityScanner->scan(
+                    $this->getFromZipArchive($zip, '[Content_Types].xml')
+                ),
+                'SimpleXMLElement',
+                Settings::getLibXmlLoaderOptions()
+            );
+
+            // Default content types
+            foreach ($contentTypes->Default as $contentType) {
+                switch ($contentType['ContentType']) {
+                    case 'application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings':
+                        $unparsedLoadedData['default_content_types'][(string) $contentType['Extension']] = (string) $contentType['ContentType'];
+
+                        break;
+                }
+            }
+
+            // Override content types
+            foreach ($contentTypes->Override as $contentType) {
+                switch ($contentType['ContentType']) {
+                    case 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml':
+                        if ($this->includeCharts) {
+                            $chartEntryRef = ltrim($contentType['PartName'], '/');
+                            $chartElements = simplexml_load_string(
+                                $this->securityScanner->scan(
+                                    $this->getFromZipArchive($zip, $chartEntryRef)
+                                ),
+                                'SimpleXMLElement',
+                                Settings::getLibXmlLoaderOptions()
+                            );
+                            $objChart = Chart::readChart($chartElements, basename($chartEntryRef, '.xml'));
+
+                            if (isset($charts[$chartEntryRef])) {
+                                $chartPositionRef = $charts[$chartEntryRef]['sheet'] . '!' . $charts[$chartEntryRef]['id'];
+                                if (isset($chartDetails[$chartPositionRef])) {
+                                    $excel->getSheetByName($charts[$chartEntryRef]['sheet'])->addChart($objChart);
+                                    $objChart->setWorksheet($excel->getSheetByName($charts[$chartEntryRef]['sheet']));
+                                    $objChart->setTopLeftPosition($chartDetails[$chartPositionRef]['fromCoordinate'], $chartDetails[$chartPositionRef]['fromOffsetX'], $chartDetails[$chartPositionRef]['fromOffsetY']);
+                                    $objChart->setBottomRightPosition($chartDetails[$chartPositionRef]['toCoordinate'], $chartDetails[$chartPositionRef]['toOffsetX'], $chartDetails[$chartPositionRef]['toOffsetY']);
+                                }
+                            }
+                        }
+
+                        break;
+
+                    // unparsed
+                    case 'application/vnd.ms-excel.controlproperties+xml':
+                        $unparsedLoadedData['override_content_types'][(string) $contentType['PartName']] = (string) $contentType['ContentType'];
+
+                        break;
+                }
+            }
+        }
+
+        $excel->setUnparsedLoadedData($unparsedLoadedData);
+
+        $zip->close();
+
+        return $excel;
+    }
+
+    private static function readColor($color, $background = false)
+    {
+        if (isset($color['rgb'])) {
+            return (string) $color['rgb'];
+        } elseif (isset($color['indexed'])) {
+            return Color::indexedColor($color['indexed'] - 7, $background)->getARGB();
+        } elseif (isset($color['theme'])) {
+            if (self::$theme !== null) {
+                $returnColour = self::$theme->getColourByIndex((int) $color['theme']);
+                if (isset($color['tint'])) {
+                    $tintAdjust = (float) $color['tint'];
+                    $returnColour = Color::changeBrightness($returnColour, $tintAdjust);
+                }
+
+                return 'FF' . $returnColour;
+            }
+        }
+
+        if ($background) {
+            return 'FFFFFFFF';
+        }
+
+        return 'FF000000';
+    }
+
+    /**
+     * @param SimpleXMLElement|stdClass $style
+     */
+    private static function readStyle(Style $docStyle, $style): void
+    {
+        $docStyle->getNumberFormat()->setFormatCode($style->numFmt);
+
+        // font
+        if (isset($style->font)) {
+            $docStyle->getFont()->setName((string) $style->font->name['val']);
+            $docStyle->getFont()->setSize((string) $style->font->sz['val']);
+            if (isset($style->font->b)) {
+                $docStyle->getFont()->setBold(!isset($style->font->b['val']) || self::boolean((string) $style->font->b['val']));
+            }
+            if (isset($style->font->i)) {
+                $docStyle->getFont()->setItalic(!isset($style->font->i['val']) || self::boolean((string) $style->font->i['val']));
+            }
+            if (isset($style->font->strike)) {
+                $docStyle->getFont()->setStrikethrough(!isset($style->font->strike['val']) || self::boolean((string) $style->font->strike['val']));
+            }
+            $docStyle->getFont()->getColor()->setARGB(self::readColor($style->font->color));
+
+            if (isset($style->font->u) && !isset($style->font->u['val'])) {
+                $docStyle->getFont()->setUnderline(\PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLE);
+            } elseif (isset($style->font->u, $style->font->u['val'])) {
+                $docStyle->getFont()->setUnderline((string) $style->font->u['val']);
+            }
+
+            if (isset($style->font->vertAlign, $style->font->vertAlign['val'])) {
+                $vertAlign = strtolower((string) $style->font->vertAlign['val']);
+                if ($vertAlign == 'superscript') {
+                    $docStyle->getFont()->setSuperscript(true);
+                }
+                if ($vertAlign == 'subscript') {
+                    $docStyle->getFont()->setSubscript(true);
+                }
+            }
+        }
+
+        // fill
+        if (isset($style->fill)) {
+            if ($style->fill->gradientFill) {
+                /** @var SimpleXMLElement $gradientFill */
+                $gradientFill = $style->fill->gradientFill[0];
+                if (!empty($gradientFill['type'])) {
+                    $docStyle->getFill()->setFillType((string) $gradientFill['type']);
+                }
+                $docStyle->getFill()->setRotation((float) ($gradientFill['degree']));
+                $gradientFill->registerXPathNamespace('sml', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
+                $docStyle->getFill()->getStartColor()->setARGB(self::readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=0]'))->color));
+                $docStyle->getFill()->getEndColor()->setARGB(self::readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=1]'))->color));
+            } elseif ($style->fill->patternFill) {
+                $patternType = (string) $style->fill->patternFill['patternType'] != '' ? (string) $style->fill->patternFill['patternType'] : 'solid';
+                $docStyle->getFill()->setFillType($patternType);
+                if ($style->fill->patternFill->fgColor) {
+                    $docStyle->getFill()->getStartColor()->setARGB(self::readColor($style->fill->patternFill->fgColor, true));
+                }
+                if ($style->fill->patternFill->bgColor) {
+                    $docStyle->getFill()->getEndColor()->setARGB(self::readColor($style->fill->patternFill->bgColor, true));
+                }
+            }
+        }
+
+        // border
+        if (isset($style->border)) {
+            $diagonalUp = self::boolean((string) $style->border['diagonalUp']);
+            $diagonalDown = self::boolean((string) $style->border['diagonalDown']);
+            if (!$diagonalUp && !$diagonalDown) {
+                $docStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_NONE);
+            } elseif ($diagonalUp && !$diagonalDown) {
+                $docStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_UP);
+            } elseif (!$diagonalUp && $diagonalDown) {
+                $docStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_DOWN);
+            } else {
+                $docStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_BOTH);
+            }
+            self::readBorder($docStyle->getBorders()->getLeft(), $style->border->left);
+            self::readBorder($docStyle->getBorders()->getRight(), $style->border->right);
+            self::readBorder($docStyle->getBorders()->getTop(), $style->border->top);
+            self::readBorder($docStyle->getBorders()->getBottom(), $style->border->bottom);
+            self::readBorder($docStyle->getBorders()->getDiagonal(), $style->border->diagonal);
+        }
+
+        // alignment
+        if (isset($style->alignment)) {
+            $docStyle->getAlignment()->setHorizontal((string) $style->alignment['horizontal']);
+            $docStyle->getAlignment()->setVertical((string) $style->alignment['vertical']);
+
+            $textRotation = 0;
+            if ((int) $style->alignment['textRotation'] <= 90) {
+                $textRotation = (int) $style->alignment['textRotation'];
+            } elseif ((int) $style->alignment['textRotation'] > 90) {
+                $textRotation = 90 - (int) $style->alignment['textRotation'];
+            }
+
+            $docStyle->getAlignment()->setTextRotation((int) $textRotation);
+            $docStyle->getAlignment()->setWrapText(self::boolean((string) $style->alignment['wrapText']));
+            $docStyle->getAlignment()->setShrinkToFit(self::boolean((string) $style->alignment['shrinkToFit']));
+            $docStyle->getAlignment()->setIndent((int) ((string) $style->alignment['indent']) > 0 ? (int) ((string) $style->alignment['indent']) : 0);
+            $docStyle->getAlignment()->setReadOrder((int) ((string) $style->alignment['readingOrder']) > 0 ? (int) ((string) $style->alignment['readingOrder']) : 0);
+        }
+
+        // protection
+        if (isset($style->protection)) {
+            if (isset($style->protection['locked'])) {
+                if (self::boolean((string) $style->protection['locked'])) {
+                    $docStyle->getProtection()->setLocked(Protection::PROTECTION_PROTECTED);
+                } else {
+                    $docStyle->getProtection()->setLocked(Protection::PROTECTION_UNPROTECTED);
+                }
+            }
+
+            if (isset($style->protection['hidden'])) {
+                if (self::boolean((string) $style->protection['hidden'])) {
+                    $docStyle->getProtection()->setHidden(Protection::PROTECTION_PROTECTED);
+                } else {
+                    $docStyle->getProtection()->setHidden(Protection::PROTECTION_UNPROTECTED);
+                }
+            }
+        }
+
+        // top-level style settings
+        if (isset($style->quotePrefix)) {
+            $docStyle->setQuotePrefix($style->quotePrefix);
+        }
+    }
+
+    /**
+     * @param SimpleXMLElement $eleBorder
+     */
+    private static function readBorder(Border $docBorder, $eleBorder): void
+    {
+        if (isset($eleBorder['style'])) {
+            $docBorder->setBorderStyle((string) $eleBorder['style']);
+        }
+        if (isset($eleBorder->color)) {
+            $docBorder->getColor()->setARGB(self::readColor($eleBorder->color));
+        }
+    }
+
+    /**
+     * @param SimpleXMLElement | null $is
+     *
+     * @return RichText
+     */
+    private function parseRichText($is)
+    {
+        $value = new RichText();
+
+        if (isset($is->t)) {
+            $value->createText(StringHelper::controlCharacterOOXML2PHP((string) $is->t));
+        } else {
+            if (is_object($is->r)) {
+                foreach ($is->r as $run) {
+                    if (!isset($run->rPr)) {
+                        $value->createText(StringHelper::controlCharacterOOXML2PHP((string) $run->t));
+                    } else {
+                        $objText = $value->createTextRun(StringHelper::controlCharacterOOXML2PHP((string) $run->t));
+
+                        if (isset($run->rPr->rFont['val'])) {
+                            $objText->getFont()->setName((string) $run->rPr->rFont['val']);
+                        }
+                        if (isset($run->rPr->sz['val'])) {
+                            $objText->getFont()->setSize((float) $run->rPr->sz['val']);
+                        }
+                        if (isset($run->rPr->color)) {
+                            $objText->getFont()->setColor(new Color(self::readColor($run->rPr->color)));
+                        }
+                        if ((isset($run->rPr->b['val']) && self::boolean((string) $run->rPr->b['val'])) ||
+                            (isset($run->rPr->b) && !isset($run->rPr->b['val']))) {
+                            $objText->getFont()->setBold(true);
+                        }
+                        if ((isset($run->rPr->i['val']) && self::boolean((string) $run->rPr->i['val'])) ||
+                            (isset($run->rPr->i) && !isset($run->rPr->i['val']))) {
+                            $objText->getFont()->setItalic(true);
+                        }
+                        if (isset($run->rPr->vertAlign, $run->rPr->vertAlign['val'])) {
+                            $vertAlign = strtolower((string) $run->rPr->vertAlign['val']);
+                            if ($vertAlign == 'superscript') {
+                                $objText->getFont()->setSuperscript(true);
+                            }
+                            if ($vertAlign == 'subscript') {
+                                $objText->getFont()->setSubscript(true);
+                            }
+                        }
+                        if (isset($run->rPr->u) && !isset($run->rPr->u['val'])) {
+                            $objText->getFont()->setUnderline(\PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLE);
+                        } elseif (isset($run->rPr->u, $run->rPr->u['val'])) {
+                            $objText->getFont()->setUnderline((string) $run->rPr->u['val']);
+                        }
+                        if ((isset($run->rPr->strike['val']) && self::boolean((string) $run->rPr->strike['val'])) ||
+                            (isset($run->rPr->strike) && !isset($run->rPr->strike['val']))) {
+                            $objText->getFont()->setStrikethrough(true);
+                        }
+                    }
+                }
+            }
+        }
+
+        return $value;
+    }
+
+    /**
+     * @param mixed $customUITarget
+     * @param mixed $zip
+     */
+    private function readRibbon(Spreadsheet $excel, $customUITarget, $zip): void
+    {
+        $baseDir = dirname($customUITarget);
+        $nameCustomUI = basename($customUITarget);
+        // get the xml file (ribbon)
+        $localRibbon = $this->getFromZipArchive($zip, $customUITarget);
+        $customUIImagesNames = [];
+        $customUIImagesBinaries = [];
+        // something like customUI/_rels/customUI.xml.rels
+        $pathRels = $baseDir . '/_rels/' . $nameCustomUI . '.rels';
+        $dataRels = $this->getFromZipArchive($zip, $pathRels);
+        if ($dataRels) {
+            // exists and not empty if the ribbon have some pictures (other than internal MSO)
+            $UIRels = simplexml_load_string(
+                $this->securityScanner->scan($dataRels),
+                'SimpleXMLElement',
+                Settings::getLibXmlLoaderOptions()
+            );
+            if (false !== $UIRels) {
+                // we need to save id and target to avoid parsing customUI.xml and "guess" if it's a pseudo callback who load the image
+                foreach ($UIRels->Relationship as $ele) {
+                    if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image') {
+                        // an image ?
+                        $customUIImagesNames[(string) $ele['Id']] = (string) $ele['Target'];
+                        $customUIImagesBinaries[(string) $ele['Target']] = $this->getFromZipArchive($zip, $baseDir . '/' . (string) $ele['Target']);
+                    }
+                }
+            }
+        }
+        if ($localRibbon) {
+            $excel->setRibbonXMLData($customUITarget, $localRibbon);
+            if (count($customUIImagesNames) > 0 && count($customUIImagesBinaries) > 0) {
+                $excel->setRibbonBinObjects($customUIImagesNames, $customUIImagesBinaries);
+            } else {
+                $excel->setRibbonBinObjects(null, null);
+            }
+        } else {
+            $excel->setRibbonXMLData(null, null);
+            $excel->setRibbonBinObjects(null, null);
+        }
+    }
+
+    private static function getArrayItem($array, $key = 0)
+    {
+        return $array[$key] ?? null;
+    }
+
+    private static function dirAdd($base, $add)
+    {
+        return preg_replace('~[^/]+/\.\./~', '', dirname($base) . "/$add");
+    }
+
+    private static function toCSSArray($style)
+    {
+        $style = self::stripWhiteSpaceFromStyleString($style);
+
+        $temp = explode(';', $style);
+        $style = [];
+        foreach ($temp as $item) {
+            $item = explode(':', $item);
+
+            if (strpos($item[1], 'px') !== false) {
+                $item[1] = str_replace('px', '', $item[1]);
+            }
+            if (strpos($item[1], 'pt') !== false) {
+                $item[1] = str_replace('pt', '', $item[1]);
+                $item[1] = Font::fontSizeToPixels($item[1]);
+            }
+            if (strpos($item[1], 'in') !== false) {
+                $item[1] = str_replace('in', '', $item[1]);
+                $item[1] = Font::inchSizeToPixels($item[1]);
+            }
+            if (strpos($item[1], 'cm') !== false) {
+                $item[1] = str_replace('cm', '', $item[1]);
+                $item[1] = Font::centimeterSizeToPixels($item[1]);
+            }
+
+            $style[$item[0]] = $item[1];
+        }
+
+        return $style;
+    }
+
+    public static function stripWhiteSpaceFromStyleString($string)
+    {
+        return trim(str_replace(["\r", "\n", ' '], '', $string), ';');
+    }
+
+    private static function boolean($value)
+    {
+        if (is_object($value)) {
+            $value = (string) $value;
+        }
+        if (is_numeric($value)) {
+            return (bool) $value;
+        }
+
+        return $value === 'true' || $value === 'TRUE';
+    }
+
+    /**
+     * @param \PhpOffice\PhpSpreadsheet\Worksheet\Drawing $objDrawing
+     * @param SimpleXMLElement $cellAnchor
+     * @param array $hyperlinks
+     */
+    private function readHyperLinkDrawing($objDrawing, $cellAnchor, $hyperlinks): void
+    {
+        $hlinkClick = $cellAnchor->pic->nvPicPr->cNvPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->hlinkClick;
+
+        if ($hlinkClick->count() === 0) {
+            return;
+        }
+
+        $hlinkId = (string) $hlinkClick->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships')['id'];
+        $hyperlink = new Hyperlink(
+            $hyperlinks[$hlinkId],
+            (string) self::getArrayItem($cellAnchor->pic->nvPicPr->cNvPr->attributes(), 'name')
+        );
+        $objDrawing->setHyperlink($hyperlink);
+    }
+
+    private function readProtection(Spreadsheet $excel, SimpleXMLElement $xmlWorkbook): void
+    {
+        if (!$xmlWorkbook->workbookProtection) {
+            return;
+        }
+
+        if ($xmlWorkbook->workbookProtection['lockRevision']) {
+            $excel->getSecurity()->setLockRevision((bool) $xmlWorkbook->workbookProtection['lockRevision']);
+        }
+
+        if ($xmlWorkbook->workbookProtection['lockStructure']) {
+            $excel->getSecurity()->setLockStructure((bool) $xmlWorkbook->workbookProtection['lockStructure']);
+        }
+
+        if ($xmlWorkbook->workbookProtection['lockWindows']) {
+            $excel->getSecurity()->setLockWindows((bool) $xmlWorkbook->workbookProtection['lockWindows']);
+        }
+
+        if ($xmlWorkbook->workbookProtection['revisionsPassword']) {
+            $excel->getSecurity()->setRevisionsPassword((string) $xmlWorkbook->workbookProtection['revisionsPassword'], true);
+        }
+
+        if ($xmlWorkbook->workbookProtection['workbookPassword']) {
+            $excel->getSecurity()->setWorkbookPassword((string) $xmlWorkbook->workbookProtection['workbookPassword'], true);
+        }
+    }
+
+    private function readFormControlProperties(Spreadsheet $excel, ZipArchive $zip, $dir, $fileWorksheet, $docSheet, array &$unparsedLoadedData): void
+    {
+        if (!$zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
+            return;
+        }
+
+        //~ http://schemas.openxmlformats.org/package/2006/relationships"
+        $relsWorksheet = simplexml_load_string(
+            $this->securityScanner->scan(
+                $this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')
+            ),
+            'SimpleXMLElement',
+            Settings::getLibXmlLoaderOptions()
+        );
+        $ctrlProps = [];
+        foreach ($relsWorksheet->Relationship as $ele) {
+            if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/ctrlProp') {
+                $ctrlProps[(string) $ele['Id']] = $ele;
+            }
+        }
+
+        $unparsedCtrlProps = &$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['ctrlProps'];
+        foreach ($ctrlProps as $rId => $ctrlProp) {
+            $rId = substr($rId, 3); // rIdXXX
+            $unparsedCtrlProps[$rId] = [];
+            $unparsedCtrlProps[$rId]['filePath'] = self::dirAdd("$dir/$fileWorksheet", $ctrlProp['Target']);
+            $unparsedCtrlProps[$rId]['relFilePath'] = (string) $ctrlProp['Target'];
+            $unparsedCtrlProps[$rId]['content'] = $this->securityScanner->scan($this->getFromZipArchive($zip, $unparsedCtrlProps[$rId]['filePath']));
+        }
+        unset($unparsedCtrlProps);
+    }
+
+    private function readPrinterSettings(Spreadsheet $excel, ZipArchive $zip, $dir, $fileWorksheet, $docSheet, array &$unparsedLoadedData): void
+    {
+        if (!$zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
+            return;
+        }
+
+        //~ http://schemas.openxmlformats.org/package/2006/relationships"
+        $relsWorksheet = simplexml_load_string(
+            $this->securityScanner->scan(
+                $this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')
+            ),
+            'SimpleXMLElement',
+            Settings::getLibXmlLoaderOptions()
+        );
+        $sheetPrinterSettings = [];
+        foreach ($relsWorksheet->Relationship as $ele) {
+            if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/printerSettings') {
+                $sheetPrinterSettings[(string) $ele['Id']] = $ele;
+            }
+        }
+
+        $unparsedPrinterSettings = &$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['printerSettings'];
+        foreach ($sheetPrinterSettings as $rId => $printerSettings) {
+            $rId = substr($rId, 3); // rIdXXX
+            $unparsedPrinterSettings[$rId] = [];
+            $unparsedPrinterSettings[$rId]['filePath'] = self::dirAdd("$dir/$fileWorksheet", $printerSettings['Target']);
+            $unparsedPrinterSettings[$rId]['relFilePath'] = (string) $printerSettings['Target'];
+            $unparsedPrinterSettings[$rId]['content'] = $this->securityScanner->scan($this->getFromZipArchive($zip, $unparsedPrinterSettings[$rId]['filePath']));
+        }
+        unset($unparsedPrinterSettings);
+    }
+
+    /**
+     * Convert an 'xsd:boolean' XML value to a PHP boolean value.
+     * A valid 'xsd:boolean' XML value can be one of the following
+     * four values: 'true', 'false', '1', '0'.  It is case sensitive.
+     *
+     * Note that just doing '(bool) $xsdBoolean' is not safe,
+     * since '(bool) "false"' returns true.
+     *
+     * @see https://www.w3.org/TR/xmlschema11-2/#boolean
+     *
+     * @param string $xsdBoolean An XML string value of type 'xsd:boolean'
+     *
+     * @return bool  Boolean value
+     */
+    private function castXsdBooleanToBool($xsdBoolean)
+    {
+        if ($xsdBoolean === 'false') {
+            return false;
+        }
+
+        return (bool) $xsdBoolean;
+    }
+
+    /**
+     * @param ZipArchive $zip Opened zip archive
+     *
+     * @return string basename of the used excel workbook
+     */
+    private function getWorkbookBaseName(ZipArchive $zip)
+    {
+        $workbookBasename = '';
+
+        // check if it is an OOXML archive
+        $rels = simplexml_load_string(
+            $this->securityScanner->scan(
+                $this->getFromZipArchive($zip, '_rels/.rels')
+            ),
+            'SimpleXMLElement',
+            Settings::getLibXmlLoaderOptions()
+        );
+        if ($rels !== false) {
+            foreach ($rels->Relationship as $rel) {
+                switch ($rel['Type']) {
+                    case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument':
+                        $basename = basename($rel['Target']);
+                        if (preg_match('/workbook.*\.xml/', $basename)) {
+                            $workbookBasename = $basename;
+                        }
+
+                        break;
+                }
+            }
+        }
+
+        return $workbookBasename;
+    }
+
+    private function readSheetProtection(Worksheet $docSheet, SimpleXMLElement $xmlSheet): void
+    {
+        if ($this->readDataOnly || !$xmlSheet->sheetProtection) {
+            return;
+        }
+
+        $algorithmName = (string) $xmlSheet->sheetProtection['algorithmName'];
+        $protection = $docSheet->getProtection();
+        $protection->setAlgorithm($algorithmName);
+
+        if ($algorithmName) {
+            $protection->setPassword((string) $xmlSheet->sheetProtection['hashValue'], true);
+            $protection->setSalt((string) $xmlSheet->sheetProtection['saltValue']);
+            $protection->setSpinCount((int) $xmlSheet->sheetProtection['spinCount']);
+        } else {
+            $protection->setPassword((string) $xmlSheet->sheetProtection['password'], true);
+        }
+
+        if ($xmlSheet->protectedRanges->protectedRange) {
+            foreach ($xmlSheet->protectedRanges->protectedRange as $protectedRange) {
+                $docSheet->protectCells((string) $protectedRange['sqref'], (string) $protectedRange['password'], true);
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php
new file mode 100755
index 0000000..f52bfd4
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php
@@ -0,0 +1,146 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
+use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use SimpleXMLElement;
+
+class AutoFilter
+{
+    private $worksheet;
+
+    private $worksheetXml;
+
+    public function __construct(Worksheet $workSheet, SimpleXMLElement $worksheetXml)
+    {
+        $this->worksheet = $workSheet;
+        $this->worksheetXml = $worksheetXml;
+    }
+
+    public function load(): void
+    {
+        // Remove all "$" in the auto filter range
+        $autoFilterRange = preg_replace('/\$/', '', $this->worksheetXml->autoFilter['ref']);
+        if (strpos($autoFilterRange, ':') !== false) {
+            $this->readAutoFilter($autoFilterRange, $this->worksheetXml);
+        }
+    }
+
+    private function readAutoFilter($autoFilterRange, $xmlSheet): void
+    {
+        $autoFilter = $this->worksheet->getAutoFilter();
+        $autoFilter->setRange($autoFilterRange);
+
+        foreach ($xmlSheet->autoFilter->filterColumn as $filterColumn) {
+            $column = $autoFilter->getColumnByOffset((int) $filterColumn['colId']);
+            //    Check for standard filters
+            if ($filterColumn->filters) {
+                $column->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER);
+                $filters = $filterColumn->filters;
+                if ((isset($filters['blank'])) && ($filters['blank'] == 1)) {
+                    //    Operator is undefined, but always treated as EQUAL
+                    $column->createRule()->setRule(null, '')->setRuleType(Rule::AUTOFILTER_RULETYPE_FILTER);
+                }
+                //    Standard filters are always an OR join, so no join rule needs to be set
+                //    Entries can be either filter elements
+                foreach ($filters->filter as $filterRule) {
+                    //    Operator is undefined, but always treated as EQUAL
+                    $column->createRule()->setRule(null, (string) $filterRule['val'])->setRuleType(Rule::AUTOFILTER_RULETYPE_FILTER);
+                }
+
+                //    Or Date Group elements
+                $this->readDateRangeAutoFilter($filters, $column);
+            }
+
+            //    Check for custom filters
+            $this->readCustomAutoFilter($filterColumn, $column);
+            //    Check for dynamic filters
+            $this->readDynamicAutoFilter($filterColumn, $column);
+            //    Check for dynamic filters
+            $this->readTopTenAutoFilter($filterColumn, $column);
+        }
+    }
+
+    private function readDateRangeAutoFilter(SimpleXMLElement $filters, Column $column): void
+    {
+        foreach ($filters->dateGroupItem as $dateGroupItem) {
+            //    Operator is undefined, but always treated as EQUAL
+            $column->createRule()->setRule(
+                null,
+                [
+                    'year' => (string) $dateGroupItem['year'],
+                    'month' => (string) $dateGroupItem['month'],
+                    'day' => (string) $dateGroupItem['day'],
+                    'hour' => (string) $dateGroupItem['hour'],
+                    'minute' => (string) $dateGroupItem['minute'],
+                    'second' => (string) $dateGroupItem['second'],
+                ],
+                (string) $dateGroupItem['dateTimeGrouping']
+            )->setRuleType(Rule::AUTOFILTER_RULETYPE_DATEGROUP);
+        }
+    }
+
+    private function readCustomAutoFilter(SimpleXMLElement $filterColumn, Column $column): void
+    {
+        if ($filterColumn->customFilters) {
+            $column->setFilterType(Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER);
+            $customFilters = $filterColumn->customFilters;
+            //    Custom filters can an AND or an OR join;
+            //        and there should only ever be one or two entries
+            if ((isset($customFilters['and'])) && ($customFilters['and'] == 1)) {
+                $column->setJoin(Column::AUTOFILTER_COLUMN_JOIN_AND);
+            }
+            foreach ($customFilters->customFilter as $filterRule) {
+                $column->createRule()->setRule(
+                    (string) $filterRule['operator'],
+                    (string) $filterRule['val']
+                )->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
+            }
+        }
+    }
+
+    private function readDynamicAutoFilter(SimpleXMLElement $filterColumn, Column $column): void
+    {
+        if ($filterColumn->dynamicFilter) {
+            $column->setFilterType(Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER);
+            //    We should only ever have one dynamic filter
+            foreach ($filterColumn->dynamicFilter as $filterRule) {
+                //    Operator is undefined, but always treated as EQUAL
+                $column->createRule()->setRule(
+                    null,
+                    (string) $filterRule['val'],
+                    (string) $filterRule['type']
+                )->setRuleType(Rule::AUTOFILTER_RULETYPE_DYNAMICFILTER);
+                if (isset($filterRule['val'])) {
+                    $column->setAttribute('val', (string) $filterRule['val']);
+                }
+                if (isset($filterRule['maxVal'])) {
+                    $column->setAttribute('maxVal', (string) $filterRule['maxVal']);
+                }
+            }
+        }
+    }
+
+    private function readTopTenAutoFilter(SimpleXMLElement $filterColumn, Column $column): void
+    {
+        if ($filterColumn->top10) {
+            $column->setFilterType(Column::AUTOFILTER_FILTERTYPE_TOPTENFILTER);
+            //    We should only ever have one top10 filter
+            foreach ($filterColumn->top10 as $filterRule) {
+                $column->createRule()->setRule(
+                    (((isset($filterRule['percent'])) && ($filterRule['percent'] == 1))
+                        ? Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT
+                        : Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE
+                    ),
+                    (string) $filterRule['val'],
+                    (((isset($filterRule['top'])) && ($filterRule['top'] == 1))
+                        ? Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP
+                        : Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BOTTOM
+                    )
+                )->setRuleType(Rule::AUTOFILTER_RULETYPE_TOPTENFILTER);
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/BaseParserClass.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/BaseParserClass.php
new file mode 100755
index 0000000..1679f01
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/BaseParserClass.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
+
+class BaseParserClass
+{
+    protected static function boolean($value)
+    {
+        if (is_object($value)) {
+            $value = (string) $value;
+        }
+
+        if (is_numeric($value)) {
+            return (bool) $value;
+        }
+
+        return $value === strtolower('true');
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Chart.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Chart.php
new file mode 100755
index 0000000..c9a230c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Chart.php
@@ -0,0 +1,567 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
+use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
+use PhpOffice\PhpSpreadsheet\Chart\Layout;
+use PhpOffice\PhpSpreadsheet\Chart\Legend;
+use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
+use PhpOffice\PhpSpreadsheet\Chart\Title;
+use PhpOffice\PhpSpreadsheet\RichText\RichText;
+use PhpOffice\PhpSpreadsheet\Style\Color;
+use PhpOffice\PhpSpreadsheet\Style\Font;
+use SimpleXMLElement;
+
+class Chart
+{
+    /**
+     * @param string $name
+     * @param string $format
+     *
+     * @return null|bool|float|int|string
+     */
+    private static function getAttribute(SimpleXMLElement $component, $name, $format)
+    {
+        $attributes = $component->attributes();
+        if (isset($attributes[$name])) {
+            if ($format == 'string') {
+                return (string) $attributes[$name];
+            } elseif ($format == 'integer') {
+                return (int) $attributes[$name];
+            } elseif ($format == 'boolean') {
+                return (bool) ($attributes[$name] === '0' || $attributes[$name] !== 'true') ? false : true;
+            }
+
+            return (float) $attributes[$name];
+        }
+
+        return null;
+    }
+
+    private static function readColor($color, $background = false)
+    {
+        if (isset($color['rgb'])) {
+            return (string) $color['rgb'];
+        } elseif (isset($color['indexed'])) {
+            return Color::indexedColor($color['indexed'] - 7, $background)->getARGB();
+        }
+    }
+
+    /**
+     * @param string $chartName
+     *
+     * @return \PhpOffice\PhpSpreadsheet\Chart\Chart
+     */
+    public static function readChart(SimpleXMLElement $chartElements, $chartName)
+    {
+        $namespacesChartMeta = $chartElements->getNamespaces(true);
+        $chartElementsC = $chartElements->children($namespacesChartMeta['c']);
+
+        $XaxisLabel = $YaxisLabel = $legend = $title = null;
+        $dispBlanksAs = $plotVisOnly = null;
+
+        foreach ($chartElementsC as $chartElementKey => $chartElement) {
+            switch ($chartElementKey) {
+                case 'chart':
+                    foreach ($chartElement as $chartDetailsKey => $chartDetails) {
+                        $chartDetailsC = $chartDetails->children($namespacesChartMeta['c']);
+                        switch ($chartDetailsKey) {
+                            case 'plotArea':
+                                $plotAreaLayout = $XaxisLable = $YaxisLable = null;
+                                $plotSeries = $plotAttributes = [];
+                                foreach ($chartDetails as $chartDetailKey => $chartDetail) {
+                                    switch ($chartDetailKey) {
+                                        case 'layout':
+                                            $plotAreaLayout = self::chartLayoutDetails($chartDetail, $namespacesChartMeta);
+
+                                            break;
+                                        case 'catAx':
+                                            if (isset($chartDetail->title)) {
+                                                $XaxisLabel = self::chartTitle($chartDetail->title->children($namespacesChartMeta['c']), $namespacesChartMeta);
+                                            }
+
+                                            break;
+                                        case 'dateAx':
+                                            if (isset($chartDetail->title)) {
+                                                $XaxisLabel = self::chartTitle($chartDetail->title->children($namespacesChartMeta['c']), $namespacesChartMeta);
+                                            }
+
+                                            break;
+                                        case 'valAx':
+                                            if (isset($chartDetail->title)) {
+                                                $YaxisLabel = self::chartTitle($chartDetail->title->children($namespacesChartMeta['c']), $namespacesChartMeta);
+                                            }
+
+                                            break;
+                                        case 'barChart':
+                                        case 'bar3DChart':
+                                            $barDirection = self::getAttribute($chartDetail->barDir, 'val', 'string');
+                                            $plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
+                                            $plotSer->setPlotDirection($barDirection);
+                                            $plotSeries[] = $plotSer;
+                                            $plotAttributes = self::readChartAttributes($chartDetail);
+
+                                            break;
+                                        case 'lineChart':
+                                        case 'line3DChart':
+                                            $plotSeries[] = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
+                                            $plotAttributes = self::readChartAttributes($chartDetail);
+
+                                            break;
+                                        case 'areaChart':
+                                        case 'area3DChart':
+                                            $plotSeries[] = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
+                                            $plotAttributes = self::readChartAttributes($chartDetail);
+
+                                            break;
+                                        case 'doughnutChart':
+                                        case 'pieChart':
+                                        case 'pie3DChart':
+                                            $explosion = isset($chartDetail->ser->explosion);
+                                            $plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
+                                            $plotSer->setPlotStyle($explosion);
+                                            $plotSeries[] = $plotSer;
+                                            $plotAttributes = self::readChartAttributes($chartDetail);
+
+                                            break;
+                                        case 'scatterChart':
+                                            $scatterStyle = self::getAttribute($chartDetail->scatterStyle, 'val', 'string');
+                                            $plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
+                                            $plotSer->setPlotStyle($scatterStyle);
+                                            $plotSeries[] = $plotSer;
+                                            $plotAttributes = self::readChartAttributes($chartDetail);
+
+                                            break;
+                                        case 'bubbleChart':
+                                            $bubbleScale = self::getAttribute($chartDetail->bubbleScale, 'val', 'integer');
+                                            $plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
+                                            $plotSer->setPlotStyle($bubbleScale);
+                                            $plotSeries[] = $plotSer;
+                                            $plotAttributes = self::readChartAttributes($chartDetail);
+
+                                            break;
+                                        case 'radarChart':
+                                            $radarStyle = self::getAttribute($chartDetail->radarStyle, 'val', 'string');
+                                            $plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
+                                            $plotSer->setPlotStyle($radarStyle);
+                                            $plotSeries[] = $plotSer;
+                                            $plotAttributes = self::readChartAttributes($chartDetail);
+
+                                            break;
+                                        case 'surfaceChart':
+                                        case 'surface3DChart':
+                                            $wireFrame = self::getAttribute($chartDetail->wireframe, 'val', 'boolean');
+                                            $plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
+                                            $plotSer->setPlotStyle($wireFrame);
+                                            $plotSeries[] = $plotSer;
+                                            $plotAttributes = self::readChartAttributes($chartDetail);
+
+                                            break;
+                                        case 'stockChart':
+                                            $plotSeries[] = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
+                                            $plotAttributes = self::readChartAttributes($plotAreaLayout);
+
+                                            break;
+                                    }
+                                }
+                                if ($plotAreaLayout == null) {
+                                    $plotAreaLayout = new Layout();
+                                }
+                                $plotArea = new PlotArea($plotAreaLayout, $plotSeries);
+                                self::setChartAttributes($plotAreaLayout, $plotAttributes);
+
+                                break;
+                            case 'plotVisOnly':
+                                $plotVisOnly = self::getAttribute($chartDetails, 'val', 'string');
+
+                                break;
+                            case 'dispBlanksAs':
+                                $dispBlanksAs = self::getAttribute($chartDetails, 'val', 'string');
+
+                                break;
+                            case 'title':
+                                $title = self::chartTitle($chartDetails, $namespacesChartMeta);
+
+                                break;
+                            case 'legend':
+                                $legendPos = 'r';
+                                $legendLayout = null;
+                                $legendOverlay = false;
+                                foreach ($chartDetails as $chartDetailKey => $chartDetail) {
+                                    switch ($chartDetailKey) {
+                                        case 'legendPos':
+                                            $legendPos = self::getAttribute($chartDetail, 'val', 'string');
+
+                                            break;
+                                        case 'overlay':
+                                            $legendOverlay = self::getAttribute($chartDetail, 'val', 'boolean');
+
+                                            break;
+                                        case 'layout':
+                                            $legendLayout = self::chartLayoutDetails($chartDetail, $namespacesChartMeta);
+
+                                            break;
+                                    }
+                                }
+                                $legend = new Legend($legendPos, $legendLayout, $legendOverlay);
+
+                                break;
+                        }
+                    }
+            }
+        }
+        $chart = new \PhpOffice\PhpSpreadsheet\Chart\Chart($chartName, $title, $legend, $plotArea, $plotVisOnly, $dispBlanksAs, $XaxisLabel, $YaxisLabel);
+
+        return $chart;
+    }
+
+    private static function chartTitle(SimpleXMLElement $titleDetails, array $namespacesChartMeta)
+    {
+        $caption = [];
+        $titleLayout = null;
+        foreach ($titleDetails as $titleDetailKey => $chartDetail) {
+            switch ($titleDetailKey) {
+                case 'tx':
+                    $titleDetails = $chartDetail->rich->children($namespacesChartMeta['a']);
+                    foreach ($titleDetails as $titleKey => $titleDetail) {
+                        switch ($titleKey) {
+                            case 'p':
+                                $titleDetailPart = $titleDetail->children($namespacesChartMeta['a']);
+                                $caption[] = self::parseRichText($titleDetailPart);
+                        }
+                    }
+
+                    break;
+                case 'layout':
+                    $titleLayout = self::chartLayoutDetails($chartDetail, $namespacesChartMeta);
+
+                    break;
+            }
+        }
+
+        return new Title($caption, $titleLayout);
+    }
+
+    private static function chartLayoutDetails($chartDetail, $namespacesChartMeta)
+    {
+        if (!isset($chartDetail->manualLayout)) {
+            return null;
+        }
+        $details = $chartDetail->manualLayout->children($namespacesChartMeta['c']);
+        if ($details === null) {
+            return null;
+        }
+        $layout = [];
+        foreach ($details as $detailKey => $detail) {
+            $layout[$detailKey] = self::getAttribute($detail, 'val', 'string');
+        }
+
+        return new Layout($layout);
+    }
+
+    private static function chartDataSeries($chartDetail, $namespacesChartMeta, $plotType)
+    {
+        $multiSeriesType = null;
+        $smoothLine = false;
+        $seriesLabel = $seriesCategory = $seriesValues = $plotOrder = [];
+
+        $seriesDetailSet = $chartDetail->children($namespacesChartMeta['c']);
+        foreach ($seriesDetailSet as $seriesDetailKey => $seriesDetails) {
+            switch ($seriesDetailKey) {
+                case 'grouping':
+                    $multiSeriesType = self::getAttribute($chartDetail->grouping, 'val', 'string');
+
+                    break;
+                case 'ser':
+                    $marker = null;
+                    $seriesIndex = '';
+                    foreach ($seriesDetails as $seriesKey => $seriesDetail) {
+                        switch ($seriesKey) {
+                            case 'idx':
+                                $seriesIndex = self::getAttribute($seriesDetail, 'val', 'integer');
+
+                                break;
+                            case 'order':
+                                $seriesOrder = self::getAttribute($seriesDetail, 'val', 'integer');
+                                $plotOrder[$seriesIndex] = $seriesOrder;
+
+                                break;
+                            case 'tx':
+                                $seriesLabel[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta);
+
+                                break;
+                            case 'marker':
+                                $marker = self::getAttribute($seriesDetail->symbol, 'val', 'string');
+
+                                break;
+                            case 'smooth':
+                                $smoothLine = self::getAttribute($seriesDetail, 'val', 'boolean');
+
+                                break;
+                            case 'cat':
+                                $seriesCategory[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta);
+
+                                break;
+                            case 'val':
+                                $seriesValues[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker);
+
+                                break;
+                            case 'xVal':
+                                $seriesCategory[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker);
+
+                                break;
+                            case 'yVal':
+                                $seriesValues[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker);
+
+                                break;
+                        }
+                    }
+            }
+        }
+
+        return new DataSeries($plotType, $multiSeriesType, $plotOrder, $seriesLabel, $seriesCategory, $seriesValues, $smoothLine);
+    }
+
+    private static function chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker = null)
+    {
+        if (isset($seriesDetail->strRef)) {
+            $seriesSource = (string) $seriesDetail->strRef->f;
+            $seriesData = self::chartDataSeriesValues($seriesDetail->strRef->strCache->children($namespacesChartMeta['c']), 's');
+
+            return new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, $seriesData['formatCode'], $seriesData['pointCount'], $seriesData['dataValues'], $marker);
+        } elseif (isset($seriesDetail->numRef)) {
+            $seriesSource = (string) $seriesDetail->numRef->f;
+            $seriesData = self::chartDataSeriesValues($seriesDetail->numRef->numCache->children($namespacesChartMeta['c']));
+
+            return new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, $seriesSource, $seriesData['formatCode'], $seriesData['pointCount'], $seriesData['dataValues'], $marker);
+        } elseif (isset($seriesDetail->multiLvlStrRef)) {
+            $seriesSource = (string) $seriesDetail->multiLvlStrRef->f;
+            $seriesData = self::chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlStrRef->multiLvlStrCache->children($namespacesChartMeta['c']), 's');
+            $seriesData['pointCount'] = count($seriesData['dataValues']);
+
+            return new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, $seriesData['formatCode'], $seriesData['pointCount'], $seriesData['dataValues'], $marker);
+        } elseif (isset($seriesDetail->multiLvlNumRef)) {
+            $seriesSource = (string) $seriesDetail->multiLvlNumRef->f;
+            $seriesData = self::chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlNumRef->multiLvlNumCache->children($namespacesChartMeta['c']), 's');
+            $seriesData['pointCount'] = count($seriesData['dataValues']);
+
+            return new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, $seriesData['formatCode'], $seriesData['pointCount'], $seriesData['dataValues'], $marker);
+        }
+
+        return null;
+    }
+
+    private static function chartDataSeriesValues($seriesValueSet, $dataType = 'n')
+    {
+        $seriesVal = [];
+        $formatCode = '';
+        $pointCount = 0;
+
+        foreach ($seriesValueSet as $seriesValueIdx => $seriesValue) {
+            switch ($seriesValueIdx) {
+                case 'ptCount':
+                    $pointCount = self::getAttribute($seriesValue, 'val', 'integer');
+
+                    break;
+                case 'formatCode':
+                    $formatCode = (string) $seriesValue;
+
+                    break;
+                case 'pt':
+                    $pointVal = self::getAttribute($seriesValue, 'idx', 'integer');
+                    if ($dataType == 's') {
+                        $seriesVal[$pointVal] = (string) $seriesValue->v;
+                    } elseif ($seriesValue->v === Functions::NA()) {
+                        $seriesVal[$pointVal] = null;
+                    } else {
+                        $seriesVal[$pointVal] = (float) $seriesValue->v;
+                    }
+
+                    break;
+            }
+        }
+
+        return [
+            'formatCode' => $formatCode,
+            'pointCount' => $pointCount,
+            'dataValues' => $seriesVal,
+        ];
+    }
+
+    private static function chartDataSeriesValuesMultiLevel($seriesValueSet, $dataType = 'n')
+    {
+        $seriesVal = [];
+        $formatCode = '';
+        $pointCount = 0;
+
+        foreach ($seriesValueSet->lvl as $seriesLevelIdx => $seriesLevel) {
+            foreach ($seriesLevel as $seriesValueIdx => $seriesValue) {
+                switch ($seriesValueIdx) {
+                    case 'ptCount':
+                        $pointCount = self::getAttribute($seriesValue, 'val', 'integer');
+
+                        break;
+                    case 'formatCode':
+                        $formatCode = (string) $seriesValue;
+
+                        break;
+                    case 'pt':
+                        $pointVal = self::getAttribute($seriesValue, 'idx', 'integer');
+                        if ($dataType == 's') {
+                            $seriesVal[$pointVal][] = (string) $seriesValue->v;
+                        } elseif ($seriesValue->v === Functions::NA()) {
+                            $seriesVal[$pointVal] = null;
+                        } else {
+                            $seriesVal[$pointVal][] = (float) $seriesValue->v;
+                        }
+
+                        break;
+                }
+            }
+        }
+
+        return [
+            'formatCode' => $formatCode,
+            'pointCount' => $pointCount,
+            'dataValues' => $seriesVal,
+        ];
+    }
+
+    private static function parseRichText(SimpleXMLElement $titleDetailPart)
+    {
+        $value = new RichText();
+        $objText = null;
+        foreach ($titleDetailPart as $titleDetailElementKey => $titleDetailElement) {
+            if (isset($titleDetailElement->t)) {
+                $objText = $value->createTextRun((string) $titleDetailElement->t);
+            }
+            if (isset($titleDetailElement->rPr)) {
+                if (isset($titleDetailElement->rPr->rFont['val'])) {
+                    $objText->getFont()->setName((string) $titleDetailElement->rPr->rFont['val']);
+                }
+
+                $fontSize = (self::getAttribute($titleDetailElement->rPr, 'sz', 'integer'));
+                if ($fontSize !== null) {
+                    $objText->getFont()->setSize(floor($fontSize / 100));
+                }
+
+                $fontColor = (self::getAttribute($titleDetailElement->rPr, 'color', 'string'));
+                if ($fontColor !== null) {
+                    $objText->getFont()->setColor(new Color(self::readColor($fontColor)));
+                }
+
+                $bold = self::getAttribute($titleDetailElement->rPr, 'b', 'boolean');
+                if ($bold !== null) {
+                    $objText->getFont()->setBold($bold);
+                }
+
+                $italic = self::getAttribute($titleDetailElement->rPr, 'i', 'boolean');
+                if ($italic !== null) {
+                    $objText->getFont()->setItalic($italic);
+                }
+
+                $baseline = self::getAttribute($titleDetailElement->rPr, 'baseline', 'integer');
+                if ($baseline !== null) {
+                    if ($baseline > 0) {
+                        $objText->getFont()->setSuperscript(true);
+                    } elseif ($baseline < 0) {
+                        $objText->getFont()->setSubscript(true);
+                    }
+                }
+
+                $underscore = (self::getAttribute($titleDetailElement->rPr, 'u', 'string'));
+                if ($underscore !== null) {
+                    if ($underscore == 'sng') {
+                        $objText->getFont()->setUnderline(Font::UNDERLINE_SINGLE);
+                    } elseif ($underscore == 'dbl') {
+                        $objText->getFont()->setUnderline(Font::UNDERLINE_DOUBLE);
+                    } else {
+                        $objText->getFont()->setUnderline(Font::UNDERLINE_NONE);
+                    }
+                }
+
+                $strikethrough = (self::getAttribute($titleDetailElement->rPr, 's', 'string'));
+                if ($strikethrough !== null) {
+                    if ($strikethrough == 'noStrike') {
+                        $objText->getFont()->setStrikethrough(false);
+                    } else {
+                        $objText->getFont()->setStrikethrough(true);
+                    }
+                }
+            }
+        }
+
+        return $value;
+    }
+
+    private static function readChartAttributes($chartDetail)
+    {
+        $plotAttributes = [];
+        if (isset($chartDetail->dLbls)) {
+            if (isset($chartDetail->dLbls->howLegendKey)) {
+                $plotAttributes['showLegendKey'] = self::getAttribute($chartDetail->dLbls->showLegendKey, 'val', 'string');
+            }
+            if (isset($chartDetail->dLbls->showVal)) {
+                $plotAttributes['showVal'] = self::getAttribute($chartDetail->dLbls->showVal, 'val', 'string');
+            }
+            if (isset($chartDetail->dLbls->showCatName)) {
+                $plotAttributes['showCatName'] = self::getAttribute($chartDetail->dLbls->showCatName, 'val', 'string');
+            }
+            if (isset($chartDetail->dLbls->showSerName)) {
+                $plotAttributes['showSerName'] = self::getAttribute($chartDetail->dLbls->showSerName, 'val', 'string');
+            }
+            if (isset($chartDetail->dLbls->showPercent)) {
+                $plotAttributes['showPercent'] = self::getAttribute($chartDetail->dLbls->showPercent, 'val', 'string');
+            }
+            if (isset($chartDetail->dLbls->showBubbleSize)) {
+                $plotAttributes['showBubbleSize'] = self::getAttribute($chartDetail->dLbls->showBubbleSize, 'val', 'string');
+            }
+            if (isset($chartDetail->dLbls->showLeaderLines)) {
+                $plotAttributes['showLeaderLines'] = self::getAttribute($chartDetail->dLbls->showLeaderLines, 'val', 'string');
+            }
+        }
+
+        return $plotAttributes;
+    }
+
+    /**
+     * @param mixed $plotAttributes
+     */
+    private static function setChartAttributes(Layout $plotArea, $plotAttributes): void
+    {
+        foreach ($plotAttributes as $plotAttributeKey => $plotAttributeValue) {
+            switch ($plotAttributeKey) {
+                case 'showLegendKey':
+                    $plotArea->setShowLegendKey($plotAttributeValue);
+
+                    break;
+                case 'showVal':
+                    $plotArea->setShowVal($plotAttributeValue);
+
+                    break;
+                case 'showCatName':
+                    $plotArea->setShowCatName($plotAttributeValue);
+
+                    break;
+                case 'showSerName':
+                    $plotArea->setShowSerName($plotAttributeValue);
+
+                    break;
+                case 'showPercent':
+                    $plotArea->setShowPercent($plotAttributeValue);
+
+                    break;
+                case 'showBubbleSize':
+                    $plotArea->setShowBubbleSize($plotAttributeValue);
+
+                    break;
+                case 'showLeaderLines':
+                    $plotArea->setShowLeaderLines($plotAttributeValue);
+
+                    break;
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php
new file mode 100755
index 0000000..339c058
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php
@@ -0,0 +1,205 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Reader\IReadFilter;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use SimpleXMLElement;
+
+class ColumnAndRowAttributes extends BaseParserClass
+{
+    private $worksheet;
+
+    private $worksheetXml;
+
+    public function __construct(Worksheet $workSheet, ?SimpleXMLElement $worksheetXml = null)
+    {
+        $this->worksheet = $workSheet;
+        $this->worksheetXml = $worksheetXml;
+    }
+
+    /**
+     * Set Worksheet column attributes by attributes array passed.
+     *
+     * @param string $columnAddress A, B, ... DX, ...
+     * @param array $columnAttributes array of attributes (indexes are attribute name, values are value)
+     *                               'xfIndex', 'visible', 'collapsed', 'outlineLevel', 'width', ... ?
+     */
+    private function setColumnAttributes($columnAddress, array $columnAttributes): void
+    {
+        if (isset($columnAttributes['xfIndex'])) {
+            $this->worksheet->getColumnDimension($columnAddress)->setXfIndex($columnAttributes['xfIndex']);
+        }
+        if (isset($columnAttributes['visible'])) {
+            $this->worksheet->getColumnDimension($columnAddress)->setVisible($columnAttributes['visible']);
+        }
+        if (isset($columnAttributes['collapsed'])) {
+            $this->worksheet->getColumnDimension($columnAddress)->setCollapsed($columnAttributes['collapsed']);
+        }
+        if (isset($columnAttributes['outlineLevel'])) {
+            $this->worksheet->getColumnDimension($columnAddress)->setOutlineLevel($columnAttributes['outlineLevel']);
+        }
+        if (isset($columnAttributes['width'])) {
+            $this->worksheet->getColumnDimension($columnAddress)->setWidth($columnAttributes['width']);
+        }
+    }
+
+    /**
+     * Set Worksheet row attributes by attributes array passed.
+     *
+     * @param int $rowNumber 1, 2, 3, ... 99, ...
+     * @param array $rowAttributes array of attributes (indexes are attribute name, values are value)
+     *                               'xfIndex', 'visible', 'collapsed', 'outlineLevel', 'rowHeight', ... ?
+     */
+    private function setRowAttributes($rowNumber, array $rowAttributes): void
+    {
+        if (isset($rowAttributes['xfIndex'])) {
+            $this->worksheet->getRowDimension($rowNumber)->setXfIndex($rowAttributes['xfIndex']);
+        }
+        if (isset($rowAttributes['visible'])) {
+            $this->worksheet->getRowDimension($rowNumber)->setVisible($rowAttributes['visible']);
+        }
+        if (isset($rowAttributes['collapsed'])) {
+            $this->worksheet->getRowDimension($rowNumber)->setCollapsed($rowAttributes['collapsed']);
+        }
+        if (isset($rowAttributes['outlineLevel'])) {
+            $this->worksheet->getRowDimension($rowNumber)->setOutlineLevel($rowAttributes['outlineLevel']);
+        }
+        if (isset($rowAttributes['rowHeight'])) {
+            $this->worksheet->getRowDimension($rowNumber)->setRowHeight($rowAttributes['rowHeight']);
+        }
+    }
+
+    /**
+     * @param IReadFilter $readFilter
+     * @param bool $readDataOnly
+     */
+    public function load(?IReadFilter $readFilter = null, $readDataOnly = false): void
+    {
+        if ($this->worksheetXml === null) {
+            return;
+        }
+
+        $columnsAttributes = [];
+        $rowsAttributes = [];
+        if (isset($this->worksheetXml->cols)) {
+            $columnsAttributes = $this->readColumnAttributes($this->worksheetXml->cols, $readDataOnly);
+        }
+
+        if ($this->worksheetXml->sheetData && $this->worksheetXml->sheetData->row) {
+            $rowsAttributes = $this->readRowAttributes($this->worksheetXml->sheetData->row, $readDataOnly);
+        }
+
+        // set columns/rows attributes
+        $columnsAttributesAreSet = [];
+        foreach ($columnsAttributes as $columnCoordinate => $columnAttributes) {
+            if ($readFilter === null ||
+                !$this->isFilteredColumn($readFilter, $columnCoordinate, $rowsAttributes)) {
+                if (!isset($columnsAttributesAreSet[$columnCoordinate])) {
+                    $this->setColumnAttributes($columnCoordinate, $columnAttributes);
+                    $columnsAttributesAreSet[$columnCoordinate] = true;
+                }
+            }
+        }
+
+        $rowsAttributesAreSet = [];
+        foreach ($rowsAttributes as $rowCoordinate => $rowAttributes) {
+            if ($readFilter === null ||
+                !$this->isFilteredRow($readFilter, $rowCoordinate, $columnsAttributes)) {
+                if (!isset($rowsAttributesAreSet[$rowCoordinate])) {
+                    $this->setRowAttributes($rowCoordinate, $rowAttributes);
+                    $rowsAttributesAreSet[$rowCoordinate] = true;
+                }
+            }
+        }
+    }
+
+    private function isFilteredColumn(IReadFilter $readFilter, $columnCoordinate, array $rowsAttributes)
+    {
+        foreach ($rowsAttributes as $rowCoordinate => $rowAttributes) {
+            if (!$readFilter->readCell($columnCoordinate, $rowCoordinate, $this->worksheet->getTitle())) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private function readColumnAttributes(SimpleXMLElement $worksheetCols, $readDataOnly)
+    {
+        $columnAttributes = [];
+
+        foreach ($worksheetCols->col as $column) {
+            $startColumn = Coordinate::stringFromColumnIndex((int) $column['min']);
+            $endColumn = Coordinate::stringFromColumnIndex((int) $column['max']);
+            ++$endColumn;
+            for ($columnAddress = $startColumn; $columnAddress !== $endColumn; ++$columnAddress) {
+                $columnAttributes[$columnAddress] = $this->readColumnRangeAttributes($column, $readDataOnly);
+
+                if ((int) ($column['max']) == 16384) {
+                    break;
+                }
+            }
+        }
+
+        return $columnAttributes;
+    }
+
+    private function readColumnRangeAttributes(SimpleXMLElement $column, $readDataOnly)
+    {
+        $columnAttributes = [];
+
+        if ($column['style'] && !$readDataOnly) {
+            $columnAttributes['xfIndex'] = (int) $column['style'];
+        }
+        if (self::boolean($column['hidden'])) {
+            $columnAttributes['visible'] = false;
+        }
+        if (self::boolean($column['collapsed'])) {
+            $columnAttributes['collapsed'] = true;
+        }
+        if (((int) $column['outlineLevel']) > 0) {
+            $columnAttributes['outlineLevel'] = (int) $column['outlineLevel'];
+        }
+        $columnAttributes['width'] = (float) $column['width'];
+
+        return $columnAttributes;
+    }
+
+    private function isFilteredRow(IReadFilter $readFilter, $rowCoordinate, array $columnsAttributes)
+    {
+        foreach ($columnsAttributes as $columnCoordinate => $columnAttributes) {
+            if (!$readFilter->readCell($columnCoordinate, $rowCoordinate, $this->worksheet->getTitle())) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private function readRowAttributes(SimpleXMLElement $worksheetRow, $readDataOnly)
+    {
+        $rowAttributes = [];
+
+        foreach ($worksheetRow as $row) {
+            if ($row['ht'] && !$readDataOnly) {
+                $rowAttributes[(int) $row['r']]['rowHeight'] = (float) $row['ht'];
+            }
+            if (self::boolean($row['hidden'])) {
+                $rowAttributes[(int) $row['r']]['visible'] = false;
+            }
+            if (self::boolean($row['collapsed'])) {
+                $rowAttributes[(int) $row['r']]['collapsed'] = true;
+            }
+            if ((int) $row['outlineLevel'] > 0) {
+                $rowAttributes[(int) $row['r']]['outlineLevel'] = (int) $row['outlineLevel'];
+            }
+            if ($row['s'] && !$readDataOnly) {
+                $rowAttributes[(int) $row['r']]['xfIndex'] = (int) $row['s'];
+            }
+        }
+
+        return $rowAttributes;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php
new file mode 100755
index 0000000..668bfd1
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php
@@ -0,0 +1,95 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Style\Conditional;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use SimpleXMLElement;
+
+class ConditionalStyles
+{
+    private $worksheet;
+
+    private $worksheetXml;
+
+    private $dxfs;
+
+    public function __construct(Worksheet $workSheet, SimpleXMLElement $worksheetXml, array $dxfs = [])
+    {
+        $this->worksheet = $workSheet;
+        $this->worksheetXml = $worksheetXml;
+        $this->dxfs = $dxfs;
+    }
+
+    public function load(): void
+    {
+        $this->setConditionalStyles(
+            $this->worksheet,
+            $this->readConditionalStyles($this->worksheetXml)
+        );
+    }
+
+    private function readConditionalStyles($xmlSheet)
+    {
+        $conditionals = [];
+        foreach ($xmlSheet->conditionalFormatting as $conditional) {
+            foreach ($conditional->cfRule as $cfRule) {
+                if (((string) $cfRule['type'] == Conditional::CONDITION_NONE
+                    || (string) $cfRule['type'] == Conditional::CONDITION_CELLIS
+                    || (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSTEXT
+                    || (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSBLANKS
+                    || (string) $cfRule['type'] == Conditional::CONDITION_NOTCONTAINSBLANKS
+                    || (string) $cfRule['type'] == Conditional::CONDITION_EXPRESSION)
+                    && isset($this->dxfs[(int) ($cfRule['dxfId'])])) {
+                    $conditionals[(string) $conditional['sqref']][(int) ($cfRule['priority'])] = $cfRule;
+                }
+            }
+        }
+
+        return $conditionals;
+    }
+
+    private function setConditionalStyles(Worksheet $worksheet, array $conditionals): void
+    {
+        foreach ($conditionals as $ref => $cfRules) {
+            ksort($cfRules);
+            $conditionalStyles = $this->readStyleRules($cfRules);
+
+            // Extract all cell references in $ref
+            $cellBlocks = explode(' ', str_replace('$', '', strtoupper($ref)));
+            foreach ($cellBlocks as $cellBlock) {
+                $worksheet->getStyle($cellBlock)->setConditionalStyles($conditionalStyles);
+            }
+        }
+    }
+
+    private function readStyleRules($cfRules)
+    {
+        $conditionalStyles = [];
+        foreach ($cfRules as $cfRule) {
+            $objConditional = new Conditional();
+            $objConditional->setConditionType((string) $cfRule['type']);
+            $objConditional->setOperatorType((string) $cfRule['operator']);
+
+            if ((string) $cfRule['text'] != '') {
+                $objConditional->setText((string) $cfRule['text']);
+            }
+
+            if (isset($cfRule['stopIfTrue']) && (int) $cfRule['stopIfTrue'] === 1) {
+                $objConditional->setStopIfTrue(true);
+            }
+
+            if (count($cfRule->formula) > 1) {
+                foreach ($cfRule->formula as $formula) {
+                    $objConditional->addCondition((string) $formula);
+                }
+            } else {
+                $objConditional->addCondition((string) $cfRule->formula);
+            }
+            $objConditional->setStyle(clone $this->dxfs[(int) ($cfRule['dxfId'])]);
+            $conditionalStyles[] = $objConditional;
+        }
+
+        return $conditionalStyles;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/DataValidations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/DataValidations.php
new file mode 100755
index 0000000..41a8c9f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/DataValidations.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use SimpleXMLElement;
+
+class DataValidations
+{
+    private $worksheet;
+
+    private $worksheetXml;
+
+    public function __construct(Worksheet $workSheet, SimpleXMLElement $worksheetXml)
+    {
+        $this->worksheet = $workSheet;
+        $this->worksheetXml = $worksheetXml;
+    }
+
+    public function load(): void
+    {
+        foreach ($this->worksheetXml->dataValidations->dataValidation as $dataValidation) {
+            // Uppercase coordinate
+            $range = strtoupper($dataValidation['sqref']);
+            $rangeSet = explode(' ', $range);
+            foreach ($rangeSet as $range) {
+                $stRange = $this->worksheet->shrinkRangeToFit($range);
+
+                // Extract all cell references in $range
+                foreach (Coordinate::extractAllCellReferencesInRange($stRange) as $reference) {
+                    // Create validation
+                    $docValidation = $this->worksheet->getCell($reference)->getDataValidation();
+                    $docValidation->setType((string) $dataValidation['type']);
+                    $docValidation->setErrorStyle((string) $dataValidation['errorStyle']);
+                    $docValidation->setOperator((string) $dataValidation['operator']);
+                    $docValidation->setAllowBlank($dataValidation['allowBlank'] != 0);
+                    $docValidation->setShowDropDown($dataValidation['showDropDown'] == 0);
+                    $docValidation->setShowInputMessage($dataValidation['showInputMessage'] != 0);
+                    $docValidation->setShowErrorMessage($dataValidation['showErrorMessage'] != 0);
+                    $docValidation->setErrorTitle((string) $dataValidation['errorTitle']);
+                    $docValidation->setError((string) $dataValidation['error']);
+                    $docValidation->setPromptTitle((string) $dataValidation['promptTitle']);
+                    $docValidation->setPrompt((string) $dataValidation['prompt']);
+                    $docValidation->setFormula1((string) $dataValidation->formula1);
+                    $docValidation->setFormula2((string) $dataValidation->formula2);
+                }
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php
new file mode 100755
index 0000000..9e6aeaf
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use SimpleXMLElement;
+
+class Hyperlinks
+{
+    private $worksheet;
+
+    private $hyperlinks = [];
+
+    public function __construct(Worksheet $workSheet)
+    {
+        $this->worksheet = $workSheet;
+    }
+
+    public function readHyperlinks(SimpleXMLElement $relsWorksheet): void
+    {
+        foreach ($relsWorksheet->Relationship as $element) {
+            if ($element['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink') {
+                $this->hyperlinks[(string) $element['Id']] = (string) $element['Target'];
+            }
+        }
+    }
+
+    public function setHyperlinks(SimpleXMLElement $worksheetXml): void
+    {
+        foreach ($worksheetXml->hyperlink as $hyperlink) {
+            $this->setHyperlink($hyperlink, $this->worksheet);
+        }
+    }
+
+    private function setHyperlink(SimpleXMLElement $hyperlink, Worksheet $worksheet): void
+    {
+        // Link url
+        $linkRel = $hyperlink->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships');
+
+        foreach (Coordinate::extractAllCellReferencesInRange($hyperlink['ref']) as $cellReference) {
+            $cell = $worksheet->getCell($cellReference);
+            if (isset($linkRel['id'])) {
+                $hyperlinkUrl = $this->hyperlinks[(string) $linkRel['id']];
+                if (isset($hyperlink['location'])) {
+                    $hyperlinkUrl .= '#' . (string) $hyperlink['location'];
+                }
+                $cell->getHyperlink()->setUrl($hyperlinkUrl);
+            } elseif (isset($hyperlink['location'])) {
+                $cell->getHyperlink()->setUrl('sheet://' . (string) $hyperlink['location']);
+            }
+
+            // Tooltip
+            if (isset($hyperlink['tooltip'])) {
+                $cell->getHyperlink()->setTooltip((string) $hyperlink['tooltip']);
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php
new file mode 100755
index 0000000..bfb7a1f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php
@@ -0,0 +1,151 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use SimpleXMLElement;
+
+class PageSetup extends BaseParserClass
+{
+    private $worksheet;
+
+    private $worksheetXml;
+
+    public function __construct(Worksheet $workSheet, ?SimpleXMLElement $worksheetXml = null)
+    {
+        $this->worksheet = $workSheet;
+        $this->worksheetXml = $worksheetXml;
+    }
+
+    public function load(array $unparsedLoadedData)
+    {
+        if (!$this->worksheetXml) {
+            return $unparsedLoadedData;
+        }
+
+        $this->margins($this->worksheetXml, $this->worksheet);
+        $unparsedLoadedData = $this->pageSetup($this->worksheetXml, $this->worksheet, $unparsedLoadedData);
+        $this->headerFooter($this->worksheetXml, $this->worksheet);
+        $this->pageBreaks($this->worksheetXml, $this->worksheet);
+
+        return $unparsedLoadedData;
+    }
+
+    private function margins(SimpleXMLElement $xmlSheet, Worksheet $worksheet): void
+    {
+        if ($xmlSheet->pageMargins) {
+            $docPageMargins = $worksheet->getPageMargins();
+            $docPageMargins->setLeft((float) ($xmlSheet->pageMargins['left']));
+            $docPageMargins->setRight((float) ($xmlSheet->pageMargins['right']));
+            $docPageMargins->setTop((float) ($xmlSheet->pageMargins['top']));
+            $docPageMargins->setBottom((float) ($xmlSheet->pageMargins['bottom']));
+            $docPageMargins->setHeader((float) ($xmlSheet->pageMargins['header']));
+            $docPageMargins->setFooter((float) ($xmlSheet->pageMargins['footer']));
+        }
+    }
+
+    private function pageSetup(SimpleXMLElement $xmlSheet, Worksheet $worksheet, array $unparsedLoadedData)
+    {
+        if ($xmlSheet->pageSetup) {
+            $docPageSetup = $worksheet->getPageSetup();
+
+            if (isset($xmlSheet->pageSetup['orientation'])) {
+                $docPageSetup->setOrientation((string) $xmlSheet->pageSetup['orientation']);
+            }
+            if (isset($xmlSheet->pageSetup['paperSize'])) {
+                $docPageSetup->setPaperSize((int) ($xmlSheet->pageSetup['paperSize']));
+            }
+            if (isset($xmlSheet->pageSetup['scale'])) {
+                $docPageSetup->setScale((int) ($xmlSheet->pageSetup['scale']), false);
+            }
+            if (isset($xmlSheet->pageSetup['fitToHeight']) && (int) ($xmlSheet->pageSetup['fitToHeight']) >= 0) {
+                $docPageSetup->setFitToHeight((int) ($xmlSheet->pageSetup['fitToHeight']), false);
+            }
+            if (isset($xmlSheet->pageSetup['fitToWidth']) && (int) ($xmlSheet->pageSetup['fitToWidth']) >= 0) {
+                $docPageSetup->setFitToWidth((int) ($xmlSheet->pageSetup['fitToWidth']), false);
+            }
+            if (isset($xmlSheet->pageSetup['firstPageNumber'], $xmlSheet->pageSetup['useFirstPageNumber']) &&
+                self::boolean((string) $xmlSheet->pageSetup['useFirstPageNumber'])) {
+                $docPageSetup->setFirstPageNumber((int) ($xmlSheet->pageSetup['firstPageNumber']));
+            }
+
+            $relAttributes = $xmlSheet->pageSetup->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships');
+            if (isset($relAttributes['id'])) {
+                $unparsedLoadedData['sheets'][$worksheet->getCodeName()]['pageSetupRelId'] = (string) $relAttributes['id'];
+            }
+        }
+
+        return $unparsedLoadedData;
+    }
+
+    private function headerFooter(SimpleXMLElement $xmlSheet, Worksheet $worksheet): void
+    {
+        if ($xmlSheet->headerFooter) {
+            $docHeaderFooter = $worksheet->getHeaderFooter();
+
+            if (isset($xmlSheet->headerFooter['differentOddEven']) &&
+                self::boolean((string) $xmlSheet->headerFooter['differentOddEven'])) {
+                $docHeaderFooter->setDifferentOddEven(true);
+            } else {
+                $docHeaderFooter->setDifferentOddEven(false);
+            }
+            if (isset($xmlSheet->headerFooter['differentFirst']) &&
+                self::boolean((string) $xmlSheet->headerFooter['differentFirst'])) {
+                $docHeaderFooter->setDifferentFirst(true);
+            } else {
+                $docHeaderFooter->setDifferentFirst(false);
+            }
+            if (isset($xmlSheet->headerFooter['scaleWithDoc']) &&
+                !self::boolean((string) $xmlSheet->headerFooter['scaleWithDoc'])) {
+                $docHeaderFooter->setScaleWithDocument(false);
+            } else {
+                $docHeaderFooter->setScaleWithDocument(true);
+            }
+            if (isset($xmlSheet->headerFooter['alignWithMargins']) &&
+                !self::boolean((string) $xmlSheet->headerFooter['alignWithMargins'])) {
+                $docHeaderFooter->setAlignWithMargins(false);
+            } else {
+                $docHeaderFooter->setAlignWithMargins(true);
+            }
+
+            $docHeaderFooter->setOddHeader((string) $xmlSheet->headerFooter->oddHeader);
+            $docHeaderFooter->setOddFooter((string) $xmlSheet->headerFooter->oddFooter);
+            $docHeaderFooter->setEvenHeader((string) $xmlSheet->headerFooter->evenHeader);
+            $docHeaderFooter->setEvenFooter((string) $xmlSheet->headerFooter->evenFooter);
+            $docHeaderFooter->setFirstHeader((string) $xmlSheet->headerFooter->firstHeader);
+            $docHeaderFooter->setFirstFooter((string) $xmlSheet->headerFooter->firstFooter);
+        }
+    }
+
+    private function pageBreaks(SimpleXMLElement $xmlSheet, Worksheet $worksheet): void
+    {
+        if ($xmlSheet->rowBreaks && $xmlSheet->rowBreaks->brk) {
+            $this->rowBreaks($xmlSheet, $worksheet);
+        }
+        if ($xmlSheet->colBreaks && $xmlSheet->colBreaks->brk) {
+            $this->columnBreaks($xmlSheet, $worksheet);
+        }
+    }
+
+    private function rowBreaks(SimpleXMLElement $xmlSheet, Worksheet $worksheet): void
+    {
+        foreach ($xmlSheet->rowBreaks->brk as $brk) {
+            if ($brk['man']) {
+                $worksheet->setBreak("A{$brk['id']}", Worksheet::BREAK_ROW);
+            }
+        }
+    }
+
+    private function columnBreaks(SimpleXMLElement $xmlSheet, Worksheet $worksheet): void
+    {
+        foreach ($xmlSheet->colBreaks->brk as $brk) {
+            if ($brk['man']) {
+                $worksheet->setBreak(
+                    Coordinate::stringFromColumnIndex(((int) $brk['id']) + 1) . '1',
+                    Worksheet::BREAK_COLUMN
+                );
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php
new file mode 100755
index 0000000..b6f3c61
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Document\Properties as DocumentProperties;
+use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
+use PhpOffice\PhpSpreadsheet\Settings;
+use SimpleXMLElement;
+
+class Properties
+{
+    private $securityScanner;
+
+    private $docProps;
+
+    public function __construct(XmlScanner $securityScanner, DocumentProperties $docProps)
+    {
+        $this->securityScanner = $securityScanner;
+        $this->docProps = $docProps;
+    }
+
+    private function extractPropertyData($propertyData)
+    {
+        return simplexml_load_string(
+            $this->securityScanner->scan($propertyData),
+            'SimpleXMLElement',
+            Settings::getLibXmlLoaderOptions()
+        );
+    }
+
+    public function readCoreProperties($propertyData): void
+    {
+        $xmlCore = $this->extractPropertyData($propertyData);
+
+        if (is_object($xmlCore)) {
+            $xmlCore->registerXPathNamespace('dc', 'http://purl.org/dc/elements/1.1/');
+            $xmlCore->registerXPathNamespace('dcterms', 'http://purl.org/dc/terms/');
+            $xmlCore->registerXPathNamespace('cp', 'http://schemas.openxmlformats.org/package/2006/metadata/core-properties');
+
+            $this->docProps->setCreator((string) self::getArrayItem($xmlCore->xpath('dc:creator')));
+            $this->docProps->setLastModifiedBy((string) self::getArrayItem($xmlCore->xpath('cp:lastModifiedBy')));
+            $this->docProps->setCreated(strtotime(self::getArrayItem($xmlCore->xpath('dcterms:created')))); //! respect xsi:type
+            $this->docProps->setModified(strtotime(self::getArrayItem($xmlCore->xpath('dcterms:modified')))); //! respect xsi:type
+            $this->docProps->setTitle((string) self::getArrayItem($xmlCore->xpath('dc:title')));
+            $this->docProps->setDescription((string) self::getArrayItem($xmlCore->xpath('dc:description')));
+            $this->docProps->setSubject((string) self::getArrayItem($xmlCore->xpath('dc:subject')));
+            $this->docProps->setKeywords((string) self::getArrayItem($xmlCore->xpath('cp:keywords')));
+            $this->docProps->setCategory((string) self::getArrayItem($xmlCore->xpath('cp:category')));
+        }
+    }
+
+    public function readExtendedProperties($propertyData): void
+    {
+        $xmlCore = $this->extractPropertyData($propertyData);
+
+        if (is_object($xmlCore)) {
+            if (isset($xmlCore->Company)) {
+                $this->docProps->setCompany((string) $xmlCore->Company);
+            }
+            if (isset($xmlCore->Manager)) {
+                $this->docProps->setManager((string) $xmlCore->Manager);
+            }
+        }
+    }
+
+    public function readCustomProperties($propertyData): void
+    {
+        $xmlCore = $this->extractPropertyData($propertyData);
+
+        if (is_object($xmlCore)) {
+            foreach ($xmlCore as $xmlProperty) {
+                /** @var SimpleXMLElement $xmlProperty */
+                $cellDataOfficeAttributes = $xmlProperty->attributes();
+                if (isset($cellDataOfficeAttributes['name'])) {
+                    $propertyName = (string) $cellDataOfficeAttributes['name'];
+                    $cellDataOfficeChildren = $xmlProperty->children('http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes');
+
+                    $attributeType = $cellDataOfficeChildren->getName();
+                    $attributeValue = (string) $cellDataOfficeChildren->{$attributeType};
+                    $attributeValue = DocumentProperties::convertProperty($attributeValue, $attributeType);
+                    $attributeType = DocumentProperties::convertPropertyType($attributeType);
+                    $this->docProps->setCustomProperty($propertyName, $attributeValue, $attributeType);
+                }
+            }
+        }
+    }
+
+    private static function getArrayItem(array $array, $key = 0)
+    {
+        return $array[$key] ?? null;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php
new file mode 100755
index 0000000..b3bee89
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use SimpleXMLElement;
+
+class SheetViewOptions extends BaseParserClass
+{
+    private $worksheet;
+
+    private $worksheetXml;
+
+    public function __construct(Worksheet $workSheet, ?SimpleXMLElement $worksheetXml = null)
+    {
+        $this->worksheet = $workSheet;
+        $this->worksheetXml = $worksheetXml;
+    }
+
+    /**
+     * @param bool $readDataOnly
+     */
+    public function load($readDataOnly = false): void
+    {
+        if ($this->worksheetXml === null) {
+            return;
+        }
+
+        if (isset($this->worksheetXml->sheetPr)) {
+            $this->tabColor($this->worksheetXml->sheetPr);
+            $this->codeName($this->worksheetXml->sheetPr);
+            $this->outlines($this->worksheetXml->sheetPr);
+            $this->pageSetup($this->worksheetXml->sheetPr);
+        }
+
+        if (isset($this->worksheetXml->sheetFormatPr)) {
+            $this->sheetFormat($this->worksheetXml->sheetFormatPr);
+        }
+
+        if (!$readDataOnly && isset($this->worksheetXml->printOptions)) {
+            $this->printOptions($this->worksheetXml->printOptions);
+        }
+    }
+
+    private function tabColor(SimpleXMLElement $sheetPr): void
+    {
+        if (isset($sheetPr->tabColor, $sheetPr->tabColor['rgb'])) {
+            $this->worksheet->getTabColor()->setARGB((string) $sheetPr->tabColor['rgb']);
+        }
+    }
+
+    private function codeName(SimpleXMLElement $sheetPr): void
+    {
+        if (isset($sheetPr['codeName'])) {
+            $this->worksheet->setCodeName((string) $sheetPr['codeName'], false);
+        }
+    }
+
+    private function outlines(SimpleXMLElement $sheetPr): void
+    {
+        if (isset($sheetPr->outlinePr)) {
+            if (isset($sheetPr->outlinePr['summaryRight']) &&
+                !self::boolean((string) $sheetPr->outlinePr['summaryRight'])) {
+                $this->worksheet->setShowSummaryRight(false);
+            } else {
+                $this->worksheet->setShowSummaryRight(true);
+            }
+
+            if (isset($sheetPr->outlinePr['summaryBelow']) &&
+                !self::boolean((string) $sheetPr->outlinePr['summaryBelow'])) {
+                $this->worksheet->setShowSummaryBelow(false);
+            } else {
+                $this->worksheet->setShowSummaryBelow(true);
+            }
+        }
+    }
+
+    private function pageSetup(SimpleXMLElement $sheetPr): void
+    {
+        if (isset($sheetPr->pageSetUpPr)) {
+            if (isset($sheetPr->pageSetUpPr['fitToPage']) &&
+                !self::boolean((string) $sheetPr->pageSetUpPr['fitToPage'])) {
+                $this->worksheet->getPageSetup()->setFitToPage(false);
+            } else {
+                $this->worksheet->getPageSetup()->setFitToPage(true);
+            }
+        }
+    }
+
+    private function sheetFormat(SimpleXMLElement $sheetFormatPr): void
+    {
+        if (isset($sheetFormatPr['customHeight']) &&
+            self::boolean((string) $sheetFormatPr['customHeight']) &&
+            isset($sheetFormatPr['defaultRowHeight'])) {
+            $this->worksheet->getDefaultRowDimension()
+                ->setRowHeight((float) $sheetFormatPr['defaultRowHeight']);
+        }
+
+        if (isset($sheetFormatPr['defaultColWidth'])) {
+            $this->worksheet->getDefaultColumnDimension()
+                ->setWidth((float) $sheetFormatPr['defaultColWidth']);
+        }
+
+        if (isset($sheetFormatPr['zeroHeight']) &&
+            ((string) $sheetFormatPr['zeroHeight'] === '1')) {
+            $this->worksheet->getDefaultRowDimension()->setZeroHeight(true);
+        }
+    }
+
+    private function printOptions(SimpleXMLElement $printOptions): void
+    {
+        if (self::boolean((string) $printOptions['gridLinesSet'])) {
+            $this->worksheet->setShowGridlines(true);
+        }
+        if (self::boolean((string) $printOptions['gridLines'])) {
+            $this->worksheet->setPrintGridlines(true);
+        }
+        if (self::boolean((string) $printOptions['horizontalCentered'])) {
+            $this->worksheet->getPageSetup()->setHorizontalCentered(true);
+        }
+        if (self::boolean((string) $printOptions['verticalCentered'])) {
+            $this->worksheet->getPageSetup()->setVerticalCentered(true);
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php
new file mode 100755
index 0000000..f6c4792
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php
@@ -0,0 +1,138 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use SimpleXMLElement;
+
+class SheetViews extends BaseParserClass
+{
+    private $sheetViewXml;
+
+    private $worksheet;
+
+    public function __construct(SimpleXMLElement $sheetViewXml, Worksheet $workSheet)
+    {
+        $this->sheetViewXml = $sheetViewXml;
+        $this->worksheet = $workSheet;
+    }
+
+    public function load(): void
+    {
+        $this->zoomScale();
+        $this->view();
+        $this->gridLines();
+        $this->headers();
+        $this->direction();
+        $this->showZeros();
+
+        if (isset($this->sheetViewXml->pane)) {
+            $this->pane();
+        }
+        if (isset($this->sheetViewXml->selection, $this->sheetViewXml->selection['sqref'])) {
+            $this->selection();
+        }
+    }
+
+    private function zoomScale(): void
+    {
+        if (isset($this->sheetViewXml['zoomScale'])) {
+            $zoomScale = (int) ($this->sheetViewXml['zoomScale']);
+            if ($zoomScale <= 0) {
+                // setZoomScale will throw an Exception if the scale is less than or equals 0
+                // that is OK when manually creating documents, but we should be able to read all documents
+                $zoomScale = 100;
+            }
+
+            $this->worksheet->getSheetView()->setZoomScale($zoomScale);
+        }
+
+        if (isset($this->sheetViewXml['zoomScaleNormal'])) {
+            $zoomScaleNormal = (int) ($this->sheetViewXml['zoomScaleNormal']);
+            if ($zoomScaleNormal <= 0) {
+                // setZoomScaleNormal will throw an Exception if the scale is less than or equals 0
+                // that is OK when manually creating documents, but we should be able to read all documents
+                $zoomScaleNormal = 100;
+            }
+
+            $this->worksheet->getSheetView()->setZoomScaleNormal($zoomScaleNormal);
+        }
+    }
+
+    private function view(): void
+    {
+        if (isset($this->sheetViewXml['view'])) {
+            $this->worksheet->getSheetView()->setView((string) $this->sheetViewXml['view']);
+        }
+    }
+
+    private function gridLines(): void
+    {
+        if (isset($this->sheetViewXml['showGridLines'])) {
+            $this->worksheet->setShowGridLines(
+                self::boolean((string) $this->sheetViewXml['showGridLines'])
+            );
+        }
+    }
+
+    private function headers(): void
+    {
+        if (isset($this->sheetViewXml['showRowColHeaders'])) {
+            $this->worksheet->setShowRowColHeaders(
+                self::boolean((string) $this->sheetViewXml['showRowColHeaders'])
+            );
+        }
+    }
+
+    private function direction(): void
+    {
+        if (isset($this->sheetViewXml['rightToLeft'])) {
+            $this->worksheet->setRightToLeft(
+                self::boolean((string) $this->sheetViewXml['rightToLeft'])
+            );
+        }
+    }
+
+    private function showZeros(): void
+    {
+        if (isset($this->sheetViewXml['showZeros'])) {
+            $this->worksheet->getSheetView()->setShowZeros(
+                self::boolean((string) $this->sheetViewXml['showZeros'])
+            );
+        }
+    }
+
+    private function pane(): void
+    {
+        $xSplit = 0;
+        $ySplit = 0;
+        $topLeftCell = null;
+
+        if (isset($this->sheetViewXml->pane['xSplit'])) {
+            $xSplit = (int) ($this->sheetViewXml->pane['xSplit']);
+        }
+
+        if (isset($this->sheetViewXml->pane['ySplit'])) {
+            $ySplit = (int) ($this->sheetViewXml->pane['ySplit']);
+        }
+
+        if (isset($this->sheetViewXml->pane['topLeftCell'])) {
+            $topLeftCell = (string) $this->sheetViewXml->pane['topLeftCell'];
+        }
+
+        $this->worksheet->freezePane(
+            Coordinate::stringFromColumnIndex($xSplit + 1) . ($ySplit + 1),
+            $topLeftCell
+        );
+    }
+
+    private function selection(): void
+    {
+        $sqref = (string) $this->sheetViewXml->selection['sqref'];
+        $sqref = explode(' ', $sqref);
+        $sqref = $sqref[0];
+
+        $this->worksheet->setSelectedCells($sqref);
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php
new file mode 100755
index 0000000..43de878
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php
@@ -0,0 +1,282 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Style\Alignment;
+use PhpOffice\PhpSpreadsheet\Style\Border;
+use PhpOffice\PhpSpreadsheet\Style\Borders;
+use PhpOffice\PhpSpreadsheet\Style\Color;
+use PhpOffice\PhpSpreadsheet\Style\Fill;
+use PhpOffice\PhpSpreadsheet\Style\Font;
+use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
+use PhpOffice\PhpSpreadsheet\Style\Protection;
+use PhpOffice\PhpSpreadsheet\Style\Style;
+use SimpleXMLElement;
+
+class Styles extends BaseParserClass
+{
+    /**
+     * Theme instance.
+     *
+     * @var Theme
+     */
+    private static $theme = null;
+
+    private $styles = [];
+
+    private $cellStyles = [];
+
+    private $styleXml;
+
+    public function __construct(SimpleXMLElement $styleXml)
+    {
+        $this->styleXml = $styleXml;
+    }
+
+    public function setStyleBaseData(?Theme $theme = null, $styles = [], $cellStyles = []): void
+    {
+        self::$theme = $theme;
+        $this->styles = $styles;
+        $this->cellStyles = $cellStyles;
+    }
+
+    private static function readFontStyle(Font $fontStyle, SimpleXMLElement $fontStyleXml): void
+    {
+        $fontStyle->setName((string) $fontStyleXml->name['val']);
+        $fontStyle->setSize((float) $fontStyleXml->sz['val']);
+
+        if (isset($fontStyleXml->b)) {
+            $fontStyle->setBold(!isset($fontStyleXml->b['val']) || self::boolean((string) $fontStyleXml->b['val']));
+        }
+        if (isset($fontStyleXml->i)) {
+            $fontStyle->setItalic(!isset($fontStyleXml->i['val']) || self::boolean((string) $fontStyleXml->i['val']));
+        }
+        if (isset($fontStyleXml->strike)) {
+            $fontStyle->setStrikethrough(!isset($fontStyleXml->strike['val']) || self::boolean((string) $fontStyleXml->strike['val']));
+        }
+        $fontStyle->getColor()->setARGB(self::readColor($fontStyleXml->color));
+
+        if (isset($fontStyleXml->u) && !isset($fontStyleXml->u['val'])) {
+            $fontStyle->setUnderline(Font::UNDERLINE_SINGLE);
+        } elseif (isset($fontStyleXml->u, $fontStyleXml->u['val'])) {
+            $fontStyle->setUnderline((string) $fontStyleXml->u['val']);
+        }
+
+        if (isset($fontStyleXml->vertAlign, $fontStyleXml->vertAlign['val'])) {
+            $verticalAlign = strtolower((string) $fontStyleXml->vertAlign['val']);
+            if ($verticalAlign === 'superscript') {
+                $fontStyle->setSuperscript(true);
+            }
+            if ($verticalAlign === 'subscript') {
+                $fontStyle->setSubscript(true);
+            }
+        }
+    }
+
+    private static function readNumberFormat(NumberFormat $numfmtStyle, SimpleXMLElement $numfmtStyleXml): void
+    {
+        if ($numfmtStyleXml->count() === 0) {
+            return;
+        }
+        $numfmt = $numfmtStyleXml->attributes();
+        if ($numfmt->count() > 0 && isset($numfmt['formatCode'])) {
+            $numfmtStyle->setFormatCode((string) $numfmt['formatCode']);
+        }
+    }
+
+    private static function readFillStyle(Fill $fillStyle, SimpleXMLElement $fillStyleXml): void
+    {
+        if ($fillStyleXml->gradientFill) {
+            /** @var SimpleXMLElement $gradientFill */
+            $gradientFill = $fillStyleXml->gradientFill[0];
+            if (!empty($gradientFill['type'])) {
+                $fillStyle->setFillType((string) $gradientFill['type']);
+            }
+            $fillStyle->setRotation((float) ($gradientFill['degree']));
+            $gradientFill->registerXPathNamespace('sml', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
+            $fillStyle->getStartColor()->setARGB(self::readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=0]'))->color));
+            $fillStyle->getEndColor()->setARGB(self::readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=1]'))->color));
+        } elseif ($fillStyleXml->patternFill) {
+            $patternType = (string) $fillStyleXml->patternFill['patternType'] != '' ? (string) $fillStyleXml->patternFill['patternType'] : 'solid';
+            $fillStyle->setFillType($patternType);
+            if ($fillStyleXml->patternFill->fgColor) {
+                $fillStyle->getStartColor()->setARGB(self::readColor($fillStyleXml->patternFill->fgColor, true));
+            } else {
+                $fillStyle->getStartColor()->setARGB('FF000000');
+            }
+            if ($fillStyleXml->patternFill->bgColor) {
+                $fillStyle->getEndColor()->setARGB(self::readColor($fillStyleXml->patternFill->bgColor, true));
+            }
+        }
+    }
+
+    private static function readBorderStyle(Borders $borderStyle, SimpleXMLElement $borderStyleXml): void
+    {
+        $diagonalUp = self::boolean((string) $borderStyleXml['diagonalUp']);
+        $diagonalDown = self::boolean((string) $borderStyleXml['diagonalDown']);
+        if (!$diagonalUp && !$diagonalDown) {
+            $borderStyle->setDiagonalDirection(Borders::DIAGONAL_NONE);
+        } elseif ($diagonalUp && !$diagonalDown) {
+            $borderStyle->setDiagonalDirection(Borders::DIAGONAL_UP);
+        } elseif (!$diagonalUp && $diagonalDown) {
+            $borderStyle->setDiagonalDirection(Borders::DIAGONAL_DOWN);
+        } else {
+            $borderStyle->setDiagonalDirection(Borders::DIAGONAL_BOTH);
+        }
+
+        self::readBorder($borderStyle->getLeft(), $borderStyleXml->left);
+        self::readBorder($borderStyle->getRight(), $borderStyleXml->right);
+        self::readBorder($borderStyle->getTop(), $borderStyleXml->top);
+        self::readBorder($borderStyle->getBottom(), $borderStyleXml->bottom);
+        self::readBorder($borderStyle->getDiagonal(), $borderStyleXml->diagonal);
+    }
+
+    private static function readBorder(Border $border, SimpleXMLElement $borderXml): void
+    {
+        if (isset($borderXml['style'])) {
+            $border->setBorderStyle((string) $borderXml['style']);
+        }
+        if (isset($borderXml->color)) {
+            $border->getColor()->setARGB(self::readColor($borderXml->color));
+        }
+    }
+
+    private static function readAlignmentStyle(Alignment $alignment, SimpleXMLElement $alignmentXml): void
+    {
+        $alignment->setHorizontal((string) $alignmentXml->alignment['horizontal']);
+        $alignment->setVertical((string) $alignmentXml->alignment['vertical']);
+
+        $textRotation = 0;
+        if ((int) $alignmentXml->alignment['textRotation'] <= 90) {
+            $textRotation = (int) $alignmentXml->alignment['textRotation'];
+        } elseif ((int) $alignmentXml->alignment['textRotation'] > 90) {
+            $textRotation = 90 - (int) $alignmentXml->alignment['textRotation'];
+        }
+
+        $alignment->setTextRotation((int) $textRotation);
+        $alignment->setWrapText(self::boolean((string) $alignmentXml->alignment['wrapText']));
+        $alignment->setShrinkToFit(self::boolean((string) $alignmentXml->alignment['shrinkToFit']));
+        $alignment->setIndent((int) ((string) $alignmentXml->alignment['indent']) > 0 ? (int) ((string) $alignmentXml->alignment['indent']) : 0);
+        $alignment->setReadOrder((int) ((string) $alignmentXml->alignment['readingOrder']) > 0 ? (int) ((string) $alignmentXml->alignment['readingOrder']) : 0);
+    }
+
+    private function readStyle(Style $docStyle, $style): void
+    {
+        if ($style->numFmt instanceof SimpleXMLElement) {
+            self::readNumberFormat($docStyle->getNumberFormat(), $style->numFmt);
+        } else {
+            $docStyle->getNumberFormat()->setFormatCode($style->numFmt);
+        }
+
+        if (isset($style->font)) {
+            self::readFontStyle($docStyle->getFont(), $style->font);
+        }
+
+        if (isset($style->fill)) {
+            self::readFillStyle($docStyle->getFill(), $style->fill);
+        }
+
+        if (isset($style->border)) {
+            self::readBorderStyle($docStyle->getBorders(), $style->border);
+        }
+
+        if (isset($style->alignment->alignment)) {
+            self::readAlignmentStyle($docStyle->getAlignment(), $style->alignment);
+        }
+
+        // protection
+        if (isset($style->protection)) {
+            $this->readProtectionLocked($docStyle, $style);
+            $this->readProtectionHidden($docStyle, $style);
+        }
+
+        // top-level style settings
+        if (isset($style->quotePrefix)) {
+            $docStyle->setQuotePrefix(true);
+        }
+    }
+
+    private function readProtectionLocked(Style $docStyle, $style): void
+    {
+        if (isset($style->protection['locked'])) {
+            if (self::boolean((string) $style->protection['locked'])) {
+                $docStyle->getProtection()->setLocked(Protection::PROTECTION_PROTECTED);
+            } else {
+                $docStyle->getProtection()->setLocked(Protection::PROTECTION_UNPROTECTED);
+            }
+        }
+    }
+
+    private function readProtectionHidden(Style $docStyle, $style): void
+    {
+        if (isset($style->protection['hidden'])) {
+            if (self::boolean((string) $style->protection['hidden'])) {
+                $docStyle->getProtection()->setHidden(Protection::PROTECTION_PROTECTED);
+            } else {
+                $docStyle->getProtection()->setHidden(Protection::PROTECTION_UNPROTECTED);
+            }
+        }
+    }
+
+    private static function readColor($color, $background = false)
+    {
+        if (isset($color['rgb'])) {
+            return (string) $color['rgb'];
+        } elseif (isset($color['indexed'])) {
+            return Color::indexedColor($color['indexed'] - 7, $background)->getARGB();
+        } elseif (isset($color['theme'])) {
+            if (self::$theme !== null) {
+                $returnColour = self::$theme->getColourByIndex((int) $color['theme']);
+                if (isset($color['tint'])) {
+                    $tintAdjust = (float) $color['tint'];
+                    $returnColour = Color::changeBrightness($returnColour, $tintAdjust);
+                }
+
+                return 'FF' . $returnColour;
+            }
+        }
+
+        return ($background) ? 'FFFFFFFF' : 'FF000000';
+    }
+
+    public function dxfs($readDataOnly = false)
+    {
+        $dxfs = [];
+        if (!$readDataOnly && $this->styleXml) {
+            //    Conditional Styles
+            if ($this->styleXml->dxfs) {
+                foreach ($this->styleXml->dxfs->dxf as $dxf) {
+                    $style = new Style(false, true);
+                    $this->readStyle($style, $dxf);
+                    $dxfs[] = $style;
+                }
+            }
+            //    Cell Styles
+            if ($this->styleXml->cellStyles) {
+                foreach ($this->styleXml->cellStyles->cellStyle as $cellStyle) {
+                    if ((int) ($cellStyle['builtinId']) == 0) {
+                        if (isset($this->cellStyles[(int) ($cellStyle['xfId'])])) {
+                            // Set default style
+                            $style = new Style();
+                            $this->readStyle($style, $this->cellStyles[(int) ($cellStyle['xfId'])]);
+
+                            // normal style, currently not using it for anything
+                        }
+                    }
+                }
+            }
+        }
+
+        return $dxfs;
+    }
+
+    public function styles()
+    {
+        return $this->styles;
+    }
+
+    private static function getArrayItem($array, $key = 0)
+    {
+        return $array[$key] ?? null;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Theme.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Theme.php
new file mode 100755
index 0000000..c105f3c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Theme.php
@@ -0,0 +1,93 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
+
+class Theme
+{
+    /**
+     * Theme Name.
+     *
+     * @var string
+     */
+    private $themeName;
+
+    /**
+     * Colour Scheme Name.
+     *
+     * @var string
+     */
+    private $colourSchemeName;
+
+    /**
+     * Colour Map.
+     *
+     * @var array of string
+     */
+    private $colourMap;
+
+    /**
+     * Create a new Theme.
+     *
+     * @param mixed $themeName
+     * @param mixed $colourSchemeName
+     * @param mixed $colourMap
+     */
+    public function __construct($themeName, $colourSchemeName, $colourMap)
+    {
+        // Initialise values
+        $this->themeName = $themeName;
+        $this->colourSchemeName = $colourSchemeName;
+        $this->colourMap = $colourMap;
+    }
+
+    /**
+     * Get Theme Name.
+     *
+     * @return string
+     */
+    public function getThemeName()
+    {
+        return $this->themeName;
+    }
+
+    /**
+     * Get colour Scheme Name.
+     *
+     * @return string
+     */
+    public function getColourSchemeName()
+    {
+        return $this->colourSchemeName;
+    }
+
+    /**
+     * Get colour Map Value by Position.
+     *
+     * @param mixed $index
+     *
+     * @return string
+     */
+    public function getColourByIndex($index)
+    {
+        if (isset($this->colourMap[$index])) {
+            return $this->colourMap[$index];
+        }
+
+        return null;
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $vars = get_object_vars($this);
+        foreach ($vars as $key => $value) {
+            if ((is_object($value)) && ($key != '_parent')) {
+                $this->$key = clone $value;
+            } else {
+                $this->$key = $value;
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php
new file mode 100755
index 0000000..15fb34a
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php
@@ -0,0 +1,858 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Cell\DataType;
+use PhpOffice\PhpSpreadsheet\Document\Properties;
+use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
+use PhpOffice\PhpSpreadsheet\RichText\RichText;
+use PhpOffice\PhpSpreadsheet\Settings;
+use PhpOffice\PhpSpreadsheet\Shared\Date;
+use PhpOffice\PhpSpreadsheet\Shared\File;
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Style\Alignment;
+use PhpOffice\PhpSpreadsheet\Style\Border;
+use PhpOffice\PhpSpreadsheet\Style\Font;
+use SimpleXMLElement;
+
+/**
+ * Reader for SpreadsheetML, the XML schema for Microsoft Office Excel 2003.
+ */
+class Xml extends BaseReader
+{
+    /**
+     * Formats.
+     *
+     * @var array
+     */
+    protected $styles = [];
+
+    /**
+     * Character set used in the file.
+     *
+     * @var string
+     */
+    protected $charSet = 'UTF-8';
+
+    /**
+     * Create a new Excel2003XML Reader instance.
+     */
+    public function __construct()
+    {
+        parent::__construct();
+        $this->securityScanner = XmlScanner::getInstance($this);
+    }
+
+    /**
+     * Can the current IReader read the file?
+     *
+     * @param string $pFilename
+     *
+     * @return bool
+     */
+    public function canRead($pFilename)
+    {
+        //    Office                    xmlns:o="urn:schemas-microsoft-com:office:office"
+        //    Excel                    xmlns:x="urn:schemas-microsoft-com:office:excel"
+        //    XML Spreadsheet            xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
+        //    Spreadsheet component    xmlns:c="urn:schemas-microsoft-com:office:component:spreadsheet"
+        //    XML schema                 xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882"
+        //    XML data type            xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"
+        //    MS-persist recordset    xmlns:rs="urn:schemas-microsoft-com:rowset"
+        //    Rowset                    xmlns:z="#RowsetSchema"
+        //
+
+        $signature = [
+            '<?xml version="1.0"',
+            '<?mso-application progid="Excel.Sheet"?>',
+        ];
+
+        // Open file
+        $this->openFile($pFilename);
+        $fileHandle = $this->fileHandle;
+
+        // Read sample data (first 2 KB will do)
+        $data = fread($fileHandle, 2048);
+        fclose($fileHandle);
+        $data = str_replace("'", '"', $data); // fix headers with single quote
+
+        $valid = true;
+        foreach ($signature as $match) {
+            // every part of the signature must be present
+            if (strpos($data, $match) === false) {
+                $valid = false;
+
+                break;
+            }
+        }
+
+        //    Retrieve charset encoding
+        if (preg_match('/<?xml.*encoding=[\'"](.*?)[\'"].*?>/um', $data, $matches)) {
+            $this->charSet = strtoupper($matches[1]);
+        }
+
+        return $valid;
+    }
+
+    /**
+     * Check if the file is a valid SimpleXML.
+     *
+     * @param string $pFilename
+     *
+     * @return false|SimpleXMLElement
+     */
+    public function trySimpleXMLLoadString($pFilename)
+    {
+        try {
+            $xml = simplexml_load_string(
+                $this->securityScanner->scan(file_get_contents($pFilename)),
+                'SimpleXMLElement',
+                Settings::getLibXmlLoaderOptions()
+            );
+        } catch (\Exception $e) {
+            throw new Exception('Cannot load invalid XML file: ' . $pFilename, 0, $e);
+        }
+
+        return $xml;
+    }
+
+    /**
+     * Reads names of the worksheets from a file, without parsing the whole file to a Spreadsheet object.
+     *
+     * @param string $pFilename
+     *
+     * @return array
+     */
+    public function listWorksheetNames($pFilename)
+    {
+        File::assertFile($pFilename);
+        if (!$this->canRead($pFilename)) {
+            throw new Exception($pFilename . ' is an Invalid Spreadsheet file.');
+        }
+
+        $worksheetNames = [];
+
+        $xml = $this->trySimpleXMLLoadString($pFilename);
+
+        $namespaces = $xml->getNamespaces(true);
+
+        $xml_ss = $xml->children($namespaces['ss']);
+        foreach ($xml_ss->Worksheet as $worksheet) {
+            $worksheet_ss = $worksheet->attributes($namespaces['ss']);
+            $worksheetNames[] = self::convertStringEncoding((string) $worksheet_ss['Name'], $this->charSet);
+        }
+
+        return $worksheetNames;
+    }
+
+    /**
+     * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
+     *
+     * @param string $pFilename
+     *
+     * @return array
+     */
+    public function listWorksheetInfo($pFilename)
+    {
+        File::assertFile($pFilename);
+
+        $worksheetInfo = [];
+
+        $xml = $this->trySimpleXMLLoadString($pFilename);
+
+        $namespaces = $xml->getNamespaces(true);
+
+        $worksheetID = 1;
+        $xml_ss = $xml->children($namespaces['ss']);
+        foreach ($xml_ss->Worksheet as $worksheet) {
+            $worksheet_ss = $worksheet->attributes($namespaces['ss']);
+
+            $tmpInfo = [];
+            $tmpInfo['worksheetName'] = '';
+            $tmpInfo['lastColumnLetter'] = 'A';
+            $tmpInfo['lastColumnIndex'] = 0;
+            $tmpInfo['totalRows'] = 0;
+            $tmpInfo['totalColumns'] = 0;
+
+            if (isset($worksheet_ss['Name'])) {
+                $tmpInfo['worksheetName'] = (string) $worksheet_ss['Name'];
+            } else {
+                $tmpInfo['worksheetName'] = "Worksheet_{$worksheetID}";
+            }
+
+            if (isset($worksheet->Table->Row)) {
+                $rowIndex = 0;
+
+                foreach ($worksheet->Table->Row as $rowData) {
+                    $columnIndex = 0;
+                    $rowHasData = false;
+
+                    foreach ($rowData->Cell as $cell) {
+                        if (isset($cell->Data)) {
+                            $tmpInfo['lastColumnIndex'] = max($tmpInfo['lastColumnIndex'], $columnIndex);
+                            $rowHasData = true;
+                        }
+
+                        ++$columnIndex;
+                    }
+
+                    ++$rowIndex;
+
+                    if ($rowHasData) {
+                        $tmpInfo['totalRows'] = max($tmpInfo['totalRows'], $rowIndex);
+                    }
+                }
+            }
+
+            $tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1);
+            $tmpInfo['totalColumns'] = $tmpInfo['lastColumnIndex'] + 1;
+
+            $worksheetInfo[] = $tmpInfo;
+            ++$worksheetID;
+        }
+
+        return $worksheetInfo;
+    }
+
+    /**
+     * Loads Spreadsheet from file.
+     *
+     * @param string $pFilename
+     *
+     * @return Spreadsheet
+     */
+    public function load($pFilename)
+    {
+        // Create new Spreadsheet
+        $spreadsheet = new Spreadsheet();
+        $spreadsheet->removeSheetByIndex(0);
+
+        // Load into this instance
+        return $this->loadIntoExisting($pFilename, $spreadsheet);
+    }
+
+    private static function identifyFixedStyleValue($styleList, &$styleAttributeValue)
+    {
+        $styleAttributeValue = strtolower($styleAttributeValue);
+        foreach ($styleList as $style) {
+            if ($styleAttributeValue == strtolower($style)) {
+                $styleAttributeValue = $style;
+
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * pixel units to excel width units(units of 1/256th of a character width).
+     *
+     * @param float $pxs
+     *
+     * @return float
+     */
+    protected static function pixel2WidthUnits($pxs)
+    {
+        $UNIT_OFFSET_MAP = [0, 36, 73, 109, 146, 182, 219];
+
+        $widthUnits = 256 * ($pxs / 7);
+        $widthUnits += $UNIT_OFFSET_MAP[($pxs % 7)];
+
+        return $widthUnits;
+    }
+
+    /**
+     * excel width units(units of 1/256th of a character width) to pixel units.
+     *
+     * @param float $widthUnits
+     *
+     * @return float
+     */
+    protected static function widthUnits2Pixel($widthUnits)
+    {
+        $pixels = ($widthUnits / 256) * 7;
+        $offsetWidthUnits = $widthUnits % 256;
+
+        return $pixels + round($offsetWidthUnits / (256 / 7));
+    }
+
+    protected static function hex2str($hex)
+    {
+        return chr(hexdec($hex[1]));
+    }
+
+    /**
+     * Loads from file into Spreadsheet instance.
+     *
+     * @param string $pFilename
+     *
+     * @return Spreadsheet
+     */
+    public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet)
+    {
+        File::assertFile($pFilename);
+        if (!$this->canRead($pFilename)) {
+            throw new Exception($pFilename . ' is an Invalid Spreadsheet file.');
+        }
+
+        $xml = $this->trySimpleXMLLoadString($pFilename);
+
+        $namespaces = $xml->getNamespaces(true);
+
+        $docProps = $spreadsheet->getProperties();
+        if (isset($xml->DocumentProperties[0])) {
+            foreach ($xml->DocumentProperties[0] as $propertyName => $propertyValue) {
+                switch ($propertyName) {
+                    case 'Title':
+                        $docProps->setTitle(self::convertStringEncoding($propertyValue, $this->charSet));
+
+                        break;
+                    case 'Subject':
+                        $docProps->setSubject(self::convertStringEncoding($propertyValue, $this->charSet));
+
+                        break;
+                    case 'Author':
+                        $docProps->setCreator(self::convertStringEncoding($propertyValue, $this->charSet));
+
+                        break;
+                    case 'Created':
+                        $creationDate = strtotime($propertyValue);
+                        $docProps->setCreated($creationDate);
+
+                        break;
+                    case 'LastAuthor':
+                        $docProps->setLastModifiedBy(self::convertStringEncoding($propertyValue, $this->charSet));
+
+                        break;
+                    case 'LastSaved':
+                        $lastSaveDate = strtotime($propertyValue);
+                        $docProps->setModified($lastSaveDate);
+
+                        break;
+                    case 'Company':
+                        $docProps->setCompany(self::convertStringEncoding($propertyValue, $this->charSet));
+
+                        break;
+                    case 'Category':
+                        $docProps->setCategory(self::convertStringEncoding($propertyValue, $this->charSet));
+
+                        break;
+                    case 'Manager':
+                        $docProps->setManager(self::convertStringEncoding($propertyValue, $this->charSet));
+
+                        break;
+                    case 'Keywords':
+                        $docProps->setKeywords(self::convertStringEncoding($propertyValue, $this->charSet));
+
+                        break;
+                    case 'Description':
+                        $docProps->setDescription(self::convertStringEncoding($propertyValue, $this->charSet));
+
+                        break;
+                }
+            }
+        }
+        if (isset($xml->CustomDocumentProperties)) {
+            foreach ($xml->CustomDocumentProperties[0] as $propertyName => $propertyValue) {
+                $propertyAttributes = $propertyValue->attributes($namespaces['dt']);
+                $propertyName = preg_replace_callback('/_x([0-9a-z]{4})_/', ['self', 'hex2str'], $propertyName);
+                $propertyType = Properties::PROPERTY_TYPE_UNKNOWN;
+                switch ((string) $propertyAttributes) {
+                    case 'string':
+                        $propertyType = Properties::PROPERTY_TYPE_STRING;
+                        $propertyValue = trim($propertyValue);
+
+                        break;
+                    case 'boolean':
+                        $propertyType = Properties::PROPERTY_TYPE_BOOLEAN;
+                        $propertyValue = (bool) $propertyValue;
+
+                        break;
+                    case 'integer':
+                        $propertyType = Properties::PROPERTY_TYPE_INTEGER;
+                        $propertyValue = (int) $propertyValue;
+
+                        break;
+                    case 'float':
+                        $propertyType = Properties::PROPERTY_TYPE_FLOAT;
+                        $propertyValue = (float) $propertyValue;
+
+                        break;
+                    case 'dateTime.tz':
+                        $propertyType = Properties::PROPERTY_TYPE_DATE;
+                        $propertyValue = strtotime(trim($propertyValue));
+
+                        break;
+                }
+                $docProps->setCustomProperty($propertyName, $propertyValue, $propertyType);
+            }
+        }
+
+        $this->parseStyles($xml, $namespaces);
+
+        $worksheetID = 0;
+        $xml_ss = $xml->children($namespaces['ss']);
+
+        foreach ($xml_ss->Worksheet as $worksheet) {
+            $worksheet_ss = $worksheet->attributes($namespaces['ss']);
+
+            if ((isset($this->loadSheetsOnly)) && (isset($worksheet_ss['Name'])) &&
+                (!in_array($worksheet_ss['Name'], $this->loadSheetsOnly))) {
+                continue;
+            }
+
+            // Create new Worksheet
+            $spreadsheet->createSheet();
+            $spreadsheet->setActiveSheetIndex($worksheetID);
+            if (isset($worksheet_ss['Name'])) {
+                $worksheetName = self::convertStringEncoding((string) $worksheet_ss['Name'], $this->charSet);
+                //    Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in
+                //        formula cells... during the load, all formulae should be correct, and we're simply bringing
+                //        the worksheet name in line with the formula, not the reverse
+                $spreadsheet->getActiveSheet()->setTitle($worksheetName, false, false);
+            }
+
+            $columnID = 'A';
+            if (isset($worksheet->Table->Column)) {
+                foreach ($worksheet->Table->Column as $columnData) {
+                    $columnData_ss = $columnData->attributes($namespaces['ss']);
+                    if (isset($columnData_ss['Index'])) {
+                        $columnID = Coordinate::stringFromColumnIndex((int) $columnData_ss['Index']);
+                    }
+                    if (isset($columnData_ss['Width'])) {
+                        $columnWidth = $columnData_ss['Width'];
+                        $spreadsheet->getActiveSheet()->getColumnDimension($columnID)->setWidth($columnWidth / 5.4);
+                    }
+                    ++$columnID;
+                }
+            }
+
+            $rowID = 1;
+            if (isset($worksheet->Table->Row)) {
+                $additionalMergedCells = 0;
+                foreach ($worksheet->Table->Row as $rowData) {
+                    $rowHasData = false;
+                    $row_ss = $rowData->attributes($namespaces['ss']);
+                    if (isset($row_ss['Index'])) {
+                        $rowID = (int) $row_ss['Index'];
+                    }
+
+                    $columnID = 'A';
+                    foreach ($rowData->Cell as $cell) {
+                        $cell_ss = $cell->attributes($namespaces['ss']);
+                        if (isset($cell_ss['Index'])) {
+                            $columnID = Coordinate::stringFromColumnIndex((int) $cell_ss['Index']);
+                        }
+                        $cellRange = $columnID . $rowID;
+
+                        if ($this->getReadFilter() !== null) {
+                            if (!$this->getReadFilter()->readCell($columnID, $rowID, $worksheetName)) {
+                                ++$columnID;
+
+                                continue;
+                            }
+                        }
+
+                        if (isset($cell_ss['HRef'])) {
+                            $spreadsheet->getActiveSheet()->getCell($cellRange)->getHyperlink()->setUrl($cell_ss['HRef']);
+                        }
+
+                        if ((isset($cell_ss['MergeAcross'])) || (isset($cell_ss['MergeDown']))) {
+                            $columnTo = $columnID;
+                            if (isset($cell_ss['MergeAcross'])) {
+                                $additionalMergedCells += (int) $cell_ss['MergeAcross'];
+                                $columnTo = Coordinate::stringFromColumnIndex(Coordinate::columnIndexFromString($columnID) + $cell_ss['MergeAcross']);
+                            }
+                            $rowTo = $rowID;
+                            if (isset($cell_ss['MergeDown'])) {
+                                $rowTo = $rowTo + $cell_ss['MergeDown'];
+                            }
+                            $cellRange .= ':' . $columnTo . $rowTo;
+                            $spreadsheet->getActiveSheet()->mergeCells($cellRange);
+                        }
+
+                        $cellIsSet = $hasCalculatedValue = false;
+                        $cellDataFormula = '';
+                        if (isset($cell_ss['Formula'])) {
+                            $cellDataFormula = $cell_ss['Formula'];
+                            $hasCalculatedValue = true;
+                        }
+                        if (isset($cell->Data)) {
+                            $cellValue = $cellData = $cell->Data;
+                            $type = DataType::TYPE_NULL;
+                            $cellData_ss = $cellData->attributes($namespaces['ss']);
+                            if (isset($cellData_ss['Type'])) {
+                                $cellDataType = $cellData_ss['Type'];
+                                switch ($cellDataType) {
+                                    /*
+                                    const TYPE_STRING        = 's';
+                                    const TYPE_FORMULA        = 'f';
+                                    const TYPE_NUMERIC        = 'n';
+                                    const TYPE_BOOL            = 'b';
+                                    const TYPE_NULL            = 'null';
+                                    const TYPE_INLINE        = 'inlineStr';
+                                    const TYPE_ERROR        = 'e';
+                                    */
+                                    case 'String':
+                                        $cellValue = self::convertStringEncoding($cellValue, $this->charSet);
+                                        $type = DataType::TYPE_STRING;
+
+                                        break;
+                                    case 'Number':
+                                        $type = DataType::TYPE_NUMERIC;
+                                        $cellValue = (float) $cellValue;
+                                        if (floor($cellValue) == $cellValue) {
+                                            $cellValue = (int) $cellValue;
+                                        }
+
+                                        break;
+                                    case 'Boolean':
+                                        $type = DataType::TYPE_BOOL;
+                                        $cellValue = ($cellValue != 0);
+
+                                        break;
+                                    case 'DateTime':
+                                        $type = DataType::TYPE_NUMERIC;
+                                        $cellValue = Date::PHPToExcel(strtotime($cellValue));
+
+                                        break;
+                                    case 'Error':
+                                        $type = DataType::TYPE_ERROR;
+
+                                        break;
+                                }
+                            }
+
+                            if ($hasCalculatedValue) {
+                                $type = DataType::TYPE_FORMULA;
+                                $columnNumber = Coordinate::columnIndexFromString($columnID);
+                                if (substr($cellDataFormula, 0, 3) == 'of:') {
+                                    $cellDataFormula = substr($cellDataFormula, 3);
+                                    $temp = explode('"', $cellDataFormula);
+                                    $key = false;
+                                    foreach ($temp as &$value) {
+                                        //    Only replace in alternate array entries (i.e. non-quoted blocks)
+                                        if ($key = !$key) {
+                                            $value = str_replace(['[.', '.', ']'], '', $value);
+                                        }
+                                    }
+                                } else {
+                                    //    Convert R1C1 style references to A1 style references (but only when not quoted)
+                                    $temp = explode('"', $cellDataFormula);
+                                    $key = false;
+                                    foreach ($temp as &$value) {
+                                        //    Only replace in alternate array entries (i.e. non-quoted blocks)
+                                        if ($key = !$key) {
+                                            preg_match_all('/(R(\[?-?\d*\]?))(C(\[?-?\d*\]?))/', $value, $cellReferences, PREG_SET_ORDER + PREG_OFFSET_CAPTURE);
+                                            //    Reverse the matches array, otherwise all our offsets will become incorrect if we modify our way
+                                            //        through the formula from left to right. Reversing means that we work right to left.through
+                                            //        the formula
+                                            $cellReferences = array_reverse($cellReferences);
+                                            //    Loop through each R1C1 style reference in turn, converting it to its A1 style equivalent,
+                                            //        then modify the formula to use that new reference
+                                            foreach ($cellReferences as $cellReference) {
+                                                $rowReference = $cellReference[2][0];
+                                                //    Empty R reference is the current row
+                                                if ($rowReference == '') {
+                                                    $rowReference = $rowID;
+                                                }
+                                                //    Bracketed R references are relative to the current row
+                                                if ($rowReference[0] == '[') {
+                                                    $rowReference = $rowID + trim($rowReference, '[]');
+                                                }
+                                                $columnReference = $cellReference[4][0];
+                                                //    Empty C reference is the current column
+                                                if ($columnReference == '') {
+                                                    $columnReference = $columnNumber;
+                                                }
+                                                //    Bracketed C references are relative to the current column
+                                                if ($columnReference[0] == '[') {
+                                                    $columnReference = $columnNumber + trim($columnReference, '[]');
+                                                }
+                                                $A1CellReference = Coordinate::stringFromColumnIndex($columnReference) . $rowReference;
+                                                $value = substr_replace($value, $A1CellReference, $cellReference[0][1], strlen($cellReference[0][0]));
+                                            }
+                                        }
+                                    }
+                                }
+                                unset($value);
+                                //    Then rebuild the formula string
+                                $cellDataFormula = implode('"', $temp);
+                            }
+
+                            $spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setValueExplicit((($hasCalculatedValue) ? $cellDataFormula : $cellValue), $type);
+                            if ($hasCalculatedValue) {
+                                $spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setCalculatedValue($cellValue);
+                            }
+                            $cellIsSet = $rowHasData = true;
+                        }
+
+                        if (isset($cell->Comment)) {
+                            $commentAttributes = $cell->Comment->attributes($namespaces['ss']);
+                            $author = 'unknown';
+                            if (isset($commentAttributes->Author)) {
+                                $author = (string) $commentAttributes->Author;
+                            }
+                            $node = $cell->Comment->Data->asXML();
+                            $annotation = strip_tags($node);
+                            $spreadsheet->getActiveSheet()->getComment($columnID . $rowID)->setAuthor(self::convertStringEncoding($author, $this->charSet))->setText($this->parseRichText($annotation));
+                        }
+
+                        if (($cellIsSet) && (isset($cell_ss['StyleID']))) {
+                            $style = (string) $cell_ss['StyleID'];
+                            if ((isset($this->styles[$style])) && (!empty($this->styles[$style]))) {
+                                if (!$spreadsheet->getActiveSheet()->cellExists($columnID . $rowID)) {
+                                    $spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setValue(null);
+                                }
+                                $spreadsheet->getActiveSheet()->getStyle($cellRange)->applyFromArray($this->styles[$style]);
+                            }
+                        }
+                        ++$columnID;
+                        while ($additionalMergedCells > 0) {
+                            ++$columnID;
+                            --$additionalMergedCells;
+                        }
+                    }
+
+                    if ($rowHasData) {
+                        if (isset($row_ss['Height'])) {
+                            $rowHeight = $row_ss['Height'];
+                            $spreadsheet->getActiveSheet()->getRowDimension($rowID)->setRowHeight($rowHeight);
+                        }
+                    }
+
+                    ++$rowID;
+                }
+            }
+            ++$worksheetID;
+        }
+
+        // Return
+        return $spreadsheet;
+    }
+
+    protected static function convertStringEncoding($string, $charset)
+    {
+        if ($charset != 'UTF-8') {
+            return StringHelper::convertEncoding($string, 'UTF-8', $charset);
+        }
+
+        return $string;
+    }
+
+    protected function parseRichText($is)
+    {
+        $value = new RichText();
+
+        $value->createText(self::convertStringEncoding($is, $this->charSet));
+
+        return $value;
+    }
+
+    private function parseStyles(SimpleXMLElement $xml, array $namespaces): void
+    {
+        if (!isset($xml->Styles)) {
+            return;
+        }
+
+        foreach ($xml->Styles[0] as $style) {
+            $style_ss = $style->attributes($namespaces['ss']);
+            $styleID = (string) $style_ss['ID'];
+            $this->styles[$styleID] = (isset($this->styles['Default'])) ? $this->styles['Default'] : [];
+            foreach ($style as $styleType => $styleData) {
+                $styleAttributes = $styleData->attributes($namespaces['ss']);
+                switch ($styleType) {
+                    case 'Alignment':
+                        $this->parseStyleAlignment($styleID, $styleAttributes);
+
+                        break;
+                    case 'Borders':
+                        $this->parseStyleBorders($styleID, $styleData, $namespaces);
+
+                        break;
+                    case 'Font':
+                        $this->parseStyleFont($styleID, $styleAttributes);
+
+                        break;
+                    case 'Interior':
+                        $this->parseStyleInterior($styleID, $styleAttributes);
+
+                        break;
+                    case 'NumberFormat':
+                        $this->parseStyleNumberFormat($styleID, $styleAttributes);
+
+                        break;
+                }
+            }
+        }
+    }
+
+    /**
+     * @param string $styleID
+     */
+    private function parseStyleAlignment($styleID, SimpleXMLElement $styleAttributes): void
+    {
+        $verticalAlignmentStyles = [
+            Alignment::VERTICAL_BOTTOM,
+            Alignment::VERTICAL_TOP,
+            Alignment::VERTICAL_CENTER,
+            Alignment::VERTICAL_JUSTIFY,
+        ];
+        $horizontalAlignmentStyles = [
+            Alignment::HORIZONTAL_GENERAL,
+            Alignment::HORIZONTAL_LEFT,
+            Alignment::HORIZONTAL_RIGHT,
+            Alignment::HORIZONTAL_CENTER,
+            Alignment::HORIZONTAL_CENTER_CONTINUOUS,
+            Alignment::HORIZONTAL_JUSTIFY,
+        ];
+
+        foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
+            $styleAttributeValue = (string) $styleAttributeValue;
+            switch ($styleAttributeKey) {
+                case 'Vertical':
+                    if (self::identifyFixedStyleValue($verticalAlignmentStyles, $styleAttributeValue)) {
+                        $this->styles[$styleID]['alignment']['vertical'] = $styleAttributeValue;
+                    }
+
+                    break;
+                case 'Horizontal':
+                    if (self::identifyFixedStyleValue($horizontalAlignmentStyles, $styleAttributeValue)) {
+                        $this->styles[$styleID]['alignment']['horizontal'] = $styleAttributeValue;
+                    }
+
+                    break;
+                case 'WrapText':
+                    $this->styles[$styleID]['alignment']['wrapText'] = true;
+
+                    break;
+            }
+        }
+    }
+
+    /**
+     * @param $styleID
+     */
+    private function parseStyleBorders($styleID, SimpleXMLElement $styleData, array $namespaces): void
+    {
+        foreach ($styleData->Border as $borderStyle) {
+            $borderAttributes = $borderStyle->attributes($namespaces['ss']);
+            $thisBorder = [];
+            foreach ($borderAttributes as $borderStyleKey => $borderStyleValue) {
+                switch ($borderStyleKey) {
+                    case 'LineStyle':
+                        $thisBorder['borderStyle'] = Border::BORDER_MEDIUM;
+
+                        break;
+                    case 'Weight':
+                        break;
+                    case 'Position':
+                        $borderPosition = strtolower($borderStyleValue);
+
+                        break;
+                    case 'Color':
+                        $borderColour = substr($borderStyleValue, 1);
+                        $thisBorder['color']['rgb'] = $borderColour;
+
+                        break;
+                }
+            }
+            if (!empty($thisBorder)) {
+                if (($borderPosition == 'left') || ($borderPosition == 'right') || ($borderPosition == 'top') || ($borderPosition == 'bottom')) {
+                    $this->styles[$styleID]['borders'][$borderPosition] = $thisBorder;
+                }
+            }
+        }
+    }
+
+    /**
+     * @param $styleID
+     */
+    private function parseStyleFont($styleID, SimpleXMLElement $styleAttributes): void
+    {
+        $underlineStyles = [
+            Font::UNDERLINE_NONE,
+            Font::UNDERLINE_DOUBLE,
+            Font::UNDERLINE_DOUBLEACCOUNTING,
+            Font::UNDERLINE_SINGLE,
+            Font::UNDERLINE_SINGLEACCOUNTING,
+        ];
+
+        foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
+            $styleAttributeValue = (string) $styleAttributeValue;
+            switch ($styleAttributeKey) {
+                case 'FontName':
+                    $this->styles[$styleID]['font']['name'] = $styleAttributeValue;
+
+                    break;
+                case 'Size':
+                    $this->styles[$styleID]['font']['size'] = $styleAttributeValue;
+
+                    break;
+                case 'Color':
+                    $this->styles[$styleID]['font']['color']['rgb'] = substr($styleAttributeValue, 1);
+
+                    break;
+                case 'Bold':
+                    $this->styles[$styleID]['font']['bold'] = true;
+
+                    break;
+                case 'Italic':
+                    $this->styles[$styleID]['font']['italic'] = true;
+
+                    break;
+                case 'Underline':
+                    if (self::identifyFixedStyleValue($underlineStyles, $styleAttributeValue)) {
+                        $this->styles[$styleID]['font']['underline'] = $styleAttributeValue;
+                    }
+
+                    break;
+            }
+        }
+    }
+
+    /**
+     * @param $styleID
+     */
+    private function parseStyleInterior($styleID, SimpleXMLElement $styleAttributes): void
+    {
+        foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
+            switch ($styleAttributeKey) {
+                case 'Color':
+                    $this->styles[$styleID]['fill']['color']['rgb'] = substr($styleAttributeValue, 1);
+
+                    break;
+                case 'Pattern':
+                    $this->styles[$styleID]['fill']['fillType'] = strtolower($styleAttributeValue);
+
+                    break;
+            }
+        }
+    }
+
+    /**
+     * @param $styleID
+     */
+    private function parseStyleNumberFormat($styleID, SimpleXMLElement $styleAttributes): void
+    {
+        $fromFormats = ['\-', '\ '];
+        $toFormats = ['-', ' '];
+
+        foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
+            $styleAttributeValue = str_replace($fromFormats, $toFormats, $styleAttributeValue);
+            switch ($styleAttributeValue) {
+                case 'Short Date':
+                    $styleAttributeValue = 'dd/mm/yyyy';
+
+                    break;
+            }
+
+            if ($styleAttributeValue > '') {
+                $this->styles[$styleID]['numberFormat']['formatCode'] = $styleAttributeValue;
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/ReferenceHelper.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/ReferenceHelper.php
new file mode 100755
index 0000000..0b0f6dd
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/ReferenceHelper.php
@@ -0,0 +1,898 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Cell\DataType;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+
+class ReferenceHelper
+{
+    /**    Constants                */
+    /**    Regular Expressions      */
+    const REFHELPER_REGEXP_CELLREF = '((\w*|\'[^!]*\')!)?(?<![:a-z\$])(\$?[a-z]{1,3}\$?\d+)(?=[^:!\d\'])';
+    const REFHELPER_REGEXP_CELLRANGE = '((\w*|\'[^!]*\')!)?(\$?[a-z]{1,3}\$?\d+):(\$?[a-z]{1,3}\$?\d+)';
+    const REFHELPER_REGEXP_ROWRANGE = '((\w*|\'[^!]*\')!)?(\$?\d+):(\$?\d+)';
+    const REFHELPER_REGEXP_COLRANGE = '((\w*|\'[^!]*\')!)?(\$?[a-z]{1,3}):(\$?[a-z]{1,3})';
+
+    /**
+     * Instance of this class.
+     *
+     * @var ReferenceHelper
+     */
+    private static $instance;
+
+    /**
+     * Get an instance of this class.
+     *
+     * @return ReferenceHelper
+     */
+    public static function getInstance()
+    {
+        if (!isset(self::$instance) || (self::$instance === null)) {
+            self::$instance = new self();
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     * Create a new ReferenceHelper.
+     */
+    protected function __construct()
+    {
+    }
+
+    /**
+     * Compare two column addresses
+     * Intended for use as a Callback function for sorting column addresses by column.
+     *
+     * @param string $a First column to test (e.g. 'AA')
+     * @param string $b Second column to test (e.g. 'Z')
+     *
+     * @return int
+     */
+    public static function columnSort($a, $b)
+    {
+        return strcasecmp(strlen($a) . $a, strlen($b) . $b);
+    }
+
+    /**
+     * Compare two column addresses
+     * Intended for use as a Callback function for reverse sorting column addresses by column.
+     *
+     * @param string $a First column to test (e.g. 'AA')
+     * @param string $b Second column to test (e.g. 'Z')
+     *
+     * @return int
+     */
+    public static function columnReverseSort($a, $b)
+    {
+        return 1 - strcasecmp(strlen($a) . $a, strlen($b) . $b);
+    }
+
+    /**
+     * Compare two cell addresses
+     * Intended for use as a Callback function for sorting cell addresses by column and row.
+     *
+     * @param string $a First cell to test (e.g. 'AA1')
+     * @param string $b Second cell to test (e.g. 'Z1')
+     *
+     * @return int
+     */
+    public static function cellSort($a, $b)
+    {
+        [$ac, $ar] = sscanf($a, '%[A-Z]%d');
+        [$bc, $br] = sscanf($b, '%[A-Z]%d');
+
+        if ($ar === $br) {
+            return strcasecmp(strlen($ac) . $ac, strlen($bc) . $bc);
+        }
+
+        return ($ar < $br) ? -1 : 1;
+    }
+
+    /**
+     * Compare two cell addresses
+     * Intended for use as a Callback function for sorting cell addresses by column and row.
+     *
+     * @param string $a First cell to test (e.g. 'AA1')
+     * @param string $b Second cell to test (e.g. 'Z1')
+     *
+     * @return int
+     */
+    public static function cellReverseSort($a, $b)
+    {
+        [$ac, $ar] = sscanf($a, '%[A-Z]%d');
+        [$bc, $br] = sscanf($b, '%[A-Z]%d');
+
+        if ($ar === $br) {
+            return 1 - strcasecmp(strlen($ac) . $ac, strlen($bc) . $bc);
+        }
+
+        return ($ar < $br) ? 1 : -1;
+    }
+
+    /**
+     * Test whether a cell address falls within a defined range of cells.
+     *
+     * @param string $cellAddress Address of the cell we're testing
+     * @param int $beforeRow Number of the row we're inserting/deleting before
+     * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion)
+     * @param int $beforeColumnIndex Index number of the column we're inserting/deleting before
+     * @param int $pNumCols Number of columns to insert/delete (negative values indicate deletion)
+     *
+     * @return bool
+     */
+    private static function cellAddressInDeleteRange($cellAddress, $beforeRow, $pNumRows, $beforeColumnIndex, $pNumCols)
+    {
+        [$cellColumn, $cellRow] = Coordinate::coordinateFromString($cellAddress);
+        $cellColumnIndex = Coordinate::columnIndexFromString($cellColumn);
+        //    Is cell within the range of rows/columns if we're deleting
+        if ($pNumRows < 0 &&
+            ($cellRow >= ($beforeRow + $pNumRows)) &&
+            ($cellRow < $beforeRow)) {
+            return true;
+        } elseif ($pNumCols < 0 &&
+            ($cellColumnIndex >= ($beforeColumnIndex + $pNumCols)) &&
+            ($cellColumnIndex < $beforeColumnIndex)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Update page breaks when inserting/deleting rows/columns.
+     *
+     * @param Worksheet $pSheet The worksheet that we're editing
+     * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
+     * @param int $beforeColumnIndex Index number of the column we're inserting/deleting before
+     * @param int $pNumCols Number of columns to insert/delete (negative values indicate deletion)
+     * @param int $beforeRow Number of the row we're inserting/deleting before
+     * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion)
+     */
+    protected function adjustPageBreaks(Worksheet $pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows): void
+    {
+        $aBreaks = $pSheet->getBreaks();
+        ($pNumCols > 0 || $pNumRows > 0) ?
+            uksort($aBreaks, ['self', 'cellReverseSort']) : uksort($aBreaks, ['self', 'cellSort']);
+
+        foreach ($aBreaks as $key => $value) {
+            if (self::cellAddressInDeleteRange($key, $beforeRow, $pNumRows, $beforeColumnIndex, $pNumCols)) {
+                //    If we're deleting, then clear any defined breaks that are within the range
+                //        of rows/columns that we're deleting
+                $pSheet->setBreak($key, Worksheet::BREAK_NONE);
+            } else {
+                //    Otherwise update any affected breaks by inserting a new break at the appropriate point
+                //        and removing the old affected break
+                $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
+                if ($key != $newReference) {
+                    $pSheet->setBreak($newReference, $value)
+                        ->setBreak($key, Worksheet::BREAK_NONE);
+                }
+            }
+        }
+    }
+
+    /**
+     * Update cell comments when inserting/deleting rows/columns.
+     *
+     * @param Worksheet $pSheet The worksheet that we're editing
+     * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
+     * @param int $beforeColumnIndex Index number of the column we're inserting/deleting before
+     * @param int $pNumCols Number of columns to insert/delete (negative values indicate deletion)
+     * @param int $beforeRow Number of the row we're inserting/deleting before
+     * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion)
+     */
+    protected function adjustComments($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows): void
+    {
+        $aComments = $pSheet->getComments();
+        $aNewComments = []; // the new array of all comments
+
+        foreach ($aComments as $key => &$value) {
+            // Any comments inside a deleted range will be ignored
+            if (!self::cellAddressInDeleteRange($key, $beforeRow, $pNumRows, $beforeColumnIndex, $pNumCols)) {
+                // Otherwise build a new array of comments indexed by the adjusted cell reference
+                $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
+                $aNewComments[$newReference] = $value;
+            }
+        }
+        //    Replace the comments array with the new set of comments
+        $pSheet->setComments($aNewComments);
+    }
+
+    /**
+     * Update hyperlinks when inserting/deleting rows/columns.
+     *
+     * @param Worksheet $pSheet The worksheet that we're editing
+     * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
+     * @param int $beforeColumnIndex Index number of the column we're inserting/deleting before
+     * @param int $pNumCols Number of columns to insert/delete (negative values indicate deletion)
+     * @param int $beforeRow Number of the row we're inserting/deleting before
+     * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion)
+     */
+    protected function adjustHyperlinks($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows): void
+    {
+        $aHyperlinkCollection = $pSheet->getHyperlinkCollection();
+        ($pNumCols > 0 || $pNumRows > 0) ?
+            uksort($aHyperlinkCollection, ['self', 'cellReverseSort']) : uksort($aHyperlinkCollection, ['self', 'cellSort']);
+
+        foreach ($aHyperlinkCollection as $key => $value) {
+            $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
+            if ($key != $newReference) {
+                $pSheet->setHyperlink($newReference, $value);
+                $pSheet->setHyperlink($key, null);
+            }
+        }
+    }
+
+    /**
+     * Update data validations when inserting/deleting rows/columns.
+     *
+     * @param Worksheet $pSheet The worksheet that we're editing
+     * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
+     * @param int $beforeColumnIndex Index number of the column we're inserting/deleting before
+     * @param int $pNumCols Number of columns to insert/delete (negative values indicate deletion)
+     * @param int $beforeRow Number of the row we're inserting/deleting before
+     * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion)
+     */
+    protected function adjustDataValidations($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows): void
+    {
+        $aDataValidationCollection = $pSheet->getDataValidationCollection();
+        ($pNumCols > 0 || $pNumRows > 0) ?
+            uksort($aDataValidationCollection, ['self', 'cellReverseSort']) : uksort($aDataValidationCollection, ['self', 'cellSort']);
+
+        foreach ($aDataValidationCollection as $key => $value) {
+            $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
+            if ($key != $newReference) {
+                $pSheet->setDataValidation($newReference, $value);
+                $pSheet->setDataValidation($key, null);
+            }
+        }
+    }
+
+    /**
+     * Update merged cells when inserting/deleting rows/columns.
+     *
+     * @param Worksheet $pSheet The worksheet that we're editing
+     * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
+     * @param int $beforeColumnIndex Index number of the column we're inserting/deleting before
+     * @param int $pNumCols Number of columns to insert/delete (negative values indicate deletion)
+     * @param int $beforeRow Number of the row we're inserting/deleting before
+     * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion)
+     */
+    protected function adjustMergeCells($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows): void
+    {
+        $aMergeCells = $pSheet->getMergeCells();
+        $aNewMergeCells = []; // the new array of all merge cells
+        foreach ($aMergeCells as $key => &$value) {
+            $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
+            $aNewMergeCells[$newReference] = $newReference;
+        }
+        $pSheet->setMergeCells($aNewMergeCells); // replace the merge cells array
+    }
+
+    /**
+     * Update protected cells when inserting/deleting rows/columns.
+     *
+     * @param Worksheet $pSheet The worksheet that we're editing
+     * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
+     * @param int $beforeColumnIndex Index number of the column we're inserting/deleting before
+     * @param int $pNumCols Number of columns to insert/delete (negative values indicate deletion)
+     * @param int $beforeRow Number of the row we're inserting/deleting before
+     * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion)
+     */
+    protected function adjustProtectedCells($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows): void
+    {
+        $aProtectedCells = $pSheet->getProtectedCells();
+        ($pNumCols > 0 || $pNumRows > 0) ?
+            uksort($aProtectedCells, ['self', 'cellReverseSort']) : uksort($aProtectedCells, ['self', 'cellSort']);
+        foreach ($aProtectedCells as $key => $value) {
+            $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
+            if ($key != $newReference) {
+                $pSheet->protectCells($newReference, $value, true);
+                $pSheet->unprotectCells($key);
+            }
+        }
+    }
+
+    /**
+     * Update column dimensions when inserting/deleting rows/columns.
+     *
+     * @param Worksheet $pSheet The worksheet that we're editing
+     * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
+     * @param int $beforeColumnIndex Index number of the column we're inserting/deleting before
+     * @param int $pNumCols Number of columns to insert/delete (negative values indicate deletion)
+     * @param int $beforeRow Number of the row we're inserting/deleting before
+     * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion)
+     */
+    protected function adjustColumnDimensions($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows): void
+    {
+        $aColumnDimensions = array_reverse($pSheet->getColumnDimensions(), true);
+        if (!empty($aColumnDimensions)) {
+            foreach ($aColumnDimensions as $objColumnDimension) {
+                $newReference = $this->updateCellReference($objColumnDimension->getColumnIndex() . '1', $pBefore, $pNumCols, $pNumRows);
+                [$newReference] = Coordinate::coordinateFromString($newReference);
+                if ($objColumnDimension->getColumnIndex() != $newReference) {
+                    $objColumnDimension->setColumnIndex($newReference);
+                }
+            }
+            $pSheet->refreshColumnDimensions();
+        }
+    }
+
+    /**
+     * Update row dimensions when inserting/deleting rows/columns.
+     *
+     * @param Worksheet $pSheet The worksheet that we're editing
+     * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
+     * @param int $beforeColumnIndex Index number of the column we're inserting/deleting before
+     * @param int $pNumCols Number of columns to insert/delete (negative values indicate deletion)
+     * @param int $beforeRow Number of the row we're inserting/deleting before
+     * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion)
+     */
+    protected function adjustRowDimensions($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows): void
+    {
+        $aRowDimensions = array_reverse($pSheet->getRowDimensions(), true);
+        if (!empty($aRowDimensions)) {
+            foreach ($aRowDimensions as $objRowDimension) {
+                $newReference = $this->updateCellReference('A' . $objRowDimension->getRowIndex(), $pBefore, $pNumCols, $pNumRows);
+                [, $newReference] = Coordinate::coordinateFromString($newReference);
+                if ($objRowDimension->getRowIndex() != $newReference) {
+                    $objRowDimension->setRowIndex($newReference);
+                }
+            }
+            $pSheet->refreshRowDimensions();
+
+            $copyDimension = $pSheet->getRowDimension($beforeRow - 1);
+            for ($i = $beforeRow; $i <= $beforeRow - 1 + $pNumRows; ++$i) {
+                $newDimension = $pSheet->getRowDimension($i);
+                $newDimension->setRowHeight($copyDimension->getRowHeight());
+                $newDimension->setVisible($copyDimension->getVisible());
+                $newDimension->setOutlineLevel($copyDimension->getOutlineLevel());
+                $newDimension->setCollapsed($copyDimension->getCollapsed());
+            }
+        }
+    }
+
+    /**
+     * Insert a new column or row, updating all possible related data.
+     *
+     * @param string $pBefore Insert before this cell address (e.g. 'A1')
+     * @param int $pNumCols Number of columns to insert/delete (negative values indicate deletion)
+     * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion)
+     * @param Worksheet $pSheet The worksheet that we're editing
+     */
+    public function insertNewBefore($pBefore, $pNumCols, $pNumRows, Worksheet $pSheet): void
+    {
+        $remove = ($pNumCols < 0 || $pNumRows < 0);
+        $allCoordinates = $pSheet->getCoordinates();
+
+        // Get coordinate of $pBefore
+        [$beforeColumn, $beforeRow] = Coordinate::coordinateFromString($pBefore);
+        $beforeColumnIndex = Coordinate::columnIndexFromString($beforeColumn);
+
+        // Clear cells if we are removing columns or rows
+        $highestColumn = $pSheet->getHighestColumn();
+        $highestRow = $pSheet->getHighestRow();
+
+        // 1. Clear column strips if we are removing columns
+        if ($pNumCols < 0 && $beforeColumnIndex - 2 + $pNumCols > 0) {
+            for ($i = 1; $i <= $highestRow - 1; ++$i) {
+                for ($j = $beforeColumnIndex - 1 + $pNumCols; $j <= $beforeColumnIndex - 2; ++$j) {
+                    $coordinate = Coordinate::stringFromColumnIndex($j + 1) . $i;
+                    $pSheet->removeConditionalStyles($coordinate);
+                    if ($pSheet->cellExists($coordinate)) {
+                        $pSheet->getCell($coordinate)->setValueExplicit('', DataType::TYPE_NULL);
+                        $pSheet->getCell($coordinate)->setXfIndex(0);
+                    }
+                }
+            }
+        }
+
+        // 2. Clear row strips if we are removing rows
+        if ($pNumRows < 0 && $beforeRow - 1 + $pNumRows > 0) {
+            for ($i = $beforeColumnIndex - 1; $i <= Coordinate::columnIndexFromString($highestColumn) - 1; ++$i) {
+                for ($j = $beforeRow + $pNumRows; $j <= $beforeRow - 1; ++$j) {
+                    $coordinate = Coordinate::stringFromColumnIndex($i + 1) . $j;
+                    $pSheet->removeConditionalStyles($coordinate);
+                    if ($pSheet->cellExists($coordinate)) {
+                        $pSheet->getCell($coordinate)->setValueExplicit('', DataType::TYPE_NULL);
+                        $pSheet->getCell($coordinate)->setXfIndex(0);
+                    }
+                }
+            }
+        }
+
+        // Loop through cells, bottom-up, and change cell coordinate
+        if ($remove) {
+            // It's faster to reverse and pop than to use unshift, especially with large cell collections
+            $allCoordinates = array_reverse($allCoordinates);
+        }
+        while ($coordinate = array_pop($allCoordinates)) {
+            $cell = $pSheet->getCell($coordinate);
+            $cellIndex = Coordinate::columnIndexFromString($cell->getColumn());
+
+            if ($cellIndex - 1 + $pNumCols < 0) {
+                continue;
+            }
+
+            // New coordinate
+            $newCoordinate = Coordinate::stringFromColumnIndex($cellIndex + $pNumCols) . ($cell->getRow() + $pNumRows);
+
+            // Should the cell be updated? Move value and cellXf index from one cell to another.
+            if (($cellIndex >= $beforeColumnIndex) && ($cell->getRow() >= $beforeRow)) {
+                // Update cell styles
+                $pSheet->getCell($newCoordinate)->setXfIndex($cell->getXfIndex());
+
+                // Insert this cell at its new location
+                if ($cell->getDataType() == DataType::TYPE_FORMULA) {
+                    // Formula should be adjusted
+                    $pSheet->getCell($newCoordinate)
+                        ->setValue($this->updateFormulaReferences($cell->getValue(), $pBefore, $pNumCols, $pNumRows, $pSheet->getTitle()));
+                } else {
+                    // Formula should not be adjusted
+                    $pSheet->getCell($newCoordinate)->setValue($cell->getValue());
+                }
+
+                // Clear the original cell
+                $pSheet->getCellCollection()->delete($coordinate);
+            } else {
+                /*    We don't need to update styles for rows/columns before our insertion position,
+                        but we do still need to adjust any formulae    in those cells                    */
+                if ($cell->getDataType() == DataType::TYPE_FORMULA) {
+                    // Formula should be adjusted
+                    $cell->setValue($this->updateFormulaReferences($cell->getValue(), $pBefore, $pNumCols, $pNumRows, $pSheet->getTitle()));
+                }
+            }
+        }
+
+        // Duplicate styles for the newly inserted cells
+        $highestColumn = $pSheet->getHighestColumn();
+        $highestRow = $pSheet->getHighestRow();
+
+        if ($pNumCols > 0 && $beforeColumnIndex - 2 > 0) {
+            for ($i = $beforeRow; $i <= $highestRow - 1; ++$i) {
+                // Style
+                $coordinate = Coordinate::stringFromColumnIndex($beforeColumnIndex - 1) . $i;
+                if ($pSheet->cellExists($coordinate)) {
+                    $xfIndex = $pSheet->getCell($coordinate)->getXfIndex();
+                    $conditionalStyles = $pSheet->conditionalStylesExists($coordinate) ?
+                        $pSheet->getConditionalStyles($coordinate) : false;
+                    for ($j = $beforeColumnIndex; $j <= $beforeColumnIndex - 1 + $pNumCols; ++$j) {
+                        $pSheet->getCellByColumnAndRow($j, $i)->setXfIndex($xfIndex);
+                        if ($conditionalStyles) {
+                            $cloned = [];
+                            foreach ($conditionalStyles as $conditionalStyle) {
+                                $cloned[] = clone $conditionalStyle;
+                            }
+                            $pSheet->setConditionalStyles(Coordinate::stringFromColumnIndex($j) . $i, $cloned);
+                        }
+                    }
+                }
+            }
+        }
+
+        if ($pNumRows > 0 && $beforeRow - 1 > 0) {
+            for ($i = $beforeColumnIndex; $i <= Coordinate::columnIndexFromString($highestColumn); ++$i) {
+                // Style
+                $coordinate = Coordinate::stringFromColumnIndex($i) . ($beforeRow - 1);
+                if ($pSheet->cellExists($coordinate)) {
+                    $xfIndex = $pSheet->getCell($coordinate)->getXfIndex();
+                    $conditionalStyles = $pSheet->conditionalStylesExists($coordinate) ?
+                        $pSheet->getConditionalStyles($coordinate) : false;
+                    for ($j = $beforeRow; $j <= $beforeRow - 1 + $pNumRows; ++$j) {
+                        $pSheet->getCell(Coordinate::stringFromColumnIndex($i) . $j)->setXfIndex($xfIndex);
+                        if ($conditionalStyles) {
+                            $cloned = [];
+                            foreach ($conditionalStyles as $conditionalStyle) {
+                                $cloned[] = clone $conditionalStyle;
+                            }
+                            $pSheet->setConditionalStyles(Coordinate::stringFromColumnIndex($i) . $j, $cloned);
+                        }
+                    }
+                }
+            }
+        }
+
+        // Update worksheet: column dimensions
+        $this->adjustColumnDimensions($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);
+
+        // Update worksheet: row dimensions
+        $this->adjustRowDimensions($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);
+
+        //    Update worksheet: page breaks
+        $this->adjustPageBreaks($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);
+
+        //    Update worksheet: comments
+        $this->adjustComments($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);
+
+        // Update worksheet: hyperlinks
+        $this->adjustHyperlinks($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);
+
+        // Update worksheet: data validations
+        $this->adjustDataValidations($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);
+
+        // Update worksheet: merge cells
+        $this->adjustMergeCells($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);
+
+        // Update worksheet: protected cells
+        $this->adjustProtectedCells($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);
+
+        // Update worksheet: autofilter
+        $autoFilter = $pSheet->getAutoFilter();
+        $autoFilterRange = $autoFilter->getRange();
+        if (!empty($autoFilterRange)) {
+            if ($pNumCols != 0) {
+                $autoFilterColumns = $autoFilter->getColumns();
+                if (count($autoFilterColumns) > 0) {
+                    $column = '';
+                    $row = 0;
+                    sscanf($pBefore, '%[A-Z]%d', $column, $row);
+                    $columnIndex = Coordinate::columnIndexFromString($column);
+                    [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($autoFilterRange);
+                    if ($columnIndex <= $rangeEnd[0]) {
+                        if ($pNumCols < 0) {
+                            //    If we're actually deleting any columns that fall within the autofilter range,
+                            //        then we delete any rules for those columns
+                            $deleteColumn = $columnIndex + $pNumCols - 1;
+                            $deleteCount = abs($pNumCols);
+                            for ($i = 1; $i <= $deleteCount; ++$i) {
+                                if (isset($autoFilterColumns[Coordinate::stringFromColumnIndex($deleteColumn + 1)])) {
+                                    $autoFilter->clearColumn(Coordinate::stringFromColumnIndex($deleteColumn + 1));
+                                }
+                                ++$deleteColumn;
+                            }
+                        }
+                        $startCol = ($columnIndex > $rangeStart[0]) ? $columnIndex : $rangeStart[0];
+
+                        //    Shuffle columns in autofilter range
+                        if ($pNumCols > 0) {
+                            $startColRef = $startCol;
+                            $endColRef = $rangeEnd[0];
+                            $toColRef = $rangeEnd[0] + $pNumCols;
+
+                            do {
+                                $autoFilter->shiftColumn(Coordinate::stringFromColumnIndex($endColRef), Coordinate::stringFromColumnIndex($toColRef));
+                                --$endColRef;
+                                --$toColRef;
+                            } while ($startColRef <= $endColRef);
+                        } else {
+                            //    For delete, we shuffle from beginning to end to avoid overwriting
+                            $startColID = Coordinate::stringFromColumnIndex($startCol);
+                            $toColID = Coordinate::stringFromColumnIndex($startCol + $pNumCols);
+                            $endColID = Coordinate::stringFromColumnIndex($rangeEnd[0] + 1);
+                            do {
+                                $autoFilter->shiftColumn($startColID, $toColID);
+                                ++$startColID;
+                                ++$toColID;
+                            } while ($startColID != $endColID);
+                        }
+                    }
+                }
+            }
+            $pSheet->setAutoFilter($this->updateCellReference($autoFilterRange, $pBefore, $pNumCols, $pNumRows));
+        }
+
+        // Update worksheet: freeze pane
+        if ($pSheet->getFreezePane()) {
+            $splitCell = $pSheet->getFreezePane();
+            $topLeftCell = $pSheet->getTopLeftCell();
+
+            $splitCell = $this->updateCellReference($splitCell, $pBefore, $pNumCols, $pNumRows);
+            $topLeftCell = $this->updateCellReference($topLeftCell, $pBefore, $pNumCols, $pNumRows);
+
+            $pSheet->freezePane($splitCell, $topLeftCell);
+        }
+
+        // Page setup
+        if ($pSheet->getPageSetup()->isPrintAreaSet()) {
+            $pSheet->getPageSetup()->setPrintArea($this->updateCellReference($pSheet->getPageSetup()->getPrintArea(), $pBefore, $pNumCols, $pNumRows));
+        }
+
+        // Update worksheet: drawings
+        $aDrawings = $pSheet->getDrawingCollection();
+        foreach ($aDrawings as $objDrawing) {
+            $newReference = $this->updateCellReference($objDrawing->getCoordinates(), $pBefore, $pNumCols, $pNumRows);
+            if ($objDrawing->getCoordinates() != $newReference) {
+                $objDrawing->setCoordinates($newReference);
+            }
+        }
+
+        // Update workbook: named ranges
+        if (count($pSheet->getParent()->getNamedRanges()) > 0) {
+            foreach ($pSheet->getParent()->getNamedRanges() as $namedRange) {
+                if ($namedRange->getWorksheet()->getHashCode() == $pSheet->getHashCode()) {
+                    $namedRange->setRange($this->updateCellReference($namedRange->getRange(), $pBefore, $pNumCols, $pNumRows));
+                }
+            }
+        }
+
+        // Garbage collect
+        $pSheet->garbageCollect();
+    }
+
+    /**
+     * Update references within formulas.
+     *
+     * @param string $pFormula Formula to update
+     * @param string $pBefore Insert before this one
+     * @param int $pNumCols Number of columns to insert
+     * @param int $pNumRows Number of rows to insert
+     * @param string $sheetName Worksheet name/title
+     *
+     * @return string Updated formula
+     */
+    public function updateFormulaReferences($pFormula = '', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0, $sheetName = '')
+    {
+        //    Update cell references in the formula
+        $formulaBlocks = explode('"', $pFormula);
+        $i = false;
+        foreach ($formulaBlocks as &$formulaBlock) {
+            //    Ignore blocks that were enclosed in quotes (alternating entries in the $formulaBlocks array after the explode)
+            if ($i = !$i) {
+                $adjustCount = 0;
+                $newCellTokens = $cellTokens = [];
+                //    Search for row ranges (e.g. 'Sheet1'!3:5 or 3:5) with or without $ absolutes (e.g. $3:5)
+                $matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_ROWRANGE . '/i', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
+                if ($matchCount > 0) {
+                    foreach ($matches as $match) {
+                        $fromString = ($match[2] > '') ? $match[2] . '!' : '';
+                        $fromString .= $match[3] . ':' . $match[4];
+                        $modified3 = substr($this->updateCellReference('$A' . $match[3], $pBefore, $pNumCols, $pNumRows), 2);
+                        $modified4 = substr($this->updateCellReference('$A' . $match[4], $pBefore, $pNumCols, $pNumRows), 2);
+
+                        if ($match[3] . ':' . $match[4] !== $modified3 . ':' . $modified4) {
+                            if (($match[2] == '') || (trim($match[2], "'") == $sheetName)) {
+                                $toString = ($match[2] > '') ? $match[2] . '!' : '';
+                                $toString .= $modified3 . ':' . $modified4;
+                                //    Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
+                                $column = 100000;
+                                $row = 10000000 + trim($match[3], '$');
+                                $cellIndex = $column . $row;
+
+                                $newCellTokens[$cellIndex] = preg_quote($toString, '/');
+                                $cellTokens[$cellIndex] = '/(?<!\d\$\!)' . preg_quote($fromString, '/') . '(?!\d)/i';
+                                ++$adjustCount;
+                            }
+                        }
+                    }
+                }
+                //    Search for column ranges (e.g. 'Sheet1'!C:E or C:E) with or without $ absolutes (e.g. $C:E)
+                $matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_COLRANGE . '/i', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
+                if ($matchCount > 0) {
+                    foreach ($matches as $match) {
+                        $fromString = ($match[2] > '') ? $match[2] . '!' : '';
+                        $fromString .= $match[3] . ':' . $match[4];
+                        $modified3 = substr($this->updateCellReference($match[3] . '$1', $pBefore, $pNumCols, $pNumRows), 0, -2);
+                        $modified4 = substr($this->updateCellReference($match[4] . '$1', $pBefore, $pNumCols, $pNumRows), 0, -2);
+
+                        if ($match[3] . ':' . $match[4] !== $modified3 . ':' . $modified4) {
+                            if (($match[2] == '') || (trim($match[2], "'") == $sheetName)) {
+                                $toString = ($match[2] > '') ? $match[2] . '!' : '';
+                                $toString .= $modified3 . ':' . $modified4;
+                                //    Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
+                                $column = Coordinate::columnIndexFromString(trim($match[3], '$')) + 100000;
+                                $row = 10000000;
+                                $cellIndex = $column . $row;
+
+                                $newCellTokens[$cellIndex] = preg_quote($toString, '/');
+                                $cellTokens[$cellIndex] = '/(?<![A-Z\$\!])' . preg_quote($fromString, '/') . '(?![A-Z])/i';
+                                ++$adjustCount;
+                            }
+                        }
+                    }
+                }
+                //    Search for cell ranges (e.g. 'Sheet1'!A3:C5 or A3:C5) with or without $ absolutes (e.g. $A1:C$5)
+                $matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_CELLRANGE . '/i', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
+                if ($matchCount > 0) {
+                    foreach ($matches as $match) {
+                        $fromString = ($match[2] > '') ? $match[2] . '!' : '';
+                        $fromString .= $match[3] . ':' . $match[4];
+                        $modified3 = $this->updateCellReference($match[3], $pBefore, $pNumCols, $pNumRows);
+                        $modified4 = $this->updateCellReference($match[4], $pBefore, $pNumCols, $pNumRows);
+
+                        if ($match[3] . $match[4] !== $modified3 . $modified4) {
+                            if (($match[2] == '') || (trim($match[2], "'") == $sheetName)) {
+                                $toString = ($match[2] > '') ? $match[2] . '!' : '';
+                                $toString .= $modified3 . ':' . $modified4;
+                                [$column, $row] = Coordinate::coordinateFromString($match[3]);
+                                //    Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
+                                $column = Coordinate::columnIndexFromString(trim($column, '$')) + 100000;
+                                $row = trim($row, '$') + 10000000;
+                                $cellIndex = $column . $row;
+
+                                $newCellTokens[$cellIndex] = preg_quote($toString, '/');
+                                $cellTokens[$cellIndex] = '/(?<![A-Z]\$\!)' . preg_quote($fromString, '/') . '(?!\d)/i';
+                                ++$adjustCount;
+                            }
+                        }
+                    }
+                }
+                //    Search for cell references (e.g. 'Sheet1'!A3 or C5) with or without $ absolutes (e.g. $A1 or C$5)
+                $matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_CELLREF . '/i', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
+
+                if ($matchCount > 0) {
+                    foreach ($matches as $match) {
+                        $fromString = ($match[2] > '') ? $match[2] . '!' : '';
+                        $fromString .= $match[3];
+
+                        $modified3 = $this->updateCellReference($match[3], $pBefore, $pNumCols, $pNumRows);
+                        if ($match[3] !== $modified3) {
+                            if (($match[2] == '') || (trim($match[2], "'") == $sheetName)) {
+                                $toString = ($match[2] > '') ? $match[2] . '!' : '';
+                                $toString .= $modified3;
+                                [$column, $row] = Coordinate::coordinateFromString($match[3]);
+                                //    Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
+                                $column = Coordinate::columnIndexFromString(trim($column, '$')) + 100000;
+                                $row = trim($row, '$') + 10000000;
+                                $cellIndex = $row . $column;
+
+                                $newCellTokens[$cellIndex] = preg_quote($toString, '/');
+                                $cellTokens[$cellIndex] = '/(?<![A-Z\$\!])' . preg_quote($fromString, '/') . '(?!\d)/i';
+                                ++$adjustCount;
+                            }
+                        }
+                    }
+                }
+                if ($adjustCount > 0) {
+                    if ($pNumCols > 0 || $pNumRows > 0) {
+                        krsort($cellTokens);
+                        krsort($newCellTokens);
+                    } else {
+                        ksort($cellTokens);
+                        ksort($newCellTokens);
+                    }   //  Update cell references in the formula
+                    $formulaBlock = str_replace('\\', '', preg_replace($cellTokens, $newCellTokens, $formulaBlock));
+                }
+            }
+        }
+        unset($formulaBlock);
+
+        //    Then rebuild the formula string
+        return implode('"', $formulaBlocks);
+    }
+
+    /**
+     * Update cell reference.
+     *
+     * @param string $pCellRange Cell range
+     * @param string $pBefore Insert before this one
+     * @param int $pNumCols Number of columns to increment
+     * @param int $pNumRows Number of rows to increment
+     *
+     * @return string Updated cell range
+     */
+    public function updateCellReference($pCellRange = 'A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0)
+    {
+        // Is it in another worksheet? Will not have to update anything.
+        if (strpos($pCellRange, '!') !== false) {
+            return $pCellRange;
+        // Is it a range or a single cell?
+        } elseif (!Coordinate::coordinateIsRange($pCellRange)) {
+            // Single cell
+            return $this->updateSingleCellReference($pCellRange, $pBefore, $pNumCols, $pNumRows);
+        } elseif (Coordinate::coordinateIsRange($pCellRange)) {
+            // Range
+            return $this->updateCellRange($pCellRange, $pBefore, $pNumCols, $pNumRows);
+        }
+
+        // Return original
+        return $pCellRange;
+    }
+
+    /**
+     * Update named formulas (i.e. containing worksheet references / named ranges).
+     *
+     * @param Spreadsheet $spreadsheet Object to update
+     * @param string $oldName Old name (name to replace)
+     * @param string $newName New name
+     */
+    public function updateNamedFormulas(Spreadsheet $spreadsheet, $oldName = '', $newName = ''): void
+    {
+        if ($oldName == '') {
+            return;
+        }
+
+        foreach ($spreadsheet->getWorksheetIterator() as $sheet) {
+            foreach ($sheet->getCoordinates(false) as $coordinate) {
+                $cell = $sheet->getCell($coordinate);
+                if (($cell !== null) && ($cell->getDataType() == DataType::TYPE_FORMULA)) {
+                    $formula = $cell->getValue();
+                    if (strpos($formula, $oldName) !== false) {
+                        $formula = str_replace("'" . $oldName . "'!", "'" . $newName . "'!", $formula);
+                        $formula = str_replace($oldName . '!', $newName . '!', $formula);
+                        $cell->setValueExplicit($formula, DataType::TYPE_FORMULA);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Update cell range.
+     *
+     * @param string $pCellRange Cell range    (e.g. 'B2:D4', 'B:C' or '2:3')
+     * @param string $pBefore Insert before this one
+     * @param int $pNumCols Number of columns to increment
+     * @param int $pNumRows Number of rows to increment
+     *
+     * @return string Updated cell range
+     */
+    private function updateCellRange($pCellRange = 'A1:A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0)
+    {
+        if (!Coordinate::coordinateIsRange($pCellRange)) {
+            throw new Exception('Only cell ranges may be passed to this method.');
+        }
+
+        // Update range
+        $range = Coordinate::splitRange($pCellRange);
+        $ic = count($range);
+        for ($i = 0; $i < $ic; ++$i) {
+            $jc = count($range[$i]);
+            for ($j = 0; $j < $jc; ++$j) {
+                if (ctype_alpha($range[$i][$j])) {
+                    $r = Coordinate::coordinateFromString($this->updateSingleCellReference($range[$i][$j] . '1', $pBefore, $pNumCols, $pNumRows));
+                    $range[$i][$j] = $r[0];
+                } elseif (ctype_digit($range[$i][$j])) {
+                    $r = Coordinate::coordinateFromString($this->updateSingleCellReference('A' . $range[$i][$j], $pBefore, $pNumCols, $pNumRows));
+                    $range[$i][$j] = $r[1];
+                } else {
+                    $range[$i][$j] = $this->updateSingleCellReference($range[$i][$j], $pBefore, $pNumCols, $pNumRows);
+                }
+            }
+        }
+
+        // Recreate range string
+        return Coordinate::buildRange($range);
+    }
+
+    /**
+     * Update single cell reference.
+     *
+     * @param string $pCellReference Single cell reference
+     * @param string $pBefore Insert before this one
+     * @param int $pNumCols Number of columns to increment
+     * @param int $pNumRows Number of rows to increment
+     *
+     * @return string Updated cell reference
+     */
+    private function updateSingleCellReference($pCellReference = 'A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0)
+    {
+        if (Coordinate::coordinateIsRange($pCellReference)) {
+            throw new Exception('Only single cell references may be passed to this method.');
+        }
+
+        // Get coordinate of $pBefore
+        [$beforeColumn, $beforeRow] = Coordinate::coordinateFromString($pBefore);
+
+        // Get coordinate of $pCellReference
+        [$newColumn, $newRow] = Coordinate::coordinateFromString($pCellReference);
+
+        // Verify which parts should be updated
+        $updateColumn = (($newColumn[0] != '$') && ($beforeColumn[0] != '$') && (Coordinate::columnIndexFromString($newColumn) >= Coordinate::columnIndexFromString($beforeColumn)));
+        $updateRow = (($newRow[0] != '$') && ($beforeRow[0] != '$') && $newRow >= $beforeRow);
+
+        // Create new column reference
+        if ($updateColumn) {
+            $newColumn = Coordinate::stringFromColumnIndex(Coordinate::columnIndexFromString($newColumn) + $pNumCols);
+        }
+
+        // Create new row reference
+        if ($updateRow) {
+            $newRow = $newRow + $pNumRows;
+        }
+
+        // Return new reference
+        return $newColumn . $newRow;
+    }
+
+    /**
+     * __clone implementation. Cloning should not be allowed in a Singleton!
+     */
+    final public function __clone()
+    {
+        throw new Exception('Cloning a Singleton is not allowed!');
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/ITextElement.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/ITextElement.php
new file mode 100755
index 0000000..6995467
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/ITextElement.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\RichText;
+
+interface ITextElement
+{
+    /**
+     * Get text.
+     *
+     * @return string Text
+     */
+    public function getText();
+
+    /**
+     * Set text.
+     *
+     * @param $text string Text
+     *
+     * @return ITextElement
+     */
+    public function setText($text);
+
+    /**
+     * Get font.
+     *
+     * @return null|\PhpOffice\PhpSpreadsheet\Style\Font
+     */
+    public function getFont();
+
+    /**
+     * Get hash code.
+     *
+     * @return string Hash code
+     */
+    public function getHashCode();
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/RichText.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/RichText.php
new file mode 100755
index 0000000..104177b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/RichText.php
@@ -0,0 +1,170 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\RichText;
+
+use PhpOffice\PhpSpreadsheet\Cell\Cell;
+use PhpOffice\PhpSpreadsheet\Cell\DataType;
+use PhpOffice\PhpSpreadsheet\IComparable;
+
+class RichText implements IComparable
+{
+    /**
+     * Rich text elements.
+     *
+     * @var ITextElement[]
+     */
+    private $richTextElements;
+
+    /**
+     * Create a new RichText instance.
+     *
+     * @param Cell $pCell
+     */
+    public function __construct(?Cell $pCell = null)
+    {
+        // Initialise variables
+        $this->richTextElements = [];
+
+        // Rich-Text string attached to cell?
+        if ($pCell !== null) {
+            // Add cell text and style
+            if ($pCell->getValue() != '') {
+                $objRun = new Run($pCell->getValue());
+                $objRun->setFont(clone $pCell->getWorksheet()->getStyle($pCell->getCoordinate())->getFont());
+                $this->addText($objRun);
+            }
+
+            // Set parent value
+            $pCell->setValueExplicit($this, DataType::TYPE_STRING);
+        }
+    }
+
+    /**
+     * Add text.
+     *
+     * @param ITextElement $pText Rich text element
+     *
+     * @return $this
+     */
+    public function addText(ITextElement $pText)
+    {
+        $this->richTextElements[] = $pText;
+
+        return $this;
+    }
+
+    /**
+     * Create text.
+     *
+     * @param string $pText Text
+     *
+     * @return TextElement
+     */
+    public function createText($pText)
+    {
+        $objText = new TextElement($pText);
+        $this->addText($objText);
+
+        return $objText;
+    }
+
+    /**
+     * Create text run.
+     *
+     * @param string $pText Text
+     *
+     * @return Run
+     */
+    public function createTextRun($pText)
+    {
+        $objText = new Run($pText);
+        $this->addText($objText);
+
+        return $objText;
+    }
+
+    /**
+     * Get plain text.
+     *
+     * @return string
+     */
+    public function getPlainText()
+    {
+        // Return value
+        $returnValue = '';
+
+        // Loop through all ITextElements
+        foreach ($this->richTextElements as $text) {
+            $returnValue .= $text->getText();
+        }
+
+        return $returnValue;
+    }
+
+    /**
+     * Convert to string.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return $this->getPlainText();
+    }
+
+    /**
+     * Get Rich Text elements.
+     *
+     * @return ITextElement[]
+     */
+    public function getRichTextElements()
+    {
+        return $this->richTextElements;
+    }
+
+    /**
+     * Set Rich Text elements.
+     *
+     * @param ITextElement[] $textElements Array of elements
+     *
+     * @return $this
+     */
+    public function setRichTextElements(array $textElements)
+    {
+        $this->richTextElements = $textElements;
+
+        return $this;
+    }
+
+    /**
+     * Get hash code.
+     *
+     * @return string Hash code
+     */
+    public function getHashCode()
+    {
+        $hashElements = '';
+        foreach ($this->richTextElements as $element) {
+            $hashElements .= $element->getHashCode();
+        }
+
+        return md5(
+            $hashElements .
+            __CLASS__
+        );
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $vars = get_object_vars($this);
+        foreach ($vars as $key => $value) {
+            if (is_object($value)) {
+                $this->$key = clone $value;
+            } else {
+                $this->$key = $value;
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/Run.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/Run.php
new file mode 100755
index 0000000..592d0e3
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/Run.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\RichText;
+
+use PhpOffice\PhpSpreadsheet\Style\Font;
+
+class Run extends TextElement implements ITextElement
+{
+    /**
+     * Font.
+     *
+     * @var Font
+     */
+    private $font;
+
+    /**
+     * Create a new Run instance.
+     *
+     * @param string $pText Text
+     */
+    public function __construct($pText = '')
+    {
+        parent::__construct($pText);
+        // Initialise variables
+        $this->font = new Font();
+    }
+
+    /**
+     * Get font.
+     *
+     * @return null|\PhpOffice\PhpSpreadsheet\Style\Font
+     */
+    public function getFont()
+    {
+        return $this->font;
+    }
+
+    /**
+     * Set font.
+     *
+     * @param Font $pFont Font
+     *
+     * @return $this
+     */
+    public function setFont(?Font $pFont = null)
+    {
+        $this->font = $pFont;
+
+        return $this;
+    }
+
+    /**
+     * Get hash code.
+     *
+     * @return string Hash code
+     */
+    public function getHashCode()
+    {
+        return md5(
+            $this->getText() .
+            $this->font->getHashCode() .
+            __CLASS__
+        );
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/TextElement.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/TextElement.php
new file mode 100755
index 0000000..f8be5d5
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/TextElement.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\RichText;
+
+class TextElement implements ITextElement
+{
+    /**
+     * Text.
+     *
+     * @var string
+     */
+    private $text;
+
+    /**
+     * Create a new TextElement instance.
+     *
+     * @param string $pText Text
+     */
+    public function __construct($pText = '')
+    {
+        // Initialise variables
+        $this->text = $pText;
+    }
+
+    /**
+     * Get text.
+     *
+     * @return string Text
+     */
+    public function getText()
+    {
+        return $this->text;
+    }
+
+    /**
+     * Set text.
+     *
+     * @param $text string Text
+     *
+     * @return $this
+     */
+    public function setText($text)
+    {
+        $this->text = $text;
+
+        return $this;
+    }
+
+    /**
+     * Get font.
+     *
+     * @return null|\PhpOffice\PhpSpreadsheet\Style\Font
+     */
+    public function getFont()
+    {
+        return null;
+    }
+
+    /**
+     * Get hash code.
+     *
+     * @return string Hash code
+     */
+    public function getHashCode()
+    {
+        return md5(
+            $this->text .
+            __CLASS__
+        );
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $vars = get_object_vars($this);
+        foreach ($vars as $key => $value) {
+            if (is_object($value)) {
+                $this->$key = clone $value;
+            } else {
+                $this->$key = $value;
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php
new file mode 100755
index 0000000..4e0c91e
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php
@@ -0,0 +1,159 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
+use PhpOffice\PhpSpreadsheet\Chart\Renderer\IRenderer;
+use PhpOffice\PhpSpreadsheet\Collection\Memory;
+use Psr\SimpleCache\CacheInterface;
+
+class Settings
+{
+    /**
+     * Class name of the chart renderer used for rendering charts
+     * eg: PhpOffice\PhpSpreadsheet\Chart\Renderer\JpGraph.
+     *
+     * @var string
+     */
+    private static $chartRenderer;
+
+    /**
+     * Default options for libxml loader.
+     *
+     * @var int
+     */
+    private static $libXmlLoaderOptions = null;
+
+    /**
+     * Allow/disallow libxml_disable_entity_loader() call when not thread safe.
+     * Default behaviour is to do the check, but if you're running PHP versions
+     *      7.2 < 7.2.1
+     * then you may need to disable this check to prevent unwanted behaviour in other threads
+     * SECURITY WARNING: Changing this flag is not recommended.
+     *
+     * @var bool
+     */
+    private static $libXmlDisableEntityLoader = true;
+
+    /**
+     * The cache implementation to be used for cell collection.
+     *
+     * @var CacheInterface
+     */
+    private static $cache;
+
+    /**
+     * Set the locale code to use for formula translations and any special formatting.
+     *
+     * @param string $locale The locale code to use (e.g. "fr" or "pt_br" or "en_uk")
+     *
+     * @return bool Success or failure
+     */
+    public static function setLocale($locale)
+    {
+        return Calculation::getInstance()->setLocale($locale);
+    }
+
+    /**
+     * Identify to PhpSpreadsheet the external library to use for rendering charts.
+     *
+     * @param string $rendererClass Class name of the chart renderer
+     *    eg: PhpOffice\PhpSpreadsheet\Chart\Renderer\JpGraph
+     */
+    public static function setChartRenderer($rendererClass): void
+    {
+        if (!is_a($rendererClass, IRenderer::class, true)) {
+            throw new Exception('Chart renderer must implement ' . IRenderer::class);
+        }
+
+        self::$chartRenderer = $rendererClass;
+    }
+
+    /**
+     * Return the Chart Rendering Library that PhpSpreadsheet is currently configured to use.
+     *
+     * @return null|string Class name of the chart renderer
+     *    eg: PhpOffice\PhpSpreadsheet\Chart\Renderer\JpGraph
+     */
+    public static function getChartRenderer()
+    {
+        return self::$chartRenderer;
+    }
+
+    /**
+     * Set default options for libxml loader.
+     *
+     * @param int $options Default options for libxml loader
+     */
+    public static function setLibXmlLoaderOptions($options): void
+    {
+        if ($options === null && defined('LIBXML_DTDLOAD')) {
+            $options = LIBXML_DTDLOAD | LIBXML_DTDATTR;
+        }
+        self::$libXmlLoaderOptions = $options;
+    }
+
+    /**
+     * Get default options for libxml loader.
+     * Defaults to LIBXML_DTDLOAD | LIBXML_DTDATTR when not set explicitly.
+     *
+     * @return int Default options for libxml loader
+     */
+    public static function getLibXmlLoaderOptions()
+    {
+        if (self::$libXmlLoaderOptions === null && defined('LIBXML_DTDLOAD')) {
+            self::setLibXmlLoaderOptions(LIBXML_DTDLOAD | LIBXML_DTDATTR);
+        } elseif (self::$libXmlLoaderOptions === null) {
+            self::$libXmlLoaderOptions = true;
+        }
+
+        return self::$libXmlLoaderOptions;
+    }
+
+    /**
+     * Enable/Disable the entity loader for libxml loader.
+     * Allow/disallow libxml_disable_entity_loader() call when not thread safe.
+     * Default behaviour is to do the check, but if you're running PHP versions
+     *      7.2 < 7.2.1
+     * then you may need to disable this check to prevent unwanted behaviour in other threads
+     * SECURITY WARNING: Changing this flag to false is not recommended.
+     *
+     * @param bool $state
+     */
+    public static function setLibXmlDisableEntityLoader($state): void
+    {
+        self::$libXmlDisableEntityLoader = (bool) $state;
+    }
+
+    /**
+     * Return the state of the entity loader (disabled/enabled) for libxml loader.
+     *
+     * @return bool $state
+     */
+    public static function getLibXmlDisableEntityLoader()
+    {
+        return self::$libXmlDisableEntityLoader;
+    }
+
+    /**
+     * Sets the implementation of cache that should be used for cell collection.
+     */
+    public static function setCache(CacheInterface $cache): void
+    {
+        self::$cache = $cache;
+    }
+
+    /**
+     * Gets the implementation of cache that should be used for cell collection.
+     *
+     * @return CacheInterface
+     */
+    public static function getCache()
+    {
+        if (!self::$cache) {
+            self::$cache = new Memory();
+        }
+
+        return self::$cache;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/CodePage.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/CodePage.php
new file mode 100755
index 0000000..97cbfbb
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/CodePage.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared;
+
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+
+class CodePage
+{
+    private static $pageArray = [
+        0 => 'CP1252', //    CodePage is not always correctly set when the xls file was saved by Apple's Numbers program
+        367 => 'ASCII', //    ASCII
+        437 => 'CP437', //    OEM US
+        //720 => 'notsupported', //    OEM Arabic
+        737 => 'CP737', //    OEM Greek
+        775 => 'CP775', //    OEM Baltic
+        850 => 'CP850', //    OEM Latin I
+        852 => 'CP852', //    OEM Latin II (Central European)
+        855 => 'CP855', //    OEM Cyrillic
+        857 => 'CP857', //    OEM Turkish
+        858 => 'CP858', //    OEM Multilingual Latin I with Euro
+        860 => 'CP860', //    OEM Portugese
+        861 => 'CP861', //    OEM Icelandic
+        862 => 'CP862', //    OEM Hebrew
+        863 => 'CP863', //    OEM Canadian (French)
+        864 => 'CP864', //    OEM Arabic
+        865 => 'CP865', //    OEM Nordic
+        866 => 'CP866', //    OEM Cyrillic (Russian)
+        869 => 'CP869', //    OEM Greek (Modern)
+        874 => 'CP874', //    ANSI Thai
+        932 => 'CP932', //    ANSI Japanese Shift-JIS
+        936 => 'CP936', //    ANSI Chinese Simplified GBK
+        949 => 'CP949', //    ANSI Korean (Wansung)
+        950 => 'CP950', //    ANSI Chinese Traditional BIG5
+        1200 => 'UTF-16LE', //    UTF-16 (BIFF8)
+        1250 => 'CP1250', //    ANSI Latin II (Central European)
+        1251 => 'CP1251', //    ANSI Cyrillic
+        1252 => 'CP1252', //    ANSI Latin I (BIFF4-BIFF7)
+        1253 => 'CP1253', //    ANSI Greek
+        1254 => 'CP1254', //    ANSI Turkish
+        1255 => 'CP1255', //    ANSI Hebrew
+        1256 => 'CP1256', //    ANSI Arabic
+        1257 => 'CP1257', //    ANSI Baltic
+        1258 => 'CP1258', //    ANSI Vietnamese
+        1361 => 'CP1361', //    ANSI Korean (Johab)
+        10000 => 'MAC', //    Apple Roman
+        10001 => 'CP932', //    Macintosh Japanese
+        10002 => 'CP950', //    Macintosh Chinese Traditional
+        10003 => 'CP1361', //    Macintosh Korean
+        10004 => 'MACARABIC', //    Apple Arabic
+        10005 => 'MACHEBREW', //    Apple Hebrew
+        10006 => 'MACGREEK', //    Macintosh Greek
+        10007 => 'MACCYRILLIC', //    Macintosh Cyrillic
+        10008 => 'CP936', //    Macintosh - Simplified Chinese (GB 2312)
+        10010 => 'MACROMANIA', //    Macintosh Romania
+        10017 => 'MACUKRAINE', //    Macintosh Ukraine
+        10021 => 'MACTHAI', //    Macintosh Thai
+        10029 => 'MACCENTRALEUROPE', //    Macintosh Central Europe
+        10079 => 'MACICELAND', //    Macintosh Icelandic
+        10081 => 'MACTURKISH', //    Macintosh Turkish
+        10082 => 'MACCROATIAN', //    Macintosh Croatian
+        21010 => 'UTF-16LE', //    UTF-16 (BIFF8) This isn't correct, but some Excel writer libraries erroneously use Codepage 21010 for UTF-16LE
+        32768 => 'MAC', //    Apple Roman
+        //32769 => 'unsupported', //    ANSI Latin I (BIFF2-BIFF3)
+        65000 => 'UTF-7', //    Unicode (UTF-7)
+        65001 => 'UTF-8', //    Unicode (UTF-8)
+    ];
+
+    /**
+     * Convert Microsoft Code Page Identifier to Code Page Name which iconv
+     * and mbstring understands.
+     *
+     * @param int $codePage Microsoft Code Page Indentifier
+     *
+     * @return string Code Page Name
+     */
+    public static function numberToName(int $codePage): string
+    {
+        if (array_key_exists($codePage, self::$pageArray)) {
+            return self::$pageArray[$codePage];
+        }
+        if ($codePage == 720 || $codePage == 32769) {
+            throw new PhpSpreadsheetException("Code page $codePage not supported."); //    OEM Arabic
+        }
+
+        throw new PhpSpreadsheetException('Unknown codepage: ' . $codePage);
+    }
+
+    public static function getEncodings(): array
+    {
+        return self::$pageArray;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Date.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Date.php
new file mode 100755
index 0000000..9dc9929
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Date.php
@@ -0,0 +1,483 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared;
+
+use DateTimeInterface;
+use DateTimeZone;
+use PhpOffice\PhpSpreadsheet\Calculation\DateTime;
+use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Cell\Cell;
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
+
+class Date
+{
+    /** constants */
+    const CALENDAR_WINDOWS_1900 = 1900; //    Base date of 1st Jan 1900 = 1.0
+    const CALENDAR_MAC_1904 = 1904; //    Base date of 2nd Jan 1904 = 1.0
+
+    /**
+     * Names of the months of the year, indexed by shortname
+     * Planned usage for locale settings.
+     *
+     * @var string[]
+     */
+    public static $monthNames = [
+        'Jan' => 'January',
+        'Feb' => 'February',
+        'Mar' => 'March',
+        'Apr' => 'April',
+        'May' => 'May',
+        'Jun' => 'June',
+        'Jul' => 'July',
+        'Aug' => 'August',
+        'Sep' => 'September',
+        'Oct' => 'October',
+        'Nov' => 'November',
+        'Dec' => 'December',
+    ];
+
+    /**
+     * @var string[]
+     */
+    public static $numberSuffixes = [
+        'st',
+        'nd',
+        'rd',
+        'th',
+    ];
+
+    /**
+     * Base calendar year to use for calculations
+     * Value is either CALENDAR_WINDOWS_1900 (1900) or CALENDAR_MAC_1904 (1904).
+     *
+     * @var int
+     */
+    protected static $excelCalendar = self::CALENDAR_WINDOWS_1900;
+
+    /**
+     * Default timezone to use for DateTime objects.
+     *
+     * @var null|DateTimeZone
+     */
+    protected static $defaultTimeZone;
+
+    /**
+     * Set the Excel calendar (Windows 1900 or Mac 1904).
+     *
+     * @param int $baseDate Excel base date (1900 or 1904)
+     *
+     * @return bool Success or failure
+     */
+    public static function setExcelCalendar($baseDate)
+    {
+        if (($baseDate == self::CALENDAR_WINDOWS_1900) ||
+            ($baseDate == self::CALENDAR_MAC_1904)) {
+            self::$excelCalendar = $baseDate;
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Return the Excel calendar (Windows 1900 or Mac 1904).
+     *
+     * @return int Excel base date (1900 or 1904)
+     */
+    public static function getExcelCalendar()
+    {
+        return self::$excelCalendar;
+    }
+
+    /**
+     * Set the Default timezone to use for dates.
+     *
+     * @param DateTimeZone|string $timeZone The timezone to set for all Excel datetimestamp to PHP DateTime Object conversions
+     *
+     * @return bool Success or failure
+     */
+    public static function setDefaultTimezone($timeZone)
+    {
+        try {
+            $timeZone = self::validateTimeZone($timeZone);
+            self::$defaultTimeZone = $timeZone;
+            $retval = true;
+        } catch (PhpSpreadsheetException $e) {
+            $retval = false;
+        }
+
+        return $retval;
+    }
+
+    /**
+     * Return the Default timezone being used for dates.
+     *
+     * @return DateTimeZone The timezone being used as default for Excel timestamp to PHP DateTime object
+     */
+    public static function getDefaultTimezone()
+    {
+        if (self::$defaultTimeZone === null) {
+            self::$defaultTimeZone = new DateTimeZone('UTC');
+        }
+
+        return self::$defaultTimeZone;
+    }
+
+    /**
+     * Validate a timezone.
+     *
+     * @param DateTimeZone|string $timeZone The timezone to validate, either as a timezone string or object
+     *
+     * @return DateTimeZone The timezone as a timezone object
+     */
+    private static function validateTimeZone($timeZone)
+    {
+        if ($timeZone instanceof DateTimeZone) {
+            return $timeZone;
+        }
+        if (in_array($timeZone, DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC))) {
+            return new DateTimeZone($timeZone);
+        }
+
+        throw new PhpSpreadsheetException('Invalid timezone');
+    }
+
+    /**
+     * Convert a MS serialized datetime value from Excel to a PHP Date/Time object.
+     *
+     * @param float|int $excelTimestamp MS Excel serialized date/time value
+     * @param null|DateTimeZone|string $timeZone The timezone to assume for the Excel timestamp,
+     *                                                                        if you don't want to treat it as a UTC value
+     *                                                                    Use the default (UST) unless you absolutely need a conversion
+     *
+     * @return \DateTime PHP date/time object
+     */
+    public static function excelToDateTimeObject($excelTimestamp, $timeZone = null)
+    {
+        $timeZone = ($timeZone === null) ? self::getDefaultTimezone() : self::validateTimeZone($timeZone);
+        if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_EXCEL) {
+            if ($excelTimestamp < 1.0) {
+                // Unix timestamp base date
+                $baseDate = new \DateTime('1970-01-01', $timeZone);
+            } else {
+                // MS Excel calendar base dates
+                if (self::$excelCalendar == self::CALENDAR_WINDOWS_1900) {
+                    // Allow adjustment for 1900 Leap Year in MS Excel
+                    $baseDate = ($excelTimestamp < 60) ? new \DateTime('1899-12-31', $timeZone) : new \DateTime('1899-12-30', $timeZone);
+                } else {
+                    $baseDate = new \DateTime('1904-01-01', $timeZone);
+                }
+            }
+        } else {
+            $baseDate = new \DateTime('1899-12-30', $timeZone);
+        }
+
+        $days = floor($excelTimestamp);
+        $partDay = $excelTimestamp - $days;
+        $hours = floor($partDay * 24);
+        $partDay = $partDay * 24 - $hours;
+        $minutes = floor($partDay * 60);
+        $partDay = $partDay * 60 - $minutes;
+        $seconds = round($partDay * 60);
+
+        if ($days >= 0) {
+            $days = '+' . $days;
+        }
+        $interval = $days . ' days';
+
+        return $baseDate->modify($interval)
+            ->setTime((int) $hours, (int) $minutes, (int) $seconds);
+    }
+
+    /**
+     * Convert a MS serialized datetime value from Excel to a unix timestamp.
+     *
+     * @param float|int $excelTimestamp MS Excel serialized date/time value
+     * @param null|DateTimeZone|string $timeZone The timezone to assume for the Excel timestamp,
+     *                                                                        if you don't want to treat it as a UTC value
+     *                                                                    Use the default (UST) unless you absolutely need a conversion
+     *
+     * @return int Unix timetamp for this date/time
+     */
+    public static function excelToTimestamp($excelTimestamp, $timeZone = null)
+    {
+        return (int) self::excelToDateTimeObject($excelTimestamp, $timeZone)
+            ->format('U');
+    }
+
+    /**
+     * Convert a date from PHP to an MS Excel serialized date/time value.
+     *
+     * @param mixed $dateValue Unix Timestamp or PHP DateTime object or a string
+     *
+     * @return bool|float Excel date/time value
+     *                                  or boolean FALSE on failure
+     */
+    public static function PHPToExcel($dateValue)
+    {
+        if ((is_object($dateValue)) && ($dateValue instanceof DateTimeInterface)) {
+            return self::dateTimeToExcel($dateValue);
+        } elseif (is_numeric($dateValue)) {
+            return self::timestampToExcel($dateValue);
+        } elseif (is_string($dateValue)) {
+            return self::stringToExcel($dateValue);
+        }
+
+        return false;
+    }
+
+    /**
+     * Convert a PHP DateTime object to an MS Excel serialized date/time value.
+     *
+     * @param DateTimeInterface $dateValue PHP DateTime object
+     *
+     * @return float MS Excel serialized date/time value
+     */
+    public static function dateTimeToExcel(DateTimeInterface $dateValue)
+    {
+        return self::formattedPHPToExcel(
+            (int) $dateValue->format('Y'),
+            (int) $dateValue->format('m'),
+            (int) $dateValue->format('d'),
+            (int) $dateValue->format('H'),
+            (int) $dateValue->format('i'),
+            (int) $dateValue->format('s')
+        );
+    }
+
+    /**
+     * Convert a Unix timestamp to an MS Excel serialized date/time value.
+     *
+     * @param int $dateValue Unix Timestamp
+     *
+     * @return float MS Excel serialized date/time value
+     */
+    public static function timestampToExcel($dateValue)
+    {
+        if (!is_numeric($dateValue)) {
+            return false;
+        }
+
+        return self::dateTimeToExcel(new \DateTime('@' . $dateValue));
+    }
+
+    /**
+     * formattedPHPToExcel.
+     *
+     * @param int $year
+     * @param int $month
+     * @param int $day
+     * @param int $hours
+     * @param int $minutes
+     * @param int $seconds
+     *
+     * @return float Excel date/time value
+     */
+    public static function formattedPHPToExcel($year, $month, $day, $hours = 0, $minutes = 0, $seconds = 0)
+    {
+        if (self::$excelCalendar == self::CALENDAR_WINDOWS_1900) {
+            //
+            //    Fudge factor for the erroneous fact that the year 1900 is treated as a Leap Year in MS Excel
+            //    This affects every date following 28th February 1900
+            //
+            $excel1900isLeapYear = true;
+            if (($year == 1900) && ($month <= 2)) {
+                $excel1900isLeapYear = false;
+            }
+            $myexcelBaseDate = 2415020;
+        } else {
+            $myexcelBaseDate = 2416481;
+            $excel1900isLeapYear = false;
+        }
+
+        //    Julian base date Adjustment
+        if ($month > 2) {
+            $month -= 3;
+        } else {
+            $month += 9;
+            --$year;
+        }
+
+        //    Calculate the Julian Date, then subtract the Excel base date (JD 2415020 = 31-Dec-1899 Giving Excel Date of 0)
+        $century = substr($year, 0, 2);
+        $decade = substr($year, 2, 2);
+        $excelDate = floor((146097 * $century) / 4) + floor((1461 * $decade) / 4) + floor((153 * $month + 2) / 5) + $day + 1721119 - $myexcelBaseDate + $excel1900isLeapYear;
+
+        $excelTime = (($hours * 3600) + ($minutes * 60) + $seconds) / 86400;
+
+        return (float) $excelDate + $excelTime;
+    }
+
+    /**
+     * Is a given cell a date/time?
+     *
+     * @return bool
+     */
+    public static function isDateTime(Cell $pCell)
+    {
+        return is_numeric($pCell->getCalculatedValue()) &&
+            self::isDateTimeFormat(
+                $pCell->getWorksheet()->getStyle(
+                    $pCell->getCoordinate()
+                )->getNumberFormat()
+            );
+    }
+
+    /**
+     * Is a given number format a date/time?
+     *
+     * @return bool
+     */
+    public static function isDateTimeFormat(NumberFormat $pFormat)
+    {
+        return self::isDateTimeFormatCode($pFormat->getFormatCode());
+    }
+
+    private static $possibleDateFormatCharacters = 'eymdHs';
+
+    /**
+     * Is a given number format code a date/time?
+     *
+     * @param string $pFormatCode
+     *
+     * @return bool
+     */
+    public static function isDateTimeFormatCode($pFormatCode)
+    {
+        if (strtolower($pFormatCode) === strtolower(NumberFormat::FORMAT_GENERAL)) {
+            //    "General" contains an epoch letter 'e', so we trap for it explicitly here (case-insensitive check)
+            return false;
+        }
+        if (preg_match('/[0#]E[+-]0/i', $pFormatCode)) {
+            //    Scientific format
+            return false;
+        }
+
+        // Switch on formatcode
+        switch ($pFormatCode) {
+            //    Explicitly defined date formats
+            case NumberFormat::FORMAT_DATE_YYYYMMDD:
+            case NumberFormat::FORMAT_DATE_YYYYMMDD2:
+            case NumberFormat::FORMAT_DATE_DDMMYYYY:
+            case NumberFormat::FORMAT_DATE_DMYSLASH:
+            case NumberFormat::FORMAT_DATE_DMYMINUS:
+            case NumberFormat::FORMAT_DATE_DMMINUS:
+            case NumberFormat::FORMAT_DATE_MYMINUS:
+            case NumberFormat::FORMAT_DATE_DATETIME:
+            case NumberFormat::FORMAT_DATE_TIME1:
+            case NumberFormat::FORMAT_DATE_TIME2:
+            case NumberFormat::FORMAT_DATE_TIME3:
+            case NumberFormat::FORMAT_DATE_TIME4:
+            case NumberFormat::FORMAT_DATE_TIME5:
+            case NumberFormat::FORMAT_DATE_TIME6:
+            case NumberFormat::FORMAT_DATE_TIME7:
+            case NumberFormat::FORMAT_DATE_TIME8:
+            case NumberFormat::FORMAT_DATE_YYYYMMDDSLASH:
+            case NumberFormat::FORMAT_DATE_XLSX14:
+            case NumberFormat::FORMAT_DATE_XLSX15:
+            case NumberFormat::FORMAT_DATE_XLSX16:
+            case NumberFormat::FORMAT_DATE_XLSX17:
+            case NumberFormat::FORMAT_DATE_XLSX22:
+                return true;
+        }
+
+        //    Typically number, currency or accounting (or occasionally fraction) formats
+        if ((substr($pFormatCode, 0, 1) == '_') || (substr($pFormatCode, 0, 2) == '0 ')) {
+            return false;
+        }
+        // Try checking for any of the date formatting characters that don't appear within square braces
+        if (preg_match('/(^|\])[^\[]*[' . self::$possibleDateFormatCharacters . ']/i', $pFormatCode)) {
+            //    We might also have a format mask containing quoted strings...
+            //        we don't want to test for any of our characters within the quoted blocks
+            if (strpos($pFormatCode, '"') !== false) {
+                $segMatcher = false;
+                foreach (explode('"', $pFormatCode) as $subVal) {
+                    //    Only test in alternate array entries (the non-quoted blocks)
+                    if (($segMatcher = !$segMatcher) &&
+                        (preg_match('/(^|\])[^\[]*[' . self::$possibleDateFormatCharacters . ']/i', $subVal))) {
+                        return true;
+                    }
+                }
+
+                return false;
+            }
+
+            return true;
+        }
+
+        // No date...
+        return false;
+    }
+
+    /**
+     * Convert a date/time string to Excel time.
+     *
+     * @param string $dateValue Examples: '2009-12-31', '2009-12-31 15:59', '2009-12-31 15:59:10'
+     *
+     * @return false|float Excel date/time serial value
+     */
+    public static function stringToExcel($dateValue)
+    {
+        if (strlen($dateValue) < 2) {
+            return false;
+        }
+        if (!preg_match('/^(\d{1,4}[ \.\/\-][A-Z]{3,9}([ \.\/\-]\d{1,4})?|[A-Z]{3,9}[ \.\/\-]\d{1,4}([ \.\/\-]\d{1,4})?|\d{1,4}[ \.\/\-]\d{1,4}([ \.\/\-]\d{1,4})?)( \d{1,2}:\d{1,2}(:\d{1,2})?)?$/iu', $dateValue)) {
+            return false;
+        }
+
+        $dateValueNew = DateTime::DATEVALUE($dateValue);
+
+        if ($dateValueNew === Functions::VALUE()) {
+            return false;
+        }
+
+        if (strpos($dateValue, ':') !== false) {
+            $timeValue = DateTime::TIMEVALUE($dateValue);
+            if ($timeValue === Functions::VALUE()) {
+                return false;
+            }
+            $dateValueNew += $timeValue;
+        }
+
+        return $dateValueNew;
+    }
+
+    /**
+     * Converts a month name (either a long or a short name) to a month number.
+     *
+     * @param string $month Month name or abbreviation
+     *
+     * @return int|string Month number (1 - 12), or the original string argument if it isn't a valid month name
+     */
+    public static function monthStringToNumber($month)
+    {
+        $monthIndex = 1;
+        foreach (self::$monthNames as $shortMonthName => $longMonthName) {
+            if (($month === $longMonthName) || ($month === $shortMonthName)) {
+                return $monthIndex;
+            }
+            ++$monthIndex;
+        }
+
+        return $month;
+    }
+
+    /**
+     * Strips an ordinal from a numeric value.
+     *
+     * @param string $day Day number with an ordinal
+     *
+     * @return int|string The integer value with any ordinal stripped, or the original string argument if it isn't a valid numeric
+     */
+    public static function dayStringToNumber($day)
+    {
+        $strippedDayValue = (str_replace(self::$numberSuffixes, '', $day));
+        if (is_numeric($strippedDayValue)) {
+            return (int) $strippedDayValue;
+        }
+
+        return $day;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Drawing.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Drawing.php
new file mode 100755
index 0000000..25d6910
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Drawing.php
@@ -0,0 +1,249 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared;
+
+class Drawing
+{
+    /**
+     * Convert pixels to EMU.
+     *
+     * @param int $pValue Value in pixels
+     *
+     * @return int Value in EMU
+     */
+    public static function pixelsToEMU($pValue)
+    {
+        return round($pValue * 9525);
+    }
+
+    /**
+     * Convert EMU to pixels.
+     *
+     * @param int $pValue Value in EMU
+     *
+     * @return int Value in pixels
+     */
+    public static function EMUToPixels($pValue)
+    {
+        if ($pValue != 0) {
+            return round($pValue / 9525);
+        }
+
+        return 0;
+    }
+
+    /**
+     * Convert pixels to column width. Exact algorithm not known.
+     * By inspection of a real Excel file using Calibri 11, one finds 1000px ~ 142.85546875
+     * This gives a conversion factor of 7. Also, we assume that pixels and font size are proportional.
+     *
+     * @param int $pValue Value in pixels
+     * @param \PhpOffice\PhpSpreadsheet\Style\Font $pDefaultFont Default font of the workbook
+     *
+     * @return int Value in cell dimension
+     */
+    public static function pixelsToCellDimension($pValue, \PhpOffice\PhpSpreadsheet\Style\Font $pDefaultFont)
+    {
+        // Font name and size
+        $name = $pDefaultFont->getName();
+        $size = $pDefaultFont->getSize();
+
+        if (isset(Font::$defaultColumnWidths[$name][$size])) {
+            // Exact width can be determined
+            $colWidth = $pValue * Font::$defaultColumnWidths[$name][$size]['width'] / Font::$defaultColumnWidths[$name][$size]['px'];
+        } else {
+            // We don't have data for this particular font and size, use approximation by
+            // extrapolating from Calibri 11
+            $colWidth = $pValue * 11 * Font::$defaultColumnWidths['Calibri'][11]['width'] / Font::$defaultColumnWidths['Calibri'][11]['px'] / $size;
+        }
+
+        return $colWidth;
+    }
+
+    /**
+     * Convert column width from (intrinsic) Excel units to pixels.
+     *
+     * @param float $pValue Value in cell dimension
+     * @param \PhpOffice\PhpSpreadsheet\Style\Font $pDefaultFont Default font of the workbook
+     *
+     * @return int Value in pixels
+     */
+    public static function cellDimensionToPixels($pValue, \PhpOffice\PhpSpreadsheet\Style\Font $pDefaultFont)
+    {
+        // Font name and size
+        $name = $pDefaultFont->getName();
+        $size = $pDefaultFont->getSize();
+
+        if (isset(Font::$defaultColumnWidths[$name][$size])) {
+            // Exact width can be determined
+            $colWidth = $pValue * Font::$defaultColumnWidths[$name][$size]['px'] / Font::$defaultColumnWidths[$name][$size]['width'];
+        } else {
+            // We don't have data for this particular font and size, use approximation by
+            // extrapolating from Calibri 11
+            $colWidth = $pValue * $size * Font::$defaultColumnWidths['Calibri'][11]['px'] / Font::$defaultColumnWidths['Calibri'][11]['width'] / 11;
+        }
+
+        // Round pixels to closest integer
+        $colWidth = (int) round($colWidth);
+
+        return $colWidth;
+    }
+
+    /**
+     * Convert pixels to points.
+     *
+     * @param int $pValue Value in pixels
+     *
+     * @return float Value in points
+     */
+    public static function pixelsToPoints($pValue)
+    {
+        return $pValue * 0.67777777;
+    }
+
+    /**
+     * Convert points to pixels.
+     *
+     * @param int $pValue Value in points
+     *
+     * @return int Value in pixels
+     */
+    public static function pointsToPixels($pValue)
+    {
+        if ($pValue != 0) {
+            return (int) ceil($pValue * 1.333333333);
+        }
+
+        return 0;
+    }
+
+    /**
+     * Convert degrees to angle.
+     *
+     * @param int $pValue Degrees
+     *
+     * @return int Angle
+     */
+    public static function degreesToAngle($pValue)
+    {
+        return (int) round($pValue * 60000);
+    }
+
+    /**
+     * Convert angle to degrees.
+     *
+     * @param int $pValue Angle
+     *
+     * @return int Degrees
+     */
+    public static function angleToDegrees($pValue)
+    {
+        if ($pValue != 0) {
+            return round($pValue / 60000);
+        }
+
+        return 0;
+    }
+
+    /**
+     * Create a new image from file. By alexander at alexauto dot nl.
+     *
+     * @see http://www.php.net/manual/en/function.imagecreatefromwbmp.php#86214
+     *
+     * @param string $p_sFile Path to Windows DIB (BMP) image
+     *
+     * @return resource
+     */
+    public static function imagecreatefrombmp($p_sFile)
+    {
+        //    Load the image into a string
+        $file = fopen($p_sFile, 'rb');
+        $read = fread($file, 10);
+        while (!feof($file) && ($read != '')) {
+            $read .= fread($file, 1024);
+        }
+
+        $temp = unpack('H*', $read);
+        $hex = $temp[1];
+        $header = substr($hex, 0, 108);
+
+        //    Process the header
+        //    Structure: http://www.fastgraph.com/help/bmp_header_format.html
+        if (substr($header, 0, 4) == '424d') {
+            //    Cut it in parts of 2 bytes
+            $header_parts = str_split($header, 2);
+
+            //    Get the width        4 bytes
+            $width = hexdec($header_parts[19] . $header_parts[18]);
+
+            //    Get the height        4 bytes
+            $height = hexdec($header_parts[23] . $header_parts[22]);
+
+            //    Unset the header params
+            unset($header_parts);
+        }
+
+        //    Define starting X and Y
+        $x = 0;
+        $y = 1;
+
+        //    Create newimage
+        $image = imagecreatetruecolor($width, $height);
+
+        //    Grab the body from the image
+        $body = substr($hex, 108);
+
+        //    Calculate if padding at the end-line is needed
+        //    Divided by two to keep overview.
+        //    1 byte = 2 HEX-chars
+        $body_size = (strlen($body) / 2);
+        $header_size = ($width * $height);
+
+        //    Use end-line padding? Only when needed
+        $usePadding = ($body_size > ($header_size * 3) + 4);
+
+        //    Using a for-loop with index-calculation instaid of str_split to avoid large memory consumption
+        //    Calculate the next DWORD-position in the body
+        for ($i = 0; $i < $body_size; $i += 3) {
+            //    Calculate line-ending and padding
+            if ($x >= $width) {
+                // If padding needed, ignore image-padding
+                // Shift i to the ending of the current 32-bit-block
+                if ($usePadding) {
+                    $i += $width % 4;
+                }
+
+                //    Reset horizontal position
+                $x = 0;
+
+                //    Raise the height-position (bottom-up)
+                ++$y;
+
+                //    Reached the image-height? Break the for-loop
+                if ($y > $height) {
+                    break;
+                }
+            }
+
+            // Calculation of the RGB-pixel (defined as BGR in image-data)
+            // Define $i_pos as absolute position in the body
+            $i_pos = $i * 2;
+            $r = hexdec($body[$i_pos + 4] . $body[$i_pos + 5]);
+            $g = hexdec($body[$i_pos + 2] . $body[$i_pos + 3]);
+            $b = hexdec($body[$i_pos] . $body[$i_pos + 1]);
+
+            // Calculate and draw the pixel
+            $color = imagecolorallocate($image, $r, $g, $b);
+            imagesetpixel($image, $x, $height - $y, $color);
+
+            // Raise the horizontal position
+            ++$x;
+        }
+
+        // Unset the body / free the memory
+        unset($body);
+
+        //    Return image-object
+        return $image;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher.php
new file mode 100755
index 0000000..c6d0a6f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared;
+
+class Escher
+{
+    /**
+     * Drawing Group Container.
+     *
+     * @var Escher\DggContainer
+     */
+    private $dggContainer;
+
+    /**
+     * Drawing Container.
+     *
+     * @var Escher\DgContainer
+     */
+    private $dgContainer;
+
+    /**
+     * Get Drawing Group Container.
+     *
+     * @return Escher\DggContainer
+     */
+    public function getDggContainer()
+    {
+        return $this->dggContainer;
+    }
+
+    /**
+     * Set Drawing Group Container.
+     *
+     * @param Escher\DggContainer $dggContainer
+     *
+     * @return Escher\DggContainer
+     */
+    public function setDggContainer($dggContainer)
+    {
+        return $this->dggContainer = $dggContainer;
+    }
+
+    /**
+     * Get Drawing Container.
+     *
+     * @return Escher\DgContainer
+     */
+    public function getDgContainer()
+    {
+        return $this->dgContainer;
+    }
+
+    /**
+     * Set Drawing Container.
+     *
+     * @param Escher\DgContainer $dgContainer
+     *
+     * @return Escher\DgContainer
+     */
+    public function setDgContainer($dgContainer)
+    {
+        return $this->dgContainer = $dgContainer;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer.php
new file mode 100755
index 0000000..b0d75d7
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared\Escher;
+
+class DgContainer
+{
+    /**
+     * Drawing index, 1-based.
+     *
+     * @var int
+     */
+    private $dgId;
+
+    /**
+     * Last shape index in this drawing.
+     *
+     * @var int
+     */
+    private $lastSpId;
+
+    private $spgrContainer;
+
+    public function getDgId()
+    {
+        return $this->dgId;
+    }
+
+    public function setDgId($value): void
+    {
+        $this->dgId = $value;
+    }
+
+    public function getLastSpId()
+    {
+        return $this->lastSpId;
+    }
+
+    public function setLastSpId($value): void
+    {
+        $this->lastSpId = $value;
+    }
+
+    public function getSpgrContainer()
+    {
+        return $this->spgrContainer;
+    }
+
+    public function setSpgrContainer($spgrContainer)
+    {
+        return $this->spgrContainer = $spgrContainer;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer.php
new file mode 100755
index 0000000..1da8772
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer;
+
+class SpgrContainer
+{
+    /**
+     * Parent Shape Group Container.
+     *
+     * @var \PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer
+     */
+    private $parent;
+
+    /**
+     * Shape Container collection.
+     *
+     * @var array
+     */
+    private $children = [];
+
+    /**
+     * Set parent Shape Group Container.
+     *
+     * @param \PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer $parent
+     */
+    public function setParent($parent): void
+    {
+        $this->parent = $parent;
+    }
+
+    /**
+     * Get the parent Shape Group Container if any.
+     *
+     * @return null|\PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer
+     */
+    public function getParent()
+    {
+        return $this->parent;
+    }
+
+    /**
+     * Add a child. This will be either spgrContainer or spContainer.
+     *
+     * @param mixed $child
+     */
+    public function addChild($child): void
+    {
+        $this->children[] = $child;
+        $child->setParent($this);
+    }
+
+    /**
+     * Get collection of Shape Containers.
+     */
+    public function getChildren()
+    {
+        return $this->children;
+    }
+
+    /**
+     * Recursively get all spContainers within this spgrContainer.
+     *
+     * @return SpgrContainer\SpContainer[]
+     */
+    public function getAllSpContainers()
+    {
+        $allSpContainers = [];
+
+        foreach ($this->children as $child) {
+            if ($child instanceof self) {
+                $allSpContainers = array_merge($allSpContainers, $child->getAllSpContainers());
+            } else {
+                $allSpContainers[] = $child;
+            }
+        }
+
+        return $allSpContainers;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php
new file mode 100755
index 0000000..8a81ff5
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php
@@ -0,0 +1,369 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer;
+
+use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer;
+
+class SpContainer
+{
+    /**
+     * Parent Shape Group Container.
+     *
+     * @var SpgrContainer
+     */
+    private $parent;
+
+    /**
+     * Is this a group shape?
+     *
+     * @var bool
+     */
+    private $spgr = false;
+
+    /**
+     * Shape type.
+     *
+     * @var int
+     */
+    private $spType;
+
+    /**
+     * Shape flag.
+     *
+     * @var int
+     */
+    private $spFlag;
+
+    /**
+     * Shape index (usually group shape has index 0, and the rest: 1,2,3...).
+     *
+     * @var int
+     */
+    private $spId;
+
+    /**
+     * Array of options.
+     *
+     * @var array
+     */
+    private $OPT;
+
+    /**
+     * Cell coordinates of upper-left corner of shape, e.g. 'A1'.
+     *
+     * @var string
+     */
+    private $startCoordinates;
+
+    /**
+     * Horizontal offset of upper-left corner of shape measured in 1/1024 of column width.
+     *
+     * @var int
+     */
+    private $startOffsetX;
+
+    /**
+     * Vertical offset of upper-left corner of shape measured in 1/256 of row height.
+     *
+     * @var int
+     */
+    private $startOffsetY;
+
+    /**
+     * Cell coordinates of bottom-right corner of shape, e.g. 'B2'.
+     *
+     * @var string
+     */
+    private $endCoordinates;
+
+    /**
+     * Horizontal offset of bottom-right corner of shape measured in 1/1024 of column width.
+     *
+     * @var int
+     */
+    private $endOffsetX;
+
+    /**
+     * Vertical offset of bottom-right corner of shape measured in 1/256 of row height.
+     *
+     * @var int
+     */
+    private $endOffsetY;
+
+    /**
+     * Set parent Shape Group Container.
+     *
+     * @param SpgrContainer $parent
+     */
+    public function setParent($parent): void
+    {
+        $this->parent = $parent;
+    }
+
+    /**
+     * Get the parent Shape Group Container.
+     *
+     * @return SpgrContainer
+     */
+    public function getParent()
+    {
+        return $this->parent;
+    }
+
+    /**
+     * Set whether this is a group shape.
+     *
+     * @param bool $value
+     */
+    public function setSpgr($value): void
+    {
+        $this->spgr = $value;
+    }
+
+    /**
+     * Get whether this is a group shape.
+     *
+     * @return bool
+     */
+    public function getSpgr()
+    {
+        return $this->spgr;
+    }
+
+    /**
+     * Set the shape type.
+     *
+     * @param int $value
+     */
+    public function setSpType($value): void
+    {
+        $this->spType = $value;
+    }
+
+    /**
+     * Get the shape type.
+     *
+     * @return int
+     */
+    public function getSpType()
+    {
+        return $this->spType;
+    }
+
+    /**
+     * Set the shape flag.
+     *
+     * @param int $value
+     */
+    public function setSpFlag($value): void
+    {
+        $this->spFlag = $value;
+    }
+
+    /**
+     * Get the shape flag.
+     *
+     * @return int
+     */
+    public function getSpFlag()
+    {
+        return $this->spFlag;
+    }
+
+    /**
+     * Set the shape index.
+     *
+     * @param int $value
+     */
+    public function setSpId($value): void
+    {
+        $this->spId = $value;
+    }
+
+    /**
+     * Get the shape index.
+     *
+     * @return int
+     */
+    public function getSpId()
+    {
+        return $this->spId;
+    }
+
+    /**
+     * Set an option for the Shape Group Container.
+     *
+     * @param int $property The number specifies the option
+     * @param mixed $value
+     */
+    public function setOPT($property, $value): void
+    {
+        $this->OPT[$property] = $value;
+    }
+
+    /**
+     * Get an option for the Shape Group Container.
+     *
+     * @param int $property The number specifies the option
+     *
+     * @return mixed
+     */
+    public function getOPT($property)
+    {
+        if (isset($this->OPT[$property])) {
+            return $this->OPT[$property];
+        }
+
+        return null;
+    }
+
+    /**
+     * Get the collection of options.
+     *
+     * @return array
+     */
+    public function getOPTCollection()
+    {
+        return $this->OPT;
+    }
+
+    /**
+     * Set cell coordinates of upper-left corner of shape.
+     *
+     * @param string $value eg: 'A1'
+     */
+    public function setStartCoordinates($value): void
+    {
+        $this->startCoordinates = $value;
+    }
+
+    /**
+     * Get cell coordinates of upper-left corner of shape.
+     *
+     * @return string
+     */
+    public function getStartCoordinates()
+    {
+        return $this->startCoordinates;
+    }
+
+    /**
+     * Set offset in x-direction of upper-left corner of shape measured in 1/1024 of column width.
+     *
+     * @param int $startOffsetX
+     */
+    public function setStartOffsetX($startOffsetX): void
+    {
+        $this->startOffsetX = $startOffsetX;
+    }
+
+    /**
+     * Get offset in x-direction of upper-left corner of shape measured in 1/1024 of column width.
+     *
+     * @return int
+     */
+    public function getStartOffsetX()
+    {
+        return $this->startOffsetX;
+    }
+
+    /**
+     * Set offset in y-direction of upper-left corner of shape measured in 1/256 of row height.
+     *
+     * @param int $startOffsetY
+     */
+    public function setStartOffsetY($startOffsetY): void
+    {
+        $this->startOffsetY = $startOffsetY;
+    }
+
+    /**
+     * Get offset in y-direction of upper-left corner of shape measured in 1/256 of row height.
+     *
+     * @return int
+     */
+    public function getStartOffsetY()
+    {
+        return $this->startOffsetY;
+    }
+
+    /**
+     * Set cell coordinates of bottom-right corner of shape.
+     *
+     * @param string $value eg: 'A1'
+     */
+    public function setEndCoordinates($value): void
+    {
+        $this->endCoordinates = $value;
+    }
+
+    /**
+     * Get cell coordinates of bottom-right corner of shape.
+     *
+     * @return string
+     */
+    public function getEndCoordinates()
+    {
+        return $this->endCoordinates;
+    }
+
+    /**
+     * Set offset in x-direction of bottom-right corner of shape measured in 1/1024 of column width.
+     *
+     * @param int $endOffsetX
+     */
+    public function setEndOffsetX($endOffsetX): void
+    {
+        $this->endOffsetX = $endOffsetX;
+    }
+
+    /**
+     * Get offset in x-direction of bottom-right corner of shape measured in 1/1024 of column width.
+     *
+     * @return int
+     */
+    public function getEndOffsetX()
+    {
+        return $this->endOffsetX;
+    }
+
+    /**
+     * Set offset in y-direction of bottom-right corner of shape measured in 1/256 of row height.
+     *
+     * @param int $endOffsetY
+     */
+    public function setEndOffsetY($endOffsetY): void
+    {
+        $this->endOffsetY = $endOffsetY;
+    }
+
+    /**
+     * Get offset in y-direction of bottom-right corner of shape measured in 1/256 of row height.
+     *
+     * @return int
+     */
+    public function getEndOffsetY()
+    {
+        return $this->endOffsetY;
+    }
+
+    /**
+     * Get the nesting level of this spContainer. This is the number of spgrContainers between this spContainer and
+     * the dgContainer. A value of 1 = immediately within first spgrContainer
+     * Higher nesting level occurs if and only if spContainer is part of a shape group.
+     *
+     * @return int Nesting level
+     */
+    public function getNestingLevel()
+    {
+        $nestingLevel = 0;
+
+        $parent = $this->getParent();
+        while ($parent instanceof SpgrContainer) {
+            ++$nestingLevel;
+            $parent = $parent->getParent();
+        }
+
+        return $nestingLevel;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer.php
new file mode 100755
index 0000000..1bd15b9
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer.php
@@ -0,0 +1,175 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared\Escher;
+
+class DggContainer
+{
+    /**
+     * Maximum shape index of all shapes in all drawings increased by one.
+     *
+     * @var int
+     */
+    private $spIdMax;
+
+    /**
+     * Total number of drawings saved.
+     *
+     * @var int
+     */
+    private $cDgSaved;
+
+    /**
+     * Total number of shapes saved (including group shapes).
+     *
+     * @var int
+     */
+    private $cSpSaved;
+
+    /**
+     * BLIP Store Container.
+     *
+     * @var DggContainer\BstoreContainer
+     */
+    private $bstoreContainer;
+
+    /**
+     * Array of options for the drawing group.
+     *
+     * @var array
+     */
+    private $OPT = [];
+
+    /**
+     * Array of identifier clusters containg information about the maximum shape identifiers.
+     *
+     * @var array
+     */
+    private $IDCLs = [];
+
+    /**
+     * Get maximum shape index of all shapes in all drawings (plus one).
+     *
+     * @return int
+     */
+    public function getSpIdMax()
+    {
+        return $this->spIdMax;
+    }
+
+    /**
+     * Set maximum shape index of all shapes in all drawings (plus one).
+     *
+     * @param int $value
+     */
+    public function setSpIdMax($value): void
+    {
+        $this->spIdMax = $value;
+    }
+
+    /**
+     * Get total number of drawings saved.
+     *
+     * @return int
+     */
+    public function getCDgSaved()
+    {
+        return $this->cDgSaved;
+    }
+
+    /**
+     * Set total number of drawings saved.
+     *
+     * @param int $value
+     */
+    public function setCDgSaved($value): void
+    {
+        $this->cDgSaved = $value;
+    }
+
+    /**
+     * Get total number of shapes saved (including group shapes).
+     *
+     * @return int
+     */
+    public function getCSpSaved()
+    {
+        return $this->cSpSaved;
+    }
+
+    /**
+     * Set total number of shapes saved (including group shapes).
+     *
+     * @param int $value
+     */
+    public function setCSpSaved($value): void
+    {
+        $this->cSpSaved = $value;
+    }
+
+    /**
+     * Get BLIP Store Container.
+     *
+     * @return DggContainer\BstoreContainer
+     */
+    public function getBstoreContainer()
+    {
+        return $this->bstoreContainer;
+    }
+
+    /**
+     * Set BLIP Store Container.
+     *
+     * @param DggContainer\BstoreContainer $bstoreContainer
+     */
+    public function setBstoreContainer($bstoreContainer): void
+    {
+        $this->bstoreContainer = $bstoreContainer;
+    }
+
+    /**
+     * Set an option for the drawing group.
+     *
+     * @param int $property The number specifies the option
+     * @param mixed $value
+     */
+    public function setOPT($property, $value): void
+    {
+        $this->OPT[$property] = $value;
+    }
+
+    /**
+     * Get an option for the drawing group.
+     *
+     * @param int $property The number specifies the option
+     *
+     * @return mixed
+     */
+    public function getOPT($property)
+    {
+        if (isset($this->OPT[$property])) {
+            return $this->OPT[$property];
+        }
+
+        return null;
+    }
+
+    /**
+     * Get identifier clusters.
+     *
+     * @return array
+     */
+    public function getIDCLs()
+    {
+        return $this->IDCLs;
+    }
+
+    /**
+     * Set identifier clusters. [<drawingId> => <max shape id>, ...].
+     *
+     * @param array $pValue
+     */
+    public function setIDCLs($pValue): void
+    {
+        $this->IDCLs = $pValue;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer.php
new file mode 100755
index 0000000..b07786f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer;
+
+class BstoreContainer
+{
+    /**
+     * BLIP Store Entries. Each of them holds one BLIP (Big Large Image or Picture).
+     *
+     * @var array
+     */
+    private $BSECollection = [];
+
+    /**
+     * Add a BLIP Store Entry.
+     *
+     * @param BstoreContainer\BSE $BSE
+     */
+    public function addBSE($BSE): void
+    {
+        $this->BSECollection[] = $BSE;
+        $BSE->setParent($this);
+    }
+
+    /**
+     * Get the collection of BLIP Store Entries.
+     *
+     * @return BstoreContainer\BSE[]
+     */
+    public function getBSECollection()
+    {
+        return $this->BSECollection;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE.php
new file mode 100755
index 0000000..e885146
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer;
+
+class BSE
+{
+    const BLIPTYPE_ERROR = 0x00;
+    const BLIPTYPE_UNKNOWN = 0x01;
+    const BLIPTYPE_EMF = 0x02;
+    const BLIPTYPE_WMF = 0x03;
+    const BLIPTYPE_PICT = 0x04;
+    const BLIPTYPE_JPEG = 0x05;
+    const BLIPTYPE_PNG = 0x06;
+    const BLIPTYPE_DIB = 0x07;
+    const BLIPTYPE_TIFF = 0x11;
+    const BLIPTYPE_CMYKJPEG = 0x12;
+
+    /**
+     * The parent BLIP Store Entry Container.
+     *
+     * @var \PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer
+     */
+    private $parent;
+
+    /**
+     * The BLIP (Big Large Image or Picture).
+     *
+     * @var BSE\Blip
+     */
+    private $blip;
+
+    /**
+     * The BLIP type.
+     *
+     * @var int
+     */
+    private $blipType;
+
+    /**
+     * Set parent BLIP Store Entry Container.
+     *
+     * @param \PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer $parent
+     */
+    public function setParent($parent): void
+    {
+        $this->parent = $parent;
+    }
+
+    /**
+     * Get the BLIP.
+     *
+     * @return BSE\Blip
+     */
+    public function getBlip()
+    {
+        return $this->blip;
+    }
+
+    /**
+     * Set the BLIP.
+     *
+     * @param BSE\Blip $blip
+     */
+    public function setBlip($blip): void
+    {
+        $this->blip = $blip;
+        $blip->setParent($this);
+    }
+
+    /**
+     * Get the BLIP type.
+     *
+     * @return int
+     */
+    public function getBlipType()
+    {
+        return $this->blipType;
+    }
+
+    /**
+     * Set the BLIP type.
+     *
+     * @param int $blipType
+     */
+    public function setBlipType($blipType): void
+    {
+        $this->blipType = $blipType;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE/Blip.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE/Blip.php
new file mode 100755
index 0000000..500d7ea
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE/Blip.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE;
+
+class Blip
+{
+    /**
+     * The parent BSE.
+     *
+     * @var \PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE
+     */
+    private $parent;
+
+    /**
+     * Raw image data.
+     *
+     * @var string
+     */
+    private $data;
+
+    /**
+     * Get the raw image data.
+     *
+     * @return string
+     */
+    public function getData()
+    {
+        return $this->data;
+    }
+
+    /**
+     * Set the raw image data.
+     *
+     * @param string $data
+     */
+    public function setData($data): void
+    {
+        $this->data = $data;
+    }
+
+    /**
+     * Set parent BSE.
+     *
+     * @param \PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE $parent
+     */
+    public function setParent($parent): void
+    {
+        $this->parent = $parent;
+    }
+
+    /**
+     * Get parent BSE.
+     *
+     * @return \PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE $parent
+     */
+    public function getParent()
+    {
+        return $this->parent;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/File.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/File.php
new file mode 100755
index 0000000..7525df8
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/File.php
@@ -0,0 +1,142 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared;
+
+use InvalidArgumentException;
+use ZipArchive;
+
+class File
+{
+    /**
+     * Use Temp or File Upload Temp for temporary files.
+     *
+     * @var bool
+     */
+    protected static $useUploadTempDirectory = false;
+
+    /**
+     * Set the flag indicating whether the File Upload Temp directory should be used for temporary files.
+     *
+     * @param bool $useUploadTempDir Use File Upload Temporary directory (true or false)
+     */
+    public static function setUseUploadTempDirectory($useUploadTempDir): void
+    {
+        self::$useUploadTempDirectory = (bool) $useUploadTempDir;
+    }
+
+    /**
+     * Get the flag indicating whether the File Upload Temp directory should be used for temporary files.
+     *
+     * @return bool Use File Upload Temporary directory (true or false)
+     */
+    public static function getUseUploadTempDirectory()
+    {
+        return self::$useUploadTempDirectory;
+    }
+
+    /**
+     * Verify if a file exists.
+     *
+     * @param string $pFilename Filename
+     *
+     * @return bool
+     */
+    public static function fileExists($pFilename)
+    {
+        // Sick construction, but it seems that
+        // file_exists returns strange values when
+        // doing the original file_exists on ZIP archives...
+        if (strtolower(substr($pFilename, 0, 3)) == 'zip') {
+            // Open ZIP file and verify if the file exists
+            $zipFile = substr($pFilename, 6, strpos($pFilename, '#') - 6);
+            $archiveFile = substr($pFilename, strpos($pFilename, '#') + 1);
+
+            $zip = new ZipArchive();
+            if ($zip->open($zipFile) === true) {
+                $returnValue = ($zip->getFromName($archiveFile) !== false);
+                $zip->close();
+
+                return $returnValue;
+            }
+
+            return false;
+        }
+
+        return file_exists($pFilename);
+    }
+
+    /**
+     * Returns canonicalized absolute pathname, also for ZIP archives.
+     *
+     * @param string $pFilename
+     *
+     * @return string
+     */
+    public static function realpath($pFilename)
+    {
+        // Returnvalue
+        $returnValue = '';
+
+        // Try using realpath()
+        if (file_exists($pFilename)) {
+            $returnValue = realpath($pFilename);
+        }
+
+        // Found something?
+        if ($returnValue == '' || ($returnValue === null)) {
+            $pathArray = explode('/', $pFilename);
+            while (in_array('..', $pathArray) && $pathArray[0] != '..') {
+                $iMax = count($pathArray);
+                for ($i = 0; $i < $iMax; ++$i) {
+                    if ($pathArray[$i] == '..' && $i > 0) {
+                        unset($pathArray[$i], $pathArray[$i - 1]);
+
+                        break;
+                    }
+                }
+            }
+            $returnValue = implode('/', $pathArray);
+        }
+
+        // Return
+        return $returnValue;
+    }
+
+    /**
+     * Get the systems temporary directory.
+     *
+     * @return string
+     */
+    public static function sysGetTempDir()
+    {
+        if (self::$useUploadTempDirectory) {
+            //  use upload-directory when defined to allow running on environments having very restricted
+            //      open_basedir configs
+            if (ini_get('upload_tmp_dir') !== false) {
+                if ($temp = ini_get('upload_tmp_dir')) {
+                    if (file_exists($temp)) {
+                        return realpath($temp);
+                    }
+                }
+            }
+        }
+
+        return realpath(sys_get_temp_dir());
+    }
+
+    /**
+     * Assert that given path is an existing file and is readable, otherwise throw exception.
+     *
+     * @param string $filename
+     */
+    public static function assertFile($filename): void
+    {
+        if (!is_file($filename)) {
+            throw new InvalidArgumentException('File "' . $filename . '" does not exist.');
+        }
+
+        if (!is_readable($filename)) {
+            throw new InvalidArgumentException('Could not open "' . $filename . '" for reading.');
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Font.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Font.php
new file mode 100755
index 0000000..ca94529
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Font.php
@@ -0,0 +1,759 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared;
+
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+use PhpOffice\PhpSpreadsheet\RichText\RichText;
+
+class Font
+{
+    // Methods for resolving autosize value
+    const AUTOSIZE_METHOD_APPROX = 'approx';
+    const AUTOSIZE_METHOD_EXACT = 'exact';
+
+    private static $autoSizeMethods = [
+        self::AUTOSIZE_METHOD_APPROX,
+        self::AUTOSIZE_METHOD_EXACT,
+    ];
+
+    /** Character set codes used by BIFF5-8 in Font records */
+    const CHARSET_ANSI_LATIN = 0x00;
+    const CHARSET_SYSTEM_DEFAULT = 0x01;
+    const CHARSET_SYMBOL = 0x02;
+    const CHARSET_APPLE_ROMAN = 0x4D;
+    const CHARSET_ANSI_JAPANESE_SHIFTJIS = 0x80;
+    const CHARSET_ANSI_KOREAN_HANGUL = 0x81;
+    const CHARSET_ANSI_KOREAN_JOHAB = 0x82;
+    const CHARSET_ANSI_CHINESE_SIMIPLIFIED = 0x86; //    gb2312
+    const CHARSET_ANSI_CHINESE_TRADITIONAL = 0x88; //    big5
+    const CHARSET_ANSI_GREEK = 0xA1;
+    const CHARSET_ANSI_TURKISH = 0xA2;
+    const CHARSET_ANSI_VIETNAMESE = 0xA3;
+    const CHARSET_ANSI_HEBREW = 0xB1;
+    const CHARSET_ANSI_ARABIC = 0xB2;
+    const CHARSET_ANSI_BALTIC = 0xBA;
+    const CHARSET_ANSI_CYRILLIC = 0xCC;
+    const CHARSET_ANSI_THAI = 0xDD;
+    const CHARSET_ANSI_LATIN_II = 0xEE;
+    const CHARSET_OEM_LATIN_I = 0xFF;
+
+    //  XXX: Constants created!
+    /** Font filenames */
+    const ARIAL = 'arial.ttf';
+    const ARIAL_BOLD = 'arialbd.ttf';
+    const ARIAL_ITALIC = 'ariali.ttf';
+    const ARIAL_BOLD_ITALIC = 'arialbi.ttf';
+
+    const CALIBRI = 'CALIBRI.TTF';
+    const CALIBRI_BOLD = 'CALIBRIB.TTF';
+    const CALIBRI_ITALIC = 'CALIBRII.TTF';
+    const CALIBRI_BOLD_ITALIC = 'CALIBRIZ.TTF';
+
+    const COMIC_SANS_MS = 'comic.ttf';
+    const COMIC_SANS_MS_BOLD = 'comicbd.ttf';
+
+    const COURIER_NEW = 'cour.ttf';
+    const COURIER_NEW_BOLD = 'courbd.ttf';
+    const COURIER_NEW_ITALIC = 'couri.ttf';
+    const COURIER_NEW_BOLD_ITALIC = 'courbi.ttf';
+
+    const GEORGIA = 'georgia.ttf';
+    const GEORGIA_BOLD = 'georgiab.ttf';
+    const GEORGIA_ITALIC = 'georgiai.ttf';
+    const GEORGIA_BOLD_ITALIC = 'georgiaz.ttf';
+
+    const IMPACT = 'impact.ttf';
+
+    const LIBERATION_SANS = 'LiberationSans-Regular.ttf';
+    const LIBERATION_SANS_BOLD = 'LiberationSans-Bold.ttf';
+    const LIBERATION_SANS_ITALIC = 'LiberationSans-Italic.ttf';
+    const LIBERATION_SANS_BOLD_ITALIC = 'LiberationSans-BoldItalic.ttf';
+
+    const LUCIDA_CONSOLE = 'lucon.ttf';
+    const LUCIDA_SANS_UNICODE = 'l_10646.ttf';
+
+    const MICROSOFT_SANS_SERIF = 'micross.ttf';
+
+    const PALATINO_LINOTYPE = 'pala.ttf';
+    const PALATINO_LINOTYPE_BOLD = 'palab.ttf';
+    const PALATINO_LINOTYPE_ITALIC = 'palai.ttf';
+    const PALATINO_LINOTYPE_BOLD_ITALIC = 'palabi.ttf';
+
+    const SYMBOL = 'symbol.ttf';
+
+    const TAHOMA = 'tahoma.ttf';
+    const TAHOMA_BOLD = 'tahomabd.ttf';
+
+    const TIMES_NEW_ROMAN = 'times.ttf';
+    const TIMES_NEW_ROMAN_BOLD = 'timesbd.ttf';
+    const TIMES_NEW_ROMAN_ITALIC = 'timesi.ttf';
+    const TIMES_NEW_ROMAN_BOLD_ITALIC = 'timesbi.ttf';
+
+    const TREBUCHET_MS = 'trebuc.ttf';
+    const TREBUCHET_MS_BOLD = 'trebucbd.ttf';
+    const TREBUCHET_MS_ITALIC = 'trebucit.ttf';
+    const TREBUCHET_MS_BOLD_ITALIC = 'trebucbi.ttf';
+
+    const VERDANA = 'verdana.ttf';
+    const VERDANA_BOLD = 'verdanab.ttf';
+    const VERDANA_ITALIC = 'verdanai.ttf';
+    const VERDANA_BOLD_ITALIC = 'verdanaz.ttf';
+
+    /**
+     * AutoSize method.
+     *
+     * @var string
+     */
+    private static $autoSizeMethod = self::AUTOSIZE_METHOD_APPROX;
+
+    /**
+     * Path to folder containing TrueType font .ttf files.
+     *
+     * @var string
+     */
+    private static $trueTypeFontPath = null;
+
+    /**
+     * How wide is a default column for a given default font and size?
+     * Empirical data found by inspecting real Excel files and reading off the pixel width
+     * in Microsoft Office Excel 2007.
+     *
+     * @var array
+     */
+    public static $defaultColumnWidths = [
+        'Arial' => [
+            1 => ['px' => 24, 'width' => 12.00000000],
+            2 => ['px' => 24, 'width' => 12.00000000],
+            3 => ['px' => 32, 'width' => 10.66406250],
+            4 => ['px' => 32, 'width' => 10.66406250],
+            5 => ['px' => 40, 'width' => 10.00000000],
+            6 => ['px' => 48, 'width' => 9.59765625],
+            7 => ['px' => 48, 'width' => 9.59765625],
+            8 => ['px' => 56, 'width' => 9.33203125],
+            9 => ['px' => 64, 'width' => 9.14062500],
+            10 => ['px' => 64, 'width' => 9.14062500],
+        ],
+        'Calibri' => [
+            1 => ['px' => 24, 'width' => 12.00000000],
+            2 => ['px' => 24, 'width' => 12.00000000],
+            3 => ['px' => 32, 'width' => 10.66406250],
+            4 => ['px' => 32, 'width' => 10.66406250],
+            5 => ['px' => 40, 'width' => 10.00000000],
+            6 => ['px' => 48, 'width' => 9.59765625],
+            7 => ['px' => 48, 'width' => 9.59765625],
+            8 => ['px' => 56, 'width' => 9.33203125],
+            9 => ['px' => 56, 'width' => 9.33203125],
+            10 => ['px' => 64, 'width' => 9.14062500],
+            11 => ['px' => 64, 'width' => 9.14062500],
+        ],
+        'Verdana' => [
+            1 => ['px' => 24, 'width' => 12.00000000],
+            2 => ['px' => 24, 'width' => 12.00000000],
+            3 => ['px' => 32, 'width' => 10.66406250],
+            4 => ['px' => 32, 'width' => 10.66406250],
+            5 => ['px' => 40, 'width' => 10.00000000],
+            6 => ['px' => 48, 'width' => 9.59765625],
+            7 => ['px' => 48, 'width' => 9.59765625],
+            8 => ['px' => 64, 'width' => 9.14062500],
+            9 => ['px' => 72, 'width' => 9.00000000],
+            10 => ['px' => 72, 'width' => 9.00000000],
+        ],
+    ];
+
+    /**
+     * Set autoSize method.
+     *
+     * @param string $pValue see self::AUTOSIZE_METHOD_*
+     *
+     * @return bool Success or failure
+     */
+    public static function setAutoSizeMethod($pValue)
+    {
+        if (!in_array($pValue, self::$autoSizeMethods)) {
+            return false;
+        }
+        self::$autoSizeMethod = $pValue;
+
+        return true;
+    }
+
+    /**
+     * Get autoSize method.
+     *
+     * @return string
+     */
+    public static function getAutoSizeMethod()
+    {
+        return self::$autoSizeMethod;
+    }
+
+    /**
+     * Set the path to the folder containing .ttf files. There should be a trailing slash.
+     * Typical locations on variout some platforms:
+     *    <ul>
+     *        <li>C:/Windows/Fonts/</li>
+     *        <li>/usr/share/fonts/truetype/</li>
+     *        <li>~/.fonts/</li>
+     * </ul>.
+     *
+     * @param string $pValue
+     */
+    public static function setTrueTypeFontPath($pValue): void
+    {
+        self::$trueTypeFontPath = $pValue;
+    }
+
+    /**
+     * Get the path to the folder containing .ttf files.
+     *
+     * @return string
+     */
+    public static function getTrueTypeFontPath()
+    {
+        return self::$trueTypeFontPath;
+    }
+
+    /**
+     * Calculate an (approximate) OpenXML column width, based on font size and text contained.
+     *
+     * @param \PhpOffice\PhpSpreadsheet\Style\Font $font Font object
+     * @param RichText|string $cellText Text to calculate width
+     * @param int $rotation Rotation angle
+     * @param null|\PhpOffice\PhpSpreadsheet\Style\Font $defaultFont Font object
+     *
+     * @return int Column width
+     */
+    public static function calculateColumnWidth(\PhpOffice\PhpSpreadsheet\Style\Font $font, $cellText = '', $rotation = 0, ?\PhpOffice\PhpSpreadsheet\Style\Font $defaultFont = null)
+    {
+        // If it is rich text, use plain text
+        if ($cellText instanceof RichText) {
+            $cellText = $cellText->getPlainText();
+        }
+
+        // Special case if there are one or more newline characters ("\n")
+        if (strpos($cellText, "\n") !== false) {
+            $lineTexts = explode("\n", $cellText);
+            $lineWidths = [];
+            foreach ($lineTexts as $lineText) {
+                $lineWidths[] = self::calculateColumnWidth($font, $lineText, $rotation = 0, $defaultFont);
+            }
+
+            return max($lineWidths); // width of longest line in cell
+        }
+
+        // Try to get the exact text width in pixels
+        $approximate = self::$autoSizeMethod == self::AUTOSIZE_METHOD_APPROX;
+        if (!$approximate) {
+            $columnWidthAdjust = ceil(self::getTextWidthPixelsExact('n', $font, 0) * 1.07);
+
+            try {
+                // Width of text in pixels excl. padding
+                // and addition because Excel adds some padding, just use approx width of 'n' glyph
+                $columnWidth = self::getTextWidthPixelsExact($cellText, $font, $rotation) + $columnWidthAdjust;
+            } catch (PhpSpreadsheetException $e) {
+                $approximate = true;
+            }
+        }
+
+        if ($approximate) {
+            $columnWidthAdjust = self::getTextWidthPixelsApprox('n', $font, 0);
+            // Width of text in pixels excl. padding, approximation
+            // and addition because Excel adds some padding, just use approx width of 'n' glyph
+            $columnWidth = self::getTextWidthPixelsApprox($cellText, $font, $rotation) + $columnWidthAdjust;
+        }
+
+        // Convert from pixel width to column width
+        $columnWidth = Drawing::pixelsToCellDimension($columnWidth, $defaultFont);
+
+        // Return
+        return round($columnWidth, 6);
+    }
+
+    /**
+     * Get GD text width in pixels for a string of text in a certain font at a certain rotation angle.
+     *
+     * @param string $text
+     * @param \PhpOffice\PhpSpreadsheet\Style\Font
+     * @param int $rotation
+     *
+     * @return int
+     */
+    public static function getTextWidthPixelsExact($text, \PhpOffice\PhpSpreadsheet\Style\Font $font, $rotation = 0)
+    {
+        if (!function_exists('imagettfbbox')) {
+            throw new PhpSpreadsheetException('GD library needs to be enabled');
+        }
+
+        // font size should really be supplied in pixels in GD2,
+        // but since GD2 seems to assume 72dpi, pixels and points are the same
+        $fontFile = self::getTrueTypeFontFileFromFont($font);
+        $textBox = imagettfbbox($font->getSize(), $rotation, $fontFile, $text);
+
+        // Get corners positions
+        $lowerLeftCornerX = $textBox[0];
+        $lowerRightCornerX = $textBox[2];
+        $upperRightCornerX = $textBox[4];
+        $upperLeftCornerX = $textBox[6];
+
+        // Consider the rotation when calculating the width
+        return max($lowerRightCornerX - $upperLeftCornerX, $upperRightCornerX - $lowerLeftCornerX);
+    }
+
+    /**
+     * Get approximate width in pixels for a string of text in a certain font at a certain rotation angle.
+     *
+     * @param string $columnText
+     * @param int $rotation
+     *
+     * @return int Text width in pixels (no padding added)
+     */
+    public static function getTextWidthPixelsApprox($columnText, \PhpOffice\PhpSpreadsheet\Style\Font $font, $rotation = 0)
+    {
+        $fontName = $font->getName();
+        $fontSize = $font->getSize();
+
+        // Calculate column width in pixels. We assume fixed glyph width. Result varies with font name and size.
+        switch ($fontName) {
+            case 'Calibri':
+                // value 8.26 was found via interpolation by inspecting real Excel files with Calibri 11 font.
+                $columnWidth = (int) (8.26 * StringHelper::countCharacters($columnText));
+                $columnWidth = $columnWidth * $fontSize / 11; // extrapolate from font size
+                break;
+            case 'Arial':
+                // value 8 was set because of experience in different exports at Arial 10 font.
+                $columnWidth = (int) (8 * StringHelper::countCharacters($columnText));
+                $columnWidth = $columnWidth * $fontSize / 10; // extrapolate from font size
+                break;
+            case 'Verdana':
+                // value 8 was found via interpolation by inspecting real Excel files with Verdana 10 font.
+                $columnWidth = (int) (8 * StringHelper::countCharacters($columnText));
+                $columnWidth = $columnWidth * $fontSize / 10; // extrapolate from font size
+                break;
+            default:
+                // just assume Calibri
+                $columnWidth = (int) (8.26 * StringHelper::countCharacters($columnText));
+                $columnWidth = $columnWidth * $fontSize / 11; // extrapolate from font size
+                break;
+        }
+
+        // Calculate approximate rotated column width
+        if ($rotation !== 0) {
+            if ($rotation == -165) {
+                // stacked text
+                $columnWidth = 4; // approximation
+            } else {
+                // rotated text
+                $columnWidth = $columnWidth * cos(deg2rad($rotation))
+                                + $fontSize * abs(sin(deg2rad($rotation))) / 5; // approximation
+            }
+        }
+
+        // pixel width is an integer
+        return (int) $columnWidth;
+    }
+
+    /**
+     * Calculate an (approximate) pixel size, based on a font points size.
+     *
+     * @param int $fontSizeInPoints Font size (in points)
+     *
+     * @return int Font size (in pixels)
+     */
+    public static function fontSizeToPixels($fontSizeInPoints)
+    {
+        return (int) ((4 / 3) * $fontSizeInPoints);
+    }
+
+    /**
+     * Calculate an (approximate) pixel size, based on inch size.
+     *
+     * @param int $sizeInInch Font size (in inch)
+     *
+     * @return int Size (in pixels)
+     */
+    public static function inchSizeToPixels($sizeInInch)
+    {
+        return $sizeInInch * 96;
+    }
+
+    /**
+     * Calculate an (approximate) pixel size, based on centimeter size.
+     *
+     * @param int $sizeInCm Font size (in centimeters)
+     *
+     * @return float Size (in pixels)
+     */
+    public static function centimeterSizeToPixels($sizeInCm)
+    {
+        return $sizeInCm * 37.795275591;
+    }
+
+    /**
+     * Returns the font path given the font.
+     *
+     * @param \PhpOffice\PhpSpreadsheet\Style\Font $font
+     *
+     * @return string Path to TrueType font file
+     */
+    public static function getTrueTypeFontFileFromFont($font)
+    {
+        if (!file_exists(self::$trueTypeFontPath) || !is_dir(self::$trueTypeFontPath)) {
+            throw new PhpSpreadsheetException('Valid directory to TrueType Font files not specified');
+        }
+
+        $name = $font->getName();
+        $bold = $font->getBold();
+        $italic = $font->getItalic();
+
+        // Check if we can map font to true type font file
+        switch ($name) {
+            case 'Arial':
+                $fontFile = (
+                    $bold ? ($italic ? self::ARIAL_BOLD_ITALIC : self::ARIAL_BOLD)
+                          : ($italic ? self::ARIAL_ITALIC : self::ARIAL)
+                );
+
+                break;
+            case 'Calibri':
+                $fontFile = (
+                    $bold ? ($italic ? self::CALIBRI_BOLD_ITALIC : self::CALIBRI_BOLD)
+                          : ($italic ? self::CALIBRI_ITALIC : self::CALIBRI)
+                );
+
+                break;
+            case 'Courier New':
+                $fontFile = (
+                    $bold ? ($italic ? self::COURIER_NEW_BOLD_ITALIC : self::COURIER_NEW_BOLD)
+                          : ($italic ? self::COURIER_NEW_ITALIC : self::COURIER_NEW)
+                );
+
+                break;
+            case 'Comic Sans MS':
+                $fontFile = (
+                    $bold ? self::COMIC_SANS_MS_BOLD : self::COMIC_SANS_MS
+                );
+
+                break;
+            case 'Georgia':
+                $fontFile = (
+                    $bold ? ($italic ? self::GEORGIA_BOLD_ITALIC : self::GEORGIA_BOLD)
+                          : ($italic ? self::GEORGIA_ITALIC : self::GEORGIA)
+                );
+
+                break;
+            case 'Impact':
+                $fontFile = self::IMPACT;
+
+                break;
+            case 'Liberation Sans':
+                $fontFile = (
+                    $bold ? ($italic ? self::LIBERATION_SANS_BOLD_ITALIC : self::LIBERATION_SANS_BOLD)
+                          : ($italic ? self::LIBERATION_SANS_ITALIC : self::LIBERATION_SANS)
+                );
+
+                break;
+            case 'Lucida Console':
+                $fontFile = self::LUCIDA_CONSOLE;
+
+                break;
+            case 'Lucida Sans Unicode':
+                $fontFile = self::LUCIDA_SANS_UNICODE;
+
+                break;
+            case 'Microsoft Sans Serif':
+                $fontFile = self::MICROSOFT_SANS_SERIF;
+
+                break;
+            case 'Palatino Linotype':
+                $fontFile = (
+                    $bold ? ($italic ? self::PALATINO_LINOTYPE_BOLD_ITALIC : self::PALATINO_LINOTYPE_BOLD)
+                          : ($italic ? self::PALATINO_LINOTYPE_ITALIC : self::PALATINO_LINOTYPE)
+                );
+
+                break;
+            case 'Symbol':
+                $fontFile = self::SYMBOL;
+
+                break;
+            case 'Tahoma':
+                $fontFile = (
+                    $bold ? self::TAHOMA_BOLD : self::TAHOMA
+                );
+
+                break;
+            case 'Times New Roman':
+                $fontFile = (
+                    $bold ? ($italic ? self::TIMES_NEW_ROMAN_BOLD_ITALIC : self::TIMES_NEW_ROMAN_BOLD)
+                          : ($italic ? self::TIMES_NEW_ROMAN_ITALIC : self::TIMES_NEW_ROMAN)
+                );
+
+                break;
+            case 'Trebuchet MS':
+                $fontFile = (
+                    $bold ? ($italic ? self::TREBUCHET_MS_BOLD_ITALIC : self::TREBUCHET_MS_BOLD)
+                          : ($italic ? self::TREBUCHET_MS_ITALIC : self::TREBUCHET_MS)
+                );
+
+                break;
+            case 'Verdana':
+                $fontFile = (
+                    $bold ? ($italic ? self::VERDANA_BOLD_ITALIC : self::VERDANA_BOLD)
+                          : ($italic ? self::VERDANA_ITALIC : self::VERDANA)
+                );
+
+                break;
+            default:
+                throw new PhpSpreadsheetException('Unknown font name "' . $name . '". Cannot map to TrueType font file');
+
+                break;
+        }
+
+        $fontFile = self::$trueTypeFontPath . $fontFile;
+
+        // Check if file actually exists
+        if (!file_exists($fontFile)) {
+            throw new PhpSpreadsheetException('TrueType Font file not found');
+        }
+
+        return $fontFile;
+    }
+
+    /**
+     * Returns the associated charset for the font name.
+     *
+     * @param string $name Font name
+     *
+     * @return int Character set code
+     */
+    public static function getCharsetFromFontName($name)
+    {
+        switch ($name) {
+            // Add more cases. Check FONT records in real Excel files.
+            case 'EucrosiaUPC':
+                return self::CHARSET_ANSI_THAI;
+            case 'Wingdings':
+                return self::CHARSET_SYMBOL;
+            case 'Wingdings 2':
+                return self::CHARSET_SYMBOL;
+            case 'Wingdings 3':
+                return self::CHARSET_SYMBOL;
+            default:
+                return self::CHARSET_ANSI_LATIN;
+        }
+    }
+
+    /**
+     * Get the effective column width for columns without a column dimension or column with width -1
+     * For example, for Calibri 11 this is 9.140625 (64 px).
+     *
+     * @param \PhpOffice\PhpSpreadsheet\Style\Font $font The workbooks default font
+     * @param bool $pPixels true = return column width in pixels, false = return in OOXML units
+     *
+     * @return mixed Column width
+     */
+    public static function getDefaultColumnWidthByFont(\PhpOffice\PhpSpreadsheet\Style\Font $font, $pPixels = false)
+    {
+        if (isset(self::$defaultColumnWidths[$font->getName()][$font->getSize()])) {
+            // Exact width can be determined
+            $columnWidth = $pPixels ?
+                self::$defaultColumnWidths[$font->getName()][$font->getSize()]['px']
+                    : self::$defaultColumnWidths[$font->getName()][$font->getSize()]['width'];
+        } else {
+            // We don't have data for this particular font and size, use approximation by
+            // extrapolating from Calibri 11
+            $columnWidth = $pPixels ?
+                self::$defaultColumnWidths['Calibri'][11]['px']
+                    : self::$defaultColumnWidths['Calibri'][11]['width'];
+            $columnWidth = $columnWidth * $font->getSize() / 11;
+
+            // Round pixels to closest integer
+            if ($pPixels) {
+                $columnWidth = (int) round($columnWidth);
+            }
+        }
+
+        return $columnWidth;
+    }
+
+    /**
+     * Get the effective row height for rows without a row dimension or rows with height -1
+     * For example, for Calibri 11 this is 15 points.
+     *
+     * @param \PhpOffice\PhpSpreadsheet\Style\Font $font The workbooks default font
+     *
+     * @return float Row height in points
+     */
+    public static function getDefaultRowHeightByFont(\PhpOffice\PhpSpreadsheet\Style\Font $font)
+    {
+        switch ($font->getName()) {
+            case 'Arial':
+                switch ($font->getSize()) {
+                    case 10:
+                        // inspection of Arial 10 workbook says 12.75pt ~17px
+                        $rowHeight = 12.75;
+
+                        break;
+                    case 9:
+                        // inspection of Arial 9 workbook says 12.00pt ~16px
+                        $rowHeight = 12;
+
+                        break;
+                    case 8:
+                        // inspection of Arial 8 workbook says 11.25pt ~15px
+                        $rowHeight = 11.25;
+
+                        break;
+                    case 7:
+                        // inspection of Arial 7 workbook says 9.00pt ~12px
+                        $rowHeight = 9;
+
+                        break;
+                    case 6:
+                    case 5:
+                        // inspection of Arial 5,6 workbook says 8.25pt ~11px
+                        $rowHeight = 8.25;
+
+                        break;
+                    case 4:
+                        // inspection of Arial 4 workbook says 6.75pt ~9px
+                        $rowHeight = 6.75;
+
+                        break;
+                    case 3:
+                        // inspection of Arial 3 workbook says 6.00pt ~8px
+                        $rowHeight = 6;
+
+                        break;
+                    case 2:
+                    case 1:
+                        // inspection of Arial 1,2 workbook says 5.25pt ~7px
+                        $rowHeight = 5.25;
+
+                        break;
+                    default:
+                        // use Arial 10 workbook as an approximation, extrapolation
+                        $rowHeight = 12.75 * $font->getSize() / 10;
+
+                        break;
+                }
+
+                break;
+            case 'Calibri':
+                switch ($font->getSize()) {
+                    case 11:
+                        // inspection of Calibri 11 workbook says 15.00pt ~20px
+                        $rowHeight = 15;
+
+                        break;
+                    case 10:
+                        // inspection of Calibri 10 workbook says 12.75pt ~17px
+                        $rowHeight = 12.75;
+
+                        break;
+                    case 9:
+                        // inspection of Calibri 9 workbook says 12.00pt ~16px
+                        $rowHeight = 12;
+
+                        break;
+                    case 8:
+                        // inspection of Calibri 8 workbook says 11.25pt ~15px
+                        $rowHeight = 11.25;
+
+                        break;
+                    case 7:
+                        // inspection of Calibri 7 workbook says 9.00pt ~12px
+                        $rowHeight = 9;
+
+                        break;
+                    case 6:
+                    case 5:
+                        // inspection of Calibri 5,6 workbook says 8.25pt ~11px
+                        $rowHeight = 8.25;
+
+                        break;
+                    case 4:
+                        // inspection of Calibri 4 workbook says 6.75pt ~9px
+                        $rowHeight = 6.75;
+
+                        break;
+                    case 3:
+                        // inspection of Calibri 3 workbook says 6.00pt ~8px
+                        $rowHeight = 6.00;
+
+                        break;
+                    case 2:
+                    case 1:
+                        // inspection of Calibri 1,2 workbook says 5.25pt ~7px
+                        $rowHeight = 5.25;
+
+                        break;
+                    default:
+                        // use Calibri 11 workbook as an approximation, extrapolation
+                        $rowHeight = 15 * $font->getSize() / 11;
+
+                        break;
+                }
+
+                break;
+            case 'Verdana':
+                switch ($font->getSize()) {
+                    case 10:
+                        // inspection of Verdana 10 workbook says 12.75pt ~17px
+                        $rowHeight = 12.75;
+
+                        break;
+                    case 9:
+                        // inspection of Verdana 9 workbook says 11.25pt ~15px
+                        $rowHeight = 11.25;
+
+                        break;
+                    case 8:
+                        // inspection of Verdana 8 workbook says 10.50pt ~14px
+                        $rowHeight = 10.50;
+
+                        break;
+                    case 7:
+                        // inspection of Verdana 7 workbook says 9.00pt ~12px
+                        $rowHeight = 9.00;
+
+                        break;
+                    case 6:
+                    case 5:
+                        // inspection of Verdana 5,6 workbook says 8.25pt ~11px
+                        $rowHeight = 8.25;
+
+                        break;
+                    case 4:
+                        // inspection of Verdana 4 workbook says 6.75pt ~9px
+                        $rowHeight = 6.75;
+
+                        break;
+                    case 3:
+                        // inspection of Verdana 3 workbook says 6.00pt ~8px
+                        $rowHeight = 6;
+
+                        break;
+                    case 2:
+                    case 1:
+                        // inspection of Verdana 1,2 workbook says 5.25pt ~7px
+                        $rowHeight = 5.25;
+
+                        break;
+                    default:
+                        // use Verdana 10 workbook as an approximation, extrapolation
+                        $rowHeight = 12.75 * $font->getSize() / 10;
+
+                        break;
+                }
+
+                break;
+            default:
+                // just use Calibri as an approximation
+                $rowHeight = 15 * $font->getSize() / 11;
+
+                break;
+        }
+
+        return $rowHeight;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/CHANGELOG.TXT b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/CHANGELOG.TXT
new file mode 100755
index 0000000..1c18a5d
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/CHANGELOG.TXT
@@ -0,0 +1,16 @@
+Mar 1, 2005 11:15 AST by PM
+
++ For consistency, renamed Math.php to Maths.java, utils to util, 
+  tests to test, docs to doc - 
+
++ Removed conditional logic from top of Matrix class.
+
++ Switched to using hypo function in Maths.php for all php-hypot calls.
+  NOTE TO SELF: Need to make sure that all decompositions have been 
+  switched over to using the bundled hypo.
+
+Feb 25, 2005 at 10:00 AST by PM
+
++ Recommend using simpler Error.php instead of JAMA_Error.php but 
+  can be persuaded otherwise.
+
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/CholeskyDecomposition.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/CholeskyDecomposition.php
new file mode 100755
index 0000000..2b241d5
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/CholeskyDecomposition.php
@@ -0,0 +1,147 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared\JAMA;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalculationException;
+
+/**
+ *    Cholesky decomposition class.
+ *
+ *    For a symmetric, positive definite matrix A, the Cholesky decomposition
+ *    is an lower triangular matrix L so that A = L*L'.
+ *
+ *    If the matrix is not symmetric or positive definite, the constructor
+ *    returns a partial decomposition and sets an internal flag that may
+ *    be queried by the isSPD() method.
+ *
+ *    @author Paul Meagher
+ *    @author Michael Bommarito
+ *
+ *    @version 1.2
+ */
+class CholeskyDecomposition
+{
+    /**
+     * Decomposition storage.
+     *
+     * @var array
+     */
+    private $L = [];
+
+    /**
+     * Matrix row and column dimension.
+     *
+     * @var int
+     */
+    private $m;
+
+    /**
+     * Symmetric positive definite flag.
+     *
+     * @var bool
+     */
+    private $isspd = true;
+
+    /**
+     * CholeskyDecomposition.
+     *
+     *    Class constructor - decomposes symmetric positive definite matrix
+     *
+     * @param Matrix $A Matrix square symmetric positive definite matrix
+     */
+    public function __construct(Matrix $A)
+    {
+        $this->L = $A->getArray();
+        $this->m = $A->getRowDimension();
+
+        for ($i = 0; $i < $this->m; ++$i) {
+            for ($j = $i; $j < $this->m; ++$j) {
+                for ($sum = $this->L[$i][$j], $k = $i - 1; $k >= 0; --$k) {
+                    $sum -= $this->L[$i][$k] * $this->L[$j][$k];
+                }
+                if ($i == $j) {
+                    if ($sum >= 0) {
+                        $this->L[$i][$i] = sqrt($sum);
+                    } else {
+                        $this->isspd = false;
+                    }
+                } else {
+                    if ($this->L[$i][$i] != 0) {
+                        $this->L[$j][$i] = $sum / $this->L[$i][$i];
+                    }
+                }
+            }
+
+            for ($k = $i + 1; $k < $this->m; ++$k) {
+                $this->L[$i][$k] = 0.0;
+            }
+        }
+    }
+
+    /**
+     *    Is the matrix symmetric and positive definite?
+     *
+     * @return bool
+     */
+    public function isSPD()
+    {
+        return $this->isspd;
+    }
+
+    /**
+     * getL.
+     *
+     * Return triangular factor.
+     *
+     * @return Matrix Lower triangular matrix
+     */
+    public function getL()
+    {
+        return new Matrix($this->L);
+    }
+
+    /**
+     * Solve A*X = B.
+     *
+     * @param $B Row-equal matrix
+     *
+     * @return Matrix L * L' * X = B
+     */
+    public function solve(Matrix $B)
+    {
+        if ($B->getRowDimension() == $this->m) {
+            if ($this->isspd) {
+                $X = $B->getArrayCopy();
+                $nx = $B->getColumnDimension();
+
+                for ($k = 0; $k < $this->m; ++$k) {
+                    for ($i = $k + 1; $i < $this->m; ++$i) {
+                        for ($j = 0; $j < $nx; ++$j) {
+                            $X[$i][$j] -= $X[$k][$j] * $this->L[$i][$k];
+                        }
+                    }
+                    for ($j = 0; $j < $nx; ++$j) {
+                        $X[$k][$j] /= $this->L[$k][$k];
+                    }
+                }
+
+                for ($k = $this->m - 1; $k >= 0; --$k) {
+                    for ($j = 0; $j < $nx; ++$j) {
+                        $X[$k][$j] /= $this->L[$k][$k];
+                    }
+                    for ($i = 0; $i < $k; ++$i) {
+                        for ($j = 0; $j < $nx; ++$j) {
+                            $X[$i][$j] -= $X[$k][$j] * $this->L[$k][$i];
+                        }
+                    }
+                }
+
+                return new Matrix($X, $this->m, $nx);
+            }
+
+            throw new CalculationException(Matrix::MATRIX_SPD_EXCEPTION);
+        }
+
+        throw new CalculationException(Matrix::MATRIX_DIMENSION_EXCEPTION);
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/EigenvalueDecomposition.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/EigenvalueDecomposition.php
new file mode 100755
index 0000000..f6fbccb
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/EigenvalueDecomposition.php
@@ -0,0 +1,861 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared\JAMA;
+
+/**
+ *    Class to obtain eigenvalues and eigenvectors of a real matrix.
+ *
+ *    If A is symmetric, then A = V*D*V' where the eigenvalue matrix D
+ *    is diagonal and the eigenvector matrix V is orthogonal (i.e.
+ *    A = V.times(D.times(V.transpose())) and V.times(V.transpose())
+ *    equals the identity matrix).
+ *
+ *    If A is not symmetric, then the eigenvalue matrix D is block diagonal
+ *    with the real eigenvalues in 1-by-1 blocks and any complex eigenvalues,
+ *    lambda + i*mu, in 2-by-2 blocks, [lambda, mu; -mu, lambda].  The
+ *    columns of V represent the eigenvectors in the sense that A*V = V*D,
+ *    i.e. A.times(V) equals V.times(D).  The matrix V may be badly
+ *    conditioned, or even singular, so the validity of the equation
+ *    A = V*D*inverse(V) depends upon V.cond().
+ *
+ *    @author  Paul Meagher
+ *
+ *    @version 1.1
+ */
+class EigenvalueDecomposition
+{
+    /**
+     * Row and column dimension (square matrix).
+     *
+     * @var int
+     */
+    private $n;
+
+    /**
+     * Arrays for internal storage of eigenvalues.
+     *
+     * @var array
+     */
+    private $d = [];
+
+    private $e = [];
+
+    /**
+     * Array for internal storage of eigenvectors.
+     *
+     * @var array
+     */
+    private $V = [];
+
+    /**
+     * Array for internal storage of nonsymmetric Hessenberg form.
+     *
+     * @var array
+     */
+    private $H = [];
+
+    /**
+     * Working storage for nonsymmetric algorithm.
+     *
+     * @var array
+     */
+    private $ort;
+
+    /**
+     * Used for complex scalar division.
+     *
+     * @var float
+     */
+    private $cdivr;
+
+    private $cdivi;
+
+    /**
+     * Symmetric Householder reduction to tridiagonal form.
+     */
+    private function tred2(): void
+    {
+        //  This is derived from the Algol procedures tred2 by
+        //  Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
+        //  Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
+        //  Fortran subroutine in EISPACK.
+        $this->d = $this->V[$this->n - 1];
+        // Householder reduction to tridiagonal form.
+        for ($i = $this->n - 1; $i > 0; --$i) {
+            $i_ = $i - 1;
+            // Scale to avoid under/overflow.
+            $h = $scale = 0.0;
+            $scale += array_sum(array_map('abs', $this->d));
+            if ($scale == 0.0) {
+                $this->e[$i] = $this->d[$i_];
+                $this->d = array_slice($this->V[$i_], 0, $i_);
+                for ($j = 0; $j < $i; ++$j) {
+                    $this->V[$j][$i] = $this->V[$i][$j] = 0.0;
+                }
+            } else {
+                // Generate Householder vector.
+                for ($k = 0; $k < $i; ++$k) {
+                    $this->d[$k] /= $scale;
+                    $h += $this->d[$k] ** 2;
+                }
+                $f = $this->d[$i_];
+                $g = sqrt($h);
+                if ($f > 0) {
+                    $g = -$g;
+                }
+                $this->e[$i] = $scale * $g;
+                $h = $h - $f * $g;
+                $this->d[$i_] = $f - $g;
+                for ($j = 0; $j < $i; ++$j) {
+                    $this->e[$j] = 0.0;
+                }
+                // Apply similarity transformation to remaining columns.
+                for ($j = 0; $j < $i; ++$j) {
+                    $f = $this->d[$j];
+                    $this->V[$j][$i] = $f;
+                    $g = $this->e[$j] + $this->V[$j][$j] * $f;
+                    for ($k = $j + 1; $k <= $i_; ++$k) {
+                        $g += $this->V[$k][$j] * $this->d[$k];
+                        $this->e[$k] += $this->V[$k][$j] * $f;
+                    }
+                    $this->e[$j] = $g;
+                }
+                $f = 0.0;
+                for ($j = 0; $j < $i; ++$j) {
+                    $this->e[$j] /= $h;
+                    $f += $this->e[$j] * $this->d[$j];
+                }
+                $hh = $f / (2 * $h);
+                for ($j = 0; $j < $i; ++$j) {
+                    $this->e[$j] -= $hh * $this->d[$j];
+                }
+                for ($j = 0; $j < $i; ++$j) {
+                    $f = $this->d[$j];
+                    $g = $this->e[$j];
+                    for ($k = $j; $k <= $i_; ++$k) {
+                        $this->V[$k][$j] -= ($f * $this->e[$k] + $g * $this->d[$k]);
+                    }
+                    $this->d[$j] = $this->V[$i - 1][$j];
+                    $this->V[$i][$j] = 0.0;
+                }
+            }
+            $this->d[$i] = $h;
+        }
+
+        // Accumulate transformations.
+        for ($i = 0; $i < $this->n - 1; ++$i) {
+            $this->V[$this->n - 1][$i] = $this->V[$i][$i];
+            $this->V[$i][$i] = 1.0;
+            $h = $this->d[$i + 1];
+            if ($h != 0.0) {
+                for ($k = 0; $k <= $i; ++$k) {
+                    $this->d[$k] = $this->V[$k][$i + 1] / $h;
+                }
+                for ($j = 0; $j <= $i; ++$j) {
+                    $g = 0.0;
+                    for ($k = 0; $k <= $i; ++$k) {
+                        $g += $this->V[$k][$i + 1] * $this->V[$k][$j];
+                    }
+                    for ($k = 0; $k <= $i; ++$k) {
+                        $this->V[$k][$j] -= $g * $this->d[$k];
+                    }
+                }
+            }
+            for ($k = 0; $k <= $i; ++$k) {
+                $this->V[$k][$i + 1] = 0.0;
+            }
+        }
+
+        $this->d = $this->V[$this->n - 1];
+        $this->V[$this->n - 1] = array_fill(0, $j, 0.0);
+        $this->V[$this->n - 1][$this->n - 1] = 1.0;
+        $this->e[0] = 0.0;
+    }
+
+    /**
+     * Symmetric tridiagonal QL algorithm.
+     *
+     *    This is derived from the Algol procedures tql2, by
+     *    Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
+     *    Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
+     * Fortran subroutine in EISPACK.
+     */
+    private function tql2(): void
+    {
+        for ($i = 1; $i < $this->n; ++$i) {
+            $this->e[$i - 1] = $this->e[$i];
+        }
+        $this->e[$this->n - 1] = 0.0;
+        $f = 0.0;
+        $tst1 = 0.0;
+        $eps = 2.0 ** (-52.0);
+
+        for ($l = 0; $l < $this->n; ++$l) {
+            // Find small subdiagonal element
+            $tst1 = max($tst1, abs($this->d[$l]) + abs($this->e[$l]));
+            $m = $l;
+            while ($m < $this->n) {
+                if (abs($this->e[$m]) <= $eps * $tst1) {
+                    break;
+                }
+                ++$m;
+            }
+            // If m == l, $this->d[l] is an eigenvalue,
+            // otherwise, iterate.
+            if ($m > $l) {
+                $iter = 0;
+                do {
+                    // Could check iteration count here.
+                    ++$iter;
+                    // Compute implicit shift
+                    $g = $this->d[$l];
+                    $p = ($this->d[$l + 1] - $g) / (2.0 * $this->e[$l]);
+                    $r = hypo($p, 1.0);
+                    if ($p < 0) {
+                        $r *= -1;
+                    }
+                    $this->d[$l] = $this->e[$l] / ($p + $r);
+                    $this->d[$l + 1] = $this->e[$l] * ($p + $r);
+                    $dl1 = $this->d[$l + 1];
+                    $h = $g - $this->d[$l];
+                    for ($i = $l + 2; $i < $this->n; ++$i) {
+                        $this->d[$i] -= $h;
+                    }
+                    $f += $h;
+                    // Implicit QL transformation.
+                    $p = $this->d[$m];
+                    $c = 1.0;
+                    $c2 = $c3 = $c;
+                    $el1 = $this->e[$l + 1];
+                    $s = $s2 = 0.0;
+                    for ($i = $m - 1; $i >= $l; --$i) {
+                        $c3 = $c2;
+                        $c2 = $c;
+                        $s2 = $s;
+                        $g = $c * $this->e[$i];
+                        $h = $c * $p;
+                        $r = hypo($p, $this->e[$i]);
+                        $this->e[$i + 1] = $s * $r;
+                        $s = $this->e[$i] / $r;
+                        $c = $p / $r;
+                        $p = $c * $this->d[$i] - $s * $g;
+                        $this->d[$i + 1] = $h + $s * ($c * $g + $s * $this->d[$i]);
+                        // Accumulate transformation.
+                        for ($k = 0; $k < $this->n; ++$k) {
+                            $h = $this->V[$k][$i + 1];
+                            $this->V[$k][$i + 1] = $s * $this->V[$k][$i] + $c * $h;
+                            $this->V[$k][$i] = $c * $this->V[$k][$i] - $s * $h;
+                        }
+                    }
+                    $p = -$s * $s2 * $c3 * $el1 * $this->e[$l] / $dl1;
+                    $this->e[$l] = $s * $p;
+                    $this->d[$l] = $c * $p;
+                    // Check for convergence.
+                } while (abs($this->e[$l]) > $eps * $tst1);
+            }
+            $this->d[$l] = $this->d[$l] + $f;
+            $this->e[$l] = 0.0;
+        }
+
+        // Sort eigenvalues and corresponding vectors.
+        for ($i = 0; $i < $this->n - 1; ++$i) {
+            $k = $i;
+            $p = $this->d[$i];
+            for ($j = $i + 1; $j < $this->n; ++$j) {
+                if ($this->d[$j] < $p) {
+                    $k = $j;
+                    $p = $this->d[$j];
+                }
+            }
+            if ($k != $i) {
+                $this->d[$k] = $this->d[$i];
+                $this->d[$i] = $p;
+                for ($j = 0; $j < $this->n; ++$j) {
+                    $p = $this->V[$j][$i];
+                    $this->V[$j][$i] = $this->V[$j][$k];
+                    $this->V[$j][$k] = $p;
+                }
+            }
+        }
+    }
+
+    /**
+     * Nonsymmetric reduction to Hessenberg form.
+     *
+     *    This is derived from the Algol procedures orthes and ortran,
+     *    by Martin and Wilkinson, Handbook for Auto. Comp.,
+     *    Vol.ii-Linear Algebra, and the corresponding
+     * Fortran subroutines in EISPACK.
+     */
+    private function orthes(): void
+    {
+        $low = 0;
+        $high = $this->n - 1;
+
+        for ($m = $low + 1; $m <= $high - 1; ++$m) {
+            // Scale column.
+            $scale = 0.0;
+            for ($i = $m; $i <= $high; ++$i) {
+                $scale = $scale + abs($this->H[$i][$m - 1]);
+            }
+            if ($scale != 0.0) {
+                // Compute Householder transformation.
+                $h = 0.0;
+                for ($i = $high; $i >= $m; --$i) {
+                    $this->ort[$i] = $this->H[$i][$m - 1] / $scale;
+                    $h += $this->ort[$i] * $this->ort[$i];
+                }
+                $g = sqrt($h);
+                if ($this->ort[$m] > 0) {
+                    $g *= -1;
+                }
+                $h -= $this->ort[$m] * $g;
+                $this->ort[$m] -= $g;
+                // Apply Householder similarity transformation
+                // H = (I -u * u' / h) * H * (I -u * u') / h)
+                for ($j = $m; $j < $this->n; ++$j) {
+                    $f = 0.0;
+                    for ($i = $high; $i >= $m; --$i) {
+                        $f += $this->ort[$i] * $this->H[$i][$j];
+                    }
+                    $f /= $h;
+                    for ($i = $m; $i <= $high; ++$i) {
+                        $this->H[$i][$j] -= $f * $this->ort[$i];
+                    }
+                }
+                for ($i = 0; $i <= $high; ++$i) {
+                    $f = 0.0;
+                    for ($j = $high; $j >= $m; --$j) {
+                        $f += $this->ort[$j] * $this->H[$i][$j];
+                    }
+                    $f = $f / $h;
+                    for ($j = $m; $j <= $high; ++$j) {
+                        $this->H[$i][$j] -= $f * $this->ort[$j];
+                    }
+                }
+                $this->ort[$m] = $scale * $this->ort[$m];
+                $this->H[$m][$m - 1] = $scale * $g;
+            }
+        }
+
+        // Accumulate transformations (Algol's ortran).
+        for ($i = 0; $i < $this->n; ++$i) {
+            for ($j = 0; $j < $this->n; ++$j) {
+                $this->V[$i][$j] = ($i == $j ? 1.0 : 0.0);
+            }
+        }
+        for ($m = $high - 1; $m >= $low + 1; --$m) {
+            if ($this->H[$m][$m - 1] != 0.0) {
+                for ($i = $m + 1; $i <= $high; ++$i) {
+                    $this->ort[$i] = $this->H[$i][$m - 1];
+                }
+                for ($j = $m; $j <= $high; ++$j) {
+                    $g = 0.0;
+                    for ($i = $m; $i <= $high; ++$i) {
+                        $g += $this->ort[$i] * $this->V[$i][$j];
+                    }
+                    // Double division avoids possible underflow
+                    $g = ($g / $this->ort[$m]) / $this->H[$m][$m - 1];
+                    for ($i = $m; $i <= $high; ++$i) {
+                        $this->V[$i][$j] += $g * $this->ort[$i];
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Performs complex division.
+     *
+     * @param mixed $xr
+     * @param mixed $xi
+     * @param mixed $yr
+     * @param mixed $yi
+     */
+    private function cdiv($xr, $xi, $yr, $yi): void
+    {
+        if (abs($yr) > abs($yi)) {
+            $r = $yi / $yr;
+            $d = $yr + $r * $yi;
+            $this->cdivr = ($xr + $r * $xi) / $d;
+            $this->cdivi = ($xi - $r * $xr) / $d;
+        } else {
+            $r = $yr / $yi;
+            $d = $yi + $r * $yr;
+            $this->cdivr = ($r * $xr + $xi) / $d;
+            $this->cdivi = ($r * $xi - $xr) / $d;
+        }
+    }
+
+    /**
+     * Nonsymmetric reduction from Hessenberg to real Schur form.
+     *
+     *    Code is derived from the Algol procedure hqr2,
+     *    by Martin and Wilkinson, Handbook for Auto. Comp.,
+     *    Vol.ii-Linear Algebra, and the corresponding
+     * Fortran subroutine in EISPACK.
+     */
+    private function hqr2(): void
+    {
+        //  Initialize
+        $nn = $this->n;
+        $n = $nn - 1;
+        $low = 0;
+        $high = $nn - 1;
+        $eps = 2.0 ** (-52.0);
+        $exshift = 0.0;
+        $p = $q = $r = $s = $z = 0;
+        // Store roots isolated by balanc and compute matrix norm
+        $norm = 0.0;
+
+        for ($i = 0; $i < $nn; ++$i) {
+            if (($i < $low) || ($i > $high)) {
+                $this->d[$i] = $this->H[$i][$i];
+                $this->e[$i] = 0.0;
+            }
+            for ($j = max($i - 1, 0); $j < $nn; ++$j) {
+                $norm = $norm + abs($this->H[$i][$j]);
+            }
+        }
+
+        // Outer loop over eigenvalue index
+        $iter = 0;
+        while ($n >= $low) {
+            // Look for single small sub-diagonal element
+            $l = $n;
+            while ($l > $low) {
+                $s = abs($this->H[$l - 1][$l - 1]) + abs($this->H[$l][$l]);
+                if ($s == 0.0) {
+                    $s = $norm;
+                }
+                if (abs($this->H[$l][$l - 1]) < $eps * $s) {
+                    break;
+                }
+                --$l;
+            }
+            // Check for convergence
+            // One root found
+            if ($l == $n) {
+                $this->H[$n][$n] = $this->H[$n][$n] + $exshift;
+                $this->d[$n] = $this->H[$n][$n];
+                $this->e[$n] = 0.0;
+                --$n;
+                $iter = 0;
+            // Two roots found
+            } elseif ($l == $n - 1) {
+                $w = $this->H[$n][$n - 1] * $this->H[$n - 1][$n];
+                $p = ($this->H[$n - 1][$n - 1] - $this->H[$n][$n]) / 2.0;
+                $q = $p * $p + $w;
+                $z = sqrt(abs($q));
+                $this->H[$n][$n] = $this->H[$n][$n] + $exshift;
+                $this->H[$n - 1][$n - 1] = $this->H[$n - 1][$n - 1] + $exshift;
+                $x = $this->H[$n][$n];
+                // Real pair
+                if ($q >= 0) {
+                    if ($p >= 0) {
+                        $z = $p + $z;
+                    } else {
+                        $z = $p - $z;
+                    }
+                    $this->d[$n - 1] = $x + $z;
+                    $this->d[$n] = $this->d[$n - 1];
+                    if ($z != 0.0) {
+                        $this->d[$n] = $x - $w / $z;
+                    }
+                    $this->e[$n - 1] = 0.0;
+                    $this->e[$n] = 0.0;
+                    $x = $this->H[$n][$n - 1];
+                    $s = abs($x) + abs($z);
+                    $p = $x / $s;
+                    $q = $z / $s;
+                    $r = sqrt($p * $p + $q * $q);
+                    $p = $p / $r;
+                    $q = $q / $r;
+                    // Row modification
+                    for ($j = $n - 1; $j < $nn; ++$j) {
+                        $z = $this->H[$n - 1][$j];
+                        $this->H[$n - 1][$j] = $q * $z + $p * $this->H[$n][$j];
+                        $this->H[$n][$j] = $q * $this->H[$n][$j] - $p * $z;
+                    }
+                    // Column modification
+                    for ($i = 0; $i <= $n; ++$i) {
+                        $z = $this->H[$i][$n - 1];
+                        $this->H[$i][$n - 1] = $q * $z + $p * $this->H[$i][$n];
+                        $this->H[$i][$n] = $q * $this->H[$i][$n] - $p * $z;
+                    }
+                    // Accumulate transformations
+                    for ($i = $low; $i <= $high; ++$i) {
+                        $z = $this->V[$i][$n - 1];
+                        $this->V[$i][$n - 1] = $q * $z + $p * $this->V[$i][$n];
+                        $this->V[$i][$n] = $q * $this->V[$i][$n] - $p * $z;
+                    }
+                    // Complex pair
+                } else {
+                    $this->d[$n - 1] = $x + $p;
+                    $this->d[$n] = $x + $p;
+                    $this->e[$n - 1] = $z;
+                    $this->e[$n] = -$z;
+                }
+                $n = $n - 2;
+                $iter = 0;
+            // No convergence yet
+            } else {
+                // Form shift
+                $x = $this->H[$n][$n];
+                $y = 0.0;
+                $w = 0.0;
+                if ($l < $n) {
+                    $y = $this->H[$n - 1][$n - 1];
+                    $w = $this->H[$n][$n - 1] * $this->H[$n - 1][$n];
+                }
+                // Wilkinson's original ad hoc shift
+                if ($iter == 10) {
+                    $exshift += $x;
+                    for ($i = $low; $i <= $n; ++$i) {
+                        $this->H[$i][$i] -= $x;
+                    }
+                    $s = abs($this->H[$n][$n - 1]) + abs($this->H[$n - 1][$n - 2]);
+                    $x = $y = 0.75 * $s;
+                    $w = -0.4375 * $s * $s;
+                }
+                // MATLAB's new ad hoc shift
+                if ($iter == 30) {
+                    $s = ($y - $x) / 2.0;
+                    $s = $s * $s + $w;
+                    if ($s > 0) {
+                        $s = sqrt($s);
+                        if ($y < $x) {
+                            $s = -$s;
+                        }
+                        $s = $x - $w / (($y - $x) / 2.0 + $s);
+                        for ($i = $low; $i <= $n; ++$i) {
+                            $this->H[$i][$i] -= $s;
+                        }
+                        $exshift += $s;
+                        $x = $y = $w = 0.964;
+                    }
+                }
+                // Could check iteration count here.
+                $iter = $iter + 1;
+                // Look for two consecutive small sub-diagonal elements
+                $m = $n - 2;
+                while ($m >= $l) {
+                    $z = $this->H[$m][$m];
+                    $r = $x - $z;
+                    $s = $y - $z;
+                    $p = ($r * $s - $w) / $this->H[$m + 1][$m] + $this->H[$m][$m + 1];
+                    $q = $this->H[$m + 1][$m + 1] - $z - $r - $s;
+                    $r = $this->H[$m + 2][$m + 1];
+                    $s = abs($p) + abs($q) + abs($r);
+                    $p = $p / $s;
+                    $q = $q / $s;
+                    $r = $r / $s;
+                    if ($m == $l) {
+                        break;
+                    }
+                    if (abs($this->H[$m][$m - 1]) * (abs($q) + abs($r)) <
+                        $eps * (abs($p) * (abs($this->H[$m - 1][$m - 1]) + abs($z) + abs($this->H[$m + 1][$m + 1])))) {
+                        break;
+                    }
+                    --$m;
+                }
+                for ($i = $m + 2; $i <= $n; ++$i) {
+                    $this->H[$i][$i - 2] = 0.0;
+                    if ($i > $m + 2) {
+                        $this->H[$i][$i - 3] = 0.0;
+                    }
+                }
+                // Double QR step involving rows l:n and columns m:n
+                for ($k = $m; $k <= $n - 1; ++$k) {
+                    $notlast = ($k != $n - 1);
+                    if ($k != $m) {
+                        $p = $this->H[$k][$k - 1];
+                        $q = $this->H[$k + 1][$k - 1];
+                        $r = ($notlast ? $this->H[$k + 2][$k - 1] : 0.0);
+                        $x = abs($p) + abs($q) + abs($r);
+                        if ($x != 0.0) {
+                            $p = $p / $x;
+                            $q = $q / $x;
+                            $r = $r / $x;
+                        }
+                    }
+                    if ($x == 0.0) {
+                        break;
+                    }
+                    $s = sqrt($p * $p + $q * $q + $r * $r);
+                    if ($p < 0) {
+                        $s = -$s;
+                    }
+                    if ($s != 0) {
+                        if ($k != $m) {
+                            $this->H[$k][$k - 1] = -$s * $x;
+                        } elseif ($l != $m) {
+                            $this->H[$k][$k - 1] = -$this->H[$k][$k - 1];
+                        }
+                        $p = $p + $s;
+                        $x = $p / $s;
+                        $y = $q / $s;
+                        $z = $r / $s;
+                        $q = $q / $p;
+                        $r = $r / $p;
+                        // Row modification
+                        for ($j = $k; $j < $nn; ++$j) {
+                            $p = $this->H[$k][$j] + $q * $this->H[$k + 1][$j];
+                            if ($notlast) {
+                                $p = $p + $r * $this->H[$k + 2][$j];
+                                $this->H[$k + 2][$j] = $this->H[$k + 2][$j] - $p * $z;
+                            }
+                            $this->H[$k][$j] = $this->H[$k][$j] - $p * $x;
+                            $this->H[$k + 1][$j] = $this->H[$k + 1][$j] - $p * $y;
+                        }
+                        // Column modification
+                        $iMax = min($n, $k + 3);
+                        for ($i = 0; $i <= $iMax; ++$i) {
+                            $p = $x * $this->H[$i][$k] + $y * $this->H[$i][$k + 1];
+                            if ($notlast) {
+                                $p = $p + $z * $this->H[$i][$k + 2];
+                                $this->H[$i][$k + 2] = $this->H[$i][$k + 2] - $p * $r;
+                            }
+                            $this->H[$i][$k] = $this->H[$i][$k] - $p;
+                            $this->H[$i][$k + 1] = $this->H[$i][$k + 1] - $p * $q;
+                        }
+                        // Accumulate transformations
+                        for ($i = $low; $i <= $high; ++$i) {
+                            $p = $x * $this->V[$i][$k] + $y * $this->V[$i][$k + 1];
+                            if ($notlast) {
+                                $p = $p + $z * $this->V[$i][$k + 2];
+                                $this->V[$i][$k + 2] = $this->V[$i][$k + 2] - $p * $r;
+                            }
+                            $this->V[$i][$k] = $this->V[$i][$k] - $p;
+                            $this->V[$i][$k + 1] = $this->V[$i][$k + 1] - $p * $q;
+                        }
+                    }  // ($s != 0)
+                }  // k loop
+            }  // check convergence
+        }  // while ($n >= $low)
+
+        // Backsubstitute to find vectors of upper triangular form
+        if ($norm == 0.0) {
+            return;
+        }
+
+        for ($n = $nn - 1; $n >= 0; --$n) {
+            $p = $this->d[$n];
+            $q = $this->e[$n];
+            // Real vector
+            if ($q == 0) {
+                $l = $n;
+                $this->H[$n][$n] = 1.0;
+                for ($i = $n - 1; $i >= 0; --$i) {
+                    $w = $this->H[$i][$i] - $p;
+                    $r = 0.0;
+                    for ($j = $l; $j <= $n; ++$j) {
+                        $r = $r + $this->H[$i][$j] * $this->H[$j][$n];
+                    }
+                    if ($this->e[$i] < 0.0) {
+                        $z = $w;
+                        $s = $r;
+                    } else {
+                        $l = $i;
+                        if ($this->e[$i] == 0.0) {
+                            if ($w != 0.0) {
+                                $this->H[$i][$n] = -$r / $w;
+                            } else {
+                                $this->H[$i][$n] = -$r / ($eps * $norm);
+                            }
+                            // Solve real equations
+                        } else {
+                            $x = $this->H[$i][$i + 1];
+                            $y = $this->H[$i + 1][$i];
+                            $q = ($this->d[$i] - $p) * ($this->d[$i] - $p) + $this->e[$i] * $this->e[$i];
+                            $t = ($x * $s - $z * $r) / $q;
+                            $this->H[$i][$n] = $t;
+                            if (abs($x) > abs($z)) {
+                                $this->H[$i + 1][$n] = (-$r - $w * $t) / $x;
+                            } else {
+                                $this->H[$i + 1][$n] = (-$s - $y * $t) / $z;
+                            }
+                        }
+                        // Overflow control
+                        $t = abs($this->H[$i][$n]);
+                        if (($eps * $t) * $t > 1) {
+                            for ($j = $i; $j <= $n; ++$j) {
+                                $this->H[$j][$n] = $this->H[$j][$n] / $t;
+                            }
+                        }
+                    }
+                }
+                // Complex vector
+            } elseif ($q < 0) {
+                $l = $n - 1;
+                // Last vector component imaginary so matrix is triangular
+                if (abs($this->H[$n][$n - 1]) > abs($this->H[$n - 1][$n])) {
+                    $this->H[$n - 1][$n - 1] = $q / $this->H[$n][$n - 1];
+                    $this->H[$n - 1][$n] = -($this->H[$n][$n] - $p) / $this->H[$n][$n - 1];
+                } else {
+                    $this->cdiv(0.0, -$this->H[$n - 1][$n], $this->H[$n - 1][$n - 1] - $p, $q);
+                    $this->H[$n - 1][$n - 1] = $this->cdivr;
+                    $this->H[$n - 1][$n] = $this->cdivi;
+                }
+                $this->H[$n][$n - 1] = 0.0;
+                $this->H[$n][$n] = 1.0;
+                for ($i = $n - 2; $i >= 0; --$i) {
+                    // double ra,sa,vr,vi;
+                    $ra = 0.0;
+                    $sa = 0.0;
+                    for ($j = $l; $j <= $n; ++$j) {
+                        $ra = $ra + $this->H[$i][$j] * $this->H[$j][$n - 1];
+                        $sa = $sa + $this->H[$i][$j] * $this->H[$j][$n];
+                    }
+                    $w = $this->H[$i][$i] - $p;
+                    if ($this->e[$i] < 0.0) {
+                        $z = $w;
+                        $r = $ra;
+                        $s = $sa;
+                    } else {
+                        $l = $i;
+                        if ($this->e[$i] == 0) {
+                            $this->cdiv(-$ra, -$sa, $w, $q);
+                            $this->H[$i][$n - 1] = $this->cdivr;
+                            $this->H[$i][$n] = $this->cdivi;
+                        } else {
+                            // Solve complex equations
+                            $x = $this->H[$i][$i + 1];
+                            $y = $this->H[$i + 1][$i];
+                            $vr = ($this->d[$i] - $p) * ($this->d[$i] - $p) + $this->e[$i] * $this->e[$i] - $q * $q;
+                            $vi = ($this->d[$i] - $p) * 2.0 * $q;
+                            if ($vr == 0.0 & $vi == 0.0) {
+                                $vr = $eps * $norm * (abs($w) + abs($q) + abs($x) + abs($y) + abs($z));
+                            }
+                            $this->cdiv($x * $r - $z * $ra + $q * $sa, $x * $s - $z * $sa - $q * $ra, $vr, $vi);
+                            $this->H[$i][$n - 1] = $this->cdivr;
+                            $this->H[$i][$n] = $this->cdivi;
+                            if (abs($x) > (abs($z) + abs($q))) {
+                                $this->H[$i + 1][$n - 1] = (-$ra - $w * $this->H[$i][$n - 1] + $q * $this->H[$i][$n]) / $x;
+                                $this->H[$i + 1][$n] = (-$sa - $w * $this->H[$i][$n] - $q * $this->H[$i][$n - 1]) / $x;
+                            } else {
+                                $this->cdiv(-$r - $y * $this->H[$i][$n - 1], -$s - $y * $this->H[$i][$n], $z, $q);
+                                $this->H[$i + 1][$n - 1] = $this->cdivr;
+                                $this->H[$i + 1][$n] = $this->cdivi;
+                            }
+                        }
+                        // Overflow control
+                        $t = max(abs($this->H[$i][$n - 1]), abs($this->H[$i][$n]));
+                        if (($eps * $t) * $t > 1) {
+                            for ($j = $i; $j <= $n; ++$j) {
+                                $this->H[$j][$n - 1] = $this->H[$j][$n - 1] / $t;
+                                $this->H[$j][$n] = $this->H[$j][$n] / $t;
+                            }
+                        }
+                    } // end else
+                } // end for
+            } // end else for complex case
+        } // end for
+
+        // Vectors of isolated roots
+        for ($i = 0; $i < $nn; ++$i) {
+            if ($i < $low | $i > $high) {
+                for ($j = $i; $j < $nn; ++$j) {
+                    $this->V[$i][$j] = $this->H[$i][$j];
+                }
+            }
+        }
+
+        // Back transformation to get eigenvectors of original matrix
+        for ($j = $nn - 1; $j >= $low; --$j) {
+            for ($i = $low; $i <= $high; ++$i) {
+                $z = 0.0;
+                $kMax = min($j, $high);
+                for ($k = $low; $k <= $kMax; ++$k) {
+                    $z = $z + $this->V[$i][$k] * $this->H[$k][$j];
+                }
+                $this->V[$i][$j] = $z;
+            }
+        }
+    }
+
+    // end hqr2
+
+    /**
+     * Constructor: Check for symmetry, then construct the eigenvalue decomposition.
+     *
+     * @param mixed $Arg A Square matrix
+     */
+    public function __construct($Arg)
+    {
+        $this->A = $Arg->getArray();
+        $this->n = $Arg->getColumnDimension();
+
+        $issymmetric = true;
+        for ($j = 0; ($j < $this->n) & $issymmetric; ++$j) {
+            for ($i = 0; ($i < $this->n) & $issymmetric; ++$i) {
+                $issymmetric = ($this->A[$i][$j] == $this->A[$j][$i]);
+            }
+        }
+
+        if ($issymmetric) {
+            $this->V = $this->A;
+            // Tridiagonalize.
+            $this->tred2();
+            // Diagonalize.
+            $this->tql2();
+        } else {
+            $this->H = $this->A;
+            $this->ort = [];
+            // Reduce to Hessenberg form.
+            $this->orthes();
+            // Reduce Hessenberg to real Schur form.
+            $this->hqr2();
+        }
+    }
+
+    /**
+     * Return the eigenvector matrix.
+     *
+     * @return Matrix V
+     */
+    public function getV()
+    {
+        return new Matrix($this->V, $this->n, $this->n);
+    }
+
+    /**
+     * Return the real parts of the eigenvalues.
+     *
+     * @return array real(diag(D))
+     */
+    public function getRealEigenvalues()
+    {
+        return $this->d;
+    }
+
+    /**
+     * Return the imaginary parts of the eigenvalues.
+     *
+     * @return array imag(diag(D))
+     */
+    public function getImagEigenvalues()
+    {
+        return $this->e;
+    }
+
+    /**
+     * Return the block diagonal eigenvalue matrix.
+     *
+     * @return Matrix D
+     */
+    public function getD()
+    {
+        for ($i = 0; $i < $this->n; ++$i) {
+            $D[$i] = array_fill(0, $this->n, 0.0);
+            $D[$i][$i] = $this->d[$i];
+            if ($this->e[$i] == 0) {
+                continue;
+            }
+            $o = ($this->e[$i] > 0) ? $i + 1 : $i - 1;
+            $D[$i][$o] = $this->e[$i];
+        }
+
+        return new Matrix($D);
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/LUDecomposition.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/LUDecomposition.php
new file mode 100755
index 0000000..4aecff7
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/LUDecomposition.php
@@ -0,0 +1,282 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared\JAMA;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalculationException;
+
+/**
+ *    For an m-by-n matrix A with m >= n, the LU decomposition is an m-by-n
+ *    unit lower triangular matrix L, an n-by-n upper triangular matrix U,
+ *    and a permutation vector piv of length m so that A(piv,:) = L*U.
+ *    If m < n, then L is m-by-m and U is m-by-n.
+ *
+ *    The LU decompostion with pivoting always exists, even if the matrix is
+ *    singular, so the constructor will never fail. The primary use of the
+ *    LU decomposition is in the solution of square systems of simultaneous
+ *    linear equations. This will fail if isNonsingular() returns false.
+ *
+ *    @author Paul Meagher
+ *    @author Bartosz Matosiuk
+ *    @author Michael Bommarito
+ *
+ *    @version 1.1
+ */
+class LUDecomposition
+{
+    const MATRIX_SINGULAR_EXCEPTION = 'Can only perform operation on singular matrix.';
+    const MATRIX_SQUARE_EXCEPTION = 'Mismatched Row dimension';
+
+    /**
+     * Decomposition storage.
+     *
+     * @var array
+     */
+    private $LU = [];
+
+    /**
+     * Row dimension.
+     *
+     * @var int
+     */
+    private $m;
+
+    /**
+     * Column dimension.
+     *
+     * @var int
+     */
+    private $n;
+
+    /**
+     * Pivot sign.
+     *
+     * @var int
+     */
+    private $pivsign;
+
+    /**
+     * Internal storage of pivot vector.
+     *
+     * @var array
+     */
+    private $piv = [];
+
+    /**
+     * LU Decomposition constructor.
+     *
+     * @param Matrix $A Rectangular matrix
+     */
+    public function __construct($A)
+    {
+        if ($A instanceof Matrix) {
+            // Use a "left-looking", dot-product, Crout/Doolittle algorithm.
+            $this->LU = $A->getArray();
+            $this->m = $A->getRowDimension();
+            $this->n = $A->getColumnDimension();
+            for ($i = 0; $i < $this->m; ++$i) {
+                $this->piv[$i] = $i;
+            }
+            $this->pivsign = 1;
+            $LUrowi = $LUcolj = [];
+
+            // Outer loop.
+            for ($j = 0; $j < $this->n; ++$j) {
+                // Make a copy of the j-th column to localize references.
+                for ($i = 0; $i < $this->m; ++$i) {
+                    $LUcolj[$i] = &$this->LU[$i][$j];
+                }
+                // Apply previous transformations.
+                for ($i = 0; $i < $this->m; ++$i) {
+                    $LUrowi = $this->LU[$i];
+                    // Most of the time is spent in the following dot product.
+                    $kmax = min($i, $j);
+                    $s = 0.0;
+                    for ($k = 0; $k < $kmax; ++$k) {
+                        $s += $LUrowi[$k] * $LUcolj[$k];
+                    }
+                    $LUrowi[$j] = $LUcolj[$i] -= $s;
+                }
+                // Find pivot and exchange if necessary.
+                $p = $j;
+                for ($i = $j + 1; $i < $this->m; ++$i) {
+                    if (abs($LUcolj[$i]) > abs($LUcolj[$p])) {
+                        $p = $i;
+                    }
+                }
+                if ($p != $j) {
+                    for ($k = 0; $k < $this->n; ++$k) {
+                        $t = $this->LU[$p][$k];
+                        $this->LU[$p][$k] = $this->LU[$j][$k];
+                        $this->LU[$j][$k] = $t;
+                    }
+                    $k = $this->piv[$p];
+                    $this->piv[$p] = $this->piv[$j];
+                    $this->piv[$j] = $k;
+                    $this->pivsign = $this->pivsign * -1;
+                }
+                // Compute multipliers.
+                if (($j < $this->m) && ($this->LU[$j][$j] != 0.0)) {
+                    for ($i = $j + 1; $i < $this->m; ++$i) {
+                        $this->LU[$i][$j] /= $this->LU[$j][$j];
+                    }
+                }
+            }
+        } else {
+            throw new CalculationException(Matrix::ARGUMENT_TYPE_EXCEPTION);
+        }
+    }
+
+    //    function __construct()
+
+    /**
+     * Get lower triangular factor.
+     *
+     * @return Matrix Lower triangular factor
+     */
+    public function getL()
+    {
+        for ($i = 0; $i < $this->m; ++$i) {
+            for ($j = 0; $j < $this->n; ++$j) {
+                if ($i > $j) {
+                    $L[$i][$j] = $this->LU[$i][$j];
+                } elseif ($i == $j) {
+                    $L[$i][$j] = 1.0;
+                } else {
+                    $L[$i][$j] = 0.0;
+                }
+            }
+        }
+
+        return new Matrix($L);
+    }
+
+    //    function getL()
+
+    /**
+     * Get upper triangular factor.
+     *
+     * @return Matrix Upper triangular factor
+     */
+    public function getU()
+    {
+        for ($i = 0; $i < $this->n; ++$i) {
+            for ($j = 0; $j < $this->n; ++$j) {
+                if ($i <= $j) {
+                    $U[$i][$j] = $this->LU[$i][$j];
+                } else {
+                    $U[$i][$j] = 0.0;
+                }
+            }
+        }
+
+        return new Matrix($U);
+    }
+
+    //    function getU()
+
+    /**
+     * Return pivot permutation vector.
+     *
+     * @return array Pivot vector
+     */
+    public function getPivot()
+    {
+        return $this->piv;
+    }
+
+    //    function getPivot()
+
+    /**
+     * Alias for getPivot.
+     *
+     *    @see getPivot
+     */
+    public function getDoublePivot()
+    {
+        return $this->getPivot();
+    }
+
+    //    function getDoublePivot()
+
+    /**
+     *    Is the matrix nonsingular?
+     *
+     * @return bool true if U, and hence A, is nonsingular
+     */
+    public function isNonsingular()
+    {
+        for ($j = 0; $j < $this->n; ++$j) {
+            if ($this->LU[$j][$j] == 0) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    //    function isNonsingular()
+
+    /**
+     * Count determinants.
+     *
+     * @return array d matrix deterninat
+     */
+    public function det()
+    {
+        if ($this->m == $this->n) {
+            $d = $this->pivsign;
+            for ($j = 0; $j < $this->n; ++$j) {
+                $d *= $this->LU[$j][$j];
+            }
+
+            return $d;
+        }
+
+        throw new CalculationException(Matrix::MATRIX_DIMENSION_EXCEPTION);
+    }
+
+    //    function det()
+
+    /**
+     * Solve A*X = B.
+     *
+     * @param mixed $B a Matrix with as many rows as A and any number of columns
+     *
+     * @return Matrix X so that L*U*X = B(piv,:)
+     */
+    public function solve($B)
+    {
+        if ($B->getRowDimension() == $this->m) {
+            if ($this->isNonsingular()) {
+                // Copy right hand side with pivoting
+                $nx = $B->getColumnDimension();
+                $X = $B->getMatrix($this->piv, 0, $nx - 1);
+                // Solve L*Y = B(piv,:)
+                for ($k = 0; $k < $this->n; ++$k) {
+                    for ($i = $k + 1; $i < $this->n; ++$i) {
+                        for ($j = 0; $j < $nx; ++$j) {
+                            $X->A[$i][$j] -= $X->A[$k][$j] * $this->LU[$i][$k];
+                        }
+                    }
+                }
+                // Solve U*X = Y;
+                for ($k = $this->n - 1; $k >= 0; --$k) {
+                    for ($j = 0; $j < $nx; ++$j) {
+                        $X->A[$k][$j] /= $this->LU[$k][$k];
+                    }
+                    for ($i = 0; $i < $k; ++$i) {
+                        for ($j = 0; $j < $nx; ++$j) {
+                            $X->A[$i][$j] -= $X->A[$k][$j] * $this->LU[$i][$k];
+                        }
+                    }
+                }
+
+                return $X;
+            }
+
+            throw new CalculationException(self::MATRIX_SINGULAR_EXCEPTION);
+        }
+
+        throw new CalculationException(self::MATRIX_SQUARE_EXCEPTION);
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/Matrix.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/Matrix.php
new file mode 100755
index 0000000..a5cb6de
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/Matrix.php
@@ -0,0 +1,1202 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared\JAMA;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalculationException;
+use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+
+/**
+ * Matrix class.
+ *
+ * @author Paul Meagher
+ * @author Michael Bommarito
+ * @author Lukasz Karapuda
+ * @author Bartek Matosiuk
+ *
+ * @version 1.8
+ *
+ * @see https://math.nist.gov/javanumerics/jama/
+ */
+class Matrix
+{
+    const POLYMORPHIC_ARGUMENT_EXCEPTION = 'Invalid argument pattern for polymorphic function.';
+    const ARGUMENT_TYPE_EXCEPTION = 'Invalid argument type.';
+    const ARGUMENT_BOUNDS_EXCEPTION = 'Invalid argument range.';
+    const MATRIX_DIMENSION_EXCEPTION = 'Matrix dimensions are not equal.';
+    const ARRAY_LENGTH_EXCEPTION = 'Array length must be a multiple of m.';
+    const MATRIX_SPD_EXCEPTION = 'Can only perform operation on symmetric positive definite matrix.';
+
+    /**
+     * Matrix storage.
+     *
+     * @var array
+     */
+    public $A = [];
+
+    /**
+     * Matrix row dimension.
+     *
+     * @var int
+     */
+    private $m;
+
+    /**
+     * Matrix column dimension.
+     *
+     * @var int
+     */
+    private $n;
+
+    /**
+     * Polymorphic constructor.
+     *
+     * As PHP has no support for polymorphic constructors, we use tricks to make our own sort of polymorphism using func_num_args, func_get_arg, and gettype. In essence, we're just implementing a simple RTTI filter and calling the appropriate constructor.
+     */
+    public function __construct(...$args)
+    {
+        if (count($args) > 0) {
+            $match = implode(',', array_map('gettype', $args));
+
+            switch ($match) {
+                //Rectangular matrix - m x n initialized from 2D array
+                case 'array':
+                    $this->m = count($args[0]);
+                    $this->n = count($args[0][0]);
+                    $this->A = $args[0];
+
+                    break;
+                //Square matrix - n x n
+                case 'integer':
+                    $this->m = $args[0];
+                    $this->n = $args[0];
+                    $this->A = array_fill(0, $this->m, array_fill(0, $this->n, 0));
+
+                    break;
+                //Rectangular matrix - m x n
+                case 'integer,integer':
+                    $this->m = $args[0];
+                    $this->n = $args[1];
+                    $this->A = array_fill(0, $this->m, array_fill(0, $this->n, 0));
+
+                    break;
+                //Rectangular matrix - m x n initialized from packed array
+                case 'array,integer':
+                    $this->m = $args[1];
+                    if ($this->m != 0) {
+                        $this->n = count($args[0]) / $this->m;
+                    } else {
+                        $this->n = 0;
+                    }
+                    if (($this->m * $this->n) == count($args[0])) {
+                        for ($i = 0; $i < $this->m; ++$i) {
+                            for ($j = 0; $j < $this->n; ++$j) {
+                                $this->A[$i][$j] = $args[0][$i + $j * $this->m];
+                            }
+                        }
+                    } else {
+                        throw new CalculationException(self::ARRAY_LENGTH_EXCEPTION);
+                    }
+
+                    break;
+                default:
+                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+
+                    break;
+            }
+        } else {
+            throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+        }
+    }
+
+    /**
+     * getArray.
+     *
+     * @return array Matrix array
+     */
+    public function getArray()
+    {
+        return $this->A;
+    }
+
+    /**
+     * getRowDimension.
+     *
+     * @return int Row dimension
+     */
+    public function getRowDimension()
+    {
+        return $this->m;
+    }
+
+    /**
+     * getColumnDimension.
+     *
+     * @return int Column dimension
+     */
+    public function getColumnDimension()
+    {
+        return $this->n;
+    }
+
+    /**
+     * get.
+     *
+     * Get the i,j-th element of the matrix.
+     *
+     * @param int $i Row position
+     * @param int $j Column position
+     *
+     * @return mixed Element (int/float/double)
+     */
+    public function get($i = null, $j = null)
+    {
+        return $this->A[$i][$j];
+    }
+
+    /**
+     * getMatrix.
+     *
+     *    Get a submatrix
+     *
+     * @return Matrix Submatrix
+     */
+    public function getMatrix(...$args)
+    {
+        if (count($args) > 0) {
+            $match = implode(',', array_map('gettype', $args));
+
+            switch ($match) {
+                //A($i0...; $j0...)
+                case 'integer,integer':
+                    [$i0, $j0] = $args;
+                    if ($i0 >= 0) {
+                        $m = $this->m - $i0;
+                    } else {
+                        throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
+                    }
+                    if ($j0 >= 0) {
+                        $n = $this->n - $j0;
+                    } else {
+                        throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
+                    }
+                    $R = new self($m, $n);
+                    for ($i = $i0; $i < $this->m; ++$i) {
+                        for ($j = $j0; $j < $this->n; ++$j) {
+                            $R->set($i, $j, $this->A[$i][$j]);
+                        }
+                    }
+
+                    return $R;
+
+                    break;
+                //A($i0...$iF; $j0...$jF)
+                case 'integer,integer,integer,integer':
+                    [$i0, $iF, $j0, $jF] = $args;
+                    if (($iF > $i0) && ($this->m >= $iF) && ($i0 >= 0)) {
+                        $m = $iF - $i0;
+                    } else {
+                        throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
+                    }
+                    if (($jF > $j0) && ($this->n >= $jF) && ($j0 >= 0)) {
+                        $n = $jF - $j0;
+                    } else {
+                        throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
+                    }
+                    $R = new self($m + 1, $n + 1);
+                    for ($i = $i0; $i <= $iF; ++$i) {
+                        for ($j = $j0; $j <= $jF; ++$j) {
+                            $R->set($i - $i0, $j - $j0, $this->A[$i][$j]);
+                        }
+                    }
+
+                    return $R;
+
+                    break;
+                //$R = array of row indices; $C = array of column indices
+                case 'array,array':
+                    [$RL, $CL] = $args;
+                    if (count($RL) > 0) {
+                        $m = count($RL);
+                    } else {
+                        throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
+                    }
+                    if (count($CL) > 0) {
+                        $n = count($CL);
+                    } else {
+                        throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
+                    }
+                    $R = new self($m, $n);
+                    for ($i = 0; $i < $m; ++$i) {
+                        for ($j = 0; $j < $n; ++$j) {
+                            $R->set($i, $j, $this->A[$RL[$i]][$CL[$j]]);
+                        }
+                    }
+
+                    return $R;
+
+                    break;
+                //A($i0...$iF); $CL = array of column indices
+                case 'integer,integer,array':
+                    [$i0, $iF, $CL] = $args;
+                    if (($iF > $i0) && ($this->m >= $iF) && ($i0 >= 0)) {
+                        $m = $iF - $i0;
+                    } else {
+                        throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
+                    }
+                    if (count($CL) > 0) {
+                        $n = count($CL);
+                    } else {
+                        throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
+                    }
+                    $R = new self($m, $n);
+                    for ($i = $i0; $i < $iF; ++$i) {
+                        for ($j = 0; $j < $n; ++$j) {
+                            $R->set($i - $i0, $j, $this->A[$i][$CL[$j]]);
+                        }
+                    }
+
+                    return $R;
+
+                    break;
+                //$RL = array of row indices
+                case 'array,integer,integer':
+                    [$RL, $j0, $jF] = $args;
+                    if (count($RL) > 0) {
+                        $m = count($RL);
+                    } else {
+                        throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
+                    }
+                    if (($jF >= $j0) && ($this->n >= $jF) && ($j0 >= 0)) {
+                        $n = $jF - $j0;
+                    } else {
+                        throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
+                    }
+                    $R = new self($m, $n + 1);
+                    for ($i = 0; $i < $m; ++$i) {
+                        for ($j = $j0; $j <= $jF; ++$j) {
+                            $R->set($i, $j - $j0, $this->A[$RL[$i]][$j]);
+                        }
+                    }
+
+                    return $R;
+
+                    break;
+                default:
+                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+
+                    break;
+            }
+        } else {
+            throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+        }
+    }
+
+    /**
+     * checkMatrixDimensions.
+     *
+     *    Is matrix B the same size?
+     *
+     * @param Matrix $B Matrix B
+     *
+     * @return bool
+     */
+    public function checkMatrixDimensions($B = null)
+    {
+        if ($B instanceof self) {
+            if (($this->m == $B->getRowDimension()) && ($this->n == $B->getColumnDimension())) {
+                return true;
+            }
+
+            throw new CalculationException(self::MATRIX_DIMENSION_EXCEPTION);
+        }
+
+        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
+    }
+
+    //    function checkMatrixDimensions()
+
+    /**
+     * set.
+     *
+     * Set the i,j-th element of the matrix.
+     *
+     * @param int $i Row position
+     * @param int $j Column position
+     * @param mixed $c Int/float/double value
+     *
+     * @return mixed Element (int/float/double)
+     */
+    public function set($i = null, $j = null, $c = null)
+    {
+        // Optimized set version just has this
+        $this->A[$i][$j] = $c;
+    }
+
+    //    function set()
+
+    /**
+     * identity.
+     *
+     * Generate an identity matrix.
+     *
+     * @param int $m Row dimension
+     * @param int $n Column dimension
+     *
+     * @return Matrix Identity matrix
+     */
+    public function identity($m = null, $n = null)
+    {
+        return $this->diagonal($m, $n, 1);
+    }
+
+    /**
+     * diagonal.
+     *
+     *    Generate a diagonal matrix
+     *
+     * @param int $m Row dimension
+     * @param int $n Column dimension
+     * @param mixed $c Diagonal value
+     *
+     * @return Matrix Diagonal matrix
+     */
+    public function diagonal($m = null, $n = null, $c = 1)
+    {
+        $R = new self($m, $n);
+        for ($i = 0; $i < $m; ++$i) {
+            $R->set($i, $i, $c);
+        }
+
+        return $R;
+    }
+
+    /**
+     * getMatrixByRow.
+     *
+     *    Get a submatrix by row index/range
+     *
+     * @param int $i0 Initial row index
+     * @param int $iF Final row index
+     *
+     * @return Matrix Submatrix
+     */
+    public function getMatrixByRow($i0 = null, $iF = null)
+    {
+        if (is_int($i0)) {
+            if (is_int($iF)) {
+                return $this->getMatrix($i0, 0, $iF + 1, $this->n);
+            }
+
+            return $this->getMatrix($i0, 0, $i0 + 1, $this->n);
+        }
+
+        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
+    }
+
+    /**
+     * getMatrixByCol.
+     *
+     *    Get a submatrix by column index/range
+     *
+     * @param int $j0 Initial column index
+     * @param int $jF Final column index
+     *
+     * @return Matrix Submatrix
+     */
+    public function getMatrixByCol($j0 = null, $jF = null)
+    {
+        if (is_int($j0)) {
+            if (is_int($jF)) {
+                return $this->getMatrix(0, $j0, $this->m, $jF + 1);
+            }
+
+            return $this->getMatrix(0, $j0, $this->m, $j0 + 1);
+        }
+
+        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
+    }
+
+    /**
+     * transpose.
+     *
+     *    Tranpose matrix
+     *
+     * @return Matrix Transposed matrix
+     */
+    public function transpose()
+    {
+        $R = new self($this->n, $this->m);
+        for ($i = 0; $i < $this->m; ++$i) {
+            for ($j = 0; $j < $this->n; ++$j) {
+                $R->set($j, $i, $this->A[$i][$j]);
+            }
+        }
+
+        return $R;
+    }
+
+    //    function transpose()
+
+    /**
+     * trace.
+     *
+     *    Sum of diagonal elements
+     *
+     * @return float Sum of diagonal elements
+     */
+    public function trace()
+    {
+        $s = 0;
+        $n = min($this->m, $this->n);
+        for ($i = 0; $i < $n; ++$i) {
+            $s += $this->A[$i][$i];
+        }
+
+        return $s;
+    }
+
+    /**
+     * uminus.
+     *
+     *    Unary minus matrix -A
+     *
+     * @return Matrix Unary minus matrix
+     */
+    public function uminus()
+    {
+    }
+
+    /**
+     * plus.
+     *
+     *    A + B
+     *
+     * @return Matrix Sum
+     */
+    public function plus(...$args)
+    {
+        if (count($args) > 0) {
+            $match = implode(',', array_map('gettype', $args));
+
+            switch ($match) {
+                case 'object':
+                    if ($args[0] instanceof self) {
+                        $M = $args[0];
+                    } else {
+                        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
+                    }
+
+                    break;
+                case 'array':
+                    $M = new self($args[0]);
+
+                    break;
+                default:
+                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+
+                    break;
+            }
+            $this->checkMatrixDimensions($M);
+            for ($i = 0; $i < $this->m; ++$i) {
+                for ($j = 0; $j < $this->n; ++$j) {
+                    $M->set($i, $j, $M->get($i, $j) + $this->A[$i][$j]);
+                }
+            }
+
+            return $M;
+        }
+
+        throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+    }
+
+    /**
+     * plusEquals.
+     *
+     *    A = A + B
+     *
+     * @return $this
+     */
+    public function plusEquals(...$args)
+    {
+        if (count($args) > 0) {
+            $match = implode(',', array_map('gettype', $args));
+
+            switch ($match) {
+                case 'object':
+                    if ($args[0] instanceof self) {
+                        $M = $args[0];
+                    } else {
+                        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
+                    }
+
+                    break;
+                case 'array':
+                    $M = new self($args[0]);
+
+                    break;
+                default:
+                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+
+                    break;
+            }
+            $this->checkMatrixDimensions($M);
+            for ($i = 0; $i < $this->m; ++$i) {
+                for ($j = 0; $j < $this->n; ++$j) {
+                    $validValues = true;
+                    $value = $M->get($i, $j);
+                    if ((is_string($this->A[$i][$j])) && (strlen($this->A[$i][$j]) > 0) && (!is_numeric($this->A[$i][$j]))) {
+                        $this->A[$i][$j] = trim($this->A[$i][$j], '"');
+                        $validValues &= StringHelper::convertToNumberIfFraction($this->A[$i][$j]);
+                    }
+                    if ((is_string($value)) && (strlen($value) > 0) && (!is_numeric($value))) {
+                        $value = trim($value, '"');
+                        $validValues &= StringHelper::convertToNumberIfFraction($value);
+                    }
+                    if ($validValues) {
+                        $this->A[$i][$j] += $value;
+                    } else {
+                        $this->A[$i][$j] = Functions::NAN();
+                    }
+                }
+            }
+
+            return $this;
+        }
+
+        throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+    }
+
+    /**
+     * minus.
+     *
+     *    A - B
+     *
+     * @return Matrix Sum
+     */
+    public function minus(...$args)
+    {
+        if (count($args) > 0) {
+            $match = implode(',', array_map('gettype', $args));
+
+            switch ($match) {
+                case 'object':
+                    if ($args[0] instanceof self) {
+                        $M = $args[0];
+                    } else {
+                        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
+                    }
+
+                    break;
+                case 'array':
+                    $M = new self($args[0]);
+
+                    break;
+                default:
+                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+
+                    break;
+            }
+            $this->checkMatrixDimensions($M);
+            for ($i = 0; $i < $this->m; ++$i) {
+                for ($j = 0; $j < $this->n; ++$j) {
+                    $M->set($i, $j, $M->get($i, $j) - $this->A[$i][$j]);
+                }
+            }
+
+            return $M;
+        }
+
+        throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+    }
+
+    /**
+     * minusEquals.
+     *
+     *    A = A - B
+     *
+     * @return $this
+     */
+    public function minusEquals(...$args)
+    {
+        if (count($args) > 0) {
+            $match = implode(',', array_map('gettype', $args));
+
+            switch ($match) {
+                case 'object':
+                    if ($args[0] instanceof self) {
+                        $M = $args[0];
+                    } else {
+                        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
+                    }
+
+                    break;
+                case 'array':
+                    $M = new self($args[0]);
+
+                    break;
+                default:
+                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+
+                    break;
+            }
+            $this->checkMatrixDimensions($M);
+            for ($i = 0; $i < $this->m; ++$i) {
+                for ($j = 0; $j < $this->n; ++$j) {
+                    $validValues = true;
+                    $value = $M->get($i, $j);
+                    if ((is_string($this->A[$i][$j])) && (strlen($this->A[$i][$j]) > 0) && (!is_numeric($this->A[$i][$j]))) {
+                        $this->A[$i][$j] = trim($this->A[$i][$j], '"');
+                        $validValues &= StringHelper::convertToNumberIfFraction($this->A[$i][$j]);
+                    }
+                    if ((is_string($value)) && (strlen($value) > 0) && (!is_numeric($value))) {
+                        $value = trim($value, '"');
+                        $validValues &= StringHelper::convertToNumberIfFraction($value);
+                    }
+                    if ($validValues) {
+                        $this->A[$i][$j] -= $value;
+                    } else {
+                        $this->A[$i][$j] = Functions::NAN();
+                    }
+                }
+            }
+
+            return $this;
+        }
+
+        throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+    }
+
+    /**
+     * arrayTimes.
+     *
+     *    Element-by-element multiplication
+     *    Cij = Aij * Bij
+     *
+     * @return Matrix Matrix Cij
+     */
+    public function arrayTimes(...$args)
+    {
+        if (count($args) > 0) {
+            $match = implode(',', array_map('gettype', $args));
+
+            switch ($match) {
+                case 'object':
+                    if ($args[0] instanceof self) {
+                        $M = $args[0];
+                    } else {
+                        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
+                    }
+
+                    break;
+                case 'array':
+                    $M = new self($args[0]);
+
+                    break;
+                default:
+                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+
+                    break;
+            }
+            $this->checkMatrixDimensions($M);
+            for ($i = 0; $i < $this->m; ++$i) {
+                for ($j = 0; $j < $this->n; ++$j) {
+                    $M->set($i, $j, $M->get($i, $j) * $this->A[$i][$j]);
+                }
+            }
+
+            return $M;
+        }
+
+        throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+    }
+
+    /**
+     * arrayTimesEquals.
+     *
+     *    Element-by-element multiplication
+     *    Aij = Aij * Bij
+     *
+     * @return $this
+     */
+    public function arrayTimesEquals(...$args)
+    {
+        if (count($args) > 0) {
+            $match = implode(',', array_map('gettype', $args));
+
+            switch ($match) {
+                case 'object':
+                    if ($args[0] instanceof self) {
+                        $M = $args[0];
+                    } else {
+                        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
+                    }
+
+                    break;
+                case 'array':
+                    $M = new self($args[0]);
+
+                    break;
+                default:
+                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+
+                    break;
+            }
+            $this->checkMatrixDimensions($M);
+            for ($i = 0; $i < $this->m; ++$i) {
+                for ($j = 0; $j < $this->n; ++$j) {
+                    $validValues = true;
+                    $value = $M->get($i, $j);
+                    if ((is_string($this->A[$i][$j])) && (strlen($this->A[$i][$j]) > 0) && (!is_numeric($this->A[$i][$j]))) {
+                        $this->A[$i][$j] = trim($this->A[$i][$j], '"');
+                        $validValues &= StringHelper::convertToNumberIfFraction($this->A[$i][$j]);
+                    }
+                    if ((is_string($value)) && (strlen($value) > 0) && (!is_numeric($value))) {
+                        $value = trim($value, '"');
+                        $validValues &= StringHelper::convertToNumberIfFraction($value);
+                    }
+                    if ($validValues) {
+                        $this->A[$i][$j] *= $value;
+                    } else {
+                        $this->A[$i][$j] = Functions::NAN();
+                    }
+                }
+            }
+
+            return $this;
+        }
+
+        throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+    }
+
+    /**
+     * arrayRightDivide.
+     *
+     *    Element-by-element right division
+     *    A / B
+     *
+     * @return Matrix Division result
+     */
+    public function arrayRightDivide(...$args)
+    {
+        if (count($args) > 0) {
+            $match = implode(',', array_map('gettype', $args));
+
+            switch ($match) {
+                case 'object':
+                    if ($args[0] instanceof self) {
+                        $M = $args[0];
+                    } else {
+                        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
+                    }
+
+                    break;
+                case 'array':
+                    $M = new self($args[0]);
+
+                    break;
+                default:
+                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+
+                    break;
+            }
+            $this->checkMatrixDimensions($M);
+            for ($i = 0; $i < $this->m; ++$i) {
+                for ($j = 0; $j < $this->n; ++$j) {
+                    $validValues = true;
+                    $value = $M->get($i, $j);
+                    if ((is_string($this->A[$i][$j])) && (strlen($this->A[$i][$j]) > 0) && (!is_numeric($this->A[$i][$j]))) {
+                        $this->A[$i][$j] = trim($this->A[$i][$j], '"');
+                        $validValues &= StringHelper::convertToNumberIfFraction($this->A[$i][$j]);
+                    }
+                    if ((is_string($value)) && (strlen($value) > 0) && (!is_numeric($value))) {
+                        $value = trim($value, '"');
+                        $validValues &= StringHelper::convertToNumberIfFraction($value);
+                    }
+                    if ($validValues) {
+                        if ($value == 0) {
+                            //    Trap for Divide by Zero error
+                            $M->set($i, $j, '#DIV/0!');
+                        } else {
+                            $M->set($i, $j, $this->A[$i][$j] / $value);
+                        }
+                    } else {
+                        $M->set($i, $j, Functions::NAN());
+                    }
+                }
+            }
+
+            return $M;
+        }
+
+        throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+    }
+
+    /**
+     * arrayRightDivideEquals.
+     *
+     *    Element-by-element right division
+     *    Aij = Aij / Bij
+     *
+     * @return Matrix Matrix Aij
+     */
+    public function arrayRightDivideEquals(...$args)
+    {
+        if (count($args) > 0) {
+            $match = implode(',', array_map('gettype', $args));
+
+            switch ($match) {
+                case 'object':
+                    if ($args[0] instanceof self) {
+                        $M = $args[0];
+                    } else {
+                        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
+                    }
+
+                    break;
+                case 'array':
+                    $M = new self($args[0]);
+
+                    break;
+                default:
+                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+
+                    break;
+            }
+            $this->checkMatrixDimensions($M);
+            for ($i = 0; $i < $this->m; ++$i) {
+                for ($j = 0; $j < $this->n; ++$j) {
+                    $this->A[$i][$j] = $this->A[$i][$j] / $M->get($i, $j);
+                }
+            }
+
+            return $M;
+        }
+
+        throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+    }
+
+    /**
+     * arrayLeftDivide.
+     *
+     *    Element-by-element Left division
+     *    A / B
+     *
+     * @return Matrix Division result
+     */
+    public function arrayLeftDivide(...$args)
+    {
+        if (count($args) > 0) {
+            $match = implode(',', array_map('gettype', $args));
+
+            switch ($match) {
+                case 'object':
+                    if ($args[0] instanceof self) {
+                        $M = $args[0];
+                    } else {
+                        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
+                    }
+
+                    break;
+                case 'array':
+                    $M = new self($args[0]);
+
+                    break;
+                default:
+                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+
+                    break;
+            }
+            $this->checkMatrixDimensions($M);
+            for ($i = 0; $i < $this->m; ++$i) {
+                for ($j = 0; $j < $this->n; ++$j) {
+                    $M->set($i, $j, $M->get($i, $j) / $this->A[$i][$j]);
+                }
+            }
+
+            return $M;
+        }
+
+        throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+    }
+
+    /**
+     * arrayLeftDivideEquals.
+     *
+     *    Element-by-element Left division
+     *    Aij = Aij / Bij
+     *
+     * @return Matrix Matrix Aij
+     */
+    public function arrayLeftDivideEquals(...$args)
+    {
+        if (count($args) > 0) {
+            $match = implode(',', array_map('gettype', $args));
+
+            switch ($match) {
+                case 'object':
+                    if ($args[0] instanceof self) {
+                        $M = $args[0];
+                    } else {
+                        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
+                    }
+
+                    break;
+                case 'array':
+                    $M = new self($args[0]);
+
+                    break;
+                default:
+                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+
+                    break;
+            }
+            $this->checkMatrixDimensions($M);
+            for ($i = 0; $i < $this->m; ++$i) {
+                for ($j = 0; $j < $this->n; ++$j) {
+                    $this->A[$i][$j] = $M->get($i, $j) / $this->A[$i][$j];
+                }
+            }
+
+            return $M;
+        }
+
+        throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+    }
+
+    /**
+     * times.
+     *
+     *    Matrix multiplication
+     *
+     * @return Matrix Product
+     */
+    public function times(...$args)
+    {
+        if (count($args) > 0) {
+            $match = implode(',', array_map('gettype', $args));
+
+            switch ($match) {
+                case 'object':
+                    if ($args[0] instanceof self) {
+                        $B = $args[0];
+                    } else {
+                        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
+                    }
+                    if ($this->n == $B->m) {
+                        $C = new self($this->m, $B->n);
+                        for ($j = 0; $j < $B->n; ++$j) {
+                            $Bcolj = [];
+                            for ($k = 0; $k < $this->n; ++$k) {
+                                $Bcolj[$k] = $B->A[$k][$j];
+                            }
+                            for ($i = 0; $i < $this->m; ++$i) {
+                                $Arowi = $this->A[$i];
+                                $s = 0;
+                                for ($k = 0; $k < $this->n; ++$k) {
+                                    $s += $Arowi[$k] * $Bcolj[$k];
+                                }
+                                $C->A[$i][$j] = $s;
+                            }
+                        }
+
+                        return $C;
+                    }
+
+                    throw new CalculationException(self::MATRIX_DIMENSION_EXCEPTION);
+                case 'array':
+                    $B = new self($args[0]);
+                    if ($this->n == $B->m) {
+                        $C = new self($this->m, $B->n);
+                        for ($i = 0; $i < $C->m; ++$i) {
+                            for ($j = 0; $j < $C->n; ++$j) {
+                                $s = '0';
+                                for ($k = 0; $k < $C->n; ++$k) {
+                                    $s += $this->A[$i][$k] * $B->A[$k][$j];
+                                }
+                                $C->A[$i][$j] = $s;
+                            }
+                        }
+
+                        return $C;
+                    }
+
+                    throw new CalculationException(self::MATRIX_DIMENSION_EXCEPTION);
+                case 'integer':
+                    $C = new self($this->A);
+                    for ($i = 0; $i < $C->m; ++$i) {
+                        for ($j = 0; $j < $C->n; ++$j) {
+                            $C->A[$i][$j] *= $args[0];
+                        }
+                    }
+
+                    return $C;
+                case 'double':
+                    $C = new self($this->m, $this->n);
+                    for ($i = 0; $i < $C->m; ++$i) {
+                        for ($j = 0; $j < $C->n; ++$j) {
+                            $C->A[$i][$j] = $args[0] * $this->A[$i][$j];
+                        }
+                    }
+
+                    return $C;
+                case 'float':
+                    $C = new self($this->A);
+                    for ($i = 0; $i < $C->m; ++$i) {
+                        for ($j = 0; $j < $C->n; ++$j) {
+                            $C->A[$i][$j] *= $args[0];
+                        }
+                    }
+
+                    return $C;
+                default:
+                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+            }
+        } else {
+            throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+        }
+    }
+
+    /**
+     * power.
+     *
+     *    A = A ^ B
+     *
+     * @return $this
+     */
+    public function power(...$args)
+    {
+        if (count($args) > 0) {
+            $match = implode(',', array_map('gettype', $args));
+
+            switch ($match) {
+                case 'object':
+                    if ($args[0] instanceof self) {
+                        $M = $args[0];
+                    } else {
+                        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
+                    }
+
+                    break;
+                case 'array':
+                    $M = new self($args[0]);
+
+                    break;
+                default:
+                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+
+                    break;
+            }
+            $this->checkMatrixDimensions($M);
+            for ($i = 0; $i < $this->m; ++$i) {
+                for ($j = 0; $j < $this->n; ++$j) {
+                    $validValues = true;
+                    $value = $M->get($i, $j);
+                    if ((is_string($this->A[$i][$j])) && (strlen($this->A[$i][$j]) > 0) && (!is_numeric($this->A[$i][$j]))) {
+                        $this->A[$i][$j] = trim($this->A[$i][$j], '"');
+                        $validValues &= StringHelper::convertToNumberIfFraction($this->A[$i][$j]);
+                    }
+                    if ((is_string($value)) && (strlen($value) > 0) && (!is_numeric($value))) {
+                        $value = trim($value, '"');
+                        $validValues &= StringHelper::convertToNumberIfFraction($value);
+                    }
+                    if ($validValues) {
+                        $this->A[$i][$j] = $this->A[$i][$j] ** $value;
+                    } else {
+                        $this->A[$i][$j] = Functions::NAN();
+                    }
+                }
+            }
+
+            return $this;
+        }
+
+        throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+    }
+
+    /**
+     * concat.
+     *
+     *    A = A & B
+     *
+     * @return $this
+     */
+    public function concat(...$args)
+    {
+        if (count($args) > 0) {
+            $match = implode(',', array_map('gettype', $args));
+
+            switch ($match) {
+                case 'object':
+                    if ($args[0] instanceof self) {
+                        $M = $args[0];
+                    } else {
+                        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
+                    }
+
+                    break;
+                case 'array':
+                    $M = new self($args[0]);
+
+                    break;
+                default:
+                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+
+                    break;
+            }
+            $this->checkMatrixDimensions($M);
+            for ($i = 0; $i < $this->m; ++$i) {
+                for ($j = 0; $j < $this->n; ++$j) {
+                    $this->A[$i][$j] = trim($this->A[$i][$j], '"') . trim($M->get($i, $j), '"');
+                }
+            }
+
+            return $this;
+        }
+
+        throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
+    }
+
+    /**
+     * Solve A*X = B.
+     *
+     * @param Matrix $B Right hand side
+     *
+     * @return Matrix ... Solution if A is square, least squares solution otherwise
+     */
+    public function solve($B)
+    {
+        if ($this->m == $this->n) {
+            $LU = new LUDecomposition($this);
+
+            return $LU->solve($B);
+        }
+        $QR = new QRDecomposition($this);
+
+        return $QR->solve($B);
+    }
+
+    /**
+     * Matrix inverse or pseudoinverse.
+     *
+     * @return Matrix ... Inverse(A) if A is square, pseudoinverse otherwise.
+     */
+    public function inverse()
+    {
+        return $this->solve($this->identity($this->m, $this->m));
+    }
+
+    /**
+     * det.
+     *
+     *    Calculate determinant
+     *
+     * @return float Determinant
+     */
+    public function det()
+    {
+        $L = new LUDecomposition($this);
+
+        return $L->det();
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/QRDecomposition.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/QRDecomposition.php
new file mode 100755
index 0000000..3bb8a10
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/QRDecomposition.php
@@ -0,0 +1,249 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared\JAMA;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalculationException;
+
+/**
+ *    For an m-by-n matrix A with m >= n, the QR decomposition is an m-by-n
+ *    orthogonal matrix Q and an n-by-n upper triangular matrix R so that
+ *    A = Q*R.
+ *
+ *    The QR decompostion always exists, even if the matrix does not have
+ *    full rank, so the constructor will never fail.  The primary use of the
+ *    QR decomposition is in the least squares solution of nonsquare systems
+ *    of simultaneous linear equations.  This will fail if isFullRank()
+ *    returns false.
+ *
+ *    @author  Paul Meagher
+ *
+ *    @version 1.1
+ */
+class QRDecomposition
+{
+    const MATRIX_RANK_EXCEPTION = 'Can only perform operation on full-rank matrix.';
+
+    /**
+     * Array for internal storage of decomposition.
+     *
+     * @var array
+     */
+    private $QR = [];
+
+    /**
+     * Row dimension.
+     *
+     * @var int
+     */
+    private $m;
+
+    /**
+     * Column dimension.
+     *
+     * @var int
+     */
+    private $n;
+
+    /**
+     * Array for internal storage of diagonal of R.
+     *
+     * @var array
+     */
+    private $Rdiag = [];
+
+    /**
+     * QR Decomposition computed by Householder reflections.
+     *
+     * @param matrix $A Rectangular matrix
+     */
+    public function __construct($A)
+    {
+        if ($A instanceof Matrix) {
+            // Initialize.
+            $this->QR = $A->getArray();
+            $this->m = $A->getRowDimension();
+            $this->n = $A->getColumnDimension();
+            // Main loop.
+            for ($k = 0; $k < $this->n; ++$k) {
+                // Compute 2-norm of k-th column without under/overflow.
+                $nrm = 0.0;
+                for ($i = $k; $i < $this->m; ++$i) {
+                    $nrm = hypo($nrm, $this->QR[$i][$k]);
+                }
+                if ($nrm != 0.0) {
+                    // Form k-th Householder vector.
+                    if ($this->QR[$k][$k] < 0) {
+                        $nrm = -$nrm;
+                    }
+                    for ($i = $k; $i < $this->m; ++$i) {
+                        $this->QR[$i][$k] /= $nrm;
+                    }
+                    $this->QR[$k][$k] += 1.0;
+                    // Apply transformation to remaining columns.
+                    for ($j = $k + 1; $j < $this->n; ++$j) {
+                        $s = 0.0;
+                        for ($i = $k; $i < $this->m; ++$i) {
+                            $s += $this->QR[$i][$k] * $this->QR[$i][$j];
+                        }
+                        $s = -$s / $this->QR[$k][$k];
+                        for ($i = $k; $i < $this->m; ++$i) {
+                            $this->QR[$i][$j] += $s * $this->QR[$i][$k];
+                        }
+                    }
+                }
+                $this->Rdiag[$k] = -$nrm;
+            }
+        } else {
+            throw new CalculationException(Matrix::ARGUMENT_TYPE_EXCEPTION);
+        }
+    }
+
+    //    function __construct()
+
+    /**
+     *    Is the matrix full rank?
+     *
+     * @return bool true if R, and hence A, has full rank, else false
+     */
+    public function isFullRank()
+    {
+        for ($j = 0; $j < $this->n; ++$j) {
+            if ($this->Rdiag[$j] == 0) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    //    function isFullRank()
+
+    /**
+     * Return the Householder vectors.
+     *
+     * @return Matrix Lower trapezoidal matrix whose columns define the reflections
+     */
+    public function getH()
+    {
+        $H = [];
+        for ($i = 0; $i < $this->m; ++$i) {
+            for ($j = 0; $j < $this->n; ++$j) {
+                if ($i >= $j) {
+                    $H[$i][$j] = $this->QR[$i][$j];
+                } else {
+                    $H[$i][$j] = 0.0;
+                }
+            }
+        }
+
+        return new Matrix($H);
+    }
+
+    //    function getH()
+
+    /**
+     * Return the upper triangular factor.
+     *
+     * @return Matrix upper triangular factor
+     */
+    public function getR()
+    {
+        $R = [];
+        for ($i = 0; $i < $this->n; ++$i) {
+            for ($j = 0; $j < $this->n; ++$j) {
+                if ($i < $j) {
+                    $R[$i][$j] = $this->QR[$i][$j];
+                } elseif ($i == $j) {
+                    $R[$i][$j] = $this->Rdiag[$i];
+                } else {
+                    $R[$i][$j] = 0.0;
+                }
+            }
+        }
+
+        return new Matrix($R);
+    }
+
+    //    function getR()
+
+    /**
+     * Generate and return the (economy-sized) orthogonal factor.
+     *
+     * @return Matrix orthogonal factor
+     */
+    public function getQ()
+    {
+        $Q = [];
+        for ($k = $this->n - 1; $k >= 0; --$k) {
+            for ($i = 0; $i < $this->m; ++$i) {
+                $Q[$i][$k] = 0.0;
+            }
+            $Q[$k][$k] = 1.0;
+            for ($j = $k; $j < $this->n; ++$j) {
+                if ($this->QR[$k][$k] != 0) {
+                    $s = 0.0;
+                    for ($i = $k; $i < $this->m; ++$i) {
+                        $s += $this->QR[$i][$k] * $Q[$i][$j];
+                    }
+                    $s = -$s / $this->QR[$k][$k];
+                    for ($i = $k; $i < $this->m; ++$i) {
+                        $Q[$i][$j] += $s * $this->QR[$i][$k];
+                    }
+                }
+            }
+        }
+
+        return new Matrix($Q);
+    }
+
+    //    function getQ()
+
+    /**
+     * Least squares solution of A*X = B.
+     *
+     * @param Matrix $B a Matrix with as many rows as A and any number of columns
+     *
+     * @return Matrix matrix that minimizes the two norm of Q*R*X-B
+     */
+    public function solve($B)
+    {
+        if ($B->getRowDimension() == $this->m) {
+            if ($this->isFullRank()) {
+                // Copy right hand side
+                $nx = $B->getColumnDimension();
+                $X = $B->getArrayCopy();
+                // Compute Y = transpose(Q)*B
+                for ($k = 0; $k < $this->n; ++$k) {
+                    for ($j = 0; $j < $nx; ++$j) {
+                        $s = 0.0;
+                        for ($i = $k; $i < $this->m; ++$i) {
+                            $s += $this->QR[$i][$k] * $X[$i][$j];
+                        }
+                        $s = -$s / $this->QR[$k][$k];
+                        for ($i = $k; $i < $this->m; ++$i) {
+                            $X[$i][$j] += $s * $this->QR[$i][$k];
+                        }
+                    }
+                }
+                // Solve R*X = Y;
+                for ($k = $this->n - 1; $k >= 0; --$k) {
+                    for ($j = 0; $j < $nx; ++$j) {
+                        $X[$k][$j] /= $this->Rdiag[$k];
+                    }
+                    for ($i = 0; $i < $k; ++$i) {
+                        for ($j = 0; $j < $nx; ++$j) {
+                            $X[$i][$j] -= $X[$k][$j] * $this->QR[$i][$k];
+                        }
+                    }
+                }
+                $X = new Matrix($X);
+
+                return $X->getMatrix(0, $this->n - 1, 0, $nx);
+            }
+
+            throw new CalculationException(self::MATRIX_RANK_EXCEPTION);
+        }
+
+        throw new CalculationException(Matrix::MATRIX_DIMENSION_EXCEPTION);
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/SingularValueDecomposition.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/SingularValueDecomposition.php
new file mode 100755
index 0000000..b997fb7
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/SingularValueDecomposition.php
@@ -0,0 +1,528 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared\JAMA;
+
+/**
+ *    For an m-by-n matrix A with m >= n, the singular value decomposition is
+ *    an m-by-n orthogonal matrix U, an n-by-n diagonal matrix S, and
+ *    an n-by-n orthogonal matrix V so that A = U*S*V'.
+ *
+ *    The singular values, sigma[$k] = S[$k][$k], are ordered so that
+ *    sigma[0] >= sigma[1] >= ... >= sigma[n-1].
+ *
+ *    The singular value decompostion always exists, so the constructor will
+ *    never fail.  The matrix condition number and the effective numerical
+ *    rank can be computed from this decomposition.
+ *
+ *    @author  Paul Meagher
+ *
+ *    @version 1.1
+ */
+class SingularValueDecomposition
+{
+    /**
+     * Internal storage of U.
+     *
+     * @var array
+     */
+    private $U = [];
+
+    /**
+     * Internal storage of V.
+     *
+     * @var array
+     */
+    private $V = [];
+
+    /**
+     * Internal storage of singular values.
+     *
+     * @var array
+     */
+    private $s = [];
+
+    /**
+     * Row dimension.
+     *
+     * @var int
+     */
+    private $m;
+
+    /**
+     * Column dimension.
+     *
+     * @var int
+     */
+    private $n;
+
+    /**
+     * Construct the singular value decomposition.
+     *
+     * Derived from LINPACK code.
+     *
+     * @param mixed $Arg Rectangular matrix
+     */
+    public function __construct($Arg)
+    {
+        // Initialize.
+        $A = $Arg->getArrayCopy();
+        $this->m = $Arg->getRowDimension();
+        $this->n = $Arg->getColumnDimension();
+        $nu = min($this->m, $this->n);
+        $e = [];
+        $work = [];
+        $wantu = true;
+        $wantv = true;
+        $nct = min($this->m - 1, $this->n);
+        $nrt = max(0, min($this->n - 2, $this->m));
+
+        // Reduce A to bidiagonal form, storing the diagonal elements
+        // in s and the super-diagonal elements in e.
+        $kMax = max($nct, $nrt);
+        for ($k = 0; $k < $kMax; ++$k) {
+            if ($k < $nct) {
+                // Compute the transformation for the k-th column and
+                // place the k-th diagonal in s[$k].
+                // Compute 2-norm of k-th column without under/overflow.
+                $this->s[$k] = 0;
+                for ($i = $k; $i < $this->m; ++$i) {
+                    $this->s[$k] = hypo($this->s[$k], $A[$i][$k]);
+                }
+                if ($this->s[$k] != 0.0) {
+                    if ($A[$k][$k] < 0.0) {
+                        $this->s[$k] = -$this->s[$k];
+                    }
+                    for ($i = $k; $i < $this->m; ++$i) {
+                        $A[$i][$k] /= $this->s[$k];
+                    }
+                    $A[$k][$k] += 1.0;
+                }
+                $this->s[$k] = -$this->s[$k];
+            }
+
+            for ($j = $k + 1; $j < $this->n; ++$j) {
+                if (($k < $nct) & ($this->s[$k] != 0.0)) {
+                    // Apply the transformation.
+                    $t = 0;
+                    for ($i = $k; $i < $this->m; ++$i) {
+                        $t += $A[$i][$k] * $A[$i][$j];
+                    }
+                    $t = -$t / $A[$k][$k];
+                    for ($i = $k; $i < $this->m; ++$i) {
+                        $A[$i][$j] += $t * $A[$i][$k];
+                    }
+                    // Place the k-th row of A into e for the
+                    // subsequent calculation of the row transformation.
+                    $e[$j] = $A[$k][$j];
+                }
+            }
+
+            if ($wantu && ($k < $nct)) {
+                // Place the transformation in U for subsequent back
+                // multiplication.
+                for ($i = $k; $i < $this->m; ++$i) {
+                    $this->U[$i][$k] = $A[$i][$k];
+                }
+            }
+
+            if ($k < $nrt) {
+                // Compute the k-th row transformation and place the
+                // k-th super-diagonal in e[$k].
+                // Compute 2-norm without under/overflow.
+                $e[$k] = 0;
+                for ($i = $k + 1; $i < $this->n; ++$i) {
+                    $e[$k] = hypo($e[$k], $e[$i]);
+                }
+                if ($e[$k] != 0.0) {
+                    if ($e[$k + 1] < 0.0) {
+                        $e[$k] = -$e[$k];
+                    }
+                    for ($i = $k + 1; $i < $this->n; ++$i) {
+                        $e[$i] /= $e[$k];
+                    }
+                    $e[$k + 1] += 1.0;
+                }
+                $e[$k] = -$e[$k];
+                if (($k + 1 < $this->m) && ($e[$k] != 0.0)) {
+                    // Apply the transformation.
+                    for ($i = $k + 1; $i < $this->m; ++$i) {
+                        $work[$i] = 0.0;
+                    }
+                    for ($j = $k + 1; $j < $this->n; ++$j) {
+                        for ($i = $k + 1; $i < $this->m; ++$i) {
+                            $work[$i] += $e[$j] * $A[$i][$j];
+                        }
+                    }
+                    for ($j = $k + 1; $j < $this->n; ++$j) {
+                        $t = -$e[$j] / $e[$k + 1];
+                        for ($i = $k + 1; $i < $this->m; ++$i) {
+                            $A[$i][$j] += $t * $work[$i];
+                        }
+                    }
+                }
+                if ($wantv) {
+                    // Place the transformation in V for subsequent
+                    // back multiplication.
+                    for ($i = $k + 1; $i < $this->n; ++$i) {
+                        $this->V[$i][$k] = $e[$i];
+                    }
+                }
+            }
+        }
+
+        // Set up the final bidiagonal matrix or order p.
+        $p = min($this->n, $this->m + 1);
+        if ($nct < $this->n) {
+            $this->s[$nct] = $A[$nct][$nct];
+        }
+        if ($this->m < $p) {
+            $this->s[$p - 1] = 0.0;
+        }
+        if ($nrt + 1 < $p) {
+            $e[$nrt] = $A[$nrt][$p - 1];
+        }
+        $e[$p - 1] = 0.0;
+        // If required, generate U.
+        if ($wantu) {
+            for ($j = $nct; $j < $nu; ++$j) {
+                for ($i = 0; $i < $this->m; ++$i) {
+                    $this->U[$i][$j] = 0.0;
+                }
+                $this->U[$j][$j] = 1.0;
+            }
+            for ($k = $nct - 1; $k >= 0; --$k) {
+                if ($this->s[$k] != 0.0) {
+                    for ($j = $k + 1; $j < $nu; ++$j) {
+                        $t = 0;
+                        for ($i = $k; $i < $this->m; ++$i) {
+                            $t += $this->U[$i][$k] * $this->U[$i][$j];
+                        }
+                        $t = -$t / $this->U[$k][$k];
+                        for ($i = $k; $i < $this->m; ++$i) {
+                            $this->U[$i][$j] += $t * $this->U[$i][$k];
+                        }
+                    }
+                    for ($i = $k; $i < $this->m; ++$i) {
+                        $this->U[$i][$k] = -$this->U[$i][$k];
+                    }
+                    $this->U[$k][$k] = 1.0 + $this->U[$k][$k];
+                    for ($i = 0; $i < $k - 1; ++$i) {
+                        $this->U[$i][$k] = 0.0;
+                    }
+                } else {
+                    for ($i = 0; $i < $this->m; ++$i) {
+                        $this->U[$i][$k] = 0.0;
+                    }
+                    $this->U[$k][$k] = 1.0;
+                }
+            }
+        }
+
+        // If required, generate V.
+        if ($wantv) {
+            for ($k = $this->n - 1; $k >= 0; --$k) {
+                if (($k < $nrt) && ($e[$k] != 0.0)) {
+                    for ($j = $k + 1; $j < $nu; ++$j) {
+                        $t = 0;
+                        for ($i = $k + 1; $i < $this->n; ++$i) {
+                            $t += $this->V[$i][$k] * $this->V[$i][$j];
+                        }
+                        $t = -$t / $this->V[$k + 1][$k];
+                        for ($i = $k + 1; $i < $this->n; ++$i) {
+                            $this->V[$i][$j] += $t * $this->V[$i][$k];
+                        }
+                    }
+                }
+                for ($i = 0; $i < $this->n; ++$i) {
+                    $this->V[$i][$k] = 0.0;
+                }
+                $this->V[$k][$k] = 1.0;
+            }
+        }
+
+        // Main iteration loop for the singular values.
+        $pp = $p - 1;
+        $iter = 0;
+        $eps = 2.0 ** (-52.0);
+
+        while ($p > 0) {
+            // Here is where a test for too many iterations would go.
+            // This section of the program inspects for negligible
+            // elements in the s and e arrays.  On completion the
+            // variables kase and k are set as follows:
+            // kase = 1  if s(p) and e[k-1] are negligible and k<p
+            // kase = 2  if s(k) is negligible and k<p
+            // kase = 3  if e[k-1] is negligible, k<p, and
+            //           s(k), ..., s(p) are not negligible (qr step).
+            // kase = 4  if e(p-1) is negligible (convergence).
+            for ($k = $p - 2; $k >= -1; --$k) {
+                if ($k == -1) {
+                    break;
+                }
+                if (abs($e[$k]) <= $eps * (abs($this->s[$k]) + abs($this->s[$k + 1]))) {
+                    $e[$k] = 0.0;
+
+                    break;
+                }
+            }
+            if ($k == $p - 2) {
+                $kase = 4;
+            } else {
+                for ($ks = $p - 1; $ks >= $k; --$ks) {
+                    if ($ks == $k) {
+                        break;
+                    }
+                    $t = ($ks != $p ? abs($e[$ks]) : 0.) + ($ks != $k + 1 ? abs($e[$ks - 1]) : 0.);
+                    if (abs($this->s[$ks]) <= $eps * $t) {
+                        $this->s[$ks] = 0.0;
+
+                        break;
+                    }
+                }
+                if ($ks == $k) {
+                    $kase = 3;
+                } elseif ($ks == $p - 1) {
+                    $kase = 1;
+                } else {
+                    $kase = 2;
+                    $k = $ks;
+                }
+            }
+            ++$k;
+
+            // Perform the task indicated by kase.
+            switch ($kase) {
+                // Deflate negligible s(p).
+                case 1:
+                    $f = $e[$p - 2];
+                    $e[$p - 2] = 0.0;
+                    for ($j = $p - 2; $j >= $k; --$j) {
+                        $t = hypo($this->s[$j], $f);
+                        $cs = $this->s[$j] / $t;
+                        $sn = $f / $t;
+                        $this->s[$j] = $t;
+                        if ($j != $k) {
+                            $f = -$sn * $e[$j - 1];
+                            $e[$j - 1] = $cs * $e[$j - 1];
+                        }
+                        if ($wantv) {
+                            for ($i = 0; $i < $this->n; ++$i) {
+                                $t = $cs * $this->V[$i][$j] + $sn * $this->V[$i][$p - 1];
+                                $this->V[$i][$p - 1] = -$sn * $this->V[$i][$j] + $cs * $this->V[$i][$p - 1];
+                                $this->V[$i][$j] = $t;
+                            }
+                        }
+                    }
+
+                    break;
+                // Split at negligible s(k).
+                case 2:
+                    $f = $e[$k - 1];
+                    $e[$k - 1] = 0.0;
+                    for ($j = $k; $j < $p; ++$j) {
+                        $t = hypo($this->s[$j], $f);
+                        $cs = $this->s[$j] / $t;
+                        $sn = $f / $t;
+                        $this->s[$j] = $t;
+                        $f = -$sn * $e[$j];
+                        $e[$j] = $cs * $e[$j];
+                        if ($wantu) {
+                            for ($i = 0; $i < $this->m; ++$i) {
+                                $t = $cs * $this->U[$i][$j] + $sn * $this->U[$i][$k - 1];
+                                $this->U[$i][$k - 1] = -$sn * $this->U[$i][$j] + $cs * $this->U[$i][$k - 1];
+                                $this->U[$i][$j] = $t;
+                            }
+                        }
+                    }
+
+                    break;
+                // Perform one qr step.
+                case 3:
+                    // Calculate the shift.
+                    $scale = max(max(max(max(abs($this->s[$p - 1]), abs($this->s[$p - 2])), abs($e[$p - 2])), abs($this->s[$k])), abs($e[$k]));
+                    $sp = $this->s[$p - 1] / $scale;
+                    $spm1 = $this->s[$p - 2] / $scale;
+                    $epm1 = $e[$p - 2] / $scale;
+                    $sk = $this->s[$k] / $scale;
+                    $ek = $e[$k] / $scale;
+                    $b = (($spm1 + $sp) * ($spm1 - $sp) + $epm1 * $epm1) / 2.0;
+                    $c = ($sp * $epm1) * ($sp * $epm1);
+                    $shift = 0.0;
+                    if (($b != 0.0) || ($c != 0.0)) {
+                        $shift = sqrt($b * $b + $c);
+                        if ($b < 0.0) {
+                            $shift = -$shift;
+                        }
+                        $shift = $c / ($b + $shift);
+                    }
+                    $f = ($sk + $sp) * ($sk - $sp) + $shift;
+                    $g = $sk * $ek;
+                    // Chase zeros.
+                    for ($j = $k; $j < $p - 1; ++$j) {
+                        $t = hypo($f, $g);
+                        $cs = $f / $t;
+                        $sn = $g / $t;
+                        if ($j != $k) {
+                            $e[$j - 1] = $t;
+                        }
+                        $f = $cs * $this->s[$j] + $sn * $e[$j];
+                        $e[$j] = $cs * $e[$j] - $sn * $this->s[$j];
+                        $g = $sn * $this->s[$j + 1];
+                        $this->s[$j + 1] = $cs * $this->s[$j + 1];
+                        if ($wantv) {
+                            for ($i = 0; $i < $this->n; ++$i) {
+                                $t = $cs * $this->V[$i][$j] + $sn * $this->V[$i][$j + 1];
+                                $this->V[$i][$j + 1] = -$sn * $this->V[$i][$j] + $cs * $this->V[$i][$j + 1];
+                                $this->V[$i][$j] = $t;
+                            }
+                        }
+                        $t = hypo($f, $g);
+                        $cs = $f / $t;
+                        $sn = $g / $t;
+                        $this->s[$j] = $t;
+                        $f = $cs * $e[$j] + $sn * $this->s[$j + 1];
+                        $this->s[$j + 1] = -$sn * $e[$j] + $cs * $this->s[$j + 1];
+                        $g = $sn * $e[$j + 1];
+                        $e[$j + 1] = $cs * $e[$j + 1];
+                        if ($wantu && ($j < $this->m - 1)) {
+                            for ($i = 0; $i < $this->m; ++$i) {
+                                $t = $cs * $this->U[$i][$j] + $sn * $this->U[$i][$j + 1];
+                                $this->U[$i][$j + 1] = -$sn * $this->U[$i][$j] + $cs * $this->U[$i][$j + 1];
+                                $this->U[$i][$j] = $t;
+                            }
+                        }
+                    }
+                    $e[$p - 2] = $f;
+                    $iter = $iter + 1;
+
+                    break;
+                // Convergence.
+                case 4:
+                    // Make the singular values positive.
+                    if ($this->s[$k] <= 0.0) {
+                        $this->s[$k] = ($this->s[$k] < 0.0 ? -$this->s[$k] : 0.0);
+                        if ($wantv) {
+                            for ($i = 0; $i <= $pp; ++$i) {
+                                $this->V[$i][$k] = -$this->V[$i][$k];
+                            }
+                        }
+                    }
+                    // Order the singular values.
+                    while ($k < $pp) {
+                        if ($this->s[$k] >= $this->s[$k + 1]) {
+                            break;
+                        }
+                        $t = $this->s[$k];
+                        $this->s[$k] = $this->s[$k + 1];
+                        $this->s[$k + 1] = $t;
+                        if ($wantv && ($k < $this->n - 1)) {
+                            for ($i = 0; $i < $this->n; ++$i) {
+                                $t = $this->V[$i][$k + 1];
+                                $this->V[$i][$k + 1] = $this->V[$i][$k];
+                                $this->V[$i][$k] = $t;
+                            }
+                        }
+                        if ($wantu && ($k < $this->m - 1)) {
+                            for ($i = 0; $i < $this->m; ++$i) {
+                                $t = $this->U[$i][$k + 1];
+                                $this->U[$i][$k + 1] = $this->U[$i][$k];
+                                $this->U[$i][$k] = $t;
+                            }
+                        }
+                        ++$k;
+                    }
+                    $iter = 0;
+                    --$p;
+
+                    break;
+            } // end switch
+        } // end while
+    }
+
+    /**
+     * Return the left singular vectors.
+     *
+     * @return Matrix U
+     */
+    public function getU()
+    {
+        return new Matrix($this->U, $this->m, min($this->m + 1, $this->n));
+    }
+
+    /**
+     * Return the right singular vectors.
+     *
+     * @return Matrix V
+     */
+    public function getV()
+    {
+        return new Matrix($this->V);
+    }
+
+    /**
+     * Return the one-dimensional array of singular values.
+     *
+     * @return array diagonal of S
+     */
+    public function getSingularValues()
+    {
+        return $this->s;
+    }
+
+    /**
+     * Return the diagonal matrix of singular values.
+     *
+     * @return Matrix S
+     */
+    public function getS()
+    {
+        for ($i = 0; $i < $this->n; ++$i) {
+            for ($j = 0; $j < $this->n; ++$j) {
+                $S[$i][$j] = 0.0;
+            }
+            $S[$i][$i] = $this->s[$i];
+        }
+
+        return new Matrix($S);
+    }
+
+    /**
+     * Two norm.
+     *
+     * @return float max(S)
+     */
+    public function norm2()
+    {
+        return $this->s[0];
+    }
+
+    /**
+     * Two norm condition number.
+     *
+     * @return float max(S)/min(S)
+     */
+    public function cond()
+    {
+        return $this->s[0] / $this->s[min($this->m, $this->n) - 1];
+    }
+
+    /**
+     * Effective numerical matrix rank.
+     *
+     * @return int Number of nonnegligible singular values
+     */
+    public function rank()
+    {
+        $eps = 2.0 ** (-52.0);
+        $tol = max($this->m, $this->n) * $this->s[0] * $eps;
+        $r = 0;
+        $iMax = count($this->s);
+        for ($i = 0; $i < $iMax; ++$i) {
+            if ($this->s[$i] > $tol) {
+                ++$r;
+            }
+        }
+
+        return $r;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/utils/Maths.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/utils/Maths.php
new file mode 100755
index 0000000..68c3864
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/utils/Maths.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ *    Pythagorean Theorem:.
+ *
+ *    a = 3
+ *    b = 4
+ *    r = sqrt(square(a) + square(b))
+ *    r = 5
+ *
+ *    r = sqrt(a^2 + b^2) without under/overflow.
+ *
+ * @param mixed $a
+ * @param mixed $b
+ *
+ * @return float
+ */
+function hypo($a, $b)
+{
+    if (abs($a) > abs($b)) {
+        $r = $b / $a;
+        $r = abs($a) * sqrt(1 + $r * $r);
+    } elseif ($b != 0) {
+        $r = $a / $b;
+        $r = abs($b) * sqrt(1 + $r * $r);
+    } else {
+        $r = 0.0;
+    }
+
+    return $r;
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE.php
new file mode 100755
index 0000000..32b56e1
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE.php
@@ -0,0 +1,567 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared;
+
+// vim: set expandtab tabstop=4 shiftwidth=4:
+// +----------------------------------------------------------------------+
+// | PHP Version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2002 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license,      |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/2_02.txt.                                 |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Xavier Noguer <xnoguer@php.net>                              |
+// | Based on OLE::Storage_Lite by Kawai, Takanori                        |
+// +----------------------------------------------------------------------+
+//
+
+use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
+use PhpOffice\PhpSpreadsheet\Shared\OLE\ChainedBlockStream;
+use PhpOffice\PhpSpreadsheet\Shared\OLE\PPS\Root;
+
+/*
+ * Array for storing OLE instances that are accessed from
+ * OLE_ChainedBlockStream::stream_open().
+ *
+ * @var array
+ */
+$GLOBALS['_OLE_INSTANCES'] = [];
+
+/**
+ * OLE package base class.
+ *
+ * @author   Xavier Noguer <xnoguer@php.net>
+ * @author   Christian Schmidt <schmidt@php.net>
+ */
+class OLE
+{
+    const OLE_PPS_TYPE_ROOT = 5;
+    const OLE_PPS_TYPE_DIR = 1;
+    const OLE_PPS_TYPE_FILE = 2;
+    const OLE_DATA_SIZE_SMALL = 0x1000;
+    const OLE_LONG_INT_SIZE = 4;
+    const OLE_PPS_SIZE = 0x80;
+
+    /**
+     * The file handle for reading an OLE container.
+     *
+     * @var resource
+     */
+    public $_file_handle;
+
+    /**
+     * Array of PPS's found on the OLE container.
+     *
+     * @var array
+     */
+    public $_list = [];
+
+    /**
+     * Root directory of OLE container.
+     *
+     * @var Root
+     */
+    public $root;
+
+    /**
+     * Big Block Allocation Table.
+     *
+     * @var array (blockId => nextBlockId)
+     */
+    public $bbat;
+
+    /**
+     * Short Block Allocation Table.
+     *
+     * @var array (blockId => nextBlockId)
+     */
+    public $sbat;
+
+    /**
+     * Size of big blocks. This is usually 512.
+     *
+     * @var int number of octets per block
+     */
+    public $bigBlockSize;
+
+    /**
+     * Size of small blocks. This is usually 64.
+     *
+     * @var int number of octets per block
+     */
+    public $smallBlockSize;
+
+    /**
+     * Threshold for big blocks.
+     *
+     * @var int
+     */
+    public $bigBlockThreshold;
+
+    /**
+     * Reads an OLE container from the contents of the file given.
+     *
+     * @acces public
+     *
+     * @param string $file
+     *
+     * @return bool true on success, PEAR_Error on failure
+     */
+    public function read($file)
+    {
+        $fh = fopen($file, 'rb');
+        if (!$fh) {
+            throw new ReaderException("Can't open file $file");
+        }
+        $this->_file_handle = $fh;
+
+        $signature = fread($fh, 8);
+        if ("\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" != $signature) {
+            throw new ReaderException("File doesn't seem to be an OLE container.");
+        }
+        fseek($fh, 28);
+        if (fread($fh, 2) != "\xFE\xFF") {
+            // This shouldn't be a problem in practice
+            throw new ReaderException('Only Little-Endian encoding is supported.');
+        }
+        // Size of blocks and short blocks in bytes
+        $this->bigBlockSize = 2 ** self::_readInt2($fh);
+        $this->smallBlockSize = 2 ** self::_readInt2($fh);
+
+        // Skip UID, revision number and version number
+        fseek($fh, 44);
+        // Number of blocks in Big Block Allocation Table
+        $bbatBlockCount = self::_readInt4($fh);
+
+        // Root chain 1st block
+        $directoryFirstBlockId = self::_readInt4($fh);
+
+        // Skip unused bytes
+        fseek($fh, 56);
+        // Streams shorter than this are stored using small blocks
+        $this->bigBlockThreshold = self::_readInt4($fh);
+        // Block id of first sector in Short Block Allocation Table
+        $sbatFirstBlockId = self::_readInt4($fh);
+        // Number of blocks in Short Block Allocation Table
+        $sbbatBlockCount = self::_readInt4($fh);
+        // Block id of first sector in Master Block Allocation Table
+        $mbatFirstBlockId = self::_readInt4($fh);
+        // Number of blocks in Master Block Allocation Table
+        $mbbatBlockCount = self::_readInt4($fh);
+        $this->bbat = [];
+
+        // Remaining 4 * 109 bytes of current block is beginning of Master
+        // Block Allocation Table
+        $mbatBlocks = [];
+        for ($i = 0; $i < 109; ++$i) {
+            $mbatBlocks[] = self::_readInt4($fh);
+        }
+
+        // Read rest of Master Block Allocation Table (if any is left)
+        $pos = $this->_getBlockOffset($mbatFirstBlockId);
+        for ($i = 0; $i < $mbbatBlockCount; ++$i) {
+            fseek($fh, $pos);
+            for ($j = 0; $j < $this->bigBlockSize / 4 - 1; ++$j) {
+                $mbatBlocks[] = self::_readInt4($fh);
+            }
+            // Last block id in each block points to next block
+            $pos = $this->_getBlockOffset(self::_readInt4($fh));
+        }
+
+        // Read Big Block Allocation Table according to chain specified by $mbatBlocks
+        for ($i = 0; $i < $bbatBlockCount; ++$i) {
+            $pos = $this->_getBlockOffset($mbatBlocks[$i]);
+            fseek($fh, $pos);
+            for ($j = 0; $j < $this->bigBlockSize / 4; ++$j) {
+                $this->bbat[] = self::_readInt4($fh);
+            }
+        }
+
+        // Read short block allocation table (SBAT)
+        $this->sbat = [];
+        $shortBlockCount = $sbbatBlockCount * $this->bigBlockSize / 4;
+        $sbatFh = $this->getStream($sbatFirstBlockId);
+        for ($blockId = 0; $blockId < $shortBlockCount; ++$blockId) {
+            $this->sbat[$blockId] = self::_readInt4($sbatFh);
+        }
+        fclose($sbatFh);
+
+        $this->_readPpsWks($directoryFirstBlockId);
+
+        return true;
+    }
+
+    /**
+     * @param int $blockId byte offset from beginning of file
+     *
+     * @return int
+     */
+    public function _getBlockOffset($blockId)
+    {
+        return 512 + $blockId * $this->bigBlockSize;
+    }
+
+    /**
+     * Returns a stream for use with fread() etc. External callers should
+     * use \PhpOffice\PhpSpreadsheet\Shared\OLE\PPS\File::getStream().
+     *
+     * @param int|OLE\PPS $blockIdOrPps block id or PPS
+     *
+     * @return resource read-only stream
+     */
+    public function getStream($blockIdOrPps)
+    {
+        static $isRegistered = false;
+        if (!$isRegistered) {
+            stream_wrapper_register('ole-chainedblockstream', ChainedBlockStream::class);
+            $isRegistered = true;
+        }
+
+        // Store current instance in global array, so that it can be accessed
+        // in OLE_ChainedBlockStream::stream_open().
+        // Object is removed from self::$instances in OLE_Stream::close().
+        $GLOBALS['_OLE_INSTANCES'][] = $this;
+        $instanceId = end(array_keys($GLOBALS['_OLE_INSTANCES']));
+
+        $path = 'ole-chainedblockstream://oleInstanceId=' . $instanceId;
+        if ($blockIdOrPps instanceof OLE\PPS) {
+            $path .= '&blockId=' . $blockIdOrPps->startBlock;
+            $path .= '&size=' . $blockIdOrPps->Size;
+        } else {
+            $path .= '&blockId=' . $blockIdOrPps;
+        }
+
+        return fopen($path, 'rb');
+    }
+
+    /**
+     * Reads a signed char.
+     *
+     * @param resource $fh file handle
+     *
+     * @return int
+     */
+    private static function _readInt1($fh)
+    {
+        [, $tmp] = unpack('c', fread($fh, 1));
+
+        return $tmp;
+    }
+
+    /**
+     * Reads an unsigned short (2 octets).
+     *
+     * @param resource $fh file handle
+     *
+     * @return int
+     */
+    private static function _readInt2($fh)
+    {
+        [, $tmp] = unpack('v', fread($fh, 2));
+
+        return $tmp;
+    }
+
+    /**
+     * Reads an unsigned long (4 octets).
+     *
+     * @param resource $fh file handle
+     *
+     * @return int
+     */
+    private static function _readInt4($fh)
+    {
+        [, $tmp] = unpack('V', fread($fh, 4));
+
+        return $tmp;
+    }
+
+    /**
+     * Gets information about all PPS's on the OLE container from the PPS WK's
+     * creates an OLE_PPS object for each one.
+     *
+     * @param int $blockId the block id of the first block
+     *
+     * @return bool true on success, PEAR_Error on failure
+     */
+    public function _readPpsWks($blockId)
+    {
+        $fh = $this->getStream($blockId);
+        for ($pos = 0; true; $pos += 128) {
+            fseek($fh, $pos, SEEK_SET);
+            $nameUtf16 = fread($fh, 64);
+            $nameLength = self::_readInt2($fh);
+            $nameUtf16 = substr($nameUtf16, 0, $nameLength - 2);
+            // Simple conversion from UTF-16LE to ISO-8859-1
+            $name = str_replace("\x00", '', $nameUtf16);
+            $type = self::_readInt1($fh);
+            switch ($type) {
+                case self::OLE_PPS_TYPE_ROOT:
+                    $pps = new OLE\PPS\Root(null, null, []);
+                    $this->root = $pps;
+
+                    break;
+                case self::OLE_PPS_TYPE_DIR:
+                    $pps = new OLE\PPS(null, null, null, null, null, null, null, null, null, []);
+
+                    break;
+                case self::OLE_PPS_TYPE_FILE:
+                    $pps = new OLE\PPS\File($name);
+
+                    break;
+                default:
+                    break;
+            }
+            fseek($fh, 1, SEEK_CUR);
+            $pps->Type = $type;
+            $pps->Name = $name;
+            $pps->PrevPps = self::_readInt4($fh);
+            $pps->NextPps = self::_readInt4($fh);
+            $pps->DirPps = self::_readInt4($fh);
+            fseek($fh, 20, SEEK_CUR);
+            $pps->Time1st = self::OLE2LocalDate(fread($fh, 8));
+            $pps->Time2nd = self::OLE2LocalDate(fread($fh, 8));
+            $pps->startBlock = self::_readInt4($fh);
+            $pps->Size = self::_readInt4($fh);
+            $pps->No = count($this->_list);
+            $this->_list[] = $pps;
+
+            // check if the PPS tree (starting from root) is complete
+            if (isset($this->root) && $this->_ppsTreeComplete($this->root->No)) {
+                break;
+            }
+        }
+        fclose($fh);
+
+        // Initialize $pps->children on directories
+        foreach ($this->_list as $pps) {
+            if ($pps->Type == self::OLE_PPS_TYPE_DIR || $pps->Type == self::OLE_PPS_TYPE_ROOT) {
+                $nos = [$pps->DirPps];
+                $pps->children = [];
+                while ($nos) {
+                    $no = array_pop($nos);
+                    if ($no != -1) {
+                        $childPps = $this->_list[$no];
+                        $nos[] = $childPps->PrevPps;
+                        $nos[] = $childPps->NextPps;
+                        $pps->children[] = $childPps;
+                    }
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * It checks whether the PPS tree is complete (all PPS's read)
+     * starting with the given PPS (not necessarily root).
+     *
+     * @param int $index The index of the PPS from which we are checking
+     *
+     * @return bool Whether the PPS tree for the given PPS is complete
+     */
+    public function _ppsTreeComplete($index)
+    {
+        return isset($this->_list[$index]) &&
+            ($pps = $this->_list[$index]) &&
+            ($pps->PrevPps == -1 ||
+                $this->_ppsTreeComplete($pps->PrevPps)) &&
+            ($pps->NextPps == -1 ||
+                $this->_ppsTreeComplete($pps->NextPps)) &&
+            ($pps->DirPps == -1 ||
+                $this->_ppsTreeComplete($pps->DirPps));
+    }
+
+    /**
+     * Checks whether a PPS is a File PPS or not.
+     * If there is no PPS for the index given, it will return false.
+     *
+     * @param int $index The index for the PPS
+     *
+     * @return bool true if it's a File PPS, false otherwise
+     */
+    public function isFile($index)
+    {
+        if (isset($this->_list[$index])) {
+            return $this->_list[$index]->Type == self::OLE_PPS_TYPE_FILE;
+        }
+
+        return false;
+    }
+
+    /**
+     * Checks whether a PPS is a Root PPS or not.
+     * If there is no PPS for the index given, it will return false.
+     *
+     * @param int $index the index for the PPS
+     *
+     * @return bool true if it's a Root PPS, false otherwise
+     */
+    public function isRoot($index)
+    {
+        if (isset($this->_list[$index])) {
+            return $this->_list[$index]->Type == self::OLE_PPS_TYPE_ROOT;
+        }
+
+        return false;
+    }
+
+    /**
+     * Gives the total number of PPS's found in the OLE container.
+     *
+     * @return int The total number of PPS's found in the OLE container
+     */
+    public function ppsTotal()
+    {
+        return count($this->_list);
+    }
+
+    /**
+     * Gets data from a PPS
+     * If there is no PPS for the index given, it will return an empty string.
+     *
+     * @param int $index The index for the PPS
+     * @param int $position The position from which to start reading
+     *                          (relative to the PPS)
+     * @param int $length The amount of bytes to read (at most)
+     *
+     * @return string The binary string containing the data requested
+     *
+     * @see OLE_PPS_File::getStream()
+     */
+    public function getData($index, $position, $length)
+    {
+        // if position is not valid return empty string
+        if (!isset($this->_list[$index]) || ($position >= $this->_list[$index]->Size) || ($position < 0)) {
+            return '';
+        }
+        $fh = $this->getStream($this->_list[$index]);
+        $data = stream_get_contents($fh, $length, $position);
+        fclose($fh);
+
+        return $data;
+    }
+
+    /**
+     * Gets the data length from a PPS
+     * If there is no PPS for the index given, it will return 0.
+     *
+     * @param int $index The index for the PPS
+     *
+     * @return int The amount of bytes in data the PPS has
+     */
+    public function getDataLength($index)
+    {
+        if (isset($this->_list[$index])) {
+            return $this->_list[$index]->Size;
+        }
+
+        return 0;
+    }
+
+    /**
+     * Utility function to transform ASCII text to Unicode.
+     *
+     * @param string $ascii The ASCII string to transform
+     *
+     * @return string The string in Unicode
+     */
+    public static function ascToUcs($ascii)
+    {
+        $rawname = '';
+        $iMax = strlen($ascii);
+        for ($i = 0; $i < $iMax; ++$i) {
+            $rawname .= $ascii[$i]
+                . "\x00";
+        }
+
+        return $rawname;
+    }
+
+    /**
+     * Utility function
+     * Returns a string for the OLE container with the date given.
+     *
+     * @param int $date A timestamp
+     *
+     * @return string The string for the OLE container
+     */
+    public static function localDateToOLE($date)
+    {
+        if (!isset($date)) {
+            return "\x00\x00\x00\x00\x00\x00\x00\x00";
+        }
+
+        // factor used for separating numbers into 4 bytes parts
+        $factor = 2 ** 32;
+
+        // days from 1-1-1601 until the beggining of UNIX era
+        $days = 134774;
+        // calculate seconds
+        $big_date = $days * 24 * 3600 + gmmktime(date('H', $date), date('i', $date), date('s', $date), date('m', $date), date('d', $date), date('Y', $date));
+        // multiply just to make MS happy
+        $big_date *= 10000000;
+
+        $high_part = floor($big_date / $factor);
+        // lower 4 bytes
+        $low_part = floor((($big_date / $factor) - $high_part) * $factor);
+
+        // Make HEX string
+        $res = '';
+
+        for ($i = 0; $i < 4; ++$i) {
+            $hex = $low_part % 0x100;
+            $res .= pack('c', $hex);
+            $low_part /= 0x100;
+        }
+        for ($i = 0; $i < 4; ++$i) {
+            $hex = $high_part % 0x100;
+            $res .= pack('c', $hex);
+            $high_part /= 0x100;
+        }
+
+        return $res;
+    }
+
+    /**
+     * Returns a timestamp from an OLE container's date.
+     *
+     * @param string $oleTimestamp A binary string with the encoded date
+     *
+     * @return int The Unix timestamp corresponding to the string
+     */
+    public static function OLE2LocalDate($oleTimestamp)
+    {
+        if (strlen($oleTimestamp) != 8) {
+            throw new ReaderException('Expecting 8 byte string');
+        }
+
+        // convert to units of 100 ns since 1601:
+        $unpackedTimestamp = unpack('v4', $oleTimestamp);
+        $timestampHigh = (float) $unpackedTimestamp[4] * 65536 + (float) $unpackedTimestamp[3];
+        $timestampLow = (float) $unpackedTimestamp[2] * 65536 + (float) $unpackedTimestamp[1];
+
+        // translate to seconds since 1601:
+        $timestampHigh /= 10000000;
+        $timestampLow /= 10000000;
+
+        // days from 1601 to 1970:
+        $days = 134774;
+
+        // translate to seconds since 1970:
+        $unixTimestamp = floor(65536.0 * 65536.0 * $timestampHigh + $timestampLow - $days * 24 * 3600 + 0.5);
+
+        if ((int) $unixTimestamp == $unixTimestamp) {
+            return (int) $unixTimestamp;
+        }
+
+        return $unixTimestamp >= 0.0 ? PHP_INT_MAX : PHP_INT_MIN;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php
new file mode 100755
index 0000000..ecaa97e
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php
@@ -0,0 +1,196 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared\OLE;
+
+use PhpOffice\PhpSpreadsheet\Shared\OLE;
+
+class ChainedBlockStream
+{
+    /**
+     * The OLE container of the file that is being read.
+     *
+     * @var OLE
+     */
+    public $ole;
+
+    /**
+     * Parameters specified by fopen().
+     *
+     * @var array
+     */
+    public $params;
+
+    /**
+     * The binary data of the file.
+     *
+     * @var string
+     */
+    public $data;
+
+    /**
+     * The file pointer.
+     *
+     * @var int byte offset
+     */
+    public $pos;
+
+    /**
+     * Implements support for fopen().
+     * For creating streams using this wrapper, use OLE_PPS_File::getStream().
+     *
+     * @param string $path resource name including scheme, e.g.
+     *                                    ole-chainedblockstream://oleInstanceId=1
+     * @param string $mode only "r" is supported
+     * @param int $options mask of STREAM_REPORT_ERRORS and STREAM_USE_PATH
+     * @param string &$openedPath absolute path of the opened stream (out parameter)
+     *
+     * @return bool true on success
+     */
+    public function stream_open($path, $mode, $options, &$openedPath) // @codingStandardsIgnoreLine
+    {
+        if ($mode != 'r') {
+            if ($options & STREAM_REPORT_ERRORS) {
+                trigger_error('Only reading is supported', E_USER_WARNING);
+            }
+
+            return false;
+        }
+
+        // 25 is length of "ole-chainedblockstream://"
+        parse_str(substr($path, 25), $this->params);
+        if (!isset($this->params['oleInstanceId'], $this->params['blockId'], $GLOBALS['_OLE_INSTANCES'][$this->params['oleInstanceId']])) {
+            if ($options & STREAM_REPORT_ERRORS) {
+                trigger_error('OLE stream not found', E_USER_WARNING);
+            }
+
+            return false;
+        }
+        $this->ole = $GLOBALS['_OLE_INSTANCES'][$this->params['oleInstanceId']];
+
+        $blockId = $this->params['blockId'];
+        $this->data = '';
+        if (isset($this->params['size']) && $this->params['size'] < $this->ole->bigBlockThreshold && $blockId != $this->ole->root->startBlock) {
+            // Block id refers to small blocks
+            $rootPos = $this->ole->_getBlockOffset($this->ole->root->startBlock);
+            while ($blockId != -2) {
+                $pos = $rootPos + $blockId * $this->ole->bigBlockSize;
+                $blockId = $this->ole->sbat[$blockId];
+                fseek($this->ole->_file_handle, $pos);
+                $this->data .= fread($this->ole->_file_handle, $this->ole->bigBlockSize);
+            }
+        } else {
+            // Block id refers to big blocks
+            while ($blockId != -2) {
+                $pos = $this->ole->_getBlockOffset($blockId);
+                fseek($this->ole->_file_handle, $pos);
+                $this->data .= fread($this->ole->_file_handle, $this->ole->bigBlockSize);
+                $blockId = $this->ole->bbat[$blockId];
+            }
+        }
+        if (isset($this->params['size'])) {
+            $this->data = substr($this->data, 0, $this->params['size']);
+        }
+
+        if ($options & STREAM_USE_PATH) {
+            $openedPath = $path;
+        }
+
+        return true;
+    }
+
+    /**
+     * Implements support for fclose().
+     */
+    public function stream_close(): void // @codingStandardsIgnoreLine
+    {
+        $this->ole = null;
+        unset($GLOBALS['_OLE_INSTANCES']);
+    }
+
+    /**
+     * Implements support for fread(), fgets() etc.
+     *
+     * @param int $count maximum number of bytes to read
+     *
+     * @return string
+     */
+    public function stream_read($count) // @codingStandardsIgnoreLine
+    {
+        if ($this->stream_eof()) {
+            return false;
+        }
+        $s = substr($this->data, $this->pos, $count);
+        $this->pos += $count;
+
+        return $s;
+    }
+
+    /**
+     * Implements support for feof().
+     *
+     * @return bool TRUE if the file pointer is at EOF; otherwise FALSE
+     */
+    public function stream_eof() // @codingStandardsIgnoreLine
+    {
+        return $this->pos >= strlen($this->data);
+    }
+
+    /**
+     * Returns the position of the file pointer, i.e. its offset into the file
+     * stream. Implements support for ftell().
+     *
+     * @return int
+     */
+    public function stream_tell() // @codingStandardsIgnoreLine
+    {
+        return $this->pos;
+    }
+
+    /**
+     * Implements support for fseek().
+     *
+     * @param int $offset byte offset
+     * @param int $whence SEEK_SET, SEEK_CUR or SEEK_END
+     *
+     * @return bool
+     */
+    public function stream_seek($offset, $whence) // @codingStandardsIgnoreLine
+    {
+        if ($whence == SEEK_SET && $offset >= 0) {
+            $this->pos = $offset;
+        } elseif ($whence == SEEK_CUR && -$offset <= $this->pos) {
+            $this->pos += $offset;
+        } elseif ($whence == SEEK_END && -$offset <= count($this->data)) {
+            $this->pos = strlen($this->data) + $offset;
+        } else {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Implements support for fstat(). Currently the only supported field is
+     * "size".
+     *
+     * @return array
+     */
+    public function stream_stat() // @codingStandardsIgnoreLine
+    {
+        return [
+            'size' => strlen($this->data),
+        ];
+    }
+
+    // Methods used by stream_wrapper_register() that are not implemented:
+    // bool stream_flush ( void )
+    // int stream_write ( string data )
+    // bool rename ( string path_from, string path_to )
+    // bool mkdir ( string path, int mode, int options )
+    // bool rmdir ( string path, int options )
+    // bool dir_opendir ( string path, int options )
+    // array url_stat ( string path, int flags )
+    // string dir_readdir ( void )
+    // bool dir_rewinddir ( void )
+    // bool dir_closedir ( void )
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS.php
new file mode 100755
index 0000000..0407e0d
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS.php
@@ -0,0 +1,236 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared\OLE;
+
+// vim: set expandtab tabstop=4 shiftwidth=4:
+// +----------------------------------------------------------------------+
+// | PHP Version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2002 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license,      |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/2_02.txt.                                 |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Xavier Noguer <xnoguer@php.net>                              |
+// | Based on OLE::Storage_Lite by Kawai, Takanori                        |
+// +----------------------------------------------------------------------+
+//
+use PhpOffice\PhpSpreadsheet\Shared\OLE;
+
+/**
+ * Class for creating PPS's for OLE containers.
+ *
+ * @author   Xavier Noguer <xnoguer@php.net>
+ */
+class PPS
+{
+    /**
+     * The PPS index.
+     *
+     * @var int
+     */
+    public $No;
+
+    /**
+     * The PPS name (in Unicode).
+     *
+     * @var string
+     */
+    public $Name;
+
+    /**
+     * The PPS type. Dir, Root or File.
+     *
+     * @var int
+     */
+    public $Type;
+
+    /**
+     * The index of the previous PPS.
+     *
+     * @var int
+     */
+    public $PrevPps;
+
+    /**
+     * The index of the next PPS.
+     *
+     * @var int
+     */
+    public $NextPps;
+
+    /**
+     * The index of it's first child if this is a Dir or Root PPS.
+     *
+     * @var int
+     */
+    public $DirPps;
+
+    /**
+     * A timestamp.
+     *
+     * @var int
+     */
+    public $Time1st;
+
+    /**
+     * A timestamp.
+     *
+     * @var int
+     */
+    public $Time2nd;
+
+    /**
+     * Starting block (small or big) for this PPS's data  inside the container.
+     *
+     * @var int
+     */
+    public $startBlock;
+
+    /**
+     * The size of the PPS's data (in bytes).
+     *
+     * @var int
+     */
+    public $Size;
+
+    /**
+     * The PPS's data (only used if it's not using a temporary file).
+     *
+     * @var string
+     */
+    public $_data;
+
+    /**
+     * Array of child PPS's (only used by Root and Dir PPS's).
+     *
+     * @var array
+     */
+    public $children = [];
+
+    /**
+     * Pointer to OLE container.
+     *
+     * @var OLE
+     */
+    public $ole;
+
+    /**
+     * The constructor.
+     *
+     * @param int $No The PPS index
+     * @param string $name The PPS name
+     * @param int $type The PPS type. Dir, Root or File
+     * @param int $prev The index of the previous PPS
+     * @param int $next The index of the next PPS
+     * @param int $dir The index of it's first child if this is a Dir or Root PPS
+     * @param int $time_1st A timestamp
+     * @param int $time_2nd A timestamp
+     * @param string $data The (usually binary) source data of the PPS
+     * @param array $children Array containing children PPS for this PPS
+     */
+    public function __construct($No, $name, $type, $prev, $next, $dir, $time_1st, $time_2nd, $data, $children)
+    {
+        $this->No = $No;
+        $this->Name = $name;
+        $this->Type = $type;
+        $this->PrevPps = $prev;
+        $this->NextPps = $next;
+        $this->DirPps = $dir;
+        $this->Time1st = $time_1st;
+        $this->Time2nd = $time_2nd;
+        $this->_data = $data;
+        $this->children = $children;
+        if ($data != '') {
+            $this->Size = strlen($data);
+        } else {
+            $this->Size = 0;
+        }
+    }
+
+    /**
+     * Returns the amount of data saved for this PPS.
+     *
+     * @return int The amount of data (in bytes)
+     */
+    public function getDataLen()
+    {
+        if (!isset($this->_data)) {
+            return 0;
+        }
+
+        return strlen($this->_data);
+    }
+
+    /**
+     * Returns a string with the PPS's WK (What is a WK?).
+     *
+     * @return string The binary string
+     */
+    public function _getPpsWk()
+    {
+        $ret = str_pad($this->Name, 64, "\x00");
+
+        $ret .= pack('v', strlen($this->Name) + 2)  // 66
+            . pack('c', $this->Type)              // 67
+            . pack('c', 0x00) //UK                // 68
+            . pack('V', $this->PrevPps) //Prev    // 72
+            . pack('V', $this->NextPps) //Next    // 76
+            . pack('V', $this->DirPps)  //Dir     // 80
+            . "\x00\x09\x02\x00"                  // 84
+            . "\x00\x00\x00\x00"                  // 88
+            . "\xc0\x00\x00\x00"                  // 92
+            . "\x00\x00\x00\x46"                  // 96 // Seems to be ok only for Root
+            . "\x00\x00\x00\x00"                  // 100
+            . OLE::localDateToOLE($this->Time1st)          // 108
+            . OLE::localDateToOLE($this->Time2nd)          // 116
+            . pack('V', isset($this->startBlock) ? $this->startBlock : 0)  // 120
+            . pack('V', $this->Size)               // 124
+            . pack('V', 0); // 128
+        return $ret;
+    }
+
+    /**
+     * Updates index and pointers to previous, next and children PPS's for this
+     * PPS. I don't think it'll work with Dir PPS's.
+     *
+     * @param array &$raList Reference to the array of PPS's for the whole OLE
+     *                          container
+     * @param mixed $to_save
+     * @param mixed $depth
+     *
+     * @return int The index for this PPS
+     */
+    public static function _savePpsSetPnt(&$raList, $to_save, $depth = 0)
+    {
+        if (!is_array($to_save) || (empty($to_save))) {
+            return 0xFFFFFFFF;
+        } elseif (count($to_save) == 1) {
+            $cnt = count($raList);
+            // If the first entry, it's the root... Don't clone it!
+            $raList[$cnt] = ($depth == 0) ? $to_save[0] : clone $to_save[0];
+            $raList[$cnt]->No = $cnt;
+            $raList[$cnt]->PrevPps = 0xFFFFFFFF;
+            $raList[$cnt]->NextPps = 0xFFFFFFFF;
+            $raList[$cnt]->DirPps = self::_savePpsSetPnt($raList, @$raList[$cnt]->children, $depth++);
+        } else {
+            $iPos = floor(count($to_save) / 2);
+            $aPrev = array_slice($to_save, 0, $iPos);
+            $aNext = array_slice($to_save, $iPos + 1);
+            $cnt = count($raList);
+            // If the first entry, it's the root... Don't clone it!
+            $raList[$cnt] = ($depth == 0) ? $to_save[$iPos] : clone $to_save[$iPos];
+            $raList[$cnt]->No = $cnt;
+            $raList[$cnt]->PrevPps = self::_savePpsSetPnt($raList, $aPrev, $depth++);
+            $raList[$cnt]->NextPps = self::_savePpsSetPnt($raList, $aNext, $depth++);
+            $raList[$cnt]->DirPps = self::_savePpsSetPnt($raList, @$raList[$cnt]->children, $depth++);
+        }
+
+        return $cnt;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/File.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/File.php
new file mode 100755
index 0000000..dd1cda2
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/File.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared\OLE\PPS;
+
+// vim: set expandtab tabstop=4 shiftwidth=4:
+// +----------------------------------------------------------------------+
+// | PHP Version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2002 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license,      |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/2_02.txt.                                 |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Xavier Noguer <xnoguer@php.net>                              |
+// | Based on OLE::Storage_Lite by Kawai, Takanori                        |
+// +----------------------------------------------------------------------+
+//
+use PhpOffice\PhpSpreadsheet\Shared\OLE;
+use PhpOffice\PhpSpreadsheet\Shared\OLE\PPS;
+
+/**
+ * Class for creating File PPS's for OLE containers.
+ *
+ * @author   Xavier Noguer <xnoguer@php.net>
+ */
+class File extends PPS
+{
+    /**
+     * The constructor.
+     *
+     * @param string $name The name of the file (in Unicode)
+     *
+     * @see OLE::ascToUcs()
+     */
+    public function __construct($name)
+    {
+        parent::__construct(null, $name, OLE::OLE_PPS_TYPE_FILE, null, null, null, null, null, '', []);
+    }
+
+    /**
+     * Initialization method. Has to be called right after OLE_PPS_File().
+     *
+     * @return mixed true on success
+     */
+    public function init()
+    {
+        return true;
+    }
+
+    /**
+     * Append data to PPS.
+     *
+     * @param string $data The data to append
+     */
+    public function append($data): void
+    {
+        $this->_data .= $data;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/Root.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/Root.php
new file mode 100755
index 0000000..11655c6
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/Root.php
@@ -0,0 +1,426 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared\OLE\PPS;
+
+// vim: set expandtab tabstop=4 shiftwidth=4:
+// +----------------------------------------------------------------------+
+// | PHP Version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2002 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license,      |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/2_02.txt.                                 |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Xavier Noguer <xnoguer@php.net>                              |
+// | Based on OLE::Storage_Lite by Kawai, Takanori                        |
+// +----------------------------------------------------------------------+
+//
+use PhpOffice\PhpSpreadsheet\Shared\OLE;
+use PhpOffice\PhpSpreadsheet\Shared\OLE\PPS;
+
+/**
+ * Class for creating Root PPS's for OLE containers.
+ *
+ * @author   Xavier Noguer <xnoguer@php.net>
+ */
+class Root extends PPS
+{
+    /**
+     * @var resource
+     */
+    private $fileHandle;
+
+    /**
+     * @var int
+     */
+    private $smallBlockSize;
+
+    /**
+     * @var int
+     */
+    private $bigBlockSize;
+
+    /**
+     * @param int $time_1st A timestamp
+     * @param int $time_2nd A timestamp
+     * @param File[] $raChild
+     */
+    public function __construct($time_1st, $time_2nd, $raChild)
+    {
+        parent::__construct(null, OLE::ascToUcs('Root Entry'), OLE::OLE_PPS_TYPE_ROOT, null, null, null, $time_1st, $time_2nd, null, $raChild);
+    }
+
+    /**
+     * Method for saving the whole OLE container (including files).
+     * In fact, if called with an empty argument (or '-'), it saves to a
+     * temporary file and then outputs it's contents to stdout.
+     * If a resource pointer to a stream created by fopen() is passed
+     * it will be used, but you have to close such stream by yourself.
+     *
+     * @param resource $fileHandle the name of the file or stream where to save the OLE container
+     *
+     * @return bool true on success
+     */
+    public function save($fileHandle)
+    {
+        $this->fileHandle = $fileHandle;
+
+        // Initial Setting for saving
+        $this->bigBlockSize = 2 ** (
+            (isset($this->bigBlockSize)) ? self::adjust2($this->bigBlockSize) : 9
+            );
+        $this->smallBlockSize = 2 ** (
+            (isset($this->smallBlockSize)) ? self::adjust2($this->smallBlockSize) : 6
+            );
+
+        // Make an array of PPS's (for Save)
+        $aList = [];
+        PPS::_savePpsSetPnt($aList, [$this]);
+        // calculate values for header
+        [$iSBDcnt, $iBBcnt, $iPPScnt] = $this->_calcSize($aList); //, $rhInfo);
+        // Save Header
+        $this->_saveHeader($iSBDcnt, $iBBcnt, $iPPScnt);
+
+        // Make Small Data string (write SBD)
+        $this->_data = $this->_makeSmallData($aList);
+
+        // Write BB
+        $this->_saveBigData($iSBDcnt, $aList);
+        // Write PPS
+        $this->_savePps($aList);
+        // Write Big Block Depot and BDList and Adding Header informations
+        $this->_saveBbd($iSBDcnt, $iBBcnt, $iPPScnt);
+
+        return true;
+    }
+
+    /**
+     * Calculate some numbers.
+     *
+     * @param array $raList Reference to an array of PPS's
+     *
+     * @return float[] The array of numbers
+     */
+    public function _calcSize(&$raList)
+    {
+        // Calculate Basic Setting
+        [$iSBDcnt, $iBBcnt, $iPPScnt] = [0, 0, 0];
+        $iSmallLen = 0;
+        $iSBcnt = 0;
+        $iCount = count($raList);
+        for ($i = 0; $i < $iCount; ++$i) {
+            if ($raList[$i]->Type == OLE::OLE_PPS_TYPE_FILE) {
+                $raList[$i]->Size = $raList[$i]->getDataLen();
+                if ($raList[$i]->Size < OLE::OLE_DATA_SIZE_SMALL) {
+                    $iSBcnt += floor($raList[$i]->Size / $this->smallBlockSize)
+                        + (($raList[$i]->Size % $this->smallBlockSize) ? 1 : 0);
+                } else {
+                    $iBBcnt += (floor($raList[$i]->Size / $this->bigBlockSize) +
+                        (($raList[$i]->Size % $this->bigBlockSize) ? 1 : 0));
+                }
+            }
+        }
+        $iSmallLen = $iSBcnt * $this->smallBlockSize;
+        $iSlCnt = floor($this->bigBlockSize / OLE::OLE_LONG_INT_SIZE);
+        $iSBDcnt = floor($iSBcnt / $iSlCnt) + (($iSBcnt % $iSlCnt) ? 1 : 0);
+        $iBBcnt += (floor($iSmallLen / $this->bigBlockSize) +
+            (($iSmallLen % $this->bigBlockSize) ? 1 : 0));
+        $iCnt = count($raList);
+        $iBdCnt = $this->bigBlockSize / OLE::OLE_PPS_SIZE;
+        $iPPScnt = (floor($iCnt / $iBdCnt) + (($iCnt % $iBdCnt) ? 1 : 0));
+
+        return [$iSBDcnt, $iBBcnt, $iPPScnt];
+    }
+
+    /**
+     * Helper function for caculating a magic value for block sizes.
+     *
+     * @param int $i2 The argument
+     *
+     * @return float
+     *
+     * @see save()
+     */
+    private static function adjust2($i2)
+    {
+        $iWk = log($i2) / log(2);
+
+        return ($iWk > floor($iWk)) ? floor($iWk) + 1 : $iWk;
+    }
+
+    /**
+     * Save OLE header.
+     *
+     * @param int $iSBDcnt
+     * @param int $iBBcnt
+     * @param int $iPPScnt
+     */
+    public function _saveHeader($iSBDcnt, $iBBcnt, $iPPScnt): void
+    {
+        $FILE = $this->fileHandle;
+
+        // Calculate Basic Setting
+        $iBlCnt = $this->bigBlockSize / OLE::OLE_LONG_INT_SIZE;
+        $i1stBdL = ($this->bigBlockSize - 0x4C) / OLE::OLE_LONG_INT_SIZE;
+
+        $iBdExL = 0;
+        $iAll = $iBBcnt + $iPPScnt + $iSBDcnt;
+        $iAllW = $iAll;
+        $iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt) ? 1 : 0);
+        $iBdCnt = floor(($iAll + $iBdCntW) / $iBlCnt) + ((($iAllW + $iBdCntW) % $iBlCnt) ? 1 : 0);
+
+        // Calculate BD count
+        if ($iBdCnt > $i1stBdL) {
+            while (1) {
+                ++$iBdExL;
+                ++$iAllW;
+                $iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt) ? 1 : 0);
+                $iBdCnt = floor(($iAllW + $iBdCntW) / $iBlCnt) + ((($iAllW + $iBdCntW) % $iBlCnt) ? 1 : 0);
+                if ($iBdCnt <= ($iBdExL * $iBlCnt + $i1stBdL)) {
+                    break;
+                }
+            }
+        }
+
+        // Save Header
+        fwrite(
+            $FILE,
+            "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1"
+            . "\x00\x00\x00\x00"
+            . "\x00\x00\x00\x00"
+            . "\x00\x00\x00\x00"
+            . "\x00\x00\x00\x00"
+            . pack('v', 0x3b)
+            . pack('v', 0x03)
+            . pack('v', -2)
+            . pack('v', 9)
+            . pack('v', 6)
+            . pack('v', 0)
+            . "\x00\x00\x00\x00"
+            . "\x00\x00\x00\x00"
+            . pack('V', $iBdCnt)
+            . pack('V', $iBBcnt + $iSBDcnt) //ROOT START
+            . pack('V', 0)
+            . pack('V', 0x1000)
+            . pack('V', $iSBDcnt ? 0 : -2) //Small Block Depot
+            . pack('V', $iSBDcnt)
+        );
+        // Extra BDList Start, Count
+        if ($iBdCnt < $i1stBdL) {
+            fwrite(
+                $FILE,
+                pack('V', -2) // Extra BDList Start
+                . pack('V', 0)// Extra BDList Count
+            );
+        } else {
+            fwrite($FILE, pack('V', $iAll + $iBdCnt) . pack('V', $iBdExL));
+        }
+
+        // BDList
+        for ($i = 0; $i < $i1stBdL && $i < $iBdCnt; ++$i) {
+            fwrite($FILE, pack('V', $iAll + $i));
+        }
+        if ($i < $i1stBdL) {
+            $jB = $i1stBdL - $i;
+            for ($j = 0; $j < $jB; ++$j) {
+                fwrite($FILE, (pack('V', -1)));
+            }
+        }
+    }
+
+    /**
+     * Saving big data (PPS's with data bigger than \PhpOffice\PhpSpreadsheet\Shared\OLE::OLE_DATA_SIZE_SMALL).
+     *
+     * @param int $iStBlk
+     * @param array &$raList Reference to array of PPS's
+     */
+    public function _saveBigData($iStBlk, &$raList): void
+    {
+        $FILE = $this->fileHandle;
+
+        // cycle through PPS's
+        $iCount = count($raList);
+        for ($i = 0; $i < $iCount; ++$i) {
+            if ($raList[$i]->Type != OLE::OLE_PPS_TYPE_DIR) {
+                $raList[$i]->Size = $raList[$i]->getDataLen();
+                if (($raList[$i]->Size >= OLE::OLE_DATA_SIZE_SMALL) || (($raList[$i]->Type == OLE::OLE_PPS_TYPE_ROOT) && isset($raList[$i]->_data))) {
+                    fwrite($FILE, $raList[$i]->_data);
+
+                    if ($raList[$i]->Size % $this->bigBlockSize) {
+                        fwrite($FILE, str_repeat("\x00", $this->bigBlockSize - ($raList[$i]->Size % $this->bigBlockSize)));
+                    }
+                    // Set For PPS
+                    $raList[$i]->startBlock = $iStBlk;
+                    $iStBlk +=
+                        (floor($raList[$i]->Size / $this->bigBlockSize) +
+                            (($raList[$i]->Size % $this->bigBlockSize) ? 1 : 0));
+                }
+            }
+        }
+    }
+
+    /**
+     * get small data (PPS's with data smaller than \PhpOffice\PhpSpreadsheet\Shared\OLE::OLE_DATA_SIZE_SMALL).
+     *
+     * @param array &$raList Reference to array of PPS's
+     *
+     * @return string
+     */
+    public function _makeSmallData(&$raList)
+    {
+        $sRes = '';
+        $FILE = $this->fileHandle;
+        $iSmBlk = 0;
+
+        $iCount = count($raList);
+        for ($i = 0; $i < $iCount; ++$i) {
+            // Make SBD, small data string
+            if ($raList[$i]->Type == OLE::OLE_PPS_TYPE_FILE) {
+                if ($raList[$i]->Size <= 0) {
+                    continue;
+                }
+                if ($raList[$i]->Size < OLE::OLE_DATA_SIZE_SMALL) {
+                    $iSmbCnt = floor($raList[$i]->Size / $this->smallBlockSize)
+                        + (($raList[$i]->Size % $this->smallBlockSize) ? 1 : 0);
+                    // Add to SBD
+                    $jB = $iSmbCnt - 1;
+                    for ($j = 0; $j < $jB; ++$j) {
+                        fwrite($FILE, pack('V', $j + $iSmBlk + 1));
+                    }
+                    fwrite($FILE, pack('V', -2));
+
+                    // Add to Data String(this will be written for RootEntry)
+                    $sRes .= $raList[$i]->_data;
+                    if ($raList[$i]->Size % $this->smallBlockSize) {
+                        $sRes .= str_repeat("\x00", $this->smallBlockSize - ($raList[$i]->Size % $this->smallBlockSize));
+                    }
+                    // Set for PPS
+                    $raList[$i]->startBlock = $iSmBlk;
+                    $iSmBlk += $iSmbCnt;
+                }
+            }
+        }
+        $iSbCnt = floor($this->bigBlockSize / OLE::OLE_LONG_INT_SIZE);
+        if ($iSmBlk % $iSbCnt) {
+            $iB = $iSbCnt - ($iSmBlk % $iSbCnt);
+            for ($i = 0; $i < $iB; ++$i) {
+                fwrite($FILE, pack('V', -1));
+            }
+        }
+
+        return $sRes;
+    }
+
+    /**
+     * Saves all the PPS's WKs.
+     *
+     * @param array $raList Reference to an array with all PPS's
+     */
+    public function _savePps(&$raList): void
+    {
+        // Save each PPS WK
+        $iC = count($raList);
+        for ($i = 0; $i < $iC; ++$i) {
+            fwrite($this->fileHandle, $raList[$i]->_getPpsWk());
+        }
+        // Adjust for Block
+        $iCnt = count($raList);
+        $iBCnt = $this->bigBlockSize / OLE::OLE_PPS_SIZE;
+        if ($iCnt % $iBCnt) {
+            fwrite($this->fileHandle, str_repeat("\x00", ($iBCnt - ($iCnt % $iBCnt)) * OLE::OLE_PPS_SIZE));
+        }
+    }
+
+    /**
+     * Saving Big Block Depot.
+     *
+     * @param int $iSbdSize
+     * @param int $iBsize
+     * @param int $iPpsCnt
+     */
+    public function _saveBbd($iSbdSize, $iBsize, $iPpsCnt): void
+    {
+        $FILE = $this->fileHandle;
+        // Calculate Basic Setting
+        $iBbCnt = $this->bigBlockSize / OLE::OLE_LONG_INT_SIZE;
+        $i1stBdL = ($this->bigBlockSize - 0x4C) / OLE::OLE_LONG_INT_SIZE;
+
+        $iBdExL = 0;
+        $iAll = $iBsize + $iPpsCnt + $iSbdSize;
+        $iAllW = $iAll;
+        $iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt) ? 1 : 0);
+        $iBdCnt = floor(($iAll + $iBdCntW) / $iBbCnt) + ((($iAllW + $iBdCntW) % $iBbCnt) ? 1 : 0);
+        // Calculate BD count
+        if ($iBdCnt > $i1stBdL) {
+            while (1) {
+                ++$iBdExL;
+                ++$iAllW;
+                $iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt) ? 1 : 0);
+                $iBdCnt = floor(($iAllW + $iBdCntW) / $iBbCnt) + ((($iAllW + $iBdCntW) % $iBbCnt) ? 1 : 0);
+                if ($iBdCnt <= ($iBdExL * $iBbCnt + $i1stBdL)) {
+                    break;
+                }
+            }
+        }
+
+        // Making BD
+        // Set for SBD
+        if ($iSbdSize > 0) {
+            for ($i = 0; $i < ($iSbdSize - 1); ++$i) {
+                fwrite($FILE, pack('V', $i + 1));
+            }
+            fwrite($FILE, pack('V', -2));
+        }
+        // Set for B
+        for ($i = 0; $i < ($iBsize - 1); ++$i) {
+            fwrite($FILE, pack('V', $i + $iSbdSize + 1));
+        }
+        fwrite($FILE, pack('V', -2));
+
+        // Set for PPS
+        for ($i = 0; $i < ($iPpsCnt - 1); ++$i) {
+            fwrite($FILE, pack('V', $i + $iSbdSize + $iBsize + 1));
+        }
+        fwrite($FILE, pack('V', -2));
+        // Set for BBD itself ( 0xFFFFFFFD : BBD)
+        for ($i = 0; $i < $iBdCnt; ++$i) {
+            fwrite($FILE, pack('V', 0xFFFFFFFD));
+        }
+        // Set for ExtraBDList
+        for ($i = 0; $i < $iBdExL; ++$i) {
+            fwrite($FILE, pack('V', 0xFFFFFFFC));
+        }
+        // Adjust for Block
+        if (($iAllW + $iBdCnt) % $iBbCnt) {
+            $iBlock = ($iBbCnt - (($iAllW + $iBdCnt) % $iBbCnt));
+            for ($i = 0; $i < $iBlock; ++$i) {
+                fwrite($FILE, pack('V', -1));
+            }
+        }
+        // Extra BDList
+        if ($iBdCnt > $i1stBdL) {
+            $iN = 0;
+            $iNb = 0;
+            for ($i = $i1stBdL; $i < $iBdCnt; $i++, ++$iN) {
+                if ($iN >= ($iBbCnt - 1)) {
+                    $iN = 0;
+                    ++$iNb;
+                    fwrite($FILE, pack('V', $iAll + $iBdCnt + $iNb));
+                }
+                fwrite($FILE, pack('V', $iBsize + $iSbdSize + $iPpsCnt + $i));
+            }
+            if (($iBdCnt - $i1stBdL) % ($iBbCnt - 1)) {
+                $iB = ($iBbCnt - 1) - (($iBdCnt - $i1stBdL) % ($iBbCnt - 1));
+                for ($i = 0; $i < $iB; ++$i) {
+                    fwrite($FILE, pack('V', -1));
+                }
+            }
+            fwrite($FILE, pack('V', -2));
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLERead.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLERead.php
new file mode 100755
index 0000000..b6d5422
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLERead.php
@@ -0,0 +1,350 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared;
+
+use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
+
+class OLERead
+{
+    private $data = '';
+
+    // Size of a sector = 512 bytes
+    const BIG_BLOCK_SIZE = 0x200;
+
+    // Size of a short sector = 64 bytes
+    const SMALL_BLOCK_SIZE = 0x40;
+
+    // Size of a directory entry always = 128 bytes
+    const PROPERTY_STORAGE_BLOCK_SIZE = 0x80;
+
+    // Minimum size of a standard stream = 4096 bytes, streams smaller than this are stored as short streams
+    const SMALL_BLOCK_THRESHOLD = 0x1000;
+
+    // header offsets
+    const NUM_BIG_BLOCK_DEPOT_BLOCKS_POS = 0x2c;
+    const ROOT_START_BLOCK_POS = 0x30;
+    const SMALL_BLOCK_DEPOT_BLOCK_POS = 0x3c;
+    const EXTENSION_BLOCK_POS = 0x44;
+    const NUM_EXTENSION_BLOCK_POS = 0x48;
+    const BIG_BLOCK_DEPOT_BLOCKS_POS = 0x4c;
+
+    // property storage offsets (directory offsets)
+    const SIZE_OF_NAME_POS = 0x40;
+    const TYPE_POS = 0x42;
+    const START_BLOCK_POS = 0x74;
+    const SIZE_POS = 0x78;
+
+    public $wrkbook;
+
+    public $summaryInformation;
+
+    public $documentSummaryInformation;
+
+    /**
+     * @var int
+     */
+    private $numBigBlockDepotBlocks;
+
+    /**
+     * @var int
+     */
+    private $rootStartBlock;
+
+    /**
+     * @var int
+     */
+    private $sbdStartBlock;
+
+    /**
+     * @var int
+     */
+    private $extensionBlock;
+
+    /**
+     * @var int
+     */
+    private $numExtensionBlocks;
+
+    /**
+     * @var string
+     */
+    private $bigBlockChain;
+
+    /**
+     * @var string
+     */
+    private $smallBlockChain;
+
+    /**
+     * @var string
+     */
+    private $entry;
+
+    /**
+     * @var int
+     */
+    private $rootentry;
+
+    /**
+     * @var array
+     */
+    private $props = [];
+
+    /**
+     * Read the file.
+     *
+     * @param $pFilename string Filename
+     */
+    public function read($pFilename): void
+    {
+        File::assertFile($pFilename);
+
+        // Get the file identifier
+        // Don't bother reading the whole file until we know it's a valid OLE file
+        $this->data = file_get_contents($pFilename, false, null, 0, 8);
+
+        // Check OLE identifier
+        $identifierOle = pack('CCCCCCCC', 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1);
+        if ($this->data != $identifierOle) {
+            throw new ReaderException('The filename ' . $pFilename . ' is not recognised as an OLE file');
+        }
+
+        // Get the file data
+        $this->data = file_get_contents($pFilename);
+
+        // Total number of sectors used for the SAT
+        $this->numBigBlockDepotBlocks = self::getInt4d($this->data, self::NUM_BIG_BLOCK_DEPOT_BLOCKS_POS);
+
+        // SecID of the first sector of the directory stream
+        $this->rootStartBlock = self::getInt4d($this->data, self::ROOT_START_BLOCK_POS);
+
+        // SecID of the first sector of the SSAT (or -2 if not extant)
+        $this->sbdStartBlock = self::getInt4d($this->data, self::SMALL_BLOCK_DEPOT_BLOCK_POS);
+
+        // SecID of the first sector of the MSAT (or -2 if no additional sectors are used)
+        $this->extensionBlock = self::getInt4d($this->data, self::EXTENSION_BLOCK_POS);
+
+        // Total number of sectors used by MSAT
+        $this->numExtensionBlocks = self::getInt4d($this->data, self::NUM_EXTENSION_BLOCK_POS);
+
+        $bigBlockDepotBlocks = [];
+        $pos = self::BIG_BLOCK_DEPOT_BLOCKS_POS;
+
+        $bbdBlocks = $this->numBigBlockDepotBlocks;
+
+        if ($this->numExtensionBlocks != 0) {
+            $bbdBlocks = (self::BIG_BLOCK_SIZE - self::BIG_BLOCK_DEPOT_BLOCKS_POS) / 4;
+        }
+
+        for ($i = 0; $i < $bbdBlocks; ++$i) {
+            $bigBlockDepotBlocks[$i] = self::getInt4d($this->data, $pos);
+            $pos += 4;
+        }
+
+        for ($j = 0; $j < $this->numExtensionBlocks; ++$j) {
+            $pos = ($this->extensionBlock + 1) * self::BIG_BLOCK_SIZE;
+            $blocksToRead = min($this->numBigBlockDepotBlocks - $bbdBlocks, self::BIG_BLOCK_SIZE / 4 - 1);
+
+            for ($i = $bbdBlocks; $i < $bbdBlocks + $blocksToRead; ++$i) {
+                $bigBlockDepotBlocks[$i] = self::getInt4d($this->data, $pos);
+                $pos += 4;
+            }
+
+            $bbdBlocks += $blocksToRead;
+            if ($bbdBlocks < $this->numBigBlockDepotBlocks) {
+                $this->extensionBlock = self::getInt4d($this->data, $pos);
+            }
+        }
+
+        $pos = 0;
+        $this->bigBlockChain = '';
+        $bbs = self::BIG_BLOCK_SIZE / 4;
+        for ($i = 0; $i < $this->numBigBlockDepotBlocks; ++$i) {
+            $pos = ($bigBlockDepotBlocks[$i] + 1) * self::BIG_BLOCK_SIZE;
+
+            $this->bigBlockChain .= substr($this->data, $pos, 4 * $bbs);
+            $pos += 4 * $bbs;
+        }
+
+        $pos = 0;
+        $sbdBlock = $this->sbdStartBlock;
+        $this->smallBlockChain = '';
+        while ($sbdBlock != -2) {
+            $pos = ($sbdBlock + 1) * self::BIG_BLOCK_SIZE;
+
+            $this->smallBlockChain .= substr($this->data, $pos, 4 * $bbs);
+            $pos += 4 * $bbs;
+
+            $sbdBlock = self::getInt4d($this->bigBlockChain, $sbdBlock * 4);
+        }
+
+        // read the directory stream
+        $block = $this->rootStartBlock;
+        $this->entry = $this->_readData($block);
+
+        $this->readPropertySets();
+    }
+
+    /**
+     * Extract binary stream data.
+     *
+     * @param int $stream
+     *
+     * @return string
+     */
+    public function getStream($stream)
+    {
+        if ($stream === null) {
+            return null;
+        }
+
+        $streamData = '';
+
+        if ($this->props[$stream]['size'] < self::SMALL_BLOCK_THRESHOLD) {
+            $rootdata = $this->_readData($this->props[$this->rootentry]['startBlock']);
+
+            $block = $this->props[$stream]['startBlock'];
+
+            while ($block != -2) {
+                $pos = $block * self::SMALL_BLOCK_SIZE;
+                $streamData .= substr($rootdata, $pos, self::SMALL_BLOCK_SIZE);
+
+                $block = self::getInt4d($this->smallBlockChain, $block * 4);
+            }
+
+            return $streamData;
+        }
+        $numBlocks = $this->props[$stream]['size'] / self::BIG_BLOCK_SIZE;
+        if ($this->props[$stream]['size'] % self::BIG_BLOCK_SIZE != 0) {
+            ++$numBlocks;
+        }
+
+        if ($numBlocks == 0) {
+            return '';
+        }
+
+        $block = $this->props[$stream]['startBlock'];
+
+        while ($block != -2) {
+            $pos = ($block + 1) * self::BIG_BLOCK_SIZE;
+            $streamData .= substr($this->data, $pos, self::BIG_BLOCK_SIZE);
+            $block = self::getInt4d($this->bigBlockChain, $block * 4);
+        }
+
+        return $streamData;
+    }
+
+    /**
+     * Read a standard stream (by joining sectors using information from SAT).
+     *
+     * @param int $bl Sector ID where the stream starts
+     *
+     * @return string Data for standard stream
+     */
+    private function _readData($bl)
+    {
+        $block = $bl;
+        $data = '';
+
+        while ($block != -2) {
+            $pos = ($block + 1) * self::BIG_BLOCK_SIZE;
+            $data .= substr($this->data, $pos, self::BIG_BLOCK_SIZE);
+            $block = self::getInt4d($this->bigBlockChain, $block * 4);
+        }
+
+        return $data;
+    }
+
+    /**
+     * Read entries in the directory stream.
+     */
+    private function readPropertySets(): void
+    {
+        $offset = 0;
+
+        // loop through entires, each entry is 128 bytes
+        $entryLen = strlen($this->entry);
+        while ($offset < $entryLen) {
+            // entry data (128 bytes)
+            $d = substr($this->entry, $offset, self::PROPERTY_STORAGE_BLOCK_SIZE);
+
+            // size in bytes of name
+            $nameSize = ord($d[self::SIZE_OF_NAME_POS]) | (ord($d[self::SIZE_OF_NAME_POS + 1]) << 8);
+
+            // type of entry
+            $type = ord($d[self::TYPE_POS]);
+
+            // sectorID of first sector or short sector, if this entry refers to a stream (the case with workbook)
+            // sectorID of first sector of the short-stream container stream, if this entry is root entry
+            $startBlock = self::getInt4d($d, self::START_BLOCK_POS);
+
+            $size = self::getInt4d($d, self::SIZE_POS);
+
+            $name = str_replace("\x00", '', substr($d, 0, $nameSize));
+
+            $this->props[] = [
+                'name' => $name,
+                'type' => $type,
+                'startBlock' => $startBlock,
+                'size' => $size,
+            ];
+
+            // tmp helper to simplify checks
+            $upName = strtoupper($name);
+
+            // Workbook directory entry (BIFF5 uses Book, BIFF8 uses Workbook)
+            if (($upName === 'WORKBOOK') || ($upName === 'BOOK')) {
+                $this->wrkbook = count($this->props) - 1;
+            } elseif ($upName === 'ROOT ENTRY' || $upName === 'R') {
+                // Root entry
+                $this->rootentry = count($this->props) - 1;
+            }
+
+            // Summary information
+            if ($name == chr(5) . 'SummaryInformation') {
+                $this->summaryInformation = count($this->props) - 1;
+            }
+
+            // Additional Document Summary information
+            if ($name == chr(5) . 'DocumentSummaryInformation') {
+                $this->documentSummaryInformation = count($this->props) - 1;
+            }
+
+            $offset += self::PROPERTY_STORAGE_BLOCK_SIZE;
+        }
+    }
+
+    /**
+     * Read 4 bytes of data at specified position.
+     *
+     * @param string $data
+     * @param int $pos
+     *
+     * @return int
+     */
+    private static function getInt4d($data, $pos)
+    {
+        if ($pos < 0) {
+            // Invalid position
+            throw new ReaderException('Parameter pos=' . $pos . ' is invalid.');
+        }
+
+        $len = strlen($data);
+        if ($len < $pos + 4) {
+            $data .= str_repeat("\0", $pos + 4 - $len);
+        }
+
+        // FIX: represent numbers correctly on 64-bit system
+        // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
+        // Changed by Andreas Rehm 2006 to ensure correct result of the <<24 block on 32 and 64bit systems
+        $_or_24 = ord($data[$pos + 3]);
+        if ($_or_24 >= 128) {
+            // negative number
+            $_ord_24 = -abs((256 - $_or_24) << 24);
+        } else {
+            $_ord_24 = ($_or_24 & 127) << 24;
+        }
+
+        return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/PasswordHasher.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/PasswordHasher.php
new file mode 100755
index 0000000..9fefe88
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/PasswordHasher.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared;
+
+use PhpOffice\PhpSpreadsheet\Exception;
+use PhpOffice\PhpSpreadsheet\Worksheet\Protection;
+
+class PasswordHasher
+{
+    /**
+     * Get algorithm name for PHP.
+     */
+    private static function getAlgorithm(string $algorithmName): string
+    {
+        if (!$algorithmName) {
+            return '';
+        }
+
+        // Mapping between algorithm name in Excel and algorithm name in PHP
+        $mapping = [
+            Protection::ALGORITHM_MD2 => 'md2',
+            Protection::ALGORITHM_MD4 => 'md4',
+            Protection::ALGORITHM_MD5 => 'md5',
+            Protection::ALGORITHM_SHA_1 => 'sha1',
+            Protection::ALGORITHM_SHA_256 => 'sha256',
+            Protection::ALGORITHM_SHA_384 => 'sha384',
+            Protection::ALGORITHM_SHA_512 => 'sha512',
+            Protection::ALGORITHM_RIPEMD_128 => 'ripemd128',
+            Protection::ALGORITHM_RIPEMD_160 => 'ripemd160',
+            Protection::ALGORITHM_WHIRLPOOL => 'whirlpool',
+        ];
+
+        if (array_key_exists($algorithmName, $mapping)) {
+            return $mapping[$algorithmName];
+        }
+
+        throw new Exception('Unsupported password algorithm: ' . $algorithmName);
+    }
+
+    /**
+     * Create a password hash from a given string.
+     *
+     * This method is based on the algorithm provided by
+     * Daniel Rentz of OpenOffice and the PEAR package
+     * Spreadsheet_Excel_Writer by Xavier Noguer <xnoguer@rezebra.com>.
+     *
+     * @param string $pPassword Password to hash
+     */
+    private static function defaultHashPassword(string $pPassword): string
+    {
+        $password = 0x0000;
+        $charPos = 1; // char position
+
+        // split the plain text password in its component characters
+        $chars = preg_split('//', $pPassword, -1, PREG_SPLIT_NO_EMPTY);
+        foreach ($chars as $char) {
+            $value = ord($char) << $charPos++; // shifted ASCII value
+            $rotated_bits = $value >> 15; // rotated bits beyond bit 15
+            $value &= 0x7fff; // first 15 bits
+            $password ^= ($value | $rotated_bits);
+        }
+
+        $password ^= strlen($pPassword);
+        $password ^= 0xCE4B;
+
+        return strtoupper(dechex($password));
+    }
+
+    /**
+     * Create a password hash from a given string by a specific algorithm.
+     *
+     * 2.4.2.4 ISO Write Protection Method
+     *
+     * @see https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-offcrypto/1357ea58-646e-4483-92ef-95d718079d6f
+     *
+     * @param string $password Password to hash
+     * @param string $algorithm Hash algorithm used to compute the password hash value
+     * @param string $salt Pseudorandom string
+     * @param int $spinCount Number of times to iterate on a hash of a password
+     *
+     * @return string Hashed password
+     */
+    public static function hashPassword(string $password, string $algorithm = '', string $salt = '', int $spinCount = 10000): string
+    {
+        $phpAlgorithm = self::getAlgorithm($algorithm);
+        if (!$phpAlgorithm) {
+            return self::defaultHashPassword($password);
+        }
+
+        $saltValue = base64_decode($salt);
+        $encodedPassword = mb_convert_encoding($password, 'UCS-2LE', 'UTF-8');
+
+        $hashValue = hash($phpAlgorithm, $saltValue . $encodedPassword, true);
+        for ($i = 0; $i < $spinCount; ++$i) {
+            $hashValue = hash($phpAlgorithm, $hashValue . pack('L', $i), true);
+        }
+
+        return base64_encode($hashValue);
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/StringHelper.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/StringHelper.php
new file mode 100755
index 0000000..9ae3241
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/StringHelper.php
@@ -0,0 +1,722 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
+
+class StringHelper
+{
+    /**    Constants                */
+    /**    Regular Expressions        */
+    //    Fraction
+    const STRING_REGEXP_FRACTION = '(-?)(\d+)\s+(\d+\/\d+)';
+
+    /**
+     * Control characters array.
+     *
+     * @var string[]
+     */
+    private static $controlCharacters = [];
+
+    /**
+     * SYLK Characters array.
+     *
+     * @var array
+     */
+    private static $SYLKCharacters = [];
+
+    /**
+     * Decimal separator.
+     *
+     * @var string
+     */
+    private static $decimalSeparator;
+
+    /**
+     * Thousands separator.
+     *
+     * @var string
+     */
+    private static $thousandsSeparator;
+
+    /**
+     * Currency code.
+     *
+     * @var string
+     */
+    private static $currencyCode;
+
+    /**
+     * Is iconv extension avalable?
+     *
+     * @var bool
+     */
+    private static $isIconvEnabled;
+
+    /**
+     * iconv options.
+     *
+     * @var string
+     */
+    private static $iconvOptions = '//IGNORE//TRANSLIT';
+
+    /**
+     * Build control characters array.
+     */
+    private static function buildControlCharacters(): void
+    {
+        for ($i = 0; $i <= 31; ++$i) {
+            if ($i != 9 && $i != 10 && $i != 13) {
+                $find = '_x' . sprintf('%04s', strtoupper(dechex($i))) . '_';
+                $replace = chr($i);
+                self::$controlCharacters[$find] = $replace;
+            }
+        }
+    }
+
+    /**
+     * Build SYLK characters array.
+     */
+    private static function buildSYLKCharacters(): void
+    {
+        self::$SYLKCharacters = [
+            "\x1B 0" => chr(0),
+            "\x1B 1" => chr(1),
+            "\x1B 2" => chr(2),
+            "\x1B 3" => chr(3),
+            "\x1B 4" => chr(4),
+            "\x1B 5" => chr(5),
+            "\x1B 6" => chr(6),
+            "\x1B 7" => chr(7),
+            "\x1B 8" => chr(8),
+            "\x1B 9" => chr(9),
+            "\x1B :" => chr(10),
+            "\x1B ;" => chr(11),
+            "\x1B <" => chr(12),
+            "\x1B =" => chr(13),
+            "\x1B >" => chr(14),
+            "\x1B ?" => chr(15),
+            "\x1B!0" => chr(16),
+            "\x1B!1" => chr(17),
+            "\x1B!2" => chr(18),
+            "\x1B!3" => chr(19),
+            "\x1B!4" => chr(20),
+            "\x1B!5" => chr(21),
+            "\x1B!6" => chr(22),
+            "\x1B!7" => chr(23),
+            "\x1B!8" => chr(24),
+            "\x1B!9" => chr(25),
+            "\x1B!:" => chr(26),
+            "\x1B!;" => chr(27),
+            "\x1B!<" => chr(28),
+            "\x1B!=" => chr(29),
+            "\x1B!>" => chr(30),
+            "\x1B!?" => chr(31),
+            "\x1B'?" => chr(127),
+            "\x1B(0" => '€', // 128 in CP1252
+            "\x1B(2" => '‚', // 130 in CP1252
+            "\x1B(3" => 'ƒ', // 131 in CP1252
+            "\x1B(4" => '„', // 132 in CP1252
+            "\x1B(5" => '…', // 133 in CP1252
+            "\x1B(6" => '†', // 134 in CP1252
+            "\x1B(7" => '‡', // 135 in CP1252
+            "\x1B(8" => 'ˆ', // 136 in CP1252
+            "\x1B(9" => '‰', // 137 in CP1252
+            "\x1B(:" => 'Š', // 138 in CP1252
+            "\x1B(;" => '‹', // 139 in CP1252
+            "\x1BNj" => 'Œ', // 140 in CP1252
+            "\x1B(>" => 'Ž', // 142 in CP1252
+            "\x1B)1" => '‘', // 145 in CP1252
+            "\x1B)2" => '’', // 146 in CP1252
+            "\x1B)3" => '“', // 147 in CP1252
+            "\x1B)4" => '”', // 148 in CP1252
+            "\x1B)5" => '•', // 149 in CP1252
+            "\x1B)6" => '–', // 150 in CP1252
+            "\x1B)7" => '—', // 151 in CP1252
+            "\x1B)8" => '˜', // 152 in CP1252
+            "\x1B)9" => '™', // 153 in CP1252
+            "\x1B):" => 'š', // 154 in CP1252
+            "\x1B);" => '›', // 155 in CP1252
+            "\x1BNz" => 'œ', // 156 in CP1252
+            "\x1B)>" => 'ž', // 158 in CP1252
+            "\x1B)?" => 'Ÿ', // 159 in CP1252
+            "\x1B*0" => ' ', // 160 in CP1252
+            "\x1BN!" => '¡', // 161 in CP1252
+            "\x1BN\"" => '¢', // 162 in CP1252
+            "\x1BN#" => '£', // 163 in CP1252
+            "\x1BN(" => '¤', // 164 in CP1252
+            "\x1BN%" => '¥', // 165 in CP1252
+            "\x1B*6" => '¦', // 166 in CP1252
+            "\x1BN'" => '§', // 167 in CP1252
+            "\x1BNH " => '¨', // 168 in CP1252
+            "\x1BNS" => '©', // 169 in CP1252
+            "\x1BNc" => 'ª', // 170 in CP1252
+            "\x1BN+" => '«', // 171 in CP1252
+            "\x1B*<" => '¬', // 172 in CP1252
+            "\x1B*=" => '­', // 173 in CP1252
+            "\x1BNR" => '®', // 174 in CP1252
+            "\x1B*?" => '¯', // 175 in CP1252
+            "\x1BN0" => '°', // 176 in CP1252
+            "\x1BN1" => '±', // 177 in CP1252
+            "\x1BN2" => '²', // 178 in CP1252
+            "\x1BN3" => '³', // 179 in CP1252
+            "\x1BNB " => '´', // 180 in CP1252
+            "\x1BN5" => 'µ', // 181 in CP1252
+            "\x1BN6" => '¶', // 182 in CP1252
+            "\x1BN7" => '·', // 183 in CP1252
+            "\x1B+8" => '¸', // 184 in CP1252
+            "\x1BNQ" => '¹', // 185 in CP1252
+            "\x1BNk" => 'º', // 186 in CP1252
+            "\x1BN;" => '»', // 187 in CP1252
+            "\x1BN<" => '¼', // 188 in CP1252
+            "\x1BN=" => '½', // 189 in CP1252
+            "\x1BN>" => '¾', // 190 in CP1252
+            "\x1BN?" => '¿', // 191 in CP1252
+            "\x1BNAA" => 'À', // 192 in CP1252
+            "\x1BNBA" => 'Á', // 193 in CP1252
+            "\x1BNCA" => 'Â', // 194 in CP1252
+            "\x1BNDA" => 'Ã', // 195 in CP1252
+            "\x1BNHA" => 'Ä', // 196 in CP1252
+            "\x1BNJA" => 'Å', // 197 in CP1252
+            "\x1BNa" => 'Æ', // 198 in CP1252
+            "\x1BNKC" => 'Ç', // 199 in CP1252
+            "\x1BNAE" => 'È', // 200 in CP1252
+            "\x1BNBE" => 'É', // 201 in CP1252
+            "\x1BNCE" => 'Ê', // 202 in CP1252
+            "\x1BNHE" => 'Ë', // 203 in CP1252
+            "\x1BNAI" => 'Ì', // 204 in CP1252
+            "\x1BNBI" => 'Í', // 205 in CP1252
+            "\x1BNCI" => 'Î', // 206 in CP1252
+            "\x1BNHI" => 'Ï', // 207 in CP1252
+            "\x1BNb" => 'Ð', // 208 in CP1252
+            "\x1BNDN" => 'Ñ', // 209 in CP1252
+            "\x1BNAO" => 'Ò', // 210 in CP1252
+            "\x1BNBO" => 'Ó', // 211 in CP1252
+            "\x1BNCO" => 'Ô', // 212 in CP1252
+            "\x1BNDO" => 'Õ', // 213 in CP1252
+            "\x1BNHO" => 'Ö', // 214 in CP1252
+            "\x1B-7" => '×', // 215 in CP1252
+            "\x1BNi" => 'Ø', // 216 in CP1252
+            "\x1BNAU" => 'Ù', // 217 in CP1252
+            "\x1BNBU" => 'Ú', // 218 in CP1252
+            "\x1BNCU" => 'Û', // 219 in CP1252
+            "\x1BNHU" => 'Ü', // 220 in CP1252
+            "\x1B-=" => 'Ý', // 221 in CP1252
+            "\x1BNl" => 'Þ', // 222 in CP1252
+            "\x1BN{" => 'ß', // 223 in CP1252
+            "\x1BNAa" => 'à', // 224 in CP1252
+            "\x1BNBa" => 'á', // 225 in CP1252
+            "\x1BNCa" => 'â', // 226 in CP1252
+            "\x1BNDa" => 'ã', // 227 in CP1252
+            "\x1BNHa" => 'ä', // 228 in CP1252
+            "\x1BNJa" => 'å', // 229 in CP1252
+            "\x1BNq" => 'æ', // 230 in CP1252
+            "\x1BNKc" => 'ç', // 231 in CP1252
+            "\x1BNAe" => 'è', // 232 in CP1252
+            "\x1BNBe" => 'é', // 233 in CP1252
+            "\x1BNCe" => 'ê', // 234 in CP1252
+            "\x1BNHe" => 'ë', // 235 in CP1252
+            "\x1BNAi" => 'ì', // 236 in CP1252
+            "\x1BNBi" => 'í', // 237 in CP1252
+            "\x1BNCi" => 'î', // 238 in CP1252
+            "\x1BNHi" => 'ï', // 239 in CP1252
+            "\x1BNs" => 'ð', // 240 in CP1252
+            "\x1BNDn" => 'ñ', // 241 in CP1252
+            "\x1BNAo" => 'ò', // 242 in CP1252
+            "\x1BNBo" => 'ó', // 243 in CP1252
+            "\x1BNCo" => 'ô', // 244 in CP1252
+            "\x1BNDo" => 'õ', // 245 in CP1252
+            "\x1BNHo" => 'ö', // 246 in CP1252
+            "\x1B/7" => '÷', // 247 in CP1252
+            "\x1BNy" => 'ø', // 248 in CP1252
+            "\x1BNAu" => 'ù', // 249 in CP1252
+            "\x1BNBu" => 'ú', // 250 in CP1252
+            "\x1BNCu" => 'û', // 251 in CP1252
+            "\x1BNHu" => 'ü', // 252 in CP1252
+            "\x1B/=" => 'ý', // 253 in CP1252
+            "\x1BN|" => 'þ', // 254 in CP1252
+            "\x1BNHy" => 'ÿ', // 255 in CP1252
+        ];
+    }
+
+    /**
+     * Get whether iconv extension is available.
+     *
+     * @return bool
+     */
+    public static function getIsIconvEnabled()
+    {
+        if (isset(self::$isIconvEnabled)) {
+            return self::$isIconvEnabled;
+        }
+
+        // Assume no problems with iconv
+        self::$isIconvEnabled = true;
+
+        // Fail if iconv doesn't exist
+        if (!function_exists('iconv')) {
+            self::$isIconvEnabled = false;
+        } elseif (!@iconv('UTF-8', 'UTF-16LE', 'x')) {
+            // Sometimes iconv is not working, and e.g. iconv('UTF-8', 'UTF-16LE', 'x') just returns false,
+            self::$isIconvEnabled = false;
+        } elseif (defined('PHP_OS') && @stristr(PHP_OS, 'AIX') && defined('ICONV_IMPL') && (@strcasecmp(ICONV_IMPL, 'unknown') == 0) && defined('ICONV_VERSION') && (@strcasecmp(ICONV_VERSION, 'unknown') == 0)) {
+            // CUSTOM: IBM AIX iconv() does not work
+            self::$isIconvEnabled = false;
+        }
+
+        // Deactivate iconv default options if they fail (as seen on IMB i)
+        if (self::$isIconvEnabled && !@iconv('UTF-8', 'UTF-16LE' . self::$iconvOptions, 'x')) {
+            self::$iconvOptions = '';
+        }
+
+        return self::$isIconvEnabled;
+    }
+
+    private static function buildCharacterSets(): void
+    {
+        if (empty(self::$controlCharacters)) {
+            self::buildControlCharacters();
+        }
+
+        if (empty(self::$SYLKCharacters)) {
+            self::buildSYLKCharacters();
+        }
+    }
+
+    /**
+     * Convert from OpenXML escaped control character to PHP control character.
+     *
+     * Excel 2007 team:
+     * ----------------
+     * That's correct, control characters are stored directly in the shared-strings table.
+     * We do encode characters that cannot be represented in XML using the following escape sequence:
+     * _xHHHH_ where H represents a hexadecimal character in the character's value...
+     * So you could end up with something like _x0008_ in a string (either in a cell value (<v>)
+     * element or in the shared string <t> element.
+     *
+     * @param string $value Value to unescape
+     *
+     * @return string
+     */
+    public static function controlCharacterOOXML2PHP($value)
+    {
+        self::buildCharacterSets();
+
+        return str_replace(array_keys(self::$controlCharacters), array_values(self::$controlCharacters), $value);
+    }
+
+    /**
+     * Convert from PHP control character to OpenXML escaped control character.
+     *
+     * Excel 2007 team:
+     * ----------------
+     * That's correct, control characters are stored directly in the shared-strings table.
+     * We do encode characters that cannot be represented in XML using the following escape sequence:
+     * _xHHHH_ where H represents a hexadecimal character in the character's value...
+     * So you could end up with something like _x0008_ in a string (either in a cell value (<v>)
+     * element or in the shared string <t> element.
+     *
+     * @param string $value Value to escape
+     *
+     * @return string
+     */
+    public static function controlCharacterPHP2OOXML($value)
+    {
+        self::buildCharacterSets();
+
+        return str_replace(array_values(self::$controlCharacters), array_keys(self::$controlCharacters), $value);
+    }
+
+    /**
+     * Try to sanitize UTF8, stripping invalid byte sequences. Not perfect. Does not surrogate characters.
+     *
+     * @param string $value
+     *
+     * @return string
+     */
+    public static function sanitizeUTF8($value)
+    {
+        if (self::getIsIconvEnabled()) {
+            $value = @iconv('UTF-8', 'UTF-8', $value);
+
+            return $value;
+        }
+
+        $value = mb_convert_encoding($value, 'UTF-8', 'UTF-8');
+
+        return $value;
+    }
+
+    /**
+     * Check if a string contains UTF8 data.
+     *
+     * @param string $value
+     *
+     * @return bool
+     */
+    public static function isUTF8($value)
+    {
+        return $value === '' || preg_match('/^./su', $value) === 1;
+    }
+
+    /**
+     * Formats a numeric value as a string for output in various output writers forcing
+     * point as decimal separator in case locale is other than English.
+     *
+     * @param mixed $value
+     *
+     * @return string
+     */
+    public static function formatNumber($value)
+    {
+        if (is_float($value)) {
+            return str_replace(',', '.', $value);
+        }
+
+        return (string) $value;
+    }
+
+    /**
+     * Converts a UTF-8 string into BIFF8 Unicode string data (8-bit string length)
+     * Writes the string using uncompressed notation, no rich text, no Asian phonetics
+     * If mbstring extension is not available, ASCII is assumed, and compressed notation is used
+     * although this will give wrong results for non-ASCII strings
+     * see OpenOffice.org's Documentation of the Microsoft Excel File Format, sect. 2.5.3.
+     *
+     * @param string $value UTF-8 encoded string
+     * @param mixed[] $arrcRuns Details of rich text runs in $value
+     *
+     * @return string
+     */
+    public static function UTF8toBIFF8UnicodeShort($value, $arrcRuns = [])
+    {
+        // character count
+        $ln = self::countCharacters($value, 'UTF-8');
+        // option flags
+        if (empty($arrcRuns)) {
+            $data = pack('CC', $ln, 0x0001);
+            // characters
+            $data .= self::convertEncoding($value, 'UTF-16LE', 'UTF-8');
+        } else {
+            $data = pack('vC', $ln, 0x09);
+            $data .= pack('v', count($arrcRuns));
+            // characters
+            $data .= self::convertEncoding($value, 'UTF-16LE', 'UTF-8');
+            foreach ($arrcRuns as $cRun) {
+                $data .= pack('v', $cRun['strlen']);
+                $data .= pack('v', $cRun['fontidx']);
+            }
+        }
+
+        return $data;
+    }
+
+    /**
+     * Converts a UTF-8 string into BIFF8 Unicode string data (16-bit string length)
+     * Writes the string using uncompressed notation, no rich text, no Asian phonetics
+     * If mbstring extension is not available, ASCII is assumed, and compressed notation is used
+     * although this will give wrong results for non-ASCII strings
+     * see OpenOffice.org's Documentation of the Microsoft Excel File Format, sect. 2.5.3.
+     *
+     * @param string $value UTF-8 encoded string
+     *
+     * @return string
+     */
+    public static function UTF8toBIFF8UnicodeLong($value)
+    {
+        // character count
+        $ln = self::countCharacters($value, 'UTF-8');
+
+        // characters
+        $chars = self::convertEncoding($value, 'UTF-16LE', 'UTF-8');
+
+        return pack('vC', $ln, 0x0001) . $chars;
+    }
+
+    /**
+     * Convert string from one encoding to another.
+     *
+     * @param string $value
+     * @param string $to Encoding to convert to, e.g. 'UTF-8'
+     * @param string $from Encoding to convert from, e.g. 'UTF-16LE'
+     *
+     * @return string
+     */
+    public static function convertEncoding($value, $to, $from)
+    {
+        if (self::getIsIconvEnabled()) {
+            $result = iconv($from, $to . self::$iconvOptions, $value);
+            if (false !== $result) {
+                return $result;
+            }
+        }
+
+        return mb_convert_encoding($value, $to, $from);
+    }
+
+    /**
+     * Get character count.
+     *
+     * @param string $value
+     * @param string $enc Encoding
+     *
+     * @return int Character count
+     */
+    public static function countCharacters($value, $enc = 'UTF-8')
+    {
+        return mb_strlen($value, $enc);
+    }
+
+    /**
+     * Get a substring of a UTF-8 encoded string.
+     *
+     * @param string $pValue UTF-8 encoded string
+     * @param int $pStart Start offset
+     * @param int $pLength Maximum number of characters in substring
+     *
+     * @return string
+     */
+    public static function substring($pValue, $pStart, $pLength = 0)
+    {
+        return mb_substr($pValue, $pStart, $pLength, 'UTF-8');
+    }
+
+    /**
+     * Convert a UTF-8 encoded string to upper case.
+     *
+     * @param string $pValue UTF-8 encoded string
+     *
+     * @return string
+     */
+    public static function strToUpper($pValue)
+    {
+        return mb_convert_case($pValue, MB_CASE_UPPER, 'UTF-8');
+    }
+
+    /**
+     * Convert a UTF-8 encoded string to lower case.
+     *
+     * @param string $pValue UTF-8 encoded string
+     *
+     * @return string
+     */
+    public static function strToLower($pValue)
+    {
+        return mb_convert_case($pValue, MB_CASE_LOWER, 'UTF-8');
+    }
+
+    /**
+     * Convert a UTF-8 encoded string to title/proper case
+     * (uppercase every first character in each word, lower case all other characters).
+     *
+     * @param string $pValue UTF-8 encoded string
+     *
+     * @return string
+     */
+    public static function strToTitle($pValue)
+    {
+        return mb_convert_case($pValue, MB_CASE_TITLE, 'UTF-8');
+    }
+
+    public static function mbIsUpper($char)
+    {
+        return mb_strtolower($char, 'UTF-8') != $char;
+    }
+
+    public static function mbStrSplit($string)
+    {
+        // Split at all position not after the start: ^
+        // and not before the end: $
+        return preg_split('/(?<!^)(?!$)/u', $string);
+    }
+
+    /**
+     * Reverse the case of a string, so that all uppercase characters become lowercase
+     * and all lowercase characters become uppercase.
+     *
+     * @param string $pValue UTF-8 encoded string
+     *
+     * @return string
+     */
+    public static function strCaseReverse($pValue)
+    {
+        $characters = self::mbStrSplit($pValue);
+        foreach ($characters as &$character) {
+            if (self::mbIsUpper($character)) {
+                $character = mb_strtolower($character, 'UTF-8');
+            } else {
+                $character = mb_strtoupper($character, 'UTF-8');
+            }
+        }
+
+        return implode('', $characters);
+    }
+
+    /**
+     * Identify whether a string contains a fractional numeric value,
+     * and convert it to a numeric if it is.
+     *
+     * @param string &$operand string value to test
+     *
+     * @return bool
+     */
+    public static function convertToNumberIfFraction(&$operand)
+    {
+        if (preg_match('/^' . self::STRING_REGEXP_FRACTION . '$/i', $operand, $match)) {
+            $sign = ($match[1] == '-') ? '-' : '+';
+            $fractionFormula = '=' . $sign . $match[2] . $sign . $match[3];
+            $operand = Calculation::getInstance()->_calculateFormulaValue($fractionFormula);
+
+            return true;
+        }
+
+        return false;
+    }
+
+    //    function convertToNumberIfFraction()
+
+    /**
+     * Get the decimal separator. If it has not yet been set explicitly, try to obtain number
+     * formatting information from locale.
+     *
+     * @return string
+     */
+    public static function getDecimalSeparator()
+    {
+        if (!isset(self::$decimalSeparator)) {
+            $localeconv = localeconv();
+            self::$decimalSeparator = ($localeconv['decimal_point'] != '')
+                ? $localeconv['decimal_point'] : $localeconv['mon_decimal_point'];
+
+            if (self::$decimalSeparator == '') {
+                // Default to .
+                self::$decimalSeparator = '.';
+            }
+        }
+
+        return self::$decimalSeparator;
+    }
+
+    /**
+     * Set the decimal separator. Only used by NumberFormat::toFormattedString()
+     * to format output by \PhpOffice\PhpSpreadsheet\Writer\Html and \PhpOffice\PhpSpreadsheet\Writer\Pdf.
+     *
+     * @param string $pValue Character for decimal separator
+     */
+    public static function setDecimalSeparator($pValue): void
+    {
+        self::$decimalSeparator = $pValue;
+    }
+
+    /**
+     * Get the thousands separator. If it has not yet been set explicitly, try to obtain number
+     * formatting information from locale.
+     *
+     * @return string
+     */
+    public static function getThousandsSeparator()
+    {
+        if (!isset(self::$thousandsSeparator)) {
+            $localeconv = localeconv();
+            self::$thousandsSeparator = ($localeconv['thousands_sep'] != '')
+                ? $localeconv['thousands_sep'] : $localeconv['mon_thousands_sep'];
+
+            if (self::$thousandsSeparator == '') {
+                // Default to .
+                self::$thousandsSeparator = ',';
+            }
+        }
+
+        return self::$thousandsSeparator;
+    }
+
+    /**
+     * Set the thousands separator. Only used by NumberFormat::toFormattedString()
+     * to format output by \PhpOffice\PhpSpreadsheet\Writer\Html and \PhpOffice\PhpSpreadsheet\Writer\Pdf.
+     *
+     * @param string $pValue Character for thousands separator
+     */
+    public static function setThousandsSeparator($pValue): void
+    {
+        self::$thousandsSeparator = $pValue;
+    }
+
+    /**
+     *    Get the currency code. If it has not yet been set explicitly, try to obtain the
+     *        symbol information from locale.
+     *
+     * @return string
+     */
+    public static function getCurrencyCode()
+    {
+        if (!empty(self::$currencyCode)) {
+            return self::$currencyCode;
+        }
+        self::$currencyCode = '$';
+        $localeconv = localeconv();
+        if (!empty($localeconv['currency_symbol'])) {
+            self::$currencyCode = $localeconv['currency_symbol'];
+
+            return self::$currencyCode;
+        }
+        if (!empty($localeconv['int_curr_symbol'])) {
+            self::$currencyCode = $localeconv['int_curr_symbol'];
+
+            return self::$currencyCode;
+        }
+
+        return self::$currencyCode;
+    }
+
+    /**
+     * Set the currency code. Only used by NumberFormat::toFormattedString()
+     *        to format output by \PhpOffice\PhpSpreadsheet\Writer\Html and \PhpOffice\PhpSpreadsheet\Writer\Pdf.
+     *
+     * @param string $pValue Character for currency code
+     */
+    public static function setCurrencyCode($pValue): void
+    {
+        self::$currencyCode = $pValue;
+    }
+
+    /**
+     * Convert SYLK encoded string to UTF-8.
+     *
+     * @param string $pValue
+     *
+     * @return string UTF-8 encoded string
+     */
+    public static function SYLKtoUTF8($pValue)
+    {
+        self::buildCharacterSets();
+
+        // If there is no escape character in the string there is nothing to do
+        if (strpos($pValue, '') === false) {
+            return $pValue;
+        }
+
+        foreach (self::$SYLKCharacters as $k => $v) {
+            $pValue = str_replace($k, $v, $pValue);
+        }
+
+        return $pValue;
+    }
+
+    /**
+     * Retrieve any leading numeric part of a string, or return the full string if no leading numeric
+     * (handles basic integer or float, but not exponent or non decimal).
+     *
+     * @param string $value
+     *
+     * @return mixed string or only the leading numeric part of the string
+     */
+    public static function testStringAsNumeric($value)
+    {
+        if (is_numeric($value)) {
+            return $value;
+        }
+        $v = (float) $value;
+
+        return (is_numeric(substr($value, 0, strlen($v)))) ? $v : $value;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/TimeZone.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/TimeZone.php
new file mode 100755
index 0000000..43fd365
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/TimeZone.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared;
+
+use DateTimeZone;
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+
+class TimeZone
+{
+    /**
+     * Default Timezone used for date/time conversions.
+     *
+     * @var string
+     */
+    protected static $timezone = 'UTC';
+
+    /**
+     * Validate a Timezone name.
+     *
+     * @param string $timezone Time zone (e.g. 'Europe/London')
+     *
+     * @return bool Success or failure
+     */
+    private static function validateTimeZone($timezone)
+    {
+        return in_array($timezone, DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC));
+    }
+
+    /**
+     * Set the Default Timezone used for date/time conversions.
+     *
+     * @param string $timezone Time zone (e.g. 'Europe/London')
+     *
+     * @return bool Success or failure
+     */
+    public static function setTimeZone($timezone)
+    {
+        if (self::validateTimezone($timezone)) {
+            self::$timezone = $timezone;
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Return the Default Timezone used for date/time conversions.
+     *
+     * @return string Timezone (e.g. 'Europe/London')
+     */
+    public static function getTimeZone()
+    {
+        return self::$timezone;
+    }
+
+    /**
+     *    Return the Timezone offset used for date/time conversions to/from UST
+     * This requires both the timezone and the calculated date/time to allow for local DST.
+     *
+     * @param string $timezone The timezone for finding the adjustment to UST
+     * @param int $timestamp PHP date/time value
+     *
+     * @return int Number of seconds for timezone adjustment
+     */
+    public static function getTimeZoneAdjustment($timezone, $timestamp)
+    {
+        if ($timezone !== null) {
+            if (!self::validateTimezone($timezone)) {
+                throw new PhpSpreadsheetException('Invalid timezone ' . $timezone);
+            }
+        } else {
+            $timezone = self::$timezone;
+        }
+
+        $objTimezone = new DateTimeZone($timezone);
+        $transitions = $objTimezone->getTransitions($timestamp, $timestamp);
+
+        return (count($transitions) > 0) ? $transitions[0]['offset'] : 0;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/BestFit.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/BestFit.php
new file mode 100755
index 0000000..c949972
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/BestFit.php
@@ -0,0 +1,463 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared\Trend;
+
+class BestFit
+{
+    /**
+     * Indicator flag for a calculation error.
+     *
+     * @var bool
+     */
+    protected $error = false;
+
+    /**
+     * Algorithm type to use for best-fit.
+     *
+     * @var string
+     */
+    protected $bestFitType = 'undetermined';
+
+    /**
+     * Number of entries in the sets of x- and y-value arrays.
+     *
+     * @var int
+     */
+    protected $valueCount = 0;
+
+    /**
+     * X-value dataseries of values.
+     *
+     * @var float[]
+     */
+    protected $xValues = [];
+
+    /**
+     * Y-value dataseries of values.
+     *
+     * @var float[]
+     */
+    protected $yValues = [];
+
+    /**
+     * Flag indicating whether values should be adjusted to Y=0.
+     *
+     * @var bool
+     */
+    protected $adjustToZero = false;
+
+    /**
+     * Y-value series of best-fit values.
+     *
+     * @var float[]
+     */
+    protected $yBestFitValues = [];
+
+    protected $goodnessOfFit = 1;
+
+    protected $stdevOfResiduals = 0;
+
+    protected $covariance = 0;
+
+    protected $correlation = 0;
+
+    protected $SSRegression = 0;
+
+    protected $SSResiduals = 0;
+
+    protected $DFResiduals = 0;
+
+    protected $f = 0;
+
+    protected $slope = 0;
+
+    protected $slopeSE = 0;
+
+    protected $intersect = 0;
+
+    protected $intersectSE = 0;
+
+    protected $xOffset = 0;
+
+    protected $yOffset = 0;
+
+    public function getError()
+    {
+        return $this->error;
+    }
+
+    public function getBestFitType()
+    {
+        return $this->bestFitType;
+    }
+
+    /**
+     * Return the Y-Value for a specified value of X.
+     *
+     * @param float $xValue X-Value
+     *
+     * @return bool Y-Value
+     */
+    public function getValueOfYForX($xValue)
+    {
+        return false;
+    }
+
+    /**
+     * Return the X-Value for a specified value of Y.
+     *
+     * @param float $yValue Y-Value
+     *
+     * @return bool X-Value
+     */
+    public function getValueOfXForY($yValue)
+    {
+        return false;
+    }
+
+    /**
+     * Return the original set of X-Values.
+     *
+     * @return float[] X-Values
+     */
+    public function getXValues()
+    {
+        return $this->xValues;
+    }
+
+    /**
+     * Return the Equation of the best-fit line.
+     *
+     * @param int $dp Number of places of decimal precision to display
+     *
+     * @return bool
+     */
+    public function getEquation($dp = 0)
+    {
+        return false;
+    }
+
+    /**
+     * Return the Slope of the line.
+     *
+     * @param int $dp Number of places of decimal precision to display
+     *
+     * @return float
+     */
+    public function getSlope($dp = 0)
+    {
+        if ($dp != 0) {
+            return round($this->slope, $dp);
+        }
+
+        return $this->slope;
+    }
+
+    /**
+     * Return the standard error of the Slope.
+     *
+     * @param int $dp Number of places of decimal precision to display
+     *
+     * @return float
+     */
+    public function getSlopeSE($dp = 0)
+    {
+        if ($dp != 0) {
+            return round($this->slopeSE, $dp);
+        }
+
+        return $this->slopeSE;
+    }
+
+    /**
+     * Return the Value of X where it intersects Y = 0.
+     *
+     * @param int $dp Number of places of decimal precision to display
+     *
+     * @return float
+     */
+    public function getIntersect($dp = 0)
+    {
+        if ($dp != 0) {
+            return round($this->intersect, $dp);
+        }
+
+        return $this->intersect;
+    }
+
+    /**
+     * Return the standard error of the Intersect.
+     *
+     * @param int $dp Number of places of decimal precision to display
+     *
+     * @return float
+     */
+    public function getIntersectSE($dp = 0)
+    {
+        if ($dp != 0) {
+            return round($this->intersectSE, $dp);
+        }
+
+        return $this->intersectSE;
+    }
+
+    /**
+     * Return the goodness of fit for this regression.
+     *
+     * @param int $dp Number of places of decimal precision to return
+     *
+     * @return float
+     */
+    public function getGoodnessOfFit($dp = 0)
+    {
+        if ($dp != 0) {
+            return round($this->goodnessOfFit, $dp);
+        }
+
+        return $this->goodnessOfFit;
+    }
+
+    /**
+     * Return the goodness of fit for this regression.
+     *
+     * @param int $dp Number of places of decimal precision to return
+     *
+     * @return float
+     */
+    public function getGoodnessOfFitPercent($dp = 0)
+    {
+        if ($dp != 0) {
+            return round($this->goodnessOfFit * 100, $dp);
+        }
+
+        return $this->goodnessOfFit * 100;
+    }
+
+    /**
+     * Return the standard deviation of the residuals for this regression.
+     *
+     * @param int $dp Number of places of decimal precision to return
+     *
+     * @return float
+     */
+    public function getStdevOfResiduals($dp = 0)
+    {
+        if ($dp != 0) {
+            return round($this->stdevOfResiduals, $dp);
+        }
+
+        return $this->stdevOfResiduals;
+    }
+
+    /**
+     * @param int $dp Number of places of decimal precision to return
+     *
+     * @return float
+     */
+    public function getSSRegression($dp = 0)
+    {
+        if ($dp != 0) {
+            return round($this->SSRegression, $dp);
+        }
+
+        return $this->SSRegression;
+    }
+
+    /**
+     * @param int $dp Number of places of decimal precision to return
+     *
+     * @return float
+     */
+    public function getSSResiduals($dp = 0)
+    {
+        if ($dp != 0) {
+            return round($this->SSResiduals, $dp);
+        }
+
+        return $this->SSResiduals;
+    }
+
+    /**
+     * @param int $dp Number of places of decimal precision to return
+     *
+     * @return float
+     */
+    public function getDFResiduals($dp = 0)
+    {
+        if ($dp != 0) {
+            return round($this->DFResiduals, $dp);
+        }
+
+        return $this->DFResiduals;
+    }
+
+    /**
+     * @param int $dp Number of places of decimal precision to return
+     *
+     * @return float
+     */
+    public function getF($dp = 0)
+    {
+        if ($dp != 0) {
+            return round($this->f, $dp);
+        }
+
+        return $this->f;
+    }
+
+    /**
+     * @param int $dp Number of places of decimal precision to return
+     *
+     * @return float
+     */
+    public function getCovariance($dp = 0)
+    {
+        if ($dp != 0) {
+            return round($this->covariance, $dp);
+        }
+
+        return $this->covariance;
+    }
+
+    /**
+     * @param int $dp Number of places of decimal precision to return
+     *
+     * @return float
+     */
+    public function getCorrelation($dp = 0)
+    {
+        if ($dp != 0) {
+            return round($this->correlation, $dp);
+        }
+
+        return $this->correlation;
+    }
+
+    /**
+     * @return float[]
+     */
+    public function getYBestFitValues()
+    {
+        return $this->yBestFitValues;
+    }
+
+    protected function calculateGoodnessOfFit($sumX, $sumY, $sumX2, $sumY2, $sumXY, $meanX, $meanY, $const): void
+    {
+        $SSres = $SScov = $SScor = $SStot = $SSsex = 0.0;
+        foreach ($this->xValues as $xKey => $xValue) {
+            $bestFitY = $this->yBestFitValues[$xKey] = $this->getValueOfYForX($xValue);
+
+            $SSres += ($this->yValues[$xKey] - $bestFitY) * ($this->yValues[$xKey] - $bestFitY);
+            if ($const) {
+                $SStot += ($this->yValues[$xKey] - $meanY) * ($this->yValues[$xKey] - $meanY);
+            } else {
+                $SStot += $this->yValues[$xKey] * $this->yValues[$xKey];
+            }
+            $SScov += ($this->xValues[$xKey] - $meanX) * ($this->yValues[$xKey] - $meanY);
+            if ($const) {
+                $SSsex += ($this->xValues[$xKey] - $meanX) * ($this->xValues[$xKey] - $meanX);
+            } else {
+                $SSsex += $this->xValues[$xKey] * $this->xValues[$xKey];
+            }
+        }
+
+        $this->SSResiduals = $SSres;
+        $this->DFResiduals = $this->valueCount - 1 - $const;
+
+        if ($this->DFResiduals == 0.0) {
+            $this->stdevOfResiduals = 0.0;
+        } else {
+            $this->stdevOfResiduals = sqrt($SSres / $this->DFResiduals);
+        }
+        if (($SStot == 0.0) || ($SSres == $SStot)) {
+            $this->goodnessOfFit = 1;
+        } else {
+            $this->goodnessOfFit = 1 - ($SSres / $SStot);
+        }
+
+        $this->SSRegression = $this->goodnessOfFit * $SStot;
+        $this->covariance = $SScov / $this->valueCount;
+        $this->correlation = ($this->valueCount * $sumXY - $sumX * $sumY) / sqrt(($this->valueCount * $sumX2 - $sumX ** 2) * ($this->valueCount * $sumY2 - $sumY ** 2));
+        $this->slopeSE = $this->stdevOfResiduals / sqrt($SSsex);
+        $this->intersectSE = $this->stdevOfResiduals * sqrt(1 / ($this->valueCount - ($sumX * $sumX) / $sumX2));
+        if ($this->SSResiduals != 0.0) {
+            if ($this->DFResiduals == 0.0) {
+                $this->f = 0.0;
+            } else {
+                $this->f = $this->SSRegression / ($this->SSResiduals / $this->DFResiduals);
+            }
+        } else {
+            if ($this->DFResiduals == 0.0) {
+                $this->f = 0.0;
+            } else {
+                $this->f = $this->SSRegression / $this->DFResiduals;
+            }
+        }
+    }
+
+    /**
+     * @param float[] $yValues
+     * @param float[] $xValues
+     * @param bool $const
+     */
+    protected function leastSquareFit(array $yValues, array $xValues, $const): void
+    {
+        // calculate sums
+        $x_sum = array_sum($xValues);
+        $y_sum = array_sum($yValues);
+        $meanX = $x_sum / $this->valueCount;
+        $meanY = $y_sum / $this->valueCount;
+        $mBase = $mDivisor = $xx_sum = $xy_sum = $yy_sum = 0.0;
+        for ($i = 0; $i < $this->valueCount; ++$i) {
+            $xy_sum += $xValues[$i] * $yValues[$i];
+            $xx_sum += $xValues[$i] * $xValues[$i];
+            $yy_sum += $yValues[$i] * $yValues[$i];
+
+            if ($const) {
+                $mBase += ($xValues[$i] - $meanX) * ($yValues[$i] - $meanY);
+                $mDivisor += ($xValues[$i] - $meanX) * ($xValues[$i] - $meanX);
+            } else {
+                $mBase += $xValues[$i] * $yValues[$i];
+                $mDivisor += $xValues[$i] * $xValues[$i];
+            }
+        }
+
+        // calculate slope
+        $this->slope = $mBase / $mDivisor;
+
+        // calculate intersect
+        if ($const) {
+            $this->intersect = $meanY - ($this->slope * $meanX);
+        } else {
+            $this->intersect = 0;
+        }
+
+        $this->calculateGoodnessOfFit($x_sum, $y_sum, $xx_sum, $yy_sum, $xy_sum, $meanX, $meanY, $const);
+    }
+
+    /**
+     * Define the regression.
+     *
+     * @param float[] $yValues The set of Y-values for this regression
+     * @param float[] $xValues The set of X-values for this regression
+     * @param bool $const
+     */
+    public function __construct($yValues, $xValues = [], $const = true)
+    {
+        //    Calculate number of points
+        $nY = count($yValues);
+        $nX = count($xValues);
+
+        //    Define X Values if necessary
+        if ($nX == 0) {
+            $xValues = range(1, $nY);
+        } elseif ($nY != $nX) {
+            //    Ensure both arrays of points are the same size
+            $this->error = true;
+        }
+
+        $this->valueCount = $nY;
+        $this->xValues = $xValues;
+        $this->yValues = $yValues;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php
new file mode 100755
index 0000000..82866de
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php
@@ -0,0 +1,122 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared\Trend;
+
+class ExponentialBestFit extends BestFit
+{
+    /**
+     * Algorithm type to use for best-fit
+     * (Name of this Trend class).
+     *
+     * @var string
+     */
+    protected $bestFitType = 'exponential';
+
+    /**
+     * Return the Y-Value for a specified value of X.
+     *
+     * @param float $xValue X-Value
+     *
+     * @return float Y-Value
+     */
+    public function getValueOfYForX($xValue)
+    {
+        return $this->getIntersect() * $this->getSlope() ** ($xValue - $this->xOffset);
+    }
+
+    /**
+     * Return the X-Value for a specified value of Y.
+     *
+     * @param float $yValue Y-Value
+     *
+     * @return float X-Value
+     */
+    public function getValueOfXForY($yValue)
+    {
+        return log(($yValue + $this->yOffset) / $this->getIntersect()) / log($this->getSlope());
+    }
+
+    /**
+     * Return the Equation of the best-fit line.
+     *
+     * @param int $dp Number of places of decimal precision to display
+     *
+     * @return string
+     */
+    public function getEquation($dp = 0)
+    {
+        $slope = $this->getSlope($dp);
+        $intersect = $this->getIntersect($dp);
+
+        return 'Y = ' . $intersect . ' * ' . $slope . '^X';
+    }
+
+    /**
+     * Return the Slope of the line.
+     *
+     * @param int $dp Number of places of decimal precision to display
+     *
+     * @return float
+     */
+    public function getSlope($dp = 0)
+    {
+        if ($dp != 0) {
+            return round(exp($this->slope), $dp);
+        }
+
+        return exp($this->slope);
+    }
+
+    /**
+     * Return the Value of X where it intersects Y = 0.
+     *
+     * @param int $dp Number of places of decimal precision to display
+     *
+     * @return float
+     */
+    public function getIntersect($dp = 0)
+    {
+        if ($dp != 0) {
+            return round(exp($this->intersect), $dp);
+        }
+
+        return exp($this->intersect);
+    }
+
+    /**
+     * Execute the regression and calculate the goodness of fit for a set of X and Y data values.
+     *
+     * @param float[] $yValues The set of Y-values for this regression
+     * @param float[] $xValues The set of X-values for this regression
+     * @param bool $const
+     */
+    private function exponentialRegression($yValues, $xValues, $const): void
+    {
+        foreach ($yValues as &$value) {
+            if ($value < 0.0) {
+                $value = 0 - log(abs($value));
+            } elseif ($value > 0.0) {
+                $value = log($value);
+            }
+        }
+        unset($value);
+
+        $this->leastSquareFit($yValues, $xValues, $const);
+    }
+
+    /**
+     * Define the regression and calculate the goodness of fit for a set of X and Y data values.
+     *
+     * @param float[] $yValues The set of Y-values for this regression
+     * @param float[] $xValues The set of X-values for this regression
+     * @param bool $const
+     */
+    public function __construct($yValues, $xValues = [], $const = true)
+    {
+        parent::__construct($yValues, $xValues);
+
+        if (!$this->error) {
+            $this->exponentialRegression($yValues, $xValues, $const);
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php
new file mode 100755
index 0000000..26a562c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared\Trend;
+
+class LinearBestFit extends BestFit
+{
+    /**
+     * Algorithm type to use for best-fit
+     * (Name of this Trend class).
+     *
+     * @var string
+     */
+    protected $bestFitType = 'linear';
+
+    /**
+     * Return the Y-Value for a specified value of X.
+     *
+     * @param float $xValue X-Value
+     *
+     * @return float Y-Value
+     */
+    public function getValueOfYForX($xValue)
+    {
+        return $this->getIntersect() + $this->getSlope() * $xValue;
+    }
+
+    /**
+     * Return the X-Value for a specified value of Y.
+     *
+     * @param float $yValue Y-Value
+     *
+     * @return float X-Value
+     */
+    public function getValueOfXForY($yValue)
+    {
+        return ($yValue - $this->getIntersect()) / $this->getSlope();
+    }
+
+    /**
+     * Return the Equation of the best-fit line.
+     *
+     * @param int $dp Number of places of decimal precision to display
+     *
+     * @return string
+     */
+    public function getEquation($dp = 0)
+    {
+        $slope = $this->getSlope($dp);
+        $intersect = $this->getIntersect($dp);
+
+        return 'Y = ' . $intersect . ' + ' . $slope . ' * X';
+    }
+
+    /**
+     * Execute the regression and calculate the goodness of fit for a set of X and Y data values.
+     *
+     * @param float[] $yValues The set of Y-values for this regression
+     * @param float[] $xValues The set of X-values for this regression
+     * @param bool $const
+     */
+    private function linearRegression($yValues, $xValues, $const): void
+    {
+        $this->leastSquareFit($yValues, $xValues, $const);
+    }
+
+    /**
+     * Define the regression and calculate the goodness of fit for a set of X and Y data values.
+     *
+     * @param float[] $yValues The set of Y-values for this regression
+     * @param float[] $xValues The set of X-values for this regression
+     * @param bool $const
+     */
+    public function __construct($yValues, $xValues = [], $const = true)
+    {
+        parent::__construct($yValues, $xValues);
+
+        if (!$this->error) {
+            $this->linearRegression($yValues, $xValues, $const);
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php
new file mode 100755
index 0000000..c469067
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared\Trend;
+
+class LogarithmicBestFit extends BestFit
+{
+    /**
+     * Algorithm type to use for best-fit
+     * (Name of this Trend class).
+     *
+     * @var string
+     */
+    protected $bestFitType = 'logarithmic';
+
+    /**
+     * Return the Y-Value for a specified value of X.
+     *
+     * @param float $xValue X-Value
+     *
+     * @return float Y-Value
+     */
+    public function getValueOfYForX($xValue)
+    {
+        return $this->getIntersect() + $this->getSlope() * log($xValue - $this->xOffset);
+    }
+
+    /**
+     * Return the X-Value for a specified value of Y.
+     *
+     * @param float $yValue Y-Value
+     *
+     * @return float X-Value
+     */
+    public function getValueOfXForY($yValue)
+    {
+        return exp(($yValue - $this->getIntersect()) / $this->getSlope());
+    }
+
+    /**
+     * Return the Equation of the best-fit line.
+     *
+     * @param int $dp Number of places of decimal precision to display
+     *
+     * @return string
+     */
+    public function getEquation($dp = 0)
+    {
+        $slope = $this->getSlope($dp);
+        $intersect = $this->getIntersect($dp);
+
+        return 'Y = ' . $intersect . ' + ' . $slope . ' * log(X)';
+    }
+
+    /**
+     * Execute the regression and calculate the goodness of fit for a set of X and Y data values.
+     *
+     * @param float[] $yValues The set of Y-values for this regression
+     * @param float[] $xValues The set of X-values for this regression
+     * @param bool $const
+     */
+    private function logarithmicRegression($yValues, $xValues, $const): void
+    {
+        foreach ($xValues as &$value) {
+            if ($value < 0.0) {
+                $value = 0 - log(abs($value));
+            } elseif ($value > 0.0) {
+                $value = log($value);
+            }
+        }
+        unset($value);
+
+        $this->leastSquareFit($yValues, $xValues, $const);
+    }
+
+    /**
+     * Define the regression and calculate the goodness of fit for a set of X and Y data values.
+     *
+     * @param float[] $yValues The set of Y-values for this regression
+     * @param float[] $xValues The set of X-values for this regression
+     * @param bool $const
+     */
+    public function __construct($yValues, $xValues = [], $const = true)
+    {
+        parent::__construct($yValues, $xValues);
+
+        if (!$this->error) {
+            $this->logarithmicRegression($yValues, $xValues, $const);
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php
new file mode 100755
index 0000000..d959edd
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php
@@ -0,0 +1,200 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared\Trend;
+
+use PhpOffice\PhpSpreadsheet\Shared\JAMA\Matrix;
+
+class PolynomialBestFit extends BestFit
+{
+    /**
+     * Algorithm type to use for best-fit
+     * (Name of this Trend class).
+     *
+     * @var string
+     */
+    protected $bestFitType = 'polynomial';
+
+    /**
+     * Polynomial order.
+     *
+     * @var int
+     */
+    protected $order = 0;
+
+    /**
+     * Return the order of this polynomial.
+     *
+     * @return int
+     */
+    public function getOrder()
+    {
+        return $this->order;
+    }
+
+    /**
+     * Return the Y-Value for a specified value of X.
+     *
+     * @param float $xValue X-Value
+     *
+     * @return float Y-Value
+     */
+    public function getValueOfYForX($xValue)
+    {
+        $retVal = $this->getIntersect();
+        $slope = $this->getSlope();
+        foreach ($slope as $key => $value) {
+            if ($value != 0.0) {
+                $retVal += $value * $xValue ** ($key + 1);
+            }
+        }
+
+        return $retVal;
+    }
+
+    /**
+     * Return the X-Value for a specified value of Y.
+     *
+     * @param float $yValue Y-Value
+     *
+     * @return float X-Value
+     */
+    public function getValueOfXForY($yValue)
+    {
+        return ($yValue - $this->getIntersect()) / $this->getSlope();
+    }
+
+    /**
+     * Return the Equation of the best-fit line.
+     *
+     * @param int $dp Number of places of decimal precision to display
+     *
+     * @return string
+     */
+    public function getEquation($dp = 0)
+    {
+        $slope = $this->getSlope($dp);
+        $intersect = $this->getIntersect($dp);
+
+        $equation = 'Y = ' . $intersect;
+        foreach ($slope as $key => $value) {
+            if ($value != 0.0) {
+                $equation .= ' + ' . $value . ' * X';
+                if ($key > 0) {
+                    $equation .= '^' . ($key + 1);
+                }
+            }
+        }
+
+        return $equation;
+    }
+
+    /**
+     * Return the Slope of the line.
+     *
+     * @param int $dp Number of places of decimal precision to display
+     *
+     * @return string
+     */
+    public function getSlope($dp = 0)
+    {
+        if ($dp != 0) {
+            $coefficients = [];
+            foreach ($this->slope as $coefficient) {
+                $coefficients[] = round($coefficient, $dp);
+            }
+
+            return $coefficients;
+        }
+
+        return $this->slope;
+    }
+
+    public function getCoefficients($dp = 0)
+    {
+        return array_merge([$this->getIntersect($dp)], $this->getSlope($dp));
+    }
+
+    /**
+     * Execute the regression and calculate the goodness of fit for a set of X and Y data values.
+     *
+     * @param int $order Order of Polynomial for this regression
+     * @param float[] $yValues The set of Y-values for this regression
+     * @param float[] $xValues The set of X-values for this regression
+     */
+    private function polynomialRegression($order, $yValues, $xValues): void
+    {
+        // calculate sums
+        $x_sum = array_sum($xValues);
+        $y_sum = array_sum($yValues);
+        $xx_sum = $xy_sum = $yy_sum = 0;
+        for ($i = 0; $i < $this->valueCount; ++$i) {
+            $xy_sum += $xValues[$i] * $yValues[$i];
+            $xx_sum += $xValues[$i] * $xValues[$i];
+            $yy_sum += $yValues[$i] * $yValues[$i];
+        }
+        /*
+         *    This routine uses logic from the PHP port of polyfit version 0.1
+         *    written by Michael Bommarito and Paul Meagher
+         *
+         *    The function fits a polynomial function of order $order through
+         *    a series of x-y data points using least squares.
+         *
+         */
+        $A = [];
+        $B = [];
+        for ($i = 0; $i < $this->valueCount; ++$i) {
+            for ($j = 0; $j <= $order; ++$j) {
+                $A[$i][$j] = $xValues[$i] ** $j;
+            }
+        }
+        for ($i = 0; $i < $this->valueCount; ++$i) {
+            $B[$i] = [$yValues[$i]];
+        }
+        $matrixA = new Matrix($A);
+        $matrixB = new Matrix($B);
+        $C = $matrixA->solve($matrixB);
+
+        $coefficients = [];
+        for ($i = 0; $i < $C->getRowDimension(); ++$i) {
+            $r = $C->get($i, 0);
+            if (abs($r) <= 10 ** (-9)) {
+                $r = 0;
+            }
+            $coefficients[] = $r;
+        }
+
+        $this->intersect = array_shift($coefficients);
+        $this->slope = $coefficients;
+
+        $this->calculateGoodnessOfFit($x_sum, $y_sum, $xx_sum, $yy_sum, $xy_sum, 0, 0, 0);
+        foreach ($this->xValues as $xKey => $xValue) {
+            $this->yBestFitValues[$xKey] = $this->getValueOfYForX($xValue);
+        }
+    }
+
+    /**
+     * Define the regression and calculate the goodness of fit for a set of X and Y data values.
+     *
+     * @param int $order Order of Polynomial for this regression
+     * @param float[] $yValues The set of Y-values for this regression
+     * @param float[] $xValues The set of X-values for this regression
+     * @param bool $const
+     */
+    public function __construct($order, $yValues, $xValues = [], $const = true)
+    {
+        parent::__construct($yValues, $xValues);
+
+        if (!$this->error) {
+            if ($order < $this->valueCount) {
+                $this->bestFitType .= '_' . $order;
+                $this->order = $order;
+                $this->polynomialRegression($order, $yValues, $xValues);
+                if (($this->getGoodnessOfFit() < 0.0) || ($this->getGoodnessOfFit() > 1.0)) {
+                    $this->error = true;
+                }
+            } else {
+                $this->error = true;
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php
new file mode 100755
index 0000000..c53eab6
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php
@@ -0,0 +1,114 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared\Trend;
+
+class PowerBestFit extends BestFit
+{
+    /**
+     * Algorithm type to use for best-fit
+     * (Name of this Trend class).
+     *
+     * @var string
+     */
+    protected $bestFitType = 'power';
+
+    /**
+     * Return the Y-Value for a specified value of X.
+     *
+     * @param float $xValue X-Value
+     *
+     * @return float Y-Value
+     */
+    public function getValueOfYForX($xValue)
+    {
+        return $this->getIntersect() * ($xValue - $this->xOffset) ** $this->getSlope();
+    }
+
+    /**
+     * Return the X-Value for a specified value of Y.
+     *
+     * @param float $yValue Y-Value
+     *
+     * @return float X-Value
+     */
+    public function getValueOfXForY($yValue)
+    {
+        return (($yValue + $this->yOffset) / $this->getIntersect()) ** (1 / $this->getSlope());
+    }
+
+    /**
+     * Return the Equation of the best-fit line.
+     *
+     * @param int $dp Number of places of decimal precision to display
+     *
+     * @return string
+     */
+    public function getEquation($dp = 0)
+    {
+        $slope = $this->getSlope($dp);
+        $intersect = $this->getIntersect($dp);
+
+        return 'Y = ' . $intersect . ' * X^' . $slope;
+    }
+
+    /**
+     * Return the Value of X where it intersects Y = 0.
+     *
+     * @param int $dp Number of places of decimal precision to display
+     *
+     * @return float
+     */
+    public function getIntersect($dp = 0)
+    {
+        if ($dp != 0) {
+            return round(exp($this->intersect), $dp);
+        }
+
+        return exp($this->intersect);
+    }
+
+    /**
+     * Execute the regression and calculate the goodness of fit for a set of X and Y data values.
+     *
+     * @param float[] $yValues The set of Y-values for this regression
+     * @param float[] $xValues The set of X-values for this regression
+     * @param bool $const
+     */
+    private function powerRegression($yValues, $xValues, $const): void
+    {
+        foreach ($xValues as &$value) {
+            if ($value < 0.0) {
+                $value = 0 - log(abs($value));
+            } elseif ($value > 0.0) {
+                $value = log($value);
+            }
+        }
+        unset($value);
+        foreach ($yValues as &$value) {
+            if ($value < 0.0) {
+                $value = 0 - log(abs($value));
+            } elseif ($value > 0.0) {
+                $value = log($value);
+            }
+        }
+        unset($value);
+
+        $this->leastSquareFit($yValues, $xValues, $const);
+    }
+
+    /**
+     * Define the regression and calculate the goodness of fit for a set of X and Y data values.
+     *
+     * @param float[] $yValues The set of Y-values for this regression
+     * @param float[] $xValues The set of X-values for this regression
+     * @param bool $const
+     */
+    public function __construct($yValues, $xValues = [], $const = true)
+    {
+        parent::__construct($yValues, $xValues);
+
+        if (!$this->error) {
+            $this->powerRegression($yValues, $xValues, $const);
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/Trend.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/Trend.php
new file mode 100755
index 0000000..1b7b390
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/Trend.php
@@ -0,0 +1,120 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared\Trend;
+
+class Trend
+{
+    const TREND_LINEAR = 'Linear';
+    const TREND_LOGARITHMIC = 'Logarithmic';
+    const TREND_EXPONENTIAL = 'Exponential';
+    const TREND_POWER = 'Power';
+    const TREND_POLYNOMIAL_2 = 'Polynomial_2';
+    const TREND_POLYNOMIAL_3 = 'Polynomial_3';
+    const TREND_POLYNOMIAL_4 = 'Polynomial_4';
+    const TREND_POLYNOMIAL_5 = 'Polynomial_5';
+    const TREND_POLYNOMIAL_6 = 'Polynomial_6';
+    const TREND_BEST_FIT = 'Bestfit';
+    const TREND_BEST_FIT_NO_POLY = 'Bestfit_no_Polynomials';
+
+    /**
+     * Names of the best-fit Trend analysis methods.
+     *
+     * @var string[]
+     */
+    private static $trendTypes = [
+        self::TREND_LINEAR,
+        self::TREND_LOGARITHMIC,
+        self::TREND_EXPONENTIAL,
+        self::TREND_POWER,
+    ];
+
+    /**
+     * Names of the best-fit Trend polynomial orders.
+     *
+     * @var string[]
+     */
+    private static $trendTypePolynomialOrders = [
+        self::TREND_POLYNOMIAL_2,
+        self::TREND_POLYNOMIAL_3,
+        self::TREND_POLYNOMIAL_4,
+        self::TREND_POLYNOMIAL_5,
+        self::TREND_POLYNOMIAL_6,
+    ];
+
+    /**
+     * Cached results for each method when trying to identify which provides the best fit.
+     *
+     * @var bestFit[]
+     */
+    private static $trendCache = [];
+
+    public static function calculate($trendType = self::TREND_BEST_FIT, $yValues = [], $xValues = [], $const = true)
+    {
+        //    Calculate number of points in each dataset
+        $nY = count($yValues);
+        $nX = count($xValues);
+
+        //    Define X Values if necessary
+        if ($nX == 0) {
+            $xValues = range(1, $nY);
+            $nX = $nY;
+        } elseif ($nY != $nX) {
+            //    Ensure both arrays of points are the same size
+            trigger_error('Trend(): Number of elements in coordinate arrays do not match.', E_USER_ERROR);
+        }
+
+        $key = md5($trendType . $const . serialize($yValues) . serialize($xValues));
+        //    Determine which Trend method has been requested
+        switch ($trendType) {
+            //    Instantiate and return the class for the requested Trend method
+            case self::TREND_LINEAR:
+            case self::TREND_LOGARITHMIC:
+            case self::TREND_EXPONENTIAL:
+            case self::TREND_POWER:
+                if (!isset(self::$trendCache[$key])) {
+                    $className = '\PhpOffice\PhpSpreadsheet\Shared\Trend\\' . $trendType . 'BestFit';
+                    self::$trendCache[$key] = new $className($yValues, $xValues, $const);
+                }
+
+                return self::$trendCache[$key];
+            case self::TREND_POLYNOMIAL_2:
+            case self::TREND_POLYNOMIAL_3:
+            case self::TREND_POLYNOMIAL_4:
+            case self::TREND_POLYNOMIAL_5:
+            case self::TREND_POLYNOMIAL_6:
+                if (!isset(self::$trendCache[$key])) {
+                    $order = substr($trendType, -1);
+                    self::$trendCache[$key] = new PolynomialBestFit($order, $yValues, $xValues, $const);
+                }
+
+                return self::$trendCache[$key];
+            case self::TREND_BEST_FIT:
+            case self::TREND_BEST_FIT_NO_POLY:
+                //    If the request is to determine the best fit regression, then we test each Trend line in turn
+                //    Start by generating an instance of each available Trend method
+                foreach (self::$trendTypes as $trendMethod) {
+                    $className = '\PhpOffice\PhpSpreadsheet\Shared\Trend\\' . $trendType . 'BestFit';
+                    $bestFit[$trendMethod] = new $className($yValues, $xValues, $const);
+                    $bestFitValue[$trendMethod] = $bestFit[$trendMethod]->getGoodnessOfFit();
+                }
+                if ($trendType != self::TREND_BEST_FIT_NO_POLY) {
+                    foreach (self::$trendTypePolynomialOrders as $trendMethod) {
+                        $order = substr($trendMethod, -1);
+                        $bestFit[$trendMethod] = new PolynomialBestFit($order, $yValues, $xValues, $const);
+                        if ($bestFit[$trendMethod]->getError()) {
+                            unset($bestFit[$trendMethod]);
+                        } else {
+                            $bestFitValue[$trendMethod] = $bestFit[$trendMethod]->getGoodnessOfFit();
+                        }
+                    }
+                }
+                //    Determine which of our Trend lines is the best fit, and then we return the instance of that Trend class
+                arsort($bestFitValue);
+                $bestFitType = key($bestFitValue);
+
+                return $bestFit[$bestFitType];
+            default:
+                return false;
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/XMLWriter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/XMLWriter.php
new file mode 100755
index 0000000..4f7a6a0
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/XMLWriter.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared;
+
+class XMLWriter extends \XMLWriter
+{
+    public static $debugEnabled = false;
+
+    /** Temporary storage method */
+    const STORAGE_MEMORY = 1;
+    const STORAGE_DISK = 2;
+
+    /**
+     * Temporary filename.
+     *
+     * @var string
+     */
+    private $tempFileName = '';
+
+    /**
+     * Create a new XMLWriter instance.
+     *
+     * @param int $pTemporaryStorage Temporary storage location
+     * @param string $pTemporaryStorageFolder Temporary storage folder
+     */
+    public function __construct($pTemporaryStorage = self::STORAGE_MEMORY, $pTemporaryStorageFolder = null)
+    {
+        // Open temporary storage
+        if ($pTemporaryStorage == self::STORAGE_MEMORY) {
+            $this->openMemory();
+        } else {
+            // Create temporary filename
+            if ($pTemporaryStorageFolder === null) {
+                $pTemporaryStorageFolder = File::sysGetTempDir();
+            }
+            $this->tempFileName = @tempnam($pTemporaryStorageFolder, 'xml');
+
+            // Open storage
+            if ($this->openUri($this->tempFileName) === false) {
+                // Fallback to memory...
+                $this->openMemory();
+            }
+        }
+
+        // Set default values
+        if (self::$debugEnabled) {
+            $this->setIndent(true);
+        }
+    }
+
+    /**
+     * Destructor.
+     */
+    public function __destruct()
+    {
+        // Unlink temporary files
+        if ($this->tempFileName != '') {
+            @unlink($this->tempFileName);
+        }
+    }
+
+    /**
+     * Get written data.
+     *
+     * @return string
+     */
+    public function getData()
+    {
+        if ($this->tempFileName == '') {
+            return $this->outputMemory(true);
+        }
+        $this->flush();
+
+        return file_get_contents($this->tempFileName);
+    }
+
+    /**
+     * Wrapper method for writeRaw.
+     *
+     * @param string|string[] $text
+     *
+     * @return bool
+     */
+    public function writeRawData($text)
+    {
+        if (is_array($text)) {
+            $text = implode("\n", $text);
+        }
+
+        return $this->writeRaw(htmlspecialchars($text));
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Xls.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Xls.php
new file mode 100755
index 0000000..c9eaf37
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Xls.php
@@ -0,0 +1,279 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Shared;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+
+class Xls
+{
+    /**
+     * Get the width of a column in pixels. We use the relationship y = ceil(7x) where
+     * x is the width in intrinsic Excel units (measuring width in number of normal characters)
+     * This holds for Arial 10.
+     *
+     * @param Worksheet $sheet The sheet
+     * @param string $col The column
+     *
+     * @return int The width in pixels
+     */
+    public static function sizeCol($sheet, $col = 'A')
+    {
+        // default font of the workbook
+        $font = $sheet->getParent()->getDefaultStyle()->getFont();
+
+        $columnDimensions = $sheet->getColumnDimensions();
+
+        // first find the true column width in pixels (uncollapsed and unhidden)
+        if (isset($columnDimensions[$col]) && $columnDimensions[$col]->getWidth() != -1) {
+            // then we have column dimension with explicit width
+            $columnDimension = $columnDimensions[$col];
+            $width = $columnDimension->getWidth();
+            $pixelWidth = Drawing::cellDimensionToPixels($width, $font);
+        } elseif ($sheet->getDefaultColumnDimension()->getWidth() != -1) {
+            // then we have default column dimension with explicit width
+            $defaultColumnDimension = $sheet->getDefaultColumnDimension();
+            $width = $defaultColumnDimension->getWidth();
+            $pixelWidth = Drawing::cellDimensionToPixels($width, $font);
+        } else {
+            // we don't even have any default column dimension. Width depends on default font
+            $pixelWidth = Font::getDefaultColumnWidthByFont($font, true);
+        }
+
+        // now find the effective column width in pixels
+        if (isset($columnDimensions[$col]) && !$columnDimensions[$col]->getVisible()) {
+            $effectivePixelWidth = 0;
+        } else {
+            $effectivePixelWidth = $pixelWidth;
+        }
+
+        return $effectivePixelWidth;
+    }
+
+    /**
+     * Convert the height of a cell from user's units to pixels. By interpolation
+     * the relationship is: y = 4/3x. If the height hasn't been set by the user we
+     * use the default value. If the row is hidden we use a value of zero.
+     *
+     * @param Worksheet $sheet The sheet
+     * @param int $row The row index (1-based)
+     *
+     * @return int The width in pixels
+     */
+    public static function sizeRow($sheet, $row = 1)
+    {
+        // default font of the workbook
+        $font = $sheet->getParent()->getDefaultStyle()->getFont();
+
+        $rowDimensions = $sheet->getRowDimensions();
+
+        // first find the true row height in pixels (uncollapsed and unhidden)
+        if (isset($rowDimensions[$row]) && $rowDimensions[$row]->getRowHeight() != -1) {
+            // then we have a row dimension
+            $rowDimension = $rowDimensions[$row];
+            $rowHeight = $rowDimension->getRowHeight();
+            $pixelRowHeight = (int) ceil(4 * $rowHeight / 3); // here we assume Arial 10
+        } elseif ($sheet->getDefaultRowDimension()->getRowHeight() != -1) {
+            // then we have a default row dimension with explicit height
+            $defaultRowDimension = $sheet->getDefaultRowDimension();
+            $rowHeight = $defaultRowDimension->getRowHeight();
+            $pixelRowHeight = Drawing::pointsToPixels($rowHeight);
+        } else {
+            // we don't even have any default row dimension. Height depends on default font
+            $pointRowHeight = Font::getDefaultRowHeightByFont($font);
+            $pixelRowHeight = Font::fontSizeToPixels($pointRowHeight);
+        }
+
+        // now find the effective row height in pixels
+        if (isset($rowDimensions[$row]) && !$rowDimensions[$row]->getVisible()) {
+            $effectivePixelRowHeight = 0;
+        } else {
+            $effectivePixelRowHeight = $pixelRowHeight;
+        }
+
+        return $effectivePixelRowHeight;
+    }
+
+    /**
+     * Get the horizontal distance in pixels between two anchors
+     * The distanceX is found as sum of all the spanning columns widths minus correction for the two offsets.
+     *
+     * @param string $startColumn
+     * @param int $startOffsetX Offset within start cell measured in 1/1024 of the cell width
+     * @param string $endColumn
+     * @param int $endOffsetX Offset within end cell measured in 1/1024 of the cell width
+     *
+     * @return int Horizontal measured in pixels
+     */
+    public static function getDistanceX(Worksheet $sheet, $startColumn = 'A', $startOffsetX = 0, $endColumn = 'A', $endOffsetX = 0)
+    {
+        $distanceX = 0;
+
+        // add the widths of the spanning columns
+        $startColumnIndex = Coordinate::columnIndexFromString($startColumn);
+        $endColumnIndex = Coordinate::columnIndexFromString($endColumn);
+        for ($i = $startColumnIndex; $i <= $endColumnIndex; ++$i) {
+            $distanceX += self::sizeCol($sheet, Coordinate::stringFromColumnIndex($i));
+        }
+
+        // correct for offsetX in startcell
+        $distanceX -= (int) floor(self::sizeCol($sheet, $startColumn) * $startOffsetX / 1024);
+
+        // correct for offsetX in endcell
+        $distanceX -= (int) floor(self::sizeCol($sheet, $endColumn) * (1 - $endOffsetX / 1024));
+
+        return $distanceX;
+    }
+
+    /**
+     * Get the vertical distance in pixels between two anchors
+     * The distanceY is found as sum of all the spanning rows minus two offsets.
+     *
+     * @param int $startRow (1-based)
+     * @param int $startOffsetY Offset within start cell measured in 1/256 of the cell height
+     * @param int $endRow (1-based)
+     * @param int $endOffsetY Offset within end cell measured in 1/256 of the cell height
+     *
+     * @return int Vertical distance measured in pixels
+     */
+    public static function getDistanceY(Worksheet $sheet, $startRow = 1, $startOffsetY = 0, $endRow = 1, $endOffsetY = 0)
+    {
+        $distanceY = 0;
+
+        // add the widths of the spanning rows
+        for ($row = $startRow; $row <= $endRow; ++$row) {
+            $distanceY += self::sizeRow($sheet, $row);
+        }
+
+        // correct for offsetX in startcell
+        $distanceY -= (int) floor(self::sizeRow($sheet, $startRow) * $startOffsetY / 256);
+
+        // correct for offsetX in endcell
+        $distanceY -= (int) floor(self::sizeRow($sheet, $endRow) * (1 - $endOffsetY / 256));
+
+        return $distanceY;
+    }
+
+    /**
+     * Convert 1-cell anchor coordinates to 2-cell anchor coordinates
+     * This function is ported from PEAR Spreadsheet_Writer_Excel with small modifications.
+     *
+     * Calculate the vertices that define the position of the image as required by
+     * the OBJ record.
+     *
+     *         +------------+------------+
+     *         |     A      |      B     |
+     *   +-----+------------+------------+
+     *   |     |(x1,y1)     |            |
+     *   |  1  |(A1)._______|______      |
+     *   |     |    |              |     |
+     *   |     |    |              |     |
+     *   +-----+----|    BITMAP    |-----+
+     *   |     |    |              |     |
+     *   |  2  |    |______________.     |
+     *   |     |            |        (B2)|
+     *   |     |            |     (x2,y2)|
+     *   +---- +------------+------------+
+     *
+     * Example of a bitmap that covers some of the area from cell A1 to cell B2.
+     *
+     * Based on the width and height of the bitmap we need to calculate 8 vars:
+     *     $col_start, $row_start, $col_end, $row_end, $x1, $y1, $x2, $y2.
+     * The width and height of the cells are also variable and have to be taken into
+     * account.
+     * The values of $col_start and $row_start are passed in from the calling
+     * function. The values of $col_end and $row_end are calculated by subtracting
+     * the width and height of the bitmap from the width and height of the
+     * underlying cells.
+     * The vertices are expressed as a percentage of the underlying cell width as
+     * follows (rhs values are in pixels):
+     *
+     *       x1 = X / W *1024
+     *       y1 = Y / H *256
+     *       x2 = (X-1) / W *1024
+     *       y2 = (Y-1) / H *256
+     *
+     *       Where:  X is distance from the left side of the underlying cell
+     *               Y is distance from the top of the underlying cell
+     *               W is the width of the cell
+     *               H is the height of the cell
+     *
+     * @param Worksheet $sheet
+     * @param string $coordinates E.g. 'A1'
+     * @param int $offsetX Horizontal offset in pixels
+     * @param int $offsetY Vertical offset in pixels
+     * @param int $width Width in pixels
+     * @param int $height Height in pixels
+     *
+     * @return array
+     */
+    public static function oneAnchor2twoAnchor($sheet, $coordinates, $offsetX, $offsetY, $width, $height)
+    {
+        [$column, $row] = Coordinate::coordinateFromString($coordinates);
+        $col_start = Coordinate::columnIndexFromString($column);
+        $row_start = $row - 1;
+
+        $x1 = $offsetX;
+        $y1 = $offsetY;
+
+        // Initialise end cell to the same as the start cell
+        $col_end = $col_start; // Col containing lower right corner of object
+        $row_end = $row_start; // Row containing bottom right corner of object
+
+        // Zero the specified offset if greater than the cell dimensions
+        if ($x1 >= self::sizeCol($sheet, Coordinate::stringFromColumnIndex($col_start))) {
+            $x1 = 0;
+        }
+        if ($y1 >= self::sizeRow($sheet, $row_start + 1)) {
+            $y1 = 0;
+        }
+
+        $width = $width + $x1 - 1;
+        $height = $height + $y1 - 1;
+
+        // Subtract the underlying cell widths to find the end cell of the image
+        while ($width >= self::sizeCol($sheet, Coordinate::stringFromColumnIndex($col_end))) {
+            $width -= self::sizeCol($sheet, Coordinate::stringFromColumnIndex($col_end));
+            ++$col_end;
+        }
+
+        // Subtract the underlying cell heights to find the end cell of the image
+        while ($height >= self::sizeRow($sheet, $row_end + 1)) {
+            $height -= self::sizeRow($sheet, $row_end + 1);
+            ++$row_end;
+        }
+
+        // Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell
+        // with zero height or width.
+        if (self::sizeCol($sheet, Coordinate::stringFromColumnIndex($col_start)) == 0) {
+            return;
+        }
+        if (self::sizeCol($sheet, Coordinate::stringFromColumnIndex($col_end)) == 0) {
+            return;
+        }
+        if (self::sizeRow($sheet, $row_start + 1) == 0) {
+            return;
+        }
+        if (self::sizeRow($sheet, $row_end + 1) == 0) {
+            return;
+        }
+
+        // Convert the pixel values to the percentage value expected by Excel
+        $x1 = $x1 / self::sizeCol($sheet, Coordinate::stringFromColumnIndex($col_start)) * 1024;
+        $y1 = $y1 / self::sizeRow($sheet, $row_start + 1) * 256;
+        $x2 = ($width + 1) / self::sizeCol($sheet, Coordinate::stringFromColumnIndex($col_end)) * 1024; // Distance to right side of object
+        $y2 = ($height + 1) / self::sizeRow($sheet, $row_end + 1) * 256; // Distance to bottom of object
+
+        $startCoordinates = Coordinate::stringFromColumnIndex($col_start) . ($row_start + 1);
+        $endCoordinates = Coordinate::stringFromColumnIndex($col_end) . ($row_end + 1);
+
+        return [
+            'startCoordinates' => $startCoordinates,
+            'startOffsetX' => $x1,
+            'startOffsetY' => $y1,
+            'endCoordinates' => $endCoordinates,
+            'endOffsetX' => $x2,
+            'endOffsetY' => $y2,
+        ];
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Spreadsheet.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Spreadsheet.php
new file mode 100755
index 0000000..322d5f7
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Spreadsheet.php
@@ -0,0 +1,1443 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
+use PhpOffice\PhpSpreadsheet\Style\Style;
+use PhpOffice\PhpSpreadsheet\Worksheet\Iterator;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+
+class Spreadsheet
+{
+    // Allowable values for workbook window visilbity
+    const VISIBILITY_VISIBLE = 'visible';
+    const VISIBILITY_HIDDEN = 'hidden';
+    const VISIBILITY_VERY_HIDDEN = 'veryHidden';
+
+    private static $workbookViewVisibilityValues = [
+        self::VISIBILITY_VISIBLE,
+        self::VISIBILITY_HIDDEN,
+        self::VISIBILITY_VERY_HIDDEN,
+    ];
+
+    /**
+     * Unique ID.
+     *
+     * @var string
+     */
+    private $uniqueID;
+
+    /**
+     * Document properties.
+     *
+     * @var Document\Properties
+     */
+    private $properties;
+
+    /**
+     * Document security.
+     *
+     * @var Document\Security
+     */
+    private $security;
+
+    /**
+     * Collection of Worksheet objects.
+     *
+     * @var Worksheet[]
+     */
+    private $workSheetCollection = [];
+
+    /**
+     * Calculation Engine.
+     *
+     * @var Calculation
+     */
+    private $calculationEngine;
+
+    /**
+     * Active sheet index.
+     *
+     * @var int
+     */
+    private $activeSheetIndex = 0;
+
+    /**
+     * Named ranges.
+     *
+     * @var NamedRange[]
+     */
+    private $namedRanges = [];
+
+    /**
+     * CellXf supervisor.
+     *
+     * @var Style
+     */
+    private $cellXfSupervisor;
+
+    /**
+     * CellXf collection.
+     *
+     * @var Style[]
+     */
+    private $cellXfCollection = [];
+
+    /**
+     * CellStyleXf collection.
+     *
+     * @var Style[]
+     */
+    private $cellStyleXfCollection = [];
+
+    /**
+     * hasMacros : this workbook have macros ?
+     *
+     * @var bool
+     */
+    private $hasMacros = false;
+
+    /**
+     * macrosCode : all macros code as binary data (the vbaProject.bin file, this include form, code,  etc.), null if no macro.
+     *
+     * @var string
+     */
+    private $macrosCode;
+
+    /**
+     * macrosCertificate : if macros are signed, contains binary data vbaProjectSignature.bin file, null if not signed.
+     *
+     * @var string
+     */
+    private $macrosCertificate;
+
+    /**
+     * ribbonXMLData : null if workbook is'nt Excel 2007 or not contain a customized UI.
+     *
+     * @var null|string
+     */
+    private $ribbonXMLData;
+
+    /**
+     * ribbonBinObjects : null if workbook is'nt Excel 2007 or not contain embedded objects (picture(s)) for Ribbon Elements
+     * ignored if $ribbonXMLData is null.
+     *
+     * @var null|array
+     */
+    private $ribbonBinObjects;
+
+    /**
+     * List of unparsed loaded data for export to same format with better compatibility.
+     * It has to be minimized when the library start to support currently unparsed data.
+     *
+     * @var array
+     */
+    private $unparsedLoadedData = [];
+
+    /**
+     * Controls visibility of the horizonal scroll bar in the application.
+     *
+     * @var bool
+     */
+    private $showHorizontalScroll = true;
+
+    /**
+     * Controls visibility of the horizonal scroll bar in the application.
+     *
+     * @var bool
+     */
+    private $showVerticalScroll = true;
+
+    /**
+     * Controls visibility of the sheet tabs in the application.
+     *
+     * @var bool
+     */
+    private $showSheetTabs = true;
+
+    /**
+     * Specifies a boolean value that indicates whether the workbook window
+     * is minimized.
+     *
+     * @var bool
+     */
+    private $minimized = false;
+
+    /**
+     * Specifies a boolean value that indicates whether to group dates
+     * when presenting the user with filtering optiomd in the user
+     * interface.
+     *
+     * @var bool
+     */
+    private $autoFilterDateGrouping = true;
+
+    /**
+     * Specifies the index to the first sheet in the book view.
+     *
+     * @var int
+     */
+    private $firstSheetIndex = 0;
+
+    /**
+     * Specifies the visible status of the workbook.
+     *
+     * @var string
+     */
+    private $visibility = self::VISIBILITY_VISIBLE;
+
+    /**
+     * Specifies the ratio between the workbook tabs bar and the horizontal
+     * scroll bar.  TabRatio is assumed to be out of 1000 of the horizontal
+     * window width.
+     *
+     * @var int
+     */
+    private $tabRatio = 600;
+
+    /**
+     * The workbook has macros ?
+     *
+     * @return bool
+     */
+    public function hasMacros()
+    {
+        return $this->hasMacros;
+    }
+
+    /**
+     * Define if a workbook has macros.
+     *
+     * @param bool $hasMacros true|false
+     */
+    public function setHasMacros($hasMacros): void
+    {
+        $this->hasMacros = (bool) $hasMacros;
+    }
+
+    /**
+     * Set the macros code.
+     *
+     * @param string $macroCode string|null
+     */
+    public function setMacrosCode($macroCode): void
+    {
+        $this->macrosCode = $macroCode;
+        $this->setHasMacros($macroCode !== null);
+    }
+
+    /**
+     * Return the macros code.
+     *
+     * @return null|string
+     */
+    public function getMacrosCode()
+    {
+        return $this->macrosCode;
+    }
+
+    /**
+     * Set the macros certificate.
+     *
+     * @param null|string $certificate
+     */
+    public function setMacrosCertificate($certificate): void
+    {
+        $this->macrosCertificate = $certificate;
+    }
+
+    /**
+     * Is the project signed ?
+     *
+     * @return bool true|false
+     */
+    public function hasMacrosCertificate()
+    {
+        return $this->macrosCertificate !== null;
+    }
+
+    /**
+     * Return the macros certificate.
+     *
+     * @return null|string
+     */
+    public function getMacrosCertificate()
+    {
+        return $this->macrosCertificate;
+    }
+
+    /**
+     * Remove all macros, certificate from spreadsheet.
+     */
+    public function discardMacros(): void
+    {
+        $this->hasMacros = false;
+        $this->macrosCode = null;
+        $this->macrosCertificate = null;
+    }
+
+    /**
+     * set ribbon XML data.
+     *
+     * @param null|mixed $target
+     * @param null|mixed $xmlData
+     */
+    public function setRibbonXMLData($target, $xmlData): void
+    {
+        if ($target !== null && $xmlData !== null) {
+            $this->ribbonXMLData = ['target' => $target, 'data' => $xmlData];
+        } else {
+            $this->ribbonXMLData = null;
+        }
+    }
+
+    /**
+     * retrieve ribbon XML Data.
+     *
+     * return string|null|array
+     *
+     * @param string $what
+     *
+     * @return string
+     */
+    public function getRibbonXMLData($what = 'all') //we need some constants here...
+    {
+        $returnData = null;
+        $what = strtolower($what);
+        switch ($what) {
+            case 'all':
+                $returnData = $this->ribbonXMLData;
+
+                break;
+            case 'target':
+            case 'data':
+                if (is_array($this->ribbonXMLData) && isset($this->ribbonXMLData[$what])) {
+                    $returnData = $this->ribbonXMLData[$what];
+                }
+
+                break;
+        }
+
+        return $returnData;
+    }
+
+    /**
+     * store binaries ribbon objects (pictures).
+     *
+     * @param null|mixed $BinObjectsNames
+     * @param null|mixed $BinObjectsData
+     */
+    public function setRibbonBinObjects($BinObjectsNames, $BinObjectsData): void
+    {
+        if ($BinObjectsNames !== null && $BinObjectsData !== null) {
+            $this->ribbonBinObjects = ['names' => $BinObjectsNames, 'data' => $BinObjectsData];
+        } else {
+            $this->ribbonBinObjects = null;
+        }
+    }
+
+    /**
+     * List of unparsed loaded data for export to same format with better compatibility.
+     * It has to be minimized when the library start to support currently unparsed data.
+     *
+     * @internal
+     *
+     * @return array
+     */
+    public function getUnparsedLoadedData()
+    {
+        return $this->unparsedLoadedData;
+    }
+
+    /**
+     * List of unparsed loaded data for export to same format with better compatibility.
+     * It has to be minimized when the library start to support currently unparsed data.
+     *
+     * @internal
+     */
+    public function setUnparsedLoadedData(array $unparsedLoadedData): void
+    {
+        $this->unparsedLoadedData = $unparsedLoadedData;
+    }
+
+    /**
+     * return the extension of a filename. Internal use for a array_map callback (php<5.3 don't like lambda function).
+     *
+     * @param mixed $path
+     *
+     * @return string
+     */
+    private function getExtensionOnly($path)
+    {
+        return pathinfo($path, PATHINFO_EXTENSION);
+    }
+
+    /**
+     * retrieve Binaries Ribbon Objects.
+     *
+     * @param string $what
+     *
+     * @return null|array
+     */
+    public function getRibbonBinObjects($what = 'all')
+    {
+        $ReturnData = null;
+        $what = strtolower($what);
+        switch ($what) {
+            case 'all':
+                return $this->ribbonBinObjects;
+
+                break;
+            case 'names':
+            case 'data':
+                if (is_array($this->ribbonBinObjects) && isset($this->ribbonBinObjects[$what])) {
+                    $ReturnData = $this->ribbonBinObjects[$what];
+                }
+
+                break;
+            case 'types':
+                if (is_array($this->ribbonBinObjects) &&
+                    isset($this->ribbonBinObjects['data']) && is_array($this->ribbonBinObjects['data'])) {
+                    $tmpTypes = array_keys($this->ribbonBinObjects['data']);
+                    $ReturnData = array_unique(array_map([$this, 'getExtensionOnly'], $tmpTypes));
+                } else {
+                    $ReturnData = []; // the caller want an array... not null if empty
+                }
+
+                break;
+        }
+
+        return $ReturnData;
+    }
+
+    /**
+     * This workbook have a custom UI ?
+     *
+     * @return bool
+     */
+    public function hasRibbon()
+    {
+        return $this->ribbonXMLData !== null;
+    }
+
+    /**
+     * This workbook have additionnal object for the ribbon ?
+     *
+     * @return bool
+     */
+    public function hasRibbonBinObjects()
+    {
+        return $this->ribbonBinObjects !== null;
+    }
+
+    /**
+     * Check if a sheet with a specified code name already exists.
+     *
+     * @param string $pSheetCodeName Name of the worksheet to check
+     *
+     * @return bool
+     */
+    public function sheetCodeNameExists($pSheetCodeName)
+    {
+        return $this->getSheetByCodeName($pSheetCodeName) !== null;
+    }
+
+    /**
+     * Get sheet by code name. Warning : sheet don't have always a code name !
+     *
+     * @param string $pName Sheet name
+     *
+     * @return Worksheet
+     */
+    public function getSheetByCodeName($pName)
+    {
+        $worksheetCount = count($this->workSheetCollection);
+        for ($i = 0; $i < $worksheetCount; ++$i) {
+            if ($this->workSheetCollection[$i]->getCodeName() == $pName) {
+                return $this->workSheetCollection[$i];
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Create a new PhpSpreadsheet with one Worksheet.
+     */
+    public function __construct()
+    {
+        $this->uniqueID = uniqid('', true);
+        $this->calculationEngine = new Calculation($this);
+
+        // Initialise worksheet collection and add one worksheet
+        $this->workSheetCollection = [];
+        $this->workSheetCollection[] = new Worksheet($this);
+        $this->activeSheetIndex = 0;
+
+        // Create document properties
+        $this->properties = new Document\Properties();
+
+        // Create document security
+        $this->security = new Document\Security();
+
+        // Set named ranges
+        $this->namedRanges = [];
+
+        // Create the cellXf supervisor
+        $this->cellXfSupervisor = new Style(true);
+        $this->cellXfSupervisor->bindParent($this);
+
+        // Create the default style
+        $this->addCellXf(new Style());
+        $this->addCellStyleXf(new Style());
+    }
+
+    /**
+     * Code to execute when this worksheet is unset().
+     */
+    public function __destruct()
+    {
+        $this->calculationEngine = null;
+        $this->disconnectWorksheets();
+    }
+
+    /**
+     * Disconnect all worksheets from this PhpSpreadsheet workbook object,
+     * typically so that the PhpSpreadsheet object can be unset.
+     */
+    public function disconnectWorksheets(): void
+    {
+        $worksheet = null;
+        foreach ($this->workSheetCollection as $k => &$worksheet) {
+            $worksheet->disconnectCells();
+            $this->workSheetCollection[$k] = null;
+        }
+        unset($worksheet);
+        $this->workSheetCollection = [];
+    }
+
+    /**
+     * Return the calculation engine for this worksheet.
+     *
+     * @return Calculation
+     */
+    public function getCalculationEngine()
+    {
+        return $this->calculationEngine;
+    }
+
+    /**
+     * Get properties.
+     *
+     * @return Document\Properties
+     */
+    public function getProperties()
+    {
+        return $this->properties;
+    }
+
+    /**
+     * Set properties.
+     */
+    public function setProperties(Document\Properties $pValue): void
+    {
+        $this->properties = $pValue;
+    }
+
+    /**
+     * Get security.
+     *
+     * @return Document\Security
+     */
+    public function getSecurity()
+    {
+        return $this->security;
+    }
+
+    /**
+     * Set security.
+     */
+    public function setSecurity(Document\Security $pValue): void
+    {
+        $this->security = $pValue;
+    }
+
+    /**
+     * Get active sheet.
+     *
+     * @return Worksheet
+     */
+    public function getActiveSheet()
+    {
+        return $this->getSheet($this->activeSheetIndex);
+    }
+
+    /**
+     * Create sheet and add it to this workbook.
+     *
+     * @param null|int $sheetIndex Index where sheet should go (0,1,..., or null for last)
+     *
+     * @return Worksheet
+     */
+    public function createSheet($sheetIndex = null)
+    {
+        $newSheet = new Worksheet($this);
+        $this->addSheet($newSheet, $sheetIndex);
+
+        return $newSheet;
+    }
+
+    /**
+     * Check if a sheet with a specified name already exists.
+     *
+     * @param string $pSheetName Name of the worksheet to check
+     *
+     * @return bool
+     */
+    public function sheetNameExists($pSheetName)
+    {
+        return $this->getSheetByName($pSheetName) !== null;
+    }
+
+    /**
+     * Add sheet.
+     *
+     * @param null|int $iSheetIndex Index where sheet should go (0,1,..., or null for last)
+     *
+     * @return Worksheet
+     */
+    public function addSheet(Worksheet $pSheet, $iSheetIndex = null)
+    {
+        if ($this->sheetNameExists($pSheet->getTitle())) {
+            throw new Exception(
+                "Workbook already contains a worksheet named '{$pSheet->getTitle()}'. Rename this worksheet first."
+            );
+        }
+
+        if ($iSheetIndex === null) {
+            if ($this->activeSheetIndex < 0) {
+                $this->activeSheetIndex = 0;
+            }
+            $this->workSheetCollection[] = $pSheet;
+        } else {
+            // Insert the sheet at the requested index
+            array_splice(
+                $this->workSheetCollection,
+                $iSheetIndex,
+                0,
+                [$pSheet]
+            );
+
+            // Adjust active sheet index if necessary
+            if ($this->activeSheetIndex >= $iSheetIndex) {
+                ++$this->activeSheetIndex;
+            }
+        }
+
+        if ($pSheet->getParent() === null) {
+            $pSheet->rebindParent($this);
+        }
+
+        return $pSheet;
+    }
+
+    /**
+     * Remove sheet by index.
+     *
+     * @param int $pIndex Active sheet index
+     */
+    public function removeSheetByIndex($pIndex): void
+    {
+        $numSheets = count($this->workSheetCollection);
+        if ($pIndex > $numSheets - 1) {
+            throw new Exception(
+                "You tried to remove a sheet by the out of bounds index: {$pIndex}. The actual number of sheets is {$numSheets}."
+            );
+        }
+        array_splice($this->workSheetCollection, $pIndex, 1);
+
+        // Adjust active sheet index if necessary
+        if (($this->activeSheetIndex >= $pIndex) &&
+            ($pIndex > count($this->workSheetCollection) - 1)) {
+            --$this->activeSheetIndex;
+        }
+    }
+
+    /**
+     * Get sheet by index.
+     *
+     * @param int $pIndex Sheet index
+     *
+     * @return Worksheet
+     */
+    public function getSheet($pIndex)
+    {
+        if (!isset($this->workSheetCollection[$pIndex])) {
+            $numSheets = $this->getSheetCount();
+
+            throw new Exception(
+                "Your requested sheet index: {$pIndex} is out of bounds. The actual number of sheets is {$numSheets}."
+            );
+        }
+
+        return $this->workSheetCollection[$pIndex];
+    }
+
+    /**
+     * Get all sheets.
+     *
+     * @return Worksheet[]
+     */
+    public function getAllSheets()
+    {
+        return $this->workSheetCollection;
+    }
+
+    /**
+     * Get sheet by name.
+     *
+     * @param string $pName Sheet name
+     *
+     * @return null|Worksheet
+     */
+    public function getSheetByName($pName)
+    {
+        $worksheetCount = count($this->workSheetCollection);
+        for ($i = 0; $i < $worksheetCount; ++$i) {
+            if ($this->workSheetCollection[$i]->getTitle() === trim($pName, "'")) {
+                return $this->workSheetCollection[$i];
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Get index for sheet.
+     *
+     * @return int index
+     */
+    public function getIndex(Worksheet $pSheet)
+    {
+        foreach ($this->workSheetCollection as $key => $value) {
+            if ($value->getHashCode() == $pSheet->getHashCode()) {
+                return $key;
+            }
+        }
+
+        throw new Exception('Sheet does not exist.');
+    }
+
+    /**
+     * Set index for sheet by sheet name.
+     *
+     * @param string $sheetName Sheet name to modify index for
+     * @param int $newIndex New index for the sheet
+     *
+     * @return int New sheet index
+     */
+    public function setIndexByName($sheetName, $newIndex)
+    {
+        $oldIndex = $this->getIndex($this->getSheetByName($sheetName));
+        $pSheet = array_splice(
+            $this->workSheetCollection,
+            $oldIndex,
+            1
+        );
+        array_splice(
+            $this->workSheetCollection,
+            $newIndex,
+            0,
+            $pSheet
+        );
+
+        return $newIndex;
+    }
+
+    /**
+     * Get sheet count.
+     *
+     * @return int
+     */
+    public function getSheetCount()
+    {
+        return count($this->workSheetCollection);
+    }
+
+    /**
+     * Get active sheet index.
+     *
+     * @return int Active sheet index
+     */
+    public function getActiveSheetIndex()
+    {
+        return $this->activeSheetIndex;
+    }
+
+    /**
+     * Set active sheet index.
+     *
+     * @param int $pIndex Active sheet index
+     *
+     * @return Worksheet
+     */
+    public function setActiveSheetIndex($pIndex)
+    {
+        $numSheets = count($this->workSheetCollection);
+
+        if ($pIndex > $numSheets - 1) {
+            throw new Exception(
+                "You tried to set a sheet active by the out of bounds index: {$pIndex}. The actual number of sheets is {$numSheets}."
+            );
+        }
+        $this->activeSheetIndex = $pIndex;
+
+        return $this->getActiveSheet();
+    }
+
+    /**
+     * Set active sheet index by name.
+     *
+     * @param string $pValue Sheet title
+     *
+     * @return Worksheet
+     */
+    public function setActiveSheetIndexByName($pValue)
+    {
+        if (($worksheet = $this->getSheetByName($pValue)) instanceof Worksheet) {
+            $this->setActiveSheetIndex($this->getIndex($worksheet));
+
+            return $worksheet;
+        }
+
+        throw new Exception('Workbook does not contain sheet:' . $pValue);
+    }
+
+    /**
+     * Get sheet names.
+     *
+     * @return string[]
+     */
+    public function getSheetNames()
+    {
+        $returnValue = [];
+        $worksheetCount = $this->getSheetCount();
+        for ($i = 0; $i < $worksheetCount; ++$i) {
+            $returnValue[] = $this->getSheet($i)->getTitle();
+        }
+
+        return $returnValue;
+    }
+
+    /**
+     * Add external sheet.
+     *
+     * @param Worksheet $pSheet External sheet to add
+     * @param null|int $iSheetIndex Index where sheet should go (0,1,..., or null for last)
+     *
+     * @return Worksheet
+     */
+    public function addExternalSheet(Worksheet $pSheet, $iSheetIndex = null)
+    {
+        if ($this->sheetNameExists($pSheet->getTitle())) {
+            throw new Exception("Workbook already contains a worksheet named '{$pSheet->getTitle()}'. Rename the external sheet first.");
+        }
+
+        // count how many cellXfs there are in this workbook currently, we will need this below
+        $countCellXfs = count($this->cellXfCollection);
+
+        // copy all the shared cellXfs from the external workbook and append them to the current
+        foreach ($pSheet->getParent()->getCellXfCollection() as $cellXf) {
+            $this->addCellXf(clone $cellXf);
+        }
+
+        // move sheet to this workbook
+        $pSheet->rebindParent($this);
+
+        // update the cellXfs
+        foreach ($pSheet->getCoordinates(false) as $coordinate) {
+            $cell = $pSheet->getCell($coordinate);
+            $cell->setXfIndex($cell->getXfIndex() + $countCellXfs);
+        }
+
+        return $this->addSheet($pSheet, $iSheetIndex);
+    }
+
+    /**
+     * Get named ranges.
+     *
+     * @return NamedRange[]
+     */
+    public function getNamedRanges()
+    {
+        return $this->namedRanges;
+    }
+
+    /**
+     * Add named range.
+     *
+     * @return bool
+     */
+    public function addNamedRange(NamedRange $namedRange)
+    {
+        if ($namedRange->getScope() == null) {
+            // global scope
+            $this->namedRanges[$namedRange->getName()] = $namedRange;
+        } else {
+            // local scope
+            $this->namedRanges[$namedRange->getScope()->getTitle() . '!' . $namedRange->getName()] = $namedRange;
+        }
+
+        return true;
+    }
+
+    /**
+     * Get named range.
+     *
+     * @param string $namedRange
+     * @param null|Worksheet $pSheet Scope. Use null for global scope
+     *
+     * @return null|NamedRange
+     */
+    public function getNamedRange($namedRange, ?Worksheet $pSheet = null)
+    {
+        $returnValue = null;
+
+        if ($namedRange != '' && ($namedRange !== null)) {
+            // first look for global defined name
+            if (isset($this->namedRanges[$namedRange])) {
+                $returnValue = $this->namedRanges[$namedRange];
+            }
+
+            // then look for local defined name (has priority over global defined name if both names exist)
+            if (($pSheet !== null) && isset($this->namedRanges[$pSheet->getTitle() . '!' . $namedRange])) {
+                $returnValue = $this->namedRanges[$pSheet->getTitle() . '!' . $namedRange];
+            }
+        }
+
+        return $returnValue;
+    }
+
+    /**
+     * Remove named range.
+     *
+     * @param string $namedRange
+     * @param null|Worksheet $pSheet scope: use null for global scope
+     *
+     * @return $this
+     */
+    public function removeNamedRange($namedRange, ?Worksheet $pSheet = null)
+    {
+        if ($pSheet === null) {
+            if (isset($this->namedRanges[$namedRange])) {
+                unset($this->namedRanges[$namedRange]);
+            }
+        } else {
+            if (isset($this->namedRanges[$pSheet->getTitle() . '!' . $namedRange])) {
+                unset($this->namedRanges[$pSheet->getTitle() . '!' . $namedRange]);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get worksheet iterator.
+     *
+     * @return Iterator
+     */
+    public function getWorksheetIterator()
+    {
+        return new Iterator($this);
+    }
+
+    /**
+     * Copy workbook (!= clone!).
+     *
+     * @return Spreadsheet
+     */
+    public function copy()
+    {
+        $copied = clone $this;
+
+        $worksheetCount = count($this->workSheetCollection);
+        for ($i = 0; $i < $worksheetCount; ++$i) {
+            $this->workSheetCollection[$i] = $this->workSheetCollection[$i]->copy();
+            $this->workSheetCollection[$i]->rebindParent($this);
+        }
+
+        return $copied;
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        foreach ($this as $key => $val) {
+            if (is_object($val) || (is_array($val))) {
+                $this->{$key} = unserialize(serialize($val));
+            }
+        }
+    }
+
+    /**
+     * Get the workbook collection of cellXfs.
+     *
+     * @return Style[]
+     */
+    public function getCellXfCollection()
+    {
+        return $this->cellXfCollection;
+    }
+
+    /**
+     * Get cellXf by index.
+     *
+     * @param int $pIndex
+     *
+     * @return Style
+     */
+    public function getCellXfByIndex($pIndex)
+    {
+        return $this->cellXfCollection[$pIndex];
+    }
+
+    /**
+     * Get cellXf by hash code.
+     *
+     * @param string $pValue
+     *
+     * @return false|Style
+     */
+    public function getCellXfByHashCode($pValue)
+    {
+        foreach ($this->cellXfCollection as $cellXf) {
+            if ($cellXf->getHashCode() == $pValue) {
+                return $cellXf;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Check if style exists in style collection.
+     *
+     * @param Style $pCellStyle
+     *
+     * @return bool
+     */
+    public function cellXfExists($pCellStyle)
+    {
+        return in_array($pCellStyle, $this->cellXfCollection, true);
+    }
+
+    /**
+     * Get default style.
+     *
+     * @return Style
+     */
+    public function getDefaultStyle()
+    {
+        if (isset($this->cellXfCollection[0])) {
+            return $this->cellXfCollection[0];
+        }
+
+        throw new Exception('No default style found for this workbook');
+    }
+
+    /**
+     * Add a cellXf to the workbook.
+     */
+    public function addCellXf(Style $style): void
+    {
+        $this->cellXfCollection[] = $style;
+        $style->setIndex(count($this->cellXfCollection) - 1);
+    }
+
+    /**
+     * Remove cellXf by index. It is ensured that all cells get their xf index updated.
+     *
+     * @param int $pIndex Index to cellXf
+     */
+    public function removeCellXfByIndex($pIndex): void
+    {
+        if ($pIndex > count($this->cellXfCollection) - 1) {
+            throw new Exception('CellXf index is out of bounds.');
+        }
+
+        // first remove the cellXf
+        array_splice($this->cellXfCollection, $pIndex, 1);
+
+        // then update cellXf indexes for cells
+        foreach ($this->workSheetCollection as $worksheet) {
+            foreach ($worksheet->getCoordinates(false) as $coordinate) {
+                $cell = $worksheet->getCell($coordinate);
+                $xfIndex = $cell->getXfIndex();
+                if ($xfIndex > $pIndex) {
+                    // decrease xf index by 1
+                    $cell->setXfIndex($xfIndex - 1);
+                } elseif ($xfIndex == $pIndex) {
+                    // set to default xf index 0
+                    $cell->setXfIndex(0);
+                }
+            }
+        }
+    }
+
+    /**
+     * Get the cellXf supervisor.
+     *
+     * @return Style
+     */
+    public function getCellXfSupervisor()
+    {
+        return $this->cellXfSupervisor;
+    }
+
+    /**
+     * Get the workbook collection of cellStyleXfs.
+     *
+     * @return Style[]
+     */
+    public function getCellStyleXfCollection()
+    {
+        return $this->cellStyleXfCollection;
+    }
+
+    /**
+     * Get cellStyleXf by index.
+     *
+     * @param int $pIndex Index to cellXf
+     *
+     * @return Style
+     */
+    public function getCellStyleXfByIndex($pIndex)
+    {
+        return $this->cellStyleXfCollection[$pIndex];
+    }
+
+    /**
+     * Get cellStyleXf by hash code.
+     *
+     * @param string $pValue
+     *
+     * @return false|Style
+     */
+    public function getCellStyleXfByHashCode($pValue)
+    {
+        foreach ($this->cellStyleXfCollection as $cellStyleXf) {
+            if ($cellStyleXf->getHashCode() == $pValue) {
+                return $cellStyleXf;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Add a cellStyleXf to the workbook.
+     */
+    public function addCellStyleXf(Style $pStyle): void
+    {
+        $this->cellStyleXfCollection[] = $pStyle;
+        $pStyle->setIndex(count($this->cellStyleXfCollection) - 1);
+    }
+
+    /**
+     * Remove cellStyleXf by index.
+     *
+     * @param int $pIndex Index to cellXf
+     */
+    public function removeCellStyleXfByIndex($pIndex): void
+    {
+        if ($pIndex > count($this->cellStyleXfCollection) - 1) {
+            throw new Exception('CellStyleXf index is out of bounds.');
+        }
+        array_splice($this->cellStyleXfCollection, $pIndex, 1);
+    }
+
+    /**
+     * Eliminate all unneeded cellXf and afterwards update the xfIndex for all cells
+     * and columns in the workbook.
+     */
+    public function garbageCollect(): void
+    {
+        // how many references are there to each cellXf ?
+        $countReferencesCellXf = [];
+        foreach ($this->cellXfCollection as $index => $cellXf) {
+            $countReferencesCellXf[$index] = 0;
+        }
+
+        foreach ($this->getWorksheetIterator() as $sheet) {
+            // from cells
+            foreach ($sheet->getCoordinates(false) as $coordinate) {
+                $cell = $sheet->getCell($coordinate);
+                ++$countReferencesCellXf[$cell->getXfIndex()];
+            }
+
+            // from row dimensions
+            foreach ($sheet->getRowDimensions() as $rowDimension) {
+                if ($rowDimension->getXfIndex() !== null) {
+                    ++$countReferencesCellXf[$rowDimension->getXfIndex()];
+                }
+            }
+
+            // from column dimensions
+            foreach ($sheet->getColumnDimensions() as $columnDimension) {
+                ++$countReferencesCellXf[$columnDimension->getXfIndex()];
+            }
+        }
+
+        // remove cellXfs without references and create mapping so we can update xfIndex
+        // for all cells and columns
+        $countNeededCellXfs = 0;
+        foreach ($this->cellXfCollection as $index => $cellXf) {
+            if ($countReferencesCellXf[$index] > 0 || $index == 0) { // we must never remove the first cellXf
+                ++$countNeededCellXfs;
+            } else {
+                unset($this->cellXfCollection[$index]);
+            }
+            $map[$index] = $countNeededCellXfs - 1;
+        }
+        $this->cellXfCollection = array_values($this->cellXfCollection);
+
+        // update the index for all cellXfs
+        foreach ($this->cellXfCollection as $i => $cellXf) {
+            $cellXf->setIndex($i);
+        }
+
+        // make sure there is always at least one cellXf (there should be)
+        if (empty($this->cellXfCollection)) {
+            $this->cellXfCollection[] = new Style();
+        }
+
+        // update the xfIndex for all cells, row dimensions, column dimensions
+        foreach ($this->getWorksheetIterator() as $sheet) {
+            // for all cells
+            foreach ($sheet->getCoordinates(false) as $coordinate) {
+                $cell = $sheet->getCell($coordinate);
+                $cell->setXfIndex($map[$cell->getXfIndex()]);
+            }
+
+            // for all row dimensions
+            foreach ($sheet->getRowDimensions() as $rowDimension) {
+                if ($rowDimension->getXfIndex() !== null) {
+                    $rowDimension->setXfIndex($map[$rowDimension->getXfIndex()]);
+                }
+            }
+
+            // for all column dimensions
+            foreach ($sheet->getColumnDimensions() as $columnDimension) {
+                $columnDimension->setXfIndex($map[$columnDimension->getXfIndex()]);
+            }
+
+            // also do garbage collection for all the sheets
+            $sheet->garbageCollect();
+        }
+    }
+
+    /**
+     * Return the unique ID value assigned to this spreadsheet workbook.
+     *
+     * @return string
+     */
+    public function getID()
+    {
+        return $this->uniqueID;
+    }
+
+    /**
+     * Get the visibility of the horizonal scroll bar in the application.
+     *
+     * @return bool True if horizonal scroll bar is visible
+     */
+    public function getShowHorizontalScroll()
+    {
+        return $this->showHorizontalScroll;
+    }
+
+    /**
+     * Set the visibility of the horizonal scroll bar in the application.
+     *
+     * @param bool $showHorizontalScroll True if horizonal scroll bar is visible
+     */
+    public function setShowHorizontalScroll($showHorizontalScroll): void
+    {
+        $this->showHorizontalScroll = (bool) $showHorizontalScroll;
+    }
+
+    /**
+     * Get the visibility of the vertical scroll bar in the application.
+     *
+     * @return bool True if vertical scroll bar is visible
+     */
+    public function getShowVerticalScroll()
+    {
+        return $this->showVerticalScroll;
+    }
+
+    /**
+     * Set the visibility of the vertical scroll bar in the application.
+     *
+     * @param bool $showVerticalScroll True if vertical scroll bar is visible
+     */
+    public function setShowVerticalScroll($showVerticalScroll): void
+    {
+        $this->showVerticalScroll = (bool) $showVerticalScroll;
+    }
+
+    /**
+     * Get the visibility of the sheet tabs in the application.
+     *
+     * @return bool True if the sheet tabs are visible
+     */
+    public function getShowSheetTabs()
+    {
+        return $this->showSheetTabs;
+    }
+
+    /**
+     * Set the visibility of the sheet tabs  in the application.
+     *
+     * @param bool $showSheetTabs True if sheet tabs are visible
+     */
+    public function setShowSheetTabs($showSheetTabs): void
+    {
+        $this->showSheetTabs = (bool) $showSheetTabs;
+    }
+
+    /**
+     * Return whether the workbook window is minimized.
+     *
+     * @return bool true if workbook window is minimized
+     */
+    public function getMinimized()
+    {
+        return $this->minimized;
+    }
+
+    /**
+     * Set whether the workbook window is minimized.
+     *
+     * @param bool $minimized true if workbook window is minimized
+     */
+    public function setMinimized($minimized): void
+    {
+        $this->minimized = (bool) $minimized;
+    }
+
+    /**
+     * Return whether to group dates when presenting the user with
+     * filtering optiomd in the user interface.
+     *
+     * @return bool true if workbook window is minimized
+     */
+    public function getAutoFilterDateGrouping()
+    {
+        return $this->autoFilterDateGrouping;
+    }
+
+    /**
+     * Set whether to group dates when presenting the user with
+     * filtering optiomd in the user interface.
+     *
+     * @param bool $autoFilterDateGrouping true if workbook window is minimized
+     */
+    public function setAutoFilterDateGrouping($autoFilterDateGrouping): void
+    {
+        $this->autoFilterDateGrouping = (bool) $autoFilterDateGrouping;
+    }
+
+    /**
+     * Return the first sheet in the book view.
+     *
+     * @return int First sheet in book view
+     */
+    public function getFirstSheetIndex()
+    {
+        return $this->firstSheetIndex;
+    }
+
+    /**
+     * Set the first sheet in the book view.
+     *
+     * @param int $firstSheetIndex First sheet in book view
+     */
+    public function setFirstSheetIndex($firstSheetIndex): void
+    {
+        if ($firstSheetIndex >= 0) {
+            $this->firstSheetIndex = (int) $firstSheetIndex;
+        } else {
+            throw new Exception('First sheet index must be a positive integer.');
+        }
+    }
+
+    /**
+     * Return the visibility status of the workbook.
+     *
+     * This may be one of the following three values:
+     * - visibile
+     *
+     * @return string Visible status
+     */
+    public function getVisibility()
+    {
+        return $this->visibility;
+    }
+
+    /**
+     * Set the visibility status of the workbook.
+     *
+     * Valid values are:
+     *  - 'visible' (self::VISIBILITY_VISIBLE):
+     *       Workbook window is visible
+     *  - 'hidden' (self::VISIBILITY_HIDDEN):
+     *       Workbook window is hidden, but can be shown by the user
+     *       via the user interface
+     *  - 'veryHidden' (self::VISIBILITY_VERY_HIDDEN):
+     *       Workbook window is hidden and cannot be shown in the
+     *       user interface.
+     *
+     * @param string $visibility visibility status of the workbook
+     */
+    public function setVisibility($visibility): void
+    {
+        if ($visibility === null) {
+            $visibility = self::VISIBILITY_VISIBLE;
+        }
+
+        if (in_array($visibility, self::$workbookViewVisibilityValues)) {
+            $this->visibility = $visibility;
+        } else {
+            throw new Exception('Invalid visibility value.');
+        }
+    }
+
+    /**
+     * Get the ratio between the workbook tabs bar and the horizontal scroll bar.
+     * TabRatio is assumed to be out of 1000 of the horizontal window width.
+     *
+     * @return int Ratio between the workbook tabs bar and the horizontal scroll bar
+     */
+    public function getTabRatio()
+    {
+        return $this->tabRatio;
+    }
+
+    /**
+     * Set the ratio between the workbook tabs bar and the horizontal scroll bar
+     * TabRatio is assumed to be out of 1000 of the horizontal window width.
+     *
+     * @param int $tabRatio Ratio between the tabs bar and the horizontal scroll bar
+     */
+    public function setTabRatio($tabRatio): void
+    {
+        if ($tabRatio >= 0 || $tabRatio <= 1000) {
+            $this->tabRatio = (int) $tabRatio;
+        } else {
+            throw new Exception('Tab ratio must be between 0 and 1000.');
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Alignment.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Alignment.php
new file mode 100755
index 0000000..e54fa2a
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Alignment.php
@@ -0,0 +1,462 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Style;
+
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+
+class Alignment extends Supervisor
+{
+    // Horizontal alignment styles
+    const HORIZONTAL_GENERAL = 'general';
+    const HORIZONTAL_LEFT = 'left';
+    const HORIZONTAL_RIGHT = 'right';
+    const HORIZONTAL_CENTER = 'center';
+    const HORIZONTAL_CENTER_CONTINUOUS = 'centerContinuous';
+    const HORIZONTAL_JUSTIFY = 'justify';
+    const HORIZONTAL_FILL = 'fill';
+    const HORIZONTAL_DISTRIBUTED = 'distributed'; // Excel2007 only
+
+    // Vertical alignment styles
+    const VERTICAL_BOTTOM = 'bottom';
+    const VERTICAL_TOP = 'top';
+    const VERTICAL_CENTER = 'center';
+    const VERTICAL_JUSTIFY = 'justify';
+    const VERTICAL_DISTRIBUTED = 'distributed'; // Excel2007 only
+
+    // Read order
+    const READORDER_CONTEXT = 0;
+    const READORDER_LTR = 1;
+    const READORDER_RTL = 2;
+
+    /**
+     * Horizontal alignment.
+     *
+     * @var string
+     */
+    protected $horizontal = self::HORIZONTAL_GENERAL;
+
+    /**
+     * Vertical alignment.
+     *
+     * @var string
+     */
+    protected $vertical = self::VERTICAL_BOTTOM;
+
+    /**
+     * Text rotation.
+     *
+     * @var int
+     */
+    protected $textRotation = 0;
+
+    /**
+     * Wrap text.
+     *
+     * @var bool
+     */
+    protected $wrapText = false;
+
+    /**
+     * Shrink to fit.
+     *
+     * @var bool
+     */
+    protected $shrinkToFit = false;
+
+    /**
+     * Indent - only possible with horizontal alignment left and right.
+     *
+     * @var int
+     */
+    protected $indent = 0;
+
+    /**
+     * Read order.
+     *
+     * @var int
+     */
+    protected $readOrder = 0;
+
+    /**
+     * Create a new Alignment.
+     *
+     * @param bool $isSupervisor Flag indicating if this is a supervisor or not
+     *                                       Leave this value at default unless you understand exactly what
+     *                                          its ramifications are
+     * @param bool $isConditional Flag indicating if this is a conditional style or not
+     *                                       Leave this value at default unless you understand exactly what
+     *                                          its ramifications are
+     */
+    public function __construct($isSupervisor = false, $isConditional = false)
+    {
+        // Supervisor?
+        parent::__construct($isSupervisor);
+
+        if ($isConditional) {
+            $this->horizontal = null;
+            $this->vertical = null;
+            $this->textRotation = null;
+        }
+    }
+
+    /**
+     * Get the shared style component for the currently active cell in currently active sheet.
+     * Only used for style supervisor.
+     *
+     * @return Alignment
+     */
+    public function getSharedComponent()
+    {
+        return $this->parent->getSharedComponent()->getAlignment();
+    }
+
+    /**
+     * Build style array from subcomponents.
+     *
+     * @param array $array
+     *
+     * @return array
+     */
+    public function getStyleArray($array)
+    {
+        return ['alignment' => $array];
+    }
+
+    /**
+     * Apply styles from array.
+     *
+     * <code>
+     * $spreadsheet->getActiveSheet()->getStyle('B2')->getAlignment()->applyFromArray(
+     *        [
+     *            'horizontal'   => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
+     *            'vertical'     => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
+     *            'textRotation' => 0,
+     *            'wrapText'     => TRUE
+     *        ]
+     * );
+     * </code>
+     *
+     * @param array $pStyles Array containing style information
+     *
+     * @return $this
+     */
+    public function applyFromArray(array $pStyles)
+    {
+        if ($this->isSupervisor) {
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())
+                ->applyFromArray($this->getStyleArray($pStyles));
+        } else {
+            if (isset($pStyles['horizontal'])) {
+                $this->setHorizontal($pStyles['horizontal']);
+            }
+            if (isset($pStyles['vertical'])) {
+                $this->setVertical($pStyles['vertical']);
+            }
+            if (isset($pStyles['textRotation'])) {
+                $this->setTextRotation($pStyles['textRotation']);
+            }
+            if (isset($pStyles['wrapText'])) {
+                $this->setWrapText($pStyles['wrapText']);
+            }
+            if (isset($pStyles['shrinkToFit'])) {
+                $this->setShrinkToFit($pStyles['shrinkToFit']);
+            }
+            if (isset($pStyles['indent'])) {
+                $this->setIndent($pStyles['indent']);
+            }
+            if (isset($pStyles['readOrder'])) {
+                $this->setReadOrder($pStyles['readOrder']);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Horizontal.
+     *
+     * @return string
+     */
+    public function getHorizontal()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getHorizontal();
+        }
+
+        return $this->horizontal;
+    }
+
+    /**
+     * Set Horizontal.
+     *
+     * @param string $pValue see self::HORIZONTAL_*
+     *
+     * @return $this
+     */
+    public function setHorizontal($pValue)
+    {
+        if ($pValue == '') {
+            $pValue = self::HORIZONTAL_GENERAL;
+        }
+
+        if ($this->isSupervisor) {
+            $styleArray = $this->getStyleArray(['horizontal' => $pValue]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->horizontal = $pValue;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Vertical.
+     *
+     * @return string
+     */
+    public function getVertical()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getVertical();
+        }
+
+        return $this->vertical;
+    }
+
+    /**
+     * Set Vertical.
+     *
+     * @param string $pValue see self::VERTICAL_*
+     *
+     * @return $this
+     */
+    public function setVertical($pValue)
+    {
+        if ($pValue == '') {
+            $pValue = self::VERTICAL_BOTTOM;
+        }
+
+        if ($this->isSupervisor) {
+            $styleArray = $this->getStyleArray(['vertical' => $pValue]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->vertical = $pValue;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get TextRotation.
+     *
+     * @return int
+     */
+    public function getTextRotation()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getTextRotation();
+        }
+
+        return $this->textRotation;
+    }
+
+    /**
+     * Set TextRotation.
+     *
+     * @param int $pValue
+     *
+     * @return $this
+     */
+    public function setTextRotation($pValue)
+    {
+        // Excel2007 value 255 => PhpSpreadsheet value -165
+        if ($pValue == 255) {
+            $pValue = -165;
+        }
+
+        // Set rotation
+        if (($pValue >= -90 && $pValue <= 90) || $pValue == -165) {
+            if ($this->isSupervisor) {
+                $styleArray = $this->getStyleArray(['textRotation' => $pValue]);
+                $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+            } else {
+                $this->textRotation = $pValue;
+            }
+        } else {
+            throw new PhpSpreadsheetException('Text rotation should be a value between -90 and 90.');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Wrap Text.
+     *
+     * @return bool
+     */
+    public function getWrapText()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getWrapText();
+        }
+
+        return $this->wrapText;
+    }
+
+    /**
+     * Set Wrap Text.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setWrapText($pValue)
+    {
+        if ($pValue == '') {
+            $pValue = false;
+        }
+        if ($this->isSupervisor) {
+            $styleArray = $this->getStyleArray(['wrapText' => $pValue]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->wrapText = $pValue;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Shrink to fit.
+     *
+     * @return bool
+     */
+    public function getShrinkToFit()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getShrinkToFit();
+        }
+
+        return $this->shrinkToFit;
+    }
+
+    /**
+     * Set Shrink to fit.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setShrinkToFit($pValue)
+    {
+        if ($pValue == '') {
+            $pValue = false;
+        }
+        if ($this->isSupervisor) {
+            $styleArray = $this->getStyleArray(['shrinkToFit' => $pValue]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->shrinkToFit = $pValue;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get indent.
+     *
+     * @return int
+     */
+    public function getIndent()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getIndent();
+        }
+
+        return $this->indent;
+    }
+
+    /**
+     * Set indent.
+     *
+     * @param int $pValue
+     *
+     * @return $this
+     */
+    public function setIndent($pValue)
+    {
+        if ($pValue > 0) {
+            if ($this->getHorizontal() != self::HORIZONTAL_GENERAL &&
+                $this->getHorizontal() != self::HORIZONTAL_LEFT &&
+                $this->getHorizontal() != self::HORIZONTAL_RIGHT) {
+                $pValue = 0; // indent not supported
+            }
+        }
+        if ($this->isSupervisor) {
+            $styleArray = $this->getStyleArray(['indent' => $pValue]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->indent = $pValue;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get read order.
+     *
+     * @return int
+     */
+    public function getReadOrder()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getReadOrder();
+        }
+
+        return $this->readOrder;
+    }
+
+    /**
+     * Set read order.
+     *
+     * @param int $pValue
+     *
+     * @return $this
+     */
+    public function setReadOrder($pValue)
+    {
+        if ($pValue < 0 || $pValue > 2) {
+            $pValue = 0;
+        }
+        if ($this->isSupervisor) {
+            $styleArray = $this->getStyleArray(['readOrder' => $pValue]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->readOrder = $pValue;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get hash code.
+     *
+     * @return string Hash code
+     */
+    public function getHashCode()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getHashCode();
+        }
+
+        return md5(
+            $this->horizontal .
+            $this->vertical .
+            $this->textRotation .
+            ($this->wrapText ? 't' : 'f') .
+            ($this->shrinkToFit ? 't' : 'f') .
+            $this->indent .
+            $this->readOrder .
+            __CLASS__
+        );
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Border.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Border.php
new file mode 100755
index 0000000..78ad8b2
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Border.php
@@ -0,0 +1,231 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Style;
+
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+
+class Border extends Supervisor
+{
+    // Border style
+    const BORDER_NONE = 'none';
+    const BORDER_DASHDOT = 'dashDot';
+    const BORDER_DASHDOTDOT = 'dashDotDot';
+    const BORDER_DASHED = 'dashed';
+    const BORDER_DOTTED = 'dotted';
+    const BORDER_DOUBLE = 'double';
+    const BORDER_HAIR = 'hair';
+    const BORDER_MEDIUM = 'medium';
+    const BORDER_MEDIUMDASHDOT = 'mediumDashDot';
+    const BORDER_MEDIUMDASHDOTDOT = 'mediumDashDotDot';
+    const BORDER_MEDIUMDASHED = 'mediumDashed';
+    const BORDER_SLANTDASHDOT = 'slantDashDot';
+    const BORDER_THICK = 'thick';
+    const BORDER_THIN = 'thin';
+
+    /**
+     * Border style.
+     *
+     * @var string
+     */
+    protected $borderStyle = self::BORDER_NONE;
+
+    /**
+     * Border color.
+     *
+     * @var Color
+     */
+    protected $color;
+
+    /**
+     * @var int
+     */
+    public $colorIndex;
+
+    /**
+     * Create a new Border.
+     *
+     * @param bool $isSupervisor Flag indicating if this is a supervisor or not
+     *                                    Leave this value at default unless you understand exactly what
+     *                                        its ramifications are
+     * @param bool $isConditional Flag indicating if this is a conditional style or not
+     *                                    Leave this value at default unless you understand exactly what
+     *                                        its ramifications are
+     */
+    public function __construct($isSupervisor = false, $isConditional = false)
+    {
+        // Supervisor?
+        parent::__construct($isSupervisor);
+
+        // Initialise values
+        $this->color = new Color(Color::COLOR_BLACK, $isSupervisor);
+
+        // bind parent if we are a supervisor
+        if ($isSupervisor) {
+            $this->color->bindParent($this, 'color');
+        }
+    }
+
+    /**
+     * Get the shared style component for the currently active cell in currently active sheet.
+     * Only used for style supervisor.
+     *
+     * @return Border
+     */
+    public function getSharedComponent()
+    {
+        switch ($this->parentPropertyName) {
+            case 'allBorders':
+            case 'horizontal':
+            case 'inside':
+            case 'outline':
+            case 'vertical':
+                throw new PhpSpreadsheetException('Cannot get shared component for a pseudo-border.');
+
+                break;
+            case 'bottom':
+                return $this->parent->getSharedComponent()->getBottom();
+            case 'diagonal':
+                return $this->parent->getSharedComponent()->getDiagonal();
+            case 'left':
+                return $this->parent->getSharedComponent()->getLeft();
+            case 'right':
+                return $this->parent->getSharedComponent()->getRight();
+            case 'top':
+                return $this->parent->getSharedComponent()->getTop();
+        }
+    }
+
+    /**
+     * Build style array from subcomponents.
+     *
+     * @param array $array
+     *
+     * @return array
+     */
+    public function getStyleArray($array)
+    {
+        return $this->parent->getStyleArray([$this->parentPropertyName => $array]);
+    }
+
+    /**
+     * Apply styles from array.
+     *
+     * <code>
+     * $spreadsheet->getActiveSheet()->getStyle('B2')->getBorders()->getTop()->applyFromArray(
+     *        [
+     *            'borderStyle' => Border::BORDER_DASHDOT,
+     *            'color' => [
+     *                'rgb' => '808080'
+     *            ]
+     *        ]
+     * );
+     * </code>
+     *
+     * @param array $pStyles Array containing style information
+     *
+     * @return $this
+     */
+    public function applyFromArray(array $pStyles)
+    {
+        if ($this->isSupervisor) {
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
+        } else {
+            if (isset($pStyles['borderStyle'])) {
+                $this->setBorderStyle($pStyles['borderStyle']);
+            }
+            if (isset($pStyles['color'])) {
+                $this->getColor()->applyFromArray($pStyles['color']);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Border style.
+     *
+     * @return string
+     */
+    public function getBorderStyle()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getBorderStyle();
+        }
+
+        return $this->borderStyle;
+    }
+
+    /**
+     * Set Border style.
+     *
+     * @param bool|string $pValue
+     *                            When passing a boolean, FALSE equates Border::BORDER_NONE
+     *                                and TRUE to Border::BORDER_MEDIUM
+     *
+     * @return $this
+     */
+    public function setBorderStyle($pValue)
+    {
+        if (empty($pValue)) {
+            $pValue = self::BORDER_NONE;
+        } elseif (is_bool($pValue) && $pValue) {
+            $pValue = self::BORDER_MEDIUM;
+        }
+        if ($this->isSupervisor) {
+            $styleArray = $this->getStyleArray(['borderStyle' => $pValue]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->borderStyle = $pValue;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Border Color.
+     *
+     * @return Color
+     */
+    public function getColor()
+    {
+        return $this->color;
+    }
+
+    /**
+     * Set Border Color.
+     *
+     * @return $this
+     */
+    public function setColor(Color $pValue)
+    {
+        // make sure parameter is a real color and not a supervisor
+        $color = $pValue->getIsSupervisor() ? $pValue->getSharedComponent() : $pValue;
+
+        if ($this->isSupervisor) {
+            $styleArray = $this->getColor()->getStyleArray(['argb' => $color->getARGB()]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->color = $color;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get hash code.
+     *
+     * @return string Hash code
+     */
+    public function getHashCode()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getHashCode();
+        }
+
+        return md5(
+            $this->borderStyle .
+            $this->color->getHashCode() .
+            __CLASS__
+        );
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Borders.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Borders.php
new file mode 100755
index 0000000..e75d7ee
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Borders.php
@@ -0,0 +1,411 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Style;
+
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+
+class Borders extends Supervisor
+{
+    // Diagonal directions
+    const DIAGONAL_NONE = 0;
+    const DIAGONAL_UP = 1;
+    const DIAGONAL_DOWN = 2;
+    const DIAGONAL_BOTH = 3;
+
+    /**
+     * Left.
+     *
+     * @var Border
+     */
+    protected $left;
+
+    /**
+     * Right.
+     *
+     * @var Border
+     */
+    protected $right;
+
+    /**
+     * Top.
+     *
+     * @var Border
+     */
+    protected $top;
+
+    /**
+     * Bottom.
+     *
+     * @var Border
+     */
+    protected $bottom;
+
+    /**
+     * Diagonal.
+     *
+     * @var Border
+     */
+    protected $diagonal;
+
+    /**
+     * DiagonalDirection.
+     *
+     * @var int
+     */
+    protected $diagonalDirection;
+
+    /**
+     * All borders pseudo-border. Only applies to supervisor.
+     *
+     * @var Border
+     */
+    protected $allBorders;
+
+    /**
+     * Outline pseudo-border. Only applies to supervisor.
+     *
+     * @var Border
+     */
+    protected $outline;
+
+    /**
+     * Inside pseudo-border. Only applies to supervisor.
+     *
+     * @var Border
+     */
+    protected $inside;
+
+    /**
+     * Vertical pseudo-border. Only applies to supervisor.
+     *
+     * @var Border
+     */
+    protected $vertical;
+
+    /**
+     * Horizontal pseudo-border. Only applies to supervisor.
+     *
+     * @var Border
+     */
+    protected $horizontal;
+
+    /**
+     * Create a new Borders.
+     *
+     * @param bool $isSupervisor Flag indicating if this is a supervisor or not
+     *                                    Leave this value at default unless you understand exactly what
+     *                                        its ramifications are
+     * @param bool $isConditional Flag indicating if this is a conditional style or not
+     *                                    Leave this value at default unless you understand exactly what
+     *                                        its ramifications are
+     */
+    public function __construct($isSupervisor = false, $isConditional = false)
+    {
+        // Supervisor?
+        parent::__construct($isSupervisor);
+
+        // Initialise values
+        $this->left = new Border($isSupervisor, $isConditional);
+        $this->right = new Border($isSupervisor, $isConditional);
+        $this->top = new Border($isSupervisor, $isConditional);
+        $this->bottom = new Border($isSupervisor, $isConditional);
+        $this->diagonal = new Border($isSupervisor, $isConditional);
+        $this->diagonalDirection = self::DIAGONAL_NONE;
+
+        // Specially for supervisor
+        if ($isSupervisor) {
+            // Initialize pseudo-borders
+            $this->allBorders = new Border(true);
+            $this->outline = new Border(true);
+            $this->inside = new Border(true);
+            $this->vertical = new Border(true);
+            $this->horizontal = new Border(true);
+
+            // bind parent if we are a supervisor
+            $this->left->bindParent($this, 'left');
+            $this->right->bindParent($this, 'right');
+            $this->top->bindParent($this, 'top');
+            $this->bottom->bindParent($this, 'bottom');
+            $this->diagonal->bindParent($this, 'diagonal');
+            $this->allBorders->bindParent($this, 'allBorders');
+            $this->outline->bindParent($this, 'outline');
+            $this->inside->bindParent($this, 'inside');
+            $this->vertical->bindParent($this, 'vertical');
+            $this->horizontal->bindParent($this, 'horizontal');
+        }
+    }
+
+    /**
+     * Get the shared style component for the currently active cell in currently active sheet.
+     * Only used for style supervisor.
+     *
+     * @return Borders
+     */
+    public function getSharedComponent()
+    {
+        return $this->parent->getSharedComponent()->getBorders();
+    }
+
+    /**
+     * Build style array from subcomponents.
+     *
+     * @param array $array
+     *
+     * @return array
+     */
+    public function getStyleArray($array)
+    {
+        return ['borders' => $array];
+    }
+
+    /**
+     * Apply styles from array.
+     *
+     * <code>
+     * $spreadsheet->getActiveSheet()->getStyle('B2')->getBorders()->applyFromArray(
+     *         [
+     *             'bottom' => [
+     *                 'borderStyle' => Border::BORDER_DASHDOT,
+     *                 'color' => [
+     *                     'rgb' => '808080'
+     *                 ]
+     *             ],
+     *             'top' => [
+     *                 'borderStyle' => Border::BORDER_DASHDOT,
+     *                 'color' => [
+     *                     'rgb' => '808080'
+     *                 ]
+     *             ]
+     *         ]
+     * );
+     * </code>
+     *
+     * <code>
+     * $spreadsheet->getActiveSheet()->getStyle('B2')->getBorders()->applyFromArray(
+     *         [
+     *             'allBorders' => [
+     *                 'borderStyle' => Border::BORDER_DASHDOT,
+     *                 'color' => [
+     *                     'rgb' => '808080'
+     *                 ]
+     *             ]
+     *         ]
+     * );
+     * </code>
+     *
+     * @param array $pStyles Array containing style information
+     *
+     * @return $this
+     */
+    public function applyFromArray(array $pStyles)
+    {
+        if ($this->isSupervisor) {
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
+        } else {
+            if (isset($pStyles['left'])) {
+                $this->getLeft()->applyFromArray($pStyles['left']);
+            }
+            if (isset($pStyles['right'])) {
+                $this->getRight()->applyFromArray($pStyles['right']);
+            }
+            if (isset($pStyles['top'])) {
+                $this->getTop()->applyFromArray($pStyles['top']);
+            }
+            if (isset($pStyles['bottom'])) {
+                $this->getBottom()->applyFromArray($pStyles['bottom']);
+            }
+            if (isset($pStyles['diagonal'])) {
+                $this->getDiagonal()->applyFromArray($pStyles['diagonal']);
+            }
+            if (isset($pStyles['diagonalDirection'])) {
+                $this->setDiagonalDirection($pStyles['diagonalDirection']);
+            }
+            if (isset($pStyles['allBorders'])) {
+                $this->getLeft()->applyFromArray($pStyles['allBorders']);
+                $this->getRight()->applyFromArray($pStyles['allBorders']);
+                $this->getTop()->applyFromArray($pStyles['allBorders']);
+                $this->getBottom()->applyFromArray($pStyles['allBorders']);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Left.
+     *
+     * @return Border
+     */
+    public function getLeft()
+    {
+        return $this->left;
+    }
+
+    /**
+     * Get Right.
+     *
+     * @return Border
+     */
+    public function getRight()
+    {
+        return $this->right;
+    }
+
+    /**
+     * Get Top.
+     *
+     * @return Border
+     */
+    public function getTop()
+    {
+        return $this->top;
+    }
+
+    /**
+     * Get Bottom.
+     *
+     * @return Border
+     */
+    public function getBottom()
+    {
+        return $this->bottom;
+    }
+
+    /**
+     * Get Diagonal.
+     *
+     * @return Border
+     */
+    public function getDiagonal()
+    {
+        return $this->diagonal;
+    }
+
+    /**
+     * Get AllBorders (pseudo-border). Only applies to supervisor.
+     *
+     * @return Border
+     */
+    public function getAllBorders()
+    {
+        if (!$this->isSupervisor) {
+            throw new PhpSpreadsheetException('Can only get pseudo-border for supervisor.');
+        }
+
+        return $this->allBorders;
+    }
+
+    /**
+     * Get Outline (pseudo-border). Only applies to supervisor.
+     *
+     * @return Border
+     */
+    public function getOutline()
+    {
+        if (!$this->isSupervisor) {
+            throw new PhpSpreadsheetException('Can only get pseudo-border for supervisor.');
+        }
+
+        return $this->outline;
+    }
+
+    /**
+     * Get Inside (pseudo-border). Only applies to supervisor.
+     *
+     * @return Border
+     */
+    public function getInside()
+    {
+        if (!$this->isSupervisor) {
+            throw new PhpSpreadsheetException('Can only get pseudo-border for supervisor.');
+        }
+
+        return $this->inside;
+    }
+
+    /**
+     * Get Vertical (pseudo-border). Only applies to supervisor.
+     *
+     * @return Border
+     */
+    public function getVertical()
+    {
+        if (!$this->isSupervisor) {
+            throw new PhpSpreadsheetException('Can only get pseudo-border for supervisor.');
+        }
+
+        return $this->vertical;
+    }
+
+    /**
+     * Get Horizontal (pseudo-border). Only applies to supervisor.
+     *
+     * @return Border
+     */
+    public function getHorizontal()
+    {
+        if (!$this->isSupervisor) {
+            throw new PhpSpreadsheetException('Can only get pseudo-border for supervisor.');
+        }
+
+        return $this->horizontal;
+    }
+
+    /**
+     * Get DiagonalDirection.
+     *
+     * @return int
+     */
+    public function getDiagonalDirection()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getDiagonalDirection();
+        }
+
+        return $this->diagonalDirection;
+    }
+
+    /**
+     * Set DiagonalDirection.
+     *
+     * @param int $pValue see self::DIAGONAL_*
+     *
+     * @return $this
+     */
+    public function setDiagonalDirection($pValue)
+    {
+        if ($pValue == '') {
+            $pValue = self::DIAGONAL_NONE;
+        }
+        if ($this->isSupervisor) {
+            $styleArray = $this->getStyleArray(['diagonalDirection' => $pValue]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->diagonalDirection = $pValue;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get hash code.
+     *
+     * @return string Hash code
+     */
+    public function getHashCode()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getHashcode();
+        }
+
+        return md5(
+            $this->getLeft()->getHashCode() .
+            $this->getRight()->getHashCode() .
+            $this->getTop()->getHashCode() .
+            $this->getBottom()->getHashCode() .
+            $this->getDiagonal()->getHashCode() .
+            $this->getDiagonalDirection() .
+            __CLASS__
+        );
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Color.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Color.php
new file mode 100755
index 0000000..d8ba08b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Color.php
@@ -0,0 +1,407 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Style;
+
+class Color extends Supervisor
+{
+    const NAMED_COLORS = [
+        'Black',
+        'White',
+        'Red',
+        'Green',
+        'Blue',
+        'Yellow',
+        'Magenta',
+        'Cyan',
+    ];
+
+    // Colors
+    const COLOR_BLACK = 'FF000000';
+    const COLOR_WHITE = 'FFFFFFFF';
+    const COLOR_RED = 'FFFF0000';
+    const COLOR_DARKRED = 'FF800000';
+    const COLOR_BLUE = 'FF0000FF';
+    const COLOR_DARKBLUE = 'FF000080';
+    const COLOR_GREEN = 'FF00FF00';
+    const COLOR_DARKGREEN = 'FF008000';
+    const COLOR_YELLOW = 'FFFFFF00';
+    const COLOR_DARKYELLOW = 'FF808000';
+
+    /**
+     * Indexed colors array.
+     *
+     * @var array
+     */
+    protected static $indexedColors;
+
+    /**
+     * ARGB - Alpha RGB.
+     *
+     * @var string
+     */
+    protected $argb;
+
+    /**
+     * Create a new Color.
+     *
+     * @param string $pARGB ARGB value for the colour
+     * @param bool $isSupervisor Flag indicating if this is a supervisor or not
+     *                                    Leave this value at default unless you understand exactly what
+     *                                        its ramifications are
+     * @param bool $isConditional Flag indicating if this is a conditional style or not
+     *                                    Leave this value at default unless you understand exactly what
+     *                                        its ramifications are
+     */
+    public function __construct($pARGB = self::COLOR_BLACK, $isSupervisor = false, $isConditional = false)
+    {
+        //    Supervisor?
+        parent::__construct($isSupervisor);
+
+        //    Initialise values
+        if (!$isConditional) {
+            $this->argb = $pARGB;
+        }
+    }
+
+    /**
+     * Get the shared style component for the currently active cell in currently active sheet.
+     * Only used for style supervisor.
+     *
+     * @return Color
+     */
+    public function getSharedComponent()
+    {
+        switch ($this->parentPropertyName) {
+            case 'endColor':
+                return $this->parent->getSharedComponent()->getEndColor();
+            case 'color':
+                return $this->parent->getSharedComponent()->getColor();
+            case 'startColor':
+                return $this->parent->getSharedComponent()->getStartColor();
+        }
+    }
+
+    /**
+     * Build style array from subcomponents.
+     *
+     * @param array $array
+     *
+     * @return array
+     */
+    public function getStyleArray($array)
+    {
+        return $this->parent->getStyleArray([$this->parentPropertyName => $array]);
+    }
+
+    /**
+     * Apply styles from array.
+     *
+     * <code>
+     * $spreadsheet->getActiveSheet()->getStyle('B2')->getFont()->getColor()->applyFromArray(['rgb' => '808080']);
+     * </code>
+     *
+     * @param array $pStyles Array containing style information
+     *
+     * @return $this
+     */
+    public function applyFromArray(array $pStyles)
+    {
+        if ($this->isSupervisor) {
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
+        } else {
+            if (isset($pStyles['rgb'])) {
+                $this->setRGB($pStyles['rgb']);
+            }
+            if (isset($pStyles['argb'])) {
+                $this->setARGB($pStyles['argb']);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get ARGB.
+     *
+     * @return string
+     */
+    public function getARGB()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getARGB();
+        }
+
+        return $this->argb;
+    }
+
+    /**
+     * Set ARGB.
+     *
+     * @param string $pValue see self::COLOR_*
+     *
+     * @return $this
+     */
+    public function setARGB($pValue)
+    {
+        if ($pValue == '') {
+            $pValue = self::COLOR_BLACK;
+        }
+        if ($this->isSupervisor) {
+            $styleArray = $this->getStyleArray(['argb' => $pValue]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->argb = $pValue;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get RGB.
+     *
+     * @return string
+     */
+    public function getRGB()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getRGB();
+        }
+
+        return substr($this->argb, 2);
+    }
+
+    /**
+     * Set RGB.
+     *
+     * @param string $pValue RGB value
+     *
+     * @return $this
+     */
+    public function setRGB($pValue)
+    {
+        if ($pValue == '') {
+            $pValue = '000000';
+        }
+        if ($this->isSupervisor) {
+            $styleArray = $this->getStyleArray(['argb' => 'FF' . $pValue]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->argb = 'FF' . $pValue;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get a specified colour component of an RGB value.
+     *
+     * @param string $RGB The colour as an RGB value (e.g. FF00CCCC or CCDDEE
+     * @param int $offset Position within the RGB value to extract
+     * @param bool $hex Flag indicating whether the component should be returned as a hex or a
+     *                                    decimal value
+     *
+     * @return string The extracted colour component
+     */
+    private static function getColourComponent($RGB, $offset, $hex = true)
+    {
+        $colour = substr($RGB, $offset, 2);
+
+        return ($hex) ? $colour : hexdec($colour);
+    }
+
+    /**
+     * Get the red colour component of an RGB value.
+     *
+     * @param string $RGB The colour as an RGB value (e.g. FF00CCCC or CCDDEE
+     * @param bool $hex Flag indicating whether the component should be returned as a hex or a
+     *                                    decimal value
+     *
+     * @return string The red colour component
+     */
+    public static function getRed($RGB, $hex = true)
+    {
+        return self::getColourComponent($RGB, strlen($RGB) - 6, $hex);
+    }
+
+    /**
+     * Get the green colour component of an RGB value.
+     *
+     * @param string $RGB The colour as an RGB value (e.g. FF00CCCC or CCDDEE
+     * @param bool $hex Flag indicating whether the component should be returned as a hex or a
+     *                                    decimal value
+     *
+     * @return string The green colour component
+     */
+    public static function getGreen($RGB, $hex = true)
+    {
+        return self::getColourComponent($RGB, strlen($RGB) - 4, $hex);
+    }
+
+    /**
+     * Get the blue colour component of an RGB value.
+     *
+     * @param string $RGB The colour as an RGB value (e.g. FF00CCCC or CCDDEE
+     * @param bool $hex Flag indicating whether the component should be returned as a hex or a
+     *                                    decimal value
+     *
+     * @return string The blue colour component
+     */
+    public static function getBlue($RGB, $hex = true)
+    {
+        return self::getColourComponent($RGB, strlen($RGB) - 2, $hex);
+    }
+
+    /**
+     * Adjust the brightness of a color.
+     *
+     * @param string $hex The colour as an RGBA or RGB value (e.g. FF00CCCC or CCDDEE)
+     * @param float $adjustPercentage The percentage by which to adjust the colour as a float from -1 to 1
+     *
+     * @return string The adjusted colour as an RGBA or RGB value (e.g. FF00CCCC or CCDDEE)
+     */
+    public static function changeBrightness($hex, $adjustPercentage)
+    {
+        $rgba = (strlen($hex) === 8);
+
+        $red = self::getRed($hex, false);
+        $green = self::getGreen($hex, false);
+        $blue = self::getBlue($hex, false);
+        if ($adjustPercentage > 0) {
+            $red += (255 - $red) * $adjustPercentage;
+            $green += (255 - $green) * $adjustPercentage;
+            $blue += (255 - $blue) * $adjustPercentage;
+        } else {
+            $red += $red * $adjustPercentage;
+            $green += $green * $adjustPercentage;
+            $blue += $blue * $adjustPercentage;
+        }
+
+        if ($red < 0) {
+            $red = 0;
+        } elseif ($red > 255) {
+            $red = 255;
+        }
+        if ($green < 0) {
+            $green = 0;
+        } elseif ($green > 255) {
+            $green = 255;
+        }
+        if ($blue < 0) {
+            $blue = 0;
+        } elseif ($blue > 255) {
+            $blue = 255;
+        }
+
+        $rgb = strtoupper(
+            str_pad(dechex((int) $red), 2, '0', 0) .
+            str_pad(dechex((int) $green), 2, '0', 0) .
+            str_pad(dechex((int) $blue), 2, '0', 0)
+        );
+
+        return (($rgba) ? 'FF' : '') . $rgb;
+    }
+
+    /**
+     * Get indexed color.
+     *
+     * @param int $pIndex Index entry point into the colour array
+     * @param bool $background Flag to indicate whether default background or foreground colour
+     *                                            should be returned if the indexed colour doesn't exist
+     *
+     * @return self
+     */
+    public static function indexedColor($pIndex, $background = false)
+    {
+        // Clean parameter
+        $pIndex = (int) $pIndex;
+
+        // Indexed colors
+        if (self::$indexedColors === null) {
+            self::$indexedColors = [
+                1 => 'FF000000', //  System Colour #1 - Black
+                2 => 'FFFFFFFF', //  System Colour #2 - White
+                3 => 'FFFF0000', //  System Colour #3 - Red
+                4 => 'FF00FF00', //  System Colour #4 - Green
+                5 => 'FF0000FF', //  System Colour #5 - Blue
+                6 => 'FFFFFF00', //  System Colour #6 - Yellow
+                7 => 'FFFF00FF', //  System Colour #7- Magenta
+                8 => 'FF00FFFF', //  System Colour #8- Cyan
+                9 => 'FF800000', //  Standard Colour #9
+                10 => 'FF008000', //  Standard Colour #10
+                11 => 'FF000080', //  Standard Colour #11
+                12 => 'FF808000', //  Standard Colour #12
+                13 => 'FF800080', //  Standard Colour #13
+                14 => 'FF008080', //  Standard Colour #14
+                15 => 'FFC0C0C0', //  Standard Colour #15
+                16 => 'FF808080', //  Standard Colour #16
+                17 => 'FF9999FF', //  Chart Fill Colour #17
+                18 => 'FF993366', //  Chart Fill Colour #18
+                19 => 'FFFFFFCC', //  Chart Fill Colour #19
+                20 => 'FFCCFFFF', //  Chart Fill Colour #20
+                21 => 'FF660066', //  Chart Fill Colour #21
+                22 => 'FFFF8080', //  Chart Fill Colour #22
+                23 => 'FF0066CC', //  Chart Fill Colour #23
+                24 => 'FFCCCCFF', //  Chart Fill Colour #24
+                25 => 'FF000080', //  Chart Line Colour #25
+                26 => 'FFFF00FF', //  Chart Line Colour #26
+                27 => 'FFFFFF00', //  Chart Line Colour #27
+                28 => 'FF00FFFF', //  Chart Line Colour #28
+                29 => 'FF800080', //  Chart Line Colour #29
+                30 => 'FF800000', //  Chart Line Colour #30
+                31 => 'FF008080', //  Chart Line Colour #31
+                32 => 'FF0000FF', //  Chart Line Colour #32
+                33 => 'FF00CCFF', //  Standard Colour #33
+                34 => 'FFCCFFFF', //  Standard Colour #34
+                35 => 'FFCCFFCC', //  Standard Colour #35
+                36 => 'FFFFFF99', //  Standard Colour #36
+                37 => 'FF99CCFF', //  Standard Colour #37
+                38 => 'FFFF99CC', //  Standard Colour #38
+                39 => 'FFCC99FF', //  Standard Colour #39
+                40 => 'FFFFCC99', //  Standard Colour #40
+                41 => 'FF3366FF', //  Standard Colour #41
+                42 => 'FF33CCCC', //  Standard Colour #42
+                43 => 'FF99CC00', //  Standard Colour #43
+                44 => 'FFFFCC00', //  Standard Colour #44
+                45 => 'FFFF9900', //  Standard Colour #45
+                46 => 'FFFF6600', //  Standard Colour #46
+                47 => 'FF666699', //  Standard Colour #47
+                48 => 'FF969696', //  Standard Colour #48
+                49 => 'FF003366', //  Standard Colour #49
+                50 => 'FF339966', //  Standard Colour #50
+                51 => 'FF003300', //  Standard Colour #51
+                52 => 'FF333300', //  Standard Colour #52
+                53 => 'FF993300', //  Standard Colour #53
+                54 => 'FF993366', //  Standard Colour #54
+                55 => 'FF333399', //  Standard Colour #55
+                56 => 'FF333333', //  Standard Colour #56
+            ];
+        }
+
+        if (isset(self::$indexedColors[$pIndex])) {
+            return new self(self::$indexedColors[$pIndex]);
+        }
+
+        if ($background) {
+            return new self(self::COLOR_WHITE);
+        }
+
+        return new self(self::COLOR_BLACK);
+    }
+
+    /**
+     * Get hash code.
+     *
+     * @return string Hash code
+     */
+    public function getHashCode()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getHashCode();
+        }
+
+        return md5(
+            $this->argb .
+            __CLASS__
+        );
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Conditional.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Conditional.php
new file mode 100755
index 0000000..35ec479
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Conditional.php
@@ -0,0 +1,274 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Style;
+
+use PhpOffice\PhpSpreadsheet\IComparable;
+
+class Conditional implements IComparable
+{
+    // Condition types
+    const CONDITION_NONE = 'none';
+    const CONDITION_CELLIS = 'cellIs';
+    const CONDITION_CONTAINSTEXT = 'containsText';
+    const CONDITION_EXPRESSION = 'expression';
+    const CONDITION_CONTAINSBLANKS = 'containsBlanks';
+    const CONDITION_NOTCONTAINSBLANKS = 'notContainsBlanks';
+
+    // Operator types
+    const OPERATOR_NONE = '';
+    const OPERATOR_BEGINSWITH = 'beginsWith';
+    const OPERATOR_ENDSWITH = 'endsWith';
+    const OPERATOR_EQUAL = 'equal';
+    const OPERATOR_GREATERTHAN = 'greaterThan';
+    const OPERATOR_GREATERTHANOREQUAL = 'greaterThanOrEqual';
+    const OPERATOR_LESSTHAN = 'lessThan';
+    const OPERATOR_LESSTHANOREQUAL = 'lessThanOrEqual';
+    const OPERATOR_NOTEQUAL = 'notEqual';
+    const OPERATOR_CONTAINSTEXT = 'containsText';
+    const OPERATOR_NOTCONTAINS = 'notContains';
+    const OPERATOR_BETWEEN = 'between';
+    const OPERATOR_NOTBETWEEN = 'notBetween';
+
+    /**
+     * Condition type.
+     *
+     * @var string
+     */
+    private $conditionType = self::CONDITION_NONE;
+
+    /**
+     * Operator type.
+     *
+     * @var string
+     */
+    private $operatorType = self::OPERATOR_NONE;
+
+    /**
+     * Text.
+     *
+     * @var string
+     */
+    private $text;
+
+    /**
+     * Stop on this condition, if it matches.
+     *
+     * @var bool
+     */
+    private $stopIfTrue = false;
+
+    /**
+     * Condition.
+     *
+     * @var string[]
+     */
+    private $condition = [];
+
+    /**
+     * Style.
+     *
+     * @var Style
+     */
+    private $style;
+
+    /**
+     * Create a new Conditional.
+     */
+    public function __construct()
+    {
+        // Initialise values
+        $this->style = new Style(false, true);
+    }
+
+    /**
+     * Get Condition type.
+     *
+     * @return string
+     */
+    public function getConditionType()
+    {
+        return $this->conditionType;
+    }
+
+    /**
+     * Set Condition type.
+     *
+     * @param string $pValue Condition type, see self::CONDITION_*
+     *
+     * @return $this
+     */
+    public function setConditionType($pValue)
+    {
+        $this->conditionType = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Operator type.
+     *
+     * @return string
+     */
+    public function getOperatorType()
+    {
+        return $this->operatorType;
+    }
+
+    /**
+     * Set Operator type.
+     *
+     * @param string $pValue Conditional operator type, see self::OPERATOR_*
+     *
+     * @return $this
+     */
+    public function setOperatorType($pValue)
+    {
+        $this->operatorType = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get text.
+     *
+     * @return string
+     */
+    public function getText()
+    {
+        return $this->text;
+    }
+
+    /**
+     * Set text.
+     *
+     * @param string $value
+     *
+     * @return $this
+     */
+    public function setText($value)
+    {
+        $this->text = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get StopIfTrue.
+     *
+     * @return bool
+     */
+    public function getStopIfTrue()
+    {
+        return $this->stopIfTrue;
+    }
+
+    /**
+     * Set StopIfTrue.
+     *
+     * @param bool $value
+     *
+     * @return $this
+     */
+    public function setStopIfTrue($value)
+    {
+        $this->stopIfTrue = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get Conditions.
+     *
+     * @return string[]
+     */
+    public function getConditions()
+    {
+        return $this->condition;
+    }
+
+    /**
+     * Set Conditions.
+     *
+     * @param string[] $pValue Condition
+     *
+     * @return $this
+     */
+    public function setConditions($pValue)
+    {
+        if (!is_array($pValue)) {
+            $pValue = [$pValue];
+        }
+        $this->condition = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Add Condition.
+     *
+     * @param string $pValue Condition
+     *
+     * @return $this
+     */
+    public function addCondition($pValue)
+    {
+        $this->condition[] = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Style.
+     *
+     * @return Style
+     */
+    public function getStyle()
+    {
+        return $this->style;
+    }
+
+    /**
+     * Set Style.
+     *
+     * @param Style $pValue
+     *
+     * @return $this
+     */
+    public function setStyle(?Style $pValue = null)
+    {
+        $this->style = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get hash code.
+     *
+     * @return string Hash code
+     */
+    public function getHashCode()
+    {
+        return md5(
+            $this->conditionType .
+            $this->operatorType .
+            implode(';', $this->condition) .
+            $this->style->getHashCode() .
+            __CLASS__
+        );
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $vars = get_object_vars($this);
+        foreach ($vars as $key => $value) {
+            if (is_object($value)) {
+                $this->$key = clone $value;
+            } else {
+                $this->$key = $value;
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Fill.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Fill.php
new file mode 100755
index 0000000..c6baeed
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Fill.php
@@ -0,0 +1,314 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Style;
+
+class Fill extends Supervisor
+{
+    // Fill types
+    const FILL_NONE = 'none';
+    const FILL_SOLID = 'solid';
+    const FILL_GRADIENT_LINEAR = 'linear';
+    const FILL_GRADIENT_PATH = 'path';
+    const FILL_PATTERN_DARKDOWN = 'darkDown';
+    const FILL_PATTERN_DARKGRAY = 'darkGray';
+    const FILL_PATTERN_DARKGRID = 'darkGrid';
+    const FILL_PATTERN_DARKHORIZONTAL = 'darkHorizontal';
+    const FILL_PATTERN_DARKTRELLIS = 'darkTrellis';
+    const FILL_PATTERN_DARKUP = 'darkUp';
+    const FILL_PATTERN_DARKVERTICAL = 'darkVertical';
+    const FILL_PATTERN_GRAY0625 = 'gray0625';
+    const FILL_PATTERN_GRAY125 = 'gray125';
+    const FILL_PATTERN_LIGHTDOWN = 'lightDown';
+    const FILL_PATTERN_LIGHTGRAY = 'lightGray';
+    const FILL_PATTERN_LIGHTGRID = 'lightGrid';
+    const FILL_PATTERN_LIGHTHORIZONTAL = 'lightHorizontal';
+    const FILL_PATTERN_LIGHTTRELLIS = 'lightTrellis';
+    const FILL_PATTERN_LIGHTUP = 'lightUp';
+    const FILL_PATTERN_LIGHTVERTICAL = 'lightVertical';
+    const FILL_PATTERN_MEDIUMGRAY = 'mediumGray';
+
+    /**
+     * @var int
+     */
+    public $startcolorIndex;
+
+    /**
+     * @var int
+     */
+    public $endcolorIndex;
+
+    /**
+     * Fill type.
+     *
+     * @var string
+     */
+    protected $fillType = self::FILL_NONE;
+
+    /**
+     * Rotation.
+     *
+     * @var float
+     */
+    protected $rotation = 0;
+
+    /**
+     * Start color.
+     *
+     * @var Color
+     */
+    protected $startColor;
+
+    /**
+     * End color.
+     *
+     * @var Color
+     */
+    protected $endColor;
+
+    /**
+     * Create a new Fill.
+     *
+     * @param bool $isSupervisor Flag indicating if this is a supervisor or not
+     *                                    Leave this value at default unless you understand exactly what
+     *                                        its ramifications are
+     * @param bool $isConditional Flag indicating if this is a conditional style or not
+     *                                    Leave this value at default unless you understand exactly what
+     *                                        its ramifications are
+     */
+    public function __construct($isSupervisor = false, $isConditional = false)
+    {
+        // Supervisor?
+        parent::__construct($isSupervisor);
+
+        // Initialise values
+        if ($isConditional) {
+            $this->fillType = null;
+        }
+        $this->startColor = new Color(Color::COLOR_WHITE, $isSupervisor, $isConditional);
+        $this->endColor = new Color(Color::COLOR_BLACK, $isSupervisor, $isConditional);
+
+        // bind parent if we are a supervisor
+        if ($isSupervisor) {
+            $this->startColor->bindParent($this, 'startColor');
+            $this->endColor->bindParent($this, 'endColor');
+        }
+    }
+
+    /**
+     * Get the shared style component for the currently active cell in currently active sheet.
+     * Only used for style supervisor.
+     *
+     * @return Fill
+     */
+    public function getSharedComponent()
+    {
+        return $this->parent->getSharedComponent()->getFill();
+    }
+
+    /**
+     * Build style array from subcomponents.
+     *
+     * @param array $array
+     *
+     * @return array
+     */
+    public function getStyleArray($array)
+    {
+        return ['fill' => $array];
+    }
+
+    /**
+     * Apply styles from array.
+     *
+     * <code>
+     * $spreadsheet->getActiveSheet()->getStyle('B2')->getFill()->applyFromArray(
+     *     [
+     *         'fillType' => Fill::FILL_GRADIENT_LINEAR,
+     *         'rotation' => 0,
+     *         'startColor' => [
+     *             'rgb' => '000000'
+     *         ],
+     *         'endColor' => [
+     *             'argb' => 'FFFFFFFF'
+     *         ]
+     *     ]
+     * );
+     * </code>
+     *
+     * @param array $pStyles Array containing style information
+     *
+     * @return $this
+     */
+    public function applyFromArray(array $pStyles)
+    {
+        if ($this->isSupervisor) {
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
+        } else {
+            if (isset($pStyles['fillType'])) {
+                $this->setFillType($pStyles['fillType']);
+            }
+            if (isset($pStyles['rotation'])) {
+                $this->setRotation($pStyles['rotation']);
+            }
+            if (isset($pStyles['startColor'])) {
+                $this->getStartColor()->applyFromArray($pStyles['startColor']);
+            }
+            if (isset($pStyles['endColor'])) {
+                $this->getEndColor()->applyFromArray($pStyles['endColor']);
+            }
+            if (isset($pStyles['color'])) {
+                $this->getStartColor()->applyFromArray($pStyles['color']);
+                $this->getEndColor()->applyFromArray($pStyles['color']);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Fill Type.
+     *
+     * @return string
+     */
+    public function getFillType()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getFillType();
+        }
+
+        return $this->fillType;
+    }
+
+    /**
+     * Set Fill Type.
+     *
+     * @param string $pValue Fill type, see self::FILL_*
+     *
+     * @return $this
+     */
+    public function setFillType($pValue)
+    {
+        if ($this->isSupervisor) {
+            $styleArray = $this->getStyleArray(['fillType' => $pValue]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->fillType = $pValue;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Rotation.
+     *
+     * @return float
+     */
+    public function getRotation()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getRotation();
+        }
+
+        return $this->rotation;
+    }
+
+    /**
+     * Set Rotation.
+     *
+     * @param float $pValue
+     *
+     * @return $this
+     */
+    public function setRotation($pValue)
+    {
+        if ($this->isSupervisor) {
+            $styleArray = $this->getStyleArray(['rotation' => $pValue]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->rotation = $pValue;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Start Color.
+     *
+     * @return Color
+     */
+    public function getStartColor()
+    {
+        return $this->startColor;
+    }
+
+    /**
+     * Set Start Color.
+     *
+     * @return $this
+     */
+    public function setStartColor(Color $pValue)
+    {
+        // make sure parameter is a real color and not a supervisor
+        $color = $pValue->getIsSupervisor() ? $pValue->getSharedComponent() : $pValue;
+
+        if ($this->isSupervisor) {
+            $styleArray = $this->getStartColor()->getStyleArray(['argb' => $color->getARGB()]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->startColor = $color;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get End Color.
+     *
+     * @return Color
+     */
+    public function getEndColor()
+    {
+        return $this->endColor;
+    }
+
+    /**
+     * Set End Color.
+     *
+     * @return $this
+     */
+    public function setEndColor(Color $pValue)
+    {
+        // make sure parameter is a real color and not a supervisor
+        $color = $pValue->getIsSupervisor() ? $pValue->getSharedComponent() : $pValue;
+
+        if ($this->isSupervisor) {
+            $styleArray = $this->getEndColor()->getStyleArray(['argb' => $color->getARGB()]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->endColor = $color;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get hash code.
+     *
+     * @return string Hash code
+     */
+    public function getHashCode()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getHashCode();
+        }
+        // Note that we don't care about colours for fill type NONE, but could have duplicate NONEs with
+        //  different hashes if we don't explicitly prevent this
+        return md5(
+            $this->getFillType() .
+            $this->getRotation() .
+            ($this->getFillType() !== self::FILL_NONE ? $this->getStartColor()->getHashCode() : '') .
+            ($this->getFillType() !== self::FILL_NONE ? $this->getEndColor()->getHashCode() : '') .
+            __CLASS__
+        );
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Font.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Font.php
new file mode 100755
index 0000000..eee7df0
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Font.php
@@ -0,0 +1,548 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Style;
+
+class Font extends Supervisor
+{
+    // Underline types
+    const UNDERLINE_NONE = 'none';
+    const UNDERLINE_DOUBLE = 'double';
+    const UNDERLINE_DOUBLEACCOUNTING = 'doubleAccounting';
+    const UNDERLINE_SINGLE = 'single';
+    const UNDERLINE_SINGLEACCOUNTING = 'singleAccounting';
+
+    /**
+     * Font Name.
+     *
+     * @var string
+     */
+    protected $name = 'Calibri';
+
+    /**
+     * Font Size.
+     *
+     * @var float
+     */
+    protected $size = 11;
+
+    /**
+     * Bold.
+     *
+     * @var bool
+     */
+    protected $bold = false;
+
+    /**
+     * Italic.
+     *
+     * @var bool
+     */
+    protected $italic = false;
+
+    /**
+     * Superscript.
+     *
+     * @var bool
+     */
+    protected $superscript = false;
+
+    /**
+     * Subscript.
+     *
+     * @var bool
+     */
+    protected $subscript = false;
+
+    /**
+     * Underline.
+     *
+     * @var string
+     */
+    protected $underline = self::UNDERLINE_NONE;
+
+    /**
+     * Strikethrough.
+     *
+     * @var bool
+     */
+    protected $strikethrough = false;
+
+    /**
+     * Foreground color.
+     *
+     * @var Color
+     */
+    protected $color;
+
+    /**
+     * @var int
+     */
+    public $colorIndex;
+
+    /**
+     * Create a new Font.
+     *
+     * @param bool $isSupervisor Flag indicating if this is a supervisor or not
+     *                                    Leave this value at default unless you understand exactly what
+     *                                        its ramifications are
+     * @param bool $isConditional Flag indicating if this is a conditional style or not
+     *                                    Leave this value at default unless you understand exactly what
+     *                                        its ramifications are
+     */
+    public function __construct($isSupervisor = false, $isConditional = false)
+    {
+        // Supervisor?
+        parent::__construct($isSupervisor);
+
+        // Initialise values
+        if ($isConditional) {
+            $this->name = null;
+            $this->size = null;
+            $this->bold = null;
+            $this->italic = null;
+            $this->superscript = null;
+            $this->subscript = null;
+            $this->underline = null;
+            $this->strikethrough = null;
+            $this->color = new Color(Color::COLOR_BLACK, $isSupervisor, $isConditional);
+        } else {
+            $this->color = new Color(Color::COLOR_BLACK, $isSupervisor);
+        }
+        // bind parent if we are a supervisor
+        if ($isSupervisor) {
+            $this->color->bindParent($this, 'color');
+        }
+    }
+
+    /**
+     * Get the shared style component for the currently active cell in currently active sheet.
+     * Only used for style supervisor.
+     *
+     * @return Font
+     */
+    public function getSharedComponent()
+    {
+        return $this->parent->getSharedComponent()->getFont();
+    }
+
+    /**
+     * Build style array from subcomponents.
+     *
+     * @param array $array
+     *
+     * @return array
+     */
+    public function getStyleArray($array)
+    {
+        return ['font' => $array];
+    }
+
+    /**
+     * Apply styles from array.
+     *
+     * <code>
+     * $spreadsheet->getActiveSheet()->getStyle('B2')->getFont()->applyFromArray(
+     *     [
+     *         'name' => 'Arial',
+     *         'bold' => TRUE,
+     *         'italic' => FALSE,
+     *         'underline' => \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLE,
+     *         'strikethrough' => FALSE,
+     *         'color' => [
+     *             'rgb' => '808080'
+     *         ]
+     *     ]
+     * );
+     * </code>
+     *
+     * @param array $pStyles Array containing style information
+     *
+     * @return $this
+     */
+    public function applyFromArray(array $pStyles)
+    {
+        if ($this->isSupervisor) {
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
+        } else {
+            if (isset($pStyles['name'])) {
+                $this->setName($pStyles['name']);
+            }
+            if (isset($pStyles['bold'])) {
+                $this->setBold($pStyles['bold']);
+            }
+            if (isset($pStyles['italic'])) {
+                $this->setItalic($pStyles['italic']);
+            }
+            if (isset($pStyles['superscript'])) {
+                $this->setSuperscript($pStyles['superscript']);
+            }
+            if (isset($pStyles['subscript'])) {
+                $this->setSubscript($pStyles['subscript']);
+            }
+            if (isset($pStyles['underline'])) {
+                $this->setUnderline($pStyles['underline']);
+            }
+            if (isset($pStyles['strikethrough'])) {
+                $this->setStrikethrough($pStyles['strikethrough']);
+            }
+            if (isset($pStyles['color'])) {
+                $this->getColor()->applyFromArray($pStyles['color']);
+            }
+            if (isset($pStyles['size'])) {
+                $this->setSize($pStyles['size']);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Name.
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getName();
+        }
+
+        return $this->name;
+    }
+
+    /**
+     * Set Name.
+     *
+     * @param string $pValue
+     *
+     * @return $this
+     */
+    public function setName($pValue)
+    {
+        if ($pValue == '') {
+            $pValue = 'Calibri';
+        }
+        if ($this->isSupervisor) {
+            $styleArray = $this->getStyleArray(['name' => $pValue]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->name = $pValue;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Size.
+     *
+     * @return float
+     */
+    public function getSize()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getSize();
+        }
+
+        return $this->size;
+    }
+
+    /**
+     * Set Size.
+     *
+     * @param float $pValue
+     *
+     * @return $this
+     */
+    public function setSize($pValue)
+    {
+        if ($pValue == '') {
+            $pValue = 10;
+        }
+        if ($this->isSupervisor) {
+            $styleArray = $this->getStyleArray(['size' => $pValue]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->size = $pValue;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Bold.
+     *
+     * @return bool
+     */
+    public function getBold()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getBold();
+        }
+
+        return $this->bold;
+    }
+
+    /**
+     * Set Bold.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setBold($pValue)
+    {
+        if ($pValue == '') {
+            $pValue = false;
+        }
+        if ($this->isSupervisor) {
+            $styleArray = $this->getStyleArray(['bold' => $pValue]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->bold = $pValue;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Italic.
+     *
+     * @return bool
+     */
+    public function getItalic()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getItalic();
+        }
+
+        return $this->italic;
+    }
+
+    /**
+     * Set Italic.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setItalic($pValue)
+    {
+        if ($pValue == '') {
+            $pValue = false;
+        }
+        if ($this->isSupervisor) {
+            $styleArray = $this->getStyleArray(['italic' => $pValue]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->italic = $pValue;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Superscript.
+     *
+     * @return bool
+     */
+    public function getSuperscript()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getSuperscript();
+        }
+
+        return $this->superscript;
+    }
+
+    /**
+     * Set Superscript.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setSuperscript($pValue)
+    {
+        if ($pValue == '') {
+            $pValue = false;
+        }
+        if ($this->isSupervisor) {
+            $styleArray = $this->getStyleArray(['superscript' => $pValue]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->superscript = $pValue;
+            $this->subscript = !$pValue;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Subscript.
+     *
+     * @return bool
+     */
+    public function getSubscript()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getSubscript();
+        }
+
+        return $this->subscript;
+    }
+
+    /**
+     * Set Subscript.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setSubscript($pValue)
+    {
+        if ($pValue == '') {
+            $pValue = false;
+        }
+        if ($this->isSupervisor) {
+            $styleArray = $this->getStyleArray(['subscript' => $pValue]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->subscript = $pValue;
+            $this->superscript = !$pValue;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Underline.
+     *
+     * @return string
+     */
+    public function getUnderline()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getUnderline();
+        }
+
+        return $this->underline;
+    }
+
+    /**
+     * Set Underline.
+     *
+     * @param bool|string $pValue \PhpOffice\PhpSpreadsheet\Style\Font underline type
+     *                                    If a boolean is passed, then TRUE equates to UNDERLINE_SINGLE,
+     *                                        false equates to UNDERLINE_NONE
+     *
+     * @return $this
+     */
+    public function setUnderline($pValue)
+    {
+        if (is_bool($pValue)) {
+            $pValue = ($pValue) ? self::UNDERLINE_SINGLE : self::UNDERLINE_NONE;
+        } elseif ($pValue == '') {
+            $pValue = self::UNDERLINE_NONE;
+        }
+        if ($this->isSupervisor) {
+            $styleArray = $this->getStyleArray(['underline' => $pValue]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->underline = $pValue;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Strikethrough.
+     *
+     * @return bool
+     */
+    public function getStrikethrough()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getStrikethrough();
+        }
+
+        return $this->strikethrough;
+    }
+
+    /**
+     * Set Strikethrough.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setStrikethrough($pValue)
+    {
+        if ($pValue == '') {
+            $pValue = false;
+        }
+
+        if ($this->isSupervisor) {
+            $styleArray = $this->getStyleArray(['strikethrough' => $pValue]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->strikethrough = $pValue;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Color.
+     *
+     * @return Color
+     */
+    public function getColor()
+    {
+        return $this->color;
+    }
+
+    /**
+     * Set Color.
+     *
+     * @return $this
+     */
+    public function setColor(Color $pValue)
+    {
+        // make sure parameter is a real color and not a supervisor
+        $color = $pValue->getIsSupervisor() ? $pValue->getSharedComponent() : $pValue;
+
+        if ($this->isSupervisor) {
+            $styleArray = $this->getColor()->getStyleArray(['argb' => $color->getARGB()]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->color = $color;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get hash code.
+     *
+     * @return string Hash code
+     */
+    public function getHashCode()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getHashCode();
+        }
+
+        return md5(
+            $this->name .
+            $this->size .
+            ($this->bold ? 't' : 'f') .
+            ($this->italic ? 't' : 'f') .
+            ($this->superscript ? 't' : 'f') .
+            ($this->subscript ? 't' : 'f') .
+            $this->underline .
+            ($this->strikethrough ? 't' : 'f') .
+            $this->color->getHashCode() .
+            __CLASS__
+        );
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat.php
new file mode 100755
index 0000000..259acab
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat.php
@@ -0,0 +1,873 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Style;
+
+use PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
+use PhpOffice\PhpSpreadsheet\Shared\Date;
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+
+class NumberFormat extends Supervisor
+{
+    // Pre-defined formats
+    const FORMAT_GENERAL = 'General';
+
+    const FORMAT_TEXT = '@';
+
+    const FORMAT_NUMBER = '0';
+    const FORMAT_NUMBER_00 = '0.00';
+    const FORMAT_NUMBER_COMMA_SEPARATED1 = '#,##0.00';
+    const FORMAT_NUMBER_COMMA_SEPARATED2 = '#,##0.00_-';
+
+    const FORMAT_PERCENTAGE = '0%';
+    const FORMAT_PERCENTAGE_00 = '0.00%';
+
+    const FORMAT_DATE_YYYYMMDD2 = 'yyyy-mm-dd';
+    const FORMAT_DATE_YYYYMMDD = 'yyyy-mm-dd';
+    const FORMAT_DATE_DDMMYYYY = 'dd/mm/yyyy';
+    const FORMAT_DATE_DMYSLASH = 'd/m/yy';
+    const FORMAT_DATE_DMYMINUS = 'd-m-yy';
+    const FORMAT_DATE_DMMINUS = 'd-m';
+    const FORMAT_DATE_MYMINUS = 'm-yy';
+    const FORMAT_DATE_XLSX14 = 'mm-dd-yy';
+    const FORMAT_DATE_XLSX15 = 'd-mmm-yy';
+    const FORMAT_DATE_XLSX16 = 'd-mmm';
+    const FORMAT_DATE_XLSX17 = 'mmm-yy';
+    const FORMAT_DATE_XLSX22 = 'm/d/yy h:mm';
+    const FORMAT_DATE_DATETIME = 'd/m/yy h:mm';
+    const FORMAT_DATE_TIME1 = 'h:mm AM/PM';
+    const FORMAT_DATE_TIME2 = 'h:mm:ss AM/PM';
+    const FORMAT_DATE_TIME3 = 'h:mm';
+    const FORMAT_DATE_TIME4 = 'h:mm:ss';
+    const FORMAT_DATE_TIME5 = 'mm:ss';
+    const FORMAT_DATE_TIME6 = 'h:mm:ss';
+    const FORMAT_DATE_TIME7 = 'i:s.S';
+    const FORMAT_DATE_TIME8 = 'h:mm:ss;@';
+    const FORMAT_DATE_YYYYMMDDSLASH = 'yyyy/mm/dd;@';
+
+    const FORMAT_CURRENCY_USD_SIMPLE = '"$"#,##0.00_-';
+    const FORMAT_CURRENCY_USD = '$#,##0_-';
+    const FORMAT_CURRENCY_EUR_SIMPLE = '#,##0.00_-"€"';
+    const FORMAT_CURRENCY_EUR = '#,##0_-"€"';
+    const FORMAT_ACCOUNTING_USD = '_("$"* #,##0.00_);_("$"* \(#,##0.00\);_("$"* "-"??_);_(@_)';
+    const FORMAT_ACCOUNTING_EUR = '_("€"* #,##0.00_);_("€"* \(#,##0.00\);_("€"* "-"??_);_(@_)';
+
+    /**
+     * Excel built-in number formats.
+     *
+     * @var array
+     */
+    protected static $builtInFormats;
+
+    /**
+     * Excel built-in number formats (flipped, for faster lookups).
+     *
+     * @var array
+     */
+    protected static $flippedBuiltInFormats;
+
+    /**
+     * Format Code.
+     *
+     * @var string
+     */
+    protected $formatCode = self::FORMAT_GENERAL;
+
+    /**
+     * Built-in format Code.
+     *
+     * @var string
+     */
+    protected $builtInFormatCode = 0;
+
+    /**
+     * Create a new NumberFormat.
+     *
+     * @param bool $isSupervisor Flag indicating if this is a supervisor or not
+     *                                    Leave this value at default unless you understand exactly what
+     *                                        its ramifications are
+     * @param bool $isConditional Flag indicating if this is a conditional style or not
+     *                                    Leave this value at default unless you understand exactly what
+     *                                        its ramifications are
+     */
+    public function __construct($isSupervisor = false, $isConditional = false)
+    {
+        // Supervisor?
+        parent::__construct($isSupervisor);
+
+        if ($isConditional) {
+            $this->formatCode = null;
+            $this->builtInFormatCode = false;
+        }
+    }
+
+    /**
+     * Get the shared style component for the currently active cell in currently active sheet.
+     * Only used for style supervisor.
+     *
+     * @return NumberFormat
+     */
+    public function getSharedComponent()
+    {
+        return $this->parent->getSharedComponent()->getNumberFormat();
+    }
+
+    /**
+     * Build style array from subcomponents.
+     *
+     * @param array $array
+     *
+     * @return array
+     */
+    public function getStyleArray($array)
+    {
+        return ['numberFormat' => $array];
+    }
+
+    /**
+     * Apply styles from array.
+     *
+     * <code>
+     * $spreadsheet->getActiveSheet()->getStyle('B2')->getNumberFormat()->applyFromArray(
+     *     [
+     *         'formatCode' => NumberFormat::FORMAT_CURRENCY_EUR_SIMPLE
+     *     ]
+     * );
+     * </code>
+     *
+     * @param array $pStyles Array containing style information
+     *
+     * @return $this
+     */
+    public function applyFromArray(array $pStyles)
+    {
+        if ($this->isSupervisor) {
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
+        } else {
+            if (isset($pStyles['formatCode'])) {
+                $this->setFormatCode($pStyles['formatCode']);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Format Code.
+     *
+     * @return string
+     */
+    public function getFormatCode()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getFormatCode();
+        }
+        if ($this->builtInFormatCode !== false) {
+            return self::builtInFormatCode($this->builtInFormatCode);
+        }
+
+        return $this->formatCode;
+    }
+
+    /**
+     * Set Format Code.
+     *
+     * @param string $pValue see self::FORMAT_*
+     *
+     * @return $this
+     */
+    public function setFormatCode($pValue)
+    {
+        if ($pValue == '') {
+            $pValue = self::FORMAT_GENERAL;
+        }
+        if ($this->isSupervisor) {
+            $styleArray = $this->getStyleArray(['formatCode' => $pValue]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->formatCode = $pValue;
+            $this->builtInFormatCode = self::builtInFormatCodeIndex($pValue);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Built-In Format Code.
+     *
+     * @return int
+     */
+    public function getBuiltInFormatCode()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getBuiltInFormatCode();
+        }
+
+        return $this->builtInFormatCode;
+    }
+
+    /**
+     * Set Built-In Format Code.
+     *
+     * @param int $pValue
+     *
+     * @return $this
+     */
+    public function setBuiltInFormatCode($pValue)
+    {
+        if ($this->isSupervisor) {
+            $styleArray = $this->getStyleArray(['formatCode' => self::builtInFormatCode($pValue)]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->builtInFormatCode = $pValue;
+            $this->formatCode = self::builtInFormatCode($pValue);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Fill built-in format codes.
+     */
+    private static function fillBuiltInFormatCodes(): void
+    {
+        //  [MS-OI29500: Microsoft Office Implementation Information for ISO/IEC-29500 Standard Compliance]
+        //  18.8.30. numFmt (Number Format)
+        //
+        //  The ECMA standard defines built-in format IDs
+        //      14: "mm-dd-yy"
+        //      22: "m/d/yy h:mm"
+        //      37: "#,##0 ;(#,##0)"
+        //      38: "#,##0 ;[Red](#,##0)"
+        //      39: "#,##0.00;(#,##0.00)"
+        //      40: "#,##0.00;[Red](#,##0.00)"
+        //      47: "mmss.0"
+        //      KOR fmt 55: "yyyy-mm-dd"
+        //  Excel defines built-in format IDs
+        //      14: "m/d/yyyy"
+        //      22: "m/d/yyyy h:mm"
+        //      37: "#,##0_);(#,##0)"
+        //      38: "#,##0_);[Red](#,##0)"
+        //      39: "#,##0.00_);(#,##0.00)"
+        //      40: "#,##0.00_);[Red](#,##0.00)"
+        //      47: "mm:ss.0"
+        //      KOR fmt 55: "yyyy/mm/dd"
+
+        // Built-in format codes
+        if (self::$builtInFormats === null) {
+            self::$builtInFormats = [];
+
+            // General
+            self::$builtInFormats[0] = self::FORMAT_GENERAL;
+            self::$builtInFormats[1] = '0';
+            self::$builtInFormats[2] = '0.00';
+            self::$builtInFormats[3] = '#,##0';
+            self::$builtInFormats[4] = '#,##0.00';
+
+            self::$builtInFormats[9] = '0%';
+            self::$builtInFormats[10] = '0.00%';
+            self::$builtInFormats[11] = '0.00E+00';
+            self::$builtInFormats[12] = '# ?/?';
+            self::$builtInFormats[13] = '# ??/??';
+            self::$builtInFormats[14] = 'm/d/yyyy'; // Despite ECMA 'mm-dd-yy';
+            self::$builtInFormats[15] = 'd-mmm-yy';
+            self::$builtInFormats[16] = 'd-mmm';
+            self::$builtInFormats[17] = 'mmm-yy';
+            self::$builtInFormats[18] = 'h:mm AM/PM';
+            self::$builtInFormats[19] = 'h:mm:ss AM/PM';
+            self::$builtInFormats[20] = 'h:mm';
+            self::$builtInFormats[21] = 'h:mm:ss';
+            self::$builtInFormats[22] = 'm/d/yyyy h:mm'; // Despite ECMA 'm/d/yy h:mm';
+
+            self::$builtInFormats[37] = '#,##0_);(#,##0)'; //  Despite ECMA '#,##0 ;(#,##0)';
+            self::$builtInFormats[38] = '#,##0_);[Red](#,##0)'; //  Despite ECMA '#,##0 ;[Red](#,##0)';
+            self::$builtInFormats[39] = '#,##0.00_);(#,##0.00)'; //  Despite ECMA '#,##0.00;(#,##0.00)';
+            self::$builtInFormats[40] = '#,##0.00_);[Red](#,##0.00)'; //  Despite ECMA '#,##0.00;[Red](#,##0.00)';
+
+            self::$builtInFormats[44] = '_("$"* #,##0.00_);_("$"* \(#,##0.00\);_("$"* "-"??_);_(@_)';
+            self::$builtInFormats[45] = 'mm:ss';
+            self::$builtInFormats[46] = '[h]:mm:ss';
+            self::$builtInFormats[47] = 'mm:ss.0'; //  Despite ECMA 'mmss.0';
+            self::$builtInFormats[48] = '##0.0E+0';
+            self::$builtInFormats[49] = '@';
+
+            // CHT
+            self::$builtInFormats[27] = '[$-404]e/m/d';
+            self::$builtInFormats[30] = 'm/d/yy';
+            self::$builtInFormats[36] = '[$-404]e/m/d';
+            self::$builtInFormats[50] = '[$-404]e/m/d';
+            self::$builtInFormats[57] = '[$-404]e/m/d';
+
+            // THA
+            self::$builtInFormats[59] = 't0';
+            self::$builtInFormats[60] = 't0.00';
+            self::$builtInFormats[61] = 't#,##0';
+            self::$builtInFormats[62] = 't#,##0.00';
+            self::$builtInFormats[67] = 't0%';
+            self::$builtInFormats[68] = 't0.00%';
+            self::$builtInFormats[69] = 't# ?/?';
+            self::$builtInFormats[70] = 't# ??/??';
+
+            // JPN
+            self::$builtInFormats[28] = '[$-411]ggge"年"m"月"d"日"';
+            self::$builtInFormats[29] = '[$-411]ggge"年"m"月"d"日"';
+            self::$builtInFormats[31] = 'yyyy"年"m"月"d"日"';
+            self::$builtInFormats[32] = 'h"時"mm"分"';
+            self::$builtInFormats[33] = 'h"時"mm"分"ss"秒"';
+            self::$builtInFormats[34] = 'yyyy"年"m"月"';
+            self::$builtInFormats[35] = 'm"月"d"日"';
+            self::$builtInFormats[51] = '[$-411]ggge"年"m"月"d"日"';
+            self::$builtInFormats[52] = 'yyyy"年"m"月"';
+            self::$builtInFormats[53] = 'm"月"d"日"';
+            self::$builtInFormats[54] = '[$-411]ggge"年"m"月"d"日"';
+            self::$builtInFormats[55] = 'yyyy"年"m"月"';
+            self::$builtInFormats[56] = 'm"月"d"日"';
+            self::$builtInFormats[58] = '[$-411]ggge"年"m"月"d"日"';
+
+            // Flip array (for faster lookups)
+            self::$flippedBuiltInFormats = array_flip(self::$builtInFormats);
+        }
+    }
+
+    /**
+     * Get built-in format code.
+     *
+     * @param int $pIndex
+     *
+     * @return string
+     */
+    public static function builtInFormatCode($pIndex)
+    {
+        // Clean parameter
+        $pIndex = (int) $pIndex;
+
+        // Ensure built-in format codes are available
+        self::fillBuiltInFormatCodes();
+
+        // Lookup format code
+        if (isset(self::$builtInFormats[$pIndex])) {
+            return self::$builtInFormats[$pIndex];
+        }
+
+        return '';
+    }
+
+    /**
+     * Get built-in format code index.
+     *
+     * @param string $formatCode
+     *
+     * @return bool|int
+     */
+    public static function builtInFormatCodeIndex($formatCode)
+    {
+        // Ensure built-in format codes are available
+        self::fillBuiltInFormatCodes();
+
+        // Lookup format code
+        if (array_key_exists($formatCode, self::$flippedBuiltInFormats)) {
+            return self::$flippedBuiltInFormats[$formatCode];
+        }
+
+        return false;
+    }
+
+    /**
+     * Get hash code.
+     *
+     * @return string Hash code
+     */
+    public function getHashCode()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getHashCode();
+        }
+
+        return md5(
+            $this->formatCode .
+            $this->builtInFormatCode .
+            __CLASS__
+        );
+    }
+
+    /**
+     * Search/replace values to convert Excel date/time format masks to PHP format masks.
+     *
+     * @var array
+     */
+    private static $dateFormatReplacements = [
+        // first remove escapes related to non-format characters
+        '\\' => '',
+        //    12-hour suffix
+        'am/pm' => 'A',
+        //    4-digit year
+        'e' => 'Y',
+        'yyyy' => 'Y',
+        //    2-digit year
+        'yy' => 'y',
+        //    first letter of month - no php equivalent
+        'mmmmm' => 'M',
+        //    full month name
+        'mmmm' => 'F',
+        //    short month name
+        'mmm' => 'M',
+        //    mm is minutes if time, but can also be month w/leading zero
+        //    so we try to identify times be the inclusion of a : separator in the mask
+        //    It isn't perfect, but the best way I know how
+        ':mm' => ':i',
+        'mm:' => 'i:',
+        //    month leading zero
+        'mm' => 'm',
+        //    month no leading zero
+        'm' => 'n',
+        //    full day of week name
+        'dddd' => 'l',
+        //    short day of week name
+        'ddd' => 'D',
+        //    days leading zero
+        'dd' => 'd',
+        //    days no leading zero
+        'd' => 'j',
+        //    seconds
+        'ss' => 's',
+        //    fractional seconds - no php equivalent
+        '.s' => '',
+    ];
+
+    /**
+     * Search/replace values to convert Excel date/time format masks hours to PHP format masks (24 hr clock).
+     *
+     * @var array
+     */
+    private static $dateFormatReplacements24 = [
+        'hh' => 'H',
+        'h' => 'G',
+    ];
+
+    /**
+     * Search/replace values to convert Excel date/time format masks hours to PHP format masks (12 hr clock).
+     *
+     * @var array
+     */
+    private static $dateFormatReplacements12 = [
+        'hh' => 'h',
+        'h' => 'g',
+    ];
+
+    private static function setLowercaseCallback($matches)
+    {
+        return mb_strtolower($matches[0]);
+    }
+
+    private static function escapeQuotesCallback($matches)
+    {
+        return '\\' . implode('\\', str_split($matches[1]));
+    }
+
+    private static function formatAsDate(&$value, &$format): void
+    {
+        // strip off first part containing e.g. [$-F800] or [$USD-409]
+        // general syntax: [$<Currency string>-<language info>]
+        // language info is in hexadecimal
+        // strip off chinese part like [DBNum1][$-804]
+        $format = preg_replace('/^(\[[0-9A-Za-z]*\])*(\[\$[A-Z]*-[0-9A-F]*\])/i', '', $format);
+
+        // OpenOffice.org uses upper-case number formats, e.g. 'YYYY', convert to lower-case;
+        //    but we don't want to change any quoted strings
+        $format = preg_replace_callback('/(?:^|")([^"]*)(?:$|")/', ['self', 'setLowercaseCallback'], $format);
+
+        // Only process the non-quoted blocks for date format characters
+        $blocks = explode('"', $format);
+        foreach ($blocks as $key => &$block) {
+            if ($key % 2 == 0) {
+                $block = strtr($block, self::$dateFormatReplacements);
+                if (!strpos($block, 'A')) {
+                    // 24-hour time format
+                    // when [h]:mm format, the [h] should replace to the hours of the value * 24
+                    if (false !== strpos($block, '[h]')) {
+                        $hours = (int) ($value * 24);
+                        $block = str_replace('[h]', $hours, $block);
+
+                        continue;
+                    }
+                    $block = strtr($block, self::$dateFormatReplacements24);
+                } else {
+                    // 12-hour time format
+                    $block = strtr($block, self::$dateFormatReplacements12);
+                }
+            }
+        }
+        $format = implode('"', $blocks);
+
+        // escape any quoted characters so that DateTime format() will render them correctly
+        $format = preg_replace_callback('/"(.*)"/U', ['self', 'escapeQuotesCallback'], $format);
+
+        $dateObj = Date::excelToDateTimeObject($value);
+        $value = $dateObj->format($format);
+    }
+
+    private static function formatAsPercentage(&$value, &$format): void
+    {
+        if ($format === self::FORMAT_PERCENTAGE) {
+            $value = round((100 * $value), 0) . '%';
+        } else {
+            if (preg_match('/\.[#0]+/', $format, $m)) {
+                $s = substr($m[0], 0, 1) . (strlen($m[0]) - 1);
+                $format = str_replace($m[0], $s, $format);
+            }
+            if (preg_match('/^[#0]+/', $format, $m)) {
+                $format = str_replace($m[0], strlen($m[0]), $format);
+            }
+            $format = '%' . str_replace('%', 'f%%', $format);
+
+            $value = sprintf($format, 100 * $value);
+        }
+    }
+
+    private static function formatAsFraction(&$value, &$format): void
+    {
+        $sign = ($value < 0) ? '-' : '';
+
+        $integerPart = floor(abs($value));
+        $decimalPart = trim(fmod(abs($value), 1), '0.');
+        $decimalLength = strlen($decimalPart);
+        $decimalDivisor = 10 ** $decimalLength;
+
+        $GCD = MathTrig::GCD($decimalPart, $decimalDivisor);
+
+        $adjustedDecimalPart = $decimalPart / $GCD;
+        $adjustedDecimalDivisor = $decimalDivisor / $GCD;
+
+        if ((strpos($format, '0') !== false)) {
+            $value = "$sign$integerPart $adjustedDecimalPart/$adjustedDecimalDivisor";
+        } elseif ((strpos($format, '#') !== false)) {
+            if ($integerPart == 0) {
+                $value = "$sign$adjustedDecimalPart/$adjustedDecimalDivisor";
+            } else {
+                $value = "$sign$integerPart $adjustedDecimalPart/$adjustedDecimalDivisor";
+            }
+        } elseif ((substr($format, 0, 3) == '? ?')) {
+            if ($integerPart == 0) {
+                $integerPart = '';
+            }
+            $value = "$sign$integerPart $adjustedDecimalPart/$adjustedDecimalDivisor";
+        } else {
+            $adjustedDecimalPart += $integerPart * $adjustedDecimalDivisor;
+            $value = "$sign$adjustedDecimalPart/$adjustedDecimalDivisor";
+        }
+    }
+
+    private static function mergeComplexNumberFormatMasks($numbers, $masks)
+    {
+        $decimalCount = strlen($numbers[1]);
+        $postDecimalMasks = [];
+
+        do {
+            $tempMask = array_pop($masks);
+            $postDecimalMasks[] = $tempMask;
+            $decimalCount -= strlen($tempMask);
+        } while ($decimalCount > 0);
+
+        return [
+            implode('.', $masks),
+            implode('.', array_reverse($postDecimalMasks)),
+        ];
+    }
+
+    private static function processComplexNumberFormatMask($number, $mask)
+    {
+        $result = $number;
+        $maskingBlockCount = preg_match_all('/0+/', $mask, $maskingBlocks, PREG_OFFSET_CAPTURE);
+
+        if ($maskingBlockCount > 1) {
+            $maskingBlocks = array_reverse($maskingBlocks[0]);
+
+            foreach ($maskingBlocks as $block) {
+                $divisor = 1 . $block[0];
+                $size = strlen($block[0]);
+                $offset = $block[1];
+
+                $blockValue = sprintf(
+                    '%0' . $size . 'd',
+                    fmod($number, $divisor)
+                );
+                $number = floor($number / $divisor);
+                $mask = substr_replace($mask, $blockValue, $offset, $size);
+            }
+            if ($number > 0) {
+                $mask = substr_replace($mask, $number, $offset, 0);
+            }
+            $result = $mask;
+        }
+
+        return $result;
+    }
+
+    private static function complexNumberFormatMask($number, $mask, $splitOnPoint = true)
+    {
+        $sign = ($number < 0.0);
+        $number = abs($number);
+
+        if ($splitOnPoint && strpos($mask, '.') !== false && strpos($number, '.') !== false) {
+            $numbers = explode('.', $number);
+            $masks = explode('.', $mask);
+            if (count($masks) > 2) {
+                $masks = self::mergeComplexNumberFormatMasks($numbers, $masks);
+            }
+            $result1 = self::complexNumberFormatMask($numbers[0], $masks[0], false);
+            $result2 = strrev(self::complexNumberFormatMask(strrev($numbers[1]), strrev($masks[1]), false));
+
+            return (($sign) ? '-' : '') . $result1 . '.' . $result2;
+        }
+
+        $result = self::processComplexNumberFormatMask($number, $mask);
+
+        return (($sign) ? '-' : '') . $result;
+    }
+
+    private static function formatStraightNumericValue($value, $format, array $matches, $useThousands, $number_regex)
+    {
+        $left = $matches[1];
+        $dec = $matches[2];
+        $right = $matches[3];
+
+        // minimun width of formatted number (including dot)
+        $minWidth = strlen($left) + strlen($dec) + strlen($right);
+        if ($useThousands) {
+            $value = number_format(
+                $value,
+                strlen($right),
+                StringHelper::getDecimalSeparator(),
+                StringHelper::getThousandsSeparator()
+            );
+            $value = preg_replace($number_regex, $value, $format);
+        } else {
+            if (preg_match('/[0#]E[+-]0/i', $format)) {
+                //    Scientific format
+                $value = sprintf('%5.2E', $value);
+            } elseif (preg_match('/0([^\d\.]+)0/', $format) || substr_count($format, '.') > 1) {
+                if ($value == (int) $value && substr_count($format, '.') === 1) {
+                    $value *= 10 ** strlen(explode('.', $format)[1]);
+                }
+                $value = self::complexNumberFormatMask($value, $format);
+            } else {
+                $sprintf_pattern = "%0$minWidth." . strlen($right) . 'f';
+                $value = sprintf($sprintf_pattern, $value);
+                $value = preg_replace($number_regex, $value, $format);
+            }
+        }
+
+        return $value;
+    }
+
+    private static function formatAsNumber($value, $format)
+    {
+        // The "_" in this string has already been stripped out,
+        // so this test is never true. Furthermore, testing
+        // on Excel shows this format uses Euro symbol, not "EUR".
+        //if ($format === self::FORMAT_CURRENCY_EUR_SIMPLE) {
+        //    return 'EUR ' . sprintf('%1.2f', $value);
+        //}
+
+        // Some non-number strings are quoted, so we'll get rid of the quotes, likewise any positional * symbols
+        $format = str_replace(['"', '*'], '', $format);
+
+        // Find out if we need thousands separator
+        // This is indicated by a comma enclosed by a digit placeholder:
+        //        #,#   or   0,0
+        $useThousands = preg_match('/(#,#|0,0)/', $format);
+        if ($useThousands) {
+            $format = preg_replace('/0,0/', '00', $format);
+            $format = preg_replace('/#,#/', '##', $format);
+        }
+
+        // Scale thousands, millions,...
+        // This is indicated by a number of commas after a digit placeholder:
+        //        #,   or    0.0,,
+        $scale = 1; // same as no scale
+        $matches = [];
+        if (preg_match('/(#|0)(,+)/', $format, $matches)) {
+            $scale = 1000 ** strlen($matches[2]);
+
+            // strip the commas
+            $format = preg_replace('/0,+/', '0', $format);
+            $format = preg_replace('/#,+/', '#', $format);
+        }
+
+        if (preg_match('/#?.*\?\/\?/', $format, $m)) {
+            if ($value != (int) $value) {
+                self::formatAsFraction($value, $format);
+            }
+        } else {
+            // Handle the number itself
+
+            // scale number
+            $value = $value / $scale;
+            // Strip #
+            $format = preg_replace('/\\#/', '0', $format);
+            // Remove locale code [$-###]
+            $format = preg_replace('/\[\$\-.*\]/', '', $format);
+
+            $n = '/\\[[^\\]]+\\]/';
+            $m = preg_replace($n, '', $format);
+            $number_regex = '/(0+)(\\.?)(0*)/';
+            if (preg_match($number_regex, $m, $matches)) {
+                $value = self::formatStraightNumericValue($value, $format, $matches, $useThousands, $number_regex);
+            }
+        }
+
+        if (preg_match('/\[\$(.*)\]/u', $format, $m)) {
+            //  Currency or Accounting
+            $currencyCode = $m[1];
+            [$currencyCode] = explode('-', $currencyCode);
+            if ($currencyCode == '') {
+                $currencyCode = StringHelper::getCurrencyCode();
+            }
+            $value = preg_replace('/\[\$([^\]]*)\]/u', $currencyCode, $value);
+        }
+
+        return $value;
+    }
+
+    private static function splitFormatCompare($value, $cond, $val, $dfcond, $dfval)
+    {
+        if (!$cond) {
+            $cond = $dfcond;
+            $val = $dfval;
+        }
+        switch ($cond) {
+            case '>':
+                return $value > $val;
+
+            case '<':
+                return $value < $val;
+
+            case '<=':
+                return $value <= $val;
+
+            case '<>':
+                return $value != $val;
+
+            case '=':
+                return $value == $val;
+        }
+
+        return $value >= $val;
+    }
+
+    private static function splitFormat($sections, $value)
+    {
+        // Extract the relevant section depending on whether number is positive, negative, or zero?
+        // Text not supported yet.
+        // Here is how the sections apply to various values in Excel:
+        //   1 section:   [POSITIVE/NEGATIVE/ZERO/TEXT]
+        //   2 sections:  [POSITIVE/ZERO/TEXT] [NEGATIVE]
+        //   3 sections:  [POSITIVE/TEXT] [NEGATIVE] [ZERO]
+        //   4 sections:  [POSITIVE] [NEGATIVE] [ZERO] [TEXT]
+        $cnt = count($sections);
+        $color_regex = '/\\[(' . implode('|', Color::NAMED_COLORS) . ')\\]/';
+        $cond_regex = '/\\[(>|>=|<|<=|=|<>)([+-]?\\d+([.]\\d+)?)\\]/';
+        $colors = ['', '', '', '', ''];
+        $condops = ['', '', '', '', ''];
+        $condvals = [0, 0, 0, 0, 0];
+        for ($idx = 0; $idx < $cnt; ++$idx) {
+            if (preg_match($color_regex, $sections[$idx], $matches)) {
+                $colors[$idx] = $matches[0];
+                $sections[$idx] = preg_replace($color_regex, '', $sections[$idx]);
+            }
+            if (preg_match($cond_regex, $sections[$idx], $matches)) {
+                $condops[$idx] = $matches[1];
+                $condvals[$idx] = $matches[2];
+                $sections[$idx] = preg_replace($cond_regex, '', $sections[$idx]);
+            }
+        }
+        $color = $colors[0];
+        $format = $sections[0];
+        $absval = $value;
+        switch ($cnt) {
+            case 2:
+                $absval = abs($value);
+                if (!self::splitFormatCompare($value, $condops[0], $condvals[0], '>=', 0)) {
+                    $color = $colors[1];
+                    $format = $sections[1];
+                }
+
+                break;
+            case 3:
+            case 4:
+                $absval = abs($value);
+                if (!self::splitFormatCompare($value, $condops[0], $condvals[0], '>', 0)) {
+                    if (self::splitFormatCompare($value, $condops[1], $condvals[1], '<', 0)) {
+                        $color = $colors[1];
+                        $format = $sections[1];
+                    } else {
+                        $color = $colors[2];
+                        $format = $sections[2];
+                    }
+                }
+
+                break;
+        }
+
+        return [$color, $format, $absval];
+    }
+
+    /**
+     * Convert a value in a pre-defined format to a PHP string.
+     *
+     * @param mixed $value Value to format
+     * @param string $format Format code, see = self::FORMAT_*
+     * @param array $callBack Callback function for additional formatting of string
+     *
+     * @return string Formatted string
+     */
+    public static function toFormattedString($value, $format, $callBack = null)
+    {
+        // For now we do not treat strings although section 4 of a format code affects strings
+        if (!is_numeric($value)) {
+            return $value;
+        }
+
+        // For 'General' format code, we just pass the value although this is not entirely the way Excel does it,
+        // it seems to round numbers to a total of 10 digits.
+        if (($format === self::FORMAT_GENERAL) || ($format === self::FORMAT_TEXT)) {
+            return $value;
+        }
+
+        // Convert any other escaped characters to quoted strings, e.g. (\T to "T")
+        $format = preg_replace('/(\\\(((.)(?!((AM\/PM)|(A\/P))))|([^ ])))(?=(?:[^"]|"[^"]*")*$)/u', '"${2}"', $format);
+
+        // Get the sections, there can be up to four sections, separated with a semi-colon (but only if not a quoted literal)
+        $sections = preg_split('/(;)(?=(?:[^"]|"[^"]*")*$)/u', $format);
+
+        [$colors, $format, $value] = self::splitFormat($sections, $value);
+
+        // In Excel formats, "_" is used to add spacing,
+        //    The following character indicates the size of the spacing, which we can't do in HTML, so we just use a standard space
+        $format = preg_replace('/_./', ' ', $format);
+
+        // Let's begin inspecting the format and converting the value to a formatted string
+
+        //  Check for date/time characters (not inside quotes)
+        if (preg_match('/(\[\$[A-Z]*-[0-9A-F]*\])*[hmsdy](?=(?:[^"]|"[^"]*")*$)/miu', $format, $matches)) {
+            // datetime format
+            self::formatAsDate($value, $format);
+        } else {
+            if (substr($format, 0, 1) === '"' && substr($format, -1, 1) === '"') {
+                $value = substr($format, 1, -1);
+            } elseif (preg_match('/%$/', $format)) {
+                // % number format
+                self::formatAsPercentage($value, $format);
+            } else {
+                $value = self::formatAsNumber($value, $format);
+            }
+        }
+
+        // Additional formatting provided by callback function
+        if ($callBack !== null) {
+            [$writerInstance, $function] = $callBack;
+            $value = $writerInstance->$function($value, $colors);
+        }
+
+        return $value;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Protection.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Protection.php
new file mode 100755
index 0000000..f695837
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Protection.php
@@ -0,0 +1,186 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Style;
+
+class Protection extends Supervisor
+{
+    /** Protection styles */
+    const PROTECTION_INHERIT = 'inherit';
+    const PROTECTION_PROTECTED = 'protected';
+    const PROTECTION_UNPROTECTED = 'unprotected';
+
+    /**
+     * Locked.
+     *
+     * @var string
+     */
+    protected $locked;
+
+    /**
+     * Hidden.
+     *
+     * @var string
+     */
+    protected $hidden;
+
+    /**
+     * Create a new Protection.
+     *
+     * @param bool $isSupervisor Flag indicating if this is a supervisor or not
+     *                                    Leave this value at default unless you understand exactly what
+     *                                        its ramifications are
+     * @param bool $isConditional Flag indicating if this is a conditional style or not
+     *                                    Leave this value at default unless you understand exactly what
+     *                                        its ramifications are
+     */
+    public function __construct($isSupervisor = false, $isConditional = false)
+    {
+        // Supervisor?
+        parent::__construct($isSupervisor);
+
+        // Initialise values
+        if (!$isConditional) {
+            $this->locked = self::PROTECTION_INHERIT;
+            $this->hidden = self::PROTECTION_INHERIT;
+        }
+    }
+
+    /**
+     * Get the shared style component for the currently active cell in currently active sheet.
+     * Only used for style supervisor.
+     *
+     * @return Protection
+     */
+    public function getSharedComponent()
+    {
+        return $this->parent->getSharedComponent()->getProtection();
+    }
+
+    /**
+     * Build style array from subcomponents.
+     *
+     * @param array $array
+     *
+     * @return array
+     */
+    public function getStyleArray($array)
+    {
+        return ['protection' => $array];
+    }
+
+    /**
+     * Apply styles from array.
+     *
+     * <code>
+     * $spreadsheet->getActiveSheet()->getStyle('B2')->getLocked()->applyFromArray(
+     *     [
+     *         'locked' => TRUE,
+     *         'hidden' => FALSE
+     *     ]
+     * );
+     * </code>
+     *
+     * @param array $pStyles Array containing style information
+     *
+     * @return $this
+     */
+    public function applyFromArray(array $pStyles)
+    {
+        if ($this->isSupervisor) {
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
+        } else {
+            if (isset($pStyles['locked'])) {
+                $this->setLocked($pStyles['locked']);
+            }
+            if (isset($pStyles['hidden'])) {
+                $this->setHidden($pStyles['hidden']);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get locked.
+     *
+     * @return string
+     */
+    public function getLocked()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getLocked();
+        }
+
+        return $this->locked;
+    }
+
+    /**
+     * Set locked.
+     *
+     * @param string $pValue see self::PROTECTION_*
+     *
+     * @return $this
+     */
+    public function setLocked($pValue)
+    {
+        if ($this->isSupervisor) {
+            $styleArray = $this->getStyleArray(['locked' => $pValue]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->locked = $pValue;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get hidden.
+     *
+     * @return string
+     */
+    public function getHidden()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getHidden();
+        }
+
+        return $this->hidden;
+    }
+
+    /**
+     * Set hidden.
+     *
+     * @param string $pValue see self::PROTECTION_*
+     *
+     * @return $this
+     */
+    public function setHidden($pValue)
+    {
+        if ($this->isSupervisor) {
+            $styleArray = $this->getStyleArray(['hidden' => $pValue]);
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->hidden = $pValue;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get hash code.
+     *
+     * @return string Hash code
+     */
+    public function getHashCode()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getHashCode();
+        }
+
+        return md5(
+            $this->locked .
+            $this->hidden .
+            __CLASS__
+        );
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Style.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Style.php
new file mode 100755
index 0000000..533a7c3
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Style.php
@@ -0,0 +1,639 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Style;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+
+class Style extends Supervisor
+{
+    /**
+     * Font.
+     *
+     * @var Font
+     */
+    protected $font;
+
+    /**
+     * Fill.
+     *
+     * @var Fill
+     */
+    protected $fill;
+
+    /**
+     * Borders.
+     *
+     * @var Borders
+     */
+    protected $borders;
+
+    /**
+     * Alignment.
+     *
+     * @var Alignment
+     */
+    protected $alignment;
+
+    /**
+     * Number Format.
+     *
+     * @var NumberFormat
+     */
+    protected $numberFormat;
+
+    /**
+     * Conditional styles.
+     *
+     * @var Conditional[]
+     */
+    protected $conditionalStyles;
+
+    /**
+     * Protection.
+     *
+     * @var Protection
+     */
+    protected $protection;
+
+    /**
+     * Index of style in collection. Only used for real style.
+     *
+     * @var int
+     */
+    protected $index;
+
+    /**
+     * Use Quote Prefix when displaying in cell editor. Only used for real style.
+     *
+     * @var bool
+     */
+    protected $quotePrefix = false;
+
+    /**
+     * Create a new Style.
+     *
+     * @param bool $isSupervisor Flag indicating if this is a supervisor or not
+     *         Leave this value at default unless you understand exactly what
+     *    its ramifications are
+     * @param bool $isConditional Flag indicating if this is a conditional style or not
+     *       Leave this value at default unless you understand exactly what
+     *    its ramifications are
+     */
+    public function __construct($isSupervisor = false, $isConditional = false)
+    {
+        parent::__construct($isSupervisor);
+
+        // Initialise values
+        $this->conditionalStyles = [];
+        $this->font = new Font($isSupervisor, $isConditional);
+        $this->fill = new Fill($isSupervisor, $isConditional);
+        $this->borders = new Borders($isSupervisor, $isConditional);
+        $this->alignment = new Alignment($isSupervisor, $isConditional);
+        $this->numberFormat = new NumberFormat($isSupervisor, $isConditional);
+        $this->protection = new Protection($isSupervisor, $isConditional);
+
+        // bind parent if we are a supervisor
+        if ($isSupervisor) {
+            $this->font->bindParent($this);
+            $this->fill->bindParent($this);
+            $this->borders->bindParent($this);
+            $this->alignment->bindParent($this);
+            $this->numberFormat->bindParent($this);
+            $this->protection->bindParent($this);
+        }
+    }
+
+    /**
+     * Get the shared style component for the currently active cell in currently active sheet.
+     * Only used for style supervisor.
+     *
+     * @return Style
+     */
+    public function getSharedComponent()
+    {
+        $activeSheet = $this->getActiveSheet();
+        $selectedCell = $this->getActiveCell(); // e.g. 'A1'
+
+        if ($activeSheet->cellExists($selectedCell)) {
+            $xfIndex = $activeSheet->getCell($selectedCell)->getXfIndex();
+        } else {
+            $xfIndex = 0;
+        }
+
+        return $this->parent->getCellXfByIndex($xfIndex);
+    }
+
+    /**
+     * Get parent. Only used for style supervisor.
+     *
+     * @return Spreadsheet
+     */
+    public function getParent()
+    {
+        return $this->parent;
+    }
+
+    /**
+     * Build style array from subcomponents.
+     *
+     * @param array $array
+     *
+     * @return array
+     */
+    public function getStyleArray($array)
+    {
+        return ['quotePrefix' => $array];
+    }
+
+    /**
+     * Apply styles from array.
+     *
+     * <code>
+     * $spreadsheet->getActiveSheet()->getStyle('B2')->applyFromArray(
+     *     [
+     *         'font' => [
+     *             'name' => 'Arial',
+     *             'bold' => true,
+     *             'italic' => false,
+     *             'underline' => Font::UNDERLINE_DOUBLE,
+     *             'strikethrough' => false,
+     *             'color' => [
+     *                 'rgb' => '808080'
+     *             ]
+     *         ],
+     *         'borders' => [
+     *             'bottom' => [
+     *                 'borderStyle' => Border::BORDER_DASHDOT,
+     *                 'color' => [
+     *                     'rgb' => '808080'
+     *                 ]
+     *             ],
+     *             'top' => [
+     *                 'borderStyle' => Border::BORDER_DASHDOT,
+     *                 'color' => [
+     *                     'rgb' => '808080'
+     *                 ]
+     *             ]
+     *         ],
+     *         'alignment' => [
+     *             'horizontal' => Alignment::HORIZONTAL_CENTER,
+     *             'vertical' => Alignment::VERTICAL_CENTER,
+     *             'wrapText' => true,
+     *         ],
+     *         'quotePrefix'    => true
+     *     ]
+     * );
+     * </code>
+     *
+     * @param array $pStyles Array containing style information
+     * @param bool $pAdvanced advanced mode for setting borders
+     *
+     * @return $this
+     */
+    public function applyFromArray(array $pStyles, $pAdvanced = true)
+    {
+        if ($this->isSupervisor) {
+            $pRange = $this->getSelectedCells();
+
+            // Uppercase coordinate
+            $pRange = strtoupper($pRange);
+
+            // Is it a cell range or a single cell?
+            if (strpos($pRange, ':') === false) {
+                $rangeA = $pRange;
+                $rangeB = $pRange;
+            } else {
+                [$rangeA, $rangeB] = explode(':', $pRange);
+            }
+
+            // Calculate range outer borders
+            $rangeStart = Coordinate::coordinateFromString($rangeA);
+            $rangeEnd = Coordinate::coordinateFromString($rangeB);
+
+            // Translate column into index
+            $rangeStart[0] = Coordinate::columnIndexFromString($rangeStart[0]);
+            $rangeEnd[0] = Coordinate::columnIndexFromString($rangeEnd[0]);
+
+            // Make sure we can loop upwards on rows and columns
+            if ($rangeStart[0] > $rangeEnd[0] && $rangeStart[1] > $rangeEnd[1]) {
+                $tmp = $rangeStart;
+                $rangeStart = $rangeEnd;
+                $rangeEnd = $tmp;
+            }
+
+            // ADVANCED MODE:
+            if ($pAdvanced && isset($pStyles['borders'])) {
+                // 'allBorders' is a shorthand property for 'outline' and 'inside' and
+                //        it applies to components that have not been set explicitly
+                if (isset($pStyles['borders']['allBorders'])) {
+                    foreach (['outline', 'inside'] as $component) {
+                        if (!isset($pStyles['borders'][$component])) {
+                            $pStyles['borders'][$component] = $pStyles['borders']['allBorders'];
+                        }
+                    }
+                    unset($pStyles['borders']['allBorders']); // not needed any more
+                }
+                // 'outline' is a shorthand property for 'top', 'right', 'bottom', 'left'
+                //        it applies to components that have not been set explicitly
+                if (isset($pStyles['borders']['outline'])) {
+                    foreach (['top', 'right', 'bottom', 'left'] as $component) {
+                        if (!isset($pStyles['borders'][$component])) {
+                            $pStyles['borders'][$component] = $pStyles['borders']['outline'];
+                        }
+                    }
+                    unset($pStyles['borders']['outline']); // not needed any more
+                }
+                // 'inside' is a shorthand property for 'vertical' and 'horizontal'
+                //        it applies to components that have not been set explicitly
+                if (isset($pStyles['borders']['inside'])) {
+                    foreach (['vertical', 'horizontal'] as $component) {
+                        if (!isset($pStyles['borders'][$component])) {
+                            $pStyles['borders'][$component] = $pStyles['borders']['inside'];
+                        }
+                    }
+                    unset($pStyles['borders']['inside']); // not needed any more
+                }
+                // width and height characteristics of selection, 1, 2, or 3 (for 3 or more)
+                $xMax = min($rangeEnd[0] - $rangeStart[0] + 1, 3);
+                $yMax = min($rangeEnd[1] - $rangeStart[1] + 1, 3);
+
+                // loop through up to 3 x 3 = 9 regions
+                for ($x = 1; $x <= $xMax; ++$x) {
+                    // start column index for region
+                    $colStart = ($x == 3) ?
+                        Coordinate::stringFromColumnIndex($rangeEnd[0])
+                            : Coordinate::stringFromColumnIndex($rangeStart[0] + $x - 1);
+                    // end column index for region
+                    $colEnd = ($x == 1) ?
+                        Coordinate::stringFromColumnIndex($rangeStart[0])
+                            : Coordinate::stringFromColumnIndex($rangeEnd[0] - $xMax + $x);
+
+                    for ($y = 1; $y <= $yMax; ++$y) {
+                        // which edges are touching the region
+                        $edges = [];
+                        if ($x == 1) {
+                            // are we at left edge
+                            $edges[] = 'left';
+                        }
+                        if ($x == $xMax) {
+                            // are we at right edge
+                            $edges[] = 'right';
+                        }
+                        if ($y == 1) {
+                            // are we at top edge?
+                            $edges[] = 'top';
+                        }
+                        if ($y == $yMax) {
+                            // are we at bottom edge?
+                            $edges[] = 'bottom';
+                        }
+
+                        // start row index for region
+                        $rowStart = ($y == 3) ?
+                            $rangeEnd[1] : $rangeStart[1] + $y - 1;
+
+                        // end row index for region
+                        $rowEnd = ($y == 1) ?
+                            $rangeStart[1] : $rangeEnd[1] - $yMax + $y;
+
+                        // build range for region
+                        $range = $colStart . $rowStart . ':' . $colEnd . $rowEnd;
+
+                        // retrieve relevant style array for region
+                        $regionStyles = $pStyles;
+                        unset($regionStyles['borders']['inside']);
+
+                        // what are the inner edges of the region when looking at the selection
+                        $innerEdges = array_diff(['top', 'right', 'bottom', 'left'], $edges);
+
+                        // inner edges that are not touching the region should take the 'inside' border properties if they have been set
+                        foreach ($innerEdges as $innerEdge) {
+                            switch ($innerEdge) {
+                                case 'top':
+                                case 'bottom':
+                                    // should pick up 'horizontal' border property if set
+                                    if (isset($pStyles['borders']['horizontal'])) {
+                                        $regionStyles['borders'][$innerEdge] = $pStyles['borders']['horizontal'];
+                                    } else {
+                                        unset($regionStyles['borders'][$innerEdge]);
+                                    }
+
+                                    break;
+                                case 'left':
+                                case 'right':
+                                    // should pick up 'vertical' border property if set
+                                    if (isset($pStyles['borders']['vertical'])) {
+                                        $regionStyles['borders'][$innerEdge] = $pStyles['borders']['vertical'];
+                                    } else {
+                                        unset($regionStyles['borders'][$innerEdge]);
+                                    }
+
+                                    break;
+                            }
+                        }
+
+                        // apply region style to region by calling applyFromArray() in simple mode
+                        $this->getActiveSheet()->getStyle($range)->applyFromArray($regionStyles, false);
+                    }
+                }
+
+                // restore initial cell selection range
+                $this->getActiveSheet()->getStyle($pRange);
+
+                return $this;
+            }
+
+            // SIMPLE MODE:
+            // Selection type, inspect
+            if (preg_match('/^[A-Z]+1:[A-Z]+1048576$/', $pRange)) {
+                $selectionType = 'COLUMN';
+            } elseif (preg_match('/^A\d+:XFD\d+$/', $pRange)) {
+                $selectionType = 'ROW';
+            } else {
+                $selectionType = 'CELL';
+            }
+
+            // First loop through columns, rows, or cells to find out which styles are affected by this operation
+            switch ($selectionType) {
+                case 'COLUMN':
+                    $oldXfIndexes = [];
+                    for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
+                        $oldXfIndexes[$this->getActiveSheet()->getColumnDimensionByColumn($col)->getXfIndex()] = true;
+                    }
+
+                    break;
+                case 'ROW':
+                    $oldXfIndexes = [];
+                    for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
+                        if ($this->getActiveSheet()->getRowDimension($row)->getXfIndex() == null) {
+                            $oldXfIndexes[0] = true; // row without explicit style should be formatted based on default style
+                        } else {
+                            $oldXfIndexes[$this->getActiveSheet()->getRowDimension($row)->getXfIndex()] = true;
+                        }
+                    }
+
+                    break;
+                case 'CELL':
+                    $oldXfIndexes = [];
+                    for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
+                        for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
+                            $oldXfIndexes[$this->getActiveSheet()->getCellByColumnAndRow($col, $row)->getXfIndex()] = true;
+                        }
+                    }
+
+                    break;
+            }
+
+            // clone each of the affected styles, apply the style array, and add the new styles to the workbook
+            $workbook = $this->getActiveSheet()->getParent();
+            foreach ($oldXfIndexes as $oldXfIndex => $dummy) {
+                $style = $workbook->getCellXfByIndex($oldXfIndex);
+                $newStyle = clone $style;
+                $newStyle->applyFromArray($pStyles);
+
+                if ($existingStyle = $workbook->getCellXfByHashCode($newStyle->getHashCode())) {
+                    // there is already such cell Xf in our collection
+                    $newXfIndexes[$oldXfIndex] = $existingStyle->getIndex();
+                } else {
+                    // we don't have such a cell Xf, need to add
+                    $workbook->addCellXf($newStyle);
+                    $newXfIndexes[$oldXfIndex] = $newStyle->getIndex();
+                }
+            }
+
+            // Loop through columns, rows, or cells again and update the XF index
+            switch ($selectionType) {
+                case 'COLUMN':
+                    for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
+                        $columnDimension = $this->getActiveSheet()->getColumnDimensionByColumn($col);
+                        $oldXfIndex = $columnDimension->getXfIndex();
+                        $columnDimension->setXfIndex($newXfIndexes[$oldXfIndex]);
+                    }
+
+                    break;
+                case 'ROW':
+                    for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
+                        $rowDimension = $this->getActiveSheet()->getRowDimension($row);
+                        $oldXfIndex = $rowDimension->getXfIndex() === null ?
+                            0 : $rowDimension->getXfIndex(); // row without explicit style should be formatted based on default style
+                        $rowDimension->setXfIndex($newXfIndexes[$oldXfIndex]);
+                    }
+
+                    break;
+                case 'CELL':
+                    for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
+                        for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
+                            $cell = $this->getActiveSheet()->getCellByColumnAndRow($col, $row);
+                            $oldXfIndex = $cell->getXfIndex();
+                            $cell->setXfIndex($newXfIndexes[$oldXfIndex]);
+                        }
+                    }
+
+                    break;
+            }
+        } else {
+            // not a supervisor, just apply the style array directly on style object
+            if (isset($pStyles['fill'])) {
+                $this->getFill()->applyFromArray($pStyles['fill']);
+            }
+            if (isset($pStyles['font'])) {
+                $this->getFont()->applyFromArray($pStyles['font']);
+            }
+            if (isset($pStyles['borders'])) {
+                $this->getBorders()->applyFromArray($pStyles['borders']);
+            }
+            if (isset($pStyles['alignment'])) {
+                $this->getAlignment()->applyFromArray($pStyles['alignment']);
+            }
+            if (isset($pStyles['numberFormat'])) {
+                $this->getNumberFormat()->applyFromArray($pStyles['numberFormat']);
+            }
+            if (isset($pStyles['protection'])) {
+                $this->getProtection()->applyFromArray($pStyles['protection']);
+            }
+            if (isset($pStyles['quotePrefix'])) {
+                $this->quotePrefix = $pStyles['quotePrefix'];
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Fill.
+     *
+     * @return Fill
+     */
+    public function getFill()
+    {
+        return $this->fill;
+    }
+
+    /**
+     * Get Font.
+     *
+     * @return Font
+     */
+    public function getFont()
+    {
+        return $this->font;
+    }
+
+    /**
+     * Set font.
+     *
+     * @return $this
+     */
+    public function setFont(Font $font)
+    {
+        $this->font = $font;
+
+        return $this;
+    }
+
+    /**
+     * Get Borders.
+     *
+     * @return Borders
+     */
+    public function getBorders()
+    {
+        return $this->borders;
+    }
+
+    /**
+     * Get Alignment.
+     *
+     * @return Alignment
+     */
+    public function getAlignment()
+    {
+        return $this->alignment;
+    }
+
+    /**
+     * Get Number Format.
+     *
+     * @return NumberFormat
+     */
+    public function getNumberFormat()
+    {
+        return $this->numberFormat;
+    }
+
+    /**
+     * Get Conditional Styles. Only used on supervisor.
+     *
+     * @return Conditional[]
+     */
+    public function getConditionalStyles()
+    {
+        return $this->getActiveSheet()->getConditionalStyles($this->getActiveCell());
+    }
+
+    /**
+     * Set Conditional Styles. Only used on supervisor.
+     *
+     * @param Conditional[] $pValue Array of conditional styles
+     *
+     * @return $this
+     */
+    public function setConditionalStyles(array $pValue)
+    {
+        $this->getActiveSheet()->setConditionalStyles($this->getSelectedCells(), $pValue);
+
+        return $this;
+    }
+
+    /**
+     * Get Protection.
+     *
+     * @return Protection
+     */
+    public function getProtection()
+    {
+        return $this->protection;
+    }
+
+    /**
+     * Get quote prefix.
+     *
+     * @return bool
+     */
+    public function getQuotePrefix()
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getQuotePrefix();
+        }
+
+        return $this->quotePrefix;
+    }
+
+    /**
+     * Set quote prefix.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setQuotePrefix($pValue)
+    {
+        if ($pValue == '') {
+            $pValue = false;
+        }
+        if ($this->isSupervisor) {
+            $styleArray = ['quotePrefix' => $pValue];
+            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+        } else {
+            $this->quotePrefix = (bool) $pValue;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get hash code.
+     *
+     * @return string Hash code
+     */
+    public function getHashCode()
+    {
+        $hashConditionals = '';
+        foreach ($this->conditionalStyles as $conditional) {
+            $hashConditionals .= $conditional->getHashCode();
+        }
+
+        return md5(
+            $this->fill->getHashCode() .
+            $this->font->getHashCode() .
+            $this->borders->getHashCode() .
+            $this->alignment->getHashCode() .
+            $this->numberFormat->getHashCode() .
+            $hashConditionals .
+            $this->protection->getHashCode() .
+            ($this->quotePrefix ? 't' : 'f') .
+            __CLASS__
+        );
+    }
+
+    /**
+     * Get own index in style collection.
+     *
+     * @return int
+     */
+    public function getIndex()
+    {
+        return $this->index;
+    }
+
+    /**
+     * Set own index in style collection.
+     *
+     * @param int $pValue
+     */
+    public function setIndex($pValue): void
+    {
+        $this->index = $pValue;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Supervisor.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Supervisor.php
new file mode 100755
index 0000000..1a70097
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Supervisor.php
@@ -0,0 +1,117 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Style;
+
+use PhpOffice\PhpSpreadsheet\IComparable;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+
+abstract class Supervisor implements IComparable
+{
+    /**
+     * Supervisor?
+     *
+     * @var bool
+     */
+    protected $isSupervisor;
+
+    /**
+     * Parent. Only used for supervisor.
+     *
+     * @var Spreadsheet|Style
+     */
+    protected $parent;
+
+    /**
+     * Parent property name.
+     *
+     * @var null|string
+     */
+    protected $parentPropertyName;
+
+    /**
+     * Create a new Supervisor.
+     *
+     * @param bool $isSupervisor Flag indicating if this is a supervisor or not
+     *                                    Leave this value at default unless you understand exactly what
+     *                                        its ramifications are
+     */
+    public function __construct($isSupervisor = false)
+    {
+        // Supervisor?
+        $this->isSupervisor = $isSupervisor;
+    }
+
+    /**
+     * Bind parent. Only used for supervisor.
+     *
+     * @param Spreadsheet|Style $parent
+     * @param null|string $parentPropertyName
+     *
+     * @return $this
+     */
+    public function bindParent($parent, $parentPropertyName = null)
+    {
+        $this->parent = $parent;
+        $this->parentPropertyName = $parentPropertyName;
+
+        return $this;
+    }
+
+    /**
+     * Is this a supervisor or a cell style component?
+     *
+     * @return bool
+     */
+    public function getIsSupervisor()
+    {
+        return $this->isSupervisor;
+    }
+
+    /**
+     * Get the currently active sheet. Only used for supervisor.
+     *
+     * @return Worksheet
+     */
+    public function getActiveSheet()
+    {
+        return $this->parent->getActiveSheet();
+    }
+
+    /**
+     * Get the currently active cell coordinate in currently active sheet.
+     * Only used for supervisor.
+     *
+     * @return string E.g. 'A1'
+     */
+    public function getSelectedCells()
+    {
+        return $this->getActiveSheet()->getSelectedCells();
+    }
+
+    /**
+     * Get the currently active cell coordinate in currently active sheet.
+     * Only used for supervisor.
+     *
+     * @return string E.g. 'A1'
+     */
+    public function getActiveCell()
+    {
+        return $this->getActiveSheet()->getActiveCell();
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $vars = get_object_vars($this);
+        foreach ($vars as $key => $value) {
+            if ((is_object($value)) && ($key != 'parent')) {
+                $this->$key = clone $value;
+            } else {
+                $this->$key = $value;
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter.php
new file mode 100755
index 0000000..c085b59
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter.php
@@ -0,0 +1,857 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
+use PhpOffice\PhpSpreadsheet\Calculation\DateTime;
+use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+use PhpOffice\PhpSpreadsheet\Shared\Date;
+
+class AutoFilter
+{
+    /**
+     * Autofilter Worksheet.
+     *
+     * @var Worksheet
+     */
+    private $workSheet;
+
+    /**
+     * Autofilter Range.
+     *
+     * @var string
+     */
+    private $range = '';
+
+    /**
+     * Autofilter Column Ruleset.
+     *
+     * @var AutoFilter\Column[]
+     */
+    private $columns = [];
+
+    /**
+     * Create a new AutoFilter.
+     *
+     * @param string $pRange Cell range (i.e. A1:E10)
+     * @param Worksheet $pSheet
+     */
+    public function __construct($pRange = '', ?Worksheet $pSheet = null)
+    {
+        $this->range = $pRange;
+        $this->workSheet = $pSheet;
+    }
+
+    /**
+     * Get AutoFilter Parent Worksheet.
+     *
+     * @return Worksheet
+     */
+    public function getParent()
+    {
+        return $this->workSheet;
+    }
+
+    /**
+     * Set AutoFilter Parent Worksheet.
+     *
+     * @param Worksheet $pSheet
+     *
+     * @return $this
+     */
+    public function setParent(?Worksheet $pSheet = null)
+    {
+        $this->workSheet = $pSheet;
+
+        return $this;
+    }
+
+    /**
+     * Get AutoFilter Range.
+     *
+     * @return string
+     */
+    public function getRange()
+    {
+        return $this->range;
+    }
+
+    /**
+     * Set AutoFilter Range.
+     *
+     * @param string $pRange Cell range (i.e. A1:E10)
+     *
+     * @return $this
+     */
+    public function setRange($pRange)
+    {
+        // extract coordinate
+        [$worksheet, $pRange] = Worksheet::extractSheetTitle($pRange, true);
+
+        if (strpos($pRange, ':') !== false) {
+            $this->range = $pRange;
+        } elseif (empty($pRange)) {
+            $this->range = '';
+        } else {
+            throw new PhpSpreadsheetException('Autofilter must be set on a range of cells.');
+        }
+
+        if (empty($pRange)) {
+            //    Discard all column rules
+            $this->columns = [];
+        } else {
+            //    Discard any column rules that are no longer valid within this range
+            [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range);
+            foreach ($this->columns as $key => $value) {
+                $colIndex = Coordinate::columnIndexFromString($key);
+                if (($rangeStart[0] > $colIndex) || ($rangeEnd[0] < $colIndex)) {
+                    unset($this->columns[$key]);
+                }
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get all AutoFilter Columns.
+     *
+     * @return AutoFilter\Column[]
+     */
+    public function getColumns()
+    {
+        return $this->columns;
+    }
+
+    /**
+     * Validate that the specified column is in the AutoFilter range.
+     *
+     * @param string $column Column name (e.g. A)
+     *
+     * @return int The column offset within the autofilter range
+     */
+    public function testColumnInRange($column)
+    {
+        if (empty($this->range)) {
+            throw new PhpSpreadsheetException('No autofilter range is defined.');
+        }
+
+        $columnIndex = Coordinate::columnIndexFromString($column);
+        [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range);
+        if (($rangeStart[0] > $columnIndex) || ($rangeEnd[0] < $columnIndex)) {
+            throw new PhpSpreadsheetException('Column is outside of current autofilter range.');
+        }
+
+        return $columnIndex - $rangeStart[0];
+    }
+
+    /**
+     * Get a specified AutoFilter Column Offset within the defined AutoFilter range.
+     *
+     * @param string $pColumn Column name (e.g. A)
+     *
+     * @return int The offset of the specified column within the autofilter range
+     */
+    public function getColumnOffset($pColumn)
+    {
+        return $this->testColumnInRange($pColumn);
+    }
+
+    /**
+     * Get a specified AutoFilter Column.
+     *
+     * @param string $pColumn Column name (e.g. A)
+     *
+     * @return AutoFilter\Column
+     */
+    public function getColumn($pColumn)
+    {
+        $this->testColumnInRange($pColumn);
+
+        if (!isset($this->columns[$pColumn])) {
+            $this->columns[$pColumn] = new AutoFilter\Column($pColumn, $this);
+        }
+
+        return $this->columns[$pColumn];
+    }
+
+    /**
+     * Get a specified AutoFilter Column by it's offset.
+     *
+     * @param int $pColumnOffset Column offset within range (starting from 0)
+     *
+     * @return AutoFilter\Column
+     */
+    public function getColumnByOffset($pColumnOffset)
+    {
+        [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range);
+        $pColumn = Coordinate::stringFromColumnIndex($rangeStart[0] + $pColumnOffset);
+
+        return $this->getColumn($pColumn);
+    }
+
+    /**
+     * Set AutoFilter.
+     *
+     * @param AutoFilter\Column|string $pColumn
+     *            A simple string containing a Column ID like 'A' is permitted
+     *
+     * @return $this
+     */
+    public function setColumn($pColumn)
+    {
+        if ((is_string($pColumn)) && (!empty($pColumn))) {
+            $column = $pColumn;
+        } elseif (is_object($pColumn) && ($pColumn instanceof AutoFilter\Column)) {
+            $column = $pColumn->getColumnIndex();
+        } else {
+            throw new PhpSpreadsheetException('Column is not within the autofilter range.');
+        }
+        $this->testColumnInRange($column);
+
+        if (is_string($pColumn)) {
+            $this->columns[$pColumn] = new AutoFilter\Column($pColumn, $this);
+        } elseif (is_object($pColumn) && ($pColumn instanceof AutoFilter\Column)) {
+            $pColumn->setParent($this);
+            $this->columns[$column] = $pColumn;
+        }
+        ksort($this->columns);
+
+        return $this;
+    }
+
+    /**
+     * Clear a specified AutoFilter Column.
+     *
+     * @param string $pColumn Column name (e.g. A)
+     *
+     * @return $this
+     */
+    public function clearColumn($pColumn)
+    {
+        $this->testColumnInRange($pColumn);
+
+        if (isset($this->columns[$pColumn])) {
+            unset($this->columns[$pColumn]);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Shift an AutoFilter Column Rule to a different column.
+     *
+     * Note: This method bypasses validation of the destination column to ensure it is within this AutoFilter range.
+     *        Nor does it verify whether any column rule already exists at $toColumn, but will simply override any existing value.
+     *        Use with caution.
+     *
+     * @param string $fromColumn Column name (e.g. A)
+     * @param string $toColumn Column name (e.g. B)
+     *
+     * @return $this
+     */
+    public function shiftColumn($fromColumn, $toColumn)
+    {
+        $fromColumn = strtoupper($fromColumn);
+        $toColumn = strtoupper($toColumn);
+
+        if (($fromColumn !== null) && (isset($this->columns[$fromColumn])) && ($toColumn !== null)) {
+            $this->columns[$fromColumn]->setParent();
+            $this->columns[$fromColumn]->setColumnIndex($toColumn);
+            $this->columns[$toColumn] = $this->columns[$fromColumn];
+            $this->columns[$toColumn]->setParent($this);
+            unset($this->columns[$fromColumn]);
+
+            ksort($this->columns);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Test if cell value is in the defined set of values.
+     *
+     * @param mixed $cellValue
+     * @param mixed[] $dataSet
+     *
+     * @return bool
+     */
+    private static function filterTestInSimpleDataSet($cellValue, $dataSet)
+    {
+        $dataSetValues = $dataSet['filterValues'];
+        $blanks = $dataSet['blanks'];
+        if (($cellValue == '') || ($cellValue === null)) {
+            return $blanks;
+        }
+
+        return in_array($cellValue, $dataSetValues);
+    }
+
+    /**
+     * Test if cell value is in the defined set of Excel date values.
+     *
+     * @param mixed $cellValue
+     * @param mixed[] $dataSet
+     *
+     * @return bool
+     */
+    private static function filterTestInDateGroupSet($cellValue, $dataSet)
+    {
+        $dateSet = $dataSet['filterValues'];
+        $blanks = $dataSet['blanks'];
+        if (($cellValue == '') || ($cellValue === null)) {
+            return $blanks;
+        }
+
+        if (is_numeric($cellValue)) {
+            $dateValue = Date::excelToTimestamp($cellValue);
+            if ($cellValue < 1) {
+                //    Just the time part
+                $dtVal = date('His', $dateValue);
+                $dateSet = $dateSet['time'];
+            } elseif ($cellValue == floor($cellValue)) {
+                //    Just the date part
+                $dtVal = date('Ymd', $dateValue);
+                $dateSet = $dateSet['date'];
+            } else {
+                //    date and time parts
+                $dtVal = date('YmdHis', $dateValue);
+                $dateSet = $dateSet['dateTime'];
+            }
+            foreach ($dateSet as $dateValue) {
+                //    Use of substr to extract value at the appropriate group level
+                if (substr($dtVal, 0, strlen($dateValue)) == $dateValue) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Test if cell value is within a set of values defined by a ruleset.
+     *
+     * @param mixed $cellValue
+     * @param mixed[] $ruleSet
+     *
+     * @return bool
+     */
+    private static function filterTestInCustomDataSet($cellValue, $ruleSet)
+    {
+        $dataSet = $ruleSet['filterRules'];
+        $join = $ruleSet['join'];
+        $customRuleForBlanks = $ruleSet['customRuleForBlanks'] ?? false;
+
+        if (!$customRuleForBlanks) {
+            //    Blank cells are always ignored, so return a FALSE
+            if (($cellValue == '') || ($cellValue === null)) {
+                return false;
+            }
+        }
+        $returnVal = ($join == AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_AND);
+        foreach ($dataSet as $rule) {
+            $retVal = false;
+
+            if (is_numeric($rule['value'])) {
+                //    Numeric values are tested using the appropriate operator
+                switch ($rule['operator']) {
+                    case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_EQUAL:
+                        $retVal = ($cellValue == $rule['value']);
+
+                        break;
+                    case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL:
+                        $retVal = ($cellValue != $rule['value']);
+
+                        break;
+                    case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN:
+                        $retVal = ($cellValue > $rule['value']);
+
+                        break;
+                    case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL:
+                        $retVal = ($cellValue >= $rule['value']);
+
+                        break;
+                    case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN:
+                        $retVal = ($cellValue < $rule['value']);
+
+                        break;
+                    case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL:
+                        $retVal = ($cellValue <= $rule['value']);
+
+                        break;
+                }
+            } elseif ($rule['value'] == '') {
+                switch ($rule['operator']) {
+                    case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_EQUAL:
+                        $retVal = (($cellValue == '') || ($cellValue === null));
+
+                        break;
+                    case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL:
+                        $retVal = (($cellValue != '') && ($cellValue !== null));
+
+                        break;
+                    default:
+                        $retVal = true;
+
+                        break;
+                }
+            } else {
+                //    String values are always tested for equality, factoring in for wildcards (hence a regexp test)
+                $retVal = preg_match('/^' . $rule['value'] . '$/i', $cellValue);
+            }
+            //    If there are multiple conditions, then we need to test both using the appropriate join operator
+            switch ($join) {
+                case AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_OR:
+                    $returnVal = $returnVal || $retVal;
+                    //    Break as soon as we have a TRUE match for OR joins,
+                    //        to avoid unnecessary additional code execution
+                    if ($returnVal) {
+                        return $returnVal;
+                    }
+
+                    break;
+                case AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_AND:
+                    $returnVal = $returnVal && $retVal;
+
+                    break;
+            }
+        }
+
+        return $returnVal;
+    }
+
+    /**
+     * Test if cell date value is matches a set of values defined by a set of months.
+     *
+     * @param mixed $cellValue
+     * @param mixed[] $monthSet
+     *
+     * @return bool
+     */
+    private static function filterTestInPeriodDateSet($cellValue, $monthSet)
+    {
+        //    Blank cells are always ignored, so return a FALSE
+        if (($cellValue == '') || ($cellValue === null)) {
+            return false;
+        }
+
+        if (is_numeric($cellValue)) {
+            $dateValue = date('m', Date::excelToTimestamp($cellValue));
+            if (in_array($dateValue, $monthSet)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Search/Replace arrays to convert Excel wildcard syntax to a regexp syntax for preg_matching.
+     *
+     * @var array
+     */
+    private static $fromReplace = ['\*', '\?', '~~', '~.*', '~.?'];
+
+    private static $toReplace = ['.*', '.', '~', '\*', '\?'];
+
+    /**
+     * Convert a dynamic rule daterange to a custom filter range expression for ease of calculation.
+     *
+     * @param string $dynamicRuleType
+     * @param AutoFilter\Column $filterColumn
+     *
+     * @return mixed[]
+     */
+    private function dynamicFilterDateRange($dynamicRuleType, &$filterColumn)
+    {
+        $rDateType = Functions::getReturnDateType();
+        Functions::setReturnDateType(Functions::RETURNDATE_PHP_NUMERIC);
+        $val = $maxVal = null;
+
+        $ruleValues = [];
+        $baseDate = DateTime::DATENOW();
+        //    Calculate start/end dates for the required date range based on current date
+        switch ($dynamicRuleType) {
+            case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTWEEK:
+                $baseDate = strtotime('-7 days', $baseDate);
+
+                break;
+            case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTWEEK:
+                $baseDate = strtotime('-7 days', $baseDate);
+
+                break;
+            case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTMONTH:
+                $baseDate = strtotime('-1 month', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
+
+                break;
+            case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTMONTH:
+                $baseDate = strtotime('+1 month', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
+
+                break;
+            case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTQUARTER:
+                $baseDate = strtotime('-3 month', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
+
+                break;
+            case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTQUARTER:
+                $baseDate = strtotime('+3 month', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
+
+                break;
+            case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTYEAR:
+                $baseDate = strtotime('-1 year', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
+
+                break;
+            case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTYEAR:
+                $baseDate = strtotime('+1 year', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
+
+                break;
+        }
+
+        switch ($dynamicRuleType) {
+            case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_TODAY:
+            case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_YESTERDAY:
+            case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_TOMORROW:
+                $maxVal = (int) Date::PHPtoExcel(strtotime('+1 day', $baseDate));
+                $val = (int) Date::PHPToExcel($baseDate);
+
+                break;
+            case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_YEARTODATE:
+                $maxVal = (int) Date::PHPtoExcel(strtotime('+1 day', $baseDate));
+                $val = (int) Date::PHPToExcel(gmmktime(0, 0, 0, 1, 1, date('Y', $baseDate)));
+
+                break;
+            case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISYEAR:
+            case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTYEAR:
+            case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTYEAR:
+                $maxVal = (int) Date::PHPToExcel(gmmktime(0, 0, 0, 31, 12, date('Y', $baseDate)));
+                ++$maxVal;
+                $val = (int) Date::PHPToExcel(gmmktime(0, 0, 0, 1, 1, date('Y', $baseDate)));
+
+                break;
+            case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISQUARTER:
+            case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTQUARTER:
+            case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTQUARTER:
+                $thisMonth = date('m', $baseDate);
+                $thisQuarter = floor(--$thisMonth / 3);
+                $maxVal = (int) Date::PHPtoExcel(gmmktime(0, 0, 0, date('t', $baseDate), (1 + $thisQuarter) * 3, date('Y', $baseDate)));
+                ++$maxVal;
+                $val = (int) Date::PHPToExcel(gmmktime(0, 0, 0, 1, 1 + $thisQuarter * 3, date('Y', $baseDate)));
+
+                break;
+            case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISMONTH:
+            case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTMONTH:
+            case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTMONTH:
+                $maxVal = (int) Date::PHPtoExcel(gmmktime(0, 0, 0, date('t', $baseDate), date('m', $baseDate), date('Y', $baseDate)));
+                ++$maxVal;
+                $val = (int) Date::PHPToExcel(gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
+
+                break;
+            case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISWEEK:
+            case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTWEEK:
+            case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTWEEK:
+                $dayOfWeek = date('w', $baseDate);
+                $val = (int) Date::PHPToExcel($baseDate) - $dayOfWeek;
+                $maxVal = $val + 7;
+
+                break;
+        }
+
+        switch ($dynamicRuleType) {
+            //    Adjust Today dates for Yesterday and Tomorrow
+            case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_YESTERDAY:
+                --$maxVal;
+                --$val;
+
+                break;
+            case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_TOMORROW:
+                ++$maxVal;
+                ++$val;
+
+                break;
+        }
+
+        //    Set the filter column rule attributes ready for writing
+        $filterColumn->setAttributes(['val' => $val, 'maxVal' => $maxVal]);
+
+        //    Set the rules for identifying rows for hide/show
+        $ruleValues[] = ['operator' => AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL, 'value' => $val];
+        $ruleValues[] = ['operator' => AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN, 'value' => $maxVal];
+        Functions::setReturnDateType($rDateType);
+
+        return ['method' => 'filterTestInCustomDataSet', 'arguments' => ['filterRules' => $ruleValues, 'join' => AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_AND]];
+    }
+
+    private function calculateTopTenValue($columnID, $startRow, $endRow, $ruleType, $ruleValue)
+    {
+        $range = $columnID . $startRow . ':' . $columnID . $endRow;
+        $dataValues = Functions::flattenArray($this->workSheet->rangeToArray($range, null, true, false));
+
+        $dataValues = array_filter($dataValues);
+        if ($ruleType == AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP) {
+            rsort($dataValues);
+        } else {
+            sort($dataValues);
+        }
+
+        return array_pop(array_slice($dataValues, 0, $ruleValue));
+    }
+
+    /**
+     * Apply the AutoFilter rules to the AutoFilter Range.
+     *
+     * @return $this
+     */
+    public function showHideRows()
+    {
+        [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range);
+
+        //    The heading row should always be visible
+        $this->workSheet->getRowDimension($rangeStart[1])->setVisible(true);
+
+        $columnFilterTests = [];
+        foreach ($this->columns as $columnID => $filterColumn) {
+            $rules = $filterColumn->getRules();
+            switch ($filterColumn->getFilterType()) {
+                case AutoFilter\Column::AUTOFILTER_FILTERTYPE_FILTER:
+                    $ruleType = null;
+                    $ruleValues = [];
+                    //    Build a list of the filter value selections
+                    foreach ($rules as $rule) {
+                        $ruleType = $rule->getRuleType();
+                        $ruleValues[] = $rule->getValue();
+                    }
+                    //    Test if we want to include blanks in our filter criteria
+                    $blanks = false;
+                    $ruleDataSet = array_filter($ruleValues);
+                    if (count($ruleValues) != count($ruleDataSet)) {
+                        $blanks = true;
+                    }
+                    if ($ruleType == AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_FILTER) {
+                        //    Filter on absolute values
+                        $columnFilterTests[$columnID] = [
+                            'method' => 'filterTestInSimpleDataSet',
+                            'arguments' => ['filterValues' => $ruleDataSet, 'blanks' => $blanks],
+                        ];
+                    } else {
+                        //    Filter on date group values
+                        $arguments = [
+                            'date' => [],
+                            'time' => [],
+                            'dateTime' => [],
+                        ];
+                        foreach ($ruleDataSet as $ruleValue) {
+                            $date = $time = '';
+                            if ((isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR])) &&
+                                ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR] !== '')) {
+                                $date .= sprintf('%04d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR]);
+                            }
+                            if ((isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH])) &&
+                                ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH] != '')) {
+                                $date .= sprintf('%02d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH]);
+                            }
+                            if ((isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY])) &&
+                                ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY] !== '')) {
+                                $date .= sprintf('%02d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY]);
+                            }
+                            if ((isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR])) &&
+                                ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR] !== '')) {
+                                $time .= sprintf('%02d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR]);
+                            }
+                            if ((isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE])) &&
+                                ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE] !== '')) {
+                                $time .= sprintf('%02d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE]);
+                            }
+                            if ((isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND])) &&
+                                ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND] !== '')) {
+                                $time .= sprintf('%02d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND]);
+                            }
+                            $dateTime = $date . $time;
+                            $arguments['date'][] = $date;
+                            $arguments['time'][] = $time;
+                            $arguments['dateTime'][] = $dateTime;
+                        }
+                        //    Remove empty elements
+                        $arguments['date'] = array_filter($arguments['date']);
+                        $arguments['time'] = array_filter($arguments['time']);
+                        $arguments['dateTime'] = array_filter($arguments['dateTime']);
+                        $columnFilterTests[$columnID] = [
+                            'method' => 'filterTestInDateGroupSet',
+                            'arguments' => ['filterValues' => $arguments, 'blanks' => $blanks],
+                        ];
+                    }
+
+                    break;
+                case AutoFilter\Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER:
+                    $customRuleForBlanks = false;
+                    $ruleValues = [];
+                    //    Build a list of the filter value selections
+                    foreach ($rules as $rule) {
+                        $ruleValue = $rule->getValue();
+                        if (!is_numeric($ruleValue)) {
+                            //    Convert to a regexp allowing for regexp reserved characters, wildcards and escaped wildcards
+                            $ruleValue = preg_quote($ruleValue);
+                            $ruleValue = str_replace(self::$fromReplace, self::$toReplace, $ruleValue);
+                            if (trim($ruleValue) == '') {
+                                $customRuleForBlanks = true;
+                                $ruleValue = trim($ruleValue);
+                            }
+                        }
+                        $ruleValues[] = ['operator' => $rule->getOperator(), 'value' => $ruleValue];
+                    }
+                    $join = $filterColumn->getJoin();
+                    $columnFilterTests[$columnID] = [
+                        'method' => 'filterTestInCustomDataSet',
+                        'arguments' => ['filterRules' => $ruleValues, 'join' => $join, 'customRuleForBlanks' => $customRuleForBlanks],
+                    ];
+
+                    break;
+                case AutoFilter\Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER:
+                    $ruleValues = [];
+                    foreach ($rules as $rule) {
+                        //    We should only ever have one Dynamic Filter Rule anyway
+                        $dynamicRuleType = $rule->getGrouping();
+                        if (($dynamicRuleType == AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE) ||
+                            ($dynamicRuleType == AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_BELOWAVERAGE)) {
+                            //    Number (Average) based
+                            //    Calculate the average
+                            $averageFormula = '=AVERAGE(' . $columnID . ($rangeStart[1] + 1) . ':' . $columnID . $rangeEnd[1] . ')';
+                            $average = Calculation::getInstance()->calculateFormula($averageFormula, null, $this->workSheet->getCell('A1'));
+                            //    Set above/below rule based on greaterThan or LessTan
+                            $operator = ($dynamicRuleType === AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE)
+                                ? AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN
+                                : AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN;
+                            $ruleValues[] = [
+                                'operator' => $operator,
+                                'value' => $average,
+                            ];
+                            $columnFilterTests[$columnID] = [
+                                'method' => 'filterTestInCustomDataSet',
+                                'arguments' => ['filterRules' => $ruleValues, 'join' => AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_OR],
+                            ];
+                        } else {
+                            //    Date based
+                            if ($dynamicRuleType[0] == 'M' || $dynamicRuleType[0] == 'Q') {
+                                $periodType = '';
+                                $period = 0;
+                                //    Month or Quarter
+                                sscanf($dynamicRuleType, '%[A-Z]%d', $periodType, $period);
+                                if ($periodType == 'M') {
+                                    $ruleValues = [$period];
+                                } else {
+                                    --$period;
+                                    $periodEnd = (1 + $period) * 3;
+                                    $periodStart = 1 + $period * 3;
+                                    $ruleValues = range($periodStart, $periodEnd);
+                                }
+                                $columnFilterTests[$columnID] = [
+                                    'method' => 'filterTestInPeriodDateSet',
+                                    'arguments' => $ruleValues,
+                                ];
+                                $filterColumn->setAttributes([]);
+                            } else {
+                                //    Date Range
+                                $columnFilterTests[$columnID] = $this->dynamicFilterDateRange($dynamicRuleType, $filterColumn);
+
+                                break;
+                            }
+                        }
+                    }
+
+                    break;
+                case AutoFilter\Column::AUTOFILTER_FILTERTYPE_TOPTENFILTER:
+                    $ruleValues = [];
+                    $dataRowCount = $rangeEnd[1] - $rangeStart[1];
+                    foreach ($rules as $rule) {
+                        //    We should only ever have one Dynamic Filter Rule anyway
+                        $toptenRuleType = $rule->getGrouping();
+                        $ruleValue = $rule->getValue();
+                        $ruleOperator = $rule->getOperator();
+                    }
+                    if ($ruleOperator === AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT) {
+                        $ruleValue = floor($ruleValue * ($dataRowCount / 100));
+                    }
+                    if ($ruleValue < 1) {
+                        $ruleValue = 1;
+                    }
+                    if ($ruleValue > 500) {
+                        $ruleValue = 500;
+                    }
+
+                    $maxVal = $this->calculateTopTenValue($columnID, $rangeStart[1] + 1, $rangeEnd[1], $toptenRuleType, $ruleValue);
+
+                    $operator = ($toptenRuleType == AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP)
+                        ? AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL
+                        : AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL;
+                    $ruleValues[] = ['operator' => $operator, 'value' => $maxVal];
+                    $columnFilterTests[$columnID] = [
+                        'method' => 'filterTestInCustomDataSet',
+                        'arguments' => ['filterRules' => $ruleValues, 'join' => AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_OR],
+                    ];
+                    $filterColumn->setAttributes(['maxVal' => $maxVal]);
+
+                    break;
+            }
+        }
+
+        //    Execute the column tests for each row in the autoFilter range to determine show/hide,
+        for ($row = $rangeStart[1] + 1; $row <= $rangeEnd[1]; ++$row) {
+            $result = true;
+            foreach ($columnFilterTests as $columnID => $columnFilterTest) {
+                $cellValue = $this->workSheet->getCell($columnID . $row)->getCalculatedValue();
+                //    Execute the filter test
+                $result = $result &&
+                    call_user_func_array(
+                        [self::class, $columnFilterTest['method']],
+                        [$cellValue, $columnFilterTest['arguments']]
+                    );
+                //    If filter test has resulted in FALSE, exit the loop straightaway rather than running any more tests
+                if (!$result) {
+                    break;
+                }
+            }
+            //    Set show/hide for the row based on the result of the autoFilter result
+            $this->workSheet->getRowDimension($row)->setVisible($result);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $vars = get_object_vars($this);
+        foreach ($vars as $key => $value) {
+            if (is_object($value)) {
+                if ($key === 'workSheet') {
+                    //    Detach from worksheet
+                    $this->{$key} = null;
+                } else {
+                    $this->{$key} = clone $value;
+                }
+            } elseif ((is_array($value)) && ($key == 'columns')) {
+                //    The columns array of \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\AutoFilter objects
+                $this->{$key} = [];
+                foreach ($value as $k => $v) {
+                    $this->{$key}[$k] = clone $v;
+                    // attach the new cloned Column to this new cloned Autofilter object
+                    $this->{$key}[$k]->setParent($this);
+                }
+            } else {
+                $this->{$key} = $value;
+            }
+        }
+    }
+
+    /**
+     * toString method replicates previous behavior by returning the range if object is
+     * referenced as a property of its parent.
+     */
+    public function __toString()
+    {
+        return (string) $this->range;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php
new file mode 100755
index 0000000..09584a7
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php
@@ -0,0 +1,380 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter;
+
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter;
+
+class Column
+{
+    const AUTOFILTER_FILTERTYPE_FILTER = 'filters';
+    const AUTOFILTER_FILTERTYPE_CUSTOMFILTER = 'customFilters';
+    //    Supports no more than 2 rules, with an And/Or join criteria
+    //        if more than 1 rule is defined
+    const AUTOFILTER_FILTERTYPE_DYNAMICFILTER = 'dynamicFilter';
+    //    Even though the filter rule is constant, the filtered data can vary
+    //        e.g. filtered by date = TODAY
+    const AUTOFILTER_FILTERTYPE_TOPTENFILTER = 'top10';
+
+    /**
+     * Types of autofilter rules.
+     *
+     * @var string[]
+     */
+    private static $filterTypes = [
+        //    Currently we're not handling
+        //        colorFilter
+        //        extLst
+        //        iconFilter
+        self::AUTOFILTER_FILTERTYPE_FILTER,
+        self::AUTOFILTER_FILTERTYPE_CUSTOMFILTER,
+        self::AUTOFILTER_FILTERTYPE_DYNAMICFILTER,
+        self::AUTOFILTER_FILTERTYPE_TOPTENFILTER,
+    ];
+
+    // Multiple Rule Connections
+    const AUTOFILTER_COLUMN_JOIN_AND = 'and';
+    const AUTOFILTER_COLUMN_JOIN_OR = 'or';
+
+    /**
+     * Join options for autofilter rules.
+     *
+     * @var string[]
+     */
+    private static $ruleJoins = [
+        self::AUTOFILTER_COLUMN_JOIN_AND,
+        self::AUTOFILTER_COLUMN_JOIN_OR,
+    ];
+
+    /**
+     * Autofilter.
+     *
+     * @var AutoFilter
+     */
+    private $parent;
+
+    /**
+     * Autofilter Column Index.
+     *
+     * @var string
+     */
+    private $columnIndex = '';
+
+    /**
+     * Autofilter Column Filter Type.
+     *
+     * @var string
+     */
+    private $filterType = self::AUTOFILTER_FILTERTYPE_FILTER;
+
+    /**
+     * Autofilter Multiple Rules And/Or.
+     *
+     * @var string
+     */
+    private $join = self::AUTOFILTER_COLUMN_JOIN_OR;
+
+    /**
+     * Autofilter Column Rules.
+     *
+     * @var array of Column\Rule
+     */
+    private $ruleset = [];
+
+    /**
+     * Autofilter Column Dynamic Attributes.
+     *
+     * @var array of mixed
+     */
+    private $attributes = [];
+
+    /**
+     * Create a new Column.
+     *
+     * @param string $pColumn Column (e.g. A)
+     * @param AutoFilter $pParent Autofilter for this column
+     */
+    public function __construct($pColumn, ?AutoFilter $pParent = null)
+    {
+        $this->columnIndex = $pColumn;
+        $this->parent = $pParent;
+    }
+
+    /**
+     * Get AutoFilter column index as string eg: 'A'.
+     *
+     * @return string
+     */
+    public function getColumnIndex()
+    {
+        return $this->columnIndex;
+    }
+
+    /**
+     * Set AutoFilter column index as string eg: 'A'.
+     *
+     * @param string $pColumn Column (e.g. A)
+     *
+     * @return $this
+     */
+    public function setColumnIndex($pColumn)
+    {
+        // Uppercase coordinate
+        $pColumn = strtoupper($pColumn);
+        if ($this->parent !== null) {
+            $this->parent->testColumnInRange($pColumn);
+        }
+
+        $this->columnIndex = $pColumn;
+
+        return $this;
+    }
+
+    /**
+     * Get this Column's AutoFilter Parent.
+     *
+     * @return AutoFilter
+     */
+    public function getParent()
+    {
+        return $this->parent;
+    }
+
+    /**
+     * Set this Column's AutoFilter Parent.
+     *
+     * @param AutoFilter $pParent
+     *
+     * @return $this
+     */
+    public function setParent(?AutoFilter $pParent = null)
+    {
+        $this->parent = $pParent;
+
+        return $this;
+    }
+
+    /**
+     * Get AutoFilter Type.
+     *
+     * @return string
+     */
+    public function getFilterType()
+    {
+        return $this->filterType;
+    }
+
+    /**
+     * Set AutoFilter Type.
+     *
+     * @param string $pFilterType
+     *
+     * @return $this
+     */
+    public function setFilterType($pFilterType)
+    {
+        if (!in_array($pFilterType, self::$filterTypes)) {
+            throw new PhpSpreadsheetException('Invalid filter type for column AutoFilter.');
+        }
+
+        $this->filterType = $pFilterType;
+
+        return $this;
+    }
+
+    /**
+     * Get AutoFilter Multiple Rules And/Or Join.
+     *
+     * @return string
+     */
+    public function getJoin()
+    {
+        return $this->join;
+    }
+
+    /**
+     * Set AutoFilter Multiple Rules And/Or.
+     *
+     * @param string $pJoin And/Or
+     *
+     * @return $this
+     */
+    public function setJoin($pJoin)
+    {
+        // Lowercase And/Or
+        $pJoin = strtolower($pJoin);
+        if (!in_array($pJoin, self::$ruleJoins)) {
+            throw new PhpSpreadsheetException('Invalid rule connection for column AutoFilter.');
+        }
+
+        $this->join = $pJoin;
+
+        return $this;
+    }
+
+    /**
+     * Set AutoFilter Attributes.
+     *
+     * @param string[] $attributes
+     *
+     * @return $this
+     */
+    public function setAttributes(array $attributes)
+    {
+        $this->attributes = $attributes;
+
+        return $this;
+    }
+
+    /**
+     * Set An AutoFilter Attribute.
+     *
+     * @param string $pName Attribute Name
+     * @param string $pValue Attribute Value
+     *
+     * @return $this
+     */
+    public function setAttribute($pName, $pValue)
+    {
+        $this->attributes[$pName] = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get AutoFilter Column Attributes.
+     *
+     * @return string[]
+     */
+    public function getAttributes()
+    {
+        return $this->attributes;
+    }
+
+    /**
+     * Get specific AutoFilter Column Attribute.
+     *
+     * @param string $pName Attribute Name
+     *
+     * @return string
+     */
+    public function getAttribute($pName)
+    {
+        if (isset($this->attributes[$pName])) {
+            return $this->attributes[$pName];
+        }
+
+        return null;
+    }
+
+    /**
+     * Get all AutoFilter Column Rules.
+     *
+     * @return Column\Rule[]
+     */
+    public function getRules()
+    {
+        return $this->ruleset;
+    }
+
+    /**
+     * Get a specified AutoFilter Column Rule.
+     *
+     * @param int $pIndex Rule index in the ruleset array
+     *
+     * @return Column\Rule
+     */
+    public function getRule($pIndex)
+    {
+        if (!isset($this->ruleset[$pIndex])) {
+            $this->ruleset[$pIndex] = new Column\Rule($this);
+        }
+
+        return $this->ruleset[$pIndex];
+    }
+
+    /**
+     * Create a new AutoFilter Column Rule in the ruleset.
+     *
+     * @return Column\Rule
+     */
+    public function createRule()
+    {
+        $this->ruleset[] = new Column\Rule($this);
+
+        return end($this->ruleset);
+    }
+
+    /**
+     * Add a new AutoFilter Column Rule to the ruleset.
+     *
+     * @return $this
+     */
+    public function addRule(Column\Rule $pRule)
+    {
+        $pRule->setParent($this);
+        $this->ruleset[] = $pRule;
+
+        return $this;
+    }
+
+    /**
+     * Delete a specified AutoFilter Column Rule
+     * If the number of rules is reduced to 1, then we reset And/Or logic to Or.
+     *
+     * @param int $pIndex Rule index in the ruleset array
+     *
+     * @return $this
+     */
+    public function deleteRule($pIndex)
+    {
+        if (isset($this->ruleset[$pIndex])) {
+            unset($this->ruleset[$pIndex]);
+            //    If we've just deleted down to a single rule, then reset And/Or joining to Or
+            if (count($this->ruleset) <= 1) {
+                $this->setJoin(self::AUTOFILTER_COLUMN_JOIN_OR);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Delete all AutoFilter Column Rules.
+     *
+     * @return $this
+     */
+    public function clearRules()
+    {
+        $this->ruleset = [];
+        $this->setJoin(self::AUTOFILTER_COLUMN_JOIN_OR);
+
+        return $this;
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $vars = get_object_vars($this);
+        foreach ($vars as $key => $value) {
+            if ($key === 'parent') {
+                // Detach from autofilter parent
+                $this->parent = null;
+            } elseif ($key === 'ruleset') {
+                // The columns array of \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\AutoFilter objects
+                $this->ruleset = [];
+                foreach ($value as $k => $v) {
+                    $cloned = clone $v;
+                    $cloned->setParent($this); // attach the new cloned Rule to this new cloned Autofilter Cloned object
+                    $this->ruleset[$k] = $cloned;
+                }
+            } elseif (is_object($value)) {
+                $this->$key = clone $value;
+            } else {
+                $this->$key = $value;
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php
new file mode 100755
index 0000000..c0a15a7
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php
@@ -0,0 +1,445 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
+
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
+
+class Rule
+{
+    const AUTOFILTER_RULETYPE_FILTER = 'filter';
+    const AUTOFILTER_RULETYPE_DATEGROUP = 'dateGroupItem';
+    const AUTOFILTER_RULETYPE_CUSTOMFILTER = 'customFilter';
+    const AUTOFILTER_RULETYPE_DYNAMICFILTER = 'dynamicFilter';
+    const AUTOFILTER_RULETYPE_TOPTENFILTER = 'top10Filter';
+
+    private static $ruleTypes = [
+        //    Currently we're not handling
+        //        colorFilter
+        //        extLst
+        //        iconFilter
+        self::AUTOFILTER_RULETYPE_FILTER,
+        self::AUTOFILTER_RULETYPE_DATEGROUP,
+        self::AUTOFILTER_RULETYPE_CUSTOMFILTER,
+        self::AUTOFILTER_RULETYPE_DYNAMICFILTER,
+        self::AUTOFILTER_RULETYPE_TOPTENFILTER,
+    ];
+
+    const AUTOFILTER_RULETYPE_DATEGROUP_YEAR = 'year';
+    const AUTOFILTER_RULETYPE_DATEGROUP_MONTH = 'month';
+    const AUTOFILTER_RULETYPE_DATEGROUP_DAY = 'day';
+    const AUTOFILTER_RULETYPE_DATEGROUP_HOUR = 'hour';
+    const AUTOFILTER_RULETYPE_DATEGROUP_MINUTE = 'minute';
+    const AUTOFILTER_RULETYPE_DATEGROUP_SECOND = 'second';
+
+    private static $dateTimeGroups = [
+        self::AUTOFILTER_RULETYPE_DATEGROUP_YEAR,
+        self::AUTOFILTER_RULETYPE_DATEGROUP_MONTH,
+        self::AUTOFILTER_RULETYPE_DATEGROUP_DAY,
+        self::AUTOFILTER_RULETYPE_DATEGROUP_HOUR,
+        self::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE,
+        self::AUTOFILTER_RULETYPE_DATEGROUP_SECOND,
+    ];
+
+    const AUTOFILTER_RULETYPE_DYNAMIC_YESTERDAY = 'yesterday';
+    const AUTOFILTER_RULETYPE_DYNAMIC_TODAY = 'today';
+    const AUTOFILTER_RULETYPE_DYNAMIC_TOMORROW = 'tomorrow';
+    const AUTOFILTER_RULETYPE_DYNAMIC_YEARTODATE = 'yearToDate';
+    const AUTOFILTER_RULETYPE_DYNAMIC_THISYEAR = 'thisYear';
+    const AUTOFILTER_RULETYPE_DYNAMIC_THISQUARTER = 'thisQuarter';
+    const AUTOFILTER_RULETYPE_DYNAMIC_THISMONTH = 'thisMonth';
+    const AUTOFILTER_RULETYPE_DYNAMIC_THISWEEK = 'thisWeek';
+    const AUTOFILTER_RULETYPE_DYNAMIC_LASTYEAR = 'lastYear';
+    const AUTOFILTER_RULETYPE_DYNAMIC_LASTQUARTER = 'lastQuarter';
+    const AUTOFILTER_RULETYPE_DYNAMIC_LASTMONTH = 'lastMonth';
+    const AUTOFILTER_RULETYPE_DYNAMIC_LASTWEEK = 'lastWeek';
+    const AUTOFILTER_RULETYPE_DYNAMIC_NEXTYEAR = 'nextYear';
+    const AUTOFILTER_RULETYPE_DYNAMIC_NEXTQUARTER = 'nextQuarter';
+    const AUTOFILTER_RULETYPE_DYNAMIC_NEXTMONTH = 'nextMonth';
+    const AUTOFILTER_RULETYPE_DYNAMIC_NEXTWEEK = 'nextWeek';
+    const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_1 = 'M1';
+    const AUTOFILTER_RULETYPE_DYNAMIC_JANUARY = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_1;
+    const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_2 = 'M2';
+    const AUTOFILTER_RULETYPE_DYNAMIC_FEBRUARY = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_2;
+    const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_3 = 'M3';
+    const AUTOFILTER_RULETYPE_DYNAMIC_MARCH = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_3;
+    const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_4 = 'M4';
+    const AUTOFILTER_RULETYPE_DYNAMIC_APRIL = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_4;
+    const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_5 = 'M5';
+    const AUTOFILTER_RULETYPE_DYNAMIC_MAY = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_5;
+    const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_6 = 'M6';
+    const AUTOFILTER_RULETYPE_DYNAMIC_JUNE = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_6;
+    const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_7 = 'M7';
+    const AUTOFILTER_RULETYPE_DYNAMIC_JULY = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_7;
+    const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_8 = 'M8';
+    const AUTOFILTER_RULETYPE_DYNAMIC_AUGUST = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_8;
+    const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_9 = 'M9';
+    const AUTOFILTER_RULETYPE_DYNAMIC_SEPTEMBER = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_9;
+    const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_10 = 'M10';
+    const AUTOFILTER_RULETYPE_DYNAMIC_OCTOBER = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_10;
+    const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_11 = 'M11';
+    const AUTOFILTER_RULETYPE_DYNAMIC_NOVEMBER = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_11;
+    const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_12 = 'M12';
+    const AUTOFILTER_RULETYPE_DYNAMIC_DECEMBER = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_12;
+    const AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_1 = 'Q1';
+    const AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_2 = 'Q2';
+    const AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_3 = 'Q3';
+    const AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_4 = 'Q4';
+    const AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE = 'aboveAverage';
+    const AUTOFILTER_RULETYPE_DYNAMIC_BELOWAVERAGE = 'belowAverage';
+
+    private static $dynamicTypes = [
+        self::AUTOFILTER_RULETYPE_DYNAMIC_YESTERDAY,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_TODAY,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_TOMORROW,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_YEARTODATE,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_THISYEAR,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_THISQUARTER,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_THISMONTH,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_THISWEEK,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_LASTYEAR,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_LASTQUARTER,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_LASTMONTH,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_LASTWEEK,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_NEXTYEAR,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_NEXTQUARTER,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_NEXTMONTH,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_NEXTWEEK,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_1,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_2,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_3,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_4,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_5,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_6,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_7,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_8,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_9,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_10,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_11,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_12,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_1,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_2,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_3,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_4,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE,
+        self::AUTOFILTER_RULETYPE_DYNAMIC_BELOWAVERAGE,
+    ];
+
+    /*
+     *    The only valid filter rule operators for filter and customFilter types are:
+     *        <xsd:enumeration value="equal"/>
+     *        <xsd:enumeration value="lessThan"/>
+     *        <xsd:enumeration value="lessThanOrEqual"/>
+     *        <xsd:enumeration value="notEqual"/>
+     *        <xsd:enumeration value="greaterThanOrEqual"/>
+     *        <xsd:enumeration value="greaterThan"/>
+     */
+    const AUTOFILTER_COLUMN_RULE_EQUAL = 'equal';
+    const AUTOFILTER_COLUMN_RULE_NOTEQUAL = 'notEqual';
+    const AUTOFILTER_COLUMN_RULE_GREATERTHAN = 'greaterThan';
+    const AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL = 'greaterThanOrEqual';
+    const AUTOFILTER_COLUMN_RULE_LESSTHAN = 'lessThan';
+    const AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL = 'lessThanOrEqual';
+
+    private static $operators = [
+        self::AUTOFILTER_COLUMN_RULE_EQUAL,
+        self::AUTOFILTER_COLUMN_RULE_NOTEQUAL,
+        self::AUTOFILTER_COLUMN_RULE_GREATERTHAN,
+        self::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL,
+        self::AUTOFILTER_COLUMN_RULE_LESSTHAN,
+        self::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL,
+    ];
+
+    const AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE = 'byValue';
+    const AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT = 'byPercent';
+
+    private static $topTenValue = [
+        self::AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE,
+        self::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT,
+    ];
+
+    const AUTOFILTER_COLUMN_RULE_TOPTEN_TOP = 'top';
+    const AUTOFILTER_COLUMN_RULE_TOPTEN_BOTTOM = 'bottom';
+
+    private static $topTenType = [
+        self::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP,
+        self::AUTOFILTER_COLUMN_RULE_TOPTEN_BOTTOM,
+    ];
+
+    // Rule Operators (Numeric, Boolean etc)
+//    const AUTOFILTER_COLUMN_RULE_BETWEEN            = 'between';        //    greaterThanOrEqual 1 && lessThanOrEqual 2
+    // Rule Operators (Numeric Special) which are translated to standard numeric operators with calculated values
+//    const AUTOFILTER_COLUMN_RULE_TOPTEN                = 'topTen';            //    greaterThan calculated value
+//    const AUTOFILTER_COLUMN_RULE_TOPTENPERCENT        = 'topTenPercent';    //    greaterThan calculated value
+//    const AUTOFILTER_COLUMN_RULE_ABOVEAVERAGE        = 'aboveAverage';    //    Value is calculated as the average
+//    const AUTOFILTER_COLUMN_RULE_BELOWAVERAGE        = 'belowAverage';    //    Value is calculated as the average
+    // Rule Operators (String) which are set as wild-carded values
+//    const AUTOFILTER_COLUMN_RULE_BEGINSWITH            = 'beginsWith';            // A*
+//    const AUTOFILTER_COLUMN_RULE_ENDSWITH            = 'endsWith';            // *Z
+//    const AUTOFILTER_COLUMN_RULE_CONTAINS            = 'contains';            // *B*
+//    const AUTOFILTER_COLUMN_RULE_DOESNTCONTAIN        = 'notEqual';            //    notEqual *B*
+    // Rule Operators (Date Special) which are translated to standard numeric operators with calculated values
+//    const AUTOFILTER_COLUMN_RULE_BEFORE                = 'lessThan';
+//    const AUTOFILTER_COLUMN_RULE_AFTER                = 'greaterThan';
+//    const AUTOFILTER_COLUMN_RULE_YESTERDAY            = 'yesterday';
+//    const AUTOFILTER_COLUMN_RULE_TODAY                = 'today';
+//    const AUTOFILTER_COLUMN_RULE_TOMORROW            = 'tomorrow';
+//    const AUTOFILTER_COLUMN_RULE_LASTWEEK            = 'lastWeek';
+//    const AUTOFILTER_COLUMN_RULE_THISWEEK            = 'thisWeek';
+//    const AUTOFILTER_COLUMN_RULE_NEXTWEEK            = 'nextWeek';
+//    const AUTOFILTER_COLUMN_RULE_LASTMONTH            = 'lastMonth';
+//    const AUTOFILTER_COLUMN_RULE_THISMONTH            = 'thisMonth';
+//    const AUTOFILTER_COLUMN_RULE_NEXTMONTH            = 'nextMonth';
+//    const AUTOFILTER_COLUMN_RULE_LASTQUARTER        = 'lastQuarter';
+//    const AUTOFILTER_COLUMN_RULE_THISQUARTER        = 'thisQuarter';
+//    const AUTOFILTER_COLUMN_RULE_NEXTQUARTER        = 'nextQuarter';
+//    const AUTOFILTER_COLUMN_RULE_LASTYEAR            = 'lastYear';
+//    const AUTOFILTER_COLUMN_RULE_THISYEAR            = 'thisYear';
+//    const AUTOFILTER_COLUMN_RULE_NEXTYEAR            = 'nextYear';
+//    const AUTOFILTER_COLUMN_RULE_YEARTODATE            = 'yearToDate';            //    <dynamicFilter val="40909" type="yearToDate" maxVal="41113"/>
+//    const AUTOFILTER_COLUMN_RULE_ALLDATESINMONTH    = 'allDatesInMonth';    //    <dynamicFilter type="M2"/> for Month/February
+//    const AUTOFILTER_COLUMN_RULE_ALLDATESINQUARTER    = 'allDatesInQuarter';    //    <dynamicFilter type="Q2"/> for Quarter 2
+
+    /**
+     * Autofilter Column.
+     *
+     * @var Column
+     */
+    private $parent;
+
+    /**
+     * Autofilter Rule Type.
+     *
+     * @var string
+     */
+    private $ruleType = self::AUTOFILTER_RULETYPE_FILTER;
+
+    /**
+     * Autofilter Rule Value.
+     *
+     * @var string
+     */
+    private $value = '';
+
+    /**
+     * Autofilter Rule Operator.
+     *
+     * @var string
+     */
+    private $operator = self::AUTOFILTER_COLUMN_RULE_EQUAL;
+
+    /**
+     * DateTimeGrouping Group Value.
+     *
+     * @var string
+     */
+    private $grouping = '';
+
+    /**
+     * Create a new Rule.
+     *
+     * @param Column $pParent
+     */
+    public function __construct(?Column $pParent = null)
+    {
+        $this->parent = $pParent;
+    }
+
+    /**
+     * Get AutoFilter Rule Type.
+     *
+     * @return string
+     */
+    public function getRuleType()
+    {
+        return $this->ruleType;
+    }
+
+    /**
+     * Set AutoFilter Rule Type.
+     *
+     * @param string $pRuleType see self::AUTOFILTER_RULETYPE_*
+     *
+     * @return $this
+     */
+    public function setRuleType($pRuleType)
+    {
+        if (!in_array($pRuleType, self::$ruleTypes)) {
+            throw new PhpSpreadsheetException('Invalid rule type for column AutoFilter Rule.');
+        }
+
+        $this->ruleType = $pRuleType;
+
+        return $this;
+    }
+
+    /**
+     * Get AutoFilter Rule Value.
+     *
+     * @return string
+     */
+    public function getValue()
+    {
+        return $this->value;
+    }
+
+    /**
+     * Set AutoFilter Rule Value.
+     *
+     * @param string|string[] $pValue
+     *
+     * @return $this
+     */
+    public function setValue($pValue)
+    {
+        if (is_array($pValue)) {
+            $grouping = -1;
+            foreach ($pValue as $key => $value) {
+                //    Validate array entries
+                if (!in_array($key, self::$dateTimeGroups)) {
+                    //    Remove any invalid entries from the value array
+                    unset($pValue[$key]);
+                } else {
+                    //    Work out what the dateTime grouping will be
+                    $grouping = max($grouping, array_search($key, self::$dateTimeGroups));
+                }
+            }
+            if (count($pValue) == 0) {
+                throw new PhpSpreadsheetException('Invalid rule value for column AutoFilter Rule.');
+            }
+            //    Set the dateTime grouping that we've anticipated
+            $this->setGrouping(self::$dateTimeGroups[$grouping]);
+        }
+        $this->value = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get AutoFilter Rule Operator.
+     *
+     * @return string
+     */
+    public function getOperator()
+    {
+        return $this->operator;
+    }
+
+    /**
+     * Set AutoFilter Rule Operator.
+     *
+     * @param string $pOperator see self::AUTOFILTER_COLUMN_RULE_*
+     *
+     * @return $this
+     */
+    public function setOperator($pOperator)
+    {
+        if (empty($pOperator)) {
+            $pOperator = self::AUTOFILTER_COLUMN_RULE_EQUAL;
+        }
+        if ((!in_array($pOperator, self::$operators)) &&
+            (!in_array($pOperator, self::$topTenValue))) {
+            throw new PhpSpreadsheetException('Invalid operator for column AutoFilter Rule.');
+        }
+        $this->operator = $pOperator;
+
+        return $this;
+    }
+
+    /**
+     * Get AutoFilter Rule Grouping.
+     *
+     * @return string
+     */
+    public function getGrouping()
+    {
+        return $this->grouping;
+    }
+
+    /**
+     * Set AutoFilter Rule Grouping.
+     *
+     * @param string $pGrouping
+     *
+     * @return $this
+     */
+    public function setGrouping($pGrouping)
+    {
+        if (($pGrouping !== null) &&
+            (!in_array($pGrouping, self::$dateTimeGroups)) &&
+            (!in_array($pGrouping, self::$dynamicTypes)) &&
+            (!in_array($pGrouping, self::$topTenType))) {
+            throw new PhpSpreadsheetException('Invalid rule type for column AutoFilter Rule.');
+        }
+        $this->grouping = $pGrouping;
+
+        return $this;
+    }
+
+    /**
+     * Set AutoFilter Rule.
+     *
+     * @param string $pOperator see self::AUTOFILTER_COLUMN_RULE_*
+     * @param string|string[] $pValue
+     * @param string $pGrouping
+     *
+     * @return $this
+     */
+    public function setRule($pOperator, $pValue, $pGrouping = null)
+    {
+        $this->setOperator($pOperator);
+        $this->setValue($pValue);
+        //  Only set grouping if it's been passed in as a user-supplied argument,
+        //      otherwise we're calculating it when we setValue() and don't want to overwrite that
+        //      If the user supplies an argumnet for grouping, then on their own head be it
+        if ($pGrouping !== null) {
+            $this->setGrouping($pGrouping);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get this Rule's AutoFilter Column Parent.
+     *
+     * @return Column
+     */
+    public function getParent()
+    {
+        return $this->parent;
+    }
+
+    /**
+     * Set this Rule's AutoFilter Column Parent.
+     *
+     * @param Column $pParent
+     *
+     * @return $this
+     */
+    public function setParent(?Column $pParent = null)
+    {
+        $this->parent = $pParent;
+
+        return $this;
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $vars = get_object_vars($this);
+        foreach ($vars as $key => $value) {
+            if (is_object($value)) {
+                if ($key == 'parent') {
+                    //    Detach from autofilter column parent
+                    $this->$key = null;
+                } else {
+                    $this->$key = clone $value;
+                }
+            } else {
+                $this->$key = $value;
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/BaseDrawing.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/BaseDrawing.php
new file mode 100755
index 0000000..1b145a8
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/BaseDrawing.php
@@ -0,0 +1,532 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+use PhpOffice\PhpSpreadsheet\Cell\Hyperlink;
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+use PhpOffice\PhpSpreadsheet\IComparable;
+
+class BaseDrawing implements IComparable
+{
+    /**
+     * Image counter.
+     *
+     * @var int
+     */
+    private static $imageCounter = 0;
+
+    /**
+     * Image index.
+     *
+     * @var int
+     */
+    private $imageIndex = 0;
+
+    /**
+     * Name.
+     *
+     * @var string
+     */
+    protected $name;
+
+    /**
+     * Description.
+     *
+     * @var string
+     */
+    protected $description;
+
+    /**
+     * Worksheet.
+     *
+     * @var Worksheet
+     */
+    protected $worksheet;
+
+    /**
+     * Coordinates.
+     *
+     * @var string
+     */
+    protected $coordinates;
+
+    /**
+     * Offset X.
+     *
+     * @var int
+     */
+    protected $offsetX;
+
+    /**
+     * Offset Y.
+     *
+     * @var int
+     */
+    protected $offsetY;
+
+    /**
+     * Width.
+     *
+     * @var int
+     */
+    protected $width;
+
+    /**
+     * Height.
+     *
+     * @var int
+     */
+    protected $height;
+
+    /**
+     * Proportional resize.
+     *
+     * @var bool
+     */
+    protected $resizeProportional;
+
+    /**
+     * Rotation.
+     *
+     * @var int
+     */
+    protected $rotation;
+
+    /**
+     * Shadow.
+     *
+     * @var Drawing\Shadow
+     */
+    protected $shadow;
+
+    /**
+     * Image hyperlink.
+     *
+     * @var null|Hyperlink
+     */
+    private $hyperlink;
+
+    /**
+     * Create a new BaseDrawing.
+     */
+    public function __construct()
+    {
+        // Initialise values
+        $this->name = '';
+        $this->description = '';
+        $this->worksheet = null;
+        $this->coordinates = 'A1';
+        $this->offsetX = 0;
+        $this->offsetY = 0;
+        $this->width = 0;
+        $this->height = 0;
+        $this->resizeProportional = true;
+        $this->rotation = 0;
+        $this->shadow = new Drawing\Shadow();
+
+        // Set image index
+        ++self::$imageCounter;
+        $this->imageIndex = self::$imageCounter;
+    }
+
+    /**
+     * Get image index.
+     *
+     * @return int
+     */
+    public function getImageIndex()
+    {
+        return $this->imageIndex;
+    }
+
+    /**
+     * Get Name.
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * Set Name.
+     *
+     * @param string $pValue
+     *
+     * @return $this
+     */
+    public function setName($pValue)
+    {
+        $this->name = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Description.
+     *
+     * @return string
+     */
+    public function getDescription()
+    {
+        return $this->description;
+    }
+
+    /**
+     * Set Description.
+     *
+     * @param string $description
+     *
+     * @return $this
+     */
+    public function setDescription($description)
+    {
+        $this->description = $description;
+
+        return $this;
+    }
+
+    /**
+     * Get Worksheet.
+     *
+     * @return Worksheet
+     */
+    public function getWorksheet()
+    {
+        return $this->worksheet;
+    }
+
+    /**
+     * Set Worksheet.
+     *
+     * @param Worksheet $pValue
+     * @param bool $pOverrideOld If a Worksheet has already been assigned, overwrite it and remove image from old Worksheet?
+     *
+     * @return $this
+     */
+    public function setWorksheet(?Worksheet $pValue = null, $pOverrideOld = false)
+    {
+        if ($this->worksheet === null) {
+            // Add drawing to \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
+            $this->worksheet = $pValue;
+            $this->worksheet->getCell($this->coordinates);
+            $this->worksheet->getDrawingCollection()->append($this);
+        } else {
+            if ($pOverrideOld) {
+                // Remove drawing from old \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
+                $iterator = $this->worksheet->getDrawingCollection()->getIterator();
+
+                while ($iterator->valid()) {
+                    if ($iterator->current()->getHashCode() == $this->getHashCode()) {
+                        $this->worksheet->getDrawingCollection()->offsetUnset($iterator->key());
+                        $this->worksheet = null;
+
+                        break;
+                    }
+                }
+
+                // Set new \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
+                $this->setWorksheet($pValue);
+            } else {
+                throw new PhpSpreadsheetException('A Worksheet has already been assigned. Drawings can only exist on one \\PhpOffice\\PhpSpreadsheet\\Worksheet.');
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Coordinates.
+     *
+     * @return string
+     */
+    public function getCoordinates()
+    {
+        return $this->coordinates;
+    }
+
+    /**
+     * Set Coordinates.
+     *
+     * @param string $pValue eg: 'A1'
+     *
+     * @return $this
+     */
+    public function setCoordinates($pValue)
+    {
+        $this->coordinates = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get OffsetX.
+     *
+     * @return int
+     */
+    public function getOffsetX()
+    {
+        return $this->offsetX;
+    }
+
+    /**
+     * Set OffsetX.
+     *
+     * @param int $pValue
+     *
+     * @return $this
+     */
+    public function setOffsetX($pValue)
+    {
+        $this->offsetX = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get OffsetY.
+     *
+     * @return int
+     */
+    public function getOffsetY()
+    {
+        return $this->offsetY;
+    }
+
+    /**
+     * Set OffsetY.
+     *
+     * @param int $pValue
+     *
+     * @return $this
+     */
+    public function setOffsetY($pValue)
+    {
+        $this->offsetY = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Width.
+     *
+     * @return int
+     */
+    public function getWidth()
+    {
+        return $this->width;
+    }
+
+    /**
+     * Set Width.
+     *
+     * @param int $pValue
+     *
+     * @return $this
+     */
+    public function setWidth($pValue)
+    {
+        // Resize proportional?
+        if ($this->resizeProportional && $pValue != 0) {
+            $ratio = $this->height / ($this->width != 0 ? $this->width : 1);
+            $this->height = round($ratio * $pValue);
+        }
+
+        // Set width
+        $this->width = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Height.
+     *
+     * @return int
+     */
+    public function getHeight()
+    {
+        return $this->height;
+    }
+
+    /**
+     * Set Height.
+     *
+     * @param int $pValue
+     *
+     * @return $this
+     */
+    public function setHeight($pValue)
+    {
+        // Resize proportional?
+        if ($this->resizeProportional && $pValue != 0) {
+            $ratio = $this->width / ($this->height != 0 ? $this->height : 1);
+            $this->width = round($ratio * $pValue);
+        }
+
+        // Set height
+        $this->height = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Set width and height with proportional resize.
+     *
+     * Example:
+     * <code>
+     * $objDrawing->setResizeProportional(true);
+     * $objDrawing->setWidthAndHeight(160,120);
+     * </code>
+     *
+     * @author Vincent@luo MSN:kele_100@hotmail.com
+     *
+     * @param int $width
+     * @param int $height
+     *
+     * @return $this
+     */
+    public function setWidthAndHeight($width, $height)
+    {
+        $xratio = $width / ($this->width != 0 ? $this->width : 1);
+        $yratio = $height / ($this->height != 0 ? $this->height : 1);
+        if ($this->resizeProportional && !($width == 0 || $height == 0)) {
+            if (($xratio * $this->height) < $height) {
+                $this->height = ceil($xratio * $this->height);
+                $this->width = $width;
+            } else {
+                $this->width = ceil($yratio * $this->width);
+                $this->height = $height;
+            }
+        } else {
+            $this->width = $width;
+            $this->height = $height;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get ResizeProportional.
+     *
+     * @return bool
+     */
+    public function getResizeProportional()
+    {
+        return $this->resizeProportional;
+    }
+
+    /**
+     * Set ResizeProportional.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setResizeProportional($pValue)
+    {
+        $this->resizeProportional = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Rotation.
+     *
+     * @return int
+     */
+    public function getRotation()
+    {
+        return $this->rotation;
+    }
+
+    /**
+     * Set Rotation.
+     *
+     * @param int $pValue
+     *
+     * @return $this
+     */
+    public function setRotation($pValue)
+    {
+        $this->rotation = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Shadow.
+     *
+     * @return Drawing\Shadow
+     */
+    public function getShadow()
+    {
+        return $this->shadow;
+    }
+
+    /**
+     * Set Shadow.
+     *
+     * @param Drawing\Shadow $pValue
+     *
+     * @return $this
+     */
+    public function setShadow(?Drawing\Shadow $pValue = null)
+    {
+        $this->shadow = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get hash code.
+     *
+     * @return string Hash code
+     */
+    public function getHashCode()
+    {
+        return md5(
+            $this->name .
+            $this->description .
+            $this->worksheet->getHashCode() .
+            $this->coordinates .
+            $this->offsetX .
+            $this->offsetY .
+            $this->width .
+            $this->height .
+            $this->rotation .
+            $this->shadow->getHashCode() .
+            __CLASS__
+        );
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $vars = get_object_vars($this);
+        foreach ($vars as $key => $value) {
+            if ($key == 'worksheet') {
+                $this->worksheet = null;
+            } elseif (is_object($value)) {
+                $this->$key = clone $value;
+            } else {
+                $this->$key = $value;
+            }
+        }
+    }
+
+    public function setHyperlink(?Hyperlink $pHyperlink = null): void
+    {
+        $this->hyperlink = $pHyperlink;
+    }
+
+    /**
+     * @return null|Hyperlink
+     */
+    public function getHyperlink()
+    {
+        return $this->hyperlink;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/CellIterator.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/CellIterator.php
new file mode 100755
index 0000000..45f76ca
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/CellIterator.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+use Iterator;
+
+abstract class CellIterator implements Iterator
+{
+    /**
+     * Worksheet to iterate.
+     *
+     * @var Worksheet
+     */
+    protected $worksheet;
+
+    /**
+     * Iterate only existing cells.
+     *
+     * @var bool
+     */
+    protected $onlyExistingCells = false;
+
+    /**
+     * Destructor.
+     */
+    public function __destruct()
+    {
+        $this->worksheet = null;
+    }
+
+    /**
+     * Get loop only existing cells.
+     *
+     * @return bool
+     */
+    public function getIterateOnlyExistingCells()
+    {
+        return $this->onlyExistingCells;
+    }
+
+    /**
+     * Validate start/end values for "IterateOnlyExistingCells" mode, and adjust if necessary.
+     */
+    abstract protected function adjustForExistingOnlyRange();
+
+    /**
+     * Set the iterator to loop only existing cells.
+     *
+     * @param bool $value
+     */
+    public function setIterateOnlyExistingCells($value): void
+    {
+        $this->onlyExistingCells = (bool) $value;
+
+        $this->adjustForExistingOnlyRange();
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Column.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Column.php
new file mode 100755
index 0000000..410e807
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Column.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+class Column
+{
+    /**
+     * \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet.
+     *
+     * @var Worksheet
+     */
+    private $parent;
+
+    /**
+     * Column index.
+     *
+     * @var string
+     */
+    private $columnIndex;
+
+    /**
+     * Create a new column.
+     *
+     * @param Worksheet $parent
+     * @param string $columnIndex
+     */
+    public function __construct(?Worksheet $parent = null, $columnIndex = 'A')
+    {
+        // Set parent and column index
+        $this->parent = $parent;
+        $this->columnIndex = $columnIndex;
+    }
+
+    /**
+     * Destructor.
+     */
+    public function __destruct()
+    {
+        $this->parent = null;
+    }
+
+    /**
+     * Get column index as string eg: 'A'.
+     *
+     * @return string
+     */
+    public function getColumnIndex()
+    {
+        return $this->columnIndex;
+    }
+
+    /**
+     * Get cell iterator.
+     *
+     * @param int $startRow The row number at which to start iterating
+     * @param int $endRow Optionally, the row number at which to stop iterating
+     *
+     * @return ColumnCellIterator
+     */
+    public function getCellIterator($startRow = 1, $endRow = null)
+    {
+        return new ColumnCellIterator($this->parent, $this->columnIndex, $startRow, $endRow);
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnCellIterator.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnCellIterator.php
new file mode 100755
index 0000000..ec16fbe
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnCellIterator.php
@@ -0,0 +1,189 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+
+class ColumnCellIterator extends CellIterator
+{
+    /**
+     * Current iterator position.
+     *
+     * @var int
+     */
+    private $currentRow;
+
+    /**
+     * Column index.
+     *
+     * @var string
+     */
+    private $columnIndex;
+
+    /**
+     * Start position.
+     *
+     * @var int
+     */
+    private $startRow = 1;
+
+    /**
+     * End position.
+     *
+     * @var int
+     */
+    private $endRow = 1;
+
+    /**
+     * Create a new row iterator.
+     *
+     * @param Worksheet $subject The worksheet to iterate over
+     * @param string $columnIndex The column that we want to iterate
+     * @param int $startRow The row number at which to start iterating
+     * @param int $endRow Optionally, the row number at which to stop iterating
+     */
+    public function __construct(?Worksheet $subject = null, $columnIndex = 'A', $startRow = 1, $endRow = null)
+    {
+        // Set subject
+        $this->worksheet = $subject;
+        $this->columnIndex = Coordinate::columnIndexFromString($columnIndex);
+        $this->resetEnd($endRow);
+        $this->resetStart($startRow);
+    }
+
+    /**
+     * (Re)Set the start row and the current row pointer.
+     *
+     * @param int $startRow The row number at which to start iterating
+     *
+     * @return $this
+     */
+    public function resetStart($startRow = 1)
+    {
+        $this->startRow = $startRow;
+        $this->adjustForExistingOnlyRange();
+        $this->seek($startRow);
+
+        return $this;
+    }
+
+    /**
+     * (Re)Set the end row.
+     *
+     * @param int $endRow The row number at which to stop iterating
+     *
+     * @return $this
+     */
+    public function resetEnd($endRow = null)
+    {
+        $this->endRow = ($endRow) ? $endRow : $this->worksheet->getHighestRow();
+        $this->adjustForExistingOnlyRange();
+
+        return $this;
+    }
+
+    /**
+     * Set the row pointer to the selected row.
+     *
+     * @param int $row The row number to set the current pointer at
+     *
+     * @return $this
+     */
+    public function seek($row = 1)
+    {
+        if (($row < $this->startRow) || ($row > $this->endRow)) {
+            throw new PhpSpreadsheetException("Row $row is out of range ({$this->startRow} - {$this->endRow})");
+        } elseif ($this->onlyExistingCells && !($this->worksheet->cellExistsByColumnAndRow($this->columnIndex, $row))) {
+            throw new PhpSpreadsheetException('In "IterateOnlyExistingCells" mode and Cell does not exist');
+        }
+        $this->currentRow = $row;
+
+        return $this;
+    }
+
+    /**
+     * Rewind the iterator to the starting row.
+     */
+    public function rewind(): void
+    {
+        $this->currentRow = $this->startRow;
+    }
+
+    /**
+     * Return the current cell in this worksheet column.
+     *
+     * @return null|\PhpOffice\PhpSpreadsheet\Cell\Cell
+     */
+    public function current()
+    {
+        return $this->worksheet->getCellByColumnAndRow($this->columnIndex, $this->currentRow);
+    }
+
+    /**
+     * Return the current iterator key.
+     *
+     * @return int
+     */
+    public function key()
+    {
+        return $this->currentRow;
+    }
+
+    /**
+     * Set the iterator to its next value.
+     */
+    public function next(): void
+    {
+        do {
+            ++$this->currentRow;
+        } while (($this->onlyExistingCells) &&
+            (!$this->worksheet->cellExistsByColumnAndRow($this->columnIndex, $this->currentRow)) &&
+            ($this->currentRow <= $this->endRow));
+    }
+
+    /**
+     * Set the iterator to its previous value.
+     */
+    public function prev(): void
+    {
+        do {
+            --$this->currentRow;
+        } while (($this->onlyExistingCells) &&
+            (!$this->worksheet->cellExistsByColumnAndRow($this->columnIndex, $this->currentRow)) &&
+            ($this->currentRow >= $this->startRow));
+    }
+
+    /**
+     * Indicate if more rows exist in the worksheet range of rows that we're iterating.
+     *
+     * @return bool
+     */
+    public function valid()
+    {
+        return $this->currentRow <= $this->endRow && $this->currentRow >= $this->startRow;
+    }
+
+    /**
+     * Validate start/end values for "IterateOnlyExistingCells" mode, and adjust if necessary.
+     */
+    protected function adjustForExistingOnlyRange(): void
+    {
+        if ($this->onlyExistingCells) {
+            while ((!$this->worksheet->cellExistsByColumnAndRow($this->columnIndex, $this->startRow)) &&
+                ($this->startRow <= $this->endRow)) {
+                ++$this->startRow;
+            }
+            if ($this->startRow > $this->endRow) {
+                throw new PhpSpreadsheetException('No cells exist within the specified range');
+            }
+            while ((!$this->worksheet->cellExistsByColumnAndRow($this->columnIndex, $this->endRow)) &&
+                ($this->endRow >= $this->startRow)) {
+                --$this->endRow;
+            }
+            if ($this->endRow < $this->startRow) {
+                throw new PhpSpreadsheetException('No cells exist within the specified range');
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnDimension.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnDimension.php
new file mode 100755
index 0000000..4e87a34
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnDimension.php
@@ -0,0 +1,115 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+class ColumnDimension extends Dimension
+{
+    /**
+     * Column index.
+     *
+     * @var string
+     */
+    private $columnIndex;
+
+    /**
+     * Column width.
+     *
+     * When this is set to a negative value, the column width should be ignored by IWriter
+     *
+     * @var float
+     */
+    private $width = -1;
+
+    /**
+     * Auto size?
+     *
+     * @var bool
+     */
+    private $autoSize = false;
+
+    /**
+     * Create a new ColumnDimension.
+     *
+     * @param string $pIndex Character column index
+     */
+    public function __construct($pIndex = 'A')
+    {
+        // Initialise values
+        $this->columnIndex = $pIndex;
+
+        // set dimension as unformatted by default
+        parent::__construct(0);
+    }
+
+    /**
+     * Get column index as string eg: 'A'.
+     *
+     * @return string
+     */
+    public function getColumnIndex()
+    {
+        return $this->columnIndex;
+    }
+
+    /**
+     * Set column index as string eg: 'A'.
+     *
+     * @param string $pValue
+     *
+     * @return $this
+     */
+    public function setColumnIndex($pValue)
+    {
+        $this->columnIndex = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Width.
+     *
+     * @return float
+     */
+    public function getWidth()
+    {
+        return $this->width;
+    }
+
+    /**
+     * Set Width.
+     *
+     * @param float $pValue
+     *
+     * @return $this
+     */
+    public function setWidth($pValue)
+    {
+        $this->width = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Auto Size.
+     *
+     * @return bool
+     */
+    public function getAutoSize()
+    {
+        return $this->autoSize;
+    }
+
+    /**
+     * Set Auto Size.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setAutoSize($pValue)
+    {
+        $this->autoSize = $pValue;
+
+        return $this;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnIterator.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnIterator.php
new file mode 100755
index 0000000..d0bb20c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnIterator.php
@@ -0,0 +1,172 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+use Iterator;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Exception;
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+
+class ColumnIterator implements Iterator
+{
+    /**
+     * Worksheet to iterate.
+     *
+     * @var Worksheet
+     */
+    private $worksheet;
+
+    /**
+     * Current iterator position.
+     *
+     * @var int
+     */
+    private $currentColumnIndex = 1;
+
+    /**
+     * Start position.
+     *
+     * @var int
+     */
+    private $startColumnIndex = 1;
+
+    /**
+     * End position.
+     *
+     * @var int
+     */
+    private $endColumnIndex = 1;
+
+    /**
+     * Create a new column iterator.
+     *
+     * @param Worksheet $worksheet The worksheet to iterate over
+     * @param string $startColumn The column address at which to start iterating
+     * @param string $endColumn Optionally, the column address at which to stop iterating
+     */
+    public function __construct(Worksheet $worksheet, $startColumn = 'A', $endColumn = null)
+    {
+        // Set subject
+        $this->worksheet = $worksheet;
+        $this->resetEnd($endColumn);
+        $this->resetStart($startColumn);
+    }
+
+    /**
+     * Destructor.
+     */
+    public function __destruct()
+    {
+        $this->worksheet = null;
+    }
+
+    /**
+     * (Re)Set the start column and the current column pointer.
+     *
+     * @param string $startColumn The column address at which to start iterating
+     *
+     * @return $this
+     */
+    public function resetStart($startColumn = 'A')
+    {
+        $startColumnIndex = Coordinate::columnIndexFromString($startColumn);
+        if ($startColumnIndex > Coordinate::columnIndexFromString($this->worksheet->getHighestColumn())) {
+            throw new Exception("Start column ({$startColumn}) is beyond highest column ({$this->worksheet->getHighestColumn()})");
+        }
+
+        $this->startColumnIndex = $startColumnIndex;
+        if ($this->endColumnIndex < $this->startColumnIndex) {
+            $this->endColumnIndex = $this->startColumnIndex;
+        }
+        $this->seek($startColumn);
+
+        return $this;
+    }
+
+    /**
+     * (Re)Set the end column.
+     *
+     * @param string $endColumn The column address at which to stop iterating
+     *
+     * @return $this
+     */
+    public function resetEnd($endColumn = null)
+    {
+        $endColumn = $endColumn ? $endColumn : $this->worksheet->getHighestColumn();
+        $this->endColumnIndex = Coordinate::columnIndexFromString($endColumn);
+
+        return $this;
+    }
+
+    /**
+     * Set the column pointer to the selected column.
+     *
+     * @param string $column The column address to set the current pointer at
+     *
+     * @return $this
+     */
+    public function seek($column = 'A')
+    {
+        $column = Coordinate::columnIndexFromString($column);
+        if (($column < $this->startColumnIndex) || ($column > $this->endColumnIndex)) {
+            throw new PhpSpreadsheetException("Column $column is out of range ({$this->startColumnIndex} - {$this->endColumnIndex})");
+        }
+        $this->currentColumnIndex = $column;
+
+        return $this;
+    }
+
+    /**
+     * Rewind the iterator to the starting column.
+     */
+    public function rewind(): void
+    {
+        $this->currentColumnIndex = $this->startColumnIndex;
+    }
+
+    /**
+     * Return the current column in this worksheet.
+     *
+     * @return Column
+     */
+    public function current()
+    {
+        return new Column($this->worksheet, Coordinate::stringFromColumnIndex($this->currentColumnIndex));
+    }
+
+    /**
+     * Return the current iterator key.
+     *
+     * @return string
+     */
+    public function key()
+    {
+        return Coordinate::stringFromColumnIndex($this->currentColumnIndex);
+    }
+
+    /**
+     * Set the iterator to its next value.
+     */
+    public function next(): void
+    {
+        ++$this->currentColumnIndex;
+    }
+
+    /**
+     * Set the iterator to its previous value.
+     */
+    public function prev(): void
+    {
+        --$this->currentColumnIndex;
+    }
+
+    /**
+     * Indicate if more columns exist in the worksheet range of columns that we're iterating.
+     *
+     * @return bool
+     */
+    public function valid()
+    {
+        return $this->currentColumnIndex <= $this->endColumnIndex && $this->currentColumnIndex >= $this->startColumnIndex;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Dimension.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Dimension.php
new file mode 100755
index 0000000..a27daf0
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Dimension.php
@@ -0,0 +1,163 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+
+abstract class Dimension
+{
+    /**
+     * Visible?
+     *
+     * @var bool
+     */
+    private $visible = true;
+
+    /**
+     * Outline level.
+     *
+     * @var int
+     */
+    private $outlineLevel = 0;
+
+    /**
+     * Collapsed.
+     *
+     * @var bool
+     */
+    private $collapsed = false;
+
+    /**
+     * Index to cellXf. Null value means row has no explicit cellXf format.
+     *
+     * @var null|int
+     */
+    private $xfIndex;
+
+    /**
+     * Create a new Dimension.
+     *
+     * @param int $initialValue Numeric row index
+     */
+    public function __construct($initialValue = null)
+    {
+        // set dimension as unformatted by default
+        $this->xfIndex = $initialValue;
+    }
+
+    /**
+     * Get Visible.
+     *
+     * @return bool
+     */
+    public function getVisible()
+    {
+        return $this->visible;
+    }
+
+    /**
+     * Set Visible.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setVisible($pValue)
+    {
+        $this->visible = (bool) $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Outline Level.
+     *
+     * @return int
+     */
+    public function getOutlineLevel()
+    {
+        return $this->outlineLevel;
+    }
+
+    /**
+     * Set Outline Level.
+     * Value must be between 0 and 7.
+     *
+     * @param int $pValue
+     *
+     * @return $this
+     */
+    public function setOutlineLevel($pValue)
+    {
+        if ($pValue < 0 || $pValue > 7) {
+            throw new PhpSpreadsheetException('Outline level must range between 0 and 7.');
+        }
+
+        $this->outlineLevel = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Collapsed.
+     *
+     * @return bool
+     */
+    public function getCollapsed()
+    {
+        return $this->collapsed;
+    }
+
+    /**
+     * Set Collapsed.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setCollapsed($pValue)
+    {
+        $this->collapsed = (bool) $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get index to cellXf.
+     *
+     * @return int
+     */
+    public function getXfIndex()
+    {
+        return $this->xfIndex;
+    }
+
+    /**
+     * Set index to cellXf.
+     *
+     * @param int $pValue
+     *
+     * @return $this
+     */
+    public function setXfIndex($pValue)
+    {
+        $this->xfIndex = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $vars = get_object_vars($this);
+        foreach ($vars as $key => $value) {
+            if (is_object($value)) {
+                $this->$key = clone $value;
+            } else {
+                $this->$key = $value;
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing.php
new file mode 100755
index 0000000..1f1dae9
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing.php
@@ -0,0 +1,114 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+
+class Drawing extends BaseDrawing
+{
+    /**
+     * Path.
+     *
+     * @var string
+     */
+    private $path;
+
+    /**
+     * Create a new Drawing.
+     */
+    public function __construct()
+    {
+        // Initialise values
+        $this->path = '';
+
+        // Initialize parent
+        parent::__construct();
+    }
+
+    /**
+     * Get Filename.
+     *
+     * @return string
+     */
+    public function getFilename()
+    {
+        return basename($this->path);
+    }
+
+    /**
+     * Get indexed filename (using image index).
+     *
+     * @return string
+     */
+    public function getIndexedFilename()
+    {
+        $fileName = $this->getFilename();
+        $fileName = str_replace(' ', '_', $fileName);
+
+        return str_replace('.' . $this->getExtension(), '', $fileName) . $this->getImageIndex() . '.' . $this->getExtension();
+    }
+
+    /**
+     * Get Extension.
+     *
+     * @return string
+     */
+    public function getExtension()
+    {
+        $exploded = explode('.', basename($this->path));
+
+        return $exploded[count($exploded) - 1];
+    }
+
+    /**
+     * Get Path.
+     *
+     * @return string
+     */
+    public function getPath()
+    {
+        return $this->path;
+    }
+
+    /**
+     * Set Path.
+     *
+     * @param string $pValue File path
+     * @param bool $pVerifyFile Verify file
+     *
+     * @return $this
+     */
+    public function setPath($pValue, $pVerifyFile = true)
+    {
+        if ($pVerifyFile) {
+            if (file_exists($pValue)) {
+                $this->path = $pValue;
+
+                if ($this->width == 0 && $this->height == 0) {
+                    // Get width/height
+                    [$this->width, $this->height] = getimagesize($pValue);
+                }
+            } else {
+                throw new PhpSpreadsheetException("File $pValue not found!");
+            }
+        } else {
+            $this->path = $pValue;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get hash code.
+     *
+     * @return string Hash code
+     */
+    public function getHashCode()
+    {
+        return md5(
+            $this->path .
+            parent::getHashCode() .
+            __CLASS__
+        );
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing/Shadow.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing/Shadow.php
new file mode 100755
index 0000000..01ffed9
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing/Shadow.php
@@ -0,0 +1,289 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
+
+use PhpOffice\PhpSpreadsheet\IComparable;
+use PhpOffice\PhpSpreadsheet\Style\Color;
+
+class Shadow implements IComparable
+{
+    // Shadow alignment
+    const SHADOW_BOTTOM = 'b';
+    const SHADOW_BOTTOM_LEFT = 'bl';
+    const SHADOW_BOTTOM_RIGHT = 'br';
+    const SHADOW_CENTER = 'ctr';
+    const SHADOW_LEFT = 'l';
+    const SHADOW_TOP = 't';
+    const SHADOW_TOP_LEFT = 'tl';
+    const SHADOW_TOP_RIGHT = 'tr';
+
+    /**
+     * Visible.
+     *
+     * @var bool
+     */
+    private $visible;
+
+    /**
+     * Blur radius.
+     *
+     * Defaults to 6
+     *
+     * @var int
+     */
+    private $blurRadius;
+
+    /**
+     * Shadow distance.
+     *
+     * Defaults to 2
+     *
+     * @var int
+     */
+    private $distance;
+
+    /**
+     * Shadow direction (in degrees).
+     *
+     * @var int
+     */
+    private $direction;
+
+    /**
+     * Shadow alignment.
+     *
+     * @var int
+     */
+    private $alignment;
+
+    /**
+     * Color.
+     *
+     * @var Color
+     */
+    private $color;
+
+    /**
+     * Alpha.
+     *
+     * @var int
+     */
+    private $alpha;
+
+    /**
+     * Create a new Shadow.
+     */
+    public function __construct()
+    {
+        // Initialise values
+        $this->visible = false;
+        $this->blurRadius = 6;
+        $this->distance = 2;
+        $this->direction = 0;
+        $this->alignment = self::SHADOW_BOTTOM_RIGHT;
+        $this->color = new Color(Color::COLOR_BLACK);
+        $this->alpha = 50;
+    }
+
+    /**
+     * Get Visible.
+     *
+     * @return bool
+     */
+    public function getVisible()
+    {
+        return $this->visible;
+    }
+
+    /**
+     * Set Visible.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setVisible($pValue)
+    {
+        $this->visible = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Blur radius.
+     *
+     * @return int
+     */
+    public function getBlurRadius()
+    {
+        return $this->blurRadius;
+    }
+
+    /**
+     * Set Blur radius.
+     *
+     * @param int $pValue
+     *
+     * @return $this
+     */
+    public function setBlurRadius($pValue)
+    {
+        $this->blurRadius = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Shadow distance.
+     *
+     * @return int
+     */
+    public function getDistance()
+    {
+        return $this->distance;
+    }
+
+    /**
+     * Set Shadow distance.
+     *
+     * @param int $pValue
+     *
+     * @return $this
+     */
+    public function setDistance($pValue)
+    {
+        $this->distance = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Shadow direction (in degrees).
+     *
+     * @return int
+     */
+    public function getDirection()
+    {
+        return $this->direction;
+    }
+
+    /**
+     * Set Shadow direction (in degrees).
+     *
+     * @param int $pValue
+     *
+     * @return $this
+     */
+    public function setDirection($pValue)
+    {
+        $this->direction = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Shadow alignment.
+     *
+     * @return int
+     */
+    public function getAlignment()
+    {
+        return $this->alignment;
+    }
+
+    /**
+     * Set Shadow alignment.
+     *
+     * @param int $pValue
+     *
+     * @return $this
+     */
+    public function setAlignment($pValue)
+    {
+        $this->alignment = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Color.
+     *
+     * @return Color
+     */
+    public function getColor()
+    {
+        return $this->color;
+    }
+
+    /**
+     * Set Color.
+     *
+     * @param Color $pValue
+     *
+     * @return $this
+     */
+    public function setColor(?Color $pValue = null)
+    {
+        $this->color = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Alpha.
+     *
+     * @return int
+     */
+    public function getAlpha()
+    {
+        return $this->alpha;
+    }
+
+    /**
+     * Set Alpha.
+     *
+     * @param int $pValue
+     *
+     * @return $this
+     */
+    public function setAlpha($pValue)
+    {
+        $this->alpha = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get hash code.
+     *
+     * @return string Hash code
+     */
+    public function getHashCode()
+    {
+        return md5(
+            ($this->visible ? 't' : 'f') .
+            $this->blurRadius .
+            $this->distance .
+            $this->direction .
+            $this->alignment .
+            $this->color->getHashCode() .
+            $this->alpha .
+            __CLASS__
+        );
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $vars = get_object_vars($this);
+        foreach ($vars as $key => $value) {
+            if (is_object($value)) {
+                $this->$key = clone $value;
+            } else {
+                $this->$key = $value;
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooter.php
new file mode 100755
index 0000000..cc37e7f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooter.php
@@ -0,0 +1,490 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+/**
+ * <code>
+ * Header/Footer Formatting Syntax taken from Office Open XML Part 4 - Markup Language Reference, page 1970:.
+ *
+ * There are a number of formatting codes that can be written inline with the actual header / footer text, which
+ * affect the formatting in the header or footer.
+ *
+ * Example: This example shows the text "Center Bold Header" on the first line (center section), and the date on
+ * the second line (center section).
+ *         &CCenter &"-,Bold"Bold&"-,Regular"Header_x000A_&D
+ *
+ * General Rules:
+ * There is no required order in which these codes must appear.
+ *
+ * The first occurrence of the following codes turns the formatting ON, the second occurrence turns it OFF again:
+ * - strikethrough
+ * - superscript
+ * - subscript
+ * Superscript and subscript cannot both be ON at same time. Whichever comes first wins and the other is ignored,
+ * while the first is ON.
+ * &L - code for "left section" (there are three header / footer locations, "left", "center", and "right"). When
+ * two or more occurrences of this section marker exist, the contents from all markers are concatenated, in the
+ * order of appearance, and placed into the left section.
+ * &P - code for "current page #"
+ * &N - code for "total pages"
+ * &font size - code for "text font size", where font size is a font size in points.
+ * &K - code for "text font color"
+ * RGB Color is specified as RRGGBB
+ * Theme Color is specifed as TTSNN where TT is the theme color Id, S is either "+" or "-" of the tint/shade
+ * value, NN is the tint/shade value.
+ * &S - code for "text strikethrough" on / off
+ * &X - code for "text super script" on / off
+ * &Y - code for "text subscript" on / off
+ * &C - code for "center section". When two or more occurrences of this section marker exist, the contents
+ * from all markers are concatenated, in the order of appearance, and placed into the center section.
+ *
+ * &D - code for "date"
+ * &T - code for "time"
+ * &G - code for "picture as background"
+ * &U - code for "text single underline"
+ * &E - code for "double underline"
+ * &R - code for "right section". When two or more occurrences of this section marker exist, the contents
+ * from all markers are concatenated, in the order of appearance, and placed into the right section.
+ * &Z - code for "this workbook's file path"
+ * &F - code for "this workbook's file name"
+ * &A - code for "sheet tab name"
+ * &+ - code for add to page #.
+ * &- - code for subtract from page #.
+ * &"font name,font type" - code for "text font name" and "text font type", where font name and font type
+ * are strings specifying the name and type of the font, separated by a comma. When a hyphen appears in font
+ * name, it means "none specified". Both of font name and font type can be localized values.
+ * &"-,Bold" - code for "bold font style"
+ * &B - also means "bold font style".
+ * &"-,Regular" - code for "regular font style"
+ * &"-,Italic" - code for "italic font style"
+ * &I - also means "italic font style"
+ * &"-,Bold Italic" code for "bold italic font style"
+ * &O - code for "outline style"
+ * &H - code for "shadow style"
+ * </code>
+ */
+class HeaderFooter
+{
+    // Header/footer image location
+    const IMAGE_HEADER_LEFT = 'LH';
+    const IMAGE_HEADER_CENTER = 'CH';
+    const IMAGE_HEADER_RIGHT = 'RH';
+    const IMAGE_FOOTER_LEFT = 'LF';
+    const IMAGE_FOOTER_CENTER = 'CF';
+    const IMAGE_FOOTER_RIGHT = 'RF';
+
+    /**
+     * OddHeader.
+     *
+     * @var string
+     */
+    private $oddHeader = '';
+
+    /**
+     * OddFooter.
+     *
+     * @var string
+     */
+    private $oddFooter = '';
+
+    /**
+     * EvenHeader.
+     *
+     * @var string
+     */
+    private $evenHeader = '';
+
+    /**
+     * EvenFooter.
+     *
+     * @var string
+     */
+    private $evenFooter = '';
+
+    /**
+     * FirstHeader.
+     *
+     * @var string
+     */
+    private $firstHeader = '';
+
+    /**
+     * FirstFooter.
+     *
+     * @var string
+     */
+    private $firstFooter = '';
+
+    /**
+     * Different header for Odd/Even, defaults to false.
+     *
+     * @var bool
+     */
+    private $differentOddEven = false;
+
+    /**
+     * Different header for first page, defaults to false.
+     *
+     * @var bool
+     */
+    private $differentFirst = false;
+
+    /**
+     * Scale with document, defaults to true.
+     *
+     * @var bool
+     */
+    private $scaleWithDocument = true;
+
+    /**
+     * Align with margins, defaults to true.
+     *
+     * @var bool
+     */
+    private $alignWithMargins = true;
+
+    /**
+     * Header/footer images.
+     *
+     * @var HeaderFooterDrawing[]
+     */
+    private $headerFooterImages = [];
+
+    /**
+     * Create a new HeaderFooter.
+     */
+    public function __construct()
+    {
+    }
+
+    /**
+     * Get OddHeader.
+     *
+     * @return string
+     */
+    public function getOddHeader()
+    {
+        return $this->oddHeader;
+    }
+
+    /**
+     * Set OddHeader.
+     *
+     * @param string $pValue
+     *
+     * @return $this
+     */
+    public function setOddHeader($pValue)
+    {
+        $this->oddHeader = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get OddFooter.
+     *
+     * @return string
+     */
+    public function getOddFooter()
+    {
+        return $this->oddFooter;
+    }
+
+    /**
+     * Set OddFooter.
+     *
+     * @param string $pValue
+     *
+     * @return $this
+     */
+    public function setOddFooter($pValue)
+    {
+        $this->oddFooter = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get EvenHeader.
+     *
+     * @return string
+     */
+    public function getEvenHeader()
+    {
+        return $this->evenHeader;
+    }
+
+    /**
+     * Set EvenHeader.
+     *
+     * @param string $pValue
+     *
+     * @return $this
+     */
+    public function setEvenHeader($pValue)
+    {
+        $this->evenHeader = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get EvenFooter.
+     *
+     * @return string
+     */
+    public function getEvenFooter()
+    {
+        return $this->evenFooter;
+    }
+
+    /**
+     * Set EvenFooter.
+     *
+     * @param string $pValue
+     *
+     * @return $this
+     */
+    public function setEvenFooter($pValue)
+    {
+        $this->evenFooter = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get FirstHeader.
+     *
+     * @return string
+     */
+    public function getFirstHeader()
+    {
+        return $this->firstHeader;
+    }
+
+    /**
+     * Set FirstHeader.
+     *
+     * @param string $pValue
+     *
+     * @return $this
+     */
+    public function setFirstHeader($pValue)
+    {
+        $this->firstHeader = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get FirstFooter.
+     *
+     * @return string
+     */
+    public function getFirstFooter()
+    {
+        return $this->firstFooter;
+    }
+
+    /**
+     * Set FirstFooter.
+     *
+     * @param string $pValue
+     *
+     * @return $this
+     */
+    public function setFirstFooter($pValue)
+    {
+        $this->firstFooter = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get DifferentOddEven.
+     *
+     * @return bool
+     */
+    public function getDifferentOddEven()
+    {
+        return $this->differentOddEven;
+    }
+
+    /**
+     * Set DifferentOddEven.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setDifferentOddEven($pValue)
+    {
+        $this->differentOddEven = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get DifferentFirst.
+     *
+     * @return bool
+     */
+    public function getDifferentFirst()
+    {
+        return $this->differentFirst;
+    }
+
+    /**
+     * Set DifferentFirst.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setDifferentFirst($pValue)
+    {
+        $this->differentFirst = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get ScaleWithDocument.
+     *
+     * @return bool
+     */
+    public function getScaleWithDocument()
+    {
+        return $this->scaleWithDocument;
+    }
+
+    /**
+     * Set ScaleWithDocument.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setScaleWithDocument($pValue)
+    {
+        $this->scaleWithDocument = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get AlignWithMargins.
+     *
+     * @return bool
+     */
+    public function getAlignWithMargins()
+    {
+        return $this->alignWithMargins;
+    }
+
+    /**
+     * Set AlignWithMargins.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setAlignWithMargins($pValue)
+    {
+        $this->alignWithMargins = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Add header/footer image.
+     *
+     * @param string $location
+     *
+     * @return $this
+     */
+    public function addImage(HeaderFooterDrawing $image, $location = self::IMAGE_HEADER_LEFT)
+    {
+        $this->headerFooterImages[$location] = $image;
+
+        return $this;
+    }
+
+    /**
+     * Remove header/footer image.
+     *
+     * @param string $location
+     *
+     * @return $this
+     */
+    public function removeImage($location = self::IMAGE_HEADER_LEFT)
+    {
+        if (isset($this->headerFooterImages[$location])) {
+            unset($this->headerFooterImages[$location]);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Set header/footer images.
+     *
+     * @param HeaderFooterDrawing[] $images
+     *
+     * @return $this
+     */
+    public function setImages(array $images)
+    {
+        $this->headerFooterImages = $images;
+
+        return $this;
+    }
+
+    /**
+     * Get header/footer images.
+     *
+     * @return HeaderFooterDrawing[]
+     */
+    public function getImages()
+    {
+        // Sort array
+        $images = [];
+        if (isset($this->headerFooterImages[self::IMAGE_HEADER_LEFT])) {
+            $images[self::IMAGE_HEADER_LEFT] = $this->headerFooterImages[self::IMAGE_HEADER_LEFT];
+        }
+        if (isset($this->headerFooterImages[self::IMAGE_HEADER_CENTER])) {
+            $images[self::IMAGE_HEADER_CENTER] = $this->headerFooterImages[self::IMAGE_HEADER_CENTER];
+        }
+        if (isset($this->headerFooterImages[self::IMAGE_HEADER_RIGHT])) {
+            $images[self::IMAGE_HEADER_RIGHT] = $this->headerFooterImages[self::IMAGE_HEADER_RIGHT];
+        }
+        if (isset($this->headerFooterImages[self::IMAGE_FOOTER_LEFT])) {
+            $images[self::IMAGE_FOOTER_LEFT] = $this->headerFooterImages[self::IMAGE_FOOTER_LEFT];
+        }
+        if (isset($this->headerFooterImages[self::IMAGE_FOOTER_CENTER])) {
+            $images[self::IMAGE_FOOTER_CENTER] = $this->headerFooterImages[self::IMAGE_FOOTER_CENTER];
+        }
+        if (isset($this->headerFooterImages[self::IMAGE_FOOTER_RIGHT])) {
+            $images[self::IMAGE_FOOTER_RIGHT] = $this->headerFooterImages[self::IMAGE_FOOTER_RIGHT];
+        }
+        $this->headerFooterImages = $images;
+
+        return $this->headerFooterImages;
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $vars = get_object_vars($this);
+        foreach ($vars as $key => $value) {
+            if (is_object($value)) {
+                $this->$key = clone $value;
+            } else {
+                $this->$key = $value;
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooterDrawing.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooterDrawing.php
new file mode 100755
index 0000000..b42c732
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooterDrawing.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+class HeaderFooterDrawing extends Drawing
+{
+    /**
+     * Get hash code.
+     *
+     * @return string Hash code
+     */
+    public function getHashCode()
+    {
+        return md5(
+            $this->getPath() .
+            $this->name .
+            $this->offsetX .
+            $this->offsetY .
+            $this->width .
+            $this->height .
+            __CLASS__
+        );
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Iterator.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Iterator.php
new file mode 100755
index 0000000..6cfed37
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Iterator.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+
+class Iterator implements \Iterator
+{
+    /**
+     * Spreadsheet to iterate.
+     *
+     * @var Spreadsheet
+     */
+    private $subject;
+
+    /**
+     * Current iterator position.
+     *
+     * @var int
+     */
+    private $position = 0;
+
+    /**
+     * Create a new worksheet iterator.
+     */
+    public function __construct(Spreadsheet $subject)
+    {
+        // Set subject
+        $this->subject = $subject;
+    }
+
+    /**
+     * Destructor.
+     */
+    public function __destruct()
+    {
+        $this->subject = null;
+    }
+
+    /**
+     * Rewind iterator.
+     */
+    public function rewind(): void
+    {
+        $this->position = 0;
+    }
+
+    /**
+     * Current Worksheet.
+     *
+     * @return Worksheet
+     */
+    public function current()
+    {
+        return $this->subject->getSheet($this->position);
+    }
+
+    /**
+     * Current key.
+     *
+     * @return int
+     */
+    public function key()
+    {
+        return $this->position;
+    }
+
+    /**
+     * Next value.
+     */
+    public function next(): void
+    {
+        ++$this->position;
+    }
+
+    /**
+     * Are there more Worksheet instances available?
+     *
+     * @return bool
+     */
+    public function valid()
+    {
+        return $this->position < $this->subject->getSheetCount() && $this->position >= 0;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php
new file mode 100755
index 0000000..22e0909
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php
@@ -0,0 +1,169 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+class MemoryDrawing extends BaseDrawing
+{
+    // Rendering functions
+    const RENDERING_DEFAULT = 'imagepng';
+    const RENDERING_PNG = 'imagepng';
+    const RENDERING_GIF = 'imagegif';
+    const RENDERING_JPEG = 'imagejpeg';
+
+    // MIME types
+    const MIMETYPE_DEFAULT = 'image/png';
+    const MIMETYPE_PNG = 'image/png';
+    const MIMETYPE_GIF = 'image/gif';
+    const MIMETYPE_JPEG = 'image/jpeg';
+
+    /**
+     * Image resource.
+     *
+     * @var resource
+     */
+    private $imageResource;
+
+    /**
+     * Rendering function.
+     *
+     * @var string
+     */
+    private $renderingFunction;
+
+    /**
+     * Mime type.
+     *
+     * @var string
+     */
+    private $mimeType;
+
+    /**
+     * Unique name.
+     *
+     * @var string
+     */
+    private $uniqueName;
+
+    /**
+     * Create a new MemoryDrawing.
+     */
+    public function __construct()
+    {
+        // Initialise values
+        $this->imageResource = null;
+        $this->renderingFunction = self::RENDERING_DEFAULT;
+        $this->mimeType = self::MIMETYPE_DEFAULT;
+        $this->uniqueName = md5(mt_rand(0, 9999) . time() . mt_rand(0, 9999));
+
+        // Initialize parent
+        parent::__construct();
+    }
+
+    /**
+     * Get image resource.
+     *
+     * @return resource
+     */
+    public function getImageResource()
+    {
+        return $this->imageResource;
+    }
+
+    /**
+     * Set image resource.
+     *
+     * @param resource $value
+     *
+     * @return $this
+     */
+    public function setImageResource($value)
+    {
+        $this->imageResource = $value;
+
+        if ($this->imageResource !== null) {
+            // Get width/height
+            $this->width = imagesx($this->imageResource);
+            $this->height = imagesy($this->imageResource);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get rendering function.
+     *
+     * @return string
+     */
+    public function getRenderingFunction()
+    {
+        return $this->renderingFunction;
+    }
+
+    /**
+     * Set rendering function.
+     *
+     * @param string $value see self::RENDERING_*
+     *
+     * @return $this
+     */
+    public function setRenderingFunction($value)
+    {
+        $this->renderingFunction = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get mime type.
+     *
+     * @return string
+     */
+    public function getMimeType()
+    {
+        return $this->mimeType;
+    }
+
+    /**
+     * Set mime type.
+     *
+     * @param string $value see self::MIMETYPE_*
+     *
+     * @return $this
+     */
+    public function setMimeType($value)
+    {
+        $this->mimeType = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get indexed filename (using image index).
+     *
+     * @return string
+     */
+    public function getIndexedFilename()
+    {
+        $extension = strtolower($this->getMimeType());
+        $extension = explode('/', $extension);
+        $extension = $extension[1];
+
+        return $this->uniqueName . $this->getImageIndex() . '.' . $extension;
+    }
+
+    /**
+     * Get hash code.
+     *
+     * @return string Hash code
+     */
+    public function getHashCode()
+    {
+        return md5(
+            $this->renderingFunction .
+            $this->mimeType .
+            $this->uniqueName .
+            parent::getHashCode() .
+            __CLASS__
+        );
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageMargins.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageMargins.php
new file mode 100755
index 0000000..9ebfb64
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageMargins.php
@@ -0,0 +1,214 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+class PageMargins
+{
+    /**
+     * Left.
+     *
+     * @var float
+     */
+    private $left = 0.7;
+
+    /**
+     * Right.
+     *
+     * @var float
+     */
+    private $right = 0.7;
+
+    /**
+     * Top.
+     *
+     * @var float
+     */
+    private $top = 0.75;
+
+    /**
+     * Bottom.
+     *
+     * @var float
+     */
+    private $bottom = 0.75;
+
+    /**
+     * Header.
+     *
+     * @var float
+     */
+    private $header = 0.3;
+
+    /**
+     * Footer.
+     *
+     * @var float
+     */
+    private $footer = 0.3;
+
+    /**
+     * Create a new PageMargins.
+     */
+    public function __construct()
+    {
+    }
+
+    /**
+     * Get Left.
+     *
+     * @return float
+     */
+    public function getLeft()
+    {
+        return $this->left;
+    }
+
+    /**
+     * Set Left.
+     *
+     * @param float $pValue
+     *
+     * @return $this
+     */
+    public function setLeft($pValue)
+    {
+        $this->left = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Right.
+     *
+     * @return float
+     */
+    public function getRight()
+    {
+        return $this->right;
+    }
+
+    /**
+     * Set Right.
+     *
+     * @param float $pValue
+     *
+     * @return $this
+     */
+    public function setRight($pValue)
+    {
+        $this->right = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Top.
+     *
+     * @return float
+     */
+    public function getTop()
+    {
+        return $this->top;
+    }
+
+    /**
+     * Set Top.
+     *
+     * @param float $pValue
+     *
+     * @return $this
+     */
+    public function setTop($pValue)
+    {
+        $this->top = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Bottom.
+     *
+     * @return float
+     */
+    public function getBottom()
+    {
+        return $this->bottom;
+    }
+
+    /**
+     * Set Bottom.
+     *
+     * @param float $pValue
+     *
+     * @return $this
+     */
+    public function setBottom($pValue)
+    {
+        $this->bottom = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Header.
+     *
+     * @return float
+     */
+    public function getHeader()
+    {
+        return $this->header;
+    }
+
+    /**
+     * Set Header.
+     *
+     * @param float $pValue
+     *
+     * @return $this
+     */
+    public function setHeader($pValue)
+    {
+        $this->header = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Footer.
+     *
+     * @return float
+     */
+    public function getFooter()
+    {
+        return $this->footer;
+    }
+
+    /**
+     * Set Footer.
+     *
+     * @param float $pValue
+     *
+     * @return $this
+     */
+    public function setFooter($pValue)
+    {
+        $this->footer = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $vars = get_object_vars($this);
+        foreach ($vars as $key => $value) {
+            if (is_object($value)) {
+                $this->$key = clone $value;
+            } else {
+                $this->$key = $value;
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageSetup.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageSetup.php
new file mode 100755
index 0000000..f29dbad
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageSetup.php
@@ -0,0 +1,835 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+
+/**
+ * <code>
+ * Paper size taken from Office Open XML Part 4 - Markup Language Reference, page 1988:.
+ *
+ * 1 = Letter paper (8.5 in. by 11 in.)
+ * 2 = Letter small paper (8.5 in. by 11 in.)
+ * 3 = Tabloid paper (11 in. by 17 in.)
+ * 4 = Ledger paper (17 in. by 11 in.)
+ * 5 = Legal paper (8.5 in. by 14 in.)
+ * 6 = Statement paper (5.5 in. by 8.5 in.)
+ * 7 = Executive paper (7.25 in. by 10.5 in.)
+ * 8 = A3 paper (297 mm by 420 mm)
+ * 9 = A4 paper (210 mm by 297 mm)
+ * 10 = A4 small paper (210 mm by 297 mm)
+ * 11 = A5 paper (148 mm by 210 mm)
+ * 12 = B4 paper (250 mm by 353 mm)
+ * 13 = B5 paper (176 mm by 250 mm)
+ * 14 = Folio paper (8.5 in. by 13 in.)
+ * 15 = Quarto paper (215 mm by 275 mm)
+ * 16 = Standard paper (10 in. by 14 in.)
+ * 17 = Standard paper (11 in. by 17 in.)
+ * 18 = Note paper (8.5 in. by 11 in.)
+ * 19 = #9 envelope (3.875 in. by 8.875 in.)
+ * 20 = #10 envelope (4.125 in. by 9.5 in.)
+ * 21 = #11 envelope (4.5 in. by 10.375 in.)
+ * 22 = #12 envelope (4.75 in. by 11 in.)
+ * 23 = #14 envelope (5 in. by 11.5 in.)
+ * 24 = C paper (17 in. by 22 in.)
+ * 25 = D paper (22 in. by 34 in.)
+ * 26 = E paper (34 in. by 44 in.)
+ * 27 = DL envelope (110 mm by 220 mm)
+ * 28 = C5 envelope (162 mm by 229 mm)
+ * 29 = C3 envelope (324 mm by 458 mm)
+ * 30 = C4 envelope (229 mm by 324 mm)
+ * 31 = C6 envelope (114 mm by 162 mm)
+ * 32 = C65 envelope (114 mm by 229 mm)
+ * 33 = B4 envelope (250 mm by 353 mm)
+ * 34 = B5 envelope (176 mm by 250 mm)
+ * 35 = B6 envelope (176 mm by 125 mm)
+ * 36 = Italy envelope (110 mm by 230 mm)
+ * 37 = Monarch envelope (3.875 in. by 7.5 in.).
+ * 38 = 6 3/4 envelope (3.625 in. by 6.5 in.)
+ * 39 = US standard fanfold (14.875 in. by 11 in.)
+ * 40 = German standard fanfold (8.5 in. by 12 in.)
+ * 41 = German legal fanfold (8.5 in. by 13 in.)
+ * 42 = ISO B4 (250 mm by 353 mm)
+ * 43 = Japanese double postcard (200 mm by 148 mm)
+ * 44 = Standard paper (9 in. by 11 in.)
+ * 45 = Standard paper (10 in. by 11 in.)
+ * 46 = Standard paper (15 in. by 11 in.)
+ * 47 = Invite envelope (220 mm by 220 mm)
+ * 50 = Letter extra paper (9.275 in. by 12 in.)
+ * 51 = Legal extra paper (9.275 in. by 15 in.)
+ * 52 = Tabloid extra paper (11.69 in. by 18 in.)
+ * 53 = A4 extra paper (236 mm by 322 mm)
+ * 54 = Letter transverse paper (8.275 in. by 11 in.)
+ * 55 = A4 transverse paper (210 mm by 297 mm)
+ * 56 = Letter extra transverse paper (9.275 in. by 12 in.)
+ * 57 = SuperA/SuperA/A4 paper (227 mm by 356 mm)
+ * 58 = SuperB/SuperB/A3 paper (305 mm by 487 mm)
+ * 59 = Letter plus paper (8.5 in. by 12.69 in.)
+ * 60 = A4 plus paper (210 mm by 330 mm)
+ * 61 = A5 transverse paper (148 mm by 210 mm)
+ * 62 = JIS B5 transverse paper (182 mm by 257 mm)
+ * 63 = A3 extra paper (322 mm by 445 mm)
+ * 64 = A5 extra paper (174 mm by 235 mm)
+ * 65 = ISO B5 extra paper (201 mm by 276 mm)
+ * 66 = A2 paper (420 mm by 594 mm)
+ * 67 = A3 transverse paper (297 mm by 420 mm)
+ * 68 = A3 extra transverse paper (322 mm by 445 mm)
+ * </code>
+ */
+class PageSetup
+{
+    // Paper size
+    const PAPERSIZE_LETTER = 1;
+    const PAPERSIZE_LETTER_SMALL = 2;
+    const PAPERSIZE_TABLOID = 3;
+    const PAPERSIZE_LEDGER = 4;
+    const PAPERSIZE_LEGAL = 5;
+    const PAPERSIZE_STATEMENT = 6;
+    const PAPERSIZE_EXECUTIVE = 7;
+    const PAPERSIZE_A3 = 8;
+    const PAPERSIZE_A4 = 9;
+    const PAPERSIZE_A4_SMALL = 10;
+    const PAPERSIZE_A5 = 11;
+    const PAPERSIZE_B4 = 12;
+    const PAPERSIZE_B5 = 13;
+    const PAPERSIZE_FOLIO = 14;
+    const PAPERSIZE_QUARTO = 15;
+    const PAPERSIZE_STANDARD_1 = 16;
+    const PAPERSIZE_STANDARD_2 = 17;
+    const PAPERSIZE_NOTE = 18;
+    const PAPERSIZE_NO9_ENVELOPE = 19;
+    const PAPERSIZE_NO10_ENVELOPE = 20;
+    const PAPERSIZE_NO11_ENVELOPE = 21;
+    const PAPERSIZE_NO12_ENVELOPE = 22;
+    const PAPERSIZE_NO14_ENVELOPE = 23;
+    const PAPERSIZE_C = 24;
+    const PAPERSIZE_D = 25;
+    const PAPERSIZE_E = 26;
+    const PAPERSIZE_DL_ENVELOPE = 27;
+    const PAPERSIZE_C5_ENVELOPE = 28;
+    const PAPERSIZE_C3_ENVELOPE = 29;
+    const PAPERSIZE_C4_ENVELOPE = 30;
+    const PAPERSIZE_C6_ENVELOPE = 31;
+    const PAPERSIZE_C65_ENVELOPE = 32;
+    const PAPERSIZE_B4_ENVELOPE = 33;
+    const PAPERSIZE_B5_ENVELOPE = 34;
+    const PAPERSIZE_B6_ENVELOPE = 35;
+    const PAPERSIZE_ITALY_ENVELOPE = 36;
+    const PAPERSIZE_MONARCH_ENVELOPE = 37;
+    const PAPERSIZE_6_3_4_ENVELOPE = 38;
+    const PAPERSIZE_US_STANDARD_FANFOLD = 39;
+    const PAPERSIZE_GERMAN_STANDARD_FANFOLD = 40;
+    const PAPERSIZE_GERMAN_LEGAL_FANFOLD = 41;
+    const PAPERSIZE_ISO_B4 = 42;
+    const PAPERSIZE_JAPANESE_DOUBLE_POSTCARD = 43;
+    const PAPERSIZE_STANDARD_PAPER_1 = 44;
+    const PAPERSIZE_STANDARD_PAPER_2 = 45;
+    const PAPERSIZE_STANDARD_PAPER_3 = 46;
+    const PAPERSIZE_INVITE_ENVELOPE = 47;
+    const PAPERSIZE_LETTER_EXTRA_PAPER = 48;
+    const PAPERSIZE_LEGAL_EXTRA_PAPER = 49;
+    const PAPERSIZE_TABLOID_EXTRA_PAPER = 50;
+    const PAPERSIZE_A4_EXTRA_PAPER = 51;
+    const PAPERSIZE_LETTER_TRANSVERSE_PAPER = 52;
+    const PAPERSIZE_A4_TRANSVERSE_PAPER = 53;
+    const PAPERSIZE_LETTER_EXTRA_TRANSVERSE_PAPER = 54;
+    const PAPERSIZE_SUPERA_SUPERA_A4_PAPER = 55;
+    const PAPERSIZE_SUPERB_SUPERB_A3_PAPER = 56;
+    const PAPERSIZE_LETTER_PLUS_PAPER = 57;
+    const PAPERSIZE_A4_PLUS_PAPER = 58;
+    const PAPERSIZE_A5_TRANSVERSE_PAPER = 59;
+    const PAPERSIZE_JIS_B5_TRANSVERSE_PAPER = 60;
+    const PAPERSIZE_A3_EXTRA_PAPER = 61;
+    const PAPERSIZE_A5_EXTRA_PAPER = 62;
+    const PAPERSIZE_ISO_B5_EXTRA_PAPER = 63;
+    const PAPERSIZE_A2_PAPER = 64;
+    const PAPERSIZE_A3_TRANSVERSE_PAPER = 65;
+    const PAPERSIZE_A3_EXTRA_TRANSVERSE_PAPER = 66;
+
+    // Page orientation
+    const ORIENTATION_DEFAULT = 'default';
+    const ORIENTATION_LANDSCAPE = 'landscape';
+    const ORIENTATION_PORTRAIT = 'portrait';
+
+    // Print Range Set Method
+    const SETPRINTRANGE_OVERWRITE = 'O';
+    const SETPRINTRANGE_INSERT = 'I';
+
+    /**
+     * Paper size.
+     *
+     * @var int
+     */
+    private $paperSize = self::PAPERSIZE_LETTER;
+
+    /**
+     * Orientation.
+     *
+     * @var string
+     */
+    private $orientation = self::ORIENTATION_DEFAULT;
+
+    /**
+     * Scale (Print Scale).
+     *
+     * Print scaling. Valid values range from 10 to 400
+     * This setting is overridden when fitToWidth and/or fitToHeight are in use
+     *
+     * @var null|int
+     */
+    private $scale = 100;
+
+    /**
+     * Fit To Page
+     * Whether scale or fitToWith / fitToHeight applies.
+     *
+     * @var bool
+     */
+    private $fitToPage = false;
+
+    /**
+     * Fit To Height
+     * Number of vertical pages to fit on.
+     *
+     * @var null|int
+     */
+    private $fitToHeight = 1;
+
+    /**
+     * Fit To Width
+     * Number of horizontal pages to fit on.
+     *
+     * @var null|int
+     */
+    private $fitToWidth = 1;
+
+    /**
+     * Columns to repeat at left.
+     *
+     * @var array Containing start column and end column, empty array if option unset
+     */
+    private $columnsToRepeatAtLeft = ['', ''];
+
+    /**
+     * Rows to repeat at top.
+     *
+     * @var array Containing start row number and end row number, empty array if option unset
+     */
+    private $rowsToRepeatAtTop = [0, 0];
+
+    /**
+     * Center page horizontally.
+     *
+     * @var bool
+     */
+    private $horizontalCentered = false;
+
+    /**
+     * Center page vertically.
+     *
+     * @var bool
+     */
+    private $verticalCentered = false;
+
+    /**
+     * Print area.
+     *
+     * @var string
+     */
+    private $printArea;
+
+    /**
+     * First page number.
+     *
+     * @var int
+     */
+    private $firstPageNumber;
+
+    /**
+     * Create a new PageSetup.
+     */
+    public function __construct()
+    {
+    }
+
+    /**
+     * Get Paper Size.
+     *
+     * @return int
+     */
+    public function getPaperSize()
+    {
+        return $this->paperSize;
+    }
+
+    /**
+     * Set Paper Size.
+     *
+     * @param int $pValue see self::PAPERSIZE_*
+     *
+     * @return $this
+     */
+    public function setPaperSize($pValue)
+    {
+        $this->paperSize = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Orientation.
+     *
+     * @return string
+     */
+    public function getOrientation()
+    {
+        return $this->orientation;
+    }
+
+    /**
+     * Set Orientation.
+     *
+     * @param string $pValue see self::ORIENTATION_*
+     *
+     * @return $this
+     */
+    public function setOrientation($pValue)
+    {
+        $this->orientation = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Scale.
+     *
+     * @return null|int
+     */
+    public function getScale()
+    {
+        return $this->scale;
+    }
+
+    /**
+     * Set Scale.
+     * Print scaling. Valid values range from 10 to 400
+     * This setting is overridden when fitToWidth and/or fitToHeight are in use.
+     *
+     * @param null|int $pValue
+     * @param bool $pUpdate Update fitToPage so scaling applies rather than fitToHeight / fitToWidth
+     *
+     * @return $this
+     */
+    public function setScale($pValue, $pUpdate = true)
+    {
+        // Microsoft Office Excel 2007 only allows setting a scale between 10 and 400 via the user interface,
+        // but it is apparently still able to handle any scale >= 0, where 0 results in 100
+        if (($pValue >= 0) || $pValue === null) {
+            $this->scale = $pValue;
+            if ($pUpdate) {
+                $this->fitToPage = false;
+            }
+        } else {
+            throw new PhpSpreadsheetException('Scale must not be negative');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Fit To Page.
+     *
+     * @return bool
+     */
+    public function getFitToPage()
+    {
+        return $this->fitToPage;
+    }
+
+    /**
+     * Set Fit To Page.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setFitToPage($pValue)
+    {
+        $this->fitToPage = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Fit To Height.
+     *
+     * @return null|int
+     */
+    public function getFitToHeight()
+    {
+        return $this->fitToHeight;
+    }
+
+    /**
+     * Set Fit To Height.
+     *
+     * @param null|int $pValue
+     * @param bool $pUpdate Update fitToPage so it applies rather than scaling
+     *
+     * @return $this
+     */
+    public function setFitToHeight($pValue, $pUpdate = true)
+    {
+        $this->fitToHeight = $pValue;
+        if ($pUpdate) {
+            $this->fitToPage = true;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get Fit To Width.
+     *
+     * @return null|int
+     */
+    public function getFitToWidth()
+    {
+        return $this->fitToWidth;
+    }
+
+    /**
+     * Set Fit To Width.
+     *
+     * @param null|int $pValue
+     * @param bool $pUpdate Update fitToPage so it applies rather than scaling
+     *
+     * @return $this
+     */
+    public function setFitToWidth($pValue, $pUpdate = true)
+    {
+        $this->fitToWidth = $pValue;
+        if ($pUpdate) {
+            $this->fitToPage = true;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Is Columns to repeat at left set?
+     *
+     * @return bool
+     */
+    public function isColumnsToRepeatAtLeftSet()
+    {
+        if (is_array($this->columnsToRepeatAtLeft)) {
+            if ($this->columnsToRepeatAtLeft[0] != '' && $this->columnsToRepeatAtLeft[1] != '') {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Get Columns to repeat at left.
+     *
+     * @return array Containing start column and end column, empty array if option unset
+     */
+    public function getColumnsToRepeatAtLeft()
+    {
+        return $this->columnsToRepeatAtLeft;
+    }
+
+    /**
+     * Set Columns to repeat at left.
+     *
+     * @param array $pValue Containing start column and end column, empty array if option unset
+     *
+     * @return $this
+     */
+    public function setColumnsToRepeatAtLeft(array $pValue)
+    {
+        $this->columnsToRepeatAtLeft = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Set Columns to repeat at left by start and end.
+     *
+     * @param string $pStart eg: 'A'
+     * @param string $pEnd eg: 'B'
+     *
+     * @return $this
+     */
+    public function setColumnsToRepeatAtLeftByStartAndEnd($pStart, $pEnd)
+    {
+        $this->columnsToRepeatAtLeft = [$pStart, $pEnd];
+
+        return $this;
+    }
+
+    /**
+     * Is Rows to repeat at top set?
+     *
+     * @return bool
+     */
+    public function isRowsToRepeatAtTopSet()
+    {
+        if (is_array($this->rowsToRepeatAtTop)) {
+            if ($this->rowsToRepeatAtTop[0] != 0 && $this->rowsToRepeatAtTop[1] != 0) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Get Rows to repeat at top.
+     *
+     * @return array Containing start column and end column, empty array if option unset
+     */
+    public function getRowsToRepeatAtTop()
+    {
+        return $this->rowsToRepeatAtTop;
+    }
+
+    /**
+     * Set Rows to repeat at top.
+     *
+     * @param array $pValue Containing start column and end column, empty array if option unset
+     *
+     * @return $this
+     */
+    public function setRowsToRepeatAtTop(array $pValue)
+    {
+        $this->rowsToRepeatAtTop = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Set Rows to repeat at top by start and end.
+     *
+     * @param int $pStart eg: 1
+     * @param int $pEnd eg: 1
+     *
+     * @return $this
+     */
+    public function setRowsToRepeatAtTopByStartAndEnd($pStart, $pEnd)
+    {
+        $this->rowsToRepeatAtTop = [$pStart, $pEnd];
+
+        return $this;
+    }
+
+    /**
+     * Get center page horizontally.
+     *
+     * @return bool
+     */
+    public function getHorizontalCentered()
+    {
+        return $this->horizontalCentered;
+    }
+
+    /**
+     * Set center page horizontally.
+     *
+     * @param bool $value
+     *
+     * @return $this
+     */
+    public function setHorizontalCentered($value)
+    {
+        $this->horizontalCentered = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get center page vertically.
+     *
+     * @return bool
+     */
+    public function getVerticalCentered()
+    {
+        return $this->verticalCentered;
+    }
+
+    /**
+     * Set center page vertically.
+     *
+     * @param bool $value
+     *
+     * @return $this
+     */
+    public function setVerticalCentered($value)
+    {
+        $this->verticalCentered = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get print area.
+     *
+     * @param int $index Identifier for a specific print area range if several ranges have been set
+     *                            Default behaviour, or a index value of 0, will return all ranges as a comma-separated string
+     *                            Otherwise, the specific range identified by the value of $index will be returned
+     *                            Print areas are numbered from 1
+     *
+     * @return string
+     */
+    public function getPrintArea($index = 0)
+    {
+        if ($index == 0) {
+            return $this->printArea;
+        }
+        $printAreas = explode(',', $this->printArea);
+        if (isset($printAreas[$index - 1])) {
+            return $printAreas[$index - 1];
+        }
+
+        throw new PhpSpreadsheetException('Requested Print Area does not exist');
+    }
+
+    /**
+     * Is print area set?
+     *
+     * @param int $index Identifier for a specific print area range if several ranges have been set
+     *                            Default behaviour, or an index value of 0, will identify whether any print range is set
+     *                            Otherwise, existence of the range identified by the value of $index will be returned
+     *                            Print areas are numbered from 1
+     *
+     * @return bool
+     */
+    public function isPrintAreaSet($index = 0)
+    {
+        if ($index == 0) {
+            return $this->printArea !== null;
+        }
+        $printAreas = explode(',', $this->printArea);
+
+        return isset($printAreas[$index - 1]);
+    }
+
+    /**
+     * Clear a print area.
+     *
+     * @param int $index Identifier for a specific print area range if several ranges have been set
+     *                            Default behaviour, or an index value of 0, will clear all print ranges that are set
+     *                            Otherwise, the range identified by the value of $index will be removed from the series
+     *                            Print areas are numbered from 1
+     *
+     * @return $this
+     */
+    public function clearPrintArea($index = 0)
+    {
+        if ($index == 0) {
+            $this->printArea = null;
+        } else {
+            $printAreas = explode(',', $this->printArea);
+            if (isset($printAreas[$index - 1])) {
+                unset($printAreas[$index - 1]);
+                $this->printArea = implode(',', $printAreas);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Set print area. e.g. 'A1:D10' or 'A1:D10,G5:M20'.
+     *
+     * @param string $value
+     * @param int $index Identifier for a specific print area range allowing several ranges to be set
+     *                            When the method is "O"verwrite, then a positive integer index will overwrite that indexed
+     *                                entry in the print areas list; a negative index value will identify which entry to
+     *                                overwrite working bacward through the print area to the list, with the last entry as -1.
+     *                                Specifying an index value of 0, will overwrite <b>all</b> existing print ranges.
+     *                            When the method is "I"nsert, then a positive index will insert after that indexed entry in
+     *                                the print areas list, while a negative index will insert before the indexed entry.
+     *                                Specifying an index value of 0, will always append the new print range at the end of the
+     *                                list.
+     *                            Print areas are numbered from 1
+     * @param string $method Determines the method used when setting multiple print areas
+     *                            Default behaviour, or the "O" method, overwrites existing print area
+     *                            The "I" method, inserts the new print area before any specified index, or at the end of the list
+     *
+     * @return $this
+     */
+    public function setPrintArea($value, $index = 0, $method = self::SETPRINTRANGE_OVERWRITE)
+    {
+        if (strpos($value, '!') !== false) {
+            throw new PhpSpreadsheetException('Cell coordinate must not specify a worksheet.');
+        } elseif (strpos($value, ':') === false) {
+            throw new PhpSpreadsheetException('Cell coordinate must be a range of cells.');
+        } elseif (strpos($value, '$') !== false) {
+            throw new PhpSpreadsheetException('Cell coordinate must not be absolute.');
+        }
+        $value = strtoupper($value);
+
+        if ($method == self::SETPRINTRANGE_OVERWRITE) {
+            if ($index == 0) {
+                $this->printArea = $value;
+            } else {
+                $printAreas = explode(',', $this->printArea);
+                if ($index < 0) {
+                    $index = count($printAreas) - abs($index) + 1;
+                }
+                if (($index <= 0) || ($index > count($printAreas))) {
+                    throw new PhpSpreadsheetException('Invalid index for setting print range.');
+                }
+                $printAreas[$index - 1] = $value;
+                $this->printArea = implode(',', $printAreas);
+            }
+        } elseif ($method == self::SETPRINTRANGE_INSERT) {
+            if ($index == 0) {
+                $this->printArea .= ($this->printArea == '') ? $value : ',' . $value;
+            } else {
+                $printAreas = explode(',', $this->printArea);
+                if ($index < 0) {
+                    $index = abs($index) - 1;
+                }
+                if ($index > count($printAreas)) {
+                    throw new PhpSpreadsheetException('Invalid index for setting print range.');
+                }
+                $printAreas = array_merge(array_slice($printAreas, 0, $index), [$value], array_slice($printAreas, $index));
+                $this->printArea = implode(',', $printAreas);
+            }
+        } else {
+            throw new PhpSpreadsheetException('Invalid method for setting print range.');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Add a new print area (e.g. 'A1:D10' or 'A1:D10,G5:M20') to the list of print areas.
+     *
+     * @param string $value
+     * @param int $index Identifier for a specific print area range allowing several ranges to be set
+     *                            A positive index will insert after that indexed entry in the print areas list, while a
+     *                                negative index will insert before the indexed entry.
+     *                                Specifying an index value of 0, will always append the new print range at the end of the
+     *                                list.
+     *                            Print areas are numbered from 1
+     *
+     * @return $this
+     */
+    public function addPrintArea($value, $index = -1)
+    {
+        return $this->setPrintArea($value, $index, self::SETPRINTRANGE_INSERT);
+    }
+
+    /**
+     * Set print area.
+     *
+     * @param int $column1 Column 1
+     * @param int $row1 Row 1
+     * @param int $column2 Column 2
+     * @param int $row2 Row 2
+     * @param int $index Identifier for a specific print area range allowing several ranges to be set
+     *                                When the method is "O"verwrite, then a positive integer index will overwrite that indexed
+     *                                    entry in the print areas list; a negative index value will identify which entry to
+     *                                    overwrite working backward through the print area to the list, with the last entry as -1.
+     *                                    Specifying an index value of 0, will overwrite <b>all</b> existing print ranges.
+     *                                When the method is "I"nsert, then a positive index will insert after that indexed entry in
+     *                                    the print areas list, while a negative index will insert before the indexed entry.
+     *                                    Specifying an index value of 0, will always append the new print range at the end of the
+     *                                    list.
+     *                                Print areas are numbered from 1
+     * @param string $method Determines the method used when setting multiple print areas
+     *                                Default behaviour, or the "O" method, overwrites existing print area
+     *                                The "I" method, inserts the new print area before any specified index, or at the end of the list
+     *
+     * @return $this
+     */
+    public function setPrintAreaByColumnAndRow($column1, $row1, $column2, $row2, $index = 0, $method = self::SETPRINTRANGE_OVERWRITE)
+    {
+        return $this->setPrintArea(
+            Coordinate::stringFromColumnIndex($column1) . $row1 . ':' . Coordinate::stringFromColumnIndex($column2) . $row2,
+            $index,
+            $method
+        );
+    }
+
+    /**
+     * Add a new print area to the list of print areas.
+     *
+     * @param int $column1 Start Column for the print area
+     * @param int $row1 Start Row for the print area
+     * @param int $column2 End Column for the print area
+     * @param int $row2 End Row for the print area
+     * @param int $index Identifier for a specific print area range allowing several ranges to be set
+     *                                A positive index will insert after that indexed entry in the print areas list, while a
+     *                                    negative index will insert before the indexed entry.
+     *                                    Specifying an index value of 0, will always append the new print range at the end of the
+     *                                    list.
+     *                                Print areas are numbered from 1
+     *
+     * @return $this
+     */
+    public function addPrintAreaByColumnAndRow($column1, $row1, $column2, $row2, $index = -1)
+    {
+        return $this->setPrintArea(
+            Coordinate::stringFromColumnIndex($column1) . $row1 . ':' . Coordinate::stringFromColumnIndex($column2) . $row2,
+            $index,
+            self::SETPRINTRANGE_INSERT
+        );
+    }
+
+    /**
+     * Get first page number.
+     *
+     * @return int
+     */
+    public function getFirstPageNumber()
+    {
+        return $this->firstPageNumber;
+    }
+
+    /**
+     * Set first page number.
+     *
+     * @param int $value
+     *
+     * @return $this
+     */
+    public function setFirstPageNumber($value)
+    {
+        $this->firstPageNumber = $value;
+
+        return $this;
+    }
+
+    /**
+     * Reset first page number.
+     *
+     * @return $this
+     */
+    public function resetFirstPageNumber()
+    {
+        return $this->setFirstPageNumber(null);
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $vars = get_object_vars($this);
+        foreach ($vars as $key => $value) {
+            if (is_object($value)) {
+                $this->$key = clone $value;
+            } else {
+                $this->$key = $value;
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Protection.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Protection.php
new file mode 100755
index 0000000..81abc0b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Protection.php
@@ -0,0 +1,698 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+use PhpOffice\PhpSpreadsheet\Shared\PasswordHasher;
+
+class Protection
+{
+    const ALGORITHM_MD2 = 'MD2';
+    const ALGORITHM_MD4 = 'MD4';
+    const ALGORITHM_MD5 = 'MD5';
+    const ALGORITHM_SHA_1 = 'SHA-1';
+    const ALGORITHM_SHA_256 = 'SHA-256';
+    const ALGORITHM_SHA_384 = 'SHA-384';
+    const ALGORITHM_SHA_512 = 'SHA-512';
+    const ALGORITHM_RIPEMD_128 = 'RIPEMD-128';
+    const ALGORITHM_RIPEMD_160 = 'RIPEMD-160';
+    const ALGORITHM_WHIRLPOOL = 'WHIRLPOOL';
+
+    /**
+     * Sheet.
+     *
+     * @var bool
+     */
+    private $sheet = false;
+
+    /**
+     * Objects.
+     *
+     * @var bool
+     */
+    private $objects = false;
+
+    /**
+     * Scenarios.
+     *
+     * @var bool
+     */
+    private $scenarios = false;
+
+    /**
+     * Format cells.
+     *
+     * @var bool
+     */
+    private $formatCells = false;
+
+    /**
+     * Format columns.
+     *
+     * @var bool
+     */
+    private $formatColumns = false;
+
+    /**
+     * Format rows.
+     *
+     * @var bool
+     */
+    private $formatRows = false;
+
+    /**
+     * Insert columns.
+     *
+     * @var bool
+     */
+    private $insertColumns = false;
+
+    /**
+     * Insert rows.
+     *
+     * @var bool
+     */
+    private $insertRows = false;
+
+    /**
+     * Insert hyperlinks.
+     *
+     * @var bool
+     */
+    private $insertHyperlinks = false;
+
+    /**
+     * Delete columns.
+     *
+     * @var bool
+     */
+    private $deleteColumns = false;
+
+    /**
+     * Delete rows.
+     *
+     * @var bool
+     */
+    private $deleteRows = false;
+
+    /**
+     * Select locked cells.
+     *
+     * @var bool
+     */
+    private $selectLockedCells = false;
+
+    /**
+     * Sort.
+     *
+     * @var bool
+     */
+    private $sort = false;
+
+    /**
+     * AutoFilter.
+     *
+     * @var bool
+     */
+    private $autoFilter = false;
+
+    /**
+     * Pivot tables.
+     *
+     * @var bool
+     */
+    private $pivotTables = false;
+
+    /**
+     * Select unlocked cells.
+     *
+     * @var bool
+     */
+    private $selectUnlockedCells = false;
+
+    /**
+     * Hashed password.
+     *
+     * @var string
+     */
+    private $password = '';
+
+    /**
+     * Algorithm name.
+     *
+     * @var string
+     */
+    private $algorithm = '';
+
+    /**
+     * Hash value.
+     *
+     * @var string
+     */
+    private $hash = '';
+
+    /**
+     * Salt value.
+     *
+     * @var string
+     */
+    private $salt = '';
+
+    /**
+     * Spin count.
+     *
+     * @var int
+     */
+    private $spinCount = 10000;
+
+    /**
+     * Create a new Protection.
+     */
+    public function __construct()
+    {
+    }
+
+    /**
+     * Is some sort of protection enabled?
+     *
+     * @return bool
+     */
+    public function isProtectionEnabled()
+    {
+        return $this->sheet ||
+            $this->objects ||
+            $this->scenarios ||
+            $this->formatCells ||
+            $this->formatColumns ||
+            $this->formatRows ||
+            $this->insertColumns ||
+            $this->insertRows ||
+            $this->insertHyperlinks ||
+            $this->deleteColumns ||
+            $this->deleteRows ||
+            $this->selectLockedCells ||
+            $this->sort ||
+            $this->autoFilter ||
+            $this->pivotTables ||
+            $this->selectUnlockedCells;
+    }
+
+    /**
+     * Get Sheet.
+     *
+     * @return bool
+     */
+    public function getSheet()
+    {
+        return $this->sheet;
+    }
+
+    /**
+     * Set Sheet.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setSheet($pValue)
+    {
+        $this->sheet = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Objects.
+     *
+     * @return bool
+     */
+    public function getObjects()
+    {
+        return $this->objects;
+    }
+
+    /**
+     * Set Objects.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setObjects($pValue)
+    {
+        $this->objects = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Scenarios.
+     *
+     * @return bool
+     */
+    public function getScenarios()
+    {
+        return $this->scenarios;
+    }
+
+    /**
+     * Set Scenarios.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setScenarios($pValue)
+    {
+        $this->scenarios = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get FormatCells.
+     *
+     * @return bool
+     */
+    public function getFormatCells()
+    {
+        return $this->formatCells;
+    }
+
+    /**
+     * Set FormatCells.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setFormatCells($pValue)
+    {
+        $this->formatCells = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get FormatColumns.
+     *
+     * @return bool
+     */
+    public function getFormatColumns()
+    {
+        return $this->formatColumns;
+    }
+
+    /**
+     * Set FormatColumns.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setFormatColumns($pValue)
+    {
+        $this->formatColumns = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get FormatRows.
+     *
+     * @return bool
+     */
+    public function getFormatRows()
+    {
+        return $this->formatRows;
+    }
+
+    /**
+     * Set FormatRows.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setFormatRows($pValue)
+    {
+        $this->formatRows = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get InsertColumns.
+     *
+     * @return bool
+     */
+    public function getInsertColumns()
+    {
+        return $this->insertColumns;
+    }
+
+    /**
+     * Set InsertColumns.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setInsertColumns($pValue)
+    {
+        $this->insertColumns = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get InsertRows.
+     *
+     * @return bool
+     */
+    public function getInsertRows()
+    {
+        return $this->insertRows;
+    }
+
+    /**
+     * Set InsertRows.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setInsertRows($pValue)
+    {
+        $this->insertRows = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get InsertHyperlinks.
+     *
+     * @return bool
+     */
+    public function getInsertHyperlinks()
+    {
+        return $this->insertHyperlinks;
+    }
+
+    /**
+     * Set InsertHyperlinks.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setInsertHyperlinks($pValue)
+    {
+        $this->insertHyperlinks = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get DeleteColumns.
+     *
+     * @return bool
+     */
+    public function getDeleteColumns()
+    {
+        return $this->deleteColumns;
+    }
+
+    /**
+     * Set DeleteColumns.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setDeleteColumns($pValue)
+    {
+        $this->deleteColumns = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get DeleteRows.
+     *
+     * @return bool
+     */
+    public function getDeleteRows()
+    {
+        return $this->deleteRows;
+    }
+
+    /**
+     * Set DeleteRows.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setDeleteRows($pValue)
+    {
+        $this->deleteRows = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get SelectLockedCells.
+     *
+     * @return bool
+     */
+    public function getSelectLockedCells()
+    {
+        return $this->selectLockedCells;
+    }
+
+    /**
+     * Set SelectLockedCells.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setSelectLockedCells($pValue)
+    {
+        $this->selectLockedCells = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Sort.
+     *
+     * @return bool
+     */
+    public function getSort()
+    {
+        return $this->sort;
+    }
+
+    /**
+     * Set Sort.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setSort($pValue)
+    {
+        $this->sort = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get AutoFilter.
+     *
+     * @return bool
+     */
+    public function getAutoFilter()
+    {
+        return $this->autoFilter;
+    }
+
+    /**
+     * Set AutoFilter.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setAutoFilter($pValue)
+    {
+        $this->autoFilter = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get PivotTables.
+     *
+     * @return bool
+     */
+    public function getPivotTables()
+    {
+        return $this->pivotTables;
+    }
+
+    /**
+     * Set PivotTables.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setPivotTables($pValue)
+    {
+        $this->pivotTables = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get SelectUnlockedCells.
+     *
+     * @return bool
+     */
+    public function getSelectUnlockedCells()
+    {
+        return $this->selectUnlockedCells;
+    }
+
+    /**
+     * Set SelectUnlockedCells.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setSelectUnlockedCells($pValue)
+    {
+        $this->selectUnlockedCells = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get hashed password.
+     *
+     * @return string
+     */
+    public function getPassword()
+    {
+        return $this->password;
+    }
+
+    /**
+     * Set Password.
+     *
+     * @param string $pValue
+     * @param bool $pAlreadyHashed If the password has already been hashed, set this to true
+     *
+     * @return $this
+     */
+    public function setPassword($pValue, $pAlreadyHashed = false)
+    {
+        if (!$pAlreadyHashed) {
+            $salt = $this->generateSalt();
+            $this->setSalt($salt);
+            $pValue = PasswordHasher::hashPassword($pValue, $this->getAlgorithm(), $this->getSalt(), $this->getSpinCount());
+        }
+
+        $this->password = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Create a pseudorandom string.
+     */
+    private function generateSalt(): string
+    {
+        return base64_encode(random_bytes(16));
+    }
+
+    /**
+     * Get algorithm name.
+     */
+    public function getAlgorithm(): string
+    {
+        return $this->algorithm;
+    }
+
+    /**
+     * Set algorithm name.
+     */
+    public function setAlgorithm(string $algorithm): void
+    {
+        $this->algorithm = $algorithm;
+    }
+
+    /**
+     * Get salt value.
+     */
+    public function getSalt(): string
+    {
+        return $this->salt;
+    }
+
+    /**
+     * Set salt value.
+     */
+    public function setSalt(string $salt): void
+    {
+        $this->salt = $salt;
+    }
+
+    /**
+     * Get spin count.
+     */
+    public function getSpinCount(): int
+    {
+        return $this->spinCount;
+    }
+
+    /**
+     * Set spin count.
+     */
+    public function setSpinCount(int $spinCount): void
+    {
+        $this->spinCount = $spinCount;
+    }
+
+    /**
+     * Verify that the given non-hashed password can "unlock" the protection.
+     */
+    public function verify(string $password): bool
+    {
+        if (!$this->isProtectionEnabled()) {
+            return true;
+        }
+
+        $hash = PasswordHasher::hashPassword($password, $this->getAlgorithm(), $this->getSalt(), $this->getSpinCount());
+
+        return $this->getPassword() === $hash;
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $vars = get_object_vars($this);
+        foreach ($vars as $key => $value) {
+            if (is_object($value)) {
+                $this->$key = clone $value;
+            } else {
+                $this->$key = $value;
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Row.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Row.php
new file mode 100755
index 0000000..4f48a34
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Row.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+class Row
+{
+    /**
+     * \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet.
+     *
+     * @var Worksheet
+     */
+    private $worksheet;
+
+    /**
+     * Row index.
+     *
+     * @var int
+     */
+    private $rowIndex = 0;
+
+    /**
+     * Create a new row.
+     *
+     * @param Worksheet $worksheet
+     * @param int $rowIndex
+     */
+    public function __construct(?Worksheet $worksheet = null, $rowIndex = 1)
+    {
+        // Set parent and row index
+        $this->worksheet = $worksheet;
+        $this->rowIndex = $rowIndex;
+    }
+
+    /**
+     * Destructor.
+     */
+    public function __destruct()
+    {
+        $this->worksheet = null;
+    }
+
+    /**
+     * Get row index.
+     *
+     * @return int
+     */
+    public function getRowIndex()
+    {
+        return $this->rowIndex;
+    }
+
+    /**
+     * Get cell iterator.
+     *
+     * @param string $startColumn The column address at which to start iterating
+     * @param string $endColumn Optionally, the column address at which to stop iterating
+     *
+     * @return RowCellIterator
+     */
+    public function getCellIterator($startColumn = 'A', $endColumn = null)
+    {
+        return new RowCellIterator($this->worksheet, $this->rowIndex, $startColumn, $endColumn);
+    }
+
+    /**
+     * Returns bound worksheet.
+     *
+     * @return Worksheet
+     */
+    public function getWorksheet()
+    {
+        return $this->worksheet;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowCellIterator.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowCellIterator.php
new file mode 100755
index 0000000..f5576dc
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowCellIterator.php
@@ -0,0 +1,195 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+
+class RowCellIterator extends CellIterator
+{
+    /**
+     * Current iterator position.
+     *
+     * @var int
+     */
+    private $currentColumnIndex;
+
+    /**
+     * Row index.
+     *
+     * @var int
+     */
+    private $rowIndex = 1;
+
+    /**
+     * Start position.
+     *
+     * @var int
+     */
+    private $startColumnIndex = 1;
+
+    /**
+     * End position.
+     *
+     * @var int
+     */
+    private $endColumnIndex = 1;
+
+    /**
+     * Create a new column iterator.
+     *
+     * @param Worksheet $worksheet The worksheet to iterate over
+     * @param int $rowIndex The row that we want to iterate
+     * @param string $startColumn The column address at which to start iterating
+     * @param string $endColumn Optionally, the column address at which to stop iterating
+     */
+    public function __construct(?Worksheet $worksheet = null, $rowIndex = 1, $startColumn = 'A', $endColumn = null)
+    {
+        // Set subject and row index
+        $this->worksheet = $worksheet;
+        $this->rowIndex = $rowIndex;
+        $this->resetEnd($endColumn);
+        $this->resetStart($startColumn);
+    }
+
+    /**
+     * (Re)Set the start column and the current column pointer.
+     *
+     * @param string $startColumn The column address at which to start iterating
+     *
+     * @return $this
+     */
+    public function resetStart($startColumn = 'A')
+    {
+        $this->startColumnIndex = Coordinate::columnIndexFromString($startColumn);
+        $this->adjustForExistingOnlyRange();
+        $this->seek(Coordinate::stringFromColumnIndex($this->startColumnIndex));
+
+        return $this;
+    }
+
+    /**
+     * (Re)Set the end column.
+     *
+     * @param string $endColumn The column address at which to stop iterating
+     *
+     * @return $this
+     */
+    public function resetEnd($endColumn = null)
+    {
+        $endColumn = $endColumn ? $endColumn : $this->worksheet->getHighestColumn();
+        $this->endColumnIndex = Coordinate::columnIndexFromString($endColumn);
+        $this->adjustForExistingOnlyRange();
+
+        return $this;
+    }
+
+    /**
+     * Set the column pointer to the selected column.
+     *
+     * @param string $column The column address to set the current pointer at
+     *
+     * @return $this
+     */
+    public function seek($column = 'A')
+    {
+        $column = Coordinate::columnIndexFromString($column);
+        if (($column < $this->startColumnIndex) || ($column > $this->endColumnIndex)) {
+            throw new PhpSpreadsheetException("Column $column is out of range ({$this->startColumnIndex} - {$this->endColumnIndex})");
+        } elseif ($this->onlyExistingCells && !($this->worksheet->cellExistsByColumnAndRow($column, $this->rowIndex))) {
+            throw new PhpSpreadsheetException('In "IterateOnlyExistingCells" mode and Cell does not exist');
+        }
+        $this->currentColumnIndex = $column;
+
+        return $this;
+    }
+
+    /**
+     * Rewind the iterator to the starting column.
+     */
+    public function rewind(): void
+    {
+        $this->currentColumnIndex = $this->startColumnIndex;
+    }
+
+    /**
+     * Return the current cell in this worksheet row.
+     *
+     * @return \PhpOffice\PhpSpreadsheet\Cell\Cell
+     */
+    public function current()
+    {
+        return $this->worksheet->getCellByColumnAndRow($this->currentColumnIndex, $this->rowIndex);
+    }
+
+    /**
+     * Return the current iterator key.
+     *
+     * @return string
+     */
+    public function key()
+    {
+        return Coordinate::stringFromColumnIndex($this->currentColumnIndex);
+    }
+
+    /**
+     * Set the iterator to its next value.
+     */
+    public function next(): void
+    {
+        do {
+            ++$this->currentColumnIndex;
+        } while (($this->onlyExistingCells) && (!$this->worksheet->cellExistsByColumnAndRow($this->currentColumnIndex, $this->rowIndex)) && ($this->currentColumnIndex <= $this->endColumnIndex));
+    }
+
+    /**
+     * Set the iterator to its previous value.
+     */
+    public function prev(): void
+    {
+        do {
+            --$this->currentColumnIndex;
+        } while (($this->onlyExistingCells) && (!$this->worksheet->cellExistsByColumnAndRow($this->currentColumnIndex, $this->rowIndex)) && ($this->currentColumnIndex >= $this->startColumnIndex));
+    }
+
+    /**
+     * Indicate if more columns exist in the worksheet range of columns that we're iterating.
+     *
+     * @return bool
+     */
+    public function valid()
+    {
+        return $this->currentColumnIndex <= $this->endColumnIndex && $this->currentColumnIndex >= $this->startColumnIndex;
+    }
+
+    /**
+     * Return the current iterator position.
+     *
+     * @return int
+     */
+    public function getCurrentColumnIndex()
+    {
+        return $this->currentColumnIndex;
+    }
+
+    /**
+     * Validate start/end values for "IterateOnlyExistingCells" mode, and adjust if necessary.
+     */
+    protected function adjustForExistingOnlyRange(): void
+    {
+        if ($this->onlyExistingCells) {
+            while ((!$this->worksheet->cellExistsByColumnAndRow($this->startColumnIndex, $this->rowIndex)) && ($this->startColumnIndex <= $this->endColumnIndex)) {
+                ++$this->startColumnIndex;
+            }
+            if ($this->startColumnIndex > $this->endColumnIndex) {
+                throw new PhpSpreadsheetException('No cells exist within the specified range');
+            }
+            while ((!$this->worksheet->cellExistsByColumnAndRow($this->endColumnIndex, $this->rowIndex)) && ($this->endColumnIndex >= $this->startColumnIndex)) {
+                --$this->endColumnIndex;
+            }
+            if ($this->endColumnIndex < $this->startColumnIndex) {
+                throw new PhpSpreadsheetException('No cells exist within the specified range');
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowDimension.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowDimension.php
new file mode 100755
index 0000000..c4a87bd
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowDimension.php
@@ -0,0 +1,115 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+class RowDimension extends Dimension
+{
+    /**
+     * Row index.
+     *
+     * @var int
+     */
+    private $rowIndex;
+
+    /**
+     * Row height (in pt).
+     *
+     * When this is set to a negative value, the row height should be ignored by IWriter
+     *
+     * @var float
+     */
+    private $height = -1;
+
+    /**
+     * ZeroHeight for Row?
+     *
+     * @var bool
+     */
+    private $zeroHeight = false;
+
+    /**
+     * Create a new RowDimension.
+     *
+     * @param int $pIndex Numeric row index
+     */
+    public function __construct($pIndex = 0)
+    {
+        // Initialise values
+        $this->rowIndex = $pIndex;
+
+        // set dimension as unformatted by default
+        parent::__construct(null);
+    }
+
+    /**
+     * Get Row Index.
+     *
+     * @return int
+     */
+    public function getRowIndex()
+    {
+        return $this->rowIndex;
+    }
+
+    /**
+     * Set Row Index.
+     *
+     * @param int $pValue
+     *
+     * @return $this
+     */
+    public function setRowIndex($pValue)
+    {
+        $this->rowIndex = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Row Height.
+     *
+     * @return float
+     */
+    public function getRowHeight()
+    {
+        return $this->height;
+    }
+
+    /**
+     * Set Row Height.
+     *
+     * @param float $pValue
+     *
+     * @return $this
+     */
+    public function setRowHeight($pValue)
+    {
+        $this->height = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get ZeroHeight.
+     *
+     * @return bool
+     */
+    public function getZeroHeight()
+    {
+        return $this->zeroHeight;
+    }
+
+    /**
+     * Set ZeroHeight.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setZeroHeight($pValue)
+    {
+        $this->zeroHeight = $pValue;
+
+        return $this;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowIterator.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowIterator.php
new file mode 100755
index 0000000..4254253
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowIterator.php
@@ -0,0 +1,167 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+use Iterator;
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+
+class RowIterator implements Iterator
+{
+    /**
+     * Worksheet to iterate.
+     *
+     * @var Worksheet
+     */
+    private $subject;
+
+    /**
+     * Current iterator position.
+     *
+     * @var int
+     */
+    private $position = 1;
+
+    /**
+     * Start position.
+     *
+     * @var int
+     */
+    private $startRow = 1;
+
+    /**
+     * End position.
+     *
+     * @var int
+     */
+    private $endRow = 1;
+
+    /**
+     * Create a new row iterator.
+     *
+     * @param Worksheet $subject The worksheet to iterate over
+     * @param int $startRow The row number at which to start iterating
+     * @param int $endRow Optionally, the row number at which to stop iterating
+     */
+    public function __construct(Worksheet $subject, $startRow = 1, $endRow = null)
+    {
+        // Set subject
+        $this->subject = $subject;
+        $this->resetEnd($endRow);
+        $this->resetStart($startRow);
+    }
+
+    /**
+     * Destructor.
+     */
+    public function __destruct()
+    {
+        $this->subject = null;
+    }
+
+    /**
+     * (Re)Set the start row and the current row pointer.
+     *
+     * @param int $startRow The row number at which to start iterating
+     *
+     * @return $this
+     */
+    public function resetStart($startRow = 1)
+    {
+        if ($startRow > $this->subject->getHighestRow()) {
+            throw new PhpSpreadsheetException("Start row ({$startRow}) is beyond highest row ({$this->subject->getHighestRow()})");
+        }
+
+        $this->startRow = $startRow;
+        if ($this->endRow < $this->startRow) {
+            $this->endRow = $this->startRow;
+        }
+        $this->seek($startRow);
+
+        return $this;
+    }
+
+    /**
+     * (Re)Set the end row.
+     *
+     * @param int $endRow The row number at which to stop iterating
+     *
+     * @return $this
+     */
+    public function resetEnd($endRow = null)
+    {
+        $this->endRow = ($endRow) ? $endRow : $this->subject->getHighestRow();
+
+        return $this;
+    }
+
+    /**
+     * Set the row pointer to the selected row.
+     *
+     * @param int $row The row number to set the current pointer at
+     *
+     * @return $this
+     */
+    public function seek($row = 1)
+    {
+        if (($row < $this->startRow) || ($row > $this->endRow)) {
+            throw new PhpSpreadsheetException("Row $row is out of range ({$this->startRow} - {$this->endRow})");
+        }
+        $this->position = $row;
+
+        return $this;
+    }
+
+    /**
+     * Rewind the iterator to the starting row.
+     */
+    public function rewind(): void
+    {
+        $this->position = $this->startRow;
+    }
+
+    /**
+     * Return the current row in this worksheet.
+     *
+     * @return Row
+     */
+    public function current()
+    {
+        return new Row($this->subject, $this->position);
+    }
+
+    /**
+     * Return the current iterator key.
+     *
+     * @return int
+     */
+    public function key()
+    {
+        return $this->position;
+    }
+
+    /**
+     * Set the iterator to its next value.
+     */
+    public function next(): void
+    {
+        ++$this->position;
+    }
+
+    /**
+     * Set the iterator to its previous value.
+     */
+    public function prev(): void
+    {
+        --$this->position;
+    }
+
+    /**
+     * Indicate if more rows exist in the worksheet range of rows that we're iterating.
+     *
+     * @return bool
+     */
+    public function valid()
+    {
+        return $this->position <= $this->endRow && $this->position >= $this->startRow;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/SheetView.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/SheetView.php
new file mode 100755
index 0000000..2f7d381
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/SheetView.php
@@ -0,0 +1,193 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+
+class SheetView
+{
+    // Sheet View types
+    const SHEETVIEW_NORMAL = 'normal';
+    const SHEETVIEW_PAGE_LAYOUT = 'pageLayout';
+    const SHEETVIEW_PAGE_BREAK_PREVIEW = 'pageBreakPreview';
+
+    private static $sheetViewTypes = [
+        self::SHEETVIEW_NORMAL,
+        self::SHEETVIEW_PAGE_LAYOUT,
+        self::SHEETVIEW_PAGE_BREAK_PREVIEW,
+    ];
+
+    /**
+     * ZoomScale.
+     *
+     * Valid values range from 10 to 400.
+     *
+     * @var int
+     */
+    private $zoomScale = 100;
+
+    /**
+     * ZoomScaleNormal.
+     *
+     * Valid values range from 10 to 400.
+     *
+     * @var int
+     */
+    private $zoomScaleNormal = 100;
+
+    /**
+     * ShowZeros.
+     *
+     * If true, "null" values from a calculation will be shown as "0". This is the default Excel behaviour and can be changed
+     * with the advanced worksheet option "Show a zero in cells that have zero value"
+     *
+     * @var bool
+     */
+    private $showZeros = true;
+
+    /**
+     * View.
+     *
+     * Valid values range from 10 to 400.
+     *
+     * @var string
+     */
+    private $sheetviewType = self::SHEETVIEW_NORMAL;
+
+    /**
+     * Create a new SheetView.
+     */
+    public function __construct()
+    {
+    }
+
+    /**
+     * Get ZoomScale.
+     *
+     * @return int
+     */
+    public function getZoomScale()
+    {
+        return $this->zoomScale;
+    }
+
+    /**
+     * Set ZoomScale.
+     * Valid values range from 10 to 400.
+     *
+     * @param int $pValue
+     *
+     * @return $this
+     */
+    public function setZoomScale($pValue)
+    {
+        // Microsoft Office Excel 2007 only allows setting a scale between 10 and 400 via the user interface,
+        // but it is apparently still able to handle any scale >= 1
+        if (($pValue >= 1) || $pValue === null) {
+            $this->zoomScale = $pValue;
+        } else {
+            throw new PhpSpreadsheetException('Scale must be greater than or equal to 1.');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get ZoomScaleNormal.
+     *
+     * @return int
+     */
+    public function getZoomScaleNormal()
+    {
+        return $this->zoomScaleNormal;
+    }
+
+    /**
+     * Set ZoomScale.
+     * Valid values range from 10 to 400.
+     *
+     * @param int $pValue
+     *
+     * @return $this
+     */
+    public function setZoomScaleNormal($pValue)
+    {
+        if (($pValue >= 1) || $pValue === null) {
+            $this->zoomScaleNormal = $pValue;
+        } else {
+            throw new PhpSpreadsheetException('Scale must be greater than or equal to 1.');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Set ShowZeroes setting.
+     *
+     * @param bool $pValue
+     */
+    public function setShowZeros($pValue): void
+    {
+        $this->showZeros = $pValue;
+    }
+
+    /**
+     * @return bool
+     */
+    public function getShowZeros()
+    {
+        return $this->showZeros;
+    }
+
+    /**
+     * Get View.
+     *
+     * @return string
+     */
+    public function getView()
+    {
+        return $this->sheetviewType;
+    }
+
+    /**
+     * Set View.
+     *
+     * Valid values are
+     *        'normal'            self::SHEETVIEW_NORMAL
+     *        'pageLayout'        self::SHEETVIEW_PAGE_LAYOUT
+     *        'pageBreakPreview'  self::SHEETVIEW_PAGE_BREAK_PREVIEW
+     *
+     * @param string $pValue
+     *
+     * @return $this
+     */
+    public function setView($pValue)
+    {
+        // MS Excel 2007 allows setting the view to 'normal', 'pageLayout' or 'pageBreakPreview' via the user interface
+        if ($pValue === null) {
+            $pValue = self::SHEETVIEW_NORMAL;
+        }
+        if (in_array($pValue, self::$sheetViewTypes)) {
+            $this->sheetviewType = $pValue;
+        } else {
+            throw new PhpSpreadsheetException('Invalid sheetview layout type.');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $vars = get_object_vars($this);
+        foreach ($vars as $key => $value) {
+            if (is_object($value)) {
+                $this->$key = clone $value;
+            } else {
+                $this->$key = $value;
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Worksheet.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Worksheet.php
new file mode 100755
index 0000000..e2b0dd8
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Worksheet.php
@@ -0,0 +1,3013 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+use ArrayObject;
+use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
+use PhpOffice\PhpSpreadsheet\Cell\Cell;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Cell\DataType;
+use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
+use PhpOffice\PhpSpreadsheet\Cell\Hyperlink;
+use PhpOffice\PhpSpreadsheet\Chart\Chart;
+use PhpOffice\PhpSpreadsheet\Collection\Cells;
+use PhpOffice\PhpSpreadsheet\Collection\CellsFactory;
+use PhpOffice\PhpSpreadsheet\Comment;
+use PhpOffice\PhpSpreadsheet\Exception;
+use PhpOffice\PhpSpreadsheet\IComparable;
+use PhpOffice\PhpSpreadsheet\NamedRange;
+use PhpOffice\PhpSpreadsheet\ReferenceHelper;
+use PhpOffice\PhpSpreadsheet\RichText\RichText;
+use PhpOffice\PhpSpreadsheet\Shared;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Style\Color;
+use PhpOffice\PhpSpreadsheet\Style\Conditional;
+use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
+use PhpOffice\PhpSpreadsheet\Style\Style;
+
+class Worksheet implements IComparable
+{
+    // Break types
+    const BREAK_NONE = 0;
+    const BREAK_ROW = 1;
+    const BREAK_COLUMN = 2;
+
+    // Sheet state
+    const SHEETSTATE_VISIBLE = 'visible';
+    const SHEETSTATE_HIDDEN = 'hidden';
+    const SHEETSTATE_VERYHIDDEN = 'veryHidden';
+
+    /**
+     * Maximum 31 characters allowed for sheet title.
+     *
+     * @var int
+     */
+    const SHEET_TITLE_MAXIMUM_LENGTH = 31;
+
+    /**
+     * Invalid characters in sheet title.
+     *
+     * @var array
+     */
+    private static $invalidCharacters = ['*', ':', '/', '\\', '?', '[', ']'];
+
+    /**
+     * Parent spreadsheet.
+     *
+     * @var Spreadsheet
+     */
+    private $parent;
+
+    /**
+     * Collection of cells.
+     *
+     * @var Cells
+     */
+    private $cellCollection;
+
+    /**
+     * Collection of row dimensions.
+     *
+     * @var RowDimension[]
+     */
+    private $rowDimensions = [];
+
+    /**
+     * Default row dimension.
+     *
+     * @var RowDimension
+     */
+    private $defaultRowDimension;
+
+    /**
+     * Collection of column dimensions.
+     *
+     * @var ColumnDimension[]
+     */
+    private $columnDimensions = [];
+
+    /**
+     * Default column dimension.
+     *
+     * @var ColumnDimension
+     */
+    private $defaultColumnDimension;
+
+    /**
+     * Collection of drawings.
+     *
+     * @var BaseDrawing[]
+     */
+    private $drawingCollection;
+
+    /**
+     * Collection of Chart objects.
+     *
+     * @var Chart[]
+     */
+    private $chartCollection = [];
+
+    /**
+     * Worksheet title.
+     *
+     * @var string
+     */
+    private $title;
+
+    /**
+     * Sheet state.
+     *
+     * @var string
+     */
+    private $sheetState;
+
+    /**
+     * Page setup.
+     *
+     * @var PageSetup
+     */
+    private $pageSetup;
+
+    /**
+     * Page margins.
+     *
+     * @var PageMargins
+     */
+    private $pageMargins;
+
+    /**
+     * Page header/footer.
+     *
+     * @var HeaderFooter
+     */
+    private $headerFooter;
+
+    /**
+     * Sheet view.
+     *
+     * @var SheetView
+     */
+    private $sheetView;
+
+    /**
+     * Protection.
+     *
+     * @var Protection
+     */
+    private $protection;
+
+    /**
+     * Collection of styles.
+     *
+     * @var Style[]
+     */
+    private $styles = [];
+
+    /**
+     * Conditional styles. Indexed by cell coordinate, e.g. 'A1'.
+     *
+     * @var array
+     */
+    private $conditionalStylesCollection = [];
+
+    /**
+     * Is the current cell collection sorted already?
+     *
+     * @var bool
+     */
+    private $cellCollectionIsSorted = false;
+
+    /**
+     * Collection of breaks.
+     *
+     * @var array
+     */
+    private $breaks = [];
+
+    /**
+     * Collection of merged cell ranges.
+     *
+     * @var string[]
+     */
+    private $mergeCells = [];
+
+    /**
+     * Collection of protected cell ranges.
+     *
+     * @var array
+     */
+    private $protectedCells = [];
+
+    /**
+     * Autofilter Range and selection.
+     *
+     * @var AutoFilter
+     */
+    private $autoFilter;
+
+    /**
+     * Freeze pane.
+     *
+     * @var null|string
+     */
+    private $freezePane;
+
+    /**
+     * Default position of the right bottom pane.
+     *
+     * @var null|string
+     */
+    private $topLeftCell;
+
+    /**
+     * Show gridlines?
+     *
+     * @var bool
+     */
+    private $showGridlines = true;
+
+    /**
+     * Print gridlines?
+     *
+     * @var bool
+     */
+    private $printGridlines = false;
+
+    /**
+     * Show row and column headers?
+     *
+     * @var bool
+     */
+    private $showRowColHeaders = true;
+
+    /**
+     * Show summary below? (Row/Column outline).
+     *
+     * @var bool
+     */
+    private $showSummaryBelow = true;
+
+    /**
+     * Show summary right? (Row/Column outline).
+     *
+     * @var bool
+     */
+    private $showSummaryRight = true;
+
+    /**
+     * Collection of comments.
+     *
+     * @var Comment[]
+     */
+    private $comments = [];
+
+    /**
+     * Active cell. (Only one!).
+     *
+     * @var string
+     */
+    private $activeCell = 'A1';
+
+    /**
+     * Selected cells.
+     *
+     * @var string
+     */
+    private $selectedCells = 'A1';
+
+    /**
+     * Cached highest column.
+     *
+     * @var string
+     */
+    private $cachedHighestColumn = 'A';
+
+    /**
+     * Cached highest row.
+     *
+     * @var int
+     */
+    private $cachedHighestRow = 1;
+
+    /**
+     * Right-to-left?
+     *
+     * @var bool
+     */
+    private $rightToLeft = false;
+
+    /**
+     * Hyperlinks. Indexed by cell coordinate, e.g. 'A1'.
+     *
+     * @var array
+     */
+    private $hyperlinkCollection = [];
+
+    /**
+     * Data validation objects. Indexed by cell coordinate, e.g. 'A1'.
+     *
+     * @var array
+     */
+    private $dataValidationCollection = [];
+
+    /**
+     * Tab color.
+     *
+     * @var Color
+     */
+    private $tabColor;
+
+    /**
+     * Dirty flag.
+     *
+     * @var bool
+     */
+    private $dirty = true;
+
+    /**
+     * Hash.
+     *
+     * @var string
+     */
+    private $hash;
+
+    /**
+     * CodeName.
+     *
+     * @var string
+     */
+    private $codeName;
+
+    /**
+     * Create a new worksheet.
+     *
+     * @param Spreadsheet $parent
+     * @param string $pTitle
+     */
+    public function __construct(?Spreadsheet $parent = null, $pTitle = 'Worksheet')
+    {
+        // Set parent and title
+        $this->parent = $parent;
+        $this->setTitle($pTitle, false);
+        // setTitle can change $pTitle
+        $this->setCodeName($this->getTitle());
+        $this->setSheetState(self::SHEETSTATE_VISIBLE);
+
+        $this->cellCollection = CellsFactory::getInstance($this);
+        // Set page setup
+        $this->pageSetup = new PageSetup();
+        // Set page margins
+        $this->pageMargins = new PageMargins();
+        // Set page header/footer
+        $this->headerFooter = new HeaderFooter();
+        // Set sheet view
+        $this->sheetView = new SheetView();
+        // Drawing collection
+        $this->drawingCollection = new ArrayObject();
+        // Chart collection
+        $this->chartCollection = new ArrayObject();
+        // Protection
+        $this->protection = new Protection();
+        // Default row dimension
+        $this->defaultRowDimension = new RowDimension(null);
+        // Default column dimension
+        $this->defaultColumnDimension = new ColumnDimension(null);
+        $this->autoFilter = new AutoFilter(null, $this);
+    }
+
+    /**
+     * Disconnect all cells from this Worksheet object,
+     * typically so that the worksheet object can be unset.
+     */
+    public function disconnectCells(): void
+    {
+        if ($this->cellCollection !== null) {
+            $this->cellCollection->unsetWorksheetCells();
+            $this->cellCollection = null;
+        }
+        //    detach ourself from the workbook, so that it can then delete this worksheet successfully
+        $this->parent = null;
+    }
+
+    /**
+     * Code to execute when this worksheet is unset().
+     */
+    public function __destruct()
+    {
+        Calculation::getInstance($this->parent)->clearCalculationCacheForWorksheet($this->title);
+
+        $this->disconnectCells();
+    }
+
+    /**
+     * Return the cell collection.
+     *
+     * @return Cells
+     */
+    public function getCellCollection()
+    {
+        return $this->cellCollection;
+    }
+
+    /**
+     * Get array of invalid characters for sheet title.
+     *
+     * @return array
+     */
+    public static function getInvalidCharacters()
+    {
+        return self::$invalidCharacters;
+    }
+
+    /**
+     * Check sheet code name for valid Excel syntax.
+     *
+     * @param string $pValue The string to check
+     *
+     * @return string The valid string
+     */
+    private static function checkSheetCodeName($pValue)
+    {
+        $CharCount = Shared\StringHelper::countCharacters($pValue);
+        if ($CharCount == 0) {
+            throw new Exception('Sheet code name cannot be empty.');
+        }
+        // Some of the printable ASCII characters are invalid:  * : / \ ? [ ] and  first and last characters cannot be a "'"
+        if ((str_replace(self::$invalidCharacters, '', $pValue) !== $pValue) ||
+            (Shared\StringHelper::substring($pValue, -1, 1) == '\'') ||
+            (Shared\StringHelper::substring($pValue, 0, 1) == '\'')) {
+            throw new Exception('Invalid character found in sheet code name');
+        }
+
+        // Enforce maximum characters allowed for sheet title
+        if ($CharCount > self::SHEET_TITLE_MAXIMUM_LENGTH) {
+            throw new Exception('Maximum ' . self::SHEET_TITLE_MAXIMUM_LENGTH . ' characters allowed in sheet code name.');
+        }
+
+        return $pValue;
+    }
+
+    /**
+     * Check sheet title for valid Excel syntax.
+     *
+     * @param string $pValue The string to check
+     *
+     * @return string The valid string
+     */
+    private static function checkSheetTitle($pValue)
+    {
+        // Some of the printable ASCII characters are invalid:  * : / \ ? [ ]
+        if (str_replace(self::$invalidCharacters, '', $pValue) !== $pValue) {
+            throw new Exception('Invalid character found in sheet title');
+        }
+
+        // Enforce maximum characters allowed for sheet title
+        if (Shared\StringHelper::countCharacters($pValue) > self::SHEET_TITLE_MAXIMUM_LENGTH) {
+            throw new Exception('Maximum ' . self::SHEET_TITLE_MAXIMUM_LENGTH . ' characters allowed in sheet title.');
+        }
+
+        return $pValue;
+    }
+
+    /**
+     * Get a sorted list of all cell coordinates currently held in the collection by row and column.
+     *
+     * @param bool $sorted Also sort the cell collection?
+     *
+     * @return string[]
+     */
+    public function getCoordinates($sorted = true)
+    {
+        if ($this->cellCollection == null) {
+            return [];
+        }
+
+        if ($sorted) {
+            return $this->cellCollection->getSortedCoordinates();
+        }
+
+        return $this->cellCollection->getCoordinates();
+    }
+
+    /**
+     * Get collection of row dimensions.
+     *
+     * @return RowDimension[]
+     */
+    public function getRowDimensions()
+    {
+        return $this->rowDimensions;
+    }
+
+    /**
+     * Get default row dimension.
+     *
+     * @return RowDimension
+     */
+    public function getDefaultRowDimension()
+    {
+        return $this->defaultRowDimension;
+    }
+
+    /**
+     * Get collection of column dimensions.
+     *
+     * @return ColumnDimension[]
+     */
+    public function getColumnDimensions()
+    {
+        return $this->columnDimensions;
+    }
+
+    /**
+     * Get default column dimension.
+     *
+     * @return ColumnDimension
+     */
+    public function getDefaultColumnDimension()
+    {
+        return $this->defaultColumnDimension;
+    }
+
+    /**
+     * Get collection of drawings.
+     *
+     * @return BaseDrawing[]
+     */
+    public function getDrawingCollection()
+    {
+        return $this->drawingCollection;
+    }
+
+    /**
+     * Get collection of charts.
+     *
+     * @return Chart[]
+     */
+    public function getChartCollection()
+    {
+        return $this->chartCollection;
+    }
+
+    /**
+     * Add chart.
+     *
+     * @param null|int $iChartIndex Index where chart should go (0,1,..., or null for last)
+     *
+     * @return Chart
+     */
+    public function addChart(Chart $pChart, $iChartIndex = null)
+    {
+        $pChart->setWorksheet($this);
+        if ($iChartIndex === null) {
+            $this->chartCollection[] = $pChart;
+        } else {
+            // Insert the chart at the requested index
+            array_splice($this->chartCollection, $iChartIndex, 0, [$pChart]);
+        }
+
+        return $pChart;
+    }
+
+    /**
+     * Return the count of charts on this worksheet.
+     *
+     * @return int The number of charts
+     */
+    public function getChartCount()
+    {
+        return count($this->chartCollection);
+    }
+
+    /**
+     * Get a chart by its index position.
+     *
+     * @param string $index Chart index position
+     *
+     * @return Chart|false
+     */
+    public function getChartByIndex($index)
+    {
+        $chartCount = count($this->chartCollection);
+        if ($chartCount == 0) {
+            return false;
+        }
+        if ($index === null) {
+            $index = --$chartCount;
+        }
+        if (!isset($this->chartCollection[$index])) {
+            return false;
+        }
+
+        return $this->chartCollection[$index];
+    }
+
+    /**
+     * Return an array of the names of charts on this worksheet.
+     *
+     * @return string[] The names of charts
+     */
+    public function getChartNames()
+    {
+        $chartNames = [];
+        foreach ($this->chartCollection as $chart) {
+            $chartNames[] = $chart->getName();
+        }
+
+        return $chartNames;
+    }
+
+    /**
+     * Get a chart by name.
+     *
+     * @param string $chartName Chart name
+     *
+     * @return Chart|false
+     */
+    public function getChartByName($chartName)
+    {
+        $chartCount = count($this->chartCollection);
+        if ($chartCount == 0) {
+            return false;
+        }
+        foreach ($this->chartCollection as $index => $chart) {
+            if ($chart->getName() == $chartName) {
+                return $this->chartCollection[$index];
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Refresh column dimensions.
+     *
+     * @return $this
+     */
+    public function refreshColumnDimensions()
+    {
+        $currentColumnDimensions = $this->getColumnDimensions();
+        $newColumnDimensions = [];
+
+        foreach ($currentColumnDimensions as $objColumnDimension) {
+            $newColumnDimensions[$objColumnDimension->getColumnIndex()] = $objColumnDimension;
+        }
+
+        $this->columnDimensions = $newColumnDimensions;
+
+        return $this;
+    }
+
+    /**
+     * Refresh row dimensions.
+     *
+     * @return $this
+     */
+    public function refreshRowDimensions()
+    {
+        $currentRowDimensions = $this->getRowDimensions();
+        $newRowDimensions = [];
+
+        foreach ($currentRowDimensions as $objRowDimension) {
+            $newRowDimensions[$objRowDimension->getRowIndex()] = $objRowDimension;
+        }
+
+        $this->rowDimensions = $newRowDimensions;
+
+        return $this;
+    }
+
+    /**
+     * Calculate worksheet dimension.
+     *
+     * @return string String containing the dimension of this worksheet
+     */
+    public function calculateWorksheetDimension()
+    {
+        // Return
+        return 'A1:' . $this->getHighestColumn() . $this->getHighestRow();
+    }
+
+    /**
+     * Calculate worksheet data dimension.
+     *
+     * @return string String containing the dimension of this worksheet that actually contain data
+     */
+    public function calculateWorksheetDataDimension()
+    {
+        // Return
+        return 'A1:' . $this->getHighestDataColumn() . $this->getHighestDataRow();
+    }
+
+    /**
+     * Calculate widths for auto-size columns.
+     *
+     * @return $this
+     */
+    public function calculateColumnWidths()
+    {
+        // initialize $autoSizes array
+        $autoSizes = [];
+        foreach ($this->getColumnDimensions() as $colDimension) {
+            if ($colDimension->getAutoSize()) {
+                $autoSizes[$colDimension->getColumnIndex()] = -1;
+            }
+        }
+
+        // There is only something to do if there are some auto-size columns
+        if (!empty($autoSizes)) {
+            // build list of cells references that participate in a merge
+            $isMergeCell = [];
+            foreach ($this->getMergeCells() as $cells) {
+                foreach (Coordinate::extractAllCellReferencesInRange($cells) as $cellReference) {
+                    $isMergeCell[$cellReference] = true;
+                }
+            }
+
+            // loop through all cells in the worksheet
+            foreach ($this->getCoordinates(false) as $coordinate) {
+                $cell = $this->getCell($coordinate, false);
+                if ($cell !== null && isset($autoSizes[$this->cellCollection->getCurrentColumn()])) {
+                    //Determine if cell is in merge range
+                    $isMerged = isset($isMergeCell[$this->cellCollection->getCurrentCoordinate()]);
+
+                    //By default merged cells should be ignored
+                    $isMergedButProceed = false;
+
+                    //The only exception is if it's a merge range value cell of a 'vertical' randge (1 column wide)
+                    if ($isMerged && $cell->isMergeRangeValueCell()) {
+                        $range = $cell->getMergeRange();
+                        $rangeBoundaries = Coordinate::rangeDimension($range);
+                        if ($rangeBoundaries[0] == 1) {
+                            $isMergedButProceed = true;
+                        }
+                    }
+
+                    // Determine width if cell does not participate in a merge or does and is a value cell of 1-column wide range
+                    if (!$isMerged || $isMergedButProceed) {
+                        // Calculated value
+                        // To formatted string
+                        $cellValue = NumberFormat::toFormattedString(
+                            $cell->getCalculatedValue(),
+                            $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode()
+                        );
+
+                        $autoSizes[$this->cellCollection->getCurrentColumn()] = max(
+                            (float) $autoSizes[$this->cellCollection->getCurrentColumn()],
+                            (float) Shared\Font::calculateColumnWidth(
+                                $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont(),
+                                $cellValue,
+                                $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getAlignment()->getTextRotation(),
+                                $this->getParent()->getDefaultStyle()->getFont()
+                            )
+                        );
+                    }
+                }
+            }
+
+            // adjust column widths
+            foreach ($autoSizes as $columnIndex => $width) {
+                if ($width == -1) {
+                    $width = $this->getDefaultColumnDimension()->getWidth();
+                }
+                $this->getColumnDimension($columnIndex)->setWidth($width);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get parent.
+     *
+     * @return Spreadsheet
+     */
+    public function getParent()
+    {
+        return $this->parent;
+    }
+
+    /**
+     * Re-bind parent.
+     *
+     * @return $this
+     */
+    public function rebindParent(Spreadsheet $parent)
+    {
+        if ($this->parent !== null) {
+            $namedRanges = $this->parent->getNamedRanges();
+            foreach ($namedRanges as $namedRange) {
+                $parent->addNamedRange($namedRange);
+            }
+
+            $this->parent->removeSheetByIndex(
+                $this->parent->getIndex($this)
+            );
+        }
+        $this->parent = $parent;
+
+        return $this;
+    }
+
+    /**
+     * Get title.
+     *
+     * @return string
+     */
+    public function getTitle()
+    {
+        return $this->title;
+    }
+
+    /**
+     * Set title.
+     *
+     * @param string $pValue String containing the dimension of this worksheet
+     * @param bool $updateFormulaCellReferences Flag indicating whether cell references in formulae should
+     *            be updated to reflect the new sheet name.
+     *          This should be left as the default true, unless you are
+     *          certain that no formula cells on any worksheet contain
+     *          references to this worksheet
+     * @param bool $validate False to skip validation of new title. WARNING: This should only be set
+     *                       at parse time (by Readers), where titles can be assumed to be valid.
+     *
+     * @return $this
+     */
+    public function setTitle($pValue, $updateFormulaCellReferences = true, $validate = true)
+    {
+        // Is this a 'rename' or not?
+        if ($this->getTitle() == $pValue) {
+            return $this;
+        }
+
+        // Old title
+        $oldTitle = $this->getTitle();
+
+        if ($validate) {
+            // Syntax check
+            self::checkSheetTitle($pValue);
+
+            if ($this->parent) {
+                // Is there already such sheet name?
+                if ($this->parent->sheetNameExists($pValue)) {
+                    // Use name, but append with lowest possible integer
+
+                    if (Shared\StringHelper::countCharacters($pValue) > 29) {
+                        $pValue = Shared\StringHelper::substring($pValue, 0, 29);
+                    }
+                    $i = 1;
+                    while ($this->parent->sheetNameExists($pValue . ' ' . $i)) {
+                        ++$i;
+                        if ($i == 10) {
+                            if (Shared\StringHelper::countCharacters($pValue) > 28) {
+                                $pValue = Shared\StringHelper::substring($pValue, 0, 28);
+                            }
+                        } elseif ($i == 100) {
+                            if (Shared\StringHelper::countCharacters($pValue) > 27) {
+                                $pValue = Shared\StringHelper::substring($pValue, 0, 27);
+                            }
+                        }
+                    }
+
+                    $pValue .= " $i";
+                }
+            }
+        }
+
+        // Set title
+        $this->title = $pValue;
+        $this->dirty = true;
+
+        if ($this->parent && $this->parent->getCalculationEngine()) {
+            // New title
+            $newTitle = $this->getTitle();
+            $this->parent->getCalculationEngine()
+                ->renameCalculationCacheForWorksheet($oldTitle, $newTitle);
+            if ($updateFormulaCellReferences) {
+                ReferenceHelper::getInstance()->updateNamedFormulas($this->parent, $oldTitle, $newTitle);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get sheet state.
+     *
+     * @return string Sheet state (visible, hidden, veryHidden)
+     */
+    public function getSheetState()
+    {
+        return $this->sheetState;
+    }
+
+    /**
+     * Set sheet state.
+     *
+     * @param string $value Sheet state (visible, hidden, veryHidden)
+     *
+     * @return $this
+     */
+    public function setSheetState($value)
+    {
+        $this->sheetState = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get page setup.
+     *
+     * @return PageSetup
+     */
+    public function getPageSetup()
+    {
+        return $this->pageSetup;
+    }
+
+    /**
+     * Set page setup.
+     *
+     * @return $this
+     */
+    public function setPageSetup(PageSetup $pValue)
+    {
+        $this->pageSetup = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get page margins.
+     *
+     * @return PageMargins
+     */
+    public function getPageMargins()
+    {
+        return $this->pageMargins;
+    }
+
+    /**
+     * Set page margins.
+     *
+     * @return $this
+     */
+    public function setPageMargins(PageMargins $pValue)
+    {
+        $this->pageMargins = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get page header/footer.
+     *
+     * @return HeaderFooter
+     */
+    public function getHeaderFooter()
+    {
+        return $this->headerFooter;
+    }
+
+    /**
+     * Set page header/footer.
+     *
+     * @return $this
+     */
+    public function setHeaderFooter(HeaderFooter $pValue)
+    {
+        $this->headerFooter = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get sheet view.
+     *
+     * @return SheetView
+     */
+    public function getSheetView()
+    {
+        return $this->sheetView;
+    }
+
+    /**
+     * Set sheet view.
+     *
+     * @return $this
+     */
+    public function setSheetView(SheetView $pValue)
+    {
+        $this->sheetView = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Protection.
+     *
+     * @return Protection
+     */
+    public function getProtection()
+    {
+        return $this->protection;
+    }
+
+    /**
+     * Set Protection.
+     *
+     * @return $this
+     */
+    public function setProtection(Protection $pValue)
+    {
+        $this->protection = $pValue;
+        $this->dirty = true;
+
+        return $this;
+    }
+
+    /**
+     * Get highest worksheet column.
+     *
+     * @param string $row Return the data highest column for the specified row,
+     *                                     or the highest column of any row if no row number is passed
+     *
+     * @return string Highest column name
+     */
+    public function getHighestColumn($row = null)
+    {
+        if ($row == null) {
+            return $this->cachedHighestColumn;
+        }
+
+        return $this->getHighestDataColumn($row);
+    }
+
+    /**
+     * Get highest worksheet column that contains data.
+     *
+     * @param string $row Return the highest data column for the specified row,
+     *                                     or the highest data column of any row if no row number is passed
+     *
+     * @return string Highest column name that contains data
+     */
+    public function getHighestDataColumn($row = null)
+    {
+        return $this->cellCollection->getHighestColumn($row);
+    }
+
+    /**
+     * Get highest worksheet row.
+     *
+     * @param string $column Return the highest data row for the specified column,
+     *                                     or the highest row of any column if no column letter is passed
+     *
+     * @return int Highest row number
+     */
+    public function getHighestRow($column = null)
+    {
+        if ($column == null) {
+            return $this->cachedHighestRow;
+        }
+
+        return $this->getHighestDataRow($column);
+    }
+
+    /**
+     * Get highest worksheet row that contains data.
+     *
+     * @param string $column Return the highest data row for the specified column,
+     *                                     or the highest data row of any column if no column letter is passed
+     *
+     * @return int Highest row number that contains data
+     */
+    public function getHighestDataRow($column = null)
+    {
+        return $this->cellCollection->getHighestRow($column);
+    }
+
+    /**
+     * Get highest worksheet column and highest row that have cell records.
+     *
+     * @return array Highest column name and highest row number
+     */
+    public function getHighestRowAndColumn()
+    {
+        return $this->cellCollection->getHighestRowAndColumn();
+    }
+
+    /**
+     * Set a cell value.
+     *
+     * @param string $pCoordinate Coordinate of the cell, eg: 'A1'
+     * @param mixed $pValue Value of the cell
+     *
+     * @return $this
+     */
+    public function setCellValue($pCoordinate, $pValue)
+    {
+        $this->getCell($pCoordinate)->setValue($pValue);
+
+        return $this;
+    }
+
+    /**
+     * Set a cell value by using numeric cell coordinates.
+     *
+     * @param int $columnIndex Numeric column coordinate of the cell
+     * @param int $row Numeric row coordinate of the cell
+     * @param mixed $value Value of the cell
+     *
+     * @return $this
+     */
+    public function setCellValueByColumnAndRow($columnIndex, $row, $value)
+    {
+        $this->getCellByColumnAndRow($columnIndex, $row)->setValue($value);
+
+        return $this;
+    }
+
+    /**
+     * Set a cell value.
+     *
+     * @param string $pCoordinate Coordinate of the cell, eg: 'A1'
+     * @param mixed $pValue Value of the cell
+     * @param string $pDataType Explicit data type, see DataType::TYPE_*
+     *
+     * @return $this
+     */
+    public function setCellValueExplicit($pCoordinate, $pValue, $pDataType)
+    {
+        // Set value
+        $this->getCell($pCoordinate)->setValueExplicit($pValue, $pDataType);
+
+        return $this;
+    }
+
+    /**
+     * Set a cell value by using numeric cell coordinates.
+     *
+     * @param int $columnIndex Numeric column coordinate of the cell
+     * @param int $row Numeric row coordinate of the cell
+     * @param mixed $value Value of the cell
+     * @param string $dataType Explicit data type, see DataType::TYPE_*
+     *
+     * @return $this
+     */
+    public function setCellValueExplicitByColumnAndRow($columnIndex, $row, $value, $dataType)
+    {
+        $this->getCellByColumnAndRow($columnIndex, $row)->setValueExplicit($value, $dataType);
+
+        return $this;
+    }
+
+    /**
+     * Get cell at a specific coordinate.
+     *
+     * @param string $pCoordinate Coordinate of the cell, eg: 'A1'
+     * @param bool $createIfNotExists Flag indicating whether a new cell should be created if it doesn't
+     *                                       already exist, or a null should be returned instead
+     *
+     * @return null|Cell Cell that was found/created or null
+     */
+    public function getCell($pCoordinate, $createIfNotExists = true)
+    {
+        // Uppercase coordinate
+        $pCoordinateUpper = strtoupper($pCoordinate);
+
+        // Check cell collection
+        if ($this->cellCollection->has($pCoordinateUpper)) {
+            return $this->cellCollection->get($pCoordinateUpper);
+        }
+
+        // Worksheet reference?
+        if (strpos($pCoordinate, '!') !== false) {
+            $worksheetReference = self::extractSheetTitle($pCoordinate, true);
+
+            return $this->parent->getSheetByName($worksheetReference[0])->getCell(strtoupper($worksheetReference[1]), $createIfNotExists);
+        }
+
+        // Named range?
+        if ((!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $pCoordinate, $matches)) &&
+            (preg_match('/^' . Calculation::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $pCoordinate, $matches))) {
+            $namedRange = NamedRange::resolveRange($pCoordinate, $this);
+            if ($namedRange !== null) {
+                $pCoordinate = $namedRange->getRange();
+
+                return $namedRange->getWorksheet()->getCell($pCoordinate, $createIfNotExists);
+            }
+        }
+
+        if (Coordinate::coordinateIsRange($pCoordinate)) {
+            throw new Exception('Cell coordinate can not be a range of cells.');
+        } elseif (strpos($pCoordinate, '$') !== false) {
+            throw new Exception('Cell coordinate must not be absolute.');
+        }
+
+        // Create new cell object, if required
+        return $createIfNotExists ? $this->createNewCell($pCoordinateUpper) : null;
+    }
+
+    /**
+     * Get cell at a specific coordinate by using numeric cell coordinates.
+     *
+     * @param int $columnIndex Numeric column coordinate of the cell
+     * @param int $row Numeric row coordinate of the cell
+     * @param bool $createIfNotExists Flag indicating whether a new cell should be created if it doesn't
+     *                                       already exist, or a null should be returned instead
+     *
+     * @return null|Cell Cell that was found/created or null
+     */
+    public function getCellByColumnAndRow($columnIndex, $row, $createIfNotExists = true)
+    {
+        $columnLetter = Coordinate::stringFromColumnIndex($columnIndex);
+        $coordinate = $columnLetter . $row;
+
+        if ($this->cellCollection->has($coordinate)) {
+            return $this->cellCollection->get($coordinate);
+        }
+
+        // Create new cell object, if required
+        return $createIfNotExists ? $this->createNewCell($coordinate) : null;
+    }
+
+    /**
+     * Create a new cell at the specified coordinate.
+     *
+     * @param string $pCoordinate Coordinate of the cell
+     *
+     * @return Cell Cell that was created
+     */
+    private function createNewCell($pCoordinate)
+    {
+        $cell = new Cell(null, DataType::TYPE_NULL, $this);
+        $this->cellCollection->add($pCoordinate, $cell);
+        $this->cellCollectionIsSorted = false;
+
+        // Coordinates
+        $aCoordinates = Coordinate::coordinateFromString($pCoordinate);
+        if (Coordinate::columnIndexFromString($this->cachedHighestColumn) < Coordinate::columnIndexFromString($aCoordinates[0])) {
+            $this->cachedHighestColumn = $aCoordinates[0];
+        }
+        if ($aCoordinates[1] > $this->cachedHighestRow) {
+            $this->cachedHighestRow = $aCoordinates[1];
+        }
+
+        // Cell needs appropriate xfIndex from dimensions records
+        //    but don't create dimension records if they don't already exist
+        $rowDimension = $this->getRowDimension($aCoordinates[1], false);
+        $columnDimension = $this->getColumnDimension($aCoordinates[0], false);
+
+        if ($rowDimension !== null && $rowDimension->getXfIndex() > 0) {
+            // then there is a row dimension with explicit style, assign it to the cell
+            $cell->setXfIndex($rowDimension->getXfIndex());
+        } elseif ($columnDimension !== null && $columnDimension->getXfIndex() > 0) {
+            // then there is a column dimension, assign it to the cell
+            $cell->setXfIndex($columnDimension->getXfIndex());
+        }
+
+        return $cell;
+    }
+
+    /**
+     * Does the cell at a specific coordinate exist?
+     *
+     * @param string $pCoordinate Coordinate of the cell eg: 'A1'
+     *
+     * @return bool
+     */
+    public function cellExists($pCoordinate)
+    {
+        // Worksheet reference?
+        if (strpos($pCoordinate, '!') !== false) {
+            $worksheetReference = self::extractSheetTitle($pCoordinate, true);
+
+            return $this->parent->getSheetByName($worksheetReference[0])->cellExists(strtoupper($worksheetReference[1]));
+        }
+
+        // Named range?
+        if ((!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $pCoordinate, $matches)) &&
+            (preg_match('/^' . Calculation::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $pCoordinate, $matches))) {
+            $namedRange = NamedRange::resolveRange($pCoordinate, $this);
+            if ($namedRange !== null) {
+                $pCoordinate = $namedRange->getRange();
+                if ($this->getHashCode() != $namedRange->getWorksheet()->getHashCode()) {
+                    if (!$namedRange->getLocalOnly()) {
+                        return $namedRange->getWorksheet()->cellExists($pCoordinate);
+                    }
+
+                    throw new Exception('Named range ' . $namedRange->getName() . ' is not accessible from within sheet ' . $this->getTitle());
+                }
+            } else {
+                return false;
+            }
+        }
+
+        // Uppercase coordinate
+        $pCoordinate = strtoupper($pCoordinate);
+
+        if (Coordinate::coordinateIsRange($pCoordinate)) {
+            throw new Exception('Cell coordinate can not be a range of cells.');
+        } elseif (strpos($pCoordinate, '$') !== false) {
+            throw new Exception('Cell coordinate must not be absolute.');
+        }
+
+        // Cell exists?
+        return $this->cellCollection->has($pCoordinate);
+    }
+
+    /**
+     * Cell at a specific coordinate by using numeric cell coordinates exists?
+     *
+     * @param int $columnIndex Numeric column coordinate of the cell
+     * @param int $row Numeric row coordinate of the cell
+     *
+     * @return bool
+     */
+    public function cellExistsByColumnAndRow($columnIndex, $row)
+    {
+        return $this->cellExists(Coordinate::stringFromColumnIndex($columnIndex) . $row);
+    }
+
+    /**
+     * Get row dimension at a specific row.
+     *
+     * @param int $pRow Numeric index of the row
+     * @param bool $create
+     *
+     * @return RowDimension
+     */
+    public function getRowDimension($pRow, $create = true)
+    {
+        // Found
+        $found = null;
+
+        // Get row dimension
+        if (!isset($this->rowDimensions[$pRow])) {
+            if (!$create) {
+                return null;
+            }
+            $this->rowDimensions[$pRow] = new RowDimension($pRow);
+
+            $this->cachedHighestRow = max($this->cachedHighestRow, $pRow);
+        }
+
+        return $this->rowDimensions[$pRow];
+    }
+
+    /**
+     * Get column dimension at a specific column.
+     *
+     * @param string $pColumn String index of the column eg: 'A'
+     * @param bool $create
+     *
+     * @return ColumnDimension
+     */
+    public function getColumnDimension($pColumn, $create = true)
+    {
+        // Uppercase coordinate
+        $pColumn = strtoupper($pColumn);
+
+        // Fetch dimensions
+        if (!isset($this->columnDimensions[$pColumn])) {
+            if (!$create) {
+                return null;
+            }
+            $this->columnDimensions[$pColumn] = new ColumnDimension($pColumn);
+
+            if (Coordinate::columnIndexFromString($this->cachedHighestColumn) < Coordinate::columnIndexFromString($pColumn)) {
+                $this->cachedHighestColumn = $pColumn;
+            }
+        }
+
+        return $this->columnDimensions[$pColumn];
+    }
+
+    /**
+     * Get column dimension at a specific column by using numeric cell coordinates.
+     *
+     * @param int $columnIndex Numeric column coordinate of the cell
+     *
+     * @return ColumnDimension
+     */
+    public function getColumnDimensionByColumn($columnIndex)
+    {
+        return $this->getColumnDimension(Coordinate::stringFromColumnIndex($columnIndex));
+    }
+
+    /**
+     * Get styles.
+     *
+     * @return Style[]
+     */
+    public function getStyles()
+    {
+        return $this->styles;
+    }
+
+    /**
+     * Get style for cell.
+     *
+     * @param string $pCellCoordinate Cell coordinate (or range) to get style for, eg: 'A1'
+     *
+     * @return Style
+     */
+    public function getStyle($pCellCoordinate)
+    {
+        // set this sheet as active
+        $this->parent->setActiveSheetIndex($this->parent->getIndex($this));
+
+        // set cell coordinate as active
+        $this->setSelectedCells($pCellCoordinate);
+
+        return $this->parent->getCellXfSupervisor();
+    }
+
+    /**
+     * Get conditional styles for a cell.
+     *
+     * @param string $pCoordinate eg: 'A1'
+     *
+     * @return Conditional[]
+     */
+    public function getConditionalStyles($pCoordinate)
+    {
+        $pCoordinate = strtoupper($pCoordinate);
+        if (!isset($this->conditionalStylesCollection[$pCoordinate])) {
+            $this->conditionalStylesCollection[$pCoordinate] = [];
+        }
+
+        return $this->conditionalStylesCollection[$pCoordinate];
+    }
+
+    /**
+     * Do conditional styles exist for this cell?
+     *
+     * @param string $pCoordinate eg: 'A1'
+     *
+     * @return bool
+     */
+    public function conditionalStylesExists($pCoordinate)
+    {
+        return isset($this->conditionalStylesCollection[strtoupper($pCoordinate)]);
+    }
+
+    /**
+     * Removes conditional styles for a cell.
+     *
+     * @param string $pCoordinate eg: 'A1'
+     *
+     * @return $this
+     */
+    public function removeConditionalStyles($pCoordinate)
+    {
+        unset($this->conditionalStylesCollection[strtoupper($pCoordinate)]);
+
+        return $this;
+    }
+
+    /**
+     * Get collection of conditional styles.
+     *
+     * @return array
+     */
+    public function getConditionalStylesCollection()
+    {
+        return $this->conditionalStylesCollection;
+    }
+
+    /**
+     * Set conditional styles.
+     *
+     * @param string $pCoordinate eg: 'A1'
+     * @param $pValue Conditional[]
+     *
+     * @return $this
+     */
+    public function setConditionalStyles($pCoordinate, $pValue)
+    {
+        $this->conditionalStylesCollection[strtoupper($pCoordinate)] = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get style for cell by using numeric cell coordinates.
+     *
+     * @param int $columnIndex1 Numeric column coordinate of the cell
+     * @param int $row1 Numeric row coordinate of the cell
+     * @param null|int $columnIndex2 Numeric column coordinate of the range cell
+     * @param null|int $row2 Numeric row coordinate of the range cell
+     *
+     * @return Style
+     */
+    public function getStyleByColumnAndRow($columnIndex1, $row1, $columnIndex2 = null, $row2 = null)
+    {
+        if ($columnIndex2 !== null && $row2 !== null) {
+            $cellRange = Coordinate::stringFromColumnIndex($columnIndex1) . $row1 . ':' . Coordinate::stringFromColumnIndex($columnIndex2) . $row2;
+
+            return $this->getStyle($cellRange);
+        }
+
+        return $this->getStyle(Coordinate::stringFromColumnIndex($columnIndex1) . $row1);
+    }
+
+    /**
+     * Duplicate cell style to a range of cells.
+     *
+     * Please note that this will overwrite existing cell styles for cells in range!
+     *
+     * @param Style $pCellStyle Cell style to duplicate
+     * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
+     *
+     * @return $this
+     */
+    public function duplicateStyle(Style $pCellStyle, $pRange)
+    {
+        // Add the style to the workbook if necessary
+        $workbook = $this->parent;
+        if ($existingStyle = $this->parent->getCellXfByHashCode($pCellStyle->getHashCode())) {
+            // there is already such cell Xf in our collection
+            $xfIndex = $existingStyle->getIndex();
+        } else {
+            // we don't have such a cell Xf, need to add
+            $workbook->addCellXf($pCellStyle);
+            $xfIndex = $pCellStyle->getIndex();
+        }
+
+        // Calculate range outer borders
+        [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($pRange . ':' . $pRange);
+
+        // Make sure we can loop upwards on rows and columns
+        if ($rangeStart[0] > $rangeEnd[0] && $rangeStart[1] > $rangeEnd[1]) {
+            $tmp = $rangeStart;
+            $rangeStart = $rangeEnd;
+            $rangeEnd = $tmp;
+        }
+
+        // Loop through cells and apply styles
+        for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
+            for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
+                $this->getCell(Coordinate::stringFromColumnIndex($col) . $row)->setXfIndex($xfIndex);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Duplicate conditional style to a range of cells.
+     *
+     * Please note that this will overwrite existing cell styles for cells in range!
+     *
+     * @param Conditional[] $pCellStyle Cell style to duplicate
+     * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
+     *
+     * @return $this
+     */
+    public function duplicateConditionalStyle(array $pCellStyle, $pRange = '')
+    {
+        foreach ($pCellStyle as $cellStyle) {
+            if (!($cellStyle instanceof Conditional)) {
+                throw new Exception('Style is not a conditional style');
+            }
+        }
+
+        // Calculate range outer borders
+        [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($pRange . ':' . $pRange);
+
+        // Make sure we can loop upwards on rows and columns
+        if ($rangeStart[0] > $rangeEnd[0] && $rangeStart[1] > $rangeEnd[1]) {
+            $tmp = $rangeStart;
+            $rangeStart = $rangeEnd;
+            $rangeEnd = $tmp;
+        }
+
+        // Loop through cells and apply styles
+        for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
+            for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
+                $this->setConditionalStyles(Coordinate::stringFromColumnIndex($col) . $row, $pCellStyle);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Set break on a cell.
+     *
+     * @param string $pCoordinate Cell coordinate (e.g. A1)
+     * @param int $pBreak Break type (type of Worksheet::BREAK_*)
+     *
+     * @return $this
+     */
+    public function setBreak($pCoordinate, $pBreak)
+    {
+        // Uppercase coordinate
+        $pCoordinate = strtoupper($pCoordinate);
+
+        if ($pCoordinate != '') {
+            if ($pBreak == self::BREAK_NONE) {
+                if (isset($this->breaks[$pCoordinate])) {
+                    unset($this->breaks[$pCoordinate]);
+                }
+            } else {
+                $this->breaks[$pCoordinate] = $pBreak;
+            }
+        } else {
+            throw new Exception('No cell coordinate specified.');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Set break on a cell by using numeric cell coordinates.
+     *
+     * @param int $columnIndex Numeric column coordinate of the cell
+     * @param int $row Numeric row coordinate of the cell
+     * @param int $break Break type (type of Worksheet::BREAK_*)
+     *
+     * @return $this
+     */
+    public function setBreakByColumnAndRow($columnIndex, $row, $break)
+    {
+        return $this->setBreak(Coordinate::stringFromColumnIndex($columnIndex) . $row, $break);
+    }
+
+    /**
+     * Get breaks.
+     *
+     * @return array[]
+     */
+    public function getBreaks()
+    {
+        return $this->breaks;
+    }
+
+    /**
+     * Set merge on a cell range.
+     *
+     * @param string $pRange Cell range (e.g. A1:E1)
+     *
+     * @return $this
+     */
+    public function mergeCells($pRange)
+    {
+        // Uppercase coordinate
+        $pRange = strtoupper($pRange);
+
+        if (strpos($pRange, ':') !== false) {
+            $this->mergeCells[$pRange] = $pRange;
+
+            // make sure cells are created
+
+            // get the cells in the range
+            $aReferences = Coordinate::extractAllCellReferencesInRange($pRange);
+
+            // create upper left cell if it does not already exist
+            $upperLeft = $aReferences[0];
+            if (!$this->cellExists($upperLeft)) {
+                $this->getCell($upperLeft)->setValueExplicit(null, DataType::TYPE_NULL);
+            }
+
+            // Blank out the rest of the cells in the range (if they exist)
+            $count = count($aReferences);
+            for ($i = 1; $i < $count; ++$i) {
+                if ($this->cellExists($aReferences[$i])) {
+                    $this->getCell($aReferences[$i])->setValueExplicit(null, DataType::TYPE_NULL);
+                }
+            }
+        } else {
+            throw new Exception('Merge must be set on a range of cells.');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Set merge on a cell range by using numeric cell coordinates.
+     *
+     * @param int $columnIndex1 Numeric column coordinate of the first cell
+     * @param int $row1 Numeric row coordinate of the first cell
+     * @param int $columnIndex2 Numeric column coordinate of the last cell
+     * @param int $row2 Numeric row coordinate of the last cell
+     *
+     * @return $this
+     */
+    public function mergeCellsByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2)
+    {
+        $cellRange = Coordinate::stringFromColumnIndex($columnIndex1) . $row1 . ':' . Coordinate::stringFromColumnIndex($columnIndex2) . $row2;
+
+        return $this->mergeCells($cellRange);
+    }
+
+    /**
+     * Remove merge on a cell range.
+     *
+     * @param string $pRange Cell range (e.g. A1:E1)
+     *
+     * @return $this
+     */
+    public function unmergeCells($pRange)
+    {
+        // Uppercase coordinate
+        $pRange = strtoupper($pRange);
+
+        if (strpos($pRange, ':') !== false) {
+            if (isset($this->mergeCells[$pRange])) {
+                unset($this->mergeCells[$pRange]);
+            } else {
+                throw new Exception('Cell range ' . $pRange . ' not known as merged.');
+            }
+        } else {
+            throw new Exception('Merge can only be removed from a range of cells.');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Remove merge on a cell range by using numeric cell coordinates.
+     *
+     * @param int $columnIndex1 Numeric column coordinate of the first cell
+     * @param int $row1 Numeric row coordinate of the first cell
+     * @param int $columnIndex2 Numeric column coordinate of the last cell
+     * @param int $row2 Numeric row coordinate of the last cell
+     *
+     * @return $this
+     */
+    public function unmergeCellsByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2)
+    {
+        $cellRange = Coordinate::stringFromColumnIndex($columnIndex1) . $row1 . ':' . Coordinate::stringFromColumnIndex($columnIndex2) . $row2;
+
+        return $this->unmergeCells($cellRange);
+    }
+
+    /**
+     * Get merge cells array.
+     *
+     * @return string[]
+     */
+    public function getMergeCells()
+    {
+        return $this->mergeCells;
+    }
+
+    /**
+     * Set merge cells array for the entire sheet. Use instead mergeCells() to merge
+     * a single cell range.
+     *
+     * @param string[] $pValue
+     *
+     * @return $this
+     */
+    public function setMergeCells(array $pValue)
+    {
+        $this->mergeCells = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Set protection on a cell range.
+     *
+     * @param string $pRange Cell (e.g. A1) or cell range (e.g. A1:E1)
+     * @param string $pPassword Password to unlock the protection
+     * @param bool $pAlreadyHashed If the password has already been hashed, set this to true
+     *
+     * @return $this
+     */
+    public function protectCells($pRange, $pPassword, $pAlreadyHashed = false)
+    {
+        // Uppercase coordinate
+        $pRange = strtoupper($pRange);
+
+        if (!$pAlreadyHashed) {
+            $pPassword = Shared\PasswordHasher::hashPassword($pPassword);
+        }
+        $this->protectedCells[$pRange] = $pPassword;
+
+        return $this;
+    }
+
+    /**
+     * Set protection on a cell range by using numeric cell coordinates.
+     *
+     * @param int $columnIndex1 Numeric column coordinate of the first cell
+     * @param int $row1 Numeric row coordinate of the first cell
+     * @param int $columnIndex2 Numeric column coordinate of the last cell
+     * @param int $row2 Numeric row coordinate of the last cell
+     * @param string $password Password to unlock the protection
+     * @param bool $alreadyHashed If the password has already been hashed, set this to true
+     *
+     * @return $this
+     */
+    public function protectCellsByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2, $password, $alreadyHashed = false)
+    {
+        $cellRange = Coordinate::stringFromColumnIndex($columnIndex1) . $row1 . ':' . Coordinate::stringFromColumnIndex($columnIndex2) . $row2;
+
+        return $this->protectCells($cellRange, $password, $alreadyHashed);
+    }
+
+    /**
+     * Remove protection on a cell range.
+     *
+     * @param string $pRange Cell (e.g. A1) or cell range (e.g. A1:E1)
+     *
+     * @return $this
+     */
+    public function unprotectCells($pRange)
+    {
+        // Uppercase coordinate
+        $pRange = strtoupper($pRange);
+
+        if (isset($this->protectedCells[$pRange])) {
+            unset($this->protectedCells[$pRange]);
+        } else {
+            throw new Exception('Cell range ' . $pRange . ' not known as protected.');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Remove protection on a cell range by using numeric cell coordinates.
+     *
+     * @param int $columnIndex1 Numeric column coordinate of the first cell
+     * @param int $row1 Numeric row coordinate of the first cell
+     * @param int $columnIndex2 Numeric column coordinate of the last cell
+     * @param int $row2 Numeric row coordinate of the last cell
+     *
+     * @return $this
+     */
+    public function unprotectCellsByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2)
+    {
+        $cellRange = Coordinate::stringFromColumnIndex($columnIndex1) . $row1 . ':' . Coordinate::stringFromColumnIndex($columnIndex2) . $row2;
+
+        return $this->unprotectCells($cellRange);
+    }
+
+    /**
+     * Get protected cells.
+     *
+     * @return array[]
+     */
+    public function getProtectedCells()
+    {
+        return $this->protectedCells;
+    }
+
+    /**
+     * Get Autofilter.
+     *
+     * @return AutoFilter
+     */
+    public function getAutoFilter()
+    {
+        return $this->autoFilter;
+    }
+
+    /**
+     * Set AutoFilter.
+     *
+     * @param AutoFilter|string $pValue
+     *            A simple string containing a Cell range like 'A1:E10' is permitted for backward compatibility
+     *
+     * @return $this
+     */
+    public function setAutoFilter($pValue)
+    {
+        if (is_string($pValue)) {
+            $this->autoFilter->setRange($pValue);
+        } elseif (is_object($pValue) && ($pValue instanceof AutoFilter)) {
+            $this->autoFilter = $pValue;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Set Autofilter Range by using numeric cell coordinates.
+     *
+     * @param int $columnIndex1 Numeric column coordinate of the first cell
+     * @param int $row1 Numeric row coordinate of the first cell
+     * @param int $columnIndex2 Numeric column coordinate of the second cell
+     * @param int $row2 Numeric row coordinate of the second cell
+     *
+     * @return $this
+     */
+    public function setAutoFilterByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2)
+    {
+        return $this->setAutoFilter(
+            Coordinate::stringFromColumnIndex($columnIndex1) . $row1
+            . ':' .
+            Coordinate::stringFromColumnIndex($columnIndex2) . $row2
+        );
+    }
+
+    /**
+     * Remove autofilter.
+     *
+     * @return $this
+     */
+    public function removeAutoFilter()
+    {
+        $this->autoFilter->setRange(null);
+
+        return $this;
+    }
+
+    /**
+     * Get Freeze Pane.
+     *
+     * @return string
+     */
+    public function getFreezePane()
+    {
+        return $this->freezePane;
+    }
+
+    /**
+     * Freeze Pane.
+     *
+     * Examples:
+     *
+     *     - A2 will freeze the rows above cell A2 (i.e row 1)
+     *     - B1 will freeze the columns to the left of cell B1 (i.e column A)
+     *     - B2 will freeze the rows above and to the left of cell B2 (i.e row 1 and column A)
+     *
+     * @param null|string $cell Position of the split
+     * @param null|string $topLeftCell default position of the right bottom pane
+     *
+     * @return $this
+     */
+    public function freezePane($cell, $topLeftCell = null)
+    {
+        if (is_string($cell) && Coordinate::coordinateIsRange($cell)) {
+            throw new Exception('Freeze pane can not be set on a range of cells.');
+        }
+
+        if ($cell !== null && $topLeftCell === null) {
+            $coordinate = Coordinate::coordinateFromString($cell);
+            $topLeftCell = $coordinate[0] . $coordinate[1];
+        }
+
+        $this->freezePane = $cell;
+        $this->topLeftCell = $topLeftCell;
+
+        return $this;
+    }
+
+    /**
+     * Freeze Pane by using numeric cell coordinates.
+     *
+     * @param int $columnIndex Numeric column coordinate of the cell
+     * @param int $row Numeric row coordinate of the cell
+     *
+     * @return $this
+     */
+    public function freezePaneByColumnAndRow($columnIndex, $row)
+    {
+        return $this->freezePane(Coordinate::stringFromColumnIndex($columnIndex) . $row);
+    }
+
+    /**
+     * Unfreeze Pane.
+     *
+     * @return $this
+     */
+    public function unfreezePane()
+    {
+        return $this->freezePane(null);
+    }
+
+    /**
+     * Get the default position of the right bottom pane.
+     *
+     * @return int
+     */
+    public function getTopLeftCell()
+    {
+        return $this->topLeftCell;
+    }
+
+    /**
+     * Insert a new row, updating all possible related data.
+     *
+     * @param int $pBefore Insert before this one
+     * @param int $pNumRows Number of rows to insert
+     *
+     * @return $this
+     */
+    public function insertNewRowBefore($pBefore, $pNumRows = 1)
+    {
+        if ($pBefore >= 1) {
+            $objReferenceHelper = ReferenceHelper::getInstance();
+            $objReferenceHelper->insertNewBefore('A' . $pBefore, 0, $pNumRows, $this);
+        } else {
+            throw new Exception('Rows can only be inserted before at least row 1.');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Insert a new column, updating all possible related data.
+     *
+     * @param string $pBefore Insert before this one, eg: 'A'
+     * @param int $pNumCols Number of columns to insert
+     *
+     * @return $this
+     */
+    public function insertNewColumnBefore($pBefore, $pNumCols = 1)
+    {
+        if (!is_numeric($pBefore)) {
+            $objReferenceHelper = ReferenceHelper::getInstance();
+            $objReferenceHelper->insertNewBefore($pBefore . '1', $pNumCols, 0, $this);
+        } else {
+            throw new Exception('Column references should not be numeric.');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Insert a new column, updating all possible related data.
+     *
+     * @param int $beforeColumnIndex Insert before this one (numeric column coordinate of the cell)
+     * @param int $pNumCols Number of columns to insert
+     *
+     * @return $this
+     */
+    public function insertNewColumnBeforeByIndex($beforeColumnIndex, $pNumCols = 1)
+    {
+        if ($beforeColumnIndex >= 1) {
+            return $this->insertNewColumnBefore(Coordinate::stringFromColumnIndex($beforeColumnIndex), $pNumCols);
+        }
+
+        throw new Exception('Columns can only be inserted before at least column A (1).');
+    }
+
+    /**
+     * Delete a row, updating all possible related data.
+     *
+     * @param int $pRow Remove starting with this one
+     * @param int $pNumRows Number of rows to remove
+     *
+     * @return $this
+     */
+    public function removeRow($pRow, $pNumRows = 1)
+    {
+        if ($pRow < 1) {
+            throw new Exception('Rows to be deleted should at least start from row 1.');
+        }
+
+        $highestRow = $this->getHighestDataRow();
+        $removedRowsCounter = 0;
+
+        for ($r = 0; $r < $pNumRows; ++$r) {
+            if ($pRow + $r <= $highestRow) {
+                $this->getCellCollection()->removeRow($pRow + $r);
+                ++$removedRowsCounter;
+            }
+        }
+
+        $objReferenceHelper = ReferenceHelper::getInstance();
+        $objReferenceHelper->insertNewBefore('A' . ($pRow + $pNumRows), 0, -$pNumRows, $this);
+        for ($r = 0; $r < $removedRowsCounter; ++$r) {
+            $this->getCellCollection()->removeRow($highestRow);
+            --$highestRow;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Remove a column, updating all possible related data.
+     *
+     * @param string $pColumn Remove starting with this one, eg: 'A'
+     * @param int $pNumCols Number of columns to remove
+     *
+     * @return $this
+     */
+    public function removeColumn($pColumn, $pNumCols = 1)
+    {
+        if (is_numeric($pColumn)) {
+            throw new Exception('Column references should not be numeric.');
+        }
+
+        $highestColumn = $this->getHighestDataColumn();
+        $highestColumnIndex = Coordinate::columnIndexFromString($highestColumn);
+        $pColumnIndex = Coordinate::columnIndexFromString($pColumn);
+
+        if ($pColumnIndex > $highestColumnIndex) {
+            return $this;
+        }
+
+        $pColumn = Coordinate::stringFromColumnIndex($pColumnIndex + $pNumCols);
+        $objReferenceHelper = ReferenceHelper::getInstance();
+        $objReferenceHelper->insertNewBefore($pColumn . '1', -$pNumCols, 0, $this);
+
+        $maxPossibleColumnsToBeRemoved = $highestColumnIndex - $pColumnIndex + 1;
+
+        for ($c = 0, $n = min($maxPossibleColumnsToBeRemoved, $pNumCols); $c < $n; ++$c) {
+            $this->getCellCollection()->removeColumn($highestColumn);
+            $highestColumn = Coordinate::stringFromColumnIndex(Coordinate::columnIndexFromString($highestColumn) - 1);
+        }
+
+        $this->garbageCollect();
+
+        return $this;
+    }
+
+    /**
+     * Remove a column, updating all possible related data.
+     *
+     * @param int $columnIndex Remove starting with this one (numeric column coordinate of the cell)
+     * @param int $numColumns Number of columns to remove
+     *
+     * @return $this
+     */
+    public function removeColumnByIndex($columnIndex, $numColumns = 1)
+    {
+        if ($columnIndex >= 1) {
+            return $this->removeColumn(Coordinate::stringFromColumnIndex($columnIndex), $numColumns);
+        }
+
+        throw new Exception('Columns to be deleted should at least start from column A (1)');
+    }
+
+    /**
+     * Show gridlines?
+     *
+     * @return bool
+     */
+    public function getShowGridlines()
+    {
+        return $this->showGridlines;
+    }
+
+    /**
+     * Set show gridlines.
+     *
+     * @param bool $pValue Show gridlines (true/false)
+     *
+     * @return $this
+     */
+    public function setShowGridlines($pValue)
+    {
+        $this->showGridlines = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Print gridlines?
+     *
+     * @return bool
+     */
+    public function getPrintGridlines()
+    {
+        return $this->printGridlines;
+    }
+
+    /**
+     * Set print gridlines.
+     *
+     * @param bool $pValue Print gridlines (true/false)
+     *
+     * @return $this
+     */
+    public function setPrintGridlines($pValue)
+    {
+        $this->printGridlines = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Show row and column headers?
+     *
+     * @return bool
+     */
+    public function getShowRowColHeaders()
+    {
+        return $this->showRowColHeaders;
+    }
+
+    /**
+     * Set show row and column headers.
+     *
+     * @param bool $pValue Show row and column headers (true/false)
+     *
+     * @return $this
+     */
+    public function setShowRowColHeaders($pValue)
+    {
+        $this->showRowColHeaders = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Show summary below? (Row/Column outlining).
+     *
+     * @return bool
+     */
+    public function getShowSummaryBelow()
+    {
+        return $this->showSummaryBelow;
+    }
+
+    /**
+     * Set show summary below.
+     *
+     * @param bool $pValue Show summary below (true/false)
+     *
+     * @return $this
+     */
+    public function setShowSummaryBelow($pValue)
+    {
+        $this->showSummaryBelow = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Show summary right? (Row/Column outlining).
+     *
+     * @return bool
+     */
+    public function getShowSummaryRight()
+    {
+        return $this->showSummaryRight;
+    }
+
+    /**
+     * Set show summary right.
+     *
+     * @param bool $pValue Show summary right (true/false)
+     *
+     * @return $this
+     */
+    public function setShowSummaryRight($pValue)
+    {
+        $this->showSummaryRight = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get comments.
+     *
+     * @return Comment[]
+     */
+    public function getComments()
+    {
+        return $this->comments;
+    }
+
+    /**
+     * Set comments array for the entire sheet.
+     *
+     * @param Comment[] $pValue
+     *
+     * @return $this
+     */
+    public function setComments(array $pValue)
+    {
+        $this->comments = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get comment for cell.
+     *
+     * @param string $pCellCoordinate Cell coordinate to get comment for, eg: 'A1'
+     *
+     * @return Comment
+     */
+    public function getComment($pCellCoordinate)
+    {
+        // Uppercase coordinate
+        $pCellCoordinate = strtoupper($pCellCoordinate);
+
+        if (Coordinate::coordinateIsRange($pCellCoordinate)) {
+            throw new Exception('Cell coordinate string can not be a range of cells.');
+        } elseif (strpos($pCellCoordinate, '$') !== false) {
+            throw new Exception('Cell coordinate string must not be absolute.');
+        } elseif ($pCellCoordinate == '') {
+            throw new Exception('Cell coordinate can not be zero-length string.');
+        }
+
+        // Check if we already have a comment for this cell.
+        if (isset($this->comments[$pCellCoordinate])) {
+            return $this->comments[$pCellCoordinate];
+        }
+
+        // If not, create a new comment.
+        $newComment = new Comment();
+        $this->comments[$pCellCoordinate] = $newComment;
+
+        return $newComment;
+    }
+
+    /**
+     * Get comment for cell by using numeric cell coordinates.
+     *
+     * @param int $columnIndex Numeric column coordinate of the cell
+     * @param int $row Numeric row coordinate of the cell
+     *
+     * @return Comment
+     */
+    public function getCommentByColumnAndRow($columnIndex, $row)
+    {
+        return $this->getComment(Coordinate::stringFromColumnIndex($columnIndex) . $row);
+    }
+
+    /**
+     * Get active cell.
+     *
+     * @return string Example: 'A1'
+     */
+    public function getActiveCell()
+    {
+        return $this->activeCell;
+    }
+
+    /**
+     * Get selected cells.
+     *
+     * @return string
+     */
+    public function getSelectedCells()
+    {
+        return $this->selectedCells;
+    }
+
+    /**
+     * Selected cell.
+     *
+     * @param string $pCoordinate Cell (i.e. A1)
+     *
+     * @return $this
+     */
+    public function setSelectedCell($pCoordinate)
+    {
+        return $this->setSelectedCells($pCoordinate);
+    }
+
+    /**
+     * Select a range of cells.
+     *
+     * @param string $pCoordinate Cell range, examples: 'A1', 'B2:G5', 'A:C', '3:6'
+     *
+     * @return $this
+     */
+    public function setSelectedCells($pCoordinate)
+    {
+        // Uppercase coordinate
+        $pCoordinate = strtoupper($pCoordinate);
+
+        // Convert 'A' to 'A:A'
+        $pCoordinate = preg_replace('/^([A-Z]+)$/', '${1}:${1}', $pCoordinate);
+
+        // Convert '1' to '1:1'
+        $pCoordinate = preg_replace('/^(\d+)$/', '${1}:${1}', $pCoordinate);
+
+        // Convert 'A:C' to 'A1:C1048576'
+        $pCoordinate = preg_replace('/^([A-Z]+):([A-Z]+)$/', '${1}1:${2}1048576', $pCoordinate);
+
+        // Convert '1:3' to 'A1:XFD3'
+        $pCoordinate = preg_replace('/^(\d+):(\d+)$/', 'A${1}:XFD${2}', $pCoordinate);
+
+        if (Coordinate::coordinateIsRange($pCoordinate)) {
+            [$first] = Coordinate::splitRange($pCoordinate);
+            $this->activeCell = $first[0];
+        } else {
+            $this->activeCell = $pCoordinate;
+        }
+        $this->selectedCells = $pCoordinate;
+
+        return $this;
+    }
+
+    /**
+     * Selected cell by using numeric cell coordinates.
+     *
+     * @param int $columnIndex Numeric column coordinate of the cell
+     * @param int $row Numeric row coordinate of the cell
+     *
+     * @return $this
+     */
+    public function setSelectedCellByColumnAndRow($columnIndex, $row)
+    {
+        return $this->setSelectedCells(Coordinate::stringFromColumnIndex($columnIndex) . $row);
+    }
+
+    /**
+     * Get right-to-left.
+     *
+     * @return bool
+     */
+    public function getRightToLeft()
+    {
+        return $this->rightToLeft;
+    }
+
+    /**
+     * Set right-to-left.
+     *
+     * @param bool $value Right-to-left true/false
+     *
+     * @return $this
+     */
+    public function setRightToLeft($value)
+    {
+        $this->rightToLeft = $value;
+
+        return $this;
+    }
+
+    /**
+     * Fill worksheet from values in array.
+     *
+     * @param array $source Source array
+     * @param mixed $nullValue Value in source array that stands for blank cell
+     * @param string $startCell Insert array starting from this cell address as the top left coordinate
+     * @param bool $strictNullComparison Apply strict comparison when testing for null values in the array
+     *
+     * @return $this
+     */
+    public function fromArray(array $source, $nullValue = null, $startCell = 'A1', $strictNullComparison = false)
+    {
+        //    Convert a 1-D array to 2-D (for ease of looping)
+        if (!is_array(end($source))) {
+            $source = [$source];
+        }
+
+        // start coordinate
+        [$startColumn, $startRow] = Coordinate::coordinateFromString($startCell);
+
+        // Loop through $source
+        foreach ($source as $rowData) {
+            $currentColumn = $startColumn;
+            foreach ($rowData as $cellValue) {
+                if ($strictNullComparison) {
+                    if ($cellValue !== $nullValue) {
+                        // Set cell value
+                        $this->getCell($currentColumn . $startRow)->setValue($cellValue);
+                    }
+                } else {
+                    if ($cellValue != $nullValue) {
+                        // Set cell value
+                        $this->getCell($currentColumn . $startRow)->setValue($cellValue);
+                    }
+                }
+                ++$currentColumn;
+            }
+            ++$startRow;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Create array from a range of cells.
+     *
+     * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
+     * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
+     * @param bool $calculateFormulas Should formulas be calculated?
+     * @param bool $formatData Should formatting be applied to cell values?
+     * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
+     *                               True - Return rows and columns indexed by their actual row and column IDs
+     *
+     * @return array
+     */
+    public function rangeToArray($pRange, $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
+    {
+        // Returnvalue
+        $returnValue = [];
+        //    Identify the range that we need to extract from the worksheet
+        [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($pRange);
+        $minCol = Coordinate::stringFromColumnIndex($rangeStart[0]);
+        $minRow = $rangeStart[1];
+        $maxCol = Coordinate::stringFromColumnIndex($rangeEnd[0]);
+        $maxRow = $rangeEnd[1];
+
+        ++$maxCol;
+        // Loop through rows
+        $r = -1;
+        for ($row = $minRow; $row <= $maxRow; ++$row) {
+            $rRef = $returnCellRef ? $row : ++$r;
+            $c = -1;
+            // Loop through columns in the current row
+            for ($col = $minCol; $col != $maxCol; ++$col) {
+                $cRef = $returnCellRef ? $col : ++$c;
+                //    Using getCell() will create a new cell if it doesn't already exist. We don't want that to happen
+                //        so we test and retrieve directly against cellCollection
+                if ($this->cellCollection->has($col . $row)) {
+                    // Cell exists
+                    $cell = $this->cellCollection->get($col . $row);
+                    if ($cell->getValue() !== null) {
+                        if ($cell->getValue() instanceof RichText) {
+                            $returnValue[$rRef][$cRef] = $cell->getValue()->getPlainText();
+                        } else {
+                            if ($calculateFormulas) {
+                                $returnValue[$rRef][$cRef] = $cell->getCalculatedValue();
+                            } else {
+                                $returnValue[$rRef][$cRef] = $cell->getValue();
+                            }
+                        }
+
+                        if ($formatData) {
+                            $style = $this->parent->getCellXfByIndex($cell->getXfIndex());
+                            $returnValue[$rRef][$cRef] = NumberFormat::toFormattedString(
+                                $returnValue[$rRef][$cRef],
+                                ($style && $style->getNumberFormat()) ? $style->getNumberFormat()->getFormatCode() : NumberFormat::FORMAT_GENERAL
+                            );
+                        }
+                    } else {
+                        // Cell holds a NULL
+                        $returnValue[$rRef][$cRef] = $nullValue;
+                    }
+                } else {
+                    // Cell doesn't exist
+                    $returnValue[$rRef][$cRef] = $nullValue;
+                }
+            }
+        }
+
+        // Return
+        return $returnValue;
+    }
+
+    /**
+     * Create array from a range of cells.
+     *
+     * @param string $pNamedRange Name of the Named Range
+     * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
+     * @param bool $calculateFormulas Should formulas be calculated?
+     * @param bool $formatData Should formatting be applied to cell values?
+     * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
+     *                                True - Return rows and columns indexed by their actual row and column IDs
+     *
+     * @return array
+     */
+    public function namedRangeToArray($pNamedRange, $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
+    {
+        $namedRange = NamedRange::resolveRange($pNamedRange, $this);
+        if ($namedRange !== null) {
+            $pWorkSheet = $namedRange->getWorksheet();
+            $pCellRange = $namedRange->getRange();
+
+            return $pWorkSheet->rangeToArray($pCellRange, $nullValue, $calculateFormulas, $formatData, $returnCellRef);
+        }
+
+        throw new Exception('Named Range ' . $pNamedRange . ' does not exist.');
+    }
+
+    /**
+     * Create array from worksheet.
+     *
+     * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
+     * @param bool $calculateFormulas Should formulas be calculated?
+     * @param bool $formatData Should formatting be applied to cell values?
+     * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
+     *                               True - Return rows and columns indexed by their actual row and column IDs
+     *
+     * @return array
+     */
+    public function toArray($nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
+    {
+        // Garbage collect...
+        $this->garbageCollect();
+
+        //    Identify the range that we need to extract from the worksheet
+        $maxCol = $this->getHighestColumn();
+        $maxRow = $this->getHighestRow();
+
+        // Return
+        return $this->rangeToArray('A1:' . $maxCol . $maxRow, $nullValue, $calculateFormulas, $formatData, $returnCellRef);
+    }
+
+    /**
+     * Get row iterator.
+     *
+     * @param int $startRow The row number at which to start iterating
+     * @param int $endRow The row number at which to stop iterating
+     *
+     * @return RowIterator
+     */
+    public function getRowIterator($startRow = 1, $endRow = null)
+    {
+        return new RowIterator($this, $startRow, $endRow);
+    }
+
+    /**
+     * Get column iterator.
+     *
+     * @param string $startColumn The column address at which to start iterating
+     * @param string $endColumn The column address at which to stop iterating
+     *
+     * @return ColumnIterator
+     */
+    public function getColumnIterator($startColumn = 'A', $endColumn = null)
+    {
+        return new ColumnIterator($this, $startColumn, $endColumn);
+    }
+
+    /**
+     * Run PhpSpreadsheet garbage collector.
+     *
+     * @return $this
+     */
+    public function garbageCollect()
+    {
+        // Flush cache
+        $this->cellCollection->get('A1');
+
+        // Lookup highest column and highest row if cells are cleaned
+        $colRow = $this->cellCollection->getHighestRowAndColumn();
+        $highestRow = $colRow['row'];
+        $highestColumn = Coordinate::columnIndexFromString($colRow['column']);
+
+        // Loop through column dimensions
+        foreach ($this->columnDimensions as $dimension) {
+            $highestColumn = max($highestColumn, Coordinate::columnIndexFromString($dimension->getColumnIndex()));
+        }
+
+        // Loop through row dimensions
+        foreach ($this->rowDimensions as $dimension) {
+            $highestRow = max($highestRow, $dimension->getRowIndex());
+        }
+
+        // Cache values
+        if ($highestColumn < 1) {
+            $this->cachedHighestColumn = 'A';
+        } else {
+            $this->cachedHighestColumn = Coordinate::stringFromColumnIndex($highestColumn);
+        }
+        $this->cachedHighestRow = $highestRow;
+
+        // Return
+        return $this;
+    }
+
+    /**
+     * Get hash code.
+     *
+     * @return string Hash code
+     */
+    public function getHashCode()
+    {
+        if ($this->dirty) {
+            $this->hash = md5($this->title . $this->autoFilter . ($this->protection->isProtectionEnabled() ? 't' : 'f') . __CLASS__);
+            $this->dirty = false;
+        }
+
+        return $this->hash;
+    }
+
+    /**
+     * Extract worksheet title from range.
+     *
+     * Example: extractSheetTitle("testSheet!A1") ==> 'A1'
+     * Example: extractSheetTitle("'testSheet 1'!A1", true) ==> ['testSheet 1', 'A1'];
+     *
+     * @param string $pRange Range to extract title from
+     * @param bool $returnRange Return range? (see example)
+     *
+     * @return mixed
+     */
+    public static function extractSheetTitle($pRange, $returnRange = false)
+    {
+        // Sheet title included?
+        if (($sep = strrpos($pRange, '!')) === false) {
+            return $returnRange ? ['', $pRange] : '';
+        }
+
+        if ($returnRange) {
+            return [substr($pRange, 0, $sep), substr($pRange, $sep + 1)];
+        }
+
+        return substr($pRange, $sep + 1);
+    }
+
+    /**
+     * Get hyperlink.
+     *
+     * @param string $pCellCoordinate Cell coordinate to get hyperlink for, eg: 'A1'
+     *
+     * @return Hyperlink
+     */
+    public function getHyperlink($pCellCoordinate)
+    {
+        // return hyperlink if we already have one
+        if (isset($this->hyperlinkCollection[$pCellCoordinate])) {
+            return $this->hyperlinkCollection[$pCellCoordinate];
+        }
+
+        // else create hyperlink
+        $this->hyperlinkCollection[$pCellCoordinate] = new Hyperlink();
+
+        return $this->hyperlinkCollection[$pCellCoordinate];
+    }
+
+    /**
+     * Set hyperlink.
+     *
+     * @param string $pCellCoordinate Cell coordinate to insert hyperlink, eg: 'A1'
+     *
+     * @return $this
+     */
+    public function setHyperlink($pCellCoordinate, ?Hyperlink $pHyperlink = null)
+    {
+        if ($pHyperlink === null) {
+            unset($this->hyperlinkCollection[$pCellCoordinate]);
+        } else {
+            $this->hyperlinkCollection[$pCellCoordinate] = $pHyperlink;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Hyperlink at a specific coordinate exists?
+     *
+     * @param string $pCoordinate eg: 'A1'
+     *
+     * @return bool
+     */
+    public function hyperlinkExists($pCoordinate)
+    {
+        return isset($this->hyperlinkCollection[$pCoordinate]);
+    }
+
+    /**
+     * Get collection of hyperlinks.
+     *
+     * @return Hyperlink[]
+     */
+    public function getHyperlinkCollection()
+    {
+        return $this->hyperlinkCollection;
+    }
+
+    /**
+     * Get data validation.
+     *
+     * @param string $pCellCoordinate Cell coordinate to get data validation for, eg: 'A1'
+     *
+     * @return DataValidation
+     */
+    public function getDataValidation($pCellCoordinate)
+    {
+        // return data validation if we already have one
+        if (isset($this->dataValidationCollection[$pCellCoordinate])) {
+            return $this->dataValidationCollection[$pCellCoordinate];
+        }
+
+        // else create data validation
+        $this->dataValidationCollection[$pCellCoordinate] = new DataValidation();
+
+        return $this->dataValidationCollection[$pCellCoordinate];
+    }
+
+    /**
+     * Set data validation.
+     *
+     * @param string $pCellCoordinate Cell coordinate to insert data validation, eg: 'A1'
+     *
+     * @return $this
+     */
+    public function setDataValidation($pCellCoordinate, ?DataValidation $pDataValidation = null)
+    {
+        if ($pDataValidation === null) {
+            unset($this->dataValidationCollection[$pCellCoordinate]);
+        } else {
+            $this->dataValidationCollection[$pCellCoordinate] = $pDataValidation;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Data validation at a specific coordinate exists?
+     *
+     * @param string $pCoordinate eg: 'A1'
+     *
+     * @return bool
+     */
+    public function dataValidationExists($pCoordinate)
+    {
+        return isset($this->dataValidationCollection[$pCoordinate]);
+    }
+
+    /**
+     * Get collection of data validations.
+     *
+     * @return DataValidation[]
+     */
+    public function getDataValidationCollection()
+    {
+        return $this->dataValidationCollection;
+    }
+
+    /**
+     * Accepts a range, returning it as a range that falls within the current highest row and column of the worksheet.
+     *
+     * @param string $range
+     *
+     * @return string Adjusted range value
+     */
+    public function shrinkRangeToFit($range)
+    {
+        $maxCol = $this->getHighestColumn();
+        $maxRow = $this->getHighestRow();
+        $maxCol = Coordinate::columnIndexFromString($maxCol);
+
+        $rangeBlocks = explode(' ', $range);
+        foreach ($rangeBlocks as &$rangeSet) {
+            $rangeBoundaries = Coordinate::getRangeBoundaries($rangeSet);
+
+            if (Coordinate::columnIndexFromString($rangeBoundaries[0][0]) > $maxCol) {
+                $rangeBoundaries[0][0] = Coordinate::stringFromColumnIndex($maxCol);
+            }
+            if ($rangeBoundaries[0][1] > $maxRow) {
+                $rangeBoundaries[0][1] = $maxRow;
+            }
+            if (Coordinate::columnIndexFromString($rangeBoundaries[1][0]) > $maxCol) {
+                $rangeBoundaries[1][0] = Coordinate::stringFromColumnIndex($maxCol);
+            }
+            if ($rangeBoundaries[1][1] > $maxRow) {
+                $rangeBoundaries[1][1] = $maxRow;
+            }
+            $rangeSet = $rangeBoundaries[0][0] . $rangeBoundaries[0][1] . ':' . $rangeBoundaries[1][0] . $rangeBoundaries[1][1];
+        }
+        unset($rangeSet);
+
+        return implode(' ', $rangeBlocks);
+    }
+
+    /**
+     * Get tab color.
+     *
+     * @return Color
+     */
+    public function getTabColor()
+    {
+        if ($this->tabColor === null) {
+            $this->tabColor = new Color();
+        }
+
+        return $this->tabColor;
+    }
+
+    /**
+     * Reset tab color.
+     *
+     * @return $this
+     */
+    public function resetTabColor()
+    {
+        $this->tabColor = null;
+        $this->tabColor = null;
+
+        return $this;
+    }
+
+    /**
+     * Tab color set?
+     *
+     * @return bool
+     */
+    public function isTabColorSet()
+    {
+        return $this->tabColor !== null;
+    }
+
+    /**
+     * Copy worksheet (!= clone!).
+     *
+     * @return static
+     */
+    public function copy()
+    {
+        return clone $this;
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        foreach ($this as $key => $val) {
+            if ($key == 'parent') {
+                continue;
+            }
+
+            if (is_object($val) || (is_array($val))) {
+                if ($key == 'cellCollection') {
+                    $newCollection = $this->cellCollection->cloneCellCollection($this);
+                    $this->cellCollection = $newCollection;
+                } elseif ($key == 'drawingCollection') {
+                    $currentCollection = $this->drawingCollection;
+                    $this->drawingCollection = new ArrayObject();
+                    foreach ($currentCollection as $item) {
+                        if (is_object($item)) {
+                            $newDrawing = clone $item;
+                            $newDrawing->setWorksheet($this);
+                        }
+                    }
+                } elseif (($key == 'autoFilter') && ($this->autoFilter instanceof AutoFilter)) {
+                    $newAutoFilter = clone $this->autoFilter;
+                    $this->autoFilter = $newAutoFilter;
+                    $this->autoFilter->setParent($this);
+                } else {
+                    $this->{$key} = unserialize(serialize($val));
+                }
+            }
+        }
+    }
+
+    /**
+     * Define the code name of the sheet.
+     *
+     * @param string $pValue Same rule as Title minus space not allowed (but, like Excel, change
+     *                       silently space to underscore)
+     * @param bool $validate False to skip validation of new title. WARNING: This should only be set
+     *                       at parse time (by Readers), where titles can be assumed to be valid.
+     *
+     * @return $this
+     */
+    public function setCodeName($pValue, $validate = true)
+    {
+        // Is this a 'rename' or not?
+        if ($this->getCodeName() == $pValue) {
+            return $this;
+        }
+
+        if ($validate) {
+            $pValue = str_replace(' ', '_', $pValue); //Excel does this automatically without flinching, we are doing the same
+
+            // Syntax check
+            // throw an exception if not valid
+            self::checkSheetCodeName($pValue);
+
+            // We use the same code that setTitle to find a valid codeName else not using a space (Excel don't like) but a '_'
+
+            if ($this->getParent()) {
+                // Is there already such sheet name?
+                if ($this->getParent()->sheetCodeNameExists($pValue)) {
+                    // Use name, but append with lowest possible integer
+
+                    if (Shared\StringHelper::countCharacters($pValue) > 29) {
+                        $pValue = Shared\StringHelper::substring($pValue, 0, 29);
+                    }
+                    $i = 1;
+                    while ($this->getParent()->sheetCodeNameExists($pValue . '_' . $i)) {
+                        ++$i;
+                        if ($i == 10) {
+                            if (Shared\StringHelper::countCharacters($pValue) > 28) {
+                                $pValue = Shared\StringHelper::substring($pValue, 0, 28);
+                            }
+                        } elseif ($i == 100) {
+                            if (Shared\StringHelper::countCharacters($pValue) > 27) {
+                                $pValue = Shared\StringHelper::substring($pValue, 0, 27);
+                            }
+                        }
+                    }
+
+                    $pValue .= '_' . $i; // ok, we have a valid name
+                }
+            }
+        }
+
+        $this->codeName = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Return the code name of the sheet.
+     *
+     * @return null|string
+     */
+    public function getCodeName()
+    {
+        return $this->codeName;
+    }
+
+    /**
+     * Sheet has a code name ?
+     *
+     * @return bool
+     */
+    public function hasCodeName()
+    {
+        return $this->codeName !== null;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/BaseWriter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/BaseWriter.php
new file mode 100755
index 0000000..afda5c4
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/BaseWriter.php
@@ -0,0 +1,131 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer;
+
+abstract class BaseWriter implements IWriter
+{
+    /**
+     * Write charts that are defined in the workbook?
+     * Identifies whether the Writer should write definitions for any charts that exist in the PhpSpreadsheet object;.
+     *
+     * @var bool
+     */
+    protected $includeCharts = false;
+
+    /**
+     * Pre-calculate formulas
+     * Forces PhpSpreadsheet to recalculate all formulae in a workbook when saving, so that the pre-calculated values are
+     * immediately available to MS Excel or other office spreadsheet viewer when opening the file.
+     *
+     * @var bool
+     */
+    protected $preCalculateFormulas = true;
+
+    /**
+     * Use disk caching where possible?
+     *
+     * @var bool
+     */
+    private $useDiskCaching = false;
+
+    /**
+     * Disk caching directory.
+     *
+     * @var string
+     */
+    private $diskCachingDirectory = './';
+
+    /**
+     * @var resource
+     */
+    protected $fileHandle;
+
+    /**
+     * @var bool
+     */
+    private $shouldCloseFile;
+
+    public function getIncludeCharts()
+    {
+        return $this->includeCharts;
+    }
+
+    public function setIncludeCharts($pValue)
+    {
+        $this->includeCharts = (bool) $pValue;
+
+        return $this;
+    }
+
+    public function getPreCalculateFormulas()
+    {
+        return $this->preCalculateFormulas;
+    }
+
+    public function setPreCalculateFormulas($pValue)
+    {
+        $this->preCalculateFormulas = (bool) $pValue;
+
+        return $this;
+    }
+
+    public function getUseDiskCaching()
+    {
+        return $this->useDiskCaching;
+    }
+
+    public function setUseDiskCaching($pValue, $pDirectory = null)
+    {
+        $this->useDiskCaching = $pValue;
+
+        if ($pDirectory !== null) {
+            if (is_dir($pDirectory)) {
+                $this->diskCachingDirectory = $pDirectory;
+            } else {
+                throw new Exception("Directory does not exist: $pDirectory");
+            }
+        }
+
+        return $this;
+    }
+
+    public function getDiskCachingDirectory()
+    {
+        return $this->diskCachingDirectory;
+    }
+
+    /**
+     * Open file handle.
+     *
+     * @param resource|string $filename
+     */
+    public function openFileHandle($filename): void
+    {
+        if (is_resource($filename)) {
+            $this->fileHandle = $filename;
+            $this->shouldCloseFile = false;
+
+            return;
+        }
+
+        $fileHandle = $filename ? fopen($filename, 'wb+') : false;
+        if ($fileHandle === false) {
+            throw new Exception('Could not open file "' . $filename . '" for writing.');
+        }
+
+        $this->fileHandle = $fileHandle;
+        $this->shouldCloseFile = true;
+    }
+
+    /**
+     * Close file handle only if we opened it ourselves.
+     */
+    protected function maybeCloseFileHandle(): void
+    {
+        if ($this->shouldCloseFile) {
+            if (!fclose($this->fileHandle)) {
+                throw new Exception('Could not close file after writing.');
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Csv.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Csv.php
new file mode 100755
index 0000000..414bdf9
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Csv.php
@@ -0,0 +1,334 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+
+class Csv extends BaseWriter
+{
+    /**
+     * PhpSpreadsheet object.
+     *
+     * @var Spreadsheet
+     */
+    private $spreadsheet;
+
+    /**
+     * Delimiter.
+     *
+     * @var string
+     */
+    private $delimiter = ',';
+
+    /**
+     * Enclosure.
+     *
+     * @var string
+     */
+    private $enclosure = '"';
+
+    /**
+     * Line ending.
+     *
+     * @var string
+     */
+    private $lineEnding = PHP_EOL;
+
+    /**
+     * Sheet index to write.
+     *
+     * @var int
+     */
+    private $sheetIndex = 0;
+
+    /**
+     * Whether to write a BOM (for UTF8).
+     *
+     * @var bool
+     */
+    private $useBOM = false;
+
+    /**
+     * Whether to write a Separator line as the first line of the file
+     *     sep=x.
+     *
+     * @var bool
+     */
+    private $includeSeparatorLine = false;
+
+    /**
+     * Whether to write a fully Excel compatible CSV file.
+     *
+     * @var bool
+     */
+    private $excelCompatibility = false;
+
+    /**
+     * Create a new CSV.
+     *
+     * @param Spreadsheet $spreadsheet Spreadsheet object
+     */
+    public function __construct(Spreadsheet $spreadsheet)
+    {
+        $this->spreadsheet = $spreadsheet;
+    }
+
+    /**
+     * Save PhpSpreadsheet to file.
+     *
+     * @param resource|string $pFilename
+     */
+    public function save($pFilename): void
+    {
+        // Fetch sheet
+        $sheet = $this->spreadsheet->getSheet($this->sheetIndex);
+
+        $saveDebugLog = Calculation::getInstance($this->spreadsheet)->getDebugLog()->getWriteDebugLog();
+        Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog(false);
+        $saveArrayReturnType = Calculation::getArrayReturnType();
+        Calculation::setArrayReturnType(Calculation::RETURN_ARRAY_AS_VALUE);
+
+        // Open file
+        $this->openFileHandle($pFilename);
+
+        if ($this->excelCompatibility) {
+            $this->setUseBOM(true); //  Enforce UTF-8 BOM Header
+            $this->setIncludeSeparatorLine(true); //  Set separator line
+            $this->setEnclosure('"'); //  Set enclosure to "
+            $this->setDelimiter(';'); //  Set delimiter to a semi-colon
+            $this->setLineEnding("\r\n");
+        }
+
+        if ($this->useBOM) {
+            // Write the UTF-8 BOM code if required
+            fwrite($this->fileHandle, "\xEF\xBB\xBF");
+        }
+
+        if ($this->includeSeparatorLine) {
+            // Write the separator line if required
+            fwrite($this->fileHandle, 'sep=' . $this->getDelimiter() . $this->lineEnding);
+        }
+
+        //    Identify the range that we need to extract from the worksheet
+        $maxCol = $sheet->getHighestDataColumn();
+        $maxRow = $sheet->getHighestDataRow();
+
+        // Write rows to file
+        for ($row = 1; $row <= $maxRow; ++$row) {
+            // Convert the row to an array...
+            $cellsArray = $sheet->rangeToArray('A' . $row . ':' . $maxCol . $row, '', $this->preCalculateFormulas);
+            // ... and write to the file
+            $this->writeLine($this->fileHandle, $cellsArray[0]);
+        }
+
+        $this->maybeCloseFileHandle();
+        Calculation::setArrayReturnType($saveArrayReturnType);
+        Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog($saveDebugLog);
+    }
+
+    /**
+     * Get delimiter.
+     *
+     * @return string
+     */
+    public function getDelimiter()
+    {
+        return $this->delimiter;
+    }
+
+    /**
+     * Set delimiter.
+     *
+     * @param string $pValue Delimiter, defaults to ','
+     *
+     * @return $this
+     */
+    public function setDelimiter($pValue)
+    {
+        $this->delimiter = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get enclosure.
+     *
+     * @return string
+     */
+    public function getEnclosure()
+    {
+        return $this->enclosure;
+    }
+
+    /**
+     * Set enclosure.
+     *
+     * @param string $pValue Enclosure, defaults to "
+     *
+     * @return $this
+     */
+    public function setEnclosure($pValue)
+    {
+        $this->enclosure = $pValue ? $pValue : '"';
+
+        return $this;
+    }
+
+    /**
+     * Get line ending.
+     *
+     * @return string
+     */
+    public function getLineEnding()
+    {
+        return $this->lineEnding;
+    }
+
+    /**
+     * Set line ending.
+     *
+     * @param string $pValue Line ending, defaults to OS line ending (PHP_EOL)
+     *
+     * @return $this
+     */
+    public function setLineEnding($pValue)
+    {
+        $this->lineEnding = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get whether BOM should be used.
+     *
+     * @return bool
+     */
+    public function getUseBOM()
+    {
+        return $this->useBOM;
+    }
+
+    /**
+     * Set whether BOM should be used.
+     *
+     * @param bool $pValue Use UTF-8 byte-order mark? Defaults to false
+     *
+     * @return $this
+     */
+    public function setUseBOM($pValue)
+    {
+        $this->useBOM = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get whether a separator line should be included.
+     *
+     * @return bool
+     */
+    public function getIncludeSeparatorLine()
+    {
+        return $this->includeSeparatorLine;
+    }
+
+    /**
+     * Set whether a separator line should be included as the first line of the file.
+     *
+     * @param bool $pValue Use separator line? Defaults to false
+     *
+     * @return $this
+     */
+    public function setIncludeSeparatorLine($pValue)
+    {
+        $this->includeSeparatorLine = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get whether the file should be saved with full Excel Compatibility.
+     *
+     * @return bool
+     */
+    public function getExcelCompatibility()
+    {
+        return $this->excelCompatibility;
+    }
+
+    /**
+     * Set whether the file should be saved with full Excel Compatibility.
+     *
+     * @param bool $pValue Set the file to be written as a fully Excel compatible csv file
+     *                                Note that this overrides other settings such as useBOM, enclosure and delimiter
+     *
+     * @return $this
+     */
+    public function setExcelCompatibility($pValue)
+    {
+        $this->excelCompatibility = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get sheet index.
+     *
+     * @return int
+     */
+    public function getSheetIndex()
+    {
+        return $this->sheetIndex;
+    }
+
+    /**
+     * Set sheet index.
+     *
+     * @param int $pValue Sheet index
+     *
+     * @return $this
+     */
+    public function setSheetIndex($pValue)
+    {
+        $this->sheetIndex = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Write line to CSV file.
+     *
+     * @param resource $pFileHandle PHP filehandle
+     * @param array $pValues Array containing values in a row
+     */
+    private function writeLine($pFileHandle, array $pValues): void
+    {
+        // No leading delimiter
+        $writeDelimiter = false;
+
+        // Build the line
+        $line = '';
+
+        foreach ($pValues as $element) {
+            // Escape enclosures
+            $element = str_replace($this->enclosure, $this->enclosure . $this->enclosure, $element);
+
+            // Add delimiter
+            if ($writeDelimiter) {
+                $line .= $this->delimiter;
+            } else {
+                $writeDelimiter = true;
+            }
+
+            // Add enclosed string
+            $line .= $this->enclosure . $element . $this->enclosure;
+        }
+
+        // Add line ending
+        $line .= $this->lineEnding;
+
+        // Write to file
+        fwrite($pFileHandle, $line);
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Exception.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Exception.php
new file mode 100755
index 0000000..92e6f5f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Exception.php
@@ -0,0 +1,9 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer;
+
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+
+class Exception extends PhpSpreadsheetException
+{
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Html.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Html.php
new file mode 100755
index 0000000..c9f2d7c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Html.php
@@ -0,0 +1,1846 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
+use PhpOffice\PhpSpreadsheet\Cell\Cell;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Chart\Chart;
+use PhpOffice\PhpSpreadsheet\RichText\RichText;
+use PhpOffice\PhpSpreadsheet\RichText\Run;
+use PhpOffice\PhpSpreadsheet\Shared\Drawing as SharedDrawing;
+use PhpOffice\PhpSpreadsheet\Shared\File;
+use PhpOffice\PhpSpreadsheet\Shared\Font as SharedFont;
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Style\Alignment;
+use PhpOffice\PhpSpreadsheet\Style\Border;
+use PhpOffice\PhpSpreadsheet\Style\Borders;
+use PhpOffice\PhpSpreadsheet\Style\Fill;
+use PhpOffice\PhpSpreadsheet\Style\Font;
+use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
+use PhpOffice\PhpSpreadsheet\Style\Style;
+use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
+use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+
+class Html extends BaseWriter
+{
+    /**
+     * Spreadsheet object.
+     *
+     * @var Spreadsheet
+     */
+    protected $spreadsheet;
+
+    /**
+     * Sheet index to write.
+     *
+     * @var int
+     */
+    private $sheetIndex = 0;
+
+    /**
+     * Images root.
+     *
+     * @var string
+     */
+    private $imagesRoot = '';
+
+    /**
+     * embed images, or link to images.
+     *
+     * @var bool
+     */
+    private $embedImages = false;
+
+    /**
+     * Use inline CSS?
+     *
+     * @var bool
+     */
+    private $useInlineCss = false;
+
+    /**
+     * Use embedded CSS?
+     *
+     * @var bool
+     */
+    private $useEmbeddedCSS = true;
+
+    /**
+     * Array of CSS styles.
+     *
+     * @var array
+     */
+    private $cssStyles;
+
+    /**
+     * Array of column widths in points.
+     *
+     * @var array
+     */
+    private $columnWidths;
+
+    /**
+     * Default font.
+     *
+     * @var Font
+     */
+    private $defaultFont;
+
+    /**
+     * Flag whether spans have been calculated.
+     *
+     * @var bool
+     */
+    private $spansAreCalculated = false;
+
+    /**
+     * Excel cells that should not be written as HTML cells.
+     *
+     * @var array
+     */
+    private $isSpannedCell = [];
+
+    /**
+     * Excel cells that are upper-left corner in a cell merge.
+     *
+     * @var array
+     */
+    private $isBaseCell = [];
+
+    /**
+     * Excel rows that should not be written as HTML rows.
+     *
+     * @var array
+     */
+    private $isSpannedRow = [];
+
+    /**
+     * Is the current writer creating PDF?
+     *
+     * @var bool
+     */
+    protected $isPdf = false;
+
+    /**
+     * Generate the Navigation block.
+     *
+     * @var bool
+     */
+    private $generateSheetNavigationBlock = true;
+
+    /**
+     * Create a new HTML.
+     */
+    public function __construct(Spreadsheet $spreadsheet)
+    {
+        $this->spreadsheet = $spreadsheet;
+        $this->defaultFont = $this->spreadsheet->getDefaultStyle()->getFont();
+    }
+
+    /**
+     * Save Spreadsheet to file.
+     *
+     * @param resource|string $pFilename
+     */
+    public function save($pFilename): void
+    {
+        // Open file
+        $this->openFileHandle($pFilename);
+
+        // Write html
+        fwrite($this->fileHandle, $this->generateHTMLAll());
+
+        // Close file
+        $this->maybeCloseFileHandle();
+    }
+
+    /**
+     * Save Spreadsheet as html to variable.
+     *
+     * @return string
+     */
+    public function generateHtmlAll()
+    {
+        // garbage collect
+        $this->spreadsheet->garbageCollect();
+
+        $saveDebugLog = Calculation::getInstance($this->spreadsheet)->getDebugLog()->getWriteDebugLog();
+        Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog(false);
+        $saveArrayReturnType = Calculation::getArrayReturnType();
+        Calculation::setArrayReturnType(Calculation::RETURN_ARRAY_AS_VALUE);
+
+        // Build CSS
+        $this->buildCSS(!$this->useInlineCss);
+
+        $html = '';
+
+        // Write headers
+        $html .= $this->generateHTMLHeader(!$this->useInlineCss);
+
+        // Write navigation (tabs)
+        if ((!$this->isPdf) && ($this->generateSheetNavigationBlock)) {
+            $html .= $this->generateNavigation();
+        }
+
+        // Write data
+        $html .= $this->generateSheetData();
+
+        // Write footer
+        $html .= $this->generateHTMLFooter();
+
+        Calculation::setArrayReturnType($saveArrayReturnType);
+        Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog($saveDebugLog);
+
+        return $html;
+    }
+
+    const VALIGN_ARR = [
+        Alignment::VERTICAL_BOTTOM => 'bottom',
+        Alignment::VERTICAL_TOP => 'top',
+        Alignment::VERTICAL_CENTER => 'middle',
+        Alignment::VERTICAL_JUSTIFY => 'middle',
+    ];
+
+    /**
+     * Map VAlign.
+     *
+     * @param string $vAlign Vertical alignment
+     *
+     * @return string
+     */
+    private function mapVAlign($vAlign)
+    {
+        return array_key_exists($vAlign, self::VALIGN_ARR) ? self::VALIGN_ARR[$vAlign] : 'baseline';
+    }
+
+    const HALIGN_ARR = [
+        Alignment::HORIZONTAL_LEFT => 'left',
+        Alignment::HORIZONTAL_RIGHT => 'right',
+        Alignment::HORIZONTAL_CENTER => 'center',
+        Alignment::HORIZONTAL_CENTER_CONTINUOUS => 'center',
+        Alignment::HORIZONTAL_JUSTIFY => 'justify',
+    ];
+
+    /**
+     * Map HAlign.
+     *
+     * @param string $hAlign Horizontal alignment
+     *
+     * @return string
+     */
+    private function mapHAlign($hAlign)
+    {
+        return array_key_exists($hAlign, self::HALIGN_ARR) ? self::HALIGN_ARR[$hAlign] : '';
+    }
+
+    const BORDER_ARR = [
+        Border::BORDER_NONE => 'none',
+        Border::BORDER_DASHDOT => '1px dashed',
+        Border::BORDER_DASHDOTDOT => '1px dotted',
+        Border::BORDER_DASHED => '1px dashed',
+        Border::BORDER_DOTTED => '1px dotted',
+        Border::BORDER_DOUBLE => '3px double',
+        Border::BORDER_HAIR => '1px solid',
+        Border::BORDER_MEDIUM => '2px solid',
+        Border::BORDER_MEDIUMDASHDOT => '2px dashed',
+        Border::BORDER_MEDIUMDASHDOTDOT => '2px dotted',
+        Border::BORDER_SLANTDASHDOT => '2px dashed',
+        Border::BORDER_THICK => '3px solid',
+    ];
+
+    /**
+     * Map border style.
+     *
+     * @param int $borderStyle Sheet index
+     *
+     * @return string
+     */
+    private function mapBorderStyle($borderStyle)
+    {
+        return array_key_exists($borderStyle, self::BORDER_ARR) ? self::BORDER_ARR[$borderStyle] : '1px solid';
+    }
+
+    /**
+     * Get sheet index.
+     *
+     * @return int
+     */
+    public function getSheetIndex()
+    {
+        return $this->sheetIndex;
+    }
+
+    /**
+     * Set sheet index.
+     *
+     * @param int $pValue Sheet index
+     *
+     * @return $this
+     */
+    public function setSheetIndex($pValue)
+    {
+        $this->sheetIndex = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get sheet index.
+     *
+     * @return bool
+     */
+    public function getGenerateSheetNavigationBlock()
+    {
+        return $this->generateSheetNavigationBlock;
+    }
+
+    /**
+     * Set sheet index.
+     *
+     * @param bool $pValue Flag indicating whether the sheet navigation block should be generated or not
+     *
+     * @return $this
+     */
+    public function setGenerateSheetNavigationBlock($pValue)
+    {
+        $this->generateSheetNavigationBlock = (bool) $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Write all sheets (resets sheetIndex to NULL).
+     *
+     * @return $this
+     */
+    public function writeAllSheets()
+    {
+        $this->sheetIndex = null;
+
+        return $this;
+    }
+
+    private static function generateMeta($val, $desc)
+    {
+        return $val ? ('      <meta name="' . $desc . '" content="' . htmlspecialchars($val) . '" />' . PHP_EOL) : '';
+    }
+
+    /**
+     * Generate HTML header.
+     *
+     * @param bool $pIncludeStyles Include styles?
+     *
+     * @return string
+     */
+    public function generateHTMLHeader($pIncludeStyles = false)
+    {
+        // Construct HTML
+        $properties = $this->spreadsheet->getProperties();
+        $html = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' . PHP_EOL;
+        $html .= '<html xmlns="http://www.w3.org/1999/xhtml">' . PHP_EOL;
+        $html .= '  <head>' . PHP_EOL;
+        $html .= '      <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />' . PHP_EOL;
+        $html .= '      <meta name="generator" content="PhpSpreadsheet, https://github.com/PHPOffice/PhpSpreadsheet" />' . PHP_EOL;
+        $html .= '      <title>' . htmlspecialchars($properties->getTitle()) . '</title>' . PHP_EOL;
+        $html .= self::generateMeta($properties->getCreator(), 'author');
+        $html .= self::generateMeta($properties->getTitle(), 'title');
+        $html .= self::generateMeta($properties->getDescription(), 'description');
+        $html .= self::generateMeta($properties->getSubject(), 'subject');
+        $html .= self::generateMeta($properties->getKeywords(), 'keywords');
+        $html .= self::generateMeta($properties->getCategory(), 'category');
+        $html .= self::generateMeta($properties->getCompany(), 'company');
+        $html .= self::generateMeta($properties->getManager(), 'manager');
+
+        $html .= $pIncludeStyles ? $this->generateStyles(true) : $this->generatePageDeclarations(true);
+
+        $html .= '  </head>' . PHP_EOL;
+        $html .= '' . PHP_EOL;
+        $html .= '  <body>' . PHP_EOL;
+
+        return $html;
+    }
+
+    private function generateSheetPrep()
+    {
+        // Ensure that Spans have been calculated?
+        $this->calculateSpans();
+
+        // Fetch sheets
+        if ($this->sheetIndex === null) {
+            $sheets = $this->spreadsheet->getAllSheets();
+        } else {
+            $sheets = [$this->spreadsheet->getSheet($this->sheetIndex)];
+        }
+
+        return $sheets;
+    }
+
+    private function generateSheetStarts($sheet, $rowMin)
+    {
+        // calculate start of <tbody>, <thead>
+        $tbodyStart = $rowMin;
+        $theadStart = $theadEnd = 0; // default: no <thead>    no </thead>
+        if ($sheet->getPageSetup()->isRowsToRepeatAtTopSet()) {
+            $rowsToRepeatAtTop = $sheet->getPageSetup()->getRowsToRepeatAtTop();
+
+            // we can only support repeating rows that start at top row
+            if ($rowsToRepeatAtTop[0] == 1) {
+                $theadStart = $rowsToRepeatAtTop[0];
+                $theadEnd = $rowsToRepeatAtTop[1];
+                $tbodyStart = $rowsToRepeatAtTop[1] + 1;
+            }
+        }
+
+        return [$theadStart, $theadEnd, $tbodyStart];
+    }
+
+    private function generateSheetTags($row, $theadStart, $theadEnd, $tbodyStart)
+    {
+        // <thead> ?
+        $startTag = ($row == $theadStart) ? ('        <thead>' . PHP_EOL) : '';
+        if (!$startTag) {
+            $startTag = ($row == $tbodyStart) ? ('        <tbody>' . PHP_EOL) : '';
+        }
+        $endTag = ($row == $theadEnd) ? ('        </thead>' . PHP_EOL) : '';
+        $cellType = ($row >= $tbodyStart) ? 'td' : 'th';
+
+        return [$cellType, $startTag, $endTag];
+    }
+
+    /**
+     * Generate sheet data.
+     *
+     * @return string
+     */
+    public function generateSheetData()
+    {
+        $sheets = $this->generateSheetPrep();
+
+        // Construct HTML
+        $html = '';
+
+        // Loop all sheets
+        $sheetId = 0;
+        foreach ($sheets as $sheet) {
+            // Write table header
+            $html .= $this->generateTableHeader($sheet);
+            // Get worksheet dimension
+            $dimension = explode(':', $sheet->calculateWorksheetDimension());
+            $dimension[0] = Coordinate::coordinateFromString($dimension[0]);
+            $dimension[0][0] = Coordinate::columnIndexFromString($dimension[0][0]);
+            $dimension[1] = Coordinate::coordinateFromString($dimension[1]);
+            $dimension[1][0] = Coordinate::columnIndexFromString($dimension[1][0]);
+
+            // row min,max
+            $rowMin = $dimension[0][1];
+            $rowMax = $dimension[1][1];
+
+            [$theadStart, $theadEnd, $tbodyStart] = $this->generateSheetStarts($sheet, $rowMin);
+
+            // Loop through cells
+            $row = $rowMin - 1;
+            while ($row++ < $rowMax) {
+                [$cellType, $startTag, $endTag] = $this->generateSheetTags($row, $theadStart, $theadEnd, $tbodyStart);
+                $html .= $startTag;
+
+                // Write row if there are HTML table cells in it
+                if (!isset($this->isSpannedRow[$sheet->getParent()->getIndex($sheet)][$row])) {
+                    // Start a new rowData
+                    $rowData = [];
+                    // Loop through columns
+                    $column = $dimension[0][0];
+                    while ($column <= $dimension[1][0]) {
+                        // Cell exists?
+                        if ($sheet->cellExistsByColumnAndRow($column, $row)) {
+                            $rowData[$column] = Coordinate::stringFromColumnIndex($column) . $row;
+                        } else {
+                            $rowData[$column] = '';
+                        }
+                        ++$column;
+                    }
+                    $html .= $this->generateRow($sheet, $rowData, $row - 1, $cellType);
+                }
+
+                $html .= $endTag;
+            }
+            $html .= $this->extendRowsForChartsAndImages($sheet, $row);
+
+            // Write table footer
+            $html .= $this->generateTableFooter();
+            // Writing PDF?
+            if ($this->isPdf && $this->useInlineCss) {
+                if ($this->sheetIndex === null && $sheetId + 1 < $this->spreadsheet->getSheetCount()) {
+                    $html .= '<div style="page-break-before:always" ></div>';
+                }
+            }
+
+            // Next sheet
+            ++$sheetId;
+        }
+
+        return $html;
+    }
+
+    /**
+     * Generate sheet tabs.
+     *
+     * @return string
+     */
+    public function generateNavigation()
+    {
+        // Fetch sheets
+        $sheets = [];
+        if ($this->sheetIndex === null) {
+            $sheets = $this->spreadsheet->getAllSheets();
+        } else {
+            $sheets[] = $this->spreadsheet->getSheet($this->sheetIndex);
+        }
+
+        // Construct HTML
+        $html = '';
+
+        // Only if there are more than 1 sheets
+        if (count($sheets) > 1) {
+            // Loop all sheets
+            $sheetId = 0;
+
+            $html .= '<ul class="navigation">' . PHP_EOL;
+
+            foreach ($sheets as $sheet) {
+                $html .= '  <li class="sheet' . $sheetId . '"><a href="#sheet' . $sheetId . '">' . $sheet->getTitle() . '</a></li>' . PHP_EOL;
+                ++$sheetId;
+            }
+
+            $html .= '</ul>' . PHP_EOL;
+        }
+
+        return $html;
+    }
+
+    /**
+     * Extend Row if chart is placed after nominal end of row.
+     * This code should be exercised by sample:
+     * Chart/32_Chart_read_write_PDF.php.
+     * However, that test is suppressed due to out-of-date
+     * Jpgraph code issuing warnings. So, don't measure
+     * code coverage for this function till that is fixed.
+     *
+     * @param Worksheet $pSheet \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
+     * @param int $row Row to check for charts
+     *
+     * @return array
+     *
+     * @codeCoverageIgnore
+     */
+    private function extendRowsForCharts(Worksheet $pSheet, $row)
+    {
+        $rowMax = $row;
+        $colMax = 'A';
+        $anyfound = false;
+        if ($this->includeCharts) {
+            foreach ($pSheet->getChartCollection() as $chart) {
+                if ($chart instanceof Chart) {
+                    $anyfound = true;
+                    $chartCoordinates = $chart->getTopLeftPosition();
+                    $chartTL = Coordinate::coordinateFromString($chartCoordinates['cell']);
+                    $chartCol = Coordinate::columnIndexFromString($chartTL[0]);
+                    if ($chartTL[1] > $rowMax) {
+                        $rowMax = $chartTL[1];
+                        if ($chartCol > Coordinate::columnIndexFromString($colMax)) {
+                            $colMax = $chartTL[0];
+                        }
+                    }
+                }
+            }
+        }
+
+        return [$rowMax, $colMax, $anyfound];
+    }
+
+    private function extendRowsForChartsAndImages(Worksheet $pSheet, $row)
+    {
+        [$rowMax, $colMax, $anyfound] = $this->extendRowsForCharts($pSheet, $row);
+
+        foreach ($pSheet->getDrawingCollection() as $drawing) {
+            $anyfound = true;
+            $imageTL = Coordinate::coordinateFromString($drawing->getCoordinates());
+            $imageCol = Coordinate::columnIndexFromString($imageTL[0]);
+            if ($imageTL[1] > $rowMax) {
+                $rowMax = $imageTL[1];
+                if ($imageCol > Coordinate::columnIndexFromString($colMax)) {
+                    $colMax = $imageTL[0];
+                }
+            }
+        }
+
+        // Don't extend rows if not needed
+        if ($row === $rowMax || !$anyfound) {
+            return '';
+        }
+
+        $html = '';
+        ++$colMax;
+        ++$row;
+        while ($row <= $rowMax) {
+            $html .= '<tr>';
+            for ($col = 'A'; $col != $colMax; ++$col) {
+                $htmlx = $this->writeImageInCell($pSheet, $col . $row);
+                $htmlx .= $this->includeCharts ? $this->writeChartInCell($pSheet, $col . $row) : '';
+                if ($htmlx) {
+                    $html .= "<td class='style0' style='position: relative;'>$htmlx</td>";
+                } else {
+                    $html .= "<td class='style0'></td>";
+                }
+            }
+            ++$row;
+            $html .= '</tr>' . PHP_EOL;
+        }
+
+        return $html;
+    }
+
+    /**
+     * Convert Windows file name to file protocol URL.
+     *
+     * @param string $filename file name on local system
+     *
+     * @return string
+     */
+    public static function winFileToUrl($filename)
+    {
+        // Windows filename
+        if (substr($filename, 1, 2) === ':\\') {
+            $filename = 'file:///' . str_replace('\\', '/', $filename);
+        }
+
+        return $filename;
+    }
+
+    /**
+     * Generate image tag in cell.
+     *
+     * @param Worksheet $pSheet \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
+     * @param string $coordinates Cell coordinates
+     *
+     * @return string
+     */
+    private function writeImageInCell(Worksheet $pSheet, $coordinates)
+    {
+        // Construct HTML
+        $html = '';
+
+        // Write images
+        foreach ($pSheet->getDrawingCollection() as $drawing) {
+            if ($drawing->getCoordinates() != $coordinates) {
+                continue;
+            }
+            $filedesc = $drawing->getDescription();
+            $filedesc = $filedesc ? htmlspecialchars($filedesc, ENT_QUOTES) : 'Embedded image';
+            if ($drawing instanceof Drawing) {
+                $filename = $drawing->getPath();
+
+                // Strip off eventual '.'
+                $filename = preg_replace('/^[.]/', '', $filename);
+
+                // Prepend images root
+                $filename = $this->getImagesRoot() . $filename;
+
+                // Strip off eventual '.' if followed by non-/
+                $filename = preg_replace('@^[.]([^/])@', '$1', $filename);
+
+                // Convert UTF8 data to PCDATA
+                $filename = htmlspecialchars($filename);
+
+                $html .= PHP_EOL;
+                $imageData = self::winFileToUrl($filename);
+
+                if ($this->embedImages && !$this->isPdf) {
+                    $picture = @file_get_contents($filename);
+                    if ($picture !== false) {
+                        $imageDetails = getimagesize($filename);
+                        // base64 encode the binary data
+                        $base64 = base64_encode($picture);
+                        $imageData = 'data:' . $imageDetails['mime'] . ';base64,' . $base64;
+                    }
+                }
+
+                $html .= '<img style="position: absolute; z-index: 1; left: ' .
+                    $drawing->getOffsetX() . 'px; top: ' . $drawing->getOffsetY() . 'px; width: ' .
+                    $drawing->getWidth() . 'px; height: ' . $drawing->getHeight() . 'px;" src="' .
+                    $imageData . '" alt="' . $filedesc . '" />';
+            } elseif ($drawing instanceof MemoryDrawing) {
+                ob_start(); //  Let's start output buffering.
+                imagepng($drawing->getImageResource()); //  This will normally output the image, but because of ob_start(), it won't.
+                $contents = ob_get_contents(); //  Instead, output above is saved to $contents
+                ob_end_clean(); //  End the output buffer.
+
+                $dataUri = 'data:image/jpeg;base64,' . base64_encode($contents);
+
+                //  Because of the nature of tables, width is more important than height.
+                //  max-width: 100% ensures that image doesnt overflow containing cell
+                //  width: X sets width of supplied image.
+                //  As a result, images bigger than cell will be contained and images smaller will not get stretched
+                $html .= '<img alt="' . $filedesc . '" src="' . $dataUri . '" style="max-width:100%;width:' . $drawing->getWidth() . 'px;" />';
+            }
+        }
+
+        return $html;
+    }
+
+    /**
+     * Generate chart tag in cell.
+     * This code should be exercised by sample:
+     * Chart/32_Chart_read_write_PDF.php.
+     * However, that test is suppressed due to out-of-date
+     * Jpgraph code issuing warnings. So, don't measure
+     * code coverage for this function till that is fixed.
+     *
+     * @param Worksheet $pSheet \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
+     * @param string $coordinates Cell coordinates
+     *
+     * @return string
+     *
+     * @codeCoverageIgnore
+     */
+    private function writeChartInCell(Worksheet $pSheet, $coordinates)
+    {
+        // Construct HTML
+        $html = '';
+
+        // Write charts
+        foreach ($pSheet->getChartCollection() as $chart) {
+            if ($chart instanceof Chart) {
+                $chartCoordinates = $chart->getTopLeftPosition();
+                if ($chartCoordinates['cell'] == $coordinates) {
+                    $chartFileName = File::sysGetTempDir() . '/' . uniqid('', true) . '.png';
+                    if (!$chart->render($chartFileName)) {
+                        return;
+                    }
+
+                    $html .= PHP_EOL;
+                    $imageDetails = getimagesize($chartFileName);
+                    $filedesc = $chart->getTitle();
+                    $filedesc = $filedesc ? self::getChartCaption($filedesc->getCaption()) : '';
+                    $filedesc = $filedesc ? htmlspecialchars($filedesc, ENT_QUOTES) : 'Embedded chart';
+                    if ($fp = fopen($chartFileName, 'rb', 0)) {
+                        $picture = fread($fp, filesize($chartFileName));
+                        fclose($fp);
+                        // base64 encode the binary data
+                        $base64 = base64_encode($picture);
+                        $imageData = 'data:' . $imageDetails['mime'] . ';base64,' . $base64;
+
+                        $html .= '<img style="position: absolute; z-index: 1; left: ' . $chartCoordinates['xOffset'] . 'px; top: ' . $chartCoordinates['yOffset'] . 'px; width: ' . $imageDetails[0] . 'px; height: ' . $imageDetails[1] . 'px;" src="' . $imageData . '" alt="' . $filedesc . '" />' . PHP_EOL;
+
+                        unlink($chartFileName);
+                    }
+                }
+            }
+        }
+
+        // Return
+        return $html;
+    }
+
+    /**
+     * Extend Row if chart is placed after nominal end of row.
+     * This code should be exercised by sample:
+     * Chart/32_Chart_read_write_PDF.php.
+     * However, that test is suppressed due to out-of-date
+     * Jpgraph code issuing warnings. So, don't measure
+     * code coverage for this function till that is fixed.
+     * Caption is described in documentation as fixed,
+     * but in 32_Chart it is somehow an array of RichText.
+     *
+     * @param mixed $cap
+     *
+     * @return string
+     *
+     * @codeCoverageIgnore
+     */
+    private static function getChartCaption($cap)
+    {
+        return is_array($cap) ? implode(' ', $cap) : $cap;
+    }
+
+    /**
+     * Generate CSS styles.
+     *
+     * @param bool $generateSurroundingHTML Generate surrounding HTML tags? (&lt;style&gt; and &lt;/style&gt;)
+     *
+     * @return string
+     */
+    public function generateStyles($generateSurroundingHTML = true)
+    {
+        // Build CSS
+        $css = $this->buildCSS($generateSurroundingHTML);
+
+        // Construct HTML
+        $html = '';
+
+        // Start styles
+        if ($generateSurroundingHTML) {
+            $html .= '    <style type="text/css">' . PHP_EOL;
+            $html .= (array_key_exists('html', $css)) ? ('      html { ' . $this->assembleCSS($css['html']) . ' }' . PHP_EOL) : '';
+        }
+
+        // Write all other styles
+        foreach ($css as $styleName => $styleDefinition) {
+            if ($styleName != 'html') {
+                $html .= '      ' . $styleName . ' { ' . $this->assembleCSS($styleDefinition) . ' }' . PHP_EOL;
+            }
+        }
+        $html .= $this->generatePageDeclarations(false);
+
+        // End styles
+        if ($generateSurroundingHTML) {
+            $html .= '    </style>' . PHP_EOL;
+        }
+
+        // Return
+        return $html;
+    }
+
+    private function buildCssRowHeights(Worksheet $sheet, array &$css, int $sheetIndex): void
+    {
+        // Calculate row heights
+        foreach ($sheet->getRowDimensions() as $rowDimension) {
+            $row = $rowDimension->getRowIndex() - 1;
+
+            // table.sheetN tr.rowYYYYYY { }
+            $css['table.sheet' . $sheetIndex . ' tr.row' . $row] = [];
+
+            if ($rowDimension->getRowHeight() != -1) {
+                $pt_height = $rowDimension->getRowHeight();
+                $css['table.sheet' . $sheetIndex . ' tr.row' . $row]['height'] = $pt_height . 'pt';
+            }
+            if ($rowDimension->getVisible() === false) {
+                $css['table.sheet' . $sheetIndex . ' tr.row' . $row]['display'] = 'none';
+                $css['table.sheet' . $sheetIndex . ' tr.row' . $row]['visibility'] = 'hidden';
+            }
+        }
+    }
+
+    private function buildCssPerSheet(Worksheet $sheet, array &$css): void
+    {
+        // Calculate hash code
+        $sheetIndex = $sheet->getParent()->getIndex($sheet);
+
+        // Build styles
+        // Calculate column widths
+        $sheet->calculateColumnWidths();
+
+        // col elements, initialize
+        $highestColumnIndex = Coordinate::columnIndexFromString($sheet->getHighestColumn()) - 1;
+        $column = -1;
+        while ($column++ < $highestColumnIndex) {
+            $this->columnWidths[$sheetIndex][$column] = 42; // approximation
+            $css['table.sheet' . $sheetIndex . ' col.col' . $column]['width'] = '42pt';
+        }
+
+        // col elements, loop through columnDimensions and set width
+        foreach ($sheet->getColumnDimensions() as $columnDimension) {
+            $column = Coordinate::columnIndexFromString($columnDimension->getColumnIndex()) - 1;
+            $width = SharedDrawing::cellDimensionToPixels($columnDimension->getWidth(), $this->defaultFont);
+            $width = SharedDrawing::pixelsToPoints($width);
+            if ($columnDimension->getVisible() === false) {
+                $css['table.sheet' . $sheetIndex . ' .column' . $column]['display'] = 'none';
+            }
+            if ($width >= 0) {
+                $this->columnWidths[$sheetIndex][$column] = $width;
+                $css['table.sheet' . $sheetIndex . ' col.col' . $column]['width'] = $width . 'pt';
+            }
+        }
+
+        // Default row height
+        $rowDimension = $sheet->getDefaultRowDimension();
+
+        // table.sheetN tr { }
+        $css['table.sheet' . $sheetIndex . ' tr'] = [];
+
+        if ($rowDimension->getRowHeight() == -1) {
+            $pt_height = SharedFont::getDefaultRowHeightByFont($this->spreadsheet->getDefaultStyle()->getFont());
+        } else {
+            $pt_height = $rowDimension->getRowHeight();
+        }
+        $css['table.sheet' . $sheetIndex . ' tr']['height'] = $pt_height . 'pt';
+        if ($rowDimension->getVisible() === false) {
+            $css['table.sheet' . $sheetIndex . ' tr']['display'] = 'none';
+            $css['table.sheet' . $sheetIndex . ' tr']['visibility'] = 'hidden';
+        }
+
+        $this->buildCssRowHeights($sheet, $css, $sheetIndex);
+    }
+
+    /**
+     * Build CSS styles.
+     *
+     * @param bool $generateSurroundingHTML Generate surrounding HTML style? (html { })
+     *
+     * @return array
+     */
+    public function buildCSS($generateSurroundingHTML = true)
+    {
+        // Cached?
+        if ($this->cssStyles !== null) {
+            return $this->cssStyles;
+        }
+
+        // Ensure that spans have been calculated
+        $this->calculateSpans();
+
+        // Construct CSS
+        $css = [];
+
+        // Start styles
+        if ($generateSurroundingHTML) {
+            // html { }
+            $css['html']['font-family'] = 'Calibri, Arial, Helvetica, sans-serif';
+            $css['html']['font-size'] = '11pt';
+            $css['html']['background-color'] = 'white';
+        }
+
+        // CSS for comments as found in LibreOffice
+        $css['a.comment-indicator:hover + div.comment'] = [
+            'background' => '#ffd',
+            'position' => 'absolute',
+            'display' => 'block',
+            'border' => '1px solid black',
+            'padding' => '0.5em',
+        ];
+
+        $css['a.comment-indicator'] = [
+            'background' => 'red',
+            'display' => 'inline-block',
+            'border' => '1px solid black',
+            'width' => '0.5em',
+            'height' => '0.5em',
+        ];
+
+        $css['div.comment']['display'] = 'none';
+
+        // table { }
+        $css['table']['border-collapse'] = 'collapse';
+
+        // .b {}
+        $css['.b']['text-align'] = 'center'; // BOOL
+
+        // .e {}
+        $css['.e']['text-align'] = 'center'; // ERROR
+
+        // .f {}
+        $css['.f']['text-align'] = 'right'; // FORMULA
+
+        // .inlineStr {}
+        $css['.inlineStr']['text-align'] = 'left'; // INLINE
+
+        // .n {}
+        $css['.n']['text-align'] = 'right'; // NUMERIC
+
+        // .s {}
+        $css['.s']['text-align'] = 'left'; // STRING
+
+        // Calculate cell style hashes
+        foreach ($this->spreadsheet->getCellXfCollection() as $index => $style) {
+            $css['td.style' . $index] = $this->createCSSStyle($style);
+            $css['th.style' . $index] = $this->createCSSStyle($style);
+        }
+
+        // Fetch sheets
+        $sheets = [];
+        if ($this->sheetIndex === null) {
+            $sheets = $this->spreadsheet->getAllSheets();
+        } else {
+            $sheets[] = $this->spreadsheet->getSheet($this->sheetIndex);
+        }
+
+        // Build styles per sheet
+        foreach ($sheets as $sheet) {
+            $this->buildCssPerSheet($sheet, $css);
+        }
+
+        // Cache
+        if ($this->cssStyles === null) {
+            $this->cssStyles = $css;
+        }
+
+        // Return
+        return $css;
+    }
+
+    /**
+     * Create CSS style.
+     *
+     * @return array
+     */
+    private function createCSSStyle(Style $pStyle)
+    {
+        // Create CSS
+        return array_merge(
+            $this->createCSSStyleAlignment($pStyle->getAlignment()),
+            $this->createCSSStyleBorders($pStyle->getBorders()),
+            $this->createCSSStyleFont($pStyle->getFont()),
+            $this->createCSSStyleFill($pStyle->getFill())
+        );
+    }
+
+    /**
+     * Create CSS style (\PhpOffice\PhpSpreadsheet\Style\Alignment).
+     *
+     * @param Alignment $pStyle \PhpOffice\PhpSpreadsheet\Style\Alignment
+     *
+     * @return array
+     */
+    private function createCSSStyleAlignment(Alignment $pStyle)
+    {
+        // Construct CSS
+        $css = [];
+
+        // Create CSS
+        $css['vertical-align'] = $this->mapVAlign($pStyle->getVertical());
+        $textAlign = $this->mapHAlign($pStyle->getHorizontal());
+        if ($textAlign) {
+            $css['text-align'] = $textAlign;
+            if (in_array($textAlign, ['left', 'right'])) {
+                $css['padding-' . $textAlign] = (string) ((int) $pStyle->getIndent() * 9) . 'px';
+            }
+        }
+
+        return $css;
+    }
+
+    /**
+     * Create CSS style (\PhpOffice\PhpSpreadsheet\Style\Font).
+     *
+     * @return array
+     */
+    private function createCSSStyleFont(Font $pStyle)
+    {
+        // Construct CSS
+        $css = [];
+
+        // Create CSS
+        if ($pStyle->getBold()) {
+            $css['font-weight'] = 'bold';
+        }
+        if ($pStyle->getUnderline() != Font::UNDERLINE_NONE && $pStyle->getStrikethrough()) {
+            $css['text-decoration'] = 'underline line-through';
+        } elseif ($pStyle->getUnderline() != Font::UNDERLINE_NONE) {
+            $css['text-decoration'] = 'underline';
+        } elseif ($pStyle->getStrikethrough()) {
+            $css['text-decoration'] = 'line-through';
+        }
+        if ($pStyle->getItalic()) {
+            $css['font-style'] = 'italic';
+        }
+
+        $css['color'] = '#' . $pStyle->getColor()->getRGB();
+        $css['font-family'] = '\'' . $pStyle->getName() . '\'';
+        $css['font-size'] = $pStyle->getSize() . 'pt';
+
+        return $css;
+    }
+
+    /**
+     * Create CSS style (Borders).
+     *
+     * @param Borders $pStyle Borders
+     *
+     * @return array
+     */
+    private function createCSSStyleBorders(Borders $pStyle)
+    {
+        // Construct CSS
+        $css = [];
+
+        // Create CSS
+        $css['border-bottom'] = $this->createCSSStyleBorder($pStyle->getBottom());
+        $css['border-top'] = $this->createCSSStyleBorder($pStyle->getTop());
+        $css['border-left'] = $this->createCSSStyleBorder($pStyle->getLeft());
+        $css['border-right'] = $this->createCSSStyleBorder($pStyle->getRight());
+
+        return $css;
+    }
+
+    /**
+     * Create CSS style (Border).
+     *
+     * @param Border $pStyle Border
+     *
+     * @return string
+     */
+    private function createCSSStyleBorder(Border $pStyle)
+    {
+        //    Create CSS - add !important to non-none border styles for merged cells
+        $borderStyle = $this->mapBorderStyle($pStyle->getBorderStyle());
+
+        return $borderStyle . ' #' . $pStyle->getColor()->getRGB() . (($borderStyle == 'none') ? '' : ' !important');
+    }
+
+    /**
+     * Create CSS style (Fill).
+     *
+     * @param Fill $pStyle Fill
+     *
+     * @return array
+     */
+    private function createCSSStyleFill(Fill $pStyle)
+    {
+        // Construct HTML
+        $css = [];
+
+        // Create CSS
+        $value = $pStyle->getFillType() == Fill::FILL_NONE ?
+            'white' : '#' . $pStyle->getStartColor()->getRGB();
+        $css['background-color'] = $value;
+
+        return $css;
+    }
+
+    /**
+     * Generate HTML footer.
+     */
+    public function generateHTMLFooter()
+    {
+        // Construct HTML
+        $html = '';
+        $html .= '  </body>' . PHP_EOL;
+        $html .= '</html>' . PHP_EOL;
+
+        return $html;
+    }
+
+    private function generateTableTagInline($pSheet, $id)
+    {
+        $style = isset($this->cssStyles['table']) ?
+            $this->assembleCSS($this->cssStyles['table']) : '';
+
+        $prntgrid = $pSheet->getPrintGridlines();
+        $viewgrid = $this->isPdf ? $prntgrid : $pSheet->getShowGridlines();
+        if ($viewgrid && $prntgrid) {
+            $html = "    <table border='1' cellpadding='1' $id cellspacing='1' style='$style' class='gridlines gridlinesp'>" . PHP_EOL;
+        } elseif ($viewgrid) {
+            $html = "    <table border='0' cellpadding='0' $id cellspacing='0' style='$style' class='gridlines'>" . PHP_EOL;
+        } elseif ($prntgrid) {
+            $html = "    <table border='0' cellpadding='0' $id cellspacing='0' style='$style' class='gridlinesp'>" . PHP_EOL;
+        } else {
+            $html = "    <table border='0' cellpadding='1' $id cellspacing='0' style='$style'>" . PHP_EOL;
+        }
+
+        return $html;
+    }
+
+    private function generateTableTag($pSheet, $id, &$html, $sheetIndex): void
+    {
+        if (!$this->useInlineCss) {
+            $gridlines = $pSheet->getShowGridlines() ? ' gridlines' : '';
+            $gridlinesp = $pSheet->getPrintGridlines() ? ' gridlinesp' : '';
+            $html .= "    <table border='0' cellpadding='0' cellspacing='0' $id class='sheet$sheetIndex$gridlines$gridlinesp'>" . PHP_EOL;
+        } else {
+            $html .= $this->generateTableTagInline($pSheet, $id);
+        }
+    }
+
+    /**
+     * Generate table header.
+     *
+     * @param Worksheet $pSheet The worksheet for the table we are writing
+     * @param bool   $showid whether or not to add id to table tag
+     *
+     * @return string
+     */
+    private function generateTableHeader($pSheet, $showid = true)
+    {
+        $sheetIndex = $pSheet->getParent()->getIndex($pSheet);
+
+        // Construct HTML
+        $html = '';
+        $id = $showid ? "id='sheet$sheetIndex'" : '';
+        if ($showid) {
+            $html .= "<div style='page: page$sheetIndex'>\n";
+        //} elseif ($this->useInlineCss) {
+        //    $html .= "<div style='page-break-before: always' ></div>\n";
+        } else {
+            $html .= "<div style='page: page$sheetIndex' class='scrpgbrk'>\n";
+        }
+
+        $this->generateTableTag($pSheet, $id, $html, $sheetIndex);
+
+        // Write <col> elements
+        $highestColumnIndex = Coordinate::columnIndexFromString($pSheet->getHighestColumn()) - 1;
+        $i = -1;
+        while ($i++ < $highestColumnIndex) {
+            if (!$this->useInlineCss) {
+                $html .= '        <col class="col' . $i . '" />' . PHP_EOL;
+            } else {
+                $style = isset($this->cssStyles['table.sheet' . $sheetIndex . ' col.col' . $i]) ?
+                    $this->assembleCSS($this->cssStyles['table.sheet' . $sheetIndex . ' col.col' . $i]) : '';
+                $html .= '        <col style="' . $style . '" />' . PHP_EOL;
+            }
+        }
+
+        return $html;
+    }
+
+    /**
+     * Generate table footer.
+     */
+    private function generateTableFooter()
+    {
+        return '    </tbody></table>' . PHP_EOL . '</div>' . PHP_EOL;
+    }
+
+    /**
+     * Generate row start.
+     *
+     * @param Worksheet $pSheet \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
+     * @param int $sheetIndex Sheet index (0-based)
+     * @param int $pRow row number
+     *
+     * @return string
+     */
+    private function generateRowStart(Worksheet $pSheet, $sheetIndex, $pRow)
+    {
+        $html = '';
+        if (count($pSheet->getBreaks()) > 0) {
+            $breaks = $pSheet->getBreaks();
+
+            // check if a break is needed before this row
+            if (isset($breaks['A' . $pRow])) {
+                // close table: </table>
+                $html .= $this->generateTableFooter();
+                if ($this->isPdf && $this->useInlineCss) {
+                    $html .= '<div style="page-break-before:always" />';
+                }
+
+                // open table again: <table> + <col> etc.
+                $html .= $this->generateTableHeader($pSheet, false);
+                $html .= '<tbody>' . PHP_EOL;
+            }
+        }
+
+        // Write row start
+        if (!$this->useInlineCss) {
+            $html .= '          <tr class="row' . $pRow . '">' . PHP_EOL;
+        } else {
+            $style = isset($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow])
+                ? $this->assembleCSS($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]) : '';
+
+            $html .= '          <tr style="' . $style . '">' . PHP_EOL;
+        }
+
+        return $html;
+    }
+
+    private function generateRowCellCss($pSheet, $cellAddress, $pRow, $colNum)
+    {
+        $cell = ($cellAddress > '') ? $pSheet->getCell($cellAddress) : '';
+        $coordinate = Coordinate::stringFromColumnIndex($colNum + 1) . ($pRow + 1);
+        if (!$this->useInlineCss) {
+            $cssClass = 'column' . $colNum;
+        } else {
+            $cssClass = [];
+            // The statements below do nothing.
+            // Commenting out the code rather than deleting it
+            // in case someone can figure out what their intent was.
+            //if ($cellType == 'th') {
+            //    if (isset($this->cssStyles['table.sheet' . $sheetIndex . ' th.column' . $colNum])) {
+            //        $this->cssStyles['table.sheet' . $sheetIndex . ' th.column' . $colNum];
+            //    }
+            //} else {
+            //    if (isset($this->cssStyles['table.sheet' . $sheetIndex . ' td.column' . $colNum])) {
+            //        $this->cssStyles['table.sheet' . $sheetIndex . ' td.column' . $colNum];
+            //    }
+            //}
+            // End of mystery statements.
+        }
+
+        return [$cell, $cssClass, $coordinate];
+    }
+
+    private function generateRowCellDataValueRich($cell, &$cellData): void
+    {
+        // Loop through rich text elements
+        $elements = $cell->getValue()->getRichTextElements();
+        foreach ($elements as $element) {
+            // Rich text start?
+            if ($element instanceof Run) {
+                $cellData .= '<span style="' . $this->assembleCSS($this->createCSSStyleFont($element->getFont())) . '">';
+
+                $cellEnd = '';
+                if ($element->getFont()->getSuperscript()) {
+                    $cellData .= '<sup>';
+                    $cellEnd = '</sup>';
+                } elseif ($element->getFont()->getSubscript()) {
+                    $cellData .= '<sub>';
+                    $cellEnd = '</sub>';
+                }
+
+                // Convert UTF8 data to PCDATA
+                $cellText = $element->getText();
+                $cellData .= htmlspecialchars($cellText);
+
+                $cellData .= $cellEnd;
+
+                $cellData .= '</span>';
+            } else {
+                // Convert UTF8 data to PCDATA
+                $cellText = $element->getText();
+                $cellData .= htmlspecialchars($cellText);
+            }
+        }
+    }
+
+    private function generateRowCellDataValue($pSheet, $cell, &$cellData): void
+    {
+        if ($cell->getValue() instanceof RichText) {
+            $this->generateRowCellDataValueRich($cell, $cellData);
+        } else {
+            $origData = $this->preCalculateFormulas ? $cell->getCalculatedValue() : $cell->getValue();
+            $cellData = NumberFormat::toFormattedString(
+                $origData,
+                $pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode(),
+                [$this, 'formatColor']
+            );
+            if ($cellData === $origData) {
+                $cellData = htmlspecialchars($cellData);
+            }
+            if ($pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont()->getSuperscript()) {
+                $cellData = '<sup>' . $cellData . '</sup>';
+            } elseif ($pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont()->getSubscript()) {
+                $cellData = '<sub>' . $cellData . '</sub>';
+            }
+        }
+    }
+
+    private function generateRowCellData($pSheet, $cell, &$cssClass, $cellType)
+    {
+        $cellData = '&nbsp;';
+        if ($cell instanceof Cell) {
+            $cellData = '';
+            // Don't know what this does, and no test cases.
+            //if ($cell->getParent() === null) {
+            //    $cell->attach($pSheet);
+            //}
+            // Value
+            $this->generateRowCellDataValue($pSheet, $cell, $cellData);
+
+            // Converts the cell content so that spaces occuring at beginning of each new line are replaced by &nbsp;
+            // Example: "  Hello\n to the world" is converted to "&nbsp;&nbsp;Hello\n&nbsp;to the world"
+            $cellData = preg_replace('/(?m)(?:^|\\G) /', '&nbsp;', $cellData);
+
+            // convert newline "\n" to '<br>'
+            $cellData = nl2br($cellData);
+
+            // Extend CSS class?
+            if (!$this->useInlineCss) {
+                $cssClass .= ' style' . $cell->getXfIndex();
+                $cssClass .= ' ' . $cell->getDataType();
+            } else {
+                if ($cellType == 'th') {
+                    if (isset($this->cssStyles['th.style' . $cell->getXfIndex()])) {
+                        $cssClass = array_merge($cssClass, $this->cssStyles['th.style' . $cell->getXfIndex()]);
+                    }
+                } else {
+                    if (isset($this->cssStyles['td.style' . $cell->getXfIndex()])) {
+                        $cssClass = array_merge($cssClass, $this->cssStyles['td.style' . $cell->getXfIndex()]);
+                    }
+                }
+
+                // General horizontal alignment: Actual horizontal alignment depends on dataType
+                $sharedStyle = $pSheet->getParent()->getCellXfByIndex($cell->getXfIndex());
+                if ($sharedStyle->getAlignment()->getHorizontal() == Alignment::HORIZONTAL_GENERAL
+                    && isset($this->cssStyles['.' . $cell->getDataType()]['text-align'])
+                ) {
+                    $cssClass['text-align'] = $this->cssStyles['.' . $cell->getDataType()]['text-align'];
+                }
+            }
+        } else {
+            // Use default borders for empty cell
+            if (is_string($cssClass)) {
+                $cssClass .= ' style0';
+            }
+        }
+
+        return $cellData;
+    }
+
+    private function generateRowIncludeCharts($pSheet, $coordinate)
+    {
+        return $this->includeCharts ? $this->writeChartInCell($pSheet, $coordinate) : '';
+    }
+
+    private function generateRowSpans($html, $rowSpan, $colSpan)
+    {
+        $html .= ($colSpan > 1) ? (' colspan="' . $colSpan . '"') : '';
+        $html .= ($rowSpan > 1) ? (' rowspan="' . $rowSpan . '"') : '';
+
+        return $html;
+    }
+
+    private function generateRowWriteCell(&$html, $pSheet, $coordinate, $cellType, $cellData, $colSpan, $rowSpan, $cssClass, $colNum, $sheetIndex, $pRow): void
+    {
+        // Image?
+        $htmlx = $this->writeImageInCell($pSheet, $coordinate);
+        // Chart?
+        $htmlx .= $this->generateRowIncludeCharts($pSheet, $coordinate);
+        // Column start
+        $html .= '            <' . $cellType;
+        if (!$this->useInlineCss && !$this->isPdf) {
+            $html .= ' class="' . $cssClass . '"';
+            if ($htmlx) {
+                $html .= " style='position: relative;'";
+            }
+        } else {
+            //** Necessary redundant code for the sake of \PhpOffice\PhpSpreadsheet\Writer\Pdf **
+            // We must explicitly write the width of the <td> element because TCPDF
+            // does not recognize e.g. <col style="width:42pt">
+            if ($this->useInlineCss) {
+                $xcssClass = $cssClass;
+            } else {
+                $html .= ' class="' . $cssClass . '"';
+                $xcssClass = [];
+            }
+            $width = 0;
+            $i = $colNum - 1;
+            $e = $colNum + $colSpan - 1;
+            while ($i++ < $e) {
+                if (isset($this->columnWidths[$sheetIndex][$i])) {
+                    $width += $this->columnWidths[$sheetIndex][$i];
+                }
+            }
+            $xcssClass['width'] = $width . 'pt';
+
+            // We must also explicitly write the height of the <td> element because TCPDF
+            // does not recognize e.g. <tr style="height:50pt">
+            if (isset($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]['height'])) {
+                $height = $this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]['height'];
+                $xcssClass['height'] = $height;
+            }
+            //** end of redundant code **
+
+            if ($htmlx) {
+                $xcssClass['position'] = 'relative';
+            }
+            $html .= ' style="' . $this->assembleCSS($xcssClass) . '"';
+        }
+        $html = $this->generateRowSpans($html, $rowSpan, $colSpan);
+
+        $html .= '>';
+        $html .= $htmlx;
+
+        $html .= $this->writeComment($pSheet, $coordinate);
+
+        // Cell data
+        $html .= $cellData;
+
+        // Column end
+        $html .= '</' . $cellType . '>' . PHP_EOL;
+    }
+
+    /**
+     * Generate row.
+     *
+     * @param Worksheet $pSheet \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
+     * @param array $pValues Array containing cells in a row
+     * @param int $pRow Row number (0-based)
+     * @param string $cellType eg: 'td'
+     *
+     * @return string
+     */
+    private function generateRow(Worksheet $pSheet, array $pValues, $pRow, $cellType)
+    {
+        // Sheet index
+        $sheetIndex = $pSheet->getParent()->getIndex($pSheet);
+        $html = $this->generateRowStart($pSheet, $sheetIndex, $pRow);
+
+        // Write cells
+        $colNum = 0;
+        foreach ($pValues as $cellAddress) {
+            [$cell, $cssClass, $coordinate] = $this->generateRowCellCss($pSheet, $cellAddress, $pRow, $colNum);
+
+            $colSpan = 1;
+            $rowSpan = 1;
+
+            // Cell Data
+            $cellData = $this->generateRowCellData($pSheet, $cell, $cssClass, $cellType);
+
+            // Hyperlink?
+            if ($pSheet->hyperlinkExists($coordinate) && !$pSheet->getHyperlink($coordinate)->isInternal()) {
+                $cellData = '<a href="' . htmlspecialchars($pSheet->getHyperlink($coordinate)->getUrl()) . '" title="' . htmlspecialchars($pSheet->getHyperlink($coordinate)->getTooltip()) . '">' . $cellData . '</a>';
+            }
+
+            // Should the cell be written or is it swallowed by a rowspan or colspan?
+            $writeCell = !(isset($this->isSpannedCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum])
+                && $this->isSpannedCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum]);
+
+            // Colspan and Rowspan
+            $colspan = 1;
+            $rowspan = 1;
+            if (isset($this->isBaseCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum])) {
+                $spans = $this->isBaseCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum];
+                $rowSpan = $spans['rowspan'];
+                $colSpan = $spans['colspan'];
+
+                //    Also apply style from last cell in merge to fix borders -
+                //        relies on !important for non-none border declarations in createCSSStyleBorder
+                $endCellCoord = Coordinate::stringFromColumnIndex($colNum + $colSpan) . ($pRow + $rowSpan);
+                if (!$this->useInlineCss) {
+                    $cssClass .= ' style' . $pSheet->getCell($endCellCoord)->getXfIndex();
+                }
+            }
+
+            // Write
+            if ($writeCell) {
+                $this->generateRowWriteCell($html, $pSheet, $coordinate, $cellType, $cellData, $colSpan, $rowSpan, $cssClass, $colNum, $sheetIndex, $pRow);
+            }
+
+            // Next column
+            ++$colNum;
+        }
+
+        // Write row end
+        $html .= '          </tr>' . PHP_EOL;
+
+        // Return
+        return $html;
+    }
+
+    /**
+     * Takes array where of CSS properties / values and converts to CSS string.
+     *
+     * @return string
+     */
+    private function assembleCSS(array $pValue = [])
+    {
+        $pairs = [];
+        foreach ($pValue as $property => $value) {
+            $pairs[] = $property . ':' . $value;
+        }
+        $string = implode('; ', $pairs);
+
+        return $string;
+    }
+
+    /**
+     * Get images root.
+     *
+     * @return string
+     */
+    public function getImagesRoot()
+    {
+        return $this->imagesRoot;
+    }
+
+    /**
+     * Set images root.
+     *
+     * @param string $pValue
+     *
+     * @return $this
+     */
+    public function setImagesRoot($pValue)
+    {
+        $this->imagesRoot = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get embed images.
+     *
+     * @return bool
+     */
+    public function getEmbedImages()
+    {
+        return $this->embedImages;
+    }
+
+    /**
+     * Set embed images.
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setEmbedImages($pValue)
+    {
+        $this->embedImages = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get use inline CSS?
+     *
+     * @return bool
+     */
+    public function getUseInlineCss()
+    {
+        return $this->useInlineCss;
+    }
+
+    /**
+     * Set use inline CSS?
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     */
+    public function setUseInlineCss($pValue)
+    {
+        $this->useInlineCss = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get use embedded CSS?
+     *
+     * @deprecated no longer used
+     *
+     * @return bool
+     *
+     * @codeCoverageIgnore
+     */
+    public function getUseEmbeddedCSS()
+    {
+        return $this->useEmbeddedCSS;
+    }
+
+    /**
+     * Set use embedded CSS?
+     *
+     * @deprecated no longer used
+     *
+     * @param bool $pValue
+     *
+     * @return $this
+     *
+     * @codeCoverageIgnore
+     */
+    public function setUseEmbeddedCSS($pValue)
+    {
+        $this->useEmbeddedCSS = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Add color to formatted string as inline style.
+     *
+     * @param string $pValue Plain formatted value without color
+     * @param string $pFormat Format code
+     *
+     * @return string
+     */
+    public function formatColor($pValue, $pFormat)
+    {
+        // Color information, e.g. [Red] is always at the beginning
+        $color = null; // initialize
+        $matches = [];
+
+        $color_regex = '/^\\[[a-zA-Z]+\\]/';
+        if (preg_match($color_regex, $pFormat, $matches)) {
+            $color = str_replace(['[', ']'], '', $matches[0]);
+            $color = strtolower($color);
+        }
+
+        // convert to PCDATA
+        $value = htmlspecialchars($pValue);
+
+        // color span tag
+        if ($color !== null) {
+            $value = '<span style="color:' . $color . '">' . $value . '</span>';
+        }
+
+        return $value;
+    }
+
+    /**
+     * Calculate information about HTML colspan and rowspan which is not always the same as Excel's.
+     */
+    private function calculateSpans(): void
+    {
+        if ($this->spansAreCalculated) {
+            return;
+        }
+        // Identify all cells that should be omitted in HTML due to cell merge.
+        // In HTML only the upper-left cell should be written and it should have
+        //   appropriate rowspan / colspan attribute
+        $sheetIndexes = $this->sheetIndex !== null ?
+            [$this->sheetIndex] : range(0, $this->spreadsheet->getSheetCount() - 1);
+
+        foreach ($sheetIndexes as $sheetIndex) {
+            $sheet = $this->spreadsheet->getSheet($sheetIndex);
+
+            $candidateSpannedRow = [];
+
+            // loop through all Excel merged cells
+            foreach ($sheet->getMergeCells() as $cells) {
+                [$cells] = Coordinate::splitRange($cells);
+                $first = $cells[0];
+                $last = $cells[1];
+
+                [$fc, $fr] = Coordinate::coordinateFromString($first);
+                $fc = Coordinate::columnIndexFromString($fc) - 1;
+
+                [$lc, $lr] = Coordinate::coordinateFromString($last);
+                $lc = Coordinate::columnIndexFromString($lc) - 1;
+
+                // loop through the individual cells in the individual merge
+                $r = $fr - 1;
+                while ($r++ < $lr) {
+                    // also, flag this row as a HTML row that is candidate to be omitted
+                    $candidateSpannedRow[$r] = $r;
+
+                    $c = $fc - 1;
+                    while ($c++ < $lc) {
+                        if (!($c == $fc && $r == $fr)) {
+                            // not the upper-left cell (should not be written in HTML)
+                            $this->isSpannedCell[$sheetIndex][$r][$c] = [
+                                'baseCell' => [$fr, $fc],
+                            ];
+                        } else {
+                            // upper-left is the base cell that should hold the colspan/rowspan attribute
+                            $this->isBaseCell[$sheetIndex][$r][$c] = [
+                                'xlrowspan' => $lr - $fr + 1, // Excel rowspan
+                                'rowspan' => $lr - $fr + 1, // HTML rowspan, value may change
+                                'xlcolspan' => $lc - $fc + 1, // Excel colspan
+                                'colspan' => $lc - $fc + 1, // HTML colspan, value may change
+                            ];
+                        }
+                    }
+                }
+            }
+
+            $this->calculateSpansOmitRows($sheet, $sheetIndex, $candidateSpannedRow);
+
+            // TODO: Same for columns
+        }
+
+        // We have calculated the spans
+        $this->spansAreCalculated = true;
+    }
+
+    private function calculateSpansOmitRows($sheet, $sheetIndex, $candidateSpannedRow): void
+    {
+        // Identify which rows should be omitted in HTML. These are the rows where all the cells
+        //   participate in a merge and the where base cells are somewhere above.
+        $countColumns = Coordinate::columnIndexFromString($sheet->getHighestColumn());
+        foreach ($candidateSpannedRow as $rowIndex) {
+            if (isset($this->isSpannedCell[$sheetIndex][$rowIndex])) {
+                if (count($this->isSpannedCell[$sheetIndex][$rowIndex]) == $countColumns) {
+                    $this->isSpannedRow[$sheetIndex][$rowIndex] = $rowIndex;
+                }
+            }
+        }
+
+        // For each of the omitted rows we found above, the affected rowspans should be subtracted by 1
+        if (isset($this->isSpannedRow[$sheetIndex])) {
+            foreach ($this->isSpannedRow[$sheetIndex] as $rowIndex) {
+                $adjustedBaseCells = [];
+                $c = -1;
+                $e = $countColumns - 1;
+                while ($c++ < $e) {
+                    $baseCell = $this->isSpannedCell[$sheetIndex][$rowIndex][$c]['baseCell'];
+
+                    if (!in_array($baseCell, $adjustedBaseCells)) {
+                        // subtract rowspan by 1
+                        --$this->isBaseCell[$sheetIndex][$baseCell[0]][$baseCell[1]]['rowspan'];
+                        $adjustedBaseCells[] = $baseCell;
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Write a comment in the same format as LibreOffice.
+     *
+     * @see https://github.com/LibreOffice/core/blob/9fc9bf3240f8c62ad7859947ab8a033ac1fe93fa/sc/source/filter/html/htmlexp.cxx#L1073-L1092
+     *
+     * @param string $coordinate
+     *
+     * @return string
+     */
+    private function writeComment(Worksheet $pSheet, $coordinate)
+    {
+        $result = '';
+        if (!$this->isPdf && isset($pSheet->getComments()[$coordinate])) {
+            $result .= '<a class="comment-indicator"></a>';
+            $result .= '<div class="comment">' . nl2br($pSheet->getComment($coordinate)->getText()->getPlainText()) . '</div>';
+            $result .= PHP_EOL;
+        }
+
+        return $result;
+    }
+
+    /**
+     * Generate @page declarations.
+     *
+     * @param bool $generateSurroundingHTML
+     *
+     * @return    string
+     */
+    private function generatePageDeclarations($generateSurroundingHTML)
+    {
+        // Ensure that Spans have been calculated?
+        $this->calculateSpans();
+
+        // Fetch sheets
+        $sheets = [];
+        if ($this->sheetIndex === null) {
+            $sheets = $this->spreadsheet->getAllSheets();
+        } else {
+            $sheets[] = $this->spreadsheet->getSheet($this->sheetIndex);
+        }
+
+        // Construct HTML
+        $htmlPage = $generateSurroundingHTML ? ('<style type="text/css">' . PHP_EOL) : '';
+
+        // Loop all sheets
+        $sheetId = 0;
+        foreach ($sheets as $pSheet) {
+            $htmlPage .= "@page page$sheetId { ";
+            $left = StringHelper::formatNumber($pSheet->getPageMargins()->getLeft()) . 'in; ';
+            $htmlPage .= 'margin-left: ' . $left;
+            $right = StringHelper::FormatNumber($pSheet->getPageMargins()->getRight()) . 'in; ';
+            $htmlPage .= 'margin-right: ' . $right;
+            $top = StringHelper::FormatNumber($pSheet->getPageMargins()->getTop()) . 'in; ';
+            $htmlPage .= 'margin-top: ' . $top;
+            $bottom = StringHelper::FormatNumber($pSheet->getPageMargins()->getBottom()) . 'in; ';
+            $htmlPage .= 'margin-bottom: ' . $bottom;
+            $orientation = $pSheet->getPageSetup()->getOrientation();
+            if ($orientation === \PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_LANDSCAPE) {
+                $htmlPage .= 'size: landscape; ';
+            } elseif ($orientation === \PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_PORTRAIT) {
+                $htmlPage .= 'size: portrait; ';
+            }
+            $htmlPage .= "}\n";
+            ++$sheetId;
+        }
+        $htmlPage .= <<<EOF
+.navigation {page-break-after: always;}
+.scrpgbrk, div + div {page-break-before: always;}
+@media screen {
+  .gridlines td {border: 1px solid black;}
+  .gridlines th {border: 1px solid black;}
+  body>div {margin-top: 5px;}
+  body>div:first-child {margin-top: 0;}
+  .scrpgbrk {margin-top: 1px;}
+}
+@media print {
+  .gridlinesp td {border: 1px solid black;}
+  .gridlinesp th {border: 1px solid black;}
+  .navigation {display: none;}
+}
+
+EOF;
+        $htmlPage .= $generateSurroundingHTML ? ('</style>' . PHP_EOL) : '';
+
+        return $htmlPage;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/IWriter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/IWriter.php
new file mode 100755
index 0000000..5129d65
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/IWriter.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer;
+
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+
+interface IWriter
+{
+    /**
+     * IWriter constructor.
+     */
+    public function __construct(Spreadsheet $spreadsheet);
+
+    /**
+     * Write charts in workbook?
+     *        If this is true, then the Writer will write definitions for any charts that exist in the PhpSpreadsheet object.
+     *        If false (the default) it will ignore any charts defined in the PhpSpreadsheet object.
+     *
+     * @return bool
+     */
+    public function getIncludeCharts();
+
+    /**
+     * Set write charts in workbook
+     *        Set to true, to advise the Writer to include any charts that exist in the PhpSpreadsheet object.
+     *        Set to false (the default) to ignore charts.
+     *
+     * @param bool $pValue
+     *
+     * @return IWriter
+     */
+    public function setIncludeCharts($pValue);
+
+    /**
+     * Get Pre-Calculate Formulas flag
+     *     If this is true (the default), then the writer will recalculate all formulae in a workbook when saving,
+     *        so that the pre-calculated values are immediately available to MS Excel or other office spreadsheet
+     *        viewer when opening the file
+     *     If false, then formulae are not calculated on save. This is faster for saving in PhpSpreadsheet, but slower
+     *        when opening the resulting file in MS Excel, because Excel has to recalculate the formulae itself.
+     *
+     * @return bool
+     */
+    public function getPreCalculateFormulas();
+
+    /**
+     * Set Pre-Calculate Formulas
+     *        Set to true (the default) to advise the Writer to calculate all formulae on save
+     *        Set to false to prevent precalculation of formulae on save.
+     *
+     * @param bool $pValue Pre-Calculate Formulas?
+     *
+     * @return IWriter
+     */
+    public function setPreCalculateFormulas($pValue);
+
+    /**
+     * Save PhpSpreadsheet to file.
+     *
+     * @param resource|string $pFilename Name of the file to save
+     */
+    public function save($pFilename);
+
+    /**
+     * Get use disk caching where possible?
+     *
+     * @return bool
+     */
+    public function getUseDiskCaching();
+
+    /**
+     * Set use disk caching where possible?
+     *
+     * @param bool $pValue
+     * @param string $pDirectory Disk caching directory
+     *
+     * @return IWriter
+     */
+    public function setUseDiskCaching($pValue, $pDirectory = null);
+
+    /**
+     * Get disk caching directory.
+     *
+     * @return string
+     */
+    public function getDiskCachingDirectory();
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods.php
new file mode 100755
index 0000000..36f3e9c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods.php
@@ -0,0 +1,156 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer;
+
+use PhpOffice\PhpSpreadsheet\Shared\File;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
+use PhpOffice\PhpSpreadsheet\Writer\Ods\Content;
+use PhpOffice\PhpSpreadsheet\Writer\Ods\Meta;
+use PhpOffice\PhpSpreadsheet\Writer\Ods\MetaInf;
+use PhpOffice\PhpSpreadsheet\Writer\Ods\Mimetype;
+use PhpOffice\PhpSpreadsheet\Writer\Ods\Settings;
+use PhpOffice\PhpSpreadsheet\Writer\Ods\Styles;
+use PhpOffice\PhpSpreadsheet\Writer\Ods\Thumbnails;
+use ZipStream\Exception\OverflowException;
+use ZipStream\Option\Archive;
+use ZipStream\ZipStream;
+
+class Ods extends BaseWriter
+{
+    /**
+     * Private writer parts.
+     *
+     * @var Ods\WriterPart[]
+     */
+    private $writerParts = [];
+
+    /**
+     * Private PhpSpreadsheet.
+     *
+     * @var Spreadsheet
+     */
+    private $spreadSheet;
+
+    /**
+     * Create a new Ods.
+     */
+    public function __construct(Spreadsheet $spreadsheet)
+    {
+        $this->setSpreadsheet($spreadsheet);
+
+        $writerPartsArray = [
+            'content' => Content::class,
+            'meta' => Meta::class,
+            'meta_inf' => MetaInf::class,
+            'mimetype' => Mimetype::class,
+            'settings' => Settings::class,
+            'styles' => Styles::class,
+            'thumbnails' => Thumbnails::class,
+        ];
+
+        foreach ($writerPartsArray as $writer => $class) {
+            $this->writerParts[$writer] = new $class($this);
+        }
+    }
+
+    /**
+     * Get writer part.
+     *
+     * @param string $pPartName Writer part name
+     *
+     * @return null|Ods\WriterPart
+     */
+    public function getWriterPart($pPartName)
+    {
+        if ($pPartName != '' && isset($this->writerParts[strtolower($pPartName)])) {
+            return $this->writerParts[strtolower($pPartName)];
+        }
+
+        return null;
+    }
+
+    /**
+     * Save PhpSpreadsheet to file.
+     *
+     * @param resource|string $pFilename
+     */
+    public function save($pFilename): void
+    {
+        if (!$this->spreadSheet) {
+            throw new WriterException('PhpSpreadsheet object unassigned.');
+        }
+
+        // garbage collect
+        $this->spreadSheet->garbageCollect();
+
+        $this->openFileHandle($pFilename);
+
+        $zip = $this->createZip();
+
+        $zip->addFile('META-INF/manifest.xml', $this->getWriterPart('meta_inf')->writeManifest());
+        $zip->addFile('Thumbnails/thumbnail.png', $this->getWriterPart('thumbnails')->writeThumbnail());
+        $zip->addFile('content.xml', $this->getWriterPart('content')->write());
+        $zip->addFile('meta.xml', $this->getWriterPart('meta')->write());
+        $zip->addFile('mimetype', $this->getWriterPart('mimetype')->write());
+        $zip->addFile('settings.xml', $this->getWriterPart('settings')->write());
+        $zip->addFile('styles.xml', $this->getWriterPart('styles')->write());
+
+        // Close file
+        try {
+            $zip->finish();
+        } catch (OverflowException $e) {
+            throw new WriterException('Could not close resource.');
+        }
+
+        $this->maybeCloseFileHandle();
+    }
+
+    /**
+     * Create zip object.
+     *
+     * @return ZipStream
+     */
+    private function createZip()
+    {
+        // Try opening the ZIP file
+        if (!is_resource($this->fileHandle)) {
+            throw new WriterException('Could not open resource for writing.');
+        }
+
+        // Create new ZIP stream
+        $options = new Archive();
+        $options->setEnableZip64(false);
+        $options->setOutputStream($this->fileHandle);
+
+        return new ZipStream(null, $options);
+    }
+
+    /**
+     * Get Spreadsheet object.
+     *
+     * @return Spreadsheet
+     */
+    public function getSpreadsheet()
+    {
+        if ($this->spreadSheet !== null) {
+            return $this->spreadSheet;
+        }
+
+        throw new WriterException('No PhpSpreadsheet assigned.');
+    }
+
+    /**
+     * Set Spreadsheet object.
+     *
+     * @param Spreadsheet $spreadsheet PhpSpreadsheet object
+     *
+     * @return $this
+     */
+    public function setSpreadsheet(Spreadsheet $spreadsheet)
+    {
+        $this->spreadSheet = $spreadsheet;
+
+        return $this;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Cell/Comment.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Cell/Comment.php
new file mode 100755
index 0000000..b0829bf
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Cell/Comment.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Ods\Cell;
+
+use PhpOffice\PhpSpreadsheet\Cell\Cell;
+use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
+
+/**
+ * @author     Alexander Pervakov <frost-nzcr4@jagmort.com>
+ */
+class Comment
+{
+    public static function write(XMLWriter $objWriter, Cell $cell): void
+    {
+        $comments = $cell->getWorksheet()->getComments();
+        if (!isset($comments[$cell->getCoordinate()])) {
+            return;
+        }
+        $comment = $comments[$cell->getCoordinate()];
+
+        $objWriter->startElement('office:annotation');
+        $objWriter->writeAttribute('svg:width', $comment->getWidth());
+        $objWriter->writeAttribute('svg:height', $comment->getHeight());
+        $objWriter->writeAttribute('svg:x', $comment->getMarginLeft());
+        $objWriter->writeAttribute('svg:y', $comment->getMarginTop());
+        $objWriter->writeElement('dc:creator', $comment->getAuthor());
+        $objWriter->writeElement('text:p', $comment->getText()->getPlainText());
+        $objWriter->endElement();
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Content.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Content.php
new file mode 100755
index 0000000..6588473
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Content.php
@@ -0,0 +1,367 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Ods;
+
+use PhpOffice\PhpSpreadsheet\Cell\Cell;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Cell\DataType;
+use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Style\Fill;
+use PhpOffice\PhpSpreadsheet\Style\Font;
+use PhpOffice\PhpSpreadsheet\Worksheet\Row;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use PhpOffice\PhpSpreadsheet\Writer\Exception;
+use PhpOffice\PhpSpreadsheet\Writer\Ods\Cell\Comment;
+
+/**
+ * @author     Alexander Pervakov <frost-nzcr4@jagmort.com>
+ */
+class Content extends WriterPart
+{
+    const NUMBER_COLS_REPEATED_MAX = 1024;
+    const NUMBER_ROWS_REPEATED_MAX = 1048576;
+    const CELL_STYLE_PREFIX = 'ce';
+
+    /**
+     * Write content.xml to XML format.
+     *
+     * @return string XML Output
+     */
+    public function write()
+    {
+        $objWriter = null;
+        if ($this->getParentWriter()->getUseDiskCaching()) {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+        } else {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
+        }
+
+        // XML header
+        $objWriter->startDocument('1.0', 'UTF-8');
+
+        // Content
+        $objWriter->startElement('office:document-content');
+        $objWriter->writeAttribute('xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0');
+        $objWriter->writeAttribute('xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0');
+        $objWriter->writeAttribute('xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0');
+        $objWriter->writeAttribute('xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0');
+        $objWriter->writeAttribute('xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0');
+        $objWriter->writeAttribute('xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0');
+        $objWriter->writeAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
+        $objWriter->writeAttribute('xmlns:dc', 'http://purl.org/dc/elements/1.1/');
+        $objWriter->writeAttribute('xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0');
+        $objWriter->writeAttribute('xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0');
+        $objWriter->writeAttribute('xmlns:presentation', 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0');
+        $objWriter->writeAttribute('xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0');
+        $objWriter->writeAttribute('xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0');
+        $objWriter->writeAttribute('xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0');
+        $objWriter->writeAttribute('xmlns:math', 'http://www.w3.org/1998/Math/MathML');
+        $objWriter->writeAttribute('xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0');
+        $objWriter->writeAttribute('xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0');
+        $objWriter->writeAttribute('xmlns:ooo', 'http://openoffice.org/2004/office');
+        $objWriter->writeAttribute('xmlns:ooow', 'http://openoffice.org/2004/writer');
+        $objWriter->writeAttribute('xmlns:oooc', 'http://openoffice.org/2004/calc');
+        $objWriter->writeAttribute('xmlns:dom', 'http://www.w3.org/2001/xml-events');
+        $objWriter->writeAttribute('xmlns:xforms', 'http://www.w3.org/2002/xforms');
+        $objWriter->writeAttribute('xmlns:xsd', 'http://www.w3.org/2001/XMLSchema');
+        $objWriter->writeAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
+        $objWriter->writeAttribute('xmlns:rpt', 'http://openoffice.org/2005/report');
+        $objWriter->writeAttribute('xmlns:of', 'urn:oasis:names:tc:opendocument:xmlns:of:1.2');
+        $objWriter->writeAttribute('xmlns:xhtml', 'http://www.w3.org/1999/xhtml');
+        $objWriter->writeAttribute('xmlns:grddl', 'http://www.w3.org/2003/g/data-view#');
+        $objWriter->writeAttribute('xmlns:tableooo', 'http://openoffice.org/2009/table');
+        $objWriter->writeAttribute('xmlns:field', 'urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0');
+        $objWriter->writeAttribute('xmlns:formx', 'urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0');
+        $objWriter->writeAttribute('xmlns:css3t', 'http://www.w3.org/TR/css3-text/');
+        $objWriter->writeAttribute('office:version', '1.2');
+
+        $objWriter->writeElement('office:scripts');
+        $objWriter->writeElement('office:font-face-decls');
+
+        // Styles XF
+        $objWriter->startElement('office:automatic-styles');
+        $this->writeXfStyles($objWriter, $this->getParentWriter()->getSpreadsheet());
+        $objWriter->endElement();
+
+        $objWriter->startElement('office:body');
+        $objWriter->startElement('office:spreadsheet');
+        $objWriter->writeElement('table:calculation-settings');
+
+        $this->writeSheets($objWriter);
+
+        $objWriter->writeElement('table:named-expressions');
+        $objWriter->endElement();
+        $objWriter->endElement();
+        $objWriter->endElement();
+
+        return $objWriter->getData();
+    }
+
+    /**
+     * Write sheets.
+     */
+    private function writeSheets(XMLWriter $objWriter): void
+    {
+        $spreadsheet = $this->getParentWriter()->getSpreadsheet(); /** @var Spreadsheet $spreadsheet */
+        $sheetCount = $spreadsheet->getSheetCount();
+        for ($i = 0; $i < $sheetCount; ++$i) {
+            $objWriter->startElement('table:table');
+            $objWriter->writeAttribute('table:name', $spreadsheet->getSheet($i)->getTitle());
+            $objWriter->writeElement('office:forms');
+            $objWriter->startElement('table:table-column');
+            $objWriter->writeAttribute('table:number-columns-repeated', self::NUMBER_COLS_REPEATED_MAX);
+            $objWriter->endElement();
+            $this->writeRows($objWriter, $spreadsheet->getSheet($i));
+            $objWriter->endElement();
+        }
+    }
+
+    /**
+     * Write rows of the specified sheet.
+     */
+    private function writeRows(XMLWriter $objWriter, Worksheet $sheet): void
+    {
+        $numberRowsRepeated = self::NUMBER_ROWS_REPEATED_MAX;
+        $span_row = 0;
+        $rows = $sheet->getRowIterator();
+        while ($rows->valid()) {
+            --$numberRowsRepeated;
+            $row = $rows->current();
+            if ($row->getCellIterator()->valid()) {
+                if ($span_row) {
+                    $objWriter->startElement('table:table-row');
+                    if ($span_row > 1) {
+                        $objWriter->writeAttribute('table:number-rows-repeated', $span_row);
+                    }
+                    $objWriter->startElement('table:table-cell');
+                    $objWriter->writeAttribute('table:number-columns-repeated', self::NUMBER_COLS_REPEATED_MAX);
+                    $objWriter->endElement();
+                    $objWriter->endElement();
+                    $span_row = 0;
+                }
+                $objWriter->startElement('table:table-row');
+                $this->writeCells($objWriter, $row);
+                $objWriter->endElement();
+            } else {
+                ++$span_row;
+            }
+            $rows->next();
+        }
+    }
+
+    /**
+     * Write cells of the specified row.
+     */
+    private function writeCells(XMLWriter $objWriter, Row $row): void
+    {
+        $numberColsRepeated = self::NUMBER_COLS_REPEATED_MAX;
+        $prevColumn = -1;
+        $cells = $row->getCellIterator();
+        while ($cells->valid()) {
+            /** @var \PhpOffice\PhpSpreadsheet\Cell\Cell $cell */
+            $cell = $cells->current();
+            $column = Coordinate::columnIndexFromString($cell->getColumn()) - 1;
+
+            $this->writeCellSpan($objWriter, $column, $prevColumn);
+            $objWriter->startElement('table:table-cell');
+            $this->writeCellMerge($objWriter, $cell);
+
+            // Style XF
+            $style = $cell->getXfIndex();
+            if ($style !== null) {
+                $objWriter->writeAttribute('table:style-name', self::CELL_STYLE_PREFIX . $style);
+            }
+
+            switch ($cell->getDataType()) {
+                case DataType::TYPE_BOOL:
+                    $objWriter->writeAttribute('office:value-type', 'boolean');
+                    $objWriter->writeAttribute('office:value', $cell->getValue());
+                    $objWriter->writeElement('text:p', $cell->getValue());
+
+                    break;
+                case DataType::TYPE_ERROR:
+                    throw new Exception('Writing of error not implemented yet.');
+
+                    break;
+                case DataType::TYPE_FORMULA:
+                    $formulaValue = $cell->getValue();
+                    if ($this->getParentWriter()->getPreCalculateFormulas()) {
+                        try {
+                            $formulaValue = $cell->getCalculatedValue();
+                        } catch (Exception $e) {
+                            // don't do anything
+                        }
+                    }
+                    $objWriter->writeAttribute('table:formula', 'of:' . $cell->getValue());
+                    if (is_numeric($formulaValue)) {
+                        $objWriter->writeAttribute('office:value-type', 'float');
+                    } else {
+                        $objWriter->writeAttribute('office:value-type', 'string');
+                    }
+                    $objWriter->writeAttribute('office:value', $formulaValue);
+                    $objWriter->writeElement('text:p', $formulaValue);
+
+                    break;
+                case DataType::TYPE_INLINE:
+                    throw new Exception('Writing of inline not implemented yet.');
+
+                    break;
+                case DataType::TYPE_NUMERIC:
+                    $objWriter->writeAttribute('office:value-type', 'float');
+                    $objWriter->writeAttribute('office:value', $cell->getValue());
+                    $objWriter->writeElement('text:p', $cell->getValue());
+
+                    break;
+                case DataType::TYPE_STRING:
+                    $objWriter->writeAttribute('office:value-type', 'string');
+                    $objWriter->writeElement('text:p', $cell->getValue());
+
+                    break;
+            }
+            Comment::write($objWriter, $cell);
+            $objWriter->endElement();
+            $prevColumn = $column;
+            $cells->next();
+        }
+        $numberColsRepeated = $numberColsRepeated - $prevColumn - 1;
+        if ($numberColsRepeated > 0) {
+            if ($numberColsRepeated > 1) {
+                $objWriter->startElement('table:table-cell');
+                $objWriter->writeAttribute('table:number-columns-repeated', $numberColsRepeated);
+                $objWriter->endElement();
+            } else {
+                $objWriter->writeElement('table:table-cell');
+            }
+        }
+    }
+
+    /**
+     * Write span.
+     *
+     * @param int $curColumn
+     * @param int $prevColumn
+     */
+    private function writeCellSpan(XMLWriter $objWriter, $curColumn, $prevColumn): void
+    {
+        $diff = $curColumn - $prevColumn - 1;
+        if (1 === $diff) {
+            $objWriter->writeElement('table:table-cell');
+        } elseif ($diff > 1) {
+            $objWriter->startElement('table:table-cell');
+            $objWriter->writeAttribute('table:number-columns-repeated', $diff);
+            $objWriter->endElement();
+        }
+    }
+
+    /**
+     * Write XF cell styles.
+     */
+    private function writeXfStyles(XMLWriter $writer, Spreadsheet $spreadsheet): void
+    {
+        foreach ($spreadsheet->getCellXfCollection() as $style) {
+            $writer->startElement('style:style');
+            $writer->writeAttribute('style:name', self::CELL_STYLE_PREFIX . $style->getIndex());
+            $writer->writeAttribute('style:family', 'table-cell');
+            $writer->writeAttribute('style:parent-style-name', 'Default');
+
+            // style:text-properties
+
+            // Font
+            $writer->startElement('style:text-properties');
+
+            $font = $style->getFont();
+
+            if ($font->getBold()) {
+                $writer->writeAttribute('fo:font-weight', 'bold');
+                $writer->writeAttribute('style:font-weight-complex', 'bold');
+                $writer->writeAttribute('style:font-weight-asian', 'bold');
+            }
+
+            if ($font->getItalic()) {
+                $writer->writeAttribute('fo:font-style', 'italic');
+            }
+
+            if ($color = $font->getColor()) {
+                $writer->writeAttribute('fo:color', sprintf('#%s', $color->getRGB()));
+            }
+
+            if ($family = $font->getName()) {
+                $writer->writeAttribute('fo:font-family', $family);
+            }
+
+            if ($size = $font->getSize()) {
+                $writer->writeAttribute('fo:font-size', sprintf('%.1Fpt', $size));
+            }
+
+            if ($font->getUnderline() && $font->getUnderline() != Font::UNDERLINE_NONE) {
+                $writer->writeAttribute('style:text-underline-style', 'solid');
+                $writer->writeAttribute('style:text-underline-width', 'auto');
+                $writer->writeAttribute('style:text-underline-color', 'font-color');
+
+                switch ($font->getUnderline()) {
+                    case Font::UNDERLINE_DOUBLE:
+                        $writer->writeAttribute('style:text-underline-type', 'double');
+
+                        break;
+                    case Font::UNDERLINE_SINGLE:
+                        $writer->writeAttribute('style:text-underline-type', 'single');
+
+                        break;
+                }
+            }
+
+            $writer->endElement(); // Close style:text-properties
+
+            // style:table-cell-properties
+
+            $writer->startElement('style:table-cell-properties');
+            $writer->writeAttribute('style:rotation-align', 'none');
+
+            // Fill
+            if ($fill = $style->getFill()) {
+                switch ($fill->getFillType()) {
+                    case Fill::FILL_SOLID:
+                        $writer->writeAttribute('fo:background-color', sprintf(
+                            '#%s',
+                            strtolower($fill->getStartColor()->getRGB())
+                        ));
+
+                        break;
+                    case Fill::FILL_GRADIENT_LINEAR:
+                    case Fill::FILL_GRADIENT_PATH:
+                        /// TODO :: To be implemented
+                        break;
+                    case Fill::FILL_NONE:
+                    default:
+                }
+            }
+
+            $writer->endElement(); // Close style:table-cell-properties
+
+            // End
+
+            $writer->endElement(); // Close style:style
+        }
+    }
+
+    /**
+     * Write attributes for merged cell.
+     */
+    private function writeCellMerge(XMLWriter $objWriter, Cell $cell): void
+    {
+        if (!$cell->isMergeRangeValueCell()) {
+            return;
+        }
+
+        $mergeRange = Coordinate::splitRange($cell->getMergeRange());
+        [$startCell, $endCell] = $mergeRange[0];
+        $start = Coordinate::coordinateFromString($startCell);
+        $end = Coordinate::coordinateFromString($endCell);
+        $columnSpan = Coordinate::columnIndexFromString($end[0]) - Coordinate::columnIndexFromString($start[0]) + 1;
+        $rowSpan = $end[1] - $start[1] + 1;
+
+        $objWriter->writeAttribute('table:number-columns-spanned', $columnSpan);
+        $objWriter->writeAttribute('table:number-rows-spanned', $rowSpan);
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Meta.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Meta.php
new file mode 100755
index 0000000..365221f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Meta.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Ods;
+
+use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+
+class Meta extends WriterPart
+{
+    /**
+     * Write meta.xml to XML format.
+     *
+     * @param Spreadsheet $spreadsheet
+     *
+     * @return string XML Output
+     */
+    public function write(?Spreadsheet $spreadsheet = null)
+    {
+        if (!$spreadsheet) {
+            $spreadsheet = $this->getParentWriter()->getSpreadsheet();
+        }
+
+        $objWriter = null;
+        if ($this->getParentWriter()->getUseDiskCaching()) {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+        } else {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
+        }
+
+        // XML header
+        $objWriter->startDocument('1.0', 'UTF-8');
+
+        // Meta
+        $objWriter->startElement('office:document-meta');
+
+        $objWriter->writeAttribute('xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0');
+        $objWriter->writeAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
+        $objWriter->writeAttribute('xmlns:dc', 'http://purl.org/dc/elements/1.1/');
+        $objWriter->writeAttribute('xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0');
+        $objWriter->writeAttribute('xmlns:ooo', 'http://openoffice.org/2004/office');
+        $objWriter->writeAttribute('xmlns:grddl', 'http://www.w3.org/2003/g/data-view#');
+        $objWriter->writeAttribute('office:version', '1.2');
+
+        $objWriter->startElement('office:meta');
+
+        $objWriter->writeElement('meta:initial-creator', $spreadsheet->getProperties()->getCreator());
+        $objWriter->writeElement('dc:creator', $spreadsheet->getProperties()->getCreator());
+        $objWriter->writeElement('meta:creation-date', date(DATE_W3C, $spreadsheet->getProperties()->getCreated()));
+        $objWriter->writeElement('dc:date', date(DATE_W3C, $spreadsheet->getProperties()->getCreated()));
+        $objWriter->writeElement('dc:title', $spreadsheet->getProperties()->getTitle());
+        $objWriter->writeElement('dc:description', $spreadsheet->getProperties()->getDescription());
+        $objWriter->writeElement('dc:subject', $spreadsheet->getProperties()->getSubject());
+        $keywords = explode(' ', $spreadsheet->getProperties()->getKeywords());
+        foreach ($keywords as $keyword) {
+            $objWriter->writeElement('meta:keyword', $keyword);
+        }
+
+        //<meta:document-statistic meta:table-count="XXX" meta:cell-count="XXX" meta:object-count="XXX"/>
+        $objWriter->startElement('meta:user-defined');
+        $objWriter->writeAttribute('meta:name', 'Company');
+        $objWriter->writeRaw($spreadsheet->getProperties()->getCompany());
+        $objWriter->endElement();
+
+        $objWriter->startElement('meta:user-defined');
+        $objWriter->writeAttribute('meta:name', 'category');
+        $objWriter->writeRaw($spreadsheet->getProperties()->getCategory());
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        return $objWriter->getData();
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/MetaInf.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/MetaInf.php
new file mode 100755
index 0000000..c9085cf
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/MetaInf.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Ods;
+
+use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
+
+class MetaInf extends WriterPart
+{
+    /**
+     * Write META-INF/manifest.xml to XML format.
+     *
+     * @return string XML Output
+     */
+    public function writeManifest()
+    {
+        $objWriter = null;
+        if ($this->getParentWriter()->getUseDiskCaching()) {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+        } else {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
+        }
+
+        // XML header
+        $objWriter->startDocument('1.0', 'UTF-8');
+
+        // Manifest
+        $objWriter->startElement('manifest:manifest');
+        $objWriter->writeAttribute('xmlns:manifest', 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0');
+        $objWriter->writeAttribute('manifest:version', '1.2');
+
+        $objWriter->startElement('manifest:file-entry');
+        $objWriter->writeAttribute('manifest:full-path', '/');
+        $objWriter->writeAttribute('manifest:version', '1.2');
+        $objWriter->writeAttribute('manifest:media-type', 'application/vnd.oasis.opendocument.spreadsheet');
+        $objWriter->endElement();
+        $objWriter->startElement('manifest:file-entry');
+        $objWriter->writeAttribute('manifest:full-path', 'meta.xml');
+        $objWriter->writeAttribute('manifest:media-type', 'text/xml');
+        $objWriter->endElement();
+        $objWriter->startElement('manifest:file-entry');
+        $objWriter->writeAttribute('manifest:full-path', 'settings.xml');
+        $objWriter->writeAttribute('manifest:media-type', 'text/xml');
+        $objWriter->endElement();
+        $objWriter->startElement('manifest:file-entry');
+        $objWriter->writeAttribute('manifest:full-path', 'content.xml');
+        $objWriter->writeAttribute('manifest:media-type', 'text/xml');
+        $objWriter->endElement();
+        $objWriter->startElement('manifest:file-entry');
+        $objWriter->writeAttribute('manifest:full-path', 'Thumbnails/thumbnail.png');
+        $objWriter->writeAttribute('manifest:media-type', 'image/png');
+        $objWriter->endElement();
+        $objWriter->startElement('manifest:file-entry');
+        $objWriter->writeAttribute('manifest:full-path', 'styles.xml');
+        $objWriter->writeAttribute('manifest:media-type', 'text/xml');
+        $objWriter->endElement();
+        $objWriter->endElement();
+
+        return $objWriter->getData();
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Mimetype.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Mimetype.php
new file mode 100755
index 0000000..4aac368
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Mimetype.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Ods;
+
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+
+class Mimetype extends WriterPart
+{
+    /**
+     * Write mimetype to plain text format.
+     *
+     * @param Spreadsheet $spreadsheet
+     *
+     * @return string XML Output
+     */
+    public function write(?Spreadsheet $spreadsheet = null)
+    {
+        return 'application/vnd.oasis.opendocument.spreadsheet';
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Settings.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Settings.php
new file mode 100755
index 0000000..d458e8c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Settings.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Ods;
+
+use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+
+class Settings extends WriterPart
+{
+    /**
+     * Write settings.xml to XML format.
+     *
+     * @param Spreadsheet $spreadsheet
+     *
+     * @return string XML Output
+     */
+    public function write(?Spreadsheet $spreadsheet = null)
+    {
+        $objWriter = null;
+        if ($this->getParentWriter()->getUseDiskCaching()) {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+        } else {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
+        }
+
+        // XML header
+        $objWriter->startDocument('1.0', 'UTF-8');
+
+        // Settings
+        $objWriter->startElement('office:document-settings');
+        $objWriter->writeAttribute('xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0');
+        $objWriter->writeAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
+        $objWriter->writeAttribute('xmlns:config', 'urn:oasis:names:tc:opendocument:xmlns:config:1.0');
+        $objWriter->writeAttribute('xmlns:ooo', 'http://openoffice.org/2004/office');
+        $objWriter->writeAttribute('office:version', '1.2');
+
+        $objWriter->startElement('office:settings');
+        $objWriter->startElement('config:config-item-set');
+        $objWriter->writeAttribute('config:name', 'ooo:view-settings');
+        $objWriter->startElement('config:config-item-map-indexed');
+        $objWriter->writeAttribute('config:name', 'Views');
+        $objWriter->endElement();
+        $objWriter->endElement();
+        $objWriter->startElement('config:config-item-set');
+        $objWriter->writeAttribute('config:name', 'ooo:configuration-settings');
+        $objWriter->endElement();
+        $objWriter->endElement();
+        $objWriter->endElement();
+
+        return $objWriter->getData();
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Styles.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Styles.php
new file mode 100755
index 0000000..7ba7eba
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Styles.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Ods;
+
+use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+
+class Styles extends WriterPart
+{
+    /**
+     * Write styles.xml to XML format.
+     *
+     * @param Spreadsheet $spreadsheet
+     *
+     * @return string XML Output
+     */
+    public function write(?Spreadsheet $spreadsheet = null)
+    {
+        $objWriter = null;
+        if ($this->getParentWriter()->getUseDiskCaching()) {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+        } else {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
+        }
+
+        // XML header
+        $objWriter->startDocument('1.0', 'UTF-8');
+
+        // Content
+        $objWriter->startElement('office:document-styles');
+        $objWriter->writeAttribute('xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0');
+        $objWriter->writeAttribute('xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0');
+        $objWriter->writeAttribute('xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0');
+        $objWriter->writeAttribute('xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0');
+        $objWriter->writeAttribute('xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0');
+        $objWriter->writeAttribute('xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0');
+        $objWriter->writeAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
+        $objWriter->writeAttribute('xmlns:dc', 'http://purl.org/dc/elements/1.1/');
+        $objWriter->writeAttribute('xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0');
+        $objWriter->writeAttribute('xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0');
+        $objWriter->writeAttribute('xmlns:presentation', 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0');
+        $objWriter->writeAttribute('xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0');
+        $objWriter->writeAttribute('xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0');
+        $objWriter->writeAttribute('xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0');
+        $objWriter->writeAttribute('xmlns:math', 'http://www.w3.org/1998/Math/MathML');
+        $objWriter->writeAttribute('xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0');
+        $objWriter->writeAttribute('xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0');
+        $objWriter->writeAttribute('xmlns:ooo', 'http://openoffice.org/2004/office');
+        $objWriter->writeAttribute('xmlns:ooow', 'http://openoffice.org/2004/writer');
+        $objWriter->writeAttribute('xmlns:oooc', 'http://openoffice.org/2004/calc');
+        $objWriter->writeAttribute('xmlns:dom', 'http://www.w3.org/2001/xml-events');
+        $objWriter->writeAttribute('xmlns:rpt', 'http://openoffice.org/2005/report');
+        $objWriter->writeAttribute('xmlns:of', 'urn:oasis:names:tc:opendocument:xmlns:of:1.2');
+        $objWriter->writeAttribute('xmlns:xhtml', 'http://www.w3.org/1999/xhtml');
+        $objWriter->writeAttribute('xmlns:grddl', 'http://www.w3.org/2003/g/data-view#');
+        $objWriter->writeAttribute('xmlns:tableooo', 'http://openoffice.org/2009/table');
+        $objWriter->writeAttribute('xmlns:css3t', 'http://www.w3.org/TR/css3-text/');
+        $objWriter->writeAttribute('office:version', '1.2');
+
+        $objWriter->writeElement('office:font-face-decls');
+        $objWriter->writeElement('office:styles');
+        $objWriter->writeElement('office:automatic-styles');
+        $objWriter->writeElement('office:master-styles');
+        $objWriter->endElement();
+
+        return $objWriter->getData();
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Thumbnails.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Thumbnails.php
new file mode 100755
index 0000000..dfab065
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Thumbnails.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Ods;
+
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+
+class Thumbnails extends WriterPart
+{
+    /**
+     * Write Thumbnails/thumbnail.png to PNG format.
+     *
+     * @param Spreadsheet $spreadsheet
+     *
+     * @return string XML Output
+     */
+    public function writeThumbnail(?Spreadsheet $spreadsheet = null)
+    {
+        return '';
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/WriterPart.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/WriterPart.php
new file mode 100755
index 0000000..1982c45
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/WriterPart.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Ods;
+
+use PhpOffice\PhpSpreadsheet\Writer\Ods;
+
+abstract class WriterPart
+{
+    /**
+     * Parent Ods object.
+     *
+     * @var Ods
+     */
+    private $parentWriter;
+
+    /**
+     * Get Ods writer.
+     *
+     * @return Ods
+     */
+    public function getParentWriter()
+    {
+        return $this->parentWriter;
+    }
+
+    /**
+     * Set parent Ods writer.
+     */
+    public function __construct(Ods $writer)
+    {
+        $this->parentWriter = $writer;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf.php
new file mode 100755
index 0000000..8722045
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf.php
@@ -0,0 +1,253 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer;
+
+use PhpOffice\PhpSpreadsheet\Shared\File;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup;
+use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
+
+abstract class Pdf extends Html
+{
+    /**
+     * Temporary storage directory.
+     *
+     * @var string
+     */
+    protected $tempDir = '';
+
+    /**
+     * Font.
+     *
+     * @var string
+     */
+    protected $font = 'freesans';
+
+    /**
+     * Orientation (Over-ride).
+     *
+     * @var string
+     */
+    protected $orientation;
+
+    /**
+     * Paper size (Over-ride).
+     *
+     * @var int
+     */
+    protected $paperSize;
+
+    /**
+     * Paper Sizes xRef List.
+     *
+     * @var array
+     */
+    protected static $paperSizes = [
+        PageSetup::PAPERSIZE_LETTER => 'LETTER', //    (8.5 in. by 11 in.)
+        PageSetup::PAPERSIZE_LETTER_SMALL => 'LETTER', //    (8.5 in. by 11 in.)
+        PageSetup::PAPERSIZE_TABLOID => [792.00, 1224.00], //    (11 in. by 17 in.)
+        PageSetup::PAPERSIZE_LEDGER => [1224.00, 792.00], //    (17 in. by 11 in.)
+        PageSetup::PAPERSIZE_LEGAL => 'LEGAL', //    (8.5 in. by 14 in.)
+        PageSetup::PAPERSIZE_STATEMENT => [396.00, 612.00], //    (5.5 in. by 8.5 in.)
+        PageSetup::PAPERSIZE_EXECUTIVE => 'EXECUTIVE', //    (7.25 in. by 10.5 in.)
+        PageSetup::PAPERSIZE_A3 => 'A3', //    (297 mm by 420 mm)
+        PageSetup::PAPERSIZE_A4 => 'A4', //    (210 mm by 297 mm)
+        PageSetup::PAPERSIZE_A4_SMALL => 'A4', //    (210 mm by 297 mm)
+        PageSetup::PAPERSIZE_A5 => 'A5', //    (148 mm by 210 mm)
+        PageSetup::PAPERSIZE_B4 => 'B4', //    (250 mm by 353 mm)
+        PageSetup::PAPERSIZE_B5 => 'B5', //    (176 mm by 250 mm)
+        PageSetup::PAPERSIZE_FOLIO => 'FOLIO', //    (8.5 in. by 13 in.)
+        PageSetup::PAPERSIZE_QUARTO => [609.45, 779.53], //    (215 mm by 275 mm)
+        PageSetup::PAPERSIZE_STANDARD_1 => [720.00, 1008.00], //    (10 in. by 14 in.)
+        PageSetup::PAPERSIZE_STANDARD_2 => [792.00, 1224.00], //    (11 in. by 17 in.)
+        PageSetup::PAPERSIZE_NOTE => 'LETTER', //    (8.5 in. by 11 in.)
+        PageSetup::PAPERSIZE_NO9_ENVELOPE => [279.00, 639.00], //    (3.875 in. by 8.875 in.)
+        PageSetup::PAPERSIZE_NO10_ENVELOPE => [297.00, 684.00], //    (4.125 in. by 9.5 in.)
+        PageSetup::PAPERSIZE_NO11_ENVELOPE => [324.00, 747.00], //    (4.5 in. by 10.375 in.)
+        PageSetup::PAPERSIZE_NO12_ENVELOPE => [342.00, 792.00], //    (4.75 in. by 11 in.)
+        PageSetup::PAPERSIZE_NO14_ENVELOPE => [360.00, 828.00], //    (5 in. by 11.5 in.)
+        PageSetup::PAPERSIZE_C => [1224.00, 1584.00], //    (17 in. by 22 in.)
+        PageSetup::PAPERSIZE_D => [1584.00, 2448.00], //    (22 in. by 34 in.)
+        PageSetup::PAPERSIZE_E => [2448.00, 3168.00], //    (34 in. by 44 in.)
+        PageSetup::PAPERSIZE_DL_ENVELOPE => [311.81, 623.62], //    (110 mm by 220 mm)
+        PageSetup::PAPERSIZE_C5_ENVELOPE => 'C5', //    (162 mm by 229 mm)
+        PageSetup::PAPERSIZE_C3_ENVELOPE => 'C3', //    (324 mm by 458 mm)
+        PageSetup::PAPERSIZE_C4_ENVELOPE => 'C4', //    (229 mm by 324 mm)
+        PageSetup::PAPERSIZE_C6_ENVELOPE => 'C6', //    (114 mm by 162 mm)
+        PageSetup::PAPERSIZE_C65_ENVELOPE => [323.15, 649.13], //    (114 mm by 229 mm)
+        PageSetup::PAPERSIZE_B4_ENVELOPE => 'B4', //    (250 mm by 353 mm)
+        PageSetup::PAPERSIZE_B5_ENVELOPE => 'B5', //    (176 mm by 250 mm)
+        PageSetup::PAPERSIZE_B6_ENVELOPE => [498.90, 354.33], //    (176 mm by 125 mm)
+        PageSetup::PAPERSIZE_ITALY_ENVELOPE => [311.81, 651.97], //    (110 mm by 230 mm)
+        PageSetup::PAPERSIZE_MONARCH_ENVELOPE => [279.00, 540.00], //    (3.875 in. by 7.5 in.)
+        PageSetup::PAPERSIZE_6_3_4_ENVELOPE => [261.00, 468.00], //    (3.625 in. by 6.5 in.)
+        PageSetup::PAPERSIZE_US_STANDARD_FANFOLD => [1071.00, 792.00], //    (14.875 in. by 11 in.)
+        PageSetup::PAPERSIZE_GERMAN_STANDARD_FANFOLD => [612.00, 864.00], //    (8.5 in. by 12 in.)
+        PageSetup::PAPERSIZE_GERMAN_LEGAL_FANFOLD => 'FOLIO', //    (8.5 in. by 13 in.)
+        PageSetup::PAPERSIZE_ISO_B4 => 'B4', //    (250 mm by 353 mm)
+        PageSetup::PAPERSIZE_JAPANESE_DOUBLE_POSTCARD => [566.93, 419.53], //    (200 mm by 148 mm)
+        PageSetup::PAPERSIZE_STANDARD_PAPER_1 => [648.00, 792.00], //    (9 in. by 11 in.)
+        PageSetup::PAPERSIZE_STANDARD_PAPER_2 => [720.00, 792.00], //    (10 in. by 11 in.)
+        PageSetup::PAPERSIZE_STANDARD_PAPER_3 => [1080.00, 792.00], //    (15 in. by 11 in.)
+        PageSetup::PAPERSIZE_INVITE_ENVELOPE => [623.62, 623.62], //    (220 mm by 220 mm)
+        PageSetup::PAPERSIZE_LETTER_EXTRA_PAPER => [667.80, 864.00], //    (9.275 in. by 12 in.)
+        PageSetup::PAPERSIZE_LEGAL_EXTRA_PAPER => [667.80, 1080.00], //    (9.275 in. by 15 in.)
+        PageSetup::PAPERSIZE_TABLOID_EXTRA_PAPER => [841.68, 1296.00], //    (11.69 in. by 18 in.)
+        PageSetup::PAPERSIZE_A4_EXTRA_PAPER => [668.98, 912.76], //    (236 mm by 322 mm)
+        PageSetup::PAPERSIZE_LETTER_TRANSVERSE_PAPER => [595.80, 792.00], //    (8.275 in. by 11 in.)
+        PageSetup::PAPERSIZE_A4_TRANSVERSE_PAPER => 'A4', //    (210 mm by 297 mm)
+        PageSetup::PAPERSIZE_LETTER_EXTRA_TRANSVERSE_PAPER => [667.80, 864.00], //    (9.275 in. by 12 in.)
+        PageSetup::PAPERSIZE_SUPERA_SUPERA_A4_PAPER => [643.46, 1009.13], //    (227 mm by 356 mm)
+        PageSetup::PAPERSIZE_SUPERB_SUPERB_A3_PAPER => [864.57, 1380.47], //    (305 mm by 487 mm)
+        PageSetup::PAPERSIZE_LETTER_PLUS_PAPER => [612.00, 913.68], //    (8.5 in. by 12.69 in.)
+        PageSetup::PAPERSIZE_A4_PLUS_PAPER => [595.28, 935.43], //    (210 mm by 330 mm)
+        PageSetup::PAPERSIZE_A5_TRANSVERSE_PAPER => 'A5', //    (148 mm by 210 mm)
+        PageSetup::PAPERSIZE_JIS_B5_TRANSVERSE_PAPER => [515.91, 728.50], //    (182 mm by 257 mm)
+        PageSetup::PAPERSIZE_A3_EXTRA_PAPER => [912.76, 1261.42], //    (322 mm by 445 mm)
+        PageSetup::PAPERSIZE_A5_EXTRA_PAPER => [493.23, 666.14], //    (174 mm by 235 mm)
+        PageSetup::PAPERSIZE_ISO_B5_EXTRA_PAPER => [569.76, 782.36], //    (201 mm by 276 mm)
+        PageSetup::PAPERSIZE_A2_PAPER => 'A2', //    (420 mm by 594 mm)
+        PageSetup::PAPERSIZE_A3_TRANSVERSE_PAPER => 'A3', //    (297 mm by 420 mm)
+        PageSetup::PAPERSIZE_A3_EXTRA_TRANSVERSE_PAPER => [912.76, 1261.42], //    (322 mm by 445 mm)
+    ];
+
+    /**
+     * Create a new PDF Writer instance.
+     *
+     * @param Spreadsheet $spreadsheet Spreadsheet object
+     */
+    public function __construct(Spreadsheet $spreadsheet)
+    {
+        parent::__construct($spreadsheet);
+        //$this->setUseInlineCss(true);
+        $this->tempDir = File::sysGetTempDir() . '/phpsppdf';
+        $this->isPdf = true;
+    }
+
+    /**
+     * Get Font.
+     *
+     * @return string
+     */
+    public function getFont()
+    {
+        return $this->font;
+    }
+
+    /**
+     * Set font. Examples:
+     *      'arialunicid0-chinese-simplified'
+     *      'arialunicid0-chinese-traditional'
+     *      'arialunicid0-korean'
+     *      'arialunicid0-japanese'.
+     *
+     * @param string $fontName
+     *
+     * @return $this
+     */
+    public function setFont($fontName)
+    {
+        $this->font = $fontName;
+
+        return $this;
+    }
+
+    /**
+     * Get Paper Size.
+     *
+     * @return int
+     */
+    public function getPaperSize()
+    {
+        return $this->paperSize;
+    }
+
+    /**
+     * Set Paper Size.
+     *
+     * @param string $pValue Paper size see PageSetup::PAPERSIZE_*
+     *
+     * @return self
+     */
+    public function setPaperSize($pValue)
+    {
+        $this->paperSize = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get Orientation.
+     *
+     * @return string
+     */
+    public function getOrientation()
+    {
+        return $this->orientation;
+    }
+
+    /**
+     * Set Orientation.
+     *
+     * @param string $pValue Page orientation see PageSetup::ORIENTATION_*
+     *
+     * @return self
+     */
+    public function setOrientation($pValue)
+    {
+        $this->orientation = $pValue;
+
+        return $this;
+    }
+
+    /**
+     * Get temporary storage directory.
+     *
+     * @return string
+     */
+    public function getTempDir()
+    {
+        return $this->tempDir;
+    }
+
+    /**
+     * Set temporary storage directory.
+     *
+     * @param string $pValue Temporary storage directory
+     *
+     * @return self
+     */
+    public function setTempDir($pValue)
+    {
+        if (is_dir($pValue)) {
+            $this->tempDir = $pValue;
+        } else {
+            throw new WriterException("Directory does not exist: $pValue");
+        }
+
+        return $this;
+    }
+
+    /**
+     * Save Spreadsheet to PDF file, pre-save.
+     *
+     * @param string $pFilename Name of the file to save as
+     *
+     * @return resource
+     */
+    protected function prepareForSave($pFilename)
+    {
+        //  Open file
+        $this->openFileHandle($pFilename);
+
+        return $this->fileHandle;
+    }
+
+    /**
+     * Save PhpSpreadsheet to PDF file, post-save.
+     */
+    protected function restoreStateAfterSave(): void
+    {
+        $this->maybeCloseFileHandle();
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php
new file mode 100755
index 0000000..9ae2cce
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Pdf;
+
+use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup;
+use PhpOffice\PhpSpreadsheet\Writer\Pdf;
+
+class Dompdf extends Pdf
+{
+    /**
+     * Gets the implementation of external PDF library that should be used.
+     *
+     * @return \Dompdf\Dompdf implementation
+     */
+    protected function createExternalWriterInstance()
+    {
+        return new \Dompdf\Dompdf();
+    }
+
+    /**
+     * Save Spreadsheet to file.
+     *
+     * @param string $pFilename Name of the file to save as
+     */
+    public function save($pFilename): void
+    {
+        $fileHandle = parent::prepareForSave($pFilename);
+
+        //  Default PDF paper size
+        $paperSize = 'LETTER'; //    Letter    (8.5 in. by 11 in.)
+
+        //  Check for paper size and page orientation
+        if ($this->getSheetIndex() === null) {
+            $orientation = ($this->spreadsheet->getSheet(0)->getPageSetup()->getOrientation()
+                == PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P';
+            $printPaperSize = $this->spreadsheet->getSheet(0)->getPageSetup()->getPaperSize();
+        } else {
+            $orientation = ($this->spreadsheet->getSheet($this->getSheetIndex())->getPageSetup()->getOrientation()
+                == PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P';
+            $printPaperSize = $this->spreadsheet->getSheet($this->getSheetIndex())->getPageSetup()->getPaperSize();
+        }
+
+        $orientation = ($orientation == 'L') ? 'landscape' : 'portrait';
+
+        //  Override Page Orientation
+        if ($this->getOrientation() !== null) {
+            $orientation = ($this->getOrientation() == PageSetup::ORIENTATION_DEFAULT)
+                ? PageSetup::ORIENTATION_PORTRAIT
+                : $this->getOrientation();
+        }
+        //  Override Paper Size
+        if ($this->getPaperSize() !== null) {
+            $printPaperSize = $this->getPaperSize();
+        }
+
+        if (isset(self::$paperSizes[$printPaperSize])) {
+            $paperSize = self::$paperSizes[$printPaperSize];
+        }
+
+        //  Create PDF
+        $pdf = $this->createExternalWriterInstance();
+        $pdf->setPaper(strtolower($paperSize), $orientation);
+
+        $pdf->loadHtml($this->generateHTMLAll());
+        $pdf->render();
+
+        //  Write to file
+        fwrite($fileHandle, $pdf->output());
+
+        parent::restoreStateAfterSave();
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php
new file mode 100755
index 0000000..75e0010
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php
@@ -0,0 +1,106 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Pdf;
+
+use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup;
+use PhpOffice\PhpSpreadsheet\Writer\Pdf;
+
+class Mpdf extends Pdf
+{
+    /**
+     * Gets the implementation of external PDF library that should be used.
+     *
+     * @param array $config Configuration array
+     *
+     * @return \Mpdf\Mpdf implementation
+     */
+    protected function createExternalWriterInstance($config)
+    {
+        return new \Mpdf\Mpdf($config);
+    }
+
+    /**
+     * Save Spreadsheet to file.
+     *
+     * @param string $pFilename Name of the file to save as
+     */
+    public function save($pFilename): void
+    {
+        $fileHandle = parent::prepareForSave($pFilename);
+
+        //  Default PDF paper size
+        $paperSize = 'LETTER'; //    Letter    (8.5 in. by 11 in.)
+
+        //  Check for paper size and page orientation
+        if (null === $this->getSheetIndex()) {
+            $orientation = ($this->spreadsheet->getSheet(0)->getPageSetup()->getOrientation()
+                == PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P';
+            $printPaperSize = $this->spreadsheet->getSheet(0)->getPageSetup()->getPaperSize();
+        } else {
+            $orientation = ($this->spreadsheet->getSheet($this->getSheetIndex())->getPageSetup()->getOrientation()
+                == PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P';
+            $printPaperSize = $this->spreadsheet->getSheet($this->getSheetIndex())->getPageSetup()->getPaperSize();
+        }
+        $this->setOrientation($orientation);
+
+        //  Override Page Orientation
+        if (null !== $this->getOrientation()) {
+            $orientation = ($this->getOrientation() == PageSetup::ORIENTATION_DEFAULT)
+                ? PageSetup::ORIENTATION_PORTRAIT
+                : $this->getOrientation();
+        }
+        $orientation = strtoupper($orientation);
+
+        //  Override Paper Size
+        if (null !== $this->getPaperSize()) {
+            $printPaperSize = $this->getPaperSize();
+        }
+
+        if (isset(self::$paperSizes[$printPaperSize])) {
+            $paperSize = self::$paperSizes[$printPaperSize];
+        }
+
+        //  Create PDF
+        $config = ['tempDir' => $this->tempDir . '/mpdf'];
+        $pdf = $this->createExternalWriterInstance($config);
+        $ortmp = $orientation;
+        $pdf->_setPageSize(strtoupper($paperSize), $ortmp);
+        $pdf->DefOrientation = $orientation;
+        $pdf->AddPageByArray([
+            'orientation' => $orientation,
+            'margin-left' => $this->inchesToMm($this->spreadsheet->getActiveSheet()->getPageMargins()->getLeft()),
+            'margin-right' => $this->inchesToMm($this->spreadsheet->getActiveSheet()->getPageMargins()->getRight()),
+            'margin-top' => $this->inchesToMm($this->spreadsheet->getActiveSheet()->getPageMargins()->getTop()),
+            'margin-bottom' => $this->inchesToMm($this->spreadsheet->getActiveSheet()->getPageMargins()->getBottom()),
+        ]);
+
+        //  Document info
+        $pdf->SetTitle($this->spreadsheet->getProperties()->getTitle());
+        $pdf->SetAuthor($this->spreadsheet->getProperties()->getCreator());
+        $pdf->SetSubject($this->spreadsheet->getProperties()->getSubject());
+        $pdf->SetKeywords($this->spreadsheet->getProperties()->getKeywords());
+        $pdf->SetCreator($this->spreadsheet->getProperties()->getCreator());
+
+        $html = $this->generateHTMLAll();
+        foreach (\array_chunk(\explode(PHP_EOL, $html), 1000) as $lines) {
+            $pdf->WriteHTML(\implode(PHP_EOL, $lines));
+        }
+
+        //  Write to file
+        fwrite($fileHandle, $pdf->Output('', 'S'));
+
+        parent::restoreStateAfterSave();
+    }
+
+    /**
+     * Convert inches to mm.
+     *
+     * @param float $inches
+     *
+     * @return float
+     */
+    private function inchesToMm($inches)
+    {
+        return $inches * 25.4;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php
new file mode 100755
index 0000000..7530b1e
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php
@@ -0,0 +1,104 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Pdf;
+
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup;
+use PhpOffice\PhpSpreadsheet\Writer\Pdf;
+
+class Tcpdf extends Pdf
+{
+    /**
+     * Create a new PDF Writer instance.
+     *
+     * @param Spreadsheet $spreadsheet Spreadsheet object
+     */
+    public function __construct(Spreadsheet $spreadsheet)
+    {
+        parent::__construct($spreadsheet);
+        $this->setUseInlineCss(true);
+    }
+
+    /**
+     * Gets the implementation of external PDF library that should be used.
+     *
+     * @param string $orientation Page orientation
+     * @param string $unit Unit measure
+     * @param string $paperSize Paper size
+     *
+     * @return \TCPDF implementation
+     */
+    protected function createExternalWriterInstance($orientation, $unit, $paperSize)
+    {
+        return new \TCPDF($orientation, $unit, $paperSize);
+    }
+
+    /**
+     * Save Spreadsheet to file.
+     *
+     * @param string $pFilename Name of the file to save as
+     */
+    public function save($pFilename): void
+    {
+        $fileHandle = parent::prepareForSave($pFilename);
+
+        //  Default PDF paper size
+        $paperSize = 'LETTER'; //    Letter    (8.5 in. by 11 in.)
+
+        //  Check for paper size and page orientation
+        if ($this->getSheetIndex() === null) {
+            $orientation = ($this->spreadsheet->getSheet(0)->getPageSetup()->getOrientation()
+                == PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P';
+            $printPaperSize = $this->spreadsheet->getSheet(0)->getPageSetup()->getPaperSize();
+            $printMargins = $this->spreadsheet->getSheet(0)->getPageMargins();
+        } else {
+            $orientation = ($this->spreadsheet->getSheet($this->getSheetIndex())->getPageSetup()->getOrientation()
+                == PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P';
+            $printPaperSize = $this->spreadsheet->getSheet($this->getSheetIndex())->getPageSetup()->getPaperSize();
+            $printMargins = $this->spreadsheet->getSheet($this->getSheetIndex())->getPageMargins();
+        }
+
+        //  Override Page Orientation
+        if ($this->getOrientation() !== null) {
+            $orientation = ($this->getOrientation() == PageSetup::ORIENTATION_LANDSCAPE)
+                ? 'L'
+                : 'P';
+        }
+        //  Override Paper Size
+        if ($this->getPaperSize() !== null) {
+            $printPaperSize = $this->getPaperSize();
+        }
+
+        if (isset(self::$paperSizes[$printPaperSize])) {
+            $paperSize = self::$paperSizes[$printPaperSize];
+        }
+
+        //  Create PDF
+        $pdf = $this->createExternalWriterInstance($orientation, 'pt', $paperSize);
+        $pdf->setFontSubsetting(false);
+        //    Set margins, converting inches to points (using 72 dpi)
+        $pdf->SetMargins($printMargins->getLeft() * 72, $printMargins->getTop() * 72, $printMargins->getRight() * 72);
+        $pdf->SetAutoPageBreak(true, $printMargins->getBottom() * 72);
+
+        $pdf->setPrintHeader(false);
+        $pdf->setPrintFooter(false);
+
+        $pdf->AddPage();
+
+        //  Set the appropriate font
+        $pdf->SetFont($this->getFont());
+        $pdf->writeHTML($this->generateHTMLAll());
+
+        //  Document info
+        $pdf->SetTitle($this->spreadsheet->getProperties()->getTitle());
+        $pdf->SetAuthor($this->spreadsheet->getProperties()->getCreator());
+        $pdf->SetSubject($this->spreadsheet->getProperties()->getSubject());
+        $pdf->SetKeywords($this->spreadsheet->getProperties()->getKeywords());
+        $pdf->SetCreator($this->spreadsheet->getProperties()->getCreator());
+
+        //  Write to file
+        fwrite($fileHandle, $pdf->output($pFilename, 'S'));
+
+        parent::restoreStateAfterSave();
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls.php
new file mode 100755
index 0000000..cf87d5b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls.php
@@ -0,0 +1,946 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
+use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\RichText\RichText;
+use PhpOffice\PhpSpreadsheet\RichText\Run;
+use PhpOffice\PhpSpreadsheet\Shared\Drawing as SharedDrawing;
+use PhpOffice\PhpSpreadsheet\Shared\Escher;
+use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer;
+use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer;
+use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer\SpContainer;
+use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer;
+use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer;
+use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE;
+use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE\Blip;
+use PhpOffice\PhpSpreadsheet\Shared\OLE;
+use PhpOffice\PhpSpreadsheet\Shared\OLE\PPS\File;
+use PhpOffice\PhpSpreadsheet\Shared\OLE\PPS\Root;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Worksheet\BaseDrawing;
+use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
+use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
+use RuntimeException;
+
+class Xls extends BaseWriter
+{
+    /**
+     * PhpSpreadsheet object.
+     *
+     * @var Spreadsheet
+     */
+    private $spreadsheet;
+
+    /**
+     * Total number of shared strings in workbook.
+     *
+     * @var int
+     */
+    private $strTotal = 0;
+
+    /**
+     * Number of unique shared strings in workbook.
+     *
+     * @var int
+     */
+    private $strUnique = 0;
+
+    /**
+     * Array of unique shared strings in workbook.
+     *
+     * @var array
+     */
+    private $strTable = [];
+
+    /**
+     * Color cache. Mapping between RGB value and color index.
+     *
+     * @var array
+     */
+    private $colors;
+
+    /**
+     * Formula parser.
+     *
+     * @var \PhpOffice\PhpSpreadsheet\Writer\Xls\Parser
+     */
+    private $parser;
+
+    /**
+     * Identifier clusters for drawings. Used in MSODRAWINGGROUP record.
+     *
+     * @var array
+     */
+    private $IDCLs;
+
+    /**
+     * Basic OLE object summary information.
+     *
+     * @var array
+     */
+    private $summaryInformation;
+
+    /**
+     * Extended OLE object document summary information.
+     *
+     * @var array
+     */
+    private $documentSummaryInformation;
+
+    /**
+     * @var \PhpOffice\PhpSpreadsheet\Writer\Xls\Workbook
+     */
+    private $writerWorkbook;
+
+    /**
+     * @var \PhpOffice\PhpSpreadsheet\Writer\Xls\Worksheet[]
+     */
+    private $writerWorksheets;
+
+    /**
+     * Create a new Xls Writer.
+     *
+     * @param Spreadsheet $spreadsheet PhpSpreadsheet object
+     */
+    public function __construct(Spreadsheet $spreadsheet)
+    {
+        $this->spreadsheet = $spreadsheet;
+
+        $this->parser = new Xls\Parser();
+    }
+
+    /**
+     * Save Spreadsheet to file.
+     *
+     * @param resource|string $pFilename
+     */
+    public function save($pFilename): void
+    {
+        // garbage collect
+        $this->spreadsheet->garbageCollect();
+
+        $saveDebugLog = Calculation::getInstance($this->spreadsheet)->getDebugLog()->getWriteDebugLog();
+        Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog(false);
+        $saveDateReturnType = Functions::getReturnDateType();
+        Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
+
+        // initialize colors array
+        $this->colors = [];
+
+        // Initialise workbook writer
+        $this->writerWorkbook = new Xls\Workbook($this->spreadsheet, $this->strTotal, $this->strUnique, $this->strTable, $this->colors, $this->parser);
+
+        // Initialise worksheet writers
+        $countSheets = $this->spreadsheet->getSheetCount();
+        for ($i = 0; $i < $countSheets; ++$i) {
+            $this->writerWorksheets[$i] = new Xls\Worksheet($this->strTotal, $this->strUnique, $this->strTable, $this->colors, $this->parser, $this->preCalculateFormulas, $this->spreadsheet->getSheet($i));
+        }
+
+        // build Escher objects. Escher objects for workbooks needs to be build before Escher object for workbook.
+        $this->buildWorksheetEschers();
+        $this->buildWorkbookEscher();
+
+        // add 15 identical cell style Xfs
+        // for now, we use the first cellXf instead of cellStyleXf
+        $cellXfCollection = $this->spreadsheet->getCellXfCollection();
+        for ($i = 0; $i < 15; ++$i) {
+            $this->writerWorkbook->addXfWriter($cellXfCollection[0], true);
+        }
+
+        // add all the cell Xfs
+        foreach ($this->spreadsheet->getCellXfCollection() as $style) {
+            $this->writerWorkbook->addXfWriter($style, false);
+        }
+
+        // add fonts from rich text eleemnts
+        for ($i = 0; $i < $countSheets; ++$i) {
+            foreach ($this->writerWorksheets[$i]->phpSheet->getCoordinates() as $coordinate) {
+                $cell = $this->writerWorksheets[$i]->phpSheet->getCell($coordinate);
+                $cVal = $cell->getValue();
+                if ($cVal instanceof RichText) {
+                    $elements = $cVal->getRichTextElements();
+                    foreach ($elements as $element) {
+                        if ($element instanceof Run) {
+                            $font = $element->getFont();
+                            $this->writerWorksheets[$i]->fontHashIndex[$font->getHashCode()] = $this->writerWorkbook->addFont($font);
+                        }
+                    }
+                }
+            }
+        }
+
+        // initialize OLE file
+        $workbookStreamName = 'Workbook';
+        $OLE = new File(OLE::ascToUcs($workbookStreamName));
+
+        // Write the worksheet streams before the global workbook stream,
+        // because the byte sizes of these are needed in the global workbook stream
+        $worksheetSizes = [];
+        for ($i = 0; $i < $countSheets; ++$i) {
+            $this->writerWorksheets[$i]->close();
+            $worksheetSizes[] = $this->writerWorksheets[$i]->_datasize;
+        }
+
+        // add binary data for global workbook stream
+        $OLE->append($this->writerWorkbook->writeWorkbook($worksheetSizes));
+
+        // add binary data for sheet streams
+        for ($i = 0; $i < $countSheets; ++$i) {
+            $OLE->append($this->writerWorksheets[$i]->getData());
+        }
+
+        $this->documentSummaryInformation = $this->writeDocumentSummaryInformation();
+        // initialize OLE Document Summary Information
+        if (isset($this->documentSummaryInformation) && !empty($this->documentSummaryInformation)) {
+            $OLE_DocumentSummaryInformation = new File(OLE::ascToUcs(chr(5) . 'DocumentSummaryInformation'));
+            $OLE_DocumentSummaryInformation->append($this->documentSummaryInformation);
+        }
+
+        $this->summaryInformation = $this->writeSummaryInformation();
+        // initialize OLE Summary Information
+        if (isset($this->summaryInformation) && !empty($this->summaryInformation)) {
+            $OLE_SummaryInformation = new File(OLE::ascToUcs(chr(5) . 'SummaryInformation'));
+            $OLE_SummaryInformation->append($this->summaryInformation);
+        }
+
+        // define OLE Parts
+        $arrRootData = [$OLE];
+        // initialize OLE Properties file
+        if (isset($OLE_SummaryInformation)) {
+            $arrRootData[] = $OLE_SummaryInformation;
+        }
+        // initialize OLE Extended Properties file
+        if (isset($OLE_DocumentSummaryInformation)) {
+            $arrRootData[] = $OLE_DocumentSummaryInformation;
+        }
+
+        $root = new Root(time(), time(), $arrRootData);
+        // save the OLE file
+        $this->openFileHandle($pFilename);
+        $root->save($this->fileHandle);
+        $this->maybeCloseFileHandle();
+
+        Functions::setReturnDateType($saveDateReturnType);
+        Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog($saveDebugLog);
+    }
+
+    /**
+     * Build the Worksheet Escher objects.
+     */
+    private function buildWorksheetEschers(): void
+    {
+        // 1-based index to BstoreContainer
+        $blipIndex = 0;
+        $lastReducedSpId = 0;
+        $lastSpId = 0;
+
+        foreach ($this->spreadsheet->getAllsheets() as $sheet) {
+            // sheet index
+            $sheetIndex = $sheet->getParent()->getIndex($sheet);
+
+            $escher = null;
+
+            // check if there are any shapes for this sheet
+            $filterRange = $sheet->getAutoFilter()->getRange();
+            if (count($sheet->getDrawingCollection()) == 0 && empty($filterRange)) {
+                continue;
+            }
+
+            // create intermediate Escher object
+            $escher = new Escher();
+
+            // dgContainer
+            $dgContainer = new DgContainer();
+
+            // set the drawing index (we use sheet index + 1)
+            $dgId = $sheet->getParent()->getIndex($sheet) + 1;
+            $dgContainer->setDgId($dgId);
+            $escher->setDgContainer($dgContainer);
+
+            // spgrContainer
+            $spgrContainer = new SpgrContainer();
+            $dgContainer->setSpgrContainer($spgrContainer);
+
+            // add one shape which is the group shape
+            $spContainer = new SpContainer();
+            $spContainer->setSpgr(true);
+            $spContainer->setSpType(0);
+            $spContainer->setSpId(($sheet->getParent()->getIndex($sheet) + 1) << 10);
+            $spgrContainer->addChild($spContainer);
+
+            // add the shapes
+
+            $countShapes[$sheetIndex] = 0; // count number of shapes (minus group shape), in sheet
+
+            foreach ($sheet->getDrawingCollection() as $drawing) {
+                ++$blipIndex;
+
+                ++$countShapes[$sheetIndex];
+
+                // add the shape
+                $spContainer = new SpContainer();
+
+                // set the shape type
+                $spContainer->setSpType(0x004B);
+                // set the shape flag
+                $spContainer->setSpFlag(0x02);
+
+                // set the shape index (we combine 1-based sheet index and $countShapes to create unique shape index)
+                $reducedSpId = $countShapes[$sheetIndex];
+                $spId = $reducedSpId | ($sheet->getParent()->getIndex($sheet) + 1) << 10;
+                $spContainer->setSpId($spId);
+
+                // keep track of last reducedSpId
+                $lastReducedSpId = $reducedSpId;
+
+                // keep track of last spId
+                $lastSpId = $spId;
+
+                // set the BLIP index
+                $spContainer->setOPT(0x4104, $blipIndex);
+
+                // set coordinates and offsets, client anchor
+                $coordinates = $drawing->getCoordinates();
+                $offsetX = $drawing->getOffsetX();
+                $offsetY = $drawing->getOffsetY();
+                $width = $drawing->getWidth();
+                $height = $drawing->getHeight();
+
+                $twoAnchor = \PhpOffice\PhpSpreadsheet\Shared\Xls::oneAnchor2twoAnchor($sheet, $coordinates, $offsetX, $offsetY, $width, $height);
+
+                $spContainer->setStartCoordinates($twoAnchor['startCoordinates']);
+                $spContainer->setStartOffsetX($twoAnchor['startOffsetX']);
+                $spContainer->setStartOffsetY($twoAnchor['startOffsetY']);
+                $spContainer->setEndCoordinates($twoAnchor['endCoordinates']);
+                $spContainer->setEndOffsetX($twoAnchor['endOffsetX']);
+                $spContainer->setEndOffsetY($twoAnchor['endOffsetY']);
+
+                $spgrContainer->addChild($spContainer);
+            }
+
+            // AutoFilters
+            if (!empty($filterRange)) {
+                $rangeBounds = Coordinate::rangeBoundaries($filterRange);
+                $iNumColStart = $rangeBounds[0][0];
+                $iNumColEnd = $rangeBounds[1][0];
+
+                $iInc = $iNumColStart;
+                while ($iInc <= $iNumColEnd) {
+                    ++$countShapes[$sheetIndex];
+
+                    // create an Drawing Object for the dropdown
+                    $oDrawing = new BaseDrawing();
+                    // get the coordinates of drawing
+                    $cDrawing = Coordinate::stringFromColumnIndex($iInc) . $rangeBounds[0][1];
+                    $oDrawing->setCoordinates($cDrawing);
+                    $oDrawing->setWorksheet($sheet);
+
+                    // add the shape
+                    $spContainer = new SpContainer();
+                    // set the shape type
+                    $spContainer->setSpType(0x00C9);
+                    // set the shape flag
+                    $spContainer->setSpFlag(0x01);
+
+                    // set the shape index (we combine 1-based sheet index and $countShapes to create unique shape index)
+                    $reducedSpId = $countShapes[$sheetIndex];
+                    $spId = $reducedSpId | ($sheet->getParent()->getIndex($sheet) + 1) << 10;
+                    $spContainer->setSpId($spId);
+
+                    // keep track of last reducedSpId
+                    $lastReducedSpId = $reducedSpId;
+
+                    // keep track of last spId
+                    $lastSpId = $spId;
+
+                    $spContainer->setOPT(0x007F, 0x01040104); // Protection -> fLockAgainstGrouping
+                    $spContainer->setOPT(0x00BF, 0x00080008); // Text -> fFitTextToShape
+                    $spContainer->setOPT(0x01BF, 0x00010000); // Fill Style -> fNoFillHitTest
+                    $spContainer->setOPT(0x01FF, 0x00080000); // Line Style -> fNoLineDrawDash
+                    $spContainer->setOPT(0x03BF, 0x000A0000); // Group Shape -> fPrint
+
+                    // set coordinates and offsets, client anchor
+                    $endCoordinates = Coordinate::stringFromColumnIndex($iInc);
+                    $endCoordinates .= $rangeBounds[0][1] + 1;
+
+                    $spContainer->setStartCoordinates($cDrawing);
+                    $spContainer->setStartOffsetX(0);
+                    $spContainer->setStartOffsetY(0);
+                    $spContainer->setEndCoordinates($endCoordinates);
+                    $spContainer->setEndOffsetX(0);
+                    $spContainer->setEndOffsetY(0);
+
+                    $spgrContainer->addChild($spContainer);
+                    ++$iInc;
+                }
+            }
+
+            // identifier clusters, used for workbook Escher object
+            $this->IDCLs[$dgId] = $lastReducedSpId;
+
+            // set last shape index
+            $dgContainer->setLastSpId($lastSpId);
+
+            // set the Escher object
+            $this->writerWorksheets[$sheetIndex]->setEscher($escher);
+        }
+    }
+
+    /**
+     * Build the Escher object corresponding to the MSODRAWINGGROUP record.
+     */
+    private function buildWorkbookEscher(): void
+    {
+        $escher = null;
+
+        // any drawings in this workbook?
+        $found = false;
+        foreach ($this->spreadsheet->getAllSheets() as $sheet) {
+            if (count($sheet->getDrawingCollection()) > 0) {
+                $found = true;
+
+                break;
+            }
+        }
+
+        // nothing to do if there are no drawings
+        if (!$found) {
+            return;
+        }
+
+        // if we reach here, then there are drawings in the workbook
+        $escher = new Escher();
+
+        // dggContainer
+        $dggContainer = new DggContainer();
+        $escher->setDggContainer($dggContainer);
+
+        // set IDCLs (identifier clusters)
+        $dggContainer->setIDCLs($this->IDCLs);
+
+        // this loop is for determining maximum shape identifier of all drawing
+        $spIdMax = 0;
+        $totalCountShapes = 0;
+        $countDrawings = 0;
+
+        foreach ($this->spreadsheet->getAllsheets() as $sheet) {
+            $sheetCountShapes = 0; // count number of shapes (minus group shape), in sheet
+
+            if (count($sheet->getDrawingCollection()) > 0) {
+                ++$countDrawings;
+
+                foreach ($sheet->getDrawingCollection() as $drawing) {
+                    ++$sheetCountShapes;
+                    ++$totalCountShapes;
+
+                    $spId = $sheetCountShapes | ($this->spreadsheet->getIndex($sheet) + 1) << 10;
+                    $spIdMax = max($spId, $spIdMax);
+                }
+            }
+        }
+
+        $dggContainer->setSpIdMax($spIdMax + 1);
+        $dggContainer->setCDgSaved($countDrawings);
+        $dggContainer->setCSpSaved($totalCountShapes + $countDrawings); // total number of shapes incl. one group shapes per drawing
+
+        // bstoreContainer
+        $bstoreContainer = new BstoreContainer();
+        $dggContainer->setBstoreContainer($bstoreContainer);
+
+        // the BSE's (all the images)
+        foreach ($this->spreadsheet->getAllsheets() as $sheet) {
+            foreach ($sheet->getDrawingCollection() as $drawing) {
+                if (!extension_loaded('gd')) {
+                    throw new RuntimeException('Saving images in xls requires gd extension');
+                }
+                if ($drawing instanceof Drawing) {
+                    $filename = $drawing->getPath();
+
+                    [$imagesx, $imagesy, $imageFormat] = getimagesize($filename);
+
+                    switch ($imageFormat) {
+                        case 1: // GIF, not supported by BIFF8, we convert to PNG
+                            $blipType = BSE::BLIPTYPE_PNG;
+                            ob_start();
+                            imagepng(imagecreatefromgif($filename));
+                            $blipData = ob_get_contents();
+                            ob_end_clean();
+
+                            break;
+                        case 2: // JPEG
+                            $blipType = BSE::BLIPTYPE_JPEG;
+                            $blipData = file_get_contents($filename);
+
+                            break;
+                        case 3: // PNG
+                            $blipType = BSE::BLIPTYPE_PNG;
+                            $blipData = file_get_contents($filename);
+
+                            break;
+                        case 6: // Windows DIB (BMP), we convert to PNG
+                            $blipType = BSE::BLIPTYPE_PNG;
+                            ob_start();
+                            imagepng(SharedDrawing::imagecreatefrombmp($filename));
+                            $blipData = ob_get_contents();
+                            ob_end_clean();
+
+                            break;
+                        default:
+                            continue 2;
+                    }
+
+                    $blip = new Blip();
+                    $blip->setData($blipData);
+
+                    $BSE = new BSE();
+                    $BSE->setBlipType($blipType);
+                    $BSE->setBlip($blip);
+
+                    $bstoreContainer->addBSE($BSE);
+                } elseif ($drawing instanceof MemoryDrawing) {
+                    switch ($drawing->getRenderingFunction()) {
+                        case MemoryDrawing::RENDERING_JPEG:
+                            $blipType = BSE::BLIPTYPE_JPEG;
+                            $renderingFunction = 'imagejpeg';
+
+                            break;
+                        case MemoryDrawing::RENDERING_GIF:
+                        case MemoryDrawing::RENDERING_PNG:
+                        case MemoryDrawing::RENDERING_DEFAULT:
+                            $blipType = BSE::BLIPTYPE_PNG;
+                            $renderingFunction = 'imagepng';
+
+                            break;
+                    }
+
+                    ob_start();
+                    call_user_func($renderingFunction, $drawing->getImageResource());
+                    $blipData = ob_get_contents();
+                    ob_end_clean();
+
+                    $blip = new Blip();
+                    $blip->setData($blipData);
+
+                    $BSE = new BSE();
+                    $BSE->setBlipType($blipType);
+                    $BSE->setBlip($blip);
+
+                    $bstoreContainer->addBSE($BSE);
+                }
+            }
+        }
+
+        // Set the Escher object
+        $this->writerWorkbook->setEscher($escher);
+    }
+
+    /**
+     * Build the OLE Part for DocumentSummary Information.
+     *
+     * @return string
+     */
+    private function writeDocumentSummaryInformation()
+    {
+        // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
+        $data = pack('v', 0xFFFE);
+        // offset: 2; size: 2;
+        $data .= pack('v', 0x0000);
+        // offset: 4; size: 2; OS version
+        $data .= pack('v', 0x0106);
+        // offset: 6; size: 2; OS indicator
+        $data .= pack('v', 0x0002);
+        // offset: 8; size: 16
+        $data .= pack('VVVV', 0x00, 0x00, 0x00, 0x00);
+        // offset: 24; size: 4; section count
+        $data .= pack('V', 0x0001);
+
+        // offset: 28; size: 16; first section's class id: 02 d5 cd d5 9c 2e 1b 10 93 97 08 00 2b 2c f9 ae
+        $data .= pack('vvvvvvvv', 0xD502, 0xD5CD, 0x2E9C, 0x101B, 0x9793, 0x0008, 0x2C2B, 0xAEF9);
+        // offset: 44; size: 4; offset of the start
+        $data .= pack('V', 0x30);
+
+        // SECTION
+        $dataSection = [];
+        $dataSection_NumProps = 0;
+        $dataSection_Summary = '';
+        $dataSection_Content = '';
+
+        // GKPIDDSI_CODEPAGE: CodePage
+        $dataSection[] = [
+            'summary' => ['pack' => 'V', 'data' => 0x01],
+            'offset' => ['pack' => 'V'],
+            'type' => ['pack' => 'V', 'data' => 0x02], // 2 byte signed integer
+            'data' => ['data' => 1252],
+        ];
+        ++$dataSection_NumProps;
+
+        // GKPIDDSI_CATEGORY : Category
+        if ($this->spreadsheet->getProperties()->getCategory()) {
+            $dataProp = $this->spreadsheet->getProperties()->getCategory();
+            $dataSection[] = [
+                'summary' => ['pack' => 'V', 'data' => 0x02],
+                'offset' => ['pack' => 'V'],
+                'type' => ['pack' => 'V', 'data' => 0x1E],
+                'data' => ['data' => $dataProp, 'length' => strlen($dataProp)],
+            ];
+            ++$dataSection_NumProps;
+        }
+        // GKPIDDSI_VERSION :Version of the application that wrote the property storage
+        $dataSection[] = [
+            'summary' => ['pack' => 'V', 'data' => 0x17],
+            'offset' => ['pack' => 'V'],
+            'type' => ['pack' => 'V', 'data' => 0x03],
+            'data' => ['pack' => 'V', 'data' => 0x000C0000],
+        ];
+        ++$dataSection_NumProps;
+        // GKPIDDSI_SCALE : FALSE
+        $dataSection[] = [
+            'summary' => ['pack' => 'V', 'data' => 0x0B],
+            'offset' => ['pack' => 'V'],
+            'type' => ['pack' => 'V', 'data' => 0x0B],
+            'data' => ['data' => false],
+        ];
+        ++$dataSection_NumProps;
+        // GKPIDDSI_LINKSDIRTY : True if any of the values for the linked properties have changed outside of the application
+        $dataSection[] = [
+            'summary' => ['pack' => 'V', 'data' => 0x10],
+            'offset' => ['pack' => 'V'],
+            'type' => ['pack' => 'V', 'data' => 0x0B],
+            'data' => ['data' => false],
+        ];
+        ++$dataSection_NumProps;
+        // GKPIDDSI_SHAREDOC : FALSE
+        $dataSection[] = [
+            'summary' => ['pack' => 'V', 'data' => 0x13],
+            'offset' => ['pack' => 'V'],
+            'type' => ['pack' => 'V', 'data' => 0x0B],
+            'data' => ['data' => false],
+        ];
+        ++$dataSection_NumProps;
+        // GKPIDDSI_HYPERLINKSCHANGED : True if any of the values for the _PID_LINKS (hyperlink text) have changed outside of the application
+        $dataSection[] = [
+            'summary' => ['pack' => 'V', 'data' => 0x16],
+            'offset' => ['pack' => 'V'],
+            'type' => ['pack' => 'V', 'data' => 0x0B],
+            'data' => ['data' => false],
+        ];
+        ++$dataSection_NumProps;
+
+        // GKPIDDSI_DOCSPARTS
+        // MS-OSHARED p75 (2.3.3.2.2.1)
+        // Structure is VtVecUnalignedLpstrValue (2.3.3.1.9)
+        // cElements
+        $dataProp = pack('v', 0x0001);
+        $dataProp .= pack('v', 0x0000);
+        // array of UnalignedLpstr
+        // cch
+        $dataProp .= pack('v', 0x000A);
+        $dataProp .= pack('v', 0x0000);
+        // value
+        $dataProp .= 'Worksheet' . chr(0);
+
+        $dataSection[] = [
+            'summary' => ['pack' => 'V', 'data' => 0x0D],
+            'offset' => ['pack' => 'V'],
+            'type' => ['pack' => 'V', 'data' => 0x101E],
+            'data' => ['data' => $dataProp, 'length' => strlen($dataProp)],
+        ];
+        ++$dataSection_NumProps;
+
+        // GKPIDDSI_HEADINGPAIR
+        // VtVecHeadingPairValue
+        // cElements
+        $dataProp = pack('v', 0x0002);
+        $dataProp .= pack('v', 0x0000);
+        // Array of vtHeadingPair
+        // vtUnalignedString - headingString
+        // stringType
+        $dataProp .= pack('v', 0x001E);
+        // padding
+        $dataProp .= pack('v', 0x0000);
+        // UnalignedLpstr
+        // cch
+        $dataProp .= pack('v', 0x0013);
+        $dataProp .= pack('v', 0x0000);
+        // value
+        $dataProp .= 'Feuilles de calcul';
+        // vtUnalignedString - headingParts
+        // wType : 0x0003 = 32 bit signed integer
+        $dataProp .= pack('v', 0x0300);
+        // padding
+        $dataProp .= pack('v', 0x0000);
+        // value
+        $dataProp .= pack('v', 0x0100);
+        $dataProp .= pack('v', 0x0000);
+        $dataProp .= pack('v', 0x0000);
+        $dataProp .= pack('v', 0x0000);
+
+        $dataSection[] = [
+            'summary' => ['pack' => 'V', 'data' => 0x0C],
+            'offset' => ['pack' => 'V'],
+            'type' => ['pack' => 'V', 'data' => 0x100C],
+            'data' => ['data' => $dataProp, 'length' => strlen($dataProp)],
+        ];
+        ++$dataSection_NumProps;
+
+        //         4     Section Length
+        //        4     Property count
+        //        8 * $dataSection_NumProps (8 =  ID (4) + OffSet(4))
+        $dataSection_Content_Offset = 8 + $dataSection_NumProps * 8;
+        foreach ($dataSection as $dataProp) {
+            // Summary
+            $dataSection_Summary .= pack($dataProp['summary']['pack'], $dataProp['summary']['data']);
+            // Offset
+            $dataSection_Summary .= pack($dataProp['offset']['pack'], $dataSection_Content_Offset);
+            // DataType
+            $dataSection_Content .= pack($dataProp['type']['pack'], $dataProp['type']['data']);
+            // Data
+            if ($dataProp['type']['data'] == 0x02) { // 2 byte signed integer
+                $dataSection_Content .= pack('V', $dataProp['data']['data']);
+
+                $dataSection_Content_Offset += 4 + 4;
+            } elseif ($dataProp['type']['data'] == 0x03) { // 4 byte signed integer
+                $dataSection_Content .= pack('V', $dataProp['data']['data']);
+
+                $dataSection_Content_Offset += 4 + 4;
+            } elseif ($dataProp['type']['data'] == 0x0B) { // Boolean
+                if ($dataProp['data']['data'] == false) {
+                    $dataSection_Content .= pack('V', 0x0000);
+                } else {
+                    $dataSection_Content .= pack('V', 0x0001);
+                }
+                $dataSection_Content_Offset += 4 + 4;
+            } elseif ($dataProp['type']['data'] == 0x1E) { // null-terminated string prepended by dword string length
+                // Null-terminated string
+                $dataProp['data']['data'] .= chr(0);
+                ++$dataProp['data']['length'];
+                // Complete the string with null string for being a %4
+                $dataProp['data']['length'] = $dataProp['data']['length'] + ((4 - $dataProp['data']['length'] % 4) == 4 ? 0 : (4 - $dataProp['data']['length'] % 4));
+                $dataProp['data']['data'] = str_pad($dataProp['data']['data'], $dataProp['data']['length'], chr(0), STR_PAD_RIGHT);
+
+                $dataSection_Content .= pack('V', $dataProp['data']['length']);
+                $dataSection_Content .= $dataProp['data']['data'];
+
+                $dataSection_Content_Offset += 4 + 4 + strlen($dataProp['data']['data']);
+            } elseif ($dataProp['type']['data'] == 0x40) { // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
+                $dataSection_Content .= $dataProp['data']['data'];
+
+                $dataSection_Content_Offset += 4 + 8;
+            } else {
+                // Data Type Not Used at the moment
+                $dataSection_Content .= $dataProp['data']['data'];
+
+                $dataSection_Content_Offset += 4 + $dataProp['data']['length'];
+            }
+        }
+        // Now $dataSection_Content_Offset contains the size of the content
+
+        // section header
+        // offset: $secOffset; size: 4; section length
+        //         + x  Size of the content (summary + content)
+        $data .= pack('V', $dataSection_Content_Offset);
+        // offset: $secOffset+4; size: 4; property count
+        $data .= pack('V', $dataSection_NumProps);
+        // Section Summary
+        $data .= $dataSection_Summary;
+        // Section Content
+        $data .= $dataSection_Content;
+
+        return $data;
+    }
+
+    /**
+     * Build the OLE Part for Summary Information.
+     *
+     * @return string
+     */
+    private function writeSummaryInformation()
+    {
+        // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
+        $data = pack('v', 0xFFFE);
+        // offset: 2; size: 2;
+        $data .= pack('v', 0x0000);
+        // offset: 4; size: 2; OS version
+        $data .= pack('v', 0x0106);
+        // offset: 6; size: 2; OS indicator
+        $data .= pack('v', 0x0002);
+        // offset: 8; size: 16
+        $data .= pack('VVVV', 0x00, 0x00, 0x00, 0x00);
+        // offset: 24; size: 4; section count
+        $data .= pack('V', 0x0001);
+
+        // offset: 28; size: 16; first section's class id: e0 85 9f f2 f9 4f 68 10 ab 91 08 00 2b 27 b3 d9
+        $data .= pack('vvvvvvvv', 0x85E0, 0xF29F, 0x4FF9, 0x1068, 0x91AB, 0x0008, 0x272B, 0xD9B3);
+        // offset: 44; size: 4; offset of the start
+        $data .= pack('V', 0x30);
+
+        // SECTION
+        $dataSection = [];
+        $dataSection_NumProps = 0;
+        $dataSection_Summary = '';
+        $dataSection_Content = '';
+
+        // CodePage : CP-1252
+        $dataSection[] = [
+            'summary' => ['pack' => 'V', 'data' => 0x01],
+            'offset' => ['pack' => 'V'],
+            'type' => ['pack' => 'V', 'data' => 0x02], // 2 byte signed integer
+            'data' => ['data' => 1252],
+        ];
+        ++$dataSection_NumProps;
+
+        //    Title
+        if ($this->spreadsheet->getProperties()->getTitle()) {
+            $dataProp = $this->spreadsheet->getProperties()->getTitle();
+            $dataSection[] = [
+                'summary' => ['pack' => 'V', 'data' => 0x02],
+                'offset' => ['pack' => 'V'],
+                'type' => ['pack' => 'V', 'data' => 0x1E], // null-terminated string prepended by dword string length
+                'data' => ['data' => $dataProp, 'length' => strlen($dataProp)],
+            ];
+            ++$dataSection_NumProps;
+        }
+        //    Subject
+        if ($this->spreadsheet->getProperties()->getSubject()) {
+            $dataProp = $this->spreadsheet->getProperties()->getSubject();
+            $dataSection[] = [
+                'summary' => ['pack' => 'V', 'data' => 0x03],
+                'offset' => ['pack' => 'V'],
+                'type' => ['pack' => 'V', 'data' => 0x1E], // null-terminated string prepended by dword string length
+                'data' => ['data' => $dataProp, 'length' => strlen($dataProp)],
+            ];
+            ++$dataSection_NumProps;
+        }
+        //    Author (Creator)
+        if ($this->spreadsheet->getProperties()->getCreator()) {
+            $dataProp = $this->spreadsheet->getProperties()->getCreator();
+            $dataSection[] = [
+                'summary' => ['pack' => 'V', 'data' => 0x04],
+                'offset' => ['pack' => 'V'],
+                'type' => ['pack' => 'V', 'data' => 0x1E], // null-terminated string prepended by dword string length
+                'data' => ['data' => $dataProp, 'length' => strlen($dataProp)],
+            ];
+            ++$dataSection_NumProps;
+        }
+        //    Keywords
+        if ($this->spreadsheet->getProperties()->getKeywords()) {
+            $dataProp = $this->spreadsheet->getProperties()->getKeywords();
+            $dataSection[] = [
+                'summary' => ['pack' => 'V', 'data' => 0x05],
+                'offset' => ['pack' => 'V'],
+                'type' => ['pack' => 'V', 'data' => 0x1E], // null-terminated string prepended by dword string length
+                'data' => ['data' => $dataProp, 'length' => strlen($dataProp)],
+            ];
+            ++$dataSection_NumProps;
+        }
+        //    Comments (Description)
+        if ($this->spreadsheet->getProperties()->getDescription()) {
+            $dataProp = $this->spreadsheet->getProperties()->getDescription();
+            $dataSection[] = [
+                'summary' => ['pack' => 'V', 'data' => 0x06],
+                'offset' => ['pack' => 'V'],
+                'type' => ['pack' => 'V', 'data' => 0x1E], // null-terminated string prepended by dword string length
+                'data' => ['data' => $dataProp, 'length' => strlen($dataProp)],
+            ];
+            ++$dataSection_NumProps;
+        }
+        //    Last Saved By (LastModifiedBy)
+        if ($this->spreadsheet->getProperties()->getLastModifiedBy()) {
+            $dataProp = $this->spreadsheet->getProperties()->getLastModifiedBy();
+            $dataSection[] = [
+                'summary' => ['pack' => 'V', 'data' => 0x08],
+                'offset' => ['pack' => 'V'],
+                'type' => ['pack' => 'V', 'data' => 0x1E], // null-terminated string prepended by dword string length
+                'data' => ['data' => $dataProp, 'length' => strlen($dataProp)],
+            ];
+            ++$dataSection_NumProps;
+        }
+        //    Created Date/Time
+        if ($this->spreadsheet->getProperties()->getCreated()) {
+            $dataProp = $this->spreadsheet->getProperties()->getCreated();
+            $dataSection[] = [
+                'summary' => ['pack' => 'V', 'data' => 0x0C],
+                'offset' => ['pack' => 'V'],
+                'type' => ['pack' => 'V', 'data' => 0x40], // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
+                'data' => ['data' => OLE::localDateToOLE($dataProp)],
+            ];
+            ++$dataSection_NumProps;
+        }
+        //    Modified Date/Time
+        if ($this->spreadsheet->getProperties()->getModified()) {
+            $dataProp = $this->spreadsheet->getProperties()->getModified();
+            $dataSection[] = [
+                'summary' => ['pack' => 'V', 'data' => 0x0D],
+                'offset' => ['pack' => 'V'],
+                'type' => ['pack' => 'V', 'data' => 0x40], // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
+                'data' => ['data' => OLE::localDateToOLE($dataProp)],
+            ];
+            ++$dataSection_NumProps;
+        }
+        //    Security
+        $dataSection[] = [
+            'summary' => ['pack' => 'V', 'data' => 0x13],
+            'offset' => ['pack' => 'V'],
+            'type' => ['pack' => 'V', 'data' => 0x03], // 4 byte signed integer
+            'data' => ['data' => 0x00],
+        ];
+        ++$dataSection_NumProps;
+
+        //         4     Section Length
+        //        4     Property count
+        //        8 * $dataSection_NumProps (8 =  ID (4) + OffSet(4))
+        $dataSection_Content_Offset = 8 + $dataSection_NumProps * 8;
+        foreach ($dataSection as $dataProp) {
+            // Summary
+            $dataSection_Summary .= pack($dataProp['summary']['pack'], $dataProp['summary']['data']);
+            // Offset
+            $dataSection_Summary .= pack($dataProp['offset']['pack'], $dataSection_Content_Offset);
+            // DataType
+            $dataSection_Content .= pack($dataProp['type']['pack'], $dataProp['type']['data']);
+            // Data
+            if ($dataProp['type']['data'] == 0x02) { // 2 byte signed integer
+                $dataSection_Content .= pack('V', $dataProp['data']['data']);
+
+                $dataSection_Content_Offset += 4 + 4;
+            } elseif ($dataProp['type']['data'] == 0x03) { // 4 byte signed integer
+                $dataSection_Content .= pack('V', $dataProp['data']['data']);
+
+                $dataSection_Content_Offset += 4 + 4;
+            } elseif ($dataProp['type']['data'] == 0x1E) { // null-terminated string prepended by dword string length
+                // Null-terminated string
+                $dataProp['data']['data'] .= chr(0);
+                ++$dataProp['data']['length'];
+                // Complete the string with null string for being a %4
+                $dataProp['data']['length'] = $dataProp['data']['length'] + ((4 - $dataProp['data']['length'] % 4) == 4 ? 0 : (4 - $dataProp['data']['length'] % 4));
+                $dataProp['data']['data'] = str_pad($dataProp['data']['data'], $dataProp['data']['length'], chr(0), STR_PAD_RIGHT);
+
+                $dataSection_Content .= pack('V', $dataProp['data']['length']);
+                $dataSection_Content .= $dataProp['data']['data'];
+
+                $dataSection_Content_Offset += 4 + 4 + strlen($dataProp['data']['data']);
+            } elseif ($dataProp['type']['data'] == 0x40) { // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
+                $dataSection_Content .= $dataProp['data']['data'];
+
+                $dataSection_Content_Offset += 4 + 8;
+            }
+            // Data Type Not Used at the moment
+        }
+        // Now $dataSection_Content_Offset contains the size of the content
+
+        // section header
+        // offset: $secOffset; size: 4; section length
+        //         + x  Size of the content (summary + content)
+        $data .= pack('V', $dataSection_Content_Offset);
+        // offset: $secOffset+4; size: 4; property count
+        $data .= pack('V', $dataSection_NumProps);
+        // Section Summary
+        $data .= $dataSection_Summary;
+        // Section Content
+        $data .= $dataSection_Content;
+
+        return $data;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/BIFFwriter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/BIFFwriter.php
new file mode 100755
index 0000000..84e27d0
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/BIFFwriter.php
@@ -0,0 +1,224 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Xls;
+
+use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
+
+// Original file header of PEAR::Spreadsheet_Excel_Writer_BIFFwriter (used as the base for this class):
+// -----------------------------------------------------------------------------------------
+// *  Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
+// *
+// *  The majority of this is _NOT_ my code.  I simply ported it from the
+// *  PERL Spreadsheet::WriteExcel module.
+// *
+// *  The author of the Spreadsheet::WriteExcel module is John McNamara
+// *  <jmcnamara@cpan.org>
+// *
+// *  I _DO_ maintain this code, and John McNamara has nothing to do with the
+// *  porting of this code to PHP.  Any questions directly related to this
+// *  class library should be directed to me.
+// *
+// *  License Information:
+// *
+// *    Spreadsheet_Excel_Writer:  A library for generating Excel Spreadsheets
+// *    Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com
+// *
+// *    This library is free software; you can redistribute it and/or
+// *    modify it under the terms of the GNU Lesser General Public
+// *    License as published by the Free Software Foundation; either
+// *    version 2.1 of the License, or (at your option) any later version.
+// *
+// *    This library is distributed in the hope that it will be useful,
+// *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+// *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// *    Lesser General Public License for more details.
+// *
+// *    You should have received a copy of the GNU Lesser General Public
+// *    License along with this library; if not, write to the Free Software
+// *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+// */
+class BIFFwriter
+{
+    /**
+     * The byte order of this architecture. 0 => little endian, 1 => big endian.
+     *
+     * @var int
+     */
+    private static $byteOrder;
+
+    /**
+     * The string containing the data of the BIFF stream.
+     *
+     * @var string
+     */
+    public $_data;
+
+    /**
+     * The size of the data in bytes. Should be the same as strlen($this->_data).
+     *
+     * @var int
+     */
+    public $_datasize;
+
+    /**
+     * The maximum length for a BIFF record (excluding record header and length field). See addContinue().
+     *
+     * @var int
+     *
+     * @see addContinue()
+     */
+    private $limit = 8224;
+
+    /**
+     * Constructor.
+     */
+    public function __construct()
+    {
+        $this->_data = '';
+        $this->_datasize = 0;
+    }
+
+    /**
+     * Determine the byte order and store it as class data to avoid
+     * recalculating it for each call to new().
+     *
+     * @return int
+     */
+    public static function getByteOrder()
+    {
+        if (!isset(self::$byteOrder)) {
+            // Check if "pack" gives the required IEEE 64bit float
+            $teststr = pack('d', 1.2345);
+            $number = pack('C8', 0x8D, 0x97, 0x6E, 0x12, 0x83, 0xC0, 0xF3, 0x3F);
+            if ($number == $teststr) {
+                $byte_order = 0; // Little Endian
+            } elseif ($number == strrev($teststr)) {
+                $byte_order = 1; // Big Endian
+            } else {
+                // Give up. I'll fix this in a later version.
+                throw new WriterException('Required floating point format not supported on this platform.');
+            }
+            self::$byteOrder = $byte_order;
+        }
+
+        return self::$byteOrder;
+    }
+
+    /**
+     * General storage function.
+     *
+     * @param string $data binary data to append
+     */
+    protected function append($data): void
+    {
+        if (strlen($data) - 4 > $this->limit) {
+            $data = $this->addContinue($data);
+        }
+        $this->_data .= $data;
+        $this->_datasize += strlen($data);
+    }
+
+    /**
+     * General storage function like append, but returns string instead of modifying $this->_data.
+     *
+     * @param string $data binary data to write
+     *
+     * @return string
+     */
+    public function writeData($data)
+    {
+        if (strlen($data) - 4 > $this->limit) {
+            $data = $this->addContinue($data);
+        }
+        $this->_datasize += strlen($data);
+
+        return $data;
+    }
+
+    /**
+     * Writes Excel BOF record to indicate the beginning of a stream or
+     * sub-stream in the BIFF file.
+     *
+     * @param int $type type of BIFF file to write: 0x0005 Workbook,
+     *                       0x0010 Worksheet
+     */
+    protected function storeBof($type): void
+    {
+        $record = 0x0809; // Record identifier    (BIFF5-BIFF8)
+        $length = 0x0010;
+
+        // by inspection of real files, MS Office Excel 2007 writes the following
+        $unknown = pack('VV', 0x000100D1, 0x00000406);
+
+        $build = 0x0DBB; //    Excel 97
+        $year = 0x07CC; //    Excel 97
+
+        $version = 0x0600; //    BIFF8
+
+        $header = pack('vv', $record, $length);
+        $data = pack('vvvv', $version, $type, $build, $year);
+        $this->append($header . $data . $unknown);
+    }
+
+    /**
+     * Writes Excel EOF record to indicate the end of a BIFF stream.
+     */
+    protected function storeEof(): void
+    {
+        $record = 0x000A; // Record identifier
+        $length = 0x0000; // Number of bytes to follow
+
+        $header = pack('vv', $record, $length);
+        $this->append($header);
+    }
+
+    /**
+     * Writes Excel EOF record to indicate the end of a BIFF stream.
+     */
+    public function writeEof()
+    {
+        $record = 0x000A; // Record identifier
+        $length = 0x0000; // Number of bytes to follow
+        $header = pack('vv', $record, $length);
+
+        return $this->writeData($header);
+    }
+
+    /**
+     * Excel limits the size of BIFF records. In Excel 5 the limit is 2084 bytes. In
+     * Excel 97 the limit is 8228 bytes. Records that are longer than these limits
+     * must be split up into CONTINUE blocks.
+     *
+     * This function takes a long BIFF record and inserts CONTINUE records as
+     * necessary.
+     *
+     * @param string $data The original binary data to be written
+     *
+     * @return string A very convenient string of continue blocks
+     */
+    private function addContinue($data)
+    {
+        $limit = $this->limit;
+        $record = 0x003C; // Record identifier
+
+        // The first 2080/8224 bytes remain intact. However, we have to change
+        // the length field of the record.
+        $tmp = substr($data, 0, 2) . pack('v', $limit) . substr($data, 4, $limit);
+
+        $header = pack('vv', $record, $limit); // Headers for continue records
+
+        // Retrieve chunks of 2080/8224 bytes +4 for the header.
+        $data_length = strlen($data);
+        for ($i = $limit + 4; $i < ($data_length - $limit); $i += $limit) {
+            $tmp .= $header;
+            $tmp .= substr($data, $i, $limit);
+        }
+
+        // Retrieve the last chunk of data
+        $header = pack('vv', $record, strlen($data) - $i);
+        $tmp .= $header;
+        $tmp .= substr($data, $i);
+
+        return $tmp;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Escher.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Escher.php
new file mode 100755
index 0000000..1ee2e90
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Escher.php
@@ -0,0 +1,510 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Xls;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer;
+use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer;
+use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer\SpContainer;
+use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer;
+use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer;
+use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE;
+use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE\Blip;
+
+class Escher
+{
+    /**
+     * The object we are writing.
+     */
+    private $object;
+
+    /**
+     * The written binary data.
+     */
+    private $data;
+
+    /**
+     * Shape offsets. Positions in binary stream where a new shape record begins.
+     *
+     * @var array
+     */
+    private $spOffsets;
+
+    /**
+     * Shape types.
+     *
+     * @var array
+     */
+    private $spTypes;
+
+    /**
+     * Constructor.
+     *
+     * @param mixed $object
+     */
+    public function __construct($object)
+    {
+        $this->object = $object;
+    }
+
+    /**
+     * Process the object to be written.
+     *
+     * @return string
+     */
+    public function close()
+    {
+        // initialize
+        $this->data = '';
+
+        switch (get_class($this->object)) {
+            case \PhpOffice\PhpSpreadsheet\Shared\Escher::class:
+                if ($dggContainer = $this->object->getDggContainer()) {
+                    $writer = new self($dggContainer);
+                    $this->data = $writer->close();
+                } elseif ($dgContainer = $this->object->getDgContainer()) {
+                    $writer = new self($dgContainer);
+                    $this->data = $writer->close();
+                    $this->spOffsets = $writer->getSpOffsets();
+                    $this->spTypes = $writer->getSpTypes();
+                }
+
+                break;
+            case DggContainer::class:
+                // this is a container record
+
+                // initialize
+                $innerData = '';
+
+                // write the dgg
+                $recVer = 0x0;
+                $recInstance = 0x0000;
+                $recType = 0xF006;
+
+                $recVerInstance = $recVer;
+                $recVerInstance |= $recInstance << 4;
+
+                // dgg data
+                $dggData =
+                    pack(
+                        'VVVV',
+                        $this->object->getSpIdMax(), // maximum shape identifier increased by one
+                        $this->object->getCDgSaved() + 1, // number of file identifier clusters increased by one
+                        $this->object->getCSpSaved(),
+                        $this->object->getCDgSaved() // count total number of drawings saved
+                    );
+
+                // add file identifier clusters (one per drawing)
+                $IDCLs = $this->object->getIDCLs();
+
+                foreach ($IDCLs as $dgId => $maxReducedSpId) {
+                    $dggData .= pack('VV', $dgId, $maxReducedSpId + 1);
+                }
+
+                $header = pack('vvV', $recVerInstance, $recType, strlen($dggData));
+                $innerData .= $header . $dggData;
+
+                // write the bstoreContainer
+                if ($bstoreContainer = $this->object->getBstoreContainer()) {
+                    $writer = new self($bstoreContainer);
+                    $innerData .= $writer->close();
+                }
+
+                // write the record
+                $recVer = 0xF;
+                $recInstance = 0x0000;
+                $recType = 0xF000;
+                $length = strlen($innerData);
+
+                $recVerInstance = $recVer;
+                $recVerInstance |= $recInstance << 4;
+
+                $header = pack('vvV', $recVerInstance, $recType, $length);
+
+                $this->data = $header . $innerData;
+
+                break;
+            case BstoreContainer::class:
+                // this is a container record
+
+                // initialize
+                $innerData = '';
+
+                // treat the inner data
+                if ($BSECollection = $this->object->getBSECollection()) {
+                    foreach ($BSECollection as $BSE) {
+                        $writer = new self($BSE);
+                        $innerData .= $writer->close();
+                    }
+                }
+
+                // write the record
+                $recVer = 0xF;
+                $recInstance = count($this->object->getBSECollection());
+                $recType = 0xF001;
+                $length = strlen($innerData);
+
+                $recVerInstance = $recVer;
+                $recVerInstance |= $recInstance << 4;
+
+                $header = pack('vvV', $recVerInstance, $recType, $length);
+
+                $this->data = $header . $innerData;
+
+                break;
+            case BSE::class:
+                // this is a semi-container record
+
+                // initialize
+                $innerData = '';
+
+                // here we treat the inner data
+                if ($blip = $this->object->getBlip()) {
+                    $writer = new self($blip);
+                    $innerData .= $writer->close();
+                }
+
+                // initialize
+                $data = '';
+
+                $btWin32 = $this->object->getBlipType();
+                $btMacOS = $this->object->getBlipType();
+                $data .= pack('CC', $btWin32, $btMacOS);
+
+                $rgbUid = pack('VVVV', 0, 0, 0, 0); // todo
+                $data .= $rgbUid;
+
+                $tag = 0;
+                $size = strlen($innerData);
+                $cRef = 1;
+                $foDelay = 0; //todo
+                $unused1 = 0x0;
+                $cbName = 0x0;
+                $unused2 = 0x0;
+                $unused3 = 0x0;
+                $data .= pack('vVVVCCCC', $tag, $size, $cRef, $foDelay, $unused1, $cbName, $unused2, $unused3);
+
+                $data .= $innerData;
+
+                // write the record
+                $recVer = 0x2;
+                $recInstance = $this->object->getBlipType();
+                $recType = 0xF007;
+                $length = strlen($data);
+
+                $recVerInstance = $recVer;
+                $recVerInstance |= $recInstance << 4;
+
+                $header = pack('vvV', $recVerInstance, $recType, $length);
+
+                $this->data = $header;
+
+                $this->data .= $data;
+
+                break;
+            case Blip::class:
+                // this is an atom record
+
+                // write the record
+                switch ($this->object->getParent()->getBlipType()) {
+                    case BSE::BLIPTYPE_JPEG:
+                        // initialize
+                        $innerData = '';
+
+                        $rgbUid1 = pack('VVVV', 0, 0, 0, 0); // todo
+                        $innerData .= $rgbUid1;
+
+                        $tag = 0xFF; // todo
+                        $innerData .= pack('C', $tag);
+
+                        $innerData .= $this->object->getData();
+
+                        $recVer = 0x0;
+                        $recInstance = 0x46A;
+                        $recType = 0xF01D;
+                        $length = strlen($innerData);
+
+                        $recVerInstance = $recVer;
+                        $recVerInstance |= $recInstance << 4;
+
+                        $header = pack('vvV', $recVerInstance, $recType, $length);
+
+                        $this->data = $header;
+
+                        $this->data .= $innerData;
+
+                        break;
+                    case BSE::BLIPTYPE_PNG:
+                        // initialize
+                        $innerData = '';
+
+                        $rgbUid1 = pack('VVVV', 0, 0, 0, 0); // todo
+                        $innerData .= $rgbUid1;
+
+                        $tag = 0xFF; // todo
+                        $innerData .= pack('C', $tag);
+
+                        $innerData .= $this->object->getData();
+
+                        $recVer = 0x0;
+                        $recInstance = 0x6E0;
+                        $recType = 0xF01E;
+                        $length = strlen($innerData);
+
+                        $recVerInstance = $recVer;
+                        $recVerInstance |= $recInstance << 4;
+
+                        $header = pack('vvV', $recVerInstance, $recType, $length);
+
+                        $this->data = $header;
+
+                        $this->data .= $innerData;
+
+                        break;
+                }
+
+                break;
+            case DgContainer::class:
+                // this is a container record
+
+                // initialize
+                $innerData = '';
+
+                // write the dg
+                $recVer = 0x0;
+                $recInstance = $this->object->getDgId();
+                $recType = 0xF008;
+                $length = 8;
+
+                $recVerInstance = $recVer;
+                $recVerInstance |= $recInstance << 4;
+
+                $header = pack('vvV', $recVerInstance, $recType, $length);
+
+                // number of shapes in this drawing (including group shape)
+                $countShapes = count($this->object->getSpgrContainer()->getChildren());
+                $innerData .= $header . pack('VV', $countShapes, $this->object->getLastSpId());
+
+                // write the spgrContainer
+                if ($spgrContainer = $this->object->getSpgrContainer()) {
+                    $writer = new self($spgrContainer);
+                    $innerData .= $writer->close();
+
+                    // get the shape offsets relative to the spgrContainer record
+                    $spOffsets = $writer->getSpOffsets();
+                    $spTypes = $writer->getSpTypes();
+
+                    // save the shape offsets relative to dgContainer
+                    foreach ($spOffsets as &$spOffset) {
+                        $spOffset += 24; // add length of dgContainer header data (8 bytes) plus dg data (16 bytes)
+                    }
+
+                    $this->spOffsets = $spOffsets;
+                    $this->spTypes = $spTypes;
+                }
+
+                // write the record
+                $recVer = 0xF;
+                $recInstance = 0x0000;
+                $recType = 0xF002;
+                $length = strlen($innerData);
+
+                $recVerInstance = $recVer;
+                $recVerInstance |= $recInstance << 4;
+
+                $header = pack('vvV', $recVerInstance, $recType, $length);
+
+                $this->data = $header . $innerData;
+
+                break;
+            case SpgrContainer::class:
+                // this is a container record
+
+                // initialize
+                $innerData = '';
+
+                // initialize spape offsets
+                $totalSize = 8;
+                $spOffsets = [];
+                $spTypes = [];
+
+                // treat the inner data
+                foreach ($this->object->getChildren() as $spContainer) {
+                    $writer = new self($spContainer);
+                    $spData = $writer->close();
+                    $innerData .= $spData;
+
+                    // save the shape offsets (where new shape records begin)
+                    $totalSize += strlen($spData);
+                    $spOffsets[] = $totalSize;
+
+                    $spTypes = array_merge($spTypes, $writer->getSpTypes());
+                }
+
+                // write the record
+                $recVer = 0xF;
+                $recInstance = 0x0000;
+                $recType = 0xF003;
+                $length = strlen($innerData);
+
+                $recVerInstance = $recVer;
+                $recVerInstance |= $recInstance << 4;
+
+                $header = pack('vvV', $recVerInstance, $recType, $length);
+
+                $this->data = $header . $innerData;
+                $this->spOffsets = $spOffsets;
+                $this->spTypes = $spTypes;
+
+                break;
+            case SpContainer::class:
+                // initialize
+                $data = '';
+
+                // build the data
+
+                // write group shape record, if necessary?
+                if ($this->object->getSpgr()) {
+                    $recVer = 0x1;
+                    $recInstance = 0x0000;
+                    $recType = 0xF009;
+                    $length = 0x00000010;
+
+                    $recVerInstance = $recVer;
+                    $recVerInstance |= $recInstance << 4;
+
+                    $header = pack('vvV', $recVerInstance, $recType, $length);
+
+                    $data .= $header . pack('VVVV', 0, 0, 0, 0);
+                }
+                $this->spTypes[] = ($this->object->getSpType());
+
+                // write the shape record
+                $recVer = 0x2;
+                $recInstance = $this->object->getSpType(); // shape type
+                $recType = 0xF00A;
+                $length = 0x00000008;
+
+                $recVerInstance = $recVer;
+                $recVerInstance |= $recInstance << 4;
+
+                $header = pack('vvV', $recVerInstance, $recType, $length);
+
+                $data .= $header . pack('VV', $this->object->getSpId(), $this->object->getSpgr() ? 0x0005 : 0x0A00);
+
+                // the options
+                if ($this->object->getOPTCollection()) {
+                    $optData = '';
+
+                    $recVer = 0x3;
+                    $recInstance = count($this->object->getOPTCollection());
+                    $recType = 0xF00B;
+                    foreach ($this->object->getOPTCollection() as $property => $value) {
+                        $optData .= pack('vV', $property, $value);
+                    }
+                    $length = strlen($optData);
+
+                    $recVerInstance = $recVer;
+                    $recVerInstance |= $recInstance << 4;
+
+                    $header = pack('vvV', $recVerInstance, $recType, $length);
+                    $data .= $header . $optData;
+                }
+
+                // the client anchor
+                if ($this->object->getStartCoordinates()) {
+                    $clientAnchorData = '';
+
+                    $recVer = 0x0;
+                    $recInstance = 0x0;
+                    $recType = 0xF010;
+
+                    // start coordinates
+                    [$column, $row] = Coordinate::coordinateFromString($this->object->getStartCoordinates());
+                    $c1 = Coordinate::columnIndexFromString($column) - 1;
+                    $r1 = $row - 1;
+
+                    // start offsetX
+                    $startOffsetX = $this->object->getStartOffsetX();
+
+                    // start offsetY
+                    $startOffsetY = $this->object->getStartOffsetY();
+
+                    // end coordinates
+                    [$column, $row] = Coordinate::coordinateFromString($this->object->getEndCoordinates());
+                    $c2 = Coordinate::columnIndexFromString($column) - 1;
+                    $r2 = $row - 1;
+
+                    // end offsetX
+                    $endOffsetX = $this->object->getEndOffsetX();
+
+                    // end offsetY
+                    $endOffsetY = $this->object->getEndOffsetY();
+
+                    $clientAnchorData = pack('vvvvvvvvv', $this->object->getSpFlag(), $c1, $startOffsetX, $r1, $startOffsetY, $c2, $endOffsetX, $r2, $endOffsetY);
+
+                    $length = strlen($clientAnchorData);
+
+                    $recVerInstance = $recVer;
+                    $recVerInstance |= $recInstance << 4;
+
+                    $header = pack('vvV', $recVerInstance, $recType, $length);
+                    $data .= $header . $clientAnchorData;
+                }
+
+                // the client data, just empty for now
+                if (!$this->object->getSpgr()) {
+                    $clientDataData = '';
+
+                    $recVer = 0x0;
+                    $recInstance = 0x0;
+                    $recType = 0xF011;
+
+                    $length = strlen($clientDataData);
+
+                    $recVerInstance = $recVer;
+                    $recVerInstance |= $recInstance << 4;
+
+                    $header = pack('vvV', $recVerInstance, $recType, $length);
+                    $data .= $header . $clientDataData;
+                }
+
+                // write the record
+                $recVer = 0xF;
+                $recInstance = 0x0000;
+                $recType = 0xF004;
+                $length = strlen($data);
+
+                $recVerInstance = $recVer;
+                $recVerInstance |= $recInstance << 4;
+
+                $header = pack('vvV', $recVerInstance, $recType, $length);
+
+                $this->data = $header . $data;
+
+                break;
+        }
+
+        return $this->data;
+    }
+
+    /**
+     * Gets the shape offsets.
+     *
+     * @return array
+     */
+    public function getSpOffsets()
+    {
+        return $this->spOffsets;
+    }
+
+    /**
+     * Gets the shape types.
+     *
+     * @return array
+     */
+    public function getSpTypes()
+    {
+        return $this->spTypes;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Font.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Font.php
new file mode 100755
index 0000000..9cb31ea
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Font.php
@@ -0,0 +1,147 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Xls;
+
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+
+class Font
+{
+    /**
+     * Color index.
+     *
+     * @var int
+     */
+    private $colorIndex;
+
+    /**
+     * Font.
+     *
+     * @var \PhpOffice\PhpSpreadsheet\Style\Font
+     */
+    private $font;
+
+    /**
+     * Constructor.
+     */
+    public function __construct(\PhpOffice\PhpSpreadsheet\Style\Font $font)
+    {
+        $this->colorIndex = 0x7FFF;
+        $this->font = $font;
+    }
+
+    /**
+     * Set the color index.
+     *
+     * @param int $colorIndex
+     */
+    public function setColorIndex($colorIndex): void
+    {
+        $this->colorIndex = $colorIndex;
+    }
+
+    /**
+     * Get font record data.
+     *
+     * @return string
+     */
+    public function writeFont()
+    {
+        $font_outline = 0;
+        $font_shadow = 0;
+
+        $icv = $this->colorIndex; // Index to color palette
+        if ($this->font->getSuperscript()) {
+            $sss = 1;
+        } elseif ($this->font->getSubscript()) {
+            $sss = 2;
+        } else {
+            $sss = 0;
+        }
+        $bFamily = 0; // Font family
+        $bCharSet = \PhpOffice\PhpSpreadsheet\Shared\Font::getCharsetFromFontName($this->font->getName()); // Character set
+
+        $record = 0x31; // Record identifier
+        $reserved = 0x00; // Reserved
+        $grbit = 0x00; // Font attributes
+        if ($this->font->getItalic()) {
+            $grbit |= 0x02;
+        }
+        if ($this->font->getStrikethrough()) {
+            $grbit |= 0x08;
+        }
+        if ($font_outline) {
+            $grbit |= 0x10;
+        }
+        if ($font_shadow) {
+            $grbit |= 0x20;
+        }
+
+        $data = pack(
+            'vvvvvCCCC',
+            // Fontsize (in twips)
+            $this->font->getSize() * 20,
+            $grbit,
+            // Colour
+            $icv,
+            // Font weight
+            self::mapBold($this->font->getBold()),
+            // Superscript/Subscript
+            $sss,
+            self::mapUnderline($this->font->getUnderline()),
+            $bFamily,
+            $bCharSet,
+            $reserved
+        );
+        $data .= StringHelper::UTF8toBIFF8UnicodeShort($this->font->getName());
+
+        $length = strlen($data);
+        $header = pack('vv', $record, $length);
+
+        return $header . $data;
+    }
+
+    /**
+     * Map to BIFF5-BIFF8 codes for bold.
+     *
+     * @param bool $bold
+     *
+     * @return int
+     */
+    private static function mapBold($bold)
+    {
+        if ($bold) {
+            return 0x2BC; //  700 = Bold font weight
+        }
+
+        return 0x190; //  400 = Normal font weight
+    }
+
+    /**
+     * Map of BIFF2-BIFF8 codes for underline styles.
+     *
+     * @var array of int
+     */
+    private static $mapUnderline = [
+        \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_NONE => 0x00,
+        \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLE => 0x01,
+        \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLE => 0x02,
+        \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLEACCOUNTING => 0x21,
+        \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLEACCOUNTING => 0x22,
+    ];
+
+    /**
+     * Map underline.
+     *
+     * @param string $underline
+     *
+     * @return int
+     */
+    private static function mapUnderline($underline)
+    {
+        if (isset(self::$mapUnderline[$underline])) {
+            return self::$mapUnderline[$underline];
+        }
+
+        return 0x00;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Parser.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Parser.php
new file mode 100755
index 0000000..5e3316a
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Parser.php
@@ -0,0 +1,1438 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Xls;
+
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet as PhpspreadsheetWorksheet;
+use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
+
+// Original file header of PEAR::Spreadsheet_Excel_Writer_Parser (used as the base for this class):
+// -----------------------------------------------------------------------------------------
+// *  Class for parsing Excel formulas
+// *
+// *  License Information:
+// *
+// *    Spreadsheet_Excel_Writer:  A library for generating Excel Spreadsheets
+// *    Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com
+// *
+// *    This library is free software; you can redistribute it and/or
+// *    modify it under the terms of the GNU Lesser General Public
+// *    License as published by the Free Software Foundation; either
+// *    version 2.1 of the License, or (at your option) any later version.
+// *
+// *    This library is distributed in the hope that it will be useful,
+// *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+// *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// *    Lesser General Public License for more details.
+// *
+// *    You should have received a copy of the GNU Lesser General Public
+// *    License along with this library; if not, write to the Free Software
+// *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+// */
+class Parser
+{
+    /**    Constants                */
+    // Sheet title in unquoted form
+    // Invalid sheet title characters cannot occur in the sheet title:
+    //         *:/\?[]
+    // Moreover, there are valid sheet title characters that cannot occur in unquoted form (there may be more?)
+    // +-% '^&<>=,;#()"{}
+    const REGEX_SHEET_TITLE_UNQUOTED = '[^\*\:\/\\\\\?\[\]\+\-\% \\\'\^\&\<\>\=\,\;\#\(\)\"\{\}]+';
+
+    // Sheet title in quoted form (without surrounding quotes)
+    // Invalid sheet title characters cannot occur in the sheet title:
+    // *:/\?[]                    (usual invalid sheet title characters)
+    // Single quote is represented as a pair ''
+    const REGEX_SHEET_TITLE_QUOTED = '(([^\*\:\/\\\\\?\[\]\\\'])+|(\\\'\\\')+)+';
+
+    /**
+     * The index of the character we are currently looking at.
+     *
+     * @var int
+     */
+    public $currentCharacter;
+
+    /**
+     * The token we are working on.
+     *
+     * @var string
+     */
+    public $currentToken;
+
+    /**
+     * The formula to parse.
+     *
+     * @var string
+     */
+    private $formula;
+
+    /**
+     * The character ahead of the current char.
+     *
+     * @var string
+     */
+    public $lookAhead;
+
+    /**
+     * The parse tree to be generated.
+     *
+     * @var string
+     */
+    private $parseTree;
+
+    /**
+     * Array of external sheets.
+     *
+     * @var array
+     */
+    private $externalSheets;
+
+    /**
+     * Array of sheet references in the form of REF structures.
+     *
+     * @var array
+     */
+    public $references;
+
+    /**
+     * The Excel ptg indices.
+     *
+     * @var array
+     */
+    private $ptg = [
+        'ptgExp' => 0x01,
+        'ptgTbl' => 0x02,
+        'ptgAdd' => 0x03,
+        'ptgSub' => 0x04,
+        'ptgMul' => 0x05,
+        'ptgDiv' => 0x06,
+        'ptgPower' => 0x07,
+        'ptgConcat' => 0x08,
+        'ptgLT' => 0x09,
+        'ptgLE' => 0x0A,
+        'ptgEQ' => 0x0B,
+        'ptgGE' => 0x0C,
+        'ptgGT' => 0x0D,
+        'ptgNE' => 0x0E,
+        'ptgIsect' => 0x0F,
+        'ptgUnion' => 0x10,
+        'ptgRange' => 0x11,
+        'ptgUplus' => 0x12,
+        'ptgUminus' => 0x13,
+        'ptgPercent' => 0x14,
+        'ptgParen' => 0x15,
+        'ptgMissArg' => 0x16,
+        'ptgStr' => 0x17,
+        'ptgAttr' => 0x19,
+        'ptgSheet' => 0x1A,
+        'ptgEndSheet' => 0x1B,
+        'ptgErr' => 0x1C,
+        'ptgBool' => 0x1D,
+        'ptgInt' => 0x1E,
+        'ptgNum' => 0x1F,
+        'ptgArray' => 0x20,
+        'ptgFunc' => 0x21,
+        'ptgFuncVar' => 0x22,
+        'ptgName' => 0x23,
+        'ptgRef' => 0x24,
+        'ptgArea' => 0x25,
+        'ptgMemArea' => 0x26,
+        'ptgMemErr' => 0x27,
+        'ptgMemNoMem' => 0x28,
+        'ptgMemFunc' => 0x29,
+        'ptgRefErr' => 0x2A,
+        'ptgAreaErr' => 0x2B,
+        'ptgRefN' => 0x2C,
+        'ptgAreaN' => 0x2D,
+        'ptgMemAreaN' => 0x2E,
+        'ptgMemNoMemN' => 0x2F,
+        'ptgNameX' => 0x39,
+        'ptgRef3d' => 0x3A,
+        'ptgArea3d' => 0x3B,
+        'ptgRefErr3d' => 0x3C,
+        'ptgAreaErr3d' => 0x3D,
+        'ptgArrayV' => 0x40,
+        'ptgFuncV' => 0x41,
+        'ptgFuncVarV' => 0x42,
+        'ptgNameV' => 0x43,
+        'ptgRefV' => 0x44,
+        'ptgAreaV' => 0x45,
+        'ptgMemAreaV' => 0x46,
+        'ptgMemErrV' => 0x47,
+        'ptgMemNoMemV' => 0x48,
+        'ptgMemFuncV' => 0x49,
+        'ptgRefErrV' => 0x4A,
+        'ptgAreaErrV' => 0x4B,
+        'ptgRefNV' => 0x4C,
+        'ptgAreaNV' => 0x4D,
+        'ptgMemAreaNV' => 0x4E,
+        'ptgMemNoMemNV' => 0x4F,
+        'ptgFuncCEV' => 0x58,
+        'ptgNameXV' => 0x59,
+        'ptgRef3dV' => 0x5A,
+        'ptgArea3dV' => 0x5B,
+        'ptgRefErr3dV' => 0x5C,
+        'ptgAreaErr3dV' => 0x5D,
+        'ptgArrayA' => 0x60,
+        'ptgFuncA' => 0x61,
+        'ptgFuncVarA' => 0x62,
+        'ptgNameA' => 0x63,
+        'ptgRefA' => 0x64,
+        'ptgAreaA' => 0x65,
+        'ptgMemAreaA' => 0x66,
+        'ptgMemErrA' => 0x67,
+        'ptgMemNoMemA' => 0x68,
+        'ptgMemFuncA' => 0x69,
+        'ptgRefErrA' => 0x6A,
+        'ptgAreaErrA' => 0x6B,
+        'ptgRefNA' => 0x6C,
+        'ptgAreaNA' => 0x6D,
+        'ptgMemAreaNA' => 0x6E,
+        'ptgMemNoMemNA' => 0x6F,
+        'ptgFuncCEA' => 0x78,
+        'ptgNameXA' => 0x79,
+        'ptgRef3dA' => 0x7A,
+        'ptgArea3dA' => 0x7B,
+        'ptgRefErr3dA' => 0x7C,
+        'ptgAreaErr3dA' => 0x7D,
+    ];
+
+    /**
+     * Thanks to Michael Meeks and Gnumeric for the initial arg values.
+     *
+     * The following hash was generated by "function_locale.pl" in the distro.
+     * Refer to function_locale.pl for non-English function names.
+     *
+     * The array elements are as follow:
+     * ptg:   The Excel function ptg code.
+     * args:  The number of arguments that the function takes:
+     *           >=0 is a fixed number of arguments.
+     *           -1  is a variable  number of arguments.
+     * class: The reference, value or array class of the function args.
+     * vol:   The function is volatile.
+     *
+     * @var array
+     */
+    private $functions = [
+        // function                  ptg  args  class  vol
+        'COUNT' => [0, -1, 0, 0],
+        'IF' => [1, -1, 1, 0],
+        'ISNA' => [2, 1, 1, 0],
+        'ISERROR' => [3, 1, 1, 0],
+        'SUM' => [4, -1, 0, 0],
+        'AVERAGE' => [5, -1, 0, 0],
+        'MIN' => [6, -1, 0, 0],
+        'MAX' => [7, -1, 0, 0],
+        'ROW' => [8, -1, 0, 0],
+        'COLUMN' => [9, -1, 0, 0],
+        'NA' => [10, 0, 0, 0],
+        'NPV' => [11, -1, 1, 0],
+        'STDEV' => [12, -1, 0, 0],
+        'DOLLAR' => [13, -1, 1, 0],
+        'FIXED' => [14, -1, 1, 0],
+        'SIN' => [15, 1, 1, 0],
+        'COS' => [16, 1, 1, 0],
+        'TAN' => [17, 1, 1, 0],
+        'ATAN' => [18, 1, 1, 0],
+        'PI' => [19, 0, 1, 0],
+        'SQRT' => [20, 1, 1, 0],
+        'EXP' => [21, 1, 1, 0],
+        'LN' => [22, 1, 1, 0],
+        'LOG10' => [23, 1, 1, 0],
+        'ABS' => [24, 1, 1, 0],
+        'INT' => [25, 1, 1, 0],
+        'SIGN' => [26, 1, 1, 0],
+        'ROUND' => [27, 2, 1, 0],
+        'LOOKUP' => [28, -1, 0, 0],
+        'INDEX' => [29, -1, 0, 1],
+        'REPT' => [30, 2, 1, 0],
+        'MID' => [31, 3, 1, 0],
+        'LEN' => [32, 1, 1, 0],
+        'VALUE' => [33, 1, 1, 0],
+        'TRUE' => [34, 0, 1, 0],
+        'FALSE' => [35, 0, 1, 0],
+        'AND' => [36, -1, 0, 0],
+        'OR' => [37, -1, 0, 0],
+        'NOT' => [38, 1, 1, 0],
+        'MOD' => [39, 2, 1, 0],
+        'DCOUNT' => [40, 3, 0, 0],
+        'DSUM' => [41, 3, 0, 0],
+        'DAVERAGE' => [42, 3, 0, 0],
+        'DMIN' => [43, 3, 0, 0],
+        'DMAX' => [44, 3, 0, 0],
+        'DSTDEV' => [45, 3, 0, 0],
+        'VAR' => [46, -1, 0, 0],
+        'DVAR' => [47, 3, 0, 0],
+        'TEXT' => [48, 2, 1, 0],
+        'LINEST' => [49, -1, 0, 0],
+        'TREND' => [50, -1, 0, 0],
+        'LOGEST' => [51, -1, 0, 0],
+        'GROWTH' => [52, -1, 0, 0],
+        'PV' => [56, -1, 1, 0],
+        'FV' => [57, -1, 1, 0],
+        'NPER' => [58, -1, 1, 0],
+        'PMT' => [59, -1, 1, 0],
+        'RATE' => [60, -1, 1, 0],
+        'MIRR' => [61, 3, 0, 0],
+        'IRR' => [62, -1, 0, 0],
+        'RAND' => [63, 0, 1, 1],
+        'MATCH' => [64, -1, 0, 0],
+        'DATE' => [65, 3, 1, 0],
+        'TIME' => [66, 3, 1, 0],
+        'DAY' => [67, 1, 1, 0],
+        'MONTH' => [68, 1, 1, 0],
+        'YEAR' => [69, 1, 1, 0],
+        'WEEKDAY' => [70, -1, 1, 0],
+        'HOUR' => [71, 1, 1, 0],
+        'MINUTE' => [72, 1, 1, 0],
+        'SECOND' => [73, 1, 1, 0],
+        'NOW' => [74, 0, 1, 1],
+        'AREAS' => [75, 1, 0, 1],
+        'ROWS' => [76, 1, 0, 1],
+        'COLUMNS' => [77, 1, 0, 1],
+        'OFFSET' => [78, -1, 0, 1],
+        'SEARCH' => [82, -1, 1, 0],
+        'TRANSPOSE' => [83, 1, 1, 0],
+        'TYPE' => [86, 1, 1, 0],
+        'ATAN2' => [97, 2, 1, 0],
+        'ASIN' => [98, 1, 1, 0],
+        'ACOS' => [99, 1, 1, 0],
+        'CHOOSE' => [100, -1, 1, 0],
+        'HLOOKUP' => [101, -1, 0, 0],
+        'VLOOKUP' => [102, -1, 0, 0],
+        'ISREF' => [105, 1, 0, 0],
+        'LOG' => [109, -1, 1, 0],
+        'CHAR' => [111, 1, 1, 0],
+        'LOWER' => [112, 1, 1, 0],
+        'UPPER' => [113, 1, 1, 0],
+        'PROPER' => [114, 1, 1, 0],
+        'LEFT' => [115, -1, 1, 0],
+        'RIGHT' => [116, -1, 1, 0],
+        'EXACT' => [117, 2, 1, 0],
+        'TRIM' => [118, 1, 1, 0],
+        'REPLACE' => [119, 4, 1, 0],
+        'SUBSTITUTE' => [120, -1, 1, 0],
+        'CODE' => [121, 1, 1, 0],
+        'FIND' => [124, -1, 1, 0],
+        'CELL' => [125, -1, 0, 1],
+        'ISERR' => [126, 1, 1, 0],
+        'ISTEXT' => [127, 1, 1, 0],
+        'ISNUMBER' => [128, 1, 1, 0],
+        'ISBLANK' => [129, 1, 1, 0],
+        'T' => [130, 1, 0, 0],
+        'N' => [131, 1, 0, 0],
+        'DATEVALUE' => [140, 1, 1, 0],
+        'TIMEVALUE' => [141, 1, 1, 0],
+        'SLN' => [142, 3, 1, 0],
+        'SYD' => [143, 4, 1, 0],
+        'DDB' => [144, -1, 1, 0],
+        'INDIRECT' => [148, -1, 1, 1],
+        'CALL' => [150, -1, 1, 0],
+        'CLEAN' => [162, 1, 1, 0],
+        'MDETERM' => [163, 1, 2, 0],
+        'MINVERSE' => [164, 1, 2, 0],
+        'MMULT' => [165, 2, 2, 0],
+        'IPMT' => [167, -1, 1, 0],
+        'PPMT' => [168, -1, 1, 0],
+        'COUNTA' => [169, -1, 0, 0],
+        'PRODUCT' => [183, -1, 0, 0],
+        'FACT' => [184, 1, 1, 0],
+        'DPRODUCT' => [189, 3, 0, 0],
+        'ISNONTEXT' => [190, 1, 1, 0],
+        'STDEVP' => [193, -1, 0, 0],
+        'VARP' => [194, -1, 0, 0],
+        'DSTDEVP' => [195, 3, 0, 0],
+        'DVARP' => [196, 3, 0, 0],
+        'TRUNC' => [197, -1, 1, 0],
+        'ISLOGICAL' => [198, 1, 1, 0],
+        'DCOUNTA' => [199, 3, 0, 0],
+        'USDOLLAR' => [204, -1, 1, 0],
+        'FINDB' => [205, -1, 1, 0],
+        'SEARCHB' => [206, -1, 1, 0],
+        'REPLACEB' => [207, 4, 1, 0],
+        'LEFTB' => [208, -1, 1, 0],
+        'RIGHTB' => [209, -1, 1, 0],
+        'MIDB' => [210, 3, 1, 0],
+        'LENB' => [211, 1, 1, 0],
+        'ROUNDUP' => [212, 2, 1, 0],
+        'ROUNDDOWN' => [213, 2, 1, 0],
+        'ASC' => [214, 1, 1, 0],
+        'DBCS' => [215, 1, 1, 0],
+        'RANK' => [216, -1, 0, 0],
+        'ADDRESS' => [219, -1, 1, 0],
+        'DAYS360' => [220, -1, 1, 0],
+        'TODAY' => [221, 0, 1, 1],
+        'VDB' => [222, -1, 1, 0],
+        'MEDIAN' => [227, -1, 0, 0],
+        'SUMPRODUCT' => [228, -1, 2, 0],
+        'SINH' => [229, 1, 1, 0],
+        'COSH' => [230, 1, 1, 0],
+        'TANH' => [231, 1, 1, 0],
+        'ASINH' => [232, 1, 1, 0],
+        'ACOSH' => [233, 1, 1, 0],
+        'ATANH' => [234, 1, 1, 0],
+        'DGET' => [235, 3, 0, 0],
+        'INFO' => [244, 1, 1, 1],
+        'DB' => [247, -1, 1, 0],
+        'FREQUENCY' => [252, 2, 0, 0],
+        'ERROR.TYPE' => [261, 1, 1, 0],
+        'REGISTER.ID' => [267, -1, 1, 0],
+        'AVEDEV' => [269, -1, 0, 0],
+        'BETADIST' => [270, -1, 1, 0],
+        'GAMMALN' => [271, 1, 1, 0],
+        'BETAINV' => [272, -1, 1, 0],
+        'BINOMDIST' => [273, 4, 1, 0],
+        'CHIDIST' => [274, 2, 1, 0],
+        'CHIINV' => [275, 2, 1, 0],
+        'COMBIN' => [276, 2, 1, 0],
+        'CONFIDENCE' => [277, 3, 1, 0],
+        'CRITBINOM' => [278, 3, 1, 0],
+        'EVEN' => [279, 1, 1, 0],
+        'EXPONDIST' => [280, 3, 1, 0],
+        'FDIST' => [281, 3, 1, 0],
+        'FINV' => [282, 3, 1, 0],
+        'FISHER' => [283, 1, 1, 0],
+        'FISHERINV' => [284, 1, 1, 0],
+        'FLOOR' => [285, 2, 1, 0],
+        'GAMMADIST' => [286, 4, 1, 0],
+        'GAMMAINV' => [287, 3, 1, 0],
+        'CEILING' => [288, 2, 1, 0],
+        'HYPGEOMDIST' => [289, 4, 1, 0],
+        'LOGNORMDIST' => [290, 3, 1, 0],
+        'LOGINV' => [291, 3, 1, 0],
+        'NEGBINOMDIST' => [292, 3, 1, 0],
+        'NORMDIST' => [293, 4, 1, 0],
+        'NORMSDIST' => [294, 1, 1, 0],
+        'NORMINV' => [295, 3, 1, 0],
+        'NORMSINV' => [296, 1, 1, 0],
+        'STANDARDIZE' => [297, 3, 1, 0],
+        'ODD' => [298, 1, 1, 0],
+        'PERMUT' => [299, 2, 1, 0],
+        'POISSON' => [300, 3, 1, 0],
+        'TDIST' => [301, 3, 1, 0],
+        'WEIBULL' => [302, 4, 1, 0],
+        'SUMXMY2' => [303, 2, 2, 0],
+        'SUMX2MY2' => [304, 2, 2, 0],
+        'SUMX2PY2' => [305, 2, 2, 0],
+        'CHITEST' => [306, 2, 2, 0],
+        'CORREL' => [307, 2, 2, 0],
+        'COVAR' => [308, 2, 2, 0],
+        'FORECAST' => [309, 3, 2, 0],
+        'FTEST' => [310, 2, 2, 0],
+        'INTERCEPT' => [311, 2, 2, 0],
+        'PEARSON' => [312, 2, 2, 0],
+        'RSQ' => [313, 2, 2, 0],
+        'STEYX' => [314, 2, 2, 0],
+        'SLOPE' => [315, 2, 2, 0],
+        'TTEST' => [316, 4, 2, 0],
+        'PROB' => [317, -1, 2, 0],
+        'DEVSQ' => [318, -1, 0, 0],
+        'GEOMEAN' => [319, -1, 0, 0],
+        'HARMEAN' => [320, -1, 0, 0],
+        'SUMSQ' => [321, -1, 0, 0],
+        'KURT' => [322, -1, 0, 0],
+        'SKEW' => [323, -1, 0, 0],
+        'ZTEST' => [324, -1, 0, 0],
+        'LARGE' => [325, 2, 0, 0],
+        'SMALL' => [326, 2, 0, 0],
+        'QUARTILE' => [327, 2, 0, 0],
+        'PERCENTILE' => [328, 2, 0, 0],
+        'PERCENTRANK' => [329, -1, 0, 0],
+        'MODE' => [330, -1, 2, 0],
+        'TRIMMEAN' => [331, 2, 0, 0],
+        'TINV' => [332, 2, 1, 0],
+        'CONCATENATE' => [336, -1, 1, 0],
+        'POWER' => [337, 2, 1, 0],
+        'RADIANS' => [342, 1, 1, 0],
+        'DEGREES' => [343, 1, 1, 0],
+        'SUBTOTAL' => [344, -1, 0, 0],
+        'SUMIF' => [345, -1, 0, 0],
+        'COUNTIF' => [346, 2, 0, 0],
+        'COUNTBLANK' => [347, 1, 0, 0],
+        'ISPMT' => [350, 4, 1, 0],
+        'DATEDIF' => [351, 3, 1, 0],
+        'DATESTRING' => [352, 1, 1, 0],
+        'NUMBERSTRING' => [353, 2, 1, 0],
+        'ROMAN' => [354, -1, 1, 0],
+        'GETPIVOTDATA' => [358, -1, 0, 0],
+        'HYPERLINK' => [359, -1, 1, 0],
+        'PHONETIC' => [360, 1, 0, 0],
+        'AVERAGEA' => [361, -1, 0, 0],
+        'MAXA' => [362, -1, 0, 0],
+        'MINA' => [363, -1, 0, 0],
+        'STDEVPA' => [364, -1, 0, 0],
+        'VARPA' => [365, -1, 0, 0],
+        'STDEVA' => [366, -1, 0, 0],
+        'VARA' => [367, -1, 0, 0],
+        'BAHTTEXT' => [368, 1, 0, 0],
+    ];
+
+    /**
+     * The class constructor.
+     */
+    public function __construct()
+    {
+        $this->currentCharacter = 0;
+        $this->currentToken = ''; // The token we are working on.
+        $this->formula = ''; // The formula to parse.
+        $this->lookAhead = ''; // The character ahead of the current char.
+        $this->parseTree = ''; // The parse tree to be generated.
+        $this->externalSheets = [];
+        $this->references = [];
+    }
+
+    /**
+     * Convert a token to the proper ptg value.
+     *
+     * @param mixed $token the token to convert
+     *
+     * @return mixed the converted token on success
+     */
+    private function convert($token)
+    {
+        if (preg_match('/"([^"]|""){0,255}"/', $token)) {
+            return $this->convertString($token);
+        } elseif (is_numeric($token)) {
+            return $this->convertNumber($token);
+        // match references like A1 or $A$1
+        } elseif (preg_match('/^\$?([A-Ia-i]?[A-Za-z])\$?(\d+)$/', $token)) {
+            return $this->convertRef2d($token);
+        // match external references like Sheet1!A1 or Sheet1:Sheet2!A1 or Sheet1!$A$1 or Sheet1:Sheet2!$A$1
+        } elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?[A-Ia-i]?[A-Za-z]\$?(\\d+)$/u', $token)) {
+            return $this->convertRef3d($token);
+        // match external references like 'Sheet1'!A1 or 'Sheet1:Sheet2'!A1 or 'Sheet1'!$A$1 or 'Sheet1:Sheet2'!$A$1
+        } elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?[A-Ia-i]?[A-Za-z]\\$?(\\d+)$/u", $token)) {
+            return $this->convertRef3d($token);
+        // match ranges like A1:B2 or $A$1:$B$2
+        } elseif (preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)\:(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)$/', $token)) {
+            return $this->convertRange2d($token);
+        // match external ranges like Sheet1!A1:B2 or Sheet1:Sheet2!A1:B2 or Sheet1!$A$1:$B$2 or Sheet1:Sheet2!$A$1:$B$2
+        } elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?([A-Ia-i]?[A-Za-z])?\$?(\\d+)\\:\$?([A-Ia-i]?[A-Za-z])?\$?(\\d+)$/u', $token)) {
+            return $this->convertRange3d($token);
+        // match external ranges like 'Sheet1'!A1:B2 or 'Sheet1:Sheet2'!A1:B2 or 'Sheet1'!$A$1:$B$2 or 'Sheet1:Sheet2'!$A$1:$B$2
+        } elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?([A-Ia-i]?[A-Za-z])?\\$?(\\d+)\\:\\$?([A-Ia-i]?[A-Za-z])?\\$?(\\d+)$/u", $token)) {
+            return $this->convertRange3d($token);
+        // operators (including parentheses)
+        } elseif (isset($this->ptg[$token])) {
+            return pack('C', $this->ptg[$token]);
+        // match error codes
+        } elseif (preg_match('/^#[A-Z0\\/]{3,5}[!?]{1}$/', $token) || $token == '#N/A') {
+            return $this->convertError($token);
+        // commented so argument number can be processed correctly. See toReversePolish().
+        /*elseif (preg_match("/[A-Z0-9\xc0-\xdc\.]+/", $token))
+        {
+            return($this->convertFunction($token, $this->_func_args));
+        }*/
+        // if it's an argument, ignore the token (the argument remains)
+        } elseif ($token == 'arg') {
+            return '';
+        }
+
+        // TODO: use real error codes
+        throw new WriterException("Unknown token $token");
+    }
+
+    /**
+     * Convert a number token to ptgInt or ptgNum.
+     *
+     * @param mixed $num an integer or double for conversion to its ptg value
+     *
+     * @return string
+     */
+    private function convertNumber($num)
+    {
+        // Integer in the range 0..2**16-1
+        if ((preg_match('/^\\d+$/', $num)) && ($num <= 65535)) {
+            return pack('Cv', $this->ptg['ptgInt'], $num);
+        }
+
+        // A float
+        if (BIFFwriter::getByteOrder()) { // if it's Big Endian
+            $num = strrev($num);
+        }
+
+        return pack('Cd', $this->ptg['ptgNum'], $num);
+    }
+
+    /**
+     * Convert a string token to ptgStr.
+     *
+     * @param string $string a string for conversion to its ptg value
+     *
+     * @return mixed the converted token on success
+     */
+    private function convertString($string)
+    {
+        // chop away beggining and ending quotes
+        $string = substr($string, 1, -1);
+        if (strlen($string) > 255) {
+            throw new WriterException('String is too long');
+        }
+
+        return pack('C', $this->ptg['ptgStr']) . StringHelper::UTF8toBIFF8UnicodeShort($string);
+    }
+
+    /**
+     * Convert a function to a ptgFunc or ptgFuncVarV depending on the number of
+     * args that it takes.
+     *
+     * @param string $token the name of the function for convertion to ptg value
+     * @param int $num_args the number of arguments the function receives
+     *
+     * @return string The packed ptg for the function
+     */
+    private function convertFunction($token, $num_args)
+    {
+        $args = $this->functions[$token][1];
+
+        // Fixed number of args eg. TIME($i, $j, $k).
+        if ($args >= 0) {
+            return pack('Cv', $this->ptg['ptgFuncV'], $this->functions[$token][0]);
+        }
+        // Variable number of args eg. SUM($i, $j, $k, ..).
+        if ($args == -1) {
+            return pack('CCv', $this->ptg['ptgFuncVarV'], $num_args, $this->functions[$token][0]);
+        }
+    }
+
+    /**
+     * Convert an Excel range such as A1:D4 to a ptgRefV.
+     *
+     * @param string $range An Excel range in the A1:A2
+     * @param int $class
+     *
+     * @return string
+     */
+    private function convertRange2d($range, $class = 0)
+    {
+        // TODO: possible class value 0,1,2 check Formula.pm
+        // Split the range into 2 cell refs
+        if (preg_match('/^(\$)?([A-Ia-i]?[A-Za-z])(\$)?(\d+)\:(\$)?([A-Ia-i]?[A-Za-z])(\$)?(\d+)$/', $range)) {
+            [$cell1, $cell2] = explode(':', $range);
+        } else {
+            // TODO: use real error codes
+            throw new WriterException('Unknown range separator');
+        }
+
+        // Convert the cell references
+        [$row1, $col1] = $this->cellToPackedRowcol($cell1);
+        [$row2, $col2] = $this->cellToPackedRowcol($cell2);
+
+        // The ptg value depends on the class of the ptg.
+        if ($class == 0) {
+            $ptgArea = pack('C', $this->ptg['ptgArea']);
+        } elseif ($class == 1) {
+            $ptgArea = pack('C', $this->ptg['ptgAreaV']);
+        } elseif ($class == 2) {
+            $ptgArea = pack('C', $this->ptg['ptgAreaA']);
+        } else {
+            // TODO: use real error codes
+            throw new WriterException("Unknown class $class");
+        }
+
+        return $ptgArea . $row1 . $row2 . $col1 . $col2;
+    }
+
+    /**
+     * Convert an Excel 3d range such as "Sheet1!A1:D4" or "Sheet1:Sheet2!A1:D4" to
+     * a ptgArea3d.
+     *
+     * @param string $token an Excel range in the Sheet1!A1:A2 format
+     *
+     * @return mixed the packed ptgArea3d token on success
+     */
+    private function convertRange3d($token)
+    {
+        // Split the ref at the ! symbol
+        [$ext_ref, $range] = PhpspreadsheetWorksheet::extractSheetTitle($token, true);
+
+        // Convert the external reference part (different for BIFF8)
+        $ext_ref = $this->getRefIndex($ext_ref);
+
+        // Split the range into 2 cell refs
+        [$cell1, $cell2] = explode(':', $range);
+
+        // Convert the cell references
+        if (preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?(\\d+)$/', $cell1)) {
+            [$row1, $col1] = $this->cellToPackedRowcol($cell1);
+            [$row2, $col2] = $this->cellToPackedRowcol($cell2);
+        } else { // It's a rows range (like 26:27)
+            [$row1, $col1, $row2, $col2] = $this->rangeToPackedRange($cell1 . ':' . $cell2);
+        }
+
+        // The ptg value depends on the class of the ptg.
+        $ptgArea = pack('C', $this->ptg['ptgArea3d']);
+
+        return $ptgArea . $ext_ref . $row1 . $row2 . $col1 . $col2;
+    }
+
+    /**
+     * Convert an Excel reference such as A1, $B2, C$3 or $D$4 to a ptgRefV.
+     *
+     * @param string $cell An Excel cell reference
+     *
+     * @return string The cell in packed() format with the corresponding ptg
+     */
+    private function convertRef2d($cell)
+    {
+        // Convert the cell reference
+        $cell_array = $this->cellToPackedRowcol($cell);
+        [$row, $col] = $cell_array;
+
+        // The ptg value depends on the class of the ptg.
+        $ptgRef = pack('C', $this->ptg['ptgRefA']);
+
+        return $ptgRef . $row . $col;
+    }
+
+    /**
+     * Convert an Excel 3d reference such as "Sheet1!A1" or "Sheet1:Sheet2!A1" to a
+     * ptgRef3d.
+     *
+     * @param string $cell An Excel cell reference
+     *
+     * @return mixed the packed ptgRef3d token on success
+     */
+    private function convertRef3d($cell)
+    {
+        // Split the ref at the ! symbol
+        [$ext_ref, $cell] = PhpspreadsheetWorksheet::extractSheetTitle($cell, true);
+
+        // Convert the external reference part (different for BIFF8)
+        $ext_ref = $this->getRefIndex($ext_ref);
+
+        // Convert the cell reference part
+        [$row, $col] = $this->cellToPackedRowcol($cell);
+
+        // The ptg value depends on the class of the ptg.
+        $ptgRef = pack('C', $this->ptg['ptgRef3dA']);
+
+        return $ptgRef . $ext_ref . $row . $col;
+    }
+
+    /**
+     * Convert an error code to a ptgErr.
+     *
+     * @param string $errorCode The error code for conversion to its ptg value
+     *
+     * @return string The error code ptgErr
+     */
+    private function convertError($errorCode)
+    {
+        switch ($errorCode) {
+            case '#NULL!':
+                return pack('C', 0x00);
+            case '#DIV/0!':
+                return pack('C', 0x07);
+            case '#VALUE!':
+                return pack('C', 0x0F);
+            case '#REF!':
+                return pack('C', 0x17);
+            case '#NAME?':
+                return pack('C', 0x1D);
+            case '#NUM!':
+                return pack('C', 0x24);
+            case '#N/A':
+                return pack('C', 0x2A);
+        }
+
+        return pack('C', 0xFF);
+    }
+
+    /**
+     * Look up the REF index that corresponds to an external sheet name
+     * (or range). If it doesn't exist yet add it to the workbook's references
+     * array. It assumes all sheet names given must exist.
+     *
+     * @param string $ext_ref The name of the external reference
+     *
+     * @return mixed The reference index in packed() format on success
+     */
+    private function getRefIndex($ext_ref)
+    {
+        $ext_ref = preg_replace("/^'/", '', $ext_ref); // Remove leading  ' if any.
+        $ext_ref = preg_replace("/'$/", '', $ext_ref); // Remove trailing ' if any.
+        $ext_ref = str_replace('\'\'', '\'', $ext_ref); // Replace escaped '' with '
+
+        // Check if there is a sheet range eg., Sheet1:Sheet2.
+        if (preg_match('/:/', $ext_ref)) {
+            [$sheet_name1, $sheet_name2] = explode(':', $ext_ref);
+
+            $sheet1 = $this->getSheetIndex($sheet_name1);
+            if ($sheet1 == -1) {
+                throw new WriterException("Unknown sheet name $sheet_name1 in formula");
+            }
+            $sheet2 = $this->getSheetIndex($sheet_name2);
+            if ($sheet2 == -1) {
+                throw new WriterException("Unknown sheet name $sheet_name2 in formula");
+            }
+
+            // Reverse max and min sheet numbers if necessary
+            if ($sheet1 > $sheet2) {
+                [$sheet1, $sheet2] = [$sheet2, $sheet1];
+            }
+        } else { // Single sheet name only.
+            $sheet1 = $this->getSheetIndex($ext_ref);
+            if ($sheet1 == -1) {
+                throw new WriterException("Unknown sheet name $ext_ref in formula");
+            }
+            $sheet2 = $sheet1;
+        }
+
+        // assume all references belong to this document
+        $supbook_index = 0x00;
+        $ref = pack('vvv', $supbook_index, $sheet1, $sheet2);
+        $totalreferences = count($this->references);
+        $index = -1;
+        for ($i = 0; $i < $totalreferences; ++$i) {
+            if ($ref == $this->references[$i]) {
+                $index = $i;
+
+                break;
+            }
+        }
+        // if REF was not found add it to references array
+        if ($index == -1) {
+            $this->references[$totalreferences] = $ref;
+            $index = $totalreferences;
+        }
+
+        return pack('v', $index);
+    }
+
+    /**
+     * Look up the index that corresponds to an external sheet name. The hash of
+     * sheet names is updated by the addworksheet() method of the
+     * \PhpOffice\PhpSpreadsheet\Writer\Xls\Workbook class.
+     *
+     * @param string $sheet_name Sheet name
+     *
+     * @return int The sheet index, -1 if the sheet was not found
+     */
+    private function getSheetIndex($sheet_name)
+    {
+        if (!isset($this->externalSheets[$sheet_name])) {
+            return -1;
+        }
+
+        return $this->externalSheets[$sheet_name];
+    }
+
+    /**
+     * This method is used to update the array of sheet names. It is
+     * called by the addWorksheet() method of the
+     * \PhpOffice\PhpSpreadsheet\Writer\Xls\Workbook class.
+     *
+     * @see \PhpOffice\PhpSpreadsheet\Writer\Xls\Workbook::addWorksheet()
+     *
+     * @param string $name The name of the worksheet being added
+     * @param int $index The index of the worksheet being added
+     */
+    public function setExtSheet($name, $index): void
+    {
+        $this->externalSheets[$name] = $index;
+    }
+
+    /**
+     * pack() row and column into the required 3 or 4 byte format.
+     *
+     * @param string $cell The Excel cell reference to be packed
+     *
+     * @return array Array containing the row and column in packed() format
+     */
+    private function cellToPackedRowcol($cell)
+    {
+        $cell = strtoupper($cell);
+        [$row, $col, $row_rel, $col_rel] = $this->cellToRowcol($cell);
+        if ($col >= 256) {
+            throw new WriterException("Column in: $cell greater than 255");
+        }
+        if ($row >= 65536) {
+            throw new WriterException("Row in: $cell greater than 65536 ");
+        }
+
+        // Set the high bits to indicate if row or col are relative.
+        $col |= $col_rel << 14;
+        $col |= $row_rel << 15;
+        $col = pack('v', $col);
+
+        $row = pack('v', $row);
+
+        return [$row, $col];
+    }
+
+    /**
+     * pack() row range into the required 3 or 4 byte format.
+     * Just using maximum col/rows, which is probably not the correct solution.
+     *
+     * @param string $range The Excel range to be packed
+     *
+     * @return array Array containing (row1,col1,row2,col2) in packed() format
+     */
+    private function rangeToPackedRange($range)
+    {
+        preg_match('/(\$)?(\d+)\:(\$)?(\d+)/', $range, $match);
+        // return absolute rows if there is a $ in the ref
+        $row1_rel = empty($match[1]) ? 1 : 0;
+        $row1 = $match[2];
+        $row2_rel = empty($match[3]) ? 1 : 0;
+        $row2 = $match[4];
+        // Convert 1-index to zero-index
+        --$row1;
+        --$row2;
+        // Trick poor inocent Excel
+        $col1 = 0;
+        $col2 = 65535; // FIXME: maximum possible value for Excel 5 (change this!!!)
+
+        // FIXME: this changes for BIFF8
+        if (($row1 >= 65536) || ($row2 >= 65536)) {
+            throw new WriterException("Row in: $range greater than 65536 ");
+        }
+
+        // Set the high bits to indicate if rows are relative.
+        $col1 |= $row1_rel << 15;
+        $col2 |= $row2_rel << 15;
+        $col1 = pack('v', $col1);
+        $col2 = pack('v', $col2);
+
+        $row1 = pack('v', $row1);
+        $row2 = pack('v', $row2);
+
+        return [$row1, $col1, $row2, $col2];
+    }
+
+    /**
+     * Convert an Excel cell reference such as A1 or $B2 or C$3 or $D$4 to a zero
+     * indexed row and column number. Also returns two (0,1) values to indicate
+     * whether the row or column are relative references.
+     *
+     * @param string $cell the Excel cell reference in A1 format
+     *
+     * @return array
+     */
+    private function cellToRowcol($cell)
+    {
+        preg_match('/(\$)?([A-I]?[A-Z])(\$)?(\d+)/', $cell, $match);
+        // return absolute column if there is a $ in the ref
+        $col_rel = empty($match[1]) ? 1 : 0;
+        $col_ref = $match[2];
+        $row_rel = empty($match[3]) ? 1 : 0;
+        $row = $match[4];
+
+        // Convert base26 column string to a number.
+        $expn = strlen($col_ref) - 1;
+        $col = 0;
+        $col_ref_length = strlen($col_ref);
+        for ($i = 0; $i < $col_ref_length; ++$i) {
+            $col += (ord($col_ref[$i]) - 64) * 26 ** $expn;
+            --$expn;
+        }
+
+        // Convert 1-index to zero-index
+        --$row;
+        --$col;
+
+        return [$row, $col, $row_rel, $col_rel];
+    }
+
+    /**
+     * Advance to the next valid token.
+     */
+    private function advance()
+    {
+        $i = $this->currentCharacter;
+        $formula_length = strlen($this->formula);
+        // eat up white spaces
+        if ($i < $formula_length) {
+            while ($this->formula[$i] == ' ') {
+                ++$i;
+            }
+
+            if ($i < ($formula_length - 1)) {
+                $this->lookAhead = $this->formula[$i + 1];
+            }
+            $token = '';
+        }
+
+        while ($i < $formula_length) {
+            $token .= $this->formula[$i];
+
+            if ($i < ($formula_length - 1)) {
+                $this->lookAhead = $this->formula[$i + 1];
+            } else {
+                $this->lookAhead = '';
+            }
+
+            if ($this->match($token) != '') {
+                $this->currentCharacter = $i + 1;
+                $this->currentToken = $token;
+
+                return 1;
+            }
+
+            if ($i < ($formula_length - 2)) {
+                $this->lookAhead = $this->formula[$i + 2];
+            } else { // if we run out of characters lookAhead becomes empty
+                $this->lookAhead = '';
+            }
+            ++$i;
+        }
+        //die("Lexical error ".$this->currentCharacter);
+    }
+
+    /**
+     * Checks if it's a valid token.
+     *
+     * @param mixed $token the token to check
+     *
+     * @return mixed The checked token or false on failure
+     */
+    private function match($token)
+    {
+        switch ($token) {
+            case '+':
+            case '-':
+            case '*':
+            case '/':
+            case '(':
+            case ')':
+            case ',':
+            case ';':
+            case '>=':
+            case '<=':
+            case '=':
+            case '<>':
+            case '^':
+            case '&':
+            case '%':
+                return $token;
+
+                break;
+            case '>':
+                if ($this->lookAhead === '=') { // it's a GE token
+                    break;
+                }
+
+                return $token;
+
+                break;
+            case '<':
+                // it's a LE or a NE token
+                if (($this->lookAhead === '=') || ($this->lookAhead === '>')) {
+                    break;
+                }
+
+                return $token;
+
+                break;
+            default:
+                // if it's a reference A1 or $A$1 or $A1 or A$1
+                if (preg_match('/^\$?[A-Ia-i]?[A-Za-z]\$?\d+$/', $token) && !preg_match('/\d/', $this->lookAhead) && ($this->lookAhead !== ':') && ($this->lookAhead !== '.') && ($this->lookAhead !== '!')) {
+                    return $token;
+                } elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?[A-Ia-i]?[A-Za-z]\$?\\d+$/u', $token) && !preg_match('/\d/', $this->lookAhead) && ($this->lookAhead !== ':') && ($this->lookAhead !== '.')) {
+                    // If it's an external reference (Sheet1!A1 or Sheet1:Sheet2!A1 or Sheet1!$A$1 or Sheet1:Sheet2!$A$1)
+                    return $token;
+                } elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?[A-Ia-i]?[A-Za-z]\\$?\\d+$/u", $token) && !preg_match('/\d/', $this->lookAhead) && ($this->lookAhead !== ':') && ($this->lookAhead !== '.')) {
+                    // If it's an external reference ('Sheet1'!A1 or 'Sheet1:Sheet2'!A1 or 'Sheet1'!$A$1 or 'Sheet1:Sheet2'!$A$1)
+                    return $token;
+                } elseif (preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+:(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+$/', $token) && !preg_match('/\d/', $this->lookAhead)) {
+                    // if it's a range A1:A2 or $A$1:$A$2
+                    return $token;
+                } elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?([A-Ia-i]?[A-Za-z])?\$?\\d+:\$?([A-Ia-i]?[A-Za-z])?\$?\\d+$/u', $token) && !preg_match('/\d/', $this->lookAhead)) {
+                    // If it's an external range like Sheet1!A1:B2 or Sheet1:Sheet2!A1:B2 or Sheet1!$A$1:$B$2 or Sheet1:Sheet2!$A$1:$B$2
+                    return $token;
+                } elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+:\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+$/u", $token) && !preg_match('/\d/', $this->lookAhead)) {
+                    // If it's an external range like 'Sheet1'!A1:B2 or 'Sheet1:Sheet2'!A1:B2 or 'Sheet1'!$A$1:$B$2 or 'Sheet1:Sheet2'!$A$1:$B$2
+                    return $token;
+                } elseif (is_numeric($token) && (!is_numeric($token . $this->lookAhead) || ($this->lookAhead == '')) && ($this->lookAhead !== '!') && ($this->lookAhead !== ':')) {
+                    // If it's a number (check that it's not a sheet name or range)
+                    return $token;
+                } elseif (preg_match('/"([^"]|""){0,255}"/', $token) && $this->lookAhead !== '"' && (substr_count($token, '"') % 2 == 0)) {
+                    // If it's a string (of maximum 255 characters)
+                    return $token;
+                } elseif (preg_match('/^#[A-Z0\\/]{3,5}[!?]{1}$/', $token) || $token === '#N/A') {
+                    // If it's an error code
+                    return $token;
+                } elseif (preg_match("/^[A-Z0-9\xc0-\xdc\\.]+$/i", $token) && ($this->lookAhead === '(')) {
+                    // if it's a function call
+                    return $token;
+                } elseif (substr($token, -1) === ')') {
+                    //    It's an argument of some description (e.g. a named range),
+                    //        precise nature yet to be determined
+                    return $token;
+                }
+
+                return '';
+        }
+    }
+
+    /**
+     * The parsing method. It parses a formula.
+     *
+     * @param string $formula the formula to parse, without the initial equal
+     *                        sign (=)
+     *
+     * @return mixed true on success
+     */
+    public function parse($formula)
+    {
+        $this->currentCharacter = 0;
+        $this->formula = (string) $formula;
+        $this->lookAhead = $formula[1] ?? '';
+        $this->advance();
+        $this->parseTree = $this->condition();
+
+        return true;
+    }
+
+    /**
+     * It parses a condition. It assumes the following rule:
+     * Cond -> Expr [(">" | "<") Expr].
+     *
+     * @return mixed The parsed ptg'd tree on success
+     */
+    private function condition()
+    {
+        $result = $this->expression();
+        if ($this->currentToken == '<') {
+            $this->advance();
+            $result2 = $this->expression();
+            $result = $this->createTree('ptgLT', $result, $result2);
+        } elseif ($this->currentToken == '>') {
+            $this->advance();
+            $result2 = $this->expression();
+            $result = $this->createTree('ptgGT', $result, $result2);
+        } elseif ($this->currentToken == '<=') {
+            $this->advance();
+            $result2 = $this->expression();
+            $result = $this->createTree('ptgLE', $result, $result2);
+        } elseif ($this->currentToken == '>=') {
+            $this->advance();
+            $result2 = $this->expression();
+            $result = $this->createTree('ptgGE', $result, $result2);
+        } elseif ($this->currentToken == '=') {
+            $this->advance();
+            $result2 = $this->expression();
+            $result = $this->createTree('ptgEQ', $result, $result2);
+        } elseif ($this->currentToken == '<>') {
+            $this->advance();
+            $result2 = $this->expression();
+            $result = $this->createTree('ptgNE', $result, $result2);
+        } elseif ($this->currentToken == '&') {
+            $this->advance();
+            $result2 = $this->expression();
+            $result = $this->createTree('ptgConcat', $result, $result2);
+        }
+
+        return $result;
+    }
+
+    /**
+     * It parses a expression. It assumes the following rule:
+     * Expr -> Term [("+" | "-") Term]
+     *      -> "string"
+     *      -> "-" Term : Negative value
+     *      -> "+" Term : Positive value
+     *      -> Error code.
+     *
+     * @return mixed The parsed ptg'd tree on success
+     */
+    private function expression()
+    {
+        // If it's a string return a string node
+        if (preg_match('/"([^"]|""){0,255}"/', $this->currentToken)) {
+            $tmp = str_replace('""', '"', $this->currentToken);
+            if (($tmp == '"') || ($tmp == '')) {
+                //    Trap for "" that has been used for an empty string
+                $tmp = '""';
+            }
+            $result = $this->createTree($tmp, '', '');
+            $this->advance();
+
+            return $result;
+        // If it's an error code
+        } elseif (preg_match('/^#[A-Z0\\/]{3,5}[!?]{1}$/', $this->currentToken) || $this->currentToken == '#N/A') {
+            $result = $this->createTree($this->currentToken, 'ptgErr', '');
+            $this->advance();
+
+            return $result;
+        // If it's a negative value
+        } elseif ($this->currentToken == '-') {
+            // catch "-" Term
+            $this->advance();
+            $result2 = $this->expression();
+
+            return $this->createTree('ptgUminus', $result2, '');
+        // If it's a positive value
+        } elseif ($this->currentToken == '+') {
+            // catch "+" Term
+            $this->advance();
+            $result2 = $this->expression();
+
+            return $this->createTree('ptgUplus', $result2, '');
+        }
+        $result = $this->term();
+        while (($this->currentToken == '+') ||
+               ($this->currentToken == '-') ||
+               ($this->currentToken == '^')) {
+            if ($this->currentToken == '+') {
+                $this->advance();
+                $result2 = $this->term();
+                $result = $this->createTree('ptgAdd', $result, $result2);
+            } elseif ($this->currentToken == '-') {
+                $this->advance();
+                $result2 = $this->term();
+                $result = $this->createTree('ptgSub', $result, $result2);
+            } else {
+                $this->advance();
+                $result2 = $this->term();
+                $result = $this->createTree('ptgPower', $result, $result2);
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * This function just introduces a ptgParen element in the tree, so that Excel
+     * doesn't get confused when working with a parenthesized formula afterwards.
+     *
+     * @see fact()
+     *
+     * @return array The parsed ptg'd tree
+     */
+    private function parenthesizedExpression()
+    {
+        return $this->createTree('ptgParen', $this->expression(), '');
+    }
+
+    /**
+     * It parses a term. It assumes the following rule:
+     * Term -> Fact [("*" | "/") Fact].
+     *
+     * @return mixed The parsed ptg'd tree on success
+     */
+    private function term()
+    {
+        $result = $this->fact();
+        while (($this->currentToken == '*') ||
+               ($this->currentToken == '/')) {
+            if ($this->currentToken == '*') {
+                $this->advance();
+                $result2 = $this->fact();
+                $result = $this->createTree('ptgMul', $result, $result2);
+            } else {
+                $this->advance();
+                $result2 = $this->fact();
+                $result = $this->createTree('ptgDiv', $result, $result2);
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * It parses a factor. It assumes the following rule:
+     * Fact -> ( Expr )
+     *       | CellRef
+     *       | CellRange
+     *       | Number
+     *       | Function.
+     *
+     * @return mixed The parsed ptg'd tree on success
+     */
+    private function fact()
+    {
+        if ($this->currentToken === '(') {
+            $this->advance(); // eat the "("
+            $result = $this->parenthesizedExpression();
+            if ($this->currentToken !== ')') {
+                throw new WriterException("')' token expected.");
+            }
+            $this->advance(); // eat the ")"
+            return $result;
+        }
+        // if it's a reference
+        if (preg_match('/^\$?[A-Ia-i]?[A-Za-z]\$?\d+$/', $this->currentToken)) {
+            $result = $this->createTree($this->currentToken, '', '');
+            $this->advance();
+
+            return $result;
+        } elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?[A-Ia-i]?[A-Za-z]\$?\\d+$/u', $this->currentToken)) {
+            // If it's an external reference (Sheet1!A1 or Sheet1:Sheet2!A1 or Sheet1!$A$1 or Sheet1:Sheet2!$A$1)
+            $result = $this->createTree($this->currentToken, '', '');
+            $this->advance();
+
+            return $result;
+        } elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?[A-Ia-i]?[A-Za-z]\\$?\\d+$/u", $this->currentToken)) {
+            // If it's an external reference ('Sheet1'!A1 or 'Sheet1:Sheet2'!A1 or 'Sheet1'!$A$1 or 'Sheet1:Sheet2'!$A$1)
+            $result = $this->createTree($this->currentToken, '', '');
+            $this->advance();
+
+            return $result;
+        } elseif (preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+:(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+$/', $this->currentToken) ||
+                preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+\.\.(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+$/', $this->currentToken)) {
+            // if it's a range A1:B2 or $A$1:$B$2
+            // must be an error?
+            $result = $this->createTree($this->currentToken, '', '');
+            $this->advance();
+
+            return $result;
+        } elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?([A-Ia-i]?[A-Za-z])?\$?\\d+:\$?([A-Ia-i]?[A-Za-z])?\$?\\d+$/u', $this->currentToken)) {
+            // If it's an external range (Sheet1!A1:B2 or Sheet1:Sheet2!A1:B2 or Sheet1!$A$1:$B$2 or Sheet1:Sheet2!$A$1:$B$2)
+            // must be an error?
+            $result = $this->createTree($this->currentToken, '', '');
+            $this->advance();
+
+            return $result;
+        } elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+:\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+$/u", $this->currentToken)) {
+            // If it's an external range ('Sheet1'!A1:B2 or 'Sheet1'!A1:B2 or 'Sheet1'!$A$1:$B$2 or 'Sheet1'!$A$1:$B$2)
+            // must be an error?
+            $result = $this->createTree($this->currentToken, '', '');
+            $this->advance();
+
+            return $result;
+        } elseif (is_numeric($this->currentToken)) {
+            // If it's a number or a percent
+            if ($this->lookAhead === '%') {
+                $result = $this->createTree('ptgPercent', $this->currentToken, '');
+                $this->advance(); // Skip the percentage operator once we've pre-built that tree
+            } else {
+                $result = $this->createTree($this->currentToken, '', '');
+            }
+            $this->advance();
+
+            return $result;
+        } elseif (preg_match("/^[A-Z0-9\xc0-\xdc\\.]+$/i", $this->currentToken)) {
+            // if it's a function call
+            return $this->func();
+        }
+
+        throw new WriterException('Syntax error: ' . $this->currentToken . ', lookahead: ' . $this->lookAhead . ', current char: ' . $this->currentCharacter);
+    }
+
+    /**
+     * It parses a function call. It assumes the following rule:
+     * Func -> ( Expr [,Expr]* ).
+     *
+     * @return mixed The parsed ptg'd tree on success
+     */
+    private function func()
+    {
+        $num_args = 0; // number of arguments received
+        $function = strtoupper($this->currentToken);
+        $result = ''; // initialize result
+        $this->advance();
+        $this->advance(); // eat the "("
+        while ($this->currentToken !== ')') {
+            if ($num_args > 0) {
+                if ($this->currentToken === ',' || $this->currentToken === ';') {
+                    $this->advance(); // eat the "," or ";"
+                } else {
+                    throw new WriterException("Syntax error: comma expected in function $function, arg #{$num_args}");
+                }
+                $result2 = $this->condition();
+                $result = $this->createTree('arg', $result, $result2);
+            } else { // first argument
+                $result2 = $this->condition();
+                $result = $this->createTree('arg', '', $result2);
+            }
+            ++$num_args;
+        }
+        if (!isset($this->functions[$function])) {
+            throw new WriterException("Function $function() doesn't exist");
+        }
+        $args = $this->functions[$function][1];
+        // If fixed number of args eg. TIME($i, $j, $k). Check that the number of args is valid.
+        if (($args >= 0) && ($args != $num_args)) {
+            throw new WriterException("Incorrect number of arguments in function $function() ");
+        }
+
+        $result = $this->createTree($function, $result, $num_args);
+        $this->advance(); // eat the ")"
+        return $result;
+    }
+
+    /**
+     * Creates a tree. In fact an array which may have one or two arrays (sub-trees)
+     * as elements.
+     *
+     * @param mixed $value the value of this node
+     * @param mixed $left the left array (sub-tree) or a final node
+     * @param mixed $right the right array (sub-tree) or a final node
+     *
+     * @return array A tree
+     */
+    private function createTree($value, $left, $right)
+    {
+        return ['value' => $value, 'left' => $left, 'right' => $right];
+    }
+
+    /**
+     * Builds a string containing the tree in reverse polish notation (What you
+     * would use in a HP calculator stack).
+     * The following tree:.
+     *
+     *    +
+     *   / \
+     *  2   3
+     *
+     * produces: "23+"
+     *
+     * The following tree:
+     *
+     *    +
+     *   / \
+     *  3   *
+     *     / \
+     *    6   A1
+     *
+     * produces: "36A1*+"
+     *
+     * In fact all operands, functions, references, etc... are written as ptg's
+     *
+     * @param array $tree the optional tree to convert
+     *
+     * @return string The tree in reverse polish notation
+     */
+    public function toReversePolish($tree = [])
+    {
+        $polish = ''; // the string we are going to return
+        if (empty($tree)) { // If it's the first call use parseTree
+            $tree = $this->parseTree;
+        }
+
+        if (is_array($tree['left'])) {
+            $converted_tree = $this->toReversePolish($tree['left']);
+            $polish .= $converted_tree;
+        } elseif ($tree['left'] != '') { // It's a final node
+            $converted_tree = $this->convert($tree['left']);
+            $polish .= $converted_tree;
+        }
+        if (is_array($tree['right'])) {
+            $converted_tree = $this->toReversePolish($tree['right']);
+            $polish .= $converted_tree;
+        } elseif ($tree['right'] != '') { // It's a final node
+            $converted_tree = $this->convert($tree['right']);
+            $polish .= $converted_tree;
+        }
+        // if it's a function convert it here (so we can set it's arguments)
+        if (preg_match("/^[A-Z0-9\xc0-\xdc\\.]+$/", $tree['value']) &&
+            !preg_match('/^([A-Ia-i]?[A-Za-z])(\d+)$/', $tree['value']) &&
+            !preg_match('/^[A-Ia-i]?[A-Za-z](\\d+)\\.\\.[A-Ia-i]?[A-Za-z](\\d+)$/', $tree['value']) &&
+            !is_numeric($tree['value']) &&
+            !isset($this->ptg[$tree['value']])) {
+            // left subtree for a function is always an array.
+            if ($tree['left'] != '') {
+                $left_tree = $this->toReversePolish($tree['left']);
+            } else {
+                $left_tree = '';
+            }
+            // add it's left subtree and return.
+            return $left_tree . $this->convertFunction($tree['value'], $tree['right']);
+        }
+        $converted_tree = $this->convert($tree['value']);
+
+        return $polish . $converted_tree;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Workbook.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Workbook.php
new file mode 100755
index 0000000..d5f61bf
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Workbook.php
@@ -0,0 +1,1147 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Xls;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+use PhpOffice\PhpSpreadsheet\Shared\Date;
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Style\Style;
+
+// Original file header of PEAR::Spreadsheet_Excel_Writer_Workbook (used as the base for this class):
+// -----------------------------------------------------------------------------------------
+// /*
+// *  Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
+// *
+// *  The majority of this is _NOT_ my code.  I simply ported it from the
+// *  PERL Spreadsheet::WriteExcel module.
+// *
+// *  The author of the Spreadsheet::WriteExcel module is John McNamara
+// *  <jmcnamara@cpan.org>
+// *
+// *  I _DO_ maintain this code, and John McNamara has nothing to do with the
+// *  porting of this code to PHP.  Any questions directly related to this
+// *  class library should be directed to me.
+// *
+// *  License Information:
+// *
+// *    Spreadsheet_Excel_Writer:  A library for generating Excel Spreadsheets
+// *    Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com
+// *
+// *    This library is free software; you can redistribute it and/or
+// *    modify it under the terms of the GNU Lesser General Public
+// *    License as published by the Free Software Foundation; either
+// *    version 2.1 of the License, or (at your option) any later version.
+// *
+// *    This library is distributed in the hope that it will be useful,
+// *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+// *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// *    Lesser General Public License for more details.
+// *
+// *    You should have received a copy of the GNU Lesser General Public
+// *    License along with this library; if not, write to the Free Software
+// *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+// */
+class Workbook extends BIFFwriter
+{
+    /**
+     * Formula parser.
+     *
+     * @var \PhpOffice\PhpSpreadsheet\Writer\Xls\Parser
+     */
+    private $parser;
+
+    /**
+     * The BIFF file size for the workbook.
+     *
+     * @var int
+     *
+     * @see calcSheetOffsets()
+     */
+    private $biffSize;
+
+    /**
+     * XF Writers.
+     *
+     * @var \PhpOffice\PhpSpreadsheet\Writer\Xls\Xf[]
+     */
+    private $xfWriters = [];
+
+    /**
+     * Array containing the colour palette.
+     *
+     * @var array
+     */
+    private $palette;
+
+    /**
+     * The codepage indicates the text encoding used for strings.
+     *
+     * @var int
+     */
+    private $codepage;
+
+    /**
+     * The country code used for localization.
+     *
+     * @var int
+     */
+    private $countryCode;
+
+    /**
+     * Workbook.
+     *
+     * @var Spreadsheet
+     */
+    private $spreadsheet;
+
+    /**
+     * Fonts writers.
+     *
+     * @var Font[]
+     */
+    private $fontWriters = [];
+
+    /**
+     * Added fonts. Maps from font's hash => index in workbook.
+     *
+     * @var array
+     */
+    private $addedFonts = [];
+
+    /**
+     * Shared number formats.
+     *
+     * @var array
+     */
+    private $numberFormats = [];
+
+    /**
+     * Added number formats. Maps from numberFormat's hash => index in workbook.
+     *
+     * @var array
+     */
+    private $addedNumberFormats = [];
+
+    /**
+     * Sizes of the binary worksheet streams.
+     *
+     * @var array
+     */
+    private $worksheetSizes = [];
+
+    /**
+     * Offsets of the binary worksheet streams relative to the start of the global workbook stream.
+     *
+     * @var array
+     */
+    private $worksheetOffsets = [];
+
+    /**
+     * Total number of shared strings in workbook.
+     *
+     * @var int
+     */
+    private $stringTotal;
+
+    /**
+     * Number of unique shared strings in workbook.
+     *
+     * @var int
+     */
+    private $stringUnique;
+
+    /**
+     * Array of unique shared strings in workbook.
+     *
+     * @var array
+     */
+    private $stringTable;
+
+    /**
+     * Color cache.
+     */
+    private $colors;
+
+    /**
+     * Escher object corresponding to MSODRAWINGGROUP.
+     *
+     * @var \PhpOffice\PhpSpreadsheet\Shared\Escher
+     */
+    private $escher;
+
+    /**
+     * Class constructor.
+     *
+     * @param Spreadsheet $spreadsheet The Workbook
+     * @param int $str_total Total number of strings
+     * @param int $str_unique Total number of unique strings
+     * @param array $str_table String Table
+     * @param array $colors Colour Table
+     * @param Parser $parser The formula parser created for the Workbook
+     */
+    public function __construct(Spreadsheet $spreadsheet, &$str_total, &$str_unique, &$str_table, &$colors, Parser $parser)
+    {
+        // It needs to call its parent's constructor explicitly
+        parent::__construct();
+
+        $this->parser = $parser;
+        $this->biffSize = 0;
+        $this->palette = [];
+        $this->countryCode = -1;
+
+        $this->stringTotal = &$str_total;
+        $this->stringUnique = &$str_unique;
+        $this->stringTable = &$str_table;
+        $this->colors = &$colors;
+        $this->setPaletteXl97();
+
+        $this->spreadsheet = $spreadsheet;
+
+        $this->codepage = 0x04B0;
+
+        // Add empty sheets and Build color cache
+        $countSheets = $spreadsheet->getSheetCount();
+        for ($i = 0; $i < $countSheets; ++$i) {
+            $phpSheet = $spreadsheet->getSheet($i);
+
+            $this->parser->setExtSheet($phpSheet->getTitle(), $i); // Register worksheet name with parser
+
+            $supbook_index = 0x00;
+            $ref = pack('vvv', $supbook_index, $i, $i);
+            $this->parser->references[] = $ref; // Register reference with parser
+
+            // Sheet tab colors?
+            if ($phpSheet->isTabColorSet()) {
+                $this->addColor($phpSheet->getTabColor()->getRGB());
+            }
+        }
+    }
+
+    /**
+     * Add a new XF writer.
+     *
+     * @param bool $isStyleXf Is it a style XF?
+     *
+     * @return int Index to XF record
+     */
+    public function addXfWriter(Style $style, $isStyleXf = false)
+    {
+        $xfWriter = new Xf($style);
+        $xfWriter->setIsStyleXf($isStyleXf);
+
+        // Add the font if not already added
+        $fontIndex = $this->addFont($style->getFont());
+
+        // Assign the font index to the xf record
+        $xfWriter->setFontIndex($fontIndex);
+
+        // Background colors, best to treat these after the font so black will come after white in custom palette
+        $xfWriter->setFgColor($this->addColor($style->getFill()->getStartColor()->getRGB()));
+        $xfWriter->setBgColor($this->addColor($style->getFill()->getEndColor()->getRGB()));
+        $xfWriter->setBottomColor($this->addColor($style->getBorders()->getBottom()->getColor()->getRGB()));
+        $xfWriter->setTopColor($this->addColor($style->getBorders()->getTop()->getColor()->getRGB()));
+        $xfWriter->setRightColor($this->addColor($style->getBorders()->getRight()->getColor()->getRGB()));
+        $xfWriter->setLeftColor($this->addColor($style->getBorders()->getLeft()->getColor()->getRGB()));
+        $xfWriter->setDiagColor($this->addColor($style->getBorders()->getDiagonal()->getColor()->getRGB()));
+
+        // Add the number format if it is not a built-in one and not already added
+        if ($style->getNumberFormat()->getBuiltInFormatCode() === false) {
+            $numberFormatHashCode = $style->getNumberFormat()->getHashCode();
+
+            if (isset($this->addedNumberFormats[$numberFormatHashCode])) {
+                $numberFormatIndex = $this->addedNumberFormats[$numberFormatHashCode];
+            } else {
+                $numberFormatIndex = 164 + count($this->numberFormats);
+                $this->numberFormats[$numberFormatIndex] = $style->getNumberFormat();
+                $this->addedNumberFormats[$numberFormatHashCode] = $numberFormatIndex;
+            }
+        } else {
+            $numberFormatIndex = (int) $style->getNumberFormat()->getBuiltInFormatCode();
+        }
+
+        // Assign the number format index to xf record
+        $xfWriter->setNumberFormatIndex($numberFormatIndex);
+
+        $this->xfWriters[] = $xfWriter;
+
+        return count($this->xfWriters) - 1;
+    }
+
+    /**
+     * Add a font to added fonts.
+     *
+     * @return int Index to FONT record
+     */
+    public function addFont(\PhpOffice\PhpSpreadsheet\Style\Font $font)
+    {
+        $fontHashCode = $font->getHashCode();
+        if (isset($this->addedFonts[$fontHashCode])) {
+            $fontIndex = $this->addedFonts[$fontHashCode];
+        } else {
+            $countFonts = count($this->fontWriters);
+            $fontIndex = ($countFonts < 4) ? $countFonts : $countFonts + 1;
+
+            $fontWriter = new Font($font);
+            $fontWriter->setColorIndex($this->addColor($font->getColor()->getRGB()));
+            $this->fontWriters[] = $fontWriter;
+
+            $this->addedFonts[$fontHashCode] = $fontIndex;
+        }
+
+        return $fontIndex;
+    }
+
+    /**
+     * Alter color palette adding a custom color.
+     *
+     * @param string $rgb E.g. 'FF00AA'
+     *
+     * @return int Color index
+     */
+    private function addColor($rgb)
+    {
+        if (!isset($this->colors[$rgb])) {
+            $color =
+                [
+                    hexdec(substr($rgb, 0, 2)),
+                    hexdec(substr($rgb, 2, 2)),
+                    hexdec(substr($rgb, 4)),
+                    0,
+                ];
+            $colorIndex = array_search($color, $this->palette);
+            if ($colorIndex) {
+                $this->colors[$rgb] = $colorIndex;
+            } else {
+                if (count($this->colors) === 0) {
+                    $lastColor = 7;
+                } else {
+                    $lastColor = end($this->colors);
+                }
+                if ($lastColor < 57) {
+                    // then we add a custom color altering the palette
+                    $colorIndex = $lastColor + 1;
+                    $this->palette[$colorIndex] = $color;
+                    $this->colors[$rgb] = $colorIndex;
+                } else {
+                    // no room for more custom colors, just map to black
+                    $colorIndex = 0;
+                }
+            }
+        } else {
+            // fetch already added custom color
+            $colorIndex = $this->colors[$rgb];
+        }
+
+        return $colorIndex;
+    }
+
+    /**
+     * Sets the colour palette to the Excel 97+ default.
+     */
+    private function setPaletteXl97(): void
+    {
+        $this->palette = [
+            0x08 => [0x00, 0x00, 0x00, 0x00],
+            0x09 => [0xff, 0xff, 0xff, 0x00],
+            0x0A => [0xff, 0x00, 0x00, 0x00],
+            0x0B => [0x00, 0xff, 0x00, 0x00],
+            0x0C => [0x00, 0x00, 0xff, 0x00],
+            0x0D => [0xff, 0xff, 0x00, 0x00],
+            0x0E => [0xff, 0x00, 0xff, 0x00],
+            0x0F => [0x00, 0xff, 0xff, 0x00],
+            0x10 => [0x80, 0x00, 0x00, 0x00],
+            0x11 => [0x00, 0x80, 0x00, 0x00],
+            0x12 => [0x00, 0x00, 0x80, 0x00],
+            0x13 => [0x80, 0x80, 0x00, 0x00],
+            0x14 => [0x80, 0x00, 0x80, 0x00],
+            0x15 => [0x00, 0x80, 0x80, 0x00],
+            0x16 => [0xc0, 0xc0, 0xc0, 0x00],
+            0x17 => [0x80, 0x80, 0x80, 0x00],
+            0x18 => [0x99, 0x99, 0xff, 0x00],
+            0x19 => [0x99, 0x33, 0x66, 0x00],
+            0x1A => [0xff, 0xff, 0xcc, 0x00],
+            0x1B => [0xcc, 0xff, 0xff, 0x00],
+            0x1C => [0x66, 0x00, 0x66, 0x00],
+            0x1D => [0xff, 0x80, 0x80, 0x00],
+            0x1E => [0x00, 0x66, 0xcc, 0x00],
+            0x1F => [0xcc, 0xcc, 0xff, 0x00],
+            0x20 => [0x00, 0x00, 0x80, 0x00],
+            0x21 => [0xff, 0x00, 0xff, 0x00],
+            0x22 => [0xff, 0xff, 0x00, 0x00],
+            0x23 => [0x00, 0xff, 0xff, 0x00],
+            0x24 => [0x80, 0x00, 0x80, 0x00],
+            0x25 => [0x80, 0x00, 0x00, 0x00],
+            0x26 => [0x00, 0x80, 0x80, 0x00],
+            0x27 => [0x00, 0x00, 0xff, 0x00],
+            0x28 => [0x00, 0xcc, 0xff, 0x00],
+            0x29 => [0xcc, 0xff, 0xff, 0x00],
+            0x2A => [0xcc, 0xff, 0xcc, 0x00],
+            0x2B => [0xff, 0xff, 0x99, 0x00],
+            0x2C => [0x99, 0xcc, 0xff, 0x00],
+            0x2D => [0xff, 0x99, 0xcc, 0x00],
+            0x2E => [0xcc, 0x99, 0xff, 0x00],
+            0x2F => [0xff, 0xcc, 0x99, 0x00],
+            0x30 => [0x33, 0x66, 0xff, 0x00],
+            0x31 => [0x33, 0xcc, 0xcc, 0x00],
+            0x32 => [0x99, 0xcc, 0x00, 0x00],
+            0x33 => [0xff, 0xcc, 0x00, 0x00],
+            0x34 => [0xff, 0x99, 0x00, 0x00],
+            0x35 => [0xff, 0x66, 0x00, 0x00],
+            0x36 => [0x66, 0x66, 0x99, 0x00],
+            0x37 => [0x96, 0x96, 0x96, 0x00],
+            0x38 => [0x00, 0x33, 0x66, 0x00],
+            0x39 => [0x33, 0x99, 0x66, 0x00],
+            0x3A => [0x00, 0x33, 0x00, 0x00],
+            0x3B => [0x33, 0x33, 0x00, 0x00],
+            0x3C => [0x99, 0x33, 0x00, 0x00],
+            0x3D => [0x99, 0x33, 0x66, 0x00],
+            0x3E => [0x33, 0x33, 0x99, 0x00],
+            0x3F => [0x33, 0x33, 0x33, 0x00],
+        ];
+    }
+
+    /**
+     * Assemble worksheets into a workbook and send the BIFF data to an OLE
+     * storage.
+     *
+     * @param array $pWorksheetSizes The sizes in bytes of the binary worksheet streams
+     *
+     * @return string Binary data for workbook stream
+     */
+    public function writeWorkbook(array $pWorksheetSizes)
+    {
+        $this->worksheetSizes = $pWorksheetSizes;
+
+        // Calculate the number of selected worksheet tabs and call the finalization
+        // methods for each worksheet
+        $total_worksheets = $this->spreadsheet->getSheetCount();
+
+        // Add part 1 of the Workbook globals, what goes before the SHEET records
+        $this->storeBof(0x0005);
+        $this->writeCodepage();
+        $this->writeWindow1();
+
+        $this->writeDateMode();
+        $this->writeAllFonts();
+        $this->writeAllNumberFormats();
+        $this->writeAllXfs();
+        $this->writeAllStyles();
+        $this->writePalette();
+
+        // Prepare part 3 of the workbook global stream, what goes after the SHEET records
+        $part3 = '';
+        if ($this->countryCode !== -1) {
+            $part3 .= $this->writeCountry();
+        }
+        $part3 .= $this->writeRecalcId();
+
+        $part3 .= $this->writeSupbookInternal();
+        /* TODO: store external SUPBOOK records and XCT and CRN records
+        in case of external references for BIFF8 */
+        $part3 .= $this->writeExternalsheetBiff8();
+        $part3 .= $this->writeAllDefinedNamesBiff8();
+        $part3 .= $this->writeMsoDrawingGroup();
+        $part3 .= $this->writeSharedStringsTable();
+
+        $part3 .= $this->writeEof();
+
+        // Add part 2 of the Workbook globals, the SHEET records
+        $this->calcSheetOffsets();
+        for ($i = 0; $i < $total_worksheets; ++$i) {
+            $this->writeBoundSheet($this->spreadsheet->getSheet($i), $this->worksheetOffsets[$i]);
+        }
+
+        // Add part 3 of the Workbook globals
+        $this->_data .= $part3;
+
+        return $this->_data;
+    }
+
+    /**
+     * Calculate offsets for Worksheet BOF records.
+     */
+    private function calcSheetOffsets(): void
+    {
+        $boundsheet_length = 10; // fixed length for a BOUNDSHEET record
+
+        // size of Workbook globals part 1 + 3
+        $offset = $this->_datasize;
+
+        // add size of Workbook globals part 2, the length of the SHEET records
+        $total_worksheets = count($this->spreadsheet->getAllSheets());
+        foreach ($this->spreadsheet->getWorksheetIterator() as $sheet) {
+            $offset += $boundsheet_length + strlen(StringHelper::UTF8toBIFF8UnicodeShort($sheet->getTitle()));
+        }
+
+        // add the sizes of each of the Sheet substreams, respectively
+        for ($i = 0; $i < $total_worksheets; ++$i) {
+            $this->worksheetOffsets[$i] = $offset;
+            $offset += $this->worksheetSizes[$i];
+        }
+        $this->biffSize = $offset;
+    }
+
+    /**
+     * Store the Excel FONT records.
+     */
+    private function writeAllFonts(): void
+    {
+        foreach ($this->fontWriters as $fontWriter) {
+            $this->append($fontWriter->writeFont());
+        }
+    }
+
+    /**
+     * Store user defined numerical formats i.e. FORMAT records.
+     */
+    private function writeAllNumberFormats(): void
+    {
+        foreach ($this->numberFormats as $numberFormatIndex => $numberFormat) {
+            $this->writeNumberFormat($numberFormat->getFormatCode(), $numberFormatIndex);
+        }
+    }
+
+    /**
+     * Write all XF records.
+     */
+    private function writeAllXfs(): void
+    {
+        foreach ($this->xfWriters as $xfWriter) {
+            $this->append($xfWriter->writeXf());
+        }
+    }
+
+    /**
+     * Write all STYLE records.
+     */
+    private function writeAllStyles(): void
+    {
+        $this->writeStyle();
+    }
+
+    /**
+     * Writes all the DEFINEDNAME records (BIFF8).
+     * So far this is only used for repeating rows/columns (print titles) and print areas.
+     */
+    private function writeAllDefinedNamesBiff8()
+    {
+        $chunk = '';
+
+        // Named ranges
+        if (count($this->spreadsheet->getNamedRanges()) > 0) {
+            // Loop named ranges
+            $namedRanges = $this->spreadsheet->getNamedRanges();
+            foreach ($namedRanges as $namedRange) {
+                // Create absolute coordinate
+                $range = Coordinate::splitRange($namedRange->getRange());
+                $iMax = count($range);
+                for ($i = 0; $i < $iMax; ++$i) {
+                    $range[$i][0] = '\'' . str_replace("'", "''", $namedRange->getWorksheet()->getTitle()) . '\'!' . Coordinate::absoluteCoordinate($range[$i][0]);
+                    if (isset($range[$i][1])) {
+                        $range[$i][1] = Coordinate::absoluteCoordinate($range[$i][1]);
+                    }
+                }
+                $range = Coordinate::buildRange($range); // e.g. Sheet1!$A$1:$B$2
+
+                // parse formula
+                try {
+                    $error = $this->parser->parse($range);
+                    $formulaData = $this->parser->toReversePolish();
+
+                    // make sure tRef3d is of type tRef3dR (0x3A)
+                    if (isset($formulaData[0]) && ($formulaData[0] == "\x7A" || $formulaData[0] == "\x5A")) {
+                        $formulaData = "\x3A" . substr($formulaData, 1);
+                    }
+
+                    if ($namedRange->getLocalOnly()) {
+                        // local scope
+                        $scope = $this->spreadsheet->getIndex($namedRange->getScope()) + 1;
+                    } else {
+                        // global scope
+                        $scope = 0;
+                    }
+                    $chunk .= $this->writeData($this->writeDefinedNameBiff8($namedRange->getName(), $formulaData, $scope, false));
+                } catch (PhpSpreadsheetException $e) {
+                    // do nothing
+                }
+            }
+        }
+
+        // total number of sheets
+        $total_worksheets = $this->spreadsheet->getSheetCount();
+
+        // write the print titles (repeating rows, columns), if any
+        for ($i = 0; $i < $total_worksheets; ++$i) {
+            $sheetSetup = $this->spreadsheet->getSheet($i)->getPageSetup();
+            // simultaneous repeatColumns repeatRows
+            if ($sheetSetup->isColumnsToRepeatAtLeftSet() && $sheetSetup->isRowsToRepeatAtTopSet()) {
+                $repeat = $sheetSetup->getColumnsToRepeatAtLeft();
+                $colmin = Coordinate::columnIndexFromString($repeat[0]) - 1;
+                $colmax = Coordinate::columnIndexFromString($repeat[1]) - 1;
+
+                $repeat = $sheetSetup->getRowsToRepeatAtTop();
+                $rowmin = $repeat[0] - 1;
+                $rowmax = $repeat[1] - 1;
+
+                // construct formula data manually
+                $formulaData = pack('Cv', 0x29, 0x17); // tMemFunc
+                $formulaData .= pack('Cvvvvv', 0x3B, $i, 0, 65535, $colmin, $colmax); // tArea3d
+                $formulaData .= pack('Cvvvvv', 0x3B, $i, $rowmin, $rowmax, 0, 255); // tArea3d
+                $formulaData .= pack('C', 0x10); // tList
+
+                // store the DEFINEDNAME record
+                $chunk .= $this->writeData($this->writeDefinedNameBiff8(pack('C', 0x07), $formulaData, $i + 1, true));
+
+            // (exclusive) either repeatColumns or repeatRows
+            } elseif ($sheetSetup->isColumnsToRepeatAtLeftSet() || $sheetSetup->isRowsToRepeatAtTopSet()) {
+                // Columns to repeat
+                if ($sheetSetup->isColumnsToRepeatAtLeftSet()) {
+                    $repeat = $sheetSetup->getColumnsToRepeatAtLeft();
+                    $colmin = Coordinate::columnIndexFromString($repeat[0]) - 1;
+                    $colmax = Coordinate::columnIndexFromString($repeat[1]) - 1;
+                } else {
+                    $colmin = 0;
+                    $colmax = 255;
+                }
+                // Rows to repeat
+                if ($sheetSetup->isRowsToRepeatAtTopSet()) {
+                    $repeat = $sheetSetup->getRowsToRepeatAtTop();
+                    $rowmin = $repeat[0] - 1;
+                    $rowmax = $repeat[1] - 1;
+                } else {
+                    $rowmin = 0;
+                    $rowmax = 65535;
+                }
+
+                // construct formula data manually because parser does not recognize absolute 3d cell references
+                $formulaData = pack('Cvvvvv', 0x3B, $i, $rowmin, $rowmax, $colmin, $colmax);
+
+                // store the DEFINEDNAME record
+                $chunk .= $this->writeData($this->writeDefinedNameBiff8(pack('C', 0x07), $formulaData, $i + 1, true));
+            }
+        }
+
+        // write the print areas, if any
+        for ($i = 0; $i < $total_worksheets; ++$i) {
+            $sheetSetup = $this->spreadsheet->getSheet($i)->getPageSetup();
+            if ($sheetSetup->isPrintAreaSet()) {
+                // Print area, e.g. A3:J6,H1:X20
+                $printArea = Coordinate::splitRange($sheetSetup->getPrintArea());
+                $countPrintArea = count($printArea);
+
+                $formulaData = '';
+                for ($j = 0; $j < $countPrintArea; ++$j) {
+                    $printAreaRect = $printArea[$j]; // e.g. A3:J6
+                    $printAreaRect[0] = Coordinate::coordinateFromString($printAreaRect[0]);
+                    $printAreaRect[1] = Coordinate::coordinateFromString($printAreaRect[1]);
+
+                    $print_rowmin = $printAreaRect[0][1] - 1;
+                    $print_rowmax = $printAreaRect[1][1] - 1;
+                    $print_colmin = Coordinate::columnIndexFromString($printAreaRect[0][0]) - 1;
+                    $print_colmax = Coordinate::columnIndexFromString($printAreaRect[1][0]) - 1;
+
+                    // construct formula data manually because parser does not recognize absolute 3d cell references
+                    $formulaData .= pack('Cvvvvv', 0x3B, $i, $print_rowmin, $print_rowmax, $print_colmin, $print_colmax);
+
+                    if ($j > 0) {
+                        $formulaData .= pack('C', 0x10); // list operator token ','
+                    }
+                }
+
+                // store the DEFINEDNAME record
+                $chunk .= $this->writeData($this->writeDefinedNameBiff8(pack('C', 0x06), $formulaData, $i + 1, true));
+            }
+        }
+
+        // write autofilters, if any
+        for ($i = 0; $i < $total_worksheets; ++$i) {
+            $sheetAutoFilter = $this->spreadsheet->getSheet($i)->getAutoFilter();
+            $autoFilterRange = $sheetAutoFilter->getRange();
+            if (!empty($autoFilterRange)) {
+                $rangeBounds = Coordinate::rangeBoundaries($autoFilterRange);
+
+                //Autofilter built in name
+                $name = pack('C', 0x0D);
+
+                $chunk .= $this->writeData($this->writeShortNameBiff8($name, $i + 1, $rangeBounds, true));
+            }
+        }
+
+        return $chunk;
+    }
+
+    /**
+     * Write a DEFINEDNAME record for BIFF8 using explicit binary formula data.
+     *
+     * @param string $name The name in UTF-8
+     * @param string $formulaData The binary formula data
+     * @param int $sheetIndex 1-based sheet index the defined name applies to. 0 = global
+     * @param bool $isBuiltIn Built-in name?
+     *
+     * @return string Complete binary record data
+     */
+    private function writeDefinedNameBiff8($name, $formulaData, $sheetIndex = 0, $isBuiltIn = false)
+    {
+        $record = 0x0018;
+
+        // option flags
+        $options = $isBuiltIn ? 0x20 : 0x00;
+
+        // length of the name, character count
+        $nlen = StringHelper::countCharacters($name);
+
+        // name with stripped length field
+        $name = substr(StringHelper::UTF8toBIFF8UnicodeLong($name), 2);
+
+        // size of the formula (in bytes)
+        $sz = strlen($formulaData);
+
+        // combine the parts
+        $data = pack('vCCvvvCCCC', $options, 0, $nlen, $sz, 0, $sheetIndex, 0, 0, 0, 0)
+            . $name . $formulaData;
+        $length = strlen($data);
+
+        $header = pack('vv', $record, $length);
+
+        return $header . $data;
+    }
+
+    /**
+     * Write a short NAME record.
+     *
+     * @param string $name
+     * @param string $sheetIndex 1-based sheet index the defined name applies to. 0 = global
+     * @param integer[][] $rangeBounds range boundaries
+     * @param bool $isHidden
+     *
+     * @return string Complete binary record data
+     * */
+    private function writeShortNameBiff8($name, $sheetIndex, $rangeBounds, $isHidden = false)
+    {
+        $record = 0x0018;
+
+        // option flags
+        $options = ($isHidden ? 0x21 : 0x00);
+
+        $extra = pack(
+            'Cvvvvv',
+            0x3B,
+            $sheetIndex - 1,
+            $rangeBounds[0][1] - 1,
+            $rangeBounds[1][1] - 1,
+            $rangeBounds[0][0] - 1,
+            $rangeBounds[1][0] - 1
+        );
+
+        // size of the formula (in bytes)
+        $sz = strlen($extra);
+
+        // combine the parts
+        $data = pack('vCCvvvCCCCC', $options, 0, 1, $sz, 0, $sheetIndex, 0, 0, 0, 0, 0)
+            . $name . $extra;
+        $length = strlen($data);
+
+        $header = pack('vv', $record, $length);
+
+        return $header . $data;
+    }
+
+    /**
+     * Stores the CODEPAGE biff record.
+     */
+    private function writeCodepage(): void
+    {
+        $record = 0x0042; // Record identifier
+        $length = 0x0002; // Number of bytes to follow
+        $cv = $this->codepage; // The code page
+
+        $header = pack('vv', $record, $length);
+        $data = pack('v', $cv);
+
+        $this->append($header . $data);
+    }
+
+    /**
+     * Write Excel BIFF WINDOW1 record.
+     */
+    private function writeWindow1(): void
+    {
+        $record = 0x003D; // Record identifier
+        $length = 0x0012; // Number of bytes to follow
+
+        $xWn = 0x0000; // Horizontal position of window
+        $yWn = 0x0000; // Vertical position of window
+        $dxWn = 0x25BC; // Width of window
+        $dyWn = 0x1572; // Height of window
+
+        $grbit = 0x0038; // Option flags
+
+        // not supported by PhpSpreadsheet, so there is only one selected sheet, the active
+        $ctabsel = 1; // Number of workbook tabs selected
+
+        $wTabRatio = 0x0258; // Tab to scrollbar ratio
+
+        // not supported by PhpSpreadsheet, set to 0
+        $itabFirst = 0; // 1st displayed worksheet
+        $itabCur = $this->spreadsheet->getActiveSheetIndex(); // Active worksheet
+
+        $header = pack('vv', $record, $length);
+        $data = pack('vvvvvvvvv', $xWn, $yWn, $dxWn, $dyWn, $grbit, $itabCur, $itabFirst, $ctabsel, $wTabRatio);
+        $this->append($header . $data);
+    }
+
+    /**
+     * Writes Excel BIFF BOUNDSHEET record.
+     *
+     * @param Worksheet $sheet Worksheet name
+     * @param int $offset Location of worksheet BOF
+     */
+    private function writeBoundSheet($sheet, $offset): void
+    {
+        $sheetname = $sheet->getTitle();
+        $record = 0x0085; // Record identifier
+
+        // sheet state
+        switch ($sheet->getSheetState()) {
+            case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::SHEETSTATE_VISIBLE:
+                $ss = 0x00;
+
+                break;
+            case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::SHEETSTATE_HIDDEN:
+                $ss = 0x01;
+
+                break;
+            case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::SHEETSTATE_VERYHIDDEN:
+                $ss = 0x02;
+
+                break;
+            default:
+                $ss = 0x00;
+
+                break;
+        }
+
+        // sheet type
+        $st = 0x00;
+
+        $grbit = 0x0000; // Visibility and sheet type
+
+        $data = pack('VCC', $offset, $ss, $st);
+        $data .= StringHelper::UTF8toBIFF8UnicodeShort($sheetname);
+
+        $length = strlen($data);
+        $header = pack('vv', $record, $length);
+        $this->append($header . $data);
+    }
+
+    /**
+     * Write Internal SUPBOOK record.
+     */
+    private function writeSupbookInternal()
+    {
+        $record = 0x01AE; // Record identifier
+        $length = 0x0004; // Bytes to follow
+
+        $header = pack('vv', $record, $length);
+        $data = pack('vv', $this->spreadsheet->getSheetCount(), 0x0401);
+
+        return $this->writeData($header . $data);
+    }
+
+    /**
+     * Writes the Excel BIFF EXTERNSHEET record. These references are used by
+     * formulas.
+     */
+    private function writeExternalsheetBiff8()
+    {
+        $totalReferences = count($this->parser->references);
+        $record = 0x0017; // Record identifier
+        $length = 2 + 6 * $totalReferences; // Number of bytes to follow
+
+        $supbook_index = 0; // FIXME: only using internal SUPBOOK record
+        $header = pack('vv', $record, $length);
+        $data = pack('v', $totalReferences);
+        for ($i = 0; $i < $totalReferences; ++$i) {
+            $data .= $this->parser->references[$i];
+        }
+
+        return $this->writeData($header . $data);
+    }
+
+    /**
+     * Write Excel BIFF STYLE records.
+     */
+    private function writeStyle(): void
+    {
+        $record = 0x0293; // Record identifier
+        $length = 0x0004; // Bytes to follow
+
+        $ixfe = 0x8000; // Index to cell style XF
+        $BuiltIn = 0x00; // Built-in style
+        $iLevel = 0xff; // Outline style level
+
+        $header = pack('vv', $record, $length);
+        $data = pack('vCC', $ixfe, $BuiltIn, $iLevel);
+        $this->append($header . $data);
+    }
+
+    /**
+     * Writes Excel FORMAT record for non "built-in" numerical formats.
+     *
+     * @param string $format Custom format string
+     * @param int $ifmt Format index code
+     */
+    private function writeNumberFormat($format, $ifmt): void
+    {
+        $record = 0x041E; // Record identifier
+
+        $numberFormatString = StringHelper::UTF8toBIFF8UnicodeLong($format);
+        $length = 2 + strlen($numberFormatString); // Number of bytes to follow
+
+        $header = pack('vv', $record, $length);
+        $data = pack('v', $ifmt) . $numberFormatString;
+        $this->append($header . $data);
+    }
+
+    /**
+     * Write DATEMODE record to indicate the date system in use (1904 or 1900).
+     */
+    private function writeDateMode(): void
+    {
+        $record = 0x0022; // Record identifier
+        $length = 0x0002; // Bytes to follow
+
+        $f1904 = (Date::getExcelCalendar() === Date::CALENDAR_MAC_1904)
+            ? 1
+            : 0; // Flag for 1904 date system
+
+        $header = pack('vv', $record, $length);
+        $data = pack('v', $f1904);
+        $this->append($header . $data);
+    }
+
+    /**
+     * Stores the COUNTRY record for localization.
+     *
+     * @return string
+     */
+    private function writeCountry()
+    {
+        $record = 0x008C; // Record identifier
+        $length = 4; // Number of bytes to follow
+
+        $header = pack('vv', $record, $length);
+        // using the same country code always for simplicity
+        $data = pack('vv', $this->countryCode, $this->countryCode);
+
+        return $this->writeData($header . $data);
+    }
+
+    /**
+     * Write the RECALCID record.
+     *
+     * @return string
+     */
+    private function writeRecalcId()
+    {
+        $record = 0x01C1; // Record identifier
+        $length = 8; // Number of bytes to follow
+
+        $header = pack('vv', $record, $length);
+
+        // by inspection of real Excel files, MS Office Excel 2007 writes this
+        $data = pack('VV', 0x000001C1, 0x00001E667);
+
+        return $this->writeData($header . $data);
+    }
+
+    /**
+     * Stores the PALETTE biff record.
+     */
+    private function writePalette(): void
+    {
+        $aref = $this->palette;
+
+        $record = 0x0092; // Record identifier
+        $length = 2 + 4 * count($aref); // Number of bytes to follow
+        $ccv = count($aref); // Number of RGB values to follow
+        $data = ''; // The RGB data
+
+        // Pack the RGB data
+        foreach ($aref as $color) {
+            foreach ($color as $byte) {
+                $data .= pack('C', $byte);
+            }
+        }
+
+        $header = pack('vvv', $record, $length, $ccv);
+        $this->append($header . $data);
+    }
+
+    /**
+     * Handling of the SST continue blocks is complicated by the need to include an
+     * additional continuation byte depending on whether the string is split between
+     * blocks or whether it starts at the beginning of the block. (There are also
+     * additional complications that will arise later when/if Rich Strings are
+     * supported).
+     *
+     * The Excel documentation says that the SST record should be followed by an
+     * EXTSST record. The EXTSST record is a hash table that is used to optimise
+     * access to SST. However, despite the documentation it doesn't seem to be
+     * required so we will ignore it.
+     *
+     * @return string Binary data
+     */
+    private function writeSharedStringsTable()
+    {
+        // maximum size of record data (excluding record header)
+        $continue_limit = 8224;
+
+        // initialize array of record data blocks
+        $recordDatas = [];
+
+        // start SST record data block with total number of strings, total number of unique strings
+        $recordData = pack('VV', $this->stringTotal, $this->stringUnique);
+
+        // loop through all (unique) strings in shared strings table
+        foreach (array_keys($this->stringTable) as $string) {
+            // here $string is a BIFF8 encoded string
+
+            // length = character count
+            $headerinfo = unpack('vlength/Cencoding', $string);
+
+            // currently, this is always 1 = uncompressed
+            $encoding = $headerinfo['encoding'];
+
+            // initialize finished writing current $string
+            $finished = false;
+
+            while ($finished === false) {
+                // normally, there will be only one cycle, but if string cannot immediately be written as is
+                // there will be need for more than one cylcle, if string longer than one record data block, there
+                // may be need for even more cycles
+
+                if (strlen($recordData) + strlen($string) <= $continue_limit) {
+                    // then we can write the string (or remainder of string) without any problems
+                    $recordData .= $string;
+
+                    if (strlen($recordData) + strlen($string) == $continue_limit) {
+                        // we close the record data block, and initialize a new one
+                        $recordDatas[] = $recordData;
+                        $recordData = '';
+                    }
+
+                    // we are finished writing this string
+                    $finished = true;
+                } else {
+                    // special treatment writing the string (or remainder of the string)
+                    // If the string is very long it may need to be written in more than one CONTINUE record.
+
+                    // check how many bytes more there is room for in the current record
+                    $space_remaining = $continue_limit - strlen($recordData);
+
+                    // minimum space needed
+                    // uncompressed: 2 byte string length length field + 1 byte option flags + 2 byte character
+                    // compressed:   2 byte string length length field + 1 byte option flags + 1 byte character
+                    $min_space_needed = ($encoding == 1) ? 5 : 4;
+
+                    // We have two cases
+                    // 1. space remaining is less than minimum space needed
+                    //        here we must waste the space remaining and move to next record data block
+                    // 2. space remaining is greater than or equal to minimum space needed
+                    //        here we write as much as we can in the current block, then move to next record data block
+
+                    // 1. space remaining is less than minimum space needed
+                    if ($space_remaining < $min_space_needed) {
+                        // we close the block, store the block data
+                        $recordDatas[] = $recordData;
+
+                        // and start new record data block where we start writing the string
+                        $recordData = '';
+
+                    // 2. space remaining is greater than or equal to minimum space needed
+                    } else {
+                        // initialize effective remaining space, for Unicode strings this may need to be reduced by 1, see below
+                        $effective_space_remaining = $space_remaining;
+
+                        // for uncompressed strings, sometimes effective space remaining is reduced by 1
+                        if ($encoding == 1 && (strlen($string) - $space_remaining) % 2 == 1) {
+                            --$effective_space_remaining;
+                        }
+
+                        // one block fininshed, store the block data
+                        $recordData .= substr($string, 0, $effective_space_remaining);
+
+                        $string = substr($string, $effective_space_remaining); // for next cycle in while loop
+                        $recordDatas[] = $recordData;
+
+                        // start new record data block with the repeated option flags
+                        $recordData = pack('C', $encoding);
+                    }
+                }
+            }
+        }
+
+        // Store the last record data block unless it is empty
+        // if there was no need for any continue records, this will be the for SST record data block itself
+        if (strlen($recordData) > 0) {
+            $recordDatas[] = $recordData;
+        }
+
+        // combine into one chunk with all the blocks SST, CONTINUE,...
+        $chunk = '';
+        foreach ($recordDatas as $i => $recordData) {
+            // first block should have the SST record header, remaing should have CONTINUE header
+            $record = ($i == 0) ? 0x00FC : 0x003C;
+
+            $header = pack('vv', $record, strlen($recordData));
+            $data = $header . $recordData;
+
+            $chunk .= $this->writeData($data);
+        }
+
+        return $chunk;
+    }
+
+    /**
+     * Writes the MSODRAWINGGROUP record if needed. Possibly split using CONTINUE records.
+     */
+    private function writeMsoDrawingGroup()
+    {
+        // write the Escher stream if necessary
+        if (isset($this->escher)) {
+            $writer = new Escher($this->escher);
+            $data = $writer->close();
+
+            $record = 0x00EB;
+            $length = strlen($data);
+            $header = pack('vv', $record, $length);
+
+            return $this->writeData($header . $data);
+        }
+
+        return '';
+    }
+
+    /**
+     * Get Escher object.
+     *
+     * @return \PhpOffice\PhpSpreadsheet\Shared\Escher
+     */
+    public function getEscher()
+    {
+        return $this->escher;
+    }
+
+    /**
+     * Set Escher object.
+     *
+     * @param \PhpOffice\PhpSpreadsheet\Shared\Escher $pValue
+     */
+    public function setEscher(?\PhpOffice\PhpSpreadsheet\Shared\Escher $pValue = null): void
+    {
+        $this->escher = $pValue;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Worksheet.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Worksheet.php
new file mode 100755
index 0000000..0dce7ba
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Worksheet.php
@@ -0,0 +1,4484 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Xls;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Cell\DataType;
+use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+use PhpOffice\PhpSpreadsheet\RichText\RichText;
+use PhpOffice\PhpSpreadsheet\RichText\Run;
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+use PhpOffice\PhpSpreadsheet\Shared\Xls;
+use PhpOffice\PhpSpreadsheet\Style\Alignment;
+use PhpOffice\PhpSpreadsheet\Style\Border;
+use PhpOffice\PhpSpreadsheet\Style\Color;
+use PhpOffice\PhpSpreadsheet\Style\Conditional;
+use PhpOffice\PhpSpreadsheet\Style\Fill;
+use PhpOffice\PhpSpreadsheet\Style\Protection;
+use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup;
+use PhpOffice\PhpSpreadsheet\Worksheet\SheetView;
+use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
+
+// Original file header of PEAR::Spreadsheet_Excel_Writer_Worksheet (used as the base for this class):
+// -----------------------------------------------------------------------------------------
+// /*
+// *  Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
+// *
+// *  The majority of this is _NOT_ my code.  I simply ported it from the
+// *  PERL Spreadsheet::WriteExcel module.
+// *
+// *  The author of the Spreadsheet::WriteExcel module is John McNamara
+// *  <jmcnamara@cpan.org>
+// *
+// *  I _DO_ maintain this code, and John McNamara has nothing to do with the
+// *  porting of this code to PHP.  Any questions directly related to this
+// *  class library should be directed to me.
+// *
+// *  License Information:
+// *
+// *    Spreadsheet_Excel_Writer:  A library for generating Excel Spreadsheets
+// *    Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com
+// *
+// *    This library is free software; you can redistribute it and/or
+// *    modify it under the terms of the GNU Lesser General Public
+// *    License as published by the Free Software Foundation; either
+// *    version 2.1 of the License, or (at your option) any later version.
+// *
+// *    This library is distributed in the hope that it will be useful,
+// *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+// *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// *    Lesser General Public License for more details.
+// *
+// *    You should have received a copy of the GNU Lesser General Public
+// *    License along with this library; if not, write to the Free Software
+// *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+// */
+class Worksheet extends BIFFwriter
+{
+    /**
+     * Formula parser.
+     *
+     * @var \PhpOffice\PhpSpreadsheet\Writer\Xls\Parser
+     */
+    private $parser;
+
+    /**
+     * Maximum number of characters for a string (LABEL record in BIFF5).
+     *
+     * @var int
+     */
+    private $xlsStringMaxLength;
+
+    /**
+     * Array containing format information for columns.
+     *
+     * @var array
+     */
+    private $columnInfo;
+
+    /**
+     * Array containing the selected area for the worksheet.
+     *
+     * @var array
+     */
+    private $selection;
+
+    /**
+     * The active pane for the worksheet.
+     *
+     * @var int
+     */
+    private $activePane;
+
+    /**
+     * Whether to use outline.
+     *
+     * @var int
+     */
+    private $outlineOn;
+
+    /**
+     * Auto outline styles.
+     *
+     * @var bool
+     */
+    private $outlineStyle;
+
+    /**
+     * Whether to have outline summary below.
+     *
+     * @var bool
+     */
+    private $outlineBelow;
+
+    /**
+     * Whether to have outline summary at the right.
+     *
+     * @var bool
+     */
+    private $outlineRight;
+
+    /**
+     * Reference to the total number of strings in the workbook.
+     *
+     * @var int
+     */
+    private $stringTotal;
+
+    /**
+     * Reference to the number of unique strings in the workbook.
+     *
+     * @var int
+     */
+    private $stringUnique;
+
+    /**
+     * Reference to the array containing all the unique strings in the workbook.
+     *
+     * @var array
+     */
+    private $stringTable;
+
+    /**
+     * Color cache.
+     */
+    private $colors;
+
+    /**
+     * Index of first used row (at least 0).
+     *
+     * @var int
+     */
+    private $firstRowIndex;
+
+    /**
+     * Index of last used row. (no used rows means -1).
+     *
+     * @var int
+     */
+    private $lastRowIndex;
+
+    /**
+     * Index of first used column (at least 0).
+     *
+     * @var int
+     */
+    private $firstColumnIndex;
+
+    /**
+     * Index of last used column (no used columns means -1).
+     *
+     * @var int
+     */
+    private $lastColumnIndex;
+
+    /**
+     * Sheet object.
+     *
+     * @var \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
+     */
+    public $phpSheet;
+
+    /**
+     * Count cell style Xfs.
+     *
+     * @var int
+     */
+    private $countCellStyleXfs;
+
+    /**
+     * Escher object corresponding to MSODRAWING.
+     *
+     * @var \PhpOffice\PhpSpreadsheet\Shared\Escher
+     */
+    private $escher;
+
+    /**
+     * Array of font hashes associated to FONT records index.
+     *
+     * @var array
+     */
+    public $fontHashIndex;
+
+    /**
+     * @var bool
+     */
+    private $preCalculateFormulas;
+
+    /**
+     * @var int
+     */
+    private $printHeaders;
+
+    /**
+     * Constructor.
+     *
+     * @param int $str_total Total number of strings
+     * @param int $str_unique Total number of unique strings
+     * @param array &$str_table String Table
+     * @param array &$colors Colour Table
+     * @param Parser $parser The formula parser created for the Workbook
+     * @param bool $preCalculateFormulas Flag indicating whether formulas should be calculated or just written
+     * @param \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $phpSheet The worksheet to write
+     */
+    public function __construct(&$str_total, &$str_unique, &$str_table, &$colors, Parser $parser, $preCalculateFormulas, \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $phpSheet)
+    {
+        // It needs to call its parent's constructor explicitly
+        parent::__construct();
+
+        $this->preCalculateFormulas = $preCalculateFormulas;
+        $this->stringTotal = &$str_total;
+        $this->stringUnique = &$str_unique;
+        $this->stringTable = &$str_table;
+        $this->colors = &$colors;
+        $this->parser = $parser;
+
+        $this->phpSheet = $phpSheet;
+
+        $this->xlsStringMaxLength = 255;
+        $this->columnInfo = [];
+        $this->selection = [0, 0, 0, 0];
+        $this->activePane = 3;
+
+        $this->printHeaders = 0;
+
+        $this->outlineStyle = 0;
+        $this->outlineBelow = 1;
+        $this->outlineRight = 1;
+        $this->outlineOn = 1;
+
+        $this->fontHashIndex = [];
+
+        // calculate values for DIMENSIONS record
+        $minR = 1;
+        $minC = 'A';
+
+        $maxR = $this->phpSheet->getHighestRow();
+        $maxC = $this->phpSheet->getHighestColumn();
+
+        // Determine lowest and highest column and row
+        $this->lastRowIndex = ($maxR > 65535) ? 65535 : $maxR;
+
+        $this->firstColumnIndex = Coordinate::columnIndexFromString($minC);
+        $this->lastColumnIndex = Coordinate::columnIndexFromString($maxC);
+
+//        if ($this->firstColumnIndex > 255) $this->firstColumnIndex = 255;
+        if ($this->lastColumnIndex > 255) {
+            $this->lastColumnIndex = 255;
+        }
+
+        $this->countCellStyleXfs = count($phpSheet->getParent()->getCellStyleXfCollection());
+    }
+
+    /**
+     * Add data to the beginning of the workbook (note the reverse order)
+     * and to the end of the workbook.
+     *
+     * @see \PhpOffice\PhpSpreadsheet\Writer\Xls\Workbook::storeWorkbook()
+     */
+    public function close(): void
+    {
+        $phpSheet = $this->phpSheet;
+
+        // Storing selected cells and active sheet because it changes while parsing cells with formulas.
+        $selectedCells = $this->phpSheet->getSelectedCells();
+        $activeSheetIndex = $this->phpSheet->getParent()->getActiveSheetIndex();
+
+        // Write BOF record
+        $this->storeBof(0x0010);
+
+        // Write PRINTHEADERS
+        $this->writePrintHeaders();
+
+        // Write PRINTGRIDLINES
+        $this->writePrintGridlines();
+
+        // Write GRIDSET
+        $this->writeGridset();
+
+        // Calculate column widths
+        $phpSheet->calculateColumnWidths();
+
+        // Column dimensions
+        if (($defaultWidth = $phpSheet->getDefaultColumnDimension()->getWidth()) < 0) {
+            $defaultWidth = \PhpOffice\PhpSpreadsheet\Shared\Font::getDefaultColumnWidthByFont($phpSheet->getParent()->getDefaultStyle()->getFont());
+        }
+
+        $columnDimensions = $phpSheet->getColumnDimensions();
+        $maxCol = $this->lastColumnIndex - 1;
+        for ($i = 0; $i <= $maxCol; ++$i) {
+            $hidden = 0;
+            $level = 0;
+            $xfIndex = 15; // there are 15 cell style Xfs
+
+            $width = $defaultWidth;
+
+            $columnLetter = Coordinate::stringFromColumnIndex($i + 1);
+            if (isset($columnDimensions[$columnLetter])) {
+                $columnDimension = $columnDimensions[$columnLetter];
+                if ($columnDimension->getWidth() >= 0) {
+                    $width = $columnDimension->getWidth();
+                }
+                $hidden = $columnDimension->getVisible() ? 0 : 1;
+                $level = $columnDimension->getOutlineLevel();
+                $xfIndex = $columnDimension->getXfIndex() + 15; // there are 15 cell style Xfs
+            }
+
+            // Components of columnInfo:
+            // $firstcol first column on the range
+            // $lastcol  last column on the range
+            // $width    width to set
+            // $xfIndex  The optional cell style Xf index to apply to the columns
+            // $hidden   The optional hidden atribute
+            // $level    The optional outline level
+            $this->columnInfo[] = [$i, $i, $width, $xfIndex, $hidden, $level];
+        }
+
+        // Write GUTS
+        $this->writeGuts();
+
+        // Write DEFAULTROWHEIGHT
+        $this->writeDefaultRowHeight();
+        // Write WSBOOL
+        $this->writeWsbool();
+        // Write horizontal and vertical page breaks
+        $this->writeBreaks();
+        // Write page header
+        $this->writeHeader();
+        // Write page footer
+        $this->writeFooter();
+        // Write page horizontal centering
+        $this->writeHcenter();
+        // Write page vertical centering
+        $this->writeVcenter();
+        // Write left margin
+        $this->writeMarginLeft();
+        // Write right margin
+        $this->writeMarginRight();
+        // Write top margin
+        $this->writeMarginTop();
+        // Write bottom margin
+        $this->writeMarginBottom();
+        // Write page setup
+        $this->writeSetup();
+        // Write sheet protection
+        $this->writeProtect();
+        // Write SCENPROTECT
+        $this->writeScenProtect();
+        // Write OBJECTPROTECT
+        $this->writeObjectProtect();
+        // Write sheet password
+        $this->writePassword();
+        // Write DEFCOLWIDTH record
+        $this->writeDefcol();
+
+        // Write the COLINFO records if they exist
+        if (!empty($this->columnInfo)) {
+            $colcount = count($this->columnInfo);
+            for ($i = 0; $i < $colcount; ++$i) {
+                $this->writeColinfo($this->columnInfo[$i]);
+            }
+        }
+        $autoFilterRange = $phpSheet->getAutoFilter()->getRange();
+        if (!empty($autoFilterRange)) {
+            // Write AUTOFILTERINFO
+            $this->writeAutoFilterInfo();
+        }
+
+        // Write sheet dimensions
+        $this->writeDimensions();
+
+        // Row dimensions
+        foreach ($phpSheet->getRowDimensions() as $rowDimension) {
+            $xfIndex = $rowDimension->getXfIndex() + 15; // there are 15 cellXfs
+            $this->writeRow($rowDimension->getRowIndex() - 1, $rowDimension->getRowHeight(), $xfIndex, ($rowDimension->getVisible() ? '0' : '1'), $rowDimension->getOutlineLevel());
+        }
+
+        // Write Cells
+        foreach ($phpSheet->getCoordinates() as $coordinate) {
+            $cell = $phpSheet->getCell($coordinate);
+            $row = $cell->getRow() - 1;
+            $column = Coordinate::columnIndexFromString($cell->getColumn()) - 1;
+
+            // Don't break Excel break the code!
+            if ($row > 65535 || $column > 255) {
+                throw new WriterException('Rows or columns overflow! Excel5 has limit to 65535 rows and 255 columns. Use XLSX instead.');
+            }
+
+            // Write cell value
+            $xfIndex = $cell->getXfIndex() + 15; // there are 15 cell style Xfs
+
+            $cVal = $cell->getValue();
+            if ($cVal instanceof RichText) {
+                $arrcRun = [];
+                $str_len = StringHelper::countCharacters($cVal->getPlainText(), 'UTF-8');
+                $str_pos = 0;
+                $elements = $cVal->getRichTextElements();
+                foreach ($elements as $element) {
+                    // FONT Index
+                    if ($element instanceof Run) {
+                        $str_fontidx = $this->fontHashIndex[$element->getFont()->getHashCode()];
+                    } else {
+                        $str_fontidx = 0;
+                    }
+                    $arrcRun[] = ['strlen' => $str_pos, 'fontidx' => $str_fontidx];
+                    // Position FROM
+                    $str_pos += StringHelper::countCharacters($element->getText(), 'UTF-8');
+                }
+                $this->writeRichTextString($row, $column, $cVal->getPlainText(), $xfIndex, $arrcRun);
+            } else {
+                switch ($cell->getDatatype()) {
+                    case DataType::TYPE_STRING:
+                    case DataType::TYPE_NULL:
+                        if ($cVal === '' || $cVal === null) {
+                            $this->writeBlank($row, $column, $xfIndex);
+                        } else {
+                            $this->writeString($row, $column, $cVal, $xfIndex);
+                        }
+
+                        break;
+                    case DataType::TYPE_NUMERIC:
+                        $this->writeNumber($row, $column, $cVal, $xfIndex);
+
+                        break;
+                    case DataType::TYPE_FORMULA:
+                        $calculatedValue = $this->preCalculateFormulas ?
+                            $cell->getCalculatedValue() : null;
+                        if (self::WRITE_FORMULA_EXCEPTION == $this->writeFormula($row, $column, $cVal, $xfIndex, $calculatedValue)) {
+                            if ($calculatedValue === null) {
+                                $calculatedValue = $cell->getCalculatedValue();
+                            }
+                            $calctype = gettype($calculatedValue);
+                            switch ($calctype) {
+                                case 'integer':
+                                case 'double':
+                                    $this->writeNumber($row, $column, $calculatedValue, $xfIndex);
+
+                                    break;
+                                case 'string':
+                                    $this->writeString($row, $column, $calculatedValue, $xfIndex);
+
+                                    break;
+                                case 'boolean':
+                                    $this->writeBoolErr($row, $column, $calculatedValue, 0, $xfIndex);
+
+                                    break;
+                                default:
+                                    $this->writeString($row, $column, $cVal, $xfIndex);
+                            }
+                        }
+
+                        break;
+                    case DataType::TYPE_BOOL:
+                        $this->writeBoolErr($row, $column, $cVal, 0, $xfIndex);
+
+                        break;
+                    case DataType::TYPE_ERROR:
+                        $this->writeBoolErr($row, $column, self::mapErrorCode($cVal), 1, $xfIndex);
+
+                        break;
+                }
+            }
+        }
+
+        // Append
+        $this->writeMsoDrawing();
+
+        // Restoring active sheet.
+        $this->phpSheet->getParent()->setActiveSheetIndex($activeSheetIndex);
+
+        // Write WINDOW2 record
+        $this->writeWindow2();
+
+        // Write PLV record
+        $this->writePageLayoutView();
+
+        // Write ZOOM record
+        $this->writeZoom();
+        if ($phpSheet->getFreezePane()) {
+            $this->writePanes();
+        }
+
+        // Restoring selected cells.
+        $this->phpSheet->setSelectedCells($selectedCells);
+
+        // Write SELECTION record
+        $this->writeSelection();
+
+        // Write MergedCellsTable Record
+        $this->writeMergedCells();
+
+        // Hyperlinks
+        foreach ($phpSheet->getHyperLinkCollection() as $coordinate => $hyperlink) {
+            [$column, $row] = Coordinate::coordinateFromString($coordinate);
+
+            $url = $hyperlink->getUrl();
+
+            if (strpos($url, 'sheet://') !== false) {
+                // internal to current workbook
+                $url = str_replace('sheet://', 'internal:', $url);
+            } elseif (preg_match('/^(http:|https:|ftp:|mailto:)/', $url)) {
+                // URL
+            } else {
+                // external (local file)
+                $url = 'external:' . $url;
+            }
+
+            $this->writeUrl($row - 1, Coordinate::columnIndexFromString($column) - 1, $url);
+        }
+
+        $this->writeDataValidity();
+        $this->writeSheetLayout();
+
+        // Write SHEETPROTECTION record
+        $this->writeSheetProtection();
+        $this->writeRangeProtection();
+
+        $arrConditionalStyles = $phpSheet->getConditionalStylesCollection();
+        if (!empty($arrConditionalStyles)) {
+            $arrConditional = [];
+            // @TODO CFRule & CFHeader
+            // Write CFHEADER record
+            $this->writeCFHeader();
+            // Write ConditionalFormattingTable records
+            foreach ($arrConditionalStyles as $cellCoordinate => $conditionalStyles) {
+                foreach ($conditionalStyles as $conditional) {
+                    if ($conditional->getConditionType() == Conditional::CONDITION_EXPRESSION
+                        || $conditional->getConditionType() == Conditional::CONDITION_CELLIS) {
+                        if (!isset($arrConditional[$conditional->getHashCode()])) {
+                            // This hash code has been handled
+                            $arrConditional[$conditional->getHashCode()] = true;
+
+                            // Write CFRULE record
+                            $this->writeCFRule($conditional);
+                        }
+                    }
+                }
+            }
+        }
+
+        $this->storeEof();
+    }
+
+    /**
+     * Write a cell range address in BIFF8
+     * always fixed range
+     * See section 2.5.14 in OpenOffice.org's Documentation of the Microsoft Excel File Format.
+     *
+     * @param string $range E.g. 'A1' or 'A1:B6'
+     *
+     * @return string Binary data
+     */
+    private function writeBIFF8CellRangeAddressFixed($range)
+    {
+        $explodes = explode(':', $range);
+
+        // extract first cell, e.g. 'A1'
+        $firstCell = $explodes[0];
+
+        // extract last cell, e.g. 'B6'
+        if (count($explodes) == 1) {
+            $lastCell = $firstCell;
+        } else {
+            $lastCell = $explodes[1];
+        }
+
+        $firstCellCoordinates = Coordinate::coordinateFromString($firstCell); // e.g. [0, 1]
+        $lastCellCoordinates = Coordinate::coordinateFromString($lastCell); // e.g. [1, 6]
+
+        return pack('vvvv', $firstCellCoordinates[1] - 1, $lastCellCoordinates[1] - 1, Coordinate::columnIndexFromString($firstCellCoordinates[0]) - 1, Coordinate::columnIndexFromString($lastCellCoordinates[0]) - 1);
+    }
+
+    /**
+     * Retrieves data from memory in one chunk, or from disk in $buffer
+     * sized chunks.
+     *
+     * @return string The data
+     */
+    public function getData()
+    {
+        $buffer = 4096;
+
+        // Return data stored in memory
+        if (isset($this->_data)) {
+            $tmp = $this->_data;
+            $this->_data = null;
+
+            return $tmp;
+        }
+
+        // No data to return
+        return false;
+    }
+
+    /**
+     * Set the option to print the row and column headers on the printed page.
+     *
+     * @param int $print Whether to print the headers or not. Defaults to 1 (print).
+     */
+    public function printRowColHeaders($print = 1): void
+    {
+        $this->printHeaders = $print;
+    }
+
+    /**
+     * This method sets the properties for outlining and grouping. The defaults
+     * correspond to Excel's defaults.
+     *
+     * @param bool $visible
+     * @param bool $symbols_below
+     * @param bool $symbols_right
+     * @param bool $auto_style
+     */
+    public function setOutline($visible = true, $symbols_below = true, $symbols_right = true, $auto_style = false): void
+    {
+        $this->outlineOn = $visible;
+        $this->outlineBelow = $symbols_below;
+        $this->outlineRight = $symbols_right;
+        $this->outlineStyle = $auto_style;
+
+        // Ensure this is a boolean vale for Window2
+        if ($this->outlineOn) {
+            $this->outlineOn = 1;
+        }
+    }
+
+    /**
+     * Write a double to the specified row and column (zero indexed).
+     * An integer can be written as a double. Excel will display an
+     * integer. $format is optional.
+     *
+     * Returns  0 : normal termination
+     *         -2 : row or column out of range
+     *
+     * @param int $row Zero indexed row
+     * @param int $col Zero indexed column
+     * @param float $num The number to write
+     * @param mixed $xfIndex The optional XF format
+     *
+     * @return int
+     */
+    private function writeNumber($row, $col, $num, $xfIndex)
+    {
+        $record = 0x0203; // Record identifier
+        $length = 0x000E; // Number of bytes to follow
+
+        $header = pack('vv', $record, $length);
+        $data = pack('vvv', $row, $col, $xfIndex);
+        $xl_double = pack('d', $num);
+        if (self::getByteOrder()) { // if it's Big Endian
+            $xl_double = strrev($xl_double);
+        }
+
+        $this->append($header . $data . $xl_double);
+
+        return 0;
+    }
+
+    /**
+     * Write a LABELSST record or a LABEL record. Which one depends on BIFF version.
+     *
+     * @param int $row Row index (0-based)
+     * @param int $col Column index (0-based)
+     * @param string $str The string
+     * @param int $xfIndex Index to XF record
+     */
+    private function writeString($row, $col, $str, $xfIndex): void
+    {
+        $this->writeLabelSst($row, $col, $str, $xfIndex);
+    }
+
+    /**
+     * Write a LABELSST record or a LABEL record. Which one depends on BIFF version
+     * It differs from writeString by the writing of rich text strings.
+     *
+     * @param int $row Row index (0-based)
+     * @param int $col Column index (0-based)
+     * @param string $str The string
+     * @param int $xfIndex The XF format index for the cell
+     * @param array $arrcRun Index to Font record and characters beginning
+     */
+    private function writeRichTextString($row, $col, $str, $xfIndex, $arrcRun): void
+    {
+        $record = 0x00FD; // Record identifier
+        $length = 0x000A; // Bytes to follow
+        $str = StringHelper::UTF8toBIFF8UnicodeShort($str, $arrcRun);
+
+        // check if string is already present
+        if (!isset($this->stringTable[$str])) {
+            $this->stringTable[$str] = $this->stringUnique++;
+        }
+        ++$this->stringTotal;
+
+        $header = pack('vv', $record, $length);
+        $data = pack('vvvV', $row, $col, $xfIndex, $this->stringTable[$str]);
+        $this->append($header . $data);
+    }
+
+    /**
+     * Write a string to the specified row and column (zero indexed).
+     * This is the BIFF8 version (no 255 chars limit).
+     * $format is optional.
+     *
+     * @param int $row Zero indexed row
+     * @param int $col Zero indexed column
+     * @param string $str The string to write
+     * @param mixed $xfIndex The XF format index for the cell
+     */
+    private function writeLabelSst($row, $col, $str, $xfIndex): void
+    {
+        $record = 0x00FD; // Record identifier
+        $length = 0x000A; // Bytes to follow
+
+        $str = StringHelper::UTF8toBIFF8UnicodeLong($str);
+
+        // check if string is already present
+        if (!isset($this->stringTable[$str])) {
+            $this->stringTable[$str] = $this->stringUnique++;
+        }
+        ++$this->stringTotal;
+
+        $header = pack('vv', $record, $length);
+        $data = pack('vvvV', $row, $col, $xfIndex, $this->stringTable[$str]);
+        $this->append($header . $data);
+    }
+
+    /**
+     * Write a blank cell to the specified row and column (zero indexed).
+     * A blank cell is used to specify formatting without adding a string
+     * or a number.
+     *
+     * A blank cell without a format serves no purpose. Therefore, we don't write
+     * a BLANK record unless a format is specified.
+     *
+     * Returns  0 : normal termination (including no format)
+     *         -1 : insufficient number of arguments
+     *         -2 : row or column out of range
+     *
+     * @param int $row Zero indexed row
+     * @param int $col Zero indexed column
+     * @param mixed $xfIndex The XF format index
+     *
+     * @return int
+     */
+    public function writeBlank($row, $col, $xfIndex)
+    {
+        $record = 0x0201; // Record identifier
+        $length = 0x0006; // Number of bytes to follow
+
+        $header = pack('vv', $record, $length);
+        $data = pack('vvv', $row, $col, $xfIndex);
+        $this->append($header . $data);
+
+        return 0;
+    }
+
+    /**
+     * Write a boolean or an error type to the specified row and column (zero indexed).
+     *
+     * @param int $row Row index (0-based)
+     * @param int $col Column index (0-based)
+     * @param int $value
+     * @param bool $isError Error or Boolean?
+     * @param int $xfIndex
+     *
+     * @return int
+     */
+    private function writeBoolErr($row, $col, $value, $isError, $xfIndex)
+    {
+        $record = 0x0205;
+        $length = 8;
+
+        $header = pack('vv', $record, $length);
+        $data = pack('vvvCC', $row, $col, $xfIndex, $value, $isError);
+        $this->append($header . $data);
+
+        return 0;
+    }
+
+    const WRITE_FORMULA_NORMAL = 0;
+    const WRITE_FORMULA_ERRORS = -1;
+    const WRITE_FORMULA_RANGE = -2;
+    const WRITE_FORMULA_EXCEPTION = -3;
+
+    /**
+     * Write a formula to the specified row and column (zero indexed).
+     * The textual representation of the formula is passed to the parser in
+     * Parser.php which returns a packed binary string.
+     *
+     * Returns  0 : WRITE_FORMULA_NORMAL  normal termination
+     *         -1 : WRITE_FORMULA_ERRORS formula errors (bad formula)
+     *         -2 : WRITE_FORMULA_RANGE  row or column out of range
+     *         -3 : WRITE_FORMULA_EXCEPTION parse raised exception, probably due to definedname
+     *
+     * @param int $row Zero indexed row
+     * @param int $col Zero indexed column
+     * @param string $formula The formula text string
+     * @param mixed $xfIndex The XF format index
+     * @param mixed $calculatedValue Calculated value
+     *
+     * @return int
+     */
+    private function writeFormula($row, $col, $formula, $xfIndex, $calculatedValue)
+    {
+        $record = 0x0006; // Record identifier
+
+        // Initialize possible additional value for STRING record that should be written after the FORMULA record?
+        $stringValue = null;
+
+        // calculated value
+        if (isset($calculatedValue)) {
+            // Since we can't yet get the data type of the calculated value,
+            // we use best effort to determine data type
+            if (is_bool($calculatedValue)) {
+                // Boolean value
+                $num = pack('CCCvCv', 0x01, 0x00, (int) $calculatedValue, 0x00, 0x00, 0xFFFF);
+            } elseif (is_int($calculatedValue) || is_float($calculatedValue)) {
+                // Numeric value
+                $num = pack('d', $calculatedValue);
+            } elseif (is_string($calculatedValue)) {
+                $errorCodes = DataType::getErrorCodes();
+                if (isset($errorCodes[$calculatedValue])) {
+                    // Error value
+                    $num = pack('CCCvCv', 0x02, 0x00, self::mapErrorCode($calculatedValue), 0x00, 0x00, 0xFFFF);
+                } elseif ($calculatedValue === '') {
+                    // Empty string (and BIFF8)
+                    $num = pack('CCCvCv', 0x03, 0x00, 0x00, 0x00, 0x00, 0xFFFF);
+                } else {
+                    // Non-empty string value (or empty string BIFF5)
+                    $stringValue = $calculatedValue;
+                    $num = pack('CCCvCv', 0x00, 0x00, 0x00, 0x00, 0x00, 0xFFFF);
+                }
+            } else {
+                // We are really not supposed to reach here
+                $num = pack('d', 0x00);
+            }
+        } else {
+            $num = pack('d', 0x00);
+        }
+
+        $grbit = 0x03; // Option flags
+        $unknown = 0x0000; // Must be zero
+
+        // Strip the '=' or '@' sign at the beginning of the formula string
+        if ($formula[0] == '=') {
+            $formula = substr($formula, 1);
+        } else {
+            // Error handling
+            $this->writeString($row, $col, 'Unrecognised character for formula', 0);
+
+            return self::WRITE_FORMULA_ERRORS;
+        }
+
+        // Parse the formula using the parser in Parser.php
+        try {
+            $error = $this->parser->parse($formula);
+            $formula = $this->parser->toReversePolish();
+
+            $formlen = strlen($formula); // Length of the binary string
+            $length = 0x16 + $formlen; // Length of the record data
+
+            $header = pack('vv', $record, $length);
+
+            $data = pack('vvv', $row, $col, $xfIndex)
+                . $num
+                . pack('vVv', $grbit, $unknown, $formlen);
+            $this->append($header . $data . $formula);
+
+            // Append also a STRING record if necessary
+            if ($stringValue !== null) {
+                $this->writeStringRecord($stringValue);
+            }
+
+            return self::WRITE_FORMULA_NORMAL;
+        } catch (PhpSpreadsheetException $e) {
+            return self::WRITE_FORMULA_EXCEPTION;
+        }
+    }
+
+    /**
+     * Write a STRING record. This.
+     *
+     * @param string $stringValue
+     */
+    private function writeStringRecord($stringValue): void
+    {
+        $record = 0x0207; // Record identifier
+        $data = StringHelper::UTF8toBIFF8UnicodeLong($stringValue);
+
+        $length = strlen($data);
+        $header = pack('vv', $record, $length);
+
+        $this->append($header . $data);
+    }
+
+    /**
+     * Write a hyperlink.
+     * This is comprised of two elements: the visible label and
+     * the invisible link. The visible label is the same as the link unless an
+     * alternative string is specified. The label is written using the
+     * writeString() method. Therefore the 255 characters string limit applies.
+     * $string and $format are optional.
+     *
+     * The hyperlink can be to a http, ftp, mail, internal sheet (not yet), or external
+     * directory url.
+     *
+     * Returns  0 : normal termination
+     *         -2 : row or column out of range
+     *         -3 : long string truncated to 255 chars
+     *
+     * @param int $row Row
+     * @param int $col Column
+     * @param string $url URL string
+     *
+     * @return int
+     */
+    private function writeUrl($row, $col, $url)
+    {
+        // Add start row and col to arg list
+        return $this->writeUrlRange($row, $col, $row, $col, $url);
+    }
+
+    /**
+     * This is the more general form of writeUrl(). It allows a hyperlink to be
+     * written to a range of cells. This function also decides the type of hyperlink
+     * to be written. These are either, Web (http, ftp, mailto), Internal
+     * (Sheet1!A1) or external ('c:\temp\foo.xls#Sheet1!A1').
+     *
+     * @param int $row1 Start row
+     * @param int $col1 Start column
+     * @param int $row2 End row
+     * @param int $col2 End column
+     * @param string $url URL string
+     *
+     * @return int
+     *
+     * @see writeUrl()
+     */
+    public function writeUrlRange($row1, $col1, $row2, $col2, $url)
+    {
+        // Check for internal/external sheet links or default to web link
+        if (preg_match('[^internal:]', $url)) {
+            return $this->writeUrlInternal($row1, $col1, $row2, $col2, $url);
+        }
+        if (preg_match('[^external:]', $url)) {
+            return $this->writeUrlExternal($row1, $col1, $row2, $col2, $url);
+        }
+
+        return $this->writeUrlWeb($row1, $col1, $row2, $col2, $url);
+    }
+
+    /**
+     * Used to write http, ftp and mailto hyperlinks.
+     * The link type ($options) is 0x03 is the same as absolute dir ref without
+     * sheet. However it is differentiated by the $unknown2 data stream.
+     *
+     * @param int $row1 Start row
+     * @param int $col1 Start column
+     * @param int $row2 End row
+     * @param int $col2 End column
+     * @param string $url URL string
+     *
+     * @return int
+     *
+     * @see writeUrl()
+     */
+    public function writeUrlWeb($row1, $col1, $row2, $col2, $url)
+    {
+        $record = 0x01B8; // Record identifier
+        $length = 0x00000; // Bytes to follow
+
+        // Pack the undocumented parts of the hyperlink stream
+        $unknown1 = pack('H*', 'D0C9EA79F9BACE118C8200AA004BA90B02000000');
+        $unknown2 = pack('H*', 'E0C9EA79F9BACE118C8200AA004BA90B');
+
+        // Pack the option flags
+        $options = pack('V', 0x03);
+
+        // Convert URL to a null terminated wchar string
+        $url = implode("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY));
+        $url = $url . "\0\0\0";
+
+        // Pack the length of the URL
+        $url_len = pack('V', strlen($url));
+
+        // Calculate the data length
+        $length = 0x34 + strlen($url);
+
+        // Pack the header data
+        $header = pack('vv', $record, $length);
+        $data = pack('vvvv', $row1, $row2, $col1, $col2);
+
+        // Write the packed data
+        $this->append($header . $data . $unknown1 . $options . $unknown2 . $url_len . $url);
+
+        return 0;
+    }
+
+    /**
+     * Used to write internal reference hyperlinks such as "Sheet1!A1".
+     *
+     * @param int $row1 Start row
+     * @param int $col1 Start column
+     * @param int $row2 End row
+     * @param int $col2 End column
+     * @param string $url URL string
+     *
+     * @return int
+     *
+     * @see writeUrl()
+     */
+    public function writeUrlInternal($row1, $col1, $row2, $col2, $url)
+    {
+        $record = 0x01B8; // Record identifier
+        $length = 0x00000; // Bytes to follow
+
+        // Strip URL type
+        $url = preg_replace('/^internal:/', '', $url);
+
+        // Pack the undocumented parts of the hyperlink stream
+        $unknown1 = pack('H*', 'D0C9EA79F9BACE118C8200AA004BA90B02000000');
+
+        // Pack the option flags
+        $options = pack('V', 0x08);
+
+        // Convert the URL type and to a null terminated wchar string
+        $url .= "\0";
+
+        // character count
+        $url_len = StringHelper::countCharacters($url);
+        $url_len = pack('V', $url_len);
+
+        $url = StringHelper::convertEncoding($url, 'UTF-16LE', 'UTF-8');
+
+        // Calculate the data length
+        $length = 0x24 + strlen($url);
+
+        // Pack the header data
+        $header = pack('vv', $record, $length);
+        $data = pack('vvvv', $row1, $row2, $col1, $col2);
+
+        // Write the packed data
+        $this->append($header . $data . $unknown1 . $options . $url_len . $url);
+
+        return 0;
+    }
+
+    /**
+     * Write links to external directory names such as 'c:\foo.xls',
+     * c:\foo.xls#Sheet1!A1', '../../foo.xls'. and '../../foo.xls#Sheet1!A1'.
+     *
+     * Note: Excel writes some relative links with the $dir_long string. We ignore
+     * these cases for the sake of simpler code.
+     *
+     * @param int $row1 Start row
+     * @param int $col1 Start column
+     * @param int $row2 End row
+     * @param int $col2 End column
+     * @param string $url URL string
+     *
+     * @return int
+     *
+     * @see writeUrl()
+     */
+    public function writeUrlExternal($row1, $col1, $row2, $col2, $url)
+    {
+        // Network drives are different. We will handle them separately
+        // MS/Novell network drives and shares start with \\
+        if (preg_match('[^external:\\\\]', $url)) {
+            return; //($this->writeUrlExternal_net($row1, $col1, $row2, $col2, $url, $str, $format));
+        }
+
+        $record = 0x01B8; // Record identifier
+        $length = 0x00000; // Bytes to follow
+
+        // Strip URL type and change Unix dir separator to Dos style (if needed)
+        //
+        $url = preg_replace('/^external:/', '', $url);
+        $url = preg_replace('/\//', '\\', $url);
+
+        // Determine if the link is relative or absolute:
+        //   relative if link contains no dir separator, "somefile.xls"
+        //   relative if link starts with up-dir, "..\..\somefile.xls"
+        //   otherwise, absolute
+
+        $absolute = 0x00; // relative path
+        if (preg_match('/^[A-Z]:/', $url)) {
+            $absolute = 0x02; // absolute path on Windows, e.g. C:\...
+        }
+        $link_type = 0x01 | $absolute;
+
+        // Determine if the link contains a sheet reference and change some of the
+        // parameters accordingly.
+        // Split the dir name and sheet name (if it exists)
+        $dir_long = $url;
+        if (preg_match('/\\#/', $url)) {
+            $link_type |= 0x08;
+        }
+
+        // Pack the link type
+        $link_type = pack('V', $link_type);
+
+        // Calculate the up-level dir count e.g.. (..\..\..\ == 3)
+        $up_count = preg_match_all('/\\.\\.\\\\/', $dir_long, $useless);
+        $up_count = pack('v', $up_count);
+
+        // Store the short dos dir name (null terminated)
+        $dir_short = preg_replace('/\\.\\.\\\\/', '', $dir_long) . "\0";
+
+        // Store the long dir name as a wchar string (non-null terminated)
+        $dir_long = $dir_long . "\0";
+
+        // Pack the lengths of the dir strings
+        $dir_short_len = pack('V', strlen($dir_short));
+        $dir_long_len = pack('V', strlen($dir_long));
+        $stream_len = pack('V', 0); //strlen($dir_long) + 0x06);
+
+        // Pack the undocumented parts of the hyperlink stream
+        $unknown1 = pack('H*', 'D0C9EA79F9BACE118C8200AA004BA90B02000000');
+        $unknown2 = pack('H*', '0303000000000000C000000000000046');
+        $unknown3 = pack('H*', 'FFFFADDE000000000000000000000000000000000000000');
+        $unknown4 = pack('v', 0x03);
+
+        // Pack the main data stream
+        $data = pack('vvvv', $row1, $row2, $col1, $col2) .
+            $unknown1 .
+            $link_type .
+            $unknown2 .
+            $up_count .
+            $dir_short_len .
+            $dir_short .
+            $unknown3 .
+            $stream_len; /*.
+                          $dir_long_len .
+                          $unknown4     .
+                          $dir_long     .
+                          $sheet_len    .
+                          $sheet        ;*/
+
+        // Pack the header data
+        $length = strlen($data);
+        $header = pack('vv', $record, $length);
+
+        // Write the packed data
+        $this->append($header . $data);
+
+        return 0;
+    }
+
+    /**
+     * This method is used to set the height and format for a row.
+     *
+     * @param int $row The row to set
+     * @param int $height Height we are giving to the row.
+     *                        Use null to set XF without setting height
+     * @param int $xfIndex The optional cell style Xf index to apply to the columns
+     * @param bool $hidden The optional hidden attribute
+     * @param int $level The optional outline level for row, in range [0,7]
+     */
+    private function writeRow($row, $height, $xfIndex, $hidden = false, $level = 0): void
+    {
+        $record = 0x0208; // Record identifier
+        $length = 0x0010; // Number of bytes to follow
+
+        $colMic = 0x0000; // First defined column
+        $colMac = 0x0000; // Last defined column
+        $irwMac = 0x0000; // Used by Excel to optimise loading
+        $reserved = 0x0000; // Reserved
+        $grbit = 0x0000; // Option flags
+        $ixfe = $xfIndex;
+
+        if ($height < 0) {
+            $height = null;
+        }
+
+        // Use writeRow($row, null, $XF) to set XF format without setting height
+        if ($height != null) {
+            $miyRw = $height * 20; // row height
+        } else {
+            $miyRw = 0xff; // default row height is 256
+        }
+
+        // Set the options flags. fUnsynced is used to show that the font and row
+        // heights are not compatible. This is usually the case for WriteExcel.
+        // The collapsed flag 0x10 doesn't seem to be used to indicate that a row
+        // is collapsed. Instead it is used to indicate that the previous row is
+        // collapsed. The zero height flag, 0x20, is used to collapse a row.
+
+        $grbit |= $level;
+        if ($hidden) {
+            $grbit |= 0x0030;
+        }
+        if ($height !== null) {
+            $grbit |= 0x0040; // fUnsynced
+        }
+        if ($xfIndex !== 0xF) {
+            $grbit |= 0x0080;
+        }
+        $grbit |= 0x0100;
+
+        $header = pack('vv', $record, $length);
+        $data = pack('vvvvvvvv', $row, $colMic, $colMac, $miyRw, $irwMac, $reserved, $grbit, $ixfe);
+        $this->append($header . $data);
+    }
+
+    /**
+     * Writes Excel DIMENSIONS to define the area in which there is data.
+     */
+    private function writeDimensions(): void
+    {
+        $record = 0x0200; // Record identifier
+
+        $length = 0x000E;
+        $data = pack('VVvvv', $this->firstRowIndex, $this->lastRowIndex + 1, $this->firstColumnIndex, $this->lastColumnIndex + 1, 0x0000); // reserved
+
+        $header = pack('vv', $record, $length);
+        $this->append($header . $data);
+    }
+
+    /**
+     * Write BIFF record Window2.
+     */
+    private function writeWindow2(): void
+    {
+        $record = 0x023E; // Record identifier
+        $length = 0x0012;
+
+        $grbit = 0x00B6; // Option flags
+        $rwTop = 0x0000; // Top row visible in window
+        $colLeft = 0x0000; // Leftmost column visible in window
+
+        // The options flags that comprise $grbit
+        $fDspFmla = 0; // 0 - bit
+        $fDspGrid = $this->phpSheet->getShowGridlines() ? 1 : 0; // 1
+        $fDspRwCol = $this->phpSheet->getShowRowColHeaders() ? 1 : 0; // 2
+        $fFrozen = $this->phpSheet->getFreezePane() ? 1 : 0; // 3
+        $fDspZeros = 1; // 4
+        $fDefaultHdr = 1; // 5
+        $fArabic = $this->phpSheet->getRightToLeft() ? 1 : 0; // 6
+        $fDspGuts = $this->outlineOn; // 7
+        $fFrozenNoSplit = 0; // 0 - bit
+        // no support in PhpSpreadsheet for selected sheet, therefore sheet is only selected if it is the active sheet
+        $fSelected = ($this->phpSheet === $this->phpSheet->getParent()->getActiveSheet()) ? 1 : 0;
+        $fPageBreakPreview = $this->phpSheet->getSheetView()->getView() === SheetView::SHEETVIEW_PAGE_BREAK_PREVIEW;
+
+        $grbit = $fDspFmla;
+        $grbit |= $fDspGrid << 1;
+        $grbit |= $fDspRwCol << 2;
+        $grbit |= $fFrozen << 3;
+        $grbit |= $fDspZeros << 4;
+        $grbit |= $fDefaultHdr << 5;
+        $grbit |= $fArabic << 6;
+        $grbit |= $fDspGuts << 7;
+        $grbit |= $fFrozenNoSplit << 8;
+        $grbit |= $fSelected << 9; // Selected sheets.
+        $grbit |= $fSelected << 10; // Active sheet.
+        $grbit |= $fPageBreakPreview << 11;
+
+        $header = pack('vv', $record, $length);
+        $data = pack('vvv', $grbit, $rwTop, $colLeft);
+
+        // FIXME !!!
+        $rgbHdr = 0x0040; // Row/column heading and gridline color index
+        $zoom_factor_page_break = ($fPageBreakPreview ? $this->phpSheet->getSheetView()->getZoomScale() : 0x0000);
+        $zoom_factor_normal = $this->phpSheet->getSheetView()->getZoomScaleNormal();
+
+        $data .= pack('vvvvV', $rgbHdr, 0x0000, $zoom_factor_page_break, $zoom_factor_normal, 0x00000000);
+
+        $this->append($header . $data);
+    }
+
+    /**
+     * Write BIFF record DEFAULTROWHEIGHT.
+     */
+    private function writeDefaultRowHeight(): void
+    {
+        $defaultRowHeight = $this->phpSheet->getDefaultRowDimension()->getRowHeight();
+
+        if ($defaultRowHeight < 0) {
+            return;
+        }
+
+        // convert to twips
+        $defaultRowHeight = (int) 20 * $defaultRowHeight;
+
+        $record = 0x0225; // Record identifier
+        $length = 0x0004; // Number of bytes to follow
+
+        $header = pack('vv', $record, $length);
+        $data = pack('vv', 1, $defaultRowHeight);
+        $this->append($header . $data);
+    }
+
+    /**
+     * Write BIFF record DEFCOLWIDTH if COLINFO records are in use.
+     */
+    private function writeDefcol(): void
+    {
+        $defaultColWidth = 8;
+
+        $record = 0x0055; // Record identifier
+        $length = 0x0002; // Number of bytes to follow
+
+        $header = pack('vv', $record, $length);
+        $data = pack('v', $defaultColWidth);
+        $this->append($header . $data);
+    }
+
+    /**
+     * Write BIFF record COLINFO to define column widths.
+     *
+     * Note: The SDK says the record length is 0x0B but Excel writes a 0x0C
+     * length record.
+     *
+     * @param array $col_array This is the only parameter received and is composed of the following:
+     *                0 => First formatted column,
+     *                1 => Last formatted column,
+     *                2 => Col width (8.43 is Excel default),
+     *                3 => The optional XF format of the column,
+     *                4 => Option flags.
+     *                5 => Optional outline level
+     */
+    private function writeColinfo($col_array): void
+    {
+        if (isset($col_array[0])) {
+            $colFirst = $col_array[0];
+        }
+        if (isset($col_array[1])) {
+            $colLast = $col_array[1];
+        }
+        if (isset($col_array[2])) {
+            $coldx = $col_array[2];
+        } else {
+            $coldx = 8.43;
+        }
+        if (isset($col_array[3])) {
+            $xfIndex = $col_array[3];
+        } else {
+            $xfIndex = 15;
+        }
+        if (isset($col_array[4])) {
+            $grbit = $col_array[4];
+        } else {
+            $grbit = 0;
+        }
+        if (isset($col_array[5])) {
+            $level = $col_array[5];
+        } else {
+            $level = 0;
+        }
+        $record = 0x007D; // Record identifier
+        $length = 0x000C; // Number of bytes to follow
+
+        $coldx *= 256; // Convert to units of 1/256 of a char
+
+        $ixfe = $xfIndex;
+        $reserved = 0x0000; // Reserved
+
+        $level = max(0, min($level, 7));
+        $grbit |= $level << 8;
+
+        $header = pack('vv', $record, $length);
+        $data = pack('vvvvvv', $colFirst, $colLast, $coldx, $ixfe, $grbit, $reserved);
+        $this->append($header . $data);
+    }
+
+    /**
+     * Write BIFF record SELECTION.
+     */
+    private function writeSelection(): void
+    {
+        // look up the selected cell range
+        $selectedCells = Coordinate::splitRange($this->phpSheet->getSelectedCells());
+        $selectedCells = $selectedCells[0];
+        if (count($selectedCells) == 2) {
+            [$first, $last] = $selectedCells;
+        } else {
+            $first = $selectedCells[0];
+            $last = $selectedCells[0];
+        }
+
+        [$colFirst, $rwFirst] = Coordinate::coordinateFromString($first);
+        $colFirst = Coordinate::columnIndexFromString($colFirst) - 1; // base 0 column index
+        --$rwFirst; // base 0 row index
+
+        [$colLast, $rwLast] = Coordinate::coordinateFromString($last);
+        $colLast = Coordinate::columnIndexFromString($colLast) - 1; // base 0 column index
+        --$rwLast; // base 0 row index
+
+        // make sure we are not out of bounds
+        $colFirst = min($colFirst, 255);
+        $colLast = min($colLast, 255);
+
+        $rwFirst = min($rwFirst, 65535);
+        $rwLast = min($rwLast, 65535);
+
+        $record = 0x001D; // Record identifier
+        $length = 0x000F; // Number of bytes to follow
+
+        $pnn = $this->activePane; // Pane position
+        $rwAct = $rwFirst; // Active row
+        $colAct = $colFirst; // Active column
+        $irefAct = 0; // Active cell ref
+        $cref = 1; // Number of refs
+
+        if (!isset($rwLast)) {
+            $rwLast = $rwFirst; // Last  row in reference
+        }
+        if (!isset($colLast)) {
+            $colLast = $colFirst; // Last  col in reference
+        }
+
+        // Swap last row/col for first row/col as necessary
+        if ($rwFirst > $rwLast) {
+            [$rwFirst, $rwLast] = [$rwLast, $rwFirst];
+        }
+
+        if ($colFirst > $colLast) {
+            [$colFirst, $colLast] = [$colLast, $colFirst];
+        }
+
+        $header = pack('vv', $record, $length);
+        $data = pack('CvvvvvvCC', $pnn, $rwAct, $colAct, $irefAct, $cref, $rwFirst, $rwLast, $colFirst, $colLast);
+        $this->append($header . $data);
+    }
+
+    /**
+     * Store the MERGEDCELLS records for all ranges of merged cells.
+     */
+    private function writeMergedCells(): void
+    {
+        $mergeCells = $this->phpSheet->getMergeCells();
+        $countMergeCells = count($mergeCells);
+
+        if ($countMergeCells == 0) {
+            return;
+        }
+
+        // maximum allowed number of merged cells per record
+        $maxCountMergeCellsPerRecord = 1027;
+
+        // record identifier
+        $record = 0x00E5;
+
+        // counter for total number of merged cells treated so far by the writer
+        $i = 0;
+
+        // counter for number of merged cells written in record currently being written
+        $j = 0;
+
+        // initialize record data
+        $recordData = '';
+
+        // loop through the merged cells
+        foreach ($mergeCells as $mergeCell) {
+            ++$i;
+            ++$j;
+
+            // extract the row and column indexes
+            $range = Coordinate::splitRange($mergeCell);
+            [$first, $last] = $range[0];
+            [$firstColumn, $firstRow] = Coordinate::coordinateFromString($first);
+            [$lastColumn, $lastRow] = Coordinate::coordinateFromString($last);
+
+            $recordData .= pack('vvvv', $firstRow - 1, $lastRow - 1, Coordinate::columnIndexFromString($firstColumn) - 1, Coordinate::columnIndexFromString($lastColumn) - 1);
+
+            // flush record if we have reached limit for number of merged cells, or reached final merged cell
+            if ($j == $maxCountMergeCellsPerRecord || $i == $countMergeCells) {
+                $recordData = pack('v', $j) . $recordData;
+                $length = strlen($recordData);
+                $header = pack('vv', $record, $length);
+                $this->append($header . $recordData);
+
+                // initialize for next record, if any
+                $recordData = '';
+                $j = 0;
+            }
+        }
+    }
+
+    /**
+     * Write SHEETLAYOUT record.
+     */
+    private function writeSheetLayout(): void
+    {
+        if (!$this->phpSheet->isTabColorSet()) {
+            return;
+        }
+
+        $recordData = pack(
+            'vvVVVvv',
+            0x0862,
+            0x0000, // unused
+            0x00000000, // unused
+            0x00000000, // unused
+            0x00000014, // size of record data
+            $this->colors[$this->phpSheet->getTabColor()->getRGB()], // color index
+            0x0000        // unused
+        );
+
+        $length = strlen($recordData);
+
+        $record = 0x0862; // Record identifier
+        $header = pack('vv', $record, $length);
+        $this->append($header . $recordData);
+    }
+
+    /**
+     * Write SHEETPROTECTION.
+     */
+    private function writeSheetProtection(): void
+    {
+        // record identifier
+        $record = 0x0867;
+
+        // prepare options
+        $options = (int) !$this->phpSheet->getProtection()->getObjects()
+            | (int) !$this->phpSheet->getProtection()->getScenarios() << 1
+            | (int) !$this->phpSheet->getProtection()->getFormatCells() << 2
+            | (int) !$this->phpSheet->getProtection()->getFormatColumns() << 3
+            | (int) !$this->phpSheet->getProtection()->getFormatRows() << 4
+            | (int) !$this->phpSheet->getProtection()->getInsertColumns() << 5
+            | (int) !$this->phpSheet->getProtection()->getInsertRows() << 6
+            | (int) !$this->phpSheet->getProtection()->getInsertHyperlinks() << 7
+            | (int) !$this->phpSheet->getProtection()->getDeleteColumns() << 8
+            | (int) !$this->phpSheet->getProtection()->getDeleteRows() << 9
+            | (int) !$this->phpSheet->getProtection()->getSelectLockedCells() << 10
+            | (int) !$this->phpSheet->getProtection()->getSort() << 11
+            | (int) !$this->phpSheet->getProtection()->getAutoFilter() << 12
+            | (int) !$this->phpSheet->getProtection()->getPivotTables() << 13
+            | (int) !$this->phpSheet->getProtection()->getSelectUnlockedCells() << 14;
+
+        // record data
+        $recordData = pack(
+            'vVVCVVvv',
+            0x0867, // repeated record identifier
+            0x0000, // not used
+            0x0000, // not used
+            0x00, // not used
+            0x01000200, // unknown data
+            0xFFFFFFFF, // unknown data
+            $options, // options
+            0x0000 // not used
+        );
+
+        $length = strlen($recordData);
+        $header = pack('vv', $record, $length);
+
+        $this->append($header . $recordData);
+    }
+
+    /**
+     * Write BIFF record RANGEPROTECTION.
+     *
+     * Openoffice.org's Documentaion of the Microsoft Excel File Format uses term RANGEPROTECTION for these records
+     * Microsoft Office Excel 97-2007 Binary File Format Specification uses term FEAT for these records
+     */
+    private function writeRangeProtection(): void
+    {
+        foreach ($this->phpSheet->getProtectedCells() as $range => $password) {
+            // number of ranges, e.g. 'A1:B3 C20:D25'
+            $cellRanges = explode(' ', $range);
+            $cref = count($cellRanges);
+
+            $recordData = pack(
+                'vvVVvCVvVv',
+                0x0868,
+                0x00,
+                0x0000,
+                0x0000,
+                0x02,
+                0x0,
+                0x0000,
+                $cref,
+                0x0000,
+                0x00
+            );
+
+            foreach ($cellRanges as $cellRange) {
+                $recordData .= $this->writeBIFF8CellRangeAddressFixed($cellRange);
+            }
+
+            // the rgbFeat structure
+            $recordData .= pack(
+                'VV',
+                0x0000,
+                hexdec($password)
+            );
+
+            $recordData .= StringHelper::UTF8toBIFF8UnicodeLong('p' . md5($recordData));
+
+            $length = strlen($recordData);
+
+            $record = 0x0868; // Record identifier
+            $header = pack('vv', $record, $length);
+            $this->append($header . $recordData);
+        }
+    }
+
+    /**
+     * Writes the Excel BIFF PANE record.
+     * The panes can either be frozen or thawed (unfrozen).
+     * Frozen panes are specified in terms of an integer number of rows and columns.
+     * Thawed panes are specified in terms of Excel's units for rows and columns.
+     */
+    private function writePanes(): void
+    {
+        $panes = [];
+        if ($this->phpSheet->getFreezePane()) {
+            [$column, $row] = Coordinate::coordinateFromString($this->phpSheet->getFreezePane());
+            $panes[0] = Coordinate::columnIndexFromString($column) - 1;
+            $panes[1] = $row - 1;
+
+            [$leftMostColumn, $topRow] = Coordinate::coordinateFromString($this->phpSheet->getTopLeftCell());
+            //Coordinates are zero-based in xls files
+            $panes[2] = $topRow - 1;
+            $panes[3] = Coordinate::columnIndexFromString($leftMostColumn) - 1;
+        } else {
+            // thaw panes
+            return;
+        }
+
+        $x = $panes[0] ?? null;
+        $y = $panes[1] ?? null;
+        $rwTop = $panes[2] ?? null;
+        $colLeft = $panes[3] ?? null;
+        if (count($panes) > 4) { // if Active pane was received
+            $pnnAct = $panes[4];
+        } else {
+            $pnnAct = null;
+        }
+        $record = 0x0041; // Record identifier
+        $length = 0x000A; // Number of bytes to follow
+
+        // Code specific to frozen or thawed panes.
+        if ($this->phpSheet->getFreezePane()) {
+            // Set default values for $rwTop and $colLeft
+            if (!isset($rwTop)) {
+                $rwTop = $y;
+            }
+            if (!isset($colLeft)) {
+                $colLeft = $x;
+            }
+        } else {
+            // Set default values for $rwTop and $colLeft
+            if (!isset($rwTop)) {
+                $rwTop = 0;
+            }
+            if (!isset($colLeft)) {
+                $colLeft = 0;
+            }
+
+            // Convert Excel's row and column units to the internal units.
+            // The default row height is 12.75
+            // The default column width is 8.43
+            // The following slope and intersection values were interpolated.
+            //
+            $y = 20 * $y + 255;
+            $x = 113.879 * $x + 390;
+        }
+
+        // Determine which pane should be active. There is also the undocumented
+        // option to override this should it be necessary: may be removed later.
+        //
+        if (!isset($pnnAct)) {
+            if ($x != 0 && $y != 0) {
+                $pnnAct = 0; // Bottom right
+            }
+            if ($x != 0 && $y == 0) {
+                $pnnAct = 1; // Top right
+            }
+            if ($x == 0 && $y != 0) {
+                $pnnAct = 2; // Bottom left
+            }
+            if ($x == 0 && $y == 0) {
+                $pnnAct = 3; // Top left
+            }
+        }
+
+        $this->activePane = $pnnAct; // Used in writeSelection
+
+        $header = pack('vv', $record, $length);
+        $data = pack('vvvvv', $x, $y, $rwTop, $colLeft, $pnnAct);
+        $this->append($header . $data);
+    }
+
+    /**
+     * Store the page setup SETUP BIFF record.
+     */
+    private function writeSetup(): void
+    {
+        $record = 0x00A1; // Record identifier
+        $length = 0x0022; // Number of bytes to follow
+
+        $iPaperSize = $this->phpSheet->getPageSetup()->getPaperSize(); // Paper size
+
+        $iScale = $this->phpSheet->getPageSetup()->getScale() ?
+            $this->phpSheet->getPageSetup()->getScale() : 100; // Print scaling factor
+
+        $iPageStart = 0x01; // Starting page number
+        $iFitWidth = (int) $this->phpSheet->getPageSetup()->getFitToWidth(); // Fit to number of pages wide
+        $iFitHeight = (int) $this->phpSheet->getPageSetup()->getFitToHeight(); // Fit to number of pages high
+        $grbit = 0x00; // Option flags
+        $iRes = 0x0258; // Print resolution
+        $iVRes = 0x0258; // Vertical print resolution
+
+        $numHdr = $this->phpSheet->getPageMargins()->getHeader(); // Header Margin
+
+        $numFtr = $this->phpSheet->getPageMargins()->getFooter(); // Footer Margin
+        $iCopies = 0x01; // Number of copies
+
+        $fLeftToRight = 0x0; // Print over then down
+
+        // Page orientation
+        $fLandscape = ($this->phpSheet->getPageSetup()->getOrientation() == PageSetup::ORIENTATION_LANDSCAPE) ?
+            0x0 : 0x1;
+
+        $fNoPls = 0x0; // Setup not read from printer
+        $fNoColor = 0x0; // Print black and white
+        $fDraft = 0x0; // Print draft quality
+        $fNotes = 0x0; // Print notes
+        $fNoOrient = 0x0; // Orientation not set
+        $fUsePage = 0x0; // Use custom starting page
+
+        $grbit = $fLeftToRight;
+        $grbit |= $fLandscape << 1;
+        $grbit |= $fNoPls << 2;
+        $grbit |= $fNoColor << 3;
+        $grbit |= $fDraft << 4;
+        $grbit |= $fNotes << 5;
+        $grbit |= $fNoOrient << 6;
+        $grbit |= $fUsePage << 7;
+
+        $numHdr = pack('d', $numHdr);
+        $numFtr = pack('d', $numFtr);
+        if (self::getByteOrder()) { // if it's Big Endian
+            $numHdr = strrev($numHdr);
+            $numFtr = strrev($numFtr);
+        }
+
+        $header = pack('vv', $record, $length);
+        $data1 = pack('vvvvvvvv', $iPaperSize, $iScale, $iPageStart, $iFitWidth, $iFitHeight, $grbit, $iRes, $iVRes);
+        $data2 = $numHdr . $numFtr;
+        $data3 = pack('v', $iCopies);
+        $this->append($header . $data1 . $data2 . $data3);
+    }
+
+    /**
+     * Store the header caption BIFF record.
+     */
+    private function writeHeader(): void
+    {
+        $record = 0x0014; // Record identifier
+
+        /* removing for now
+        // need to fix character count (multibyte!)
+        if (strlen($this->phpSheet->getHeaderFooter()->getOddHeader()) <= 255) {
+            $str      = $this->phpSheet->getHeaderFooter()->getOddHeader();       // header string
+        } else {
+            $str = '';
+        }
+        */
+
+        $recordData = StringHelper::UTF8toBIFF8UnicodeLong($this->phpSheet->getHeaderFooter()->getOddHeader());
+        $length = strlen($recordData);
+
+        $header = pack('vv', $record, $length);
+
+        $this->append($header . $recordData);
+    }
+
+    /**
+     * Store the footer caption BIFF record.
+     */
+    private function writeFooter(): void
+    {
+        $record = 0x0015; // Record identifier
+
+        /* removing for now
+        // need to fix character count (multibyte!)
+        if (strlen($this->phpSheet->getHeaderFooter()->getOddFooter()) <= 255) {
+            $str = $this->phpSheet->getHeaderFooter()->getOddFooter();
+        } else {
+            $str = '';
+        }
+        */
+
+        $recordData = StringHelper::UTF8toBIFF8UnicodeLong($this->phpSheet->getHeaderFooter()->getOddFooter());
+        $length = strlen($recordData);
+
+        $header = pack('vv', $record, $length);
+
+        $this->append($header . $recordData);
+    }
+
+    /**
+     * Store the horizontal centering HCENTER BIFF record.
+     */
+    private function writeHcenter(): void
+    {
+        $record = 0x0083; // Record identifier
+        $length = 0x0002; // Bytes to follow
+
+        $fHCenter = $this->phpSheet->getPageSetup()->getHorizontalCentered() ? 1 : 0; // Horizontal centering
+
+        $header = pack('vv', $record, $length);
+        $data = pack('v', $fHCenter);
+
+        $this->append($header . $data);
+    }
+
+    /**
+     * Store the vertical centering VCENTER BIFF record.
+     */
+    private function writeVcenter(): void
+    {
+        $record = 0x0084; // Record identifier
+        $length = 0x0002; // Bytes to follow
+
+        $fVCenter = $this->phpSheet->getPageSetup()->getVerticalCentered() ? 1 : 0; // Horizontal centering
+
+        $header = pack('vv', $record, $length);
+        $data = pack('v', $fVCenter);
+        $this->append($header . $data);
+    }
+
+    /**
+     * Store the LEFTMARGIN BIFF record.
+     */
+    private function writeMarginLeft(): void
+    {
+        $record = 0x0026; // Record identifier
+        $length = 0x0008; // Bytes to follow
+
+        $margin = $this->phpSheet->getPageMargins()->getLeft(); // Margin in inches
+
+        $header = pack('vv', $record, $length);
+        $data = pack('d', $margin);
+        if (self::getByteOrder()) { // if it's Big Endian
+            $data = strrev($data);
+        }
+
+        $this->append($header . $data);
+    }
+
+    /**
+     * Store the RIGHTMARGIN BIFF record.
+     */
+    private function writeMarginRight(): void
+    {
+        $record = 0x0027; // Record identifier
+        $length = 0x0008; // Bytes to follow
+
+        $margin = $this->phpSheet->getPageMargins()->getRight(); // Margin in inches
+
+        $header = pack('vv', $record, $length);
+        $data = pack('d', $margin);
+        if (self::getByteOrder()) { // if it's Big Endian
+            $data = strrev($data);
+        }
+
+        $this->append($header . $data);
+    }
+
+    /**
+     * Store the TOPMARGIN BIFF record.
+     */
+    private function writeMarginTop(): void
+    {
+        $record = 0x0028; // Record identifier
+        $length = 0x0008; // Bytes to follow
+
+        $margin = $this->phpSheet->getPageMargins()->getTop(); // Margin in inches
+
+        $header = pack('vv', $record, $length);
+        $data = pack('d', $margin);
+        if (self::getByteOrder()) { // if it's Big Endian
+            $data = strrev($data);
+        }
+
+        $this->append($header . $data);
+    }
+
+    /**
+     * Store the BOTTOMMARGIN BIFF record.
+     */
+    private function writeMarginBottom(): void
+    {
+        $record = 0x0029; // Record identifier
+        $length = 0x0008; // Bytes to follow
+
+        $margin = $this->phpSheet->getPageMargins()->getBottom(); // Margin in inches
+
+        $header = pack('vv', $record, $length);
+        $data = pack('d', $margin);
+        if (self::getByteOrder()) { // if it's Big Endian
+            $data = strrev($data);
+        }
+
+        $this->append($header . $data);
+    }
+
+    /**
+     * Write the PRINTHEADERS BIFF record.
+     */
+    private function writePrintHeaders(): void
+    {
+        $record = 0x002a; // Record identifier
+        $length = 0x0002; // Bytes to follow
+
+        $fPrintRwCol = $this->printHeaders; // Boolean flag
+
+        $header = pack('vv', $record, $length);
+        $data = pack('v', $fPrintRwCol);
+        $this->append($header . $data);
+    }
+
+    /**
+     * Write the PRINTGRIDLINES BIFF record. Must be used in conjunction with the
+     * GRIDSET record.
+     */
+    private function writePrintGridlines(): void
+    {
+        $record = 0x002b; // Record identifier
+        $length = 0x0002; // Bytes to follow
+
+        $fPrintGrid = $this->phpSheet->getPrintGridlines() ? 1 : 0; // Boolean flag
+
+        $header = pack('vv', $record, $length);
+        $data = pack('v', $fPrintGrid);
+        $this->append($header . $data);
+    }
+
+    /**
+     * Write the GRIDSET BIFF record. Must be used in conjunction with the
+     * PRINTGRIDLINES record.
+     */
+    private function writeGridset(): void
+    {
+        $record = 0x0082; // Record identifier
+        $length = 0x0002; // Bytes to follow
+
+        $fGridSet = !$this->phpSheet->getPrintGridlines(); // Boolean flag
+
+        $header = pack('vv', $record, $length);
+        $data = pack('v', $fGridSet);
+        $this->append($header . $data);
+    }
+
+    /**
+     * Write the AUTOFILTERINFO BIFF record. This is used to configure the number of autofilter select used in the sheet.
+     */
+    private function writeAutoFilterInfo(): void
+    {
+        $record = 0x009D; // Record identifier
+        $length = 0x0002; // Bytes to follow
+
+        $rangeBounds = Coordinate::rangeBoundaries($this->phpSheet->getAutoFilter()->getRange());
+        $iNumFilters = 1 + $rangeBounds[1][0] - $rangeBounds[0][0];
+
+        $header = pack('vv', $record, $length);
+        $data = pack('v', $iNumFilters);
+        $this->append($header . $data);
+    }
+
+    /**
+     * Write the GUTS BIFF record. This is used to configure the gutter margins
+     * where Excel outline symbols are displayed. The visibility of the gutters is
+     * controlled by a flag in WSBOOL.
+     *
+     * @see writeWsbool()
+     */
+    private function writeGuts(): void
+    {
+        $record = 0x0080; // Record identifier
+        $length = 0x0008; // Bytes to follow
+
+        $dxRwGut = 0x0000; // Size of row gutter
+        $dxColGut = 0x0000; // Size of col gutter
+
+        // determine maximum row outline level
+        $maxRowOutlineLevel = 0;
+        foreach ($this->phpSheet->getRowDimensions() as $rowDimension) {
+            $maxRowOutlineLevel = max($maxRowOutlineLevel, $rowDimension->getOutlineLevel());
+        }
+
+        $col_level = 0;
+
+        // Calculate the maximum column outline level. The equivalent calculation
+        // for the row outline level is carried out in writeRow().
+        $colcount = count($this->columnInfo);
+        for ($i = 0; $i < $colcount; ++$i) {
+            $col_level = max($this->columnInfo[$i][5], $col_level);
+        }
+
+        // Set the limits for the outline levels (0 <= x <= 7).
+        $col_level = max(0, min($col_level, 7));
+
+        // The displayed level is one greater than the max outline levels
+        if ($maxRowOutlineLevel) {
+            ++$maxRowOutlineLevel;
+        }
+        if ($col_level) {
+            ++$col_level;
+        }
+
+        $header = pack('vv', $record, $length);
+        $data = pack('vvvv', $dxRwGut, $dxColGut, $maxRowOutlineLevel, $col_level);
+
+        $this->append($header . $data);
+    }
+
+    /**
+     * Write the WSBOOL BIFF record, mainly for fit-to-page. Used in conjunction
+     * with the SETUP record.
+     */
+    private function writeWsbool(): void
+    {
+        $record = 0x0081; // Record identifier
+        $length = 0x0002; // Bytes to follow
+        $grbit = 0x0000;
+
+        // The only option that is of interest is the flag for fit to page. So we
+        // set all the options in one go.
+        //
+        // Set the option flags
+        $grbit |= 0x0001; // Auto page breaks visible
+        if ($this->outlineStyle) {
+            $grbit |= 0x0020; // Auto outline styles
+        }
+        if ($this->phpSheet->getShowSummaryBelow()) {
+            $grbit |= 0x0040; // Outline summary below
+        }
+        if ($this->phpSheet->getShowSummaryRight()) {
+            $grbit |= 0x0080; // Outline summary right
+        }
+        if ($this->phpSheet->getPageSetup()->getFitToPage()) {
+            $grbit |= 0x0100; // Page setup fit to page
+        }
+        if ($this->outlineOn) {
+            $grbit |= 0x0400; // Outline symbols displayed
+        }
+
+        $header = pack('vv', $record, $length);
+        $data = pack('v', $grbit);
+        $this->append($header . $data);
+    }
+
+    /**
+     * Write the HORIZONTALPAGEBREAKS and VERTICALPAGEBREAKS BIFF records.
+     */
+    private function writeBreaks(): void
+    {
+        // initialize
+        $vbreaks = [];
+        $hbreaks = [];
+
+        foreach ($this->phpSheet->getBreaks() as $cell => $breakType) {
+            // Fetch coordinates
+            $coordinates = Coordinate::coordinateFromString($cell);
+
+            // Decide what to do by the type of break
+            switch ($breakType) {
+                case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_COLUMN:
+                    // Add to list of vertical breaks
+                    $vbreaks[] = Coordinate::columnIndexFromString($coordinates[0]) - 1;
+
+                    break;
+                case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_ROW:
+                    // Add to list of horizontal breaks
+                    $hbreaks[] = $coordinates[1];
+
+                    break;
+                case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_NONE:
+                default:
+                    // Nothing to do
+                    break;
+            }
+        }
+
+        //horizontal page breaks
+        if (!empty($hbreaks)) {
+            // Sort and filter array of page breaks
+            sort($hbreaks, SORT_NUMERIC);
+            if ($hbreaks[0] == 0) { // don't use first break if it's 0
+                array_shift($hbreaks);
+            }
+
+            $record = 0x001b; // Record identifier
+            $cbrk = count($hbreaks); // Number of page breaks
+            $length = 2 + 6 * $cbrk; // Bytes to follow
+
+            $header = pack('vv', $record, $length);
+            $data = pack('v', $cbrk);
+
+            // Append each page break
+            foreach ($hbreaks as $hbreak) {
+                $data .= pack('vvv', $hbreak, 0x0000, 0x00ff);
+            }
+
+            $this->append($header . $data);
+        }
+
+        // vertical page breaks
+        if (!empty($vbreaks)) {
+            // 1000 vertical pagebreaks appears to be an internal Excel 5 limit.
+            // It is slightly higher in Excel 97/200, approx. 1026
+            $vbreaks = array_slice($vbreaks, 0, 1000);
+
+            // Sort and filter array of page breaks
+            sort($vbreaks, SORT_NUMERIC);
+            if ($vbreaks[0] == 0) { // don't use first break if it's 0
+                array_shift($vbreaks);
+            }
+
+            $record = 0x001a; // Record identifier
+            $cbrk = count($vbreaks); // Number of page breaks
+            $length = 2 + 6 * $cbrk; // Bytes to follow
+
+            $header = pack('vv', $record, $length);
+            $data = pack('v', $cbrk);
+
+            // Append each page break
+            foreach ($vbreaks as $vbreak) {
+                $data .= pack('vvv', $vbreak, 0x0000, 0xffff);
+            }
+
+            $this->append($header . $data);
+        }
+    }
+
+    /**
+     * Set the Biff PROTECT record to indicate that the worksheet is protected.
+     */
+    private function writeProtect(): void
+    {
+        // Exit unless sheet protection has been specified
+        if (!$this->phpSheet->getProtection()->getSheet()) {
+            return;
+        }
+
+        $record = 0x0012; // Record identifier
+        $length = 0x0002; // Bytes to follow
+
+        $fLock = 1; // Worksheet is protected
+
+        $header = pack('vv', $record, $length);
+        $data = pack('v', $fLock);
+
+        $this->append($header . $data);
+    }
+
+    /**
+     * Write SCENPROTECT.
+     */
+    private function writeScenProtect(): void
+    {
+        // Exit if sheet protection is not active
+        if (!$this->phpSheet->getProtection()->getSheet()) {
+            return;
+        }
+
+        // Exit if scenarios are not protected
+        if (!$this->phpSheet->getProtection()->getScenarios()) {
+            return;
+        }
+
+        $record = 0x00DD; // Record identifier
+        $length = 0x0002; // Bytes to follow
+
+        $header = pack('vv', $record, $length);
+        $data = pack('v', 1);
+
+        $this->append($header . $data);
+    }
+
+    /**
+     * Write OBJECTPROTECT.
+     */
+    private function writeObjectProtect(): void
+    {
+        // Exit if sheet protection is not active
+        if (!$this->phpSheet->getProtection()->getSheet()) {
+            return;
+        }
+
+        // Exit if objects are not protected
+        if (!$this->phpSheet->getProtection()->getObjects()) {
+            return;
+        }
+
+        $record = 0x0063; // Record identifier
+        $length = 0x0002; // Bytes to follow
+
+        $header = pack('vv', $record, $length);
+        $data = pack('v', 1);
+
+        $this->append($header . $data);
+    }
+
+    /**
+     * Write the worksheet PASSWORD record.
+     */
+    private function writePassword(): void
+    {
+        // Exit unless sheet protection and password have been specified
+        if (!$this->phpSheet->getProtection()->getSheet() || !$this->phpSheet->getProtection()->getPassword()) {
+            return;
+        }
+
+        $record = 0x0013; // Record identifier
+        $length = 0x0002; // Bytes to follow
+
+        $wPassword = hexdec($this->phpSheet->getProtection()->getPassword()); // Encoded password
+
+        $header = pack('vv', $record, $length);
+        $data = pack('v', $wPassword);
+
+        $this->append($header . $data);
+    }
+
+    /**
+     * Insert a 24bit bitmap image in a worksheet.
+     *
+     * @param int $row The row we are going to insert the bitmap into
+     * @param int $col The column we are going to insert the bitmap into
+     * @param mixed $bitmap The bitmap filename or GD-image resource
+     * @param int $x the horizontal position (offset) of the image inside the cell
+     * @param int $y the vertical position (offset) of the image inside the cell
+     * @param float $scale_x The horizontal scale
+     * @param float $scale_y The vertical scale
+     */
+    public function insertBitmap($row, $col, $bitmap, $x = 0, $y = 0, $scale_x = 1, $scale_y = 1): void
+    {
+        $bitmap_array = (is_resource($bitmap) ? $this->processBitmapGd($bitmap) : $this->processBitmap($bitmap));
+        [$width, $height, $size, $data] = $bitmap_array;
+
+        // Scale the frame of the image.
+        $width *= $scale_x;
+        $height *= $scale_y;
+
+        // Calculate the vertices of the image and write the OBJ record
+        $this->positionImage($col, $row, $x, $y, $width, $height);
+
+        // Write the IMDATA record to store the bitmap data
+        $record = 0x007f;
+        $length = 8 + $size;
+        $cf = 0x09;
+        $env = 0x01;
+        $lcb = $size;
+
+        $header = pack('vvvvV', $record, $length, $cf, $env, $lcb);
+        $this->append($header . $data);
+    }
+
+    /**
+     * Calculate the vertices that define the position of the image as required by
+     * the OBJ record.
+     *
+     *         +------------+------------+
+     *         |     A      |      B     |
+     *   +-----+------------+------------+
+     *   |     |(x1,y1)     |            |
+     *   |  1  |(A1)._______|______      |
+     *   |     |    |              |     |
+     *   |     |    |              |     |
+     *   +-----+----|    BITMAP    |-----+
+     *   |     |    |              |     |
+     *   |  2  |    |______________.     |
+     *   |     |            |        (B2)|
+     *   |     |            |     (x2,y2)|
+     *   +---- +------------+------------+
+     *
+     * Example of a bitmap that covers some of the area from cell A1 to cell B2.
+     *
+     * Based on the width and height of the bitmap we need to calculate 8 vars:
+     *     $col_start, $row_start, $col_end, $row_end, $x1, $y1, $x2, $y2.
+     * The width and height of the cells are also variable and have to be taken into
+     * account.
+     * The values of $col_start and $row_start are passed in from the calling
+     * function. The values of $col_end and $row_end are calculated by subtracting
+     * the width and height of the bitmap from the width and height of the
+     * underlying cells.
+     * The vertices are expressed as a percentage of the underlying cell width as
+     * follows (rhs values are in pixels):
+     *
+     *       x1 = X / W *1024
+     *       y1 = Y / H *256
+     *       x2 = (X-1) / W *1024
+     *       y2 = (Y-1) / H *256
+     *
+     *       Where:  X is distance from the left side of the underlying cell
+     *               Y is distance from the top of the underlying cell
+     *               W is the width of the cell
+     *               H is the height of the cell
+     * The SDK incorrectly states that the height should be expressed as a
+     *        percentage of 1024.
+     *
+     * @param int $col_start Col containing upper left corner of object
+     * @param int $row_start Row containing top left corner of object
+     * @param int $x1 Distance to left side of object
+     * @param int $y1 Distance to top of object
+     * @param int $width Width of image frame
+     * @param int $height Height of image frame
+     */
+    public function positionImage($col_start, $row_start, $x1, $y1, $width, $height): void
+    {
+        // Initialise end cell to the same as the start cell
+        $col_end = $col_start; // Col containing lower right corner of object
+        $row_end = $row_start; // Row containing bottom right corner of object
+
+        // Zero the specified offset if greater than the cell dimensions
+        if ($x1 >= Xls::sizeCol($this->phpSheet, Coordinate::stringFromColumnIndex($col_start + 1))) {
+            $x1 = 0;
+        }
+        if ($y1 >= Xls::sizeRow($this->phpSheet, $row_start + 1)) {
+            $y1 = 0;
+        }
+
+        $width = $width + $x1 - 1;
+        $height = $height + $y1 - 1;
+
+        // Subtract the underlying cell widths to find the end cell of the image
+        while ($width >= Xls::sizeCol($this->phpSheet, Coordinate::stringFromColumnIndex($col_end + 1))) {
+            $width -= Xls::sizeCol($this->phpSheet, Coordinate::stringFromColumnIndex($col_end + 1));
+            ++$col_end;
+        }
+
+        // Subtract the underlying cell heights to find the end cell of the image
+        while ($height >= Xls::sizeRow($this->phpSheet, $row_end + 1)) {
+            $height -= Xls::sizeRow($this->phpSheet, $row_end + 1);
+            ++$row_end;
+        }
+
+        // Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell
+        // with zero eight or width.
+        //
+        if (Xls::sizeCol($this->phpSheet, Coordinate::stringFromColumnIndex($col_start + 1)) == 0) {
+            return;
+        }
+        if (Xls::sizeCol($this->phpSheet, Coordinate::stringFromColumnIndex($col_end + 1)) == 0) {
+            return;
+        }
+        if (Xls::sizeRow($this->phpSheet, $row_start + 1) == 0) {
+            return;
+        }
+        if (Xls::sizeRow($this->phpSheet, $row_end + 1) == 0) {
+            return;
+        }
+
+        // Convert the pixel values to the percentage value expected by Excel
+        $x1 = $x1 / Xls::sizeCol($this->phpSheet, Coordinate::stringFromColumnIndex($col_start + 1)) * 1024;
+        $y1 = $y1 / Xls::sizeRow($this->phpSheet, $row_start + 1) * 256;
+        $x2 = $width / Xls::sizeCol($this->phpSheet, Coordinate::stringFromColumnIndex($col_end + 1)) * 1024; // Distance to right side of object
+        $y2 = $height / Xls::sizeRow($this->phpSheet, $row_end + 1) * 256; // Distance to bottom of object
+
+        $this->writeObjPicture($col_start, $x1, $row_start, $y1, $col_end, $x2, $row_end, $y2);
+    }
+
+    /**
+     * Store the OBJ record that precedes an IMDATA record. This could be generalise
+     * to support other Excel objects.
+     *
+     * @param int $colL Column containing upper left corner of object
+     * @param int $dxL Distance from left side of cell
+     * @param int $rwT Row containing top left corner of object
+     * @param int $dyT Distance from top of cell
+     * @param int $colR Column containing lower right corner of object
+     * @param int $dxR Distance from right of cell
+     * @param int $rwB Row containing bottom right corner of object
+     * @param int $dyB Distance from bottom of cell
+     */
+    private function writeObjPicture($colL, $dxL, $rwT, $dyT, $colR, $dxR, $rwB, $dyB): void
+    {
+        $record = 0x005d; // Record identifier
+        $length = 0x003c; // Bytes to follow
+
+        $cObj = 0x0001; // Count of objects in file (set to 1)
+        $OT = 0x0008; // Object type. 8 = Picture
+        $id = 0x0001; // Object ID
+        $grbit = 0x0614; // Option flags
+
+        $cbMacro = 0x0000; // Length of FMLA structure
+        $Reserved1 = 0x0000; // Reserved
+        $Reserved2 = 0x0000; // Reserved
+
+        $icvBack = 0x09; // Background colour
+        $icvFore = 0x09; // Foreground colour
+        $fls = 0x00; // Fill pattern
+        $fAuto = 0x00; // Automatic fill
+        $icv = 0x08; // Line colour
+        $lns = 0xff; // Line style
+        $lnw = 0x01; // Line weight
+        $fAutoB = 0x00; // Automatic border
+        $frs = 0x0000; // Frame style
+        $cf = 0x0009; // Image format, 9 = bitmap
+        $Reserved3 = 0x0000; // Reserved
+        $cbPictFmla = 0x0000; // Length of FMLA structure
+        $Reserved4 = 0x0000; // Reserved
+        $grbit2 = 0x0001; // Option flags
+        $Reserved5 = 0x0000; // Reserved
+
+        $header = pack('vv', $record, $length);
+        $data = pack('V', $cObj);
+        $data .= pack('v', $OT);
+        $data .= pack('v', $id);
+        $data .= pack('v', $grbit);
+        $data .= pack('v', $colL);
+        $data .= pack('v', $dxL);
+        $data .= pack('v', $rwT);
+        $data .= pack('v', $dyT);
+        $data .= pack('v', $colR);
+        $data .= pack('v', $dxR);
+        $data .= pack('v', $rwB);
+        $data .= pack('v', $dyB);
+        $data .= pack('v', $cbMacro);
+        $data .= pack('V', $Reserved1);
+        $data .= pack('v', $Reserved2);
+        $data .= pack('C', $icvBack);
+        $data .= pack('C', $icvFore);
+        $data .= pack('C', $fls);
+        $data .= pack('C', $fAuto);
+        $data .= pack('C', $icv);
+        $data .= pack('C', $lns);
+        $data .= pack('C', $lnw);
+        $data .= pack('C', $fAutoB);
+        $data .= pack('v', $frs);
+        $data .= pack('V', $cf);
+        $data .= pack('v', $Reserved3);
+        $data .= pack('v', $cbPictFmla);
+        $data .= pack('v', $Reserved4);
+        $data .= pack('v', $grbit2);
+        $data .= pack('V', $Reserved5);
+
+        $this->append($header . $data);
+    }
+
+    /**
+     * Convert a GD-image into the internal format.
+     *
+     * @param resource $image The image to process
+     *
+     * @return array Array with data and properties of the bitmap
+     */
+    public function processBitmapGd($image)
+    {
+        $width = imagesx($image);
+        $height = imagesy($image);
+
+        $data = pack('Vvvvv', 0x000c, $width, $height, 0x01, 0x18);
+        for ($j = $height; --$j;) {
+            for ($i = 0; $i < $width; ++$i) {
+                $color = imagecolorsforindex($image, imagecolorat($image, $i, $j));
+                foreach (['red', 'green', 'blue'] as $key) {
+                    $color[$key] = $color[$key] + round((255 - $color[$key]) * $color['alpha'] / 127);
+                }
+                $data .= chr($color['blue']) . chr($color['green']) . chr($color['red']);
+            }
+            if (3 * $width % 4) {
+                $data .= str_repeat("\x00", 4 - 3 * $width % 4);
+            }
+        }
+
+        return [$width, $height, strlen($data), $data];
+    }
+
+    /**
+     * Convert a 24 bit bitmap into the modified internal format used by Windows.
+     * This is described in BITMAPCOREHEADER and BITMAPCOREINFO structures in the
+     * MSDN library.
+     *
+     * @param string $bitmap The bitmap to process
+     *
+     * @return array Array with data and properties of the bitmap
+     */
+    public function processBitmap($bitmap)
+    {
+        // Open file.
+        $bmp_fd = @fopen($bitmap, 'rb');
+        if (!$bmp_fd) {
+            throw new WriterException("Couldn't import $bitmap");
+        }
+
+        // Slurp the file into a string.
+        $data = fread($bmp_fd, filesize($bitmap));
+
+        // Check that the file is big enough to be a bitmap.
+        if (strlen($data) <= 0x36) {
+            throw new WriterException("$bitmap doesn't contain enough data.\n");
+        }
+
+        // The first 2 bytes are used to identify the bitmap.
+        $identity = unpack('A2ident', $data);
+        if ($identity['ident'] != 'BM') {
+            throw new WriterException("$bitmap doesn't appear to be a valid bitmap image.\n");
+        }
+
+        // Remove bitmap data: ID.
+        $data = substr($data, 2);
+
+        // Read and remove the bitmap size. This is more reliable than reading
+        // the data size at offset 0x22.
+        //
+        $size_array = unpack('Vsa', substr($data, 0, 4));
+        $size = $size_array['sa'];
+        $data = substr($data, 4);
+        $size -= 0x36; // Subtract size of bitmap header.
+        $size += 0x0C; // Add size of BIFF header.
+
+        // Remove bitmap data: reserved, offset, header length.
+        $data = substr($data, 12);
+
+        // Read and remove the bitmap width and height. Verify the sizes.
+        $width_and_height = unpack('V2', substr($data, 0, 8));
+        $width = $width_and_height[1];
+        $height = $width_and_height[2];
+        $data = substr($data, 8);
+        if ($width > 0xFFFF) {
+            throw new WriterException("$bitmap: largest image width supported is 65k.\n");
+        }
+        if ($height > 0xFFFF) {
+            throw new WriterException("$bitmap: largest image height supported is 65k.\n");
+        }
+
+        // Read and remove the bitmap planes and bpp data. Verify them.
+        $planes_and_bitcount = unpack('v2', substr($data, 0, 4));
+        $data = substr($data, 4);
+        if ($planes_and_bitcount[2] != 24) { // Bitcount
+            throw new WriterException("$bitmap isn't a 24bit true color bitmap.\n");
+        }
+        if ($planes_and_bitcount[1] != 1) {
+            throw new WriterException("$bitmap: only 1 plane supported in bitmap image.\n");
+        }
+
+        // Read and remove the bitmap compression. Verify compression.
+        $compression = unpack('Vcomp', substr($data, 0, 4));
+        $data = substr($data, 4);
+
+        if ($compression['comp'] != 0) {
+            throw new WriterException("$bitmap: compression not supported in bitmap image.\n");
+        }
+
+        // Remove bitmap data: data size, hres, vres, colours, imp. colours.
+        $data = substr($data, 20);
+
+        // Add the BITMAPCOREHEADER data
+        $header = pack('Vvvvv', 0x000c, $width, $height, 0x01, 0x18);
+        $data = $header . $data;
+
+        return [$width, $height, $size, $data];
+    }
+
+    /**
+     * Store the window zoom factor. This should be a reduced fraction but for
+     * simplicity we will store all fractions with a numerator of 100.
+     */
+    private function writeZoom(): void
+    {
+        // If scale is 100 we don't need to write a record
+        if ($this->phpSheet->getSheetView()->getZoomScale() == 100) {
+            return;
+        }
+
+        $record = 0x00A0; // Record identifier
+        $length = 0x0004; // Bytes to follow
+
+        $header = pack('vv', $record, $length);
+        $data = pack('vv', $this->phpSheet->getSheetView()->getZoomScale(), 100);
+        $this->append($header . $data);
+    }
+
+    /**
+     * Get Escher object.
+     *
+     * @return \PhpOffice\PhpSpreadsheet\Shared\Escher
+     */
+    public function getEscher()
+    {
+        return $this->escher;
+    }
+
+    /**
+     * Set Escher object.
+     *
+     * @param \PhpOffice\PhpSpreadsheet\Shared\Escher $pValue
+     */
+    public function setEscher(?\PhpOffice\PhpSpreadsheet\Shared\Escher $pValue = null): void
+    {
+        $this->escher = $pValue;
+    }
+
+    /**
+     * Write MSODRAWING record.
+     */
+    private function writeMsoDrawing(): void
+    {
+        // write the Escher stream if necessary
+        if (isset($this->escher)) {
+            $writer = new Escher($this->escher);
+            $data = $writer->close();
+            $spOffsets = $writer->getSpOffsets();
+            $spTypes = $writer->getSpTypes();
+            // write the neccesary MSODRAWING, OBJ records
+
+            // split the Escher stream
+            $spOffsets[0] = 0;
+            $nm = count($spOffsets) - 1; // number of shapes excluding first shape
+            for ($i = 1; $i <= $nm; ++$i) {
+                // MSODRAWING record
+                $record = 0x00EC; // Record identifier
+
+                // chunk of Escher stream for one shape
+                $dataChunk = substr($data, $spOffsets[$i - 1], $spOffsets[$i] - $spOffsets[$i - 1]);
+
+                $length = strlen($dataChunk);
+                $header = pack('vv', $record, $length);
+
+                $this->append($header . $dataChunk);
+
+                // OBJ record
+                $record = 0x005D; // record identifier
+                $objData = '';
+
+                // ftCmo
+                if ($spTypes[$i] == 0x00C9) {
+                    // Add ftCmo (common object data) subobject
+                    $objData .=
+                        pack(
+                            'vvvvvVVV',
+                            0x0015, // 0x0015 = ftCmo
+                            0x0012, // length of ftCmo data
+                            0x0014, // object type, 0x0014 = filter
+                            $i, // object id number, Excel seems to use 1-based index, local for the sheet
+                            0x2101, // option flags, 0x2001 is what OpenOffice.org uses
+                            0, // reserved
+                            0, // reserved
+                            0  // reserved
+                        );
+
+                    // Add ftSbs Scroll bar subobject
+                    $objData .= pack('vv', 0x00C, 0x0014);
+                    $objData .= pack('H*', '0000000000000000640001000A00000010000100');
+                    // Add ftLbsData (List box data) subobject
+                    $objData .= pack('vv', 0x0013, 0x1FEE);
+                    $objData .= pack('H*', '00000000010001030000020008005700');
+                } else {
+                    // Add ftCmo (common object data) subobject
+                    $objData .=
+                        pack(
+                            'vvvvvVVV',
+                            0x0015, // 0x0015 = ftCmo
+                            0x0012, // length of ftCmo data
+                            0x0008, // object type, 0x0008 = picture
+                            $i, // object id number, Excel seems to use 1-based index, local for the sheet
+                            0x6011, // option flags, 0x6011 is what OpenOffice.org uses
+                            0, // reserved
+                            0, // reserved
+                            0  // reserved
+                        );
+                }
+
+                // ftEnd
+                $objData .=
+                    pack(
+                        'vv',
+                        0x0000, // 0x0000 = ftEnd
+                        0x0000  // length of ftEnd data
+                    );
+
+                $length = strlen($objData);
+                $header = pack('vv', $record, $length);
+                $this->append($header . $objData);
+            }
+        }
+    }
+
+    /**
+     * Store the DATAVALIDATIONS and DATAVALIDATION records.
+     */
+    private function writeDataValidity(): void
+    {
+        // Datavalidation collection
+        $dataValidationCollection = $this->phpSheet->getDataValidationCollection();
+
+        // Write data validations?
+        if (!empty($dataValidationCollection)) {
+            // DATAVALIDATIONS record
+            $record = 0x01B2; // Record identifier
+            $length = 0x0012; // Bytes to follow
+
+            $grbit = 0x0000; // Prompt box at cell, no cached validity data at DV records
+            $horPos = 0x00000000; // Horizontal position of prompt box, if fixed position
+            $verPos = 0x00000000; // Vertical position of prompt box, if fixed position
+            $objId = 0xFFFFFFFF; // Object identifier of drop down arrow object, or -1 if not visible
+
+            $header = pack('vv', $record, $length);
+            $data = pack('vVVVV', $grbit, $horPos, $verPos, $objId, count($dataValidationCollection));
+            $this->append($header . $data);
+
+            // DATAVALIDATION records
+            $record = 0x01BE; // Record identifier
+
+            foreach ($dataValidationCollection as $cellCoordinate => $dataValidation) {
+                // initialize record data
+                $data = '';
+
+                // options
+                $options = 0x00000000;
+
+                // data type
+                $type = 0x00;
+                switch ($dataValidation->getType()) {
+                    case DataValidation::TYPE_NONE:
+                        $type = 0x00;
+
+                        break;
+                    case DataValidation::TYPE_WHOLE:
+                        $type = 0x01;
+
+                        break;
+                    case DataValidation::TYPE_DECIMAL:
+                        $type = 0x02;
+
+                        break;
+                    case DataValidation::TYPE_LIST:
+                        $type = 0x03;
+
+                        break;
+                    case DataValidation::TYPE_DATE:
+                        $type = 0x04;
+
+                        break;
+                    case DataValidation::TYPE_TIME:
+                        $type = 0x05;
+
+                        break;
+                    case DataValidation::TYPE_TEXTLENGTH:
+                        $type = 0x06;
+
+                        break;
+                    case DataValidation::TYPE_CUSTOM:
+                        $type = 0x07;
+
+                        break;
+                }
+
+                $options |= $type << 0;
+
+                // error style
+                $errorStyle = 0x00;
+                switch ($dataValidation->getErrorStyle()) {
+                    case DataValidation::STYLE_STOP:
+                        $errorStyle = 0x00;
+
+                        break;
+                    case DataValidation::STYLE_WARNING:
+                        $errorStyle = 0x01;
+
+                        break;
+                    case DataValidation::STYLE_INFORMATION:
+                        $errorStyle = 0x02;
+
+                        break;
+                }
+
+                $options |= $errorStyle << 4;
+
+                // explicit formula?
+                if ($type == 0x03 && preg_match('/^\".*\"$/', $dataValidation->getFormula1())) {
+                    $options |= 0x01 << 7;
+                }
+
+                // empty cells allowed
+                $options |= $dataValidation->getAllowBlank() << 8;
+
+                // show drop down
+                $options |= (!$dataValidation->getShowDropDown()) << 9;
+
+                // show input message
+                $options |= $dataValidation->getShowInputMessage() << 18;
+
+                // show error message
+                $options |= $dataValidation->getShowErrorMessage() << 19;
+
+                // condition operator
+                $operator = 0x00;
+                switch ($dataValidation->getOperator()) {
+                    case DataValidation::OPERATOR_BETWEEN:
+                        $operator = 0x00;
+
+                        break;
+                    case DataValidation::OPERATOR_NOTBETWEEN:
+                        $operator = 0x01;
+
+                        break;
+                    case DataValidation::OPERATOR_EQUAL:
+                        $operator = 0x02;
+
+                        break;
+                    case DataValidation::OPERATOR_NOTEQUAL:
+                        $operator = 0x03;
+
+                        break;
+                    case DataValidation::OPERATOR_GREATERTHAN:
+                        $operator = 0x04;
+
+                        break;
+                    case DataValidation::OPERATOR_LESSTHAN:
+                        $operator = 0x05;
+
+                        break;
+                    case DataValidation::OPERATOR_GREATERTHANOREQUAL:
+                        $operator = 0x06;
+
+                        break;
+                    case DataValidation::OPERATOR_LESSTHANOREQUAL:
+                        $operator = 0x07;
+
+                        break;
+                }
+
+                $options |= $operator << 20;
+
+                $data = pack('V', $options);
+
+                // prompt title
+                $promptTitle = $dataValidation->getPromptTitle() !== '' ?
+                    $dataValidation->getPromptTitle() : chr(0);
+                $data .= StringHelper::UTF8toBIFF8UnicodeLong($promptTitle);
+
+                // error title
+                $errorTitle = $dataValidation->getErrorTitle() !== '' ?
+                    $dataValidation->getErrorTitle() : chr(0);
+                $data .= StringHelper::UTF8toBIFF8UnicodeLong($errorTitle);
+
+                // prompt text
+                $prompt = $dataValidation->getPrompt() !== '' ?
+                    $dataValidation->getPrompt() : chr(0);
+                $data .= StringHelper::UTF8toBIFF8UnicodeLong($prompt);
+
+                // error text
+                $error = $dataValidation->getError() !== '' ?
+                    $dataValidation->getError() : chr(0);
+                $data .= StringHelper::UTF8toBIFF8UnicodeLong($error);
+
+                // formula 1
+                try {
+                    $formula1 = $dataValidation->getFormula1();
+                    if ($type == 0x03) { // list type
+                        $formula1 = str_replace(',', chr(0), $formula1);
+                    }
+                    $this->parser->parse($formula1);
+                    $formula1 = $this->parser->toReversePolish();
+                    $sz1 = strlen($formula1);
+                } catch (PhpSpreadsheetException $e) {
+                    $sz1 = 0;
+                    $formula1 = '';
+                }
+                $data .= pack('vv', $sz1, 0x0000);
+                $data .= $formula1;
+
+                // formula 2
+                try {
+                    $formula2 = $dataValidation->getFormula2();
+                    if ($formula2 === '') {
+                        throw new WriterException('No formula2');
+                    }
+                    $this->parser->parse($formula2);
+                    $formula2 = $this->parser->toReversePolish();
+                    $sz2 = strlen($formula2);
+                } catch (PhpSpreadsheetException $e) {
+                    $sz2 = 0;
+                    $formula2 = '';
+                }
+                $data .= pack('vv', $sz2, 0x0000);
+                $data .= $formula2;
+
+                // cell range address list
+                $data .= pack('v', 0x0001);
+                $data .= $this->writeBIFF8CellRangeAddressFixed($cellCoordinate);
+
+                $length = strlen($data);
+                $header = pack('vv', $record, $length);
+
+                $this->append($header . $data);
+            }
+        }
+    }
+
+    /**
+     * Map Error code.
+     *
+     * @param string $errorCode
+     *
+     * @return int
+     */
+    private static function mapErrorCode($errorCode)
+    {
+        switch ($errorCode) {
+            case '#NULL!':
+                return 0x00;
+            case '#DIV/0!':
+                return 0x07;
+            case '#VALUE!':
+                return 0x0F;
+            case '#REF!':
+                return 0x17;
+            case '#NAME?':
+                return 0x1D;
+            case '#NUM!':
+                return 0x24;
+            case '#N/A':
+                return 0x2A;
+        }
+
+        return 0;
+    }
+
+    /**
+     * Write PLV Record.
+     */
+    private function writePageLayoutView(): void
+    {
+        $record = 0x088B; // Record identifier
+        $length = 0x0010; // Bytes to follow
+
+        $rt = 0x088B; // 2
+        $grbitFrt = 0x0000; // 2
+        $reserved = 0x0000000000000000; // 8
+        $wScalvePLV = $this->phpSheet->getSheetView()->getZoomScale(); // 2
+
+        // The options flags that comprise $grbit
+        if ($this->phpSheet->getSheetView()->getView() == SheetView::SHEETVIEW_PAGE_LAYOUT) {
+            $fPageLayoutView = 1;
+        } else {
+            $fPageLayoutView = 0;
+        }
+        $fRulerVisible = 0;
+        $fWhitespaceHidden = 0;
+
+        $grbit = $fPageLayoutView; // 2
+        $grbit |= $fRulerVisible << 1;
+        $grbit |= $fWhitespaceHidden << 3;
+
+        $header = pack('vv', $record, $length);
+        $data = pack('vvVVvv', $rt, $grbitFrt, 0x00000000, 0x00000000, $wScalvePLV, $grbit);
+        $this->append($header . $data);
+    }
+
+    /**
+     * Write CFRule Record.
+     */
+    private function writeCFRule(Conditional $conditional): void
+    {
+        $record = 0x01B1; // Record identifier
+
+        // $type : Type of the CF
+        // $operatorType : Comparison operator
+        if ($conditional->getConditionType() == Conditional::CONDITION_EXPRESSION) {
+            $type = 0x02;
+            $operatorType = 0x00;
+        } elseif ($conditional->getConditionType() == Conditional::CONDITION_CELLIS) {
+            $type = 0x01;
+
+            switch ($conditional->getOperatorType()) {
+                case Conditional::OPERATOR_NONE:
+                    $operatorType = 0x00;
+
+                    break;
+                case Conditional::OPERATOR_EQUAL:
+                    $operatorType = 0x03;
+
+                    break;
+                case Conditional::OPERATOR_GREATERTHAN:
+                    $operatorType = 0x05;
+
+                    break;
+                case Conditional::OPERATOR_GREATERTHANOREQUAL:
+                    $operatorType = 0x07;
+
+                    break;
+                case Conditional::OPERATOR_LESSTHAN:
+                    $operatorType = 0x06;
+
+                    break;
+                case Conditional::OPERATOR_LESSTHANOREQUAL:
+                    $operatorType = 0x08;
+
+                    break;
+                case Conditional::OPERATOR_NOTEQUAL:
+                    $operatorType = 0x04;
+
+                    break;
+                case Conditional::OPERATOR_BETWEEN:
+                    $operatorType = 0x01;
+
+                    break;
+                // not OPERATOR_NOTBETWEEN 0x02
+            }
+        }
+
+        // $szValue1 : size of the formula data for first value or formula
+        // $szValue2 : size of the formula data for second value or formula
+        $arrConditions = $conditional->getConditions();
+        $numConditions = count($arrConditions);
+        if ($numConditions == 1) {
+            $szValue1 = ($arrConditions[0] <= 65535 ? 3 : 0x0000);
+            $szValue2 = 0x0000;
+            $operand1 = pack('Cv', 0x1E, $arrConditions[0]);
+            $operand2 = null;
+        } elseif ($numConditions == 2 && ($conditional->getOperatorType() == Conditional::OPERATOR_BETWEEN)) {
+            $szValue1 = ($arrConditions[0] <= 65535 ? 3 : 0x0000);
+            $szValue2 = ($arrConditions[1] <= 65535 ? 3 : 0x0000);
+            $operand1 = pack('Cv', 0x1E, $arrConditions[0]);
+            $operand2 = pack('Cv', 0x1E, $arrConditions[1]);
+        } else {
+            $szValue1 = 0x0000;
+            $szValue2 = 0x0000;
+            $operand1 = null;
+            $operand2 = null;
+        }
+
+        // $flags : Option flags
+        // Alignment
+        $bAlignHz = ($conditional->getStyle()->getAlignment()->getHorizontal() == null ? 1 : 0);
+        $bAlignVt = ($conditional->getStyle()->getAlignment()->getVertical() == null ? 1 : 0);
+        $bAlignWrapTx = ($conditional->getStyle()->getAlignment()->getWrapText() == false ? 1 : 0);
+        $bTxRotation = ($conditional->getStyle()->getAlignment()->getTextRotation() == null ? 1 : 0);
+        $bIndent = ($conditional->getStyle()->getAlignment()->getIndent() == 0 ? 1 : 0);
+        $bShrinkToFit = ($conditional->getStyle()->getAlignment()->getShrinkToFit() == false ? 1 : 0);
+        if ($bAlignHz == 0 || $bAlignVt == 0 || $bAlignWrapTx == 0 || $bTxRotation == 0 || $bIndent == 0 || $bShrinkToFit == 0) {
+            $bFormatAlign = 1;
+        } else {
+            $bFormatAlign = 0;
+        }
+        // Protection
+        $bProtLocked = ($conditional->getStyle()->getProtection()->getLocked() == null ? 1 : 0);
+        $bProtHidden = ($conditional->getStyle()->getProtection()->getHidden() == null ? 1 : 0);
+        if ($bProtLocked == 0 || $bProtHidden == 0) {
+            $bFormatProt = 1;
+        } else {
+            $bFormatProt = 0;
+        }
+        // Border
+        $bBorderLeft = ($conditional->getStyle()->getBorders()->getLeft()->getColor()->getARGB() == Color::COLOR_BLACK
+        && $conditional->getStyle()->getBorders()->getLeft()->getBorderStyle() == Border::BORDER_NONE ? 1 : 0);
+        $bBorderRight = ($conditional->getStyle()->getBorders()->getRight()->getColor()->getARGB() == Color::COLOR_BLACK
+        && $conditional->getStyle()->getBorders()->getRight()->getBorderStyle() == Border::BORDER_NONE ? 1 : 0);
+        $bBorderTop = ($conditional->getStyle()->getBorders()->getTop()->getColor()->getARGB() == Color::COLOR_BLACK
+        && $conditional->getStyle()->getBorders()->getTop()->getBorderStyle() == Border::BORDER_NONE ? 1 : 0);
+        $bBorderBottom = ($conditional->getStyle()->getBorders()->getBottom()->getColor()->getARGB() == Color::COLOR_BLACK
+        && $conditional->getStyle()->getBorders()->getBottom()->getBorderStyle() == Border::BORDER_NONE ? 1 : 0);
+        if ($bBorderLeft == 0 || $bBorderRight == 0 || $bBorderTop == 0 || $bBorderBottom == 0) {
+            $bFormatBorder = 1;
+        } else {
+            $bFormatBorder = 0;
+        }
+        // Pattern
+        $bFillStyle = ($conditional->getStyle()->getFill()->getFillType() == null ? 0 : 1);
+        $bFillColor = ($conditional->getStyle()->getFill()->getStartColor()->getARGB() == null ? 0 : 1);
+        $bFillColorBg = ($conditional->getStyle()->getFill()->getEndColor()->getARGB() == null ? 0 : 1);
+        if ($bFillStyle == 0 || $bFillColor == 0 || $bFillColorBg == 0) {
+            $bFormatFill = 1;
+        } else {
+            $bFormatFill = 0;
+        }
+        // Font
+        if ($conditional->getStyle()->getFont()->getName() != null
+            || $conditional->getStyle()->getFont()->getSize() != null
+            || $conditional->getStyle()->getFont()->getBold() != null
+            || $conditional->getStyle()->getFont()->getItalic() != null
+            || $conditional->getStyle()->getFont()->getSuperscript() != null
+            || $conditional->getStyle()->getFont()->getSubscript() != null
+            || $conditional->getStyle()->getFont()->getUnderline() != null
+            || $conditional->getStyle()->getFont()->getStrikethrough() != null
+            || $conditional->getStyle()->getFont()->getColor()->getARGB() != null) {
+            $bFormatFont = 1;
+        } else {
+            $bFormatFont = 0;
+        }
+        // Alignment
+        $flags = 0;
+        $flags |= (1 == $bAlignHz ? 0x00000001 : 0);
+        $flags |= (1 == $bAlignVt ? 0x00000002 : 0);
+        $flags |= (1 == $bAlignWrapTx ? 0x00000004 : 0);
+        $flags |= (1 == $bTxRotation ? 0x00000008 : 0);
+        // Justify last line flag
+        $flags |= (1 == 1 ? 0x00000010 : 0);
+        $flags |= (1 == $bIndent ? 0x00000020 : 0);
+        $flags |= (1 == $bShrinkToFit ? 0x00000040 : 0);
+        // Default
+        $flags |= (1 == 1 ? 0x00000080 : 0);
+        // Protection
+        $flags |= (1 == $bProtLocked ? 0x00000100 : 0);
+        $flags |= (1 == $bProtHidden ? 0x00000200 : 0);
+        // Border
+        $flags |= (1 == $bBorderLeft ? 0x00000400 : 0);
+        $flags |= (1 == $bBorderRight ? 0x00000800 : 0);
+        $flags |= (1 == $bBorderTop ? 0x00001000 : 0);
+        $flags |= (1 == $bBorderBottom ? 0x00002000 : 0);
+        $flags |= (1 == 1 ? 0x00004000 : 0); // Top left to Bottom right border
+        $flags |= (1 == 1 ? 0x00008000 : 0); // Bottom left to Top right border
+        // Pattern
+        $flags |= (1 == $bFillStyle ? 0x00010000 : 0);
+        $flags |= (1 == $bFillColor ? 0x00020000 : 0);
+        $flags |= (1 == $bFillColorBg ? 0x00040000 : 0);
+        $flags |= (1 == 1 ? 0x00380000 : 0);
+        // Font
+        $flags |= (1 == $bFormatFont ? 0x04000000 : 0);
+        // Alignment:
+        $flags |= (1 == $bFormatAlign ? 0x08000000 : 0);
+        // Border
+        $flags |= (1 == $bFormatBorder ? 0x10000000 : 0);
+        // Pattern
+        $flags |= (1 == $bFormatFill ? 0x20000000 : 0);
+        // Protection
+        $flags |= (1 == $bFormatProt ? 0x40000000 : 0);
+        // Text direction
+        $flags |= (1 == 0 ? 0x80000000 : 0);
+
+        // Data Blocks
+        if ($bFormatFont == 1) {
+            // Font Name
+            if ($conditional->getStyle()->getFont()->getName() == null) {
+                $dataBlockFont = pack('VVVVVVVV', 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000);
+                $dataBlockFont .= pack('VVVVVVVV', 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000);
+            } else {
+                $dataBlockFont = StringHelper::UTF8toBIFF8UnicodeLong($conditional->getStyle()->getFont()->getName());
+            }
+            // Font Size
+            if ($conditional->getStyle()->getFont()->getSize() == null) {
+                $dataBlockFont .= pack('V', 20 * 11);
+            } else {
+                $dataBlockFont .= pack('V', 20 * $conditional->getStyle()->getFont()->getSize());
+            }
+            // Font Options
+            $dataBlockFont .= pack('V', 0);
+            // Font weight
+            if ($conditional->getStyle()->getFont()->getBold() == true) {
+                $dataBlockFont .= pack('v', 0x02BC);
+            } else {
+                $dataBlockFont .= pack('v', 0x0190);
+            }
+            // Escapement type
+            if ($conditional->getStyle()->getFont()->getSubscript() == true) {
+                $dataBlockFont .= pack('v', 0x02);
+                $fontEscapement = 0;
+            } elseif ($conditional->getStyle()->getFont()->getSuperscript() == true) {
+                $dataBlockFont .= pack('v', 0x01);
+                $fontEscapement = 0;
+            } else {
+                $dataBlockFont .= pack('v', 0x00);
+                $fontEscapement = 1;
+            }
+            // Underline type
+            switch ($conditional->getStyle()->getFont()->getUnderline()) {
+                case \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_NONE:
+                    $dataBlockFont .= pack('C', 0x00);
+                    $fontUnderline = 0;
+
+                    break;
+                case \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLE:
+                    $dataBlockFont .= pack('C', 0x02);
+                    $fontUnderline = 0;
+
+                    break;
+                case \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLEACCOUNTING:
+                    $dataBlockFont .= pack('C', 0x22);
+                    $fontUnderline = 0;
+
+                    break;
+                case \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLE:
+                    $dataBlockFont .= pack('C', 0x01);
+                    $fontUnderline = 0;
+
+                    break;
+                case \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLEACCOUNTING:
+                    $dataBlockFont .= pack('C', 0x21);
+                    $fontUnderline = 0;
+
+                    break;
+                default:
+                    $dataBlockFont .= pack('C', 0x00);
+                    $fontUnderline = 1;
+
+                    break;
+            }
+            // Not used (3)
+            $dataBlockFont .= pack('vC', 0x0000, 0x00);
+            // Font color index
+            switch ($conditional->getStyle()->getFont()->getColor()->getRGB()) {
+                case '000000':
+                    $colorIdx = 0x08;
+
+                    break;
+                case 'FFFFFF':
+                    $colorIdx = 0x09;
+
+                    break;
+                case 'FF0000':
+                    $colorIdx = 0x0A;
+
+                    break;
+                case '00FF00':
+                    $colorIdx = 0x0B;
+
+                    break;
+                case '0000FF':
+                    $colorIdx = 0x0C;
+
+                    break;
+                case 'FFFF00':
+                    $colorIdx = 0x0D;
+
+                    break;
+                case 'FF00FF':
+                    $colorIdx = 0x0E;
+
+                    break;
+                case '00FFFF':
+                    $colorIdx = 0x0F;
+
+                    break;
+                case '800000':
+                    $colorIdx = 0x10;
+
+                    break;
+                case '008000':
+                    $colorIdx = 0x11;
+
+                    break;
+                case '000080':
+                    $colorIdx = 0x12;
+
+                    break;
+                case '808000':
+                    $colorIdx = 0x13;
+
+                    break;
+                case '800080':
+                    $colorIdx = 0x14;
+
+                    break;
+                case '008080':
+                    $colorIdx = 0x15;
+
+                    break;
+                case 'C0C0C0':
+                    $colorIdx = 0x16;
+
+                    break;
+                case '808080':
+                    $colorIdx = 0x17;
+
+                    break;
+                case '9999FF':
+                    $colorIdx = 0x18;
+
+                    break;
+                case '993366':
+                    $colorIdx = 0x19;
+
+                    break;
+                case 'FFFFCC':
+                    $colorIdx = 0x1A;
+
+                    break;
+                case 'CCFFFF':
+                    $colorIdx = 0x1B;
+
+                    break;
+                case '660066':
+                    $colorIdx = 0x1C;
+
+                    break;
+                case 'FF8080':
+                    $colorIdx = 0x1D;
+
+                    break;
+                case '0066CC':
+                    $colorIdx = 0x1E;
+
+                    break;
+                case 'CCCCFF':
+                    $colorIdx = 0x1F;
+
+                    break;
+                case '000080':
+                    $colorIdx = 0x20;
+
+                    break;
+                case 'FF00FF':
+                    $colorIdx = 0x21;
+
+                    break;
+                case 'FFFF00':
+                    $colorIdx = 0x22;
+
+                    break;
+                case '00FFFF':
+                    $colorIdx = 0x23;
+
+                    break;
+                case '800080':
+                    $colorIdx = 0x24;
+
+                    break;
+                case '800000':
+                    $colorIdx = 0x25;
+
+                    break;
+                case '008080':
+                    $colorIdx = 0x26;
+
+                    break;
+                case '0000FF':
+                    $colorIdx = 0x27;
+
+                    break;
+                case '00CCFF':
+                    $colorIdx = 0x28;
+
+                    break;
+                case 'CCFFFF':
+                    $colorIdx = 0x29;
+
+                    break;
+                case 'CCFFCC':
+                    $colorIdx = 0x2A;
+
+                    break;
+                case 'FFFF99':
+                    $colorIdx = 0x2B;
+
+                    break;
+                case '99CCFF':
+                    $colorIdx = 0x2C;
+
+                    break;
+                case 'FF99CC':
+                    $colorIdx = 0x2D;
+
+                    break;
+                case 'CC99FF':
+                    $colorIdx = 0x2E;
+
+                    break;
+                case 'FFCC99':
+                    $colorIdx = 0x2F;
+
+                    break;
+                case '3366FF':
+                    $colorIdx = 0x30;
+
+                    break;
+                case '33CCCC':
+                    $colorIdx = 0x31;
+
+                    break;
+                case '99CC00':
+                    $colorIdx = 0x32;
+
+                    break;
+                case 'FFCC00':
+                    $colorIdx = 0x33;
+
+                    break;
+                case 'FF9900':
+                    $colorIdx = 0x34;
+
+                    break;
+                case 'FF6600':
+                    $colorIdx = 0x35;
+
+                    break;
+                case '666699':
+                    $colorIdx = 0x36;
+
+                    break;
+                case '969696':
+                    $colorIdx = 0x37;
+
+                    break;
+                case '003366':
+                    $colorIdx = 0x38;
+
+                    break;
+                case '339966':
+                    $colorIdx = 0x39;
+
+                    break;
+                case '003300':
+                    $colorIdx = 0x3A;
+
+                    break;
+                case '333300':
+                    $colorIdx = 0x3B;
+
+                    break;
+                case '993300':
+                    $colorIdx = 0x3C;
+
+                    break;
+                case '993366':
+                    $colorIdx = 0x3D;
+
+                    break;
+                case '333399':
+                    $colorIdx = 0x3E;
+
+                    break;
+                case '333333':
+                    $colorIdx = 0x3F;
+
+                    break;
+                default:
+                    $colorIdx = 0x00;
+
+                    break;
+            }
+            $dataBlockFont .= pack('V', $colorIdx);
+            // Not used (4)
+            $dataBlockFont .= pack('V', 0x00000000);
+            // Options flags for modified font attributes
+            $optionsFlags = 0;
+            $optionsFlagsBold = ($conditional->getStyle()->getFont()->getBold() == null ? 1 : 0);
+            $optionsFlags |= (1 == $optionsFlagsBold ? 0x00000002 : 0);
+            $optionsFlags |= (1 == 1 ? 0x00000008 : 0);
+            $optionsFlags |= (1 == 1 ? 0x00000010 : 0);
+            $optionsFlags |= (1 == 0 ? 0x00000020 : 0);
+            $optionsFlags |= (1 == 1 ? 0x00000080 : 0);
+            $dataBlockFont .= pack('V', $optionsFlags);
+            // Escapement type
+            $dataBlockFont .= pack('V', $fontEscapement);
+            // Underline type
+            $dataBlockFont .= pack('V', $fontUnderline);
+            // Always
+            $dataBlockFont .= pack('V', 0x00000000);
+            // Always
+            $dataBlockFont .= pack('V', 0x00000000);
+            // Not used (8)
+            $dataBlockFont .= pack('VV', 0x00000000, 0x00000000);
+            // Always
+            $dataBlockFont .= pack('v', 0x0001);
+        }
+        if ($bFormatAlign == 1) {
+            $blockAlign = 0;
+            // Alignment and text break
+            switch ($conditional->getStyle()->getAlignment()->getHorizontal()) {
+                case Alignment::HORIZONTAL_GENERAL:
+                    $blockAlign = 0;
+
+                    break;
+                case Alignment::HORIZONTAL_LEFT:
+                    $blockAlign = 1;
+
+                    break;
+                case Alignment::HORIZONTAL_RIGHT:
+                    $blockAlign = 3;
+
+                    break;
+                case Alignment::HORIZONTAL_CENTER:
+                    $blockAlign = 2;
+
+                    break;
+                case Alignment::HORIZONTAL_CENTER_CONTINUOUS:
+                    $blockAlign = 6;
+
+                    break;
+                case Alignment::HORIZONTAL_JUSTIFY:
+                    $blockAlign = 5;
+
+                    break;
+            }
+            if ($conditional->getStyle()->getAlignment()->getWrapText() == true) {
+                $blockAlign |= 1 << 3;
+            } else {
+                $blockAlign |= 0 << 3;
+            }
+            switch ($conditional->getStyle()->getAlignment()->getVertical()) {
+                case Alignment::VERTICAL_BOTTOM:
+                    $blockAlign = 2 << 4;
+
+                    break;
+                case Alignment::VERTICAL_TOP:
+                    $blockAlign = 0 << 4;
+
+                    break;
+                case Alignment::VERTICAL_CENTER:
+                    $blockAlign = 1 << 4;
+
+                    break;
+                case Alignment::VERTICAL_JUSTIFY:
+                    $blockAlign = 3 << 4;
+
+                    break;
+            }
+            $blockAlign |= 0 << 7;
+
+            // Text rotation angle
+            $blockRotation = $conditional->getStyle()->getAlignment()->getTextRotation();
+
+            // Indentation
+            $blockIndent = $conditional->getStyle()->getAlignment()->getIndent();
+            if ($conditional->getStyle()->getAlignment()->getShrinkToFit() == true) {
+                $blockIndent |= 1 << 4;
+            } else {
+                $blockIndent |= 0 << 4;
+            }
+            $blockIndent |= 0 << 6;
+
+            // Relative indentation
+            $blockIndentRelative = 255;
+
+            $dataBlockAlign = pack('CCvvv', $blockAlign, $blockRotation, $blockIndent, $blockIndentRelative, 0x0000);
+        }
+        if ($bFormatBorder == 1) {
+            $blockLineStyle = 0;
+            switch ($conditional->getStyle()->getBorders()->getLeft()->getBorderStyle()) {
+                case Border::BORDER_NONE:
+                    $blockLineStyle |= 0x00;
+
+                    break;
+                case Border::BORDER_THIN:
+                    $blockLineStyle |= 0x01;
+
+                    break;
+                case Border::BORDER_MEDIUM:
+                    $blockLineStyle |= 0x02;
+
+                    break;
+                case Border::BORDER_DASHED:
+                    $blockLineStyle |= 0x03;
+
+                    break;
+                case Border::BORDER_DOTTED:
+                    $blockLineStyle |= 0x04;
+
+                    break;
+                case Border::BORDER_THICK:
+                    $blockLineStyle |= 0x05;
+
+                    break;
+                case Border::BORDER_DOUBLE:
+                    $blockLineStyle |= 0x06;
+
+                    break;
+                case Border::BORDER_HAIR:
+                    $blockLineStyle |= 0x07;
+
+                    break;
+                case Border::BORDER_MEDIUMDASHED:
+                    $blockLineStyle |= 0x08;
+
+                    break;
+                case Border::BORDER_DASHDOT:
+                    $blockLineStyle |= 0x09;
+
+                    break;
+                case Border::BORDER_MEDIUMDASHDOT:
+                    $blockLineStyle |= 0x0A;
+
+                    break;
+                case Border::BORDER_DASHDOTDOT:
+                    $blockLineStyle |= 0x0B;
+
+                    break;
+                case Border::BORDER_MEDIUMDASHDOTDOT:
+                    $blockLineStyle |= 0x0C;
+
+                    break;
+                case Border::BORDER_SLANTDASHDOT:
+                    $blockLineStyle |= 0x0D;
+
+                    break;
+            }
+            switch ($conditional->getStyle()->getBorders()->getRight()->getBorderStyle()) {
+                case Border::BORDER_NONE:
+                    $blockLineStyle |= 0x00 << 4;
+
+                    break;
+                case Border::BORDER_THIN:
+                    $blockLineStyle |= 0x01 << 4;
+
+                    break;
+                case Border::BORDER_MEDIUM:
+                    $blockLineStyle |= 0x02 << 4;
+
+                    break;
+                case Border::BORDER_DASHED:
+                    $blockLineStyle |= 0x03 << 4;
+
+                    break;
+                case Border::BORDER_DOTTED:
+                    $blockLineStyle |= 0x04 << 4;
+
+                    break;
+                case Border::BORDER_THICK:
+                    $blockLineStyle |= 0x05 << 4;
+
+                    break;
+                case Border::BORDER_DOUBLE:
+                    $blockLineStyle |= 0x06 << 4;
+
+                    break;
+                case Border::BORDER_HAIR:
+                    $blockLineStyle |= 0x07 << 4;
+
+                    break;
+                case Border::BORDER_MEDIUMDASHED:
+                    $blockLineStyle |= 0x08 << 4;
+
+                    break;
+                case Border::BORDER_DASHDOT:
+                    $blockLineStyle |= 0x09 << 4;
+
+                    break;
+                case Border::BORDER_MEDIUMDASHDOT:
+                    $blockLineStyle |= 0x0A << 4;
+
+                    break;
+                case Border::BORDER_DASHDOTDOT:
+                    $blockLineStyle |= 0x0B << 4;
+
+                    break;
+                case Border::BORDER_MEDIUMDASHDOTDOT:
+                    $blockLineStyle |= 0x0C << 4;
+
+                    break;
+                case Border::BORDER_SLANTDASHDOT:
+                    $blockLineStyle |= 0x0D << 4;
+
+                    break;
+            }
+            switch ($conditional->getStyle()->getBorders()->getTop()->getBorderStyle()) {
+                case Border::BORDER_NONE:
+                    $blockLineStyle |= 0x00 << 8;
+
+                    break;
+                case Border::BORDER_THIN:
+                    $blockLineStyle |= 0x01 << 8;
+
+                    break;
+                case Border::BORDER_MEDIUM:
+                    $blockLineStyle |= 0x02 << 8;
+
+                    break;
+                case Border::BORDER_DASHED:
+                    $blockLineStyle |= 0x03 << 8;
+
+                    break;
+                case Border::BORDER_DOTTED:
+                    $blockLineStyle |= 0x04 << 8;
+
+                    break;
+                case Border::BORDER_THICK:
+                    $blockLineStyle |= 0x05 << 8;
+
+                    break;
+                case Border::BORDER_DOUBLE:
+                    $blockLineStyle |= 0x06 << 8;
+
+                    break;
+                case Border::BORDER_HAIR:
+                    $blockLineStyle |= 0x07 << 8;
+
+                    break;
+                case Border::BORDER_MEDIUMDASHED:
+                    $blockLineStyle |= 0x08 << 8;
+
+                    break;
+                case Border::BORDER_DASHDOT:
+                    $blockLineStyle |= 0x09 << 8;
+
+                    break;
+                case Border::BORDER_MEDIUMDASHDOT:
+                    $blockLineStyle |= 0x0A << 8;
+
+                    break;
+                case Border::BORDER_DASHDOTDOT:
+                    $blockLineStyle |= 0x0B << 8;
+
+                    break;
+                case Border::BORDER_MEDIUMDASHDOTDOT:
+                    $blockLineStyle |= 0x0C << 8;
+
+                    break;
+                case Border::BORDER_SLANTDASHDOT:
+                    $blockLineStyle |= 0x0D << 8;
+
+                    break;
+            }
+            switch ($conditional->getStyle()->getBorders()->getBottom()->getBorderStyle()) {
+                case Border::BORDER_NONE:
+                    $blockLineStyle |= 0x00 << 12;
+
+                    break;
+                case Border::BORDER_THIN:
+                    $blockLineStyle |= 0x01 << 12;
+
+                    break;
+                case Border::BORDER_MEDIUM:
+                    $blockLineStyle |= 0x02 << 12;
+
+                    break;
+                case Border::BORDER_DASHED:
+                    $blockLineStyle |= 0x03 << 12;
+
+                    break;
+                case Border::BORDER_DOTTED:
+                    $blockLineStyle |= 0x04 << 12;
+
+                    break;
+                case Border::BORDER_THICK:
+                    $blockLineStyle |= 0x05 << 12;
+
+                    break;
+                case Border::BORDER_DOUBLE:
+                    $blockLineStyle |= 0x06 << 12;
+
+                    break;
+                case Border::BORDER_HAIR:
+                    $blockLineStyle |= 0x07 << 12;
+
+                    break;
+                case Border::BORDER_MEDIUMDASHED:
+                    $blockLineStyle |= 0x08 << 12;
+
+                    break;
+                case Border::BORDER_DASHDOT:
+                    $blockLineStyle |= 0x09 << 12;
+
+                    break;
+                case Border::BORDER_MEDIUMDASHDOT:
+                    $blockLineStyle |= 0x0A << 12;
+
+                    break;
+                case Border::BORDER_DASHDOTDOT:
+                    $blockLineStyle |= 0x0B << 12;
+
+                    break;
+                case Border::BORDER_MEDIUMDASHDOTDOT:
+                    $blockLineStyle |= 0x0C << 12;
+
+                    break;
+                case Border::BORDER_SLANTDASHDOT:
+                    $blockLineStyle |= 0x0D << 12;
+
+                    break;
+            }
+
+            // TODO writeCFRule() => $blockLineStyle => Index Color for left line
+            // TODO writeCFRule() => $blockLineStyle => Index Color for right line
+            // TODO writeCFRule() => $blockLineStyle => Top-left to bottom-right on/off
+            // TODO writeCFRule() => $blockLineStyle => Bottom-left to top-right on/off
+            $blockColor = 0;
+            // TODO writeCFRule() => $blockColor => Index Color for top line
+            // TODO writeCFRule() => $blockColor => Index Color for bottom line
+            // TODO writeCFRule() => $blockColor => Index Color for diagonal line
+            switch ($conditional->getStyle()->getBorders()->getDiagonal()->getBorderStyle()) {
+                case Border::BORDER_NONE:
+                    $blockColor |= 0x00 << 21;
+
+                    break;
+                case Border::BORDER_THIN:
+                    $blockColor |= 0x01 << 21;
+
+                    break;
+                case Border::BORDER_MEDIUM:
+                    $blockColor |= 0x02 << 21;
+
+                    break;
+                case Border::BORDER_DASHED:
+                    $blockColor |= 0x03 << 21;
+
+                    break;
+                case Border::BORDER_DOTTED:
+                    $blockColor |= 0x04 << 21;
+
+                    break;
+                case Border::BORDER_THICK:
+                    $blockColor |= 0x05 << 21;
+
+                    break;
+                case Border::BORDER_DOUBLE:
+                    $blockColor |= 0x06 << 21;
+
+                    break;
+                case Border::BORDER_HAIR:
+                    $blockColor |= 0x07 << 21;
+
+                    break;
+                case Border::BORDER_MEDIUMDASHED:
+                    $blockColor |= 0x08 << 21;
+
+                    break;
+                case Border::BORDER_DASHDOT:
+                    $blockColor |= 0x09 << 21;
+
+                    break;
+                case Border::BORDER_MEDIUMDASHDOT:
+                    $blockColor |= 0x0A << 21;
+
+                    break;
+                case Border::BORDER_DASHDOTDOT:
+                    $blockColor |= 0x0B << 21;
+
+                    break;
+                case Border::BORDER_MEDIUMDASHDOTDOT:
+                    $blockColor |= 0x0C << 21;
+
+                    break;
+                case Border::BORDER_SLANTDASHDOT:
+                    $blockColor |= 0x0D << 21;
+
+                    break;
+            }
+            $dataBlockBorder = pack('vv', $blockLineStyle, $blockColor);
+        }
+        if ($bFormatFill == 1) {
+            // Fill Patern Style
+            $blockFillPatternStyle = 0;
+            switch ($conditional->getStyle()->getFill()->getFillType()) {
+                case Fill::FILL_NONE:
+                    $blockFillPatternStyle = 0x00;
+
+                    break;
+                case Fill::FILL_SOLID:
+                    $blockFillPatternStyle = 0x01;
+
+                    break;
+                case Fill::FILL_PATTERN_MEDIUMGRAY:
+                    $blockFillPatternStyle = 0x02;
+
+                    break;
+                case Fill::FILL_PATTERN_DARKGRAY:
+                    $blockFillPatternStyle = 0x03;
+
+                    break;
+                case Fill::FILL_PATTERN_LIGHTGRAY:
+                    $blockFillPatternStyle = 0x04;
+
+                    break;
+                case Fill::FILL_PATTERN_DARKHORIZONTAL:
+                    $blockFillPatternStyle = 0x05;
+
+                    break;
+                case Fill::FILL_PATTERN_DARKVERTICAL:
+                    $blockFillPatternStyle = 0x06;
+
+                    break;
+                case Fill::FILL_PATTERN_DARKDOWN:
+                    $blockFillPatternStyle = 0x07;
+
+                    break;
+                case Fill::FILL_PATTERN_DARKUP:
+                    $blockFillPatternStyle = 0x08;
+
+                    break;
+                case Fill::FILL_PATTERN_DARKGRID:
+                    $blockFillPatternStyle = 0x09;
+
+                    break;
+                case Fill::FILL_PATTERN_DARKTRELLIS:
+                    $blockFillPatternStyle = 0x0A;
+
+                    break;
+                case Fill::FILL_PATTERN_LIGHTHORIZONTAL:
+                    $blockFillPatternStyle = 0x0B;
+
+                    break;
+                case Fill::FILL_PATTERN_LIGHTVERTICAL:
+                    $blockFillPatternStyle = 0x0C;
+
+                    break;
+                case Fill::FILL_PATTERN_LIGHTDOWN:
+                    $blockFillPatternStyle = 0x0D;
+
+                    break;
+                case Fill::FILL_PATTERN_LIGHTUP:
+                    $blockFillPatternStyle = 0x0E;
+
+                    break;
+                case Fill::FILL_PATTERN_LIGHTGRID:
+                    $blockFillPatternStyle = 0x0F;
+
+                    break;
+                case Fill::FILL_PATTERN_LIGHTTRELLIS:
+                    $blockFillPatternStyle = 0x10;
+
+                    break;
+                case Fill::FILL_PATTERN_GRAY125:
+                    $blockFillPatternStyle = 0x11;
+
+                    break;
+                case Fill::FILL_PATTERN_GRAY0625:
+                    $blockFillPatternStyle = 0x12;
+
+                    break;
+                case Fill::FILL_GRADIENT_LINEAR:
+                    $blockFillPatternStyle = 0x00;
+
+                    break; // does not exist in BIFF8
+                case Fill::FILL_GRADIENT_PATH:
+                    $blockFillPatternStyle = 0x00;
+
+                    break; // does not exist in BIFF8
+                default:
+                    $blockFillPatternStyle = 0x00;
+
+                    break;
+            }
+            // Color
+            switch ($conditional->getStyle()->getFill()->getStartColor()->getRGB()) {
+                case '000000':
+                    $colorIdxBg = 0x08;
+
+                    break;
+                case 'FFFFFF':
+                    $colorIdxBg = 0x09;
+
+                    break;
+                case 'FF0000':
+                    $colorIdxBg = 0x0A;
+
+                    break;
+                case '00FF00':
+                    $colorIdxBg = 0x0B;
+
+                    break;
+                case '0000FF':
+                    $colorIdxBg = 0x0C;
+
+                    break;
+                case 'FFFF00':
+                    $colorIdxBg = 0x0D;
+
+                    break;
+                case 'FF00FF':
+                    $colorIdxBg = 0x0E;
+
+                    break;
+                case '00FFFF':
+                    $colorIdxBg = 0x0F;
+
+                    break;
+                case '800000':
+                    $colorIdxBg = 0x10;
+
+                    break;
+                case '008000':
+                    $colorIdxBg = 0x11;
+
+                    break;
+                case '000080':
+                    $colorIdxBg = 0x12;
+
+                    break;
+                case '808000':
+                    $colorIdxBg = 0x13;
+
+                    break;
+                case '800080':
+                    $colorIdxBg = 0x14;
+
+                    break;
+                case '008080':
+                    $colorIdxBg = 0x15;
+
+                    break;
+                case 'C0C0C0':
+                    $colorIdxBg = 0x16;
+
+                    break;
+                case '808080':
+                    $colorIdxBg = 0x17;
+
+                    break;
+                case '9999FF':
+                    $colorIdxBg = 0x18;
+
+                    break;
+                case '993366':
+                    $colorIdxBg = 0x19;
+
+                    break;
+                case 'FFFFCC':
+                    $colorIdxBg = 0x1A;
+
+                    break;
+                case 'CCFFFF':
+                    $colorIdxBg = 0x1B;
+
+                    break;
+                case '660066':
+                    $colorIdxBg = 0x1C;
+
+                    break;
+                case 'FF8080':
+                    $colorIdxBg = 0x1D;
+
+                    break;
+                case '0066CC':
+                    $colorIdxBg = 0x1E;
+
+                    break;
+                case 'CCCCFF':
+                    $colorIdxBg = 0x1F;
+
+                    break;
+                case '000080':
+                    $colorIdxBg = 0x20;
+
+                    break;
+                case 'FF00FF':
+                    $colorIdxBg = 0x21;
+
+                    break;
+                case 'FFFF00':
+                    $colorIdxBg = 0x22;
+
+                    break;
+                case '00FFFF':
+                    $colorIdxBg = 0x23;
+
+                    break;
+                case '800080':
+                    $colorIdxBg = 0x24;
+
+                    break;
+                case '800000':
+                    $colorIdxBg = 0x25;
+
+                    break;
+                case '008080':
+                    $colorIdxBg = 0x26;
+
+                    break;
+                case '0000FF':
+                    $colorIdxBg = 0x27;
+
+                    break;
+                case '00CCFF':
+                    $colorIdxBg = 0x28;
+
+                    break;
+                case 'CCFFFF':
+                    $colorIdxBg = 0x29;
+
+                    break;
+                case 'CCFFCC':
+                    $colorIdxBg = 0x2A;
+
+                    break;
+                case 'FFFF99':
+                    $colorIdxBg = 0x2B;
+
+                    break;
+                case '99CCFF':
+                    $colorIdxBg = 0x2C;
+
+                    break;
+                case 'FF99CC':
+                    $colorIdxBg = 0x2D;
+
+                    break;
+                case 'CC99FF':
+                    $colorIdxBg = 0x2E;
+
+                    break;
+                case 'FFCC99':
+                    $colorIdxBg = 0x2F;
+
+                    break;
+                case '3366FF':
+                    $colorIdxBg = 0x30;
+
+                    break;
+                case '33CCCC':
+                    $colorIdxBg = 0x31;
+
+                    break;
+                case '99CC00':
+                    $colorIdxBg = 0x32;
+
+                    break;
+                case 'FFCC00':
+                    $colorIdxBg = 0x33;
+
+                    break;
+                case 'FF9900':
+                    $colorIdxBg = 0x34;
+
+                    break;
+                case 'FF6600':
+                    $colorIdxBg = 0x35;
+
+                    break;
+                case '666699':
+                    $colorIdxBg = 0x36;
+
+                    break;
+                case '969696':
+                    $colorIdxBg = 0x37;
+
+                    break;
+                case '003366':
+                    $colorIdxBg = 0x38;
+
+                    break;
+                case '339966':
+                    $colorIdxBg = 0x39;
+
+                    break;
+                case '003300':
+                    $colorIdxBg = 0x3A;
+
+                    break;
+                case '333300':
+                    $colorIdxBg = 0x3B;
+
+                    break;
+                case '993300':
+                    $colorIdxBg = 0x3C;
+
+                    break;
+                case '993366':
+                    $colorIdxBg = 0x3D;
+
+                    break;
+                case '333399':
+                    $colorIdxBg = 0x3E;
+
+                    break;
+                case '333333':
+                    $colorIdxBg = 0x3F;
+
+                    break;
+                default:
+                    $colorIdxBg = 0x41;
+
+                    break;
+            }
+            // Fg Color
+            switch ($conditional->getStyle()->getFill()->getEndColor()->getRGB()) {
+                case '000000':
+                    $colorIdxFg = 0x08;
+
+                    break;
+                case 'FFFFFF':
+                    $colorIdxFg = 0x09;
+
+                    break;
+                case 'FF0000':
+                    $colorIdxFg = 0x0A;
+
+                    break;
+                case '00FF00':
+                    $colorIdxFg = 0x0B;
+
+                    break;
+                case '0000FF':
+                    $colorIdxFg = 0x0C;
+
+                    break;
+                case 'FFFF00':
+                    $colorIdxFg = 0x0D;
+
+                    break;
+                case 'FF00FF':
+                    $colorIdxFg = 0x0E;
+
+                    break;
+                case '00FFFF':
+                    $colorIdxFg = 0x0F;
+
+                    break;
+                case '800000':
+                    $colorIdxFg = 0x10;
+
+                    break;
+                case '008000':
+                    $colorIdxFg = 0x11;
+
+                    break;
+                case '000080':
+                    $colorIdxFg = 0x12;
+
+                    break;
+                case '808000':
+                    $colorIdxFg = 0x13;
+
+                    break;
+                case '800080':
+                    $colorIdxFg = 0x14;
+
+                    break;
+                case '008080':
+                    $colorIdxFg = 0x15;
+
+                    break;
+                case 'C0C0C0':
+                    $colorIdxFg = 0x16;
+
+                    break;
+                case '808080':
+                    $colorIdxFg = 0x17;
+
+                    break;
+                case '9999FF':
+                    $colorIdxFg = 0x18;
+
+                    break;
+                case '993366':
+                    $colorIdxFg = 0x19;
+
+                    break;
+                case 'FFFFCC':
+                    $colorIdxFg = 0x1A;
+
+                    break;
+                case 'CCFFFF':
+                    $colorIdxFg = 0x1B;
+
+                    break;
+                case '660066':
+                    $colorIdxFg = 0x1C;
+
+                    break;
+                case 'FF8080':
+                    $colorIdxFg = 0x1D;
+
+                    break;
+                case '0066CC':
+                    $colorIdxFg = 0x1E;
+
+                    break;
+                case 'CCCCFF':
+                    $colorIdxFg = 0x1F;
+
+                    break;
+                case '000080':
+                    $colorIdxFg = 0x20;
+
+                    break;
+                case 'FF00FF':
+                    $colorIdxFg = 0x21;
+
+                    break;
+                case 'FFFF00':
+                    $colorIdxFg = 0x22;
+
+                    break;
+                case '00FFFF':
+                    $colorIdxFg = 0x23;
+
+                    break;
+                case '800080':
+                    $colorIdxFg = 0x24;
+
+                    break;
+                case '800000':
+                    $colorIdxFg = 0x25;
+
+                    break;
+                case '008080':
+                    $colorIdxFg = 0x26;
+
+                    break;
+                case '0000FF':
+                    $colorIdxFg = 0x27;
+
+                    break;
+                case '00CCFF':
+                    $colorIdxFg = 0x28;
+
+                    break;
+                case 'CCFFFF':
+                    $colorIdxFg = 0x29;
+
+                    break;
+                case 'CCFFCC':
+                    $colorIdxFg = 0x2A;
+
+                    break;
+                case 'FFFF99':
+                    $colorIdxFg = 0x2B;
+
+                    break;
+                case '99CCFF':
+                    $colorIdxFg = 0x2C;
+
+                    break;
+                case 'FF99CC':
+                    $colorIdxFg = 0x2D;
+
+                    break;
+                case 'CC99FF':
+                    $colorIdxFg = 0x2E;
+
+                    break;
+                case 'FFCC99':
+                    $colorIdxFg = 0x2F;
+
+                    break;
+                case '3366FF':
+                    $colorIdxFg = 0x30;
+
+                    break;
+                case '33CCCC':
+                    $colorIdxFg = 0x31;
+
+                    break;
+                case '99CC00':
+                    $colorIdxFg = 0x32;
+
+                    break;
+                case 'FFCC00':
+                    $colorIdxFg = 0x33;
+
+                    break;
+                case 'FF9900':
+                    $colorIdxFg = 0x34;
+
+                    break;
+                case 'FF6600':
+                    $colorIdxFg = 0x35;
+
+                    break;
+                case '666699':
+                    $colorIdxFg = 0x36;
+
+                    break;
+                case '969696':
+                    $colorIdxFg = 0x37;
+
+                    break;
+                case '003366':
+                    $colorIdxFg = 0x38;
+
+                    break;
+                case '339966':
+                    $colorIdxFg = 0x39;
+
+                    break;
+                case '003300':
+                    $colorIdxFg = 0x3A;
+
+                    break;
+                case '333300':
+                    $colorIdxFg = 0x3B;
+
+                    break;
+                case '993300':
+                    $colorIdxFg = 0x3C;
+
+                    break;
+                case '993366':
+                    $colorIdxFg = 0x3D;
+
+                    break;
+                case '333399':
+                    $colorIdxFg = 0x3E;
+
+                    break;
+                case '333333':
+                    $colorIdxFg = 0x3F;
+
+                    break;
+                default:
+                    $colorIdxFg = 0x40;
+
+                    break;
+            }
+            $dataBlockFill = pack('v', $blockFillPatternStyle);
+            $dataBlockFill .= pack('v', $colorIdxFg | ($colorIdxBg << 7));
+        }
+        if ($bFormatProt == 1) {
+            $dataBlockProtection = 0;
+            if ($conditional->getStyle()->getProtection()->getLocked() == Protection::PROTECTION_PROTECTED) {
+                $dataBlockProtection = 1;
+            }
+            if ($conditional->getStyle()->getProtection()->getHidden() == Protection::PROTECTION_PROTECTED) {
+                $dataBlockProtection = 1 << 1;
+            }
+        }
+
+        $data = pack('CCvvVv', $type, $operatorType, $szValue1, $szValue2, $flags, 0x0000);
+        if ($bFormatFont == 1) { // Block Formatting : OK
+            $data .= $dataBlockFont;
+        }
+        if ($bFormatAlign == 1) {
+            $data .= $dataBlockAlign;
+        }
+        if ($bFormatBorder == 1) {
+            $data .= $dataBlockBorder;
+        }
+        if ($bFormatFill == 1) { // Block Formatting : OK
+            $data .= $dataBlockFill;
+        }
+        if ($bFormatProt == 1) {
+            $data .= $dataBlockProtection;
+        }
+        if ($operand1 !== null) {
+            $data .= $operand1;
+        }
+        if ($operand2 !== null) {
+            $data .= $operand2;
+        }
+        $header = pack('vv', $record, strlen($data));
+        $this->append($header . $data);
+    }
+
+    /**
+     * Write CFHeader record.
+     */
+    private function writeCFHeader(): void
+    {
+        $record = 0x01B0; // Record identifier
+        $length = 0x0016; // Bytes to follow
+
+        $numColumnMin = null;
+        $numColumnMax = null;
+        $numRowMin = null;
+        $numRowMax = null;
+        $arrConditional = [];
+        foreach ($this->phpSheet->getConditionalStylesCollection() as $cellCoordinate => $conditionalStyles) {
+            foreach ($conditionalStyles as $conditional) {
+                if ($conditional->getConditionType() == Conditional::CONDITION_EXPRESSION
+                    || $conditional->getConditionType() == Conditional::CONDITION_CELLIS) {
+                    if (!in_array($conditional->getHashCode(), $arrConditional)) {
+                        $arrConditional[] = $conditional->getHashCode();
+                    }
+                    // Cells
+                    $arrCoord = Coordinate::coordinateFromString($cellCoordinate);
+                    if (!is_numeric($arrCoord[0])) {
+                        $arrCoord[0] = Coordinate::columnIndexFromString($arrCoord[0]);
+                    }
+                    if ($numColumnMin === null || ($numColumnMin > $arrCoord[0])) {
+                        $numColumnMin = $arrCoord[0];
+                    }
+                    if ($numColumnMax === null || ($numColumnMax < $arrCoord[0])) {
+                        $numColumnMax = $arrCoord[0];
+                    }
+                    if ($numRowMin === null || ($numRowMin > $arrCoord[1])) {
+                        $numRowMin = $arrCoord[1];
+                    }
+                    if ($numRowMax === null || ($numRowMax < $arrCoord[1])) {
+                        $numRowMax = $arrCoord[1];
+                    }
+                }
+            }
+        }
+        $needRedraw = 1;
+        $cellRange = pack('vvvv', $numRowMin - 1, $numRowMax - 1, $numColumnMin - 1, $numColumnMax - 1);
+
+        $header = pack('vv', $record, $length);
+        $data = pack('vv', count($arrConditional), $needRedraw);
+        $data .= $cellRange;
+        $data .= pack('v', 0x0001);
+        $data .= $cellRange;
+        $this->append($header . $data);
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Xf.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Xf.php
new file mode 100755
index 0000000..3e8169b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Xf.php
@@ -0,0 +1,548 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Xls;
+
+use PhpOffice\PhpSpreadsheet\Style\Alignment;
+use PhpOffice\PhpSpreadsheet\Style\Border;
+use PhpOffice\PhpSpreadsheet\Style\Borders;
+use PhpOffice\PhpSpreadsheet\Style\Fill;
+use PhpOffice\PhpSpreadsheet\Style\Protection;
+use PhpOffice\PhpSpreadsheet\Style\Style;
+
+// Original file header of PEAR::Spreadsheet_Excel_Writer_Format (used as the base for this class):
+// -----------------------------------------------------------------------------------------
+// /*
+// *  Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
+// *
+// *  The majority of this is _NOT_ my code.  I simply ported it from the
+// *  PERL Spreadsheet::WriteExcel module.
+// *
+// *  The author of the Spreadsheet::WriteExcel module is John McNamara
+// *  <jmcnamara@cpan.org>
+// *
+// *  I _DO_ maintain this code, and John McNamara has nothing to do with the
+// *  porting of this code to PHP.  Any questions directly related to this
+// *  class library should be directed to me.
+// *
+// *  License Information:
+// *
+// *    Spreadsheet_Excel_Writer:  A library for generating Excel Spreadsheets
+// *    Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com
+// *
+// *    This library is free software; you can redistribute it and/or
+// *    modify it under the terms of the GNU Lesser General Public
+// *    License as published by the Free Software Foundation; either
+// *    version 2.1 of the License, or (at your option) any later version.
+// *
+// *    This library is distributed in the hope that it will be useful,
+// *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+// *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// *    Lesser General Public License for more details.
+// *
+// *    You should have received a copy of the GNU Lesser General Public
+// *    License along with this library; if not, write to the Free Software
+// *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+// */
+class Xf
+{
+    /**
+     * Style XF or a cell XF ?
+     *
+     * @var bool
+     */
+    private $isStyleXf;
+
+    /**
+     * Index to the FONT record. Index 4 does not exist.
+     *
+     * @var int
+     */
+    private $fontIndex;
+
+    /**
+     * An index (2 bytes) to a FORMAT record (number format).
+     *
+     * @var int
+     */
+    private $numberFormatIndex;
+
+    /**
+     * 1 bit, apparently not used.
+     *
+     * @var int
+     */
+    private $textJustLast;
+
+    /**
+     * The cell's foreground color.
+     *
+     * @var int
+     */
+    private $foregroundColor;
+
+    /**
+     * The cell's background color.
+     *
+     * @var int
+     */
+    private $backgroundColor;
+
+    /**
+     * Color of the bottom border of the cell.
+     *
+     * @var int
+     */
+    private $bottomBorderColor;
+
+    /**
+     * Color of the top border of the cell.
+     *
+     * @var int
+     */
+    private $topBorderColor;
+
+    /**
+     * Color of the left border of the cell.
+     *
+     * @var int
+     */
+    private $leftBorderColor;
+
+    /**
+     * Color of the right border of the cell.
+     *
+     * @var int
+     */
+    private $rightBorderColor;
+
+    /**
+     * Constructor.
+     *
+     * @param Style $style The XF format
+     */
+    public function __construct(Style $style)
+    {
+        $this->isStyleXf = false;
+        $this->fontIndex = 0;
+
+        $this->numberFormatIndex = 0;
+
+        $this->textJustLast = 0;
+
+        $this->foregroundColor = 0x40;
+        $this->backgroundColor = 0x41;
+
+        $this->_diag = 0;
+
+        $this->bottomBorderColor = 0x40;
+        $this->topBorderColor = 0x40;
+        $this->leftBorderColor = 0x40;
+        $this->rightBorderColor = 0x40;
+        $this->_diag_color = 0x40;
+        $this->_style = $style;
+    }
+
+    /**
+     * Generate an Excel BIFF XF record (style or cell).
+     *
+     * @return string The XF record
+     */
+    public function writeXf()
+    {
+        // Set the type of the XF record and some of the attributes.
+        if ($this->isStyleXf) {
+            $style = 0xFFF5;
+        } else {
+            $style = self::mapLocked($this->_style->getProtection()->getLocked());
+            $style |= self::mapHidden($this->_style->getProtection()->getHidden()) << 1;
+        }
+
+        // Flags to indicate if attributes have been set.
+        $atr_num = ($this->numberFormatIndex != 0) ? 1 : 0;
+        $atr_fnt = ($this->fontIndex != 0) ? 1 : 0;
+        $atr_alc = ((int) $this->_style->getAlignment()->getWrapText()) ? 1 : 0;
+        $atr_bdr = (self::mapBorderStyle($this->_style->getBorders()->getBottom()->getBorderStyle()) ||
+                        self::mapBorderStyle($this->_style->getBorders()->getTop()->getBorderStyle()) ||
+                        self::mapBorderStyle($this->_style->getBorders()->getLeft()->getBorderStyle()) ||
+                        self::mapBorderStyle($this->_style->getBorders()->getRight()->getBorderStyle())) ? 1 : 0;
+        $atr_pat = (($this->foregroundColor != 0x40) ||
+                        ($this->backgroundColor != 0x41) ||
+                        self::mapFillType($this->_style->getFill()->getFillType())) ? 1 : 0;
+        $atr_prot = self::mapLocked($this->_style->getProtection()->getLocked())
+                        | self::mapHidden($this->_style->getProtection()->getHidden());
+
+        // Zero the default border colour if the border has not been set.
+        if (self::mapBorderStyle($this->_style->getBorders()->getBottom()->getBorderStyle()) == 0) {
+            $this->bottomBorderColor = 0;
+        }
+        if (self::mapBorderStyle($this->_style->getBorders()->getTop()->getBorderStyle()) == 0) {
+            $this->topBorderColor = 0;
+        }
+        if (self::mapBorderStyle($this->_style->getBorders()->getRight()->getBorderStyle()) == 0) {
+            $this->rightBorderColor = 0;
+        }
+        if (self::mapBorderStyle($this->_style->getBorders()->getLeft()->getBorderStyle()) == 0) {
+            $this->leftBorderColor = 0;
+        }
+        if (self::mapBorderStyle($this->_style->getBorders()->getDiagonal()->getBorderStyle()) == 0) {
+            $this->_diag_color = 0;
+        }
+
+        $record = 0x00E0; // Record identifier
+        $length = 0x0014; // Number of bytes to follow
+
+        $ifnt = $this->fontIndex; // Index to FONT record
+        $ifmt = $this->numberFormatIndex; // Index to FORMAT record
+
+        $align = $this->mapHAlign($this->_style->getAlignment()->getHorizontal()); // Alignment
+        $align |= (int) $this->_style->getAlignment()->getWrapText() << 3;
+        $align |= self::mapVAlign($this->_style->getAlignment()->getVertical()) << 4;
+        $align |= $this->textJustLast << 7;
+
+        $used_attrib = $atr_num << 2;
+        $used_attrib |= $atr_fnt << 3;
+        $used_attrib |= $atr_alc << 4;
+        $used_attrib |= $atr_bdr << 5;
+        $used_attrib |= $atr_pat << 6;
+        $used_attrib |= $atr_prot << 7;
+
+        $icv = $this->foregroundColor; // fg and bg pattern colors
+        $icv |= $this->backgroundColor << 7;
+
+        $border1 = self::mapBorderStyle($this->_style->getBorders()->getLeft()->getBorderStyle()); // Border line style and color
+        $border1 |= self::mapBorderStyle($this->_style->getBorders()->getRight()->getBorderStyle()) << 4;
+        $border1 |= self::mapBorderStyle($this->_style->getBorders()->getTop()->getBorderStyle()) << 8;
+        $border1 |= self::mapBorderStyle($this->_style->getBorders()->getBottom()->getBorderStyle()) << 12;
+        $border1 |= $this->leftBorderColor << 16;
+        $border1 |= $this->rightBorderColor << 23;
+
+        $diagonalDirection = $this->_style->getBorders()->getDiagonalDirection();
+        $diag_tl_to_rb = $diagonalDirection == Borders::DIAGONAL_BOTH
+                            || $diagonalDirection == Borders::DIAGONAL_DOWN;
+        $diag_tr_to_lb = $diagonalDirection == Borders::DIAGONAL_BOTH
+                            || $diagonalDirection == Borders::DIAGONAL_UP;
+        $border1 |= $diag_tl_to_rb << 30;
+        $border1 |= $diag_tr_to_lb << 31;
+
+        $border2 = $this->topBorderColor; // Border color
+        $border2 |= $this->bottomBorderColor << 7;
+        $border2 |= $this->_diag_color << 14;
+        $border2 |= self::mapBorderStyle($this->_style->getBorders()->getDiagonal()->getBorderStyle()) << 21;
+        $border2 |= self::mapFillType($this->_style->getFill()->getFillType()) << 26;
+
+        $header = pack('vv', $record, $length);
+
+        //BIFF8 options: identation, shrinkToFit and  text direction
+        $biff8_options = $this->_style->getAlignment()->getIndent();
+        $biff8_options |= (int) $this->_style->getAlignment()->getShrinkToFit() << 4;
+
+        $data = pack('vvvC', $ifnt, $ifmt, $style, $align);
+        $data .= pack('CCC', self::mapTextRotation($this->_style->getAlignment()->getTextRotation()), $biff8_options, $used_attrib);
+        $data .= pack('VVv', $border1, $border2, $icv);
+
+        return $header . $data;
+    }
+
+    /**
+     * Is this a style XF ?
+     *
+     * @param bool $value
+     */
+    public function setIsStyleXf($value): void
+    {
+        $this->isStyleXf = $value;
+    }
+
+    /**
+     * Sets the cell's bottom border color.
+     *
+     * @param int $colorIndex Color index
+     */
+    public function setBottomColor($colorIndex): void
+    {
+        $this->bottomBorderColor = $colorIndex;
+    }
+
+    /**
+     * Sets the cell's top border color.
+     *
+     * @param int $colorIndex Color index
+     */
+    public function setTopColor($colorIndex): void
+    {
+        $this->topBorderColor = $colorIndex;
+    }
+
+    /**
+     * Sets the cell's left border color.
+     *
+     * @param int $colorIndex Color index
+     */
+    public function setLeftColor($colorIndex): void
+    {
+        $this->leftBorderColor = $colorIndex;
+    }
+
+    /**
+     * Sets the cell's right border color.
+     *
+     * @param int $colorIndex Color index
+     */
+    public function setRightColor($colorIndex): void
+    {
+        $this->rightBorderColor = $colorIndex;
+    }
+
+    /**
+     * Sets the cell's diagonal border color.
+     *
+     * @param int $colorIndex Color index
+     */
+    public function setDiagColor($colorIndex): void
+    {
+        $this->_diag_color = $colorIndex;
+    }
+
+    /**
+     * Sets the cell's foreground color.
+     *
+     * @param int $colorIndex Color index
+     */
+    public function setFgColor($colorIndex): void
+    {
+        $this->foregroundColor = $colorIndex;
+    }
+
+    /**
+     * Sets the cell's background color.
+     *
+     * @param int $colorIndex Color index
+     */
+    public function setBgColor($colorIndex): void
+    {
+        $this->backgroundColor = $colorIndex;
+    }
+
+    /**
+     * Sets the index to the number format record
+     * It can be date, time, currency, etc...
+     *
+     * @param int $numberFormatIndex Index to format record
+     */
+    public function setNumberFormatIndex($numberFormatIndex): void
+    {
+        $this->numberFormatIndex = $numberFormatIndex;
+    }
+
+    /**
+     * Set the font index.
+     *
+     * @param int $value Font index, note that value 4 does not exist
+     */
+    public function setFontIndex($value): void
+    {
+        $this->fontIndex = $value;
+    }
+
+    /**
+     * Map of BIFF2-BIFF8 codes for border styles.
+     *
+     * @var array of int
+     */
+    private static $mapBorderStyles = [
+        Border::BORDER_NONE => 0x00,
+        Border::BORDER_THIN => 0x01,
+        Border::BORDER_MEDIUM => 0x02,
+        Border::BORDER_DASHED => 0x03,
+        Border::BORDER_DOTTED => 0x04,
+        Border::BORDER_THICK => 0x05,
+        Border::BORDER_DOUBLE => 0x06,
+        Border::BORDER_HAIR => 0x07,
+        Border::BORDER_MEDIUMDASHED => 0x08,
+        Border::BORDER_DASHDOT => 0x09,
+        Border::BORDER_MEDIUMDASHDOT => 0x0A,
+        Border::BORDER_DASHDOTDOT => 0x0B,
+        Border::BORDER_MEDIUMDASHDOTDOT => 0x0C,
+        Border::BORDER_SLANTDASHDOT => 0x0D,
+    ];
+
+    /**
+     * Map border style.
+     *
+     * @param string $borderStyle
+     *
+     * @return int
+     */
+    private static function mapBorderStyle($borderStyle)
+    {
+        if (isset(self::$mapBorderStyles[$borderStyle])) {
+            return self::$mapBorderStyles[$borderStyle];
+        }
+
+        return 0x00;
+    }
+
+    /**
+     * Map of BIFF2-BIFF8 codes for fill types.
+     *
+     * @var array of int
+     */
+    private static $mapFillTypes = [
+        Fill::FILL_NONE => 0x00,
+        Fill::FILL_SOLID => 0x01,
+        Fill::FILL_PATTERN_MEDIUMGRAY => 0x02,
+        Fill::FILL_PATTERN_DARKGRAY => 0x03,
+        Fill::FILL_PATTERN_LIGHTGRAY => 0x04,
+        Fill::FILL_PATTERN_DARKHORIZONTAL => 0x05,
+        Fill::FILL_PATTERN_DARKVERTICAL => 0x06,
+        Fill::FILL_PATTERN_DARKDOWN => 0x07,
+        Fill::FILL_PATTERN_DARKUP => 0x08,
+        Fill::FILL_PATTERN_DARKGRID => 0x09,
+        Fill::FILL_PATTERN_DARKTRELLIS => 0x0A,
+        Fill::FILL_PATTERN_LIGHTHORIZONTAL => 0x0B,
+        Fill::FILL_PATTERN_LIGHTVERTICAL => 0x0C,
+        Fill::FILL_PATTERN_LIGHTDOWN => 0x0D,
+        Fill::FILL_PATTERN_LIGHTUP => 0x0E,
+        Fill::FILL_PATTERN_LIGHTGRID => 0x0F,
+        Fill::FILL_PATTERN_LIGHTTRELLIS => 0x10,
+        Fill::FILL_PATTERN_GRAY125 => 0x11,
+        Fill::FILL_PATTERN_GRAY0625 => 0x12,
+        Fill::FILL_GRADIENT_LINEAR => 0x00, // does not exist in BIFF8
+        Fill::FILL_GRADIENT_PATH => 0x00, // does not exist in BIFF8
+    ];
+
+    /**
+     * Map fill type.
+     *
+     * @param string $fillType
+     *
+     * @return int
+     */
+    private static function mapFillType($fillType)
+    {
+        if (isset(self::$mapFillTypes[$fillType])) {
+            return self::$mapFillTypes[$fillType];
+        }
+
+        return 0x00;
+    }
+
+    /**
+     * Map of BIFF2-BIFF8 codes for horizontal alignment.
+     *
+     * @var array of int
+     */
+    private static $mapHAlignments = [
+        Alignment::HORIZONTAL_GENERAL => 0,
+        Alignment::HORIZONTAL_LEFT => 1,
+        Alignment::HORIZONTAL_CENTER => 2,
+        Alignment::HORIZONTAL_RIGHT => 3,
+        Alignment::HORIZONTAL_FILL => 4,
+        Alignment::HORIZONTAL_JUSTIFY => 5,
+        Alignment::HORIZONTAL_CENTER_CONTINUOUS => 6,
+    ];
+
+    /**
+     * Map to BIFF2-BIFF8 codes for horizontal alignment.
+     *
+     * @param string $hAlign
+     *
+     * @return int
+     */
+    private function mapHAlign($hAlign)
+    {
+        if (isset(self::$mapHAlignments[$hAlign])) {
+            return self::$mapHAlignments[$hAlign];
+        }
+
+        return 0;
+    }
+
+    /**
+     * Map of BIFF2-BIFF8 codes for vertical alignment.
+     *
+     * @var array of int
+     */
+    private static $mapVAlignments = [
+        Alignment::VERTICAL_TOP => 0,
+        Alignment::VERTICAL_CENTER => 1,
+        Alignment::VERTICAL_BOTTOM => 2,
+        Alignment::VERTICAL_JUSTIFY => 3,
+    ];
+
+    /**
+     * Map to BIFF2-BIFF8 codes for vertical alignment.
+     *
+     * @param string $vAlign
+     *
+     * @return int
+     */
+    private static function mapVAlign($vAlign)
+    {
+        if (isset(self::$mapVAlignments[$vAlign])) {
+            return self::$mapVAlignments[$vAlign];
+        }
+
+        return 2;
+    }
+
+    /**
+     * Map to BIFF8 codes for text rotation angle.
+     *
+     * @param int $textRotation
+     *
+     * @return int
+     */
+    private static function mapTextRotation($textRotation)
+    {
+        if ($textRotation >= 0) {
+            return $textRotation;
+        } elseif ($textRotation == -165) {
+            return 255;
+        } elseif ($textRotation < 0) {
+            return 90 - $textRotation;
+        }
+    }
+
+    /**
+     * Map locked.
+     *
+     * @param string $locked
+     *
+     * @return int
+     */
+    private static function mapLocked($locked)
+    {
+        switch ($locked) {
+            case Protection::PROTECTION_INHERIT:
+                return 1;
+            case Protection::PROTECTION_PROTECTED:
+                return 1;
+            case Protection::PROTECTION_UNPROTECTED:
+                return 0;
+            default:
+                return 1;
+        }
+    }
+
+    /**
+     * Map hidden.
+     *
+     * @param string $hidden
+     *
+     * @return int
+     */
+    private static function mapHidden($hidden)
+    {
+        switch ($hidden) {
+            case Protection::PROTECTION_INHERIT:
+                return 0;
+            case Protection::PROTECTION_PROTECTED:
+                return 1;
+            case Protection::PROTECTION_UNPROTECTED:
+                return 0;
+            default:
+                return 0;
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx.php
new file mode 100755
index 0000000..bfcde04
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx.php
@@ -0,0 +1,538 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
+use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\HashTable;
+use PhpOffice\PhpSpreadsheet\Shared\File;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Worksheet\Drawing as WorksheetDrawing;
+use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
+use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
+use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Chart;
+use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Comments;
+use PhpOffice\PhpSpreadsheet\Writer\Xlsx\ContentTypes;
+use PhpOffice\PhpSpreadsheet\Writer\Xlsx\DocProps;
+use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Drawing;
+use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Rels;
+use PhpOffice\PhpSpreadsheet\Writer\Xlsx\RelsRibbon;
+use PhpOffice\PhpSpreadsheet\Writer\Xlsx\RelsVBA;
+use PhpOffice\PhpSpreadsheet\Writer\Xlsx\StringTable;
+use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Style;
+use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Theme;
+use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Workbook;
+use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Worksheet;
+use ZipArchive;
+use ZipStream\Exception\OverflowException;
+use ZipStream\Option\Archive;
+use ZipStream\ZipStream;
+
+class Xlsx extends BaseWriter
+{
+    /**
+     * Office2003 compatibility.
+     *
+     * @var bool
+     */
+    private $office2003compatibility = false;
+
+    /**
+     * Private writer parts.
+     *
+     * @var Xlsx\WriterPart[]
+     */
+    private $writerParts = [];
+
+    /**
+     * Private Spreadsheet.
+     *
+     * @var Spreadsheet
+     */
+    private $spreadSheet;
+
+    /**
+     * Private string table.
+     *
+     * @var string[]
+     */
+    private $stringTable = [];
+
+    /**
+     * Private unique Conditional HashTable.
+     *
+     * @var HashTable
+     */
+    private $stylesConditionalHashTable;
+
+    /**
+     * Private unique Style HashTable.
+     *
+     * @var HashTable
+     */
+    private $styleHashTable;
+
+    /**
+     * Private unique Fill HashTable.
+     *
+     * @var HashTable
+     */
+    private $fillHashTable;
+
+    /**
+     * Private unique \PhpOffice\PhpSpreadsheet\Style\Font HashTable.
+     *
+     * @var HashTable
+     */
+    private $fontHashTable;
+
+    /**
+     * Private unique Borders HashTable.
+     *
+     * @var HashTable
+     */
+    private $bordersHashTable;
+
+    /**
+     * Private unique NumberFormat HashTable.
+     *
+     * @var HashTable
+     */
+    private $numFmtHashTable;
+
+    /**
+     * Private unique \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\BaseDrawing HashTable.
+     *
+     * @var HashTable
+     */
+    private $drawingHashTable;
+
+    /**
+     * Create a new Xlsx Writer.
+     */
+    public function __construct(Spreadsheet $spreadsheet)
+    {
+        // Assign PhpSpreadsheet
+        $this->setSpreadsheet($spreadsheet);
+
+        $writerPartsArray = [
+            'stringtable' => StringTable::class,
+            'contenttypes' => ContentTypes::class,
+            'docprops' => DocProps::class,
+            'rels' => Rels::class,
+            'theme' => Theme::class,
+            'style' => Style::class,
+            'workbook' => Workbook::class,
+            'worksheet' => Worksheet::class,
+            'drawing' => Drawing::class,
+            'comments' => Comments::class,
+            'chart' => Chart::class,
+            'relsvba' => RelsVBA::class,
+            'relsribbonobjects' => RelsRibbon::class,
+        ];
+
+        //    Initialise writer parts
+        //        and Assign their parent IWriters
+        foreach ($writerPartsArray as $writer => $class) {
+            $this->writerParts[$writer] = new $class($this);
+        }
+
+        $hashTablesArray = ['stylesConditionalHashTable', 'fillHashTable', 'fontHashTable',
+            'bordersHashTable', 'numFmtHashTable', 'drawingHashTable',
+            'styleHashTable',
+        ];
+
+        // Set HashTable variables
+        foreach ($hashTablesArray as $tableName) {
+            $this->$tableName = new HashTable();
+        }
+    }
+
+    /**
+     * Get writer part.
+     *
+     * @param string $pPartName Writer part name
+     *
+     * @return \PhpOffice\PhpSpreadsheet\Writer\Xlsx\WriterPart
+     */
+    public function getWriterPart($pPartName)
+    {
+        if ($pPartName != '' && isset($this->writerParts[strtolower($pPartName)])) {
+            return $this->writerParts[strtolower($pPartName)];
+        }
+
+        return null;
+    }
+
+    /**
+     * Save PhpSpreadsheet to file.
+     *
+     * @param resource|string $pFilename
+     */
+    public function save($pFilename): void
+    {
+        if ($this->spreadSheet !== null) {
+            // garbage collect
+            $this->spreadSheet->garbageCollect();
+
+            $this->openFileHandle($pFilename);
+
+            $saveDebugLog = Calculation::getInstance($this->spreadSheet)->getDebugLog()->getWriteDebugLog();
+            Calculation::getInstance($this->spreadSheet)->getDebugLog()->setWriteDebugLog(false);
+            $saveDateReturnType = Functions::getReturnDateType();
+            Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
+
+            // Create string lookup table
+            $this->stringTable = [];
+            for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
+                $this->stringTable = $this->getWriterPart('StringTable')->createStringTable($this->spreadSheet->getSheet($i), $this->stringTable);
+            }
+
+            // Create styles dictionaries
+            $this->styleHashTable->addFromSource($this->getWriterPart('Style')->allStyles($this->spreadSheet));
+            $this->stylesConditionalHashTable->addFromSource($this->getWriterPart('Style')->allConditionalStyles($this->spreadSheet));
+            $this->fillHashTable->addFromSource($this->getWriterPart('Style')->allFills($this->spreadSheet));
+            $this->fontHashTable->addFromSource($this->getWriterPart('Style')->allFonts($this->spreadSheet));
+            $this->bordersHashTable->addFromSource($this->getWriterPart('Style')->allBorders($this->spreadSheet));
+            $this->numFmtHashTable->addFromSource($this->getWriterPart('Style')->allNumberFormats($this->spreadSheet));
+
+            // Create drawing dictionary
+            $this->drawingHashTable->addFromSource($this->getWriterPart('Drawing')->allDrawings($this->spreadSheet));
+
+            $options = new Archive();
+            $options->setEnableZip64(false);
+            $options->setOutputStream($this->fileHandle);
+
+            $zip = new ZipStream(null, $options);
+
+            // Add [Content_Types].xml to ZIP file
+            $zip->addFile('[Content_Types].xml', $this->getWriterPart('ContentTypes')->writeContentTypes($this->spreadSheet, $this->includeCharts));
+
+            //if hasMacros, add the vbaProject.bin file, Certificate file(if exists)
+            if ($this->spreadSheet->hasMacros()) {
+                $macrosCode = $this->spreadSheet->getMacrosCode();
+                if ($macrosCode !== null) {
+                    // we have the code ?
+                    $zip->addFile('xl/vbaProject.bin', $macrosCode); //allways in 'xl', allways named vbaProject.bin
+                    if ($this->spreadSheet->hasMacrosCertificate()) {
+                        //signed macros ?
+                        // Yes : add the certificate file and the related rels file
+                        $zip->addFile('xl/vbaProjectSignature.bin', $this->spreadSheet->getMacrosCertificate());
+                        $zip->addFile('xl/_rels/vbaProject.bin.rels', $this->getWriterPart('RelsVBA')->writeVBARelationships($this->spreadSheet));
+                    }
+                }
+            }
+            //a custom UI in this workbook ? add it ("base" xml and additional objects (pictures) and rels)
+            if ($this->spreadSheet->hasRibbon()) {
+                $tmpRibbonTarget = $this->spreadSheet->getRibbonXMLData('target');
+                $zip->addFile($tmpRibbonTarget, $this->spreadSheet->getRibbonXMLData('data'));
+                if ($this->spreadSheet->hasRibbonBinObjects()) {
+                    $tmpRootPath = dirname($tmpRibbonTarget) . '/';
+                    $ribbonBinObjects = $this->spreadSheet->getRibbonBinObjects('data'); //the files to write
+                    foreach ($ribbonBinObjects as $aPath => $aContent) {
+                        $zip->addFile($tmpRootPath . $aPath, $aContent);
+                    }
+                    //the rels for files
+                    $zip->addFile($tmpRootPath . '_rels/' . basename($tmpRibbonTarget) . '.rels', $this->getWriterPart('RelsRibbonObjects')->writeRibbonRelationships($this->spreadSheet));
+                }
+            }
+
+            // Add relationships to ZIP file
+            $zip->addFile('_rels/.rels', $this->getWriterPart('Rels')->writeRelationships($this->spreadSheet));
+            $zip->addFile('xl/_rels/workbook.xml.rels', $this->getWriterPart('Rels')->writeWorkbookRelationships($this->spreadSheet));
+
+            // Add document properties to ZIP file
+            $zip->addFile('docProps/app.xml', $this->getWriterPart('DocProps')->writeDocPropsApp($this->spreadSheet));
+            $zip->addFile('docProps/core.xml', $this->getWriterPart('DocProps')->writeDocPropsCore($this->spreadSheet));
+            $customPropertiesPart = $this->getWriterPart('DocProps')->writeDocPropsCustom($this->spreadSheet);
+            if ($customPropertiesPart !== null) {
+                $zip->addFile('docProps/custom.xml', $customPropertiesPart);
+            }
+
+            // Add theme to ZIP file
+            $zip->addFile('xl/theme/theme1.xml', $this->getWriterPart('Theme')->writeTheme($this->spreadSheet));
+
+            // Add string table to ZIP file
+            $zip->addFile('xl/sharedStrings.xml', $this->getWriterPart('StringTable')->writeStringTable($this->stringTable));
+
+            // Add styles to ZIP file
+            $zip->addFile('xl/styles.xml', $this->getWriterPart('Style')->writeStyles($this->spreadSheet));
+
+            // Add workbook to ZIP file
+            $zip->addFile('xl/workbook.xml', $this->getWriterPart('Workbook')->writeWorkbook($this->spreadSheet, $this->preCalculateFormulas));
+
+            $chartCount = 0;
+            // Add worksheets
+            for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
+                $zip->addFile('xl/worksheets/sheet' . ($i + 1) . '.xml', $this->getWriterPart('Worksheet')->writeWorksheet($this->spreadSheet->getSheet($i), $this->stringTable, $this->includeCharts));
+                if ($this->includeCharts) {
+                    $charts = $this->spreadSheet->getSheet($i)->getChartCollection();
+                    if (count($charts) > 0) {
+                        foreach ($charts as $chart) {
+                            $zip->addFile('xl/charts/chart' . ($chartCount + 1) . '.xml', $this->getWriterPart('Chart')->writeChart($chart, $this->preCalculateFormulas));
+                            ++$chartCount;
+                        }
+                    }
+                }
+            }
+
+            $chartRef1 = 0;
+            // Add worksheet relationships (drawings, ...)
+            for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
+                // Add relationships
+                $zip->addFile('xl/worksheets/_rels/sheet' . ($i + 1) . '.xml.rels', $this->getWriterPart('Rels')->writeWorksheetRelationships($this->spreadSheet->getSheet($i), ($i + 1), $this->includeCharts));
+
+                // Add unparsedLoadedData
+                $sheetCodeName = $this->spreadSheet->getSheet($i)->getCodeName();
+                $unparsedLoadedData = $this->spreadSheet->getUnparsedLoadedData();
+                if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['ctrlProps'])) {
+                    foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['ctrlProps'] as $ctrlProp) {
+                        $zip->addFile($ctrlProp['filePath'], $ctrlProp['content']);
+                    }
+                }
+                if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['printerSettings'])) {
+                    foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['printerSettings'] as $ctrlProp) {
+                        $zip->addFile($ctrlProp['filePath'], $ctrlProp['content']);
+                    }
+                }
+
+                $drawings = $this->spreadSheet->getSheet($i)->getDrawingCollection();
+                $drawingCount = count($drawings);
+                if ($this->includeCharts) {
+                    $chartCount = $this->spreadSheet->getSheet($i)->getChartCount();
+                }
+
+                // Add drawing and image relationship parts
+                if (($drawingCount > 0) || ($chartCount > 0)) {
+                    // Drawing relationships
+                    $zip->addFile('xl/drawings/_rels/drawing' . ($i + 1) . '.xml.rels', $this->getWriterPart('Rels')->writeDrawingRelationships($this->spreadSheet->getSheet($i), $chartRef1, $this->includeCharts));
+
+                    // Drawings
+                    $zip->addFile('xl/drawings/drawing' . ($i + 1) . '.xml', $this->getWriterPart('Drawing')->writeDrawings($this->spreadSheet->getSheet($i), $this->includeCharts));
+                } elseif (isset($unparsedLoadedData['sheets'][$sheetCodeName]['drawingAlternateContents'])) {
+                    // Drawings
+                    $zip->addFile('xl/drawings/drawing' . ($i + 1) . '.xml', $this->getWriterPart('Drawing')->writeDrawings($this->spreadSheet->getSheet($i), $this->includeCharts));
+                }
+
+                // Add unparsed drawings
+                if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['Drawings'])) {
+                    foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['Drawings'] as $relId => $drawingXml) {
+                        $drawingFile = array_search($relId, $unparsedLoadedData['sheets'][$sheetCodeName]['drawingOriginalIds']);
+                        if ($drawingFile !== false) {
+                            $drawingFile = ltrim($drawingFile, '.');
+                            $zip->addFile('xl' . $drawingFile, $drawingXml);
+                        }
+                    }
+                }
+
+                // Add comment relationship parts
+                if (count($this->spreadSheet->getSheet($i)->getComments()) > 0) {
+                    // VML Comments
+                    $zip->addFile('xl/drawings/vmlDrawing' . ($i + 1) . '.vml', $this->getWriterPart('Comments')->writeVMLComments($this->spreadSheet->getSheet($i)));
+
+                    // Comments
+                    $zip->addFile('xl/comments' . ($i + 1) . '.xml', $this->getWriterPart('Comments')->writeComments($this->spreadSheet->getSheet($i)));
+                }
+
+                // Add unparsed relationship parts
+                if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['vmlDrawings'])) {
+                    foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['vmlDrawings'] as $vmlDrawing) {
+                        $zip->addFile($vmlDrawing['filePath'], $vmlDrawing['content']);
+                    }
+                }
+
+                // Add header/footer relationship parts
+                if (count($this->spreadSheet->getSheet($i)->getHeaderFooter()->getImages()) > 0) {
+                    // VML Drawings
+                    $zip->addFile('xl/drawings/vmlDrawingHF' . ($i + 1) . '.vml', $this->getWriterPart('Drawing')->writeVMLHeaderFooterImages($this->spreadSheet->getSheet($i)));
+
+                    // VML Drawing relationships
+                    $zip->addFile('xl/drawings/_rels/vmlDrawingHF' . ($i + 1) . '.vml.rels', $this->getWriterPart('Rels')->writeHeaderFooterDrawingRelationships($this->spreadSheet->getSheet($i)));
+
+                    // Media
+                    foreach ($this->spreadSheet->getSheet($i)->getHeaderFooter()->getImages() as $image) {
+                        $zip->addFile('xl/media/' . $image->getIndexedFilename(), file_get_contents($image->getPath()));
+                    }
+                }
+            }
+
+            // Add media
+            for ($i = 0; $i < $this->getDrawingHashTable()->count(); ++$i) {
+                if ($this->getDrawingHashTable()->getByIndex($i) instanceof WorksheetDrawing) {
+                    $imageContents = null;
+                    $imagePath = $this->getDrawingHashTable()->getByIndex($i)->getPath();
+                    if (strpos($imagePath, 'zip://') !== false) {
+                        $imagePath = substr($imagePath, 6);
+                        $imagePathSplitted = explode('#', $imagePath);
+
+                        $imageZip = new ZipArchive();
+                        $imageZip->open($imagePathSplitted[0]);
+                        $imageContents = $imageZip->getFromName($imagePathSplitted[1]);
+                        $imageZip->close();
+                        unset($imageZip);
+                    } else {
+                        $imageContents = file_get_contents($imagePath);
+                    }
+
+                    $zip->addFile('xl/media/' . str_replace(' ', '_', $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename()), $imageContents);
+                } elseif ($this->getDrawingHashTable()->getByIndex($i) instanceof MemoryDrawing) {
+                    ob_start();
+                    call_user_func(
+                        $this->getDrawingHashTable()->getByIndex($i)->getRenderingFunction(),
+                        $this->getDrawingHashTable()->getByIndex($i)->getImageResource()
+                    );
+                    $imageContents = ob_get_contents();
+                    ob_end_clean();
+
+                    $zip->addFile('xl/media/' . str_replace(' ', '_', $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename()), $imageContents);
+                }
+            }
+
+            Functions::setReturnDateType($saveDateReturnType);
+            Calculation::getInstance($this->spreadSheet)->getDebugLog()->setWriteDebugLog($saveDebugLog);
+
+            // Close file
+            try {
+                $zip->finish();
+            } catch (OverflowException $e) {
+                throw new WriterException('Could not close resource.');
+            }
+
+            $this->maybeCloseFileHandle();
+        } else {
+            throw new WriterException('PhpSpreadsheet object unassigned.');
+        }
+    }
+
+    /**
+     * Get Spreadsheet object.
+     *
+     * @return Spreadsheet
+     */
+    public function getSpreadsheet()
+    {
+        if ($this->spreadSheet !== null) {
+            return $this->spreadSheet;
+        }
+
+        throw new WriterException('No Spreadsheet object assigned.');
+    }
+
+    /**
+     * Set Spreadsheet object.
+     *
+     * @param Spreadsheet $spreadsheet PhpSpreadsheet object
+     *
+     * @return $this
+     */
+    public function setSpreadsheet(Spreadsheet $spreadsheet)
+    {
+        $this->spreadSheet = $spreadsheet;
+
+        return $this;
+    }
+
+    /**
+     * Get string table.
+     *
+     * @return string[]
+     */
+    public function getStringTable()
+    {
+        return $this->stringTable;
+    }
+
+    /**
+     * Get Style HashTable.
+     *
+     * @return HashTable
+     */
+    public function getStyleHashTable()
+    {
+        return $this->styleHashTable;
+    }
+
+    /**
+     * Get Conditional HashTable.
+     *
+     * @return HashTable
+     */
+    public function getStylesConditionalHashTable()
+    {
+        return $this->stylesConditionalHashTable;
+    }
+
+    /**
+     * Get Fill HashTable.
+     *
+     * @return HashTable
+     */
+    public function getFillHashTable()
+    {
+        return $this->fillHashTable;
+    }
+
+    /**
+     * Get \PhpOffice\PhpSpreadsheet\Style\Font HashTable.
+     *
+     * @return HashTable
+     */
+    public function getFontHashTable()
+    {
+        return $this->fontHashTable;
+    }
+
+    /**
+     * Get Borders HashTable.
+     *
+     * @return HashTable
+     */
+    public function getBordersHashTable()
+    {
+        return $this->bordersHashTable;
+    }
+
+    /**
+     * Get NumberFormat HashTable.
+     *
+     * @return HashTable
+     */
+    public function getNumFmtHashTable()
+    {
+        return $this->numFmtHashTable;
+    }
+
+    /**
+     * Get \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\BaseDrawing HashTable.
+     *
+     * @return HashTable
+     */
+    public function getDrawingHashTable()
+    {
+        return $this->drawingHashTable;
+    }
+
+    /**
+     * Get Office2003 compatibility.
+     *
+     * @return bool
+     */
+    public function getOffice2003Compatibility()
+    {
+        return $this->office2003compatibility;
+    }
+
+    /**
+     * Set Office2003 compatibility.
+     *
+     * @param bool $pValue Office2003 compatibility?
+     *
+     * @return $this
+     */
+    public function setOffice2003Compatibility($pValue)
+    {
+        $this->office2003compatibility = $pValue;
+
+        return $this;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Chart.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Chart.php
new file mode 100755
index 0000000..583b262
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Chart.php
@@ -0,0 +1,1516 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Chart\Axis;
+use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
+use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
+use PhpOffice\PhpSpreadsheet\Chart\GridLines;
+use PhpOffice\PhpSpreadsheet\Chart\Layout;
+use PhpOffice\PhpSpreadsheet\Chart\Legend;
+use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
+use PhpOffice\PhpSpreadsheet\Chart\Title;
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
+use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
+
+class Chart extends WriterPart
+{
+    protected $calculateCellValues;
+
+    /**
+     * @var int
+     */
+    private $seriesIndex;
+
+    /**
+     * Write charts to XML format.
+     *
+     * @param mixed $calculateCellValues
+     *
+     * @return string XML Output
+     */
+    public function writeChart(\PhpOffice\PhpSpreadsheet\Chart\Chart $pChart, $calculateCellValues = true)
+    {
+        $this->calculateCellValues = $calculateCellValues;
+
+        // Create XML writer
+        $objWriter = null;
+        if ($this->getParentWriter()->getUseDiskCaching()) {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+        } else {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
+        }
+        //    Ensure that data series values are up-to-date before we save
+        if ($this->calculateCellValues) {
+            $pChart->refresh();
+        }
+
+        // XML header
+        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+        // c:chartSpace
+        $objWriter->startElement('c:chartSpace');
+        $objWriter->writeAttribute('xmlns:c', 'http://schemas.openxmlformats.org/drawingml/2006/chart');
+        $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main');
+        $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
+
+        $objWriter->startElement('c:date1904');
+        $objWriter->writeAttribute('val', 0);
+        $objWriter->endElement();
+        $objWriter->startElement('c:lang');
+        $objWriter->writeAttribute('val', 'en-GB');
+        $objWriter->endElement();
+        $objWriter->startElement('c:roundedCorners');
+        $objWriter->writeAttribute('val', 0);
+        $objWriter->endElement();
+
+        $this->writeAlternateContent($objWriter);
+
+        $objWriter->startElement('c:chart');
+
+        $this->writeTitle($objWriter, $pChart->getTitle());
+
+        $objWriter->startElement('c:autoTitleDeleted');
+        $objWriter->writeAttribute('val', 0);
+        $objWriter->endElement();
+
+        $this->writePlotArea($objWriter, $pChart->getWorksheet(), $pChart->getPlotArea(), $pChart->getXAxisLabel(), $pChart->getYAxisLabel(), $pChart->getChartAxisX(), $pChart->getChartAxisY(), $pChart->getMajorGridlines(), $pChart->getMinorGridlines());
+
+        $this->writeLegend($objWriter, $pChart->getLegend());
+
+        $objWriter->startElement('c:plotVisOnly');
+        $objWriter->writeAttribute('val', (int) $pChart->getPlotVisibleOnly());
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:dispBlanksAs');
+        $objWriter->writeAttribute('val', $pChart->getDisplayBlanksAs());
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:showDLblsOverMax');
+        $objWriter->writeAttribute('val', 0);
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $this->writePrintSettings($objWriter);
+
+        $objWriter->endElement();
+
+        // Return
+        return $objWriter->getData();
+    }
+
+    /**
+     * Write Chart Title.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param Title $title
+     */
+    private function writeTitle(XMLWriter $objWriter, ?Title $title = null): void
+    {
+        if ($title === null) {
+            return;
+        }
+
+        $objWriter->startElement('c:title');
+        $objWriter->startElement('c:tx');
+        $objWriter->startElement('c:rich');
+
+        $objWriter->startElement('a:bodyPr');
+        $objWriter->endElement();
+
+        $objWriter->startElement('a:lstStyle');
+        $objWriter->endElement();
+
+        $objWriter->startElement('a:p');
+
+        $caption = $title->getCaption();
+        if ((is_array($caption)) && (count($caption) > 0)) {
+            $caption = $caption[0];
+        }
+        $this->getParentWriter()->getWriterPart('stringtable')->writeRichTextForCharts($objWriter, $caption, 'a');
+
+        $objWriter->endElement();
+        $objWriter->endElement();
+        $objWriter->endElement();
+
+        $this->writeLayout($objWriter, $title->getLayout());
+
+        $objWriter->startElement('c:overlay');
+        $objWriter->writeAttribute('val', 0);
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write Chart Legend.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param Legend $legend
+     */
+    private function writeLegend(XMLWriter $objWriter, ?Legend $legend = null): void
+    {
+        if ($legend === null) {
+            return;
+        }
+
+        $objWriter->startElement('c:legend');
+
+        $objWriter->startElement('c:legendPos');
+        $objWriter->writeAttribute('val', $legend->getPosition());
+        $objWriter->endElement();
+
+        $this->writeLayout($objWriter, $legend->getLayout());
+
+        $objWriter->startElement('c:overlay');
+        $objWriter->writeAttribute('val', ($legend->getOverlay()) ? '1' : '0');
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:txPr');
+        $objWriter->startElement('a:bodyPr');
+        $objWriter->endElement();
+
+        $objWriter->startElement('a:lstStyle');
+        $objWriter->endElement();
+
+        $objWriter->startElement('a:p');
+        $objWriter->startElement('a:pPr');
+        $objWriter->writeAttribute('rtl', 0);
+
+        $objWriter->startElement('a:defRPr');
+        $objWriter->endElement();
+        $objWriter->endElement();
+
+        $objWriter->startElement('a:endParaRPr');
+        $objWriter->writeAttribute('lang', 'en-US');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write Chart Plot Area.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param Title $xAxisLabel
+     * @param Title $yAxisLabel
+     * @param Axis $xAxis
+     * @param Axis $yAxis
+     */
+    private function writePlotArea(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pSheet, PlotArea $plotArea, ?Title $xAxisLabel = null, ?Title $yAxisLabel = null, ?Axis $xAxis = null, ?Axis $yAxis = null, ?GridLines $majorGridlines = null, ?GridLines $minorGridlines = null): void
+    {
+        if ($plotArea === null) {
+            return;
+        }
+
+        $id1 = $id2 = 0;
+        $this->seriesIndex = 0;
+        $objWriter->startElement('c:plotArea');
+
+        $layout = $plotArea->getLayout();
+
+        $this->writeLayout($objWriter, $layout);
+
+        $chartTypes = self::getChartType($plotArea);
+        $catIsMultiLevelSeries = $valIsMultiLevelSeries = false;
+        $plotGroupingType = '';
+        foreach ($chartTypes as $chartType) {
+            $objWriter->startElement('c:' . $chartType);
+
+            $groupCount = $plotArea->getPlotGroupCount();
+            for ($i = 0; $i < $groupCount; ++$i) {
+                $plotGroup = $plotArea->getPlotGroupByIndex($i);
+                $groupType = $plotGroup->getPlotType();
+                if ($groupType == $chartType) {
+                    $plotStyle = $plotGroup->getPlotStyle();
+                    if ($groupType === DataSeries::TYPE_RADARCHART) {
+                        $objWriter->startElement('c:radarStyle');
+                        $objWriter->writeAttribute('val', $plotStyle);
+                        $objWriter->endElement();
+                    } elseif ($groupType === DataSeries::TYPE_SCATTERCHART) {
+                        $objWriter->startElement('c:scatterStyle');
+                        $objWriter->writeAttribute('val', $plotStyle);
+                        $objWriter->endElement();
+                    }
+
+                    $this->writePlotGroup($plotGroup, $chartType, $objWriter, $catIsMultiLevelSeries, $valIsMultiLevelSeries, $plotGroupingType);
+                }
+            }
+
+            $this->writeDataLabels($objWriter, $layout);
+
+            if ($chartType === DataSeries::TYPE_LINECHART) {
+                //    Line only, Line3D can't be smoothed
+                $objWriter->startElement('c:smooth');
+                $objWriter->writeAttribute('val', (int) $plotGroup->getSmoothLine());
+                $objWriter->endElement();
+            } elseif (($chartType === DataSeries::TYPE_BARCHART) || ($chartType === DataSeries::TYPE_BARCHART_3D)) {
+                $objWriter->startElement('c:gapWidth');
+                $objWriter->writeAttribute('val', 150);
+                $objWriter->endElement();
+
+                if ($plotGroupingType == 'percentStacked' || $plotGroupingType == 'stacked') {
+                    $objWriter->startElement('c:overlap');
+                    $objWriter->writeAttribute('val', 100);
+                    $objWriter->endElement();
+                }
+            } elseif ($chartType === DataSeries::TYPE_BUBBLECHART) {
+                $objWriter->startElement('c:bubbleScale');
+                $objWriter->writeAttribute('val', 25);
+                $objWriter->endElement();
+
+                $objWriter->startElement('c:showNegBubbles');
+                $objWriter->writeAttribute('val', 0);
+                $objWriter->endElement();
+            } elseif ($chartType === DataSeries::TYPE_STOCKCHART) {
+                $objWriter->startElement('c:hiLowLines');
+                $objWriter->endElement();
+
+                $objWriter->startElement('c:upDownBars');
+
+                $objWriter->startElement('c:gapWidth');
+                $objWriter->writeAttribute('val', 300);
+                $objWriter->endElement();
+
+                $objWriter->startElement('c:upBars');
+                $objWriter->endElement();
+
+                $objWriter->startElement('c:downBars');
+                $objWriter->endElement();
+
+                $objWriter->endElement();
+            }
+
+            //    Generate 2 unique numbers to use for axId values
+            $id1 = '75091328';
+            $id2 = '75089408';
+
+            if (($chartType !== DataSeries::TYPE_PIECHART) && ($chartType !== DataSeries::TYPE_PIECHART_3D) && ($chartType !== DataSeries::TYPE_DONUTCHART)) {
+                $objWriter->startElement('c:axId');
+                $objWriter->writeAttribute('val', $id1);
+                $objWriter->endElement();
+                $objWriter->startElement('c:axId');
+                $objWriter->writeAttribute('val', $id2);
+                $objWriter->endElement();
+            } else {
+                $objWriter->startElement('c:firstSliceAng');
+                $objWriter->writeAttribute('val', 0);
+                $objWriter->endElement();
+
+                if ($chartType === DataSeries::TYPE_DONUTCHART) {
+                    $objWriter->startElement('c:holeSize');
+                    $objWriter->writeAttribute('val', 50);
+                    $objWriter->endElement();
+                }
+            }
+
+            $objWriter->endElement();
+        }
+
+        if (($chartType !== DataSeries::TYPE_PIECHART) && ($chartType !== DataSeries::TYPE_PIECHART_3D) && ($chartType !== DataSeries::TYPE_DONUTCHART)) {
+            if ($chartType === DataSeries::TYPE_BUBBLECHART) {
+                $this->writeValueAxis($objWriter, $xAxisLabel, $chartType, $id1, $id2, $catIsMultiLevelSeries, $xAxis, $majorGridlines, $minorGridlines);
+            } else {
+                $this->writeCategoryAxis($objWriter, $xAxisLabel, $id1, $id2, $catIsMultiLevelSeries, $yAxis);
+            }
+
+            $this->writeValueAxis($objWriter, $yAxisLabel, $chartType, $id1, $id2, $valIsMultiLevelSeries, $xAxis, $majorGridlines, $minorGridlines);
+        }
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write Data Labels.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param \PhpOffice\PhpSpreadsheet\Chart\Layout $chartLayout Chart layout
+     */
+    private function writeDataLabels(XMLWriter $objWriter, ?Layout $chartLayout = null): void
+    {
+        $objWriter->startElement('c:dLbls');
+
+        $objWriter->startElement('c:showLegendKey');
+        $showLegendKey = (empty($chartLayout)) ? 0 : $chartLayout->getShowLegendKey();
+        $objWriter->writeAttribute('val', ((empty($showLegendKey)) ? 0 : 1));
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:showVal');
+        $showVal = (empty($chartLayout)) ? 0 : $chartLayout->getShowVal();
+        $objWriter->writeAttribute('val', ((empty($showVal)) ? 0 : 1));
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:showCatName');
+        $showCatName = (empty($chartLayout)) ? 0 : $chartLayout->getShowCatName();
+        $objWriter->writeAttribute('val', ((empty($showCatName)) ? 0 : 1));
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:showSerName');
+        $showSerName = (empty($chartLayout)) ? 0 : $chartLayout->getShowSerName();
+        $objWriter->writeAttribute('val', ((empty($showSerName)) ? 0 : 1));
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:showPercent');
+        $showPercent = (empty($chartLayout)) ? 0 : $chartLayout->getShowPercent();
+        $objWriter->writeAttribute('val', ((empty($showPercent)) ? 0 : 1));
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:showBubbleSize');
+        $showBubbleSize = (empty($chartLayout)) ? 0 : $chartLayout->getShowBubbleSize();
+        $objWriter->writeAttribute('val', ((empty($showBubbleSize)) ? 0 : 1));
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:showLeaderLines');
+        $showLeaderLines = (empty($chartLayout)) ? 1 : $chartLayout->getShowLeaderLines();
+        $objWriter->writeAttribute('val', ((empty($showLeaderLines)) ? 0 : 1));
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write Category Axis.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param Title $xAxisLabel
+     * @param string $id1
+     * @param string $id2
+     * @param bool $isMultiLevelSeries
+     */
+    private function writeCategoryAxis($objWriter, $xAxisLabel, $id1, $id2, $isMultiLevelSeries, Axis $yAxis): void
+    {
+        $objWriter->startElement('c:catAx');
+
+        if ($id1 > 0) {
+            $objWriter->startElement('c:axId');
+            $objWriter->writeAttribute('val', $id1);
+            $objWriter->endElement();
+        }
+
+        $objWriter->startElement('c:scaling');
+        $objWriter->startElement('c:orientation');
+        $objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('orientation'));
+        $objWriter->endElement();
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:delete');
+        $objWriter->writeAttribute('val', 0);
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:axPos');
+        $objWriter->writeAttribute('val', 'b');
+        $objWriter->endElement();
+
+        if ($xAxisLabel !== null) {
+            $objWriter->startElement('c:title');
+            $objWriter->startElement('c:tx');
+            $objWriter->startElement('c:rich');
+
+            $objWriter->startElement('a:bodyPr');
+            $objWriter->endElement();
+
+            $objWriter->startElement('a:lstStyle');
+            $objWriter->endElement();
+
+            $objWriter->startElement('a:p');
+            $objWriter->startElement('a:r');
+
+            $caption = $xAxisLabel->getCaption();
+            if (is_array($caption)) {
+                $caption = $caption[0];
+            }
+            $objWriter->startElement('a:t');
+            $objWriter->writeRawData(StringHelper::controlCharacterPHP2OOXML($caption));
+            $objWriter->endElement();
+
+            $objWriter->endElement();
+            $objWriter->endElement();
+            $objWriter->endElement();
+            $objWriter->endElement();
+
+            $layout = $xAxisLabel->getLayout();
+            $this->writeLayout($objWriter, $layout);
+
+            $objWriter->startElement('c:overlay');
+            $objWriter->writeAttribute('val', 0);
+            $objWriter->endElement();
+
+            $objWriter->endElement();
+        }
+
+        $objWriter->startElement('c:numFmt');
+        $objWriter->writeAttribute('formatCode', $yAxis->getAxisNumberFormat());
+        $objWriter->writeAttribute('sourceLinked', $yAxis->getAxisNumberSourceLinked());
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:majorTickMark');
+        $objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('major_tick_mark'));
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:minorTickMark');
+        $objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('minor_tick_mark'));
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:tickLblPos');
+        $objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('axis_labels'));
+        $objWriter->endElement();
+
+        if ($id2 > 0) {
+            $objWriter->startElement('c:crossAx');
+            $objWriter->writeAttribute('val', $id2);
+            $objWriter->endElement();
+
+            $objWriter->startElement('c:crosses');
+            $objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('horizontal_crosses'));
+            $objWriter->endElement();
+        }
+
+        $objWriter->startElement('c:auto');
+        $objWriter->writeAttribute('val', 1);
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:lblAlgn');
+        $objWriter->writeAttribute('val', 'ctr');
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:lblOffset');
+        $objWriter->writeAttribute('val', 100);
+        $objWriter->endElement();
+
+        if ($isMultiLevelSeries) {
+            $objWriter->startElement('c:noMultiLvlLbl');
+            $objWriter->writeAttribute('val', 0);
+            $objWriter->endElement();
+        }
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write Value Axis.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param Title $yAxisLabel
+     * @param string $groupType Chart type
+     * @param string $id1
+     * @param string $id2
+     * @param bool $isMultiLevelSeries
+     */
+    private function writeValueAxis($objWriter, $yAxisLabel, $groupType, $id1, $id2, $isMultiLevelSeries, Axis $xAxis, GridLines $majorGridlines, GridLines $minorGridlines): void
+    {
+        $objWriter->startElement('c:valAx');
+
+        if ($id2 > 0) {
+            $objWriter->startElement('c:axId');
+            $objWriter->writeAttribute('val', $id2);
+            $objWriter->endElement();
+        }
+
+        $objWriter->startElement('c:scaling');
+
+        if ($xAxis->getAxisOptionsProperty('maximum') !== null) {
+            $objWriter->startElement('c:max');
+            $objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('maximum'));
+            $objWriter->endElement();
+        }
+
+        if ($xAxis->getAxisOptionsProperty('minimum') !== null) {
+            $objWriter->startElement('c:min');
+            $objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('minimum'));
+            $objWriter->endElement();
+        }
+
+        $objWriter->startElement('c:orientation');
+        $objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('orientation'));
+
+        $objWriter->endElement();
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:delete');
+        $objWriter->writeAttribute('val', 0);
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:axPos');
+        $objWriter->writeAttribute('val', 'l');
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:majorGridlines');
+        $objWriter->startElement('c:spPr');
+
+        if ($majorGridlines->getLineColorProperty('value') !== null) {
+            $objWriter->startElement('a:ln');
+            $objWriter->writeAttribute('w', $majorGridlines->getLineStyleProperty('width'));
+            $objWriter->startElement('a:solidFill');
+            $objWriter->startElement("a:{$majorGridlines->getLineColorProperty('type')}");
+            $objWriter->writeAttribute('val', $majorGridlines->getLineColorProperty('value'));
+            $objWriter->startElement('a:alpha');
+            $objWriter->writeAttribute('val', $majorGridlines->getLineColorProperty('alpha'));
+            $objWriter->endElement(); //end alpha
+            $objWriter->endElement(); //end srgbClr
+            $objWriter->endElement(); //end solidFill
+
+            $objWriter->startElement('a:prstDash');
+            $objWriter->writeAttribute('val', $majorGridlines->getLineStyleProperty('dash'));
+            $objWriter->endElement();
+
+            if ($majorGridlines->getLineStyleProperty('join') == 'miter') {
+                $objWriter->startElement('a:miter');
+                $objWriter->writeAttribute('lim', '800000');
+                $objWriter->endElement();
+            } else {
+                $objWriter->startElement('a:bevel');
+                $objWriter->endElement();
+            }
+
+            if ($majorGridlines->getLineStyleProperty(['arrow', 'head', 'type']) !== null) {
+                $objWriter->startElement('a:headEnd');
+                $objWriter->writeAttribute('type', $majorGridlines->getLineStyleProperty(['arrow', 'head', 'type']));
+                $objWriter->writeAttribute('w', $majorGridlines->getLineStyleArrowParameters('head', 'w'));
+                $objWriter->writeAttribute('len', $majorGridlines->getLineStyleArrowParameters('head', 'len'));
+                $objWriter->endElement();
+            }
+
+            if ($majorGridlines->getLineStyleProperty(['arrow', 'end', 'type']) !== null) {
+                $objWriter->startElement('a:tailEnd');
+                $objWriter->writeAttribute('type', $majorGridlines->getLineStyleProperty(['arrow', 'end', 'type']));
+                $objWriter->writeAttribute('w', $majorGridlines->getLineStyleArrowParameters('end', 'w'));
+                $objWriter->writeAttribute('len', $majorGridlines->getLineStyleArrowParameters('end', 'len'));
+                $objWriter->endElement();
+            }
+            $objWriter->endElement(); //end ln
+        }
+        $objWriter->startElement('a:effectLst');
+
+        if ($majorGridlines->getGlowSize() !== null) {
+            $objWriter->startElement('a:glow');
+            $objWriter->writeAttribute('rad', $majorGridlines->getGlowSize());
+            $objWriter->startElement("a:{$majorGridlines->getGlowColor('type')}");
+            $objWriter->writeAttribute('val', $majorGridlines->getGlowColor('value'));
+            $objWriter->startElement('a:alpha');
+            $objWriter->writeAttribute('val', $majorGridlines->getGlowColor('alpha'));
+            $objWriter->endElement(); //end alpha
+            $objWriter->endElement(); //end schemeClr
+            $objWriter->endElement(); //end glow
+        }
+
+        if ($majorGridlines->getShadowProperty('presets') !== null) {
+            $objWriter->startElement("a:{$majorGridlines->getShadowProperty('effect')}");
+            if ($majorGridlines->getShadowProperty('blur') !== null) {
+                $objWriter->writeAttribute('blurRad', $majorGridlines->getShadowProperty('blur'));
+            }
+            if ($majorGridlines->getShadowProperty('distance') !== null) {
+                $objWriter->writeAttribute('dist', $majorGridlines->getShadowProperty('distance'));
+            }
+            if ($majorGridlines->getShadowProperty('direction') !== null) {
+                $objWriter->writeAttribute('dir', $majorGridlines->getShadowProperty('direction'));
+            }
+            if ($majorGridlines->getShadowProperty('algn') !== null) {
+                $objWriter->writeAttribute('algn', $majorGridlines->getShadowProperty('algn'));
+            }
+            if ($majorGridlines->getShadowProperty(['size', 'sx']) !== null) {
+                $objWriter->writeAttribute('sx', $majorGridlines->getShadowProperty(['size', 'sx']));
+            }
+            if ($majorGridlines->getShadowProperty(['size', 'sy']) !== null) {
+                $objWriter->writeAttribute('sy', $majorGridlines->getShadowProperty(['size', 'sy']));
+            }
+            if ($majorGridlines->getShadowProperty(['size', 'kx']) !== null) {
+                $objWriter->writeAttribute('kx', $majorGridlines->getShadowProperty(['size', 'kx']));
+            }
+            if ($majorGridlines->getShadowProperty('rotWithShape') !== null) {
+                $objWriter->writeAttribute('rotWithShape', $majorGridlines->getShadowProperty('rotWithShape'));
+            }
+            $objWriter->startElement("a:{$majorGridlines->getShadowProperty(['color', 'type'])}");
+            $objWriter->writeAttribute('val', $majorGridlines->getShadowProperty(['color', 'value']));
+
+            $objWriter->startElement('a:alpha');
+            $objWriter->writeAttribute('val', $majorGridlines->getShadowProperty(['color', 'alpha']));
+            $objWriter->endElement(); //end alpha
+
+            $objWriter->endElement(); //end color:type
+            $objWriter->endElement(); //end shadow
+        }
+
+        if ($majorGridlines->getSoftEdgesSize() !== null) {
+            $objWriter->startElement('a:softEdge');
+            $objWriter->writeAttribute('rad', $majorGridlines->getSoftEdgesSize());
+            $objWriter->endElement(); //end softEdge
+        }
+
+        $objWriter->endElement(); //end effectLst
+        $objWriter->endElement(); //end spPr
+        $objWriter->endElement(); //end majorGridLines
+
+        if ($minorGridlines->getObjectState()) {
+            $objWriter->startElement('c:minorGridlines');
+            $objWriter->startElement('c:spPr');
+
+            if ($minorGridlines->getLineColorProperty('value') !== null) {
+                $objWriter->startElement('a:ln');
+                $objWriter->writeAttribute('w', $minorGridlines->getLineStyleProperty('width'));
+                $objWriter->startElement('a:solidFill');
+                $objWriter->startElement("a:{$minorGridlines->getLineColorProperty('type')}");
+                $objWriter->writeAttribute('val', $minorGridlines->getLineColorProperty('value'));
+                $objWriter->startElement('a:alpha');
+                $objWriter->writeAttribute('val', $minorGridlines->getLineColorProperty('alpha'));
+                $objWriter->endElement(); //end alpha
+                $objWriter->endElement(); //end srgbClr
+                $objWriter->endElement(); //end solidFill
+
+                $objWriter->startElement('a:prstDash');
+                $objWriter->writeAttribute('val', $minorGridlines->getLineStyleProperty('dash'));
+                $objWriter->endElement();
+
+                if ($minorGridlines->getLineStyleProperty('join') == 'miter') {
+                    $objWriter->startElement('a:miter');
+                    $objWriter->writeAttribute('lim', '800000');
+                    $objWriter->endElement();
+                } else {
+                    $objWriter->startElement('a:bevel');
+                    $objWriter->endElement();
+                }
+
+                if ($minorGridlines->getLineStyleProperty(['arrow', 'head', 'type']) !== null) {
+                    $objWriter->startElement('a:headEnd');
+                    $objWriter->writeAttribute('type', $minorGridlines->getLineStyleProperty(['arrow', 'head', 'type']));
+                    $objWriter->writeAttribute('w', $minorGridlines->getLineStyleArrowParameters('head', 'w'));
+                    $objWriter->writeAttribute('len', $minorGridlines->getLineStyleArrowParameters('head', 'len'));
+                    $objWriter->endElement();
+                }
+
+                if ($minorGridlines->getLineStyleProperty(['arrow', 'end', 'type']) !== null) {
+                    $objWriter->startElement('a:tailEnd');
+                    $objWriter->writeAttribute('type', $minorGridlines->getLineStyleProperty(['arrow', 'end', 'type']));
+                    $objWriter->writeAttribute('w', $minorGridlines->getLineStyleArrowParameters('end', 'w'));
+                    $objWriter->writeAttribute('len', $minorGridlines->getLineStyleArrowParameters('end', 'len'));
+                    $objWriter->endElement();
+                }
+                $objWriter->endElement(); //end ln
+            }
+
+            $objWriter->startElement('a:effectLst');
+
+            if ($minorGridlines->getGlowSize() !== null) {
+                $objWriter->startElement('a:glow');
+                $objWriter->writeAttribute('rad', $minorGridlines->getGlowSize());
+                $objWriter->startElement("a:{$minorGridlines->getGlowColor('type')}");
+                $objWriter->writeAttribute('val', $minorGridlines->getGlowColor('value'));
+                $objWriter->startElement('a:alpha');
+                $objWriter->writeAttribute('val', $minorGridlines->getGlowColor('alpha'));
+                $objWriter->endElement(); //end alpha
+                $objWriter->endElement(); //end schemeClr
+                $objWriter->endElement(); //end glow
+            }
+
+            if ($minorGridlines->getShadowProperty('presets') !== null) {
+                $objWriter->startElement("a:{$minorGridlines->getShadowProperty('effect')}");
+                if ($minorGridlines->getShadowProperty('blur') !== null) {
+                    $objWriter->writeAttribute('blurRad', $minorGridlines->getShadowProperty('blur'));
+                }
+                if ($minorGridlines->getShadowProperty('distance') !== null) {
+                    $objWriter->writeAttribute('dist', $minorGridlines->getShadowProperty('distance'));
+                }
+                if ($minorGridlines->getShadowProperty('direction') !== null) {
+                    $objWriter->writeAttribute('dir', $minorGridlines->getShadowProperty('direction'));
+                }
+                if ($minorGridlines->getShadowProperty('algn') !== null) {
+                    $objWriter->writeAttribute('algn', $minorGridlines->getShadowProperty('algn'));
+                }
+                if ($minorGridlines->getShadowProperty(['size', 'sx']) !== null) {
+                    $objWriter->writeAttribute('sx', $minorGridlines->getShadowProperty(['size', 'sx']));
+                }
+                if ($minorGridlines->getShadowProperty(['size', 'sy']) !== null) {
+                    $objWriter->writeAttribute('sy', $minorGridlines->getShadowProperty(['size', 'sy']));
+                }
+                if ($minorGridlines->getShadowProperty(['size', 'kx']) !== null) {
+                    $objWriter->writeAttribute('kx', $minorGridlines->getShadowProperty(['size', 'kx']));
+                }
+                if ($minorGridlines->getShadowProperty('rotWithShape') !== null) {
+                    $objWriter->writeAttribute('rotWithShape', $minorGridlines->getShadowProperty('rotWithShape'));
+                }
+                $objWriter->startElement("a:{$minorGridlines->getShadowProperty(['color', 'type'])}");
+                $objWriter->writeAttribute('val', $minorGridlines->getShadowProperty(['color', 'value']));
+                $objWriter->startElement('a:alpha');
+                $objWriter->writeAttribute('val', $minorGridlines->getShadowProperty(['color', 'alpha']));
+                $objWriter->endElement(); //end alpha
+                $objWriter->endElement(); //end color:type
+                $objWriter->endElement(); //end shadow
+            }
+
+            if ($minorGridlines->getSoftEdgesSize() !== null) {
+                $objWriter->startElement('a:softEdge');
+                $objWriter->writeAttribute('rad', $minorGridlines->getSoftEdgesSize());
+                $objWriter->endElement(); //end softEdge
+            }
+
+            $objWriter->endElement(); //end effectLst
+            $objWriter->endElement(); //end spPr
+            $objWriter->endElement(); //end minorGridLines
+        }
+
+        if ($yAxisLabel !== null) {
+            $objWriter->startElement('c:title');
+            $objWriter->startElement('c:tx');
+            $objWriter->startElement('c:rich');
+
+            $objWriter->startElement('a:bodyPr');
+            $objWriter->endElement();
+
+            $objWriter->startElement('a:lstStyle');
+            $objWriter->endElement();
+
+            $objWriter->startElement('a:p');
+            $objWriter->startElement('a:r');
+
+            $caption = $yAxisLabel->getCaption();
+            if (is_array($caption)) {
+                $caption = $caption[0];
+            }
+
+            $objWriter->startElement('a:t');
+            $objWriter->writeRawData(StringHelper::controlCharacterPHP2OOXML($caption));
+            $objWriter->endElement();
+
+            $objWriter->endElement();
+            $objWriter->endElement();
+            $objWriter->endElement();
+            $objWriter->endElement();
+
+            if ($groupType !== DataSeries::TYPE_BUBBLECHART) {
+                $layout = $yAxisLabel->getLayout();
+                $this->writeLayout($objWriter, $layout);
+            }
+
+            $objWriter->startElement('c:overlay');
+            $objWriter->writeAttribute('val', 0);
+            $objWriter->endElement();
+
+            $objWriter->endElement();
+        }
+
+        $objWriter->startElement('c:numFmt');
+        $objWriter->writeAttribute('formatCode', $xAxis->getAxisNumberFormat());
+        $objWriter->writeAttribute('sourceLinked', $xAxis->getAxisNumberSourceLinked());
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:majorTickMark');
+        $objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('major_tick_mark'));
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:minorTickMark');
+        $objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('minor_tick_mark'));
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:tickLblPos');
+        $objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('axis_labels'));
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:spPr');
+
+        if ($xAxis->getFillProperty('value') !== null) {
+            $objWriter->startElement('a:solidFill');
+            $objWriter->startElement('a:' . $xAxis->getFillProperty('type'));
+            $objWriter->writeAttribute('val', $xAxis->getFillProperty('value'));
+            $objWriter->startElement('a:alpha');
+            $objWriter->writeAttribute('val', $xAxis->getFillProperty('alpha'));
+            $objWriter->endElement();
+            $objWriter->endElement();
+            $objWriter->endElement();
+        }
+
+        $objWriter->startElement('a:ln');
+
+        $objWriter->writeAttribute('w', $xAxis->getLineStyleProperty('width'));
+        $objWriter->writeAttribute('cap', $xAxis->getLineStyleProperty('cap'));
+        $objWriter->writeAttribute('cmpd', $xAxis->getLineStyleProperty('compound'));
+
+        if ($xAxis->getLineProperty('value') !== null) {
+            $objWriter->startElement('a:solidFill');
+            $objWriter->startElement('a:' . $xAxis->getLineProperty('type'));
+            $objWriter->writeAttribute('val', $xAxis->getLineProperty('value'));
+            $objWriter->startElement('a:alpha');
+            $objWriter->writeAttribute('val', $xAxis->getLineProperty('alpha'));
+            $objWriter->endElement();
+            $objWriter->endElement();
+            $objWriter->endElement();
+        }
+
+        $objWriter->startElement('a:prstDash');
+        $objWriter->writeAttribute('val', $xAxis->getLineStyleProperty('dash'));
+        $objWriter->endElement();
+
+        if ($xAxis->getLineStyleProperty('join') == 'miter') {
+            $objWriter->startElement('a:miter');
+            $objWriter->writeAttribute('lim', '800000');
+            $objWriter->endElement();
+        } else {
+            $objWriter->startElement('a:bevel');
+            $objWriter->endElement();
+        }
+
+        if ($xAxis->getLineStyleProperty(['arrow', 'head', 'type']) !== null) {
+            $objWriter->startElement('a:headEnd');
+            $objWriter->writeAttribute('type', $xAxis->getLineStyleProperty(['arrow', 'head', 'type']));
+            $objWriter->writeAttribute('w', $xAxis->getLineStyleArrowWidth('head'));
+            $objWriter->writeAttribute('len', $xAxis->getLineStyleArrowLength('head'));
+            $objWriter->endElement();
+        }
+
+        if ($xAxis->getLineStyleProperty(['arrow', 'end', 'type']) !== null) {
+            $objWriter->startElement('a:tailEnd');
+            $objWriter->writeAttribute('type', $xAxis->getLineStyleProperty(['arrow', 'end', 'type']));
+            $objWriter->writeAttribute('w', $xAxis->getLineStyleArrowWidth('end'));
+            $objWriter->writeAttribute('len', $xAxis->getLineStyleArrowLength('end'));
+            $objWriter->endElement();
+        }
+
+        $objWriter->endElement();
+
+        $objWriter->startElement('a:effectLst');
+
+        if ($xAxis->getGlowProperty('size') !== null) {
+            $objWriter->startElement('a:glow');
+            $objWriter->writeAttribute('rad', $xAxis->getGlowProperty('size'));
+            $objWriter->startElement("a:{$xAxis->getGlowProperty(['color', 'type'])}");
+            $objWriter->writeAttribute('val', $xAxis->getGlowProperty(['color', 'value']));
+            $objWriter->startElement('a:alpha');
+            $objWriter->writeAttribute('val', $xAxis->getGlowProperty(['color', 'alpha']));
+            $objWriter->endElement();
+            $objWriter->endElement();
+            $objWriter->endElement();
+        }
+
+        if ($xAxis->getShadowProperty('presets') !== null) {
+            $objWriter->startElement("a:{$xAxis->getShadowProperty('effect')}");
+
+            if ($xAxis->getShadowProperty('blur') !== null) {
+                $objWriter->writeAttribute('blurRad', $xAxis->getShadowProperty('blur'));
+            }
+            if ($xAxis->getShadowProperty('distance') !== null) {
+                $objWriter->writeAttribute('dist', $xAxis->getShadowProperty('distance'));
+            }
+            if ($xAxis->getShadowProperty('direction') !== null) {
+                $objWriter->writeAttribute('dir', $xAxis->getShadowProperty('direction'));
+            }
+            if ($xAxis->getShadowProperty('algn') !== null) {
+                $objWriter->writeAttribute('algn', $xAxis->getShadowProperty('algn'));
+            }
+            if ($xAxis->getShadowProperty(['size', 'sx']) !== null) {
+                $objWriter->writeAttribute('sx', $xAxis->getShadowProperty(['size', 'sx']));
+            }
+            if ($xAxis->getShadowProperty(['size', 'sy']) !== null) {
+                $objWriter->writeAttribute('sy', $xAxis->getShadowProperty(['size', 'sy']));
+            }
+            if ($xAxis->getShadowProperty(['size', 'kx']) !== null) {
+                $objWriter->writeAttribute('kx', $xAxis->getShadowProperty(['size', 'kx']));
+            }
+            if ($xAxis->getShadowProperty('rotWithShape') !== null) {
+                $objWriter->writeAttribute('rotWithShape', $xAxis->getShadowProperty('rotWithShape'));
+            }
+
+            $objWriter->startElement("a:{$xAxis->getShadowProperty(['color', 'type'])}");
+            $objWriter->writeAttribute('val', $xAxis->getShadowProperty(['color', 'value']));
+            $objWriter->startElement('a:alpha');
+            $objWriter->writeAttribute('val', $xAxis->getShadowProperty(['color', 'alpha']));
+            $objWriter->endElement();
+            $objWriter->endElement();
+
+            $objWriter->endElement();
+        }
+
+        if ($xAxis->getSoftEdgesSize() !== null) {
+            $objWriter->startElement('a:softEdge');
+            $objWriter->writeAttribute('rad', $xAxis->getSoftEdgesSize());
+            $objWriter->endElement();
+        }
+
+        $objWriter->endElement(); //effectList
+        $objWriter->endElement(); //end spPr
+
+        if ($id1 > 0) {
+            $objWriter->startElement('c:crossAx');
+            $objWriter->writeAttribute('val', $id2);
+            $objWriter->endElement();
+
+            if ($xAxis->getAxisOptionsProperty('horizontal_crosses_value') !== null) {
+                $objWriter->startElement('c:crossesAt');
+                $objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('horizontal_crosses_value'));
+                $objWriter->endElement();
+            } else {
+                $objWriter->startElement('c:crosses');
+                $objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('horizontal_crosses'));
+                $objWriter->endElement();
+            }
+
+            $objWriter->startElement('c:crossBetween');
+            $objWriter->writeAttribute('val', 'midCat');
+            $objWriter->endElement();
+
+            if ($xAxis->getAxisOptionsProperty('major_unit') !== null) {
+                $objWriter->startElement('c:majorUnit');
+                $objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('major_unit'));
+                $objWriter->endElement();
+            }
+
+            if ($xAxis->getAxisOptionsProperty('minor_unit') !== null) {
+                $objWriter->startElement('c:minorUnit');
+                $objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('minor_unit'));
+                $objWriter->endElement();
+            }
+        }
+
+        if ($isMultiLevelSeries) {
+            if ($groupType !== DataSeries::TYPE_BUBBLECHART) {
+                $objWriter->startElement('c:noMultiLvlLbl');
+                $objWriter->writeAttribute('val', 0);
+                $objWriter->endElement();
+            }
+        }
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Get the data series type(s) for a chart plot series.
+     *
+     * @param PlotArea $plotArea
+     *
+     * @return array|string
+     */
+    private static function getChartType($plotArea)
+    {
+        $groupCount = $plotArea->getPlotGroupCount();
+
+        if ($groupCount == 1) {
+            $chartType = [$plotArea->getPlotGroupByIndex(0)->getPlotType()];
+        } else {
+            $chartTypes = [];
+            for ($i = 0; $i < $groupCount; ++$i) {
+                $chartTypes[] = $plotArea->getPlotGroupByIndex($i)->getPlotType();
+            }
+            $chartType = array_unique($chartTypes);
+            if (count($chartTypes) == 0) {
+                throw new WriterException('Chart is not yet implemented');
+            }
+        }
+
+        return $chartType;
+    }
+
+    /**
+     * Method writing plot series values.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param int       $val       value for idx (default: 3)
+     * @param string    $fillColor hex color (default: FF9900)
+     *
+     * @return XMLWriter XML Writer
+     */
+    private function writePlotSeriesValuesElement($objWriter, $val = 3, $fillColor = 'FF9900')
+    {
+        $objWriter->startElement('c:dPt');
+        $objWriter->startElement('c:idx');
+        $objWriter->writeAttribute('val', $val);
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:bubble3D');
+        $objWriter->writeAttribute('val', 0);
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:spPr');
+        $objWriter->startElement('a:solidFill');
+        $objWriter->startElement('a:srgbClr');
+        $objWriter->writeAttribute('val', $fillColor);
+        $objWriter->endElement();
+        $objWriter->endElement();
+        $objWriter->endElement();
+        $objWriter->endElement();
+
+        return $objWriter;
+    }
+
+    /**
+     * Write Plot Group (series of related plots).
+     *
+     * @param DataSeries $plotGroup
+     * @param string $groupType Type of plot for dataseries
+     * @param XMLWriter $objWriter XML Writer
+     * @param bool &$catIsMultiLevelSeries Is category a multi-series category
+     * @param bool &$valIsMultiLevelSeries Is value set a multi-series set
+     * @param string &$plotGroupingType Type of grouping for multi-series values
+     */
+    private function writePlotGroup($plotGroup, $groupType, $objWriter, &$catIsMultiLevelSeries, &$valIsMultiLevelSeries, &$plotGroupingType): void
+    {
+        if ($plotGroup === null) {
+            return;
+        }
+
+        if (($groupType == DataSeries::TYPE_BARCHART) || ($groupType == DataSeries::TYPE_BARCHART_3D)) {
+            $objWriter->startElement('c:barDir');
+            $objWriter->writeAttribute('val', $plotGroup->getPlotDirection());
+            $objWriter->endElement();
+        }
+
+        if ($plotGroup->getPlotGrouping() !== null) {
+            $plotGroupingType = $plotGroup->getPlotGrouping();
+            $objWriter->startElement('c:grouping');
+            $objWriter->writeAttribute('val', $plotGroupingType);
+            $objWriter->endElement();
+        }
+
+        //    Get these details before the loop, because we can use the count to check for varyColors
+        $plotSeriesOrder = $plotGroup->getPlotOrder();
+        $plotSeriesCount = count($plotSeriesOrder);
+
+        if (($groupType !== DataSeries::TYPE_RADARCHART) && ($groupType !== DataSeries::TYPE_STOCKCHART)) {
+            if ($groupType !== DataSeries::TYPE_LINECHART) {
+                if (($groupType == DataSeries::TYPE_PIECHART) || ($groupType == DataSeries::TYPE_PIECHART_3D) || ($groupType == DataSeries::TYPE_DONUTCHART) || ($plotSeriesCount > 1)) {
+                    $objWriter->startElement('c:varyColors');
+                    $objWriter->writeAttribute('val', 1);
+                    $objWriter->endElement();
+                } else {
+                    $objWriter->startElement('c:varyColors');
+                    $objWriter->writeAttribute('val', 0);
+                    $objWriter->endElement();
+                }
+            }
+        }
+
+        foreach ($plotSeriesOrder as $plotSeriesIdx => $plotSeriesRef) {
+            $objWriter->startElement('c:ser');
+
+            $plotLabel = $plotGroup->getPlotLabelByIndex($plotSeriesIdx);
+            if ($plotLabel) {
+                $fillColor = $plotLabel->getFillColor();
+                if ($fillColor !== null && !is_array($fillColor)) {
+                    $objWriter->startElement('c:spPr');
+                    $objWriter->startElement('a:solidFill');
+                    $objWriter->startElement('a:srgbClr');
+                    $objWriter->writeAttribute('val', $fillColor);
+                    $objWriter->endElement();
+                    $objWriter->endElement();
+                    $objWriter->endElement();
+                }
+            }
+
+            $objWriter->startElement('c:idx');
+            $objWriter->writeAttribute('val', $this->seriesIndex + $plotSeriesIdx);
+            $objWriter->endElement();
+
+            $objWriter->startElement('c:order');
+            $objWriter->writeAttribute('val', $this->seriesIndex + $plotSeriesRef);
+            $objWriter->endElement();
+
+            //    Values
+            $plotSeriesValues = $plotGroup->getPlotValuesByIndex($plotSeriesRef);
+
+            if (($groupType == DataSeries::TYPE_PIECHART) || ($groupType == DataSeries::TYPE_PIECHART_3D) || ($groupType == DataSeries::TYPE_DONUTCHART)) {
+                $fillColorValues = $plotSeriesValues->getFillColor();
+                if ($fillColorValues !== null && is_array($fillColorValues)) {
+                    foreach ($plotSeriesValues->getDataValues() as $dataKey => $dataValue) {
+                        $this->writePlotSeriesValuesElement($objWriter, $dataKey, ($fillColorValues[$dataKey] ?? 'FF9900'));
+                    }
+                } else {
+                    $this->writePlotSeriesValuesElement($objWriter);
+                }
+            }
+
+            //    Labels
+            $plotSeriesLabel = $plotGroup->getPlotLabelByIndex($plotSeriesRef);
+            if ($plotSeriesLabel && ($plotSeriesLabel->getPointCount() > 0)) {
+                $objWriter->startElement('c:tx');
+                $objWriter->startElement('c:strRef');
+                $this->writePlotSeriesLabel($plotSeriesLabel, $objWriter);
+                $objWriter->endElement();
+                $objWriter->endElement();
+            }
+
+            //    Formatting for the points
+            if (($groupType == DataSeries::TYPE_LINECHART) || ($groupType == DataSeries::TYPE_STOCKCHART)) {
+                $plotLineWidth = 12700;
+                if ($plotSeriesValues) {
+                    $plotLineWidth = $plotSeriesValues->getLineWidth();
+                }
+
+                $objWriter->startElement('c:spPr');
+                $objWriter->startElement('a:ln');
+                $objWriter->writeAttribute('w', $plotLineWidth);
+                if ($groupType == DataSeries::TYPE_STOCKCHART) {
+                    $objWriter->startElement('a:noFill');
+                    $objWriter->endElement();
+                }
+                $objWriter->endElement();
+                $objWriter->endElement();
+            }
+
+            if ($plotSeriesValues) {
+                $plotSeriesMarker = $plotSeriesValues->getPointMarker();
+                if ($plotSeriesMarker) {
+                    $objWriter->startElement('c:marker');
+                    $objWriter->startElement('c:symbol');
+                    $objWriter->writeAttribute('val', $plotSeriesMarker);
+                    $objWriter->endElement();
+
+                    if ($plotSeriesMarker !== 'none') {
+                        $objWriter->startElement('c:size');
+                        $objWriter->writeAttribute('val', 3);
+                        $objWriter->endElement();
+                    }
+
+                    $objWriter->endElement();
+                }
+            }
+
+            if (($groupType === DataSeries::TYPE_BARCHART) || ($groupType === DataSeries::TYPE_BARCHART_3D) || ($groupType === DataSeries::TYPE_BUBBLECHART)) {
+                $objWriter->startElement('c:invertIfNegative');
+                $objWriter->writeAttribute('val', 0);
+                $objWriter->endElement();
+            }
+
+            //    Category Labels
+            $plotSeriesCategory = $plotGroup->getPlotCategoryByIndex($plotSeriesRef);
+            if ($plotSeriesCategory && ($plotSeriesCategory->getPointCount() > 0)) {
+                $catIsMultiLevelSeries = $catIsMultiLevelSeries || $plotSeriesCategory->isMultiLevelSeries();
+
+                if (($groupType == DataSeries::TYPE_PIECHART) || ($groupType == DataSeries::TYPE_PIECHART_3D) || ($groupType == DataSeries::TYPE_DONUTCHART)) {
+                    if ($plotGroup->getPlotStyle() !== null) {
+                        $plotStyle = $plotGroup->getPlotStyle();
+                        if ($plotStyle) {
+                            $objWriter->startElement('c:explosion');
+                            $objWriter->writeAttribute('val', 25);
+                            $objWriter->endElement();
+                        }
+                    }
+                }
+
+                if (($groupType === DataSeries::TYPE_BUBBLECHART) || ($groupType === DataSeries::TYPE_SCATTERCHART)) {
+                    $objWriter->startElement('c:xVal');
+                } else {
+                    $objWriter->startElement('c:cat');
+                }
+
+                $this->writePlotSeriesValues($plotSeriesCategory, $objWriter, $groupType, 'str');
+                $objWriter->endElement();
+            }
+
+            //    Values
+            if ($plotSeriesValues) {
+                $valIsMultiLevelSeries = $valIsMultiLevelSeries || $plotSeriesValues->isMultiLevelSeries();
+
+                if (($groupType === DataSeries::TYPE_BUBBLECHART) || ($groupType === DataSeries::TYPE_SCATTERCHART)) {
+                    $objWriter->startElement('c:yVal');
+                } else {
+                    $objWriter->startElement('c:val');
+                }
+
+                $this->writePlotSeriesValues($plotSeriesValues, $objWriter, $groupType, 'num');
+                $objWriter->endElement();
+            }
+
+            if ($groupType === DataSeries::TYPE_BUBBLECHART) {
+                $this->writeBubbles($plotSeriesValues, $objWriter);
+            }
+
+            $objWriter->endElement();
+        }
+
+        $this->seriesIndex += $plotSeriesIdx + 1;
+    }
+
+    /**
+     * Write Plot Series Label.
+     *
+     * @param DataSeriesValues $plotSeriesLabel
+     * @param XMLWriter $objWriter XML Writer
+     */
+    private function writePlotSeriesLabel($plotSeriesLabel, $objWriter): void
+    {
+        if ($plotSeriesLabel === null) {
+            return;
+        }
+
+        $objWriter->startElement('c:f');
+        $objWriter->writeRawData($plotSeriesLabel->getDataSource());
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:strCache');
+        $objWriter->startElement('c:ptCount');
+        $objWriter->writeAttribute('val', $plotSeriesLabel->getPointCount());
+        $objWriter->endElement();
+
+        foreach ($plotSeriesLabel->getDataValues() as $plotLabelKey => $plotLabelValue) {
+            $objWriter->startElement('c:pt');
+            $objWriter->writeAttribute('idx', $plotLabelKey);
+
+            $objWriter->startElement('c:v');
+            $objWriter->writeRawData($plotLabelValue);
+            $objWriter->endElement();
+            $objWriter->endElement();
+        }
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write Plot Series Values.
+     *
+     * @param DataSeriesValues $plotSeriesValues
+     * @param XMLWriter $objWriter XML Writer
+     * @param string $groupType Type of plot for dataseries
+     * @param string $dataType Datatype of series values
+     */
+    private function writePlotSeriesValues($plotSeriesValues, XMLWriter $objWriter, $groupType, $dataType = 'str'): void
+    {
+        if ($plotSeriesValues === null) {
+            return;
+        }
+
+        if ($plotSeriesValues->isMultiLevelSeries()) {
+            $levelCount = $plotSeriesValues->multiLevelCount();
+
+            $objWriter->startElement('c:multiLvlStrRef');
+
+            $objWriter->startElement('c:f');
+            $objWriter->writeRawData($plotSeriesValues->getDataSource());
+            $objWriter->endElement();
+
+            $objWriter->startElement('c:multiLvlStrCache');
+
+            $objWriter->startElement('c:ptCount');
+            $objWriter->writeAttribute('val', $plotSeriesValues->getPointCount());
+            $objWriter->endElement();
+
+            for ($level = 0; $level < $levelCount; ++$level) {
+                $objWriter->startElement('c:lvl');
+
+                foreach ($plotSeriesValues->getDataValues() as $plotSeriesKey => $plotSeriesValue) {
+                    if (isset($plotSeriesValue[$level])) {
+                        $objWriter->startElement('c:pt');
+                        $objWriter->writeAttribute('idx', $plotSeriesKey);
+
+                        $objWriter->startElement('c:v');
+                        $objWriter->writeRawData($plotSeriesValue[$level]);
+                        $objWriter->endElement();
+                        $objWriter->endElement();
+                    }
+                }
+
+                $objWriter->endElement();
+            }
+
+            $objWriter->endElement();
+
+            $objWriter->endElement();
+        } else {
+            $objWriter->startElement('c:' . $dataType . 'Ref');
+
+            $objWriter->startElement('c:f');
+            $objWriter->writeRawData($plotSeriesValues->getDataSource());
+            $objWriter->endElement();
+
+            $objWriter->startElement('c:' . $dataType . 'Cache');
+
+            if (($groupType != DataSeries::TYPE_PIECHART) && ($groupType != DataSeries::TYPE_PIECHART_3D) && ($groupType != DataSeries::TYPE_DONUTCHART)) {
+                if (($plotSeriesValues->getFormatCode() !== null) && ($plotSeriesValues->getFormatCode() !== '')) {
+                    $objWriter->startElement('c:formatCode');
+                    $objWriter->writeRawData($plotSeriesValues->getFormatCode());
+                    $objWriter->endElement();
+                }
+            }
+
+            $objWriter->startElement('c:ptCount');
+            $objWriter->writeAttribute('val', $plotSeriesValues->getPointCount());
+            $objWriter->endElement();
+
+            $dataValues = $plotSeriesValues->getDataValues();
+            if (!empty($dataValues)) {
+                if (is_array($dataValues)) {
+                    foreach ($dataValues as $plotSeriesKey => $plotSeriesValue) {
+                        $objWriter->startElement('c:pt');
+                        $objWriter->writeAttribute('idx', $plotSeriesKey);
+
+                        $objWriter->startElement('c:v');
+                        $objWriter->writeRawData($plotSeriesValue);
+                        $objWriter->endElement();
+                        $objWriter->endElement();
+                    }
+                }
+            }
+
+            $objWriter->endElement();
+
+            $objWriter->endElement();
+        }
+    }
+
+    /**
+     * Write Bubble Chart Details.
+     *
+     * @param DataSeriesValues $plotSeriesValues
+     * @param XMLWriter $objWriter XML Writer
+     */
+    private function writeBubbles($plotSeriesValues, $objWriter): void
+    {
+        if ($plotSeriesValues === null) {
+            return;
+        }
+
+        $objWriter->startElement('c:bubbleSize');
+        $objWriter->startElement('c:numLit');
+
+        $objWriter->startElement('c:formatCode');
+        $objWriter->writeRawData('General');
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:ptCount');
+        $objWriter->writeAttribute('val', $plotSeriesValues->getPointCount());
+        $objWriter->endElement();
+
+        $dataValues = $plotSeriesValues->getDataValues();
+        if (!empty($dataValues)) {
+            if (is_array($dataValues)) {
+                foreach ($dataValues as $plotSeriesKey => $plotSeriesValue) {
+                    $objWriter->startElement('c:pt');
+                    $objWriter->writeAttribute('idx', $plotSeriesKey);
+                    $objWriter->startElement('c:v');
+                    $objWriter->writeRawData(1);
+                    $objWriter->endElement();
+                    $objWriter->endElement();
+                }
+            }
+        }
+
+        $objWriter->endElement();
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:bubble3D');
+        $objWriter->writeAttribute('val', 0);
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write Layout.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param Layout $layout
+     */
+    private function writeLayout(XMLWriter $objWriter, ?Layout $layout = null): void
+    {
+        $objWriter->startElement('c:layout');
+
+        if ($layout !== null) {
+            $objWriter->startElement('c:manualLayout');
+
+            $layoutTarget = $layout->getLayoutTarget();
+            if ($layoutTarget !== null) {
+                $objWriter->startElement('c:layoutTarget');
+                $objWriter->writeAttribute('val', $layoutTarget);
+                $objWriter->endElement();
+            }
+
+            $xMode = $layout->getXMode();
+            if ($xMode !== null) {
+                $objWriter->startElement('c:xMode');
+                $objWriter->writeAttribute('val', $xMode);
+                $objWriter->endElement();
+            }
+
+            $yMode = $layout->getYMode();
+            if ($yMode !== null) {
+                $objWriter->startElement('c:yMode');
+                $objWriter->writeAttribute('val', $yMode);
+                $objWriter->endElement();
+            }
+
+            $x = $layout->getXPosition();
+            if ($x !== null) {
+                $objWriter->startElement('c:x');
+                $objWriter->writeAttribute('val', $x);
+                $objWriter->endElement();
+            }
+
+            $y = $layout->getYPosition();
+            if ($y !== null) {
+                $objWriter->startElement('c:y');
+                $objWriter->writeAttribute('val', $y);
+                $objWriter->endElement();
+            }
+
+            $w = $layout->getWidth();
+            if ($w !== null) {
+                $objWriter->startElement('c:w');
+                $objWriter->writeAttribute('val', $w);
+                $objWriter->endElement();
+            }
+
+            $h = $layout->getHeight();
+            if ($h !== null) {
+                $objWriter->startElement('c:h');
+                $objWriter->writeAttribute('val', $h);
+                $objWriter->endElement();
+            }
+
+            $objWriter->endElement();
+        }
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write Alternate Content block.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     */
+    private function writeAlternateContent($objWriter): void
+    {
+        $objWriter->startElement('mc:AlternateContent');
+        $objWriter->writeAttribute('xmlns:mc', 'http://schemas.openxmlformats.org/markup-compatibility/2006');
+
+        $objWriter->startElement('mc:Choice');
+        $objWriter->writeAttribute('xmlns:c14', 'http://schemas.microsoft.com/office/drawing/2007/8/2/chart');
+        $objWriter->writeAttribute('Requires', 'c14');
+
+        $objWriter->startElement('c14:style');
+        $objWriter->writeAttribute('val', '102');
+        $objWriter->endElement();
+        $objWriter->endElement();
+
+        $objWriter->startElement('mc:Fallback');
+        $objWriter->startElement('c:style');
+        $objWriter->writeAttribute('val', '2');
+        $objWriter->endElement();
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write Printer Settings.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     */
+    private function writePrintSettings($objWriter): void
+    {
+        $objWriter->startElement('c:printSettings');
+
+        $objWriter->startElement('c:headerFooter');
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:pageMargins');
+        $objWriter->writeAttribute('footer', 0.3);
+        $objWriter->writeAttribute('header', 0.3);
+        $objWriter->writeAttribute('r', 0.7);
+        $objWriter->writeAttribute('l', 0.7);
+        $objWriter->writeAttribute('t', 0.75);
+        $objWriter->writeAttribute('b', 0.75);
+        $objWriter->endElement();
+
+        $objWriter->startElement('c:pageSetup');
+        $objWriter->writeAttribute('orientation', 'portrait');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Comments.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Comments.php
new file mode 100755
index 0000000..73c4308
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Comments.php
@@ -0,0 +1,232 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Comment;
+use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
+
+class Comments extends WriterPart
+{
+    /**
+     * Write comments to XML format.
+     *
+     * @return string XML Output
+     */
+    public function writeComments(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet)
+    {
+        // Create XML writer
+        $objWriter = null;
+        if ($this->getParentWriter()->getUseDiskCaching()) {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+        } else {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
+        }
+
+        // XML header
+        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+        // Comments cache
+        $comments = $pWorksheet->getComments();
+
+        // Authors cache
+        $authors = [];
+        $authorId = 0;
+        foreach ($comments as $comment) {
+            if (!isset($authors[$comment->getAuthor()])) {
+                $authors[$comment->getAuthor()] = $authorId++;
+            }
+        }
+
+        // comments
+        $objWriter->startElement('comments');
+        $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
+
+        // Loop through authors
+        $objWriter->startElement('authors');
+        foreach ($authors as $author => $index) {
+            $objWriter->writeElement('author', $author);
+        }
+        $objWriter->endElement();
+
+        // Loop through comments
+        $objWriter->startElement('commentList');
+        foreach ($comments as $key => $value) {
+            $this->writeComment($objWriter, $key, $value, $authors);
+        }
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // Return
+        return $objWriter->getData();
+    }
+
+    /**
+     * Write comment to XML format.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param string $pCellReference Cell reference
+     * @param Comment $pComment Comment
+     * @param array $pAuthors Array of authors
+     */
+    private function writeComment(XMLWriter $objWriter, $pCellReference, Comment $pComment, array $pAuthors): void
+    {
+        // comment
+        $objWriter->startElement('comment');
+        $objWriter->writeAttribute('ref', $pCellReference);
+        $objWriter->writeAttribute('authorId', $pAuthors[$pComment->getAuthor()]);
+
+        // text
+        $objWriter->startElement('text');
+        $this->getParentWriter()->getWriterPart('stringtable')->writeRichText($objWriter, $pComment->getText());
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write VML comments to XML format.
+     *
+     * @return string XML Output
+     */
+    public function writeVMLComments(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet)
+    {
+        // Create XML writer
+        $objWriter = null;
+        if ($this->getParentWriter()->getUseDiskCaching()) {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+        } else {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
+        }
+
+        // XML header
+        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+        // Comments cache
+        $comments = $pWorksheet->getComments();
+
+        // xml
+        $objWriter->startElement('xml');
+        $objWriter->writeAttribute('xmlns:v', 'urn:schemas-microsoft-com:vml');
+        $objWriter->writeAttribute('xmlns:o', 'urn:schemas-microsoft-com:office:office');
+        $objWriter->writeAttribute('xmlns:x', 'urn:schemas-microsoft-com:office:excel');
+
+        // o:shapelayout
+        $objWriter->startElement('o:shapelayout');
+        $objWriter->writeAttribute('v:ext', 'edit');
+
+        // o:idmap
+        $objWriter->startElement('o:idmap');
+        $objWriter->writeAttribute('v:ext', 'edit');
+        $objWriter->writeAttribute('data', '1');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // v:shapetype
+        $objWriter->startElement('v:shapetype');
+        $objWriter->writeAttribute('id', '_x0000_t202');
+        $objWriter->writeAttribute('coordsize', '21600,21600');
+        $objWriter->writeAttribute('o:spt', '202');
+        $objWriter->writeAttribute('path', 'm,l,21600r21600,l21600,xe');
+
+        // v:stroke
+        $objWriter->startElement('v:stroke');
+        $objWriter->writeAttribute('joinstyle', 'miter');
+        $objWriter->endElement();
+
+        // v:path
+        $objWriter->startElement('v:path');
+        $objWriter->writeAttribute('gradientshapeok', 't');
+        $objWriter->writeAttribute('o:connecttype', 'rect');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // Loop through comments
+        foreach ($comments as $key => $value) {
+            $this->writeVMLComment($objWriter, $key, $value);
+        }
+
+        $objWriter->endElement();
+
+        // Return
+        return $objWriter->getData();
+    }
+
+    /**
+     * Write VML comment to XML format.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param string $pCellReference Cell reference, eg: 'A1'
+     * @param Comment $pComment Comment
+     */
+    private function writeVMLComment(XMLWriter $objWriter, $pCellReference, Comment $pComment): void
+    {
+        // Metadata
+        [$column, $row] = Coordinate::coordinateFromString($pCellReference);
+        $column = Coordinate::columnIndexFromString($column);
+        $id = 1024 + $column + $row;
+        $id = substr($id, 0, 4);
+
+        // v:shape
+        $objWriter->startElement('v:shape');
+        $objWriter->writeAttribute('id', '_x0000_s' . $id);
+        $objWriter->writeAttribute('type', '#_x0000_t202');
+        $objWriter->writeAttribute('style', 'position:absolute;margin-left:' . $pComment->getMarginLeft() . ';margin-top:' . $pComment->getMarginTop() . ';width:' . $pComment->getWidth() . ';height:' . $pComment->getHeight() . ';z-index:1;visibility:' . ($pComment->getVisible() ? 'visible' : 'hidden'));
+        $objWriter->writeAttribute('fillcolor', '#' . $pComment->getFillColor()->getRGB());
+        $objWriter->writeAttribute('o:insetmode', 'auto');
+
+        // v:fill
+        $objWriter->startElement('v:fill');
+        $objWriter->writeAttribute('color2', '#' . $pComment->getFillColor()->getRGB());
+        $objWriter->endElement();
+
+        // v:shadow
+        $objWriter->startElement('v:shadow');
+        $objWriter->writeAttribute('on', 't');
+        $objWriter->writeAttribute('color', 'black');
+        $objWriter->writeAttribute('obscured', 't');
+        $objWriter->endElement();
+
+        // v:path
+        $objWriter->startElement('v:path');
+        $objWriter->writeAttribute('o:connecttype', 'none');
+        $objWriter->endElement();
+
+        // v:textbox
+        $objWriter->startElement('v:textbox');
+        $objWriter->writeAttribute('style', 'mso-direction-alt:auto');
+
+        // div
+        $objWriter->startElement('div');
+        $objWriter->writeAttribute('style', 'text-align:left');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // x:ClientData
+        $objWriter->startElement('x:ClientData');
+        $objWriter->writeAttribute('ObjectType', 'Note');
+
+        // x:MoveWithCells
+        $objWriter->writeElement('x:MoveWithCells', '');
+
+        // x:SizeWithCells
+        $objWriter->writeElement('x:SizeWithCells', '');
+
+        // x:AutoFill
+        $objWriter->writeElement('x:AutoFill', 'False');
+
+        // x:Row
+        $objWriter->writeElement('x:Row', ($row - 1));
+
+        // x:Column
+        $objWriter->writeElement('x:Column', ($column - 1));
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php
new file mode 100755
index 0000000..2cff1a8
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php
@@ -0,0 +1,240 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Shared\File;
+use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
+use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
+
+class ContentTypes extends WriterPart
+{
+    /**
+     * Write content types to XML format.
+     *
+     * @param bool $includeCharts Flag indicating if we should include drawing details for charts
+     *
+     * @return string XML Output
+     */
+    public function writeContentTypes(Spreadsheet $spreadsheet, $includeCharts = false)
+    {
+        // Create XML writer
+        $objWriter = null;
+        if ($this->getParentWriter()->getUseDiskCaching()) {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+        } else {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
+        }
+
+        // XML header
+        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+        // Types
+        $objWriter->startElement('Types');
+        $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/content-types');
+
+        // Theme
+        $this->writeOverrideContentType($objWriter, '/xl/theme/theme1.xml', 'application/vnd.openxmlformats-officedocument.theme+xml');
+
+        // Styles
+        $this->writeOverrideContentType($objWriter, '/xl/styles.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml');
+
+        // Rels
+        $this->writeDefaultContentType($objWriter, 'rels', 'application/vnd.openxmlformats-package.relationships+xml');
+
+        // XML
+        $this->writeDefaultContentType($objWriter, 'xml', 'application/xml');
+
+        // VML
+        $this->writeDefaultContentType($objWriter, 'vml', 'application/vnd.openxmlformats-officedocument.vmlDrawing');
+
+        // Workbook
+        if ($spreadsheet->hasMacros()) { //Macros in workbook ?
+            // Yes : not standard content but "macroEnabled"
+            $this->writeOverrideContentType($objWriter, '/xl/workbook.xml', 'application/vnd.ms-excel.sheet.macroEnabled.main+xml');
+            //... and define a new type for the VBA project
+            // Better use Override, because we can use 'bin' also for xl\printerSettings\printerSettings1.bin
+            $this->writeOverrideContentType($objWriter, '/xl/vbaProject.bin', 'application/vnd.ms-office.vbaProject');
+            if ($spreadsheet->hasMacrosCertificate()) {
+                // signed macros ?
+                // Yes : add needed information
+                $this->writeOverrideContentType($objWriter, '/xl/vbaProjectSignature.bin', 'application/vnd.ms-office.vbaProjectSignature');
+            }
+        } else {
+            // no macros in workbook, so standard type
+            $this->writeOverrideContentType($objWriter, '/xl/workbook.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml');
+        }
+
+        // DocProps
+        $this->writeOverrideContentType($objWriter, '/docProps/app.xml', 'application/vnd.openxmlformats-officedocument.extended-properties+xml');
+
+        $this->writeOverrideContentType($objWriter, '/docProps/core.xml', 'application/vnd.openxmlformats-package.core-properties+xml');
+
+        $customPropertyList = $spreadsheet->getProperties()->getCustomProperties();
+        if (!empty($customPropertyList)) {
+            $this->writeOverrideContentType($objWriter, '/docProps/custom.xml', 'application/vnd.openxmlformats-officedocument.custom-properties+xml');
+        }
+
+        // Worksheets
+        $sheetCount = $spreadsheet->getSheetCount();
+        for ($i = 0; $i < $sheetCount; ++$i) {
+            $this->writeOverrideContentType($objWriter, '/xl/worksheets/sheet' . ($i + 1) . '.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml');
+        }
+
+        // Shared strings
+        $this->writeOverrideContentType($objWriter, '/xl/sharedStrings.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml');
+
+        // Add worksheet relationship content types
+        $unparsedLoadedData = $spreadsheet->getUnparsedLoadedData();
+        $chart = 1;
+        for ($i = 0; $i < $sheetCount; ++$i) {
+            $drawings = $spreadsheet->getSheet($i)->getDrawingCollection();
+            $drawingCount = count($drawings);
+            $chartCount = ($includeCharts) ? $spreadsheet->getSheet($i)->getChartCount() : 0;
+            $hasUnparsedDrawing = isset($unparsedLoadedData['sheets'][$spreadsheet->getSheet($i)->getCodeName()]['drawingOriginalIds']);
+
+            //    We need a drawing relationship for the worksheet if we have either drawings or charts
+            if (($drawingCount > 0) || ($chartCount > 0) || $hasUnparsedDrawing) {
+                $this->writeOverrideContentType($objWriter, '/xl/drawings/drawing' . ($i + 1) . '.xml', 'application/vnd.openxmlformats-officedocument.drawing+xml');
+            }
+
+            //    If we have charts, then we need a chart relationship for every individual chart
+            if ($chartCount > 0) {
+                for ($c = 0; $c < $chartCount; ++$c) {
+                    $this->writeOverrideContentType($objWriter, '/xl/charts/chart' . $chart++ . '.xml', 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml');
+                }
+            }
+        }
+
+        // Comments
+        for ($i = 0; $i < $sheetCount; ++$i) {
+            if (count($spreadsheet->getSheet($i)->getComments()) > 0) {
+                $this->writeOverrideContentType($objWriter, '/xl/comments' . ($i + 1) . '.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml');
+            }
+        }
+
+        // Add media content-types
+        $aMediaContentTypes = [];
+        $mediaCount = $this->getParentWriter()->getDrawingHashTable()->count();
+        for ($i = 0; $i < $mediaCount; ++$i) {
+            $extension = '';
+            $mimeType = '';
+
+            if ($this->getParentWriter()->getDrawingHashTable()->getByIndex($i) instanceof \PhpOffice\PhpSpreadsheet\Worksheet\Drawing) {
+                $extension = strtolower($this->getParentWriter()->getDrawingHashTable()->getByIndex($i)->getExtension());
+                $mimeType = $this->getImageMimeType($this->getParentWriter()->getDrawingHashTable()->getByIndex($i)->getPath());
+            } elseif ($this->getParentWriter()->getDrawingHashTable()->getByIndex($i) instanceof MemoryDrawing) {
+                $extension = strtolower($this->getParentWriter()->getDrawingHashTable()->getByIndex($i)->getMimeType());
+                $extension = explode('/', $extension);
+                $extension = $extension[1];
+
+                $mimeType = $this->getParentWriter()->getDrawingHashTable()->getByIndex($i)->getMimeType();
+            }
+
+            if (!isset($aMediaContentTypes[$extension])) {
+                $aMediaContentTypes[$extension] = $mimeType;
+
+                $this->writeDefaultContentType($objWriter, $extension, $mimeType);
+            }
+        }
+        if ($spreadsheet->hasRibbonBinObjects()) {
+            // Some additional objects in the ribbon ?
+            // we need to write "Extension" but not already write for media content
+            $tabRibbonTypes = array_diff($spreadsheet->getRibbonBinObjects('types'), array_keys($aMediaContentTypes));
+            foreach ($tabRibbonTypes as $aRibbonType) {
+                $mimeType = 'image/.' . $aRibbonType; //we wrote $mimeType like customUI Editor
+                $this->writeDefaultContentType($objWriter, $aRibbonType, $mimeType);
+            }
+        }
+        $sheetCount = $spreadsheet->getSheetCount();
+        for ($i = 0; $i < $sheetCount; ++$i) {
+            if (count($spreadsheet->getSheet($i)->getHeaderFooter()->getImages()) > 0) {
+                foreach ($spreadsheet->getSheet($i)->getHeaderFooter()->getImages() as $image) {
+                    if (!isset($aMediaContentTypes[strtolower($image->getExtension())])) {
+                        $aMediaContentTypes[strtolower($image->getExtension())] = $this->getImageMimeType($image->getPath());
+
+                        $this->writeDefaultContentType($objWriter, strtolower($image->getExtension()), $aMediaContentTypes[strtolower($image->getExtension())]);
+                    }
+                }
+            }
+        }
+
+        // unparsed defaults
+        if (isset($unparsedLoadedData['default_content_types'])) {
+            foreach ($unparsedLoadedData['default_content_types'] as $extName => $contentType) {
+                $this->writeDefaultContentType($objWriter, $extName, $contentType);
+            }
+        }
+
+        // unparsed overrides
+        if (isset($unparsedLoadedData['override_content_types'])) {
+            foreach ($unparsedLoadedData['override_content_types'] as $partName => $overrideType) {
+                $this->writeOverrideContentType($objWriter, $partName, $overrideType);
+            }
+        }
+
+        $objWriter->endElement();
+
+        // Return
+        return $objWriter->getData();
+    }
+
+    /**
+     * Get image mime type.
+     *
+     * @param string $pFile Filename
+     *
+     * @return string Mime Type
+     */
+    private function getImageMimeType($pFile)
+    {
+        if (File::fileExists($pFile)) {
+            $image = getimagesize($pFile);
+
+            return image_type_to_mime_type($image[2]);
+        }
+
+        throw new WriterException("File $pFile does not exist");
+    }
+
+    /**
+     * Write Default content type.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param string $pPartname Part name
+     * @param string $pContentType Content type
+     */
+    private function writeDefaultContentType(XMLWriter $objWriter, $pPartname, $pContentType): void
+    {
+        if ($pPartname != '' && $pContentType != '') {
+            // Write content type
+            $objWriter->startElement('Default');
+            $objWriter->writeAttribute('Extension', $pPartname);
+            $objWriter->writeAttribute('ContentType', $pContentType);
+            $objWriter->endElement();
+        } else {
+            throw new WriterException('Invalid parameters passed.');
+        }
+    }
+
+    /**
+     * Write Override content type.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param string $pPartname Part name
+     * @param string $pContentType Content type
+     */
+    private function writeOverrideContentType(XMLWriter $objWriter, $pPartname, $pContentType): void
+    {
+        if ($pPartname != '' && $pContentType != '') {
+            // Write content type
+            $objWriter->startElement('Override');
+            $objWriter->writeAttribute('PartName', $pPartname);
+            $objWriter->writeAttribute('ContentType', $pContentType);
+            $objWriter->endElement();
+        } else {
+            throw new WriterException('Invalid parameters passed.');
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/DocProps.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/DocProps.php
new file mode 100755
index 0000000..bcbc237
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/DocProps.php
@@ -0,0 +1,239 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+
+class DocProps extends WriterPart
+{
+    /**
+     * Write docProps/app.xml to XML format.
+     *
+     * @return string XML Output
+     */
+    public function writeDocPropsApp(Spreadsheet $spreadsheet)
+    {
+        // Create XML writer
+        $objWriter = null;
+        if ($this->getParentWriter()->getUseDiskCaching()) {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+        } else {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
+        }
+
+        // XML header
+        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+        // Properties
+        $objWriter->startElement('Properties');
+        $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/officeDocument/2006/extended-properties');
+        $objWriter->writeAttribute('xmlns:vt', 'http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes');
+
+        // Application
+        $objWriter->writeElement('Application', 'Microsoft Excel');
+
+        // DocSecurity
+        $objWriter->writeElement('DocSecurity', '0');
+
+        // ScaleCrop
+        $objWriter->writeElement('ScaleCrop', 'false');
+
+        // HeadingPairs
+        $objWriter->startElement('HeadingPairs');
+
+        // Vector
+        $objWriter->startElement('vt:vector');
+        $objWriter->writeAttribute('size', '2');
+        $objWriter->writeAttribute('baseType', 'variant');
+
+        // Variant
+        $objWriter->startElement('vt:variant');
+        $objWriter->writeElement('vt:lpstr', 'Worksheets');
+        $objWriter->endElement();
+
+        // Variant
+        $objWriter->startElement('vt:variant');
+        $objWriter->writeElement('vt:i4', $spreadsheet->getSheetCount());
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // TitlesOfParts
+        $objWriter->startElement('TitlesOfParts');
+
+        // Vector
+        $objWriter->startElement('vt:vector');
+        $objWriter->writeAttribute('size', $spreadsheet->getSheetCount());
+        $objWriter->writeAttribute('baseType', 'lpstr');
+
+        $sheetCount = $spreadsheet->getSheetCount();
+        for ($i = 0; $i < $sheetCount; ++$i) {
+            $objWriter->writeElement('vt:lpstr', $spreadsheet->getSheet($i)->getTitle());
+        }
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // Company
+        $objWriter->writeElement('Company', $spreadsheet->getProperties()->getCompany());
+
+        // Company
+        $objWriter->writeElement('Manager', $spreadsheet->getProperties()->getManager());
+
+        // LinksUpToDate
+        $objWriter->writeElement('LinksUpToDate', 'false');
+
+        // SharedDoc
+        $objWriter->writeElement('SharedDoc', 'false');
+
+        // HyperlinksChanged
+        $objWriter->writeElement('HyperlinksChanged', 'false');
+
+        // AppVersion
+        $objWriter->writeElement('AppVersion', '12.0000');
+
+        $objWriter->endElement();
+
+        // Return
+        return $objWriter->getData();
+    }
+
+    /**
+     * Write docProps/core.xml to XML format.
+     *
+     * @return string XML Output
+     */
+    public function writeDocPropsCore(Spreadsheet $spreadsheet)
+    {
+        // Create XML writer
+        $objWriter = null;
+        if ($this->getParentWriter()->getUseDiskCaching()) {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+        } else {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
+        }
+
+        // XML header
+        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+        // cp:coreProperties
+        $objWriter->startElement('cp:coreProperties');
+        $objWriter->writeAttribute('xmlns:cp', 'http://schemas.openxmlformats.org/package/2006/metadata/core-properties');
+        $objWriter->writeAttribute('xmlns:dc', 'http://purl.org/dc/elements/1.1/');
+        $objWriter->writeAttribute('xmlns:dcterms', 'http://purl.org/dc/terms/');
+        $objWriter->writeAttribute('xmlns:dcmitype', 'http://purl.org/dc/dcmitype/');
+        $objWriter->writeAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
+
+        // dc:creator
+        $objWriter->writeElement('dc:creator', $spreadsheet->getProperties()->getCreator());
+
+        // cp:lastModifiedBy
+        $objWriter->writeElement('cp:lastModifiedBy', $spreadsheet->getProperties()->getLastModifiedBy());
+
+        // dcterms:created
+        $objWriter->startElement('dcterms:created');
+        $objWriter->writeAttribute('xsi:type', 'dcterms:W3CDTF');
+        $objWriter->writeRawData(date(DATE_W3C, $spreadsheet->getProperties()->getCreated()));
+        $objWriter->endElement();
+
+        // dcterms:modified
+        $objWriter->startElement('dcterms:modified');
+        $objWriter->writeAttribute('xsi:type', 'dcterms:W3CDTF');
+        $objWriter->writeRawData(date(DATE_W3C, $spreadsheet->getProperties()->getModified()));
+        $objWriter->endElement();
+
+        // dc:title
+        $objWriter->writeElement('dc:title', $spreadsheet->getProperties()->getTitle());
+
+        // dc:description
+        $objWriter->writeElement('dc:description', $spreadsheet->getProperties()->getDescription());
+
+        // dc:subject
+        $objWriter->writeElement('dc:subject', $spreadsheet->getProperties()->getSubject());
+
+        // cp:keywords
+        $objWriter->writeElement('cp:keywords', $spreadsheet->getProperties()->getKeywords());
+
+        // cp:category
+        $objWriter->writeElement('cp:category', $spreadsheet->getProperties()->getCategory());
+
+        $objWriter->endElement();
+
+        // Return
+        return $objWriter->getData();
+    }
+
+    /**
+     * Write docProps/custom.xml to XML format.
+     *
+     * @return string XML Output
+     */
+    public function writeDocPropsCustom(Spreadsheet $spreadsheet)
+    {
+        $customPropertyList = $spreadsheet->getProperties()->getCustomProperties();
+        if (empty($customPropertyList)) {
+            return;
+        }
+
+        // Create XML writer
+        $objWriter = null;
+        if ($this->getParentWriter()->getUseDiskCaching()) {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+        } else {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
+        }
+
+        // XML header
+        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+        // cp:coreProperties
+        $objWriter->startElement('Properties');
+        $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/officeDocument/2006/custom-properties');
+        $objWriter->writeAttribute('xmlns:vt', 'http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes');
+
+        foreach ($customPropertyList as $key => $customProperty) {
+            $propertyValue = $spreadsheet->getProperties()->getCustomPropertyValue($customProperty);
+            $propertyType = $spreadsheet->getProperties()->getCustomPropertyType($customProperty);
+
+            $objWriter->startElement('property');
+            $objWriter->writeAttribute('fmtid', '{D5CDD505-2E9C-101B-9397-08002B2CF9AE}');
+            $objWriter->writeAttribute('pid', $key + 2);
+            $objWriter->writeAttribute('name', $customProperty);
+
+            switch ($propertyType) {
+                case 'i':
+                    $objWriter->writeElement('vt:i4', $propertyValue);
+
+                    break;
+                case 'f':
+                    $objWriter->writeElement('vt:r8', $propertyValue);
+
+                    break;
+                case 'b':
+                    $objWriter->writeElement('vt:bool', ($propertyValue) ? 'true' : 'false');
+
+                    break;
+                case 'd':
+                    $objWriter->startElement('vt:filetime');
+                    $objWriter->writeRawData(date(DATE_W3C, $propertyValue));
+                    $objWriter->endElement();
+
+                    break;
+                default:
+                    $objWriter->writeElement('vt:lpwstr', $propertyValue);
+
+                    break;
+            }
+
+            $objWriter->endElement();
+        }
+
+        $objWriter->endElement();
+
+        return $objWriter->getData();
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php
new file mode 100755
index 0000000..1713b98
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php
@@ -0,0 +1,505 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Worksheet\BaseDrawing;
+use PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooterDrawing;
+use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
+
+class Drawing extends WriterPart
+{
+    /**
+     * Write drawings to XML format.
+     *
+     * @param bool $includeCharts Flag indicating if we should include drawing details for charts
+     *
+     * @return string XML Output
+     */
+    public function writeDrawings(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet, $includeCharts = false)
+    {
+        // Create XML writer
+        $objWriter = null;
+        if ($this->getParentWriter()->getUseDiskCaching()) {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+        } else {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
+        }
+
+        // XML header
+        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+        // xdr:wsDr
+        $objWriter->startElement('xdr:wsDr');
+        $objWriter->writeAttribute('xmlns:xdr', 'http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing');
+        $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main');
+
+        // Loop through images and write drawings
+        $i = 1;
+        $iterator = $pWorksheet->getDrawingCollection()->getIterator();
+        while ($iterator->valid()) {
+            /** @var BaseDrawing $pDrawing */
+            $pDrawing = $iterator->current();
+            $pRelationId = $i;
+            $hlinkClickId = $pDrawing->getHyperlink() === null ? null : ++$i;
+
+            $this->writeDrawing($objWriter, $pDrawing, $pRelationId, $hlinkClickId);
+
+            $iterator->next();
+            ++$i;
+        }
+
+        if ($includeCharts) {
+            $chartCount = $pWorksheet->getChartCount();
+            // Loop through charts and write the chart position
+            if ($chartCount > 0) {
+                for ($c = 0; $c < $chartCount; ++$c) {
+                    $this->writeChart($objWriter, $pWorksheet->getChartByIndex($c), $c + $i);
+                }
+            }
+        }
+
+        // unparsed AlternateContent
+        $unparsedLoadedData = $pWorksheet->getParent()->getUnparsedLoadedData();
+        if (isset($unparsedLoadedData['sheets'][$pWorksheet->getCodeName()]['drawingAlternateContents'])) {
+            foreach ($unparsedLoadedData['sheets'][$pWorksheet->getCodeName()]['drawingAlternateContents'] as $drawingAlternateContent) {
+                $objWriter->writeRaw($drawingAlternateContent);
+            }
+        }
+
+        $objWriter->endElement();
+
+        // Return
+        return $objWriter->getData();
+    }
+
+    /**
+     * Write drawings to XML format.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param int $pRelationId
+     */
+    public function writeChart(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Chart\Chart $pChart, $pRelationId = -1): void
+    {
+        $tl = $pChart->getTopLeftPosition();
+        $tl['colRow'] = Coordinate::coordinateFromString($tl['cell']);
+        $br = $pChart->getBottomRightPosition();
+        $br['colRow'] = Coordinate::coordinateFromString($br['cell']);
+
+        $objWriter->startElement('xdr:twoCellAnchor');
+
+        $objWriter->startElement('xdr:from');
+        $objWriter->writeElement('xdr:col', Coordinate::columnIndexFromString($tl['colRow'][0]) - 1);
+        $objWriter->writeElement('xdr:colOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($tl['xOffset']));
+        $objWriter->writeElement('xdr:row', $tl['colRow'][1] - 1);
+        $objWriter->writeElement('xdr:rowOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($tl['yOffset']));
+        $objWriter->endElement();
+        $objWriter->startElement('xdr:to');
+        $objWriter->writeElement('xdr:col', Coordinate::columnIndexFromString($br['colRow'][0]) - 1);
+        $objWriter->writeElement('xdr:colOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($br['xOffset']));
+        $objWriter->writeElement('xdr:row', $br['colRow'][1] - 1);
+        $objWriter->writeElement('xdr:rowOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($br['yOffset']));
+        $objWriter->endElement();
+
+        $objWriter->startElement('xdr:graphicFrame');
+        $objWriter->writeAttribute('macro', '');
+        $objWriter->startElement('xdr:nvGraphicFramePr');
+        $objWriter->startElement('xdr:cNvPr');
+        $objWriter->writeAttribute('name', 'Chart ' . $pRelationId);
+        $objWriter->writeAttribute('id', 1025 * $pRelationId);
+        $objWriter->endElement();
+        $objWriter->startElement('xdr:cNvGraphicFramePr');
+        $objWriter->startElement('a:graphicFrameLocks');
+        $objWriter->endElement();
+        $objWriter->endElement();
+        $objWriter->endElement();
+
+        $objWriter->startElement('xdr:xfrm');
+        $objWriter->startElement('a:off');
+        $objWriter->writeAttribute('x', '0');
+        $objWriter->writeAttribute('y', '0');
+        $objWriter->endElement();
+        $objWriter->startElement('a:ext');
+        $objWriter->writeAttribute('cx', '0');
+        $objWriter->writeAttribute('cy', '0');
+        $objWriter->endElement();
+        $objWriter->endElement();
+
+        $objWriter->startElement('a:graphic');
+        $objWriter->startElement('a:graphicData');
+        $objWriter->writeAttribute('uri', 'http://schemas.openxmlformats.org/drawingml/2006/chart');
+        $objWriter->startElement('c:chart');
+        $objWriter->writeAttribute('xmlns:c', 'http://schemas.openxmlformats.org/drawingml/2006/chart');
+        $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
+        $objWriter->writeAttribute('r:id', 'rId' . $pRelationId);
+        $objWriter->endElement();
+        $objWriter->endElement();
+        $objWriter->endElement();
+        $objWriter->endElement();
+
+        $objWriter->startElement('xdr:clientData');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write drawings to XML format.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param int $pRelationId
+     * @param null|int $hlinkClickId
+     */
+    public function writeDrawing(XMLWriter $objWriter, BaseDrawing $pDrawing, $pRelationId = -1, $hlinkClickId = null): void
+    {
+        if ($pRelationId >= 0) {
+            // xdr:oneCellAnchor
+            $objWriter->startElement('xdr:oneCellAnchor');
+            // Image location
+            $aCoordinates = Coordinate::coordinateFromString($pDrawing->getCoordinates());
+            $aCoordinates[0] = Coordinate::columnIndexFromString($aCoordinates[0]);
+
+            // xdr:from
+            $objWriter->startElement('xdr:from');
+            $objWriter->writeElement('xdr:col', $aCoordinates[0] - 1);
+            $objWriter->writeElement('xdr:colOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($pDrawing->getOffsetX()));
+            $objWriter->writeElement('xdr:row', $aCoordinates[1] - 1);
+            $objWriter->writeElement('xdr:rowOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($pDrawing->getOffsetY()));
+            $objWriter->endElement();
+
+            // xdr:ext
+            $objWriter->startElement('xdr:ext');
+            $objWriter->writeAttribute('cx', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($pDrawing->getWidth()));
+            $objWriter->writeAttribute('cy', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($pDrawing->getHeight()));
+            $objWriter->endElement();
+
+            // xdr:pic
+            $objWriter->startElement('xdr:pic');
+
+            // xdr:nvPicPr
+            $objWriter->startElement('xdr:nvPicPr');
+
+            // xdr:cNvPr
+            $objWriter->startElement('xdr:cNvPr');
+            $objWriter->writeAttribute('id', $pRelationId);
+            $objWriter->writeAttribute('name', $pDrawing->getName());
+            $objWriter->writeAttribute('descr', $pDrawing->getDescription());
+
+            //a:hlinkClick
+            $this->writeHyperLinkDrawing($objWriter, $hlinkClickId);
+
+            $objWriter->endElement();
+
+            // xdr:cNvPicPr
+            $objWriter->startElement('xdr:cNvPicPr');
+
+            // a:picLocks
+            $objWriter->startElement('a:picLocks');
+            $objWriter->writeAttribute('noChangeAspect', '1');
+            $objWriter->endElement();
+
+            $objWriter->endElement();
+
+            $objWriter->endElement();
+
+            // xdr:blipFill
+            $objWriter->startElement('xdr:blipFill');
+
+            // a:blip
+            $objWriter->startElement('a:blip');
+            $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
+            $objWriter->writeAttribute('r:embed', 'rId' . $pRelationId);
+            $objWriter->endElement();
+
+            // a:stretch
+            $objWriter->startElement('a:stretch');
+            $objWriter->writeElement('a:fillRect', null);
+            $objWriter->endElement();
+
+            $objWriter->endElement();
+
+            // xdr:spPr
+            $objWriter->startElement('xdr:spPr');
+
+            // a:xfrm
+            $objWriter->startElement('a:xfrm');
+            $objWriter->writeAttribute('rot', \PhpOffice\PhpSpreadsheet\Shared\Drawing::degreesToAngle($pDrawing->getRotation()));
+            $objWriter->endElement();
+
+            // a:prstGeom
+            $objWriter->startElement('a:prstGeom');
+            $objWriter->writeAttribute('prst', 'rect');
+
+            // a:avLst
+            $objWriter->writeElement('a:avLst', null);
+
+            $objWriter->endElement();
+
+            if ($pDrawing->getShadow()->getVisible()) {
+                // a:effectLst
+                $objWriter->startElement('a:effectLst');
+
+                // a:outerShdw
+                $objWriter->startElement('a:outerShdw');
+                $objWriter->writeAttribute('blurRad', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($pDrawing->getShadow()->getBlurRadius()));
+                $objWriter->writeAttribute('dist', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($pDrawing->getShadow()->getDistance()));
+                $objWriter->writeAttribute('dir', \PhpOffice\PhpSpreadsheet\Shared\Drawing::degreesToAngle($pDrawing->getShadow()->getDirection()));
+                $objWriter->writeAttribute('algn', $pDrawing->getShadow()->getAlignment());
+                $objWriter->writeAttribute('rotWithShape', '0');
+
+                // a:srgbClr
+                $objWriter->startElement('a:srgbClr');
+                $objWriter->writeAttribute('val', $pDrawing->getShadow()->getColor()->getRGB());
+
+                // a:alpha
+                $objWriter->startElement('a:alpha');
+                $objWriter->writeAttribute('val', $pDrawing->getShadow()->getAlpha() * 1000);
+                $objWriter->endElement();
+
+                $objWriter->endElement();
+
+                $objWriter->endElement();
+
+                $objWriter->endElement();
+            }
+            $objWriter->endElement();
+
+            $objWriter->endElement();
+
+            // xdr:clientData
+            $objWriter->writeElement('xdr:clientData', null);
+
+            $objWriter->endElement();
+        } else {
+            throw new WriterException('Invalid parameters passed.');
+        }
+    }
+
+    /**
+     * Write VML header/footer images to XML format.
+     *
+     * @return string XML Output
+     */
+    public function writeVMLHeaderFooterImages(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet)
+    {
+        // Create XML writer
+        $objWriter = null;
+        if ($this->getParentWriter()->getUseDiskCaching()) {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+        } else {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
+        }
+
+        // XML header
+        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+        // Header/footer images
+        $images = $pWorksheet->getHeaderFooter()->getImages();
+
+        // xml
+        $objWriter->startElement('xml');
+        $objWriter->writeAttribute('xmlns:v', 'urn:schemas-microsoft-com:vml');
+        $objWriter->writeAttribute('xmlns:o', 'urn:schemas-microsoft-com:office:office');
+        $objWriter->writeAttribute('xmlns:x', 'urn:schemas-microsoft-com:office:excel');
+
+        // o:shapelayout
+        $objWriter->startElement('o:shapelayout');
+        $objWriter->writeAttribute('v:ext', 'edit');
+
+        // o:idmap
+        $objWriter->startElement('o:idmap');
+        $objWriter->writeAttribute('v:ext', 'edit');
+        $objWriter->writeAttribute('data', '1');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // v:shapetype
+        $objWriter->startElement('v:shapetype');
+        $objWriter->writeAttribute('id', '_x0000_t75');
+        $objWriter->writeAttribute('coordsize', '21600,21600');
+        $objWriter->writeAttribute('o:spt', '75');
+        $objWriter->writeAttribute('o:preferrelative', 't');
+        $objWriter->writeAttribute('path', 'm@4@5l@4@11@9@11@9@5xe');
+        $objWriter->writeAttribute('filled', 'f');
+        $objWriter->writeAttribute('stroked', 'f');
+
+        // v:stroke
+        $objWriter->startElement('v:stroke');
+        $objWriter->writeAttribute('joinstyle', 'miter');
+        $objWriter->endElement();
+
+        // v:formulas
+        $objWriter->startElement('v:formulas');
+
+        // v:f
+        $objWriter->startElement('v:f');
+        $objWriter->writeAttribute('eqn', 'if lineDrawn pixelLineWidth 0');
+        $objWriter->endElement();
+
+        // v:f
+        $objWriter->startElement('v:f');
+        $objWriter->writeAttribute('eqn', 'sum @0 1 0');
+        $objWriter->endElement();
+
+        // v:f
+        $objWriter->startElement('v:f');
+        $objWriter->writeAttribute('eqn', 'sum 0 0 @1');
+        $objWriter->endElement();
+
+        // v:f
+        $objWriter->startElement('v:f');
+        $objWriter->writeAttribute('eqn', 'prod @2 1 2');
+        $objWriter->endElement();
+
+        // v:f
+        $objWriter->startElement('v:f');
+        $objWriter->writeAttribute('eqn', 'prod @3 21600 pixelWidth');
+        $objWriter->endElement();
+
+        // v:f
+        $objWriter->startElement('v:f');
+        $objWriter->writeAttribute('eqn', 'prod @3 21600 pixelHeight');
+        $objWriter->endElement();
+
+        // v:f
+        $objWriter->startElement('v:f');
+        $objWriter->writeAttribute('eqn', 'sum @0 0 1');
+        $objWriter->endElement();
+
+        // v:f
+        $objWriter->startElement('v:f');
+        $objWriter->writeAttribute('eqn', 'prod @6 1 2');
+        $objWriter->endElement();
+
+        // v:f
+        $objWriter->startElement('v:f');
+        $objWriter->writeAttribute('eqn', 'prod @7 21600 pixelWidth');
+        $objWriter->endElement();
+
+        // v:f
+        $objWriter->startElement('v:f');
+        $objWriter->writeAttribute('eqn', 'sum @8 21600 0');
+        $objWriter->endElement();
+
+        // v:f
+        $objWriter->startElement('v:f');
+        $objWriter->writeAttribute('eqn', 'prod @7 21600 pixelHeight');
+        $objWriter->endElement();
+
+        // v:f
+        $objWriter->startElement('v:f');
+        $objWriter->writeAttribute('eqn', 'sum @10 21600 0');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // v:path
+        $objWriter->startElement('v:path');
+        $objWriter->writeAttribute('o:extrusionok', 'f');
+        $objWriter->writeAttribute('gradientshapeok', 't');
+        $objWriter->writeAttribute('o:connecttype', 'rect');
+        $objWriter->endElement();
+
+        // o:lock
+        $objWriter->startElement('o:lock');
+        $objWriter->writeAttribute('v:ext', 'edit');
+        $objWriter->writeAttribute('aspectratio', 't');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // Loop through images
+        foreach ($images as $key => $value) {
+            $this->writeVMLHeaderFooterImage($objWriter, $key, $value);
+        }
+
+        $objWriter->endElement();
+
+        // Return
+        return $objWriter->getData();
+    }
+
+    /**
+     * Write VML comment to XML format.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param string $pReference Reference
+     * @param HeaderFooterDrawing $pImage Image
+     */
+    private function writeVMLHeaderFooterImage(XMLWriter $objWriter, $pReference, HeaderFooterDrawing $pImage): void
+    {
+        // Calculate object id
+        preg_match('{(\d+)}', md5($pReference), $m);
+        $id = 1500 + (substr($m[1], 0, 2) * 1);
+
+        // Calculate offset
+        $width = $pImage->getWidth();
+        $height = $pImage->getHeight();
+        $marginLeft = $pImage->getOffsetX();
+        $marginTop = $pImage->getOffsetY();
+
+        // v:shape
+        $objWriter->startElement('v:shape');
+        $objWriter->writeAttribute('id', $pReference);
+        $objWriter->writeAttribute('o:spid', '_x0000_s' . $id);
+        $objWriter->writeAttribute('type', '#_x0000_t75');
+        $objWriter->writeAttribute('style', "position:absolute;margin-left:{$marginLeft}px;margin-top:{$marginTop}px;width:{$width}px;height:{$height}px;z-index:1");
+
+        // v:imagedata
+        $objWriter->startElement('v:imagedata');
+        $objWriter->writeAttribute('o:relid', 'rId' . $pReference);
+        $objWriter->writeAttribute('o:title', $pImage->getName());
+        $objWriter->endElement();
+
+        // o:lock
+        $objWriter->startElement('o:lock');
+        $objWriter->writeAttribute('v:ext', 'edit');
+        $objWriter->writeAttribute('textRotation', 't');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Get an array of all drawings.
+     *
+     * @return \PhpOffice\PhpSpreadsheet\Worksheet\Drawing[] All drawings in PhpSpreadsheet
+     */
+    public function allDrawings(Spreadsheet $spreadsheet)
+    {
+        // Get an array of all drawings
+        $aDrawings = [];
+
+        // Loop through PhpSpreadsheet
+        $sheetCount = $spreadsheet->getSheetCount();
+        for ($i = 0; $i < $sheetCount; ++$i) {
+            // Loop through images and add to array
+            $iterator = $spreadsheet->getSheet($i)->getDrawingCollection()->getIterator();
+            while ($iterator->valid()) {
+                $aDrawings[] = $iterator->current();
+
+                $iterator->next();
+            }
+        }
+
+        return $aDrawings;
+    }
+
+    /**
+     * @param null|int $hlinkClickId
+     */
+    private function writeHyperLinkDrawing(XMLWriter $objWriter, $hlinkClickId): void
+    {
+        if ($hlinkClickId === null) {
+            return;
+        }
+
+        $objWriter->startElement('a:hlinkClick');
+        $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
+        $objWriter->writeAttribute('r:id', 'rId' . $hlinkClickId);
+        $objWriter->endElement();
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Rels.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Rels.php
new file mode 100755
index 0000000..a4e5769
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Rels.php
@@ -0,0 +1,449 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
+use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
+
+class Rels extends WriterPart
+{
+    /**
+     * Write relationships to XML format.
+     *
+     * @return string XML Output
+     */
+    public function writeRelationships(Spreadsheet $spreadsheet)
+    {
+        // Create XML writer
+        $objWriter = null;
+        if ($this->getParentWriter()->getUseDiskCaching()) {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+        } else {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
+        }
+
+        // XML header
+        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+        // Relationships
+        $objWriter->startElement('Relationships');
+        $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
+
+        $customPropertyList = $spreadsheet->getProperties()->getCustomProperties();
+        if (!empty($customPropertyList)) {
+            // Relationship docProps/app.xml
+            $this->writeRelationship(
+                $objWriter,
+                4,
+                'http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties',
+                'docProps/custom.xml'
+            );
+        }
+
+        // Relationship docProps/app.xml
+        $this->writeRelationship(
+            $objWriter,
+            3,
+            'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties',
+            'docProps/app.xml'
+        );
+
+        // Relationship docProps/core.xml
+        $this->writeRelationship(
+            $objWriter,
+            2,
+            'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties',
+            'docProps/core.xml'
+        );
+
+        // Relationship xl/workbook.xml
+        $this->writeRelationship(
+            $objWriter,
+            1,
+            'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument',
+            'xl/workbook.xml'
+        );
+        // a custom UI in workbook ?
+        if ($spreadsheet->hasRibbon()) {
+            $this->writeRelationShip(
+                $objWriter,
+                5,
+                'http://schemas.microsoft.com/office/2006/relationships/ui/extensibility',
+                $spreadsheet->getRibbonXMLData('target')
+            );
+        }
+
+        $objWriter->endElement();
+
+        return $objWriter->getData();
+    }
+
+    /**
+     * Write workbook relationships to XML format.
+     *
+     * @return string XML Output
+     */
+    public function writeWorkbookRelationships(Spreadsheet $spreadsheet)
+    {
+        // Create XML writer
+        $objWriter = null;
+        if ($this->getParentWriter()->getUseDiskCaching()) {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+        } else {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
+        }
+
+        // XML header
+        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+        // Relationships
+        $objWriter->startElement('Relationships');
+        $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
+
+        // Relationship styles.xml
+        $this->writeRelationship(
+            $objWriter,
+            1,
+            'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles',
+            'styles.xml'
+        );
+
+        // Relationship theme/theme1.xml
+        $this->writeRelationship(
+            $objWriter,
+            2,
+            'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme',
+            'theme/theme1.xml'
+        );
+
+        // Relationship sharedStrings.xml
+        $this->writeRelationship(
+            $objWriter,
+            3,
+            'http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings',
+            'sharedStrings.xml'
+        );
+
+        // Relationships with sheets
+        $sheetCount = $spreadsheet->getSheetCount();
+        for ($i = 0; $i < $sheetCount; ++$i) {
+            $this->writeRelationship(
+                $objWriter,
+                ($i + 1 + 3),
+                'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet',
+                'worksheets/sheet' . ($i + 1) . '.xml'
+            );
+        }
+        // Relationships for vbaProject if needed
+        // id : just after the last sheet
+        if ($spreadsheet->hasMacros()) {
+            $this->writeRelationShip(
+                $objWriter,
+                ($i + 1 + 3),
+                'http://schemas.microsoft.com/office/2006/relationships/vbaProject',
+                'vbaProject.bin'
+            );
+            ++$i; //increment i if needed for an another relation
+        }
+
+        $objWriter->endElement();
+
+        return $objWriter->getData();
+    }
+
+    /**
+     * Write worksheet relationships to XML format.
+     *
+     * Numbering is as follows:
+     *     rId1                 - Drawings
+     *  rId_hyperlink_x     - Hyperlinks
+     *
+     * @param int $pWorksheetId
+     * @param bool $includeCharts Flag indicating if we should write charts
+     *
+     * @return string XML Output
+     */
+    public function writeWorksheetRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet, $pWorksheetId = 1, $includeCharts = false)
+    {
+        // Create XML writer
+        $objWriter = null;
+        if ($this->getParentWriter()->getUseDiskCaching()) {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+        } else {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
+        }
+
+        // XML header
+        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+        // Relationships
+        $objWriter->startElement('Relationships');
+        $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
+
+        // Write drawing relationships?
+        $drawingOriginalIds = [];
+        $unparsedLoadedData = $pWorksheet->getParent()->getUnparsedLoadedData();
+        if (isset($unparsedLoadedData['sheets'][$pWorksheet->getCodeName()]['drawingOriginalIds'])) {
+            $drawingOriginalIds = $unparsedLoadedData['sheets'][$pWorksheet->getCodeName()]['drawingOriginalIds'];
+        }
+
+        if ($includeCharts) {
+            $charts = $pWorksheet->getChartCollection();
+        } else {
+            $charts = [];
+        }
+
+        if (($pWorksheet->getDrawingCollection()->count() > 0) || (count($charts) > 0) || $drawingOriginalIds) {
+            $rId = 1;
+
+            // Use original $relPath to get original $rId.
+            // Take first. In future can be overwritten.
+            // (! synchronize with \PhpOffice\PhpSpreadsheet\Writer\Xlsx\Worksheet::writeDrawings)
+            reset($drawingOriginalIds);
+            $relPath = key($drawingOriginalIds);
+            if (isset($drawingOriginalIds[$relPath])) {
+                $rId = (int) (substr($drawingOriginalIds[$relPath], 3));
+            }
+
+            // Generate new $relPath to write drawing relationship
+            $relPath = '../drawings/drawing' . $pWorksheetId . '.xml';
+            $this->writeRelationship(
+                $objWriter,
+                $rId,
+                'http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing',
+                $relPath
+            );
+        }
+
+        // Write hyperlink relationships?
+        $i = 1;
+        foreach ($pWorksheet->getHyperlinkCollection() as $hyperlink) {
+            if (!$hyperlink->isInternal()) {
+                $this->writeRelationship(
+                    $objWriter,
+                    '_hyperlink_' . $i,
+                    'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink',
+                    $hyperlink->getUrl(),
+                    'External'
+                );
+
+                ++$i;
+            }
+        }
+
+        // Write comments relationship?
+        $i = 1;
+        if (count($pWorksheet->getComments()) > 0) {
+            $this->writeRelationship(
+                $objWriter,
+                '_comments_vml' . $i,
+                'http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing',
+                '../drawings/vmlDrawing' . $pWorksheetId . '.vml'
+            );
+
+            $this->writeRelationship(
+                $objWriter,
+                '_comments' . $i,
+                'http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments',
+                '../comments' . $pWorksheetId . '.xml'
+            );
+        }
+
+        // Write header/footer relationship?
+        $i = 1;
+        if (count($pWorksheet->getHeaderFooter()->getImages()) > 0) {
+            $this->writeRelationship(
+                $objWriter,
+                '_headerfooter_vml' . $i,
+                'http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing',
+                '../drawings/vmlDrawingHF' . $pWorksheetId . '.vml'
+            );
+        }
+
+        $this->writeUnparsedRelationship($pWorksheet, $objWriter, 'ctrlProps', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/ctrlProp');
+        $this->writeUnparsedRelationship($pWorksheet, $objWriter, 'vmlDrawings', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing');
+        $this->writeUnparsedRelationship($pWorksheet, $objWriter, 'printerSettings', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/printerSettings');
+
+        $objWriter->endElement();
+
+        return $objWriter->getData();
+    }
+
+    private function writeUnparsedRelationship(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet, XMLWriter $objWriter, $relationship, $type): void
+    {
+        $unparsedLoadedData = $pWorksheet->getParent()->getUnparsedLoadedData();
+        if (!isset($unparsedLoadedData['sheets'][$pWorksheet->getCodeName()][$relationship])) {
+            return;
+        }
+
+        foreach ($unparsedLoadedData['sheets'][$pWorksheet->getCodeName()][$relationship] as $rId => $value) {
+            $this->writeRelationship(
+                $objWriter,
+                $rId,
+                $type,
+                $value['relFilePath']
+            );
+        }
+    }
+
+    /**
+     * Write drawing relationships to XML format.
+     *
+     * @param int &$chartRef Chart ID
+     * @param bool $includeCharts Flag indicating if we should write charts
+     *
+     * @return string XML Output
+     */
+    public function writeDrawingRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet, &$chartRef, $includeCharts = false)
+    {
+        // Create XML writer
+        $objWriter = null;
+        if ($this->getParentWriter()->getUseDiskCaching()) {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+        } else {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
+        }
+
+        // XML header
+        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+        // Relationships
+        $objWriter->startElement('Relationships');
+        $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
+
+        // Loop through images and write relationships
+        $i = 1;
+        $iterator = $pWorksheet->getDrawingCollection()->getIterator();
+        while ($iterator->valid()) {
+            if ($iterator->current() instanceof \PhpOffice\PhpSpreadsheet\Worksheet\Drawing
+                || $iterator->current() instanceof MemoryDrawing) {
+                // Write relationship for image drawing
+                /** @var \PhpOffice\PhpSpreadsheet\Worksheet\Drawing $drawing */
+                $drawing = $iterator->current();
+                $this->writeRelationship(
+                    $objWriter,
+                    $i,
+                    'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image',
+                    '../media/' . str_replace(' ', '', $drawing->getIndexedFilename())
+                );
+
+                $i = $this->writeDrawingHyperLink($objWriter, $drawing, $i);
+            }
+
+            $iterator->next();
+            ++$i;
+        }
+
+        if ($includeCharts) {
+            // Loop through charts and write relationships
+            $chartCount = $pWorksheet->getChartCount();
+            if ($chartCount > 0) {
+                for ($c = 0; $c < $chartCount; ++$c) {
+                    $this->writeRelationship(
+                        $objWriter,
+                        $i++,
+                        'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart',
+                        '../charts/chart' . ++$chartRef . '.xml'
+                    );
+                }
+            }
+        }
+
+        $objWriter->endElement();
+
+        return $objWriter->getData();
+    }
+
+    /**
+     * Write header/footer drawing relationships to XML format.
+     *
+     * @return string XML Output
+     */
+    public function writeHeaderFooterDrawingRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet)
+    {
+        // Create XML writer
+        $objWriter = null;
+        if ($this->getParentWriter()->getUseDiskCaching()) {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+        } else {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
+        }
+
+        // XML header
+        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+        // Relationships
+        $objWriter->startElement('Relationships');
+        $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
+
+        // Loop through images and write relationships
+        foreach ($pWorksheet->getHeaderFooter()->getImages() as $key => $value) {
+            // Write relationship for image drawing
+            $this->writeRelationship(
+                $objWriter,
+                $key,
+                'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image',
+                '../media/' . $value->getIndexedFilename()
+            );
+        }
+
+        $objWriter->endElement();
+
+        return $objWriter->getData();
+    }
+
+    /**
+     * Write Override content type.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param int $pId Relationship ID. rId will be prepended!
+     * @param string $pType Relationship type
+     * @param string $pTarget Relationship target
+     * @param string $pTargetMode Relationship target mode
+     */
+    private function writeRelationship(XMLWriter $objWriter, $pId, $pType, $pTarget, $pTargetMode = ''): void
+    {
+        if ($pType != '' && $pTarget != '') {
+            // Write relationship
+            $objWriter->startElement('Relationship');
+            $objWriter->writeAttribute('Id', 'rId' . $pId);
+            $objWriter->writeAttribute('Type', $pType);
+            $objWriter->writeAttribute('Target', $pTarget);
+
+            if ($pTargetMode != '') {
+                $objWriter->writeAttribute('TargetMode', $pTargetMode);
+            }
+
+            $objWriter->endElement();
+        } else {
+            throw new WriterException('Invalid parameters passed.');
+        }
+    }
+
+    /**
+     * @param $objWriter
+     * @param \PhpOffice\PhpSpreadsheet\Worksheet\Drawing $drawing
+     * @param $i
+     *
+     * @return int
+     */
+    private function writeDrawingHyperLink($objWriter, $drawing, $i)
+    {
+        if ($drawing->getHyperlink() === null) {
+            return $i;
+        }
+
+        ++$i;
+        $this->writeRelationship(
+            $objWriter,
+            $i,
+            'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink',
+            $drawing->getHyperlink()->getUrl(),
+            $drawing->getHyperlink()->getTypeHyperlink()
+        );
+
+        return $i;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsRibbon.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsRibbon.php
new file mode 100755
index 0000000..8005207
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsRibbon.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+
+class RelsRibbon extends WriterPart
+{
+    /**
+     * Write relationships for additional objects of custom UI (ribbon).
+     *
+     * @return string XML Output
+     */
+    public function writeRibbonRelationships(Spreadsheet $spreadsheet)
+    {
+        // Create XML writer
+        $objWriter = null;
+        if ($this->getParentWriter()->getUseDiskCaching()) {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+        } else {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
+        }
+
+        // XML header
+        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+        // Relationships
+        $objWriter->startElement('Relationships');
+        $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
+        $localRels = $spreadsheet->getRibbonBinObjects('names');
+        if (is_array($localRels)) {
+            foreach ($localRels as $aId => $aTarget) {
+                $objWriter->startElement('Relationship');
+                $objWriter->writeAttribute('Id', $aId);
+                $objWriter->writeAttribute('Type', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image');
+                $objWriter->writeAttribute('Target', $aTarget);
+                $objWriter->endElement();
+            }
+        }
+        $objWriter->endElement();
+
+        return $objWriter->getData();
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsVBA.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsVBA.php
new file mode 100755
index 0000000..55bcd36
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsVBA.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+
+class RelsVBA extends WriterPart
+{
+    /**
+     * Write relationships for a signed VBA Project.
+     *
+     * @return string XML Output
+     */
+    public function writeVBARelationships(Spreadsheet $spreadsheet)
+    {
+        // Create XML writer
+        $objWriter = null;
+        if ($this->getParentWriter()->getUseDiskCaching()) {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+        } else {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
+        }
+
+        // XML header
+        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+        // Relationships
+        $objWriter->startElement('Relationships');
+        $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
+        $objWriter->startElement('Relationship');
+        $objWriter->writeAttribute('Id', 'rId1');
+        $objWriter->writeAttribute('Type', 'http://schemas.microsoft.com/office/2006/relationships/vbaProjectSignature');
+        $objWriter->writeAttribute('Target', 'vbaProjectSignature.bin');
+        $objWriter->endElement();
+        $objWriter->endElement();
+
+        return $objWriter->getData();
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php
new file mode 100755
index 0000000..872d034
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php
@@ -0,0 +1,278 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Cell\DataType;
+use PhpOffice\PhpSpreadsheet\RichText\RichText;
+use PhpOffice\PhpSpreadsheet\RichText\Run;
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+
+class StringTable extends WriterPart
+{
+    /**
+     * Create worksheet stringtable.
+     *
+     * @param Worksheet $pSheet Worksheet
+     * @param string[] $pExistingTable Existing table to eventually merge with
+     *
+     * @return string[] String table for worksheet
+     */
+    public function createStringTable(Worksheet $pSheet, $pExistingTable = null)
+    {
+        // Create string lookup table
+        $aStringTable = [];
+        $cellCollection = null;
+        $aFlippedStringTable = null; // For faster lookup
+
+        // Is an existing table given?
+        if (($pExistingTable !== null) && is_array($pExistingTable)) {
+            $aStringTable = $pExistingTable;
+        }
+
+        // Fill index array
+        $aFlippedStringTable = $this->flipStringTable($aStringTable);
+
+        // Loop through cells
+        foreach ($pSheet->getCoordinates() as $coordinate) {
+            $cell = $pSheet->getCell($coordinate);
+            $cellValue = $cell->getValue();
+            if (!is_object($cellValue) &&
+                ($cellValue !== null) &&
+                $cellValue !== '' &&
+                !isset($aFlippedStringTable[$cellValue]) &&
+                ($cell->getDataType() == DataType::TYPE_STRING || $cell->getDataType() == DataType::TYPE_STRING2 || $cell->getDataType() == DataType::TYPE_NULL)) {
+                $aStringTable[] = $cellValue;
+                $aFlippedStringTable[$cellValue] = true;
+            } elseif ($cellValue instanceof RichText &&
+                ($cellValue !== null) &&
+                !isset($aFlippedStringTable[$cellValue->getHashCode()])) {
+                $aStringTable[] = $cellValue;
+                $aFlippedStringTable[$cellValue->getHashCode()] = true;
+            }
+        }
+
+        return $aStringTable;
+    }
+
+    /**
+     * Write string table to XML format.
+     *
+     * @param string[] $pStringTable
+     *
+     * @return string XML Output
+     */
+    public function writeStringTable(array $pStringTable)
+    {
+        // Create XML writer
+        $objWriter = null;
+        if ($this->getParentWriter()->getUseDiskCaching()) {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+        } else {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
+        }
+
+        // XML header
+        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+        // String table
+        $objWriter->startElement('sst');
+        $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
+        $objWriter->writeAttribute('uniqueCount', count($pStringTable));
+
+        // Loop through string table
+        foreach ($pStringTable as $textElement) {
+            $objWriter->startElement('si');
+
+            if (!$textElement instanceof RichText) {
+                $textToWrite = StringHelper::controlCharacterPHP2OOXML($textElement);
+                $objWriter->startElement('t');
+                if ($textToWrite !== trim($textToWrite)) {
+                    $objWriter->writeAttribute('xml:space', 'preserve');
+                }
+                $objWriter->writeRawData($textToWrite);
+                $objWriter->endElement();
+            } elseif ($textElement instanceof RichText) {
+                $this->writeRichText($objWriter, $textElement);
+            }
+
+            $objWriter->endElement();
+        }
+
+        $objWriter->endElement();
+
+        return $objWriter->getData();
+    }
+
+    /**
+     * Write Rich Text.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param RichText $pRichText Rich text
+     * @param string $prefix Optional Namespace prefix
+     */
+    public function writeRichText(XMLWriter $objWriter, RichText $pRichText, $prefix = null): void
+    {
+        if ($prefix !== null) {
+            $prefix .= ':';
+        }
+
+        // Loop through rich text elements
+        $elements = $pRichText->getRichTextElements();
+        foreach ($elements as $element) {
+            // r
+            $objWriter->startElement($prefix . 'r');
+
+            // rPr
+            if ($element instanceof Run) {
+                // rPr
+                $objWriter->startElement($prefix . 'rPr');
+
+                // rFont
+                $objWriter->startElement($prefix . 'rFont');
+                $objWriter->writeAttribute('val', $element->getFont()->getName());
+                $objWriter->endElement();
+
+                // Bold
+                $objWriter->startElement($prefix . 'b');
+                $objWriter->writeAttribute('val', ($element->getFont()->getBold() ? 'true' : 'false'));
+                $objWriter->endElement();
+
+                // Italic
+                $objWriter->startElement($prefix . 'i');
+                $objWriter->writeAttribute('val', ($element->getFont()->getItalic() ? 'true' : 'false'));
+                $objWriter->endElement();
+
+                // Superscript / subscript
+                if ($element->getFont()->getSuperscript() || $element->getFont()->getSubscript()) {
+                    $objWriter->startElement($prefix . 'vertAlign');
+                    if ($element->getFont()->getSuperscript()) {
+                        $objWriter->writeAttribute('val', 'superscript');
+                    } elseif ($element->getFont()->getSubscript()) {
+                        $objWriter->writeAttribute('val', 'subscript');
+                    }
+                    $objWriter->endElement();
+                }
+
+                // Strikethrough
+                $objWriter->startElement($prefix . 'strike');
+                $objWriter->writeAttribute('val', ($element->getFont()->getStrikethrough() ? 'true' : 'false'));
+                $objWriter->endElement();
+
+                // Color
+                $objWriter->startElement($prefix . 'color');
+                $objWriter->writeAttribute('rgb', $element->getFont()->getColor()->getARGB());
+                $objWriter->endElement();
+
+                // Size
+                $objWriter->startElement($prefix . 'sz');
+                $objWriter->writeAttribute('val', $element->getFont()->getSize());
+                $objWriter->endElement();
+
+                // Underline
+                $objWriter->startElement($prefix . 'u');
+                $objWriter->writeAttribute('val', $element->getFont()->getUnderline());
+                $objWriter->endElement();
+
+                $objWriter->endElement();
+            }
+
+            // t
+            $objWriter->startElement($prefix . 't');
+            $objWriter->writeAttribute('xml:space', 'preserve');
+            $objWriter->writeRawData(StringHelper::controlCharacterPHP2OOXML($element->getText()));
+            $objWriter->endElement();
+
+            $objWriter->endElement();
+        }
+    }
+
+    /**
+     * Write Rich Text.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param RichText|string $pRichText text string or Rich text
+     * @param string $prefix Optional Namespace prefix
+     */
+    public function writeRichTextForCharts(XMLWriter $objWriter, $pRichText = null, $prefix = null): void
+    {
+        if (!$pRichText instanceof RichText) {
+            $textRun = $pRichText;
+            $pRichText = new RichText();
+            $pRichText->createTextRun($textRun);
+        }
+
+        if ($prefix !== null) {
+            $prefix .= ':';
+        }
+
+        // Loop through rich text elements
+        $elements = $pRichText->getRichTextElements();
+        foreach ($elements as $element) {
+            // r
+            $objWriter->startElement($prefix . 'r');
+
+            // rPr
+            $objWriter->startElement($prefix . 'rPr');
+
+            // Bold
+            $objWriter->writeAttribute('b', ($element->getFont()->getBold() ? 1 : 0));
+            // Italic
+            $objWriter->writeAttribute('i', ($element->getFont()->getItalic() ? 1 : 0));
+            // Underline
+            $underlineType = $element->getFont()->getUnderline();
+            switch ($underlineType) {
+                case 'single':
+                    $underlineType = 'sng';
+
+                    break;
+                case 'double':
+                    $underlineType = 'dbl';
+
+                    break;
+            }
+            $objWriter->writeAttribute('u', $underlineType);
+            // Strikethrough
+            $objWriter->writeAttribute('strike', ($element->getFont()->getStrikethrough() ? 'sngStrike' : 'noStrike'));
+
+            // rFont
+            $objWriter->startElement($prefix . 'latin');
+            $objWriter->writeAttribute('typeface', $element->getFont()->getName());
+            $objWriter->endElement();
+
+            $objWriter->endElement();
+
+            // t
+            $objWriter->startElement($prefix . 't');
+            $objWriter->writeRawData(StringHelper::controlCharacterPHP2OOXML($element->getText()));
+            $objWriter->endElement();
+
+            $objWriter->endElement();
+        }
+    }
+
+    /**
+     * Flip string table (for index searching).
+     *
+     * @param array $stringTable Stringtable
+     *
+     * @return array
+     */
+    public function flipStringTable(array $stringTable)
+    {
+        // Return value
+        $returnValue = [];
+
+        // Loop through stringtable and add flipped items to $returnValue
+        foreach ($stringTable as $key => $value) {
+            if (!$value instanceof RichText) {
+                $returnValue[$value] = $key;
+            } elseif ($value instanceof RichText) {
+                $returnValue[$value->getHashCode()] = $key;
+            }
+        }
+
+        return $returnValue;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Style.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Style.php
new file mode 100755
index 0000000..74300a9
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Style.php
@@ -0,0 +1,668 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Style\Border;
+use PhpOffice\PhpSpreadsheet\Style\Borders;
+use PhpOffice\PhpSpreadsheet\Style\Conditional;
+use PhpOffice\PhpSpreadsheet\Style\Fill;
+use PhpOffice\PhpSpreadsheet\Style\Font;
+use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
+use PhpOffice\PhpSpreadsheet\Style\Protection;
+
+class Style extends WriterPart
+{
+    /**
+     * Write styles to XML format.
+     *
+     * @return string XML Output
+     */
+    public function writeStyles(Spreadsheet $spreadsheet)
+    {
+        // Create XML writer
+        $objWriter = null;
+        if ($this->getParentWriter()->getUseDiskCaching()) {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+        } else {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
+        }
+
+        // XML header
+        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+        // styleSheet
+        $objWriter->startElement('styleSheet');
+        $objWriter->writeAttribute('xml:space', 'preserve');
+        $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
+
+        // numFmts
+        $objWriter->startElement('numFmts');
+        $objWriter->writeAttribute('count', $this->getParentWriter()->getNumFmtHashTable()->count());
+
+        // numFmt
+        for ($i = 0; $i < $this->getParentWriter()->getNumFmtHashTable()->count(); ++$i) {
+            $this->writeNumFmt($objWriter, $this->getParentWriter()->getNumFmtHashTable()->getByIndex($i), $i);
+        }
+
+        $objWriter->endElement();
+
+        // fonts
+        $objWriter->startElement('fonts');
+        $objWriter->writeAttribute('count', $this->getParentWriter()->getFontHashTable()->count());
+
+        // font
+        for ($i = 0; $i < $this->getParentWriter()->getFontHashTable()->count(); ++$i) {
+            $this->writeFont($objWriter, $this->getParentWriter()->getFontHashTable()->getByIndex($i));
+        }
+
+        $objWriter->endElement();
+
+        // fills
+        $objWriter->startElement('fills');
+        $objWriter->writeAttribute('count', $this->getParentWriter()->getFillHashTable()->count());
+
+        // fill
+        for ($i = 0; $i < $this->getParentWriter()->getFillHashTable()->count(); ++$i) {
+            $this->writeFill($objWriter, $this->getParentWriter()->getFillHashTable()->getByIndex($i));
+        }
+
+        $objWriter->endElement();
+
+        // borders
+        $objWriter->startElement('borders');
+        $objWriter->writeAttribute('count', $this->getParentWriter()->getBordersHashTable()->count());
+
+        // border
+        for ($i = 0; $i < $this->getParentWriter()->getBordersHashTable()->count(); ++$i) {
+            $this->writeBorder($objWriter, $this->getParentWriter()->getBordersHashTable()->getByIndex($i));
+        }
+
+        $objWriter->endElement();
+
+        // cellStyleXfs
+        $objWriter->startElement('cellStyleXfs');
+        $objWriter->writeAttribute('count', 1);
+
+        // xf
+        $objWriter->startElement('xf');
+        $objWriter->writeAttribute('numFmtId', 0);
+        $objWriter->writeAttribute('fontId', 0);
+        $objWriter->writeAttribute('fillId', 0);
+        $objWriter->writeAttribute('borderId', 0);
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // cellXfs
+        $objWriter->startElement('cellXfs');
+        $objWriter->writeAttribute('count', count($spreadsheet->getCellXfCollection()));
+
+        // xf
+        foreach ($spreadsheet->getCellXfCollection() as $cellXf) {
+            $this->writeCellStyleXf($objWriter, $cellXf, $spreadsheet);
+        }
+
+        $objWriter->endElement();
+
+        // cellStyles
+        $objWriter->startElement('cellStyles');
+        $objWriter->writeAttribute('count', 1);
+
+        // cellStyle
+        $objWriter->startElement('cellStyle');
+        $objWriter->writeAttribute('name', 'Normal');
+        $objWriter->writeAttribute('xfId', 0);
+        $objWriter->writeAttribute('builtinId', 0);
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // dxfs
+        $objWriter->startElement('dxfs');
+        $objWriter->writeAttribute('count', $this->getParentWriter()->getStylesConditionalHashTable()->count());
+
+        // dxf
+        for ($i = 0; $i < $this->getParentWriter()->getStylesConditionalHashTable()->count(); ++$i) {
+            $this->writeCellStyleDxf($objWriter, $this->getParentWriter()->getStylesConditionalHashTable()->getByIndex($i)->getStyle());
+        }
+
+        $objWriter->endElement();
+
+        // tableStyles
+        $objWriter->startElement('tableStyles');
+        $objWriter->writeAttribute('defaultTableStyle', 'TableStyleMedium9');
+        $objWriter->writeAttribute('defaultPivotStyle', 'PivotTableStyle1');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // Return
+        return $objWriter->getData();
+    }
+
+    /**
+     * Write Fill.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param Fill $pFill Fill style
+     */
+    private function writeFill(XMLWriter $objWriter, Fill $pFill): void
+    {
+        // Check if this is a pattern type or gradient type
+        if ($pFill->getFillType() === Fill::FILL_GRADIENT_LINEAR ||
+            $pFill->getFillType() === Fill::FILL_GRADIENT_PATH) {
+            // Gradient fill
+            $this->writeGradientFill($objWriter, $pFill);
+        } elseif ($pFill->getFillType() !== null) {
+            // Pattern fill
+            $this->writePatternFill($objWriter, $pFill);
+        }
+    }
+
+    /**
+     * Write Gradient Fill.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param Fill $pFill Fill style
+     */
+    private function writeGradientFill(XMLWriter $objWriter, Fill $pFill): void
+    {
+        // fill
+        $objWriter->startElement('fill');
+
+        // gradientFill
+        $objWriter->startElement('gradientFill');
+        $objWriter->writeAttribute('type', $pFill->getFillType());
+        $objWriter->writeAttribute('degree', $pFill->getRotation());
+
+        // stop
+        $objWriter->startElement('stop');
+        $objWriter->writeAttribute('position', '0');
+
+        // color
+        $objWriter->startElement('color');
+        $objWriter->writeAttribute('rgb', $pFill->getStartColor()->getARGB());
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // stop
+        $objWriter->startElement('stop');
+        $objWriter->writeAttribute('position', '1');
+
+        // color
+        $objWriter->startElement('color');
+        $objWriter->writeAttribute('rgb', $pFill->getEndColor()->getARGB());
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write Pattern Fill.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param Fill $pFill Fill style
+     */
+    private function writePatternFill(XMLWriter $objWriter, Fill $pFill): void
+    {
+        // fill
+        $objWriter->startElement('fill');
+
+        // patternFill
+        $objWriter->startElement('patternFill');
+        $objWriter->writeAttribute('patternType', $pFill->getFillType());
+
+        if ($pFill->getFillType() !== Fill::FILL_NONE) {
+            // fgColor
+            if ($pFill->getStartColor()->getARGB()) {
+                $objWriter->startElement('fgColor');
+                $objWriter->writeAttribute('rgb', $pFill->getStartColor()->getARGB());
+                $objWriter->endElement();
+            }
+        }
+        if ($pFill->getFillType() !== Fill::FILL_NONE) {
+            // bgColor
+            if ($pFill->getEndColor()->getARGB()) {
+                $objWriter->startElement('bgColor');
+                $objWriter->writeAttribute('rgb', $pFill->getEndColor()->getARGB());
+                $objWriter->endElement();
+            }
+        }
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write Font.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param Font $pFont Font style
+     */
+    private function writeFont(XMLWriter $objWriter, Font $pFont): void
+    {
+        // font
+        $objWriter->startElement('font');
+        //    Weird! The order of these elements actually makes a difference when opening Xlsx
+        //        files in Excel2003 with the compatibility pack. It's not documented behaviour,
+        //        and makes for a real WTF!
+
+        // Bold. We explicitly write this element also when false (like MS Office Excel 2007 does
+        // for conditional formatting). Otherwise it will apparently not be picked up in conditional
+        // formatting style dialog
+        if ($pFont->getBold() !== null) {
+            $objWriter->startElement('b');
+            $objWriter->writeAttribute('val', $pFont->getBold() ? '1' : '0');
+            $objWriter->endElement();
+        }
+
+        // Italic
+        if ($pFont->getItalic() !== null) {
+            $objWriter->startElement('i');
+            $objWriter->writeAttribute('val', $pFont->getItalic() ? '1' : '0');
+            $objWriter->endElement();
+        }
+
+        // Strikethrough
+        if ($pFont->getStrikethrough() !== null) {
+            $objWriter->startElement('strike');
+            $objWriter->writeAttribute('val', $pFont->getStrikethrough() ? '1' : '0');
+            $objWriter->endElement();
+        }
+
+        // Underline
+        if ($pFont->getUnderline() !== null) {
+            $objWriter->startElement('u');
+            $objWriter->writeAttribute('val', $pFont->getUnderline());
+            $objWriter->endElement();
+        }
+
+        // Superscript / subscript
+        if ($pFont->getSuperscript() === true || $pFont->getSubscript() === true) {
+            $objWriter->startElement('vertAlign');
+            if ($pFont->getSuperscript() === true) {
+                $objWriter->writeAttribute('val', 'superscript');
+            } elseif ($pFont->getSubscript() === true) {
+                $objWriter->writeAttribute('val', 'subscript');
+            }
+            $objWriter->endElement();
+        }
+
+        // Size
+        if ($pFont->getSize() !== null) {
+            $objWriter->startElement('sz');
+            $objWriter->writeAttribute('val', StringHelper::formatNumber($pFont->getSize()));
+            $objWriter->endElement();
+        }
+
+        // Foreground color
+        if ($pFont->getColor()->getARGB() !== null) {
+            $objWriter->startElement('color');
+            $objWriter->writeAttribute('rgb', $pFont->getColor()->getARGB());
+            $objWriter->endElement();
+        }
+
+        // Name
+        if ($pFont->getName() !== null) {
+            $objWriter->startElement('name');
+            $objWriter->writeAttribute('val', $pFont->getName());
+            $objWriter->endElement();
+        }
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write Border.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param Borders $pBorders Borders style
+     */
+    private function writeBorder(XMLWriter $objWriter, Borders $pBorders): void
+    {
+        // Write border
+        $objWriter->startElement('border');
+        // Diagonal?
+        switch ($pBorders->getDiagonalDirection()) {
+            case Borders::DIAGONAL_UP:
+                $objWriter->writeAttribute('diagonalUp', 'true');
+                $objWriter->writeAttribute('diagonalDown', 'false');
+
+                break;
+            case Borders::DIAGONAL_DOWN:
+                $objWriter->writeAttribute('diagonalUp', 'false');
+                $objWriter->writeAttribute('diagonalDown', 'true');
+
+                break;
+            case Borders::DIAGONAL_BOTH:
+                $objWriter->writeAttribute('diagonalUp', 'true');
+                $objWriter->writeAttribute('diagonalDown', 'true');
+
+                break;
+        }
+
+        // BorderPr
+        $this->writeBorderPr($objWriter, 'left', $pBorders->getLeft());
+        $this->writeBorderPr($objWriter, 'right', $pBorders->getRight());
+        $this->writeBorderPr($objWriter, 'top', $pBorders->getTop());
+        $this->writeBorderPr($objWriter, 'bottom', $pBorders->getBottom());
+        $this->writeBorderPr($objWriter, 'diagonal', $pBorders->getDiagonal());
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write Cell Style Xf.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param \PhpOffice\PhpSpreadsheet\Style\Style $pStyle Style
+     * @param Spreadsheet $spreadsheet Workbook
+     */
+    private function writeCellStyleXf(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Style\Style $pStyle, Spreadsheet $spreadsheet): void
+    {
+        // xf
+        $objWriter->startElement('xf');
+        $objWriter->writeAttribute('xfId', 0);
+        $objWriter->writeAttribute('fontId', (int) $this->getParentWriter()->getFontHashTable()->getIndexForHashCode($pStyle->getFont()->getHashCode()));
+        if ($pStyle->getQuotePrefix()) {
+            $objWriter->writeAttribute('quotePrefix', 1);
+        }
+
+        if ($pStyle->getNumberFormat()->getBuiltInFormatCode() === false) {
+            $objWriter->writeAttribute('numFmtId', (int) ($this->getParentWriter()->getNumFmtHashTable()->getIndexForHashCode($pStyle->getNumberFormat()->getHashCode()) + 164));
+        } else {
+            $objWriter->writeAttribute('numFmtId', (int) $pStyle->getNumberFormat()->getBuiltInFormatCode());
+        }
+
+        $objWriter->writeAttribute('fillId', (int) $this->getParentWriter()->getFillHashTable()->getIndexForHashCode($pStyle->getFill()->getHashCode()));
+        $objWriter->writeAttribute('borderId', (int) $this->getParentWriter()->getBordersHashTable()->getIndexForHashCode($pStyle->getBorders()->getHashCode()));
+
+        // Apply styles?
+        $objWriter->writeAttribute('applyFont', ($spreadsheet->getDefaultStyle()->getFont()->getHashCode() != $pStyle->getFont()->getHashCode()) ? '1' : '0');
+        $objWriter->writeAttribute('applyNumberFormat', ($spreadsheet->getDefaultStyle()->getNumberFormat()->getHashCode() != $pStyle->getNumberFormat()->getHashCode()) ? '1' : '0');
+        $objWriter->writeAttribute('applyFill', ($spreadsheet->getDefaultStyle()->getFill()->getHashCode() != $pStyle->getFill()->getHashCode()) ? '1' : '0');
+        $objWriter->writeAttribute('applyBorder', ($spreadsheet->getDefaultStyle()->getBorders()->getHashCode() != $pStyle->getBorders()->getHashCode()) ? '1' : '0');
+        $objWriter->writeAttribute('applyAlignment', ($spreadsheet->getDefaultStyle()->getAlignment()->getHashCode() != $pStyle->getAlignment()->getHashCode()) ? '1' : '0');
+        if ($pStyle->getProtection()->getLocked() != Protection::PROTECTION_INHERIT || $pStyle->getProtection()->getHidden() != Protection::PROTECTION_INHERIT) {
+            $objWriter->writeAttribute('applyProtection', 'true');
+        }
+
+        // alignment
+        $objWriter->startElement('alignment');
+        $objWriter->writeAttribute('horizontal', $pStyle->getAlignment()->getHorizontal());
+        $objWriter->writeAttribute('vertical', $pStyle->getAlignment()->getVertical());
+
+        $textRotation = 0;
+        if ($pStyle->getAlignment()->getTextRotation() >= 0) {
+            $textRotation = $pStyle->getAlignment()->getTextRotation();
+        } elseif ($pStyle->getAlignment()->getTextRotation() < 0) {
+            $textRotation = 90 - $pStyle->getAlignment()->getTextRotation();
+        }
+        $objWriter->writeAttribute('textRotation', $textRotation);
+
+        $objWriter->writeAttribute('wrapText', ($pStyle->getAlignment()->getWrapText() ? 'true' : 'false'));
+        $objWriter->writeAttribute('shrinkToFit', ($pStyle->getAlignment()->getShrinkToFit() ? 'true' : 'false'));
+
+        if ($pStyle->getAlignment()->getIndent() > 0) {
+            $objWriter->writeAttribute('indent', $pStyle->getAlignment()->getIndent());
+        }
+        if ($pStyle->getAlignment()->getReadOrder() > 0) {
+            $objWriter->writeAttribute('readingOrder', $pStyle->getAlignment()->getReadOrder());
+        }
+        $objWriter->endElement();
+
+        // protection
+        if ($pStyle->getProtection()->getLocked() != Protection::PROTECTION_INHERIT || $pStyle->getProtection()->getHidden() != Protection::PROTECTION_INHERIT) {
+            $objWriter->startElement('protection');
+            if ($pStyle->getProtection()->getLocked() != Protection::PROTECTION_INHERIT) {
+                $objWriter->writeAttribute('locked', ($pStyle->getProtection()->getLocked() == Protection::PROTECTION_PROTECTED ? 'true' : 'false'));
+            }
+            if ($pStyle->getProtection()->getHidden() != Protection::PROTECTION_INHERIT) {
+                $objWriter->writeAttribute('hidden', ($pStyle->getProtection()->getHidden() == Protection::PROTECTION_PROTECTED ? 'true' : 'false'));
+            }
+            $objWriter->endElement();
+        }
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write Cell Style Dxf.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param \PhpOffice\PhpSpreadsheet\Style\Style $pStyle Style
+     */
+    private function writeCellStyleDxf(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Style\Style $pStyle): void
+    {
+        // dxf
+        $objWriter->startElement('dxf');
+
+        // font
+        $this->writeFont($objWriter, $pStyle->getFont());
+
+        // numFmt
+        $this->writeNumFmt($objWriter, $pStyle->getNumberFormat());
+
+        // fill
+        $this->writeFill($objWriter, $pStyle->getFill());
+
+        // alignment
+        $objWriter->startElement('alignment');
+        if ($pStyle->getAlignment()->getHorizontal() !== null) {
+            $objWriter->writeAttribute('horizontal', $pStyle->getAlignment()->getHorizontal());
+        }
+        if ($pStyle->getAlignment()->getVertical() !== null) {
+            $objWriter->writeAttribute('vertical', $pStyle->getAlignment()->getVertical());
+        }
+
+        if ($pStyle->getAlignment()->getTextRotation() !== null) {
+            $textRotation = 0;
+            if ($pStyle->getAlignment()->getTextRotation() >= 0) {
+                $textRotation = $pStyle->getAlignment()->getTextRotation();
+            } elseif ($pStyle->getAlignment()->getTextRotation() < 0) {
+                $textRotation = 90 - $pStyle->getAlignment()->getTextRotation();
+            }
+            $objWriter->writeAttribute('textRotation', $textRotation);
+        }
+        $objWriter->endElement();
+
+        // border
+        $this->writeBorder($objWriter, $pStyle->getBorders());
+
+        // protection
+        if (($pStyle->getProtection()->getLocked() !== null) || ($pStyle->getProtection()->getHidden() !== null)) {
+            if ($pStyle->getProtection()->getLocked() !== Protection::PROTECTION_INHERIT ||
+                $pStyle->getProtection()->getHidden() !== Protection::PROTECTION_INHERIT) {
+                $objWriter->startElement('protection');
+                if (($pStyle->getProtection()->getLocked() !== null) &&
+                    ($pStyle->getProtection()->getLocked() !== Protection::PROTECTION_INHERIT)) {
+                    $objWriter->writeAttribute('locked', ($pStyle->getProtection()->getLocked() == Protection::PROTECTION_PROTECTED ? 'true' : 'false'));
+                }
+                if (($pStyle->getProtection()->getHidden() !== null) &&
+                    ($pStyle->getProtection()->getHidden() !== Protection::PROTECTION_INHERIT)) {
+                    $objWriter->writeAttribute('hidden', ($pStyle->getProtection()->getHidden() == Protection::PROTECTION_PROTECTED ? 'true' : 'false'));
+                }
+                $objWriter->endElement();
+            }
+        }
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write BorderPr.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param string $pName Element name
+     * @param Border $pBorder Border style
+     */
+    private function writeBorderPr(XMLWriter $objWriter, $pName, Border $pBorder): void
+    {
+        // Write BorderPr
+        if ($pBorder->getBorderStyle() != Border::BORDER_NONE) {
+            $objWriter->startElement($pName);
+            $objWriter->writeAttribute('style', $pBorder->getBorderStyle());
+
+            // color
+            $objWriter->startElement('color');
+            $objWriter->writeAttribute('rgb', $pBorder->getColor()->getARGB());
+            $objWriter->endElement();
+
+            $objWriter->endElement();
+        }
+    }
+
+    /**
+     * Write NumberFormat.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param NumberFormat $pNumberFormat Number Format
+     * @param int $pId Number Format identifier
+     */
+    private function writeNumFmt(XMLWriter $objWriter, NumberFormat $pNumberFormat, $pId = 0): void
+    {
+        // Translate formatcode
+        $formatCode = $pNumberFormat->getFormatCode();
+
+        // numFmt
+        if ($formatCode !== null) {
+            $objWriter->startElement('numFmt');
+            $objWriter->writeAttribute('numFmtId', ($pId + 164));
+            $objWriter->writeAttribute('formatCode', $formatCode);
+            $objWriter->endElement();
+        }
+    }
+
+    /**
+     * Get an array of all styles.
+     *
+     * @return \PhpOffice\PhpSpreadsheet\Style\Style[] All styles in PhpSpreadsheet
+     */
+    public function allStyles(Spreadsheet $spreadsheet)
+    {
+        return $spreadsheet->getCellXfCollection();
+    }
+
+    /**
+     * Get an array of all conditional styles.
+     *
+     * @return Conditional[] All conditional styles in PhpSpreadsheet
+     */
+    public function allConditionalStyles(Spreadsheet $spreadsheet)
+    {
+        // Get an array of all styles
+        $aStyles = [];
+
+        $sheetCount = $spreadsheet->getSheetCount();
+        for ($i = 0; $i < $sheetCount; ++$i) {
+            foreach ($spreadsheet->getSheet($i)->getConditionalStylesCollection() as $conditionalStyles) {
+                foreach ($conditionalStyles as $conditionalStyle) {
+                    $aStyles[] = $conditionalStyle;
+                }
+            }
+        }
+
+        return $aStyles;
+    }
+
+    /**
+     * Get an array of all fills.
+     *
+     * @return Fill[] All fills in PhpSpreadsheet
+     */
+    public function allFills(Spreadsheet $spreadsheet)
+    {
+        // Get an array of unique fills
+        $aFills = [];
+
+        // Two first fills are predefined
+        $fill0 = new Fill();
+        $fill0->setFillType(Fill::FILL_NONE);
+        $aFills[] = $fill0;
+
+        $fill1 = new Fill();
+        $fill1->setFillType(Fill::FILL_PATTERN_GRAY125);
+        $aFills[] = $fill1;
+        // The remaining fills
+        $aStyles = $this->allStyles($spreadsheet);
+        /** @var \PhpOffice\PhpSpreadsheet\Style\Style $style */
+        foreach ($aStyles as $style) {
+            if (!isset($aFills[$style->getFill()->getHashCode()])) {
+                $aFills[$style->getFill()->getHashCode()] = $style->getFill();
+            }
+        }
+
+        return $aFills;
+    }
+
+    /**
+     * Get an array of all fonts.
+     *
+     * @return Font[] All fonts in PhpSpreadsheet
+     */
+    public function allFonts(Spreadsheet $spreadsheet)
+    {
+        // Get an array of unique fonts
+        $aFonts = [];
+        $aStyles = $this->allStyles($spreadsheet);
+
+        /** @var \PhpOffice\PhpSpreadsheet\Style\Style $style */
+        foreach ($aStyles as $style) {
+            if (!isset($aFonts[$style->getFont()->getHashCode()])) {
+                $aFonts[$style->getFont()->getHashCode()] = $style->getFont();
+            }
+        }
+
+        return $aFonts;
+    }
+
+    /**
+     * Get an array of all borders.
+     *
+     * @return Borders[] All borders in PhpSpreadsheet
+     */
+    public function allBorders(Spreadsheet $spreadsheet)
+    {
+        // Get an array of unique borders
+        $aBorders = [];
+        $aStyles = $this->allStyles($spreadsheet);
+
+        /** @var \PhpOffice\PhpSpreadsheet\Style\Style $style */
+        foreach ($aStyles as $style) {
+            if (!isset($aBorders[$style->getBorders()->getHashCode()])) {
+                $aBorders[$style->getBorders()->getHashCode()] = $style->getBorders();
+            }
+        }
+
+        return $aBorders;
+    }
+
+    /**
+     * Get an array of all number formats.
+     *
+     * @return NumberFormat[] All number formats in PhpSpreadsheet
+     */
+    public function allNumberFormats(Spreadsheet $spreadsheet)
+    {
+        // Get an array of unique number formats
+        $aNumFmts = [];
+        $aStyles = $this->allStyles($spreadsheet);
+
+        /** @var \PhpOffice\PhpSpreadsheet\Style\Style $style */
+        foreach ($aStyles as $style) {
+            if ($style->getNumberFormat()->getBuiltInFormatCode() === false && !isset($aNumFmts[$style->getNumberFormat()->getHashCode()])) {
+                $aNumFmts[$style->getNumberFormat()->getHashCode()] = $style->getNumberFormat();
+            }
+        }
+
+        return $aNumFmts;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Theme.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Theme.php
new file mode 100755
index 0000000..3a47be7
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Theme.php
@@ -0,0 +1,837 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+
+class Theme extends WriterPart
+{
+    /**
+     * Map of Major fonts to write.
+     *
+     * @var array of string
+     */
+    private static $majorFonts = [
+        'Jpan' => 'MS Pゴシック',
+        'Hang' => '맑은 고딕',
+        'Hans' => '宋体',
+        'Hant' => '新細明體',
+        'Arab' => 'Times New Roman',
+        'Hebr' => 'Times New Roman',
+        'Thai' => 'Tahoma',
+        'Ethi' => 'Nyala',
+        'Beng' => 'Vrinda',
+        'Gujr' => 'Shruti',
+        'Khmr' => 'MoolBoran',
+        'Knda' => 'Tunga',
+        'Guru' => 'Raavi',
+        'Cans' => 'Euphemia',
+        'Cher' => 'Plantagenet Cherokee',
+        'Yiii' => 'Microsoft Yi Baiti',
+        'Tibt' => 'Microsoft Himalaya',
+        'Thaa' => 'MV Boli',
+        'Deva' => 'Mangal',
+        'Telu' => 'Gautami',
+        'Taml' => 'Latha',
+        'Syrc' => 'Estrangelo Edessa',
+        'Orya' => 'Kalinga',
+        'Mlym' => 'Kartika',
+        'Laoo' => 'DokChampa',
+        'Sinh' => 'Iskoola Pota',
+        'Mong' => 'Mongolian Baiti',
+        'Viet' => 'Times New Roman',
+        'Uigh' => 'Microsoft Uighur',
+        'Geor' => 'Sylfaen',
+    ];
+
+    /**
+     * Map of Minor fonts to write.
+     *
+     * @var array of string
+     */
+    private static $minorFonts = [
+        'Jpan' => 'MS Pゴシック',
+        'Hang' => '맑은 고딕',
+        'Hans' => '宋体',
+        'Hant' => '新細明體',
+        'Arab' => 'Arial',
+        'Hebr' => 'Arial',
+        'Thai' => 'Tahoma',
+        'Ethi' => 'Nyala',
+        'Beng' => 'Vrinda',
+        'Gujr' => 'Shruti',
+        'Khmr' => 'DaunPenh',
+        'Knda' => 'Tunga',
+        'Guru' => 'Raavi',
+        'Cans' => 'Euphemia',
+        'Cher' => 'Plantagenet Cherokee',
+        'Yiii' => 'Microsoft Yi Baiti',
+        'Tibt' => 'Microsoft Himalaya',
+        'Thaa' => 'MV Boli',
+        'Deva' => 'Mangal',
+        'Telu' => 'Gautami',
+        'Taml' => 'Latha',
+        'Syrc' => 'Estrangelo Edessa',
+        'Orya' => 'Kalinga',
+        'Mlym' => 'Kartika',
+        'Laoo' => 'DokChampa',
+        'Sinh' => 'Iskoola Pota',
+        'Mong' => 'Mongolian Baiti',
+        'Viet' => 'Arial',
+        'Uigh' => 'Microsoft Uighur',
+        'Geor' => 'Sylfaen',
+    ];
+
+    /**
+     * Map of core colours.
+     *
+     * @var array of string
+     */
+    private static $colourScheme = [
+        'dk2' => '1F497D',
+        'lt2' => 'EEECE1',
+        'accent1' => '4F81BD',
+        'accent2' => 'C0504D',
+        'accent3' => '9BBB59',
+        'accent4' => '8064A2',
+        'accent5' => '4BACC6',
+        'accent6' => 'F79646',
+        'hlink' => '0000FF',
+        'folHlink' => '800080',
+    ];
+
+    /**
+     * Write theme to XML format.
+     *
+     * @return string XML Output
+     */
+    public function writeTheme(Spreadsheet $spreadsheet)
+    {
+        // Create XML writer
+        $objWriter = null;
+        if ($this->getParentWriter()->getUseDiskCaching()) {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+        } else {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
+        }
+
+        // XML header
+        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+        // a:theme
+        $objWriter->startElement('a:theme');
+        $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main');
+        $objWriter->writeAttribute('name', 'Office Theme');
+
+        // a:themeElements
+        $objWriter->startElement('a:themeElements');
+
+        // a:clrScheme
+        $objWriter->startElement('a:clrScheme');
+        $objWriter->writeAttribute('name', 'Office');
+
+        // a:dk1
+        $objWriter->startElement('a:dk1');
+
+        // a:sysClr
+        $objWriter->startElement('a:sysClr');
+        $objWriter->writeAttribute('val', 'windowText');
+        $objWriter->writeAttribute('lastClr', '000000');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:lt1
+        $objWriter->startElement('a:lt1');
+
+        // a:sysClr
+        $objWriter->startElement('a:sysClr');
+        $objWriter->writeAttribute('val', 'window');
+        $objWriter->writeAttribute('lastClr', 'FFFFFF');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:dk2
+        $this->writeColourScheme($objWriter);
+
+        $objWriter->endElement();
+
+        // a:fontScheme
+        $objWriter->startElement('a:fontScheme');
+        $objWriter->writeAttribute('name', 'Office');
+
+        // a:majorFont
+        $objWriter->startElement('a:majorFont');
+        $this->writeFonts($objWriter, 'Cambria', self::$majorFonts);
+        $objWriter->endElement();
+
+        // a:minorFont
+        $objWriter->startElement('a:minorFont');
+        $this->writeFonts($objWriter, 'Calibri', self::$minorFonts);
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:fmtScheme
+        $objWriter->startElement('a:fmtScheme');
+        $objWriter->writeAttribute('name', 'Office');
+
+        // a:fillStyleLst
+        $objWriter->startElement('a:fillStyleLst');
+
+        // a:solidFill
+        $objWriter->startElement('a:solidFill');
+
+        // a:schemeClr
+        $objWriter->startElement('a:schemeClr');
+        $objWriter->writeAttribute('val', 'phClr');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:gradFill
+        $objWriter->startElement('a:gradFill');
+        $objWriter->writeAttribute('rotWithShape', '1');
+
+        // a:gsLst
+        $objWriter->startElement('a:gsLst');
+
+        // a:gs
+        $objWriter->startElement('a:gs');
+        $objWriter->writeAttribute('pos', '0');
+
+        // a:schemeClr
+        $objWriter->startElement('a:schemeClr');
+        $objWriter->writeAttribute('val', 'phClr');
+
+        // a:tint
+        $objWriter->startElement('a:tint');
+        $objWriter->writeAttribute('val', '50000');
+        $objWriter->endElement();
+
+        // a:satMod
+        $objWriter->startElement('a:satMod');
+        $objWriter->writeAttribute('val', '300000');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:gs
+        $objWriter->startElement('a:gs');
+        $objWriter->writeAttribute('pos', '35000');
+
+        // a:schemeClr
+        $objWriter->startElement('a:schemeClr');
+        $objWriter->writeAttribute('val', 'phClr');
+
+        // a:tint
+        $objWriter->startElement('a:tint');
+        $objWriter->writeAttribute('val', '37000');
+        $objWriter->endElement();
+
+        // a:satMod
+        $objWriter->startElement('a:satMod');
+        $objWriter->writeAttribute('val', '300000');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:gs
+        $objWriter->startElement('a:gs');
+        $objWriter->writeAttribute('pos', '100000');
+
+        // a:schemeClr
+        $objWriter->startElement('a:schemeClr');
+        $objWriter->writeAttribute('val', 'phClr');
+
+        // a:tint
+        $objWriter->startElement('a:tint');
+        $objWriter->writeAttribute('val', '15000');
+        $objWriter->endElement();
+
+        // a:satMod
+        $objWriter->startElement('a:satMod');
+        $objWriter->writeAttribute('val', '350000');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:lin
+        $objWriter->startElement('a:lin');
+        $objWriter->writeAttribute('ang', '16200000');
+        $objWriter->writeAttribute('scaled', '1');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:gradFill
+        $objWriter->startElement('a:gradFill');
+        $objWriter->writeAttribute('rotWithShape', '1');
+
+        // a:gsLst
+        $objWriter->startElement('a:gsLst');
+
+        // a:gs
+        $objWriter->startElement('a:gs');
+        $objWriter->writeAttribute('pos', '0');
+
+        // a:schemeClr
+        $objWriter->startElement('a:schemeClr');
+        $objWriter->writeAttribute('val', 'phClr');
+
+        // a:shade
+        $objWriter->startElement('a:shade');
+        $objWriter->writeAttribute('val', '51000');
+        $objWriter->endElement();
+
+        // a:satMod
+        $objWriter->startElement('a:satMod');
+        $objWriter->writeAttribute('val', '130000');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:gs
+        $objWriter->startElement('a:gs');
+        $objWriter->writeAttribute('pos', '80000');
+
+        // a:schemeClr
+        $objWriter->startElement('a:schemeClr');
+        $objWriter->writeAttribute('val', 'phClr');
+
+        // a:shade
+        $objWriter->startElement('a:shade');
+        $objWriter->writeAttribute('val', '93000');
+        $objWriter->endElement();
+
+        // a:satMod
+        $objWriter->startElement('a:satMod');
+        $objWriter->writeAttribute('val', '130000');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:gs
+        $objWriter->startElement('a:gs');
+        $objWriter->writeAttribute('pos', '100000');
+
+        // a:schemeClr
+        $objWriter->startElement('a:schemeClr');
+        $objWriter->writeAttribute('val', 'phClr');
+
+        // a:shade
+        $objWriter->startElement('a:shade');
+        $objWriter->writeAttribute('val', '94000');
+        $objWriter->endElement();
+
+        // a:satMod
+        $objWriter->startElement('a:satMod');
+        $objWriter->writeAttribute('val', '135000');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:lin
+        $objWriter->startElement('a:lin');
+        $objWriter->writeAttribute('ang', '16200000');
+        $objWriter->writeAttribute('scaled', '0');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:lnStyleLst
+        $objWriter->startElement('a:lnStyleLst');
+
+        // a:ln
+        $objWriter->startElement('a:ln');
+        $objWriter->writeAttribute('w', '9525');
+        $objWriter->writeAttribute('cap', 'flat');
+        $objWriter->writeAttribute('cmpd', 'sng');
+        $objWriter->writeAttribute('algn', 'ctr');
+
+        // a:solidFill
+        $objWriter->startElement('a:solidFill');
+
+        // a:schemeClr
+        $objWriter->startElement('a:schemeClr');
+        $objWriter->writeAttribute('val', 'phClr');
+
+        // a:shade
+        $objWriter->startElement('a:shade');
+        $objWriter->writeAttribute('val', '95000');
+        $objWriter->endElement();
+
+        // a:satMod
+        $objWriter->startElement('a:satMod');
+        $objWriter->writeAttribute('val', '105000');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:prstDash
+        $objWriter->startElement('a:prstDash');
+        $objWriter->writeAttribute('val', 'solid');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:ln
+        $objWriter->startElement('a:ln');
+        $objWriter->writeAttribute('w', '25400');
+        $objWriter->writeAttribute('cap', 'flat');
+        $objWriter->writeAttribute('cmpd', 'sng');
+        $objWriter->writeAttribute('algn', 'ctr');
+
+        // a:solidFill
+        $objWriter->startElement('a:solidFill');
+
+        // a:schemeClr
+        $objWriter->startElement('a:schemeClr');
+        $objWriter->writeAttribute('val', 'phClr');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:prstDash
+        $objWriter->startElement('a:prstDash');
+        $objWriter->writeAttribute('val', 'solid');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:ln
+        $objWriter->startElement('a:ln');
+        $objWriter->writeAttribute('w', '38100');
+        $objWriter->writeAttribute('cap', 'flat');
+        $objWriter->writeAttribute('cmpd', 'sng');
+        $objWriter->writeAttribute('algn', 'ctr');
+
+        // a:solidFill
+        $objWriter->startElement('a:solidFill');
+
+        // a:schemeClr
+        $objWriter->startElement('a:schemeClr');
+        $objWriter->writeAttribute('val', 'phClr');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:prstDash
+        $objWriter->startElement('a:prstDash');
+        $objWriter->writeAttribute('val', 'solid');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:effectStyleLst
+        $objWriter->startElement('a:effectStyleLst');
+
+        // a:effectStyle
+        $objWriter->startElement('a:effectStyle');
+
+        // a:effectLst
+        $objWriter->startElement('a:effectLst');
+
+        // a:outerShdw
+        $objWriter->startElement('a:outerShdw');
+        $objWriter->writeAttribute('blurRad', '40000');
+        $objWriter->writeAttribute('dist', '20000');
+        $objWriter->writeAttribute('dir', '5400000');
+        $objWriter->writeAttribute('rotWithShape', '0');
+
+        // a:srgbClr
+        $objWriter->startElement('a:srgbClr');
+        $objWriter->writeAttribute('val', '000000');
+
+        // a:alpha
+        $objWriter->startElement('a:alpha');
+        $objWriter->writeAttribute('val', '38000');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:effectStyle
+        $objWriter->startElement('a:effectStyle');
+
+        // a:effectLst
+        $objWriter->startElement('a:effectLst');
+
+        // a:outerShdw
+        $objWriter->startElement('a:outerShdw');
+        $objWriter->writeAttribute('blurRad', '40000');
+        $objWriter->writeAttribute('dist', '23000');
+        $objWriter->writeAttribute('dir', '5400000');
+        $objWriter->writeAttribute('rotWithShape', '0');
+
+        // a:srgbClr
+        $objWriter->startElement('a:srgbClr');
+        $objWriter->writeAttribute('val', '000000');
+
+        // a:alpha
+        $objWriter->startElement('a:alpha');
+        $objWriter->writeAttribute('val', '35000');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:effectStyle
+        $objWriter->startElement('a:effectStyle');
+
+        // a:effectLst
+        $objWriter->startElement('a:effectLst');
+
+        // a:outerShdw
+        $objWriter->startElement('a:outerShdw');
+        $objWriter->writeAttribute('blurRad', '40000');
+        $objWriter->writeAttribute('dist', '23000');
+        $objWriter->writeAttribute('dir', '5400000');
+        $objWriter->writeAttribute('rotWithShape', '0');
+
+        // a:srgbClr
+        $objWriter->startElement('a:srgbClr');
+        $objWriter->writeAttribute('val', '000000');
+
+        // a:alpha
+        $objWriter->startElement('a:alpha');
+        $objWriter->writeAttribute('val', '35000');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:scene3d
+        $objWriter->startElement('a:scene3d');
+
+        // a:camera
+        $objWriter->startElement('a:camera');
+        $objWriter->writeAttribute('prst', 'orthographicFront');
+
+        // a:rot
+        $objWriter->startElement('a:rot');
+        $objWriter->writeAttribute('lat', '0');
+        $objWriter->writeAttribute('lon', '0');
+        $objWriter->writeAttribute('rev', '0');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:lightRig
+        $objWriter->startElement('a:lightRig');
+        $objWriter->writeAttribute('rig', 'threePt');
+        $objWriter->writeAttribute('dir', 't');
+
+        // a:rot
+        $objWriter->startElement('a:rot');
+        $objWriter->writeAttribute('lat', '0');
+        $objWriter->writeAttribute('lon', '0');
+        $objWriter->writeAttribute('rev', '1200000');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:sp3d
+        $objWriter->startElement('a:sp3d');
+
+        // a:bevelT
+        $objWriter->startElement('a:bevelT');
+        $objWriter->writeAttribute('w', '63500');
+        $objWriter->writeAttribute('h', '25400');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:bgFillStyleLst
+        $objWriter->startElement('a:bgFillStyleLst');
+
+        // a:solidFill
+        $objWriter->startElement('a:solidFill');
+
+        // a:schemeClr
+        $objWriter->startElement('a:schemeClr');
+        $objWriter->writeAttribute('val', 'phClr');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:gradFill
+        $objWriter->startElement('a:gradFill');
+        $objWriter->writeAttribute('rotWithShape', '1');
+
+        // a:gsLst
+        $objWriter->startElement('a:gsLst');
+
+        // a:gs
+        $objWriter->startElement('a:gs');
+        $objWriter->writeAttribute('pos', '0');
+
+        // a:schemeClr
+        $objWriter->startElement('a:schemeClr');
+        $objWriter->writeAttribute('val', 'phClr');
+
+        // a:tint
+        $objWriter->startElement('a:tint');
+        $objWriter->writeAttribute('val', '40000');
+        $objWriter->endElement();
+
+        // a:satMod
+        $objWriter->startElement('a:satMod');
+        $objWriter->writeAttribute('val', '350000');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:gs
+        $objWriter->startElement('a:gs');
+        $objWriter->writeAttribute('pos', '40000');
+
+        // a:schemeClr
+        $objWriter->startElement('a:schemeClr');
+        $objWriter->writeAttribute('val', 'phClr');
+
+        // a:tint
+        $objWriter->startElement('a:tint');
+        $objWriter->writeAttribute('val', '45000');
+        $objWriter->endElement();
+
+        // a:shade
+        $objWriter->startElement('a:shade');
+        $objWriter->writeAttribute('val', '99000');
+        $objWriter->endElement();
+
+        // a:satMod
+        $objWriter->startElement('a:satMod');
+        $objWriter->writeAttribute('val', '350000');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:gs
+        $objWriter->startElement('a:gs');
+        $objWriter->writeAttribute('pos', '100000');
+
+        // a:schemeClr
+        $objWriter->startElement('a:schemeClr');
+        $objWriter->writeAttribute('val', 'phClr');
+
+        // a:shade
+        $objWriter->startElement('a:shade');
+        $objWriter->writeAttribute('val', '20000');
+        $objWriter->endElement();
+
+        // a:satMod
+        $objWriter->startElement('a:satMod');
+        $objWriter->writeAttribute('val', '255000');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:path
+        $objWriter->startElement('a:path');
+        $objWriter->writeAttribute('path', 'circle');
+
+        // a:fillToRect
+        $objWriter->startElement('a:fillToRect');
+        $objWriter->writeAttribute('l', '50000');
+        $objWriter->writeAttribute('t', '-80000');
+        $objWriter->writeAttribute('r', '50000');
+        $objWriter->writeAttribute('b', '180000');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:gradFill
+        $objWriter->startElement('a:gradFill');
+        $objWriter->writeAttribute('rotWithShape', '1');
+
+        // a:gsLst
+        $objWriter->startElement('a:gsLst');
+
+        // a:gs
+        $objWriter->startElement('a:gs');
+        $objWriter->writeAttribute('pos', '0');
+
+        // a:schemeClr
+        $objWriter->startElement('a:schemeClr');
+        $objWriter->writeAttribute('val', 'phClr');
+
+        // a:tint
+        $objWriter->startElement('a:tint');
+        $objWriter->writeAttribute('val', '80000');
+        $objWriter->endElement();
+
+        // a:satMod
+        $objWriter->startElement('a:satMod');
+        $objWriter->writeAttribute('val', '300000');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:gs
+        $objWriter->startElement('a:gs');
+        $objWriter->writeAttribute('pos', '100000');
+
+        // a:schemeClr
+        $objWriter->startElement('a:schemeClr');
+        $objWriter->writeAttribute('val', 'phClr');
+
+        // a:shade
+        $objWriter->startElement('a:shade');
+        $objWriter->writeAttribute('val', '30000');
+        $objWriter->endElement();
+
+        // a:satMod
+        $objWriter->startElement('a:satMod');
+        $objWriter->writeAttribute('val', '200000');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:path
+        $objWriter->startElement('a:path');
+        $objWriter->writeAttribute('path', 'circle');
+
+        // a:fillToRect
+        $objWriter->startElement('a:fillToRect');
+        $objWriter->writeAttribute('l', '50000');
+        $objWriter->writeAttribute('t', '50000');
+        $objWriter->writeAttribute('r', '50000');
+        $objWriter->writeAttribute('b', '50000');
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        // a:objectDefaults
+        $objWriter->writeElement('a:objectDefaults', null);
+
+        // a:extraClrSchemeLst
+        $objWriter->writeElement('a:extraClrSchemeLst', null);
+
+        $objWriter->endElement();
+
+        // Return
+        return $objWriter->getData();
+    }
+
+    /**
+     * Write fonts to XML format.
+     *
+     * @param XMLWriter $objWriter
+     * @param string $latinFont
+     * @param array of string                $fontSet
+     *
+     * @return string XML Output
+     */
+    private function writeFonts($objWriter, $latinFont, $fontSet)
+    {
+        // a:latin
+        $objWriter->startElement('a:latin');
+        $objWriter->writeAttribute('typeface', $latinFont);
+        $objWriter->endElement();
+
+        // a:ea
+        $objWriter->startElement('a:ea');
+        $objWriter->writeAttribute('typeface', '');
+        $objWriter->endElement();
+
+        // a:cs
+        $objWriter->startElement('a:cs');
+        $objWriter->writeAttribute('typeface', '');
+        $objWriter->endElement();
+
+        foreach ($fontSet as $fontScript => $typeface) {
+            $objWriter->startElement('a:font');
+            $objWriter->writeAttribute('script', $fontScript);
+            $objWriter->writeAttribute('typeface', $typeface);
+            $objWriter->endElement();
+        }
+    }
+
+    /**
+     * Write colour scheme to XML format.
+     *
+     * @param XMLWriter $objWriter
+     *
+     * @return string XML Output
+     */
+    private function writeColourScheme($objWriter)
+    {
+        foreach (self::$colourScheme as $colourName => $colourValue) {
+            $objWriter->startElement('a:' . $colourName);
+
+            $objWriter->startElement('a:srgbClr');
+            $objWriter->writeAttribute('val', $colourValue);
+            $objWriter->endElement();
+
+            $objWriter->endElement();
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php
new file mode 100755
index 0000000..88befd0
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php
@@ -0,0 +1,406 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\NamedRange;
+use PhpOffice\PhpSpreadsheet\Shared\Date;
+use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
+
+class Workbook extends WriterPart
+{
+    /**
+     * Write workbook to XML format.
+     *
+     * @param bool $recalcRequired Indicate whether formulas should be recalculated before writing
+     *
+     * @return string XML Output
+     */
+    public function writeWorkbook(Spreadsheet $spreadsheet, $recalcRequired = false)
+    {
+        // Create XML writer
+        if ($this->getParentWriter()->getUseDiskCaching()) {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+        } else {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
+        }
+
+        // XML header
+        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+        // workbook
+        $objWriter->startElement('workbook');
+        $objWriter->writeAttribute('xml:space', 'preserve');
+        $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
+        $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
+
+        // fileVersion
+        $this->writeFileVersion($objWriter);
+
+        // workbookPr
+        $this->writeWorkbookPr($objWriter);
+
+        // workbookProtection
+        $this->writeWorkbookProtection($objWriter, $spreadsheet);
+
+        // bookViews
+        if ($this->getParentWriter()->getOffice2003Compatibility() === false) {
+            $this->writeBookViews($objWriter, $spreadsheet);
+        }
+
+        // sheets
+        $this->writeSheets($objWriter, $spreadsheet);
+
+        // definedNames
+        $this->writeDefinedNames($objWriter, $spreadsheet);
+
+        // calcPr
+        $this->writeCalcPr($objWriter, $recalcRequired);
+
+        $objWriter->endElement();
+
+        // Return
+        return $objWriter->getData();
+    }
+
+    /**
+     * Write file version.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     */
+    private function writeFileVersion(XMLWriter $objWriter): void
+    {
+        $objWriter->startElement('fileVersion');
+        $objWriter->writeAttribute('appName', 'xl');
+        $objWriter->writeAttribute('lastEdited', '4');
+        $objWriter->writeAttribute('lowestEdited', '4');
+        $objWriter->writeAttribute('rupBuild', '4505');
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write WorkbookPr.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     */
+    private function writeWorkbookPr(XMLWriter $objWriter): void
+    {
+        $objWriter->startElement('workbookPr');
+
+        if (Date::getExcelCalendar() === Date::CALENDAR_MAC_1904) {
+            $objWriter->writeAttribute('date1904', '1');
+        }
+
+        $objWriter->writeAttribute('codeName', 'ThisWorkbook');
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write BookViews.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     */
+    private function writeBookViews(XMLWriter $objWriter, Spreadsheet $spreadsheet): void
+    {
+        // bookViews
+        $objWriter->startElement('bookViews');
+
+        // workbookView
+        $objWriter->startElement('workbookView');
+
+        $objWriter->writeAttribute('activeTab', $spreadsheet->getActiveSheetIndex());
+        $objWriter->writeAttribute('autoFilterDateGrouping', ($spreadsheet->getAutoFilterDateGrouping() ? 'true' : 'false'));
+        $objWriter->writeAttribute('firstSheet', $spreadsheet->getFirstSheetIndex());
+        $objWriter->writeAttribute('minimized', ($spreadsheet->getMinimized() ? 'true' : 'false'));
+        $objWriter->writeAttribute('showHorizontalScroll', ($spreadsheet->getShowHorizontalScroll() ? 'true' : 'false'));
+        $objWriter->writeAttribute('showSheetTabs', ($spreadsheet->getShowSheetTabs() ? 'true' : 'false'));
+        $objWriter->writeAttribute('showVerticalScroll', ($spreadsheet->getShowVerticalScroll() ? 'true' : 'false'));
+        $objWriter->writeAttribute('tabRatio', $spreadsheet->getTabRatio());
+        $objWriter->writeAttribute('visibility', $spreadsheet->getVisibility());
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write WorkbookProtection.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     */
+    private function writeWorkbookProtection(XMLWriter $objWriter, Spreadsheet $spreadsheet): void
+    {
+        if ($spreadsheet->getSecurity()->isSecurityEnabled()) {
+            $objWriter->startElement('workbookProtection');
+            $objWriter->writeAttribute('lockRevision', ($spreadsheet->getSecurity()->getLockRevision() ? 'true' : 'false'));
+            $objWriter->writeAttribute('lockStructure', ($spreadsheet->getSecurity()->getLockStructure() ? 'true' : 'false'));
+            $objWriter->writeAttribute('lockWindows', ($spreadsheet->getSecurity()->getLockWindows() ? 'true' : 'false'));
+
+            if ($spreadsheet->getSecurity()->getRevisionsPassword() != '') {
+                $objWriter->writeAttribute('revisionsPassword', $spreadsheet->getSecurity()->getRevisionsPassword());
+            }
+
+            if ($spreadsheet->getSecurity()->getWorkbookPassword() != '') {
+                $objWriter->writeAttribute('workbookPassword', $spreadsheet->getSecurity()->getWorkbookPassword());
+            }
+
+            $objWriter->endElement();
+        }
+    }
+
+    /**
+     * Write calcPr.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param bool $recalcRequired Indicate whether formulas should be recalculated before writing
+     */
+    private function writeCalcPr(XMLWriter $objWriter, $recalcRequired = true): void
+    {
+        $objWriter->startElement('calcPr');
+
+        //    Set the calcid to a higher value than Excel itself will use, otherwise Excel will always recalc
+        //  If MS Excel does do a recalc, then users opening a file in MS Excel will be prompted to save on exit
+        //     because the file has changed
+        $objWriter->writeAttribute('calcId', '999999');
+        $objWriter->writeAttribute('calcMode', 'auto');
+        //    fullCalcOnLoad isn't needed if we've recalculating for the save
+        $objWriter->writeAttribute('calcCompleted', ($recalcRequired) ? 1 : 0);
+        $objWriter->writeAttribute('fullCalcOnLoad', ($recalcRequired) ? 0 : 1);
+        $objWriter->writeAttribute('forceFullCalc', ($recalcRequired) ? 0 : 1);
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write sheets.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     */
+    private function writeSheets(XMLWriter $objWriter, Spreadsheet $spreadsheet): void
+    {
+        // Write sheets
+        $objWriter->startElement('sheets');
+        $sheetCount = $spreadsheet->getSheetCount();
+        for ($i = 0; $i < $sheetCount; ++$i) {
+            // sheet
+            $this->writeSheet(
+                $objWriter,
+                $spreadsheet->getSheet($i)->getTitle(),
+                ($i + 1),
+                ($i + 1 + 3),
+                $spreadsheet->getSheet($i)->getSheetState()
+            );
+        }
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write sheet.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param string $pSheetname Sheet name
+     * @param int $pSheetId Sheet id
+     * @param int $pRelId Relationship ID
+     * @param string $sheetState Sheet state (visible, hidden, veryHidden)
+     */
+    private function writeSheet(XMLWriter $objWriter, $pSheetname, $pSheetId = 1, $pRelId = 1, $sheetState = 'visible'): void
+    {
+        if ($pSheetname != '') {
+            // Write sheet
+            $objWriter->startElement('sheet');
+            $objWriter->writeAttribute('name', $pSheetname);
+            $objWriter->writeAttribute('sheetId', $pSheetId);
+            if ($sheetState !== 'visible' && $sheetState != '') {
+                $objWriter->writeAttribute('state', $sheetState);
+            }
+            $objWriter->writeAttribute('r:id', 'rId' . $pRelId);
+            $objWriter->endElement();
+        } else {
+            throw new WriterException('Invalid parameters passed.');
+        }
+    }
+
+    /**
+     * Write Defined Names.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     */
+    private function writeDefinedNames(XMLWriter $objWriter, Spreadsheet $spreadsheet): void
+    {
+        // Write defined names
+        $objWriter->startElement('definedNames');
+
+        // Named ranges
+        if (count($spreadsheet->getNamedRanges()) > 0) {
+            // Named ranges
+            $this->writeNamedRanges($objWriter, $spreadsheet);
+        }
+
+        // Other defined names
+        $sheetCount = $spreadsheet->getSheetCount();
+        for ($i = 0; $i < $sheetCount; ++$i) {
+            // definedName for autoFilter
+            $this->writeDefinedNameForAutofilter($objWriter, $spreadsheet->getSheet($i), $i);
+
+            // definedName for Print_Titles
+            $this->writeDefinedNameForPrintTitles($objWriter, $spreadsheet->getSheet($i), $i);
+
+            // definedName for Print_Area
+            $this->writeDefinedNameForPrintArea($objWriter, $spreadsheet->getSheet($i), $i);
+        }
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write named ranges.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     */
+    private function writeNamedRanges(XMLWriter $objWriter, Spreadsheet $spreadsheet): void
+    {
+        // Loop named ranges
+        $namedRanges = $spreadsheet->getNamedRanges();
+        foreach ($namedRanges as $namedRange) {
+            $this->writeDefinedNameForNamedRange($objWriter, $namedRange);
+        }
+    }
+
+    /**
+     * Write Defined Name for named range.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     */
+    private function writeDefinedNameForNamedRange(XMLWriter $objWriter, NamedRange $pNamedRange): void
+    {
+        // definedName for named range
+        $objWriter->startElement('definedName');
+        $objWriter->writeAttribute('name', $pNamedRange->getName());
+        if ($pNamedRange->getLocalOnly()) {
+            $objWriter->writeAttribute('localSheetId', $pNamedRange->getScope()->getParent()->getIndex($pNamedRange->getScope()));
+        }
+
+        // Create absolute coordinate and write as raw text
+        $range = Coordinate::splitRange($pNamedRange->getRange());
+        $iMax = count($range);
+        for ($i = 0; $i < $iMax; ++$i) {
+            $range[$i][0] = '\'' . str_replace("'", "''", $pNamedRange->getWorksheet()->getTitle()) . '\'!' . Coordinate::absoluteReference($range[$i][0]);
+            if (isset($range[$i][1])) {
+                $range[$i][1] = Coordinate::absoluteReference($range[$i][1]);
+            }
+        }
+        $range = Coordinate::buildRange($range);
+
+        $objWriter->writeRawData($range);
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write Defined Name for autoFilter.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param int $pSheetId
+     */
+    private function writeDefinedNameForAutofilter(XMLWriter $objWriter, Worksheet $pSheet, $pSheetId = 0): void
+    {
+        // definedName for autoFilter
+        $autoFilterRange = $pSheet->getAutoFilter()->getRange();
+        if (!empty($autoFilterRange)) {
+            $objWriter->startElement('definedName');
+            $objWriter->writeAttribute('name', '_xlnm._FilterDatabase');
+            $objWriter->writeAttribute('localSheetId', $pSheetId);
+            $objWriter->writeAttribute('hidden', '1');
+
+            // Create absolute coordinate and write as raw text
+            $range = Coordinate::splitRange($autoFilterRange);
+            $range = $range[0];
+            //    Strip any worksheet ref so we can make the cell ref absolute
+            [$ws, $range[0]] = Worksheet::extractSheetTitle($range[0], true);
+
+            $range[0] = Coordinate::absoluteCoordinate($range[0]);
+            $range[1] = Coordinate::absoluteCoordinate($range[1]);
+            $range = implode(':', $range);
+
+            $objWriter->writeRawData('\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!' . $range);
+
+            $objWriter->endElement();
+        }
+    }
+
+    /**
+     * Write Defined Name for PrintTitles.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param int $pSheetId
+     */
+    private function writeDefinedNameForPrintTitles(XMLWriter $objWriter, Worksheet $pSheet, $pSheetId = 0): void
+    {
+        // definedName for PrintTitles
+        if ($pSheet->getPageSetup()->isColumnsToRepeatAtLeftSet() || $pSheet->getPageSetup()->isRowsToRepeatAtTopSet()) {
+            $objWriter->startElement('definedName');
+            $objWriter->writeAttribute('name', '_xlnm.Print_Titles');
+            $objWriter->writeAttribute('localSheetId', $pSheetId);
+
+            // Setting string
+            $settingString = '';
+
+            // Columns to repeat
+            if ($pSheet->getPageSetup()->isColumnsToRepeatAtLeftSet()) {
+                $repeat = $pSheet->getPageSetup()->getColumnsToRepeatAtLeft();
+
+                $settingString .= '\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!$' . $repeat[0] . ':$' . $repeat[1];
+            }
+
+            // Rows to repeat
+            if ($pSheet->getPageSetup()->isRowsToRepeatAtTopSet()) {
+                if ($pSheet->getPageSetup()->isColumnsToRepeatAtLeftSet()) {
+                    $settingString .= ',';
+                }
+
+                $repeat = $pSheet->getPageSetup()->getRowsToRepeatAtTop();
+
+                $settingString .= '\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!$' . $repeat[0] . ':$' . $repeat[1];
+            }
+
+            $objWriter->writeRawData($settingString);
+
+            $objWriter->endElement();
+        }
+    }
+
+    /**
+     * Write Defined Name for PrintTitles.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param int $pSheetId
+     */
+    private function writeDefinedNameForPrintArea(XMLWriter $objWriter, Worksheet $pSheet, $pSheetId = 0): void
+    {
+        // definedName for PrintArea
+        if ($pSheet->getPageSetup()->isPrintAreaSet()) {
+            $objWriter->startElement('definedName');
+            $objWriter->writeAttribute('name', '_xlnm.Print_Area');
+            $objWriter->writeAttribute('localSheetId', $pSheetId);
+
+            // Print area
+            $printArea = Coordinate::splitRange($pSheet->getPageSetup()->getPrintArea());
+
+            $chunks = [];
+            foreach ($printArea as $printAreaRect) {
+                $printAreaRect[0] = Coordinate::absoluteReference($printAreaRect[0]);
+                $printAreaRect[1] = Coordinate::absoluteReference($printAreaRect[1]);
+                $chunks[] = '\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!' . implode(':', $printAreaRect);
+            }
+
+            $objWriter->writeRawData(implode(',', $chunks));
+
+            $objWriter->endElement();
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php
new file mode 100755
index 0000000..d101bb4
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php
@@ -0,0 +1,1275 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Cell\Cell;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\RichText\RichText;
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
+use PhpOffice\PhpSpreadsheet\Style\Conditional;
+use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
+use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule;
+use PhpOffice\PhpSpreadsheet\Worksheet\SheetView;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet as PhpspreadsheetWorksheet;
+
+class Worksheet extends WriterPart
+{
+    /**
+     * Write worksheet to XML format.
+     *
+     * @param string[] $pStringTable
+     * @param bool $includeCharts Flag indicating if we should write charts
+     *
+     * @return string XML Output
+     */
+    public function writeWorksheet(PhpspreadsheetWorksheet $pSheet, $pStringTable = null, $includeCharts = false)
+    {
+        // Create XML writer
+        $objWriter = null;
+        if ($this->getParentWriter()->getUseDiskCaching()) {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+        } else {
+            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
+        }
+
+        // XML header
+        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+        // Worksheet
+        $objWriter->startElement('worksheet');
+        $objWriter->writeAttribute('xml:space', 'preserve');
+        $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
+        $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
+
+        $objWriter->writeAttribute('xmlns:xdr', 'http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing');
+        $objWriter->writeAttribute('xmlns:x14', 'http://schemas.microsoft.com/office/spreadsheetml/2009/9/main');
+        $objWriter->writeAttribute('xmlns:mc', 'http://schemas.openxmlformats.org/markup-compatibility/2006');
+        $objWriter->writeAttribute('mc:Ignorable', 'x14ac');
+        $objWriter->writeAttribute('xmlns:x14ac', 'http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac');
+
+        // sheetPr
+        $this->writeSheetPr($objWriter, $pSheet);
+
+        // Dimension
+        $this->writeDimension($objWriter, $pSheet);
+
+        // sheetViews
+        $this->writeSheetViews($objWriter, $pSheet);
+
+        // sheetFormatPr
+        $this->writeSheetFormatPr($objWriter, $pSheet);
+
+        // cols
+        $this->writeCols($objWriter, $pSheet);
+
+        // sheetData
+        $this->writeSheetData($objWriter, $pSheet, $pStringTable);
+
+        // sheetProtection
+        $this->writeSheetProtection($objWriter, $pSheet);
+
+        // protectedRanges
+        $this->writeProtectedRanges($objWriter, $pSheet);
+
+        // autoFilter
+        $this->writeAutoFilter($objWriter, $pSheet);
+
+        // mergeCells
+        $this->writeMergeCells($objWriter, $pSheet);
+
+        // conditionalFormatting
+        $this->writeConditionalFormatting($objWriter, $pSheet);
+
+        // dataValidations
+        $this->writeDataValidations($objWriter, $pSheet);
+
+        // hyperlinks
+        $this->writeHyperlinks($objWriter, $pSheet);
+
+        // Print options
+        $this->writePrintOptions($objWriter, $pSheet);
+
+        // Page margins
+        $this->writePageMargins($objWriter, $pSheet);
+
+        // Page setup
+        $this->writePageSetup($objWriter, $pSheet);
+
+        // Header / footer
+        $this->writeHeaderFooter($objWriter, $pSheet);
+
+        // Breaks
+        $this->writeBreaks($objWriter, $pSheet);
+
+        // Drawings and/or Charts
+        $this->writeDrawings($objWriter, $pSheet, $includeCharts);
+
+        // LegacyDrawing
+        $this->writeLegacyDrawing($objWriter, $pSheet);
+
+        // LegacyDrawingHF
+        $this->writeLegacyDrawingHF($objWriter, $pSheet);
+
+        // AlternateContent
+        $this->writeAlternateContent($objWriter, $pSheet);
+
+        $objWriter->endElement();
+
+        // Return
+        return $objWriter->getData();
+    }
+
+    /**
+     * Write SheetPr.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param PhpspreadsheetWorksheet $pSheet Worksheet
+     */
+    private function writeSheetPr(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void
+    {
+        // sheetPr
+        $objWriter->startElement('sheetPr');
+        if ($pSheet->getParent()->hasMacros()) {
+            //if the workbook have macros, we need to have codeName for the sheet
+            if (!$pSheet->hasCodeName()) {
+                $pSheet->setCodeName($pSheet->getTitle());
+            }
+            $objWriter->writeAttribute('codeName', $pSheet->getCodeName());
+        }
+        $autoFilterRange = $pSheet->getAutoFilter()->getRange();
+        if (!empty($autoFilterRange)) {
+            $objWriter->writeAttribute('filterMode', 1);
+            $pSheet->getAutoFilter()->showHideRows();
+        }
+
+        // tabColor
+        if ($pSheet->isTabColorSet()) {
+            $objWriter->startElement('tabColor');
+            $objWriter->writeAttribute('rgb', $pSheet->getTabColor()->getARGB());
+            $objWriter->endElement();
+        }
+
+        // outlinePr
+        $objWriter->startElement('outlinePr');
+        $objWriter->writeAttribute('summaryBelow', ($pSheet->getShowSummaryBelow() ? '1' : '0'));
+        $objWriter->writeAttribute('summaryRight', ($pSheet->getShowSummaryRight() ? '1' : '0'));
+        $objWriter->endElement();
+
+        // pageSetUpPr
+        if ($pSheet->getPageSetup()->getFitToPage()) {
+            $objWriter->startElement('pageSetUpPr');
+            $objWriter->writeAttribute('fitToPage', '1');
+            $objWriter->endElement();
+        }
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write Dimension.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param PhpspreadsheetWorksheet $pSheet Worksheet
+     */
+    private function writeDimension(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void
+    {
+        // dimension
+        $objWriter->startElement('dimension');
+        $objWriter->writeAttribute('ref', $pSheet->calculateWorksheetDimension());
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write SheetViews.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param PhpspreadsheetWorksheet $pSheet Worksheet
+     */
+    private function writeSheetViews(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void
+    {
+        // sheetViews
+        $objWriter->startElement('sheetViews');
+
+        // Sheet selected?
+        $sheetSelected = false;
+        if ($this->getParentWriter()->getSpreadsheet()->getIndex($pSheet) == $this->getParentWriter()->getSpreadsheet()->getActiveSheetIndex()) {
+            $sheetSelected = true;
+        }
+
+        // sheetView
+        $objWriter->startElement('sheetView');
+        $objWriter->writeAttribute('tabSelected', $sheetSelected ? '1' : '0');
+        $objWriter->writeAttribute('workbookViewId', '0');
+
+        // Zoom scales
+        if ($pSheet->getSheetView()->getZoomScale() != 100) {
+            $objWriter->writeAttribute('zoomScale', $pSheet->getSheetView()->getZoomScale());
+        }
+        if ($pSheet->getSheetView()->getZoomScaleNormal() != 100) {
+            $objWriter->writeAttribute('zoomScaleNormal', $pSheet->getSheetView()->getZoomScaleNormal());
+        }
+
+        // Show zeros (Excel also writes this attribute only if set to false)
+        if ($pSheet->getSheetView()->getShowZeros() === false) {
+            $objWriter->writeAttribute('showZeros', 0);
+        }
+
+        // View Layout Type
+        if ($pSheet->getSheetView()->getView() !== SheetView::SHEETVIEW_NORMAL) {
+            $objWriter->writeAttribute('view', $pSheet->getSheetView()->getView());
+        }
+
+        // Gridlines
+        if ($pSheet->getShowGridlines()) {
+            $objWriter->writeAttribute('showGridLines', 'true');
+        } else {
+            $objWriter->writeAttribute('showGridLines', 'false');
+        }
+
+        // Row and column headers
+        if ($pSheet->getShowRowColHeaders()) {
+            $objWriter->writeAttribute('showRowColHeaders', '1');
+        } else {
+            $objWriter->writeAttribute('showRowColHeaders', '0');
+        }
+
+        // Right-to-left
+        if ($pSheet->getRightToLeft()) {
+            $objWriter->writeAttribute('rightToLeft', 'true');
+        }
+
+        $activeCell = $pSheet->getActiveCell();
+        $sqref = $pSheet->getSelectedCells();
+
+        // Pane
+        $pane = '';
+        if ($pSheet->getFreezePane()) {
+            [$xSplit, $ySplit] = Coordinate::coordinateFromString($pSheet->getFreezePane());
+            $xSplit = Coordinate::columnIndexFromString($xSplit);
+            --$xSplit;
+            --$ySplit;
+
+            $topLeftCell = $pSheet->getTopLeftCell();
+
+            // pane
+            $pane = 'topRight';
+            $objWriter->startElement('pane');
+            if ($xSplit > 0) {
+                $objWriter->writeAttribute('xSplit', $xSplit);
+            }
+            if ($ySplit > 0) {
+                $objWriter->writeAttribute('ySplit', $ySplit);
+                $pane = ($xSplit > 0) ? 'bottomRight' : 'bottomLeft';
+            }
+            $objWriter->writeAttribute('topLeftCell', $topLeftCell);
+            $objWriter->writeAttribute('activePane', $pane);
+            $objWriter->writeAttribute('state', 'frozen');
+            $objWriter->endElement();
+
+            if (($xSplit > 0) && ($ySplit > 0)) {
+                //    Write additional selections if more than two panes (ie both an X and a Y split)
+                $objWriter->startElement('selection');
+                $objWriter->writeAttribute('pane', 'topRight');
+                $objWriter->endElement();
+                $objWriter->startElement('selection');
+                $objWriter->writeAttribute('pane', 'bottomLeft');
+                $objWriter->endElement();
+            }
+        }
+
+        // Selection
+        // Only need to write selection element if we have a split pane
+        // We cheat a little by over-riding the active cell selection, setting it to the split cell
+        $objWriter->startElement('selection');
+        if ($pane != '') {
+            $objWriter->writeAttribute('pane', $pane);
+        }
+        $objWriter->writeAttribute('activeCell', $activeCell);
+        $objWriter->writeAttribute('sqref', $sqref);
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write SheetFormatPr.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param PhpspreadsheetWorksheet $pSheet Worksheet
+     */
+    private function writeSheetFormatPr(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void
+    {
+        // sheetFormatPr
+        $objWriter->startElement('sheetFormatPr');
+
+        // Default row height
+        if ($pSheet->getDefaultRowDimension()->getRowHeight() >= 0) {
+            $objWriter->writeAttribute('customHeight', 'true');
+            $objWriter->writeAttribute('defaultRowHeight', StringHelper::formatNumber($pSheet->getDefaultRowDimension()->getRowHeight()));
+        } else {
+            $objWriter->writeAttribute('defaultRowHeight', '14.4');
+        }
+
+        // Set Zero Height row
+        if ((string) $pSheet->getDefaultRowDimension()->getZeroHeight() === '1' ||
+            strtolower((string) $pSheet->getDefaultRowDimension()->getZeroHeight()) == 'true') {
+            $objWriter->writeAttribute('zeroHeight', '1');
+        }
+
+        // Default column width
+        if ($pSheet->getDefaultColumnDimension()->getWidth() >= 0) {
+            $objWriter->writeAttribute('defaultColWidth', StringHelper::formatNumber($pSheet->getDefaultColumnDimension()->getWidth()));
+        }
+
+        // Outline level - row
+        $outlineLevelRow = 0;
+        foreach ($pSheet->getRowDimensions() as $dimension) {
+            if ($dimension->getOutlineLevel() > $outlineLevelRow) {
+                $outlineLevelRow = $dimension->getOutlineLevel();
+            }
+        }
+        $objWriter->writeAttribute('outlineLevelRow', (int) $outlineLevelRow);
+
+        // Outline level - column
+        $outlineLevelCol = 0;
+        foreach ($pSheet->getColumnDimensions() as $dimension) {
+            if ($dimension->getOutlineLevel() > $outlineLevelCol) {
+                $outlineLevelCol = $dimension->getOutlineLevel();
+            }
+        }
+        $objWriter->writeAttribute('outlineLevelCol', (int) $outlineLevelCol);
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write Cols.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param PhpspreadsheetWorksheet $pSheet Worksheet
+     */
+    private function writeCols(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void
+    {
+        // cols
+        if (count($pSheet->getColumnDimensions()) > 0) {
+            $objWriter->startElement('cols');
+
+            $pSheet->calculateColumnWidths();
+
+            // Loop through column dimensions
+            foreach ($pSheet->getColumnDimensions() as $colDimension) {
+                // col
+                $objWriter->startElement('col');
+                $objWriter->writeAttribute('min', Coordinate::columnIndexFromString($colDimension->getColumnIndex()));
+                $objWriter->writeAttribute('max', Coordinate::columnIndexFromString($colDimension->getColumnIndex()));
+
+                if ($colDimension->getWidth() < 0) {
+                    // No width set, apply default of 10
+                    $objWriter->writeAttribute('width', '9.10');
+                } else {
+                    // Width set
+                    $objWriter->writeAttribute('width', StringHelper::formatNumber($colDimension->getWidth()));
+                }
+
+                // Column visibility
+                if ($colDimension->getVisible() === false) {
+                    $objWriter->writeAttribute('hidden', 'true');
+                }
+
+                // Auto size?
+                if ($colDimension->getAutoSize()) {
+                    $objWriter->writeAttribute('bestFit', 'true');
+                }
+
+                // Custom width?
+                if ($colDimension->getWidth() != $pSheet->getDefaultColumnDimension()->getWidth()) {
+                    $objWriter->writeAttribute('customWidth', 'true');
+                }
+
+                // Collapsed
+                if ($colDimension->getCollapsed() === true) {
+                    $objWriter->writeAttribute('collapsed', 'true');
+                }
+
+                // Outline level
+                if ($colDimension->getOutlineLevel() > 0) {
+                    $objWriter->writeAttribute('outlineLevel', $colDimension->getOutlineLevel());
+                }
+
+                // Style
+                $objWriter->writeAttribute('style', $colDimension->getXfIndex());
+
+                $objWriter->endElement();
+            }
+
+            $objWriter->endElement();
+        }
+    }
+
+    /**
+     * Write SheetProtection.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param PhpspreadsheetWorksheet $pSheet Worksheet
+     */
+    private function writeSheetProtection(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void
+    {
+        // sheetProtection
+        $objWriter->startElement('sheetProtection');
+
+        $protection = $pSheet->getProtection();
+
+        if ($protection->getAlgorithm()) {
+            $objWriter->writeAttribute('algorithmName', $protection->getAlgorithm());
+            $objWriter->writeAttribute('hashValue', $protection->getPassword());
+            $objWriter->writeAttribute('saltValue', $protection->getSalt());
+            $objWriter->writeAttribute('spinCount', $protection->getSpinCount());
+        } elseif ($protection->getPassword() !== '') {
+            $objWriter->writeAttribute('password', $protection->getPassword());
+        }
+
+        $objWriter->writeAttribute('sheet', ($protection->getSheet() ? 'true' : 'false'));
+        $objWriter->writeAttribute('objects', ($protection->getObjects() ? 'true' : 'false'));
+        $objWriter->writeAttribute('scenarios', ($protection->getScenarios() ? 'true' : 'false'));
+        $objWriter->writeAttribute('formatCells', ($protection->getFormatCells() ? 'true' : 'false'));
+        $objWriter->writeAttribute('formatColumns', ($protection->getFormatColumns() ? 'true' : 'false'));
+        $objWriter->writeAttribute('formatRows', ($protection->getFormatRows() ? 'true' : 'false'));
+        $objWriter->writeAttribute('insertColumns', ($protection->getInsertColumns() ? 'true' : 'false'));
+        $objWriter->writeAttribute('insertRows', ($protection->getInsertRows() ? 'true' : 'false'));
+        $objWriter->writeAttribute('insertHyperlinks', ($protection->getInsertHyperlinks() ? 'true' : 'false'));
+        $objWriter->writeAttribute('deleteColumns', ($protection->getDeleteColumns() ? 'true' : 'false'));
+        $objWriter->writeAttribute('deleteRows', ($protection->getDeleteRows() ? 'true' : 'false'));
+        $objWriter->writeAttribute('selectLockedCells', ($protection->getSelectLockedCells() ? 'true' : 'false'));
+        $objWriter->writeAttribute('sort', ($protection->getSort() ? 'true' : 'false'));
+        $objWriter->writeAttribute('autoFilter', ($protection->getAutoFilter() ? 'true' : 'false'));
+        $objWriter->writeAttribute('pivotTables', ($protection->getPivotTables() ? 'true' : 'false'));
+        $objWriter->writeAttribute('selectUnlockedCells', ($protection->getSelectUnlockedCells() ? 'true' : 'false'));
+        $objWriter->endElement();
+    }
+
+    private static function writeAttributeIf(XMLWriter $objWriter, $condition, string $attr, string $val): void
+    {
+        if ($condition) {
+            $objWriter->writeAttribute($attr, $val);
+        }
+    }
+
+    private static function writeElementIf(XMLWriter $objWriter, $condition, string $attr, string $val): void
+    {
+        if ($condition) {
+            $objWriter->writeElement($attr, $val);
+        }
+    }
+
+    private static function writeOtherCondElements(XMLWriter $objWriter, Conditional $conditional, string $cellCoordinate): void
+    {
+        if ($conditional->getConditionType() == Conditional::CONDITION_CELLIS
+            || $conditional->getConditionType() == Conditional::CONDITION_CONTAINSTEXT
+            || $conditional->getConditionType() == Conditional::CONDITION_EXPRESSION) {
+            foreach ($conditional->getConditions() as $formula) {
+                // Formula
+                $objWriter->writeElement('formula', Xlfn::addXlfn($formula));
+            }
+        } elseif ($conditional->getConditionType() == Conditional::CONDITION_CONTAINSBLANKS) {
+            // formula copied from ms xlsx xml source file
+            $objWriter->writeElement('formula', 'LEN(TRIM(' . $cellCoordinate . '))=0');
+        } elseif ($conditional->getConditionType() == Conditional::CONDITION_NOTCONTAINSBLANKS) {
+            // formula copied from ms xlsx xml source file
+            $objWriter->writeElement('formula', 'LEN(TRIM(' . $cellCoordinate . '))>0');
+        }
+    }
+
+    private static function writeTextCondElements(XMLWriter $objWriter, Conditional $conditional, string $cellCoordinate): void
+    {
+        $txt = $conditional->getText();
+        if ($txt !== null) {
+            $objWriter->writeAttribute('text', $txt);
+            if ($conditional->getOperatorType() == Conditional::OPERATOR_CONTAINSTEXT) {
+                $objWriter->writeElement('formula', 'NOT(ISERROR(SEARCH("' . $txt . '",' . $cellCoordinate . ')))');
+            } elseif ($conditional->getOperatorType() == Conditional::OPERATOR_BEGINSWITH) {
+                $objWriter->writeElement('formula', 'LEFT(' . $cellCoordinate . ',' . strlen($txt) . ')="' . $txt . '"');
+            } elseif ($conditional->getOperatorType() == Conditional::OPERATOR_ENDSWITH) {
+                $objWriter->writeElement('formula', 'RIGHT(' . $cellCoordinate . ',' . strlen($txt) . ')="' . $txt . '"');
+            } elseif ($conditional->getOperatorType() == Conditional::OPERATOR_NOTCONTAINS) {
+                $objWriter->writeElement('formula', 'ISERROR(SEARCH("' . $txt . '",' . $cellCoordinate . '))');
+            }
+        }
+    }
+
+    /**
+     * Write ConditionalFormatting.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param PhpspreadsheetWorksheet $pSheet Worksheet
+     */
+    private function writeConditionalFormatting(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void
+    {
+        // Conditional id
+        $id = 1;
+
+        // Loop through styles in the current worksheet
+        foreach ($pSheet->getConditionalStylesCollection() as $cellCoordinate => $conditionalStyles) {
+            foreach ($conditionalStyles as $conditional) {
+                // WHY was this again?
+                // if ($this->getParentWriter()->getStylesConditionalHashTable()->getIndexForHashCode($conditional->getHashCode()) == '') {
+                //    continue;
+                // }
+                if ($conditional->getConditionType() != Conditional::CONDITION_NONE) {
+                    // conditionalFormatting
+                    $objWriter->startElement('conditionalFormatting');
+                    $objWriter->writeAttribute('sqref', $cellCoordinate);
+
+                    // cfRule
+                    $objWriter->startElement('cfRule');
+                    $objWriter->writeAttribute('type', $conditional->getConditionType());
+                    $objWriter->writeAttribute('dxfId', $this->getParentWriter()->getStylesConditionalHashTable()->getIndexForHashCode($conditional->getHashCode()));
+                    $objWriter->writeAttribute('priority', $id++);
+
+                    self::writeAttributeif(
+                        $objWriter,
+                        ($conditional->getConditionType() == Conditional::CONDITION_CELLIS || $conditional->getConditionType() == Conditional::CONDITION_CONTAINSTEXT)
+                        && $conditional->getOperatorType() != Conditional::OPERATOR_NONE,
+                        'operator',
+                        $conditional->getOperatorType()
+                    );
+
+                    self::writeAttributeIf($objWriter, $conditional->getStopIfTrue(), 'stopIfTrue', '1');
+
+                    if ($conditional->getConditionType() == Conditional::CONDITION_CONTAINSTEXT) {
+                        self::writeTextCondElements($objWriter, $conditional, $cellCoordinate);
+                    } else {
+                        self::writeOtherCondElements($objWriter, $conditional, $cellCoordinate);
+                    }
+
+                    $objWriter->endElement();
+
+                    $objWriter->endElement();
+                }
+            }
+        }
+    }
+
+    /**
+     * Write DataValidations.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param PhpspreadsheetWorksheet $pSheet Worksheet
+     */
+    private function writeDataValidations(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void
+    {
+        // Datavalidation collection
+        $dataValidationCollection = $pSheet->getDataValidationCollection();
+
+        // Write data validations?
+        if (!empty($dataValidationCollection)) {
+            $dataValidationCollection = Coordinate::mergeRangesInCollection($dataValidationCollection);
+            $objWriter->startElement('dataValidations');
+            $objWriter->writeAttribute('count', count($dataValidationCollection));
+
+            foreach ($dataValidationCollection as $coordinate => $dv) {
+                $objWriter->startElement('dataValidation');
+
+                if ($dv->getType() != '') {
+                    $objWriter->writeAttribute('type', $dv->getType());
+                }
+
+                if ($dv->getErrorStyle() != '') {
+                    $objWriter->writeAttribute('errorStyle', $dv->getErrorStyle());
+                }
+
+                if ($dv->getOperator() != '') {
+                    $objWriter->writeAttribute('operator', $dv->getOperator());
+                }
+
+                $objWriter->writeAttribute('allowBlank', ($dv->getAllowBlank() ? '1' : '0'));
+                $objWriter->writeAttribute('showDropDown', (!$dv->getShowDropDown() ? '1' : '0'));
+                $objWriter->writeAttribute('showInputMessage', ($dv->getShowInputMessage() ? '1' : '0'));
+                $objWriter->writeAttribute('showErrorMessage', ($dv->getShowErrorMessage() ? '1' : '0'));
+
+                if ($dv->getErrorTitle() !== '') {
+                    $objWriter->writeAttribute('errorTitle', $dv->getErrorTitle());
+                }
+                if ($dv->getError() !== '') {
+                    $objWriter->writeAttribute('error', $dv->getError());
+                }
+                if ($dv->getPromptTitle() !== '') {
+                    $objWriter->writeAttribute('promptTitle', $dv->getPromptTitle());
+                }
+                if ($dv->getPrompt() !== '') {
+                    $objWriter->writeAttribute('prompt', $dv->getPrompt());
+                }
+
+                $objWriter->writeAttribute('sqref', $coordinate);
+
+                if ($dv->getFormula1() !== '') {
+                    $objWriter->writeElement('formula1', $dv->getFormula1());
+                }
+                if ($dv->getFormula2() !== '') {
+                    $objWriter->writeElement('formula2', $dv->getFormula2());
+                }
+
+                $objWriter->endElement();
+            }
+
+            $objWriter->endElement();
+        }
+    }
+
+    /**
+     * Write Hyperlinks.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param PhpspreadsheetWorksheet $pSheet Worksheet
+     */
+    private function writeHyperlinks(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void
+    {
+        // Hyperlink collection
+        $hyperlinkCollection = $pSheet->getHyperlinkCollection();
+
+        // Relation ID
+        $relationId = 1;
+
+        // Write hyperlinks?
+        if (!empty($hyperlinkCollection)) {
+            $objWriter->startElement('hyperlinks');
+
+            foreach ($hyperlinkCollection as $coordinate => $hyperlink) {
+                $objWriter->startElement('hyperlink');
+
+                $objWriter->writeAttribute('ref', $coordinate);
+                if (!$hyperlink->isInternal()) {
+                    $objWriter->writeAttribute('r:id', 'rId_hyperlink_' . $relationId);
+                    ++$relationId;
+                } else {
+                    $objWriter->writeAttribute('location', str_replace('sheet://', '', $hyperlink->getUrl()));
+                }
+
+                if ($hyperlink->getTooltip() !== '') {
+                    $objWriter->writeAttribute('tooltip', $hyperlink->getTooltip());
+                    $objWriter->writeAttribute('display', $hyperlink->getTooltip());
+                }
+
+                $objWriter->endElement();
+            }
+
+            $objWriter->endElement();
+        }
+    }
+
+    /**
+     * Write ProtectedRanges.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param PhpspreadsheetWorksheet $pSheet Worksheet
+     */
+    private function writeProtectedRanges(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void
+    {
+        if (count($pSheet->getProtectedCells()) > 0) {
+            // protectedRanges
+            $objWriter->startElement('protectedRanges');
+
+            // Loop protectedRanges
+            foreach ($pSheet->getProtectedCells() as $protectedCell => $passwordHash) {
+                // protectedRange
+                $objWriter->startElement('protectedRange');
+                $objWriter->writeAttribute('name', 'p' . md5($protectedCell));
+                $objWriter->writeAttribute('sqref', $protectedCell);
+                if (!empty($passwordHash)) {
+                    $objWriter->writeAttribute('password', $passwordHash);
+                }
+                $objWriter->endElement();
+            }
+
+            $objWriter->endElement();
+        }
+    }
+
+    /**
+     * Write MergeCells.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param PhpspreadsheetWorksheet $pSheet Worksheet
+     */
+    private function writeMergeCells(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void
+    {
+        if (count($pSheet->getMergeCells()) > 0) {
+            // mergeCells
+            $objWriter->startElement('mergeCells');
+
+            // Loop mergeCells
+            foreach ($pSheet->getMergeCells() as $mergeCell) {
+                // mergeCell
+                $objWriter->startElement('mergeCell');
+                $objWriter->writeAttribute('ref', $mergeCell);
+                $objWriter->endElement();
+            }
+
+            $objWriter->endElement();
+        }
+    }
+
+    /**
+     * Write PrintOptions.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param PhpspreadsheetWorksheet $pSheet Worksheet
+     */
+    private function writePrintOptions(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void
+    {
+        // printOptions
+        $objWriter->startElement('printOptions');
+
+        $objWriter->writeAttribute('gridLines', ($pSheet->getPrintGridlines() ? 'true' : 'false'));
+        $objWriter->writeAttribute('gridLinesSet', 'true');
+
+        if ($pSheet->getPageSetup()->getHorizontalCentered()) {
+            $objWriter->writeAttribute('horizontalCentered', 'true');
+        }
+
+        if ($pSheet->getPageSetup()->getVerticalCentered()) {
+            $objWriter->writeAttribute('verticalCentered', 'true');
+        }
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write PageMargins.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param PhpspreadsheetWorksheet $pSheet Worksheet
+     */
+    private function writePageMargins(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void
+    {
+        // pageMargins
+        $objWriter->startElement('pageMargins');
+        $objWriter->writeAttribute('left', StringHelper::formatNumber($pSheet->getPageMargins()->getLeft()));
+        $objWriter->writeAttribute('right', StringHelper::formatNumber($pSheet->getPageMargins()->getRight()));
+        $objWriter->writeAttribute('top', StringHelper::formatNumber($pSheet->getPageMargins()->getTop()));
+        $objWriter->writeAttribute('bottom', StringHelper::formatNumber($pSheet->getPageMargins()->getBottom()));
+        $objWriter->writeAttribute('header', StringHelper::formatNumber($pSheet->getPageMargins()->getHeader()));
+        $objWriter->writeAttribute('footer', StringHelper::formatNumber($pSheet->getPageMargins()->getFooter()));
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write AutoFilter.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param PhpspreadsheetWorksheet $pSheet Worksheet
+     */
+    private function writeAutoFilter(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void
+    {
+        $autoFilterRange = $pSheet->getAutoFilter()->getRange();
+        if (!empty($autoFilterRange)) {
+            // autoFilter
+            $objWriter->startElement('autoFilter');
+
+            // Strip any worksheet reference from the filter coordinates
+            $range = Coordinate::splitRange($autoFilterRange);
+            $range = $range[0];
+            //    Strip any worksheet ref
+            [$ws, $range[0]] = PhpspreadsheetWorksheet::extractSheetTitle($range[0], true);
+            $range = implode(':', $range);
+
+            $objWriter->writeAttribute('ref', str_replace('$', '', $range));
+
+            $columns = $pSheet->getAutoFilter()->getColumns();
+            if (count($columns) > 0) {
+                foreach ($columns as $columnID => $column) {
+                    $rules = $column->getRules();
+                    if (count($rules) > 0) {
+                        $objWriter->startElement('filterColumn');
+                        $objWriter->writeAttribute('colId', $pSheet->getAutoFilter()->getColumnOffset($columnID));
+
+                        $objWriter->startElement($column->getFilterType());
+                        if ($column->getJoin() == Column::AUTOFILTER_COLUMN_JOIN_AND) {
+                            $objWriter->writeAttribute('and', 1);
+                        }
+
+                        foreach ($rules as $rule) {
+                            if (($column->getFilterType() === Column::AUTOFILTER_FILTERTYPE_FILTER) &&
+                                ($rule->getOperator() === Rule::AUTOFILTER_COLUMN_RULE_EQUAL) &&
+                                ($rule->getValue() === '')) {
+                                //    Filter rule for Blanks
+                                $objWriter->writeAttribute('blank', 1);
+                            } elseif ($rule->getRuleType() === Rule::AUTOFILTER_RULETYPE_DYNAMICFILTER) {
+                                //    Dynamic Filter Rule
+                                $objWriter->writeAttribute('type', $rule->getGrouping());
+                                $val = $column->getAttribute('val');
+                                if ($val !== null) {
+                                    $objWriter->writeAttribute('val', $val);
+                                }
+                                $maxVal = $column->getAttribute('maxVal');
+                                if ($maxVal !== null) {
+                                    $objWriter->writeAttribute('maxVal', $maxVal);
+                                }
+                            } elseif ($rule->getRuleType() === Rule::AUTOFILTER_RULETYPE_TOPTENFILTER) {
+                                //    Top 10 Filter Rule
+                                $objWriter->writeAttribute('val', $rule->getValue());
+                                $objWriter->writeAttribute('percent', (($rule->getOperator() === Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT) ? '1' : '0'));
+                                $objWriter->writeAttribute('top', (($rule->getGrouping() === Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP) ? '1' : '0'));
+                            } else {
+                                //    Filter, DateGroupItem or CustomFilter
+                                $objWriter->startElement($rule->getRuleType());
+
+                                if ($rule->getOperator() !== Rule::AUTOFILTER_COLUMN_RULE_EQUAL) {
+                                    $objWriter->writeAttribute('operator', $rule->getOperator());
+                                }
+                                if ($rule->getRuleType() === Rule::AUTOFILTER_RULETYPE_DATEGROUP) {
+                                    // Date Group filters
+                                    foreach ($rule->getValue() as $key => $value) {
+                                        if ($value > '') {
+                                            $objWriter->writeAttribute($key, $value);
+                                        }
+                                    }
+                                    $objWriter->writeAttribute('dateTimeGrouping', $rule->getGrouping());
+                                } else {
+                                    $objWriter->writeAttribute('val', $rule->getValue());
+                                }
+
+                                $objWriter->endElement();
+                            }
+                        }
+
+                        $objWriter->endElement();
+
+                        $objWriter->endElement();
+                    }
+                }
+            }
+            $objWriter->endElement();
+        }
+    }
+
+    /**
+     * Write PageSetup.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param PhpspreadsheetWorksheet $pSheet Worksheet
+     */
+    private function writePageSetup(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void
+    {
+        // pageSetup
+        $objWriter->startElement('pageSetup');
+        $objWriter->writeAttribute('paperSize', $pSheet->getPageSetup()->getPaperSize());
+        $objWriter->writeAttribute('orientation', $pSheet->getPageSetup()->getOrientation());
+
+        if ($pSheet->getPageSetup()->getScale() !== null) {
+            $objWriter->writeAttribute('scale', $pSheet->getPageSetup()->getScale());
+        }
+        if ($pSheet->getPageSetup()->getFitToHeight() !== null) {
+            $objWriter->writeAttribute('fitToHeight', $pSheet->getPageSetup()->getFitToHeight());
+        } else {
+            $objWriter->writeAttribute('fitToHeight', '0');
+        }
+        if ($pSheet->getPageSetup()->getFitToWidth() !== null) {
+            $objWriter->writeAttribute('fitToWidth', $pSheet->getPageSetup()->getFitToWidth());
+        } else {
+            $objWriter->writeAttribute('fitToWidth', '0');
+        }
+        if ($pSheet->getPageSetup()->getFirstPageNumber() !== null) {
+            $objWriter->writeAttribute('firstPageNumber', $pSheet->getPageSetup()->getFirstPageNumber());
+            $objWriter->writeAttribute('useFirstPageNumber', '1');
+        }
+
+        $getUnparsedLoadedData = $pSheet->getParent()->getUnparsedLoadedData();
+        if (isset($getUnparsedLoadedData['sheets'][$pSheet->getCodeName()]['pageSetupRelId'])) {
+            $objWriter->writeAttribute('r:id', $getUnparsedLoadedData['sheets'][$pSheet->getCodeName()]['pageSetupRelId']);
+        }
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write Header / Footer.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param PhpspreadsheetWorksheet $pSheet Worksheet
+     */
+    private function writeHeaderFooter(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void
+    {
+        // headerFooter
+        $objWriter->startElement('headerFooter');
+        $objWriter->writeAttribute('differentOddEven', ($pSheet->getHeaderFooter()->getDifferentOddEven() ? 'true' : 'false'));
+        $objWriter->writeAttribute('differentFirst', ($pSheet->getHeaderFooter()->getDifferentFirst() ? 'true' : 'false'));
+        $objWriter->writeAttribute('scaleWithDoc', ($pSheet->getHeaderFooter()->getScaleWithDocument() ? 'true' : 'false'));
+        $objWriter->writeAttribute('alignWithMargins', ($pSheet->getHeaderFooter()->getAlignWithMargins() ? 'true' : 'false'));
+
+        $objWriter->writeElement('oddHeader', $pSheet->getHeaderFooter()->getOddHeader());
+        $objWriter->writeElement('oddFooter', $pSheet->getHeaderFooter()->getOddFooter());
+        $objWriter->writeElement('evenHeader', $pSheet->getHeaderFooter()->getEvenHeader());
+        $objWriter->writeElement('evenFooter', $pSheet->getHeaderFooter()->getEvenFooter());
+        $objWriter->writeElement('firstHeader', $pSheet->getHeaderFooter()->getFirstHeader());
+        $objWriter->writeElement('firstFooter', $pSheet->getHeaderFooter()->getFirstFooter());
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write Breaks.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param PhpspreadsheetWorksheet $pSheet Worksheet
+     */
+    private function writeBreaks(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void
+    {
+        // Get row and column breaks
+        $aRowBreaks = [];
+        $aColumnBreaks = [];
+        foreach ($pSheet->getBreaks() as $cell => $breakType) {
+            if ($breakType == PhpspreadsheetWorksheet::BREAK_ROW) {
+                $aRowBreaks[] = $cell;
+            } elseif ($breakType == PhpspreadsheetWorksheet::BREAK_COLUMN) {
+                $aColumnBreaks[] = $cell;
+            }
+        }
+
+        // rowBreaks
+        if (!empty($aRowBreaks)) {
+            $objWriter->startElement('rowBreaks');
+            $objWriter->writeAttribute('count', count($aRowBreaks));
+            $objWriter->writeAttribute('manualBreakCount', count($aRowBreaks));
+
+            foreach ($aRowBreaks as $cell) {
+                $coords = Coordinate::coordinateFromString($cell);
+
+                $objWriter->startElement('brk');
+                $objWriter->writeAttribute('id', $coords[1]);
+                $objWriter->writeAttribute('man', '1');
+                $objWriter->endElement();
+            }
+
+            $objWriter->endElement();
+        }
+
+        // Second, write column breaks
+        if (!empty($aColumnBreaks)) {
+            $objWriter->startElement('colBreaks');
+            $objWriter->writeAttribute('count', count($aColumnBreaks));
+            $objWriter->writeAttribute('manualBreakCount', count($aColumnBreaks));
+
+            foreach ($aColumnBreaks as $cell) {
+                $coords = Coordinate::coordinateFromString($cell);
+
+                $objWriter->startElement('brk');
+                $objWriter->writeAttribute('id', Coordinate::columnIndexFromString($coords[0]) - 1);
+                $objWriter->writeAttribute('man', '1');
+                $objWriter->endElement();
+            }
+
+            $objWriter->endElement();
+        }
+    }
+
+    /**
+     * Write SheetData.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param PhpspreadsheetWorksheet $pSheet Worksheet
+     * @param string[] $pStringTable String table
+     */
+    private function writeSheetData(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet, array $pStringTable): void
+    {
+        // Flipped stringtable, for faster index searching
+        $aFlippedStringTable = $this->getParentWriter()->getWriterPart('stringtable')->flipStringTable($pStringTable);
+
+        // sheetData
+        $objWriter->startElement('sheetData');
+
+        // Get column count
+        $colCount = Coordinate::columnIndexFromString($pSheet->getHighestColumn());
+
+        // Highest row number
+        $highestRow = $pSheet->getHighestRow();
+
+        // Loop through cells
+        $cellsByRow = [];
+        foreach ($pSheet->getCoordinates() as $coordinate) {
+            $cellAddress = Coordinate::coordinateFromString($coordinate);
+            $cellsByRow[$cellAddress[1]][] = $coordinate;
+        }
+
+        $currentRow = 0;
+        while ($currentRow++ < $highestRow) {
+            // Get row dimension
+            $rowDimension = $pSheet->getRowDimension($currentRow);
+
+            // Write current row?
+            $writeCurrentRow = isset($cellsByRow[$currentRow]) || $rowDimension->getRowHeight() >= 0 || $rowDimension->getVisible() == false || $rowDimension->getCollapsed() == true || $rowDimension->getOutlineLevel() > 0 || $rowDimension->getXfIndex() !== null;
+
+            if ($writeCurrentRow) {
+                // Start a new row
+                $objWriter->startElement('row');
+                $objWriter->writeAttribute('r', $currentRow);
+                $objWriter->writeAttribute('spans', '1:' . $colCount);
+
+                // Row dimensions
+                if ($rowDimension->getRowHeight() >= 0) {
+                    $objWriter->writeAttribute('customHeight', '1');
+                    $objWriter->writeAttribute('ht', StringHelper::formatNumber($rowDimension->getRowHeight()));
+                }
+
+                // Row visibility
+                if (!$rowDimension->getVisible() === true) {
+                    $objWriter->writeAttribute('hidden', 'true');
+                }
+
+                // Collapsed
+                if ($rowDimension->getCollapsed() === true) {
+                    $objWriter->writeAttribute('collapsed', 'true');
+                }
+
+                // Outline level
+                if ($rowDimension->getOutlineLevel() > 0) {
+                    $objWriter->writeAttribute('outlineLevel', $rowDimension->getOutlineLevel());
+                }
+
+                // Style
+                if ($rowDimension->getXfIndex() !== null) {
+                    $objWriter->writeAttribute('s', $rowDimension->getXfIndex());
+                    $objWriter->writeAttribute('customFormat', '1');
+                }
+
+                // Write cells
+                if (isset($cellsByRow[$currentRow])) {
+                    foreach ($cellsByRow[$currentRow] as $cellAddress) {
+                        // Write cell
+                        $this->writeCell($objWriter, $pSheet, $cellAddress, $aFlippedStringTable);
+                    }
+                }
+
+                // End row
+                $objWriter->endElement();
+            }
+        }
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * @param RichText|string $cellValue
+     */
+    private function writeCellInlineStr(XMLWriter $objWriter, string $mappedType, $cellValue): void
+    {
+        $objWriter->writeAttribute('t', $mappedType);
+        if (!$cellValue instanceof RichText) {
+            $objWriter->writeElement('t', StringHelper::controlCharacterPHP2OOXML(htmlspecialchars($cellValue)));
+        } elseif ($cellValue instanceof RichText) {
+            $objWriter->startElement('is');
+            $this->getParentWriter()->getWriterPart('stringtable')->writeRichText($objWriter, $cellValue);
+            $objWriter->endElement();
+        }
+    }
+
+    /**
+     * @param RichText|string $cellValue
+     * @param string[] $pFlippedStringTable
+     */
+    private function writeCellString(XMLWriter $objWriter, string $mappedType, $cellValue, array $pFlippedStringTable): void
+    {
+        $objWriter->writeAttribute('t', $mappedType);
+        if (!$cellValue instanceof RichText) {
+            self::writeElementIf($objWriter, isset($pFlippedStringTable[$cellValue]), 'v', $pFlippedStringTable[$cellValue]);
+        } else {
+            $objWriter->writeElement('v', $pFlippedStringTable[$cellValue->getHashCode()]);
+        }
+    }
+
+    /**
+     * @param float|int $cellValue
+     */
+    private function writeCellNumeric(XMLWriter $objWriter, $cellValue): void
+    {
+        //force a decimal to be written if the type is float
+        if (is_float($cellValue)) {
+            // force point as decimal separator in case current locale uses comma
+            $cellValue = str_replace(',', '.', (string) $cellValue);
+            if (strpos($cellValue, '.') === false) {
+                $cellValue = $cellValue . '.0';
+            }
+        }
+        $objWriter->writeElement('v', $cellValue);
+    }
+
+    private function writeCellBoolean(XMLWriter $objWriter, string $mappedType, bool $cellValue): void
+    {
+        $objWriter->writeAttribute('t', $mappedType);
+        $objWriter->writeElement('v', $cellValue ? '1' : '0');
+    }
+
+    private function writeCellError(XMLWriter $objWriter, string $mappedType, string $cellValue, string $formulaerr = '#NULL!'): void
+    {
+        $objWriter->writeAttribute('t', $mappedType);
+        $cellIsFormula = substr($cellValue, 0, 1) === '=';
+        self::writeElementIf($objWriter, $cellIsFormula, 'f', Xlfn::addXlfnStripEquals($cellValue));
+        $objWriter->writeElement('v', $cellIsFormula ? $formulaerr : $cellValue);
+    }
+
+    private function writeCellFormula(XMLWriter $objWriter, string $cellValue, Cell $pCell): void
+    {
+        $calculatedValue = $this->getParentWriter()->getPreCalculateFormulas() ? $pCell->getCalculatedValue() : $cellValue;
+        if (is_string($calculatedValue)) {
+            if (substr($calculatedValue, 0, 1) === '#') {
+                $this->writeCellError($objWriter, 'e', $cellValue, $calculatedValue);
+
+                return;
+            }
+            $objWriter->writeAttribute('t', 'str');
+        } elseif (is_bool($calculatedValue)) {
+            $objWriter->writeAttribute('t', 'b');
+        }
+        // array values are not yet supported
+        //$attributes = $pCell->getFormulaAttributes();
+        //if (($attributes['t'] ?? null) === 'array') {
+        //    $objWriter->startElement('f');
+        //    $objWriter->writeAttribute('t', 'array');
+        //    $objWriter->writeAttribute('ref', $pCellAddress);
+        //    $objWriter->writeAttribute('aca', '1');
+        //    $objWriter->writeAttribute('ca', '1');
+        //    $objWriter->text(substr($cellValue, 1));
+        //    $objWriter->endElement();
+        //} else {
+        //    $objWriter->writeElement('f', Xlfn::addXlfnStripEquals($cellValue));
+        //}
+        $objWriter->writeElement('f', Xlfn::addXlfnStripEquals($cellValue));
+        self::writeElementIf(
+            $objWriter,
+            $this->getParentWriter()->getOffice2003Compatibility() === false,
+            'v',
+            ($this->getParentWriter()->getPreCalculateFormulas() && !is_array($calculatedValue) && substr($calculatedValue, 0, 1) !== '#')
+                ? StringHelper::formatNumber($calculatedValue) : '0'
+        );
+    }
+
+    /**
+     * Write Cell.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param PhpspreadsheetWorksheet $pSheet Worksheet
+     * @param string $pCellAddress Cell Address
+     * @param string[] $pFlippedStringTable String table (flipped), for faster index searching
+     */
+    private function writeCell(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet, string $pCellAddress, array $pFlippedStringTable): void
+    {
+        // Cell
+        $pCell = $pSheet->getCell($pCellAddress);
+        $objWriter->startElement('c');
+        $objWriter->writeAttribute('r', $pCellAddress);
+
+        // Sheet styles
+        $xfi = $pCell->getXfIndex();
+        self::writeAttributeIf($objWriter, $xfi, 's', $xfi);
+
+        // If cell value is supplied, write cell value
+        $cellValue = $pCell->getValue();
+        if (is_object($cellValue) || $cellValue !== '') {
+            // Map type
+            $mappedType = $pCell->getDataType();
+
+            // Write data depending on its type
+            switch (strtolower($mappedType)) {
+                case 'inlinestr':    // Inline string
+                    $this->writeCellInlineStr($objWriter, $mappedType, $cellValue);
+
+                    break;
+                case 's':            // String
+                    $this->writeCellString($objWriter, $mappedType, $cellValue, $pFlippedStringTable);
+
+                    break;
+                case 'f':            // Formula
+                    $this->writeCellFormula($objWriter, $cellValue, $pCell);
+
+                    break;
+                case 'n':            // Numeric
+                    $this->writeCellNumeric($objWriter, $cellValue);
+
+                    break;
+                case 'b':            // Boolean
+                    $this->writeCellBoolean($objWriter, $mappedType, $cellValue);
+
+                    break;
+                case 'e':            // Error
+                    $this->writeCellError($objWriter, $mappedType, $cellValue);
+            }
+        }
+
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write Drawings.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param PhpspreadsheetWorksheet $pSheet Worksheet
+     * @param bool $includeCharts Flag indicating if we should include drawing details for charts
+     */
+    private function writeDrawings(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet, $includeCharts = false): void
+    {
+        $unparsedLoadedData = $pSheet->getParent()->getUnparsedLoadedData();
+        $hasUnparsedDrawing = isset($unparsedLoadedData['sheets'][$pSheet->getCodeName()]['drawingOriginalIds']);
+        $chartCount = ($includeCharts) ? $pSheet->getChartCollection()->count() : 0;
+        if ($chartCount == 0 && $pSheet->getDrawingCollection()->count() == 0 && !$hasUnparsedDrawing) {
+            return;
+        }
+
+        // If sheet contains drawings, add the relationships
+        $objWriter->startElement('drawing');
+
+        $rId = 'rId1';
+        if (isset($unparsedLoadedData['sheets'][$pSheet->getCodeName()]['drawingOriginalIds'])) {
+            $drawingOriginalIds = $unparsedLoadedData['sheets'][$pSheet->getCodeName()]['drawingOriginalIds'];
+            // take first. In future can be overriten
+            // (! synchronize with \PhpOffice\PhpSpreadsheet\Writer\Xlsx\Rels::writeWorksheetRelationships)
+            $rId = reset($drawingOriginalIds);
+        }
+
+        $objWriter->writeAttribute('r:id', $rId);
+        $objWriter->endElement();
+    }
+
+    /**
+     * Write LegacyDrawing.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param PhpspreadsheetWorksheet $pSheet Worksheet
+     */
+    private function writeLegacyDrawing(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void
+    {
+        // If sheet contains comments, add the relationships
+        if (count($pSheet->getComments()) > 0) {
+            $objWriter->startElement('legacyDrawing');
+            $objWriter->writeAttribute('r:id', 'rId_comments_vml1');
+            $objWriter->endElement();
+        }
+    }
+
+    /**
+     * Write LegacyDrawingHF.
+     *
+     * @param XMLWriter $objWriter XML Writer
+     * @param PhpspreadsheetWorksheet $pSheet Worksheet
+     */
+    private function writeLegacyDrawingHF(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void
+    {
+        // If sheet contains images, add the relationships
+        if (count($pSheet->getHeaderFooter()->getImages()) > 0) {
+            $objWriter->startElement('legacyDrawingHF');
+            $objWriter->writeAttribute('r:id', 'rId_headerfooter_vml1');
+            $objWriter->endElement();
+        }
+    }
+
+    private function writeAlternateContent(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void
+    {
+        if (empty($pSheet->getParent()->getUnparsedLoadedData()['sheets'][$pSheet->getCodeName()]['AlternateContents'])) {
+            return;
+        }
+
+        foreach ($pSheet->getParent()->getUnparsedLoadedData()['sheets'][$pSheet->getCodeName()]['AlternateContents'] as $alternateContent) {
+            $objWriter->writeRaw($alternateContent);
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/WriterPart.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/WriterPart.php
new file mode 100755
index 0000000..a9137df
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/WriterPart.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
+
+abstract class WriterPart
+{
+    /**
+     * Parent Xlsx object.
+     *
+     * @var Xlsx
+     */
+    private $parentWriter;
+
+    /**
+     * Get parent Xlsx object.
+     *
+     * @return Xlsx
+     */
+    public function getParentWriter()
+    {
+        return $this->parentWriter;
+    }
+
+    /**
+     * Set parent Xlsx object.
+     */
+    public function __construct(Xlsx $pWriter)
+    {
+        $this->parentWriter = $pWriter;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Xlfn.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Xlfn.php
new file mode 100755
index 0000000..8f7c07e
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Xlfn.php
@@ -0,0 +1,159 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
+
+class Xlfn
+{
+    const XLFNREGEXP = '/(?<!_xlfn[.])\\b('
+            // functions added with Excel 2010
+        . 'beta[.]dist'
+        . '|beta[.]inv'
+        . '|binom[.]dist'
+        . '|binom[.]inv'
+        . '|chisq[.]dist'
+        . '|chisq[.]dist[.]rt'
+        . '|chisq[.]inv'
+        . '|chisq[.]inv[.]rt'
+        . '|chisq[.]test'
+        . '|confidence[.]norm'
+        . '|confidence[.]t'
+        . '|covariance[.]p'
+        . '|covariance[.]s'
+        . '|erf[.]precise'
+        . '|erfc[.]precise'
+        . '|expon[.]dist'
+        . '|f[.]dist'
+        . '|f[.]dist[.]rt'
+        . '|f[.]inv'
+        . '|f[.]inv[.]rt'
+        . '|f[.]test'
+        . '|gamma[.]dist'
+        . '|gamma[.]inv'
+        . '|gammaln[.]precise'
+        . '|lognorm[.]dist'
+        . '|lognorm[.]inv'
+        . '|mode[.]mult'
+        . '|mode[.]sngl'
+        . '|negbinom[.]dist'
+        . '|networkdays[.]intl'
+        . '|norm[.]dist'
+        . '|norm[.]inv'
+        . '|norm[.]s[.]dist'
+        . '|norm[.]s[.]inv'
+        . '|percentile[.]exc'
+        . '|percentile[.]inc'
+        . '|percentrank[.]exc'
+        . '|percentrank[.]inc'
+        . '|poisson[.]dist'
+        . '|quartile[.]exc'
+        . '|quartile[.]inc'
+        . '|rank[.]avg'
+        . '|rank[.]eq'
+        . '|stdev[.]p'
+        . '|stdev[.]s'
+        . '|t[.]dist'
+        . '|t[.]dist[.]2t'
+        . '|t[.]dist[.]rt'
+        . '|t[.]inv'
+        . '|t[.]inv[.]2t'
+        . '|t[.]test'
+        . '|var[.]p'
+        . '|var[.]s'
+        . '|weibull[.]dist'
+        . '|z[.]test'
+        // functions added with Excel 2013
+        . '|acot'
+        . '|acoth'
+        . '|arabic'
+        . '|averageifs'
+        . '|binom[.]dist[.]range'
+        . '|bitand'
+        . '|bitlshift'
+        . '|bitor'
+        . '|bitrshift'
+        . '|bitxor'
+        . '|ceiling[.]math'
+        . '|combina'
+        . '|cot'
+        . '|coth'
+        . '|csc'
+        . '|csch'
+        . '|days'
+        . '|dbcs'
+        . '|decimal'
+        . '|encodeurl'
+        . '|filterxml'
+        . '|floor[.]math'
+        . '|formulatext'
+        . '|gamma'
+        . '|gauss'
+        . '|ifna'
+        . '|imcosh'
+        . '|imcot'
+        . '|imcsc'
+        . '|imcsch'
+        . '|imsec'
+        . '|imsech'
+        . '|imsinh'
+        . '|imtan'
+        . '|isformula'
+        . '|iso[.]ceiling'
+        . '|isoweeknum'
+        . '|munit'
+        . '|numbervalue'
+        . '|pduration'
+        . '|permutationa'
+        . '|phi'
+        . '|rri'
+        . '|sec'
+        . '|sech'
+        . '|sheet'
+        . '|sheets'
+        . '|skew[.]p'
+        . '|unichar'
+        . '|unicode'
+        . '|webservice'
+        . '|xor'
+        // functions added with Excel 2016
+        . '|forecast[.]et2'
+        . '|forecast[.]ets[.]confint'
+        . '|forecast[.]ets[.]seasonality'
+        . '|forecast[.]ets[.]stat'
+        . '|forecast[.]linear'
+        . '|switch'
+        // functions added with Excel 2019
+        . '|concat'
+        . '|countifs'
+        . '|ifs'
+        . '|maxifs'
+        . '|minifs'
+        . '|sumifs'
+        . '|textjoin'
+        // functions added with Excel 365
+        . '|filter'
+        . '|randarray'
+        . '|sequence'
+        . '|sort'
+        . '|sortby'
+        . '|unique'
+        . '|xlookup'
+        . '|xmatch'
+        . ')(?=\\s*[(])/i';
+
+    /**
+     * Prefix function name in string with _xlfn. where required.
+     */
+    public static function addXlfn(string $funcstring): string
+    {
+        return preg_replace(self::XLFNREGEXP, '_xlfn.$1', $funcstring);
+    }
+
+    /**
+     * Prefix function name in string with _xlfn. where required.
+     * Leading character, expected to be equals sign, is stripped.
+     */
+    public static function addXlfnStripEquals(string $funcstring): string
+    {
+        return self::addXlfn(substr($funcstring, 1));
+    }
+}
diff --git a/vendor/pimple/pimple/.gitignore b/vendor/pimple/pimple/.gitignore
new file mode 100755
index 0000000..c089b09
--- /dev/null
+++ b/vendor/pimple/pimple/.gitignore
@@ -0,0 +1,3 @@
+phpunit.xml
+composer.lock
+/vendor/
diff --git a/vendor/pimple/pimple/.travis.yml b/vendor/pimple/pimple/.travis.yml
new file mode 100755
index 0000000..196f7fc
--- /dev/null
+++ b/vendor/pimple/pimple/.travis.yml
@@ -0,0 +1,40 @@
+language: php
+
+env:
+  matrix:
+    - PIMPLE_EXT=no
+    - PIMPLE_EXT=yes
+  global:
+    - REPORT_EXIT_STATUS=1
+
+php:
+  - 5.3
+  - 5.4
+  - 5.5
+  - 5.6
+  - 7.0
+  - 7.1
+
+before_script:
+  - composer self-update
+  - COMPOSER_ROOT_VERSION=dev-master composer install
+  - if [ "$PIMPLE_EXT" == "yes" ]; then sh -c "cd ext/pimple && phpize && ./configure && make && sudo make install"; fi
+  - if [ "$PIMPLE_EXT" == "yes" ]; then echo "extension=pimple.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`; fi
+
+script:
+  - cd ext/pimple
+  - if [ "$PIMPLE_EXT" == "yes" ]; then yes n | make test | tee output ; grep -E 'Tests failed +. +0' output; fi
+  - if [ "$PIMPLE_EXT" == "yes" ]; then export SYMFONY_DEPRECATIONS_HELPER=weak; fi
+  - cd ../..
+  - ./vendor/bin/simple-phpunit
+
+matrix:
+  include:
+    - php: hhvm
+      dist: trusty
+      env: PIMPLE_EXT=no
+  exclude:
+    - php: 7.0
+      env: PIMPLE_EXT=yes
+    - php: 7.1
+      env: PIMPLE_EXT=yes
diff --git a/vendor/pimple/pimple/CHANGELOG b/vendor/pimple/pimple/CHANGELOG
new file mode 100755
index 0000000..ba56760
--- /dev/null
+++ b/vendor/pimple/pimple/CHANGELOG
@@ -0,0 +1,59 @@
+* 3.2.3 (2017-XX-XX)
+
+ * n/a
+
+* 3.2.2 (2017-07-23)
+
+ * reverted extending a protected closure throws an exception (deprecated it instead)
+
+* 3.2.1 (2017-07-17)
+
+ * fixed PHP error
+
+* 3.2.0 (2017-07-17)
+
+ * added a PSR-11 service locator
+ * added a PSR-11 wrapper
+ * added ServiceIterator
+ * fixed extending a protected closure (now throws InvalidServiceIdentifierException)
+
+* 3.1.0 (2017-07-03)
+
+ * deprecated the C extension
+ * added support for PSR-11 exceptions
+
+* 3.0.2 (2015-09-11)
+
+ * refactored the C extension
+ * minor non-significant changes
+
+* 3.0.1 (2015-07-30)
+
+ * simplified some code
+ * fixed a segfault in the C extension
+
+* 3.0.0 (2014-07-24)
+
+ * removed the Pimple class alias (use Pimple\Container instead)
+
+* 2.1.1 (2014-07-24)
+
+ * fixed compiler warnings for the C extension
+ * fixed code when dealing with circular references
+
+* 2.1.0 (2014-06-24)
+
+ * moved the Pimple to Pimple\Container (with a BC layer -- Pimple is now a
+   deprecated alias which will be removed in Pimple 3.0)
+ * added Pimple\ServiceProviderInterface (and Pimple::register())
+
+* 2.0.0 (2014-02-10)
+
+ * changed extend to automatically re-assign the extended service and keep it as shared or factory
+   (to keep BC, extend still returns the extended service)
+ * changed services to be shared by default (use factory() for factory
+   services)
+
+* 1.0.0
+
+ * initial version
diff --git a/vendor/pimple/pimple/LICENSE b/vendor/pimple/pimple/LICENSE
new file mode 100755
index 0000000..e02dc5a
--- /dev/null
+++ b/vendor/pimple/pimple/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2009-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.
diff --git a/vendor/pimple/pimple/README.rst b/vendor/pimple/pimple/README.rst
new file mode 100755
index 0000000..a03b6d3
--- /dev/null
+++ b/vendor/pimple/pimple/README.rst
@@ -0,0 +1,326 @@
+Pimple
+======
+
+.. caution::
+
+    This is the documentation for Pimple 3.x. If you are using Pimple 1.x, read
+    the `Pimple 1.x documentation`_. Reading the Pimple 1.x code is also a good
+    way to learn more about how to create a simple Dependency Injection
+    Container (recent versions of Pimple are more focused on performance).
+
+Pimple is a small Dependency Injection Container for PHP.
+
+Installation
+------------
+
+Before using Pimple in your project, add it to your ``composer.json`` file:
+
+.. code-block:: bash
+
+    $ ./composer.phar require pimple/pimple "^3.0"
+
+Usage
+-----
+
+Creating a container is a matter of creating a ``Container`` instance:
+
+.. code-block:: php
+
+    use Pimple\Container;
+
+    $container = new Container();
+
+As many other dependency injection containers, Pimple manages two different
+kind of data: **services** and **parameters**.
+
+Defining Services
+~~~~~~~~~~~~~~~~~
+
+A service is an object that does something as part of a larger system. Examples
+of services: a database connection, a templating engine, or a mailer. Almost
+any **global** object can be a service.
+
+Services are defined by **anonymous functions** that return an instance of an
+object:
+
+.. code-block:: php
+
+    // define some services
+    $container['session_storage'] = function ($c) {
+        return new SessionStorage('SESSION_ID');
+    };
+
+    $container['session'] = function ($c) {
+        return new Session($c['session_storage']);
+    };
+
+Notice that the anonymous function has access to the current container
+instance, allowing references to other services or parameters.
+
+As objects are only created when you get them, the order of the definitions
+does not matter.
+
+Using the defined services is also very easy:
+
+.. code-block:: php
+
+    // get the session object
+    $session = $container['session'];
+
+    // the above call is roughly equivalent to the following code:
+    // $storage = new SessionStorage('SESSION_ID');
+    // $session = new Session($storage);
+
+Defining Factory Services
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+By default, each time you get a service, Pimple returns the **same instance**
+of it. If you want a different instance to be returned for all calls, wrap your
+anonymous function with the ``factory()`` method
+
+.. code-block:: php
+
+    $container['session'] = $container->factory(function ($c) {
+        return new Session($c['session_storage']);
+    });
+
+Now, each call to ``$container['session']`` returns a new instance of the
+session.
+
+Defining Parameters
+~~~~~~~~~~~~~~~~~~~
+
+Defining a parameter allows to ease the configuration of your container from
+the outside and to store global values:
+
+.. code-block:: php
+
+    // define some parameters
+    $container['cookie_name'] = 'SESSION_ID';
+    $container['session_storage_class'] = 'SessionStorage';
+
+If you change the ``session_storage`` service definition like below:
+
+.. code-block:: php
+
+    $container['session_storage'] = function ($c) {
+        return new $c['session_storage_class']($c['cookie_name']);
+    };
+
+You can now easily change the cookie name by overriding the
+``cookie_name`` parameter instead of redefining the service
+definition.
+
+Protecting Parameters
+~~~~~~~~~~~~~~~~~~~~~
+
+Because Pimple sees anonymous functions as service definitions, you need to
+wrap anonymous functions with the ``protect()`` method to store them as
+parameters:
+
+.. code-block:: php
+
+    $container['random_func'] = $container->protect(function () {
+        return rand();
+    });
+
+Modifying Services after Definition
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In some cases you may want to modify a service definition after it has been
+defined. You can use the ``extend()`` method to define additional code to be
+run on your service just after it is created:
+
+.. code-block:: php
+
+    $container['session_storage'] = function ($c) {
+        return new $c['session_storage_class']($c['cookie_name']);
+    };
+
+    $container->extend('session_storage', function ($storage, $c) {
+        $storage->...();
+
+        return $storage;
+    });
+
+The first argument is the name of the service to extend, the second a function
+that gets access to the object instance and the container.
+
+Extending a Container
+~~~~~~~~~~~~~~~~~~~~~
+
+If you use the same libraries over and over, you might want to reuse some
+services from one project to the next one; package your services into a
+**provider** by implementing ``Pimple\ServiceProviderInterface``:
+
+.. code-block:: php
+
+    use Pimple\Container;
+
+    class FooProvider implements Pimple\ServiceProviderInterface
+    {
+        public function register(Container $pimple)
+        {
+            // register some services and parameters
+            // on $pimple
+        }
+    }
+
+Then, register the provider on a Container:
+
+.. code-block:: php
+
+    $pimple->register(new FooProvider());
+
+Fetching the Service Creation Function
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When you access an object, Pimple automatically calls the anonymous function
+that you defined, which creates the service object for you. If you want to get
+raw access to this function, you can use the ``raw()`` method:
+
+.. code-block:: php
+
+    $container['session'] = function ($c) {
+        return new Session($c['session_storage']);
+    };
+
+    $sessionFunction = $container->raw('session');
+
+PSR-11 compatibility
+--------------------
+
+For historical reasons, the ``Container`` class does not implement the PSR-11
+``ContainerInterface``. However, Pimple provides a helper class that will let
+you decouple your code from the Pimple container class.
+
+The PSR-11 container class
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``Pimple\Psr11\Container`` class lets you access the content of an
+underlying Pimple container using ``Psr\Container\ContainerInterface``
+methods:
+
+.. code-block:: php
+
+    use Pimple\Container;
+    use Pimple\Psr11\Container as PsrContainer;
+
+    $container = new Container();
+    $container['service'] = function ($c) {
+        return new Service();
+    };
+    $psr11 = new PsrContainer($container);
+
+    $controller = function (PsrContainer $container) {
+        $service = $container->get('service');
+    };
+    $controller($psr11);
+
+Using the PSR-11 ServiceLocator
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Sometimes, a service needs access to several other services without being sure
+that all of them will actually be used. In those cases, you may want the
+instantiation of the services to be lazy.
+
+The traditional solution is to inject the entire service container to get only
+the services really needed. However, this is not recommended because it gives
+services a too broad access to the rest of the application and it hides their
+actual dependencies.
+
+The ``ServiceLocator`` is intended to solve this problem by giving access to a
+set of predefined services while instantiating them only when actually needed.
+
+It also allows you to make your services available under a different name than
+the one used to register them. For instance, you may want to use an object
+that expects an instance of ``EventDispatcherInterface`` to be available under
+the name ``event_dispatcher`` while your event dispatcher has been
+registered under the name ``dispatcher``:
+
+.. code-block:: php
+
+    use Monolog\Logger;
+    use Pimple\Psr11\ServiceLocator;
+    use Psr\Container\ContainerInterface;
+    use Symfony\Component\EventDispatcher\EventDispatcher;
+
+    class MyService
+    {
+        /**
+         * "logger" must be an instance of Psr\Log\LoggerInterface
+         * "event_dispatcher" must be an instance of Symfony\Component\EventDispatcher\EventDispatcherInterface
+         */
+        private $services;
+
+        public function __construct(ContainerInterface $services)
+        {
+            $this->services = $services;
+        }
+    }
+
+    $container['logger'] = function ($c) {
+        return new Monolog\Logger();
+    };
+    $container['dispatcher'] = function () {
+        return new EventDispatcher();
+    };
+
+    $container['service'] = function ($c) {
+        $locator = new ServiceLocator($c, array('logger', 'event_dispatcher' => 'dispatcher'));
+
+        return new MyService($locator);
+    };
+
+Referencing a Collection of Services Lazily
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Passing a collection of services instances in an array may prove inefficient
+if the class that consumes the collection only needs to iterate over it at a
+later stage, when one of its method is called. It can also lead to problems
+if there is a circular dependency between one of the services stored in the
+collection and the class that consumes it.
+
+The ``ServiceIterator`` class helps you solve these issues. It receives a
+list of service names during instantiation and will retrieve the services
+when iterated over:
+
+.. code-block:: php
+
+    use Pimple\Container;
+    use Pimple\ServiceIterator;
+
+    class AuthorizationService
+    {
+        private $voters;
+
+        public function __construct($voters)
+        {
+            $this->voters = $voters;
+        }
+
+        public function canAccess($resource)
+        {
+            foreach ($this->voters as $voter) {
+                if (true === $voter->canAccess($resource) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+    }
+
+    $container = new Container();
+
+    $container['voter1'] = function ($c) {
+        return new SomeVoter();
+    }
+    $container['voter2'] = function ($c) {
+        return new SomeOtherVoter($c['auth']);
+    }
+    $container['auth'] = function ($c) {
+        return new AuthorizationService(new ServiceIterator($c, array('voter1', 'voter2'));
+    }
+
+.. _Pimple 1.x documentation: https://github.com/silexphp/Pimple/tree/1.1
diff --git a/vendor/pimple/pimple/composer.json b/vendor/pimple/pimple/composer.json
new file mode 100755
index 0000000..dabf190
--- /dev/null
+++ b/vendor/pimple/pimple/composer.json
@@ -0,0 +1,29 @@
+{
+    "name": "pimple/pimple",
+    "type": "library",
+    "description": "Pimple, a simple Dependency Injection Container",
+    "keywords": ["dependency injection", "container"],
+    "homepage": "http://pimple.sensiolabs.org",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Fabien Potencier",
+            "email": "fabien@symfony.com"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.0",
+        "psr/container": "^1.0"
+    },
+    "require-dev": {
+        "symfony/phpunit-bridge": "^3.2"
+    },
+    "autoload": {
+        "psr-0": { "Pimple": "src/" }
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "3.2.x-dev"
+        }
+    }
+}
diff --git a/vendor/pimple/pimple/ext/pimple/.gitignore b/vendor/pimple/pimple/ext/pimple/.gitignore
new file mode 100755
index 0000000..1861088
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/.gitignore
@@ -0,0 +1,30 @@
+*.sw*
+.deps
+Makefile
+Makefile.fragments
+Makefile.global
+Makefile.objects
+acinclude.m4
+aclocal.m4
+build/
+config.cache
+config.guess
+config.h
+config.h.in
+config.log
+config.nice
+config.status
+config.sub
+configure
+configure.in
+install-sh
+libtool
+ltmain.sh
+missing
+mkinstalldirs
+run-tests.php
+*.loT
+.libs/
+modules/
+*.la
+*.lo
diff --git a/vendor/pimple/pimple/ext/pimple/README.md b/vendor/pimple/pimple/ext/pimple/README.md
new file mode 100755
index 0000000..7b39eb2
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/README.md
@@ -0,0 +1,12 @@
+This is Pimple 2 implemented in C
+
+* PHP >= 5.3
+* Not tested under Windows, might work
+
+Install
+=======
+
+    > phpize
+    > ./configure
+    > make
+    > make install
diff --git a/vendor/pimple/pimple/ext/pimple/config.m4 b/vendor/pimple/pimple/ext/pimple/config.m4
new file mode 100755
index 0000000..3a6e9aa
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/config.m4
@@ -0,0 +1,63 @@
+dnl $Id$
+dnl config.m4 for extension pimple
+
+dnl Comments in this file start with the string 'dnl'.
+dnl Remove where necessary. This file will not work
+dnl without editing.
+
+dnl If your extension references something external, use with:
+
+dnl PHP_ARG_WITH(pimple, for pimple support,
+dnl Make sure that the comment is aligned:
+dnl [  --with-pimple             Include pimple support])
+
+dnl Otherwise use enable:
+
+PHP_ARG_ENABLE(pimple, whether to enable pimple support,
+dnl Make sure that the comment is aligned:
+[  --enable-pimple           Enable pimple support])
+
+if test "$PHP_PIMPLE" != "no"; then
+  dnl Write more examples of tests here...
+
+  dnl # --with-pimple -> check with-path
+  dnl SEARCH_PATH="/usr/local /usr"     # you might want to change this
+  dnl SEARCH_FOR="/include/pimple.h"  # you most likely want to change this
+  dnl if test -r $PHP_PIMPLE/$SEARCH_FOR; then # path given as parameter
+  dnl   PIMPLE_DIR=$PHP_PIMPLE
+  dnl else # search default path list
+  dnl   AC_MSG_CHECKING([for pimple files in default path])
+  dnl   for i in $SEARCH_PATH ; do
+  dnl     if test -r $i/$SEARCH_FOR; then
+  dnl       PIMPLE_DIR=$i
+  dnl       AC_MSG_RESULT(found in $i)
+  dnl     fi
+  dnl   done
+  dnl fi
+  dnl
+  dnl if test -z "$PIMPLE_DIR"; then
+  dnl   AC_MSG_RESULT([not found])
+  dnl   AC_MSG_ERROR([Please reinstall the pimple distribution])
+  dnl fi
+
+  dnl # --with-pimple -> add include path
+  dnl PHP_ADD_INCLUDE($PIMPLE_DIR/include)
+
+  dnl # --with-pimple -> check for lib and symbol presence
+  dnl LIBNAME=pimple # you may want to change this
+  dnl LIBSYMBOL=pimple # you most likely want to change this
+
+  dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,
+  dnl [
+  dnl   PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $PIMPLE_DIR/lib, PIMPLE_SHARED_LIBADD)
+  dnl   AC_DEFINE(HAVE_PIMPLELIB,1,[ ])
+  dnl ],[
+  dnl   AC_MSG_ERROR([wrong pimple lib version or lib not found])
+  dnl ],[
+  dnl   -L$PIMPLE_DIR/lib -lm
+  dnl ])
+  dnl
+  dnl PHP_SUBST(PIMPLE_SHARED_LIBADD)
+
+  PHP_NEW_EXTENSION(pimple, pimple.c, $ext_shared)
+fi
diff --git a/vendor/pimple/pimple/ext/pimple/config.w32 b/vendor/pimple/pimple/ext/pimple/config.w32
new file mode 100755
index 0000000..39857b3
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/config.w32
@@ -0,0 +1,13 @@
+// $Id$
+// vim:ft=javascript
+
+// If your extension references something external, use ARG_WITH
+// ARG_WITH("pimple", "for pimple support", "no");
+
+// Otherwise, use ARG_ENABLE
+// ARG_ENABLE("pimple", "enable pimple support", "no");
+
+if (PHP_PIMPLE != "no") {
+	EXTENSION("pimple", "pimple.c");
+}
+
diff --git a/vendor/pimple/pimple/ext/pimple/php_pimple.h b/vendor/pimple/pimple/ext/pimple/php_pimple.h
new file mode 100755
index 0000000..eed7c17
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/php_pimple.h
@@ -0,0 +1,137 @@
+
+/*
+ * This file is part of Pimple.
+ *
+ * Copyright (c) 2014 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.
+ */
+
+#ifndef PHP_PIMPLE_H
+#define PHP_PIMPLE_H
+
+extern zend_module_entry pimple_module_entry;
+#define phpext_pimple_ptr &pimple_module_entry
+
+#ifdef PHP_WIN32
+#	define PHP_PIMPLE_API __declspec(dllexport)
+#elif defined(__GNUC__) && __GNUC__ >= 4
+#	define PHP_PIMPLE_API __attribute__ ((visibility("default")))
+#else
+#	define PHP_PIMPLE_API
+#endif
+
+#ifdef ZTS
+#include "TSRM.h"
+#endif
+
+#define PIMPLE_VERSION "3.2.3-DEV"
+
+#define PIMPLE_NS "Pimple"
+#define PSR_CONTAINER_NS "Psr\\Container"
+#define PIMPLE_EXCEPTION_NS "Pimple\\Exception"
+
+#define PIMPLE_DEFAULT_ZVAL_CACHE_NUM   5
+#define PIMPLE_DEFAULT_ZVAL_VALUES_NUM 10
+
+#define PIMPLE_DEPRECATE do { \
+	int er = EG(error_reporting); \
+	EG(error_reporting) = 0;\
+	php_error(E_DEPRECATED, "The Pimple C extension is deprecated since version 3.1 and will be removed in 4.0."); \
+	EG(error_reporting) = er; \
+} while (0);
+
+zend_module_entry *get_module(void);
+
+PHP_MINIT_FUNCTION(pimple);
+PHP_MINFO_FUNCTION(pimple);
+
+PHP_METHOD(FrozenServiceException, __construct);
+PHP_METHOD(InvalidServiceIdentifierException, __construct);
+PHP_METHOD(UnknownIdentifierException, __construct);
+
+PHP_METHOD(Pimple, __construct);
+PHP_METHOD(Pimple, factory);
+PHP_METHOD(Pimple, protect);
+PHP_METHOD(Pimple, raw);
+PHP_METHOD(Pimple, extend);
+PHP_METHOD(Pimple, keys);
+PHP_METHOD(Pimple, register);
+PHP_METHOD(Pimple, offsetSet);
+PHP_METHOD(Pimple, offsetUnset);
+PHP_METHOD(Pimple, offsetGet);
+PHP_METHOD(Pimple, offsetExists);
+
+PHP_METHOD(PimpleClosure, invoker);
+
+typedef struct _pimple_bucket_value {
+	zval *value; /* Must be the first element */
+	zval *raw;
+	zend_object_handle handle_num;
+	enum {
+		PIMPLE_IS_PARAM   = 0,
+		PIMPLE_IS_SERVICE = 2
+	} type;
+	zend_bool initialized;
+	zend_fcall_info_cache fcc;
+} pimple_bucket_value;
+
+typedef struct _pimple_object {
+	zend_object zobj;
+	HashTable values;
+	HashTable factories;
+	HashTable protected;
+} pimple_object;
+
+typedef struct _pimple_closure_object {
+	zend_object zobj;
+	zval *callable;
+	zval *factory;
+} pimple_closure_object;
+
+static const char sensiolabs_logo[] = "<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHYAAAAUCAMAAABvRTlyAAAAz1BMVEUAAAAAAAAAAAAsThWB5j4AAACD6T8AAACC6D+C6D6C6D+C6D4AAAAAAACC6D4AAAAAAACC6D8AAAAAAAAAAAAAAAAAAAAAAACC6D4AAAAAAAAAAACC6D4AAAAAAAAAAAAAAAAAAAAAAACC6D8AAACC6D4AAAAAAAAAAAAAAAAAAACC6D8AAACC6D6C6D+B6D+C6D+C6D+C6D8AAACC6D6C6D4AAACC6D/K/2KC6D+B6D6C6D6C6D+C6D8sTxUyWRhEeiEAAACC6D+C5z6B6D7drnEVAAAAQXRSTlMAE3oCNSUuDHFHzxaF9UFsu+irX+zlKzYimaJXktyOSFD6BolxqT7QGMMdarMIpuO28r9EolXKgR16OphfXYd4V14GtB4AAAMpSURBVEjHvVSJctowEF1jjME2RziMwUCoMfd9heZqG4n//6buLpJjkmYm03byZmxJa2nf6u2uQcG2bfhqRN4LoTKBzyGDm68M7mAwcOEdjo4zhA/Rf9Go/CVtTgiRhXfIC3EDH8F/eUX1/9KexRo+QgOdtHDsEe/sM7QT32/+K61Z1LFXcXJxN4pTbu1aTQUzuy2PIA0rDo0/0Aa5XFaJvKaVTrubywXvaa1Wq4Vu/Snr3Y7Aojh4VccwykW2N2oQ8wmjyut6+Q1t5ywIG5Npj1sh5E0B7YOzFDjfuRfaOh3O+MbbVNfTWS9COZk3Obd2su5d0a6IU9KLREbw8gEehWSr1r2sPWciXLG38r5NdW0xu9eioU87omjC9yNaMi5GNf6WppVSOqXCFkmCvMB3p9SROLoYQn5pDgQOujA1xjYvqH+plUdkwnmII8VxR/PKYkrfLLomhVlE3b/LhNbNr7hp0H2JaOc4v8dFB58HSsFTSafaqtY1sT3GO8wsy5rhokYPlRJdjPMajyYqTt1EHF/2uqSWQWmAjCUSmQ1MS3g8Btf1XOsy7YIC0CB1b5Xw1Vhba0zbxiCAQLH9TNPmHJXQUtJAN0KcDsoqLxsNvJrJExa7mKIdp2lRE2WexiS4pqWk/0jROlw6K6bV9YOBDGAuqMJ0bnuUKGB0L27bxgRhGEbzihbhxxXaQC88Vkwq8ldCi86RApWUb0Q+4VDosBCc+1s81lUdnBavH4Zp2mm3O44USwOfvSo9oBiwpFg71lMS1VKJLKljS3j9p+fOTvXXlsSNuEv6YPaZda9uRope0VJfKdo7fPiYfSmvFjXQbkhY0d9hCbBWIktRgEDieDhf1N3wbbkmNNgRy8hyl620yGQat/grV3HMpc2HDKTVmOPFz6ylPCKt/nXcAyV260jaAowwIW0YuBzrOgb/KrddZS9OmJaLgpWK4JX2DDuklcLZSDGcn8Vmx9YDNvT6UsjyBApRyFQVX7Vxm9TGxE16nmfRd8/zQoDmggQOTRh5Hv8pMt9Q/L2JmSwkMCE7dA4BuDjHJwfu0Om4QAhOjrN5XkIatglfiN/bUPdCQFjTYgAAAABJRU5ErkJggg==\">";
+
+static void pimple_exception_call_parent_constructor(zval *this_ptr, const char *format, const char *arg1 TSRMLS_DC);
+
+static int pimple_zval_to_pimpleval(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC);
+static int pimple_zval_is_valid_callback(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC);
+
+static void pimple_bucket_dtor(pimple_bucket_value *bucket);
+static void pimple_free_bucket(pimple_bucket_value *bucket);
+
+static zval *pimple_object_read_dimension(zval *object, zval *offset, int type TSRMLS_DC);
+static void pimple_object_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC);
+static int pimple_object_has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC);
+static void pimple_object_unset_dimension(zval *object, zval *offset TSRMLS_DC);
+static zend_object_value pimple_object_create(zend_class_entry *ce TSRMLS_DC);
+static void pimple_free_object_storage(pimple_object *obj TSRMLS_DC);
+
+static void pimple_closure_free_object_storage(pimple_closure_object *obj TSRMLS_DC);
+static zend_object_value pimple_closure_object_create(zend_class_entry *ce TSRMLS_DC);
+static zend_function *pimple_closure_get_constructor(zval * TSRMLS_DC);
+static int pimple_closure_get_closure(zval *obj, zend_class_entry **ce_ptr, union _zend_function **fptr_ptr, zval **zobj_ptr TSRMLS_DC);
+
+#ifdef ZTS
+#define PIMPLE_G(v) TSRMG(pimple_globals_id, zend_pimple_globals *, v)
+#else
+#define PIMPLE_G(v) (pimple_globals.v)
+#endif
+
+#endif	/* PHP_PIMPLE_H */
+
diff --git a/vendor/pimple/pimple/ext/pimple/pimple.c b/vendor/pimple/pimple/ext/pimple/pimple.c
new file mode 100755
index 0000000..c80499b
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/pimple.c
@@ -0,0 +1,1114 @@
+
+/*
+ * This file is part of Pimple.
+ *
+ * Copyright (c) 2014 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "php_pimple.h"
+#include "pimple_compat.h"
+#include "zend_interfaces.h"
+#include "zend.h"
+#include "Zend/zend_closures.h"
+#include "ext/spl/spl_exceptions.h"
+#include "Zend/zend_exceptions.h"
+#include "main/php_output.h"
+#include "SAPI.h"
+
+static zend_class_entry *pimple_ce_PsrContainerInterface;
+static zend_class_entry *pimple_ce_PsrContainerExceptionInterface;
+static zend_class_entry *pimple_ce_PsrNotFoundExceptionInterface;
+
+static zend_class_entry *pimple_ce_ExpectedInvokableException;
+static zend_class_entry *pimple_ce_FrozenServiceException;
+static zend_class_entry *pimple_ce_InvalidServiceIdentifierException;
+static zend_class_entry *pimple_ce_UnknownIdentifierException;
+
+static zend_class_entry *pimple_ce;
+static zend_object_handlers pimple_object_handlers;
+static zend_class_entry *pimple_closure_ce;
+static zend_class_entry *pimple_serviceprovider_ce;
+static zend_object_handlers pimple_closure_object_handlers;
+static zend_internal_function pimple_closure_invoker_function;
+
+#define FETCH_DIM_HANDLERS_VARS 	pimple_object *pimple_obj = NULL; \
+									ulong index; \
+									pimple_obj = (pimple_object *)zend_object_store_get_object(object TSRMLS_CC); \
+
+#define PIMPLE_OBJECT_HANDLE_INHERITANCE_OBJECT_HANDLERS	do { \
+	if (ce != pimple_ce) { \
+		zend_hash_find(&ce->function_table, ZEND_STRS("offsetget"), (void **)&function); \
+		if (function->common.scope != ce) { /* if the function is not defined in this actual class */ \
+			pimple_object_handlers.read_dimension = pimple_object_read_dimension; /* then overwrite the handler to use custom one */ \
+		} \
+		zend_hash_find(&ce->function_table, ZEND_STRS("offsetset"), (void **)&function); \
+		if (function->common.scope != ce) { \
+			pimple_object_handlers.write_dimension = pimple_object_write_dimension; \
+		} \
+		zend_hash_find(&ce->function_table, ZEND_STRS("offsetexists"), (void **)&function); \
+		if (function->common.scope != ce) { \
+			pimple_object_handlers.has_dimension = pimple_object_has_dimension; \
+		} \
+		zend_hash_find(&ce->function_table, ZEND_STRS("offsetunset"), (void **)&function); \
+		if (function->common.scope != ce) { \
+			pimple_object_handlers.unset_dimension = pimple_object_unset_dimension; \
+		} \
+	} else { \
+		pimple_object_handlers.read_dimension = pimple_object_read_dimension; \
+		pimple_object_handlers.write_dimension = pimple_object_write_dimension; \
+		pimple_object_handlers.has_dimension = pimple_object_has_dimension; \
+		pimple_object_handlers.unset_dimension = pimple_object_unset_dimension; \
+	}\
+											} while(0);
+
+#define PIMPLE_CALL_CB	do { \
+			zend_fcall_info_argn(&fci TSRMLS_CC, 1, &object); \
+			fci.size           = sizeof(fci); \
+			fci.object_ptr     = retval->fcc.object_ptr; \
+			fci.function_name  = retval->value; \
+			fci.no_separation  = 1; \
+			fci.retval_ptr_ptr = &retval_ptr_ptr; \
+\
+			zend_call_function(&fci, &retval->fcc TSRMLS_CC); \
+			efree(fci.params); \
+			if (EG(exception)) { \
+				return EG(uninitialized_zval_ptr); \
+			} \
+						} while(0);
+
+
+/* Psr\Container\ContainerInterface */
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pimple_PsrContainerInterface_get, 0, 0, 1)
+ZEND_ARG_INFO(0, id)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pimple_PsrContainerInterface_has, 0, 0, 1)
+ZEND_ARG_INFO(0, id)
+ZEND_END_ARG_INFO()
+
+static const zend_function_entry pimple_ce_PsrContainerInterface_functions[] = {
+	PHP_ABSTRACT_ME(ContainerInterface, get, arginfo_pimple_PsrContainerInterface_get)
+	PHP_ABSTRACT_ME(ContainerInterface, has, arginfo_pimple_PsrContainerInterface_has)
+	PHP_FE_END
+};
+
+/* Psr\Container\ContainerExceptionInterface */
+static const zend_function_entry pimple_ce_PsrContainerExceptionInterface_functions[] = {
+	PHP_FE_END
+};
+
+/* Psr\Container\NotFoundExceptionInterface */
+static const zend_function_entry pimple_ce_PsrNotFoundExceptionInterface_functions[] = {
+	PHP_FE_END
+};
+
+/* Pimple\Exception\FrozenServiceException */
+ZEND_BEGIN_ARG_INFO_EX(arginfo_FrozenServiceException___construct, 0, 0, 1)
+ZEND_ARG_INFO(0, id)
+ZEND_END_ARG_INFO()
+
+static const zend_function_entry pimple_ce_FrozenServiceException_functions[] = {
+	PHP_ME(FrozenServiceException, __construct, arginfo_FrozenServiceException___construct, ZEND_ACC_PUBLIC)
+	PHP_FE_END
+};
+
+/* Pimple\Exception\InvalidServiceIdentifierException */
+ZEND_BEGIN_ARG_INFO_EX(arginfo_InvalidServiceIdentifierException___construct, 0, 0, 1)
+ZEND_ARG_INFO(0, id)
+ZEND_END_ARG_INFO()
+
+static const zend_function_entry pimple_ce_InvalidServiceIdentifierException_functions[] = {
+	PHP_ME(InvalidServiceIdentifierException, __construct, arginfo_InvalidServiceIdentifierException___construct, ZEND_ACC_PUBLIC)
+	PHP_FE_END
+};
+
+/* Pimple\Exception\UnknownIdentifierException */
+ZEND_BEGIN_ARG_INFO_EX(arginfo_UnknownIdentifierException___construct, 0, 0, 1)
+ZEND_ARG_INFO(0, id)
+ZEND_END_ARG_INFO()
+
+static const zend_function_entry pimple_ce_UnknownIdentifierException_functions[] = {
+	PHP_ME(UnknownIdentifierException, __construct, arginfo_UnknownIdentifierException___construct, ZEND_ACC_PUBLIC)
+	PHP_FE_END
+};
+
+/* Pimple\Container */
+ZEND_BEGIN_ARG_INFO_EX(arginfo___construct, 0, 0, 0)
+ZEND_ARG_ARRAY_INFO(0, value, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetset, 0, 0, 2)
+ZEND_ARG_INFO(0, offset)
+ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetget, 0, 0, 1)
+ZEND_ARG_INFO(0, offset)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetexists, 0, 0, 1)
+ZEND_ARG_INFO(0, offset)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetunset, 0, 0, 1)
+ZEND_ARG_INFO(0, offset)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_factory, 0, 0, 1)
+ZEND_ARG_INFO(0, callable)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_protect, 0, 0, 1)
+ZEND_ARG_INFO(0, callable)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_raw, 0, 0, 1)
+ZEND_ARG_INFO(0, id)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_extend, 0, 0, 2)
+ZEND_ARG_INFO(0, id)
+ZEND_ARG_INFO(0, callable)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_keys, 0, 0, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_register, 0, 0, 1)
+ZEND_ARG_OBJ_INFO(0, provider, Pimple\\ServiceProviderInterface, 0)
+ZEND_ARG_ARRAY_INFO(0, values, 1)
+ZEND_END_ARG_INFO()
+
+static const zend_function_entry pimple_ce_functions[] = {
+	PHP_ME(Pimple, __construct,	arginfo___construct, ZEND_ACC_PUBLIC)
+	PHP_ME(Pimple, factory,         arginfo_factory,         ZEND_ACC_PUBLIC)
+	PHP_ME(Pimple, protect,         arginfo_protect,         ZEND_ACC_PUBLIC)
+	PHP_ME(Pimple, raw,             arginfo_raw,             ZEND_ACC_PUBLIC)
+	PHP_ME(Pimple, extend,          arginfo_extend,          ZEND_ACC_PUBLIC)
+	PHP_ME(Pimple, keys,            arginfo_keys,            ZEND_ACC_PUBLIC)
+	PHP_ME(Pimple, register,		arginfo_register,		 ZEND_ACC_PUBLIC)
+
+	PHP_ME(Pimple, offsetSet,       arginfo_offsetset,       ZEND_ACC_PUBLIC)
+	PHP_ME(Pimple, offsetGet,       arginfo_offsetget,       ZEND_ACC_PUBLIC)
+	PHP_ME(Pimple, offsetExists,    arginfo_offsetexists,    ZEND_ACC_PUBLIC)
+	PHP_ME(Pimple, offsetUnset,     arginfo_offsetunset,     ZEND_ACC_PUBLIC)
+	PHP_FE_END
+};
+
+/* Pimple\ServiceProviderInterface */
+ZEND_BEGIN_ARG_INFO_EX(arginfo_serviceprovider_register, 0, 0, 1)
+ZEND_ARG_OBJ_INFO(0, pimple, Pimple\\Container, 0)
+ZEND_END_ARG_INFO()
+
+static const zend_function_entry pimple_serviceprovider_iface_ce_functions[] = {
+	PHP_ABSTRACT_ME(ServiceProviderInterface, register, arginfo_serviceprovider_register)
+	PHP_FE_END
+};
+
+/* parent::__construct(sprintf("Something with %s", $arg1)) */
+static void pimple_exception_call_parent_constructor(zval *this_ptr, const char *format, const char *arg1 TSRMLS_DC)
+{
+	zend_class_entry *ce = Z_OBJCE_P(this_ptr);
+	char *message = NULL;
+	int message_len;
+	zval *constructor_arg;
+
+	message_len = spprintf(&message, 0, format, arg1);
+	ALLOC_INIT_ZVAL(constructor_arg);
+	ZVAL_STRINGL(constructor_arg, message, message_len, 1);
+
+	zend_call_method_with_1_params(&this_ptr, ce, &ce->parent->constructor, "__construct", NULL, constructor_arg);
+
+	efree(message);
+	zval_ptr_dtor(&constructor_arg);
+}
+
+/**
+ * Pass a single string parameter to exception constructor and throw
+ */
+static void pimple_throw_exception_string(zend_class_entry *ce, const char *message, zend_uint message_len TSRMLS_DC)
+{
+	zval *exception, *param;
+
+	ALLOC_INIT_ZVAL(exception);
+	object_init_ex(exception, ce);
+
+	ALLOC_INIT_ZVAL(param);
+	ZVAL_STRINGL(param, message, message_len, 1);
+
+	zend_call_method_with_1_params(&exception, ce, &ce->constructor, "__construct", NULL, param);
+
+	zend_throw_exception_object(exception TSRMLS_CC);
+
+	zval_ptr_dtor(&param);
+}
+
+static void pimple_closure_free_object_storage(pimple_closure_object *obj TSRMLS_DC)
+{
+	zend_object_std_dtor(&obj->zobj TSRMLS_CC);
+	if (obj->factory) {
+		zval_ptr_dtor(&obj->factory);
+	}
+	if (obj->callable) {
+		zval_ptr_dtor(&obj->callable);
+	}
+	efree(obj);
+}
+
+static void pimple_free_object_storage(pimple_object *obj TSRMLS_DC)
+{
+	zend_hash_destroy(&obj->factories);
+	zend_hash_destroy(&obj->protected);
+	zend_hash_destroy(&obj->values);
+	zend_object_std_dtor(&obj->zobj TSRMLS_CC);
+	efree(obj);
+}
+
+static void pimple_free_bucket(pimple_bucket_value *bucket)
+{
+	if (bucket->raw) {
+		zval_ptr_dtor(&bucket->raw);
+	}
+}
+
+static zend_object_value pimple_closure_object_create(zend_class_entry *ce TSRMLS_DC)
+{
+	zend_object_value retval;
+	pimple_closure_object *pimple_closure_obj = NULL;
+
+	pimple_closure_obj = ecalloc(1, sizeof(pimple_closure_object));
+	ZEND_OBJ_INIT(&pimple_closure_obj->zobj, ce);
+
+	pimple_closure_object_handlers.get_constructor = pimple_closure_get_constructor;
+	retval.handlers = &pimple_closure_object_handlers;
+	retval.handle   = zend_objects_store_put(pimple_closure_obj, (zend_objects_store_dtor_t) zend_objects_destroy_object, (zend_objects_free_object_storage_t) pimple_closure_free_object_storage, NULL TSRMLS_CC);
+
+	return retval;
+}
+
+static zend_function *pimple_closure_get_constructor(zval *obj TSRMLS_DC)
+{
+	zend_error(E_ERROR, "Pimple\\ContainerClosure is an internal class and cannot be instantiated");
+
+	return NULL;
+}
+
+static int pimple_closure_get_closure(zval *obj, zend_class_entry **ce_ptr, union _zend_function **fptr_ptr, zval **zobj_ptr TSRMLS_DC)
+{
+	*zobj_ptr = obj;
+	*ce_ptr   = Z_OBJCE_P(obj);
+	*fptr_ptr = (zend_function *)&pimple_closure_invoker_function;
+
+	return SUCCESS;
+}
+
+static zend_object_value pimple_object_create(zend_class_entry *ce TSRMLS_DC)
+{
+	zend_object_value retval;
+	pimple_object *pimple_obj  = NULL;
+	zend_function *function    = NULL;
+
+	pimple_obj = emalloc(sizeof(pimple_object));
+	ZEND_OBJ_INIT(&pimple_obj->zobj, ce);
+
+	PIMPLE_OBJECT_HANDLE_INHERITANCE_OBJECT_HANDLERS
+
+	retval.handlers = &pimple_object_handlers;
+	retval.handle   = zend_objects_store_put(pimple_obj, (zend_objects_store_dtor_t) zend_objects_destroy_object, (zend_objects_free_object_storage_t) pimple_free_object_storage, NULL TSRMLS_CC);
+
+	zend_hash_init(&pimple_obj->factories, PIMPLE_DEFAULT_ZVAL_CACHE_NUM, NULL, (dtor_func_t)pimple_bucket_dtor, 0);
+	zend_hash_init(&pimple_obj->protected, PIMPLE_DEFAULT_ZVAL_CACHE_NUM, NULL, (dtor_func_t)pimple_bucket_dtor, 0);
+	zend_hash_init(&pimple_obj->values, PIMPLE_DEFAULT_ZVAL_VALUES_NUM, NULL, (dtor_func_t)pimple_bucket_dtor, 0);
+
+	return retval;
+}
+
+static void pimple_object_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC)
+{
+	FETCH_DIM_HANDLERS_VARS
+
+	pimple_bucket_value pimple_value = {0}, *found_value = NULL;
+	ulong hash;
+
+	pimple_zval_to_pimpleval(value, &pimple_value TSRMLS_CC);
+
+	if (!offset) {/* $p[] = 'foo' when not overloaded */
+		zend_hash_next_index_insert(&pimple_obj->values, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL);
+		Z_ADDREF_P(value);
+		return;
+	}
+
+	switch (Z_TYPE_P(offset)) {
+	case IS_STRING:
+		hash = zend_hash_func(Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1);
+		zend_hash_quick_find(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, hash, (void **)&found_value);
+		if (found_value && found_value->type == PIMPLE_IS_SERVICE && found_value->initialized == 1) {
+			pimple_free_bucket(&pimple_value);
+			pimple_throw_exception_string(pimple_ce_FrozenServiceException, Z_STRVAL_P(offset), Z_STRLEN_P(offset) TSRMLS_CC);
+			return;
+		}
+		if (zend_hash_quick_update(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, hash, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL) == FAILURE) {
+			pimple_free_bucket(&pimple_value);
+			return;
+		}
+		Z_ADDREF_P(value);
+	break;
+	case IS_DOUBLE:
+	case IS_BOOL:
+	case IS_LONG:
+		if (Z_TYPE_P(offset) == IS_DOUBLE) {
+			index = (ulong)Z_DVAL_P(offset);
+		} else {
+			index = Z_LVAL_P(offset);
+		}
+		zend_hash_index_find(&pimple_obj->values, index, (void **)&found_value);
+		if (found_value && found_value->type == PIMPLE_IS_SERVICE && found_value->initialized == 1) {
+			pimple_free_bucket(&pimple_value);
+			convert_to_string(offset);
+			pimple_throw_exception_string(pimple_ce_FrozenServiceException, Z_STRVAL_P(offset), Z_STRLEN_P(offset) TSRMLS_CC);
+			return;
+		}
+		if (zend_hash_index_update(&pimple_obj->values, index, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL) == FAILURE) {
+			pimple_free_bucket(&pimple_value);
+			return;
+		}
+		Z_ADDREF_P(value);
+	break;
+	case IS_NULL: /* $p[] = 'foo' when overloaded */
+		zend_hash_next_index_insert(&pimple_obj->values, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL);
+		Z_ADDREF_P(value);
+	break;
+	default:
+		pimple_free_bucket(&pimple_value);
+		zend_error(E_WARNING, "Unsupported offset type");
+	}
+}
+
+static void pimple_object_unset_dimension(zval *object, zval *offset TSRMLS_DC)
+{
+	FETCH_DIM_HANDLERS_VARS
+
+	switch (Z_TYPE_P(offset)) {
+	case IS_STRING:
+		zend_symtable_del(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1);
+		zend_symtable_del(&pimple_obj->factories, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1);
+		zend_symtable_del(&pimple_obj->protected, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1);
+	break;
+	case IS_DOUBLE:
+	case IS_BOOL:
+	case IS_LONG:
+		if (Z_TYPE_P(offset) == IS_DOUBLE) {
+			index = (ulong)Z_DVAL_P(offset);
+		} else {
+			index = Z_LVAL_P(offset);
+		}
+		zend_hash_index_del(&pimple_obj->values, index);
+		zend_hash_index_del(&pimple_obj->factories, index);
+		zend_hash_index_del(&pimple_obj->protected, index);
+	break;
+	default:
+		zend_error(E_WARNING, "Unsupported offset type");
+	}
+}
+
+static int pimple_object_has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC)
+{
+	FETCH_DIM_HANDLERS_VARS
+
+	pimple_bucket_value *retval = NULL;
+
+	switch (Z_TYPE_P(offset)) {
+	case IS_STRING:
+		if (zend_symtable_find(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void **)&retval) == SUCCESS) {
+			switch (check_empty) {
+			case 0: /* isset */
+				return 1; /* Differs from PHP behavior (Z_TYPE_P(retval->value) != IS_NULL;) */
+			case 1: /* empty */
+			default:
+				return zend_is_true(retval->value);
+			}
+		}
+		return 0;
+	break;
+	case IS_DOUBLE:
+	case IS_BOOL:
+	case IS_LONG:
+		if (Z_TYPE_P(offset) == IS_DOUBLE) {
+			index = (ulong)Z_DVAL_P(offset);
+		} else {
+			index = Z_LVAL_P(offset);
+		}
+		if (zend_hash_index_find(&pimple_obj->values, index, (void **)&retval) == SUCCESS) {
+			switch (check_empty) {
+				case 0: /* isset */
+					return 1; /* Differs from PHP behavior (Z_TYPE_P(retval->value) != IS_NULL;)*/
+				case 1: /* empty */
+				default:
+					return zend_is_true(retval->value);
+			}
+		}
+		return 0;
+	break;
+	default:
+		zend_error(E_WARNING, "Unsupported offset type");
+		return 0;
+	}
+}
+
+static zval *pimple_object_read_dimension(zval *object, zval *offset, int type TSRMLS_DC)
+{
+	FETCH_DIM_HANDLERS_VARS
+
+	pimple_bucket_value *retval = NULL;
+	zend_fcall_info fci         = {0};
+	zval *retval_ptr_ptr        = NULL;
+
+	switch (Z_TYPE_P(offset)) {
+	case IS_STRING:
+		if (zend_symtable_find(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void **)&retval) == FAILURE) {
+			pimple_throw_exception_string(pimple_ce_UnknownIdentifierException, Z_STRVAL_P(offset), Z_STRLEN_P(offset) TSRMLS_CC);
+
+			return EG(uninitialized_zval_ptr);
+		}
+	break;
+	case IS_DOUBLE:
+	case IS_BOOL:
+	case IS_LONG:
+		if (Z_TYPE_P(offset) == IS_DOUBLE) {
+			index = (ulong)Z_DVAL_P(offset);
+		} else {
+			index = Z_LVAL_P(offset);
+		}
+		if (zend_hash_index_find(&pimple_obj->values, index, (void **)&retval) == FAILURE) {
+			return EG(uninitialized_zval_ptr);
+		}
+	break;
+	case IS_NULL: /* $p[][3] = 'foo' first dim access */
+		return EG(uninitialized_zval_ptr);
+	break;
+	default:
+		zend_error(E_WARNING, "Unsupported offset type");
+		return EG(uninitialized_zval_ptr);
+	}
+
+	if(retval->type == PIMPLE_IS_PARAM) {
+		return retval->value;
+	}
+
+	if (zend_hash_index_exists(&pimple_obj->protected, retval->handle_num)) {
+		/* Service is protected, return the value every time */
+		return retval->value;
+	}
+
+	if (zend_hash_index_exists(&pimple_obj->factories, retval->handle_num)) {
+		/* Service is a factory, call it every time and never cache its result */
+		PIMPLE_CALL_CB
+		Z_DELREF_P(retval_ptr_ptr); /* fetch dim addr will increment refcount */
+		return retval_ptr_ptr;
+	}
+
+	if (retval->initialized == 1) {
+		/* Service has already been called, return its cached value */
+		return retval->value;
+	}
+
+	ALLOC_INIT_ZVAL(retval->raw);
+	MAKE_COPY_ZVAL(&retval->value, retval->raw);
+
+	PIMPLE_CALL_CB
+
+	retval->initialized = 1;
+	zval_ptr_dtor(&retval->value);
+	retval->value = retval_ptr_ptr;
+
+	return retval->value;
+}
+
+static int pimple_zval_is_valid_callback(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC)
+{
+	if (Z_TYPE_P(_zval) != IS_OBJECT) {
+		return FAILURE;
+	}
+
+	if (_pimple_bucket_value->fcc.called_scope) {
+		return SUCCESS;
+	}
+
+	if (Z_OBJ_HANDLER_P(_zval, get_closure) && Z_OBJ_HANDLER_P(_zval, get_closure)(_zval, &_pimple_bucket_value->fcc.calling_scope, &_pimple_bucket_value->fcc.function_handler, &_pimple_bucket_value->fcc.object_ptr TSRMLS_CC) == SUCCESS) {
+		_pimple_bucket_value->fcc.called_scope = _pimple_bucket_value->fcc.calling_scope;
+		return SUCCESS;
+	} else {
+		return FAILURE;
+	}
+}
+
+static int pimple_zval_to_pimpleval(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC)
+{
+	_pimple_bucket_value->value = _zval;
+
+	if (Z_TYPE_P(_zval) != IS_OBJECT) {
+		return PIMPLE_IS_PARAM;
+	}
+
+	if (pimple_zval_is_valid_callback(_zval, _pimple_bucket_value TSRMLS_CC) == SUCCESS) {
+		_pimple_bucket_value->type       = PIMPLE_IS_SERVICE;
+		_pimple_bucket_value->handle_num = Z_OBJ_HANDLE_P(_zval);
+	}
+
+	return PIMPLE_IS_SERVICE;
+}
+
+static void pimple_bucket_dtor(pimple_bucket_value *bucket)
+{
+	zval_ptr_dtor(&bucket->value);
+	pimple_free_bucket(bucket);
+}
+
+PHP_METHOD(FrozenServiceException, __construct)
+{
+	char *id = NULL;
+	int id_len;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &id, &id_len) == FAILURE) {
+		return;
+	}
+	pimple_exception_call_parent_constructor(getThis(), "Cannot override frozen service \"%s\".", id TSRMLS_CC);
+}
+
+PHP_METHOD(InvalidServiceIdentifierException, __construct)
+{
+	char *id = NULL;
+	int id_len;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &id, &id_len) == FAILURE) {
+		return;
+	}
+	pimple_exception_call_parent_constructor(getThis(), "Identifier \"%s\" does not contain an object definition.", id TSRMLS_CC);
+}
+
+PHP_METHOD(UnknownIdentifierException, __construct)
+{
+	char *id = NULL;
+	int id_len;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &id, &id_len) == FAILURE) {
+		return;
+	}
+	pimple_exception_call_parent_constructor(getThis(), "Identifier \"%s\" is not defined.", id TSRMLS_CC);
+}
+
+PHP_METHOD(Pimple, protect)
+{
+	zval *protected     = NULL;
+	pimple_object *pobj = NULL;
+	pimple_bucket_value bucket = {0};
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &protected) == FAILURE) {
+		return;
+	}
+
+	if (pimple_zval_is_valid_callback(protected, &bucket TSRMLS_CC) == FAILURE) {
+		pimple_free_bucket(&bucket);
+		zend_throw_exception(pimple_ce_ExpectedInvokableException, "Callable is not a Closure or invokable object.", 0 TSRMLS_CC);
+		return;
+	}
+
+	pimple_zval_to_pimpleval(protected, &bucket TSRMLS_CC);
+	pobj = (pimple_object *)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+	if (zend_hash_index_update(&pobj->protected, bucket.handle_num, (void *)&bucket, sizeof(pimple_bucket_value), NULL) == SUCCESS) {
+		Z_ADDREF_P(protected);
+		RETURN_ZVAL(protected, 1 , 0);
+	} else {
+		pimple_free_bucket(&bucket);
+	}
+	RETURN_FALSE;
+}
+
+PHP_METHOD(Pimple, raw)
+{
+	zval *offset = NULL;
+	pimple_object *pobj        = NULL;
+	pimple_bucket_value *value = NULL;
+	ulong index;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) {
+		return;
+	}
+
+	pobj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+	switch (Z_TYPE_P(offset)) {
+		case IS_STRING:
+			if (zend_symtable_find(&pobj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void *)&value) == FAILURE) {
+				pimple_throw_exception_string(pimple_ce_UnknownIdentifierException, Z_STRVAL_P(offset), Z_STRLEN_P(offset) TSRMLS_CC);
+				RETURN_NULL();
+			}
+		break;
+		case IS_DOUBLE:
+		case IS_BOOL:
+		case IS_LONG:
+			if (Z_TYPE_P(offset) == IS_DOUBLE) {
+				index = (ulong)Z_DVAL_P(offset);
+			} else {
+				index = Z_LVAL_P(offset);
+			}
+			if (zend_hash_index_find(&pobj->values, index, (void *)&value) == FAILURE) {
+				RETURN_NULL();
+			}
+		break;
+		case IS_NULL:
+		default:
+			zend_error(E_WARNING, "Unsupported offset type");
+	}
+
+	if (value->raw) {
+		RETVAL_ZVAL(value->raw, 1, 0);
+	} else {
+		RETVAL_ZVAL(value->value, 1, 0);
+	}
+}
+
+PHP_METHOD(Pimple, extend)
+{
+	zval *offset = NULL, *callable = NULL, *pimple_closure_obj = NULL;
+	pimple_bucket_value bucket = {0}, *value = NULL;
+	pimple_object *pobj          = NULL;
+	pimple_closure_object *pcobj = NULL;
+	ulong index;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &offset, &callable) == FAILURE) {
+		return;
+	}
+
+	pobj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+	switch (Z_TYPE_P(offset)) {
+		case IS_STRING:
+			if (zend_symtable_find(&pobj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void *)&value) == FAILURE) {
+				pimple_throw_exception_string(pimple_ce_UnknownIdentifierException, Z_STRVAL_P(offset), Z_STRLEN_P(offset) TSRMLS_CC);
+				RETURN_NULL();
+			}
+
+			if (value->type != PIMPLE_IS_SERVICE) {
+				pimple_throw_exception_string(pimple_ce_InvalidServiceIdentifierException, Z_STRVAL_P(offset), Z_STRLEN_P(offset) TSRMLS_CC);
+				RETURN_NULL();
+			}
+			if (zend_hash_index_exists(&pobj->protected, value->handle_num)) {
+				int er = EG(error_reporting);
+				EG(error_reporting) = 0;
+				php_error(E_DEPRECATED, "How Pimple behaves when extending protected closures will be fixed in Pimple 4. Are you sure \"%s\" should be protected?", Z_STRVAL_P(offset));
+				EG(error_reporting) = er;
+			}
+		break;
+		case IS_DOUBLE:
+		case IS_BOOL:
+		case IS_LONG:
+			if (Z_TYPE_P(offset) == IS_DOUBLE) {
+				index = (ulong)Z_DVAL_P(offset);
+			} else {
+				index = Z_LVAL_P(offset);
+			}
+			if (zend_hash_index_find(&pobj->values, index, (void *)&value) == FAILURE) {
+				convert_to_string(offset);
+				pimple_throw_exception_string(pimple_ce_UnknownIdentifierException, Z_STRVAL_P(offset), Z_STRLEN_P(offset) TSRMLS_CC);
+				RETURN_NULL();
+			}
+			if (value->type != PIMPLE_IS_SERVICE) {
+				convert_to_string(offset);
+				pimple_throw_exception_string(pimple_ce_InvalidServiceIdentifierException, Z_STRVAL_P(offset), Z_STRLEN_P(offset) TSRMLS_CC);
+				RETURN_NULL();
+			}
+			if (zend_hash_index_exists(&pobj->protected, value->handle_num)) {
+				int er = EG(error_reporting);
+				EG(error_reporting) = 0;
+				php_error(E_DEPRECATED, "How Pimple behaves when extending protected closures will be fixed in Pimple 4. Are you sure \"%ld\" should be protected?", index);
+				EG(error_reporting) = er;
+			}
+		break;
+		case IS_NULL:
+		default:
+			zend_error(E_WARNING, "Unsupported offset type");
+	}
+
+	if (pimple_zval_is_valid_callback(callable, &bucket TSRMLS_CC) == FAILURE) {
+		pimple_free_bucket(&bucket);
+		zend_throw_exception(pimple_ce_ExpectedInvokableException, "Extension service definition is not a Closure or invokable object.", 0 TSRMLS_CC);
+		RETURN_NULL();
+	}
+	pimple_free_bucket(&bucket);
+
+	ALLOC_INIT_ZVAL(pimple_closure_obj);
+	object_init_ex(pimple_closure_obj, pimple_closure_ce);
+
+	pcobj = zend_object_store_get_object(pimple_closure_obj TSRMLS_CC);
+	pcobj->callable = callable;
+	pcobj->factory  = value->value;
+	Z_ADDREF_P(callable);
+	Z_ADDREF_P(value->value);
+
+	if (zend_hash_index_exists(&pobj->factories, value->handle_num)) {
+		pimple_zval_to_pimpleval(pimple_closure_obj, &bucket TSRMLS_CC);
+		zend_hash_index_del(&pobj->factories, value->handle_num);
+		zend_hash_index_update(&pobj->factories, bucket.handle_num, (void *)&bucket, sizeof(pimple_bucket_value), NULL);
+		Z_ADDREF_P(pimple_closure_obj);
+	}
+
+	pimple_object_write_dimension(getThis(), offset, pimple_closure_obj TSRMLS_CC);
+
+	RETVAL_ZVAL(pimple_closure_obj, 1, 1);
+}
+
+PHP_METHOD(Pimple, keys)
+{
+	HashPosition pos;
+	pimple_object *pobj = NULL;
+	zval **value        = NULL;
+	zval *endval        = NULL;
+	char *str_index     = NULL;
+	int str_len;
+	ulong num_index;
+
+	if (zend_parse_parameters_none() == FAILURE) {
+		return;
+	}
+
+	pobj = zend_object_store_get_object(getThis() TSRMLS_CC);
+	array_init_size(return_value, zend_hash_num_elements(&pobj->values));
+
+	zend_hash_internal_pointer_reset_ex(&pobj->values, &pos);
+
+	while(zend_hash_get_current_data_ex(&pobj->values, (void **)&value, &pos) == SUCCESS) {
+		MAKE_STD_ZVAL(endval);
+		switch (zend_hash_get_current_key_ex(&pobj->values, &str_index, (uint *)&str_len, &num_index, 0, &pos)) {
+			case HASH_KEY_IS_STRING:
+				ZVAL_STRINGL(endval, str_index, str_len - 1, 1);
+				zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &endval, sizeof(zval *), NULL);
+			break;
+			case HASH_KEY_IS_LONG:
+				ZVAL_LONG(endval, num_index);
+				zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &endval, sizeof(zval *), NULL);
+			break;
+		}
+	zend_hash_move_forward_ex(&pobj->values, &pos);
+	}
+}
+
+PHP_METHOD(Pimple, factory)
+{
+	zval *factory       = NULL;
+	pimple_object *pobj = NULL;
+	pimple_bucket_value bucket = {0};
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &factory) == FAILURE) {
+		return;
+	}
+
+	if (pimple_zval_is_valid_callback(factory, &bucket TSRMLS_CC) == FAILURE) {
+		pimple_free_bucket(&bucket);
+		zend_throw_exception(pimple_ce_ExpectedInvokableException, "Service definition is not a Closure or invokable object.", 0 TSRMLS_CC);
+		return;
+	}
+
+	pimple_zval_to_pimpleval(factory, &bucket TSRMLS_CC);
+	pobj = (pimple_object *)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+	if (zend_hash_index_update(&pobj->factories, bucket.handle_num, (void *)&bucket, sizeof(pimple_bucket_value), NULL) == SUCCESS) {
+		Z_ADDREF_P(factory);
+		RETURN_ZVAL(factory, 1 , 0);
+	} else {
+		pimple_free_bucket(&bucket);
+	}
+
+	RETURN_FALSE;
+}
+
+PHP_METHOD(Pimple, offsetSet)
+{
+	zval *offset = NULL, *value = NULL;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &offset, &value) == FAILURE) {
+		return;
+	}
+
+	pimple_object_write_dimension(getThis(), offset, value TSRMLS_CC);
+}
+
+PHP_METHOD(Pimple, offsetGet)
+{
+	zval *offset = NULL, *retval = NULL;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) {
+		return;
+	}
+
+	retval = pimple_object_read_dimension(getThis(), offset, 0 TSRMLS_CC);
+
+	RETVAL_ZVAL(retval, 1, 0);
+}
+
+PHP_METHOD(Pimple, offsetUnset)
+{
+	zval *offset = NULL;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) {
+		return;
+	}
+
+	pimple_object_unset_dimension(getThis(), offset TSRMLS_CC);
+}
+
+PHP_METHOD(Pimple, offsetExists)
+{
+	zval *offset = NULL;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) {
+		return;
+	}
+
+	RETVAL_BOOL(pimple_object_has_dimension(getThis(), offset, 1 TSRMLS_CC));
+}
+
+PHP_METHOD(Pimple, register)
+{
+	zval *provider;
+	zval **data;
+	zval *retval = NULL;
+	zval key;
+
+	HashTable *array = NULL;
+	HashPosition pos;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|h", &provider, pimple_serviceprovider_ce, &array) == FAILURE) {
+		return;
+	}
+
+	RETVAL_ZVAL(getThis(), 1, 0);
+
+	zend_call_method_with_1_params(&provider, Z_OBJCE_P(provider), NULL, "register", &retval, getThis());
+
+	if (retval) {
+		zval_ptr_dtor(&retval);
+	}
+
+	if (!array) {
+		return;
+	}
+
+	zend_hash_internal_pointer_reset_ex(array, &pos);
+
+	while(zend_hash_get_current_data_ex(array, (void **)&data, &pos) == SUCCESS) {
+		zend_hash_get_current_key_zval_ex(array, &key, &pos);
+		pimple_object_write_dimension(getThis(), &key, *data TSRMLS_CC);
+		zend_hash_move_forward_ex(array, &pos);
+	}
+}
+
+PHP_METHOD(Pimple, __construct)
+{
+	zval *values = NULL, **pData = NULL, offset;
+	HashPosition pos;
+	char *str_index = NULL;
+	zend_uint str_length;
+	ulong num_index;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!", &values) == FAILURE) {
+		return;
+	}
+
+	PIMPLE_DEPRECATE
+
+	if (!values) {
+		return;
+	}
+
+	zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(values), &pos);
+	while (zend_hash_has_more_elements_ex(Z_ARRVAL_P(values), &pos) == SUCCESS) {
+			zend_hash_get_current_data_ex(Z_ARRVAL_P(values), (void **)&pData, &pos);
+			zend_hash_get_current_key_ex(Z_ARRVAL_P(values), &str_index, &str_length, &num_index, 0, &pos);
+			INIT_ZVAL(offset);
+			if (zend_hash_get_current_key_type_ex(Z_ARRVAL_P(values), &pos) == HASH_KEY_IS_LONG) {
+				ZVAL_LONG(&offset, num_index);
+			} else {
+				ZVAL_STRINGL(&offset, str_index, (str_length - 1), 0);
+			}
+		pimple_object_write_dimension(getThis(), &offset, *pData TSRMLS_CC);
+		zend_hash_move_forward_ex(Z_ARRVAL_P(values), &pos);
+	}
+}
+
+/*
+ * This is PHP code snippet handling extend()s calls :
+
+  $extended = function ($c) use ($callable, $factory) {
+      return $callable($factory($c), $c);
+  };
+
+ */
+PHP_METHOD(PimpleClosure, invoker)
+{
+	pimple_closure_object *pcobj = NULL;
+	zval *arg = NULL, *retval = NULL, *newretval = NULL;
+	zend_fcall_info fci        = {0};
+	zval **args[2];
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &arg) == FAILURE) {
+		return;
+	}
+
+	pcobj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+	fci.function_name = pcobj->factory;
+	args[0] = &arg;
+	zend_fcall_info_argp(&fci TSRMLS_CC, 1, args);
+	fci.retval_ptr_ptr = &retval;
+	fci.size = sizeof(fci);
+
+	if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE || EG(exception)) {
+		efree(fci.params);
+		return; /* Should here return default zval */
+	}
+
+	efree(fci.params);
+	memset(&fci, 0, sizeof(fci));
+	fci.size = sizeof(fci);
+
+	fci.function_name = pcobj->callable;
+	args[0] = &retval;
+	args[1] = &arg;
+	zend_fcall_info_argp(&fci TSRMLS_CC, 2, args);
+	fci.retval_ptr_ptr = &newretval;
+
+	if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE || EG(exception)) {
+		efree(fci.params);
+		zval_ptr_dtor(&retval);
+		return;
+	}
+
+	efree(fci.params);
+	zval_ptr_dtor(&retval);
+
+	RETVAL_ZVAL(newretval, 1 ,1);
+}
+
+PHP_MINIT_FUNCTION(pimple)
+{
+	zend_class_entry tmp_ce_PsrContainerInterface, tmp_ce_PsrContainerExceptionInterface, tmp_ce_PsrNotFoundExceptionInterface;
+	zend_class_entry tmp_ce_ExpectedInvokableException, tmp_ce_FrozenServiceException, tmp_ce_InvalidServiceIdentifierException, tmp_ce_UnknownIdentifierException;
+	zend_class_entry tmp_pimple_ce, tmp_pimple_closure_ce, tmp_pimple_serviceprovider_iface_ce;
+
+	/* Psr\Container namespace */
+	INIT_NS_CLASS_ENTRY(tmp_ce_PsrContainerInterface,          PSR_CONTAINER_NS, "ContainerInterface",           pimple_ce_PsrContainerInterface_functions);
+	INIT_NS_CLASS_ENTRY(tmp_ce_PsrContainerExceptionInterface, PSR_CONTAINER_NS, "ContainerExceptionInterface", pimple_ce_PsrContainerExceptionInterface_functions);
+	INIT_NS_CLASS_ENTRY(tmp_ce_PsrNotFoundExceptionInterface,  PSR_CONTAINER_NS, "NotFoundExceptionInterface",  pimple_ce_PsrNotFoundExceptionInterface_functions);
+
+	pimple_ce_PsrContainerInterface          = zend_register_internal_interface(&tmp_ce_PsrContainerInterface TSRMLS_CC);
+	pimple_ce_PsrContainerExceptionInterface = zend_register_internal_interface(&tmp_ce_PsrContainerExceptionInterface TSRMLS_CC);
+	pimple_ce_PsrNotFoundExceptionInterface  = zend_register_internal_interface(&tmp_ce_PsrNotFoundExceptionInterface TSRMLS_CC);
+
+	zend_class_implements(pimple_ce_PsrNotFoundExceptionInterface TSRMLS_CC, 1, pimple_ce_PsrContainerExceptionInterface);
+
+	/* Pimple\Exception namespace */
+	INIT_NS_CLASS_ENTRY(tmp_ce_ExpectedInvokableException,        PIMPLE_EXCEPTION_NS, "ExpectedInvokableException",         NULL);
+	INIT_NS_CLASS_ENTRY(tmp_ce_FrozenServiceException,            PIMPLE_EXCEPTION_NS, "FrozenServiceException",             pimple_ce_FrozenServiceException_functions);
+	INIT_NS_CLASS_ENTRY(tmp_ce_InvalidServiceIdentifierException, PIMPLE_EXCEPTION_NS, "InvalidServiceIdentifierException", pimple_ce_InvalidServiceIdentifierException_functions);
+	INIT_NS_CLASS_ENTRY(tmp_ce_UnknownIdentifierException,        PIMPLE_EXCEPTION_NS, "UnknownIdentifierException",         pimple_ce_UnknownIdentifierException_functions);
+
+	pimple_ce_ExpectedInvokableException        = zend_register_internal_class_ex(&tmp_ce_ExpectedInvokableException, spl_ce_InvalidArgumentException, NULL TSRMLS_CC);
+	pimple_ce_FrozenServiceException            = zend_register_internal_class_ex(&tmp_ce_FrozenServiceException, spl_ce_RuntimeException, NULL TSRMLS_CC);
+	pimple_ce_InvalidServiceIdentifierException = zend_register_internal_class_ex(&tmp_ce_InvalidServiceIdentifierException, spl_ce_InvalidArgumentException, NULL TSRMLS_CC);
+	pimple_ce_UnknownIdentifierException        = zend_register_internal_class_ex(&tmp_ce_UnknownIdentifierException, spl_ce_InvalidArgumentException, NULL TSRMLS_CC);
+
+	zend_class_implements(pimple_ce_ExpectedInvokableException TSRMLS_CC,        1, pimple_ce_PsrContainerExceptionInterface);
+	zend_class_implements(pimple_ce_FrozenServiceException TSRMLS_CC,            1, pimple_ce_PsrContainerExceptionInterface);
+	zend_class_implements(pimple_ce_InvalidServiceIdentifierException TSRMLS_CC, 1, pimple_ce_PsrContainerExceptionInterface);
+	zend_class_implements(pimple_ce_UnknownIdentifierException TSRMLS_CC,        1, pimple_ce_PsrNotFoundExceptionInterface);
+
+    /* Pimple namespace */
+	INIT_NS_CLASS_ENTRY(tmp_pimple_ce, PIMPLE_NS, "Container", pimple_ce_functions);
+	INIT_NS_CLASS_ENTRY(tmp_pimple_closure_ce, PIMPLE_NS, "ContainerClosure", NULL);
+	INIT_NS_CLASS_ENTRY(tmp_pimple_serviceprovider_iface_ce, PIMPLE_NS, "ServiceProviderInterface", pimple_serviceprovider_iface_ce_functions);
+
+	tmp_pimple_ce.create_object         = pimple_object_create;
+	tmp_pimple_closure_ce.create_object = pimple_closure_object_create;
+
+	pimple_ce = zend_register_internal_class(&tmp_pimple_ce TSRMLS_CC);
+	zend_class_implements(pimple_ce TSRMLS_CC, 1, zend_ce_arrayaccess);
+
+	pimple_closure_ce = zend_register_internal_class(&tmp_pimple_closure_ce TSRMLS_CC);
+	pimple_closure_ce->ce_flags |= ZEND_ACC_FINAL_CLASS;
+
+	pimple_serviceprovider_ce = zend_register_internal_interface(&tmp_pimple_serviceprovider_iface_ce TSRMLS_CC);
+
+	memcpy(&pimple_closure_object_handlers, zend_get_std_object_handlers(), sizeof(*zend_get_std_object_handlers()));
+	pimple_object_handlers                     = std_object_handlers;
+	pimple_closure_object_handlers.get_closure = pimple_closure_get_closure;
+
+	pimple_closure_invoker_function.function_name     = "Pimple closure internal invoker";
+	pimple_closure_invoker_function.fn_flags         |= ZEND_ACC_CLOSURE;
+	pimple_closure_invoker_function.handler           = ZEND_MN(PimpleClosure_invoker);
+	pimple_closure_invoker_function.num_args          = 1;
+	pimple_closure_invoker_function.required_num_args = 1;
+	pimple_closure_invoker_function.scope             = pimple_closure_ce;
+	pimple_closure_invoker_function.type              = ZEND_INTERNAL_FUNCTION;
+	pimple_closure_invoker_function.module            = &pimple_module_entry;
+
+	return SUCCESS;
+}
+
+PHP_MINFO_FUNCTION(pimple)
+{
+	php_info_print_table_start();
+	php_info_print_table_header(2, "SensioLabs Pimple C support", "enabled");
+	php_info_print_table_row(2, "Pimple supported version", PIMPLE_VERSION);
+	php_info_print_table_end();
+
+	php_info_print_box_start(0);
+	php_write((void *)ZEND_STRL("SensioLabs Pimple C support developed by Julien Pauli") TSRMLS_CC);
+	if (!sapi_module.phpinfo_as_text) {
+		php_write((void *)ZEND_STRL(sensiolabs_logo) TSRMLS_CC);
+	}
+	php_info_print_box_end();
+}
+
+zend_module_entry pimple_module_entry = {
+	STANDARD_MODULE_HEADER,
+	"pimple",
+	NULL,
+	PHP_MINIT(pimple),
+	NULL,
+	NULL,
+	NULL,
+	PHP_MINFO(pimple),
+	PIMPLE_VERSION,
+	STANDARD_MODULE_PROPERTIES
+};
+
+#ifdef COMPILE_DL_PIMPLE
+ZEND_GET_MODULE(pimple)
+#endif
diff --git a/vendor/pimple/pimple/ext/pimple/pimple_compat.h b/vendor/pimple/pimple/ext/pimple/pimple_compat.h
new file mode 100755
index 0000000..d234e17
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/pimple_compat.h
@@ -0,0 +1,81 @@
+
+/*
+ * This file is part of Pimple.
+ *
+ * Copyright (c) 2014 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.
+ */
+
+#ifndef PIMPLE_COMPAT_H_
+#define PIMPLE_COMPAT_H_
+
+#include "Zend/zend_extensions.h" /* for ZEND_EXTENSION_API_NO */
+
+#define PHP_5_0_X_API_NO		220040412
+#define PHP_5_1_X_API_NO		220051025
+#define PHP_5_2_X_API_NO		220060519
+#define PHP_5_3_X_API_NO		220090626
+#define PHP_5_4_X_API_NO		220100525
+#define PHP_5_5_X_API_NO		220121212
+#define PHP_5_6_X_API_NO		220131226
+
+#define IS_PHP_56 ZEND_EXTENSION_API_NO == PHP_5_6_X_API_NO
+#define IS_AT_LEAST_PHP_56 ZEND_EXTENSION_API_NO >= PHP_5_6_X_API_NO
+
+#define IS_PHP_55 ZEND_EXTENSION_API_NO == PHP_5_5_X_API_NO
+#define IS_AT_LEAST_PHP_55 ZEND_EXTENSION_API_NO >= PHP_5_5_X_API_NO
+
+#define IS_PHP_54 ZEND_EXTENSION_API_NO == PHP_5_4_X_API_NO
+#define IS_AT_LEAST_PHP_54 ZEND_EXTENSION_API_NO >= PHP_5_4_X_API_NO
+
+#define IS_PHP_53 ZEND_EXTENSION_API_NO == PHP_5_3_X_API_NO
+#define IS_AT_LEAST_PHP_53 ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
+
+#if IS_PHP_53
+#define object_properties_init(obj, ce) do { \
+		 zend_hash_copy(obj->properties, &ce->default_properties, zval_copy_property_ctor(ce), NULL, sizeof(zval *)); \
+		} while (0);
+#endif
+
+#define ZEND_OBJ_INIT(obj, ce) do { \
+		zend_object_std_init(obj, ce TSRMLS_CC); \
+		object_properties_init((obj), (ce)); \
+	} while(0);
+
+#if IS_PHP_53 || IS_PHP_54
+static void zend_hash_get_current_key_zval_ex(const HashTable *ht, zval *key, HashPosition *pos) {
+    Bucket *p;
+
+    p = pos ? (*pos) : ht->pInternalPointer;
+
+    if (!p) {
+        Z_TYPE_P(key) = IS_NULL;
+    } else if (p->nKeyLength) {
+        Z_TYPE_P(key) = IS_STRING;
+        Z_STRVAL_P(key) = estrndup(p->arKey, p->nKeyLength - 1);
+        Z_STRLEN_P(key) = p->nKeyLength - 1;
+    } else {
+        Z_TYPE_P(key) = IS_LONG;
+        Z_LVAL_P(key) = p->h;
+    }
+}
+#endif
+
+#endif /* PIMPLE_COMPAT_H_ */
diff --git a/vendor/pimple/pimple/ext/pimple/tests/001.phpt b/vendor/pimple/pimple/ext/pimple/tests/001.phpt
new file mode 100755
index 0000000..0809ea2
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/tests/001.phpt
@@ -0,0 +1,45 @@
+--TEST--
+Test for read_dim/write_dim handlers
+--SKIPIF--
+<?php if (!extension_loaded("pimple")) print "skip"; ?>
+--FILE--
+<?php 
+$p = new Pimple\Container();
+$p[42] = 'foo';
+$p['foo'] = 42;
+
+echo $p[42];
+echo "\n";
+echo $p['foo'];
+echo "\n";
+try {
+	var_dump($p['nonexistant']);
+	echo "Exception excpected";
+} catch (InvalidArgumentException $e) { }
+
+$p[54.2] = 'foo2';
+echo $p[54];
+echo "\n";
+$p[242.99] = 'foo99';
+echo $p[242];
+
+echo "\n";
+
+$p[5] = 'bar';
+$p[5] = 'baz';
+echo $p[5];
+
+echo "\n";
+
+$p['str'] = 'str';
+$p['str'] = 'strstr';
+echo $p['str'];
+?>
+
+--EXPECTF--
+foo
+42
+foo2
+foo99
+baz
+strstr
\ No newline at end of file
diff --git a/vendor/pimple/pimple/ext/pimple/tests/002.phpt b/vendor/pimple/pimple/ext/pimple/tests/002.phpt
new file mode 100755
index 0000000..7b56d2c
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/tests/002.phpt
@@ -0,0 +1,15 @@
+--TEST--
+Test for constructor
+--SKIPIF--
+<?php if (!extension_loaded("pimple")) print "skip"; ?>
+--FILE--
+<?php 
+$p = new Pimple\Container();
+var_dump($p[42]);
+
+$p = new Pimple\Container(array(42=>'foo'));
+var_dump($p[42]);
+?>
+--EXPECT--
+NULL
+string(3) "foo"
diff --git a/vendor/pimple/pimple/ext/pimple/tests/003.phpt b/vendor/pimple/pimple/ext/pimple/tests/003.phpt
new file mode 100755
index 0000000..a22cfa3
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/tests/003.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Test empty dimensions
+--SKIPIF--
+<?php if (!extension_loaded("pimple")) print "skip"; ?>
+--FILE--
+<?php 
+$p = new Pimple\Container();
+$p[] = 42;
+var_dump($p[0]);
+$p[41] = 'foo';
+$p[] = 'bar';
+var_dump($p[42]);
+?>
+--EXPECT--
+int(42)
+string(3) "bar"
\ No newline at end of file
diff --git a/vendor/pimple/pimple/ext/pimple/tests/004.phpt b/vendor/pimple/pimple/ext/pimple/tests/004.phpt
new file mode 100755
index 0000000..1e1d251
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/tests/004.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Test has/unset dim handlers
+--SKIPIF--
+<?php if (!extension_loaded("pimple")) print "skip"; ?>
+--FILE--
+<?php 
+$p = new Pimple\Container();
+$p[] = 42;
+var_dump($p[0]);
+unset($p[0]);
+var_dump($p[0]);
+$p['foo'] = 'bar';
+var_dump(isset($p['foo']));
+unset($p['foo']);
+try {
+	var_dump($p['foo']);
+	echo "Excpected exception";
+} catch (InvalidArgumentException $e) { }
+var_dump(isset($p['bar']));
+$p['bar'] = NULL;
+var_dump(isset($p['bar']));
+var_dump(empty($p['bar']));
+?>
+--EXPECT--
+int(42)
+NULL
+bool(true)
+bool(false)
+bool(true)
+bool(true)
\ No newline at end of file
diff --git a/vendor/pimple/pimple/ext/pimple/tests/005.phpt b/vendor/pimple/pimple/ext/pimple/tests/005.phpt
new file mode 100755
index 0000000..0479ee0
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/tests/005.phpt
@@ -0,0 +1,27 @@
+--TEST--
+Test simple class inheritance
+--SKIPIF--
+<?php if (!extension_loaded("pimple")) print "skip"; ?>
+--FILE--
+<?php 
+class MyPimple extends Pimple\Container
+{
+	public $someAttr = 'fooAttr';
+
+    public function offsetget($o)
+    {
+        var_dump("hit");
+        return parent::offsetget($o);
+    }
+}
+
+$p = new MyPimple;
+$p[42] = 'foo';
+echo $p[42];
+echo "\n";
+echo $p->someAttr;
+?>
+--EXPECT--
+string(3) "hit"
+foo
+fooAttr
\ No newline at end of file
diff --git a/vendor/pimple/pimple/ext/pimple/tests/006.phpt b/vendor/pimple/pimple/ext/pimple/tests/006.phpt
new file mode 100755
index 0000000..cfe8a11
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/tests/006.phpt
@@ -0,0 +1,51 @@
+--TEST--
+Test complex class inheritance
+--SKIPIF--
+<?php if (!extension_loaded("pimple")) print "skip"; ?>
+--FILE--
+<?php 
+class MyPimple extends Pimple\Container
+{
+    public function offsetget($o)
+    {
+        var_dump("hit offsetget in " . __CLASS__);
+        return parent::offsetget($o);
+    }
+}
+
+class TestPimple extends MyPimple
+{
+    public function __construct($values)
+    {
+        array_shift($values);
+        parent::__construct($values);
+    }
+    
+    public function offsetget($o)
+    {
+        var_dump('hit offsetget in ' . __CLASS__);
+        return parent::offsetget($o);
+    }
+    
+    public function offsetset($o, $v)
+    {
+        var_dump('hit offsetset');
+        return parent::offsetset($o, $v);
+    }
+}
+
+$defaultValues = array('foo' => 'bar', 88 => 'baz');
+
+$p = new TestPimple($defaultValues);
+$p[42] = 'foo';
+var_dump($p[42]);
+var_dump($p[0]);
+?>
+--EXPECT--
+string(13) "hit offsetset"
+string(27) "hit offsetget in TestPimple"
+string(25) "hit offsetget in MyPimple"
+string(3) "foo"
+string(27) "hit offsetget in TestPimple"
+string(25) "hit offsetget in MyPimple"
+string(3) "baz"
\ No newline at end of file
diff --git a/vendor/pimple/pimple/ext/pimple/tests/007.phpt b/vendor/pimple/pimple/ext/pimple/tests/007.phpt
new file mode 100755
index 0000000..5aac683
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/tests/007.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Test for read_dim/write_dim handlers
+--SKIPIF--
+<?php if (!extension_loaded("pimple")) print "skip"; ?>
+--FILE--
+<?php 
+$p = new Pimple\Container();
+$p[42] = 'foo';
+$p['foo'] = 42;
+
+echo $p[42];
+echo "\n";
+echo $p['foo'];
+echo "\n";
+try {
+	var_dump($p['nonexistant']);
+	echo "Exception excpected";
+} catch (InvalidArgumentException $e) { }
+?>
+--EXPECTF--
+foo
+42
\ No newline at end of file
diff --git a/vendor/pimple/pimple/ext/pimple/tests/008.phpt b/vendor/pimple/pimple/ext/pimple/tests/008.phpt
new file mode 100755
index 0000000..db7eeec
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/tests/008.phpt
@@ -0,0 +1,29 @@
+--TEST--
+Test frozen services
+--SKIPIF--
+<?php if (!extension_loaded("pimple")) print "skip"; ?>
+--FILE--
+<?php 
+$p = new Pimple\Container();
+$p[42] = 'foo';
+$p[42] = 'bar';
+
+$p['foo'] = function () { };
+$p['foo'] = function () { };
+
+$a = $p['foo'];
+
+try {
+	$p['foo'] = function () { };
+	echo "Exception excpected";
+} catch (RuntimeException $e) { }
+
+$p[42] = function() { };
+$a = $p[42];
+
+try {
+	$p[42] = function () { };
+	echo "Exception excpected";
+} catch (RuntimeException $e) { }
+?>
+--EXPECTF--
diff --git a/vendor/pimple/pimple/ext/pimple/tests/009.phpt b/vendor/pimple/pimple/ext/pimple/tests/009.phpt
new file mode 100755
index 0000000..bb05ea2
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/tests/009.phpt
@@ -0,0 +1,13 @@
+--TEST--
+Test service is called as callback, and only once
+--SKIPIF--
+<?php if (!extension_loaded("pimple")) print "skip"; ?>
+--FILE--
+<?php 
+$p = new Pimple\Container();
+$p['foo'] = function($arg) use ($p) { var_dump($p === $arg); };
+$a = $p['foo'];
+$b = $p['foo']; /* should return not calling the callback */
+?>
+--EXPECTF--
+bool(true)
\ No newline at end of file
diff --git a/vendor/pimple/pimple/ext/pimple/tests/010.phpt b/vendor/pimple/pimple/ext/pimple/tests/010.phpt
new file mode 100755
index 0000000..badce01
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/tests/010.phpt
@@ -0,0 +1,45 @@
+--TEST--
+Test service is called as callback for every callback type
+--SKIPIF--
+<?php if (!extension_loaded("pimple")) print "skip"; ?>
+--FILE--
+<?php
+function callme()
+{
+    return 'called';
+}
+
+$a = function() { return 'called'; };
+
+class Foo
+{
+    public static function bar()
+    {
+        return 'called';
+    }
+}
+ 
+$p = new Pimple\Container();
+$p['foo'] = 'callme';
+echo $p['foo'] . "\n";
+
+$p['bar'] = $a;
+echo $p['bar'] . "\n";
+
+$p['baz'] = "Foo::bar";
+echo $p['baz'] . "\n";
+
+$p['foobar'] = array('Foo', 'bar');
+var_dump($p['foobar']);
+
+?>
+--EXPECTF--
+callme
+called
+Foo::bar
+array(2) {
+  [0]=>
+  string(3) "Foo"
+  [1]=>
+  string(3) "bar"
+}
\ No newline at end of file
diff --git a/vendor/pimple/pimple/ext/pimple/tests/011.phpt b/vendor/pimple/pimple/ext/pimple/tests/011.phpt
new file mode 100755
index 0000000..6682ab8
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/tests/011.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test service callback throwing an exception
+--SKIPIF--
+<?php if (!extension_loaded("pimple")) print "skip"; ?>
+--FILE--
+<?php
+class CallBackException extends RuntimeException { }
+
+$p = new Pimple\Container();
+$p['foo'] = function () { throw new CallBackException; };
+try {
+	echo $p['foo'] . "\n";
+	echo "should not come here";
+} catch (CallBackException $e) {
+	echo "all right!";
+}
+?>
+--EXPECTF--
+all right!
\ No newline at end of file
diff --git a/vendor/pimple/pimple/ext/pimple/tests/012.phpt b/vendor/pimple/pimple/ext/pimple/tests/012.phpt
new file mode 100755
index 0000000..4c6ac48
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/tests/012.phpt
@@ -0,0 +1,28 @@
+--TEST--
+Test service factory
+--SKIPIF--
+<?php if (!extension_loaded("pimple")) print "skip"; ?>
+--FILE--
+<?php
+
+$p = new Pimple\Container();
+
+$p->factory($f = function() { var_dump('called-1'); return 'ret-1';});
+
+$p[] = $f;
+
+$p[] = function () { var_dump('called-2'); return 'ret-2'; };
+
+var_dump($p[0]);
+var_dump($p[0]);
+var_dump($p[1]);
+var_dump($p[1]);
+?>
+--EXPECTF--
+string(8) "called-1"
+string(5) "ret-1"
+string(8) "called-1"
+string(5) "ret-1"
+string(8) "called-2"
+string(5) "ret-2"
+string(5) "ret-2"
\ No newline at end of file
diff --git a/vendor/pimple/pimple/ext/pimple/tests/013.phpt b/vendor/pimple/pimple/ext/pimple/tests/013.phpt
new file mode 100755
index 0000000..f419958
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/tests/013.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Test keys()
+--SKIPIF--
+<?php if (!extension_loaded("pimple")) print "skip"; ?>
+--FILE--
+<?php
+
+$p = new Pimple\Container();
+
+var_dump($p->keys());
+
+$p['foo'] = 'bar';
+$p[] = 'foo';
+
+var_dump($p->keys());
+
+unset($p['foo']);
+
+var_dump($p->keys());
+?>
+--EXPECTF--
+array(0) {
+}
+array(2) {
+  [0]=>
+  string(3) "foo"
+  [1]=>
+  int(0)
+}
+array(1) {
+  [0]=>
+  int(0)
+}
\ No newline at end of file
diff --git a/vendor/pimple/pimple/ext/pimple/tests/014.phpt b/vendor/pimple/pimple/ext/pimple/tests/014.phpt
new file mode 100755
index 0000000..ac93721
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/tests/014.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Test raw()
+--SKIPIF--
+<?php if (!extension_loaded("pimple")) print "skip"; ?>
+--FILE--
+<?php
+
+$p = new Pimple\Container();
+$f = function () { var_dump('called-2'); return 'ret-2'; };
+
+$p['foo'] = $f;
+$p[42]    = $f;
+
+var_dump($p['foo']);
+var_dump($p->raw('foo'));
+var_dump($p[42]);
+
+unset($p['foo']);
+
+try {
+	$p->raw('foo');
+	echo "expected exception";
+} catch (InvalidArgumentException $e) { }
+--EXPECTF--
+string(8) "called-2"
+string(5) "ret-2"
+object(Closure)#%i (0) {
+}
+string(8) "called-2"
+string(5) "ret-2"
\ No newline at end of file
diff --git a/vendor/pimple/pimple/ext/pimple/tests/015.phpt b/vendor/pimple/pimple/ext/pimple/tests/015.phpt
new file mode 100755
index 0000000..314f008
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/tests/015.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Test protect()
+--SKIPIF--
+<?php if (!extension_loaded("pimple")) print "skip"; ?>
+--FILE--
+<?php
+
+$p = new Pimple\Container();
+$f = function () { return 'foo'; };
+$p['foo'] = $f;
+
+$p->protect($f);
+
+var_dump($p['foo']);
+--EXPECTF--
+object(Closure)#%i (0) {
+}
\ No newline at end of file
diff --git a/vendor/pimple/pimple/ext/pimple/tests/016.phpt b/vendor/pimple/pimple/ext/pimple/tests/016.phpt
new file mode 100755
index 0000000..e55edb0
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/tests/016.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Test extend()
+--SKIPIF--
+<?php if (!extension_loaded("pimple")) print "skip"; ?>
+--FILE--
+<?php
+/*
+ This is part of Pimple::extend() code :
+
+          $extended = function ($c) use ($callable, $factory) {
+             return $callable($factory($c), $c);
+          };
+*/
+
+$p = new Pimple\Container();
+$p[12] = function ($v) { var_dump($v); return 'foo';}; /* $factory in code above */
+
+$c = $p->extend(12, function ($w) { var_dump($w); return 'bar'; }); /* $callable in code above */
+
+var_dump($c('param'));
+--EXPECTF--
+string(5) "param"
+string(3) "foo"
+string(3) "bar"
\ No newline at end of file
diff --git a/vendor/pimple/pimple/ext/pimple/tests/017.phpt b/vendor/pimple/pimple/ext/pimple/tests/017.phpt
new file mode 100755
index 0000000..bac23ce
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/tests/017.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Test extend() with exception in service extension
+--SKIPIF--
+<?php if (!extension_loaded("pimple")) print "skip"; ?>
+--FILE--
+<?php
+
+$p = new Pimple\Container();
+$p[12] = function ($v) { return 'foo';};
+
+$c = $p->extend(12, function ($w) { throw new BadMethodCallException; });
+
+try {
+	$p[12];
+	echo "Exception expected";
+} catch (BadMethodCallException $e) { }
+--EXPECTF--
diff --git a/vendor/pimple/pimple/ext/pimple/tests/017_1.phpt b/vendor/pimple/pimple/ext/pimple/tests/017_1.phpt
new file mode 100755
index 0000000..8f881d6
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/tests/017_1.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Test extend() with exception in service factory
+--SKIPIF--
+<?php if (!extension_loaded("pimple")) print "skip"; ?>
+--FILE--
+<?php
+
+$p = new Pimple\Container();
+$p[12] = function ($v) { throw new BadMethodCallException; };
+
+$c = $p->extend(12, function ($w) { return 'foobar'; });
+
+try {
+	$p[12];
+	echo "Exception expected";
+} catch (BadMethodCallException $e) { }
+--EXPECTF--
diff --git a/vendor/pimple/pimple/ext/pimple/tests/018.phpt b/vendor/pimple/pimple/ext/pimple/tests/018.phpt
new file mode 100755
index 0000000..27c12a1
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/tests/018.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test register()
+--SKIPIF--
+<?php if (!extension_loaded("pimple")) print "skip"; ?>
+--FILE--
+<?php
+
+class Foo implements Pimple\ServiceProviderInterface
+{
+    public function register(Pimple\Container $p)
+    {
+        var_dump($p);
+    }
+}
+
+$p = new Pimple\Container();
+$p->register(new Foo, array(42 => 'bar'));
+
+var_dump($p[42]);
+--EXPECTF--
+object(Pimple\Container)#1 (0) {
+}
+string(3) "bar"
\ No newline at end of file
diff --git a/vendor/pimple/pimple/ext/pimple/tests/019.phpt b/vendor/pimple/pimple/ext/pimple/tests/019.phpt
new file mode 100755
index 0000000..28a9aec
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/tests/019.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test register() returns static and is a fluent interface
+--SKIPIF--
+<?php if (!extension_loaded("pimple")) print "skip"; ?>
+--FILE--
+<?php
+
+class Foo implements Pimple\ServiceProviderInterface
+{
+    public function register(Pimple\Container $p)
+    {
+    }
+}
+
+$p = new Pimple\Container();
+var_dump($p === $p->register(new Foo));
+--EXPECTF--
+bool(true)
diff --git a/vendor/pimple/pimple/ext/pimple/tests/bench.phpb b/vendor/pimple/pimple/ext/pimple/tests/bench.phpb
new file mode 100755
index 0000000..8f983e6
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/tests/bench.phpb
@@ -0,0 +1,51 @@
+<?php
+
+if (!class_exists('Pimple\Container')) {
+    require_once __DIR__ . '/../../../src/Pimple/Container.php';
+} else {
+    echo "pimple-c extension detected, using...\n\n";
+}
+
+$time = microtime(true);
+
+function foo() { }
+$factory = function () { };
+
+for ($i=0; $i<10000; $i++) {
+
+$p = new Pimple\Container;
+
+$p['foo'] = 'bar';
+
+if (!isset($p[3])) {
+    $p[3] = $p['foo'];
+    $p[]  = 'bar';
+}
+
+$p[2] = 42;
+
+if (isset($p[2])) {
+	unset($p[2]);
+}
+
+$p[42] = $p['foo'];
+
+$p['cb'] = function($arg) { };
+
+$p[] = $p['cb'];
+
+echo $p['cb'];
+echo $p['cb'];
+echo $p['cb'];
+
+//$p->factory($factory);
+
+$p['factory'] = $factory;
+
+echo $p['factory'];
+echo $p['factory'];
+echo $p['factory'];
+
+}
+
+echo microtime(true)  - $time;
diff --git a/vendor/pimple/pimple/ext/pimple/tests/bench_shared.phpb b/vendor/pimple/pimple/ext/pimple/tests/bench_shared.phpb
new file mode 100755
index 0000000..aec541f
--- /dev/null
+++ b/vendor/pimple/pimple/ext/pimple/tests/bench_shared.phpb
@@ -0,0 +1,25 @@
+<?php
+
+if (!class_exists('Pimple\Container')) {
+    require_once __DIR__ . '/../../../src/Pimple/Container.php';
+} else {
+    echo "pimple-c extension detected, using...\n\n";
+}
+
+$time = microtime(true);
+
+
+$service = function ($arg) { return "I'm a service"; };
+
+for ($i=0; $i<10000; $i++) {
+
+$p = new Pimple\Container;
+$p['my_service'] = $service;
+
+$a = $p['my_service'];
+$b = $p['my_service'];
+
+}
+
+echo microtime(true) - $time;
+?>
diff --git a/vendor/pimple/pimple/phpunit.xml.dist b/vendor/pimple/pimple/phpunit.xml.dist
new file mode 100755
index 0000000..5c8d487
--- /dev/null
+++ b/vendor/pimple/pimple/phpunit.xml.dist
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
+         backupGlobals="false"
+         colors="true"
+         bootstrap="vendor/autoload.php"
+>
+    <testsuites>
+        <testsuite name="Pimple Test Suite">
+            <directory>./src/Pimple/Tests</directory>
+        </testsuite>
+    </testsuites>
+</phpunit>
diff --git a/vendor/pimple/pimple/src/Pimple/Container.php b/vendor/pimple/pimple/src/Pimple/Container.php
new file mode 100755
index 0000000..707b92b
--- /dev/null
+++ b/vendor/pimple/pimple/src/Pimple/Container.php
@@ -0,0 +1,298 @@
+<?php
+
+/*
+ * This file is part of Pimple.
+ *
+ * Copyright (c) 2009 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.
+ */
+
+namespace Pimple;
+
+use Pimple\Exception\ExpectedInvokableException;
+use Pimple\Exception\FrozenServiceException;
+use Pimple\Exception\InvalidServiceIdentifierException;
+use Pimple\Exception\UnknownIdentifierException;
+
+/**
+ * Container main class.
+ *
+ * @author Fabien Potencier
+ */
+class Container implements \ArrayAccess
+{
+    private $values = array();
+    private $factories;
+    private $protected;
+    private $frozen = array();
+    private $raw = array();
+    private $keys = array();
+
+    /**
+     * Instantiates the container.
+     *
+     * Objects and parameters can be passed as argument to the constructor.
+     *
+     * @param array $values The parameters or objects
+     */
+    public function __construct(array $values = array())
+    {
+        $this->factories = new \SplObjectStorage();
+        $this->protected = new \SplObjectStorage();
+
+        foreach ($values as $key => $value) {
+            $this->offsetSet($key, $value);
+        }
+    }
+
+    /**
+     * Sets a parameter or an object.
+     *
+     * Objects must be defined as Closures.
+     *
+     * Allowing any PHP callable leads to difficult to debug problems
+     * as function names (strings) are callable (creating a function with
+     * the same name as an existing parameter would break your container).
+     *
+     * @param string $id    The unique identifier for the parameter or object
+     * @param mixed  $value The value of the parameter or a closure to define an object
+     *
+     * @throws FrozenServiceException Prevent override of a frozen service
+     */
+    public function offsetSet($id, $value)
+    {
+        if (isset($this->frozen[$id])) {
+            throw new FrozenServiceException($id);
+        }
+
+        $this->values[$id] = $value;
+        $this->keys[$id] = true;
+    }
+
+    /**
+     * Gets a parameter or an object.
+     *
+     * @param string $id The unique identifier for the parameter or object
+     *
+     * @return mixed The value of the parameter or an object
+     *
+     * @throws UnknownIdentifierException If the identifier is not defined
+     */
+    public function offsetGet($id)
+    {
+        if (!isset($this->keys[$id])) {
+            throw new UnknownIdentifierException($id);
+        }
+
+        if (
+            isset($this->raw[$id])
+            || !\is_object($this->values[$id])
+            || isset($this->protected[$this->values[$id]])
+            || !\method_exists($this->values[$id], '__invoke')
+        ) {
+            return $this->values[$id];
+        }
+
+        if (isset($this->factories[$this->values[$id]])) {
+            return $this->values[$id]($this);
+        }
+
+        $raw = $this->values[$id];
+        $val = $this->values[$id] = $raw($this);
+        $this->raw[$id] = $raw;
+
+        $this->frozen[$id] = true;
+
+        return $val;
+    }
+
+    /**
+     * Checks if a parameter or an object is set.
+     *
+     * @param string $id The unique identifier for the parameter or object
+     *
+     * @return bool
+     */
+    public function offsetExists($id)
+    {
+        return isset($this->keys[$id]);
+    }
+
+    /**
+     * Unsets a parameter or an object.
+     *
+     * @param string $id The unique identifier for the parameter or object
+     */
+    public function offsetUnset($id)
+    {
+        if (isset($this->keys[$id])) {
+            if (\is_object($this->values[$id])) {
+                unset($this->factories[$this->values[$id]], $this->protected[$this->values[$id]]);
+            }
+
+            unset($this->values[$id], $this->frozen[$id], $this->raw[$id], $this->keys[$id]);
+        }
+    }
+
+    /**
+     * Marks a callable as being a factory service.
+     *
+     * @param callable $callable A service definition to be used as a factory
+     *
+     * @return callable The passed callable
+     *
+     * @throws ExpectedInvokableException Service definition has to be a closure or an invokable object
+     */
+    public function factory($callable)
+    {
+        if (!\method_exists($callable, '__invoke')) {
+            throw new ExpectedInvokableException('Service definition is not a Closure or invokable object.');
+        }
+
+        $this->factories->attach($callable);
+
+        return $callable;
+    }
+
+    /**
+     * Protects a callable from being interpreted as a service.
+     *
+     * This is useful when you want to store a callable as a parameter.
+     *
+     * @param callable $callable A callable to protect from being evaluated
+     *
+     * @return callable The passed callable
+     *
+     * @throws ExpectedInvokableException Service definition has to be a closure or an invokable object
+     */
+    public function protect($callable)
+    {
+        if (!\method_exists($callable, '__invoke')) {
+            throw new ExpectedInvokableException('Callable is not a Closure or invokable object.');
+        }
+
+        $this->protected->attach($callable);
+
+        return $callable;
+    }
+
+    /**
+     * Gets a parameter or the closure defining an object.
+     *
+     * @param string $id The unique identifier for the parameter or object
+     *
+     * @return mixed The value of the parameter or the closure defining an object
+     *
+     * @throws UnknownIdentifierException If the identifier is not defined
+     */
+    public function raw($id)
+    {
+        if (!isset($this->keys[$id])) {
+            throw new UnknownIdentifierException($id);
+        }
+
+        if (isset($this->raw[$id])) {
+            return $this->raw[$id];
+        }
+
+        return $this->values[$id];
+    }
+
+    /**
+     * Extends an object definition.
+     *
+     * Useful when you want to extend an existing object definition,
+     * without necessarily loading that object.
+     *
+     * @param string   $id       The unique identifier for the object
+     * @param callable $callable A service definition to extend the original
+     *
+     * @return callable The wrapped callable
+     *
+     * @throws UnknownIdentifierException        If the identifier is not defined
+     * @throws FrozenServiceException            If the service is frozen
+     * @throws InvalidServiceIdentifierException If the identifier belongs to a parameter
+     * @throws ExpectedInvokableException        If the extension callable is not a closure or an invokable object
+     */
+    public function extend($id, $callable)
+    {
+        if (!isset($this->keys[$id])) {
+            throw new UnknownIdentifierException($id);
+        }
+
+        if (isset($this->frozen[$id])) {
+            throw new FrozenServiceException($id);
+        }
+
+        if (!\is_object($this->values[$id]) || !\method_exists($this->values[$id], '__invoke')) {
+            throw new InvalidServiceIdentifierException($id);
+        }
+
+        if (isset($this->protected[$this->values[$id]])) {
+            @\trigger_error(\sprintf('How Pimple behaves when extending protected closures will be fixed in Pimple 4. Are you sure "%s" should be protected?', $id), \E_USER_DEPRECATED);
+        }
+
+        if (!\is_object($callable) || !\method_exists($callable, '__invoke')) {
+            throw new ExpectedInvokableException('Extension service definition is not a Closure or invokable object.');
+        }
+
+        $factory = $this->values[$id];
+
+        $extended = function ($c) use ($callable, $factory) {
+            return $callable($factory($c), $c);
+        };
+
+        if (isset($this->factories[$factory])) {
+            $this->factories->detach($factory);
+            $this->factories->attach($extended);
+        }
+
+        return $this[$id] = $extended;
+    }
+
+    /**
+     * Returns all defined value names.
+     *
+     * @return array An array of value names
+     */
+    public function keys()
+    {
+        return \array_keys($this->values);
+    }
+
+    /**
+     * Registers a service provider.
+     *
+     * @param ServiceProviderInterface $provider A ServiceProviderInterface instance
+     * @param array                    $values   An array of values that customizes the provider
+     *
+     * @return static
+     */
+    public function register(ServiceProviderInterface $provider, array $values = array())
+    {
+        $provider->register($this);
+
+        foreach ($values as $key => $value) {
+            $this[$key] = $value;
+        }
+
+        return $this;
+    }
+}
diff --git a/vendor/pimple/pimple/src/Pimple/Exception/ExpectedInvokableException.php b/vendor/pimple/pimple/src/Pimple/Exception/ExpectedInvokableException.php
new file mode 100755
index 0000000..7228421
--- /dev/null
+++ b/vendor/pimple/pimple/src/Pimple/Exception/ExpectedInvokableException.php
@@ -0,0 +1,38 @@
+<?php
+
+/*
+ * This file is part of Pimple.
+ *
+ * Copyright (c) 2009 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.
+ */
+
+namespace Pimple\Exception;
+
+use Psr\Container\ContainerExceptionInterface;
+
+/**
+ * A closure or invokable object was expected.
+ *
+ * @author Pascal Luna <skalpa@zetareticuli.org>
+ */
+class ExpectedInvokableException extends \InvalidArgumentException implements ContainerExceptionInterface
+{
+}
diff --git a/vendor/pimple/pimple/src/Pimple/Exception/FrozenServiceException.php b/vendor/pimple/pimple/src/Pimple/Exception/FrozenServiceException.php
new file mode 100755
index 0000000..e4d2f6d
--- /dev/null
+++ b/vendor/pimple/pimple/src/Pimple/Exception/FrozenServiceException.php
@@ -0,0 +1,45 @@
+<?php
+
+/*
+ * This file is part of Pimple.
+ *
+ * Copyright (c) 2009 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.
+ */
+
+namespace Pimple\Exception;
+
+use Psr\Container\ContainerExceptionInterface;
+
+/**
+ * An attempt to modify a frozen service was made.
+ *
+ * @author Pascal Luna <skalpa@zetareticuli.org>
+ */
+class FrozenServiceException extends \RuntimeException implements ContainerExceptionInterface
+{
+    /**
+     * @param string $id Identifier of the frozen service
+     */
+    public function __construct($id)
+    {
+        parent::__construct(\sprintf('Cannot override frozen service "%s".', $id));
+    }
+}
diff --git a/vendor/pimple/pimple/src/Pimple/Exception/InvalidServiceIdentifierException.php b/vendor/pimple/pimple/src/Pimple/Exception/InvalidServiceIdentifierException.php
new file mode 100755
index 0000000..91e82f9
--- /dev/null
+++ b/vendor/pimple/pimple/src/Pimple/Exception/InvalidServiceIdentifierException.php
@@ -0,0 +1,45 @@
+<?php
+
+/*
+ * This file is part of Pimple.
+ *
+ * Copyright (c) 2009 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.
+ */
+
+namespace Pimple\Exception;
+
+use Psr\Container\NotFoundExceptionInterface;
+
+/**
+ * An attempt to perform an operation that requires a service identifier was made.
+ *
+ * @author Pascal Luna <skalpa@zetareticuli.org>
+ */
+class InvalidServiceIdentifierException extends \InvalidArgumentException implements NotFoundExceptionInterface
+{
+    /**
+     * @param string $id The invalid identifier
+     */
+    public function __construct($id)
+    {
+        parent::__construct(\sprintf('Identifier "%s" does not contain an object definition.', $id));
+    }
+}
diff --git a/vendor/pimple/pimple/src/Pimple/Exception/UnknownIdentifierException.php b/vendor/pimple/pimple/src/Pimple/Exception/UnknownIdentifierException.php
new file mode 100755
index 0000000..fb6b626
--- /dev/null
+++ b/vendor/pimple/pimple/src/Pimple/Exception/UnknownIdentifierException.php
@@ -0,0 +1,45 @@
+<?php
+
+/*
+ * This file is part of Pimple.
+ *
+ * Copyright (c) 2009 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.
+ */
+
+namespace Pimple\Exception;
+
+use Psr\Container\NotFoundExceptionInterface;
+
+/**
+ * The identifier of a valid service or parameter was expected.
+ *
+ * @author Pascal Luna <skalpa@zetareticuli.org>
+ */
+class UnknownIdentifierException extends \InvalidArgumentException implements NotFoundExceptionInterface
+{
+    /**
+     * @param string $id The unknown identifier
+     */
+    public function __construct($id)
+    {
+        parent::__construct(\sprintf('Identifier "%s" is not defined.', $id));
+    }
+}
diff --git a/vendor/pimple/pimple/src/Pimple/Psr11/Container.php b/vendor/pimple/pimple/src/Pimple/Psr11/Container.php
new file mode 100755
index 0000000..cadbfff
--- /dev/null
+++ b/vendor/pimple/pimple/src/Pimple/Psr11/Container.php
@@ -0,0 +1,55 @@
+<?php
+
+/*
+ * This file is part of Pimple.
+ *
+ * Copyright (c) 2009-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.
+ */
+
+namespace Pimple\Psr11;
+
+use Pimple\Container as PimpleContainer;
+use Psr\Container\ContainerInterface;
+
+/**
+ * PSR-11 compliant wrapper.
+ *
+ * @author Pascal Luna <skalpa@zetareticuli.org>
+ */
+final class Container implements ContainerInterface
+{
+    private $pimple;
+
+    public function __construct(PimpleContainer $pimple)
+    {
+        $this->pimple = $pimple;
+    }
+
+    public function get($id)
+    {
+        return $this->pimple[$id];
+    }
+
+    public function has($id)
+    {
+        return isset($this->pimple[$id]);
+    }
+}
diff --git a/vendor/pimple/pimple/src/Pimple/Psr11/ServiceLocator.php b/vendor/pimple/pimple/src/Pimple/Psr11/ServiceLocator.php
new file mode 100755
index 0000000..3361c6f
--- /dev/null
+++ b/vendor/pimple/pimple/src/Pimple/Psr11/ServiceLocator.php
@@ -0,0 +1,75 @@
+<?php
+
+/*
+ * This file is part of Pimple.
+ *
+ * Copyright (c) 2009 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.
+ */
+
+namespace Pimple\Psr11;
+
+use Pimple\Container as PimpleContainer;
+use Pimple\Exception\UnknownIdentifierException;
+use Psr\Container\ContainerInterface;
+
+/**
+ * Pimple PSR-11 service locator.
+ *
+ * @author Pascal Luna <skalpa@zetareticuli.org>
+ */
+class ServiceLocator implements ContainerInterface
+{
+    private $container;
+    private $aliases = array();
+
+    /**
+     * @param PimpleContainer $container The Container instance used to locate services
+     * @param array           $ids       Array of service ids that can be located. String keys can be used to define aliases
+     */
+    public function __construct(PimpleContainer $container, array $ids)
+    {
+        $this->container = $container;
+
+        foreach ($ids as $key => $id) {
+            $this->aliases[\is_int($key) ? $id : $key] = $id;
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function get($id)
+    {
+        if (!isset($this->aliases[$id])) {
+            throw new UnknownIdentifierException($id);
+        }
+
+        return $this->container[$this->aliases[$id]];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function has($id)
+    {
+        return isset($this->aliases[$id]) && isset($this->container[$this->aliases[$id]]);
+    }
+}
diff --git a/vendor/pimple/pimple/src/Pimple/ServiceIterator.php b/vendor/pimple/pimple/src/Pimple/ServiceIterator.php
new file mode 100755
index 0000000..5cde518
--- /dev/null
+++ b/vendor/pimple/pimple/src/Pimple/ServiceIterator.php
@@ -0,0 +1,69 @@
+<?php
+
+/*
+ * This file is part of Pimple.
+ *
+ * Copyright (c) 2009 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.
+ */
+
+namespace Pimple;
+
+/**
+ * Lazy service iterator.
+ *
+ * @author Pascal Luna <skalpa@zetareticuli.org>
+ */
+final class ServiceIterator implements \Iterator
+{
+    private $container;
+    private $ids;
+
+    public function __construct(Container $container, array $ids)
+    {
+        $this->container = $container;
+        $this->ids = $ids;
+    }
+
+    public function rewind()
+    {
+        \reset($this->ids);
+    }
+
+    public function current()
+    {
+        return $this->container[\current($this->ids)];
+    }
+
+    public function key()
+    {
+        return \current($this->ids);
+    }
+
+    public function next()
+    {
+        \next($this->ids);
+    }
+
+    public function valid()
+    {
+        return null !== \key($this->ids);
+    }
+}
diff --git a/vendor/pimple/pimple/src/Pimple/ServiceProviderInterface.php b/vendor/pimple/pimple/src/Pimple/ServiceProviderInterface.php
new file mode 100755
index 0000000..c004594
--- /dev/null
+++ b/vendor/pimple/pimple/src/Pimple/ServiceProviderInterface.php
@@ -0,0 +1,46 @@
+<?php
+
+/*
+ * This file is part of Pimple.
+ *
+ * Copyright (c) 2009 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.
+ */
+
+namespace Pimple;
+
+/**
+ * Pimple service provider interface.
+ *
+ * @author  Fabien Potencier
+ * @author  Dominik Zogg
+ */
+interface ServiceProviderInterface
+{
+    /**
+     * Registers services on the given container.
+     *
+     * This method should only be used to configure services and parameters.
+     * It should not get services.
+     *
+     * @param Container $pimple A container instance
+     */
+    public function register(Container $pimple);
+}
diff --git a/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Invokable.php b/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Invokable.php
new file mode 100755
index 0000000..aba453b
--- /dev/null
+++ b/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Invokable.php
@@ -0,0 +1,38 @@
+<?php
+
+/*
+ * This file is part of Pimple.
+ *
+ * Copyright (c) 2009 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.
+ */
+
+namespace Pimple\Tests\Fixtures;
+
+class Invokable
+{
+    public function __invoke($value = null)
+    {
+        $service = new Service();
+        $service->value = $value;
+
+        return $service;
+    }
+}
diff --git a/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php b/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php
new file mode 100755
index 0000000..33cd4e5
--- /dev/null
+++ b/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php
@@ -0,0 +1,34 @@
+<?php
+
+/*
+ * This file is part of Pimple.
+ *
+ * Copyright (c) 2009 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.
+ */
+
+namespace Pimple\Tests\Fixtures;
+
+class NonInvokable
+{
+    public function __call($a, $b)
+    {
+    }
+}
diff --git a/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/PimpleServiceProvider.php b/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/PimpleServiceProvider.php
new file mode 100755
index 0000000..0c910af
--- /dev/null
+++ b/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/PimpleServiceProvider.php
@@ -0,0 +1,54 @@
+<?php
+
+/*
+ * This file is part of Pimple.
+ *
+ * Copyright (c) 2009 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.
+ */
+
+namespace Pimple\Tests\Fixtures;
+
+use Pimple\Container;
+use Pimple\ServiceProviderInterface;
+
+class PimpleServiceProvider implements ServiceProviderInterface
+{
+    /**
+     * Registers services on the given container.
+     *
+     * This method should only be used to configure services and parameters.
+     * It should not get services.
+     *
+     * @param Container $pimple An Container instance
+     */
+    public function register(Container $pimple)
+    {
+        $pimple['param'] = 'value';
+
+        $pimple['service'] = function () {
+            return new Service();
+        };
+
+        $pimple['factory'] = $pimple->factory(function () {
+            return new Service();
+        });
+    }
+}
diff --git a/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php b/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php
new file mode 100755
index 0000000..d71b184
--- /dev/null
+++ b/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php
@@ -0,0 +1,35 @@
+<?php
+
+/*
+ * This file is part of Pimple.
+ *
+ * Copyright (c) 2009 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.
+ */
+
+namespace Pimple\Tests\Fixtures;
+
+/**
+ * @author  Igor Wiedler <igor@wiedler.ch>
+ */
+class Service
+{
+    public $value;
+}
diff --git a/vendor/pimple/pimple/src/Pimple/Tests/PimpleServiceProviderInterfaceTest.php b/vendor/pimple/pimple/src/Pimple/Tests/PimpleServiceProviderInterfaceTest.php
new file mode 100755
index 0000000..8e5c4c7
--- /dev/null
+++ b/vendor/pimple/pimple/src/Pimple/Tests/PimpleServiceProviderInterfaceTest.php
@@ -0,0 +1,76 @@
+<?php
+
+/*
+ * This file is part of Pimple.
+ *
+ * Copyright (c) 2009 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.
+ */
+
+namespace Pimple\Tests;
+
+use Pimple\Container;
+
+/**
+ * @author  Dominik Zogg <dominik.zogg@gmail.com>
+ */
+class PimpleServiceProviderInterfaceTest extends \PHPUnit_Framework_TestCase
+{
+    public function testProvider()
+    {
+        $pimple = new Container();
+
+        $pimpleServiceProvider = new Fixtures\PimpleServiceProvider();
+        $pimpleServiceProvider->register($pimple);
+
+        $this->assertEquals('value', $pimple['param']);
+        $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $pimple['service']);
+
+        $serviceOne = $pimple['factory'];
+        $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne);
+
+        $serviceTwo = $pimple['factory'];
+        $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo);
+
+        $this->assertNotSame($serviceOne, $serviceTwo);
+    }
+
+    public function testProviderWithRegisterMethod()
+    {
+        $pimple = new Container();
+
+        $pimple->register(new Fixtures\PimpleServiceProvider(), array(
+            'anotherParameter' => 'anotherValue',
+        ));
+
+        $this->assertEquals('value', $pimple['param']);
+        $this->assertEquals('anotherValue', $pimple['anotherParameter']);
+
+        $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $pimple['service']);
+
+        $serviceOne = $pimple['factory'];
+        $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne);
+
+        $serviceTwo = $pimple['factory'];
+        $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo);
+
+        $this->assertNotSame($serviceOne, $serviceTwo);
+    }
+}
diff --git a/vendor/pimple/pimple/src/Pimple/Tests/PimpleTest.php b/vendor/pimple/pimple/src/Pimple/Tests/PimpleTest.php
new file mode 100755
index 0000000..acb66e0
--- /dev/null
+++ b/vendor/pimple/pimple/src/Pimple/Tests/PimpleTest.php
@@ -0,0 +1,589 @@
+<?php
+
+/*
+ * This file is part of Pimple.
+ *
+ * Copyright (c) 2009 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.
+ */
+
+namespace Pimple\Tests;
+
+use Pimple\Container;
+
+/**
+ * @author Igor Wiedler <igor@wiedler.ch>
+ */
+class PimpleTest extends \PHPUnit_Framework_TestCase
+{
+    public function testWithString()
+    {
+        $pimple = new Container();
+        $pimple['param'] = 'value';
+
+        $this->assertEquals('value', $pimple['param']);
+    }
+
+    public function testWithClosure()
+    {
+        $pimple = new Container();
+        $pimple['service'] = function () {
+            return new Fixtures\Service();
+        };
+
+        $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $pimple['service']);
+    }
+
+    public function testServicesShouldBeDifferent()
+    {
+        $pimple = new Container();
+        $pimple['service'] = $pimple->factory(function () {
+            return new Fixtures\Service();
+        });
+
+        $serviceOne = $pimple['service'];
+        $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne);
+
+        $serviceTwo = $pimple['service'];
+        $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo);
+
+        $this->assertNotSame($serviceOne, $serviceTwo);
+    }
+
+    public function testShouldPassContainerAsParameter()
+    {
+        $pimple = new Container();
+        $pimple['service'] = function () {
+            return new Fixtures\Service();
+        };
+        $pimple['container'] = function ($container) {
+            return $container;
+        };
+
+        $this->assertNotSame($pimple, $pimple['service']);
+        $this->assertSame($pimple, $pimple['container']);
+    }
+
+    public function testIsset()
+    {
+        $pimple = new Container();
+        $pimple['param'] = 'value';
+        $pimple['service'] = function () {
+            return new Fixtures\Service();
+        };
+
+        $pimple['null'] = null;
+
+        $this->assertTrue(isset($pimple['param']));
+        $this->assertTrue(isset($pimple['service']));
+        $this->assertTrue(isset($pimple['null']));
+        $this->assertFalse(isset($pimple['non_existent']));
+    }
+
+    public function testConstructorInjection()
+    {
+        $params = array('param' => 'value');
+        $pimple = new Container($params);
+
+        $this->assertSame($params['param'], $pimple['param']);
+    }
+
+    /**
+     * @expectedException \Pimple\Exception\UnknownIdentifierException
+     * @expectedExceptionMessage Identifier "foo" is not defined.
+     */
+    public function testOffsetGetValidatesKeyIsPresent()
+    {
+        $pimple = new Container();
+        echo $pimple['foo'];
+    }
+
+    /**
+     * @group legacy
+     * @expectedException \InvalidArgumentException
+     * @expectedExceptionMessage Identifier "foo" is not defined.
+     */
+    public function testLegacyOffsetGetValidatesKeyIsPresent()
+    {
+        $pimple = new Container();
+        echo $pimple['foo'];
+    }
+
+    public function testOffsetGetHonorsNullValues()
+    {
+        $pimple = new Container();
+        $pimple['foo'] = null;
+        $this->assertNull($pimple['foo']);
+    }
+
+    public function testUnset()
+    {
+        $pimple = new Container();
+        $pimple['param'] = 'value';
+        $pimple['service'] = function () {
+            return new Fixtures\Service();
+        };
+
+        unset($pimple['param'], $pimple['service']);
+        $this->assertFalse(isset($pimple['param']));
+        $this->assertFalse(isset($pimple['service']));
+    }
+
+    /**
+     * @dataProvider serviceDefinitionProvider
+     */
+    public function testShare($service)
+    {
+        $pimple = new Container();
+        $pimple['shared_service'] = $service;
+
+        $serviceOne = $pimple['shared_service'];
+        $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne);
+
+        $serviceTwo = $pimple['shared_service'];
+        $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo);
+
+        $this->assertSame($serviceOne, $serviceTwo);
+    }
+
+    /**
+     * @dataProvider serviceDefinitionProvider
+     */
+    public function testProtect($service)
+    {
+        $pimple = new Container();
+        $pimple['protected'] = $pimple->protect($service);
+
+        $this->assertSame($service, $pimple['protected']);
+    }
+
+    public function testGlobalFunctionNameAsParameterValue()
+    {
+        $pimple = new Container();
+        $pimple['global_function'] = 'strlen';
+        $this->assertSame('strlen', $pimple['global_function']);
+    }
+
+    public function testRaw()
+    {
+        $pimple = new Container();
+        $pimple['service'] = $definition = $pimple->factory(function () { return 'foo'; });
+        $this->assertSame($definition, $pimple->raw('service'));
+    }
+
+    public function testRawHonorsNullValues()
+    {
+        $pimple = new Container();
+        $pimple['foo'] = null;
+        $this->assertNull($pimple->raw('foo'));
+    }
+
+    public function testFluentRegister()
+    {
+        $pimple = new Container();
+        $this->assertSame($pimple, $pimple->register($this->getMockBuilder('Pimple\ServiceProviderInterface')->getMock()));
+    }
+
+    /**
+     * @expectedException \Pimple\Exception\UnknownIdentifierException
+     * @expectedExceptionMessage Identifier "foo" is not defined.
+     */
+    public function testRawValidatesKeyIsPresent()
+    {
+        $pimple = new Container();
+        $pimple->raw('foo');
+    }
+
+    /**
+     * @group legacy
+     * @expectedException \InvalidArgumentException
+     * @expectedExceptionMessage Identifier "foo" is not defined.
+     */
+    public function testLegacyRawValidatesKeyIsPresent()
+    {
+        $pimple = new Container();
+        $pimple->raw('foo');
+    }
+
+    /**
+     * @dataProvider serviceDefinitionProvider
+     */
+    public function testExtend($service)
+    {
+        $pimple = new Container();
+        $pimple['shared_service'] = function () {
+            return new Fixtures\Service();
+        };
+        $pimple['factory_service'] = $pimple->factory(function () {
+            return new Fixtures\Service();
+        });
+
+        $pimple->extend('shared_service', $service);
+        $serviceOne = $pimple['shared_service'];
+        $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne);
+        $serviceTwo = $pimple['shared_service'];
+        $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo);
+        $this->assertSame($serviceOne, $serviceTwo);
+        $this->assertSame($serviceOne->value, $serviceTwo->value);
+
+        $pimple->extend('factory_service', $service);
+        $serviceOne = $pimple['factory_service'];
+        $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne);
+        $serviceTwo = $pimple['factory_service'];
+        $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo);
+        $this->assertNotSame($serviceOne, $serviceTwo);
+        $this->assertNotSame($serviceOne->value, $serviceTwo->value);
+    }
+
+    public function testExtendDoesNotLeakWithFactories()
+    {
+        if (extension_loaded('pimple')) {
+            $this->markTestSkipped('Pimple extension does not support this test');
+        }
+        $pimple = new Container();
+
+        $pimple['foo'] = $pimple->factory(function () { return; });
+        $pimple['foo'] = $pimple->extend('foo', function ($foo, $pimple) { return; });
+        unset($pimple['foo']);
+
+        $p = new \ReflectionProperty($pimple, 'values');
+        $p->setAccessible(true);
+        $this->assertEmpty($p->getValue($pimple));
+
+        $p = new \ReflectionProperty($pimple, 'factories');
+        $p->setAccessible(true);
+        $this->assertCount(0, $p->getValue($pimple));
+    }
+
+    /**
+     * @expectedException \Pimple\Exception\UnknownIdentifierException
+     * @expectedExceptionMessage Identifier "foo" is not defined.
+     */
+    public function testExtendValidatesKeyIsPresent()
+    {
+        $pimple = new Container();
+        $pimple->extend('foo', function () {});
+    }
+
+    /**
+     * @group legacy
+     * @expectedException \InvalidArgumentException
+     * @expectedExceptionMessage Identifier "foo" is not defined.
+     */
+    public function testLegacyExtendValidatesKeyIsPresent()
+    {
+        $pimple = new Container();
+        $pimple->extend('foo', function () {});
+    }
+
+    public function testKeys()
+    {
+        $pimple = new Container();
+        $pimple['foo'] = 123;
+        $pimple['bar'] = 123;
+
+        $this->assertEquals(array('foo', 'bar'), $pimple->keys());
+    }
+
+    /** @test */
+    public function settingAnInvokableObjectShouldTreatItAsFactory()
+    {
+        $pimple = new Container();
+        $pimple['invokable'] = new Fixtures\Invokable();
+
+        $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $pimple['invokable']);
+    }
+
+    /** @test */
+    public function settingNonInvokableObjectShouldTreatItAsParameter()
+    {
+        $pimple = new Container();
+        $pimple['non_invokable'] = new Fixtures\NonInvokable();
+
+        $this->assertInstanceOf('Pimple\Tests\Fixtures\NonInvokable', $pimple['non_invokable']);
+    }
+
+    /**
+     * @dataProvider badServiceDefinitionProvider
+     * @expectedException \Pimple\Exception\ExpectedInvokableException
+     * @expectedExceptionMessage Service definition is not a Closure or invokable object.
+     */
+    public function testFactoryFailsForInvalidServiceDefinitions($service)
+    {
+        $pimple = new Container();
+        $pimple->factory($service);
+    }
+
+    /**
+     * @group legacy
+     * @dataProvider badServiceDefinitionProvider
+     * @expectedException \InvalidArgumentException
+     * @expectedExceptionMessage Service definition is not a Closure or invokable object.
+     */
+    public function testLegacyFactoryFailsForInvalidServiceDefinitions($service)
+    {
+        $pimple = new Container();
+        $pimple->factory($service);
+    }
+
+    /**
+     * @dataProvider badServiceDefinitionProvider
+     * @expectedException \Pimple\Exception\ExpectedInvokableException
+     * @expectedExceptionMessage Callable is not a Closure or invokable object.
+     */
+    public function testProtectFailsForInvalidServiceDefinitions($service)
+    {
+        $pimple = new Container();
+        $pimple->protect($service);
+    }
+
+    /**
+     * @group legacy
+     * @dataProvider badServiceDefinitionProvider
+     * @expectedException \InvalidArgumentException
+     * @expectedExceptionMessage Callable is not a Closure or invokable object.
+     */
+    public function testLegacyProtectFailsForInvalidServiceDefinitions($service)
+    {
+        $pimple = new Container();
+        $pimple->protect($service);
+    }
+
+    /**
+     * @dataProvider badServiceDefinitionProvider
+     * @expectedException \Pimple\Exception\InvalidServiceIdentifierException
+     * @expectedExceptionMessage Identifier "foo" does not contain an object definition.
+     */
+    public function testExtendFailsForKeysNotContainingServiceDefinitions($service)
+    {
+        $pimple = new Container();
+        $pimple['foo'] = $service;
+        $pimple->extend('foo', function () {});
+    }
+
+    /**
+     * @group legacy
+     * @dataProvider badServiceDefinitionProvider
+     * @expectedException \InvalidArgumentException
+     * @expectedExceptionMessage Identifier "foo" does not contain an object definition.
+     */
+    public function testLegacyExtendFailsForKeysNotContainingServiceDefinitions($service)
+    {
+        $pimple = new Container();
+        $pimple['foo'] = $service;
+        $pimple->extend('foo', function () {});
+    }
+
+    /**
+     * @group legacy
+     * @expectedDeprecation How Pimple behaves when extending protected closures will be fixed in Pimple 4. Are you sure "foo" should be protected?
+     */
+    public function testExtendingProtectedClosureDeprecation()
+    {
+        $pimple = new Container();
+        $pimple['foo'] = $pimple->protect(function () {
+            return 'bar';
+        });
+
+        $pimple->extend('foo', function ($value) {
+            return $value.'-baz';
+        });
+
+        $this->assertSame('bar-baz', $pimple['foo']);
+    }
+
+    /**
+     * @dataProvider badServiceDefinitionProvider
+     * @expectedException \Pimple\Exception\ExpectedInvokableException
+     * @expectedExceptionMessage Extension service definition is not a Closure or invokable object.
+     */
+    public function testExtendFailsForInvalidServiceDefinitions($service)
+    {
+        $pimple = new Container();
+        $pimple['foo'] = function () {};
+        $pimple->extend('foo', $service);
+    }
+
+    /**
+     * @group legacy
+     * @dataProvider badServiceDefinitionProvider
+     * @expectedException \InvalidArgumentException
+     * @expectedExceptionMessage Extension service definition is not a Closure or invokable object.
+     */
+    public function testLegacyExtendFailsForInvalidServiceDefinitions($service)
+    {
+        $pimple = new Container();
+        $pimple['foo'] = function () {};
+        $pimple->extend('foo', $service);
+    }
+
+    /**
+     * @expectedException \Pimple\Exception\FrozenServiceException
+     * @expectedExceptionMessage Cannot override frozen service "foo".
+     */
+    public function testExtendFailsIfFrozenServiceIsNonInvokable()
+    {
+        $pimple = new Container();
+        $pimple['foo'] = function () {
+            return new Fixtures\NonInvokable();
+        };
+        $foo = $pimple['foo'];
+
+        $pimple->extend('foo', function () {});
+    }
+
+    /**
+     * @expectedException \Pimple\Exception\FrozenServiceException
+     * @expectedExceptionMessage Cannot override frozen service "foo".
+     */
+    public function testExtendFailsIfFrozenServiceIsInvokable()
+    {
+        $pimple = new Container();
+        $pimple['foo'] = function () {
+            return new Fixtures\Invokable();
+        };
+        $foo = $pimple['foo'];
+
+        $pimple->extend('foo', function () {});
+    }
+
+    /**
+     * Provider for invalid service definitions.
+     */
+    public function badServiceDefinitionProvider()
+    {
+        return array(
+          array(123),
+          array(new Fixtures\NonInvokable()),
+        );
+    }
+
+    /**
+     * Provider for service definitions.
+     */
+    public function serviceDefinitionProvider()
+    {
+        return array(
+            array(function ($value) {
+                $service = new Fixtures\Service();
+                $service->value = $value;
+
+                return $service;
+            }),
+            array(new Fixtures\Invokable()),
+        );
+    }
+
+    public function testDefiningNewServiceAfterFreeze()
+    {
+        $pimple = new Container();
+        $pimple['foo'] = function () {
+            return 'foo';
+        };
+        $foo = $pimple['foo'];
+
+        $pimple['bar'] = function () {
+            return 'bar';
+        };
+        $this->assertSame('bar', $pimple['bar']);
+    }
+
+    /**
+     * @expectedException \Pimple\Exception\FrozenServiceException
+     * @expectedExceptionMessage Cannot override frozen service "foo".
+     */
+    public function testOverridingServiceAfterFreeze()
+    {
+        $pimple = new Container();
+        $pimple['foo'] = function () {
+            return 'foo';
+        };
+        $foo = $pimple['foo'];
+
+        $pimple['foo'] = function () {
+            return 'bar';
+        };
+    }
+
+    /**
+     * @group legacy
+     * @expectedException \RuntimeException
+     * @expectedExceptionMessage Cannot override frozen service "foo".
+     */
+    public function testLegacyOverridingServiceAfterFreeze()
+    {
+        $pimple = new Container();
+        $pimple['foo'] = function () {
+            return 'foo';
+        };
+        $foo = $pimple['foo'];
+
+        $pimple['foo'] = function () {
+            return 'bar';
+        };
+    }
+
+    public function testRemovingServiceAfterFreeze()
+    {
+        $pimple = new Container();
+        $pimple['foo'] = function () {
+            return 'foo';
+        };
+        $foo = $pimple['foo'];
+
+        unset($pimple['foo']);
+        $pimple['foo'] = function () {
+            return 'bar';
+        };
+        $this->assertSame('bar', $pimple['foo']);
+    }
+
+    public function testExtendingService()
+    {
+        $pimple = new Container();
+        $pimple['foo'] = function () {
+            return 'foo';
+        };
+        $pimple['foo'] = $pimple->extend('foo', function ($foo, $app) {
+            return "$foo.bar";
+        });
+        $pimple['foo'] = $pimple->extend('foo', function ($foo, $app) {
+            return "$foo.baz";
+        });
+        $this->assertSame('foo.bar.baz', $pimple['foo']);
+    }
+
+    public function testExtendingServiceAfterOtherServiceFreeze()
+    {
+        $pimple = new Container();
+        $pimple['foo'] = function () {
+            return 'foo';
+        };
+        $pimple['bar'] = function () {
+            return 'bar';
+        };
+        $foo = $pimple['foo'];
+
+        $pimple['bar'] = $pimple->extend('bar', function ($bar, $app) {
+            return "$bar.baz";
+        });
+        $this->assertSame('bar.baz', $pimple['bar']);
+    }
+}
diff --git a/vendor/pimple/pimple/src/Pimple/Tests/Psr11/ContainerTest.php b/vendor/pimple/pimple/src/Pimple/Tests/Psr11/ContainerTest.php
new file mode 100755
index 0000000..7ca2d7f
--- /dev/null
+++ b/vendor/pimple/pimple/src/Pimple/Tests/Psr11/ContainerTest.php
@@ -0,0 +1,77 @@
+<?php
+
+/*
+ * This file is part of Pimple.
+ *
+ * Copyright (c) 2009-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.
+ */
+
+namespace Pimple\Tests\Psr11;
+
+use PHPUnit\Framework\TestCase;
+use Pimple\Container;
+use Pimple\Psr11\Container as PsrContainer;
+use Pimple\Tests\Fixtures\Service;
+
+class ContainerTest extends TestCase
+{
+    public function testGetReturnsExistingService()
+    {
+        $pimple = new Container();
+        $pimple['service'] = function () {
+            return new Service();
+        };
+        $psr = new PsrContainer($pimple);
+
+        $this->assertSame($pimple['service'], $psr->get('service'));
+    }
+
+    /**
+     * @expectedException \Psr\Container\NotFoundExceptionInterface
+     * @expectedExceptionMessage Identifier "service" is not defined.
+     */
+    public function testGetThrowsExceptionIfServiceIsNotFound()
+    {
+        $pimple = new Container();
+        $psr = new PsrContainer($pimple);
+
+        $psr->get('service');
+    }
+
+    public function testHasReturnsTrueIfServiceExists()
+    {
+        $pimple = new Container();
+        $pimple['service'] = function () {
+            return new Service();
+        };
+        $psr = new PsrContainer($pimple);
+
+        $this->assertTrue($psr->has('service'));
+    }
+
+    public function testHasReturnsFalseIfServiceDoesNotExist()
+    {
+        $pimple = new Container();
+        $psr = new PsrContainer($pimple);
+
+        $this->assertFalse($psr->has('service'));
+    }
+}
diff --git a/vendor/pimple/pimple/src/Pimple/Tests/Psr11/ServiceLocatorTest.php b/vendor/pimple/pimple/src/Pimple/Tests/Psr11/ServiceLocatorTest.php
new file mode 100755
index 0000000..c9a0812
--- /dev/null
+++ b/vendor/pimple/pimple/src/Pimple/Tests/Psr11/ServiceLocatorTest.php
@@ -0,0 +1,134 @@
+<?php
+
+/*
+ * This file is part of Pimple.
+ *
+ * Copyright (c) 2009 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.
+ */
+
+namespace Pimple\Tests\Psr11;
+
+use PHPUnit\Framework\TestCase;
+use Pimple\Container;
+use Pimple\Psr11\ServiceLocator;
+use Pimple\Tests\Fixtures;
+
+/**
+ * ServiceLocator test case.
+ *
+ * @author Pascal Luna <skalpa@zetareticuli.org>
+ */
+class ServiceLocatorTest extends TestCase
+{
+    public function testCanAccessServices()
+    {
+        $pimple = new Container();
+        $pimple['service'] = function () {
+            return new Fixtures\Service();
+        };
+        $locator = new ServiceLocator($pimple, array('service'));
+
+        $this->assertSame($pimple['service'], $locator->get('service'));
+    }
+
+    public function testCanAccessAliasedServices()
+    {
+        $pimple = new Container();
+        $pimple['service'] = function () {
+            return new Fixtures\Service();
+        };
+        $locator = new ServiceLocator($pimple, array('alias' => 'service'));
+
+        $this->assertSame($pimple['service'], $locator->get('alias'));
+    }
+
+    /**
+     * @expectedException \Pimple\Exception\UnknownIdentifierException
+     * @expectedExceptionMessage Identifier "service" is not defined.
+     */
+    public function testCannotAccessAliasedServiceUsingRealIdentifier()
+    {
+        $pimple = new Container();
+        $pimple['service'] = function () {
+            return new Fixtures\Service();
+        };
+        $locator = new ServiceLocator($pimple, array('alias' => 'service'));
+
+        $service = $locator->get('service');
+    }
+
+    /**
+     * @expectedException \Pimple\Exception\UnknownIdentifierException
+     * @expectedExceptionMessage Identifier "foo" is not defined.
+     */
+    public function testGetValidatesServiceCanBeLocated()
+    {
+        $pimple = new Container();
+        $pimple['service'] = function () {
+            return new Fixtures\Service();
+        };
+        $locator = new ServiceLocator($pimple, array('alias' => 'service'));
+
+        $service = $locator->get('foo');
+    }
+
+    /**
+     * @expectedException \Pimple\Exception\UnknownIdentifierException
+     * @expectedExceptionMessage Identifier "invalid" is not defined.
+     */
+    public function testGetValidatesTargetServiceExists()
+    {
+        $pimple = new Container();
+        $pimple['service'] = function () {
+            return new Fixtures\Service();
+        };
+        $locator = new ServiceLocator($pimple, array('alias' => 'invalid'));
+
+        $service = $locator->get('alias');
+    }
+
+    public function testHasValidatesServiceCanBeLocated()
+    {
+        $pimple = new Container();
+        $pimple['service1'] = function () {
+            return new Fixtures\Service();
+        };
+        $pimple['service2'] = function () {
+            return new Fixtures\Service();
+        };
+        $locator = new ServiceLocator($pimple, array('service1'));
+
+        $this->assertTrue($locator->has('service1'));
+        $this->assertFalse($locator->has('service2'));
+    }
+
+    public function testHasChecksIfTargetServiceExists()
+    {
+        $pimple = new Container();
+        $pimple['service'] = function () {
+            return new Fixtures\Service();
+        };
+        $locator = new ServiceLocator($pimple, array('foo' => 'service', 'bar' => 'invalid'));
+
+        $this->assertTrue($locator->has('foo'));
+        $this->assertFalse($locator->has('bar'));
+    }
+}
diff --git a/vendor/pimple/pimple/src/Pimple/Tests/ServiceIteratorTest.php b/vendor/pimple/pimple/src/Pimple/Tests/ServiceIteratorTest.php
new file mode 100755
index 0000000..5dd52f0
--- /dev/null
+++ b/vendor/pimple/pimple/src/Pimple/Tests/ServiceIteratorTest.php
@@ -0,0 +1,52 @@
+<?php
+
+/*
+ * This file is part of Pimple.
+ *
+ * Copyright (c) 2009 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.
+ */
+
+namespace Pimple\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Pimple\Container;
+use Pimple\ServiceIterator;
+use Pimple\Tests\Fixtures\Service;
+
+class ServiceIteratorTest extends TestCase
+{
+    public function testIsIterable()
+    {
+        $pimple = new Container();
+        $pimple['service1'] = function () {
+            return new Service();
+        };
+        $pimple['service2'] = function () {
+            return new Service();
+        };
+        $pimple['service3'] = function () {
+            return new Service();
+        };
+        $iterator = new ServiceIterator($pimple, array('service1', 'service2'));
+
+        $this->assertSame(array('service1' => $pimple['service1'], 'service2' => $pimple['service2']), iterator_to_array($iterator));
+    }
+}
diff --git a/vendor/psr/cache/CHANGELOG.md b/vendor/psr/cache/CHANGELOG.md
new file mode 100755
index 0000000..58ddab0
--- /dev/null
+++ b/vendor/psr/cache/CHANGELOG.md
@@ -0,0 +1,16 @@
+# Changelog
+
+All notable changes to this project will be documented in this file, in reverse chronological order by release.
+
+## 1.0.1 - 2016-08-06
+
+### Fixed
+
+- Make spacing consistent in phpdoc annotations php-fig/cache#9 - chalasr
+- Fix grammar in phpdoc annotations php-fig/cache#10 - chalasr
+- Be more specific in docblocks that `getItems()` and `deleteItems()` take an array of strings (`string[]`) compared to just `array` php-fig/cache#8 - GrahamCampbell
+- For `expiresAt()` and `expiresAfter()` in CacheItemInterface fix docblock to specify null as a valid parameters as well as an implementation of DateTimeInterface php-fig/cache#7 - GrahamCampbell
+
+## 1.0.0 - 2015-12-11
+
+Initial stable release; reflects accepted PSR-6 specification
diff --git a/vendor/psr/cache/LICENSE.txt b/vendor/psr/cache/LICENSE.txt
new file mode 100755
index 0000000..b1c2c97
--- /dev/null
+++ b/vendor/psr/cache/LICENSE.txt
@@ -0,0 +1,19 @@
+Copyright (c) 2015 PHP Framework Interoperability Group
+
+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.
diff --git a/vendor/psr/cache/README.md b/vendor/psr/cache/README.md
new file mode 100755
index 0000000..c8706ce
--- /dev/null
+++ b/vendor/psr/cache/README.md
@@ -0,0 +1,9 @@
+PSR Cache
+=========
+
+This repository holds all interfaces defined by
+[PSR-6](http://www.php-fig.org/psr/psr-6/).
+
+Note that this is not a Cache implementation of its own. It is merely an
+interface that describes a Cache implementation. See the specification for more 
+details.
diff --git a/vendor/psr/cache/composer.json b/vendor/psr/cache/composer.json
new file mode 100755
index 0000000..e828fec
--- /dev/null
+++ b/vendor/psr/cache/composer.json
@@ -0,0 +1,25 @@
+{
+    "name": "psr/cache",
+    "description": "Common interface for caching libraries",
+    "keywords": ["psr", "psr-6", "cache"],
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "PHP-FIG",
+            "homepage": "http://www.php-fig.org/"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "Psr\\Cache\\": "src/"
+        }
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.0.x-dev"
+        }
+    }
+}
diff --git a/vendor/psr/cache/src/CacheException.php b/vendor/psr/cache/src/CacheException.php
new file mode 100755
index 0000000..e27f22f
--- /dev/null
+++ b/vendor/psr/cache/src/CacheException.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace Psr\Cache;
+
+/**
+ * Exception interface for all exceptions thrown by an Implementing Library.
+ */
+interface CacheException
+{
+}
diff --git a/vendor/psr/cache/src/CacheItemInterface.php b/vendor/psr/cache/src/CacheItemInterface.php
new file mode 100755
index 0000000..63d05dd
--- /dev/null
+++ b/vendor/psr/cache/src/CacheItemInterface.php
@@ -0,0 +1,105 @@
+<?php
+
+namespace Psr\Cache;
+
+/**
+ * CacheItemInterface defines an interface for interacting with objects inside a cache.
+ *
+ * Each Item object MUST be associated with a specific key, which can be set
+ * according to the implementing system and is typically passed by the
+ * Cache\CacheItemPoolInterface object.
+ *
+ * The Cache\CacheItemInterface object encapsulates the storage and retrieval of
+ * cache items. Each Cache\CacheItemInterface is generated by a
+ * Cache\CacheItemPoolInterface object, which is responsible for any required
+ * setup as well as associating the object with a unique Key.
+ * Cache\CacheItemInterface objects MUST be able to store and retrieve any type
+ * of PHP value defined in the Data section of the specification.
+ *
+ * Calling Libraries MUST NOT instantiate Item objects themselves. They may only
+ * be requested from a Pool object via the getItem() method.  Calling Libraries
+ * SHOULD NOT assume that an Item created by one Implementing Library is
+ * compatible with a Pool from another Implementing Library.
+ */
+interface CacheItemInterface
+{
+    /**
+     * Returns the key for the current cache item.
+     *
+     * The key is loaded by the Implementing Library, but should be available to
+     * the higher level callers when needed.
+     *
+     * @return string
+     *   The key string for this cache item.
+     */
+    public function getKey();
+
+    /**
+     * Retrieves the value of the item from the cache associated with this object's key.
+     *
+     * The value returned must be identical to the value originally stored by set().
+     *
+     * If isHit() returns false, this method MUST return null. Note that null
+     * is a legitimate cached value, so the isHit() method SHOULD be used to
+     * differentiate between "null value was found" and "no value was found."
+     *
+     * @return mixed
+     *   The value corresponding to this cache item's key, or null if not found.
+     */
+    public function get();
+
+    /**
+     * Confirms if the cache item lookup resulted in a cache hit.
+     *
+     * Note: This method MUST NOT have a race condition between calling isHit()
+     * and calling get().
+     *
+     * @return bool
+     *   True if the request resulted in a cache hit. False otherwise.
+     */
+    public function isHit();
+
+    /**
+     * Sets the value represented by this cache item.
+     *
+     * The $value argument may be any item that can be serialized by PHP,
+     * although the method of serialization is left up to the Implementing
+     * Library.
+     *
+     * @param mixed $value
+     *   The serializable value to be stored.
+     *
+     * @return static
+     *   The invoked object.
+     */
+    public function set($value);
+
+    /**
+     * Sets the expiration time for this cache item.
+     *
+     * @param \DateTimeInterface|null $expiration
+     *   The point in time after which the item MUST be considered expired.
+     *   If null is passed explicitly, a default value MAY be used. If none is set,
+     *   the value should be stored permanently or for as long as the
+     *   implementation allows.
+     *
+     * @return static
+     *   The called object.
+     */
+    public function expiresAt($expiration);
+
+    /**
+     * Sets the expiration time for this cache item.
+     *
+     * @param int|\DateInterval|null $time
+     *   The period of time from the present after which the item MUST be considered
+     *   expired. An integer parameter is understood to be the time in seconds until
+     *   expiration. If null is passed explicitly, a default value MAY be used.
+     *   If none is set, the value should be stored permanently or for as long as the
+     *   implementation allows.
+     *
+     * @return static
+     *   The called object.
+     */
+    public function expiresAfter($time);
+}
diff --git a/vendor/psr/cache/src/CacheItemPoolInterface.php b/vendor/psr/cache/src/CacheItemPoolInterface.php
new file mode 100755
index 0000000..0351419
--- /dev/null
+++ b/vendor/psr/cache/src/CacheItemPoolInterface.php
@@ -0,0 +1,138 @@
+<?php
+
+namespace Psr\Cache;
+
+/**
+ * CacheItemPoolInterface generates CacheItemInterface objects.
+ *
+ * The primary purpose of Cache\CacheItemPoolInterface is to accept a key from
+ * the Calling Library and return the associated Cache\CacheItemInterface object.
+ * It is also the primary point of interaction with the entire cache collection.
+ * All configuration and initialization of the Pool is left up to an
+ * Implementing Library.
+ */
+interface CacheItemPoolInterface
+{
+    /**
+     * Returns a Cache Item representing the specified key.
+     *
+     * This method must always return a CacheItemInterface object, even in case of
+     * a cache miss. It MUST NOT return null.
+     *
+     * @param string $key
+     *   The key for which to return the corresponding Cache Item.
+     *
+     * @throws InvalidArgumentException
+     *   If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException
+     *   MUST be thrown.
+     *
+     * @return CacheItemInterface
+     *   The corresponding Cache Item.
+     */
+    public function getItem($key);
+
+    /**
+     * Returns a traversable set of cache items.
+     *
+     * @param string[] $keys
+     *   An indexed array of keys of items to retrieve.
+     *
+     * @throws InvalidArgumentException
+     *   If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException
+     *   MUST be thrown.
+     *
+     * @return array|\Traversable
+     *   A traversable collection of Cache Items keyed by the cache keys of
+     *   each item. A Cache item will be returned for each key, even if that
+     *   key is not found. However, if no keys are specified then an empty
+     *   traversable MUST be returned instead.
+     */
+    public function getItems(array $keys = array());
+
+    /**
+     * Confirms if the cache contains specified cache item.
+     *
+     * Note: This method MAY avoid retrieving the cached value for performance reasons.
+     * This could result in a race condition with CacheItemInterface::get(). To avoid
+     * such situation use CacheItemInterface::isHit() instead.
+     *
+     * @param string $key
+     *   The key for which to check existence.
+     *
+     * @throws InvalidArgumentException
+     *   If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException
+     *   MUST be thrown.
+     *
+     * @return bool
+     *   True if item exists in the cache, false otherwise.
+     */
+    public function hasItem($key);
+
+    /**
+     * Deletes all items in the pool.
+     *
+     * @return bool
+     *   True if the pool was successfully cleared. False if there was an error.
+     */
+    public function clear();
+
+    /**
+     * Removes the item from the pool.
+     *
+     * @param string $key
+     *   The key to delete.
+     *
+     * @throws InvalidArgumentException
+     *   If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException
+     *   MUST be thrown.
+     *
+     * @return bool
+     *   True if the item was successfully removed. False if there was an error.
+     */
+    public function deleteItem($key);
+
+    /**
+     * Removes multiple items from the pool.
+     *
+     * @param string[] $keys
+     *   An array of keys that should be removed from the pool.
+
+     * @throws InvalidArgumentException
+     *   If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException
+     *   MUST be thrown.
+     *
+     * @return bool
+     *   True if the items were successfully removed. False if there was an error.
+     */
+    public function deleteItems(array $keys);
+
+    /**
+     * Persists a cache item immediately.
+     *
+     * @param CacheItemInterface $item
+     *   The cache item to save.
+     *
+     * @return bool
+     *   True if the item was successfully persisted. False if there was an error.
+     */
+    public function save(CacheItemInterface $item);
+
+    /**
+     * Sets a cache item to be persisted later.
+     *
+     * @param CacheItemInterface $item
+     *   The cache item to save.
+     *
+     * @return bool
+     *   False if the item could not be queued or if a commit was attempted and failed. True otherwise.
+     */
+    public function saveDeferred(CacheItemInterface $item);
+
+    /**
+     * Persists any deferred cache items.
+     *
+     * @return bool
+     *   True if all not-yet-saved items were successfully saved or there were none. False otherwise.
+     */
+    public function commit();
+}
diff --git a/vendor/psr/cache/src/InvalidArgumentException.php b/vendor/psr/cache/src/InvalidArgumentException.php
new file mode 100755
index 0000000..be7c6fa
--- /dev/null
+++ b/vendor/psr/cache/src/InvalidArgumentException.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace Psr\Cache;
+
+/**
+ * Exception interface for invalid cache arguments.
+ *
+ * Any time an invalid argument is passed into a method it must throw an
+ * exception class which implements Psr\Cache\InvalidArgumentException.
+ */
+interface InvalidArgumentException extends CacheException
+{
+}
diff --git a/vendor/psr/container/.gitignore b/vendor/psr/container/.gitignore
new file mode 100755
index 0000000..b2395aa
--- /dev/null
+++ b/vendor/psr/container/.gitignore
@@ -0,0 +1,3 @@
+composer.lock
+composer.phar
+/vendor/
diff --git a/vendor/psr/container/LICENSE b/vendor/psr/container/LICENSE
new file mode 100755
index 0000000..2877a48
--- /dev/null
+++ b/vendor/psr/container/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2013-2016 container-interop
+Copyright (c) 2016 PHP Framework Interoperability Group
+
+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.
diff --git a/vendor/psr/container/README.md b/vendor/psr/container/README.md
new file mode 100755
index 0000000..084f6df
--- /dev/null
+++ b/vendor/psr/container/README.md
@@ -0,0 +1,5 @@
+# PSR Container
+
+This repository holds all interfaces/classes/traits related to [PSR-11](https://github.com/container-interop/fig-standards/blob/master/proposed/container.md).
+
+Note that this is not a container implementation of its own. See the specification for more details.
diff --git a/vendor/psr/container/composer.json b/vendor/psr/container/composer.json
new file mode 100755
index 0000000..b8ee012
--- /dev/null
+++ b/vendor/psr/container/composer.json
@@ -0,0 +1,27 @@
+{
+    "name": "psr/container",
+    "type": "library",
+    "description": "Common Container Interface (PHP FIG PSR-11)",
+    "keywords": ["psr", "psr-11", "container", "container-interop", "container-interface"],
+    "homepage": "https://github.com/php-fig/container",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "PHP-FIG",
+            "homepage": "http://www.php-fig.org/"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "Psr\\Container\\": "src/"
+        }
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.0.x-dev"
+        }
+    }
+}
diff --git a/vendor/psr/container/src/ContainerExceptionInterface.php b/vendor/psr/container/src/ContainerExceptionInterface.php
new file mode 100755
index 0000000..d35c6b4
--- /dev/null
+++ b/vendor/psr/container/src/ContainerExceptionInterface.php
@@ -0,0 +1,13 @@
+<?php
+/**
+ * @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace Psr\Container;
+
+/**
+ * Base interface representing a generic exception in a container.
+ */
+interface ContainerExceptionInterface
+{
+}
diff --git a/vendor/psr/container/src/ContainerInterface.php b/vendor/psr/container/src/ContainerInterface.php
new file mode 100755
index 0000000..c3a7206
--- /dev/null
+++ b/vendor/psr/container/src/ContainerInterface.php
@@ -0,0 +1,37 @@
+<?php
+/**
+ * @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace Psr\Container;
+
+/**
+ * Describes the interface of a container that exposes methods to read its entries.
+ */
+interface ContainerInterface
+{
+    /**
+     * Finds an entry of the container by its identifier and returns it.
+     *
+     * @param string $id Identifier of the entry to look for.
+     *
+     * @throws NotFoundExceptionInterface  No entry was found for **this** identifier.
+     * @throws ContainerExceptionInterface Error while retrieving the entry.
+     *
+     * @return mixed Entry.
+     */
+    public function get($id);
+
+    /**
+     * Returns true if the container can return an entry for the given identifier.
+     * Returns false otherwise.
+     *
+     * `has($id)` returning true does not mean that `get($id)` will not throw an exception.
+     * It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`.
+     *
+     * @param string $id Identifier of the entry to look for.
+     *
+     * @return bool
+     */
+    public function has($id);
+}
diff --git a/vendor/psr/container/src/NotFoundExceptionInterface.php b/vendor/psr/container/src/NotFoundExceptionInterface.php
new file mode 100755
index 0000000..6566704
--- /dev/null
+++ b/vendor/psr/container/src/NotFoundExceptionInterface.php
@@ -0,0 +1,13 @@
+<?php
+/**
+ * @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace Psr\Container;
+
+/**
+ * No entry was found in the container.
+ */
+interface NotFoundExceptionInterface extends ContainerExceptionInterface
+{
+}
diff --git a/vendor/psr/http-client/CHANGELOG.md b/vendor/psr/http-client/CHANGELOG.md
new file mode 100755
index 0000000..e2dc25f
--- /dev/null
+++ b/vendor/psr/http-client/CHANGELOG.md
@@ -0,0 +1,23 @@
+# Changelog
+
+All notable changes to this project will be documented in this file, in reverse chronological order by release.
+
+## 1.0.1
+
+Allow installation with PHP 8. No code changes.
+
+## 1.0.0
+
+First stable release. No changes since 0.3.0.
+
+## 0.3.0
+
+Added Interface suffix on exceptions
+ 
+## 0.2.0 
+
+All exceptions are in `Psr\Http\Client` namespace
+
+## 0.1.0
+
+First release
diff --git a/vendor/psr/http-client/LICENSE b/vendor/psr/http-client/LICENSE
new file mode 100755
index 0000000..cd5e002
--- /dev/null
+++ b/vendor/psr/http-client/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2017 PHP Framework Interoperability Group
+
+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.
diff --git a/vendor/psr/http-client/README.md b/vendor/psr/http-client/README.md
new file mode 100755
index 0000000..6876b84
--- /dev/null
+++ b/vendor/psr/http-client/README.md
@@ -0,0 +1,12 @@
+HTTP Client
+===========
+
+This repository holds all the common code related to [PSR-18 (HTTP Client)][psr-url].
+
+Note that this is not a HTTP Client implementation of its own. It is merely abstractions that describe the components of a HTTP Client.
+
+The installable [package][package-url] and [implementations][implementation-url] are listed on Packagist.
+
+[psr-url]: http://www.php-fig.org/psr/psr-18
+[package-url]: https://packagist.org/packages/psr/http-client
+[implementation-url]: https://packagist.org/providers/psr/http-client-implementation
diff --git a/vendor/psr/http-client/composer.json b/vendor/psr/http-client/composer.json
new file mode 100755
index 0000000..c195f8f
--- /dev/null
+++ b/vendor/psr/http-client/composer.json
@@ -0,0 +1,27 @@
+{
+    "name": "psr/http-client",
+    "description": "Common interface for HTTP clients",
+    "keywords": ["psr", "psr-18", "http", "http-client"],
+    "homepage": "https://github.com/php-fig/http-client",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "PHP-FIG",
+            "homepage": "http://www.php-fig.org/"
+        }
+    ],
+    "require": {
+        "php": "^7.0 || ^8.0",
+        "psr/http-message": "^1.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "Psr\\Http\\Client\\": "src/"
+        }
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.0.x-dev"
+        }
+    }
+}
diff --git a/vendor/psr/http-client/src/ClientExceptionInterface.php b/vendor/psr/http-client/src/ClientExceptionInterface.php
new file mode 100755
index 0000000..aa0b9cf
--- /dev/null
+++ b/vendor/psr/http-client/src/ClientExceptionInterface.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace Psr\Http\Client;
+
+/**
+ * Every HTTP client related exception MUST implement this interface.
+ */
+interface ClientExceptionInterface extends \Throwable
+{
+}
diff --git a/vendor/psr/http-client/src/ClientInterface.php b/vendor/psr/http-client/src/ClientInterface.php
new file mode 100755
index 0000000..ad99fd4
--- /dev/null
+++ b/vendor/psr/http-client/src/ClientInterface.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace Psr\Http\Client;
+
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+interface ClientInterface
+{
+    /**
+     * Sends a PSR-7 request and returns a PSR-7 response.
+     *
+     * @param RequestInterface $request
+     *
+     * @return ResponseInterface
+     *
+     * @throws \Psr\Http\Client\ClientExceptionInterface If an error happens while processing the request.
+     */
+    public function sendRequest(RequestInterface $request): ResponseInterface;
+}
diff --git a/vendor/psr/http-client/src/NetworkExceptionInterface.php b/vendor/psr/http-client/src/NetworkExceptionInterface.php
new file mode 100755
index 0000000..4a11627
--- /dev/null
+++ b/vendor/psr/http-client/src/NetworkExceptionInterface.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Psr\Http\Client;
+
+use Psr\Http\Message\RequestInterface;
+
+/**
+ * Thrown when the request cannot be completed because of network issues.
+ *
+ * There is no response object as this exception is thrown when no response has been received.
+ *
+ * Example: the target host name can not be resolved or the connection failed.
+ */
+interface NetworkExceptionInterface extends ClientExceptionInterface
+{
+    /**
+     * Returns the request.
+     *
+     * The request object MAY be a different object from the one passed to ClientInterface::sendRequest()
+     *
+     * @return RequestInterface
+     */
+    public function getRequest(): RequestInterface;
+}
diff --git a/vendor/psr/http-client/src/RequestExceptionInterface.php b/vendor/psr/http-client/src/RequestExceptionInterface.php
new file mode 100755
index 0000000..b17fc34
--- /dev/null
+++ b/vendor/psr/http-client/src/RequestExceptionInterface.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Psr\Http\Client;
+
+use Psr\Http\Message\RequestInterface;
+
+/**
+ * Exception for when a request failed.
+ *
+ * Examples:
+ *      - Request is invalid (e.g. method is missing)
+ *      - Runtime request errors (e.g. the body stream is not seekable)
+ */
+interface RequestExceptionInterface extends ClientExceptionInterface
+{
+    /**
+     * Returns the request.
+     *
+     * The request object MAY be a different object from the one passed to ClientInterface::sendRequest()
+     *
+     * @return RequestInterface
+     */
+    public function getRequest(): RequestInterface;
+}
diff --git a/vendor/psr/http-factory/.gitignore b/vendor/psr/http-factory/.gitignore
new file mode 100755
index 0000000..d8a7996
--- /dev/null
+++ b/vendor/psr/http-factory/.gitignore
@@ -0,0 +1,2 @@
+composer.lock
+vendor/
diff --git a/vendor/psr/http-factory/.pullapprove.yml b/vendor/psr/http-factory/.pullapprove.yml
new file mode 100755
index 0000000..8cf0819
--- /dev/null
+++ b/vendor/psr/http-factory/.pullapprove.yml
@@ -0,0 +1,7 @@
+extends: default
+reviewers:
+    -
+        name: contributors
+        required: 1
+        teams:
+            - http-factory-contributors
diff --git a/vendor/psr/http-factory/LICENSE b/vendor/psr/http-factory/LICENSE
new file mode 100755
index 0000000..3f1559b
--- /dev/null
+++ b/vendor/psr/http-factory/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 PHP-FIG
+
+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.
diff --git a/vendor/psr/http-factory/README.md b/vendor/psr/http-factory/README.md
new file mode 100755
index 0000000..41d362a
--- /dev/null
+++ b/vendor/psr/http-factory/README.md
@@ -0,0 +1,10 @@
+HTTP Factories
+==============
+
+This repository holds all interfaces related to [PSR-17 (HTTP Message Factories)][psr-17]. 
+Please refer to the specification for a description.
+
+You can find implementations of the specification by looking for packages providing the 
+[psr/http-factory-implementation](https://packagist.org/providers/psr/http-factory-implementation) virtual package.
+
+[psr-17]: https://www.php-fig.org/psr/psr-17/
diff --git a/vendor/psr/http-factory/composer.json b/vendor/psr/http-factory/composer.json
new file mode 100755
index 0000000..af62b29
--- /dev/null
+++ b/vendor/psr/http-factory/composer.json
@@ -0,0 +1,35 @@
+{
+    "name": "psr/http-factory",
+    "description": "Common interfaces for PSR-7 HTTP message factories",
+    "keywords": [
+        "psr",
+        "psr-7",
+        "psr-17",
+        "http",
+        "factory",
+        "message",
+        "request",
+        "response"
+    ],
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "PHP-FIG",
+            "homepage": "http://www.php-fig.org/"
+        }
+    ],
+    "require": {
+        "php": ">=7.0.0",
+        "psr/http-message": "^1.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "Psr\\Http\\Message\\": "src/"
+        }
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.0.x-dev"
+        }
+    }
+}
diff --git a/vendor/psr/http-factory/src/RequestFactoryInterface.php b/vendor/psr/http-factory/src/RequestFactoryInterface.php
new file mode 100755
index 0000000..cb39a08
--- /dev/null
+++ b/vendor/psr/http-factory/src/RequestFactoryInterface.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Psr\Http\Message;
+
+interface RequestFactoryInterface
+{
+    /**
+     * Create a new request.
+     *
+     * @param string $method The HTTP method associated with the request.
+     * @param UriInterface|string $uri The URI associated with the request. If
+     *     the value is a string, the factory MUST create a UriInterface
+     *     instance based on it.
+     *
+     * @return RequestInterface
+     */
+    public function createRequest(string $method, $uri): RequestInterface;
+}
diff --git a/vendor/psr/http-factory/src/ResponseFactoryInterface.php b/vendor/psr/http-factory/src/ResponseFactoryInterface.php
new file mode 100755
index 0000000..212562f
--- /dev/null
+++ b/vendor/psr/http-factory/src/ResponseFactoryInterface.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Psr\Http\Message;
+
+interface ResponseFactoryInterface
+{
+    /**
+     * Create a new response.
+     *
+     * @param int $code HTTP status code; defaults to 200
+     * @param string $reasonPhrase Reason phrase to associate with status code
+     *     in generated response; if none is provided implementations MAY use
+     *     the defaults as suggested in the HTTP specification.
+     *
+     * @return ResponseInterface
+     */
+    public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface;
+}
diff --git a/vendor/psr/http-factory/src/ServerRequestFactoryInterface.php b/vendor/psr/http-factory/src/ServerRequestFactoryInterface.php
new file mode 100755
index 0000000..9fe031a
--- /dev/null
+++ b/vendor/psr/http-factory/src/ServerRequestFactoryInterface.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Psr\Http\Message;
+
+interface ServerRequestFactoryInterface
+{
+    /**
+     * Create a new server request.
+     *
+     * Note that server-params are taken precisely as given - no parsing/processing
+     * of the given values is performed, and, in particular, no attempt is made to
+     * determine the HTTP method or URI, which must be provided explicitly.
+     *
+     * @param string $method The HTTP method associated with the request.
+     * @param UriInterface|string $uri The URI associated with the request. If
+     *     the value is a string, the factory MUST create a UriInterface
+     *     instance based on it.
+     * @param array $serverParams Array of SAPI parameters with which to seed
+     *     the generated request instance.
+     *
+     * @return ServerRequestInterface
+     */
+    public function createServerRequest(string $method, $uri, array $serverParams = []): ServerRequestInterface;
+}
diff --git a/vendor/psr/http-factory/src/StreamFactoryInterface.php b/vendor/psr/http-factory/src/StreamFactoryInterface.php
new file mode 100755
index 0000000..90e3240
--- /dev/null
+++ b/vendor/psr/http-factory/src/StreamFactoryInterface.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Psr\Http\Message;
+
+interface StreamFactoryInterface
+{
+    /**
+     * Create a new stream from a string.
+     *
+     * The stream SHOULD be created with a temporary resource.
+     *
+     * @param string $content String content with which to populate the stream.
+     *
+     * @return StreamInterface
+     */
+    public function createStream(string $content = ''): StreamInterface;
+
+    /**
+     * Create a stream from an existing file.
+     *
+     * The file MUST be opened using the given mode, which may be any mode
+     * supported by the `fopen` function.
+     *
+     * The `$filename` MAY be any string supported by `fopen()`.
+     *
+     * @param string $filename Filename or stream URI to use as basis of stream.
+     * @param string $mode Mode with which to open the underlying filename/stream.
+     *
+     * @return StreamInterface
+     * @throws \RuntimeException If the file cannot be opened.
+     * @throws \InvalidArgumentException If the mode is invalid.
+     */
+    public function createStreamFromFile(string $filename, string $mode = 'r'): StreamInterface;
+
+    /**
+     * Create a new stream from an existing resource.
+     *
+     * The stream MUST be readable and may be writable.
+     *
+     * @param resource $resource PHP resource to use as basis of stream.
+     *
+     * @return StreamInterface
+     */
+    public function createStreamFromResource($resource): StreamInterface;
+}
diff --git a/vendor/psr/http-factory/src/UploadedFileFactoryInterface.php b/vendor/psr/http-factory/src/UploadedFileFactoryInterface.php
new file mode 100755
index 0000000..7db4e30
--- /dev/null
+++ b/vendor/psr/http-factory/src/UploadedFileFactoryInterface.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Psr\Http\Message;
+
+interface UploadedFileFactoryInterface
+{
+    /**
+     * Create a new uploaded file.
+     *
+     * If a size is not provided it will be determined by checking the size of
+     * the file.
+     *
+     * @see http://php.net/manual/features.file-upload.post-method.php
+     * @see http://php.net/manual/features.file-upload.errors.php
+     *
+     * @param StreamInterface $stream Underlying stream representing the
+     *     uploaded file content.
+     * @param int $size in bytes
+     * @param int $error PHP file upload error
+     * @param string $clientFilename Filename as provided by the client, if any.
+     * @param string $clientMediaType Media type as provided by the client, if any.
+     *
+     * @return UploadedFileInterface
+     *
+     * @throws \InvalidArgumentException If the file resource is not readable.
+     */
+    public function createUploadedFile(
+        StreamInterface $stream,
+        int $size = null,
+        int $error = \UPLOAD_ERR_OK,
+        string $clientFilename = null,
+        string $clientMediaType = null
+    ): UploadedFileInterface;
+}
diff --git a/vendor/psr/http-factory/src/UriFactoryInterface.php b/vendor/psr/http-factory/src/UriFactoryInterface.php
new file mode 100755
index 0000000..06df0b4
--- /dev/null
+++ b/vendor/psr/http-factory/src/UriFactoryInterface.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Psr\Http\Message;
+
+interface UriFactoryInterface
+{
+    /**
+     * Create a new URI.
+     *
+     * @param string $uri
+     *
+     * @return UriInterface
+     *
+     * @throws \InvalidArgumentException If the given URI cannot be parsed.
+     */
+    public function createUri(string $uri = ''): UriInterface;
+}
diff --git a/vendor/psr/http-message/CHANGELOG.md b/vendor/psr/http-message/CHANGELOG.md
new file mode 100755
index 0000000..74b1ef9
--- /dev/null
+++ b/vendor/psr/http-message/CHANGELOG.md
@@ -0,0 +1,36 @@
+# Changelog
+
+All notable changes to this project will be documented in this file, in reverse chronological order by release.
+
+## 1.0.1 - 2016-08-06
+
+### Added
+
+- Nothing.
+
+### Deprecated
+
+- Nothing.
+
+### Removed
+
+- Nothing.
+
+### Fixed
+
+- Updated all `@return self` annotation references in interfaces to use
+  `@return static`, which more closelly follows the semantics of the
+  specification.
+- Updated the `MessageInterface::getHeaders()` return annotation to use the
+  value `string[][]`, indicating the format is a nested array of strings.
+- Updated the `@link` annotation for `RequestInterface::withRequestTarget()`
+  to point to the correct section of RFC 7230.
+- Updated the `ServerRequestInterface::withUploadedFiles()` parameter annotation
+  to add the parameter name (`$uploadedFiles`).
+- Updated a `@throws` annotation for the `UploadedFileInterface::moveTo()`
+  method to correctly reference the method parameter (it was referencing an
+  incorrect parameter name previously).
+
+## 1.0.0 - 2016-05-18
+
+Initial stable release; reflects accepted PSR-7 specification.
diff --git a/vendor/psr/http-message/LICENSE b/vendor/psr/http-message/LICENSE
new file mode 100755
index 0000000..c2d8e45
--- /dev/null
+++ b/vendor/psr/http-message/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2014 PHP Framework Interoperability Group
+
+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.
diff --git a/vendor/psr/http-message/README.md b/vendor/psr/http-message/README.md
new file mode 100755
index 0000000..2818533
--- /dev/null
+++ b/vendor/psr/http-message/README.md
@@ -0,0 +1,13 @@
+PSR Http Message
+================
+
+This repository holds all interfaces/classes/traits related to
+[PSR-7](http://www.php-fig.org/psr/psr-7/).
+
+Note that this is not a HTTP message implementation of its own. It is merely an
+interface that describes a HTTP message. See the specification for more details.
+
+Usage
+-----
+
+We'll certainly need some stuff in here.
\ No newline at end of file
diff --git a/vendor/psr/http-message/composer.json b/vendor/psr/http-message/composer.json
new file mode 100755
index 0000000..b0d2937
--- /dev/null
+++ b/vendor/psr/http-message/composer.json
@@ -0,0 +1,26 @@
+{
+    "name": "psr/http-message",
+    "description": "Common interface for HTTP messages",
+    "keywords": ["psr", "psr-7", "http", "http-message", "request", "response"],
+    "homepage": "https://github.com/php-fig/http-message",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "PHP-FIG",
+            "homepage": "http://www.php-fig.org/"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "Psr\\Http\\Message\\": "src/"
+        }
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.0.x-dev"
+        }
+    }
+}
diff --git a/vendor/psr/http-message/src/MessageInterface.php b/vendor/psr/http-message/src/MessageInterface.php
new file mode 100755
index 0000000..dd46e5e
--- /dev/null
+++ b/vendor/psr/http-message/src/MessageInterface.php
@@ -0,0 +1,187 @@
+<?php
+
+namespace Psr\Http\Message;
+
+/**
+ * HTTP messages consist of requests from a client to a server and responses
+ * from a server to a client. This interface defines the methods common to
+ * each.
+ *
+ * Messages are considered immutable; all methods that might change state MUST
+ * be implemented such that they retain the internal state of the current
+ * message and return an instance that contains the changed state.
+ *
+ * @link http://www.ietf.org/rfc/rfc7230.txt
+ * @link http://www.ietf.org/rfc/rfc7231.txt
+ */
+interface MessageInterface
+{
+    /**
+     * Retrieves the HTTP protocol version as a string.
+     *
+     * The string MUST contain only the HTTP version number (e.g., "1.1", "1.0").
+     *
+     * @return string HTTP protocol version.
+     */
+    public function getProtocolVersion();
+
+    /**
+     * Return an instance with the specified HTTP protocol version.
+     *
+     * The version string MUST contain only the HTTP version number (e.g.,
+     * "1.1", "1.0").
+     *
+     * This method MUST be implemented in such a way as to retain the
+     * immutability of the message, and MUST return an instance that has the
+     * new protocol version.
+     *
+     * @param string $version HTTP protocol version
+     * @return static
+     */
+    public function withProtocolVersion($version);
+
+    /**
+     * Retrieves all message header values.
+     *
+     * The keys represent the header name as it will be sent over the wire, and
+     * each value is an array of strings associated with the header.
+     *
+     *     // Represent the headers as a string
+     *     foreach ($message->getHeaders() as $name => $values) {
+     *         echo $name . ": " . implode(", ", $values);
+     *     }
+     *
+     *     // Emit headers iteratively:
+     *     foreach ($message->getHeaders() as $name => $values) {
+     *         foreach ($values as $value) {
+     *             header(sprintf('%s: %s', $name, $value), false);
+     *         }
+     *     }
+     *
+     * While header names are not case-sensitive, getHeaders() will preserve the
+     * exact case in which headers were originally specified.
+     *
+     * @return string[][] Returns an associative array of the message's headers. Each
+     *     key MUST be a header name, and each value MUST be an array of strings
+     *     for that header.
+     */
+    public function getHeaders();
+
+    /**
+     * Checks if a header exists by the given case-insensitive name.
+     *
+     * @param string $name Case-insensitive header field name.
+     * @return bool Returns true if any header names match the given header
+     *     name using a case-insensitive string comparison. Returns false if
+     *     no matching header name is found in the message.
+     */
+    public function hasHeader($name);
+
+    /**
+     * Retrieves a message header value by the given case-insensitive name.
+     *
+     * This method returns an array of all the header values of the given
+     * case-insensitive header name.
+     *
+     * If the header does not appear in the message, this method MUST return an
+     * empty array.
+     *
+     * @param string $name Case-insensitive header field name.
+     * @return string[] An array of string values as provided for the given
+     *    header. If the header does not appear in the message, this method MUST
+     *    return an empty array.
+     */
+    public function getHeader($name);
+
+    /**
+     * Retrieves a comma-separated string of the values for a single header.
+     *
+     * This method returns all of the header values of the given
+     * case-insensitive header name as a string concatenated together using
+     * a comma.
+     *
+     * NOTE: Not all header values may be appropriately represented using
+     * comma concatenation. For such headers, use getHeader() instead
+     * and supply your own delimiter when concatenating.
+     *
+     * If the header does not appear in the message, this method MUST return
+     * an empty string.
+     *
+     * @param string $name Case-insensitive header field name.
+     * @return string A string of values as provided for the given header
+     *    concatenated together using a comma. If the header does not appear in
+     *    the message, this method MUST return an empty string.
+     */
+    public function getHeaderLine($name);
+
+    /**
+     * Return an instance with the provided value replacing the specified header.
+     *
+     * While header names are case-insensitive, the casing of the header will
+     * be preserved by this function, and returned from getHeaders().
+     *
+     * This method MUST be implemented in such a way as to retain the
+     * immutability of the message, and MUST return an instance that has the
+     * new and/or updated header and value.
+     *
+     * @param string $name Case-insensitive header field name.
+     * @param string|string[] $value Header value(s).
+     * @return static
+     * @throws \InvalidArgumentException for invalid header names or values.
+     */
+    public function withHeader($name, $value);
+
+    /**
+     * Return an instance with the specified header appended with the given value.
+     *
+     * Existing values for the specified header will be maintained. The new
+     * value(s) will be appended to the existing list. If the header did not
+     * exist previously, it will be added.
+     *
+     * This method MUST be implemented in such a way as to retain the
+     * immutability of the message, and MUST return an instance that has the
+     * new header and/or value.
+     *
+     * @param string $name Case-insensitive header field name to add.
+     * @param string|string[] $value Header value(s).
+     * @return static
+     * @throws \InvalidArgumentException for invalid header names or values.
+     */
+    public function withAddedHeader($name, $value);
+
+    /**
+     * Return an instance without the specified header.
+     *
+     * Header resolution MUST be done without case-sensitivity.
+     *
+     * This method MUST be implemented in such a way as to retain the
+     * immutability of the message, and MUST return an instance that removes
+     * the named header.
+     *
+     * @param string $name Case-insensitive header field name to remove.
+     * @return static
+     */
+    public function withoutHeader($name);
+
+    /**
+     * Gets the body of the message.
+     *
+     * @return StreamInterface Returns the body as a stream.
+     */
+    public function getBody();
+
+    /**
+     * Return an instance with the specified message body.
+     *
+     * The body MUST be a StreamInterface object.
+     *
+     * This method MUST be implemented in such a way as to retain the
+     * immutability of the message, and MUST return a new instance that has the
+     * new body stream.
+     *
+     * @param StreamInterface $body Body.
+     * @return static
+     * @throws \InvalidArgumentException When the body is not valid.
+     */
+    public function withBody(StreamInterface $body);
+}
diff --git a/vendor/psr/http-message/src/RequestInterface.php b/vendor/psr/http-message/src/RequestInterface.php
new file mode 100755
index 0000000..a96d4fd
--- /dev/null
+++ b/vendor/psr/http-message/src/RequestInterface.php
@@ -0,0 +1,129 @@
+<?php
+
+namespace Psr\Http\Message;
+
+/**
+ * Representation of an outgoing, client-side request.
+ *
+ * Per the HTTP specification, this interface includes properties for
+ * each of the following:
+ *
+ * - Protocol version
+ * - HTTP method
+ * - URI
+ * - Headers
+ * - Message body
+ *
+ * During construction, implementations MUST attempt to set the Host header from
+ * a provided URI if no Host header is provided.
+ *
+ * Requests are considered immutable; all methods that might change state MUST
+ * be implemented such that they retain the internal state of the current
+ * message and return an instance that contains the changed state.
+ */
+interface RequestInterface extends MessageInterface
+{
+    /**
+     * Retrieves the message's request target.
+     *
+     * Retrieves the message's request-target either as it will appear (for
+     * clients), as it appeared at request (for servers), or as it was
+     * specified for the instance (see withRequestTarget()).
+     *
+     * In most cases, this will be the origin-form of the composed URI,
+     * unless a value was provided to the concrete implementation (see
+     * withRequestTarget() below).
+     *
+     * If no URI is available, and no request-target has been specifically
+     * provided, this method MUST return the string "/".
+     *
+     * @return string
+     */
+    public function getRequestTarget();
+
+    /**
+     * Return an instance with the specific request-target.
+     *
+     * If the request needs a non-origin-form request-target — e.g., for
+     * specifying an absolute-form, authority-form, or asterisk-form —
+     * this method may be used to create an instance with the specified
+     * request-target, verbatim.
+     *
+     * This method MUST be implemented in such a way as to retain the
+     * immutability of the message, and MUST return an instance that has the
+     * changed request target.
+     *
+     * @link http://tools.ietf.org/html/rfc7230#section-5.3 (for the various
+     *     request-target forms allowed in request messages)
+     * @param mixed $requestTarget
+     * @return static
+     */
+    public function withRequestTarget($requestTarget);
+
+    /**
+     * Retrieves the HTTP method of the request.
+     *
+     * @return string Returns the request method.
+     */
+    public function getMethod();
+
+    /**
+     * Return an instance with the provided HTTP method.
+     *
+     * While HTTP method names are typically all uppercase characters, HTTP
+     * method names are case-sensitive and thus implementations SHOULD NOT
+     * modify the given string.
+     *
+     * This method MUST be implemented in such a way as to retain the
+     * immutability of the message, and MUST return an instance that has the
+     * changed request method.
+     *
+     * @param string $method Case-sensitive method.
+     * @return static
+     * @throws \InvalidArgumentException for invalid HTTP methods.
+     */
+    public function withMethod($method);
+
+    /**
+     * Retrieves the URI instance.
+     *
+     * This method MUST return a UriInterface instance.
+     *
+     * @link http://tools.ietf.org/html/rfc3986#section-4.3
+     * @return UriInterface Returns a UriInterface instance
+     *     representing the URI of the request.
+     */
+    public function getUri();
+
+    /**
+     * Returns an instance with the provided URI.
+     *
+     * This method MUST update the Host header of the returned request by
+     * default if the URI contains a host component. If the URI does not
+     * contain a host component, any pre-existing Host header MUST be carried
+     * over to the returned request.
+     *
+     * You can opt-in to preserving the original state of the Host header by
+     * setting `$preserveHost` to `true`. When `$preserveHost` is set to
+     * `true`, this method interacts with the Host header in the following ways:
+     *
+     * - If the Host header is missing or empty, and the new URI contains
+     *   a host component, this method MUST update the Host header in the returned
+     *   request.
+     * - If the Host header is missing or empty, and the new URI does not contain a
+     *   host component, this method MUST NOT update the Host header in the returned
+     *   request.
+     * - If a Host header is present and non-empty, this method MUST NOT update
+     *   the Host header in the returned request.
+     *
+     * This method MUST be implemented in such a way as to retain the
+     * immutability of the message, and MUST return an instance that has the
+     * new UriInterface instance.
+     *
+     * @link http://tools.ietf.org/html/rfc3986#section-4.3
+     * @param UriInterface $uri New request URI to use.
+     * @param bool $preserveHost Preserve the original state of the Host header.
+     * @return static
+     */
+    public function withUri(UriInterface $uri, $preserveHost = false);
+}
diff --git a/vendor/psr/http-message/src/ResponseInterface.php b/vendor/psr/http-message/src/ResponseInterface.php
new file mode 100755
index 0000000..c306514
--- /dev/null
+++ b/vendor/psr/http-message/src/ResponseInterface.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace Psr\Http\Message;
+
+/**
+ * Representation of an outgoing, server-side response.
+ *
+ * Per the HTTP specification, this interface includes properties for
+ * each of the following:
+ *
+ * - Protocol version
+ * - Status code and reason phrase
+ * - Headers
+ * - Message body
+ *
+ * Responses are considered immutable; all methods that might change state MUST
+ * be implemented such that they retain the internal state of the current
+ * message and return an instance that contains the changed state.
+ */
+interface ResponseInterface extends MessageInterface
+{
+    /**
+     * Gets the response status code.
+     *
+     * The status code is a 3-digit integer result code of the server's attempt
+     * to understand and satisfy the request.
+     *
+     * @return int Status code.
+     */
+    public function getStatusCode();
+
+    /**
+     * Return an instance with the specified status code and, optionally, reason phrase.
+     *
+     * If no reason phrase is specified, implementations MAY choose to default
+     * to the RFC 7231 or IANA recommended reason phrase for the response's
+     * status code.
+     *
+     * This method MUST be implemented in such a way as to retain the
+     * immutability of the message, and MUST return an instance that has the
+     * updated status and reason phrase.
+     *
+     * @link http://tools.ietf.org/html/rfc7231#section-6
+     * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
+     * @param int $code The 3-digit integer result code to set.
+     * @param string $reasonPhrase The reason phrase to use with the
+     *     provided status code; if none is provided, implementations MAY
+     *     use the defaults as suggested in the HTTP specification.
+     * @return static
+     * @throws \InvalidArgumentException For invalid status code arguments.
+     */
+    public function withStatus($code, $reasonPhrase = '');
+
+    /**
+     * Gets the response reason phrase associated with the status code.
+     *
+     * Because a reason phrase is not a required element in a response
+     * status line, the reason phrase value MAY be null. Implementations MAY
+     * choose to return the default RFC 7231 recommended reason phrase (or those
+     * listed in the IANA HTTP Status Code Registry) for the response's
+     * status code.
+     *
+     * @link http://tools.ietf.org/html/rfc7231#section-6
+     * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
+     * @return string Reason phrase; must return an empty string if none present.
+     */
+    public function getReasonPhrase();
+}
diff --git a/vendor/psr/http-message/src/ServerRequestInterface.php b/vendor/psr/http-message/src/ServerRequestInterface.php
new file mode 100755
index 0000000..0251234
--- /dev/null
+++ b/vendor/psr/http-message/src/ServerRequestInterface.php
@@ -0,0 +1,261 @@
+<?php
+
+namespace Psr\Http\Message;
+
+/**
+ * Representation of an incoming, server-side HTTP request.
+ *
+ * Per the HTTP specification, this interface includes properties for
+ * each of the following:
+ *
+ * - Protocol version
+ * - HTTP method
+ * - URI
+ * - Headers
+ * - Message body
+ *
+ * Additionally, it encapsulates all data as it has arrived to the
+ * application from the CGI and/or PHP environment, including:
+ *
+ * - The values represented in $_SERVER.
+ * - Any cookies provided (generally via $_COOKIE)
+ * - Query string arguments (generally via $_GET, or as parsed via parse_str())
+ * - Upload files, if any (as represented by $_FILES)
+ * - Deserialized body parameters (generally from $_POST)
+ *
+ * $_SERVER values MUST be treated as immutable, as they represent application
+ * state at the time of request; as such, no methods are provided to allow
+ * modification of those values. The other values provide such methods, as they
+ * can be restored from $_SERVER or the request body, and may need treatment
+ * during the application (e.g., body parameters may be deserialized based on
+ * content type).
+ *
+ * Additionally, this interface recognizes the utility of introspecting a
+ * request to derive and match additional parameters (e.g., via URI path
+ * matching, decrypting cookie values, deserializing non-form-encoded body
+ * content, matching authorization headers to users, etc). These parameters
+ * are stored in an "attributes" property.
+ *
+ * Requests are considered immutable; all methods that might change state MUST
+ * be implemented such that they retain the internal state of the current
+ * message and return an instance that contains the changed state.
+ */
+interface ServerRequestInterface extends RequestInterface
+{
+    /**
+     * Retrieve server parameters.
+     *
+     * Retrieves data related to the incoming request environment,
+     * typically derived from PHP's $_SERVER superglobal. The data IS NOT
+     * REQUIRED to originate from $_SERVER.
+     *
+     * @return array
+     */
+    public function getServerParams();
+
+    /**
+     * Retrieve cookies.
+     *
+     * Retrieves cookies sent by the client to the server.
+     *
+     * The data MUST be compatible with the structure of the $_COOKIE
+     * superglobal.
+     *
+     * @return array
+     */
+    public function getCookieParams();
+
+    /**
+     * Return an instance with the specified cookies.
+     *
+     * The data IS NOT REQUIRED to come from the $_COOKIE superglobal, but MUST
+     * be compatible with the structure of $_COOKIE. Typically, this data will
+     * be injected at instantiation.
+     *
+     * This method MUST NOT update the related Cookie header of the request
+     * instance, nor related values in the server params.
+     *
+     * This method MUST be implemented in such a way as to retain the
+     * immutability of the message, and MUST return an instance that has the
+     * updated cookie values.
+     *
+     * @param array $cookies Array of key/value pairs representing cookies.
+     * @return static
+     */
+    public function withCookieParams(array $cookies);
+
+    /**
+     * Retrieve query string arguments.
+     *
+     * Retrieves the deserialized query string arguments, if any.
+     *
+     * Note: the query params might not be in sync with the URI or server
+     * params. If you need to ensure you are only getting the original
+     * values, you may need to parse the query string from `getUri()->getQuery()`
+     * or from the `QUERY_STRING` server param.
+     *
+     * @return array
+     */
+    public function getQueryParams();
+
+    /**
+     * Return an instance with the specified query string arguments.
+     *
+     * These values SHOULD remain immutable over the course of the incoming
+     * request. They MAY be injected during instantiation, such as from PHP's
+     * $_GET superglobal, or MAY be derived from some other value such as the
+     * URI. In cases where the arguments are parsed from the URI, the data
+     * MUST be compatible with what PHP's parse_str() would return for
+     * purposes of how duplicate query parameters are handled, and how nested
+     * sets are handled.
+     *
+     * Setting query string arguments MUST NOT change the URI stored by the
+     * request, nor the values in the server params.
+     *
+     * This method MUST be implemented in such a way as to retain the
+     * immutability of the message, and MUST return an instance that has the
+     * updated query string arguments.
+     *
+     * @param array $query Array of query string arguments, typically from
+     *     $_GET.
+     * @return static
+     */
+    public function withQueryParams(array $query);
+
+    /**
+     * Retrieve normalized file upload data.
+     *
+     * This method returns upload metadata in a normalized tree, with each leaf
+     * an instance of Psr\Http\Message\UploadedFileInterface.
+     *
+     * These values MAY be prepared from $_FILES or the message body during
+     * instantiation, or MAY be injected via withUploadedFiles().
+     *
+     * @return array An array tree of UploadedFileInterface instances; an empty
+     *     array MUST be returned if no data is present.
+     */
+    public function getUploadedFiles();
+
+    /**
+     * Create a new instance with the specified uploaded files.
+     *
+     * This method MUST be implemented in such a way as to retain the
+     * immutability of the message, and MUST return an instance that has the
+     * updated body parameters.
+     *
+     * @param array $uploadedFiles An array tree of UploadedFileInterface instances.
+     * @return static
+     * @throws \InvalidArgumentException if an invalid structure is provided.
+     */
+    public function withUploadedFiles(array $uploadedFiles);
+
+    /**
+     * Retrieve any parameters provided in the request body.
+     *
+     * If the request Content-Type is either application/x-www-form-urlencoded
+     * or multipart/form-data, and the request method is POST, this method MUST
+     * return the contents of $_POST.
+     *
+     * Otherwise, this method may return any results of deserializing
+     * the request body content; as parsing returns structured content, the
+     * potential types MUST be arrays or objects only. A null value indicates
+     * the absence of body content.
+     *
+     * @return null|array|object The deserialized body parameters, if any.
+     *     These will typically be an array or object.
+     */
+    public function getParsedBody();
+
+    /**
+     * Return an instance with the specified body parameters.
+     *
+     * These MAY be injected during instantiation.
+     *
+     * If the request Content-Type is either application/x-www-form-urlencoded
+     * or multipart/form-data, and the request method is POST, use this method
+     * ONLY to inject the contents of $_POST.
+     *
+     * The data IS NOT REQUIRED to come from $_POST, but MUST be the results of
+     * deserializing the request body content. Deserialization/parsing returns
+     * structured data, and, as such, this method ONLY accepts arrays or objects,
+     * or a null value if nothing was available to parse.
+     *
+     * As an example, if content negotiation determines that the request data
+     * is a JSON payload, this method could be used to create a request
+     * instance with the deserialized parameters.
+     *
+     * This method MUST be implemented in such a way as to retain the
+     * immutability of the message, and MUST return an instance that has the
+     * updated body parameters.
+     *
+     * @param null|array|object $data The deserialized body data. This will
+     *     typically be in an array or object.
+     * @return static
+     * @throws \InvalidArgumentException if an unsupported argument type is
+     *     provided.
+     */
+    public function withParsedBody($data);
+
+    /**
+     * Retrieve attributes derived from the request.
+     *
+     * The request "attributes" may be used to allow injection of any
+     * parameters derived from the request: e.g., the results of path
+     * match operations; the results of decrypting cookies; the results of
+     * deserializing non-form-encoded message bodies; etc. Attributes
+     * will be application and request specific, and CAN be mutable.
+     *
+     * @return array Attributes derived from the request.
+     */
+    public function getAttributes();
+
+    /**
+     * Retrieve a single derived request attribute.
+     *
+     * Retrieves a single derived request attribute as described in
+     * getAttributes(). If the attribute has not been previously set, returns
+     * the default value as provided.
+     *
+     * This method obviates the need for a hasAttribute() method, as it allows
+     * specifying a default value to return if the attribute is not found.
+     *
+     * @see getAttributes()
+     * @param string $name The attribute name.
+     * @param mixed $default Default value to return if the attribute does not exist.
+     * @return mixed
+     */
+    public function getAttribute($name, $default = null);
+
+    /**
+     * Return an instance with the specified derived request attribute.
+     *
+     * This method allows setting a single derived request attribute as
+     * described in getAttributes().
+     *
+     * This method MUST be implemented in such a way as to retain the
+     * immutability of the message, and MUST return an instance that has the
+     * updated attribute.
+     *
+     * @see getAttributes()
+     * @param string $name The attribute name.
+     * @param mixed $value The value of the attribute.
+     * @return static
+     */
+    public function withAttribute($name, $value);
+
+    /**
+     * Return an instance that removes the specified derived request attribute.
+     *
+     * This method allows removing a single derived request attribute as
+     * described in getAttributes().
+     *
+     * This method MUST be implemented in such a way as to retain the
+     * immutability of the message, and MUST return an instance that removes
+     * the attribute.
+     *
+     * @see getAttributes()
+     * @param string $name The attribute name.
+     * @return static
+     */
+    public function withoutAttribute($name);
+}
diff --git a/vendor/psr/http-message/src/StreamInterface.php b/vendor/psr/http-message/src/StreamInterface.php
new file mode 100755
index 0000000..f68f391
--- /dev/null
+++ b/vendor/psr/http-message/src/StreamInterface.php
@@ -0,0 +1,158 @@
+<?php
+
+namespace Psr\Http\Message;
+
+/**
+ * Describes a data stream.
+ *
+ * Typically, an instance will wrap a PHP stream; this interface provides
+ * a wrapper around the most common operations, including serialization of
+ * the entire stream to a string.
+ */
+interface StreamInterface
+{
+    /**
+     * Reads all data from the stream into a string, from the beginning to end.
+     *
+     * This method MUST attempt to seek to the beginning of the stream before
+     * reading data and read the stream until the end is reached.
+     *
+     * Warning: This could attempt to load a large amount of data into memory.
+     *
+     * This method MUST NOT raise an exception in order to conform with PHP's
+     * string casting operations.
+     *
+     * @see http://php.net/manual/en/language.oop5.magic.php#object.tostring
+     * @return string
+     */
+    public function __toString();
+
+    /**
+     * Closes the stream and any underlying resources.
+     *
+     * @return void
+     */
+    public function close();
+
+    /**
+     * Separates any underlying resources from the stream.
+     *
+     * After the stream has been detached, the stream is in an unusable state.
+     *
+     * @return resource|null Underlying PHP stream, if any
+     */
+    public function detach();
+
+    /**
+     * Get the size of the stream if known.
+     *
+     * @return int|null Returns the size in bytes if known, or null if unknown.
+     */
+    public function getSize();
+
+    /**
+     * Returns the current position of the file read/write pointer
+     *
+     * @return int Position of the file pointer
+     * @throws \RuntimeException on error.
+     */
+    public function tell();
+
+    /**
+     * Returns true if the stream is at the end of the stream.
+     *
+     * @return bool
+     */
+    public function eof();
+
+    /**
+     * Returns whether or not the stream is seekable.
+     *
+     * @return bool
+     */
+    public function isSeekable();
+
+    /**
+     * Seek to a position in the stream.
+     *
+     * @link http://www.php.net/manual/en/function.fseek.php
+     * @param int $offset Stream offset
+     * @param int $whence Specifies how the cursor position will be calculated
+     *     based on the seek offset. Valid values are identical to the built-in
+     *     PHP $whence values for `fseek()`.  SEEK_SET: Set position equal to
+     *     offset bytes SEEK_CUR: Set position to current location plus offset
+     *     SEEK_END: Set position to end-of-stream plus offset.
+     * @throws \RuntimeException on failure.
+     */
+    public function seek($offset, $whence = SEEK_SET);
+
+    /**
+     * Seek to the beginning of the stream.
+     *
+     * If the stream is not seekable, this method will raise an exception;
+     * otherwise, it will perform a seek(0).
+     *
+     * @see seek()
+     * @link http://www.php.net/manual/en/function.fseek.php
+     * @throws \RuntimeException on failure.
+     */
+    public function rewind();
+
+    /**
+     * Returns whether or not the stream is writable.
+     *
+     * @return bool
+     */
+    public function isWritable();
+
+    /**
+     * Write data to the stream.
+     *
+     * @param string $string The string that is to be written.
+     * @return int Returns the number of bytes written to the stream.
+     * @throws \RuntimeException on failure.
+     */
+    public function write($string);
+
+    /**
+     * Returns whether or not the stream is readable.
+     *
+     * @return bool
+     */
+    public function isReadable();
+
+    /**
+     * Read data from the stream.
+     *
+     * @param int $length Read up to $length bytes from the object and return
+     *     them. Fewer than $length bytes may be returned if underlying stream
+     *     call returns fewer bytes.
+     * @return string Returns the data read from the stream, or an empty string
+     *     if no bytes are available.
+     * @throws \RuntimeException if an error occurs.
+     */
+    public function read($length);
+
+    /**
+     * Returns the remaining contents in a string
+     *
+     * @return string
+     * @throws \RuntimeException if unable to read or an error occurs while
+     *     reading.
+     */
+    public function getContents();
+
+    /**
+     * Get stream metadata as an associative array or retrieve a specific key.
+     *
+     * The keys returned are identical to the keys returned from PHP's
+     * stream_get_meta_data() function.
+     *
+     * @link http://php.net/manual/en/function.stream-get-meta-data.php
+     * @param string $key Specific metadata to retrieve.
+     * @return array|mixed|null Returns an associative array if no key is
+     *     provided. Returns a specific key value if a key is provided and the
+     *     value is found, or null if the key is not found.
+     */
+    public function getMetadata($key = null);
+}
diff --git a/vendor/psr/http-message/src/UploadedFileInterface.php b/vendor/psr/http-message/src/UploadedFileInterface.php
new file mode 100755
index 0000000..f8a6901
--- /dev/null
+++ b/vendor/psr/http-message/src/UploadedFileInterface.php
@@ -0,0 +1,123 @@
+<?php
+
+namespace Psr\Http\Message;
+
+/**
+ * Value object representing a file uploaded through an HTTP request.
+ *
+ * Instances of this interface are considered immutable; all methods that
+ * might change state MUST be implemented such that they retain the internal
+ * state of the current instance and return an instance that contains the
+ * changed state.
+ */
+interface UploadedFileInterface
+{
+    /**
+     * Retrieve a stream representing the uploaded file.
+     *
+     * This method MUST return a StreamInterface instance, representing the
+     * uploaded file. The purpose of this method is to allow utilizing native PHP
+     * stream functionality to manipulate the file upload, such as
+     * stream_copy_to_stream() (though the result will need to be decorated in a
+     * native PHP stream wrapper to work with such functions).
+     *
+     * If the moveTo() method has been called previously, this method MUST raise
+     * an exception.
+     *
+     * @return StreamInterface Stream representation of the uploaded file.
+     * @throws \RuntimeException in cases when no stream is available or can be
+     *     created.
+     */
+    public function getStream();
+
+    /**
+     * Move the uploaded file to a new location.
+     *
+     * Use this method as an alternative to move_uploaded_file(). This method is
+     * guaranteed to work in both SAPI and non-SAPI environments.
+     * Implementations must determine which environment they are in, and use the
+     * appropriate method (move_uploaded_file(), rename(), or a stream
+     * operation) to perform the operation.
+     *
+     * $targetPath may be an absolute path, or a relative path. If it is a
+     * relative path, resolution should be the same as used by PHP's rename()
+     * function.
+     *
+     * The original file or stream MUST be removed on completion.
+     *
+     * If this method is called more than once, any subsequent calls MUST raise
+     * an exception.
+     *
+     * When used in an SAPI environment where $_FILES is populated, when writing
+     * files via moveTo(), is_uploaded_file() and move_uploaded_file() SHOULD be
+     * used to ensure permissions and upload status are verified correctly.
+     *
+     * If you wish to move to a stream, use getStream(), as SAPI operations
+     * cannot guarantee writing to stream destinations.
+     *
+     * @see http://php.net/is_uploaded_file
+     * @see http://php.net/move_uploaded_file
+     * @param string $targetPath Path to which to move the uploaded file.
+     * @throws \InvalidArgumentException if the $targetPath specified is invalid.
+     * @throws \RuntimeException on any error during the move operation, or on
+     *     the second or subsequent call to the method.
+     */
+    public function moveTo($targetPath);
+    
+    /**
+     * Retrieve the file size.
+     *
+     * Implementations SHOULD return the value stored in the "size" key of
+     * the file in the $_FILES array if available, as PHP calculates this based
+     * on the actual size transmitted.
+     *
+     * @return int|null The file size in bytes or null if unknown.
+     */
+    public function getSize();
+    
+    /**
+     * Retrieve the error associated with the uploaded file.
+     *
+     * The return value MUST be one of PHP's UPLOAD_ERR_XXX constants.
+     *
+     * If the file was uploaded successfully, this method MUST return
+     * UPLOAD_ERR_OK.
+     *
+     * Implementations SHOULD return the value stored in the "error" key of
+     * the file in the $_FILES array.
+     *
+     * @see http://php.net/manual/en/features.file-upload.errors.php
+     * @return int One of PHP's UPLOAD_ERR_XXX constants.
+     */
+    public function getError();
+    
+    /**
+     * Retrieve the filename sent by the client.
+     *
+     * Do not trust the value returned by this method. A client could send
+     * a malicious filename with the intention to corrupt or hack your
+     * application.
+     *
+     * Implementations SHOULD return the value stored in the "name" key of
+     * the file in the $_FILES array.
+     *
+     * @return string|null The filename sent by the client or null if none
+     *     was provided.
+     */
+    public function getClientFilename();
+    
+    /**
+     * Retrieve the media type sent by the client.
+     *
+     * Do not trust the value returned by this method. A client could send
+     * a malicious media type with the intention to corrupt or hack your
+     * application.
+     *
+     * Implementations SHOULD return the value stored in the "type" key of
+     * the file in the $_FILES array.
+     *
+     * @return string|null The media type sent by the client or null if none
+     *     was provided.
+     */
+    public function getClientMediaType();
+}
diff --git a/vendor/psr/http-message/src/UriInterface.php b/vendor/psr/http-message/src/UriInterface.php
new file mode 100755
index 0000000..9d7ab9e
--- /dev/null
+++ b/vendor/psr/http-message/src/UriInterface.php
@@ -0,0 +1,323 @@
+<?php
+namespace Psr\Http\Message;
+
+/**
+ * Value object representing a URI.
+ *
+ * This interface is meant to represent URIs according to RFC 3986 and to
+ * provide methods for most common operations. Additional functionality for
+ * working with URIs can be provided on top of the interface or externally.
+ * Its primary use is for HTTP requests, but may also be used in other
+ * contexts.
+ *
+ * Instances of this interface are considered immutable; all methods that
+ * might change state MUST be implemented such that they retain the internal
+ * state of the current instance and return an instance that contains the
+ * changed state.
+ *
+ * Typically the Host header will be also be present in the request message.
+ * For server-side requests, the scheme will typically be discoverable in the
+ * server parameters.
+ *
+ * @link http://tools.ietf.org/html/rfc3986 (the URI specification)
+ */
+interface UriInterface
+{
+    /**
+     * Retrieve the scheme component of the URI.
+     *
+     * If no scheme is present, this method MUST return an empty string.
+     *
+     * The value returned MUST be normalized to lowercase, per RFC 3986
+     * Section 3.1.
+     *
+     * The trailing ":" character is not part of the scheme and MUST NOT be
+     * added.
+     *
+     * @see https://tools.ietf.org/html/rfc3986#section-3.1
+     * @return string The URI scheme.
+     */
+    public function getScheme();
+
+    /**
+     * Retrieve the authority component of the URI.
+     *
+     * If no authority information is present, this method MUST return an empty
+     * string.
+     *
+     * The authority syntax of the URI is:
+     *
+     * <pre>
+     * [user-info@]host[:port]
+     * </pre>
+     *
+     * If the port component is not set or is the standard port for the current
+     * scheme, it SHOULD NOT be included.
+     *
+     * @see https://tools.ietf.org/html/rfc3986#section-3.2
+     * @return string The URI authority, in "[user-info@]host[:port]" format.
+     */
+    public function getAuthority();
+
+    /**
+     * Retrieve the user information component of the URI.
+     *
+     * If no user information is present, this method MUST return an empty
+     * string.
+     *
+     * If a user is present in the URI, this will return that value;
+     * additionally, if the password is also present, it will be appended to the
+     * user value, with a colon (":") separating the values.
+     *
+     * The trailing "@" character is not part of the user information and MUST
+     * NOT be added.
+     *
+     * @return string The URI user information, in "username[:password]" format.
+     */
+    public function getUserInfo();
+
+    /**
+     * Retrieve the host component of the URI.
+     *
+     * If no host is present, this method MUST return an empty string.
+     *
+     * The value returned MUST be normalized to lowercase, per RFC 3986
+     * Section 3.2.2.
+     *
+     * @see http://tools.ietf.org/html/rfc3986#section-3.2.2
+     * @return string The URI host.
+     */
+    public function getHost();
+
+    /**
+     * Retrieve the port component of the URI.
+     *
+     * If a port is present, and it is non-standard for the current scheme,
+     * this method MUST return it as an integer. If the port is the standard port
+     * used with the current scheme, this method SHOULD return null.
+     *
+     * If no port is present, and no scheme is present, this method MUST return
+     * a null value.
+     *
+     * If no port is present, but a scheme is present, this method MAY return
+     * the standard port for that scheme, but SHOULD return null.
+     *
+     * @return null|int The URI port.
+     */
+    public function getPort();
+
+    /**
+     * Retrieve the path component of the URI.
+     *
+     * The path can either be empty or absolute (starting with a slash) or
+     * rootless (not starting with a slash). Implementations MUST support all
+     * three syntaxes.
+     *
+     * Normally, the empty path "" and absolute path "/" are considered equal as
+     * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically
+     * do this normalization because in contexts with a trimmed base path, e.g.
+     * the front controller, this difference becomes significant. It's the task
+     * of the user to handle both "" and "/".
+     *
+     * The value returned MUST be percent-encoded, but MUST NOT double-encode
+     * any characters. To determine what characters to encode, please refer to
+     * RFC 3986, Sections 2 and 3.3.
+     *
+     * As an example, if the value should include a slash ("/") not intended as
+     * delimiter between path segments, that value MUST be passed in encoded
+     * form (e.g., "%2F") to the instance.
+     *
+     * @see https://tools.ietf.org/html/rfc3986#section-2
+     * @see https://tools.ietf.org/html/rfc3986#section-3.3
+     * @return string The URI path.
+     */
+    public function getPath();
+
+    /**
+     * Retrieve the query string of the URI.
+     *
+     * If no query string is present, this method MUST return an empty string.
+     *
+     * The leading "?" character is not part of the query and MUST NOT be
+     * added.
+     *
+     * The value returned MUST be percent-encoded, but MUST NOT double-encode
+     * any characters. To determine what characters to encode, please refer to
+     * RFC 3986, Sections 2 and 3.4.
+     *
+     * As an example, if a value in a key/value pair of the query string should
+     * include an ampersand ("&") not intended as a delimiter between values,
+     * that value MUST be passed in encoded form (e.g., "%26") to the instance.
+     *
+     * @see https://tools.ietf.org/html/rfc3986#section-2
+     * @see https://tools.ietf.org/html/rfc3986#section-3.4
+     * @return string The URI query string.
+     */
+    public function getQuery();
+
+    /**
+     * Retrieve the fragment component of the URI.
+     *
+     * If no fragment is present, this method MUST return an empty string.
+     *
+     * The leading "#" character is not part of the fragment and MUST NOT be
+     * added.
+     *
+     * The value returned MUST be percent-encoded, but MUST NOT double-encode
+     * any characters. To determine what characters to encode, please refer to
+     * RFC 3986, Sections 2 and 3.5.
+     *
+     * @see https://tools.ietf.org/html/rfc3986#section-2
+     * @see https://tools.ietf.org/html/rfc3986#section-3.5
+     * @return string The URI fragment.
+     */
+    public function getFragment();
+
+    /**
+     * Return an instance with the specified scheme.
+     *
+     * This method MUST retain the state of the current instance, and return
+     * an instance that contains the specified scheme.
+     *
+     * Implementations MUST support the schemes "http" and "https" case
+     * insensitively, and MAY accommodate other schemes if required.
+     *
+     * An empty scheme is equivalent to removing the scheme.
+     *
+     * @param string $scheme The scheme to use with the new instance.
+     * @return static A new instance with the specified scheme.
+     * @throws \InvalidArgumentException for invalid or unsupported schemes.
+     */
+    public function withScheme($scheme);
+
+    /**
+     * Return an instance with the specified user information.
+     *
+     * This method MUST retain the state of the current instance, and return
+     * an instance that contains the specified user information.
+     *
+     * Password is optional, but the user information MUST include the
+     * user; an empty string for the user is equivalent to removing user
+     * information.
+     *
+     * @param string $user The user name to use for authority.
+     * @param null|string $password The password associated with $user.
+     * @return static A new instance with the specified user information.
+     */
+    public function withUserInfo($user, $password = null);
+
+    /**
+     * Return an instance with the specified host.
+     *
+     * This method MUST retain the state of the current instance, and return
+     * an instance that contains the specified host.
+     *
+     * An empty host value is equivalent to removing the host.
+     *
+     * @param string $host The hostname to use with the new instance.
+     * @return static A new instance with the specified host.
+     * @throws \InvalidArgumentException for invalid hostnames.
+     */
+    public function withHost($host);
+
+    /**
+     * Return an instance with the specified port.
+     *
+     * This method MUST retain the state of the current instance, and return
+     * an instance that contains the specified port.
+     *
+     * Implementations MUST raise an exception for ports outside the
+     * established TCP and UDP port ranges.
+     *
+     * A null value provided for the port is equivalent to removing the port
+     * information.
+     *
+     * @param null|int $port The port to use with the new instance; a null value
+     *     removes the port information.
+     * @return static A new instance with the specified port.
+     * @throws \InvalidArgumentException for invalid ports.
+     */
+    public function withPort($port);
+
+    /**
+     * Return an instance with the specified path.
+     *
+     * This method MUST retain the state of the current instance, and return
+     * an instance that contains the specified path.
+     *
+     * The path can either be empty or absolute (starting with a slash) or
+     * rootless (not starting with a slash). Implementations MUST support all
+     * three syntaxes.
+     *
+     * If the path is intended to be domain-relative rather than path relative then
+     * it must begin with a slash ("/"). Paths not starting with a slash ("/")
+     * are assumed to be relative to some base path known to the application or
+     * consumer.
+     *
+     * Users can provide both encoded and decoded path characters.
+     * Implementations ensure the correct encoding as outlined in getPath().
+     *
+     * @param string $path The path to use with the new instance.
+     * @return static A new instance with the specified path.
+     * @throws \InvalidArgumentException for invalid paths.
+     */
+    public function withPath($path);
+
+    /**
+     * Return an instance with the specified query string.
+     *
+     * This method MUST retain the state of the current instance, and return
+     * an instance that contains the specified query string.
+     *
+     * Users can provide both encoded and decoded query characters.
+     * Implementations ensure the correct encoding as outlined in getQuery().
+     *
+     * An empty query string value is equivalent to removing the query string.
+     *
+     * @param string $query The query string to use with the new instance.
+     * @return static A new instance with the specified query string.
+     * @throws \InvalidArgumentException for invalid query strings.
+     */
+    public function withQuery($query);
+
+    /**
+     * Return an instance with the specified URI fragment.
+     *
+     * This method MUST retain the state of the current instance, and return
+     * an instance that contains the specified URI fragment.
+     *
+     * Users can provide both encoded and decoded fragment characters.
+     * Implementations ensure the correct encoding as outlined in getFragment().
+     *
+     * An empty fragment value is equivalent to removing the fragment.
+     *
+     * @param string $fragment The fragment to use with the new instance.
+     * @return static A new instance with the specified fragment.
+     */
+    public function withFragment($fragment);
+
+    /**
+     * Return the string representation as a URI reference.
+     *
+     * Depending on which components of the URI are present, the resulting
+     * string is either a full URI or relative reference according to RFC 3986,
+     * Section 4.1. The method concatenates the various components of the URI,
+     * using the appropriate delimiters:
+     *
+     * - If a scheme is present, it MUST be suffixed by ":".
+     * - If an authority is present, it MUST be prefixed by "//".
+     * - The path can be concatenated without delimiters. But there are two
+     *   cases where the path has to be adjusted to make the URI reference
+     *   valid as PHP does not allow to throw an exception in __toString():
+     *     - If the path is rootless and an authority is present, the path MUST
+     *       be prefixed by "/".
+     *     - If the path is starting with more than one "/" and no authority is
+     *       present, the starting slashes MUST be reduced to one.
+     * - If a query is present, it MUST be prefixed by "?".
+     * - If a fragment is present, it MUST be prefixed by "#".
+     *
+     * @see http://tools.ietf.org/html/rfc3986#section-4.1
+     * @return string
+     */
+    public function __toString();
+}
diff --git a/vendor/psr/log/.gitignore b/vendor/psr/log/.gitignore
new file mode 100755
index 0000000..22d0d82
--- /dev/null
+++ b/vendor/psr/log/.gitignore
@@ -0,0 +1 @@
+vendor
diff --git a/vendor/psr/log/LICENSE b/vendor/psr/log/LICENSE
new file mode 100755
index 0000000..474c952
--- /dev/null
+++ b/vendor/psr/log/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2012 PHP Framework Interoperability Group
+
+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.
diff --git a/vendor/psr/log/Psr/Log/AbstractLogger.php b/vendor/psr/log/Psr/Log/AbstractLogger.php
new file mode 100755
index 0000000..90e721a
--- /dev/null
+++ b/vendor/psr/log/Psr/Log/AbstractLogger.php
@@ -0,0 +1,128 @@
+<?php
+
+namespace Psr\Log;
+
+/**
+ * This is a simple Logger implementation that other Loggers can inherit from.
+ *
+ * It simply delegates all log-level-specific methods to the `log` method to
+ * reduce boilerplate code that a simple Logger that does the same thing with
+ * messages regardless of the error level has to implement.
+ */
+abstract class AbstractLogger implements LoggerInterface
+{
+    /**
+     * System is unusable.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function emergency($message, array $context = array())
+    {
+        $this->log(LogLevel::EMERGENCY, $message, $context);
+    }
+
+    /**
+     * Action must be taken immediately.
+     *
+     * Example: Entire website down, database unavailable, etc. This should
+     * trigger the SMS alerts and wake you up.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function alert($message, array $context = array())
+    {
+        $this->log(LogLevel::ALERT, $message, $context);
+    }
+
+    /**
+     * Critical conditions.
+     *
+     * Example: Application component unavailable, unexpected exception.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function critical($message, array $context = array())
+    {
+        $this->log(LogLevel::CRITICAL, $message, $context);
+    }
+
+    /**
+     * Runtime errors that do not require immediate action but should typically
+     * be logged and monitored.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function error($message, array $context = array())
+    {
+        $this->log(LogLevel::ERROR, $message, $context);
+    }
+
+    /**
+     * Exceptional occurrences that are not errors.
+     *
+     * Example: Use of deprecated APIs, poor use of an API, undesirable things
+     * that are not necessarily wrong.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function warning($message, array $context = array())
+    {
+        $this->log(LogLevel::WARNING, $message, $context);
+    }
+
+    /**
+     * Normal but significant events.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function notice($message, array $context = array())
+    {
+        $this->log(LogLevel::NOTICE, $message, $context);
+    }
+
+    /**
+     * Interesting events.
+     *
+     * Example: User logs in, SQL logs.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function info($message, array $context = array())
+    {
+        $this->log(LogLevel::INFO, $message, $context);
+    }
+
+    /**
+     * Detailed debug information.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function debug($message, array $context = array())
+    {
+        $this->log(LogLevel::DEBUG, $message, $context);
+    }
+}
diff --git a/vendor/psr/log/Psr/Log/InvalidArgumentException.php b/vendor/psr/log/Psr/Log/InvalidArgumentException.php
new file mode 100755
index 0000000..67f852d
--- /dev/null
+++ b/vendor/psr/log/Psr/Log/InvalidArgumentException.php
@@ -0,0 +1,7 @@
+<?php
+
+namespace Psr\Log;
+
+class InvalidArgumentException extends \InvalidArgumentException
+{
+}
diff --git a/vendor/psr/log/Psr/Log/LogLevel.php b/vendor/psr/log/Psr/Log/LogLevel.php
new file mode 100755
index 0000000..9cebcac
--- /dev/null
+++ b/vendor/psr/log/Psr/Log/LogLevel.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Psr\Log;
+
+/**
+ * Describes log levels.
+ */
+class LogLevel
+{
+    const EMERGENCY = 'emergency';
+    const ALERT     = 'alert';
+    const CRITICAL  = 'critical';
+    const ERROR     = 'error';
+    const WARNING   = 'warning';
+    const NOTICE    = 'notice';
+    const INFO      = 'info';
+    const DEBUG     = 'debug';
+}
diff --git a/vendor/psr/log/Psr/Log/LoggerAwareInterface.php b/vendor/psr/log/Psr/Log/LoggerAwareInterface.php
new file mode 100755
index 0000000..4d64f47
--- /dev/null
+++ b/vendor/psr/log/Psr/Log/LoggerAwareInterface.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Psr\Log;
+
+/**
+ * Describes a logger-aware instance.
+ */
+interface LoggerAwareInterface
+{
+    /**
+     * Sets a logger instance on the object.
+     *
+     * @param LoggerInterface $logger
+     *
+     * @return void
+     */
+    public function setLogger(LoggerInterface $logger);
+}
diff --git a/vendor/psr/log/Psr/Log/LoggerAwareTrait.php b/vendor/psr/log/Psr/Log/LoggerAwareTrait.php
new file mode 100755
index 0000000..639f79b
--- /dev/null
+++ b/vendor/psr/log/Psr/Log/LoggerAwareTrait.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace Psr\Log;
+
+/**
+ * Basic Implementation of LoggerAwareInterface.
+ */
+trait LoggerAwareTrait
+{
+    /**
+     * The logger instance.
+     *
+     * @var LoggerInterface
+     */
+    protected $logger;
+
+    /**
+     * Sets a logger.
+     *
+     * @param LoggerInterface $logger
+     */
+    public function setLogger(LoggerInterface $logger)
+    {
+        $this->logger = $logger;
+    }
+}
diff --git a/vendor/psr/log/Psr/Log/LoggerInterface.php b/vendor/psr/log/Psr/Log/LoggerInterface.php
new file mode 100755
index 0000000..e695046
--- /dev/null
+++ b/vendor/psr/log/Psr/Log/LoggerInterface.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace Psr\Log;
+
+/**
+ * Describes a logger instance.
+ *
+ * The message MUST be a string or object implementing __toString().
+ *
+ * The message MAY contain placeholders in the form: {foo} where foo
+ * will be replaced by the context data in key "foo".
+ *
+ * The context array can contain arbitrary data. The only assumption that
+ * can be made by implementors is that if an Exception instance is given
+ * to produce a stack trace, it MUST be in a key named "exception".
+ *
+ * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
+ * for the full interface specification.
+ */
+interface LoggerInterface
+{
+    /**
+     * System is unusable.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function emergency($message, array $context = array());
+
+    /**
+     * Action must be taken immediately.
+     *
+     * Example: Entire website down, database unavailable, etc. This should
+     * trigger the SMS alerts and wake you up.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function alert($message, array $context = array());
+
+    /**
+     * Critical conditions.
+     *
+     * Example: Application component unavailable, unexpected exception.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function critical($message, array $context = array());
+
+    /**
+     * Runtime errors that do not require immediate action but should typically
+     * be logged and monitored.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function error($message, array $context = array());
+
+    /**
+     * Exceptional occurrences that are not errors.
+     *
+     * Example: Use of deprecated APIs, poor use of an API, undesirable things
+     * that are not necessarily wrong.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function warning($message, array $context = array());
+
+    /**
+     * Normal but significant events.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function notice($message, array $context = array());
+
+    /**
+     * Interesting events.
+     *
+     * Example: User logs in, SQL logs.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function info($message, array $context = array());
+
+    /**
+     * Detailed debug information.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function debug($message, array $context = array());
+
+    /**
+     * Logs with an arbitrary level.
+     *
+     * @param mixed  $level
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     *
+     * @throws \Psr\Log\InvalidArgumentException
+     */
+    public function log($level, $message, array $context = array());
+}
diff --git a/vendor/psr/log/Psr/Log/LoggerTrait.php b/vendor/psr/log/Psr/Log/LoggerTrait.php
new file mode 100755
index 0000000..e392fef
--- /dev/null
+++ b/vendor/psr/log/Psr/Log/LoggerTrait.php
@@ -0,0 +1,142 @@
+<?php
+
+namespace Psr\Log;
+
+/**
+ * This is a simple Logger trait that classes unable to extend AbstractLogger
+ * (because they extend another class, etc) can include.
+ *
+ * It simply delegates all log-level-specific methods to the `log` method to
+ * reduce boilerplate code that a simple Logger that does the same thing with
+ * messages regardless of the error level has to implement.
+ */
+trait LoggerTrait
+{
+    /**
+     * System is unusable.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function emergency($message, array $context = array())
+    {
+        $this->log(LogLevel::EMERGENCY, $message, $context);
+    }
+
+    /**
+     * Action must be taken immediately.
+     *
+     * Example: Entire website down, database unavailable, etc. This should
+     * trigger the SMS alerts and wake you up.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function alert($message, array $context = array())
+    {
+        $this->log(LogLevel::ALERT, $message, $context);
+    }
+
+    /**
+     * Critical conditions.
+     *
+     * Example: Application component unavailable, unexpected exception.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function critical($message, array $context = array())
+    {
+        $this->log(LogLevel::CRITICAL, $message, $context);
+    }
+
+    /**
+     * Runtime errors that do not require immediate action but should typically
+     * be logged and monitored.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function error($message, array $context = array())
+    {
+        $this->log(LogLevel::ERROR, $message, $context);
+    }
+
+    /**
+     * Exceptional occurrences that are not errors.
+     *
+     * Example: Use of deprecated APIs, poor use of an API, undesirable things
+     * that are not necessarily wrong.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function warning($message, array $context = array())
+    {
+        $this->log(LogLevel::WARNING, $message, $context);
+    }
+
+    /**
+     * Normal but significant events.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function notice($message, array $context = array())
+    {
+        $this->log(LogLevel::NOTICE, $message, $context);
+    }
+
+    /**
+     * Interesting events.
+     *
+     * Example: User logs in, SQL logs.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function info($message, array $context = array())
+    {
+        $this->log(LogLevel::INFO, $message, $context);
+    }
+
+    /**
+     * Detailed debug information.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function debug($message, array $context = array())
+    {
+        $this->log(LogLevel::DEBUG, $message, $context);
+    }
+
+    /**
+     * Logs with an arbitrary level.
+     *
+     * @param mixed  $level
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     *
+     * @throws \Psr\Log\InvalidArgumentException
+     */
+    abstract public function log($level, $message, array $context = array());
+}
diff --git a/vendor/psr/log/Psr/Log/NullLogger.php b/vendor/psr/log/Psr/Log/NullLogger.php
new file mode 100755
index 0000000..c8f7293
--- /dev/null
+++ b/vendor/psr/log/Psr/Log/NullLogger.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Psr\Log;
+
+/**
+ * This Logger can be used to avoid conditional log calls.
+ *
+ * Logging should always be optional, and if no logger is provided to your
+ * library creating a NullLogger instance to have something to throw logs at
+ * is a good way to avoid littering your code with `if ($this->logger) { }`
+ * blocks.
+ */
+class NullLogger extends AbstractLogger
+{
+    /**
+     * Logs with an arbitrary level.
+     *
+     * @param mixed  $level
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     *
+     * @throws \Psr\Log\InvalidArgumentException
+     */
+    public function log($level, $message, array $context = array())
+    {
+        // noop
+    }
+}
diff --git a/vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php b/vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php
new file mode 100755
index 0000000..9ecb6c4
--- /dev/null
+++ b/vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php
@@ -0,0 +1,146 @@
+<?php
+
+namespace Psr\Log\Test;
+
+use Psr\Log\LoggerInterface;
+use Psr\Log\LogLevel;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * Provides a base test class for ensuring compliance with the LoggerInterface.
+ *
+ * Implementors can extend the class and implement abstract methods to run this
+ * as part of their test suite.
+ */
+abstract class LoggerInterfaceTest extends TestCase
+{
+    /**
+     * @return LoggerInterface
+     */
+    abstract public function getLogger();
+
+    /**
+     * This must return the log messages in order.
+     *
+     * The simple formatting of the messages is: "<LOG LEVEL> <MESSAGE>".
+     *
+     * Example ->error('Foo') would yield "error Foo".
+     *
+     * @return string[]
+     */
+    abstract public function getLogs();
+
+    public function testImplements()
+    {
+        $this->assertInstanceOf('Psr\Log\LoggerInterface', $this->getLogger());
+    }
+
+    /**
+     * @dataProvider provideLevelsAndMessages
+     */
+    public function testLogsAtAllLevels($level, $message)
+    {
+        $logger = $this->getLogger();
+        $logger->{$level}($message, array('user' => 'Bob'));
+        $logger->log($level, $message, array('user' => 'Bob'));
+
+        $expected = array(
+            $level.' message of level '.$level.' with context: Bob',
+            $level.' message of level '.$level.' with context: Bob',
+        );
+        $this->assertEquals($expected, $this->getLogs());
+    }
+
+    public function provideLevelsAndMessages()
+    {
+        return array(
+            LogLevel::EMERGENCY => array(LogLevel::EMERGENCY, 'message of level emergency with context: {user}'),
+            LogLevel::ALERT => array(LogLevel::ALERT, 'message of level alert with context: {user}'),
+            LogLevel::CRITICAL => array(LogLevel::CRITICAL, 'message of level critical with context: {user}'),
+            LogLevel::ERROR => array(LogLevel::ERROR, 'message of level error with context: {user}'),
+            LogLevel::WARNING => array(LogLevel::WARNING, 'message of level warning with context: {user}'),
+            LogLevel::NOTICE => array(LogLevel::NOTICE, 'message of level notice with context: {user}'),
+            LogLevel::INFO => array(LogLevel::INFO, 'message of level info with context: {user}'),
+            LogLevel::DEBUG => array(LogLevel::DEBUG, 'message of level debug with context: {user}'),
+        );
+    }
+
+    /**
+     * @expectedException \Psr\Log\InvalidArgumentException
+     */
+    public function testThrowsOnInvalidLevel()
+    {
+        $logger = $this->getLogger();
+        $logger->log('invalid level', 'Foo');
+    }
+
+    public function testContextReplacement()
+    {
+        $logger = $this->getLogger();
+        $logger->info('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar'));
+
+        $expected = array('info {Message {nothing} Bob Bar a}');
+        $this->assertEquals($expected, $this->getLogs());
+    }
+
+    public function testObjectCastToString()
+    {
+        if (method_exists($this, 'createPartialMock')) {
+            $dummy = $this->createPartialMock('Psr\Log\Test\DummyTest', array('__toString'));
+        } else {
+            $dummy = $this->getMock('Psr\Log\Test\DummyTest', array('__toString'));
+        }
+        $dummy->expects($this->once())
+            ->method('__toString')
+            ->will($this->returnValue('DUMMY'));
+
+        $this->getLogger()->warning($dummy);
+
+        $expected = array('warning DUMMY');
+        $this->assertEquals($expected, $this->getLogs());
+    }
+
+    public function testContextCanContainAnything()
+    {
+        $closed = fopen('php://memory', 'r');
+        fclose($closed);
+
+        $context = array(
+            'bool' => true,
+            'null' => null,
+            'string' => 'Foo',
+            'int' => 0,
+            'float' => 0.5,
+            'nested' => array('with object' => new DummyTest),
+            'object' => new \DateTime,
+            'resource' => fopen('php://memory', 'r'),
+            'closed' => $closed,
+        );
+
+        $this->getLogger()->warning('Crazy context data', $context);
+
+        $expected = array('warning Crazy context data');
+        $this->assertEquals($expected, $this->getLogs());
+    }
+
+    public function testContextExceptionKeyCanBeExceptionOrOtherValues()
+    {
+        $logger = $this->getLogger();
+        $logger->warning('Random message', array('exception' => 'oops'));
+        $logger->critical('Uncaught Exception!', array('exception' => new \LogicException('Fail')));
+
+        $expected = array(
+            'warning Random message',
+            'critical Uncaught Exception!'
+        );
+        $this->assertEquals($expected, $this->getLogs());
+    }
+}
+
+class DummyTest
+{
+    public function __toString()
+    {
+        return 'DummyTest';
+    }
+}
diff --git a/vendor/psr/log/Psr/Log/Test/TestLogger.php b/vendor/psr/log/Psr/Log/Test/TestLogger.php
new file mode 100755
index 0000000..1be3230
--- /dev/null
+++ b/vendor/psr/log/Psr/Log/Test/TestLogger.php
@@ -0,0 +1,147 @@
+<?php
+
+namespace Psr\Log\Test;
+
+use Psr\Log\AbstractLogger;
+
+/**
+ * Used for testing purposes.
+ *
+ * It records all records and gives you access to them for verification.
+ *
+ * @method bool hasEmergency($record)
+ * @method bool hasAlert($record)
+ * @method bool hasCritical($record)
+ * @method bool hasError($record)
+ * @method bool hasWarning($record)
+ * @method bool hasNotice($record)
+ * @method bool hasInfo($record)
+ * @method bool hasDebug($record)
+ *
+ * @method bool hasEmergencyRecords()
+ * @method bool hasAlertRecords()
+ * @method bool hasCriticalRecords()
+ * @method bool hasErrorRecords()
+ * @method bool hasWarningRecords()
+ * @method bool hasNoticeRecords()
+ * @method bool hasInfoRecords()
+ * @method bool hasDebugRecords()
+ *
+ * @method bool hasEmergencyThatContains($message)
+ * @method bool hasAlertThatContains($message)
+ * @method bool hasCriticalThatContains($message)
+ * @method bool hasErrorThatContains($message)
+ * @method bool hasWarningThatContains($message)
+ * @method bool hasNoticeThatContains($message)
+ * @method bool hasInfoThatContains($message)
+ * @method bool hasDebugThatContains($message)
+ *
+ * @method bool hasEmergencyThatMatches($message)
+ * @method bool hasAlertThatMatches($message)
+ * @method bool hasCriticalThatMatches($message)
+ * @method bool hasErrorThatMatches($message)
+ * @method bool hasWarningThatMatches($message)
+ * @method bool hasNoticeThatMatches($message)
+ * @method bool hasInfoThatMatches($message)
+ * @method bool hasDebugThatMatches($message)
+ *
+ * @method bool hasEmergencyThatPasses($message)
+ * @method bool hasAlertThatPasses($message)
+ * @method bool hasCriticalThatPasses($message)
+ * @method bool hasErrorThatPasses($message)
+ * @method bool hasWarningThatPasses($message)
+ * @method bool hasNoticeThatPasses($message)
+ * @method bool hasInfoThatPasses($message)
+ * @method bool hasDebugThatPasses($message)
+ */
+class TestLogger extends AbstractLogger
+{
+    /**
+     * @var array
+     */
+    public $records = [];
+
+    public $recordsByLevel = [];
+
+    /**
+     * @inheritdoc
+     */
+    public function log($level, $message, array $context = [])
+    {
+        $record = [
+            'level' => $level,
+            'message' => $message,
+            'context' => $context,
+        ];
+
+        $this->recordsByLevel[$record['level']][] = $record;
+        $this->records[] = $record;
+    }
+
+    public function hasRecords($level)
+    {
+        return isset($this->recordsByLevel[$level]);
+    }
+
+    public function hasRecord($record, $level)
+    {
+        if (is_string($record)) {
+            $record = ['message' => $record];
+        }
+        return $this->hasRecordThatPasses(function ($rec) use ($record) {
+            if ($rec['message'] !== $record['message']) {
+                return false;
+            }
+            if (isset($record['context']) && $rec['context'] !== $record['context']) {
+                return false;
+            }
+            return true;
+        }, $level);
+    }
+
+    public function hasRecordThatContains($message, $level)
+    {
+        return $this->hasRecordThatPasses(function ($rec) use ($message) {
+            return strpos($rec['message'], $message) !== false;
+        }, $level);
+    }
+
+    public function hasRecordThatMatches($regex, $level)
+    {
+        return $this->hasRecordThatPasses(function ($rec) use ($regex) {
+            return preg_match($regex, $rec['message']) > 0;
+        }, $level);
+    }
+
+    public function hasRecordThatPasses(callable $predicate, $level)
+    {
+        if (!isset($this->recordsByLevel[$level])) {
+            return false;
+        }
+        foreach ($this->recordsByLevel[$level] as $i => $rec) {
+            if (call_user_func($predicate, $rec, $i)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public function __call($method, $args)
+    {
+        if (preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) {
+            $genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3];
+            $level = strtolower($matches[2]);
+            if (method_exists($this, $genericMethod)) {
+                $args[] = $level;
+                return call_user_func_array([$this, $genericMethod], $args);
+            }
+        }
+        throw new \BadMethodCallException('Call to undefined method ' . get_class($this) . '::' . $method . '()');
+    }
+
+    public function reset()
+    {
+        $this->records = [];
+        $this->recordsByLevel = [];
+    }
+}
diff --git a/vendor/psr/log/README.md b/vendor/psr/log/README.md
new file mode 100755
index 0000000..a9f20c4
--- /dev/null
+++ b/vendor/psr/log/README.md
@@ -0,0 +1,58 @@
+PSR Log
+=======
+
+This repository holds all interfaces/classes/traits related to
+[PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md).
+
+Note that this is not a logger of its own. It is merely an interface that
+describes a logger. See the specification for more details.
+
+Installation
+------------
+
+```bash
+composer require psr/log
+```
+
+Usage
+-----
+
+If you need a logger, you can use the interface like this:
+
+```php
+<?php
+
+use Psr\Log\LoggerInterface;
+
+class Foo
+{
+    private $logger;
+
+    public function __construct(LoggerInterface $logger = null)
+    {
+        $this->logger = $logger;
+    }
+
+    public function doSomething()
+    {
+        if ($this->logger) {
+            $this->logger->info('Doing work');
+        }
+           
+        try {
+            $this->doSomethingElse();
+        } catch (Exception $exception) {
+            $this->logger->error('Oh no!', array('exception' => $exception));
+        }
+
+        // do something useful
+    }
+}
+```
+
+You can then pick one of the implementations of the interface to get a logger.
+
+If you want to implement the interface, you can require this package and
+implement `Psr\Log\LoggerInterface` in your code. Please read the
+[specification text](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md)
+for details.
diff --git a/vendor/psr/log/composer.json b/vendor/psr/log/composer.json
new file mode 100755
index 0000000..3f6d4ee
--- /dev/null
+++ b/vendor/psr/log/composer.json
@@ -0,0 +1,26 @@
+{
+    "name": "psr/log",
+    "description": "Common interface for logging libraries",
+    "keywords": ["psr", "psr-3", "log"],
+    "homepage": "https://github.com/php-fig/log",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "PHP-FIG",
+            "homepage": "http://www.php-fig.org/"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "Psr\\Log\\": "Psr/Log/"
+        }
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.1.x-dev"
+        }
+    }
+}
diff --git a/vendor/psr/simple-cache/.editorconfig b/vendor/psr/simple-cache/.editorconfig
new file mode 100755
index 0000000..48542cb
--- /dev/null
+++ b/vendor/psr/simple-cache/.editorconfig
@@ -0,0 +1,12 @@
+; This file is for unifying the coding style for different editors and IDEs.
+; More information at http://editorconfig.org
+
+root = true
+
+[*]
+charset = utf-8
+indent_size = 4
+indent_style = space
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
diff --git a/vendor/psr/simple-cache/LICENSE.md b/vendor/psr/simple-cache/LICENSE.md
new file mode 100755
index 0000000..e49a7c8
--- /dev/null
+++ b/vendor/psr/simple-cache/LICENSE.md
@@ -0,0 +1,21 @@
+# The MIT License (MIT)
+
+Copyright (c) 2016 PHP Framework Interoperability Group
+
+> 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.
diff --git a/vendor/psr/simple-cache/README.md b/vendor/psr/simple-cache/README.md
new file mode 100755
index 0000000..43641d1
--- /dev/null
+++ b/vendor/psr/simple-cache/README.md
@@ -0,0 +1,8 @@
+PHP FIG Simple Cache PSR
+========================
+
+This repository holds all interfaces related to PSR-16.
+
+Note that this is not a cache implementation of its own. It is merely an interface that describes a cache implementation. See [the specification](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-16-simple-cache.md) for more details.
+
+You can find implementations of the specification by looking for packages providing the [psr/simple-cache-implementation](https://packagist.org/providers/psr/simple-cache-implementation) virtual package.
diff --git a/vendor/psr/simple-cache/composer.json b/vendor/psr/simple-cache/composer.json
new file mode 100755
index 0000000..2978fa5
--- /dev/null
+++ b/vendor/psr/simple-cache/composer.json
@@ -0,0 +1,25 @@
+{
+    "name": "psr/simple-cache",
+    "description": "Common interfaces for simple caching",
+    "keywords": ["psr", "psr-16", "cache", "simple-cache", "caching"],
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "PHP-FIG",
+            "homepage": "http://www.php-fig.org/"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "Psr\\SimpleCache\\": "src/"
+        }
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.0.x-dev"
+        }
+    }
+}
diff --git a/vendor/psr/simple-cache/src/CacheException.php b/vendor/psr/simple-cache/src/CacheException.php
new file mode 100755
index 0000000..eba5381
--- /dev/null
+++ b/vendor/psr/simple-cache/src/CacheException.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace Psr\SimpleCache;
+
+/**
+ * Interface used for all types of exceptions thrown by the implementing library.
+ */
+interface CacheException
+{
+}
diff --git a/vendor/psr/simple-cache/src/CacheInterface.php b/vendor/psr/simple-cache/src/CacheInterface.php
new file mode 100755
index 0000000..99e8d95
--- /dev/null
+++ b/vendor/psr/simple-cache/src/CacheInterface.php
@@ -0,0 +1,114 @@
+<?php
+
+namespace Psr\SimpleCache;
+
+interface CacheInterface
+{
+    /**
+     * Fetches a value from the cache.
+     *
+     * @param string $key     The unique key of this item in the cache.
+     * @param mixed  $default Default value to return if the key does not exist.
+     *
+     * @return mixed The value of the item from the cache, or $default in case of cache miss.
+     *
+     * @throws \Psr\SimpleCache\InvalidArgumentException
+     *   MUST be thrown if the $key string is not a legal value.
+     */
+    public function get($key, $default = null);
+
+    /**
+     * Persists data in the cache, uniquely referenced by a key with an optional expiration TTL time.
+     *
+     * @param string                 $key   The key of the item to store.
+     * @param mixed                  $value The value of the item to store, must be serializable.
+     * @param null|int|\DateInterval $ttl   Optional. The TTL value of this item. If no value is sent and
+     *                                      the driver supports TTL then the library may set a default value
+     *                                      for it or let the driver take care of that.
+     *
+     * @return bool True on success and false on failure.
+     *
+     * @throws \Psr\SimpleCache\InvalidArgumentException
+     *   MUST be thrown if the $key string is not a legal value.
+     */
+    public function set($key, $value, $ttl = null);
+
+    /**
+     * Delete an item from the cache by its unique key.
+     *
+     * @param string $key The unique cache key of the item to delete.
+     *
+     * @return bool True if the item was successfully removed. False if there was an error.
+     *
+     * @throws \Psr\SimpleCache\InvalidArgumentException
+     *   MUST be thrown if the $key string is not a legal value.
+     */
+    public function delete($key);
+
+    /**
+     * Wipes clean the entire cache's keys.
+     *
+     * @return bool True on success and false on failure.
+     */
+    public function clear();
+
+    /**
+     * Obtains multiple cache items by their unique keys.
+     *
+     * @param iterable $keys    A list of keys that can obtained in a single operation.
+     * @param mixed    $default Default value to return for keys that do not exist.
+     *
+     * @return iterable A list of key => value pairs. Cache keys that do not exist or are stale will have $default as value.
+     *
+     * @throws \Psr\SimpleCache\InvalidArgumentException
+     *   MUST be thrown if $keys is neither an array nor a Traversable,
+     *   or if any of the $keys are not a legal value.
+     */
+    public function getMultiple($keys, $default = null);
+
+    /**
+     * Persists a set of key => value pairs in the cache, with an optional TTL.
+     *
+     * @param iterable               $values A list of key => value pairs for a multiple-set operation.
+     * @param null|int|\DateInterval $ttl    Optional. The TTL value of this item. If no value is sent and
+     *                                       the driver supports TTL then the library may set a default value
+     *                                       for it or let the driver take care of that.
+     *
+     * @return bool True on success and false on failure.
+     *
+     * @throws \Psr\SimpleCache\InvalidArgumentException
+     *   MUST be thrown if $values is neither an array nor a Traversable,
+     *   or if any of the $values are not a legal value.
+     */
+    public function setMultiple($values, $ttl = null);
+
+    /**
+     * Deletes multiple cache items in a single operation.
+     *
+     * @param iterable $keys A list of string-based keys to be deleted.
+     *
+     * @return bool True if the items were successfully removed. False if there was an error.
+     *
+     * @throws \Psr\SimpleCache\InvalidArgumentException
+     *   MUST be thrown if $keys is neither an array nor a Traversable,
+     *   or if any of the $keys are not a legal value.
+     */
+    public function deleteMultiple($keys);
+
+    /**
+     * Determines whether an item is present in the cache.
+     *
+     * NOTE: It is recommended that has() is only to be used for cache warming type purposes
+     * and not to be used within your live applications operations for get/set, as this method
+     * is subject to a race condition where your has() will return true and immediately after,
+     * another script can remove it making the state of your app out of date.
+     *
+     * @param string $key The cache item key.
+     *
+     * @return bool
+     *
+     * @throws \Psr\SimpleCache\InvalidArgumentException
+     *   MUST be thrown if the $key string is not a legal value.
+     */
+    public function has($key);
+}
diff --git a/vendor/psr/simple-cache/src/InvalidArgumentException.php b/vendor/psr/simple-cache/src/InvalidArgumentException.php
new file mode 100755
index 0000000..6a9524a
--- /dev/null
+++ b/vendor/psr/simple-cache/src/InvalidArgumentException.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace Psr\SimpleCache;
+
+/**
+ * Exception interface for invalid cache arguments.
+ *
+ * When an invalid argument is passed it must throw an exception which implements
+ * this interface
+ */
+interface InvalidArgumentException extends CacheException
+{
+}
diff --git a/vendor/qcloud/cos-sdk-v5/.gitignore b/vendor/qcloud/cos-sdk-v5/.gitignore
new file mode 100755
index 0000000..1c0b8d4
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/.gitignore
@@ -0,0 +1,2 @@
+.vscode/
+.idea/
\ No newline at end of file
diff --git a/vendor/qcloud/cos-sdk-v5/.travis.yml b/vendor/qcloud/cos-sdk-v5/.travis.yml
new file mode 100755
index 0000000..99220f2
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/.travis.yml
@@ -0,0 +1,6 @@
+language: php
+php:
+  - 5.6
+script: 
+  - composer install
+  - phpunit -v
diff --git a/vendor/qcloud/cos-sdk-v5/LICENSE b/vendor/qcloud/cos-sdk-v5/LICENSE
new file mode 100755
index 0000000..2c948c8
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 腾讯云
+
+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.
diff --git a/vendor/qcloud/cos-sdk-v5/README.md b/vendor/qcloud/cos-sdk-v5/README.md
new file mode 100755
index 0000000..87bc395
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/README.md
@@ -0,0 +1,264 @@
+# COS-PHP-SDK-V5
+腾讯云COS-PHP-SDK-V5([XML API](https://cloud.tencent.com/document/product/436/7751))
+
+[![Latest Stable Version](https://poser.pugx.org/qcloud/cos-sdk-v5/v/stable)](https://packagist.org/packages/qcloud/cos-sdk-v5)
+[![Total Downloads](https://img.shields.io/packagist/dt/qcloud/cos-sdk-v5.svg?style=flat)](https://packagist.org/packages/qcloud/cos-sdk-v5)
+[![Build Status](https://travis-ci.org/tencentyun/cos-php-sdk-v5.svg?branch=master)](https://travis-ci.org/tencentyun/cos-php-sdk-v5)
+
+## 环境准备
+*   PHP 5.3+
+    您可以通过`php -v`命令查看当前的 PHP 版本。
+*   cURL 扩展
+    您可以通过`php -m`命令查看 cURL 扩展是否已经安装好。
+
+>- Ubuntu 系统中,您可以使用 apt-get 包管理器安装 PHP 的 cURL 扩展,安装命令如下。
+```
+sudo apt-get install php-curl
+```
+>- CentOS 系统中,您可以使用 yum 包管理器安装 PHP 的 cURL 扩展。
+```
+sudo yum install php-curl
+```
+
+## SDK 安装
+SDK 安装有三种方式:
+* Composer 方式
+* Phar 方式
+* 源码方式
+
+### Composer 方式
+推荐使用 Composer 安装 cos-php-sdk-v5,Composer 是 PHP 的依赖管理工具,允许您声明项目所需的依赖,然后自动将它们安装到您的项目中。
+> 您可以在 [Composer 官网](https://getcomposer.org/) 上找到更多关于如何安装 Composer,配置自动加载以及用于定义依赖项的其他最佳实践等相关信息。
+
+#### 安装步骤: 
+1. 打开终端。
+2. 下载 Composer,执行以下命令。
+```
+curl -sS https://getcomposer.org/installer | php
+```
+3. 创建一个名为`composer.json`的文件,内容如下。
+```
+{
+    "require": {
+        "qcloud/cos-sdk-v5": "1.*"
+    }
+}
+```
+4. 使用 Composer 安装,执行以下命令。
+```
+php composer.phar install
+```
+使用该命令后会在当前目录中创建一个 vendor 文件夹,里面包含 SDK 的依赖库和一个 autoload.php 脚本,方便在项目中调用。
+5. 通过 autoloader 脚本调用 cos-php-sdk-v5。
+```
+require '/path/to/sdk/vendor/autoload.php';
+```
+
+现在您的项目已经可以使用 COS 的 V5 版本 SDK 了。
+
+### Phar 方式
+Phar 方式安装 SDK 的步骤如下:
+1. 在 [GitHub 发布页面](https://github.com/tencentyun/cos-php-sdk-v5/releases) 下载相应的 phar 文件。
+2.  在代码中引入 phar 文件:
+```
+require  '/path/to/cos-sdk-v5.phar';
+```
+
+### 源码方式
+源码方式安装 SDK 的步骤如下:
+1.  在 [GitHub 发布页面](https://github.com/tencentyun/cos-php-sdk-v5/releases) 下载相应的 zip 文件。
+2.  解压通过 autoload.php 脚本加载 SDK:
+```
+require '/path/to/sdk/vendor/autoload.php';
+```
+
+## 快速入门 
+可参照 Demo 程序,详见 [sample.php](https://github.com/tencentyun/cos-php-sdk-v5/blob/master/sample.php)。
+
+## 接口文档
+php sdk 接口文档,详见https://cloud.tencent.com/document/product/436/12267
+
+### 配置文件
+```php
+$cosClient = new Qcloud\Cos\Client(array('region' => '<Region>',
+    'credentials'=> array(
+        'secretId'    => '<SecretId>',
+        'secretKey' => '<SecretKey>')));
+```
+
+若您使用 [临时密钥](https://cloud.tencent.com/document/product/436/14048) 初始化,请用下面方式创建实例。
+
+```
+$cosClient = new Qcloud\Cos\Client(array('region' => '<Region>',
+    'credentials'=> array(
+        'secretId'    => '<SecretId>',
+        'secretKey' => '<SecretKey>',
+        'token' => '<XCosSecurityToken>')));
+```
+
+### 上传文件
+* 使用putObject接口上传文件(最大5G)
+* 使用Upload接口分块上传文件
+```php
+# 上传文件
+## putObject(上传接口,最大支持上传5G文件)
+### 上传内存中的字符串
+//bucket 的命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式
+try {
+    $result = $cosClient->putObject(array(
+        'Bucket' => $bucket,
+        'Key' => $key,
+        'Body' => 'Hello World!'));
+    print_r($result);
+} catch (\Exception $e) {
+    echo "$e\n";
+}
+
+### 上传文件流
+try {
+    $result = $cosClient->putObject(array(
+        'Bucket' => $bucket,
+        'Key' => $key,
+        'Body' => fopen($local_path, 'rb')));
+    print_r($result);
+} catch (\Exception $e) {
+    echo "$e\n";
+}
+
+### 设置header和meta
+try {
+    $result = $cosClient->putObject(array(
+        'Bucket' => $bucket,
+        'Key' => $key,
+        'Body' => fopen($local_path, 'rb'),
+        'ACL' => 'string',
+        'CacheControl' => 'string',
+        'ContentDisposition' => 'string',
+        'ContentEncoding' => 'string',
+        'ContentLanguage' => 'string',
+        'ContentLength' => integer,
+        'ContentType' => 'string',
+        'Expires' => 'mixed type: string (date format)|int (unix timestamp)|\DateTime',
+        'Metadata' => array(
+            'string' => 'string',
+        ),
+        'StorageClass' => 'string'));
+    print_r($result);
+} catch (\Exception $e) {
+    echo "$e\n";
+}
+
+## Upload(高级上传接口,默认使用分块上传最大支持50T)
+### 上传内存中的字符串
+try {
+    $result = $cosClient->Upload(
+        $bucket = $bucket,
+        $key = $key,
+        $body = 'Hello World!');
+    print_r($result);
+} catch (\Exception $e) {
+    echo "$e\n";
+}
+
+### 上传文件流
+try {
+    $result = $cosClient->Upload(
+        $bucket = $bucket,
+        $key = $key,
+        $body = fopen($local_path, 'rb'));
+    print_r($result);
+} catch (\Exception $e) {
+    echo "$e\n";
+}
+
+### 设置header和meta
+try {
+    $result = $cosClient->Upload(
+        $bucket= $bucket,
+        $key = $key,
+        $body = fopen($local_path, 'rb'),
+        $options = array(
+            'ACL' => 'string',
+            'CacheControl' => 'string',
+            'ContentDisposition' => 'string',
+            'ContentEncoding' => 'string',
+            'ContentLanguage' => 'string',
+            'ContentLength' => integer,
+            'ContentType' => 'string',
+            'Expires' => 'mixed type: string (date format)|int (unix timestamp)|\DateTime',
+            'Metadata' => array(
+                'string' => 'string',
+            ),
+            'StorageClass' => 'string'));
+    print_r($result);
+} catch (\Exception $e) {
+    echo "$e\n";
+}
+```
+
+### 下载文件
+* 使用getObject接口下载文件
+* 使用getObjectUrl接口获取文件下载URL
+```php
+# 下载文件
+## getObject(下载文件)
+### 下载到内存
+//bucket 的命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式
+try {
+    $result = $cosClient->getObject(array(
+        'Bucket' => $bucket,
+        'Key' => $key));
+    echo($result['Body']);
+} catch (\Exception $e) {
+    echo "$e\n";
+}
+
+### 下载到本地
+try {
+    $result = $cosClient->getObject(array(
+        'Bucket' => $bucket,
+        'Key' => $key,
+        'SaveAs' => $local_path));
+} catch (\Exception $e) {
+    echo "$e\n";
+}
+
+### 指定下载范围
+/*
+ * Range 字段格式为 'bytes=a-b'
+ */
+try {
+    $result = $cosClient->getObject(array(
+        'Bucket' => $bucket,
+        'Key' => $key,
+        'Range' => 'bytes=0-10',
+        'SaveAs' => $local_path));
+} catch (\Exception $e) {
+    echo "$e\n";
+}
+
+### 设置返回header
+try {
+    $result = $cosClient->getObject(array(
+        'Bucket' => $bucket,
+        'Key' => $key,
+        'ResponseCacheControl' => 'string',
+        'ResponseContentDisposition' => 'string',
+        'ResponseContentEncoding' => 'string',
+        'ResponseContentLanguage' => 'string',
+        'ResponseContentType' => 'string',
+        'ResponseExpires' => 'mixed type: string (date format)|int (unix timestamp)|\DateTime',
+        'SaveAs' => $local_path));
+} catch (\Exception $e) {
+    echo "$e\n";
+}
+
+## getObjectUrl(获取文件UrL)
+try {
+    $signedUrl = $cosClient->getObjectUrl($bucket, $key, '+10 minutes');
+    echo $signedUrl;
+} catch (\Exception $e) {
+    print_r($e);
+}
+```
+
diff --git a/vendor/qcloud/cos-sdk-v5/composer.json b/vendor/qcloud/cos-sdk-v5/composer.json
new file mode 100755
index 0000000..bdeff53
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/composer.json
@@ -0,0 +1,27 @@
+{
+    "name": "qcloud/cos-sdk-v5",
+    "description": "PHP SDK for QCloud COS",
+    "keywords": [
+        "qcloud", "cos", "php"
+    ],
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "yaozongyou",
+            "email": "yaozongyou@vip.qq.com"
+        },
+        {
+            "name": "lewzylu",
+            "email": "327874225@qq.com"
+        }
+    ],
+    "autoload": {
+      "psr-0": {
+        "Qcloud\\Cos\\": "src/"
+     }
+    },
+    "require": {
+        "php": ">=5.3.0",
+        "guzzle/guzzle": "~3.7"
+    }
+}
diff --git a/vendor/qcloud/cos-sdk-v5/phpunit.xml b/vendor/qcloud/cos-sdk-v5/phpunit.xml
new file mode 100755
index 0000000..3c7a4c1
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/phpunit.xml
@@ -0,0 +1,7 @@
+<phpunit bootstrap="vendor/autoload.php">
+  <testsuites>
+    <testsuite name="cos">
+      <directory>src/Qcloud/Cos/Tests</directory>
+    </testsuite>
+  </testsuites>
+</phpunit>
diff --git a/vendor/qcloud/cos-sdk-v5/sample.php b/vendor/qcloud/cos-sdk-v5/sample.php
new file mode 100755
index 0000000..4c98c86
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample.php
@@ -0,0 +1,824 @@
+<?php
+
+require 'vendor/autoload.php';
+
+$cosClient = new Qcloud\Cos\Client(array(
+    'region' => 'COS_REGION', #地域,如ap-guangzhou,ap-beijing-1
+    'credentials' => array(
+        'secretId' => 'COS_KEY',
+        'secretKey' => 'COS_SECRET',
+    ),
+));
+
+// 若初始化 Client 时未填写 appId,则 bucket 的命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式
+$bucket = 'test2-1252448703';
+$key = 'a.txt';
+$local_path = "E:/a.txt";
+
+# 上传文件
+## putObject(上传接口,最大支持上传5G文件)
+### 上传内存中的字符串
+try {
+    $result = $cosClient->putObject(array(
+        'Bucket' => $bucket,
+        'Key' => $key,
+        'Body' => 'Hello World!'
+    ));
+    print_r($result);
+    # 可以直接通过$result读出返回结果
+    echo ($result['ETag']);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+### 上传文件流
+try {
+    $result = $cosClient->putObject(array(
+        'Bucket' => $bucket,
+        'Key' => $key,
+        'Body' => fopen($local_path, 'rb')
+    ));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+### 设置header和meta
+try {
+    $result = $cosClient->putObject(array(
+        'Bucket' => $bucket,
+        'Key' => $key,
+        'Body' => fopen($local_path, 'rb'),
+        'ACL' => 'string',
+        'CacheControl' => 'string',
+        'ContentDisposition' => 'string',
+        'ContentEncoding' => 'string',
+        'ContentLanguage' => 'string',
+        'ContentLength' => integer,
+        'cONTENTType' => 'string',
+        'Expires' => 'mixed type: string (date format)|int (unix timestamp)|\DateTime',
+        'GrantFullControl' => 'string',
+        'GrantRead' => 'string',
+        'GrantWrite' => 'string',
+        'Metadata' => array(
+            'string' => 'string',
+        ),
+        'StorageClass' => 'string'
+    ));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+## Upload(高级上传接口,默认使用分块上传最大支持50T)
+### 上传内存中的字符串
+try {
+    $result = $cosClient->upload(
+        $bucket = $bucket,
+        $key = $key,
+        $body = 'Hello World!'
+    );
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+### 上传文件流
+try {
+    $result = $cosClient->upload(
+        $bucket = $bucket,
+        $key = $key,
+        $body = fopen($local_path, 'rb')
+    );
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+### 设置header和meta
+try {
+    $result = $cosClient->upload(
+        $bucket = $bucket,
+        $key = $key,
+        $body = fopen($local_path, 'rb'),
+        $options = array(
+            'ACL' => 'string',
+            'CacheControl' => 'string',
+            'ContentDisposition' => 'string',
+            'ContentEncoding' => 'string',
+            'ContentLanguage' => 'string',
+            'ContentLength' => integer,
+            'ContentType' => 'string',
+            'Expires' => 'mixed type: string (date format)|int (unix timestamp)|\DateTime',
+            'GrantFullControl' => 'string',
+            'GrantRead' => 'string',
+            'GrantWrite' => 'string',
+            'Metadata' => array(
+                'string' => 'string',
+            ),
+            'StorageClass' => 'string'
+        )
+    );
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+## 预签名上传createPresignedUrl
+## 获取带有签名的url
+### 简单上传预签名
+try {
+    #此处可以替换为其他上传接口
+    $command = $cosClient->getCommand('putObject', array(
+        'Bucket' => $bucket,
+        'Key' => $key,
+        'Body' => '', //Body可以任意
+    ));
+    $signedUrl = $command->createPresignedUrl('+10 minutes');
+    echo ($signedUrl);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+### 分块上传预签名
+try {
+    #此处可以替换为其他上传接口
+    $command = $cosClient->getCommand('uploadPart', array(
+        'Bucket' => $bucket,
+        'Key' => $key,
+        'UploadId' => '',
+        'PartNumber' => '1',
+        'Body' => '', //Body可以任意
+    ));
+    $signedUrl = $command->createPresignedUrl('+10 minutes');
+    echo ($signedUrl);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+### 获取签名
+try {
+    #此处可以替换为其他上传接口
+    $command = $cosClient->getCommand('putObject', array(
+        'Bucket' => $bucket,
+        'Key' => $key,
+        'Body' => '', //Body可以任意
+    ));
+    $signedUrl = $command->createAuthorization('+10 minutes');
+    echo ($signedUrl);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+
+# 下载文件
+## getObject(下载文件)
+### 下载到内存
+try {
+    $result = $cosClient->getObject(array(
+        'Bucket' => $bucket,
+        'Key' => $key
+    ));
+    echo $result['Body'];
+} catch (\Exception $e) {
+    echo($e);
+}
+
+### 下载到本地
+try {
+    $result = $cosClient->getObject(array(
+        'Bucket' => $bucket,
+        'Key' => $key,
+        'SaveAs' => $local_path
+    ));
+} catch (\Exception $e) {
+    echo($e);
+}
+
+### 指定下载范围
+/*
+ * Range 字段格式为 'bytes=a-b'
+ */
+try {
+    $result = $cosClient->getObject(array(
+        'Bucket' => $bucket,
+        'Key' => $key,
+        'Range' => 'bytes=0-10',
+        'SaveAs' => $local_path
+    ));
+} catch (\Exception $e) {
+    echo($e);
+}
+
+### 设置返回header
+try {
+    $result = $cosClient->getObject(array(
+        'Bucket' => $bucket,
+        'Key' => $key,
+        'ResponseCacheControl' => 'string',
+        'ResponseContentDisposition' => 'string',
+        'ResponseContentEncoding' => 'string',
+        'ResponseContentLanguage' => 'string',
+        'ResponseContentType' => 'string',
+        'ResponseExpires' => 'mixed type: string (date format)|int (unix timestamp)|\DateTime',
+        'SaveAs' => $local_path
+    ));
+} catch (\Exception $e) {
+    echo($e);
+}
+
+## getObjectUrl(获取文件UrL)
+try {
+    $signedUrl = $cosClient->getObjectUrl($bucket, $key, '+10 minutes');
+    echo $signedUrl;
+} catch (\Exception $e) {
+    echo($e);
+}
+
+# 删除object
+## deleteObject
+try {
+    $result = $cosClient->deleteObject(array(
+        'Bucket' => $bucket,
+        'Key' => $key,
+        'VersionId' => 'string'
+    ));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+# 删除多个object
+## deleteObjects
+try {
+    $result = $cosClient->deleteObjects(array(
+        'Bucket' => 'string',
+        'Objects' => array(
+            array(
+                'Key' => $key,
+                'VersionId' => 'string',
+            ),
+            // ... repeated
+        ),
+    ));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+# 获取object信息
+## headObject
+/*
+ * 可代替isObjectExist接口,查询object是否存在
+ */
+try {
+    $result = $cosClient->headObject(array(
+        'Bucket' => $bucket,
+        'Key' => '11',
+        'VersionId' => '111',
+    ));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+# 获取bucket列表
+## listBuckets
+try {
+    $result = $cosClient->listBuckets();
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+# 创建bucket
+## createBucket
+try {
+    $result = $cosClient->createBucket(array('Bucket' => $bucket));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+# 删除bucket
+## deleteBucket
+try {
+    $result = $cosClient->deleteBucket(array(
+        'Bucket' => $bucket
+    ));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+# 获取bucket信息
+## headBucket
+/*
+ * 可代替isBucketExist接口,查询bucket是否存在
+ */
+try {
+    $result = $cosClient->headBucket(array(
+        'Bucket' => $bucket
+    ));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+# 列出bucket下的object
+## listObjects
+### 列出所有object
+/*
+ * 该接口一次最多列出1000个,需要列出所有请参考其他服务中的清空并删除bucket接口
+ */
+try {
+    $result = $cosClient->listObjects(array(
+        'Bucket' => $bucket
+    ));
+    foreach ($result['Contents'] as $rt) {
+        print_r($rt);
+    }
+} catch (\Exception $e) {
+    echo($e);
+}
+
+### 列出带有前缀的object
+try {
+    $result = $cosClient->listObjects(array(
+        'Bucket' => $bucket,
+        'Prefix' => 'string'
+    ));
+    foreach ($result['Contents'] as $rt) {
+        print_r($rt);
+    }
+} catch (\Exception $e) {
+    echo($e);
+}
+
+# 获取bucket地域
+## getBucketLocation
+try {
+    $result = $cosClient->getBucketLocation(array(
+        'Bucket' => 'lewzylu02',
+    ));
+} catch (\Exception $e) {
+    echo($e);
+};
+
+# 多版本相关
+## putBucketVersioning(开启关闭某个bucket的多版本)
+try {
+    $result = $cosClient->putBucketVersioning(array(
+        'Bucket' => $bucket,
+        'Status' => 'Enabled'
+    ));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+## ListObjectVersions(列出多版本object)
+/*
+ * 同名文件会出现多个版本
+ */
+try {
+    $result = $cosClient->listObjectVersions(array(
+        'Bucket' => $bucket,
+        'Prefix' => 'string'
+    ));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+## getBucketVersioning(获取某个bucket多版本属性)
+try {
+    $result = $cosClient->getBucketVersioning(
+        array('Bucket' => $bucket));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+# ACL相关
+## PutBucketAcl(设置bucketACL)
+try {
+    $result = $cosClient->putBucketAcl(array(
+        'Bucket' => $bucket,
+        'Grants' => array(
+            array(
+                'Grantee' => array(
+                    'DisplayName' => 'qcs::cam::uin/327874225:uin/327874225',
+                    'ID' => 'qcs::cam::uin/327874225:uin/327874225',
+                    'Type' => 'CanonicalUser',
+                ),
+                'Permission' => 'FULL_CONTROL',
+            ),
+            // ... repeated
+        ),
+        'Owner' => array(
+            'DisplayName' => 'qcs::cam::uin/3210232098:uin/3210232098',
+            'ID' => 'qcs::cam::uin/3210232098:uin/3210232098',
+        )));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+## getBucketAcl(获取bucketACL)
+try {
+    $result = $cosClient->getBucketAcl(array(
+        'Bucket' => $bucket));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+## putObjectAcl(设置objectACL)
+try {
+    $result = $cosClient->putObjectAcl(array(
+        'Bucket' => $bucket,
+        'Key' => $key,
+        'Grants' => array(
+            array(
+                'Grantee' => array(
+                    'DisplayName' => 'qcs::cam::uin/327874225:uin/327874225',
+                    'ID' => 'qcs::cam::uin/327874225:uin/327874225',
+                    'Type' => 'CanonicalUser',
+                ),
+                'Permission' => 'FULL_CONTROL',
+            ),
+            // ... repeated
+        ),
+        'Owner' => array(
+            'DisplayName' => 'qcs::cam::uin/3210232098:uin/3210232098',
+            'ID' => 'qcs::cam::uin/3210232098:uin/3210232098',
+        )));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+## GetObjectAcl(获取objectACL)
+try {
+    $result = $cosClient->getObjectAcl(array(
+        'Bucket' => $bucket,
+        'Key' => $key));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+# 生命周期相关
+## putBucketLifecycle(设置bucket生命周期)
+try {
+    $result = $cosClient->putBucketLifecycle(array(
+        'Bucket' => $bucket,
+        'Rules' => array(
+            array(
+                'Expiration' => array(
+                    'Days' => 1000,
+                ),
+                'ID' => 'id1',
+                'Filter' => array(
+                    'Prefix' => 'documents/',
+                ),
+                'Status' => 'Enabled',
+                'Transitions' => array(
+                    array(
+                        'Days' => 200,
+                        'StorageClass' => 'NEARLINE'),
+                ),
+            ),
+        )));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+## getBucketLifecycle(获取bucket生命周期)
+try {
+    $result = $cosClient->getBucketLifecycle(array(
+        'Bucket' => $bucket,
+    ));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+## deleteBucketLifecycle(删除bucket生命周期)
+try {
+    $result = $cosClient->deleteBucketLifecycle(array(
+        'Bucket' => $bucket,
+    ));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+# 跨域相关
+## putBucketCors(设置bucket跨域)
+try {
+    $result = $cosClient->putBucketCors(array(
+        'Bucket' => $bucket,
+        'CORSRules' => array(
+            array(
+                'ID' => '1234',
+                'AllowedHeaders' => array('*'),
+                'AllowedMethods' => array('PUT'),
+                'AllowedOrigins' => array('http://www.qq.com'),
+            ),
+        ),
+    ));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+## getBucketCors(获取bucket跨域信息)
+try {
+    $result = $cosClient->getBucketCors(array());
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+## deleteBucketCors(删除bucket跨域)
+try {
+    $result = $cosClient->deleteBucketCors(array(
+        // Bucket is required
+        'Bucket' => $bucket,
+    ));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+# 跨区域复制相关
+## PutBucketReplication(设置bucket跨区域复制)
+### 注意:目标bucket和源bucket都需要开启多版本
+try {
+    $result = $cosClient->putBucketReplication(array(
+        'Bucket' => $bucket,
+        'Role' => 'qcs::cam::uin/327874225:uin/327874225',
+        'Rules'=>array(
+            array(
+                'Status' => 'Enabled',
+                'ID' => 'string',
+                'Prefix' => 'string',
+                'Destination' => array(
+                    'Bucket' => 'qcs::cos:ap-guangzhou::lewzylu01-1252448703',
+                    'StorageClass' => 'standard',
+                ),
+                // ...repeated
+            ),
+        ),
+    ));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+## GetBucketReplication(获取bucket跨区域复制信息)
+try {
+    $result = $cosClient->getBucketReplication(array(
+        'Bucket' => $bucket
+    ));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+## DeleteBucketReplication(删除bucket跨区域复制信息)
+try {
+    $result = $cosClient->deleteBucketReplication(array(
+        'Bucket' => $bucket
+    ));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+# 回调相关
+## PutBucketNotification
+try {
+    $result = $cosClient->putBucketNotification(array(
+            "Bucket" => $bucket,
+            "CloudFunctionConfigurations"=> array(
+                array(
+                    "Id" => "test-1",
+                    "Filter" => array(
+                        "Key" => array(
+                            "FilterRules" => array(
+                                array(
+                                    "Name" => "Prefix",
+                                    "Value" => "111"
+                                ),
+                                array(
+                                    "Name" => "Suffix",
+                                    "Value" => "111"
+                                ),
+                            ),
+                        )
+                    ),
+                    "CloudFunction" => "qcs:0:video:sh:appid/1253125191:video/10010",
+                    "Events" => array(
+                        'Event' => "cos:ObjectCreated:*"
+                    )
+                ),
+                array(
+                    "Id" => "test-2",
+                    "Filter" => array(
+                        "Key" => array(
+                            "FilterRules" => array(
+                                array(
+                                    "Name" => "Prefix",
+                                    "Value" => "111"
+                                ),
+                                array(
+                                    "Name" => "Suffix",
+                                    "Value" => "111"
+                                ),
+                            ),
+                        )
+                    ),
+                    "CloudFunction" => "qcs:0:video:sh:appid/1253125191:video/10010",
+                    "Events" => array(
+                        'Event' => "cos:ObjectRemove:*"
+                    )
+                ),
+            ))
+    );
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+## GetBucketNotification
+try {
+    $result = $cosClient->getBucketNotification(array(
+        'Bucket' => $bucket
+    ));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+# 复制
+## copyobject(简单复制)
+/*
+ * 将{bucket},{region},{cos_path},{versionId}替换成复制源的真实信息
+ */
+try {
+    $result = $cosClient->copyObject(array(
+        'Bucket' => $bucket,
+        'CopySource' => '{bucket}.cos.{region}.myqcloud.com/{cos_path}?versionId={versionId}',
+        'Key' => 'string',
+    ));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+## Copy(分块并发复制)
+/*
+ * 将{bucket},{region},{cos_path},{versionId}替换成复制源的真实信息
+ */
+try {
+    $result = $cosClient->copy(
+        $bucket = $bucket,
+        $key = $key,
+        $copysource = '{bucket}.cos.{region}.myqcloud.com/{cos_path}',
+        $options = array('VersionId' => '{versionId}'
+        ));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+# 恢复归档文件
+## restoreObject
+try {
+    $result = $cosClient->restoreObject(array(
+        'Bucket' => $bucket,
+        'Key' => $key,
+        'Days' => 7,
+        'CASJobParameters' => array(
+            'Tier' => 'Bulk',
+        ),
+    ));
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+# 其他服务
+## 列出某bucket下所有的object
+try {
+    $prefix = '';
+    $marker = '';
+    while (true) {
+        $result = $cosClient->listObjects(array(
+            'Bucket' => $bucket,
+            'Marker' => $marker,
+            'MaxKeys' => 1000
+        ));
+        foreach ($result['Contents'] as $rt) {
+            print_r($rt['Key'] . " ");
+            /*
+             * 使用下面的代码可以删除全部object
+             */
+            // try {
+            //     $result = $cosClient->deleteobjects(array(
+            //         'Bucket' => $bucket,
+            //         'Key' => $rt['Key']));
+            //     print_r($result);
+            // } catch (\Exception $e) {
+            //     echo($e);
+            // }
+        }
+        $marker = $result['NextMarker'];
+        if (!$result['IsTruncated']) {
+            break;
+        }
+    }
+} catch (\Exception $e) {
+    echo($e);
+}
+
+## 删除所有因上传失败而产生的分块
+/*
+ * 可以清理掉因分块上传失败
+ */
+try {
+    while (true) {
+        $result = $cosClient->listMultipartUploads(
+            array('Bucket' => $bucket,
+                'Prefix' => ''));
+        if (count($result['Uploads']) == 0) {
+            break;
+        }
+        foreach ($result['Uploads'] as $upload) {
+            try {
+                $rt = $cosClient->abortMultipartUpload(array(
+                    'Bucket' => $bucket,
+                    'Key' => $upload['Key'],
+                    'UploadId' => $upload['UploadId']
+                ));
+                print_r($rt);
+            } catch (\Exception $e) {
+                echo($e);
+            }
+        }
+    }
+} catch (\Exception $e) {
+    echo($e);
+}
+
+## 分块上传断点重传
+/*
+ * 仅适用于分块上传失败的情况
+ * 需要填写上传失败的uploadId
+ */
+try {
+    $result = $cosClient->resumeUpload(
+        $bucket = $bucket,
+        $key = $key,
+        $body = fopen("E:/test.txt", 'rb'),
+        $uploadId = '152448808231afdf221eb558ab15d1e455d2afd025c5663936142fdf5614ebf6d1668e2eda'
+    );
+    print_r($result);
+} catch (\Exception $e) {
+    echo($e);
+}
+
+## 删除某些前缀的空bucket
+function startsWith($haystack, $needle)
+{
+    $length = strlen($needle);
+    return (substr($haystack, 0, $length) === $needle);
+}
+
+try {
+    $result = $cosClient->listBuckets();
+    foreach ($result['Buckets'] as $bucket) {
+        $region = $bucket['Location'];
+        $name = $bucket['Name'];
+        if (startsWith($name, 'lewzylu')) {
+            try {
+                $cosClient2 = new Qcloud\Cos\Client(array(
+                    'region' => $region,
+                    'credentials' => array(
+                        //getenv为获取本地环境变量,请替换为真实密钥
+                        'secretId' => getenv('COS_KEY'),
+                        'secretKey' => getenv('COS_SECRET'))
+                ));
+                $rt = $cosClient2->deleteBucket(array('Bucket' => $name));
+                print_r($rt);
+            } catch (\Exception $e) {
+            }
+        }
+    }
+} catch (\Exception $e) {
+    echo($e);
+}
diff --git a/vendor/qcloud/cos-sdk-v5/sample/abortMultipartUpload.php b/vendor/qcloud/cos-sdk-v5/sample/abortMultipartUpload.php
new file mode 100755
index 0000000..50cd6da
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/abortMultipartUpload.php
@@ -0,0 +1,27 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+try {
+    $result = $cosClient->abortMultipartUpload(array(
+        'Bucket' => 'examplebucket-125000000', //格式:BucketName-APPID
+        'Key' => 'exampleobject', 
+        'UploadId' => 'string',
+    ));
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    echo($e);
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/sample/catchException.php b/vendor/qcloud/cos-sdk-v5/sample/catchException.php
new file mode 100755
index 0000000..b44e6f1
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/catchException.php
@@ -0,0 +1,30 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+try {
+    $result = $cosClient->getBucketAcl(array(
+        'Bucket' => 'examplebucket-125000000' //格式:BucketName-APPID
+    ));
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    $statusCode = $e->getStatusCode(); // 获取错误码
+    $errorMessage = $e->getMessage(); // 获取错误信息
+    $requestId = $e->getRequestId(); // 获取错误的requestId
+    $errorCode = $e->getCosErrorCode(); // 获取错误名称
+    $request = $e->getRequest(); // 获取完整的请求
+    $response = $e->getResponse(); // 获取完整的响应
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/sample/completeMultipartUpload.php b/vendor/qcloud/cos-sdk-v5/sample/completeMultipartUpload.php
new file mode 100755
index 0000000..35e0e04
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/completeMultipartUpload.php
@@ -0,0 +1,37 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+try {
+    $result = $cosClient->completeMultipartUpload(array(
+        'Bucket' => 'examplebucket-125000000', //格式:BucketName-APPID
+        'Key' => 'exampleobject', 
+        'UploadId' => 'string',
+        'Parts' => array(
+            array(
+                'ETag' => 'string',
+                'PartNumber' => integer,
+            ),
+            array(
+                'ETag' => 'string',
+                'PartNumber' => integer,
+            )),
+            // ... repeated
+    ));
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    echo($e);
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/sample/copyObject.php b/vendor/qcloud/cos-sdk-v5/sample/copyObject.php
new file mode 100755
index 0000000..813fc46
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/copyObject.php
@@ -0,0 +1,28 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+try {
+    $result = $cosClient->copyObject(array(
+        'Bucket' => 'examplebucket-125000000', //格式:BucketName-APPID
+        'Key' => 'exampleobject',
+        'CopySource' => 'examplebucket2-125000000.cos.ap-guangzhou.myqcloud.com/exampleobject',
+        'MetadataDirective' => 'Replaced',
+    ));
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    echo($e);
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/sample/createBucket.php b/vendor/qcloud/cos-sdk-v5/sample/createBucket.php
new file mode 100755
index 0000000..eafad49
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/createBucket.php
@@ -0,0 +1,25 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+try {
+    $result = $cosClient->createBucket(array(
+        'Bucket' => 'examplebucket-125000000' //格式:BucketName-APPID
+    ));
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    echo($e);
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/sample/createMultipartUpload.php b/vendor/qcloud/cos-sdk-v5/sample/createMultipartUpload.php
new file mode 100755
index 0000000..bedca7e
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/createMultipartUpload.php
@@ -0,0 +1,39 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+try {
+    $result = $cosClient->createMultipartUpload(array(
+        'Bucket' => 'examplebucket-125000000', //格式:BucketName-APPID
+        'Key' => 'exampleobject',
+        /*
+        'CacheControl' => 'string',
+        'ContentDisposition' => 'string',
+        'ContentEncoding' => 'string',
+        'ContentLanguage' => 'string',
+        'ContentLength' => integer,
+        'ContentType' => 'string',
+        'Expires' => 'string',
+        'Metadata' => array(
+            'string' => 'string',
+        ),
+        'StorageClass' => 'string'
+        */
+    ));
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    echo($e);
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/sample/deleteBucket.php b/vendor/qcloud/cos-sdk-v5/sample/deleteBucket.php
new file mode 100755
index 0000000..a3d1fc2
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/deleteBucket.php
@@ -0,0 +1,25 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+try {
+    $result = $cosClient->deleteBucket(array(
+        'Bucket' => 'examplebucket-125000000' //格式:BucketName-APPID
+    ));
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    echo($e);
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/sample/deleteBucketCors.php b/vendor/qcloud/cos-sdk-v5/sample/deleteBucketCors.php
new file mode 100755
index 0000000..fc958e1
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/deleteBucketCors.php
@@ -0,0 +1,25 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+try {
+    $result = $cosClient->deleteBucketCors(array(
+        'Bucket' => 'examplebucket-125000000' //格式:BucketName-APPID
+    ));
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    echo($e);
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/sample/deleteBucketLifecycle.php b/vendor/qcloud/cos-sdk-v5/sample/deleteBucketLifecycle.php
new file mode 100755
index 0000000..dbe3183
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/deleteBucketLifecycle.php
@@ -0,0 +1,25 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+try {
+    $result = $cosClient->deleteBucketLifecycle(array(
+        'Bucket' => 'examplebucket-125000000' //格式:BucketName-APPID
+    ));
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    echo($e);
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/sample/deleteObject.php b/vendor/qcloud/cos-sdk-v5/sample/deleteObject.php
new file mode 100755
index 0000000..d89655e
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/deleteObject.php
@@ -0,0 +1,26 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+try {
+    $result = $cosClient->deleteObject(array(
+        'Bucket' => 'examplebucket-125000000', //格式:BucketName-APPID
+        'Key' => 'exampleobject',
+    ));
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    echo($e);
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/sample/getBucketAcl.php b/vendor/qcloud/cos-sdk-v5/sample/getBucketAcl.php
new file mode 100755
index 0000000..997c56f
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/getBucketAcl.php
@@ -0,0 +1,25 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+try {
+    $result = $cosClient->getBucketAcl(array(
+        'Bucket' => 'examplebucket-125000000' //格式:BucketName-APPID
+    ));
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    echo($e);
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/sample/getBucketCors.php b/vendor/qcloud/cos-sdk-v5/sample/getBucketCors.php
new file mode 100755
index 0000000..55b347f
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/getBucketCors.php
@@ -0,0 +1,25 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+try {
+    $result = $cosClient->getBucketCors(array(
+        'Bucket' => 'examplebucket-125000000' //格式:BucketName-APPID
+    ));
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    echo($e);
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/sample/getBucketLifecycle.php b/vendor/qcloud/cos-sdk-v5/sample/getBucketLifecycle.php
new file mode 100755
index 0000000..aef6702
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/getBucketLifecycle.php
@@ -0,0 +1,25 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+try {
+    $result = $cosClient->getBucketLifecycle(array(
+        'Bucket' => 'examplebucket-125000000' //格式:BucketName-APPID
+    ));
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    echo($e);
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/sample/getObject.php b/vendor/qcloud/cos-sdk-v5/sample/getObject.php
new file mode 100755
index 0000000..cbc0da7
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/getObject.php
@@ -0,0 +1,36 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+try {
+    $result = $cosClient->getObject(array(
+        'Bucket' => 'examplebucket-125000000', //格式:BucketName-APPID
+        'Key' => 'exampleobject',
+        'SaveAs' => '/data/exampleobject',
+        /*
+        'Range' => 'bytes=0-10',
+        'ResponseCacheControl' => 'string',
+        'ResponseContentDisposition' => 'string',
+        'ResponseContentEncoding' => 'string',
+        'ResponseContentLanguage' => 'string',
+        'ResponseContentType' => 'string',
+        'ResponseExpires' => 'string',
+        */
+    ));
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    echo($e);
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/sample/headBucket.php b/vendor/qcloud/cos-sdk-v5/sample/headBucket.php
new file mode 100755
index 0000000..2f8d0c7
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/headBucket.php
@@ -0,0 +1,25 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+try {
+    $result = $cosClient->headBucket(array(
+        'Bucket' => 'examplebucket-125000000' //格式:BucketName-APPID
+    ));
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    echo($e);
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/sample/headObject.php b/vendor/qcloud/cos-sdk-v5/sample/headObject.php
new file mode 100755
index 0000000..0d6ba2b
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/headObject.php
@@ -0,0 +1,26 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+try {
+    $result = $cosClient->headObject(array(
+        'Bucket' => 'examplebucket-125000000', //格式:BucketName-APPID
+        'Key' => 'exampleobject',
+    ));
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    echo($e);
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/sample/listBucket.php b/vendor/qcloud/cos-sdk-v5/sample/listBucket.php
new file mode 100755
index 0000000..6dfcf9a
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/listBucket.php
@@ -0,0 +1,24 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+
+try {
+    $result = $cosClient->listBuckets();
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    echo($e);
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/sample/listMultipartUploads.php b/vendor/qcloud/cos-sdk-v5/sample/listMultipartUploads.php
new file mode 100755
index 0000000..35c8100
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/listMultipartUploads.php
@@ -0,0 +1,31 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+try {
+    $result = $cosClient->listMultipartUploads(array(
+        'Bucket' => 'examplebucket-125000000', //格式:BucketName-APPID
+        'Delimiter' => '/',
+        'EncodingType' => 'url',
+        'KeyMarker' => 'string',
+        'UploadIdMarker' => 'string',
+        'Prefix' => 'prfix',
+        'MaxUploads' => 1000,
+    ));
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    echo($e);
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/sample/listObjects.php b/vendor/qcloud/cos-sdk-v5/sample/listObjects.php
new file mode 100755
index 0000000..6a4f2a1
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/listObjects.php
@@ -0,0 +1,30 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+try {
+    $result = $cosClient->listObjects(array(
+        'Bucket' => 'examplebucket-125000000', //格式:BucketName-APPID
+        'Delimiter' => '/',
+        'EncodingType' => 'url',
+        'Marker' => 'prefix/picture.jpg',
+        'Prefix' => 'prfix',
+        'MaxKeys' => 1000,
+    ));
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    echo($e);
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/sample/listParts.php b/vendor/qcloud/cos-sdk-v5/sample/listParts.php
new file mode 100755
index 0000000..6b18478
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/listParts.php
@@ -0,0 +1,29 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+try {
+    $result = $cosClient->listParts(array(
+        'Bucket' => 'examplebucket-125000000', //格式:BucketName-APPID
+        'Key' => 'exampleobject',
+        'UploadId' => 'NWNhNDY0YzFfMmZiNTM1MGFfNTM2YV8xYjliMTg',
+        'PartNumberMarker' => 1,
+        'MaxParts' => 1000,
+    ));
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    echo($e);
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/sample/putBucketAcl.php b/vendor/qcloud/cos-sdk-v5/sample/putBucketAcl.php
new file mode 100755
index 0000000..c5b475f
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/putBucketAcl.php
@@ -0,0 +1,41 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+try {
+    $result = $cosClient->putBucketAcl(array(
+        //bucket的命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式
+        'Bucket' => 'examplebucket-125000000',
+        'ACL' => 'private',
+        'Grants' => array(
+            array(
+                'Grantee' => array(
+                    'DisplayName' => 'qcs::cam::uin/100000000001:uin/100000000001',
+                    'ID' => 'qcs::cam::uin/100000000001:uin/100000000001',
+                    'Type' => 'CanonicalUser',
+                ),
+                'Permission' => 'FULL_CONTROL',
+            ),
+            // ... repeated
+        ),
+        'Owner' => array(
+            'DisplayName' => 'qcs::cam::uin/3210232098:uin/3210232098',
+            'ID' => 'qcs::cam::uin/3210232098:uin/3210232098',
+        )));
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    echo "$e\n";
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/sample/putBucketCors.php b/vendor/qcloud/cos-sdk-v5/sample/putBucketCors.php
new file mode 100755
index 0000000..375077b
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/putBucketCors.php
@@ -0,0 +1,33 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+try {
+    $result = $cosClient->putBucketCors(array(
+        'Bucket' => 'examplebucket-125000000', //格式:BucketName-APPID
+        'CORSRules' => array(
+            array(
+                'ID' => '1234',
+                'AllowedHeaders' => array('*'),
+                'AllowedMethods' => array('PUT'),
+                'AllowedOrigins' => array('http://www.qq.com'),
+            ),
+        ), 
+    ));
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    echo "$e\n";
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/sample/putBucketLifecycle.php b/vendor/qcloud/cos-sdk-v5/sample/putBucketLifecycle.php
new file mode 100755
index 0000000..5f7d818
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/putBucketLifecycle.php
@@ -0,0 +1,35 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+try {
+    $result = $cosClient->putBucketCors(array(
+        'Bucket' => 'examplebucket-125000000', //格式:BucketName-APPID
+        'CORSRules' => array(
+            array(
+                'AllowedHeaders' => array('*',),
+                'AllowedMethods' => array('Put', ),
+                'AllowedOrigins' => array('*', ),
+                'ExposeHeaders' => array('*', ),
+                'MaxAgeSeconds' => 1,
+            ),
+            // ... repeated
+        )
+    ));
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    echo "$e\n";
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/sample/putObject.php b/vendor/qcloud/cos-sdk-v5/sample/putObject.php
new file mode 100755
index 0000000..b97c5d0
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/putObject.php
@@ -0,0 +1,41 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+$local_path = "/data/exampleobject";
+try {
+    $result = $cosClient->putObject(array(
+        'Bucket' => 'examplebucket-125000000', //格式:BucketName-APPID
+        'Key' => 'exampleobject',
+        'Body' => fopen($local_path, 'rb'),
+        /*
+        'CacheControl' => 'string',
+        'ContentDisposition' => 'string',
+        'ContentEncoding' => 'string',
+        'ContentLanguage' => 'string',
+        'ContentLength' => integer,
+        'ContentType' => 'string',
+        'Expires' => 'string',
+        'Metadata' => array(
+            'string' => 'string',
+        ),
+        'StorageClass' => 'string'
+        */
+    ));
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    echo($e);
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/sample/restoreObject.php b/vendor/qcloud/cos-sdk-v5/sample/restoreObject.php
new file mode 100755
index 0000000..79eeffb
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/restoreObject.php
@@ -0,0 +1,30 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+try {
+    $result = $cosClient->putObject(array(
+        'Bucket' => 'examplebucket-125000000', //格式:BucketName-APPID
+        'Key' => 'exampleobject',
+        'Days' => integer,
+        'CASJobParameters' => array(
+            'Tier' =>'string'
+        )      
+    ));
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    echo($e);
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/sample/upload.php b/vendor/qcloud/cos-sdk-v5/sample/upload.php
new file mode 100755
index 0000000..70a24a2
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/upload.php
@@ -0,0 +1,49 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+$local_path = "/data/exampleobject";
+try {
+    $result = $cosClient->upload(
+        $bucket = 'examplebucket-125000000', //格式:BucketName-APPID
+        $key = 'exampleobject',
+        $body = fopen($local_path, 'rb')
+        /*
+        $options = array(
+            'ACL' => 'string',
+            'CacheControl' => 'string',
+            'ContentDisposition' => 'string',
+            'ContentEncoding' => 'string',
+            'ContentLanguage' => 'string',
+            'ContentLength' => integer,
+            'ContentType' => 'string',
+            'Expires' => 'string',
+            'GrantFullControl' => 'string',
+            'GrantRead' => 'string',
+            'GrantWrite' => 'string',
+            'Metadata' => array(
+                'string' => 'string',
+            ),
+            'ContentMD5' => 'string',
+            'ServerSideEncryption' => 'string',
+            'StorageClass' => 'string'
+        )
+        */
+    );
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    echo($e);
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/sample/uploadPart.php b/vendor/qcloud/cos-sdk-v5/sample/uploadPart.php
new file mode 100755
index 0000000..d255807
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/sample/uploadPart.php
@@ -0,0 +1,31 @@
+<?php
+
+require dirname(__FILE__) . '/../vendor/autoload.php';
+
+$secretId = "COS_SECRETID"; //"云 API 密钥 SecretId";
+$secretKey = "COS_SECRETKEY"; //"云 API 密钥 SecretKey";
+$region = "ap-beijing"; //设置一个默认的存储桶地域
+$cosClient = new Qcloud\Cos\Client(
+    array(
+        'region' => $region,
+        'schema' => 'https', //协议头部,默认为http
+        'credentials'=> array(
+            'secretId'  => $secretId ,
+            'secretKey' => $secretKey)));
+try {
+    $result = $cosClient->uploadPart(array(
+        'Bucket' => 'examplebucket-125000000', //格式:BucketName-APPID
+        'Key' => 'exampleobject', 
+        'Body' => 'string',
+        'UploadId' => 'NWNhNDY0YzFfMmZiNTM1MGFfNTM2YV8xYjliMTg',
+        'PartNumber' => 1,
+        'ContentMD5' => 'string',
+        'ContentLength' => 100,
+    ));
+    // 请求成功
+    print_r($result);
+} catch (\Exception $e) {
+    // 请求失败
+    echo($e);
+}
+
diff --git a/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/BucketStyleListener.php b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/BucketStyleListener.php
new file mode 100755
index 0000000..a7f29b8
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/BucketStyleListener.php
@@ -0,0 +1,107 @@
+<?php
+
+namespace Qcloud\Cos;
+
+use Guzzle\Common\Event;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Change from path style to host style, currently only host style is supported in cos.
+ */
+function endWith($haystack, $needle) {
+    $length = strlen($needle);
+    if($length == 0)
+    {
+        return true;
+    }
+    return (substr($haystack, -$length) === $needle);
+}
+class BucketStyleListener implements EventSubscriberInterface {
+
+    private $appId;  // string: application id.
+    private $ip;
+    private $port;
+    private $ipport;
+    private $endpoint;
+
+    public function __construct($appId, $ip=null, $port=null, $endpoint=null) {
+        $this->appId = $appId;
+        $this->ip = $ip;
+        $this->port = $port;
+        $this->ipport = null;
+        $this->endpoint = $endpoint;
+        if ($ip != null) {
+            $this->ipport = $ip;
+            if ($port != null) {
+                $this->ipport = $ip.":".$port;
+            }
+        }
+    }
+
+    public static function getSubscribedEvents() {
+        return array('command.after_prepare' => array('onCommandAfterPrepare', -230));
+    }
+
+    /**
+     * Change from path style to host style.
+     * @param Event $event Event emitted.
+     */
+    public function onCommandAfterPrepare(Event $event) {
+
+        $command = $event['command'];
+        $bucket = $command['Bucket'];
+        $request = $command->getRequest();
+        if ($command->getName() == 'ListBuckets')
+        {
+            if ($this->ipport != null) {
+                $request->setHost($this->ipport);
+                $request->setHeader('Host', 'service.cos.myqcloud.com');
+            } else if ($this->endpoint != null) {
+                $request->setHost($this->endpoint);
+                $request->setHeader('Host', 'service.cos.myqcloud.com');
+            }
+            else {
+
+                $request->setHost('service.cos.myqcloud.com');
+            }
+            return ;
+        }
+        if ($key = $command['Key']) {
+            // Modify the command Key to account for the {/Key*} explosion into an array
+            if (is_array($key)) {
+                $command['Key'] = $key = implode('/', $key);
+            }
+        }
+        $request->setHeader('Date', gmdate('D, d M Y H:i:s T'));
+
+        $url_bucket = rawurlencode($bucket);
+        $request->setPath(preg_replace("#^/{$url_bucket}#", '', $request->getPath()));
+        if ($this->appId != null && endWith($bucket,'-'.$this->appId) == False)
+        {
+            $bucket = $bucket.'-'.$this->appId;
+        }
+        $request->getParams()->set('bucket', $bucket)->set('key', $key);
+
+        $realHost = $bucket. '.' . $request->getHost();
+        if($this->ipport != null) {
+            $request->setHost($this->ipport);
+            $request->setHeader('Host', $realHost);
+        } else {
+            if($this->endpoint != null) {
+                $tmp = $bucket. '.' . $this->endpoint;
+                $request->setHost($tmp);
+            } else {
+                $request->setHost($realHost);
+            }
+        }
+        if (!$bucket) {
+            $request->getParams()->set('cos.resource', '/');
+        } else {
+            // Bucket style needs a trailing slash
+            $request->getParams()->set(
+                'cos.resource',
+                '/' . rawurlencode($bucket) . ($key ? ('/' . Client::encodeKey($key)) : '/')
+            );
+        }
+    }
+} 
diff --git a/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Client.php b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Client.php
new file mode 100755
index 0000000..53bb78d
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Client.php
@@ -0,0 +1,250 @@
+<?php
+
+namespace Qcloud\Cos;
+
+use Guzzle\Service\Description\Parameter;
+use Guzzle\Service\Description\ServiceDescription;
+use Guzzle\Service\Client as GSClient;
+use Guzzle\Common\Collection;
+use Guzzle\Http\EntityBody;
+use Guzzle\Http\Message\RequestInterface;
+use Qcloud\Cos\Signature;
+use Qcloud\Cos\TokenListener;
+
+class Client extends GSClient {
+    const VERSION = '1.3.3';
+
+    private $region;       // string: region.
+    private $credentials;
+    private $appId;        // string: application id.
+    private $secretId;     // string: secret id.
+    private $secretKey;    // string: secret key.
+    private $timeout;      // int: timeout
+    private $connect_timeout; // int: connect_timeout
+    private $signature;
+    private $schema;
+    private $ip;
+    private $port;
+    private $config;
+    public function __construct($config) {
+        $this->config = $config;
+        $this->region = $config['region'];
+        $regionmap = array('cn-east'=>'ap-shanghai',
+            'cn-south'=>'ap-guangzhou',
+            'cn-north'=>'ap-beijing-1',
+            'cn-south-2'=>'ap-guangzhou-2',
+            'cn-southwest'=>'ap-chengdu',
+            'sg'=>'ap-singapore',
+            'tj'=>'ap-beijing-1',
+            'bj'=>'ap-beijing',
+            'sh'=>'ap-shanghai',
+            'gz'=>'ap-guangzhou',
+            'cd'=>'ap-chengdu',
+            'sgp'=>'ap-singapore',);
+        $this->schema = isset($config['schema']) ? $config['schema'] : 'http';
+        $this->ip = isset($config['ip']) ? $config['ip'] : null;
+        $this->port = isset($config['port']) ? $config['port'] : null;
+        $this->endpoint = isset($config['endpoint']) ? $config['endpoint'] : null;
+        $this->region =  isset($regionmap[$this->region]) ? $regionmap[$this->region] : $this->region;
+        $this->credentials = $config['credentials'];
+        $this->appId = isset($config['credentials']['appId']) ? $config['credentials']['appId'] : null;
+        $this->secretId = $config['credentials']['secretId'];
+        $this->secretKey = $config['credentials']['secretKey'];
+        $this->token = isset($config['credentials']['token']) ? $config['credentials']['token'] : null;
+        $this->timeout = isset($config['timeout']) ? $config['timeout'] : 3600;
+        $this->connect_timeout = isset($config['connect_timeout']) ? $config['connect_timeout'] : 3600;
+        $this->signature = new signature($this->secretId, $this->secretKey);
+        parent::__construct(
+            $this->schema.'://cos.' . $this->region . '.myqcloud.com/',    // base url
+            array('request.options' => array('timeout' => $this->timeout, 'connect_timeout' => $this->connect_timeout),
+            )); // show curl verbose or not
+        $desc = ServiceDescription::factory(Service::getService());
+        $this->setDescription($desc);
+        $this->setUserAgent('cos-php-sdk-v5.' . Client::VERSION, true);
+        $this->addSubscriber(new ExceptionListener());
+        $this->addSubscriber(new Md5Listener($this->signature));
+        $this->addSubscriber(new TokenListener($this->token));
+        $this->addSubscriber(new SignatureListener($this->secretId, $this->secretKey));
+        $this->addSubscriber(new BucketStyleListener($this->appId, $this->ip, $this->port, $this->endpoint));
+        $this->addSubscriber(new UploadBodyListener(array('PutObject', 'UploadPart')));
+    }
+
+    public function __destruct() {
+    }
+
+    public function __call($method, $args) {
+        return parent::__call(ucfirst($method), $args);
+    }
+
+    public function createAuthorization(RequestInterface $request, $expires)
+    {
+        if ($request->getClient() !== $this) {
+            throw new InvalidArgumentException('The request object must be associated with the client. Use the '
+                . '$client->get(), $client->head(), $client->post(), $client->put(), etc. methods when passing in a '
+                . 'request object');
+        }
+        return $this->signature->createAuthorization($request, $expires);
+    }
+    public function createPresignedUrl(RequestInterface $request, $expires)
+    {
+        if ($request->getClient() !== $this) {
+            throw new InvalidArgumentException('The request object must be associated with the client. Use the '
+                . '$client->get(), $client->head(), $client->post(), $client->put(), etc. methods when passing in a '
+                . 'request object');
+        }
+        return $this->signature->createPresignedUrl($request, $expires);
+    }
+    public function getObjectUrl($bucket, $key, $expires = null, array $args = array())
+    {
+        $command = $this->getCommand('GetObject', $args + array('Bucket' => $bucket, 'Key' => $key));
+
+        if ($command->hasKey('Scheme')) {
+            $scheme = $command['Scheme'];
+            $request = $command->remove('Scheme')->prepare()->setScheme($scheme)->setPort(null);
+        } else {
+            $request = $command->prepare();
+        }
+
+        return $expires ? $this->createPresignedUrl($request, $expires) : $request->getUrl();
+    }
+    public function Upload($bucket, $key, $body, $options = array()) {
+        $body = EntityBody::factory($body);
+        $options = Collection::fromConfig(array_change_key_case($options), array(
+            'min_part_size' => MultipartUpload::MIN_PART_SIZE,
+            'params'        => $options));
+        if ($body->getSize() < $options['min_part_size']) {
+            // Perform a simple PutObject operation
+            $rt = $this->putObject(array(
+                    'Bucket' => $bucket,
+                    'Key'    => $key,
+                    'Body'   => $body,
+                ) + $options['params']);
+
+            $rt['Location'] = $rt['ObjectURL'];
+            $rt['Location'] = ltrim($rt['Location'], $this->schema. "://");
+            unset($rt['ObjectURL']);
+        }
+        else {
+            $multipartUpload = new MultipartUpload($this, $body, $options['min_part_size'], array(
+                    'Bucket' => $bucket,
+                    'Key' => $key,
+                    'Body' => $body,
+                ) + $options['params']);
+
+            $rt = $multipartUpload->performUploading();
+            unset($rt['Bucket']);
+            unset($rt['Key']);
+        }
+        return $rt;
+    }
+
+    public function resumeUpload($bucket, $key, $body, $uploadId, $options = array()) {
+        $body = EntityBody::factory($body);
+        $options = Collection::fromConfig(array_change_key_case($options), array(
+            'min_part_size' => MultipartUpload::MIN_PART_SIZE,
+            'params'        => $options));
+        $multipartUpload = new MultipartUpload($this, $body, $options['min_part_size'], array(
+                'Bucket' => $bucket,
+                'Key' => $key,
+                'Body' => $body,
+                'UploadId' => $uploadId,
+            ) + $options['params']);
+
+        $rt = $multipartUpload->resumeUploading();
+        return $rt;
+    }
+
+    public function Copy($bucket, $key, $copysource, $options = array()) {
+
+        $options = Collection::fromConfig(array_change_key_case($options), array(
+            'min_part_size' => Copy::MIN_PART_SIZE,
+            'params'        => $options));
+        $sourcelistdot  =  explode('.',$copysource);
+        $sourcelistline = explode('-',$sourcelistdot[0]);
+        $sourceappid = array_pop($sourcelistline);
+        $sourcebucket = implode('-', $sourcelistline);
+        $sourceregion = $sourcelistdot[2];
+        $sourcekey = substr(strstr($copysource,'/'),1);
+        $sourceversion = "";
+        $sourceconfig = $this->config;
+        $sourceconfig['region'] = $sourceregion;
+        $sourceconfig['credentials']['appId'] = $sourceappid;
+        $cosClient = new Client($sourceconfig);
+        if (!key_exists('VersionId',$options['params'])) {
+            $sourceversion = "";
+        }
+        else {
+            $sourceversion = $options['params']['VersionId'];
+        }
+        $rt = $cosClient->headObject(array('Bucket'=>$sourcebucket,
+            'Key'=>$sourcekey,
+            'VersionId'=>$sourceversion));
+        $contentlength =$rt['ContentLength'];
+
+        if ($contentlength < $options['min_part_size']) {
+            return $this->copyObject(array(
+                    'Bucket' => $bucket,
+                    'Key'    => $key,
+                    'CopySource'   => $copysource."?versionId=".$sourceversion,
+                ) + $options['params']);
+        }
+        $copy = new Copy($this, $contentlength, $copysource."?versionId=".$sourceversion, $options['min_part_size'], array(
+                'Bucket' => $bucket,
+                'Key'    => $key
+            ) + $options['params']);
+
+        return $copy->copy();
+    }
+
+    /**
+     * Determines whether or not a bucket exists by name
+     *
+     * @param string $bucket    The name of the bucket
+     * @param bool   $accept403 Set to true if 403s are acceptable
+     * @param array  $options   Additional options to add to the executed command
+     *
+     * @return bool
+     */
+    public function doesBucketExist($bucket, $accept403 = true, array $options = array())
+    {
+        try {
+            $this->HeadBucket(array(
+                'Bucket' => $bucket));
+            return True;
+        }catch (\Exception $e){
+            return False;
+        }
+    }
+
+    /**
+     * Determines whether or not an object exists by name
+     *
+     * @param string $bucket  The name of the bucket
+     * @param string $key     The key of the object
+     * @param array  $options Additional options to add to the executed command
+     *
+     * @return bool
+     */
+    public function doesObjectExist($bucket, $key, array $options = array())
+    {
+        try {
+            $this->HeadObject(array(
+                'Bucket' => $bucket,
+                'Key' => $key));
+            return True;
+        }catch (\Exception $e){
+            return False;
+        }
+    }
+    public static function encodeKey($key) {
+//        return $key;
+        return str_replace('%2F', '/', rawurlencode($key));
+    }
+
+    public static function explodeKey($key) {
+        // Remove a leading slash if one is found
+        return explode('/', $key && $key[0] == '/' ? substr($key, 1) : $key);
+//        return $key;
+//        return ltrim($key, "/");
+    }
+}
diff --git a/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Command.php b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Command.php
new file mode 100755
index 0000000..647bb52
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Command.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Qcloud\Cos;
+
+use Guzzle\Service\Command\OperationCommand;
+use Guzzle\Service\Resource\Model;
+use Guzzle\Common\Event;
+
+/**
+ * Adds functionality to Qcloud Cos commands:
+ *   - Adds the PutObject URL to a response
+ *   - Allows creating a Pre-signed URL from any command
+ */
+class Command extends OperationCommand {
+    /**
+     * Create a pre-signed URL for the operation
+     *
+     * @param int|string $expires The Unix timestamp to expire at or a string that can be evaluated by strtotime
+     *
+     * @return string
+     */
+    public function createPresignedUrl($expires)
+    {
+        return $this->client->createPresignedUrl($this->prepare(), $expires);
+    }
+    public function createAuthorization($expires)
+    {
+        return $this->client->createAuthorization($this->prepare(), $expires);
+    }
+
+    protected function process() {
+        parent::process();
+        // Set the GetObject URL if using the PutObject operation
+        if ($this->result instanceof Model && $this->getName() == 'PutObject') {
+            $request = $this->getRequest();;
+            $this->result->set('ObjectURL', $request->getUrl());
+        }
+    }
+}
diff --git a/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Copy.php b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Copy.php
new file mode 100755
index 0000000..06d464a
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Copy.php
@@ -0,0 +1,140 @@
+<?php
+
+namespace Qcloud\Cos;
+
+use Guzzle\Http\ReadLimitEntityBody;
+
+
+class Copy {
+    /**
+     * const var: part size from 5MB to 5GB, and max parts of 10000 are allowed for each upload.
+     */
+    const MIN_PART_SIZE = 5242880;
+    const MAX_PART_SIZE = 5368709120;
+    const MAX_PARTS     = 10000;
+
+    private $client;
+    private $source;
+    private $options;
+    private $partSize;
+    private $size;
+
+    public function __construct($client, $contentlength, $source, $minPartSize, $options = array()) {
+        $this->client = $client;
+        $this->source = $source;
+        $this->options = $options;
+        $this->size = $contentlength;
+        $this->partSize = $this->calculatePartSize($minPartSize);
+        $this->concurrency = isset($options['concurrency']) ? $options['concurrency'] : 10;
+        $this->retry = isset($options['retry']) ? $options['retry'] : 5;
+    }
+    public function copy() {
+        $uploadId= $this->initiateMultipartUpload();
+        for ($i = 0; $i < 5; $i += 1) {
+            $rt = $this->uploadParts($uploadId);
+            if ($rt == 0) {
+                break;
+            }
+            sleep(1 << $i);
+        }
+        return $this->client->completeMultipartUpload(array(
+            'Bucket' => $this->options['Bucket'],
+            'Key' => $this->options['Key'],
+            'UploadId' => $uploadId,
+            'Parts' => $this->parts));
+
+    }
+    public function uploadParts($uploadId) {
+        $commands = array();
+        $offset = 0;
+        $partNumber = 1;
+        $partSize = $this->partSize;
+        $finishedNum = 0;
+        $this->parts = array();
+        for (;;) {
+
+            if ($offset + $partSize  >= $this->size)
+            {
+                $partSize = $this->size - $offset;
+            }
+            $params = array(
+                'Bucket' => $this->options['Bucket'],
+                'Key' => $this->options['Key'],
+                'UploadId' => $uploadId,
+                'PartNumber' => $partNumber,
+                'CopySource'=> $this->source,
+                'CopySourceRange' => 'bytes='.((string)$offset).'-'.(string)($offset+$partSize - 1),
+            );
+            if(!isset($parts[$partNumber])) {
+                $commands[] = $this->client->getCommand('UploadPartCopy', $params);
+            }
+            if ($partNumber % $this->concurrency == 0) {
+                $this->client->execute($commands);
+                $commands = array();
+            }
+            ++$partNumber;
+            $offset += $partSize;
+            if ($this->size == $offset)
+            {
+                break;
+            }
+        }
+        if (!empty($commands)) {
+            $this->client->execute($commands);
+        }
+        try {
+            $marker = 0;
+            $finishedNum = 1;
+            while (true) {
+                $rt = $this->client->listParts(array(
+                    'Bucket' => $this->options['Bucket'],
+                    'Key' => $this->options['Key'],
+                    'PartNumberMarker' => $marker,
+                    'MaxParts' => 1000,
+                    'UploadId' => $uploadId));
+                if (!empty($rt['Parts'])) {
+                    foreach ($rt['Parts'] as $part) {
+                        $part = array('PartNumber' => $finishedNum, 'ETag' => $part['ETag']);
+                        $this->parts[$finishedNum] = $part;
+                        $finishedNum++;
+                    }
+                }
+                $marker = $rt['NextPartNumberMarker'];
+                if (!$rt['IsTruncated']) {
+                    break;
+                }
+            }
+        } catch (\Exception $e) {
+            echo($e);
+        }
+        if ($finishedNum == $partNumber) {
+            return 0;
+        } else {
+            return -1;
+        }
+
+    }
+
+
+    private function calculatePartSize($minPartSize)
+    {
+        $partSize = intval(ceil(($this->size / self::MAX_PARTS)));
+        $partSize = max($minPartSize, $partSize);
+        $partSize = min($partSize, self::MAX_PART_SIZE);
+        $partSize = max($partSize, self::MIN_PART_SIZE);
+
+        return $partSize;
+    }
+
+    private function initiateMultipartUpload() {
+        $result = $this->client->createMultipartUpload($this->options);
+        return $result['UploadId'];
+    }
+
+}
+function partUploadCopy($client, $params) {
+    $rt = $client->uploadPartCopy($params);
+//    $part = array('PartNumber' => $params['PartNumber'], 'ETag' => $rt['ETag']);
+    $rt['PartNumber'] = $params['PartNumber'];
+    return $rt;
+}
\ No newline at end of file
diff --git a/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/BucketAlreadyExistsException.php b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/BucketAlreadyExistsException.php
new file mode 100755
index 0000000..08de5ac
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/BucketAlreadyExistsException.php
@@ -0,0 +1,5 @@
+<?php
+
+namespace Qcloud\Cos\Exception;
+
+class BucketAlreadyExistsException extends CosException {}
diff --git a/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/BucketNotEmptyException.php b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/BucketNotEmptyException.php
new file mode 100755
index 0000000..867d2b0
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/BucketNotEmptyException.php
@@ -0,0 +1,6 @@
+<?php
+
+namespace Qcloud\Cos\Exception;
+
+// The bucket you tried to delete is not empty.
+class BucketNotEmptyException extends CosException {}
diff --git a/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/CosException.php b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/CosException.php
new file mode 100755
index 0000000..bcf0628
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/CosException.php
@@ -0,0 +1,7 @@
+<?php
+
+namespace Qcloud\Cos\Exception;
+
+use Qcloud\Cos\Exception\ServiceResponseException;
+
+class CosException extends ServiceResponseException {}
diff --git a/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/CurlException.php b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/CurlException.php
new file mode 100755
index 0000000..67e8469
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/CurlException.php
@@ -0,0 +1,5 @@
+<?php
+
+namespace Qcloud\Cos\Exception;
+
+class CurlException extends CosException {}
diff --git a/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/InvalidArgumentException.php b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/InvalidArgumentException.php
new file mode 100755
index 0000000..b747131
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/InvalidArgumentException.php
@@ -0,0 +1,5 @@
+<?php
+
+namespace Qcloud\Cos\Exception;
+
+class InvalidArgumentException extends CosException {}
diff --git a/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/NoSuchBucketException.php b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/NoSuchBucketException.php
new file mode 100755
index 0000000..d05a9b9
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/NoSuchBucketException.php
@@ -0,0 +1,6 @@
+<?php
+
+namespace Qcloud\Cos\Exception;
+
+// The specified bucket does not exist.
+class NoSuchBucketException extends CosException {}
diff --git a/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/NoSuchKeyException.php b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/NoSuchKeyException.php
new file mode 100755
index 0000000..1e5987c
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/NoSuchKeyException.php
@@ -0,0 +1,6 @@
+<?php
+
+namespace Qcloud\Cos\Exception;
+
+// The specified key does not exist.
+class NoSuchKeyException extends CosException {}
diff --git a/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/NoSuchUploadException.php b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/NoSuchUploadException.php
new file mode 100755
index 0000000..ca4ccb9
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/NoSuchUploadException.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace Qcloud\Cos\Exception;
+
+/**
+ * The specified multipart upload does not exist.
+ */
+class NoSuchUploadException extends CosException {}
diff --git a/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/ServiceResponseException.php b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/ServiceResponseException.php
new file mode 100755
index 0000000..25718ef
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/ServiceResponseException.php
@@ -0,0 +1,189 @@
+<?php
+
+namespace Qcloud\Cos\Exception;
+
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\Response;
+
+class ServiceResponseException extends \RuntimeException {
+
+    /**
+     * @var Response Response
+     */
+    protected $response;
+
+    /**
+     * @var RequestInterface Request
+     */
+    protected $request;
+
+    /**
+     * @var string Request ID
+     */
+    protected $requestId;
+
+    /**
+     * @var string Exception type (client / server)
+     */
+    protected $exceptionType;
+
+    /**
+     * @var string Exception code
+     */
+    protected $exceptionCode;
+
+    /**
+     * Set the exception code
+     *
+     * @param string $code Exception code
+     */
+    public function setExceptionCode($code) {
+        $this->exceptionCode = $code;
+    }
+
+    /**
+     * Get the exception code
+     *
+     * @return string|null
+     */
+    public function getExceptionCode() {
+        return $this->exceptionCode;
+    }
+
+    /**
+     * Set the exception type
+     *
+     * @param string $type Exception type
+     */
+    public function setExceptionType($type) {
+        $this->exceptionType = $type;
+    }
+
+    /**
+     * Get the exception type (one of client or server)
+     *
+     * @return string|null
+     */
+    public function getExceptionType() {
+        return $this->exceptionType;
+    }
+
+    /**
+     * Set the request ID
+     *
+     * @param string $id Request ID
+     */
+    public function setRequestId($id) {
+        $this->requestId = $id;
+    }
+
+    /**
+     * Get the Request ID
+     *
+     * @return string|null
+     */
+    public function getRequestId() {
+        return $this->requestId;
+    }
+
+    /**
+     * Set the associated response
+     *
+     * @param Response $response Response
+     */
+    public function setResponse(Response $response) {
+        $this->response = $response;
+    }
+
+    /**
+     * Get the associated response object
+     *
+     * @return Response|null
+     */
+    public function getResponse() {
+        return $this->response;
+    }
+
+    /**
+     * Set the associated request
+     *
+     * @param RequestInterface $request
+     */
+    public function setRequest(RequestInterface $request) {
+        $this->request = $request;
+    }
+
+    /**
+     * Get the associated request object
+     *
+     * @return RequestInterface|null
+     */
+    public function getRequest() {
+        return $this->request;
+    }
+
+    /**
+     * Get the status code of the response
+     *
+     * @return int|null
+     */
+    public function getStatusCode() {
+        return $this->response ? $this->response->getStatusCode() : null;
+    }
+
+    /**
+     * Cast to a string
+     *
+     * @return string
+     */
+    public function __toString() {
+        $message = get_class($this) . ': '
+            . 'Cos Error Code: ' . $this->getExceptionCode() . ', '
+            . 'Status Code: ' . $this->getStatusCode() . ', '
+            . 'Cos Request ID: ' . $this->getRequestId() . ', '
+            . 'Cos Error Type: ' . $this->getExceptionType() . ', '
+            . 'Cos Error Message: ' . $this->getMessage();
+
+        // Add the User-Agent if available
+        if ($this->request) {
+            $message .= ', ' . 'User-Agent: ' . $this->request->getHeader('User-Agent');
+        }
+
+        return $message;
+    }
+
+    /**
+     * Get the request ID of the error. This value is only present if a
+     * response was received, and is not present in the event of a networking
+     * error.
+     *
+     * Same as `getRequestId()` method, but matches the interface for SDKv3.
+     *
+     * @return string|null Returns null if no response was received
+     */
+    public function getCosRequestId() {
+        return $this->requestId;
+    }
+
+    /**
+     * Get the Cos error type.
+     *
+     * Same as `getExceptionType()` method, but matches the interface for SDKv3.
+     *
+     * @return string|null Returns null if no response was received
+     */
+    public function getCosErrorType() {
+        return $this->exceptionType;
+    }
+
+    /**
+     * Get the Cos error code.
+     *
+     * Same as `getExceptionCode()` method, but matches the interface for SDKv3.
+     *
+     * @return string|null Returns null if no response was received
+     */
+    public function getCosErrorCode() {
+        return $this->exceptionCode;
+    }
+}
diff --git a/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/ExceptionListener.php b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/ExceptionListener.php
new file mode 100755
index 0000000..a7c0b69
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/ExceptionListener.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace Qcloud\Cos;
+
+use Qcloud\Cos\Exception\ServiceResponseException;
+use Qcloud\Cos\Exception\NoSuchKeyException;
+
+use Guzzle\Common\Event;
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\Response;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Converts generic Guzzle response exceptions into cos specific exceptions
+ */
+class ExceptionListener implements EventSubscriberInterface {
+    protected $parser;
+    protected $defaultException;
+
+    public function __construct() {
+        $this->parser = new ExceptionParser();
+        $this->defaultException = 'Qcloud\Cos\Exception\ServiceResponseException';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public static function getSubscribedEvents() {
+        return array('request.error' => array('onRequestError', -1));
+    }
+
+    /**
+     * Throws a more meaningful request exception if available
+     *
+     * @param Event $event Event emitted
+     */
+    public function onRequestError(Event $event) {
+        $e = $this->fromResponse($event['request'], $event['response']);
+        $event->stopPropagation();
+        throw $e;
+    }
+
+    public function fromResponse(RequestInterface $request, Response $response) {
+        $parts = $this->parser->parse($request, $response);
+
+        $className = 'Qcloud\\Cos\\Exception\\' . $parts['code'];
+        if (substr($className, -9) !== 'Exception') {
+            $className .= 'Exception';
+        }
+
+        $className = class_exists($className) ? $className : $this->defaultException;
+
+        return $this->createException($className, $request, $response, $parts);
+    }
+
+    protected function createException($className, RequestInterface $request, Response $response, array $parts) {
+        $class = new $className($parts['message']);
+
+        if ($class instanceof ServiceResponseException) {
+            $class->setExceptionCode($parts['code']);
+            $class->setExceptionType($parts['type']);
+            $class->setResponse($response);
+            $class->setRequest($request);
+            $class->setRequestId($parts['request_id']);
+        }
+
+        return $class;
+    }
+}
diff --git a/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/ExceptionParser.php b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/ExceptionParser.php
new file mode 100755
index 0000000..be65eed
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/ExceptionParser.php
@@ -0,0 +1,109 @@
+<?php
+
+namespace Qcloud\Cos;
+
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\Response;
+
+/**
+ * Parses default XML exception responses
+ */
+class ExceptionParser {
+
+    public function parse(RequestInterface $request, Response $response) {
+        $data = array(
+            'code'       => null,
+            'message'    => null,
+            'type'       => $response->isClientError() ? 'client' : 'server',
+            'request_id' => null,
+            'parsed'     => null
+        );
+
+        $body = $response->getBody(true);
+
+        if (!$body) {
+            $this->parseHeaders($request, $response, $data);
+            return $data;
+        }
+
+        try {
+            $xml = new \SimpleXMLElement(utf8_encode($body));
+            $this->parseBody($xml, $data);
+            return $data;
+        } catch (\Exception $e) {
+            $data['code'] = 'PhpInternalXmlParseError';
+            $data['message'] = 'A non-XML response was received';
+            return $data;
+        }
+    }
+
+    /**
+     * Parses additional exception information from the response headers
+     *
+     * @param RequestInterface $request  Request that was issued
+     * @param Response         $response The response from the request
+     * @param array            $data     The current set of exception data
+     */
+    protected function parseHeaders(RequestInterface $request, Response $response, array &$data) {
+        $data['message'] = $response->getStatusCode() . ' ' . $response->getReasonPhrase();
+        if ($requestId = $response->getHeader('x-cos-request-id')) {
+            $data['request_id'] = $requestId;
+            $data['message'] .= " (Request-ID: $requestId)";
+        }
+
+        // Get the request
+        $status  = $response->getStatusCode();
+        $method  = $request->getMethod();
+
+        // Attempt to determine code for 403s and 404s
+        if ($status === 403) {
+            $data['code'] = 'AccessDenied';
+        } elseif ($method === 'HEAD' && $status === 404) {
+            $path   = explode('/', trim($request->getPath(), '/'));
+            $host   = explode('.', $request->getHost());
+            $bucket = (count($host) >= 4) ? $host[0] : array_shift($path);
+            $object = array_shift($path);
+
+            if ($bucket && $object) {
+                $data['code'] = 'NoSuchKey';
+            } elseif ($bucket) {
+                $data['code'] = 'NoSuchBucket';
+            }
+        }
+    }
+
+    /**
+     * Parses additional exception information from the response body
+     *
+     * @param \SimpleXMLElement $body The response body as XML
+     * @param array             $data The current set of exception data
+     */
+    protected function parseBody(\SimpleXMLElement $body, array &$data) {
+        $data['parsed'] = $body;
+
+        $namespaces = $body->getDocNamespaces();
+        if (isset($namespaces[''])) {
+            // Account for the default namespace being defined and PHP not being able to handle it :(
+            $body->registerXPathNamespace('ns', $namespaces['']);
+            $prefix = 'ns:';
+        } else {
+            $prefix = '';
+        }
+
+        if ($tempXml = $body->xpath("//{$prefix}Code[1]")) {
+            $data['code'] = (string) $tempXml[0];
+        }
+
+        if ($tempXml = $body->xpath("//{$prefix}Message[1]")) {
+            $data['message'] = (string) $tempXml[0];
+        }
+
+        $tempXml = $body->xpath("//{$prefix}RequestId[1]");
+        if (empty($tempXml)) {
+            $tempXml = $body->xpath("//{$prefix}RequestID[1]");
+        }
+        if (isset($tempXml[0])) {
+            $data['request_id'] = (string) $tempXml[0];
+        }
+    }
+}
diff --git a/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Md5Listener.php b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Md5Listener.php
new file mode 100755
index 0000000..9595600
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Md5Listener.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace Qcloud\Cos;
+
+use Guzzle\Common\Event;
+use Guzzle\Service\Command\CommandInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Adds required and optional Content-MD5 headers
+ */
+class Md5Listener implements EventSubscriberInterface
+{
+    /** @var S3SignatureInterface */
+    private $signature;
+
+    public static function getSubscribedEvents()
+    {
+        return array('command.after_prepare' => 'onCommandAfterPrepare');
+    }
+
+    public function __construct(Signature $signature)
+    {
+        $this->signature = $signature;
+    }
+
+    public function onCommandAfterPrepare(Event $event)
+    {
+        $command = $event['command'];
+        $operation = $command->getOperation();
+
+        if ($operation->getData('contentMd5')) {
+            // Add the MD5 if it is required for all signers
+            $this->addMd5($command);
+        } elseif ($operation->hasParam('ContentMD5')) {
+            $value = $command['ContentMD5'];
+            // Add a computed MD5 if the parameter is set to true or if
+            // not using Signature V4 and the value is not set (null).
+            if ($value === true ||
+                ($value === null && !($this->signature instanceof SignatureV4))
+            ) {
+                $this->addMd5($command);
+            }
+        }
+    }
+
+    private function addMd5(CommandInterface $command)
+    {
+        $request = $command->getRequest();
+        $body = $request->getBody();
+        if ($body && $body->getSize() > 0) {
+            if (false !== ($md5 = $body->getContentMd5(true, true))) {
+                $request->setHeader('Content-MD5', $md5);
+            }
+        }
+    }
+}
diff --git a/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/MultipartUpload.php b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/MultipartUpload.php
new file mode 100755
index 0000000..30a942d
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/MultipartUpload.php
@@ -0,0 +1,132 @@
+<?php
+
+namespace Qcloud\Cos;
+
+use Guzzle\Http\ReadLimitEntityBody;
+use Qcloud\Cos\Exception\CosException;
+class MultipartUpload {
+    /**
+     * const var: part size from 5MB to 5GB, and max parts of 10000 are allowed for each upload.
+     */
+    const MIN_PART_SIZE = 5242880;
+    const MAX_PART_SIZE = 5368709120;
+    const MAX_PARTS     = 10000;
+
+    private $client;
+    private $source;
+    private $options;
+    private $partSize;
+
+    public function __construct($client, $source, $minPartSize, $options = array()) {
+        $this->client = $client;
+        $this->source = $source;
+        $this->options = $options;
+        $this->partSize = $this->calculatePartSize($minPartSize);
+    }
+
+    public function performUploading() {
+        $uploadId = $this->initiateMultipartUpload();
+
+        $partNumber = 1;
+        $parts = array();
+        for (;;) {
+            if ($this->source->isConsumed()) {
+                break;
+            }
+
+            $body = new ReadLimitEntityBody($this->source, $this->partSize, $this->source->ftell());
+            if ($body->getContentLength() == 0) {
+                break;
+            }
+            $result = $this->client->uploadPart(array(
+                        'Bucket' => $this->options['Bucket'],
+                        'Key' => $this->options['Key'],
+                        'Body' => $body,
+                        'UploadId' => $uploadId,
+                        'PartNumber' => $partNumber));
+            if (md5($body) != substr($result['ETag'], 1, -1)){
+                throw new CosException("ETag check inconsistency");
+            }
+            $part = array('PartNumber' => $partNumber, 'ETag' => $result['ETag']);
+            array_push($parts, $part);
+            ++$partNumber;
+        }
+        try {
+            $rt = $this->client->completeMultipartUpload(array(
+                'Bucket' => $this->options['Bucket'],
+                'Key' => $this->options['Key'],
+                'UploadId' => $uploadId,
+                'Parts' => $parts));
+        } catch(\Exception $e){
+            throw $e;
+        }
+        return $rt;
+    }
+
+    public function resumeUploading() {
+        $uploadId = $this->options['UploadId'];
+        $rt = $this->client->ListParts(
+            array('UploadId' => $uploadId,
+                'Bucket'=>$this->options['Bucket'],
+                'Key'=>$this->options['Key']));
+                $parts = array();
+        $offset = $this->partSize;
+        if (count($rt['Parts']) > 0) {
+            foreach ($rt['Parts'] as $part) {
+                $parts[$part['PartNumber'] - 1] = array('PartNumber' => $part['PartNumber'], 'ETag' => $part['ETag']);
+            }
+        }
+        for ($partNumber = 1;;++$partNumber,$offset+=$body->getContentLength()) {
+            if ($this->source->isConsumed()) {
+                break;
+            }
+
+            $body = new ReadLimitEntityBody($this->source, $this->partSize, $this->source->ftell());
+            if ($body->getContentLength() == 0) {
+                break;
+            }
+
+
+            if (array_key_exists($partNumber-1,$parts)){
+
+                if (md5($body) != substr($parts[$partNumber-1]['ETag'], 1, -1)){
+                    throw new CosException("ETag check inconsistency");
+                }
+                $body->setOffset($offset);
+                continue;
+            }
+
+            $result = $this->client->uploadPart(array(
+                'Bucket' => $this->options['Bucket'],
+                'Key' => $this->options['Key'],
+                'Body' => $body,
+                'UploadId' => $uploadId,
+                'PartNumber' => $partNumber));
+            if (md5($body) != substr($result['ETag'], 1, -1)){
+                throw new CosException("ETag check inconsistency");
+            }
+            $parts[$partNumber-1] = array('PartNumber' => $partNumber, 'ETag' => $result['ETag']);
+
+        }
+        $rt = $this->client->completeMultipartUpload(array(
+            'Bucket' => $this->options['Bucket'],
+            'Key' => $this->options['Key'],
+            'UploadId' => $uploadId,
+            'Parts' => $parts));
+        return $rt;
+    }
+
+    private function calculatePartSize($minPartSize) {
+        $partSize = intval(ceil(($this->source->getContentLength() / self::MAX_PARTS)));
+        $partSize = max($minPartSize, $partSize);
+        $partSize = min($partSize, self::MAX_PART_SIZE);
+        $partSize = max($partSize, self::MIN_PART_SIZE);
+
+        return $partSize;
+    }
+
+    private function initiateMultipartUpload() {
+        $result = $this->client->createMultipartUpload($this->options);
+        return $result['UploadId'];
+    }
+}
diff --git a/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Service.php b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Service.php
new file mode 100755
index 0000000..75037c6
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Service.php
@@ -0,0 +1,4360 @@
+<?php
+
+namespace Qcloud\Cos;
+
+// http://guzzle3.readthedocs.io/webservice-client/guzzle-service-descriptions.html
+class Service {
+    public static function getService() {
+        return array(
+            'name' => 'Cos Service',
+            'apiVersion' => 'V5',
+            'description' => 'Cos V5 API Service',
+
+            'operations' => array(
+                /**
+                舍弃一个分块上传且删除已上传的分片块的方法.
+
+                COS 支持舍弃一个分块上传且删除已上传的分片块. 注意,已上传但是未终止的分片块会占用存储空间进 而产生存储费用.因此,建议及时完成分块上传 或者舍弃分块上传.
+
+                关于分块上传的具体描述,请查看 https://cloud.tencent.com/document/product/436/14112.
+
+                关于舍弃一个分块上传且删除已上传的分片块接口的描述,请查看 https://cloud.tencent.com/document/product/436/7740.
+
+                cos php SDK 中舍弃一个分块上传且删除已上传的分片块请求的方法具体步骤如下:
+
+                1. 初始化客户端cosClient,填入存储桶名,和一些额外需要的参数,如授权的具体信息等。
+
+                2. 调用 AbortMultipfartUpload 接口发出请求。
+
+                3. 接收该接口的返回数据,若没有抛出异常,则操作成功。
+                 */
+                'AbortMultipartUpload' => array(
+                    'httpMethod' => 'DELETE',
+                    'uri' => '/{Bucket}{/Key*}',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'AbortMultipartUploadOutput',
+                    'responseType' => 'model',
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri'),
+                        'Key' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                            'minLength' => 1,
+                            'filters' => array(
+                                'Qcloud\\Cos\\Client::explodeKey')),
+                        'UploadId' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'uploadId')),
+                    'errorResponses' => array(
+                        array(
+                            'reason' => 'The specified multipart upload does not exist.',
+                            'class' => 'NoSuchUploadException'))),
+                /**
+                创建存储桶(Bucket)的方法.
+
+                在开始使用 COS 时,需要在指定的账号下先创建一个 Bucket 以便于对象的使用和管理. 并指定 Bucket 所属的地域.创建 Bucket 的用户默认成为 Bucket 的持有者.若创建 Bucket 时没有指定访问权限,则默认 为私有读写(private)权限.
+
+                可用地域,可以查看https://cloud.tencent.com/document/product/436/6224.
+
+                关于创建 Bucket 描述,请查看 https://cloud.tencent.com/document/product/436/14106.
+
+                关于创建存储桶(Bucket)接口的具体 描述,请查看 https://cloud.tencent.com/document/product/436/7738.
+
+                cos php SDK 中创建 Bucket的方法具体步骤如下:
+
+                1. 初始化客户端cosClient,填入存储桶名,和一些额外需要的参数,如授权的具体信息等。
+
+                2. 调用 CreateBucket 接口发出请求。
+
+                3. 接收该接口的返回数据,若没有抛出异常,则创建成功。
+
+                示例:
+                $result = $cosClient->createBucket(array('Bucket' => 'testbucket-1252448703'));
+                 */
+                'CreateBucket' => array(
+                    'httpMethod' => 'PUT',
+                    'uri' => '/{Bucket}',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'CreateBucketOutput',
+                    'responseType' => 'model',
+                    'data' => array(
+                        'xmlRoot' => array(
+                            'name' => 'CreateBucketConfiguration')),
+                    'parameters' => array(
+                        'ACL' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-acl'),
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri')),
+                    'errorResponses' => array(
+                        array(
+                            'reason' => 'The requested bucket name is not available. The bucket namespace is shared by all users of the system. Please select a different name and try again.',
+                            'class' => 'BucketAlreadyExistsException'))),
+                /**
+                完成整个分块上传的方法.
+
+                当使用分块上传(uploadPart(UploadPartRequest))完对象的所有块以后,必须调用该 completeMultiUpload(CompleteMultiUploadRequest) 或者 completeMultiUploadAsync(CompleteMultiUploadRequest, CosXmlResultListener) 来完成整个文件的分块上传.且在该请求的 Body 中需要给出每一个块的 PartNumber 和 ETag,用来校验块的准 确性.
+
+                分块上传适合于在弱网络或高带宽环境下上传较大的对象.SDK 支持自行切分对象并分别调用uploadPart(UploadPartRequest)上传各 个分块.
+
+                关于分块上传的描述,请查看 https://cloud.tencent.com/document/product/436/14112.
+
+                关于完成整个分片上传接口的描述,请查看 https://cloud.tencent.com/document/product/436/7742.
+
+                1. 初始化客户端cosClient,填入存储桶名,和一些额外需要的参数,如授权的具体信息等。
+
+                2. 调用 CompleteMultipartUpload 接口发出请求。
+
+                3. 接收该接口的返回数据,若没有抛出异常,则操作成功。
+
+                 */
+                'CompleteMultipartUpload' => array(
+                    'httpMethod' => 'POST',
+                    'uri' => '/{Bucket}{/Key*}',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'CompleteMultipartUploadOutput',
+                    'responseType' => 'model',
+                    'data' => array(
+                        'xmlRoot' => array(
+                            'name' => 'CompleteMultipartUpload')),
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri'),
+                        'Key' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                            'minLength' => 1,
+                            'filters' => array(
+                                'Qcloud\\Cos\\Client::explodeKey')),
+                        'Parts' => array(
+                            'type' => 'array',
+                            'location' => 'xml',
+                            'data' => array(
+                                'xmlFlattened' => true),
+                            'items' => array(
+                                'name' => 'CompletedPart',
+                                'type' => 'object',
+                                'sentAs' => 'Part',
+                                'properties' => array(
+                                    'ETag' => array(
+                                        'type' => 'string'),
+                                    'PartNumber' => array(
+                                        'type' => 'numeric')))),
+                        'UploadId' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'uploadId'),
+                        'command.expects' => array(
+                            'static' => true,
+                            'default' => 'application/xml'))),
+                'CreateMultipartUpload' => array(
+                    'httpMethod' => 'POST',
+                    'uri' => '/{Bucket}{/Key*}?uploads',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'CreateMultipartUploadOutput',
+                    'responseType' => 'model',
+                    'data' => array(
+                        'xmlRoot' => array(
+                            'name' => 'CreateMultipartUploadRequest')),
+                    'parameters' => array(
+                        'ACL' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-acl',
+                        ),
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                        ),
+                        'CacheControl' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Cache-Control',
+                        ),
+                        'ContentDisposition' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Content-Disposition',
+                        ),
+                        'ContentEncoding' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Content-Encoding',
+                        ),
+                        'ContentLanguage' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Content-Language',
+                        ),
+                        'ContentType' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Content-Type',
+                        ),
+                        'Expires' => array(
+                            'type' => array(
+                                'object',
+                                'string',
+                                'integer',
+                            ),
+                            'format' => 'date-time-http',
+                            'location' => 'header',
+                        ),
+                        'GrantFullControl' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-grant-full-control',
+                        ),
+                        'GrantRead' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-grant-read',
+                        ),
+                        'GrantReadACP' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-grant-read-acp',
+                        ),
+                        'GrantWriteACP' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-grant-write-acp',
+                        ),
+                        'Key' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                            'minLength' => 1,
+                            'filters' => array(
+                                'Qcloud\\Cos\\Client::explodeKey')
+                        ),
+                        'Metadata' => array(
+                            'type' => 'object',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-meta-',
+                            'additionalProperties' => array(
+                                'type' => 'string',
+                            ),
+                        ),
+                        'ServerSideEncryption' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption',
+                        ),
+                        'StorageClass' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-storage-class',
+                        ),
+                        'WebsiteRedirectLocation' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-website-redirect-location',
+                        ),
+                        'SSECustomerAlgorithm' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+                        ),
+                        'SSECustomerKey' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-key',
+                        ),
+                        'SSECustomerKeyMD5' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-key-MD5',
+                        ),
+                        'SSEKMSKeyId' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-aws-kms-key-id',
+                        ),
+                        'RequestPayer' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-payer',
+                        ),
+                        'ACP' => array(
+                            'type' => 'object',
+                            'additionalProperties' => true,
+                        ),
+                        'command.expects' => array(
+                            'static' => true,
+                            'default' => 'application/xml',
+                        ))),
+                'CopyObject' => array(
+                    'httpMethod' => 'PUT',
+                    'uri' => '/{Bucket}{/Key*}',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'CopyObjectOutput',
+                    'responseType' => 'model',
+                    'data' => array(
+                        'xmlRoot' => array(
+                            'name' => 'CopyObjectRequest',
+                        ),
+                    ),
+                    'parameters' => array(
+                        'ACL' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-acl',
+                        ),
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                        ),
+                        'CacheControl' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Cache-Control',
+                        ),
+                        'ContentDisposition' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Content-Disposition',
+                        ),
+                        'ContentEncoding' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Content-Encoding',
+                        ),
+                        'ContentLanguage' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Content-Language',
+                        ),
+                        'ContentType' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Content-Type',
+                        ),
+                        'CopySource' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-copy-source',
+                        ),
+                        'CopySourceIfMatch' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-copy-source-if-match',
+                        ),
+                        'CopySourceIfModifiedSince' => array(
+                            'type' => array(
+                                'object',
+                                'string',
+                                'integer',
+                            ),
+                            'format' => 'date-time-http',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-copy-source-if-modified-since',
+                        ),
+                        'CopySourceIfNoneMatch' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-copy-source-if-none-match',
+                        ),
+                        'CopySourceIfUnmodifiedSince' => array(
+                            'type' => array(
+                                'object',
+                                'string',
+                                'integer',
+                            ),
+                            'format' => 'date-time-http',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-copy-source-if-unmodified-since',
+                        ),
+                        'Expires' => array(
+                            'type' => array(
+                                'object',
+                                'string',
+                                'integer',
+                            ),
+                            'format' => 'date-time-http',
+                            'location' => 'header',
+                        ),
+                        'GrantFullControl' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-grant-full-control',
+                        ),
+                        'GrantRead' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-grant-read',
+                        ),
+                        'GrantReadACP' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-grant-read-acp',
+                        ),
+                        'GrantWriteACP' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-grant-write-acp',
+                        ),
+                        'Key' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                            'minLength' => 1,
+                            'filters' => array(
+                                'Qcloud\\Cos\\Client::explodeKey')
+                        ),
+                        'Metadata' => array(
+                            'type' => 'object',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-meta-',
+                            'additionalProperties' => array(
+                                'type' => 'string',
+                            ),
+                        ),
+                        'MetadataDirective' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-metadata-directive',
+                        ),
+                        'ServerSideEncryption' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption',
+                        ),
+                        'StorageClass' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-storage-class',
+                        ),
+                        'WebsiteRedirectLocation' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-website-redirect-location',
+                        ),
+                        'SSECustomerAlgorithm' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+                        ),
+                        'SSECustomerKey' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-key',
+                        ),
+                        'CopySourceSSECustomerAlgorithm' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-copy-source-server-side-encryption-customer-algorithm',
+                        ),
+                        'CopySourceSSECustomerKey' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-copy-source-server-side-encryption-customer-key',
+                        ),
+                        'CopySourceSSECustomerKeyMD5' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-copy-source-server-side-encryption-customer-key-MD5',
+                        ),
+                        'RequestPayer' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-payer',
+                        ),
+                        'ACP' => array(
+                            'type' => 'object',
+                            'additionalProperties' => true,
+                        ),
+                        'command.expects' => array(
+                            'static' => true,
+                            'default' => 'application/xml',
+                        ),
+                    ),
+                    'errorResponses' => array(
+                        array(
+                            'reason' => 'The source object of the COPY operation is not in the active tier.',
+                            'class' => 'ObjectNotInActiveTierErrorException',
+                        ),
+                    ),
+                ),
+                /**
+                删除存储桶 (Bucket)的方法.
+
+                COS 目前仅支持删除已经清空的 Bucket,如果 Bucket 中仍有对象,将会删除失败. 因此,在执行删除 Bucket 前,需确保 Bucket 内已经没有对象. 删除 Bucket 时,还需要确保操作的身份已被授权该操作,并确认 传入了正确的存储桶名称和地域参数, 请参阅 putBucket(PutBucketRequest).
+
+                关于删除 Bucket 的描述,请查看 https://cloud.tencent.com/document/product/436/14105.
+
+                关于删除 Bucket 接口的具体描述,请查看https://cloud.tencent.com/document/product/436/7732.
+
+                cos php SDK 中删除 Bucket 的方法具体步骤如下:
+
+                1. 初始化客户端cosClient,填入存储桶名,和一些额外需要的参数,如授权的具体信息等。
+
+                2. 调用 DeleteBucket 接口发出请求。
+
+                3. 接收该接口的返回数据,若没有抛出异常,删除成功。
+
+                示例:
+                $result = $cosClient->deleteBucket(array(
+                'Bucket' => 'testbucket-1252448703'));
+                print_r($result);
+                 */
+                'DeleteBucket' => array(
+                    'httpMethod' => 'DELETE',
+                    'uri' => '/{Bucket}',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'DeleteBucketOutput',
+                    'responseType' => 'model',
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri'))),
+                /**
+                删除跨域访问配置信息的方法.
+
+                若是 Bucket 不需要支持跨域访问配置,可以调用此接口删除已配置的跨域访问信息. 跨域访问配置可以通过 putBucketCORS(PutBucketCORSRequest) 或者 putBucketCORSAsync(PutBucketCORSRequest, CosXmlResultListener) 方法来开启 Bucket 的跨域访问 支持.
+
+                关于删除跨域访问配置信息接口的具体描述,请查看https://cloud.tencent.com/document/product/436/8283.
+
+                cos php SDK 中删除跨域访问配置信息的方法具体步骤如下:
+
+                1. 初始化客户端cosClient,填入存储桶名,和一些额外需要的参数,如授权的具体信息等。
+
+                2. 调用 DeleteBucketCORS 接口发出请求。
+
+                3. 接收该接口的返回数据,若没有抛出异常,删除成功。
+
+                示例:
+                $result = $cosClient->deleteBucketCors(array(
+                // Bucket is required
+                'Bucket' => 'testbucket-1252448703',
+                ));
+                 */
+                'DeleteBucketCors' => array(
+                    'httpMethod' => 'DELETE',
+                    'uri' => '/{Bucket}?cors',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'DeleteBucketCorsOutput',
+                    'responseType' => 'model',
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                        ),
+                    ),
+                ),
+                /**
+                删除 COS 上单个对象的方法.
+
+                COS 支持直接删除一个或多个对象,当仅需要删除一个对象时,只需要提供对象的名称(即对象键)即可.
+
+                关于删除 COS 上单个对象的具体描述,请查看 https://cloud.tencent.com/document/product/436/14119.
+
+                关于删除 COS 上单个对象接口的具体描述,请查看 https://cloud.tencent.com/document/product/436/7743.
+
+                cos php SDK 中删除 COS 上单个对象请求的方法具体步骤如下:
+
+                1. 初始化客户端cosClient,填入存储桶名,和一些额外需要的参数,如授权的具体信息等。
+
+                2. 调用 DeleteObject 接口发出请求。
+
+                3. 接收该接口的返回数据,若没有抛出异常,则删除成功。
+
+                示例:
+                $result = $cosClient->deleteObject(array(
+                'Bucket' => 'testbucket-1252448703',
+                'Key' => '111.txt',
+                'VersionId' => 'string'));
+                 */
+                'DeleteObject' => array(
+                    'httpMethod' => 'DELETE',
+                    'uri' => '/{Bucket}{/Key*}',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'DeleteObjectOutput',
+                    'responseType' => 'model',
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri'),
+                        'Key' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                            'minLength' => 1,
+                            'filters' => array(
+                                'Qcloud\\Cos\\Client::explodeKey')),
+                        'MFA' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-mfa',
+                        ),
+                        'VersionId' => array(
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'versionId',
+                        ),
+                        'RequestPayer' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-payer',
+                        ),)),
+                /**
+                批量删除 COS 对象的方法.
+
+                COS 支持批量删除指定 Bucket 中 对象,单次请求最大支持批量删除 1000 个 对象. 请求中删除一个不存在的对象,仍然认为是成功的. 对于响应结果,COS提供 Verbose 和 Quiet 两种模式:Verbose 模式将返回每个对象的删除结果;Quiet 模式只返回删除报错的对象信息. 请求必须携带 Content-MD5 用来校验请求Body 的完整性.
+
+                关于批量删除 COS 对象接口的描述,请查看https://cloud.tencent.com/document/product/436/8289.
+
+                cos php SDK 中批量删除 COS 对象的方法具体步骤如下:
+
+                1. 初始化客户端cosClient,填入存储桶名,和一些额外需要的参数,如授权的具体信息等。
+
+                2. 调用 DeleteObjects 接口发出请求。
+
+                3. 接收该接口的返回数据,若没有抛出异常,则删除成功。
+
+                示例:
+                $result = $cosClient->deleteObjects(array(
+                // Bucket is required
+                'Bucket' => 'testbucket-1252448703',
+                // Objects is required
+                'Objects' => array(
+                array(
+                // Key is required
+                'Key' => 'string',
+                'VersionId' => 'string',
+                ),
+                // ... repeated
+                ),
+                ));
+                 */
+                'DeleteObjects' => array(
+                    'httpMethod' => 'POST',
+                    'uri' => '/{Bucket}?delete',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'DeleteObjectsOutput',
+                    'responseType' => 'model',
+                    'data' => array(
+                        'xmlRoot' => array(
+                            'name' => 'Delete',
+                        ),
+                        'contentMd5' => true,
+                    ),
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                        ),
+                        'Objects' => array(
+                            'required' => true,
+                            'type' => 'array',
+                            'location' => 'xml',
+                            'data' => array(
+                                'xmlFlattened' => true,
+                            ),
+                            'items' => array(
+                                'name' => 'ObjectIdentifier',
+                                'type' => 'object',
+                                'sentAs' => 'Object',
+                                'properties' => array(
+                                    'Key' => array(
+                                        'required' => true,
+                                        'type' => 'string',
+                                        'minLength' => 1,
+                                    ),
+                                    'VersionId' => array(
+                                        'type' => 'string',
+                                    ),
+                                ),
+                            ),
+                        ),
+                        'Quiet' => array(
+                            'type' => 'boolean',
+                            'format' => 'boolean-string',
+                            'location' => 'xml',
+                        ),
+                        'MFA' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-mfa',
+                        ),
+                        'RequestPayer' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-payer',
+                        ),
+                        'command.expects' => array(
+                            'static' => true,
+                            'default' => 'application/xml',
+                        ),
+                    ),
+                ),
+                /**
+                删除存储桶(Bucket) 的生命周期配置的方法.
+
+                COS 支持删除已配置的 Bucket 的生命周期列表. COS 支持以生命周期配置的方式来管理 Bucket 中 对象的生命周期,生命周期配置包含一个或多个将 应用于一组对象规则的规则集 (其中每个规则为 COS 定义一个操作),请参阅 putBucketLifecycle(PutBucketLifecycleRequest).
+
+                关于删除 Bucket 的生命周期配置接口的具体描述,请查看https://cloud.tencent.com/document/product/436/8284.
+
+                cos php SDK 中删除 Bucket 的生命周期配置的方法具体步骤如下:
+
+                1. 初始化客户端cosClient,填入存储桶名,和一些额外需要的参数,如授权的具体信息等。
+
+                2. 调用 DeleteBucketLifeCycle 接口发出请求。
+
+                3. 接收该接口的返回数据,若没有抛出异常,删除成功。
+
+                示例:
+                $result = $cosClient->deleteBucketLifecycle(array(
+                // Bucket is required
+                'Bucket' =>'testbucket-1252448703',
+                ));
+                 */
+                'DeleteBucketLifecycle' => array(
+                    'httpMethod' => 'DELETE',
+                    'uri' => '/{Bucket}?lifecycle',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'DeleteBucketLifecycleOutput',
+                    'responseType' => 'model',
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                        ),
+                    ),
+                ),
+                /**
+                删除跨区域复制配置的方法.
+
+                当不需要进行跨区域复制时,可以删除 Bucket 的跨区域复制配置. 跨区域复制,可以查阅putBucketReplication(PutBucketReplicationRequest)
+
+                cos php SDK 中删除跨区域复制配置的方法具体步骤如下:
+
+                1. 初始化客户端cosClient,填入存储桶名,和一些额外需要的参数,如授权的具体信息等。
+
+                2. 调用 DeleteBucketReplication 接口发出请求。
+
+                3. 接收该接口的返回数据,若没有抛出异常,获取成功。
+
+                 */
+                'DeleteBucketReplication' => array(
+                    'httpMethod' => 'DELETE',
+                    'uri' => '/{Bucket}?replication',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'DeleteBucketReplicationOutput',
+                    'responseType' => 'model',
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                        ),
+                    ),
+                ),
+                'GetObject' => array(
+                    'httpMethod' => 'GET',
+                    'uri' => '/{Bucket}{/Key*}',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'GetObjectOutput',
+                    'responseType' => 'model',
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri'),
+                        'IfMatch' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'If-Match'),
+                        'IfModifiedSince' => array(
+                            'type' => array(
+                                'object',
+                                'string',
+                                'integer'),
+                            'format' => 'date-time-http',
+                            'location' => 'header',
+                            'sentAs' => 'If-Modified-Since'),
+                        'IfNoneMatch' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'If-None-Match'),
+                        'IfUnmodifiedSince' => array(
+                            'type' => array(
+                                'object',
+                                'string',
+                                'integer'),
+                            'format' => 'date-time-http',
+                            'location' => 'header',
+                            'sentAs' => 'If-Unmodified-Since'),
+                        'Key' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                            'minLength' => 1,
+                            'filters' => array(
+                                'Qcloud\\Cos\\Client::explodeKey')),
+                        'Range' => array(
+                            'type' => 'string',
+                            'location' => 'header'),
+                        'ResponseCacheControl' => array(
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'response-cache-control'),
+                        'ResponseContentDisposition' => array(
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'response-content-disposition'),
+                        'ResponseContentEncoding' => array(
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'response-content-encoding'),
+                        'ResponseContentLanguage' => array(
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'response-content-language'),
+                        'ResponseContentType' => array(
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'response-content-type'),
+                        'ResponseExpires' => array(
+                            'type' => array(
+                                'object',
+                                'string',
+                                'integer'),
+                            'format' => 'date-time-http',
+                            'location' => 'query',
+                            'sentAs' => 'response-expires'),
+                        'VersionId' => array(
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'versionId',
+                        ),
+                        'SaveAs' => array(
+                            'location' => 'response_body')),
+                    'errorResponses' => array(
+                        array(
+                            'reason' => 'The specified key does not exist.',
+                            'class' => 'NoSuchKeyException'))),
+                /**
+                获取 COS 对象的访问权限信息(Access Control List, ACL)的方法.
+
+                Bucket 的持有者可获取该 Bucket 下的某个对象的 ACL 信息,如被授权者以及被授权的信息. ACL 权限包括读、写、读写权限.
+
+                关于获取 COS 对象的 ACL 接口的具体描述,请查看https://cloud.tencent.com/document/product/436/7744.
+
+                cos php SDK 中获取 COS 对象的 ACL 的方法具体步骤如下:
+
+                1. 初始化客户端cosClient,填入存储桶名,和一些额外需要的参数,如授权的具体信息等。
+
+                2. 调用 GetObjectAcl 接口发出请求。
+
+                3. 接收该接口的返回数据,若没有抛出异常,则获取成功。
+
+                示例:
+                $result = $cosClient->getObjectAcl(array(
+                'Bucket' => 'testbucket-1252448703',
+                'Key' => '11'));
+                 */
+                'GetObjectAcl' => array(
+                    'httpMethod' => 'GET',
+                    'uri' => '/{Bucket}{/Key*}?acl',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'GetObjectAclOutput',
+                    'responseType' => 'model',
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                        ),
+                        'Key' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                            'minLength' => 1,
+                            'filters' => array(
+                                'Qcloud\\Cos\\Client::explodeKey')
+                        ),
+                        'VersionId' => array(
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'versionId',
+                        ),
+                        'RequestPayer' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-payer',
+                        ),
+                        'command.expects' => array(
+                            'static' => true,
+                            'default' => 'application/xml',
+                        ),
+                    ),
+                    'errorResponses' => array(
+                        array(
+                            'reason' => 'The specified key does not exist.',
+                            'class' => 'NoSuchKeyException',
+                        ),
+                    ),
+                ),
+                /**
+                获取存储桶(Bucket) 的访问权限信息(Access Control List, ACL)的方法.
+
+                ACL 权限包括读、写、读写权限. COS 中 Bucket 是有访问权限控制的.可以通过获取 Bucket 的 ACL 表(putBucketACL(PutBucketACLRequest)),来查看那些用户拥有 Bucket 访 问权限.
+
+                关于获取 Bucket 的 ACL 接口的具体描述,请查看 https://cloud.tencent.com/document/product/436/7733.
+
+                cos php SDK 中获取 Bucket 的 ACL 的方法具体步骤如下:
+
+                1. 初始化客户端cosClient,填入存储桶名,和一些额外需要的参数,如授权的具体信息等。
+
+                2. 调用 GetBucketACL 接口发出请求。
+
+                3. 接收该接口的返回数据,若没有抛出异常,则获取成功。
+
+
+                示例:
+                $result = $cosClient->GetBucketAcl(array(
+                'Bucket' => 'testbucket-1252448703',));
+                 */
+                'GetBucketAcl' => array(
+                    'httpMethod' => 'GET',
+                    'uri' => '/{Bucket}?acl',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'GetBucketAclOutput',
+                    'responseType' => 'model',
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri'),
+                        'command.expects' => array(
+                            'static' => true,
+                            'default' => 'application/xml'))),
+                /**
+                查询存储桶(Bucket) 跨域访问配置信息的方法.
+
+                COS 支持查询当前 Bucket 跨域访问配置信息,以确定是否配置跨域信息.当跨域访问配置不存在时,请求 返回403 Forbidden. 跨域访问配置可以通过 putBucketCORS(PutBucketCORSRequest) 或者 putBucketCORSAsync(PutBucketCORSRequest, CosXmlResultListener) 方法来开启 Bucket 的跨域访问 支持.
+
+                关于查询 Bucket 跨域访问配置信息接口的具体描述, 请查看 https://cloud.tencent.com/document/product/436/8274.
+
+                cos php SDK 中查询 Bucket 跨域访问配置信息的方法具体步骤如下:
+
+                1. 初始化客户端cosClient,填入存储桶名,和一些额外需要的参数,如授权的具体信息等。
+
+                2. 调用 GetBucketCORS 接口发出请求。
+
+                3. 接收该接口的返回数据,若没有抛出异常,则获取成功。
+
+
+                示例:
+                $result = $cosClient->getBucketCors(array(
+                // Bucket is required
+                'Bucket' => 'testbucket-1252448703',
+                ));
+                 */
+                'GetBucketCors' => array(
+                    'httpMethod' => 'GET',
+                    'uri' => '/{Bucket}?cors',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'GetBucketCorsOutput',
+                    'responseType' => 'model',
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                        ),
+                        'command.expects' => array(
+                            'static' => true,
+                            'default' => 'application/xml',
+                        ),
+                    ),
+                ),
+                /**
+                查询存储桶(Bucket) 的生命周期配置的方法.
+
+                COS 支持以生命周期配置的方式来管理 Bucket 中对象的生命周期,生命周期配置包含一个或多个将 应用于一组对象规则的规则集 (其中每个规则为 COS 定义一个操作),请参阅 putBucketLifecycle(PutBucketLifecycleRequest).
+
+                关于查询 Bucket 的生命周期配置接口的具体描述,请查看https://cloud.tencent.com/document/product/436/8278.
+
+                cos php SDK 中查询 Bucket 的生命周期配置的方法具体步骤如下:
+
+                1. 初始化客户端cosClient,填入存储桶名,和一些额外需要的参数,如授权的具体信息等。
+
+                2. 调用 GetBucketLifecycle 接口发出请求。
+
+                3. 接收该接口的返回数据,若没有抛出异常,则获取成功。
+
+
+                示例:
+                $result = $cosClient->getBucketLifecycle(array(
+                'Bucket' => 'testbucket-1252448703',
+                ));
+                 */
+                'GetBucketLifecycle' => array(
+                    'httpMethod' => 'GET',
+                    'uri' => '/{Bucket}?lifecycle',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'GetBucketLifecycleOutput',
+                    'responseType' => 'model',
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                        ),
+                        'command.expects' => array(
+                            'static' => true,
+                            'default' => 'application/xml',
+                        ),
+                    ),
+                ),
+                /**
+                获取存储桶(Bucket)版本控制信息的方法.
+
+                通过查询版本控制信息,可以得知该 Bucket 的版本控制功能是处于禁用状态还是启用状态(Enabled 或者 Suspended), 开启版本控制功能,可参考putBucketVersioning(PutBucketVersioningRequest).
+
+                cos php SDK 中获取 Bucket 版本控制信息的方法具体步骤如下:
+
+                1. 初始化客户端cosClient,填入存储桶名,和一些额外需要的参数,如授权的具体信息等。
+
+                2. 调用 GetBucketVersioning 接口发出请求。
+
+                3. 接收该接口的返回数据,若没有抛出异常,获取成功。
+
+                示例:
+                $result = $cosClient->getBucketVersioning(
+                array('Bucket' => 'lewzylu02-1252448703'));
+                 */
+                'GetBucketVersioning' => array(
+                    'httpMethod' => 'GET',
+                    'uri' => '/{Bucket}?versioning',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'GetBucketVersioningOutput',
+                    'responseType' => 'model',
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                        ),
+                        'command.expects' => array(
+                            'static' => true,
+                            'default' => 'application/xml',
+                        ),
+                    ),
+                ),
+                /**
+                获取跨区域复制配置信息的方法.
+
+                跨区域复制是支持不同区域 Bucket 自动复制对象, 请查阅putBucketReplication(PutBucketReplicationRequest).
+
+                cos php SDK 中获取跨区域复制配置信息的方法具体步骤如下:
+
+                1. 初始化客户端cosClient,填入存储桶名,和一些额外需要的参数,如授权的具体信息等。
+
+                2. 调用 GetBucketReplication 接口发出请求。
+
+                3. 接收该接口的返回数据,若没有抛出异常,设置成功。
+
+                 */
+                'GetBucketReplication' => array(
+                    'httpMethod' => 'GET',
+                    'uri' => '/{Bucket}?replication',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'GetBucketReplicationOutput',
+                    'responseType' => 'model',
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                        ),
+                        'command.expects' => array(
+                            'static' => true,
+                            'default' => 'application/xml',
+                        ),
+                    ),
+                ),
+                /**
+                获取存储桶(Bucket) 所在的地域信息的方法.
+
+                在创建 Bucket 时,需要指定所属该 Bucket 所属地域信息.
+
+                COS 支持的地域信息,可查看https://cloud.tencent.com/document/product/436/6224.
+
+                关于获取 Bucket 所在的地域信息接口的具体描述,请查看https://cloud.tencent.com/document/product/436/8275.
+
+                cos php SDK 中获取 Bucket 所在的地域信息的方法具体步骤如下:
+
+                1. 初始化客户端cosClient,填入存储桶名,和一些额外需要的参数,如授权的具体信息等。
+
+                2. 调用 GetBucketLocation 接口发出请求。
+
+                3. 接收该接口的返回数据,若没有抛出异常,则获取成功。
+
+
+                示例:
+                $result = $cosClient->getBucketLocation(array(
+                'Bucket' => 'testbucket-1252448703',
+                ));
+                 */
+                'GetBucketLocation' => array(
+                    'httpMethod' => 'GET',
+                    'uri' => '/{Bucket}?location',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'GetBucketLocationOutput',
+                    'responseType' => 'model',
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                        ),
+                    ),
+                ),
+                'GetBucketNotification' => array(
+                    'httpMethod' => 'GET',
+                    'uri' => '/{Bucket}?notification',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'GetBucketNotificationOutput',
+                    'responseType' => 'model',
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                        ),
+                        'command.expects' => array(
+                            'static' => true,
+                            'default' => 'application/xml',
+                        ),
+                    ),
+                ),
+                'UploadPart' => array(
+                    'httpMethod' => 'PUT',
+                    'uri' => '/{Bucket}{/Key*}',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'UploadPartOutput',
+                    'responseType' => 'model',
+                    'data' => array(
+                        'xmlRoot' => array(
+                            'name' => 'UploadPartRequest')),
+                    'parameters' => array(
+                        'Body' => array(
+                            'type' => array(
+                                'string',
+                                'object'),
+                            'location' => 'body'),
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri'),
+                        'ContentLength' => array(
+                            'type' => 'numeric',
+                            'location' => 'header',
+                            'sentAs' => 'Content-Length'),
+                        'ContentMD5' => array(
+                            'type' => array(
+                                'string',
+                                'boolean'),
+                            'location' => 'header',
+                            'sentAs' => 'Content-MD5'),
+                        'Key' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                            'minLength' => 1,
+                            'filters' => array(
+                                'Qcloud\\Cos\\Client::explodeKey')),
+                        'PartNumber' => array(
+                            'required' => true,
+                            'type' => 'numeric',
+                            'location' => 'query',
+                            'sentAs' => 'partNumber'),
+                        'UploadId' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'uploadId'),
+                        'ServerSideEncryption' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption',
+                        ),
+                        'SSECustomerAlgorithm' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+                        ),
+                        'SSECustomerKey' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-key',
+                        ),
+                        'SSECustomerKeyMD5' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-key-MD5',
+                        ),
+                        'RequestPayer' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-payer',
+                        ))),
+                'PutObject' => array(
+                    'httpMethod' => 'PUT',
+                    'uri' => '/{Bucket}{/Key*}',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'PutObjectOutput',
+                    'responseType' => 'model',
+                    'data' => array(
+                        'xmlRoot' => array(
+                            'name' => 'PutObjectRequest')),
+                    'parameters' => array(
+                        'ACL' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-acl'),
+                        'Body' => array(
+                            'type' => array(
+                                'string',
+                                'object'),
+                            'location' => 'body'),
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri'),
+                        'CacheControl' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Cache-Control'),
+                        'ContentDisposition' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Content-Disposition'),
+                        'ContentEncoding' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Content-Encoding'),
+                        'ContentLanguage' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Content-Language'),
+                        'ContentLength' => array(
+                            'type' => 'numeric',
+                            'location' => 'header',
+                            'sentAs' => 'Content-Length'),
+                        'ContentMD5' => array(
+                            'type' => array(
+                                'string',
+                                'boolean'),
+                            'location' => 'header',
+                            'sentAs' => 'Content-MD5'),
+                        'ContentType' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Content-Type'),
+                        'Key' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                            'minLength' => 1,
+                            'filters' => array(
+                                'Qcloud\\Cos\\Client::explodeKey')),
+                        'Metadata' => array(
+                            'type' => 'object',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-meta-',
+                            'additionalProperties' => array(
+                                'type' => 'string')
+                        ),
+                        'ServerSideEncryption' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption',
+                        ),
+                        'StorageClass' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-storage-class',
+                        ),
+                        'WebsiteRedirectLocation' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-website-redirect-location',
+                        ),
+                        'SSECustomerAlgorithm' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+                        ),
+                        'SSECustomerKey' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-key',
+                        ),
+                        'SSECustomerKeyMD5' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-key-MD5',
+                        ),
+                        'SSEKMSKeyId' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-aws-kms-key-id',
+                        ),
+                        'RequestPayer' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-payer',
+                        ),
+                        'ACP' => array(
+                            'type' => 'object',
+                            'additionalProperties' => true,
+                        ))),
+                /**
+                设置 COS 对象的访问权限信息(Access Control List, ACL)的方法.
+
+                ACL权限包括读、写、读写权限. COS 对象的 ACL 可以通过 header头部:"x-cos-acl","x-cos-grant-read","x-cos-grant-write", "x-cos-grant-full-control" 传入 ACL 信息,或者通过 Body 以 XML 格式传入 ACL 信息.这两种方式只 能选择其中一种,否则引起冲突. 传入新的 ACL 将覆盖原有 ACL信息.ACL策略数上限1000,建议用户不要每个上传文件都设置 ACL.
+
+                关于设置 COS 对象的ACL接口的具体描述,请查看https://cloud.tencent.com/document/product/436/7748.
+
+                cos PHP SDK 中设置 COS 对象的 ACL 的方法具体步骤如下:
+
+                1. 初始化客户端cosClient,填入存储桶名,和一些额外需要的参数,如授权的具体信息等。
+
+                2. 调用 PutObjectAcl 对象中的方法发出请求。
+
+                3. 接收该接口的返回数据,若没有抛出异常,则设置成功。
+
+                示例:
+                $cosClient->PutObjectAcl(array(
+                'Bucket' => $this->bucket,
+                'Key' => '11',
+                'Grants' => array(
+                array(
+                'Grantee' => array(
+                'DisplayName' => 'qcs::cam::uin/327874225:uin/327874225',
+                'ID' => 'qcs::cam::uin/327874225:uin/327874225',
+                'Type' => 'CanonicalUser',
+                ),
+                'Permission' => 'FULL_CONTROL',
+                ),
+                // ... repeated
+                ),
+                'Owner' => array(
+                'DisplayName' => 'qcs::cam::uin/2779643970:uin/2779643970',
+                'ID' => 'qcs::cam::uin/2779643970:uin/2779643970',
+                )));
+                 */
+                'PutObjectAcl' => array(
+                    'httpMethod' => 'PUT',
+                    'uri' => '/{Bucket}{/Key*}?acl',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'PutObjectAclOutput',
+                    'responseType' => 'model',
+                    'data' => array(
+                        'xmlRoot' => array(
+                            'name' => 'AccessControlPolicy',
+                        ),
+                    ),
+                    'parameters' => array(
+                        'ACL' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-acl',
+                        ),
+                        'Grants' => array(
+                            'type' => 'array',
+                            'location' => 'xml',
+                            'sentAs' => 'AccessControlList',
+                            'items' => array(
+                                'name' => 'Grant',
+                                'type' => 'object',
+                                'properties' => array(
+                                    'Grantee' => array(
+                                        'type' => 'object',
+                                        'properties' => array(
+                                            'DisplayName' => array(
+                                                'type' => 'string'),
+                                            'ID' => array(
+                                                'type' => 'string'),
+                                            'Type' => array(
+                                                'type' => 'string',
+                                                'sentAs' => 'xsi:type',
+                                                'data' => array(
+                                                    'xmlAttribute' => true,
+                                                    'xmlNamespace' => 'http://www.w3.org/2001/XMLSchema-instance')),
+                                            'URI' => array(
+                                                'type' => 'string') )),
+                                    'Permission' => array(
+                                        'type' => 'string',
+                                    ),
+                                ),
+                            ),
+                        ),
+                        'Owner' => array(
+                            'type' => 'object',
+                            'location' => 'xml',
+                            'properties' => array(
+                                'DisplayName' => array(
+                                    'type' => 'string',
+                                ),
+                                'ID' => array(
+                                    'type' => 'string',
+                                ),
+                            ),
+                        ),
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                        ),
+                        'GrantFullControl' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-grant-full-control',
+                        ),
+                        'GrantRead' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-grant-read',
+                        ),
+                        'GrantReadACP' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-grant-read-acp',
+                        ),
+                        'GrantWrite' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-grant-write',
+                        ),
+                        'GrantWriteACP' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-grant-write-acp',
+                        ),
+                        'Key' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                            'minLength' => 1,
+                            'filters' => array(
+                                'Qcloud\\Cos\\Client::explodeKey')
+                        ),
+                        'RequestPayer' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-payer',
+                        ),
+                        'ACP' => array(
+                            'type' => 'object',
+                            'additionalProperties' => true,
+                        ),
+                    ),
+                    'errorResponses' => array(
+                        array(
+                            'reason' => 'The specified key does not exist.',
+                            'class' => 'NoSuchKeyException',
+                        ),
+                    ),
+                ),
+                /**
+                设置存储桶(Bucket) 的访问权限(Access Control List, ACL)的方法.
+
+                ACL 权限包括读、写、读写权限. 写入 Bucket 的 ACL 可以通过 header头部:"x-cos-acl","x-cos-grant-read","x-cos-grant-write", "x-cos-grant-full-control" 传入 ACL 信息,或者通过 Body 以 XML 格式传入 ACL 信息.这两种方式只 能选择其中一种,否则引起冲突. 传入新的 ACL 将覆盖原有 ACL信息. 私有 Bucket 可以下可以给某个文件夹设置成公有,那么该文件夹下的文件都是公有;但是把文件夹设置成私有后,在该文件夹下的文件设置 的公有属性,不会生效.
+
+                关于设置 Bucket 的ACL接口的具体描述,请查看 https://cloud.tencent.com/document/product/436/7737.
+
+                cos php SDK 中设置 Bucket 的ACL的方法具体步骤如下:
+
+                1. 初始化客户端cosClient,填入存储桶名,和一些额外需要的参数,如授权的具体信息等。
+
+                2. 调用 PutObjectAcl 接口发出请求。
+
+                3. 接收该接口的返回数据,若没有抛出异常,设置成功。
+
+
+                示例:
+                $result = $cosClient->PutObjectAcl(array(
+                'Bucket' => 'testbucket-1252448703',
+                'Key' => '111',
+                'Grants' => array(
+                array(
+                'Grantee' => array(
+                'DisplayName' => 'qcs::cam::uin/327874225:uin/327874225',
+                'ID' => 'qcs::cam::uin/327874225:uin/327874225',
+                'Type' => 'CanonicalUser',
+                ),
+                'Permission' => 'FULL_CONTROL',
+                ),
+                // ... repeated
+                ),
+                'Owner' => array(
+                'DisplayName' => 'qcs::cam::uin/3210232098:uin/3210232098',
+                'ID' => 'qcs::cam::uin/3210232098:uin/3210232098',
+                ),));
+                 */
+                'PutBucketAcl' => array(
+                    'httpMethod' => 'PUT',
+                    'uri' => '/{Bucket}?acl',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'PutBucketAclOutput',
+                    'responseType' => 'model',
+                    'data' => array(
+                        'xmlRoot' => array(
+                            'name' => 'AccessControlPolicy',
+                        ),
+                    ),
+                    'parameters' => array(
+                        'ACL' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-acl',
+                        ),
+                        'Grants' => array(
+                            'type' => 'array',
+                            'location' => 'xml',
+                            'sentAs' => 'AccessControlList',
+                            'items' => array(
+                                'name' => 'Grant',
+                                'type' => 'object',
+                                'properties' => array(
+                                    'Grantee' => array(
+                                        'type' => 'object',
+                                        'properties' => array(
+                                            'DisplayName' => array(
+                                                'type' => 'string',
+                                            ),
+                                            'EmailAddress' => array(
+                                                'type' => 'string',
+                                            ),
+                                            'ID' => array(
+                                                'type' => 'string',
+                                            ),
+                                            'Type' => array(
+                                                'required' => true,
+                                                'type' => 'string',
+                                                'sentAs' => 'xsi:type',
+                                                'data' => array(
+                                                    'xmlAttribute' => true,
+                                                    'xmlNamespace' => 'http://www.w3.org/2001/XMLSchema-instance',
+                                                ),
+                                            ),
+                                            'URI' => array(
+                                                'type' => 'string',
+                                            ),
+                                        ),
+                                    ),
+                                    'Permission' => array(
+                                        'type' => 'string',
+                                    ),
+                                ),
+                            ),
+                        ),
+                        'Owner' => array(
+                            'type' => 'object',
+                            'location' => 'xml',
+                            'properties' => array(
+                                'DisplayName' => array(
+                                    'type' => 'string',
+                                ),
+                                'ID' => array(
+                                    'type' => 'string',
+                                ),
+                            ),
+                        ),
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                        ),
+                        'GrantFullControl' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-grant-full-control',
+                        ),
+                        'GrantRead' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-grant-read',
+                        ),
+                        'GrantReadACP' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-grant-read-acp',
+                        ),
+                        'GrantWrite' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-grant-write',
+                        ),
+                        'GrantWriteACP' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-grant-write-acp',
+                        ),
+                        'ACP' => array(
+                            'type' => 'object',
+                            'additionalProperties' => true,
+                        ),
+                    ),
+                ),
+                /**
+                设置存储桶(Bucket) 的跨域配置信息的方法.
+
+                跨域访问配置的预请求是指在发送跨域请求之前会发送一个 OPTIONS 请求并带上特定的来源域,HTTP 方 法和 header 信息等给 COS,以决定是否可以发送真正的跨域请求. 当跨域访问配置不存在时,请求返回403 Forbidden.
+
+                默认情况下,Bucket的持有者可以直接配置 Bucket的跨域信息 ,Bucket 持有者也可以将配置权限授予其他用户.新的配置是覆盖当前的所有配置信 息,而不是新增一条配置.可以通过传入 XML 格式的配置文件来实现配置,文件大小限制为64 KB.
+
+                关于设置 Bucket 的跨域配置信息接口的具体描述,请查看 https://cloud.tencent.com/document/product/436/8279.
+
+                cos php SDK 中设置 Bucket 的跨域配置信息的方法具体步骤如下:
+
+                1. 初始化客户端cosClient,填入存储桶名,和一些额外需要的参数,如授权的具体信息等。
+
+                2. 调用 PutBucketCORS 接口发出请求。
+
+                3. 接收该接口的返回数据,若没有抛出异常,设置成功。
+
+                示例:
+                $result = $cosClient->putBucketCors(array(
+                // Bucket is required
+                'Bucket' => 'testbucket-1252448703',
+                // CORSRules is required
+                'CORSRules' => array(
+                array(
+                'ID' => '1234',
+                'AllowedHeaders' => array('*'),
+                // AllowedMethods is required
+                'AllowedMethods' => array('PUT'),
+                // AllowedOrigins is required
+                'AllowedOrigins' => array('http://www.qq.com', ),
+                ),
+                // ... repeated
+                ),
+                ));
+                 */
+                'PutBucketCors' => array(
+                    'httpMethod' => 'PUT',
+                    'uri' => '/{Bucket}?cors',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'PutBucketCorsOutput',
+                    'responseType' => 'model',
+                    'data' => array(
+                        'xmlRoot' => array(
+                            'name' => 'CORSConfiguration',
+                        ),
+                        'contentMd5' => true,
+                    ),
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                        ),
+                        'CORSRules' => array(
+                            'required' => true,
+                            'type' => 'array',
+                            'location' => 'xml',
+                            'data' => array(
+                                'xmlFlattened' => true,
+                            ),
+                            'items' => array(
+                                'name' => 'CORSRule',
+                                'type' => 'object',
+                                'sentAs' => 'CORSRule',
+                                'properties' => array(
+                                    'ID' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'AllowedHeaders' => array(
+                                        'type' => 'array',
+                                        'data' => array(
+                                            'xmlFlattened' => true,
+                                        ),
+                                        'items' => array(
+                                            'name' => 'AllowedHeader',
+                                            'type' => 'string',
+                                            'sentAs' => 'AllowedHeader',
+                                        ),
+                                    ),
+                                    'AllowedMethods' => array(
+                                        'required' => true,
+                                        'type' => 'array',
+                                        'data' => array(
+                                            'xmlFlattened' => true,
+                                        ),
+                                        'items' => array(
+                                            'name' => 'AllowedMethod',
+                                            'type' => 'string',
+                                            'sentAs' => 'AllowedMethod',
+                                        ),
+                                    ),
+                                    'AllowedOrigins' => array(
+                                        'required' => true,
+                                        'type' => 'array',
+                                        'data' => array(
+                                            'xmlFlattened' => true,
+                                        ),
+                                        'items' => array(
+                                            'name' => 'AllowedOrigin',
+                                            'type' => 'string',
+                                            'sentAs' => 'AllowedOrigin',
+                                        ),
+                                    ),
+                                    'ExposeHeaders' => array(
+                                        'type' => 'array',
+                                        'data' => array(
+                                            'xmlFlattened' => true,
+                                        ),
+                                        'items' => array(
+                                            'name' => 'ExposeHeader',
+                                            'type' => 'string',
+                                            'sentAs' => 'ExposeHeader',
+                                        ),
+                                    ),
+                                    'MaxAgeSeconds' => array(
+                                        'type' => 'numeric',
+                                    ),
+                                ),
+                            ),
+                        ),
+                    ),
+                ),
+                /**
+                设置存储桶(Bucket) 生命周期配置的方法.
+
+                COS 支持以生命周期配置的方式来管理 Bucket 中对象的生命周期. 如果该 Bucket 已配置生命周期,新的配置的同时则会覆盖原有的配置. 生命周期配置包含一个或多个将应用于一组对象规则的规则集 (其中每个规则为 COS 定义一个操作)。这些操作分为以下两种:转换操作,过期操作.
+
+                转换操作,定义对象转换为另一个存储类的时间(例如,您可以选择在对象创建 30 天后将其转换为低频存储类别,同 时也支持将数据沉降到归档存储类别.
+
+                过期操作,指定 Object 的过期时间,COS 将会自动为用户删除过期的 Object.
+
+                关于Bucket 生命周期配置接口的具体描述,请查看 https://cloud.tencent.com/document/product/436/8280
+
+                cos php SDK 中Bucket 生命周期配置的方法具体步骤如下:
+
+                1. 初始化客户端cosClient,填入存储桶名,和一些额外需要的参数,如授权的具体信息等。
+
+                2. 调用 PutBucketLifecycle 接口发出请求。
+
+                3. 接收该接口的返回数据,若没有抛出异常,设置成功。
+
+                示例:
+                $result = $cosClient->putBucketLifecycle(array(
+                // Bucket is required
+                'Bucket' => 'lewzylu06-1252448703',
+                // Rules is required
+                'Rules' => array(
+                array(
+                'Expiration' => array(
+                'Days' => 1000,
+                ),
+                'ID' => 'id1',
+                'Filter' => array(
+                'Prefix' => 'documents/'
+                ),
+                // Status is required
+                'Status' => 'Enabled',
+                'Transitions' => array(
+                array(
+                'Days' => 200,
+                'StorageClass' => 'NEARLINE'),
+                ),
+                // ... repeated
+                ),
+                )));
+                 */
+                'PutBucketLifecycle' => array(
+                    'httpMethod' => 'PUT',
+                    'uri' => '/{Bucket}?lifecycle',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'PutBucketLifecycleOutput',
+                    'responseType' => 'model',
+                    'data' => array(
+                        'xmlRoot' => array(
+                            'name' => 'LifecycleConfiguration',
+                        ),
+                        'contentMd5' => true,
+                    ),
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                        ),
+                        'Rules' => array(
+                            'required' => true,
+                            'type' => 'array',
+                            'location' => 'xml',
+                            'data' => array(
+                                'xmlFlattened' => true,
+                            ),
+                            'items' => array(
+                                'name' => 'Rule',
+                                'type' => 'object',
+                                'sentAs' => 'Rule',
+                                'properties' => array(
+                                    'Expiration' => array(
+                                        'type' => 'object',
+                                        'properties' => array(
+                                            'Date' => array(
+                                                'type' => array(
+                                                    'object',
+                                                    'string',
+                                                    'integer',
+                                                ),
+                                                'format' => 'date-time',
+                                            ),
+                                            'Days' => array(
+                                                'type' => 'numeric',
+                                            ),
+                                        ),
+                                    ),
+                                    'ID' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'Filter' => array(
+                                        'type' => 'object',
+                                        'require' => true,
+                                        'properties' => array(
+                                            'Prefix' => array(
+                                                'type' => 'string',
+                                                'require' => true,
+                                            ),
+                                            'Tag' => array(
+                                                'type' => 'object',
+                                                'require' => true,
+                                                'properties' => array(
+                                                    'Key' => array(
+                                                        'type' => 'string'
+                                                    ),
+                                                    'filters' => array(
+                                                        'Qcloud\\Cos\\Client::explodeKey'),
+                                                    'Value' => array(
+                                                        'type' => 'string'
+                                                    ),
+                                                )
+                                            )
+                                        ),
+                                    ),
+                                    'Status' => array(
+                                        'required' => true,
+                                        'type' => 'string',
+                                    ),
+                                    'Transitions' => array(
+                                        'type' => 'array',
+                                        'location' => 'xml',
+                                        'data' => array(
+                                            'xmlFlattened' => true,
+                                        ),
+                                        'items' => array(
+                                            'name' => 'Transition',
+                                            'type' => 'object',
+                                            'sentAs' => 'Transition',
+                                            'properties' => array(
+                                                'Date' => array(
+                                                    'type' => array(
+                                                        'object',
+                                                        'string',
+                                                        'integer',
+                                                    ),
+                                                    'format' => 'date-time',
+                                                ),
+                                                'Days' => array(
+                                                    'type' => 'numeric',
+                                                ),
+                                                'StorageClass' => array(
+                                                    'type' => 'string',
+                                                )))),
+                                    'NoncurrentVersionTransition' => array(
+                                        'type' => 'object',
+                                        'properties' => array(
+                                            'NoncurrentDays' => array(
+                                                'type' => 'numeric',
+                                            ),
+                                            'StorageClass' => array(
+                                                'type' => 'string',
+                                            ),
+                                        ),
+                                    ),
+                                    'NoncurrentVersionExpiration' => array(
+                                        'type' => 'object',
+                                        'properties' => array(
+                                            'NoncurrentDays' => array(
+                                                'type' => 'numeric',
+                                            ),
+                                        ),
+                                    ),
+                                ),
+                            ),
+                        ),
+                    ),
+                ),
+                /**
+                存储桶(Bucket)版本控制的方法.
+
+                版本管理功能一经打开,只能暂停,不能关闭. 通过版本控制,可以在一个 Bucket 中保留一个对象的多个版本. 版本控制可以防止意外覆盖和删除对象,以便检索早期版本的对象. 默认情况下,版本控制功能处于禁用状态,需要主动去启用或者暂停(Enabled 或者 Suspended).
+
+                cos php SDK 中 Bucket 版本控制启用或者暂停的方法具体步骤如下:
+
+                1. 初始化客户端cosClient,填入存储桶名,和一些额外需要的参数,如授权的具体信息等。
+
+                2. 调用 PutBucketVersioning 接口发出请求。
+
+                3. 接收该接口的返回数据,若没有抛出异常,设置成功。
+
+                示例:
+                $result = $cosClient->putBucketVersioning(
+                array('Bucket' => 'testbucket-1252448703',
+                'Status' => 'Enabled'));
+                 */
+                'PutBucketVersioning' => array(
+                    'httpMethod' => 'PUT',
+                    'uri' => '/{Bucket}?versioning',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'PutBucketVersioningOutput',
+                    'responseType' => 'model',
+                    'data' => array(
+                        'xmlRoot' => array(
+                            'name' => 'VersioningConfiguration',
+                        ),
+                    ),
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                        ),
+                        'MFA' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-mfa',
+                        ),
+                        'MFADelete' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                            'sentAs' => 'MfaDelete',
+                        ),
+                        'Status' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                    ),
+                ),
+                /**
+                配置跨区域复制的方法.
+
+                跨区域复制是支持不同区域 Bucket 自动异步复制对象.注意,不能是同区域的 Bucket, 且源 Bucket 和目 标 Bucket 必须已启用版本控制putBucketVersioning(PutBucketVersioningRequest).
+
+                cos php SDK 中配置跨区域复制的方法具体步骤如下:
+
+                1. 初始化客户端cosClient,填入存储桶名,和一些额外需要的参数,如授权的具体信息等。
+
+                2. 调用 PutBucketRelication 接口发出请求。
+
+                3. 接收该接口的返回数据,若没有抛出异常,设置成功。
+
+                示例:
+
+                 */
+                'PutBucketReplication' => array(
+                    'httpMethod' => 'PUT',
+                    'uri' => '/{Bucket}?replication',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'PutBucketReplicationOutput',
+                    'responseType' => 'model',
+                    'data' => array(
+                        'xmlRoot' => array(
+                            'name' => 'ReplicationConfiguration',
+                        ),
+                        'contentMd5' => true,
+                    ),
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                        ),
+                        'Role' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                        'Rules' => array(
+                            'required' => true,
+                            'type' => 'array',
+                            'location' => 'xml',
+                            'data' => array(
+                                'xmlFlattened' => true,
+                            ),
+                            'items' => array(
+                                'name' => 'ReplicationRule',
+                                'type' => 'object',
+                                'sentAs' => 'Rule',
+                                'properties' => array(
+                                    'ID' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'Prefix' => array(
+                                        'required' => true,
+                                        'type' => 'string',
+                                    ),
+                                    'Status' => array(
+                                        'required' => true,
+                                        'type' => 'string',
+                                    ),
+                                    'Destination' => array(
+                                        'required' => true,
+                                        'type' => 'object',
+                                        'properties' => array(
+                                            'Bucket' => array(
+                                                'required' => true,
+                                                'type' => 'string',
+                                            ),
+                                            'StorageClass' => array(
+                                                'type' => 'string',
+                                            ),
+                                        ),
+                                    ),
+                                ),
+                            ),
+                        ),
+                    ),
+                ),
+                /**
+                设置存储桶(Bucket) 的回调设置的方法.
+                 */
+                'PutBucketNotification' => array(
+                    'httpMethod' => 'PUT',
+                    'uri' => '/{Bucket}?notification',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'PutBucketNotificationOutput',
+                    'responseType' => 'model',
+                    'data' => array(
+                        'xmlRoot' => array(
+                            'name' => 'NotificationConfiguration',
+                        ),
+                    ),
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                        ),
+                        'CloudFunctionConfigurations' => array(
+                            'type' => 'array',
+                            'location' => 'xml',
+                            'data' => array(
+                                'xmlFlattened' => true,
+                            ),
+                            'items' => array(
+                                'name' => 'CloudFunctionConfiguration',
+                                'type' => 'object',
+                                'sentAs' => 'CloudFunctionConfiguration',
+                                'properties' => array(
+                                    'Id' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'CloudFunction' => array(
+                                        'required' => true,
+                                        'type' => 'string',
+                                        'sentAs' => 'CloudFunction',
+                                    ),
+                                    'Events' => array(
+                                        'required' => true,
+                                        'type' => 'array',
+                                        'data' => array(
+                                            'xmlFlattened' => true,
+                                        ),
+                                        'items' => array(
+                                            'name' => 'Event',
+                                            'type' => 'string',
+                                            'sentAs' => 'Event',
+                                        ),
+                                    ),
+                                    'Filter' => array(
+                                        'type' => 'object',
+                                        'properties' => array(
+                                            'Key' => array(
+                                                'type' => 'object',
+                                                'sentAs' => 'Key',
+                                                'properties' => array(
+                                                    'FilterRules' => array(
+                                                        'type' => 'array',
+                                                        'data' => array(
+                                                            'xmlFlattened' => true,
+                                                        ),
+                                                        'items' => array(
+                                                            'name' => 'FilterRule',
+                                                            'type' => 'object',
+                                                            'sentAs' => 'FilterRule',
+                                                            'properties' => array(
+                                                                'Name' => array(
+                                                                    'type' => 'string',
+                                                                ),
+                                                                'Value' => array(
+                                                                    'type' => 'string',
+                                                                ),
+                                                            ),
+                                                        ),
+                                                    ),
+                                                ),
+                                            ),
+                                            'filters' => array(
+                                                'Qcloud\\Cos\\Client::explodeKey')
+                                        ),
+                                    ),
+                                ),
+                            ),
+                        ),
+                    ),
+                ),
+                'RestoreObject' => array(
+                    'httpMethod' => 'POST',
+                    'uri' => '/{Bucket}{/Key*}?restore',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'RestoreObjectOutput',
+                    'responseType' => 'model',
+                    'data' => array(
+                        'xmlRoot' => array(
+                            'name' => 'RestoreRequest',
+                        ),
+                    ),
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                        ),
+                        'Key' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                            'minLength' => 1,
+                            'filters' => array(
+                                'Qcloud\\Cos\\Client::explodeKey')
+                        ),
+                        'VersionId' => array(
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'versionId',
+                        ),
+                        'Days' => array(
+                            'required' => true,
+                            'type' => 'numeric',
+                            'location' => 'xml',
+                        ),
+                        'CASJobParameters' => array(
+                            'type' => 'object',
+                            'location' => 'xml',
+                            'properties' => array(
+                                'Tier' => array(
+                                    'type' => 'string',
+                                    'required' => true,
+                                ),
+                            ),
+                        ),
+                        'RequestPayer' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-payer',
+                        ),
+                    ),
+                    'errorResponses' => array(
+                        array(
+                            'reason' => 'This operation is not allowed against this storage tier',
+                            'class' => 'ObjectAlreadyInActiveTierErrorException',
+                        ),
+                    ),
+                ),
+                /**
+                查询存储桶(Bucket)中正在进行中的分块上传对象的方法.
+
+                COS 支持查询 Bucket 中有哪些正在进行中的分块上传对象,单次请求操作最多列出 1000 个正在进行中的 分块上传对象.
+
+                关于查询 Bucket 中正在进行中的分块上传对象接口的具体描述,请查看 https://cloud.tencent.com/document/product/436/7736.
+
+                cos php SDK 中查询 Bucket 中正在进行中的分块上传对象的方法具体步骤如下:
+
+                1. 初始化客户端cosClient,填入存储桶名,和一些额外需要的参数,如授权的具体信息等。
+
+                2. 调用 ListParts 接口发出请求。
+
+                3. 接收该接口的返回数据,若没有抛出异常,获取成功。
+
+                 */
+                'ListParts' => array(
+                    'httpMethod' => 'GET',
+                    'uri' => '/{Bucket}{/Key*}',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'ListPartsOutput',
+                    'responseType' => 'model',
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri'),
+                        'Key' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                            'minLength' => 1,
+                            'filters' => array(
+                                'Qcloud\\Cos\\Client::explodeKey')),
+                        'MaxParts' => array(
+                            'type' => 'numeric',
+                            'location' => 'query',
+                            'sentAs' => 'max-parts'),
+                        'PartNumberMarker' => array(
+                            'type' => 'numeric',
+                            'location' => 'query',
+                            'sentAs' => 'part-number-marker'),
+                        'UploadId' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'uploadId'),
+                        'command.expects' => array(
+                            'static' => true,
+                            'default' => 'application/xml'))),
+                /**
+                查询存储桶(Bucket) 下的部分或者全部对象的方法.
+
+                COS 支持列出指定 Bucket 下的部分或者全部对象.
+
+                每次默认返回的最大条目数为 1000 条.
+
+                如果无法一次返回所有的对象,则返回结果中的 IsTruncated 为 true,同时会附加一个 NextMarker 字段,提示下 一个条目的起点.
+
+                若一次请求,已经返回了全部对象,则不会有 NextMarker 这个字段,同时 IsTruncated 为 false.
+
+                若把 prefix 设置为某个文件夹的全路径名,则可以列出以此 prefix 为开头的文件,即该文件 夹下递归的所有文件和子文件夹.
+
+                如果再设置 delimiter 定界符为 “/”,则只列出该文件夹下的文件,子文件夹下递归的文件和文件夹名 将不被列出.而子文件夹名将会以 CommonPrefix 的形式给出.
+
+                关于查询Bucket 下的部分或者全部对象接口的具体描述,请查看https://cloud.tencent.com/document/product/436/7734.
+
+                cos php SDK 中查询 Bucket 下的部分或者全部对象的方法具体步骤如下:
+
+                1. 初始化客户端cosClient,填入存储桶名,和一些额外需要的参数,如授权的具体信息等。
+
+                2. 调用 ListObjects 接口发出请求。
+
+                3. 接收该接口的返回数据,若没有抛出异常,则list成功。
+
+                示例:
+                $result = $cosClient->ListObjects(array(
+                'Bucket' =>  'testbucket-1252448703'));
+                 */
+                'ListObjects' => array(
+                    'httpMethod' => 'GET',
+                    'uri' => '/{Bucket}',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'ListObjectsOutput',
+                    'responseType' => 'model',
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri'),
+                        'Delimiter' => array(
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'delimiter'),
+                        'EncodingType' => array(
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'encoding-type'),
+                        'Marker' => array(
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'marker'),
+                        'MaxKeys' => array(
+                            'type' => 'numeric',
+                            'location' => 'query',
+                            'sentAs' => 'max-keys'),
+                        'Prefix' => array(
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'prefix'),
+                        'command.expects' => array(
+                            'static' => true,
+                            'default' => 'application/xml')),
+                    'errorResponses' => array(
+                        array(
+                            'reason' => 'The specified bucket does not exist.',
+                            'class' => 'NoSuchBucketException'))),
+                /**
+                获取所属账户的所有存储空间列表的方法.
+
+                通过使用帯 Authorization 签名认证的请求,可以获取签名中 APPID 所属账户的所有存储空间列表 (Bucket list).
+
+                关于获取所有存储空间列表接口的具体描述,请查看https://cloud.tencent.com/document/product/436/8291.
+
+                cos php SDK 中获取所属账户的所有存储空间列表的方法具体步骤如下:
+
+                1. 初始化客户端cosClient,填入存储桶名,和一些额外需要的参数,如授权的具体信息等。
+
+                2. 调用 ListBuckets 接口发出请求。
+
+                3. 接收该接口的返回数据,若没有抛出异常,获取成功。
+
+                示例:
+                $result = $cosClient->listBuckets();
+                print_r($result);
+                 */
+                'ListBuckets' => array(
+                    'httpMethod' => 'GET',
+                    'uri' => '/',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'ListBucketsOutput',
+                    'responseType' => 'model',
+                    'parameters' => array(
+                        'command.expects' => array(
+                            'static' => true,
+                            'default' => 'application/xml',
+                        ),
+                    ),
+                ),
+                'ListObjectVersions' => array(
+                    'httpMethod' => 'GET',
+                    'uri' => '/{Bucket}?versions',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'ListObjectVersionsOutput',
+                    'responseType' => 'model',
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                        ),
+                        'Delimiter' => array(
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'delimiter',
+                        ),
+                        'EncodingType' => array(
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'encoding-type',
+                        ),
+                        'KeyMarker' => array(
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'key-marker',
+                        ),
+                        'MaxKeys' => array(
+                            'type' => 'numeric',
+                            'location' => 'query',
+                            'sentAs' => 'max-keys',
+                        ),
+                        'Prefix' => array(
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'prefix',
+                        ),
+                        'VersionIdMarker' => array(
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'version-id-marker',
+                        ),
+                        'command.expects' => array(
+                            'static' => true,
+                            'default' => 'application/xml',
+                        ),
+                    ),
+                ),
+                'ListMultipartUploads' => array(
+                    'httpMethod' => 'GET',
+                    'uri' => '/{Bucket}?uploads',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'ListMultipartUploadsOutput',
+                    'responseType' => 'model',
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                        ),
+                        'Delimiter' => array(
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'delimiter',
+                        ),
+                        'EncodingType' => array(
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'encoding-type',
+                        ),
+                        'KeyMarker' => array(
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'key-marker',
+                        ),
+                        'MaxUploads' => array(
+                            'type' => 'numeric',
+                            'location' => 'query',
+                            'sentAs' => 'max-uploads',
+                        ),
+                        'Prefix' => array(
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'prefix',
+                        ),
+                        'UploadIdMarker' => array(
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'upload-id-marker',
+                        ),
+                        'command.expects' => array(
+                            'static' => true,
+                            'default' => 'application/xml',
+                        ),
+                    ),
+                ),
+                'HeadObject' => array(
+                    'httpMethod' => 'HEAD',
+                    'uri' => '/{Bucket}{/Key*}',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'HeadObjectOutput',
+                    'responseType' => 'model',
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                        ),
+                        'IfMatch' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'If-Match',
+                        ),
+                        'IfModifiedSince' => array(
+                            'type' => array(
+                                'object',
+                                'string',
+                                'integer',
+                            ),
+                            'format' => 'date-time-http',
+                            'location' => 'header',
+                            'sentAs' => 'If-Modified-Since',
+                        ),
+                        'IfNoneMatch' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'If-None-Match',
+                        ),
+                        'IfUnmodifiedSince' => array(
+                            'type' => array(
+                                'object',
+                                'string',
+                                'integer',
+                            ),
+                            'format' => 'date-time-http',
+                            'location' => 'header',
+                            'sentAs' => 'If-Unmodified-Since',
+                        ),
+                        'Key' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                            'minLength' => 1,
+                            'filters' => array(
+                                'Qcloud\\Cos\\Client::explodeKey')
+                        ),
+                        'Range' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                        ),
+                        'VersionId' => array(
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'versionId',
+                        ),
+                        'SSECustomerAlgorithm' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+                        ),
+                        'SSECustomerKey' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-key',
+                        ),
+                        'SSECustomerKeyMD5' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-key-MD5',
+                        ),
+                        'RequestPayer' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-payer',
+                        ),
+                    ),
+                    'errorResponses' => array(
+                        array(
+                            'reason' => 'The specified key does not exist.',
+                            'class' => 'NoSuchKeyException',
+                        ),
+                    ),
+                ),
+                /**
+                存储桶(Bucket) 是否存在的方法.
+
+                在开始使用 COS 时,需要确认该 Bucket 是否存在,是否有权限访问.若不存在,则可以调用putBucket(PutBucketRequest) 创建.
+
+                关于确认该 Bucket 是否存在,是否有权限访问接口的具体描述,请查看https://cloud.tencent.com/document/product/436/7735.
+
+                cos php SDK 中Bucket 是否存在的方法具体步骤如下:
+
+                1. 初始化客户端cosClient,填入存储桶名,和一些额外需要的参数,如授权的具体信息等。
+
+                2. 调用 HeadBucket 接口发出请求。
+
+                3. 接收该接口的返回数据,若没有抛出异常,获取成功。
+
+                示例:
+                $result = $cosClient->headObject(array(
+                'Bucket' => 'testbucket-1252448703',
+                'Key' => '11',
+                'VersionId' =>'111',
+                'ServerSideEncryption' => 'AES256'));
+                 */
+                'HeadBucket' => array(
+                    'httpMethod' => 'HEAD',
+                    'uri' => '/{Bucket}',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'HeadBucketOutput',
+                    'responseType' => 'model',
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                        ),
+                    ),
+                    'errorResponses' => array(
+                        array(
+                            'reason' => 'The specified bucket does not exist.',
+                            'class' => 'NoSuchBucketException',
+                        ),
+                    ),
+                ),
+                'UploadPartCopy' => array(
+                    'httpMethod' => 'PUT',
+                    'uri' => '/{Bucket}{/Key*}',
+                    'class' => 'Qcloud\\Cos\\Command',
+                    'responseClass' => 'UploadPartCopyOutput',
+                    'responseType' => 'model',
+                    'data' => array(
+                        'xmlRoot' => array(
+                            'name' => 'UploadPartCopyRequest',
+                        ),
+                    ),
+                    'parameters' => array(
+                        'Bucket' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                        ),
+                        'CopySource' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-copy-source',
+                        ),
+                        'CopySourceIfMatch' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-copy-source-if-match',
+                        ),
+                        'CopySourceIfModifiedSince' => array(
+                            'type' => array(
+                                'object',
+                                'string',
+                                'integer',
+                            ),
+                            'format' => 'date-time-http',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-copy-source-if-modified-since',
+                        ),
+                        'CopySourceIfNoneMatch' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-copy-source-if-none-match',
+                        ),
+                        'CopySourceIfUnmodifiedSince' => array(
+                            'type' => array(
+                                'object',
+                                'string',
+                                'integer',
+                            ),
+                            'format' => 'date-time-http',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-copy-source-if-unmodified-since',
+                        ),
+                        'CopySourceRange' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-copy-source-range',
+                        ),
+                        'Key' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'uri',
+                            'minLength' => 1,
+                            'filters' => array(
+                                'Qcloud\\Cos\\Client::explodeKey')
+                        ),
+                        'PartNumber' => array(
+                            'required' => true,
+                            'type' => 'numeric',
+                            'location' => 'query',
+                            'sentAs' => 'partNumber',
+                        ),
+                        'UploadId' => array(
+                            'required' => true,
+                            'type' => 'string',
+                            'location' => 'query',
+                            'sentAs' => 'uploadId',
+                        ),
+                        'SSECustomerAlgorithm' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+                        ),
+                        'SSECustomerKey' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-key',
+                        ),
+                        'SSECustomerKeyMD5' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-key-MD5',
+                        ),
+                        'CopySourceSSECustomerAlgorithm' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-copy-source-server-side-encryption-customer-algorithm',
+                        ),
+                        'CopySourceSSECustomerKey' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-copy-source-server-side-encryption-customer-key',
+                        ),
+                        'CopySourceSSECustomerKeyMD5' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-copy-source-server-side-encryption-customer-key-MD5',
+                        ),
+                        'RequestPayer' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-payer',
+                        ),
+                        'command.expects' => array(
+                            'static' => true,
+                            'default' => 'application/xml',
+                        ),
+                    ),
+                ),),
+            'models' => array(
+                'AbortMultipartUploadOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id'))),
+                'CreateBucketOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'Location' => array(
+                            'type' => 'string',
+                            'location' => 'header'),
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id'))),
+                'CompleteMultipartUploadOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'Location' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                        'Bucket' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                        'Key' => array(
+                            'type' => 'string',
+                            'location' => 'xml'
+                        ),
+                        'Expiration' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-expiration',
+                        ),
+                        'ETag' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                        'ServerSideEncryption' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption',
+                        ),
+                        'VersionId' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-version-id',
+                        ),
+                        'SSEKMSKeyId' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-aws-kms-key-id',
+                        ),
+                        'RequestCharged' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-charged',
+                        ),
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'CreateMultipartUploadOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'Bucket' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                            'sentAs' => 'Bucket'),
+                        'Key' => array(
+                            'type' => 'string',
+                            'location' => 'xml'),
+                        'UploadId' => array(
+                            'type' => 'string',
+                            'location' => 'xml'),
+                        'ServerSideEncryption' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption',
+                        ),
+                        'SSECustomerAlgorithm' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+                        ),
+                        'SSECustomerKeyMD5' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-key-MD5',
+                        ),
+                        'SSEKMSKeyId' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-aws-kms-key-id',
+                        ),
+                        'RequestCharged' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-charged',
+                        ),
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ))),
+                'CopyObjectOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'ETag' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                        'LastModified' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                        'Expiration' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-expiration',
+                        ),
+                        'CopySourceVersionId' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-copy-source-version-id',
+                        ),
+                        'VersionId' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-version-id',
+                        ),
+                        'ServerSideEncryption' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption',
+                        ),
+                        'SSECustomerAlgorithm' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+                        ),
+                        'RequestCharged' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-charged',
+                        ),
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'DeleteBucketOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id'))),
+                'DeleteBucketCorsOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'DeleteObjectOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'DeleteMarker' => array(
+                            'type' => 'boolean',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-delete-marker',
+                        ),
+                        'VersionId' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-version-id',
+                        ),
+                        'RequestCharged' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-charged',
+                        ),
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'DeleteObjectsOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'Deleted' => array(
+                            'type' => 'array',
+                            'location' => 'xml',
+                            'data' => array(
+                                'xmlFlattened' => true,
+                            ),
+                            'items' => array(
+                                'name' => 'DeletedObject',
+                                'type' => 'object',
+                                'properties' => array(
+                                    'Key' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'VersionId' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'DeleteMarker' => array(
+                                        'type' => 'boolean',
+                                    ),
+                                    'DeleteMarkerVersionId' => array(
+                                        'type' => 'string',
+                                    ),
+                                ),
+                            ),
+                        ),
+                        'RequestCharged' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-charged',
+                        ),
+                        'Errors' => array(
+                            'type' => 'array',
+                            'location' => 'xml',
+                            'sentAs' => 'Error',
+                            'data' => array(
+                                'xmlFlattened' => true,
+                            ),
+                            'items' => array(
+                                'name' => 'Error',
+                                'type' => 'object',
+                                'sentAs' => 'Error',
+                                'properties' => array(
+                                    'Key' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'VersionId' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'Code' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'Message' => array(
+                                        'type' => 'string',
+                                    ),
+                                ),
+                            ),
+                        ),
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'DeleteBucketLifecycleOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'DeleteBucketReplicationOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'GetObjectOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'Body' => array(
+                            'type' => 'string',
+                            'instanceOf' => 'Guzzle\\Http\\EntityBody',
+                            'location' => 'body',
+                        ),
+                        'DeleteMarker' => array(
+                            'type' => 'boolean',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-delete-marker',
+                        ),
+                        'AcceptRanges' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'accept-ranges',
+                        ),
+                        'Expiration' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-expiration',
+                        ),
+                        'Restore' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-restore',
+                        ),
+                        'LastModified' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Last-Modified',
+                        ),
+                        'ContentLength' => array(
+                            'type' => 'numeric',
+                            'location' => 'header',
+                            'sentAs' => 'Content-Length',
+                        ),
+                        'ETag' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                        ),
+                        'MissingMeta' => array(
+                            'type' => 'numeric',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-missing-meta',
+                        ),
+                        'VersionId' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-version-id',
+                        ),
+                        'CacheControl' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Cache-Control',
+                        ),
+                        'ContentDisposition' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Content-Disposition',
+                        ),
+                        'ContentEncoding' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Content-Encoding',
+                        ),
+                        'ContentLanguage' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Content-Language',
+                        ),
+                        'ContentRange' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Content-Range',
+                        ),
+                        'ContentType' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Content-Type',
+                        ),
+                        'Expires' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                        ),
+                        'WebsiteRedirectLocation' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-website-redirect-location',
+                        ),
+                        'ServerSideEncryption' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption',
+                        ),
+                        'Metadata' => array(
+                            'type' => 'object',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-meta-',
+                            'additionalProperties' => array(
+                                'type' => 'string',
+                            ),
+                        ),
+                        'SSECustomerAlgorithm' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+                        ),
+                        'SSECustomerKeyMD5' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-key-MD5',
+                        ),
+                        'SSEKMSKeyId' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-aws-kms-key-id',
+                        ),
+                        'StorageClass' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-storage-class',
+                        ),
+                        'RequestCharged' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-charged',
+                        ),
+                        'ReplicationStatus' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-replication-status',
+                        ),
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'GetObjectAclOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'Owner' => array(
+                            'type' => 'object',
+                            'location' => 'xml',
+                            'properties' => array(
+                                'DisplayName' => array(
+                                    'type' => 'string',
+                                ),
+                                'ID' => array(
+                                    'type' => 'string',
+                                ),
+                            ),
+                        ),
+                        'Grants' => array(
+                            'type' => 'array',
+                            'location' => 'xml',
+                            'sentAs' => 'AccessControlList',
+                            'items' => array(
+                                'name' => 'Grant',
+                                'type' => 'object',
+                                'sentAs' => 'Grant',
+                                'properties' => array(
+                                    'Grantee' => array(
+                                        'type' => 'object',
+                                        'properties' => array(
+                                            'DisplayName' => array(
+                                                'type' => 'string'),
+                                            /*
+                                                'EmailAddress' => array(
+                                                    'type' => 'string'),
+                                                */
+                                            'ID' => array(
+                                                'type' => 'string'),
+                                            /*
+                                                'Type' => array(
+                                                    'type' => 'string',
+                                                    'sentAs' => 'xsi:type',
+                                                    'data' => array(
+                                                        'xmlAttribute' => true,
+                                                        'xmlNamespace' => 'http://www.w3.org/2001/XMLSchema-instance')),
+                                                */
+                                            /*'URI' => array(
+                                                    'type' => 'string') */)),
+                                    'Permission' => array(
+                                        'type' => 'string',
+                                    ),
+                                ),
+                            ),
+                        ),
+                        'RequestCharged' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-charged',
+                        ),
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'GetBucketAclOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'Owner' => array(
+                            'type' => 'object',
+                            'location' => 'xml',
+                            'properties' => array(
+                                'DisplayName' => array(
+                                    'type' => 'string'),
+                                'ID' => array(
+                                    'type' => 'string'))),
+                        'Grants' => array(
+                            'type' => 'array',
+                            'location' => 'xml',
+                            'sentAs' => 'AccessControlList',
+                            'items' => array(
+                                'name' => 'Grant',
+                                'type' => 'object',
+                                'sentAs' => 'Grant',
+                                'properties' => array(
+                                    'Grantee' => array(
+                                        'type' => 'object',
+                                        'properties' => array(
+                                            'DisplayName' => array(
+                                                'type' => 'string'),
+                                            /*
+                                                'EmailAddress' => array(
+                                                    'type' => 'string'),
+                                                */
+                                            'ID' => array(
+                                                'type' => 'string'),
+                                            /*
+                                                'Type' => array(
+                                                    'type' => 'string',
+                                                    'sentAs' => 'xsi:type',
+                                                    'data' => array(
+                                                        'xmlAttribute' => true,
+                                                        'xmlNamespace' => 'http://www.w3.org/2001/XMLSchema-instance')),
+                                                */
+                                            /*'URI' => array(
+                                                    'type' => 'string') */)),
+                                    'Permission' => array(
+                                        'type' => 'string')))),
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id'))),
+                'GetBucketCorsOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'CORSRules' => array(
+                            'type' => 'array',
+                            'location' => 'xml',
+                            'sentAs' => 'CORSRule',
+                            'data' => array(
+                                'xmlFlattened' => true,
+                            ),
+                            'items' => array(
+                                'name' => 'CORSRule',
+                                'type' => 'object',
+                                'sentAs' => 'CORSRule',
+                                'properties' => array(
+                                    'ID' => array(
+                                        'type' => 'string'),
+                                    'AllowedHeaders' => array(
+                                        'type' => 'array',
+                                        'sentAs' => 'AllowedHeader',
+                                        'data' => array(
+                                            'xmlFlattened' => true,
+                                        ),
+                                        'items' => array(
+                                            'name' => 'AllowedHeader',
+                                            'type' => 'string',
+                                            'sentAs' => 'AllowedHeader',
+                                        ),
+                                    ),
+                                    'AllowedMethods' => array(
+                                        'type' => 'array',
+                                        'sentAs' => 'AllowedMethod',
+                                        'data' => array(
+                                            'xmlFlattened' => true,
+                                        ),
+                                        'items' => array(
+                                            'name' => 'AllowedMethod',
+                                            'type' => 'string',
+                                            'sentAs' => 'AllowedMethod',
+                                        ),
+                                    ),
+                                    'AllowedOrigins' => array(
+                                        'type' => 'array',
+                                        'sentAs' => 'AllowedOrigin',
+                                        'data' => array(
+                                            'xmlFlattened' => true,
+                                        ),
+                                        'items' => array(
+                                            'name' => 'AllowedOrigin',
+                                            'type' => 'string',
+                                            'sentAs' => 'AllowedOrigin',
+                                        ),
+                                    ),
+                                    'ExposeHeaders' => array(
+                                        'type' => 'array',
+                                        'sentAs' => 'ExposeHeader',
+                                        'data' => array(
+                                            'xmlFlattened' => true,
+                                        ),
+                                        'items' => array(
+                                            'name' => 'ExposeHeader',
+                                            'type' => 'string',
+                                            'sentAs' => 'ExposeHeader',
+                                        ),
+                                    ),
+                                    'MaxAgeSeconds' => array(
+                                        'type' => 'numeric',
+                                    ),
+                                ),
+                            ),
+                        ),
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'GetBucketLifecycleOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'Rules' => array(
+                            'type' => 'array',
+                            'location' => 'xml',
+                            'sentAs' => 'Rule',
+                            'data' => array(
+                                'xmlFlattened' => true,
+                            ),
+                            'items' => array(
+                                'name' => 'Rule',
+                                'type' => 'object',
+                                'sentAs' => 'Rule',
+                                'properties' => array(
+                                    'Expiration' => array(
+                                        'type' => 'object',
+                                        'properties' => array(
+                                            'Date' => array(
+                                                'type' => 'string',
+                                            ),
+                                            'Days' => array(
+                                                'type' => 'numeric',
+                                            ),
+                                        ),
+                                    ),
+                                    'ID' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'Filter' => array(
+                                        'type' => 'object',
+                                        'properties' => array(
+                                            'Prefix' => array(
+                                                'type' => 'string',
+                                            ),
+                                            'Tag' => array(
+                                                'type' => 'object',
+                                                'properties' => array(
+                                                    'Key' => array(
+                                                        'type' => 'string'
+                                                    ),
+                                                    'Value' => array(
+                                                        'type' => 'string'
+                                                    ),
+                                                )
+                                            )
+                                        ),
+                                    ),
+                                    'Status' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'Transition' => array(
+                                        'type' => 'object',
+                                        'properties' => array(
+                                            'Date' => array(
+                                                'type' => 'string',
+                                            ),
+                                            'Days' => array(
+                                                'type' => 'numeric',
+                                            ),
+                                            'StorageClass' => array(
+                                                'type' => 'string',
+                                            ),
+                                        ),
+                                    ),
+                                    'NoncurrentVersionTransition' => array(
+                                        'type' => 'object',
+                                        'properties' => array(
+                                            'NoncurrentDays' => array(
+                                                'type' => 'numeric',
+                                            ),
+                                            'StorageClass' => array(
+                                                'type' => 'string',
+                                            ),
+                                        ),
+                                    ),
+                                    'NoncurrentVersionExpiration' => array(
+                                        'type' => 'object',
+                                        'properties' => array(
+                                            'NoncurrentDays' => array(
+                                                'type' => 'numeric',
+                                            ),
+                                        ),
+                                    ),
+                                ),
+                            ),
+                        ),
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'GetBucketVersioningOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'Status' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                        'MFADelete' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                            'sentAs' => 'MfaDelete',
+                        ),
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'GetBucketReplicationOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'Role' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                        'Rules' => array(
+                            'type' => 'array',
+                            'location' => 'xml',
+                            'sentAs' => 'Rule',
+                            'data' => array(
+                                'xmlFlattened' => true,
+                            ),
+                            'items' => array(
+                                'name' => 'ReplicationRule',
+                                'type' => 'object',
+                                'sentAs' => 'Rule',
+                                'properties' => array(
+                                    'ID' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'Prefix' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'Status' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'Destination' => array(
+                                        'type' => 'object',
+                                        'properties' => array(
+                                            'Bucket' => array(
+                                                'type' => 'string',
+                                            ),
+                                            'StorageClass' => array(
+                                                'type' => 'string',
+                                            ),
+                                        ),
+                                    ),
+                                ),
+                            ),
+                        ),
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'GetBucketLocationOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'Location' => array(
+                            'type' => 'string',
+                            'location' => 'body',
+                            'filters' => array(
+                                'strval',
+                                'strip_tags',
+                                'trim',
+                            ),
+                        ),
+                    ),
+                ),
+                'UploadPartOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'ServerSideEncryption' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption',
+                        ),
+                        'ETag' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                        ),
+                        'SSECustomerAlgorithm' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+                        ),
+                        'SSECustomerKeyMD5' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-key-MD5',
+                        ),
+                        'SSEKMSKeyId' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-aws-kms-key-id',
+                        ),
+                        'RequestCharged' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-charged',
+                        ),
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'UploadPartCopyOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'CopySourceVersionId' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-copy-source-version-id',
+                        ),
+                        'ETag' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                        'LastModified' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                        'ServerSideEncryption' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption',
+                        ),
+                        'SSECustomerAlgorithm' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+                        ),
+                        'SSECustomerKeyMD5' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-key-MD5',
+                        ),
+                        'SSEKMSKeyId' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-aws-kms-key-id',
+                        ),
+                        'RequestCharged' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-charged',
+                        ),
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'PutBucketAclOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id'))),
+                'PutObjectOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'Expiration' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-expiration',
+                        ),
+                        'ETag' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                        ),
+                        'ServerSideEncryption' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption',
+                        ),
+                        'VersionId' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-version-id',
+                        ),
+                        'SSECustomerAlgorithm' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+                        ),
+                        'SSECustomerKeyMD5' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-key-MD5',
+                        ),
+                        'SSEKMSKeyId' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-aws-kms-key-id',
+                        ),
+                        'RequestCharged' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-charged',
+                        ),
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'PutObjectAclOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'RequestCharged' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-charged',
+                        ),
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'PutBucketCorsOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'PutBucketLifecycleOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'PutBucketVersioningOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'PutBucketReplicationOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'PutBucketNotificationOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'RestoreObjectOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'RequestCharged' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-charged',
+                        ),
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'ListPartsOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'Bucket' => array(
+                            'type' => 'string',
+                            'location' => 'xml'),
+                        'Key' => array(
+                            'type' => 'string',
+                            'location' => 'xml'),
+                        'UploadId' => array(
+                            'type' => 'string',
+                            'location' => 'xml'),
+                        'PartNumberMarker' => array(
+                            'type' => 'numeric',
+                            'location' => 'xml'),
+                        'NextPartNumberMarker' => array(
+                            'type' => 'numeric',
+                            'location' => 'xml'),
+                        'MaxParts' => array(
+                            'type' => 'numeric',
+                            'location' => 'xml'),
+                        'IsTruncated' => array(
+                            'type' => 'boolean',
+                            'location' => 'xml'),
+                        'Parts' => array(
+                            'type' => 'array',
+                            'location' => 'xml',
+                            'sentAs' => 'Part',
+                            'data' => array(
+                                'xmlFlattened' => true),
+                            'items' => array(
+                                'name' => 'Part',
+                                'type' => 'object',
+                                'sentAs' => 'Part',
+                                'properties' => array(
+                                    'PartNumber' => array(
+                                        'type' => 'numeric'),
+                                    'LastModified' => array(
+                                        'type' => 'string'),
+                                    'ETag' => array(
+                                        'type' => 'string'),
+                                    'Size' => array(
+                                        'type' => 'numeric')))),
+                        'Initiator' => array(
+                            'type' => 'object',
+                            'location' => 'xml',
+                            'properties' => array(
+                                'ID' => array(
+                                    'type' => 'string'),
+                                'DisplayName' => array(
+                                    'type' => 'string'))),
+                        'Owner' => array(
+                            'type' => 'object',
+                            'location' => 'xml',
+                            'properties' => array(
+                                'DisplayName' => array(
+                                    'type' => 'string'),
+                                'ID' => array(
+                                    'type' => 'string'))),
+                        'StorageClass' => array(
+                            'type' => 'string',
+                            'location' => 'xml'),
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id'))),
+                'ListObjectsOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'IsTruncated' => array(
+                            'type' => 'boolean',
+                            'location' => 'xml'),
+                        'Marker' => array(
+                            'type' => 'string',
+                            'location' => 'xml'),
+                        'NextMarker' => array(
+                            'type' => 'string',
+                            'location' => 'xml'),
+                        'Contents' => array(
+                            'type' => 'array',
+                            'location' => 'xml',
+                            'data' => array(
+                                'xmlFlattened' => true),
+                            'items' => array(
+                                'name' => 'Object',
+                                'type' => 'object',
+                                'properties' => array(
+                                    'Key' => array(
+                                        'type' => 'string'),
+                                    'LastModified' => array(
+                                        'type' => 'string'),
+                                    'ETag' => array(
+                                        'type' => 'string'),
+                                    'Size' => array(
+                                        'type' => 'numeric'),
+                                    'StorageClass' => array(
+                                        'type' => 'string'),
+                                    'Owner' => array(
+                                        'type' => 'object',
+                                        'properties' => array(
+                                            'DisplayName' => array(
+                                                'type' => 'string'),
+                                            'ID' => array(
+                                                'type' => 'string')))))),
+                        'Name' => array(
+                            'type' => 'string',
+                            'location' => 'xml'),
+                        'Prefix' => array(
+                            'type' => 'string',
+                            'location' => 'xml'),
+                        'Delimiter' => array(
+                            'type' => 'string',
+                            'location' => 'xml'),
+                        'MaxKeys' => array(
+                            'type' => 'numeric',
+                            'location' => 'xml'),
+                        'CommonPrefixes' => array(
+                            'type' => 'array',
+                            'location' => 'xml',
+                            'data' => array(
+                                'xmlFlattened' => true),
+                            'items' => array(
+                                'name' => 'CommonPrefix',
+                                'type' => 'object',
+                                'properties' => array(
+                                    'Prefix' => array(
+                                        'type' => 'string')))),
+                        'EncodingType' => array(
+                            'type' => 'string',
+                            'location' => 'xml'),
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id'))),
+                'ListBucketsOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'Buckets' => array(
+                            'type' => 'array',
+                            'location' => 'xml',
+                            'items' => array(
+                                'name' => 'Bucket',
+                                'type' => 'object',
+                                'sentAs' => 'Bucket',
+                                'properties' => array(
+                                    'Name' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'CreationDate' => array(
+                                        'type' => 'string',
+                                    ),
+                                ),
+                            ),
+                        ),
+                        'Owner' => array(
+                            'type' => 'object',
+                            'location' => 'xml',
+                            'properties' => array(
+                                'DisplayName' => array(
+                                    'type' => 'string',
+                                ),
+                                'ID' => array(
+                                    'type' => 'string',
+                                ),
+                            ),
+                        ),
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'ListObjectVersionsOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'IsTruncated' => array(
+                            'type' => 'boolean',
+                            'location' => 'xml',
+                        ),
+                        'KeyMarker' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                        'VersionIdMarker' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                        'NextKeyMarker' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                        'NextVersionIdMarker' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                        'Versions' => array(
+                            'type' => 'array',
+                            'location' => 'xml',
+                            'sentAs' => 'Version',
+                            'data' => array(
+                                'xmlFlattened' => true,
+                            ),
+                            'items' => array(
+                                'name' => 'ObjectVersion',
+                                'type' => 'object',
+                                'sentAs' => 'Version',
+                                'properties' => array(
+                                    'ETag' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'Size' => array(
+                                        'type' => 'numeric',
+                                    ),
+                                    'StorageClass' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'Key' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'VersionId' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'IsLatest' => array(
+                                        'type' => 'boolean',
+                                    ),
+                                    'LastModified' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'Owner' => array(
+                                        'type' => 'object',
+                                        'properties' => array(
+                                            'DisplayName' => array(
+                                                'type' => 'string',
+                                            ),
+                                            'ID' => array(
+                                                'type' => 'string',
+                                            ),
+                                        ),
+                                    ),
+                                ),
+                            ),
+                        ),
+                        'DeleteMarkers' => array(
+                            'type' => 'array',
+                            'location' => 'xml',
+                            'sentAs' => 'DeleteMarker',
+                            'data' => array(
+                                'xmlFlattened' => true,
+                            ),
+                            'items' => array(
+                                'name' => 'DeleteMarkerEntry',
+                                'type' => 'object',
+                                'sentAs' => 'DeleteMarker',
+                                'properties' => array(
+                                    'Owner' => array(
+                                        'type' => 'object',
+                                        'properties' => array(
+                                            'DisplayName' => array(
+                                                'type' => 'string',
+                                            ),
+                                            'ID' => array(
+                                                'type' => 'string',
+                                            ),
+                                        ),
+                                    ),
+                                    'Key' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'VersionId' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'IsLatest' => array(
+                                        'type' => 'boolean',
+                                    ),
+                                    'LastModified' => array(
+                                        'type' => 'string',
+                                    ),
+                                ),
+                            ),
+                        ),
+                        'Name' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                        'Prefix' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                        'Delimiter' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                        'MaxKeys' => array(
+                            'type' => 'numeric',
+                            'location' => 'xml',
+                        ),
+                        'CommonPrefixes' => array(
+                            'type' => 'array',
+                            'location' => 'xml',
+                            'data' => array(
+                                'xmlFlattened' => true,
+                            ),
+                            'items' => array(
+                                'name' => 'CommonPrefix',
+                                'type' => 'object',
+                                'properties' => array(
+                                    'Prefix' => array(
+                                        'type' => 'string',
+                                    ),
+                                ),
+                            ),
+                        ),
+                        'EncodingType' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'ListMultipartUploadsOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'Bucket' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                        'KeyMarker' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                        'UploadIdMarker' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                        'NextKeyMarker' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                        'Prefix' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                        'Delimiter' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                        'NextUploadIdMarker' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                        'MaxUploads' => array(
+                            'type' => 'numeric',
+                            'location' => 'xml',
+                        ),
+                        'IsTruncated' => array(
+                            'type' => 'boolean',
+                            'location' => 'xml',
+                        ),
+                        'Uploads' => array(
+                            'type' => 'array',
+                            'location' => 'xml',
+                            'sentAs' => 'Upload',
+                            'data' => array(
+                                'xmlFlattened' => true,
+                            ),
+                            'items' => array(
+                                'name' => 'MultipartUpload',
+                                'type' => 'object',
+                                'sentAs' => 'Upload',
+                                'properties' => array(
+                                    'UploadId' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'Key' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'Initiated' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'StorageClass' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'Owner' => array(
+                                        'type' => 'object',
+                                        'properties' => array(
+                                            'DisplayName' => array(
+                                                'type' => 'string',
+                                            ),
+                                            'ID' => array(
+                                                'type' => 'string',
+                                            ),
+                                        ),
+                                    ),
+                                    'Initiator' => array(
+                                        'type' => 'object',
+                                        'properties' => array(
+                                            'ID' => array(
+                                                'type' => 'string',
+                                            ),
+                                            'DisplayName' => array(
+                                                'type' => 'string',
+                                            ),
+                                        ),
+                                    ),
+                                ),
+                            ),
+                        ),
+                        'CommonPrefixes' => array(
+                            'type' => 'array',
+                            'location' => 'xml',
+                            'data' => array(
+                                'xmlFlattened' => true,
+                            ),
+                            'items' => array(
+                                'name' => 'CommonPrefix',
+                                'type' => 'object',
+                                'properties' => array(
+                                    'Prefix' => array(
+                                        'type' => 'string',
+                                    ),
+                                ),
+                            ),
+                        ),
+                        'EncodingType' => array(
+                            'type' => 'string',
+                            'location' => 'xml',
+                        ),
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'HeadObjectOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'DeleteMarker' => array(
+                            'type' => 'boolean',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-delete-marker',
+                        ),
+                        'AcceptRanges' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'accept-ranges',
+                        ),
+                        'Expiration' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-expiration',
+                        ),
+                        'Restore' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-restore',
+                        ),
+                        'LastModified' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Last-Modified',
+                        ),
+                        'ContentLength' => array(
+                            'type' => 'numeric',
+                            'location' => 'header',
+                            'sentAs' => 'Content-Length',
+                        ),
+                        'ETag' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                        ),
+                        'MissingMeta' => array(
+                            'type' => 'numeric',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-missing-meta',
+                        ),
+                        'VersionId' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-version-id',
+                        ),
+                        'CacheControl' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Cache-Control',
+                        ),
+                        'ContentDisposition' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Content-Disposition',
+                        ),
+                        'ContentEncoding' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Content-Encoding',
+                        ),
+                        'ContentLanguage' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Content-Language',
+                        ),
+                        'ContentType' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'Content-Type',
+                        ),
+                        'Expires' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                        ),
+                        'WebsiteRedirectLocation' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-website-redirect-location',
+                        ),
+                        'ServerSideEncryption' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption',
+                        ),
+                        'Metadata' => array(
+                            'type' => 'object',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-meta-',
+                            'additionalProperties' => array(
+                                'type' => 'string',
+                            ),
+                        ),
+                        'SSECustomerAlgorithm' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+                        ),
+                        'SSECustomerKeyMD5' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-customer-key-MD5',
+                        ),
+                        'SSEKMSKeyId' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-server-side-encryption-aws-kms-key-id',
+                        ),
+                        'StorageClass' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-storage-class',
+                        ),
+                        'RequestCharged' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-charged',
+                        ),
+                        'ReplicationStatus' => array(
+                            'type' => 'string',
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-replication-status',
+                        ),
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ))),
+                'HeadBucketOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                ),
+                'GetBucketNotificationOutput' => array(
+                    'type' => 'object',
+                    'additionalProperties' => true,
+                    'properties' => array(
+                        'CloudFunctionConfigurations' => array(
+                            'type' => 'array',
+                            'location' => 'xml',
+                            'sentAs' => 'CloudFunctionConfiguration',
+                            'data' => array(
+                                'xmlFlattened' => true,
+                            ),
+                            'items' => array(
+                                'name' => 'CloudFunctionConfiguration',
+                                'type' => 'object',
+                                'sentAs' => 'CloudFunctionConfiguration',
+                                'properties' => array(
+                                    'Id' => array(
+                                        'type' => 'string',
+                                    ),
+                                    'CloudFunction' => array(
+                                        'type' => 'string',
+                                        'sentAs' => 'CloudFunction',
+                                    ),
+                                    'Events' => array(
+                                        'type' => 'array',
+                                        'sentAs' => 'Event',
+                                        'data' => array(
+                                            'xmlFlattened' => true,
+                                        ),
+                                        'items' => array(
+                                            'name' => 'Event',
+                                            'type' => 'string',
+                                            'sentAs' => 'Event',
+                                        ),
+                                    ),
+                                    'Filter' => array(
+                                        'type' => 'object',
+                                        'properties' => array(
+                                            'Key' => array(
+                                                'type' => 'object',
+                                                'sentAs' => 'Key',
+                                                'properties' => array(
+                                                    'FilterRules' => array(
+                                                        'type' => 'array',
+                                                        'sentAs' => 'FilterRule',
+                                                        'data' => array(
+                                                            'xmlFlattened' => true,
+                                                        ),
+                                                        'items' => array(
+                                                            'name' => 'FilterRule',
+                                                            'type' => 'object',
+                                                            'sentAs' => 'FilterRule',
+                                                            'properties' => array(
+                                                                'Name' => array(
+                                                                    'type' => 'string',
+                                                                ),
+                                                                'Value' => array(
+                                                                    'type' => 'string',
+                                                                ),
+                                                            ),
+                                                        ),
+                                                    ),
+                                                ),
+                                            ),
+                                        ),
+                                    ),
+                                ),
+                            ),
+                        ),
+                        'RequestId' => array(
+                            'location' => 'header',
+                            'sentAs' => 'x-cos-request-id',
+                        ),
+                    ),
+                )));
+    }
+}
diff --git a/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Signature.php b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Signature.php
new file mode 100755
index 0000000..2b1b46c
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Signature.php
@@ -0,0 +1,50 @@
+<?php
+namespace Qcloud\Cos;
+use Guzzle\Http\Message\RequestInterface;
+class Signature {
+    private $accessKey;           // string: access key.
+    private $secretKey;           // string: secret key.
+    public function __construct($accessKey, $secretKey) {
+        $this->accessKey = $accessKey;
+        $this->secretKey = $secretKey;
+    }
+    public function __destruct() {
+    }
+    public function signRequest(RequestInterface $request) {
+        $signTime = (string)(time() - 60) . ';' . (string)(time() + 3600);
+        $httpString = strtolower($request->getMethod()) . "\n" . urldecode($request->getPath()) .
+            "\n\nhost=" . $request->getHost() . "\n";
+        $sha1edHttpString = sha1($httpString);
+        $stringToSign = "sha1\n$signTime\n$sha1edHttpString\n";
+        $signKey = hash_hmac('sha1', $signTime, $this->secretKey);
+        $signature = hash_hmac('sha1', $stringToSign, $signKey);
+        $authorization = 'q-sign-algorithm=sha1&q-ak='. $this->accessKey .
+            "&q-sign-time=$signTime&q-key-time=$signTime&q-header-list=host&q-url-param-list=&" .
+            "q-signature=$signature";
+        $request->setHeader('Authorization', $authorization);
+    }
+    public function createAuthorization(
+        RequestInterface $request,
+        $expires = "10 minutes"
+    ) {
+        $signTime = (string)(time() - 60) . ';' . (string)(strtotime($expires));
+        $httpString = strtolower($request->getMethod()) . "\n" . urldecode($request->getPath()) .
+            "\n\nhost=" . $request->getHost() . "\n";
+        $sha1edHttpString = sha1($httpString);
+        $stringToSign = "sha1\n$signTime\n$sha1edHttpString\n";
+        $signKey = hash_hmac('sha1', $signTime, $this->secretKey);
+        $signature = hash_hmac('sha1', $stringToSign, $signKey);
+        $authorization = 'q-sign-algorithm=sha1&q-ak='. $this->accessKey .
+            "&q-sign-time=$signTime&q-key-time=$signTime&q-header-list=host&q-url-param-list=&" .
+            "q-signature=$signature";
+        return $authorization;
+    }
+    public function createPresignedUrl(
+        RequestInterface $request,
+        $expires = "10 minutes"
+    ) {
+        $authorization = $this->createAuthorization($request, $expires);
+        $request->getQuery()->add('sign', $authorization);
+        return $request->getUrl();
+    }
+}
diff --git a/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/SignatureListener.php b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/SignatureListener.php
new file mode 100755
index 0000000..a979583
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/SignatureListener.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Qcloud\Cos;
+
+use Guzzle\Common\Event;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Listener used to sign requests before they are sent over the wire.
+ */
+class SignatureListener implements EventSubscriberInterface {
+    // cos signature.
+    protected $signature;
+
+    /**
+     * Construct a new request signing plugin
+     */
+    public function __construct($accessKey, $secretKey) {
+        $this->signature = new Signature($accessKey, $secretKey);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public static function getSubscribedEvents()
+    {
+        return array(
+            'request.before_send'        => array('onRequestBeforeSend', -255));
+    }
+
+    /**
+     * Signs requests before they are sent
+     *
+     * @param Event $event Event emitted
+     */
+    public function onRequestBeforeSend(Event $event) {
+
+        $this->signature->signRequest($event['request']);
+/*
+        if(!$this->credentials instanceof NullCredentials) {
+            $this->signature->signRequest($event['request'], $this->credentials);
+        }
+*/
+    }
+}
diff --git a/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Tests/Test.php b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Tests/Test.php
new file mode 100755
index 0000000..8b50b18
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Tests/Test.php
@@ -0,0 +1,1347 @@
+<?php
+
+namespace Qcloud\Cos\Tests;
+
+use Qcloud\Cos\Client;
+use Qcloud\Cos\Exception\ServiceResponseException;
+class BucketTest extends \PHPUnit_Framework_TestCase
+{
+    private $cosClient;
+    private $bucket;
+    protected function setUp()
+    {
+        $this->bucket = getenv('COS_BUCKET');
+        TestHelper::nuke($this->bucket);
+        $this->cosClient = new Client(array('region' => getenv('COS_REGION'),
+            'credentials' => array(
+                'appId' => getenv('COS_APPID'),
+                'secretId' => getenv('COS_KEY'),
+                'secretKey' => getenv('COS_SECRET'))));
+        sleep(5);
+    }
+
+    protected function tearDown()
+    {
+        TestHelper::nuke($this->bucket);
+    }
+
+    /**********************************
+     * TestBucket
+     **********************************/
+
+    /*
+     * put bucket,bucket已经存在
+     * BucketAlreadyOwnedByYou
+     * 409
+     */
+    public function testCreateExistingBucket()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+        } catch (ServiceResponseException $e) {
+            $this->assertTrue($e->getExceptionCode() === 'BucketAlreadyOwnedByYou' && $e->getStatusCode() === 409);
+        }
+    }
+
+    /*
+     * put bucket,bucket名称非法
+     * InvalidBucketName
+     * 400
+     */
+    public function testCreateInvalidBucket()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => 'qwe_213'));
+        } catch (ServiceResponseException $e) {
+            $this->assertTrue($e->getExceptionCode() === 'InvalidBucketName' && $e->getStatusCode() === 400);
+        }
+    }
+
+    /*
+     * put bucket,设置bucket公公权限为private
+     * 200
+     */
+    public function testCreatePrivateBucket()
+    {
+        try {
+            $this->cosClient->createBucket(
+                array(
+                    'Bucket' => $this->bucket,
+                    'ACL'=>'private'
+                ));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * put bucket,设置bucket公公权限为public-read
+     * 200
+     */
+    public function testCreatePublicReadBucket()
+    {
+        try {
+            $this->cosClient->createBucket(
+                array(
+                    'Bucket' => $this->bucket,
+                    'ACL'=>'public-read'
+                ));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * put bucket,公共权限非法
+     * InvalidArgument
+     * 400
+     */
+    public function testCreateInvalidACLBucket()
+    {
+        try {
+            $this->cosClient->createBucket(
+                array(
+                    'Bucket' => $this->bucket,
+                    'ACL'=>'public'
+                ));
+        } catch (ServiceResponseException $e) {
+            $this->assertTrue($e->getExceptionCode() === 'InvalidArgument' && $e->getStatusCode() === 400);
+        }
+    }
+
+    /*
+     * put bucket acl,设置bucket公共权限为private
+     * 200
+     */
+    public function testPutBucketAclPrivate()
+    {
+        try {
+
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->PutBucketAcl(
+                array(
+                    'Bucket' => $this->bucket,
+                    'ACL'=>'private'
+                ));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * put bucket acl,设置bucket公共权限为public-read
+     * 200
+     */
+    public function testPutBucketAclPublicRead()
+    {
+        try {
+
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->PutBucketAcl(
+                array(
+                    'Bucket' => $this->bucket,
+                    'ACL'=>'public-read'
+                ));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * put bucket acl,公共权限非法
+     * InvalidArgument
+     * 400
+     */
+    public function testPutBucketAclInvalid()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->PutBucketAcl(
+                array(
+                    'Bucket' => $this->bucket,
+                    'ACL'=>'public'
+                ));
+        } catch (ServiceResponseException $e) {
+            $this->assertTrue($e->getExceptionCode() === 'InvalidArgument' && $e->getStatusCode() === 400);
+        }
+    }
+
+    /*
+     * put bucket acl,设置bucket账号权限为grant-read
+     * 200
+     */
+    public function testPutBucketAclReadToUser()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->PutBucketAcl(array(
+                'Bucket' =>  $this->bucket,
+                'GrantRead' => 'id="qcs::cam::uin/2779643970:uin/2779643970"'));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * put bucket acl,设置bucket账号权限为grant-write
+     * 200
+     */
+    public function testPutBucketAclWriteToUser()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->PutBucketAcl(array(
+                'Bucket' =>  $this->bucket,
+                'GrantWrite' => 'id="qcs::cam::uin/2779643970:uin/2779643970"'));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * put bucket acl,设置bucket账号权限为grant-full-control
+     * 200
+     */
+    public function testPutBucketAclFullToUser()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->PutBucketAcl(array(
+                'Bucket' =>  $this->bucket,
+                'GrantFullControl' => 'id="qcs::cam::uin/2779643970:uin/2779643970"'));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * put bucket acl,设置bucket账号权限,同时授权给多个账户
+     * 200
+     */
+    public function testPutBucketAclToUsers()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->PutBucketAcl(array(
+                'Bucket' =>  $this->bucket,
+                'GrantFullControl' => 'id="qcs::cam::uin/2779643970:uin/2779643970",id="qcs::cam::uin/2779643970:uin/2779643970",id="qcs::cam::uin/2779643970:uin/2779643970"'));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * put bucket acl,设置bucket账号权限,授权给子账号
+     * 200
+     */
+    public function testPutBucketAclToSubuser()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->PutBucketAcl(array(
+                'Bucket' =>  $this->bucket,
+                'GrantFullControl' => 'id="qcs::cam::uin/2779643970:uin/2779643970"'));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * put bucket acl,设置bucket账号权限,同时指定read、write和fullcontrol
+     * 200
+     */
+    public function testPutBucketAclReadWriteFull()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->PutBucketAcl(array(
+                'Bucket' =>  $this->bucket,
+                'GrantRead' => 'id="qcs::cam::uin/123:uin/123"',
+                'GrantWrite' => 'id="qcs::cam::uin/2779643970:uin/2779643970"',
+                'GrantFullControl' => 'id="qcs::cam::uin/2779643970:uin/2779643970"',));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * put bucket acl,设置bucket账号权限,grant值非法
+     * InvalidArgument
+     * 400
+     */
+    public function testPutBucketAclInvalidGrant()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->PutBucketAcl(array(
+                'Bucket' =>  $this->bucket,
+                'GrantFullControl' => 'id="qcs::camuin/321023:uin/2779643970"',));
+        } catch (ServiceResponseException $e) {
+            $this->assertTrue($e->getExceptionCode() === 'InvalidArgument' && $e->getStatusCode() === 400);
+        }
+    }
+
+    /*
+     * put bucket acl,设置bucket账号权限,通过body方式授权
+     * 200
+     */
+    public function testPutBucketAclByBody()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->PutBucketAcl(array(
+                'Bucket' => $this->bucket,
+                'Grants' => array(
+                    array(
+                        'Grantee' => array(
+                            'DisplayName' => 'qcs::cam::uin/2779643970:uin/2779643970',
+                            'ID' => 'qcs::cam::uin/2779643970:uin/2779643970',
+                            'Type' => 'CanonicalUser',
+                        ),
+                        'Permission' => 'FULL_CONTROL',
+                    ),
+                    // ... repeated
+                ),
+                'Owner' => array(
+                    'DisplayName' => 'qcs::cam::uin/2779643970:uin/2779643970',
+                    'ID' => 'qcs::cam::uin/2779643970:uin/2779643970',
+                )));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * put bucket acl,设置bucket账号权限,通过body方式授权给anyone
+     * 200
+     */
+    public function testPutBucketAclByBodyToAnyone()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->PutBucketAcl(array(
+                'Bucket' => $this->bucket,
+                'Grants' => array(
+                    array(
+                        'Grantee' => array(
+                            'DisplayName' => 'qcs::cam::anyone:anyone',
+                            'ID' => 'qcs::cam::anyone:anyone',
+                            'Type' => 'CanonicalUser',
+                        ),
+                        'Permission' => 'FULL_CONTROL',
+                    ),
+                    // ... repeated
+                ),
+                'Owner' => array(
+                    'DisplayName' => 'qcs::cam::uin/2779643970:uin/2779643970',
+                    'ID' => 'qcs::cam::uin/2779643970:uin/2779643970',
+                )));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * put bucket acl,bucket不存在
+     * NoSuchBucket
+     * 404
+     */
+    public function testPutBucketAclBucketNonexisted()
+    {
+        try {
+            $this->cosClient->PutBucketAcl(array(
+                'Bucket' =>  $this->bucket,
+                'GrantFullControl' => 'id="qcs::cam::uin/321023:uin/2779643970"',));
+        } catch (ServiceResponseException $e) {
+//            echo($e->getExceptionCode());
+//            echo($e->getStatusCode());
+            $this->assertTrue($e->getExceptionCode() === 'NoSuchBucket' && $e->getStatusCode() === 404);
+        }
+    }
+
+    /*
+     * put bucket acl,覆盖设置
+     * x200
+     */
+    public function testPutBucketAclCover()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->PutBucketAcl(array(
+                'Bucket' =>  $this->bucket,
+                'GrantFullControl' => 'id="qcs::cam::uin/2779643970:uin/2779643970"',
+                'GrantRead' => 'id="qcs::cam::uin/2779643970:uin/2779643970"',
+                'GrantWrite' => 'id="qcs::cam::uin/2779643970:uin/2779643970"'));
+            $this->cosClient->PutBucketAcl(array(
+                'Bucket' =>  $this->bucket,
+                'GrantWrite' => 'id="qcs::cam::uin/2779643970:uin/2779643970"'));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * 正常head bucket
+     * 200
+     */
+    public function testHeadBucket()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->HeadBucket(array(
+                'Bucket' =>  $this->bucket));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * head bucket,bucket不存在
+     * NoSuchBucket
+     * 404
+     */
+    public function testHeadBucketNonexisted()
+    {
+        try {
+            $this->cosClient->HeadBucket(array(
+                'Bucket' =>  $this->bucket,));
+        } catch (ServiceResponseException $e) {
+//            echo($e->getExceptionCode());
+//            echo($e->getStatusCode());
+            $this->assertTrue($e->getExceptionCode() === 'NoSuchBucket' && $e->getStatusCode() === 404);
+        }
+    }
+
+    /*
+     * get bucket,bucket为空
+     * 200
+     */
+    public function testGetBucketEmpty()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->ListObjects(array(
+                'Bucket' =>  $this->bucket));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * get bucket,bucket不存在
+     * NoSuchBucket
+     * 404
+     */
+    public function testGetBucketNonexisted()
+    {
+        try {
+            $this->cosClient->ListObjects(array(
+                'Bucket' =>  $this->bucket,));
+        } catch (ServiceResponseException $e) {
+            $this->assertTrue($e->getExceptionCode() === 'NoSuchBucket' && $e->getStatusCode() === 404);
+        }
+    }
+
+
+    /*
+     * put bucket cors,cors规则包含多条
+     * 200
+     */
+    public function testPutBucketCors()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->putBucketCors(array(
+                // Bucket is required
+                'Bucket' => $this->bucket,
+                // CORSRules is required
+                'CORSRules' => array(
+                    array(
+                        'ID' => '1234',
+                        'AllowedHeaders' => array('*',),
+                        // AllowedMethods is required
+                        'AllowedMethods' => array('PUT',),
+                        // AllowedOrigins is required
+                        'AllowedOrigins' => array('*',),
+                        'ExposeHeaders' => array('*',),
+                        'MaxAgeSeconds' => 1,
+                    ),
+                    array(
+                        'ID' => '12345',
+                        'AllowedHeaders' => array('*',),
+                        // AllowedMethods is required
+                        'AllowedMethods' => array('PUT',),
+                        // AllowedOrigins is required
+                        'AllowedOrigins' => array('*',),
+                        'ExposeHeaders' => array('*',),
+                        'MaxAgeSeconds' => 1,
+                    ),
+                    // ... repeated
+                ),
+            ));
+            $this->cosClient->getBucketCors(array(
+                // Bucket is required
+                'Bucket' => $this->bucket,));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+
+    /*
+     * 正常get bucket cors
+     * 200
+     */
+    public function testGetBucketCors()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->putBucketCors(array(
+                // Bucket is required
+                'Bucket' => $this->bucket,
+                // CORSRules is required
+                'CORSRules' => array(
+                    array(
+                        'ID' => '1234',
+                        'AllowedHeaders' => array('*',),
+                        // AllowedMethods is required
+                        'AllowedMethods' => array('PUT',),
+                        // AllowedOrigins is required
+                        'AllowedOrigins' => array('*',),
+                        'ExposeHeaders' => array('*',),
+                        'MaxAgeSeconds' => 1,
+                    ),
+                    array(
+                        'ID' => '12345',
+                        'AllowedHeaders' => array('*',),
+                        // AllowedMethods is required
+                        'AllowedMethods' => array('PUT',),
+                        // AllowedOrigins is required
+                        'AllowedOrigins' => array('*',),
+                        'ExposeHeaders' => array('*',),
+                        'MaxAgeSeconds' => 1,
+                    ),
+                    // ... repeated
+                ),
+            ));
+            $this->cosClient->getBucketCors(array(
+                // Bucket is required
+                'Bucket' => $this->bucket,));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * bucket未设置cors规则,发送get bucket cors
+     * NoSuchCORSConfiguration
+     * 404
+     */
+    public function testGetBucketCorsNull()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->getBucketCors(array(
+                // Bucket is required
+                'Bucket' => $this->bucket,));
+        } catch (ServiceResponseException $e) {
+//            echo($e->getExceptionCode());
+//            echo($e->getStatusCode());
+            $this->assertTrue($e->getExceptionCode() === 'NoSuchCORSConfiguration' && $e->getStatusCode() === 404);
+        }
+    }
+
+    /*
+     * bucket未设置cors规则,发送get bucket cors
+     * NoSuchCORSConfiguration
+     * 404
+     */
+    public function testGetBucketCorsNonExisted()
+    {
+        try {
+            $this->cosClient->getBucketCors(array(
+                // Bucket is required
+                'Bucket' => $this->bucket,));
+        } catch (ServiceResponseException $e) {
+//            echo($e->getExceptionCode());
+//            echo($e->getStatusCode());
+            $this->assertTrue($e->getExceptionCode() === 'NoSuchBucket' && $e->getStatusCode() === 404);
+        }
+    }
+
+    /*
+     * 正常get bucket lifecycle
+     * 200
+     */
+    public function testGetBucketLifecycle()
+    {
+        try {
+            $result = $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $result = $this->cosClient->putBucketLifecycle(array(
+                    'Bucket' => $this->bucket,
+                    'Rules' => array(
+                        array(
+                            'Status' => 'Enabled',
+                            'Filter' => array(
+                                'Tag' => array(
+                                    'Key' => 'datalevel',
+                                    'Value' => 'backup'
+                                )
+                            ),
+                            'Transitions' => array(
+                                array(
+                                    # 30天后转换为Standard_IA
+                                    'Days' => 30,
+                                    'StorageClass' => 'Standard_IA'),
+                                array(
+                                    # 365天后转换为Archive
+                                    'Days' => 365,
+                                    'StorageClass' => 'Archive')
+                            ),
+                            'Expiration' => array(
+                                # 3650天后过期删除
+                                'Days' => 3650,
+                            )
+                        )
+                    )
+                ));
+            sleep(3);
+            $result = $this->cosClient->getBucketLifecycle(array(
+                // Bucket is required
+                'Bucket' => $this->bucket,
+            ));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * 正常delete bucket lifecycle
+     * 200
+     */
+    public function testDeleteBucketLifecycle()
+    {
+        try {
+            $result = $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $result = $this->cosClient->putBucketLifecycle(array(
+                'Bucket' => $this->bucket,
+                'Rules' => array(
+                    array(
+                        'Status' => 'Enabled',
+                        'Filter' => array(
+                            'Tag' => array(
+                                'Key' => 'datalevel',
+                                'Value' => 'backup'
+                            )
+                        ),
+                        'Transitions' => array(
+                            array(
+                                # 30天后转换为Standard_IA
+                                'Days' => 30,
+                                'StorageClass' => 'Standard_IA'),
+                            array(
+                                # 365天后转换为Archive
+                                'Days' => 365,
+                                'StorageClass' => 'Archive')
+                        ),
+                        'Expiration' => array(
+                            # 3650天后过期删除
+                            'Days' => 3650,
+                        )
+                    )
+                )
+            ));
+            $result = $this->cosClient->deleteBucketLifecycle(array(
+                // Bucket is required
+                'Bucket' => $this->bucket,
+            ));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * put bucket lifecycle,请求body中不指定filter
+     * 200
+     */
+    public function testPutBucketLifecycleNonFilter()
+    {
+        try {
+            $result = $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $result = $this->cosClient->putBucketLifecycle(array(
+                // Bucket is required
+                'Bucket' => $this->bucket,
+                // Rules is required
+                'Rules' => array(
+                    array(
+                        'Expiration' => array(
+                            'Days' => 1000,
+                        ),
+                        'ID' => 'id1',
+                        // Status is required
+                        'Status' => 'Enabled',
+                        'Transitions' => array(
+                            array(
+                                'Days' => 100,
+                                'StorageClass' => 'Standard_IA'),
+                        ),
+                        // ... repeated
+                    ),
+                )));
+        } catch (ServiceResponseException $e) {
+            $this->assertTrue($e->getExceptionCode() === 'NoSuchBucket' && $e->getStatusCode() === 404);
+
+        }
+    }
+
+    /*
+     * put bucket,bucket名称带有-
+     * 200
+     */
+    public function testPutBucket2()
+    {
+        try {
+            try{
+                $this->cosClient->deleteBucket(array('Bucket' => '12345-'.$this->bucket));
+            } catch (\Exception $e) {
+            }
+            $this->cosClient->createBucket(array('Bucket' => '12345-'.$this->bucket));
+            $this->cosClient->deleteBucket(array('Bucket' => '12345-'.$this->bucket));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * put bucket,bucket名称带有两个-
+     * 200
+     */
+    public function testPutBucket3()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket.'-12333-4445'));
+            $this->cosClient->deleteBucket(array('Bucket' => $this->bucket.'-12333-4445'));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * 正常get bucket location
+     * 200
+     */
+        public function testGetBucketLocation()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->getBucketLocation(array('Bucket' => $this->bucket));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * bucket不存在,发送get bucket location请求
+     * NoSuchBucket
+     * 404
+     */
+    public function testGetBucketLocationNonExisted()
+    {
+        try {
+            $this->cosClient->getBucketLocation(array('Bucket' => $this->bucket));
+        } catch (ServiceResponseException $e) {
+            //            echo($e->getExceptionCode());
+            //            echo($e->getStatusCode());
+            $this->assertTrue($e->getExceptionCode() === 'NoSuchBucket' && $e->getStatusCode() === 404);
+        }
+    }
+
+    /**********************************
+     * TestObject
+     **********************************/
+
+    /*
+     * put object,请求头部携带服务端加密参数
+     * 200
+     */
+    public function testPutObjectEncryption()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->putObject(array(
+                'Bucket' => $this->bucket,
+                'Key' => '11//32//43',
+                'Body' => 'Hello World!',
+                'ServerSideEncryption' => 'AES256'));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * 上传文件Bucket不存在
+     * NoSuchBucket
+     * 404
+     */
+    public function testPutObjectIntoNonexistedBucket() {
+        try {
+            $this->cosClient->putObject(array(
+                'Bucket' => $this->bucket, 'Key' => 'hello.txt', 'Body' => 'Hello World'));
+        } catch (ServiceResponseException $e) {
+            $this->assertTrue($e->getExceptionCode() === 'NoSuchBucket');
+            $this->assertTrue($e->getStatusCode() === 404);
+        }
+    }
+
+
+    /*
+     * 上传小文件
+     * 200
+     */
+    public function testUploadSmallObject() {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->upload($this->bucket, '你好.txt', 'Hello World');
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * 上传空文件
+     * 200
+     */
+    public function testPutObjectEmpty() {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->upload($this->bucket, '你好.txt', '123');
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * 上传已存在的文件
+     * 200
+     */
+    public function testPutObjectExisted() {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->upload($this->bucket, '你好.txt', '1234124');
+            $this->cosClient->upload($this->bucket, '你好.txt', '请二位qwe');
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * put object,请求头部携带自定义头部x-cos-meta-
+     * 200
+     */
+    public function testPutObjectMeta() {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->putObject(array(
+                'Bucket' => $this->bucket,
+                'Key' => '你好.txt',
+                'Body' => '1234124',
+                'Metadata' => array(
+                     'lew' => str_repeat('a', 1 * 1024),
+            )));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * put object,请求头部携带自定义头部x-cos-meta-
+     * KeyTooLong
+     * 400
+     */
+    public function testPutObjectMeta2K() {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->putObject(array(
+                'Bucket' => $this->bucket,
+                'Key' => '你好.txt',
+                'Body' => '1234124',
+                'Metadata' => array(
+                    'lew' => str_repeat('a', 3 * 1024),
+                )));
+        } catch (ServiceResponseException $e) {
+//            echo($e->getExceptionCode());
+//            echo($e->getStatusCode());
+            $this->assertTrue($e->getExceptionCode() === 'KeyTooLong' && $e->getStatusCode() === 400);
+        }
+    }
+
+    /*
+     * 上传复杂文件名的文件
+     * 200
+     */
+    public function testUploadComplexObject() {
+        try {
+            $result = $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->upload($this->bucket, '→↓←→↖↗↙↘! \"#$%&\'()*+,-./0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~', 'Hello World');
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * 上传大文件
+     * 200
+     */
+    public function testUploadLargeObject() {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->upload($this->bucket, 'hello.txt', str_repeat('a', 9 * 1024 * 1024));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * 下载文件
+     * 200
+     */
+    public function testGetObject() {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->upload($this->bucket, '你好.txt', 'Hello World');
+            $this->cosClient->getObject(array(
+                'Bucket' => $this->bucket,
+                'Key' => '你好.txt',));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * get object,object名称包含特殊字符
+     * 200
+     */
+    public function testGetObjectSpecialName() {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->upload($this->bucket, '你好<>!@#^%^&*&(&^!@#@!.txt', 'Hello World');
+            $this->cosClient->getObject(array(
+                'Bucket' => $this->bucket,
+                'Key' => '你好<>!@#^%^&*&(&^!@#@!.txt',));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * get object,请求头部带if-match,参数值为true
+     * 200
+     */
+    public function testGetObjectIfMatchTrue() {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->upload($this->bucket, '你好.txt', 'Hello World');
+            $this->cosClient->getObject(array(
+                'Bucket' => $this->bucket,
+                'Key' => '你好.txt',
+                'IfMatch' => '"b10a8db164e0754105b7a99be72e3fe5"'));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+
+    /*
+     * get object,请求头部带if-match,参数值为false
+     * PreconditionFailed
+     * 412
+     */
+    public function testGetObjectIfMatchFalse() {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->upload($this->bucket, '你好.txt', 'Hello World');
+            $this->cosClient->getObject(array(
+                'Bucket' => $this->bucket,
+                'Key' => '你好.txt',
+                'IfMatch' => '""'));
+        } catch (ServiceResponseException $e) {
+//            echo($e->getExceptionCode());
+//            echo($e->getStatusCode());
+            $this->assertTrue($e->getExceptionCode() === 'PreconditionFailed' && $e->getStatusCode() === 412);
+        }
+    }
+
+    /*
+     * get object,请求头部带if-none-match,参数值为true
+     * 200
+     */
+    public function testGetObjectIfNoneMatchTrue() {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->upload($this->bucket, '你好.txt', 'Hello World');
+            $this->cosClient->getObject(array(
+                'Bucket' => $this->bucket,
+                'Key' => '你好.txt',
+                'IfNoneMatch' => '"b10a8db164e0754105b7a99be72e3fe5"'));
+        } catch (ServiceResponseException $e) {
+            $this->assertTrue($e->getExceptionCode() === 'NotModified' && $e->getStatusCode() === 304);
+        }
+    }
+
+
+    /*
+     * get object,请求头部带if-none-match,参数值为false
+     * PreconditionFailed
+     * 412
+     */
+    public function testGetObjectIfNoneMatchFalse() {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->upload($this->bucket, '你好.txt', 'Hello World');
+            $this->cosClient->getObject(array(
+                'Bucket' => $this->bucket,
+                'Key' => '你好.txt',
+                'IfNoneMatch' => '""'));
+
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * 获取文件url
+     * 200
+     */
+    public function testGetObjectUrl() {
+        try{
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->getObjectUrl($this->bucket, 'hello.txt', '+10 minutes');
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * 设置objectacl
+     * 200
+     */
+    public function testPutObjectACL() {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->upload($this->bucket, '11', 'hello.txt');
+            $this->cosClient->PutObjectAcl(array(
+                'Bucket' => $this->bucket,
+                'Key' => '11',
+                'Grants' => array(
+                    array(
+                        'Grantee' => array(
+                            'DisplayName' => 'qcs::cam::uin/2779643970:uin/2779643970',
+                            'ID' => 'qcs::cam::uin/2779643970:uin/2779643970',
+                            'Type' => 'CanonicalUser',
+                        ),
+                        'Permission' => 'FULL_CONTROL',
+                    ),
+                    // ... repeated
+                ),
+                'Owner' => array(
+                    'DisplayName' => 'qcs::cam::uin/2779643970:uin/2779643970',
+                    'ID' => 'qcs::cam::uin/2779643970:uin/2779643970',
+                )));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+
+    }
+
+
+    /*
+     * 获取objectacl
+     * 200
+     */
+    public function testGetObjectACL()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->upload($this->bucket, '11', 'hello.txt');
+            $this->cosClient->PutObjectAcl(array(
+                'Bucket' => $this->bucket,
+                'Key' => '11',
+                'Grants' => array(
+                    array(
+                        'Grantee' => array(
+                            'DisplayName' => 'qcs::cam::uin/2779643970:uin/2779643970',
+                            'ID' => 'qcs::cam::uin/2779643970:uin/2779643970',
+                            'Type' => 'CanonicalUser',
+                        ),
+                        'Permission' => 'FULL_CONTROL',
+                    ),
+                    // ... repeated
+                ),
+                'Owner' => array(
+                    'DisplayName' => 'qcs::cam::uin/2779643970:uin/2779643970',
+                    'ID' => 'qcs::cam::uin/2779643970:uin/2779643970',
+                )));
+
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+        * put object acl,设置object公共权限为private
+        * 200
+        */
+    public function testPutObjectAclPrivate()
+    {
+        try {
+
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->putObject(array('Bucket' => $this->bucket,'Key' => '你好.txt', 'Body' => '123'));
+            $this->cosClient->PutObjectAcl(
+                array(
+                    'Bucket' => $this->bucket,
+                    'Key' => '你好.txt',
+                    'ACL'=>'private'
+                ));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * put object acl,设置object公共权限为public-read
+     * 200
+     */
+    public function testPutObjectAclPublicRead()
+    {
+        try {
+
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->putObject(array('Bucket' => $this->bucket,'Key' => '你好.txt', 'Body' => '123'));
+            $this->cosClient->PutObjectAcl(
+                array(
+                    'Bucket' => $this->bucket,
+                    'Key' => '你好.txt',
+                    'ACL'=>'public-read'
+                ));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * put object acl,公共权限非法
+     * InvalidArgument
+     * 400
+     */
+    public function testPutObjectAclInvalid()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->putObject(array('Bucket' => $this->bucket,'Key' => '你好.txt', 'Body' => '123'));
+            $this->cosClient->PutObjectAcl(
+                array(
+                    'Bucket' => $this->bucket,
+                    'Key' => '你好.txt',
+                    'ACL'=>'public'
+                ));
+        } catch (ServiceResponseException $e) {
+            $this->assertTrue($e->getExceptionCode() === 'InvalidArgument' && $e->getStatusCode() === 400);
+        }
+    }
+
+    /*
+     * put object acl,设置object账号权限为grant-read
+     * 200
+     */
+    public function testPutObjectAclReadToUser()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->putObject(array('Bucket' => $this->bucket,'Key' => '你好.txt', 'Body' => '123'));
+            $this->cosClient->PutObjectAcl(array(
+                'Bucket' =>  $this->bucket,
+                'Key' => '你好.txt',
+                'GrantRead' => 'id="qcs::cam::uin/2779643970:uin/2779643970"'));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * put object acl,设置object账号权限为grant-write
+     * 200
+     */
+//    public function testPutObjectAclWriteToUser()
+//    {
+//        try {
+//            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+//            $this->cosClient->putObject(array('Bucket' => $this->bucket,'Key' => '你好.txt', 'Body' => '123'));
+//            $this->cosClient->PutObjectAcl(array(
+//                'Bucket' =>  $this->bucket,
+//                'Key' => '你好.txt',
+//                'GrantWrite' => 'id="qcs::cam::uin/2779643970:uin/2779643970"'));
+//        } catch (ServiceResponseException $e) {
+//            $this->assertFalse(true, $e);
+//        }
+//    }
+
+    /*
+     * put object acl,设置object账号权限为grant-full-control
+     * 200
+     */
+    public function testPutObjectAclFullToUser()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->putObject(array('Bucket' => $this->bucket,'Key' => '你好.txt', 'Body' => '123'));
+            $this->cosClient->PutObjectAcl(array(
+                'Bucket' =>  $this->bucket,
+                'Key' => '你好.txt',
+                'GrantFullControl' => 'id="qcs::cam::uin/2779643970:uin/2779643970"'));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * put object acl,设置object账号权限,同时授权给多个账户
+     * 200
+     */
+    public function testPutObjectAclToUsers()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->putObject(array('Bucket' => $this->bucket,'Key' => '你好.txt', 'Body' => '123'));
+            $this->cosClient->PutObjectAcl(array(
+                'Bucket' =>  $this->bucket,
+                'Key' => '你好.txt',
+                'GrantFullControl' => 'id="qcs::cam::uin/2779643970:uin/2779643970",id="qcs::cam::uin/2779643970:uin/2779643970",id="qcs::cam::uin/2779643970:uin/2779643970"'));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * put object acl,设置object账号权限,授权给子账号
+     * 200
+     */
+    public function testPutObjectAclToSubuser()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->putObject(array('Bucket' => $this->bucket,'Key' => '你好.txt', 'Body' => '123'));
+            $this->cosClient->PutObjectAcl(array(
+                'Bucket' =>  $this->bucket,
+                'Key' => '你好.txt',
+                'GrantFullControl' => 'id="qcs::cam::uin/2779643970:uin/2779643970"'));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * put object acl,设置object账号权限,同时指定read、write和fullcontrol
+     * 200
+     */
+//    public function testPutObjectAclReadWriteFull()
+//    {
+//        try {
+//            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+//            $this->cosClient->putObject(array('Bucket' => $this->bucket,'Key' => '你好.txt', 'Body' => '123'));
+//            $this->cosClient->PutObjectAcl(array(
+//                'Bucket' =>  $this->bucket,
+//                'Key' => '你好.txt',
+//                'GrantRead' => 'id="qcs::cam::uin/123:uin/123"',
+//                'GrantWrite' => 'id="qcs::cam::uin/2779643970:uin/2779643970"',
+//                'GrantFullControl' => 'id="qcs::cam::uin/2779643970:uin/2779643970"',));
+//        } catch (ServiceResponseException $e) {
+//            $this->assertFalse(true, $e);
+//        }
+//    }
+
+    /*
+     * put object acl,设置object账号权限,grant值非法
+     * InvalidArgument
+     * 400
+     */
+    public function testPutObjectAclInvalidGrant()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->putObject(array('Bucket' => $this->bucket,'Key' => '你好.txt', 'Body' => '123'));
+            $this->cosClient->PutObjectAcl(array(
+                'Bucket' =>  $this->bucket,
+                'Key' => '你好.txt',
+                'GrantFullControl' => 'id="qcs::camuin/321023:uin/2779643970"',));
+        } catch (ServiceResponseException $e) {
+            $this->assertTrue($e->getExceptionCode() === 'InvalidArgument' && $e->getStatusCode() === 400);
+        }
+    }
+
+    /*
+     * put object acl,设置object账号权限,通过body方式授权
+     * 200
+     */
+    public function testPutObjectAclByBody()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->putObject(array('Bucket' => $this->bucket,'Key' => '你好.txt', 'Body' => '123'));
+            $this->cosClient->PutObjectAcl(array(
+                'Bucket' => $this->bucket,
+                'Key' => '你好.txt',
+                'Grants' => array(
+                    array(
+                        'Grantee' => array(
+                            'DisplayName' => 'qcs::cam::uin/2779643970:uin/2779643970',
+                            'ID' => 'qcs::cam::uin/2779643970:uin/2779643970',
+                            'Type' => 'CanonicalUser',
+                        ),
+                        'Permission' => 'FULL_CONTROL',
+                    ),
+                    // ... repeated
+                ),
+                'Owner' => array(
+                    'DisplayName' => 'qcs::cam::uin/2779643970:uin/2779643970',
+                    'ID' => 'qcs::cam::uin/2779643970:uin/2779643970',
+                )));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+    /*
+     * put object acl,设置object账号权限,通过body方式授权给anyone
+     * 200
+     */
+    public function testPutObjectAclByBodyToAnyone()
+    {
+        try {
+            $this->cosClient->createBucket(array('Bucket' => $this->bucket));
+            $this->cosClient->putObject(array('Bucket' => $this->bucket,'Key' => '你好.txt', 'Body' => '123'));
+            $this->cosClient->putObjectAcl(array(
+                'Bucket' => $this->bucket,
+                'Key' => '你好.txt',
+                'Grants' => array(
+                    array(
+                        'Grantee' => array(
+                            'DisplayName' => 'qcs::cam::anyone:anyone',
+                            'ID' => 'qcs::cam::anyone:anyone',
+                            'Type' => 'CanonicalUser',
+                        ),
+                        'Permission' => 'FULL_CONTROL',
+                    ),
+                    // ... repeated
+                ),
+                'Owner' => array(
+                    'DisplayName' => 'qcs::cam::uin/2779643970:uin/2779643970',
+                    'ID' => 'qcs::cam::uin/2779643970:uin/2779643970',
+                )));
+        } catch (ServiceResponseException $e) {
+            $this->assertFalse(true, $e);
+        }
+    }
+
+}
diff --git a/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Tests/TestHelper.php b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Tests/TestHelper.php
new file mode 100755
index 0000000..367b887
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Tests/TestHelper.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace Qcloud\Cos\Tests;
+
+use Qcloud\Cos\Client;
+
+class TestHelper {
+
+    public static function nuke($bucket) {
+        try {
+            $cosClient = new Client(array('region' => getenv('COS_REGION'),
+                        'credentials'=> array(
+                        'appId' => getenv('COS_APPID'),
+                        'secretId'    => getenv('COS_KEY'),
+                        'secretKey' => getenv('COS_SECRET'))));
+            $result = $cosClient->listObjects(array('Bucket' => $bucket));
+            if ($result->get('Contents')) {
+                foreach ($result ->get('Contents') as $content) {
+                    $cosClient->deleteObject(array('Bucket' => $bucket, 'Key' => $content['Key']));
+                }
+            }
+            $cosClient->deleteBucket(array('Bucket' => $bucket));
+
+            while(True){
+                $result = $cosClient->ListMultipartUploads(
+                    array('Bucket' => $bucket,
+                        'Prefix' => ''));
+                if (count($result['Uploads']) == 0){
+                    break;
+                }
+                foreach ($result['Uploads'] as $upload) {
+                    try {
+                        $rt = $cosClient->AbortMultipartUpload(
+                            array('Bucket' => $bucket,
+                                'Key' => $upload['Key'],
+                                'UploadId' => $upload['UploadId']));
+                        print_r($rt);
+                    } catch (\Exception $e) {
+                        print_r($e);
+                    }
+                }
+            }
+        } catch (\Exception $e) {
+            //echo "$e\n";
+            // Ignore
+        }
+    }
+}
diff --git a/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/TokenListener.php b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/TokenListener.php
new file mode 100755
index 0000000..8bde999
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/TokenListener.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace Qcloud\Cos;
+
+use Aws\Common\Credentials\CredentialsInterface;
+use Aws\Common\Credentials\NullCredentials;
+use Guzzle\Common\Event;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Listener used to sign requests before they are sent over the wire.
+ */
+class TokenListener implements EventSubscriberInterface {
+    // cos signature.
+    protected $token;
+
+    /**
+     * Construct a new request signing plugin
+     */
+    public function __construct($token) {
+        $this->token = $token;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public static function getSubscribedEvents()
+    {
+        return array(
+            'request.before_send'        => array('onRequestBeforeSend', -240));
+    }
+
+    /**
+     * Signs requests before they are sent
+     *
+     * @param Event $event Event emitted
+     */
+    public function onRequestBeforeSend(Event $event) {
+        if ($this->token != null) {
+            $event['request']->setHeader('x-cos-security-token', $this->token);
+        }
+/*
+        if(!$this->credentials instanceof NullCredentials) {
+            $this->signature->signRequest($event['request'], $this->credentials);
+        }
+*/
+    }
+}
diff --git a/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/UploadBodyListener.php b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/UploadBodyListener.php
new file mode 100755
index 0000000..f318029
--- /dev/null
+++ b/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/UploadBodyListener.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace Qcloud\Cos;
+
+use Qcloud\Cos\Exception\InvalidArgumentException;
+use Guzzle\Common\Event;
+use Guzzle\Http\EntityBody;
+use Guzzle\Service\Command\AbstractCommand as Command;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Prepares the body parameter of a command such that the parameter is more flexible
+ * (e.g. accepts file handles) with the value it accepts but converts it to the correct format
+ * for the command. Also looks for a "Filename" parameter.
+ */
+class UploadBodyListener implements EventSubscriberInterface {
+    /**
+     * @var array The names of the commands of which to modify the body parameter
+     */
+    protected $commands;
+
+    /**
+     * @var string The key for the upload body parameter
+     */
+    protected $bodyParameter;
+
+    /**
+     * @var string The key for the source file parameter
+     */
+    protected $sourceParameter;
+
+    /**
+     * @param array  $commands        The commands to modify
+     * @param string $bodyParameter   The key for the body parameter
+     * @param string $sourceParameter The key for the source file parameter
+     */
+    public function __construct(array $commands, $bodyParameter = 'Body', $sourceParameter = 'SourceFile') {
+        $this->commands = $commands;
+        $this->bodyParameter = (string) $bodyParameter;
+        $this->sourceParameter = (string) $sourceParameter;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public static function getSubscribedEvents() {
+        return array('command.before_prepare' => array('onCommandBeforePrepare'));
+    }
+
+    /**
+     * Converts filenames and file handles into EntityBody objects before the command is validated
+     *
+     * @param Event $event Event emitted
+     * @throws InvalidArgumentException
+     */
+    public function onCommandBeforePrepare(Event $event) {
+        /** @var Command $command */
+        $command = $event['command'];
+        if (in_array($command->getName(), $this->commands)) {
+            // Get the interesting parameters
+            $source = $command->get($this->sourceParameter);
+            $body = $command->get($this->bodyParameter);
+
+            // If a file path is passed in then get the file handle
+            if (is_string($source) && file_exists($source)) {
+                $body = fopen($source, 'rb');
+            }
+
+            // Prepare the body parameter and remove the source file parameter
+            if (null !== $body) {
+                $command->remove($this->sourceParameter);
+                $command->set($this->bodyParameter, EntityBody::factory($body));
+            } else {
+                throw new InvalidArgumentException(
+                        "You must specify a non-null value for the {$this->bodyParameter} or {$this->sourceParameter} parameters.");
+            }
+        }
+    }
+}
diff --git a/vendor/qcloud_sts/qcloud-sts-sdk/.gitignore b/vendor/qcloud_sts/qcloud-sts-sdk/.gitignore
new file mode 100755
index 0000000..a67d42b
--- /dev/null
+++ b/vendor/qcloud_sts/qcloud-sts-sdk/.gitignore
@@ -0,0 +1,6 @@
+composer.phar
+/vendor/
+
+# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control
+# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
+# composer.lock
diff --git a/vendor/qcloud_sts/qcloud-sts-sdk/LICENSE b/vendor/qcloud_sts/qcloud-sts-sdk/LICENSE
new file mode 100755
index 0000000..2a0387f
--- /dev/null
+++ b/vendor/qcloud_sts/qcloud-sts-sdk/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 腾讯云
+
+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.
diff --git a/vendor/qcloud_sts/qcloud-sts-sdk/README.md b/vendor/qcloud_sts/qcloud-sts-sdk/README.md
new file mode 100755
index 0000000..a4fdc66
--- /dev/null
+++ b/vendor/qcloud_sts/qcloud-sts-sdk/README.md
@@ -0,0 +1,61 @@
+## 获取 SDK
+
+- composer 安装
+```
+创建composer.json的文件,内容如下:
+{
+    "require":{
+        "qcloud_sts/qcloud-sts-sdk": "3.0.*"
+    }
+}
+```
+
+## 查看示例
+
+请查看 [demo 示例](https://github.com/tencentyun/qcloud-cos-sts-sdk/tree/master/php/demo),里面描述了如何调用SDK。
+
+## 接口说明
+
+### getTempKeys
+
+获取临时密钥接口
+
+### 参数说明
+
+|字段|类型|描述|
+| ---- | ---- | ---- |
+|secretId|string| 云 API 密钥 Id|
+|secretKey|string| 云 API 密钥 key|
+|durationSeconds|long| 要申请的临时密钥最长有效时间,单位秒,默认 1800,最大可设置 7200 |
+|bucket|string| 存储桶名称:bucketName-appid, 如 test-125000000|
+|region|string| 存储桶所属地域,如 ap-guangzhou|
+|allowPrefix|string|资源的前缀,如授予操作所有资源,则为`*`;如授予操作某个路径a下的所有资源,则为 `a/*`,如授予只能操作特定的文件a/test.jpg, 则为`a/test.jpg`|
+|allowActions|array| 授予 COS API 权限集合, 如简单上传操作:name/cos:PutObject|
+|policy|array| 策略:由 allowActions、bucket、region、allowPrefix字段组成的描述授权的具体信息|
+
+### 返回值说明
+
+|字段|类型|描述|
+| ---- | ---- | ---- |
+|credentials | string | 临时密钥信息 |
+|tmpSecretId | string | 临时密钥 Id,可用于计算签名 |
+|tmpSecretKey | string | 临时密钥 Key,可用于计算签名 |
+|sessionToken | string | 请求时需要用的 token 字符串,最终请求 COS API 时,需要放在 Header 的 x-cos-security-token 字段 |
+|startTime | string | 密钥的起止时间,是 UNIX 时间戳 |
+|expiredTime | string | 密钥的失效时间,是 UNIX 时间戳 |
+
+### 返回数据示例
+
+```
+{
+	"expiredTime": 1589258683,
+	"expiration": "2020-05-12T04:44:43Z",
+	"credentials": {
+		"sessionToken": "Biypn6exa48PpMe7wFerEnNMpBKKPQo180c57e0a5275ebae506d7851a85f36a4P0TV5UFR3FYJjsoZA1tk6uRKoDRzc6-60BmwLqdS75OhjHEa7GlVYpL_ofKQJTpPKziKX7FnI10D_6qtLdjzf2NdsyUtQEd5kPpDCOQJZn9-BpleqWQe8oyH_2u7xi2f0FtjCYaoGIZ_lUqlILXQwr0B0t3hLfL4xNE-EmVjUlUXa16HxVCn4_hJetqo9LmI0AgLOjCbYx9aVrsV10eDsRta-TQSIXmJNP3aJ6oz8d8GBTgTE1I2qSFDnv9pjtQKW8HZWI_glPIfmHXCCwAESxEFL_owGz839Va0qYhF6LkfVmsuoU1zNcvJR1w3cIE6izV3SKHaOtWaew3IOervuOPoN3S2oYGNwv2EavtDAWyUBIeI7X6nMVzlpnyJ-3bkIhOq9QVIQAs8wh5A0u9mvKWugT5t6qgyEgvEZSj9k6p-JjwxMgLC6s5uK1i_nnf4fN7ZQ6I-JAfHnH4jEDiVtJgXqfuWPX_vnzskyR2Co6E",
+		"tmpSecretId": "AKIDTRPc-oe6c_avPSRwFVsPDyy3IoAr3szMajlOGuoEXY1232YLy6j4f-xZ5zL-NBMG",
+		"tmpSecretKey": "2v29SZztGYk6SGwHYm\/chJXdD3zPRFasmPoJiCmlR\/I="
+	},
+	"requestId": "69ef6295-b981-464d-9816-9c2ef92189d1",
+	"startTime": 1589256883
+}
+```
diff --git a/vendor/qcloud_sts/qcloud-sts-sdk/composer.json b/vendor/qcloud_sts/qcloud-sts-sdk/composer.json
new file mode 100755
index 0000000..22509b4
--- /dev/null
+++ b/vendor/qcloud_sts/qcloud-sts-sdk/composer.json
@@ -0,0 +1,23 @@
+{
+    "name": "tencent/qcloud-sts-sdk",
+    "description": "PHP SDK for QCloud STS",
+    "keywords": [
+        "qcloud", "sts", "cos", "php"
+    ],
+	"homepage": "https://github.com/tencentyun/qcloud-cos-sts-sdk",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "qcloudterminal",
+            "email": "qcloudterminal@gmail.com"
+        }
+    ],
+    "autoload": {
+      "psr-4": {
+        "QCloud\\COSSTS\\": "src"
+      }
+    },
+    "require": {
+        "php": ">=5.3.0"
+    }
+}
diff --git a/vendor/qcloud_sts/qcloud-sts-sdk/src/Scope.php b/vendor/qcloud_sts/qcloud-sts-sdk/src/Scope.php
new file mode 100755
index 0000000..794fef3
--- /dev/null
+++ b/vendor/qcloud_sts/qcloud-sts-sdk/src/Scope.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace QCloud\COSSTS;
+
+class Scope{
+	var $action;
+	var $bucket;
+	var $region;
+	var $resourcePrefix;
+	var $effect = 'allow';
+	function __construct($action, $bucket, $region, $resourcePrefix){
+		$this->action = $action;
+		$this->bucket = $bucket;
+		$this->region = $region;
+		$this->resourcePrefix = $resourcePrefix;
+	}
+	
+	function set_effect($isAllow){
+		if($isAllow){
+			$this->effect = 'allow';
+		}else{
+			$this->effect = 'deny';
+		}
+	}
+	
+	function get_action(){
+		if($this->action == null){
+			throw new \Exception("action == null");
+		}
+		return $this->action;
+	}
+	
+	function get_resource(){
+		if($this->bucket == null){
+			throw new \Exception("bucket == null");
+		}
+		if($this->resourcePrefix == null){
+			throw new \Exception("resourcePrefix == null");
+		}
+		$index = strripos($this->bucket, '-');
+		if($index < 0){
+			throw new Exception("bucket is invalid: " + $this->bucket);
+		}
+		$appid = substr($this->bucket, $index + 1);
+		if(!(strpos($this->resourcePrefix, '/') === 0)){
+			$this->resourcePrefix = '/' . $this->resourcePrefix;
+		}
+		return 'qcs::cos:' . $this->region . ':uid/' . $appid . ':' . $this->bucket . $this->resourcePrefix;
+	}
+	
+	function get_effect(){
+		return $this->effect;
+	}
+}
+
+?>
\ No newline at end of file
diff --git a/vendor/qcloud_sts/qcloud-sts-sdk/src/Sts.php b/vendor/qcloud_sts/qcloud-sts-sdk/src/Sts.php
new file mode 100755
index 0000000..4c3322d
--- /dev/null
+++ b/vendor/qcloud_sts/qcloud-sts-sdk/src/Sts.php
@@ -0,0 +1,166 @@
+<?php
+
+namespace QCloud\COSSTS;
+
+class Sts{
+	// 临时密钥计算样例
+	
+	function _hex2bin($data) {
+		$len = strlen($data);
+		return pack("H" . $len, $data);
+	}
+	// obj 转 query string
+	function json2str($obj, $notEncode = false) {
+		ksort($obj);
+		$arr = array();
+		if(!is_array($obj)){
+			throw new \Exception($obj + " must be a array");
+		}
+		foreach ($obj as $key => $val) {
+			array_push($arr, $key . '=' . ($notEncode ? $val : rawurlencode($val)));
+		}
+		return join('&', $arr);
+	}
+	// 计算临时密钥用的签名
+	function getSignature($opt, $key, $method, $config) {
+		$formatString = $method . $config['domain'] . '/?' . $this->json2str($opt, 1);
+		$sign = hash_hmac('sha1', $formatString, $key);
+		$sign = base64_encode($this->_hex2bin($sign));
+		return $sign;
+	}
+	// v2接口的key首字母小写,v3改成大写,此处做了向下兼容
+	function backwardCompat($result) {
+		if(!is_array($result)){
+			throw new \Exception($result + " must be a array");
+		}
+		$compat = array();
+		foreach ($result as $key => $value) {
+			if(is_array($value)) {
+				$compat[lcfirst($key)] = $this->backwardCompat($value);
+			} elseif ($key == 'Token') {
+				$compat['sessionToken'] = $value;
+			} else {
+				$compat[lcfirst($key)] = $value;
+			}
+		}
+		return $compat;
+	}
+	// 获取临时密钥
+	function getTempKeys($config) {
+				$result = null;
+		try{
+			if(array_key_exists('policy', $config)){
+				$policy = $config['policy'];
+			}else{
+				if(array_key_exists('bucket', $config)){
+					$ShortBucketName = substr($config['bucket'],0, strripos($config['bucket'], '-'));
+					$AppId = substr($config['bucket'], 1 + strripos($config['bucket'], '-'));
+				}else{
+					throw new \Exception("bucket== null");
+				}
+				if(array_key_exists('allowPrefix', $config)){
+					if(!(strpos($config['allowPrefix'], '/') === 0)){
+					$config['allowPrefix'] = '/' . $config['allowPrefix'];
+					}
+				}else{
+					throw new \Exception("allowPrefix == null");
+				}
+				$policy = array(
+					'version'=> '2.0',
+					'statement'=> array(
+						array(
+							'action'=> $config['allowActions'],
+							'effect'=> 'allow',
+							'principal'=> array('qcs'=> array('*')),
+							'resource'=> array(
+								'qcs::cos:' . $config['region'] . ':uid/' . $AppId . ':' . $config['bucket'] . $config['allowPrefix']
+							)
+						)
+					)
+				);	
+			}
+			$policyStr = str_replace('\\/', '/', json_encode($policy));
+			$Action = 'GetFederationToken';
+			$Nonce = rand(10000, 20000);
+			$Timestamp = time();
+			$Method = 'POST';
+			if(array_key_exists('durationSeconds', $config)){
+				if(!(is_integer($config['durationSeconds']))){
+					throw new \Exception("durationSeconds must be a int type");
+				}
+			}
+			$params = array(
+				'SecretId'=> $config['secretId'],
+				'Timestamp'=> $Timestamp,
+				'Nonce'=> $Nonce,
+				'Action'=> $Action,
+				'DurationSeconds'=> $config['durationSeconds'],
+				'Version'=>'2018-08-13',
+				'Name'=> 'cos',
+				'Region'=> $config['region'],
+				'Policy'=> urlencode($policyStr)
+			);
+			$params['Signature'] = $this->getSignature($params, $config['secretKey'], $Method, $config);
+			$url = $config['url'];
+			$ch = curl_init($url);
+			if(array_key_exists('proxy', $config)){
+				$config['proxy'] && curl_setopt($ch, CURLOPT_PROXY, $config['proxy']);
+			}
+			curl_setopt($ch, CURLOPT_HEADER, 0);
+			curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,0);
+			curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,0);
+			curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+			curl_setopt($ch, CURLOPT_POST, 1);
+			curl_setopt($ch, CURLOPT_POSTFIELDS, $this->json2str($params));
+			$result = curl_exec($ch);
+			if(curl_errno($ch)) $result = curl_error($ch);
+			curl_close($ch);
+			$result = json_decode($result, 1);
+			if (isset($result['Response'])) {
+				$result = $result['Response'];
+				if(isset($result['Error'])){
+					throw new \Exception("get cam failed");
+				}
+				$result['startTime'] = $result['ExpiredTime'] - $config['durationSeconds'];
+			}
+			$result = $this->backwardCompat($result);
+			return $result;
+		}catch(\Exception $e){
+			if($result == null){
+				$result = "error: " . + $e->getMessage();
+			}else{
+				$result = json_encode($result);
+			}
+			throw new \Exception($result);
+		}
+	}
+	
+	// get policy
+	function getPolicy($scopes){
+		if (!is_array($scopes)){
+			return null;
+		}
+		$statements = array();
+		
+		for($i=0, $counts=count($scopes); $i < $counts; $i++){
+			$actions=array();
+			$resources = array();
+			array_push($actions, $scopes[$i]->get_action());
+			array_push($resources, $scopes[$i]->get_resource());
+			
+			$statement = array(
+			'actions' => $actions,
+			'effect' => $scopes[$i]->get_effect(),
+			'resource' => $resources
+			);
+			array_push($statements, $statement);
+		}
+			
+		$policy = array(
+			'version' => '2.0',
+			'statement' => $statements
+		);
+		return $policy;
+	}	
+}
+?>
\ No newline at end of file
diff --git a/vendor/qiniu/php-sdk/.gitignore b/vendor/qiniu/php-sdk/.gitignore
new file mode 100755
index 0000000..4c842c8
--- /dev/null
+++ b/vendor/qiniu/php-sdk/.gitignore
@@ -0,0 +1,12 @@
+*.phar
+*.zip
+build/artifacts
+phpunit.xml
+phpunit.functional.xml
+.DS_Store
+.swp
+.build
+composer.lock
+vendor
+src/package.xml
+.idea/
diff --git a/vendor/qiniu/php-sdk/.scrutinizer.yml b/vendor/qiniu/php-sdk/.scrutinizer.yml
new file mode 100755
index 0000000..8d9304c
--- /dev/null
+++ b/vendor/qiniu/php-sdk/.scrutinizer.yml
@@ -0,0 +1,35 @@
+filter:
+    excluded_paths: [tests/*]
+checks:
+    php:
+        code_rating: true
+        remove_extra_empty_lines: true
+        remove_php_closing_tag: true
+        remove_trailing_whitespace: true
+        fix_use_statements:
+            remove_unused: true
+            preserve_multiple: false
+            preserve_blanklines: true
+            order_alphabetically: true
+        fix_php_opening_tag: true
+        fix_linefeed: true
+        fix_line_ending: true
+        fix_identation_4spaces: true
+        fix_doc_comments: true
+tools:
+    external_code_coverage:
+        timeout: 1200
+        runs: 3
+    php_analyzer: true
+    php_code_coverage: false
+    php_code_sniffer:
+        config:
+            standard: PSR2
+        filter:
+            paths: ['src']
+    php_loc:
+        enabled: true
+        excluded_dirs: [vendor, tests]
+    php_cpd:
+        enabled: true
+        excluded_dirs: [vendor, tests]
diff --git a/vendor/qiniu/php-sdk/.travis.yml b/vendor/qiniu/php-sdk/.travis.yml
new file mode 100755
index 0000000..cada8cd
--- /dev/null
+++ b/vendor/qiniu/php-sdk/.travis.yml
@@ -0,0 +1,28 @@
+sudo: false
+language: php
+
+php:
+  - 5.4
+  - 5.5
+  - 5.6
+  - 7.0
+
+before_script:
+  - export QINIU_TEST_ENV="travis"
+  - travis_retry composer self-update
+  - travis_retry composer install --no-interaction --prefer-source --dev
+
+script:
+  - ./vendor/bin/phpcs --standard=PSR2 src
+  - ./vendor/bin/phpcs --standard=PSR2 examples
+  - ./vendor/bin/phpcs --standard=PSR2 tests
+  - ./vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover tests/Qiniu/Tests/
+
+after_script:
+  - wget https://scrutinizer-ci.com/ocular.phar
+  - php ocular.phar code-coverage:upload --format=php-clover coverage.clover
+
+env:
+  global:
+    - secure: "V9BsntXQZwvO9EOD6itzaae2uq+GemzyTUTxMTJx1/jFoUNpCU2O2UAgjA2XSEr5sgci0KWDV4Krbzv3EBB4uplOFLMI3w32256UHbT9E0x3YjhfPJZk68MH1iS1be7X81LDHON7yveavK8987s3qzjeUcbfLSPgccT+cvf7+dc="
+    - QINIU_ACCESS_KEY=vHg2e7nOh7Jsucv2Azr5FH6omPgX22zoJRWa0FN5
\ No newline at end of file
diff --git a/vendor/qiniu/php-sdk/CHANGELOG.md b/vendor/qiniu/php-sdk/CHANGELOG.md
new file mode 100755
index 0000000..8a284bd
--- /dev/null
+++ b/vendor/qiniu/php-sdk/CHANGELOG.md
@@ -0,0 +1,105 @@
+# Changelog
+
+## 7.2.7 (2018-11-06)
+* 添加 QVM 内网上传到 KODO 的 zone 设置
+
+## 7.2.6 (2018-05-18)
+* 修复rs,rsf在不同机房默认的https域名
+
+## 7.2.5 (2018-05-10)
+* 修复表单上传中多余的参数checkCrc导致的fname错位问题
+
+## 7.2.4 (2018-05-09)
+### 增加
+* 连麦功能
+
+## 7.2.3 (2018-01-20)
+### 增加
+* 新加坡机房
+### 修正
+* 获取域名的入口域名
+* http回复头部兼容大小写
+
+## 7.2.2 (2017-11-06)
+### 增加
+* Qiniu算法的鉴权方法
+
+## 7.1.4 (2017-06-21)
+### 增加
+* cdn 文件/目录 刷新
+* cdn 获取 流量/带宽 
+* cdn 获取域名的访问日志列表
+* cdn 对资源链接进行时间戳防盗链签名
+
+## 7.1.3 (2016-11-18)
+### 增加
+* move, copy操作增加force参数
+
+## 7.1.2 (2016-11-12)
+### 修正
+* 明确抛出获取各区域域名失败时的报错
+
+## 7.1.1 (2016-11-02)
+### 修正
+* 多区域配置文件存储目录从home修改到tmp目录
+
+
+## 7.1.0 (2016-10-22)
+### 增加
+* 多存储区域的支持
+
+## 7.0.8 (2016-07-19)
+### 增加
+* demo
+* https url 支持
+* deleteAfterDays 策略
+* 添加图片处理链接统一拼接方法 by @SherlockRen
+
+## 7.0.7 (2016-01-12)
+### 修正
+* PersistentFop参数pipeline和notify_url失效
+* resume 模式 close file inputstream
+
+## 7.0.6 (2015-12-05)
+### 修正
+* php7.0 Json 对空字符串解析单元测试报错
+* 开启安全模式或者设置可操作目录树时,设置CURLOPT_FOLLOWLOCATION报错, by @twocabbages
+* fetch 支持不指定key, by @sinkcup
+
+## 7.0.5 (2015-10-29)
+### 增加
+* 增加上传策略最小文件大小限制 fsizeMin
+* 增加常见examples
+
+## 7.0.4 (2015-07-23)
+### 修正
+* 一些地方的严格比较检查
+* resumeupload 备用地址失效
+
+## 7.0.3 (2015-07-10)
+### 修改
+* 多zone 支持
+
+## 7.0.2 (2015-04-18)
+### 修改
+* fetch 接口返回内容调整
+* pfop 接口调整
+
+###修正
+* exception 类调用
+
+## 7.0.1 (2015-03-27)
+### 增加
+* 增加代码注释
+
+## 7.0.0 (2015-02-03)
+
+### 增加
+* 简化上传接口
+* 自动选择断点续上传还是直传
+* 重构代码,接口和内部结构更清晰
+* 改变mime
+* 代码覆盖度报告
+* policy改为array, 便于灵活增加,并加入过期字段检查
+* 文件列表支持目录形式
+* 利用元编程方式支持 fop 和 pfop
diff --git a/vendor/qiniu/php-sdk/CONTRIBUTING.md b/vendor/qiniu/php-sdk/CONTRIBUTING.md
new file mode 100755
index 0000000..0466bf9
--- /dev/null
+++ b/vendor/qiniu/php-sdk/CONTRIBUTING.md
@@ -0,0 +1,30 @@
+# 贡献代码指南
+
+我们非常欢迎大家来贡献代码,我们会向贡献者致以最诚挚的敬意。
+
+一般可以通过在Github上提交[Pull Request](https://github.com/qiniu/php-sdk)来贡献代码。
+
+## Pull Request要求
+
+- **[PSR-2 编码风格标准](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** 。要通过项目中的code sniffer检查。
+
+- **代码格式** 提交前 请按 ./vendor/bin/phpcbf --standard=PSR2  进行格式化。
+
+- **必须添加测试!** - 如果没有测试(单元测试、集成测试都可以),那么提交的补丁是不会通过的。
+
+- **记得更新文档** - 保证`README.md`以及其他相关文档及时更新,和代码的变更保持一致性。
+
+- **考虑我们的发布周期** - 我们的版本号会服从[SemVer v2.0.0](http://semver.org/),我们绝对不会随意变更对外的API。
+
+- **创建feature分支** - 最好不要从你的master分支提交 pull request。
+
+- **一个feature提交一个pull请求** - 如果你的代码变更了多个操作,那就提交多个pull请求吧。
+
+- **清晰的commit历史** - 保证你的pull请求的每次commit操作都是有意义的。如果你开发中需要执行多次的即时commit操作,那么请把它们放到一起再提交pull请求。
+
+## 运行测试
+
+``` bash
+./vendor/bin/phpunit tests/Qiniu/Tests/
+
+```
diff --git a/vendor/qiniu/php-sdk/LICENSE b/vendor/qiniu/php-sdk/LICENSE
new file mode 100755
index 0000000..ba646be
--- /dev/null
+++ b/vendor/qiniu/php-sdk/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Qiniu, Ltd.<sdk@qiniu.com>
+
+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.
+
diff --git a/vendor/qiniu/php-sdk/README.md b/vendor/qiniu/php-sdk/README.md
new file mode 100755
index 0000000..16c8b49
--- /dev/null
+++ b/vendor/qiniu/php-sdk/README.md
@@ -0,0 +1,75 @@
+# Qiniu Cloud SDK for PHP
+[![doxygen.io](http://doxygen.io/github.com/qiniu/php-sdk/?status.svg)](http://doxygen.io/github.com/qiniu/php-sdk/)
+[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE)
+[![Build Status](https://travis-ci.org/qiniu/php-sdk.svg)](https://travis-ci.org/qiniu/php-sdk)
+[![Latest Stable Version](https://img.shields.io/packagist/v/qiniu/php-sdk.svg)](https://packagist.org/packages/qiniu/php-sdk)
+[![Total Downloads](https://img.shields.io/packagist/dt/qiniu/php-sdk.svg)](https://packagist.org/packages/qiniu/php-sdk)
+[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/qiniu/php-sdk/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/qiniu/php-sdk/?branch=master)
+[![Code Coverage](https://scrutinizer-ci.com/g/qiniu/php-sdk/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/qiniu/php-sdk/?branch=master)
+[![Join Chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/qiniu/php-sdk?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+[![@qiniu on weibo](http://img.shields.io/badge/weibo-%40qiniutek-blue.svg)](http://weibo.com/qiniutek)
+
+## 安装
+
+* 通过composer,这是推荐的方式,可以使用composer.json 声明依赖,或者运行下面的命令。SDK 包已经放到这里 [`qiniu/php-sdk`][install-packagist] 。
+```bash
+$ composer require qiniu/php-sdk
+```
+* 直接下载安装,SDK 没有依赖其他第三方库,但需要参照 composer的autoloader,增加一个自己的autoloader程序。
+
+## 运行环境
+
+| Qiniu SDK版本 | PHP 版本 |
+|:--------------------:|:---------------------------:|
+|          7.x         |  cURL extension,   5.3 - 5.6,7.0 |
+|          6.x         |  cURL extension,   5.2 - 5.6 |
+
+## 使用方法
+
+### 上传
+```php
+use Qiniu\Storage\UploadManager;
+use Qiniu\Auth;
+...
+    $upManager = new UploadManager();
+    $auth = new Auth($accessKey, $secretKey);
+    $token = $auth->uploadToken($bucketName);
+    list($ret, $error) = $upManager->put($token, 'formput', 'hello world');
+...
+```
+
+## 测试
+
+``` bash
+$ ./vendor/bin/phpunit tests/Qiniu/Tests/
+```
+
+## 常见问题
+
+- $error保留了请求响应的信息,失败情况下ret 为none, 将$error可以打印出来,提交给我们。
+- API 的使用 demo 可以参考 [单元测试](https://github.com/qiniu/php-sdk/blob/master/tests)。
+
+## 代码贡献
+
+详情参考[代码提交指南](https://github.com/qiniu/php-sdk/blob/master/CONTRIBUTING.md)。
+
+## 贡献记录
+
+- [所有贡献者](https://github.com/qiniu/php-sdk/contributors)
+
+## 联系我们
+
+- 如果需要帮助,请提交工单(在portal右侧点击咨询和建议提交工单,或者直接向 support@qiniu.com 发送邮件)
+- 如果有什么问题,可以到问答社区提问,[问答社区](http://qiniu.segmentfault.com/)
+- 更详细的文档,见[官方文档站](http://developer.qiniu.com/)
+- 如果发现了bug, 欢迎提交 [issue](https://github.com/qiniu/php-sdk/issues)
+- 如果有功能需求,欢迎提交 [issue](https://github.com/qiniu/php-sdk/issues)
+- 如果要提交代码,欢迎提交 pull request
+- 欢迎关注我们的[微信](http://www.qiniu.com/#weixin) [微博](http://weibo.com/qiniutek),及时获取动态信息。
+
+## 代码许可
+
+The MIT License (MIT).详情见 [License文件](https://github.com/qiniu/php-sdk/blob/master/LICENSE).
+
+[packagist]: http://packagist.org
+[install-packagist]: https://packagist.org/packages/qiniu/php-sdk
diff --git a/vendor/qiniu/php-sdk/autoload.php b/vendor/qiniu/php-sdk/autoload.php
new file mode 100755
index 0000000..4379b91
--- /dev/null
+++ b/vendor/qiniu/php-sdk/autoload.php
@@ -0,0 +1,14 @@
+<?php
+
+function classLoader($class)
+{
+    $path = str_replace('\\', DIRECTORY_SEPARATOR, $class);
+    $file = __DIR__ . '/src/' . $path . '.php';
+
+    if (file_exists($file)) {
+        require_once $file;
+    }
+}
+spl_autoload_register('classLoader');
+
+require_once  __DIR__ . '/src/Qiniu/functions.php';
diff --git a/vendor/qiniu/php-sdk/composer.json b/vendor/qiniu/php-sdk/composer.json
new file mode 100755
index 0000000..52544fe
--- /dev/null
+++ b/vendor/qiniu/php-sdk/composer.json
@@ -0,0 +1,26 @@
+{
+    "name": "qiniu/php-sdk",
+    "type": "library",
+    "description": "Qiniu Resource (Cloud) Storage SDK for PHP",
+    "keywords": ["qiniu", "storage", "sdk", "cloud"],
+    "homepage": "http://developer.qiniu.com/",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Qiniu",
+            "email": "sdk@qiniu.com",
+            "homepage": "http://www.qiniu.com"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.3"
+    },
+    "require-dev": {
+        "phpunit/phpunit": "~4.0",
+        "squizlabs/php_codesniffer": "~2.3"
+    },
+    "autoload": {
+        "psr-4": {"Qiniu\\": "src/Qiniu"},
+        "files": ["src/Qiniu/functions.php"]
+    }
+}
diff --git a/vendor/qiniu/php-sdk/docs/rtc/README.md b/vendor/qiniu/php-sdk/docs/rtc/README.md
new file mode 100755
index 0000000..7b98ca0
--- /dev/null
+++ b/vendor/qiniu/php-sdk/docs/rtc/README.md
@@ -0,0 +1,71 @@
+# Rtc Streaming Cloud Server-Side Library For PHP
+
+## Features
+
+- Appclient
+    - [x] 创建房间: client->createApp()
+    - [x] 查看房间: client->getApp()
+    - [x] 删除房间: client->deleteApp()
+    - [x] 生成房间token: client->appToken()
+
+
+
+## Contents
+
+- [Installation](#installation)
+- [Usage](#usage)
+    - [Configuration](#configuration)
+    - [App](#app)
+        - [Create a app](#create-a-app)
+        - [Get a app](#get-a-app)
+        - [Delete a app](#delete-a-app)
+        - [Generate a app token](#generate-a-app-token)
+
+
+## Usage
+
+### App
+
+#### Create a app
+
+```php
+$ak = "gwd_gV4gPKZZsmEOvAuNU1AcumicmuHooTfu64q5";
+$sk = "xxxx";
+$auth = new Auth($ak, $sk);
+$client = new Qiniu\Rtc\AppClient($auth);
+$resp=$client->createApp("901","testApp");
+print_r($resp);
+```
+
+#### Get an app
+
+```php
+$ak = "gwd_gV4gPKZZsmEOvAuNU1AcumicmuHooTfu64q5";
+$sk = "xxxx";
+$auth = new Auth($ak, $sk);
+$client = new Qiniu\Rtc\AppClient($auth);
+$resp=$client->getApp("deq02uhb6");
+print_r($resp);
+```
+
+#### Delete an app
+
+```php
+$ak = "gwd_gV4gPKZZsmEOvAuNU1AcumicmuHooTfu64q5";
+$sk = "xxxx";
+$auth = new Auth($ak, $sk);
+$client = new Qiniu\Rtc\AppClient($auth);
+$resp=$client->deleteApp("deq02uhb6");
+print_r($resp);
+```
+
+#### Generate an app token
+
+```php
+$ak = "gwd_gV4gPKZZsmEOvAuNU1AcumicmuHooTfu64q5";
+$sk = "xxxx";
+$auth = new Auth($ak, $sk);
+$client = new Qiniu\Rtc\AppClient($auth);
+$resp=$client->appToken("deq02uhb6", "lfx", '1111', (time()+3600), 'user');
+print_r($resp);
+```
\ No newline at end of file
diff --git a/vendor/qiniu/php-sdk/docs/rtc/example.php b/vendor/qiniu/php-sdk/docs/rtc/example.php
new file mode 100755
index 0000000..f30b9b4
--- /dev/null
+++ b/vendor/qiniu/php-sdk/docs/rtc/example.php
@@ -0,0 +1,42 @@
+<?php
+require_once("../../autoload.php");
+
+use \Qiniu\Auth;
+
+$ak = 'gwd_gV4gPKZZsmEOvAuNU1AcumicmuHooTfu64q5';
+$sk = 'xxxx';
+
+$auth = new Auth($ak, $sk);
+$client = new Qiniu\Rtc\AppClient($auth);
+$hub = 'lfxlive';
+$title = 'lfxl';
+try {
+    //创建app
+    $resp = $client->createApp($hub, $title, $maxUsers);
+    print_r($resp);
+    // 获取app状态
+    $resp = $client->getApp('dgdl5ge8y');
+    print_r($resp);
+    //修改app状态
+    $mergePublishRtmp = null;
+    $mergePublishRtmp['enable'] = true;
+    $resp = $client->updateApp('dgdl5ge8y', $hub, $title, $maxUsers, $mergePublishRtmp);
+    print_r($resp);
+    //删除app
+    $resp = $client->deleteApp('dgdl5ge8y');
+    print_r($resp);
+    //获取房间连麦的成员
+    $resp=$client->listUser("dgbfvvzid", 'lfxl');
+    print_r($resp);
+    //剔除房间的连麦成员
+    $resp=$client->kickUser("dgbfvvzid", 'lfx', "qiniu-f6e07b78-4dc8-45fb-a701-a9e158abb8e6");
+    print_r($resp);
+    // 列举房间
+    $resp=$client->listActiveRooms("dgbfvvzid", 'lfx', null, null);
+    print_r($resp);
+    //鉴权的有效时间: 1个小时.
+    $resp = $client->appToken("dgd4vecde", "lfxl", '1111', (time()+3600), 'user');
+    print_r($resp);
+} catch (\Exception $e) {
+    echo "Error:", $e, "\n";
+}
diff --git a/vendor/qiniu/php-sdk/docs/sms/example.php b/vendor/qiniu/php-sdk/docs/sms/example.php
new file mode 100755
index 0000000..541c0d6
--- /dev/null
+++ b/vendor/qiniu/php-sdk/docs/sms/example.php
@@ -0,0 +1,70 @@
+<?php
+require_once("../../autoload.php");
+
+use \Qiniu\Auth;
+
+$ak="xxxx";
+$sk="xxxx";
+
+$auth = new Auth($ak, $sk);
+$client = new Qiniu\Sms\Sms($auth);
+
+//发送信息模块
+$template_id="1131792074274775040";
+$mobiles=array("18011111111","18011111111");
+$code = array('code' => 'code' );
+try {
+    //发送短信
+    $resp = $client->sendMessage($template_id, $mobiles, $code);
+    print_r($resp);
+} catch (\Exception $e) {
+    echo "Error:", $e, "\n";
+}exit;
+//模板模块
+$name="tstest001";
+$template="tesy001 ${code}";
+$type="notification";
+$description="tstest001";
+$signature_id="1131464448834277376";
+$id="1131810682442883072";
+
+try {
+    //创建模板
+    $resp = $client->createTemplate($name, $template, $type, $description, $signature_id);
+    print_r($resp);
+    //查询模板
+    $resp = $client->queryTemplate();
+    print_r($resp);
+    //修改模板
+    $resp = $client->updateTemplate($id, $name, $template, $description, $signature_id);
+    print_r($resp);
+    //删除模板
+    $resp = $client->deleteTemplate($id);
+    print_r($resp);
+} catch (\Exception $e) {
+    echo "Error:", $e, "\n";
+}
+//签名模块
+$signature = 'lfxlive2';
+$source = 'enterprises_and_institutions';
+$pic="/Users/Desktop/sss.jpg";
+$audit_status="passed";
+$page=1;
+$page_size=1;
+$id="1131464448834277376";
+
+try {
+    //创建签名
+    $resp = $client->createSignature($signature, $source, $pic);
+    print_r($resp);
+     //查询签名
+    $resp = $client->checkSignature($audit_status);
+    //修改签名
+    $resp = $client->updateSignature($id, $signature, $source, $pic);
+    print_r($resp);
+    //删除ID
+    $resp = $client->deleteSignature($id);
+    print_r($resp);
+} catch (\Exception $e) {
+    echo "Error:", $e, "\n";
+}
diff --git a/vendor/qiniu/php-sdk/examples/README.md b/vendor/qiniu/php-sdk/examples/README.md
new file mode 100755
index 0000000..6cf8e30
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/README.md
@@ -0,0 +1,10 @@
+# examples
+
+这些 examples 旨在帮助你快速了解使用七牛的sdk。这些demo都是可以直接运行的, 但是在运行之前需要填上您自己的参数。
+
+比如:
+
+* `$bucket`  需要填上您想操作的 [bucket名字](http://developer.qiniu.com/docs/v6/api/overview/concepts.html#bucket)。
+* `$accessKey` 和 `$secretKey` 可以在我们的[管理后台](https://portal.qiniu.com/setting/key)找到。
+* 在进行`视频转码`, `压缩文件`等异步操作时 需要使用到的队列名称也可以在我们[管理后台](https://portal.qiniu.com/mps/pipeline)新建。
+
diff --git a/vendor/qiniu/php-sdk/examples/bucket_lifecycleRule.php b/vendor/qiniu/php-sdk/examples/bucket_lifecycleRule.php
new file mode 100755
index 0000000..3f7fefd
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/bucket_lifecycleRule.php
@@ -0,0 +1,30 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+$bucket = 'xxxx';
+$name = 'demo';
+$prefix = 'test';
+$delete_after_days = 80;
+$to_line_after_days =70;
+
+list($Info, $err) = $bucketManager->bucketLifecycleRule(
+    $bucket,
+    $name,
+    $prefix,
+    $delete_after_days,
+    $to_line_after_days
+);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($Info);
+}
diff --git a/vendor/qiniu/php-sdk/examples/cdn_get_bandwidth.php b/vendor/qiniu/php-sdk/examples/cdn_get_bandwidth.php
new file mode 100755
index 0000000..4d2ccf6
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/cdn_get_bandwidth.php
@@ -0,0 +1,40 @@
+<?php
+
+require_once __DIR__ . '/../autoload.php';
+
+use \Qiniu\Cdn\CdnManager;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+
+$auth = new Qiniu\Auth($accessKey, $secretKey);
+$cdnManager = new CdnManager($auth);
+
+//获取流量和带宽数据
+//参考文档:http://developer.qiniu.com/article/fusion/api/traffic-bandwidth.html
+
+$domains = array(
+    "javasdk.qiniudn.com",
+    "phpsdk.qiniudn.com"
+);
+
+$startDate = "2017-08-20";
+$endDate = "2017-08-21";
+
+//5min or hour or day
+$granularity = "day";
+
+//获取带宽数据
+list($bandwidthData, $getBandwidthErr) = $cdnManager->getBandwidthData(
+    $domains,
+    $startDate,
+    $endDate,
+    $granularity
+);
+
+if ($getBandwidthErr != null) {
+    var_dump($getBandwidthErr);
+} else {
+    echo "get bandwidth data success\n";
+    print_r($bandwidthData);
+}
diff --git a/vendor/qiniu/php-sdk/examples/cdn_get_flux.php b/vendor/qiniu/php-sdk/examples/cdn_get_flux.php
new file mode 100755
index 0000000..56da550
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/cdn_get_flux.php
@@ -0,0 +1,34 @@
+<?php
+
+require_once __DIR__ . '/../autoload.php';
+
+use \Qiniu\Cdn\CdnManager;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+
+$auth = new Qiniu\Auth($accessKey, $secretKey);
+$cdnManager = new CdnManager($auth);
+
+//获取流量和带宽数据
+//参考文档:http://developer.qiniu.com/article/fusion/api/traffic-bandwidth.html
+
+$domains = array(
+    "javasdk.qiniudn.com",
+    "phpsdk.qiniudn.com"
+);
+
+$startDate = "2017-08-20";
+$endDate = "2017-08-21";
+
+//5min or hour or day
+$granularity = "day";
+
+//获取流量数据
+list($fluxData, $getFluxErr) = $cdnManager->getFluxData($domains, $startDate, $endDate, $granularity);
+if ($getFluxErr != null) {
+    var_dump($getFluxErr);
+} else {
+    echo "get flux data success\n";
+    print_r($fluxData);
+}
diff --git a/vendor/qiniu/php-sdk/examples/cdn_get_log_list.php b/vendor/qiniu/php-sdk/examples/cdn_get_log_list.php
new file mode 100755
index 0000000..4e5c942
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/cdn_get_log_list.php
@@ -0,0 +1,29 @@
+<?php
+
+require_once __DIR__ . '/../autoload.php';
+
+use \Qiniu\Cdn\CdnManager;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+
+$auth = new Qiniu\Auth($accessKey, $secretKey);
+$cdnManager = new CdnManager($auth);
+
+$domains = array(
+    "javasdk.qiniudn.com",
+    "phpsdk.qiniudn.com"
+);
+
+$logDate = '2017-08-20';
+
+//获取日志下载链接
+//参考文档:http://developer.qiniu.com/article/fusion/api/log.html
+
+list($logListData, $getLogErr) = $cdnManager->getCdnLogList($domains, $logDate);
+if ($getLogErr != null) {
+    var_dump($getLogErr);
+} else {
+    echo "get cdn log list success\n";
+    print_r($logListData);
+}
diff --git a/vendor/qiniu/php-sdk/examples/cdn_refresh_urls_dirs.php b/vendor/qiniu/php-sdk/examples/cdn_refresh_urls_dirs.php
new file mode 100755
index 0000000..c05e75f
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/cdn_refresh_urls_dirs.php
@@ -0,0 +1,52 @@
+<?php
+
+require_once __DIR__ . '/../autoload.php';
+
+use \Qiniu\Cdn\CdnManager;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+
+$auth = new Qiniu\Auth($accessKey, $secretKey);
+
+//待刷新的文件列表和目录,文件列表最多一次100个,目录最多一次10个
+//参考文档:http://developer.qiniu.com/article/fusion/api/refresh.html
+$urls = array(
+    "http://phpsdk.qiniudn.com/qiniu.jpg",
+    "http://phpsdk.qiniudn.com/qiniu2.jpg",
+);
+
+//刷新目录需要联系七牛技术支持开通账户权限
+$dirs = array(
+    "http://phpsdk.qiniudn.com/test/"
+);
+
+$cdnManager = new CdnManager($auth);
+
+// 目前客户默认没有目录刷新权限,刷新会有400038报错,参考:https://developer.qiniu.com/fusion/api/1229/cache-refresh
+// 需要刷新目录请工单联系技术支持 https://support.qiniu.com/tickets/category
+list($refreshResult, $refreshErr) = $cdnManager->refreshUrlsAndDirs($urls, $dirs);
+if ($refreshErr != null) {
+    var_dump($refreshErr);
+} else {
+    echo "refresh request sent\n";
+    print_r($refreshResult);
+}
+
+//如果只有刷新链接或者目录的需求,可以分布使用
+
+list($refreshResult, $refreshErr) = $cdnManager->refreshUrls($urls);
+if ($refreshErr != null) {
+    var_dump($refreshErr);
+} else {
+    echo "refresh request sent\n";
+    print_r($refreshResult);
+}
+
+list($refreshResult, $refreshErr) = $cdnManager->refreshDirs($dirs);
+if ($refreshErr != null) {
+    var_dump($refreshErr);
+} else {
+    echo "refresh request sent\n";
+    print_r($refreshResult);
+}
diff --git a/vendor/qiniu/php-sdk/examples/cdn_timestamp_antileech.php b/vendor/qiniu/php-sdk/examples/cdn_timestamp_antileech.php
new file mode 100755
index 0000000..d9fd023
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/cdn_timestamp_antileech.php
@@ -0,0 +1,19 @@
+<?php
+
+require_once __DIR__ . '/../autoload.php';
+
+use \Qiniu\Cdn\CdnManager;
+
+//创建时间戳防盗链
+//时间戳防盗链密钥,后台获取
+$encryptKey = 'your_domain_timestamp_antileech_encryptkey';
+
+//带访问协议的域名
+$url1 = 'http://phpsdk.qiniuts.com/24.jpg?avinfo';
+$url2 = 'http://phpsdk.qiniuts.com/24.jpg';
+
+//有效期时间(单位秒)
+$durationInSeconds = 3600;
+
+$signedUrl = CdnManager::createTimestampAntiLeechUrl($url1, $encryptKey, $durationInSeconds);
+print($signedUrl);
diff --git a/vendor/qiniu/php-sdk/examples/delete_bucket.php b/vendor/qiniu/php-sdk/examples/delete_bucket.php
new file mode 100755
index 0000000..dc2e305
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/delete_bucket.php
@@ -0,0 +1,20 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+$name = 'xxxx';
+
+list($Info, $err) = $bucketManager->deleteBucket($name);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($Info);
+}
diff --git a/vendor/qiniu/php-sdk/examples/delete_bucketEvent.php b/vendor/qiniu/php-sdk/examples/delete_bucketEvent.php
new file mode 100755
index 0000000..00df6ca
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/delete_bucketEvent.php
@@ -0,0 +1,21 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+$bucket = 'xxxx';
+$name = 'demo';
+
+list($Info, $err) = $bucketManager->deleteBucketEvent($bucket, $name);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($Info);
+}
diff --git a/vendor/qiniu/php-sdk/examples/delete_bucketLifecycleRule.php b/vendor/qiniu/php-sdk/examples/delete_bucketLifecycleRule.php
new file mode 100755
index 0000000..a536992
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/delete_bucketLifecycleRule.php
@@ -0,0 +1,21 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+$bucket = 'xxxx';
+$name = 'demo';
+
+list($Info, $err) = $bucketManager->deleteBucketLifecycleRule($bucket, $name);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($Info);
+}
diff --git a/vendor/qiniu/php-sdk/examples/get_bucketEvents.php b/vendor/qiniu/php-sdk/examples/get_bucketEvents.php
new file mode 100755
index 0000000..53a5c88
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/get_bucketEvents.php
@@ -0,0 +1,20 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+$bucket = 'xxxx';
+
+list($Info, $err) = $bucketManager->getBucketEvents($bucket);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($Info);
+}
diff --git a/vendor/qiniu/php-sdk/examples/get_bucketLifecycleRules.php b/vendor/qiniu/php-sdk/examples/get_bucketLifecycleRules.php
new file mode 100755
index 0000000..652bee8
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/get_bucketLifecycleRules.php
@@ -0,0 +1,20 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+$bucket = 'xxxx';
+
+list($Info, $err) = $bucketManager->getBucketLifecycleRules($bucket);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($Info);
+}
diff --git a/vendor/qiniu/php-sdk/examples/get_bucketList.php b/vendor/qiniu/php-sdk/examples/get_bucketList.php
new file mode 100755
index 0000000..74aaa65
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/get_bucketList.php
@@ -0,0 +1,20 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+$region = 'z1';
+
+list($Info, $err) = $bucketManager->listbuckets($region);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($Info);
+}
diff --git a/vendor/qiniu/php-sdk/examples/get_bucketQuota.php b/vendor/qiniu/php-sdk/examples/get_bucketQuota.php
new file mode 100755
index 0000000..563395d
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/get_bucketQuota.php
@@ -0,0 +1,20 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+$bucket = 'xxxx';
+
+list($Info, $err) = $bucketManager->getBucketQuota($bucket);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($Info);
+}
diff --git a/vendor/qiniu/php-sdk/examples/get_bucketinfo.php b/vendor/qiniu/php-sdk/examples/get_bucketinfo.php
new file mode 100755
index 0000000..ff052a4
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/get_bucketinfo.php
@@ -0,0 +1,20 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+$bucket = 'xxxx';
+
+list($Info, $err) = $bucketManager->bucketInfo($bucket);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($Info);
+}
diff --git a/vendor/qiniu/php-sdk/examples/get_bucketinfos.php b/vendor/qiniu/php-sdk/examples/get_bucketinfos.php
new file mode 100755
index 0000000..0ad6503
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/get_bucketinfos.php
@@ -0,0 +1,20 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+$region = 'z1';
+
+list($Info, $err) = $bucketManager->bucketInfos($region);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($Info);
+}
diff --git a/vendor/qiniu/php-sdk/examples/get_corsRules.php b/vendor/qiniu/php-sdk/examples/get_corsRules.php
new file mode 100755
index 0000000..fbfde2d
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/get_corsRules.php
@@ -0,0 +1,20 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+$bucket = 'xxxx';
+
+list($Info, $err) = $bucketManager->getCorsRules($bucket);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($Info);
+}
diff --git a/vendor/qiniu/php-sdk/examples/image_url_builder.php b/vendor/qiniu/php-sdk/examples/image_url_builder.php
new file mode 100755
index 0000000..e99a728
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/image_url_builder.php
@@ -0,0 +1,74 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+// 引入图片处理类
+use Qiniu\Processing\ImageUrlBuilder;
+
+$imageUrlBuilder = new ImageUrlBuilder();
+
+// 要处理图片
+$url = 'http://78re52.com1.z0.glb.clouddn.com/resource/gogopher.jpg';
+$url2 = 'http://78re52.com1.z0.glb.clouddn.com/resource/gogopher.jpg?watermark/1/gravity/SouthEast/dx/0/dy/0/image/'
+    . 'aHR0cDovL2Fkcy1jZG4uY2h1Y2h1amllLmNvbS9Ga1R6bnpIY2RLdmRBUFc5cHZZZ3pTc21UY0tB';
+$waterImage = 'http://developer.qiniu.com/resource/logo-2.jpg';
+
+/**
+ * 缩略图链接拼接
+ *
+ * @param  string $url 图片链接
+ * @param  int $mode 缩略模式
+ * @param  int $width 宽度
+ * @param  int $height 长度
+ * @param  string $format 输出类型 [可选]
+ * @param  int $quality 图片质量 [可选]
+ * @param  int $interlace 是否支持渐进显示 [可选]
+ * @param  int $ignoreError 忽略结果 [可选]
+ * @return string
+ * @link http://developer.qiniu.com/code/v6/api/kodo-api/image/imageview2.html
+ * @author Sherlock Ren <sherlock_ren@icloud.com>
+ */
+$thumbLink = $imageUrlBuilder->thumbnail($url, 1, 100, 100);
+
+// 函数方式调用 也可拼接多个操作参数 图片+水印
+$thumbLink2 = \Qiniu\thumbnail($url2, 1, 100, 100);
+var_dump($thumbLink, $thumbLink2);
+
+/**
+ * 图片水印
+ *
+ * @param  string $url 图片链接
+ * @param  string $image 水印图片链接
+ * @param  numeric $dissolve 透明度 [可选]
+ * @param  string $gravity 水印位置 [可选]
+ * @param  numeric $dx 横轴边距 [可选]
+ * @param  numeric $dy 纵轴边距 [可选]
+ * @param  numeric $watermarkScale 自适应原图的短边比例 [可选]
+ * @link   http://developer.qiniu.com/code/v6/api/kodo-api/image/watermark.html
+ * @return string
+ * @author Sherlock Ren <sherlock_ren@icloud.com>
+ */
+$waterLink = $imageUrlBuilder->waterImg($url, $waterImage);
+// 函数调用方法
+//$waterLink = \Qiniu\waterImg($url, $waterImage);
+var_dump($waterLink);
+
+/**
+ * 文字水印
+ *
+ * @param  string $url 图片链接
+ * @param  string $text 文字
+ * @param  string $font 文字字体
+ * @param  string $fontSize 文字字号
+ * @param  string $fontColor 文字颜色 [可选]
+ * @param  numeric $dissolve 透明度 [可选]
+ * @param  string $gravity 水印位置 [可选]
+ * @param  numeric $dx 横轴边距 [可选]
+ * @param  numeric $dy 纵轴边距 [可选]
+ * @link   http://developer.qiniu.com/code/v6/api/kodo-api/image/watermark.html#text-watermark
+ * @return string
+ * @author Sherlock Ren <sherlock_ren@icloud.com>
+ */
+$textLink = $imageUrlBuilder->waterText($url, '你瞅啥', '微软雅黑', 300);
+// 函数调用方法
+// $textLink = \Qiniu\waterText($url, '你瞅啥', '微软雅黑', 300);
+var_dump($textLink);
diff --git a/vendor/qiniu/php-sdk/examples/persistent_fop_init.php b/vendor/qiniu/php-sdk/examples/persistent_fop_init.php
new file mode 100755
index 0000000..2df01e9
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/persistent_fop_init.php
@@ -0,0 +1,19 @@
+<?php
+
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+use Qiniu\Processing\PersistentFop;
+
+$accessKey = 'Access_Key';
+$secretKey = 'Secret_Key';
+$auth = new Auth($accessKey, $secretKey);
+
+// 要转码的文件所在的空间。
+$bucket = 'Bucket_Name';
+
+// 转码是使用的队列名称。 https://portal.qiniu.com/mps/pipeline
+$pipeline = 'pipeline_name';
+
+// 初始化
+$pfop = new PersistentFop($auth, $bucket, $pipeline);
diff --git a/vendor/qiniu/php-sdk/examples/persistent_fop_status.php b/vendor/qiniu/php-sdk/examples/persistent_fop_status.php
new file mode 100755
index 0000000..0c26f7a
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/persistent_fop_status.php
@@ -0,0 +1,18 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Processing\PersistentFop;
+
+$pfop = new Qiniu\Processing\PersistentFop(null, null);
+
+// 触发持久化处理后返回的 Id
+$persistentId = 'z1.5b8a48e5856db843bc24cfc3';
+
+// 通过persistentId查询该 触发持久化处理的状态
+list($ret, $err) = $pfop->status($persistentId);
+
+if ($err) {
+    print_r($err);
+} else {
+    print_r($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/pfop_mkzip.php b/vendor/qiniu/php-sdk/examples/pfop_mkzip.php
new file mode 100755
index 0000000..746b356
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/pfop_mkzip.php
@@ -0,0 +1,43 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+use Qiniu\Processing\PersistentFop;
+
+// 去我们的portal 后台来获取AK, SK
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+$key = 'qiniu.png';
+
+$auth = new Auth($accessKey, $secretKey);
+// 异步任务的队列, 去后台新建: https://portal.qiniu.com/mps/pipeline
+$pipeline = 'sdktest';
+
+$pfop = new PersistentFop($auth, null);
+
+// 进行zip压缩的url
+$url1 = 'http://phpsdk.qiniudn.com/php-logo.png';
+$url2 = 'http://phpsdk.qiniudn.com/1.png';
+
+//压缩后的key
+$zipKey = 'test.zip';
+
+$fops = 'mkzip/2/url/' . \Qiniu\base64_urlSafeEncode($url1);
+$fops .= '/url/' . \Qiniu\base64_urlSafeEncode($url2);
+$fops .= '|saveas/' . \Qiniu\base64_urlSafeEncode("$bucket:$zipKey");
+
+$notify_url = null;
+$force = false;
+
+list($id, $err) = $pfop->execute($bucket, $key, $fops, $pipeline, $notify_url, $force);
+
+echo "\n====> pfop mkzip result: \n";
+if ($err != null) {
+    var_dump($err);
+} else {
+    echo "PersistentFop Id: $id\n";
+
+    $res = "http://api.qiniu.com/status/get/prefop?id=$id";
+    echo "Processing result: $res";
+}
diff --git a/vendor/qiniu/php-sdk/examples/pfop_vframe.php b/vendor/qiniu/php-sdk/examples/pfop_vframe.php
new file mode 100755
index 0000000..e1df2d5
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/pfop_vframe.php
@@ -0,0 +1,46 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+use Qiniu\Processing\PersistentFop;
+
+//对已经上传到七牛的视频发起异步转码操作 
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+$auth = new Auth($accessKey, $secretKey);
+
+//要转码的文件所在的空间和文件名。
+$key = 'qiniu.mp4';
+
+//转码是使用的队列名称。 https://portal.qiniu.com/mps/pipeline
+$pipeline = 'sdktest';
+
+//转码完成后通知到你的业务服务器。
+$notifyUrl = 'http://375dec79.ngrok.com/notify.php';
+$force = false;
+
+$config = new \Qiniu\Config();
+$config->useHTTPS = true;
+$pfop = new PersistentFop($auth, $config);
+
+//要进行视频截图操作
+$fops = "vframe/jpg/offset/1/w/480/h/360/rotate/90|saveas/" .
+    \Qiniu\base64_urlSafeEncode($bucket . ":qiniu_480x360.jpg");
+
+list($id, $err) = $pfop->execute($bucket, $key, $fops, $pipeline, $notifyUrl, $force);
+echo "\n====> pfop avthumb result: \n";
+if ($err != null) {
+    var_dump($err);
+} else {
+    echo "PersistentFop Id: $id\n";
+}
+
+//查询转码的进度和状态
+list($ret, $err) = $pfop->status($id);
+echo "\n====> pfop avthumb status: \n";
+if ($err != null) {
+    var_dump($err);
+} else {
+    var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/pfop_video_avthumb.php b/vendor/qiniu/php-sdk/examples/pfop_video_avthumb.php
new file mode 100755
index 0000000..aebe815
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/pfop_video_avthumb.php
@@ -0,0 +1,47 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+use Qiniu\Processing\PersistentFop;
+
+//对已经上传到七牛的视频发起异步转码操作
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+
+$auth = new Auth($accessKey, $secretKey);
+
+//要转码的文件所在的空间和文件名。
+$key = 'qiniu.mp4';
+
+//转码是使用的队列名称。 https://portal.qiniu.com/mps/pipeline
+$pipeline = 'sdktest';
+$force = false;
+
+//转码完成后通知到你的业务服务器。
+$notifyUrl = 'http://375dec79.ngrok.com/notify.php';
+$config = new \Qiniu\Config();
+//$config->useHTTPS=true;
+
+$pfop = new PersistentFop($auth, $config);
+
+//要进行转码的转码操作。 http://developer.qiniu.com/docs/v6/api/reference/fop/av/avthumb.html
+$fops = "avthumb/mp4/s/640x360/vb/1.4m|saveas/" . \Qiniu\base64_urlSafeEncode($bucket . ":qiniu_640x360.mp4");
+
+list($id, $err) = $pfop->execute($bucket, $key, $fops, $pipeline, $notifyUrl, $force);
+echo "\n====> pfop avthumb result: \n";
+if ($err != null) {
+    var_dump($err);
+} else {
+    echo "PersistentFop Id: $id\n";
+}
+
+//查询转码的进度和状态
+list($ret, $err) = $pfop->status($id);
+echo "\n====> pfop avthumb status: \n";
+if ($err != null) {
+    var_dump($err);
+} else {
+    var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/pfop_watermark.php b/vendor/qiniu/php-sdk/examples/pfop_watermark.php
new file mode 100755
index 0000000..72aa6c4
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/pfop_watermark.php
@@ -0,0 +1,52 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+use Qiniu\Processing\PersistentFop;
+
+//对已经上传到七牛的视频发起异步转码操作 
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+
+$auth = new Auth($accessKey, $secretKey);
+
+//要转码的文件所在的空间和文件名。
+$key = 'qiniu.mp4';
+
+//转码是使用的队列名称。 https://portal.qiniu.com/mps/pipeline
+$pipeline = 'sdktest';
+
+//转码完成后通知到你的业务服务器。
+$notifyUrl = 'http://375dec79.ngrok.com/notify.php';
+$force = false;
+
+$config = new \Qiniu\Config();
+//$config->useHTTPS=true;
+$pfop = new PersistentFop($auth, $config);
+
+//需要添加水印的图片UrlSafeBase64
+//可以参考http://developer.qiniu.com/code/v6/api/dora-api/av/video-watermark.html
+$base64URL = Qiniu\base64_urlSafeEncode('http://devtools.qiniu.com/qiniu.png');
+
+//水印参数
+$fops = "avthumb/mp4/s/640x360/vb/1.4m/image/" . $base64URL . "|saveas/"
+    . \Qiniu\base64_urlSafeEncode($bucket . ":qiniu_wm.mp4");
+
+list($id, $err) = $pfop->execute($bucket, $key, $fops, $pipeline, $notifyUrl, $force);
+echo "\n====> pfop avthumb result: \n";
+if ($err != null) {
+    var_dump($err);
+} else {
+    echo "PersistentFop Id: $id\n";
+}
+
+//查询转码的进度和状态
+list($ret, $err) = $pfop->status($id);
+echo "\n====> pfop avthumb status: \n";
+if ($err != null) {
+    var_dump($err);
+} else {
+    var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/php-logo.png b/vendor/qiniu/php-sdk/examples/php-logo.png
new file mode 100755
index 0000000..77e051f
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/php-logo.png
Binary files differ
diff --git a/vendor/qiniu/php-sdk/examples/prefop.php b/vendor/qiniu/php-sdk/examples/prefop.php
new file mode 100755
index 0000000..ae61a5f
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/prefop.php
@@ -0,0 +1,29 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+use Qiniu\Processing\PersistentFop;
+
+$accessKey = 'Access_Key';
+$secretKey = 'Secret_Key';
+$auth = new Auth($accessKey, $secretKey);
+
+//要持久化处理的文件所在的空间和文件名。
+$bucket = 'Bucket_Name';
+
+//持久化处理使用的队列名称。 https://portal.qiniu.com/mps/pipeline
+$pipeline = 'pipeline_name';
+
+//持久化处理完成后通知到你的业务服务器。
+$notifyUrl = 'http://375dec79.ngrok.com/notify.php';
+$pfop = new PersistentFop($auth, $bucket, $pipeline, $notifyUrl);
+
+$id = "z2.5955c739e3d0041bf80c9baa";
+//查询持久化处理的进度和状态
+list($ret, $err) = $pfop->status($id);
+echo "\n====> pfop avthumb status: \n";
+if ($err != null) {
+    var_dump($err);
+} else {
+    var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/pulpvideo.php b/vendor/qiniu/php-sdk/examples/pulpvideo.php
new file mode 100755
index 0000000..bad8821
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/pulpvideo.php
@@ -0,0 +1,55 @@
+<?php
+
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+use Qiniu\Http\Client;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$argusManager = new \Qiniu\Storage\ArgusManager($auth, $config);
+
+$reqBody = array();
+$reqBody['uri'] = "xxxx";
+
+$ops = array();
+$ops = array(
+    array(
+        'op' => 'pulp',
+        'params' => array(
+            'labels' => array(
+                array(
+                    'label' => "1",
+                    'select' => 1,
+                    'score' => 2,
+                ),
+            )
+        )
+    ),
+);
+
+$params = array();
+$params = array(
+    'async' => false,
+    'vframe' => array(
+        'mode' => 1,
+        'interval' => 8,
+    )
+);
+
+$req = array();
+$req['data'] = $reqBody;
+$req['ops'] = $ops;
+$req['params'] = $params;
+$body = json_encode($req);
+
+$vid = "xxxx";
+list($ret, $err) = $argusManager->pulpVideo($body, $vid);
+
+if ($err !== null) {
+    var_dump($err);
+} else {
+    var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/put_bucketAccessMode.php b/vendor/qiniu/php-sdk/examples/put_bucketAccessMode.php
new file mode 100755
index 0000000..b453926
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/put_bucketAccessMode.php
@@ -0,0 +1,21 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+$bucket = 'xxxx';
+$private = 1;
+
+list($Info, $err) = $bucketManager->putBucketAccessMode($bucket, $private);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($Info);
+}
diff --git a/vendor/qiniu/php-sdk/examples/put_bucketAccessStyleMode.php b/vendor/qiniu/php-sdk/examples/put_bucketAccessStyleMode.php
new file mode 100755
index 0000000..2f7c27f
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/put_bucketAccessStyleMode.php
@@ -0,0 +1,21 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+$bucket = 'xxxx';
+$mode = 1;
+
+list($Info, $err) = $bucketManager->putBucketAccessStyleMode($bucket, $mode);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($Info);
+}
diff --git a/vendor/qiniu/php-sdk/examples/put_bucketEvent.php b/vendor/qiniu/php-sdk/examples/put_bucketEvent.php
new file mode 100755
index 0000000..33dbb44
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/put_bucketEvent.php
@@ -0,0 +1,25 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+$bucket = 'xxxx';
+$name = 'testdemo';
+$prefix = 'test1';
+$suffix = 'mp3';
+$event = array("move","copy");
+$callbackURL = 'http://www.qiniu.com';
+
+list($Info, $err) = $bucketManager->putBucketEvent($bucket, $name, $prefix, $suffix, $event, $callbackURL);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($Info);
+}
diff --git a/vendor/qiniu/php-sdk/examples/put_bucketMaxAge.php b/vendor/qiniu/php-sdk/examples/put_bucketMaxAge.php
new file mode 100755
index 0000000..77092e9
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/put_bucketMaxAge.php
@@ -0,0 +1,21 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+$bucket = 'xxxx';
+$maxAge = 15768000;
+
+list($Info, $err) = $bucketManager->putBucketMaxAge($bucket, $maxAge);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($Info);
+}
diff --git a/vendor/qiniu/php-sdk/examples/put_bucketQuota.php b/vendor/qiniu/php-sdk/examples/put_bucketQuota.php
new file mode 100755
index 0000000..18082b6
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/put_bucketQuota.php
@@ -0,0 +1,22 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+$bucket = 'xxxx';
+$size = 99999;
+$count = 99;
+
+list($Info, $err) = $bucketManager->putBucketQuota($bucket, $size, $count);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($Info);
+}
diff --git a/vendor/qiniu/php-sdk/examples/put_referAntiLeech.php b/vendor/qiniu/php-sdk/examples/put_referAntiLeech.php
new file mode 100755
index 0000000..6828bc9
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/put_referAntiLeech.php
@@ -0,0 +1,23 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+$bucket = 'xxxx';
+$mode = 1;
+$norefer = "1";
+$pattern = "*.qiniu.com";
+
+list($Info, $err) = $bucketManager->putReferAntiLeech($bucket, $mode, $norefer, $pattern);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($Info);
+}
diff --git a/vendor/qiniu/php-sdk/examples/qetag.php b/vendor/qiniu/php-sdk/examples/qetag.php
new file mode 100755
index 0000000..f6aff8a
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/qetag.php
@@ -0,0 +1,11 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+use Qiniu\Etag;
+
+$localFile = "/Users/jemy/Documents/qiniu.mp4";
+list($etag, $err) = Etag::sum($localFile);
+if ($err == null) {
+    echo "Etag: $etag";
+} else {
+    var_dump($err);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_batch_change_mime.php b/vendor/qiniu/php-sdk/examples/rs_batch_change_mime.php
new file mode 100755
index 0000000..d7d07cf
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_batch_change_mime.php
@@ -0,0 +1,28 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use \Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+//每次最多不能超过1000个
+$keyMimePairs = array(
+    'qiniu.mp4' => 'video/x-mp4',
+    'qiniu.png' => 'image/x-png',
+    'qiniu.jpg' => 'image/x-jpg'
+);
+
+$ops = $bucketManager->buildBatchChangeMime($bucket, $keyMimePairs);
+list($ret, $err) = $bucketManager->batch($ops);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_batch_change_type.php b/vendor/qiniu/php-sdk/examples/rs_batch_change_type.php
new file mode 100755
index 0000000..5f3f1cd
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_batch_change_type.php
@@ -0,0 +1,34 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use \Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+//每次最多不能超过1000个
+$keys = array(
+    'qiniu.mp4',
+    'qiniu.png',
+    'qiniu.jpg'
+);
+
+$keyTypePairs = array();
+//type=0表示普通存储,type=1表示低频存储
+foreach ($keys as $key) {
+    $keyTypePairs[$key] = 1;
+}
+
+$ops = $bucketManager->buildBatchChangeType($bucket, $keyTypePairs);
+list($ret, $err) = $bucketManager->batch($ops);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_batch_copy.php b/vendor/qiniu/php-sdk/examples/rs_batch_copy.php
new file mode 100755
index 0000000..988c642
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_batch_copy.php
@@ -0,0 +1,36 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use \Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+//每次最多不能超过1000个
+$keys = array(
+    'qiniu.mp4',
+    'qiniu.png',
+    'qiniu.jpg'
+);
+
+$keyPairs = array();
+foreach ($keys as $key) {
+    $keyPairs[$key] = $key . "_copy";
+}
+
+$srcBucket = $bucket;
+$destBucket = $bucket;
+
+$ops = $bucketManager->buildBatchCopy($srcBucket, $keyPairs, $destBucket, true);
+list($ret, $err) = $bucketManager->batch($ops);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_batch_delete.php b/vendor/qiniu/php-sdk/examples/rs_batch_delete.php
new file mode 100755
index 0000000..4f15586
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_batch_delete.php
@@ -0,0 +1,28 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use \Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+//每次最多不能超过1000个
+$keys = array(
+    'qiniu.mp4',
+    'qiniu.png',
+    'qiniu.jpg'
+);
+
+$ops = $bucketManager->buildBatchDelete($bucket, $keys);
+list($ret, $err) = $bucketManager->batch($ops);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_batch_delete_after_days.php b/vendor/qiniu/php-sdk/examples/rs_batch_delete_after_days.php
new file mode 100755
index 0000000..dabfe84
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_batch_delete_after_days.php
@@ -0,0 +1,34 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use \Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+//每次最多不能超过1000个
+$keys = array(
+    'qiniu.mp4',
+    'qiniu.png',
+    'qiniu.jpg'
+);
+
+$keyDayPairs = array();
+//day=0表示永久存储
+foreach ($keys as $key) {
+    $keyDayPairs[$key] = 0;
+}
+
+$ops = $bucketManager->buildBatchDeleteAfterDays($bucket, $keyDayPairs);
+list($ret, $err) = $bucketManager->batch($ops);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_batch_move.php b/vendor/qiniu/php-sdk/examples/rs_batch_move.php
new file mode 100755
index 0000000..8922522
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_batch_move.php
@@ -0,0 +1,36 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use \Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+//每次最多不能超过1000个
+$keys = array(
+    'qiniu.mp4',
+    'qiniu.png',
+    'qiniu.jpg'
+);
+
+$keyPairs = array();
+foreach ($keys as $key) {
+    $keyPairs[$key . "_copy"] = $key . "_move";
+}
+
+$srcBucket = $bucket;
+$destBucket = $bucket;
+
+$ops = $bucketManager->buildBatchMove($srcBucket, $keyPairs, $destBucket, true);
+list($ret, $err) = $bucketManager->batch($ops);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_batch_stat.php b/vendor/qiniu/php-sdk/examples/rs_batch_stat.php
new file mode 100755
index 0000000..a95fee7
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_batch_stat.php
@@ -0,0 +1,28 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use \Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+//每次最多不能超过1000个
+$keys = array(
+    'qiniu.mp4',
+    'qiniu.png',
+    'qiniu.jpg'
+);
+
+$ops = $bucketManager->buildBatchStat($bucket, $keys);
+list($ret, $err) = $bucketManager->batch($ops);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_bucket_domains.php b/vendor/qiniu/php-sdk/examples/rs_bucket_domains.php
new file mode 100755
index 0000000..ea27cdc
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_bucket_domains.php
@@ -0,0 +1,19 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use \Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+list($domains, $err) = $bucketManager->domains($bucket);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($domains);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_buckets.php b/vendor/qiniu/php-sdk/examples/rs_buckets.php
new file mode 100755
index 0000000..5fe1304
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_buckets.php
@@ -0,0 +1,19 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use \Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+list($buckets, $err) = $bucketManager->buckets(true);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($buckets);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_change_mime.php b/vendor/qiniu/php-sdk/examples/rs_change_mime.php
new file mode 100755
index 0000000..0d3f3ad
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_change_mime.php
@@ -0,0 +1,20 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use \Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+
+$key = 'qiniu.mp4';
+$newMime = 'video/x-mp4';
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+$err = $bucketManager->changeMime($bucket, $key, $newMime);
+if ($err) {
+    print_r($err);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_change_status.php b/vendor/qiniu/php-sdk/examples/rs_change_status.php
new file mode 100755
index 0000000..cbcea5c
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_change_status.php
@@ -0,0 +1,20 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use \Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+
+$key = "qiniu.jpg";
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+$status = 1;//0表示启用;1表示禁用
+
+$err = $bucketManager->changeStatus($bucket, $key, $status);
+if ($err) {
+    print_r($err);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_change_type.php b/vendor/qiniu/php-sdk/examples/rs_change_type.php
new file mode 100755
index 0000000..acb8963
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_change_type.php
@@ -0,0 +1,20 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use \Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+
+$key = "qiniu.mp4";
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+$fileType = 1;//0 表示普通存储,1表示低频存储
+
+$err = $bucketManager->changeType($bucket, $key, $fileType);
+if ($err) {
+    print_r($err);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_copy.php b/vendor/qiniu/php-sdk/examples/rs_copy.php
new file mode 100755
index 0000000..10e7de8
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_copy.php
@@ -0,0 +1,22 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use \Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+
+$key = "qiniu.mp4";
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+$srcBucket = $bucket;
+$destBucket = $bucket;
+$srcKey = $key;
+$destKey = $key . "_copy";
+$err = $bucketManager->copy($srcBucket, $srcKey, $destBucket, $destKey, true);
+if ($err) {
+    print_r($err);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_delete.php b/vendor/qiniu/php-sdk/examples/rs_delete.php
new file mode 100755
index 0000000..365d3be
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_delete.php
@@ -0,0 +1,17 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use \Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+
+$key = "qiniu.mp4_copy";
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+$err = $bucketManager->delete($bucket, $key);
+if ($err) {
+    print_r($err);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_delete_after_days.php b/vendor/qiniu/php-sdk/examples/rs_delete_after_days.php
new file mode 100755
index 0000000..ba0b586
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_delete_after_days.php
@@ -0,0 +1,20 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use \Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+
+$key = 'qiniu.mp4';
+$days = 10;
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+$err = $bucketManager->deleteAfterDays($bucket, $key, $days);
+if ($err) {
+    print_r($err);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_download_urls.php b/vendor/qiniu/php-sdk/examples/rs_download_urls.php
new file mode 100755
index 0000000..522b9f2
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_download_urls.php
@@ -0,0 +1,17 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+
+// 构建Auth对象
+$auth = new Auth($accessKey, $secretKey);
+
+// 私有空间中的外链 http://<domain>/<file_key>
+$baseUrl = 'http://if-pri.qiniudn.com/qiniu.png?imageView2/1/h/500';
+// 对链接进行签名
+$signedUrl = $auth->privateDownloadUrl($baseUrl);
+
+echo $signedUrl;
diff --git a/vendor/qiniu/php-sdk/examples/rs_fetch.php b/vendor/qiniu/php-sdk/examples/rs_fetch.php
new file mode 100755
index 0000000..6792410
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_fetch.php
@@ -0,0 +1,34 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+use Qiniu\Storage\BucketManager;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+
+$auth = new Auth($accessKey, $secretKey);
+$bucketManager = new BucketManager($auth);
+
+$url = 'http://devtools.qiniu.com/qiniu.png';
+$key = time() . '.png';
+
+// 指定抓取的文件保存名称
+list($ret, $err) = $bucketManager->fetch($url, $bucket, $key);
+echo "=====> fetch $url to bucket: $bucket  key: $key\n";
+if ($err !== null) {
+    var_dump($err);
+} else {
+    print_r($ret);
+}
+
+// 不指定key时,以文件内容的hash作为文件名
+$key = null;
+list($ret, $err) = $bucketManager->fetch($url, $bucket, $key);
+echo "=====> fetch $url to bucket: $bucket  key: $(etag)\n";
+if ($err !== null) {
+    var_dump($err);
+} else {
+    print_r($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_move.php b/vendor/qiniu/php-sdk/examples/rs_move.php
new file mode 100755
index 0000000..5610585
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_move.php
@@ -0,0 +1,22 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use \Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+
+$key = "qiniu.mp4";
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+$srcBucket = $bucket;
+$destBucket = $bucket;
+$srcKey = $key . "_copy";
+$destKey = $key . "_move";
+$err = $bucketManager->move($srcBucket, $srcKey, $destBucket, $destKey, true);
+if ($err) {
+    print_r($err);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_prefetch.php b/vendor/qiniu/php-sdk/examples/rs_prefetch.php
new file mode 100755
index 0000000..de947a7
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_prefetch.php
@@ -0,0 +1,17 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use \Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+
+$key = "qiniu.mp4";
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+$err = $bucketManager->prefetch($bucket, $key);
+if ($err) {
+    print_r($err);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_pub_domain.php b/vendor/qiniu/php-sdk/examples/rs_pub_domain.php
new file mode 100755
index 0000000..2e81922
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_pub_domain.php
@@ -0,0 +1,19 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use \Qiniu\Auth;
+
+$accessKey = 'gwd_gV4gPKZZsmEOvAuNU1AcumicmuHooTfu64q5';
+$secretKey = '9G4isTkVuj5ITPqH1ajhljJMTc2k4m-hZh5r5ZsK';
+$bucket = 'file';
+
+$domain = 'lfxfile.qiniuts.com';
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+$err = $bucketManager->publishDomain($bucket, $domain);
+if ($err) {
+    print_r($err);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_stat.php b/vendor/qiniu/php-sdk/examples/rs_stat.php
new file mode 100755
index 0000000..891e4e0
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_stat.php
@@ -0,0 +1,19 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use \Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+
+$key = "qiniu.mp4";
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+list($fileInfo, $err) = $bucketManager->stat($bucket, $key);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($fileInfo);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rsf_list_bucket.php b/vendor/qiniu/php-sdk/examples/rsf_list_bucket.php
new file mode 100755
index 0000000..5ce9a62
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rsf_list_bucket.php
@@ -0,0 +1,46 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+use Qiniu\Storage\BucketManager;
+
+// http://developer.qiniu.com/docs/v6/api/reference/rs/list.html#list-description
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+$auth = new Auth($accessKey, $secretKey);
+$bucketManager = new BucketManager($auth);
+
+
+// 要列取文件的公共前缀
+$prefix = '';
+
+// 上次列举返回的位置标记,作为本次列举的起点信息。
+$marker = '';
+
+// 本次列举的条目数
+$limit = 200;
+
+$delimiter = '/';
+
+// 列举文件
+do {
+    list($ret, $err) = $bucketManager->listFiles($bucket, $prefix, $marker, $limit, $delimiter);
+    if ($err !== null) {
+        echo "\n====> list file err: \n";
+        var_dump($err);
+    } else {
+        $marker = null;
+        if (array_key_exists('marker', $ret)) {
+            $marker = $ret['marker'];
+        }
+        echo "Marker: $marker\n";
+        echo "\nList Items====>\n";
+        //var_dump($ret['items']);
+        print('items count:' . count($ret['items']) . "\n");
+        if (array_key_exists('commonPrefixes', $ret)) {
+            print_r($ret['commonPrefixes']);
+        }
+    }
+} while (!empty($marker));
diff --git a/vendor/qiniu/php-sdk/examples/rsf_list_files.php b/vendor/qiniu/php-sdk/examples/rsf_list_files.php
new file mode 100755
index 0000000..a3981c5
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rsf_list_files.php
@@ -0,0 +1,38 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+use Qiniu\Storage\BucketManager;
+
+// http://developer.qiniu.com/docs/v6/api/reference/rs/list.html#list-description
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+$auth = new Auth($accessKey, $secretKey);
+$bucketManager = new BucketManager($auth);
+
+
+// 要列取文件的公共前缀
+$prefix = '';
+
+// 上次列举返回的位置标记,作为本次列举的起点信息。
+$marker = '';
+
+// 本次列举的条目数
+$limit = 100;
+
+$delimiter = '/';
+
+// 列举文件
+list($ret, $err) = $bucketManager->listFiles($bucket, $prefix, $marker, $limit, $delimiter);
+if ($err !== null) {
+    echo "\n====> list file err: \n";
+    var_dump($err);
+} else {
+    if (array_key_exists('marker', $ret)) {
+        echo "Marker:" . $ret["marker"] . "\n";
+    }
+    echo "\nList Iterms====>\n";
+    //var_dump($ret['items']);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rsf_v2list_bucket.php b/vendor/qiniu/php-sdk/examples/rsf_v2list_bucket.php
new file mode 100755
index 0000000..4dcf270
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rsf_v2list_bucket.php
@@ -0,0 +1,33 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+use Qiniu\Storage\BucketManager;
+
+// http://developer.qiniu.com/docs/v6/api/reference/rs/list.html#list-description
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = 'xxxx';
+$auth = new Auth($accessKey, $secretKey);
+$bucketManager = new BucketManager($auth);
+
+
+// 要列取文件的公共前缀
+$prefix = '';
+
+// 上次列举返回的位置标记,作为本次列举的起点信息。
+$marker = '';
+
+// 本次列举的条目数
+$limit = 1000;
+
+$delimiter = '';
+
+list($ret, $err) = $bucketManager->listFilesv2($bucket, $prefix, $marker, $limit, $delimiter, true);
+
+if ($err) {
+    print_r($err);
+} else {
+    print_r($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/saveas.php b/vendor/qiniu/php-sdk/examples/saveas.php
new file mode 100755
index 0000000..d896f3b
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/saveas.php
@@ -0,0 +1,28 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+use Qiniu\Processing\PersistentFop;
+
+// 后台来获取AK, SK
+$accessKey = 'Access_Key';
+$secretKey = 'Secret_Key';
+
+//生成EncodedEntryURI的值
+$entry = '<bucket>:<Key>';//<Key>为生成缩略图的文件名
+//生成的值
+$encodedEntryURI = \Qiniu\base64_urlSafeEncode($entry);
+
+//使用SecretKey对新的下载URL进行HMAC1-SHA1签名
+$newurl = "78re52.com1.z0.glb.clouddn.com/resource/Ship.jpg?imageView2/2/w/200/h/200|saveas/" . $encodedEntryURI;
+
+$sign = hash_hmac("sha1", $newurl, $secretKey, true);
+
+//对签名进行URL安全的Base64编码
+$encodedSign = \Qiniu\base64_urlSafeEncode($sign);
+//最终得到的完整下载URL
+$finalURL = "http://" . $newurl . "/sign/" . $accessKey . ":" . $encodedSign;
+
+$callbackBody = file_get_contents("$finalURL");
+
+echo $callbackBody;
diff --git a/vendor/qiniu/php-sdk/examples/update_bucketEvent.php b/vendor/qiniu/php-sdk/examples/update_bucketEvent.php
new file mode 100755
index 0000000..26347ac
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/update_bucketEvent.php
@@ -0,0 +1,25 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+$bucket = 'xxxx';
+$name = 'demo';
+$prefix = 'test';
+$suffix = 'mp4';
+$event = 'mkfile';
+$callbackURL = 'https://www.qiniu.com';
+
+list($Info, $err) = $bucketManager->updateBucketEvent($bucket, $name, $prefix, $suffix, $event, $callbackURL);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($Info);
+}
diff --git a/vendor/qiniu/php-sdk/examples/update_bucketLifecycleRule.php b/vendor/qiniu/php-sdk/examples/update_bucketLifecycleRule.php
new file mode 100755
index 0000000..85eb07a
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/update_bucketLifecycleRule.php
@@ -0,0 +1,30 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+
+$auth = new Auth($accessKey, $secretKey);
+$config = new \Qiniu\Config();
+$bucketManager = new \Qiniu\Storage\BucketManager($auth, $config);
+
+$bucket = 'xxxx';
+$name = 'demo';
+$prefix = 'test';
+$delete_after_days = 90;
+$to_line_after_days =80;
+
+list($Info, $err) = $bucketManager->updateBucketLifecycleRule(
+    $bucket,
+    $name,
+    $prefix,
+    $delete_after_days,
+    $to_line_after_days
+);
+if ($err) {
+    print_r($err);
+} else {
+    print_r($Info);
+}
diff --git a/vendor/qiniu/php-sdk/examples/upload_and_callback.php b/vendor/qiniu/php-sdk/examples/upload_and_callback.php
new file mode 100755
index 0000000..1a70705
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/upload_and_callback.php
@@ -0,0 +1,34 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+use Qiniu\Auth;
+use Qiniu\Storage\UploadManager;
+
+// use Qiniu\Config;
+// use Qiniu\Zone;
+
+// 指定zone上传
+// $zone = Zone::qvmZonez0(); //华东QVM内网上传指定host
+// $config = new Config($zone);
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+$auth = new Auth($accessKey, $secretKey);
+// 上传文件到七牛后, 七牛将文件名和文件大小回调给业务服务器.
+// 可参考文档: http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html
+$policy = array(
+    'callbackUrl' => 'http://your.domain.com/upload_verify_callback.php',
+    'callbackBody' => 'filename=$(fname)&filesize=$(fsize)'
+);
+$uptoken = $auth->uploadToken($bucket, null, 3600, $policy);
+//上传文件的本地路径
+$filePath = './php-logo.png';
+//指定 config
+// $uploadMgr = new UploadManager($config);
+$uploadMgr = new UploadManager();
+list($ret, $err) = $uploadMgr->putFile($uptoken, null, $filePath);
+echo "\n====> putFile result: \n";
+if ($err !== null) {
+    var_dump($err);
+} else {
+    var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/upload_and_pfop.php b/vendor/qiniu/php-sdk/examples/upload_and_pfop.php
new file mode 100755
index 0000000..898c09c
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/upload_and_pfop.php
@@ -0,0 +1,38 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+use Qiniu\Storage\UploadManager;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+$auth = new Auth($accessKey, $secretKey);
+
+
+// 在七牛保存的文件名
+$key = 'php-logo.png';
+$uploadMgr = new UploadManager();
+
+$pfop = "imageMogr2/rotate/90|saveas/" . \Qiniu\base64_urlSafeEncode($bucket . ":php-logo-rotate.png");
+
+//转码完成后通知到你的业务服务器。(公网可以访问,并相应200 OK)
+$notifyUrl = 'http://notify.fake.com';
+
+//独立的转码队列:https://portal.qiniu.com/mps/pipeline
+$pipeline = 'sdktest';
+
+$policy = array(
+    'persistentOps' => $pfop,
+    'persistentNotifyUrl' => $notifyUrl,
+    'persistentPipeline' => $pipeline
+);
+$token = $auth->uploadToken($bucket, null, 3600, $policy);
+
+list($ret, $err) = $uploadMgr->putFile($token, null, $key);
+echo "\n====> putFile result: \n";
+if ($err !== null) {
+    var_dump($err);
+} else {
+    var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/upload_mgr_init.php b/vendor/qiniu/php-sdk/examples/upload_mgr_init.php
new file mode 100755
index 0000000..3459ef1
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/upload_mgr_init.php
@@ -0,0 +1,18 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+use Qiniu\Storage\UploadManager;
+
+$accessKey = 'Access_Key';
+$secretKey = 'Secret_Key';
+$auth = new Auth($accessKey, $secretKey);
+
+// 空间名  http://developer.qiniu.com/docs/v6/api/overview/concepts.html#bucket
+$bucket = 'Bucket_Name';
+
+// 生成上传Token
+$token = $auth->uploadToken($bucket);
+
+// 构建 UploadManager 对象
+$uploadMgr = new UploadManager();
diff --git a/vendor/qiniu/php-sdk/examples/upload_multi_demos.php b/vendor/qiniu/php-sdk/examples/upload_multi_demos.php
new file mode 100755
index 0000000..3bbcd60
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/upload_multi_demos.php
@@ -0,0 +1,85 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+use Qiniu\Storage\UploadManager;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+$pipeline = 'sdktest';
+
+$auth = new Auth($accessKey, $secretKey);
+$token = $auth->uploadToken($bucket);
+$uploadMgr = new UploadManager();
+
+//----------------------------------------upload demo1 ----------------------------------------
+// 上传字符串到七牛
+list($ret, $err) = $uploadMgr->put($token, null, 'content string');
+echo "\n====> put result: \n";
+if ($err !== null) {
+    var_dump($err);
+} else {
+    var_dump($ret);
+}
+
+
+//----------------------------------------upload demo2 ----------------------------------------
+// 上传文件到七牛
+$filePath = './php-logo.png';
+$key = 'php-logo.png';
+list($ret, $err) = $uploadMgr->putFile($token, $key, $filePath);
+echo "\n====> putFile result: \n";
+if ($err !== null) {
+    var_dump($err);
+} else {
+    var_dump($ret);
+}
+
+
+//----------------------------------------upload demo3 ----------------------------------------
+// 上传文件到七牛后, 七牛将文件名和文件大小回调给业务服务器.
+// 可参考文档: http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html
+$policy = array(
+    'callbackUrl' => 'http://172.30.251.210/upload_verify_callback.php',
+    'callbackBody' => 'filename=$(fname)&filesize=$(fsize)'
+//  'callbackBodyType' => 'application/json',                       
+//  'callbackBody' => '{"filename":$(fname), "filesize": $(fsize)}'  //设置application/json格式回调
+);
+$token = $auth->uploadToken($bucket, null, 3600, $policy);
+
+
+list($ret, $err) = $uploadMgr->putFile($token, null, $key);
+echo "\n====> putFile result: \n";
+if ($err !== null) {
+    var_dump($err);
+} else {
+    var_dump($ret);
+}
+
+
+//----------------------------------------upload demo4 ----------------------------------------
+//上传视频,上传完成后进行m3u8的转码, 并给视频打水印
+$wmImg = Qiniu\base64_urlSafeEncode('http://devtools.qiniudn.com/qiniu.png');
+$pfop = "avthumb/m3u8/wmImage/$wmImg";
+
+//转码完成后回调到业务服务器。(公网可以访问,并相应200 OK)
+$notifyUrl = 'http://notify.fake.com';
+
+//独立的转码队列:https://portal.qiniu.com/mps/pipeline
+
+
+$policy = array(
+    'persistentOps' => $pfop,
+    'persistentNotifyUrl' => $notifyUrl,
+    'persistentPipeline' => $pipeline
+);
+$token = $auth->uploadToken($bucket, null, 3600, $policy);
+print($token);
+list($ret, $err) = $uploadMgr->putFile($token, null, $key);
+echo "\n====> putFile result: \n";
+if ($err !== null) {
+    var_dump($err);
+} else {
+    var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/upload_simple_file.php b/vendor/qiniu/php-sdk/examples/upload_simple_file.php
new file mode 100755
index 0000000..9d003f0
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/upload_simple_file.php
@@ -0,0 +1,37 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+// 引入鉴权类
+use Qiniu\Auth;
+
+// 引入上传类
+use Qiniu\Storage\UploadManager;
+
+// 需要填写你的 Access Key 和 Secret Key
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+
+// 构建鉴权对象
+$auth = new Auth($accessKey, $secretKey);
+
+// 生成上传 Token
+$token = $auth->uploadToken($bucket);
+
+// 要上传文件的本地路径
+$filePath = './php-logo.png';
+
+// 上传到七牛后保存的文件名
+$key = 'my-php-logo.png';
+
+// 初始化 UploadManager 对象并进行文件的上传。
+$uploadMgr = new UploadManager();
+
+// 调用 UploadManager 的 putFile 方法进行文件的上传。
+list($ret, $err) = $uploadMgr->putFile($token, $key, $filePath);
+echo "\n====> putFile result: \n";
+if ($err !== null) {
+    var_dump($err);
+} else {
+    var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/upload_tokens.php b/vendor/qiniu/php-sdk/examples/upload_tokens.php
new file mode 100755
index 0000000..90f2423
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/upload_tokens.php
@@ -0,0 +1,68 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use \Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+
+// 初始化Auth状态
+$auth = new Auth($accessKey, $secretKey);
+
+// 简单上传凭证
+$expires = 3600;
+
+$policy = null;
+$upToken = $auth->uploadToken($bucket, null, $expires, $policy, true);
+print($upToken . "\n");
+
+// 自定义凭证有效期(示例2小时)
+$expires = 7200;
+$upToken = $auth->uploadToken($bucket, null, $expires, $policy, true);
+print($upToken . "\n");
+
+// 覆盖上传凭证
+$expires = 3600;
+$keyToOverwrite = 'qiniu.mp4';
+$upToken = $auth->uploadToken($bucket, $keyToOverwrite, $expires, $policy, true);
+print($upToken . "\n");
+
+//自定义上传回复(非callback模式)凭证
+$returnBody = '{"key":"$(key)","hash":"$(etag)","fsize":$(fsize),"bucket":"$(bucket)","name":"$(x:name)"}';
+$policy = array(
+    'returnBody' => $returnBody
+);
+$upToken = $auth->uploadToken($bucket, null, $expires, $policy, true);
+print($upToken . "\n");
+
+//带回调业务服务器的凭证(application/json)
+$policy = array(
+    'callbackUrl' => 'http://api.example.com/qiniu/upload/callback',
+    'callbackBody' => '{"key":"$(key)","hash":"$(etag)","fsize":$(fsize),"bucket":"$(bucket)","name":"$(x:name)"}',
+    'callbackBodyType' => 'application/json'
+);
+$upToken = $auth->uploadToken($bucket, null, $expires, $policy, true);
+print($upToken . "\n");
+
+
+//带回调业务服务器的凭证(application/x-www-form-urlencoded)
+$policy = array(
+    'callbackUrl' => 'http://api.example.com/qiniu/upload/callback',
+    'callbackBody' => 'key=$(key)&hash=$(etag)&bucket=$(bucket)&fsize=$(fsize)&name=$(x:name)'
+);
+$upToken = $auth->uploadToken($bucket, null, $expires, $policy, true);
+print($upToken . "\n");
+
+//带数据处理的凭证
+$saveMp4Entry = \Qiniu\base64_urlSafeEncode($bucket . ":avthumb_test_target.mp4");
+$saveJpgEntry = \Qiniu\base64_urlSafeEncode($bucket . ":vframe_test_target.jpg");
+$avthumbMp4Fop = "avthumb/mp4|saveas/" . $saveMp4Entry;
+$vframeJpgFop = "vframe/jpg/offset/1|saveas/" . $saveJpgEntry;
+$policy = array(
+    'persistentOps' => $avthumbMp4Fop . ";" . $vframeJpgFop,
+    'persistentPipeline' => "video-pipe",
+    'persistentNotifyUrl' => "http://api.example.com/qiniu/pfop/notify",
+);
+$upToken = $auth->uploadToken($bucket, null, $expires, $policy, true);
+print($upToken . "\n");
diff --git a/vendor/qiniu/php-sdk/examples/upload_verify_callback.php b/vendor/qiniu/php-sdk/examples/upload_verify_callback.php
new file mode 100755
index 0000000..ffbbc80
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/upload_verify_callback.php
@@ -0,0 +1,32 @@
+<?php
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$bucket = getenv('QINIU_TEST_BUCKET');
+
+$auth = new Auth($accessKey, $secretKey);
+
+//获取回调的body信息
+$callbackBody = file_get_contents('php://input');
+
+//回调的contentType
+$contentType = 'application/x-www-form-urlencoded';
+
+//回调的签名信息,可以验证该回调是否来自七牛
+$authorization = $_SERVER['HTTP_AUTHORIZATION'];
+
+//七牛回调的url,具体可以参考:http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html
+$url = 'http://172.30.251.210/upload_verify_callback.php';
+
+$isQiniuCallback = $auth->verifyCallback($contentType, $authorization, $url, $callbackBody);
+
+if ($isQiniuCallback) {
+    $resp = array('ret' => 'success');
+} else {
+    $resp = array('ret' => 'failed');
+}
+
+echo json_encode($resp);
diff --git a/vendor/qiniu/php-sdk/phpunit.xml.dist b/vendor/qiniu/php-sdk/phpunit.xml.dist
new file mode 100755
index 0000000..840f6e5
--- /dev/null
+++ b/vendor/qiniu/php-sdk/phpunit.xml.dist
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit backupGlobals="false"
+         backupStaticAttributes="false"
+         bootstrap="./tests/bootstrap.php"
+         colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         processIsolation="false"
+         stopOnFailure="false"
+>
+    <testsuites>
+        <testsuite name="Qiniu SDK Tests">
+            <directory>tests</directory>
+        </testsuite>
+    </testsuites>
+
+</phpunit>
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Auth.php b/vendor/qiniu/php-sdk/src/Qiniu/Auth.php
new file mode 100755
index 0000000..b5e4a6b
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Auth.php
@@ -0,0 +1,187 @@
+<?php
+namespace Qiniu;
+
+use Qiniu\Zone;
+
+final class Auth
+{
+    private $accessKey;
+    private $secretKey;
+
+    public function __construct($accessKey, $secretKey)
+    {
+        $this->accessKey = $accessKey;
+        $this->secretKey = $secretKey;
+    }
+
+    public function getAccessKey()
+    {
+        return $this->accessKey;
+    }
+
+    public function sign($data)
+    {
+        $hmac = hash_hmac('sha1', $data, $this->secretKey, true);
+        return $this->accessKey . ':' . \Qiniu\base64_urlSafeEncode($hmac);
+    }
+
+    public function signWithData($data)
+    {
+        $encodedData = \Qiniu\base64_urlSafeEncode($data);
+        return $this->sign($encodedData) . ':' . $encodedData;
+    }
+
+    public function signRequest($urlString, $body, $contentType = null)
+    {
+        $url = parse_url($urlString);
+        $data = '';
+        if (array_key_exists('path', $url)) {
+            $data = $url['path'];
+        }
+        if (array_key_exists('query', $url)) {
+            $data .= '?' . $url['query'];
+        }
+        $data .= "\n";
+
+        if ($body !== null && $contentType === 'application/x-www-form-urlencoded') {
+            $data .= $body;
+        }
+        return $this->sign($data);
+    }
+
+    public function verifyCallback($contentType, $originAuthorization, $url, $body)
+    {
+        $authorization = 'QBox ' . $this->signRequest($url, $body, $contentType);
+        return $originAuthorization === $authorization;
+    }
+
+    public function privateDownloadUrl($baseUrl, $expires = 3600)
+    {
+        $deadline = time() + $expires;
+
+        $pos = strpos($baseUrl, '?');
+        if ($pos !== false) {
+            $baseUrl .= '&e=';
+        } else {
+            $baseUrl .= '?e=';
+        }
+        $baseUrl .= $deadline;
+
+        $token = $this->sign($baseUrl);
+        return "$baseUrl&token=$token";
+    }
+
+    public function uploadToken($bucket, $key = null, $expires = 3600, $policy = null, $strictPolicy = true)
+    {
+        $deadline = time() + $expires;
+        $scope = $bucket;
+        if ($key !== null) {
+            $scope .= ':' . $key;
+        }
+
+        $args = self::copyPolicy($args, $policy, $strictPolicy);
+        $args['scope'] = $scope;
+        $args['deadline'] = $deadline;
+
+        $b = json_encode($args);
+        return $this->signWithData($b);
+    }
+
+    /**
+     *上传策略,参数规格详见
+     *http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html
+     */
+    private static $policyFields = array(
+        'callbackUrl',
+        'callbackBody',
+        'callbackHost',
+        'callbackBodyType',
+        'callbackFetchKey',
+
+        'returnUrl',
+        'returnBody',
+
+        'endUser',
+        'saveKey',
+        'insertOnly',
+
+        'detectMime',
+        'mimeLimit',
+        'fsizeMin',
+        'fsizeLimit',
+
+        'persistentOps',
+        'persistentNotifyUrl',
+        'persistentPipeline',
+
+        'deleteAfterDays',
+        'fileType',
+        'isPrefixalScope',
+    );
+
+    private static function copyPolicy(&$policy, $originPolicy, $strictPolicy)
+    {
+        if ($originPolicy === null) {
+            return array();
+        }
+        foreach ($originPolicy as $key => $value) {
+            if (!$strictPolicy || in_array((string)$key, self::$policyFields, true)) {
+                $policy[$key] = $value;
+            }
+        }
+        return $policy;
+    }
+
+    public function authorization($url, $body = null, $contentType = null)
+    {
+        $authorization = 'QBox ' . $this->signRequest($url, $body, $contentType);
+        return array('Authorization' => $authorization);
+    }
+
+    public function authorizationV2($url, $method, $body = null, $contentType = null)
+    {
+        $urlItems = parse_url($url);
+        $host = $urlItems['host'];
+
+        if (isset($urlItems['port'])) {
+            $port = $urlItems['port'];
+        } else {
+            $port = '';
+        }
+
+        $path = $urlItems['path'];
+        if (isset($urlItems['query'])) {
+            $query = $urlItems['query'];
+        } else {
+            $query = '';
+        }
+
+        //write request uri
+        $toSignStr = $method . ' ' . $path;
+        if (!empty($query)) {
+            $toSignStr .= '?' . $query;
+        }
+
+        //write host and port
+        $toSignStr .= "\nHost: " . $host;
+        if (!empty($port)) {
+            $toSignStr .= ":" . $port;
+        }
+
+        //write content type
+        if (!empty($contentType)) {
+            $toSignStr .= "\nContent-Type: " . $contentType;
+        }
+
+        $toSignStr .= "\n\n";
+
+        //write body
+        if (!empty($body)) {
+            $toSignStr .= $body;
+        }
+
+        $sign = $this->sign($toSignStr);
+        $auth = 'Qiniu ' . $sign;
+        return array('Authorization' => $auth);
+    }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Cdn/CdnManager.php b/vendor/qiniu/php-sdk/src/Qiniu/Cdn/CdnManager.php
new file mode 100755
index 0000000..c324757
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Cdn/CdnManager.php
@@ -0,0 +1,192 @@
+<?php
+
+namespace Qiniu\Cdn;
+
+use Qiniu\Auth;
+use Qiniu\Http\Error;
+use Qiniu\Http\Client;
+
+final class CdnManager
+{
+
+    private $auth;
+    private $server;
+
+    public function __construct(Auth $auth)
+    {
+        $this->auth = $auth;
+        $this->server = 'http://fusion.qiniuapi.com';
+    }
+
+    /**
+     * @param array $urls 待刷新的文件链接数组
+     * @return array
+     */
+    public function refreshUrls(array $urls)
+    {
+        return $this->refreshUrlsAndDirs($urls, array());
+    }
+
+    /**
+     * @param array $dirs 待刷新的文件链接数组
+     * @return array
+     * 目前客户默认没有目录刷新权限,刷新会有400038报错,参考:https://developer.qiniu.com/fusion/api/1229/cache-refresh
+     * 需要刷新目录请工单联系技术支持 https://support.qiniu.com/tickets/category
+     */
+    public function refreshDirs(array $dirs)
+    {
+        return $this->refreshUrlsAndDirs(array(), $dirs);
+    }
+
+    /**
+     * @param array $urls 待刷新的文件链接数组
+     * @param array $dirs 待刷新的目录链接数组
+     *
+     * @return array 刷新的请求回复和错误,参考 examples/cdn_manager.php 代码
+     * @link http://developer.qiniu.com/article/fusion/api/refresh.html
+     *
+     * 目前客户默认没有目录刷新权限,刷新会有400038报错,参考:https://developer.qiniu.com/fusion/api/1229/cache-refresh
+     * 需要刷新目录请工单联系技术支持 https://support.qiniu.com/tickets/category
+     */
+    public function refreshUrlsAndDirs(array $urls, array  $dirs)
+    {
+        $req = array();
+        if (!empty($urls)) {
+            $req['urls'] = $urls;
+        }
+        if (!empty($dirs)) {
+            $req['dirs'] = $dirs;
+        }
+
+        $url = $this->server . '/v2/tune/refresh';
+        $body = json_encode($req);
+        return $this->post($url, $body);
+    }
+
+    /**
+     * @param array $urls 待预取的文件链接数组
+     *
+     * @return array 预取的请求回复和错误,参考 examples/cdn_manager.php 代码
+     *
+     * @link http://developer.qiniu.com/article/fusion/api/refresh.html
+     */
+    public function prefetchUrls(array $urls)
+    {
+        $req = array(
+            'urls' => $urls,
+        );
+
+        $url = $this->server . '/v2/tune/prefetch';
+        $body = json_encode($req);
+        return $this->post($url, $body);
+    }
+
+    /**
+     * @param array $domains 待获取带宽数据的域名数组
+     * @param string $startDate 开始的日期,格式类似 2017-01-01
+     * @param string $endDate 结束的日期,格式类似 2017-01-01
+     * @param string $granularity 获取数据的时间间隔,可以是 5min, hour 或者 day
+     *
+     * @return array 带宽数据和错误信息,参考 examples/cdn_manager.php 代码
+     *
+     * @link http://developer.qiniu.com/article/fusion/api/traffic-bandwidth.html
+     */
+    public function getBandwidthData(array $domains, $startDate, $endDate, $granularity)
+    {
+        $req = array();
+        $req['domains'] = implode(';', $domains);
+        $req['startDate'] = $startDate;
+        $req['endDate'] = $endDate;
+        $req['granularity'] = $granularity;
+
+        $url = $this->server . '/v2/tune/bandwidth';
+        $body = json_encode($req);
+        return $this->post($url, $body);
+    }
+
+    /**
+     * @param array $domains 待获取流量数据的域名数组
+     * @param string $startDate 开始的日期,格式类似 2017-01-01
+     * @param string $endDate 结束的日期,格式类似 2017-01-01
+     * @param string $granularity 获取数据的时间间隔,可以是 5min, hour 或者 day
+     *
+     * @return array 流量数据和错误信息,参考 examples/cdn_manager.php 代码
+     *
+     * @link http://developer.qiniu.com/article/fusion/api/traffic-bandwidth.html
+     */
+    public function getFluxData(array $domains, $startDate, $endDate, $granularity)
+    {
+        $req = array();
+        $req['domains'] = implode(';', $domains);
+        $req['startDate'] = $startDate;
+        $req['endDate'] = $endDate;
+        $req['granularity'] = $granularity;
+
+        $url = $this->server . '/v2/tune/flux';
+        $body = json_encode($req);
+        return $this->post($url, $body);
+    }
+
+    /**
+     * @param array $domains 待获取日志下载链接的域名数组
+     * @param string $logDate 获取指定日期的日志下载链接,格式类似 2017-01-01
+     *
+     * @return array 日志下载链接数据和错误信息,参考 examples/cdn_manager.php 代码
+     *
+     * @link http://developer.qiniu.com/article/fusion/api/log.html
+     */
+    public function getCdnLogList(array $domains, $logDate)
+    {
+        $req = array();
+        $req['domains'] = implode(';', $domains);
+        $req['day'] = $logDate;
+
+        $url = $this->server . '/v2/tune/log/list';
+        $body = json_encode($req);
+        return $this->post($url, $body);
+    }
+
+    private function post($url, $body)
+    {
+        $headers = $this->auth->authorization($url, $body, 'application/json');
+        $headers['Content-Type'] = 'application/json';
+        $ret = Client::post($url, $body, $headers);
+        if (!$ret->ok()) {
+            return array(null, new Error($url, $ret));
+        }
+        $r = ($ret->body === null) ? array() : $ret->json();
+        return array($r, null);
+    }
+
+    /**
+     * 构建时间戳防盗链鉴权的访问外链
+     *
+     * @param string $rawUrl 需要签名的资源url
+     * @param string $encryptKey 时间戳防盗链密钥
+     * @param string $durationInSeconds 链接的有效期(以秒为单位)
+     *
+     * @return string 带鉴权信息的资源外链,参考 examples/cdn_timestamp_antileech.php 代码
+     */
+    public static function createTimestampAntiLeechUrl($rawUrl, $encryptKey, $durationInSeconds)
+    {
+        $parsedUrl = parse_url($rawUrl);
+
+        $deadline = time() + $durationInSeconds;
+        $expireHex = dechex($deadline);
+
+        $path = isset($parsedUrl['path']) ? $parsedUrl['path'] : '';
+        $path = implode('/', array_map('rawurlencode', explode('/', $path)));
+        $strToSign = $encryptKey . $path . $expireHex;
+        $signStr = md5($strToSign);
+
+        $url = $parsedUrl['scheme'].'://'.$parsedUrl['host'].$path;
+        
+        if (isset($parsedUrl['query'])) {
+            $signedUrl = $url . '&sign=' . $signStr . '&t=' . $expireHex;
+        } else {
+            $signedUrl = $url . '?sign=' . $signStr . '&t=' . $expireHex;
+        }
+
+        return $signedUrl;
+    }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Config.php b/vendor/qiniu/php-sdk/src/Qiniu/Config.php
new file mode 100755
index 0000000..ec75df6
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Config.php
@@ -0,0 +1,140 @@
+<?php
+namespace Qiniu;
+
+final class Config
+{
+    const SDK_VER = '7.2.9';
+
+    const BLOCK_SIZE = 4194304; //4*1024*1024 分块上传块大小,该参数为接口规格,不能修改
+
+    const RSF_HOST = 'rsf.qiniu.com';
+    const API_HOST = 'api.qiniu.com';
+    const RS_HOST = 'rs.qiniu.com';      //RS Host
+    const UC_HOST = 'uc.qbox.me';              //UC Host
+    const RTCAPI_HOST = 'http://rtc.qiniuapi.com';
+    const ARGUS_HOST = 'argus.atlab.ai';
+    const CASTER_HOST = 'pili-caster.qiniuapi.com';
+    const SMS_HOST="https://sms.qiniuapi.com";
+    const RTCAPI_VERSION = 'v3';
+    const SMS_VERSION='v1';
+
+    // Zone 空间对应的存储区域
+    public $region;
+    //BOOL 是否使用https域名
+    public $useHTTPS;
+    //BOOL 是否使用CDN加速上传域名
+    public $useCdnDomains;
+    // Zone Cache
+    private $regionCache;
+
+    // 构造函数
+    public function __construct(Region $z = null)
+    {
+        $this->zone = $z;
+        $this->useHTTPS = false;
+        $this->useCdnDomains = false;
+        $this->regionCache = array();
+    }
+
+    public function getUpHost($accessKey, $bucket)
+    {
+        $region = $this->getRegion($accessKey, $bucket);
+        if ($this->useHTTPS === true) {
+            $scheme = "https://";
+        } else {
+            $scheme = "http://";
+        }
+
+        $host = $region->srcUpHosts[0];
+        if ($this->useCdnDomains === true) {
+            $host = $region->cdnUpHosts[0];
+        }
+
+        return $scheme . $host;
+    }
+
+    public function getUpBackupHost($accessKey, $bucket)
+    {
+        $region = $this->getRegion($accessKey, $bucket);
+        if ($this->useHTTPS === true) {
+            $scheme = "https://";
+        } else {
+            $scheme = "http://";
+        }
+
+        $host = $region->cdnUpHosts[0];
+        if ($this->useCdnDomains === true) {
+            $host = $region->srcUpHosts[0];
+        }
+
+        return $scheme . $host;
+    }
+
+    public function getRsHost($accessKey, $bucket)
+    {
+        $region = $this->getRegion($accessKey, $bucket);
+
+        if ($this->useHTTPS === true) {
+            $scheme = "https://";
+        } else {
+            $scheme = "http://";
+        }
+
+        return $scheme . $region->rsHost;
+    }
+
+    public function getRsfHost($accessKey, $bucket)
+    {
+        $region = $this->getRegion($accessKey, $bucket);
+
+        if ($this->useHTTPS === true) {
+            $scheme = "https://";
+        } else {
+            $scheme = "http://";
+        }
+
+        return $scheme . $region->rsfHost;
+    }
+
+    public function getIovipHost($accessKey, $bucket)
+    {
+        $region = $this->getRegion($accessKey, $bucket);
+
+        if ($this->useHTTPS === true) {
+            $scheme = "https://";
+        } else {
+            $scheme = "http://";
+        }
+
+        return $scheme . $region->iovipHost;
+    }
+
+    public function getApiHost($accessKey, $bucket)
+    {
+        $region = $this->getRegion($accessKey, $bucket);
+
+        if ($this->useHTTPS === true) {
+            $scheme = "https://";
+        } else {
+            $scheme = "http://";
+        }
+
+        return $scheme . $region->apiHost;
+    }
+
+    private function getRegion($accessKey, $bucket)
+    {
+        $cacheId = "$accessKey:$bucket";
+
+        if (isset($this->regionCache[$cacheId])) {
+            $region = $this->regionCache[$cacheId];
+        } elseif (isset($this->zone)) {
+            $region = $this->zone;
+            $this->regionCache[$cacheId] = $region;
+        } else {
+            $region = Zone::queryZone($accessKey, $bucket);
+            $this->regionCache[$cacheId] = $region;
+        }
+        return $region;
+    }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Etag.php b/vendor/qiniu/php-sdk/src/Qiniu/Etag.php
new file mode 100755
index 0000000..d7be064
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Etag.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace Qiniu;
+
+use Qiniu\Config;
+
+final class Etag
+{
+    private static function packArray($v, $a)
+    {
+        return call_user_func_array('pack', array_merge(array($v), (array)$a));
+    }
+
+    private static function blockCount($fsize)
+    {
+        return intval(($fsize + (Config::BLOCK_SIZE - 1)) / Config::BLOCK_SIZE);
+    }
+
+    private static function calcSha1($data)
+    {
+        $sha1Str = sha1($data, true);
+        $err = error_get_last();
+        if ($err !== null) {
+            return array(null, $err);
+        }
+        $byteArray = unpack('C*', $sha1Str);
+        return array($byteArray, null);
+    }
+
+
+    public static function sum($filename)
+    {
+        $fhandler = fopen($filename, 'r');
+        $err = error_get_last();
+        if ($err !== null) {
+            return array(null, $err);
+        }
+
+        $fstat = fstat($fhandler);
+        $fsize = $fstat['size'];
+        if ((int)$fsize === 0) {
+            fclose($fhandler);
+            return array('Fto5o-5ea0sNMlW_75VgGJCv2AcJ', null);
+        }
+        $blockCnt = self::blockCount($fsize);
+        $sha1Buf = array();
+
+        if ($blockCnt <= 1) {
+            array_push($sha1Buf, 0x16);
+            $fdata = fread($fhandler, Config::BLOCK_SIZE);
+            if ($err !== null) {
+                fclose($fhandler);
+                return array(null, $err);
+            }
+            list($sha1Code,) = self::calcSha1($fdata);
+            $sha1Buf = array_merge($sha1Buf, $sha1Code);
+        } else {
+            array_push($sha1Buf, 0x96);
+            $sha1BlockBuf = array();
+            for ($i = 0; $i < $blockCnt; $i++) {
+                $fdata = fread($fhandler, Config::BLOCK_SIZE);
+                list($sha1Code, $err) = self::calcSha1($fdata);
+                if ($err !== null) {
+                    fclose($fhandler);
+                    return array(null, $err);
+                }
+                $sha1BlockBuf = array_merge($sha1BlockBuf, $sha1Code);
+            }
+            $tmpData = self::packArray('C*', $sha1BlockBuf);
+            list($sha1Final,) = self::calcSha1($tmpData);
+            $sha1Buf = array_merge($sha1Buf, $sha1Final);
+        }
+        $etag = \Qiniu\base64_urlSafeEncode(self::packArray('C*', $sha1Buf));
+        return array($etag, null);
+    }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Http/Client.php b/vendor/qiniu/php-sdk/src/Qiniu/Http/Client.php
new file mode 100755
index 0000000..0fe03ce
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Http/Client.php
@@ -0,0 +1,160 @@
+<?php
+namespace Qiniu\Http;
+
+use Qiniu\Config;
+use Qiniu\Http\Request;
+use Qiniu\Http\Response;
+
+final class Client
+{
+    public static function get($url, array $headers = array())
+    {
+        $request = new Request('GET', $url, $headers);
+        return self::sendRequest($request);
+    }
+
+    public static function delete($url, array $headers = array())
+    {
+        $request = new Request('DELETE', $url, $headers);
+        return self::sendRequest($request);
+    }
+
+    public static function post($url, $body, array $headers = array())
+    {
+        $request = new Request('POST', $url, $headers, $body);
+        return self::sendRequest($request);
+    }
+
+    public static function PUT($url, $body, array $headers = array())
+    {
+        $request = new Request('PUT', $url, $headers, $body);
+        return self::sendRequest($request);
+    }
+
+    public static function multipartPost(
+        $url,
+        $fields,
+        $name,
+        $fileName,
+        $fileBody,
+        $mimeType = null,
+        array $headers = array()
+    ) {
+        $data = array();
+        $mimeBoundary = md5(microtime());
+
+        foreach ($fields as $key => $val) {
+            array_push($data, '--' . $mimeBoundary);
+            array_push($data, "Content-Disposition: form-data; name=\"$key\"");
+            array_push($data, '');
+            array_push($data, $val);
+        }
+
+        array_push($data, '--' . $mimeBoundary);
+        $finalMimeType = empty($mimeType) ? 'application/octet-stream' : $mimeType;
+        $finalFileName = self::escapeQuotes($fileName);
+        array_push($data, "Content-Disposition: form-data; name=\"$name\"; filename=\"$finalFileName\"");
+        array_push($data, "Content-Type: $finalMimeType");
+        array_push($data, '');
+        array_push($data, $fileBody);
+
+        array_push($data, '--' . $mimeBoundary . '--');
+        array_push($data, '');
+
+        $body = implode("\r\n", $data);
+        // var_dump($data);exit;
+        $contentType = 'multipart/form-data; boundary=' . $mimeBoundary;
+        $headers['Content-Type'] = $contentType;
+        $request = new Request('POST', $url, $headers, $body);
+        return self::sendRequest($request);
+    }
+
+    private static function userAgent()
+    {
+        $sdkInfo = "QiniuPHP/" . Config::SDK_VER;
+
+        $systemInfo = php_uname("s");
+        $machineInfo = php_uname("m");
+
+        $envInfo = "($systemInfo/$machineInfo)";
+
+        $phpVer = phpversion();
+
+        $ua = "$sdkInfo $envInfo PHP/$phpVer";
+        return $ua;
+    }
+
+    public static function sendRequest($request)
+    {
+        $t1 = microtime(true);
+        $ch = curl_init();
+        $options = array(
+            CURLOPT_USERAGENT => self::userAgent(),
+            CURLOPT_RETURNTRANSFER => true,
+            CURLOPT_SSL_VERIFYPEER => false,
+            CURLOPT_SSL_VERIFYHOST => false,
+            CURLOPT_HEADER => true,
+            CURLOPT_NOBODY => false,
+            CURLOPT_CUSTOMREQUEST => $request->method,
+            CURLOPT_URL => $request->url,
+        );
+        // Handle open_basedir & safe mode
+        if (!ini_get('safe_mode') && !ini_get('open_basedir')) {
+            $options[CURLOPT_FOLLOWLOCATION] = true;
+        }
+        if (!empty($request->headers)) {
+            $headers = array();
+            foreach ($request->headers as $key => $val) {
+                array_push($headers, "$key: $val");
+            }
+            $options[CURLOPT_HTTPHEADER] = $headers;
+        }
+        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
+        if (!empty($request->body)) {
+            $options[CURLOPT_POSTFIELDS] = $request->body;
+        }
+        curl_setopt_array($ch, $options);
+        $result = curl_exec($ch);
+        $t2 = microtime(true);
+        $duration = round($t2 - $t1, 3);
+        $ret = curl_errno($ch);
+        if ($ret !== 0) {
+            $r = new Response(-1, $duration, array(), null, curl_error($ch));
+            curl_close($ch);
+            return $r;
+        }
+        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+        $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
+        $headers = self::parseHeaders(substr($result, 0, $header_size));
+        $body = substr($result, $header_size);
+        curl_close($ch);
+        return new Response($code, $duration, $headers, $body, null);
+    }
+
+    private static function parseHeaders($raw)
+    {
+        $headers = array();
+        $headerLines = explode("\r\n", $raw);
+        foreach ($headerLines as $line) {
+            $headerLine = trim($line);
+            $kv = explode(':', $headerLine);
+            if (count($kv) > 1) {
+                $kv[0] =self::ucwordsHyphen($kv[0]);
+                $headers[$kv[0]] = trim($kv[1]);
+            }
+        }
+        return $headers;
+    }
+
+    private static function escapeQuotes($str)
+    {
+        $find = array("\\", "\"");
+        $replace = array("\\\\", "\\\"");
+        return str_replace($find, $replace, $str);
+    }
+    
+    private static function ucwordsHyphen($str)
+    {
+        return str_replace('- ', '-', ucwords(str_replace('-', '- ', $str)));
+    }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Http/Error.php b/vendor/qiniu/php-sdk/src/Qiniu/Http/Error.php
new file mode 100755
index 0000000..73477cf
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Http/Error.php
@@ -0,0 +1,35 @@
+<?php
+namespace Qiniu\Http;
+
+/**
+ * 七牛业务请求逻辑错误封装类,主要用来解析API请求返回如下的内容:
+ * <pre>
+ *     {"error" : "detailed error message"}
+ * </pre>
+ */
+final class Error
+{
+    private $url;
+    private $response;
+
+    public function __construct($url, $response)
+    {
+        $this->url = $url;
+        $this->response = $response;
+    }
+
+    public function code()
+    {
+        return $this->response->statusCode;
+    }
+
+    public function getResponse()
+    {
+        return $this->response;
+    }
+
+    public function message()
+    {
+        return $this->response->error;
+    }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Http/Request.php b/vendor/qiniu/php-sdk/src/Qiniu/Http/Request.php
new file mode 100755
index 0000000..43b0bfd
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Http/Request.php
@@ -0,0 +1,18 @@
+<?php
+namespace Qiniu\Http;
+
+final class Request
+{
+    public $url;
+    public $headers;
+    public $body;
+    public $method;
+
+    public function __construct($method, $url, array $headers = array(), $body = null)
+    {
+        $this->method = strtoupper($method);
+        $this->url = $url;
+        $this->headers = $headers;
+        $this->body = $body;
+    }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Http/Response.php b/vendor/qiniu/php-sdk/src/Qiniu/Http/Response.php
new file mode 100755
index 0000000..f22ab37
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Http/Response.php
@@ -0,0 +1,176 @@
+<?php
+
+namespace Qiniu\Http;
+
+/**
+ * HTTP response Object
+ */
+final class Response
+{
+    public $statusCode;
+    public $headers;
+    public $body;
+    public $error;
+    private $jsonData;
+    public $duration;
+
+    /** @var array Mapping of status codes to reason phrases */
+    private static $statusTexts = array(
+        100 => 'Continue',
+        101 => 'Switching Protocols',
+        102 => 'Processing',
+        200 => 'OK',
+        201 => 'Created',
+        202 => 'Accepted',
+        203 => 'Non-Authoritative Information',
+        204 => 'No Content',
+        205 => 'Reset Content',
+        206 => 'Partial Content',
+        207 => 'Multi-Status',
+        208 => 'Already Reported',
+        226 => 'IM Used',
+        300 => 'Multiple Choices',
+        301 => 'Moved Permanently',
+        302 => 'Found',
+        303 => 'See Other',
+        304 => 'Not Modified',
+        305 => 'Use Proxy',
+        307 => 'Temporary Redirect',
+        308 => 'Permanent Redirect',
+        400 => 'Bad Request',
+        401 => 'Unauthorized',
+        402 => 'Payment Required',
+        403 => 'Forbidden',
+        404 => 'Not Found',
+        405 => 'Method Not Allowed',
+        406 => 'Not Acceptable',
+        407 => 'Proxy Authentication Required',
+        408 => 'Request Timeout',
+        409 => 'Conflict',
+        410 => 'Gone',
+        411 => 'Length Required',
+        412 => 'Precondition Failed',
+        413 => 'Request Entity Too Large',
+        414 => 'Request-URI Too Long',
+        415 => 'Unsupported Media Type',
+        416 => 'Requested Range Not Satisfiable',
+        417 => 'Expectation Failed',
+        422 => 'Unprocessable Entity',
+        423 => 'Locked',
+        424 => 'Failed Dependency',
+        425 => 'Reserved for WebDAV advanced collections expired proposal',
+        426 => 'Upgrade required',
+        428 => 'Precondition Required',
+        429 => 'Too Many Requests',
+        431 => 'Request Header Fields Too Large',
+        500 => 'Internal Server Error',
+        501 => 'Not Implemented',
+        502 => 'Bad Gateway',
+        503 => 'Service Unavailable',
+        504 => 'Gateway Timeout',
+        505 => 'HTTP Version Not Supported',
+        506 => 'Variant Also Negotiates (Experimental)',
+        507 => 'Insufficient Storage',
+        508 => 'Loop Detected',
+        510 => 'Not Extended',
+        511 => 'Network Authentication Required',
+    );
+
+    /**
+     * @param int $code 状态码
+     * @param double $duration 请求时长
+     * @param array $headers 响应头部
+     * @param string $body 响应内容
+     * @param string $error 错误描述
+     */
+    public function __construct($code, $duration, array $headers = array(), $body = null, $error = null)
+    {
+        $this->statusCode = $code;
+        $this->duration = $duration;
+        $this->headers = $headers;
+        $this->body = $body;
+        $this->error = $error;
+        $this->jsonData = null;
+        if ($error !== null) {
+            return;
+        }
+
+        if ($body === null) {
+            if ($code >= 400) {
+                $this->error = self::$statusTexts[$code];
+            }
+            return;
+        }
+        if (self::isJson($headers)) {
+            try {
+                $jsonData = self::bodyJson($body);
+                if ($code >= 400) {
+                    $this->error = $body;
+                    if ($jsonData['error'] !== null) {
+                        $this->error = $jsonData['error'];
+                    }
+                }
+                $this->jsonData = $jsonData;
+            } catch (\InvalidArgumentException $e) {
+                $this->error = $body;
+                if ($code >= 200 && $code < 300) {
+                    $this->error = $e->getMessage();
+                }
+            }
+        } elseif ($code >= 400) {
+            $this->error = $body;
+        }
+        return;
+    }
+
+    public function json()
+    {
+        return $this->jsonData;
+    }
+
+    private static function bodyJson($body)
+    {
+        return \Qiniu\json_decode((string) $body, true, 512);
+    }
+
+    public function xVia()
+    {
+        $via = $this->headers['X-Via'];
+        if ($via === null) {
+            $via = $this->headers['X-Px'];
+        }
+        if ($via === null) {
+            $via = $this->headers['Fw-Via'];
+        }
+        return $via;
+    }
+
+    public function xLog()
+    {
+        return $this->headers['X-Log'];
+    }
+
+    public function xReqId()
+    {
+        return $this->headers['X-Reqid'];
+    }
+
+    public function ok()
+    {
+        return $this->statusCode >= 200 && $this->statusCode < 300 && $this->error === null;
+    }
+
+    public function needRetry()
+    {
+        $code = $this->statusCode;
+        if ($code < 0 || ($code / 100 === 5 and $code !== 579) || $code === 996) {
+            return true;
+        }
+    }
+
+    private static function isJson($headers)
+    {
+        return array_key_exists('Content-Type', $headers) &&
+        strpos($headers['Content-Type'], 'application/json') === 0;
+    }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Processing/ImageUrlBuilder.php b/vendor/qiniu/php-sdk/src/Qiniu/Processing/ImageUrlBuilder.php
new file mode 100755
index 0000000..1ac5bf7
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Processing/ImageUrlBuilder.php
@@ -0,0 +1,282 @@
+<?php
+namespace Qiniu\Processing;
+
+use Qiniu;
+
+/**
+ * 主要涉及图片链接拼接
+ *
+ * @link http://developer.qiniu.com/code/v6/api/kodo-api/image/imageview2.html
+ */
+final class ImageUrlBuilder
+{
+    /**
+     * mode合法范围值
+     *
+     * @var array
+     */
+    protected $modeArr = array(0, 1, 2, 3, 4, 5);
+
+    /**
+     * format合法值
+     *
+     * @var array
+     */
+    protected $formatArr = array('psd', 'jpeg', 'png', 'gif', 'webp', 'tiff', 'bmp');
+
+    /**
+     * 水印图片位置合法值
+     *
+     * @var array
+     */
+    protected $gravityArr = array('NorthWest', 'North', 'NorthEast',
+        'West', 'Center', 'East', 'SouthWest', 'South', 'SouthEast');
+
+    /**
+     * 缩略图链接拼接
+     *
+     * @param  string $url 图片链接
+     * @param  int $mode 缩略模式
+     * @param  int $width 宽度
+     * @param  int $height 长度
+     * @param  string $format 输出类型
+     * @param  int $quality 图片质量
+     * @param  int $interlace 是否支持渐进显示
+     * @param  int $ignoreError 忽略结果
+     * @return string
+     * @link http://developer.qiniu.com/code/v6/api/kodo-api/image/imageview2.html
+     * @author Sherlock Ren <sherlock_ren@icloud.com>
+     */
+    public function thumbnail(
+        $url,
+        $mode,
+        $width,
+        $height,
+        $format = null,
+        $interlace = null,
+        $quality = null,
+        $ignoreError = 1
+    ) {
+
+        // url合法效验
+        if (!$this->isUrl($url)) {
+            return $url;
+        }
+
+        // 参数合法性效验
+        if (!in_array(intval($mode), $this->modeArr, true)) {
+            return $url;
+        }
+
+        if (!$width || !$height) {
+            return $url;
+        }
+
+        $thumbStr = 'imageView2/' . $mode . '/w/' . $width . '/h/' . $height . '/';
+
+        // 拼接输出格式
+        if (!is_null($format)
+            && in_array($format, $this->formatArr)
+        ) {
+            $thumbStr .= 'format/' . $format . '/';
+        }
+
+        // 拼接渐进显示
+        if (!is_null($interlace)
+            && in_array(intval($interlace), array(0, 1), true)
+        ) {
+            $thumbStr .= 'interlace/' . $interlace . '/';
+        }
+
+        // 拼接图片质量
+        if (!is_null($quality)
+            && intval($quality) >= 0
+            && intval($quality) <= 100
+        ) {
+            $thumbStr .= 'q/' . $quality . '/';
+        }
+
+        $thumbStr .= 'ignore-error/' . $ignoreError . '/';
+
+        // 如果有query_string用|线分割实现多参数
+        return $url . ($this->hasQuery($url) ? '|' : '?') . $thumbStr;
+    }
+
+    /**
+     * 图片水印
+     *
+     * @param  string $url 图片链接
+     * @param  string $image 水印图片链接
+     * @param  numeric $dissolve 透明度
+     * @param  string $gravity 水印位置
+     * @param  numeric $dx 横轴边距
+     * @param  numeric $dy 纵轴边距
+     * @param  numeric $watermarkScale 自适应原图的短边比例
+     * @link   http://developer.qiniu.com/code/v6/api/kodo-api/image/watermark.html
+     * @return string
+     * @author Sherlock Ren <sherlock_ren@icloud.com>
+     */
+    public function waterImg(
+        $url,
+        $image,
+        $dissolve = 100,
+        $gravity = 'SouthEast',
+        $dx = null,
+        $dy = null,
+        $watermarkScale = null
+    ) {
+        // url合法效验
+        if (!$this->isUrl($url)) {
+            return $url;
+        }
+
+        $waterStr = 'watermark/1/image/' . \Qiniu\base64_urlSafeEncode($image) . '/';
+
+        // 拼接水印透明度
+        if (is_numeric($dissolve)
+            && $dissolve <= 100
+        ) {
+            $waterStr .= 'dissolve/' . $dissolve . '/';
+        }
+
+        // 拼接水印位置
+        if (in_array($gravity, $this->gravityArr, true)) {
+            $waterStr .= 'gravity/' . $gravity . '/';
+        }
+
+        // 拼接横轴边距
+        if (!is_null($dx)
+            && is_numeric($dx)
+        ) {
+            $waterStr .= 'dx/' . $dx . '/';
+        }
+
+        // 拼接纵轴边距
+        if (!is_null($dy)
+            && is_numeric($dy)
+        ) {
+            $waterStr .= 'dy/' . $dy . '/';
+        }
+
+        // 拼接自适应原图的短边比例
+        if (!is_null($watermarkScale)
+            && is_numeric($watermarkScale)
+            && $watermarkScale > 0
+            && $watermarkScale < 1
+        ) {
+            $waterStr .= 'ws/' . $watermarkScale . '/';
+        }
+
+        // 如果有query_string用|线分割实现多参数
+        return $url . ($this->hasQuery($url) ? '|' : '?') . $waterStr;
+    }
+
+    /**
+     * 文字水印
+     *
+     * @param  string $url 图片链接
+     * @param  string $text 文字
+     * @param  string $font 文字字体
+     * @param  string $fontSize 文字字号
+     * @param  string $fontColor 文字颜色
+     * @param  numeric $dissolve 透明度
+     * @param  string $gravity 水印位置
+     * @param  numeric $dx 横轴边距
+     * @param  numeric $dy 纵轴边距
+     * @link   http://developer.qiniu.com/code/v6/api/kodo-api/image/watermark.html#text-watermark
+     * @return string
+     * @author Sherlock Ren <sherlock_ren@icloud.com>
+     */
+    public function waterText(
+        $url,
+        $text,
+        $font = '黑体',
+        $fontSize = 0,
+        $fontColor = null,
+        $dissolve = 100,
+        $gravity = 'SouthEast',
+        $dx = null,
+        $dy = null
+    ) {
+        // url合法效验
+        if (!$this->isUrl($url)) {
+            return $url;
+        }
+
+        $waterStr = 'watermark/2/text/'
+            . \Qiniu\base64_urlSafeEncode($text) . '/font/'
+            . \Qiniu\base64_urlSafeEncode($font) . '/';
+
+        // 拼接文字大小
+        if (is_int($fontSize)) {
+            $waterStr .= 'fontsize/' . $fontSize . '/';
+        }
+
+        // 拼接文字颜色
+        if (!is_null($fontColor)
+            && $fontColor
+        ) {
+            $waterStr .= 'fill/' . \Qiniu\base64_urlSafeEncode($fontColor) . '/';
+        }
+
+        // 拼接水印透明度
+        if (is_numeric($dissolve)
+            && $dissolve <= 100
+        ) {
+            $waterStr .= 'dissolve/' . $dissolve . '/';
+        }
+
+        // 拼接水印位置
+        if (in_array($gravity, $this->gravityArr, true)) {
+            $waterStr .= 'gravity/' . $gravity . '/';
+        }
+
+        // 拼接横轴边距
+        if (!is_null($dx)
+            && is_numeric($dx)
+        ) {
+            $waterStr .= 'dx/' . $dx . '/';
+        }
+
+        // 拼接纵轴边距
+        if (!is_null($dy)
+            && is_numeric($dy)
+        ) {
+            $waterStr .= 'dy/' . $dy . '/';
+        }
+
+        // 如果有query_string用|线分割实现多参数
+        return $url . ($this->hasQuery($url) ? '|' : '?') . $waterStr;
+    }
+
+    /**
+     * 效验url合法性
+     *
+     * @param  string $url url链接
+     * @return string
+     * @author Sherlock Ren <sherlock_ren@icloud.com>
+     */
+    protected function isUrl($url)
+    {
+        $urlArr = parse_url($url);
+
+        return $urlArr['scheme']
+        && in_array($urlArr['scheme'], array('http', 'https'))
+        && $urlArr['host']
+        && $urlArr['path'];
+    }
+
+    /**
+     * 检测是否有query
+     *
+     * @param  string $url url链接
+     * @return string
+     * @author Sherlock Ren <sherlock_ren@icloud.com>
+     */
+    protected function hasQuery($url)
+    {
+        $urlArr = parse_url($url);
+
+        return !empty($urlArr['query']);
+    }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Processing/Operation.php b/vendor/qiniu/php-sdk/src/Qiniu/Processing/Operation.php
new file mode 100755
index 0000000..919136f
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Processing/Operation.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace Qiniu\Processing;
+
+use Qiniu\Http\Client;
+use Qiniu\Http\Error;
+
+final class Operation
+{
+
+    private $auth;
+    private $token_expire;
+    private $domain;
+
+    public function __construct($domain, $auth = null, $token_expire = 3600)
+    {
+        $this->auth = $auth;
+        $this->domain = $domain;
+        $this->token_expire = $token_expire;
+    }
+
+
+    /**
+     * 对资源文件进行处理
+     *
+     * @param $key   待处理的资源文件名
+     * @param $fops   string|array  fop操作,多次fop操作以array的形式传入。
+     *                eg. imageView2/1/w/200/h/200, imageMogr2/thumbnail/!75px
+     *
+     * @return array 文件处理后的结果及错误。
+     *
+     * @link http://developer.qiniu.com/docs/v6/api/reference/fop/
+     */
+    public function execute($key, $fops)
+    {
+        $url = $this->buildUrl($key, $fops);
+        $resp = Client::get($url);
+        if (!$resp->ok()) {
+            return array(null, new Error($url, $resp));
+        }
+        if ($resp->json() !== null) {
+            return array($resp->json(), null);
+        }
+        return array($resp->body, null);
+    }
+
+    public function buildUrl($key, $fops, $protocol = 'http')
+    {
+        if (is_array($fops)) {
+            $fops = implode('|', $fops);
+        }
+
+        $url = $protocol . "://$this->domain/$key?$fops";
+        if ($this->auth !== null) {
+            $url = $this->auth->privateDownloadUrl($url, $this->token_expire);
+        }
+
+        return $url;
+    }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Processing/PersistentFop.php b/vendor/qiniu/php-sdk/src/Qiniu/Processing/PersistentFop.php
new file mode 100755
index 0000000..24e7b73
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Processing/PersistentFop.php
@@ -0,0 +1,94 @@
+<?php
+namespace Qiniu\Processing;
+
+use Qiniu\Config;
+use Qiniu\Http\Client;
+use Qiniu\Http\Error;
+use Qiniu\Processing\Operation;
+
+/**
+ * 持久化处理类,该类用于主动触发异步持久化操作.
+ *
+ * @link http://developer.qiniu.com/docs/v6/api/reference/fop/pfop/pfop.html
+ */
+final class PersistentFop
+{
+    /**
+     * @var 账号管理密钥对,Auth对象
+     */
+    private $auth;
+
+    /*
+     * @var 配置对象,Config 对象
+     * */
+    private $config;
+
+
+    public function __construct($auth, $config = null)
+    {
+        $this->auth = $auth;
+        if ($config == null) {
+            $this->config = new Config();
+        } else {
+            $this->config = $config;
+        }
+    }
+
+    /**
+     * 对资源文件进行异步持久化处理
+     * @param $bucket     资源所在空间
+     * @param $key        待处理的源文件
+     * @param $fops       string|array  待处理的pfop操作,多个pfop操作以array的形式传入。
+     *                    eg. avthumb/mp3/ab/192k, vframe/jpg/offset/7/w/480/h/360
+     * @param $pipeline   资源处理队列
+     * @param $notify_url 处理结果通知地址
+     * @param $force      是否强制执行一次新的指令
+     *
+     *
+     * @return array 返回持久化处理的persistentId, 和返回的错误。
+     *
+     * @link http://developer.qiniu.com/docs/v6/api/reference/fop/
+     */
+    public function execute($bucket, $key, $fops, $pipeline = null, $notify_url = null, $force = false)
+    {
+        if (is_array($fops)) {
+            $fops = implode(';', $fops);
+        }
+        $params = array('bucket' => $bucket, 'key' => $key, 'fops' => $fops);
+        \Qiniu\setWithoutEmpty($params, 'pipeline', $pipeline);
+        \Qiniu\setWithoutEmpty($params, 'notifyURL', $notify_url);
+        if ($force) {
+            $params['force'] = 1;
+        }
+        $data = http_build_query($params);
+        $scheme = "http://";
+        if ($this->config->useHTTPS === true) {
+            $scheme = "https://";
+        }
+        $url = $scheme . Config::API_HOST . '/pfop/';
+        $headers = $this->auth->authorization($url, $data, 'application/x-www-form-urlencoded');
+        $headers['Content-Type'] = 'application/x-www-form-urlencoded';
+        $response = Client::post($url, $data, $headers);
+        if (!$response->ok()) {
+            return array(null, new Error($url, $response));
+        }
+        $r = $response->json();
+        $id = $r['persistentId'];
+        return array($id, null);
+    }
+
+    public function status($id)
+    {
+        $scheme = "http://";
+
+        if ($this->config->useHTTPS === true) {
+            $scheme = "https://";
+        }
+        $url = $scheme . Config::API_HOST . "/status/get/prefop?id=$id";
+        $response = Client::get($url);
+        if (!$response->ok()) {
+            return array(null, new Error($url, $response));
+        }
+        return array($response->json(), null);
+    }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Region.php b/vendor/qiniu/php-sdk/src/Qiniu/Region.php
new file mode 100755
index 0000000..eae21d1
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Region.php
@@ -0,0 +1,196 @@
+<?php
+namespace Qiniu;
+
+use Qiniu\Http\Client;
+use Qiniu\Http\Error;
+
+class Region
+{
+
+    //源站上传域名
+    public $srcUpHosts;
+    //CDN加速上传域名
+    public $cdnUpHosts;
+    //资源管理域名
+    public $rsHost;
+    //资源列举域名
+    public $rsfHost;
+    //资源处理域名
+    public $apiHost;
+    //IOVIP域名
+    public $iovipHost;
+
+    //构造一个Region对象
+    public function __construct(
+        $srcUpHosts = array(),
+        $cdnUpHosts = array(),
+        $rsHost = "rs.qiniu.com",
+        $rsfHost = "rsf.qiniu.com",
+        $apiHost = "api.qiniu.com",
+        $iovipHost = null
+    ) {
+
+        $this->srcUpHosts = $srcUpHosts;
+        $this->cdnUpHosts = $cdnUpHosts;
+        $this->rsHost = $rsHost;
+        $this->rsfHost = $rsfHost;
+        $this->apiHost = $apiHost;
+        $this->iovipHost = $iovipHost;
+    }
+
+    //华东机房
+    public static function regionHuadong()
+    {
+        $regionHuadong = new Region(
+            array("up.qiniup.com", 'up-jjh.qiniup.com', 'up-xs.qiniup.com'),
+            array('upload.qiniup.com', 'upload-jjh.qiniup.com', 'upload-xs.qiniup.com'),
+            'rs.qbox.me',
+            'rsf.qbox.me',
+            'api.qiniu.com',
+            'iovip.qbox.me'
+        );
+        return $regionHuadong;
+    }
+
+    //华东机房内网上传
+    public static function qvmRegionHuadong()
+    {
+        $qvmRegionHuadong = new Region(
+            array("free-qvm-z0-xs.qiniup.com"),
+            'rs.qbox.me',
+            'rsf.qbox.me',
+            'api.qiniu.com',
+            'iovip.qbox.me'
+        );
+        return $qvmRegionHuadong;
+    }
+
+    //华北机房内网上传
+    public static function qvmRegionHuabei()
+    {
+        $qvmRegionHuabei = new Region(
+            array("free-qvm-z1-zz.qiniup.com"),
+            "rs-z1.qbox.me",
+            "rsf-z1.qbox.me",
+            "api-z1.qiniu.com",
+            "iovip-z1.qbox.me"
+        );
+        return $qvmRegionHuabei;
+    }
+
+    //华北机房
+    public static function regionHuabei()
+    {
+        $regionHuabei = new Region(
+            array('up-z1.qiniup.com'),
+            array('upload-z1.qiniup.com'),
+            "rs-z1.qbox.me",
+            "rsf-z1.qbox.me",
+            "api-z1.qiniu.com",
+            "iovip-z1.qbox.me"
+        );
+
+        return $regionHuabei;
+    }
+
+    //华南机房
+    public static function regionHuanan()
+    {
+        $regionHuanan = new Region(
+            array('up-z2.qiniup.com', 'up-dg.qiniup.com', 'up-fs.qiniup.com'),
+            array('upload-z2.qiniup.com', 'upload-dg.qiniup.com', 'upload-fs.qiniup.com'),
+            "rs-z2.qbox.me",
+            "rsf-z2.qbox.me",
+            "api-z2.qiniu.com",
+            "iovip-z2.qbox.me"
+        );
+        return $regionHuanan;
+    }
+
+    //北美机房
+    public static function regionNorthAmerica()
+    {
+        //北美机房
+        $regionNorthAmerica = new Region(
+            array('up-na0.qiniup.com'),
+            array('upload-na0.qiniup.com'),
+            "rs-na0.qbox.me",
+            "rsf-na0.qbox.me",
+            "api-na0.qiniu.com",
+            "iovip-na0.qbox.me"
+        );
+        return $regionNorthAmerica;
+    }
+
+    //新加坡机房
+    public static function regionSingapore()
+    {
+        //新加坡机房
+        $regionSingapore = new Region(
+            array('up-as0.qiniup.com'),
+            array('upload-as0.qiniup.com'),
+            "rs-as0.qbox.me",
+            "rsf-as0.qbox.me",
+            "api-as0.qiniu.com",
+            "iovip-as0.qbox.me"
+        );
+        return $regionSingapore;
+    }
+
+    /*
+     * GET /v2/query?ak=<ak>&&bucket=<bucket>
+     **/
+    public static function queryRegion($ak, $bucket)
+    {
+        $Region = new Region();
+        $url = Config::API_HOST . '/v2/query' . "?ak=$ak&bucket=$bucket";
+        $ret = Client::Get($url);
+        if (!$ret->ok()) {
+            return array(null, new Error($url, $ret));
+        }
+        $r = ($ret->body === null) ? array() : $ret->json();
+        //parse Region;
+
+        $iovipHost = $r['io']['src']['main'][0];
+        $Region->iovipHost = $iovipHost;
+        $accMain = $r['up']['acc']['main'][0];
+        array_push($Region->cdnUpHosts, $accMain);
+        if (isset($r['up']['acc']['backup'])) {
+            foreach ($r['up']['acc']['backup'] as $key => $value) {
+                array_push($Region->cdnUpHosts, $value);
+            }
+        }
+        $srcMain = $r['up']['src']['main'][0];
+        array_push($Region->srcUpHosts, $srcMain);
+        if (isset($r['up']['src']['backup'])) {
+            foreach ($r['up']['src']['backup'] as $key => $value) {
+                array_push($Region->srcUpHosts, $value);
+            }
+        }
+
+        //set specific hosts
+        if (strstr($Region->iovipHost, "z1") !== false) {
+            $Region->rsHost = "rs-z1.qbox.me";
+            $Region->rsfHost = "rsf-z1.qbox.me";
+            $Region->apiHost = "api-z1.qiniu.com";
+        } elseif (strstr($Region->iovipHost, "z2") !== false) {
+            $Region->rsHost = "rs-z2.qbox.me";
+            $Region->rsfHost = "rsf-z2.qbox.me";
+            $Region->apiHost = "api-z2.qiniu.com";
+        } elseif (strstr($Region->iovipHost, "na0") !== false) {
+            $Region->rsHost = "rs-na0.qbox.me";
+            $Region->rsfHost = "rsf-na0.qbox.me";
+            $Region->apiHost = "api-na0.qiniu.com";
+        } elseif (strstr($Region->iovipHost, "as0") !== false) {
+            $Region->rsHost = "rs-as0.qbox.me";
+            $Region->rsfHost = "rsf-as0.qbox.me";
+            $Region->apiHost = "api-as0.qiniu.com";
+        } else {
+            $Region->rsHost = "rs.qbox.me";
+            $Region->rsfHost = "rsf.qbox.me";
+            $Region->apiHost = "api.qiniu.com";
+        }
+
+        return $Region;
+    }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Rtc/AppClient.php b/vendor/qiniu/php-sdk/src/Qiniu/Rtc/AppClient.php
new file mode 100755
index 0000000..0fd0019
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Rtc/AppClient.php
@@ -0,0 +1,209 @@
+<?php
+namespace Qiniu\Rtc;
+
+use Qiniu\Http\Client;
+use Qiniu\Http\Error;
+use Qiniu\Config;
+use Qiniu\Auth;
+
+class AppClient
+{
+    private $auth;
+    private $baseURL;
+
+    public function __construct(Auth $auth)
+    {
+        $this->auth = $auth;
+
+        $this->baseURL = sprintf("%s/%s/apps", Config::RTCAPI_HOST, Config::RTCAPI_VERSION);
+    }
+
+    /*
+     * 创建应用
+     * hub: 直播空间名
+     * title: app 的名称  注意,Title 不是唯一标识,重复 create 动作将生成多个 app
+     * maxUsers:人数限制
+     * NoAutoKickUser: bool 类型,可选,禁止自动踢人(抢流)。默认为 false ,
+       即同一个身份的 client (app/room/user) ,新的连麦请求可以成功,旧连接被关闭。
+     */
+    public function createApp($hub, $title, $maxUsers = null, $noAutoKickUser = null)
+    {
+        $params['hub'] = $hub;
+        $params['title'] = $title;
+        if (!empty($maxUsers)) {
+            $params['maxUsers'] = $maxUsers;
+        }
+        if (!empty($noAutoKickUser)) {
+            $params['noAutoKickUser'] = $noAutoKickUser;
+        }
+        $body = json_encode($params);
+        $ret = $this->post($this->baseURL, $body);
+        return $ret;
+    }
+
+    /*
+     * 更新应用
+     * appId: app 的唯一标识,创建的时候由系统生成。
+     * Title: app 的名称, 可选。
+     * Hub: 绑定的直播 hub,可选,用于合流后 rtmp 推流。
+     * MaxUsers: int 类型,可选,连麦房间支持的最大在线人数。
+     * NoAutoKickUser: bool 类型,可选,禁止自动踢人。
+     * MergePublishRtmp: 连麦合流转推 RTMP 的配置,可选择。其详细配置包括如下
+            Enable: 布尔类型,用于开启和关闭所有房间的合流功能。
+            AudioOnly: 布尔类型,可选,指定是否只合成音频。
+            Height, Width: int64,可选,指定合流输出的高和宽,默认为 640 x 480。
+            OutputFps: int64,可选,指定合流输出的帧率,默认为 25 fps 。
+            OutputKbps: int64,可选,指定合流输出的码率,默认为 1000 。
+            URL: 合流后转推旁路直播的地址,可选,支持魔法变量配置按照连麦房间号生成不同的推流地址。如果是转推到七牛直播云,不建议使用该配置。
+            StreamTitle: 转推七牛直播云的流名,可选,支持魔法变量配置按照连麦房间号生成不同的流名。例如,配置 Hub 为 qn-zhibo ,配置 StreamTitle 为 $(roomName) ,则房间 meeting-001 的合流将会被转推到 rtmp://pili-publish.qn-zhibo.***.com/qn-zhibo/meeting-001地址。详细配置细则,请咨询七牛技术支持。
+     */
+    public function updateApp($appId, $hub, $title, $maxUsers = null, $mergePublishRtmp = null, $noAutoKickUser = null)
+    {
+        $url = $this->baseURL . '/' . $appId;
+        $params['hub'] = $hub;
+        $params['title'] = $title;
+        if (!empty($maxUsers)) {
+            $params['maxUsers'] = $maxUsers;
+        }
+        if (!empty($noAutoKickUser)) {
+            $params['noAutoKickUser'] = $noAutoKickUser;
+        }
+        if (!empty($mergePublishRtmp)) {
+            $params['mergePublishRtmp'] = $mergePublishRtmp;
+        }
+        $body = json_encode($params);
+        $ret = $this->post($url, $body);
+        return $ret;
+    }
+
+    /*
+     * 获取应用信息
+     * appId: app 的唯一标识,创建的时候由系统生成。
+     */
+    public function getApp($appId)
+    {
+        $url = $this->baseURL . '/' . $appId;
+        $ret  = $this->get($url);
+        return $ret;
+    }
+
+    /*
+     * 删除应用
+     * appId: app 的唯一标识,创建的时候由系统生成
+     */
+    public function deleteApp($appId)
+    {
+        $url = $this->baseURL . '/' . $appId;
+        list(, $err)  = $this->delete($url);
+        return $err;
+    }
+
+    /*
+     * 获取房间内用户列表
+     * appId: app 的唯一标识,创建的时候由系统生成。
+     * roomName: 操作所查询的连麦房间。
+     */
+    public function listUser($appId, $roomName)
+    {
+        $url = sprintf("%s/%s/rooms/%s/users", $this->baseURL, $appId, $roomName);
+        $ret  = $this->get($url);
+        return $ret;
+    }
+
+   /*
+    * 踢出用户
+    * appId: app 的唯一标识,创建的时候由系统生成。
+    * roomName: 连麦房间
+    * userId: 请求加入房间的用户ID
+    */
+    public function kickUser($appId, $roomName, $userId)
+    {
+        $url = sprintf("%s/%s/rooms/%s/users/%s", $this->baseURL, $appId, $roomName, $userId);
+        list(, $err)  = $this->delete($url);
+        return $err;
+    }
+
+    /*
+     * 获取应用中活跃房间
+     * appId: app 的唯一标识,创建的时候由系统生成。
+     * prefix: 所查询房间名的前缀索引,可以为空。
+     * offset: int 类型,分页查询的位移标记。
+     * limit: int 类型,此次查询的最大长度。
+     * GET /v3/apps/<AppID>/rooms?prefix=<RoomNamePrefix>&offset=<Offset>&limit=<Limit>
+     */
+    public function listActiveRooms($appId, $prefix = null, $offset = null, $limit = null)
+    {
+        if (isset($prefix)) {
+            $query['prefix'] = $prefix;
+        }
+        if (isset($offset)) {
+            $query['offset'] = $offset;
+        }
+        if (isset($limit)) {
+            $query['limit'] = $limit;
+        }
+        if (isset($query) && !empty($query)) {
+            $query = '?' . http_build_query($query);
+            $url = sprintf("%s/%s/rooms%s", $this->baseURL, $appId, $query);
+        } else {
+            $url = sprintf("%s/%s/rooms", $this->baseURL, $appId);
+        }
+        $ret  = $this->get($url);
+        return $ret;
+    }
+
+    /*
+     * 生成加入房间的令牌
+     * appId: app 的唯一标识,创建的时候由系统生成。
+     * roomName: 房间名称,需满足规格 ^[a-zA-Z0-9_-]{3,64}$
+     * userId: 请求加入房间的用户 ID,需满足规格 ^[a-zA-Z0-9_-]{3,50}$
+     * expireAt: int64 类型,鉴权的有效时间,传入以秒为单位的64位Unix
+       绝对时间,token 将在该时间后失效。
+     * permission: 该用户的房间管理权限,"admin" 或 "user",默认为 "user" 。
+       当权限角色为 "admin" 时,拥有将其他用户移除出房间等特权.
+     */
+    public function appToken($appId, $roomName, $userId, $expireAt, $permission)
+    {
+        $params['appId'] = $appId;
+        $params['userId'] = $userId;
+        $params['roomName'] = $roomName;
+        $params['permission'] = $permission;
+        $params['expireAt'] = $expireAt;
+        $appAccessString = json_encode($params);
+        return $this->auth->signWithData($appAccessString);
+    }
+
+    private function get($url, $cType = null)
+    {
+        $rtcToken = $this->auth->authorizationV2($url, "GET", null, $cType);
+        $rtcToken['Content-Type'] = $cType;
+        $ret = Client::get($url, $rtcToken);
+        if (!$ret->ok()) {
+            return array(null, new Error($url, $ret));
+        }
+        return array($ret->json(), null);
+    }
+
+    private function delete($url, $contentType = 'application/json')
+    {
+        $rtcToken = $this->auth->authorizationV2($url, "DELETE", null, $contentType);
+        $rtcToken['Content-Type'] = $contentType;
+        $ret = Client::delete($url, $rtcToken);
+        if (!$ret->ok()) {
+            return array(null, new Error($url, $ret));
+        }
+        return array($ret->json(), null);
+    }
+
+    private function post($url, $body, $contentType = 'application/json')
+    {
+        $rtcToken = $this->auth->authorizationV2($url, "POST", $body, $contentType);
+        $rtcToken['Content-Type'] = $contentType;
+        $ret = Client::post($url, $body, $rtcToken);
+        if (!$ret->ok()) {
+            return array(null, new Error($url, $ret));
+        }
+        $r = ($ret->body === null) ? array() : $ret->json();
+        return array($r, null);
+    }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Sms/Sms.php b/vendor/qiniu/php-sdk/src/Qiniu/Sms/Sms.php
new file mode 100755
index 0000000..592d6ba
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Sms/Sms.php
@@ -0,0 +1,337 @@
+<?php
+namespace Qiniu\Sms;
+
+use Qiniu\Http\Client;
+use Qiniu\Http\Error;
+use Qiniu\Config;
+use Qiniu\Auth;
+
+class Sms
+{
+    private $auth;
+    private $baseURL;
+
+    public function __construct(Auth $auth)
+    {
+        $this->auth = $auth;
+
+        $this->baseURL = sprintf("%s/%s/", Config::SMS_HOST, Config::SMS_VERSION);
+    }
+
+    /*
+     * 创建签名
+     * signature: string 类型,必填,【长度限制8个字符内】超过长度会报错
+     * source: string   类型,必填,申请签名时必须指定签名来源。取值范围为:
+        nterprises_and_institutions 企事业单位的全称或简称
+        website 工信部备案网站的全称或简称
+        app APP应用的全称或简称
+        public_number_or_small_program 公众号或小程序的全称或简称
+        store_name 电商平台店铺名的全称或简称
+        trade_name 商标名的全称或简称,
+     * pics: 本地的图片路径 string 类型,可选
+     *@return: 类型array {
+        "signature_id": <signature_id>
+        }
+     */
+    public function createSignature(string $signature, string $source, string $pics = null)
+    {
+        $params['signature'] = $signature;
+        $params['source'] = $source;
+        if (!empty($pics)) {
+            $params['pics'] = $this->imgToBase64($pics);
+        }
+        $body = json_encode($params);
+        $url =$this->baseURL.'signature';
+        $ret = $this->post($url, $body);
+        return $ret;
+    }
+
+    /*
+    * 编辑签名
+    *  id 签名id : string 类型,必填,
+    * signature: string 类型,必填,
+    * source: string    类型,必填,申请签名时必须指定签名来源。取值范围为:
+        enterprises_and_institutions 企事业单位的全称或简称
+        website 工信部备案网站的全称或简称
+        app APP应用的全称或简称
+        public_number_or_small_program 公众号或小程序的全称或简称
+        store_name 电商平台店铺名的全称或简称
+        trade_name 商标名的全称或简称,
+    * pics: 本地的图片路径 string   类型,可选,
+    * @return: 类型array {
+        "signature": string
+        }
+    */
+    public function updateSignature(string $id, string $signature, string $source, string $pics = null)
+    {
+        $params['signature'] = $signature;
+        $params['source'] = $source;
+        if (!empty($pics)) {
+            $params['pics'] = $this->imgToBase64($pics);
+        }
+        $body = json_encode($params);
+        $url =$this->baseURL.'signature/'.$id;
+        $ret = $this->PUT($url, $body);
+        return $ret;
+    }
+
+    /*
+ * 查询签名
+ * audit_status: 审核状态 string 类型,可选,
+   取值范围为: "passed"(通过), "rejected"(未通过), "reviewing"(审核中)
+ * page:页码 int  类型,
+ * page_size: 分页大小 int 类型,可选, 默认为20
+ *@return: 类型array {
+    "items": [{
+        "id": string,
+        "signature": string,
+        "source": string,
+        "audit_status": string,
+        "reject_reason": string,
+        "created_at": int64,
+        "updated_at": int64
+            }...],
+    "total": int,
+    "page": int,
+    "page_size": int,
+    }
+ */
+    public function checkSignature(string $audit_status = null, int $page = 1, int $page_size = 20)
+    {
+
+        $url = sprintf(
+            "%s?audit_status=%s&page=%s&page_size=%s",
+            $this->baseURL.'signature',
+            $audit_status,
+            $page,
+            $page_size
+        );
+        $ret  = $this->get($url);
+        return $ret;
+    }
+
+
+    /*
+ * 删除签名
+ * id 签名id string 类型,必填,
+ * @retrun : 请求成功 HTTP 状态码为 200
+ */
+    public function deleteSignature(string $id)
+    {
+        $url = $this->baseURL . 'signature/' . $id;
+        list(, $err)  = $this->delete($url);
+        return $err;
+    }
+
+
+
+
+    /*
+    * 创建模板
+    * name  : 模板名称 string 类型 ,必填
+    * template:  模板内容 string  类型,必填
+    * type: 模板类型 string 类型,必填,
+      取值范围为: notification (通知类短信), verification (验证码短信), marketing (营销类短信)
+    * description:  申请理由简述 string  类型,必填
+    * signature_id:  已经审核通过的签名 string  类型,必填
+    * @return: 类型 array {
+        "template_id": string
+                }
+    */
+    public function createTemplate(
+        string $name,
+        string $template,
+        string $type,
+        string $description,
+        string $signture_id
+    ) {
+        $params['name'] = $name;
+        $params['template'] = $template;
+        $params['type'] = $type;
+        $params['description'] = $description;
+        $params['signature_id'] = $signture_id;
+
+        $body = json_encode($params);
+        $url =$this->baseURL.'template';
+        $ret = $this->post($url, $body);
+        return $ret;
+    }
+
+    /*
+  * 查询模板
+  * audit_status: 审核状态 string 类型 ,可选,
+    取值范围为: passed (通过), rejected (未通过), reviewing (审核中)
+  * page:  页码 int  类型,可选,默认为 1
+  * page_size: 分页大小 int 类型,可选,默认为 20
+  * @return: 类型array{
+      "items": [{
+            "id": string,
+            "name": string,
+            "template": string,
+            "audit_status": string,
+            "reject_reason": string,
+            "type": string,
+            "signature_id": string, // 模版绑定的签名ID
+            "signature_text": string, // 模版绑定的签名内容
+            "created_at": int64,
+            "updated_at": int64
+        }...],
+        "total": int,
+        "page": int,
+        "page_size": int
+        }
+  */
+    public function queryTemplate(string $audit_status = null, int $page = 1, int $page_size = 20)
+    {
+
+        $url = sprintf(
+            "%s?audit_status=%s&page=%s&page_size=%s",
+            $this->baseURL.'template',
+            $audit_status,
+            $page,
+            $page_size
+        );
+        $ret  = $this->get($url);
+        return $ret;
+    }
+
+    /*
+    * 编辑模板
+    * id :模板id
+    * name  : 模板名称 string 类型 ,必填
+    * template:  模板内容 string  类型,必填
+    * description:  申请理由简述 string  类型,必填
+    * signature_id:  已经审核通过的签名 string  类型,必填
+    * @retrun : 请求成功 HTTP 状态码为 200
+    */
+    public function updateTemplate(
+        string $id,
+        string $name,
+        string $template,
+        string $description,
+        string $signature_id
+    ) {
+        $params['name'] = $name;
+        $params['template'] = $template;
+        $params['description'] = $description;
+        $params['signature_id'] = $signature_id;
+        $body = json_encode($params);
+        $url =$this->baseURL.'template/'.$id;
+        $ret = $this->PUT($url, $body);
+        return $ret;
+    }
+
+    /*
+    * 删除模板
+    * id :模板id string 类型,必填,
+    * @retrun : 请求成功 HTTP 状态码为 200
+    */
+    public function deleteTemplate(string $id)
+    {
+        $url = $this->baseURL . 'template/' . $id;
+        list(, $err)  = $this->delete($url);
+        return $err;
+    }
+
+    /*
+    * 发送短信
+    * 编辑模板
+    * template_id :模板id string类型,必填
+    * mobiles   : 手机号数组 []string 类型 ,必填
+    * parameters:  模板内容 map[string]string     类型,可选
+    * @return: 类型json {
+        "job_id": string
+        }
+    */
+    public function sendMessage(string $template_id, array $mobiles, array $parameters = null)
+    {
+        $params['template_id'] = $template_id;
+        $params['mobiles'] = $mobiles;
+        if (!empty($parameters)) {
+            $params['parameters'] = $parameters;
+        }
+        $body = json_encode($params);
+        $url =$this->baseURL.'message';
+        $ret = $this->post($url, $body);
+        return $ret;
+    }
+
+    public function imgToBase64(string $img_file)
+    {
+        $img_base64 = '';
+        if (file_exists($img_file)) {
+            $app_img_file = $img_file; // 图片路径
+            $img_info = getimagesize($app_img_file); // 取得图片的大小,类型等
+            $fp = fopen($app_img_file, "r"); // 图片是否可读权限
+            if ($fp) {
+                $filesize = filesize($app_img_file);
+                if ($filesize > 5*1024*1024) {
+                    die("pic size < 5M !");
+                }
+                $content = fread($fp, $filesize);
+                $file_content = chunk_split(base64_encode($content)); // base64编码
+                switch ($img_info[2]) {           //判读图片类型
+                    case 1:
+                        $img_type = 'gif';
+                        break;
+                    case 2:
+                        $img_type = 'jpg';
+                        break;
+                    case 3:
+                        $img_type = 'png';
+                        break;
+                }
+                //合成图片的base64编码
+                $img_base64 = 'data:image/' . $img_type . ';base64,' . $file_content;
+            }
+            fclose($fp);
+        }
+
+        return $img_base64;
+    }
+
+    private function get($url, $cType = null)
+    {
+        $rtcToken = $this->auth->authorizationV2($url, "GET", null, $cType);
+        $rtcToken['Content-Type'] = $cType;
+        $ret = Client::get($url, $rtcToken);
+        if (!$ret->ok()) {
+            return array(null, new Error($url, $ret));
+        }
+        return array($ret->json(), null);
+    }
+
+    private function delete($url, $contentType = 'application/json')
+    {
+        $rtcToken = $this->auth->authorizationV2($url, "DELETE", null, $contentType);
+        $rtcToken['Content-Type'] = $contentType;
+        $ret = Client::delete($url, $rtcToken);
+        if (!$ret->ok()) {
+            return array(null, new Error($url, $ret));
+        }
+        return array($ret->json(), null);
+    }
+
+    private function post($url, $body, $contentType = 'application/json')
+    {
+        $rtcToken = $this->auth->authorizationV2($url, "POST", $body, $contentType);
+        $rtcToken['Content-Type'] = $contentType;
+        $ret = Client::post($url, $body, $rtcToken);
+        if (!$ret->ok()) {
+            return array(null, new Error($url, $ret));
+        }
+        $r = ($ret->body === null) ? array() : $ret->json();
+        return array($r, null);
+    }
+    private function PUT($url, $body, $contentType = 'application/json')
+    {
+        $rtcToken = $this->auth->authorizationV2($url, "PUT", $body, $contentType);
+        $rtcToken['Content-Type'] = $contentType;
+        $ret = Client::put($url, $body, $rtcToken);
+        if (!$ret->ok()) {
+            return array(null, new Error($url, $ret));
+        }
+        $r = ($ret->body === null) ? array() : $ret->json();
+        return array($r, null);
+    }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Storage/ArgusManager.php b/vendor/qiniu/php-sdk/src/Qiniu/Storage/ArgusManager.php
new file mode 100755
index 0000000..0889370
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Storage/ArgusManager.php
@@ -0,0 +1,73 @@
+<?php
+namespace Qiniu\Storage;
+
+use Qiniu\Auth;
+use Qiniu\Config;
+use Qiniu\Zone;
+use Qiniu\Http\Client;
+use Qiniu\Http\Error;
+
+/**
+ * 主要涉及了鉴黄接口的实现,具体的接口规格可以参考
+ *
+ * @link https://developer.qiniu.com/dora/manual/3674/kodo-product-introduction
+ */
+final class ArgusManager
+{
+    private $auth;
+    private $config;
+
+    public function __construct(Auth $auth, Config $config = null)
+    {
+        $this->auth = $auth;
+        if ($config == null) {
+            $this->config = new Config();
+        } else {
+            $this->config = $config;
+        }
+    }
+
+    /**
+     * 视频鉴黄
+     *
+     * @param $body     body信息
+     * @param $vid      videoID
+     *
+     * @return mixed      成功返回NULL,失败返回对象Qiniu\Http\Error
+     * @link  https://developer.qiniu.com/dora/manual/4258/video-pulp
+     */
+    public function pulpVideo($body, $vid)
+    {
+        $path = '/v1/video/' . $vid;
+        
+        return $this->arPost($path, $body);
+    }
+
+    private function getArHost()
+    {
+        $scheme = "http://";
+        if ($this->config->useHTTPS == true) {
+            $scheme = "https://";
+        }
+        return $scheme . Config::ARGUS_HOST;
+    }
+
+    private function arPost($path, $body = null)
+    {
+        $url = $this->getArHost() . $path;
+        return $this->post($url, $body);
+    }
+
+    private function post($url, $body)
+    {
+        $headers = $this->auth->authorizationV2($url, 'POST', $body, 'application/json');
+        $headers['Content-Type']='application/json';
+        $ret = Client::post($url, $body, $headers);
+        if (!$ret->ok()) {
+            print($ret->statusCode);
+            return array(null, new Error($url, $ret));
+        }
+        $r = ($ret->body === null) ? array() : $ret->json();
+        return array($r, null);
+    }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Storage/BucketManager.php b/vendor/qiniu/php-sdk/src/Qiniu/Storage/BucketManager.php
new file mode 100755
index 0000000..0a2413d
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Storage/BucketManager.php
@@ -0,0 +1,1107 @@
+<?php
+namespace Qiniu\Storage;
+
+use Qiniu\Auth;
+use Qiniu\Config;
+use Qiniu\Zone;
+use Qiniu\Http\Client;
+use Qiniu\Http\Error;
+
+/**
+ * 主要涉及了空间资源管理及批量操作接口的实现,具体的接口规格可以参考
+ *
+ * @link https://developer.qiniu.com/kodo/api/1274/rs
+ */
+final class BucketManager
+{
+    private $auth;
+    private $config;
+
+    public function __construct(Auth $auth, Config $config = null)
+    {
+        $this->auth = $auth;
+        if ($config == null) {
+            $this->config = new Config();
+        } else {
+            $this->config = $config;
+        }
+    }
+
+    /**
+     * 获取指定账号下所有的空间名。
+     *
+     * @return string[] 包含所有空间名
+     */
+    public function buckets($shared = true)
+    {
+        $includeShared = "false";
+        if ($shared === true) {
+            $includeShared = "true";
+        }
+        return $this->rsGet('/buckets?shared=' . $includeShared);
+    }
+
+    /**
+     * 列举空间,返回bucket列表
+     * region 指定区域,global 指定全局空间。
+     * 在指定了 region 参数时,
+     * 如果指定 global 为 true,那么忽略 region 参数指定的区域,返回所有区域的全局空间。
+     * 如果没有指定 global 为 true,那么返回指定区域中非全局空间。
+     * 在没有指定 region 参数时(包括指定为空""),
+     * 如果指定 global 为 true,那么返回所有区域的全局空间。
+     * 如果没有指定 global 为 true,那么返回指定区域中所有的空间,包括全局空间。
+     * 在指定了line为 true 时,只返回 Line 空间;否则,只返回非 Line 空间。
+     * share 参数用于指定共享空间。
+     */
+
+    public function listbuckets(
+        $region = null,
+        $line = 'false',
+        $shared = 'false'
+    ) {
+        $path = '/v3/buckets?region=' . $region . '&line=' . $line . '&shared=' . $shared;
+        $info = $this->ucPost($path);
+        return $info;
+    }
+
+    /**
+     * 创建空间
+     *
+     * @param $name     创建的空间名
+     * @param $region    创建的区域,默认华东
+     *
+     * @return mixed      成功返回NULL,失败返回对象Qiniu\Http\Error
+     */
+    public function createBucket($name, $region = 'z0')
+    {
+        $path = '/mkbucketv2/'.$name.'/region/' . $region;
+        return $this->rsPost($path, null);
+    }
+
+    /**
+     * 删除空间
+     *
+     * @param $name     删除的空间名
+     *
+     * @return mixed      成功返回NULL,失败返回对象Qiniu\Http\Error
+     */
+    public function deleteBucket($name)
+    {
+        $path = '/drop/'.$name;
+        return $this->rsPost($path, null);
+    }
+
+    /**
+     * 获取指定空间绑定的所有的域名
+     *
+     * @return string[] 包含所有空间域名
+     */
+    public function domains($bucket)
+    {
+        return $this->apiGet('/v6/domain/list?tbl=' . $bucket);
+    }
+
+    /**
+     * 获取指定空间的相关信息
+     *
+     * @return string[] 包含空间信息
+     */
+    public function bucketInfo($bucket)
+    {
+        $path = '/v2/bucketInfo?bucket=' . $bucket;
+        $info = $this->ucPost($path);
+        return $info;
+    }
+
+    /**
+     * 获取指定zone的空间信息列表
+     * 在Region 未指定且Global 不为 true 时(包含未指定的情况,下同),返回用户的所有空间。
+     * 在指定了 region 参数且 global 不为 true 时,只列举非全局空间。
+     * shared 不指定shared参数或指定shared为rw或false时,返回包含具有读写权限空间,
+     * 指定shared为rd或true时,返回包含具有读权限空间。
+     * fs:如果为 true,会返回每个空间当前的文件数和存储量(实时数据)。
+     * @return string[] 包含空间信息
+     */
+    public function bucketInfos($region = null, $shared = 'false', $fs = 'false')
+    {
+        $path = '/v2/bucketInfos?region=' . $region . '&shared=' . $shared . '&fs=' . $fs;
+        $info = $this->ucPost($path);
+        return $info;
+    }
+
+    /**
+     * 获取空间绑定的域名列表
+     * @return string[] 包含空间绑定的所有域名
+     */
+
+    /**
+     * 列取空间的文件列表
+     *
+     * @param $bucket     空间名
+     * @param $prefix     列举前缀
+     * @param $marker     列举标识符
+     * @param $limit      单次列举个数限制
+     * @param $delimiter  指定目录分隔符
+     *
+     * @return array    包含文件信息的数组,类似:[
+*                                              {
+*                                                 "hash" => "<Hash string>",
+*                                                  "key" => "<Key string>",
+*                                                  "fsize" => "<file size>",
+*                                                  "putTime" => "<file modify time>"
+*                                              },
+*                                              ...
+*                                            ]
+     * @link  http://developer.qiniu.com/docs/v6/api/reference/rs/list.html
+     */
+    public function listFiles(
+        $bucket,
+        $prefix = null,
+        $marker = null,
+        $limit = 1000,
+        $delimiter = null
+    ) {
+        $query = array('bucket' => $bucket);
+        \Qiniu\setWithoutEmpty($query, 'prefix', $prefix);
+        \Qiniu\setWithoutEmpty($query, 'marker', $marker);
+        \Qiniu\setWithoutEmpty($query, 'limit', $limit);
+        \Qiniu\setWithoutEmpty($query, 'delimiter', $delimiter);
+        $url = $this->getRsfHost() . '/list?' . http_build_query($query);
+        return $this->get($url);
+    }
+
+    /**
+     * 列取空间的文件列表
+     *
+     * @param $bucket     空间名
+     * @param $prefix     列举前缀
+     * @param $marker     列举标识符
+     * @param $limit      单次列举个数限制
+     * @param $delimiter  指定目录分隔符
+     * @param $skipconfirm  是否跳过已删除条目的确认机制
+     *
+     * @return array    包含文件信息的数组,类似:[
+*                                              {
+*                                                 "hash" => "<Hash string>",
+*                                                  "key" => "<Key string>",
+*                                                  "fsize" => "<file size>",
+*                                                  "putTime" => "<file modify time>"
+*                                              },
+*                                              ...
+*                                            ]
+     * @link  http://developer.qiniu.com/docs/v6/api/reference/rs/list.html
+     */
+    public function listFilesv2(
+        $bucket,
+        $prefix = null,
+        $marker = null,
+        $limit = 1000,
+        $delimiter = null,
+        $skipconfirm = true
+    ) {
+        $query = array('bucket' => $bucket);
+        \Qiniu\setWithoutEmpty($query, 'prefix', $prefix);
+        \Qiniu\setWithoutEmpty($query, 'marker', $marker);
+        \Qiniu\setWithoutEmpty($query, 'limit', $limit);
+        \Qiniu\setWithoutEmpty($query, 'delimiter', $delimiter);
+        \Qiniu\setWithoutEmpty($query, 'skipconfirm', $skipconfirm);
+        $path = '/v2/list?' . http_build_query($query);
+        $url = $this->getRsfHost() . $path;
+        $headers = $this->auth->authorization($url, null, 'application/x-www-form-urlencoded');
+        $ret = Client::post($url, null, $headers);
+        if (!$ret->ok()) {
+            return array(null, new Error($url, $ret));
+        }
+        $r = explode("\n", $ret->body);
+        $pop = array_pop($r);
+        return array($r, null);
+    }
+
+    /**
+     * 设置Referer防盗链
+     *
+     * @param $bucket     空间名
+     * @param $mode     0: 表示关闭Referer(使用此选项将会忽略以下参数并将恢复默认值);
+     * 1: 表示设置Referer白名单; 2:表示设置Referer黑名单
+     * @param $norefer     0: 表示不允许空 Refer 访问; 1: 表示允许空 Refer 访问
+     * @param $pattern      规则字符串, 当前允许格式分为三种: 一种为空主机头域名,
+     * 比如 foo.com; 一种是泛域名,比如 *.bar.com; 一种是完全通配符,
+     * 即一个 *; 多个规则之间用;隔开, 比如: foo.com;*.bar.com;sub.foo.com;*.sub.bar.com
+     * @param $source_enabled  源站是否支持,默认为0只给CDN配置, 设置为1表示开启源站防盗链
+     *
+     * @return mixed      成功返回NULL,失败返回对象Qiniu\Http\Error
+     */
+    // public function referAntiLeech(){
+
+    // }
+
+    /**
+     * 增加bucket生命规则
+     *
+     * @param $bucket     空间名
+     * @param $name     规则名称 bucket 内唯一,长度小于50,不能为空,只能为
+     * 字母、数字、下划线
+     * @param $prefix     同一个 bucket 里面前缀不能重复
+     * @param $delete_after_days      指定上传文件多少天后删除,指定为0表示不删除,
+     * 大于0表示多少天后删除,需大于 to_line_after_days
+     * @param $to_line_after_days  指定文件上传多少天后转低频存储。指定为0表示
+     * 不转低频存储,小于0表示上传的文件立即变低频存储
+     * @return mixed      成功返回NULL,失败返回对象Qiniu\Http\Error
+     */
+    public function bucketLifecycleRule(
+        $bucket,
+        $name,
+        $prefix,
+        $delete_after_days,
+        $to_line_after_days
+    ) {
+        $path = '/rules/add';
+        if ($bucket) {
+            $params['bucket'] = $bucket;
+        }
+        if ($name) {
+            $params['name'] = $name;
+        }
+        if ($prefix) {
+            $params['prefix'] = $prefix;
+        }
+        if ($delete_after_days) {
+            $params['delete_after_days'] = $delete_after_days;
+        }
+        if ($to_line_after_days) {
+            $params['to_line_after_days'] = $to_line_after_days;
+        }
+        $data = http_build_query($params);
+        $info = $this->ucPost($path, $data);
+        return $info;
+    }
+
+    /**
+     * 更新bucket生命规则
+     *
+     * @param $bucket     空间名
+     * @param $name     规则名称 bucket 内唯一,长度小于50,不能为空,只能为字母、
+     * 数字、下划线
+     * @param $prefix     同一个 bucket 里面前缀不能重复
+     * @param $delete_after_days      指定上传文件多少天后删除,指定为0表示不删除,
+     * 大于0表示多少天后删除,需大于 to_line_after_days
+     * @param $to_line_after_days  指定文件上传多少天后转低频存储。指定为0表示不
+     * 转低频存储,小于0表示上传的文件立即变低频存储
+     * @return mixed      成功返回NULL,失败返回对象Qiniu\Http\Error
+     */
+    public function updateBucketLifecycleRule(
+        $bucket,
+        $name,
+        $prefix,
+        $delete_after_days,
+        $to_line_after_days
+    ) {
+        $path = '/rules/update';
+        if ($bucket) {
+            $params['bucket'] = $bucket;
+        }
+        if ($name) {
+            $params['name'] = $name;
+        }
+        if ($prefix) {
+            $params['prefix'] = $prefix;
+        }
+        if ($delete_after_days) {
+            $params['delete_after_days'] = $delete_after_days;
+        }
+        if ($to_line_after_days) {
+            $params['to_line_after_days'] = $to_line_after_days;
+        }
+        $data = http_build_query($params);
+        $info = $this->ucPost($path, $data);
+        return $info;
+    }
+
+    /**
+     * 获取bucket生命规则
+     *
+     * @param $bucket     空间名
+     * @return mixed      成功返回NULL,失败返回对象Qiniu\Http\Error
+     */
+    public function getBucketLifecycleRules($bucket)
+    {
+        $path = '/rules/get?bucket=' . $bucket;
+        $info = $this->ucGet($path);
+        return $info;
+    }
+
+    /**
+     * 删除bucket生命规则
+     *
+     * @param $bucket     空间名
+     * @param $name     规则名称 bucket 内唯一,长度小于50,不能为空,
+     * 只能为字母、数字、下划线()
+     * @return mixed      成功返回NULL,失败返回对象Qiniu\Http\Error
+     */
+    public function deleteBucketLifecycleRule($bucket, $name)
+    {
+        $path = '/rules/delete';
+        if ($bucket) {
+            $params['bucket'] = $bucket;
+        }
+        if ($name) {
+            $params['name'] = $name;
+        }
+        $data = http_build_query($params);
+        $info = $this->ucPost($path, $data);
+        return $info;
+    }
+
+    /**
+     * 增加bucket事件通知规则
+     *
+     * @param $bucket     空间名
+     * @param $name     规则名称 bucket 内唯一,长度小于50,不能为空,
+     * 只能为字母、数字、下划线()
+     * @param $prefix     同一个 bucket 里面前缀不能重复
+     * @param $suffix      可选,文件配置的后缀
+     * @param $event  事件类型,可以指定多个,包括 put,mkfile,delete,copy,move,append,
+     * disable,enable,deleteMarkerCreate
+     * @param $callbackURL 通知URL,可以指定多个,失败依次重试
+     * @param $access_key 可选,设置的话会对通知请求用对应的ak、sk进行签名
+     * @param $host 可选,通知请求的host
+     *
+     * @return mixed      成功返回NULL,失败返回对象Qiniu\Http\Error
+     */
+    public function putBucketEvent(
+        $bucket,
+        $name,
+        $prefix,
+        $suffix,
+        $event,
+        $callbackURL,
+        $access_key = null,
+        $host = null
+    ) {
+        $path = '/events/add';
+        if ($bucket) {
+            $params['bucket'] = $bucket;
+        }
+        if ($name) {
+            $params['name'] = $name;
+        }
+        if ($prefix) {
+            $params['prefix'] = $prefix;
+        }
+        if ($suffix) {
+            $params['suffix'] = $suffix;
+        }
+        if ($callbackURL) {
+            $params['callbackURL'] = $callbackURL;
+        }
+        if ($access_key) {
+            $params['access_key'] = $access_key;
+        }
+        if ($host) {
+            $params['host'] = $host;
+        }
+        $data = http_build_query($params);
+        if ($event) {
+            $eventpath = "";
+            foreach ($event as $key => $value) {
+                $eventpath .= "&event=$value";
+            }
+            $data .= $eventpath;
+        }
+        $info = $this->ucPost($path, $data);
+        return $info;
+    }
+
+    /**
+     * 更新bucket事件通知规则
+     *
+     * @param $bucket     空间名
+     * @param $name     规则名称 bucket 内唯一,长度小于50,不能为空,
+     * 只能为字母、数字、下划线()
+     * @param $prefix     同一个 bucket 里面前缀不能重复
+     * @param $suffix      可选,文件配置的后缀
+     * @param $event  事件类型,可以指定多个,包括 put,mkfile,delete,copy,move,append,disable,
+     * enable,deleteMarkerCreate
+     * @param $callbackURL 通知URL,可以指定多个,失败依次重试
+     * @param $access_key 可选,设置的话会对通知请求用对应的ak、sk进行签名
+     * @param $host 可选,通知请求的host
+     *
+     * @return mixed      成功返回NULL,失败返回对象Qiniu\Http\Error
+     */
+    public function updateBucketEvent(
+        $bucket,
+        $name,
+        $prefix,
+        $suffix,
+        $event,
+        $callbackURL,
+        $access_key = null,
+        $host = null
+    ) {
+        $path = '/events/update';
+        if ($bucket) {
+            $params['bucket'] = $bucket;
+        }
+        if ($name) {
+            $params['name'] = $name;
+        }
+        if ($prefix) {
+            $params['prefix'] = $prefix;
+        }
+        if ($suffix) {
+            $params['suffix'] = $suffix;
+        }
+        if ($event) {
+            $params['event'] = $event;
+        }
+        if ($callbackURL) {
+            $params['callbackURL'] = $callbackURL;
+        }
+        if ($access_key) {
+            $params['access_key'] = $access_key;
+        }
+        if ($host) {
+            $params['host'] = $host;
+        }
+        $data = http_build_query($params);
+        $info = $this->ucPost($path, $data);
+        return $info;
+    }
+
+    /**
+     * 获取bucket事件通知规则
+     *
+     * @param $bucket     空间名
+     * @return mixed      成功返回NULL,失败返回对象Qiniu\Http\Error
+     */
+    public function getBucketEvents($bucket)
+    {
+        $path = '/events/get?bucket=' . $bucket;
+        $info = $this->ucGet($path);
+        return $info;
+    }
+
+    /**
+     * 删除bucket事件通知规则
+     *
+     * @param $bucket     空间名
+     * @param $name     规则名称 bucket 内唯一,长度小于50,不能为空,
+     * 只能为字母、数字、下划线
+     * @return mixed      成功返回NULL,失败返回对象Qiniu\Http\Error
+     */
+    public function deleteBucketEvent($bucket, $name)
+    {
+        $path = '/events/delete';
+        if ($bucket) {
+            $params['bucket'] = $bucket;
+        }
+        if ($name) {
+            $params['name'] = $name;
+        }
+        $data = http_build_query($params);
+        $info = $this->ucPost($path, $data);
+        return $info;
+    }
+
+    /**
+     * 设置bucket的跨域信息,最多允许设置10条跨域规则。
+     * 对于同一个域名如果设置了多条规则,那么按顺序使用第一条匹配的规则去生成返回值。
+     * 对于简单跨域请求,只匹配 Origin;
+     * allowed_orgin: 允许的域名。必填;支持通配符*;*表示全部匹配;只有第一个*生效;
+     * 需要设置"Scheme";大小写敏感。例如
+     * 规则:http://*.abc.*.com 请求:"http://test.abc.test.com" 结果:不通过
+     * 规则:"http://abc.com" 请求:"https://abc.com"/"abc.com" 结果:不通过
+     * 规则:"abc.com" 请求:"http://abc.com" 结果:不通过
+     * allowed_method: 允许的方法。必填;不支持通配符;大小写不敏感;
+     * allowed_header: 允许的header。选填;支持通配符*,
+     * 但只能是单独的*,表示允许全部header,其他*不生效;
+     * 空则不允许任何header;大小写不敏感;
+     * exposed_header: 暴露的header。选填;不支持通配符;
+     * X-Log, X-Reqid是默认会暴露的两个header;
+     * 其他的header如果没有设置,则不会暴露;大小写不敏感;
+     * max_age: 结果可以缓存的时间。选填;空则不缓存;
+     * allowed_credentials:该配置不支持设置,默认为true。
+     * 备注:如果没有设置任何corsRules,那么默认允许所有的跨域请求
+     */
+    // public function putCorsRules(string $bucket, array $params)
+    // {
+    //     $path = '/corsRules/set/' . $bucket;
+    //     $data = json_encode($params);
+    //     $info = $this->ucPost($path, $data);
+    //     return $info;
+    // }
+
+    /**
+     * 获取bucket的跨域信息
+     * $bucket 空间名
+     */
+    public function getCorsRules($bucket)
+    {
+        $path = '/corsRules/get/' . $bucket;
+        $info = $this->ucGet($path);
+        return $info;
+    }
+
+    /**
+     * 设置回源规则
+     * 使用该API设置源站优先级高于/image设置的源站,即IO优先读取source接口设置的源站配置,
+     * 如果存在会忽略/image设置的源站
+     * Bucket 空间名
+     * Host(可选)回源Host
+     * RetryCodes(可选),镜像回源时源站返回Code可以重试,最多指定3个,当前只支持4xx错误码重试
+     * SourceQiniuAK,SourceQiniuSK(可选)如果存在将在回源时对URL进行签名,客户源站可以验证
+     * 以保证请求来自Qiniu服务器
+     * Expires(可选) 签名过期时间,如果不设置默认为1小时
+     * Addr 回源地址,不可重复。
+     * Weight 权重,范围限制1-100,不填默认为1,回源时会根据所有源的权重值进行源站选择,
+     * 主备源会分开计算.
+     * Backup 是否备用回源,回源优先尝试主源
+     */
+    // public function putBucktSourceConfig(array $params)
+    // {
+    //     $path = '/mirrorConfig/set';
+    //     $data = json_encode($params);
+    //     $info = $this->ucPostV2($path, $data);
+    //     return $info;
+    // }
+
+    /**
+     * 获取空间回源配置
+     */
+    public function getBucktSourceConfig(array $params)
+    {
+        $path = '/mirrorConfig/get';
+        $data = json_encode($params);
+        $info = $this->ucPostV2($path, $data);
+        return $info;
+    }
+
+    /**
+     * 开关原图保护
+     * mode 为1表示开启原图保护,0表示关闭
+     */
+    public function putBucketAccessStyleMode($bucket, $mode)
+    {
+        $path = '/accessMode/' . $bucket . '/mode/' . $mode;
+        $info = $this->ucPost($path, null);
+        return $info;
+    }
+
+    /**
+     * 设置私有属性
+     * private为0表示公开,为1表示私有
+     */
+    public function putBucketAccessMode($bucket, $private)
+    {
+        $path = '/bucket/' . $bucket . '/private/' . $private;
+        $info = $this->ucPost($path, null);
+        return $info;
+    }
+
+    /**
+     * 设置referer防盗链
+     * bucket=<BucketName>: bucket 名
+     * mode=<AntiLeechMode>:
+     * 0: 表示关闭Referer(使用此选项将会忽略以下参数并将恢复默认值);
+     * 1: 表示设置Referer白名单; 2: 表示设置Referer黑名单
+     * norefer=<NoRefer>: 0: 表示不允许空 Refer 访问;
+     * 1: 表示允许空 Refer 访问
+     * pattern=<Pattern>: 规则字符串, 当前允许格式分为三种:
+     * 一种为空主机头域名, 比如 foo.com;
+     * 一种是泛域名, 比如 *.bar.com; 一种是完全通配符, 即一个 *;
+     * 多个规则之间用;隔开, 比如: foo.com;*.bar.com;sub.foo.com;*.sub.bar.com
+     * 空主机头域名可以是多级域名,比如 foo.bar.com。
+     * 多个域名之间不允许夹带空白字符。
+     * source_enabled=:1
+     */
+    public function putReferAntiLeech($bucket, $mode, $norefer, $pattern, $enabled = 1)
+    {
+        $path = "/referAntiLeech?bucket=$bucket&mode=$mode&norefer=$norefer&pattern=$pattern&source_enabled=$enabled";
+        $info = $this->ucPost($path, null);
+        return $info;
+    }
+
+    /**
+     * 设置Bucket的maxAge
+     * maxAge为0或者负数表示为默认值(31536000)
+     */
+    public function putBucketMaxAge($bucket, $maxAge)
+    {
+        $path = '/maxAge?bucket=' . $bucket . '&maxAge=' . $maxAge;
+        $info = $this->ucPost($path, null);
+        return $info;
+    }
+
+    /**
+     * 设置配额
+     * <bucket>: 空间名称,不支持授权空间
+     * <size>: 空间存储量配额,参数传入0或不传表示不更改当前配置,传入-1表示取消限额,
+     * 新创建的空间默认没有限额。
+     * <count>: 空间文件数配额,参数含义同<size>
+     */
+    public function putBucketQuota($bucket, $size, $count)
+    {
+        $path = '/setbucketquota/' . $bucket . '/size/' . $size . '/count/' . $count;
+        $info = $this->apiPost($path, null);
+        return $info;
+    }
+
+    /**
+     * 获取配额
+     * bucket 空间名称
+     */
+    public function getBucketQuota($bucket)
+    {
+        $path = '/getbucketquota/' . $bucket;
+        $info = $this->apiPost($path, null);
+        return $info;
+    }
+
+    /**
+     * 获取资源的元信息,但不返回文件内容
+     *
+     * @param $bucket     待获取信息资源所在的空间
+     * @param $key        待获取资源的文件名
+     *
+     * @return array    包含文件信息的数组,类似:
+*                                              [
+*                                                  "hash" => "<Hash string>",
+*                                                  "key" => "<Key string>",
+*                                                  "fsize" => <file size>,
+*                                                  "putTime" => "<file modify time>"
+*                                                  "fileType" => <file type>
+*                                              ]
+     *
+     * @link  http://developer.qiniu.com/docs/v6/api/reference/rs/stat.html
+     */
+    public function stat($bucket, $key)
+    {
+        $path = '/stat/' . \Qiniu\entry($bucket, $key);
+        return $this->rsGet($path);
+    }
+
+    /**
+     * 删除指定资源
+     *
+     * @param $bucket     待删除资源所在的空间
+     * @param $key        待删除资源的文件名
+     *
+     * @return mixed      成功返回NULL,失败返回对象Qiniu\Http\Error
+     * @link  http://developer.qiniu.com/docs/v6/api/reference/rs/delete.html
+     */
+    public function delete($bucket, $key)
+    {
+        $path = '/delete/' . \Qiniu\entry($bucket, $key);
+        list(, $error) = $this->rsPost($path);
+        return $error;
+    }
+
+
+    /**
+     * 给资源进行重命名,本质为move操作。
+     *
+     * @param $bucket     待操作资源所在空间
+     * @param $oldname    待操作资源文件名
+     * @param $newname    目标资源文件名
+     *
+     * @return mixed      成功返回NULL,失败返回对象Qiniu\Http\Error
+     */
+    public function rename($bucket, $oldname, $newname)
+    {
+        return $this->move($bucket, $oldname, $bucket, $newname);
+    }
+
+    /**
+     * 对资源进行复制。
+     *
+     * @param $from_bucket     待操作资源所在空间
+     * @param $from_key        待操作资源文件名
+     * @param $to_bucket       目标资源空间名
+     * @param $to_key          目标资源文件名
+     *
+     * @return mixed      成功返回NULL,失败返回对象Qiniu\Http\Error
+     * @link  http://developer.qiniu.com/docs/v6/api/reference/rs/copy.html
+     */
+    public function copy($from_bucket, $from_key, $to_bucket, $to_key, $force = false)
+    {
+        $from = \Qiniu\entry($from_bucket, $from_key);
+        $to = \Qiniu\entry($to_bucket, $to_key);
+        $path = '/copy/' . $from . '/' . $to;
+        if ($force === true) {
+            $path .= '/force/true';
+        }
+        list(, $error) = $this->rsPost($path);
+        return $error;
+    }
+
+    /**
+     * 将资源从一个空间到另一个空间
+     *
+     * @param $from_bucket     待操作资源所在空间
+     * @param $from_key        待操作资源文件名
+     * @param $to_bucket       目标资源空间名
+     * @param $to_key          目标资源文件名
+     *
+     * @return mixed      成功返回NULL,失败返回对象Qiniu\Http\Error
+     * @link  http://developer.qiniu.com/docs/v6/api/reference/rs/move.html
+     */
+    public function move($from_bucket, $from_key, $to_bucket, $to_key, $force = false)
+    {
+        $from = \Qiniu\entry($from_bucket, $from_key);
+        $to = \Qiniu\entry($to_bucket, $to_key);
+        $path = '/move/' . $from . '/' . $to;
+        if ($force) {
+            $path .= '/force/true';
+        }
+        list(, $error) = $this->rsPost($path);
+        return $error;
+    }
+
+    /**
+     * 主动修改指定资源的文件元信息
+     *
+     * @param $bucket     待操作资源所在空间
+     * @param $key        待操作资源文件名
+     * @param $mime       待操作文件目标mimeType
+     *
+     * @return mixed      成功返回NULL,失败返回对象Qiniu\Http\Error
+     * @link  http://developer.qiniu.com/docs/v6/api/reference/rs/chgm.html
+     */
+    public function changeMime($bucket, $key, $mime)
+    {
+        $resource = \Qiniu\entry($bucket, $key);
+        $encode_mime = \Qiniu\base64_urlSafeEncode($mime);
+        $path = '/chgm/' . $resource . '/mime/' . $encode_mime;
+        list(, $error) = $this->rsPost($path);
+        return $error;
+    }
+
+
+    /**
+     * 修改指定资源的存储类型
+     *
+     * @param $bucket     待操作资源所在空间
+     * @param $key        待操作资源文件名
+     * @param $fileType       待操作文件目标文件类型
+     *
+     * @return mixed      成功返回NULL,失败返回对象Qiniu\Http\Error
+     * @link  https://developer.qiniu.com/kodo/api/3710/modify-the-file-type
+     */
+    public function changeType($bucket, $key, $fileType)
+    {
+        $resource = \Qiniu\entry($bucket, $key);
+        $path = '/chtype/' . $resource . '/type/' . $fileType;
+        list(, $error) = $this->rsPost($path);
+        return $error;
+    }
+
+    /**
+     * 修改文件的存储状态,即禁用状态和启用状态间的的互相转换
+     *
+     * @param $bucket     待操作资源所在空间
+     * @param $key        待操作资源文件名
+     * @param $status       待操作文件目标文件类型
+     *
+     * @return mixed      成功返回NULL,失败返回对象Qiniu\Http\Error
+     * @link  https://developer.qiniu.com/kodo/api/4173/modify-the-file-status
+     */
+    public function changeStatus($bucket, $key, $status)
+    {
+        $resource = \Qiniu\entry($bucket, $key);
+        $path = '/chstatus/' . $resource . '/status/' . $status;
+        list(, $error) = $this->rsPost($path);
+        return $error;
+    }
+
+    /**
+     * 从指定URL抓取资源,并将该资源存储到指定空间中
+     *
+     * @param $url        指定的URL
+     * @param $bucket     目标资源空间
+     * @param $key        目标资源文件名
+     *
+     * @return array    包含已拉取的文件信息。
+     *                         成功时:  [
+     *                                          [
+     *                                              "hash" => "<Hash string>",
+     *                                              "key" => "<Key string>"
+     *                                          ],
+     *                                          null
+     *                                  ]
+     *
+     *                         失败时:  [
+     *                                          null,
+     *                                         Qiniu/Http/Error
+     *                                  ]
+     * @link  http://developer.qiniu.com/docs/v6/api/reference/rs/fetch.html
+     */
+    public function fetch($url, $bucket, $key = null)
+    {
+
+        $resource = \Qiniu\base64_urlSafeEncode($url);
+        $to = \Qiniu\entry($bucket, $key);
+        $path = '/fetch/' . $resource . '/to/' . $to;
+
+        $ak = $this->auth->getAccessKey();
+        $ioHost = $this->config->getIovipHost($ak, $bucket);
+
+        $url = $ioHost . $path;
+        return $this->post($url, null);
+    }
+
+    /**
+     * 从镜像源站抓取资源到空间中,如果空间中已经存在,则覆盖该资源
+     *
+     * @param $bucket     待获取资源所在的空间
+     * @param $key        代获取资源文件名
+     *
+     * @return mixed      成功返回NULL,失败返回对象Qiniu\Http\Error
+     * @link  http://developer.qiniu.com/docs/v6/api/reference/rs/prefetch.html
+     */
+    public function prefetch($bucket, $key)
+    {
+        $resource = \Qiniu\entry($bucket, $key);
+        $path = '/prefetch/' . $resource;
+
+        $ak = $this->auth->getAccessKey();
+        $ioHost = $this->config->getIovipHost($ak, $bucket);
+
+        $url = $ioHost . $path;
+        list(, $error) = $this->post($url, null);
+        return $error;
+    }
+
+    /**
+     * 在单次请求中进行多个资源管理操作
+     *
+     * @param $operations     资源管理操作数组
+     *
+     * @return array 每个资源的处理情况,结果类似:
+     *              [
+     *                   { "code" => <HttpCode int>, "data" => <Data> },
+     *                   { "code" => <HttpCode int> },
+     *                   { "code" => <HttpCode int> },
+     *                   { "code" => <HttpCode int> },
+     *                   { "code" => <HttpCode int>, "data" => { "error": "<ErrorMessage string>" } },
+     *                   ...
+     *               ]
+     * @link http://developer.qiniu.com/docs/v6/api/reference/rs/batch.html
+     */
+    public function batch($operations)
+    {
+        $params = 'op=' . implode('&op=', $operations);
+        return $this->rsPost('/batch', $params);
+    }
+
+    /**
+     * 设置文件的生命周期
+     *
+     * @param $bucket 设置文件生命周期文件所在的空间
+     * @param $key    设置文件生命周期文件的文件名
+     * @param $days   设置该文件多少天后删除,当$days设置为0时表示取消该文件的生命周期
+     *
+     * @return Mixed
+     * @link https://developer.qiniu.com/kodo/api/update-file-lifecycle
+     */
+    public function deleteAfterDays($bucket, $key, $days)
+    {
+        $entry = \Qiniu\entry($bucket, $key);
+        $path = "/deleteAfterDays/$entry/$days";
+        list(, $error) = $this->rsPost($path);
+        return $error;
+    }
+
+    private function getRsfHost()
+    {
+        $scheme = "http://";
+        if ($this->config->useHTTPS == true) {
+            $scheme = "https://";
+        }
+        return $scheme . Config::RSF_HOST;
+    }
+
+    private function getRsHost()
+    {
+        $scheme = "http://";
+        if ($this->config->useHTTPS == true) {
+            $scheme = "https://";
+        }
+        return $scheme . Config::RS_HOST;
+    }
+
+    private function getApiHost()
+    {
+        $scheme = "http://";
+        if ($this->config->useHTTPS == true) {
+            $scheme = "https://";
+        }
+        return $scheme . Config::API_HOST;
+    }
+
+    private function getUcHost()
+    {
+        $scheme = "http://";
+        if ($this->config->useHTTPS == true) {
+            $scheme = "https://";
+        }
+        return $scheme . Config::UC_HOST;
+    }
+
+    private function rsPost($path, $body = null)
+    {
+        $url = $this->getRsHost() . $path;
+        return $this->post($url, $body);
+    }
+
+    private function apiPost($path, $body = null)
+    {
+        $url = $this->getApiHost() . $path;
+        return $this->post($url, $body);
+    }
+
+    private function ucPost($path, $body = null)
+    {
+        $url = $this->getUcHost() . $path;
+        return $this->post($url, $body);
+    }
+
+    private function ucGet($path)
+    {
+        $url = $this->getUcHost() . $path;
+        return $this->get($url);
+    }
+
+    private function apiGet($path)
+    {
+        $url = $this->getApiHost() . $path;
+        return $this->get($url);
+    }
+
+    private function rsGet($path)
+    {
+        $url = $this->getRsHost() . $path;
+        return $this->get($url);
+    }
+
+    private function get($url)
+    {
+        $headers = $this->auth->authorization($url);
+        $ret = Client::get($url, $headers);
+        if (!$ret->ok()) {
+            return array(null, new Error($url, $ret));
+        }
+        return array($ret->json(), null);
+    }
+
+    private function post($url, $body)
+    {
+        $headers = $this->auth->authorization($url, $body, 'application/x-www-form-urlencoded');
+        $ret = Client::post($url, $body, $headers);
+        if (!$ret->ok()) {
+            return array(null, new Error($url, $ret));
+        }
+        $r = ($ret->body === null) ? array() : $ret->json();
+        return array($r, null);
+    }
+
+    private function ucPostV2($path, $body)
+    {
+        $url = $this->getUcHost() . $path;
+        return $this->postV2($url, $body);
+    }
+
+    private function postV2($url, $body)
+    {
+        $headers = $this->auth->authorizationV2($url, 'POST', $body, 'application/json');
+        $headers["Content-Type"] = 'application/json';
+        $ret = Client::post($url, $body, $headers);
+        if (!$ret->ok()) {
+            return array(null, new Error($url, $ret));
+        }
+        $r = ($ret->body === null) ? array() : $ret->json();
+        return array($r, null);
+    }
+
+    public static function buildBatchCopy($source_bucket, $key_pairs, $target_bucket, $force)
+    {
+        return self::twoKeyBatch('/copy', $source_bucket, $key_pairs, $target_bucket, $force);
+    }
+
+
+    public static function buildBatchRename($bucket, $key_pairs, $force)
+    {
+        return self::buildBatchMove($bucket, $key_pairs, $bucket, $force);
+    }
+
+
+    public static function buildBatchMove($source_bucket, $key_pairs, $target_bucket, $force)
+    {
+        return self::twoKeyBatch('/move', $source_bucket, $key_pairs, $target_bucket, $force);
+    }
+
+
+    public static function buildBatchDelete($bucket, $keys)
+    {
+        return self::oneKeyBatch('/delete', $bucket, $keys);
+    }
+
+
+    public static function buildBatchStat($bucket, $keys)
+    {
+        return self::oneKeyBatch('/stat', $bucket, $keys);
+    }
+
+    public static function buildBatchDeleteAfterDays($bucket, $key_day_pairs)
+    {
+        $data = array();
+        foreach ($key_day_pairs as $key => $day) {
+            array_push($data, '/deleteAfterDays/' . \Qiniu\entry($bucket, $key) . '/' . $day);
+        }
+        return $data;
+    }
+
+    public static function buildBatchChangeMime($bucket, $key_mime_pairs)
+    {
+        $data = array();
+        foreach ($key_mime_pairs as $key => $mime) {
+            array_push($data, '/chgm/' . \Qiniu\entry($bucket, $key) . '/mime/' . base64_encode($mime));
+        }
+        return $data;
+    }
+
+    public static function buildBatchChangeType($bucket, $key_type_pairs)
+    {
+        $data = array();
+        foreach ($key_type_pairs as $key => $type) {
+            array_push($data, '/chtype/' . \Qiniu\entry($bucket, $key) . '/type/' . $type);
+        }
+        return $data;
+    }
+
+    private static function oneKeyBatch($operation, $bucket, $keys)
+    {
+        $data = array();
+        foreach ($keys as $key) {
+            array_push($data, $operation . '/' . \Qiniu\entry($bucket, $key));
+        }
+        return $data;
+    }
+
+    private static function twoKeyBatch($operation, $source_bucket, $key_pairs, $target_bucket, $force)
+    {
+        if ($target_bucket === null) {
+            $target_bucket = $source_bucket;
+        }
+        $data = array();
+        $forceOp = "false";
+        if ($force) {
+            $forceOp = "true";
+        }
+        foreach ($key_pairs as $from_key => $to_key) {
+            $from = \Qiniu\entry($source_bucket, $from_key);
+            $to = \Qiniu\entry($target_bucket, $to_key);
+            array_push($data, $operation . '/' . $from . '/' . $to . "/force/" . $forceOp);
+        }
+        return $data;
+    }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Storage/FormUploader.php b/vendor/qiniu/php-sdk/src/Qiniu/Storage/FormUploader.php
new file mode 100755
index 0000000..d87756a
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Storage/FormUploader.php
@@ -0,0 +1,137 @@
+<?php
+namespace Qiniu\Storage;
+
+use Qiniu\Http\Client;
+use Qiniu\Http\Error;
+
+final class FormUploader
+{
+
+    /**
+     * 上传二进制流到七牛, 内部使用
+     *
+     * @param $upToken    上传凭证
+     * @param $key        上传文件名
+     * @param $data       上传二进制流
+     * @param $config     上传配置
+     * @param $params     自定义变量,规格参考
+     *                    http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar
+     * @param $mime       上传数据的mimeType
+     *
+     * @return array    包含已上传文件的信息,类似:
+     *                                              [
+     *                                                  "hash" => "<Hash string>",
+     *                                                  "key" => "<Key string>"
+     *                                              ]
+     */
+    public static function put(
+        $upToken,
+        $key,
+        $data,
+        $config,
+        $params,
+        $mime,
+        $fname
+    ) {
+        $fields = array('token' => $upToken);
+        if ($key === null) {
+        } else {
+            $fields['key'] = $key;
+        }
+
+        //enable crc32 check by default
+        $fields['crc32'] = \Qiniu\crc32_data($data);
+
+        if ($params) {
+            foreach ($params as $k => $v) {
+                $fields[$k] = $v;
+            }
+        }
+
+        list($accessKey, $bucket, $err) = \Qiniu\explodeUpToken($upToken);
+        if ($err != null) {
+            return array(null, $err);
+        }
+
+        $upHost = $config->getUpHost($accessKey, $bucket);
+
+        $response = Client::multipartPost($upHost, $fields, 'file', $fname, $data, $mime);
+        if (!$response->ok()) {
+            return array(null, new Error($upHost, $response));
+        }
+        return array($response->json(), null);
+    }
+
+    /**
+     * 上传文件到七牛,内部使用
+     *
+     * @param $upToken    上传凭证
+     * @param $key        上传文件名
+     * @param $filePath   上传文件的路径
+     * @param $config     上传配置
+     * @param $params     自定义变量,规格参考
+     *                    http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar
+     * @param $mime       上传数据的mimeType
+     *
+     * @return array    包含已上传文件的信息,类似:
+     *                                              [
+     *                                                  "hash" => "<Hash string>",
+     *                                                  "key" => "<Key string>"
+     *                                              ]
+     */
+    public static function putFile(
+        $upToken,
+        $key,
+        $filePath,
+        $config,
+        $params,
+        $mime
+    ) {
+
+
+        $fields = array('token' => $upToken, 'file' => self::createFile($filePath, $mime));
+        if ($key !== null) {
+            $fields['key'] = $key;
+        }
+
+        $fields['crc32'] = \Qiniu\crc32_file($filePath);
+
+        if ($params) {
+            foreach ($params as $k => $v) {
+                $fields[$k] = $v;
+            }
+        }
+        $fields['key'] = $key;
+        $headers = array('Content-Type' => 'multipart/form-data');
+
+        list($accessKey, $bucket, $err) = \Qiniu\explodeUpToken($upToken);
+        if ($err != null) {
+            return array(null, $err);
+        }
+
+        $upHost = $config->getUpHost($accessKey, $bucket);
+
+        $response = Client::post($upHost, $fields, $headers);
+        if (!$response->ok()) {
+            return array(null, new Error($upHost, $response));
+        }
+        return array($response->json(), null);
+    }
+
+    private static function createFile($filename, $mime)
+    {
+        // PHP 5.5 introduced a CurlFile object that deprecates the old @filename syntax
+        // See: https://wiki.php.net/rfc/curl-file-upload
+        if (function_exists('curl_file_create')) {
+            return curl_file_create($filename, $mime);
+        }
+
+        // Use the old style if using an older version of PHP
+        $value = "@{$filename}";
+        if (!empty($mime)) {
+            $value .= ';type=' . $mime;
+        }
+
+        return $value;
+    }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Storage/ResumeUploader.php b/vendor/qiniu/php-sdk/src/Qiniu/Storage/ResumeUploader.php
new file mode 100755
index 0000000..5d8bf87
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Storage/ResumeUploader.php
@@ -0,0 +1,169 @@
+<?php
+namespace Qiniu\Storage;
+
+use Qiniu\Config;
+use Qiniu\Http\Client;
+use Qiniu\Http\Error;
+
+/**
+ * 断点续上传类, 该类主要实现了断点续上传中的分块上传,
+ * 以及相应地创建块和创建文件过程.
+ *
+ * @link http://developer.qiniu.com/docs/v6/api/reference/up/mkblk.html
+ * @link http://developer.qiniu.com/docs/v6/api/reference/up/mkfile.html
+ */
+final class ResumeUploader
+{
+    private $upToken;
+    private $key;
+    private $inputStream;
+    private $size;
+    private $params;
+    private $mime;
+    private $contexts;
+    private $host;
+    private $currentUrl;
+    private $config;
+
+    /**
+     * 上传二进制流到七牛
+     *
+     * @param $upToken    上传凭证
+     * @param $key        上传文件名
+     * @param $inputStream 上传二进制流
+     * @param $size       上传流的大小
+     * @param $params     自定义变量
+     * @param $mime       上传数据的mimeType
+     *
+     * @link http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar
+     */
+    public function __construct(
+        $upToken,
+        $key,
+        $inputStream,
+        $size,
+        $params,
+        $mime,
+        $config
+    ) {
+
+        $this->upToken = $upToken;
+        $this->key = $key;
+        $this->inputStream = $inputStream;
+        $this->size = $size;
+        $this->params = $params;
+        $this->mime = $mime;
+        $this->contexts = array();
+        $this->config = $config;
+
+        list($accessKey, $bucket, $err) = \Qiniu\explodeUpToken($upToken);
+        if ($err != null) {
+            return array(null, $err);
+        }
+
+        $upHost = $config->getUpHost($accessKey, $bucket);
+        if ($err != null) {
+            throw new \Exception($err->message(), 1);
+        }
+        $this->host = $upHost;
+    }
+
+    /**
+     * 上传操作
+     */
+    public function upload($fname)
+    {
+        $uploaded = 0;
+        while ($uploaded < $this->size) {
+            $blockSize = $this->blockSize($uploaded);
+            $data = fread($this->inputStream, $blockSize);
+            if ($data === false) {
+                throw new \Exception("file read failed", 1);
+            }
+            $crc = \Qiniu\crc32_data($data);
+            $response = $this->makeBlock($data, $blockSize);
+            $ret = null;
+            if ($response->ok() && $response->json() != null) {
+                $ret = $response->json();
+            }
+            if ($response->statusCode < 0) {
+                list($accessKey, $bucket, $err) = \Qiniu\explodeUpToken($this->upToken);
+                if ($err != null) {
+                    return array(null, $err);
+                }
+
+                $upHostBackup = $this->config->getUpBackupHost($accessKey, $bucket);
+                $this->host = $upHostBackup;
+            }
+            if ($response->needRetry() || !isset($ret['crc32']) || $crc != $ret['crc32']) {
+                $response = $this->makeBlock($data, $blockSize);
+                $ret = $response->json();
+            }
+
+            if (!$response->ok() || !isset($ret['crc32']) || $crc != $ret['crc32']) {
+                return array(null, new Error($this->currentUrl, $response));
+            }
+            array_push($this->contexts, $ret['ctx']);
+            $uploaded += $blockSize;
+        }
+        return $this->makeFile($fname);
+    }
+
+    /**
+     * 创建块
+     */
+    private function makeBlock($block, $blockSize)
+    {
+        $url = $this->host . '/mkblk/' . $blockSize;
+        return $this->post($url, $block);
+    }
+
+    private function fileUrl($fname)
+    {
+        $url = $this->host . '/mkfile/' . $this->size;
+        $url .= '/mimeType/' . \Qiniu\base64_urlSafeEncode($this->mime);
+        if ($this->key != null) {
+            $url .= '/key/' . \Qiniu\base64_urlSafeEncode($this->key);
+        }
+        $url .= '/fname/' . \Qiniu\base64_urlSafeEncode($fname);
+        if (!empty($this->params)) {
+            foreach ($this->params as $key => $value) {
+                $val = \Qiniu\base64_urlSafeEncode($value);
+                $url .= "/$key/$val";
+            }
+        }
+        return $url;
+    }
+
+    /**
+     * 创建文件
+     */
+    private function makeFile($fname)
+    {
+        $url = $this->fileUrl($fname);
+        $body = implode(',', $this->contexts);
+        $response = $this->post($url, $body);
+        if ($response->needRetry()) {
+            $response = $this->post($url, $body);
+        }
+        if (!$response->ok()) {
+            return array(null, new Error($this->currentUrl, $response));
+        }
+        return array($response->json(), null);
+    }
+
+    private function post($url, $data)
+    {
+        $this->currentUrl = $url;
+        $headers = array('Authorization' => 'UpToken ' . $this->upToken);
+        return Client::post($url, $data, $headers);
+    }
+
+    private function blockSize($uploaded)
+    {
+        if ($this->size < $uploaded + Config::BLOCK_SIZE) {
+            return $this->size - $uploaded;
+        }
+        return Config::BLOCK_SIZE;
+    }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Storage/UploadManager.php b/vendor/qiniu/php-sdk/src/Qiniu/Storage/UploadManager.php
new file mode 100755
index 0000000..209df11
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Storage/UploadManager.php
@@ -0,0 +1,144 @@
+<?php
+namespace Qiniu\Storage;
+
+use Qiniu\Config;
+use Qiniu\Http\HttpClient;
+use Qiniu\Storage\ResumeUploader;
+use Qiniu\Storage\FormUploader;
+
+/**
+ * 主要涉及了资源上传接口的实现
+ *
+ * @link http://developer.qiniu.com/docs/v6/api/reference/up/
+ */
+final class UploadManager
+{
+    private $config;
+
+    public function __construct(Config $config = null)
+    {
+        if ($config === null) {
+            $config = new Config();
+        }
+        $this->config = $config;
+    }
+
+    /**
+     * 上传二进制流到七牛
+     *
+     * @param $upToken    上传凭证
+     * @param $key        上传文件名
+     * @param $data       上传二进制流
+     * @param $params     自定义变量,规格参考
+     *                    http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar
+     * @param $mime       上传数据的mimeType
+     * @param $checkCrc   是否校验crc32
+     *
+     * @return array    包含已上传文件的信息,类似:
+     *                                              [
+     *                                                  "hash" => "<Hash string>",
+     *                                                  "key" => "<Key string>"
+     *                                              ]
+     */
+    public function put(
+        $upToken,
+        $key,
+        $data,
+        $params = null,
+        $mime = 'application/octet-stream',
+        $fname = null
+    ) {
+    
+        $params = self::trimParams($params);
+        return FormUploader::put(
+            $upToken,
+            $key,
+            $data,
+            $this->config,
+            $params,
+            $mime,
+            $fname
+        );
+    }
+
+
+    /**
+     * 上传文件到七牛
+     *
+     * @param $upToken    上传凭证
+     * @param $key        上传文件名
+     * @param $filePath   上传文件的路径
+     * @param $params     自定义变量,规格参考
+     *                    http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar
+     * @param $mime       上传数据的mimeType
+     * @param $checkCrc   是否校验crc32
+     *
+     * @return array    包含已上传文件的信息,类似:
+     *                                              [
+     *                                                  "hash" => "<Hash string>",
+     *                                                  "key" => "<Key string>"
+     *                                              ]
+     */
+    public function putFile(
+        $upToken,
+        $key,
+        $filePath,
+        $params = null,
+        $mime = 'application/octet-stream',
+        $checkCrc = false
+    ) {
+    
+        $file = fopen($filePath, 'rb');
+        if ($file === false) {
+            throw new \Exception("file can not open", 1);
+        }
+        $params = self::trimParams($params);
+        $stat = fstat($file);
+        $size = $stat['size'];
+        if ($size <= Config::BLOCK_SIZE) {
+            $data = fread($file, $size);
+            fclose($file);
+            if ($data === false) {
+                throw new \Exception("file can not read", 1);
+            }
+            return FormUploader::put(
+                $upToken,
+                $key,
+                $data,
+                $this->config,
+                $params,
+                $mime,
+                basename($filePath)
+            );
+        }
+
+        $up = new ResumeUploader(
+            $upToken,
+            $key,
+            $file,
+            $size,
+            $params,
+            $mime,
+            $this->config
+        );
+        $ret = $up->upload(basename($filePath));
+        fclose($file);
+        return $ret;
+    }
+
+    public static function trimParams($params)
+    {
+        if ($params === null) {
+            return null;
+        }
+        $ret = array();
+        foreach ($params as $k => $v) {
+            $pos1 = strpos($k, 'x:');
+            $pos2 = strpos($k, 'x-qn-meta-');
+            if (($pos1 === 0 || $pos2 === 0) && !empty($v)) {
+                $ret[$k] = $v;
+            }
+        }
+        return $ret;
+    }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Zone.php b/vendor/qiniu/php-sdk/src/Qiniu/Zone.php
new file mode 100755
index 0000000..7af0e3f
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Zone.php
@@ -0,0 +1,47 @@
+<?php
+namespace Qiniu;
+
+use Qiniu\Region;
+
+class Zone extends Region
+{
+    public static function zonez0()
+    {
+        return parent::regionHuadong();
+    }
+
+    public static function zonez1()
+    {
+        return parent::regionHuabei();
+    }
+
+    public static function zonez2()
+    {
+        return parent::regionHuanan();
+    }
+
+    public static function zoneAs0()
+    {
+        return parent::regionSingapore();
+    }
+
+    public static function zoneNa0()
+    {
+        return parent::regionNorthAmerica();
+    }
+
+    public static function qvmZonez0()
+    {
+        return parent::qvmRegionHuadong();
+    }
+
+    public static function qvmZonez1()
+    {
+        return parent::qvmRegionHuabei();
+    }
+
+    public static function queryZone($ak, $bucket)
+    {
+        return parent::queryRegion($ak, $bucket);
+    }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/functions.php b/vendor/qiniu/php-sdk/src/Qiniu/functions.php
new file mode 100755
index 0000000..5831a51
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/functions.php
@@ -0,0 +1,264 @@
+<?php
+
+namespace Qiniu;
+
+use Qiniu\Config;
+
+if (!defined('QINIU_FUNCTIONS_VERSION')) {
+    define('QINIU_FUNCTIONS_VERSION', Config::SDK_VER);
+
+    /**
+     * 计算文件的crc32检验码:
+     *
+     * @param $file string  待计算校验码的文件路径
+     *
+     * @return string 文件内容的crc32校验码
+     */
+    function crc32_file($file)
+    {
+        $hash = hash_file('crc32b', $file);
+        $array = unpack('N', pack('H*', $hash));
+        return sprintf('%u', $array[1]);
+    }
+
+    /**
+     * 计算输入流的crc32检验码
+     *
+     * @param $data 待计算校验码的字符串
+     *
+     * @return string 输入字符串的crc32校验码
+     */
+    function crc32_data($data)
+    {
+        $hash = hash('crc32b', $data);
+        $array = unpack('N', pack('H*', $hash));
+        return sprintf('%u', $array[1]);
+    }
+
+    /**
+     * 对提供的数据进行urlsafe的base64编码。
+     *
+     * @param string $data 待编码的数据,一般为字符串
+     *
+     * @return string 编码后的字符串
+     * @link http://developer.qiniu.com/docs/v6/api/overview/appendix.html#urlsafe-base64
+     */
+    function base64_urlSafeEncode($data)
+    {
+        $find = array('+', '/');
+        $replace = array('-', '_');
+        return str_replace($find, $replace, base64_encode($data));
+    }
+
+    /**
+     * 对提供的urlsafe的base64编码的数据进行解码
+     *
+     * @param string $str 待解码的数据,一般为字符串
+     *
+     * @return string 解码后的字符串
+     */
+    function base64_urlSafeDecode($str)
+    {
+        $find = array('-', '_');
+        $replace = array('+', '/');
+        return base64_decode(str_replace($find, $replace, $str));
+    }
+
+    /**
+     * Wrapper for JSON decode that implements error detection with helpful
+     * error messages.
+     *
+     * @param string $json JSON data to parse
+     * @param bool $assoc When true, returned objects will be converted
+     *                        into associative arrays.
+     * @param int $depth User specified recursion depth.
+     *
+     * @return mixed
+     * @throws \InvalidArgumentException if the JSON cannot be parsed.
+     * @link http://www.php.net/manual/en/function.json-decode.php
+     */
+    function json_decode($json, $assoc = false, $depth = 512)
+    {
+        static $jsonErrors = array(
+            JSON_ERROR_DEPTH => 'JSON_ERROR_DEPTH - Maximum stack depth exceeded',
+            JSON_ERROR_STATE_MISMATCH => 'JSON_ERROR_STATE_MISMATCH - Underflow or the modes mismatch',
+            JSON_ERROR_CTRL_CHAR => 'JSON_ERROR_CTRL_CHAR - Unexpected control character found',
+            JSON_ERROR_SYNTAX => 'JSON_ERROR_SYNTAX - Syntax error, malformed JSON',
+            JSON_ERROR_UTF8 => 'JSON_ERROR_UTF8 - Malformed UTF-8 characters, possibly incorrectly encoded'
+        );
+
+        if (empty($json)) {
+            return null;
+        }
+        $data = \json_decode($json, $assoc, $depth);
+
+        if (JSON_ERROR_NONE !== json_last_error()) {
+            $last = json_last_error();
+            throw new \InvalidArgumentException(
+                'Unable to parse JSON data: '
+                . (isset($jsonErrors[$last])
+                    ? $jsonErrors[$last]
+                    : 'Unknown error')
+            );
+        }
+
+        return $data;
+    }
+
+    /**
+     * 计算七牛API中的数据格式
+     *
+     * @param $bucket 待操作的空间名
+     * @param $key 待操作的文件名
+     *
+     * @return string  符合七牛API规格的数据格式
+     * @link http://developer.qiniu.com/docs/v6/api/reference/data-formats.html
+     */
+    function entry($bucket, $key)
+    {
+        $en = $bucket;
+        if (!empty($key)) {
+            $en = $bucket . ':' . $key;
+        }
+        return base64_urlSafeEncode($en);
+    }
+
+    /**
+     * array 辅助方法,无值时不set
+     *
+     * @param $array 待操作array
+     * @param $key key
+     * @param $value value 为null时 不设置
+     *
+     * @return array 原来的array,便于连续操作
+     */
+    function setWithoutEmpty(&$array, $key, $value)
+    {
+        if (!empty($value)) {
+            $array[$key] = $value;
+        }
+        return $array;
+    }
+
+    /**
+     * 缩略图链接拼接
+     *
+     * @param  string $url 图片链接
+     * @param  int $mode 缩略模式
+     * @param  int $width 宽度
+     * @param  int $height 长度
+     * @param  string $format 输出类型
+     * @param  int $quality 图片质量
+     * @param  int $interlace 是否支持渐进显示
+     * @param  int $ignoreError 忽略结果
+     * @return string
+     * @link http://developer.qiniu.com/code/v6/api/kodo-api/image/imageview2.html
+     * @author Sherlock Ren <sherlock_ren@icloud.com>
+     */
+    function thumbnail(
+        $url,
+        $mode,
+        $width,
+        $height,
+        $format = null,
+        $quality = null,
+        $interlace = null,
+        $ignoreError = 1
+    ) {
+
+        static $imageUrlBuilder = null;
+        if (is_null($imageUrlBuilder)) {
+            $imageUrlBuilder = new \Qiniu\Processing\ImageUrlBuilder;
+        }
+
+        return call_user_func_array(array($imageUrlBuilder, 'thumbnail'), func_get_args());
+    }
+
+    /**
+     * 图片水印
+     *
+     * @param  string $url 图片链接
+     * @param  string $image 水印图片链接
+     * @param  numeric $dissolve 透明度
+     * @param  string $gravity 水印位置
+     * @param  numeric $dx 横轴边距
+     * @param  numeric $dy 纵轴边距
+     * @param  numeric $watermarkScale 自适应原图的短边比例
+     * @link   http://developer.qiniu.com/code/v6/api/kodo-api/image/watermark.html
+     * @return string
+     * @author Sherlock Ren <sherlock_ren@icloud.com>
+     */
+    function waterImg(
+        $url,
+        $image,
+        $dissolve = 100,
+        $gravity = 'SouthEast',
+        $dx = null,
+        $dy = null,
+        $watermarkScale = null
+    ) {
+
+        static $imageUrlBuilder = null;
+        if (is_null($imageUrlBuilder)) {
+            $imageUrlBuilder = new \Qiniu\Processing\ImageUrlBuilder;
+        }
+
+        return call_user_func_array(array($imageUrlBuilder, 'waterImg'), func_get_args());
+    }
+
+    /**
+     * 文字水印
+     *
+     * @param  string $url 图片链接
+     * @param  string $text 文字
+     * @param  string $font 文字字体
+     * @param  string $fontSize 文字字号
+     * @param  string $fontColor 文字颜色
+     * @param  numeric $dissolve 透明度
+     * @param  string $gravity 水印位置
+     * @param  numeric $dx 横轴边距
+     * @param  numeric $dy 纵轴边距
+     * @link   http://developer.qiniu.com/code/v6/api/kodo-api/image/watermark.html#text-watermark
+     * @return string
+     * @author Sherlock Ren <sherlock_ren@icloud.com>
+     */
+    function waterText(
+        $url,
+        $text,
+        $font = '黑体',
+        $fontSize = 0,
+        $fontColor = null,
+        $dissolve = 100,
+        $gravity = 'SouthEast',
+        $dx = null,
+        $dy = null
+    ) {
+
+        static $imageUrlBuilder = null;
+        if (is_null($imageUrlBuilder)) {
+            $imageUrlBuilder = new \Qiniu\Processing\ImageUrlBuilder;
+        }
+
+        return call_user_func_array(array($imageUrlBuilder, 'waterText'), func_get_args());
+    }
+
+    /**
+     *  从uptoken解析accessKey和bucket
+     *
+     * @param $upToken
+     * @return array(ak,bucket,err=null)
+     */
+    function explodeUpToken($upToken)
+    {
+        $items = explode(':', $upToken);
+        if (count($items) != 3) {
+            return array(null, null, "invalid uptoken");
+        }
+        $accessKey = $items[0];
+        $putPolicy = json_decode(base64_urlSafeDecode($items[2]));
+        $scope = $putPolicy->scope;
+        $scopeItems = explode(':', $scope);
+        $bucket = $scopeItems[0];
+        return array($accessKey, $bucket, null);
+    }
+}
diff --git a/vendor/qiniu/php-sdk/test-env.sh b/vendor/qiniu/php-sdk/test-env.sh
new file mode 100755
index 0000000..eedf6b5
--- /dev/null
+++ b/vendor/qiniu/php-sdk/test-env.sh
@@ -0,0 +1,4 @@
+export QINIU_ACCESS_KEY=xxx
+export QINIU_SECRET_KEY=xxx
+export QINIU_TEST_BUCKET=phpsdk
+export QINIU_TEST_DOMAIN=phpsdk.qiniudn.com
\ No newline at end of file
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/AuthTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/AuthTest.php
new file mode 100755
index 0000000..712cbd5
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/AuthTest.php
@@ -0,0 +1,71 @@
+<?php
+// Hack to override the time returned from the S3SignatureV4
+// @codingStandardsIgnoreStart
+namespace Qiniu {
+    function time()
+    {
+        return isset($_SERVER['override_qiniu_auth_time'])
+            ? 1234567890
+            : \time();
+    }
+}
+
+namespace Qiniu\Tests {
+    use Qiniu\Auth;
+
+    // @codingStandardsIgnoreEnd
+
+    class AuthTest extends \PHPUnit_Framework_TestCase
+    {
+
+        public function testSign()
+        {
+            global $dummyAuth;
+            $token = $dummyAuth->sign('test');
+            $this->assertEquals('abcdefghklmnopq:mSNBTR7uS2crJsyFr2Amwv1LaYg=', $token);
+        }
+
+        public function testSignWithData()
+        {
+            global $dummyAuth;
+            $token = $dummyAuth->signWithData('test');
+            $this->assertEquals('abcdefghklmnopq:-jP8eEV9v48MkYiBGs81aDxl60E=:dGVzdA==', $token);
+        }
+
+        public function testSignRequest()
+        {
+            global $dummyAuth;
+            $token = $dummyAuth->signRequest('http://www.qiniu.com?go=1', 'test', '');
+            $this->assertEquals('abcdefghklmnopq:cFyRVoWrE3IugPIMP5YJFTO-O-Y=', $token);
+            $ctype = 'application/x-www-form-urlencoded';
+            $token = $dummyAuth->signRequest('http://www.qiniu.com?go=1', 'test', $ctype);
+            $this->assertEquals($token, 'abcdefghklmnopq:svWRNcacOE-YMsc70nuIYdaa1e4=');
+        }
+
+        public function testPrivateDownloadUrl()
+        {
+            global $dummyAuth;
+            $_SERVER['override_qiniu_auth_time'] = true;
+            $url = $dummyAuth->privateDownloadUrl('http://www.qiniu.com?go=1');
+            $expect = 'http://www.qiniu.com?go=1&e=1234571490&token=abcdefghklmnopq:8vzBeLZ9W3E4kbBLFLW0Xe0u7v4=';
+            $this->assertEquals($expect, $url);
+            unset($_SERVER['override_qiniu_auth_time']);
+        }
+
+        public function testUploadToken()
+        {
+            global $dummyAuth;
+            $_SERVER['override_qiniu_auth_time'] = true;
+            $token = $dummyAuth->uploadToken('1', '2', 3600, array('endUser' => 'y'));
+            // @codingStandardsIgnoreStart
+            $exp = 'abcdefghklmnopq:yyeexeUkPOROoTGvwBjJ0F0VLEo=:eyJlbmRVc2VyIjoieSIsInNjb3BlIjoiMToyIiwiZGVhZGxpbmUiOjEyMzQ1NzE0OTB9';
+            // @codingStandardsIgnoreEnd
+            $this->assertEquals($exp, $token);
+            unset($_SERVER['override_qiniu_auth_time']);
+        }
+
+        public function testVerifyCallback()
+        {
+        }
+    }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/Base64Test.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/Base64Test.php
new file mode 100755
index 0000000..6d63353
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/Base64Test.php
@@ -0,0 +1,14 @@
+<?php
+namespace Qiniu\Tests;
+
+use Qiniu;
+
+class Base64Test extends \PHPUnit_Framework_TestCase
+{
+    public function testUrlSafe()
+    {
+        $a = '你好';
+        $b = \Qiniu\base64_urlSafeEncode($a);
+        $this->assertEquals($a, \Qiniu\base64_urlSafeDecode($b));
+    }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/BucketTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/BucketTest.php
new file mode 100755
index 0000000..534d8c2
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/BucketTest.php
@@ -0,0 +1,227 @@
+<?php
+namespace Qiniu\Tests;
+
+use Qiniu\Storage\BucketManager;
+
+class BucketTest extends \PHPUnit_Framework_TestCase
+{
+    protected $bucketManager;
+    protected $dummyBucketManager;
+    protected $bucketName;
+    protected $key;
+    protected $key2;
+
+    protected function setUp()
+    {
+        global $bucketName;
+        global $key;
+        global $key2;
+        $this->bucketName = $bucketName;
+        $this->key = $key;
+        $this->key2 = $key2;
+
+        global $testAuth;
+        $this->bucketManager = new BucketManager($testAuth);
+
+        global $dummyAuth;
+        $this->dummyBucketManager = new BucketManager($dummyAuth);
+    }
+
+    public function testBuckets()
+    {
+
+        list($list, $error) = $this->bucketManager->buckets();
+        $this->assertTrue(in_array($this->bucketName, $list));
+        $this->assertNull($error);
+
+        list($list2, $error) = $this->dummyBucketManager->buckets();
+        $this->assertEquals(401, $error->code());
+        $this->assertNull($list2);
+        $this->assertNotNull($error->message());
+    }
+
+    public function testList()
+    {
+        list($ret, $error) = $this->bucketManager->listFiles($this->bucketName, null, null, 10);
+        $this->assertNotNull($ret['items'][0]);
+        $this->assertNotNull($ret['marker']);
+    }
+
+    public function testStat()
+    {
+        list($stat, $error) = $this->bucketManager->stat($this->bucketName, $this->key);
+        $this->assertNotNull($stat);
+        $this->assertNull($error);
+        $this->assertNotNull($stat['hash']);
+
+        list($stat, $error) = $this->bucketManager->stat($this->bucketName, 'nofile');
+        $this->assertNull($stat);
+        $this->assertEquals(612, $error->code());
+        $this->assertNotNull($error->message());
+
+        list($stat, $error) = $this->bucketManager->stat('nobucket', 'nofile');
+        $this->assertNull($stat);
+        $this->assertEquals(631, $error->code());
+        $this->assertNotNull($error->message());
+    }
+
+    public function testDelete()
+    {
+        $error = $this->bucketManager->delete($this->bucketName, 'del');
+        $this->assertEquals(612, $error->code());
+    }
+
+
+    public function testRename()
+    {
+        $key = 'renamefrom' . rand();
+        $this->bucketManager->copy($this->bucketName, $this->key, $this->bucketName, $key);
+        $key2 = 'renameto' . $key;
+        $error = $this->bucketManager->rename($this->bucketName, $key, $key2);
+        $this->assertNull($error);
+        $error = $this->bucketManager->delete($this->bucketName, $key2);
+        $this->assertNull($error);
+    }
+
+
+    public function testCopy()
+    {
+        $key = 'copyto' . rand();
+        $this->bucketManager->delete($this->bucketName, $key);
+
+        $error = $this->bucketManager->copy(
+            $this->bucketName,
+            $this->key,
+            $this->bucketName,
+            $key
+        );
+        $this->assertNull($error);
+
+        //test force copy
+        $error = $this->bucketManager->copy(
+            $this->bucketName,
+            $this->key2,
+            $this->bucketName,
+            $key,
+            true
+        );
+        $this->assertNull($error);
+
+        list($key2Stat,) = $this->bucketManager->stat($this->bucketName, $this->key2);
+        list($key2CopiedStat,) = $this->bucketManager->stat($this->bucketName, $key);
+
+        $this->assertEquals($key2Stat['hash'], $key2CopiedStat['hash']);
+
+        $error = $this->bucketManager->delete($this->bucketName, $key);
+        $this->assertNull($error);
+    }
+
+
+    public function testChangeMime()
+    {
+        $error = $this->bucketManager->changeMime(
+            $this->bucketName,
+            'php-sdk.html',
+            'text/html'
+        );
+        $this->assertNull($error);
+    }
+
+    public function testPrefetch()
+    {
+        $error = $this->bucketManager->prefetch(
+            $this->bucketName,
+            'php-sdk.html'
+        );
+        $this->assertNull($error);
+    }
+
+    public function testFetch()
+    {
+        list($ret, $error) = $this->bucketManager->fetch(
+            'http://developer.qiniu.com/docs/v6/sdk/php-sdk.html',
+            $this->bucketName,
+            'fetch.html'
+        );
+        $this->assertArrayHasKey('hash', $ret);
+        $this->assertNull($error);
+
+        list($ret, $error) = $this->bucketManager->fetch(
+            'http://developer.qiniu.com/docs/v6/sdk/php-sdk.html',
+            $this->bucketName,
+            ''
+        );
+        $this->assertArrayHasKey('key', $ret);
+        $this->assertNull($error);
+
+        list($ret, $error) = $this->bucketManager->fetch(
+            'http://developer.qiniu.com/docs/v6/sdk/php-sdk.html',
+            $this->bucketName
+        );
+        $this->assertArrayHasKey('key', $ret);
+        $this->assertNull($error);
+    }
+
+    public function testBatchCopy()
+    {
+        $key = 'copyto' . rand();
+        $ops = BucketManager::buildBatchCopy(
+            $this->bucketName,
+            array($this->key => $key),
+            $this->bucketName,
+            true
+        );
+        list($ret, $error) = $this->bucketManager->batch($ops);
+        $this->assertEquals(200, $ret[0]['code']);
+        $ops = BucketManager::buildBatchDelete($this->bucketName, array($key));
+        list($ret, $error) = $this->bucketManager->batch($ops);
+        $this->assertEquals(200, $ret[0]['code']);
+    }
+
+    public function testBatchMove()
+    {
+        $key = 'movefrom' . rand();
+        $this->bucketManager->copy($this->bucketName, $this->key, $this->bucketName, $key);
+        $key2 = $key . 'to';
+        $ops = BucketManager::buildBatchMove(
+            $this->bucketName,
+            array($key => $key2),
+            $this->bucketName,
+            true
+        );
+        list($ret, $error) = $this->bucketManager->batch($ops);
+        $this->assertEquals(200, $ret[0]['code']);
+        $error = $this->bucketManager->delete($this->bucketName, $key2);
+        $this->assertNull($error);
+    }
+
+    public function testBatchRename()
+    {
+        $key = 'rename' . rand();
+        $this->bucketManager->copy($this->bucketName, $this->key, $this->bucketName, $key);
+        $key2 = $key . 'to';
+        $ops = BucketManager::buildBatchRename($this->bucketName, array($key => $key2), true);
+        list($ret, $error) = $this->bucketManager->batch($ops);
+        $this->assertEquals(200, $ret[0]['code']);
+        $error = $this->bucketManager->delete($this->bucketName, $key2);
+        $this->assertNull($error);
+    }
+
+    public function testBatchStat()
+    {
+        $ops = BucketManager::buildBatchStat($this->bucketName, array('php-sdk.html'));
+        list($ret, $error) = $this->bucketManager->batch($ops);
+        $this->assertEquals(200, $ret[0]['code']);
+    }
+
+    public function testDeleteAfterDays()
+    {
+        $key = rand();
+        $err = $this->bucketManager->deleteAfterDays($this->bucketName, $key, 1);
+        $this->assertEquals(612, $err->code());
+
+        $this->bucketManager->copy($this->bucketName, $this->key, $this->bucketName, $key);
+        $err = $this->bucketManager->deleteAfterDays($this->bucketName, $key, 1);
+        $this->assertEquals(null, $err);
+    }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/CdnManagerTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/CdnManagerTest.php
new file mode 100755
index 0000000..3c45bbd
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/CdnManagerTest.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Created by IntelliJ IDEA.
+ * User: wf
+ * Date: 2017/6/21
+ * Time: AM8:46
+ */
+
+namespace Qiniu\Tests;
+
+use Qiniu\Cdn\CdnManager;
+use Qiniu\Http\Client;
+
+class CdnManagerTest extends \PHPUnit_Framework_TestCase
+{
+    protected $cdnManager;
+    protected $encryptKey;
+    protected $imgUrl;
+
+    protected function setUp()
+    {
+        global $timestampAntiLeechEncryptKey;
+        global $customDomain;
+        global $testAuth;
+
+        $this->cdnManager = new CdnManager($testAuth);
+        $this->encryptKey = $timestampAntiLeechEncryptKey;
+        $this->imgUrl = $customDomain . '/24.jpg';
+    }
+
+    public function testCreateTimestampAntiLeechUrl()
+    {
+
+        $signUrl = $this->cdnManager->createTimestampAntiLeechUrl($this->imgUrl, $this->encryptKey, 3600);
+
+        $response = Client::get($signUrl);
+        
+        $this->assertEquals($response->statusCode, 200);
+        $this->assertNull($response->error);
+    }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/Crc32Test.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/Crc32Test.php
new file mode 100755
index 0000000..bfb36da
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/Crc32Test.php
@@ -0,0 +1,21 @@
+<?php
+namespace Qiniu\Tests;
+
+use Qiniu;
+
+class Crc32Test extends \PHPUnit_Framework_TestCase
+{
+    public function testData()
+    {
+        $a = '你好';
+        $b = \Qiniu\crc32_data($a);
+        $this->assertEquals('1352841281', $b);
+    }
+
+    public function testFile()
+    {
+        $b = \Qiniu\crc32_file(__file__);
+        $c = \Qiniu\crc32_file(__file__);
+        $this->assertEquals($c, $b);
+    }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/DownloadTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/DownloadTest.php
new file mode 100755
index 0000000..82990f2
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/DownloadTest.php
@@ -0,0 +1,25 @@
+<?php
+namespace Qiniu\Tests;
+
+use Qiniu\Http\Client;
+
+class DownloadTest extends \PHPUnit_Framework_TestCase
+{
+    public function test()
+    {
+        global $testAuth;
+        $base_url = 'http://private-res.qiniudn.com/gogopher.jpg';
+        $private_url = $testAuth->privateDownloadUrl($base_url);
+        $response = Client::get($private_url);
+        $this->assertEquals(200, $response->statusCode);
+    }
+
+    public function testFop()
+    {
+        global $testAuth;
+        $base_url = 'http://private-res.qiniudn.com/gogopher.jpg?exif';
+        $private_url = $testAuth->privateDownloadUrl($base_url);
+        $response = Client::get($private_url);
+        $this->assertEquals(200, $response->statusCode);
+    }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/EtagTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/EtagTest.php
new file mode 100755
index 0000000..42a1499
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/EtagTest.php
@@ -0,0 +1,52 @@
+<?php
+namespace Qiniu\Tests;
+
+use Qiniu\Etag;
+
+class EtagTest extends \PHPUnit_Framework_TestCase
+{
+    public function test0M()
+    {
+        $file = qiniuTempFile(0);
+        list($r, $error) = Etag::sum($file);
+        unlink($file);
+        $this->assertEquals('Fto5o-5ea0sNMlW_75VgGJCv2AcJ', $r);
+        $this->assertNull($error);
+    }
+
+    public function testLess4M()
+    {
+        $file = qiniuTempFile(3 * 1024 * 1024);
+        list($r, $error) = Etag::sum($file);
+        unlink($file);
+        $this->assertEquals('Fs5BpnAjRykYTg6o5E09cjuXrDkG', $r);
+        $this->assertNull($error);
+    }
+
+    public function test4M()
+    {
+        $file = qiniuTempFile(4 * 1024 * 1024);
+        list($r, $error) = Etag::sum($file);
+        unlink($file);
+        $this->assertEquals('FiuKULnybewpEnrfTmxjsxc-3dWp', $r);
+        $this->assertNull($error);
+    }
+
+    public function testMore4M()
+    {
+        $file = qiniuTempFile(5 * 1024 * 1024);
+        list($r, $error) = Etag::sum($file);
+        unlink($file);
+        $this->assertEquals('lhvyfIWMYFTq4s4alzlhXoAkqfVL', $r);
+        $this->assertNull($error);
+    }
+
+    public function test8M()
+    {
+        $file = qiniuTempFile(8 * 1024 * 1024);
+        list($r, $error) = Etag::sum($file);
+        unlink($file);
+        $this->assertEquals('lmRm9ZfGZ86bnMys4wRTWtJj9ClG', $r);
+        $this->assertNull($error);
+    }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/FopTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/FopTest.php
new file mode 100755
index 0000000..e1ea730
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/FopTest.php
@@ -0,0 +1,37 @@
+<?php
+namespace Qiniu\Tests;
+
+use Qiniu\Processing\Operation;
+use Qiniu\Processing\PersistentFop;
+
+class FopTest extends \PHPUnit_Framework_TestCase
+{
+    public function testExifPub()
+    {
+        $fop = new Operation('testres.qiniudn.com');
+        list($exif, $error) = $fop->execute('gogopher.jpg', 'exif');
+        $this->assertNull($error);
+        $this->assertNotNull($exif);
+    }
+
+    public function testExifPrivate()
+    {
+        global $testAuth;
+        $fop = new Operation('private-res.qiniudn.com', $testAuth);
+        list($exif, $error) = $fop->execute('noexif.jpg', 'exif');
+        $this->assertNotNull($error);
+        $this->assertNull($exif);
+    }
+
+    public function testbuildUrl()
+    {
+        $fops = 'imageView2/2/h/200';
+        $fop = new Operation('testres.qiniudn.com');
+        $url = $fop->buildUrl('gogopher.jpg', $fops);
+        $this->assertEquals($url, 'http://testres.qiniudn.com/gogopher.jpg?imageView2/2/h/200');
+
+        $fops = array('imageView2/2/h/200', 'imageInfo');
+        $url = $fop->buildUrl('gogopher.jpg', $fops);
+        $this->assertEquals($url, 'http://testres.qiniudn.com/gogopher.jpg?imageView2/2/h/200|imageInfo');
+    }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/FormUpTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/FormUpTest.php
new file mode 100755
index 0000000..4813eed
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/FormUpTest.php
@@ -0,0 +1,59 @@
+<?php
+namespace Qiniu\Tests;
+
+use Qiniu\Storage\FormUploader;
+use Qiniu\Storage\UploadManager;
+use Qiniu\Config;
+
+class FormUpTest extends \PHPUnit_Framework_TestCase
+{
+    protected $bucketName;
+    protected $auth;
+    protected $cfg;
+
+    protected function setUp()
+    {
+        global $bucketName;
+        $this->bucketName = $bucketName;
+
+        global $testAuth;
+        $this->auth = $testAuth;
+        $this->cfg = new Config();
+    }
+
+    public function testData()
+    {
+        $token = $this->auth->uploadToken($this->bucketName);
+        list($ret, $error) = FormUploader::put($token, 'formput', 'hello world', $this->cfg, null, 'text/plain', null);
+        $this->assertNull($error);
+        $this->assertNotNull($ret['hash']);
+    }
+
+    public function testData2()
+    {
+        $upManager = new UploadManager();
+        $token = $this->auth->uploadToken($this->bucketName);
+        list($ret, $error) = $upManager->put($token, 'formput', 'hello world', null, 'text/plain', null);
+        $this->assertNull($error);
+        $this->assertNotNull($ret['hash']);
+    }
+
+    public function testFile()
+    {
+        $key = 'formPutFile';
+        $token = $this->auth->uploadToken($this->bucketName, $key);
+        list($ret, $error) = FormUploader::putFile($token, $key, __file__, $this->cfg, null, 'text/plain', null);
+        $this->assertNull($error);
+        $this->assertNotNull($ret['hash']);
+    }
+
+    public function testFile2()
+    {
+        $key = 'formPutFile';
+        $token = $this->auth->uploadToken($this->bucketName, $key);
+        $upManager = new UploadManager();
+        list($ret, $error) = $upManager->putFile($token, $key, __file__, null, 'text/plain', null);
+        $this->assertNull($error);
+        $this->assertNotNull($ret['hash']);
+    }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/HttpTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/HttpTest.php
new file mode 100755
index 0000000..e2ab5fc
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/HttpTest.php
@@ -0,0 +1,43 @@
+<?php
+namespace Qiniu\Tests;
+
+use Qiniu\Http\Client;
+
+class HttpTest extends \PHPUnit_Framework_TestCase
+{
+    public function testGet()
+    {
+        $response = Client::get('baidu.com');
+        $this->assertEquals($response->statusCode, 200);
+        $this->assertNotNull($response->body);
+        $this->assertNull($response->error);
+    }
+
+    public function testGetQiniu()
+    {
+        $response = Client::get('up.qiniu.com');
+        $this->assertEquals(405, $response->statusCode);
+        $this->assertNotNull($response->body);
+        $this->assertNotNull($response->xReqId());
+        $this->assertNotNull($response->xLog());
+        $this->assertNotNull($response->error);
+    }
+
+    public function testPost()
+    {
+        $response = Client::post('baidu.com', null);
+        $this->assertEquals($response->statusCode, 200);
+        $this->assertNotNull($response->body);
+        $this->assertNull($response->error);
+    }
+
+    public function testPostQiniu()
+    {
+        $response = Client::post('up.qiniu.com', null);
+        $this->assertEquals($response->statusCode, 400);
+        $this->assertNotNull($response->body);
+        $this->assertNotNull($response->xReqId());
+        $this->assertNotNull($response->xLog());
+        $this->assertNotNull($response->error);
+    }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/ImageUrlBuilderTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/ImageUrlBuilderTest.php
new file mode 100755
index 0000000..fca87b6
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/ImageUrlBuilderTest.php
@@ -0,0 +1,261 @@
+<?php
+
+namespace Qiniu\Tests;
+
+/**
+ * imageprocess test
+ *
+ * @package Qiniu
+ * @subpackage test
+ * @author Sherlock Ren <sherlock_ren@icloud.com>
+ */
+class ImageUrlBuilderTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * 缩略图测试
+     *
+     * @test
+     * @return void
+     * @author Sherlock Ren <sherlock_ren@icloud.com>
+     */
+    public function testThumbutl()
+    {
+        $imageUrlBuilder = new \Qiniu\Processing\ImageUrlBuilder();
+        $url = 'http://78re52.com1.z0.glb.clouddn.com/resource/gogopher.jpg';
+        $url2 = $url . '?watermark/1/gravity/SouthEast/dx/0/dy/0/image/'
+            . 'aHR0cDovL2Fkcy1jZG4uY2h1Y2h1amllLmNvbS9Ga1R6bnpIY2RLdmRBUFc5cHZZZ3pTc21UY0tB';
+        // 异常测试
+        $this->assertEquals($url, $imageUrlBuilder->thumbnail($url, 1, 0, 0));
+        $this->assertEquals($url, \Qiniu\thumbnail($url, 1, 0, 0));
+
+        // 简单缩略测试
+        $this->assertEquals(
+            $url . '?imageView2/1/w/200/h/200/ignore-error/1/',
+            $imageUrlBuilder->thumbnail($url, 1, 200, 200)
+        );
+        $this->assertEquals(
+            $url . '?imageView2/1/w/200/h/200/ignore-error/1/',
+            \Qiniu\thumbnail($url, 1, 200, 200)
+        );
+
+        // 输出格式测试
+        $this->assertEquals(
+            $url . '?imageView2/1/w/200/h/200/format/png/ignore-error/1/',
+            $imageUrlBuilder->thumbnail($url, 1, 200, 200, 'png')
+        );
+        $this->assertEquals(
+            $url . '?imageView2/1/w/200/h/200/format/png/ignore-error/1/',
+            \Qiniu\thumbnail($url, 1, 200, 200, 'png')
+        );
+
+        // 渐进显示测试
+        $this->assertEquals(
+            $url . '?imageView2/1/w/200/h/200/format/png/interlace/1/ignore-error/1/',
+            $imageUrlBuilder->thumbnail($url, 1, 200, 200, 'png', 1)
+        );
+        $this->assertEquals(
+            $url . '?imageView2/1/w/200/h/200/format/png/ignore-error/1/',
+            \Qiniu\thumbnail($url, 1, 200, 200, 'png', 2)
+        );
+
+        // 图片质量测试
+        $this->assertEquals(
+            $url . '?imageView2/1/w/200/h/200/format/png/interlace/1/q/80/ignore-error/1/',
+            $imageUrlBuilder->thumbnail($url, 1, 200, 200, 'png', 1, 80)
+        );
+        $this->assertEquals(
+            $url . '?imageView2/1/w/200/h/200/format/png/interlace/1/ignore-error/1/',
+            \Qiniu\thumbnail($url, 1, 200, 200, 'png', 1, 101)
+        );
+
+        // 多参数测试
+        $this->assertEquals(
+            $url2 . '|imageView2/1/w/200/h/200/ignore-error/1/',
+            $imageUrlBuilder->thumbnail($url2, 1, 200, 200)
+        );
+        $this->assertEquals(
+            $url2 . '|imageView2/1/w/200/h/200/ignore-error/1/',
+            \Qiniu\thumbnail($url2, 1, 200, 200)
+        );
+    }
+
+    /**
+     * 图片水印测试
+     *
+     * @test
+     * @param  void
+     * @return void
+     * @author Sherlock Ren <sherlock_ren@icloud.com>
+     */
+    public function waterImgTest()
+    {
+        $imageUrlBuilder = new \Qiniu\Processing\ImageUrlBuilder();
+        $url = 'http://78re52.com1.z0.glb.clouddn.com/resource/gogopher.jpg';
+        $url2 = $url . '?imageView2/1/w/200/h/200/format/png/ignore-error/1/';
+        $image = 'http://developer.qiniu.com/resource/logo-2.jpg';
+
+        // 水印简单测试
+        $this->assertEquals(
+            $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+            . '/dissolve/100/gravity/SouthEast/',
+            $imageUrlBuilder->waterImg($url, $image)
+        );
+        $this->assertEquals(
+            $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+            . '/gravity/SouthEast/',
+            $imageUrlBuilder->waterImg($url, $image, 101)
+        );
+        $this->assertEquals(
+            $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw==/',
+            $imageUrlBuilder->waterImg($url, $image, 101, 'sdfsd')
+        );
+        $this->assertEquals(
+            $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+            . '/dissolve/100/gravity/SouthEast/',
+            \Qiniu\waterImg($url, $image)
+        );
+
+        // 横轴边距测试
+        $this->assertEquals(
+            $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+            . '/dissolve/100/gravity/SouthEast/dx/10/',
+            $imageUrlBuilder->waterImg($url, $image, 100, 'SouthEast', 10)
+        );
+        $this->assertEquals(
+            $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+            . '/dissolve/100/gravity/SouthEast/',
+            \Qiniu\waterImg($url, $image, 100, 'SouthEast', 'sad')
+        );
+
+        // 纵轴边距测试
+        $this->assertEquals(
+            $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+            . '/dissolve/100/gravity/SouthEast/dx/10/dy/10/',
+            $imageUrlBuilder->waterImg($url, $image, 100, 'SouthEast', 10, 10)
+        );
+        $this->assertEquals(
+            $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+            . '/dissolve/100/gravity/SouthEast/',
+            \Qiniu\waterImg($url, $image, 100, 'SouthEast', 'sad', 'asdf')
+        );
+
+        // 自适应原图的短边比例测试
+        $this->assertEquals(
+            $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+            . '/dissolve/100/gravity/SouthEast/dx/10/dy/10/ws/0.5/',
+            $imageUrlBuilder->waterImg($url, $image, 100, 'SouthEast', 10, 10, 0.5)
+        );
+        $this->assertEquals(
+            $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+            . '/dissolve/100/gravity/SouthEast/',
+            \Qiniu\waterImg($url, $image, 100, 'SouthEast', 'sad', 'asdf', 2)
+        );
+
+        // 多参数测试
+        $this->assertEquals(
+            $url2 . '|watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+            . '/dissolve/100/gravity/SouthEast/',
+            $imageUrlBuilder->waterImg($url2, $image)
+        );
+        $this->assertEquals(
+            $url2 . '|watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+            . '/dissolve/100/gravity/SouthEast/',
+            \Qiniu\waterImg($url2, $image)
+        );
+    }
+
+    /**
+     * 文字水印测试
+     *
+     * @test
+     * @param  void
+     * @return void
+     * @author Sherlock Ren <sherlock_ren@icloud.com>
+     */
+    public function waterTextTest()
+    {
+        $imageUrlBuilder = new \Qiniu\Processing\ImageUrlBuilder();
+        $url = 'http://78re52.com1.z0.glb.clouddn.com/resource/gogopher.jpg';
+        $url2 = $url . '?imageView2/1/w/200/h/200/format/png/ignore-error/1/';
+        $text = '测试一下';
+        $font = '微软雅黑';
+        $fontColor = '#FF0000';
+
+        // 水印简单测试
+        $this->assertEquals($url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/'
+            . 'fontsize/500/dissolve/100/gravity/SouthEast/', $imageUrlBuilder->waterText($url, $text, $font, 500));
+        $this->assertEquals(
+            $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/'
+            . 'dissolve/100/gravity/SouthEast/',
+            \Qiniu\waterText($url, $text, $font, 'sdf')
+        );
+
+        // 字体颜色测试
+        $this->assertEquals(
+            $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/'
+            . 'I0ZGMDAwMA==/dissolve/100/gravity/SouthEast/',
+            $imageUrlBuilder->waterText($url, $text, $font, 500, $fontColor)
+        );
+        $this->assertEquals(
+            $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fill/I0ZGMDAwMA=='
+            . '/dissolve/100/gravity/SouthEast/',
+            \Qiniu\waterText($url, $text, $font, 'sdf', $fontColor)
+        );
+
+        // 透明度测试
+        $this->assertEquals(
+            $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0ZGMDAwMA=='
+            . '/dissolve/80/gravity/SouthEast/',
+            $imageUrlBuilder->waterText($url, $text, $font, 500, $fontColor, 80)
+        );
+        $this->assertEquals(
+            $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fill/I0ZGMDAwMA=='
+            . '/gravity/SouthEast/',
+            \Qiniu\waterText($url, $text, $font, 'sdf', $fontColor, 101)
+        );
+
+        // 水印位置测试
+        $this->assertEquals(
+            $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0ZGMDAwMA=='
+            . '/dissolve/80/gravity/East/',
+            $imageUrlBuilder->waterText($url, $text, $font, 500, $fontColor, 80, 'East')
+        );
+        $this->assertEquals(
+            $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fill/I0ZGMDAwMA==/',
+            \Qiniu\waterText($url, $text, $font, 'sdf', $fontColor, 101, 'sdfsdf')
+        );
+
+        // 横轴距离测试
+        $this->assertEquals(
+            $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0ZGMDAwMA=='
+            . '/dissolve/80/gravity/East/dx/10/',
+            $imageUrlBuilder->waterText($url, $text, $font, 500, $fontColor, 80, 'East', 10)
+        );
+        $this->assertEquals(
+            $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fill/I0ZGMDAwMA==/',
+            \Qiniu\waterText($url, $text, $font, 'sdf', $fontColor, 101, 'sdfsdf', 'sdfs')
+        );
+
+        // 纵轴距离测试
+        $this->assertEquals(
+            $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0ZGMDAwMA=='
+            . '/dissolve/80/gravity/East/dx/10/dy/10/',
+            $imageUrlBuilder->waterText($url, $text, $font, 500, $fontColor, 80, 'East', 10, 10)
+        );
+        $this->assertEquals(
+            $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fill/I0ZGMDAwMA==/',
+            \Qiniu\waterText($url, $text, $font, 'sdf', $fontColor, 101, 'sdfsdf', 'sdfs', 'ssdf')
+        );
+        // 多参数测试
+        $this->assertEquals(
+            $url2 . '|watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/'
+            . 'fontsize/500/dissolve/100/gravity/SouthEast/',
+            $imageUrlBuilder->waterText($url2, $text, $font, 500)
+        );
+        $this->assertEquals(
+            $url2 . '|watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/'
+            . 'fontsize/500/dissolve/100/gravity/SouthEast/',
+            \Qiniu\waterText($url2, $text, $font, 500)
+        );
+    }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/PfopTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/PfopTest.php
new file mode 100755
index 0000000..d03b3f6
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/PfopTest.php
@@ -0,0 +1,66 @@
+<?php
+namespace Qiniu\Tests;
+
+use Qiniu\Processing\Operation;
+use Qiniu\Processing\PersistentFop;
+
+class PfopTest extends \PHPUnit_Framework_TestCase
+{
+    public function testPfop()
+    {
+        global $testAuth;
+        $bucket = 'testres';
+        $key = 'sintel_trailer.mp4';
+        $pfop = new PersistentFop($testAuth, null);
+
+        $fops = 'avthumb/m3u8/segtime/10/vcodec/libx264/s/320x240';
+        list($id, $error) = $pfop->execute($bucket, $key, $fops);
+        $this->assertNull($error);
+        list($status, $error) = $pfop->status($id);
+        $this->assertNotNull($status);
+        $this->assertNull($error);
+    }
+
+
+    public function testPfops()
+    {
+        global $testAuth;
+        $bucket = 'testres';
+        $key = 'sintel_trailer.mp4';
+        $fops = array(
+            'avthumb/m3u8/segtime/10/vcodec/libx264/s/320x240',
+            'vframe/jpg/offset/7/w/480/h/360',
+        );
+        $pfop = new PersistentFop($testAuth, null);
+
+        list($id, $error) = $pfop->execute($bucket, $key, $fops);
+        $this->assertNull($error);
+
+        list($status, $error) = $pfop->status($id);
+        $this->assertNotNull($status);
+        $this->assertNull($error);
+    }
+
+    public function testMkzip()
+    {
+        global $testAuth;
+        $bucket = 'phpsdk';
+        $key = 'php-logo.png';
+        $pfop = new PersistentFop($testAuth, null);
+
+        $url1 = 'http://phpsdk.qiniudn.com/php-logo.png';
+        $url2 = 'http://phpsdk.qiniudn.com/php-sdk.html';
+        $zipKey = 'test.zip';
+
+        $fops = 'mkzip/2/url/' . \Qiniu\base64_urlSafeEncode($url1);
+        $fops .= '/url/' . \Qiniu\base64_urlSafeEncode($url2);
+        $fops .= '|saveas/' . \Qiniu\base64_urlSafeEncode("$bucket:$zipKey");
+
+        list($id, $error) = $pfop->execute($bucket, $key, $fops);
+        $this->assertNull($error);
+
+        list($status, $error) = $pfop->status($id);
+        $this->assertNotNull($status);
+        $this->assertNull($error);
+    }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/ResumeUpTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/ResumeUpTest.php
new file mode 100755
index 0000000..41e6ea8
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/ResumeUpTest.php
@@ -0,0 +1,60 @@
+<?php
+namespace Qiniu\Tests;
+
+use Qiniu\Storage\ResumeUploader;
+use Qiniu\Storage\UploadManager;
+use Qiniu\Config;
+use Qiniu\Zone;
+
+class ResumeUpTest extends \PHPUnit_Framework_TestCase
+{
+    protected $bucketName;
+    protected $auth;
+
+    protected function setUp()
+    {
+        global $bucketName;
+        $this->bucketName = $bucketName;
+
+        global $testAuth;
+        $this->auth = $testAuth;
+    }
+
+    public function test4ML()
+    {
+        $key = 'resumePutFile4ML';
+        $upManager = new UploadManager();
+        $token = $this->auth->uploadToken($this->bucketName, $key);
+        $tempFile = qiniuTempFile(4 * 1024 * 1024 + 10);
+        list($ret, $error) = $upManager->putFile($token, $key, $tempFile);
+        $this->assertNull($error);
+        $this->assertNotNull($ret['hash']);
+        unlink($tempFile);
+    }
+
+    public function test4ML2()
+    {
+        $key = 'resumePutFile4ML';
+        $zone = new Zone(array('up.fake.qiniu.com'), array('up.qiniup.com'));
+        $cfg = new Config($zone);
+        $upManager = new UploadManager($cfg);
+        $token = $this->auth->uploadToken($this->bucketName, $key);
+        $tempFile = qiniuTempFile(4 * 1024 * 1024 + 10);
+        list($ret, $error) = $upManager->putFile($token, $key, $tempFile);
+        $this->assertNull($error);
+        $this->assertNotNull($ret['hash']);
+        unlink($tempFile);
+    }
+
+    // public function test8M()
+    // {
+    //     $key = 'resumePutFile8M';
+    //     $upManager = new UploadManager();
+    //     $token = $this->auth->uploadToken($this->bucketName, $key);
+    //     $tempFile = qiniuTempFile(8*1024*1024+10);
+    //     list($ret, $error) = $upManager->putFile($token, $key, $tempFile);
+    //     $this->assertNull($error);
+    //     $this->assertNotNull($ret['hash']);
+    //     unlink($tempFile);
+    // }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/ZoneTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/ZoneTest.php
new file mode 100755
index 0000000..d32875b
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/ZoneTest.php
@@ -0,0 +1,59 @@
+<?php
+namespace Qiniu\Tests;
+
+use Qiniu;
+use Qiniu\Zone;
+
+class ZoneTest extends \PHPUnit_Framework_TestCase
+{
+    protected $zone;
+    protected $zoneHttps;
+    protected $ak;
+
+    protected $bucketName;
+    protected $bucketNameBC;
+    protected $bucketNameNA;
+
+
+    protected function setUp()
+    {
+        global $bucketName;
+        $this->bucketName = $bucketName;
+
+        global $bucketNameBC;
+        $this->bucketNameBC = $bucketNameBC;
+
+        global $bucketNameNA;
+        $this->bucketNameNA = $bucketNameNA;
+
+        global $accessKey;
+        $this->ak = $accessKey;
+
+        $this->zone = new Zone();
+        $this->zoneHttps = new Zone('https');
+    }
+
+    public function testUpHosts()
+    {
+        $zone = Zone::queryZone($this->ak, $this->bucketName);
+        $this->assertContains('upload.qiniup.com', $zone->cdnUpHosts);
+
+        $zone = Zone::queryZone($this->ak, $this->bucketNameBC);
+        $this->assertContains('upload-z1.qiniup.com', $zone->cdnUpHosts);
+
+        $zone = Zone::queryZone($this->ak, $this->bucketNameNA);
+        $this->assertContains('upload-na0.qiniup.com', $zone->cdnUpHosts);
+    }
+
+    public function testIoHosts()
+    {
+        $zone = Zone::queryZone($this->ak, $this->bucketName);
+        $this->assertEquals($zone->iovipHost, 'iovip.qbox.me');
+
+        $zone = Zone::queryZone($this->ak, $this->bucketNameBC);
+        $this->assertEquals($zone->iovipHost, 'iovip-z1.qbox.me');
+
+        $zone = Zone::queryZone($this->ak, $this->bucketNameNA);
+        $this->assertEquals($zone->iovipHost, 'iovip-na0.qbox.me');
+    }
+}
diff --git a/vendor/qiniu/php-sdk/tests/bootstrap.php b/vendor/qiniu/php-sdk/tests/bootstrap.php
new file mode 100755
index 0000000..c300057
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/bootstrap.php
@@ -0,0 +1,43 @@
+<?php
+// @codingStandardsIgnoreFile
+require_once __DIR__ . '/../autoload.php';
+
+use Qiniu\Auth;
+
+$accessKey = getenv('QINIU_ACCESS_KEY');
+$secretKey = getenv('QINIU_SECRET_KEY');
+$testAuth = new Auth($accessKey, $secretKey);
+
+$bucketName = 'phpsdk';
+$key = 'php-logo.png';
+$key2 = 'niu.jpg';
+
+$bucketNameBC = 'phpsdk-bc';
+$bucketNameNA = 'phpsdk-na';
+
+$dummyAccessKey = 'abcdefghklmnopq';
+$dummySecretKey = '1234567890';
+$dummyAuth = new Auth($dummyAccessKey, $dummySecretKey);
+
+//cdn
+$timestampAntiLeechEncryptKey = getenv('QINIU_TIMESTAMP_ENCRPTKEY');
+$customDomain = "http://phpsdk.peterpy.cn";
+
+$tid = getenv('TRAVIS_JOB_NUMBER');
+if (!empty($tid)) {
+    $pid = getmypid();
+    $tid = strstr($tid, '.');
+    $tid .= '.' . $pid;
+}
+
+function qiniuTempFile($size)
+{
+    $fileName = tempnam(sys_get_temp_dir(), 'qiniu_');
+    $file = fopen($fileName, 'wb');
+    if ($size > 0) {
+        fseek($file, $size - 1);
+        fwrite($file, ' ');
+    }
+    fclose($file);
+    return $fileName;
+}
diff --git a/vendor/ralouphie/getallheaders/.gitignore b/vendor/ralouphie/getallheaders/.gitignore
new file mode 100755
index 0000000..1324e7d
--- /dev/null
+++ b/vendor/ralouphie/getallheaders/.gitignore
@@ -0,0 +1,5 @@
+.idea
+.DS_store
+/vendor/
+composer.phar
+composer.lock
diff --git a/vendor/ralouphie/getallheaders/.travis.yml b/vendor/ralouphie/getallheaders/.travis.yml
new file mode 100755
index 0000000..f45b55f
--- /dev/null
+++ b/vendor/ralouphie/getallheaders/.travis.yml
@@ -0,0 +1,18 @@
+language: php
+
+php:
+  - 5.3
+  - 5.4
+  - 5.5
+  - 5.6
+  - 7.0
+
+before_script:
+  - composer install
+
+script:
+  - mkdir -p build/logs
+  - php vendor/bin/phpunit -c phpunit.xml
+
+after_script:
+  - php vendor/bin/coveralls -v
\ No newline at end of file
diff --git a/vendor/ralouphie/getallheaders/LICENSE b/vendor/ralouphie/getallheaders/LICENSE
new file mode 100755
index 0000000..be5540c
--- /dev/null
+++ b/vendor/ralouphie/getallheaders/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Ralph Khattar
+
+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.
diff --git a/vendor/ralouphie/getallheaders/README.md b/vendor/ralouphie/getallheaders/README.md
new file mode 100755
index 0000000..f3329d6
--- /dev/null
+++ b/vendor/ralouphie/getallheaders/README.md
@@ -0,0 +1,19 @@
+getallheaders
+=============
+
+PHP `getallheaders()` polyfill. Compatible with PHP >= 5.3.
+
+[![Build Status](https://travis-ci.org/ralouphie/getallheaders.svg?branch=master)](https://travis-ci.org/ralouphie/getallheaders)
+[![Coverage Status](https://coveralls.io/repos/ralouphie/getallheaders/badge.png?branch=master)](https://coveralls.io/r/ralouphie/getallheaders?branch=master)
+[![Latest Stable Version](https://poser.pugx.org/ralouphie/getallheaders/v/stable.png)](https://packagist.org/packages/ralouphie/getallheaders)
+[![Latest Unstable Version](https://poser.pugx.org/ralouphie/getallheaders/v/unstable.png)](https://packagist.org/packages/ralouphie/getallheaders)
+[![License](https://poser.pugx.org/ralouphie/getallheaders/license.png)](https://packagist.org/packages/ralouphie/getallheaders)
+
+
+This is a simple polyfill for [`getallheaders()`](http://www.php.net/manual/en/function.getallheaders.php).
+
+## Install
+
+```
+composer require ralouphie/getallheaders
+```
diff --git a/vendor/ralouphie/getallheaders/composer.json b/vendor/ralouphie/getallheaders/composer.json
new file mode 100755
index 0000000..5a0d595
--- /dev/null
+++ b/vendor/ralouphie/getallheaders/composer.json
@@ -0,0 +1,21 @@
+{
+	"name": "ralouphie/getallheaders",
+	"description": "A polyfill for getallheaders.",
+	"license": "MIT",
+	"authors": [
+		{
+			"name": "Ralph Khattar",
+			"email": "ralph.khattar@gmail.com"
+		}
+	],
+	"require": {
+		"php": ">=5.3"
+	},
+	"require-dev": {
+		"phpunit/phpunit": "~3.7.0",
+		"satooshi/php-coveralls": ">=1.0"
+	},
+	"autoload": {
+		"files": ["src/getallheaders.php"]
+	}
+}
\ No newline at end of file
diff --git a/vendor/ralouphie/getallheaders/phpunit.xml b/vendor/ralouphie/getallheaders/phpunit.xml
new file mode 100755
index 0000000..7255b23
--- /dev/null
+++ b/vendor/ralouphie/getallheaders/phpunit.xml
@@ -0,0 +1,22 @@
+<phpunit
+	bootstrap="vendor/autoload.php"
+	convertErrorsToExceptions="true"
+	convertNoticesToExceptions="true"
+	convertWarningsToExceptions="true"
+	strict="true">
+
+	<testsuite>
+		<directory>./tests</directory>
+	</testsuite>
+
+	<filter>
+		<whitelist>
+			<directory suffix=".php">src</directory>
+		</whitelist>
+	</filter>
+
+	<logging>
+		<log type="coverage-clover" target="build/logs/clover.xml"/>
+	</logging>
+
+</phpunit>
\ No newline at end of file
diff --git a/vendor/ralouphie/getallheaders/src/getallheaders.php b/vendor/ralouphie/getallheaders/src/getallheaders.php
new file mode 100755
index 0000000..c7285a5
--- /dev/null
+++ b/vendor/ralouphie/getallheaders/src/getallheaders.php
@@ -0,0 +1,46 @@
+<?php
+
+if (!function_exists('getallheaders')) {
+
+    /**
+     * Get all HTTP header key/values as an associative array for the current request.
+     *
+     * @return string[string] The HTTP header key/value pairs.
+     */
+    function getallheaders()
+    {
+        $headers = array();
+
+        $copy_server = array(
+            'CONTENT_TYPE'   => 'Content-Type',
+            'CONTENT_LENGTH' => 'Content-Length',
+            'CONTENT_MD5'    => 'Content-Md5',
+        );
+
+        foreach ($_SERVER as $key => $value) {
+            if (substr($key, 0, 5) === 'HTTP_') {
+                $key = substr($key, 5);
+                if (!isset($copy_server[$key]) || !isset($_SERVER[$key])) {
+                    $key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', $key))));
+                    $headers[$key] = $value;
+                }
+            } elseif (isset($copy_server[$key])) {
+                $headers[$copy_server[$key]] = $value;
+            }
+        }
+
+        if (!isset($headers['Authorization'])) {
+            if (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
+                $headers['Authorization'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
+            } elseif (isset($_SERVER['PHP_AUTH_USER'])) {
+                $basic_pass = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : '';
+                $headers['Authorization'] = 'Basic ' . base64_encode($_SERVER['PHP_AUTH_USER'] . ':' . $basic_pass);
+            } elseif (isset($_SERVER['PHP_AUTH_DIGEST'])) {
+                $headers['Authorization'] = $_SERVER['PHP_AUTH_DIGEST'];
+            }
+        }
+
+        return $headers;
+    }
+
+}
diff --git a/vendor/ralouphie/getallheaders/tests/GetAllHeadersTest.php b/vendor/ralouphie/getallheaders/tests/GetAllHeadersTest.php
new file mode 100755
index 0000000..8e3d179
--- /dev/null
+++ b/vendor/ralouphie/getallheaders/tests/GetAllHeadersTest.php
@@ -0,0 +1,121 @@
+<?php
+
+class GetAllHeadersTest extends \PHPUnit_Framework_TestCase
+{
+
+    /**
+     * @dataProvider testWorksData
+     */
+    public function testWorks($test_type, $expected, $server)
+    {
+        foreach ($server as $key => $val) {
+            $_SERVER[$key] = $val;
+        }
+        $result = getallheaders();
+        $this->assertEquals($expected, $result, "Error testing $test_type works.");
+    }
+
+    public function testWorksData()
+    {
+        return array(
+            array(
+                'normal case',
+                array(
+                    'Key-One'                 => 'foo',
+                    'Key-Two'                 => 'bar',
+                    'Another-Key-For-Testing' => 'baz'
+                ),
+                array(
+                    'HTTP_KEY_ONE'                 => 'foo',
+                    'HTTP_KEY_TWO'                 => 'bar',
+                    'HTTP_ANOTHER_KEY_FOR_TESTING' => 'baz'
+                )
+            ),
+            array(
+                'Content-Type',
+                array(
+                    'Content-Type' => 'two'
+                ),
+                array(
+                    'HTTP_CONTENT_TYPE' => 'one',
+                    'CONTENT_TYPE'      => 'two'
+                )
+            ),
+            array(
+                'Content-Length',
+                array(
+                    'Content-Length' => '222'
+                ),
+                array(
+                    'CONTENT_LENGTH'      => '222',
+                    'HTTP_CONTENT_LENGTH' => '111'
+                )
+            ),
+            array(
+                'Content-Length (HTTP_CONTENT_LENGTH only)',
+                array(
+                    'Content-Length' => '111'
+                ),
+                array(
+                    'HTTP_CONTENT_LENGTH' => '111'
+                )
+            ),
+            array(
+                'Content-MD5',
+                array(
+                    'Content-Md5' => 'aef123'
+                ),
+                array(
+                    'CONTENT_MD5'      => 'aef123',
+                    'HTTP_CONTENT_MD5' => 'fea321'
+                )
+            ),
+            array(
+                'Content-MD5 (HTTP_CONTENT_MD5 only)',
+                array(
+                    'Content-Md5' => 'f123'
+                ),
+                array(
+                    'HTTP_CONTENT_MD5' => 'f123'
+                )
+            ),
+            array(
+                'Authorization (normal)',
+                array(
+                    'Authorization' => 'testing'
+                ),
+                array(
+                    'HTTP_AUTHORIZATION' => 'testing',
+                )
+            ),
+            array(
+                'Authorization (redirect)',
+                array(
+                    'Authorization' => 'testing redirect'
+                ),
+                array(
+                    'REDIRECT_HTTP_AUTHORIZATION' => 'testing redirect',
+                )
+            ),
+            array(
+                'Authorization (PHP_AUTH_USER + PHP_AUTH_PW)',
+                array(
+                    'Authorization' => 'Basic ' . base64_encode('foo:bar')
+                ),
+                array(
+                    'PHP_AUTH_USER' => 'foo',
+                    'PHP_AUTH_PW'   => 'bar'
+                )
+            ),
+            array(
+                'Authorization (PHP_AUTH_DIGEST)',
+                array(
+                    'Authorization' => 'example-digest'
+                ),
+                array(
+                    'PHP_AUTH_DIGEST' => 'example-digest'
+                )
+            )
+        );
+    }
+}
diff --git a/vendor/services.php b/vendor/services.php
new file mode 100755
index 0000000..23a761b
--- /dev/null
+++ b/vendor/services.php
@@ -0,0 +1,9 @@
+<?php 
+// This file is automatically generated at:2023-04-12 09:28:46
+declare (strict_types = 1);
+return array (
+  0 => 'think\\captcha\\CaptchaService',
+  1 => 'think\\migration\\Service',
+  2 => 'think\\app\\Service',
+  3 => 'think\\queue\\Service',
+);
\ No newline at end of file
diff --git a/vendor/songshenzong/support/.gitignore b/vendor/songshenzong/support/.gitignore
new file mode 100755
index 0000000..e8144b2
--- /dev/null
+++ b/vendor/songshenzong/support/.gitignore
@@ -0,0 +1,7 @@
+.DS_Store
+.idea
+/vendor/
+/support.iml
+build
+composer.lock
+/.php_cs.cache
diff --git a/vendor/songshenzong/support/.travis.yml b/vendor/songshenzong/support/.travis.yml
new file mode 100755
index 0000000..5fc36f9
--- /dev/null
+++ b/vendor/songshenzong/support/.travis.yml
@@ -0,0 +1,22 @@
+language: php
+install: true
+php:
+  - 7.2
+before_script:
+  - composer install --no-interaction --prefer-source --dev
+  - curl -sSfL -o ~/.phpenv/versions/hhvm/bin/phpunit https://phar.phpunit.de/phpunit-5.7.phar
+script: phpunit
+before_deploy:
+  - git config --local user.email "i@songshenzong.com"
+  - git config --local user.name "Songshenzong"
+  - export GIT_TAG=$TRAVIS_BRANCH-0.1.$TRAVIS_BUILD_NUMBER
+  - git tag $GIT_TAG -a -m "Generated tag from TravisCI for build $TRAVIS_BUILD_NUMBER"
+deploy:
+  provider: releases
+  api_key:
+    secure: tMfMInav0NrKJUZw861pXhAfhioLm/f6dSP9fyNePOnUbbKAqubac6Lh6IbVLlM//tY+hF5p0Ln4q1XYn7JAd9PzO3tWkZC83HDIP9cbZnMKqXU5EaX2fuRwbxt82ISYXbKtwfRBtR2ZkTUSk1rh1TtjkXSJ5Wpv+An2GefF5jwPKeME6fqLsIlCBlReFFD+o36wJaS8vMIIsOsaVxmhZsjBpSBAX6YyV+bOGwgK6YTfPXjh3nz7JrJ1mx+XxFU60cMlpYHgw46FF9ZgJB03T+UtQswUTGDtHdSmVP4nentzuw90prO2w51fOK4cLj7c2Bx/9Wda4Cte8XO1g7sBoSuVaHDtn3qini8pSA1jvjx6GXojqk3NnV3PLA+BOJm7yrNWssrAQxPBm6PJsF7YSrX418MTzoMhvz8TBmc6R9S5r7kyeaB4pY0ZP6O/04C59U/79sVbQO184FkiIX5Ezf7wRsaKCuxa/FavyAdfXG4WmDQLJmvLUa1bzvRd+O94/I9eoh+1/YhN7sPlXctHjdWKIdy5StkB43gP7r+DorBWF3rHrtmGTjAk17SJxyTsiqDmLgNTTeu5+f9j/40Jz65fiN1QFLTQBVXKEWcaGX1kB85GRpa4mQebI7TzqGpGHPkP+3Fc7Fjw8Jc6a06LTDPJVSMSSChi8j/IKYyMb0k=
+  file:
+    - "README.md"
+  on:
+    repo: songshenzong/support
+    tags: true
diff --git a/vendor/songshenzong/support/CHANGELOG.md b/vendor/songshenzong/support/CHANGELOG.md
new file mode 100755
index 0000000..ab009bf
--- /dev/null
+++ b/vendor/songshenzong/support/CHANGELOG.md
@@ -0,0 +1,24 @@
+# ChangeLog
+
+---
+
+## 2.0.5 - 2019-08-30
+- Improved `Time`.
+
+## 2.0.4 - 2019-08-30
+- Added `Time`.
+
+## 2.0.3 - 2019-08-27
+- Added `Env` for `OS`.
+
+## 2.0.2 - 2019-08-14
+- Added `get` for `Arrays`.
+
+## 2.0.1 - 2019-06-12
+- Added `Arrays`.
+
+## 2.0.0 - 2019-06-12
+- Improved Design.
+
+## 1.0.0 - 2017-03-07
+- Released
diff --git a/vendor/songshenzong/support/LICENSE.txt b/vendor/songshenzong/support/LICENSE.txt
new file mode 100755
index 0000000..6bdfed4
--- /dev/null
+++ b/vendor/songshenzong/support/LICENSE.txt
@@ -0,0 +1,19 @@
+Copyright (C) 2019 Songshenzong
+
+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.
diff --git a/vendor/songshenzong/support/README.md b/vendor/songshenzong/support/README.md
new file mode 100755
index 0000000..ca217d4
--- /dev/null
+++ b/vendor/songshenzong/support/README.md
@@ -0,0 +1,32 @@
+[![Build Status](https://travis-ci.org/songshenzong/support.svg?branch=master)][travis]
+[![Total Downloads](https://poser.pugx.org/songshenzong/support/d/total.svg)][packagist]
+[![Latest Stable Version](https://poser.pugx.org/songshenzong/support/v/stable.svg)][packagist]
+[![License](https://poser.pugx.org/songshenzong/support/license.svg)][packagist]
+[![PHP Version](https://img.shields.io/packagist/php-v/songshenzong/support.svg)][packagist]
+
+
+## Installation
+
+Require this package with composer:
+
+
+```shell
+composer require songshenzong/support
+```
+
+## Quick Examples
+
+Please refer to our extensive [Wiki documentation](https://github.com/songshenzong/support/wiki) for more information.
+
+
+## Support
+
+For answers you may not find in the Wiki, avoid posting issues. Feel free to ask for support on Songshenzong.com
+
+
+## License
+
+This package is licensed under the [BSD 3-Clause license](http://opensource.org/licenses/BSD-3-Clause).
+
+[packagist]: https://packagist.org/packages/songshenzong/support
+[travis]: https://travis-ci.org/songshenzong/support
diff --git a/vendor/songshenzong/support/composer.json b/vendor/songshenzong/support/composer.json
new file mode 100755
index 0000000..61da3c7
--- /dev/null
+++ b/vendor/songshenzong/support/composer.json
@@ -0,0 +1,60 @@
+{
+    "name": "songshenzong/support",
+    "description": "The Songshenzong Support package.",
+    "keywords": [
+        "support",
+        "laravel",
+        "web",
+        "Tools"
+    ],
+    "license": "MIT",
+    "homepage": "http://songshenzong.com",
+    "support": {
+        "issues": "https://github.com/songshenzong/support/issues",
+        "source": "https://github.com/songshenzong/support"
+    },
+    "authors": [
+        {
+            "name": "Songshenzong",
+            "email": "i@songshenzong.com"
+        }
+    ],
+    "require": {
+        "php": ">=5.5",
+        "ext-xml": "*",
+        "ext-json": "*",
+        "ext-simplexml": "*",
+        "danielstjules/stringy": "^3.1"
+    },
+    "require-dev": {
+        "phpunit/phpunit": "^4.8.35|^5.4.3",
+        "laravel/framework": "^5.8"
+    },
+    "autoload": {
+        "psr-4": {
+            "Songshenzong\\Support\\": "src/"
+        },
+        "files": [
+            "src/StringsHelpers.php",
+            "src/BashEchoHelpers.php"
+        ]
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "Songshenzong\\Support\\Test\\": "tests/"
+        }
+    },
+    "extra": {
+        "laravel": {
+            "providers": [
+                "Songshenzong\\Support\\StringsServiceProvider"
+            ],
+            "aliases": {
+                "Strings": "Songshenzong\\Support\\StringsFacade"
+            }
+        }
+    },
+    "scripts": {
+        "fixer": "php-cs-fixer fix ./"
+    }
+}
diff --git a/vendor/songshenzong/support/phpunit.xml b/vendor/songshenzong/support/phpunit.xml
new file mode 100755
index 0000000..ee3926d
--- /dev/null
+++ b/vendor/songshenzong/support/phpunit.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit bootstrap="./tests/bootstrap.php"
+         colors="true"
+         processIsolation="false"
+         stopOnFailure="false"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         testSuiteLoaderFile="phpunit/src/Runner/StandardTestSuiteLoader.php">
+
+    <testsuites>
+        <testsuite name="All">
+            <directory>tests</directory>
+        </testsuite>
+    </testsuites>
+
+    <groups>
+        <exclude>
+            <group>integration</group>
+        </exclude>
+    </groups>
+
+    <logging>
+        <log type="junit" target="build/junit.xml"/>
+        <log type="coverage-clover" target="build/coverage/clover.xml"/>
+    </logging>
+
+    <filter>
+        <whitelist>
+            <directory suffix=".php">./src</directory>
+            <exclude>
+            </exclude>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/vendor/songshenzong/support/src/Arrays.php b/vendor/songshenzong/support/src/Arrays.php
new file mode 100755
index 0000000..910e2c7
--- /dev/null
+++ b/vendor/songshenzong/support/src/Arrays.php
@@ -0,0 +1,84 @@
+<?php
+
+namespace Songshenzong\Support;
+
+use InvalidArgumentException;
+
+/**
+ * Class Arrays
+ *
+ * @package Songshenzong\Support
+ */
+class Arrays
+{
+    /**
+     * @param array $arrays
+     *
+     * @return array
+     */
+    public static function merge(array $arrays)
+    {
+        $result = [];
+        foreach ($arrays as $array) {
+            foreach ($array as $key => $value) {
+                if (is_int($key)) {
+                    $result[] = $value;
+                    continue;
+                }
+
+                if (isset($result[$key]) && is_array($result[$key])) {
+                    $result[$key] = self::merge(
+                        [$result[$key], $value]
+                    );
+                    continue;
+                }
+
+                $result[$key] = $value;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Returns item from array or $default if item is not set.
+     *
+     * @param array            $array
+     * @param string|int|array $key one or more keys
+     * @param null             $default
+     *
+     * @return mixed
+     */
+    public static function get(array $array, $key, $default = null)
+    {
+        foreach (is_array($key) ? $key : [$key] as $k) {
+            if (is_array($array) && array_key_exists($k, $array)) {
+                $array = $array[$k];
+            } else {
+                if (func_num_args() < 3) {
+                    throw new InvalidArgumentException("Missing item '$k'.");
+                }
+
+                return $default;
+            }
+        }
+
+        return $array;
+    }
+
+    /**
+     * @param array $system_version
+     */
+    public static function versionCompare(array &$system_version)
+    {
+        for ($i = 0; $i < count($system_version) - 1; $i++) {
+            for ($j = 0; $j < count($system_version) - 1 - $i; $j++) {
+                if (version_compare($system_version[$j], $system_version[$j + 1], '<')) {
+                    $tmp                    = $system_version[$j];
+                    $system_version[$j]     = $system_version[$j + 1];
+                    $system_version[$j + 1] = $tmp;
+                }
+            }
+        }
+    }
+}
diff --git a/vendor/songshenzong/support/src/BashEcho.php b/vendor/songshenzong/support/src/BashEcho.php
new file mode 100755
index 0000000..35ca0d5
--- /dev/null
+++ b/vendor/songshenzong/support/src/BashEcho.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace Songshenzong\Support;
+
+/**
+ * Class BashEcho
+ *
+ * @package Songshenzong\Support
+ */
+class BashEcho
+{
+
+    /**
+     * @param string $string
+     */
+    public static function echoRed($string)
+    {
+        $cmd = "echo -ne \"\033[31m" . $string . " \033[0m\n\"";
+        $a   = exec($cmd);
+        print  $a . PHP_EOL;
+    }
+
+    /**
+     * @param string $string
+     */
+    public static function echoCyan($string)
+    {
+        $cmd = "printf \"\033[35m" . $string . "\033[0m\n\"";
+        $a   = exec($cmd);
+        print $a . PHP_EOL;
+    }
+
+    /**
+     * @param string $string
+     */
+    public static function echoGreen($string)
+    {
+        $cmd = "printf \"\033[32m" . $string . "\033[0m\n\"";
+        $a   = exec($cmd);
+        print $a . PHP_EOL;
+    }
+
+    /**
+     * @param string $string
+     */
+    public static function echoBrown($string)
+    {
+        $cmd = "printf \"\033[33m" . $string . "\033[0m\n\"";
+        $a   = exec($cmd);
+        print $a . PHP_EOL;
+    }
+
+    /**
+     * @param string $string
+     */
+    public static function echoBlue($string)
+    {
+        $cmd = "printf \"\033[34m" . $string . "\033[0m\n\"";
+        $a   = exec($cmd);
+        print $a . PHP_EOL;
+    }
+
+    /**
+     * @param string $string
+     */
+    public static function echoPurple($string)
+    {
+        $cmd = "printf \"\033[35m" . $string . "\033[0m\n\"";
+        $a   = exec($cmd);
+        print  $a . PHP_EOL;
+    }
+}
diff --git a/vendor/songshenzong/support/src/BashEchoHelpers.php b/vendor/songshenzong/support/src/BashEchoHelpers.php
new file mode 100755
index 0000000..372d006
--- /dev/null
+++ b/vendor/songshenzong/support/src/BashEchoHelpers.php
@@ -0,0 +1,64 @@
+<?php
+
+use Songshenzong\Support\BashEcho;
+
+if (!function_exists('echoRed')) {
+    /**
+     * @param $string
+     */
+    function echoRed($string)
+    {
+        BashEcho::echoRed($string);
+    }
+}
+
+if (!function_exists('echoGreen')) {
+    /**
+     * @param $string
+     */
+    function echoGreen($string)
+    {
+        BashEcho::echoGreen($string);
+    }
+}
+
+if (!function_exists('echoBrown')) {
+    /**
+     * @param $string
+     */
+    function echoBrown($string)
+    {
+        BashEcho::echoBrown($string);
+    }
+}
+
+if (!function_exists('echoBlue')) {
+
+    /**
+     * @param $string
+     */
+    function echoBlue($string)
+    {
+        BashEcho::echoBlue($string);
+    }
+}
+
+if (!function_exists('echoPurple')) {
+    /**
+     * @param $string
+     */
+    function echoPurple($string)
+    {
+        BashEcho::echoPurple($string);
+    }
+}
+
+if (!function_exists('echoCyan')) {
+    /**
+     * @param $string
+     */
+    function echoCyan($string)
+    {
+        BashEcho::echoCyan($string);
+    }
+}
diff --git a/vendor/songshenzong/support/src/Env.php b/vendor/songshenzong/support/src/Env.php
new file mode 100755
index 0000000..de9002a
--- /dev/null
+++ b/vendor/songshenzong/support/src/Env.php
@@ -0,0 +1,101 @@
+<?php
+
+namespace Songshenzong\Support;
+
+use Closure;
+
+/**
+ * Class Env
+ *
+ * @package Songshenzong\Support
+ */
+class Env
+{
+
+    /**
+     * Gets the value of an environment variable.
+     *
+     * @param string $key
+     * @param mixed  $default
+     *
+     * @return mixed
+     */
+    public static function env($key, $default = null)
+    {
+        $value = getenv($key);
+
+        if ($value === false) {
+            return self::value($default);
+        }
+
+        if (self::envSubstr($value)) {
+            return substr($value, 1, -1);
+        }
+
+        return self::envConversion($value);
+    }
+
+    /**
+     * @param string $key
+     *
+     * @return bool|mixed
+     */
+    public static function envNotEmpty($key)
+    {
+        $value = self::env($key, false);
+        if ($value) {
+            return $value;
+        }
+
+        return false;
+    }
+
+    /**
+     * Return the default value of the given value.
+     *
+     * @param mixed $value
+     *
+     * @return mixed
+     */
+    public static function value($value)
+    {
+        return $value instanceof Closure ? $value() : $value;
+    }
+
+    /**
+     * @param $value
+     *
+     * @return bool
+     */
+    public static function envSubstr($value)
+    {
+        return ($valueLength = strlen($value)) > 1
+               && strpos($value, '"') === 0
+               && $value[$valueLength - 1] === '"';
+    }
+
+    /**
+     * @param $value
+     *
+     * @return bool|string|null
+     */
+    public static function envConversion($value)
+    {
+        $key = strtolower($value);
+
+        if ($key === 'null' || $key === '(null)') {
+            return null;
+        }
+
+        $list = [
+            'true'    => true,
+            '(true)'  => true,
+            'false'   => false,
+            '(false)' => false,
+            'empty'   => '',
+            '(empty)' => '',
+        ];
+
+        return isset($list[$key]) ? $list[$key] : $value;
+    }
+}
diff --git a/vendor/songshenzong/support/src/OS.php b/vendor/songshenzong/support/src/OS.php
new file mode 100755
index 0000000..7af044e
--- /dev/null
+++ b/vendor/songshenzong/support/src/OS.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace Songshenzong\Support;
+
+use Stringy\Stringy;
+
+/**
+ * Class OS
+ *
+ * @package Songshenzong\Support
+ */
+class OS
+{
+    /**
+     * @param      $filename
+     *
+     * @return bool
+     */
+    public static function inOpenBasedir($filename)
+    {
+        $open_basedir = ini_get('open_basedir');
+        if (!$open_basedir) {
+            return true;
+        }
+
+        $dirs = explode(PATH_SEPARATOR, $open_basedir);
+
+        return empty($dirs) || self::inDir($filename, $dirs);
+    }
+
+    /**
+     * @param string $filename
+     * @param array  $dirs
+     *
+     * @return bool
+     */
+    public static function inDir($filename, array $dirs)
+    {
+        foreach ($dirs as $dir) {
+            if (!Stringy::create($dir)->endsWith(DIRECTORY_SEPARATOR)) {
+                $dir .= DIRECTORY_SEPARATOR;
+            }
+
+            if (0 === strpos($filename, $dir)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * @return bool
+     */
+    public static function isWindows()
+    {
+        return PATH_SEPARATOR === ';';
+    }
+
+    /**
+     * Gets the environment's HOME directory.
+     *
+     * @return null|string
+     */
+    public static function getHomeDirectory()
+    {
+        if (getenv('HOME')) {
+            return getenv('HOME');
+        }
+
+        return (getenv('HOMEDRIVE') && getenv('HOMEPATH'))
+            ? getenv('HOMEDRIVE') . getenv('HOMEPATH')
+            : null;
+    }
+}
diff --git a/vendor/songshenzong/support/src/Strings.php b/vendor/songshenzong/support/src/Strings.php
new file mode 100755
index 0000000..a5cb566
--- /dev/null
+++ b/vendor/songshenzong/support/src/Strings.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Songshenzong\Support;
+
+use Songshenzong\Support\Traits\Str;
+use Songshenzong\Support\Traits\Uri;
+
+/**
+ * Class Strings
+ *
+ * @package Songshenzong\Support
+ */
+class Strings
+{
+    use Str, Uri;
+}
diff --git a/vendor/songshenzong/support/src/StringsFacade.php b/vendor/songshenzong/support/src/StringsFacade.php
new file mode 100755
index 0000000..3b8d1d5
--- /dev/null
+++ b/vendor/songshenzong/support/src/StringsFacade.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace Songshenzong\Support;
+
+use Illuminate\Support\Facades\Facade;
+
+/**
+ * Class StringsFacade
+ *
+ * @package Songshenzong\Support
+ */
+class StringsFacade extends Facade
+{
+    /**
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return 'Strings';
+    }
+}
diff --git a/vendor/songshenzong/support/src/StringsHelpers.php b/vendor/songshenzong/support/src/StringsHelpers.php
new file mode 100755
index 0000000..bc4b13d
--- /dev/null
+++ b/vendor/songshenzong/support/src/StringsHelpers.php
@@ -0,0 +1,13 @@
+<?php
+
+use Songshenzong\Support\Strings;
+
+if (!function_exists('strings')) {
+    /**
+     * @return Strings
+     */
+    function strings()
+    {
+        return new Strings;
+    }
+}
diff --git a/vendor/songshenzong/support/src/StringsServiceProvider.php b/vendor/songshenzong/support/src/StringsServiceProvider.php
new file mode 100755
index 0000000..1145f47
--- /dev/null
+++ b/vendor/songshenzong/support/src/StringsServiceProvider.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace Songshenzong\Support;
+
+use Illuminate\Support\ServiceProvider;
+
+/**
+ * Class StringsServiceProvider
+ *
+ * @package Songshenzong\Support
+ */
+class StringsServiceProvider extends ServiceProvider
+{
+    /**
+     * Indicates if loading of the provider is deferred.
+     *
+     * @var bool
+     */
+    protected $defer = false;
+
+    /**
+     * Register the service provider.
+     *
+     * @return void
+     */
+    public function register()
+    {
+        $this->app->singleton(
+            'Strings',
+            static function () {
+                return new Strings();
+            }
+        );
+
+        $this->app->alias('Strings', StringsFacade::class);
+    }
+}
diff --git a/vendor/songshenzong/support/src/Time.php b/vendor/songshenzong/support/src/Time.php
new file mode 100755
index 0000000..82bd1a1
--- /dev/null
+++ b/vendor/songshenzong/support/src/Time.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace Songshenzong\Support;
+
+/**
+ * Class Time
+ *
+ * @package Songshenzong\Support
+ */
+class Time
+{
+
+    /**
+     * @param int|string $begin_time
+     * @param int|string $end_time
+     *
+     * @return array
+     */
+    public static function dates($begin_time, $end_time = null)
+    {
+        if ($end_time === null) {
+            $end_time = time();
+        }
+
+        // Y-m-d to timestamp
+        if (!is_int($begin_time)) {
+            $begin_time = strtotime($begin_time);
+        }
+
+        // Y-m-d to timestamp
+        if (!is_int($end_time)) {
+            $end_time = strtotime($end_time);
+        }
+
+        $dates = [];
+
+        for ($start = $begin_time; $start <= $end_time; $start += 86400) {
+            $dates[] = date('Y-m-d', $start);
+        }
+
+        return $dates;
+    }
+
+    /**
+     * Format Time.
+     *
+     * @param string $time_string
+     *
+     * @return false|string
+     */
+    public static function formatTime($time_string)
+    {
+        $time  = time() - strtotime($time_string);
+        $time1 = time() - strtotime('today');
+        if ($time < 60) {
+            $str = '刚刚';
+        } elseif ($time < 3600) {
+            $min = floor($time / 60);
+            $str = $min . '分钟前';
+        } elseif ($time < 24 * 3600) {
+            $min = floor($time / (60 * 60));
+            $str = $min . '小时前';
+        } elseif ($time > $time1 && $time < 7 * 24 * 3600) {
+            $min = floor($time / (3600 * 24));
+            $str = $min . '天前';
+        } else {
+            $str = date('n月j日 H:i', strtotime($time_string));
+        }
+
+        return $str;
+    }
+}
diff --git a/vendor/songshenzong/support/src/Traits/Str.php b/vendor/songshenzong/support/src/Traits/Str.php
new file mode 100755
index 0000000..29f44a3
--- /dev/null
+++ b/vendor/songshenzong/support/src/Traits/Str.php
@@ -0,0 +1,295 @@
+<?php
+
+namespace Songshenzong\Support\Traits;
+
+use stdClass;
+use SimpleXMLElement;
+
+/**
+ * Trait Str
+ *
+ * @package Songshenzong\Support\Traits
+ */
+trait Str
+{
+
+    /**
+     * @param string $string
+     *
+     * @return array
+     */
+    public static function toArray($string = '')
+    {
+        if ($string === '') {
+            return [];
+        }
+
+        if (self::isJson($string)) {
+            return json_decode($string, true);
+        }
+
+        if (self::isXml($string)) {
+            return self::xmlToArray($string);
+        }
+
+        $unserialize = self::unserialize($string);
+        if ($unserialize === false) {
+            return [];
+        }
+
+        if (\is_array($unserialize)) {
+            return $unserialize;
+        }
+
+        return [];
+    }
+
+    /**
+     * @param string $string
+     *
+     * @return bool
+     */
+    public static function isJson($string = '')
+    {
+        if ($string === '') {
+            return false;
+        }
+
+        \json_decode($string, true);
+        if (\json_last_error()) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * @param string $string
+     *
+     * @return bool
+     */
+    public static function isXml($string = '')
+    {
+        if ($string === '') {
+            return false;
+        }
+
+        $xml_parser = xml_parser_create();
+        if (!xml_parse($xml_parser, $string, true)) {
+            xml_parser_free($xml_parser);
+
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * @param string $string
+     *
+     * @return array
+     */
+    public static function xmlToArray($string)
+    {
+        return json_decode(json_encode(simplexml_load_string($string)), true);
+    }
+
+    /**
+     * @param string $serialized
+     *
+     * @return mixed
+     */
+    public static function unserialize($serialized)
+    {
+        // Set Handle
+        set_error_handler(
+            function () {
+            },
+            E_ALL
+        );
+        $result = unserialize((string)$serialized);
+        // Restores the previous error handler function
+        restore_error_handler();
+        if ($result === false) {
+            return false;
+        }
+
+        return $result;
+    }
+
+    /**
+     * @param string $string
+     *
+     * @return stdClass|SimpleXMLElement
+     *
+     */
+    public static function toObject($string = '')
+    {
+        if ($string === '') {
+            return new stdClass();
+        }
+
+        if (self::isJson($string)) {
+            return json_decode($string);
+        }
+
+        if (self::isXml($string)) {
+            return self::xmlToObject($string);
+        }
+
+        $unserialize = self::unserialize($string);
+        if ($unserialize === false) {
+            return new stdClass();
+        }
+
+        if (\is_object($unserialize)) {
+            return $unserialize;
+        }
+
+        return new stdClass();
+    }
+
+    /**
+     * @param string $string
+     *
+     * @return SimpleXMLElement
+     */
+    public static function xmlToObject($string)
+    {
+        return simplexml_load_string($string);
+    }
+
+    /**
+     * @param string $string
+     *
+     * @return bool
+     */
+    public static function isSerialized($string)
+    {
+        // Set Handle
+        set_error_handler(
+            function () {
+            },
+            E_ALL
+        );
+        $result = unserialize($string);
+        // Restores the previous error handler function
+        restore_error_handler();
+
+        return !($result === false);
+    }
+
+    /**
+     * @param string $string
+     *
+     * @return string
+     */
+    public static function filter($string)
+    {
+        $filter = [
+            "\n",
+            '`',
+            '·',
+            '~',
+            '!',
+            '!',
+            '@',
+            '#',
+            '$',
+            '¥',
+            '%',
+            '^',
+            '……',
+            '&',
+            '*',
+            '(',
+            ')',
+            '(',
+            ')',
+            '-',
+            '_',
+            '——',
+            '+',
+            '=',
+            '|',
+            '\\',
+            '[',
+            ']',
+            '【',
+            '】',
+            '{',
+            '}',
+            ';',
+            ';',
+            ':',
+            ':',
+            '\'',
+            '"',
+            '“',
+            '”',
+            ',',
+            ',',
+            '<',
+            '>',
+            '《',
+            '》',
+            '.',
+            '。',
+            '/',
+            '、',
+            '?',
+            '?',
+            ';',
+            'nbsp',
+        ];
+
+        $str = str_replace($filter, '', $string);
+
+        return trim($str);
+    }
+
+    /**
+     * @param string $string
+     *
+     * @return string
+     */
+    public static function trim($string)
+    {
+        $filter = [
+            "\0",
+            "\n",
+            "\t",
+            "\x0B",
+            "\r",
+            ' ',
+        ];
+
+        $str = str_replace($filter, '', $string);
+
+        return trim($str);
+    }
+
+    /**
+     * Is Set and Not Empty.
+     *
+     * @param $value
+     *
+     * @return bool
+     */
+    public static function isSetAndNotEmpty($value)
+    {
+        return isset($value) && !empty($value);
+    }
+
+    /**
+     * Is Set and Not Empty and Not Null.
+     *
+     * @param $value
+     *
+     * @return bool
+     */
+    public static function isSetAndNotEmptyAndNotNull($value)
+    {
+        return isset($value) && !empty($value) && $value !== 'null';
+    }
+}
diff --git a/vendor/songshenzong/support/src/Traits/Uri.php b/vendor/songshenzong/support/src/Traits/Uri.php
new file mode 100755
index 0000000..02e7566
--- /dev/null
+++ b/vendor/songshenzong/support/src/Traits/Uri.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace Songshenzong\Support\Traits;
+
+/**
+ * Trait Uri
+ *
+ * @package Songshenzong\Support\Traits
+ */
+trait Uri
+{
+
+    /**
+     * @param string $uri
+     * @param array  $parameters
+     * @param bool   $appendsCurrentUri
+     *
+     * @return string
+     */
+    public static function uri($uri, array $parameters = [], $appendsCurrentUri = false)
+    {
+        $uriComponents = parse_url($uri);
+
+        if (isset($uriComponents['query'])) {
+            \parse_str($uriComponents['query'], $uriComponents['query']);
+        } else {
+            $uriComponents['query'] = [];
+        }
+
+        if ($appendsCurrentUri) {
+            $newQuery = $parameters + $_GET + $uriComponents['query'];
+        } else {
+            $newQuery = $parameters + $uriComponents['query'];
+        }
+
+        $newUriComponents = isset($uriComponents['scheme']) ? $uriComponents['scheme'] . '://' : '';
+        $newUriComponents .= isset($uriComponents['host']) ? $uriComponents['host'] : '';
+        $newUriComponents .= isset($uriComponents['port']) ? ':' . $uriComponents['port'] : '';
+        $newUriComponents .= isset($uriComponents['path']) ? $uriComponents['path'] : '';
+        $newUriComponents .= '?' . http_build_query($newQuery);
+
+        return $newUriComponents;
+    }
+
+    /**
+     * @param $host
+     *
+     * @return bool
+     */
+    public static function host($host)
+    {
+        if (!isset($_SERVER['HTTP_HOST'])) {
+            return false;
+        }
+        $lower = \strtolower($_SERVER['HTTP_HOST']);
+
+        if (\is_string($host)) {
+            return $host === $lower;
+        }
+
+        if (\is_array($host)) {
+            foreach ($host as $item) {
+                if ($item === $lower) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        return false;
+    }
+}
diff --git a/vendor/songshenzong/support/tests/Core/EchoBashTest.php b/vendor/songshenzong/support/tests/Core/EchoBashTest.php
new file mode 100755
index 0000000..7cefe0a
--- /dev/null
+++ b/vendor/songshenzong/support/tests/Core/EchoBashTest.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Songshenzong\Support\Test\Core;
+
+use PHPUnit\Framework\TestCase;
+
+/**
+ * Class EchoBashTest
+ *
+ * @package Songshenzong\Support\Test\Core
+ */
+class EchoBashTest extends TestCase
+{
+
+    /**
+     * @throws \PHPUnit\Framework\ExpectationFailedException
+     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+     */
+    public function testIsJson()
+    {
+        echoRed('echo Red');
+        echoPurple('echo Purple');
+        echoGreen('echo Green');
+        echoCyan('echo Cyan');
+        echoBrown('echo Brown');
+        echoBlue('echo Blue');
+        $this->assertEquals(true, true);
+    }
+}
diff --git a/vendor/songshenzong/support/tests/Core/StrTest.php b/vendor/songshenzong/support/tests/Core/StrTest.php
new file mode 100755
index 0000000..d85e31f
--- /dev/null
+++ b/vendor/songshenzong/support/tests/Core/StrTest.php
@@ -0,0 +1,108 @@
+<?php
+
+namespace Songshenzong\Support\Test\Core;
+
+use stdClass;
+use PHPUnit\Framework\TestCase;
+use Songshenzong\Support\Strings;
+
+/**
+ * Class StrTest
+ *
+ * @package Songshenzong\Support\Test\Core
+ */
+class StrTest extends TestCase
+{
+    /**
+     * @var string
+     */
+    protected $xmlString = <<<ETO
+<?xml version="1.0" encoding="UTF-8"?>
+<books>
+  <book>
+    <author>Jack Herrington</author>
+    <title>PHP Hacks</title>
+    <publisher>O'Reilly</publisher>
+  </book>
+  <book>
+    <author>Jack Herrington</author>
+    <title>Podcasting Hacks</title>
+    <publisher>O'Reilly</publisher>
+  </book>
+</books>
+ETO;
+
+    /**
+     * @var string
+     */
+    protected $jsonString = '{ "tools": [ { "name":"css format" , "site":"https://songshenzong.com" }, { "name":"pwd check" , "site":"https://songshenzong.com" } ] }';
+
+    /**
+     * @var array
+     */
+    protected $simpleJsonArray = ['json' => true];
+
+    /**
+     * @var string
+     */
+    protected $simpleJsonString = '{"json":true}';
+
+    /**
+     * @throws \PHPUnit\Framework\ExpectationFailedException
+     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+     */
+    public function testIsJson()
+    {
+        $this->assertEquals(false, Strings::isJson());
+        $this->assertEquals(false, Strings::isJson(''));
+        $this->assertEquals(false, Strings::isJson($this->xmlString));
+        $this->assertEquals(true, Strings::isJson($this->simpleJsonString));
+        $this->assertEquals(true, Strings::isJson($this->jsonString));
+    }
+
+    /**
+     * @throws \PHPUnit\Framework\ExpectationFailedException
+     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+     */
+    public function testIsXml()
+    {
+        $this->assertEquals(false, Strings::isXml(''));
+        $this->assertEquals(false, Strings::isXml());
+        $this->assertEquals(false, Strings::isXml($this->jsonString));
+        $this->assertEquals(true, Strings::isXml($this->xmlString));
+        $this->assertEquals(false, Strings::isXml($this->simpleJsonString));
+    }
+
+    /**
+     * @throws \PHPUnit\Framework\ExpectationFailedException
+     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+     */
+    public function testToArray()
+    {
+        $this->assertEquals($this->simpleJsonArray, Strings::toArray($this->simpleJsonString));
+        $this->assertEquals([], Strings::toArray());
+        $this->assertEquals([], Strings::toArray(''));
+    }
+
+    /**
+     * @throws \PHPUnit\Framework\ExpectationFailedException
+     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+     */
+    public function testToObject()
+    {
+        $this->assertEquals(true, Strings::toObject($this->simpleJsonString)->json);
+        $this->assertObjectHasAttribute('book', Strings::toObject($this->xmlString));
+        $this->assertEquals(new stdClass(), Strings::toObject());
+        $this->assertEquals(new stdClass(), Strings::toObject(''));
+    }
+
+    /**
+     * @throws \PHPUnit\Framework\ExpectationFailedException
+     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+     */
+    public function testXmlToArray()
+    {
+        $this->assertArrayHasKey('book', Strings::xmlToArray($this->xmlString));
+        $this->assertObjectHasAttribute('book', Strings::xmlToObject($this->xmlString));
+    }
+}
diff --git a/vendor/songshenzong/support/tests/OSTest.php b/vendor/songshenzong/support/tests/OSTest.php
new file mode 100755
index 0000000..301a45c
--- /dev/null
+++ b/vendor/songshenzong/support/tests/OSTest.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Songshenzong\Support\Test\Core;
+
+use ReflectionClass;
+use ReflectionException;
+use Songshenzong\Support\OS;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * Class OSTest
+ *
+ * @package Songshenzong\Support\Test\Core
+ */
+class OSTest extends TestCase
+{
+
+    /**
+     * @throws ReflectionException
+     */
+    public function testGetsHomeDirectoryForWindowsUser()
+    {
+        putenv('HOME=');
+        putenv('HOMEDRIVE=C:');
+        putenv('HOMEPATH=\\Users\\Support');
+        $ref    = new ReflectionClass(OS::class);
+        $method = $ref->getMethod('getHomeDirectory');
+        $method->setAccessible(true);
+        $this->assertEquals('C:\\Users\\Support', $method->invoke(null));
+    }
+
+    /**
+     * @depends testGetsHomeDirectoryForWindowsUser
+     * @throws ReflectionException
+     */
+    public function testGetsHomeDirectoryForLinuxUser()
+    {
+        putenv('HOME=/root');
+        putenv('HOMEDRIVE=');
+        putenv('HOMEPATH=');
+        $ref    = new ReflectionClass(OS::class);
+        $method = $ref->getMethod('getHomeDirectory');
+        $method->setAccessible(true);
+        $this->assertEquals('/root', $method->invoke(null));
+    }
+}
diff --git a/vendor/songshenzong/support/tests/TimeTest.php b/vendor/songshenzong/support/tests/TimeTest.php
new file mode 100755
index 0000000..99610c3
--- /dev/null
+++ b/vendor/songshenzong/support/tests/TimeTest.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Songshenzong\Support\Test\Core;
+
+use Songshenzong\Support\Time;
+use PHPUnit\Framework\TestCase;
+
+class TimeTest extends TestCase
+{
+    public function testDates()
+    {
+        $expect = [
+            '2019-09-10',
+            '2019-09-11',
+            '2019-09-12',
+        ];
+
+        self::assertEquals($expect, Time::dates('2019-09-10', '2019-09-12'));
+
+        self::assertEquals($expect, Time::dates(strtotime('2019-09-10'), strtotime('2019-09-12')));
+    }
+}
diff --git a/vendor/songshenzong/support/tests/bootstrap.php b/vendor/songshenzong/support/tests/bootstrap.php
new file mode 100755
index 0000000..fefd973
--- /dev/null
+++ b/vendor/songshenzong/support/tests/bootstrap.php
@@ -0,0 +1,11 @@
+<?php
+
+error_reporting(E_ALL);
+ini_set('display_errors', '1');
+
+if (!ini_get('date.timezone')) {
+    date_default_timezone_set('GMT');
+}
+
+$loader = require dirname(__DIR__) . '/vendor/autoload.php';
+$loader->add('Songshenzong\\Support\\Test\\', __DIR__);
diff --git a/vendor/spatie/macroable/.styleci.yml b/vendor/spatie/macroable/.styleci.yml
new file mode 100755
index 0000000..f4d3cbc
--- /dev/null
+++ b/vendor/spatie/macroable/.styleci.yml
@@ -0,0 +1,4 @@
+preset: laravel
+
+disabled:
+  - single_class_element_per_statement
diff --git a/vendor/spatie/macroable/CHANGELOG.md b/vendor/spatie/macroable/CHANGELOG.md
new file mode 100755
index 0000000..2afae93
--- /dev/null
+++ b/vendor/spatie/macroable/CHANGELOG.md
@@ -0,0 +1,7 @@
+# Changelog
+
+All notable changes to `macroable` will be documented in this file
+
+## 1.0.0 - 2017-09-16
+
+- initial release
diff --git a/vendor/spatie/macroable/CONTRIBUTING.md b/vendor/spatie/macroable/CONTRIBUTING.md
new file mode 100755
index 0000000..4da74e3
--- /dev/null
+++ b/vendor/spatie/macroable/CONTRIBUTING.md
@@ -0,0 +1,55 @@
+# Contributing
+
+Contributions are **welcome** and will be fully **credited**.
+
+Please read and understand the contribution guide before creating an issue or pull request.
+
+## Etiquette
+
+This project is open source, and as such, the maintainers give their free time to build and maintain the source code
+held within. They make the code freely available in the hope that it will be of use to other developers. It would be
+extremely unfair for them to suffer abuse or anger for their hard work.
+
+Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the
+world that developers are civilized and selfless people.
+
+It's the duty of the maintainer to ensure that all submissions to the project are of sufficient
+quality to benefit the project. Many developers have different skillsets, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used.
+
+## Viability
+
+When requesting or submitting new features, first consider whether it might be useful to others. Open
+source projects are used by many developers, who may have entirely different needs to your own. Think about
+whether or not your feature is likely to be used by other users of the project.
+
+## Procedure
+
+Before filing an issue:
+
+- Attempt to replicate the problem, to ensure that it wasn't a coincidental incident.
+- Check to make sure your feature suggestion isn't already present within the project.
+- Check the pull requests tab to ensure that the bug doesn't have a fix in progress.
+- Check the pull requests tab to ensure that the feature isn't already in progress.
+
+Before submitting a pull request:
+
+- Check the codebase to ensure that your feature doesn't already exist.
+- Check the pull requests to ensure that another person hasn't already submitted the feature or fix.
+
+## Requirements
+
+If the project maintainer has any additional requirements, you will find them listed here.
+
+- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer).
+
+- **Add tests!** - Your patch won't be accepted if it doesn't have tests.
+
+- **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date.
+
+- **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option.
+
+- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests.
+
+- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting.
+
+**Happy coding**!
diff --git a/vendor/spatie/macroable/LICENSE.md b/vendor/spatie/macroable/LICENSE.md
new file mode 100755
index 0000000..0b32f5f
--- /dev/null
+++ b/vendor/spatie/macroable/LICENSE.md
@@ -0,0 +1,21 @@
+# The MIT License (MIT)
+
+Copyright (c) Spatie bvba <info@spatie.be>
+
+> 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.
diff --git a/vendor/spatie/macroable/README.md b/vendor/spatie/macroable/README.md
new file mode 100755
index 0000000..ed24e2b
--- /dev/null
+++ b/vendor/spatie/macroable/README.md
@@ -0,0 +1,130 @@
+# A trait to dynamically add methods to a class
+
+[![Latest Version on Packagist](https://img.shields.io/packagist/v/spatie/macroable.svg?style=flat-square)](https://packagist.org/packages/spatie/macroable)
+[![Build Status](https://img.shields.io/travis/spatie/macroable/master.svg?style=flat-square)](https://travis-ci.org/spatie/macroable)
+[![SensioLabsInsight](https://img.shields.io/sensiolabs/i/c3dbed0e-9794-4c54-b40c-ccaf5a1394de.svg?style=flat-square)](https://insight.sensiolabs.com/projects/c3dbed0e-9794-4c54-b40c-ccaf5a1394de)
+[![Quality Score](https://img.shields.io/scrutinizer/g/spatie/macroable.svg?style=flat-square)](https://scrutinizer-ci.com/g/spatie/macroable)
+[![Total Downloads](https://img.shields.io/packagist/dt/spatie/macroable.svg?style=flat-square)](https://packagist.org/packages/spatie/macroable)
+
+This package provides a trait that, when applied to a class, makes it possible to add methods to that class at runtime.
+
+Here's a quick example:
+
+```php
+$myClass = new class() {
+    use Spatie\Macroable\Macroable;
+};
+
+$myClass::macro('concatenate', function(... $strings) {
+   return implode('-', $strings);
+};
+
+$myClass->concatenate('one', 'two', 'three'); // returns 'one-two-three'
+```
+
+The idea of a macroable trait and the implementation is taken from [the `macroable` trait](https://github.com/laravel/framework/blob/master/src/Illuminate/Support/Traits/Macroable.php) of the [the Laravel framework](https://laravel.com).
+
+
+## Postcardware
+
+You're free to use this package (it's [MIT-licensed](LICENSE.md)), but if it makes it to your production environment we highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using.
+
+Our address is: Spatie, Samberstraat 69D, 2060 Antwerp, Belgium.
+
+We publish all received postcards [on our company website](https://spatie.be/en/opensource/postcards).
+
+## Installation
+
+You can install the package via composer:
+
+```bash
+composer require spatie/macroable
+```
+
+## Usage
+
+You can add a new method to a class using `macro`:
+
+```php
+$macroableClass = new class() {
+    use Spatie\Macroable\Macroable;
+};
+
+$macroableClass::macro('concatenate', function(... $strings) {
+   return implode('-', $strings);
+};
+
+$myClass->concatenate('one', 'two', 'three'); // returns 'one-two-three'
+```
+
+Callables passed to the `macro` function will be bound to the `class`
+
+```php
+$macroableClass = new class() {
+    
+    protected $name = 'myName';
+    
+    use Spatie\Macroable\Macroable;
+};
+
+$macroableClass::macro('getName', function() {
+   return $this->name;
+};
+
+$macroableClass->getName(); // returns 'myName'
+```
+
+You can also add multiple methods in one go my using a mixin class. A mixin class contains methods that return callables. Each method from the mixin will be registered on the macroable class.
+
+```php
+$mixin = new class() {
+    public function mixinMethod()
+    {
+       return 'mixinMethod';
+    }
+    
+    public function anotherMixinMethod()
+    {
+       return 'anotherMixinMethod';
+    }
+};
+
+$macroableClass->mixin($mixin);
+
+$macroableClass->mixinMethod() // returns 'mixinMethod';
+
+$macroableClass->anotherMixinMethod() // returns 'anotherMixinMethod';
+```
+
+## Changelog
+
+Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently.
+
+## Testing
+
+``` bash
+composer test
+```
+
+## Contributing
+
+Please see [CONTRIBUTING](CONTRIBUTING.md) for details.
+
+## Security
+
+If you discover any security related issues, please email freek@spatie.be instead of using the issue tracker.
+
+## Credits
+
+- [Freek Van der Herten](https://github.com/freekmurze)
+- [All Contributors](../../contributors)
+
+Idea and code is taken from [the `macroable` trait](https://github.com/laravel/framework/blob/master/src/Illuminate/Support/Traits/Macroable.php) of the [the Laravel framework](https://laravel.com).
+
+## About Spatie
+
+Spatie is a webdesign agency based in Antwerp, Belgium. You'll find an overview of all our open source projects [on our website](https://spatie.be/opensource).
+
+## License
+
+The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
diff --git a/vendor/spatie/macroable/composer.json b/vendor/spatie/macroable/composer.json
new file mode 100755
index 0000000..d455f83
--- /dev/null
+++ b/vendor/spatie/macroable/composer.json
@@ -0,0 +1,40 @@
+{
+    "name": "spatie/macroable",
+    "description": "A trait to dynamically add methods to a class",
+    "keywords": [
+        "spatie",
+        "macroable"
+    ],
+    "homepage": "https://github.com/spatie/macroable",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Freek Van der Herten",
+            "email": "freek@spatie.be",
+            "homepage": "https://spatie.be",
+            "role": "Developer"
+        }
+    ],
+    "require": {
+        "php": "^7.0"
+    },
+    "require-dev": {
+        "phpunit/phpunit": "^6.3"
+    },
+    "autoload": {
+        "psr-4": {
+            "Spatie\\Macroable\\": "src"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "Spatie\\Macroable\\Test\\": "tests"
+        }
+    },
+    "scripts": {
+        "test": "vendor/bin/phpunit"
+    },
+    "config": {
+        "sort-packages": true
+    }
+}
diff --git a/vendor/spatie/macroable/config/.gitkeep b/vendor/spatie/macroable/config/.gitkeep
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/vendor/spatie/macroable/config/.gitkeep
diff --git a/vendor/spatie/macroable/src/Macroable.php b/vendor/spatie/macroable/src/Macroable.php
new file mode 100755
index 0000000..e7e0208
--- /dev/null
+++ b/vendor/spatie/macroable/src/Macroable.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace Spatie\Macroable;
+
+use Closure;
+use ReflectionClass;
+use ReflectionMethod;
+use BadMethodCallException;
+
+trait Macroable
+{
+    protected static $macros = [];
+
+    /**
+     * Register a custom macro.
+     *
+     * @param  string $name
+     * @param  object|callable  $macro
+     */
+    public static function macro(string $name, $macro)
+    {
+        static::$macros[$name] = $macro;
+    }
+
+    /**
+     * Mix another object into the class.
+     *
+     * @param  object  $mixin
+     */
+    public static function mixin($mixin)
+    {
+        $methods = (new ReflectionClass($mixin))->getMethods(
+            ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
+        );
+
+        foreach ($methods as $method) {
+            $method->setAccessible(true);
+
+            static::macro($method->name, $method->invoke($mixin));
+        }
+    }
+
+    public static function hasMacro(string $name): bool
+    {
+        return isset(static::$macros[$name]);
+    }
+
+    public static function __callStatic($method, $parameters)
+    {
+        if (! static::hasMacro($method)) {
+            throw new BadMethodCallException("Method {$method} does not exist.");
+        }
+
+        if (static::$macros[$method] instanceof Closure) {
+            return call_user_func_array(Closure::bind(static::$macros[$method], null, static::class), $parameters);
+        }
+
+        return call_user_func_array(static::$macros[$method], $parameters);
+    }
+
+    public function __call($method, $parameters)
+    {
+        if (! static::hasMacro($method)) {
+            throw new BadMethodCallException("Method {$method} does not exist.");
+        }
+
+        $macro = static::$macros[$method];
+
+        if ($macro instanceof Closure) {
+            return call_user_func_array($macro->bindTo($this, static::class), $parameters);
+        }
+
+        return call_user_func_array($macro, $parameters);
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/.gitignore b/vendor/symfony/event-dispatcher/.gitignore
new file mode 100755
index 0000000..c49a5d8
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/.gitignore
@@ -0,0 +1,3 @@
+vendor/
+composer.lock
+phpunit.xml
diff --git a/vendor/symfony/event-dispatcher/CHANGELOG.md b/vendor/symfony/event-dispatcher/CHANGELOG.md
new file mode 100755
index 0000000..bb42ee1
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/CHANGELOG.md
@@ -0,0 +1,23 @@
+CHANGELOG
+=========
+
+2.5.0
+-----
+
+ * added Debug\TraceableEventDispatcher (originally in HttpKernel)
+ * changed Debug\TraceableEventDispatcherInterface to extend EventDispatcherInterface
+ * added RegisterListenersPass (originally in HttpKernel)
+
+2.1.0
+-----
+
+ * added TraceableEventDispatcherInterface
+ * added ContainerAwareEventDispatcher
+ * added a reference to the EventDispatcher on the Event
+ * added a reference to the Event name on the event
+ * added fluid interface to the dispatch() method which now returns the Event
+   object
+ * added GenericEvent event class
+ * added the possibility for subscribers to subscribe several times for the
+   same event
+ * added ImmutableEventDispatcher
diff --git a/vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php b/vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php
new file mode 100755
index 0000000..4dcede7
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php
@@ -0,0 +1,183 @@
+<?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\EventDispatcher;
+
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Lazily loads listeners and subscribers from the dependency injection
+ * container.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ * @author Jordan Alliot <jordan.alliot@gmail.com>
+ */
+class ContainerAwareEventDispatcher extends EventDispatcher
+{
+    private $container;
+
+    /**
+     * The service IDs of the event listeners and subscribers.
+     */
+    private $listenerIds = array();
+
+    /**
+     * The services registered as listeners.
+     */
+    private $listeners = array();
+
+    public function __construct(ContainerInterface $container)
+    {
+        $this->container = $container;
+    }
+
+    /**
+     * Adds a service as event listener.
+     *
+     * @param string $eventName Event for which the listener is added
+     * @param array  $callback  The service ID of the listener service & the method
+     *                          name that has to be called
+     * @param int    $priority  The higher this value, the earlier an event listener
+     *                          will be triggered in the chain.
+     *                          Defaults to 0.
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function addListenerService($eventName, $callback, $priority = 0)
+    {
+        if (!\is_array($callback) || 2 !== \count($callback)) {
+            throw new \InvalidArgumentException('Expected an array("service", "method") argument');
+        }
+
+        $this->listenerIds[$eventName][] = array($callback[0], $callback[1], $priority);
+    }
+
+    public function removeListener($eventName, $listener)
+    {
+        $this->lazyLoad($eventName);
+
+        if (isset($this->listenerIds[$eventName])) {
+            foreach ($this->listenerIds[$eventName] as $i => $args) {
+                list($serviceId, $method) = $args;
+                $key = $serviceId.'.'.$method;
+                if (isset($this->listeners[$eventName][$key]) && $listener === array($this->listeners[$eventName][$key], $method)) {
+                    unset($this->listeners[$eventName][$key]);
+                    if (empty($this->listeners[$eventName])) {
+                        unset($this->listeners[$eventName]);
+                    }
+                    unset($this->listenerIds[$eventName][$i]);
+                    if (empty($this->listenerIds[$eventName])) {
+                        unset($this->listenerIds[$eventName]);
+                    }
+                }
+            }
+        }
+
+        parent::removeListener($eventName, $listener);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasListeners($eventName = null)
+    {
+        if (null === $eventName) {
+            return $this->listenerIds || $this->listeners || parent::hasListeners();
+        }
+
+        if (isset($this->listenerIds[$eventName])) {
+            return true;
+        }
+
+        return parent::hasListeners($eventName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getListeners($eventName = null)
+    {
+        if (null === $eventName) {
+            foreach ($this->listenerIds as $serviceEventName => $args) {
+                $this->lazyLoad($serviceEventName);
+            }
+        } else {
+            $this->lazyLoad($eventName);
+        }
+
+        return parent::getListeners($eventName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getListenerPriority($eventName, $listener)
+    {
+        $this->lazyLoad($eventName);
+
+        return parent::getListenerPriority($eventName, $listener);
+    }
+
+    /**
+     * Adds a service as event subscriber.
+     *
+     * @param string $serviceId The service ID of the subscriber service
+     * @param string $class     The service's class name (which must implement EventSubscriberInterface)
+     */
+    public function addSubscriberService($serviceId, $class)
+    {
+        foreach ($class::getSubscribedEvents() as $eventName => $params) {
+            if (\is_string($params)) {
+                $this->listenerIds[$eventName][] = array($serviceId, $params, 0);
+            } elseif (\is_string($params[0])) {
+                $this->listenerIds[$eventName][] = array($serviceId, $params[0], isset($params[1]) ? $params[1] : 0);
+            } else {
+                foreach ($params as $listener) {
+                    $this->listenerIds[$eventName][] = array($serviceId, $listener[0], isset($listener[1]) ? $listener[1] : 0);
+                }
+            }
+        }
+    }
+
+    public function getContainer()
+    {
+        return $this->container;
+    }
+
+    /**
+     * Lazily loads listeners for this event from the dependency injection
+     * container.
+     *
+     * @param string $eventName The name of the event to dispatch. The name of
+     *                          the event is the name of the method that is
+     *                          invoked on listeners.
+     */
+    protected function lazyLoad($eventName)
+    {
+        if (isset($this->listenerIds[$eventName])) {
+            foreach ($this->listenerIds[$eventName] as $args) {
+                list($serviceId, $method, $priority) = $args;
+                $listener = $this->container->get($serviceId);
+
+                $key = $serviceId.'.'.$method;
+                if (!isset($this->listeners[$eventName][$key])) {
+                    $this->addListener($eventName, array($listener, $method), $priority);
+                } elseif ($this->listeners[$eventName][$key] !== $listener) {
+                    parent::removeListener($eventName, array($this->listeners[$eventName][$key], $method));
+                    $this->addListener($eventName, array($listener, $method), $priority);
+                }
+
+                $this->listeners[$eventName][$key] = $listener;
+            }
+        }
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php b/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php
new file mode 100755
index 0000000..53d7c5d
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php
@@ -0,0 +1,375 @@
+<?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\EventDispatcher\Debug;
+
+use Psr\Log\LoggerInterface;
+use Symfony\Component\EventDispatcher\Event;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\Stopwatch\Stopwatch;
+
+/**
+ * Collects some data about event listeners.
+ *
+ * This event dispatcher delegates the dispatching to another one.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class TraceableEventDispatcher implements TraceableEventDispatcherInterface
+{
+    protected $logger;
+    protected $stopwatch;
+
+    private $called;
+    private $dispatcher;
+    private $wrappedListeners;
+
+    public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $stopwatch, LoggerInterface $logger = null)
+    {
+        $this->dispatcher = $dispatcher;
+        $this->stopwatch = $stopwatch;
+        $this->logger = $logger;
+        $this->called = array();
+        $this->wrappedListeners = array();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addListener($eventName, $listener, $priority = 0)
+    {
+        $this->dispatcher->addListener($eventName, $listener, $priority);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addSubscriber(EventSubscriberInterface $subscriber)
+    {
+        $this->dispatcher->addSubscriber($subscriber);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function removeListener($eventName, $listener)
+    {
+        if (isset($this->wrappedListeners[$eventName])) {
+            foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) {
+                if ($wrappedListener->getWrappedListener() === $listener) {
+                    $listener = $wrappedListener;
+                    unset($this->wrappedListeners[$eventName][$index]);
+                    break;
+                }
+            }
+        }
+
+        return $this->dispatcher->removeListener($eventName, $listener);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function removeSubscriber(EventSubscriberInterface $subscriber)
+    {
+        return $this->dispatcher->removeSubscriber($subscriber);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getListeners($eventName = null)
+    {
+        return $this->dispatcher->getListeners($eventName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getListenerPriority($eventName, $listener)
+    {
+        if (!method_exists($this->dispatcher, 'getListenerPriority')) {
+            return 0;
+        }
+
+        return $this->dispatcher->getListenerPriority($eventName, $listener);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasListeners($eventName = null)
+    {
+        return $this->dispatcher->hasListeners($eventName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dispatch($eventName, Event $event = null)
+    {
+        if (null === $event) {
+            $event = new Event();
+        }
+
+        if (null !== $this->logger && $event->isPropagationStopped()) {
+            $this->logger->debug(sprintf('The "%s" event is already stopped. No listeners have been called.', $eventName));
+        }
+
+        $this->preProcess($eventName);
+        $this->preDispatch($eventName, $event);
+
+        $e = $this->stopwatch->start($eventName, 'section');
+
+        $this->dispatcher->dispatch($eventName, $event);
+
+        if ($e->isStarted()) {
+            $e->stop();
+        }
+
+        $this->postDispatch($eventName, $event);
+        $this->postProcess($eventName);
+
+        return $event;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getCalledListeners()
+    {
+        $called = array();
+        foreach ($this->called as $eventName => $listeners) {
+            foreach ($listeners as $listener) {
+                $info = $this->getListenerInfo($listener->getWrappedListener(), $eventName);
+                $called[$eventName.'.'.$info['pretty']] = $info;
+            }
+        }
+
+        return $called;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getNotCalledListeners()
+    {
+        try {
+            $allListeners = $this->getListeners();
+        } catch (\Exception $e) {
+            if (null !== $this->logger) {
+                $this->logger->info('An exception was thrown while getting the uncalled listeners.', array('exception' => $e));
+            }
+
+            // unable to retrieve the uncalled listeners
+            return array();
+        }
+
+        $notCalled = array();
+        foreach ($allListeners as $eventName => $listeners) {
+            foreach ($listeners as $listener) {
+                $called = false;
+                if (isset($this->called[$eventName])) {
+                    foreach ($this->called[$eventName] as $l) {
+                        if ($l->getWrappedListener() === $listener) {
+                            $called = true;
+
+                            break;
+                        }
+                    }
+                }
+
+                if (!$called) {
+                    $info = $this->getListenerInfo($listener, $eventName);
+                    $notCalled[$eventName.'.'.$info['pretty']] = $info;
+                }
+            }
+        }
+
+        uasort($notCalled, array($this, 'sortListenersByPriority'));
+
+        return $notCalled;
+    }
+
+    /**
+     * Proxies all method calls to the original event dispatcher.
+     *
+     * @param string $method    The method name
+     * @param array  $arguments The method arguments
+     *
+     * @return mixed
+     */
+    public function __call($method, $arguments)
+    {
+        return \call_user_func_array(array($this->dispatcher, $method), $arguments);
+    }
+
+    /**
+     * Called before dispatching the event.
+     *
+     * @param string $eventName The event name
+     * @param Event  $event     The event
+     */
+    protected function preDispatch($eventName, Event $event)
+    {
+    }
+
+    /**
+     * Called after dispatching the event.
+     *
+     * @param string $eventName The event name
+     * @param Event  $event     The event
+     */
+    protected function postDispatch($eventName, Event $event)
+    {
+    }
+
+    private function preProcess($eventName)
+    {
+        foreach ($this->dispatcher->getListeners($eventName) as $listener) {
+            $info = $this->getListenerInfo($listener, $eventName);
+            $name = isset($info['class']) ? $info['class'] : $info['type'];
+            $wrappedListener = new WrappedListener($listener, $name, $this->stopwatch, $this);
+            $this->wrappedListeners[$eventName][] = $wrappedListener;
+            $this->dispatcher->removeListener($eventName, $listener);
+            $this->dispatcher->addListener($eventName, $wrappedListener, $info['priority']);
+        }
+    }
+
+    private function postProcess($eventName)
+    {
+        unset($this->wrappedListeners[$eventName]);
+        $skipped = false;
+        foreach ($this->dispatcher->getListeners($eventName) as $listener) {
+            if (!$listener instanceof WrappedListener) { // #12845: a new listener was added during dispatch.
+                continue;
+            }
+            // Unwrap listener
+            $priority = $this->getListenerPriority($eventName, $listener);
+            $this->dispatcher->removeListener($eventName, $listener);
+            $this->dispatcher->addListener($eventName, $listener->getWrappedListener(), $priority);
+
+            $info = $this->getListenerInfo($listener->getWrappedListener(), $eventName);
+            if ($listener->wasCalled()) {
+                if (null !== $this->logger) {
+                    $this->logger->debug(sprintf('Notified event "%s" to listener "%s".', $eventName, $info['pretty']));
+                }
+
+                if (!isset($this->called[$eventName])) {
+                    $this->called[$eventName] = new \SplObjectStorage();
+                }
+
+                $this->called[$eventName]->attach($listener);
+            }
+
+            if (null !== $this->logger && $skipped) {
+                $this->logger->debug(sprintf('Listener "%s" was not called for event "%s".', $info['pretty'], $eventName));
+            }
+
+            if ($listener->stoppedPropagation()) {
+                if (null !== $this->logger) {
+                    $this->logger->debug(sprintf('Listener "%s" stopped propagation of the event "%s".', $info['pretty'], $eventName));
+                }
+
+                $skipped = true;
+            }
+        }
+    }
+
+    /**
+     * Returns information about the listener.
+     *
+     * @param object $listener  The listener
+     * @param string $eventName The event name
+     *
+     * @return array Information about the listener
+     */
+    private function getListenerInfo($listener, $eventName)
+    {
+        $info = array(
+            'event' => $eventName,
+            'priority' => $this->getListenerPriority($eventName, $listener),
+        );
+
+        // unwrap for correct listener info
+        if ($listener instanceof WrappedListener) {
+            $listener = $listener->getWrappedListener();
+        }
+
+        if ($listener instanceof \Closure) {
+            $info += array(
+                'type' => 'Closure',
+                'pretty' => 'closure',
+            );
+        } elseif (\is_string($listener)) {
+            try {
+                $r = new \ReflectionFunction($listener);
+                $file = $r->getFileName();
+                $line = $r->getStartLine();
+            } catch (\ReflectionException $e) {
+                $file = null;
+                $line = null;
+            }
+            $info += array(
+                'type' => 'Function',
+                'function' => $listener,
+                'file' => $file,
+                'line' => $line,
+                'pretty' => $listener,
+            );
+        } elseif (\is_array($listener) || (\is_object($listener) && \is_callable($listener))) {
+            if (!\is_array($listener)) {
+                $listener = array($listener, '__invoke');
+            }
+            $class = \is_object($listener[0]) ? \get_class($listener[0]) : $listener[0];
+            try {
+                $r = new \ReflectionMethod($class, $listener[1]);
+                $file = $r->getFileName();
+                $line = $r->getStartLine();
+            } catch (\ReflectionException $e) {
+                $file = null;
+                $line = null;
+            }
+            $info += array(
+                'type' => 'Method',
+                'class' => $class,
+                'method' => $listener[1],
+                'file' => $file,
+                'line' => $line,
+                'pretty' => $class.'::'.$listener[1],
+            );
+        }
+
+        return $info;
+    }
+
+    private function sortListenersByPriority($a, $b)
+    {
+        if (\is_int($a['priority']) && !\is_int($b['priority'])) {
+            return 1;
+        }
+
+        if (!\is_int($a['priority']) && \is_int($b['priority'])) {
+            return -1;
+        }
+
+        if ($a['priority'] === $b['priority']) {
+            return 0;
+        }
+
+        if ($a['priority'] > $b['priority']) {
+            return -1;
+        }
+
+        return 1;
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php b/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php
new file mode 100755
index 0000000..5483e81
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php
@@ -0,0 +1,34 @@
+<?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\EventDispatcher\Debug;
+
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+
+/**
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface TraceableEventDispatcherInterface extends EventDispatcherInterface
+{
+    /**
+     * Gets the called listeners.
+     *
+     * @return array An array of called listeners
+     */
+    public function getCalledListeners();
+
+    /**
+     * Gets the not called listeners.
+     *
+     * @return array An array of not called listeners
+     */
+    public function getNotCalledListeners();
+}
diff --git a/vendor/symfony/event-dispatcher/Debug/WrappedListener.php b/vendor/symfony/event-dispatcher/Debug/WrappedListener.php
new file mode 100755
index 0000000..1552af0
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/Debug/WrappedListener.php
@@ -0,0 +1,71 @@
+<?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\EventDispatcher\Debug;
+
+use Symfony\Component\EventDispatcher\Event;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\Stopwatch\Stopwatch;
+
+/**
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class WrappedListener
+{
+    private $listener;
+    private $name;
+    private $called;
+    private $stoppedPropagation;
+    private $stopwatch;
+    private $dispatcher;
+
+    public function __construct($listener, $name, Stopwatch $stopwatch, EventDispatcherInterface $dispatcher = null)
+    {
+        $this->listener = $listener;
+        $this->name = $name;
+        $this->stopwatch = $stopwatch;
+        $this->dispatcher = $dispatcher;
+        $this->called = false;
+        $this->stoppedPropagation = false;
+    }
+
+    public function getWrappedListener()
+    {
+        return $this->listener;
+    }
+
+    public function wasCalled()
+    {
+        return $this->called;
+    }
+
+    public function stoppedPropagation()
+    {
+        return $this->stoppedPropagation;
+    }
+
+    public function __invoke(Event $event, $eventName, EventDispatcherInterface $dispatcher)
+    {
+        $this->called = true;
+
+        $e = $this->stopwatch->start($this->name, 'event_listener');
+
+        \call_user_func($this->listener, $event, $eventName, $this->dispatcher ?: $dispatcher);
+
+        if ($e->isStarted()) {
+            $e->stop();
+        }
+
+        if ($event->isPropagationStopped()) {
+            $this->stoppedPropagation = true;
+        }
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php b/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php
new file mode 100755
index 0000000..5a94ae8
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php
@@ -0,0 +1,100 @@
+<?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\EventDispatcher\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+/**
+ * Compiler pass to register tagged services for an event dispatcher.
+ */
+class RegisterListenersPass implements CompilerPassInterface
+{
+    protected $dispatcherService;
+    protected $listenerTag;
+    protected $subscriberTag;
+
+    /**
+     * @param string $dispatcherService Service name of the event dispatcher in processed container
+     * @param string $listenerTag       Tag name used for listener
+     * @param string $subscriberTag     Tag name used for subscribers
+     */
+    public function __construct($dispatcherService = 'event_dispatcher', $listenerTag = 'kernel.event_listener', $subscriberTag = 'kernel.event_subscriber')
+    {
+        $this->dispatcherService = $dispatcherService;
+        $this->listenerTag = $listenerTag;
+        $this->subscriberTag = $subscriberTag;
+    }
+
+    public function process(ContainerBuilder $container)
+    {
+        if (!$container->hasDefinition($this->dispatcherService) && !$container->hasAlias($this->dispatcherService)) {
+            return;
+        }
+
+        $definition = $container->findDefinition($this->dispatcherService);
+
+        foreach ($container->findTaggedServiceIds($this->listenerTag) as $id => $events) {
+            $def = $container->getDefinition($id);
+            if (!$def->isPublic()) {
+                throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event listeners are lazy-loaded.', $id));
+            }
+
+            if ($def->isAbstract()) {
+                throw new \InvalidArgumentException(sprintf('The service "%s" must not be abstract as event listeners are lazy-loaded.', $id));
+            }
+
+            foreach ($events as $event) {
+                $priority = isset($event['priority']) ? $event['priority'] : 0;
+
+                if (!isset($event['event'])) {
+                    throw new \InvalidArgumentException(sprintf('Service "%s" must define the "event" attribute on "%s" tags.', $id, $this->listenerTag));
+                }
+
+                if (!isset($event['method'])) {
+                    $event['method'] = 'on'.preg_replace_callback(array(
+                        '/(?<=\b)[a-z]/i',
+                        '/[^a-z0-9]/i',
+                    ), function ($matches) { return strtoupper($matches[0]); }, $event['event']);
+                    $event['method'] = preg_replace('/[^a-z0-9]/i', '', $event['method']);
+                }
+
+                $definition->addMethodCall('addListenerService', array($event['event'], array($id, $event['method']), $priority));
+            }
+        }
+
+        foreach ($container->findTaggedServiceIds($this->subscriberTag) as $id => $attributes) {
+            $def = $container->getDefinition($id);
+            if (!$def->isPublic()) {
+                throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event subscribers are lazy-loaded.', $id));
+            }
+
+            if ($def->isAbstract()) {
+                throw new \InvalidArgumentException(sprintf('The service "%s" must not be abstract as event subscribers are lazy-loaded.', $id));
+            }
+
+            // We must assume that the class value has been correctly filled, even if the service is created by a factory
+            $class = $container->getParameterBag()->resolveValue($def->getClass());
+            $interface = 'Symfony\Component\EventDispatcher\EventSubscriberInterface';
+
+            if (!is_subclass_of($class, $interface)) {
+                if (!class_exists($class, false)) {
+                    throw new \InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
+                }
+
+                throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface));
+            }
+
+            $definition->addMethodCall('addSubscriberService', array($id, $class));
+        }
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/Event.php b/vendor/symfony/event-dispatcher/Event.php
new file mode 100755
index 0000000..320919a
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/Event.php
@@ -0,0 +1,120 @@
+<?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\EventDispatcher;
+
+/**
+ * Event is the base class for classes containing event data.
+ *
+ * This class contains no event data. It is used by events that do not pass
+ * state information to an event handler when an event is raised.
+ *
+ * You can call the method stopPropagation() to abort the execution of
+ * further listeners in your event listener.
+ *
+ * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
+ * @author Jonathan Wage <jonwage@gmail.com>
+ * @author Roman Borschel <roman@code-factory.org>
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class Event
+{
+    /**
+     * @var bool Whether no further event listeners should be triggered
+     */
+    private $propagationStopped = false;
+
+    /**
+     * @var EventDispatcherInterface Dispatcher that dispatched this event
+     */
+    private $dispatcher;
+
+    /**
+     * @var string This event's name
+     */
+    private $name;
+
+    /**
+     * Returns whether further event listeners should be triggered.
+     *
+     * @see Event::stopPropagation()
+     *
+     * @return bool Whether propagation was already stopped for this event
+     */
+    public function isPropagationStopped()
+    {
+        return $this->propagationStopped;
+    }
+
+    /**
+     * Stops the propagation of the event to further event listeners.
+     *
+     * If multiple event listeners are connected to the same event, no
+     * further event listener will be triggered once any trigger calls
+     * stopPropagation().
+     */
+    public function stopPropagation()
+    {
+        $this->propagationStopped = true;
+    }
+
+    /**
+     * Stores the EventDispatcher that dispatches this Event.
+     *
+     * @param EventDispatcherInterface $dispatcher
+     *
+     * @deprecated since version 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call.
+     */
+    public function setDispatcher(EventDispatcherInterface $dispatcher)
+    {
+        $this->dispatcher = $dispatcher;
+    }
+
+    /**
+     * Returns the EventDispatcher that dispatches this Event.
+     *
+     * @return EventDispatcherInterface
+     *
+     * @deprecated since version 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call.
+     */
+    public function getDispatcher()
+    {
+        @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.4 and will be removed in 3.0. The event dispatcher instance can be received in the listener call instead.', E_USER_DEPRECATED);
+
+        return $this->dispatcher;
+    }
+
+    /**
+     * Gets the event's name.
+     *
+     * @return string
+     *
+     * @deprecated since version 2.4, to be removed in 3.0. The event name is passed to the listener call.
+     */
+    public function getName()
+    {
+        @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.4 and will be removed in 3.0. The event name can be received in the listener call instead.', E_USER_DEPRECATED);
+
+        return $this->name;
+    }
+
+    /**
+     * Sets the event's name property.
+     *
+     * @param string $name The event name
+     *
+     * @deprecated since version 2.4, to be removed in 3.0. The event name is passed to the listener call.
+     */
+    public function setName($name)
+    {
+        $this->name = $name;
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/EventDispatcher.php b/vendor/symfony/event-dispatcher/EventDispatcher.php
new file mode 100755
index 0000000..b41b98e
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/EventDispatcher.php
@@ -0,0 +1,198 @@
+<?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\EventDispatcher;
+
+/**
+ * The EventDispatcherInterface is the central point of Symfony's event listener system.
+ *
+ * Listeners are registered on the manager and events are dispatched through the
+ * manager.
+ *
+ * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
+ * @author Jonathan Wage <jonwage@gmail.com>
+ * @author Roman Borschel <roman@code-factory.org>
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ * @author Jordan Alliot <jordan.alliot@gmail.com>
+ */
+class EventDispatcher implements EventDispatcherInterface
+{
+    private $listeners = array();
+    private $sorted = array();
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dispatch($eventName, Event $event = null)
+    {
+        if (null === $event) {
+            $event = new Event();
+        }
+
+        $event->setDispatcher($this);
+        $event->setName($eventName);
+
+        if ($listeners = $this->getListeners($eventName)) {
+            $this->doDispatch($listeners, $eventName, $event);
+        }
+
+        return $event;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getListeners($eventName = null)
+    {
+        if (null !== $eventName) {
+            if (!isset($this->listeners[$eventName])) {
+                return array();
+            }
+
+            if (!isset($this->sorted[$eventName])) {
+                $this->sortListeners($eventName);
+            }
+
+            return $this->sorted[$eventName];
+        }
+
+        foreach ($this->listeners as $eventName => $eventListeners) {
+            if (!isset($this->sorted[$eventName])) {
+                $this->sortListeners($eventName);
+            }
+        }
+
+        return array_filter($this->sorted);
+    }
+
+    /**
+     * Gets the listener priority for a specific event.
+     *
+     * Returns null if the event or the listener does not exist.
+     *
+     * @param string   $eventName The name of the event
+     * @param callable $listener  The listener
+     *
+     * @return int|null The event listener priority
+     */
+    public function getListenerPriority($eventName, $listener)
+    {
+        if (!isset($this->listeners[$eventName])) {
+            return;
+        }
+
+        foreach ($this->listeners[$eventName] as $priority => $listeners) {
+            if (false !== \in_array($listener, $listeners, true)) {
+                return $priority;
+            }
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasListeners($eventName = null)
+    {
+        return (bool) $this->getListeners($eventName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addListener($eventName, $listener, $priority = 0)
+    {
+        $this->listeners[$eventName][$priority][] = $listener;
+        unset($this->sorted[$eventName]);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function removeListener($eventName, $listener)
+    {
+        if (!isset($this->listeners[$eventName])) {
+            return;
+        }
+
+        foreach ($this->listeners[$eventName] as $priority => $listeners) {
+            if (false !== ($key = array_search($listener, $listeners, true))) {
+                unset($this->listeners[$eventName][$priority][$key], $this->sorted[$eventName]);
+            }
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addSubscriber(EventSubscriberInterface $subscriber)
+    {
+        foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
+            if (\is_string($params)) {
+                $this->addListener($eventName, array($subscriber, $params));
+            } elseif (\is_string($params[0])) {
+                $this->addListener($eventName, array($subscriber, $params[0]), isset($params[1]) ? $params[1] : 0);
+            } else {
+                foreach ($params as $listener) {
+                    $this->addListener($eventName, array($subscriber, $listener[0]), isset($listener[1]) ? $listener[1] : 0);
+                }
+            }
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function removeSubscriber(EventSubscriberInterface $subscriber)
+    {
+        foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
+            if (\is_array($params) && \is_array($params[0])) {
+                foreach ($params as $listener) {
+                    $this->removeListener($eventName, array($subscriber, $listener[0]));
+                }
+            } else {
+                $this->removeListener($eventName, array($subscriber, \is_string($params) ? $params : $params[0]));
+            }
+        }
+    }
+
+    /**
+     * Triggers the listeners of an event.
+     *
+     * This method can be overridden to add functionality that is executed
+     * for each listener.
+     *
+     * @param callable[] $listeners The event listeners
+     * @param string     $eventName The name of the event to dispatch
+     * @param Event      $event     The event object to pass to the event handlers/listeners
+     */
+    protected function doDispatch($listeners, $eventName, Event $event)
+    {
+        foreach ($listeners as $listener) {
+            if ($event->isPropagationStopped()) {
+                break;
+            }
+            \call_user_func($listener, $event, $eventName, $this);
+        }
+    }
+
+    /**
+     * Sorts the internal list of listeners for the given event by priority.
+     *
+     * @param string $eventName The name of the event
+     */
+    private function sortListeners($eventName)
+    {
+        krsort($this->listeners[$eventName]);
+        $this->sorted[$eventName] = \call_user_func_array('array_merge', $this->listeners[$eventName]);
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/EventDispatcherInterface.php b/vendor/symfony/event-dispatcher/EventDispatcherInterface.php
new file mode 100755
index 0000000..60160a9
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/EventDispatcherInterface.php
@@ -0,0 +1,81 @@
+<?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\EventDispatcher;
+
+/**
+ * The EventDispatcherInterface is the central point of Symfony's event listener system.
+ * Listeners are registered on the manager and events are dispatched through the
+ * manager.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+interface EventDispatcherInterface
+{
+    /**
+     * Dispatches an event to all registered listeners.
+     *
+     * @param string $eventName The name of the event to dispatch. The name of
+     *                          the event is the name of the method that is
+     *                          invoked on listeners.
+     * @param Event  $event     The event to pass to the event handlers/listeners
+     *                          If not supplied, an empty Event instance is created
+     *
+     * @return Event
+     */
+    public function dispatch($eventName, Event $event = null);
+
+    /**
+     * Adds an event listener that listens on the specified events.
+     *
+     * @param string   $eventName The event to listen on
+     * @param callable $listener  The listener
+     * @param int      $priority  The higher this value, the earlier an event
+     *                            listener will be triggered in the chain (defaults to 0)
+     */
+    public function addListener($eventName, $listener, $priority = 0);
+
+    /**
+     * Adds an event subscriber.
+     *
+     * The subscriber is asked for all the events he is
+     * interested in and added as a listener for these events.
+     */
+    public function addSubscriber(EventSubscriberInterface $subscriber);
+
+    /**
+     * Removes an event listener from the specified events.
+     *
+     * @param string   $eventName The event to remove a listener from
+     * @param callable $listener  The listener to remove
+     */
+    public function removeListener($eventName, $listener);
+
+    public function removeSubscriber(EventSubscriberInterface $subscriber);
+
+    /**
+     * Gets the listeners of a specific event or all listeners sorted by descending priority.
+     *
+     * @param string $eventName The name of the event
+     *
+     * @return array The event listeners for the specified event, or all event listeners by event name
+     */
+    public function getListeners($eventName = null);
+
+    /**
+     * Checks whether an event has any registered listeners.
+     *
+     * @param string $eventName The name of the event
+     *
+     * @return bool true if the specified event has any listeners, false otherwise
+     */
+    public function hasListeners($eventName = null);
+}
diff --git a/vendor/symfony/event-dispatcher/EventSubscriberInterface.php b/vendor/symfony/event-dispatcher/EventSubscriberInterface.php
new file mode 100755
index 0000000..8af7789
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/EventSubscriberInterface.php
@@ -0,0 +1,46 @@
+<?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\EventDispatcher;
+
+/**
+ * An EventSubscriber knows himself what events he is interested in.
+ * If an EventSubscriber is added to an EventDispatcherInterface, the manager invokes
+ * {@link getSubscribedEvents} and registers the subscriber as a listener for all
+ * returned events.
+ *
+ * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
+ * @author Jonathan Wage <jonwage@gmail.com>
+ * @author Roman Borschel <roman@code-factory.org>
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+interface EventSubscriberInterface
+{
+    /**
+     * Returns an array of event names this subscriber wants to listen to.
+     *
+     * The array keys are event names and the value can be:
+     *
+     *  * The method name to call (priority defaults to 0)
+     *  * An array composed of the method name to call and the priority
+     *  * An array of arrays composed of the method names to call and respective
+     *    priorities, or 0 if unset
+     *
+     * For instance:
+     *
+     *  * array('eventName' => 'methodName')
+     *  * array('eventName' => array('methodName', $priority))
+     *  * array('eventName' => array(array('methodName1', $priority), array('methodName2')))
+     *
+     * @return array The event names to listen to
+     */
+    public static function getSubscribedEvents();
+}
diff --git a/vendor/symfony/event-dispatcher/GenericEvent.php b/vendor/symfony/event-dispatcher/GenericEvent.php
new file mode 100755
index 0000000..f0be7e1
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/GenericEvent.php
@@ -0,0 +1,175 @@
+<?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\EventDispatcher;
+
+/**
+ * Event encapsulation class.
+ *
+ * Encapsulates events thus decoupling the observer from the subject they encapsulate.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate
+{
+    protected $subject;
+    protected $arguments;
+
+    /**
+     * Encapsulate an event with $subject and $args.
+     *
+     * @param mixed $subject   The subject of the event, usually an object or a callable
+     * @param array $arguments Arguments to store in the event
+     */
+    public function __construct($subject = null, array $arguments = array())
+    {
+        $this->subject = $subject;
+        $this->arguments = $arguments;
+    }
+
+    /**
+     * Getter for subject property.
+     *
+     * @return mixed The observer subject
+     */
+    public function getSubject()
+    {
+        return $this->subject;
+    }
+
+    /**
+     * Get argument by key.
+     *
+     * @param string $key Key
+     *
+     * @return mixed Contents of array key
+     *
+     * @throws \InvalidArgumentException if key is not found
+     */
+    public function getArgument($key)
+    {
+        if ($this->hasArgument($key)) {
+            return $this->arguments[$key];
+        }
+
+        throw new \InvalidArgumentException(sprintf('Argument "%s" not found.', $key));
+    }
+
+    /**
+     * Add argument to event.
+     *
+     * @param string $key   Argument name
+     * @param mixed  $value Value
+     *
+     * @return $this
+     */
+    public function setArgument($key, $value)
+    {
+        $this->arguments[$key] = $value;
+
+        return $this;
+    }
+
+    /**
+     * Getter for all arguments.
+     *
+     * @return array
+     */
+    public function getArguments()
+    {
+        return $this->arguments;
+    }
+
+    /**
+     * Set args property.
+     *
+     * @param array $args Arguments
+     *
+     * @return $this
+     */
+    public function setArguments(array $args = array())
+    {
+        $this->arguments = $args;
+
+        return $this;
+    }
+
+    /**
+     * Has argument.
+     *
+     * @param string $key Key of arguments array
+     *
+     * @return bool
+     */
+    public function hasArgument($key)
+    {
+        return array_key_exists($key, $this->arguments);
+    }
+
+    /**
+     * ArrayAccess for argument getter.
+     *
+     * @param string $key Array key
+     *
+     * @return mixed
+     *
+     * @throws \InvalidArgumentException if key does not exist in $this->args
+     */
+    public function offsetGet($key)
+    {
+        return $this->getArgument($key);
+    }
+
+    /**
+     * ArrayAccess for argument setter.
+     *
+     * @param string $key   Array key to set
+     * @param mixed  $value Value
+     */
+    public function offsetSet($key, $value)
+    {
+        $this->setArgument($key, $value);
+    }
+
+    /**
+     * ArrayAccess for unset argument.
+     *
+     * @param string $key Array key
+     */
+    public function offsetUnset($key)
+    {
+        if ($this->hasArgument($key)) {
+            unset($this->arguments[$key]);
+        }
+    }
+
+    /**
+     * ArrayAccess has argument.
+     *
+     * @param string $key Array key
+     *
+     * @return bool
+     */
+    public function offsetExists($key)
+    {
+        return $this->hasArgument($key);
+    }
+
+    /**
+     * IteratorAggregate for iterating over the object like an array.
+     *
+     * @return \ArrayIterator
+     */
+    public function getIterator()
+    {
+        return new \ArrayIterator($this->arguments);
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php b/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php
new file mode 100755
index 0000000..b3cf56c
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php
@@ -0,0 +1,91 @@
+<?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\EventDispatcher;
+
+/**
+ * A read-only proxy for an event dispatcher.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class ImmutableEventDispatcher implements EventDispatcherInterface
+{
+    private $dispatcher;
+
+    public function __construct(EventDispatcherInterface $dispatcher)
+    {
+        $this->dispatcher = $dispatcher;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dispatch($eventName, Event $event = null)
+    {
+        return $this->dispatcher->dispatch($eventName, $event);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addListener($eventName, $listener, $priority = 0)
+    {
+        throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addSubscriber(EventSubscriberInterface $subscriber)
+    {
+        throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function removeListener($eventName, $listener)
+    {
+        throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function removeSubscriber(EventSubscriberInterface $subscriber)
+    {
+        throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getListeners($eventName = null)
+    {
+        return $this->dispatcher->getListeners($eventName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getListenerPriority($eventName, $listener)
+    {
+        return $this->dispatcher->getListenerPriority($eventName, $listener);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasListeners($eventName = null)
+    {
+        return $this->dispatcher->hasListeners($eventName);
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/LICENSE b/vendor/symfony/event-dispatcher/LICENSE
new file mode 100755
index 0000000..21d7fb9
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2004-2018 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.
diff --git a/vendor/symfony/event-dispatcher/README.md b/vendor/symfony/event-dispatcher/README.md
new file mode 100755
index 0000000..185c3fe
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/README.md
@@ -0,0 +1,15 @@
+EventDispatcher Component
+=========================
+
+The EventDispatcher component provides tools that allow your application
+components to communicate with each other by dispatching events and listening to
+them.
+
+Resources
+---------
+
+  * [Documentation](https://symfony.com/doc/current/components/event_dispatcher/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)
diff --git a/vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php b/vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php
new file mode 100755
index 0000000..48de632
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php
@@ -0,0 +1,398 @@
+<?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\EventDispatcher\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\EventDispatcher\Event;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+abstract class AbstractEventDispatcherTest extends TestCase
+{
+    /* Some pseudo events */
+    const preFoo = 'pre.foo';
+    const postFoo = 'post.foo';
+    const preBar = 'pre.bar';
+    const postBar = 'post.bar';
+
+    /**
+     * @var EventDispatcher
+     */
+    private $dispatcher;
+
+    private $listener;
+
+    protected function setUp()
+    {
+        $this->dispatcher = $this->createEventDispatcher();
+        $this->listener = new TestEventListener();
+    }
+
+    protected function tearDown()
+    {
+        $this->dispatcher = null;
+        $this->listener = null;
+    }
+
+    abstract protected function createEventDispatcher();
+
+    public function testInitialState()
+    {
+        $this->assertEquals(array(), $this->dispatcher->getListeners());
+        $this->assertFalse($this->dispatcher->hasListeners(self::preFoo));
+        $this->assertFalse($this->dispatcher->hasListeners(self::postFoo));
+    }
+
+    public function testAddListener()
+    {
+        $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo'));
+        $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo'));
+        $this->assertTrue($this->dispatcher->hasListeners());
+        $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
+        $this->assertTrue($this->dispatcher->hasListeners(self::postFoo));
+        $this->assertCount(1, $this->dispatcher->getListeners(self::preFoo));
+        $this->assertCount(1, $this->dispatcher->getListeners(self::postFoo));
+        $this->assertCount(2, $this->dispatcher->getListeners());
+    }
+
+    public function testGetListenersSortsByPriority()
+    {
+        $listener1 = new TestEventListener();
+        $listener2 = new TestEventListener();
+        $listener3 = new TestEventListener();
+        $listener1->name = '1';
+        $listener2->name = '2';
+        $listener3->name = '3';
+
+        $this->dispatcher->addListener('pre.foo', array($listener1, 'preFoo'), -10);
+        $this->dispatcher->addListener('pre.foo', array($listener2, 'preFoo'), 10);
+        $this->dispatcher->addListener('pre.foo', array($listener3, 'preFoo'));
+
+        $expected = array(
+            array($listener2, 'preFoo'),
+            array($listener3, 'preFoo'),
+            array($listener1, 'preFoo'),
+        );
+
+        $this->assertSame($expected, $this->dispatcher->getListeners('pre.foo'));
+    }
+
+    public function testGetAllListenersSortsByPriority()
+    {
+        $listener1 = new TestEventListener();
+        $listener2 = new TestEventListener();
+        $listener3 = new TestEventListener();
+        $listener4 = new TestEventListener();
+        $listener5 = new TestEventListener();
+        $listener6 = new TestEventListener();
+
+        $this->dispatcher->addListener('pre.foo', $listener1, -10);
+        $this->dispatcher->addListener('pre.foo', $listener2);
+        $this->dispatcher->addListener('pre.foo', $listener3, 10);
+        $this->dispatcher->addListener('post.foo', $listener4, -10);
+        $this->dispatcher->addListener('post.foo', $listener5);
+        $this->dispatcher->addListener('post.foo', $listener6, 10);
+
+        $expected = array(
+            'pre.foo' => array($listener3, $listener2, $listener1),
+            'post.foo' => array($listener6, $listener5, $listener4),
+        );
+
+        $this->assertSame($expected, $this->dispatcher->getListeners());
+    }
+
+    public function testGetListenerPriority()
+    {
+        $listener1 = new TestEventListener();
+        $listener2 = new TestEventListener();
+
+        $this->dispatcher->addListener('pre.foo', $listener1, -10);
+        $this->dispatcher->addListener('pre.foo', $listener2);
+
+        $this->assertSame(-10, $this->dispatcher->getListenerPriority('pre.foo', $listener1));
+        $this->assertSame(0, $this->dispatcher->getListenerPriority('pre.foo', $listener2));
+        $this->assertNull($this->dispatcher->getListenerPriority('pre.bar', $listener2));
+        $this->assertNull($this->dispatcher->getListenerPriority('pre.foo', function () {}));
+    }
+
+    public function testDispatch()
+    {
+        $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo'));
+        $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo'));
+        $this->dispatcher->dispatch(self::preFoo);
+        $this->assertTrue($this->listener->preFooInvoked);
+        $this->assertFalse($this->listener->postFooInvoked);
+        $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch('noevent'));
+        $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(self::preFoo));
+        $event = new Event();
+        $return = $this->dispatcher->dispatch(self::preFoo, $event);
+        $this->assertSame($event, $return);
+    }
+
+    /**
+     * @group legacy
+     */
+    public function testLegacyDispatch()
+    {
+        $event = new Event();
+        $this->dispatcher->dispatch(self::preFoo, $event);
+        $this->assertEquals('pre.foo', $event->getName());
+    }
+
+    public function testDispatchForClosure()
+    {
+        $invoked = 0;
+        $listener = function () use (&$invoked) {
+            ++$invoked;
+        };
+        $this->dispatcher->addListener('pre.foo', $listener);
+        $this->dispatcher->addListener('post.foo', $listener);
+        $this->dispatcher->dispatch(self::preFoo);
+        $this->assertEquals(1, $invoked);
+    }
+
+    public function testStopEventPropagation()
+    {
+        $otherListener = new TestEventListener();
+
+        // postFoo() stops the propagation, so only one listener should
+        // be executed
+        // Manually set priority to enforce $this->listener to be called first
+        $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo'), 10);
+        $this->dispatcher->addListener('post.foo', array($otherListener, 'postFoo'));
+        $this->dispatcher->dispatch(self::postFoo);
+        $this->assertTrue($this->listener->postFooInvoked);
+        $this->assertFalse($otherListener->postFooInvoked);
+    }
+
+    public function testDispatchByPriority()
+    {
+        $invoked = array();
+        $listener1 = function () use (&$invoked) {
+            $invoked[] = '1';
+        };
+        $listener2 = function () use (&$invoked) {
+            $invoked[] = '2';
+        };
+        $listener3 = function () use (&$invoked) {
+            $invoked[] = '3';
+        };
+        $this->dispatcher->addListener('pre.foo', $listener1, -10);
+        $this->dispatcher->addListener('pre.foo', $listener2);
+        $this->dispatcher->addListener('pre.foo', $listener3, 10);
+        $this->dispatcher->dispatch(self::preFoo);
+        $this->assertEquals(array('3', '2', '1'), $invoked);
+    }
+
+    public function testRemoveListener()
+    {
+        $this->dispatcher->addListener('pre.bar', $this->listener);
+        $this->assertTrue($this->dispatcher->hasListeners(self::preBar));
+        $this->dispatcher->removeListener('pre.bar', $this->listener);
+        $this->assertFalse($this->dispatcher->hasListeners(self::preBar));
+        $this->dispatcher->removeListener('notExists', $this->listener);
+    }
+
+    public function testAddSubscriber()
+    {
+        $eventSubscriber = new TestEventSubscriber();
+        $this->dispatcher->addSubscriber($eventSubscriber);
+        $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
+        $this->assertTrue($this->dispatcher->hasListeners(self::postFoo));
+    }
+
+    public function testAddSubscriberWithPriorities()
+    {
+        $eventSubscriber = new TestEventSubscriber();
+        $this->dispatcher->addSubscriber($eventSubscriber);
+
+        $eventSubscriber = new TestEventSubscriberWithPriorities();
+        $this->dispatcher->addSubscriber($eventSubscriber);
+
+        $listeners = $this->dispatcher->getListeners('pre.foo');
+        $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
+        $this->assertCount(2, $listeners);
+        $this->assertInstanceOf('Symfony\Component\EventDispatcher\Tests\TestEventSubscriberWithPriorities', $listeners[0][0]);
+    }
+
+    public function testAddSubscriberWithMultipleListeners()
+    {
+        $eventSubscriber = new TestEventSubscriberWithMultipleListeners();
+        $this->dispatcher->addSubscriber($eventSubscriber);
+
+        $listeners = $this->dispatcher->getListeners('pre.foo');
+        $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
+        $this->assertCount(2, $listeners);
+        $this->assertEquals('preFoo2', $listeners[0][1]);
+    }
+
+    public function testRemoveSubscriber()
+    {
+        $eventSubscriber = new TestEventSubscriber();
+        $this->dispatcher->addSubscriber($eventSubscriber);
+        $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
+        $this->assertTrue($this->dispatcher->hasListeners(self::postFoo));
+        $this->dispatcher->removeSubscriber($eventSubscriber);
+        $this->assertFalse($this->dispatcher->hasListeners(self::preFoo));
+        $this->assertFalse($this->dispatcher->hasListeners(self::postFoo));
+    }
+
+    public function testRemoveSubscriberWithPriorities()
+    {
+        $eventSubscriber = new TestEventSubscriberWithPriorities();
+        $this->dispatcher->addSubscriber($eventSubscriber);
+        $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
+        $this->dispatcher->removeSubscriber($eventSubscriber);
+        $this->assertFalse($this->dispatcher->hasListeners(self::preFoo));
+    }
+
+    public function testRemoveSubscriberWithMultipleListeners()
+    {
+        $eventSubscriber = new TestEventSubscriberWithMultipleListeners();
+        $this->dispatcher->addSubscriber($eventSubscriber);
+        $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
+        $this->assertCount(2, $this->dispatcher->getListeners(self::preFoo));
+        $this->dispatcher->removeSubscriber($eventSubscriber);
+        $this->assertFalse($this->dispatcher->hasListeners(self::preFoo));
+    }
+
+    /**
+     * @group legacy
+     */
+    public function testLegacyEventReceivesTheDispatcherInstance()
+    {
+        $dispatcher = null;
+        $this->dispatcher->addListener('test', function ($event) use (&$dispatcher) {
+            $dispatcher = $event->getDispatcher();
+        });
+        $this->dispatcher->dispatch('test');
+        $this->assertSame($this->dispatcher, $dispatcher);
+    }
+
+    public function testEventReceivesTheDispatcherInstanceAsArgument()
+    {
+        $listener = new TestWithDispatcher();
+        $this->dispatcher->addListener('test', array($listener, 'foo'));
+        $this->assertNull($listener->name);
+        $this->assertNull($listener->dispatcher);
+        $this->dispatcher->dispatch('test');
+        $this->assertEquals('test', $listener->name);
+        $this->assertSame($this->dispatcher, $listener->dispatcher);
+    }
+
+    /**
+     * @see https://bugs.php.net/bug.php?id=62976
+     *
+     * This bug affects:
+     *  - The PHP 5.3 branch for versions < 5.3.18
+     *  - The PHP 5.4 branch for versions < 5.4.8
+     *  - The PHP 5.5 branch is not affected
+     */
+    public function testWorkaroundForPhpBug62976()
+    {
+        $dispatcher = $this->createEventDispatcher();
+        $dispatcher->addListener('bug.62976', new CallableClass());
+        $dispatcher->removeListener('bug.62976', function () {});
+        $this->assertTrue($dispatcher->hasListeners('bug.62976'));
+    }
+
+    public function testHasListenersWhenAddedCallbackListenerIsRemoved()
+    {
+        $listener = function () {};
+        $this->dispatcher->addListener('foo', $listener);
+        $this->dispatcher->removeListener('foo', $listener);
+        $this->assertFalse($this->dispatcher->hasListeners());
+    }
+
+    public function testGetListenersWhenAddedCallbackListenerIsRemoved()
+    {
+        $listener = function () {};
+        $this->dispatcher->addListener('foo', $listener);
+        $this->dispatcher->removeListener('foo', $listener);
+        $this->assertSame(array(), $this->dispatcher->getListeners());
+    }
+
+    public function testHasListenersWithoutEventsReturnsFalseAfterHasListenersWithEventHasBeenCalled()
+    {
+        $this->assertFalse($this->dispatcher->hasListeners('foo'));
+        $this->assertFalse($this->dispatcher->hasListeners());
+    }
+}
+
+class CallableClass
+{
+    public function __invoke()
+    {
+    }
+}
+
+class TestEventListener
+{
+    public $preFooInvoked = false;
+    public $postFooInvoked = false;
+
+    /* Listener methods */
+
+    public function preFoo(Event $e)
+    {
+        $this->preFooInvoked = true;
+    }
+
+    public function postFoo(Event $e)
+    {
+        $this->postFooInvoked = true;
+
+        $e->stopPropagation();
+    }
+}
+
+class TestWithDispatcher
+{
+    public $name;
+    public $dispatcher;
+
+    public function foo(Event $e, $name, $dispatcher)
+    {
+        $this->name = $name;
+        $this->dispatcher = $dispatcher;
+    }
+}
+
+class TestEventSubscriber implements EventSubscriberInterface
+{
+    public static function getSubscribedEvents()
+    {
+        return array('pre.foo' => 'preFoo', 'post.foo' => 'postFoo');
+    }
+}
+
+class TestEventSubscriberWithPriorities implements EventSubscriberInterface
+{
+    public static function getSubscribedEvents()
+    {
+        return array(
+            'pre.foo' => array('preFoo', 10),
+            'post.foo' => array('postFoo'),
+        );
+    }
+}
+
+class TestEventSubscriberWithMultipleListeners implements EventSubscriberInterface
+{
+    public static function getSubscribedEvents()
+    {
+        return array('pre.foo' => array(
+            array('preFoo1'),
+            array('preFoo2', 10),
+        ));
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/Tests/ContainerAwareEventDispatcherTest.php b/vendor/symfony/event-dispatcher/Tests/ContainerAwareEventDispatcherTest.php
new file mode 100755
index 0000000..224a292
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/Tests/ContainerAwareEventDispatcherTest.php
@@ -0,0 +1,277 @@
+<?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\EventDispatcher\Tests;
+
+use Symfony\Component\DependencyInjection\Container;
+use Symfony\Component\DependencyInjection\Scope;
+use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher;
+use Symfony\Component\EventDispatcher\Event;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+class ContainerAwareEventDispatcherTest extends AbstractEventDispatcherTest
+{
+    protected function createEventDispatcher()
+    {
+        $container = new Container();
+
+        return new ContainerAwareEventDispatcher($container);
+    }
+
+    public function testAddAListenerService()
+    {
+        $event = new Event();
+
+        $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock();
+
+        $service
+            ->expects($this->once())
+            ->method('onEvent')
+            ->with($event)
+        ;
+
+        $container = new Container();
+        $container->set('service.listener', $service);
+
+        $dispatcher = new ContainerAwareEventDispatcher($container);
+        $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'));
+
+        $dispatcher->dispatch('onEvent', $event);
+    }
+
+    public function testAddASubscriberService()
+    {
+        $event = new Event();
+
+        $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\SubscriberService')->getMock();
+
+        $service
+            ->expects($this->once())
+            ->method('onEvent')
+            ->with($event)
+        ;
+
+        $service
+            ->expects($this->once())
+            ->method('onEventWithPriority')
+            ->with($event)
+        ;
+
+        $service
+            ->expects($this->once())
+            ->method('onEventNested')
+            ->with($event)
+        ;
+
+        $container = new Container();
+        $container->set('service.subscriber', $service);
+
+        $dispatcher = new ContainerAwareEventDispatcher($container);
+        $dispatcher->addSubscriberService('service.subscriber', 'Symfony\Component\EventDispatcher\Tests\SubscriberService');
+
+        $dispatcher->dispatch('onEvent', $event);
+        $dispatcher->dispatch('onEventWithPriority', $event);
+        $dispatcher->dispatch('onEventNested', $event);
+    }
+
+    public function testPreventDuplicateListenerService()
+    {
+        $event = new Event();
+
+        $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock();
+
+        $service
+            ->expects($this->once())
+            ->method('onEvent')
+            ->with($event)
+        ;
+
+        $container = new Container();
+        $container->set('service.listener', $service);
+
+        $dispatcher = new ContainerAwareEventDispatcher($container);
+        $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 5);
+        $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 10);
+
+        $dispatcher->dispatch('onEvent', $event);
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     * @group legacy
+     */
+    public function testTriggerAListenerServiceOutOfScope()
+    {
+        $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock();
+
+        $scope = new Scope('scope');
+        $container = new Container();
+        $container->addScope($scope);
+        $container->enterScope('scope');
+
+        $container->set('service.listener', $service, 'scope');
+
+        $dispatcher = new ContainerAwareEventDispatcher($container);
+        $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'));
+
+        $container->leaveScope('scope');
+        $dispatcher->dispatch('onEvent');
+    }
+
+    /**
+     * @group legacy
+     */
+    public function testReEnteringAScope()
+    {
+        $event = new Event();
+
+        $service1 = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock();
+
+        $service1
+            ->expects($this->exactly(2))
+            ->method('onEvent')
+            ->with($event)
+        ;
+
+        $scope = new Scope('scope');
+        $container = new Container();
+        $container->addScope($scope);
+        $container->enterScope('scope');
+
+        $container->set('service.listener', $service1, 'scope');
+
+        $dispatcher = new ContainerAwareEventDispatcher($container);
+        $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'));
+        $dispatcher->dispatch('onEvent', $event);
+
+        $service2 = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock();
+
+        $service2
+            ->expects($this->once())
+            ->method('onEvent')
+            ->with($event)
+        ;
+
+        $container->enterScope('scope');
+        $container->set('service.listener', $service2, 'scope');
+
+        $dispatcher->dispatch('onEvent', $event);
+
+        $container->leaveScope('scope');
+
+        $dispatcher->dispatch('onEvent');
+    }
+
+    public function testHasListenersOnLazyLoad()
+    {
+        $event = new Event();
+
+        $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock();
+
+        $container = new Container();
+        $container->set('service.listener', $service);
+
+        $dispatcher = new ContainerAwareEventDispatcher($container);
+        $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'));
+
+        $event->setDispatcher($dispatcher);
+        $event->setName('onEvent');
+
+        $service
+            ->expects($this->once())
+            ->method('onEvent')
+            ->with($event)
+        ;
+
+        $this->assertTrue($dispatcher->hasListeners());
+
+        if ($dispatcher->hasListeners('onEvent')) {
+            $dispatcher->dispatch('onEvent');
+        }
+    }
+
+    public function testGetListenersOnLazyLoad()
+    {
+        $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock();
+
+        $container = new Container();
+        $container->set('service.listener', $service);
+
+        $dispatcher = new ContainerAwareEventDispatcher($container);
+        $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'));
+
+        $listeners = $dispatcher->getListeners();
+
+        $this->assertArrayHasKey('onEvent', $listeners);
+
+        $this->assertCount(1, $dispatcher->getListeners('onEvent'));
+    }
+
+    public function testRemoveAfterDispatch()
+    {
+        $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock();
+
+        $container = new Container();
+        $container->set('service.listener', $service);
+
+        $dispatcher = new ContainerAwareEventDispatcher($container);
+        $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'));
+
+        $dispatcher->dispatch('onEvent', new Event());
+        $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent'));
+        $this->assertFalse($dispatcher->hasListeners('onEvent'));
+    }
+
+    public function testRemoveBeforeDispatch()
+    {
+        $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock();
+
+        $container = new Container();
+        $container->set('service.listener', $service);
+
+        $dispatcher = new ContainerAwareEventDispatcher($container);
+        $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'));
+
+        $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent'));
+        $this->assertFalse($dispatcher->hasListeners('onEvent'));
+    }
+}
+
+class Service
+{
+    public function onEvent(Event $e)
+    {
+    }
+}
+
+class SubscriberService implements EventSubscriberInterface
+{
+    public static function getSubscribedEvents()
+    {
+        return array(
+            'onEvent' => 'onEvent',
+            'onEventWithPriority' => array('onEventWithPriority', 10),
+            'onEventNested' => array(array('onEventNested')),
+        );
+    }
+
+    public function onEvent(Event $e)
+    {
+    }
+
+    public function onEventWithPriority(Event $e)
+    {
+    }
+
+    public function onEventNested(Event $e)
+    {
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php b/vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php
new file mode 100755
index 0000000..ffdda38
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php
@@ -0,0 +1,254 @@
+<?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\EventDispatcher\Tests\Debug;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher;
+use Symfony\Component\EventDispatcher\Debug\WrappedListener;
+use Symfony\Component\EventDispatcher\Event;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\Stopwatch\Stopwatch;
+
+class TraceableEventDispatcherTest extends TestCase
+{
+    public function testAddRemoveListener()
+    {
+        $dispatcher = new EventDispatcher();
+        $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
+
+        $tdispatcher->addListener('foo', $listener = function () {});
+        $listeners = $dispatcher->getListeners('foo');
+        $this->assertCount(1, $listeners);
+        $this->assertSame($listener, $listeners[0]);
+
+        $tdispatcher->removeListener('foo', $listener);
+        $this->assertCount(0, $dispatcher->getListeners('foo'));
+    }
+
+    public function testGetListeners()
+    {
+        $dispatcher = new EventDispatcher();
+        $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
+
+        $tdispatcher->addListener('foo', $listener = function () {});
+        $this->assertSame($dispatcher->getListeners('foo'), $tdispatcher->getListeners('foo'));
+    }
+
+    public function testHasListeners()
+    {
+        $dispatcher = new EventDispatcher();
+        $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
+
+        $this->assertFalse($dispatcher->hasListeners('foo'));
+        $this->assertFalse($tdispatcher->hasListeners('foo'));
+
+        $tdispatcher->addListener('foo', $listener = function () {});
+        $this->assertTrue($dispatcher->hasListeners('foo'));
+        $this->assertTrue($tdispatcher->hasListeners('foo'));
+    }
+
+    public function testGetListenerPriority()
+    {
+        $dispatcher = new EventDispatcher();
+        $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
+
+        $tdispatcher->addListener('foo', function () {}, 123);
+
+        $listeners = $dispatcher->getListeners('foo');
+        $this->assertSame(123, $tdispatcher->getListenerPriority('foo', $listeners[0]));
+
+        // Verify that priority is preserved when listener is removed and re-added
+        // in preProcess() and postProcess().
+        $tdispatcher->dispatch('foo', new Event());
+        $listeners = $dispatcher->getListeners('foo');
+        $this->assertSame(123, $tdispatcher->getListenerPriority('foo', $listeners[0]));
+    }
+
+    public function testGetListenerPriorityReturnsZeroWhenWrappedMethodDoesNotExist()
+    {
+        $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock();
+        $traceableEventDispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
+        $traceableEventDispatcher->addListener('foo', function () {}, 123);
+        $listeners = $traceableEventDispatcher->getListeners('foo');
+
+        $this->assertSame(0, $traceableEventDispatcher->getListenerPriority('foo', $listeners[0]));
+    }
+
+    public function testAddRemoveSubscriber()
+    {
+        $dispatcher = new EventDispatcher();
+        $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
+
+        $subscriber = new EventSubscriber();
+
+        $tdispatcher->addSubscriber($subscriber);
+        $listeners = $dispatcher->getListeners('foo');
+        $this->assertCount(1, $listeners);
+        $this->assertSame(array($subscriber, 'call'), $listeners[0]);
+
+        $tdispatcher->removeSubscriber($subscriber);
+        $this->assertCount(0, $dispatcher->getListeners('foo'));
+    }
+
+    /**
+     * @dataProvider isWrappedDataProvider
+     *
+     * @param bool $isWrapped
+     */
+    public function testGetCalledListeners($isWrapped)
+    {
+        $dispatcher = new EventDispatcher();
+        $stopWatch = new Stopwatch();
+        $tdispatcher = new TraceableEventDispatcher($dispatcher, $stopWatch);
+
+        $listener = function () {};
+        if ($isWrapped) {
+            $listener = new WrappedListener($listener, 'foo', $stopWatch, $dispatcher);
+        }
+
+        $tdispatcher->addListener('foo', $listener, 5);
+
+        $this->assertEquals(array(), $tdispatcher->getCalledListeners());
+        $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure', 'priority' => 5)), $tdispatcher->getNotCalledListeners());
+
+        $tdispatcher->dispatch('foo');
+
+        $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure', 'priority' => 5)), $tdispatcher->getCalledListeners());
+        $this->assertEquals(array(), $tdispatcher->getNotCalledListeners());
+    }
+
+    public function isWrappedDataProvider()
+    {
+        return array(
+            array(false),
+            array(true),
+        );
+    }
+
+    public function testGetCalledListenersNested()
+    {
+        $tdispatcher = null;
+        $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
+        $dispatcher->addListener('foo', function (Event $event, $eventName, $dispatcher) use (&$tdispatcher) {
+            $tdispatcher = $dispatcher;
+            $dispatcher->dispatch('bar');
+        });
+        $dispatcher->addListener('bar', function (Event $event) {});
+        $dispatcher->dispatch('foo');
+        $this->assertSame($dispatcher, $tdispatcher);
+        $this->assertCount(2, $dispatcher->getCalledListeners());
+    }
+
+    public function testLogger()
+    {
+        $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
+
+        $dispatcher = new EventDispatcher();
+        $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger);
+        $tdispatcher->addListener('foo', $listener1 = function () {});
+        $tdispatcher->addListener('foo', $listener2 = function () {});
+
+        $logger->expects($this->at(0))->method('debug')->with('Notified event "foo" to listener "closure".');
+        $logger->expects($this->at(1))->method('debug')->with('Notified event "foo" to listener "closure".');
+
+        $tdispatcher->dispatch('foo');
+    }
+
+    public function testLoggerWithStoppedEvent()
+    {
+        $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
+
+        $dispatcher = new EventDispatcher();
+        $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger);
+        $tdispatcher->addListener('foo', $listener1 = function (Event $event) { $event->stopPropagation(); });
+        $tdispatcher->addListener('foo', $listener2 = function () {});
+
+        $logger->expects($this->at(0))->method('debug')->with('Notified event "foo" to listener "closure".');
+        $logger->expects($this->at(1))->method('debug')->with('Listener "closure" stopped propagation of the event "foo".');
+        $logger->expects($this->at(2))->method('debug')->with('Listener "closure" was not called for event "foo".');
+
+        $tdispatcher->dispatch('foo');
+    }
+
+    public function testDispatchCallListeners()
+    {
+        $called = array();
+
+        $dispatcher = new EventDispatcher();
+        $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
+        $tdispatcher->addListener('foo', function () use (&$called) { $called[] = 'foo1'; }, 10);
+        $tdispatcher->addListener('foo', function () use (&$called) { $called[] = 'foo2'; }, 20);
+
+        $tdispatcher->dispatch('foo');
+
+        $this->assertSame(array('foo2', 'foo1'), $called);
+    }
+
+    public function testDispatchNested()
+    {
+        $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
+        $loop = 1;
+        $dispatchedEvents = 0;
+        $dispatcher->addListener('foo', $listener1 = function () use ($dispatcher, &$loop) {
+            ++$loop;
+            if (2 == $loop) {
+                $dispatcher->dispatch('foo');
+            }
+        });
+        $dispatcher->addListener('foo', function () use (&$dispatchedEvents) {
+            ++$dispatchedEvents;
+        });
+
+        $dispatcher->dispatch('foo');
+
+        $this->assertSame(2, $dispatchedEvents);
+    }
+
+    public function testDispatchReusedEventNested()
+    {
+        $nestedCall = false;
+        $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
+        $dispatcher->addListener('foo', function (Event $e) use ($dispatcher) {
+            $dispatcher->dispatch('bar', $e);
+        });
+        $dispatcher->addListener('bar', function (Event $e) use (&$nestedCall) {
+            $nestedCall = true;
+        });
+
+        $this->assertFalse($nestedCall);
+        $dispatcher->dispatch('foo');
+        $this->assertTrue($nestedCall);
+    }
+
+    public function testListenerCanRemoveItselfWhenExecuted()
+    {
+        $eventDispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
+        $listener1 = function ($event, $eventName, EventDispatcherInterface $dispatcher) use (&$listener1) {
+            $dispatcher->removeListener('foo', $listener1);
+        };
+        $eventDispatcher->addListener('foo', $listener1);
+        $eventDispatcher->addListener('foo', function () {});
+        $eventDispatcher->dispatch('foo');
+
+        $this->assertCount(1, $eventDispatcher->getListeners('foo'), 'expected listener1 to be removed');
+    }
+}
+
+class EventSubscriber implements EventSubscriberInterface
+{
+    public static function getSubscribedEvents()
+    {
+        return array('foo' => 'call');
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php b/vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php
new file mode 100755
index 0000000..7490d0d
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php
@@ -0,0 +1,154 @@
+<?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\EventDispatcher\Tests\DependencyInjection;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass;
+
+class RegisterListenersPassTest extends TestCase
+{
+    /**
+     * Tests that event subscribers not implementing EventSubscriberInterface
+     * trigger an exception.
+     *
+     * @expectedException \InvalidArgumentException
+     */
+    public function testEventSubscriberWithoutInterface()
+    {
+        $builder = new ContainerBuilder();
+        $builder->register('event_dispatcher');
+        $builder->register('my_event_subscriber', 'stdClass')
+            ->addTag('kernel.event_subscriber');
+
+        $registerListenersPass = new RegisterListenersPass();
+        $registerListenersPass->process($builder);
+    }
+
+    public function testValidEventSubscriber()
+    {
+        $services = array(
+            'my_event_subscriber' => array(0 => array()),
+        );
+
+        $builder = new ContainerBuilder();
+        $eventDispatcherDefinition = $builder->register('event_dispatcher');
+        $builder->register('my_event_subscriber', 'Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService')
+            ->addTag('kernel.event_subscriber');
+
+        $registerListenersPass = new RegisterListenersPass();
+        $registerListenersPass->process($builder);
+
+        $this->assertEquals(array(array('addSubscriberService', array('my_event_subscriber', 'Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService'))), $eventDispatcherDefinition->getMethodCalls());
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     * @expectedExceptionMessage The service "foo" must be public as event listeners are lazy-loaded.
+     */
+    public function testPrivateEventListener()
+    {
+        $container = new ContainerBuilder();
+        $container->register('foo', 'stdClass')->setPublic(false)->addTag('kernel.event_listener', array());
+        $container->register('event_dispatcher', 'stdClass');
+
+        $registerListenersPass = new RegisterListenersPass();
+        $registerListenersPass->process($container);
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     * @expectedExceptionMessage The service "foo" must be public as event subscribers are lazy-loaded.
+     */
+    public function testPrivateEventSubscriber()
+    {
+        $container = new ContainerBuilder();
+        $container->register('foo', 'stdClass')->setPublic(false)->addTag('kernel.event_subscriber', array());
+        $container->register('event_dispatcher', 'stdClass');
+
+        $registerListenersPass = new RegisterListenersPass();
+        $registerListenersPass->process($container);
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     * @expectedExceptionMessage The service "foo" must not be abstract as event listeners are lazy-loaded.
+     */
+    public function testAbstractEventListener()
+    {
+        $container = new ContainerBuilder();
+        $container->register('foo', 'stdClass')->setAbstract(true)->addTag('kernel.event_listener', array());
+        $container->register('event_dispatcher', 'stdClass');
+
+        $registerListenersPass = new RegisterListenersPass();
+        $registerListenersPass->process($container);
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     * @expectedExceptionMessage The service "foo" must not be abstract as event subscribers are lazy-loaded.
+     */
+    public function testAbstractEventSubscriber()
+    {
+        $container = new ContainerBuilder();
+        $container->register('foo', 'stdClass')->setAbstract(true)->addTag('kernel.event_subscriber', array());
+        $container->register('event_dispatcher', 'stdClass');
+
+        $registerListenersPass = new RegisterListenersPass();
+        $registerListenersPass->process($container);
+    }
+
+    public function testEventSubscriberResolvableClassName()
+    {
+        $container = new ContainerBuilder();
+
+        $container->setParameter('subscriber.class', 'Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService');
+        $container->register('foo', '%subscriber.class%')->addTag('kernel.event_subscriber', array());
+        $container->register('event_dispatcher', 'stdClass');
+
+        $registerListenersPass = new RegisterListenersPass();
+        $registerListenersPass->process($container);
+
+        $definition = $container->getDefinition('event_dispatcher');
+        $expected_calls = array(
+            array(
+                'addSubscriberService',
+                array(
+                    'foo',
+                    'Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService',
+                ),
+            ),
+        );
+        $this->assertSame($expected_calls, $definition->getMethodCalls());
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     * @expectedExceptionMessage You have requested a non-existent parameter "subscriber.class"
+     */
+    public function testEventSubscriberUnresolvableClassName()
+    {
+        $container = new ContainerBuilder();
+        $container->register('foo', '%subscriber.class%')->addTag('kernel.event_subscriber', array());
+        $container->register('event_dispatcher', 'stdClass');
+
+        $registerListenersPass = new RegisterListenersPass();
+        $registerListenersPass->process($container);
+    }
+}
+
+class SubscriberService implements \Symfony\Component\EventDispatcher\EventSubscriberInterface
+{
+    public static function getSubscribedEvents()
+    {
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php b/vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php
new file mode 100755
index 0000000..5faa5c8
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php
@@ -0,0 +1,22 @@
+<?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\EventDispatcher\Tests;
+
+use Symfony\Component\EventDispatcher\EventDispatcher;
+
+class EventDispatcherTest extends AbstractEventDispatcherTest
+{
+    protected function createEventDispatcher()
+    {
+        return new EventDispatcher();
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/Tests/EventTest.php b/vendor/symfony/event-dispatcher/Tests/EventTest.php
new file mode 100755
index 0000000..bdc14ab
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/Tests/EventTest.php
@@ -0,0 +1,97 @@
+<?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\EventDispatcher\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\EventDispatcher\Event;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+
+/**
+ * Test class for Event.
+ */
+class EventTest extends TestCase
+{
+    /**
+     * @var \Symfony\Component\EventDispatcher\Event
+     */
+    protected $event;
+
+    /**
+     * @var \Symfony\Component\EventDispatcher\EventDispatcher
+     */
+    protected $dispatcher;
+
+    /**
+     * Sets up the fixture, for example, opens a network connection.
+     * This method is called before a test is executed.
+     */
+    protected function setUp()
+    {
+        $this->event = new Event();
+        $this->dispatcher = new EventDispatcher();
+    }
+
+    /**
+     * Tears down the fixture, for example, closes a network connection.
+     * This method is called after a test is executed.
+     */
+    protected function tearDown()
+    {
+        $this->event = null;
+        $this->dispatcher = null;
+    }
+
+    public function testIsPropagationStopped()
+    {
+        $this->assertFalse($this->event->isPropagationStopped());
+    }
+
+    public function testStopPropagationAndIsPropagationStopped()
+    {
+        $this->event->stopPropagation();
+        $this->assertTrue($this->event->isPropagationStopped());
+    }
+
+    /**
+     * @group legacy
+     */
+    public function testLegacySetDispatcher()
+    {
+        $this->event->setDispatcher($this->dispatcher);
+        $this->assertSame($this->dispatcher, $this->event->getDispatcher());
+    }
+
+    /**
+     * @group legacy
+     */
+    public function testLegacyGetDispatcher()
+    {
+        $this->assertNull($this->event->getDispatcher());
+    }
+
+    /**
+     * @group legacy
+     */
+    public function testLegacyGetName()
+    {
+        $this->assertNull($this->event->getName());
+    }
+
+    /**
+     * @group legacy
+     */
+    public function testLegacySetName()
+    {
+        $this->event->setName('foo');
+        $this->assertEquals('foo', $this->event->getName());
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/Tests/GenericEventTest.php b/vendor/symfony/event-dispatcher/Tests/GenericEventTest.php
new file mode 100755
index 0000000..b63f69d
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/Tests/GenericEventTest.php
@@ -0,0 +1,136 @@
+<?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\EventDispatcher\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\EventDispatcher\GenericEvent;
+
+/**
+ * Test class for Event.
+ */
+class GenericEventTest extends TestCase
+{
+    /**
+     * @var GenericEvent
+     */
+    private $event;
+
+    private $subject;
+
+    /**
+     * Prepares the environment before running a test.
+     */
+    protected function setUp()
+    {
+        $this->subject = new \stdClass();
+        $this->event = new GenericEvent($this->subject, array('name' => 'Event'));
+    }
+
+    /**
+     * Cleans up the environment after running a test.
+     */
+    protected function tearDown()
+    {
+        $this->subject = null;
+        $this->event = null;
+    }
+
+    public function testConstruct()
+    {
+        $this->assertEquals($this->event, new GenericEvent($this->subject, array('name' => 'Event')));
+    }
+
+    /**
+     * Tests Event->getArgs().
+     */
+    public function testGetArguments()
+    {
+        // test getting all
+        $this->assertSame(array('name' => 'Event'), $this->event->getArguments());
+    }
+
+    public function testSetArguments()
+    {
+        $result = $this->event->setArguments(array('foo' => 'bar'));
+        $this->assertAttributeSame(array('foo' => 'bar'), 'arguments', $this->event);
+        $this->assertSame($this->event, $result);
+    }
+
+    public function testSetArgument()
+    {
+        $result = $this->event->setArgument('foo2', 'bar2');
+        $this->assertAttributeSame(array('name' => 'Event', 'foo2' => 'bar2'), 'arguments', $this->event);
+        $this->assertEquals($this->event, $result);
+    }
+
+    public function testGetArgument()
+    {
+        // test getting key
+        $this->assertEquals('Event', $this->event->getArgument('name'));
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testGetArgException()
+    {
+        $this->event->getArgument('nameNotExist');
+    }
+
+    public function testOffsetGet()
+    {
+        // test getting key
+        $this->assertEquals('Event', $this->event['name']);
+
+        // test getting invalid arg
+        $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('InvalidArgumentException');
+        $this->assertFalse($this->event['nameNotExist']);
+    }
+
+    public function testOffsetSet()
+    {
+        $this->event['foo2'] = 'bar2';
+        $this->assertAttributeSame(array('name' => 'Event', 'foo2' => 'bar2'), 'arguments', $this->event);
+    }
+
+    public function testOffsetUnset()
+    {
+        unset($this->event['name']);
+        $this->assertAttributeSame(array(), 'arguments', $this->event);
+    }
+
+    public function testOffsetIsset()
+    {
+        $this->assertArrayHasKey('name', $this->event);
+        $this->assertArrayNotHasKey('nameNotExist', $this->event);
+    }
+
+    public function testHasArgument()
+    {
+        $this->assertTrue($this->event->hasArgument('name'));
+        $this->assertFalse($this->event->hasArgument('nameNotExist'));
+    }
+
+    public function testGetSubject()
+    {
+        $this->assertSame($this->subject, $this->event->getSubject());
+    }
+
+    public function testHasIterator()
+    {
+        $data = array();
+        foreach ($this->event as $key => $value) {
+            $data[$key] = $value;
+        }
+        $this->assertEquals(array('name' => 'Event'), $data);
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php b/vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php
new file mode 100755
index 0000000..04f2861
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php
@@ -0,0 +1,106 @@
+<?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\EventDispatcher\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\EventDispatcher\Event;
+use Symfony\Component\EventDispatcher\ImmutableEventDispatcher;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class ImmutableEventDispatcherTest extends TestCase
+{
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    private $innerDispatcher;
+
+    /**
+     * @var ImmutableEventDispatcher
+     */
+    private $dispatcher;
+
+    protected function setUp()
+    {
+        $this->innerDispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock();
+        $this->dispatcher = new ImmutableEventDispatcher($this->innerDispatcher);
+    }
+
+    public function testDispatchDelegates()
+    {
+        $event = new Event();
+
+        $this->innerDispatcher->expects($this->once())
+            ->method('dispatch')
+            ->with('event', $event)
+            ->will($this->returnValue('result'));
+
+        $this->assertSame('result', $this->dispatcher->dispatch('event', $event));
+    }
+
+    public function testGetListenersDelegates()
+    {
+        $this->innerDispatcher->expects($this->once())
+            ->method('getListeners')
+            ->with('event')
+            ->will($this->returnValue('result'));
+
+        $this->assertSame('result', $this->dispatcher->getListeners('event'));
+    }
+
+    public function testHasListenersDelegates()
+    {
+        $this->innerDispatcher->expects($this->once())
+            ->method('hasListeners')
+            ->with('event')
+            ->will($this->returnValue('result'));
+
+        $this->assertSame('result', $this->dispatcher->hasListeners('event'));
+    }
+
+    /**
+     * @expectedException \BadMethodCallException
+     */
+    public function testAddListenerDisallowed()
+    {
+        $this->dispatcher->addListener('event', function () { return 'foo'; });
+    }
+
+    /**
+     * @expectedException \BadMethodCallException
+     */
+    public function testAddSubscriberDisallowed()
+    {
+        $subscriber = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventSubscriberInterface')->getMock();
+
+        $this->dispatcher->addSubscriber($subscriber);
+    }
+
+    /**
+     * @expectedException \BadMethodCallException
+     */
+    public function testRemoveListenerDisallowed()
+    {
+        $this->dispatcher->removeListener('event', function () { return 'foo'; });
+    }
+
+    /**
+     * @expectedException \BadMethodCallException
+     */
+    public function testRemoveSubscriberDisallowed()
+    {
+        $subscriber = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventSubscriberInterface')->getMock();
+
+        $this->dispatcher->removeSubscriber($subscriber);
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/composer.json b/vendor/symfony/event-dispatcher/composer.json
new file mode 100755
index 0000000..14fc24b
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/composer.json
@@ -0,0 +1,44 @@
+{
+    "name": "symfony/event-dispatcher",
+    "type": "library",
+    "description": "Symfony EventDispatcher Component",
+    "keywords": [],
+    "homepage": "https://symfony.com",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Fabien Potencier",
+            "email": "fabien@symfony.com"
+        },
+        {
+            "name": "Symfony Community",
+            "homepage": "https://symfony.com/contributors"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.9"
+    },
+    "require-dev": {
+        "symfony/dependency-injection": "~2.6|~3.0.0",
+        "symfony/expression-language": "~2.6|~3.0.0",
+        "symfony/config": "^2.0.5|~3.0.0",
+        "symfony/stopwatch": "~2.3|~3.0.0",
+        "psr/log": "~1.0"
+    },
+    "suggest": {
+        "symfony/dependency-injection": "",
+        "symfony/http-kernel": ""
+    },
+    "autoload": {
+        "psr-4": { "Symfony\\Component\\EventDispatcher\\": "" },
+        "exclude-from-classmap": [
+            "/Tests/"
+        ]
+    },
+    "minimum-stability": "dev",
+    "extra": {
+        "branch-alias": {
+            "dev-master": "2.8-dev"
+        }
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/phpunit.xml.dist b/vendor/symfony/event-dispatcher/phpunit.xml.dist
new file mode 100755
index 0000000..f2eb169
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/phpunit.xml.dist
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/5.2/phpunit.xsd"
+         backupGlobals="false"
+         colors="true"
+         bootstrap="vendor/autoload.php"
+         failOnRisky="true"
+         failOnWarning="true"
+>
+    <php>
+        <ini name="error_reporting" value="-1" />
+    </php>
+
+    <testsuites>
+        <testsuite name="Symfony EventDispatcher Component Test Suite">
+            <directory>./Tests/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./</directory>
+            <exclude>
+                <directory>./Resources</directory>
+                <directory>./Tests</directory>
+                <directory>./vendor</directory>
+            </exclude>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/vendor/symfony/http-foundation/.gitignore b/vendor/symfony/http-foundation/.gitignore
new file mode 100755
index 0000000..c49a5d8
--- /dev/null
+++ b/vendor/symfony/http-foundation/.gitignore
@@ -0,0 +1,3 @@
+vendor/
+composer.lock
+phpunit.xml
diff --git a/vendor/symfony/http-foundation/AcceptHeader.php b/vendor/symfony/http-foundation/AcceptHeader.php
new file mode 100755
index 0000000..968b71f
--- /dev/null
+++ b/vendor/symfony/http-foundation/AcceptHeader.php
@@ -0,0 +1,168 @@
+<?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\HttpFoundation;
+
+/**
+ * Represents an Accept-* header.
+ *
+ * An accept header is compound with a list of items,
+ * sorted by descending quality.
+ *
+ * @author Jean-François Simon <contact@jfsimon.fr>
+ */
+class AcceptHeader
+{
+    /**
+     * @var AcceptHeaderItem[]
+     */
+    private $items = [];
+
+    /**
+     * @var bool
+     */
+    private $sorted = true;
+
+    /**
+     * @param AcceptHeaderItem[] $items
+     */
+    public function __construct(array $items)
+    {
+        foreach ($items as $item) {
+            $this->add($item);
+        }
+    }
+
+    /**
+     * Builds an AcceptHeader instance from a string.
+     *
+     * @param string $headerValue
+     *
+     * @return self
+     */
+    public static function fromString($headerValue)
+    {
+        $index = 0;
+
+        return new self(array_map(function ($itemValue) use (&$index) {
+            $item = AcceptHeaderItem::fromString($itemValue);
+            $item->setIndex($index++);
+
+            return $item;
+        }, preg_split('/\s*(?:,*("[^"]+"),*|,*(\'[^\']+\'),*|,+)\s*/', $headerValue, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE)));
+    }
+
+    /**
+     * Returns header value's string representation.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return implode(',', $this->items);
+    }
+
+    /**
+     * Tests if header has given value.
+     *
+     * @param string $value
+     *
+     * @return bool
+     */
+    public function has($value)
+    {
+        return isset($this->items[$value]);
+    }
+
+    /**
+     * Returns given value's item, if exists.
+     *
+     * @param string $value
+     *
+     * @return AcceptHeaderItem|null
+     */
+    public function get($value)
+    {
+        return isset($this->items[$value]) ? $this->items[$value] : null;
+    }
+
+    /**
+     * Adds an item.
+     *
+     * @return $this
+     */
+    public function add(AcceptHeaderItem $item)
+    {
+        $this->items[$item->getValue()] = $item;
+        $this->sorted = false;
+
+        return $this;
+    }
+
+    /**
+     * Returns all items.
+     *
+     * @return AcceptHeaderItem[]
+     */
+    public function all()
+    {
+        $this->sort();
+
+        return $this->items;
+    }
+
+    /**
+     * Filters items on their value using given regex.
+     *
+     * @param string $pattern
+     *
+     * @return self
+     */
+    public function filter($pattern)
+    {
+        return new self(array_filter($this->items, function (AcceptHeaderItem $item) use ($pattern) {
+            return preg_match($pattern, $item->getValue());
+        }));
+    }
+
+    /**
+     * Returns first item.
+     *
+     * @return AcceptHeaderItem|null
+     */
+    public function first()
+    {
+        $this->sort();
+
+        return !empty($this->items) ? reset($this->items) : null;
+    }
+
+    /**
+     * Sorts items by descending quality.
+     */
+    private function sort()
+    {
+        if (!$this->sorted) {
+            uasort($this->items, function (AcceptHeaderItem $a, AcceptHeaderItem $b) {
+                $qA = $a->getQuality();
+                $qB = $b->getQuality();
+
+                if ($qA === $qB) {
+                    return $a->getIndex() > $b->getIndex() ? 1 : -1;
+                }
+
+                return $qA > $qB ? -1 : 1;
+            });
+
+            $this->sorted = true;
+        }
+    }
+}
diff --git a/vendor/symfony/http-foundation/AcceptHeaderItem.php b/vendor/symfony/http-foundation/AcceptHeaderItem.php
new file mode 100755
index 0000000..f6e8968
--- /dev/null
+++ b/vendor/symfony/http-foundation/AcceptHeaderItem.php
@@ -0,0 +1,209 @@
+<?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\HttpFoundation;
+
+/**
+ * Represents an Accept-* header item.
+ *
+ * @author Jean-François Simon <contact@jfsimon.fr>
+ */
+class AcceptHeaderItem
+{
+    private $value;
+    private $quality = 1.0;
+    private $index = 0;
+    private $attributes = [];
+
+    /**
+     * @param string $value
+     * @param array  $attributes
+     */
+    public function __construct($value, array $attributes = [])
+    {
+        $this->value = $value;
+        foreach ($attributes as $name => $value) {
+            $this->setAttribute($name, $value);
+        }
+    }
+
+    /**
+     * Builds an AcceptHeaderInstance instance from a string.
+     *
+     * @param string $itemValue
+     *
+     * @return self
+     */
+    public static function fromString($itemValue)
+    {
+        $bits = preg_split('/\s*(?:;*("[^"]+");*|;*(\'[^\']+\');*|;+)\s*/', $itemValue, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
+        $value = array_shift($bits);
+        $attributes = [];
+
+        $lastNullAttribute = null;
+        foreach ($bits as $bit) {
+            if (($start = substr($bit, 0, 1)) === ($end = substr($bit, -1)) && ('"' === $start || '\'' === $start)) {
+                $attributes[$lastNullAttribute] = substr($bit, 1, -1);
+            } elseif ('=' === $end) {
+                $lastNullAttribute = $bit = substr($bit, 0, -1);
+                $attributes[$bit] = null;
+            } else {
+                $parts = explode('=', $bit);
+                $attributes[$parts[0]] = isset($parts[1]) && \strlen($parts[1]) > 0 ? $parts[1] : '';
+            }
+        }
+
+        return new self(($start = substr($value, 0, 1)) === ($end = substr($value, -1)) && ('"' === $start || '\'' === $start) ? substr($value, 1, -1) : $value, $attributes);
+    }
+
+    /**
+     * Returns header value's string representation.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        $string = $this->value.($this->quality < 1 ? ';q='.$this->quality : '');
+        if (\count($this->attributes) > 0) {
+            $string .= ';'.implode(';', array_map(function ($name, $value) {
+                return sprintf(preg_match('/[,;=]/', $value) ? '%s="%s"' : '%s=%s', $name, $value);
+            }, array_keys($this->attributes), $this->attributes));
+        }
+
+        return $string;
+    }
+
+    /**
+     * Set the item value.
+     *
+     * @param string $value
+     *
+     * @return $this
+     */
+    public function setValue($value)
+    {
+        $this->value = $value;
+
+        return $this;
+    }
+
+    /**
+     * Returns the item value.
+     *
+     * @return string
+     */
+    public function getValue()
+    {
+        return $this->value;
+    }
+
+    /**
+     * Set the item quality.
+     *
+     * @param float $quality
+     *
+     * @return $this
+     */
+    public function setQuality($quality)
+    {
+        $this->quality = $quality;
+
+        return $this;
+    }
+
+    /**
+     * Returns the item quality.
+     *
+     * @return float
+     */
+    public function getQuality()
+    {
+        return $this->quality;
+    }
+
+    /**
+     * Set the item index.
+     *
+     * @param int $index
+     *
+     * @return $this
+     */
+    public function setIndex($index)
+    {
+        $this->index = $index;
+
+        return $this;
+    }
+
+    /**
+     * Returns the item index.
+     *
+     * @return int
+     */
+    public function getIndex()
+    {
+        return $this->index;
+    }
+
+    /**
+     * Tests if an attribute exists.
+     *
+     * @param string $name
+     *
+     * @return bool
+     */
+    public function hasAttribute($name)
+    {
+        return isset($this->attributes[$name]);
+    }
+
+    /**
+     * Returns an attribute by its name.
+     *
+     * @param string $name
+     * @param mixed  $default
+     *
+     * @return mixed
+     */
+    public function getAttribute($name, $default = null)
+    {
+        return isset($this->attributes[$name]) ? $this->attributes[$name] : $default;
+    }
+
+    /**
+     * Returns all attributes.
+     *
+     * @return array
+     */
+    public function getAttributes()
+    {
+        return $this->attributes;
+    }
+
+    /**
+     * Set an attribute.
+     *
+     * @param string $name
+     * @param string $value
+     *
+     * @return $this
+     */
+    public function setAttribute($name, $value)
+    {
+        if ('q' === $name) {
+            $this->quality = (float) $value;
+        } else {
+            $this->attributes[$name] = (string) $value;
+        }
+
+        return $this;
+    }
+}
diff --git a/vendor/symfony/http-foundation/ApacheRequest.php b/vendor/symfony/http-foundation/ApacheRequest.php
new file mode 100755
index 0000000..4e99186
--- /dev/null
+++ b/vendor/symfony/http-foundation/ApacheRequest.php
@@ -0,0 +1,43 @@
+<?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\HttpFoundation;
+
+/**
+ * Request represents an HTTP request from an Apache server.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class ApacheRequest extends Request
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function prepareRequestUri()
+    {
+        return $this->server->get('REQUEST_URI');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function prepareBaseUrl()
+    {
+        $baseUrl = $this->server->get('SCRIPT_NAME');
+
+        if (false === strpos($this->server->get('REQUEST_URI'), $baseUrl)) {
+            // assume mod_rewrite
+            return rtrim(\dirname($baseUrl), '/\\');
+        }
+
+        return $baseUrl;
+    }
+}
diff --git a/vendor/symfony/http-foundation/BinaryFileResponse.php b/vendor/symfony/http-foundation/BinaryFileResponse.php
new file mode 100755
index 0000000..cf648df
--- /dev/null
+++ b/vendor/symfony/http-foundation/BinaryFileResponse.php
@@ -0,0 +1,359 @@
+<?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\HttpFoundation;
+
+use Symfony\Component\HttpFoundation\File\Exception\FileException;
+use Symfony\Component\HttpFoundation\File\File;
+
+/**
+ * BinaryFileResponse represents an HTTP response delivering a file.
+ *
+ * @author Niklas Fiekas <niklas.fiekas@tu-clausthal.de>
+ * @author stealth35 <stealth35-php@live.fr>
+ * @author Igor Wiedler <igor@wiedler.ch>
+ * @author Jordan Alliot <jordan.alliot@gmail.com>
+ * @author Sergey Linnik <linniksa@gmail.com>
+ */
+class BinaryFileResponse extends Response
+{
+    protected static $trustXSendfileTypeHeader = false;
+
+    /**
+     * @var File
+     */
+    protected $file;
+    protected $offset = 0;
+    protected $maxlen = -1;
+    protected $deleteFileAfterSend = false;
+
+    /**
+     * @param \SplFileInfo|string $file               The file to stream
+     * @param int                 $status             The response status code
+     * @param array               $headers            An array of response headers
+     * @param bool                $public             Files are public by default
+     * @param string|null         $contentDisposition The type of Content-Disposition to set automatically with the filename
+     * @param bool                $autoEtag           Whether the ETag header should be automatically set
+     * @param bool                $autoLastModified   Whether the Last-Modified header should be automatically set
+     */
+    public function __construct($file, $status = 200, $headers = [], $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
+    {
+        parent::__construct(null, $status, $headers);
+
+        $this->setFile($file, $contentDisposition, $autoEtag, $autoLastModified);
+
+        if ($public) {
+            $this->setPublic();
+        }
+    }
+
+    /**
+     * @param \SplFileInfo|string $file               The file to stream
+     * @param int                 $status             The response status code
+     * @param array               $headers            An array of response headers
+     * @param bool                $public             Files are public by default
+     * @param string|null         $contentDisposition The type of Content-Disposition to set automatically with the filename
+     * @param bool                $autoEtag           Whether the ETag header should be automatically set
+     * @param bool                $autoLastModified   Whether the Last-Modified header should be automatically set
+     *
+     * @return static
+     */
+    public static function create($file = null, $status = 200, $headers = [], $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
+    {
+        return new static($file, $status, $headers, $public, $contentDisposition, $autoEtag, $autoLastModified);
+    }
+
+    /**
+     * Sets the file to stream.
+     *
+     * @param \SplFileInfo|string $file               The file to stream
+     * @param string              $contentDisposition
+     * @param bool                $autoEtag
+     * @param bool                $autoLastModified
+     *
+     * @return $this
+     *
+     * @throws FileException
+     */
+    public function setFile($file, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
+    {
+        if (!$file instanceof File) {
+            if ($file instanceof \SplFileInfo) {
+                $file = new File($file->getPathname());
+            } else {
+                $file = new File((string) $file);
+            }
+        }
+
+        if (!$file->isReadable()) {
+            throw new FileException('File must be readable.');
+        }
+
+        $this->file = $file;
+
+        if ($autoEtag) {
+            $this->setAutoEtag();
+        }
+
+        if ($autoLastModified) {
+            $this->setAutoLastModified();
+        }
+
+        if ($contentDisposition) {
+            $this->setContentDisposition($contentDisposition);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Gets the file.
+     *
+     * @return File The file to stream
+     */
+    public function getFile()
+    {
+        return $this->file;
+    }
+
+    /**
+     * Automatically sets the Last-Modified header according the file modification date.
+     */
+    public function setAutoLastModified()
+    {
+        $this->setLastModified(\DateTime::createFromFormat('U', $this->file->getMTime()));
+
+        return $this;
+    }
+
+    /**
+     * Automatically sets the ETag header according to the checksum of the file.
+     */
+    public function setAutoEtag()
+    {
+        $this->setEtag(base64_encode(hash_file('sha256', $this->file->getPathname(), true)));
+
+        return $this;
+    }
+
+    /**
+     * Sets the Content-Disposition header with the given filename.
+     *
+     * @param string $disposition      ResponseHeaderBag::DISPOSITION_INLINE or ResponseHeaderBag::DISPOSITION_ATTACHMENT
+     * @param string $filename         Optionally use this UTF-8 encoded filename instead of the real name of the file
+     * @param string $filenameFallback A fallback filename, containing only ASCII characters. Defaults to an automatically encoded filename
+     *
+     * @return $this
+     */
+    public function setContentDisposition($disposition, $filename = '', $filenameFallback = '')
+    {
+        if ('' === $filename) {
+            $filename = $this->file->getFilename();
+        }
+
+        if ('' === $filenameFallback && (!preg_match('/^[\x20-\x7e]*$/', $filename) || false !== strpos($filename, '%'))) {
+            $encoding = mb_detect_encoding($filename, null, true) ?: '8bit';
+
+            for ($i = 0, $filenameLength = mb_strlen($filename, $encoding); $i < $filenameLength; ++$i) {
+                $char = mb_substr($filename, $i, 1, $encoding);
+
+                if ('%' === $char || \ord($char) < 32 || \ord($char) > 126) {
+                    $filenameFallback .= '_';
+                } else {
+                    $filenameFallback .= $char;
+                }
+            }
+        }
+
+        $dispositionHeader = $this->headers->makeDisposition($disposition, $filename, $filenameFallback);
+        $this->headers->set('Content-Disposition', $dispositionHeader);
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function prepare(Request $request)
+    {
+        if (!$this->headers->has('Content-Type')) {
+            $this->headers->set('Content-Type', $this->file->getMimeType() ?: 'application/octet-stream');
+        }
+
+        if ('HTTP/1.0' !== $request->server->get('SERVER_PROTOCOL')) {
+            $this->setProtocolVersion('1.1');
+        }
+
+        $this->ensureIEOverSSLCompatibility($request);
+
+        $this->offset = 0;
+        $this->maxlen = -1;
+
+        if (false === $fileSize = $this->file->getSize()) {
+            return $this;
+        }
+        $this->headers->set('Content-Length', $fileSize);
+
+        if (!$this->headers->has('Accept-Ranges')) {
+            // Only accept ranges on safe HTTP methods
+            $this->headers->set('Accept-Ranges', $request->isMethodSafe(false) ? 'bytes' : 'none');
+        }
+
+        if (self::$trustXSendfileTypeHeader && $request->headers->has('X-Sendfile-Type')) {
+            // Use X-Sendfile, do not send any content.
+            $type = $request->headers->get('X-Sendfile-Type');
+            $path = $this->file->getRealPath();
+            // Fall back to scheme://path for stream wrapped locations.
+            if (false === $path) {
+                $path = $this->file->getPathname();
+            }
+            if ('x-accel-redirect' === strtolower($type)) {
+                // Do X-Accel-Mapping substitutions.
+                // @link http://wiki.nginx.org/X-accel#X-Accel-Redirect
+                foreach (explode(',', $request->headers->get('X-Accel-Mapping', '')) as $mapping) {
+                    $mapping = explode('=', $mapping, 2);
+
+                    if (2 === \count($mapping)) {
+                        $pathPrefix = trim($mapping[0]);
+                        $location = trim($mapping[1]);
+
+                        if (substr($path, 0, \strlen($pathPrefix)) === $pathPrefix) {
+                            $path = $location.substr($path, \strlen($pathPrefix));
+                            break;
+                        }
+                    }
+                }
+            }
+            $this->headers->set($type, $path);
+            $this->maxlen = 0;
+        } elseif ($request->headers->has('Range')) {
+            // Process the range headers.
+            if (!$request->headers->has('If-Range') || $this->hasValidIfRangeHeader($request->headers->get('If-Range'))) {
+                $range = $request->headers->get('Range');
+
+                list($start, $end) = explode('-', substr($range, 6), 2) + [0];
+
+                $end = ('' === $end) ? $fileSize - 1 : (int) $end;
+
+                if ('' === $start) {
+                    $start = $fileSize - $end;
+                    $end = $fileSize - 1;
+                } else {
+                    $start = (int) $start;
+                }
+
+                if ($start <= $end) {
+                    if ($start < 0 || $end > $fileSize - 1) {
+                        $this->setStatusCode(416);
+                        $this->headers->set('Content-Range', sprintf('bytes */%s', $fileSize));
+                    } elseif (0 !== $start || $end !== $fileSize - 1) {
+                        $this->maxlen = $end < $fileSize ? $end - $start + 1 : -1;
+                        $this->offset = $start;
+
+                        $this->setStatusCode(206);
+                        $this->headers->set('Content-Range', sprintf('bytes %s-%s/%s', $start, $end, $fileSize));
+                        $this->headers->set('Content-Length', $end - $start + 1);
+                    }
+                }
+            }
+        }
+
+        return $this;
+    }
+
+    private function hasValidIfRangeHeader($header)
+    {
+        if ($this->getEtag() === $header) {
+            return true;
+        }
+
+        if (null === $lastModified = $this->getLastModified()) {
+            return false;
+        }
+
+        return $lastModified->format('D, d M Y H:i:s').' GMT' === $header;
+    }
+
+    /**
+     * Sends the file.
+     *
+     * {@inheritdoc}
+     */
+    public function sendContent()
+    {
+        if (!$this->isSuccessful()) {
+            return parent::sendContent();
+        }
+
+        if (0 === $this->maxlen) {
+            return $this;
+        }
+
+        $out = fopen('php://output', 'wb');
+        $file = fopen($this->file->getPathname(), 'rb');
+
+        stream_copy_to_stream($file, $out, $this->maxlen, $this->offset);
+
+        fclose($out);
+        fclose($file);
+
+        if ($this->deleteFileAfterSend && file_exists($this->file->getPathname())) {
+            unlink($this->file->getPathname());
+        }
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @throws \LogicException when the content is not null
+     */
+    public function setContent($content)
+    {
+        if (null !== $content) {
+            throw new \LogicException('The content cannot be set on a BinaryFileResponse instance.');
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @return false
+     */
+    public function getContent()
+    {
+        return false;
+    }
+
+    /**
+     * Trust X-Sendfile-Type header.
+     */
+    public static function trustXSendfileTypeHeader()
+    {
+        self::$trustXSendfileTypeHeader = true;
+    }
+
+    /**
+     * If this is set to true, the file will be unlinked after the request is send
+     * Note: If the X-Sendfile header is used, the deleteFileAfterSend setting will not be used.
+     *
+     * @param bool $shouldDelete
+     *
+     * @return $this
+     */
+    public function deleteFileAfterSend($shouldDelete)
+    {
+        $this->deleteFileAfterSend = $shouldDelete;
+
+        return $this;
+    }
+}
diff --git a/vendor/symfony/http-foundation/CHANGELOG.md b/vendor/symfony/http-foundation/CHANGELOG.md
new file mode 100755
index 0000000..7bfde80
--- /dev/null
+++ b/vendor/symfony/http-foundation/CHANGELOG.md
@@ -0,0 +1,165 @@
+CHANGELOG
+=========
+
+3.4.14
+------
+
+ * [BC BREAK] Support for the IIS-only `X_ORIGINAL_URL` and `X_REWRITE_URL`
+   HTTP headers has been dropped for security reasons.
+
+3.4.0
+-----
+
+ * implemented PHP 7.0's `SessionUpdateTimestampHandlerInterface` with a new
+   `AbstractSessionHandler` base class and a new `StrictSessionHandler` wrapper
+ * deprecated the `WriteCheckSessionHandler`, `NativeSessionHandler` and `NativeProxy` classes
+ * deprecated setting session save handlers that do not implement `\SessionHandlerInterface` in `NativeSessionStorage::setSaveHandler()`
+ * deprecated using `MongoDbSessionHandler` with the legacy mongo extension; use it with the mongodb/mongodb package and ext-mongodb instead
+ * deprecated `MemcacheSessionHandler`; use `MemcachedSessionHandler` instead
+
+3.3.0
+-----
+
+ * the `Request::setTrustedProxies()` method takes a new `$trustedHeaderSet` argument,
+   see http://symfony.com/doc/current/components/http_foundation/trusting_proxies.html for more info,
+ * deprecated the `Request::setTrustedHeaderName()` and `Request::getTrustedHeaderName()` methods,
+ * added `File\Stream`, to be passed to `BinaryFileResponse` when the size of the served file is unknown,
+   disabling `Range` and `Content-Length` handling, switching to chunked encoding instead
+ * added the `Cookie::fromString()` method that allows to create a cookie from a
+   raw header string
+
+3.1.0
+-----
+
+ * Added support for creating `JsonResponse` with a string of JSON data
+
+3.0.0
+-----
+
+ * The precedence of parameters returned from `Request::get()` changed from "GET, PATH, BODY" to "PATH, GET, BODY"
+
+2.8.0
+-----
+
+ * Finding deep items in `ParameterBag::get()` is deprecated since version 2.8 and
+   will be removed in 3.0.
+
+2.6.0
+-----
+
+ * PdoSessionHandler changes
+   - implemented different session locking strategies to prevent loss of data by concurrent access to the same session
+   - [BC BREAK] save session data in a binary column without base64_encode
+   - [BC BREAK] added lifetime column to the session table which allows to have different lifetimes for each session
+   - implemented lazy connections that are only opened when a session is used by either passing a dsn string
+     explicitly or falling back to session.save_path ini setting
+   - added a createTable method that initializes a correctly defined table depending on the database vendor
+
+2.5.0
+-----
+
+ * added `JsonResponse::setEncodingOptions()` & `JsonResponse::getEncodingOptions()` for easier manipulation
+   of the options used while encoding data to JSON format.
+
+2.4.0
+-----
+
+ * added RequestStack
+ * added Request::getEncodings()
+ * added accessors methods to session handlers
+
+2.3.0
+-----
+
+ * added support for ranges of IPs in trusted proxies
+ * `UploadedFile::isValid` now returns false if the file was not uploaded via HTTP (in a non-test mode)
+ * Improved error-handling of `\Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler`
+   to ensure the supplied PDO handler throws Exceptions on error (as the class expects). Added related test cases
+   to verify that Exceptions are properly thrown when the PDO queries fail.
+
+2.2.0
+-----
+
+ * fixed the Request::create() precedence (URI information always take precedence now)
+ * added Request::getTrustedProxies()
+ * deprecated Request::isProxyTrusted()
+ * [BC BREAK] JsonResponse does not turn a top level empty array to an object anymore, use an ArrayObject to enforce objects
+ * added a IpUtils class to check if an IP belongs to a CIDR
+ * added Request::getRealMethod() to get the "real" HTTP method (getMethod() returns the "intended" HTTP method)
+ * disabled _method request parameter support by default (call Request::enableHttpMethodParameterOverride() to
+   enable it, and Request::getHttpMethodParameterOverride() to check if it is supported)
+ * Request::splitHttpAcceptHeader() method is deprecated and will be removed in 2.3
+ * Deprecated Flashbag::count() and \Countable interface, will be removed in 2.3
+
+2.1.0
+-----
+
+ * added Request::getSchemeAndHttpHost() and Request::getUserInfo()
+ * added a fluent interface to the Response class
+ * added Request::isProxyTrusted()
+ * added JsonResponse
+ * added a getTargetUrl method to RedirectResponse
+ * added support for streamed responses
+ * made Response::prepare() method the place to enforce HTTP specification
+ * [BC BREAK] moved management of the locale from the Session class to the Request class
+ * added a generic access to the PHP built-in filter mechanism: ParameterBag::filter()
+ * made FileBinaryMimeTypeGuesser command configurable
+ * added Request::getUser() and Request::getPassword()
+ * added support for the PATCH method in Request
+ * removed the ContentTypeMimeTypeGuesser class as it is deprecated and never used on PHP 5.3
+ * added ResponseHeaderBag::makeDisposition() (implements RFC 6266)
+ * made mimetype to extension conversion configurable
+ * [BC BREAK] Moved all session related classes and interfaces into own namespace, as
+   `Symfony\Component\HttpFoundation\Session` and renamed classes accordingly.
+   Session handlers are located in the subnamespace `Symfony\Component\HttpFoundation\Session\Handler`.
+ * SessionHandlers must implement `\SessionHandlerInterface` or extend from the
+   `Symfony\Component\HttpFoundation\Storage\Handler\NativeSessionHandler` base class.
+ * Added internal storage driver proxy mechanism for forward compatibility with
+   PHP 5.4 `\SessionHandler` class.
+ * Added session handlers for custom Memcache, Memcached and Null session save handlers.
+ * [BC BREAK] Removed `NativeSessionStorage` and replaced with `NativeFileSessionHandler`.
+ * [BC BREAK] `SessionStorageInterface` methods removed: `write()`, `read()` and
+   `remove()`.  Added `getBag()`, `registerBag()`.  The `NativeSessionStorage` class
+   is a mediator for the session storage internals including the session handlers
+   which do the real work of participating in the internal PHP session workflow.
+ * [BC BREAK] Introduced mock implementations of `SessionStorage` to enable unit
+   and functional testing without starting real PHP sessions.  Removed
+   `ArraySessionStorage`, and replaced with `MockArraySessionStorage` for unit
+   tests; removed `FilesystemSessionStorage`, and replaced with`MockFileSessionStorage`
+   for functional tests.  These do not interact with global session ini
+   configuration values, session functions or `$_SESSION` superglobal. This means
+   they can be configured directly allowing multiple instances to work without
+   conflicting in the same PHP process.
+ * [BC BREAK] Removed the `close()` method from the `Session` class, as this is
+   now redundant.
+ * Deprecated the following methods from the Session class: `setFlash()`, `setFlashes()`
+   `getFlash()`, `hasFlash()`, and `removeFlash()`. Use `getFlashBag()` instead
+   which returns a `FlashBagInterface`.
+ * `Session->clear()` now only clears session attributes as before it cleared
+   flash messages and attributes. `Session->getFlashBag()->all()` clears flashes now.
+ * Session data is now managed by `SessionBagInterface` to better encapsulate
+   session data.
+ * Refactored session attribute and flash messages system to their own
+  `SessionBagInterface` implementations.
+ * Added `FlashBag`. Flashes expire when retrieved by `get()` or `all()`. This
+   implementation is ESI compatible.
+ * Added `AutoExpireFlashBag` (default) to replicate Symfony 2.0.x auto expire
+   behavior of messages auto expiring after one page page load.  Messages must
+   be retrieved by `get()` or `all()`.
+ * Added `Symfony\Component\HttpFoundation\Attribute\AttributeBag` to replicate
+   attributes storage behavior from 2.0.x (default).
+ * Added `Symfony\Component\HttpFoundation\Attribute\NamespacedAttributeBag` for
+   namespace session attributes.
+ * Flash API can stores messages in an array so there may be multiple messages
+   per flash type.  The old `Session` class API remains without BC break as it
+   will allow single messages as before.
+ * Added basic session meta-data to the session to record session create time,
+   last updated time, and the lifetime of the session cookie that was provided
+   to the client.
+ * Request::getClientIp() method doesn't take a parameter anymore but bases
+   itself on the trustProxy parameter.
+ * Added isMethod() to Request object.
+ * [BC BREAK] The methods `getPathInfo()`, `getBaseUrl()` and `getBasePath()` of
+   a `Request` now all return a raw value (vs a urldecoded value before). Any call
+   to one of these methods must be checked and wrapped in a `rawurldecode()` if
+   needed.
diff --git a/vendor/symfony/http-foundation/Cookie.php b/vendor/symfony/http-foundation/Cookie.php
new file mode 100755
index 0000000..83a9708
--- /dev/null
+++ b/vendor/symfony/http-foundation/Cookie.php
@@ -0,0 +1,292 @@
+<?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\HttpFoundation;
+
+/**
+ * Represents a cookie.
+ *
+ * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ */
+class Cookie
+{
+    protected $name;
+    protected $value;
+    protected $domain;
+    protected $expire;
+    protected $path;
+    protected $secure;
+    protected $httpOnly;
+    private $raw;
+    private $sameSite;
+
+    const SAMESITE_NONE = 'none';
+    const SAMESITE_LAX = 'lax';
+    const SAMESITE_STRICT = 'strict';
+
+    /**
+     * Creates cookie from raw header string.
+     *
+     * @param string $cookie
+     * @param bool   $decode
+     *
+     * @return static
+     */
+    public static function fromString($cookie, $decode = false)
+    {
+        $data = [
+            'expires' => 0,
+            'path' => '/',
+            'domain' => null,
+            'secure' => false,
+            'httponly' => false,
+            'raw' => !$decode,
+            'samesite' => null,
+        ];
+        foreach (explode(';', $cookie) as $part) {
+            if (false === strpos($part, '=')) {
+                $key = trim($part);
+                $value = true;
+            } else {
+                list($key, $value) = explode('=', trim($part), 2);
+                $key = trim($key);
+                $value = trim($value);
+            }
+            if (!isset($data['name'])) {
+                $data['name'] = $decode ? urldecode($key) : $key;
+                $data['value'] = true === $value ? null : ($decode ? urldecode($value) : $value);
+                continue;
+            }
+            switch ($key = strtolower($key)) {
+                case 'name':
+                case 'value':
+                    break;
+                case 'max-age':
+                    $data['expires'] = time() + (int) $value;
+                    break;
+                default:
+                    $data[$key] = $value;
+                    break;
+            }
+        }
+
+        return new static($data['name'], $data['value'], $data['expires'], $data['path'], $data['domain'], $data['secure'], $data['httponly'], $data['raw'], $data['samesite']);
+    }
+
+    /**
+     * @param string                        $name     The name of the cookie
+     * @param string|null                   $value    The value of the cookie
+     * @param int|string|\DateTimeInterface $expire   The time the cookie expires
+     * @param string                        $path     The path on the server in which the cookie will be available on
+     * @param string|null                   $domain   The domain that the cookie is available to
+     * @param bool                          $secure   Whether the cookie should only be transmitted over a secure HTTPS connection from the client
+     * @param bool                          $httpOnly Whether the cookie will be made accessible only through the HTTP protocol
+     * @param bool                          $raw      Whether the cookie value should be sent with no url encoding
+     * @param string|null                   $sameSite Whether the cookie will be available for cross-site requests
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function __construct($name, $value = null, $expire = 0, $path = '/', $domain = null, $secure = false, $httpOnly = true, $raw = false, $sameSite = null)
+    {
+        // from PHP source code
+        if (preg_match("/[=,; \t\r\n\013\014]/", $name)) {
+            throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $name));
+        }
+
+        if (empty($name)) {
+            throw new \InvalidArgumentException('The cookie name cannot be empty.');
+        }
+
+        // convert expiration time to a Unix timestamp
+        if ($expire instanceof \DateTimeInterface) {
+            $expire = $expire->format('U');
+        } elseif (!is_numeric($expire)) {
+            $expire = strtotime($expire);
+
+            if (false === $expire) {
+                throw new \InvalidArgumentException('The cookie expiration time is not valid.');
+            }
+        }
+
+        $this->name = $name;
+        $this->value = $value;
+        $this->domain = $domain;
+        $this->expire = 0 < $expire ? (int) $expire : 0;
+        $this->path = empty($path) ? '/' : $path;
+        $this->secure = (bool) $secure;
+        $this->httpOnly = (bool) $httpOnly;
+        $this->raw = (bool) $raw;
+
+        if (null !== $sameSite) {
+            $sameSite = strtolower($sameSite);
+        }
+
+        if (!\in_array($sameSite, [self::SAMESITE_LAX, self::SAMESITE_STRICT, self::SAMESITE_NONE, null], true)) {
+            throw new \InvalidArgumentException('The "sameSite" parameter value is not valid.');
+        }
+
+        $this->sameSite = $sameSite;
+    }
+
+    /**
+     * Returns the cookie as a string.
+     *
+     * @return string The cookie
+     */
+    public function __toString()
+    {
+        $str = ($this->isRaw() ? $this->getName() : urlencode($this->getName())).'=';
+
+        if ('' === (string) $this->getValue()) {
+            $str .= 'deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0';
+        } else {
+            $str .= $this->isRaw() ? $this->getValue() : rawurlencode($this->getValue());
+
+            if (0 !== $this->getExpiresTime()) {
+                $str .= '; expires='.gmdate('D, d-M-Y H:i:s T', $this->getExpiresTime()).'; Max-Age='.$this->getMaxAge();
+            }
+        }
+
+        if ($this->getPath()) {
+            $str .= '; path='.$this->getPath();
+        }
+
+        if ($this->getDomain()) {
+            $str .= '; domain='.$this->getDomain();
+        }
+
+        if (true === $this->isSecure()) {
+            $str .= '; secure';
+        }
+
+        if (true === $this->isHttpOnly()) {
+            $str .= '; httponly';
+        }
+
+        if (null !== $this->getSameSite()) {
+            $str .= '; samesite='.$this->getSameSite();
+        }
+
+        return $str;
+    }
+
+    /**
+     * Gets the name of the cookie.
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * Gets the value of the cookie.
+     *
+     * @return string|null
+     */
+    public function getValue()
+    {
+        return $this->value;
+    }
+
+    /**
+     * Gets the domain that the cookie is available to.
+     *
+     * @return string|null
+     */
+    public function getDomain()
+    {
+        return $this->domain;
+    }
+
+    /**
+     * Gets the time the cookie expires.
+     *
+     * @return int
+     */
+    public function getExpiresTime()
+    {
+        return $this->expire;
+    }
+
+    /**
+     * Gets the max-age attribute.
+     *
+     * @return int
+     */
+    public function getMaxAge()
+    {
+        $maxAge = $this->expire - time();
+
+        return 0 >= $maxAge ? 0 : $maxAge;
+    }
+
+    /**
+     * Gets the path on the server in which the cookie will be available on.
+     *
+     * @return string
+     */
+    public function getPath()
+    {
+        return $this->path;
+    }
+
+    /**
+     * Checks whether the cookie should only be transmitted over a secure HTTPS connection from the client.
+     *
+     * @return bool
+     */
+    public function isSecure()
+    {
+        return $this->secure;
+    }
+
+    /**
+     * Checks whether the cookie will be made accessible only through the HTTP protocol.
+     *
+     * @return bool
+     */
+    public function isHttpOnly()
+    {
+        return $this->httpOnly;
+    }
+
+    /**
+     * Whether this cookie is about to be cleared.
+     *
+     * @return bool
+     */
+    public function isCleared()
+    {
+        return 0 !== $this->expire && $this->expire < time();
+    }
+
+    /**
+     * Checks if the cookie value should be sent with no url encoding.
+     *
+     * @return bool
+     */
+    public function isRaw()
+    {
+        return $this->raw;
+    }
+
+    /**
+     * Gets the SameSite attribute.
+     *
+     * @return string|null
+     */
+    public function getSameSite()
+    {
+        return $this->sameSite;
+    }
+}
diff --git a/vendor/symfony/http-foundation/Exception/ConflictingHeadersException.php b/vendor/symfony/http-foundation/Exception/ConflictingHeadersException.php
new file mode 100755
index 0000000..5fcf5b4
--- /dev/null
+++ b/vendor/symfony/http-foundation/Exception/ConflictingHeadersException.php
@@ -0,0 +1,21 @@
+<?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\HttpFoundation\Exception;
+
+/**
+ * The HTTP request contains headers with conflicting information.
+ *
+ * @author Magnus Nordlander <magnus@fervo.se>
+ */
+class ConflictingHeadersException extends \UnexpectedValueException implements RequestExceptionInterface
+{
+}
diff --git a/vendor/symfony/http-foundation/Exception/RequestExceptionInterface.php b/vendor/symfony/http-foundation/Exception/RequestExceptionInterface.php
new file mode 100755
index 0000000..478d0dc
--- /dev/null
+++ b/vendor/symfony/http-foundation/Exception/RequestExceptionInterface.php
@@ -0,0 +1,21 @@
+<?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\HttpFoundation\Exception;
+
+/**
+ * Interface for Request exceptions.
+ *
+ * Exceptions implementing this interface should trigger an HTTP 400 response in the application code.
+ */
+interface RequestExceptionInterface
+{
+}
diff --git a/vendor/symfony/http-foundation/Exception/SuspiciousOperationException.php b/vendor/symfony/http-foundation/Exception/SuspiciousOperationException.php
new file mode 100755
index 0000000..ae7a5f1
--- /dev/null
+++ b/vendor/symfony/http-foundation/Exception/SuspiciousOperationException.php
@@ -0,0 +1,20 @@
+<?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\HttpFoundation\Exception;
+
+/**
+ * Raised when a user has performed an operation that should be considered
+ * suspicious from a security perspective.
+ */
+class SuspiciousOperationException extends \UnexpectedValueException implements RequestExceptionInterface
+{
+}
diff --git a/vendor/symfony/http-foundation/ExpressionRequestMatcher.php b/vendor/symfony/http-foundation/ExpressionRequestMatcher.php
new file mode 100755
index 0000000..26bed7d
--- /dev/null
+++ b/vendor/symfony/http-foundation/ExpressionRequestMatcher.php
@@ -0,0 +1,47 @@
+<?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\HttpFoundation;
+
+use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
+
+/**
+ * ExpressionRequestMatcher uses an expression to match a Request.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class ExpressionRequestMatcher extends RequestMatcher
+{
+    private $language;
+    private $expression;
+
+    public function setExpression(ExpressionLanguage $language, $expression)
+    {
+        $this->language = $language;
+        $this->expression = $expression;
+    }
+
+    public function matches(Request $request)
+    {
+        if (!$this->language) {
+            throw new \LogicException('Unable to match the request as the expression language is not available.');
+        }
+
+        return $this->language->evaluate($this->expression, [
+            'request' => $request,
+            'method' => $request->getMethod(),
+            'path' => rawurldecode($request->getPathInfo()),
+            'host' => $request->getHost(),
+            'ip' => $request->getClientIp(),
+            'attributes' => $request->attributes->all(),
+        ]) && parent::matches($request);
+    }
+}
diff --git a/vendor/symfony/http-foundation/File/Exception/AccessDeniedException.php b/vendor/symfony/http-foundation/File/Exception/AccessDeniedException.php
new file mode 100755
index 0000000..3b8e41d
--- /dev/null
+++ b/vendor/symfony/http-foundation/File/Exception/AccessDeniedException.php
@@ -0,0 +1,28 @@
+<?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\HttpFoundation\File\Exception;
+
+/**
+ * Thrown when the access on a file was denied.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class AccessDeniedException extends FileException
+{
+    /**
+     * @param string $path The path to the accessed file
+     */
+    public function __construct($path)
+    {
+        parent::__construct(sprintf('The file %s could not be accessed', $path));
+    }
+}
diff --git a/vendor/symfony/http-foundation/File/Exception/FileException.php b/vendor/symfony/http-foundation/File/Exception/FileException.php
new file mode 100755
index 0000000..fad5133
--- /dev/null
+++ b/vendor/symfony/http-foundation/File/Exception/FileException.php
@@ -0,0 +1,21 @@
+<?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\HttpFoundation\File\Exception;
+
+/**
+ * Thrown when an error occurred in the component File.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class FileException extends \RuntimeException
+{
+}
diff --git a/vendor/symfony/http-foundation/File/Exception/FileNotFoundException.php b/vendor/symfony/http-foundation/File/Exception/FileNotFoundException.php
new file mode 100755
index 0000000..bfcc37e
--- /dev/null
+++ b/vendor/symfony/http-foundation/File/Exception/FileNotFoundException.php
@@ -0,0 +1,28 @@
+<?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\HttpFoundation\File\Exception;
+
+/**
+ * Thrown when a file was not found.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class FileNotFoundException extends FileException
+{
+    /**
+     * @param string $path The path to the file that was not found
+     */
+    public function __construct($path)
+    {
+        parent::__construct(sprintf('The file "%s" does not exist', $path));
+    }
+}
diff --git a/vendor/symfony/http-foundation/File/Exception/UnexpectedTypeException.php b/vendor/symfony/http-foundation/File/Exception/UnexpectedTypeException.php
new file mode 100755
index 0000000..62005d3
--- /dev/null
+++ b/vendor/symfony/http-foundation/File/Exception/UnexpectedTypeException.php
@@ -0,0 +1,20 @@
+<?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\HttpFoundation\File\Exception;
+
+class UnexpectedTypeException extends FileException
+{
+    public function __construct($value, $expectedType)
+    {
+        parent::__construct(sprintf('Expected argument of type %s, %s given', $expectedType, \is_object($value) ? \get_class($value) : \gettype($value)));
+    }
+}
diff --git a/vendor/symfony/http-foundation/File/Exception/UploadException.php b/vendor/symfony/http-foundation/File/Exception/UploadException.php
new file mode 100755
index 0000000..7074e76
--- /dev/null
+++ b/vendor/symfony/http-foundation/File/Exception/UploadException.php
@@ -0,0 +1,21 @@
+<?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\HttpFoundation\File\Exception;
+
+/**
+ * Thrown when an error occurred during file upload.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class UploadException extends FileException
+{
+}
diff --git a/vendor/symfony/http-foundation/File/File.php b/vendor/symfony/http-foundation/File/File.php
new file mode 100755
index 0000000..3422058
--- /dev/null
+++ b/vendor/symfony/http-foundation/File/File.php
@@ -0,0 +1,138 @@
+<?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\HttpFoundation\File;
+
+use Symfony\Component\HttpFoundation\File\Exception\FileException;
+use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
+use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser;
+use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
+
+/**
+ * A file in the file system.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class File extends \SplFileInfo
+{
+    /**
+     * Constructs a new file from the given path.
+     *
+     * @param string $path      The path to the file
+     * @param bool   $checkPath Whether to check the path or not
+     *
+     * @throws FileNotFoundException If the given path is not a file
+     */
+    public function __construct($path, $checkPath = true)
+    {
+        if ($checkPath && !is_file($path)) {
+            throw new FileNotFoundException($path);
+        }
+
+        parent::__construct($path);
+    }
+
+    /**
+     * Returns the extension based on the mime type.
+     *
+     * If the mime type is unknown, returns null.
+     *
+     * This method uses the mime type as guessed by getMimeType()
+     * to guess the file extension.
+     *
+     * @return string|null The guessed extension or null if it cannot be guessed
+     *
+     * @see ExtensionGuesser
+     * @see getMimeType()
+     */
+    public function guessExtension()
+    {
+        $type = $this->getMimeType();
+        $guesser = ExtensionGuesser::getInstance();
+
+        return $guesser->guess($type);
+    }
+
+    /**
+     * Returns the mime type of the file.
+     *
+     * The mime type is guessed using a MimeTypeGuesser instance, which uses finfo(),
+     * mime_content_type() and the system binary "file" (in this order), depending on
+     * which of those are available.
+     *
+     * @return string|null The guessed mime type (e.g. "application/pdf")
+     *
+     * @see MimeTypeGuesser
+     */
+    public function getMimeType()
+    {
+        $guesser = MimeTypeGuesser::getInstance();
+
+        return $guesser->guess($this->getPathname());
+    }
+
+    /**
+     * Moves the file to a new location.
+     *
+     * @param string $directory The destination folder
+     * @param string $name      The new file name
+     *
+     * @return self A File object representing the new file
+     *
+     * @throws FileException if the target file could not be created
+     */
+    public function move($directory, $name = null)
+    {
+        $target = $this->getTargetFile($directory, $name);
+
+        set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; });
+        $renamed = rename($this->getPathname(), $target);
+        restore_error_handler();
+        if (!$renamed) {
+            throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error)));
+        }
+
+        @chmod($target, 0666 & ~umask());
+
+        return $target;
+    }
+
+    protected function getTargetFile($directory, $name = null)
+    {
+        if (!is_dir($directory)) {
+            if (false === @mkdir($directory, 0777, true) && !is_dir($directory)) {
+                throw new FileException(sprintf('Unable to create the "%s" directory', $directory));
+            }
+        } elseif (!is_writable($directory)) {
+            throw new FileException(sprintf('Unable to write in the "%s" directory', $directory));
+        }
+
+        $target = rtrim($directory, '/\\').\DIRECTORY_SEPARATOR.(null === $name ? $this->getBasename() : $this->getName($name));
+
+        return new self($target, false);
+    }
+
+    /**
+     * Returns locale independent base name of the given path.
+     *
+     * @param string $name The new file name
+     *
+     * @return string containing
+     */
+    protected function getName($name)
+    {
+        $originalName = str_replace('\\', '/', $name);
+        $pos = strrpos($originalName, '/');
+        $originalName = false === $pos ? $originalName : substr($originalName, $pos + 1);
+
+        return $originalName;
+    }
+}
diff --git a/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesser.php b/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesser.php
new file mode 100755
index 0000000..80f4d47
--- /dev/null
+++ b/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesser.php
@@ -0,0 +1,94 @@
+<?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\HttpFoundation\File\MimeType;
+
+/**
+ * A singleton mime type to file extension guesser.
+ *
+ * A default guesser is provided.
+ * You can register custom guessers by calling the register()
+ * method on the singleton instance:
+ *
+ *     $guesser = ExtensionGuesser::getInstance();
+ *     $guesser->register(new MyCustomExtensionGuesser());
+ *
+ * The last registered guesser is preferred over previously registered ones.
+ */
+class ExtensionGuesser implements ExtensionGuesserInterface
+{
+    /**
+     * The singleton instance.
+     *
+     * @var ExtensionGuesser
+     */
+    private static $instance = null;
+
+    /**
+     * All registered ExtensionGuesserInterface instances.
+     *
+     * @var array
+     */
+    protected $guessers = [];
+
+    /**
+     * Returns the singleton instance.
+     *
+     * @return self
+     */
+    public static function getInstance()
+    {
+        if (null === self::$instance) {
+            self::$instance = new self();
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     * Registers all natively provided extension guessers.
+     */
+    private function __construct()
+    {
+        $this->register(new MimeTypeExtensionGuesser());
+    }
+
+    /**
+     * Registers a new extension guesser.
+     *
+     * When guessing, this guesser is preferred over previously registered ones.
+     */
+    public function register(ExtensionGuesserInterface $guesser)
+    {
+        array_unshift($this->guessers, $guesser);
+    }
+
+    /**
+     * Tries to guess the extension.
+     *
+     * The mime type is passed to each registered mime type guesser in reverse order
+     * of their registration (last registered is queried first). Once a guesser
+     * returns a value that is not NULL, this method terminates and returns the
+     * value.
+     *
+     * @param string $mimeType The mime type
+     *
+     * @return string The guessed extension or NULL, if none could be guessed
+     */
+    public function guess($mimeType)
+    {
+        foreach ($this->guessers as $guesser) {
+            if (null !== $extension = $guesser->guess($mimeType)) {
+                return $extension;
+            }
+        }
+    }
+}
diff --git a/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesserInterface.php b/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesserInterface.php
new file mode 100755
index 0000000..d19a0e5
--- /dev/null
+++ b/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesserInterface.php
@@ -0,0 +1,27 @@
+<?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\HttpFoundation\File\MimeType;
+
+/**
+ * Guesses the file extension corresponding to a given mime type.
+ */
+interface ExtensionGuesserInterface
+{
+    /**
+     * Makes a best guess for a file extension, given a mime type.
+     *
+     * @param string $mimeType The mime type
+     *
+     * @return string The guessed extension or NULL, if none could be guessed
+     */
+    public function guess($mimeType);
+}
diff --git a/vendor/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.php b/vendor/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.php
new file mode 100755
index 0000000..34e015e
--- /dev/null
+++ b/vendor/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.php
@@ -0,0 +1,99 @@
+<?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\HttpFoundation\File\MimeType;
+
+use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
+use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
+
+/**
+ * Guesses the mime type with the binary "file" (only available on *nix).
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface
+{
+    private $cmd;
+
+    /**
+     * The $cmd pattern must contain a "%s" string that will be replaced
+     * with the file name to guess.
+     *
+     * The command output must start with the mime type of the file.
+     *
+     * @param string $cmd The command to run to get the mime type of a file
+     */
+    public function __construct($cmd = 'file -b --mime %s 2>/dev/null')
+    {
+        $this->cmd = $cmd;
+    }
+
+    /**
+     * Returns whether this guesser is supported on the current OS.
+     *
+     * @return bool
+     */
+    public static function isSupported()
+    {
+        static $supported = null;
+
+        if (null !== $supported) {
+            return $supported;
+        }
+
+        if ('\\' === \DIRECTORY_SEPARATOR || !\function_exists('passthru') || !\function_exists('escapeshellarg')) {
+            return $supported = false;
+        }
+
+        ob_start();
+        passthru('command -v file', $exitStatus);
+        $binPath = trim(ob_get_clean());
+
+        return $supported = 0 === $exitStatus && '' !== $binPath;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function guess($path)
+    {
+        if (!is_file($path)) {
+            throw new FileNotFoundException($path);
+        }
+
+        if (!is_readable($path)) {
+            throw new AccessDeniedException($path);
+        }
+
+        if (!self::isSupported()) {
+            return;
+        }
+
+        ob_start();
+
+        // need to use --mime instead of -i. see #6641
+        passthru(sprintf($this->cmd, escapeshellarg($path)), $return);
+        if ($return > 0) {
+            ob_end_clean();
+
+            return;
+        }
+
+        $type = trim(ob_get_clean());
+
+        if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-\.]+)#i', $type, $match)) {
+            // it's not a type, but an error message
+            return;
+        }
+
+        return $match[1];
+    }
+}
diff --git a/vendor/symfony/http-foundation/File/MimeType/FileinfoMimeTypeGuesser.php b/vendor/symfony/http-foundation/File/MimeType/FileinfoMimeTypeGuesser.php
new file mode 100755
index 0000000..bf1ee9f
--- /dev/null
+++ b/vendor/symfony/http-foundation/File/MimeType/FileinfoMimeTypeGuesser.php
@@ -0,0 +1,69 @@
+<?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\HttpFoundation\File\MimeType;
+
+use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
+use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
+
+/**
+ * Guesses the mime type using the PECL extension FileInfo.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface
+{
+    private $magicFile;
+
+    /**
+     * @param string $magicFile A magic file to use with the finfo instance
+     *
+     * @see http://www.php.net/manual/en/function.finfo-open.php
+     */
+    public function __construct($magicFile = null)
+    {
+        $this->magicFile = $magicFile;
+    }
+
+    /**
+     * Returns whether this guesser is supported on the current OS/PHP setup.
+     *
+     * @return bool
+     */
+    public static function isSupported()
+    {
+        return \function_exists('finfo_open');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function guess($path)
+    {
+        if (!is_file($path)) {
+            throw new FileNotFoundException($path);
+        }
+
+        if (!is_readable($path)) {
+            throw new AccessDeniedException($path);
+        }
+
+        if (!self::isSupported()) {
+            return;
+        }
+
+        if (!$finfo = new \finfo(FILEINFO_MIME_TYPE, $this->magicFile)) {
+            return;
+        }
+
+        return $finfo->file($path);
+    }
+}
diff --git a/vendor/symfony/http-foundation/File/MimeType/MimeTypeExtensionGuesser.php b/vendor/symfony/http-foundation/File/MimeType/MimeTypeExtensionGuesser.php
new file mode 100755
index 0000000..c0f9140
--- /dev/null
+++ b/vendor/symfony/http-foundation/File/MimeType/MimeTypeExtensionGuesser.php
@@ -0,0 +1,819 @@
+<?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\HttpFoundation\File\MimeType;
+
+/**
+ * Provides a best-guess mapping of mime type to file extension.
+ */
+class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
+{
+    /**
+     * A map of mime types and their default extensions.
+     *
+     * This list has been placed under the public domain by the Apache HTTPD project.
+     * This list has been updated from upstream on 2019-01-14.
+     *
+     * @see https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
+     */
+    protected $defaultExtensions = [
+        'application/andrew-inset' => 'ez',
+        'application/applixware' => 'aw',
+        'application/atom+xml' => 'atom',
+        'application/atomcat+xml' => 'atomcat',
+        'application/atomsvc+xml' => 'atomsvc',
+        'application/ccxml+xml' => 'ccxml',
+        'application/cdmi-capability' => 'cdmia',
+        'application/cdmi-container' => 'cdmic',
+        'application/cdmi-domain' => 'cdmid',
+        'application/cdmi-object' => 'cdmio',
+        'application/cdmi-queue' => 'cdmiq',
+        'application/cu-seeme' => 'cu',
+        'application/davmount+xml' => 'davmount',
+        'application/docbook+xml' => 'dbk',
+        'application/dssc+der' => 'dssc',
+        'application/dssc+xml' => 'xdssc',
+        'application/ecmascript' => 'ecma',
+        'application/emma+xml' => 'emma',
+        'application/epub+zip' => 'epub',
+        'application/exi' => 'exi',
+        'application/font-tdpfr' => 'pfr',
+        'application/gml+xml' => 'gml',
+        'application/gpx+xml' => 'gpx',
+        'application/gxf' => 'gxf',
+        'application/hyperstudio' => 'stk',
+        'application/inkml+xml' => 'ink',
+        'application/ipfix' => 'ipfix',
+        'application/java-archive' => 'jar',
+        'application/java-serialized-object' => 'ser',
+        'application/java-vm' => 'class',
+        'application/javascript' => 'js',
+        'application/json' => 'json',
+        'application/jsonml+json' => 'jsonml',
+        'application/lost+xml' => 'lostxml',
+        'application/mac-binhex40' => 'hqx',
+        'application/mac-compactpro' => 'cpt',
+        'application/mads+xml' => 'mads',
+        'application/marc' => 'mrc',
+        'application/marcxml+xml' => 'mrcx',
+        'application/mathematica' => 'ma',
+        'application/mathml+xml' => 'mathml',
+        'application/mbox' => 'mbox',
+        'application/mediaservercontrol+xml' => 'mscml',
+        'application/metalink+xml' => 'metalink',
+        'application/metalink4+xml' => 'meta4',
+        'application/mets+xml' => 'mets',
+        'application/mods+xml' => 'mods',
+        'application/mp21' => 'm21',
+        'application/mp4' => 'mp4s',
+        'application/msword' => 'doc',
+        'application/mxf' => 'mxf',
+        'application/octet-stream' => 'bin',
+        'application/oda' => 'oda',
+        'application/oebps-package+xml' => 'opf',
+        'application/ogg' => 'ogx',
+        'application/omdoc+xml' => 'omdoc',
+        'application/onenote' => 'onetoc',
+        'application/oxps' => 'oxps',
+        'application/patch-ops-error+xml' => 'xer',
+        'application/pdf' => 'pdf',
+        'application/pgp-encrypted' => 'pgp',
+        'application/pgp-signature' => 'asc',
+        'application/pics-rules' => 'prf',
+        'application/pkcs10' => 'p10',
+        'application/pkcs7-mime' => 'p7m',
+        'application/pkcs7-signature' => 'p7s',
+        'application/pkcs8' => 'p8',
+        'application/pkix-attr-cert' => 'ac',
+        'application/pkix-cert' => 'cer',
+        'application/pkix-crl' => 'crl',
+        'application/pkix-pkipath' => 'pkipath',
+        'application/pkixcmp' => 'pki',
+        'application/pls+xml' => 'pls',
+        'application/postscript' => 'ai',
+        'application/prs.cww' => 'cww',
+        'application/pskc+xml' => 'pskcxml',
+        'application/rdf+xml' => 'rdf',
+        'application/reginfo+xml' => 'rif',
+        'application/relax-ng-compact-syntax' => 'rnc',
+        'application/resource-lists+xml' => 'rl',
+        'application/resource-lists-diff+xml' => 'rld',
+        'application/rls-services+xml' => 'rs',
+        'application/rpki-ghostbusters' => 'gbr',
+        'application/rpki-manifest' => 'mft',
+        'application/rpki-roa' => 'roa',
+        'application/rsd+xml' => 'rsd',
+        'application/rss+xml' => 'rss',
+        'application/rtf' => 'rtf',
+        'application/sbml+xml' => 'sbml',
+        'application/scvp-cv-request' => 'scq',
+        'application/scvp-cv-response' => 'scs',
+        'application/scvp-vp-request' => 'spq',
+        'application/scvp-vp-response' => 'spp',
+        'application/sdp' => 'sdp',
+        'application/set-payment-initiation' => 'setpay',
+        'application/set-registration-initiation' => 'setreg',
+        'application/shf+xml' => 'shf',
+        'application/smil+xml' => 'smi',
+        'application/sparql-query' => 'rq',
+        'application/sparql-results+xml' => 'srx',
+        'application/srgs' => 'gram',
+        'application/srgs+xml' => 'grxml',
+        'application/sru+xml' => 'sru',
+        'application/ssdl+xml' => 'ssdl',
+        'application/ssml+xml' => 'ssml',
+        'application/tei+xml' => 'tei',
+        'application/thraud+xml' => 'tfi',
+        'application/timestamped-data' => 'tsd',
+        'application/vnd.3gpp.pic-bw-large' => 'plb',
+        'application/vnd.3gpp.pic-bw-small' => 'psb',
+        'application/vnd.3gpp.pic-bw-var' => 'pvb',
+        'application/vnd.3gpp2.tcap' => 'tcap',
+        'application/vnd.3m.post-it-notes' => 'pwn',
+        'application/vnd.accpac.simply.aso' => 'aso',
+        'application/vnd.accpac.simply.imp' => 'imp',
+        'application/vnd.acucobol' => 'acu',
+        'application/vnd.acucorp' => 'atc',
+        'application/vnd.adobe.air-application-installer-package+zip' => 'air',
+        'application/vnd.adobe.formscentral.fcdt' => 'fcdt',
+        'application/vnd.adobe.fxp' => 'fxp',
+        'application/vnd.adobe.xdp+xml' => 'xdp',
+        'application/vnd.adobe.xfdf' => 'xfdf',
+        'application/vnd.ahead.space' => 'ahead',
+        'application/vnd.airzip.filesecure.azf' => 'azf',
+        'application/vnd.airzip.filesecure.azs' => 'azs',
+        'application/vnd.amazon.ebook' => 'azw',
+        'application/vnd.americandynamics.acc' => 'acc',
+        'application/vnd.amiga.ami' => 'ami',
+        'application/vnd.android.package-archive' => 'apk',
+        'application/vnd.anser-web-certificate-issue-initiation' => 'cii',
+        'application/vnd.anser-web-funds-transfer-initiation' => 'fti',
+        'application/vnd.antix.game-component' => 'atx',
+        'application/vnd.apple.installer+xml' => 'mpkg',
+        'application/vnd.apple.mpegurl' => 'm3u8',
+        'application/vnd.aristanetworks.swi' => 'swi',
+        'application/vnd.astraea-software.iota' => 'iota',
+        'application/vnd.audiograph' => 'aep',
+        'application/vnd.blueice.multipass' => 'mpm',
+        'application/vnd.bmi' => 'bmi',
+        'application/vnd.businessobjects' => 'rep',
+        'application/vnd.chemdraw+xml' => 'cdxml',
+        'application/vnd.chipnuts.karaoke-mmd' => 'mmd',
+        'application/vnd.cinderella' => 'cdy',
+        'application/vnd.claymore' => 'cla',
+        'application/vnd.cloanto.rp9' => 'rp9',
+        'application/vnd.clonk.c4group' => 'c4g',
+        'application/vnd.cluetrust.cartomobile-config' => 'c11amc',
+        'application/vnd.cluetrust.cartomobile-config-pkg' => 'c11amz',
+        'application/vnd.commonspace' => 'csp',
+        'application/vnd.contact.cmsg' => 'cdbcmsg',
+        'application/vnd.cosmocaller' => 'cmc',
+        'application/vnd.crick.clicker' => 'clkx',
+        'application/vnd.crick.clicker.keyboard' => 'clkk',
+        'application/vnd.crick.clicker.palette' => 'clkp',
+        'application/vnd.crick.clicker.template' => 'clkt',
+        'application/vnd.crick.clicker.wordbank' => 'clkw',
+        'application/vnd.criticaltools.wbs+xml' => 'wbs',
+        'application/vnd.ctc-posml' => 'pml',
+        'application/vnd.cups-ppd' => 'ppd',
+        'application/vnd.curl.car' => 'car',
+        'application/vnd.curl.pcurl' => 'pcurl',
+        'application/vnd.dart' => 'dart',
+        'application/vnd.data-vision.rdz' => 'rdz',
+        'application/vnd.dece.data' => 'uvf',
+        'application/vnd.dece.ttml+xml' => 'uvt',
+        'application/vnd.dece.unspecified' => 'uvx',
+        'application/vnd.dece.zip' => 'uvz',
+        'application/vnd.denovo.fcselayout-link' => 'fe_launch',
+        'application/vnd.dna' => 'dna',
+        'application/vnd.dolby.mlp' => 'mlp',
+        'application/vnd.dpgraph' => 'dpg',
+        'application/vnd.dreamfactory' => 'dfac',
+        'application/vnd.ds-keypoint' => 'kpxx',
+        'application/vnd.dvb.ait' => 'ait',
+        'application/vnd.dvb.service' => 'svc',
+        'application/vnd.dynageo' => 'geo',
+        'application/vnd.ecowin.chart' => 'mag',
+        'application/vnd.enliven' => 'nml',
+        'application/vnd.epson.esf' => 'esf',
+        'application/vnd.epson.msf' => 'msf',
+        'application/vnd.epson.quickanime' => 'qam',
+        'application/vnd.epson.salt' => 'slt',
+        'application/vnd.epson.ssf' => 'ssf',
+        'application/vnd.eszigno3+xml' => 'es3',
+        'application/vnd.ezpix-album' => 'ez2',
+        'application/vnd.ezpix-package' => 'ez3',
+        'application/vnd.fdf' => 'fdf',
+        'application/vnd.fdsn.mseed' => 'mseed',
+        'application/vnd.fdsn.seed' => 'seed',
+        'application/vnd.flographit' => 'gph',
+        'application/vnd.fluxtime.clip' => 'ftc',
+        'application/vnd.framemaker' => 'fm',
+        'application/vnd.frogans.fnc' => 'fnc',
+        'application/vnd.frogans.ltf' => 'ltf',
+        'application/vnd.fsc.weblaunch' => 'fsc',
+        'application/vnd.fujitsu.oasys' => 'oas',
+        'application/vnd.fujitsu.oasys2' => 'oa2',
+        'application/vnd.fujitsu.oasys3' => 'oa3',
+        'application/vnd.fujitsu.oasysgp' => 'fg5',
+        'application/vnd.fujitsu.oasysprs' => 'bh2',
+        'application/vnd.fujixerox.ddd' => 'ddd',
+        'application/vnd.fujixerox.docuworks' => 'xdw',
+        'application/vnd.fujixerox.docuworks.binder' => 'xbd',
+        'application/vnd.fuzzysheet' => 'fzs',
+        'application/vnd.genomatix.tuxedo' => 'txd',
+        'application/vnd.geogebra.file' => 'ggb',
+        'application/vnd.geogebra.tool' => 'ggt',
+        'application/vnd.geometry-explorer' => 'gex',
+        'application/vnd.geonext' => 'gxt',
+        'application/vnd.geoplan' => 'g2w',
+        'application/vnd.geospace' => 'g3w',
+        'application/vnd.gmx' => 'gmx',
+        'application/vnd.google-earth.kml+xml' => 'kml',
+        'application/vnd.google-earth.kmz' => 'kmz',
+        'application/vnd.grafeq' => 'gqf',
+        'application/vnd.groove-account' => 'gac',
+        'application/vnd.groove-help' => 'ghf',
+        'application/vnd.groove-identity-message' => 'gim',
+        'application/vnd.groove-injector' => 'grv',
+        'application/vnd.groove-tool-message' => 'gtm',
+        'application/vnd.groove-tool-template' => 'tpl',
+        'application/vnd.groove-vcard' => 'vcg',
+        'application/vnd.hal+xml' => 'hal',
+        'application/vnd.handheld-entertainment+xml' => 'zmm',
+        'application/vnd.hbci' => 'hbci',
+        'application/vnd.hhe.lesson-player' => 'les',
+        'application/vnd.hp-hpgl' => 'hpgl',
+        'application/vnd.hp-hpid' => 'hpid',
+        'application/vnd.hp-hps' => 'hps',
+        'application/vnd.hp-jlyt' => 'jlt',
+        'application/vnd.hp-pcl' => 'pcl',
+        'application/vnd.hp-pclxl' => 'pclxl',
+        'application/vnd.hydrostatix.sof-data' => 'sfd-hdstx',
+        'application/vnd.ibm.minipay' => 'mpy',
+        'application/vnd.ibm.modcap' => 'afp',
+        'application/vnd.ibm.rights-management' => 'irm',
+        'application/vnd.ibm.secure-container' => 'sc',
+        'application/vnd.iccprofile' => 'icc',
+        'application/vnd.igloader' => 'igl',
+        'application/vnd.immervision-ivp' => 'ivp',
+        'application/vnd.immervision-ivu' => 'ivu',
+        'application/vnd.insors.igm' => 'igm',
+        'application/vnd.intercon.formnet' => 'xpw',
+        'application/vnd.intergeo' => 'i2g',
+        'application/vnd.intu.qbo' => 'qbo',
+        'application/vnd.intu.qfx' => 'qfx',
+        'application/vnd.ipunplugged.rcprofile' => 'rcprofile',
+        'application/vnd.irepository.package+xml' => 'irp',
+        'application/vnd.is-xpr' => 'xpr',
+        'application/vnd.isac.fcs' => 'fcs',
+        'application/vnd.jam' => 'jam',
+        'application/vnd.jcp.javame.midlet-rms' => 'rms',
+        'application/vnd.jisp' => 'jisp',
+        'application/vnd.joost.joda-archive' => 'joda',
+        'application/vnd.kahootz' => 'ktz',
+        'application/vnd.kde.karbon' => 'karbon',
+        'application/vnd.kde.kchart' => 'chrt',
+        'application/vnd.kde.kformula' => 'kfo',
+        'application/vnd.kde.kivio' => 'flw',
+        'application/vnd.kde.kontour' => 'kon',
+        'application/vnd.kde.kpresenter' => 'kpr',
+        'application/vnd.kde.kspread' => 'ksp',
+        'application/vnd.kde.kword' => 'kwd',
+        'application/vnd.kenameaapp' => 'htke',
+        'application/vnd.kidspiration' => 'kia',
+        'application/vnd.kinar' => 'kne',
+        'application/vnd.koan' => 'skp',
+        'application/vnd.kodak-descriptor' => 'sse',
+        'application/vnd.las.las+xml' => 'lasxml',
+        'application/vnd.llamagraphics.life-balance.desktop' => 'lbd',
+        'application/vnd.llamagraphics.life-balance.exchange+xml' => 'lbe',
+        'application/vnd.lotus-1-2-3' => '123',
+        'application/vnd.lotus-approach' => 'apr',
+        'application/vnd.lotus-freelance' => 'pre',
+        'application/vnd.lotus-notes' => 'nsf',
+        'application/vnd.lotus-organizer' => 'org',
+        'application/vnd.lotus-screencam' => 'scm',
+        'application/vnd.lotus-wordpro' => 'lwp',
+        'application/vnd.macports.portpkg' => 'portpkg',
+        'application/vnd.mcd' => 'mcd',
+        'application/vnd.medcalcdata' => 'mc1',
+        'application/vnd.mediastation.cdkey' => 'cdkey',
+        'application/vnd.mfer' => 'mwf',
+        'application/vnd.mfmp' => 'mfm',
+        'application/vnd.micrografx.flo' => 'flo',
+        'application/vnd.micrografx.igx' => 'igx',
+        'application/vnd.mif' => 'mif',
+        'application/vnd.mobius.daf' => 'daf',
+        'application/vnd.mobius.dis' => 'dis',
+        'application/vnd.mobius.mbk' => 'mbk',
+        'application/vnd.mobius.mqy' => 'mqy',
+        'application/vnd.mobius.msl' => 'msl',
+        'application/vnd.mobius.plc' => 'plc',
+        'application/vnd.mobius.txf' => 'txf',
+        'application/vnd.mophun.application' => 'mpn',
+        'application/vnd.mophun.certificate' => 'mpc',
+        'application/vnd.mozilla.xul+xml' => 'xul',
+        'application/vnd.ms-artgalry' => 'cil',
+        'application/vnd.ms-cab-compressed' => 'cab',
+        'application/vnd.ms-excel' => 'xls',
+        'application/vnd.ms-excel.addin.macroenabled.12' => 'xlam',
+        'application/vnd.ms-excel.sheet.binary.macroenabled.12' => 'xlsb',
+        'application/vnd.ms-excel.sheet.macroenabled.12' => 'xlsm',
+        'application/vnd.ms-excel.template.macroenabled.12' => 'xltm',
+        'application/vnd.ms-fontobject' => 'eot',
+        'application/vnd.ms-htmlhelp' => 'chm',
+        'application/vnd.ms-ims' => 'ims',
+        'application/vnd.ms-lrm' => 'lrm',
+        'application/vnd.ms-officetheme' => 'thmx',
+        'application/vnd.ms-pki.seccat' => 'cat',
+        'application/vnd.ms-pki.stl' => 'stl',
+        'application/vnd.ms-powerpoint' => 'ppt',
+        'application/vnd.ms-powerpoint.addin.macroenabled.12' => 'ppam',
+        'application/vnd.ms-powerpoint.presentation.macroenabled.12' => 'pptm',
+        'application/vnd.ms-powerpoint.slide.macroenabled.12' => 'sldm',
+        'application/vnd.ms-powerpoint.slideshow.macroenabled.12' => 'ppsm',
+        'application/vnd.ms-powerpoint.template.macroenabled.12' => 'potm',
+        'application/vnd.ms-project' => 'mpp',
+        'application/vnd.ms-word.document.macroenabled.12' => 'docm',
+        'application/vnd.ms-word.template.macroenabled.12' => 'dotm',
+        'application/vnd.ms-works' => 'wps',
+        'application/vnd.ms-wpl' => 'wpl',
+        'application/vnd.ms-xpsdocument' => 'xps',
+        'application/vnd.mseq' => 'mseq',
+        'application/vnd.musician' => 'mus',
+        'application/vnd.muvee.style' => 'msty',
+        'application/vnd.mynfc' => 'taglet',
+        'application/vnd.neurolanguage.nlu' => 'nlu',
+        'application/vnd.nitf' => 'ntf',
+        'application/vnd.noblenet-directory' => 'nnd',
+        'application/vnd.noblenet-sealer' => 'nns',
+        'application/vnd.noblenet-web' => 'nnw',
+        'application/vnd.nokia.n-gage.data' => 'ngdat',
+        'application/vnd.nokia.n-gage.symbian.install' => 'n-gage',
+        'application/vnd.nokia.radio-preset' => 'rpst',
+        'application/vnd.nokia.radio-presets' => 'rpss',
+        'application/vnd.novadigm.edm' => 'edm',
+        'application/vnd.novadigm.edx' => 'edx',
+        'application/vnd.novadigm.ext' => 'ext',
+        'application/vnd.oasis.opendocument.chart' => 'odc',
+        'application/vnd.oasis.opendocument.chart-template' => 'otc',
+        'application/vnd.oasis.opendocument.database' => 'odb',
+        'application/vnd.oasis.opendocument.formula' => 'odf',
+        'application/vnd.oasis.opendocument.formula-template' => 'odft',
+        'application/vnd.oasis.opendocument.graphics' => 'odg',
+        'application/vnd.oasis.opendocument.graphics-template' => 'otg',
+        'application/vnd.oasis.opendocument.image' => 'odi',
+        'application/vnd.oasis.opendocument.image-template' => 'oti',
+        'application/vnd.oasis.opendocument.presentation' => 'odp',
+        'application/vnd.oasis.opendocument.presentation-template' => 'otp',
+        'application/vnd.oasis.opendocument.spreadsheet' => 'ods',
+        'application/vnd.oasis.opendocument.spreadsheet-template' => 'ots',
+        'application/vnd.oasis.opendocument.text' => 'odt',
+        'application/vnd.oasis.opendocument.text-master' => 'odm',
+        'application/vnd.oasis.opendocument.text-template' => 'ott',
+        'application/vnd.oasis.opendocument.text-web' => 'oth',
+        'application/vnd.olpc-sugar' => 'xo',
+        'application/vnd.oma.dd2+xml' => 'dd2',
+        'application/vnd.openofficeorg.extension' => 'oxt',
+        'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx',
+        'application/vnd.openxmlformats-officedocument.presentationml.slide' => 'sldx',
+        'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'ppsx',
+        'application/vnd.openxmlformats-officedocument.presentationml.template' => 'potx',
+        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx',
+        'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => 'xltx',
+        'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',
+        'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'dotx',
+        'application/vnd.osgeo.mapguide.package' => 'mgp',
+        'application/vnd.osgi.dp' => 'dp',
+        'application/vnd.osgi.subsystem' => 'esa',
+        'application/vnd.palm' => 'pdb',
+        'application/vnd.pawaafile' => 'paw',
+        'application/vnd.pg.format' => 'str',
+        'application/vnd.pg.osasli' => 'ei6',
+        'application/vnd.picsel' => 'efif',
+        'application/vnd.pmi.widget' => 'wg',
+        'application/vnd.pocketlearn' => 'plf',
+        'application/vnd.powerbuilder6' => 'pbd',
+        'application/vnd.previewsystems.box' => 'box',
+        'application/vnd.proteus.magazine' => 'mgz',
+        'application/vnd.publishare-delta-tree' => 'qps',
+        'application/vnd.pvi.ptid1' => 'ptid',
+        'application/vnd.quark.quarkxpress' => 'qxd',
+        'application/vnd.realvnc.bed' => 'bed',
+        'application/vnd.recordare.musicxml' => 'mxl',
+        'application/vnd.recordare.musicxml+xml' => 'musicxml',
+        'application/vnd.rig.cryptonote' => 'cryptonote',
+        'application/vnd.rim.cod' => 'cod',
+        'application/vnd.rn-realmedia' => 'rm',
+        'application/vnd.rn-realmedia-vbr' => 'rmvb',
+        'application/vnd.route66.link66+xml' => 'link66',
+        'application/vnd.sailingtracker.track' => 'st',
+        'application/vnd.seemail' => 'see',
+        'application/vnd.sema' => 'sema',
+        'application/vnd.semd' => 'semd',
+        'application/vnd.semf' => 'semf',
+        'application/vnd.shana.informed.formdata' => 'ifm',
+        'application/vnd.shana.informed.formtemplate' => 'itp',
+        'application/vnd.shana.informed.interchange' => 'iif',
+        'application/vnd.shana.informed.package' => 'ipk',
+        'application/vnd.simtech-mindmapper' => 'twd',
+        'application/vnd.smaf' => 'mmf',
+        'application/vnd.smart.teacher' => 'teacher',
+        'application/vnd.solent.sdkm+xml' => 'sdkm',
+        'application/vnd.spotfire.dxp' => 'dxp',
+        'application/vnd.spotfire.sfs' => 'sfs',
+        'application/vnd.stardivision.calc' => 'sdc',
+        'application/vnd.stardivision.draw' => 'sda',
+        'application/vnd.stardivision.impress' => 'sdd',
+        'application/vnd.stardivision.math' => 'smf',
+        'application/vnd.stardivision.writer' => 'sdw',
+        'application/vnd.stardivision.writer-global' => 'sgl',
+        'application/vnd.stepmania.package' => 'smzip',
+        'application/vnd.stepmania.stepchart' => 'sm',
+        'application/vnd.sun.xml.calc' => 'sxc',
+        'application/vnd.sun.xml.calc.template' => 'stc',
+        'application/vnd.sun.xml.draw' => 'sxd',
+        'application/vnd.sun.xml.draw.template' => 'std',
+        'application/vnd.sun.xml.impress' => 'sxi',
+        'application/vnd.sun.xml.impress.template' => 'sti',
+        'application/vnd.sun.xml.math' => 'sxm',
+        'application/vnd.sun.xml.writer' => 'sxw',
+        'application/vnd.sun.xml.writer.global' => 'sxg',
+        'application/vnd.sun.xml.writer.template' => 'stw',
+        'application/vnd.sus-calendar' => 'sus',
+        'application/vnd.svd' => 'svd',
+        'application/vnd.symbian.install' => 'sis',
+        'application/vnd.syncml+xml' => 'xsm',
+        'application/vnd.syncml.dm+wbxml' => 'bdm',
+        'application/vnd.syncml.dm+xml' => 'xdm',
+        'application/vnd.tao.intent-module-archive' => 'tao',
+        'application/vnd.tcpdump.pcap' => 'pcap',
+        'application/vnd.tmobile-livetv' => 'tmo',
+        'application/vnd.trid.tpt' => 'tpt',
+        'application/vnd.triscape.mxs' => 'mxs',
+        'application/vnd.trueapp' => 'tra',
+        'application/vnd.ufdl' => 'ufd',
+        'application/vnd.uiq.theme' => 'utz',
+        'application/vnd.umajin' => 'umj',
+        'application/vnd.unity' => 'unityweb',
+        'application/vnd.uoml+xml' => 'uoml',
+        'application/vnd.vcx' => 'vcx',
+        'application/vnd.visio' => 'vsd',
+        'application/vnd.visionary' => 'vis',
+        'application/vnd.vsf' => 'vsf',
+        'application/vnd.wap.wbxml' => 'wbxml',
+        'application/vnd.wap.wmlc' => 'wmlc',
+        'application/vnd.wap.wmlscriptc' => 'wmlsc',
+        'application/vnd.webturbo' => 'wtb',
+        'application/vnd.wolfram.player' => 'nbp',
+        'application/vnd.wordperfect' => 'wpd',
+        'application/vnd.wqd' => 'wqd',
+        'application/vnd.wt.stf' => 'stf',
+        'application/vnd.xara' => 'xar',
+        'application/vnd.xfdl' => 'xfdl',
+        'application/vnd.yamaha.hv-dic' => 'hvd',
+        'application/vnd.yamaha.hv-script' => 'hvs',
+        'application/vnd.yamaha.hv-voice' => 'hvp',
+        'application/vnd.yamaha.openscoreformat' => 'osf',
+        'application/vnd.yamaha.openscoreformat.osfpvg+xml' => 'osfpvg',
+        'application/vnd.yamaha.smaf-audio' => 'saf',
+        'application/vnd.yamaha.smaf-phrase' => 'spf',
+        'application/vnd.yellowriver-custom-menu' => 'cmp',
+        'application/vnd.zul' => 'zir',
+        'application/vnd.zzazz.deck+xml' => 'zaz',
+        'application/voicexml+xml' => 'vxml',
+        'application/widget' => 'wgt',
+        'application/winhlp' => 'hlp',
+        'application/wsdl+xml' => 'wsdl',
+        'application/wspolicy+xml' => 'wspolicy',
+        'application/x-7z-compressed' => '7z',
+        'application/x-abiword' => 'abw',
+        'application/x-ace-compressed' => 'ace',
+        'application/x-apple-diskimage' => 'dmg',
+        'application/x-authorware-bin' => 'aab',
+        'application/x-authorware-map' => 'aam',
+        'application/x-authorware-seg' => 'aas',
+        'application/x-bcpio' => 'bcpio',
+        'application/x-bittorrent' => 'torrent',
+        'application/x-blorb' => 'blb',
+        'application/x-bzip' => 'bz',
+        'application/x-bzip2' => 'bz2',
+        'application/x-cbr' => 'cbr',
+        'application/x-cdlink' => 'vcd',
+        'application/x-cfs-compressed' => 'cfs',
+        'application/x-chat' => 'chat',
+        'application/x-chess-pgn' => 'pgn',
+        'application/x-conference' => 'nsc',
+        'application/x-cpio' => 'cpio',
+        'application/x-csh' => 'csh',
+        'application/x-debian-package' => 'deb',
+        'application/x-dgc-compressed' => 'dgc',
+        'application/x-director' => 'dir',
+        'application/x-doom' => 'wad',
+        'application/x-dtbncx+xml' => 'ncx',
+        'application/x-dtbook+xml' => 'dtb',
+        'application/x-dtbresource+xml' => 'res',
+        'application/x-dvi' => 'dvi',
+        'application/x-envoy' => 'evy',
+        'application/x-eva' => 'eva',
+        'application/x-font-bdf' => 'bdf',
+        'application/x-font-ghostscript' => 'gsf',
+        'application/x-font-linux-psf' => 'psf',
+        'application/x-font-otf' => 'otf',
+        'application/x-font-pcf' => 'pcf',
+        'application/x-font-snf' => 'snf',
+        'application/x-font-ttf' => 'ttf',
+        'application/x-font-type1' => 'pfa',
+        'application/x-font-woff' => 'woff',
+        'application/x-freearc' => 'arc',
+        'application/x-futuresplash' => 'spl',
+        'application/x-gca-compressed' => 'gca',
+        'application/x-glulx' => 'ulx',
+        'application/x-gnumeric' => 'gnumeric',
+        'application/x-gramps-xml' => 'gramps',
+        'application/x-gtar' => 'gtar',
+        'application/x-hdf' => 'hdf',
+        'application/x-install-instructions' => 'install',
+        'application/x-iso9660-image' => 'iso',
+        'application/x-java-jnlp-file' => 'jnlp',
+        'application/x-latex' => 'latex',
+        'application/x-lzh-compressed' => 'lzh',
+        'application/x-mie' => 'mie',
+        'application/x-mobipocket-ebook' => 'prc',
+        'application/x-ms-application' => 'application',
+        'application/x-ms-shortcut' => 'lnk',
+        'application/x-ms-wmd' => 'wmd',
+        'application/x-ms-wmz' => 'wmz',
+        'application/x-ms-xbap' => 'xbap',
+        'application/x-msaccess' => 'mdb',
+        'application/x-msbinder' => 'obd',
+        'application/x-mscardfile' => 'crd',
+        'application/x-msclip' => 'clp',
+        'application/x-msdownload' => 'exe',
+        'application/x-msmediaview' => 'mvb',
+        'application/x-msmetafile' => 'wmf',
+        'application/x-msmoney' => 'mny',
+        'application/x-mspublisher' => 'pub',
+        'application/x-msschedule' => 'scd',
+        'application/x-msterminal' => 'trm',
+        'application/x-mswrite' => 'wri',
+        'application/x-netcdf' => 'nc',
+        'application/x-nzb' => 'nzb',
+        'application/x-pkcs12' => 'p12',
+        'application/x-pkcs7-certificates' => 'p7b',
+        'application/x-pkcs7-certreqresp' => 'p7r',
+        'application/x-rar-compressed' => 'rar',
+        'application/x-rar' => 'rar',
+        'application/x-research-info-systems' => 'ris',
+        'application/x-sh' => 'sh',
+        'application/x-shar' => 'shar',
+        'application/x-shockwave-flash' => 'swf',
+        'application/x-silverlight-app' => 'xap',
+        'application/x-sql' => 'sql',
+        'application/x-stuffit' => 'sit',
+        'application/x-stuffitx' => 'sitx',
+        'application/x-subrip' => 'srt',
+        'application/x-sv4cpio' => 'sv4cpio',
+        'application/x-sv4crc' => 'sv4crc',
+        'application/x-t3vm-image' => 't3',
+        'application/x-tads' => 'gam',
+        'application/x-tar' => 'tar',
+        'application/x-tcl' => 'tcl',
+        'application/x-tex' => 'tex',
+        'application/x-tex-tfm' => 'tfm',
+        'application/x-texinfo' => 'texinfo',
+        'application/x-tgif' => 'obj',
+        'application/x-ustar' => 'ustar',
+        'application/x-wais-source' => 'src',
+        'application/x-x509-ca-cert' => 'der',
+        'application/x-xfig' => 'fig',
+        'application/x-xliff+xml' => 'xlf',
+        'application/x-xpinstall' => 'xpi',
+        'application/x-xz' => 'xz',
+        'application/x-zip-compressed' => 'zip',
+        'application/x-zmachine' => 'z1',
+        'application/xaml+xml' => 'xaml',
+        'application/xcap-diff+xml' => 'xdf',
+        'application/xenc+xml' => 'xenc',
+        'application/xhtml+xml' => 'xhtml',
+        'application/xml' => 'xml',
+        'application/xml-dtd' => 'dtd',
+        'application/xop+xml' => 'xop',
+        'application/xproc+xml' => 'xpl',
+        'application/xslt+xml' => 'xslt',
+        'application/xspf+xml' => 'xspf',
+        'application/xv+xml' => 'mxml',
+        'application/yang' => 'yang',
+        'application/yin+xml' => 'yin',
+        'application/zip' => 'zip',
+        'audio/adpcm' => 'adp',
+        'audio/basic' => 'au',
+        'audio/midi' => 'mid',
+        'audio/mp4' => 'm4a',
+        'audio/mpeg' => 'mpga',
+        'audio/ogg' => 'oga',
+        'audio/s3m' => 's3m',
+        'audio/silk' => 'sil',
+        'audio/vnd.dece.audio' => 'uva',
+        'audio/vnd.digital-winds' => 'eol',
+        'audio/vnd.dra' => 'dra',
+        'audio/vnd.dts' => 'dts',
+        'audio/vnd.dts.hd' => 'dtshd',
+        'audio/vnd.lucent.voice' => 'lvp',
+        'audio/vnd.ms-playready.media.pya' => 'pya',
+        'audio/vnd.nuera.ecelp4800' => 'ecelp4800',
+        'audio/vnd.nuera.ecelp7470' => 'ecelp7470',
+        'audio/vnd.nuera.ecelp9600' => 'ecelp9600',
+        'audio/vnd.rip' => 'rip',
+        'audio/webm' => 'weba',
+        'audio/x-aac' => 'aac',
+        'audio/x-aiff' => 'aif',
+        'audio/x-caf' => 'caf',
+        'audio/x-flac' => 'flac',
+        'audio/x-matroska' => 'mka',
+        'audio/x-mpegurl' => 'm3u',
+        'audio/x-ms-wax' => 'wax',
+        'audio/x-ms-wma' => 'wma',
+        'audio/x-pn-realaudio' => 'ram',
+        'audio/x-pn-realaudio-plugin' => 'rmp',
+        'audio/x-wav' => 'wav',
+        'audio/xm' => 'xm',
+        'chemical/x-cdx' => 'cdx',
+        'chemical/x-cif' => 'cif',
+        'chemical/x-cmdf' => 'cmdf',
+        'chemical/x-cml' => 'cml',
+        'chemical/x-csml' => 'csml',
+        'chemical/x-xyz' => 'xyz',
+        'font/collection' => 'ttc',
+        'font/otf' => 'otf',
+        'font/ttf' => 'ttf',
+        'font/woff' => 'woff',
+        'font/woff2' => 'woff2',
+        'image/bmp' => 'bmp',
+        'image/x-ms-bmp' => 'bmp',
+        'image/cgm' => 'cgm',
+        'image/g3fax' => 'g3',
+        'image/gif' => 'gif',
+        'image/ief' => 'ief',
+        'image/jpeg' => 'jpeg',
+        'image/pjpeg' => 'jpeg',
+        'image/ktx' => 'ktx',
+        'image/png' => 'png',
+        'image/prs.btif' => 'btif',
+        'image/sgi' => 'sgi',
+        'image/svg+xml' => 'svg',
+        'image/tiff' => 'tiff',
+        'image/vnd.adobe.photoshop' => 'psd',
+        'image/vnd.dece.graphic' => 'uvi',
+        'image/vnd.djvu' => 'djvu',
+        'image/vnd.dvb.subtitle' => 'sub',
+        'image/vnd.dwg' => 'dwg',
+        'image/vnd.dxf' => 'dxf',
+        'image/vnd.fastbidsheet' => 'fbs',
+        'image/vnd.fpx' => 'fpx',
+        'image/vnd.fst' => 'fst',
+        'image/vnd.fujixerox.edmics-mmr' => 'mmr',
+        'image/vnd.fujixerox.edmics-rlc' => 'rlc',
+        'image/vnd.ms-modi' => 'mdi',
+        'image/vnd.ms-photo' => 'wdp',
+        'image/vnd.net-fpx' => 'npx',
+        'image/vnd.wap.wbmp' => 'wbmp',
+        'image/vnd.xiff' => 'xif',
+        'image/webp' => 'webp',
+        'image/x-3ds' => '3ds',
+        'image/x-cmu-raster' => 'ras',
+        'image/x-cmx' => 'cmx',
+        'image/x-freehand' => 'fh',
+        'image/x-icon' => 'ico',
+        'image/x-mrsid-image' => 'sid',
+        'image/x-pcx' => 'pcx',
+        'image/x-pict' => 'pic',
+        'image/x-portable-anymap' => 'pnm',
+        'image/x-portable-bitmap' => 'pbm',
+        'image/x-portable-graymap' => 'pgm',
+        'image/x-portable-pixmap' => 'ppm',
+        'image/x-rgb' => 'rgb',
+        'image/x-tga' => 'tga',
+        'image/x-xbitmap' => 'xbm',
+        'image/x-xpixmap' => 'xpm',
+        'image/x-xwindowdump' => 'xwd',
+        'message/rfc822' => 'eml',
+        'model/iges' => 'igs',
+        'model/mesh' => 'msh',
+        'model/vnd.collada+xml' => 'dae',
+        'model/vnd.dwf' => 'dwf',
+        'model/vnd.gdl' => 'gdl',
+        'model/vnd.gtw' => 'gtw',
+        'model/vnd.mts' => 'mts',
+        'model/vnd.vtu' => 'vtu',
+        'model/vrml' => 'wrl',
+        'model/x3d+binary' => 'x3db',
+        'model/x3d+vrml' => 'x3dv',
+        'model/x3d+xml' => 'x3d',
+        'text/cache-manifest' => 'appcache',
+        'text/calendar' => 'ics',
+        'text/css' => 'css',
+        'text/csv' => 'csv',
+        'text/html' => 'html',
+        'text/n3' => 'n3',
+        'text/plain' => 'txt',
+        'text/prs.lines.tag' => 'dsc',
+        'text/richtext' => 'rtx',
+        'text/rtf' => 'rtf',
+        'text/sgml' => 'sgml',
+        'text/tab-separated-values' => 'tsv',
+        'text/troff' => 't',
+        'text/turtle' => 'ttl',
+        'text/uri-list' => 'uri',
+        'text/vcard' => 'vcard',
+        'text/vnd.curl' => 'curl',
+        'text/vnd.curl.dcurl' => 'dcurl',
+        'text/vnd.curl.mcurl' => 'mcurl',
+        'text/vnd.curl.scurl' => 'scurl',
+        'text/vnd.dvb.subtitle' => 'sub',
+        'text/vnd.fly' => 'fly',
+        'text/vnd.fmi.flexstor' => 'flx',
+        'text/vnd.graphviz' => 'gv',
+        'text/vnd.in3d.3dml' => '3dml',
+        'text/vnd.in3d.spot' => 'spot',
+        'text/vnd.sun.j2me.app-descriptor' => 'jad',
+        'text/vnd.wap.wml' => 'wml',
+        'text/vnd.wap.wmlscript' => 'wmls',
+        'text/vtt' => 'vtt',
+        'text/x-asm' => 's',
+        'text/x-c' => 'c',
+        'text/x-fortran' => 'f',
+        'text/x-java-source' => 'java',
+        'text/x-nfo' => 'nfo',
+        'text/x-opml' => 'opml',
+        'text/x-pascal' => 'p',
+        'text/x-setext' => 'etx',
+        'text/x-sfv' => 'sfv',
+        'text/x-uuencode' => 'uu',
+        'text/x-vcalendar' => 'vcs',
+        'text/x-vcard' => 'vcf',
+        'video/3gpp' => '3gp',
+        'video/3gpp2' => '3g2',
+        'video/h261' => 'h261',
+        'video/h263' => 'h263',
+        'video/h264' => 'h264',
+        'video/jpeg' => 'jpgv',
+        'video/jpm' => 'jpm',
+        'video/mj2' => 'mj2',
+        'video/mp4' => 'mp4',
+        'video/mpeg' => 'mpeg',
+        'video/ogg' => 'ogv',
+        'video/quicktime' => 'qt',
+        'video/vnd.dece.hd' => 'uvh',
+        'video/vnd.dece.mobile' => 'uvm',
+        'video/vnd.dece.pd' => 'uvp',
+        'video/vnd.dece.sd' => 'uvs',
+        'video/vnd.dece.video' => 'uvv',
+        'video/vnd.dvb.file' => 'dvb',
+        'video/vnd.fvt' => 'fvt',
+        'video/vnd.mpegurl' => 'mxu',
+        'video/vnd.ms-playready.media.pyv' => 'pyv',
+        'video/vnd.uvvu.mp4' => 'uvu',
+        'video/vnd.vivo' => 'viv',
+        'video/webm' => 'webm',
+        'video/x-f4v' => 'f4v',
+        'video/x-fli' => 'fli',
+        'video/x-flv' => 'flv',
+        'video/x-m4v' => 'm4v',
+        'video/x-matroska' => 'mkv',
+        'video/x-mng' => 'mng',
+        'video/x-ms-asf' => 'asf',
+        'video/x-ms-vob' => 'vob',
+        'video/x-ms-wm' => 'wm',
+        'video/x-ms-wmv' => 'wmv',
+        'video/x-ms-wmx' => 'wmx',
+        'video/x-ms-wvx' => 'wvx',
+        'video/x-msvideo' => 'avi',
+        'video/x-sgi-movie' => 'movie',
+        'video/x-smv' => 'smv',
+        'x-conference/x-cooltalk' => 'ice',
+    ];
+
+    /**
+     * {@inheritdoc}
+     */
+    public function guess($mimeType)
+    {
+        if (isset($this->defaultExtensions[$mimeType])) {
+            return $this->defaultExtensions[$mimeType];
+        }
+
+        $lcMimeType = strtolower($mimeType);
+
+        return isset($this->defaultExtensions[$lcMimeType]) ? $this->defaultExtensions[$lcMimeType] : null;
+    }
+}
diff --git a/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesser.php b/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesser.php
new file mode 100755
index 0000000..95d1ee2
--- /dev/null
+++ b/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesser.php
@@ -0,0 +1,133 @@
+<?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\HttpFoundation\File\MimeType;
+
+use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
+use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
+
+/**
+ * A singleton mime type guesser.
+ *
+ * By default, all mime type guessers provided by the framework are installed
+ * (if available on the current OS/PHP setup).
+ *
+ * You can register custom guessers by calling the register() method on the
+ * singleton instance. Custom guessers are always called before any default ones.
+ *
+ *     $guesser = MimeTypeGuesser::getInstance();
+ *     $guesser->register(new MyCustomMimeTypeGuesser());
+ *
+ * If you want to change the order of the default guessers, just re-register your
+ * preferred one as a custom one. The last registered guesser is preferred over
+ * previously registered ones.
+ *
+ * Re-registering a built-in guesser also allows you to configure it:
+ *
+ *     $guesser = MimeTypeGuesser::getInstance();
+ *     $guesser->register(new FileinfoMimeTypeGuesser('/path/to/magic/file'));
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class MimeTypeGuesser implements MimeTypeGuesserInterface
+{
+    /**
+     * The singleton instance.
+     *
+     * @var MimeTypeGuesser
+     */
+    private static $instance = null;
+
+    /**
+     * All registered MimeTypeGuesserInterface instances.
+     *
+     * @var array
+     */
+    protected $guessers = [];
+
+    /**
+     * Returns the singleton instance.
+     *
+     * @return self
+     */
+    public static function getInstance()
+    {
+        if (null === self::$instance) {
+            self::$instance = new self();
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     * Resets the singleton instance.
+     */
+    public static function reset()
+    {
+        self::$instance = null;
+    }
+
+    /**
+     * Registers all natively provided mime type guessers.
+     */
+    private function __construct()
+    {
+        $this->register(new FileBinaryMimeTypeGuesser());
+        $this->register(new FileinfoMimeTypeGuesser());
+    }
+
+    /**
+     * Registers a new mime type guesser.
+     *
+     * When guessing, this guesser is preferred over previously registered ones.
+     */
+    public function register(MimeTypeGuesserInterface $guesser)
+    {
+        array_unshift($this->guessers, $guesser);
+    }
+
+    /**
+     * Tries to guess the mime type of the given file.
+     *
+     * The file is passed to each registered mime type guesser in reverse order
+     * of their registration (last registered is queried first). Once a guesser
+     * returns a value that is not NULL, this method terminates and returns the
+     * value.
+     *
+     * @param string $path The path to the file
+     *
+     * @return string The mime type or NULL, if none could be guessed
+     *
+     * @throws \LogicException
+     * @throws FileNotFoundException
+     * @throws AccessDeniedException
+     */
+    public function guess($path)
+    {
+        if (!is_file($path)) {
+            throw new FileNotFoundException($path);
+        }
+
+        if (!is_readable($path)) {
+            throw new AccessDeniedException($path);
+        }
+
+        foreach ($this->guessers as $guesser) {
+            if (null !== $mimeType = $guesser->guess($path)) {
+                return $mimeType;
+            }
+        }
+
+        if (2 === \count($this->guessers) && !FileBinaryMimeTypeGuesser::isSupported() && !FileinfoMimeTypeGuesser::isSupported()) {
+            throw new \LogicException('Unable to guess the mime type as no guessers are available (Did you enable the php_fileinfo extension?)');
+        }
+    }
+}
diff --git a/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesserInterface.php b/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesserInterface.php
new file mode 100755
index 0000000..5ac1acb
--- /dev/null
+++ b/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesserInterface.php
@@ -0,0 +1,35 @@
+<?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\HttpFoundation\File\MimeType;
+
+use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
+use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
+
+/**
+ * Guesses the mime type of a file.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+interface MimeTypeGuesserInterface
+{
+    /**
+     * Guesses the mime type of the file with the given path.
+     *
+     * @param string $path The path to the file
+     *
+     * @return string The mime type or NULL, if none could be guessed
+     *
+     * @throws FileNotFoundException If the file does not exist
+     * @throws AccessDeniedException If the file could not be read
+     */
+    public function guess($path);
+}
diff --git a/vendor/symfony/http-foundation/File/Stream.php b/vendor/symfony/http-foundation/File/Stream.php
new file mode 100755
index 0000000..69ae74c
--- /dev/null
+++ b/vendor/symfony/http-foundation/File/Stream.php
@@ -0,0 +1,28 @@
+<?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\HttpFoundation\File;
+
+/**
+ * A PHP stream of unknown size.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class Stream extends File
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getSize()
+    {
+        return false;
+    }
+}
diff --git a/vendor/symfony/http-foundation/File/UploadedFile.php b/vendor/symfony/http-foundation/File/UploadedFile.php
new file mode 100755
index 0000000..a44c664
--- /dev/null
+++ b/vendor/symfony/http-foundation/File/UploadedFile.php
@@ -0,0 +1,268 @@
+<?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\HttpFoundation\File;
+
+use Symfony\Component\HttpFoundation\File\Exception\FileException;
+use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
+use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser;
+
+/**
+ * A file uploaded through a form.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ * @author Florian Eckerstorfer <florian@eckerstorfer.org>
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class UploadedFile extends File
+{
+    private $test = false;
+    private $originalName;
+    private $mimeType;
+    private $size;
+    private $error;
+
+    /**
+     * Accepts the information of the uploaded file as provided by the PHP global $_FILES.
+     *
+     * The file object is only created when the uploaded file is valid (i.e. when the
+     * isValid() method returns true). Otherwise the only methods that could be called
+     * on an UploadedFile instance are:
+     *
+     *   * getClientOriginalName,
+     *   * getClientMimeType,
+     *   * isValid,
+     *   * getError.
+     *
+     * Calling any other method on an non-valid instance will cause an unpredictable result.
+     *
+     * @param string      $path         The full temporary path to the file
+     * @param string      $originalName The original file name of the uploaded file
+     * @param string|null $mimeType     The type of the file as provided by PHP; null defaults to application/octet-stream
+     * @param int|null    $size         The file size provided by the uploader
+     * @param int|null    $error        The error constant of the upload (one of PHP's UPLOAD_ERR_XXX constants); null defaults to UPLOAD_ERR_OK
+     * @param bool        $test         Whether the test mode is active
+     *                                  Local files are used in test mode hence the code should not enforce HTTP uploads
+     *
+     * @throws FileException         If file_uploads is disabled
+     * @throws FileNotFoundException If the file does not exist
+     */
+    public function __construct($path, $originalName, $mimeType = null, $size = null, $error = null, $test = false)
+    {
+        $this->originalName = $this->getName($originalName);
+        $this->mimeType = $mimeType ?: 'application/octet-stream';
+        $this->size = $size;
+        $this->error = $error ?: UPLOAD_ERR_OK;
+        $this->test = (bool) $test;
+
+        parent::__construct($path, UPLOAD_ERR_OK === $this->error);
+    }
+
+    /**
+     * Returns the original file name.
+     *
+     * It is extracted from the request from which the file has been uploaded.
+     * Then it should not be considered as a safe value.
+     *
+     * @return string|null The original name
+     */
+    public function getClientOriginalName()
+    {
+        return $this->originalName;
+    }
+
+    /**
+     * Returns the original file extension.
+     *
+     * It is extracted from the original file name that was uploaded.
+     * Then it should not be considered as a safe value.
+     *
+     * @return string The extension
+     */
+    public function getClientOriginalExtension()
+    {
+        return pathinfo($this->originalName, PATHINFO_EXTENSION);
+    }
+
+    /**
+     * Returns the file mime type.
+     *
+     * The client mime type is extracted from the request from which the file
+     * was uploaded, so it should not be considered as a safe value.
+     *
+     * For a trusted mime type, use getMimeType() instead (which guesses the mime
+     * type based on the file content).
+     *
+     * @return string|null The mime type
+     *
+     * @see getMimeType()
+     */
+    public function getClientMimeType()
+    {
+        return $this->mimeType;
+    }
+
+    /**
+     * Returns the extension based on the client mime type.
+     *
+     * If the mime type is unknown, returns null.
+     *
+     * This method uses the mime type as guessed by getClientMimeType()
+     * to guess the file extension. As such, the extension returned
+     * by this method cannot be trusted.
+     *
+     * For a trusted extension, use guessExtension() instead (which guesses
+     * the extension based on the guessed mime type for the file).
+     *
+     * @return string|null The guessed extension or null if it cannot be guessed
+     *
+     * @see guessExtension()
+     * @see getClientMimeType()
+     */
+    public function guessClientExtension()
+    {
+        $type = $this->getClientMimeType();
+        $guesser = ExtensionGuesser::getInstance();
+
+        return $guesser->guess($type);
+    }
+
+    /**
+     * Returns the file size.
+     *
+     * It is extracted from the request from which the file has been uploaded.
+     * Then it should not be considered as a safe value.
+     *
+     * @return int|null The file size
+     */
+    public function getClientSize()
+    {
+        return $this->size;
+    }
+
+    /**
+     * Returns the upload error.
+     *
+     * If the upload was successful, the constant UPLOAD_ERR_OK is returned.
+     * Otherwise one of the other UPLOAD_ERR_XXX constants is returned.
+     *
+     * @return int The upload error
+     */
+    public function getError()
+    {
+        return $this->error;
+    }
+
+    /**
+     * Returns whether the file was uploaded successfully.
+     *
+     * @return bool True if the file has been uploaded with HTTP and no error occurred
+     */
+    public function isValid()
+    {
+        $isOk = UPLOAD_ERR_OK === $this->error;
+
+        return $this->test ? $isOk : $isOk && is_uploaded_file($this->getPathname());
+    }
+
+    /**
+     * Moves the file to a new location.
+     *
+     * @param string $directory The destination folder
+     * @param string $name      The new file name
+     *
+     * @return File A File object representing the new file
+     *
+     * @throws FileException if, for any reason, the file could not have been moved
+     */
+    public function move($directory, $name = null)
+    {
+        if ($this->isValid()) {
+            if ($this->test) {
+                return parent::move($directory, $name);
+            }
+
+            $target = $this->getTargetFile($directory, $name);
+
+            set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; });
+            $moved = move_uploaded_file($this->getPathname(), $target);
+            restore_error_handler();
+            if (!$moved) {
+                throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error)));
+            }
+
+            @chmod($target, 0666 & ~umask());
+
+            return $target;
+        }
+
+        throw new FileException($this->getErrorMessage());
+    }
+
+    /**
+     * Returns the maximum size of an uploaded file as configured in php.ini.
+     *
+     * @return int The maximum size of an uploaded file in bytes
+     */
+    public static function getMaxFilesize()
+    {
+        $iniMax = strtolower(ini_get('upload_max_filesize'));
+
+        if ('' === $iniMax) {
+            return PHP_INT_MAX;
+        }
+
+        $max = ltrim($iniMax, '+');
+        if (0 === strpos($max, '0x')) {
+            $max = \intval($max, 16);
+        } elseif (0 === strpos($max, '0')) {
+            $max = \intval($max, 8);
+        } else {
+            $max = (int) $max;
+        }
+
+        switch (substr($iniMax, -1)) {
+            case 't': $max *= 1024;
+            // no break
+            case 'g': $max *= 1024;
+            // no break
+            case 'm': $max *= 1024;
+            // no break
+            case 'k': $max *= 1024;
+        }
+
+        return $max;
+    }
+
+    /**
+     * Returns an informative upload error message.
+     *
+     * @return string The error message regarding the specified error code
+     */
+    public function getErrorMessage()
+    {
+        static $errors = [
+            UPLOAD_ERR_INI_SIZE => 'The file "%s" exceeds your upload_max_filesize ini directive (limit is %d KiB).',
+            UPLOAD_ERR_FORM_SIZE => 'The file "%s" exceeds the upload limit defined in your form.',
+            UPLOAD_ERR_PARTIAL => 'The file "%s" was only partially uploaded.',
+            UPLOAD_ERR_NO_FILE => 'No file was uploaded.',
+            UPLOAD_ERR_CANT_WRITE => 'The file "%s" could not be written on disk.',
+            UPLOAD_ERR_NO_TMP_DIR => 'File could not be uploaded: missing temporary directory.',
+            UPLOAD_ERR_EXTENSION => 'File upload was stopped by a PHP extension.',
+        ];
+
+        $errorCode = $this->error;
+        $maxFilesize = UPLOAD_ERR_INI_SIZE === $errorCode ? self::getMaxFilesize() / 1024 : 0;
+        $message = isset($errors[$errorCode]) ? $errors[$errorCode] : 'The file "%s" was not uploaded due to an unknown error.';
+
+        return sprintf($message, $this->getClientOriginalName(), $maxFilesize);
+    }
+}
diff --git a/vendor/symfony/http-foundation/FileBag.php b/vendor/symfony/http-foundation/FileBag.php
new file mode 100755
index 0000000..ca849b3
--- /dev/null
+++ b/vendor/symfony/http-foundation/FileBag.php
@@ -0,0 +1,144 @@
+<?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\HttpFoundation;
+
+use Symfony\Component\HttpFoundation\File\UploadedFile;
+
+/**
+ * FileBag is a container for uploaded files.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Bulat Shakirzyanov <mallluhuct@gmail.com>
+ */
+class FileBag extends ParameterBag
+{
+    private static $fileKeys = ['error', 'name', 'size', 'tmp_name', 'type'];
+
+    /**
+     * @param array $parameters An array of HTTP files
+     */
+    public function __construct(array $parameters = [])
+    {
+        $this->replace($parameters);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function replace(array $files = [])
+    {
+        $this->parameters = [];
+        $this->add($files);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function set($key, $value)
+    {
+        if (!\is_array($value) && !$value instanceof UploadedFile) {
+            throw new \InvalidArgumentException('An uploaded file must be an array or an instance of UploadedFile.');
+        }
+
+        parent::set($key, $this->convertFileInformation($value));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function add(array $files = [])
+    {
+        foreach ($files as $key => $file) {
+            $this->set($key, $file);
+        }
+    }
+
+    /**
+     * Converts uploaded files to UploadedFile instances.
+     *
+     * @param array|UploadedFile $file A (multi-dimensional) array of uploaded file information
+     *
+     * @return UploadedFile[]|UploadedFile|null A (multi-dimensional) array of UploadedFile instances
+     */
+    protected function convertFileInformation($file)
+    {
+        if ($file instanceof UploadedFile) {
+            return $file;
+        }
+
+        $file = $this->fixPhpFilesArray($file);
+        if (\is_array($file)) {
+            $keys = array_keys($file);
+            sort($keys);
+
+            if ($keys == self::$fileKeys) {
+                if (UPLOAD_ERR_NO_FILE == $file['error']) {
+                    $file = null;
+                } else {
+                    $file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['size'], $file['error']);
+                }
+            } else {
+                $file = array_map([$this, 'convertFileInformation'], $file);
+                if (array_keys($keys) === $keys) {
+                    $file = array_filter($file);
+                }
+            }
+        }
+
+        return $file;
+    }
+
+    /**
+     * Fixes a malformed PHP $_FILES array.
+     *
+     * PHP has a bug that the format of the $_FILES array differs, depending on
+     * whether the uploaded file fields had normal field names or array-like
+     * field names ("normal" vs. "parent[child]").
+     *
+     * This method fixes the array to look like the "normal" $_FILES array.
+     *
+     * It's safe to pass an already converted array, in which case this method
+     * just returns the original array unmodified.
+     *
+     * @return array
+     */
+    protected function fixPhpFilesArray($data)
+    {
+        if (!\is_array($data)) {
+            return $data;
+        }
+
+        $keys = array_keys($data);
+        sort($keys);
+
+        if (self::$fileKeys != $keys || !isset($data['name']) || !\is_array($data['name'])) {
+            return $data;
+        }
+
+        $files = $data;
+        foreach (self::$fileKeys as $k) {
+            unset($files[$k]);
+        }
+
+        foreach ($data['name'] as $key => $name) {
+            $files[$key] = $this->fixPhpFilesArray([
+                'error' => $data['error'][$key],
+                'name' => $name,
+                'type' => $data['type'][$key],
+                'tmp_name' => $data['tmp_name'][$key],
+                'size' => $data['size'][$key],
+            ]);
+        }
+
+        return $files;
+    }
+}
diff --git a/vendor/symfony/http-foundation/HeaderBag.php b/vendor/symfony/http-foundation/HeaderBag.php
new file mode 100755
index 0000000..9798173
--- /dev/null
+++ b/vendor/symfony/http-foundation/HeaderBag.php
@@ -0,0 +1,331 @@
+<?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\HttpFoundation;
+
+/**
+ * HeaderBag is a container for HTTP headers.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class HeaderBag implements \IteratorAggregate, \Countable
+{
+    protected $headers = [];
+    protected $cacheControl = [];
+
+    /**
+     * @param array $headers An array of HTTP headers
+     */
+    public function __construct(array $headers = [])
+    {
+        foreach ($headers as $key => $values) {
+            $this->set($key, $values);
+        }
+    }
+
+    /**
+     * Returns the headers as a string.
+     *
+     * @return string The headers
+     */
+    public function __toString()
+    {
+        if (!$headers = $this->all()) {
+            return '';
+        }
+
+        ksort($headers);
+        $max = max(array_map('strlen', array_keys($headers))) + 1;
+        $content = '';
+        foreach ($headers as $name => $values) {
+            $name = implode('-', array_map('ucfirst', explode('-', $name)));
+            foreach ($values as $value) {
+                $content .= sprintf("%-{$max}s %s\r\n", $name.':', $value);
+            }
+        }
+
+        return $content;
+    }
+
+    /**
+     * Returns the headers.
+     *
+     * @return array An array of headers
+     */
+    public function all()
+    {
+        return $this->headers;
+    }
+
+    /**
+     * Returns the parameter keys.
+     *
+     * @return array An array of parameter keys
+     */
+    public function keys()
+    {
+        return array_keys($this->all());
+    }
+
+    /**
+     * Replaces the current HTTP headers by a new set.
+     *
+     * @param array $headers An array of HTTP headers
+     */
+    public function replace(array $headers = [])
+    {
+        $this->headers = [];
+        $this->add($headers);
+    }
+
+    /**
+     * Adds new headers the current HTTP headers set.
+     *
+     * @param array $headers An array of HTTP headers
+     */
+    public function add(array $headers)
+    {
+        foreach ($headers as $key => $values) {
+            $this->set($key, $values);
+        }
+    }
+
+    /**
+     * Returns a header value by name.
+     *
+     * @param string      $key     The header name
+     * @param string|null $default The default value
+     * @param bool        $first   Whether to return the first value or all header values
+     *
+     * @return string|string[]|null The first header value or default value if $first is true, an array of values otherwise
+     */
+    public function get($key, $default = null, $first = true)
+    {
+        $key = str_replace('_', '-', strtolower($key));
+        $headers = $this->all();
+
+        if (!\array_key_exists($key, $headers)) {
+            if (null === $default) {
+                return $first ? null : [];
+            }
+
+            return $first ? $default : [$default];
+        }
+
+        if ($first) {
+            return \count($headers[$key]) ? $headers[$key][0] : $default;
+        }
+
+        return $headers[$key];
+    }
+
+    /**
+     * Sets a header by name.
+     *
+     * @param string          $key     The key
+     * @param string|string[] $values  The value or an array of values
+     * @param bool            $replace Whether to replace the actual value or not (true by default)
+     */
+    public function set($key, $values, $replace = true)
+    {
+        $key = str_replace('_', '-', strtolower($key));
+
+        if (\is_array($values)) {
+            $values = array_values($values);
+
+            if (true === $replace || !isset($this->headers[$key])) {
+                $this->headers[$key] = $values;
+            } else {
+                $this->headers[$key] = array_merge($this->headers[$key], $values);
+            }
+        } else {
+            if (true === $replace || !isset($this->headers[$key])) {
+                $this->headers[$key] = [$values];
+            } else {
+                $this->headers[$key][] = $values;
+            }
+        }
+
+        if ('cache-control' === $key) {
+            $this->cacheControl = $this->parseCacheControl(implode(', ', $this->headers[$key]));
+        }
+    }
+
+    /**
+     * Returns true if the HTTP header is defined.
+     *
+     * @param string $key The HTTP header
+     *
+     * @return bool true if the parameter exists, false otherwise
+     */
+    public function has($key)
+    {
+        return \array_key_exists(str_replace('_', '-', strtolower($key)), $this->all());
+    }
+
+    /**
+     * Returns true if the given HTTP header contains the given value.
+     *
+     * @param string $key   The HTTP header name
+     * @param string $value The HTTP value
+     *
+     * @return bool true if the value is contained in the header, false otherwise
+     */
+    public function contains($key, $value)
+    {
+        return \in_array($value, $this->get($key, null, false));
+    }
+
+    /**
+     * Removes a header.
+     *
+     * @param string $key The HTTP header name
+     */
+    public function remove($key)
+    {
+        $key = str_replace('_', '-', strtolower($key));
+
+        unset($this->headers[$key]);
+
+        if ('cache-control' === $key) {
+            $this->cacheControl = [];
+        }
+    }
+
+    /**
+     * Returns the HTTP header value converted to a date.
+     *
+     * @param string    $key     The parameter key
+     * @param \DateTime $default The default value
+     *
+     * @return \DateTime|null The parsed DateTime or the default value if the header does not exist
+     *
+     * @throws \RuntimeException When the HTTP header is not parseable
+     */
+    public function getDate($key, \DateTime $default = null)
+    {
+        if (null === $value = $this->get($key)) {
+            return $default;
+        }
+
+        if (false === $date = \DateTime::createFromFormat(DATE_RFC2822, $value)) {
+            throw new \RuntimeException(sprintf('The %s HTTP header is not parseable (%s).', $key, $value));
+        }
+
+        return $date;
+    }
+
+    /**
+     * Adds a custom Cache-Control directive.
+     *
+     * @param string $key   The Cache-Control directive name
+     * @param mixed  $value The Cache-Control directive value
+     */
+    public function addCacheControlDirective($key, $value = true)
+    {
+        $this->cacheControl[$key] = $value;
+
+        $this->set('Cache-Control', $this->getCacheControlHeader());
+    }
+
+    /**
+     * Returns true if the Cache-Control directive is defined.
+     *
+     * @param string $key The Cache-Control directive
+     *
+     * @return bool true if the directive exists, false otherwise
+     */
+    public function hasCacheControlDirective($key)
+    {
+        return \array_key_exists($key, $this->cacheControl);
+    }
+
+    /**
+     * Returns a Cache-Control directive value by name.
+     *
+     * @param string $key The directive name
+     *
+     * @return mixed|null The directive value if defined, null otherwise
+     */
+    public function getCacheControlDirective($key)
+    {
+        return \array_key_exists($key, $this->cacheControl) ? $this->cacheControl[$key] : null;
+    }
+
+    /**
+     * Removes a Cache-Control directive.
+     *
+     * @param string $key The Cache-Control directive
+     */
+    public function removeCacheControlDirective($key)
+    {
+        unset($this->cacheControl[$key]);
+
+        $this->set('Cache-Control', $this->getCacheControlHeader());
+    }
+
+    /**
+     * Returns an iterator for headers.
+     *
+     * @return \ArrayIterator An \ArrayIterator instance
+     */
+    public function getIterator()
+    {
+        return new \ArrayIterator($this->headers);
+    }
+
+    /**
+     * Returns the number of headers.
+     *
+     * @return int The number of headers
+     */
+    public function count()
+    {
+        return \count($this->headers);
+    }
+
+    protected function getCacheControlHeader()
+    {
+        $parts = [];
+        ksort($this->cacheControl);
+        foreach ($this->cacheControl as $key => $value) {
+            if (true === $value) {
+                $parts[] = $key;
+            } else {
+                if (preg_match('#[^a-zA-Z0-9._-]#', $value)) {
+                    $value = '"'.$value.'"';
+                }
+
+                $parts[] = "$key=$value";
+            }
+        }
+
+        return implode(', ', $parts);
+    }
+
+    /**
+     * Parses a Cache-Control HTTP header.
+     *
+     * @param string $header The value of the Cache-Control HTTP header
+     *
+     * @return array An array representing the attribute values
+     */
+    protected function parseCacheControl($header)
+    {
+        $cacheControl = [];
+        preg_match_all('#([a-zA-Z][a-zA-Z_-]*)\s*(?:=(?:"([^"]*)"|([^ \t",;]*)))?#', $header, $matches, PREG_SET_ORDER);
+        foreach ($matches as $match) {
+            $cacheControl[strtolower($match[1])] = isset($match[3]) ? $match[3] : (isset($match[2]) ? $match[2] : true);
+        }
+
+        return $cacheControl;
+    }
+}
diff --git a/vendor/symfony/http-foundation/IpUtils.php b/vendor/symfony/http-foundation/IpUtils.php
new file mode 100755
index 0000000..67d13e5
--- /dev/null
+++ b/vendor/symfony/http-foundation/IpUtils.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\HttpFoundation;
+
+/**
+ * Http utility functions.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class IpUtils
+{
+    private static $checkedIps = [];
+
+    /**
+     * This class should not be instantiated.
+     */
+    private function __construct()
+    {
+    }
+
+    /**
+     * Checks if an IPv4 or IPv6 address is contained in the list of given IPs or subnets.
+     *
+     * @param string       $requestIp IP to check
+     * @param string|array $ips       List of IPs or subnets (can be a string if only a single one)
+     *
+     * @return bool Whether the IP is valid
+     */
+    public static function checkIp($requestIp, $ips)
+    {
+        if (!\is_array($ips)) {
+            $ips = [$ips];
+        }
+
+        $method = substr_count($requestIp, ':') > 1 ? 'checkIp6' : 'checkIp4';
+
+        foreach ($ips as $ip) {
+            if (self::$method($requestIp, $ip)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Compares two IPv4 addresses.
+     * In case a subnet is given, it checks if it contains the request IP.
+     *
+     * @param string $requestIp IPv4 address to check
+     * @param string $ip        IPv4 address or subnet in CIDR notation
+     *
+     * @return bool Whether the request IP matches the IP, or whether the request IP is within the CIDR subnet
+     */
+    public static function checkIp4($requestIp, $ip)
+    {
+        $cacheKey = $requestIp.'-'.$ip;
+        if (isset(self::$checkedIps[$cacheKey])) {
+            return self::$checkedIps[$cacheKey];
+        }
+
+        if (!filter_var($requestIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
+            return self::$checkedIps[$cacheKey] = false;
+        }
+
+        if (false !== strpos($ip, '/')) {
+            list($address, $netmask) = explode('/', $ip, 2);
+
+            if ('0' === $netmask) {
+                return self::$checkedIps[$cacheKey] = filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
+            }
+
+            if ($netmask < 0 || $netmask > 32) {
+                return self::$checkedIps[$cacheKey] = false;
+            }
+        } else {
+            $address = $ip;
+            $netmask = 32;
+        }
+
+        if (false === ip2long($address)) {
+            return self::$checkedIps[$cacheKey] = false;
+        }
+
+        return self::$checkedIps[$cacheKey] = 0 === substr_compare(sprintf('%032b', ip2long($requestIp)), sprintf('%032b', ip2long($address)), 0, $netmask);
+    }
+
+    /**
+     * Compares two IPv6 addresses.
+     * In case a subnet is given, it checks if it contains the request IP.
+     *
+     * @author David Soria Parra <dsp at php dot net>
+     *
+     * @see https://github.com/dsp/v6tools
+     *
+     * @param string $requestIp IPv6 address to check
+     * @param string $ip        IPv6 address or subnet in CIDR notation
+     *
+     * @return bool Whether the IP is valid
+     *
+     * @throws \RuntimeException When IPV6 support is not enabled
+     */
+    public static function checkIp6($requestIp, $ip)
+    {
+        $cacheKey = $requestIp.'-'.$ip;
+        if (isset(self::$checkedIps[$cacheKey])) {
+            return self::$checkedIps[$cacheKey];
+        }
+
+        if (!((\extension_loaded('sockets') && \defined('AF_INET6')) || @inet_pton('::1'))) {
+            throw new \RuntimeException('Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".');
+        }
+
+        if (false !== strpos($ip, '/')) {
+            list($address, $netmask) = explode('/', $ip, 2);
+
+            if ('0' === $netmask) {
+                return (bool) unpack('n*', @inet_pton($address));
+            }
+
+            if ($netmask < 1 || $netmask > 128) {
+                return self::$checkedIps[$cacheKey] = false;
+            }
+        } else {
+            $address = $ip;
+            $netmask = 128;
+        }
+
+        $bytesAddr = unpack('n*', @inet_pton($address));
+        $bytesTest = unpack('n*', @inet_pton($requestIp));
+
+        if (!$bytesAddr || !$bytesTest) {
+            return self::$checkedIps[$cacheKey] = false;
+        }
+
+        for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; ++$i) {
+            $left = $netmask - 16 * ($i - 1);
+            $left = ($left <= 16) ? $left : 16;
+            $mask = ~(0xffff >> $left) & 0xffff;
+            if (($bytesAddr[$i] & $mask) != ($bytesTest[$i] & $mask)) {
+                return self::$checkedIps[$cacheKey] = false;
+            }
+        }
+
+        return self::$checkedIps[$cacheKey] = true;
+    }
+}
diff --git a/vendor/symfony/http-foundation/JsonResponse.php b/vendor/symfony/http-foundation/JsonResponse.php
new file mode 100755
index 0000000..6fb32ee
--- /dev/null
+++ b/vendor/symfony/http-foundation/JsonResponse.php
@@ -0,0 +1,220 @@
+<?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\HttpFoundation;
+
+/**
+ * Response represents an HTTP response in JSON format.
+ *
+ * Note that this class does not force the returned JSON content to be an
+ * object. It is however recommended that you do return an object as it
+ * protects yourself against XSSI and JSON-JavaScript Hijacking.
+ *
+ * @see https://www.owasp.org/index.php/OWASP_AJAX_Security_Guidelines#Always_return_JSON_with_an_Object_on_the_outside
+ *
+ * @author Igor Wiedler <igor@wiedler.ch>
+ */
+class JsonResponse extends Response
+{
+    protected $data;
+    protected $callback;
+
+    // Encode <, >, ', &, and " characters in the JSON, making it also safe to be embedded into HTML.
+    // 15 === JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT
+    const DEFAULT_ENCODING_OPTIONS = 15;
+
+    protected $encodingOptions = self::DEFAULT_ENCODING_OPTIONS;
+
+    /**
+     * @param mixed $data    The response data
+     * @param int   $status  The response status code
+     * @param array $headers An array of response headers
+     * @param bool  $json    If the data is already a JSON string
+     */
+    public function __construct($data = null, $status = 200, $headers = [], $json = false)
+    {
+        parent::__construct('', $status, $headers);
+
+        if (null === $data) {
+            $data = new \ArrayObject();
+        }
+
+        $json ? $this->setJson($data) : $this->setData($data);
+    }
+
+    /**
+     * Factory method for chainability.
+     *
+     * Example:
+     *
+     *     return JsonResponse::create($data, 200)
+     *         ->setSharedMaxAge(300);
+     *
+     * @param mixed $data    The json response data
+     * @param int   $status  The response status code
+     * @param array $headers An array of response headers
+     *
+     * @return static
+     */
+    public static function create($data = null, $status = 200, $headers = [])
+    {
+        return new static($data, $status, $headers);
+    }
+
+    /**
+     * Make easier the creation of JsonResponse from raw json.
+     */
+    public static function fromJsonString($data = null, $status = 200, $headers = [])
+    {
+        return new static($data, $status, $headers, true);
+    }
+
+    /**
+     * Sets the JSONP callback.
+     *
+     * @param string|null $callback The JSONP callback or null to use none
+     *
+     * @return $this
+     *
+     * @throws \InvalidArgumentException When the callback name is not valid
+     */
+    public function setCallback($callback = null)
+    {
+        if (null !== $callback) {
+            // partially taken from http://www.geekality.net/2011/08/03/valid-javascript-identifier/
+            // partially taken from https://github.com/willdurand/JsonpCallbackValidator
+            //      JsonpCallbackValidator is released under the MIT License. See https://github.com/willdurand/JsonpCallbackValidator/blob/v1.1.0/LICENSE for details.
+            //      (c) William Durand <william.durand1@gmail.com>
+            $pattern = '/^[$_\p{L}][$_\p{L}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\x{200C}\x{200D}]*(?:\[(?:"(?:\\\.|[^"\\\])*"|\'(?:\\\.|[^\'\\\])*\'|\d+)\])*?$/u';
+            $reserved = [
+                'break', 'do', 'instanceof', 'typeof', 'case', 'else', 'new', 'var', 'catch', 'finally', 'return', 'void', 'continue', 'for', 'switch', 'while',
+                'debugger', 'function', 'this', 'with', 'default', 'if', 'throw', 'delete', 'in', 'try', 'class', 'enum', 'extends', 'super',  'const', 'export',
+                'import', 'implements', 'let', 'private', 'public', 'yield', 'interface', 'package', 'protected', 'static', 'null', 'true', 'false',
+            ];
+            $parts = explode('.', $callback);
+            foreach ($parts as $part) {
+                if (!preg_match($pattern, $part) || \in_array($part, $reserved, true)) {
+                    throw new \InvalidArgumentException('The callback name is not valid.');
+                }
+            }
+        }
+
+        $this->callback = $callback;
+
+        return $this->update();
+    }
+
+    /**
+     * Sets a raw string containing a JSON document to be sent.
+     *
+     * @param string $json
+     *
+     * @return $this
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function setJson($json)
+    {
+        $this->data = $json;
+
+        return $this->update();
+    }
+
+    /**
+     * Sets the data to be sent as JSON.
+     *
+     * @param mixed $data
+     *
+     * @return $this
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function setData($data = [])
+    {
+        if (\defined('HHVM_VERSION')) {
+            // HHVM does not trigger any warnings and let exceptions
+            // thrown from a JsonSerializable object pass through.
+            // If only PHP did the same...
+            $data = json_encode($data, $this->encodingOptions);
+        } else {
+            if (!interface_exists('JsonSerializable', false)) {
+                set_error_handler(function () { return false; });
+                try {
+                    $data = @json_encode($data, $this->encodingOptions);
+                } finally {
+                    restore_error_handler();
+                }
+            } else {
+                try {
+                    $data = json_encode($data, $this->encodingOptions);
+                } catch (\Exception $e) {
+                    if ('Exception' === \get_class($e) && 0 === strpos($e->getMessage(), 'Failed calling ')) {
+                        throw $e->getPrevious() ?: $e;
+                    }
+                    throw $e;
+                }
+            }
+        }
+
+        if (JSON_ERROR_NONE !== json_last_error()) {
+            throw new \InvalidArgumentException(json_last_error_msg());
+        }
+
+        return $this->setJson($data);
+    }
+
+    /**
+     * Returns options used while encoding data to JSON.
+     *
+     * @return int
+     */
+    public function getEncodingOptions()
+    {
+        return $this->encodingOptions;
+    }
+
+    /**
+     * Sets options used while encoding data to JSON.
+     *
+     * @param int $encodingOptions
+     *
+     * @return $this
+     */
+    public function setEncodingOptions($encodingOptions)
+    {
+        $this->encodingOptions = (int) $encodingOptions;
+
+        return $this->setData(json_decode($this->data));
+    }
+
+    /**
+     * Updates the content and headers according to the JSON data and callback.
+     *
+     * @return $this
+     */
+    protected function update()
+    {
+        if (null !== $this->callback) {
+            // Not using application/javascript for compatibility reasons with older browsers.
+            $this->headers->set('Content-Type', 'text/javascript');
+
+            return $this->setContent(sprintf('/**/%s(%s);', $this->callback, $this->data));
+        }
+
+        // Only set the header when there is none or when it equals 'text/javascript' (from a previous update with callback)
+        // in order to not overwrite a custom definition.
+        if (!$this->headers->has('Content-Type') || 'text/javascript' === $this->headers->get('Content-Type')) {
+            $this->headers->set('Content-Type', 'application/json');
+        }
+
+        return $this->setContent($this->data);
+    }
+}
diff --git a/vendor/symfony/http-foundation/LICENSE b/vendor/symfony/http-foundation/LICENSE
new file mode 100755
index 0000000..a677f43
--- /dev/null
+++ b/vendor/symfony/http-foundation/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2004-2019 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.
diff --git a/vendor/symfony/http-foundation/ParameterBag.php b/vendor/symfony/http-foundation/ParameterBag.php
new file mode 100755
index 0000000..f05e4a2
--- /dev/null
+++ b/vendor/symfony/http-foundation/ParameterBag.php
@@ -0,0 +1,234 @@
+<?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\HttpFoundation;
+
+/**
+ * ParameterBag is a container for key/value pairs.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class ParameterBag implements \IteratorAggregate, \Countable
+{
+    /**
+     * Parameter storage.
+     */
+    protected $parameters;
+
+    /**
+     * @param array $parameters An array of parameters
+     */
+    public function __construct(array $parameters = [])
+    {
+        $this->parameters = $parameters;
+    }
+
+    /**
+     * Returns the parameters.
+     *
+     * @return array An array of parameters
+     */
+    public function all()
+    {
+        return $this->parameters;
+    }
+
+    /**
+     * Returns the parameter keys.
+     *
+     * @return array An array of parameter keys
+     */
+    public function keys()
+    {
+        return array_keys($this->parameters);
+    }
+
+    /**
+     * Replaces the current parameters by a new set.
+     *
+     * @param array $parameters An array of parameters
+     */
+    public function replace(array $parameters = [])
+    {
+        $this->parameters = $parameters;
+    }
+
+    /**
+     * Adds parameters.
+     *
+     * @param array $parameters An array of parameters
+     */
+    public function add(array $parameters = [])
+    {
+        $this->parameters = array_replace($this->parameters, $parameters);
+    }
+
+    /**
+     * Returns a parameter by name.
+     *
+     * @param string $key     The key
+     * @param mixed  $default The default value if the parameter key does not exist
+     *
+     * @return mixed
+     */
+    public function get($key, $default = null)
+    {
+        return \array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default;
+    }
+
+    /**
+     * Sets a parameter by name.
+     *
+     * @param string $key   The key
+     * @param mixed  $value The value
+     */
+    public function set($key, $value)
+    {
+        $this->parameters[$key] = $value;
+    }
+
+    /**
+     * Returns true if the parameter is defined.
+     *
+     * @param string $key The key
+     *
+     * @return bool true if the parameter exists, false otherwise
+     */
+    public function has($key)
+    {
+        return \array_key_exists($key, $this->parameters);
+    }
+
+    /**
+     * Removes a parameter.
+     *
+     * @param string $key The key
+     */
+    public function remove($key)
+    {
+        unset($this->parameters[$key]);
+    }
+
+    /**
+     * Returns the alphabetic characters of the parameter value.
+     *
+     * @param string $key     The parameter key
+     * @param string $default The default value if the parameter key does not exist
+     *
+     * @return string The filtered value
+     */
+    public function getAlpha($key, $default = '')
+    {
+        return preg_replace('/[^[:alpha:]]/', '', $this->get($key, $default));
+    }
+
+    /**
+     * Returns the alphabetic characters and digits of the parameter value.
+     *
+     * @param string $key     The parameter key
+     * @param string $default The default value if the parameter key does not exist
+     *
+     * @return string The filtered value
+     */
+    public function getAlnum($key, $default = '')
+    {
+        return preg_replace('/[^[:alnum:]]/', '', $this->get($key, $default));
+    }
+
+    /**
+     * Returns the digits of the parameter value.
+     *
+     * @param string $key     The parameter key
+     * @param string $default The default value if the parameter key does not exist
+     *
+     * @return string The filtered value
+     */
+    public function getDigits($key, $default = '')
+    {
+        // we need to remove - and + because they're allowed in the filter
+        return str_replace(['-', '+'], '', $this->filter($key, $default, FILTER_SANITIZE_NUMBER_INT));
+    }
+
+    /**
+     * Returns the parameter value converted to integer.
+     *
+     * @param string $key     The parameter key
+     * @param int    $default The default value if the parameter key does not exist
+     *
+     * @return int The filtered value
+     */
+    public function getInt($key, $default = 0)
+    {
+        return (int) $this->get($key, $default);
+    }
+
+    /**
+     * Returns the parameter value converted to boolean.
+     *
+     * @param string $key     The parameter key
+     * @param bool   $default The default value if the parameter key does not exist
+     *
+     * @return bool The filtered value
+     */
+    public function getBoolean($key, $default = false)
+    {
+        return $this->filter($key, $default, FILTER_VALIDATE_BOOLEAN);
+    }
+
+    /**
+     * Filter key.
+     *
+     * @param string $key     Key
+     * @param mixed  $default Default = null
+     * @param int    $filter  FILTER_* constant
+     * @param mixed  $options Filter options
+     *
+     * @see http://php.net/manual/en/function.filter-var.php
+     *
+     * @return mixed
+     */
+    public function filter($key, $default = null, $filter = FILTER_DEFAULT, $options = [])
+    {
+        $value = $this->get($key, $default);
+
+        // Always turn $options into an array - this allows filter_var option shortcuts.
+        if (!\is_array($options) && $options) {
+            $options = ['flags' => $options];
+        }
+
+        // Add a convenience check for arrays.
+        if (\is_array($value) && !isset($options['flags'])) {
+            $options['flags'] = FILTER_REQUIRE_ARRAY;
+        }
+
+        return filter_var($value, $filter, $options);
+    }
+
+    /**
+     * Returns an iterator for parameters.
+     *
+     * @return \ArrayIterator An \ArrayIterator instance
+     */
+    public function getIterator()
+    {
+        return new \ArrayIterator($this->parameters);
+    }
+
+    /**
+     * Returns the number of parameters.
+     *
+     * @return int The number of parameters
+     */
+    public function count()
+    {
+        return \count($this->parameters);
+    }
+}
diff --git a/vendor/symfony/http-foundation/README.md b/vendor/symfony/http-foundation/README.md
new file mode 100755
index 0000000..8907f0b
--- /dev/null
+++ b/vendor/symfony/http-foundation/README.md
@@ -0,0 +1,14 @@
+HttpFoundation Component
+========================
+
+The HttpFoundation component defines an object-oriented layer for the HTTP
+specification.
+
+Resources
+---------
+
+  * [Documentation](https://symfony.com/doc/current/components/http_foundation/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)
diff --git a/vendor/symfony/http-foundation/RedirectResponse.php b/vendor/symfony/http-foundation/RedirectResponse.php
new file mode 100755
index 0000000..5e1dfe5
--- /dev/null
+++ b/vendor/symfony/http-foundation/RedirectResponse.php
@@ -0,0 +1,109 @@
+<?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\HttpFoundation;
+
+/**
+ * RedirectResponse represents an HTTP response doing a redirect.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class RedirectResponse extends Response
+{
+    protected $targetUrl;
+
+    /**
+     * Creates a redirect response so that it conforms to the rules defined for a redirect status code.
+     *
+     * @param string $url     The URL to redirect to. The URL should be a full URL, with schema etc.,
+     *                        but practically every browser redirects on paths only as well
+     * @param int    $status  The status code (302 by default)
+     * @param array  $headers The headers (Location is always set to the given URL)
+     *
+     * @throws \InvalidArgumentException
+     *
+     * @see http://tools.ietf.org/html/rfc2616#section-10.3
+     */
+    public function __construct($url, $status = 302, $headers = [])
+    {
+        parent::__construct('', $status, $headers);
+
+        $this->setTargetUrl($url);
+
+        if (!$this->isRedirect()) {
+            throw new \InvalidArgumentException(sprintf('The HTTP status code is not a redirect ("%s" given).', $status));
+        }
+
+        if (301 == $status && !\array_key_exists('cache-control', $headers)) {
+            $this->headers->remove('cache-control');
+        }
+    }
+
+    /**
+     * Factory method for chainability.
+     *
+     * @param string $url     The url to redirect to
+     * @param int    $status  The response status code
+     * @param array  $headers An array of response headers
+     *
+     * @return static
+     */
+    public static function create($url = '', $status = 302, $headers = [])
+    {
+        return new static($url, $status, $headers);
+    }
+
+    /**
+     * Returns the target URL.
+     *
+     * @return string target URL
+     */
+    public function getTargetUrl()
+    {
+        return $this->targetUrl;
+    }
+
+    /**
+     * Sets the redirect target of this response.
+     *
+     * @param string $url The URL to redirect to
+     *
+     * @return $this
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function setTargetUrl($url)
+    {
+        if (empty($url)) {
+            throw new \InvalidArgumentException('Cannot redirect to an empty URL.');
+        }
+
+        $this->targetUrl = $url;
+
+        $this->setContent(
+            sprintf('<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="UTF-8" />
+        <meta http-equiv="refresh" content="0;url=%1$s" />
+
+        <title>Redirecting to %1$s</title>
+    </head>
+    <body>
+        Redirecting to <a href="%1$s">%1$s</a>.
+    </body>
+</html>', htmlspecialchars($url, ENT_QUOTES, 'UTF-8')));
+
+        $this->headers->set('Location', $url);
+
+        return $this;
+    }
+}
diff --git a/vendor/symfony/http-foundation/Request.php b/vendor/symfony/http-foundation/Request.php
new file mode 100755
index 0000000..ea3f460
--- /dev/null
+++ b/vendor/symfony/http-foundation/Request.php
@@ -0,0 +1,2191 @@
+<?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\HttpFoundation;
+
+use Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException;
+use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
+use Symfony\Component\HttpFoundation\Session\SessionInterface;
+
+/**
+ * Request represents an HTTP request.
+ *
+ * The methods dealing with URL accept / return a raw path (% encoded):
+ *   * getBasePath
+ *   * getBaseUrl
+ *   * getPathInfo
+ *   * getRequestUri
+ *   * getUri
+ *   * getUriForPath
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class Request
+{
+    const HEADER_FORWARDED = 0b00001; // When using RFC 7239
+    const HEADER_X_FORWARDED_FOR = 0b00010;
+    const HEADER_X_FORWARDED_HOST = 0b00100;
+    const HEADER_X_FORWARDED_PROTO = 0b01000;
+    const HEADER_X_FORWARDED_PORT = 0b10000;
+    const HEADER_X_FORWARDED_ALL = 0b11110; // All "X-Forwarded-*" headers
+    const HEADER_X_FORWARDED_AWS_ELB = 0b11010; // AWS ELB doesn't send X-Forwarded-Host
+
+    /** @deprecated since version 3.3, to be removed in 4.0 */
+    const HEADER_CLIENT_IP = self::HEADER_X_FORWARDED_FOR;
+    /** @deprecated since version 3.3, to be removed in 4.0 */
+    const HEADER_CLIENT_HOST = self::HEADER_X_FORWARDED_HOST;
+    /** @deprecated since version 3.3, to be removed in 4.0 */
+    const HEADER_CLIENT_PROTO = self::HEADER_X_FORWARDED_PROTO;
+    /** @deprecated since version 3.3, to be removed in 4.0 */
+    const HEADER_CLIENT_PORT = self::HEADER_X_FORWARDED_PORT;
+
+    const METHOD_HEAD = 'HEAD';
+    const METHOD_GET = 'GET';
+    const METHOD_POST = 'POST';
+    const METHOD_PUT = 'PUT';
+    const METHOD_PATCH = 'PATCH';
+    const METHOD_DELETE = 'DELETE';
+    const METHOD_PURGE = 'PURGE';
+    const METHOD_OPTIONS = 'OPTIONS';
+    const METHOD_TRACE = 'TRACE';
+    const METHOD_CONNECT = 'CONNECT';
+
+    /**
+     * @var string[]
+     */
+    protected static $trustedProxies = [];
+
+    /**
+     * @var string[]
+     */
+    protected static $trustedHostPatterns = [];
+
+    /**
+     * @var string[]
+     */
+    protected static $trustedHosts = [];
+
+    /**
+     * Names for headers that can be trusted when
+     * using trusted proxies.
+     *
+     * The FORWARDED header is the standard as of rfc7239.
+     *
+     * The other headers are non-standard, but widely used
+     * by popular reverse proxies (like Apache mod_proxy or Amazon EC2).
+     *
+     * @deprecated since version 3.3, to be removed in 4.0
+     */
+    protected static $trustedHeaders = [
+        self::HEADER_FORWARDED => 'FORWARDED',
+        self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
+        self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST',
+        self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
+        self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
+    ];
+
+    protected static $httpMethodParameterOverride = false;
+
+    /**
+     * Custom parameters.
+     *
+     * @var \Symfony\Component\HttpFoundation\ParameterBag
+     */
+    public $attributes;
+
+    /**
+     * Request body parameters ($_POST).
+     *
+     * @var \Symfony\Component\HttpFoundation\ParameterBag
+     */
+    public $request;
+
+    /**
+     * Query string parameters ($_GET).
+     *
+     * @var \Symfony\Component\HttpFoundation\ParameterBag
+     */
+    public $query;
+
+    /**
+     * Server and execution environment parameters ($_SERVER).
+     *
+     * @var \Symfony\Component\HttpFoundation\ServerBag
+     */
+    public $server;
+
+    /**
+     * Uploaded files ($_FILES).
+     *
+     * @var \Symfony\Component\HttpFoundation\FileBag
+     */
+    public $files;
+
+    /**
+     * Cookies ($_COOKIE).
+     *
+     * @var \Symfony\Component\HttpFoundation\ParameterBag
+     */
+    public $cookies;
+
+    /**
+     * Headers (taken from the $_SERVER).
+     *
+     * @var \Symfony\Component\HttpFoundation\HeaderBag
+     */
+    public $headers;
+
+    /**
+     * @var string|resource|false|null
+     */
+    protected $content;
+
+    /**
+     * @var array
+     */
+    protected $languages;
+
+    /**
+     * @var array
+     */
+    protected $charsets;
+
+    /**
+     * @var array
+     */
+    protected $encodings;
+
+    /**
+     * @var array
+     */
+    protected $acceptableContentTypes;
+
+    /**
+     * @var string
+     */
+    protected $pathInfo;
+
+    /**
+     * @var string
+     */
+    protected $requestUri;
+
+    /**
+     * @var string
+     */
+    protected $baseUrl;
+
+    /**
+     * @var string
+     */
+    protected $basePath;
+
+    /**
+     * @var string
+     */
+    protected $method;
+
+    /**
+     * @var string
+     */
+    protected $format;
+
+    /**
+     * @var \Symfony\Component\HttpFoundation\Session\SessionInterface
+     */
+    protected $session;
+
+    /**
+     * @var string
+     */
+    protected $locale;
+
+    /**
+     * @var string
+     */
+    protected $defaultLocale = 'en';
+
+    /**
+     * @var array
+     */
+    protected static $formats;
+
+    protected static $requestFactory;
+
+    private $isHostValid = true;
+    private $isForwardedValid = true;
+
+    private static $trustedHeaderSet = -1;
+
+    /** @deprecated since version 3.3, to be removed in 4.0 */
+    private static $trustedHeaderNames = [
+        self::HEADER_FORWARDED => 'FORWARDED',
+        self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
+        self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST',
+        self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
+        self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
+    ];
+
+    private static $forwardedParams = [
+        self::HEADER_X_FORWARDED_FOR => 'for',
+        self::HEADER_X_FORWARDED_HOST => 'host',
+        self::HEADER_X_FORWARDED_PROTO => 'proto',
+        self::HEADER_X_FORWARDED_PORT => 'host',
+    ];
+
+    /**
+     * @param array                $query      The GET parameters
+     * @param array                $request    The POST parameters
+     * @param array                $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
+     * @param array                $cookies    The COOKIE parameters
+     * @param array                $files      The FILES parameters
+     * @param array                $server     The SERVER parameters
+     * @param string|resource|null $content    The raw body data
+     */
+    public function __construct(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null)
+    {
+        $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content);
+    }
+
+    /**
+     * Sets the parameters for this request.
+     *
+     * This method also re-initializes all properties.
+     *
+     * @param array                $query      The GET parameters
+     * @param array                $request    The POST parameters
+     * @param array                $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
+     * @param array                $cookies    The COOKIE parameters
+     * @param array                $files      The FILES parameters
+     * @param array                $server     The SERVER parameters
+     * @param string|resource|null $content    The raw body data
+     */
+    public function initialize(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null)
+    {
+        $this->request = new ParameterBag($request);
+        $this->query = new ParameterBag($query);
+        $this->attributes = new ParameterBag($attributes);
+        $this->cookies = new ParameterBag($cookies);
+        $this->files = new FileBag($files);
+        $this->server = new ServerBag($server);
+        $this->headers = new HeaderBag($this->server->getHeaders());
+
+        $this->content = $content;
+        $this->languages = null;
+        $this->charsets = null;
+        $this->encodings = null;
+        $this->acceptableContentTypes = null;
+        $this->pathInfo = null;
+        $this->requestUri = null;
+        $this->baseUrl = null;
+        $this->basePath = null;
+        $this->method = null;
+        $this->format = null;
+    }
+
+    /**
+     * Creates a new request with values from PHP's super globals.
+     *
+     * @return static
+     */
+    public static function createFromGlobals()
+    {
+        // With the php's bug #66606, the php's built-in web server
+        // stores the Content-Type and Content-Length header values in
+        // HTTP_CONTENT_TYPE and HTTP_CONTENT_LENGTH fields.
+        $server = $_SERVER;
+        if ('cli-server' === \PHP_SAPI) {
+            if (\array_key_exists('HTTP_CONTENT_LENGTH', $_SERVER)) {
+                $server['CONTENT_LENGTH'] = $_SERVER['HTTP_CONTENT_LENGTH'];
+            }
+            if (\array_key_exists('HTTP_CONTENT_TYPE', $_SERVER)) {
+                $server['CONTENT_TYPE'] = $_SERVER['HTTP_CONTENT_TYPE'];
+            }
+        }
+
+        $request = self::createRequestFromFactory($_GET, $_POST, [], $_COOKIE, $_FILES, $server);
+
+        if (0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded')
+            && \in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), ['PUT', 'DELETE', 'PATCH'])
+        ) {
+            parse_str($request->getContent(), $data);
+            $request->request = new ParameterBag($data);
+        }
+
+        return $request;
+    }
+
+    /**
+     * Creates a Request based on a given URI and configuration.
+     *
+     * The information contained in the URI always take precedence
+     * over the other information (server and parameters).
+     *
+     * @param string               $uri        The URI
+     * @param string               $method     The HTTP method
+     * @param array                $parameters The query (GET) or request (POST) parameters
+     * @param array                $cookies    The request cookies ($_COOKIE)
+     * @param array                $files      The request files ($_FILES)
+     * @param array                $server     The server parameters ($_SERVER)
+     * @param string|resource|null $content    The raw body data
+     *
+     * @return static
+     */
+    public static function create($uri, $method = 'GET', $parameters = [], $cookies = [], $files = [], $server = [], $content = null)
+    {
+        $server = array_replace([
+            'SERVER_NAME' => 'localhost',
+            'SERVER_PORT' => 80,
+            'HTTP_HOST' => 'localhost',
+            'HTTP_USER_AGENT' => 'Symfony/3.X',
+            'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
+            'HTTP_ACCEPT_LANGUAGE' => 'en-us,en;q=0.5',
+            'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
+            'REMOTE_ADDR' => '127.0.0.1',
+            'SCRIPT_NAME' => '',
+            'SCRIPT_FILENAME' => '',
+            'SERVER_PROTOCOL' => 'HTTP/1.1',
+            'REQUEST_TIME' => time(),
+        ], $server);
+
+        $server['PATH_INFO'] = '';
+        $server['REQUEST_METHOD'] = strtoupper($method);
+
+        $components = parse_url($uri);
+        if (isset($components['host'])) {
+            $server['SERVER_NAME'] = $components['host'];
+            $server['HTTP_HOST'] = $components['host'];
+        }
+
+        if (isset($components['scheme'])) {
+            if ('https' === $components['scheme']) {
+                $server['HTTPS'] = 'on';
+                $server['SERVER_PORT'] = 443;
+            } else {
+                unset($server['HTTPS']);
+                $server['SERVER_PORT'] = 80;
+            }
+        }
+
+        if (isset($components['port'])) {
+            $server['SERVER_PORT'] = $components['port'];
+            $server['HTTP_HOST'] .= ':'.$components['port'];
+        }
+
+        if (isset($components['user'])) {
+            $server['PHP_AUTH_USER'] = $components['user'];
+        }
+
+        if (isset($components['pass'])) {
+            $server['PHP_AUTH_PW'] = $components['pass'];
+        }
+
+        if (!isset($components['path'])) {
+            $components['path'] = '/';
+        }
+
+        switch (strtoupper($method)) {
+            case 'POST':
+            case 'PUT':
+            case 'DELETE':
+                if (!isset($server['CONTENT_TYPE'])) {
+                    $server['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';
+                }
+                // no break
+            case 'PATCH':
+                $request = $parameters;
+                $query = [];
+                break;
+            default:
+                $request = [];
+                $query = $parameters;
+                break;
+        }
+
+        $queryString = '';
+        if (isset($components['query'])) {
+            parse_str(html_entity_decode($components['query']), $qs);
+
+            if ($query) {
+                $query = array_replace($qs, $query);
+                $queryString = http_build_query($query, '', '&');
+            } else {
+                $query = $qs;
+                $queryString = $components['query'];
+            }
+        } elseif ($query) {
+            $queryString = http_build_query($query, '', '&');
+        }
+
+        $server['REQUEST_URI'] = $components['path'].('' !== $queryString ? '?'.$queryString : '');
+        $server['QUERY_STRING'] = $queryString;
+
+        return self::createRequestFromFactory($query, $request, [], $cookies, $files, $server, $content);
+    }
+
+    /**
+     * Sets a callable able to create a Request instance.
+     *
+     * This is mainly useful when you need to override the Request class
+     * to keep BC with an existing system. It should not be used for any
+     * other purpose.
+     *
+     * @param callable|null $callable A PHP callable
+     */
+    public static function setFactory($callable)
+    {
+        self::$requestFactory = $callable;
+    }
+
+    /**
+     * Clones a request and overrides some of its parameters.
+     *
+     * @param array $query      The GET parameters
+     * @param array $request    The POST parameters
+     * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
+     * @param array $cookies    The COOKIE parameters
+     * @param array $files      The FILES parameters
+     * @param array $server     The SERVER parameters
+     *
+     * @return static
+     */
+    public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null)
+    {
+        $dup = clone $this;
+        if (null !== $query) {
+            $dup->query = new ParameterBag($query);
+        }
+        if (null !== $request) {
+            $dup->request = new ParameterBag($request);
+        }
+        if (null !== $attributes) {
+            $dup->attributes = new ParameterBag($attributes);
+        }
+        if (null !== $cookies) {
+            $dup->cookies = new ParameterBag($cookies);
+        }
+        if (null !== $files) {
+            $dup->files = new FileBag($files);
+        }
+        if (null !== $server) {
+            $dup->server = new ServerBag($server);
+            $dup->headers = new HeaderBag($dup->server->getHeaders());
+        }
+        $dup->languages = null;
+        $dup->charsets = null;
+        $dup->encodings = null;
+        $dup->acceptableContentTypes = null;
+        $dup->pathInfo = null;
+        $dup->requestUri = null;
+        $dup->baseUrl = null;
+        $dup->basePath = null;
+        $dup->method = null;
+        $dup->format = null;
+
+        if (!$dup->get('_format') && $this->get('_format')) {
+            $dup->attributes->set('_format', $this->get('_format'));
+        }
+
+        if (!$dup->getRequestFormat(null)) {
+            $dup->setRequestFormat($this->getRequestFormat(null));
+        }
+
+        return $dup;
+    }
+
+    /**
+     * Clones the current request.
+     *
+     * Note that the session is not cloned as duplicated requests
+     * are most of the time sub-requests of the main one.
+     */
+    public function __clone()
+    {
+        $this->query = clone $this->query;
+        $this->request = clone $this->request;
+        $this->attributes = clone $this->attributes;
+        $this->cookies = clone $this->cookies;
+        $this->files = clone $this->files;
+        $this->server = clone $this->server;
+        $this->headers = clone $this->headers;
+    }
+
+    /**
+     * Returns the request as a string.
+     *
+     * @return string The request
+     */
+    public function __toString()
+    {
+        try {
+            $content = $this->getContent();
+        } catch (\LogicException $e) {
+            return trigger_error($e, E_USER_ERROR);
+        }
+
+        $cookieHeader = '';
+        $cookies = [];
+
+        foreach ($this->cookies as $k => $v) {
+            $cookies[] = $k.'='.$v;
+        }
+
+        if (!empty($cookies)) {
+            $cookieHeader = 'Cookie: '.implode('; ', $cookies)."\r\n";
+        }
+
+        return
+            sprintf('%s %s %s', $this->getMethod(), $this->getRequestUri(), $this->server->get('SERVER_PROTOCOL'))."\r\n".
+            $this->headers.
+            $cookieHeader."\r\n".
+            $content;
+    }
+
+    /**
+     * Overrides the PHP global variables according to this request instance.
+     *
+     * It overrides $_GET, $_POST, $_REQUEST, $_SERVER, $_COOKIE.
+     * $_FILES is never overridden, see rfc1867
+     */
+    public function overrideGlobals()
+    {
+        $this->server->set('QUERY_STRING', static::normalizeQueryString(http_build_query($this->query->all(), '', '&')));
+
+        $_GET = $this->query->all();
+        $_POST = $this->request->all();
+        $_SERVER = $this->server->all();
+        $_COOKIE = $this->cookies->all();
+
+        foreach ($this->headers->all() as $key => $value) {
+            $key = strtoupper(str_replace('-', '_', $key));
+            if (\in_array($key, ['CONTENT_TYPE', 'CONTENT_LENGTH'])) {
+                $_SERVER[$key] = implode(', ', $value);
+            } else {
+                $_SERVER['HTTP_'.$key] = implode(', ', $value);
+            }
+        }
+
+        $request = ['g' => $_GET, 'p' => $_POST, 'c' => $_COOKIE];
+
+        $requestOrder = ini_get('request_order') ?: ini_get('variables_order');
+        $requestOrder = preg_replace('#[^cgp]#', '', strtolower($requestOrder)) ?: 'gp';
+
+        $_REQUEST = [];
+        foreach (str_split($requestOrder) as $order) {
+            $_REQUEST = array_merge($_REQUEST, $request[$order]);
+        }
+    }
+
+    /**
+     * Sets a list of trusted proxies.
+     *
+     * You should only list the reverse proxies that you manage directly.
+     *
+     * @param array $proxies          A list of trusted proxies
+     * @param int   $trustedHeaderSet A bit field of Request::HEADER_*, to set which headers to trust from your proxies
+     *
+     * @throws \InvalidArgumentException When $trustedHeaderSet is invalid
+     */
+    public static function setTrustedProxies(array $proxies/*, int $trustedHeaderSet*/)
+    {
+        self::$trustedProxies = $proxies;
+
+        if (2 > \func_num_args()) {
+            @trigger_error(sprintf('The %s() method expects a bit field of Request::HEADER_* as second argument since Symfony 3.3. Defining it will be required in 4.0. ', __METHOD__), E_USER_DEPRECATED);
+
+            return;
+        }
+        $trustedHeaderSet = (int) func_get_arg(1);
+
+        foreach (self::$trustedHeaderNames as $header => $name) {
+            self::$trustedHeaders[$header] = $header & $trustedHeaderSet ? $name : null;
+        }
+        self::$trustedHeaderSet = $trustedHeaderSet;
+    }
+
+    /**
+     * Gets the list of trusted proxies.
+     *
+     * @return array An array of trusted proxies
+     */
+    public static function getTrustedProxies()
+    {
+        return self::$trustedProxies;
+    }
+
+    /**
+     * Gets the set of trusted headers from trusted proxies.
+     *
+     * @return int A bit field of Request::HEADER_* that defines which headers are trusted from your proxies
+     */
+    public static function getTrustedHeaderSet()
+    {
+        return self::$trustedHeaderSet;
+    }
+
+    /**
+     * Sets a list of trusted host patterns.
+     *
+     * You should only list the hosts you manage using regexs.
+     *
+     * @param array $hostPatterns A list of trusted host patterns
+     */
+    public static function setTrustedHosts(array $hostPatterns)
+    {
+        self::$trustedHostPatterns = array_map(function ($hostPattern) {
+            return sprintf('{%s}i', $hostPattern);
+        }, $hostPatterns);
+        // we need to reset trusted hosts on trusted host patterns change
+        self::$trustedHosts = [];
+    }
+
+    /**
+     * Gets the list of trusted host patterns.
+     *
+     * @return array An array of trusted host patterns
+     */
+    public static function getTrustedHosts()
+    {
+        return self::$trustedHostPatterns;
+    }
+
+    /**
+     * Sets the name for trusted headers.
+     *
+     * The following header keys are supported:
+     *
+     *  * Request::HEADER_CLIENT_IP:    defaults to X-Forwarded-For   (see getClientIp())
+     *  * Request::HEADER_CLIENT_HOST:  defaults to X-Forwarded-Host  (see getHost())
+     *  * Request::HEADER_CLIENT_PORT:  defaults to X-Forwarded-Port  (see getPort())
+     *  * Request::HEADER_CLIENT_PROTO: defaults to X-Forwarded-Proto (see getScheme() and isSecure())
+     *  * Request::HEADER_FORWARDED:    defaults to Forwarded         (see RFC 7239)
+     *
+     * Setting an empty value allows to disable the trusted header for the given key.
+     *
+     * @param string $key   The header key
+     * @param string $value The header name
+     *
+     * @throws \InvalidArgumentException
+     *
+     * @deprecated since version 3.3, to be removed in 4.0. Use the $trustedHeaderSet argument of the Request::setTrustedProxies() method instead.
+     */
+    public static function setTrustedHeaderName($key, $value)
+    {
+        @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the $trustedHeaderSet argument of the Request::setTrustedProxies() method instead.', __METHOD__), E_USER_DEPRECATED);
+
+        if ('forwarded' === $key) {
+            $key = self::HEADER_FORWARDED;
+        } elseif ('client_ip' === $key) {
+            $key = self::HEADER_CLIENT_IP;
+        } elseif ('client_host' === $key) {
+            $key = self::HEADER_CLIENT_HOST;
+        } elseif ('client_proto' === $key) {
+            $key = self::HEADER_CLIENT_PROTO;
+        } elseif ('client_port' === $key) {
+            $key = self::HEADER_CLIENT_PORT;
+        } elseif (!\array_key_exists($key, self::$trustedHeaders)) {
+            throw new \InvalidArgumentException(sprintf('Unable to set the trusted header name for key "%s".', $key));
+        }
+
+        self::$trustedHeaders[$key] = $value;
+
+        if (null !== $value) {
+            self::$trustedHeaderNames[$key] = $value;
+            self::$trustedHeaderSet |= $key;
+        } else {
+            self::$trustedHeaderSet &= ~$key;
+        }
+    }
+
+    /**
+     * Gets the trusted proxy header name.
+     *
+     * @param string $key The header key
+     *
+     * @return string The header name
+     *
+     * @throws \InvalidArgumentException
+     *
+     * @deprecated since version 3.3, to be removed in 4.0. Use the Request::getTrustedHeaderSet() method instead.
+     */
+    public static function getTrustedHeaderName($key)
+    {
+        if (2 > \func_num_args() || func_get_arg(1)) {
+            @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the Request::getTrustedHeaderSet() method instead.', __METHOD__), E_USER_DEPRECATED);
+        }
+
+        if (!\array_key_exists($key, self::$trustedHeaders)) {
+            throw new \InvalidArgumentException(sprintf('Unable to get the trusted header name for key "%s".', $key));
+        }
+
+        return self::$trustedHeaders[$key];
+    }
+
+    /**
+     * Normalizes a query string.
+     *
+     * It builds a normalized query string, where keys/value pairs are alphabetized,
+     * have consistent escaping and unneeded delimiters are removed.
+     *
+     * @param string $qs Query string
+     *
+     * @return string A normalized query string for the Request
+     */
+    public static function normalizeQueryString($qs)
+    {
+        if ('' == $qs) {
+            return '';
+        }
+
+        $parts = [];
+        $order = [];
+
+        foreach (explode('&', $qs) as $param) {
+            if ('' === $param || '=' === $param[0]) {
+                // Ignore useless delimiters, e.g. "x=y&".
+                // Also ignore pairs with empty key, even if there was a value, e.g. "=value", as such nameless values cannot be retrieved anyway.
+                // PHP also does not include them when building _GET.
+                continue;
+            }
+
+            $keyValuePair = explode('=', $param, 2);
+
+            // GET parameters, that are submitted from a HTML form, encode spaces as "+" by default (as defined in enctype application/x-www-form-urlencoded).
+            // PHP also converts "+" to spaces when filling the global _GET or when using the function parse_str. This is why we use urldecode and then normalize to
+            // RFC 3986 with rawurlencode.
+            $parts[] = isset($keyValuePair[1]) ?
+                rawurlencode(urldecode($keyValuePair[0])).'='.rawurlencode(urldecode($keyValuePair[1])) :
+                rawurlencode(urldecode($keyValuePair[0]));
+            $order[] = urldecode($keyValuePair[0]);
+        }
+
+        array_multisort($order, SORT_ASC, $parts);
+
+        return implode('&', $parts);
+    }
+
+    /**
+     * Enables support for the _method request parameter to determine the intended HTTP method.
+     *
+     * Be warned that enabling this feature might lead to CSRF issues in your code.
+     * Check that you are using CSRF tokens when required.
+     * If the HTTP method parameter override is enabled, an html-form with method "POST" can be altered
+     * and used to send a "PUT" or "DELETE" request via the _method request parameter.
+     * If these methods are not protected against CSRF, this presents a possible vulnerability.
+     *
+     * The HTTP method can only be overridden when the real HTTP method is POST.
+     */
+    public static function enableHttpMethodParameterOverride()
+    {
+        self::$httpMethodParameterOverride = true;
+    }
+
+    /**
+     * Checks whether support for the _method request parameter is enabled.
+     *
+     * @return bool True when the _method request parameter is enabled, false otherwise
+     */
+    public static function getHttpMethodParameterOverride()
+    {
+        return self::$httpMethodParameterOverride;
+    }
+
+    /**
+     * Gets a "parameter" value from any bag.
+     *
+     * This method is mainly useful for libraries that want to provide some flexibility. If you don't need the
+     * flexibility in controllers, it is better to explicitly get request parameters from the appropriate
+     * public property instead (attributes, query, request).
+     *
+     * Order of precedence: PATH (routing placeholders or custom attributes), GET, BODY
+     *
+     * @param string $key     The key
+     * @param mixed  $default The default value if the parameter key does not exist
+     *
+     * @return mixed
+     */
+    public function get($key, $default = null)
+    {
+        if ($this !== $result = $this->attributes->get($key, $this)) {
+            return $result;
+        }
+
+        if ($this !== $result = $this->query->get($key, $this)) {
+            return $result;
+        }
+
+        if ($this !== $result = $this->request->get($key, $this)) {
+            return $result;
+        }
+
+        return $default;
+    }
+
+    /**
+     * Gets the Session.
+     *
+     * @return SessionInterface|null The session
+     */
+    public function getSession()
+    {
+        return $this->session;
+    }
+
+    /**
+     * Whether the request contains a Session which was started in one of the
+     * previous requests.
+     *
+     * @return bool
+     */
+    public function hasPreviousSession()
+    {
+        // the check for $this->session avoids malicious users trying to fake a session cookie with proper name
+        return $this->hasSession() && $this->cookies->has($this->session->getName());
+    }
+
+    /**
+     * Whether the request contains a Session object.
+     *
+     * This method does not give any information about the state of the session object,
+     * like whether the session is started or not. It is just a way to check if this Request
+     * is associated with a Session instance.
+     *
+     * @return bool true when the Request contains a Session object, false otherwise
+     */
+    public function hasSession()
+    {
+        return null !== $this->session;
+    }
+
+    /**
+     * Sets the Session.
+     *
+     * @param SessionInterface $session The Session
+     */
+    public function setSession(SessionInterface $session)
+    {
+        $this->session = $session;
+    }
+
+    /**
+     * Returns the client IP addresses.
+     *
+     * In the returned array the most trusted IP address is first, and the
+     * least trusted one last. The "real" client IP address is the last one,
+     * but this is also the least trusted one. Trusted proxies are stripped.
+     *
+     * Use this method carefully; you should use getClientIp() instead.
+     *
+     * @return array The client IP addresses
+     *
+     * @see getClientIp()
+     */
+    public function getClientIps()
+    {
+        $ip = $this->server->get('REMOTE_ADDR');
+
+        if (!$this->isFromTrustedProxy()) {
+            return [$ip];
+        }
+
+        return $this->getTrustedValues(self::HEADER_CLIENT_IP, $ip) ?: [$ip];
+    }
+
+    /**
+     * Returns the client IP address.
+     *
+     * This method can read the client IP address from the "X-Forwarded-For" header
+     * when trusted proxies were set via "setTrustedProxies()". The "X-Forwarded-For"
+     * header value is a comma+space separated list of IP addresses, the left-most
+     * being the original client, and each successive proxy that passed the request
+     * adding the IP address where it received the request from.
+     *
+     * If your reverse proxy uses a different header name than "X-Forwarded-For",
+     * ("Client-Ip" for instance), configure it via the $trustedHeaderSet
+     * argument of the Request::setTrustedProxies() method instead.
+     *
+     * @return string|null The client IP address
+     *
+     * @see getClientIps()
+     * @see http://en.wikipedia.org/wiki/X-Forwarded-For
+     */
+    public function getClientIp()
+    {
+        $ipAddresses = $this->getClientIps();
+
+        return $ipAddresses[0];
+    }
+
+    /**
+     * Returns current script name.
+     *
+     * @return string
+     */
+    public function getScriptName()
+    {
+        return $this->server->get('SCRIPT_NAME', $this->server->get('ORIG_SCRIPT_NAME', ''));
+    }
+
+    /**
+     * Returns the path being requested relative to the executed script.
+     *
+     * The path info always starts with a /.
+     *
+     * Suppose this request is instantiated from /mysite on localhost:
+     *
+     *  * http://localhost/mysite              returns an empty string
+     *  * http://localhost/mysite/about        returns '/about'
+     *  * http://localhost/mysite/enco%20ded   returns '/enco%20ded'
+     *  * http://localhost/mysite/about?var=1  returns '/about'
+     *
+     * @return string The raw path (i.e. not urldecoded)
+     */
+    public function getPathInfo()
+    {
+        if (null === $this->pathInfo) {
+            $this->pathInfo = $this->preparePathInfo();
+        }
+
+        return $this->pathInfo;
+    }
+
+    /**
+     * Returns the root path from which this request is executed.
+     *
+     * Suppose that an index.php file instantiates this request object:
+     *
+     *  * http://localhost/index.php         returns an empty string
+     *  * http://localhost/index.php/page    returns an empty string
+     *  * http://localhost/web/index.php     returns '/web'
+     *  * http://localhost/we%20b/index.php  returns '/we%20b'
+     *
+     * @return string The raw path (i.e. not urldecoded)
+     */
+    public function getBasePath()
+    {
+        if (null === $this->basePath) {
+            $this->basePath = $this->prepareBasePath();
+        }
+
+        return $this->basePath;
+    }
+
+    /**
+     * Returns the root URL from which this request is executed.
+     *
+     * The base URL never ends with a /.
+     *
+     * This is similar to getBasePath(), except that it also includes the
+     * script filename (e.g. index.php) if one exists.
+     *
+     * @return string The raw URL (i.e. not urldecoded)
+     */
+    public function getBaseUrl()
+    {
+        if (null === $this->baseUrl) {
+            $this->baseUrl = $this->prepareBaseUrl();
+        }
+
+        return $this->baseUrl;
+    }
+
+    /**
+     * Gets the request's scheme.
+     *
+     * @return string
+     */
+    public function getScheme()
+    {
+        return $this->isSecure() ? 'https' : 'http';
+    }
+
+    /**
+     * Returns the port on which the request is made.
+     *
+     * This method can read the client port from the "X-Forwarded-Port" header
+     * when trusted proxies were set via "setTrustedProxies()".
+     *
+     * The "X-Forwarded-Port" header must contain the client port.
+     *
+     * If your reverse proxy uses a different header name than "X-Forwarded-Port",
+     * configure it via via the $trustedHeaderSet argument of the
+     * Request::setTrustedProxies() method instead.
+     *
+     * @return int|string can be a string if fetched from the server bag
+     */
+    public function getPort()
+    {
+        if ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_CLIENT_PORT)) {
+            $host = $host[0];
+        } elseif ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_CLIENT_HOST)) {
+            $host = $host[0];
+        } elseif (!$host = $this->headers->get('HOST')) {
+            return $this->server->get('SERVER_PORT');
+        }
+
+        if ('[' === $host[0]) {
+            $pos = strpos($host, ':', strrpos($host, ']'));
+        } else {
+            $pos = strrpos($host, ':');
+        }
+
+        if (false !== $pos) {
+            return (int) substr($host, $pos + 1);
+        }
+
+        return 'https' === $this->getScheme() ? 443 : 80;
+    }
+
+    /**
+     * Returns the user.
+     *
+     * @return string|null
+     */
+    public function getUser()
+    {
+        return $this->headers->get('PHP_AUTH_USER');
+    }
+
+    /**
+     * Returns the password.
+     *
+     * @return string|null
+     */
+    public function getPassword()
+    {
+        return $this->headers->get('PHP_AUTH_PW');
+    }
+
+    /**
+     * Gets the user info.
+     *
+     * @return string A user name and, optionally, scheme-specific information about how to gain authorization to access the server
+     */
+    public function getUserInfo()
+    {
+        $userinfo = $this->getUser();
+
+        $pass = $this->getPassword();
+        if ('' != $pass) {
+            $userinfo .= ":$pass";
+        }
+
+        return $userinfo;
+    }
+
+    /**
+     * Returns the HTTP host being requested.
+     *
+     * The port name will be appended to the host if it's non-standard.
+     *
+     * @return string
+     */
+    public function getHttpHost()
+    {
+        $scheme = $this->getScheme();
+        $port = $this->getPort();
+
+        if (('http' == $scheme && 80 == $port) || ('https' == $scheme && 443 == $port)) {
+            return $this->getHost();
+        }
+
+        return $this->getHost().':'.$port;
+    }
+
+    /**
+     * Returns the requested URI (path and query string).
+     *
+     * @return string The raw URI (i.e. not URI decoded)
+     */
+    public function getRequestUri()
+    {
+        if (null === $this->requestUri) {
+            $this->requestUri = $this->prepareRequestUri();
+        }
+
+        return $this->requestUri;
+    }
+
+    /**
+     * Gets the scheme and HTTP host.
+     *
+     * If the URL was called with basic authentication, the user
+     * and the password are not added to the generated string.
+     *
+     * @return string The scheme and HTTP host
+     */
+    public function getSchemeAndHttpHost()
+    {
+        return $this->getScheme().'://'.$this->getHttpHost();
+    }
+
+    /**
+     * Generates a normalized URI (URL) for the Request.
+     *
+     * @return string A normalized URI (URL) for the Request
+     *
+     * @see getQueryString()
+     */
+    public function getUri()
+    {
+        if (null !== $qs = $this->getQueryString()) {
+            $qs = '?'.$qs;
+        }
+
+        return $this->getSchemeAndHttpHost().$this->getBaseUrl().$this->getPathInfo().$qs;
+    }
+
+    /**
+     * Generates a normalized URI for the given path.
+     *
+     * @param string $path A path to use instead of the current one
+     *
+     * @return string The normalized URI for the path
+     */
+    public function getUriForPath($path)
+    {
+        return $this->getSchemeAndHttpHost().$this->getBaseUrl().$path;
+    }
+
+    /**
+     * Returns the path as relative reference from the current Request path.
+     *
+     * Only the URIs path component (no schema, host etc.) is relevant and must be given.
+     * Both paths must be absolute and not contain relative parts.
+     * Relative URLs from one resource to another are useful when generating self-contained downloadable document archives.
+     * Furthermore, they can be used to reduce the link size in documents.
+     *
+     * Example target paths, given a base path of "/a/b/c/d":
+     * - "/a/b/c/d"     -> ""
+     * - "/a/b/c/"      -> "./"
+     * - "/a/b/"        -> "../"
+     * - "/a/b/c/other" -> "other"
+     * - "/a/x/y"       -> "../../x/y"
+     *
+     * @param string $path The target path
+     *
+     * @return string The relative target path
+     */
+    public function getRelativeUriForPath($path)
+    {
+        // be sure that we are dealing with an absolute path
+        if (!isset($path[0]) || '/' !== $path[0]) {
+            return $path;
+        }
+
+        if ($path === $basePath = $this->getPathInfo()) {
+            return '';
+        }
+
+        $sourceDirs = explode('/', isset($basePath[0]) && '/' === $basePath[0] ? substr($basePath, 1) : $basePath);
+        $targetDirs = explode('/', substr($path, 1));
+        array_pop($sourceDirs);
+        $targetFile = array_pop($targetDirs);
+
+        foreach ($sourceDirs as $i => $dir) {
+            if (isset($targetDirs[$i]) && $dir === $targetDirs[$i]) {
+                unset($sourceDirs[$i], $targetDirs[$i]);
+            } else {
+                break;
+            }
+        }
+
+        $targetDirs[] = $targetFile;
+        $path = str_repeat('../', \count($sourceDirs)).implode('/', $targetDirs);
+
+        // A reference to the same base directory or an empty subdirectory must be prefixed with "./".
+        // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used
+        // as the first segment of a relative-path reference, as it would be mistaken for a scheme name
+        // (see http://tools.ietf.org/html/rfc3986#section-4.2).
+        return !isset($path[0]) || '/' === $path[0]
+            || false !== ($colonPos = strpos($path, ':')) && ($colonPos < ($slashPos = strpos($path, '/')) || false === $slashPos)
+            ? "./$path" : $path;
+    }
+
+    /**
+     * Generates the normalized query string for the Request.
+     *
+     * It builds a normalized query string, where keys/value pairs are alphabetized
+     * and have consistent escaping.
+     *
+     * @return string|null A normalized query string for the Request
+     */
+    public function getQueryString()
+    {
+        $qs = static::normalizeQueryString($this->server->get('QUERY_STRING'));
+
+        return '' === $qs ? null : $qs;
+    }
+
+    /**
+     * Checks whether the request is secure or not.
+     *
+     * This method can read the client protocol from the "X-Forwarded-Proto" header
+     * when trusted proxies were set via "setTrustedProxies()".
+     *
+     * The "X-Forwarded-Proto" header must contain the protocol: "https" or "http".
+     *
+     * If your reverse proxy uses a different header name than "X-Forwarded-Proto"
+     * ("SSL_HTTPS" for instance), configure it via the $trustedHeaderSet
+     * argument of the Request::setTrustedProxies() method instead.
+     *
+     * @return bool
+     */
+    public function isSecure()
+    {
+        if ($this->isFromTrustedProxy() && $proto = $this->getTrustedValues(self::HEADER_CLIENT_PROTO)) {
+            return \in_array(strtolower($proto[0]), ['https', 'on', 'ssl', '1'], true);
+        }
+
+        $https = $this->server->get('HTTPS');
+
+        return !empty($https) && 'off' !== strtolower($https);
+    }
+
+    /**
+     * Returns the host name.
+     *
+     * This method can read the client host name from the "X-Forwarded-Host" header
+     * when trusted proxies were set via "setTrustedProxies()".
+     *
+     * The "X-Forwarded-Host" header must contain the client host name.
+     *
+     * If your reverse proxy uses a different header name than "X-Forwarded-Host",
+     * configure it via the $trustedHeaderSet argument of the
+     * Request::setTrustedProxies() method instead.
+     *
+     * @return string
+     *
+     * @throws SuspiciousOperationException when the host name is invalid or not trusted
+     */
+    public function getHost()
+    {
+        if ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_CLIENT_HOST)) {
+            $host = $host[0];
+        } elseif (!$host = $this->headers->get('HOST')) {
+            if (!$host = $this->server->get('SERVER_NAME')) {
+                $host = $this->server->get('SERVER_ADDR', '');
+            }
+        }
+
+        // trim and remove port number from host
+        // host is lowercase as per RFC 952/2181
+        $host = strtolower(preg_replace('/:\d+$/', '', trim($host)));
+
+        // as the host can come from the user (HTTP_HOST and depending on the configuration, SERVER_NAME too can come from the user)
+        // check that it does not contain forbidden characters (see RFC 952 and RFC 2181)
+        // use preg_replace() instead of preg_match() to prevent DoS attacks with long host names
+        if ($host && '' !== preg_replace('/(?:^\[)?[a-zA-Z0-9-:\]_]+\.?/', '', $host)) {
+            if (!$this->isHostValid) {
+                return '';
+            }
+            $this->isHostValid = false;
+
+            throw new SuspiciousOperationException(sprintf('Invalid Host "%s".', $host));
+        }
+
+        if (\count(self::$trustedHostPatterns) > 0) {
+            // to avoid host header injection attacks, you should provide a list of trusted host patterns
+
+            if (\in_array($host, self::$trustedHosts)) {
+                return $host;
+            }
+
+            foreach (self::$trustedHostPatterns as $pattern) {
+                if (preg_match($pattern, $host)) {
+                    self::$trustedHosts[] = $host;
+
+                    return $host;
+                }
+            }
+
+            if (!$this->isHostValid) {
+                return '';
+            }
+            $this->isHostValid = false;
+
+            throw new SuspiciousOperationException(sprintf('Untrusted Host "%s".', $host));
+        }
+
+        return $host;
+    }
+
+    /**
+     * Sets the request method.
+     *
+     * @param string $method
+     */
+    public function setMethod($method)
+    {
+        $this->method = null;
+        $this->server->set('REQUEST_METHOD', $method);
+    }
+
+    /**
+     * Gets the request "intended" method.
+     *
+     * If the X-HTTP-Method-Override header is set, and if the method is a POST,
+     * then it is used to determine the "real" intended HTTP method.
+     *
+     * The _method request parameter can also be used to determine the HTTP method,
+     * but only if enableHttpMethodParameterOverride() has been called.
+     *
+     * The method is always an uppercased string.
+     *
+     * @return string The request method
+     *
+     * @see getRealMethod()
+     */
+    public function getMethod()
+    {
+        if (null !== $this->method) {
+            return $this->method;
+        }
+
+        $this->method = strtoupper($this->server->get('REQUEST_METHOD', 'GET'));
+
+        if ('POST' !== $this->method) {
+            return $this->method;
+        }
+
+        $method = $this->headers->get('X-HTTP-METHOD-OVERRIDE');
+
+        if (!$method && self::$httpMethodParameterOverride) {
+            $method = $this->request->get('_method', $this->query->get('_method', 'POST'));
+        }
+
+        if (!\is_string($method)) {
+            return $this->method;
+        }
+
+        $method = strtoupper($method);
+
+        if (\in_array($method, ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'PATCH', 'PURGE', 'TRACE'], true)) {
+            return $this->method = $method;
+        }
+
+        if (!preg_match('/^[A-Z]++$/D', $method)) {
+            throw new SuspiciousOperationException(sprintf('Invalid method override "%s".', $method));
+        }
+
+        return $this->method = $method;
+    }
+
+    /**
+     * Gets the "real" request method.
+     *
+     * @return string The request method
+     *
+     * @see getMethod()
+     */
+    public function getRealMethod()
+    {
+        return strtoupper($this->server->get('REQUEST_METHOD', 'GET'));
+    }
+
+    /**
+     * Gets the mime type associated with the format.
+     *
+     * @param string $format The format
+     *
+     * @return string|null The associated mime type (null if not found)
+     */
+    public function getMimeType($format)
+    {
+        if (null === static::$formats) {
+            static::initializeFormats();
+        }
+
+        return isset(static::$formats[$format]) ? static::$formats[$format][0] : null;
+    }
+
+    /**
+     * Gets the mime types associated with the format.
+     *
+     * @param string $format The format
+     *
+     * @return array The associated mime types
+     */
+    public static function getMimeTypes($format)
+    {
+        if (null === static::$formats) {
+            static::initializeFormats();
+        }
+
+        return isset(static::$formats[$format]) ? static::$formats[$format] : [];
+    }
+
+    /**
+     * Gets the format associated with the mime type.
+     *
+     * @param string $mimeType The associated mime type
+     *
+     * @return string|null The format (null if not found)
+     */
+    public function getFormat($mimeType)
+    {
+        $canonicalMimeType = null;
+        if (false !== $pos = strpos($mimeType, ';')) {
+            $canonicalMimeType = trim(substr($mimeType, 0, $pos));
+        }
+
+        if (null === static::$formats) {
+            static::initializeFormats();
+        }
+
+        foreach (static::$formats as $format => $mimeTypes) {
+            if (\in_array($mimeType, (array) $mimeTypes)) {
+                return $format;
+            }
+            if (null !== $canonicalMimeType && \in_array($canonicalMimeType, (array) $mimeTypes)) {
+                return $format;
+            }
+        }
+    }
+
+    /**
+     * Associates a format with mime types.
+     *
+     * @param string       $format    The format
+     * @param string|array $mimeTypes The associated mime types (the preferred one must be the first as it will be used as the content type)
+     */
+    public function setFormat($format, $mimeTypes)
+    {
+        if (null === static::$formats) {
+            static::initializeFormats();
+        }
+
+        static::$formats[$format] = \is_array($mimeTypes) ? $mimeTypes : [$mimeTypes];
+    }
+
+    /**
+     * Gets the request format.
+     *
+     * Here is the process to determine the format:
+     *
+     *  * format defined by the user (with setRequestFormat())
+     *  * _format request attribute
+     *  * $default
+     *
+     * @param string|null $default The default format
+     *
+     * @return string|null The request format
+     */
+    public function getRequestFormat($default = 'html')
+    {
+        if (null === $this->format) {
+            $this->format = $this->attributes->get('_format');
+        }
+
+        return null === $this->format ? $default : $this->format;
+    }
+
+    /**
+     * Sets the request format.
+     *
+     * @param string $format The request format
+     */
+    public function setRequestFormat($format)
+    {
+        $this->format = $format;
+    }
+
+    /**
+     * Gets the format associated with the request.
+     *
+     * @return string|null The format (null if no content type is present)
+     */
+    public function getContentType()
+    {
+        return $this->getFormat($this->headers->get('CONTENT_TYPE'));
+    }
+
+    /**
+     * Sets the default locale.
+     *
+     * @param string $locale
+     */
+    public function setDefaultLocale($locale)
+    {
+        $this->defaultLocale = $locale;
+
+        if (null === $this->locale) {
+            $this->setPhpDefaultLocale($locale);
+        }
+    }
+
+    /**
+     * Get the default locale.
+     *
+     * @return string
+     */
+    public function getDefaultLocale()
+    {
+        return $this->defaultLocale;
+    }
+
+    /**
+     * Sets the locale.
+     *
+     * @param string $locale
+     */
+    public function setLocale($locale)
+    {
+        $this->setPhpDefaultLocale($this->locale = $locale);
+    }
+
+    /**
+     * Get the locale.
+     *
+     * @return string
+     */
+    public function getLocale()
+    {
+        return null === $this->locale ? $this->defaultLocale : $this->locale;
+    }
+
+    /**
+     * Checks if the request method is of specified type.
+     *
+     * @param string $method Uppercase request method (GET, POST etc)
+     *
+     * @return bool
+     */
+    public function isMethod($method)
+    {
+        return $this->getMethod() === strtoupper($method);
+    }
+
+    /**
+     * Checks whether or not the method is safe.
+     *
+     * @see https://tools.ietf.org/html/rfc7231#section-4.2.1
+     *
+     * @param bool $andCacheable Adds the additional condition that the method should be cacheable. True by default.
+     *
+     * @return bool
+     */
+    public function isMethodSafe(/* $andCacheable = true */)
+    {
+        if (!\func_num_args() || func_get_arg(0)) {
+            // This deprecation should be turned into a BadMethodCallException in 4.0 (without adding the argument in the signature)
+            // then setting $andCacheable to false should be deprecated in 4.1
+            @trigger_error('Checking only for cacheable HTTP methods with Symfony\Component\HttpFoundation\Request::isMethodSafe() is deprecated since Symfony 3.2 and will throw an exception in 4.0. Disable checking only for cacheable methods by calling the method with `false` as first argument or use the Request::isMethodCacheable() instead.', E_USER_DEPRECATED);
+
+            return \in_array($this->getMethod(), ['GET', 'HEAD']);
+        }
+
+        return \in_array($this->getMethod(), ['GET', 'HEAD', 'OPTIONS', 'TRACE']);
+    }
+
+    /**
+     * Checks whether or not the method is idempotent.
+     *
+     * @return bool
+     */
+    public function isMethodIdempotent()
+    {
+        return \in_array($this->getMethod(), ['HEAD', 'GET', 'PUT', 'DELETE', 'TRACE', 'OPTIONS', 'PURGE']);
+    }
+
+    /**
+     * Checks whether the method is cacheable or not.
+     *
+     * @see https://tools.ietf.org/html/rfc7231#section-4.2.3
+     *
+     * @return bool True for GET and HEAD, false otherwise
+     */
+    public function isMethodCacheable()
+    {
+        return \in_array($this->getMethod(), ['GET', 'HEAD']);
+    }
+
+    /**
+     * Returns the protocol version.
+     *
+     * If the application is behind a proxy, the protocol version used in the
+     * requests between the client and the proxy and between the proxy and the
+     * server might be different. This returns the former (from the "Via" header)
+     * if the proxy is trusted (see "setTrustedProxies()"), otherwise it returns
+     * the latter (from the "SERVER_PROTOCOL" server parameter).
+     *
+     * @return string
+     */
+    public function getProtocolVersion()
+    {
+        if ($this->isFromTrustedProxy()) {
+            preg_match('~^(HTTP/)?([1-9]\.[0-9]) ~', $this->headers->get('Via'), $matches);
+
+            if ($matches) {
+                return 'HTTP/'.$matches[2];
+            }
+        }
+
+        return $this->server->get('SERVER_PROTOCOL');
+    }
+
+    /**
+     * Returns the request body content.
+     *
+     * @param bool $asResource If true, a resource will be returned
+     *
+     * @return string|resource The request body content or a resource to read the body stream
+     *
+     * @throws \LogicException
+     */
+    public function getContent($asResource = false)
+    {
+        $currentContentIsResource = \is_resource($this->content);
+        if (\PHP_VERSION_ID < 50600 && false === $this->content) {
+            throw new \LogicException('getContent() can only be called once when using the resource return type and PHP below 5.6.');
+        }
+
+        if (true === $asResource) {
+            if ($currentContentIsResource) {
+                rewind($this->content);
+
+                return $this->content;
+            }
+
+            // Content passed in parameter (test)
+            if (\is_string($this->content)) {
+                $resource = fopen('php://temp', 'r+');
+                fwrite($resource, $this->content);
+                rewind($resource);
+
+                return $resource;
+            }
+
+            $this->content = false;
+
+            return fopen('php://input', 'rb');
+        }
+
+        if ($currentContentIsResource) {
+            rewind($this->content);
+
+            return stream_get_contents($this->content);
+        }
+
+        if (null === $this->content || false === $this->content) {
+            $this->content = file_get_contents('php://input');
+        }
+
+        return $this->content;
+    }
+
+    /**
+     * Gets the Etags.
+     *
+     * @return array The entity tags
+     */
+    public function getETags()
+    {
+        return preg_split('/\s*,\s*/', $this->headers->get('if_none_match'), null, PREG_SPLIT_NO_EMPTY);
+    }
+
+    /**
+     * @return bool
+     */
+    public function isNoCache()
+    {
+        return $this->headers->hasCacheControlDirective('no-cache') || 'no-cache' == $this->headers->get('Pragma');
+    }
+
+    /**
+     * Returns the preferred language.
+     *
+     * @param array $locales An array of ordered available locales
+     *
+     * @return string|null The preferred locale
+     */
+    public function getPreferredLanguage(array $locales = null)
+    {
+        $preferredLanguages = $this->getLanguages();
+
+        if (empty($locales)) {
+            return isset($preferredLanguages[0]) ? $preferredLanguages[0] : null;
+        }
+
+        if (!$preferredLanguages) {
+            return $locales[0];
+        }
+
+        $extendedPreferredLanguages = [];
+        foreach ($preferredLanguages as $language) {
+            $extendedPreferredLanguages[] = $language;
+            if (false !== $position = strpos($language, '_')) {
+                $superLanguage = substr($language, 0, $position);
+                if (!\in_array($superLanguage, $preferredLanguages)) {
+                    $extendedPreferredLanguages[] = $superLanguage;
+                }
+            }
+        }
+
+        $preferredLanguages = array_values(array_intersect($extendedPreferredLanguages, $locales));
+
+        return isset($preferredLanguages[0]) ? $preferredLanguages[0] : $locales[0];
+    }
+
+    /**
+     * Gets a list of languages acceptable by the client browser.
+     *
+     * @return array Languages ordered in the user browser preferences
+     */
+    public function getLanguages()
+    {
+        if (null !== $this->languages) {
+            return $this->languages;
+        }
+
+        $languages = AcceptHeader::fromString($this->headers->get('Accept-Language'))->all();
+        $this->languages = [];
+        foreach ($languages as $lang => $acceptHeaderItem) {
+            if (false !== strpos($lang, '-')) {
+                $codes = explode('-', $lang);
+                if ('i' === $codes[0]) {
+                    // Language not listed in ISO 639 that are not variants
+                    // of any listed language, which can be registered with the
+                    // i-prefix, such as i-cherokee
+                    if (\count($codes) > 1) {
+                        $lang = $codes[1];
+                    }
+                } else {
+                    for ($i = 0, $max = \count($codes); $i < $max; ++$i) {
+                        if (0 === $i) {
+                            $lang = strtolower($codes[0]);
+                        } else {
+                            $lang .= '_'.strtoupper($codes[$i]);
+                        }
+                    }
+                }
+            }
+
+            $this->languages[] = $lang;
+        }
+
+        return $this->languages;
+    }
+
+    /**
+     * Gets a list of charsets acceptable by the client browser.
+     *
+     * @return array List of charsets in preferable order
+     */
+    public function getCharsets()
+    {
+        if (null !== $this->charsets) {
+            return $this->charsets;
+        }
+
+        return $this->charsets = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Charset'))->all());
+    }
+
+    /**
+     * Gets a list of encodings acceptable by the client browser.
+     *
+     * @return array List of encodings in preferable order
+     */
+    public function getEncodings()
+    {
+        if (null !== $this->encodings) {
+            return $this->encodings;
+        }
+
+        return $this->encodings = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Encoding'))->all());
+    }
+
+    /**
+     * Gets a list of content types acceptable by the client browser.
+     *
+     * @return array List of content types in preferable order
+     */
+    public function getAcceptableContentTypes()
+    {
+        if (null !== $this->acceptableContentTypes) {
+            return $this->acceptableContentTypes;
+        }
+
+        return $this->acceptableContentTypes = array_keys(AcceptHeader::fromString($this->headers->get('Accept'))->all());
+    }
+
+    /**
+     * Returns true if the request is a XMLHttpRequest.
+     *
+     * It works if your JavaScript library sets an X-Requested-With HTTP header.
+     * It is known to work with common JavaScript frameworks:
+     *
+     * @see http://en.wikipedia.org/wiki/List_of_Ajax_frameworks#JavaScript
+     *
+     * @return bool true if the request is an XMLHttpRequest, false otherwise
+     */
+    public function isXmlHttpRequest()
+    {
+        return 'XMLHttpRequest' == $this->headers->get('X-Requested-With');
+    }
+
+    /*
+     * The following methods are derived from code of the Zend Framework (1.10dev - 2010-01-24)
+     *
+     * Code subject to the new BSD license (http://framework.zend.com/license/new-bsd).
+     *
+     * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+     */
+
+    protected function prepareRequestUri()
+    {
+        $requestUri = '';
+
+        if ('1' == $this->server->get('IIS_WasUrlRewritten') && '' != $this->server->get('UNENCODED_URL')) {
+            // IIS7 with URL Rewrite: make sure we get the unencoded URL (double slash problem)
+            $requestUri = $this->server->get('UNENCODED_URL');
+            $this->server->remove('UNENCODED_URL');
+            $this->server->remove('IIS_WasUrlRewritten');
+        } elseif ($this->server->has('REQUEST_URI')) {
+            $requestUri = $this->server->get('REQUEST_URI');
+
+            if ('' !== $requestUri && '/' === $requestUri[0]) {
+                // To only use path and query remove the fragment.
+                if (false !== $pos = strpos($requestUri, '#')) {
+                    $requestUri = substr($requestUri, 0, $pos);
+                }
+            } else {
+                // HTTP proxy reqs setup request URI with scheme and host [and port] + the URL path,
+                // only use URL path.
+                $uriComponents = parse_url($requestUri);
+
+                if (isset($uriComponents['path'])) {
+                    $requestUri = $uriComponents['path'];
+                }
+
+                if (isset($uriComponents['query'])) {
+                    $requestUri .= '?'.$uriComponents['query'];
+                }
+            }
+        } elseif ($this->server->has('ORIG_PATH_INFO')) {
+            // IIS 5.0, PHP as CGI
+            $requestUri = $this->server->get('ORIG_PATH_INFO');
+            if ('' != $this->server->get('QUERY_STRING')) {
+                $requestUri .= '?'.$this->server->get('QUERY_STRING');
+            }
+            $this->server->remove('ORIG_PATH_INFO');
+        }
+
+        // normalize the request URI to ease creating sub-requests from this request
+        $this->server->set('REQUEST_URI', $requestUri);
+
+        return $requestUri;
+    }
+
+    /**
+     * Prepares the base URL.
+     *
+     * @return string
+     */
+    protected function prepareBaseUrl()
+    {
+        $filename = basename($this->server->get('SCRIPT_FILENAME'));
+
+        if (basename($this->server->get('SCRIPT_NAME')) === $filename) {
+            $baseUrl = $this->server->get('SCRIPT_NAME');
+        } elseif (basename($this->server->get('PHP_SELF')) === $filename) {
+            $baseUrl = $this->server->get('PHP_SELF');
+        } elseif (basename($this->server->get('ORIG_SCRIPT_NAME')) === $filename) {
+            $baseUrl = $this->server->get('ORIG_SCRIPT_NAME'); // 1and1 shared hosting compatibility
+        } else {
+            // Backtrack up the script_filename to find the portion matching
+            // php_self
+            $path = $this->server->get('PHP_SELF', '');
+            $file = $this->server->get('SCRIPT_FILENAME', '');
+            $segs = explode('/', trim($file, '/'));
+            $segs = array_reverse($segs);
+            $index = 0;
+            $last = \count($segs);
+            $baseUrl = '';
+            do {
+                $seg = $segs[$index];
+                $baseUrl = '/'.$seg.$baseUrl;
+                ++$index;
+            } while ($last > $index && (false !== $pos = strpos($path, $baseUrl)) && 0 != $pos);
+        }
+
+        // Does the baseUrl have anything in common with the request_uri?
+        $requestUri = $this->getRequestUri();
+        if ('' !== $requestUri && '/' !== $requestUri[0]) {
+            $requestUri = '/'.$requestUri;
+        }
+
+        if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, $baseUrl)) {
+            // full $baseUrl matches
+            return $prefix;
+        }
+
+        if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, rtrim(\dirname($baseUrl), '/'.\DIRECTORY_SEPARATOR).'/')) {
+            // directory portion of $baseUrl matches
+            return rtrim($prefix, '/'.\DIRECTORY_SEPARATOR);
+        }
+
+        $truncatedRequestUri = $requestUri;
+        if (false !== $pos = strpos($requestUri, '?')) {
+            $truncatedRequestUri = substr($requestUri, 0, $pos);
+        }
+
+        $basename = basename($baseUrl);
+        if (empty($basename) || !strpos(rawurldecode($truncatedRequestUri), $basename)) {
+            // no match whatsoever; set it blank
+            return '';
+        }
+
+        // If using mod_rewrite or ISAPI_Rewrite strip the script filename
+        // out of baseUrl. $pos !== 0 makes sure it is not matching a value
+        // from PATH_INFO or QUERY_STRING
+        if (\strlen($requestUri) >= \strlen($baseUrl) && (false !== $pos = strpos($requestUri, $baseUrl)) && 0 !== $pos) {
+            $baseUrl = substr($requestUri, 0, $pos + \strlen($baseUrl));
+        }
+
+        return rtrim($baseUrl, '/'.\DIRECTORY_SEPARATOR);
+    }
+
+    /**
+     * Prepares the base path.
+     *
+     * @return string base path
+     */
+    protected function prepareBasePath()
+    {
+        $baseUrl = $this->getBaseUrl();
+        if (empty($baseUrl)) {
+            return '';
+        }
+
+        $filename = basename($this->server->get('SCRIPT_FILENAME'));
+        if (basename($baseUrl) === $filename) {
+            $basePath = \dirname($baseUrl);
+        } else {
+            $basePath = $baseUrl;
+        }
+
+        if ('\\' === \DIRECTORY_SEPARATOR) {
+            $basePath = str_replace('\\', '/', $basePath);
+        }
+
+        return rtrim($basePath, '/');
+    }
+
+    /**
+     * Prepares the path info.
+     *
+     * @return string path info
+     */
+    protected function preparePathInfo()
+    {
+        if (null === ($requestUri = $this->getRequestUri())) {
+            return '/';
+        }
+
+        // Remove the query string from REQUEST_URI
+        if (false !== $pos = strpos($requestUri, '?')) {
+            $requestUri = substr($requestUri, 0, $pos);
+        }
+        if ('' !== $requestUri && '/' !== $requestUri[0]) {
+            $requestUri = '/'.$requestUri;
+        }
+
+        if (null === ($baseUrl = $this->getBaseUrl())) {
+            return $requestUri;
+        }
+
+        $pathInfo = substr($requestUri, \strlen($baseUrl));
+        if (false === $pathInfo || '' === $pathInfo) {
+            // If substr() returns false then PATH_INFO is set to an empty string
+            return '/';
+        }
+
+        return (string) $pathInfo;
+    }
+
+    /**
+     * Initializes HTTP request formats.
+     */
+    protected static function initializeFormats()
+    {
+        static::$formats = [
+            'html' => ['text/html', 'application/xhtml+xml'],
+            'txt' => ['text/plain'],
+            'js' => ['application/javascript', 'application/x-javascript', 'text/javascript'],
+            'css' => ['text/css'],
+            'json' => ['application/json', 'application/x-json'],
+            'jsonld' => ['application/ld+json'],
+            'xml' => ['text/xml', 'application/xml', 'application/x-xml'],
+            'rdf' => ['application/rdf+xml'],
+            'atom' => ['application/atom+xml'],
+            'rss' => ['application/rss+xml'],
+            'form' => ['application/x-www-form-urlencoded'],
+        ];
+    }
+
+    /**
+     * Sets the default PHP locale.
+     *
+     * @param string $locale
+     */
+    private function setPhpDefaultLocale($locale)
+    {
+        // if either the class Locale doesn't exist, or an exception is thrown when
+        // setting the default locale, the intl module is not installed, and
+        // the call can be ignored:
+        try {
+            if (class_exists('Locale', false)) {
+                \Locale::setDefault($locale);
+            }
+        } catch (\Exception $e) {
+        }
+    }
+
+    /**
+     * Returns the prefix as encoded in the string when the string starts with
+     * the given prefix, false otherwise.
+     *
+     * @param string $string The urlencoded string
+     * @param string $prefix The prefix not encoded
+     *
+     * @return string|false The prefix as it is encoded in $string, or false
+     */
+    private function getUrlencodedPrefix($string, $prefix)
+    {
+        if (0 !== strpos(rawurldecode($string), $prefix)) {
+            return false;
+        }
+
+        $len = \strlen($prefix);
+
+        if (preg_match(sprintf('#^(%%[[:xdigit:]]{2}|.){%d}#', $len), $string, $match)) {
+            return $match[0];
+        }
+
+        return false;
+    }
+
+    private static function createRequestFromFactory(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null)
+    {
+        if (self::$requestFactory) {
+            $request = \call_user_func(self::$requestFactory, $query, $request, $attributes, $cookies, $files, $server, $content);
+
+            if (!$request instanceof self) {
+                throw new \LogicException('The Request factory must return an instance of Symfony\Component\HttpFoundation\Request.');
+            }
+
+            return $request;
+        }
+
+        return new static($query, $request, $attributes, $cookies, $files, $server, $content);
+    }
+
+    /**
+     * Indicates whether this request originated from a trusted proxy.
+     *
+     * This can be useful to determine whether or not to trust the
+     * contents of a proxy-specific header.
+     *
+     * @return bool true if the request came from a trusted proxy, false otherwise
+     */
+    public function isFromTrustedProxy()
+    {
+        return self::$trustedProxies && IpUtils::checkIp($this->server->get('REMOTE_ADDR'), self::$trustedProxies);
+    }
+
+    private function getTrustedValues($type, $ip = null)
+    {
+        $clientValues = [];
+        $forwardedValues = [];
+
+        if (self::$trustedHeaders[$type] && $this->headers->has(self::$trustedHeaders[$type])) {
+            foreach (explode(',', $this->headers->get(self::$trustedHeaders[$type])) as $v) {
+                $clientValues[] = (self::HEADER_CLIENT_PORT === $type ? '0.0.0.0:' : '').trim($v);
+            }
+        }
+
+        if (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) {
+            $forwardedValues = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]);
+            $forwardedValues = preg_match_all(sprintf('{(?:%s)="?([a-zA-Z0-9\.:_\-/\[\]]*+)}', self::$forwardedParams[$type]), $forwardedValues, $matches) ? $matches[1] : [];
+            if (self::HEADER_CLIENT_PORT === $type) {
+                foreach ($forwardedValues as $k => $v) {
+                    if (']' === substr($v, -1) || false === $v = strrchr($v, ':')) {
+                        $v = $this->isSecure() ? ':443' : ':80';
+                    }
+                    $forwardedValues[$k] = '0.0.0.0'.$v;
+                }
+            }
+        }
+
+        if (null !== $ip) {
+            $clientValues = $this->normalizeAndFilterClientIps($clientValues, $ip);
+            $forwardedValues = $this->normalizeAndFilterClientIps($forwardedValues, $ip);
+        }
+
+        if ($forwardedValues === $clientValues || !$clientValues) {
+            return $forwardedValues;
+        }
+
+        if (!$forwardedValues) {
+            return $clientValues;
+        }
+
+        if (!$this->isForwardedValid) {
+            return null !== $ip ? ['0.0.0.0', $ip] : [];
+        }
+        $this->isForwardedValid = false;
+
+        throw new ConflictingHeadersException(sprintf('The request has both a trusted "%s" header and a trusted "%s" header, conflicting with each other. You should either configure your proxy to remove one of them, or configure your project to distrust the offending one.', self::$trustedHeaders[self::HEADER_FORWARDED], self::$trustedHeaders[$type]));
+    }
+
+    private function normalizeAndFilterClientIps(array $clientIps, $ip)
+    {
+        if (!$clientIps) {
+            return [];
+        }
+        $clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from
+        $firstTrustedIp = null;
+
+        foreach ($clientIps as $key => $clientIp) {
+            if (strpos($clientIp, '.')) {
+                // Strip :port from IPv4 addresses. This is allowed in Forwarded
+                // and may occur in X-Forwarded-For.
+                $i = strpos($clientIp, ':');
+                if ($i) {
+                    $clientIps[$key] = $clientIp = substr($clientIp, 0, $i);
+                }
+            } elseif (0 === strpos($clientIp, '[')) {
+                // Strip brackets and :port from IPv6 addresses.
+                $i = strpos($clientIp, ']', 1);
+                $clientIps[$key] = $clientIp = substr($clientIp, 1, $i - 1);
+            }
+
+            if (!filter_var($clientIp, FILTER_VALIDATE_IP)) {
+                unset($clientIps[$key]);
+
+                continue;
+            }
+
+            if (IpUtils::checkIp($clientIp, self::$trustedProxies)) {
+                unset($clientIps[$key]);
+
+                // Fallback to this when the client IP falls into the range of trusted proxies
+                if (null === $firstTrustedIp) {
+                    $firstTrustedIp = $clientIp;
+                }
+            }
+        }
+
+        // Now the IP chain contains only untrusted proxies and the client IP
+        return $clientIps ? array_reverse($clientIps) : [$firstTrustedIp];
+    }
+}
diff --git a/vendor/symfony/http-foundation/RequestMatcher.php b/vendor/symfony/http-foundation/RequestMatcher.php
new file mode 100755
index 0000000..cadf70c
--- /dev/null
+++ b/vendor/symfony/http-foundation/RequestMatcher.php
@@ -0,0 +1,178 @@
+<?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\HttpFoundation;
+
+/**
+ * RequestMatcher compares a pre-defined set of checks against a Request instance.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class RequestMatcher implements RequestMatcherInterface
+{
+    /**
+     * @var string|null
+     */
+    private $path;
+
+    /**
+     * @var string|null
+     */
+    private $host;
+
+    /**
+     * @var string[]
+     */
+    private $methods = [];
+
+    /**
+     * @var string[]
+     */
+    private $ips = [];
+
+    /**
+     * @var array
+     */
+    private $attributes = [];
+
+    /**
+     * @var string[]
+     */
+    private $schemes = [];
+
+    /**
+     * @param string|null          $path
+     * @param string|null          $host
+     * @param string|string[]|null $methods
+     * @param string|string[]|null $ips
+     * @param array                $attributes
+     * @param string|string[]|null $schemes
+     */
+    public function __construct($path = null, $host = null, $methods = null, $ips = null, array $attributes = [], $schemes = null)
+    {
+        $this->matchPath($path);
+        $this->matchHost($host);
+        $this->matchMethod($methods);
+        $this->matchIps($ips);
+        $this->matchScheme($schemes);
+
+        foreach ($attributes as $k => $v) {
+            $this->matchAttribute($k, $v);
+        }
+    }
+
+    /**
+     * Adds a check for the HTTP scheme.
+     *
+     * @param string|string[]|null $scheme An HTTP scheme or an array of HTTP schemes
+     */
+    public function matchScheme($scheme)
+    {
+        $this->schemes = null !== $scheme ? array_map('strtolower', (array) $scheme) : [];
+    }
+
+    /**
+     * Adds a check for the URL host name.
+     *
+     * @param string|null $regexp A Regexp
+     */
+    public function matchHost($regexp)
+    {
+        $this->host = $regexp;
+    }
+
+    /**
+     * Adds a check for the URL path info.
+     *
+     * @param string|null $regexp A Regexp
+     */
+    public function matchPath($regexp)
+    {
+        $this->path = $regexp;
+    }
+
+    /**
+     * Adds a check for the client IP.
+     *
+     * @param string $ip A specific IP address or a range specified using IP/netmask like 192.168.1.0/24
+     */
+    public function matchIp($ip)
+    {
+        $this->matchIps($ip);
+    }
+
+    /**
+     * Adds a check for the client IP.
+     *
+     * @param string|string[]|null $ips A specific IP address or a range specified using IP/netmask like 192.168.1.0/24
+     */
+    public function matchIps($ips)
+    {
+        $this->ips = null !== $ips ? (array) $ips : [];
+    }
+
+    /**
+     * Adds a check for the HTTP method.
+     *
+     * @param string|string[]|null $method An HTTP method or an array of HTTP methods
+     */
+    public function matchMethod($method)
+    {
+        $this->methods = null !== $method ? array_map('strtoupper', (array) $method) : [];
+    }
+
+    /**
+     * Adds a check for request attribute.
+     *
+     * @param string $key    The request attribute name
+     * @param string $regexp A Regexp
+     */
+    public function matchAttribute($key, $regexp)
+    {
+        $this->attributes[$key] = $regexp;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function matches(Request $request)
+    {
+        if ($this->schemes && !\in_array($request->getScheme(), $this->schemes, true)) {
+            return false;
+        }
+
+        if ($this->methods && !\in_array($request->getMethod(), $this->methods, true)) {
+            return false;
+        }
+
+        foreach ($this->attributes as $key => $pattern) {
+            if (!preg_match('{'.$pattern.'}', $request->attributes->get($key))) {
+                return false;
+            }
+        }
+
+        if (null !== $this->path && !preg_match('{'.$this->path.'}', rawurldecode($request->getPathInfo()))) {
+            return false;
+        }
+
+        if (null !== $this->host && !preg_match('{'.$this->host.'}i', $request->getHost())) {
+            return false;
+        }
+
+        if (IpUtils::checkIp($request->getClientIp(), $this->ips)) {
+            return true;
+        }
+
+        // Note to future implementors: add additional checks above the
+        // foreach above or else your check might not be run!
+        return 0 === \count($this->ips);
+    }
+}
diff --git a/vendor/symfony/http-foundation/RequestMatcherInterface.php b/vendor/symfony/http-foundation/RequestMatcherInterface.php
new file mode 100755
index 0000000..c26db3e
--- /dev/null
+++ b/vendor/symfony/http-foundation/RequestMatcherInterface.php
@@ -0,0 +1,27 @@
+<?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\HttpFoundation;
+
+/**
+ * RequestMatcherInterface is an interface for strategies to match a Request.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface RequestMatcherInterface
+{
+    /**
+     * Decides whether the rule(s) implemented by the strategy matches the supplied request.
+     *
+     * @return bool true if the request matches, false otherwise
+     */
+    public function matches(Request $request);
+}
diff --git a/vendor/symfony/http-foundation/RequestStack.php b/vendor/symfony/http-foundation/RequestStack.php
new file mode 100755
index 0000000..885d78a
--- /dev/null
+++ b/vendor/symfony/http-foundation/RequestStack.php
@@ -0,0 +1,103 @@
+<?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\HttpFoundation;
+
+/**
+ * Request stack that controls the lifecycle of requests.
+ *
+ * @author Benjamin Eberlei <kontakt@beberlei.de>
+ */
+class RequestStack
+{
+    /**
+     * @var Request[]
+     */
+    private $requests = [];
+
+    /**
+     * Pushes a Request on the stack.
+     *
+     * This method should generally not be called directly as the stack
+     * management should be taken care of by the application itself.
+     */
+    public function push(Request $request)
+    {
+        $this->requests[] = $request;
+    }
+
+    /**
+     * Pops the current request from the stack.
+     *
+     * This operation lets the current request go out of scope.
+     *
+     * This method should generally not be called directly as the stack
+     * management should be taken care of by the application itself.
+     *
+     * @return Request|null
+     */
+    public function pop()
+    {
+        if (!$this->requests) {
+            return;
+        }
+
+        return array_pop($this->requests);
+    }
+
+    /**
+     * @return Request|null
+     */
+    public function getCurrentRequest()
+    {
+        return end($this->requests) ?: null;
+    }
+
+    /**
+     * Gets the master Request.
+     *
+     * Be warned that making your code aware of the master request
+     * might make it un-compatible with other features of your framework
+     * like ESI support.
+     *
+     * @return Request|null
+     */
+    public function getMasterRequest()
+    {
+        if (!$this->requests) {
+            return;
+        }
+
+        return $this->requests[0];
+    }
+
+    /**
+     * Returns the parent request of the current.
+     *
+     * Be warned that making your code aware of the parent request
+     * might make it un-compatible with other features of your framework
+     * like ESI support.
+     *
+     * If current Request is the master request, it returns null.
+     *
+     * @return Request|null
+     */
+    public function getParentRequest()
+    {
+        $pos = \count($this->requests) - 2;
+
+        if (!isset($this->requests[$pos])) {
+            return;
+        }
+
+        return $this->requests[$pos];
+    }
+}
diff --git a/vendor/symfony/http-foundation/Response.php b/vendor/symfony/http-foundation/Response.php
new file mode 100755
index 0000000..b7d1162
--- /dev/null
+++ b/vendor/symfony/http-foundation/Response.php
@@ -0,0 +1,1308 @@
+<?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\HttpFoundation;
+
+/**
+ * Response represents an HTTP response.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class Response
+{
+    const HTTP_CONTINUE = 100;
+    const HTTP_SWITCHING_PROTOCOLS = 101;
+    const HTTP_PROCESSING = 102;            // RFC2518
+    const HTTP_EARLY_HINTS = 103;           // RFC8297
+    const HTTP_OK = 200;
+    const HTTP_CREATED = 201;
+    const HTTP_ACCEPTED = 202;
+    const HTTP_NON_AUTHORITATIVE_INFORMATION = 203;
+    const HTTP_NO_CONTENT = 204;
+    const HTTP_RESET_CONTENT = 205;
+    const HTTP_PARTIAL_CONTENT = 206;
+    const HTTP_MULTI_STATUS = 207;          // RFC4918
+    const HTTP_ALREADY_REPORTED = 208;      // RFC5842
+    const HTTP_IM_USED = 226;               // RFC3229
+    const HTTP_MULTIPLE_CHOICES = 300;
+    const HTTP_MOVED_PERMANENTLY = 301;
+    const HTTP_FOUND = 302;
+    const HTTP_SEE_OTHER = 303;
+    const HTTP_NOT_MODIFIED = 304;
+    const HTTP_USE_PROXY = 305;
+    const HTTP_RESERVED = 306;
+    const HTTP_TEMPORARY_REDIRECT = 307;
+    const HTTP_PERMANENTLY_REDIRECT = 308;  // RFC7238
+    const HTTP_BAD_REQUEST = 400;
+    const HTTP_UNAUTHORIZED = 401;
+    const HTTP_PAYMENT_REQUIRED = 402;
+    const HTTP_FORBIDDEN = 403;
+    const HTTP_NOT_FOUND = 404;
+    const HTTP_METHOD_NOT_ALLOWED = 405;
+    const HTTP_NOT_ACCEPTABLE = 406;
+    const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
+    const HTTP_REQUEST_TIMEOUT = 408;
+    const HTTP_CONFLICT = 409;
+    const HTTP_GONE = 410;
+    const HTTP_LENGTH_REQUIRED = 411;
+    const HTTP_PRECONDITION_FAILED = 412;
+    const HTTP_REQUEST_ENTITY_TOO_LARGE = 413;
+    const HTTP_REQUEST_URI_TOO_LONG = 414;
+    const HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
+    const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
+    const HTTP_EXPECTATION_FAILED = 417;
+    const HTTP_I_AM_A_TEAPOT = 418;                                               // RFC2324
+    const HTTP_MISDIRECTED_REQUEST = 421;                                         // RFC7540
+    const HTTP_UNPROCESSABLE_ENTITY = 422;                                        // RFC4918
+    const HTTP_LOCKED = 423;                                                      // RFC4918
+    const HTTP_FAILED_DEPENDENCY = 424;                                           // RFC4918
+
+    /**
+     * @deprecated
+     */
+    const HTTP_RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL = 425;   // RFC2817
+    const HTTP_TOO_EARLY = 425;                                                   // RFC-ietf-httpbis-replay-04
+    const HTTP_UPGRADE_REQUIRED = 426;                                            // RFC2817
+    const HTTP_PRECONDITION_REQUIRED = 428;                                       // RFC6585
+    const HTTP_TOO_MANY_REQUESTS = 429;                                           // RFC6585
+    const HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431;                             // RFC6585
+    const HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451;
+    const HTTP_INTERNAL_SERVER_ERROR = 500;
+    const HTTP_NOT_IMPLEMENTED = 501;
+    const HTTP_BAD_GATEWAY = 502;
+    const HTTP_SERVICE_UNAVAILABLE = 503;
+    const HTTP_GATEWAY_TIMEOUT = 504;
+    const HTTP_VERSION_NOT_SUPPORTED = 505;
+    const HTTP_VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL = 506;                        // RFC2295
+    const HTTP_INSUFFICIENT_STORAGE = 507;                                        // RFC4918
+    const HTTP_LOOP_DETECTED = 508;                                               // RFC5842
+    const HTTP_NOT_EXTENDED = 510;                                                // RFC2774
+    const HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511;                             // RFC6585
+
+    /**
+     * @var \Symfony\Component\HttpFoundation\ResponseHeaderBag
+     */
+    public $headers;
+
+    /**
+     * @var string
+     */
+    protected $content;
+
+    /**
+     * @var string
+     */
+    protected $version;
+
+    /**
+     * @var int
+     */
+    protected $statusCode;
+
+    /**
+     * @var string
+     */
+    protected $statusText;
+
+    /**
+     * @var string
+     */
+    protected $charset;
+
+    /**
+     * Status codes translation table.
+     *
+     * The list of codes is complete according to the
+     * {@link http://www.iana.org/assignments/http-status-codes/ Hypertext Transfer Protocol (HTTP) Status Code Registry}
+     * (last updated 2016-03-01).
+     *
+     * Unless otherwise noted, the status code is defined in RFC2616.
+     *
+     * @var array
+     */
+    public static $statusTexts = [
+        100 => 'Continue',
+        101 => 'Switching Protocols',
+        102 => 'Processing',            // RFC2518
+        103 => 'Early Hints',
+        200 => 'OK',
+        201 => 'Created',
+        202 => 'Accepted',
+        203 => 'Non-Authoritative Information',
+        204 => 'No Content',
+        205 => 'Reset Content',
+        206 => 'Partial Content',
+        207 => 'Multi-Status',          // RFC4918
+        208 => 'Already Reported',      // RFC5842
+        226 => 'IM Used',               // RFC3229
+        300 => 'Multiple Choices',
+        301 => 'Moved Permanently',
+        302 => 'Found',
+        303 => 'See Other',
+        304 => 'Not Modified',
+        305 => 'Use Proxy',
+        307 => 'Temporary Redirect',
+        308 => 'Permanent Redirect',    // RFC7238
+        400 => 'Bad Request',
+        401 => 'Unauthorized',
+        402 => 'Payment Required',
+        403 => 'Forbidden',
+        404 => 'Not Found',
+        405 => 'Method Not Allowed',
+        406 => 'Not Acceptable',
+        407 => 'Proxy Authentication Required',
+        408 => 'Request Timeout',
+        409 => 'Conflict',
+        410 => 'Gone',
+        411 => 'Length Required',
+        412 => 'Precondition Failed',
+        413 => 'Payload Too Large',
+        414 => 'URI Too Long',
+        415 => 'Unsupported Media Type',
+        416 => 'Range Not Satisfiable',
+        417 => 'Expectation Failed',
+        418 => 'I\'m a teapot',                                               // RFC2324
+        421 => 'Misdirected Request',                                         // RFC7540
+        422 => 'Unprocessable Entity',                                        // RFC4918
+        423 => 'Locked',                                                      // RFC4918
+        424 => 'Failed Dependency',                                           // RFC4918
+        425 => 'Too Early',                                                   // RFC-ietf-httpbis-replay-04
+        426 => 'Upgrade Required',                                            // RFC2817
+        428 => 'Precondition Required',                                       // RFC6585
+        429 => 'Too Many Requests',                                           // RFC6585
+        431 => 'Request Header Fields Too Large',                             // RFC6585
+        451 => 'Unavailable For Legal Reasons',                               // RFC7725
+        500 => 'Internal Server Error',
+        501 => 'Not Implemented',
+        502 => 'Bad Gateway',
+        503 => 'Service Unavailable',
+        504 => 'Gateway Timeout',
+        505 => 'HTTP Version Not Supported',
+        506 => 'Variant Also Negotiates',                                     // RFC2295
+        507 => 'Insufficient Storage',                                        // RFC4918
+        508 => 'Loop Detected',                                               // RFC5842
+        510 => 'Not Extended',                                                // RFC2774
+        511 => 'Network Authentication Required',                             // RFC6585
+    ];
+
+    /**
+     * @param mixed $content The response content, see setContent()
+     * @param int   $status  The response status code
+     * @param array $headers An array of response headers
+     *
+     * @throws \InvalidArgumentException When the HTTP status code is not valid
+     */
+    public function __construct($content = '', $status = 200, $headers = [])
+    {
+        $this->headers = new ResponseHeaderBag($headers);
+        $this->setContent($content);
+        $this->setStatusCode($status);
+        $this->setProtocolVersion('1.0');
+    }
+
+    /**
+     * Factory method for chainability.
+     *
+     * Example:
+     *
+     *     return Response::create($body, 200)
+     *         ->setSharedMaxAge(300);
+     *
+     * @param mixed $content The response content, see setContent()
+     * @param int   $status  The response status code
+     * @param array $headers An array of response headers
+     *
+     * @return static
+     */
+    public static function create($content = '', $status = 200, $headers = [])
+    {
+        return new static($content, $status, $headers);
+    }
+
+    /**
+     * Returns the Response as an HTTP string.
+     *
+     * The string representation of the Response is the same as the
+     * one that will be sent to the client only if the prepare() method
+     * has been called before.
+     *
+     * @return string The Response as an HTTP string
+     *
+     * @see prepare()
+     */
+    public function __toString()
+    {
+        return
+            sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText)."\r\n".
+            $this->headers."\r\n".
+            $this->getContent();
+    }
+
+    /**
+     * Clones the current Response instance.
+     */
+    public function __clone()
+    {
+        $this->headers = clone $this->headers;
+    }
+
+    /**
+     * Prepares the Response before it is sent to the client.
+     *
+     * This method tweaks the Response to ensure that it is
+     * compliant with RFC 2616. Most of the changes are based on
+     * the Request that is "associated" with this Response.
+     *
+     * @return $this
+     */
+    public function prepare(Request $request)
+    {
+        $headers = $this->headers;
+
+        if ($this->isInformational() || $this->isEmpty()) {
+            $this->setContent(null);
+            $headers->remove('Content-Type');
+            $headers->remove('Content-Length');
+        } else {
+            // Content-type based on the Request
+            if (!$headers->has('Content-Type')) {
+                $format = $request->getRequestFormat();
+                if (null !== $format && $mimeType = $request->getMimeType($format)) {
+                    $headers->set('Content-Type', $mimeType);
+                }
+            }
+
+            // Fix Content-Type
+            $charset = $this->charset ?: 'UTF-8';
+            if (!$headers->has('Content-Type')) {
+                $headers->set('Content-Type', 'text/html; charset='.$charset);
+            } elseif (0 === stripos($headers->get('Content-Type'), 'text/') && false === stripos($headers->get('Content-Type'), 'charset')) {
+                // add the charset
+                $headers->set('Content-Type', $headers->get('Content-Type').'; charset='.$charset);
+            }
+
+            // Fix Content-Length
+            if ($headers->has('Transfer-Encoding')) {
+                $headers->remove('Content-Length');
+            }
+
+            if ($request->isMethod('HEAD')) {
+                // cf. RFC2616 14.13
+                $length = $headers->get('Content-Length');
+                $this->setContent(null);
+                if ($length) {
+                    $headers->set('Content-Length', $length);
+                }
+            }
+        }
+
+        // Fix protocol
+        if ('HTTP/1.0' != $request->server->get('SERVER_PROTOCOL')) {
+            $this->setProtocolVersion('1.1');
+        }
+
+        // Check if we need to send extra expire info headers
+        if ('1.0' == $this->getProtocolVersion() && false !== strpos($headers->get('Cache-Control'), 'no-cache')) {
+            $headers->set('pragma', 'no-cache');
+            $headers->set('expires', -1);
+        }
+
+        $this->ensureIEOverSSLCompatibility($request);
+
+        return $this;
+    }
+
+    /**
+     * Sends HTTP headers.
+     *
+     * @return $this
+     */
+    public function sendHeaders()
+    {
+        // headers have already been sent by the developer
+        if (headers_sent()) {
+            return $this;
+        }
+
+        // headers
+        foreach ($this->headers->allPreserveCaseWithoutCookies() as $name => $values) {
+            $replace = 0 === strcasecmp($name, 'Content-Type');
+            foreach ($values as $value) {
+                header($name.': '.$value, $replace, $this->statusCode);
+            }
+        }
+
+        // cookies
+        foreach ($this->headers->getCookies() as $cookie) {
+            header('Set-Cookie: '.$cookie->getName().strstr($cookie, '='), false, $this->statusCode);
+        }
+
+        // status
+        header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode);
+
+        return $this;
+    }
+
+    /**
+     * Sends content for the current web response.
+     *
+     * @return $this
+     */
+    public function sendContent()
+    {
+        echo $this->content;
+
+        return $this;
+    }
+
+    /**
+     * Sends HTTP headers and content.
+     *
+     * @return $this
+     */
+    public function send()
+    {
+        $this->sendHeaders();
+        $this->sendContent();
+
+        if (\function_exists('fastcgi_finish_request')) {
+            fastcgi_finish_request();
+        } elseif (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) {
+            static::closeOutputBuffers(0, true);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Sets the response content.
+     *
+     * Valid types are strings, numbers, null, and objects that implement a __toString() method.
+     *
+     * @param mixed $content Content that can be cast to string
+     *
+     * @return $this
+     *
+     * @throws \UnexpectedValueException
+     */
+    public function setContent($content)
+    {
+        if (null !== $content && !\is_string($content) && !is_numeric($content) && !\is_callable([$content, '__toString'])) {
+            throw new \UnexpectedValueException(sprintf('The Response content must be a string or object implementing __toString(), "%s" given.', \gettype($content)));
+        }
+
+        $this->content = (string) $content;
+
+        return $this;
+    }
+
+    /**
+     * Gets the current response content.
+     *
+     * @return string Content
+     */
+    public function getContent()
+    {
+        return $this->content;
+    }
+
+    /**
+     * Sets the HTTP protocol version (1.0 or 1.1).
+     *
+     * @param string $version The HTTP protocol version
+     *
+     * @return $this
+     *
+     * @final since version 3.2
+     */
+    public function setProtocolVersion($version)
+    {
+        $this->version = $version;
+
+        return $this;
+    }
+
+    /**
+     * Gets the HTTP protocol version.
+     *
+     * @return string The HTTP protocol version
+     *
+     * @final since version 3.2
+     */
+    public function getProtocolVersion()
+    {
+        return $this->version;
+    }
+
+    /**
+     * Sets the response status code.
+     *
+     * If the status text is null it will be automatically populated for the known
+     * status codes and left empty otherwise.
+     *
+     * @param int   $code HTTP status code
+     * @param mixed $text HTTP status text
+     *
+     * @return $this
+     *
+     * @throws \InvalidArgumentException When the HTTP status code is not valid
+     *
+     * @final since version 3.2
+     */
+    public function setStatusCode($code, $text = null)
+    {
+        $this->statusCode = $code = (int) $code;
+        if ($this->isInvalid()) {
+            throw new \InvalidArgumentException(sprintf('The HTTP status code "%s" is not valid.', $code));
+        }
+
+        if (null === $text) {
+            $this->statusText = isset(self::$statusTexts[$code]) ? self::$statusTexts[$code] : 'unknown status';
+
+            return $this;
+        }
+
+        if (false === $text) {
+            $this->statusText = '';
+
+            return $this;
+        }
+
+        $this->statusText = $text;
+
+        return $this;
+    }
+
+    /**
+     * Retrieves the status code for the current web response.
+     *
+     * @return int Status code
+     *
+     * @final since version 3.2
+     */
+    public function getStatusCode()
+    {
+        return $this->statusCode;
+    }
+
+    /**
+     * Sets the response charset.
+     *
+     * @param string $charset Character set
+     *
+     * @return $this
+     *
+     * @final since version 3.2
+     */
+    public function setCharset($charset)
+    {
+        $this->charset = $charset;
+
+        return $this;
+    }
+
+    /**
+     * Retrieves the response charset.
+     *
+     * @return string Character set
+     *
+     * @final since version 3.2
+     */
+    public function getCharset()
+    {
+        return $this->charset;
+    }
+
+    /**
+     * Returns true if the response may safely be kept in a shared (surrogate) cache.
+     *
+     * Responses marked "private" with an explicit Cache-Control directive are
+     * considered uncacheable.
+     *
+     * Responses with neither a freshness lifetime (Expires, max-age) nor cache
+     * validator (Last-Modified, ETag) are considered uncacheable because there is
+     * no way to tell when or how to remove them from the cache.
+     *
+     * Note that RFC 7231 and RFC 7234 possibly allow for a more permissive implementation,
+     * for example "status codes that are defined as cacheable by default [...]
+     * can be reused by a cache with heuristic expiration unless otherwise indicated"
+     * (https://tools.ietf.org/html/rfc7231#section-6.1)
+     *
+     * @return bool true if the response is worth caching, false otherwise
+     *
+     * @final since version 3.3
+     */
+    public function isCacheable()
+    {
+        if (!\in_array($this->statusCode, [200, 203, 300, 301, 302, 404, 410])) {
+            return false;
+        }
+
+        if ($this->headers->hasCacheControlDirective('no-store') || $this->headers->getCacheControlDirective('private')) {
+            return false;
+        }
+
+        return $this->isValidateable() || $this->isFresh();
+    }
+
+    /**
+     * Returns true if the response is "fresh".
+     *
+     * Fresh responses may be served from cache without any interaction with the
+     * origin. A response is considered fresh when it includes a Cache-Control/max-age
+     * indicator or Expires header and the calculated age is less than the freshness lifetime.
+     *
+     * @return bool true if the response is fresh, false otherwise
+     *
+     * @final since version 3.3
+     */
+    public function isFresh()
+    {
+        return $this->getTtl() > 0;
+    }
+
+    /**
+     * Returns true if the response includes headers that can be used to validate
+     * the response with the origin server using a conditional GET request.
+     *
+     * @return bool true if the response is validateable, false otherwise
+     *
+     * @final since version 3.3
+     */
+    public function isValidateable()
+    {
+        return $this->headers->has('Last-Modified') || $this->headers->has('ETag');
+    }
+
+    /**
+     * Marks the response as "private".
+     *
+     * It makes the response ineligible for serving other clients.
+     *
+     * @return $this
+     *
+     * @final since version 3.2
+     */
+    public function setPrivate()
+    {
+        $this->headers->removeCacheControlDirective('public');
+        $this->headers->addCacheControlDirective('private');
+
+        return $this;
+    }
+
+    /**
+     * Marks the response as "public".
+     *
+     * It makes the response eligible for serving other clients.
+     *
+     * @return $this
+     *
+     * @final since version 3.2
+     */
+    public function setPublic()
+    {
+        $this->headers->addCacheControlDirective('public');
+        $this->headers->removeCacheControlDirective('private');
+
+        return $this;
+    }
+
+    /**
+     * Marks the response as "immutable".
+     *
+     * @param bool $immutable enables or disables the immutable directive
+     *
+     * @return $this
+     *
+     * @final
+     */
+    public function setImmutable($immutable = true)
+    {
+        if ($immutable) {
+            $this->headers->addCacheControlDirective('immutable');
+        } else {
+            $this->headers->removeCacheControlDirective('immutable');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Returns true if the response is marked as "immutable".
+     *
+     * @return bool returns true if the response is marked as "immutable"; otherwise false
+     *
+     * @final
+     */
+    public function isImmutable()
+    {
+        return $this->headers->hasCacheControlDirective('immutable');
+    }
+
+    /**
+     * Returns true if the response must be revalidated by caches.
+     *
+     * This method indicates that the response must not be served stale by a
+     * cache in any circumstance without first revalidating with the origin.
+     * When present, the TTL of the response should not be overridden to be
+     * greater than the value provided by the origin.
+     *
+     * @return bool true if the response must be revalidated by a cache, false otherwise
+     *
+     * @final since version 3.3
+     */
+    public function mustRevalidate()
+    {
+        return $this->headers->hasCacheControlDirective('must-revalidate') || $this->headers->hasCacheControlDirective('proxy-revalidate');
+    }
+
+    /**
+     * Returns the Date header as a DateTime instance.
+     *
+     * @return \DateTime A \DateTime instance
+     *
+     * @throws \RuntimeException When the header is not parseable
+     *
+     * @final since version 3.2
+     */
+    public function getDate()
+    {
+        return $this->headers->getDate('Date');
+    }
+
+    /**
+     * Sets the Date header.
+     *
+     * @return $this
+     *
+     * @final since version 3.2
+     */
+    public function setDate(\DateTime $date)
+    {
+        $date->setTimezone(new \DateTimeZone('UTC'));
+        $this->headers->set('Date', $date->format('D, d M Y H:i:s').' GMT');
+
+        return $this;
+    }
+
+    /**
+     * Returns the age of the response.
+     *
+     * @return int The age of the response in seconds
+     *
+     * @final since version 3.2
+     */
+    public function getAge()
+    {
+        if (null !== $age = $this->headers->get('Age')) {
+            return (int) $age;
+        }
+
+        return max(time() - $this->getDate()->format('U'), 0);
+    }
+
+    /**
+     * Marks the response stale by setting the Age header to be equal to the maximum age of the response.
+     *
+     * @return $this
+     */
+    public function expire()
+    {
+        if ($this->isFresh()) {
+            $this->headers->set('Age', $this->getMaxAge());
+            $this->headers->remove('Expires');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Returns the value of the Expires header as a DateTime instance.
+     *
+     * @return \DateTime|null A DateTime instance or null if the header does not exist
+     *
+     * @final since version 3.2
+     */
+    public function getExpires()
+    {
+        try {
+            return $this->headers->getDate('Expires');
+        } catch (\RuntimeException $e) {
+            // according to RFC 2616 invalid date formats (e.g. "0" and "-1") must be treated as in the past
+            return \DateTime::createFromFormat(DATE_RFC2822, 'Sat, 01 Jan 00 00:00:00 +0000');
+        }
+    }
+
+    /**
+     * Sets the Expires HTTP header with a DateTime instance.
+     *
+     * Passing null as value will remove the header.
+     *
+     * @param \DateTime|null $date A \DateTime instance or null to remove the header
+     *
+     * @return $this
+     *
+     * @final since version 3.2
+     */
+    public function setExpires(\DateTime $date = null)
+    {
+        if (null === $date) {
+            $this->headers->remove('Expires');
+        } else {
+            $date = clone $date;
+            $date->setTimezone(new \DateTimeZone('UTC'));
+            $this->headers->set('Expires', $date->format('D, d M Y H:i:s').' GMT');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Returns the number of seconds after the time specified in the response's Date
+     * header when the response should no longer be considered fresh.
+     *
+     * First, it checks for a s-maxage directive, then a max-age directive, and then it falls
+     * back on an expires header. It returns null when no maximum age can be established.
+     *
+     * @return int|null Number of seconds
+     *
+     * @final since version 3.2
+     */
+    public function getMaxAge()
+    {
+        if ($this->headers->hasCacheControlDirective('s-maxage')) {
+            return (int) $this->headers->getCacheControlDirective('s-maxage');
+        }
+
+        if ($this->headers->hasCacheControlDirective('max-age')) {
+            return (int) $this->headers->getCacheControlDirective('max-age');
+        }
+
+        if (null !== $this->getExpires()) {
+            return $this->getExpires()->format('U') - $this->getDate()->format('U');
+        }
+    }
+
+    /**
+     * Sets the number of seconds after which the response should no longer be considered fresh.
+     *
+     * This methods sets the Cache-Control max-age directive.
+     *
+     * @param int $value Number of seconds
+     *
+     * @return $this
+     *
+     * @final since version 3.2
+     */
+    public function setMaxAge($value)
+    {
+        $this->headers->addCacheControlDirective('max-age', $value);
+
+        return $this;
+    }
+
+    /**
+     * Sets the number of seconds after which the response should no longer be considered fresh by shared caches.
+     *
+     * This methods sets the Cache-Control s-maxage directive.
+     *
+     * @param int $value Number of seconds
+     *
+     * @return $this
+     *
+     * @final since version 3.2
+     */
+    public function setSharedMaxAge($value)
+    {
+        $this->setPublic();
+        $this->headers->addCacheControlDirective('s-maxage', $value);
+
+        return $this;
+    }
+
+    /**
+     * Returns the response's time-to-live in seconds.
+     *
+     * It returns null when no freshness information is present in the response.
+     *
+     * When the responses TTL is <= 0, the response may not be served from cache without first
+     * revalidating with the origin.
+     *
+     * @return int|null The TTL in seconds
+     *
+     * @final since version 3.2
+     */
+    public function getTtl()
+    {
+        if (null !== $maxAge = $this->getMaxAge()) {
+            return $maxAge - $this->getAge();
+        }
+    }
+
+    /**
+     * Sets the response's time-to-live for shared caches.
+     *
+     * This method adjusts the Cache-Control/s-maxage directive.
+     *
+     * @param int $seconds Number of seconds
+     *
+     * @return $this
+     *
+     * @final since version 3.2
+     */
+    public function setTtl($seconds)
+    {
+        $this->setSharedMaxAge($this->getAge() + $seconds);
+
+        return $this;
+    }
+
+    /**
+     * Sets the response's time-to-live for private/client caches.
+     *
+     * This method adjusts the Cache-Control/max-age directive.
+     *
+     * @param int $seconds Number of seconds
+     *
+     * @return $this
+     *
+     * @final since version 3.2
+     */
+    public function setClientTtl($seconds)
+    {
+        $this->setMaxAge($this->getAge() + $seconds);
+
+        return $this;
+    }
+
+    /**
+     * Returns the Last-Modified HTTP header as a DateTime instance.
+     *
+     * @return \DateTime|null A DateTime instance or null if the header does not exist
+     *
+     * @throws \RuntimeException When the HTTP header is not parseable
+     *
+     * @final since version 3.2
+     */
+    public function getLastModified()
+    {
+        return $this->headers->getDate('Last-Modified');
+    }
+
+    /**
+     * Sets the Last-Modified HTTP header with a DateTime instance.
+     *
+     * Passing null as value will remove the header.
+     *
+     * @param \DateTime|null $date A \DateTime instance or null to remove the header
+     *
+     * @return $this
+     *
+     * @final since version 3.2
+     */
+    public function setLastModified(\DateTime $date = null)
+    {
+        if (null === $date) {
+            $this->headers->remove('Last-Modified');
+        } else {
+            $date = clone $date;
+            $date->setTimezone(new \DateTimeZone('UTC'));
+            $this->headers->set('Last-Modified', $date->format('D, d M Y H:i:s').' GMT');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Returns the literal value of the ETag HTTP header.
+     *
+     * @return string|null The ETag HTTP header or null if it does not exist
+     *
+     * @final since version 3.2
+     */
+    public function getEtag()
+    {
+        return $this->headers->get('ETag');
+    }
+
+    /**
+     * Sets the ETag value.
+     *
+     * @param string|null $etag The ETag unique identifier or null to remove the header
+     * @param bool        $weak Whether you want a weak ETag or not
+     *
+     * @return $this
+     *
+     * @final since version 3.2
+     */
+    public function setEtag($etag = null, $weak = false)
+    {
+        if (null === $etag) {
+            $this->headers->remove('Etag');
+        } else {
+            if (0 !== strpos($etag, '"')) {
+                $etag = '"'.$etag.'"';
+            }
+
+            $this->headers->set('ETag', (true === $weak ? 'W/' : '').$etag);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Sets the response's cache headers (validation and/or expiration).
+     *
+     * Available options are: etag, last_modified, max_age, s_maxage, private, public and immutable.
+     *
+     * @param array $options An array of cache options
+     *
+     * @return $this
+     *
+     * @throws \InvalidArgumentException
+     *
+     * @final since version 3.3
+     */
+    public function setCache(array $options)
+    {
+        if ($diff = array_diff(array_keys($options), ['etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public', 'immutable'])) {
+            throw new \InvalidArgumentException(sprintf('Response does not support the following options: "%s".', implode('", "', $diff)));
+        }
+
+        if (isset($options['etag'])) {
+            $this->setEtag($options['etag']);
+        }
+
+        if (isset($options['last_modified'])) {
+            $this->setLastModified($options['last_modified']);
+        }
+
+        if (isset($options['max_age'])) {
+            $this->setMaxAge($options['max_age']);
+        }
+
+        if (isset($options['s_maxage'])) {
+            $this->setSharedMaxAge($options['s_maxage']);
+        }
+
+        if (isset($options['public'])) {
+            if ($options['public']) {
+                $this->setPublic();
+            } else {
+                $this->setPrivate();
+            }
+        }
+
+        if (isset($options['private'])) {
+            if ($options['private']) {
+                $this->setPrivate();
+            } else {
+                $this->setPublic();
+            }
+        }
+
+        if (isset($options['immutable'])) {
+            $this->setImmutable((bool) $options['immutable']);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Modifies the response so that it conforms to the rules defined for a 304 status code.
+     *
+     * This sets the status, removes the body, and discards any headers
+     * that MUST NOT be included in 304 responses.
+     *
+     * @return $this
+     *
+     * @see http://tools.ietf.org/html/rfc2616#section-10.3.5
+     *
+     * @final since version 3.3
+     */
+    public function setNotModified()
+    {
+        $this->setStatusCode(304);
+        $this->setContent(null);
+
+        // remove headers that MUST NOT be included with 304 Not Modified responses
+        foreach (['Allow', 'Content-Encoding', 'Content-Language', 'Content-Length', 'Content-MD5', 'Content-Type', 'Last-Modified'] as $header) {
+            $this->headers->remove($header);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Returns true if the response includes a Vary header.
+     *
+     * @return bool true if the response includes a Vary header, false otherwise
+     *
+     * @final since version 3.2
+     */
+    public function hasVary()
+    {
+        return null !== $this->headers->get('Vary');
+    }
+
+    /**
+     * Returns an array of header names given in the Vary header.
+     *
+     * @return array An array of Vary names
+     *
+     * @final since version 3.2
+     */
+    public function getVary()
+    {
+        if (!$vary = $this->headers->get('Vary', null, false)) {
+            return [];
+        }
+
+        $ret = [];
+        foreach ($vary as $item) {
+            $ret = array_merge($ret, preg_split('/[\s,]+/', $item));
+        }
+
+        return $ret;
+    }
+
+    /**
+     * Sets the Vary header.
+     *
+     * @param string|array $headers
+     * @param bool         $replace Whether to replace the actual value or not (true by default)
+     *
+     * @return $this
+     *
+     * @final since version 3.2
+     */
+    public function setVary($headers, $replace = true)
+    {
+        $this->headers->set('Vary', $headers, $replace);
+
+        return $this;
+    }
+
+    /**
+     * Determines if the Response validators (ETag, Last-Modified) match
+     * a conditional value specified in the Request.
+     *
+     * If the Response is not modified, it sets the status code to 304 and
+     * removes the actual content by calling the setNotModified() method.
+     *
+     * @return bool true if the Response validators match the Request, false otherwise
+     *
+     * @final since version 3.3
+     */
+    public function isNotModified(Request $request)
+    {
+        if (!$request->isMethodCacheable()) {
+            return false;
+        }
+
+        $notModified = false;
+        $lastModified = $this->headers->get('Last-Modified');
+        $modifiedSince = $request->headers->get('If-Modified-Since');
+
+        if ($etags = $request->getETags()) {
+            $notModified = \in_array($this->getEtag(), $etags) || \in_array('*', $etags);
+        }
+
+        if ($modifiedSince && $lastModified) {
+            $notModified = strtotime($modifiedSince) >= strtotime($lastModified) && (!$etags || $notModified);
+        }
+
+        if ($notModified) {
+            $this->setNotModified();
+        }
+
+        return $notModified;
+    }
+
+    /**
+     * Is response invalid?
+     *
+     * @return bool
+     *
+     * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
+     *
+     * @final since version 3.2
+     */
+    public function isInvalid()
+    {
+        return $this->statusCode < 100 || $this->statusCode >= 600;
+    }
+
+    /**
+     * Is response informative?
+     *
+     * @return bool
+     *
+     * @final since version 3.3
+     */
+    public function isInformational()
+    {
+        return $this->statusCode >= 100 && $this->statusCode < 200;
+    }
+
+    /**
+     * Is response successful?
+     *
+     * @return bool
+     *
+     * @final since version 3.2
+     */
+    public function isSuccessful()
+    {
+        return $this->statusCode >= 200 && $this->statusCode < 300;
+    }
+
+    /**
+     * Is the response a redirect?
+     *
+     * @return bool
+     *
+     * @final since version 3.2
+     */
+    public function isRedirection()
+    {
+        return $this->statusCode >= 300 && $this->statusCode < 400;
+    }
+
+    /**
+     * Is there a client error?
+     *
+     * @return bool
+     *
+     * @final since version 3.2
+     */
+    public function isClientError()
+    {
+        return $this->statusCode >= 400 && $this->statusCode < 500;
+    }
+
+    /**
+     * Was there a server side error?
+     *
+     * @return bool
+     *
+     * @final since version 3.3
+     */
+    public function isServerError()
+    {
+        return $this->statusCode >= 500 && $this->statusCode < 600;
+    }
+
+    /**
+     * Is the response OK?
+     *
+     * @return bool
+     *
+     * @final since version 3.2
+     */
+    public function isOk()
+    {
+        return 200 === $this->statusCode;
+    }
+
+    /**
+     * Is the response forbidden?
+     *
+     * @return bool
+     *
+     * @final since version 3.2
+     */
+    public function isForbidden()
+    {
+        return 403 === $this->statusCode;
+    }
+
+    /**
+     * Is the response a not found error?
+     *
+     * @return bool
+     *
+     * @final since version 3.2
+     */
+    public function isNotFound()
+    {
+        return 404 === $this->statusCode;
+    }
+
+    /**
+     * Is the response a redirect of some form?
+     *
+     * @param string $location
+     *
+     * @return bool
+     *
+     * @final since version 3.2
+     */
+    public function isRedirect($location = null)
+    {
+        return \in_array($this->statusCode, [201, 301, 302, 303, 307, 308]) && (null === $location ?: $location == $this->headers->get('Location'));
+    }
+
+    /**
+     * Is the response empty?
+     *
+     * @return bool
+     *
+     * @final since version 3.2
+     */
+    public function isEmpty()
+    {
+        return \in_array($this->statusCode, [204, 304]);
+    }
+
+    /**
+     * Cleans or flushes output buffers up to target level.
+     *
+     * Resulting level can be greater than target level if a non-removable buffer has been encountered.
+     *
+     * @param int  $targetLevel The target output buffering level
+     * @param bool $flush       Whether to flush or clean the buffers
+     *
+     * @final since version 3.3
+     */
+    public static function closeOutputBuffers($targetLevel, $flush)
+    {
+        $status = ob_get_status(true);
+        $level = \count($status);
+        // PHP_OUTPUT_HANDLER_* are not defined on HHVM 3.3
+        $flags = \defined('PHP_OUTPUT_HANDLER_REMOVABLE') ? PHP_OUTPUT_HANDLER_REMOVABLE | ($flush ? PHP_OUTPUT_HANDLER_FLUSHABLE : PHP_OUTPUT_HANDLER_CLEANABLE) : -1;
+
+        while ($level-- > $targetLevel && ($s = $status[$level]) && (!isset($s['del']) ? !isset($s['flags']) || ($s['flags'] & $flags) === $flags : $s['del'])) {
+            if ($flush) {
+                ob_end_flush();
+            } else {
+                ob_end_clean();
+            }
+        }
+    }
+
+    /**
+     * Checks if we need to remove Cache-Control for SSL encrypted downloads when using IE < 9.
+     *
+     * @see http://support.microsoft.com/kb/323308
+     *
+     * @final since version 3.3
+     */
+    protected function ensureIEOverSSLCompatibility(Request $request)
+    {
+        if (false !== stripos($this->headers->get('Content-Disposition'), 'attachment') && 1 == preg_match('/MSIE (.*?);/i', $request->server->get('HTTP_USER_AGENT'), $match) && true === $request->isSecure()) {
+            if ((int) preg_replace('/(MSIE )(.*?);/', '$2', $match[0]) < 9) {
+                $this->headers->remove('Cache-Control');
+            }
+        }
+    }
+}
diff --git a/vendor/symfony/http-foundation/ResponseHeaderBag.php b/vendor/symfony/http-foundation/ResponseHeaderBag.php
new file mode 100755
index 0000000..1dc8dc2
--- /dev/null
+++ b/vendor/symfony/http-foundation/ResponseHeaderBag.php
@@ -0,0 +1,340 @@
+<?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\HttpFoundation;
+
+/**
+ * ResponseHeaderBag is a container for Response HTTP headers.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class ResponseHeaderBag extends HeaderBag
+{
+    const COOKIES_FLAT = 'flat';
+    const COOKIES_ARRAY = 'array';
+
+    const DISPOSITION_ATTACHMENT = 'attachment';
+    const DISPOSITION_INLINE = 'inline';
+
+    protected $computedCacheControl = [];
+    protected $cookies = [];
+    protected $headerNames = [];
+
+    public function __construct(array $headers = [])
+    {
+        parent::__construct($headers);
+
+        if (!isset($this->headers['cache-control'])) {
+            $this->set('Cache-Control', '');
+        }
+
+        /* RFC2616 - 14.18 says all Responses need to have a Date */
+        if (!isset($this->headers['date'])) {
+            $this->initDate();
+        }
+    }
+
+    /**
+     * Returns the headers, with original capitalizations.
+     *
+     * @return array An array of headers
+     */
+    public function allPreserveCase()
+    {
+        $headers = [];
+        foreach ($this->all() as $name => $value) {
+            $headers[isset($this->headerNames[$name]) ? $this->headerNames[$name] : $name] = $value;
+        }
+
+        return $headers;
+    }
+
+    public function allPreserveCaseWithoutCookies()
+    {
+        $headers = $this->allPreserveCase();
+        if (isset($this->headerNames['set-cookie'])) {
+            unset($headers[$this->headerNames['set-cookie']]);
+        }
+
+        return $headers;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function replace(array $headers = [])
+    {
+        $this->headerNames = [];
+
+        parent::replace($headers);
+
+        if (!isset($this->headers['cache-control'])) {
+            $this->set('Cache-Control', '');
+        }
+
+        if (!isset($this->headers['date'])) {
+            $this->initDate();
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function all()
+    {
+        $headers = parent::all();
+        foreach ($this->getCookies() as $cookie) {
+            $headers['set-cookie'][] = (string) $cookie;
+        }
+
+        return $headers;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function set($key, $values, $replace = true)
+    {
+        $uniqueKey = str_replace('_', '-', strtolower($key));
+
+        if ('set-cookie' === $uniqueKey) {
+            if ($replace) {
+                $this->cookies = [];
+            }
+            foreach ((array) $values as $cookie) {
+                $this->setCookie(Cookie::fromString($cookie));
+            }
+            $this->headerNames[$uniqueKey] = $key;
+
+            return;
+        }
+
+        $this->headerNames[$uniqueKey] = $key;
+
+        parent::set($key, $values, $replace);
+
+        // ensure the cache-control header has sensible defaults
+        if (\in_array($uniqueKey, ['cache-control', 'etag', 'last-modified', 'expires'], true)) {
+            $computed = $this->computeCacheControlValue();
+            $this->headers['cache-control'] = [$computed];
+            $this->headerNames['cache-control'] = 'Cache-Control';
+            $this->computedCacheControl = $this->parseCacheControl($computed);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function remove($key)
+    {
+        $uniqueKey = str_replace('_', '-', strtolower($key));
+        unset($this->headerNames[$uniqueKey]);
+
+        if ('set-cookie' === $uniqueKey) {
+            $this->cookies = [];
+
+            return;
+        }
+
+        parent::remove($key);
+
+        if ('cache-control' === $uniqueKey) {
+            $this->computedCacheControl = [];
+        }
+
+        if ('date' === $uniqueKey) {
+            $this->initDate();
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasCacheControlDirective($key)
+    {
+        return \array_key_exists($key, $this->computedCacheControl);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getCacheControlDirective($key)
+    {
+        return \array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null;
+    }
+
+    public function setCookie(Cookie $cookie)
+    {
+        $this->cookies[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie;
+        $this->headerNames['set-cookie'] = 'Set-Cookie';
+    }
+
+    /**
+     * Removes a cookie from the array, but does not unset it in the browser.
+     *
+     * @param string $name
+     * @param string $path
+     * @param string $domain
+     */
+    public function removeCookie($name, $path = '/', $domain = null)
+    {
+        if (null === $path) {
+            $path = '/';
+        }
+
+        unset($this->cookies[$domain][$path][$name]);
+
+        if (empty($this->cookies[$domain][$path])) {
+            unset($this->cookies[$domain][$path]);
+
+            if (empty($this->cookies[$domain])) {
+                unset($this->cookies[$domain]);
+            }
+        }
+
+        if (empty($this->cookies)) {
+            unset($this->headerNames['set-cookie']);
+        }
+    }
+
+    /**
+     * Returns an array with all cookies.
+     *
+     * @param string $format
+     *
+     * @return Cookie[]
+     *
+     * @throws \InvalidArgumentException When the $format is invalid
+     */
+    public function getCookies($format = self::COOKIES_FLAT)
+    {
+        if (!\in_array($format, [self::COOKIES_FLAT, self::COOKIES_ARRAY])) {
+            throw new \InvalidArgumentException(sprintf('Format "%s" invalid (%s).', $format, implode(', ', [self::COOKIES_FLAT, self::COOKIES_ARRAY])));
+        }
+
+        if (self::COOKIES_ARRAY === $format) {
+            return $this->cookies;
+        }
+
+        $flattenedCookies = [];
+        foreach ($this->cookies as $path) {
+            foreach ($path as $cookies) {
+                foreach ($cookies as $cookie) {
+                    $flattenedCookies[] = $cookie;
+                }
+            }
+        }
+
+        return $flattenedCookies;
+    }
+
+    /**
+     * Clears a cookie in the browser.
+     *
+     * @param string $name
+     * @param string $path
+     * @param string $domain
+     * @param bool   $secure
+     * @param bool   $httpOnly
+     */
+    public function clearCookie($name, $path = '/', $domain = null, $secure = false, $httpOnly = true)
+    {
+        $this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly));
+    }
+
+    /**
+     * Generates a HTTP Content-Disposition field-value.
+     *
+     * @param string $disposition      One of "inline" or "attachment"
+     * @param string $filename         A unicode string
+     * @param string $filenameFallback A string containing only ASCII characters that
+     *                                 is semantically equivalent to $filename. If the filename is already ASCII,
+     *                                 it can be omitted, or just copied from $filename
+     *
+     * @return string A string suitable for use as a Content-Disposition field-value
+     *
+     * @throws \InvalidArgumentException
+     *
+     * @see RFC 6266
+     */
+    public function makeDisposition($disposition, $filename, $filenameFallback = '')
+    {
+        if (!\in_array($disposition, [self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE])) {
+            throw new \InvalidArgumentException(sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE));
+        }
+
+        if ('' == $filenameFallback) {
+            $filenameFallback = $filename;
+        }
+
+        // filenameFallback is not ASCII.
+        if (!preg_match('/^[\x20-\x7e]*$/', $filenameFallback)) {
+            throw new \InvalidArgumentException('The filename fallback must only contain ASCII characters.');
+        }
+
+        // percent characters aren't safe in fallback.
+        if (false !== strpos($filenameFallback, '%')) {
+            throw new \InvalidArgumentException('The filename fallback cannot contain the "%" character.');
+        }
+
+        // path separators aren't allowed in either.
+        if (false !== strpos($filename, '/') || false !== strpos($filename, '\\') || false !== strpos($filenameFallback, '/') || false !== strpos($filenameFallback, '\\')) {
+            throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.');
+        }
+
+        $output = sprintf('%s; filename="%s"', $disposition, str_replace('"', '\\"', $filenameFallback));
+
+        if ($filename !== $filenameFallback) {
+            $output .= sprintf("; filename*=utf-8''%s", rawurlencode($filename));
+        }
+
+        return $output;
+    }
+
+    /**
+     * Returns the calculated value of the cache-control header.
+     *
+     * This considers several other headers and calculates or modifies the
+     * cache-control header to a sensible, conservative value.
+     *
+     * @return string
+     */
+    protected function computeCacheControlValue()
+    {
+        if (!$this->cacheControl && !$this->has('ETag') && !$this->has('Last-Modified') && !$this->has('Expires')) {
+            return 'no-cache, private';
+        }
+
+        if (!$this->cacheControl) {
+            // conservative by default
+            return 'private, must-revalidate';
+        }
+
+        $header = $this->getCacheControlHeader();
+        if (isset($this->cacheControl['public']) || isset($this->cacheControl['private'])) {
+            return $header;
+        }
+
+        // public if s-maxage is defined, private otherwise
+        if (!isset($this->cacheControl['s-maxage'])) {
+            return $header.', private';
+        }
+
+        return $header;
+    }
+
+    private function initDate()
+    {
+        $now = \DateTime::createFromFormat('U', time());
+        $now->setTimezone(new \DateTimeZone('UTC'));
+        $this->set('Date', $now->format('D, d M Y H:i:s').' GMT');
+    }
+}
diff --git a/vendor/symfony/http-foundation/ServerBag.php b/vendor/symfony/http-foundation/ServerBag.php
new file mode 100755
index 0000000..90da49f
--- /dev/null
+++ b/vendor/symfony/http-foundation/ServerBag.php
@@ -0,0 +1,102 @@
+<?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\HttpFoundation;
+
+/**
+ * ServerBag is a container for HTTP headers from the $_SERVER variable.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Bulat Shakirzyanov <mallluhuct@gmail.com>
+ * @author Robert Kiss <kepten@gmail.com>
+ */
+class ServerBag extends ParameterBag
+{
+    /**
+     * Gets the HTTP headers.
+     *
+     * @return array
+     */
+    public function getHeaders()
+    {
+        $headers = [];
+        $contentHeaders = ['CONTENT_LENGTH' => true, 'CONTENT_MD5' => true, 'CONTENT_TYPE' => true];
+        foreach ($this->parameters as $key => $value) {
+            if (0 === strpos($key, 'HTTP_')) {
+                $headers[substr($key, 5)] = $value;
+            }
+            // CONTENT_* are not prefixed with HTTP_
+            elseif (isset($contentHeaders[$key])) {
+                $headers[$key] = $value;
+            }
+        }
+
+        if (isset($this->parameters['PHP_AUTH_USER'])) {
+            $headers['PHP_AUTH_USER'] = $this->parameters['PHP_AUTH_USER'];
+            $headers['PHP_AUTH_PW'] = isset($this->parameters['PHP_AUTH_PW']) ? $this->parameters['PHP_AUTH_PW'] : '';
+        } else {
+            /*
+             * php-cgi under Apache does not pass HTTP Basic user/pass to PHP by default
+             * For this workaround to work, add these lines to your .htaccess file:
+             * RewriteCond %{HTTP:Authorization} ^(.+)$
+             * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
+             *
+             * A sample .htaccess file:
+             * RewriteEngine On
+             * RewriteCond %{HTTP:Authorization} ^(.+)$
+             * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
+             * RewriteCond %{REQUEST_FILENAME} !-f
+             * RewriteRule ^(.*)$ app.php [QSA,L]
+             */
+
+            $authorizationHeader = null;
+            if (isset($this->parameters['HTTP_AUTHORIZATION'])) {
+                $authorizationHeader = $this->parameters['HTTP_AUTHORIZATION'];
+            } elseif (isset($this->parameters['REDIRECT_HTTP_AUTHORIZATION'])) {
+                $authorizationHeader = $this->parameters['REDIRECT_HTTP_AUTHORIZATION'];
+            }
+
+            if (null !== $authorizationHeader) {
+                if (0 === stripos($authorizationHeader, 'basic ')) {
+                    // Decode AUTHORIZATION header into PHP_AUTH_USER and PHP_AUTH_PW when authorization header is basic
+                    $exploded = explode(':', base64_decode(substr($authorizationHeader, 6)), 2);
+                    if (2 == \count($exploded)) {
+                        list($headers['PHP_AUTH_USER'], $headers['PHP_AUTH_PW']) = $exploded;
+                    }
+                } elseif (empty($this->parameters['PHP_AUTH_DIGEST']) && (0 === stripos($authorizationHeader, 'digest '))) {
+                    // In some circumstances PHP_AUTH_DIGEST needs to be set
+                    $headers['PHP_AUTH_DIGEST'] = $authorizationHeader;
+                    $this->parameters['PHP_AUTH_DIGEST'] = $authorizationHeader;
+                } elseif (0 === stripos($authorizationHeader, 'bearer ')) {
+                    /*
+                     * XXX: Since there is no PHP_AUTH_BEARER in PHP predefined variables,
+                     *      I'll just set $headers['AUTHORIZATION'] here.
+                     *      http://php.net/manual/en/reserved.variables.server.php
+                     */
+                    $headers['AUTHORIZATION'] = $authorizationHeader;
+                }
+            }
+        }
+
+        if (isset($headers['AUTHORIZATION'])) {
+            return $headers;
+        }
+
+        // PHP_AUTH_USER/PHP_AUTH_PW
+        if (isset($headers['PHP_AUTH_USER'])) {
+            $headers['AUTHORIZATION'] = 'Basic '.base64_encode($headers['PHP_AUTH_USER'].':'.$headers['PHP_AUTH_PW']);
+        } elseif (isset($headers['PHP_AUTH_DIGEST'])) {
+            $headers['AUTHORIZATION'] = $headers['PHP_AUTH_DIGEST'];
+        }
+
+        return $headers;
+    }
+}
diff --git a/vendor/symfony/http-foundation/Session/Attribute/AttributeBag.php b/vendor/symfony/http-foundation/Session/Attribute/AttributeBag.php
new file mode 100755
index 0000000..07118e8
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/Attribute/AttributeBag.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\HttpFoundation\Session\Attribute;
+
+/**
+ * This class relates to session attribute storage.
+ */
+class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Countable
+{
+    private $name = 'attributes';
+    private $storageKey;
+
+    protected $attributes = [];
+
+    /**
+     * @param string $storageKey The key used to store attributes in the session
+     */
+    public function __construct($storageKey = '_sf2_attributes')
+    {
+        $this->storageKey = $storageKey;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    public function setName($name)
+    {
+        $this->name = $name;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function initialize(array &$attributes)
+    {
+        $this->attributes = &$attributes;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getStorageKey()
+    {
+        return $this->storageKey;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function has($name)
+    {
+        return \array_key_exists($name, $this->attributes);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function get($name, $default = null)
+    {
+        return \array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function set($name, $value)
+    {
+        $this->attributes[$name] = $value;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function all()
+    {
+        return $this->attributes;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function replace(array $attributes)
+    {
+        $this->attributes = [];
+        foreach ($attributes as $key => $value) {
+            $this->set($key, $value);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function remove($name)
+    {
+        $retval = null;
+        if (\array_key_exists($name, $this->attributes)) {
+            $retval = $this->attributes[$name];
+            unset($this->attributes[$name]);
+        }
+
+        return $retval;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function clear()
+    {
+        $return = $this->attributes;
+        $this->attributes = [];
+
+        return $return;
+    }
+
+    /**
+     * Returns an iterator for attributes.
+     *
+     * @return \ArrayIterator An \ArrayIterator instance
+     */
+    public function getIterator()
+    {
+        return new \ArrayIterator($this->attributes);
+    }
+
+    /**
+     * Returns the number of attributes.
+     *
+     * @return int The number of attributes
+     */
+    public function count()
+    {
+        return \count($this->attributes);
+    }
+}
diff --git a/vendor/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php b/vendor/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php
new file mode 100755
index 0000000..0d8d179
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php
@@ -0,0 +1,72 @@
+<?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\HttpFoundation\Session\Attribute;
+
+use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
+
+/**
+ * Attributes store.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+interface AttributeBagInterface extends SessionBagInterface
+{
+    /**
+     * Checks if an attribute is defined.
+     *
+     * @param string $name The attribute name
+     *
+     * @return bool true if the attribute is defined, false otherwise
+     */
+    public function has($name);
+
+    /**
+     * Returns an attribute.
+     *
+     * @param string $name    The attribute name
+     * @param mixed  $default The default value if not found
+     *
+     * @return mixed
+     */
+    public function get($name, $default = null);
+
+    /**
+     * Sets an attribute.
+     *
+     * @param string $name
+     * @param mixed  $value
+     */
+    public function set($name, $value);
+
+    /**
+     * Returns attributes.
+     *
+     * @return array Attributes
+     */
+    public function all();
+
+    /**
+     * Sets attributes.
+     *
+     * @param array $attributes Attributes
+     */
+    public function replace(array $attributes);
+
+    /**
+     * Removes an attribute.
+     *
+     * @param string $name
+     *
+     * @return mixed The removed value or null when it does not exist
+     */
+    public function remove($name);
+}
diff --git a/vendor/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php b/vendor/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php
new file mode 100755
index 0000000..bbf2e39
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php
@@ -0,0 +1,159 @@
+<?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\HttpFoundation\Session\Attribute;
+
+/**
+ * This class provides structured storage of session attributes using
+ * a name spacing character in the key.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class NamespacedAttributeBag extends AttributeBag
+{
+    private $namespaceCharacter;
+
+    /**
+     * @param string $storageKey         Session storage key
+     * @param string $namespaceCharacter Namespace character to use in keys
+     */
+    public function __construct($storageKey = '_sf2_attributes', $namespaceCharacter = '/')
+    {
+        $this->namespaceCharacter = $namespaceCharacter;
+        parent::__construct($storageKey);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function has($name)
+    {
+        // reference mismatch: if fixed, re-introduced in array_key_exists; keep as it is
+        $attributes = $this->resolveAttributePath($name);
+        $name = $this->resolveKey($name);
+
+        if (null === $attributes) {
+            return false;
+        }
+
+        return \array_key_exists($name, $attributes);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function get($name, $default = null)
+    {
+        // reference mismatch: if fixed, re-introduced in array_key_exists; keep as it is
+        $attributes = $this->resolveAttributePath($name);
+        $name = $this->resolveKey($name);
+
+        if (null === $attributes) {
+            return $default;
+        }
+
+        return \array_key_exists($name, $attributes) ? $attributes[$name] : $default;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function set($name, $value)
+    {
+        $attributes = &$this->resolveAttributePath($name, true);
+        $name = $this->resolveKey($name);
+        $attributes[$name] = $value;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function remove($name)
+    {
+        $retval = null;
+        $attributes = &$this->resolveAttributePath($name);
+        $name = $this->resolveKey($name);
+        if (null !== $attributes && \array_key_exists($name, $attributes)) {
+            $retval = $attributes[$name];
+            unset($attributes[$name]);
+        }
+
+        return $retval;
+    }
+
+    /**
+     * Resolves a path in attributes property and returns it as a reference.
+     *
+     * This method allows structured namespacing of session attributes.
+     *
+     * @param string $name         Key name
+     * @param bool   $writeContext Write context, default false
+     *
+     * @return array
+     */
+    protected function &resolveAttributePath($name, $writeContext = false)
+    {
+        $array = &$this->attributes;
+        $name = (0 === strpos($name, $this->namespaceCharacter)) ? substr($name, 1) : $name;
+
+        // Check if there is anything to do, else return
+        if (!$name) {
+            return $array;
+        }
+
+        $parts = explode($this->namespaceCharacter, $name);
+        if (\count($parts) < 2) {
+            if (!$writeContext) {
+                return $array;
+            }
+
+            $array[$parts[0]] = [];
+
+            return $array;
+        }
+
+        unset($parts[\count($parts) - 1]);
+
+        foreach ($parts as $part) {
+            if (null !== $array && !\array_key_exists($part, $array)) {
+                if (!$writeContext) {
+                    $null = null;
+
+                    return $null;
+                }
+
+                $array[$part] = [];
+            }
+
+            $array = &$array[$part];
+        }
+
+        return $array;
+    }
+
+    /**
+     * Resolves the key from the name.
+     *
+     * This is the last part in a dot separated string.
+     *
+     * @param string $name
+     *
+     * @return string
+     */
+    protected function resolveKey($name)
+    {
+        if (false !== $pos = strrpos($name, $this->namespaceCharacter)) {
+            $name = substr($name, $pos + 1);
+        }
+
+        return $name;
+    }
+}
diff --git a/vendor/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php b/vendor/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php
new file mode 100755
index 0000000..451c4a5
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php
@@ -0,0 +1,161 @@
+<?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\HttpFoundation\Session\Flash;
+
+/**
+ * AutoExpireFlashBag flash message container.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class AutoExpireFlashBag implements FlashBagInterface
+{
+    private $name = 'flashes';
+    private $flashes = ['display' => [], 'new' => []];
+    private $storageKey;
+
+    /**
+     * @param string $storageKey The key used to store flashes in the session
+     */
+    public function __construct($storageKey = '_symfony_flashes')
+    {
+        $this->storageKey = $storageKey;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    public function setName($name)
+    {
+        $this->name = $name;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function initialize(array &$flashes)
+    {
+        $this->flashes = &$flashes;
+
+        // The logic: messages from the last request will be stored in new, so we move them to previous
+        // This request we will show what is in 'display'.  What is placed into 'new' this time round will
+        // be moved to display next time round.
+        $this->flashes['display'] = \array_key_exists('new', $this->flashes) ? $this->flashes['new'] : [];
+        $this->flashes['new'] = [];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function add($type, $message)
+    {
+        $this->flashes['new'][$type][] = $message;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function peek($type, array $default = [])
+    {
+        return $this->has($type) ? $this->flashes['display'][$type] : $default;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function peekAll()
+    {
+        return \array_key_exists('display', $this->flashes) ? (array) $this->flashes['display'] : [];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function get($type, array $default = [])
+    {
+        $return = $default;
+
+        if (!$this->has($type)) {
+            return $return;
+        }
+
+        if (isset($this->flashes['display'][$type])) {
+            $return = $this->flashes['display'][$type];
+            unset($this->flashes['display'][$type]);
+        }
+
+        return $return;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function all()
+    {
+        $return = $this->flashes['display'];
+        $this->flashes['display'] = [];
+
+        return $return;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setAll(array $messages)
+    {
+        $this->flashes['new'] = $messages;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function set($type, $messages)
+    {
+        $this->flashes['new'][$type] = (array) $messages;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function has($type)
+    {
+        return \array_key_exists($type, $this->flashes['display']) && $this->flashes['display'][$type];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function keys()
+    {
+        return array_keys($this->flashes['display']);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getStorageKey()
+    {
+        return $this->storageKey;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function clear()
+    {
+        return $this->all();
+    }
+}
diff --git a/vendor/symfony/http-foundation/Session/Flash/FlashBag.php b/vendor/symfony/http-foundation/Session/Flash/FlashBag.php
new file mode 100755
index 0000000..f5d984a
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/Flash/FlashBag.php
@@ -0,0 +1,152 @@
+<?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\HttpFoundation\Session\Flash;
+
+/**
+ * FlashBag flash message container.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class FlashBag implements FlashBagInterface
+{
+    private $name = 'flashes';
+    private $flashes = [];
+    private $storageKey;
+
+    /**
+     * @param string $storageKey The key used to store flashes in the session
+     */
+    public function __construct($storageKey = '_symfony_flashes')
+    {
+        $this->storageKey = $storageKey;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    public function setName($name)
+    {
+        $this->name = $name;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function initialize(array &$flashes)
+    {
+        $this->flashes = &$flashes;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function add($type, $message)
+    {
+        $this->flashes[$type][] = $message;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function peek($type, array $default = [])
+    {
+        return $this->has($type) ? $this->flashes[$type] : $default;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function peekAll()
+    {
+        return $this->flashes;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function get($type, array $default = [])
+    {
+        if (!$this->has($type)) {
+            return $default;
+        }
+
+        $return = $this->flashes[$type];
+
+        unset($this->flashes[$type]);
+
+        return $return;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function all()
+    {
+        $return = $this->peekAll();
+        $this->flashes = [];
+
+        return $return;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function set($type, $messages)
+    {
+        $this->flashes[$type] = (array) $messages;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setAll(array $messages)
+    {
+        $this->flashes = $messages;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function has($type)
+    {
+        return \array_key_exists($type, $this->flashes) && $this->flashes[$type];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function keys()
+    {
+        return array_keys($this->flashes);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getStorageKey()
+    {
+        return $this->storageKey;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function clear()
+    {
+        return $this->all();
+    }
+}
diff --git a/vendor/symfony/http-foundation/Session/Flash/FlashBagInterface.php b/vendor/symfony/http-foundation/Session/Flash/FlashBagInterface.php
new file mode 100755
index 0000000..99e8074
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/Flash/FlashBagInterface.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\HttpFoundation\Session\Flash;
+
+use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
+
+/**
+ * FlashBagInterface.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+interface FlashBagInterface extends SessionBagInterface
+{
+    /**
+     * Adds a flash message for the given type.
+     *
+     * @param string $type
+     * @param mixed  $message
+     */
+    public function add($type, $message);
+
+    /**
+     * Registers one or more messages for a given type.
+     *
+     * @param string       $type
+     * @param string|array $messages
+     */
+    public function set($type, $messages);
+
+    /**
+     * Gets flash messages for a given type.
+     *
+     * @param string $type    Message category type
+     * @param array  $default Default value if $type does not exist
+     *
+     * @return array
+     */
+    public function peek($type, array $default = []);
+
+    /**
+     * Gets all flash messages.
+     *
+     * @return array
+     */
+    public function peekAll();
+
+    /**
+     * Gets and clears flash from the stack.
+     *
+     * @param string $type
+     * @param array  $default Default value if $type does not exist
+     *
+     * @return array
+     */
+    public function get($type, array $default = []);
+
+    /**
+     * Gets and clears flashes from the stack.
+     *
+     * @return array
+     */
+    public function all();
+
+    /**
+     * Sets all flash messages.
+     */
+    public function setAll(array $messages);
+
+    /**
+     * Has flash messages for a given type?
+     *
+     * @param string $type
+     *
+     * @return bool
+     */
+    public function has($type);
+
+    /**
+     * Returns a list of all defined types.
+     *
+     * @return array
+     */
+    public function keys();
+}
diff --git a/vendor/symfony/http-foundation/Session/Session.php b/vendor/symfony/http-foundation/Session/Session.php
new file mode 100755
index 0000000..867ceba
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/Session.php
@@ -0,0 +1,280 @@
+<?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\HttpFoundation\Session;
+
+use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
+use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface;
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
+use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
+use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface;
+
+/**
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Drak <drak@zikula.org>
+ */
+class Session implements SessionInterface, \IteratorAggregate, \Countable
+{
+    protected $storage;
+
+    private $flashName;
+    private $attributeName;
+    private $data = [];
+    private $usageIndex = 0;
+
+    /**
+     * @param SessionStorageInterface $storage    A SessionStorageInterface instance
+     * @param AttributeBagInterface   $attributes An AttributeBagInterface instance, (defaults null for default AttributeBag)
+     * @param FlashBagInterface       $flashes    A FlashBagInterface instance (defaults null for default FlashBag)
+     */
+    public function __construct(SessionStorageInterface $storage = null, AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null)
+    {
+        $this->storage = $storage ?: new NativeSessionStorage();
+
+        $attributes = $attributes ?: new AttributeBag();
+        $this->attributeName = $attributes->getName();
+        $this->registerBag($attributes);
+
+        $flashes = $flashes ?: new FlashBag();
+        $this->flashName = $flashes->getName();
+        $this->registerBag($flashes);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function start()
+    {
+        return $this->storage->start();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function has($name)
+    {
+        return $this->getAttributeBag()->has($name);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function get($name, $default = null)
+    {
+        return $this->getAttributeBag()->get($name, $default);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function set($name, $value)
+    {
+        $this->getAttributeBag()->set($name, $value);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function all()
+    {
+        return $this->getAttributeBag()->all();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function replace(array $attributes)
+    {
+        $this->getAttributeBag()->replace($attributes);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function remove($name)
+    {
+        return $this->getAttributeBag()->remove($name);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function clear()
+    {
+        $this->getAttributeBag()->clear();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isStarted()
+    {
+        return $this->storage->isStarted();
+    }
+
+    /**
+     * Returns an iterator for attributes.
+     *
+     * @return \ArrayIterator An \ArrayIterator instance
+     */
+    public function getIterator()
+    {
+        return new \ArrayIterator($this->getAttributeBag()->all());
+    }
+
+    /**
+     * Returns the number of attributes.
+     *
+     * @return int The number of attributes
+     */
+    public function count()
+    {
+        return \count($this->getAttributeBag()->all());
+    }
+
+    /**
+     * @return int
+     *
+     * @internal
+     */
+    public function getUsageIndex()
+    {
+        return $this->usageIndex;
+    }
+
+    /**
+     * @return bool
+     *
+     * @internal
+     */
+    public function isEmpty()
+    {
+        if ($this->isStarted()) {
+            ++$this->usageIndex;
+        }
+        foreach ($this->data as &$data) {
+            if (!empty($data)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function invalidate($lifetime = null)
+    {
+        $this->storage->clear();
+
+        return $this->migrate(true, $lifetime);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function migrate($destroy = false, $lifetime = null)
+    {
+        return $this->storage->regenerate($destroy, $lifetime);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function save()
+    {
+        $this->storage->save();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return $this->storage->getId();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setId($id)
+    {
+        if ($this->storage->getId() !== $id) {
+            $this->storage->setId($id);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getName()
+    {
+        return $this->storage->getName();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setName($name)
+    {
+        $this->storage->setName($name);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getMetadataBag()
+    {
+        ++$this->usageIndex;
+
+        return $this->storage->getMetadataBag();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function registerBag(SessionBagInterface $bag)
+    {
+        $this->storage->registerBag(new SessionBagProxy($bag, $this->data, $this->usageIndex));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getBag($name)
+    {
+        return $this->storage->getBag($name)->getBag();
+    }
+
+    /**
+     * Gets the flashbag interface.
+     *
+     * @return FlashBagInterface
+     */
+    public function getFlashBag()
+    {
+        return $this->getBag($this->flashName);
+    }
+
+    /**
+     * Gets the attributebag interface.
+     *
+     * Note that this method was added to help with IDE autocompletion.
+     *
+     * @return AttributeBagInterface
+     */
+    private function getAttributeBag()
+    {
+        return $this->getBag($this->attributeName);
+    }
+}
diff --git a/vendor/symfony/http-foundation/Session/SessionBagInterface.php b/vendor/symfony/http-foundation/Session/SessionBagInterface.php
new file mode 100755
index 0000000..8e37d06
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/SessionBagInterface.php
@@ -0,0 +1,46 @@
+<?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\HttpFoundation\Session;
+
+/**
+ * Session Bag store.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+interface SessionBagInterface
+{
+    /**
+     * Gets this bag's name.
+     *
+     * @return string
+     */
+    public function getName();
+
+    /**
+     * Initializes the Bag.
+     */
+    public function initialize(array &$array);
+
+    /**
+     * Gets the storage key for this bag.
+     *
+     * @return string
+     */
+    public function getStorageKey();
+
+    /**
+     * Clears out data from bag.
+     *
+     * @return mixed Whatever data was contained
+     */
+    public function clear();
+}
diff --git a/vendor/symfony/http-foundation/Session/SessionBagProxy.php b/vendor/symfony/http-foundation/Session/SessionBagProxy.php
new file mode 100755
index 0000000..3504bdf
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/SessionBagProxy.php
@@ -0,0 +1,89 @@
+<?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\HttpFoundation\Session;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ *
+ * @internal
+ */
+final class SessionBagProxy implements SessionBagInterface
+{
+    private $bag;
+    private $data;
+    private $usageIndex;
+
+    public function __construct(SessionBagInterface $bag, array &$data, &$usageIndex)
+    {
+        $this->bag = $bag;
+        $this->data = &$data;
+        $this->usageIndex = &$usageIndex;
+    }
+
+    /**
+     * @return SessionBagInterface
+     */
+    public function getBag()
+    {
+        ++$this->usageIndex;
+
+        return $this->bag;
+    }
+
+    /**
+     * @return bool
+     */
+    public function isEmpty()
+    {
+        if (!isset($this->data[$this->bag->getStorageKey()])) {
+            return true;
+        }
+        ++$this->usageIndex;
+
+        return empty($this->data[$this->bag->getStorageKey()]);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getName()
+    {
+        return $this->bag->getName();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function initialize(array &$array)
+    {
+        ++$this->usageIndex;
+        $this->data[$this->bag->getStorageKey()] = &$array;
+
+        $this->bag->initialize($array);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getStorageKey()
+    {
+        return $this->bag->getStorageKey();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function clear()
+    {
+        return $this->bag->clear();
+    }
+}
diff --git a/vendor/symfony/http-foundation/Session/SessionInterface.php b/vendor/symfony/http-foundation/Session/SessionInterface.php
new file mode 100755
index 0000000..95fca85
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/SessionInterface.php
@@ -0,0 +1,180 @@
+<?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\HttpFoundation\Session;
+
+use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag;
+
+/**
+ * Interface for the session.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+interface SessionInterface
+{
+    /**
+     * Starts the session storage.
+     *
+     * @return bool True if session started
+     *
+     * @throws \RuntimeException if session fails to start
+     */
+    public function start();
+
+    /**
+     * Returns the session ID.
+     *
+     * @return string The session ID
+     */
+    public function getId();
+
+    /**
+     * Sets the session ID.
+     *
+     * @param string $id
+     */
+    public function setId($id);
+
+    /**
+     * Returns the session name.
+     *
+     * @return mixed The session name
+     */
+    public function getName();
+
+    /**
+     * Sets the session name.
+     *
+     * @param string $name
+     */
+    public function setName($name);
+
+    /**
+     * Invalidates the current session.
+     *
+     * Clears all session attributes and flashes and regenerates the
+     * session and deletes the old session from persistence.
+     *
+     * @param int $lifetime Sets the cookie lifetime for the session cookie. A null value
+     *                      will leave the system settings unchanged, 0 sets the cookie
+     *                      to expire with browser session. Time is in seconds, and is
+     *                      not a Unix timestamp.
+     *
+     * @return bool True if session invalidated, false if error
+     */
+    public function invalidate($lifetime = null);
+
+    /**
+     * Migrates the current session to a new session id while maintaining all
+     * session attributes.
+     *
+     * @param bool $destroy  Whether to delete the old session or leave it to garbage collection
+     * @param int  $lifetime Sets the cookie lifetime for the session cookie. A null value
+     *                       will leave the system settings unchanged, 0 sets the cookie
+     *                       to expire with browser session. Time is in seconds, and is
+     *                       not a Unix timestamp.
+     *
+     * @return bool True if session migrated, false if error
+     */
+    public function migrate($destroy = false, $lifetime = null);
+
+    /**
+     * Force the session to be saved and closed.
+     *
+     * This method is generally not required for real sessions as
+     * the session will be automatically saved at the end of
+     * code execution.
+     */
+    public function save();
+
+    /**
+     * Checks if an attribute is defined.
+     *
+     * @param string $name The attribute name
+     *
+     * @return bool true if the attribute is defined, false otherwise
+     */
+    public function has($name);
+
+    /**
+     * Returns an attribute.
+     *
+     * @param string $name    The attribute name
+     * @param mixed  $default The default value if not found
+     *
+     * @return mixed
+     */
+    public function get($name, $default = null);
+
+    /**
+     * Sets an attribute.
+     *
+     * @param string $name
+     * @param mixed  $value
+     */
+    public function set($name, $value);
+
+    /**
+     * Returns attributes.
+     *
+     * @return array Attributes
+     */
+    public function all();
+
+    /**
+     * Sets attributes.
+     *
+     * @param array $attributes Attributes
+     */
+    public function replace(array $attributes);
+
+    /**
+     * Removes an attribute.
+     *
+     * @param string $name
+     *
+     * @return mixed The removed value or null when it does not exist
+     */
+    public function remove($name);
+
+    /**
+     * Clears all attributes.
+     */
+    public function clear();
+
+    /**
+     * Checks if the session was started.
+     *
+     * @return bool
+     */
+    public function isStarted();
+
+    /**
+     * Registers a SessionBagInterface with the session.
+     */
+    public function registerBag(SessionBagInterface $bag);
+
+    /**
+     * Gets a bag instance by name.
+     *
+     * @param string $name
+     *
+     * @return SessionBagInterface
+     */
+    public function getBag($name);
+
+    /**
+     * Gets session meta.
+     *
+     * @return MetadataBag
+     */
+    public function getMetadataBag();
+}
diff --git a/vendor/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php
new file mode 100755
index 0000000..eb09c0b
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php
@@ -0,0 +1,168 @@
+<?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\HttpFoundation\Session\Storage\Handler;
+
+/**
+ * This abstract session handler provides a generic implementation
+ * of the PHP 7.0 SessionUpdateTimestampHandlerInterface,
+ * enabling strict and lazy session handling.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+abstract class AbstractSessionHandler implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface
+{
+    private $sessionName;
+    private $prefetchId;
+    private $prefetchData;
+    private $newSessionId;
+    private $igbinaryEmptyData;
+
+    /**
+     * {@inheritdoc}
+     */
+    public function open($savePath, $sessionName)
+    {
+        $this->sessionName = $sessionName;
+        if (!headers_sent() && !ini_get('session.cache_limiter') && '0' !== ini_get('session.cache_limiter')) {
+            header(sprintf('Cache-Control: max-age=%d, private, must-revalidate', 60 * (int) ini_get('session.cache_expire')));
+        }
+
+        return true;
+    }
+
+    /**
+     * @param string $sessionId
+     *
+     * @return string
+     */
+    abstract protected function doRead($sessionId);
+
+    /**
+     * @param string $sessionId
+     * @param string $data
+     *
+     * @return bool
+     */
+    abstract protected function doWrite($sessionId, $data);
+
+    /**
+     * @param string $sessionId
+     *
+     * @return bool
+     */
+    abstract protected function doDestroy($sessionId);
+
+    /**
+     * {@inheritdoc}
+     */
+    public function validateId($sessionId)
+    {
+        $this->prefetchData = $this->read($sessionId);
+        $this->prefetchId = $sessionId;
+
+        return '' !== $this->prefetchData;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function read($sessionId)
+    {
+        if (null !== $this->prefetchId) {
+            $prefetchId = $this->prefetchId;
+            $prefetchData = $this->prefetchData;
+            $this->prefetchId = $this->prefetchData = null;
+
+            if ($prefetchId === $sessionId || '' === $prefetchData) {
+                $this->newSessionId = '' === $prefetchData ? $sessionId : null;
+
+                return $prefetchData;
+            }
+        }
+
+        $data = $this->doRead($sessionId);
+        $this->newSessionId = '' === $data ? $sessionId : null;
+        if (\PHP_VERSION_ID < 70000) {
+            $this->prefetchData = $data;
+        }
+
+        return $data;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function write($sessionId, $data)
+    {
+        if (\PHP_VERSION_ID < 70000 && $this->prefetchData) {
+            $readData = $this->prefetchData;
+            $this->prefetchData = null;
+
+            if ($readData === $data) {
+                return $this->updateTimestamp($sessionId, $data);
+            }
+        }
+        if (null === $this->igbinaryEmptyData) {
+            // see https://github.com/igbinary/igbinary/issues/146
+            $this->igbinaryEmptyData = \function_exists('igbinary_serialize') ? igbinary_serialize([]) : '';
+        }
+        if ('' === $data || $this->igbinaryEmptyData === $data) {
+            return $this->destroy($sessionId);
+        }
+        $this->newSessionId = null;
+
+        return $this->doWrite($sessionId, $data);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function destroy($sessionId)
+    {
+        if (\PHP_VERSION_ID < 70000) {
+            $this->prefetchData = null;
+        }
+        if (!headers_sent() && filter_var(ini_get('session.use_cookies'), FILTER_VALIDATE_BOOLEAN)) {
+            if (!$this->sessionName) {
+                throw new \LogicException(sprintf('Session name cannot be empty, did you forget to call "parent::open()" in "%s"?.', \get_class($this)));
+            }
+            $sessionCookie = sprintf(' %s=', urlencode($this->sessionName));
+            $sessionCookieWithId = sprintf('%s%s;', $sessionCookie, urlencode($sessionId));
+            $sessionCookieFound = false;
+            $otherCookies = [];
+            foreach (headers_list() as $h) {
+                if (0 !== stripos($h, 'Set-Cookie:')) {
+                    continue;
+                }
+                if (11 === strpos($h, $sessionCookie, 11)) {
+                    $sessionCookieFound = true;
+
+                    if (11 !== strpos($h, $sessionCookieWithId, 11)) {
+                        $otherCookies[] = $h;
+                    }
+                } else {
+                    $otherCookies[] = $h;
+                }
+            }
+            if ($sessionCookieFound) {
+                header_remove('Set-Cookie');
+                foreach ($otherCookies as $h) {
+                    header($h, false);
+                }
+            } else {
+                setcookie($this->sessionName, '', 0, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), filter_var(ini_get('session.cookie_secure'), FILTER_VALIDATE_BOOLEAN), filter_var(ini_get('session.cookie_httponly'), FILTER_VALIDATE_BOOLEAN));
+            }
+        }
+
+        return $this->newSessionId === $sessionId || $this->doDestroy($sessionId);
+    }
+}
diff --git a/vendor/symfony/http-foundation/Session/Storage/Handler/MemcacheSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/MemcacheSessionHandler.php
new file mode 100755
index 0000000..3abc33c
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/Storage/Handler/MemcacheSessionHandler.php
@@ -0,0 +1,118 @@
+<?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\HttpFoundation\Session\Storage\Handler;
+
+@trigger_error(sprintf('The class %s is deprecated since Symfony 3.4 and will be removed in 4.0. Use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler instead.', MemcacheSessionHandler::class), E_USER_DEPRECATED);
+
+/**
+ * @author Drak <drak@zikula.org>
+ *
+ * @deprecated since version 3.4, to be removed in 4.0. Use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler instead.
+ */
+class MemcacheSessionHandler implements \SessionHandlerInterface
+{
+    private $memcache;
+
+    /**
+     * @var int Time to live in seconds
+     */
+    private $ttl;
+
+    /**
+     * @var string Key prefix for shared environments
+     */
+    private $prefix;
+
+    /**
+     * Constructor.
+     *
+     * List of available options:
+     *  * prefix: The prefix to use for the memcache keys in order to avoid collision
+     *  * expiretime: The time to live in seconds
+     *
+     * @param \Memcache $memcache A \Memcache instance
+     * @param array     $options  An associative array of Memcache options
+     *
+     * @throws \InvalidArgumentException When unsupported options are passed
+     */
+    public function __construct(\Memcache $memcache, array $options = [])
+    {
+        if ($diff = array_diff(array_keys($options), ['prefix', 'expiretime'])) {
+            throw new \InvalidArgumentException(sprintf('The following options are not supported "%s"', implode(', ', $diff)));
+        }
+
+        $this->memcache = $memcache;
+        $this->ttl = isset($options['expiretime']) ? (int) $options['expiretime'] : 86400;
+        $this->prefix = isset($options['prefix']) ? $options['prefix'] : 'sf2s';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function open($savePath, $sessionName)
+    {
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function close()
+    {
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function read($sessionId)
+    {
+        return $this->memcache->get($this->prefix.$sessionId) ?: '';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function write($sessionId, $data)
+    {
+        return $this->memcache->set($this->prefix.$sessionId, $data, 0, time() + $this->ttl);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function destroy($sessionId)
+    {
+        $this->memcache->delete($this->prefix.$sessionId);
+
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function gc($maxlifetime)
+    {
+        // not required here because memcache will auto expire the records anyhow.
+        return true;
+    }
+
+    /**
+     * Return a Memcache instance.
+     *
+     * @return \Memcache
+     */
+    protected function getMemcache()
+    {
+        return $this->memcache;
+    }
+}
diff --git a/vendor/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php
new file mode 100755
index 0000000..1db590b
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php
@@ -0,0 +1,122 @@
+<?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\HttpFoundation\Session\Storage\Handler;
+
+/**
+ * Memcached based session storage handler based on the Memcached class
+ * provided by the PHP memcached extension.
+ *
+ * @see http://php.net/memcached
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class MemcachedSessionHandler extends AbstractSessionHandler
+{
+    private $memcached;
+
+    /**
+     * @var int Time to live in seconds
+     */
+    private $ttl;
+
+    /**
+     * @var string Key prefix for shared environments
+     */
+    private $prefix;
+
+    /**
+     * Constructor.
+     *
+     * List of available options:
+     *  * prefix: The prefix to use for the memcached keys in order to avoid collision
+     *  * expiretime: The time to live in seconds.
+     *
+     * @param \Memcached $memcached A \Memcached instance
+     * @param array      $options   An associative array of Memcached options
+     *
+     * @throws \InvalidArgumentException When unsupported options are passed
+     */
+    public function __construct(\Memcached $memcached, array $options = [])
+    {
+        $this->memcached = $memcached;
+
+        if ($diff = array_diff(array_keys($options), ['prefix', 'expiretime'])) {
+            throw new \InvalidArgumentException(sprintf('The following options are not supported "%s"', implode(', ', $diff)));
+        }
+
+        $this->ttl = isset($options['expiretime']) ? (int) $options['expiretime'] : 86400;
+        $this->prefix = isset($options['prefix']) ? $options['prefix'] : 'sf2s';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function close()
+    {
+        return $this->memcached->quit();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function doRead($sessionId)
+    {
+        return $this->memcached->get($this->prefix.$sessionId) ?: '';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function updateTimestamp($sessionId, $data)
+    {
+        $this->memcached->touch($this->prefix.$sessionId, time() + $this->ttl);
+
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function doWrite($sessionId, $data)
+    {
+        return $this->memcached->set($this->prefix.$sessionId, $data, time() + $this->ttl);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function doDestroy($sessionId)
+    {
+        $result = $this->memcached->delete($this->prefix.$sessionId);
+
+        return $result || \Memcached::RES_NOTFOUND == $this->memcached->getResultCode();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function gc($maxlifetime)
+    {
+        // not required here because memcached will auto expire the records anyhow.
+        return true;
+    }
+
+    /**
+     * Return a Memcached instance.
+     *
+     * @return \Memcached
+     */
+    protected function getMemcached()
+    {
+        return $this->memcached;
+    }
+}
diff --git a/vendor/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php
new file mode 100755
index 0000000..3b5ccaa
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php
@@ -0,0 +1,255 @@
+<?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\HttpFoundation\Session\Storage\Handler;
+
+/**
+ * Session handler using the mongodb/mongodb package and MongoDB driver extension.
+ *
+ * @author Markus Bachmann <markus.bachmann@bachi.biz>
+ *
+ * @see https://packagist.org/packages/mongodb/mongodb
+ * @see http://php.net/manual/en/set.mongodb.php
+ */
+class MongoDbSessionHandler extends AbstractSessionHandler
+{
+    private $mongo;
+
+    /**
+     * @var \MongoCollection
+     */
+    private $collection;
+
+    /**
+     * @var array
+     */
+    private $options;
+
+    /**
+     * Constructor.
+     *
+     * List of available options:
+     *  * database: The name of the database [required]
+     *  * collection: The name of the collection [required]
+     *  * id_field: The field name for storing the session id [default: _id]
+     *  * data_field: The field name for storing the session data [default: data]
+     *  * time_field: The field name for storing the timestamp [default: time]
+     *  * expiry_field: The field name for storing the expiry-timestamp [default: expires_at].
+     *
+     * It is strongly recommended to put an index on the `expiry_field` for
+     * garbage-collection. Alternatively it's possible to automatically expire
+     * the sessions in the database as described below:
+     *
+     * A TTL collections can be used on MongoDB 2.2+ to cleanup expired sessions
+     * automatically. Such an index can for example look like this:
+     *
+     *     db.<session-collection>.ensureIndex(
+     *         { "<expiry-field>": 1 },
+     *         { "expireAfterSeconds": 0 }
+     *     )
+     *
+     * More details on: http://docs.mongodb.org/manual/tutorial/expire-data/
+     *
+     * If you use such an index, you can drop `gc_probability` to 0 since
+     * no garbage-collection is required.
+     *
+     * @param \MongoDB\Client $mongo   A MongoDB\Client instance
+     * @param array           $options An associative array of field options
+     *
+     * @throws \InvalidArgumentException When MongoClient or Mongo instance not provided
+     * @throws \InvalidArgumentException When "database" or "collection" not provided
+     */
+    public function __construct($mongo, array $options)
+    {
+        if ($mongo instanceof \MongoClient || $mongo instanceof \Mongo) {
+            @trigger_error(sprintf('Using %s with the legacy mongo extension is deprecated as of 3.4 and will be removed in 4.0. Use it with the mongodb/mongodb package and ext-mongodb instead.', __CLASS__), E_USER_DEPRECATED);
+        }
+
+        if (!($mongo instanceof \MongoDB\Client || $mongo instanceof \MongoClient || $mongo instanceof \Mongo)) {
+            throw new \InvalidArgumentException('MongoClient or Mongo instance required');
+        }
+
+        if (!isset($options['database']) || !isset($options['collection'])) {
+            throw new \InvalidArgumentException('You must provide the "database" and "collection" option for MongoDBSessionHandler');
+        }
+
+        $this->mongo = $mongo;
+
+        $this->options = array_merge([
+            'id_field' => '_id',
+            'data_field' => 'data',
+            'time_field' => 'time',
+            'expiry_field' => 'expires_at',
+        ], $options);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function close()
+    {
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function doDestroy($sessionId)
+    {
+        $methodName = $this->mongo instanceof \MongoDB\Client ? 'deleteOne' : 'remove';
+
+        $this->getCollection()->$methodName([
+            $this->options['id_field'] => $sessionId,
+        ]);
+
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function gc($maxlifetime)
+    {
+        $methodName = $this->mongo instanceof \MongoDB\Client ? 'deleteMany' : 'remove';
+
+        $this->getCollection()->$methodName([
+            $this->options['expiry_field'] => ['$lt' => $this->createDateTime()],
+        ]);
+
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function doWrite($sessionId, $data)
+    {
+        $expiry = $this->createDateTime(time() + (int) ini_get('session.gc_maxlifetime'));
+
+        $fields = [
+            $this->options['time_field'] => $this->createDateTime(),
+            $this->options['expiry_field'] => $expiry,
+        ];
+
+        $options = ['upsert' => true];
+
+        if ($this->mongo instanceof \MongoDB\Client) {
+            $fields[$this->options['data_field']] = new \MongoDB\BSON\Binary($data, \MongoDB\BSON\Binary::TYPE_OLD_BINARY);
+        } else {
+            $fields[$this->options['data_field']] = new \MongoBinData($data, \MongoBinData::BYTE_ARRAY);
+            $options['multiple'] = false;
+        }
+
+        $methodName = $this->mongo instanceof \MongoDB\Client ? 'updateOne' : 'update';
+
+        $this->getCollection()->$methodName(
+            [$this->options['id_field'] => $sessionId],
+            ['$set' => $fields],
+            $options
+        );
+
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function updateTimestamp($sessionId, $data)
+    {
+        $expiry = $this->createDateTime(time() + (int) ini_get('session.gc_maxlifetime'));
+
+        if ($this->mongo instanceof \MongoDB\Client) {
+            $methodName = 'updateOne';
+            $options = [];
+        } else {
+            $methodName = 'update';
+            $options = ['multiple' => false];
+        }
+
+        $this->getCollection()->$methodName(
+            [$this->options['id_field'] => $sessionId],
+            ['$set' => [
+                $this->options['time_field'] => $this->createDateTime(),
+                $this->options['expiry_field'] => $expiry,
+            ]],
+            $options
+        );
+
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function doRead($sessionId)
+    {
+        $dbData = $this->getCollection()->findOne([
+            $this->options['id_field'] => $sessionId,
+            $this->options['expiry_field'] => ['$gte' => $this->createDateTime()],
+        ]);
+
+        if (null === $dbData) {
+            return '';
+        }
+
+        if ($dbData[$this->options['data_field']] instanceof \MongoDB\BSON\Binary) {
+            return $dbData[$this->options['data_field']]->getData();
+        }
+
+        return $dbData[$this->options['data_field']]->bin;
+    }
+
+    /**
+     * Return a "MongoCollection" instance.
+     *
+     * @return \MongoCollection
+     */
+    private function getCollection()
+    {
+        if (null === $this->collection) {
+            $this->collection = $this->mongo->selectCollection($this->options['database'], $this->options['collection']);
+        }
+
+        return $this->collection;
+    }
+
+    /**
+     * Return a Mongo instance.
+     *
+     * @return \Mongo|\MongoClient|\MongoDB\Client
+     */
+    protected function getMongo()
+    {
+        return $this->mongo;
+    }
+
+    /**
+     * Create a date object using the class appropriate for the current mongo connection.
+     *
+     * Return an instance of a MongoDate or \MongoDB\BSON\UTCDateTime
+     *
+     * @param int $seconds An integer representing UTC seconds since Jan 1 1970.  Defaults to now.
+     *
+     * @return \MongoDate|\MongoDB\BSON\UTCDateTime
+     */
+    private function createDateTime($seconds = null)
+    {
+        if (null === $seconds) {
+            $seconds = time();
+        }
+
+        if ($this->mongo instanceof \MongoDB\Client) {
+            return new \MongoDB\BSON\UTCDateTime($seconds * 1000);
+        }
+
+        return new \MongoDate($seconds);
+    }
+}
diff --git a/vendor/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php
new file mode 100755
index 0000000..4e9704b
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php
@@ -0,0 +1,55 @@
+<?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\HttpFoundation\Session\Storage\Handler;
+
+/**
+ * Native session handler using PHP's built in file storage.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class NativeFileSessionHandler extends NativeSessionHandler
+{
+    /**
+     * @param string $savePath Path of directory to save session files
+     *                         Default null will leave setting as defined by PHP.
+     *                         '/path', 'N;/path', or 'N;octal-mode;/path
+     *
+     * @see http://php.net/session.configuration.php#ini.session.save-path for further details.
+     *
+     * @throws \InvalidArgumentException On invalid $savePath
+     * @throws \RuntimeException         When failing to create the save directory
+     */
+    public function __construct($savePath = null)
+    {
+        if (null === $savePath) {
+            $savePath = ini_get('session.save_path');
+        }
+
+        $baseDir = $savePath;
+
+        if ($count = substr_count($savePath, ';')) {
+            if ($count > 2) {
+                throw new \InvalidArgumentException(sprintf('Invalid argument $savePath \'%s\'', $savePath));
+            }
+
+            // characters after last ';' are the path
+            $baseDir = ltrim(strrchr($savePath, ';'), ';');
+        }
+
+        if ($baseDir && !is_dir($baseDir) && !@mkdir($baseDir, 0777, true) && !is_dir($baseDir)) {
+            throw new \RuntimeException(sprintf('Session Storage was not able to create directory "%s"', $baseDir));
+        }
+
+        ini_set('session.save_path', $savePath);
+        ini_set('session.save_handler', 'files');
+    }
+}
diff --git a/vendor/symfony/http-foundation/Session/Storage/Handler/NativeSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/NativeSessionHandler.php
new file mode 100755
index 0000000..9be4528
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/Storage/Handler/NativeSessionHandler.php
@@ -0,0 +1,24 @@
+<?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\HttpFoundation\Session\Storage\Handler;
+
+/**
+ * @deprecated since version 3.4, to be removed in 4.0. Use \SessionHandler instead.
+ * @see http://php.net/sessionhandler
+ */
+class NativeSessionHandler extends \SessionHandler
+{
+    public function __construct()
+    {
+        @trigger_error('The '.__NAMESPACE__.'\NativeSessionHandler class is deprecated since Symfony 3.4 and will be removed in 4.0. Use the \SessionHandler class instead.', E_USER_DEPRECATED);
+    }
+}
diff --git a/vendor/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php
new file mode 100755
index 0000000..8d19315
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php
@@ -0,0 +1,76 @@
+<?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\HttpFoundation\Session\Storage\Handler;
+
+/**
+ * Can be used in unit testing or in a situations where persisted sessions are not desired.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class NullSessionHandler extends AbstractSessionHandler
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function close()
+    {
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function validateId($sessionId)
+    {
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function doRead($sessionId)
+    {
+        return '';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function updateTimestamp($sessionId, $data)
+    {
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function doWrite($sessionId, $data)
+    {
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function doDestroy($sessionId)
+    {
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function gc($maxlifetime)
+    {
+        return true;
+    }
+}
diff --git a/vendor/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php
new file mode 100755
index 0000000..bc088e7
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php
@@ -0,0 +1,918 @@
+<?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\HttpFoundation\Session\Storage\Handler;
+
+/**
+ * Session handler using a PDO connection to read and write data.
+ *
+ * It works with MySQL, PostgreSQL, Oracle, SQL Server and SQLite and implements
+ * different locking strategies to handle concurrent access to the same session.
+ * Locking is necessary to prevent loss of data due to race conditions and to keep
+ * the session data consistent between read() and write(). With locking, requests
+ * for the same session will wait until the other one finished writing. For this
+ * reason it's best practice to close a session as early as possible to improve
+ * concurrency. PHPs internal files session handler also implements locking.
+ *
+ * Attention: Since SQLite does not support row level locks but locks the whole database,
+ * it means only one session can be accessed at a time. Even different sessions would wait
+ * for another to finish. So saving session in SQLite should only be considered for
+ * development or prototypes.
+ *
+ * Session data is a binary string that can contain non-printable characters like the null byte.
+ * For this reason it must be saved in a binary column in the database like BLOB in MySQL.
+ * Saving it in a character column could corrupt the data. You can use createTable()
+ * to initialize a correctly defined table.
+ *
+ * @see http://php.net/sessionhandlerinterface
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Michael Williams <michael.williams@funsational.com>
+ * @author Tobias Schultze <http://tobion.de>
+ */
+class PdoSessionHandler extends AbstractSessionHandler
+{
+    /**
+     * No locking is done. This means sessions are prone to loss of data due to
+     * race conditions of concurrent requests to the same session. The last session
+     * write will win in this case. It might be useful when you implement your own
+     * logic to deal with this like an optimistic approach.
+     */
+    const LOCK_NONE = 0;
+
+    /**
+     * Creates an application-level lock on a session. The disadvantage is that the
+     * lock is not enforced by the database and thus other, unaware parts of the
+     * application could still concurrently modify the session. The advantage is it
+     * does not require a transaction.
+     * This mode is not available for SQLite and not yet implemented for oci and sqlsrv.
+     */
+    const LOCK_ADVISORY = 1;
+
+    /**
+     * Issues a real row lock. Since it uses a transaction between opening and
+     * closing a session, you have to be careful when you use same database connection
+     * that you also use for your application logic. This mode is the default because
+     * it's the only reliable solution across DBMSs.
+     */
+    const LOCK_TRANSACTIONAL = 2;
+
+    /**
+     * @var \PDO|null PDO instance or null when not connected yet
+     */
+    private $pdo;
+
+    /**
+     * @var string|false|null DSN string or null for session.save_path or false when lazy connection disabled
+     */
+    private $dsn = false;
+
+    /**
+     * @var string Database driver
+     */
+    private $driver;
+
+    /**
+     * @var string Table name
+     */
+    private $table = 'sessions';
+
+    /**
+     * @var string Column for session id
+     */
+    private $idCol = 'sess_id';
+
+    /**
+     * @var string Column for session data
+     */
+    private $dataCol = 'sess_data';
+
+    /**
+     * @var string Column for lifetime
+     */
+    private $lifetimeCol = 'sess_lifetime';
+
+    /**
+     * @var string Column for timestamp
+     */
+    private $timeCol = 'sess_time';
+
+    /**
+     * @var string Username when lazy-connect
+     */
+    private $username = '';
+
+    /**
+     * @var string Password when lazy-connect
+     */
+    private $password = '';
+
+    /**
+     * @var array Connection options when lazy-connect
+     */
+    private $connectionOptions = [];
+
+    /**
+     * @var int The strategy for locking, see constants
+     */
+    private $lockMode = self::LOCK_TRANSACTIONAL;
+
+    /**
+     * It's an array to support multiple reads before closing which is manual, non-standard usage.
+     *
+     * @var \PDOStatement[] An array of statements to release advisory locks
+     */
+    private $unlockStatements = [];
+
+    /**
+     * @var bool True when the current session exists but expired according to session.gc_maxlifetime
+     */
+    private $sessionExpired = false;
+
+    /**
+     * @var bool Whether a transaction is active
+     */
+    private $inTransaction = false;
+
+    /**
+     * @var bool Whether gc() has been called
+     */
+    private $gcCalled = false;
+
+    /**
+     * You can either pass an existing database connection as PDO instance or
+     * pass a DSN string that will be used to lazy-connect to the database
+     * when the session is actually used. Furthermore it's possible to pass null
+     * which will then use the session.save_path ini setting as PDO DSN parameter.
+     *
+     * List of available options:
+     *  * db_table: The name of the table [default: sessions]
+     *  * db_id_col: The column where to store the session id [default: sess_id]
+     *  * db_data_col: The column where to store the session data [default: sess_data]
+     *  * db_lifetime_col: The column where to store the lifetime [default: sess_lifetime]
+     *  * db_time_col: The column where to store the timestamp [default: sess_time]
+     *  * db_username: The username when lazy-connect [default: '']
+     *  * db_password: The password when lazy-connect [default: '']
+     *  * db_connection_options: An array of driver-specific connection options [default: []]
+     *  * lock_mode: The strategy for locking, see constants [default: LOCK_TRANSACTIONAL]
+     *
+     * @param \PDO|string|null $pdoOrDsn A \PDO instance or DSN string or URL string or null
+     * @param array            $options  An associative array of options
+     *
+     * @throws \InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION
+     */
+    public function __construct($pdoOrDsn = null, array $options = [])
+    {
+        if ($pdoOrDsn instanceof \PDO) {
+            if (\PDO::ERRMODE_EXCEPTION !== $pdoOrDsn->getAttribute(\PDO::ATTR_ERRMODE)) {
+                throw new \InvalidArgumentException(sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION))', __CLASS__));
+            }
+
+            $this->pdo = $pdoOrDsn;
+            $this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME);
+        } elseif (\is_string($pdoOrDsn) && false !== strpos($pdoOrDsn, '://')) {
+            $this->dsn = $this->buildDsnFromUrl($pdoOrDsn);
+        } else {
+            $this->dsn = $pdoOrDsn;
+        }
+
+        $this->table = isset($options['db_table']) ? $options['db_table'] : $this->table;
+        $this->idCol = isset($options['db_id_col']) ? $options['db_id_col'] : $this->idCol;
+        $this->dataCol = isset($options['db_data_col']) ? $options['db_data_col'] : $this->dataCol;
+        $this->lifetimeCol = isset($options['db_lifetime_col']) ? $options['db_lifetime_col'] : $this->lifetimeCol;
+        $this->timeCol = isset($options['db_time_col']) ? $options['db_time_col'] : $this->timeCol;
+        $this->username = isset($options['db_username']) ? $options['db_username'] : $this->username;
+        $this->password = isset($options['db_password']) ? $options['db_password'] : $this->password;
+        $this->connectionOptions = isset($options['db_connection_options']) ? $options['db_connection_options'] : $this->connectionOptions;
+        $this->lockMode = isset($options['lock_mode']) ? $options['lock_mode'] : $this->lockMode;
+    }
+
+    /**
+     * Creates the table to store sessions which can be called once for setup.
+     *
+     * Session ID is saved in a column of maximum length 128 because that is enough even
+     * for a 512 bit configured session.hash_function like Whirlpool. Session data is
+     * saved in a BLOB. One could also use a shorter inlined varbinary column
+     * if one was sure the data fits into it.
+     *
+     * @throws \PDOException    When the table already exists
+     * @throws \DomainException When an unsupported PDO driver is used
+     */
+    public function createTable()
+    {
+        // connect if we are not yet
+        $this->getConnection();
+
+        switch ($this->driver) {
+            case 'mysql':
+                // We use varbinary for the ID column because it prevents unwanted conversions:
+                // - character set conversions between server and client
+                // - trailing space removal
+                // - case-insensitivity
+                // - language processing like é == e
+                $sql = "CREATE TABLE $this->table ($this->idCol VARBINARY(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol MEDIUMINT NOT NULL, $this->timeCol INTEGER UNSIGNED NOT NULL) COLLATE utf8_bin, ENGINE = InnoDB";
+                break;
+            case 'sqlite':
+                $sql = "CREATE TABLE $this->table ($this->idCol TEXT NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)";
+                break;
+            case 'pgsql':
+                $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(128) NOT NULL PRIMARY KEY, $this->dataCol BYTEA NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)";
+                break;
+            case 'oci':
+                $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR2(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)";
+                break;
+            case 'sqlsrv':
+                $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(128) NOT NULL PRIMARY KEY, $this->dataCol VARBINARY(MAX) NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)";
+                break;
+            default:
+                throw new \DomainException(sprintf('Creating the session table is currently not implemented for PDO driver "%s".', $this->driver));
+        }
+
+        try {
+            $this->pdo->exec($sql);
+        } catch (\PDOException $e) {
+            $this->rollback();
+
+            throw $e;
+        }
+    }
+
+    /**
+     * Returns true when the current session exists but expired according to session.gc_maxlifetime.
+     *
+     * Can be used to distinguish between a new session and one that expired due to inactivity.
+     *
+     * @return bool Whether current session expired
+     */
+    public function isSessionExpired()
+    {
+        return $this->sessionExpired;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function open($savePath, $sessionName)
+    {
+        $this->sessionExpired = false;
+
+        if (null === $this->pdo) {
+            $this->connect($this->dsn ?: $savePath);
+        }
+
+        return parent::open($savePath, $sessionName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function read($sessionId)
+    {
+        try {
+            return parent::read($sessionId);
+        } catch (\PDOException $e) {
+            $this->rollback();
+
+            throw $e;
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function gc($maxlifetime)
+    {
+        // We delay gc() to close() so that it is executed outside the transactional and blocking read-write process.
+        // This way, pruning expired sessions does not block them from being started while the current session is used.
+        $this->gcCalled = true;
+
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function doDestroy($sessionId)
+    {
+        // delete the record associated with this id
+        $sql = "DELETE FROM $this->table WHERE $this->idCol = :id";
+
+        try {
+            $stmt = $this->pdo->prepare($sql);
+            $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
+            $stmt->execute();
+        } catch (\PDOException $e) {
+            $this->rollback();
+
+            throw $e;
+        }
+
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function doWrite($sessionId, $data)
+    {
+        $maxlifetime = (int) ini_get('session.gc_maxlifetime');
+
+        try {
+            // We use a single MERGE SQL query when supported by the database.
+            $mergeStmt = $this->getMergeStatement($sessionId, $data, $maxlifetime);
+            if (null !== $mergeStmt) {
+                $mergeStmt->execute();
+
+                return true;
+            }
+
+            $updateStmt = $this->getUpdateStatement($sessionId, $data, $maxlifetime);
+            $updateStmt->execute();
+
+            // When MERGE is not supported, like in Postgres < 9.5, we have to use this approach that can result in
+            // duplicate key errors when the same session is written simultaneously (given the LOCK_NONE behavior).
+            // We can just catch such an error and re-execute the update. This is similar to a serializable
+            // transaction with retry logic on serialization failures but without the overhead and without possible
+            // false positives due to longer gap locking.
+            if (!$updateStmt->rowCount()) {
+                try {
+                    $insertStmt = $this->getInsertStatement($sessionId, $data, $maxlifetime);
+                    $insertStmt->execute();
+                } catch (\PDOException $e) {
+                    // Handle integrity violation SQLSTATE 23000 (or a subclass like 23505 in Postgres) for duplicate keys
+                    if (0 === strpos($e->getCode(), '23')) {
+                        $updateStmt->execute();
+                    } else {
+                        throw $e;
+                    }
+                }
+            }
+        } catch (\PDOException $e) {
+            $this->rollback();
+
+            throw $e;
+        }
+
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function updateTimestamp($sessionId, $data)
+    {
+        $maxlifetime = (int) ini_get('session.gc_maxlifetime');
+
+        try {
+            $updateStmt = $this->pdo->prepare(
+                "UPDATE $this->table SET $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id"
+            );
+            $updateStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
+            $updateStmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
+            $updateStmt->bindValue(':time', time(), \PDO::PARAM_INT);
+            $updateStmt->execute();
+        } catch (\PDOException $e) {
+            $this->rollback();
+
+            throw $e;
+        }
+
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function close()
+    {
+        $this->commit();
+
+        while ($unlockStmt = array_shift($this->unlockStatements)) {
+            $unlockStmt->execute();
+        }
+
+        if ($this->gcCalled) {
+            $this->gcCalled = false;
+
+            // delete the session records that have expired
+            if ('mysql' === $this->driver) {
+                $sql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol < :time";
+            } else {
+                $sql = "DELETE FROM $this->table WHERE $this->lifetimeCol < :time - $this->timeCol";
+            }
+
+            $stmt = $this->pdo->prepare($sql);
+            $stmt->bindValue(':time', time(), \PDO::PARAM_INT);
+            $stmt->execute();
+        }
+
+        if (false !== $this->dsn) {
+            $this->pdo = null; // only close lazy-connection
+        }
+
+        return true;
+    }
+
+    /**
+     * Lazy-connects to the database.
+     *
+     * @param string $dsn DSN string
+     */
+    private function connect($dsn)
+    {
+        $this->pdo = new \PDO($dsn, $this->username, $this->password, $this->connectionOptions);
+        $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
+        $this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME);
+    }
+
+    /**
+     * Builds a PDO DSN from a URL-like connection string.
+     *
+     * @param string $dsnOrUrl
+     *
+     * @return string
+     *
+     * @todo implement missing support for oci DSN (which look totally different from other PDO ones)
+     */
+    private function buildDsnFromUrl($dsnOrUrl)
+    {
+        // (pdo_)?sqlite3?:///... => (pdo_)?sqlite3?://localhost/... or else the URL will be invalid
+        $url = preg_replace('#^((?:pdo_)?sqlite3?):///#', '$1://localhost/', $dsnOrUrl);
+
+        $params = parse_url($url);
+
+        if (false === $params) {
+            return $dsnOrUrl; // If the URL is not valid, let's assume it might be a DSN already.
+        }
+
+        $params = array_map('rawurldecode', $params);
+
+        // Override the default username and password. Values passed through options will still win over these in the constructor.
+        if (isset($params['user'])) {
+            $this->username = $params['user'];
+        }
+
+        if (isset($params['pass'])) {
+            $this->password = $params['pass'];
+        }
+
+        if (!isset($params['scheme'])) {
+            throw new \InvalidArgumentException('URLs without scheme are not supported to configure the PdoSessionHandler');
+        }
+
+        $driverAliasMap = [
+            'mssql' => 'sqlsrv',
+            'mysql2' => 'mysql', // Amazon RDS, for some weird reason
+            'postgres' => 'pgsql',
+            'postgresql' => 'pgsql',
+            'sqlite3' => 'sqlite',
+        ];
+
+        $driver = isset($driverAliasMap[$params['scheme']]) ? $driverAliasMap[$params['scheme']] : $params['scheme'];
+
+        // Doctrine DBAL supports passing its internal pdo_* driver names directly too (allowing both dashes and underscores). This allows supporting the same here.
+        if (0 === strpos($driver, 'pdo_') || 0 === strpos($driver, 'pdo-')) {
+            $driver = substr($driver, 4);
+        }
+
+        switch ($driver) {
+            case 'mysql':
+            case 'pgsql':
+                $dsn = $driver.':';
+
+                if (isset($params['host']) && '' !== $params['host']) {
+                    $dsn .= 'host='.$params['host'].';';
+                }
+
+                if (isset($params['port']) && '' !== $params['port']) {
+                    $dsn .= 'port='.$params['port'].';';
+                }
+
+                if (isset($params['path'])) {
+                    $dbName = substr($params['path'], 1); // Remove the leading slash
+                    $dsn .= 'dbname='.$dbName.';';
+                }
+
+                return $dsn;
+
+            case 'sqlite':
+                return 'sqlite:'.substr($params['path'], 1);
+
+            case 'sqlsrv':
+                $dsn = 'sqlsrv:server=';
+
+                if (isset($params['host'])) {
+                    $dsn .= $params['host'];
+                }
+
+                if (isset($params['port']) && '' !== $params['port']) {
+                    $dsn .= ','.$params['port'];
+                }
+
+                if (isset($params['path'])) {
+                    $dbName = substr($params['path'], 1); // Remove the leading slash
+                    $dsn .= ';Database='.$dbName;
+                }
+
+                return $dsn;
+
+            default:
+                throw new \InvalidArgumentException(sprintf('The scheme "%s" is not supported by the PdoSessionHandler URL configuration. Pass a PDO DSN directly.', $params['scheme']));
+        }
+    }
+
+    /**
+     * Helper method to begin a transaction.
+     *
+     * Since SQLite does not support row level locks, we have to acquire a reserved lock
+     * on the database immediately. Because of https://bugs.php.net/42766 we have to create
+     * such a transaction manually which also means we cannot use PDO::commit or
+     * PDO::rollback or PDO::inTransaction for SQLite.
+     *
+     * Also MySQLs default isolation, REPEATABLE READ, causes deadlock for different sessions
+     * due to http://www.mysqlperformanceblog.com/2013/12/12/one-more-innodb-gap-lock-to-avoid/ .
+     * So we change it to READ COMMITTED.
+     */
+    private function beginTransaction()
+    {
+        if (!$this->inTransaction) {
+            if ('sqlite' === $this->driver) {
+                $this->pdo->exec('BEGIN IMMEDIATE TRANSACTION');
+            } else {
+                if ('mysql' === $this->driver) {
+                    $this->pdo->exec('SET TRANSACTION ISOLATION LEVEL READ COMMITTED');
+                }
+                $this->pdo->beginTransaction();
+            }
+            $this->inTransaction = true;
+        }
+    }
+
+    /**
+     * Helper method to commit a transaction.
+     */
+    private function commit()
+    {
+        if ($this->inTransaction) {
+            try {
+                // commit read-write transaction which also releases the lock
+                if ('sqlite' === $this->driver) {
+                    $this->pdo->exec('COMMIT');
+                } else {
+                    $this->pdo->commit();
+                }
+                $this->inTransaction = false;
+            } catch (\PDOException $e) {
+                $this->rollback();
+
+                throw $e;
+            }
+        }
+    }
+
+    /**
+     * Helper method to rollback a transaction.
+     */
+    private function rollback()
+    {
+        // We only need to rollback if we are in a transaction. Otherwise the resulting
+        // error would hide the real problem why rollback was called. We might not be
+        // in a transaction when not using the transactional locking behavior or when
+        // two callbacks (e.g. destroy and write) are invoked that both fail.
+        if ($this->inTransaction) {
+            if ('sqlite' === $this->driver) {
+                $this->pdo->exec('ROLLBACK');
+            } else {
+                $this->pdo->rollBack();
+            }
+            $this->inTransaction = false;
+        }
+    }
+
+    /**
+     * Reads the session data in respect to the different locking strategies.
+     *
+     * We need to make sure we do not return session data that is already considered garbage according
+     * to the session.gc_maxlifetime setting because gc() is called after read() and only sometimes.
+     *
+     * @param string $sessionId Session ID
+     *
+     * @return string The session data
+     */
+    protected function doRead($sessionId)
+    {
+        if (self::LOCK_ADVISORY === $this->lockMode) {
+            $this->unlockStatements[] = $this->doAdvisoryLock($sessionId);
+        }
+
+        $selectSql = $this->getSelectSql();
+        $selectStmt = $this->pdo->prepare($selectSql);
+        $selectStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
+        $insertStmt = null;
+
+        do {
+            $selectStmt->execute();
+            $sessionRows = $selectStmt->fetchAll(\PDO::FETCH_NUM);
+
+            if ($sessionRows) {
+                if ($sessionRows[0][1] + $sessionRows[0][2] < time()) {
+                    $this->sessionExpired = true;
+
+                    return '';
+                }
+
+                return \is_resource($sessionRows[0][0]) ? stream_get_contents($sessionRows[0][0]) : $sessionRows[0][0];
+            }
+
+            if (null !== $insertStmt) {
+                $this->rollback();
+                throw new \RuntimeException('Failed to read session: INSERT reported a duplicate id but next SELECT did not return any data.');
+            }
+
+            if (!filter_var(ini_get('session.use_strict_mode'), FILTER_VALIDATE_BOOLEAN) && self::LOCK_TRANSACTIONAL === $this->lockMode && 'sqlite' !== $this->driver) {
+                // In strict mode, session fixation is not possible: new sessions always start with a unique
+                // random id, so that concurrency is not possible and this code path can be skipped.
+                // Exclusive-reading of non-existent rows does not block, so we need to do an insert to block
+                // until other connections to the session are committed.
+                try {
+                    $insertStmt = $this->getInsertStatement($sessionId, '', 0);
+                    $insertStmt->execute();
+                } catch (\PDOException $e) {
+                    // Catch duplicate key error because other connection created the session already.
+                    // It would only not be the case when the other connection destroyed the session.
+                    if (0 === strpos($e->getCode(), '23')) {
+                        // Retrieve finished session data written by concurrent connection by restarting the loop.
+                        // We have to start a new transaction as a failed query will mark the current transaction as
+                        // aborted in PostgreSQL and disallow further queries within it.
+                        $this->rollback();
+                        $this->beginTransaction();
+                        continue;
+                    }
+
+                    throw $e;
+                }
+            }
+
+            return '';
+        } while (true);
+    }
+
+    /**
+     * Executes an application-level lock on the database.
+     *
+     * @param string $sessionId Session ID
+     *
+     * @return \PDOStatement The statement that needs to be executed later to release the lock
+     *
+     * @throws \DomainException When an unsupported PDO driver is used
+     *
+     * @todo implement missing advisory locks
+     *       - for oci using DBMS_LOCK.REQUEST
+     *       - for sqlsrv using sp_getapplock with LockOwner = Session
+     */
+    private function doAdvisoryLock($sessionId)
+    {
+        switch ($this->driver) {
+            case 'mysql':
+                // MySQL 5.7.5 and later enforces a maximum length on lock names of 64 characters. Previously, no limit was enforced.
+                $lockId = \substr($sessionId, 0, 64);
+                // should we handle the return value? 0 on timeout, null on error
+                // we use a timeout of 50 seconds which is also the default for innodb_lock_wait_timeout
+                $stmt = $this->pdo->prepare('SELECT GET_LOCK(:key, 50)');
+                $stmt->bindValue(':key', $lockId, \PDO::PARAM_STR);
+                $stmt->execute();
+
+                $releaseStmt = $this->pdo->prepare('DO RELEASE_LOCK(:key)');
+                $releaseStmt->bindValue(':key', $lockId, \PDO::PARAM_STR);
+
+                return $releaseStmt;
+            case 'pgsql':
+                // Obtaining an exclusive session level advisory lock requires an integer key.
+                // When session.sid_bits_per_character > 4, the session id can contain non-hex-characters.
+                // So we cannot just use hexdec().
+                if (4 === \PHP_INT_SIZE) {
+                    $sessionInt1 = $this->convertStringToInt($sessionId);
+                    $sessionInt2 = $this->convertStringToInt(substr($sessionId, 4, 4));
+
+                    $stmt = $this->pdo->prepare('SELECT pg_advisory_lock(:key1, :key2)');
+                    $stmt->bindValue(':key1', $sessionInt1, \PDO::PARAM_INT);
+                    $stmt->bindValue(':key2', $sessionInt2, \PDO::PARAM_INT);
+                    $stmt->execute();
+
+                    $releaseStmt = $this->pdo->prepare('SELECT pg_advisory_unlock(:key1, :key2)');
+                    $releaseStmt->bindValue(':key1', $sessionInt1, \PDO::PARAM_INT);
+                    $releaseStmt->bindValue(':key2', $sessionInt2, \PDO::PARAM_INT);
+                } else {
+                    $sessionBigInt = $this->convertStringToInt($sessionId);
+
+                    $stmt = $this->pdo->prepare('SELECT pg_advisory_lock(:key)');
+                    $stmt->bindValue(':key', $sessionBigInt, \PDO::PARAM_INT);
+                    $stmt->execute();
+
+                    $releaseStmt = $this->pdo->prepare('SELECT pg_advisory_unlock(:key)');
+                    $releaseStmt->bindValue(':key', $sessionBigInt, \PDO::PARAM_INT);
+                }
+
+                return $releaseStmt;
+            case 'sqlite':
+                throw new \DomainException('SQLite does not support advisory locks.');
+            default:
+                throw new \DomainException(sprintf('Advisory locks are currently not implemented for PDO driver "%s".', $this->driver));
+        }
+    }
+
+    /**
+     * Encodes the first 4 (when PHP_INT_SIZE == 4) or 8 characters of the string as an integer.
+     *
+     * Keep in mind, PHP integers are signed.
+     *
+     * @param string $string
+     *
+     * @return int
+     */
+    private function convertStringToInt($string)
+    {
+        if (4 === \PHP_INT_SIZE) {
+            return (\ord($string[3]) << 24) + (\ord($string[2]) << 16) + (\ord($string[1]) << 8) + \ord($string[0]);
+        }
+
+        $int1 = (\ord($string[7]) << 24) + (\ord($string[6]) << 16) + (\ord($string[5]) << 8) + \ord($string[4]);
+        $int2 = (\ord($string[3]) << 24) + (\ord($string[2]) << 16) + (\ord($string[1]) << 8) + \ord($string[0]);
+
+        return $int2 + ($int1 << 32);
+    }
+
+    /**
+     * Return a locking or nonlocking SQL query to read session information.
+     *
+     * @return string The SQL string
+     *
+     * @throws \DomainException When an unsupported PDO driver is used
+     */
+    private function getSelectSql()
+    {
+        if (self::LOCK_TRANSACTIONAL === $this->lockMode) {
+            $this->beginTransaction();
+
+            switch ($this->driver) {
+                case 'mysql':
+                case 'oci':
+                case 'pgsql':
+                    return "SELECT $this->dataCol, $this->lifetimeCol, $this->timeCol FROM $this->table WHERE $this->idCol = :id FOR UPDATE";
+                case 'sqlsrv':
+                    return "SELECT $this->dataCol, $this->lifetimeCol, $this->timeCol FROM $this->table WITH (UPDLOCK, ROWLOCK) WHERE $this->idCol = :id";
+                case 'sqlite':
+                    // we already locked when starting transaction
+                    break;
+                default:
+                    throw new \DomainException(sprintf('Transactional locks are currently not implemented for PDO driver "%s".', $this->driver));
+            }
+        }
+
+        return "SELECT $this->dataCol, $this->lifetimeCol, $this->timeCol FROM $this->table WHERE $this->idCol = :id";
+    }
+
+    /**
+     * Returns an insert statement supported by the database for writing session data.
+     *
+     * @param string $sessionId   Session ID
+     * @param string $sessionData Encoded session data
+     * @param int    $maxlifetime session.gc_maxlifetime
+     *
+     * @return \PDOStatement The insert statement
+     */
+    private function getInsertStatement($sessionId, $sessionData, $maxlifetime)
+    {
+        switch ($this->driver) {
+            case 'oci':
+                $data = fopen('php://memory', 'r+');
+                fwrite($data, $sessionData);
+                rewind($data);
+                $sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, EMPTY_BLOB(), :lifetime, :time) RETURNING $this->dataCol into :data";
+                break;
+            default:
+                $data = $sessionData;
+                $sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)";
+                break;
+        }
+
+        $stmt = $this->pdo->prepare($sql);
+        $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
+        $stmt->bindParam(':data', $data, \PDO::PARAM_LOB);
+        $stmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
+        $stmt->bindValue(':time', time(), \PDO::PARAM_INT);
+
+        return $stmt;
+    }
+
+    /**
+     * Returns an update statement supported by the database for writing session data.
+     *
+     * @param string $sessionId   Session ID
+     * @param string $sessionData Encoded session data
+     * @param int    $maxlifetime session.gc_maxlifetime
+     *
+     * @return \PDOStatement The update statement
+     */
+    private function getUpdateStatement($sessionId, $sessionData, $maxlifetime)
+    {
+        switch ($this->driver) {
+            case 'oci':
+                $data = fopen('php://memory', 'r+');
+                fwrite($data, $sessionData);
+                rewind($data);
+                $sql = "UPDATE $this->table SET $this->dataCol = EMPTY_BLOB(), $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id RETURNING $this->dataCol into :data";
+                break;
+            default:
+                $data = $sessionData;
+                $sql = "UPDATE $this->table SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id";
+                break;
+        }
+
+        $stmt = $this->pdo->prepare($sql);
+        $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
+        $stmt->bindParam(':data', $data, \PDO::PARAM_LOB);
+        $stmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
+        $stmt->bindValue(':time', time(), \PDO::PARAM_INT);
+
+        return $stmt;
+    }
+
+    /**
+     * Returns a merge/upsert (i.e. insert or update) statement when supported by the database for writing session data.
+     *
+     * @param string $sessionId   Session ID
+     * @param string $data        Encoded session data
+     * @param int    $maxlifetime session.gc_maxlifetime
+     *
+     * @return \PDOStatement|null The merge statement or null when not supported
+     */
+    private function getMergeStatement($sessionId, $data, $maxlifetime)
+    {
+        switch (true) {
+            case 'mysql' === $this->driver:
+                $mergeSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
+                    "ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->lifetimeCol = VALUES($this->lifetimeCol), $this->timeCol = VALUES($this->timeCol)";
+                break;
+            case 'sqlsrv' === $this->driver && version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '10', '>='):
+                // MERGE is only available since SQL Server 2008 and must be terminated by semicolon
+                // It also requires HOLDLOCK according to http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx
+                $mergeSql = "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = ?) ".
+                    "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (?, ?, ?, ?) ".
+                    "WHEN MATCHED THEN UPDATE SET $this->dataCol = ?, $this->lifetimeCol = ?, $this->timeCol = ?;";
+                break;
+            case 'sqlite' === $this->driver:
+                $mergeSql = "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)";
+                break;
+            case 'pgsql' === $this->driver && version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '9.5', '>='):
+                $mergeSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
+                    "ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol, $this->lifetimeCol, $this->timeCol) = (EXCLUDED.$this->dataCol, EXCLUDED.$this->lifetimeCol, EXCLUDED.$this->timeCol)";
+                break;
+            default:
+                // MERGE is not supported with LOBs: http://www.oracle.com/technetwork/articles/fuecks-lobs-095315.html
+                return null;
+        }
+
+        $mergeStmt = $this->pdo->prepare($mergeSql);
+
+        if ('sqlsrv' === $this->driver) {
+            $mergeStmt->bindParam(1, $sessionId, \PDO::PARAM_STR);
+            $mergeStmt->bindParam(2, $sessionId, \PDO::PARAM_STR);
+            $mergeStmt->bindParam(3, $data, \PDO::PARAM_LOB);
+            $mergeStmt->bindParam(4, $maxlifetime, \PDO::PARAM_INT);
+            $mergeStmt->bindValue(5, time(), \PDO::PARAM_INT);
+            $mergeStmt->bindParam(6, $data, \PDO::PARAM_LOB);
+            $mergeStmt->bindParam(7, $maxlifetime, \PDO::PARAM_INT);
+            $mergeStmt->bindValue(8, time(), \PDO::PARAM_INT);
+        } else {
+            $mergeStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
+            $mergeStmt->bindParam(':data', $data, \PDO::PARAM_LOB);
+            $mergeStmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
+            $mergeStmt->bindValue(':time', time(), \PDO::PARAM_INT);
+        }
+
+        return $mergeStmt;
+    }
+
+    /**
+     * Return a PDO instance.
+     *
+     * @return \PDO
+     */
+    protected function getConnection()
+    {
+        if (null === $this->pdo) {
+            $this->connect($this->dsn ?: ini_get('session.save_path'));
+        }
+
+        return $this->pdo;
+    }
+}
diff --git a/vendor/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php
new file mode 100755
index 0000000..83a1f2c
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php
@@ -0,0 +1,103 @@
+<?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\HttpFoundation\Session\Storage\Handler;
+
+/**
+ * Adds basic `SessionUpdateTimestampHandlerInterface` behaviors to another `SessionHandlerInterface`.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class StrictSessionHandler extends AbstractSessionHandler
+{
+    private $handler;
+    private $doDestroy;
+
+    public function __construct(\SessionHandlerInterface $handler)
+    {
+        if ($handler instanceof \SessionUpdateTimestampHandlerInterface) {
+            throw new \LogicException(sprintf('"%s" is already an instance of "SessionUpdateTimestampHandlerInterface", you cannot wrap it with "%s".', \get_class($handler), self::class));
+        }
+
+        $this->handler = $handler;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function open($savePath, $sessionName)
+    {
+        parent::open($savePath, $sessionName);
+
+        return $this->handler->open($savePath, $sessionName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function doRead($sessionId)
+    {
+        return $this->handler->read($sessionId);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function updateTimestamp($sessionId, $data)
+    {
+        return $this->write($sessionId, $data);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function doWrite($sessionId, $data)
+    {
+        return $this->handler->write($sessionId, $data);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function destroy($sessionId)
+    {
+        $this->doDestroy = true;
+        $destroyed = parent::destroy($sessionId);
+
+        return $this->doDestroy ? $this->doDestroy($sessionId) : $destroyed;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function doDestroy($sessionId)
+    {
+        $this->doDestroy = false;
+
+        return $this->handler->destroy($sessionId);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function close()
+    {
+        return $this->handler->close();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function gc($maxlifetime)
+    {
+        return $this->handler->gc($maxlifetime);
+    }
+}
diff --git a/vendor/symfony/http-foundation/Session/Storage/Handler/WriteCheckSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/WriteCheckSessionHandler.php
new file mode 100755
index 0000000..127e47f
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/Storage/Handler/WriteCheckSessionHandler.php
@@ -0,0 +1,92 @@
+<?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\HttpFoundation\Session\Storage\Handler;
+
+/**
+ * Wraps another SessionHandlerInterface to only write the session when it has been modified.
+ *
+ * @author Adrien Brault <adrien.brault@gmail.com>
+ *
+ * @deprecated since version 3.4, to be removed in 4.0. Implement `SessionUpdateTimestampHandlerInterface` or extend `AbstractSessionHandler` instead.
+ */
+class WriteCheckSessionHandler implements \SessionHandlerInterface
+{
+    private $wrappedSessionHandler;
+
+    /**
+     * @var array sessionId => session
+     */
+    private $readSessions;
+
+    public function __construct(\SessionHandlerInterface $wrappedSessionHandler)
+    {
+        @trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Implement `SessionUpdateTimestampHandlerInterface` or extend `AbstractSessionHandler` instead.', self::class), E_USER_DEPRECATED);
+
+        $this->wrappedSessionHandler = $wrappedSessionHandler;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function close()
+    {
+        return $this->wrappedSessionHandler->close();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function destroy($sessionId)
+    {
+        return $this->wrappedSessionHandler->destroy($sessionId);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function gc($maxlifetime)
+    {
+        return $this->wrappedSessionHandler->gc($maxlifetime);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function open($savePath, $sessionName)
+    {
+        return $this->wrappedSessionHandler->open($savePath, $sessionName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function read($sessionId)
+    {
+        $session = $this->wrappedSessionHandler->read($sessionId);
+
+        $this->readSessions[$sessionId] = $session;
+
+        return $session;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function write($sessionId, $data)
+    {
+        if (isset($this->readSessions[$sessionId]) && $data === $this->readSessions[$sessionId]) {
+            return true;
+        }
+
+        return $this->wrappedSessionHandler->write($sessionId, $data);
+    }
+}
diff --git a/vendor/symfony/http-foundation/Session/Storage/MetadataBag.php b/vendor/symfony/http-foundation/Session/Storage/MetadataBag.php
new file mode 100755
index 0000000..a62f108
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/Storage/MetadataBag.php
@@ -0,0 +1,168 @@
+<?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\HttpFoundation\Session\Storage;
+
+use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
+
+/**
+ * Metadata container.
+ *
+ * Adds metadata to the session.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class MetadataBag implements SessionBagInterface
+{
+    const CREATED = 'c';
+    const UPDATED = 'u';
+    const LIFETIME = 'l';
+
+    /**
+     * @var string
+     */
+    private $name = '__metadata';
+
+    /**
+     * @var string
+     */
+    private $storageKey;
+
+    /**
+     * @var array
+     */
+    protected $meta = [self::CREATED => 0, self::UPDATED => 0, self::LIFETIME => 0];
+
+    /**
+     * Unix timestamp.
+     *
+     * @var int
+     */
+    private $lastUsed;
+
+    /**
+     * @var int
+     */
+    private $updateThreshold;
+
+    /**
+     * @param string $storageKey      The key used to store bag in the session
+     * @param int    $updateThreshold The time to wait between two UPDATED updates
+     */
+    public function __construct($storageKey = '_sf2_meta', $updateThreshold = 0)
+    {
+        $this->storageKey = $storageKey;
+        $this->updateThreshold = $updateThreshold;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function initialize(array &$array)
+    {
+        $this->meta = &$array;
+
+        if (isset($array[self::CREATED])) {
+            $this->lastUsed = $this->meta[self::UPDATED];
+
+            $timeStamp = time();
+            if ($timeStamp - $array[self::UPDATED] >= $this->updateThreshold) {
+                $this->meta[self::UPDATED] = $timeStamp;
+            }
+        } else {
+            $this->stampCreated();
+        }
+    }
+
+    /**
+     * Gets the lifetime that the session cookie was set with.
+     *
+     * @return int
+     */
+    public function getLifetime()
+    {
+        return $this->meta[self::LIFETIME];
+    }
+
+    /**
+     * Stamps a new session's metadata.
+     *
+     * @param int $lifetime Sets the cookie lifetime for the session cookie. A null value
+     *                      will leave the system settings unchanged, 0 sets the cookie
+     *                      to expire with browser session. Time is in seconds, and is
+     *                      not a Unix timestamp.
+     */
+    public function stampNew($lifetime = null)
+    {
+        $this->stampCreated($lifetime);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getStorageKey()
+    {
+        return $this->storageKey;
+    }
+
+    /**
+     * Gets the created timestamp metadata.
+     *
+     * @return int Unix timestamp
+     */
+    public function getCreated()
+    {
+        return $this->meta[self::CREATED];
+    }
+
+    /**
+     * Gets the last used metadata.
+     *
+     * @return int Unix timestamp
+     */
+    public function getLastUsed()
+    {
+        return $this->lastUsed;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function clear()
+    {
+        // nothing to do
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * Sets name.
+     *
+     * @param string $name
+     */
+    public function setName($name)
+    {
+        $this->name = $name;
+    }
+
+    private function stampCreated($lifetime = null)
+    {
+        $timeStamp = time();
+        $this->meta[self::CREATED] = $this->meta[self::UPDATED] = $this->lastUsed = $timeStamp;
+        $this->meta[self::LIFETIME] = (null === $lifetime) ? ini_get('session.cookie_lifetime') : $lifetime;
+    }
+}
diff --git a/vendor/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php b/vendor/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php
new file mode 100755
index 0000000..c1e7523
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php
@@ -0,0 +1,256 @@
+<?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\HttpFoundation\Session\Storage;
+
+use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
+
+/**
+ * MockArraySessionStorage mocks the session for unit tests.
+ *
+ * No PHP session is actually started since a session can be initialized
+ * and shutdown only once per PHP execution cycle.
+ *
+ * When doing functional testing, you should use MockFileSessionStorage instead.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Bulat Shakirzyanov <mallluhuct@gmail.com>
+ * @author Drak <drak@zikula.org>
+ */
+class MockArraySessionStorage implements SessionStorageInterface
+{
+    /**
+     * @var string
+     */
+    protected $id = '';
+
+    /**
+     * @var string
+     */
+    protected $name;
+
+    /**
+     * @var bool
+     */
+    protected $started = false;
+
+    /**
+     * @var bool
+     */
+    protected $closed = false;
+
+    /**
+     * @var array
+     */
+    protected $data = [];
+
+    /**
+     * @var MetadataBag
+     */
+    protected $metadataBag;
+
+    /**
+     * @var array|SessionBagInterface[]
+     */
+    protected $bags = [];
+
+    /**
+     * @param string      $name    Session name
+     * @param MetadataBag $metaBag MetadataBag instance
+     */
+    public function __construct($name = 'MOCKSESSID', MetadataBag $metaBag = null)
+    {
+        $this->name = $name;
+        $this->setMetadataBag($metaBag);
+    }
+
+    public function setSessionData(array $array)
+    {
+        $this->data = $array;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function start()
+    {
+        if ($this->started) {
+            return true;
+        }
+
+        if (empty($this->id)) {
+            $this->id = $this->generateId();
+        }
+
+        $this->loadSession();
+
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function regenerate($destroy = false, $lifetime = null)
+    {
+        if (!$this->started) {
+            $this->start();
+        }
+
+        $this->metadataBag->stampNew($lifetime);
+        $this->id = $this->generateId();
+
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setId($id)
+    {
+        if ($this->started) {
+            throw new \LogicException('Cannot set session ID after the session has started.');
+        }
+
+        $this->id = $id;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setName($name)
+    {
+        $this->name = $name;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function save()
+    {
+        if (!$this->started || $this->closed) {
+            throw new \RuntimeException('Trying to save a session that was not started yet or was already closed');
+        }
+        // nothing to do since we don't persist the session data
+        $this->closed = false;
+        $this->started = false;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function clear()
+    {
+        // clear out the bags
+        foreach ($this->bags as $bag) {
+            $bag->clear();
+        }
+
+        // clear out the session
+        $this->data = [];
+
+        // reconnect the bags to the session
+        $this->loadSession();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function registerBag(SessionBagInterface $bag)
+    {
+        $this->bags[$bag->getName()] = $bag;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getBag($name)
+    {
+        if (!isset($this->bags[$name])) {
+            throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name));
+        }
+
+        if (!$this->started) {
+            $this->start();
+        }
+
+        return $this->bags[$name];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isStarted()
+    {
+        return $this->started;
+    }
+
+    public function setMetadataBag(MetadataBag $bag = null)
+    {
+        if (null === $bag) {
+            $bag = new MetadataBag();
+        }
+
+        $this->metadataBag = $bag;
+    }
+
+    /**
+     * Gets the MetadataBag.
+     *
+     * @return MetadataBag
+     */
+    public function getMetadataBag()
+    {
+        return $this->metadataBag;
+    }
+
+    /**
+     * Generates a session ID.
+     *
+     * This doesn't need to be particularly cryptographically secure since this is just
+     * a mock.
+     *
+     * @return string
+     */
+    protected function generateId()
+    {
+        return hash('sha256', uniqid('ss_mock_', true));
+    }
+
+    protected function loadSession()
+    {
+        $bags = array_merge($this->bags, [$this->metadataBag]);
+
+        foreach ($bags as $bag) {
+            $key = $bag->getStorageKey();
+            $this->data[$key] = isset($this->data[$key]) ? $this->data[$key] : [];
+            $bag->initialize($this->data[$key]);
+        }
+
+        $this->started = true;
+        $this->closed = false;
+    }
+}
diff --git a/vendor/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php b/vendor/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php
new file mode 100755
index 0000000..9bbd1ba
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php
@@ -0,0 +1,152 @@
+<?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\HttpFoundation\Session\Storage;
+
+/**
+ * MockFileSessionStorage is used to mock sessions for
+ * functional testing when done in a single PHP process.
+ *
+ * No PHP session is actually started since a session can be initialized
+ * and shutdown only once per PHP execution cycle and this class does
+ * not pollute any session related globals, including session_*() functions
+ * or session.* PHP ini directives.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class MockFileSessionStorage extends MockArraySessionStorage
+{
+    private $savePath;
+
+    /**
+     * @param string      $savePath Path of directory to save session files
+     * @param string      $name     Session name
+     * @param MetadataBag $metaBag  MetadataBag instance
+     */
+    public function __construct($savePath = null, $name = 'MOCKSESSID', MetadataBag $metaBag = null)
+    {
+        if (null === $savePath) {
+            $savePath = sys_get_temp_dir();
+        }
+
+        if (!is_dir($savePath) && !@mkdir($savePath, 0777, true) && !is_dir($savePath)) {
+            throw new \RuntimeException(sprintf('Session Storage was not able to create directory "%s"', $savePath));
+        }
+
+        $this->savePath = $savePath;
+
+        parent::__construct($name, $metaBag);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function start()
+    {
+        if ($this->started) {
+            return true;
+        }
+
+        if (!$this->id) {
+            $this->id = $this->generateId();
+        }
+
+        $this->read();
+
+        $this->started = true;
+
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function regenerate($destroy = false, $lifetime = null)
+    {
+        if (!$this->started) {
+            $this->start();
+        }
+
+        if ($destroy) {
+            $this->destroy();
+        }
+
+        return parent::regenerate($destroy, $lifetime);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function save()
+    {
+        if (!$this->started) {
+            throw new \RuntimeException('Trying to save a session that was not started yet or was already closed');
+        }
+
+        $data = $this->data;
+
+        foreach ($this->bags as $bag) {
+            if (empty($data[$key = $bag->getStorageKey()])) {
+                unset($data[$key]);
+            }
+        }
+        if ([$key = $this->metadataBag->getStorageKey()] === array_keys($data)) {
+            unset($data[$key]);
+        }
+
+        try {
+            if ($data) {
+                file_put_contents($this->getFilePath(), serialize($data));
+            } else {
+                $this->destroy();
+            }
+        } finally {
+            $this->data = $data;
+        }
+
+        // this is needed for Silex, where the session object is re-used across requests
+        // in functional tests. In Symfony, the container is rebooted, so we don't have
+        // this issue
+        $this->started = false;
+    }
+
+    /**
+     * Deletes a session from persistent storage.
+     * Deliberately leaves session data in memory intact.
+     */
+    private function destroy()
+    {
+        if (is_file($this->getFilePath())) {
+            unlink($this->getFilePath());
+        }
+    }
+
+    /**
+     * Calculate path to file.
+     *
+     * @return string File path
+     */
+    private function getFilePath()
+    {
+        return $this->savePath.'/'.$this->id.'.mocksess';
+    }
+
+    /**
+     * Reads session from storage and loads session.
+     */
+    private function read()
+    {
+        $filePath = $this->getFilePath();
+        $this->data = is_readable($filePath) && is_file($filePath) ? unserialize(file_get_contents($filePath)) : [];
+
+        $this->loadSession();
+    }
+}
diff --git a/vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php b/vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php
new file mode 100755
index 0000000..c4dbe75
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php
@@ -0,0 +1,436 @@
+<?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\HttpFoundation\Session\Storage;
+
+use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler;
+use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
+use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
+
+/**
+ * This provides a base class for session attribute storage.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class NativeSessionStorage implements SessionStorageInterface
+{
+    /**
+     * @var SessionBagInterface[]
+     */
+    protected $bags = [];
+
+    /**
+     * @var bool
+     */
+    protected $started = false;
+
+    /**
+     * @var bool
+     */
+    protected $closed = false;
+
+    /**
+     * @var AbstractProxy|\SessionHandlerInterface
+     */
+    protected $saveHandler;
+
+    /**
+     * @var MetadataBag
+     */
+    protected $metadataBag;
+
+    /**
+     * Depending on how you want the storage driver to behave you probably
+     * want to override this constructor entirely.
+     *
+     * List of options for $options array with their defaults.
+     *
+     * @see http://php.net/session.configuration for options
+     * but we omit 'session.' from the beginning of the keys for convenience.
+     *
+     * ("auto_start", is not supported as it tells PHP to start a session before
+     * PHP starts to execute user-land code. Setting during runtime has no effect).
+     *
+     * cache_limiter, "" (use "0" to prevent headers from being sent entirely).
+     * cache_expire, "0"
+     * cookie_domain, ""
+     * cookie_httponly, ""
+     * cookie_lifetime, "0"
+     * cookie_path, "/"
+     * cookie_secure, ""
+     * entropy_file, ""
+     * entropy_length, "0"
+     * gc_divisor, "100"
+     * gc_maxlifetime, "1440"
+     * gc_probability, "1"
+     * hash_bits_per_character, "4"
+     * hash_function, "0"
+     * lazy_write, "1"
+     * name, "PHPSESSID"
+     * referer_check, ""
+     * serialize_handler, "php"
+     * use_strict_mode, "0"
+     * use_cookies, "1"
+     * use_only_cookies, "1"
+     * use_trans_sid, "0"
+     * upload_progress.enabled, "1"
+     * upload_progress.cleanup, "1"
+     * upload_progress.prefix, "upload_progress_"
+     * upload_progress.name, "PHP_SESSION_UPLOAD_PROGRESS"
+     * upload_progress.freq, "1%"
+     * upload_progress.min-freq, "1"
+     * url_rewriter.tags, "a=href,area=href,frame=src,form=,fieldset="
+     * sid_length, "32"
+     * sid_bits_per_character, "5"
+     * trans_sid_hosts, $_SERVER['HTTP_HOST']
+     * trans_sid_tags, "a=href,area=href,frame=src,form="
+     *
+     * @param array                         $options Session configuration options
+     * @param \SessionHandlerInterface|null $handler
+     * @param MetadataBag                   $metaBag MetadataBag
+     */
+    public function __construct(array $options = [], $handler = null, MetadataBag $metaBag = null)
+    {
+        $options += [
+            'cache_limiter' => '',
+            'cache_expire' => 0,
+            'use_cookies' => 1,
+            'lazy_write' => 1,
+        ];
+
+        session_register_shutdown();
+
+        $this->setMetadataBag($metaBag);
+        $this->setOptions($options);
+        $this->setSaveHandler($handler);
+    }
+
+    /**
+     * Gets the save handler instance.
+     *
+     * @return AbstractProxy|\SessionHandlerInterface
+     */
+    public function getSaveHandler()
+    {
+        return $this->saveHandler;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function start()
+    {
+        if ($this->started) {
+            return true;
+        }
+
+        if (\PHP_SESSION_ACTIVE === session_status()) {
+            throw new \RuntimeException('Failed to start the session: already started by PHP.');
+        }
+
+        if (filter_var(ini_get('session.use_cookies'), FILTER_VALIDATE_BOOLEAN) && headers_sent($file, $line)) {
+            throw new \RuntimeException(sprintf('Failed to start the session because headers have already been sent by "%s" at line %d.', $file, $line));
+        }
+
+        // ok to try and start the session
+        if (!session_start()) {
+            throw new \RuntimeException('Failed to start the session');
+        }
+
+        $this->loadSession();
+
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return $this->saveHandler->getId();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setId($id)
+    {
+        $this->saveHandler->setId($id);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getName()
+    {
+        return $this->saveHandler->getName();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setName($name)
+    {
+        $this->saveHandler->setName($name);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function regenerate($destroy = false, $lifetime = null)
+    {
+        // Cannot regenerate the session ID for non-active sessions.
+        if (\PHP_SESSION_ACTIVE !== session_status()) {
+            return false;
+        }
+
+        if (headers_sent()) {
+            return false;
+        }
+
+        if (null !== $lifetime) {
+            ini_set('session.cookie_lifetime', $lifetime);
+        }
+
+        if ($destroy) {
+            $this->metadataBag->stampNew();
+        }
+
+        $isRegenerated = session_regenerate_id($destroy);
+
+        // The reference to $_SESSION in session bags is lost in PHP7 and we need to re-create it.
+        // @see https://bugs.php.net/bug.php?id=70013
+        $this->loadSession();
+
+        return $isRegenerated;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function save()
+    {
+        $session = $_SESSION;
+
+        foreach ($this->bags as $bag) {
+            if (empty($_SESSION[$key = $bag->getStorageKey()])) {
+                unset($_SESSION[$key]);
+            }
+        }
+        if ([$key = $this->metadataBag->getStorageKey()] === array_keys($_SESSION)) {
+            unset($_SESSION[$key]);
+        }
+
+        // Register error handler to add information about the current save handler
+        $previousHandler = set_error_handler(function ($type, $msg, $file, $line) use (&$previousHandler) {
+            if (E_WARNING === $type && 0 === strpos($msg, 'session_write_close():')) {
+                $handler = $this->saveHandler instanceof SessionHandlerProxy ? $this->saveHandler->getHandler() : $this->saveHandler;
+                $msg = sprintf('session_write_close(): Failed to write session data with "%s" handler', \get_class($handler));
+            }
+
+            return $previousHandler ? $previousHandler($type, $msg, $file, $line) : false;
+        });
+
+        try {
+            session_write_close();
+        } finally {
+            restore_error_handler();
+            $_SESSION = $session;
+        }
+
+        $this->closed = true;
+        $this->started = false;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function clear()
+    {
+        // clear out the bags
+        foreach ($this->bags as $bag) {
+            $bag->clear();
+        }
+
+        // clear out the session
+        $_SESSION = [];
+
+        // reconnect the bags to the session
+        $this->loadSession();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function registerBag(SessionBagInterface $bag)
+    {
+        if ($this->started) {
+            throw new \LogicException('Cannot register a bag when the session is already started.');
+        }
+
+        $this->bags[$bag->getName()] = $bag;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getBag($name)
+    {
+        if (!isset($this->bags[$name])) {
+            throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name));
+        }
+
+        if (!$this->started && $this->saveHandler->isActive()) {
+            $this->loadSession();
+        } elseif (!$this->started) {
+            $this->start();
+        }
+
+        return $this->bags[$name];
+    }
+
+    public function setMetadataBag(MetadataBag $metaBag = null)
+    {
+        if (null === $metaBag) {
+            $metaBag = new MetadataBag();
+        }
+
+        $this->metadataBag = $metaBag;
+    }
+
+    /**
+     * Gets the MetadataBag.
+     *
+     * @return MetadataBag
+     */
+    public function getMetadataBag()
+    {
+        return $this->metadataBag;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isStarted()
+    {
+        return $this->started;
+    }
+
+    /**
+     * Sets session.* ini variables.
+     *
+     * For convenience we omit 'session.' from the beginning of the keys.
+     * Explicitly ignores other ini keys.
+     *
+     * @param array $options Session ini directives [key => value]
+     *
+     * @see http://php.net/session.configuration
+     */
+    public function setOptions(array $options)
+    {
+        if (headers_sent() || \PHP_SESSION_ACTIVE === session_status()) {
+            return;
+        }
+
+        $validOptions = array_flip([
+            'cache_expire', 'cache_limiter', 'cookie_domain', 'cookie_httponly',
+            'cookie_lifetime', 'cookie_path', 'cookie_secure',
+            'entropy_file', 'entropy_length', 'gc_divisor',
+            'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character',
+            'hash_function', 'lazy_write', 'name', 'referer_check',
+            'serialize_handler', 'use_strict_mode', 'use_cookies',
+            'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled',
+            'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name',
+            'upload_progress.freq', 'upload_progress.min_freq', 'url_rewriter.tags',
+            'sid_length', 'sid_bits_per_character', 'trans_sid_hosts', 'trans_sid_tags',
+        ]);
+
+        foreach ($options as $key => $value) {
+            if (isset($validOptions[$key])) {
+                ini_set('url_rewriter.tags' !== $key ? 'session.'.$key : $key, $value);
+            }
+        }
+    }
+
+    /**
+     * Registers session save handler as a PHP session handler.
+     *
+     * To use internal PHP session save handlers, override this method using ini_set with
+     * session.save_handler and session.save_path e.g.
+     *
+     *     ini_set('session.save_handler', 'files');
+     *     ini_set('session.save_path', '/tmp');
+     *
+     * or pass in a \SessionHandler instance which configures session.save_handler in the
+     * constructor, for a template see NativeFileSessionHandler or use handlers in
+     * composer package drak/native-session
+     *
+     * @see http://php.net/session-set-save-handler
+     * @see http://php.net/sessionhandlerinterface
+     * @see http://php.net/sessionhandler
+     * @see http://github.com/drak/NativeSession
+     *
+     * @param \SessionHandlerInterface|null $saveHandler
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function setSaveHandler($saveHandler = null)
+    {
+        if (!$saveHandler instanceof AbstractProxy &&
+            !$saveHandler instanceof \SessionHandlerInterface &&
+            null !== $saveHandler) {
+            throw new \InvalidArgumentException('Must be instance of AbstractProxy; implement \SessionHandlerInterface; or be null.');
+        }
+
+        // Wrap $saveHandler in proxy and prevent double wrapping of proxy
+        if (!$saveHandler instanceof AbstractProxy && $saveHandler instanceof \SessionHandlerInterface) {
+            $saveHandler = new SessionHandlerProxy($saveHandler);
+        } elseif (!$saveHandler instanceof AbstractProxy) {
+            $saveHandler = new SessionHandlerProxy(new StrictSessionHandler(new \SessionHandler()));
+        }
+        $this->saveHandler = $saveHandler;
+
+        if (headers_sent() || \PHP_SESSION_ACTIVE === session_status()) {
+            return;
+        }
+
+        if ($this->saveHandler instanceof SessionHandlerProxy) {
+            session_set_save_handler($this->saveHandler, false);
+        }
+    }
+
+    /**
+     * Load the session with attributes.
+     *
+     * After starting the session, PHP retrieves the session from whatever handlers
+     * are set to (either PHP's internal, or a custom save handler set with session_set_save_handler()).
+     * PHP takes the return value from the read() handler, unserializes it
+     * and populates $_SESSION with the result automatically.
+     */
+    protected function loadSession(array &$session = null)
+    {
+        if (null === $session) {
+            $session = &$_SESSION;
+        }
+
+        $bags = array_merge($this->bags, [$this->metadataBag]);
+
+        foreach ($bags as $bag) {
+            $key = $bag->getStorageKey();
+            $session[$key] = isset($session[$key]) ? $session[$key] : [];
+            $bag->initialize($session[$key]);
+        }
+
+        $this->started = true;
+        $this->closed = false;
+    }
+}
diff --git a/vendor/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php b/vendor/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php
new file mode 100755
index 0000000..662ed50
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php
@@ -0,0 +1,59 @@
+<?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\HttpFoundation\Session\Storage;
+
+/**
+ * Allows session to be started by PHP and managed by Symfony.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class PhpBridgeSessionStorage extends NativeSessionStorage
+{
+    /**
+     * @param \SessionHandlerInterface|null $handler
+     * @param MetadataBag                   $metaBag MetadataBag
+     */
+    public function __construct($handler = null, MetadataBag $metaBag = null)
+    {
+        $this->setMetadataBag($metaBag);
+        $this->setSaveHandler($handler);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function start()
+    {
+        if ($this->started) {
+            return true;
+        }
+
+        $this->loadSession();
+
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function clear()
+    {
+        // clear out the bags and nothing else that may be set
+        // since the purpose of this driver is to share a handler
+        foreach ($this->bags as $bag) {
+            $bag->clear();
+        }
+
+        // reconnect the bags to the session
+        $this->loadSession();
+    }
+}
diff --git a/vendor/symfony/http-foundation/Session/Storage/Proxy/AbstractProxy.php b/vendor/symfony/http-foundation/Session/Storage/Proxy/AbstractProxy.php
new file mode 100755
index 0000000..09c9248
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/Storage/Proxy/AbstractProxy.php
@@ -0,0 +1,122 @@
+<?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\HttpFoundation\Session\Storage\Proxy;
+
+/**
+ * @author Drak <drak@zikula.org>
+ */
+abstract class AbstractProxy
+{
+    /**
+     * Flag if handler wraps an internal PHP session handler (using \SessionHandler).
+     *
+     * @var bool
+     */
+    protected $wrapper = false;
+
+    /**
+     * @var string
+     */
+    protected $saveHandlerName;
+
+    /**
+     * Gets the session.save_handler name.
+     *
+     * @return string
+     */
+    public function getSaveHandlerName()
+    {
+        return $this->saveHandlerName;
+    }
+
+    /**
+     * Is this proxy handler and instance of \SessionHandlerInterface.
+     *
+     * @return bool
+     */
+    public function isSessionHandlerInterface()
+    {
+        return $this instanceof \SessionHandlerInterface;
+    }
+
+    /**
+     * Returns true if this handler wraps an internal PHP session save handler using \SessionHandler.
+     *
+     * @return bool
+     */
+    public function isWrapper()
+    {
+        return $this->wrapper;
+    }
+
+    /**
+     * Has a session started?
+     *
+     * @return bool
+     */
+    public function isActive()
+    {
+        return \PHP_SESSION_ACTIVE === session_status();
+    }
+
+    /**
+     * Gets the session ID.
+     *
+     * @return string
+     */
+    public function getId()
+    {
+        return session_id();
+    }
+
+    /**
+     * Sets the session ID.
+     *
+     * @param string $id
+     *
+     * @throws \LogicException
+     */
+    public function setId($id)
+    {
+        if ($this->isActive()) {
+            throw new \LogicException('Cannot change the ID of an active session');
+        }
+
+        session_id($id);
+    }
+
+    /**
+     * Gets the session name.
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        return session_name();
+    }
+
+    /**
+     * Sets the session name.
+     *
+     * @param string $name
+     *
+     * @throws \LogicException
+     */
+    public function setName($name)
+    {
+        if ($this->isActive()) {
+            throw new \LogicException('Cannot change the name of an active session');
+        }
+
+        session_name($name);
+    }
+}
diff --git a/vendor/symfony/http-foundation/Session/Storage/Proxy/NativeProxy.php b/vendor/symfony/http-foundation/Session/Storage/Proxy/NativeProxy.php
new file mode 100755
index 0000000..082eed1
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/Storage/Proxy/NativeProxy.php
@@ -0,0 +1,40 @@
+<?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\HttpFoundation\Session\Storage\Proxy;
+
+@trigger_error('The '.__NAMESPACE__.'\NativeProxy class is deprecated since Symfony 3.4 and will be removed in 4.0. Use your session handler implementation directly.', E_USER_DEPRECATED);
+
+/**
+ * This proxy is built-in session handlers in PHP 5.3.x.
+ *
+ * @deprecated since version 3.4, to be removed in 4.0. Use your session handler implementation directly.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class NativeProxy extends AbstractProxy
+{
+    public function __construct()
+    {
+        // this makes an educated guess as to what the handler is since it should already be set.
+        $this->saveHandlerName = ini_get('session.save_handler');
+    }
+
+    /**
+     * Returns true if this handler wraps an internal PHP session save handler using \SessionHandler.
+     *
+     * @return bool False
+     */
+    public function isWrapper()
+    {
+        return false;
+    }
+}
diff --git a/vendor/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php b/vendor/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php
new file mode 100755
index 0000000..b11cc39
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php
@@ -0,0 +1,101 @@
+<?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\HttpFoundation\Session\Storage\Proxy;
+
+/**
+ * @author Drak <drak@zikula.org>
+ */
+class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface
+{
+    protected $handler;
+
+    public function __construct(\SessionHandlerInterface $handler)
+    {
+        $this->handler = $handler;
+        $this->wrapper = ($handler instanceof \SessionHandler);
+        $this->saveHandlerName = $this->wrapper ? ini_get('session.save_handler') : 'user';
+    }
+
+    /**
+     * @return \SessionHandlerInterface
+     */
+    public function getHandler()
+    {
+        return $this->handler;
+    }
+
+    // \SessionHandlerInterface
+
+    /**
+     * {@inheritdoc}
+     */
+    public function open($savePath, $sessionName)
+    {
+        return (bool) $this->handler->open($savePath, $sessionName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function close()
+    {
+        return (bool) $this->handler->close();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function read($sessionId)
+    {
+        return (string) $this->handler->read($sessionId);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function write($sessionId, $data)
+    {
+        return (bool) $this->handler->write($sessionId, $data);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function destroy($sessionId)
+    {
+        return (bool) $this->handler->destroy($sessionId);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function gc($maxlifetime)
+    {
+        return (bool) $this->handler->gc($maxlifetime);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function validateId($sessionId)
+    {
+        return !$this->handler instanceof \SessionUpdateTimestampHandlerInterface || $this->handler->validateId($sessionId);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function updateTimestamp($sessionId, $data)
+    {
+        return $this->handler instanceof \SessionUpdateTimestampHandlerInterface ? $this->handler->updateTimestamp($sessionId, $data) : $this->write($sessionId, $data);
+    }
+}
diff --git a/vendor/symfony/http-foundation/Session/Storage/SessionStorageInterface.php b/vendor/symfony/http-foundation/Session/Storage/SessionStorageInterface.php
new file mode 100755
index 0000000..66e8b33
--- /dev/null
+++ b/vendor/symfony/http-foundation/Session/Storage/SessionStorageInterface.php
@@ -0,0 +1,137 @@
+<?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\HttpFoundation\Session\Storage;
+
+use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
+
+/**
+ * StorageInterface.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Drak <drak@zikula.org>
+ */
+interface SessionStorageInterface
+{
+    /**
+     * Starts the session.
+     *
+     * @return bool True if started
+     *
+     * @throws \RuntimeException if something goes wrong starting the session
+     */
+    public function start();
+
+    /**
+     * Checks if the session is started.
+     *
+     * @return bool True if started, false otherwise
+     */
+    public function isStarted();
+
+    /**
+     * Returns the session ID.
+     *
+     * @return string The session ID or empty
+     */
+    public function getId();
+
+    /**
+     * Sets the session ID.
+     *
+     * @param string $id
+     */
+    public function setId($id);
+
+    /**
+     * Returns the session name.
+     *
+     * @return mixed The session name
+     */
+    public function getName();
+
+    /**
+     * Sets the session name.
+     *
+     * @param string $name
+     */
+    public function setName($name);
+
+    /**
+     * Regenerates id that represents this storage.
+     *
+     * This method must invoke session_regenerate_id($destroy) unless
+     * this interface is used for a storage object designed for unit
+     * or functional testing where a real PHP session would interfere
+     * with testing.
+     *
+     * Note regenerate+destroy should not clear the session data in memory
+     * only delete the session data from persistent storage.
+     *
+     * Care: When regenerating the session ID no locking is involved in PHP's
+     * session design. See https://bugs.php.net/bug.php?id=61470 for a discussion.
+     * So you must make sure the regenerated session is saved BEFORE sending the
+     * headers with the new ID. Symfony's HttpKernel offers a listener for this.
+     * See Symfony\Component\HttpKernel\EventListener\SaveSessionListener.
+     * Otherwise session data could get lost again for concurrent requests with the
+     * new ID. One result could be that you get logged out after just logging in.
+     *
+     * @param bool $destroy  Destroy session when regenerating?
+     * @param int  $lifetime Sets the cookie lifetime for the session cookie. A null value
+     *                       will leave the system settings unchanged, 0 sets the cookie
+     *                       to expire with browser session. Time is in seconds, and is
+     *                       not a Unix timestamp.
+     *
+     * @return bool True if session regenerated, false if error
+     *
+     * @throws \RuntimeException If an error occurs while regenerating this storage
+     */
+    public function regenerate($destroy = false, $lifetime = null);
+
+    /**
+     * Force the session to be saved and closed.
+     *
+     * This method must invoke session_write_close() unless this interface is
+     * used for a storage object design for unit or functional testing where
+     * a real PHP session would interfere with testing, in which case
+     * it should actually persist the session data if required.
+     *
+     * @throws \RuntimeException if the session is saved without being started, or if the session
+     *                           is already closed
+     */
+    public function save();
+
+    /**
+     * Clear all session data in memory.
+     */
+    public function clear();
+
+    /**
+     * Gets a SessionBagInterface by name.
+     *
+     * @param string $name
+     *
+     * @return SessionBagInterface
+     *
+     * @throws \InvalidArgumentException If the bag does not exist
+     */
+    public function getBag($name);
+
+    /**
+     * Registers a SessionBagInterface for use.
+     */
+    public function registerBag(SessionBagInterface $bag);
+
+    /**
+     * @return MetadataBag
+     */
+    public function getMetadataBag();
+}
diff --git a/vendor/symfony/http-foundation/StreamedResponse.php b/vendor/symfony/http-foundation/StreamedResponse.php
new file mode 100755
index 0000000..8bc5fc9
--- /dev/null
+++ b/vendor/symfony/http-foundation/StreamedResponse.php
@@ -0,0 +1,146 @@
+<?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\HttpFoundation;
+
+/**
+ * StreamedResponse represents a streamed HTTP response.
+ *
+ * A StreamedResponse uses a callback for its content.
+ *
+ * The callback should use the standard PHP functions like echo
+ * to stream the response back to the client. The flush() function
+ * can also be used if needed.
+ *
+ * @see flush()
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class StreamedResponse extends Response
+{
+    protected $callback;
+    protected $streamed;
+    private $headersSent;
+
+    /**
+     * @param callable|null $callback A valid PHP callback or null to set it later
+     * @param int           $status   The response status code
+     * @param array         $headers  An array of response headers
+     */
+    public function __construct(callable $callback = null, $status = 200, $headers = [])
+    {
+        parent::__construct(null, $status, $headers);
+
+        if (null !== $callback) {
+            $this->setCallback($callback);
+        }
+        $this->streamed = false;
+        $this->headersSent = false;
+    }
+
+    /**
+     * Factory method for chainability.
+     *
+     * @param callable|null $callback A valid PHP callback or null to set it later
+     * @param int           $status   The response status code
+     * @param array         $headers  An array of response headers
+     *
+     * @return static
+     */
+    public static function create($callback = null, $status = 200, $headers = [])
+    {
+        return new static($callback, $status, $headers);
+    }
+
+    /**
+     * Sets the PHP callback associated with this Response.
+     *
+     * @param callable $callback A valid PHP callback
+     *
+     * @return $this
+     */
+    public function setCallback(callable $callback)
+    {
+        $this->callback = $callback;
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * This method only sends the headers once.
+     *
+     * @return $this
+     */
+    public function sendHeaders()
+    {
+        if ($this->headersSent) {
+            return $this;
+        }
+
+        $this->headersSent = true;
+
+        return parent::sendHeaders();
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * This method only sends the content once.
+     *
+     * @return $this
+     */
+    public function sendContent()
+    {
+        if ($this->streamed) {
+            return $this;
+        }
+
+        $this->streamed = true;
+
+        if (null === $this->callback) {
+            throw new \LogicException('The Response callback must not be null.');
+        }
+
+        \call_user_func($this->callback);
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @throws \LogicException when the content is not null
+     *
+     * @return $this
+     */
+    public function setContent($content)
+    {
+        if (null !== $content) {
+            throw new \LogicException('The content cannot be set on a StreamedResponse instance.');
+        }
+
+        $this->streamed = true;
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @return false
+     */
+    public function getContent()
+    {
+        return false;
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/AcceptHeaderItemTest.php b/vendor/symfony/http-foundation/Tests/AcceptHeaderItemTest.php
new file mode 100755
index 0000000..a40a762
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/AcceptHeaderItemTest.php
@@ -0,0 +1,113 @@
+<?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\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\AcceptHeaderItem;
+
+class AcceptHeaderItemTest extends TestCase
+{
+    /**
+     * @dataProvider provideFromStringData
+     */
+    public function testFromString($string, $value, array $attributes)
+    {
+        $item = AcceptHeaderItem::fromString($string);
+        $this->assertEquals($value, $item->getValue());
+        $this->assertEquals($attributes, $item->getAttributes());
+    }
+
+    public function provideFromStringData()
+    {
+        return [
+            [
+                'text/html',
+                'text/html', [],
+            ],
+            [
+                '"this;should,not=matter"',
+                'this;should,not=matter', [],
+            ],
+            [
+                "text/plain; charset=utf-8;param=\"this;should,not=matter\";\tfootnotes=true",
+                'text/plain', ['charset' => 'utf-8', 'param' => 'this;should,not=matter', 'footnotes' => 'true'],
+            ],
+            [
+                '"this;should,not=matter";charset=utf-8',
+                'this;should,not=matter', ['charset' => 'utf-8'],
+            ],
+        ];
+    }
+
+    /**
+     * @dataProvider provideToStringData
+     */
+    public function testToString($value, array $attributes, $string)
+    {
+        $item = new AcceptHeaderItem($value, $attributes);
+        $this->assertEquals($string, (string) $item);
+    }
+
+    public function provideToStringData()
+    {
+        return [
+            [
+                'text/html', [],
+                'text/html',
+            ],
+            [
+                'text/plain', ['charset' => 'utf-8', 'param' => 'this;should,not=matter', 'footnotes' => 'true'],
+                'text/plain;charset=utf-8;param="this;should,not=matter";footnotes=true',
+            ],
+        ];
+    }
+
+    public function testValue()
+    {
+        $item = new AcceptHeaderItem('value', []);
+        $this->assertEquals('value', $item->getValue());
+
+        $item->setValue('new value');
+        $this->assertEquals('new value', $item->getValue());
+
+        $item->setValue(1);
+        $this->assertEquals('1', $item->getValue());
+    }
+
+    public function testQuality()
+    {
+        $item = new AcceptHeaderItem('value', []);
+        $this->assertEquals(1.0, $item->getQuality());
+
+        $item->setQuality(0.5);
+        $this->assertEquals(0.5, $item->getQuality());
+
+        $item->setAttribute('q', 0.75);
+        $this->assertEquals(0.75, $item->getQuality());
+        $this->assertFalse($item->hasAttribute('q'));
+    }
+
+    public function testAttribute()
+    {
+        $item = new AcceptHeaderItem('value', []);
+        $this->assertEquals([], $item->getAttributes());
+        $this->assertFalse($item->hasAttribute('test'));
+        $this->assertNull($item->getAttribute('test'));
+        $this->assertEquals('default', $item->getAttribute('test', 'default'));
+
+        $item->setAttribute('test', 'value');
+        $this->assertEquals(['test' => 'value'], $item->getAttributes());
+        $this->assertTrue($item->hasAttribute('test'));
+        $this->assertEquals('value', $item->getAttribute('test'));
+        $this->assertEquals('value', $item->getAttribute('test', 'default'));
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/AcceptHeaderTest.php b/vendor/symfony/http-foundation/Tests/AcceptHeaderTest.php
new file mode 100755
index 0000000..3199869
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/AcceptHeaderTest.php
@@ -0,0 +1,103 @@
+<?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\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\AcceptHeader;
+use Symfony\Component\HttpFoundation\AcceptHeaderItem;
+
+class AcceptHeaderTest extends TestCase
+{
+    public function testFirst()
+    {
+        $header = AcceptHeader::fromString('text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c');
+        $this->assertSame('text/html', $header->first()->getValue());
+    }
+
+    /**
+     * @dataProvider provideFromStringData
+     */
+    public function testFromString($string, array $items)
+    {
+        $header = AcceptHeader::fromString($string);
+        $parsed = array_values($header->all());
+        // reset index since the fixtures don't have them set
+        foreach ($parsed as $item) {
+            $item->setIndex(0);
+        }
+        $this->assertEquals($items, $parsed);
+    }
+
+    public function provideFromStringData()
+    {
+        return [
+            ['', []],
+            ['gzip', [new AcceptHeaderItem('gzip')]],
+            ['gzip,deflate,sdch', [new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch')]],
+            ["gzip, deflate\t,sdch", [new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch')]],
+            ['"this;should,not=matter"', [new AcceptHeaderItem('this;should,not=matter')]],
+        ];
+    }
+
+    /**
+     * @dataProvider provideToStringData
+     */
+    public function testToString(array $items, $string)
+    {
+        $header = new AcceptHeader($items);
+        $this->assertEquals($string, (string) $header);
+    }
+
+    public function provideToStringData()
+    {
+        return [
+            [[], ''],
+            [[new AcceptHeaderItem('gzip')], 'gzip'],
+            [[new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch')], 'gzip,deflate,sdch'],
+            [[new AcceptHeaderItem('this;should,not=matter')], 'this;should,not=matter'],
+        ];
+    }
+
+    /**
+     * @dataProvider provideFilterData
+     */
+    public function testFilter($string, $filter, array $values)
+    {
+        $header = AcceptHeader::fromString($string)->filter($filter);
+        $this->assertEquals($values, array_keys($header->all()));
+    }
+
+    public function provideFilterData()
+    {
+        return [
+            ['fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4', '/fr.*/', ['fr-FR', 'fr']],
+        ];
+    }
+
+    /**
+     * @dataProvider provideSortingData
+     */
+    public function testSorting($string, array $values)
+    {
+        $header = AcceptHeader::fromString($string);
+        $this->assertEquals($values, array_keys($header->all()));
+    }
+
+    public function provideSortingData()
+    {
+        return [
+            'quality has priority' => ['*;q=0.3,ISO-8859-1,utf-8;q=0.7', ['ISO-8859-1', 'utf-8', '*']],
+            'order matters when q is equal' => ['*;q=0.3,ISO-8859-1;q=0.7,utf-8;q=0.7', ['ISO-8859-1', 'utf-8', '*']],
+            'order matters when q is equal2' => ['*;q=0.3,utf-8;q=0.7,ISO-8859-1;q=0.7', ['utf-8', 'ISO-8859-1', '*']],
+        ];
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/ApacheRequestTest.php b/vendor/symfony/http-foundation/Tests/ApacheRequestTest.php
new file mode 100755
index 0000000..6fa3b88
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/ApacheRequestTest.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\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\ApacheRequest;
+
+class ApacheRequestTest extends TestCase
+{
+    /**
+     * @dataProvider provideServerVars
+     */
+    public function testUriMethods($server, $expectedRequestUri, $expectedBaseUrl, $expectedPathInfo)
+    {
+        $request = new ApacheRequest();
+        $request->server->replace($server);
+
+        $this->assertEquals($expectedRequestUri, $request->getRequestUri(), '->getRequestUri() is correct');
+        $this->assertEquals($expectedBaseUrl, $request->getBaseUrl(), '->getBaseUrl() is correct');
+        $this->assertEquals($expectedPathInfo, $request->getPathInfo(), '->getPathInfo() is correct');
+    }
+
+    public function provideServerVars()
+    {
+        return [
+            [
+                [
+                    'REQUEST_URI' => '/foo/app_dev.php/bar',
+                    'SCRIPT_NAME' => '/foo/app_dev.php',
+                    'PATH_INFO' => '/bar',
+                ],
+                '/foo/app_dev.php/bar',
+                '/foo/app_dev.php',
+                '/bar',
+            ],
+            [
+                [
+                    'REQUEST_URI' => '/foo/bar',
+                    'SCRIPT_NAME' => '/foo/app_dev.php',
+                ],
+                '/foo/bar',
+                '/foo',
+                '/bar',
+            ],
+            [
+                [
+                    'REQUEST_URI' => '/app_dev.php/foo/bar',
+                    'SCRIPT_NAME' => '/app_dev.php',
+                    'PATH_INFO' => '/foo/bar',
+                ],
+                '/app_dev.php/foo/bar',
+                '/app_dev.php',
+                '/foo/bar',
+            ],
+            [
+                [
+                    'REQUEST_URI' => '/foo/bar',
+                    'SCRIPT_NAME' => '/app_dev.php',
+                ],
+                '/foo/bar',
+                '',
+                '/foo/bar',
+            ],
+            [
+                [
+                    'REQUEST_URI' => '/app_dev.php',
+                    'SCRIPT_NAME' => '/app_dev.php',
+                ],
+                '/app_dev.php',
+                '/app_dev.php',
+                '/',
+            ],
+            [
+                [
+                    'REQUEST_URI' => '/',
+                    'SCRIPT_NAME' => '/app_dev.php',
+                ],
+                '/',
+                '',
+                '/',
+            ],
+        ];
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/BinaryFileResponseTest.php b/vendor/symfony/http-foundation/Tests/BinaryFileResponseTest.php
new file mode 100755
index 0000000..c89f20d
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/BinaryFileResponseTest.php
@@ -0,0 +1,365 @@
+<?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\HttpFoundation\Tests;
+
+use Symfony\Component\HttpFoundation\BinaryFileResponse;
+use Symfony\Component\HttpFoundation\File\Stream;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\ResponseHeaderBag;
+use Symfony\Component\HttpFoundation\Tests\File\FakeFile;
+
+class BinaryFileResponseTest extends ResponseTestCase
+{
+    public function testConstruction()
+    {
+        $file = __DIR__.'/../README.md';
+        $response = new BinaryFileResponse($file, 404, ['X-Header' => 'Foo'], true, null, true, true);
+        $this->assertEquals(404, $response->getStatusCode());
+        $this->assertEquals('Foo', $response->headers->get('X-Header'));
+        $this->assertTrue($response->headers->has('ETag'));
+        $this->assertTrue($response->headers->has('Last-Modified'));
+        $this->assertFalse($response->headers->has('Content-Disposition'));
+
+        $response = BinaryFileResponse::create($file, 404, [], true, ResponseHeaderBag::DISPOSITION_INLINE);
+        $this->assertEquals(404, $response->getStatusCode());
+        $this->assertFalse($response->headers->has('ETag'));
+        $this->assertEquals('inline; filename="README.md"', $response->headers->get('Content-Disposition'));
+    }
+
+    public function testConstructWithNonAsciiFilename()
+    {
+        touch(sys_get_temp_dir().'/fööö.html');
+
+        $response = new BinaryFileResponse(sys_get_temp_dir().'/fööö.html', 200, [], true, 'attachment');
+
+        @unlink(sys_get_temp_dir().'/fööö.html');
+
+        $this->assertSame('fööö.html', $response->getFile()->getFilename());
+    }
+
+    /**
+     * @expectedException \LogicException
+     */
+    public function testSetContent()
+    {
+        $response = new BinaryFileResponse(__FILE__);
+        $response->setContent('foo');
+    }
+
+    public function testGetContent()
+    {
+        $response = new BinaryFileResponse(__FILE__);
+        $this->assertFalse($response->getContent());
+    }
+
+    public function testSetContentDispositionGeneratesSafeFallbackFilename()
+    {
+        $response = new BinaryFileResponse(__FILE__);
+        $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, 'föö.html');
+
+        $this->assertSame('attachment; filename="f__.html"; filename*=utf-8\'\'f%C3%B6%C3%B6.html', $response->headers->get('Content-Disposition'));
+    }
+
+    public function testSetContentDispositionGeneratesSafeFallbackFilenameForWronglyEncodedFilename()
+    {
+        $response = new BinaryFileResponse(__FILE__);
+
+        $iso88591EncodedFilename = utf8_decode('föö.html');
+        $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $iso88591EncodedFilename);
+
+        // the parameter filename* is invalid in this case (rawurldecode('f%F6%F6') does not provide a UTF-8 string but an ISO-8859-1 encoded one)
+        $this->assertSame('attachment; filename="f__.html"; filename*=utf-8\'\'f%F6%F6.html', $response->headers->get('Content-Disposition'));
+    }
+
+    /**
+     * @dataProvider provideRanges
+     */
+    public function testRequests($requestRange, $offset, $length, $responseRange)
+    {
+        $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, ['Content-Type' => 'application/octet-stream'])->setAutoEtag();
+
+        // do a request to get the ETag
+        $request = Request::create('/');
+        $response->prepare($request);
+        $etag = $response->headers->get('ETag');
+
+        // prepare a request for a range of the testing file
+        $request = Request::create('/');
+        $request->headers->set('If-Range', $etag);
+        $request->headers->set('Range', $requestRange);
+
+        $file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r');
+        fseek($file, $offset);
+        $data = fread($file, $length);
+        fclose($file);
+
+        $this->expectOutputString($data);
+        $response = clone $response;
+        $response->prepare($request);
+        $response->sendContent();
+
+        $this->assertEquals(206, $response->getStatusCode());
+        $this->assertEquals($responseRange, $response->headers->get('Content-Range'));
+        $this->assertSame($length, $response->headers->get('Content-Length'));
+    }
+
+    /**
+     * @dataProvider provideRanges
+     */
+    public function testRequestsWithoutEtag($requestRange, $offset, $length, $responseRange)
+    {
+        $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, ['Content-Type' => 'application/octet-stream']);
+
+        // do a request to get the LastModified
+        $request = Request::create('/');
+        $response->prepare($request);
+        $lastModified = $response->headers->get('Last-Modified');
+
+        // prepare a request for a range of the testing file
+        $request = Request::create('/');
+        $request->headers->set('If-Range', $lastModified);
+        $request->headers->set('Range', $requestRange);
+
+        $file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r');
+        fseek($file, $offset);
+        $data = fread($file, $length);
+        fclose($file);
+
+        $this->expectOutputString($data);
+        $response = clone $response;
+        $response->prepare($request);
+        $response->sendContent();
+
+        $this->assertEquals(206, $response->getStatusCode());
+        $this->assertEquals($responseRange, $response->headers->get('Content-Range'));
+    }
+
+    public function provideRanges()
+    {
+        return [
+            ['bytes=1-4', 1, 4, 'bytes 1-4/35'],
+            ['bytes=-5', 30, 5, 'bytes 30-34/35'],
+            ['bytes=30-', 30, 5, 'bytes 30-34/35'],
+            ['bytes=30-30', 30, 1, 'bytes 30-30/35'],
+            ['bytes=30-34', 30, 5, 'bytes 30-34/35'],
+        ];
+    }
+
+    public function testRangeRequestsWithoutLastModifiedDate()
+    {
+        // prevent auto last modified
+        $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, ['Content-Type' => 'application/octet-stream'], true, null, false, false);
+
+        // prepare a request for a range of the testing file
+        $request = Request::create('/');
+        $request->headers->set('If-Range', date('D, d M Y H:i:s').' GMT');
+        $request->headers->set('Range', 'bytes=1-4');
+
+        $this->expectOutputString(file_get_contents(__DIR__.'/File/Fixtures/test.gif'));
+        $response = clone $response;
+        $response->prepare($request);
+        $response->sendContent();
+
+        $this->assertEquals(200, $response->getStatusCode());
+        $this->assertNull($response->headers->get('Content-Range'));
+    }
+
+    /**
+     * @dataProvider provideFullFileRanges
+     */
+    public function testFullFileRequests($requestRange)
+    {
+        $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, ['Content-Type' => 'application/octet-stream'])->setAutoEtag();
+
+        // prepare a request for a range of the testing file
+        $request = Request::create('/');
+        $request->headers->set('Range', $requestRange);
+
+        $file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r');
+        $data = fread($file, 35);
+        fclose($file);
+
+        $this->expectOutputString($data);
+        $response = clone $response;
+        $response->prepare($request);
+        $response->sendContent();
+
+        $this->assertEquals(200, $response->getStatusCode());
+    }
+
+    public function provideFullFileRanges()
+    {
+        return [
+            ['bytes=0-'],
+            ['bytes=0-34'],
+            ['bytes=-35'],
+            // Syntactical invalid range-request should also return the full resource
+            ['bytes=20-10'],
+            ['bytes=50-40'],
+        ];
+    }
+
+    public function testUnpreparedResponseSendsFullFile()
+    {
+        $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200);
+
+        $data = file_get_contents(__DIR__.'/File/Fixtures/test.gif');
+
+        $this->expectOutputString($data);
+        $response = clone $response;
+        $response->sendContent();
+
+        $this->assertEquals(200, $response->getStatusCode());
+    }
+
+    /**
+     * @dataProvider provideInvalidRanges
+     */
+    public function testInvalidRequests($requestRange)
+    {
+        $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, ['Content-Type' => 'application/octet-stream'])->setAutoEtag();
+
+        // prepare a request for a range of the testing file
+        $request = Request::create('/');
+        $request->headers->set('Range', $requestRange);
+
+        $response = clone $response;
+        $response->prepare($request);
+        $response->sendContent();
+
+        $this->assertEquals(416, $response->getStatusCode());
+        $this->assertEquals('bytes */35', $response->headers->get('Content-Range'));
+    }
+
+    public function provideInvalidRanges()
+    {
+        return [
+            ['bytes=-40'],
+            ['bytes=30-40'],
+        ];
+    }
+
+    /**
+     * @dataProvider provideXSendfileFiles
+     */
+    public function testXSendfile($file)
+    {
+        $request = Request::create('/');
+        $request->headers->set('X-Sendfile-Type', 'X-Sendfile');
+
+        BinaryFileResponse::trustXSendfileTypeHeader();
+        $response = BinaryFileResponse::create($file, 200, ['Content-Type' => 'application/octet-stream']);
+        $response->prepare($request);
+
+        $this->expectOutputString('');
+        $response->sendContent();
+
+        $this->assertContains('README.md', $response->headers->get('X-Sendfile'));
+    }
+
+    public function provideXSendfileFiles()
+    {
+        return [
+            [__DIR__.'/../README.md'],
+            ['file://'.__DIR__.'/../README.md'],
+        ];
+    }
+
+    /**
+     * @dataProvider getSampleXAccelMappings
+     */
+    public function testXAccelMapping($realpath, $mapping, $virtual)
+    {
+        $request = Request::create('/');
+        $request->headers->set('X-Sendfile-Type', 'X-Accel-Redirect');
+        $request->headers->set('X-Accel-Mapping', $mapping);
+
+        $file = new FakeFile($realpath, __DIR__.'/File/Fixtures/test');
+
+        BinaryFileResponse::trustXSendfileTypeHeader();
+        $response = new BinaryFileResponse($file, 200, ['Content-Type' => 'application/octet-stream']);
+        $reflection = new \ReflectionObject($response);
+        $property = $reflection->getProperty('file');
+        $property->setAccessible(true);
+        $property->setValue($response, $file);
+
+        $response->prepare($request);
+        $this->assertEquals($virtual, $response->headers->get('X-Accel-Redirect'));
+    }
+
+    public function testDeleteFileAfterSend()
+    {
+        $request = Request::create('/');
+
+        $path = __DIR__.'/File/Fixtures/to_delete';
+        touch($path);
+        $realPath = realpath($path);
+        $this->assertFileExists($realPath);
+
+        $response = new BinaryFileResponse($realPath, 200, ['Content-Type' => 'application/octet-stream']);
+        $response->deleteFileAfterSend(true);
+
+        $response->prepare($request);
+        $response->sendContent();
+
+        $this->assertFileNotExists($path);
+    }
+
+    public function testAcceptRangeOnUnsafeMethods()
+    {
+        $request = Request::create('/', 'POST');
+        $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, ['Content-Type' => 'application/octet-stream']);
+        $response->prepare($request);
+
+        $this->assertEquals('none', $response->headers->get('Accept-Ranges'));
+    }
+
+    public function testAcceptRangeNotOverriden()
+    {
+        $request = Request::create('/', 'POST');
+        $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, ['Content-Type' => 'application/octet-stream']);
+        $response->headers->set('Accept-Ranges', 'foo');
+        $response->prepare($request);
+
+        $this->assertEquals('foo', $response->headers->get('Accept-Ranges'));
+    }
+
+    public function getSampleXAccelMappings()
+    {
+        return [
+            ['/var/www/var/www/files/foo.txt', '/var/www/=/files/', '/files/var/www/files/foo.txt'],
+            ['/home/foo/bar.txt', '/var/www/=/files/,/home/foo/=/baz/', '/baz/bar.txt'],
+        ];
+    }
+
+    public function testStream()
+    {
+        $request = Request::create('/');
+        $response = new BinaryFileResponse(new Stream(__DIR__.'/../README.md'), 200, ['Content-Type' => 'text/plain']);
+        $response->prepare($request);
+
+        $this->assertNull($response->headers->get('Content-Length'));
+    }
+
+    protected function provideResponse()
+    {
+        return new BinaryFileResponse(__DIR__.'/../README.md', 200, ['Content-Type' => 'application/octet-stream']);
+    }
+
+    public static function tearDownAfterClass()
+    {
+        $path = __DIR__.'/../Fixtures/to_delete';
+        if (file_exists($path)) {
+            @unlink($path);
+        }
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/CookieTest.php b/vendor/symfony/http-foundation/Tests/CookieTest.php
new file mode 100755
index 0000000..aaf2edb
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/CookieTest.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\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Cookie;
+
+/**
+ * CookieTest.
+ *
+ * @author John Kary <john@johnkary.net>
+ * @author Hugo Hamon <hugo.hamon@sensio.com>
+ *
+ * @group time-sensitive
+ */
+class CookieTest extends TestCase
+{
+    public function invalidNames()
+    {
+        return [
+            [''],
+            [',MyName'],
+            [';MyName'],
+            [' MyName'],
+            ["\tMyName"],
+            ["\rMyName"],
+            ["\nMyName"],
+            ["\013MyName"],
+            ["\014MyName"],
+        ];
+    }
+
+    /**
+     * @dataProvider invalidNames
+     * @expectedException \InvalidArgumentException
+     */
+    public function testInstantiationThrowsExceptionIfCookieNameContainsInvalidCharacters($name)
+    {
+        new Cookie($name);
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testInvalidExpiration()
+    {
+        new Cookie('MyCookie', 'foo', 'bar');
+    }
+
+    public function testNegativeExpirationIsNotPossible()
+    {
+        $cookie = new Cookie('foo', 'bar', -100);
+
+        $this->assertSame(0, $cookie->getExpiresTime());
+    }
+
+    public function testGetValue()
+    {
+        $value = 'MyValue';
+        $cookie = new Cookie('MyCookie', $value);
+
+        $this->assertSame($value, $cookie->getValue(), '->getValue() returns the proper value');
+    }
+
+    public function testGetPath()
+    {
+        $cookie = new Cookie('foo', 'bar');
+
+        $this->assertSame('/', $cookie->getPath(), '->getPath() returns / as the default path');
+    }
+
+    public function testGetExpiresTime()
+    {
+        $cookie = new Cookie('foo', 'bar');
+
+        $this->assertEquals(0, $cookie->getExpiresTime(), '->getExpiresTime() returns the default expire date');
+
+        $cookie = new Cookie('foo', 'bar', $expire = time() + 3600);
+
+        $this->assertEquals($expire, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
+    }
+
+    public function testGetExpiresTimeIsCastToInt()
+    {
+        $cookie = new Cookie('foo', 'bar', 3600.9);
+
+        $this->assertSame(3600, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date as an integer');
+    }
+
+    public function testConstructorWithDateTime()
+    {
+        $expire = new \DateTime();
+        $cookie = new Cookie('foo', 'bar', $expire);
+
+        $this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
+    }
+
+    /**
+     * @requires PHP 5.5
+     */
+    public function testConstructorWithDateTimeImmutable()
+    {
+        $expire = new \DateTimeImmutable();
+        $cookie = new Cookie('foo', 'bar', $expire);
+
+        $this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
+    }
+
+    public function testGetExpiresTimeWithStringValue()
+    {
+        $value = '+1 day';
+        $cookie = new Cookie('foo', 'bar', $value);
+        $expire = strtotime($value);
+
+        $this->assertEquals($expire, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date', 1);
+    }
+
+    public function testGetDomain()
+    {
+        $cookie = new Cookie('foo', 'bar', 0, '/', '.myfoodomain.com');
+
+        $this->assertEquals('.myfoodomain.com', $cookie->getDomain(), '->getDomain() returns the domain name on which the cookie is valid');
+    }
+
+    public function testIsSecure()
+    {
+        $cookie = new Cookie('foo', 'bar', 0, '/', '.myfoodomain.com', true);
+
+        $this->assertTrue($cookie->isSecure(), '->isSecure() returns whether the cookie is transmitted over HTTPS');
+    }
+
+    public function testIsHttpOnly()
+    {
+        $cookie = new Cookie('foo', 'bar', 0, '/', '.myfoodomain.com', false, true);
+
+        $this->assertTrue($cookie->isHttpOnly(), '->isHttpOnly() returns whether the cookie is only transmitted over HTTP');
+    }
+
+    public function testCookieIsNotCleared()
+    {
+        $cookie = new Cookie('foo', 'bar', time() + 3600 * 24);
+
+        $this->assertFalse($cookie->isCleared(), '->isCleared() returns false if the cookie did not expire yet');
+    }
+
+    public function testCookieIsCleared()
+    {
+        $cookie = new Cookie('foo', 'bar', time() - 20);
+
+        $this->assertTrue($cookie->isCleared(), '->isCleared() returns true if the cookie has expired');
+
+        $cookie = new Cookie('foo', 'bar');
+
+        $this->assertFalse($cookie->isCleared());
+
+        $cookie = new Cookie('foo', 'bar', 0);
+
+        $this->assertFalse($cookie->isCleared());
+
+        $cookie = new Cookie('foo', 'bar', -1);
+
+        $this->assertFalse($cookie->isCleared());
+    }
+
+    public function testToString()
+    {
+        $cookie = new Cookie('foo', 'bar', $expire = strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true);
+        $this->assertEquals('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; Max-Age=0; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() returns string representation of the cookie');
+
+        $cookie = new Cookie('foo', 'bar with white spaces', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true);
+        $this->assertEquals('foo=bar%20with%20white%20spaces; expires=Fri, 20-May-2011 15:25:52 GMT; Max-Age=0; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() encodes the value of the cookie according to RFC 3986 (white space = %20)');
+
+        $cookie = new Cookie('foo', null, 1, '/admin/', '.myfoodomain.com');
+        $this->assertEquals('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', $expire = time() - 31536001).'; Max-Age=0; path=/admin/; domain=.myfoodomain.com; httponly', (string) $cookie, '->__toString() returns string representation of a cleared cookie if value is NULL');
+
+        $cookie = new Cookie('foo', 'bar', 0, '/', '');
+        $this->assertEquals('foo=bar; path=/; httponly', (string) $cookie);
+    }
+
+    public function testRawCookie()
+    {
+        $cookie = new Cookie('foo', 'b a r', 0, '/', null, false, false);
+        $this->assertFalse($cookie->isRaw());
+        $this->assertEquals('foo=b%20a%20r; path=/', (string) $cookie);
+
+        $cookie = new Cookie('foo', 'b+a+r', 0, '/', null, false, false, true);
+        $this->assertTrue($cookie->isRaw());
+        $this->assertEquals('foo=b+a+r; path=/', (string) $cookie);
+    }
+
+    public function testGetMaxAge()
+    {
+        $cookie = new Cookie('foo', 'bar');
+        $this->assertEquals(0, $cookie->getMaxAge());
+
+        $cookie = new Cookie('foo', 'bar', $expire = time() + 100);
+        $this->assertEquals($expire - time(), $cookie->getMaxAge());
+
+        $cookie = new Cookie('foo', 'bar', $expire = time() - 100);
+        $this->assertEquals(0, $cookie->getMaxAge());
+    }
+
+    public function testFromString()
+    {
+        $cookie = Cookie::fromString('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; path=/; domain=.myfoodomain.com; secure; httponly');
+        $this->assertEquals(new Cookie('foo', 'bar', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true, true, true), $cookie);
+
+        $cookie = Cookie::fromString('foo=bar', true);
+        $this->assertEquals(new Cookie('foo', 'bar', 0, '/', null, false, false), $cookie);
+    }
+
+    public function testFromStringWithHttpOnly()
+    {
+        $cookie = Cookie::fromString('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; path=/; domain=.myfoodomain.com; secure; httponly');
+        $this->assertTrue($cookie->isHttpOnly());
+
+        $cookie = Cookie::fromString('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; path=/; domain=.myfoodomain.com; secure');
+        $this->assertFalse($cookie->isHttpOnly());
+    }
+
+    public function testSameSiteAttributeIsCaseInsensitive()
+    {
+        $cookie = new Cookie('foo', 'bar', 0, '/', null, false, true, false, 'Lax');
+        $this->assertEquals('lax', $cookie->getSameSite());
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/ExpressionRequestMatcherTest.php b/vendor/symfony/http-foundation/Tests/ExpressionRequestMatcherTest.php
new file mode 100755
index 0000000..2afdade
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/ExpressionRequestMatcherTest.php
@@ -0,0 +1,69 @@
+<?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\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
+use Symfony\Component\HttpFoundation\ExpressionRequestMatcher;
+use Symfony\Component\HttpFoundation\Request;
+
+class ExpressionRequestMatcherTest extends TestCase
+{
+    /**
+     * @expectedException \LogicException
+     */
+    public function testWhenNoExpressionIsSet()
+    {
+        $expressionRequestMatcher = new ExpressionRequestMatcher();
+        $expressionRequestMatcher->matches(new Request());
+    }
+
+    /**
+     * @dataProvider provideExpressions
+     */
+    public function testMatchesWhenParentMatchesIsTrue($expression, $expected)
+    {
+        $request = Request::create('/foo');
+        $expressionRequestMatcher = new ExpressionRequestMatcher();
+
+        $expressionRequestMatcher->setExpression(new ExpressionLanguage(), $expression);
+        $this->assertSame($expected, $expressionRequestMatcher->matches($request));
+    }
+
+    /**
+     * @dataProvider provideExpressions
+     */
+    public function testMatchesWhenParentMatchesIsFalse($expression)
+    {
+        $request = Request::create('/foo');
+        $request->attributes->set('foo', 'foo');
+        $expressionRequestMatcher = new ExpressionRequestMatcher();
+        $expressionRequestMatcher->matchAttribute('foo', 'bar');
+
+        $expressionRequestMatcher->setExpression(new ExpressionLanguage(), $expression);
+        $this->assertFalse($expressionRequestMatcher->matches($request));
+    }
+
+    public function provideExpressions()
+    {
+        return [
+            ['request.getMethod() == method', true],
+            ['request.getPathInfo() == path', true],
+            ['request.getHost() == host', true],
+            ['request.getClientIp() == ip', true],
+            ['request.attributes.all() == attributes', true],
+            ['request.getMethod() == method && request.getPathInfo() == path && request.getHost() == host && request.getClientIp() == ip &&  request.attributes.all() == attributes', true],
+            ['request.getMethod() != method', false],
+            ['request.getMethod() != method && request.getPathInfo() == path && request.getHost() == host && request.getClientIp() == ip &&  request.attributes.all() == attributes', false],
+        ];
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/File/FakeFile.php b/vendor/symfony/http-foundation/Tests/File/FakeFile.php
new file mode 100755
index 0000000..c415989
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/File/FakeFile.php
@@ -0,0 +1,45 @@
+<?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\HttpFoundation\Tests\File;
+
+use Symfony\Component\HttpFoundation\File\File as OrigFile;
+
+class FakeFile extends OrigFile
+{
+    private $realpath;
+
+    public function __construct($realpath, $path)
+    {
+        $this->realpath = $realpath;
+        parent::__construct($path, false);
+    }
+
+    public function isReadable()
+    {
+        return true;
+    }
+
+    public function getRealpath()
+    {
+        return $this->realpath;
+    }
+
+    public function getSize()
+    {
+        return 42;
+    }
+
+    public function getMTime()
+    {
+        return time();
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/File/FileTest.php b/vendor/symfony/http-foundation/Tests/File/FileTest.php
new file mode 100755
index 0000000..fb82dae
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/File/FileTest.php
@@ -0,0 +1,180 @@
+<?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\HttpFoundation\Tests\File;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\File\File;
+use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
+
+class FileTest extends TestCase
+{
+    protected $file;
+
+    public function testGetMimeTypeUsesMimeTypeGuessers()
+    {
+        $file = new File(__DIR__.'/Fixtures/test.gif');
+        $guesser = $this->createMockGuesser($file->getPathname(), 'image/gif');
+
+        MimeTypeGuesser::getInstance()->register($guesser);
+
+        $this->assertEquals('image/gif', $file->getMimeType());
+    }
+
+    public function testGuessExtensionWithoutGuesser()
+    {
+        $file = new File(__DIR__.'/Fixtures/directory/.empty');
+
+        $this->assertNull($file->guessExtension());
+    }
+
+    public function testGuessExtensionIsBasedOnMimeType()
+    {
+        $file = new File(__DIR__.'/Fixtures/test');
+        $guesser = $this->createMockGuesser($file->getPathname(), 'image/gif');
+
+        MimeTypeGuesser::getInstance()->register($guesser);
+
+        $this->assertEquals('gif', $file->guessExtension());
+    }
+
+    /**
+     * @requires extension fileinfo
+     */
+    public function testGuessExtensionWithReset()
+    {
+        $file = new File(__DIR__.'/Fixtures/other-file.example');
+        $guesser = $this->createMockGuesser($file->getPathname(), 'image/gif');
+        MimeTypeGuesser::getInstance()->register($guesser);
+
+        $this->assertEquals('gif', $file->guessExtension());
+
+        MimeTypeGuesser::reset();
+
+        $this->assertNull($file->guessExtension());
+    }
+
+    public function testConstructWhenFileNotExists()
+    {
+        $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
+
+        new File(__DIR__.'/Fixtures/not_here');
+    }
+
+    public function testMove()
+    {
+        $path = __DIR__.'/Fixtures/test.copy.gif';
+        $targetDir = __DIR__.'/Fixtures/directory';
+        $targetPath = $targetDir.'/test.copy.gif';
+        @unlink($path);
+        @unlink($targetPath);
+        copy(__DIR__.'/Fixtures/test.gif', $path);
+
+        $file = new File($path);
+        $movedFile = $file->move($targetDir);
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\File\File', $movedFile);
+
+        $this->assertFileExists($targetPath);
+        $this->assertFileNotExists($path);
+        $this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
+
+        @unlink($targetPath);
+    }
+
+    public function testMoveWithNewName()
+    {
+        $path = __DIR__.'/Fixtures/test.copy.gif';
+        $targetDir = __DIR__.'/Fixtures/directory';
+        $targetPath = $targetDir.'/test.newname.gif';
+        @unlink($path);
+        @unlink($targetPath);
+        copy(__DIR__.'/Fixtures/test.gif', $path);
+
+        $file = new File($path);
+        $movedFile = $file->move($targetDir, 'test.newname.gif');
+
+        $this->assertFileExists($targetPath);
+        $this->assertFileNotExists($path);
+        $this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
+
+        @unlink($targetPath);
+    }
+
+    public function getFilenameFixtures()
+    {
+        return [
+            ['original.gif', 'original.gif'],
+            ['..\\..\\original.gif', 'original.gif'],
+            ['../../original.gif', 'original.gif'],
+            ['файлfile.gif', 'файлfile.gif'],
+            ['..\\..\\файлfile.gif', 'файлfile.gif'],
+            ['../../файлfile.gif', 'файлfile.gif'],
+        ];
+    }
+
+    /**
+     * @dataProvider getFilenameFixtures
+     */
+    public function testMoveWithNonLatinName($filename, $sanitizedFilename)
+    {
+        $path = __DIR__.'/Fixtures/'.$sanitizedFilename;
+        $targetDir = __DIR__.'/Fixtures/directory/';
+        $targetPath = $targetDir.$sanitizedFilename;
+        @unlink($path);
+        @unlink($targetPath);
+        copy(__DIR__.'/Fixtures/test.gif', $path);
+
+        $file = new File($path);
+        $movedFile = $file->move($targetDir, $filename);
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\File\File', $movedFile);
+
+        $this->assertFileExists($targetPath);
+        $this->assertFileNotExists($path);
+        $this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
+
+        @unlink($targetPath);
+    }
+
+    public function testMoveToAnUnexistentDirectory()
+    {
+        $sourcePath = __DIR__.'/Fixtures/test.copy.gif';
+        $targetDir = __DIR__.'/Fixtures/directory/sub';
+        $targetPath = $targetDir.'/test.copy.gif';
+        @unlink($sourcePath);
+        @unlink($targetPath);
+        @rmdir($targetDir);
+        copy(__DIR__.'/Fixtures/test.gif', $sourcePath);
+
+        $file = new File($sourcePath);
+        $movedFile = $file->move($targetDir);
+
+        $this->assertFileExists($targetPath);
+        $this->assertFileNotExists($sourcePath);
+        $this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
+
+        @unlink($sourcePath);
+        @unlink($targetPath);
+        @rmdir($targetDir);
+    }
+
+    protected function createMockGuesser($path, $mimeType)
+    {
+        $guesser = $this->getMockBuilder('Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface')->getMock();
+        $guesser
+            ->expects($this->once())
+            ->method('guess')
+            ->with($this->equalTo($path))
+            ->will($this->returnValue($mimeType))
+        ;
+
+        return $guesser;
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/File/Fixtures/.unknownextension b/vendor/symfony/http-foundation/Tests/File/Fixtures/.unknownextension
new file mode 100755
index 0000000..4d1ae35
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/File/Fixtures/.unknownextension
@@ -0,0 +1 @@
+f
\ No newline at end of file
diff --git a/vendor/symfony/http-foundation/Tests/File/Fixtures/case-sensitive-mime-type.xlsm b/vendor/symfony/http-foundation/Tests/File/Fixtures/case-sensitive-mime-type.xlsm
new file mode 100755
index 0000000..94d85e6
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/File/Fixtures/case-sensitive-mime-type.xlsm
Binary files differ
diff --git a/vendor/symfony/http-foundation/Tests/File/Fixtures/directory/.empty b/vendor/symfony/http-foundation/Tests/File/Fixtures/directory/.empty
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/File/Fixtures/directory/.empty
diff --git a/vendor/symfony/http-foundation/Tests/File/Fixtures/other-file.example b/vendor/symfony/http-foundation/Tests/File/Fixtures/other-file.example
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/File/Fixtures/other-file.example
diff --git a/vendor/symfony/http-foundation/Tests/File/Fixtures/test b/vendor/symfony/http-foundation/Tests/File/Fixtures/test
new file mode 100755
index 0000000..b636f4b
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/File/Fixtures/test
Binary files differ
diff --git a/vendor/symfony/http-foundation/Tests/File/Fixtures/test.gif b/vendor/symfony/http-foundation/Tests/File/Fixtures/test.gif
new file mode 100755
index 0000000..b636f4b
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/File/Fixtures/test.gif
Binary files differ
diff --git a/vendor/symfony/http-foundation/Tests/File/MimeType/MimeTypeTest.php b/vendor/symfony/http-foundation/Tests/File/MimeType/MimeTypeTest.php
new file mode 100755
index 0000000..bb88807
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/File/MimeType/MimeTypeTest.php
@@ -0,0 +1,90 @@
+<?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\HttpFoundation\Tests\File\MimeType;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\File\MimeType\FileBinaryMimeTypeGuesser;
+use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
+
+/**
+ * @requires extension fileinfo
+ */
+class MimeTypeTest extends TestCase
+{
+    protected $path;
+
+    public function testGuessImageWithoutExtension()
+    {
+        $this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test'));
+    }
+
+    public function testGuessImageWithDirectory()
+    {
+        $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
+
+        MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/directory');
+    }
+
+    public function testGuessImageWithFileBinaryMimeTypeGuesser()
+    {
+        $guesser = MimeTypeGuesser::getInstance();
+        $guesser->register(new FileBinaryMimeTypeGuesser());
+        $this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test'));
+    }
+
+    public function testGuessImageWithKnownExtension()
+    {
+        $this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test.gif'));
+    }
+
+    public function testGuessFileWithUnknownExtension()
+    {
+        $this->assertEquals('application/octet-stream', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/.unknownextension'));
+    }
+
+    public function testGuessWithIncorrectPath()
+    {
+        $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
+        MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/not_here');
+    }
+
+    public function testGuessWithNonReadablePath()
+    {
+        if ('\\' === \DIRECTORY_SEPARATOR) {
+            $this->markTestSkipped('Can not verify chmod operations on Windows');
+        }
+
+        if (!getenv('USER') || 'root' === getenv('USER')) {
+            $this->markTestSkipped('This test will fail if run under superuser');
+        }
+
+        $path = __DIR__.'/../Fixtures/to_delete';
+        touch($path);
+        @chmod($path, 0333);
+
+        if ('0333' == substr(sprintf('%o', fileperms($path)), -4)) {
+            $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException');
+            MimeTypeGuesser::getInstance()->guess($path);
+        } else {
+            $this->markTestSkipped('Can not verify chmod operations, change of file permissions failed');
+        }
+    }
+
+    public static function tearDownAfterClass()
+    {
+        $path = __DIR__.'/../Fixtures/to_delete';
+        if (file_exists($path)) {
+            @chmod($path, 0666);
+            @unlink($path);
+        }
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/File/UploadedFileTest.php b/vendor/symfony/http-foundation/Tests/File/UploadedFileTest.php
new file mode 100755
index 0000000..5a37cda
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/File/UploadedFileTest.php
@@ -0,0 +1,286 @@
+<?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\HttpFoundation\Tests\File;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\File\UploadedFile;
+
+class UploadedFileTest extends TestCase
+{
+    protected function setUp()
+    {
+        if (!ini_get('file_uploads')) {
+            $this->markTestSkipped('file_uploads is disabled in php.ini');
+        }
+    }
+
+    public function testConstructWhenFileNotExists()
+    {
+        $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
+
+        new UploadedFile(
+            __DIR__.'/Fixtures/not_here',
+            'original.gif',
+            null
+        );
+    }
+
+    public function testFileUploadsWithNoMimeType()
+    {
+        $file = new UploadedFile(
+            __DIR__.'/Fixtures/test.gif',
+            'original.gif',
+            null,
+            filesize(__DIR__.'/Fixtures/test.gif'),
+            UPLOAD_ERR_OK
+        );
+
+        $this->assertEquals('application/octet-stream', $file->getClientMimeType());
+
+        if (\extension_loaded('fileinfo')) {
+            $this->assertEquals('image/gif', $file->getMimeType());
+        }
+    }
+
+    public function testFileUploadsWithUnknownMimeType()
+    {
+        $file = new UploadedFile(
+            __DIR__.'/Fixtures/.unknownextension',
+            'original.gif',
+            null,
+            filesize(__DIR__.'/Fixtures/.unknownextension'),
+            UPLOAD_ERR_OK
+        );
+
+        $this->assertEquals('application/octet-stream', $file->getClientMimeType());
+    }
+
+    public function testGuessClientExtension()
+    {
+        $file = new UploadedFile(
+            __DIR__.'/Fixtures/test.gif',
+            'original.gif',
+            'image/gif',
+            filesize(__DIR__.'/Fixtures/test.gif'),
+            null
+        );
+
+        $this->assertEquals('gif', $file->guessClientExtension());
+    }
+
+    public function testGuessClientExtensionWithIncorrectMimeType()
+    {
+        $file = new UploadedFile(
+            __DIR__.'/Fixtures/test.gif',
+            'original.gif',
+            'image/jpeg',
+            filesize(__DIR__.'/Fixtures/test.gif'),
+            null
+        );
+
+        $this->assertEquals('jpeg', $file->guessClientExtension());
+    }
+
+    public function testCaseSensitiveMimeType()
+    {
+        $file = new UploadedFile(
+            __DIR__.'/Fixtures/case-sensitive-mime-type.xlsm',
+            'test.xlsm',
+            'application/vnd.ms-excel.sheet.macroEnabled.12',
+            filesize(__DIR__.'/Fixtures/case-sensitive-mime-type.xlsm'),
+            null
+        );
+
+        $this->assertEquals('xlsm', $file->guessClientExtension());
+    }
+
+    public function testErrorIsOkByDefault()
+    {
+        $file = new UploadedFile(
+            __DIR__.'/Fixtures/test.gif',
+            'original.gif',
+            'image/gif',
+            filesize(__DIR__.'/Fixtures/test.gif'),
+            null
+        );
+
+        $this->assertEquals(UPLOAD_ERR_OK, $file->getError());
+    }
+
+    public function testGetClientOriginalName()
+    {
+        $file = new UploadedFile(
+            __DIR__.'/Fixtures/test.gif',
+            'original.gif',
+            'image/gif',
+            filesize(__DIR__.'/Fixtures/test.gif'),
+            null
+        );
+
+        $this->assertEquals('original.gif', $file->getClientOriginalName());
+    }
+
+    public function testGetClientOriginalExtension()
+    {
+        $file = new UploadedFile(
+            __DIR__.'/Fixtures/test.gif',
+            'original.gif',
+            'image/gif',
+            filesize(__DIR__.'/Fixtures/test.gif'),
+            null
+        );
+
+        $this->assertEquals('gif', $file->getClientOriginalExtension());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\HttpFoundation\File\Exception\FileException
+     */
+    public function testMoveLocalFileIsNotAllowed()
+    {
+        $file = new UploadedFile(
+            __DIR__.'/Fixtures/test.gif',
+            'original.gif',
+            'image/gif',
+            filesize(__DIR__.'/Fixtures/test.gif'),
+            UPLOAD_ERR_OK
+        );
+
+        $movedFile = $file->move(__DIR__.'/Fixtures/directory');
+    }
+
+    public function testMoveLocalFileIsAllowedInTestMode()
+    {
+        $path = __DIR__.'/Fixtures/test.copy.gif';
+        $targetDir = __DIR__.'/Fixtures/directory';
+        $targetPath = $targetDir.'/test.copy.gif';
+        @unlink($path);
+        @unlink($targetPath);
+        copy(__DIR__.'/Fixtures/test.gif', $path);
+
+        $file = new UploadedFile(
+            $path,
+            'original.gif',
+            'image/gif',
+            filesize($path),
+            UPLOAD_ERR_OK,
+            true
+        );
+
+        $movedFile = $file->move(__DIR__.'/Fixtures/directory');
+
+        $this->assertFileExists($targetPath);
+        $this->assertFileNotExists($path);
+        $this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
+
+        @unlink($targetPath);
+    }
+
+    public function testGetClientOriginalNameSanitizeFilename()
+    {
+        $file = new UploadedFile(
+            __DIR__.'/Fixtures/test.gif',
+            '../../original.gif',
+            'image/gif',
+            filesize(__DIR__.'/Fixtures/test.gif'),
+            null
+        );
+
+        $this->assertEquals('original.gif', $file->getClientOriginalName());
+    }
+
+    public function testGetSize()
+    {
+        $file = new UploadedFile(
+            __DIR__.'/Fixtures/test.gif',
+            'original.gif',
+            'image/gif',
+            filesize(__DIR__.'/Fixtures/test.gif'),
+            null
+        );
+
+        $this->assertEquals(filesize(__DIR__.'/Fixtures/test.gif'), $file->getSize());
+
+        $file = new UploadedFile(
+            __DIR__.'/Fixtures/test',
+            'original.gif',
+            'image/gif'
+        );
+
+        $this->assertEquals(filesize(__DIR__.'/Fixtures/test'), $file->getSize());
+    }
+
+    public function testGetExtension()
+    {
+        $file = new UploadedFile(
+            __DIR__.'/Fixtures/test.gif',
+            'original.gif',
+            null
+        );
+
+        $this->assertEquals('gif', $file->getExtension());
+    }
+
+    public function testIsValid()
+    {
+        $file = new UploadedFile(
+            __DIR__.'/Fixtures/test.gif',
+            'original.gif',
+            null,
+            filesize(__DIR__.'/Fixtures/test.gif'),
+            UPLOAD_ERR_OK,
+            true
+        );
+
+        $this->assertTrue($file->isValid());
+    }
+
+    /**
+     * @dataProvider uploadedFileErrorProvider
+     */
+    public function testIsInvalidOnUploadError($error)
+    {
+        $file = new UploadedFile(
+            __DIR__.'/Fixtures/test.gif',
+            'original.gif',
+            null,
+            filesize(__DIR__.'/Fixtures/test.gif'),
+            $error
+        );
+
+        $this->assertFalse($file->isValid());
+    }
+
+    public function uploadedFileErrorProvider()
+    {
+        return [
+            [UPLOAD_ERR_INI_SIZE],
+            [UPLOAD_ERR_FORM_SIZE],
+            [UPLOAD_ERR_PARTIAL],
+            [UPLOAD_ERR_NO_TMP_DIR],
+            [UPLOAD_ERR_EXTENSION],
+        ];
+    }
+
+    public function testIsInvalidIfNotHttpUpload()
+    {
+        $file = new UploadedFile(
+            __DIR__.'/Fixtures/test.gif',
+            'original.gif',
+            null,
+            filesize(__DIR__.'/Fixtures/test.gif'),
+            UPLOAD_ERR_OK
+        );
+
+        $this->assertFalse($file->isValid());
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/FileBagTest.php b/vendor/symfony/http-foundation/Tests/FileBagTest.php
new file mode 100755
index 0000000..0b6d660
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/FileBagTest.php
@@ -0,0 +1,175 @@
+<?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\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\File\UploadedFile;
+use Symfony\Component\HttpFoundation\FileBag;
+
+/**
+ * FileBagTest.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Bulat Shakirzyanov <mallluhuct@gmail.com>
+ */
+class FileBagTest extends TestCase
+{
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testFileMustBeAnArrayOrUploadedFile()
+    {
+        new FileBag(['file' => 'foo']);
+    }
+
+    public function testShouldConvertsUploadedFiles()
+    {
+        $tmpFile = $this->createTempFile();
+        $file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0);
+
+        $bag = new FileBag(['file' => [
+            'name' => basename($tmpFile),
+            'type' => 'text/plain',
+            'tmp_name' => $tmpFile,
+            'error' => 0,
+            'size' => 100,
+        ]]);
+
+        $this->assertEquals($file, $bag->get('file'));
+    }
+
+    public function testShouldSetEmptyUploadedFilesToNull()
+    {
+        $bag = new FileBag(['file' => [
+            'name' => '',
+            'type' => '',
+            'tmp_name' => '',
+            'error' => UPLOAD_ERR_NO_FILE,
+            'size' => 0,
+        ]]);
+
+        $this->assertNull($bag->get('file'));
+    }
+
+    public function testShouldRemoveEmptyUploadedFilesForMultiUpload()
+    {
+        $bag = new FileBag(['files' => [
+            'name' => [''],
+            'type' => [''],
+            'tmp_name' => [''],
+            'error' => [UPLOAD_ERR_NO_FILE],
+            'size' => [0],
+        ]]);
+
+        $this->assertSame([], $bag->get('files'));
+    }
+
+    public function testShouldNotRemoveEmptyUploadedFilesForAssociativeArray()
+    {
+        $bag = new FileBag(['files' => [
+            'name' => ['file1' => ''],
+            'type' => ['file1' => ''],
+            'tmp_name' => ['file1' => ''],
+            'error' => ['file1' => UPLOAD_ERR_NO_FILE],
+            'size' => ['file1' => 0],
+        ]]);
+
+        $this->assertSame(['file1' => null], $bag->get('files'));
+    }
+
+    public function testShouldConvertUploadedFilesWithPhpBug()
+    {
+        $tmpFile = $this->createTempFile();
+        $file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0);
+
+        $bag = new FileBag([
+            'child' => [
+                'name' => [
+                    'file' => basename($tmpFile),
+                ],
+                'type' => [
+                    'file' => 'text/plain',
+                ],
+                'tmp_name' => [
+                    'file' => $tmpFile,
+                ],
+                'error' => [
+                    'file' => 0,
+                ],
+                'size' => [
+                    'file' => 100,
+                ],
+            ],
+        ]);
+
+        $files = $bag->all();
+        $this->assertEquals($file, $files['child']['file']);
+    }
+
+    public function testShouldConvertNestedUploadedFilesWithPhpBug()
+    {
+        $tmpFile = $this->createTempFile();
+        $file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0);
+
+        $bag = new FileBag([
+            'child' => [
+                'name' => [
+                    'sub' => ['file' => basename($tmpFile)],
+                ],
+                'type' => [
+                    'sub' => ['file' => 'text/plain'],
+                ],
+                'tmp_name' => [
+                    'sub' => ['file' => $tmpFile],
+                ],
+                'error' => [
+                    'sub' => ['file' => 0],
+                ],
+                'size' => [
+                    'sub' => ['file' => 100],
+                ],
+            ],
+        ]);
+
+        $files = $bag->all();
+        $this->assertEquals($file, $files['child']['sub']['file']);
+    }
+
+    public function testShouldNotConvertNestedUploadedFiles()
+    {
+        $tmpFile = $this->createTempFile();
+        $file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0);
+        $bag = new FileBag(['image' => ['file' => $file]]);
+
+        $files = $bag->all();
+        $this->assertEquals($file, $files['image']['file']);
+    }
+
+    protected function createTempFile()
+    {
+        return tempnam(sys_get_temp_dir().'/form_test', 'FormTest');
+    }
+
+    protected function setUp()
+    {
+        mkdir(sys_get_temp_dir().'/form_test', 0777, true);
+    }
+
+    protected function tearDown()
+    {
+        foreach (glob(sys_get_temp_dir().'/form_test/*') as $file) {
+            unlink($file);
+        }
+
+        rmdir(sys_get_temp_dir().'/form_test');
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/common.inc b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/common.inc
new file mode 100755
index 0000000..0bdf9e4
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/common.inc
@@ -0,0 +1,43 @@
+<?php
+
+use Symfony\Component\HttpFoundation\Response;
+
+$parent = __DIR__;
+while (!@file_exists($parent.'/vendor/autoload.php')) {
+    if (!@file_exists($parent)) {
+        // open_basedir restriction in effect
+        break;
+    }
+    if ($parent === dirname($parent)) {
+        echo "vendor/autoload.php not found\n";
+        exit(1);
+    }
+
+    $parent = dirname($parent);
+}
+
+require $parent.'/vendor/autoload.php';
+
+error_reporting(-1);
+ini_set('html_errors', 0);
+ini_set('display_errors', 1);
+
+if (filter_var(ini_get('xdebug.default_enable'), FILTER_VALIDATE_BOOLEAN)) {
+    xdebug_disable();
+}
+
+header_remove('X-Powered-By');
+header('Content-Type: text/plain; charset=utf-8');
+
+register_shutdown_function(function () {
+    echo "\n";
+    session_write_close();
+    print_r(headers_list());
+    echo "shutdown\n";
+});
+ob_start();
+
+$r = new Response();
+$r->headers->set('Date', 'Sat, 12 Nov 1955 20:04:00 GMT');
+
+return $r;
diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_max_age.expected b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_max_age.expected
new file mode 100755
index 0000000..bdb9d02
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_max_age.expected
@@ -0,0 +1,11 @@
+
+Warning: Expiry date cannot have a year greater than 9999 in %scookie_max_age.php on line 10
+
+Array
+(
+    [0] => Content-Type: text/plain; charset=utf-8
+    [1] => Cache-Control: no-cache, private
+    [2] => Date: Sat, 12 Nov 1955 20:04:00 GMT
+    [3] => Set-Cookie: foo=bar; expires=Sat, 01-Jan-10000 02:46:40 GMT; Max-Age=%d; path=/
+)
+shutdown
diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_max_age.php b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_max_age.php
new file mode 100755
index 0000000..8775a5c
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_max_age.php
@@ -0,0 +1,10 @@
+<?php
+
+use Symfony\Component\HttpFoundation\Cookie;
+
+$r = require __DIR__.'/common.inc';
+
+$r->headers->setCookie(new Cookie('foo', 'bar', 253402310800, '', null, false, false));
+$r->sendHeaders();
+
+setcookie('foo2', 'bar', 253402310800, '/');
diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.expected b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.expected
new file mode 100755
index 0000000..0c09797
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.expected
@@ -0,0 +1,10 @@
+
+Array
+(
+    [0] => Content-Type: text/plain; charset=utf-8
+    [1] => Cache-Control: no-cache, private
+    [2] => Date: Sat, 12 Nov 1955 20:04:00 GMT
+    [3] => Set-Cookie: ?*():@&+$/%#[]=?*():@&+$/%#[]; path=/
+    [4] => Set-Cookie: ?*():@&+$/%#[]=?*():@&+$/%#[]; path=/
+)
+shutdown
diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.php b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.php
new file mode 100755
index 0000000..2ca5b59
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.php
@@ -0,0 +1,12 @@
+<?php
+
+use Symfony\Component\HttpFoundation\Cookie;
+
+$r = require __DIR__.'/common.inc';
+
+$str = '?*():@&+$/%#[]';
+
+$r->headers->setCookie(new Cookie($str, $str, 0, '/', null, false, false, true));
+$r->sendHeaders();
+
+setrawcookie($str, $str, 0, '/', null, false, false);
diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_lax.expected b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_lax.expected
new file mode 100755
index 0000000..cbde2cb
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_lax.expected
@@ -0,0 +1,9 @@
+
+Array
+(
+    [0] => Content-Type: text/plain; charset=utf-8
+    [1] => Cache-Control: no-cache, private
+    [2] => Date: Sat, 12 Nov 1955 20:04:00 GMT
+    [3] => Set-Cookie: CookieSamesiteLaxTest=LaxValue; path=/; httponly; samesite=lax
+)
+shutdown
diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_lax.php b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_lax.php
new file mode 100755
index 0000000..9a476f1
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_lax.php
@@ -0,0 +1,8 @@
+<?php
+
+use Symfony\Component\HttpFoundation\Cookie;
+
+$r = require __DIR__.'/common.inc';
+
+$r->headers->setCookie(new Cookie('CookieSamesiteLaxTest', 'LaxValue', 0, '/', null, false, true, false, Cookie::SAMESITE_LAX));
+$r->sendHeaders();
diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_strict.expected b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_strict.expected
new file mode 100755
index 0000000..adc491f
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_strict.expected
@@ -0,0 +1,9 @@
+
+Array
+(
+    [0] => Content-Type: text/plain; charset=utf-8
+    [1] => Cache-Control: no-cache, private
+    [2] => Date: Sat, 12 Nov 1955 20:04:00 GMT
+    [3] => Set-Cookie: CookieSamesiteStrictTest=StrictValue; path=/; httponly; samesite=strict
+)
+shutdown
diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_strict.php b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_strict.php
new file mode 100755
index 0000000..3bcb41f
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_strict.php
@@ -0,0 +1,8 @@
+<?php
+
+use Symfony\Component\HttpFoundation\Cookie;
+
+$r = require __DIR__.'/common.inc';
+
+$r->headers->setCookie(new Cookie('CookieSamesiteStrictTest', 'StrictValue', 0, '/', null, false, true, false, Cookie::SAMESITE_STRICT));
+$r->sendHeaders();
diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_urlencode.expected b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_urlencode.expected
new file mode 100755
index 0000000..14e44a3
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_urlencode.expected
@@ -0,0 +1,10 @@
+
+Array
+(
+    [0] => Content-Type: text/plain; charset=utf-8
+    [1] => Cache-Control: no-cache, private
+    [2] => Date: Sat, 12 Nov 1955 20:04:00 GMT
+    [3] => Set-Cookie: ?*():@&+$/%#[]=%3F%2A%28%29%3A%40%26%2B%24%2F%25%23%5B%5D; path=/
+    [4] => Set-Cookie: ?*():@&+$/%#[]=%3F%2A%28%29%3A%40%26%2B%24%2F%25%23%5B%5D; path=/
+)
+shutdown
diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_urlencode.php b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_urlencode.php
new file mode 100755
index 0000000..05b9af3
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_urlencode.php
@@ -0,0 +1,12 @@
+<?php
+
+use Symfony\Component\HttpFoundation\Cookie;
+
+$r = require __DIR__.'/common.inc';
+
+$str = '?*():@&+$/%#[]';
+
+$r->headers->setCookie(new Cookie($str, $str, 0, '', null, false, false));
+$r->sendHeaders();
+
+setcookie($str, $str, 0, '/');
diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/invalid_cookie_name.expected b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/invalid_cookie_name.expected
new file mode 100755
index 0000000..2b560f0
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/invalid_cookie_name.expected
@@ -0,0 +1,6 @@
+The cookie name "Hello + world" contains invalid characters.
+Array
+(
+    [0] => Content-Type: text/plain; charset=utf-8
+)
+shutdown
diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/invalid_cookie_name.php b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/invalid_cookie_name.php
new file mode 100755
index 0000000..3fe1571
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/invalid_cookie_name.php
@@ -0,0 +1,11 @@
+<?php
+
+use Symfony\Component\HttpFoundation\Cookie;
+
+$r = require __DIR__.'/common.inc';
+
+try {
+    $r->headers->setCookie(new Cookie('Hello + world', 'hodor'));
+} catch (\InvalidArgumentException $e) {
+    echo $e->getMessage();
+}
diff --git a/vendor/symfony/http-foundation/Tests/HeaderBagTest.php b/vendor/symfony/http-foundation/Tests/HeaderBagTest.php
new file mode 100755
index 0000000..6c4915f
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/HeaderBagTest.php
@@ -0,0 +1,205 @@
+<?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\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\HeaderBag;
+
+class HeaderBagTest extends TestCase
+{
+    public function testConstructor()
+    {
+        $bag = new HeaderBag(['foo' => 'bar']);
+        $this->assertTrue($bag->has('foo'));
+    }
+
+    public function testToStringNull()
+    {
+        $bag = new HeaderBag();
+        $this->assertEquals('', $bag->__toString());
+    }
+
+    public function testToStringNotNull()
+    {
+        $bag = new HeaderBag(['foo' => 'bar']);
+        $this->assertEquals("Foo: bar\r\n", $bag->__toString());
+    }
+
+    public function testKeys()
+    {
+        $bag = new HeaderBag(['foo' => 'bar']);
+        $keys = $bag->keys();
+        $this->assertEquals('foo', $keys[0]);
+    }
+
+    public function testGetDate()
+    {
+        $bag = new HeaderBag(['foo' => 'Tue, 4 Sep 2012 20:00:00 +0200']);
+        $headerDate = $bag->getDate('foo');
+        $this->assertInstanceOf('DateTime', $headerDate);
+    }
+
+    /**
+     * @expectedException \RuntimeException
+     */
+    public function testGetDateException()
+    {
+        $bag = new HeaderBag(['foo' => 'Tue']);
+        $headerDate = $bag->getDate('foo');
+    }
+
+    public function testGetCacheControlHeader()
+    {
+        $bag = new HeaderBag();
+        $bag->addCacheControlDirective('public', '#a');
+        $this->assertTrue($bag->hasCacheControlDirective('public'));
+        $this->assertEquals('#a', $bag->getCacheControlDirective('public'));
+    }
+
+    public function testAll()
+    {
+        $bag = new HeaderBag(['foo' => 'bar']);
+        $this->assertEquals(['foo' => ['bar']], $bag->all(), '->all() gets all the input');
+
+        $bag = new HeaderBag(['FOO' => 'BAR']);
+        $this->assertEquals(['foo' => ['BAR']], $bag->all(), '->all() gets all the input key are lower case');
+    }
+
+    public function testReplace()
+    {
+        $bag = new HeaderBag(['foo' => 'bar']);
+
+        $bag->replace(['NOPE' => 'BAR']);
+        $this->assertEquals(['nope' => ['BAR']], $bag->all(), '->replace() replaces the input with the argument');
+        $this->assertFalse($bag->has('foo'), '->replace() overrides previously set the input');
+    }
+
+    public function testGet()
+    {
+        $bag = new HeaderBag(['foo' => 'bar', 'fuzz' => 'bizz']);
+        $this->assertEquals('bar', $bag->get('foo'), '->get return current value');
+        $this->assertEquals('bar', $bag->get('FoO'), '->get key in case insensitive');
+        $this->assertEquals(['bar'], $bag->get('foo', 'nope', false), '->get return the value as array');
+
+        // defaults
+        $this->assertNull($bag->get('none'), '->get unknown values returns null');
+        $this->assertEquals('default', $bag->get('none', 'default'), '->get unknown values returns default');
+        $this->assertEquals(['default'], $bag->get('none', 'default', false), '->get unknown values returns default as array');
+
+        $bag->set('foo', 'bor', false);
+        $this->assertEquals('bar', $bag->get('foo'), '->get return first value');
+        $this->assertEquals(['bar', 'bor'], $bag->get('foo', 'nope', false), '->get return all values as array');
+    }
+
+    public function testSetAssociativeArray()
+    {
+        $bag = new HeaderBag();
+        $bag->set('foo', ['bad-assoc-index' => 'value']);
+        $this->assertSame('value', $bag->get('foo'));
+        $this->assertEquals(['value'], $bag->get('foo', 'nope', false), 'assoc indices of multi-valued headers are ignored');
+    }
+
+    public function testContains()
+    {
+        $bag = new HeaderBag(['foo' => 'bar', 'fuzz' => 'bizz']);
+        $this->assertTrue($bag->contains('foo', 'bar'), '->contains first value');
+        $this->assertTrue($bag->contains('fuzz', 'bizz'), '->contains second value');
+        $this->assertFalse($bag->contains('nope', 'nope'), '->contains unknown value');
+        $this->assertFalse($bag->contains('foo', 'nope'), '->contains unknown value');
+
+        // Multiple values
+        $bag->set('foo', 'bor', false);
+        $this->assertTrue($bag->contains('foo', 'bar'), '->contains first value');
+        $this->assertTrue($bag->contains('foo', 'bor'), '->contains second value');
+        $this->assertFalse($bag->contains('foo', 'nope'), '->contains unknown value');
+    }
+
+    public function testCacheControlDirectiveAccessors()
+    {
+        $bag = new HeaderBag();
+        $bag->addCacheControlDirective('public');
+
+        $this->assertTrue($bag->hasCacheControlDirective('public'));
+        $this->assertTrue($bag->getCacheControlDirective('public'));
+        $this->assertEquals('public', $bag->get('cache-control'));
+
+        $bag->addCacheControlDirective('max-age', 10);
+        $this->assertTrue($bag->hasCacheControlDirective('max-age'));
+        $this->assertEquals(10, $bag->getCacheControlDirective('max-age'));
+        $this->assertEquals('max-age=10, public', $bag->get('cache-control'));
+
+        $bag->removeCacheControlDirective('max-age');
+        $this->assertFalse($bag->hasCacheControlDirective('max-age'));
+    }
+
+    public function testCacheControlDirectiveParsing()
+    {
+        $bag = new HeaderBag(['cache-control' => 'public, max-age=10']);
+        $this->assertTrue($bag->hasCacheControlDirective('public'));
+        $this->assertTrue($bag->getCacheControlDirective('public'));
+
+        $this->assertTrue($bag->hasCacheControlDirective('max-age'));
+        $this->assertEquals(10, $bag->getCacheControlDirective('max-age'));
+
+        $bag->addCacheControlDirective('s-maxage', 100);
+        $this->assertEquals('max-age=10, public, s-maxage=100', $bag->get('cache-control'));
+    }
+
+    public function testCacheControlDirectiveParsingQuotedZero()
+    {
+        $bag = new HeaderBag(['cache-control' => 'max-age="0"']);
+        $this->assertTrue($bag->hasCacheControlDirective('max-age'));
+        $this->assertEquals(0, $bag->getCacheControlDirective('max-age'));
+    }
+
+    public function testCacheControlDirectiveOverrideWithReplace()
+    {
+        $bag = new HeaderBag(['cache-control' => 'private, max-age=100']);
+        $bag->replace(['cache-control' => 'public, max-age=10']);
+        $this->assertTrue($bag->hasCacheControlDirective('public'));
+        $this->assertTrue($bag->getCacheControlDirective('public'));
+
+        $this->assertTrue($bag->hasCacheControlDirective('max-age'));
+        $this->assertEquals(10, $bag->getCacheControlDirective('max-age'));
+    }
+
+    public function testCacheControlClone()
+    {
+        $headers = ['foo' => 'bar'];
+        $bag1 = new HeaderBag($headers);
+        $bag2 = new HeaderBag($bag1->all());
+
+        $this->assertEquals($bag1->all(), $bag2->all());
+    }
+
+    public function testGetIterator()
+    {
+        $headers = ['foo' => 'bar', 'hello' => 'world', 'third' => 'charm'];
+        $headerBag = new HeaderBag($headers);
+
+        $i = 0;
+        foreach ($headerBag as $key => $val) {
+            ++$i;
+            $this->assertEquals([$headers[$key]], $val);
+        }
+
+        $this->assertEquals(\count($headers), $i);
+    }
+
+    public function testCount()
+    {
+        $headers = ['foo' => 'bar', 'HELLO' => 'WORLD'];
+        $headerBag = new HeaderBag($headers);
+
+        $this->assertCount(\count($headers), $headerBag);
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/IpUtilsTest.php b/vendor/symfony/http-foundation/Tests/IpUtilsTest.php
new file mode 100755
index 0000000..c7f76b5
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/IpUtilsTest.php
@@ -0,0 +1,104 @@
+<?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\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\IpUtils;
+
+class IpUtilsTest extends TestCase
+{
+    /**
+     * @dataProvider getIpv4Data
+     */
+    public function testIpv4($matches, $remoteAddr, $cidr)
+    {
+        $this->assertSame($matches, IpUtils::checkIp($remoteAddr, $cidr));
+    }
+
+    public function getIpv4Data()
+    {
+        return [
+            [true, '192.168.1.1', '192.168.1.1'],
+            [true, '192.168.1.1', '192.168.1.1/1'],
+            [true, '192.168.1.1', '192.168.1.0/24'],
+            [false, '192.168.1.1', '1.2.3.4/1'],
+            [false, '192.168.1.1', '192.168.1.1/33'], // invalid subnet
+            [true, '192.168.1.1', ['1.2.3.4/1', '192.168.1.0/24']],
+            [true, '192.168.1.1', ['192.168.1.0/24', '1.2.3.4/1']],
+            [false, '192.168.1.1', ['1.2.3.4/1', '4.3.2.1/1']],
+            [true, '1.2.3.4', '0.0.0.0/0'],
+            [true, '1.2.3.4', '192.168.1.0/0'],
+            [false, '1.2.3.4', '256.256.256/0'], // invalid CIDR notation
+            [false, 'an_invalid_ip', '192.168.1.0/24'],
+        ];
+    }
+
+    /**
+     * @dataProvider getIpv6Data
+     */
+    public function testIpv6($matches, $remoteAddr, $cidr)
+    {
+        if (!\defined('AF_INET6')) {
+            $this->markTestSkipped('Only works when PHP is compiled without the option "disable-ipv6".');
+        }
+
+        $this->assertSame($matches, IpUtils::checkIp($remoteAddr, $cidr));
+    }
+
+    public function getIpv6Data()
+    {
+        return [
+            [true, '2a01:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'],
+            [false, '2a00:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'],
+            [false, '2a01:198:603:0:396e:4789:8e99:890f', '::1'],
+            [true, '0:0:0:0:0:0:0:1', '::1'],
+            [false, '0:0:603:0:396e:4789:8e99:0001', '::1'],
+            [true, '0:0:603:0:396e:4789:8e99:0001', '::/0'],
+            [true, '0:0:603:0:396e:4789:8e99:0001', '2a01:198:603:0::/0'],
+            [true, '2a01:198:603:0:396e:4789:8e99:890f', ['::1', '2a01:198:603:0::/65']],
+            [true, '2a01:198:603:0:396e:4789:8e99:890f', ['2a01:198:603:0::/65', '::1']],
+            [false, '2a01:198:603:0:396e:4789:8e99:890f', ['::1', '1a01:198:603:0::/65']],
+            [false, '}__test|O:21:&quot;JDatabaseDriverMysqli&quot;:3:{s:2', '::1'],
+            [false, '2a01:198:603:0:396e:4789:8e99:890f', 'unknown'],
+        ];
+    }
+
+    /**
+     * @expectedException \RuntimeException
+     * @requires extension sockets
+     */
+    public function testAnIpv6WithOptionDisabledIpv6()
+    {
+        if (\defined('AF_INET6')) {
+            $this->markTestSkipped('Only works when PHP is compiled with the option "disable-ipv6".');
+        }
+
+        IpUtils::checkIp('2a01:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65');
+    }
+
+    /**
+     * @dataProvider invalidIpAddressData
+     */
+    public function testInvalidIpAddressesDoNotMatch($requestIp, $proxyIp)
+    {
+        $this->assertFalse(IpUtils::checkIp4($requestIp, $proxyIp));
+    }
+
+    public function invalidIpAddressData()
+    {
+        return [
+            'invalid proxy wildcard' => ['192.168.20.13', '*'],
+            'invalid proxy missing netmask' => ['192.168.20.13', '0.0.0.0'],
+            'invalid request IP with invalid proxy wildcard' => ['0.0.0.0', '*'],
+        ];
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/JsonResponseTest.php b/vendor/symfony/http-foundation/Tests/JsonResponseTest.php
new file mode 100755
index 0000000..5425896
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/JsonResponseTest.php
@@ -0,0 +1,266 @@
+<?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\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\JsonResponse;
+
+class JsonResponseTest extends TestCase
+{
+    protected function setUp()
+    {
+        parent::setUp();
+
+        if (!\defined('HHVM_VERSION')) {
+            $this->iniSet('serialize_precision', 14);
+        }
+    }
+
+    public function testConstructorEmptyCreatesJsonObject()
+    {
+        $response = new JsonResponse();
+        $this->assertSame('{}', $response->getContent());
+    }
+
+    public function testConstructorWithArrayCreatesJsonArray()
+    {
+        $response = new JsonResponse([0, 1, 2, 3]);
+        $this->assertSame('[0,1,2,3]', $response->getContent());
+    }
+
+    public function testConstructorWithAssocArrayCreatesJsonObject()
+    {
+        $response = new JsonResponse(['foo' => 'bar']);
+        $this->assertSame('{"foo":"bar"}', $response->getContent());
+    }
+
+    public function testConstructorWithSimpleTypes()
+    {
+        $response = new JsonResponse('foo');
+        $this->assertSame('"foo"', $response->getContent());
+
+        $response = new JsonResponse(0);
+        $this->assertSame('0', $response->getContent());
+
+        $response = new JsonResponse(0.1);
+        $this->assertSame('0.1', $response->getContent());
+
+        $response = new JsonResponse(true);
+        $this->assertSame('true', $response->getContent());
+    }
+
+    public function testConstructorWithCustomStatus()
+    {
+        $response = new JsonResponse([], 202);
+        $this->assertSame(202, $response->getStatusCode());
+    }
+
+    public function testConstructorAddsContentTypeHeader()
+    {
+        $response = new JsonResponse();
+        $this->assertSame('application/json', $response->headers->get('Content-Type'));
+    }
+
+    public function testConstructorWithCustomHeaders()
+    {
+        $response = new JsonResponse([], 200, ['ETag' => 'foo']);
+        $this->assertSame('application/json', $response->headers->get('Content-Type'));
+        $this->assertSame('foo', $response->headers->get('ETag'));
+    }
+
+    public function testConstructorWithCustomContentType()
+    {
+        $headers = ['Content-Type' => 'application/vnd.acme.blog-v1+json'];
+
+        $response = new JsonResponse([], 200, $headers);
+        $this->assertSame('application/vnd.acme.blog-v1+json', $response->headers->get('Content-Type'));
+    }
+
+    public function testSetJson()
+    {
+        $response = new JsonResponse('1', 200, [], true);
+        $this->assertEquals('1', $response->getContent());
+
+        $response = new JsonResponse('[1]', 200, [], true);
+        $this->assertEquals('[1]', $response->getContent());
+
+        $response = new JsonResponse(null, 200, []);
+        $response->setJson('true');
+        $this->assertEquals('true', $response->getContent());
+    }
+
+    public function testCreate()
+    {
+        $response = JsonResponse::create(['foo' => 'bar'], 204);
+
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
+        $this->assertEquals('{"foo":"bar"}', $response->getContent());
+        $this->assertEquals(204, $response->getStatusCode());
+    }
+
+    public function testStaticCreateEmptyJsonObject()
+    {
+        $response = JsonResponse::create();
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
+        $this->assertSame('{}', $response->getContent());
+    }
+
+    public function testStaticCreateJsonArray()
+    {
+        $response = JsonResponse::create([0, 1, 2, 3]);
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
+        $this->assertSame('[0,1,2,3]', $response->getContent());
+    }
+
+    public function testStaticCreateJsonObject()
+    {
+        $response = JsonResponse::create(['foo' => 'bar']);
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
+        $this->assertSame('{"foo":"bar"}', $response->getContent());
+    }
+
+    public function testStaticCreateWithSimpleTypes()
+    {
+        $response = JsonResponse::create('foo');
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
+        $this->assertSame('"foo"', $response->getContent());
+
+        $response = JsonResponse::create(0);
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
+        $this->assertSame('0', $response->getContent());
+
+        $response = JsonResponse::create(0.1);
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
+        $this->assertSame('0.1', $response->getContent());
+
+        $response = JsonResponse::create(true);
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
+        $this->assertSame('true', $response->getContent());
+    }
+
+    public function testStaticCreateWithCustomStatus()
+    {
+        $response = JsonResponse::create([], 202);
+        $this->assertSame(202, $response->getStatusCode());
+    }
+
+    public function testStaticCreateAddsContentTypeHeader()
+    {
+        $response = JsonResponse::create();
+        $this->assertSame('application/json', $response->headers->get('Content-Type'));
+    }
+
+    public function testStaticCreateWithCustomHeaders()
+    {
+        $response = JsonResponse::create([], 200, ['ETag' => 'foo']);
+        $this->assertSame('application/json', $response->headers->get('Content-Type'));
+        $this->assertSame('foo', $response->headers->get('ETag'));
+    }
+
+    public function testStaticCreateWithCustomContentType()
+    {
+        $headers = ['Content-Type' => 'application/vnd.acme.blog-v1+json'];
+
+        $response = JsonResponse::create([], 200, $headers);
+        $this->assertSame('application/vnd.acme.blog-v1+json', $response->headers->get('Content-Type'));
+    }
+
+    public function testSetCallback()
+    {
+        $response = JsonResponse::create(['foo' => 'bar'])->setCallback('callback');
+
+        $this->assertEquals('/**/callback({"foo":"bar"});', $response->getContent());
+        $this->assertEquals('text/javascript', $response->headers->get('Content-Type'));
+    }
+
+    public function testJsonEncodeFlags()
+    {
+        $response = new JsonResponse('<>\'&"');
+
+        $this->assertEquals('"\u003C\u003E\u0027\u0026\u0022"', $response->getContent());
+    }
+
+    public function testGetEncodingOptions()
+    {
+        $response = new JsonResponse();
+
+        $this->assertEquals(JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT, $response->getEncodingOptions());
+    }
+
+    public function testSetEncodingOptions()
+    {
+        $response = new JsonResponse();
+        $response->setData([[1, 2, 3]]);
+
+        $this->assertEquals('[[1,2,3]]', $response->getContent());
+
+        $response->setEncodingOptions(JSON_FORCE_OBJECT);
+
+        $this->assertEquals('{"0":{"0":1,"1":2,"2":3}}', $response->getContent());
+    }
+
+    public function testItAcceptsJsonAsString()
+    {
+        $response = JsonResponse::fromJsonString('{"foo":"bar"}');
+        $this->assertSame('{"foo":"bar"}', $response->getContent());
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testSetCallbackInvalidIdentifier()
+    {
+        $response = new JsonResponse('foo');
+        $response->setCallback('+invalid');
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testSetContent()
+    {
+        JsonResponse::create("\xB1\x31");
+    }
+
+    /**
+     * @expectedException \Exception
+     * @expectedExceptionMessage This error is expected
+     */
+    public function testSetContentJsonSerializeError()
+    {
+        if (!interface_exists('JsonSerializable', false)) {
+            $this->markTestSkipped('JsonSerializable is required.');
+        }
+
+        $serializable = new JsonSerializableObject();
+
+        JsonResponse::create($serializable);
+    }
+
+    public function testSetComplexCallback()
+    {
+        $response = JsonResponse::create(['foo' => 'bar']);
+        $response->setCallback('ಠ_ಠ["foo"].bar[0]');
+
+        $this->assertEquals('/**/ಠ_ಠ["foo"].bar[0]({"foo":"bar"});', $response->getContent());
+    }
+}
+
+if (interface_exists('JsonSerializable', false)) {
+    class JsonSerializableObject implements \JsonSerializable
+    {
+        public function jsonSerialize()
+        {
+            throw new \Exception('This error is expected');
+        }
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/ParameterBagTest.php b/vendor/symfony/http-foundation/Tests/ParameterBagTest.php
new file mode 100755
index 0000000..d2a5c99
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/ParameterBagTest.php
@@ -0,0 +1,194 @@
+<?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\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\ParameterBag;
+
+class ParameterBagTest extends TestCase
+{
+    public function testConstructor()
+    {
+        $this->testAll();
+    }
+
+    public function testAll()
+    {
+        $bag = new ParameterBag(['foo' => 'bar']);
+        $this->assertEquals(['foo' => 'bar'], $bag->all(), '->all() gets all the input');
+    }
+
+    public function testKeys()
+    {
+        $bag = new ParameterBag(['foo' => 'bar']);
+        $this->assertEquals(['foo'], $bag->keys());
+    }
+
+    public function testAdd()
+    {
+        $bag = new ParameterBag(['foo' => 'bar']);
+        $bag->add(['bar' => 'bas']);
+        $this->assertEquals(['foo' => 'bar', 'bar' => 'bas'], $bag->all());
+    }
+
+    public function testRemove()
+    {
+        $bag = new ParameterBag(['foo' => 'bar']);
+        $bag->add(['bar' => 'bas']);
+        $this->assertEquals(['foo' => 'bar', 'bar' => 'bas'], $bag->all());
+        $bag->remove('bar');
+        $this->assertEquals(['foo' => 'bar'], $bag->all());
+    }
+
+    public function testReplace()
+    {
+        $bag = new ParameterBag(['foo' => 'bar']);
+
+        $bag->replace(['FOO' => 'BAR']);
+        $this->assertEquals(['FOO' => 'BAR'], $bag->all(), '->replace() replaces the input with the argument');
+        $this->assertFalse($bag->has('foo'), '->replace() overrides previously set the input');
+    }
+
+    public function testGet()
+    {
+        $bag = new ParameterBag(['foo' => 'bar', 'null' => null]);
+
+        $this->assertEquals('bar', $bag->get('foo'), '->get() gets the value of a parameter');
+        $this->assertEquals('default', $bag->get('unknown', 'default'), '->get() returns second argument as default if a parameter is not defined');
+        $this->assertNull($bag->get('null', 'default'), '->get() returns null if null is set');
+    }
+
+    public function testGetDoesNotUseDeepByDefault()
+    {
+        $bag = new ParameterBag(['foo' => ['bar' => 'moo']]);
+
+        $this->assertNull($bag->get('foo[bar]'));
+    }
+
+    public function testSet()
+    {
+        $bag = new ParameterBag([]);
+
+        $bag->set('foo', 'bar');
+        $this->assertEquals('bar', $bag->get('foo'), '->set() sets the value of parameter');
+
+        $bag->set('foo', 'baz');
+        $this->assertEquals('baz', $bag->get('foo'), '->set() overrides previously set parameter');
+    }
+
+    public function testHas()
+    {
+        $bag = new ParameterBag(['foo' => 'bar']);
+
+        $this->assertTrue($bag->has('foo'), '->has() returns true if a parameter is defined');
+        $this->assertFalse($bag->has('unknown'), '->has() return false if a parameter is not defined');
+    }
+
+    public function testGetAlpha()
+    {
+        $bag = new ParameterBag(['word' => 'foo_BAR_012']);
+
+        $this->assertEquals('fooBAR', $bag->getAlpha('word'), '->getAlpha() gets only alphabetic characters');
+        $this->assertEquals('', $bag->getAlpha('unknown'), '->getAlpha() returns empty string if a parameter is not defined');
+    }
+
+    public function testGetAlnum()
+    {
+        $bag = new ParameterBag(['word' => 'foo_BAR_012']);
+
+        $this->assertEquals('fooBAR012', $bag->getAlnum('word'), '->getAlnum() gets only alphanumeric characters');
+        $this->assertEquals('', $bag->getAlnum('unknown'), '->getAlnum() returns empty string if a parameter is not defined');
+    }
+
+    public function testGetDigits()
+    {
+        $bag = new ParameterBag(['word' => 'foo_BAR_012']);
+
+        $this->assertEquals('012', $bag->getDigits('word'), '->getDigits() gets only digits as string');
+        $this->assertEquals('', $bag->getDigits('unknown'), '->getDigits() returns empty string if a parameter is not defined');
+    }
+
+    public function testGetInt()
+    {
+        $bag = new ParameterBag(['digits' => '0123']);
+
+        $this->assertEquals(123, $bag->getInt('digits'), '->getInt() gets a value of parameter as integer');
+        $this->assertEquals(0, $bag->getInt('unknown'), '->getInt() returns zero if a parameter is not defined');
+    }
+
+    public function testFilter()
+    {
+        $bag = new ParameterBag([
+            'digits' => '0123ab',
+            'email' => 'example@example.com',
+            'url' => 'http://example.com/foo',
+            'dec' => '256',
+            'hex' => '0x100',
+            'array' => ['bang'],
+            ]);
+
+        $this->assertEmpty($bag->filter('nokey'), '->filter() should return empty by default if no key is found');
+
+        $this->assertEquals('0123', $bag->filter('digits', '', FILTER_SANITIZE_NUMBER_INT), '->filter() gets a value of parameter as integer filtering out invalid characters');
+
+        $this->assertEquals('example@example.com', $bag->filter('email', '', FILTER_VALIDATE_EMAIL), '->filter() gets a value of parameter as email');
+
+        $this->assertEquals('http://example.com/foo', $bag->filter('url', '', FILTER_VALIDATE_URL, ['flags' => FILTER_FLAG_PATH_REQUIRED]), '->filter() gets a value of parameter as URL with a path');
+
+        // This test is repeated for code-coverage
+        $this->assertEquals('http://example.com/foo', $bag->filter('url', '', FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED), '->filter() gets a value of parameter as URL with a path');
+
+        $this->assertFalse($bag->filter('dec', '', FILTER_VALIDATE_INT, [
+            'flags' => FILTER_FLAG_ALLOW_HEX,
+            'options' => ['min_range' => 1, 'max_range' => 0xff],
+        ]), '->filter() gets a value of parameter as integer between boundaries');
+
+        $this->assertFalse($bag->filter('hex', '', FILTER_VALIDATE_INT, [
+            'flags' => FILTER_FLAG_ALLOW_HEX,
+            'options' => ['min_range' => 1, 'max_range' => 0xff],
+        ]), '->filter() gets a value of parameter as integer between boundaries');
+
+        $this->assertEquals(['bang'], $bag->filter('array', ''), '->filter() gets a value of parameter as an array');
+    }
+
+    public function testGetIterator()
+    {
+        $parameters = ['foo' => 'bar', 'hello' => 'world'];
+        $bag = new ParameterBag($parameters);
+
+        $i = 0;
+        foreach ($bag as $key => $val) {
+            ++$i;
+            $this->assertEquals($parameters[$key], $val);
+        }
+
+        $this->assertEquals(\count($parameters), $i);
+    }
+
+    public function testCount()
+    {
+        $parameters = ['foo' => 'bar', 'hello' => 'world'];
+        $bag = new ParameterBag($parameters);
+
+        $this->assertCount(\count($parameters), $bag);
+    }
+
+    public function testGetBoolean()
+    {
+        $parameters = ['string_true' => 'true', 'string_false' => 'false'];
+        $bag = new ParameterBag($parameters);
+
+        $this->assertTrue($bag->getBoolean('string_true'), '->getBoolean() gets the string true as boolean true');
+        $this->assertFalse($bag->getBoolean('string_false'), '->getBoolean() gets the string false as boolean false');
+        $this->assertFalse($bag->getBoolean('unknown'), '->getBoolean() returns false if a parameter is not defined');
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/RedirectResponseTest.php b/vendor/symfony/http-foundation/Tests/RedirectResponseTest.php
new file mode 100755
index 0000000..64c3e73
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/RedirectResponseTest.php
@@ -0,0 +1,97 @@
+<?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\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+
+class RedirectResponseTest extends TestCase
+{
+    public function testGenerateMetaRedirect()
+    {
+        $response = new RedirectResponse('foo.bar');
+
+        $this->assertEquals(1, preg_match(
+            '#<meta http-equiv="refresh" content="\d+;url=foo\.bar" />#',
+            preg_replace(['/\s+/', '/\'/'], [' ', '"'], $response->getContent())
+        ));
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testRedirectResponseConstructorNullUrl()
+    {
+        $response = new RedirectResponse(null);
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testRedirectResponseConstructorWrongStatusCode()
+    {
+        $response = new RedirectResponse('foo.bar', 404);
+    }
+
+    public function testGenerateLocationHeader()
+    {
+        $response = new RedirectResponse('foo.bar');
+
+        $this->assertTrue($response->headers->has('Location'));
+        $this->assertEquals('foo.bar', $response->headers->get('Location'));
+    }
+
+    public function testGetTargetUrl()
+    {
+        $response = new RedirectResponse('foo.bar');
+
+        $this->assertEquals('foo.bar', $response->getTargetUrl());
+    }
+
+    public function testSetTargetUrl()
+    {
+        $response = new RedirectResponse('foo.bar');
+        $response->setTargetUrl('baz.beep');
+
+        $this->assertEquals('baz.beep', $response->getTargetUrl());
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testSetTargetUrlNull()
+    {
+        $response = new RedirectResponse('foo.bar');
+        $response->setTargetUrl(null);
+    }
+
+    public function testCreate()
+    {
+        $response = RedirectResponse::create('foo', 301);
+
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse', $response);
+        $this->assertEquals(301, $response->getStatusCode());
+    }
+
+    public function testCacheHeaders()
+    {
+        $response = new RedirectResponse('foo.bar', 301);
+        $this->assertFalse($response->headers->hasCacheControlDirective('no-cache'));
+
+        $response = new RedirectResponse('foo.bar', 301, ['cache-control' => 'max-age=86400']);
+        $this->assertFalse($response->headers->hasCacheControlDirective('no-cache'));
+        $this->assertTrue($response->headers->hasCacheControlDirective('max-age'));
+
+        $response = new RedirectResponse('foo.bar', 302);
+        $this->assertTrue($response->headers->hasCacheControlDirective('no-cache'));
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/RequestMatcherTest.php b/vendor/symfony/http-foundation/Tests/RequestMatcherTest.php
new file mode 100755
index 0000000..7fb6925
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/RequestMatcherTest.php
@@ -0,0 +1,151 @@
+<?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\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\RequestMatcher;
+
+class RequestMatcherTest extends TestCase
+{
+    /**
+     * @dataProvider getMethodData
+     */
+    public function testMethod($requestMethod, $matcherMethod, $isMatch)
+    {
+        $matcher = new RequestMatcher();
+        $matcher->matchMethod($matcherMethod);
+        $request = Request::create('', $requestMethod);
+        $this->assertSame($isMatch, $matcher->matches($request));
+
+        $matcher = new RequestMatcher(null, null, $matcherMethod);
+        $request = Request::create('', $requestMethod);
+        $this->assertSame($isMatch, $matcher->matches($request));
+    }
+
+    public function getMethodData()
+    {
+        return [
+            ['get', 'get', true],
+            ['get', ['get', 'post'], true],
+            ['get', 'post', false],
+            ['get', 'GET', true],
+            ['get', ['GET', 'POST'], true],
+            ['get', 'POST', false],
+        ];
+    }
+
+    public function testScheme()
+    {
+        $httpRequest = $request = $request = Request::create('');
+        $httpsRequest = $request = $request = Request::create('', 'get', [], [], [], ['HTTPS' => 'on']);
+
+        $matcher = new RequestMatcher();
+        $matcher->matchScheme('https');
+        $this->assertFalse($matcher->matches($httpRequest));
+        $this->assertTrue($matcher->matches($httpsRequest));
+
+        $matcher->matchScheme('http');
+        $this->assertFalse($matcher->matches($httpsRequest));
+        $this->assertTrue($matcher->matches($httpRequest));
+
+        $matcher = new RequestMatcher();
+        $this->assertTrue($matcher->matches($httpsRequest));
+        $this->assertTrue($matcher->matches($httpRequest));
+    }
+
+    /**
+     * @dataProvider getHostData
+     */
+    public function testHost($pattern, $isMatch)
+    {
+        $matcher = new RequestMatcher();
+        $request = Request::create('', 'get', [], [], [], ['HTTP_HOST' => 'foo.example.com']);
+
+        $matcher->matchHost($pattern);
+        $this->assertSame($isMatch, $matcher->matches($request));
+
+        $matcher = new RequestMatcher(null, $pattern);
+        $this->assertSame($isMatch, $matcher->matches($request));
+    }
+
+    public function getHostData()
+    {
+        return [
+            ['.*\.example\.com', true],
+            ['\.example\.com$', true],
+            ['^.*\.example\.com$', true],
+            ['.*\.sensio\.com', false],
+            ['.*\.example\.COM', true],
+            ['\.example\.COM$', true],
+            ['^.*\.example\.COM$', true],
+            ['.*\.sensio\.COM', false],
+        ];
+    }
+
+    public function testPath()
+    {
+        $matcher = new RequestMatcher();
+
+        $request = Request::create('/admin/foo');
+
+        $matcher->matchPath('/admin/.*');
+        $this->assertTrue($matcher->matches($request));
+
+        $matcher->matchPath('/admin');
+        $this->assertTrue($matcher->matches($request));
+
+        $matcher->matchPath('^/admin/.*$');
+        $this->assertTrue($matcher->matches($request));
+
+        $matcher->matchMethod('/blog/.*');
+        $this->assertFalse($matcher->matches($request));
+    }
+
+    public function testPathWithLocaleIsNotSupported()
+    {
+        $matcher = new RequestMatcher();
+        $request = Request::create('/en/login');
+        $request->setLocale('en');
+
+        $matcher->matchPath('^/{_locale}/login$');
+        $this->assertFalse($matcher->matches($request));
+    }
+
+    public function testPathWithEncodedCharacters()
+    {
+        $matcher = new RequestMatcher();
+        $request = Request::create('/admin/fo%20o');
+        $matcher->matchPath('^/admin/fo o*$');
+        $this->assertTrue($matcher->matches($request));
+    }
+
+    public function testAttributes()
+    {
+        $matcher = new RequestMatcher();
+
+        $request = Request::create('/admin/foo');
+        $request->attributes->set('foo', 'foo_bar');
+
+        $matcher->matchAttribute('foo', 'foo_.*');
+        $this->assertTrue($matcher->matches($request));
+
+        $matcher->matchAttribute('foo', 'foo');
+        $this->assertTrue($matcher->matches($request));
+
+        $matcher->matchAttribute('foo', '^foo_bar$');
+        $this->assertTrue($matcher->matches($request));
+
+        $matcher->matchAttribute('foo', 'babar');
+        $this->assertFalse($matcher->matches($request));
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/RequestStackTest.php b/vendor/symfony/http-foundation/Tests/RequestStackTest.php
new file mode 100755
index 0000000..a84fb26
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/RequestStackTest.php
@@ -0,0 +1,70 @@
+<?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\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\RequestStack;
+
+class RequestStackTest extends TestCase
+{
+    public function testGetCurrentRequest()
+    {
+        $requestStack = new RequestStack();
+        $this->assertNull($requestStack->getCurrentRequest());
+
+        $request = Request::create('/foo');
+
+        $requestStack->push($request);
+        $this->assertSame($request, $requestStack->getCurrentRequest());
+
+        $this->assertSame($request, $requestStack->pop());
+        $this->assertNull($requestStack->getCurrentRequest());
+
+        $this->assertNull($requestStack->pop());
+    }
+
+    public function testGetMasterRequest()
+    {
+        $requestStack = new RequestStack();
+        $this->assertNull($requestStack->getMasterRequest());
+
+        $masterRequest = Request::create('/foo');
+        $subRequest = Request::create('/bar');
+
+        $requestStack->push($masterRequest);
+        $requestStack->push($subRequest);
+
+        $this->assertSame($masterRequest, $requestStack->getMasterRequest());
+    }
+
+    public function testGetParentRequest()
+    {
+        $requestStack = new RequestStack();
+        $this->assertNull($requestStack->getParentRequest());
+
+        $masterRequest = Request::create('/foo');
+
+        $requestStack->push($masterRequest);
+        $this->assertNull($requestStack->getParentRequest());
+
+        $firstSubRequest = Request::create('/bar');
+
+        $requestStack->push($firstSubRequest);
+        $this->assertSame($masterRequest, $requestStack->getParentRequest());
+
+        $secondSubRequest = Request::create('/baz');
+
+        $requestStack->push($secondSubRequest);
+        $this->assertSame($firstSubRequest, $requestStack->getParentRequest());
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/RequestTest.php b/vendor/symfony/http-foundation/Tests/RequestTest.php
new file mode 100755
index 0000000..d266e1f
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/RequestTest.php
@@ -0,0 +1,2446 @@
+<?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\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Session\Session;
+use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
+
+class RequestTest extends TestCase
+{
+    protected function tearDown()
+    {
+        Request::setTrustedProxies([], -1);
+        Request::setTrustedHosts([]);
+    }
+
+    public function testInitialize()
+    {
+        $request = new Request();
+
+        $request->initialize(['foo' => 'bar']);
+        $this->assertEquals('bar', $request->query->get('foo'), '->initialize() takes an array of query parameters as its first argument');
+
+        $request->initialize([], ['foo' => 'bar']);
+        $this->assertEquals('bar', $request->request->get('foo'), '->initialize() takes an array of request parameters as its second argument');
+
+        $request->initialize([], [], ['foo' => 'bar']);
+        $this->assertEquals('bar', $request->attributes->get('foo'), '->initialize() takes an array of attributes as its third argument');
+
+        $request->initialize([], [], [], [], [], ['HTTP_FOO' => 'bar']);
+        $this->assertEquals('bar', $request->headers->get('FOO'), '->initialize() takes an array of HTTP headers as its sixth argument');
+    }
+
+    public function testGetLocale()
+    {
+        $request = new Request();
+        $request->setLocale('pl');
+        $locale = $request->getLocale();
+        $this->assertEquals('pl', $locale);
+    }
+
+    public function testGetUser()
+    {
+        $request = Request::create('http://user:password@test.com');
+        $user = $request->getUser();
+
+        $this->assertEquals('user', $user);
+    }
+
+    public function testGetPassword()
+    {
+        $request = Request::create('http://user:password@test.com');
+        $password = $request->getPassword();
+
+        $this->assertEquals('password', $password);
+    }
+
+    public function testIsNoCache()
+    {
+        $request = new Request();
+        $isNoCache = $request->isNoCache();
+
+        $this->assertFalse($isNoCache);
+    }
+
+    public function testGetContentType()
+    {
+        $request = new Request();
+        $contentType = $request->getContentType();
+
+        $this->assertNull($contentType);
+    }
+
+    public function testSetDefaultLocale()
+    {
+        $request = new Request();
+        $request->setDefaultLocale('pl');
+        $locale = $request->getLocale();
+
+        $this->assertEquals('pl', $locale);
+    }
+
+    public function testCreate()
+    {
+        $request = Request::create('http://test.com/foo?bar=baz');
+        $this->assertEquals('http://test.com/foo?bar=baz', $request->getUri());
+        $this->assertEquals('/foo', $request->getPathInfo());
+        $this->assertEquals('bar=baz', $request->getQueryString());
+        $this->assertEquals(80, $request->getPort());
+        $this->assertEquals('test.com', $request->getHttpHost());
+        $this->assertFalse($request->isSecure());
+
+        $request = Request::create('http://test.com/foo', 'GET', ['bar' => 'baz']);
+        $this->assertEquals('http://test.com/foo?bar=baz', $request->getUri());
+        $this->assertEquals('/foo', $request->getPathInfo());
+        $this->assertEquals('bar=baz', $request->getQueryString());
+        $this->assertEquals(80, $request->getPort());
+        $this->assertEquals('test.com', $request->getHttpHost());
+        $this->assertFalse($request->isSecure());
+
+        $request = Request::create('http://test.com/foo?bar=foo', 'GET', ['bar' => 'baz']);
+        $this->assertEquals('http://test.com/foo?bar=baz', $request->getUri());
+        $this->assertEquals('/foo', $request->getPathInfo());
+        $this->assertEquals('bar=baz', $request->getQueryString());
+        $this->assertEquals(80, $request->getPort());
+        $this->assertEquals('test.com', $request->getHttpHost());
+        $this->assertFalse($request->isSecure());
+
+        $request = Request::create('https://test.com/foo?bar=baz');
+        $this->assertEquals('https://test.com/foo?bar=baz', $request->getUri());
+        $this->assertEquals('/foo', $request->getPathInfo());
+        $this->assertEquals('bar=baz', $request->getQueryString());
+        $this->assertEquals(443, $request->getPort());
+        $this->assertEquals('test.com', $request->getHttpHost());
+        $this->assertTrue($request->isSecure());
+
+        $request = Request::create('test.com:90/foo');
+        $this->assertEquals('http://test.com:90/foo', $request->getUri());
+        $this->assertEquals('/foo', $request->getPathInfo());
+        $this->assertEquals('test.com', $request->getHost());
+        $this->assertEquals('test.com:90', $request->getHttpHost());
+        $this->assertEquals(90, $request->getPort());
+        $this->assertFalse($request->isSecure());
+
+        $request = Request::create('https://test.com:90/foo');
+        $this->assertEquals('https://test.com:90/foo', $request->getUri());
+        $this->assertEquals('/foo', $request->getPathInfo());
+        $this->assertEquals('test.com', $request->getHost());
+        $this->assertEquals('test.com:90', $request->getHttpHost());
+        $this->assertEquals(90, $request->getPort());
+        $this->assertTrue($request->isSecure());
+
+        $request = Request::create('https://127.0.0.1:90/foo');
+        $this->assertEquals('https://127.0.0.1:90/foo', $request->getUri());
+        $this->assertEquals('/foo', $request->getPathInfo());
+        $this->assertEquals('127.0.0.1', $request->getHost());
+        $this->assertEquals('127.0.0.1:90', $request->getHttpHost());
+        $this->assertEquals(90, $request->getPort());
+        $this->assertTrue($request->isSecure());
+
+        $request = Request::create('https://[::1]:90/foo');
+        $this->assertEquals('https://[::1]:90/foo', $request->getUri());
+        $this->assertEquals('/foo', $request->getPathInfo());
+        $this->assertEquals('[::1]', $request->getHost());
+        $this->assertEquals('[::1]:90', $request->getHttpHost());
+        $this->assertEquals(90, $request->getPort());
+        $this->assertTrue($request->isSecure());
+
+        $request = Request::create('https://[::1]/foo');
+        $this->assertEquals('https://[::1]/foo', $request->getUri());
+        $this->assertEquals('/foo', $request->getPathInfo());
+        $this->assertEquals('[::1]', $request->getHost());
+        $this->assertEquals('[::1]', $request->getHttpHost());
+        $this->assertEquals(443, $request->getPort());
+        $this->assertTrue($request->isSecure());
+
+        $json = '{"jsonrpc":"2.0","method":"echo","id":7,"params":["Hello World"]}';
+        $request = Request::create('http://example.com/jsonrpc', 'POST', [], [], [], [], $json);
+        $this->assertEquals($json, $request->getContent());
+        $this->assertFalse($request->isSecure());
+
+        $request = Request::create('http://test.com');
+        $this->assertEquals('http://test.com/', $request->getUri());
+        $this->assertEquals('/', $request->getPathInfo());
+        $this->assertEquals('', $request->getQueryString());
+        $this->assertEquals(80, $request->getPort());
+        $this->assertEquals('test.com', $request->getHttpHost());
+        $this->assertFalse($request->isSecure());
+
+        $request = Request::create('http://test.com?test=1');
+        $this->assertEquals('http://test.com/?test=1', $request->getUri());
+        $this->assertEquals('/', $request->getPathInfo());
+        $this->assertEquals('test=1', $request->getQueryString());
+        $this->assertEquals(80, $request->getPort());
+        $this->assertEquals('test.com', $request->getHttpHost());
+        $this->assertFalse($request->isSecure());
+
+        $request = Request::create('http://test.com:90/?test=1');
+        $this->assertEquals('http://test.com:90/?test=1', $request->getUri());
+        $this->assertEquals('/', $request->getPathInfo());
+        $this->assertEquals('test=1', $request->getQueryString());
+        $this->assertEquals(90, $request->getPort());
+        $this->assertEquals('test.com:90', $request->getHttpHost());
+        $this->assertFalse($request->isSecure());
+
+        $request = Request::create('http://username:password@test.com');
+        $this->assertEquals('http://test.com/', $request->getUri());
+        $this->assertEquals('/', $request->getPathInfo());
+        $this->assertEquals('', $request->getQueryString());
+        $this->assertEquals(80, $request->getPort());
+        $this->assertEquals('test.com', $request->getHttpHost());
+        $this->assertEquals('username', $request->getUser());
+        $this->assertEquals('password', $request->getPassword());
+        $this->assertFalse($request->isSecure());
+
+        $request = Request::create('http://username@test.com');
+        $this->assertEquals('http://test.com/', $request->getUri());
+        $this->assertEquals('/', $request->getPathInfo());
+        $this->assertEquals('', $request->getQueryString());
+        $this->assertEquals(80, $request->getPort());
+        $this->assertEquals('test.com', $request->getHttpHost());
+        $this->assertEquals('username', $request->getUser());
+        $this->assertSame('', $request->getPassword());
+        $this->assertFalse($request->isSecure());
+
+        $request = Request::create('http://test.com/?foo');
+        $this->assertEquals('/?foo', $request->getRequestUri());
+        $this->assertEquals(['foo' => ''], $request->query->all());
+
+        // assume rewrite rule: (.*) --> app/app.php; app/ is a symlink to a symfony web/ directory
+        $request = Request::create('http://test.com/apparthotel-1234', 'GET', [], [], [],
+            [
+                'DOCUMENT_ROOT' => '/var/www/www.test.com',
+                'SCRIPT_FILENAME' => '/var/www/www.test.com/app/app.php',
+                'SCRIPT_NAME' => '/app/app.php',
+                'PHP_SELF' => '/app/app.php/apparthotel-1234',
+            ]);
+        $this->assertEquals('http://test.com/apparthotel-1234', $request->getUri());
+        $this->assertEquals('/apparthotel-1234', $request->getPathInfo());
+        $this->assertEquals('', $request->getQueryString());
+        $this->assertEquals(80, $request->getPort());
+        $this->assertEquals('test.com', $request->getHttpHost());
+        $this->assertFalse($request->isSecure());
+
+        // Fragment should not be included in the URI
+        $request = Request::create('http://test.com/foo#bar');
+        $this->assertEquals('http://test.com/foo', $request->getUri());
+    }
+
+    public function testCreateWithRequestUri()
+    {
+        $request = Request::create('http://test.com:80/foo');
+        $request->server->set('REQUEST_URI', 'http://test.com:80/foo');
+        $this->assertEquals('http://test.com/foo', $request->getUri());
+        $this->assertEquals('/foo', $request->getPathInfo());
+        $this->assertEquals('test.com', $request->getHost());
+        $this->assertEquals('test.com', $request->getHttpHost());
+        $this->assertEquals(80, $request->getPort());
+        $this->assertFalse($request->isSecure());
+
+        $request = Request::create('http://test.com:8080/foo');
+        $request->server->set('REQUEST_URI', 'http://test.com:8080/foo');
+        $this->assertEquals('http://test.com:8080/foo', $request->getUri());
+        $this->assertEquals('/foo', $request->getPathInfo());
+        $this->assertEquals('test.com', $request->getHost());
+        $this->assertEquals('test.com:8080', $request->getHttpHost());
+        $this->assertEquals(8080, $request->getPort());
+        $this->assertFalse($request->isSecure());
+
+        $request = Request::create('http://test.com/foo?bar=foo', 'GET', ['bar' => 'baz']);
+        $request->server->set('REQUEST_URI', 'http://test.com/foo?bar=foo');
+        $this->assertEquals('http://test.com/foo?bar=baz', $request->getUri());
+        $this->assertEquals('/foo', $request->getPathInfo());
+        $this->assertEquals('bar=baz', $request->getQueryString());
+        $this->assertEquals('test.com', $request->getHost());
+        $this->assertEquals('test.com', $request->getHttpHost());
+        $this->assertEquals(80, $request->getPort());
+        $this->assertFalse($request->isSecure());
+
+        $request = Request::create('https://test.com:443/foo');
+        $request->server->set('REQUEST_URI', 'https://test.com:443/foo');
+        $this->assertEquals('https://test.com/foo', $request->getUri());
+        $this->assertEquals('/foo', $request->getPathInfo());
+        $this->assertEquals('test.com', $request->getHost());
+        $this->assertEquals('test.com', $request->getHttpHost());
+        $this->assertEquals(443, $request->getPort());
+        $this->assertTrue($request->isSecure());
+
+        // Fragment should not be included in the URI
+        $request = Request::create('http://test.com/foo#bar');
+        $request->server->set('REQUEST_URI', 'http://test.com/foo#bar');
+        $this->assertEquals('http://test.com/foo', $request->getUri());
+    }
+
+    /**
+     * @dataProvider getRequestUriData
+     */
+    public function testGetRequestUri($serverRequestUri, $expected, $message)
+    {
+        $request = new Request();
+        $request->server->add([
+            'REQUEST_URI' => $serverRequestUri,
+
+            // For having http://test.com
+            'SERVER_NAME' => 'test.com',
+            'SERVER_PORT' => 80,
+        ]);
+
+        $this->assertSame($expected, $request->getRequestUri(), $message);
+        $this->assertSame($expected, $request->server->get('REQUEST_URI'), 'Normalize the request URI.');
+    }
+
+    public function getRequestUriData()
+    {
+        $message = 'Do not modify the path.';
+        yield ['/foo', '/foo', $message];
+        yield ['//bar/foo', '//bar/foo', $message];
+        yield ['///bar/foo', '///bar/foo', $message];
+
+        $message = 'Handle when the scheme, host are on REQUEST_URI.';
+        yield ['http://test.com/foo?bar=baz', '/foo?bar=baz', $message];
+
+        $message = 'Handle when the scheme, host and port are on REQUEST_URI.';
+        yield ['http://test.com:80/foo', '/foo', $message];
+        yield ['https://test.com:8080/foo', '/foo', $message];
+        yield ['https://test.com:443/foo', '/foo', $message];
+
+        $message = 'Fragment should not be included in the URI';
+        yield ['http://test.com/foo#bar', '/foo', $message];
+        yield ['/foo#bar', '/foo', $message];
+    }
+
+    public function testGetRequestUriWithoutRequiredHeader()
+    {
+        $expected = '';
+
+        $request = new Request();
+
+        $message = 'Fallback to empty URI when headers are missing.';
+        $this->assertSame($expected, $request->getRequestUri(), $message);
+        $this->assertSame($expected, $request->server->get('REQUEST_URI'), 'Normalize the request URI.');
+    }
+
+    public function testCreateCheckPrecedence()
+    {
+        // server is used by default
+        $request = Request::create('/', 'DELETE', [], [], [], [
+            'HTTP_HOST' => 'example.com',
+            'HTTPS' => 'on',
+            'SERVER_PORT' => 443,
+            'PHP_AUTH_USER' => 'fabien',
+            'PHP_AUTH_PW' => 'pa$$',
+            'QUERY_STRING' => 'foo=bar',
+            'CONTENT_TYPE' => 'application/json',
+        ]);
+        $this->assertEquals('example.com', $request->getHost());
+        $this->assertEquals(443, $request->getPort());
+        $this->assertTrue($request->isSecure());
+        $this->assertEquals('fabien', $request->getUser());
+        $this->assertEquals('pa$$', $request->getPassword());
+        $this->assertEquals('', $request->getQueryString());
+        $this->assertEquals('application/json', $request->headers->get('CONTENT_TYPE'));
+
+        // URI has precedence over server
+        $request = Request::create('http://thomas:pokemon@example.net:8080/?foo=bar', 'GET', [], [], [], [
+            'HTTP_HOST' => 'example.com',
+            'HTTPS' => 'on',
+            'SERVER_PORT' => 443,
+        ]);
+        $this->assertEquals('example.net', $request->getHost());
+        $this->assertEquals(8080, $request->getPort());
+        $this->assertFalse($request->isSecure());
+        $this->assertEquals('thomas', $request->getUser());
+        $this->assertEquals('pokemon', $request->getPassword());
+        $this->assertEquals('foo=bar', $request->getQueryString());
+    }
+
+    public function testDuplicate()
+    {
+        $request = new Request(['foo' => 'bar'], ['foo' => 'bar'], ['foo' => 'bar'], [], [], ['HTTP_FOO' => 'bar']);
+        $dup = $request->duplicate();
+
+        $this->assertEquals($request->query->all(), $dup->query->all(), '->duplicate() duplicates a request an copy the current query parameters');
+        $this->assertEquals($request->request->all(), $dup->request->all(), '->duplicate() duplicates a request an copy the current request parameters');
+        $this->assertEquals($request->attributes->all(), $dup->attributes->all(), '->duplicate() duplicates a request an copy the current attributes');
+        $this->assertEquals($request->headers->all(), $dup->headers->all(), '->duplicate() duplicates a request an copy the current HTTP headers');
+
+        $dup = $request->duplicate(['foo' => 'foobar'], ['foo' => 'foobar'], ['foo' => 'foobar'], [], [], ['HTTP_FOO' => 'foobar']);
+
+        $this->assertEquals(['foo' => 'foobar'], $dup->query->all(), '->duplicate() overrides the query parameters if provided');
+        $this->assertEquals(['foo' => 'foobar'], $dup->request->all(), '->duplicate() overrides the request parameters if provided');
+        $this->assertEquals(['foo' => 'foobar'], $dup->attributes->all(), '->duplicate() overrides the attributes if provided');
+        $this->assertEquals(['foo' => ['foobar']], $dup->headers->all(), '->duplicate() overrides the HTTP header if provided');
+    }
+
+    public function testDuplicateWithFormat()
+    {
+        $request = new Request([], [], ['_format' => 'json']);
+        $dup = $request->duplicate();
+
+        $this->assertEquals('json', $dup->getRequestFormat());
+        $this->assertEquals('json', $dup->attributes->get('_format'));
+
+        $request = new Request();
+        $request->setRequestFormat('xml');
+        $dup = $request->duplicate();
+
+        $this->assertEquals('xml', $dup->getRequestFormat());
+    }
+
+    /**
+     * @dataProvider getFormatToMimeTypeMapProviderWithAdditionalNullFormat
+     */
+    public function testGetFormatFromMimeType($format, $mimeTypes)
+    {
+        $request = new Request();
+        foreach ($mimeTypes as $mime) {
+            $this->assertEquals($format, $request->getFormat($mime));
+        }
+        $request->setFormat($format, $mimeTypes);
+        foreach ($mimeTypes as $mime) {
+            $this->assertEquals($format, $request->getFormat($mime));
+
+            if (null !== $format) {
+                $this->assertEquals($mimeTypes[0], $request->getMimeType($format));
+            }
+        }
+    }
+
+    public function getFormatToMimeTypeMapProviderWithAdditionalNullFormat()
+    {
+        return array_merge(
+            [[null, [null, 'unexistent-mime-type']]],
+            $this->getFormatToMimeTypeMapProvider()
+        );
+    }
+
+    public function testGetFormatFromMimeTypeWithParameters()
+    {
+        $request = new Request();
+        $this->assertEquals('json', $request->getFormat('application/json; charset=utf-8'));
+        $this->assertEquals('json', $request->getFormat('application/json;charset=utf-8'));
+        $this->assertEquals('json', $request->getFormat('application/json ; charset=utf-8'));
+        $this->assertEquals('json', $request->getFormat('application/json ;charset=utf-8'));
+    }
+
+    /**
+     * @dataProvider getFormatToMimeTypeMapProvider
+     */
+    public function testGetMimeTypeFromFormat($format, $mimeTypes)
+    {
+        $request = new Request();
+        $this->assertEquals($mimeTypes[0], $request->getMimeType($format));
+    }
+
+    /**
+     * @dataProvider getFormatToMimeTypeMapProvider
+     */
+    public function testGetMimeTypesFromFormat($format, $mimeTypes)
+    {
+        $this->assertEquals($mimeTypes, Request::getMimeTypes($format));
+    }
+
+    public function testGetMimeTypesFromInexistentFormat()
+    {
+        $request = new Request();
+        $this->assertNull($request->getMimeType('foo'));
+        $this->assertEquals([], Request::getMimeTypes('foo'));
+    }
+
+    public function testGetFormatWithCustomMimeType()
+    {
+        $request = new Request();
+        $request->setFormat('custom', 'application/vnd.foo.api;myversion=2.3');
+        $this->assertEquals('custom', $request->getFormat('application/vnd.foo.api;myversion=2.3'));
+    }
+
+    public function getFormatToMimeTypeMapProvider()
+    {
+        return [
+            ['txt', ['text/plain']],
+            ['js', ['application/javascript', 'application/x-javascript', 'text/javascript']],
+            ['css', ['text/css']],
+            ['json', ['application/json', 'application/x-json']],
+            ['jsonld', ['application/ld+json']],
+            ['xml', ['text/xml', 'application/xml', 'application/x-xml']],
+            ['rdf', ['application/rdf+xml']],
+            ['atom', ['application/atom+xml']],
+        ];
+    }
+
+    public function testGetUri()
+    {
+        $server = [];
+
+        // Standard Request on non default PORT
+        // http://host:8080/index.php/path/info?query=string
+
+        $server['HTTP_HOST'] = 'host:8080';
+        $server['SERVER_NAME'] = 'servername';
+        $server['SERVER_PORT'] = '8080';
+
+        $server['QUERY_STRING'] = 'query=string';
+        $server['REQUEST_URI'] = '/index.php/path/info?query=string';
+        $server['SCRIPT_NAME'] = '/index.php';
+        $server['PATH_INFO'] = '/path/info';
+        $server['PATH_TRANSLATED'] = 'redirect:/index.php/path/info';
+        $server['PHP_SELF'] = '/index_dev.php/path/info';
+        $server['SCRIPT_FILENAME'] = '/some/where/index.php';
+
+        $request = new Request();
+
+        $request->initialize([], [], [], [], [], $server);
+
+        $this->assertEquals('http://host:8080/index.php/path/info?query=string', $request->getUri(), '->getUri() with non default port');
+
+        // Use std port number
+        $server['HTTP_HOST'] = 'host';
+        $server['SERVER_NAME'] = 'servername';
+        $server['SERVER_PORT'] = '80';
+
+        $request->initialize([], [], [], [], [], $server);
+
+        $this->assertEquals('http://host/index.php/path/info?query=string', $request->getUri(), '->getUri() with default port');
+
+        // Without HOST HEADER
+        unset($server['HTTP_HOST']);
+        $server['SERVER_NAME'] = 'servername';
+        $server['SERVER_PORT'] = '80';
+
+        $request->initialize([], [], [], [], [], $server);
+
+        $this->assertEquals('http://servername/index.php/path/info?query=string', $request->getUri(), '->getUri() with default port without HOST_HEADER');
+
+        // Request with URL REWRITING (hide index.php)
+        //   RewriteCond %{REQUEST_FILENAME} !-f
+        //   RewriteRule ^(.*)$ index.php [QSA,L]
+        // http://host:8080/path/info?query=string
+        $server = [];
+        $server['HTTP_HOST'] = 'host:8080';
+        $server['SERVER_NAME'] = 'servername';
+        $server['SERVER_PORT'] = '8080';
+
+        $server['REDIRECT_QUERY_STRING'] = 'query=string';
+        $server['REDIRECT_URL'] = '/path/info';
+        $server['SCRIPT_NAME'] = '/index.php';
+        $server['QUERY_STRING'] = 'query=string';
+        $server['REQUEST_URI'] = '/path/info?toto=test&1=1';
+        $server['SCRIPT_NAME'] = '/index.php';
+        $server['PHP_SELF'] = '/index.php';
+        $server['SCRIPT_FILENAME'] = '/some/where/index.php';
+
+        $request->initialize([], [], [], [], [], $server);
+        $this->assertEquals('http://host:8080/path/info?query=string', $request->getUri(), '->getUri() with rewrite');
+
+        // Use std port number
+        //  http://host/path/info?query=string
+        $server['HTTP_HOST'] = 'host';
+        $server['SERVER_NAME'] = 'servername';
+        $server['SERVER_PORT'] = '80';
+
+        $request->initialize([], [], [], [], [], $server);
+
+        $this->assertEquals('http://host/path/info?query=string', $request->getUri(), '->getUri() with rewrite and default port');
+
+        // Without HOST HEADER
+        unset($server['HTTP_HOST']);
+        $server['SERVER_NAME'] = 'servername';
+        $server['SERVER_PORT'] = '80';
+
+        $request->initialize([], [], [], [], [], $server);
+
+        $this->assertEquals('http://servername/path/info?query=string', $request->getUri(), '->getUri() with rewrite, default port without HOST_HEADER');
+
+        // With encoded characters
+
+        $server = [
+            'HTTP_HOST' => 'host:8080',
+            'SERVER_NAME' => 'servername',
+            'SERVER_PORT' => '8080',
+            'QUERY_STRING' => 'query=string',
+            'REQUEST_URI' => '/ba%20se/index_dev.php/foo%20bar/in+fo?query=string',
+            'SCRIPT_NAME' => '/ba se/index_dev.php',
+            'PATH_TRANSLATED' => 'redirect:/index.php/foo bar/in+fo',
+            'PHP_SELF' => '/ba se/index_dev.php/path/info',
+            'SCRIPT_FILENAME' => '/some/where/ba se/index_dev.php',
+        ];
+
+        $request->initialize([], [], [], [], [], $server);
+
+        $this->assertEquals(
+            'http://host:8080/ba%20se/index_dev.php/foo%20bar/in+fo?query=string',
+            $request->getUri()
+        );
+
+        // with user info
+
+        $server['PHP_AUTH_USER'] = 'fabien';
+        $request->initialize([], [], [], [], [], $server);
+        $this->assertEquals('http://host:8080/ba%20se/index_dev.php/foo%20bar/in+fo?query=string', $request->getUri());
+
+        $server['PHP_AUTH_PW'] = 'symfony';
+        $request->initialize([], [], [], [], [], $server);
+        $this->assertEquals('http://host:8080/ba%20se/index_dev.php/foo%20bar/in+fo?query=string', $request->getUri());
+    }
+
+    public function testGetUriForPath()
+    {
+        $request = Request::create('http://test.com/foo?bar=baz');
+        $this->assertEquals('http://test.com/some/path', $request->getUriForPath('/some/path'));
+
+        $request = Request::create('http://test.com:90/foo?bar=baz');
+        $this->assertEquals('http://test.com:90/some/path', $request->getUriForPath('/some/path'));
+
+        $request = Request::create('https://test.com/foo?bar=baz');
+        $this->assertEquals('https://test.com/some/path', $request->getUriForPath('/some/path'));
+
+        $request = Request::create('https://test.com:90/foo?bar=baz');
+        $this->assertEquals('https://test.com:90/some/path', $request->getUriForPath('/some/path'));
+
+        $server = [];
+
+        // Standard Request on non default PORT
+        // http://host:8080/index.php/path/info?query=string
+
+        $server['HTTP_HOST'] = 'host:8080';
+        $server['SERVER_NAME'] = 'servername';
+        $server['SERVER_PORT'] = '8080';
+
+        $server['QUERY_STRING'] = 'query=string';
+        $server['REQUEST_URI'] = '/index.php/path/info?query=string';
+        $server['SCRIPT_NAME'] = '/index.php';
+        $server['PATH_INFO'] = '/path/info';
+        $server['PATH_TRANSLATED'] = 'redirect:/index.php/path/info';
+        $server['PHP_SELF'] = '/index_dev.php/path/info';
+        $server['SCRIPT_FILENAME'] = '/some/where/index.php';
+
+        $request = new Request();
+
+        $request->initialize([], [], [], [], [], $server);
+
+        $this->assertEquals('http://host:8080/index.php/some/path', $request->getUriForPath('/some/path'), '->getUriForPath() with non default port');
+
+        // Use std port number
+        $server['HTTP_HOST'] = 'host';
+        $server['SERVER_NAME'] = 'servername';
+        $server['SERVER_PORT'] = '80';
+
+        $request->initialize([], [], [], [], [], $server);
+
+        $this->assertEquals('http://host/index.php/some/path', $request->getUriForPath('/some/path'), '->getUriForPath() with default port');
+
+        // Without HOST HEADER
+        unset($server['HTTP_HOST']);
+        $server['SERVER_NAME'] = 'servername';
+        $server['SERVER_PORT'] = '80';
+
+        $request->initialize([], [], [], [], [], $server);
+
+        $this->assertEquals('http://servername/index.php/some/path', $request->getUriForPath('/some/path'), '->getUriForPath() with default port without HOST_HEADER');
+
+        // Request with URL REWRITING (hide index.php)
+        //   RewriteCond %{REQUEST_FILENAME} !-f
+        //   RewriteRule ^(.*)$ index.php [QSA,L]
+        // http://host:8080/path/info?query=string
+        $server = [];
+        $server['HTTP_HOST'] = 'host:8080';
+        $server['SERVER_NAME'] = 'servername';
+        $server['SERVER_PORT'] = '8080';
+
+        $server['REDIRECT_QUERY_STRING'] = 'query=string';
+        $server['REDIRECT_URL'] = '/path/info';
+        $server['SCRIPT_NAME'] = '/index.php';
+        $server['QUERY_STRING'] = 'query=string';
+        $server['REQUEST_URI'] = '/path/info?toto=test&1=1';
+        $server['SCRIPT_NAME'] = '/index.php';
+        $server['PHP_SELF'] = '/index.php';
+        $server['SCRIPT_FILENAME'] = '/some/where/index.php';
+
+        $request->initialize([], [], [], [], [], $server);
+        $this->assertEquals('http://host:8080/some/path', $request->getUriForPath('/some/path'), '->getUri() with rewrite');
+
+        // Use std port number
+        //  http://host/path/info?query=string
+        $server['HTTP_HOST'] = 'host';
+        $server['SERVER_NAME'] = 'servername';
+        $server['SERVER_PORT'] = '80';
+
+        $request->initialize([], [], [], [], [], $server);
+
+        $this->assertEquals('http://host/some/path', $request->getUriForPath('/some/path'), '->getUriForPath() with rewrite and default port');
+
+        // Without HOST HEADER
+        unset($server['HTTP_HOST']);
+        $server['SERVER_NAME'] = 'servername';
+        $server['SERVER_PORT'] = '80';
+
+        $request->initialize([], [], [], [], [], $server);
+
+        $this->assertEquals('http://servername/some/path', $request->getUriForPath('/some/path'), '->getUriForPath() with rewrite, default port without HOST_HEADER');
+        $this->assertEquals('servername', $request->getHttpHost());
+
+        // with user info
+
+        $server['PHP_AUTH_USER'] = 'fabien';
+        $request->initialize([], [], [], [], [], $server);
+        $this->assertEquals('http://servername/some/path', $request->getUriForPath('/some/path'));
+
+        $server['PHP_AUTH_PW'] = 'symfony';
+        $request->initialize([], [], [], [], [], $server);
+        $this->assertEquals('http://servername/some/path', $request->getUriForPath('/some/path'));
+    }
+
+    /**
+     * @dataProvider getRelativeUriForPathData()
+     */
+    public function testGetRelativeUriForPath($expected, $pathinfo, $path)
+    {
+        $this->assertEquals($expected, Request::create($pathinfo)->getRelativeUriForPath($path));
+    }
+
+    public function getRelativeUriForPathData()
+    {
+        return [
+            ['me.png', '/foo', '/me.png'],
+            ['../me.png', '/foo/bar', '/me.png'],
+            ['me.png', '/foo/bar', '/foo/me.png'],
+            ['../baz/me.png', '/foo/bar/b', '/foo/baz/me.png'],
+            ['../../fooz/baz/me.png', '/foo/bar/b', '/fooz/baz/me.png'],
+            ['baz/me.png', '/foo/bar/b', 'baz/me.png'],
+        ];
+    }
+
+    public function testGetUserInfo()
+    {
+        $request = new Request();
+
+        $server = ['PHP_AUTH_USER' => 'fabien'];
+        $request->initialize([], [], [], [], [], $server);
+        $this->assertEquals('fabien', $request->getUserInfo());
+
+        $server['PHP_AUTH_USER'] = '0';
+        $request->initialize([], [], [], [], [], $server);
+        $this->assertEquals('0', $request->getUserInfo());
+
+        $server['PHP_AUTH_PW'] = '0';
+        $request->initialize([], [], [], [], [], $server);
+        $this->assertEquals('0:0', $request->getUserInfo());
+    }
+
+    public function testGetSchemeAndHttpHost()
+    {
+        $request = new Request();
+
+        $server = [];
+        $server['SERVER_NAME'] = 'servername';
+        $server['SERVER_PORT'] = '90';
+        $request->initialize([], [], [], [], [], $server);
+        $this->assertEquals('http://servername:90', $request->getSchemeAndHttpHost());
+
+        $server['PHP_AUTH_USER'] = 'fabien';
+        $request->initialize([], [], [], [], [], $server);
+        $this->assertEquals('http://servername:90', $request->getSchemeAndHttpHost());
+
+        $server['PHP_AUTH_USER'] = '0';
+        $request->initialize([], [], [], [], [], $server);
+        $this->assertEquals('http://servername:90', $request->getSchemeAndHttpHost());
+
+        $server['PHP_AUTH_PW'] = '0';
+        $request->initialize([], [], [], [], [], $server);
+        $this->assertEquals('http://servername:90', $request->getSchemeAndHttpHost());
+    }
+
+    /**
+     * @dataProvider getQueryStringNormalizationData
+     */
+    public function testGetQueryString($query, $expectedQuery, $msg)
+    {
+        $request = new Request();
+
+        $request->server->set('QUERY_STRING', $query);
+        $this->assertSame($expectedQuery, $request->getQueryString(), $msg);
+    }
+
+    public function getQueryStringNormalizationData()
+    {
+        return [
+            ['foo', 'foo', 'works with valueless parameters'],
+            ['foo=', 'foo=', 'includes a dangling equal sign'],
+            ['bar=&foo=bar', 'bar=&foo=bar', '->works with empty parameters'],
+            ['foo=bar&bar=', 'bar=&foo=bar', 'sorts keys alphabetically'],
+
+            // GET parameters, that are submitted from a HTML form, encode spaces as "+" by default (as defined in enctype application/x-www-form-urlencoded).
+            // PHP also converts "+" to spaces when filling the global _GET or when using the function parse_str.
+            ['baz=Foo%20Baz&bar=Foo+Bar', 'bar=Foo%20Bar&baz=Foo%20Baz', 'normalizes spaces in both encodings "%20" and "+"'],
+
+            ['foo[]=1&foo[]=2', 'foo%5B%5D=1&foo%5B%5D=2', 'allows array notation'],
+            ['foo=1&foo=2', 'foo=1&foo=2', 'allows repeated parameters'],
+            ['pa%3Dram=foo%26bar%3Dbaz&test=test', 'pa%3Dram=foo%26bar%3Dbaz&test=test', 'works with encoded delimiters'],
+            ['0', '0', 'allows "0"'],
+            ['Foo Bar&Foo%20Baz', 'Foo%20Bar&Foo%20Baz', 'normalizes encoding in keys'],
+            ['bar=Foo Bar&baz=Foo%20Baz', 'bar=Foo%20Bar&baz=Foo%20Baz', 'normalizes encoding in values'],
+            ['foo=bar&&&test&&', 'foo=bar&test', 'removes unneeded delimiters'],
+            ['formula=e=m*c^2', 'formula=e%3Dm%2Ac%5E2', 'correctly treats only the first "=" as delimiter and the next as value'],
+
+            // Ignore pairs with empty key, even if there was a value, e.g. "=value", as such nameless values cannot be retrieved anyway.
+            // PHP also does not include them when building _GET.
+            ['foo=bar&=a=b&=x=y', 'foo=bar', 'removes params with empty key'],
+        ];
+    }
+
+    public function testGetQueryStringReturnsNull()
+    {
+        $request = new Request();
+
+        $this->assertNull($request->getQueryString(), '->getQueryString() returns null for non-existent query string');
+
+        $request->server->set('QUERY_STRING', '');
+        $this->assertNull($request->getQueryString(), '->getQueryString() returns null for empty query string');
+    }
+
+    public function testGetHost()
+    {
+        $request = new Request();
+
+        $request->initialize(['foo' => 'bar']);
+        $this->assertEquals('', $request->getHost(), '->getHost() return empty string if not initialized');
+
+        $request->initialize([], [], [], [], [], ['HTTP_HOST' => 'www.example.com']);
+        $this->assertEquals('www.example.com', $request->getHost(), '->getHost() from Host Header');
+
+        // Host header with port number
+        $request->initialize([], [], [], [], [], ['HTTP_HOST' => 'www.example.com:8080']);
+        $this->assertEquals('www.example.com', $request->getHost(), '->getHost() from Host Header with port number');
+
+        // Server values
+        $request->initialize([], [], [], [], [], ['SERVER_NAME' => 'www.example.com']);
+        $this->assertEquals('www.example.com', $request->getHost(), '->getHost() from server name');
+
+        $request->initialize([], [], [], [], [], ['SERVER_NAME' => 'www.example.com', 'HTTP_HOST' => 'www.host.com']);
+        $this->assertEquals('www.host.com', $request->getHost(), '->getHost() value from Host header has priority over SERVER_NAME ');
+    }
+
+    public function testGetPort()
+    {
+        $request = Request::create('http://example.com', 'GET', [], [], [], [
+            'HTTP_X_FORWARDED_PROTO' => 'https',
+            'HTTP_X_FORWARDED_PORT' => '443',
+        ]);
+        $port = $request->getPort();
+
+        $this->assertEquals(80, $port, 'Without trusted proxies FORWARDED_PROTO and FORWARDED_PORT are ignored.');
+
+        Request::setTrustedProxies(['1.1.1.1'], Request::HEADER_X_FORWARDED_ALL);
+        $request = Request::create('http://example.com', 'GET', [], [], [], [
+            'HTTP_X_FORWARDED_PROTO' => 'https',
+            'HTTP_X_FORWARDED_PORT' => '8443',
+        ]);
+        $this->assertEquals(80, $request->getPort(), 'With PROTO and PORT on untrusted connection server value takes precedence.');
+        $request->server->set('REMOTE_ADDR', '1.1.1.1');
+        $this->assertEquals(8443, $request->getPort(), 'With PROTO and PORT set PORT takes precedence.');
+
+        $request = Request::create('http://example.com', 'GET', [], [], [], [
+            'HTTP_X_FORWARDED_PROTO' => 'https',
+        ]);
+        $this->assertEquals(80, $request->getPort(), 'With only PROTO set getPort() ignores trusted headers on untrusted connection.');
+        $request->server->set('REMOTE_ADDR', '1.1.1.1');
+        $this->assertEquals(443, $request->getPort(), 'With only PROTO set getPort() defaults to 443.');
+
+        $request = Request::create('http://example.com', 'GET', [], [], [], [
+            'HTTP_X_FORWARDED_PROTO' => 'http',
+        ]);
+        $this->assertEquals(80, $request->getPort(), 'If X_FORWARDED_PROTO is set to HTTP getPort() ignores trusted headers on untrusted connection.');
+        $request->server->set('REMOTE_ADDR', '1.1.1.1');
+        $this->assertEquals(80, $request->getPort(), 'If X_FORWARDED_PROTO is set to HTTP getPort() returns port of the original request.');
+
+        $request = Request::create('http://example.com', 'GET', [], [], [], [
+            'HTTP_X_FORWARDED_PROTO' => 'On',
+        ]);
+        $this->assertEquals(80, $request->getPort(), 'With only PROTO set and value is On, getPort() ignores trusted headers on untrusted connection.');
+        $request->server->set('REMOTE_ADDR', '1.1.1.1');
+        $this->assertEquals(443, $request->getPort(), 'With only PROTO set and value is On, getPort() defaults to 443.');
+
+        $request = Request::create('http://example.com', 'GET', [], [], [], [
+            'HTTP_X_FORWARDED_PROTO' => '1',
+        ]);
+        $this->assertEquals(80, $request->getPort(), 'With only PROTO set and value is 1, getPort() ignores trusted headers on untrusted connection.');
+        $request->server->set('REMOTE_ADDR', '1.1.1.1');
+        $this->assertEquals(443, $request->getPort(), 'With only PROTO set and value is 1, getPort() defaults to 443.');
+
+        $request = Request::create('http://example.com', 'GET', [], [], [], [
+            'HTTP_X_FORWARDED_PROTO' => 'something-else',
+        ]);
+        $port = $request->getPort();
+        $this->assertEquals(80, $port, 'With only PROTO set and value is not recognized, getPort() defaults to 80.');
+    }
+
+    /**
+     * @expectedException \RuntimeException
+     */
+    public function testGetHostWithFakeHttpHostValue()
+    {
+        $request = new Request();
+        $request->initialize([], [], [], [], [], ['HTTP_HOST' => 'www.host.com?query=string']);
+        $request->getHost();
+    }
+
+    public function testGetSetMethod()
+    {
+        $request = new Request();
+
+        $this->assertEquals('GET', $request->getMethod(), '->getMethod() returns GET if no method is defined');
+
+        $request->setMethod('get');
+        $this->assertEquals('GET', $request->getMethod(), '->getMethod() returns an uppercased string');
+
+        $request->setMethod('PURGE');
+        $this->assertEquals('PURGE', $request->getMethod(), '->getMethod() returns the method even if it is not a standard one');
+
+        $request->setMethod('POST');
+        $this->assertEquals('POST', $request->getMethod(), '->getMethod() returns the method POST if no _method is defined');
+
+        $request->setMethod('POST');
+        $request->request->set('_method', 'purge');
+        $this->assertEquals('POST', $request->getMethod(), '->getMethod() does not return the method from _method if defined and POST but support not enabled');
+
+        $request = new Request();
+        $request->setMethod('POST');
+        $request->request->set('_method', 'purge');
+
+        $this->assertFalse(Request::getHttpMethodParameterOverride(), 'httpMethodParameterOverride should be disabled by default');
+
+        Request::enableHttpMethodParameterOverride();
+
+        $this->assertTrue(Request::getHttpMethodParameterOverride(), 'httpMethodParameterOverride should be enabled now but it is not');
+
+        $this->assertEquals('PURGE', $request->getMethod(), '->getMethod() returns the method from _method if defined and POST');
+        $this->disableHttpMethodParameterOverride();
+
+        $request = new Request();
+        $request->setMethod('POST');
+        $request->query->set('_method', 'purge');
+        $this->assertEquals('POST', $request->getMethod(), '->getMethod() does not return the method from _method if defined and POST but support not enabled');
+
+        $request = new Request();
+        $request->setMethod('POST');
+        $request->query->set('_method', 'purge');
+        Request::enableHttpMethodParameterOverride();
+        $this->assertEquals('PURGE', $request->getMethod(), '->getMethod() returns the method from _method if defined and POST');
+        $this->disableHttpMethodParameterOverride();
+
+        $request = new Request();
+        $request->setMethod('POST');
+        $request->headers->set('X-HTTP-METHOD-OVERRIDE', 'delete');
+        $this->assertEquals('DELETE', $request->getMethod(), '->getMethod() returns the method from X-HTTP-Method-Override even though _method is set if defined and POST');
+
+        $request = new Request();
+        $request->setMethod('POST');
+        $request->headers->set('X-HTTP-METHOD-OVERRIDE', 'delete');
+        $this->assertEquals('DELETE', $request->getMethod(), '->getMethod() returns the method from X-HTTP-Method-Override if defined and POST');
+
+        $request = new Request();
+        $request->setMethod('POST');
+        $request->query->set('_method', ['delete', 'patch']);
+        $this->assertSame('POST', $request->getMethod(), '->getMethod() returns the request method if invalid type is defined in query');
+    }
+
+    /**
+     * @dataProvider getClientIpsProvider
+     */
+    public function testGetClientIp($expected, $remoteAddr, $httpForwardedFor, $trustedProxies)
+    {
+        $request = $this->getRequestInstanceForClientIpTests($remoteAddr, $httpForwardedFor, $trustedProxies);
+
+        $this->assertEquals($expected[0], $request->getClientIp());
+    }
+
+    /**
+     * @dataProvider getClientIpsProvider
+     */
+    public function testGetClientIps($expected, $remoteAddr, $httpForwardedFor, $trustedProxies)
+    {
+        $request = $this->getRequestInstanceForClientIpTests($remoteAddr, $httpForwardedFor, $trustedProxies);
+
+        $this->assertEquals($expected, $request->getClientIps());
+    }
+
+    /**
+     * @dataProvider getClientIpsForwardedProvider
+     */
+    public function testGetClientIpsForwarded($expected, $remoteAddr, $httpForwarded, $trustedProxies)
+    {
+        $request = $this->getRequestInstanceForClientIpsForwardedTests($remoteAddr, $httpForwarded, $trustedProxies);
+
+        $this->assertEquals($expected, $request->getClientIps());
+    }
+
+    public function getClientIpsForwardedProvider()
+    {
+        //              $expected                                  $remoteAddr  $httpForwarded                                       $trustedProxies
+        return [
+            [['127.0.0.1'],                              '127.0.0.1', 'for="_gazonk"',                                      null],
+            [['127.0.0.1'],                              '127.0.0.1', 'for="_gazonk"',                                      ['127.0.0.1']],
+            [['88.88.88.88'],                            '127.0.0.1', 'for="88.88.88.88:80"',                               ['127.0.0.1']],
+            [['192.0.2.60'],                             '::1',       'for=192.0.2.60;proto=http;by=203.0.113.43',          ['::1']],
+            [['2620:0:1cfe:face:b00c::3', '192.0.2.43'], '::1',       'for=192.0.2.43, for=2620:0:1cfe:face:b00c::3',       ['::1']],
+            [['2001:db8:cafe::17'],                      '::1',       'for="[2001:db8:cafe::17]:4711',                      ['::1']],
+        ];
+    }
+
+    public function getClientIpsProvider()
+    {
+        //        $expected                          $remoteAddr                 $httpForwardedFor            $trustedProxies
+        return [
+            // simple IPv4
+            [['88.88.88.88'],              '88.88.88.88',              null,                        null],
+            // trust the IPv4 remote addr
+            [['88.88.88.88'],              '88.88.88.88',              null,                        ['88.88.88.88']],
+
+            // simple IPv6
+            [['::1'],                      '::1',                      null,                        null],
+            // trust the IPv6 remote addr
+            [['::1'],                      '::1',                      null,                        ['::1']],
+
+            // forwarded for with remote IPv4 addr not trusted
+            [['127.0.0.1'],                '127.0.0.1',                '88.88.88.88',               null],
+            // forwarded for with remote IPv4 addr trusted + comma
+            [['88.88.88.88'],              '127.0.0.1',                '88.88.88.88,',              ['127.0.0.1']],
+            // forwarded for with remote IPv4 and all FF addrs trusted
+            [['88.88.88.88'],              '127.0.0.1',                '88.88.88.88',               ['127.0.0.1', '88.88.88.88']],
+            // forwarded for with remote IPv4 range trusted
+            [['88.88.88.88'],              '123.45.67.89',             '88.88.88.88',               ['123.45.67.0/24']],
+
+            // forwarded for with remote IPv6 addr not trusted
+            [['1620:0:1cfe:face:b00c::3'], '1620:0:1cfe:face:b00c::3', '2620:0:1cfe:face:b00c::3',  null],
+            // forwarded for with remote IPv6 addr trusted
+            [['2620:0:1cfe:face:b00c::3'], '1620:0:1cfe:face:b00c::3', '2620:0:1cfe:face:b00c::3', ['1620:0:1cfe:face:b00c::3']],
+            // forwarded for with remote IPv6 range trusted
+            [['88.88.88.88'],              '2a01:198:603:0:396e:4789:8e99:890f', '88.88.88.88',     ['2a01:198:603:0::/65']],
+
+            // multiple forwarded for with remote IPv4 addr trusted
+            [['88.88.88.88', '87.65.43.21', '127.0.0.1'], '123.45.67.89', '127.0.0.1, 87.65.43.21, 88.88.88.88', ['123.45.67.89']],
+            // multiple forwarded for with remote IPv4 addr and some reverse proxies trusted
+            [['87.65.43.21', '127.0.0.1'], '123.45.67.89',             '127.0.0.1, 87.65.43.21, 88.88.88.88', ['123.45.67.89', '88.88.88.88']],
+            // multiple forwarded for with remote IPv4 addr and some reverse proxies trusted but in the middle
+            [['88.88.88.88', '127.0.0.1'], '123.45.67.89',             '127.0.0.1, 87.65.43.21, 88.88.88.88', ['123.45.67.89', '87.65.43.21']],
+            // multiple forwarded for with remote IPv4 addr and all reverse proxies trusted
+            [['127.0.0.1'],                '123.45.67.89',             '127.0.0.1, 87.65.43.21, 88.88.88.88', ['123.45.67.89', '87.65.43.21', '88.88.88.88', '127.0.0.1']],
+
+            // multiple forwarded for with remote IPv6 addr trusted
+            [['2620:0:1cfe:face:b00c::3', '3620:0:1cfe:face:b00c::3'], '1620:0:1cfe:face:b00c::3', '3620:0:1cfe:face:b00c::3,2620:0:1cfe:face:b00c::3', ['1620:0:1cfe:face:b00c::3']],
+            // multiple forwarded for with remote IPv6 addr and some reverse proxies trusted
+            [['3620:0:1cfe:face:b00c::3'], '1620:0:1cfe:face:b00c::3', '3620:0:1cfe:face:b00c::3,2620:0:1cfe:face:b00c::3', ['1620:0:1cfe:face:b00c::3', '2620:0:1cfe:face:b00c::3']],
+            // multiple forwarded for with remote IPv4 addr and some reverse proxies trusted but in the middle
+            [['2620:0:1cfe:face:b00c::3', '4620:0:1cfe:face:b00c::3'], '1620:0:1cfe:face:b00c::3', '4620:0:1cfe:face:b00c::3,3620:0:1cfe:face:b00c::3,2620:0:1cfe:face:b00c::3', ['1620:0:1cfe:face:b00c::3', '3620:0:1cfe:face:b00c::3']],
+
+            // client IP with port
+            [['88.88.88.88'], '127.0.0.1', '88.88.88.88:12345, 127.0.0.1', ['127.0.0.1']],
+
+            // invalid forwarded IP is ignored
+            [['88.88.88.88'], '127.0.0.1', 'unknown,88.88.88.88', ['127.0.0.1']],
+            [['88.88.88.88'], '127.0.0.1', '}__test|O:21:&quot;JDatabaseDriverMysqli&quot;:3:{s:2,88.88.88.88', ['127.0.0.1']],
+        ];
+    }
+
+    /**
+     * @expectedException \Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException
+     * @dataProvider getClientIpsWithConflictingHeadersProvider
+     */
+    public function testGetClientIpsWithConflictingHeaders($httpForwarded, $httpXForwardedFor)
+    {
+        $request = new Request();
+
+        $server = [
+            'REMOTE_ADDR' => '88.88.88.88',
+            'HTTP_FORWARDED' => $httpForwarded,
+            'HTTP_X_FORWARDED_FOR' => $httpXForwardedFor,
+        ];
+
+        Request::setTrustedProxies(['88.88.88.88'], Request::HEADER_X_FORWARDED_ALL | Request::HEADER_FORWARDED);
+
+        $request->initialize([], [], [], [], [], $server);
+
+        $request->getClientIps();
+    }
+
+    /**
+     * @dataProvider getClientIpsWithConflictingHeadersProvider
+     */
+    public function testGetClientIpsOnlyXHttpForwardedForTrusted($httpForwarded, $httpXForwardedFor)
+    {
+        $request = new Request();
+
+        $server = [
+            'REMOTE_ADDR' => '88.88.88.88',
+            'HTTP_FORWARDED' => $httpForwarded,
+            'HTTP_X_FORWARDED_FOR' => $httpXForwardedFor,
+        ];
+
+        Request::setTrustedProxies(['88.88.88.88'], Request::HEADER_X_FORWARDED_FOR);
+
+        $request->initialize([], [], [], [], [], $server);
+
+        $this->assertSame(array_reverse(explode(',', $httpXForwardedFor)), $request->getClientIps());
+    }
+
+    public function getClientIpsWithConflictingHeadersProvider()
+    {
+        //        $httpForwarded                   $httpXForwardedFor
+        return [
+            ['for=87.65.43.21',                 '192.0.2.60'],
+            ['for=87.65.43.21, for=192.0.2.60', '192.0.2.60'],
+            ['for=192.0.2.60',                  '192.0.2.60,87.65.43.21'],
+            ['for="::face", for=192.0.2.60',    '192.0.2.60,192.0.2.43'],
+            ['for=87.65.43.21, for=192.0.2.60', '192.0.2.60,87.65.43.21'],
+        ];
+    }
+
+    /**
+     * @dataProvider getClientIpsWithAgreeingHeadersProvider
+     */
+    public function testGetClientIpsWithAgreeingHeaders($httpForwarded, $httpXForwardedFor, $expectedIps)
+    {
+        $request = new Request();
+
+        $server = [
+            'REMOTE_ADDR' => '88.88.88.88',
+            'HTTP_FORWARDED' => $httpForwarded,
+            'HTTP_X_FORWARDED_FOR' => $httpXForwardedFor,
+        ];
+
+        Request::setTrustedProxies(['88.88.88.88'], -1);
+
+        $request->initialize([], [], [], [], [], $server);
+
+        $clientIps = $request->getClientIps();
+
+        $this->assertSame($expectedIps, $clientIps);
+    }
+
+    public function getClientIpsWithAgreeingHeadersProvider()
+    {
+        //        $httpForwarded                               $httpXForwardedFor
+        return [
+            ['for="192.0.2.60"',                          '192.0.2.60',             ['192.0.2.60']],
+            ['for=192.0.2.60, for=87.65.43.21',           '192.0.2.60,87.65.43.21', ['87.65.43.21', '192.0.2.60']],
+            ['for="[::face]", for=192.0.2.60',            '::face,192.0.2.60',      ['192.0.2.60', '::face']],
+            ['for="192.0.2.60:80"',                       '192.0.2.60',             ['192.0.2.60']],
+            ['for=192.0.2.60;proto=http;by=203.0.113.43', '192.0.2.60',             ['192.0.2.60']],
+            ['for="[2001:db8:cafe::17]:4711"',            '2001:db8:cafe::17',      ['2001:db8:cafe::17']],
+        ];
+    }
+
+    public function testGetContentWorksTwiceInDefaultMode()
+    {
+        $req = new Request();
+        $this->assertEquals('', $req->getContent());
+        $this->assertEquals('', $req->getContent());
+    }
+
+    public function testGetContentReturnsResource()
+    {
+        $req = new Request();
+        $retval = $req->getContent(true);
+        $this->assertInternalType('resource', $retval);
+        $this->assertEquals('', fread($retval, 1));
+        $this->assertTrue(feof($retval));
+    }
+
+    public function testGetContentReturnsResourceWhenContentSetInConstructor()
+    {
+        $req = new Request([], [], [], [], [], [], 'MyContent');
+        $resource = $req->getContent(true);
+
+        $this->assertInternalType('resource', $resource);
+        $this->assertEquals('MyContent', stream_get_contents($resource));
+    }
+
+    public function testContentAsResource()
+    {
+        $resource = fopen('php://memory', 'r+');
+        fwrite($resource, 'My other content');
+        rewind($resource);
+
+        $req = new Request([], [], [], [], [], [], $resource);
+        $this->assertEquals('My other content', stream_get_contents($req->getContent(true)));
+        $this->assertEquals('My other content', $req->getContent());
+    }
+
+    /**
+     * @expectedException \LogicException
+     * @dataProvider getContentCantBeCalledTwiceWithResourcesProvider
+     */
+    public function testGetContentCantBeCalledTwiceWithResources($first, $second)
+    {
+        if (\PHP_VERSION_ID >= 50600) {
+            $this->markTestSkipped('PHP >= 5.6 allows to open php://input several times.');
+        }
+
+        $req = new Request();
+        $req->getContent($first);
+        $req->getContent($second);
+    }
+
+    public function getContentCantBeCalledTwiceWithResourcesProvider()
+    {
+        return [
+            'Resource then fetch' => [true, false],
+            'Resource then resource' => [true, true],
+        ];
+    }
+
+    /**
+     * @dataProvider getContentCanBeCalledTwiceWithResourcesProvider
+     * @requires PHP 5.6
+     */
+    public function testGetContentCanBeCalledTwiceWithResources($first, $second)
+    {
+        $req = new Request();
+        $a = $req->getContent($first);
+        $b = $req->getContent($second);
+
+        if ($first) {
+            $a = stream_get_contents($a);
+        }
+
+        if ($second) {
+            $b = stream_get_contents($b);
+        }
+
+        $this->assertSame($a, $b);
+    }
+
+    public function getContentCanBeCalledTwiceWithResourcesProvider()
+    {
+        return [
+            'Fetch then fetch' => [false, false],
+            'Fetch then resource' => [false, true],
+            'Resource then fetch' => [true, false],
+            'Resource then resource' => [true, true],
+        ];
+    }
+
+    public function provideOverloadedMethods()
+    {
+        return [
+            ['PUT'],
+            ['DELETE'],
+            ['PATCH'],
+            ['put'],
+            ['delete'],
+            ['patch'],
+        ];
+    }
+
+    /**
+     * @dataProvider provideOverloadedMethods
+     */
+    public function testCreateFromGlobals($method)
+    {
+        $normalizedMethod = strtoupper($method);
+
+        $_GET['foo1'] = 'bar1';
+        $_POST['foo2'] = 'bar2';
+        $_COOKIE['foo3'] = 'bar3';
+        $_FILES['foo4'] = ['bar4'];
+        $_SERVER['foo5'] = 'bar5';
+
+        $request = Request::createFromGlobals();
+        $this->assertEquals('bar1', $request->query->get('foo1'), '::fromGlobals() uses values from $_GET');
+        $this->assertEquals('bar2', $request->request->get('foo2'), '::fromGlobals() uses values from $_POST');
+        $this->assertEquals('bar3', $request->cookies->get('foo3'), '::fromGlobals() uses values from $_COOKIE');
+        $this->assertEquals(['bar4'], $request->files->get('foo4'), '::fromGlobals() uses values from $_FILES');
+        $this->assertEquals('bar5', $request->server->get('foo5'), '::fromGlobals() uses values from $_SERVER');
+
+        unset($_GET['foo1'], $_POST['foo2'], $_COOKIE['foo3'], $_FILES['foo4'], $_SERVER['foo5']);
+
+        $_SERVER['REQUEST_METHOD'] = $method;
+        $_SERVER['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';
+        $request = RequestContentProxy::createFromGlobals();
+        $this->assertEquals($normalizedMethod, $request->getMethod());
+        $this->assertEquals('mycontent', $request->request->get('content'));
+
+        unset($_SERVER['REQUEST_METHOD'], $_SERVER['CONTENT_TYPE']);
+
+        Request::createFromGlobals();
+        Request::enableHttpMethodParameterOverride();
+        $_POST['_method'] = $method;
+        $_POST['foo6'] = 'bar6';
+        $_SERVER['REQUEST_METHOD'] = 'PoSt';
+        $request = Request::createFromGlobals();
+        $this->assertEquals($normalizedMethod, $request->getMethod());
+        $this->assertEquals('POST', $request->getRealMethod());
+        $this->assertEquals('bar6', $request->request->get('foo6'));
+
+        unset($_POST['_method'], $_POST['foo6'], $_SERVER['REQUEST_METHOD']);
+        $this->disableHttpMethodParameterOverride();
+    }
+
+    public function testOverrideGlobals()
+    {
+        $request = new Request();
+        $request->initialize(['foo' => 'bar']);
+
+        // as the Request::overrideGlobals really work, it erase $_SERVER, so we must backup it
+        $server = $_SERVER;
+
+        $request->overrideGlobals();
+
+        $this->assertEquals(['foo' => 'bar'], $_GET);
+
+        $request->initialize([], ['foo' => 'bar']);
+        $request->overrideGlobals();
+
+        $this->assertEquals(['foo' => 'bar'], $_POST);
+
+        $this->assertArrayNotHasKey('HTTP_X_FORWARDED_PROTO', $_SERVER);
+
+        $request->headers->set('X_FORWARDED_PROTO', 'https');
+
+        Request::setTrustedProxies(['1.1.1.1'], Request::HEADER_X_FORWARDED_ALL);
+        $this->assertFalse($request->isSecure());
+        $request->server->set('REMOTE_ADDR', '1.1.1.1');
+        $this->assertTrue($request->isSecure());
+
+        $request->overrideGlobals();
+
+        $this->assertArrayHasKey('HTTP_X_FORWARDED_PROTO', $_SERVER);
+
+        $request->headers->set('CONTENT_TYPE', 'multipart/form-data');
+        $request->headers->set('CONTENT_LENGTH', 12345);
+
+        $request->overrideGlobals();
+
+        $this->assertArrayHasKey('CONTENT_TYPE', $_SERVER);
+        $this->assertArrayHasKey('CONTENT_LENGTH', $_SERVER);
+
+        $request->initialize(['foo' => 'bar', 'baz' => 'foo']);
+        $request->query->remove('baz');
+
+        $request->overrideGlobals();
+
+        $this->assertEquals(['foo' => 'bar'], $_GET);
+        $this->assertEquals('foo=bar', $_SERVER['QUERY_STRING']);
+        $this->assertEquals('foo=bar', $request->server->get('QUERY_STRING'));
+
+        // restore initial $_SERVER array
+        $_SERVER = $server;
+    }
+
+    public function testGetScriptName()
+    {
+        $request = new Request();
+        $this->assertEquals('', $request->getScriptName());
+
+        $server = [];
+        $server['SCRIPT_NAME'] = '/index.php';
+
+        $request->initialize([], [], [], [], [], $server);
+
+        $this->assertEquals('/index.php', $request->getScriptName());
+
+        $server = [];
+        $server['ORIG_SCRIPT_NAME'] = '/frontend.php';
+        $request->initialize([], [], [], [], [], $server);
+
+        $this->assertEquals('/frontend.php', $request->getScriptName());
+
+        $server = [];
+        $server['SCRIPT_NAME'] = '/index.php';
+        $server['ORIG_SCRIPT_NAME'] = '/frontend.php';
+        $request->initialize([], [], [], [], [], $server);
+
+        $this->assertEquals('/index.php', $request->getScriptName());
+    }
+
+    public function testGetBasePath()
+    {
+        $request = new Request();
+        $this->assertEquals('', $request->getBasePath());
+
+        $server = [];
+        $server['SCRIPT_FILENAME'] = '/some/where/index.php';
+        $request->initialize([], [], [], [], [], $server);
+        $this->assertEquals('', $request->getBasePath());
+
+        $server = [];
+        $server['SCRIPT_FILENAME'] = '/some/where/index.php';
+        $server['SCRIPT_NAME'] = '/index.php';
+        $request->initialize([], [], [], [], [], $server);
+
+        $this->assertEquals('', $request->getBasePath());
+
+        $server = [];
+        $server['SCRIPT_FILENAME'] = '/some/where/index.php';
+        $server['PHP_SELF'] = '/index.php';
+        $request->initialize([], [], [], [], [], $server);
+
+        $this->assertEquals('', $request->getBasePath());
+
+        $server = [];
+        $server['SCRIPT_FILENAME'] = '/some/where/index.php';
+        $server['ORIG_SCRIPT_NAME'] = '/index.php';
+        $request->initialize([], [], [], [], [], $server);
+
+        $this->assertEquals('', $request->getBasePath());
+    }
+
+    public function testGetPathInfo()
+    {
+        $request = new Request();
+        $this->assertEquals('/', $request->getPathInfo());
+
+        $server = [];
+        $server['REQUEST_URI'] = '/path/info';
+        $request->initialize([], [], [], [], [], $server);
+
+        $this->assertEquals('/path/info', $request->getPathInfo());
+
+        $server = [];
+        $server['REQUEST_URI'] = '/path%20test/info';
+        $request->initialize([], [], [], [], [], $server);
+
+        $this->assertEquals('/path%20test/info', $request->getPathInfo());
+
+        $server = [];
+        $server['REQUEST_URI'] = '?a=b';
+        $request->initialize([], [], [], [], [], $server);
+
+        $this->assertEquals('/', $request->getPathInfo());
+    }
+
+    public function testGetParameterPrecedence()
+    {
+        $request = new Request();
+        $request->attributes->set('foo', 'attr');
+        $request->query->set('foo', 'query');
+        $request->request->set('foo', 'body');
+
+        $this->assertSame('attr', $request->get('foo'));
+
+        $request->attributes->remove('foo');
+        $this->assertSame('query', $request->get('foo'));
+
+        $request->query->remove('foo');
+        $this->assertSame('body', $request->get('foo'));
+
+        $request->request->remove('foo');
+        $this->assertNull($request->get('foo'));
+    }
+
+    public function testGetPreferredLanguage()
+    {
+        $request = new Request();
+        $this->assertNull($request->getPreferredLanguage());
+        $this->assertNull($request->getPreferredLanguage([]));
+        $this->assertEquals('fr', $request->getPreferredLanguage(['fr']));
+        $this->assertEquals('fr', $request->getPreferredLanguage(['fr', 'en']));
+        $this->assertEquals('en', $request->getPreferredLanguage(['en', 'fr']));
+        $this->assertEquals('fr-ch', $request->getPreferredLanguage(['fr-ch', 'fr-fr']));
+
+        $request = new Request();
+        $request->headers->set('Accept-language', 'zh, en-us; q=0.8, en; q=0.6');
+        $this->assertEquals('en', $request->getPreferredLanguage(['en', 'en-us']));
+
+        $request = new Request();
+        $request->headers->set('Accept-language', 'zh, en-us; q=0.8, en; q=0.6');
+        $this->assertEquals('en', $request->getPreferredLanguage(['fr', 'en']));
+
+        $request = new Request();
+        $request->headers->set('Accept-language', 'zh, en-us; q=0.8');
+        $this->assertEquals('en', $request->getPreferredLanguage(['fr', 'en']));
+
+        $request = new Request();
+        $request->headers->set('Accept-language', 'zh, en-us; q=0.8, fr-fr; q=0.6, fr; q=0.5');
+        $this->assertEquals('en', $request->getPreferredLanguage(['fr', 'en']));
+    }
+
+    public function testIsXmlHttpRequest()
+    {
+        $request = new Request();
+        $this->assertFalse($request->isXmlHttpRequest());
+
+        $request->headers->set('X-Requested-With', 'XMLHttpRequest');
+        $this->assertTrue($request->isXmlHttpRequest());
+
+        $request->headers->remove('X-Requested-With');
+        $this->assertFalse($request->isXmlHttpRequest());
+    }
+
+    /**
+     * @requires extension intl
+     */
+    public function testIntlLocale()
+    {
+        $request = new Request();
+
+        $request->setDefaultLocale('fr');
+        $this->assertEquals('fr', $request->getLocale());
+        $this->assertEquals('fr', \Locale::getDefault());
+
+        $request->setLocale('en');
+        $this->assertEquals('en', $request->getLocale());
+        $this->assertEquals('en', \Locale::getDefault());
+
+        $request->setDefaultLocale('de');
+        $this->assertEquals('en', $request->getLocale());
+        $this->assertEquals('en', \Locale::getDefault());
+    }
+
+    public function testGetCharsets()
+    {
+        $request = new Request();
+        $this->assertEquals([], $request->getCharsets());
+        $request->headers->set('Accept-Charset', 'ISO-8859-1, US-ASCII, UTF-8; q=0.8, ISO-10646-UCS-2; q=0.6');
+        $this->assertEquals([], $request->getCharsets()); // testing caching
+
+        $request = new Request();
+        $request->headers->set('Accept-Charset', 'ISO-8859-1, US-ASCII, UTF-8; q=0.8, ISO-10646-UCS-2; q=0.6');
+        $this->assertEquals(['ISO-8859-1', 'US-ASCII', 'UTF-8', 'ISO-10646-UCS-2'], $request->getCharsets());
+
+        $request = new Request();
+        $request->headers->set('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.7');
+        $this->assertEquals(['ISO-8859-1', 'utf-8', '*'], $request->getCharsets());
+    }
+
+    public function testGetEncodings()
+    {
+        $request = new Request();
+        $this->assertEquals([], $request->getEncodings());
+        $request->headers->set('Accept-Encoding', 'gzip,deflate,sdch');
+        $this->assertEquals([], $request->getEncodings()); // testing caching
+
+        $request = new Request();
+        $request->headers->set('Accept-Encoding', 'gzip,deflate,sdch');
+        $this->assertEquals(['gzip', 'deflate', 'sdch'], $request->getEncodings());
+
+        $request = new Request();
+        $request->headers->set('Accept-Encoding', 'gzip;q=0.4,deflate;q=0.9,compress;q=0.7');
+        $this->assertEquals(['deflate', 'compress', 'gzip'], $request->getEncodings());
+    }
+
+    public function testGetAcceptableContentTypes()
+    {
+        $request = new Request();
+        $this->assertEquals([], $request->getAcceptableContentTypes());
+        $request->headers->set('Accept', 'application/vnd.wap.wmlscriptc, text/vnd.wap.wml, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/html, multipart/mixed, */*');
+        $this->assertEquals([], $request->getAcceptableContentTypes()); // testing caching
+
+        $request = new Request();
+        $request->headers->set('Accept', 'application/vnd.wap.wmlscriptc, text/vnd.wap.wml, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/html, multipart/mixed, */*');
+        $this->assertEquals(['application/vnd.wap.wmlscriptc', 'text/vnd.wap.wml', 'application/vnd.wap.xhtml+xml', 'application/xhtml+xml', 'text/html', 'multipart/mixed', '*/*'], $request->getAcceptableContentTypes());
+    }
+
+    public function testGetLanguages()
+    {
+        $request = new Request();
+        $this->assertEquals([], $request->getLanguages());
+
+        $request = new Request();
+        $request->headers->set('Accept-language', 'zh, en-us; q=0.8, en; q=0.6');
+        $this->assertEquals(['zh', 'en_US', 'en'], $request->getLanguages());
+        $this->assertEquals(['zh', 'en_US', 'en'], $request->getLanguages());
+
+        $request = new Request();
+        $request->headers->set('Accept-language', 'zh, en-us; q=0.6, en; q=0.8');
+        $this->assertEquals(['zh', 'en', 'en_US'], $request->getLanguages()); // Test out of order qvalues
+
+        $request = new Request();
+        $request->headers->set('Accept-language', 'zh, en, en-us');
+        $this->assertEquals(['zh', 'en', 'en_US'], $request->getLanguages()); // Test equal weighting without qvalues
+
+        $request = new Request();
+        $request->headers->set('Accept-language', 'zh; q=0.6, en, en-us; q=0.6');
+        $this->assertEquals(['en', 'zh', 'en_US'], $request->getLanguages()); // Test equal weighting with qvalues
+
+        $request = new Request();
+        $request->headers->set('Accept-language', 'zh, i-cherokee; q=0.6');
+        $this->assertEquals(['zh', 'cherokee'], $request->getLanguages());
+    }
+
+    public function testGetRequestFormat()
+    {
+        $request = new Request();
+        $this->assertEquals('html', $request->getRequestFormat());
+
+        // Ensure that setting different default values over time is possible,
+        // aka. setRequestFormat determines the state.
+        $this->assertEquals('json', $request->getRequestFormat('json'));
+        $this->assertEquals('html', $request->getRequestFormat('html'));
+
+        $request = new Request();
+        $this->assertNull($request->getRequestFormat(null));
+
+        $request = new Request();
+        $this->assertNull($request->setRequestFormat('foo'));
+        $this->assertEquals('foo', $request->getRequestFormat(null));
+
+        $request = new Request(['_format' => 'foo']);
+        $this->assertEquals('html', $request->getRequestFormat());
+    }
+
+    public function testHasSession()
+    {
+        $request = new Request();
+
+        $this->assertFalse($request->hasSession());
+        $request->setSession(new Session(new MockArraySessionStorage()));
+        $this->assertTrue($request->hasSession());
+    }
+
+    public function testGetSession()
+    {
+        $request = new Request();
+
+        $request->setSession(new Session(new MockArraySessionStorage()));
+        $this->assertTrue($request->hasSession());
+
+        $session = $request->getSession();
+        $this->assertObjectHasAttribute('storage', $session);
+        $this->assertObjectHasAttribute('flashName', $session);
+        $this->assertObjectHasAttribute('attributeName', $session);
+    }
+
+    public function testHasPreviousSession()
+    {
+        $request = new Request();
+
+        $this->assertFalse($request->hasPreviousSession());
+        $request->cookies->set('MOCKSESSID', 'foo');
+        $this->assertFalse($request->hasPreviousSession());
+        $request->setSession(new Session(new MockArraySessionStorage()));
+        $this->assertTrue($request->hasPreviousSession());
+    }
+
+    public function testToString()
+    {
+        $request = new Request();
+
+        $request->headers->set('Accept-language', 'zh, en-us; q=0.8, en; q=0.6');
+        $request->cookies->set('Foo', 'Bar');
+
+        $asString = (string) $request;
+
+        $this->assertContains('Accept-Language: zh, en-us; q=0.8, en; q=0.6', $asString);
+        $this->assertContains('Cookie: Foo=Bar', $asString);
+
+        $request->cookies->set('Another', 'Cookie');
+
+        $asString = (string) $request;
+
+        $this->assertContains('Cookie: Foo=Bar; Another=Cookie', $asString);
+    }
+
+    public function testIsMethod()
+    {
+        $request = new Request();
+        $request->setMethod('POST');
+        $this->assertTrue($request->isMethod('POST'));
+        $this->assertTrue($request->isMethod('post'));
+        $this->assertFalse($request->isMethod('GET'));
+        $this->assertFalse($request->isMethod('get'));
+
+        $request->setMethod('GET');
+        $this->assertTrue($request->isMethod('GET'));
+        $this->assertTrue($request->isMethod('get'));
+        $this->assertFalse($request->isMethod('POST'));
+        $this->assertFalse($request->isMethod('post'));
+    }
+
+    /**
+     * @dataProvider getBaseUrlData
+     */
+    public function testGetBaseUrl($uri, $server, $expectedBaseUrl, $expectedPathInfo)
+    {
+        $request = Request::create($uri, 'GET', [], [], [], $server);
+
+        $this->assertSame($expectedBaseUrl, $request->getBaseUrl(), 'baseUrl');
+        $this->assertSame($expectedPathInfo, $request->getPathInfo(), 'pathInfo');
+    }
+
+    public function getBaseUrlData()
+    {
+        return [
+            [
+                '/fruit/strawberry/1234index.php/blah',
+                [
+                    'SCRIPT_FILENAME' => 'E:/Sites/cc-new/public_html/fruit/index.php',
+                    'SCRIPT_NAME' => '/fruit/index.php',
+                    'PHP_SELF' => '/fruit/index.php',
+                ],
+                '/fruit',
+                '/strawberry/1234index.php/blah',
+            ],
+            [
+                '/fruit/strawberry/1234index.php/blah',
+                [
+                    'SCRIPT_FILENAME' => 'E:/Sites/cc-new/public_html/index.php',
+                    'SCRIPT_NAME' => '/index.php',
+                    'PHP_SELF' => '/index.php',
+                ],
+                '',
+                '/fruit/strawberry/1234index.php/blah',
+            ],
+            [
+                '/foo%20bar/',
+                [
+                    'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo bar/app.php',
+                    'SCRIPT_NAME' => '/foo bar/app.php',
+                    'PHP_SELF' => '/foo bar/app.php',
+                ],
+                '/foo%20bar',
+                '/',
+            ],
+            [
+                '/foo%20bar/home',
+                [
+                    'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo bar/app.php',
+                    'SCRIPT_NAME' => '/foo bar/app.php',
+                    'PHP_SELF' => '/foo bar/app.php',
+                ],
+                '/foo%20bar',
+                '/home',
+            ],
+            [
+                '/foo%20bar/app.php/home',
+                [
+                    'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo bar/app.php',
+                    'SCRIPT_NAME' => '/foo bar/app.php',
+                    'PHP_SELF' => '/foo bar/app.php',
+                ],
+                '/foo%20bar/app.php',
+                '/home',
+            ],
+            [
+                '/foo%20bar/app.php/home%3Dbaz',
+                [
+                    'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo bar/app.php',
+                    'SCRIPT_NAME' => '/foo bar/app.php',
+                    'PHP_SELF' => '/foo bar/app.php',
+                ],
+                '/foo%20bar/app.php',
+                '/home%3Dbaz',
+            ],
+            [
+                '/foo/bar+baz',
+                [
+                    'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo/app.php',
+                    'SCRIPT_NAME' => '/foo/app.php',
+                    'PHP_SELF' => '/foo/app.php',
+                ],
+                '/foo',
+                '/bar+baz',
+            ],
+        ];
+    }
+
+    /**
+     * @dataProvider urlencodedStringPrefixData
+     */
+    public function testUrlencodedStringPrefix($string, $prefix, $expect)
+    {
+        $request = new Request();
+
+        $me = new \ReflectionMethod($request, 'getUrlencodedPrefix');
+        $me->setAccessible(true);
+
+        $this->assertSame($expect, $me->invoke($request, $string, $prefix));
+    }
+
+    public function urlencodedStringPrefixData()
+    {
+        return [
+            ['foo', 'foo', 'foo'],
+            ['fo%6f', 'foo', 'fo%6f'],
+            ['foo/bar', 'foo', 'foo'],
+            ['fo%6f/bar', 'foo', 'fo%6f'],
+            ['f%6f%6f/bar', 'foo', 'f%6f%6f'],
+            ['%66%6F%6F/bar', 'foo', '%66%6F%6F'],
+            ['fo+o/bar', 'fo+o', 'fo+o'],
+            ['fo%2Bo/bar', 'fo+o', 'fo%2Bo'],
+        ];
+    }
+
+    private function disableHttpMethodParameterOverride()
+    {
+        $class = new \ReflectionClass('Symfony\\Component\\HttpFoundation\\Request');
+        $property = $class->getProperty('httpMethodParameterOverride');
+        $property->setAccessible(true);
+        $property->setValue(false);
+    }
+
+    private function getRequestInstanceForClientIpTests($remoteAddr, $httpForwardedFor, $trustedProxies)
+    {
+        $request = new Request();
+
+        $server = ['REMOTE_ADDR' => $remoteAddr];
+        if (null !== $httpForwardedFor) {
+            $server['HTTP_X_FORWARDED_FOR'] = $httpForwardedFor;
+        }
+
+        if ($trustedProxies) {
+            Request::setTrustedProxies($trustedProxies, Request::HEADER_X_FORWARDED_ALL);
+        }
+
+        $request->initialize([], [], [], [], [], $server);
+
+        return $request;
+    }
+
+    private function getRequestInstanceForClientIpsForwardedTests($remoteAddr, $httpForwarded, $trustedProxies)
+    {
+        $request = new Request();
+
+        $server = ['REMOTE_ADDR' => $remoteAddr];
+
+        if (null !== $httpForwarded) {
+            $server['HTTP_FORWARDED'] = $httpForwarded;
+        }
+
+        if ($trustedProxies) {
+            Request::setTrustedProxies($trustedProxies, Request::HEADER_FORWARDED);
+        }
+
+        $request->initialize([], [], [], [], [], $server);
+
+        return $request;
+    }
+
+    public function testTrustedProxiesXForwardedFor()
+    {
+        $request = Request::create('http://example.com/');
+        $request->server->set('REMOTE_ADDR', '3.3.3.3');
+        $request->headers->set('X_FORWARDED_FOR', '1.1.1.1, 2.2.2.2');
+        $request->headers->set('X_FORWARDED_HOST', 'foo.example.com:1234, real.example.com:8080');
+        $request->headers->set('X_FORWARDED_PROTO', 'https');
+        $request->headers->set('X_FORWARDED_PORT', 443);
+
+        // no trusted proxies
+        $this->assertEquals('3.3.3.3', $request->getClientIp());
+        $this->assertEquals('example.com', $request->getHost());
+        $this->assertEquals(80, $request->getPort());
+        $this->assertFalse($request->isSecure());
+
+        // disabling proxy trusting
+        Request::setTrustedProxies([], Request::HEADER_X_FORWARDED_ALL);
+        $this->assertEquals('3.3.3.3', $request->getClientIp());
+        $this->assertEquals('example.com', $request->getHost());
+        $this->assertEquals(80, $request->getPort());
+        $this->assertFalse($request->isSecure());
+
+        // request is forwarded by a non-trusted proxy
+        Request::setTrustedProxies(['2.2.2.2'], Request::HEADER_X_FORWARDED_ALL);
+        $this->assertEquals('3.3.3.3', $request->getClientIp());
+        $this->assertEquals('example.com', $request->getHost());
+        $this->assertEquals(80, $request->getPort());
+        $this->assertFalse($request->isSecure());
+
+        // trusted proxy via setTrustedProxies()
+        Request::setTrustedProxies(['3.3.3.3', '2.2.2.2'], Request::HEADER_X_FORWARDED_ALL);
+        $this->assertEquals('1.1.1.1', $request->getClientIp());
+        $this->assertEquals('foo.example.com', $request->getHost());
+        $this->assertEquals(443, $request->getPort());
+        $this->assertTrue($request->isSecure());
+
+        // trusted proxy via setTrustedProxies()
+        Request::setTrustedProxies(['3.3.3.4', '2.2.2.2'], Request::HEADER_X_FORWARDED_ALL);
+        $this->assertEquals('3.3.3.3', $request->getClientIp());
+        $this->assertEquals('example.com', $request->getHost());
+        $this->assertEquals(80, $request->getPort());
+        $this->assertFalse($request->isSecure());
+
+        // check various X_FORWARDED_PROTO header values
+        Request::setTrustedProxies(['3.3.3.3', '2.2.2.2'], Request::HEADER_X_FORWARDED_ALL);
+        $request->headers->set('X_FORWARDED_PROTO', 'ssl');
+        $this->assertTrue($request->isSecure());
+
+        $request->headers->set('X_FORWARDED_PROTO', 'https, http');
+        $this->assertTrue($request->isSecure());
+    }
+
+    /**
+     * @group legacy
+     * @expectedDeprecation The "Symfony\Component\HttpFoundation\Request::setTrustedHeaderName()" method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the $trustedHeaderSet argument of the Request::setTrustedProxies() method instead.
+     */
+    public function testLegacyTrustedProxies()
+    {
+        $request = Request::create('http://example.com/');
+        $request->server->set('REMOTE_ADDR', '3.3.3.3');
+        $request->headers->set('X_FORWARDED_FOR', '1.1.1.1, 2.2.2.2');
+        $request->headers->set('X_FORWARDED_HOST', 'foo.example.com, real.example.com:8080');
+        $request->headers->set('X_FORWARDED_PROTO', 'https');
+        $request->headers->set('X_FORWARDED_PORT', 443);
+        $request->headers->set('X_MY_FOR', '3.3.3.3, 4.4.4.4');
+        $request->headers->set('X_MY_HOST', 'my.example.com');
+        $request->headers->set('X_MY_PROTO', 'http');
+        $request->headers->set('X_MY_PORT', 81);
+
+        Request::setTrustedProxies(['3.3.3.3', '2.2.2.2'], Request::HEADER_X_FORWARDED_ALL);
+
+        // custom header names
+        Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, 'X_MY_FOR');
+        Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, 'X_MY_HOST');
+        Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, 'X_MY_PORT');
+        Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, 'X_MY_PROTO');
+        $this->assertEquals('4.4.4.4', $request->getClientIp());
+        $this->assertEquals('my.example.com', $request->getHost());
+        $this->assertEquals(81, $request->getPort());
+        $this->assertFalse($request->isSecure());
+
+        // disabling via empty header names
+        Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, null);
+        Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, null);
+        Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, null);
+        Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, null);
+        $this->assertEquals('3.3.3.3', $request->getClientIp());
+        $this->assertEquals('example.com', $request->getHost());
+        $this->assertEquals(80, $request->getPort());
+        $this->assertFalse($request->isSecure());
+
+        //reset
+        Request::setTrustedHeaderName(Request::HEADER_FORWARDED, 'FORWARDED');
+        Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, 'X_FORWARDED_FOR');
+        Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, 'X_FORWARDED_HOST');
+        Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, 'X_FORWARDED_PORT');
+        Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, 'X_FORWARDED_PROTO');
+    }
+
+    public function testTrustedProxiesForwarded()
+    {
+        $request = Request::create('http://example.com/');
+        $request->server->set('REMOTE_ADDR', '3.3.3.3');
+        $request->headers->set('FORWARDED', 'for=1.1.1.1, host=foo.example.com:8080, proto=https, for=2.2.2.2, host=real.example.com:8080');
+
+        // no trusted proxies
+        $this->assertEquals('3.3.3.3', $request->getClientIp());
+        $this->assertEquals('example.com', $request->getHost());
+        $this->assertEquals(80, $request->getPort());
+        $this->assertFalse($request->isSecure());
+
+        // disabling proxy trusting
+        Request::setTrustedProxies([], Request::HEADER_FORWARDED);
+        $this->assertEquals('3.3.3.3', $request->getClientIp());
+        $this->assertEquals('example.com', $request->getHost());
+        $this->assertEquals(80, $request->getPort());
+        $this->assertFalse($request->isSecure());
+
+        // request is forwarded by a non-trusted proxy
+        Request::setTrustedProxies(['2.2.2.2'], Request::HEADER_FORWARDED);
+        $this->assertEquals('3.3.3.3', $request->getClientIp());
+        $this->assertEquals('example.com', $request->getHost());
+        $this->assertEquals(80, $request->getPort());
+        $this->assertFalse($request->isSecure());
+
+        // trusted proxy via setTrustedProxies()
+        Request::setTrustedProxies(['3.3.3.3', '2.2.2.2'], Request::HEADER_FORWARDED);
+        $this->assertEquals('1.1.1.1', $request->getClientIp());
+        $this->assertEquals('foo.example.com', $request->getHost());
+        $this->assertEquals(8080, $request->getPort());
+        $this->assertTrue($request->isSecure());
+
+        // trusted proxy via setTrustedProxies()
+        Request::setTrustedProxies(['3.3.3.4', '2.2.2.2'], Request::HEADER_FORWARDED);
+        $this->assertEquals('3.3.3.3', $request->getClientIp());
+        $this->assertEquals('example.com', $request->getHost());
+        $this->assertEquals(80, $request->getPort());
+        $this->assertFalse($request->isSecure());
+
+        // check various X_FORWARDED_PROTO header values
+        Request::setTrustedProxies(['3.3.3.3', '2.2.2.2'], Request::HEADER_FORWARDED);
+        $request->headers->set('FORWARDED', 'proto=ssl');
+        $this->assertTrue($request->isSecure());
+
+        $request->headers->set('FORWARDED', 'proto=https, proto=http');
+        $this->assertTrue($request->isSecure());
+    }
+
+    /**
+     * @group legacy
+     * @expectedException \InvalidArgumentException
+     */
+    public function testSetTrustedProxiesInvalidHeaderName()
+    {
+        Request::create('http://example.com/');
+        Request::setTrustedHeaderName('bogus name', 'X_MY_FOR');
+    }
+
+    /**
+     * @group legacy
+     * @expectedException \InvalidArgumentException
+     */
+    public function testGetTrustedProxiesInvalidHeaderName()
+    {
+        Request::create('http://example.com/');
+        Request::getTrustedHeaderName('bogus name');
+    }
+
+    /**
+     * @dataProvider iisRequestUriProvider
+     */
+    public function testIISRequestUri($headers, $server, $expectedRequestUri)
+    {
+        $request = new Request();
+        $request->headers->replace($headers);
+        $request->server->replace($server);
+
+        $this->assertEquals($expectedRequestUri, $request->getRequestUri(), '->getRequestUri() is correct');
+
+        $subRequestUri = '/bar/foo';
+        $subRequest = Request::create($subRequestUri, 'get', [], [], [], $request->server->all());
+        $this->assertEquals($subRequestUri, $subRequest->getRequestUri(), '->getRequestUri() is correct in sub request');
+    }
+
+    public function iisRequestUriProvider()
+    {
+        return [
+            [
+                [],
+                [
+                    'IIS_WasUrlRewritten' => '1',
+                    'UNENCODED_URL' => '/foo/bar',
+                ],
+                '/foo/bar',
+            ],
+            [
+                [],
+                [
+                    'ORIG_PATH_INFO' => '/foo/bar',
+                ],
+                '/foo/bar',
+            ],
+            [
+                [],
+                [
+                    'ORIG_PATH_INFO' => '/foo/bar',
+                    'QUERY_STRING' => 'foo=bar',
+                ],
+                '/foo/bar?foo=bar',
+            ],
+        ];
+    }
+
+    public function testTrustedHosts()
+    {
+        // create a request
+        $request = Request::create('/');
+
+        // no trusted host set -> no host check
+        $request->headers->set('host', 'evil.com');
+        $this->assertEquals('evil.com', $request->getHost());
+
+        // add a trusted domain and all its subdomains
+        Request::setTrustedHosts(['^([a-z]{9}\.)?trusted\.com$']);
+
+        // untrusted host
+        $request->headers->set('host', 'evil.com');
+        try {
+            $request->getHost();
+            $this->fail('Request::getHost() should throw an exception when host is not trusted.');
+        } catch (SuspiciousOperationException $e) {
+            $this->assertEquals('Untrusted Host "evil.com".', $e->getMessage());
+        }
+
+        // trusted hosts
+        $request->headers->set('host', 'trusted.com');
+        $this->assertEquals('trusted.com', $request->getHost());
+        $this->assertEquals(80, $request->getPort());
+
+        $request->server->set('HTTPS', true);
+        $request->headers->set('host', 'trusted.com');
+        $this->assertEquals('trusted.com', $request->getHost());
+        $this->assertEquals(443, $request->getPort());
+        $request->server->set('HTTPS', false);
+
+        $request->headers->set('host', 'trusted.com:8000');
+        $this->assertEquals('trusted.com', $request->getHost());
+        $this->assertEquals(8000, $request->getPort());
+
+        $request->headers->set('host', 'subdomain.trusted.com');
+        $this->assertEquals('subdomain.trusted.com', $request->getHost());
+    }
+
+    public function testSetTrustedHostsDoesNotBreakOnSpecialCharacters()
+    {
+        Request::setTrustedHosts(['localhost(\.local){0,1}#,example.com', 'localhost']);
+
+        $request = Request::create('/');
+        $request->headers->set('host', 'localhost');
+        $this->assertSame('localhost', $request->getHost());
+    }
+
+    public function testFactory()
+    {
+        Request::setFactory(function (array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null) {
+            return new NewRequest();
+        });
+
+        $this->assertEquals('foo', Request::create('/')->getFoo());
+
+        Request::setFactory(null);
+    }
+
+    /**
+     * @dataProvider getLongHostNames
+     */
+    public function testVeryLongHosts($host)
+    {
+        $start = microtime(true);
+
+        $request = Request::create('/');
+        $request->headers->set('host', $host);
+        $this->assertEquals($host, $request->getHost());
+        $this->assertLessThan(5, microtime(true) - $start);
+    }
+
+    /**
+     * @dataProvider getHostValidities
+     */
+    public function testHostValidity($host, $isValid, $expectedHost = null, $expectedPort = null)
+    {
+        $request = Request::create('/');
+        $request->headers->set('host', $host);
+
+        if ($isValid) {
+            $this->assertSame($expectedHost ?: $host, $request->getHost());
+            if ($expectedPort) {
+                $this->assertSame($expectedPort, $request->getPort());
+            }
+        } else {
+            if (method_exists($this, 'expectException')) {
+                $this->expectException(SuspiciousOperationException::class);
+                $this->expectExceptionMessage('Invalid Host');
+            } else {
+                $this->setExpectedException(SuspiciousOperationException::class, 'Invalid Host');
+            }
+
+            $request->getHost();
+        }
+    }
+
+    public function getHostValidities()
+    {
+        return [
+            ['.a', false],
+            ['a..', false],
+            ['a.', true],
+            ["\xE9", false],
+            ['[::1]', true],
+            ['[::1]:80', true, '[::1]', 80],
+            [str_repeat('.', 101), false],
+        ];
+    }
+
+    public function getLongHostNames()
+    {
+        return [
+            ['a'.str_repeat('.a', 40000)],
+            [str_repeat(':', 101)],
+        ];
+    }
+
+    /**
+     * @dataProvider methodIdempotentProvider
+     */
+    public function testMethodIdempotent($method, $idempotent)
+    {
+        $request = new Request();
+        $request->setMethod($method);
+        $this->assertEquals($idempotent, $request->isMethodIdempotent());
+    }
+
+    public function methodIdempotentProvider()
+    {
+        return [
+            ['HEAD', true],
+            ['GET', true],
+            ['POST', false],
+            ['PUT', true],
+            ['PATCH', false],
+            ['DELETE', true],
+            ['PURGE', true],
+            ['OPTIONS', true],
+            ['TRACE', true],
+            ['CONNECT', false],
+        ];
+    }
+
+    /**
+     * @dataProvider methodSafeProvider
+     */
+    public function testMethodSafe($method, $safe)
+    {
+        $request = new Request();
+        $request->setMethod($method);
+        $this->assertEquals($safe, $request->isMethodSafe(false));
+    }
+
+    public function methodSafeProvider()
+    {
+        return [
+            ['HEAD', true],
+            ['GET', true],
+            ['POST', false],
+            ['PUT', false],
+            ['PATCH', false],
+            ['DELETE', false],
+            ['PURGE', false],
+            ['OPTIONS', true],
+            ['TRACE', true],
+            ['CONNECT', false],
+        ];
+    }
+
+    /**
+     * @group legacy
+     * @expectedDeprecation Checking only for cacheable HTTP methods with Symfony\Component\HttpFoundation\Request::isMethodSafe() is deprecated since Symfony 3.2 and will throw an exception in 4.0. Disable checking only for cacheable methods by calling the method with `false` as first argument or use the Request::isMethodCacheable() instead.
+     */
+    public function testMethodSafeChecksCacheable()
+    {
+        $request = new Request();
+        $request->setMethod('OPTIONS');
+        $this->assertFalse($request->isMethodSafe());
+    }
+
+    /**
+     * @dataProvider methodCacheableProvider
+     */
+    public function testMethodCacheable($method, $cacheable)
+    {
+        $request = new Request();
+        $request->setMethod($method);
+        $this->assertEquals($cacheable, $request->isMethodCacheable());
+    }
+
+    public function methodCacheableProvider()
+    {
+        return [
+            ['HEAD', true],
+            ['GET', true],
+            ['POST', false],
+            ['PUT', false],
+            ['PATCH', false],
+            ['DELETE', false],
+            ['PURGE', false],
+            ['OPTIONS', false],
+            ['TRACE', false],
+            ['CONNECT', false],
+        ];
+    }
+
+    /**
+     * @group legacy
+     */
+    public function testGetTrustedHeaderName()
+    {
+        Request::setTrustedProxies(['8.8.8.8'], Request::HEADER_X_FORWARDED_ALL);
+
+        $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_FORWARDED));
+        $this->assertSame('X_FORWARDED_FOR', Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP));
+        $this->assertSame('X_FORWARDED_HOST', Request::getTrustedHeaderName(Request::HEADER_CLIENT_HOST));
+        $this->assertSame('X_FORWARDED_PORT', Request::getTrustedHeaderName(Request::HEADER_CLIENT_PORT));
+        $this->assertSame('X_FORWARDED_PROTO', Request::getTrustedHeaderName(Request::HEADER_CLIENT_PROTO));
+
+        Request::setTrustedProxies(['8.8.8.8'], Request::HEADER_FORWARDED);
+
+        $this->assertSame('FORWARDED', Request::getTrustedHeaderName(Request::HEADER_FORWARDED));
+        $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP));
+        $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_HOST));
+        $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_PORT));
+        $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_PROTO));
+
+        Request::setTrustedHeaderName(Request::HEADER_FORWARDED, 'A');
+        Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, 'B');
+        Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, 'C');
+        Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, 'D');
+        Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, 'E');
+
+        Request::setTrustedProxies(['8.8.8.8'], Request::HEADER_FORWARDED);
+
+        $this->assertSame('A', Request::getTrustedHeaderName(Request::HEADER_FORWARDED));
+        $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP));
+        $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_HOST));
+        $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_PORT));
+        $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_PROTO));
+
+        Request::setTrustedProxies(['8.8.8.8'], Request::HEADER_X_FORWARDED_ALL);
+
+        $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_FORWARDED));
+        $this->assertSame('B', Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP));
+        $this->assertSame('C', Request::getTrustedHeaderName(Request::HEADER_CLIENT_HOST));
+        $this->assertSame('D', Request::getTrustedHeaderName(Request::HEADER_CLIENT_PORT));
+        $this->assertSame('E', Request::getTrustedHeaderName(Request::HEADER_CLIENT_PROTO));
+
+        Request::setTrustedProxies(['8.8.8.8'], Request::HEADER_FORWARDED);
+
+        $this->assertSame('A', Request::getTrustedHeaderName(Request::HEADER_FORWARDED));
+
+        //reset
+        Request::setTrustedHeaderName(Request::HEADER_FORWARDED, 'FORWARDED');
+        Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, 'X_FORWARDED_FOR');
+        Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, 'X_FORWARDED_HOST');
+        Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, 'X_FORWARDED_PORT');
+        Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, 'X_FORWARDED_PROTO');
+    }
+
+    /**
+     * @dataProvider protocolVersionProvider
+     */
+    public function testProtocolVersion($serverProtocol, $trustedProxy, $via, $expected)
+    {
+        if ($trustedProxy) {
+            Request::setTrustedProxies(['1.1.1.1'], -1);
+        }
+
+        $request = new Request();
+        $request->server->set('SERVER_PROTOCOL', $serverProtocol);
+        $request->server->set('REMOTE_ADDR', '1.1.1.1');
+        $request->headers->set('Via', $via);
+
+        $this->assertSame($expected, $request->getProtocolVersion());
+    }
+
+    public function protocolVersionProvider()
+    {
+        return [
+            'untrusted without via' => ['HTTP/2.0', false, '', 'HTTP/2.0'],
+            'untrusted with via' => ['HTTP/2.0', false, '1.0 fred, 1.1 nowhere.com (Apache/1.1)', 'HTTP/2.0'],
+            'trusted without via' => ['HTTP/2.0', true, '', 'HTTP/2.0'],
+            'trusted with via' => ['HTTP/2.0', true, '1.0 fred, 1.1 nowhere.com (Apache/1.1)', 'HTTP/1.0'],
+            'trusted with via and protocol name' => ['HTTP/2.0', true, 'HTTP/1.0 fred, HTTP/1.1 nowhere.com (Apache/1.1)', 'HTTP/1.0'],
+            'trusted with broken via' => ['HTTP/2.0', true, 'HTTP/1^0 foo', 'HTTP/2.0'],
+            'trusted with partially-broken via' => ['HTTP/2.0', true, '1.0 fred, foo', 'HTTP/1.0'],
+        ];
+    }
+
+    public function nonstandardRequestsData()
+    {
+        return [
+            ['',  '', '/', 'http://host:8080/', ''],
+            ['/', '', '/', 'http://host:8080/', ''],
+
+            ['hello/app.php/x',  '', '/x', 'http://host:8080/hello/app.php/x', '/hello', '/hello/app.php'],
+            ['/hello/app.php/x', '', '/x', 'http://host:8080/hello/app.php/x', '/hello', '/hello/app.php'],
+
+            ['',      'a=b', '/', 'http://host:8080/?a=b'],
+            ['?a=b',  'a=b', '/', 'http://host:8080/?a=b'],
+            ['/?a=b', 'a=b', '/', 'http://host:8080/?a=b'],
+
+            ['x',      'a=b', '/x', 'http://host:8080/x?a=b'],
+            ['x?a=b',  'a=b', '/x', 'http://host:8080/x?a=b'],
+            ['/x?a=b', 'a=b', '/x', 'http://host:8080/x?a=b'],
+
+            ['hello/x',  '', '/x', 'http://host:8080/hello/x', '/hello'],
+            ['/hello/x', '', '/x', 'http://host:8080/hello/x', '/hello'],
+
+            ['hello/app.php/x',      'a=b', '/x', 'http://host:8080/hello/app.php/x?a=b', '/hello', '/hello/app.php'],
+            ['hello/app.php/x?a=b',  'a=b', '/x', 'http://host:8080/hello/app.php/x?a=b', '/hello', '/hello/app.php'],
+            ['/hello/app.php/x?a=b', 'a=b', '/x', 'http://host:8080/hello/app.php/x?a=b', '/hello', '/hello/app.php'],
+        ];
+    }
+
+    /**
+     * @dataProvider nonstandardRequestsData
+     */
+    public function testNonstandardRequests($requestUri, $queryString, $expectedPathInfo, $expectedUri, $expectedBasePath = '', $expectedBaseUrl = null)
+    {
+        if (null === $expectedBaseUrl) {
+            $expectedBaseUrl = $expectedBasePath;
+        }
+
+        $server = [
+            'HTTP_HOST' => 'host:8080',
+            'SERVER_PORT' => '8080',
+            'QUERY_STRING' => $queryString,
+            'PHP_SELF' => '/hello/app.php',
+            'SCRIPT_FILENAME' => '/some/path/app.php',
+            'REQUEST_URI' => $requestUri,
+        ];
+
+        $request = new Request([], [], [], [], [], $server);
+
+        $this->assertEquals($expectedPathInfo, $request->getPathInfo());
+        $this->assertEquals($expectedUri, $request->getUri());
+        $this->assertEquals($queryString, $request->getQueryString());
+        $this->assertEquals(8080, $request->getPort());
+        $this->assertEquals('host:8080', $request->getHttpHost());
+        $this->assertEquals($expectedBaseUrl, $request->getBaseUrl());
+        $this->assertEquals($expectedBasePath, $request->getBasePath());
+    }
+
+    public function testTrustedHost()
+    {
+        Request::setTrustedProxies(['1.1.1.1'], -1);
+
+        $request = Request::create('/');
+        $request->server->set('REMOTE_ADDR', '1.1.1.1');
+        $request->headers->set('Forwarded', 'host=localhost:8080');
+        $request->headers->set('X-Forwarded-Host', 'localhost:8080');
+
+        $this->assertSame('localhost:8080', $request->getHttpHost());
+        $this->assertSame(8080, $request->getPort());
+
+        $request = Request::create('/');
+        $request->server->set('REMOTE_ADDR', '1.1.1.1');
+        $request->headers->set('Forwarded', 'host="[::1]:443"');
+        $request->headers->set('X-Forwarded-Host', '[::1]:443');
+        $request->headers->set('X-Forwarded-Port', 443);
+
+        $this->assertSame('[::1]:443', $request->getHttpHost());
+        $this->assertSame(443, $request->getPort());
+    }
+
+    public function testTrustedPort()
+    {
+        Request::setTrustedProxies(['1.1.1.1'], -1);
+
+        $request = Request::create('/');
+        $request->server->set('REMOTE_ADDR', '1.1.1.1');
+        $request->headers->set('Forwarded', 'host=localhost:8080');
+        $request->headers->set('X-Forwarded-Port', 8080);
+
+        $this->assertSame(8080, $request->getPort());
+
+        $request = Request::create('/');
+        $request->server->set('REMOTE_ADDR', '1.1.1.1');
+        $request->headers->set('Forwarded', 'host=localhost');
+        $request->headers->set('X-Forwarded-Port', 80);
+
+        $this->assertSame(80, $request->getPort());
+
+        $request = Request::create('/');
+        $request->server->set('REMOTE_ADDR', '1.1.1.1');
+        $request->headers->set('Forwarded', 'host="[::1]"');
+        $request->headers->set('X-Forwarded-Proto', 'https');
+        $request->headers->set('X-Forwarded-Port', 443);
+
+        $this->assertSame(443, $request->getPort());
+    }
+}
+
+class RequestContentProxy extends Request
+{
+    public function getContent($asResource = false)
+    {
+        return http_build_query(['_method' => 'PUT', 'content' => 'mycontent'], '', '&');
+    }
+}
+
+class NewRequest extends Request
+{
+    public function getFoo()
+    {
+        return 'foo';
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/ResponseFunctionalTest.php b/vendor/symfony/http-foundation/Tests/ResponseFunctionalTest.php
new file mode 100755
index 0000000..3d3e696
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/ResponseFunctionalTest.php
@@ -0,0 +1,58 @@
+<?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\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @requires PHP 7.0
+ */
+class ResponseFunctionalTest extends TestCase
+{
+    private static $server;
+
+    public static function setUpBeforeClass()
+    {
+        $spec = [
+            1 => ['file', '/dev/null', 'w'],
+            2 => ['file', '/dev/null', 'w'],
+        ];
+        if (!self::$server = @proc_open('exec php -S localhost:8054', $spec, $pipes, __DIR__.'/Fixtures/response-functional')) {
+            self::markTestSkipped('PHP server unable to start.');
+        }
+        sleep(1);
+    }
+
+    public static function tearDownAfterClass()
+    {
+        if (self::$server) {
+            proc_terminate(self::$server);
+            proc_close(self::$server);
+        }
+    }
+
+    /**
+     * @dataProvider provideCookie
+     */
+    public function testCookie($fixture)
+    {
+        $result = file_get_contents(sprintf('http://localhost:8054/%s.php', $fixture));
+        $this->assertStringMatchesFormatFile(__DIR__.sprintf('/Fixtures/response-functional/%s.expected', $fixture), $result);
+    }
+
+    public function provideCookie()
+    {
+        foreach (glob(__DIR__.'/Fixtures/response-functional/*.php') as $file) {
+            yield [pathinfo($file, PATHINFO_FILENAME)];
+        }
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/ResponseHeaderBagTest.php b/vendor/symfony/http-foundation/Tests/ResponseHeaderBagTest.php
new file mode 100755
index 0000000..93aacf2
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/ResponseHeaderBagTest.php
@@ -0,0 +1,363 @@
+<?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\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Cookie;
+use Symfony\Component\HttpFoundation\ResponseHeaderBag;
+
+/**
+ * @group time-sensitive
+ */
+class ResponseHeaderBagTest extends TestCase
+{
+    public function testAllPreserveCase()
+    {
+        $headers = [
+            'fOo' => 'BAR',
+            'ETag' => 'xyzzy',
+            'Content-MD5' => 'Q2hlY2sgSW50ZWdyaXR5IQ==',
+            'P3P' => 'CP="CAO PSA OUR"',
+            'WWW-Authenticate' => 'Basic realm="WallyWorld"',
+            'X-UA-Compatible' => 'IE=edge,chrome=1',
+            'X-XSS-Protection' => '1; mode=block',
+        ];
+
+        $bag = new ResponseHeaderBag($headers);
+        $allPreservedCase = $bag->allPreserveCase();
+
+        foreach (array_keys($headers) as $headerName) {
+            $this->assertArrayHasKey($headerName, $allPreservedCase, '->allPreserveCase() gets all input keys in original case');
+        }
+    }
+
+    public function testCacheControlHeader()
+    {
+        $bag = new ResponseHeaderBag([]);
+        $this->assertEquals('no-cache, private', $bag->get('Cache-Control'));
+        $this->assertTrue($bag->hasCacheControlDirective('no-cache'));
+
+        $bag = new ResponseHeaderBag(['Cache-Control' => 'public']);
+        $this->assertEquals('public', $bag->get('Cache-Control'));
+        $this->assertTrue($bag->hasCacheControlDirective('public'));
+
+        $bag = new ResponseHeaderBag(['ETag' => 'abcde']);
+        $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control'));
+        $this->assertTrue($bag->hasCacheControlDirective('private'));
+        $this->assertTrue($bag->hasCacheControlDirective('must-revalidate'));
+        $this->assertFalse($bag->hasCacheControlDirective('max-age'));
+
+        $bag = new ResponseHeaderBag(['Expires' => 'Wed, 16 Feb 2011 14:17:43 GMT']);
+        $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control'));
+
+        $bag = new ResponseHeaderBag([
+            'Expires' => 'Wed, 16 Feb 2011 14:17:43 GMT',
+            'Cache-Control' => 'max-age=3600',
+        ]);
+        $this->assertEquals('max-age=3600, private', $bag->get('Cache-Control'));
+
+        $bag = new ResponseHeaderBag(['Last-Modified' => 'abcde']);
+        $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control'));
+
+        $bag = new ResponseHeaderBag(['Etag' => 'abcde', 'Last-Modified' => 'abcde']);
+        $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control'));
+
+        $bag = new ResponseHeaderBag(['cache-control' => 'max-age=100']);
+        $this->assertEquals('max-age=100, private', $bag->get('Cache-Control'));
+
+        $bag = new ResponseHeaderBag(['cache-control' => 's-maxage=100']);
+        $this->assertEquals('s-maxage=100', $bag->get('Cache-Control'));
+
+        $bag = new ResponseHeaderBag(['cache-control' => 'private, max-age=100']);
+        $this->assertEquals('max-age=100, private', $bag->get('Cache-Control'));
+
+        $bag = new ResponseHeaderBag(['cache-control' => 'public, max-age=100']);
+        $this->assertEquals('max-age=100, public', $bag->get('Cache-Control'));
+
+        $bag = new ResponseHeaderBag();
+        $bag->set('Last-Modified', 'abcde');
+        $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control'));
+
+        $bag = new ResponseHeaderBag();
+        $bag->set('Cache-Control', ['public', 'must-revalidate']);
+        $this->assertCount(1, $bag->get('Cache-Control', null, false));
+        $this->assertEquals('must-revalidate, public', $bag->get('Cache-Control'));
+
+        $bag = new ResponseHeaderBag();
+        $bag->set('Cache-Control', 'public');
+        $bag->set('Cache-Control', 'must-revalidate', false);
+        $this->assertCount(1, $bag->get('Cache-Control', null, false));
+        $this->assertEquals('must-revalidate, public', $bag->get('Cache-Control'));
+    }
+
+    public function testCacheControlClone()
+    {
+        $headers = ['foo' => 'bar'];
+        $bag1 = new ResponseHeaderBag($headers);
+        $bag2 = new ResponseHeaderBag($bag1->allPreserveCase());
+        $this->assertEquals($bag1->allPreserveCase(), $bag2->allPreserveCase());
+    }
+
+    public function testToStringIncludesCookieHeaders()
+    {
+        $bag = new ResponseHeaderBag([]);
+        $bag->setCookie(new Cookie('foo', 'bar'));
+
+        $this->assertSetCookieHeader('foo=bar; path=/; httponly', $bag);
+
+        $bag->clearCookie('foo');
+
+        $this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0; path=/; httponly', $bag);
+    }
+
+    public function testClearCookieSecureNotHttpOnly()
+    {
+        $bag = new ResponseHeaderBag([]);
+
+        $bag->clearCookie('foo', '/', null, true, false);
+
+        $this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0; path=/; secure', $bag);
+    }
+
+    public function testReplace()
+    {
+        $bag = new ResponseHeaderBag([]);
+        $this->assertEquals('no-cache, private', $bag->get('Cache-Control'));
+        $this->assertTrue($bag->hasCacheControlDirective('no-cache'));
+
+        $bag->replace(['Cache-Control' => 'public']);
+        $this->assertEquals('public', $bag->get('Cache-Control'));
+        $this->assertTrue($bag->hasCacheControlDirective('public'));
+    }
+
+    public function testReplaceWithRemove()
+    {
+        $bag = new ResponseHeaderBag([]);
+        $this->assertEquals('no-cache, private', $bag->get('Cache-Control'));
+        $this->assertTrue($bag->hasCacheControlDirective('no-cache'));
+
+        $bag->remove('Cache-Control');
+        $bag->replace([]);
+        $this->assertEquals('no-cache, private', $bag->get('Cache-Control'));
+        $this->assertTrue($bag->hasCacheControlDirective('no-cache'));
+    }
+
+    public function testCookiesWithSameNames()
+    {
+        $bag = new ResponseHeaderBag();
+        $bag->setCookie(new Cookie('foo', 'bar', 0, '/path/foo', 'foo.bar'));
+        $bag->setCookie(new Cookie('foo', 'bar', 0, '/path/bar', 'foo.bar'));
+        $bag->setCookie(new Cookie('foo', 'bar', 0, '/path/bar', 'bar.foo'));
+        $bag->setCookie(new Cookie('foo', 'bar'));
+
+        $this->assertCount(4, $bag->getCookies());
+        $this->assertEquals('foo=bar; path=/path/foo; domain=foo.bar; httponly', $bag->get('set-cookie'));
+        $this->assertEquals([
+            'foo=bar; path=/path/foo; domain=foo.bar; httponly',
+            'foo=bar; path=/path/bar; domain=foo.bar; httponly',
+            'foo=bar; path=/path/bar; domain=bar.foo; httponly',
+            'foo=bar; path=/; httponly',
+        ], $bag->get('set-cookie', null, false));
+
+        $this->assertSetCookieHeader('foo=bar; path=/path/foo; domain=foo.bar; httponly', $bag);
+        $this->assertSetCookieHeader('foo=bar; path=/path/bar; domain=foo.bar; httponly', $bag);
+        $this->assertSetCookieHeader('foo=bar; path=/path/bar; domain=bar.foo; httponly', $bag);
+        $this->assertSetCookieHeader('foo=bar; path=/; httponly', $bag);
+
+        $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
+
+        $this->assertArrayHasKey('foo', $cookies['foo.bar']['/path/foo']);
+        $this->assertArrayHasKey('foo', $cookies['foo.bar']['/path/bar']);
+        $this->assertArrayHasKey('foo', $cookies['bar.foo']['/path/bar']);
+        $this->assertArrayHasKey('foo', $cookies['']['/']);
+    }
+
+    public function testRemoveCookie()
+    {
+        $bag = new ResponseHeaderBag();
+        $this->assertFalse($bag->has('set-cookie'));
+
+        $bag->setCookie(new Cookie('foo', 'bar', 0, '/path/foo', 'foo.bar'));
+        $bag->setCookie(new Cookie('bar', 'foo', 0, '/path/bar', 'foo.bar'));
+        $this->assertTrue($bag->has('set-cookie'));
+
+        $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
+        $this->assertArrayHasKey('/path/foo', $cookies['foo.bar']);
+
+        $bag->removeCookie('foo', '/path/foo', 'foo.bar');
+        $this->assertTrue($bag->has('set-cookie'));
+
+        $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
+        $this->assertArrayNotHasKey('/path/foo', $cookies['foo.bar']);
+
+        $bag->removeCookie('bar', '/path/bar', 'foo.bar');
+        $this->assertFalse($bag->has('set-cookie'));
+
+        $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
+        $this->assertArrayNotHasKey('foo.bar', $cookies);
+    }
+
+    public function testRemoveCookieWithNullRemove()
+    {
+        $bag = new ResponseHeaderBag();
+        $bag->setCookie(new Cookie('foo', 'bar', 0));
+        $bag->setCookie(new Cookie('bar', 'foo', 0));
+
+        $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
+        $this->assertArrayHasKey('/', $cookies['']);
+
+        $bag->removeCookie('foo', null);
+        $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
+        $this->assertArrayNotHasKey('foo', $cookies['']['/']);
+
+        $bag->removeCookie('bar', null);
+        $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
+        $this->assertFalse(isset($cookies['']['/']['bar']));
+    }
+
+    public function testSetCookieHeader()
+    {
+        $bag = new ResponseHeaderBag();
+        $bag->set('set-cookie', 'foo=bar');
+        $this->assertEquals([new Cookie('foo', 'bar', 0, '/', null, false, false, true)], $bag->getCookies());
+
+        $bag->set('set-cookie', 'foo2=bar2', false);
+        $this->assertEquals([
+            new Cookie('foo', 'bar', 0, '/', null, false, false, true),
+            new Cookie('foo2', 'bar2', 0, '/', null, false, false, true),
+        ], $bag->getCookies());
+
+        $bag->remove('set-cookie');
+        $this->assertEquals([], $bag->getCookies());
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testGetCookiesWithInvalidArgument()
+    {
+        $bag = new ResponseHeaderBag();
+
+        $bag->getCookies('invalid_argument');
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testMakeDispositionInvalidDisposition()
+    {
+        $headers = new ResponseHeaderBag();
+
+        $headers->makeDisposition('invalid', 'foo.html');
+    }
+
+    /**
+     * @dataProvider provideMakeDisposition
+     */
+    public function testMakeDisposition($disposition, $filename, $filenameFallback, $expected)
+    {
+        $headers = new ResponseHeaderBag();
+
+        $this->assertEquals($expected, $headers->makeDisposition($disposition, $filename, $filenameFallback));
+    }
+
+    public function testToStringDoesntMessUpHeaders()
+    {
+        $headers = new ResponseHeaderBag();
+
+        $headers->set('Location', 'http://www.symfony.com');
+        $headers->set('Content-type', 'text/html');
+
+        (string) $headers;
+
+        $allHeaders = $headers->allPreserveCase();
+        $this->assertEquals(['http://www.symfony.com'], $allHeaders['Location']);
+        $this->assertEquals(['text/html'], $allHeaders['Content-type']);
+    }
+
+    public function provideMakeDisposition()
+    {
+        return [
+            ['attachment', 'foo.html', 'foo.html', 'attachment; filename="foo.html"'],
+            ['attachment', 'foo.html', '', 'attachment; filename="foo.html"'],
+            ['attachment', 'foo bar.html', '', 'attachment; filename="foo bar.html"'],
+            ['attachment', 'foo "bar".html', '', 'attachment; filename="foo \\"bar\\".html"'],
+            ['attachment', 'foo%20bar.html', 'foo bar.html', 'attachment; filename="foo bar.html"; filename*=utf-8\'\'foo%2520bar.html'],
+            ['attachment', 'föö.html', 'foo.html', 'attachment; filename="foo.html"; filename*=utf-8\'\'f%C3%B6%C3%B6.html'],
+        ];
+    }
+
+    /**
+     * @dataProvider provideMakeDispositionFail
+     * @expectedException \InvalidArgumentException
+     */
+    public function testMakeDispositionFail($disposition, $filename)
+    {
+        $headers = new ResponseHeaderBag();
+
+        $headers->makeDisposition($disposition, $filename);
+    }
+
+    public function provideMakeDispositionFail()
+    {
+        return [
+            ['attachment', 'foo%20bar.html'],
+            ['attachment', 'foo/bar.html'],
+            ['attachment', '/foo.html'],
+            ['attachment', 'foo\bar.html'],
+            ['attachment', '\foo.html'],
+            ['attachment', 'föö.html'],
+        ];
+    }
+
+    public function testDateHeaderAddedOnCreation()
+    {
+        $now = time();
+
+        $bag = new ResponseHeaderBag();
+        $this->assertTrue($bag->has('Date'));
+
+        $this->assertEquals($now, $bag->getDate('Date')->getTimestamp());
+    }
+
+    public function testDateHeaderCanBeSetOnCreation()
+    {
+        $someDate = 'Thu, 23 Mar 2017 09:15:12 GMT';
+        $bag = new ResponseHeaderBag(['Date' => $someDate]);
+
+        $this->assertEquals($someDate, $bag->get('Date'));
+    }
+
+    public function testDateHeaderWillBeRecreatedWhenRemoved()
+    {
+        $someDate = 'Thu, 23 Mar 2017 09:15:12 GMT';
+        $bag = new ResponseHeaderBag(['Date' => $someDate]);
+        $bag->remove('Date');
+
+        // a (new) Date header is still present
+        $this->assertTrue($bag->has('Date'));
+        $this->assertNotEquals($someDate, $bag->get('Date'));
+    }
+
+    public function testDateHeaderWillBeRecreatedWhenHeadersAreReplaced()
+    {
+        $bag = new ResponseHeaderBag();
+        $bag->replace([]);
+
+        $this->assertTrue($bag->has('Date'));
+    }
+
+    private function assertSetCookieHeader($expected, ResponseHeaderBag $actual)
+    {
+        $this->assertRegExp('#^Set-Cookie:\s+'.preg_quote($expected, '#').'$#m', str_replace("\r\n", "\n", (string) $actual));
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/ResponseTest.php b/vendor/symfony/http-foundation/Tests/ResponseTest.php
new file mode 100755
index 0000000..fe6bc02
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/ResponseTest.php
@@ -0,0 +1,1014 @@
+<?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\HttpFoundation\Tests;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * @group time-sensitive
+ */
+class ResponseTest extends ResponseTestCase
+{
+    public function testCreate()
+    {
+        $response = Response::create('foo', 301, ['Foo' => 'bar']);
+
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
+        $this->assertEquals(301, $response->getStatusCode());
+        $this->assertEquals('bar', $response->headers->get('foo'));
+    }
+
+    public function testToString()
+    {
+        $response = new Response();
+        $response = explode("\r\n", $response);
+        $this->assertEquals('HTTP/1.0 200 OK', $response[0]);
+        $this->assertEquals('Cache-Control: no-cache, private', $response[1]);
+    }
+
+    public function testClone()
+    {
+        $response = new Response();
+        $responseClone = clone $response;
+        $this->assertEquals($response, $responseClone);
+    }
+
+    public function testSendHeaders()
+    {
+        $response = new Response();
+        $headers = $response->sendHeaders();
+        $this->assertObjectHasAttribute('headers', $headers);
+        $this->assertObjectHasAttribute('content', $headers);
+        $this->assertObjectHasAttribute('version', $headers);
+        $this->assertObjectHasAttribute('statusCode', $headers);
+        $this->assertObjectHasAttribute('statusText', $headers);
+        $this->assertObjectHasAttribute('charset', $headers);
+    }
+
+    public function testSend()
+    {
+        $response = new Response();
+        $responseSend = $response->send();
+        $this->assertObjectHasAttribute('headers', $responseSend);
+        $this->assertObjectHasAttribute('content', $responseSend);
+        $this->assertObjectHasAttribute('version', $responseSend);
+        $this->assertObjectHasAttribute('statusCode', $responseSend);
+        $this->assertObjectHasAttribute('statusText', $responseSend);
+        $this->assertObjectHasAttribute('charset', $responseSend);
+    }
+
+    public function testGetCharset()
+    {
+        $response = new Response();
+        $charsetOrigin = 'UTF-8';
+        $response->setCharset($charsetOrigin);
+        $charset = $response->getCharset();
+        $this->assertEquals($charsetOrigin, $charset);
+    }
+
+    public function testIsCacheable()
+    {
+        $response = new Response();
+        $this->assertFalse($response->isCacheable());
+    }
+
+    public function testIsCacheableWithErrorCode()
+    {
+        $response = new Response('', 500);
+        $this->assertFalse($response->isCacheable());
+    }
+
+    public function testIsCacheableWithNoStoreDirective()
+    {
+        $response = new Response();
+        $response->headers->set('cache-control', 'private');
+        $this->assertFalse($response->isCacheable());
+    }
+
+    public function testIsCacheableWithSetTtl()
+    {
+        $response = new Response();
+        $response->setTtl(10);
+        $this->assertTrue($response->isCacheable());
+    }
+
+    public function testMustRevalidate()
+    {
+        $response = new Response();
+        $this->assertFalse($response->mustRevalidate());
+    }
+
+    public function testMustRevalidateWithMustRevalidateCacheControlHeader()
+    {
+        $response = new Response();
+        $response->headers->set('cache-control', 'must-revalidate');
+
+        $this->assertTrue($response->mustRevalidate());
+    }
+
+    public function testMustRevalidateWithProxyRevalidateCacheControlHeader()
+    {
+        $response = new Response();
+        $response->headers->set('cache-control', 'proxy-revalidate');
+
+        $this->assertTrue($response->mustRevalidate());
+    }
+
+    public function testSetNotModified()
+    {
+        $response = new Response('foo');
+        $modified = $response->setNotModified();
+        $this->assertObjectHasAttribute('headers', $modified);
+        $this->assertObjectHasAttribute('content', $modified);
+        $this->assertObjectHasAttribute('version', $modified);
+        $this->assertObjectHasAttribute('statusCode', $modified);
+        $this->assertObjectHasAttribute('statusText', $modified);
+        $this->assertObjectHasAttribute('charset', $modified);
+        $this->assertEquals(304, $modified->getStatusCode());
+
+        ob_start();
+        $modified->sendContent();
+        $string = ob_get_clean();
+        $this->assertEmpty($string);
+    }
+
+    public function testIsSuccessful()
+    {
+        $response = new Response();
+        $this->assertTrue($response->isSuccessful());
+    }
+
+    public function testIsNotModified()
+    {
+        $response = new Response();
+        $modified = $response->isNotModified(new Request());
+        $this->assertFalse($modified);
+    }
+
+    public function testIsNotModifiedNotSafe()
+    {
+        $request = Request::create('/homepage', 'POST');
+
+        $response = new Response();
+        $this->assertFalse($response->isNotModified($request));
+    }
+
+    public function testIsNotModifiedLastModified()
+    {
+        $before = 'Sun, 25 Aug 2013 18:32:31 GMT';
+        $modified = 'Sun, 25 Aug 2013 18:33:31 GMT';
+        $after = 'Sun, 25 Aug 2013 19:33:31 GMT';
+
+        $request = new Request();
+        $request->headers->set('If-Modified-Since', $modified);
+
+        $response = new Response();
+
+        $response->headers->set('Last-Modified', $modified);
+        $this->assertTrue($response->isNotModified($request));
+
+        $response->headers->set('Last-Modified', $before);
+        $this->assertTrue($response->isNotModified($request));
+
+        $response->headers->set('Last-Modified', $after);
+        $this->assertFalse($response->isNotModified($request));
+
+        $response->headers->set('Last-Modified', '');
+        $this->assertFalse($response->isNotModified($request));
+    }
+
+    public function testIsNotModifiedEtag()
+    {
+        $etagOne = 'randomly_generated_etag';
+        $etagTwo = 'randomly_generated_etag_2';
+
+        $request = new Request();
+        $request->headers->set('if_none_match', sprintf('%s, %s, %s', $etagOne, $etagTwo, 'etagThree'));
+
+        $response = new Response();
+
+        $response->headers->set('ETag', $etagOne);
+        $this->assertTrue($response->isNotModified($request));
+
+        $response->headers->set('ETag', $etagTwo);
+        $this->assertTrue($response->isNotModified($request));
+
+        $response->headers->set('ETag', '');
+        $this->assertFalse($response->isNotModified($request));
+    }
+
+    public function testIsNotModifiedLastModifiedAndEtag()
+    {
+        $before = 'Sun, 25 Aug 2013 18:32:31 GMT';
+        $modified = 'Sun, 25 Aug 2013 18:33:31 GMT';
+        $after = 'Sun, 25 Aug 2013 19:33:31 GMT';
+        $etag = 'randomly_generated_etag';
+
+        $request = new Request();
+        $request->headers->set('if_none_match', sprintf('%s, %s', $etag, 'etagThree'));
+        $request->headers->set('If-Modified-Since', $modified);
+
+        $response = new Response();
+
+        $response->headers->set('ETag', $etag);
+        $response->headers->set('Last-Modified', $after);
+        $this->assertFalse($response->isNotModified($request));
+
+        $response->headers->set('ETag', 'non-existent-etag');
+        $response->headers->set('Last-Modified', $before);
+        $this->assertFalse($response->isNotModified($request));
+
+        $response->headers->set('ETag', $etag);
+        $response->headers->set('Last-Modified', $modified);
+        $this->assertTrue($response->isNotModified($request));
+    }
+
+    public function testIsNotModifiedIfModifiedSinceAndEtagWithoutLastModified()
+    {
+        $modified = 'Sun, 25 Aug 2013 18:33:31 GMT';
+        $etag = 'randomly_generated_etag';
+
+        $request = new Request();
+        $request->headers->set('if_none_match', sprintf('%s, %s', $etag, 'etagThree'));
+        $request->headers->set('If-Modified-Since', $modified);
+
+        $response = new Response();
+
+        $response->headers->set('ETag', $etag);
+        $this->assertTrue($response->isNotModified($request));
+
+        $response->headers->set('ETag', 'non-existent-etag');
+        $this->assertFalse($response->isNotModified($request));
+    }
+
+    public function testIsValidateable()
+    {
+        $response = new Response('', 200, ['Last-Modified' => $this->createDateTimeOneHourAgo()->format(DATE_RFC2822)]);
+        $this->assertTrue($response->isValidateable(), '->isValidateable() returns true if Last-Modified is present');
+
+        $response = new Response('', 200, ['ETag' => '"12345"']);
+        $this->assertTrue($response->isValidateable(), '->isValidateable() returns true if ETag is present');
+
+        $response = new Response();
+        $this->assertFalse($response->isValidateable(), '->isValidateable() returns false when no validator is present');
+    }
+
+    public function testGetDate()
+    {
+        $oneHourAgo = $this->createDateTimeOneHourAgo();
+        $response = new Response('', 200, ['Date' => $oneHourAgo->format(DATE_RFC2822)]);
+        $date = $response->getDate();
+        $this->assertEquals($oneHourAgo->getTimestamp(), $date->getTimestamp(), '->getDate() returns the Date header if present');
+
+        $response = new Response();
+        $date = $response->getDate();
+        $this->assertEquals(time(), $date->getTimestamp(), '->getDate() returns the current Date if no Date header present');
+
+        $response = new Response('', 200, ['Date' => $this->createDateTimeOneHourAgo()->format(DATE_RFC2822)]);
+        $now = $this->createDateTimeNow();
+        $response->headers->set('Date', $now->format(DATE_RFC2822));
+        $date = $response->getDate();
+        $this->assertEquals($now->getTimestamp(), $date->getTimestamp(), '->getDate() returns the date when the header has been modified');
+
+        $response = new Response('', 200);
+        $now = $this->createDateTimeNow();
+        $response->headers->remove('Date');
+        $date = $response->getDate();
+        $this->assertEquals($now->getTimestamp(), $date->getTimestamp(), '->getDate() returns the current Date when the header has previously been removed');
+    }
+
+    public function testGetMaxAge()
+    {
+        $response = new Response();
+        $response->headers->set('Cache-Control', 's-maxage=600, max-age=0');
+        $this->assertEquals(600, $response->getMaxAge(), '->getMaxAge() uses s-maxage cache control directive when present');
+
+        $response = new Response();
+        $response->headers->set('Cache-Control', 'max-age=600');
+        $this->assertEquals(600, $response->getMaxAge(), '->getMaxAge() falls back to max-age when no s-maxage directive present');
+
+        $response = new Response();
+        $response->headers->set('Cache-Control', 'must-revalidate');
+        $response->headers->set('Expires', $this->createDateTimeOneHourLater()->format(DATE_RFC2822));
+        $this->assertEquals(3600, $response->getMaxAge(), '->getMaxAge() falls back to Expires when no max-age or s-maxage directive present');
+
+        $response = new Response();
+        $response->headers->set('Cache-Control', 'must-revalidate');
+        $response->headers->set('Expires', -1);
+        $this->assertEquals('Sat, 01 Jan 00 00:00:00 +0000', $response->getExpires()->format(DATE_RFC822));
+
+        $response = new Response();
+        $this->assertNull($response->getMaxAge(), '->getMaxAge() returns null if no freshness information available');
+    }
+
+    public function testSetSharedMaxAge()
+    {
+        $response = new Response();
+        $response->setSharedMaxAge(20);
+
+        $cacheControl = $response->headers->get('Cache-Control');
+        $this->assertEquals('public, s-maxage=20', $cacheControl);
+    }
+
+    public function testIsPrivate()
+    {
+        $response = new Response();
+        $response->headers->set('Cache-Control', 'max-age=100');
+        $response->setPrivate();
+        $this->assertEquals(100, $response->headers->getCacheControlDirective('max-age'), '->isPrivate() adds the private Cache-Control directive when set to true');
+        $this->assertTrue($response->headers->getCacheControlDirective('private'), '->isPrivate() adds the private Cache-Control directive when set to true');
+
+        $response = new Response();
+        $response->headers->set('Cache-Control', 'public, max-age=100');
+        $response->setPrivate();
+        $this->assertEquals(100, $response->headers->getCacheControlDirective('max-age'), '->isPrivate() adds the private Cache-Control directive when set to true');
+        $this->assertTrue($response->headers->getCacheControlDirective('private'), '->isPrivate() adds the private Cache-Control directive when set to true');
+        $this->assertFalse($response->headers->hasCacheControlDirective('public'), '->isPrivate() removes the public Cache-Control directive');
+    }
+
+    public function testExpire()
+    {
+        $response = new Response();
+        $response->headers->set('Cache-Control', 'max-age=100');
+        $response->expire();
+        $this->assertEquals(100, $response->headers->get('Age'), '->expire() sets the Age to max-age when present');
+
+        $response = new Response();
+        $response->headers->set('Cache-Control', 'max-age=100, s-maxage=500');
+        $response->expire();
+        $this->assertEquals(500, $response->headers->get('Age'), '->expire() sets the Age to s-maxage when both max-age and s-maxage are present');
+
+        $response = new Response();
+        $response->headers->set('Cache-Control', 'max-age=5, s-maxage=500');
+        $response->headers->set('Age', '1000');
+        $response->expire();
+        $this->assertEquals(1000, $response->headers->get('Age'), '->expire() does nothing when the response is already stale/expired');
+
+        $response = new Response();
+        $response->expire();
+        $this->assertFalse($response->headers->has('Age'), '->expire() does nothing when the response does not include freshness information');
+
+        $response = new Response();
+        $response->headers->set('Expires', -1);
+        $response->expire();
+        $this->assertNull($response->headers->get('Age'), '->expire() does not set the Age when the response is expired');
+
+        $response = new Response();
+        $response->headers->set('Expires', date(DATE_RFC2822, time() + 600));
+        $response->expire();
+        $this->assertNull($response->headers->get('Expires'), '->expire() removes the Expires header when the response is fresh');
+    }
+
+    public function testGetTtl()
+    {
+        $response = new Response();
+        $this->assertNull($response->getTtl(), '->getTtl() returns null when no Expires or Cache-Control headers are present');
+
+        $response = new Response();
+        $response->headers->set('Expires', $this->createDateTimeOneHourLater()->format(DATE_RFC2822));
+        $this->assertEquals(3600, $response->getTtl(), '->getTtl() uses the Expires header when no max-age is present');
+
+        $response = new Response();
+        $response->headers->set('Expires', $this->createDateTimeOneHourAgo()->format(DATE_RFC2822));
+        $this->assertLessThan(0, $response->getTtl(), '->getTtl() returns negative values when Expires is in past');
+
+        $response = new Response();
+        $response->headers->set('Expires', $response->getDate()->format(DATE_RFC2822));
+        $response->headers->set('Age', 0);
+        $this->assertSame(0, $response->getTtl(), '->getTtl() correctly handles zero');
+
+        $response = new Response();
+        $response->headers->set('Cache-Control', 'max-age=60');
+        $this->assertEquals(60, $response->getTtl(), '->getTtl() uses Cache-Control max-age when present');
+    }
+
+    public function testSetClientTtl()
+    {
+        $response = new Response();
+        $response->setClientTtl(10);
+
+        $this->assertEquals($response->getMaxAge(), $response->getAge() + 10);
+    }
+
+    public function testGetSetProtocolVersion()
+    {
+        $response = new Response();
+
+        $this->assertEquals('1.0', $response->getProtocolVersion());
+
+        $response->setProtocolVersion('1.1');
+
+        $this->assertEquals('1.1', $response->getProtocolVersion());
+    }
+
+    public function testGetVary()
+    {
+        $response = new Response();
+        $this->assertEquals([], $response->getVary(), '->getVary() returns an empty array if no Vary header is present');
+
+        $response = new Response();
+        $response->headers->set('Vary', 'Accept-Language');
+        $this->assertEquals(['Accept-Language'], $response->getVary(), '->getVary() parses a single header name value');
+
+        $response = new Response();
+        $response->headers->set('Vary', 'Accept-Language User-Agent    X-Foo');
+        $this->assertEquals(['Accept-Language', 'User-Agent', 'X-Foo'], $response->getVary(), '->getVary() parses multiple header name values separated by spaces');
+
+        $response = new Response();
+        $response->headers->set('Vary', 'Accept-Language,User-Agent,    X-Foo');
+        $this->assertEquals(['Accept-Language', 'User-Agent', 'X-Foo'], $response->getVary(), '->getVary() parses multiple header name values separated by commas');
+
+        $vary = ['Accept-Language', 'User-Agent', 'X-foo'];
+
+        $response = new Response();
+        $response->headers->set('Vary', $vary);
+        $this->assertEquals($vary, $response->getVary(), '->getVary() parses multiple header name values in arrays');
+
+        $response = new Response();
+        $response->headers->set('Vary', 'Accept-Language, User-Agent, X-foo');
+        $this->assertEquals($vary, $response->getVary(), '->getVary() parses multiple header name values in arrays');
+    }
+
+    public function testSetVary()
+    {
+        $response = new Response();
+        $response->setVary('Accept-Language');
+        $this->assertEquals(['Accept-Language'], $response->getVary());
+
+        $response->setVary('Accept-Language, User-Agent');
+        $this->assertEquals(['Accept-Language', 'User-Agent'], $response->getVary(), '->setVary() replace the vary header by default');
+
+        $response->setVary('X-Foo', false);
+        $this->assertEquals(['Accept-Language', 'User-Agent', 'X-Foo'], $response->getVary(), '->setVary() doesn\'t wipe out earlier Vary headers if replace is set to false');
+    }
+
+    public function testDefaultContentType()
+    {
+        $headerMock = $this->getMockBuilder('Symfony\Component\HttpFoundation\ResponseHeaderBag')->setMethods(['set'])->getMock();
+        $headerMock->expects($this->at(0))
+            ->method('set')
+            ->with('Content-Type', 'text/html');
+        $headerMock->expects($this->at(1))
+            ->method('set')
+            ->with('Content-Type', 'text/html; charset=UTF-8');
+
+        $response = new Response('foo');
+        $response->headers = $headerMock;
+
+        $response->prepare(new Request());
+    }
+
+    public function testContentTypeCharset()
+    {
+        $response = new Response();
+        $response->headers->set('Content-Type', 'text/css');
+
+        // force fixContentType() to be called
+        $response->prepare(new Request());
+
+        $this->assertEquals('text/css; charset=UTF-8', $response->headers->get('Content-Type'));
+    }
+
+    public function testPrepareDoesNothingIfContentTypeIsSet()
+    {
+        $response = new Response('foo');
+        $response->headers->set('Content-Type', 'text/plain');
+
+        $response->prepare(new Request());
+
+        $this->assertEquals('text/plain; charset=UTF-8', $response->headers->get('content-type'));
+    }
+
+    public function testPrepareDoesNothingIfRequestFormatIsNotDefined()
+    {
+        $response = new Response('foo');
+
+        $response->prepare(new Request());
+
+        $this->assertEquals('text/html; charset=UTF-8', $response->headers->get('content-type'));
+    }
+
+    public function testPrepareSetContentType()
+    {
+        $response = new Response('foo');
+        $request = Request::create('/');
+        $request->setRequestFormat('json');
+
+        $response->prepare($request);
+
+        $this->assertEquals('application/json', $response->headers->get('content-type'));
+    }
+
+    public function testPrepareRemovesContentForHeadRequests()
+    {
+        $response = new Response('foo');
+        $request = Request::create('/', 'HEAD');
+
+        $length = 12345;
+        $response->headers->set('Content-Length', $length);
+        $response->prepare($request);
+
+        $this->assertEquals('', $response->getContent());
+        $this->assertEquals($length, $response->headers->get('Content-Length'), 'Content-Length should be as if it was GET; see RFC2616 14.13');
+    }
+
+    public function testPrepareRemovesContentForInformationalResponse()
+    {
+        $response = new Response('foo');
+        $request = Request::create('/');
+
+        $response->setContent('content');
+        $response->setStatusCode(101);
+        $response->prepare($request);
+        $this->assertEquals('', $response->getContent());
+        $this->assertFalse($response->headers->has('Content-Type'));
+        $this->assertFalse($response->headers->has('Content-Type'));
+
+        $response->setContent('content');
+        $response->setStatusCode(304);
+        $response->prepare($request);
+        $this->assertEquals('', $response->getContent());
+        $this->assertFalse($response->headers->has('Content-Type'));
+        $this->assertFalse($response->headers->has('Content-Length'));
+    }
+
+    public function testPrepareRemovesContentLength()
+    {
+        $response = new Response('foo');
+        $request = Request::create('/');
+
+        $response->headers->set('Content-Length', 12345);
+        $response->prepare($request);
+        $this->assertEquals(12345, $response->headers->get('Content-Length'));
+
+        $response->headers->set('Transfer-Encoding', 'chunked');
+        $response->prepare($request);
+        $this->assertFalse($response->headers->has('Content-Length'));
+    }
+
+    public function testPrepareSetsPragmaOnHttp10Only()
+    {
+        $request = Request::create('/', 'GET');
+        $request->server->set('SERVER_PROTOCOL', 'HTTP/1.0');
+
+        $response = new Response('foo');
+        $response->prepare($request);
+        $this->assertEquals('no-cache', $response->headers->get('pragma'));
+        $this->assertEquals('-1', $response->headers->get('expires'));
+
+        $request->server->set('SERVER_PROTOCOL', 'HTTP/1.1');
+        $response = new Response('foo');
+        $response->prepare($request);
+        $this->assertFalse($response->headers->has('pragma'));
+        $this->assertFalse($response->headers->has('expires'));
+    }
+
+    public function testSetCache()
+    {
+        $response = new Response();
+        // ['etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public']
+        try {
+            $response->setCache(['wrong option' => 'value']);
+            $this->fail('->setCache() throws an InvalidArgumentException if an option is not supported');
+        } catch (\Exception $e) {
+            $this->assertInstanceOf('InvalidArgumentException', $e, '->setCache() throws an InvalidArgumentException if an option is not supported');
+            $this->assertContains('"wrong option"', $e->getMessage());
+        }
+
+        $options = ['etag' => '"whatever"'];
+        $response->setCache($options);
+        $this->assertEquals($response->getEtag(), '"whatever"');
+
+        $now = $this->createDateTimeNow();
+        $options = ['last_modified' => $now];
+        $response->setCache($options);
+        $this->assertEquals($response->getLastModified()->getTimestamp(), $now->getTimestamp());
+
+        $options = ['max_age' => 100];
+        $response->setCache($options);
+        $this->assertEquals($response->getMaxAge(), 100);
+
+        $options = ['s_maxage' => 200];
+        $response->setCache($options);
+        $this->assertEquals($response->getMaxAge(), 200);
+
+        $this->assertTrue($response->headers->hasCacheControlDirective('public'));
+        $this->assertFalse($response->headers->hasCacheControlDirective('private'));
+
+        $response->setCache(['public' => true]);
+        $this->assertTrue($response->headers->hasCacheControlDirective('public'));
+        $this->assertFalse($response->headers->hasCacheControlDirective('private'));
+
+        $response->setCache(['public' => false]);
+        $this->assertFalse($response->headers->hasCacheControlDirective('public'));
+        $this->assertTrue($response->headers->hasCacheControlDirective('private'));
+
+        $response->setCache(['private' => true]);
+        $this->assertFalse($response->headers->hasCacheControlDirective('public'));
+        $this->assertTrue($response->headers->hasCacheControlDirective('private'));
+
+        $response->setCache(['private' => false]);
+        $this->assertTrue($response->headers->hasCacheControlDirective('public'));
+        $this->assertFalse($response->headers->hasCacheControlDirective('private'));
+
+        $response->setCache(['immutable' => true]);
+        $this->assertTrue($response->headers->hasCacheControlDirective('immutable'));
+
+        $response->setCache(['immutable' => false]);
+        $this->assertFalse($response->headers->hasCacheControlDirective('immutable'));
+    }
+
+    public function testSendContent()
+    {
+        $response = new Response('test response rendering', 200);
+
+        ob_start();
+        $response->sendContent();
+        $string = ob_get_clean();
+        $this->assertContains('test response rendering', $string);
+    }
+
+    public function testSetPublic()
+    {
+        $response = new Response();
+        $response->setPublic();
+
+        $this->assertTrue($response->headers->hasCacheControlDirective('public'));
+        $this->assertFalse($response->headers->hasCacheControlDirective('private'));
+    }
+
+    public function testSetImmutable()
+    {
+        $response = new Response();
+        $response->setImmutable();
+
+        $this->assertTrue($response->headers->hasCacheControlDirective('immutable'));
+    }
+
+    public function testIsImmutable()
+    {
+        $response = new Response();
+        $response->setImmutable();
+
+        $this->assertTrue($response->isImmutable());
+    }
+
+    public function testSetExpires()
+    {
+        $response = new Response();
+        $response->setExpires(null);
+
+        $this->assertNull($response->getExpires(), '->setExpires() remove the header when passed null');
+
+        $now = $this->createDateTimeNow();
+        $response->setExpires($now);
+
+        $this->assertEquals($response->getExpires()->getTimestamp(), $now->getTimestamp());
+    }
+
+    public function testSetLastModified()
+    {
+        $response = new Response();
+        $response->setLastModified($this->createDateTimeNow());
+        $this->assertNotNull($response->getLastModified());
+
+        $response->setLastModified(null);
+        $this->assertNull($response->getLastModified());
+    }
+
+    public function testIsInvalid()
+    {
+        $response = new Response();
+
+        try {
+            $response->setStatusCode(99);
+            $this->fail();
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue($response->isInvalid());
+        }
+
+        try {
+            $response->setStatusCode(650);
+            $this->fail();
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue($response->isInvalid());
+        }
+
+        $response = new Response('', 200);
+        $this->assertFalse($response->isInvalid());
+    }
+
+    /**
+     * @dataProvider getStatusCodeFixtures
+     */
+    public function testSetStatusCode($code, $text, $expectedText)
+    {
+        $response = new Response();
+
+        $response->setStatusCode($code, $text);
+
+        $statusText = new \ReflectionProperty($response, 'statusText');
+        $statusText->setAccessible(true);
+
+        $this->assertEquals($expectedText, $statusText->getValue($response));
+    }
+
+    public function getStatusCodeFixtures()
+    {
+        return [
+            ['200', null, 'OK'],
+            ['200', false, ''],
+            ['200', 'foo', 'foo'],
+            ['199', null, 'unknown status'],
+            ['199', false, ''],
+            ['199', 'foo', 'foo'],
+        ];
+    }
+
+    public function testIsInformational()
+    {
+        $response = new Response('', 100);
+        $this->assertTrue($response->isInformational());
+
+        $response = new Response('', 200);
+        $this->assertFalse($response->isInformational());
+    }
+
+    public function testIsRedirectRedirection()
+    {
+        foreach ([301, 302, 303, 307] as $code) {
+            $response = new Response('', $code);
+            $this->assertTrue($response->isRedirection());
+            $this->assertTrue($response->isRedirect());
+        }
+
+        $response = new Response('', 304);
+        $this->assertTrue($response->isRedirection());
+        $this->assertFalse($response->isRedirect());
+
+        $response = new Response('', 200);
+        $this->assertFalse($response->isRedirection());
+        $this->assertFalse($response->isRedirect());
+
+        $response = new Response('', 404);
+        $this->assertFalse($response->isRedirection());
+        $this->assertFalse($response->isRedirect());
+
+        $response = new Response('', 301, ['Location' => '/good-uri']);
+        $this->assertFalse($response->isRedirect('/bad-uri'));
+        $this->assertTrue($response->isRedirect('/good-uri'));
+    }
+
+    public function testIsNotFound()
+    {
+        $response = new Response('', 404);
+        $this->assertTrue($response->isNotFound());
+
+        $response = new Response('', 200);
+        $this->assertFalse($response->isNotFound());
+    }
+
+    public function testIsEmpty()
+    {
+        foreach ([204, 304] as $code) {
+            $response = new Response('', $code);
+            $this->assertTrue($response->isEmpty());
+        }
+
+        $response = new Response('', 200);
+        $this->assertFalse($response->isEmpty());
+    }
+
+    public function testIsForbidden()
+    {
+        $response = new Response('', 403);
+        $this->assertTrue($response->isForbidden());
+
+        $response = new Response('', 200);
+        $this->assertFalse($response->isForbidden());
+    }
+
+    public function testIsOk()
+    {
+        $response = new Response('', 200);
+        $this->assertTrue($response->isOk());
+
+        $response = new Response('', 404);
+        $this->assertFalse($response->isOk());
+    }
+
+    public function testIsServerOrClientError()
+    {
+        $response = new Response('', 404);
+        $this->assertTrue($response->isClientError());
+        $this->assertFalse($response->isServerError());
+
+        $response = new Response('', 500);
+        $this->assertFalse($response->isClientError());
+        $this->assertTrue($response->isServerError());
+    }
+
+    public function testHasVary()
+    {
+        $response = new Response();
+        $this->assertFalse($response->hasVary());
+
+        $response->setVary('User-Agent');
+        $this->assertTrue($response->hasVary());
+    }
+
+    public function testSetEtag()
+    {
+        $response = new Response('', 200, ['ETag' => '"12345"']);
+        $response->setEtag();
+
+        $this->assertNull($response->headers->get('Etag'), '->setEtag() removes Etags when call with null');
+    }
+
+    /**
+     * @dataProvider validContentProvider
+     */
+    public function testSetContent($content)
+    {
+        $response = new Response();
+        $response->setContent($content);
+        $this->assertEquals((string) $content, $response->getContent());
+    }
+
+    /**
+     * @expectedException \UnexpectedValueException
+     * @dataProvider invalidContentProvider
+     */
+    public function testSetContentInvalid($content)
+    {
+        $response = new Response();
+        $response->setContent($content);
+    }
+
+    public function testSettersAreChainable()
+    {
+        $response = new Response();
+
+        $setters = [
+            'setProtocolVersion' => '1.0',
+            'setCharset' => 'UTF-8',
+            'setPublic' => null,
+            'setPrivate' => null,
+            'setDate' => $this->createDateTimeNow(),
+            'expire' => null,
+            'setMaxAge' => 1,
+            'setSharedMaxAge' => 1,
+            'setTtl' => 1,
+            'setClientTtl' => 1,
+        ];
+
+        foreach ($setters as $setter => $arg) {
+            $this->assertEquals($response, $response->{$setter}($arg));
+        }
+    }
+
+    public function testNoDeprecationsAreTriggered()
+    {
+        new DefaultResponse();
+        $this->getMockBuilder(Response::class)->getMock();
+
+        // we just need to ensure that subclasses of Response can be created without any deprecations
+        // being triggered if the subclass does not override any final methods
+        $this->addToAssertionCount(1);
+    }
+
+    public function validContentProvider()
+    {
+        return [
+            'obj' => [new StringableObject()],
+            'string' => ['Foo'],
+            'int' => [2],
+        ];
+    }
+
+    public function invalidContentProvider()
+    {
+        return [
+            'obj' => [new \stdClass()],
+            'array' => [[]],
+            'bool' => [true, '1'],
+        ];
+    }
+
+    protected function createDateTimeOneHourAgo()
+    {
+        return $this->createDateTimeNow()->sub(new \DateInterval('PT1H'));
+    }
+
+    protected function createDateTimeOneHourLater()
+    {
+        return $this->createDateTimeNow()->add(new \DateInterval('PT1H'));
+    }
+
+    protected function createDateTimeNow()
+    {
+        $date = new \DateTime();
+
+        return $date->setTimestamp(time());
+    }
+
+    protected function provideResponse()
+    {
+        return new Response();
+    }
+
+    /**
+     * @see       http://github.com/zendframework/zend-diactoros for the canonical source repository
+     *
+     * @author    Fábio Pacheco
+     * @copyright Copyright (c) 2015-2016 Zend Technologies USA Inc. (http://www.zend.com)
+     * @license   https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
+     */
+    public function ianaCodesReasonPhrasesProvider()
+    {
+        if (!\in_array('https', stream_get_wrappers(), true)) {
+            $this->markTestSkipped('The "https" wrapper is not available');
+        }
+
+        $ianaHttpStatusCodes = new \DOMDocument();
+
+        $context = stream_context_create([
+            'http' => [
+                'method' => 'GET',
+                'timeout' => 30,
+                'user_agent' => __METHOD__,
+            ],
+        ]);
+
+        $ianaHttpStatusCodes->loadXML(file_get_contents('https://www.iana.org/assignments/http-status-codes/http-status-codes.xml', false, $context));
+        if (!$ianaHttpStatusCodes->relaxNGValidate(__DIR__.'/schema/http-status-codes.rng')) {
+            self::fail('Invalid IANA\'s HTTP status code list.');
+        }
+
+        $ianaCodesReasonPhrases = [];
+
+        $xpath = new \DOMXPath($ianaHttpStatusCodes);
+        $xpath->registerNamespace('ns', 'http://www.iana.org/assignments');
+
+        $records = $xpath->query('//ns:record');
+        foreach ($records as $record) {
+            $value = $xpath->query('.//ns:value', $record)->item(0)->nodeValue;
+            $description = $xpath->query('.//ns:description', $record)->item(0)->nodeValue;
+
+            if (\in_array($description, ['Unassigned', '(Unused)'], true)) {
+                continue;
+            }
+
+            if (preg_match('/^([0-9]+)\s*\-\s*([0-9]+)$/', $value, $matches)) {
+                for ($value = $matches[1]; $value <= $matches[2]; ++$value) {
+                    $ianaCodesReasonPhrases[] = [$value, $description];
+                }
+            } else {
+                $ianaCodesReasonPhrases[] = [$value, $description];
+            }
+        }
+
+        return $ianaCodesReasonPhrases;
+    }
+
+    /**
+     * @dataProvider ianaCodesReasonPhrasesProvider
+     */
+    public function testReasonPhraseDefaultsAgainstIana($code, $reasonPhrase)
+    {
+        $this->assertEquals($reasonPhrase, Response::$statusTexts[$code]);
+    }
+}
+
+class StringableObject
+{
+    public function __toString()
+    {
+        return 'Foo';
+    }
+}
+
+class DefaultResponse extends Response
+{
+}
+
+class ExtendedResponse extends Response
+{
+    public function setLastModified(\DateTime $date = null)
+    {
+    }
+
+    public function getDate()
+    {
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/ResponseTestCase.php b/vendor/symfony/http-foundation/Tests/ResponseTestCase.php
new file mode 100755
index 0000000..4ead34c
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/ResponseTestCase.php
@@ -0,0 +1,89 @@
+<?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\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Request;
+
+abstract class ResponseTestCase extends TestCase
+{
+    public function testNoCacheControlHeaderOnAttachmentUsingHTTPSAndMSIE()
+    {
+        // Check for HTTPS and IE 8
+        $request = new Request();
+        $request->server->set('HTTPS', true);
+        $request->server->set('HTTP_USER_AGENT', 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)');
+
+        $response = $this->provideResponse();
+        $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
+        $response->prepare($request);
+
+        $this->assertFalse($response->headers->has('Cache-Control'));
+
+        // Check for IE 10 and HTTPS
+        $request->server->set('HTTP_USER_AGENT', 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)');
+
+        $response = $this->provideResponse();
+        $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
+        $response->prepare($request);
+
+        $this->assertTrue($response->headers->has('Cache-Control'));
+
+        // Check for IE 9 and HTTPS
+        $request->server->set('HTTP_USER_AGENT', 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 7.1; Trident/5.0)');
+
+        $response = $this->provideResponse();
+        $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
+        $response->prepare($request);
+
+        $this->assertTrue($response->headers->has('Cache-Control'));
+
+        // Check for IE 9 and HTTP
+        $request->server->set('HTTPS', false);
+
+        $response = $this->provideResponse();
+        $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
+        $response->prepare($request);
+
+        $this->assertTrue($response->headers->has('Cache-Control'));
+
+        // Check for IE 8 and HTTP
+        $request->server->set('HTTP_USER_AGENT', 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)');
+
+        $response = $this->provideResponse();
+        $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
+        $response->prepare($request);
+
+        $this->assertTrue($response->headers->has('Cache-Control'));
+
+        // Check for non-IE and HTTPS
+        $request->server->set('HTTPS', true);
+        $request->server->set('HTTP_USER_AGENT', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.60 Safari/537.17');
+
+        $response = $this->provideResponse();
+        $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
+        $response->prepare($request);
+
+        $this->assertTrue($response->headers->has('Cache-Control'));
+
+        // Check for non-IE and HTTP
+        $request->server->set('HTTPS', false);
+
+        $response = $this->provideResponse();
+        $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
+        $response->prepare($request);
+
+        $this->assertTrue($response->headers->has('Cache-Control'));
+    }
+
+    abstract protected function provideResponse();
+}
diff --git a/vendor/symfony/http-foundation/Tests/ServerBagTest.php b/vendor/symfony/http-foundation/Tests/ServerBagTest.php
new file mode 100755
index 0000000..0663b11
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/ServerBagTest.php
@@ -0,0 +1,170 @@
+<?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\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\ServerBag;
+
+/**
+ * ServerBagTest.
+ *
+ * @author Bulat Shakirzyanov <mallluhuct@gmail.com>
+ */
+class ServerBagTest extends TestCase
+{
+    public function testShouldExtractHeadersFromServerArray()
+    {
+        $server = [
+            'SOME_SERVER_VARIABLE' => 'value',
+            'SOME_SERVER_VARIABLE2' => 'value',
+            'ROOT' => 'value',
+            'HTTP_CONTENT_TYPE' => 'text/html',
+            'HTTP_CONTENT_LENGTH' => '0',
+            'HTTP_ETAG' => 'asdf',
+            'PHP_AUTH_USER' => 'foo',
+            'PHP_AUTH_PW' => 'bar',
+        ];
+
+        $bag = new ServerBag($server);
+
+        $this->assertEquals([
+            'CONTENT_TYPE' => 'text/html',
+            'CONTENT_LENGTH' => '0',
+            'ETAG' => 'asdf',
+            'AUTHORIZATION' => 'Basic '.base64_encode('foo:bar'),
+            'PHP_AUTH_USER' => 'foo',
+            'PHP_AUTH_PW' => 'bar',
+        ], $bag->getHeaders());
+    }
+
+    public function testHttpPasswordIsOptional()
+    {
+        $bag = new ServerBag(['PHP_AUTH_USER' => 'foo']);
+
+        $this->assertEquals([
+            'AUTHORIZATION' => 'Basic '.base64_encode('foo:'),
+            'PHP_AUTH_USER' => 'foo',
+            'PHP_AUTH_PW' => '',
+        ], $bag->getHeaders());
+    }
+
+    public function testHttpBasicAuthWithPhpCgi()
+    {
+        $bag = new ServerBag(['HTTP_AUTHORIZATION' => 'Basic '.base64_encode('foo:bar')]);
+
+        $this->assertEquals([
+            'AUTHORIZATION' => 'Basic '.base64_encode('foo:bar'),
+            'PHP_AUTH_USER' => 'foo',
+            'PHP_AUTH_PW' => 'bar',
+        ], $bag->getHeaders());
+    }
+
+    public function testHttpBasicAuthWithPhpCgiBogus()
+    {
+        $bag = new ServerBag(['HTTP_AUTHORIZATION' => 'Basic_'.base64_encode('foo:bar')]);
+
+        // Username and passwords should not be set as the header is bogus
+        $headers = $bag->getHeaders();
+        $this->assertArrayNotHasKey('PHP_AUTH_USER', $headers);
+        $this->assertArrayNotHasKey('PHP_AUTH_PW', $headers);
+    }
+
+    public function testHttpBasicAuthWithPhpCgiRedirect()
+    {
+        $bag = new ServerBag(['REDIRECT_HTTP_AUTHORIZATION' => 'Basic '.base64_encode('username:pass:word')]);
+
+        $this->assertEquals([
+            'AUTHORIZATION' => 'Basic '.base64_encode('username:pass:word'),
+            'PHP_AUTH_USER' => 'username',
+            'PHP_AUTH_PW' => 'pass:word',
+        ], $bag->getHeaders());
+    }
+
+    public function testHttpBasicAuthWithPhpCgiEmptyPassword()
+    {
+        $bag = new ServerBag(['HTTP_AUTHORIZATION' => 'Basic '.base64_encode('foo:')]);
+
+        $this->assertEquals([
+            'AUTHORIZATION' => 'Basic '.base64_encode('foo:'),
+            'PHP_AUTH_USER' => 'foo',
+            'PHP_AUTH_PW' => '',
+        ], $bag->getHeaders());
+    }
+
+    public function testHttpDigestAuthWithPhpCgi()
+    {
+        $digest = 'Digest username="foo", realm="acme", nonce="'.md5('secret').'", uri="/protected, qop="auth"';
+        $bag = new ServerBag(['HTTP_AUTHORIZATION' => $digest]);
+
+        $this->assertEquals([
+            'AUTHORIZATION' => $digest,
+            'PHP_AUTH_DIGEST' => $digest,
+        ], $bag->getHeaders());
+    }
+
+    public function testHttpDigestAuthWithPhpCgiBogus()
+    {
+        $digest = 'Digest_username="foo", realm="acme", nonce="'.md5('secret').'", uri="/protected, qop="auth"';
+        $bag = new ServerBag(['HTTP_AUTHORIZATION' => $digest]);
+
+        // Username and passwords should not be set as the header is bogus
+        $headers = $bag->getHeaders();
+        $this->assertArrayNotHasKey('PHP_AUTH_USER', $headers);
+        $this->assertArrayNotHasKey('PHP_AUTH_PW', $headers);
+    }
+
+    public function testHttpDigestAuthWithPhpCgiRedirect()
+    {
+        $digest = 'Digest username="foo", realm="acme", nonce="'.md5('secret').'", uri="/protected, qop="auth"';
+        $bag = new ServerBag(['REDIRECT_HTTP_AUTHORIZATION' => $digest]);
+
+        $this->assertEquals([
+            'AUTHORIZATION' => $digest,
+            'PHP_AUTH_DIGEST' => $digest,
+        ], $bag->getHeaders());
+    }
+
+    public function testOAuthBearerAuth()
+    {
+        $headerContent = 'Bearer L-yLEOr9zhmUYRkzN1jwwxwQ-PBNiKDc8dgfB4hTfvo';
+        $bag = new ServerBag(['HTTP_AUTHORIZATION' => $headerContent]);
+
+        $this->assertEquals([
+            'AUTHORIZATION' => $headerContent,
+        ], $bag->getHeaders());
+    }
+
+    public function testOAuthBearerAuthWithRedirect()
+    {
+        $headerContent = 'Bearer L-yLEOr9zhmUYRkzN1jwwxwQ-PBNiKDc8dgfB4hTfvo';
+        $bag = new ServerBag(['REDIRECT_HTTP_AUTHORIZATION' => $headerContent]);
+
+        $this->assertEquals([
+            'AUTHORIZATION' => $headerContent,
+        ], $bag->getHeaders());
+    }
+
+    /**
+     * @see https://github.com/symfony/symfony/issues/17345
+     */
+    public function testItDoesNotOverwriteTheAuthorizationHeaderIfItIsAlreadySet()
+    {
+        $headerContent = 'Bearer L-yLEOr9zhmUYRkzN1jwwxwQ-PBNiKDc8dgfB4hTfvo';
+        $bag = new ServerBag(['PHP_AUTH_USER' => 'foo', 'HTTP_AUTHORIZATION' => $headerContent]);
+
+        $this->assertEquals([
+            'AUTHORIZATION' => $headerContent,
+            'PHP_AUTH_USER' => 'foo',
+            'PHP_AUTH_PW' => '',
+        ], $bag->getHeaders());
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/Session/Attribute/AttributeBagTest.php b/vendor/symfony/http-foundation/Tests/Session/Attribute/AttributeBagTest.php
new file mode 100755
index 0000000..3f2f7b3
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Attribute/AttributeBagTest.php
@@ -0,0 +1,186 @@
+<?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\HttpFoundation\Tests\Session\Attribute;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
+
+/**
+ * Tests AttributeBag.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class AttributeBagTest extends TestCase
+{
+    private $array = [];
+
+    /**
+     * @var AttributeBag
+     */
+    private $bag;
+
+    protected function setUp()
+    {
+        $this->array = [
+            'hello' => 'world',
+            'always' => 'be happy',
+            'user.login' => 'drak',
+            'csrf.token' => [
+                'a' => '1234',
+                'b' => '4321',
+            ],
+            'category' => [
+                'fishing' => [
+                    'first' => 'cod',
+                    'second' => 'sole',
+                ],
+            ],
+        ];
+        $this->bag = new AttributeBag('_sf2');
+        $this->bag->initialize($this->array);
+    }
+
+    protected function tearDown()
+    {
+        $this->bag = null;
+        $this->array = [];
+    }
+
+    public function testInitialize()
+    {
+        $bag = new AttributeBag();
+        $bag->initialize($this->array);
+        $this->assertEquals($this->array, $bag->all());
+        $array = ['should' => 'change'];
+        $bag->initialize($array);
+        $this->assertEquals($array, $bag->all());
+    }
+
+    public function testGetStorageKey()
+    {
+        $this->assertEquals('_sf2', $this->bag->getStorageKey());
+        $attributeBag = new AttributeBag('test');
+        $this->assertEquals('test', $attributeBag->getStorageKey());
+    }
+
+    public function testGetSetName()
+    {
+        $this->assertEquals('attributes', $this->bag->getName());
+        $this->bag->setName('foo');
+        $this->assertEquals('foo', $this->bag->getName());
+    }
+
+    /**
+     * @dataProvider attributesProvider
+     */
+    public function testHas($key, $value, $exists)
+    {
+        $this->assertEquals($exists, $this->bag->has($key));
+    }
+
+    /**
+     * @dataProvider attributesProvider
+     */
+    public function testGet($key, $value, $expected)
+    {
+        $this->assertEquals($value, $this->bag->get($key));
+    }
+
+    public function testGetDefaults()
+    {
+        $this->assertNull($this->bag->get('user2.login'));
+        $this->assertEquals('default', $this->bag->get('user2.login', 'default'));
+    }
+
+    /**
+     * @dataProvider attributesProvider
+     */
+    public function testSet($key, $value, $expected)
+    {
+        $this->bag->set($key, $value);
+        $this->assertEquals($value, $this->bag->get($key));
+    }
+
+    public function testAll()
+    {
+        $this->assertEquals($this->array, $this->bag->all());
+
+        $this->bag->set('hello', 'fabien');
+        $array = $this->array;
+        $array['hello'] = 'fabien';
+        $this->assertEquals($array, $this->bag->all());
+    }
+
+    public function testReplace()
+    {
+        $array = [];
+        $array['name'] = 'jack';
+        $array['foo.bar'] = 'beep';
+        $this->bag->replace($array);
+        $this->assertEquals($array, $this->bag->all());
+        $this->assertNull($this->bag->get('hello'));
+        $this->assertNull($this->bag->get('always'));
+        $this->assertNull($this->bag->get('user.login'));
+    }
+
+    public function testRemove()
+    {
+        $this->assertEquals('world', $this->bag->get('hello'));
+        $this->bag->remove('hello');
+        $this->assertNull($this->bag->get('hello'));
+
+        $this->assertEquals('be happy', $this->bag->get('always'));
+        $this->bag->remove('always');
+        $this->assertNull($this->bag->get('always'));
+
+        $this->assertEquals('drak', $this->bag->get('user.login'));
+        $this->bag->remove('user.login');
+        $this->assertNull($this->bag->get('user.login'));
+    }
+
+    public function testClear()
+    {
+        $this->bag->clear();
+        $this->assertEquals([], $this->bag->all());
+    }
+
+    public function attributesProvider()
+    {
+        return [
+            ['hello', 'world', true],
+            ['always', 'be happy', true],
+            ['user.login', 'drak', true],
+            ['csrf.token', ['a' => '1234', 'b' => '4321'], true],
+            ['category', ['fishing' => ['first' => 'cod', 'second' => 'sole']], true],
+            ['user2.login', null, false],
+            ['never', null, false],
+            ['bye', null, false],
+            ['bye/for/now', null, false],
+        ];
+    }
+
+    public function testGetIterator()
+    {
+        $i = 0;
+        foreach ($this->bag as $key => $val) {
+            $this->assertEquals($this->array[$key], $val);
+            ++$i;
+        }
+
+        $this->assertEquals(\count($this->array), $i);
+    }
+
+    public function testCount()
+    {
+        $this->assertCount(\count($this->array), $this->bag);
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php b/vendor/symfony/http-foundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php
new file mode 100755
index 0000000..6b4bb17
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php
@@ -0,0 +1,204 @@
+<?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\HttpFoundation\Tests\Session\Attribute;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag;
+
+/**
+ * Tests NamespacedAttributeBag.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class NamespacedAttributeBagTest extends TestCase
+{
+    private $array = [];
+
+    /**
+     * @var NamespacedAttributeBag
+     */
+    private $bag;
+
+    protected function setUp()
+    {
+        $this->array = [
+            'hello' => 'world',
+            'always' => 'be happy',
+            'user.login' => 'drak',
+            'csrf.token' => [
+                'a' => '1234',
+                'b' => '4321',
+            ],
+            'category' => [
+                'fishing' => [
+                    'first' => 'cod',
+                    'second' => 'sole',
+                ],
+            ],
+        ];
+        $this->bag = new NamespacedAttributeBag('_sf2', '/');
+        $this->bag->initialize($this->array);
+    }
+
+    protected function tearDown()
+    {
+        $this->bag = null;
+        $this->array = [];
+    }
+
+    public function testInitialize()
+    {
+        $bag = new NamespacedAttributeBag();
+        $bag->initialize($this->array);
+        $this->assertEquals($this->array, $this->bag->all());
+        $array = ['should' => 'not stick'];
+        $bag->initialize($array);
+
+        // should have remained the same
+        $this->assertEquals($this->array, $this->bag->all());
+    }
+
+    public function testGetStorageKey()
+    {
+        $this->assertEquals('_sf2', $this->bag->getStorageKey());
+        $attributeBag = new NamespacedAttributeBag('test');
+        $this->assertEquals('test', $attributeBag->getStorageKey());
+    }
+
+    /**
+     * @dataProvider attributesProvider
+     */
+    public function testHas($key, $value, $exists)
+    {
+        $this->assertEquals($exists, $this->bag->has($key));
+    }
+
+    /**
+     * @dataProvider attributesProvider
+     */
+    public function testHasNoSideEffect($key, $value, $expected)
+    {
+        $expected = json_encode($this->bag->all());
+        $this->bag->has($key);
+
+        $this->assertEquals($expected, json_encode($this->bag->all()));
+    }
+
+    /**
+     * @dataProvider attributesProvider
+     */
+    public function testGet($key, $value, $expected)
+    {
+        $this->assertEquals($value, $this->bag->get($key));
+    }
+
+    public function testGetDefaults()
+    {
+        $this->assertNull($this->bag->get('user2.login'));
+        $this->assertEquals('default', $this->bag->get('user2.login', 'default'));
+    }
+
+    /**
+     * @dataProvider attributesProvider
+     */
+    public function testGetNoSideEffect($key, $value, $expected)
+    {
+        $expected = json_encode($this->bag->all());
+        $this->bag->get($key);
+
+        $this->assertEquals($expected, json_encode($this->bag->all()));
+    }
+
+    /**
+     * @dataProvider attributesProvider
+     */
+    public function testSet($key, $value, $expected)
+    {
+        $this->bag->set($key, $value);
+        $this->assertEquals($value, $this->bag->get($key));
+    }
+
+    public function testAll()
+    {
+        $this->assertEquals($this->array, $this->bag->all());
+
+        $this->bag->set('hello', 'fabien');
+        $array = $this->array;
+        $array['hello'] = 'fabien';
+        $this->assertEquals($array, $this->bag->all());
+    }
+
+    public function testReplace()
+    {
+        $array = [];
+        $array['name'] = 'jack';
+        $array['foo.bar'] = 'beep';
+        $this->bag->replace($array);
+        $this->assertEquals($array, $this->bag->all());
+        $this->assertNull($this->bag->get('hello'));
+        $this->assertNull($this->bag->get('always'));
+        $this->assertNull($this->bag->get('user.login'));
+    }
+
+    public function testRemove()
+    {
+        $this->assertEquals('world', $this->bag->get('hello'));
+        $this->bag->remove('hello');
+        $this->assertNull($this->bag->get('hello'));
+
+        $this->assertEquals('be happy', $this->bag->get('always'));
+        $this->bag->remove('always');
+        $this->assertNull($this->bag->get('always'));
+
+        $this->assertEquals('drak', $this->bag->get('user.login'));
+        $this->bag->remove('user.login');
+        $this->assertNull($this->bag->get('user.login'));
+    }
+
+    public function testRemoveExistingNamespacedAttribute()
+    {
+        $this->assertSame('cod', $this->bag->remove('category/fishing/first'));
+    }
+
+    public function testRemoveNonexistingNamespacedAttribute()
+    {
+        $this->assertNull($this->bag->remove('foo/bar/baz'));
+    }
+
+    public function testClear()
+    {
+        $this->bag->clear();
+        $this->assertEquals([], $this->bag->all());
+    }
+
+    public function attributesProvider()
+    {
+        return [
+            ['hello', 'world', true],
+            ['always', 'be happy', true],
+            ['user.login', 'drak', true],
+            ['csrf.token', ['a' => '1234', 'b' => '4321'], true],
+            ['csrf.token/a', '1234', true],
+            ['csrf.token/b', '4321', true],
+            ['category', ['fishing' => ['first' => 'cod', 'second' => 'sole']], true],
+            ['category/fishing', ['first' => 'cod', 'second' => 'sole'], true],
+            ['category/fishing/missing/first', null, false],
+            ['category/fishing/first', 'cod', true],
+            ['category/fishing/second', 'sole', true],
+            ['category/fishing/missing/second', null, false],
+            ['user2.login', null, false],
+            ['never', null, false],
+            ['bye', null, false],
+            ['bye/for/now', null, false],
+        ];
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/Session/Flash/AutoExpireFlashBagTest.php b/vendor/symfony/http-foundation/Tests/Session/Flash/AutoExpireFlashBagTest.php
new file mode 100755
index 0000000..b4e2c3a
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Flash/AutoExpireFlashBagTest.php
@@ -0,0 +1,161 @@
+<?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\HttpFoundation\Tests\Session\Flash;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag as FlashBag;
+
+/**
+ * AutoExpireFlashBagTest.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class AutoExpireFlashBagTest extends TestCase
+{
+    /**
+     * @var \Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag
+     */
+    private $bag;
+
+    protected $array = [];
+
+    protected function setUp()
+    {
+        parent::setUp();
+        $this->bag = new FlashBag();
+        $this->array = ['new' => ['notice' => ['A previous flash message']]];
+        $this->bag->initialize($this->array);
+    }
+
+    protected function tearDown()
+    {
+        $this->bag = null;
+        parent::tearDown();
+    }
+
+    public function testInitialize()
+    {
+        $bag = new FlashBag();
+        $array = ['new' => ['notice' => ['A previous flash message']]];
+        $bag->initialize($array);
+        $this->assertEquals(['A previous flash message'], $bag->peek('notice'));
+        $array = ['new' => [
+                'notice' => ['Something else'],
+                'error' => ['a'],
+            ]];
+        $bag->initialize($array);
+        $this->assertEquals(['Something else'], $bag->peek('notice'));
+        $this->assertEquals(['a'], $bag->peek('error'));
+    }
+
+    public function testGetStorageKey()
+    {
+        $this->assertEquals('_symfony_flashes', $this->bag->getStorageKey());
+        $attributeBag = new FlashBag('test');
+        $this->assertEquals('test', $attributeBag->getStorageKey());
+    }
+
+    public function testGetSetName()
+    {
+        $this->assertEquals('flashes', $this->bag->getName());
+        $this->bag->setName('foo');
+        $this->assertEquals('foo', $this->bag->getName());
+    }
+
+    public function testPeek()
+    {
+        $this->assertEquals([], $this->bag->peek('non_existing'));
+        $this->assertEquals(['default'], $this->bag->peek('non_existing', ['default']));
+        $this->assertEquals(['A previous flash message'], $this->bag->peek('notice'));
+        $this->assertEquals(['A previous flash message'], $this->bag->peek('notice'));
+    }
+
+    public function testSet()
+    {
+        $this->bag->set('notice', 'Foo');
+        $this->assertEquals(['A previous flash message'], $this->bag->peek('notice'));
+    }
+
+    public function testHas()
+    {
+        $this->assertFalse($this->bag->has('nothing'));
+        $this->assertTrue($this->bag->has('notice'));
+    }
+
+    public function testKeys()
+    {
+        $this->assertEquals(['notice'], $this->bag->keys());
+    }
+
+    public function testPeekAll()
+    {
+        $array = [
+            'new' => [
+                'notice' => 'Foo',
+                'error' => 'Bar',
+            ],
+        ];
+
+        $this->bag->initialize($array);
+        $this->assertEquals([
+            'notice' => 'Foo',
+            'error' => 'Bar',
+            ], $this->bag->peekAll()
+        );
+
+        $this->assertEquals([
+            'notice' => 'Foo',
+            'error' => 'Bar',
+            ], $this->bag->peekAll()
+        );
+    }
+
+    public function testGet()
+    {
+        $this->assertEquals([], $this->bag->get('non_existing'));
+        $this->assertEquals(['default'], $this->bag->get('non_existing', ['default']));
+        $this->assertEquals(['A previous flash message'], $this->bag->get('notice'));
+        $this->assertEquals([], $this->bag->get('notice'));
+    }
+
+    public function testSetAll()
+    {
+        $this->bag->setAll(['a' => 'first', 'b' => 'second']);
+        $this->assertFalse($this->bag->has('a'));
+        $this->assertFalse($this->bag->has('b'));
+    }
+
+    public function testAll()
+    {
+        $this->bag->set('notice', 'Foo');
+        $this->bag->set('error', 'Bar');
+        $this->assertEquals([
+            'notice' => ['A previous flash message'],
+            ], $this->bag->all()
+        );
+
+        $this->assertEquals([], $this->bag->all());
+    }
+
+    public function testClear()
+    {
+        $this->assertEquals(['notice' => ['A previous flash message']], $this->bag->clear());
+    }
+
+    public function testDoNotRemoveTheNewFlashesWhenDisplayingTheExistingOnes()
+    {
+        $this->bag->add('success', 'Something');
+        $this->bag->all();
+
+        $this->assertEquals(['new' => ['success' => ['Something']], 'display' => []], $this->array);
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/Session/Flash/FlashBagTest.php b/vendor/symfony/http-foundation/Tests/Session/Flash/FlashBagTest.php
new file mode 100755
index 0000000..6d8619e
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Flash/FlashBagTest.php
@@ -0,0 +1,157 @@
+<?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\HttpFoundation\Tests\Session\Flash;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
+
+/**
+ * FlashBagTest.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class FlashBagTest extends TestCase
+{
+    /**
+     * @var \Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface
+     */
+    private $bag;
+
+    protected $array = [];
+
+    protected function setUp()
+    {
+        parent::setUp();
+        $this->bag = new FlashBag();
+        $this->array = ['notice' => ['A previous flash message']];
+        $this->bag->initialize($this->array);
+    }
+
+    protected function tearDown()
+    {
+        $this->bag = null;
+        parent::tearDown();
+    }
+
+    public function testInitialize()
+    {
+        $bag = new FlashBag();
+        $bag->initialize($this->array);
+        $this->assertEquals($this->array, $bag->peekAll());
+        $array = ['should' => ['change']];
+        $bag->initialize($array);
+        $this->assertEquals($array, $bag->peekAll());
+    }
+
+    public function testGetStorageKey()
+    {
+        $this->assertEquals('_symfony_flashes', $this->bag->getStorageKey());
+        $attributeBag = new FlashBag('test');
+        $this->assertEquals('test', $attributeBag->getStorageKey());
+    }
+
+    public function testGetSetName()
+    {
+        $this->assertEquals('flashes', $this->bag->getName());
+        $this->bag->setName('foo');
+        $this->assertEquals('foo', $this->bag->getName());
+    }
+
+    public function testPeek()
+    {
+        $this->assertEquals([], $this->bag->peek('non_existing'));
+        $this->assertEquals(['default'], $this->bag->peek('not_existing', ['default']));
+        $this->assertEquals(['A previous flash message'], $this->bag->peek('notice'));
+        $this->assertEquals(['A previous flash message'], $this->bag->peek('notice'));
+    }
+
+    public function testAdd()
+    {
+        $tab = ['bar' => 'baz'];
+        $this->bag->add('string_message', 'lorem');
+        $this->bag->add('object_message', new \stdClass());
+        $this->bag->add('array_message', $tab);
+
+        $this->assertEquals(['lorem'], $this->bag->get('string_message'));
+        $this->assertEquals([new \stdClass()], $this->bag->get('object_message'));
+        $this->assertEquals([$tab], $this->bag->get('array_message'));
+    }
+
+    public function testGet()
+    {
+        $this->assertEquals([], $this->bag->get('non_existing'));
+        $this->assertEquals(['default'], $this->bag->get('not_existing', ['default']));
+        $this->assertEquals(['A previous flash message'], $this->bag->get('notice'));
+        $this->assertEquals([], $this->bag->get('notice'));
+    }
+
+    public function testAll()
+    {
+        $this->bag->set('notice', 'Foo');
+        $this->bag->set('error', 'Bar');
+        $this->assertEquals([
+            'notice' => ['Foo'],
+            'error' => ['Bar'], ], $this->bag->all()
+        );
+
+        $this->assertEquals([], $this->bag->all());
+    }
+
+    public function testSet()
+    {
+        $this->bag->set('notice', 'Foo');
+        $this->bag->set('notice', 'Bar');
+        $this->assertEquals(['Bar'], $this->bag->peek('notice'));
+    }
+
+    public function testHas()
+    {
+        $this->assertFalse($this->bag->has('nothing'));
+        $this->assertTrue($this->bag->has('notice'));
+    }
+
+    public function testKeys()
+    {
+        $this->assertEquals(['notice'], $this->bag->keys());
+    }
+
+    public function testSetAll()
+    {
+        $this->bag->add('one_flash', 'Foo');
+        $this->bag->add('another_flash', 'Bar');
+        $this->assertTrue($this->bag->has('one_flash'));
+        $this->assertTrue($this->bag->has('another_flash'));
+        $this->bag->setAll(['unique_flash' => 'FooBar']);
+        $this->assertFalse($this->bag->has('one_flash'));
+        $this->assertFalse($this->bag->has('another_flash'));
+        $this->assertSame(['unique_flash' => 'FooBar'], $this->bag->all());
+        $this->assertSame([], $this->bag->all());
+    }
+
+    public function testPeekAll()
+    {
+        $this->bag->set('notice', 'Foo');
+        $this->bag->set('error', 'Bar');
+        $this->assertEquals([
+            'notice' => ['Foo'],
+            'error' => ['Bar'],
+            ], $this->bag->peekAll()
+        );
+        $this->assertTrue($this->bag->has('notice'));
+        $this->assertTrue($this->bag->has('error'));
+        $this->assertEquals([
+            'notice' => ['Foo'],
+            'error' => ['Bar'],
+            ], $this->bag->peekAll()
+        );
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/Session/SessionTest.php b/vendor/symfony/http-foundation/Tests/Session/SessionTest.php
new file mode 100755
index 0000000..afa00fc
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/SessionTest.php
@@ -0,0 +1,263 @@
+<?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\HttpFoundation\Tests\Session;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
+use Symfony\Component\HttpFoundation\Session\Session;
+use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
+
+/**
+ * SessionTest.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Robert Schönthal <seroscho@googlemail.com>
+ * @author Drak <drak@zikula.org>
+ */
+class SessionTest extends TestCase
+{
+    /**
+     * @var \Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface
+     */
+    protected $storage;
+
+    /**
+     * @var \Symfony\Component\HttpFoundation\Session\SessionInterface
+     */
+    protected $session;
+
+    protected function setUp()
+    {
+        $this->storage = new MockArraySessionStorage();
+        $this->session = new Session($this->storage, new AttributeBag(), new FlashBag());
+    }
+
+    protected function tearDown()
+    {
+        $this->storage = null;
+        $this->session = null;
+    }
+
+    public function testStart()
+    {
+        $this->assertEquals('', $this->session->getId());
+        $this->assertTrue($this->session->start());
+        $this->assertNotEquals('', $this->session->getId());
+    }
+
+    public function testIsStarted()
+    {
+        $this->assertFalse($this->session->isStarted());
+        $this->session->start();
+        $this->assertTrue($this->session->isStarted());
+    }
+
+    public function testSetId()
+    {
+        $this->assertEquals('', $this->session->getId());
+        $this->session->setId('0123456789abcdef');
+        $this->session->start();
+        $this->assertEquals('0123456789abcdef', $this->session->getId());
+    }
+
+    public function testSetIdAfterStart()
+    {
+        $this->session->start();
+        $id = $this->session->getId();
+
+        $e = null;
+        try {
+            $this->session->setId($id);
+        } catch (\Exception $e) {
+        }
+
+        $this->assertNull($e);
+
+        try {
+            $this->session->setId('different');
+        } catch (\Exception $e) {
+        }
+
+        $this->assertInstanceOf('\LogicException', $e);
+    }
+
+    public function testSetName()
+    {
+        $this->assertEquals('MOCKSESSID', $this->session->getName());
+        $this->session->setName('session.test.com');
+        $this->session->start();
+        $this->assertEquals('session.test.com', $this->session->getName());
+    }
+
+    public function testGet()
+    {
+        // tests defaults
+        $this->assertNull($this->session->get('foo'));
+        $this->assertEquals(1, $this->session->get('foo', 1));
+    }
+
+    /**
+     * @dataProvider setProvider
+     */
+    public function testSet($key, $value)
+    {
+        $this->session->set($key, $value);
+        $this->assertEquals($value, $this->session->get($key));
+    }
+
+    /**
+     * @dataProvider setProvider
+     */
+    public function testHas($key, $value)
+    {
+        $this->session->set($key, $value);
+        $this->assertTrue($this->session->has($key));
+        $this->assertFalse($this->session->has($key.'non_value'));
+    }
+
+    public function testReplace()
+    {
+        $this->session->replace(['happiness' => 'be good', 'symfony' => 'awesome']);
+        $this->assertEquals(['happiness' => 'be good', 'symfony' => 'awesome'], $this->session->all());
+        $this->session->replace([]);
+        $this->assertEquals([], $this->session->all());
+    }
+
+    /**
+     * @dataProvider setProvider
+     */
+    public function testAll($key, $value, $result)
+    {
+        $this->session->set($key, $value);
+        $this->assertEquals($result, $this->session->all());
+    }
+
+    /**
+     * @dataProvider setProvider
+     */
+    public function testClear($key, $value)
+    {
+        $this->session->set('hi', 'fabien');
+        $this->session->set($key, $value);
+        $this->session->clear();
+        $this->assertEquals([], $this->session->all());
+    }
+
+    public function setProvider()
+    {
+        return [
+            ['foo', 'bar', ['foo' => 'bar']],
+            ['foo.bar', 'too much beer', ['foo.bar' => 'too much beer']],
+            ['great', 'symfony is great', ['great' => 'symfony is great']],
+        ];
+    }
+
+    /**
+     * @dataProvider setProvider
+     */
+    public function testRemove($key, $value)
+    {
+        $this->session->set('hi.world', 'have a nice day');
+        $this->session->set($key, $value);
+        $this->session->remove($key);
+        $this->assertEquals(['hi.world' => 'have a nice day'], $this->session->all());
+    }
+
+    public function testInvalidate()
+    {
+        $this->session->set('invalidate', 123);
+        $this->session->invalidate();
+        $this->assertEquals([], $this->session->all());
+    }
+
+    public function testMigrate()
+    {
+        $this->session->set('migrate', 321);
+        $this->session->migrate();
+        $this->assertEquals(321, $this->session->get('migrate'));
+    }
+
+    public function testMigrateDestroy()
+    {
+        $this->session->set('migrate', 333);
+        $this->session->migrate(true);
+        $this->assertEquals(333, $this->session->get('migrate'));
+    }
+
+    public function testSave()
+    {
+        $this->session->start();
+        $this->session->save();
+
+        $this->assertFalse($this->session->isStarted());
+    }
+
+    public function testGetId()
+    {
+        $this->assertEquals('', $this->session->getId());
+        $this->session->start();
+        $this->assertNotEquals('', $this->session->getId());
+    }
+
+    public function testGetFlashBag()
+    {
+        $this->assertInstanceOf('Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface', $this->session->getFlashBag());
+    }
+
+    public function testGetIterator()
+    {
+        $attributes = ['hello' => 'world', 'symfony' => 'rocks'];
+        foreach ($attributes as $key => $val) {
+            $this->session->set($key, $val);
+        }
+
+        $i = 0;
+        foreach ($this->session as $key => $val) {
+            $this->assertEquals($attributes[$key], $val);
+            ++$i;
+        }
+
+        $this->assertEquals(\count($attributes), $i);
+    }
+
+    public function testGetCount()
+    {
+        $this->session->set('hello', 'world');
+        $this->session->set('symfony', 'rocks');
+
+        $this->assertCount(2, $this->session);
+    }
+
+    public function testGetMeta()
+    {
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\MetadataBag', $this->session->getMetadataBag());
+    }
+
+    public function testIsEmpty()
+    {
+        $this->assertTrue($this->session->isEmpty());
+
+        $this->session->set('hello', 'world');
+        $this->assertFalse($this->session->isEmpty());
+
+        $this->session->remove('hello');
+        $this->assertTrue($this->session->isEmpty());
+
+        $flash = $this->session->getFlashBag();
+        $flash->set('hello', 'world');
+        $this->assertFalse($this->session->isEmpty());
+
+        $flash->get('hello');
+        $this->assertTrue($this->session->isEmpty());
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php
new file mode 100755
index 0000000..98bc67b
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php
@@ -0,0 +1,61 @@
+<?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\HttpFoundation\Tests\Session\Storage\Handler;
+
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @requires PHP 7.0
+ */
+class AbstractSessionHandlerTest extends TestCase
+{
+    private static $server;
+
+    public static function setUpBeforeClass()
+    {
+        $spec = [
+            1 => ['file', '/dev/null', 'w'],
+            2 => ['file', '/dev/null', 'w'],
+        ];
+        if (!self::$server = @proc_open('exec php -S localhost:8053', $spec, $pipes, __DIR__.'/Fixtures')) {
+            self::markTestSkipped('PHP server unable to start.');
+        }
+        sleep(1);
+    }
+
+    public static function tearDownAfterClass()
+    {
+        if (self::$server) {
+            proc_terminate(self::$server);
+            proc_close(self::$server);
+        }
+    }
+
+    /**
+     * @dataProvider provideSession
+     */
+    public function testSession($fixture)
+    {
+        $context = ['http' => ['header' => "Cookie: sid=123abc\r\n"]];
+        $context = stream_context_create($context);
+        $result = file_get_contents(sprintf('http://localhost:8053/%s.php', $fixture), false, $context);
+
+        $this->assertStringEqualsFile(__DIR__.sprintf('/Fixtures/%s.expected', $fixture), $result);
+    }
+
+    public function provideSession()
+    {
+        foreach (glob(__DIR__.'/Fixtures/*.php') as $file) {
+            yield [pathinfo($file, PATHINFO_FILENAME)];
+        }
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/common.inc b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/common.inc
new file mode 100755
index 0000000..7a064c7
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/common.inc
@@ -0,0 +1,151 @@
+<?php
+
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\AbstractSessionHandler;
+
+$parent = __DIR__;
+while (!@file_exists($parent.'/vendor/autoload.php')) {
+    if (!@file_exists($parent)) {
+        // open_basedir restriction in effect
+        break;
+    }
+    if ($parent === dirname($parent)) {
+        echo "vendor/autoload.php not found\n";
+        exit(1);
+    }
+
+    $parent = dirname($parent);
+}
+
+require $parent.'/vendor/autoload.php';
+
+error_reporting(-1);
+ini_set('html_errors', 0);
+ini_set('display_errors', 1);
+ini_set('session.gc_probability', 0);
+ini_set('session.serialize_handler', 'php');
+ini_set('session.cookie_lifetime', 0);
+ini_set('session.cookie_domain', '');
+ini_set('session.cookie_secure', '');
+ini_set('session.cookie_httponly', '');
+ini_set('session.use_cookies', 1);
+ini_set('session.use_only_cookies', 1);
+ini_set('session.cache_expire', 180);
+ini_set('session.cookie_path', '/');
+ini_set('session.cookie_domain', '');
+ini_set('session.cookie_secure', 1);
+ini_set('session.cookie_httponly', 1);
+ini_set('session.use_strict_mode', 1);
+ini_set('session.lazy_write', 1);
+ini_set('session.name', 'sid');
+ini_set('session.save_path', __DIR__);
+ini_set('session.cache_limiter', '');
+
+header_remove('X-Powered-By');
+header('Content-Type: text/plain; charset=utf-8');
+
+register_shutdown_function(function () {
+    echo "\n";
+    session_write_close();
+    print_r(headers_list());
+    echo "shutdown\n";
+});
+ob_start();
+
+class TestSessionHandler extends AbstractSessionHandler
+{
+    private $data;
+
+    public function __construct($data = '')
+    {
+        $this->data = $data;
+    }
+
+    public function open($path, $name)
+    {
+        echo __FUNCTION__, "\n";
+
+        return parent::open($path, $name);
+    }
+
+    public function validateId($sessionId)
+    {
+        echo __FUNCTION__, "\n";
+
+        return parent::validateId($sessionId);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function read($sessionId)
+    {
+        echo __FUNCTION__, "\n";
+
+        return parent::read($sessionId);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function updateTimestamp($sessionId, $data)
+    {
+        echo __FUNCTION__, "\n";
+
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function write($sessionId, $data)
+    {
+        echo __FUNCTION__, "\n";
+
+        return parent::write($sessionId, $data);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function destroy($sessionId)
+    {
+        echo __FUNCTION__, "\n";
+
+        return parent::destroy($sessionId);
+    }
+
+    public function close()
+    {
+        echo __FUNCTION__, "\n";
+
+        return true;
+    }
+
+    public function gc($maxLifetime)
+    {
+        echo __FUNCTION__, "\n";
+
+        return true;
+    }
+
+    protected function doRead($sessionId)
+    {
+        echo __FUNCTION__.': ', $this->data, "\n";
+
+        return $this->data;
+    }
+
+    protected function doWrite($sessionId, $data)
+    {
+        echo __FUNCTION__.': ', $data, "\n";
+
+        return true;
+    }
+
+    protected function doDestroy($sessionId)
+    {
+        echo __FUNCTION__, "\n";
+
+        return true;
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.expected b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.expected
new file mode 100755
index 0000000..8203714
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.expected
@@ -0,0 +1,17 @@
+open
+validateId
+read
+doRead: abc|i:123;
+read
+
+write
+destroy
+doDestroy
+close
+Array
+(
+    [0] => Content-Type: text/plain; charset=utf-8
+    [1] => Cache-Control: max-age=10800, private, must-revalidate
+    [2] => Set-Cookie: sid=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; secure; HttpOnly
+)
+shutdown
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.php
new file mode 100755
index 0000000..3cfc125
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.php
@@ -0,0 +1,8 @@
+<?php
+
+require __DIR__.'/common.inc';
+
+session_set_save_handler(new TestSessionHandler('abc|i:123;'), false);
+session_start();
+
+unset($_SESSION['abc']);
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.expected b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.expected
new file mode 100755
index 0000000..587adaf
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.expected
@@ -0,0 +1,14 @@
+open
+validateId
+read
+doRead: abc|i:123;
+read
+123
+updateTimestamp
+close
+Array
+(
+    [0] => Content-Type: text/plain; charset=utf-8
+    [1] => Cache-Control: max-age=10800, private, must-revalidate
+)
+shutdown
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.php
new file mode 100755
index 0000000..3e62fb9
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.php
@@ -0,0 +1,8 @@
+<?php
+
+require __DIR__.'/common.inc';
+
+session_set_save_handler(new TestSessionHandler('abc|i:123;'), false);
+session_start();
+
+echo $_SESSION['abc'];
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.expected b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.expected
new file mode 100755
index 0000000..baa5f2f
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.expected
@@ -0,0 +1,24 @@
+open
+validateId
+read
+doRead: abc|i:123;
+read
+destroy
+doDestroy
+close
+open
+validateId
+read
+doRead: abc|i:123;
+read
+
+write
+doWrite: abc|i:123;
+close
+Array
+(
+    [0] => Content-Type: text/plain; charset=utf-8
+    [1] => Cache-Control: max-age=10800, private, must-revalidate
+    [2] => Set-Cookie: sid=random_session_id; path=/; secure; HttpOnly
+)
+shutdown
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.php
new file mode 100755
index 0000000..a0f635c
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.php
@@ -0,0 +1,10 @@
+<?php
+
+require __DIR__.'/common.inc';
+
+session_set_save_handler(new TestSessionHandler('abc|i:123;'), false);
+session_start();
+
+session_regenerate_id(true);
+
+ob_start(function ($buffer) { return str_replace(session_id(), 'random_session_id', $buffer); });
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.expected b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.expected
new file mode 100755
index 0000000..4533a10
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.expected
@@ -0,0 +1,20 @@
+open
+validateId
+read
+doRead: 
+read
+Array
+(
+    [0] => bar
+)
+$_SESSION is not empty
+write
+destroy
+close
+$_SESSION is not empty
+Array
+(
+    [0] => Content-Type: text/plain; charset=utf-8
+    [1] => Cache-Control: max-age=0, private, must-revalidate
+)
+shutdown
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.php
new file mode 100755
index 0000000..96dca3c
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.php
@@ -0,0 +1,24 @@
+<?php
+
+require __DIR__.'/common.inc';
+
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
+use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
+
+$storage = new NativeSessionStorage();
+$storage->setSaveHandler(new TestSessionHandler());
+$flash = new FlashBag();
+$storage->registerBag($flash);
+$storage->start();
+
+$flash->add('foo', 'bar');
+
+print_r($flash->get('foo'));
+echo empty($_SESSION) ? '$_SESSION is empty' : '$_SESSION is not empty';
+echo "\n";
+
+$storage->save();
+
+echo empty($_SESSION) ? '$_SESSION is empty' : '$_SESSION is not empty';
+
+ob_start(function ($buffer) { return str_replace(session_id(), 'random_session_id', $buffer); });
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.expected b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.expected
new file mode 100755
index 0000000..33da0a5
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.expected
@@ -0,0 +1,15 @@
+open
+validateId
+read
+doRead: abc|i:123;
+read
+
+updateTimestamp
+close
+Array
+(
+    [0] => Content-Type: text/plain; charset=utf-8
+    [1] => Cache-Control: max-age=10800, private, must-revalidate
+    [2] => Set-Cookie: abc=def
+)
+shutdown
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.php
new file mode 100755
index 0000000..ffb5b20
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.php
@@ -0,0 +1,8 @@
+<?php
+
+require __DIR__.'/common.inc';
+
+session_set_save_handler(new TestSessionHandler('abc|i:123;'), false);
+session_start();
+
+setcookie('abc', 'def');
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.expected b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.expected
new file mode 100755
index 0000000..5de2d9e
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.expected
@@ -0,0 +1,24 @@
+open
+validateId
+read
+doRead: abc|i:123;
+read
+updateTimestamp
+close
+open
+validateId
+read
+doRead: abc|i:123;
+read
+
+write
+destroy
+doDestroy
+close
+Array
+(
+    [0] => Content-Type: text/plain; charset=utf-8
+    [1] => Cache-Control: max-age=10800, private, must-revalidate
+    [2] => Set-Cookie: abc=def
+)
+shutdown
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.php
new file mode 100755
index 0000000..ec51193
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.php
@@ -0,0 +1,13 @@
+<?php
+
+require __DIR__.'/common.inc';
+
+setcookie('abc', 'def');
+
+session_set_save_handler(new TestSessionHandler('abc|i:123;'), false);
+session_start();
+session_write_close();
+session_start();
+
+$_SESSION['abc'] = 234;
+unset($_SESSION['abc']);
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.php
new file mode 100755
index 0000000..68daa4e
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.php
@@ -0,0 +1,135 @@
+<?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\HttpFoundation\Tests\Session\Storage\Handler;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcacheSessionHandler;
+
+/**
+ * @requires extension memcache
+ * @group time-sensitive
+ * @group legacy
+ */
+class MemcacheSessionHandlerTest extends TestCase
+{
+    const PREFIX = 'prefix_';
+    const TTL = 1000;
+
+    /**
+     * @var MemcacheSessionHandler
+     */
+    protected $storage;
+
+    protected $memcache;
+
+    protected function setUp()
+    {
+        if (\defined('HHVM_VERSION')) {
+            $this->markTestSkipped('PHPUnit_MockObject cannot mock the Memcache class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289');
+        }
+
+        parent::setUp();
+        $this->memcache = $this->getMockBuilder('Memcache')->getMock();
+        $this->storage = new MemcacheSessionHandler(
+            $this->memcache,
+            ['prefix' => self::PREFIX, 'expiretime' => self::TTL]
+        );
+    }
+
+    protected function tearDown()
+    {
+        $this->memcache = null;
+        $this->storage = null;
+        parent::tearDown();
+    }
+
+    public function testOpenSession()
+    {
+        $this->assertTrue($this->storage->open('', ''));
+    }
+
+    public function testCloseSession()
+    {
+        $this->assertTrue($this->storage->close());
+    }
+
+    public function testReadSession()
+    {
+        $this->memcache
+            ->expects($this->once())
+            ->method('get')
+            ->with(self::PREFIX.'id')
+        ;
+
+        $this->assertEquals('', $this->storage->read('id'));
+    }
+
+    public function testWriteSession()
+    {
+        $this->memcache
+            ->expects($this->once())
+            ->method('set')
+            ->with(self::PREFIX.'id', 'data', 0, $this->equalTo(time() + self::TTL, 2))
+            ->will($this->returnValue(true))
+        ;
+
+        $this->assertTrue($this->storage->write('id', 'data'));
+    }
+
+    public function testDestroySession()
+    {
+        $this->memcache
+            ->expects($this->once())
+            ->method('delete')
+            ->with(self::PREFIX.'id')
+            ->will($this->returnValue(true))
+        ;
+
+        $this->assertTrue($this->storage->destroy('id'));
+    }
+
+    public function testGcSession()
+    {
+        $this->assertTrue($this->storage->gc(123));
+    }
+
+    /**
+     * @dataProvider getOptionFixtures
+     */
+    public function testSupportedOptions($options, $supported)
+    {
+        try {
+            new MemcacheSessionHandler($this->memcache, $options);
+            $this->assertTrue($supported);
+        } catch (\InvalidArgumentException $e) {
+            $this->assertFalse($supported);
+        }
+    }
+
+    public function getOptionFixtures()
+    {
+        return [
+            [['prefix' => 'session'], true],
+            [['expiretime' => 100], true],
+            [['prefix' => 'session', 'expiretime' => 200], true],
+            [['expiretime' => 100, 'foo' => 'bar'], false],
+        ];
+    }
+
+    public function testGetConnection()
+    {
+        $method = new \ReflectionMethod($this->storage, 'getMemcache');
+        $method->setAccessible(true);
+
+        $this->assertInstanceOf('\Memcache', $method->invoke($this->storage));
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php
new file mode 100755
index 0000000..8133373
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php
@@ -0,0 +1,145 @@
+<?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\HttpFoundation\Tests\Session\Storage\Handler;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler;
+
+/**
+ * @requires extension memcached
+ * @group time-sensitive
+ */
+class MemcachedSessionHandlerTest extends TestCase
+{
+    const PREFIX = 'prefix_';
+    const TTL = 1000;
+
+    /**
+     * @var MemcachedSessionHandler
+     */
+    protected $storage;
+
+    protected $memcached;
+
+    protected function setUp()
+    {
+        if (\defined('HHVM_VERSION')) {
+            $this->markTestSkipped('PHPUnit_MockObject cannot mock the Memcached class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289');
+        }
+
+        parent::setUp();
+
+        if (version_compare(phpversion('memcached'), '2.2.0', '>=') && version_compare(phpversion('memcached'), '3.0.0b1', '<')) {
+            $this->markTestSkipped('Tests can only be run with memcached extension 2.1.0 or lower, or 3.0.0b1 or higher');
+        }
+
+        $this->memcached = $this->getMockBuilder('Memcached')->getMock();
+        $this->storage = new MemcachedSessionHandler(
+            $this->memcached,
+            ['prefix' => self::PREFIX, 'expiretime' => self::TTL]
+        );
+    }
+
+    protected function tearDown()
+    {
+        $this->memcached = null;
+        $this->storage = null;
+        parent::tearDown();
+    }
+
+    public function testOpenSession()
+    {
+        $this->assertTrue($this->storage->open('', ''));
+    }
+
+    public function testCloseSession()
+    {
+        $this->memcached
+            ->expects($this->once())
+            ->method('quit')
+            ->will($this->returnValue(true))
+        ;
+
+        $this->assertTrue($this->storage->close());
+    }
+
+    public function testReadSession()
+    {
+        $this->memcached
+            ->expects($this->once())
+            ->method('get')
+            ->with(self::PREFIX.'id')
+        ;
+
+        $this->assertEquals('', $this->storage->read('id'));
+    }
+
+    public function testWriteSession()
+    {
+        $this->memcached
+            ->expects($this->once())
+            ->method('set')
+            ->with(self::PREFIX.'id', 'data', $this->equalTo(time() + self::TTL, 2))
+            ->will($this->returnValue(true))
+        ;
+
+        $this->assertTrue($this->storage->write('id', 'data'));
+    }
+
+    public function testDestroySession()
+    {
+        $this->memcached
+            ->expects($this->once())
+            ->method('delete')
+            ->with(self::PREFIX.'id')
+            ->will($this->returnValue(true))
+        ;
+
+        $this->assertTrue($this->storage->destroy('id'));
+    }
+
+    public function testGcSession()
+    {
+        $this->assertTrue($this->storage->gc(123));
+    }
+
+    /**
+     * @dataProvider getOptionFixtures
+     */
+    public function testSupportedOptions($options, $supported)
+    {
+        try {
+            new MemcachedSessionHandler($this->memcached, $options);
+            $this->assertTrue($supported);
+        } catch (\InvalidArgumentException $e) {
+            $this->assertFalse($supported);
+        }
+    }
+
+    public function getOptionFixtures()
+    {
+        return [
+            [['prefix' => 'session'], true],
+            [['expiretime' => 100], true],
+            [['prefix' => 'session', 'expiretime' => 200], true],
+            [['expiretime' => 100, 'foo' => 'bar'], false],
+        ];
+    }
+
+    public function testGetConnection()
+    {
+        $method = new \ReflectionMethod($this->storage, 'getMemcached');
+        $method->setAccessible(true);
+
+        $this->assertInstanceOf('\Memcached', $method->invoke($this->storage));
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php
new file mode 100755
index 0000000..6f2742c
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php
@@ -0,0 +1,333 @@
+<?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\HttpFoundation\Tests\Session\Storage\Handler;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler;
+
+/**
+ * @author Markus Bachmann <markus.bachmann@bachi.biz>
+ * @group time-sensitive
+ * @group legacy
+ */
+class MongoDbSessionHandlerTest extends TestCase
+{
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    private $mongo;
+    private $storage;
+    public $options;
+
+    protected function setUp()
+    {
+        parent::setUp();
+
+        if (\extension_loaded('mongodb')) {
+            if (!class_exists('MongoDB\Client')) {
+                $this->markTestSkipped('The mongodb/mongodb package is required.');
+            }
+        } elseif (!\extension_loaded('mongo')) {
+            $this->markTestSkipped('The Mongo or MongoDB extension is required.');
+        }
+
+        if (phpversion('mongodb')) {
+            $mongoClass = 'MongoDB\Client';
+        } else {
+            $mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient';
+        }
+
+        $this->mongo = $this->getMockBuilder($mongoClass)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->options = [
+            'id_field' => '_id',
+            'data_field' => 'data',
+            'time_field' => 'time',
+            'expiry_field' => 'expires_at',
+            'database' => 'sf2-test',
+            'collection' => 'session-test',
+        ];
+
+        $this->storage = new MongoDbSessionHandler($this->mongo, $this->options);
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testConstructorShouldThrowExceptionForInvalidMongo()
+    {
+        new MongoDbSessionHandler(new \stdClass(), $this->options);
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testConstructorShouldThrowExceptionForMissingOptions()
+    {
+        new MongoDbSessionHandler($this->mongo, []);
+    }
+
+    public function testOpenMethodAlwaysReturnTrue()
+    {
+        $this->assertTrue($this->storage->open('test', 'test'), 'The "open" method should always return true');
+    }
+
+    public function testCloseMethodAlwaysReturnTrue()
+    {
+        $this->assertTrue($this->storage->close(), 'The "close" method should always return true');
+    }
+
+    public function testRead()
+    {
+        $collection = $this->createMongoCollectionMock();
+
+        $this->mongo->expects($this->once())
+            ->method('selectCollection')
+            ->with($this->options['database'], $this->options['collection'])
+            ->will($this->returnValue($collection));
+
+        // defining the timeout before the actual method call
+        // allows to test for "greater than" values in the $criteria
+        $testTimeout = time() + 1;
+
+        $collection->expects($this->once())
+            ->method('findOne')
+            ->will($this->returnCallback(function ($criteria) use ($testTimeout) {
+                $this->assertArrayHasKey($this->options['id_field'], $criteria);
+                $this->assertEquals($criteria[$this->options['id_field']], 'foo');
+
+                $this->assertArrayHasKey($this->options['expiry_field'], $criteria);
+                $this->assertArrayHasKey('$gte', $criteria[$this->options['expiry_field']]);
+
+                if (phpversion('mongodb')) {
+                    $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$gte']);
+                    $this->assertGreaterThanOrEqual(round((string) $criteria[$this->options['expiry_field']]['$gte'] / 1000), $testTimeout);
+                } else {
+                    $this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$gte']);
+                    $this->assertGreaterThanOrEqual($criteria[$this->options['expiry_field']]['$gte']->sec, $testTimeout);
+                }
+
+                $fields = [
+                    $this->options['id_field'] => 'foo',
+                ];
+
+                if (phpversion('mongodb')) {
+                    $fields[$this->options['data_field']] = new \MongoDB\BSON\Binary('bar', \MongoDB\BSON\Binary::TYPE_OLD_BINARY);
+                    $fields[$this->options['id_field']] = new \MongoDB\BSON\UTCDateTime(time() * 1000);
+                } else {
+                    $fields[$this->options['data_field']] = new \MongoBinData('bar', \MongoBinData::BYTE_ARRAY);
+                    $fields[$this->options['id_field']] = new \MongoDate();
+                }
+
+                return $fields;
+            }));
+
+        $this->assertEquals('bar', $this->storage->read('foo'));
+    }
+
+    public function testWrite()
+    {
+        $collection = $this->createMongoCollectionMock();
+
+        $this->mongo->expects($this->once())
+            ->method('selectCollection')
+            ->with($this->options['database'], $this->options['collection'])
+            ->will($this->returnValue($collection));
+
+        $data = [];
+
+        $methodName = phpversion('mongodb') ? 'updateOne' : 'update';
+
+        $collection->expects($this->once())
+            ->method($methodName)
+            ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
+                $this->assertEquals([$this->options['id_field'] => 'foo'], $criteria);
+
+                if (phpversion('mongodb')) {
+                    $this->assertEquals(['upsert' => true], $options);
+                } else {
+                    $this->assertEquals(['upsert' => true, 'multiple' => false], $options);
+                }
+
+                $data = $updateData['$set'];
+            }));
+
+        $expectedExpiry = time() + (int) ini_get('session.gc_maxlifetime');
+        $this->assertTrue($this->storage->write('foo', 'bar'));
+
+        if (phpversion('mongodb')) {
+            $this->assertEquals('bar', $data[$this->options['data_field']]->getData());
+            $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['time_field']]);
+            $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['expiry_field']]);
+            $this->assertGreaterThanOrEqual($expectedExpiry, round((string) $data[$this->options['expiry_field']] / 1000));
+        } else {
+            $this->assertEquals('bar', $data[$this->options['data_field']]->bin);
+            $this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]);
+            $this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]);
+            $this->assertGreaterThanOrEqual($expectedExpiry, $data[$this->options['expiry_field']]->sec);
+        }
+    }
+
+    public function testWriteWhenUsingExpiresField()
+    {
+        $this->options = [
+            'id_field' => '_id',
+            'data_field' => 'data',
+            'time_field' => 'time',
+            'database' => 'sf2-test',
+            'collection' => 'session-test',
+            'expiry_field' => 'expiresAt',
+        ];
+
+        $this->storage = new MongoDbSessionHandler($this->mongo, $this->options);
+
+        $collection = $this->createMongoCollectionMock();
+
+        $this->mongo->expects($this->once())
+            ->method('selectCollection')
+            ->with($this->options['database'], $this->options['collection'])
+            ->will($this->returnValue($collection));
+
+        $data = [];
+
+        $methodName = phpversion('mongodb') ? 'updateOne' : 'update';
+
+        $collection->expects($this->once())
+            ->method($methodName)
+            ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
+                $this->assertEquals([$this->options['id_field'] => 'foo'], $criteria);
+
+                if (phpversion('mongodb')) {
+                    $this->assertEquals(['upsert' => true], $options);
+                } else {
+                    $this->assertEquals(['upsert' => true, 'multiple' => false], $options);
+                }
+
+                $data = $updateData['$set'];
+            }));
+
+        $this->assertTrue($this->storage->write('foo', 'bar'));
+
+        if (phpversion('mongodb')) {
+            $this->assertEquals('bar', $data[$this->options['data_field']]->getData());
+            $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['time_field']]);
+            $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['expiry_field']]);
+        } else {
+            $this->assertEquals('bar', $data[$this->options['data_field']]->bin);
+            $this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]);
+            $this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]);
+        }
+    }
+
+    public function testReplaceSessionData()
+    {
+        $collection = $this->createMongoCollectionMock();
+
+        $this->mongo->expects($this->once())
+            ->method('selectCollection')
+            ->with($this->options['database'], $this->options['collection'])
+            ->will($this->returnValue($collection));
+
+        $data = [];
+
+        $methodName = phpversion('mongodb') ? 'updateOne' : 'update';
+
+        $collection->expects($this->exactly(2))
+            ->method($methodName)
+            ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
+                $data = $updateData;
+            }));
+
+        $this->storage->write('foo', 'bar');
+        $this->storage->write('foo', 'foobar');
+
+        if (phpversion('mongodb')) {
+            $this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->getData());
+        } else {
+            $this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->bin);
+        }
+    }
+
+    public function testDestroy()
+    {
+        $collection = $this->createMongoCollectionMock();
+
+        $this->mongo->expects($this->once())
+            ->method('selectCollection')
+            ->with($this->options['database'], $this->options['collection'])
+            ->will($this->returnValue($collection));
+
+        $methodName = phpversion('mongodb') ? 'deleteOne' : 'remove';
+
+        $collection->expects($this->once())
+            ->method($methodName)
+            ->with([$this->options['id_field'] => 'foo']);
+
+        $this->assertTrue($this->storage->destroy('foo'));
+    }
+
+    public function testGc()
+    {
+        $collection = $this->createMongoCollectionMock();
+
+        $this->mongo->expects($this->once())
+            ->method('selectCollection')
+            ->with($this->options['database'], $this->options['collection'])
+            ->will($this->returnValue($collection));
+
+        $methodName = phpversion('mongodb') ? 'deleteMany' : 'remove';
+
+        $collection->expects($this->once())
+            ->method($methodName)
+            ->will($this->returnCallback(function ($criteria) {
+                if (phpversion('mongodb')) {
+                    $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$lt']);
+                    $this->assertGreaterThanOrEqual(time() - 1, round((string) $criteria[$this->options['expiry_field']]['$lt'] / 1000));
+                } else {
+                    $this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$lt']);
+                    $this->assertGreaterThanOrEqual(time() - 1, $criteria[$this->options['expiry_field']]['$lt']->sec);
+                }
+            }));
+
+        $this->assertTrue($this->storage->gc(1));
+    }
+
+    public function testGetConnection()
+    {
+        $method = new \ReflectionMethod($this->storage, 'getMongo');
+        $method->setAccessible(true);
+
+        if (phpversion('mongodb')) {
+            $mongoClass = 'MongoDB\Client';
+        } else {
+            $mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient';
+        }
+
+        $this->assertInstanceOf($mongoClass, $method->invoke($this->storage));
+    }
+
+    private function createMongoCollectionMock()
+    {
+        $collectionClass = 'MongoCollection';
+        if (phpversion('mongodb')) {
+            $collectionClass = 'MongoDB\Collection';
+        }
+
+        $collection = $this->getMockBuilder($collectionClass)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        return $collection;
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php
new file mode 100755
index 0000000..dc827d8
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php
@@ -0,0 +1,77 @@
+<?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\HttpFoundation\Tests\Session\Storage\Handler;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler;
+use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
+
+/**
+ * Test class for NativeFileSessionHandler.
+ *
+ * @author Drak <drak@zikula.org>
+ *
+ * @runTestsInSeparateProcesses
+ * @preserveGlobalState disabled
+ */
+class NativeFileSessionHandlerTest extends TestCase
+{
+    public function testConstruct()
+    {
+        $storage = new NativeSessionStorage(['name' => 'TESTING'], new NativeFileSessionHandler(sys_get_temp_dir()));
+
+        $this->assertEquals('files', $storage->getSaveHandler()->getSaveHandlerName());
+        $this->assertEquals('user', ini_get('session.save_handler'));
+
+        $this->assertEquals(sys_get_temp_dir(), ini_get('session.save_path'));
+        $this->assertEquals('TESTING', ini_get('session.name'));
+    }
+
+    /**
+     * @dataProvider savePathDataProvider
+     */
+    public function testConstructSavePath($savePath, $expectedSavePath, $path)
+    {
+        $handler = new NativeFileSessionHandler($savePath);
+        $this->assertEquals($expectedSavePath, ini_get('session.save_path'));
+        $this->assertTrue(is_dir(realpath($path)));
+
+        rmdir($path);
+    }
+
+    public function savePathDataProvider()
+    {
+        $base = sys_get_temp_dir();
+
+        return [
+            ["$base/foo", "$base/foo", "$base/foo"],
+            ["5;$base/foo", "5;$base/foo", "$base/foo"],
+            ["5;0600;$base/foo", "5;0600;$base/foo", "$base/foo"],
+        ];
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testConstructException()
+    {
+        $handler = new NativeFileSessionHandler('something;invalid;with;too-many-args');
+    }
+
+    public function testConstructDefault()
+    {
+        $path = ini_get('session.save_path');
+        $storage = new NativeSessionStorage(['name' => 'TESTING'], new NativeFileSessionHandler());
+
+        $this->assertEquals($path, ini_get('session.save_path'));
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeSessionHandlerTest.php
new file mode 100755
index 0000000..4a9fb60
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeSessionHandlerTest.php
@@ -0,0 +1,38 @@
+<?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\HttpFoundation\Tests\Session\Storage\Handler;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler;
+
+/**
+ * Test class for NativeSessionHandler.
+ *
+ * @author Drak <drak@zikula.org>
+ *
+ * @runTestsInSeparateProcesses
+ * @preserveGlobalState disabled
+ * @group legacy
+ */
+class NativeSessionHandlerTest extends TestCase
+{
+    /**
+     * @expectedDeprecation The Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler class is deprecated since Symfony 3.4 and will be removed in 4.0. Use the \SessionHandler class instead.
+     */
+    public function testConstruct()
+    {
+        $handler = new NativeSessionHandler();
+
+        $this->assertInstanceOf('SessionHandler', $handler);
+        $this->assertTrue($handler instanceof NativeSessionHandler);
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php
new file mode 100755
index 0000000..0d246e1
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php
@@ -0,0 +1,59 @@
+<?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\HttpFoundation\Tests\Session\Storage\Handler;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Session;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
+use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
+
+/**
+ * Test class for NullSessionHandler.
+ *
+ * @author Drak <drak@zikula.org>
+ *
+ * @runTestsInSeparateProcesses
+ * @preserveGlobalState disabled
+ */
+class NullSessionHandlerTest extends TestCase
+{
+    public function testSaveHandlers()
+    {
+        $storage = $this->getStorage();
+        $this->assertEquals('user', ini_get('session.save_handler'));
+    }
+
+    public function testSession()
+    {
+        session_id('nullsessionstorage');
+        $storage = $this->getStorage();
+        $session = new Session($storage);
+        $this->assertNull($session->get('something'));
+        $session->set('something', 'unique');
+        $this->assertEquals('unique', $session->get('something'));
+    }
+
+    public function testNothingIsPersisted()
+    {
+        session_id('nullsessionstorage');
+        $storage = $this->getStorage();
+        $session = new Session($storage);
+        $session->start();
+        $this->assertEquals('nullsessionstorage', $session->getId());
+        $this->assertNull($session->get('something'));
+    }
+
+    public function getStorage()
+    {
+        return new NativeSessionStorage([], new NullSessionHandler());
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php
new file mode 100755
index 0000000..9010784
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php
@@ -0,0 +1,411 @@
+<?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\HttpFoundation\Tests\Session\Storage\Handler;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;
+
+/**
+ * @requires extension pdo_sqlite
+ * @group time-sensitive
+ */
+class PdoSessionHandlerTest extends TestCase
+{
+    private $dbFile;
+
+    protected function tearDown()
+    {
+        // make sure the temporary database file is deleted when it has been created (even when a test fails)
+        if ($this->dbFile) {
+            @unlink($this->dbFile);
+        }
+        parent::tearDown();
+    }
+
+    protected function getPersistentSqliteDsn()
+    {
+        $this->dbFile = tempnam(sys_get_temp_dir(), 'sf2_sqlite_sessions');
+
+        return 'sqlite:'.$this->dbFile;
+    }
+
+    protected function getMemorySqlitePdo()
+    {
+        $pdo = new \PDO('sqlite::memory:');
+        $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
+        $storage = new PdoSessionHandler($pdo);
+        $storage->createTable();
+
+        return $pdo;
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testWrongPdoErrMode()
+    {
+        $pdo = $this->getMemorySqlitePdo();
+        $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT);
+
+        $storage = new PdoSessionHandler($pdo);
+    }
+
+    /**
+     * @expectedException \RuntimeException
+     */
+    public function testInexistentTable()
+    {
+        $storage = new PdoSessionHandler($this->getMemorySqlitePdo(), ['db_table' => 'inexistent_table']);
+        $storage->open('', 'sid');
+        $storage->read('id');
+        $storage->write('id', 'data');
+        $storage->close();
+    }
+
+    /**
+     * @expectedException \RuntimeException
+     */
+    public function testCreateTableTwice()
+    {
+        $storage = new PdoSessionHandler($this->getMemorySqlitePdo());
+        $storage->createTable();
+    }
+
+    public function testWithLazyDsnConnection()
+    {
+        $dsn = $this->getPersistentSqliteDsn();
+
+        $storage = new PdoSessionHandler($dsn);
+        $storage->createTable();
+        $storage->open('', 'sid');
+        $data = $storage->read('id');
+        $storage->write('id', 'data');
+        $storage->close();
+        $this->assertSame('', $data, 'New session returns empty string data');
+
+        $storage->open('', 'sid');
+        $data = $storage->read('id');
+        $storage->close();
+        $this->assertSame('data', $data, 'Written value can be read back correctly');
+    }
+
+    public function testWithLazySavePathConnection()
+    {
+        $dsn = $this->getPersistentSqliteDsn();
+
+        // Open is called with what ini_set('session.save_path', $dsn) would mean
+        $storage = new PdoSessionHandler(null);
+        $storage->open($dsn, 'sid');
+        $storage->createTable();
+        $data = $storage->read('id');
+        $storage->write('id', 'data');
+        $storage->close();
+        $this->assertSame('', $data, 'New session returns empty string data');
+
+        $storage->open($dsn, 'sid');
+        $data = $storage->read('id');
+        $storage->close();
+        $this->assertSame('data', $data, 'Written value can be read back correctly');
+    }
+
+    public function testReadWriteReadWithNullByte()
+    {
+        $sessionData = 'da'."\0".'ta';
+
+        $storage = new PdoSessionHandler($this->getMemorySqlitePdo());
+        $storage->open('', 'sid');
+        $readData = $storage->read('id');
+        $storage->write('id', $sessionData);
+        $storage->close();
+        $this->assertSame('', $readData, 'New session returns empty string data');
+
+        $storage->open('', 'sid');
+        $readData = $storage->read('id');
+        $storage->close();
+        $this->assertSame($sessionData, $readData, 'Written value can be read back correctly');
+    }
+
+    public function testReadConvertsStreamToString()
+    {
+        if (\defined('HHVM_VERSION')) {
+            $this->markTestSkipped('PHPUnit_MockObject cannot mock the PDOStatement class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289');
+        }
+
+        $pdo = new MockPdo('pgsql');
+        $pdo->prepareResult = $this->getMockBuilder('PDOStatement')->getMock();
+
+        $content = 'foobar';
+        $stream = $this->createStream($content);
+
+        $pdo->prepareResult->expects($this->once())->method('fetchAll')
+            ->will($this->returnValue([[$stream, 42, time()]]));
+
+        $storage = new PdoSessionHandler($pdo);
+        $result = $storage->read('foo');
+
+        $this->assertSame($content, $result);
+    }
+
+    public function testReadLockedConvertsStreamToString()
+    {
+        if (\defined('HHVM_VERSION')) {
+            $this->markTestSkipped('PHPUnit_MockObject cannot mock the PDOStatement class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289');
+        }
+        if (filter_var(ini_get('session.use_strict_mode'), FILTER_VALIDATE_BOOLEAN)) {
+            $this->markTestSkipped('Strict mode needs no locking for new sessions.');
+        }
+
+        $pdo = new MockPdo('pgsql');
+        $selectStmt = $this->getMockBuilder('PDOStatement')->getMock();
+        $insertStmt = $this->getMockBuilder('PDOStatement')->getMock();
+
+        $pdo->prepareResult = function ($statement) use ($selectStmt, $insertStmt) {
+            return 0 === strpos($statement, 'INSERT') ? $insertStmt : $selectStmt;
+        };
+
+        $content = 'foobar';
+        $stream = $this->createStream($content);
+        $exception = null;
+
+        $selectStmt->expects($this->atLeast(2))->method('fetchAll')
+            ->will($this->returnCallback(function () use (&$exception, $stream) {
+                return $exception ? [[$stream, 42, time()]] : [];
+            }));
+
+        $insertStmt->expects($this->once())->method('execute')
+            ->will($this->returnCallback(function () use (&$exception) {
+                throw $exception = new \PDOException('', '23');
+            }));
+
+        $storage = new PdoSessionHandler($pdo);
+        $result = $storage->read('foo');
+
+        $this->assertSame($content, $result);
+    }
+
+    public function testReadingRequiresExactlySameId()
+    {
+        $storage = new PdoSessionHandler($this->getMemorySqlitePdo());
+        $storage->open('', 'sid');
+        $storage->write('id', 'data');
+        $storage->write('test', 'data');
+        $storage->write('space ', 'data');
+        $storage->close();
+
+        $storage->open('', 'sid');
+        $readDataCaseSensitive = $storage->read('ID');
+        $readDataNoCharFolding = $storage->read('tést');
+        $readDataKeepSpace = $storage->read('space ');
+        $readDataExtraSpace = $storage->read('space  ');
+        $storage->close();
+
+        $this->assertSame('', $readDataCaseSensitive, 'Retrieval by ID should be case-sensitive (collation setting)');
+        $this->assertSame('', $readDataNoCharFolding, 'Retrieval by ID should not do character folding (collation setting)');
+        $this->assertSame('data', $readDataKeepSpace, 'Retrieval by ID requires spaces as-is');
+        $this->assertSame('', $readDataExtraSpace, 'Retrieval by ID requires spaces as-is');
+    }
+
+    /**
+     * Simulates session_regenerate_id(true) which will require an INSERT or UPDATE (replace).
+     */
+    public function testWriteDifferentSessionIdThanRead()
+    {
+        $storage = new PdoSessionHandler($this->getMemorySqlitePdo());
+        $storage->open('', 'sid');
+        $storage->read('id');
+        $storage->destroy('id');
+        $storage->write('new_id', 'data_of_new_session_id');
+        $storage->close();
+
+        $storage->open('', 'sid');
+        $data = $storage->read('new_id');
+        $storage->close();
+
+        $this->assertSame('data_of_new_session_id', $data, 'Data of regenerated session id is available');
+    }
+
+    public function testWrongUsageStillWorks()
+    {
+        // wrong method sequence that should no happen, but still works
+        $storage = new PdoSessionHandler($this->getMemorySqlitePdo());
+        $storage->write('id', 'data');
+        $storage->write('other_id', 'other_data');
+        $storage->destroy('inexistent');
+        $storage->open('', 'sid');
+        $data = $storage->read('id');
+        $otherData = $storage->read('other_id');
+        $storage->close();
+
+        $this->assertSame('data', $data);
+        $this->assertSame('other_data', $otherData);
+    }
+
+    public function testSessionDestroy()
+    {
+        $pdo = $this->getMemorySqlitePdo();
+        $storage = new PdoSessionHandler($pdo);
+
+        $storage->open('', 'sid');
+        $storage->read('id');
+        $storage->write('id', 'data');
+        $storage->close();
+        $this->assertEquals(1, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn());
+
+        $storage->open('', 'sid');
+        $storage->read('id');
+        $storage->destroy('id');
+        $storage->close();
+        $this->assertEquals(0, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn());
+
+        $storage->open('', 'sid');
+        $data = $storage->read('id');
+        $storage->close();
+        $this->assertSame('', $data, 'Destroyed session returns empty string');
+    }
+
+    /**
+     * @runInSeparateProcess
+     */
+    public function testSessionGC()
+    {
+        $previousLifeTime = ini_set('session.gc_maxlifetime', 1000);
+        $pdo = $this->getMemorySqlitePdo();
+        $storage = new PdoSessionHandler($pdo);
+
+        $storage->open('', 'sid');
+        $storage->read('id');
+        $storage->write('id', 'data');
+        $storage->close();
+
+        $storage->open('', 'sid');
+        $storage->read('gc_id');
+        ini_set('session.gc_maxlifetime', -1); // test that you can set lifetime of a session after it has been read
+        $storage->write('gc_id', 'data');
+        $storage->close();
+        $this->assertEquals(2, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn(), 'No session pruned because gc not called');
+
+        $storage->open('', 'sid');
+        $data = $storage->read('gc_id');
+        $storage->gc(-1);
+        $storage->close();
+
+        ini_set('session.gc_maxlifetime', $previousLifeTime);
+
+        $this->assertSame('', $data, 'Session already considered garbage, so not returning data even if it is not pruned yet');
+        $this->assertEquals(1, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn(), 'Expired session is pruned');
+    }
+
+    public function testGetConnection()
+    {
+        $storage = new PdoSessionHandler($this->getMemorySqlitePdo());
+
+        $method = new \ReflectionMethod($storage, 'getConnection');
+        $method->setAccessible(true);
+
+        $this->assertInstanceOf('\PDO', $method->invoke($storage));
+    }
+
+    public function testGetConnectionConnectsIfNeeded()
+    {
+        $storage = new PdoSessionHandler('sqlite::memory:');
+
+        $method = new \ReflectionMethod($storage, 'getConnection');
+        $method->setAccessible(true);
+
+        $this->assertInstanceOf('\PDO', $method->invoke($storage));
+    }
+
+    /**
+     * @dataProvider provideUrlDsnPairs
+     */
+    public function testUrlDsn($url, $expectedDsn, $expectedUser = null, $expectedPassword = null)
+    {
+        $storage = new PdoSessionHandler($url);
+
+        $this->assertAttributeEquals($expectedDsn, 'dsn', $storage);
+
+        if (null !== $expectedUser) {
+            $this->assertAttributeEquals($expectedUser, 'username', $storage);
+        }
+
+        if (null !== $expectedPassword) {
+            $this->assertAttributeEquals($expectedPassword, 'password', $storage);
+        }
+    }
+
+    public function provideUrlDsnPairs()
+    {
+        yield ['mysql://localhost/test', 'mysql:host=localhost;dbname=test;'];
+        yield ['mysql://localhost:56/test', 'mysql:host=localhost;port=56;dbname=test;'];
+        yield ['mysql2://root:pwd@localhost/test', 'mysql:host=localhost;dbname=test;', 'root', 'pwd'];
+        yield ['postgres://localhost/test', 'pgsql:host=localhost;dbname=test;'];
+        yield ['postgresql://localhost:5634/test', 'pgsql:host=localhost;port=5634;dbname=test;'];
+        yield ['postgres://root:pwd@localhost/test', 'pgsql:host=localhost;dbname=test;', 'root', 'pwd'];
+        yield 'sqlite relative path' => ['sqlite://localhost/tmp/test', 'sqlite:tmp/test'];
+        yield 'sqlite absolute path' => ['sqlite://localhost//tmp/test', 'sqlite:/tmp/test'];
+        yield 'sqlite relative path without host' => ['sqlite:///tmp/test', 'sqlite:tmp/test'];
+        yield 'sqlite absolute path without host' => ['sqlite3:////tmp/test', 'sqlite:/tmp/test'];
+        yield ['sqlite://localhost/:memory:', 'sqlite::memory:'];
+        yield ['mssql://localhost/test', 'sqlsrv:server=localhost;Database=test'];
+        yield ['mssql://localhost:56/test', 'sqlsrv:server=localhost,56;Database=test'];
+    }
+
+    private function createStream($content)
+    {
+        $stream = tmpfile();
+        fwrite($stream, $content);
+        fseek($stream, 0);
+
+        return $stream;
+    }
+}
+
+class MockPdo extends \PDO
+{
+    public $prepareResult;
+    private $driverName;
+    private $errorMode;
+
+    public function __construct($driverName = null, $errorMode = null)
+    {
+        $this->driverName = $driverName;
+        $this->errorMode = null !== $errorMode ?: \PDO::ERRMODE_EXCEPTION;
+    }
+
+    public function getAttribute($attribute)
+    {
+        if (\PDO::ATTR_ERRMODE === $attribute) {
+            return $this->errorMode;
+        }
+
+        if (\PDO::ATTR_DRIVER_NAME === $attribute) {
+            return $this->driverName;
+        }
+
+        return parent::getAttribute($attribute);
+    }
+
+    public function prepare($statement, $driverOptions = [])
+    {
+        return \is_callable($this->prepareResult)
+            ? \call_user_func($this->prepareResult, $statement, $driverOptions)
+            : $this->prepareResult;
+    }
+
+    public function beginTransaction()
+    {
+    }
+
+    public function rollBack()
+    {
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/StrictSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/StrictSessionHandlerTest.php
new file mode 100755
index 0000000..6a0d168
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/StrictSessionHandlerTest.php
@@ -0,0 +1,189 @@
+<?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\HttpFoundation\Tests\Session\Storage\Handler;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\AbstractSessionHandler;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler;
+
+class StrictSessionHandlerTest extends TestCase
+{
+    public function testOpen()
+    {
+        $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+        $handler->expects($this->once())->method('open')
+            ->with('path', 'name')->willReturn(true);
+        $proxy = new StrictSessionHandler($handler);
+
+        $this->assertInstanceOf('SessionUpdateTimestampHandlerInterface', $proxy);
+        $this->assertInstanceOf(AbstractSessionHandler::class, $proxy);
+        $this->assertTrue($proxy->open('path', 'name'));
+    }
+
+    public function testCloseSession()
+    {
+        $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+        $handler->expects($this->once())->method('close')
+            ->willReturn(true);
+        $proxy = new StrictSessionHandler($handler);
+
+        $this->assertTrue($proxy->close());
+    }
+
+    public function testValidateIdOK()
+    {
+        $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+        $handler->expects($this->once())->method('read')
+            ->with('id')->willReturn('data');
+        $proxy = new StrictSessionHandler($handler);
+
+        $this->assertTrue($proxy->validateId('id'));
+    }
+
+    public function testValidateIdKO()
+    {
+        $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+        $handler->expects($this->once())->method('read')
+            ->with('id')->willReturn('');
+        $proxy = new StrictSessionHandler($handler);
+
+        $this->assertFalse($proxy->validateId('id'));
+    }
+
+    public function testRead()
+    {
+        $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+        $handler->expects($this->once())->method('read')
+            ->with('id')->willReturn('data');
+        $proxy = new StrictSessionHandler($handler);
+
+        $this->assertSame('data', $proxy->read('id'));
+    }
+
+    public function testReadWithValidateIdOK()
+    {
+        $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+        $handler->expects($this->once())->method('read')
+            ->with('id')->willReturn('data');
+        $proxy = new StrictSessionHandler($handler);
+
+        $this->assertTrue($proxy->validateId('id'));
+        $this->assertSame('data', $proxy->read('id'));
+    }
+
+    public function testReadWithValidateIdMismatch()
+    {
+        $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+        $handler->expects($this->exactly(2))->method('read')
+            ->withConsecutive(['id1'], ['id2'])
+            ->will($this->onConsecutiveCalls('data1', 'data2'));
+        $proxy = new StrictSessionHandler($handler);
+
+        $this->assertTrue($proxy->validateId('id1'));
+        $this->assertSame('data2', $proxy->read('id2'));
+    }
+
+    public function testUpdateTimestamp()
+    {
+        $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+        $handler->expects($this->once())->method('write')
+            ->with('id', 'data')->willReturn(true);
+        $proxy = new StrictSessionHandler($handler);
+
+        $this->assertTrue($proxy->updateTimestamp('id', 'data'));
+    }
+
+    public function testWrite()
+    {
+        $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+        $handler->expects($this->once())->method('write')
+            ->with('id', 'data')->willReturn(true);
+        $proxy = new StrictSessionHandler($handler);
+
+        $this->assertTrue($proxy->write('id', 'data'));
+    }
+
+    public function testWriteEmptyNewSession()
+    {
+        $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+        $handler->expects($this->once())->method('read')
+            ->with('id')->willReturn('');
+        $handler->expects($this->never())->method('write');
+        $handler->expects($this->once())->method('destroy')->willReturn(true);
+        $proxy = new StrictSessionHandler($handler);
+
+        $this->assertFalse($proxy->validateId('id'));
+        $this->assertSame('', $proxy->read('id'));
+        $this->assertTrue($proxy->write('id', ''));
+    }
+
+    public function testWriteEmptyExistingSession()
+    {
+        $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+        $handler->expects($this->once())->method('read')
+            ->with('id')->willReturn('data');
+        $handler->expects($this->never())->method('write');
+        $handler->expects($this->once())->method('destroy')->willReturn(true);
+        $proxy = new StrictSessionHandler($handler);
+
+        $this->assertSame('data', $proxy->read('id'));
+        $this->assertTrue($proxy->write('id', ''));
+    }
+
+    public function testDestroy()
+    {
+        $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+        $handler->expects($this->once())->method('destroy')
+            ->with('id')->willReturn(true);
+        $proxy = new StrictSessionHandler($handler);
+
+        $this->assertTrue($proxy->destroy('id'));
+    }
+
+    public function testDestroyNewSession()
+    {
+        $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+        $handler->expects($this->once())->method('read')
+            ->with('id')->willReturn('');
+        $handler->expects($this->once())->method('destroy')->willReturn(true);
+        $proxy = new StrictSessionHandler($handler);
+
+        $this->assertSame('', $proxy->read('id'));
+        $this->assertTrue($proxy->destroy('id'));
+    }
+
+    public function testDestroyNonEmptyNewSession()
+    {
+        $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+        $handler->expects($this->once())->method('read')
+            ->with('id')->willReturn('');
+        $handler->expects($this->once())->method('write')
+            ->with('id', 'data')->willReturn(true);
+        $handler->expects($this->once())->method('destroy')
+            ->with('id')->willReturn(true);
+        $proxy = new StrictSessionHandler($handler);
+
+        $this->assertSame('', $proxy->read('id'));
+        $this->assertTrue($proxy->write('id', 'data'));
+        $this->assertTrue($proxy->destroy('id'));
+    }
+
+    public function testGc()
+    {
+        $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+        $handler->expects($this->once())->method('gc')
+            ->with(123)->willReturn(true);
+        $proxy = new StrictSessionHandler($handler);
+
+        $this->assertTrue($proxy->gc(123));
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/WriteCheckSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/WriteCheckSessionHandlerTest.php
new file mode 100755
index 0000000..898a7d1
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/WriteCheckSessionHandlerTest.php
@@ -0,0 +1,97 @@
+<?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\HttpFoundation\Tests\Session\Storage\Handler;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler;
+
+/**
+ * @author Adrien Brault <adrien.brault@gmail.com>
+ *
+ * @group legacy
+ */
+class WriteCheckSessionHandlerTest extends TestCase
+{
+    public function test()
+    {
+        $wrappedSessionHandlerMock = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+        $writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock);
+
+        $wrappedSessionHandlerMock
+            ->expects($this->once())
+            ->method('close')
+            ->with()
+            ->will($this->returnValue(true))
+        ;
+
+        $this->assertTrue($writeCheckSessionHandler->close());
+    }
+
+    public function testWrite()
+    {
+        $wrappedSessionHandlerMock = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+        $writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock);
+
+        $wrappedSessionHandlerMock
+            ->expects($this->once())
+            ->method('write')
+            ->with('foo', 'bar')
+            ->will($this->returnValue(true))
+        ;
+
+        $this->assertTrue($writeCheckSessionHandler->write('foo', 'bar'));
+    }
+
+    public function testSkippedWrite()
+    {
+        $wrappedSessionHandlerMock = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+        $writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock);
+
+        $wrappedSessionHandlerMock
+            ->expects($this->once())
+            ->method('read')
+            ->with('foo')
+            ->will($this->returnValue('bar'))
+        ;
+
+        $wrappedSessionHandlerMock
+            ->expects($this->never())
+            ->method('write')
+        ;
+
+        $this->assertEquals('bar', $writeCheckSessionHandler->read('foo'));
+        $this->assertTrue($writeCheckSessionHandler->write('foo', 'bar'));
+    }
+
+    public function testNonSkippedWrite()
+    {
+        $wrappedSessionHandlerMock = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+        $writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock);
+
+        $wrappedSessionHandlerMock
+            ->expects($this->once())
+            ->method('read')
+            ->with('foo')
+            ->will($this->returnValue('bar'))
+        ;
+
+        $wrappedSessionHandlerMock
+            ->expects($this->once())
+            ->method('write')
+            ->with('foo', 'baZZZ')
+            ->will($this->returnValue(true))
+        ;
+
+        $this->assertEquals('bar', $writeCheckSessionHandler->read('foo'));
+        $this->assertTrue($writeCheckSessionHandler->write('foo', 'baZZZ'));
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/MetadataBagTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/MetadataBagTest.php
new file mode 100755
index 0000000..2c4758b
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/MetadataBagTest.php
@@ -0,0 +1,139 @@
+<?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\HttpFoundation\Tests\Session\Storage;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag;
+
+/**
+ * Test class for MetadataBag.
+ *
+ * @group time-sensitive
+ */
+class MetadataBagTest extends TestCase
+{
+    /**
+     * @var MetadataBag
+     */
+    protected $bag;
+
+    protected $array = [];
+
+    protected function setUp()
+    {
+        parent::setUp();
+        $this->bag = new MetadataBag();
+        $this->array = [MetadataBag::CREATED => 1234567, MetadataBag::UPDATED => 12345678, MetadataBag::LIFETIME => 0];
+        $this->bag->initialize($this->array);
+    }
+
+    protected function tearDown()
+    {
+        $this->array = [];
+        $this->bag = null;
+        parent::tearDown();
+    }
+
+    public function testInitialize()
+    {
+        $sessionMetadata = [];
+
+        $bag1 = new MetadataBag();
+        $bag1->initialize($sessionMetadata);
+        $this->assertGreaterThanOrEqual(time(), $bag1->getCreated());
+        $this->assertEquals($bag1->getCreated(), $bag1->getLastUsed());
+
+        sleep(1);
+        $bag2 = new MetadataBag();
+        $bag2->initialize($sessionMetadata);
+        $this->assertEquals($bag1->getCreated(), $bag2->getCreated());
+        $this->assertEquals($bag1->getLastUsed(), $bag2->getLastUsed());
+        $this->assertEquals($bag2->getCreated(), $bag2->getLastUsed());
+
+        sleep(1);
+        $bag3 = new MetadataBag();
+        $bag3->initialize($sessionMetadata);
+        $this->assertEquals($bag1->getCreated(), $bag3->getCreated());
+        $this->assertGreaterThan($bag2->getLastUsed(), $bag3->getLastUsed());
+        $this->assertNotEquals($bag3->getCreated(), $bag3->getLastUsed());
+    }
+
+    public function testGetSetName()
+    {
+        $this->assertEquals('__metadata', $this->bag->getName());
+        $this->bag->setName('foo');
+        $this->assertEquals('foo', $this->bag->getName());
+    }
+
+    public function testGetStorageKey()
+    {
+        $this->assertEquals('_sf2_meta', $this->bag->getStorageKey());
+    }
+
+    public function testGetLifetime()
+    {
+        $bag = new MetadataBag();
+        $array = [MetadataBag::CREATED => 1234567, MetadataBag::UPDATED => 12345678, MetadataBag::LIFETIME => 1000];
+        $bag->initialize($array);
+        $this->assertEquals(1000, $bag->getLifetime());
+    }
+
+    public function testGetCreated()
+    {
+        $this->assertEquals(1234567, $this->bag->getCreated());
+    }
+
+    public function testGetLastUsed()
+    {
+        $this->assertLessThanOrEqual(time(), $this->bag->getLastUsed());
+    }
+
+    public function testClear()
+    {
+        $this->bag->clear();
+
+        // the clear method has no side effects, we just want to ensure it doesn't trigger any exceptions
+        $this->addToAssertionCount(1);
+    }
+
+    public function testSkipLastUsedUpdate()
+    {
+        $bag = new MetadataBag('', 30);
+        $timeStamp = time();
+
+        $created = $timeStamp - 15;
+        $sessionMetadata = [
+            MetadataBag::CREATED => $created,
+            MetadataBag::UPDATED => $created,
+            MetadataBag::LIFETIME => 1000,
+        ];
+        $bag->initialize($sessionMetadata);
+
+        $this->assertEquals($created, $sessionMetadata[MetadataBag::UPDATED]);
+    }
+
+    public function testDoesNotSkipLastUsedUpdate()
+    {
+        $bag = new MetadataBag('', 30);
+        $timeStamp = time();
+
+        $created = $timeStamp - 45;
+        $sessionMetadata = [
+            MetadataBag::CREATED => $created,
+            MetadataBag::UPDATED => $created,
+            MetadataBag::LIFETIME => 1000,
+        ];
+        $bag->initialize($sessionMetadata);
+
+        $this->assertEquals($timeStamp, $sessionMetadata[MetadataBag::UPDATED]);
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/MockArraySessionStorageTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/MockArraySessionStorageTest.php
new file mode 100755
index 0000000..2e3024e
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/MockArraySessionStorageTest.php
@@ -0,0 +1,131 @@
+<?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\HttpFoundation\Tests\Session\Storage;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
+use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
+
+/**
+ * Test class for MockArraySessionStorage.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class MockArraySessionStorageTest extends TestCase
+{
+    /**
+     * @var MockArraySessionStorage
+     */
+    private $storage;
+
+    /**
+     * @var AttributeBag
+     */
+    private $attributes;
+
+    /**
+     * @var FlashBag
+     */
+    private $flashes;
+
+    private $data;
+
+    protected function setUp()
+    {
+        $this->attributes = new AttributeBag();
+        $this->flashes = new FlashBag();
+
+        $this->data = [
+            $this->attributes->getStorageKey() => ['foo' => 'bar'],
+            $this->flashes->getStorageKey() => ['notice' => 'hello'],
+        ];
+
+        $this->storage = new MockArraySessionStorage();
+        $this->storage->registerBag($this->flashes);
+        $this->storage->registerBag($this->attributes);
+        $this->storage->setSessionData($this->data);
+    }
+
+    protected function tearDown()
+    {
+        $this->data = null;
+        $this->flashes = null;
+        $this->attributes = null;
+        $this->storage = null;
+    }
+
+    public function testStart()
+    {
+        $this->assertEquals('', $this->storage->getId());
+        $this->storage->start();
+        $id = $this->storage->getId();
+        $this->assertNotEquals('', $id);
+        $this->storage->start();
+        $this->assertEquals($id, $this->storage->getId());
+    }
+
+    public function testRegenerate()
+    {
+        $this->storage->start();
+        $id = $this->storage->getId();
+        $this->storage->regenerate();
+        $this->assertNotEquals($id, $this->storage->getId());
+        $this->assertEquals(['foo' => 'bar'], $this->storage->getBag('attributes')->all());
+        $this->assertEquals(['notice' => 'hello'], $this->storage->getBag('flashes')->peekAll());
+
+        $id = $this->storage->getId();
+        $this->storage->regenerate(true);
+        $this->assertNotEquals($id, $this->storage->getId());
+        $this->assertEquals(['foo' => 'bar'], $this->storage->getBag('attributes')->all());
+        $this->assertEquals(['notice' => 'hello'], $this->storage->getBag('flashes')->peekAll());
+    }
+
+    public function testGetId()
+    {
+        $this->assertEquals('', $this->storage->getId());
+        $this->storage->start();
+        $this->assertNotEquals('', $this->storage->getId());
+    }
+
+    public function testClearClearsBags()
+    {
+        $this->storage->clear();
+
+        $this->assertSame([], $this->storage->getBag('attributes')->all());
+        $this->assertSame([], $this->storage->getBag('flashes')->peekAll());
+    }
+
+    public function testClearStartsSession()
+    {
+        $this->storage->clear();
+
+        $this->assertTrue($this->storage->isStarted());
+    }
+
+    public function testClearWithNoBagsStartsSession()
+    {
+        $storage = new MockArraySessionStorage();
+
+        $storage->clear();
+
+        $this->assertTrue($storage->isStarted());
+    }
+
+    /**
+     * @expectedException \RuntimeException
+     */
+    public function testUnstartedSave()
+    {
+        $this->storage->save();
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/MockFileSessionStorageTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/MockFileSessionStorageTest.php
new file mode 100755
index 0000000..9e2692d
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/MockFileSessionStorageTest.php
@@ -0,0 +1,127 @@
+<?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\HttpFoundation\Tests\Session\Storage;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
+use Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage;
+
+/**
+ * Test class for MockFileSessionStorage.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class MockFileSessionStorageTest extends TestCase
+{
+    /**
+     * @var string
+     */
+    private $sessionDir;
+
+    /**
+     * @var MockFileSessionStorage
+     */
+    protected $storage;
+
+    protected function setUp()
+    {
+        $this->sessionDir = sys_get_temp_dir().'/sf2test';
+        $this->storage = $this->getStorage();
+    }
+
+    protected function tearDown()
+    {
+        $this->sessionDir = null;
+        $this->storage = null;
+        array_map('unlink', glob($this->sessionDir.'/*.session'));
+        if (is_dir($this->sessionDir)) {
+            rmdir($this->sessionDir);
+        }
+    }
+
+    public function testStart()
+    {
+        $this->assertEquals('', $this->storage->getId());
+        $this->assertTrue($this->storage->start());
+        $id = $this->storage->getId();
+        $this->assertNotEquals('', $this->storage->getId());
+        $this->assertTrue($this->storage->start());
+        $this->assertEquals($id, $this->storage->getId());
+    }
+
+    public function testRegenerate()
+    {
+        $this->storage->start();
+        $this->storage->getBag('attributes')->set('regenerate', 1234);
+        $this->storage->regenerate();
+        $this->assertEquals(1234, $this->storage->getBag('attributes')->get('regenerate'));
+        $this->storage->regenerate(true);
+        $this->assertEquals(1234, $this->storage->getBag('attributes')->get('regenerate'));
+    }
+
+    public function testGetId()
+    {
+        $this->assertEquals('', $this->storage->getId());
+        $this->storage->start();
+        $this->assertNotEquals('', $this->storage->getId());
+    }
+
+    public function testSave()
+    {
+        $this->storage->start();
+        $id = $this->storage->getId();
+        $this->assertNotEquals('108', $this->storage->getBag('attributes')->get('new'));
+        $this->assertFalse($this->storage->getBag('flashes')->has('newkey'));
+        $this->storage->getBag('attributes')->set('new', '108');
+        $this->storage->getBag('flashes')->set('newkey', 'test');
+        $this->storage->save();
+
+        $storage = $this->getStorage();
+        $storage->setId($id);
+        $storage->start();
+        $this->assertEquals('108', $storage->getBag('attributes')->get('new'));
+        $this->assertTrue($storage->getBag('flashes')->has('newkey'));
+        $this->assertEquals(['test'], $storage->getBag('flashes')->peek('newkey'));
+    }
+
+    public function testMultipleInstances()
+    {
+        $storage1 = $this->getStorage();
+        $storage1->start();
+        $storage1->getBag('attributes')->set('foo', 'bar');
+        $storage1->save();
+
+        $storage2 = $this->getStorage();
+        $storage2->setId($storage1->getId());
+        $storage2->start();
+        $this->assertEquals('bar', $storage2->getBag('attributes')->get('foo'), 'values persist between instances');
+    }
+
+    /**
+     * @expectedException \RuntimeException
+     */
+    public function testSaveWithoutStart()
+    {
+        $storage1 = $this->getStorage();
+        $storage1->save();
+    }
+
+    private function getStorage()
+    {
+        $storage = new MockFileSessionStorage($this->sessionDir);
+        $storage->registerBag(new FlashBag());
+        $storage->registerBag(new AttributeBag());
+
+        return $storage;
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/NativeSessionStorageTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/NativeSessionStorageTest.php
new file mode 100755
index 0000000..7cc2eb7
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/NativeSessionStorageTest.php
@@ -0,0 +1,296 @@
+<?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\HttpFoundation\Tests\Session\Storage;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
+use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
+use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
+
+/**
+ * Test class for NativeSessionStorage.
+ *
+ * @author Drak <drak@zikula.org>
+ *
+ * These tests require separate processes.
+ *
+ * @runTestsInSeparateProcesses
+ * @preserveGlobalState disabled
+ */
+class NativeSessionStorageTest extends TestCase
+{
+    private $savePath;
+
+    protected function setUp()
+    {
+        $this->iniSet('session.save_handler', 'files');
+        $this->iniSet('session.save_path', $this->savePath = sys_get_temp_dir().'/sf2test');
+        if (!is_dir($this->savePath)) {
+            mkdir($this->savePath);
+        }
+    }
+
+    protected function tearDown()
+    {
+        session_write_close();
+        array_map('unlink', glob($this->savePath.'/*'));
+        if (is_dir($this->savePath)) {
+            rmdir($this->savePath);
+        }
+
+        $this->savePath = null;
+    }
+
+    /**
+     * @return NativeSessionStorage
+     */
+    protected function getStorage(array $options = [])
+    {
+        $storage = new NativeSessionStorage($options);
+        $storage->registerBag(new AttributeBag());
+
+        return $storage;
+    }
+
+    public function testBag()
+    {
+        $storage = $this->getStorage();
+        $bag = new FlashBag();
+        $storage->registerBag($bag);
+        $this->assertSame($bag, $storage->getBag($bag->getName()));
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testRegisterBagException()
+    {
+        $storage = $this->getStorage();
+        $storage->getBag('non_existing');
+    }
+
+    /**
+     * @expectedException \LogicException
+     */
+    public function testRegisterBagForAStartedSessionThrowsException()
+    {
+        $storage = $this->getStorage();
+        $storage->start();
+        $storage->registerBag(new AttributeBag());
+    }
+
+    public function testGetId()
+    {
+        $storage = $this->getStorage();
+        $this->assertSame('', $storage->getId(), 'Empty ID before starting session');
+
+        $storage->start();
+        $id = $storage->getId();
+        $this->assertInternalType('string', $id);
+        $this->assertNotSame('', $id);
+
+        $storage->save();
+        $this->assertSame($id, $storage->getId(), 'ID stays after saving session');
+    }
+
+    public function testRegenerate()
+    {
+        $storage = $this->getStorage();
+        $storage->start();
+        $id = $storage->getId();
+        $storage->getBag('attributes')->set('lucky', 7);
+        $storage->regenerate();
+        $this->assertNotEquals($id, $storage->getId());
+        $this->assertEquals(7, $storage->getBag('attributes')->get('lucky'));
+    }
+
+    public function testRegenerateDestroy()
+    {
+        $storage = $this->getStorage();
+        $storage->start();
+        $id = $storage->getId();
+        $storage->getBag('attributes')->set('legs', 11);
+        $storage->regenerate(true);
+        $this->assertNotEquals($id, $storage->getId());
+        $this->assertEquals(11, $storage->getBag('attributes')->get('legs'));
+    }
+
+    public function testSessionGlobalIsUpToDateAfterIdRegeneration()
+    {
+        $storage = $this->getStorage();
+        $storage->start();
+        $storage->getBag('attributes')->set('lucky', 7);
+        $storage->regenerate();
+        $storage->getBag('attributes')->set('lucky', 42);
+
+        $this->assertEquals(42, $_SESSION['_sf2_attributes']['lucky']);
+    }
+
+    public function testRegenerationFailureDoesNotFlagStorageAsStarted()
+    {
+        $storage = $this->getStorage();
+        $this->assertFalse($storage->regenerate());
+        $this->assertFalse($storage->isStarted());
+    }
+
+    public function testDefaultSessionCacheLimiter()
+    {
+        $this->iniSet('session.cache_limiter', 'nocache');
+
+        $storage = new NativeSessionStorage();
+        $this->assertEquals('', ini_get('session.cache_limiter'));
+    }
+
+    public function testExplicitSessionCacheLimiter()
+    {
+        $this->iniSet('session.cache_limiter', 'nocache');
+
+        $storage = new NativeSessionStorage(['cache_limiter' => 'public']);
+        $this->assertEquals('public', ini_get('session.cache_limiter'));
+    }
+
+    public function testCookieOptions()
+    {
+        $options = [
+            'cookie_lifetime' => 123456,
+            'cookie_path' => '/my/cookie/path',
+            'cookie_domain' => 'symfony.example.com',
+            'cookie_secure' => true,
+            'cookie_httponly' => false,
+        ];
+
+        $this->getStorage($options);
+        $temp = session_get_cookie_params();
+        $gco = [];
+
+        foreach ($temp as $key => $value) {
+            $gco['cookie_'.$key] = $value;
+        }
+
+        unset($gco['cookie_samesite']);
+
+        $this->assertEquals($options, $gco);
+    }
+
+    public function testSessionOptions()
+    {
+        if (\defined('HHVM_VERSION')) {
+            $this->markTestSkipped('HHVM is not handled in this test case.');
+        }
+
+        $options = [
+            'url_rewriter.tags' => 'a=href',
+            'cache_expire' => '200',
+        ];
+
+        $this->getStorage($options);
+
+        $this->assertSame('a=href', ini_get('url_rewriter.tags'));
+        $this->assertSame('200', ini_get('session.cache_expire'));
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testSetSaveHandlerException()
+    {
+        $storage = $this->getStorage();
+        $storage->setSaveHandler(new \stdClass());
+    }
+
+    public function testSetSaveHandler()
+    {
+        $this->iniSet('session.save_handler', 'files');
+        $storage = $this->getStorage();
+        $storage->setSaveHandler();
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
+        $storage->setSaveHandler(null);
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
+        $storage->setSaveHandler(new SessionHandlerProxy(new NativeFileSessionHandler()));
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
+        $storage->setSaveHandler(new NativeFileSessionHandler());
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
+        $storage->setSaveHandler(new SessionHandlerProxy(new NullSessionHandler()));
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
+        $storage->setSaveHandler(new NullSessionHandler());
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
+    }
+
+    /**
+     * @expectedException \RuntimeException
+     */
+    public function testStarted()
+    {
+        $storage = $this->getStorage();
+
+        $this->assertFalse($storage->getSaveHandler()->isActive());
+        $this->assertFalse($storage->isStarted());
+
+        session_start();
+        $this->assertTrue(isset($_SESSION));
+        $this->assertTrue($storage->getSaveHandler()->isActive());
+
+        // PHP session might have started, but the storage driver has not, so false is correct here
+        $this->assertFalse($storage->isStarted());
+
+        $key = $storage->getMetadataBag()->getStorageKey();
+        $this->assertArrayNotHasKey($key, $_SESSION);
+        $storage->start();
+    }
+
+    public function testRestart()
+    {
+        $storage = $this->getStorage();
+        $storage->start();
+        $id = $storage->getId();
+        $storage->getBag('attributes')->set('lucky', 7);
+        $storage->save();
+        $storage->start();
+        $this->assertSame($id, $storage->getId(), 'Same session ID after restarting');
+        $this->assertSame(7, $storage->getBag('attributes')->get('lucky'), 'Data still available');
+    }
+
+    public function testCanCreateNativeSessionStorageWhenSessionAlreadyStarted()
+    {
+        session_start();
+        $this->getStorage();
+
+        // Assert no exception has been thrown by `getStorage()`
+        $this->addToAssertionCount(1);
+    }
+
+    public function testSetSessionOptionsOnceSessionStartedIsIgnored()
+    {
+        session_start();
+        $this->getStorage([
+            'name' => 'something-else',
+        ]);
+
+        // Assert no exception has been thrown by `getStorage()`
+        $this->addToAssertionCount(1);
+    }
+
+    public function testGetBagsOnceSessionStartedIsIgnored()
+    {
+        session_start();
+        $bag = new AttributeBag();
+        $bag->setName('flashes');
+
+        $storage = $this->getStorage();
+        $storage->registerBag($bag);
+
+        $this->assertEquals($storage->getBag('flashes'), $bag);
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php
new file mode 100755
index 0000000..752be61
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php
@@ -0,0 +1,96 @@
+<?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\HttpFoundation\Tests\Session\Storage;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
+use Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage;
+
+/**
+ * Test class for PhpSessionStorage.
+ *
+ * @author Drak <drak@zikula.org>
+ *
+ * These tests require separate processes.
+ *
+ * @runTestsInSeparateProcesses
+ * @preserveGlobalState disabled
+ */
+class PhpBridgeSessionStorageTest extends TestCase
+{
+    private $savePath;
+
+    protected function setUp()
+    {
+        $this->iniSet('session.save_handler', 'files');
+        $this->iniSet('session.save_path', $this->savePath = sys_get_temp_dir().'/sf2test');
+        if (!is_dir($this->savePath)) {
+            mkdir($this->savePath);
+        }
+    }
+
+    protected function tearDown()
+    {
+        session_write_close();
+        array_map('unlink', glob($this->savePath.'/*'));
+        if (is_dir($this->savePath)) {
+            rmdir($this->savePath);
+        }
+
+        $this->savePath = null;
+    }
+
+    /**
+     * @return PhpBridgeSessionStorage
+     */
+    protected function getStorage()
+    {
+        $storage = new PhpBridgeSessionStorage();
+        $storage->registerBag(new AttributeBag());
+
+        return $storage;
+    }
+
+    public function testPhpSession()
+    {
+        $storage = $this->getStorage();
+
+        $this->assertFalse($storage->getSaveHandler()->isActive());
+        $this->assertFalse($storage->isStarted());
+
+        session_start();
+        $this->assertTrue(isset($_SESSION));
+        // in PHP 5.4 we can reliably detect a session started
+        $this->assertTrue($storage->getSaveHandler()->isActive());
+        // PHP session might have started, but the storage driver has not, so false is correct here
+        $this->assertFalse($storage->isStarted());
+
+        $key = $storage->getMetadataBag()->getStorageKey();
+        $this->assertArrayNotHasKey($key, $_SESSION);
+        $storage->start();
+        $this->assertArrayHasKey($key, $_SESSION);
+    }
+
+    public function testClear()
+    {
+        $storage = $this->getStorage();
+        session_start();
+        $_SESSION['drak'] = 'loves symfony';
+        $storage->getBag('attributes')->set('symfony', 'greatness');
+        $key = $storage->getBag('attributes')->getStorageKey();
+        $this->assertEquals($_SESSION[$key], ['symfony' => 'greatness']);
+        $this->assertEquals($_SESSION['drak'], 'loves symfony');
+        $storage->clear();
+        $this->assertEquals($_SESSION[$key], []);
+        $this->assertEquals($_SESSION['drak'], 'loves symfony');
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/AbstractProxyTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/AbstractProxyTest.php
new file mode 100755
index 0000000..cbb291f
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/AbstractProxyTest.php
@@ -0,0 +1,113 @@
+<?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\HttpFoundation\Tests\Session\Storage\Proxy;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
+use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
+
+/**
+ * Test class for AbstractProxy.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class AbstractProxyTest extends TestCase
+{
+    /**
+     * @var AbstractProxy
+     */
+    protected $proxy;
+
+    protected function setUp()
+    {
+        $this->proxy = $this->getMockForAbstractClass(AbstractProxy::class);
+    }
+
+    protected function tearDown()
+    {
+        $this->proxy = null;
+    }
+
+    public function testGetSaveHandlerName()
+    {
+        $this->assertNull($this->proxy->getSaveHandlerName());
+    }
+
+    public function testIsSessionHandlerInterface()
+    {
+        $this->assertFalse($this->proxy->isSessionHandlerInterface());
+        $sh = new SessionHandlerProxy(new \SessionHandler());
+        $this->assertTrue($sh->isSessionHandlerInterface());
+    }
+
+    public function testIsWrapper()
+    {
+        $this->assertFalse($this->proxy->isWrapper());
+    }
+
+    /**
+     * @runInSeparateProcess
+     * @preserveGlobalState disabled
+     */
+    public function testIsActive()
+    {
+        $this->assertFalse($this->proxy->isActive());
+        session_start();
+        $this->assertTrue($this->proxy->isActive());
+    }
+
+    /**
+     * @runInSeparateProcess
+     * @preserveGlobalState disabled
+     */
+    public function testName()
+    {
+        $this->assertEquals(session_name(), $this->proxy->getName());
+        $this->proxy->setName('foo');
+        $this->assertEquals('foo', $this->proxy->getName());
+        $this->assertEquals(session_name(), $this->proxy->getName());
+    }
+
+    /**
+     * @runInSeparateProcess
+     * @preserveGlobalState disabled
+     * @expectedException \LogicException
+     */
+    public function testNameException()
+    {
+        session_start();
+        $this->proxy->setName('foo');
+    }
+
+    /**
+     * @runInSeparateProcess
+     * @preserveGlobalState disabled
+     */
+    public function testId()
+    {
+        $this->assertEquals(session_id(), $this->proxy->getId());
+        $this->proxy->setId('foo');
+        $this->assertEquals('foo', $this->proxy->getId());
+        $this->assertEquals(session_id(), $this->proxy->getId());
+    }
+
+    /**
+     * @runInSeparateProcess
+     * @preserveGlobalState disabled
+     * @expectedException \LogicException
+     */
+    public function testIdException()
+    {
+        session_start();
+        $this->proxy->setId('foo');
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/NativeProxyTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/NativeProxyTest.php
new file mode 100755
index 0000000..ed4fee6
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/NativeProxyTest.php
@@ -0,0 +1,38 @@
+<?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\HttpFoundation\Tests\Session\Storage\Proxy;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy;
+
+/**
+ * Test class for NativeProxy.
+ *
+ * @group legacy
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class NativeProxyTest extends TestCase
+{
+    public function testIsWrapper()
+    {
+        $proxy = new NativeProxy();
+        $this->assertFalse($proxy->isWrapper());
+    }
+
+    public function testGetSaveHandlerName()
+    {
+        $name = ini_get('session.save_handler');
+        $proxy = new NativeProxy();
+        $this->assertEquals($name, $proxy->getSaveHandlerName());
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php
new file mode 100755
index 0000000..0459a8c
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php
@@ -0,0 +1,157 @@
+<?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\HttpFoundation\Tests\Session\Storage\Proxy;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
+
+/**
+ * Tests for SessionHandlerProxy class.
+ *
+ * @author Drak <drak@zikula.org>
+ *
+ * @runTestsInSeparateProcesses
+ * @preserveGlobalState disabled
+ */
+class SessionHandlerProxyTest extends TestCase
+{
+    /**
+     * @var \PHPUnit_Framework_MockObject_Matcher
+     */
+    private $mock;
+
+    /**
+     * @var SessionHandlerProxy
+     */
+    private $proxy;
+
+    protected function setUp()
+    {
+        $this->mock = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+        $this->proxy = new SessionHandlerProxy($this->mock);
+    }
+
+    protected function tearDown()
+    {
+        $this->mock = null;
+        $this->proxy = null;
+    }
+
+    public function testOpenTrue()
+    {
+        $this->mock->expects($this->once())
+            ->method('open')
+            ->will($this->returnValue(true));
+
+        $this->assertFalse($this->proxy->isActive());
+        $this->proxy->open('name', 'id');
+        $this->assertFalse($this->proxy->isActive());
+    }
+
+    public function testOpenFalse()
+    {
+        $this->mock->expects($this->once())
+            ->method('open')
+            ->will($this->returnValue(false));
+
+        $this->assertFalse($this->proxy->isActive());
+        $this->proxy->open('name', 'id');
+        $this->assertFalse($this->proxy->isActive());
+    }
+
+    public function testClose()
+    {
+        $this->mock->expects($this->once())
+            ->method('close')
+            ->will($this->returnValue(true));
+
+        $this->assertFalse($this->proxy->isActive());
+        $this->proxy->close();
+        $this->assertFalse($this->proxy->isActive());
+    }
+
+    public function testCloseFalse()
+    {
+        $this->mock->expects($this->once())
+            ->method('close')
+            ->will($this->returnValue(false));
+
+        $this->assertFalse($this->proxy->isActive());
+        $this->proxy->close();
+        $this->assertFalse($this->proxy->isActive());
+    }
+
+    public function testRead()
+    {
+        $this->mock->expects($this->once())
+            ->method('read');
+
+        $this->proxy->read('id');
+    }
+
+    public function testWrite()
+    {
+        $this->mock->expects($this->once())
+            ->method('write');
+
+        $this->proxy->write('id', 'data');
+    }
+
+    public function testDestroy()
+    {
+        $this->mock->expects($this->once())
+            ->method('destroy');
+
+        $this->proxy->destroy('id');
+    }
+
+    public function testGc()
+    {
+        $this->mock->expects($this->once())
+            ->method('gc');
+
+        $this->proxy->gc(86400);
+    }
+
+    /**
+     * @requires PHPUnit 5.1
+     */
+    public function testValidateId()
+    {
+        $mock = $this->getMockBuilder(['SessionHandlerInterface', 'SessionUpdateTimestampHandlerInterface'])->getMock();
+        $mock->expects($this->once())
+            ->method('validateId');
+
+        $proxy = new SessionHandlerProxy($mock);
+        $proxy->validateId('id');
+
+        $this->assertTrue($this->proxy->validateId('id'));
+    }
+
+    /**
+     * @requires PHPUnit 5.1
+     */
+    public function testUpdateTimestamp()
+    {
+        $mock = $this->getMockBuilder(['SessionHandlerInterface', 'SessionUpdateTimestampHandlerInterface'])->getMock();
+        $mock->expects($this->once())
+            ->method('updateTimestamp');
+
+        $proxy = new SessionHandlerProxy($mock);
+        $proxy->updateTimestamp('id', 'data');
+
+        $this->mock->expects($this->once())
+            ->method('write');
+
+        $this->proxy->updateTimestamp('id', 'data');
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/StreamedResponseTest.php b/vendor/symfony/http-foundation/Tests/StreamedResponseTest.php
new file mode 100755
index 0000000..62dfc9b
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/StreamedResponseTest.php
@@ -0,0 +1,144 @@
+<?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\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\StreamedResponse;
+
+class StreamedResponseTest extends TestCase
+{
+    public function testConstructor()
+    {
+        $response = new StreamedResponse(function () { echo 'foo'; }, 404, ['Content-Type' => 'text/plain']);
+
+        $this->assertEquals(404, $response->getStatusCode());
+        $this->assertEquals('text/plain', $response->headers->get('Content-Type'));
+    }
+
+    public function testPrepareWith11Protocol()
+    {
+        $response = new StreamedResponse(function () { echo 'foo'; });
+        $request = Request::create('/');
+        $request->server->set('SERVER_PROTOCOL', 'HTTP/1.1');
+
+        $response->prepare($request);
+
+        $this->assertEquals('1.1', $response->getProtocolVersion());
+        $this->assertNotEquals('chunked', $response->headers->get('Transfer-Encoding'), 'Apache assumes responses with a Transfer-Encoding header set to chunked to already be encoded.');
+    }
+
+    public function testPrepareWith10Protocol()
+    {
+        $response = new StreamedResponse(function () { echo 'foo'; });
+        $request = Request::create('/');
+        $request->server->set('SERVER_PROTOCOL', 'HTTP/1.0');
+
+        $response->prepare($request);
+
+        $this->assertEquals('1.0', $response->getProtocolVersion());
+        $this->assertNull($response->headers->get('Transfer-Encoding'));
+    }
+
+    public function testPrepareWithHeadRequest()
+    {
+        $response = new StreamedResponse(function () { echo 'foo'; }, 200, ['Content-Length' => '123']);
+        $request = Request::create('/', 'HEAD');
+
+        $response->prepare($request);
+
+        $this->assertSame('123', $response->headers->get('Content-Length'));
+    }
+
+    public function testPrepareWithCacheHeaders()
+    {
+        $response = new StreamedResponse(function () { echo 'foo'; }, 200, ['Cache-Control' => 'max-age=600, public']);
+        $request = Request::create('/', 'GET');
+
+        $response->prepare($request);
+        $this->assertEquals('max-age=600, public', $response->headers->get('Cache-Control'));
+    }
+
+    public function testSendContent()
+    {
+        $called = 0;
+
+        $response = new StreamedResponse(function () use (&$called) { ++$called; });
+
+        $response->sendContent();
+        $this->assertEquals(1, $called);
+
+        $response->sendContent();
+        $this->assertEquals(1, $called);
+    }
+
+    /**
+     * @expectedException \LogicException
+     */
+    public function testSendContentWithNonCallable()
+    {
+        $response = new StreamedResponse(null);
+        $response->sendContent();
+    }
+
+    /**
+     * @expectedException \LogicException
+     */
+    public function testSetContent()
+    {
+        $response = new StreamedResponse(function () { echo 'foo'; });
+        $response->setContent('foo');
+    }
+
+    public function testGetContent()
+    {
+        $response = new StreamedResponse(function () { echo 'foo'; });
+        $this->assertFalse($response->getContent());
+    }
+
+    public function testCreate()
+    {
+        $response = StreamedResponse::create(function () {}, 204);
+
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response);
+        $this->assertEquals(204, $response->getStatusCode());
+    }
+
+    public function testReturnThis()
+    {
+        $response = new StreamedResponse(function () {});
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response->sendContent());
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response->sendContent());
+
+        $response = new StreamedResponse(function () {});
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response->sendHeaders());
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response->sendHeaders());
+    }
+
+    public function testSetNotModified()
+    {
+        $response = new StreamedResponse(function () { echo 'foo'; });
+        $modified = $response->setNotModified();
+        $this->assertObjectHasAttribute('headers', $modified);
+        $this->assertObjectHasAttribute('content', $modified);
+        $this->assertObjectHasAttribute('version', $modified);
+        $this->assertObjectHasAttribute('statusCode', $modified);
+        $this->assertObjectHasAttribute('statusText', $modified);
+        $this->assertObjectHasAttribute('charset', $modified);
+        $this->assertEquals(304, $modified->getStatusCode());
+
+        ob_start();
+        $modified->sendContent();
+        $string = ob_get_clean();
+        $this->assertEmpty($string);
+    }
+}
diff --git a/vendor/symfony/http-foundation/Tests/schema/http-status-codes.rng b/vendor/symfony/http-foundation/Tests/schema/http-status-codes.rng
new file mode 100755
index 0000000..73708ca
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/schema/http-status-codes.rng
@@ -0,0 +1,31 @@
+<?xml version='1.0'?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+  datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
+  ns="http://www.iana.org/assignments">
+
+  <include href="iana-registry.rng"/>
+
+  <start>
+    <element name="registry">
+      <ref name="registryMeta"/>
+      <element name="registry">
+        <ref name="registryMeta"/>
+        <zeroOrMore>
+          <element name="record">
+            <optional>
+              <attribute name="date"><ref name="genericDate"/></attribute>
+            </optional>
+            <optional>
+              <attribute name="updated"><ref name="genericDate"/></attribute>
+            </optional>
+            <element name="value"><ref name="genericRange"/></element>
+            <element name="description"><text/></element>
+            <ref name="references"/>
+          </element>
+        </zeroOrMore>
+      </element>
+      <ref name="people"/>
+    </element>
+  </start>
+
+</grammar>
diff --git a/vendor/symfony/http-foundation/Tests/schema/iana-registry.rng b/vendor/symfony/http-foundation/Tests/schema/iana-registry.rng
new file mode 100755
index 0000000..b9c3ca9
--- /dev/null
+++ b/vendor/symfony/http-foundation/Tests/schema/iana-registry.rng
@@ -0,0 +1,198 @@
+<?xml version='1.0'?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+  datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
+  ns="http://www.iana.org/assignments">
+
+  <define name="registryMeta">
+    <interleave>
+      <attribute name="id"><data type="ID"/></attribute>
+      <optional><element name="title"><ref name="text_with_references"/></element></optional>
+      <optional><element name="created"><ref name="genericDate"/></element></optional>
+      <optional><element name="updated"><data type="date"/></element></optional>
+      <optional><element name="registration_rule"><ref
+            name="text_with_references"/></element></optional>
+      <optional><element name="expert"><text/></element></optional>
+      <optional><element name="description"><ref name="text_with_references"/></element></optional>
+      <zeroOrMore><element name="note"><ref name="text_with_references"/></element></zeroOrMore>
+      <ref name="references"/>
+      <optional><element name="hide"><empty/></element></optional>
+      <zeroOrMore><element name="category"><text/></element></zeroOrMore>
+      <zeroOrMore><ref name="range"/></zeroOrMore>
+      <optional><ref name="file"/></optional>
+    </interleave>
+  </define>
+
+  <define name="range">
+    <element name="range">
+       <interleave>
+	  <element name="value"><text/></element>
+	  <optional><element name="hex"><text/></element></optional>
+	  <element name="registration_rule"><ref name="text_with_references"/></element>
+	  <optional><element name="note"><ref name="text_with_references"/></element></optional>
+	  <optional><ref name="xref"/></optional>
+       </interleave>
+    </element>
+  </define>
+
+  <define name="people">
+    <element name="people">
+      <zeroOrMore>
+        <element name="person">
+          <attribute name="id"><data type="ID"/></attribute>
+          <optional><element name="name"><text/></element></optional>
+          <optional><element name="org"><text/></element></optional>
+          <zeroOrMore><element name="uri"><data type="anyURI"/></element></zeroOrMore>
+          <optional><element name="updated"><ref name="genericDate"/></element></optional>
+        </element>
+      </zeroOrMore>
+    </element>
+  </define>
+
+  <define name="xref">
+    <element name="xref">
+      <optional>
+        <attribute name="lastupdated"><ref name="genericDate"/></attribute>
+      </optional>
+      <choice>
+        <group>
+          <attribute name="type"><value>uri</value></attribute>
+          <attribute name="data"><data type="anyURI"/></attribute>
+        </group>
+        <group>
+          <attribute name="type"><value>rfc</value></attribute>
+          <attribute name="data">
+            <data type="string">
+              <param name="pattern">(rfc|bcp|std)\d+</param>
+            </data>
+          </attribute>
+        </group>
+        <group>
+          <attribute name="type"><value>rfc-errata</value></attribute>
+          <attribute name="data"><data type="positiveInteger"/></attribute>
+        </group>
+        <group>
+          <attribute name="type"><value>draft</value></attribute>
+          <attribute name="data">
+            <data type="string">
+              <param name="pattern">(draft|RFC)(-[a-zA-Z0-9]+)+</param>
+            </data>
+          </attribute>
+        </group>
+        <group>
+          <attribute name="type"><value>registry</value></attribute>
+          <attribute name="data"><data type="NCName"/></attribute>
+        </group>
+        <group>
+          <attribute name="type"><value>person</value></attribute>
+          <attribute name="data"><data type="NCName"/></attribute>
+        </group>
+        <group>
+          <attribute name="type"><value>text</value></attribute>
+        </group>
+        <group>
+          <attribute name="type"><value>note</value></attribute>
+          <attribute name="data"><data type="positiveInteger"/></attribute>
+        </group>
+        <group>
+          <attribute name="type"><value>unicode</value></attribute>
+          <attribute name="data">
+            <data type="string">
+              <param name="pattern">ucd\d+\.\d+\.\d+</param>
+            </data>
+          </attribute>
+        </group>
+      </choice>
+      <text/>
+    </element>
+  </define>
+
+  <define name="references">
+    <zeroOrMore>
+      <ref name="xref"/>
+    </zeroOrMore>
+  </define>
+
+  <define name="text_with_references">
+    <interleave>
+      <zeroOrMore>
+        <text/>
+        <optional><ref name="xref"/></optional>
+      </zeroOrMore>
+    </interleave>
+  </define>
+
+  <define name="richText">
+    <zeroOrMore>
+      <choice>
+        <interleave>
+          <ref name="text_with_references"/>
+          <optional><element name="br"><empty/></element></optional>
+        </interleave>
+        <element name="paragraph">
+          <interleave>
+            <ref name="text_with_references"/>
+            <optional><element name="br"><empty/></element></optional>
+          </interleave>
+        </element>
+        <element name="artwork"><text/></element>
+      </choice>
+    </zeroOrMore>
+  </define>
+
+  <define name="genericRange">
+    <data type="string">
+      <param name="pattern">(\d+|0x[\da-fA-F]+)(\s*-\s*(\d+|0x[\da-fA-F]+))?</param>
+    </data>
+  </define>
+
+  <define name="genericDate">
+    <choice>
+      <data type="date"/>
+      <data type="gYearMonth"/>
+    </choice>
+  </define>
+
+  <define name="hex32">
+    <data type="string">
+      <param name="pattern">0x[0-9]{8}</param>
+    </data>
+  </define>
+
+  <define name="binary">
+    <data type="string">
+      <param name="pattern">[0-1]+</param>
+    </data>
+  </define>
+
+  <define name="footnotes">
+    <zeroOrMore>
+      <element name="footnote">
+        <attribute name="anchor"><data type="positiveInteger"/></attribute>
+        <interleave>
+          <zeroOrMore>
+            <text/>
+            <optional><ref name="xref"/></optional>
+          </zeroOrMore>
+        </interleave>
+      </element>
+    </zeroOrMore>
+  </define>
+
+  <define name="file">
+    <element name="file">
+      <attribute name="type">
+        <choice>
+          <value>legacy</value>
+          <value>mib</value>
+          <value>template</value>
+          <value>json</value>
+        </choice>
+      </attribute>
+      <optional>
+        <attribute name="name"/>
+      </optional>
+      <data type="anyURI"/>
+    </element>
+  </define>
+
+</grammar>
diff --git a/vendor/symfony/http-foundation/composer.json b/vendor/symfony/http-foundation/composer.json
new file mode 100755
index 0000000..f6c6f2e
--- /dev/null
+++ b/vendor/symfony/http-foundation/composer.json
@@ -0,0 +1,38 @@
+{
+    "name": "symfony/http-foundation",
+    "type": "library",
+    "description": "Symfony HttpFoundation Component",
+    "keywords": [],
+    "homepage": "https://symfony.com",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Fabien Potencier",
+            "email": "fabien@symfony.com"
+        },
+        {
+            "name": "Symfony Community",
+            "homepage": "https://symfony.com/contributors"
+        }
+    ],
+    "require": {
+        "php": "^5.5.9|>=7.0.8",
+        "symfony/polyfill-mbstring": "~1.1",
+        "symfony/polyfill-php70": "~1.6"
+    },
+    "require-dev": {
+        "symfony/expression-language": "~2.8|~3.0|~4.0"
+    },
+    "autoload": {
+        "psr-4": { "Symfony\\Component\\HttpFoundation\\": "" },
+        "exclude-from-classmap": [
+            "/Tests/"
+        ]
+    },
+    "minimum-stability": "dev",
+    "extra": {
+        "branch-alias": {
+            "dev-master": "3.4-dev"
+        }
+    }
+}
diff --git a/vendor/symfony/http-foundation/phpunit.xml.dist b/vendor/symfony/http-foundation/phpunit.xml.dist
new file mode 100755
index 0000000..f57bc9e
--- /dev/null
+++ b/vendor/symfony/http-foundation/phpunit.xml.dist
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/5.2/phpunit.xsd"
+         backupGlobals="false"
+         colors="true"
+         bootstrap="vendor/autoload.php"
+         failOnRisky="true"
+         failOnWarning="true"
+>
+    <php>
+        <ini name="error_reporting" value="-1" />
+    </php>
+
+    <testsuites>
+        <testsuite name="Symfony HttpFoundation Component Test Suite">
+            <directory>./Tests/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./</directory>
+            <exclude>
+                <directory>./Resources</directory>
+                <directory>./Tests</directory>
+                <directory>./vendor</directory>
+            </exclude>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/vendor/symfony/polyfill-intl-idn/Idn.php b/vendor/symfony/polyfill-intl-idn/Idn.php
new file mode 100755
index 0000000..127437e
--- /dev/null
+++ b/vendor/symfony/polyfill-intl-idn/Idn.php
@@ -0,0 +1,915 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com> and Trevor Rowbotham <trevor.rowbotham@pm.me>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Polyfill\Intl\Idn;
+
+use Exception;
+use Normalizer;
+use Symfony\Polyfill\Intl\Idn\Resources\unidata\DisallowedRanges;
+use Symfony\Polyfill\Intl\Idn\Resources\unidata\Regex;
+
+/**
+ * @see https://www.unicode.org/reports/tr46/
+ *
+ * @internal
+ */
+final class Idn
+{
+    const ERROR_EMPTY_LABEL = 1;
+    const ERROR_LABEL_TOO_LONG = 2;
+    const ERROR_DOMAIN_NAME_TOO_LONG = 4;
+    const ERROR_LEADING_HYPHEN = 8;
+    const ERROR_TRAILING_HYPHEN = 0x10;
+    const ERROR_HYPHEN_3_4 = 0x20;
+    const ERROR_LEADING_COMBINING_MARK = 0x40;
+    const ERROR_DISALLOWED = 0x80;
+    const ERROR_PUNYCODE = 0x100;
+    const ERROR_LABEL_HAS_DOT = 0x200;
+    const ERROR_INVALID_ACE_LABEL = 0x400;
+    const ERROR_BIDI = 0x800;
+    const ERROR_CONTEXTJ = 0x1000;
+    const ERROR_CONTEXTO_PUNCTUATION = 0x2000;
+    const ERROR_CONTEXTO_DIGITS = 0x4000;
+
+    const INTL_IDNA_VARIANT_2003 = 0;
+    const INTL_IDNA_VARIANT_UTS46 = 1;
+
+    const MAX_DOMAIN_SIZE = 253;
+    const MAX_LABEL_SIZE = 63;
+
+    const BASE = 36;
+    const TMIN = 1;
+    const TMAX = 26;
+    const SKEW = 38;
+    const DAMP = 700;
+    const INITIAL_BIAS = 72;
+    const INITIAL_N = 128;
+    const DELIMITER = '-';
+    const MAX_INT = 2147483647;
+
+    /**
+     * Contains the numeric value of a basic code point (for use in representing integers) in the
+     * range 0 to BASE-1, or -1 if b is does not represent a value.
+     *
+     * @var array<int, int>
+     */
+    private static $basicToDigit = array(
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1,
+
+        -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+
+        -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    );
+
+    /**
+     * @var array<int, int>
+     */
+    private static $virama;
+
+    /**
+     * @var array<int, string>
+     */
+    private static $mapped;
+
+    /**
+     * @var array<int, bool>
+     */
+    private static $ignored;
+
+    /**
+     * @var array<int, string>
+     */
+    private static $deviation;
+
+    /**
+     * @var array<int, bool>
+     */
+    private static $disallowed;
+
+    /**
+     * @var array<int, string>
+     */
+    private static $disallowed_STD3_mapped;
+
+    /**
+     * @var array<int, bool>
+     */
+    private static $disallowed_STD3_valid;
+
+    /**
+     * @var bool
+     */
+    private static $mappingTableLoaded = false;
+
+    /**
+     * @see https://www.unicode.org/reports/tr46/#ToASCII
+     *
+     * @param string $domainName
+     * @param int    $options
+     * @param int    $variant
+     * @param array  $idna_info
+     *
+     * @return string|false
+     */
+    public static function idn_to_ascii($domainName, $options = IDNA_DEFAULT, $variant = self::INTL_IDNA_VARIANT_UTS46, &$idna_info = array())
+    {
+        if (\PHP_VERSION_ID >= 70200 && self::INTL_IDNA_VARIANT_2003 === $variant) {
+            @trigger_error('idn_to_ascii(): INTL_IDNA_VARIANT_2003 is deprecated', E_USER_DEPRECATED);
+        }
+
+        $options = array(
+            'CheckHyphens' => true,
+            'CheckBidi' => self::INTL_IDNA_VARIANT_2003 === $variant || 0 !== ($options & \IDNA_CHECK_BIDI),
+            'CheckJoiners' => self::INTL_IDNA_VARIANT_UTS46 === $variant && 0 !== ($options & \IDNA_CHECK_CONTEXTJ),
+            'UseSTD3ASCIIRules' => 0 !== ($options & \IDNA_USE_STD3_RULES),
+            'Transitional_Processing' => self::INTL_IDNA_VARIANT_2003 === $variant || 0 === ($options & \IDNA_NONTRANSITIONAL_TO_ASCII),
+            'VerifyDnsLength' => true,
+        );
+        $info = new Info();
+        $labels = self::process((string) $domainName, $options, $info);
+
+        foreach ($labels as $i => $label) {
+            // Only convert labels to punycode that contain non-ASCII code points
+            if (1 === preg_match('/[^\x00-\x7F]/', $label)) {
+                try {
+                    $label = 'xn--'.self::punycodeEncode($label);
+                } catch (Exception $e) {
+                    $info->errors |= self::ERROR_PUNYCODE;
+                }
+
+                $labels[$i] = $label;
+            }
+        }
+
+        if ($options['VerifyDnsLength']) {
+            self::validateDomainAndLabelLength($labels, $info);
+        }
+
+        $idna_info = array(
+            'result' => implode('.', $labels),
+            'isTransitionalDifferent' => $info->transitionalDifferent,
+            'errors' => $info->errors,
+        );
+
+        return 0 === $info->errors ? $idna_info['result'] : false;
+    }
+
+    /**
+     * @see https://www.unicode.org/reports/tr46/#ToUnicode
+     *
+     * @param string $domainName
+     * @param int    $options
+     * @param int    $variant
+     * @param array  $idna_info
+     *
+     * @return string|false
+     */
+    public static function idn_to_utf8($domainName, $options = IDNA_DEFAULT, $variant = self::INTL_IDNA_VARIANT_UTS46, &$idna_info = array())
+    {
+        if (\PHP_VERSION_ID >= 70200 && self::INTL_IDNA_VARIANT_2003 === $variant) {
+            @trigger_error('idn_to_utf8(): INTL_IDNA_VARIANT_2003 is deprecated', E_USER_DEPRECATED);
+        }
+
+        $info = new Info();
+        $labels = self::process((string) $domainName, array(
+            'CheckHyphens' => true,
+            'CheckBidi' => self::INTL_IDNA_VARIANT_2003 === $variant || 0 !== ($options & \IDNA_CHECK_BIDI),
+            'CheckJoiners' => self::INTL_IDNA_VARIANT_UTS46 === $variant && 0 !== ($options & \IDNA_CHECK_CONTEXTJ),
+            'UseSTD3ASCIIRules' => 0 !== ($options & \IDNA_USE_STD3_RULES),
+            'Transitional_Processing' => self::INTL_IDNA_VARIANT_2003 === $variant || 0 === ($options & \IDNA_NONTRANSITIONAL_TO_UNICODE),
+        ), $info);
+        $idna_info = array(
+            'result' => implode('.', $labels),
+            'isTransitionalDifferent' => $info->transitionalDifferent,
+            'errors' => $info->errors,
+        );
+
+        return 0 === $info->errors ? $idna_info['result'] : false;
+    }
+
+    /**
+     * @param string $label
+     *
+     * @return bool
+     */
+    private static function isValidContextJ(array $codePoints, $label)
+    {
+        if (!isset(self::$virama)) {
+            self::$virama = require __DIR__.\DIRECTORY_SEPARATOR.'Resources'.\DIRECTORY_SEPARATOR.'unidata'.\DIRECTORY_SEPARATOR.'virama.php';
+        }
+
+        $offset = 0;
+
+        foreach ($codePoints as $i => $codePoint) {
+            if (0x200C !== $codePoint && 0x200D !== $codePoint) {
+                continue;
+            }
+
+            if (!isset($codePoints[$i - 1])) {
+                return false;
+            }
+
+            // If Canonical_Combining_Class(Before(cp)) .eq. Virama Then True;
+            if (isset(self::$virama[$codePoints[$i - 1]])) {
+                continue;
+            }
+
+            // If RegExpMatch((Joining_Type:{L,D})(Joining_Type:T)*\u200C(Joining_Type:T)*(Joining_Type:{R,D})) Then
+            // True;
+            // Generated RegExp = ([Joining_Type:{L,D}][Joining_Type:T]*\u200C[Joining_Type:T]*)[Joining_Type:{R,D}]
+            if (0x200C === $codePoint && 1 === preg_match(Regex::ZWNJ, $label, $matches, PREG_OFFSET_CAPTURE, $offset)) {
+                $offset += \strlen($matches[1][0]);
+
+                continue;
+            }
+
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * @see https://www.unicode.org/reports/tr46/#ProcessingStepMap
+     *
+     * @param string              $input
+     * @param array<string, bool> $options
+     *
+     * @return string
+     */
+    private static function mapCodePoints($input, array $options, Info $info)
+    {
+        $str = '';
+        $useSTD3ASCIIRules = $options['UseSTD3ASCIIRules'];
+        $transitional = $options['Transitional_Processing'];
+
+        foreach (self::utf8Decode($input) as $codePoint) {
+            $data = self::lookupCodePointStatus($codePoint, $useSTD3ASCIIRules);
+
+            switch ($data['status']) {
+                case 'disallowed':
+                    $info->errors |= self::ERROR_DISALLOWED;
+
+                    // no break.
+
+                case 'valid':
+                    $str .= mb_chr($codePoint, 'utf-8');
+
+                    break;
+
+                case 'ignored':
+                    // Do nothing.
+                    break;
+
+                case 'mapped':
+                    $str .= $data['mapping'];
+
+                    break;
+
+                case 'deviation':
+                    $info->transitionalDifferent = true;
+                    $str .= ($transitional ? $data['mapping'] : mb_chr($codePoint, 'utf-8'));
+
+                    break;
+            }
+        }
+
+        return $str;
+    }
+
+    /**
+     * @see https://www.unicode.org/reports/tr46/#Processing
+     *
+     * @param string              $domain
+     * @param array<string, bool> $options
+     *
+     * @return array<int, string>
+     */
+    private static function process($domain, array $options, Info $info)
+    {
+        // If VerifyDnsLength is not set, we are doing ToUnicode otherwise we are doing ToASCII and
+        // we need to respect the VerifyDnsLength option.
+        $checkForEmptyLabels = !isset($options['VerifyDnsLength']) || $options['VerifyDnsLength'];
+
+        if ($checkForEmptyLabels && '' === $domain) {
+            $info->errors |= self::ERROR_EMPTY_LABEL;
+
+            return array($domain);
+        }
+
+        // Step 1. Map each code point in the domain name string
+        $domain = self::mapCodePoints($domain, $options, $info);
+
+        // Step 2. Normalize the domain name string to Unicode Normalization Form C.
+        if (!Normalizer::isNormalized($domain, Normalizer::FORM_C)) {
+            $domain = Normalizer::normalize($domain, Normalizer::FORM_C);
+        }
+
+        // Step 3. Break the string into labels at U+002E (.) FULL STOP.
+        $labels = explode('.', $domain);
+        $lastLabelIndex = \count($labels) - 1;
+
+        // Step 4. Convert and validate each label in the domain name string.
+        foreach ($labels as $i => $label) {
+            $validationOptions = $options;
+
+            if ('xn--' === substr($label, 0, 4)) {
+                try {
+                    $label = self::punycodeDecode(substr($label, 4));
+                } catch (Exception $e) {
+                    $info->errors |= self::ERROR_PUNYCODE;
+
+                    continue;
+                }
+
+                $validationOptions['Transitional_Processing'] = false;
+                $labels[$i] = $label;
+            }
+
+            self::validateLabel($label, $info, $validationOptions, $i > 0 && $i === $lastLabelIndex);
+        }
+
+        if ($info->bidiDomain && !$info->validBidiDomain) {
+            $info->errors |= self::ERROR_BIDI;
+        }
+
+        // Any input domain name string that does not record an error has been successfully
+        // processed according to this specification. Conversely, if an input domain_name string
+        // causes an error, then the processing of the input domain_name string fails. Determining
+        // what to do with error input is up to the caller, and not in the scope of this document.
+        return $labels;
+    }
+
+    /**
+     * @see https://tools.ietf.org/html/rfc5893#section-2
+     *
+     * @param string $label
+     */
+    private static function validateBidiLabel($label, Info $info)
+    {
+        if (1 === preg_match(Regex::RTL_LABEL, $label)) {
+            $info->bidiDomain = true;
+
+            // Step 1. The first character must be a character with Bidi property L, R, or AL.
+            // If it has the R or AL property, it is an RTL label
+            if (1 !== preg_match(Regex::BIDI_STEP_1_RTL, $label)) {
+                $info->validBidiDomain = false;
+
+                return;
+            }
+
+            // Step 2. In an RTL label, only characters with the Bidi properties R, AL, AN, EN, ES,
+            // CS, ET, ON, BN, or NSM are allowed.
+            if (1 === preg_match(Regex::BIDI_STEP_2, $label)) {
+                $info->validBidiDomain = false;
+
+                return;
+            }
+
+            // Step 3. In an RTL label, the end of the label must be a character with Bidi property
+            // R, AL, EN, or AN, followed by zero or more characters with Bidi property NSM.
+            if (1 !== preg_match(Regex::BIDI_STEP_3, $label)) {
+                $info->validBidiDomain = false;
+
+                return;
+            }
+
+            // Step 4. In an RTL label, if an EN is present, no AN may be present, and vice versa.
+            if (1 === preg_match(Regex::BIDI_STEP_4_AN, $label) && 1 === preg_match(Regex::BIDI_STEP_4_EN, $label)) {
+                $info->validBidiDomain = false;
+
+                return;
+            }
+
+            return;
+        }
+
+        // We are a LTR label
+        // Step 1. The first character must be a character with Bidi property L, R, or AL.
+        // If it has the L property, it is an LTR label.
+        if (1 !== preg_match(Regex::BIDI_STEP_1_LTR, $label)) {
+            $info->validBidiDomain = false;
+
+            return;
+        }
+
+        // Step 5. In an LTR label, only characters with the Bidi properties L, EN,
+        // ES, CS, ET, ON, BN, or NSM are allowed.
+        if (1 === preg_match(Regex::BIDI_STEP_5, $label)) {
+            $info->validBidiDomain = false;
+
+            return;
+        }
+
+        // Step 6.In an LTR label, the end of the label must be a character with Bidi property L or
+        // EN, followed by zero or more characters with Bidi property NSM.
+        if (1 !== preg_match(Regex::BIDI_STEP_6, $label)) {
+            $info->validBidiDomain = false;
+
+            return;
+        }
+    }
+
+    /**
+     * @param array<int, string> $labels
+     */
+    private static function validateDomainAndLabelLength(array $labels, Info $info)
+    {
+        $maxDomainSize = self::MAX_DOMAIN_SIZE;
+        $length = \count($labels);
+
+        // Number of "." delimiters.
+        $domainLength = $length - 1;
+
+        // If the last label is empty and it is not the first label, then it is the root label.
+        // Increase the max size by 1, making it 254, to account for the root label's "."
+        // delimiter. This also means we don't need to check the last label's length for being too
+        // long.
+        if ($length > 1 && '' === $labels[$length - 1]) {
+            ++$maxDomainSize;
+            --$length;
+        }
+
+        for ($i = 0; $i < $length; ++$i) {
+            $bytes = \strlen($labels[$i]);
+            $domainLength += $bytes;
+
+            if ($bytes > self::MAX_LABEL_SIZE) {
+                $info->errors |= self::ERROR_LABEL_TOO_LONG;
+            }
+        }
+
+        if ($domainLength > $maxDomainSize) {
+            $info->errors |= self::ERROR_DOMAIN_NAME_TOO_LONG;
+        }
+    }
+
+    /**
+     * @see https://www.unicode.org/reports/tr46/#Validity_Criteria
+     *
+     * @param string              $label
+     * @param array<string, bool> $options
+     * @param bool                $canBeEmpty
+     */
+    private static function validateLabel($label, Info $info, array $options, $canBeEmpty)
+    {
+        if ('' === $label) {
+            if (!$canBeEmpty && (!isset($options['VerifyDnsLength']) || $options['VerifyDnsLength'])) {
+                $info->errors |= self::ERROR_EMPTY_LABEL;
+            }
+
+            return;
+        }
+
+        // Step 1. The label must be in Unicode Normalization Form C.
+        if (!Normalizer::isNormalized($label, Normalizer::FORM_C)) {
+            $info->errors |= self::ERROR_INVALID_ACE_LABEL;
+        }
+
+        $codePoints = self::utf8Decode($label);
+
+        if ($options['CheckHyphens']) {
+            // Step 2. If CheckHyphens, the label must not contain a U+002D HYPHEN-MINUS character
+            // in both the thrid and fourth positions.
+            if (isset($codePoints[2], $codePoints[3]) && 0x002D === $codePoints[2] && 0x002D === $codePoints[3]) {
+                $info->errors |= self::ERROR_HYPHEN_3_4;
+            }
+
+            // Step 3. If CheckHyphens, the label must neither begin nor end with a U+002D
+            // HYPHEN-MINUS character.
+            if ('-' === substr($label, 0, 1)) {
+                $info->errors |= self::ERROR_LEADING_HYPHEN;
+            }
+
+            if ('-' === substr($label, -1, 1)) {
+                $info->errors |= self::ERROR_TRAILING_HYPHEN;
+            }
+        }
+
+        // Step 4. The label must not contain a U+002E (.) FULL STOP.
+        if (false !== strpos($label, '.')) {
+            $info->errors |= self::ERROR_LABEL_HAS_DOT;
+        }
+
+        // Step 5. The label must not begin with a combining mark, that is: General_Category=Mark.
+        if (1 === preg_match(Regex::COMBINING_MARK, $label)) {
+            $info->errors |= self::ERROR_LEADING_COMBINING_MARK;
+        }
+
+        // Step 6. Each code point in the label must only have certain status values according to
+        // Section 5, IDNA Mapping Table:
+        $transitional = $options['Transitional_Processing'];
+        $useSTD3ASCIIRules = $options['UseSTD3ASCIIRules'];
+
+        foreach ($codePoints as $codePoint) {
+            $data = self::lookupCodePointStatus($codePoint, $useSTD3ASCIIRules);
+            $status = $data['status'];
+
+            if ('valid' === $status || (!$transitional && 'deviation' === $status)) {
+                continue;
+            }
+
+            $info->errors |= self::ERROR_DISALLOWED;
+
+            break;
+        }
+
+        // Step 7. If CheckJoiners, the label must satisify the ContextJ rules from Appendix A, in
+        // The Unicode Code Points and Internationalized Domain Names for Applications (IDNA)
+        // [IDNA2008].
+        if ($options['CheckJoiners'] && !self::isValidContextJ($codePoints, $label)) {
+            $info->errors |= self::ERROR_CONTEXTJ;
+        }
+
+        // Step 8. If CheckBidi, and if the domain name is a  Bidi domain name, then the label must
+        // satisfy all six of the numbered conditions in [IDNA2008] RFC 5893, Section 2.
+        if ($options['CheckBidi'] && (!$info->bidiDomain || $info->validBidiDomain)) {
+            self::validateBidiLabel($label, $info);
+        }
+    }
+
+    /**
+     * @see https://tools.ietf.org/html/rfc3492#section-6.2
+     *
+     * @param string $input
+     *
+     * @return string
+     */
+    private static function punycodeDecode($input)
+    {
+        $n = self::INITIAL_N;
+        $out = 0;
+        $i = 0;
+        $bias = self::INITIAL_BIAS;
+        $lastDelimIndex = strrpos($input, self::DELIMITER);
+        $b = false === $lastDelimIndex ? 0 : $lastDelimIndex;
+        $inputLength = \strlen($input);
+        $output = array();
+        $bytes = array_map('ord', str_split($input));
+
+        for ($j = 0; $j < $b; ++$j) {
+            if ($bytes[$j] > 0x7F) {
+                throw new Exception('Invalid input');
+            }
+
+            $output[$out++] = $input[$j];
+        }
+
+        if ($b > 0) {
+            ++$b;
+        }
+
+        for ($in = $b; $in < $inputLength; ++$out) {
+            $oldi = $i;
+            $w = 1;
+
+            for ($k = self::BASE; /* no condition */; $k += self::BASE) {
+                if ($in >= $inputLength) {
+                    throw new Exception('Invalid input');
+                }
+
+                $digit = self::$basicToDigit[$bytes[$in++] & 0xFF];
+
+                if ($digit < 0) {
+                    throw new Exception('Invalid input');
+                }
+
+                if ($digit > intdiv(self::MAX_INT - $i, $w)) {
+                    throw new Exception('Integer overflow');
+                }
+
+                $i += $digit * $w;
+
+                if ($k <= $bias) {
+                    $t = self::TMIN;
+                } elseif ($k >= $bias + self::TMAX) {
+                    $t = self::TMAX;
+                } else {
+                    $t = $k - $bias;
+                }
+
+                if ($digit < $t) {
+                    break;
+                }
+
+                $baseMinusT = self::BASE - $t;
+
+                if ($w > intdiv(self::MAX_INT, $baseMinusT)) {
+                    throw new Exception('Integer overflow');
+                }
+
+                $w *= $baseMinusT;
+            }
+
+            $outPlusOne = $out + 1;
+            $bias = self::adaptBias($i - $oldi, $outPlusOne, 0 === $oldi);
+
+            if (intdiv($i, $outPlusOne) > self::MAX_INT - $n) {
+                throw new Exception('Integer overflow');
+            }
+
+            $n += intdiv($i, $outPlusOne);
+            $i %= $outPlusOne;
+            array_splice($output, $i++, 0, array(mb_chr($n, 'utf-8')));
+        }
+
+        return implode('', $output);
+    }
+
+    /**
+     * @see https://tools.ietf.org/html/rfc3492#section-6.3
+     *
+     * @param string $input
+     *
+     * @return string
+     */
+    private static function punycodeEncode($input)
+    {
+        $n = self::INITIAL_N;
+        $delta = 0;
+        $out = 0;
+        $bias = self::INITIAL_BIAS;
+        $inputLength = 0;
+        $output = '';
+        $iter = self::utf8Decode($input);
+
+        foreach ($iter as $codePoint) {
+            ++$inputLength;
+
+            if ($codePoint < 0x80) {
+                $output .= \chr($codePoint);
+                ++$out;
+            }
+        }
+
+        $h = $out;
+        $b = $out;
+
+        if ($b > 0) {
+            $output .= self::DELIMITER;
+            ++$out;
+        }
+
+        while ($h < $inputLength) {
+            $m = self::MAX_INT;
+
+            foreach ($iter as $codePoint) {
+                if ($codePoint >= $n && $codePoint < $m) {
+                    $m = $codePoint;
+                }
+            }
+
+            if ($m - $n > intdiv(self::MAX_INT - $delta, $h + 1)) {
+                throw new Exception('Integer overflow');
+            }
+
+            $delta += ($m - $n) * ($h + 1);
+            $n = $m;
+
+            foreach ($iter as $codePoint) {
+                if ($codePoint < $n && 0 === ++$delta) {
+                    throw new Exception('Integer overflow');
+                } elseif ($codePoint === $n) {
+                    $q = $delta;
+
+                    for ($k = self::BASE; /* no condition */; $k += self::BASE) {
+                        if ($k <= $bias) {
+                            $t = self::TMIN;
+                        } elseif ($k >= $bias + self::TMAX) {
+                            $t = self::TMAX;
+                        } else {
+                            $t = $k - $bias;
+                        }
+
+                        if ($q < $t) {
+                            break;
+                        }
+
+                        $qMinusT = $q - $t;
+                        $baseMinusT = self::BASE - $t;
+                        $output .= self::encodeDigit($t + ($qMinusT) % ($baseMinusT), false);
+                        ++$out;
+                        $q = intdiv($qMinusT, $baseMinusT);
+                    }
+
+                    $output .= self::encodeDigit($q, false);
+                    ++$out;
+                    $bias = self::adaptBias($delta, $h + 1, $h === $b);
+                    $delta = 0;
+                    ++$h;
+                }
+            }
+
+            ++$delta;
+            ++$n;
+        }
+
+        return $output;
+    }
+
+    /**
+     * @see https://tools.ietf.org/html/rfc3492#section-6.1
+     *
+     * @param int  $delta
+     * @param int  $numPoints
+     * @param bool $firstTime
+     *
+     * @return int
+     */
+    private static function adaptBias($delta, $numPoints, $firstTime)
+    {
+        // xxx >> 1 is a faster way of doing intdiv(xxx, 2)
+        $delta = $firstTime ? intdiv($delta, self::DAMP) : $delta >> 1;
+        $delta += intdiv($delta, $numPoints);
+        $k = 0;
+
+        while ($delta > ((self::BASE - self::TMIN) * self::TMAX) >> 1) {
+            $delta = intdiv($delta, self::BASE - self::TMIN);
+            $k += self::BASE;
+        }
+
+        return $k + intdiv((self::BASE - self::TMIN + 1) * $delta, $delta + self::SKEW);
+    }
+
+    /**
+     * @param int  $d
+     * @param bool $flag
+     *
+     * @return string
+     */
+    private static function encodeDigit($d, $flag)
+    {
+        return \chr($d + 22 + 75 * ($d < 26 ? 1 : 0) - (($flag ? 1 : 0) << 5));
+    }
+
+    /**
+     * Takes a UTF-8 encoded string and converts it into a series of integer code points. Any
+     * invalid byte sequences will be replaced by a U+FFFD replacement code point.
+     *
+     * @see https://encoding.spec.whatwg.org/#utf-8-decoder
+     *
+     * @param string $input
+     *
+     * @return array<int, int>
+     */
+    private static function utf8Decode($input)
+    {
+        $bytesSeen = 0;
+        $bytesNeeded = 0;
+        $lowerBoundary = 0x80;
+        $upperBoundary = 0xBF;
+        $codePoint = 0;
+        $codePoints = array();
+        $length = \strlen($input);
+
+        for ($i = 0; $i < $length; ++$i) {
+            $byte = \ord($input[$i]);
+
+            if (0 === $bytesNeeded) {
+                if ($byte >= 0x00 && $byte <= 0x7F) {
+                    $codePoints[] = $byte;
+
+                    continue;
+                }
+
+                if ($byte >= 0xC2 && $byte <= 0xDF) {
+                    $bytesNeeded = 1;
+                    $codePoint = $byte & 0x1F;
+                } elseif ($byte >= 0xE0 && $byte <= 0xEF) {
+                    if (0xE0 === $byte) {
+                        $lowerBoundary = 0xA0;
+                    } elseif (0xED === $byte) {
+                        $upperBoundary = 0x9F;
+                    }
+
+                    $bytesNeeded = 2;
+                    $codePoint = $byte & 0xF;
+                } elseif ($byte >= 0xF0 && $byte <= 0xF4) {
+                    if (0xF0 === $byte) {
+                        $lowerBoundary = 0x90;
+                    } elseif (0xF4 === $byte) {
+                        $upperBoundary = 0x8F;
+                    }
+
+                    $bytesNeeded = 3;
+                    $codePoint = $byte & 0x7;
+                } else {
+                    $codePoints[] = 0xFFFD;
+                }
+
+                continue;
+            }
+
+            if ($byte < $lowerBoundary || $byte > $upperBoundary) {
+                $codePoint = 0;
+                $bytesNeeded = 0;
+                $bytesSeen = 0;
+                $lowerBoundary = 0x80;
+                $upperBoundary = 0xBF;
+                --$i;
+                $codePoints[] = 0xFFFD;
+
+                continue;
+            }
+
+            $lowerBoundary = 0x80;
+            $upperBoundary = 0xBF;
+            $codePoint = ($codePoint << 6) | ($byte & 0x3F);
+
+            if (++$bytesSeen !== $bytesNeeded) {
+                continue;
+            }
+
+            $codePoints[] = $codePoint;
+            $codePoint = 0;
+            $bytesNeeded = 0;
+            $bytesSeen = 0;
+        }
+
+        // String unexpectedly ended, so append a U+FFFD code point.
+        if (0 !== $bytesNeeded) {
+            $codePoints[] = 0xFFFD;
+        }
+
+        return $codePoints;
+    }
+
+    /**
+     * @param int  $codePoint
+     * @param bool $useSTD3ASCIIRules
+     *
+     * @return array{status: string, mapping?: string}
+     */
+    private static function lookupCodePointStatus($codePoint, $useSTD3ASCIIRules)
+    {
+        if (!self::$mappingTableLoaded) {
+            self::$mappingTableLoaded = true;
+            self::$mapped = require __DIR__.'/Resources/unidata/mapped.php';
+            self::$ignored = require __DIR__.'/Resources/unidata/ignored.php';
+            self::$deviation = require __DIR__.'/Resources/unidata/deviation.php';
+            self::$disallowed = require __DIR__.'/Resources/unidata/disallowed.php';
+            self::$disallowed_STD3_mapped = require __DIR__.'/Resources/unidata/disallowed_STD3_mapped.php';
+            self::$disallowed_STD3_valid = require __DIR__.'/Resources/unidata/disallowed_STD3_valid.php';
+        }
+
+        if (isset(self::$mapped[$codePoint])) {
+            return array('status' => 'mapped', 'mapping' => self::$mapped[$codePoint]);
+        }
+
+        if (isset(self::$ignored[$codePoint])) {
+            return array('status' => 'ignored');
+        }
+
+        if (isset(self::$deviation[$codePoint])) {
+            return array('status' => 'deviation', 'mapping' => self::$deviation[$codePoint]);
+        }
+
+        if (isset(self::$disallowed[$codePoint]) || DisallowedRanges::inRange($codePoint)) {
+            return array('status' => 'disallowed');
+        }
+
+        $isDisallowedMapped = isset(self::$disallowed_STD3_mapped[$codePoint]);
+
+        if ($isDisallowedMapped || isset(self::$disallowed_STD3_valid[$codePoint])) {
+            $status = 'disallowed';
+
+            if (!$useSTD3ASCIIRules) {
+                $status = $isDisallowedMapped ? 'mapped' : 'valid';
+            }
+
+            if ($isDisallowedMapped) {
+                return array('status' => $status, 'mapping' => self::$disallowed_STD3_mapped[$codePoint]);
+            }
+
+            return array('status' => $status);
+        }
+
+        return array('status' => 'valid');
+    }
+}
diff --git a/vendor/symfony/polyfill-intl-idn/Info.php b/vendor/symfony/polyfill-intl-idn/Info.php
new file mode 100755
index 0000000..25c3582
--- /dev/null
+++ b/vendor/symfony/polyfill-intl-idn/Info.php
@@ -0,0 +1,23 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com> and Trevor Rowbotham <trevor.rowbotham@pm.me>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Polyfill\Intl\Idn;
+
+/**
+ * @internal
+ */
+class Info
+{
+    public $bidiDomain = false;
+    public $errors = 0;
+    public $validBidiDomain = true;
+    public $transitionalDifferent = false;
+}
diff --git a/vendor/symfony/polyfill-intl-idn/LICENSE b/vendor/symfony/polyfill-intl-idn/LICENSE
new file mode 100755
index 0000000..03c5e25
--- /dev/null
+++ b/vendor/symfony/polyfill-intl-idn/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2018-2019 Fabien Potencier and Trevor Rowbotham <trevor.rowbotham@pm.me>
+
+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.
diff --git a/vendor/symfony/polyfill-intl-idn/README.md b/vendor/symfony/polyfill-intl-idn/README.md
new file mode 100755
index 0000000..2e75f2e
--- /dev/null
+++ b/vendor/symfony/polyfill-intl-idn/README.md
@@ -0,0 +1,12 @@
+Symfony Polyfill / Intl: Idn
+============================
+
+This component provides [`idn_to_ascii`](https://php.net/idn-to-ascii) and [`idn_to_utf8`](https://php.net/idn-to-utf8) functions to users who run php versions without the [Intl](https://php.net/intl) extension.
+
+More information can be found in the
+[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md).
+
+License
+=======
+
+This library is released under the [MIT license](LICENSE).
diff --git a/vendor/symfony/polyfill-intl-idn/Resources/unidata/DisallowedRanges.php b/vendor/symfony/polyfill-intl-idn/Resources/unidata/DisallowedRanges.php
new file mode 100755
index 0000000..5bb70e4
--- /dev/null
+++ b/vendor/symfony/polyfill-intl-idn/Resources/unidata/DisallowedRanges.php
@@ -0,0 +1,375 @@
+<?php
+
+namespace Symfony\Polyfill\Intl\Idn\Resources\unidata;
+
+/**
+ * @internal
+ */
+final class DisallowedRanges
+{
+    /**
+     * @param int $codePoint
+     *
+     * @return bool
+     */
+    public static function inRange($codePoint)
+    {
+        if ($codePoint >= 128 && $codePoint <= 159) {
+            return true;
+        }
+
+        if ($codePoint >= 2155 && $codePoint <= 2207) {
+            return true;
+        }
+
+        if ($codePoint >= 3676 && $codePoint <= 3712) {
+            return true;
+        }
+
+        if ($codePoint >= 3808 && $codePoint <= 3839) {
+            return true;
+        }
+
+        if ($codePoint >= 4059 && $codePoint <= 4095) {
+            return true;
+        }
+
+        if ($codePoint >= 4256 && $codePoint <= 4293) {
+            return true;
+        }
+
+        if ($codePoint >= 6849 && $codePoint <= 6911) {
+            return true;
+        }
+
+        if ($codePoint >= 11859 && $codePoint <= 11903) {
+            return true;
+        }
+
+        if ($codePoint >= 42955 && $codePoint <= 42996) {
+            return true;
+        }
+
+        if ($codePoint >= 55296 && $codePoint <= 57343) {
+            return true;
+        }
+
+        if ($codePoint >= 57344 && $codePoint <= 63743) {
+            return true;
+        }
+
+        if ($codePoint >= 64218 && $codePoint <= 64255) {
+            return true;
+        }
+
+        if ($codePoint >= 64976 && $codePoint <= 65007) {
+            return true;
+        }
+
+        if ($codePoint >= 65630 && $codePoint <= 65663) {
+            return true;
+        }
+
+        if ($codePoint >= 65953 && $codePoint <= 65999) {
+            return true;
+        }
+
+        if ($codePoint >= 66046 && $codePoint <= 66175) {
+            return true;
+        }
+
+        if ($codePoint >= 66518 && $codePoint <= 66559) {
+            return true;
+        }
+
+        if ($codePoint >= 66928 && $codePoint <= 67071) {
+            return true;
+        }
+
+        if ($codePoint >= 67432 && $codePoint <= 67583) {
+            return true;
+        }
+
+        if ($codePoint >= 67760 && $codePoint <= 67807) {
+            return true;
+        }
+
+        if ($codePoint >= 67904 && $codePoint <= 67967) {
+            return true;
+        }
+
+        if ($codePoint >= 68256 && $codePoint <= 68287) {
+            return true;
+        }
+
+        if ($codePoint >= 68528 && $codePoint <= 68607) {
+            return true;
+        }
+
+        if ($codePoint >= 68681 && $codePoint <= 68735) {
+            return true;
+        }
+
+        if ($codePoint >= 68922 && $codePoint <= 69215) {
+            return true;
+        }
+
+        if ($codePoint >= 69298 && $codePoint <= 69375) {
+            return true;
+        }
+
+        if ($codePoint >= 69466 && $codePoint <= 69551) {
+            return true;
+        }
+
+        if ($codePoint >= 70207 && $codePoint <= 70271) {
+            return true;
+        }
+
+        if ($codePoint >= 70517 && $codePoint <= 70655) {
+            return true;
+        }
+
+        if ($codePoint >= 70874 && $codePoint <= 71039) {
+            return true;
+        }
+
+        if ($codePoint >= 71134 && $codePoint <= 71167) {
+            return true;
+        }
+
+        if ($codePoint >= 71370 && $codePoint <= 71423) {
+            return true;
+        }
+
+        if ($codePoint >= 71488 && $codePoint <= 71679) {
+            return true;
+        }
+
+        if ($codePoint >= 71740 && $codePoint <= 71839) {
+            return true;
+        }
+
+        if ($codePoint >= 72026 && $codePoint <= 72095) {
+            return true;
+        }
+
+        if ($codePoint >= 72441 && $codePoint <= 72703) {
+            return true;
+        }
+
+        if ($codePoint >= 72887 && $codePoint <= 72959) {
+            return true;
+        }
+
+        if ($codePoint >= 73130 && $codePoint <= 73439) {
+            return true;
+        }
+
+        if ($codePoint >= 73465 && $codePoint <= 73647) {
+            return true;
+        }
+
+        if ($codePoint >= 74650 && $codePoint <= 74751) {
+            return true;
+        }
+
+        if ($codePoint >= 75076 && $codePoint <= 77823) {
+            return true;
+        }
+
+        if ($codePoint >= 78905 && $codePoint <= 82943) {
+            return true;
+        }
+
+        if ($codePoint >= 83527 && $codePoint <= 92159) {
+            return true;
+        }
+
+        if ($codePoint >= 92784 && $codePoint <= 92879) {
+            return true;
+        }
+
+        if ($codePoint >= 93072 && $codePoint <= 93759) {
+            return true;
+        }
+
+        if ($codePoint >= 93851 && $codePoint <= 93951) {
+            return true;
+        }
+
+        if ($codePoint >= 94112 && $codePoint <= 94175) {
+            return true;
+        }
+
+        if ($codePoint >= 101590 && $codePoint <= 101631) {
+            return true;
+        }
+
+        if ($codePoint >= 101641 && $codePoint <= 110591) {
+            return true;
+        }
+
+        if ($codePoint >= 110879 && $codePoint <= 110927) {
+            return true;
+        }
+
+        if ($codePoint >= 111356 && $codePoint <= 113663) {
+            return true;
+        }
+
+        if ($codePoint >= 113828 && $codePoint <= 118783) {
+            return true;
+        }
+
+        if ($codePoint >= 119366 && $codePoint <= 119519) {
+            return true;
+        }
+
+        if ($codePoint >= 119673 && $codePoint <= 119807) {
+            return true;
+        }
+
+        if ($codePoint >= 121520 && $codePoint <= 122879) {
+            return true;
+        }
+
+        if ($codePoint >= 122923 && $codePoint <= 123135) {
+            return true;
+        }
+
+        if ($codePoint >= 123216 && $codePoint <= 123583) {
+            return true;
+        }
+
+        if ($codePoint >= 123648 && $codePoint <= 124927) {
+            return true;
+        }
+
+        if ($codePoint >= 125143 && $codePoint <= 125183) {
+            return true;
+        }
+
+        if ($codePoint >= 125280 && $codePoint <= 126064) {
+            return true;
+        }
+
+        if ($codePoint >= 126133 && $codePoint <= 126208) {
+            return true;
+        }
+
+        if ($codePoint >= 126270 && $codePoint <= 126463) {
+            return true;
+        }
+
+        if ($codePoint >= 126652 && $codePoint <= 126703) {
+            return true;
+        }
+
+        if ($codePoint >= 126706 && $codePoint <= 126975) {
+            return true;
+        }
+
+        if ($codePoint >= 127406 && $codePoint <= 127461) {
+            return true;
+        }
+
+        if ($codePoint >= 127590 && $codePoint <= 127743) {
+            return true;
+        }
+
+        if ($codePoint >= 129202 && $codePoint <= 129279) {
+            return true;
+        }
+
+        if ($codePoint >= 129751 && $codePoint <= 129791) {
+            return true;
+        }
+
+        if ($codePoint >= 129995 && $codePoint <= 130031) {
+            return true;
+        }
+
+        if ($codePoint >= 130042 && $codePoint <= 131069) {
+            return true;
+        }
+
+        if ($codePoint >= 173790 && $codePoint <= 173823) {
+            return true;
+        }
+
+        if ($codePoint >= 191457 && $codePoint <= 194559) {
+            return true;
+        }
+
+        if ($codePoint >= 195102 && $codePoint <= 196605) {
+            return true;
+        }
+
+        if ($codePoint >= 201547 && $codePoint <= 262141) {
+            return true;
+        }
+
+        if ($codePoint >= 262144 && $codePoint <= 327677) {
+            return true;
+        }
+
+        if ($codePoint >= 327680 && $codePoint <= 393213) {
+            return true;
+        }
+
+        if ($codePoint >= 393216 && $codePoint <= 458749) {
+            return true;
+        }
+
+        if ($codePoint >= 458752 && $codePoint <= 524285) {
+            return true;
+        }
+
+        if ($codePoint >= 524288 && $codePoint <= 589821) {
+            return true;
+        }
+
+        if ($codePoint >= 589824 && $codePoint <= 655357) {
+            return true;
+        }
+
+        if ($codePoint >= 655360 && $codePoint <= 720893) {
+            return true;
+        }
+
+        if ($codePoint >= 720896 && $codePoint <= 786429) {
+            return true;
+        }
+
+        if ($codePoint >= 786432 && $codePoint <= 851965) {
+            return true;
+        }
+
+        if ($codePoint >= 851968 && $codePoint <= 917501) {
+            return true;
+        }
+
+        if ($codePoint >= 917536 && $codePoint <= 917631) {
+            return true;
+        }
+
+        if ($codePoint >= 917632 && $codePoint <= 917759) {
+            return true;
+        }
+
+        if ($codePoint >= 918000 && $codePoint <= 983037) {
+            return true;
+        }
+
+        if ($codePoint >= 983040 && $codePoint <= 1048573) {
+            return true;
+        }
+
+        if ($codePoint >= 1048576 && $codePoint <= 1114109) {
+            return true;
+        }
+
+        return false;
+    }
+}
diff --git a/vendor/symfony/polyfill-intl-idn/Resources/unidata/Regex.php b/vendor/symfony/polyfill-intl-idn/Resources/unidata/Regex.php
new file mode 100755
index 0000000..5c1c51d
--- /dev/null
+++ b/vendor/symfony/polyfill-intl-idn/Resources/unidata/Regex.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Symfony\Polyfill\Intl\Idn\Resources\unidata;
+
+/**
+ * @internal
+ */
+final class Regex
+{
+    const COMBINING_MARK = '/^[\x{0300}-\x{036F}\x{0483}-\x{0487}\x{0488}-\x{0489}\x{0591}-\x{05BD}\x{05BF}\x{05C1}-\x{05C2}\x{05C4}-\x{05C5}\x{05C7}\x{0610}-\x{061A}\x{064B}-\x{065F}\x{0670}\x{06D6}-\x{06DC}\x{06DF}-\x{06E4}\x{06E7}-\x{06E8}\x{06EA}-\x{06ED}\x{0711}\x{0730}-\x{074A}\x{07A6}-\x{07B0}\x{07EB}-\x{07F3}\x{07FD}\x{0816}-\x{0819}\x{081B}-\x{0823}\x{0825}-\x{0827}\x{0829}-\x{082D}\x{0859}-\x{085B}\x{08D3}-\x{08E1}\x{08E3}-\x{0902}\x{0903}\x{093A}\x{093B}\x{093C}\x{093E}-\x{0940}\x{0941}-\x{0948}\x{0949}-\x{094C}\x{094D}\x{094E}-\x{094F}\x{0951}-\x{0957}\x{0962}-\x{0963}\x{0981}\x{0982}-\x{0983}\x{09BC}\x{09BE}-\x{09C0}\x{09C1}-\x{09C4}\x{09C7}-\x{09C8}\x{09CB}-\x{09CC}\x{09CD}\x{09D7}\x{09E2}-\x{09E3}\x{09FE}\x{0A01}-\x{0A02}\x{0A03}\x{0A3C}\x{0A3E}-\x{0A40}\x{0A41}-\x{0A42}\x{0A47}-\x{0A48}\x{0A4B}-\x{0A4D}\x{0A51}\x{0A70}-\x{0A71}\x{0A75}\x{0A81}-\x{0A82}\x{0A83}\x{0ABC}\x{0ABE}-\x{0AC0}\x{0AC1}-\x{0AC5}\x{0AC7}-\x{0AC8}\x{0AC9}\x{0ACB}-\x{0ACC}\x{0ACD}\x{0AE2}-\x{0AE3}\x{0AFA}-\x{0AFF}\x{0B01}\x{0B02}-\x{0B03}\x{0B3C}\x{0B3E}\x{0B3F}\x{0B40}\x{0B41}-\x{0B44}\x{0B47}-\x{0B48}\x{0B4B}-\x{0B4C}\x{0B4D}\x{0B55}-\x{0B56}\x{0B57}\x{0B62}-\x{0B63}\x{0B82}\x{0BBE}-\x{0BBF}\x{0BC0}\x{0BC1}-\x{0BC2}\x{0BC6}-\x{0BC8}\x{0BCA}-\x{0BCC}\x{0BCD}\x{0BD7}\x{0C00}\x{0C01}-\x{0C03}\x{0C04}\x{0C3E}-\x{0C40}\x{0C41}-\x{0C44}\x{0C46}-\x{0C48}\x{0C4A}-\x{0C4D}\x{0C55}-\x{0C56}\x{0C62}-\x{0C63}\x{0C81}\x{0C82}-\x{0C83}\x{0CBC}\x{0CBE}\x{0CBF}\x{0CC0}-\x{0CC4}\x{0CC6}\x{0CC7}-\x{0CC8}\x{0CCA}-\x{0CCB}\x{0CCC}-\x{0CCD}\x{0CD5}-\x{0CD6}\x{0CE2}-\x{0CE3}\x{0D00}-\x{0D01}\x{0D02}-\x{0D03}\x{0D3B}-\x{0D3C}\x{0D3E}-\x{0D40}\x{0D41}-\x{0D44}\x{0D46}-\x{0D48}\x{0D4A}-\x{0D4C}\x{0D4D}\x{0D57}\x{0D62}-\x{0D63}\x{0D81}\x{0D82}-\x{0D83}\x{0DCA}\x{0DCF}-\x{0DD1}\x{0DD2}-\x{0DD4}\x{0DD6}\x{0DD8}-\x{0DDF}\x{0DF2}-\x{0DF3}\x{0E31}\x{0E34}-\x{0E3A}\x{0E47}-\x{0E4E}\x{0EB1}\x{0EB4}-\x{0EBC}\x{0EC8}-\x{0ECD}\x{0F18}-\x{0F19}\x{0F35}\x{0F37}\x{0F39}\x{0F3E}-\x{0F3F}\x{0F71}-\x{0F7E}\x{0F7F}\x{0F80}-\x{0F84}\x{0F86}-\x{0F87}\x{0F8D}-\x{0F97}\x{0F99}-\x{0FBC}\x{0FC6}\x{102B}-\x{102C}\x{102D}-\x{1030}\x{1031}\x{1032}-\x{1037}\x{1038}\x{1039}-\x{103A}\x{103B}-\x{103C}\x{103D}-\x{103E}\x{1056}-\x{1057}\x{1058}-\x{1059}\x{105E}-\x{1060}\x{1062}-\x{1064}\x{1067}-\x{106D}\x{1071}-\x{1074}\x{1082}\x{1083}-\x{1084}\x{1085}-\x{1086}\x{1087}-\x{108C}\x{108D}\x{108F}\x{109A}-\x{109C}\x{109D}\x{135D}-\x{135F}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}-\x{1753}\x{1772}-\x{1773}\x{17B4}-\x{17B5}\x{17B6}\x{17B7}-\x{17BD}\x{17BE}-\x{17C5}\x{17C6}\x{17C7}-\x{17C8}\x{17C9}-\x{17D3}\x{17DD}\x{180B}-\x{180D}\x{1885}-\x{1886}\x{18A9}\x{1920}-\x{1922}\x{1923}-\x{1926}\x{1927}-\x{1928}\x{1929}-\x{192B}\x{1930}-\x{1931}\x{1932}\x{1933}-\x{1938}\x{1939}-\x{193B}\x{1A17}-\x{1A18}\x{1A19}-\x{1A1A}\x{1A1B}\x{1A55}\x{1A56}\x{1A57}\x{1A58}-\x{1A5E}\x{1A60}\x{1A61}\x{1A62}\x{1A63}-\x{1A64}\x{1A65}-\x{1A6C}\x{1A6D}-\x{1A72}\x{1A73}-\x{1A7C}\x{1A7F}\x{1AB0}-\x{1ABD}\x{1ABE}\x{1ABF}-\x{1AC0}\x{1B00}-\x{1B03}\x{1B04}\x{1B34}\x{1B35}\x{1B36}-\x{1B3A}\x{1B3B}\x{1B3C}\x{1B3D}-\x{1B41}\x{1B42}\x{1B43}-\x{1B44}\x{1B6B}-\x{1B73}\x{1B80}-\x{1B81}\x{1B82}\x{1BA1}\x{1BA2}-\x{1BA5}\x{1BA6}-\x{1BA7}\x{1BA8}-\x{1BA9}\x{1BAA}\x{1BAB}-\x{1BAD}\x{1BE6}\x{1BE7}\x{1BE8}-\x{1BE9}\x{1BEA}-\x{1BEC}\x{1BED}\x{1BEE}\x{1BEF}-\x{1BF1}\x{1BF2}-\x{1BF3}\x{1C24}-\x{1C2B}\x{1C2C}-\x{1C33}\x{1C34}-\x{1C35}\x{1C36}-\x{1C37}\x{1CD0}-\x{1CD2}\x{1CD4}-\x{1CE0}\x{1CE1}\x{1CE2}-\x{1CE8}\x{1CED}\x{1CF4}\x{1CF7}\x{1CF8}-\x{1CF9}\x{1DC0}-\x{1DF9}\x{1DFB}-\x{1DFF}\x{20D0}-\x{20DC}\x{20DD}-\x{20E0}\x{20E1}\x{20E2}-\x{20E4}\x{20E5}-\x{20F0}\x{2CEF}-\x{2CF1}\x{2D7F}\x{2DE0}-\x{2DFF}\x{302A}-\x{302D}\x{302E}-\x{302F}\x{3099}-\x{309A}\x{A66F}\x{A670}-\x{A672}\x{A674}-\x{A67D}\x{A69E}-\x{A69F}\x{A6F0}-\x{A6F1}\x{A802}\x{A806}\x{A80B}\x{A823}-\x{A824}\x{A825}-\x{A826}\x{A827}\x{A82C}\x{A880}-\x{A881}\x{A8B4}-\x{A8C3}\x{A8C4}-\x{A8C5}\x{A8E0}-\x{A8F1}\x{A8FF}\x{A926}-\x{A92D}\x{A947}-\x{A951}\x{A952}-\x{A953}\x{A980}-\x{A982}\x{A983}\x{A9B3}\x{A9B4}-\x{A9B5}\x{A9B6}-\x{A9B9}\x{A9BA}-\x{A9BB}\x{A9BC}-\x{A9BD}\x{A9BE}-\x{A9C0}\x{A9E5}\x{AA29}-\x{AA2E}\x{AA2F}-\x{AA30}\x{AA31}-\x{AA32}\x{AA33}-\x{AA34}\x{AA35}-\x{AA36}\x{AA43}\x{AA4C}\x{AA4D}\x{AA7B}\x{AA7C}\x{AA7D}\x{AAB0}\x{AAB2}-\x{AAB4}\x{AAB7}-\x{AAB8}\x{AABE}-\x{AABF}\x{AAC1}\x{AAEB}\x{AAEC}-\x{AAED}\x{AAEE}-\x{AAEF}\x{AAF5}\x{AAF6}\x{ABE3}-\x{ABE4}\x{ABE5}\x{ABE6}-\x{ABE7}\x{ABE8}\x{ABE9}-\x{ABEA}\x{ABEC}\x{ABED}\x{FB1E}\x{FE00}-\x{FE0F}\x{FE20}-\x{FE2F}\x{101FD}\x{102E0}\x{10376}-\x{1037A}\x{10A01}-\x{10A03}\x{10A05}-\x{10A06}\x{10A0C}-\x{10A0F}\x{10A38}-\x{10A3A}\x{10A3F}\x{10AE5}-\x{10AE6}\x{10D24}-\x{10D27}\x{10EAB}-\x{10EAC}\x{10F46}-\x{10F50}\x{11000}\x{11001}\x{11002}\x{11038}-\x{11046}\x{1107F}-\x{11081}\x{11082}\x{110B0}-\x{110B2}\x{110B3}-\x{110B6}\x{110B7}-\x{110B8}\x{110B9}-\x{110BA}\x{11100}-\x{11102}\x{11127}-\x{1112B}\x{1112C}\x{1112D}-\x{11134}\x{11145}-\x{11146}\x{11173}\x{11180}-\x{11181}\x{11182}\x{111B3}-\x{111B5}\x{111B6}-\x{111BE}\x{111BF}-\x{111C0}\x{111C9}-\x{111CC}\x{111CE}\x{111CF}\x{1122C}-\x{1122E}\x{1122F}-\x{11231}\x{11232}-\x{11233}\x{11234}\x{11235}\x{11236}-\x{11237}\x{1123E}\x{112DF}\x{112E0}-\x{112E2}\x{112E3}-\x{112EA}\x{11300}-\x{11301}\x{11302}-\x{11303}\x{1133B}-\x{1133C}\x{1133E}-\x{1133F}\x{11340}\x{11341}-\x{11344}\x{11347}-\x{11348}\x{1134B}-\x{1134D}\x{11357}\x{11362}-\x{11363}\x{11366}-\x{1136C}\x{11370}-\x{11374}\x{11435}-\x{11437}\x{11438}-\x{1143F}\x{11440}-\x{11441}\x{11442}-\x{11444}\x{11445}\x{11446}\x{1145E}\x{114B0}-\x{114B2}\x{114B3}-\x{114B8}\x{114B9}\x{114BA}\x{114BB}-\x{114BE}\x{114BF}-\x{114C0}\x{114C1}\x{114C2}-\x{114C3}\x{115AF}-\x{115B1}\x{115B2}-\x{115B5}\x{115B8}-\x{115BB}\x{115BC}-\x{115BD}\x{115BE}\x{115BF}-\x{115C0}\x{115DC}-\x{115DD}\x{11630}-\x{11632}\x{11633}-\x{1163A}\x{1163B}-\x{1163C}\x{1163D}\x{1163E}\x{1163F}-\x{11640}\x{116AB}\x{116AC}\x{116AD}\x{116AE}-\x{116AF}\x{116B0}-\x{116B5}\x{116B6}\x{116B7}\x{1171D}-\x{1171F}\x{11720}-\x{11721}\x{11722}-\x{11725}\x{11726}\x{11727}-\x{1172B}\x{1182C}-\x{1182E}\x{1182F}-\x{11837}\x{11838}\x{11839}-\x{1183A}\x{11930}-\x{11935}\x{11937}-\x{11938}\x{1193B}-\x{1193C}\x{1193D}\x{1193E}\x{11940}\x{11942}\x{11943}\x{119D1}-\x{119D3}\x{119D4}-\x{119D7}\x{119DA}-\x{119DB}\x{119DC}-\x{119DF}\x{119E0}\x{119E4}\x{11A01}-\x{11A0A}\x{11A33}-\x{11A38}\x{11A39}\x{11A3B}-\x{11A3E}\x{11A47}\x{11A51}-\x{11A56}\x{11A57}-\x{11A58}\x{11A59}-\x{11A5B}\x{11A8A}-\x{11A96}\x{11A97}\x{11A98}-\x{11A99}\x{11C2F}\x{11C30}-\x{11C36}\x{11C38}-\x{11C3D}\x{11C3E}\x{11C3F}\x{11C92}-\x{11CA7}\x{11CA9}\x{11CAA}-\x{11CB0}\x{11CB1}\x{11CB2}-\x{11CB3}\x{11CB4}\x{11CB5}-\x{11CB6}\x{11D31}-\x{11D36}\x{11D3A}\x{11D3C}-\x{11D3D}\x{11D3F}-\x{11D45}\x{11D47}\x{11D8A}-\x{11D8E}\x{11D90}-\x{11D91}\x{11D93}-\x{11D94}\x{11D95}\x{11D96}\x{11D97}\x{11EF3}-\x{11EF4}\x{11EF5}-\x{11EF6}\x{16AF0}-\x{16AF4}\x{16B30}-\x{16B36}\x{16F4F}\x{16F51}-\x{16F87}\x{16F8F}-\x{16F92}\x{16FE4}\x{16FF0}-\x{16FF1}\x{1BC9D}-\x{1BC9E}\x{1D165}-\x{1D166}\x{1D167}-\x{1D169}\x{1D16D}-\x{1D172}\x{1D17B}-\x{1D182}\x{1D185}-\x{1D18B}\x{1D1AA}-\x{1D1AD}\x{1D242}-\x{1D244}\x{1DA00}-\x{1DA36}\x{1DA3B}-\x{1DA6C}\x{1DA75}\x{1DA84}\x{1DA9B}-\x{1DA9F}\x{1DAA1}-\x{1DAAF}\x{1E000}-\x{1E006}\x{1E008}-\x{1E018}\x{1E01B}-\x{1E021}\x{1E023}-\x{1E024}\x{1E026}-\x{1E02A}\x{1E130}-\x{1E136}\x{1E2EC}-\x{1E2EF}\x{1E8D0}-\x{1E8D6}\x{1E944}-\x{1E94A}\x{E0100}-\x{E01EF}]/u';
+
+    const RTL_LABEL = '/[\x{0590}\x{05BE}\x{05C0}\x{05C3}\x{05C6}\x{05C8}-\x{05CF}\x{05D0}-\x{05EA}\x{05EB}-\x{05EE}\x{05EF}-\x{05F2}\x{05F3}-\x{05F4}\x{05F5}-\x{05FF}\x{0600}-\x{0605}\x{0608}\x{060B}\x{060D}\x{061B}\x{061C}\x{061D}\x{061E}-\x{061F}\x{0620}-\x{063F}\x{0640}\x{0641}-\x{064A}\x{0660}-\x{0669}\x{066B}-\x{066C}\x{066D}\x{066E}-\x{066F}\x{0671}-\x{06D3}\x{06D4}\x{06D5}\x{06DD}\x{06E5}-\x{06E6}\x{06EE}-\x{06EF}\x{06FA}-\x{06FC}\x{06FD}-\x{06FE}\x{06FF}\x{0700}-\x{070D}\x{070E}\x{070F}\x{0710}\x{0712}-\x{072F}\x{074B}-\x{074C}\x{074D}-\x{07A5}\x{07B1}\x{07B2}-\x{07BF}\x{07C0}-\x{07C9}\x{07CA}-\x{07EA}\x{07F4}-\x{07F5}\x{07FA}\x{07FB}-\x{07FC}\x{07FE}-\x{07FF}\x{0800}-\x{0815}\x{081A}\x{0824}\x{0828}\x{082E}-\x{082F}\x{0830}-\x{083E}\x{083F}\x{0840}-\x{0858}\x{085C}-\x{085D}\x{085E}\x{085F}\x{0860}-\x{086A}\x{086B}-\x{086F}\x{0870}-\x{089F}\x{08A0}-\x{08B4}\x{08B5}\x{08B6}-\x{08C7}\x{08C8}-\x{08D2}\x{08E2}\x{200F}\x{FB1D}\x{FB1F}-\x{FB28}\x{FB2A}-\x{FB36}\x{FB37}\x{FB38}-\x{FB3C}\x{FB3D}\x{FB3E}\x{FB3F}\x{FB40}-\x{FB41}\x{FB42}\x{FB43}-\x{FB44}\x{FB45}\x{FB46}-\x{FB4F}\x{FB50}-\x{FBB1}\x{FBB2}-\x{FBC1}\x{FBC2}-\x{FBD2}\x{FBD3}-\x{FD3D}\x{FD40}-\x{FD4F}\x{FD50}-\x{FD8F}\x{FD90}-\x{FD91}\x{FD92}-\x{FDC7}\x{FDC8}-\x{FDCF}\x{FDF0}-\x{FDFB}\x{FDFC}\x{FDFE}-\x{FDFF}\x{FE70}-\x{FE74}\x{FE75}\x{FE76}-\x{FEFC}\x{FEFD}-\x{FEFE}\x{10800}-\x{10805}\x{10806}-\x{10807}\x{10808}\x{10809}\x{1080A}-\x{10835}\x{10836}\x{10837}-\x{10838}\x{10839}-\x{1083B}\x{1083C}\x{1083D}-\x{1083E}\x{1083F}-\x{10855}\x{10856}\x{10857}\x{10858}-\x{1085F}\x{10860}-\x{10876}\x{10877}-\x{10878}\x{10879}-\x{1087F}\x{10880}-\x{1089E}\x{1089F}-\x{108A6}\x{108A7}-\x{108AF}\x{108B0}-\x{108DF}\x{108E0}-\x{108F2}\x{108F3}\x{108F4}-\x{108F5}\x{108F6}-\x{108FA}\x{108FB}-\x{108FF}\x{10900}-\x{10915}\x{10916}-\x{1091B}\x{1091C}-\x{1091E}\x{10920}-\x{10939}\x{1093A}-\x{1093E}\x{1093F}\x{10940}-\x{1097F}\x{10980}-\x{109B7}\x{109B8}-\x{109BB}\x{109BC}-\x{109BD}\x{109BE}-\x{109BF}\x{109C0}-\x{109CF}\x{109D0}-\x{109D1}\x{109D2}-\x{109FF}\x{10A00}\x{10A04}\x{10A07}-\x{10A0B}\x{10A10}-\x{10A13}\x{10A14}\x{10A15}-\x{10A17}\x{10A18}\x{10A19}-\x{10A35}\x{10A36}-\x{10A37}\x{10A3B}-\x{10A3E}\x{10A40}-\x{10A48}\x{10A49}-\x{10A4F}\x{10A50}-\x{10A58}\x{10A59}-\x{10A5F}\x{10A60}-\x{10A7C}\x{10A7D}-\x{10A7E}\x{10A7F}\x{10A80}-\x{10A9C}\x{10A9D}-\x{10A9F}\x{10AA0}-\x{10ABF}\x{10AC0}-\x{10AC7}\x{10AC8}\x{10AC9}-\x{10AE4}\x{10AE7}-\x{10AEA}\x{10AEB}-\x{10AEF}\x{10AF0}-\x{10AF6}\x{10AF7}-\x{10AFF}\x{10B00}-\x{10B35}\x{10B36}-\x{10B38}\x{10B40}-\x{10B55}\x{10B56}-\x{10B57}\x{10B58}-\x{10B5F}\x{10B60}-\x{10B72}\x{10B73}-\x{10B77}\x{10B78}-\x{10B7F}\x{10B80}-\x{10B91}\x{10B92}-\x{10B98}\x{10B99}-\x{10B9C}\x{10B9D}-\x{10BA8}\x{10BA9}-\x{10BAF}\x{10BB0}-\x{10BFF}\x{10C00}-\x{10C48}\x{10C49}-\x{10C7F}\x{10C80}-\x{10CB2}\x{10CB3}-\x{10CBF}\x{10CC0}-\x{10CF2}\x{10CF3}-\x{10CF9}\x{10CFA}-\x{10CFF}\x{10D00}-\x{10D23}\x{10D28}-\x{10D2F}\x{10D30}-\x{10D39}\x{10D3A}-\x{10D3F}\x{10D40}-\x{10E5F}\x{10E60}-\x{10E7E}\x{10E7F}\x{10E80}-\x{10EA9}\x{10EAA}\x{10EAD}\x{10EAE}-\x{10EAF}\x{10EB0}-\x{10EB1}\x{10EB2}-\x{10EFF}\x{10F00}-\x{10F1C}\x{10F1D}-\x{10F26}\x{10F27}\x{10F28}-\x{10F2F}\x{10F30}-\x{10F45}\x{10F51}-\x{10F54}\x{10F55}-\x{10F59}\x{10F5A}-\x{10F6F}\x{10F70}-\x{10FAF}\x{10FB0}-\x{10FC4}\x{10FC5}-\x{10FCB}\x{10FCC}-\x{10FDF}\x{10FE0}-\x{10FF6}\x{10FF7}-\x{10FFF}\x{1E800}-\x{1E8C4}\x{1E8C5}-\x{1E8C6}\x{1E8C7}-\x{1E8CF}\x{1E8D7}-\x{1E8FF}\x{1E900}-\x{1E943}\x{1E94B}\x{1E94C}-\x{1E94F}\x{1E950}-\x{1E959}\x{1E95A}-\x{1E95D}\x{1E95E}-\x{1E95F}\x{1E960}-\x{1EC6F}\x{1EC70}\x{1EC71}-\x{1ECAB}\x{1ECAC}\x{1ECAD}-\x{1ECAF}\x{1ECB0}\x{1ECB1}-\x{1ECB4}\x{1ECB5}-\x{1ECBF}\x{1ECC0}-\x{1ECFF}\x{1ED00}\x{1ED01}-\x{1ED2D}\x{1ED2E}\x{1ED2F}-\x{1ED3D}\x{1ED3E}-\x{1ED4F}\x{1ED50}-\x{1EDFF}\x{1EE00}-\x{1EE03}\x{1EE04}\x{1EE05}-\x{1EE1F}\x{1EE20}\x{1EE21}-\x{1EE22}\x{1EE23}\x{1EE24}\x{1EE25}-\x{1EE26}\x{1EE27}\x{1EE28}\x{1EE29}-\x{1EE32}\x{1EE33}\x{1EE34}-\x{1EE37}\x{1EE38}\x{1EE39}\x{1EE3A}\x{1EE3B}\x{1EE3C}-\x{1EE41}\x{1EE42}\x{1EE43}-\x{1EE46}\x{1EE47}\x{1EE48}\x{1EE49}\x{1EE4A}\x{1EE4B}\x{1EE4C}\x{1EE4D}-\x{1EE4F}\x{1EE50}\x{1EE51}-\x{1EE52}\x{1EE53}\x{1EE54}\x{1EE55}-\x{1EE56}\x{1EE57}\x{1EE58}\x{1EE59}\x{1EE5A}\x{1EE5B}\x{1EE5C}\x{1EE5D}\x{1EE5E}\x{1EE5F}\x{1EE60}\x{1EE61}-\x{1EE62}\x{1EE63}\x{1EE64}\x{1EE65}-\x{1EE66}\x{1EE67}-\x{1EE6A}\x{1EE6B}\x{1EE6C}-\x{1EE72}\x{1EE73}\x{1EE74}-\x{1EE77}\x{1EE78}\x{1EE79}-\x{1EE7C}\x{1EE7D}\x{1EE7E}\x{1EE7F}\x{1EE80}-\x{1EE89}\x{1EE8A}\x{1EE8B}-\x{1EE9B}\x{1EE9C}-\x{1EEA0}\x{1EEA1}-\x{1EEA3}\x{1EEA4}\x{1EEA5}-\x{1EEA9}\x{1EEAA}\x{1EEAB}-\x{1EEBB}\x{1EEBC}-\x{1EEEF}\x{1EEF2}-\x{1EEFF}\x{1EF00}-\x{1EFFF}]/u';
+
+    const BIDI_STEP_1_LTR = '/^[^\x{0000}-\x{0008}\x{0009}\x{000A}\x{000B}\x{000C}\x{000D}\x{000E}-\x{001B}\x{001C}-\x{001E}\x{001F}\x{0020}\x{0021}-\x{0022}\x{0023}\x{0024}\x{0025}\x{0026}-\x{0027}\x{0028}\x{0029}\x{002A}\x{002B}\x{002C}\x{002D}\x{002E}-\x{002F}\x{0030}-\x{0039}\x{003A}\x{003B}\x{003C}-\x{003E}\x{003F}-\x{0040}\x{005B}\x{005C}\x{005D}\x{005E}\x{005F}\x{0060}\x{007B}\x{007C}\x{007D}\x{007E}\x{007F}-\x{0084}\x{0085}\x{0086}-\x{009F}\x{00A0}\x{00A1}\x{00A2}-\x{00A5}\x{00A6}\x{00A7}\x{00A8}\x{00A9}\x{00AB}\x{00AC}\x{00AD}\x{00AE}\x{00AF}\x{00B0}\x{00B1}\x{00B2}-\x{00B3}\x{00B4}\x{00B6}-\x{00B7}\x{00B8}\x{00B9}\x{00BB}\x{00BC}-\x{00BE}\x{00BF}\x{00D7}\x{00F7}\x{02B9}-\x{02BA}\x{02C2}-\x{02C5}\x{02C6}-\x{02CF}\x{02D2}-\x{02DF}\x{02E5}-\x{02EB}\x{02EC}\x{02ED}\x{02EF}-\x{02FF}\x{0300}-\x{036F}\x{0374}\x{0375}\x{037E}\x{0384}-\x{0385}\x{0387}\x{03F6}\x{0483}-\x{0487}\x{0488}-\x{0489}\x{058A}\x{058D}-\x{058E}\x{058F}\x{0590}\x{0591}-\x{05BD}\x{05BE}\x{05BF}\x{05C0}\x{05C1}-\x{05C2}\x{05C3}\x{05C4}-\x{05C5}\x{05C6}\x{05C7}\x{05C8}-\x{05CF}\x{05D0}-\x{05EA}\x{05EB}-\x{05EE}\x{05EF}-\x{05F2}\x{05F3}-\x{05F4}\x{05F5}-\x{05FF}\x{0600}-\x{0605}\x{0606}-\x{0607}\x{0608}\x{0609}-\x{060A}\x{060B}\x{060C}\x{060D}\x{060E}-\x{060F}\x{0610}-\x{061A}\x{061B}\x{061C}\x{061D}\x{061E}-\x{061F}\x{0620}-\x{063F}\x{0640}\x{0641}-\x{064A}\x{064B}-\x{065F}\x{0660}-\x{0669}\x{066A}\x{066B}-\x{066C}\x{066D}\x{066E}-\x{066F}\x{0670}\x{0671}-\x{06D3}\x{06D4}\x{06D5}\x{06D6}-\x{06DC}\x{06DD}\x{06DE}\x{06DF}-\x{06E4}\x{06E5}-\x{06E6}\x{06E7}-\x{06E8}\x{06E9}\x{06EA}-\x{06ED}\x{06EE}-\x{06EF}\x{06F0}-\x{06F9}\x{06FA}-\x{06FC}\x{06FD}-\x{06FE}\x{06FF}\x{0700}-\x{070D}\x{070E}\x{070F}\x{0710}\x{0711}\x{0712}-\x{072F}\x{0730}-\x{074A}\x{074B}-\x{074C}\x{074D}-\x{07A5}\x{07A6}-\x{07B0}\x{07B1}\x{07B2}-\x{07BF}\x{07C0}-\x{07C9}\x{07CA}-\x{07EA}\x{07EB}-\x{07F3}\x{07F4}-\x{07F5}\x{07F6}\x{07F7}-\x{07F9}\x{07FA}\x{07FB}-\x{07FC}\x{07FD}\x{07FE}-\x{07FF}\x{0800}-\x{0815}\x{0816}-\x{0819}\x{081A}\x{081B}-\x{0823}\x{0824}\x{0825}-\x{0827}\x{0828}\x{0829}-\x{082D}\x{082E}-\x{082F}\x{0830}-\x{083E}\x{083F}\x{0840}-\x{0858}\x{0859}-\x{085B}\x{085C}-\x{085D}\x{085E}\x{085F}\x{0860}-\x{086A}\x{086B}-\x{086F}\x{0870}-\x{089F}\x{08A0}-\x{08B4}\x{08B5}\x{08B6}-\x{08C7}\x{08C8}-\x{08D2}\x{08D3}-\x{08E1}\x{08E2}\x{08E3}-\x{0902}\x{093A}\x{093C}\x{0941}-\x{0948}\x{094D}\x{0951}-\x{0957}\x{0962}-\x{0963}\x{0981}\x{09BC}\x{09C1}-\x{09C4}\x{09CD}\x{09E2}-\x{09E3}\x{09F2}-\x{09F3}\x{09FB}\x{09FE}\x{0A01}-\x{0A02}\x{0A3C}\x{0A41}-\x{0A42}\x{0A47}-\x{0A48}\x{0A4B}-\x{0A4D}\x{0A51}\x{0A70}-\x{0A71}\x{0A75}\x{0A81}-\x{0A82}\x{0ABC}\x{0AC1}-\x{0AC5}\x{0AC7}-\x{0AC8}\x{0ACD}\x{0AE2}-\x{0AE3}\x{0AF1}\x{0AFA}-\x{0AFF}\x{0B01}\x{0B3C}\x{0B3F}\x{0B41}-\x{0B44}\x{0B4D}\x{0B55}-\x{0B56}\x{0B62}-\x{0B63}\x{0B82}\x{0BC0}\x{0BCD}\x{0BF3}-\x{0BF8}\x{0BF9}\x{0BFA}\x{0C00}\x{0C04}\x{0C3E}-\x{0C40}\x{0C46}-\x{0C48}\x{0C4A}-\x{0C4D}\x{0C55}-\x{0C56}\x{0C62}-\x{0C63}\x{0C78}-\x{0C7E}\x{0C81}\x{0CBC}\x{0CCC}-\x{0CCD}\x{0CE2}-\x{0CE3}\x{0D00}-\x{0D01}\x{0D3B}-\x{0D3C}\x{0D41}-\x{0D44}\x{0D4D}\x{0D62}-\x{0D63}\x{0D81}\x{0DCA}\x{0DD2}-\x{0DD4}\x{0DD6}\x{0E31}\x{0E34}-\x{0E3A}\x{0E3F}\x{0E47}-\x{0E4E}\x{0EB1}\x{0EB4}-\x{0EBC}\x{0EC8}-\x{0ECD}\x{0F18}-\x{0F19}\x{0F35}\x{0F37}\x{0F39}\x{0F3A}\x{0F3B}\x{0F3C}\x{0F3D}\x{0F71}-\x{0F7E}\x{0F80}-\x{0F84}\x{0F86}-\x{0F87}\x{0F8D}-\x{0F97}\x{0F99}-\x{0FBC}\x{0FC6}\x{102D}-\x{1030}\x{1032}-\x{1037}\x{1039}-\x{103A}\x{103D}-\x{103E}\x{1058}-\x{1059}\x{105E}-\x{1060}\x{1071}-\x{1074}\x{1082}\x{1085}-\x{1086}\x{108D}\x{109D}\x{135D}-\x{135F}\x{1390}-\x{1399}\x{1400}\x{1680}\x{169B}\x{169C}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}-\x{1753}\x{1772}-\x{1773}\x{17B4}-\x{17B5}\x{17B7}-\x{17BD}\x{17C6}\x{17C9}-\x{17D3}\x{17DB}\x{17DD}\x{17F0}-\x{17F9}\x{1800}-\x{1805}\x{1806}\x{1807}-\x{180A}\x{180B}-\x{180D}\x{180E}\x{1885}-\x{1886}\x{18A9}\x{1920}-\x{1922}\x{1927}-\x{1928}\x{1932}\x{1939}-\x{193B}\x{1940}\x{1944}-\x{1945}\x{19DE}-\x{19FF}\x{1A17}-\x{1A18}\x{1A1B}\x{1A56}\x{1A58}-\x{1A5E}\x{1A60}\x{1A62}\x{1A65}-\x{1A6C}\x{1A73}-\x{1A7C}\x{1A7F}\x{1AB0}-\x{1ABD}\x{1ABE}\x{1ABF}-\x{1AC0}\x{1B00}-\x{1B03}\x{1B34}\x{1B36}-\x{1B3A}\x{1B3C}\x{1B42}\x{1B6B}-\x{1B73}\x{1B80}-\x{1B81}\x{1BA2}-\x{1BA5}\x{1BA8}-\x{1BA9}\x{1BAB}-\x{1BAD}\x{1BE6}\x{1BE8}-\x{1BE9}\x{1BED}\x{1BEF}-\x{1BF1}\x{1C2C}-\x{1C33}\x{1C36}-\x{1C37}\x{1CD0}-\x{1CD2}\x{1CD4}-\x{1CE0}\x{1CE2}-\x{1CE8}\x{1CED}\x{1CF4}\x{1CF8}-\x{1CF9}\x{1DC0}-\x{1DF9}\x{1DFB}-\x{1DFF}\x{1FBD}\x{1FBF}-\x{1FC1}\x{1FCD}-\x{1FCF}\x{1FDD}-\x{1FDF}\x{1FED}-\x{1FEF}\x{1FFD}-\x{1FFE}\x{2000}-\x{200A}\x{200B}-\x{200D}\x{200F}\x{2010}-\x{2015}\x{2016}-\x{2017}\x{2018}\x{2019}\x{201A}\x{201B}-\x{201C}\x{201D}\x{201E}\x{201F}\x{2020}-\x{2027}\x{2028}\x{2029}\x{202A}\x{202B}\x{202C}\x{202D}\x{202E}\x{202F}\x{2030}-\x{2034}\x{2035}-\x{2038}\x{2039}\x{203A}\x{203B}-\x{203E}\x{203F}-\x{2040}\x{2041}-\x{2043}\x{2044}\x{2045}\x{2046}\x{2047}-\x{2051}\x{2052}\x{2053}\x{2054}\x{2055}-\x{205E}\x{205F}\x{2060}-\x{2064}\x{2065}\x{2066}\x{2067}\x{2068}\x{2069}\x{206A}-\x{206F}\x{2070}\x{2074}-\x{2079}\x{207A}-\x{207B}\x{207C}\x{207D}\x{207E}\x{2080}-\x{2089}\x{208A}-\x{208B}\x{208C}\x{208D}\x{208E}\x{20A0}-\x{20BF}\x{20C0}-\x{20CF}\x{20D0}-\x{20DC}\x{20DD}-\x{20E0}\x{20E1}\x{20E2}-\x{20E4}\x{20E5}-\x{20F0}\x{2100}-\x{2101}\x{2103}-\x{2106}\x{2108}-\x{2109}\x{2114}\x{2116}-\x{2117}\x{2118}\x{211E}-\x{2123}\x{2125}\x{2127}\x{2129}\x{212E}\x{213A}-\x{213B}\x{2140}-\x{2144}\x{214A}\x{214B}\x{214C}-\x{214D}\x{2150}-\x{215F}\x{2189}\x{218A}-\x{218B}\x{2190}-\x{2194}\x{2195}-\x{2199}\x{219A}-\x{219B}\x{219C}-\x{219F}\x{21A0}\x{21A1}-\x{21A2}\x{21A3}\x{21A4}-\x{21A5}\x{21A6}\x{21A7}-\x{21AD}\x{21AE}\x{21AF}-\x{21CD}\x{21CE}-\x{21CF}\x{21D0}-\x{21D1}\x{21D2}\x{21D3}\x{21D4}\x{21D5}-\x{21F3}\x{21F4}-\x{2211}\x{2212}\x{2213}\x{2214}-\x{22FF}\x{2300}-\x{2307}\x{2308}\x{2309}\x{230A}\x{230B}\x{230C}-\x{231F}\x{2320}-\x{2321}\x{2322}-\x{2328}\x{2329}\x{232A}\x{232B}-\x{2335}\x{237B}\x{237C}\x{237D}-\x{2394}\x{2396}-\x{239A}\x{239B}-\x{23B3}\x{23B4}-\x{23DB}\x{23DC}-\x{23E1}\x{23E2}-\x{2426}\x{2440}-\x{244A}\x{2460}-\x{2487}\x{2488}-\x{249B}\x{24EA}-\x{24FF}\x{2500}-\x{25B6}\x{25B7}\x{25B8}-\x{25C0}\x{25C1}\x{25C2}-\x{25F7}\x{25F8}-\x{25FF}\x{2600}-\x{266E}\x{266F}\x{2670}-\x{26AB}\x{26AD}-\x{2767}\x{2768}\x{2769}\x{276A}\x{276B}\x{276C}\x{276D}\x{276E}\x{276F}\x{2770}\x{2771}\x{2772}\x{2773}\x{2774}\x{2775}\x{2776}-\x{2793}\x{2794}-\x{27BF}\x{27C0}-\x{27C4}\x{27C5}\x{27C6}\x{27C7}-\x{27E5}\x{27E6}\x{27E7}\x{27E8}\x{27E9}\x{27EA}\x{27EB}\x{27EC}\x{27ED}\x{27EE}\x{27EF}\x{27F0}-\x{27FF}\x{2900}-\x{2982}\x{2983}\x{2984}\x{2985}\x{2986}\x{2987}\x{2988}\x{2989}\x{298A}\x{298B}\x{298C}\x{298D}\x{298E}\x{298F}\x{2990}\x{2991}\x{2992}\x{2993}\x{2994}\x{2995}\x{2996}\x{2997}\x{2998}\x{2999}-\x{29D7}\x{29D8}\x{29D9}\x{29DA}\x{29DB}\x{29DC}-\x{29FB}\x{29FC}\x{29FD}\x{29FE}-\x{2AFF}\x{2B00}-\x{2B2F}\x{2B30}-\x{2B44}\x{2B45}-\x{2B46}\x{2B47}-\x{2B4C}\x{2B4D}-\x{2B73}\x{2B76}-\x{2B95}\x{2B97}-\x{2BFF}\x{2CE5}-\x{2CEA}\x{2CEF}-\x{2CF1}\x{2CF9}-\x{2CFC}\x{2CFD}\x{2CFE}-\x{2CFF}\x{2D7F}\x{2DE0}-\x{2DFF}\x{2E00}-\x{2E01}\x{2E02}\x{2E03}\x{2E04}\x{2E05}\x{2E06}-\x{2E08}\x{2E09}\x{2E0A}\x{2E0B}\x{2E0C}\x{2E0D}\x{2E0E}-\x{2E16}\x{2E17}\x{2E18}-\x{2E19}\x{2E1A}\x{2E1B}\x{2E1C}\x{2E1D}\x{2E1E}-\x{2E1F}\x{2E20}\x{2E21}\x{2E22}\x{2E23}\x{2E24}\x{2E25}\x{2E26}\x{2E27}\x{2E28}\x{2E29}\x{2E2A}-\x{2E2E}\x{2E2F}\x{2E30}-\x{2E39}\x{2E3A}-\x{2E3B}\x{2E3C}-\x{2E3F}\x{2E40}\x{2E41}\x{2E42}\x{2E43}-\x{2E4F}\x{2E50}-\x{2E51}\x{2E52}\x{2E80}-\x{2E99}\x{2E9B}-\x{2EF3}\x{2F00}-\x{2FD5}\x{2FF0}-\x{2FFB}\x{3000}\x{3001}-\x{3003}\x{3004}\x{3008}\x{3009}\x{300A}\x{300B}\x{300C}\x{300D}\x{300E}\x{300F}\x{3010}\x{3011}\x{3012}-\x{3013}\x{3014}\x{3015}\x{3016}\x{3017}\x{3018}\x{3019}\x{301A}\x{301B}\x{301C}\x{301D}\x{301E}-\x{301F}\x{3020}\x{302A}-\x{302D}\x{3030}\x{3036}-\x{3037}\x{303D}\x{303E}-\x{303F}\x{3099}-\x{309A}\x{309B}-\x{309C}\x{30A0}\x{30FB}\x{31C0}-\x{31E3}\x{321D}-\x{321E}\x{3250}\x{3251}-\x{325F}\x{327C}-\x{327E}\x{32B1}-\x{32BF}\x{32CC}-\x{32CF}\x{3377}-\x{337A}\x{33DE}-\x{33DF}\x{33FF}\x{4DC0}-\x{4DFF}\x{A490}-\x{A4C6}\x{A60D}-\x{A60F}\x{A66F}\x{A670}-\x{A672}\x{A673}\x{A674}-\x{A67D}\x{A67E}\x{A67F}\x{A69E}-\x{A69F}\x{A6F0}-\x{A6F1}\x{A700}-\x{A716}\x{A717}-\x{A71F}\x{A720}-\x{A721}\x{A788}\x{A802}\x{A806}\x{A80B}\x{A825}-\x{A826}\x{A828}-\x{A82B}\x{A82C}\x{A838}\x{A839}\x{A874}-\x{A877}\x{A8C4}-\x{A8C5}\x{A8E0}-\x{A8F1}\x{A8FF}\x{A926}-\x{A92D}\x{A947}-\x{A951}\x{A980}-\x{A982}\x{A9B3}\x{A9B6}-\x{A9B9}\x{A9BC}-\x{A9BD}\x{A9E5}\x{AA29}-\x{AA2E}\x{AA31}-\x{AA32}\x{AA35}-\x{AA36}\x{AA43}\x{AA4C}\x{AA7C}\x{AAB0}\x{AAB2}-\x{AAB4}\x{AAB7}-\x{AAB8}\x{AABE}-\x{AABF}\x{AAC1}\x{AAEC}-\x{AAED}\x{AAF6}\x{AB6A}-\x{AB6B}\x{ABE5}\x{ABE8}\x{ABED}\x{FB1D}\x{FB1E}\x{FB1F}-\x{FB28}\x{FB29}\x{FB2A}-\x{FB36}\x{FB37}\x{FB38}-\x{FB3C}\x{FB3D}\x{FB3E}\x{FB3F}\x{FB40}-\x{FB41}\x{FB42}\x{FB43}-\x{FB44}\x{FB45}\x{FB46}-\x{FB4F}\x{FB50}-\x{FBB1}\x{FBB2}-\x{FBC1}\x{FBC2}-\x{FBD2}\x{FBD3}-\x{FD3D}\x{FD3E}\x{FD3F}\x{FD40}-\x{FD4F}\x{FD50}-\x{FD8F}\x{FD90}-\x{FD91}\x{FD92}-\x{FDC7}\x{FDC8}-\x{FDCF}\x{FDD0}-\x{FDEF}\x{FDF0}-\x{FDFB}\x{FDFC}\x{FDFD}\x{FDFE}-\x{FDFF}\x{FE00}-\x{FE0F}\x{FE10}-\x{FE16}\x{FE17}\x{FE18}\x{FE19}\x{FE20}-\x{FE2F}\x{FE30}\x{FE31}-\x{FE32}\x{FE33}-\x{FE34}\x{FE35}\x{FE36}\x{FE37}\x{FE38}\x{FE39}\x{FE3A}\x{FE3B}\x{FE3C}\x{FE3D}\x{FE3E}\x{FE3F}\x{FE40}\x{FE41}\x{FE42}\x{FE43}\x{FE44}\x{FE45}-\x{FE46}\x{FE47}\x{FE48}\x{FE49}-\x{FE4C}\x{FE4D}-\x{FE4F}\x{FE50}\x{FE51}\x{FE52}\x{FE54}\x{FE55}\x{FE56}-\x{FE57}\x{FE58}\x{FE59}\x{FE5A}\x{FE5B}\x{FE5C}\x{FE5D}\x{FE5E}\x{FE5F}\x{FE60}-\x{FE61}\x{FE62}\x{FE63}\x{FE64}-\x{FE66}\x{FE68}\x{FE69}\x{FE6A}\x{FE6B}\x{FE70}-\x{FE74}\x{FE75}\x{FE76}-\x{FEFC}\x{FEFD}-\x{FEFE}\x{FEFF}\x{FF01}-\x{FF02}\x{FF03}\x{FF04}\x{FF05}\x{FF06}-\x{FF07}\x{FF08}\x{FF09}\x{FF0A}\x{FF0B}\x{FF0C}\x{FF0D}\x{FF0E}-\x{FF0F}\x{FF10}-\x{FF19}\x{FF1A}\x{FF1B}\x{FF1C}-\x{FF1E}\x{FF1F}-\x{FF20}\x{FF3B}\x{FF3C}\x{FF3D}\x{FF3E}\x{FF3F}\x{FF40}\x{FF5B}\x{FF5C}\x{FF5D}\x{FF5E}\x{FF5F}\x{FF60}\x{FF61}\x{FF62}\x{FF63}\x{FF64}-\x{FF65}\x{FFE0}-\x{FFE1}\x{FFE2}\x{FFE3}\x{FFE4}\x{FFE5}-\x{FFE6}\x{FFE8}\x{FFE9}-\x{FFEC}\x{FFED}-\x{FFEE}\x{FFF0}-\x{FFF8}\x{FFF9}-\x{FFFB}\x{FFFC}-\x{FFFD}\x{FFFE}-\x{FFFF}\x{10101}\x{10140}-\x{10174}\x{10175}-\x{10178}\x{10179}-\x{10189}\x{1018A}-\x{1018B}\x{1018C}\x{10190}-\x{1019C}\x{101A0}\x{101FD}\x{102E0}\x{102E1}-\x{102FB}\x{10376}-\x{1037A}\x{10800}-\x{10805}\x{10806}-\x{10807}\x{10808}\x{10809}\x{1080A}-\x{10835}\x{10836}\x{10837}-\x{10838}\x{10839}-\x{1083B}\x{1083C}\x{1083D}-\x{1083E}\x{1083F}-\x{10855}\x{10856}\x{10857}\x{10858}-\x{1085F}\x{10860}-\x{10876}\x{10877}-\x{10878}\x{10879}-\x{1087F}\x{10880}-\x{1089E}\x{1089F}-\x{108A6}\x{108A7}-\x{108AF}\x{108B0}-\x{108DF}\x{108E0}-\x{108F2}\x{108F3}\x{108F4}-\x{108F5}\x{108F6}-\x{108FA}\x{108FB}-\x{108FF}\x{10900}-\x{10915}\x{10916}-\x{1091B}\x{1091C}-\x{1091E}\x{1091F}\x{10920}-\x{10939}\x{1093A}-\x{1093E}\x{1093F}\x{10940}-\x{1097F}\x{10980}-\x{109B7}\x{109B8}-\x{109BB}\x{109BC}-\x{109BD}\x{109BE}-\x{109BF}\x{109C0}-\x{109CF}\x{109D0}-\x{109D1}\x{109D2}-\x{109FF}\x{10A00}\x{10A01}-\x{10A03}\x{10A04}\x{10A05}-\x{10A06}\x{10A07}-\x{10A0B}\x{10A0C}-\x{10A0F}\x{10A10}-\x{10A13}\x{10A14}\x{10A15}-\x{10A17}\x{10A18}\x{10A19}-\x{10A35}\x{10A36}-\x{10A37}\x{10A38}-\x{10A3A}\x{10A3B}-\x{10A3E}\x{10A3F}\x{10A40}-\x{10A48}\x{10A49}-\x{10A4F}\x{10A50}-\x{10A58}\x{10A59}-\x{10A5F}\x{10A60}-\x{10A7C}\x{10A7D}-\x{10A7E}\x{10A7F}\x{10A80}-\x{10A9C}\x{10A9D}-\x{10A9F}\x{10AA0}-\x{10ABF}\x{10AC0}-\x{10AC7}\x{10AC8}\x{10AC9}-\x{10AE4}\x{10AE5}-\x{10AE6}\x{10AE7}-\x{10AEA}\x{10AEB}-\x{10AEF}\x{10AF0}-\x{10AF6}\x{10AF7}-\x{10AFF}\x{10B00}-\x{10B35}\x{10B36}-\x{10B38}\x{10B39}-\x{10B3F}\x{10B40}-\x{10B55}\x{10B56}-\x{10B57}\x{10B58}-\x{10B5F}\x{10B60}-\x{10B72}\x{10B73}-\x{10B77}\x{10B78}-\x{10B7F}\x{10B80}-\x{10B91}\x{10B92}-\x{10B98}\x{10B99}-\x{10B9C}\x{10B9D}-\x{10BA8}\x{10BA9}-\x{10BAF}\x{10BB0}-\x{10BFF}\x{10C00}-\x{10C48}\x{10C49}-\x{10C7F}\x{10C80}-\x{10CB2}\x{10CB3}-\x{10CBF}\x{10CC0}-\x{10CF2}\x{10CF3}-\x{10CF9}\x{10CFA}-\x{10CFF}\x{10D00}-\x{10D23}\x{10D24}-\x{10D27}\x{10D28}-\x{10D2F}\x{10D30}-\x{10D39}\x{10D3A}-\x{10D3F}\x{10D40}-\x{10E5F}\x{10E60}-\x{10E7E}\x{10E7F}\x{10E80}-\x{10EA9}\x{10EAA}\x{10EAB}-\x{10EAC}\x{10EAD}\x{10EAE}-\x{10EAF}\x{10EB0}-\x{10EB1}\x{10EB2}-\x{10EFF}\x{10F00}-\x{10F1C}\x{10F1D}-\x{10F26}\x{10F27}\x{10F28}-\x{10F2F}\x{10F30}-\x{10F45}\x{10F46}-\x{10F50}\x{10F51}-\x{10F54}\x{10F55}-\x{10F59}\x{10F5A}-\x{10F6F}\x{10F70}-\x{10FAF}\x{10FB0}-\x{10FC4}\x{10FC5}-\x{10FCB}\x{10FCC}-\x{10FDF}\x{10FE0}-\x{10FF6}\x{10FF7}-\x{10FFF}\x{11001}\x{11038}-\x{11046}\x{11052}-\x{11065}\x{1107F}-\x{11081}\x{110B3}-\x{110B6}\x{110B9}-\x{110BA}\x{11100}-\x{11102}\x{11127}-\x{1112B}\x{1112D}-\x{11134}\x{11173}\x{11180}-\x{11181}\x{111B6}-\x{111BE}\x{111C9}-\x{111CC}\x{111CF}\x{1122F}-\x{11231}\x{11234}\x{11236}-\x{11237}\x{1123E}\x{112DF}\x{112E3}-\x{112EA}\x{11300}-\x{11301}\x{1133B}-\x{1133C}\x{11340}\x{11366}-\x{1136C}\x{11370}-\x{11374}\x{11438}-\x{1143F}\x{11442}-\x{11444}\x{11446}\x{1145E}\x{114B3}-\x{114B8}\x{114BA}\x{114BF}-\x{114C0}\x{114C2}-\x{114C3}\x{115B2}-\x{115B5}\x{115BC}-\x{115BD}\x{115BF}-\x{115C0}\x{115DC}-\x{115DD}\x{11633}-\x{1163A}\x{1163D}\x{1163F}-\x{11640}\x{11660}-\x{1166C}\x{116AB}\x{116AD}\x{116B0}-\x{116B5}\x{116B7}\x{1171D}-\x{1171F}\x{11722}-\x{11725}\x{11727}-\x{1172B}\x{1182F}-\x{11837}\x{11839}-\x{1183A}\x{1193B}-\x{1193C}\x{1193E}\x{11943}\x{119D4}-\x{119D7}\x{119DA}-\x{119DB}\x{119E0}\x{11A01}-\x{11A06}\x{11A09}-\x{11A0A}\x{11A33}-\x{11A38}\x{11A3B}-\x{11A3E}\x{11A47}\x{11A51}-\x{11A56}\x{11A59}-\x{11A5B}\x{11A8A}-\x{11A96}\x{11A98}-\x{11A99}\x{11C30}-\x{11C36}\x{11C38}-\x{11C3D}\x{11C92}-\x{11CA7}\x{11CAA}-\x{11CB0}\x{11CB2}-\x{11CB3}\x{11CB5}-\x{11CB6}\x{11D31}-\x{11D36}\x{11D3A}\x{11D3C}-\x{11D3D}\x{11D3F}-\x{11D45}\x{11D47}\x{11D90}-\x{11D91}\x{11D95}\x{11D97}\x{11EF3}-\x{11EF4}\x{11FD5}-\x{11FDC}\x{11FDD}-\x{11FE0}\x{11FE1}-\x{11FF1}\x{16AF0}-\x{16AF4}\x{16B30}-\x{16B36}\x{16F4F}\x{16F8F}-\x{16F92}\x{16FE2}\x{16FE4}\x{1BC9D}-\x{1BC9E}\x{1BCA0}-\x{1BCA3}\x{1D167}-\x{1D169}\x{1D173}-\x{1D17A}\x{1D17B}-\x{1D182}\x{1D185}-\x{1D18B}\x{1D1AA}-\x{1D1AD}\x{1D200}-\x{1D241}\x{1D242}-\x{1D244}\x{1D245}\x{1D300}-\x{1D356}\x{1D6DB}\x{1D715}\x{1D74F}\x{1D789}\x{1D7C3}\x{1D7CE}-\x{1D7FF}\x{1DA00}-\x{1DA36}\x{1DA3B}-\x{1DA6C}\x{1DA75}\x{1DA84}\x{1DA9B}-\x{1DA9F}\x{1DAA1}-\x{1DAAF}\x{1E000}-\x{1E006}\x{1E008}-\x{1E018}\x{1E01B}-\x{1E021}\x{1E023}-\x{1E024}\x{1E026}-\x{1E02A}\x{1E130}-\x{1E136}\x{1E2EC}-\x{1E2EF}\x{1E2FF}\x{1E800}-\x{1E8C4}\x{1E8C5}-\x{1E8C6}\x{1E8C7}-\x{1E8CF}\x{1E8D0}-\x{1E8D6}\x{1E8D7}-\x{1E8FF}\x{1E900}-\x{1E943}\x{1E944}-\x{1E94A}\x{1E94B}\x{1E94C}-\x{1E94F}\x{1E950}-\x{1E959}\x{1E95A}-\x{1E95D}\x{1E95E}-\x{1E95F}\x{1E960}-\x{1EC6F}\x{1EC70}\x{1EC71}-\x{1ECAB}\x{1ECAC}\x{1ECAD}-\x{1ECAF}\x{1ECB0}\x{1ECB1}-\x{1ECB4}\x{1ECB5}-\x{1ECBF}\x{1ECC0}-\x{1ECFF}\x{1ED00}\x{1ED01}-\x{1ED2D}\x{1ED2E}\x{1ED2F}-\x{1ED3D}\x{1ED3E}-\x{1ED4F}\x{1ED50}-\x{1EDFF}\x{1EE00}-\x{1EE03}\x{1EE04}\x{1EE05}-\x{1EE1F}\x{1EE20}\x{1EE21}-\x{1EE22}\x{1EE23}\x{1EE24}\x{1EE25}-\x{1EE26}\x{1EE27}\x{1EE28}\x{1EE29}-\x{1EE32}\x{1EE33}\x{1EE34}-\x{1EE37}\x{1EE38}\x{1EE39}\x{1EE3A}\x{1EE3B}\x{1EE3C}-\x{1EE41}\x{1EE42}\x{1EE43}-\x{1EE46}\x{1EE47}\x{1EE48}\x{1EE49}\x{1EE4A}\x{1EE4B}\x{1EE4C}\x{1EE4D}-\x{1EE4F}\x{1EE50}\x{1EE51}-\x{1EE52}\x{1EE53}\x{1EE54}\x{1EE55}-\x{1EE56}\x{1EE57}\x{1EE58}\x{1EE59}\x{1EE5A}\x{1EE5B}\x{1EE5C}\x{1EE5D}\x{1EE5E}\x{1EE5F}\x{1EE60}\x{1EE61}-\x{1EE62}\x{1EE63}\x{1EE64}\x{1EE65}-\x{1EE66}\x{1EE67}-\x{1EE6A}\x{1EE6B}\x{1EE6C}-\x{1EE72}\x{1EE73}\x{1EE74}-\x{1EE77}\x{1EE78}\x{1EE79}-\x{1EE7C}\x{1EE7D}\x{1EE7E}\x{1EE7F}\x{1EE80}-\x{1EE89}\x{1EE8A}\x{1EE8B}-\x{1EE9B}\x{1EE9C}-\x{1EEA0}\x{1EEA1}-\x{1EEA3}\x{1EEA4}\x{1EEA5}-\x{1EEA9}\x{1EEAA}\x{1EEAB}-\x{1EEBB}\x{1EEBC}-\x{1EEEF}\x{1EEF0}-\x{1EEF1}\x{1EEF2}-\x{1EEFF}\x{1EF00}-\x{1EFFF}\x{1F000}-\x{1F02B}\x{1F030}-\x{1F093}\x{1F0A0}-\x{1F0AE}\x{1F0B1}-\x{1F0BF}\x{1F0C1}-\x{1F0CF}\x{1F0D1}-\x{1F0F5}\x{1F100}-\x{1F10A}\x{1F10B}-\x{1F10C}\x{1F10D}-\x{1F10F}\x{1F12F}\x{1F16A}-\x{1F16F}\x{1F1AD}\x{1F260}-\x{1F265}\x{1F300}-\x{1F3FA}\x{1F3FB}-\x{1F3FF}\x{1F400}-\x{1F6D7}\x{1F6E0}-\x{1F6EC}\x{1F6F0}-\x{1F6FC}\x{1F700}-\x{1F773}\x{1F780}-\x{1F7D8}\x{1F7E0}-\x{1F7EB}\x{1F800}-\x{1F80B}\x{1F810}-\x{1F847}\x{1F850}-\x{1F859}\x{1F860}-\x{1F887}\x{1F890}-\x{1F8AD}\x{1F8B0}-\x{1F8B1}\x{1F900}-\x{1F978}\x{1F97A}-\x{1F9CB}\x{1F9CD}-\x{1FA53}\x{1FA60}-\x{1FA6D}\x{1FA70}-\x{1FA74}\x{1FA78}-\x{1FA7A}\x{1FA80}-\x{1FA86}\x{1FA90}-\x{1FAA8}\x{1FAB0}-\x{1FAB6}\x{1FAC0}-\x{1FAC2}\x{1FAD0}-\x{1FAD6}\x{1FB00}-\x{1FB92}\x{1FB94}-\x{1FBCA}\x{1FBF0}-\x{1FBF9}\x{1FFFE}-\x{1FFFF}\x{2FFFE}-\x{2FFFF}\x{3FFFE}-\x{3FFFF}\x{4FFFE}-\x{4FFFF}\x{5FFFE}-\x{5FFFF}\x{6FFFE}-\x{6FFFF}\x{7FFFE}-\x{7FFFF}\x{8FFFE}-\x{8FFFF}\x{9FFFE}-\x{9FFFF}\x{AFFFE}-\x{AFFFF}\x{BFFFE}-\x{BFFFF}\x{CFFFE}-\x{CFFFF}\x{DFFFE}-\x{E0000}\x{E0001}\x{E0002}-\x{E001F}\x{E0020}-\x{E007F}\x{E0080}-\x{E00FF}\x{E0100}-\x{E01EF}\x{E01F0}-\x{E0FFF}\x{EFFFE}-\x{EFFFF}\x{FFFFE}-\x{FFFFF}\x{10FFFE}-\x{10FFFF}]/u';
+    const BIDI_STEP_1_RTL = '/^[\x{0590}\x{05BE}\x{05C0}\x{05C3}\x{05C6}\x{05C8}-\x{05CF}\x{05D0}-\x{05EA}\x{05EB}-\x{05EE}\x{05EF}-\x{05F2}\x{05F3}-\x{05F4}\x{05F5}-\x{05FF}\x{0608}\x{060B}\x{060D}\x{061B}\x{061C}\x{061D}\x{061E}-\x{061F}\x{0620}-\x{063F}\x{0640}\x{0641}-\x{064A}\x{066D}\x{066E}-\x{066F}\x{0671}-\x{06D3}\x{06D4}\x{06D5}\x{06E5}-\x{06E6}\x{06EE}-\x{06EF}\x{06FA}-\x{06FC}\x{06FD}-\x{06FE}\x{06FF}\x{0700}-\x{070D}\x{070E}\x{070F}\x{0710}\x{0712}-\x{072F}\x{074B}-\x{074C}\x{074D}-\x{07A5}\x{07B1}\x{07B2}-\x{07BF}\x{07C0}-\x{07C9}\x{07CA}-\x{07EA}\x{07F4}-\x{07F5}\x{07FA}\x{07FB}-\x{07FC}\x{07FE}-\x{07FF}\x{0800}-\x{0815}\x{081A}\x{0824}\x{0828}\x{082E}-\x{082F}\x{0830}-\x{083E}\x{083F}\x{0840}-\x{0858}\x{085C}-\x{085D}\x{085E}\x{085F}\x{0860}-\x{086A}\x{086B}-\x{086F}\x{0870}-\x{089F}\x{08A0}-\x{08B4}\x{08B5}\x{08B6}-\x{08C7}\x{08C8}-\x{08D2}\x{200F}\x{FB1D}\x{FB1F}-\x{FB28}\x{FB2A}-\x{FB36}\x{FB37}\x{FB38}-\x{FB3C}\x{FB3D}\x{FB3E}\x{FB3F}\x{FB40}-\x{FB41}\x{FB42}\x{FB43}-\x{FB44}\x{FB45}\x{FB46}-\x{FB4F}\x{FB50}-\x{FBB1}\x{FBB2}-\x{FBC1}\x{FBC2}-\x{FBD2}\x{FBD3}-\x{FD3D}\x{FD40}-\x{FD4F}\x{FD50}-\x{FD8F}\x{FD90}-\x{FD91}\x{FD92}-\x{FDC7}\x{FDC8}-\x{FDCF}\x{FDF0}-\x{FDFB}\x{FDFC}\x{FDFE}-\x{FDFF}\x{FE70}-\x{FE74}\x{FE75}\x{FE76}-\x{FEFC}\x{FEFD}-\x{FEFE}\x{10800}-\x{10805}\x{10806}-\x{10807}\x{10808}\x{10809}\x{1080A}-\x{10835}\x{10836}\x{10837}-\x{10838}\x{10839}-\x{1083B}\x{1083C}\x{1083D}-\x{1083E}\x{1083F}-\x{10855}\x{10856}\x{10857}\x{10858}-\x{1085F}\x{10860}-\x{10876}\x{10877}-\x{10878}\x{10879}-\x{1087F}\x{10880}-\x{1089E}\x{1089F}-\x{108A6}\x{108A7}-\x{108AF}\x{108B0}-\x{108DF}\x{108E0}-\x{108F2}\x{108F3}\x{108F4}-\x{108F5}\x{108F6}-\x{108FA}\x{108FB}-\x{108FF}\x{10900}-\x{10915}\x{10916}-\x{1091B}\x{1091C}-\x{1091E}\x{10920}-\x{10939}\x{1093A}-\x{1093E}\x{1093F}\x{10940}-\x{1097F}\x{10980}-\x{109B7}\x{109B8}-\x{109BB}\x{109BC}-\x{109BD}\x{109BE}-\x{109BF}\x{109C0}-\x{109CF}\x{109D0}-\x{109D1}\x{109D2}-\x{109FF}\x{10A00}\x{10A04}\x{10A07}-\x{10A0B}\x{10A10}-\x{10A13}\x{10A14}\x{10A15}-\x{10A17}\x{10A18}\x{10A19}-\x{10A35}\x{10A36}-\x{10A37}\x{10A3B}-\x{10A3E}\x{10A40}-\x{10A48}\x{10A49}-\x{10A4F}\x{10A50}-\x{10A58}\x{10A59}-\x{10A5F}\x{10A60}-\x{10A7C}\x{10A7D}-\x{10A7E}\x{10A7F}\x{10A80}-\x{10A9C}\x{10A9D}-\x{10A9F}\x{10AA0}-\x{10ABF}\x{10AC0}-\x{10AC7}\x{10AC8}\x{10AC9}-\x{10AE4}\x{10AE7}-\x{10AEA}\x{10AEB}-\x{10AEF}\x{10AF0}-\x{10AF6}\x{10AF7}-\x{10AFF}\x{10B00}-\x{10B35}\x{10B36}-\x{10B38}\x{10B40}-\x{10B55}\x{10B56}-\x{10B57}\x{10B58}-\x{10B5F}\x{10B60}-\x{10B72}\x{10B73}-\x{10B77}\x{10B78}-\x{10B7F}\x{10B80}-\x{10B91}\x{10B92}-\x{10B98}\x{10B99}-\x{10B9C}\x{10B9D}-\x{10BA8}\x{10BA9}-\x{10BAF}\x{10BB0}-\x{10BFF}\x{10C00}-\x{10C48}\x{10C49}-\x{10C7F}\x{10C80}-\x{10CB2}\x{10CB3}-\x{10CBF}\x{10CC0}-\x{10CF2}\x{10CF3}-\x{10CF9}\x{10CFA}-\x{10CFF}\x{10D00}-\x{10D23}\x{10D28}-\x{10D2F}\x{10D3A}-\x{10D3F}\x{10D40}-\x{10E5F}\x{10E7F}\x{10E80}-\x{10EA9}\x{10EAA}\x{10EAD}\x{10EAE}-\x{10EAF}\x{10EB0}-\x{10EB1}\x{10EB2}-\x{10EFF}\x{10F00}-\x{10F1C}\x{10F1D}-\x{10F26}\x{10F27}\x{10F28}-\x{10F2F}\x{10F30}-\x{10F45}\x{10F51}-\x{10F54}\x{10F55}-\x{10F59}\x{10F5A}-\x{10F6F}\x{10F70}-\x{10FAF}\x{10FB0}-\x{10FC4}\x{10FC5}-\x{10FCB}\x{10FCC}-\x{10FDF}\x{10FE0}-\x{10FF6}\x{10FF7}-\x{10FFF}\x{1E800}-\x{1E8C4}\x{1E8C5}-\x{1E8C6}\x{1E8C7}-\x{1E8CF}\x{1E8D7}-\x{1E8FF}\x{1E900}-\x{1E943}\x{1E94B}\x{1E94C}-\x{1E94F}\x{1E950}-\x{1E959}\x{1E95A}-\x{1E95D}\x{1E95E}-\x{1E95F}\x{1E960}-\x{1EC6F}\x{1EC70}\x{1EC71}-\x{1ECAB}\x{1ECAC}\x{1ECAD}-\x{1ECAF}\x{1ECB0}\x{1ECB1}-\x{1ECB4}\x{1ECB5}-\x{1ECBF}\x{1ECC0}-\x{1ECFF}\x{1ED00}\x{1ED01}-\x{1ED2D}\x{1ED2E}\x{1ED2F}-\x{1ED3D}\x{1ED3E}-\x{1ED4F}\x{1ED50}-\x{1EDFF}\x{1EE00}-\x{1EE03}\x{1EE04}\x{1EE05}-\x{1EE1F}\x{1EE20}\x{1EE21}-\x{1EE22}\x{1EE23}\x{1EE24}\x{1EE25}-\x{1EE26}\x{1EE27}\x{1EE28}\x{1EE29}-\x{1EE32}\x{1EE33}\x{1EE34}-\x{1EE37}\x{1EE38}\x{1EE39}\x{1EE3A}\x{1EE3B}\x{1EE3C}-\x{1EE41}\x{1EE42}\x{1EE43}-\x{1EE46}\x{1EE47}\x{1EE48}\x{1EE49}\x{1EE4A}\x{1EE4B}\x{1EE4C}\x{1EE4D}-\x{1EE4F}\x{1EE50}\x{1EE51}-\x{1EE52}\x{1EE53}\x{1EE54}\x{1EE55}-\x{1EE56}\x{1EE57}\x{1EE58}\x{1EE59}\x{1EE5A}\x{1EE5B}\x{1EE5C}\x{1EE5D}\x{1EE5E}\x{1EE5F}\x{1EE60}\x{1EE61}-\x{1EE62}\x{1EE63}\x{1EE64}\x{1EE65}-\x{1EE66}\x{1EE67}-\x{1EE6A}\x{1EE6B}\x{1EE6C}-\x{1EE72}\x{1EE73}\x{1EE74}-\x{1EE77}\x{1EE78}\x{1EE79}-\x{1EE7C}\x{1EE7D}\x{1EE7E}\x{1EE7F}\x{1EE80}-\x{1EE89}\x{1EE8A}\x{1EE8B}-\x{1EE9B}\x{1EE9C}-\x{1EEA0}\x{1EEA1}-\x{1EEA3}\x{1EEA4}\x{1EEA5}-\x{1EEA9}\x{1EEAA}\x{1EEAB}-\x{1EEBB}\x{1EEBC}-\x{1EEEF}\x{1EEF2}-\x{1EEFF}\x{1EF00}-\x{1EFFF}]/u';
+    const BIDI_STEP_2 = '/[^\x{0000}-\x{0008}\x{000E}-\x{001B}\x{0021}-\x{0022}\x{0023}\x{0024}\x{0025}\x{0026}-\x{0027}\x{0028}\x{0029}\x{002A}\x{002B}\x{002C}\x{002D}\x{002E}-\x{002F}\x{0030}-\x{0039}\x{003A}\x{003B}\x{003C}-\x{003E}\x{003F}-\x{0040}\x{005B}\x{005C}\x{005D}\x{005E}\x{005F}\x{0060}\x{007B}\x{007C}\x{007D}\x{007E}\x{007F}-\x{0084}\x{0086}-\x{009F}\x{00A0}\x{00A1}\x{00A2}-\x{00A5}\x{00A6}\x{00A7}\x{00A8}\x{00A9}\x{00AB}\x{00AC}\x{00AD}\x{00AE}\x{00AF}\x{00B0}\x{00B1}\x{00B2}-\x{00B3}\x{00B4}\x{00B6}-\x{00B7}\x{00B8}\x{00B9}\x{00BB}\x{00BC}-\x{00BE}\x{00BF}\x{00D7}\x{00F7}\x{02B9}-\x{02BA}\x{02C2}-\x{02C5}\x{02C6}-\x{02CF}\x{02D2}-\x{02DF}\x{02E5}-\x{02EB}\x{02EC}\x{02ED}\x{02EF}-\x{02FF}\x{0300}-\x{036F}\x{0374}\x{0375}\x{037E}\x{0384}-\x{0385}\x{0387}\x{03F6}\x{0483}-\x{0487}\x{0488}-\x{0489}\x{058A}\x{058D}-\x{058E}\x{058F}\x{0590}\x{0591}-\x{05BD}\x{05BE}\x{05BF}\x{05C0}\x{05C1}-\x{05C2}\x{05C3}\x{05C4}-\x{05C5}\x{05C6}\x{05C7}\x{05C8}-\x{05CF}\x{05D0}-\x{05EA}\x{05EB}-\x{05EE}\x{05EF}-\x{05F2}\x{05F3}-\x{05F4}\x{05F5}-\x{05FF}\x{0600}-\x{0605}\x{0606}-\x{0607}\x{0608}\x{0609}-\x{060A}\x{060B}\x{060C}\x{060D}\x{060E}-\x{060F}\x{0610}-\x{061A}\x{061B}\x{061C}\x{061D}\x{061E}-\x{061F}\x{0620}-\x{063F}\x{0640}\x{0641}-\x{064A}\x{064B}-\x{065F}\x{0660}-\x{0669}\x{066A}\x{066B}-\x{066C}\x{066D}\x{066E}-\x{066F}\x{0670}\x{0671}-\x{06D3}\x{06D4}\x{06D5}\x{06D6}-\x{06DC}\x{06DD}\x{06DE}\x{06DF}-\x{06E4}\x{06E5}-\x{06E6}\x{06E7}-\x{06E8}\x{06E9}\x{06EA}-\x{06ED}\x{06EE}-\x{06EF}\x{06F0}-\x{06F9}\x{06FA}-\x{06FC}\x{06FD}-\x{06FE}\x{06FF}\x{0700}-\x{070D}\x{070E}\x{070F}\x{0710}\x{0711}\x{0712}-\x{072F}\x{0730}-\x{074A}\x{074B}-\x{074C}\x{074D}-\x{07A5}\x{07A6}-\x{07B0}\x{07B1}\x{07B2}-\x{07BF}\x{07C0}-\x{07C9}\x{07CA}-\x{07EA}\x{07EB}-\x{07F3}\x{07F4}-\x{07F5}\x{07F6}\x{07F7}-\x{07F9}\x{07FA}\x{07FB}-\x{07FC}\x{07FD}\x{07FE}-\x{07FF}\x{0800}-\x{0815}\x{0816}-\x{0819}\x{081A}\x{081B}-\x{0823}\x{0824}\x{0825}-\x{0827}\x{0828}\x{0829}-\x{082D}\x{082E}-\x{082F}\x{0830}-\x{083E}\x{083F}\x{0840}-\x{0858}\x{0859}-\x{085B}\x{085C}-\x{085D}\x{085E}\x{085F}\x{0860}-\x{086A}\x{086B}-\x{086F}\x{0870}-\x{089F}\x{08A0}-\x{08B4}\x{08B5}\x{08B6}-\x{08C7}\x{08C8}-\x{08D2}\x{08D3}-\x{08E1}\x{08E2}\x{08E3}-\x{0902}\x{093A}\x{093C}\x{0941}-\x{0948}\x{094D}\x{0951}-\x{0957}\x{0962}-\x{0963}\x{0981}\x{09BC}\x{09C1}-\x{09C4}\x{09CD}\x{09E2}-\x{09E3}\x{09F2}-\x{09F3}\x{09FB}\x{09FE}\x{0A01}-\x{0A02}\x{0A3C}\x{0A41}-\x{0A42}\x{0A47}-\x{0A48}\x{0A4B}-\x{0A4D}\x{0A51}\x{0A70}-\x{0A71}\x{0A75}\x{0A81}-\x{0A82}\x{0ABC}\x{0AC1}-\x{0AC5}\x{0AC7}-\x{0AC8}\x{0ACD}\x{0AE2}-\x{0AE3}\x{0AF1}\x{0AFA}-\x{0AFF}\x{0B01}\x{0B3C}\x{0B3F}\x{0B41}-\x{0B44}\x{0B4D}\x{0B55}-\x{0B56}\x{0B62}-\x{0B63}\x{0B82}\x{0BC0}\x{0BCD}\x{0BF3}-\x{0BF8}\x{0BF9}\x{0BFA}\x{0C00}\x{0C04}\x{0C3E}-\x{0C40}\x{0C46}-\x{0C48}\x{0C4A}-\x{0C4D}\x{0C55}-\x{0C56}\x{0C62}-\x{0C63}\x{0C78}-\x{0C7E}\x{0C81}\x{0CBC}\x{0CCC}-\x{0CCD}\x{0CE2}-\x{0CE3}\x{0D00}-\x{0D01}\x{0D3B}-\x{0D3C}\x{0D41}-\x{0D44}\x{0D4D}\x{0D62}-\x{0D63}\x{0D81}\x{0DCA}\x{0DD2}-\x{0DD4}\x{0DD6}\x{0E31}\x{0E34}-\x{0E3A}\x{0E3F}\x{0E47}-\x{0E4E}\x{0EB1}\x{0EB4}-\x{0EBC}\x{0EC8}-\x{0ECD}\x{0F18}-\x{0F19}\x{0F35}\x{0F37}\x{0F39}\x{0F3A}\x{0F3B}\x{0F3C}\x{0F3D}\x{0F71}-\x{0F7E}\x{0F80}-\x{0F84}\x{0F86}-\x{0F87}\x{0F8D}-\x{0F97}\x{0F99}-\x{0FBC}\x{0FC6}\x{102D}-\x{1030}\x{1032}-\x{1037}\x{1039}-\x{103A}\x{103D}-\x{103E}\x{1058}-\x{1059}\x{105E}-\x{1060}\x{1071}-\x{1074}\x{1082}\x{1085}-\x{1086}\x{108D}\x{109D}\x{135D}-\x{135F}\x{1390}-\x{1399}\x{1400}\x{169B}\x{169C}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}-\x{1753}\x{1772}-\x{1773}\x{17B4}-\x{17B5}\x{17B7}-\x{17BD}\x{17C6}\x{17C9}-\x{17D3}\x{17DB}\x{17DD}\x{17F0}-\x{17F9}\x{1800}-\x{1805}\x{1806}\x{1807}-\x{180A}\x{180B}-\x{180D}\x{180E}\x{1885}-\x{1886}\x{18A9}\x{1920}-\x{1922}\x{1927}-\x{1928}\x{1932}\x{1939}-\x{193B}\x{1940}\x{1944}-\x{1945}\x{19DE}-\x{19FF}\x{1A17}-\x{1A18}\x{1A1B}\x{1A56}\x{1A58}-\x{1A5E}\x{1A60}\x{1A62}\x{1A65}-\x{1A6C}\x{1A73}-\x{1A7C}\x{1A7F}\x{1AB0}-\x{1ABD}\x{1ABE}\x{1ABF}-\x{1AC0}\x{1B00}-\x{1B03}\x{1B34}\x{1B36}-\x{1B3A}\x{1B3C}\x{1B42}\x{1B6B}-\x{1B73}\x{1B80}-\x{1B81}\x{1BA2}-\x{1BA5}\x{1BA8}-\x{1BA9}\x{1BAB}-\x{1BAD}\x{1BE6}\x{1BE8}-\x{1BE9}\x{1BED}\x{1BEF}-\x{1BF1}\x{1C2C}-\x{1C33}\x{1C36}-\x{1C37}\x{1CD0}-\x{1CD2}\x{1CD4}-\x{1CE0}\x{1CE2}-\x{1CE8}\x{1CED}\x{1CF4}\x{1CF8}-\x{1CF9}\x{1DC0}-\x{1DF9}\x{1DFB}-\x{1DFF}\x{1FBD}\x{1FBF}-\x{1FC1}\x{1FCD}-\x{1FCF}\x{1FDD}-\x{1FDF}\x{1FED}-\x{1FEF}\x{1FFD}-\x{1FFE}\x{200B}-\x{200D}\x{200F}\x{2010}-\x{2015}\x{2016}-\x{2017}\x{2018}\x{2019}\x{201A}\x{201B}-\x{201C}\x{201D}\x{201E}\x{201F}\x{2020}-\x{2027}\x{202F}\x{2030}-\x{2034}\x{2035}-\x{2038}\x{2039}\x{203A}\x{203B}-\x{203E}\x{203F}-\x{2040}\x{2041}-\x{2043}\x{2044}\x{2045}\x{2046}\x{2047}-\x{2051}\x{2052}\x{2053}\x{2054}\x{2055}-\x{205E}\x{2060}-\x{2064}\x{2065}\x{206A}-\x{206F}\x{2070}\x{2074}-\x{2079}\x{207A}-\x{207B}\x{207C}\x{207D}\x{207E}\x{2080}-\x{2089}\x{208A}-\x{208B}\x{208C}\x{208D}\x{208E}\x{20A0}-\x{20BF}\x{20C0}-\x{20CF}\x{20D0}-\x{20DC}\x{20DD}-\x{20E0}\x{20E1}\x{20E2}-\x{20E4}\x{20E5}-\x{20F0}\x{2100}-\x{2101}\x{2103}-\x{2106}\x{2108}-\x{2109}\x{2114}\x{2116}-\x{2117}\x{2118}\x{211E}-\x{2123}\x{2125}\x{2127}\x{2129}\x{212E}\x{213A}-\x{213B}\x{2140}-\x{2144}\x{214A}\x{214B}\x{214C}-\x{214D}\x{2150}-\x{215F}\x{2189}\x{218A}-\x{218B}\x{2190}-\x{2194}\x{2195}-\x{2199}\x{219A}-\x{219B}\x{219C}-\x{219F}\x{21A0}\x{21A1}-\x{21A2}\x{21A3}\x{21A4}-\x{21A5}\x{21A6}\x{21A7}-\x{21AD}\x{21AE}\x{21AF}-\x{21CD}\x{21CE}-\x{21CF}\x{21D0}-\x{21D1}\x{21D2}\x{21D3}\x{21D4}\x{21D5}-\x{21F3}\x{21F4}-\x{2211}\x{2212}\x{2213}\x{2214}-\x{22FF}\x{2300}-\x{2307}\x{2308}\x{2309}\x{230A}\x{230B}\x{230C}-\x{231F}\x{2320}-\x{2321}\x{2322}-\x{2328}\x{2329}\x{232A}\x{232B}-\x{2335}\x{237B}\x{237C}\x{237D}-\x{2394}\x{2396}-\x{239A}\x{239B}-\x{23B3}\x{23B4}-\x{23DB}\x{23DC}-\x{23E1}\x{23E2}-\x{2426}\x{2440}-\x{244A}\x{2460}-\x{2487}\x{2488}-\x{249B}\x{24EA}-\x{24FF}\x{2500}-\x{25B6}\x{25B7}\x{25B8}-\x{25C0}\x{25C1}\x{25C2}-\x{25F7}\x{25F8}-\x{25FF}\x{2600}-\x{266E}\x{266F}\x{2670}-\x{26AB}\x{26AD}-\x{2767}\x{2768}\x{2769}\x{276A}\x{276B}\x{276C}\x{276D}\x{276E}\x{276F}\x{2770}\x{2771}\x{2772}\x{2773}\x{2774}\x{2775}\x{2776}-\x{2793}\x{2794}-\x{27BF}\x{27C0}-\x{27C4}\x{27C5}\x{27C6}\x{27C7}-\x{27E5}\x{27E6}\x{27E7}\x{27E8}\x{27E9}\x{27EA}\x{27EB}\x{27EC}\x{27ED}\x{27EE}\x{27EF}\x{27F0}-\x{27FF}\x{2900}-\x{2982}\x{2983}\x{2984}\x{2985}\x{2986}\x{2987}\x{2988}\x{2989}\x{298A}\x{298B}\x{298C}\x{298D}\x{298E}\x{298F}\x{2990}\x{2991}\x{2992}\x{2993}\x{2994}\x{2995}\x{2996}\x{2997}\x{2998}\x{2999}-\x{29D7}\x{29D8}\x{29D9}\x{29DA}\x{29DB}\x{29DC}-\x{29FB}\x{29FC}\x{29FD}\x{29FE}-\x{2AFF}\x{2B00}-\x{2B2F}\x{2B30}-\x{2B44}\x{2B45}-\x{2B46}\x{2B47}-\x{2B4C}\x{2B4D}-\x{2B73}\x{2B76}-\x{2B95}\x{2B97}-\x{2BFF}\x{2CE5}-\x{2CEA}\x{2CEF}-\x{2CF1}\x{2CF9}-\x{2CFC}\x{2CFD}\x{2CFE}-\x{2CFF}\x{2D7F}\x{2DE0}-\x{2DFF}\x{2E00}-\x{2E01}\x{2E02}\x{2E03}\x{2E04}\x{2E05}\x{2E06}-\x{2E08}\x{2E09}\x{2E0A}\x{2E0B}\x{2E0C}\x{2E0D}\x{2E0E}-\x{2E16}\x{2E17}\x{2E18}-\x{2E19}\x{2E1A}\x{2E1B}\x{2E1C}\x{2E1D}\x{2E1E}-\x{2E1F}\x{2E20}\x{2E21}\x{2E22}\x{2E23}\x{2E24}\x{2E25}\x{2E26}\x{2E27}\x{2E28}\x{2E29}\x{2E2A}-\x{2E2E}\x{2E2F}\x{2E30}-\x{2E39}\x{2E3A}-\x{2E3B}\x{2E3C}-\x{2E3F}\x{2E40}\x{2E41}\x{2E42}\x{2E43}-\x{2E4F}\x{2E50}-\x{2E51}\x{2E52}\x{2E80}-\x{2E99}\x{2E9B}-\x{2EF3}\x{2F00}-\x{2FD5}\x{2FF0}-\x{2FFB}\x{3001}-\x{3003}\x{3004}\x{3008}\x{3009}\x{300A}\x{300B}\x{300C}\x{300D}\x{300E}\x{300F}\x{3010}\x{3011}\x{3012}-\x{3013}\x{3014}\x{3015}\x{3016}\x{3017}\x{3018}\x{3019}\x{301A}\x{301B}\x{301C}\x{301D}\x{301E}-\x{301F}\x{3020}\x{302A}-\x{302D}\x{3030}\x{3036}-\x{3037}\x{303D}\x{303E}-\x{303F}\x{3099}-\x{309A}\x{309B}-\x{309C}\x{30A0}\x{30FB}\x{31C0}-\x{31E3}\x{321D}-\x{321E}\x{3250}\x{3251}-\x{325F}\x{327C}-\x{327E}\x{32B1}-\x{32BF}\x{32CC}-\x{32CF}\x{3377}-\x{337A}\x{33DE}-\x{33DF}\x{33FF}\x{4DC0}-\x{4DFF}\x{A490}-\x{A4C6}\x{A60D}-\x{A60F}\x{A66F}\x{A670}-\x{A672}\x{A673}\x{A674}-\x{A67D}\x{A67E}\x{A67F}\x{A69E}-\x{A69F}\x{A6F0}-\x{A6F1}\x{A700}-\x{A716}\x{A717}-\x{A71F}\x{A720}-\x{A721}\x{A788}\x{A802}\x{A806}\x{A80B}\x{A825}-\x{A826}\x{A828}-\x{A82B}\x{A82C}\x{A838}\x{A839}\x{A874}-\x{A877}\x{A8C4}-\x{A8C5}\x{A8E0}-\x{A8F1}\x{A8FF}\x{A926}-\x{A92D}\x{A947}-\x{A951}\x{A980}-\x{A982}\x{A9B3}\x{A9B6}-\x{A9B9}\x{A9BC}-\x{A9BD}\x{A9E5}\x{AA29}-\x{AA2E}\x{AA31}-\x{AA32}\x{AA35}-\x{AA36}\x{AA43}\x{AA4C}\x{AA7C}\x{AAB0}\x{AAB2}-\x{AAB4}\x{AAB7}-\x{AAB8}\x{AABE}-\x{AABF}\x{AAC1}\x{AAEC}-\x{AAED}\x{AAF6}\x{AB6A}-\x{AB6B}\x{ABE5}\x{ABE8}\x{ABED}\x{FB1D}\x{FB1E}\x{FB1F}-\x{FB28}\x{FB29}\x{FB2A}-\x{FB36}\x{FB37}\x{FB38}-\x{FB3C}\x{FB3D}\x{FB3E}\x{FB3F}\x{FB40}-\x{FB41}\x{FB42}\x{FB43}-\x{FB44}\x{FB45}\x{FB46}-\x{FB4F}\x{FB50}-\x{FBB1}\x{FBB2}-\x{FBC1}\x{FBC2}-\x{FBD2}\x{FBD3}-\x{FD3D}\x{FD3E}\x{FD3F}\x{FD40}-\x{FD4F}\x{FD50}-\x{FD8F}\x{FD90}-\x{FD91}\x{FD92}-\x{FDC7}\x{FDC8}-\x{FDCF}\x{FDD0}-\x{FDEF}\x{FDF0}-\x{FDFB}\x{FDFC}\x{FDFD}\x{FDFE}-\x{FDFF}\x{FE00}-\x{FE0F}\x{FE10}-\x{FE16}\x{FE17}\x{FE18}\x{FE19}\x{FE20}-\x{FE2F}\x{FE30}\x{FE31}-\x{FE32}\x{FE33}-\x{FE34}\x{FE35}\x{FE36}\x{FE37}\x{FE38}\x{FE39}\x{FE3A}\x{FE3B}\x{FE3C}\x{FE3D}\x{FE3E}\x{FE3F}\x{FE40}\x{FE41}\x{FE42}\x{FE43}\x{FE44}\x{FE45}-\x{FE46}\x{FE47}\x{FE48}\x{FE49}-\x{FE4C}\x{FE4D}-\x{FE4F}\x{FE50}\x{FE51}\x{FE52}\x{FE54}\x{FE55}\x{FE56}-\x{FE57}\x{FE58}\x{FE59}\x{FE5A}\x{FE5B}\x{FE5C}\x{FE5D}\x{FE5E}\x{FE5F}\x{FE60}-\x{FE61}\x{FE62}\x{FE63}\x{FE64}-\x{FE66}\x{FE68}\x{FE69}\x{FE6A}\x{FE6B}\x{FE70}-\x{FE74}\x{FE75}\x{FE76}-\x{FEFC}\x{FEFD}-\x{FEFE}\x{FEFF}\x{FF01}-\x{FF02}\x{FF03}\x{FF04}\x{FF05}\x{FF06}-\x{FF07}\x{FF08}\x{FF09}\x{FF0A}\x{FF0B}\x{FF0C}\x{FF0D}\x{FF0E}-\x{FF0F}\x{FF10}-\x{FF19}\x{FF1A}\x{FF1B}\x{FF1C}-\x{FF1E}\x{FF1F}-\x{FF20}\x{FF3B}\x{FF3C}\x{FF3D}\x{FF3E}\x{FF3F}\x{FF40}\x{FF5B}\x{FF5C}\x{FF5D}\x{FF5E}\x{FF5F}\x{FF60}\x{FF61}\x{FF62}\x{FF63}\x{FF64}-\x{FF65}\x{FFE0}-\x{FFE1}\x{FFE2}\x{FFE3}\x{FFE4}\x{FFE5}-\x{FFE6}\x{FFE8}\x{FFE9}-\x{FFEC}\x{FFED}-\x{FFEE}\x{FFF0}-\x{FFF8}\x{FFF9}-\x{FFFB}\x{FFFC}-\x{FFFD}\x{FFFE}-\x{FFFF}\x{10101}\x{10140}-\x{10174}\x{10175}-\x{10178}\x{10179}-\x{10189}\x{1018A}-\x{1018B}\x{1018C}\x{10190}-\x{1019C}\x{101A0}\x{101FD}\x{102E0}\x{102E1}-\x{102FB}\x{10376}-\x{1037A}\x{10800}-\x{10805}\x{10806}-\x{10807}\x{10808}\x{10809}\x{1080A}-\x{10835}\x{10836}\x{10837}-\x{10838}\x{10839}-\x{1083B}\x{1083C}\x{1083D}-\x{1083E}\x{1083F}-\x{10855}\x{10856}\x{10857}\x{10858}-\x{1085F}\x{10860}-\x{10876}\x{10877}-\x{10878}\x{10879}-\x{1087F}\x{10880}-\x{1089E}\x{1089F}-\x{108A6}\x{108A7}-\x{108AF}\x{108B0}-\x{108DF}\x{108E0}-\x{108F2}\x{108F3}\x{108F4}-\x{108F5}\x{108F6}-\x{108FA}\x{108FB}-\x{108FF}\x{10900}-\x{10915}\x{10916}-\x{1091B}\x{1091C}-\x{1091E}\x{1091F}\x{10920}-\x{10939}\x{1093A}-\x{1093E}\x{1093F}\x{10940}-\x{1097F}\x{10980}-\x{109B7}\x{109B8}-\x{109BB}\x{109BC}-\x{109BD}\x{109BE}-\x{109BF}\x{109C0}-\x{109CF}\x{109D0}-\x{109D1}\x{109D2}-\x{109FF}\x{10A00}\x{10A01}-\x{10A03}\x{10A04}\x{10A05}-\x{10A06}\x{10A07}-\x{10A0B}\x{10A0C}-\x{10A0F}\x{10A10}-\x{10A13}\x{10A14}\x{10A15}-\x{10A17}\x{10A18}\x{10A19}-\x{10A35}\x{10A36}-\x{10A37}\x{10A38}-\x{10A3A}\x{10A3B}-\x{10A3E}\x{10A3F}\x{10A40}-\x{10A48}\x{10A49}-\x{10A4F}\x{10A50}-\x{10A58}\x{10A59}-\x{10A5F}\x{10A60}-\x{10A7C}\x{10A7D}-\x{10A7E}\x{10A7F}\x{10A80}-\x{10A9C}\x{10A9D}-\x{10A9F}\x{10AA0}-\x{10ABF}\x{10AC0}-\x{10AC7}\x{10AC8}\x{10AC9}-\x{10AE4}\x{10AE5}-\x{10AE6}\x{10AE7}-\x{10AEA}\x{10AEB}-\x{10AEF}\x{10AF0}-\x{10AF6}\x{10AF7}-\x{10AFF}\x{10B00}-\x{10B35}\x{10B36}-\x{10B38}\x{10B39}-\x{10B3F}\x{10B40}-\x{10B55}\x{10B56}-\x{10B57}\x{10B58}-\x{10B5F}\x{10B60}-\x{10B72}\x{10B73}-\x{10B77}\x{10B78}-\x{10B7F}\x{10B80}-\x{10B91}\x{10B92}-\x{10B98}\x{10B99}-\x{10B9C}\x{10B9D}-\x{10BA8}\x{10BA9}-\x{10BAF}\x{10BB0}-\x{10BFF}\x{10C00}-\x{10C48}\x{10C49}-\x{10C7F}\x{10C80}-\x{10CB2}\x{10CB3}-\x{10CBF}\x{10CC0}-\x{10CF2}\x{10CF3}-\x{10CF9}\x{10CFA}-\x{10CFF}\x{10D00}-\x{10D23}\x{10D24}-\x{10D27}\x{10D28}-\x{10D2F}\x{10D30}-\x{10D39}\x{10D3A}-\x{10D3F}\x{10D40}-\x{10E5F}\x{10E60}-\x{10E7E}\x{10E7F}\x{10E80}-\x{10EA9}\x{10EAA}\x{10EAB}-\x{10EAC}\x{10EAD}\x{10EAE}-\x{10EAF}\x{10EB0}-\x{10EB1}\x{10EB2}-\x{10EFF}\x{10F00}-\x{10F1C}\x{10F1D}-\x{10F26}\x{10F27}\x{10F28}-\x{10F2F}\x{10F30}-\x{10F45}\x{10F46}-\x{10F50}\x{10F51}-\x{10F54}\x{10F55}-\x{10F59}\x{10F5A}-\x{10F6F}\x{10F70}-\x{10FAF}\x{10FB0}-\x{10FC4}\x{10FC5}-\x{10FCB}\x{10FCC}-\x{10FDF}\x{10FE0}-\x{10FF6}\x{10FF7}-\x{10FFF}\x{11001}\x{11038}-\x{11046}\x{11052}-\x{11065}\x{1107F}-\x{11081}\x{110B3}-\x{110B6}\x{110B9}-\x{110BA}\x{11100}-\x{11102}\x{11127}-\x{1112B}\x{1112D}-\x{11134}\x{11173}\x{11180}-\x{11181}\x{111B6}-\x{111BE}\x{111C9}-\x{111CC}\x{111CF}\x{1122F}-\x{11231}\x{11234}\x{11236}-\x{11237}\x{1123E}\x{112DF}\x{112E3}-\x{112EA}\x{11300}-\x{11301}\x{1133B}-\x{1133C}\x{11340}\x{11366}-\x{1136C}\x{11370}-\x{11374}\x{11438}-\x{1143F}\x{11442}-\x{11444}\x{11446}\x{1145E}\x{114B3}-\x{114B8}\x{114BA}\x{114BF}-\x{114C0}\x{114C2}-\x{114C3}\x{115B2}-\x{115B5}\x{115BC}-\x{115BD}\x{115BF}-\x{115C0}\x{115DC}-\x{115DD}\x{11633}-\x{1163A}\x{1163D}\x{1163F}-\x{11640}\x{11660}-\x{1166C}\x{116AB}\x{116AD}\x{116B0}-\x{116B5}\x{116B7}\x{1171D}-\x{1171F}\x{11722}-\x{11725}\x{11727}-\x{1172B}\x{1182F}-\x{11837}\x{11839}-\x{1183A}\x{1193B}-\x{1193C}\x{1193E}\x{11943}\x{119D4}-\x{119D7}\x{119DA}-\x{119DB}\x{119E0}\x{11A01}-\x{11A06}\x{11A09}-\x{11A0A}\x{11A33}-\x{11A38}\x{11A3B}-\x{11A3E}\x{11A47}\x{11A51}-\x{11A56}\x{11A59}-\x{11A5B}\x{11A8A}-\x{11A96}\x{11A98}-\x{11A99}\x{11C30}-\x{11C36}\x{11C38}-\x{11C3D}\x{11C92}-\x{11CA7}\x{11CAA}-\x{11CB0}\x{11CB2}-\x{11CB3}\x{11CB5}-\x{11CB6}\x{11D31}-\x{11D36}\x{11D3A}\x{11D3C}-\x{11D3D}\x{11D3F}-\x{11D45}\x{11D47}\x{11D90}-\x{11D91}\x{11D95}\x{11D97}\x{11EF3}-\x{11EF4}\x{11FD5}-\x{11FDC}\x{11FDD}-\x{11FE0}\x{11FE1}-\x{11FF1}\x{16AF0}-\x{16AF4}\x{16B30}-\x{16B36}\x{16F4F}\x{16F8F}-\x{16F92}\x{16FE2}\x{16FE4}\x{1BC9D}-\x{1BC9E}\x{1BCA0}-\x{1BCA3}\x{1D167}-\x{1D169}\x{1D173}-\x{1D17A}\x{1D17B}-\x{1D182}\x{1D185}-\x{1D18B}\x{1D1AA}-\x{1D1AD}\x{1D200}-\x{1D241}\x{1D242}-\x{1D244}\x{1D245}\x{1D300}-\x{1D356}\x{1D6DB}\x{1D715}\x{1D74F}\x{1D789}\x{1D7C3}\x{1D7CE}-\x{1D7FF}\x{1DA00}-\x{1DA36}\x{1DA3B}-\x{1DA6C}\x{1DA75}\x{1DA84}\x{1DA9B}-\x{1DA9F}\x{1DAA1}-\x{1DAAF}\x{1E000}-\x{1E006}\x{1E008}-\x{1E018}\x{1E01B}-\x{1E021}\x{1E023}-\x{1E024}\x{1E026}-\x{1E02A}\x{1E130}-\x{1E136}\x{1E2EC}-\x{1E2EF}\x{1E2FF}\x{1E800}-\x{1E8C4}\x{1E8C5}-\x{1E8C6}\x{1E8C7}-\x{1E8CF}\x{1E8D0}-\x{1E8D6}\x{1E8D7}-\x{1E8FF}\x{1E900}-\x{1E943}\x{1E944}-\x{1E94A}\x{1E94B}\x{1E94C}-\x{1E94F}\x{1E950}-\x{1E959}\x{1E95A}-\x{1E95D}\x{1E95E}-\x{1E95F}\x{1E960}-\x{1EC6F}\x{1EC70}\x{1EC71}-\x{1ECAB}\x{1ECAC}\x{1ECAD}-\x{1ECAF}\x{1ECB0}\x{1ECB1}-\x{1ECB4}\x{1ECB5}-\x{1ECBF}\x{1ECC0}-\x{1ECFF}\x{1ED00}\x{1ED01}-\x{1ED2D}\x{1ED2E}\x{1ED2F}-\x{1ED3D}\x{1ED3E}-\x{1ED4F}\x{1ED50}-\x{1EDFF}\x{1EE00}-\x{1EE03}\x{1EE04}\x{1EE05}-\x{1EE1F}\x{1EE20}\x{1EE21}-\x{1EE22}\x{1EE23}\x{1EE24}\x{1EE25}-\x{1EE26}\x{1EE27}\x{1EE28}\x{1EE29}-\x{1EE32}\x{1EE33}\x{1EE34}-\x{1EE37}\x{1EE38}\x{1EE39}\x{1EE3A}\x{1EE3B}\x{1EE3C}-\x{1EE41}\x{1EE42}\x{1EE43}-\x{1EE46}\x{1EE47}\x{1EE48}\x{1EE49}\x{1EE4A}\x{1EE4B}\x{1EE4C}\x{1EE4D}-\x{1EE4F}\x{1EE50}\x{1EE51}-\x{1EE52}\x{1EE53}\x{1EE54}\x{1EE55}-\x{1EE56}\x{1EE57}\x{1EE58}\x{1EE59}\x{1EE5A}\x{1EE5B}\x{1EE5C}\x{1EE5D}\x{1EE5E}\x{1EE5F}\x{1EE60}\x{1EE61}-\x{1EE62}\x{1EE63}\x{1EE64}\x{1EE65}-\x{1EE66}\x{1EE67}-\x{1EE6A}\x{1EE6B}\x{1EE6C}-\x{1EE72}\x{1EE73}\x{1EE74}-\x{1EE77}\x{1EE78}\x{1EE79}-\x{1EE7C}\x{1EE7D}\x{1EE7E}\x{1EE7F}\x{1EE80}-\x{1EE89}\x{1EE8A}\x{1EE8B}-\x{1EE9B}\x{1EE9C}-\x{1EEA0}\x{1EEA1}-\x{1EEA3}\x{1EEA4}\x{1EEA5}-\x{1EEA9}\x{1EEAA}\x{1EEAB}-\x{1EEBB}\x{1EEBC}-\x{1EEEF}\x{1EEF0}-\x{1EEF1}\x{1EEF2}-\x{1EEFF}\x{1EF00}-\x{1EFFF}\x{1F000}-\x{1F02B}\x{1F030}-\x{1F093}\x{1F0A0}-\x{1F0AE}\x{1F0B1}-\x{1F0BF}\x{1F0C1}-\x{1F0CF}\x{1F0D1}-\x{1F0F5}\x{1F100}-\x{1F10A}\x{1F10B}-\x{1F10C}\x{1F10D}-\x{1F10F}\x{1F12F}\x{1F16A}-\x{1F16F}\x{1F1AD}\x{1F260}-\x{1F265}\x{1F300}-\x{1F3FA}\x{1F3FB}-\x{1F3FF}\x{1F400}-\x{1F6D7}\x{1F6E0}-\x{1F6EC}\x{1F6F0}-\x{1F6FC}\x{1F700}-\x{1F773}\x{1F780}-\x{1F7D8}\x{1F7E0}-\x{1F7EB}\x{1F800}-\x{1F80B}\x{1F810}-\x{1F847}\x{1F850}-\x{1F859}\x{1F860}-\x{1F887}\x{1F890}-\x{1F8AD}\x{1F8B0}-\x{1F8B1}\x{1F900}-\x{1F978}\x{1F97A}-\x{1F9CB}\x{1F9CD}-\x{1FA53}\x{1FA60}-\x{1FA6D}\x{1FA70}-\x{1FA74}\x{1FA78}-\x{1FA7A}\x{1FA80}-\x{1FA86}\x{1FA90}-\x{1FAA8}\x{1FAB0}-\x{1FAB6}\x{1FAC0}-\x{1FAC2}\x{1FAD0}-\x{1FAD6}\x{1FB00}-\x{1FB92}\x{1FB94}-\x{1FBCA}\x{1FBF0}-\x{1FBF9}\x{1FFFE}-\x{1FFFF}\x{2FFFE}-\x{2FFFF}\x{3FFFE}-\x{3FFFF}\x{4FFFE}-\x{4FFFF}\x{5FFFE}-\x{5FFFF}\x{6FFFE}-\x{6FFFF}\x{7FFFE}-\x{7FFFF}\x{8FFFE}-\x{8FFFF}\x{9FFFE}-\x{9FFFF}\x{AFFFE}-\x{AFFFF}\x{BFFFE}-\x{BFFFF}\x{CFFFE}-\x{CFFFF}\x{DFFFE}-\x{E0000}\x{E0001}\x{E0002}-\x{E001F}\x{E0020}-\x{E007F}\x{E0080}-\x{E00FF}\x{E0100}-\x{E01EF}\x{E01F0}-\x{E0FFF}\x{EFFFE}-\x{EFFFF}\x{FFFFE}-\x{FFFFF}\x{10FFFE}-\x{10FFFF}]/u';
+    const BIDI_STEP_3 = '/[\x{0030}-\x{0039}\x{00B2}-\x{00B3}\x{00B9}\x{0590}\x{05BE}\x{05C0}\x{05C3}\x{05C6}\x{05C8}-\x{05CF}\x{05D0}-\x{05EA}\x{05EB}-\x{05EE}\x{05EF}-\x{05F2}\x{05F3}-\x{05F4}\x{05F5}-\x{05FF}\x{0600}-\x{0605}\x{0608}\x{060B}\x{060D}\x{061B}\x{061C}\x{061D}\x{061E}-\x{061F}\x{0620}-\x{063F}\x{0640}\x{0641}-\x{064A}\x{0660}-\x{0669}\x{066B}-\x{066C}\x{066D}\x{066E}-\x{066F}\x{0671}-\x{06D3}\x{06D4}\x{06D5}\x{06DD}\x{06E5}-\x{06E6}\x{06EE}-\x{06EF}\x{06F0}-\x{06F9}\x{06FA}-\x{06FC}\x{06FD}-\x{06FE}\x{06FF}\x{0700}-\x{070D}\x{070E}\x{070F}\x{0710}\x{0712}-\x{072F}\x{074B}-\x{074C}\x{074D}-\x{07A5}\x{07B1}\x{07B2}-\x{07BF}\x{07C0}-\x{07C9}\x{07CA}-\x{07EA}\x{07F4}-\x{07F5}\x{07FA}\x{07FB}-\x{07FC}\x{07FE}-\x{07FF}\x{0800}-\x{0815}\x{081A}\x{0824}\x{0828}\x{082E}-\x{082F}\x{0830}-\x{083E}\x{083F}\x{0840}-\x{0858}\x{085C}-\x{085D}\x{085E}\x{085F}\x{0860}-\x{086A}\x{086B}-\x{086F}\x{0870}-\x{089F}\x{08A0}-\x{08B4}\x{08B5}\x{08B6}-\x{08C7}\x{08C8}-\x{08D2}\x{08E2}\x{200F}\x{2070}\x{2074}-\x{2079}\x{2080}-\x{2089}\x{2488}-\x{249B}\x{FB1D}\x{FB1F}-\x{FB28}\x{FB2A}-\x{FB36}\x{FB37}\x{FB38}-\x{FB3C}\x{FB3D}\x{FB3E}\x{FB3F}\x{FB40}-\x{FB41}\x{FB42}\x{FB43}-\x{FB44}\x{FB45}\x{FB46}-\x{FB4F}\x{FB50}-\x{FBB1}\x{FBB2}-\x{FBC1}\x{FBC2}-\x{FBD2}\x{FBD3}-\x{FD3D}\x{FD40}-\x{FD4F}\x{FD50}-\x{FD8F}\x{FD90}-\x{FD91}\x{FD92}-\x{FDC7}\x{FDC8}-\x{FDCF}\x{FDF0}-\x{FDFB}\x{FDFC}\x{FDFE}-\x{FDFF}\x{FE70}-\x{FE74}\x{FE75}\x{FE76}-\x{FEFC}\x{FEFD}-\x{FEFE}\x{FF10}-\x{FF19}\x{102E1}-\x{102FB}\x{10800}-\x{10805}\x{10806}-\x{10807}\x{10808}\x{10809}\x{1080A}-\x{10835}\x{10836}\x{10837}-\x{10838}\x{10839}-\x{1083B}\x{1083C}\x{1083D}-\x{1083E}\x{1083F}-\x{10855}\x{10856}\x{10857}\x{10858}-\x{1085F}\x{10860}-\x{10876}\x{10877}-\x{10878}\x{10879}-\x{1087F}\x{10880}-\x{1089E}\x{1089F}-\x{108A6}\x{108A7}-\x{108AF}\x{108B0}-\x{108DF}\x{108E0}-\x{108F2}\x{108F3}\x{108F4}-\x{108F5}\x{108F6}-\x{108FA}\x{108FB}-\x{108FF}\x{10900}-\x{10915}\x{10916}-\x{1091B}\x{1091C}-\x{1091E}\x{10920}-\x{10939}\x{1093A}-\x{1093E}\x{1093F}\x{10940}-\x{1097F}\x{10980}-\x{109B7}\x{109B8}-\x{109BB}\x{109BC}-\x{109BD}\x{109BE}-\x{109BF}\x{109C0}-\x{109CF}\x{109D0}-\x{109D1}\x{109D2}-\x{109FF}\x{10A00}\x{10A04}\x{10A07}-\x{10A0B}\x{10A10}-\x{10A13}\x{10A14}\x{10A15}-\x{10A17}\x{10A18}\x{10A19}-\x{10A35}\x{10A36}-\x{10A37}\x{10A3B}-\x{10A3E}\x{10A40}-\x{10A48}\x{10A49}-\x{10A4F}\x{10A50}-\x{10A58}\x{10A59}-\x{10A5F}\x{10A60}-\x{10A7C}\x{10A7D}-\x{10A7E}\x{10A7F}\x{10A80}-\x{10A9C}\x{10A9D}-\x{10A9F}\x{10AA0}-\x{10ABF}\x{10AC0}-\x{10AC7}\x{10AC8}\x{10AC9}-\x{10AE4}\x{10AE7}-\x{10AEA}\x{10AEB}-\x{10AEF}\x{10AF0}-\x{10AF6}\x{10AF7}-\x{10AFF}\x{10B00}-\x{10B35}\x{10B36}-\x{10B38}\x{10B40}-\x{10B55}\x{10B56}-\x{10B57}\x{10B58}-\x{10B5F}\x{10B60}-\x{10B72}\x{10B73}-\x{10B77}\x{10B78}-\x{10B7F}\x{10B80}-\x{10B91}\x{10B92}-\x{10B98}\x{10B99}-\x{10B9C}\x{10B9D}-\x{10BA8}\x{10BA9}-\x{10BAF}\x{10BB0}-\x{10BFF}\x{10C00}-\x{10C48}\x{10C49}-\x{10C7F}\x{10C80}-\x{10CB2}\x{10CB3}-\x{10CBF}\x{10CC0}-\x{10CF2}\x{10CF3}-\x{10CF9}\x{10CFA}-\x{10CFF}\x{10D00}-\x{10D23}\x{10D28}-\x{10D2F}\x{10D30}-\x{10D39}\x{10D3A}-\x{10D3F}\x{10D40}-\x{10E5F}\x{10E60}-\x{10E7E}\x{10E7F}\x{10E80}-\x{10EA9}\x{10EAA}\x{10EAD}\x{10EAE}-\x{10EAF}\x{10EB0}-\x{10EB1}\x{10EB2}-\x{10EFF}\x{10F00}-\x{10F1C}\x{10F1D}-\x{10F26}\x{10F27}\x{10F28}-\x{10F2F}\x{10F30}-\x{10F45}\x{10F51}-\x{10F54}\x{10F55}-\x{10F59}\x{10F5A}-\x{10F6F}\x{10F70}-\x{10FAF}\x{10FB0}-\x{10FC4}\x{10FC5}-\x{10FCB}\x{10FCC}-\x{10FDF}\x{10FE0}-\x{10FF6}\x{10FF7}-\x{10FFF}\x{1D7CE}-\x{1D7FF}\x{1E800}-\x{1E8C4}\x{1E8C5}-\x{1E8C6}\x{1E8C7}-\x{1E8CF}\x{1E8D7}-\x{1E8FF}\x{1E900}-\x{1E943}\x{1E94B}\x{1E94C}-\x{1E94F}\x{1E950}-\x{1E959}\x{1E95A}-\x{1E95D}\x{1E95E}-\x{1E95F}\x{1E960}-\x{1EC6F}\x{1EC70}\x{1EC71}-\x{1ECAB}\x{1ECAC}\x{1ECAD}-\x{1ECAF}\x{1ECB0}\x{1ECB1}-\x{1ECB4}\x{1ECB5}-\x{1ECBF}\x{1ECC0}-\x{1ECFF}\x{1ED00}\x{1ED01}-\x{1ED2D}\x{1ED2E}\x{1ED2F}-\x{1ED3D}\x{1ED3E}-\x{1ED4F}\x{1ED50}-\x{1EDFF}\x{1EE00}-\x{1EE03}\x{1EE04}\x{1EE05}-\x{1EE1F}\x{1EE20}\x{1EE21}-\x{1EE22}\x{1EE23}\x{1EE24}\x{1EE25}-\x{1EE26}\x{1EE27}\x{1EE28}\x{1EE29}-\x{1EE32}\x{1EE33}\x{1EE34}-\x{1EE37}\x{1EE38}\x{1EE39}\x{1EE3A}\x{1EE3B}\x{1EE3C}-\x{1EE41}\x{1EE42}\x{1EE43}-\x{1EE46}\x{1EE47}\x{1EE48}\x{1EE49}\x{1EE4A}\x{1EE4B}\x{1EE4C}\x{1EE4D}-\x{1EE4F}\x{1EE50}\x{1EE51}-\x{1EE52}\x{1EE53}\x{1EE54}\x{1EE55}-\x{1EE56}\x{1EE57}\x{1EE58}\x{1EE59}\x{1EE5A}\x{1EE5B}\x{1EE5C}\x{1EE5D}\x{1EE5E}\x{1EE5F}\x{1EE60}\x{1EE61}-\x{1EE62}\x{1EE63}\x{1EE64}\x{1EE65}-\x{1EE66}\x{1EE67}-\x{1EE6A}\x{1EE6B}\x{1EE6C}-\x{1EE72}\x{1EE73}\x{1EE74}-\x{1EE77}\x{1EE78}\x{1EE79}-\x{1EE7C}\x{1EE7D}\x{1EE7E}\x{1EE7F}\x{1EE80}-\x{1EE89}\x{1EE8A}\x{1EE8B}-\x{1EE9B}\x{1EE9C}-\x{1EEA0}\x{1EEA1}-\x{1EEA3}\x{1EEA4}\x{1EEA5}-\x{1EEA9}\x{1EEAA}\x{1EEAB}-\x{1EEBB}\x{1EEBC}-\x{1EEEF}\x{1EEF2}-\x{1EEFF}\x{1EF00}-\x{1EFFF}\x{1F100}-\x{1F10A}\x{1FBF0}-\x{1FBF9}][\x{0300}-\x{036F}\x{0483}-\x{0487}\x{0488}-\x{0489}\x{0591}-\x{05BD}\x{05BF}\x{05C1}-\x{05C2}\x{05C4}-\x{05C5}\x{05C7}\x{0610}-\x{061A}\x{064B}-\x{065F}\x{0670}\x{06D6}-\x{06DC}\x{06DF}-\x{06E4}\x{06E7}-\x{06E8}\x{06EA}-\x{06ED}\x{0711}\x{0730}-\x{074A}\x{07A6}-\x{07B0}\x{07EB}-\x{07F3}\x{07FD}\x{0816}-\x{0819}\x{081B}-\x{0823}\x{0825}-\x{0827}\x{0829}-\x{082D}\x{0859}-\x{085B}\x{08D3}-\x{08E1}\x{08E3}-\x{0902}\x{093A}\x{093C}\x{0941}-\x{0948}\x{094D}\x{0951}-\x{0957}\x{0962}-\x{0963}\x{0981}\x{09BC}\x{09C1}-\x{09C4}\x{09CD}\x{09E2}-\x{09E3}\x{09FE}\x{0A01}-\x{0A02}\x{0A3C}\x{0A41}-\x{0A42}\x{0A47}-\x{0A48}\x{0A4B}-\x{0A4D}\x{0A51}\x{0A70}-\x{0A71}\x{0A75}\x{0A81}-\x{0A82}\x{0ABC}\x{0AC1}-\x{0AC5}\x{0AC7}-\x{0AC8}\x{0ACD}\x{0AE2}-\x{0AE3}\x{0AFA}-\x{0AFF}\x{0B01}\x{0B3C}\x{0B3F}\x{0B41}-\x{0B44}\x{0B4D}\x{0B55}-\x{0B56}\x{0B62}-\x{0B63}\x{0B82}\x{0BC0}\x{0BCD}\x{0C00}\x{0C04}\x{0C3E}-\x{0C40}\x{0C46}-\x{0C48}\x{0C4A}-\x{0C4D}\x{0C55}-\x{0C56}\x{0C62}-\x{0C63}\x{0C81}\x{0CBC}\x{0CCC}-\x{0CCD}\x{0CE2}-\x{0CE3}\x{0D00}-\x{0D01}\x{0D3B}-\x{0D3C}\x{0D41}-\x{0D44}\x{0D4D}\x{0D62}-\x{0D63}\x{0D81}\x{0DCA}\x{0DD2}-\x{0DD4}\x{0DD6}\x{0E31}\x{0E34}-\x{0E3A}\x{0E47}-\x{0E4E}\x{0EB1}\x{0EB4}-\x{0EBC}\x{0EC8}-\x{0ECD}\x{0F18}-\x{0F19}\x{0F35}\x{0F37}\x{0F39}\x{0F71}-\x{0F7E}\x{0F80}-\x{0F84}\x{0F86}-\x{0F87}\x{0F8D}-\x{0F97}\x{0F99}-\x{0FBC}\x{0FC6}\x{102D}-\x{1030}\x{1032}-\x{1037}\x{1039}-\x{103A}\x{103D}-\x{103E}\x{1058}-\x{1059}\x{105E}-\x{1060}\x{1071}-\x{1074}\x{1082}\x{1085}-\x{1086}\x{108D}\x{109D}\x{135D}-\x{135F}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}-\x{1753}\x{1772}-\x{1773}\x{17B4}-\x{17B5}\x{17B7}-\x{17BD}\x{17C6}\x{17C9}-\x{17D3}\x{17DD}\x{180B}-\x{180D}\x{1885}-\x{1886}\x{18A9}\x{1920}-\x{1922}\x{1927}-\x{1928}\x{1932}\x{1939}-\x{193B}\x{1A17}-\x{1A18}\x{1A1B}\x{1A56}\x{1A58}-\x{1A5E}\x{1A60}\x{1A62}\x{1A65}-\x{1A6C}\x{1A73}-\x{1A7C}\x{1A7F}\x{1AB0}-\x{1ABD}\x{1ABE}\x{1ABF}-\x{1AC0}\x{1B00}-\x{1B03}\x{1B34}\x{1B36}-\x{1B3A}\x{1B3C}\x{1B42}\x{1B6B}-\x{1B73}\x{1B80}-\x{1B81}\x{1BA2}-\x{1BA5}\x{1BA8}-\x{1BA9}\x{1BAB}-\x{1BAD}\x{1BE6}\x{1BE8}-\x{1BE9}\x{1BED}\x{1BEF}-\x{1BF1}\x{1C2C}-\x{1C33}\x{1C36}-\x{1C37}\x{1CD0}-\x{1CD2}\x{1CD4}-\x{1CE0}\x{1CE2}-\x{1CE8}\x{1CED}\x{1CF4}\x{1CF8}-\x{1CF9}\x{1DC0}-\x{1DF9}\x{1DFB}-\x{1DFF}\x{20D0}-\x{20DC}\x{20DD}-\x{20E0}\x{20E1}\x{20E2}-\x{20E4}\x{20E5}-\x{20F0}\x{2CEF}-\x{2CF1}\x{2D7F}\x{2DE0}-\x{2DFF}\x{302A}-\x{302D}\x{3099}-\x{309A}\x{A66F}\x{A670}-\x{A672}\x{A674}-\x{A67D}\x{A69E}-\x{A69F}\x{A6F0}-\x{A6F1}\x{A802}\x{A806}\x{A80B}\x{A825}-\x{A826}\x{A82C}\x{A8C4}-\x{A8C5}\x{A8E0}-\x{A8F1}\x{A8FF}\x{A926}-\x{A92D}\x{A947}-\x{A951}\x{A980}-\x{A982}\x{A9B3}\x{A9B6}-\x{A9B9}\x{A9BC}-\x{A9BD}\x{A9E5}\x{AA29}-\x{AA2E}\x{AA31}-\x{AA32}\x{AA35}-\x{AA36}\x{AA43}\x{AA4C}\x{AA7C}\x{AAB0}\x{AAB2}-\x{AAB4}\x{AAB7}-\x{AAB8}\x{AABE}-\x{AABF}\x{AAC1}\x{AAEC}-\x{AAED}\x{AAF6}\x{ABE5}\x{ABE8}\x{ABED}\x{FB1E}\x{FE00}-\x{FE0F}\x{FE20}-\x{FE2F}\x{101FD}\x{102E0}\x{10376}-\x{1037A}\x{10A01}-\x{10A03}\x{10A05}-\x{10A06}\x{10A0C}-\x{10A0F}\x{10A38}-\x{10A3A}\x{10A3F}\x{10AE5}-\x{10AE6}\x{10D24}-\x{10D27}\x{10EAB}-\x{10EAC}\x{10F46}-\x{10F50}\x{11001}\x{11038}-\x{11046}\x{1107F}-\x{11081}\x{110B3}-\x{110B6}\x{110B9}-\x{110BA}\x{11100}-\x{11102}\x{11127}-\x{1112B}\x{1112D}-\x{11134}\x{11173}\x{11180}-\x{11181}\x{111B6}-\x{111BE}\x{111C9}-\x{111CC}\x{111CF}\x{1122F}-\x{11231}\x{11234}\x{11236}-\x{11237}\x{1123E}\x{112DF}\x{112E3}-\x{112EA}\x{11300}-\x{11301}\x{1133B}-\x{1133C}\x{11340}\x{11366}-\x{1136C}\x{11370}-\x{11374}\x{11438}-\x{1143F}\x{11442}-\x{11444}\x{11446}\x{1145E}\x{114B3}-\x{114B8}\x{114BA}\x{114BF}-\x{114C0}\x{114C2}-\x{114C3}\x{115B2}-\x{115B5}\x{115BC}-\x{115BD}\x{115BF}-\x{115C0}\x{115DC}-\x{115DD}\x{11633}-\x{1163A}\x{1163D}\x{1163F}-\x{11640}\x{116AB}\x{116AD}\x{116B0}-\x{116B5}\x{116B7}\x{1171D}-\x{1171F}\x{11722}-\x{11725}\x{11727}-\x{1172B}\x{1182F}-\x{11837}\x{11839}-\x{1183A}\x{1193B}-\x{1193C}\x{1193E}\x{11943}\x{119D4}-\x{119D7}\x{119DA}-\x{119DB}\x{119E0}\x{11A01}-\x{11A06}\x{11A09}-\x{11A0A}\x{11A33}-\x{11A38}\x{11A3B}-\x{11A3E}\x{11A47}\x{11A51}-\x{11A56}\x{11A59}-\x{11A5B}\x{11A8A}-\x{11A96}\x{11A98}-\x{11A99}\x{11C30}-\x{11C36}\x{11C38}-\x{11C3D}\x{11C92}-\x{11CA7}\x{11CAA}-\x{11CB0}\x{11CB2}-\x{11CB3}\x{11CB5}-\x{11CB6}\x{11D31}-\x{11D36}\x{11D3A}\x{11D3C}-\x{11D3D}\x{11D3F}-\x{11D45}\x{11D47}\x{11D90}-\x{11D91}\x{11D95}\x{11D97}\x{11EF3}-\x{11EF4}\x{16AF0}-\x{16AF4}\x{16B30}-\x{16B36}\x{16F4F}\x{16F8F}-\x{16F92}\x{16FE4}\x{1BC9D}-\x{1BC9E}\x{1D167}-\x{1D169}\x{1D17B}-\x{1D182}\x{1D185}-\x{1D18B}\x{1D1AA}-\x{1D1AD}\x{1D242}-\x{1D244}\x{1DA00}-\x{1DA36}\x{1DA3B}-\x{1DA6C}\x{1DA75}\x{1DA84}\x{1DA9B}-\x{1DA9F}\x{1DAA1}-\x{1DAAF}\x{1E000}-\x{1E006}\x{1E008}-\x{1E018}\x{1E01B}-\x{1E021}\x{1E023}-\x{1E024}\x{1E026}-\x{1E02A}\x{1E130}-\x{1E136}\x{1E2EC}-\x{1E2EF}\x{1E8D0}-\x{1E8D6}\x{1E944}-\x{1E94A}\x{E0100}-\x{E01EF}]*$/u';
+    const BIDI_STEP_4_AN = '/[\x{0600}-\x{0605}\x{0660}-\x{0669}\x{066B}-\x{066C}\x{06DD}\x{08E2}\x{10D30}-\x{10D39}\x{10E60}-\x{10E7E}]/u';
+    const BIDI_STEP_4_EN = '/[\x{0030}-\x{0039}\x{00B2}-\x{00B3}\x{00B9}\x{06F0}-\x{06F9}\x{2070}\x{2074}-\x{2079}\x{2080}-\x{2089}\x{2488}-\x{249B}\x{FF10}-\x{FF19}\x{102E1}-\x{102FB}\x{1D7CE}-\x{1D7FF}\x{1F100}-\x{1F10A}\x{1FBF0}-\x{1FBF9}]/u';
+    const BIDI_STEP_5 = '/[\x{0009}\x{000A}\x{000B}\x{000C}\x{000D}\x{001C}-\x{001E}\x{001F}\x{0020}\x{0085}\x{0590}\x{05BE}\x{05C0}\x{05C3}\x{05C6}\x{05C8}-\x{05CF}\x{05D0}-\x{05EA}\x{05EB}-\x{05EE}\x{05EF}-\x{05F2}\x{05F3}-\x{05F4}\x{05F5}-\x{05FF}\x{0600}-\x{0605}\x{0608}\x{060B}\x{060D}\x{061B}\x{061C}\x{061D}\x{061E}-\x{061F}\x{0620}-\x{063F}\x{0640}\x{0641}-\x{064A}\x{0660}-\x{0669}\x{066B}-\x{066C}\x{066D}\x{066E}-\x{066F}\x{0671}-\x{06D3}\x{06D4}\x{06D5}\x{06DD}\x{06E5}-\x{06E6}\x{06EE}-\x{06EF}\x{06FA}-\x{06FC}\x{06FD}-\x{06FE}\x{06FF}\x{0700}-\x{070D}\x{070E}\x{070F}\x{0710}\x{0712}-\x{072F}\x{074B}-\x{074C}\x{074D}-\x{07A5}\x{07B1}\x{07B2}-\x{07BF}\x{07C0}-\x{07C9}\x{07CA}-\x{07EA}\x{07F4}-\x{07F5}\x{07FA}\x{07FB}-\x{07FC}\x{07FE}-\x{07FF}\x{0800}-\x{0815}\x{081A}\x{0824}\x{0828}\x{082E}-\x{082F}\x{0830}-\x{083E}\x{083F}\x{0840}-\x{0858}\x{085C}-\x{085D}\x{085E}\x{085F}\x{0860}-\x{086A}\x{086B}-\x{086F}\x{0870}-\x{089F}\x{08A0}-\x{08B4}\x{08B5}\x{08B6}-\x{08C7}\x{08C8}-\x{08D2}\x{08E2}\x{1680}\x{2000}-\x{200A}\x{200F}\x{2028}\x{2029}\x{202A}\x{202B}\x{202C}\x{202D}\x{202E}\x{205F}\x{2066}\x{2067}\x{2068}\x{2069}\x{3000}\x{FB1D}\x{FB1F}-\x{FB28}\x{FB2A}-\x{FB36}\x{FB37}\x{FB38}-\x{FB3C}\x{FB3D}\x{FB3E}\x{FB3F}\x{FB40}-\x{FB41}\x{FB42}\x{FB43}-\x{FB44}\x{FB45}\x{FB46}-\x{FB4F}\x{FB50}-\x{FBB1}\x{FBB2}-\x{FBC1}\x{FBC2}-\x{FBD2}\x{FBD3}-\x{FD3D}\x{FD40}-\x{FD4F}\x{FD50}-\x{FD8F}\x{FD90}-\x{FD91}\x{FD92}-\x{FDC7}\x{FDC8}-\x{FDCF}\x{FDF0}-\x{FDFB}\x{FDFC}\x{FDFE}-\x{FDFF}\x{FE70}-\x{FE74}\x{FE75}\x{FE76}-\x{FEFC}\x{FEFD}-\x{FEFE}\x{10800}-\x{10805}\x{10806}-\x{10807}\x{10808}\x{10809}\x{1080A}-\x{10835}\x{10836}\x{10837}-\x{10838}\x{10839}-\x{1083B}\x{1083C}\x{1083D}-\x{1083E}\x{1083F}-\x{10855}\x{10856}\x{10857}\x{10858}-\x{1085F}\x{10860}-\x{10876}\x{10877}-\x{10878}\x{10879}-\x{1087F}\x{10880}-\x{1089E}\x{1089F}-\x{108A6}\x{108A7}-\x{108AF}\x{108B0}-\x{108DF}\x{108E0}-\x{108F2}\x{108F3}\x{108F4}-\x{108F5}\x{108F6}-\x{108FA}\x{108FB}-\x{108FF}\x{10900}-\x{10915}\x{10916}-\x{1091B}\x{1091C}-\x{1091E}\x{10920}-\x{10939}\x{1093A}-\x{1093E}\x{1093F}\x{10940}-\x{1097F}\x{10980}-\x{109B7}\x{109B8}-\x{109BB}\x{109BC}-\x{109BD}\x{109BE}-\x{109BF}\x{109C0}-\x{109CF}\x{109D0}-\x{109D1}\x{109D2}-\x{109FF}\x{10A00}\x{10A04}\x{10A07}-\x{10A0B}\x{10A10}-\x{10A13}\x{10A14}\x{10A15}-\x{10A17}\x{10A18}\x{10A19}-\x{10A35}\x{10A36}-\x{10A37}\x{10A3B}-\x{10A3E}\x{10A40}-\x{10A48}\x{10A49}-\x{10A4F}\x{10A50}-\x{10A58}\x{10A59}-\x{10A5F}\x{10A60}-\x{10A7C}\x{10A7D}-\x{10A7E}\x{10A7F}\x{10A80}-\x{10A9C}\x{10A9D}-\x{10A9F}\x{10AA0}-\x{10ABF}\x{10AC0}-\x{10AC7}\x{10AC8}\x{10AC9}-\x{10AE4}\x{10AE7}-\x{10AEA}\x{10AEB}-\x{10AEF}\x{10AF0}-\x{10AF6}\x{10AF7}-\x{10AFF}\x{10B00}-\x{10B35}\x{10B36}-\x{10B38}\x{10B40}-\x{10B55}\x{10B56}-\x{10B57}\x{10B58}-\x{10B5F}\x{10B60}-\x{10B72}\x{10B73}-\x{10B77}\x{10B78}-\x{10B7F}\x{10B80}-\x{10B91}\x{10B92}-\x{10B98}\x{10B99}-\x{10B9C}\x{10B9D}-\x{10BA8}\x{10BA9}-\x{10BAF}\x{10BB0}-\x{10BFF}\x{10C00}-\x{10C48}\x{10C49}-\x{10C7F}\x{10C80}-\x{10CB2}\x{10CB3}-\x{10CBF}\x{10CC0}-\x{10CF2}\x{10CF3}-\x{10CF9}\x{10CFA}-\x{10CFF}\x{10D00}-\x{10D23}\x{10D28}-\x{10D2F}\x{10D30}-\x{10D39}\x{10D3A}-\x{10D3F}\x{10D40}-\x{10E5F}\x{10E60}-\x{10E7E}\x{10E7F}\x{10E80}-\x{10EA9}\x{10EAA}\x{10EAD}\x{10EAE}-\x{10EAF}\x{10EB0}-\x{10EB1}\x{10EB2}-\x{10EFF}\x{10F00}-\x{10F1C}\x{10F1D}-\x{10F26}\x{10F27}\x{10F28}-\x{10F2F}\x{10F30}-\x{10F45}\x{10F51}-\x{10F54}\x{10F55}-\x{10F59}\x{10F5A}-\x{10F6F}\x{10F70}-\x{10FAF}\x{10FB0}-\x{10FC4}\x{10FC5}-\x{10FCB}\x{10FCC}-\x{10FDF}\x{10FE0}-\x{10FF6}\x{10FF7}-\x{10FFF}\x{1E800}-\x{1E8C4}\x{1E8C5}-\x{1E8C6}\x{1E8C7}-\x{1E8CF}\x{1E8D7}-\x{1E8FF}\x{1E900}-\x{1E943}\x{1E94B}\x{1E94C}-\x{1E94F}\x{1E950}-\x{1E959}\x{1E95A}-\x{1E95D}\x{1E95E}-\x{1E95F}\x{1E960}-\x{1EC6F}\x{1EC70}\x{1EC71}-\x{1ECAB}\x{1ECAC}\x{1ECAD}-\x{1ECAF}\x{1ECB0}\x{1ECB1}-\x{1ECB4}\x{1ECB5}-\x{1ECBF}\x{1ECC0}-\x{1ECFF}\x{1ED00}\x{1ED01}-\x{1ED2D}\x{1ED2E}\x{1ED2F}-\x{1ED3D}\x{1ED3E}-\x{1ED4F}\x{1ED50}-\x{1EDFF}\x{1EE00}-\x{1EE03}\x{1EE04}\x{1EE05}-\x{1EE1F}\x{1EE20}\x{1EE21}-\x{1EE22}\x{1EE23}\x{1EE24}\x{1EE25}-\x{1EE26}\x{1EE27}\x{1EE28}\x{1EE29}-\x{1EE32}\x{1EE33}\x{1EE34}-\x{1EE37}\x{1EE38}\x{1EE39}\x{1EE3A}\x{1EE3B}\x{1EE3C}-\x{1EE41}\x{1EE42}\x{1EE43}-\x{1EE46}\x{1EE47}\x{1EE48}\x{1EE49}\x{1EE4A}\x{1EE4B}\x{1EE4C}\x{1EE4D}-\x{1EE4F}\x{1EE50}\x{1EE51}-\x{1EE52}\x{1EE53}\x{1EE54}\x{1EE55}-\x{1EE56}\x{1EE57}\x{1EE58}\x{1EE59}\x{1EE5A}\x{1EE5B}\x{1EE5C}\x{1EE5D}\x{1EE5E}\x{1EE5F}\x{1EE60}\x{1EE61}-\x{1EE62}\x{1EE63}\x{1EE64}\x{1EE65}-\x{1EE66}\x{1EE67}-\x{1EE6A}\x{1EE6B}\x{1EE6C}-\x{1EE72}\x{1EE73}\x{1EE74}-\x{1EE77}\x{1EE78}\x{1EE79}-\x{1EE7C}\x{1EE7D}\x{1EE7E}\x{1EE7F}\x{1EE80}-\x{1EE89}\x{1EE8A}\x{1EE8B}-\x{1EE9B}\x{1EE9C}-\x{1EEA0}\x{1EEA1}-\x{1EEA3}\x{1EEA4}\x{1EEA5}-\x{1EEA9}\x{1EEAA}\x{1EEAB}-\x{1EEBB}\x{1EEBC}-\x{1EEEF}\x{1EEF2}-\x{1EEFF}\x{1EF00}-\x{1EFFF}]/u';
+    const BIDI_STEP_6 = '/[^\x{0000}-\x{0008}\x{0009}\x{000A}\x{000B}\x{000C}\x{000D}\x{000E}-\x{001B}\x{001C}-\x{001E}\x{001F}\x{0020}\x{0021}-\x{0022}\x{0023}\x{0024}\x{0025}\x{0026}-\x{0027}\x{0028}\x{0029}\x{002A}\x{002B}\x{002C}\x{002D}\x{002E}-\x{002F}\x{003A}\x{003B}\x{003C}-\x{003E}\x{003F}-\x{0040}\x{005B}\x{005C}\x{005D}\x{005E}\x{005F}\x{0060}\x{007B}\x{007C}\x{007D}\x{007E}\x{007F}-\x{0084}\x{0085}\x{0086}-\x{009F}\x{00A0}\x{00A1}\x{00A2}-\x{00A5}\x{00A6}\x{00A7}\x{00A8}\x{00A9}\x{00AB}\x{00AC}\x{00AD}\x{00AE}\x{00AF}\x{00B0}\x{00B1}\x{00B4}\x{00B6}-\x{00B7}\x{00B8}\x{00BB}\x{00BC}-\x{00BE}\x{00BF}\x{00D7}\x{00F7}\x{02B9}-\x{02BA}\x{02C2}-\x{02C5}\x{02C6}-\x{02CF}\x{02D2}-\x{02DF}\x{02E5}-\x{02EB}\x{02EC}\x{02ED}\x{02EF}-\x{02FF}\x{0300}-\x{036F}\x{0374}\x{0375}\x{037E}\x{0384}-\x{0385}\x{0387}\x{03F6}\x{0483}-\x{0487}\x{0488}-\x{0489}\x{058A}\x{058D}-\x{058E}\x{058F}\x{0590}\x{0591}-\x{05BD}\x{05BE}\x{05BF}\x{05C0}\x{05C1}-\x{05C2}\x{05C3}\x{05C4}-\x{05C5}\x{05C6}\x{05C7}\x{05C8}-\x{05CF}\x{05D0}-\x{05EA}\x{05EB}-\x{05EE}\x{05EF}-\x{05F2}\x{05F3}-\x{05F4}\x{05F5}-\x{05FF}\x{0600}-\x{0605}\x{0606}-\x{0607}\x{0608}\x{0609}-\x{060A}\x{060B}\x{060C}\x{060D}\x{060E}-\x{060F}\x{0610}-\x{061A}\x{061B}\x{061C}\x{061D}\x{061E}-\x{061F}\x{0620}-\x{063F}\x{0640}\x{0641}-\x{064A}\x{064B}-\x{065F}\x{0660}-\x{0669}\x{066A}\x{066B}-\x{066C}\x{066D}\x{066E}-\x{066F}\x{0670}\x{0671}-\x{06D3}\x{06D4}\x{06D5}\x{06D6}-\x{06DC}\x{06DD}\x{06DE}\x{06DF}-\x{06E4}\x{06E5}-\x{06E6}\x{06E7}-\x{06E8}\x{06E9}\x{06EA}-\x{06ED}\x{06EE}-\x{06EF}\x{06FA}-\x{06FC}\x{06FD}-\x{06FE}\x{06FF}\x{0700}-\x{070D}\x{070E}\x{070F}\x{0710}\x{0711}\x{0712}-\x{072F}\x{0730}-\x{074A}\x{074B}-\x{074C}\x{074D}-\x{07A5}\x{07A6}-\x{07B0}\x{07B1}\x{07B2}-\x{07BF}\x{07C0}-\x{07C9}\x{07CA}-\x{07EA}\x{07EB}-\x{07F3}\x{07F4}-\x{07F5}\x{07F6}\x{07F7}-\x{07F9}\x{07FA}\x{07FB}-\x{07FC}\x{07FD}\x{07FE}-\x{07FF}\x{0800}-\x{0815}\x{0816}-\x{0819}\x{081A}\x{081B}-\x{0823}\x{0824}\x{0825}-\x{0827}\x{0828}\x{0829}-\x{082D}\x{082E}-\x{082F}\x{0830}-\x{083E}\x{083F}\x{0840}-\x{0858}\x{0859}-\x{085B}\x{085C}-\x{085D}\x{085E}\x{085F}\x{0860}-\x{086A}\x{086B}-\x{086F}\x{0870}-\x{089F}\x{08A0}-\x{08B4}\x{08B5}\x{08B6}-\x{08C7}\x{08C8}-\x{08D2}\x{08D3}-\x{08E1}\x{08E2}\x{08E3}-\x{0902}\x{093A}\x{093C}\x{0941}-\x{0948}\x{094D}\x{0951}-\x{0957}\x{0962}-\x{0963}\x{0981}\x{09BC}\x{09C1}-\x{09C4}\x{09CD}\x{09E2}-\x{09E3}\x{09F2}-\x{09F3}\x{09FB}\x{09FE}\x{0A01}-\x{0A02}\x{0A3C}\x{0A41}-\x{0A42}\x{0A47}-\x{0A48}\x{0A4B}-\x{0A4D}\x{0A51}\x{0A70}-\x{0A71}\x{0A75}\x{0A81}-\x{0A82}\x{0ABC}\x{0AC1}-\x{0AC5}\x{0AC7}-\x{0AC8}\x{0ACD}\x{0AE2}-\x{0AE3}\x{0AF1}\x{0AFA}-\x{0AFF}\x{0B01}\x{0B3C}\x{0B3F}\x{0B41}-\x{0B44}\x{0B4D}\x{0B55}-\x{0B56}\x{0B62}-\x{0B63}\x{0B82}\x{0BC0}\x{0BCD}\x{0BF3}-\x{0BF8}\x{0BF9}\x{0BFA}\x{0C00}\x{0C04}\x{0C3E}-\x{0C40}\x{0C46}-\x{0C48}\x{0C4A}-\x{0C4D}\x{0C55}-\x{0C56}\x{0C62}-\x{0C63}\x{0C78}-\x{0C7E}\x{0C81}\x{0CBC}\x{0CCC}-\x{0CCD}\x{0CE2}-\x{0CE3}\x{0D00}-\x{0D01}\x{0D3B}-\x{0D3C}\x{0D41}-\x{0D44}\x{0D4D}\x{0D62}-\x{0D63}\x{0D81}\x{0DCA}\x{0DD2}-\x{0DD4}\x{0DD6}\x{0E31}\x{0E34}-\x{0E3A}\x{0E3F}\x{0E47}-\x{0E4E}\x{0EB1}\x{0EB4}-\x{0EBC}\x{0EC8}-\x{0ECD}\x{0F18}-\x{0F19}\x{0F35}\x{0F37}\x{0F39}\x{0F3A}\x{0F3B}\x{0F3C}\x{0F3D}\x{0F71}-\x{0F7E}\x{0F80}-\x{0F84}\x{0F86}-\x{0F87}\x{0F8D}-\x{0F97}\x{0F99}-\x{0FBC}\x{0FC6}\x{102D}-\x{1030}\x{1032}-\x{1037}\x{1039}-\x{103A}\x{103D}-\x{103E}\x{1058}-\x{1059}\x{105E}-\x{1060}\x{1071}-\x{1074}\x{1082}\x{1085}-\x{1086}\x{108D}\x{109D}\x{135D}-\x{135F}\x{1390}-\x{1399}\x{1400}\x{1680}\x{169B}\x{169C}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}-\x{1753}\x{1772}-\x{1773}\x{17B4}-\x{17B5}\x{17B7}-\x{17BD}\x{17C6}\x{17C9}-\x{17D3}\x{17DB}\x{17DD}\x{17F0}-\x{17F9}\x{1800}-\x{1805}\x{1806}\x{1807}-\x{180A}\x{180B}-\x{180D}\x{180E}\x{1885}-\x{1886}\x{18A9}\x{1920}-\x{1922}\x{1927}-\x{1928}\x{1932}\x{1939}-\x{193B}\x{1940}\x{1944}-\x{1945}\x{19DE}-\x{19FF}\x{1A17}-\x{1A18}\x{1A1B}\x{1A56}\x{1A58}-\x{1A5E}\x{1A60}\x{1A62}\x{1A65}-\x{1A6C}\x{1A73}-\x{1A7C}\x{1A7F}\x{1AB0}-\x{1ABD}\x{1ABE}\x{1ABF}-\x{1AC0}\x{1B00}-\x{1B03}\x{1B34}\x{1B36}-\x{1B3A}\x{1B3C}\x{1B42}\x{1B6B}-\x{1B73}\x{1B80}-\x{1B81}\x{1BA2}-\x{1BA5}\x{1BA8}-\x{1BA9}\x{1BAB}-\x{1BAD}\x{1BE6}\x{1BE8}-\x{1BE9}\x{1BED}\x{1BEF}-\x{1BF1}\x{1C2C}-\x{1C33}\x{1C36}-\x{1C37}\x{1CD0}-\x{1CD2}\x{1CD4}-\x{1CE0}\x{1CE2}-\x{1CE8}\x{1CED}\x{1CF4}\x{1CF8}-\x{1CF9}\x{1DC0}-\x{1DF9}\x{1DFB}-\x{1DFF}\x{1FBD}\x{1FBF}-\x{1FC1}\x{1FCD}-\x{1FCF}\x{1FDD}-\x{1FDF}\x{1FED}-\x{1FEF}\x{1FFD}-\x{1FFE}\x{2000}-\x{200A}\x{200B}-\x{200D}\x{200F}\x{2010}-\x{2015}\x{2016}-\x{2017}\x{2018}\x{2019}\x{201A}\x{201B}-\x{201C}\x{201D}\x{201E}\x{201F}\x{2020}-\x{2027}\x{2028}\x{2029}\x{202A}\x{202B}\x{202C}\x{202D}\x{202E}\x{202F}\x{2030}-\x{2034}\x{2035}-\x{2038}\x{2039}\x{203A}\x{203B}-\x{203E}\x{203F}-\x{2040}\x{2041}-\x{2043}\x{2044}\x{2045}\x{2046}\x{2047}-\x{2051}\x{2052}\x{2053}\x{2054}\x{2055}-\x{205E}\x{205F}\x{2060}-\x{2064}\x{2065}\x{2066}\x{2067}\x{2068}\x{2069}\x{206A}-\x{206F}\x{207A}-\x{207B}\x{207C}\x{207D}\x{207E}\x{208A}-\x{208B}\x{208C}\x{208D}\x{208E}\x{20A0}-\x{20BF}\x{20C0}-\x{20CF}\x{20D0}-\x{20DC}\x{20DD}-\x{20E0}\x{20E1}\x{20E2}-\x{20E4}\x{20E5}-\x{20F0}\x{2100}-\x{2101}\x{2103}-\x{2106}\x{2108}-\x{2109}\x{2114}\x{2116}-\x{2117}\x{2118}\x{211E}-\x{2123}\x{2125}\x{2127}\x{2129}\x{212E}\x{213A}-\x{213B}\x{2140}-\x{2144}\x{214A}\x{214B}\x{214C}-\x{214D}\x{2150}-\x{215F}\x{2189}\x{218A}-\x{218B}\x{2190}-\x{2194}\x{2195}-\x{2199}\x{219A}-\x{219B}\x{219C}-\x{219F}\x{21A0}\x{21A1}-\x{21A2}\x{21A3}\x{21A4}-\x{21A5}\x{21A6}\x{21A7}-\x{21AD}\x{21AE}\x{21AF}-\x{21CD}\x{21CE}-\x{21CF}\x{21D0}-\x{21D1}\x{21D2}\x{21D3}\x{21D4}\x{21D5}-\x{21F3}\x{21F4}-\x{2211}\x{2212}\x{2213}\x{2214}-\x{22FF}\x{2300}-\x{2307}\x{2308}\x{2309}\x{230A}\x{230B}\x{230C}-\x{231F}\x{2320}-\x{2321}\x{2322}-\x{2328}\x{2329}\x{232A}\x{232B}-\x{2335}\x{237B}\x{237C}\x{237D}-\x{2394}\x{2396}-\x{239A}\x{239B}-\x{23B3}\x{23B4}-\x{23DB}\x{23DC}-\x{23E1}\x{23E2}-\x{2426}\x{2440}-\x{244A}\x{2460}-\x{2487}\x{24EA}-\x{24FF}\x{2500}-\x{25B6}\x{25B7}\x{25B8}-\x{25C0}\x{25C1}\x{25C2}-\x{25F7}\x{25F8}-\x{25FF}\x{2600}-\x{266E}\x{266F}\x{2670}-\x{26AB}\x{26AD}-\x{2767}\x{2768}\x{2769}\x{276A}\x{276B}\x{276C}\x{276D}\x{276E}\x{276F}\x{2770}\x{2771}\x{2772}\x{2773}\x{2774}\x{2775}\x{2776}-\x{2793}\x{2794}-\x{27BF}\x{27C0}-\x{27C4}\x{27C5}\x{27C6}\x{27C7}-\x{27E5}\x{27E6}\x{27E7}\x{27E8}\x{27E9}\x{27EA}\x{27EB}\x{27EC}\x{27ED}\x{27EE}\x{27EF}\x{27F0}-\x{27FF}\x{2900}-\x{2982}\x{2983}\x{2984}\x{2985}\x{2986}\x{2987}\x{2988}\x{2989}\x{298A}\x{298B}\x{298C}\x{298D}\x{298E}\x{298F}\x{2990}\x{2991}\x{2992}\x{2993}\x{2994}\x{2995}\x{2996}\x{2997}\x{2998}\x{2999}-\x{29D7}\x{29D8}\x{29D9}\x{29DA}\x{29DB}\x{29DC}-\x{29FB}\x{29FC}\x{29FD}\x{29FE}-\x{2AFF}\x{2B00}-\x{2B2F}\x{2B30}-\x{2B44}\x{2B45}-\x{2B46}\x{2B47}-\x{2B4C}\x{2B4D}-\x{2B73}\x{2B76}-\x{2B95}\x{2B97}-\x{2BFF}\x{2CE5}-\x{2CEA}\x{2CEF}-\x{2CF1}\x{2CF9}-\x{2CFC}\x{2CFD}\x{2CFE}-\x{2CFF}\x{2D7F}\x{2DE0}-\x{2DFF}\x{2E00}-\x{2E01}\x{2E02}\x{2E03}\x{2E04}\x{2E05}\x{2E06}-\x{2E08}\x{2E09}\x{2E0A}\x{2E0B}\x{2E0C}\x{2E0D}\x{2E0E}-\x{2E16}\x{2E17}\x{2E18}-\x{2E19}\x{2E1A}\x{2E1B}\x{2E1C}\x{2E1D}\x{2E1E}-\x{2E1F}\x{2E20}\x{2E21}\x{2E22}\x{2E23}\x{2E24}\x{2E25}\x{2E26}\x{2E27}\x{2E28}\x{2E29}\x{2E2A}-\x{2E2E}\x{2E2F}\x{2E30}-\x{2E39}\x{2E3A}-\x{2E3B}\x{2E3C}-\x{2E3F}\x{2E40}\x{2E41}\x{2E42}\x{2E43}-\x{2E4F}\x{2E50}-\x{2E51}\x{2E52}\x{2E80}-\x{2E99}\x{2E9B}-\x{2EF3}\x{2F00}-\x{2FD5}\x{2FF0}-\x{2FFB}\x{3000}\x{3001}-\x{3003}\x{3004}\x{3008}\x{3009}\x{300A}\x{300B}\x{300C}\x{300D}\x{300E}\x{300F}\x{3010}\x{3011}\x{3012}-\x{3013}\x{3014}\x{3015}\x{3016}\x{3017}\x{3018}\x{3019}\x{301A}\x{301B}\x{301C}\x{301D}\x{301E}-\x{301F}\x{3020}\x{302A}-\x{302D}\x{3030}\x{3036}-\x{3037}\x{303D}\x{303E}-\x{303F}\x{3099}-\x{309A}\x{309B}-\x{309C}\x{30A0}\x{30FB}\x{31C0}-\x{31E3}\x{321D}-\x{321E}\x{3250}\x{3251}-\x{325F}\x{327C}-\x{327E}\x{32B1}-\x{32BF}\x{32CC}-\x{32CF}\x{3377}-\x{337A}\x{33DE}-\x{33DF}\x{33FF}\x{4DC0}-\x{4DFF}\x{A490}-\x{A4C6}\x{A60D}-\x{A60F}\x{A66F}\x{A670}-\x{A672}\x{A673}\x{A674}-\x{A67D}\x{A67E}\x{A67F}\x{A69E}-\x{A69F}\x{A6F0}-\x{A6F1}\x{A700}-\x{A716}\x{A717}-\x{A71F}\x{A720}-\x{A721}\x{A788}\x{A802}\x{A806}\x{A80B}\x{A825}-\x{A826}\x{A828}-\x{A82B}\x{A82C}\x{A838}\x{A839}\x{A874}-\x{A877}\x{A8C4}-\x{A8C5}\x{A8E0}-\x{A8F1}\x{A8FF}\x{A926}-\x{A92D}\x{A947}-\x{A951}\x{A980}-\x{A982}\x{A9B3}\x{A9B6}-\x{A9B9}\x{A9BC}-\x{A9BD}\x{A9E5}\x{AA29}-\x{AA2E}\x{AA31}-\x{AA32}\x{AA35}-\x{AA36}\x{AA43}\x{AA4C}\x{AA7C}\x{AAB0}\x{AAB2}-\x{AAB4}\x{AAB7}-\x{AAB8}\x{AABE}-\x{AABF}\x{AAC1}\x{AAEC}-\x{AAED}\x{AAF6}\x{AB6A}-\x{AB6B}\x{ABE5}\x{ABE8}\x{ABED}\x{FB1D}\x{FB1E}\x{FB1F}-\x{FB28}\x{FB29}\x{FB2A}-\x{FB36}\x{FB37}\x{FB38}-\x{FB3C}\x{FB3D}\x{FB3E}\x{FB3F}\x{FB40}-\x{FB41}\x{FB42}\x{FB43}-\x{FB44}\x{FB45}\x{FB46}-\x{FB4F}\x{FB50}-\x{FBB1}\x{FBB2}-\x{FBC1}\x{FBC2}-\x{FBD2}\x{FBD3}-\x{FD3D}\x{FD3E}\x{FD3F}\x{FD40}-\x{FD4F}\x{FD50}-\x{FD8F}\x{FD90}-\x{FD91}\x{FD92}-\x{FDC7}\x{FDC8}-\x{FDCF}\x{FDD0}-\x{FDEF}\x{FDF0}-\x{FDFB}\x{FDFC}\x{FDFD}\x{FDFE}-\x{FDFF}\x{FE00}-\x{FE0F}\x{FE10}-\x{FE16}\x{FE17}\x{FE18}\x{FE19}\x{FE20}-\x{FE2F}\x{FE30}\x{FE31}-\x{FE32}\x{FE33}-\x{FE34}\x{FE35}\x{FE36}\x{FE37}\x{FE38}\x{FE39}\x{FE3A}\x{FE3B}\x{FE3C}\x{FE3D}\x{FE3E}\x{FE3F}\x{FE40}\x{FE41}\x{FE42}\x{FE43}\x{FE44}\x{FE45}-\x{FE46}\x{FE47}\x{FE48}\x{FE49}-\x{FE4C}\x{FE4D}-\x{FE4F}\x{FE50}\x{FE51}\x{FE52}\x{FE54}\x{FE55}\x{FE56}-\x{FE57}\x{FE58}\x{FE59}\x{FE5A}\x{FE5B}\x{FE5C}\x{FE5D}\x{FE5E}\x{FE5F}\x{FE60}-\x{FE61}\x{FE62}\x{FE63}\x{FE64}-\x{FE66}\x{FE68}\x{FE69}\x{FE6A}\x{FE6B}\x{FE70}-\x{FE74}\x{FE75}\x{FE76}-\x{FEFC}\x{FEFD}-\x{FEFE}\x{FEFF}\x{FF01}-\x{FF02}\x{FF03}\x{FF04}\x{FF05}\x{FF06}-\x{FF07}\x{FF08}\x{FF09}\x{FF0A}\x{FF0B}\x{FF0C}\x{FF0D}\x{FF0E}-\x{FF0F}\x{FF1A}\x{FF1B}\x{FF1C}-\x{FF1E}\x{FF1F}-\x{FF20}\x{FF3B}\x{FF3C}\x{FF3D}\x{FF3E}\x{FF3F}\x{FF40}\x{FF5B}\x{FF5C}\x{FF5D}\x{FF5E}\x{FF5F}\x{FF60}\x{FF61}\x{FF62}\x{FF63}\x{FF64}-\x{FF65}\x{FFE0}-\x{FFE1}\x{FFE2}\x{FFE3}\x{FFE4}\x{FFE5}-\x{FFE6}\x{FFE8}\x{FFE9}-\x{FFEC}\x{FFED}-\x{FFEE}\x{FFF0}-\x{FFF8}\x{FFF9}-\x{FFFB}\x{FFFC}-\x{FFFD}\x{FFFE}-\x{FFFF}\x{10101}\x{10140}-\x{10174}\x{10175}-\x{10178}\x{10179}-\x{10189}\x{1018A}-\x{1018B}\x{1018C}\x{10190}-\x{1019C}\x{101A0}\x{101FD}\x{102E0}\x{10376}-\x{1037A}\x{10800}-\x{10805}\x{10806}-\x{10807}\x{10808}\x{10809}\x{1080A}-\x{10835}\x{10836}\x{10837}-\x{10838}\x{10839}-\x{1083B}\x{1083C}\x{1083D}-\x{1083E}\x{1083F}-\x{10855}\x{10856}\x{10857}\x{10858}-\x{1085F}\x{10860}-\x{10876}\x{10877}-\x{10878}\x{10879}-\x{1087F}\x{10880}-\x{1089E}\x{1089F}-\x{108A6}\x{108A7}-\x{108AF}\x{108B0}-\x{108DF}\x{108E0}-\x{108F2}\x{108F3}\x{108F4}-\x{108F5}\x{108F6}-\x{108FA}\x{108FB}-\x{108FF}\x{10900}-\x{10915}\x{10916}-\x{1091B}\x{1091C}-\x{1091E}\x{1091F}\x{10920}-\x{10939}\x{1093A}-\x{1093E}\x{1093F}\x{10940}-\x{1097F}\x{10980}-\x{109B7}\x{109B8}-\x{109BB}\x{109BC}-\x{109BD}\x{109BE}-\x{109BF}\x{109C0}-\x{109CF}\x{109D0}-\x{109D1}\x{109D2}-\x{109FF}\x{10A00}\x{10A01}-\x{10A03}\x{10A04}\x{10A05}-\x{10A06}\x{10A07}-\x{10A0B}\x{10A0C}-\x{10A0F}\x{10A10}-\x{10A13}\x{10A14}\x{10A15}-\x{10A17}\x{10A18}\x{10A19}-\x{10A35}\x{10A36}-\x{10A37}\x{10A38}-\x{10A3A}\x{10A3B}-\x{10A3E}\x{10A3F}\x{10A40}-\x{10A48}\x{10A49}-\x{10A4F}\x{10A50}-\x{10A58}\x{10A59}-\x{10A5F}\x{10A60}-\x{10A7C}\x{10A7D}-\x{10A7E}\x{10A7F}\x{10A80}-\x{10A9C}\x{10A9D}-\x{10A9F}\x{10AA0}-\x{10ABF}\x{10AC0}-\x{10AC7}\x{10AC8}\x{10AC9}-\x{10AE4}\x{10AE5}-\x{10AE6}\x{10AE7}-\x{10AEA}\x{10AEB}-\x{10AEF}\x{10AF0}-\x{10AF6}\x{10AF7}-\x{10AFF}\x{10B00}-\x{10B35}\x{10B36}-\x{10B38}\x{10B39}-\x{10B3F}\x{10B40}-\x{10B55}\x{10B56}-\x{10B57}\x{10B58}-\x{10B5F}\x{10B60}-\x{10B72}\x{10B73}-\x{10B77}\x{10B78}-\x{10B7F}\x{10B80}-\x{10B91}\x{10B92}-\x{10B98}\x{10B99}-\x{10B9C}\x{10B9D}-\x{10BA8}\x{10BA9}-\x{10BAF}\x{10BB0}-\x{10BFF}\x{10C00}-\x{10C48}\x{10C49}-\x{10C7F}\x{10C80}-\x{10CB2}\x{10CB3}-\x{10CBF}\x{10CC0}-\x{10CF2}\x{10CF3}-\x{10CF9}\x{10CFA}-\x{10CFF}\x{10D00}-\x{10D23}\x{10D24}-\x{10D27}\x{10D28}-\x{10D2F}\x{10D30}-\x{10D39}\x{10D3A}-\x{10D3F}\x{10D40}-\x{10E5F}\x{10E60}-\x{10E7E}\x{10E7F}\x{10E80}-\x{10EA9}\x{10EAA}\x{10EAB}-\x{10EAC}\x{10EAD}\x{10EAE}-\x{10EAF}\x{10EB0}-\x{10EB1}\x{10EB2}-\x{10EFF}\x{10F00}-\x{10F1C}\x{10F1D}-\x{10F26}\x{10F27}\x{10F28}-\x{10F2F}\x{10F30}-\x{10F45}\x{10F46}-\x{10F50}\x{10F51}-\x{10F54}\x{10F55}-\x{10F59}\x{10F5A}-\x{10F6F}\x{10F70}-\x{10FAF}\x{10FB0}-\x{10FC4}\x{10FC5}-\x{10FCB}\x{10FCC}-\x{10FDF}\x{10FE0}-\x{10FF6}\x{10FF7}-\x{10FFF}\x{11001}\x{11038}-\x{11046}\x{11052}-\x{11065}\x{1107F}-\x{11081}\x{110B3}-\x{110B6}\x{110B9}-\x{110BA}\x{11100}-\x{11102}\x{11127}-\x{1112B}\x{1112D}-\x{11134}\x{11173}\x{11180}-\x{11181}\x{111B6}-\x{111BE}\x{111C9}-\x{111CC}\x{111CF}\x{1122F}-\x{11231}\x{11234}\x{11236}-\x{11237}\x{1123E}\x{112DF}\x{112E3}-\x{112EA}\x{11300}-\x{11301}\x{1133B}-\x{1133C}\x{11340}\x{11366}-\x{1136C}\x{11370}-\x{11374}\x{11438}-\x{1143F}\x{11442}-\x{11444}\x{11446}\x{1145E}\x{114B3}-\x{114B8}\x{114BA}\x{114BF}-\x{114C0}\x{114C2}-\x{114C3}\x{115B2}-\x{115B5}\x{115BC}-\x{115BD}\x{115BF}-\x{115C0}\x{115DC}-\x{115DD}\x{11633}-\x{1163A}\x{1163D}\x{1163F}-\x{11640}\x{11660}-\x{1166C}\x{116AB}\x{116AD}\x{116B0}-\x{116B5}\x{116B7}\x{1171D}-\x{1171F}\x{11722}-\x{11725}\x{11727}-\x{1172B}\x{1182F}-\x{11837}\x{11839}-\x{1183A}\x{1193B}-\x{1193C}\x{1193E}\x{11943}\x{119D4}-\x{119D7}\x{119DA}-\x{119DB}\x{119E0}\x{11A01}-\x{11A06}\x{11A09}-\x{11A0A}\x{11A33}-\x{11A38}\x{11A3B}-\x{11A3E}\x{11A47}\x{11A51}-\x{11A56}\x{11A59}-\x{11A5B}\x{11A8A}-\x{11A96}\x{11A98}-\x{11A99}\x{11C30}-\x{11C36}\x{11C38}-\x{11C3D}\x{11C92}-\x{11CA7}\x{11CAA}-\x{11CB0}\x{11CB2}-\x{11CB3}\x{11CB5}-\x{11CB6}\x{11D31}-\x{11D36}\x{11D3A}\x{11D3C}-\x{11D3D}\x{11D3F}-\x{11D45}\x{11D47}\x{11D90}-\x{11D91}\x{11D95}\x{11D97}\x{11EF3}-\x{11EF4}\x{11FD5}-\x{11FDC}\x{11FDD}-\x{11FE0}\x{11FE1}-\x{11FF1}\x{16AF0}-\x{16AF4}\x{16B30}-\x{16B36}\x{16F4F}\x{16F8F}-\x{16F92}\x{16FE2}\x{16FE4}\x{1BC9D}-\x{1BC9E}\x{1BCA0}-\x{1BCA3}\x{1D167}-\x{1D169}\x{1D173}-\x{1D17A}\x{1D17B}-\x{1D182}\x{1D185}-\x{1D18B}\x{1D1AA}-\x{1D1AD}\x{1D200}-\x{1D241}\x{1D242}-\x{1D244}\x{1D245}\x{1D300}-\x{1D356}\x{1D6DB}\x{1D715}\x{1D74F}\x{1D789}\x{1D7C3}\x{1DA00}-\x{1DA36}\x{1DA3B}-\x{1DA6C}\x{1DA75}\x{1DA84}\x{1DA9B}-\x{1DA9F}\x{1DAA1}-\x{1DAAF}\x{1E000}-\x{1E006}\x{1E008}-\x{1E018}\x{1E01B}-\x{1E021}\x{1E023}-\x{1E024}\x{1E026}-\x{1E02A}\x{1E130}-\x{1E136}\x{1E2EC}-\x{1E2EF}\x{1E2FF}\x{1E800}-\x{1E8C4}\x{1E8C5}-\x{1E8C6}\x{1E8C7}-\x{1E8CF}\x{1E8D0}-\x{1E8D6}\x{1E8D7}-\x{1E8FF}\x{1E900}-\x{1E943}\x{1E944}-\x{1E94A}\x{1E94B}\x{1E94C}-\x{1E94F}\x{1E950}-\x{1E959}\x{1E95A}-\x{1E95D}\x{1E95E}-\x{1E95F}\x{1E960}-\x{1EC6F}\x{1EC70}\x{1EC71}-\x{1ECAB}\x{1ECAC}\x{1ECAD}-\x{1ECAF}\x{1ECB0}\x{1ECB1}-\x{1ECB4}\x{1ECB5}-\x{1ECBF}\x{1ECC0}-\x{1ECFF}\x{1ED00}\x{1ED01}-\x{1ED2D}\x{1ED2E}\x{1ED2F}-\x{1ED3D}\x{1ED3E}-\x{1ED4F}\x{1ED50}-\x{1EDFF}\x{1EE00}-\x{1EE03}\x{1EE04}\x{1EE05}-\x{1EE1F}\x{1EE20}\x{1EE21}-\x{1EE22}\x{1EE23}\x{1EE24}\x{1EE25}-\x{1EE26}\x{1EE27}\x{1EE28}\x{1EE29}-\x{1EE32}\x{1EE33}\x{1EE34}-\x{1EE37}\x{1EE38}\x{1EE39}\x{1EE3A}\x{1EE3B}\x{1EE3C}-\x{1EE41}\x{1EE42}\x{1EE43}-\x{1EE46}\x{1EE47}\x{1EE48}\x{1EE49}\x{1EE4A}\x{1EE4B}\x{1EE4C}\x{1EE4D}-\x{1EE4F}\x{1EE50}\x{1EE51}-\x{1EE52}\x{1EE53}\x{1EE54}\x{1EE55}-\x{1EE56}\x{1EE57}\x{1EE58}\x{1EE59}\x{1EE5A}\x{1EE5B}\x{1EE5C}\x{1EE5D}\x{1EE5E}\x{1EE5F}\x{1EE60}\x{1EE61}-\x{1EE62}\x{1EE63}\x{1EE64}\x{1EE65}-\x{1EE66}\x{1EE67}-\x{1EE6A}\x{1EE6B}\x{1EE6C}-\x{1EE72}\x{1EE73}\x{1EE74}-\x{1EE77}\x{1EE78}\x{1EE79}-\x{1EE7C}\x{1EE7D}\x{1EE7E}\x{1EE7F}\x{1EE80}-\x{1EE89}\x{1EE8A}\x{1EE8B}-\x{1EE9B}\x{1EE9C}-\x{1EEA0}\x{1EEA1}-\x{1EEA3}\x{1EEA4}\x{1EEA5}-\x{1EEA9}\x{1EEAA}\x{1EEAB}-\x{1EEBB}\x{1EEBC}-\x{1EEEF}\x{1EEF0}-\x{1EEF1}\x{1EEF2}-\x{1EEFF}\x{1EF00}-\x{1EFFF}\x{1F000}-\x{1F02B}\x{1F030}-\x{1F093}\x{1F0A0}-\x{1F0AE}\x{1F0B1}-\x{1F0BF}\x{1F0C1}-\x{1F0CF}\x{1F0D1}-\x{1F0F5}\x{1F10B}-\x{1F10C}\x{1F10D}-\x{1F10F}\x{1F12F}\x{1F16A}-\x{1F16F}\x{1F1AD}\x{1F260}-\x{1F265}\x{1F300}-\x{1F3FA}\x{1F3FB}-\x{1F3FF}\x{1F400}-\x{1F6D7}\x{1F6E0}-\x{1F6EC}\x{1F6F0}-\x{1F6FC}\x{1F700}-\x{1F773}\x{1F780}-\x{1F7D8}\x{1F7E0}-\x{1F7EB}\x{1F800}-\x{1F80B}\x{1F810}-\x{1F847}\x{1F850}-\x{1F859}\x{1F860}-\x{1F887}\x{1F890}-\x{1F8AD}\x{1F8B0}-\x{1F8B1}\x{1F900}-\x{1F978}\x{1F97A}-\x{1F9CB}\x{1F9CD}-\x{1FA53}\x{1FA60}-\x{1FA6D}\x{1FA70}-\x{1FA74}\x{1FA78}-\x{1FA7A}\x{1FA80}-\x{1FA86}\x{1FA90}-\x{1FAA8}\x{1FAB0}-\x{1FAB6}\x{1FAC0}-\x{1FAC2}\x{1FAD0}-\x{1FAD6}\x{1FB00}-\x{1FB92}\x{1FB94}-\x{1FBCA}\x{1FFFE}-\x{1FFFF}\x{2FFFE}-\x{2FFFF}\x{3FFFE}-\x{3FFFF}\x{4FFFE}-\x{4FFFF}\x{5FFFE}-\x{5FFFF}\x{6FFFE}-\x{6FFFF}\x{7FFFE}-\x{7FFFF}\x{8FFFE}-\x{8FFFF}\x{9FFFE}-\x{9FFFF}\x{AFFFE}-\x{AFFFF}\x{BFFFE}-\x{BFFFF}\x{CFFFE}-\x{CFFFF}\x{DFFFE}-\x{E0000}\x{E0001}\x{E0002}-\x{E001F}\x{E0020}-\x{E007F}\x{E0080}-\x{E00FF}\x{E0100}-\x{E01EF}\x{E01F0}-\x{E0FFF}\x{EFFFE}-\x{EFFFF}\x{FFFFE}-\x{FFFFF}\x{10FFFE}-\x{10FFFF}][\x{0300}-\x{036F}\x{0483}-\x{0487}\x{0488}-\x{0489}\x{0591}-\x{05BD}\x{05BF}\x{05C1}-\x{05C2}\x{05C4}-\x{05C5}\x{05C7}\x{0610}-\x{061A}\x{064B}-\x{065F}\x{0670}\x{06D6}-\x{06DC}\x{06DF}-\x{06E4}\x{06E7}-\x{06E8}\x{06EA}-\x{06ED}\x{0711}\x{0730}-\x{074A}\x{07A6}-\x{07B0}\x{07EB}-\x{07F3}\x{07FD}\x{0816}-\x{0819}\x{081B}-\x{0823}\x{0825}-\x{0827}\x{0829}-\x{082D}\x{0859}-\x{085B}\x{08D3}-\x{08E1}\x{08E3}-\x{0902}\x{093A}\x{093C}\x{0941}-\x{0948}\x{094D}\x{0951}-\x{0957}\x{0962}-\x{0963}\x{0981}\x{09BC}\x{09C1}-\x{09C4}\x{09CD}\x{09E2}-\x{09E3}\x{09FE}\x{0A01}-\x{0A02}\x{0A3C}\x{0A41}-\x{0A42}\x{0A47}-\x{0A48}\x{0A4B}-\x{0A4D}\x{0A51}\x{0A70}-\x{0A71}\x{0A75}\x{0A81}-\x{0A82}\x{0ABC}\x{0AC1}-\x{0AC5}\x{0AC7}-\x{0AC8}\x{0ACD}\x{0AE2}-\x{0AE3}\x{0AFA}-\x{0AFF}\x{0B01}\x{0B3C}\x{0B3F}\x{0B41}-\x{0B44}\x{0B4D}\x{0B55}-\x{0B56}\x{0B62}-\x{0B63}\x{0B82}\x{0BC0}\x{0BCD}\x{0C00}\x{0C04}\x{0C3E}-\x{0C40}\x{0C46}-\x{0C48}\x{0C4A}-\x{0C4D}\x{0C55}-\x{0C56}\x{0C62}-\x{0C63}\x{0C81}\x{0CBC}\x{0CCC}-\x{0CCD}\x{0CE2}-\x{0CE3}\x{0D00}-\x{0D01}\x{0D3B}-\x{0D3C}\x{0D41}-\x{0D44}\x{0D4D}\x{0D62}-\x{0D63}\x{0D81}\x{0DCA}\x{0DD2}-\x{0DD4}\x{0DD6}\x{0E31}\x{0E34}-\x{0E3A}\x{0E47}-\x{0E4E}\x{0EB1}\x{0EB4}-\x{0EBC}\x{0EC8}-\x{0ECD}\x{0F18}-\x{0F19}\x{0F35}\x{0F37}\x{0F39}\x{0F71}-\x{0F7E}\x{0F80}-\x{0F84}\x{0F86}-\x{0F87}\x{0F8D}-\x{0F97}\x{0F99}-\x{0FBC}\x{0FC6}\x{102D}-\x{1030}\x{1032}-\x{1037}\x{1039}-\x{103A}\x{103D}-\x{103E}\x{1058}-\x{1059}\x{105E}-\x{1060}\x{1071}-\x{1074}\x{1082}\x{1085}-\x{1086}\x{108D}\x{109D}\x{135D}-\x{135F}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}-\x{1753}\x{1772}-\x{1773}\x{17B4}-\x{17B5}\x{17B7}-\x{17BD}\x{17C6}\x{17C9}-\x{17D3}\x{17DD}\x{180B}-\x{180D}\x{1885}-\x{1886}\x{18A9}\x{1920}-\x{1922}\x{1927}-\x{1928}\x{1932}\x{1939}-\x{193B}\x{1A17}-\x{1A18}\x{1A1B}\x{1A56}\x{1A58}-\x{1A5E}\x{1A60}\x{1A62}\x{1A65}-\x{1A6C}\x{1A73}-\x{1A7C}\x{1A7F}\x{1AB0}-\x{1ABD}\x{1ABE}\x{1ABF}-\x{1AC0}\x{1B00}-\x{1B03}\x{1B34}\x{1B36}-\x{1B3A}\x{1B3C}\x{1B42}\x{1B6B}-\x{1B73}\x{1B80}-\x{1B81}\x{1BA2}-\x{1BA5}\x{1BA8}-\x{1BA9}\x{1BAB}-\x{1BAD}\x{1BE6}\x{1BE8}-\x{1BE9}\x{1BED}\x{1BEF}-\x{1BF1}\x{1C2C}-\x{1C33}\x{1C36}-\x{1C37}\x{1CD0}-\x{1CD2}\x{1CD4}-\x{1CE0}\x{1CE2}-\x{1CE8}\x{1CED}\x{1CF4}\x{1CF8}-\x{1CF9}\x{1DC0}-\x{1DF9}\x{1DFB}-\x{1DFF}\x{20D0}-\x{20DC}\x{20DD}-\x{20E0}\x{20E1}\x{20E2}-\x{20E4}\x{20E5}-\x{20F0}\x{2CEF}-\x{2CF1}\x{2D7F}\x{2DE0}-\x{2DFF}\x{302A}-\x{302D}\x{3099}-\x{309A}\x{A66F}\x{A670}-\x{A672}\x{A674}-\x{A67D}\x{A69E}-\x{A69F}\x{A6F0}-\x{A6F1}\x{A802}\x{A806}\x{A80B}\x{A825}-\x{A826}\x{A82C}\x{A8C4}-\x{A8C5}\x{A8E0}-\x{A8F1}\x{A8FF}\x{A926}-\x{A92D}\x{A947}-\x{A951}\x{A980}-\x{A982}\x{A9B3}\x{A9B6}-\x{A9B9}\x{A9BC}-\x{A9BD}\x{A9E5}\x{AA29}-\x{AA2E}\x{AA31}-\x{AA32}\x{AA35}-\x{AA36}\x{AA43}\x{AA4C}\x{AA7C}\x{AAB0}\x{AAB2}-\x{AAB4}\x{AAB7}-\x{AAB8}\x{AABE}-\x{AABF}\x{AAC1}\x{AAEC}-\x{AAED}\x{AAF6}\x{ABE5}\x{ABE8}\x{ABED}\x{FB1E}\x{FE00}-\x{FE0F}\x{FE20}-\x{FE2F}\x{101FD}\x{102E0}\x{10376}-\x{1037A}\x{10A01}-\x{10A03}\x{10A05}-\x{10A06}\x{10A0C}-\x{10A0F}\x{10A38}-\x{10A3A}\x{10A3F}\x{10AE5}-\x{10AE6}\x{10D24}-\x{10D27}\x{10EAB}-\x{10EAC}\x{10F46}-\x{10F50}\x{11001}\x{11038}-\x{11046}\x{1107F}-\x{11081}\x{110B3}-\x{110B6}\x{110B9}-\x{110BA}\x{11100}-\x{11102}\x{11127}-\x{1112B}\x{1112D}-\x{11134}\x{11173}\x{11180}-\x{11181}\x{111B6}-\x{111BE}\x{111C9}-\x{111CC}\x{111CF}\x{1122F}-\x{11231}\x{11234}\x{11236}-\x{11237}\x{1123E}\x{112DF}\x{112E3}-\x{112EA}\x{11300}-\x{11301}\x{1133B}-\x{1133C}\x{11340}\x{11366}-\x{1136C}\x{11370}-\x{11374}\x{11438}-\x{1143F}\x{11442}-\x{11444}\x{11446}\x{1145E}\x{114B3}-\x{114B8}\x{114BA}\x{114BF}-\x{114C0}\x{114C2}-\x{114C3}\x{115B2}-\x{115B5}\x{115BC}-\x{115BD}\x{115BF}-\x{115C0}\x{115DC}-\x{115DD}\x{11633}-\x{1163A}\x{1163D}\x{1163F}-\x{11640}\x{116AB}\x{116AD}\x{116B0}-\x{116B5}\x{116B7}\x{1171D}-\x{1171F}\x{11722}-\x{11725}\x{11727}-\x{1172B}\x{1182F}-\x{11837}\x{11839}-\x{1183A}\x{1193B}-\x{1193C}\x{1193E}\x{11943}\x{119D4}-\x{119D7}\x{119DA}-\x{119DB}\x{119E0}\x{11A01}-\x{11A06}\x{11A09}-\x{11A0A}\x{11A33}-\x{11A38}\x{11A3B}-\x{11A3E}\x{11A47}\x{11A51}-\x{11A56}\x{11A59}-\x{11A5B}\x{11A8A}-\x{11A96}\x{11A98}-\x{11A99}\x{11C30}-\x{11C36}\x{11C38}-\x{11C3D}\x{11C92}-\x{11CA7}\x{11CAA}-\x{11CB0}\x{11CB2}-\x{11CB3}\x{11CB5}-\x{11CB6}\x{11D31}-\x{11D36}\x{11D3A}\x{11D3C}-\x{11D3D}\x{11D3F}-\x{11D45}\x{11D47}\x{11D90}-\x{11D91}\x{11D95}\x{11D97}\x{11EF3}-\x{11EF4}\x{16AF0}-\x{16AF4}\x{16B30}-\x{16B36}\x{16F4F}\x{16F8F}-\x{16F92}\x{16FE4}\x{1BC9D}-\x{1BC9E}\x{1D167}-\x{1D169}\x{1D17B}-\x{1D182}\x{1D185}-\x{1D18B}\x{1D1AA}-\x{1D1AD}\x{1D242}-\x{1D244}\x{1DA00}-\x{1DA36}\x{1DA3B}-\x{1DA6C}\x{1DA75}\x{1DA84}\x{1DA9B}-\x{1DA9F}\x{1DAA1}-\x{1DAAF}\x{1E000}-\x{1E006}\x{1E008}-\x{1E018}\x{1E01B}-\x{1E021}\x{1E023}-\x{1E024}\x{1E026}-\x{1E02A}\x{1E130}-\x{1E136}\x{1E2EC}-\x{1E2EF}\x{1E8D0}-\x{1E8D6}\x{1E944}-\x{1E94A}\x{E0100}-\x{E01EF}]*$/u';
+
+    const ZWNJ = '/([\x{A872}\x{10ACD}\x{10AD7}\x{10D00}\x{10FCB}\x{0620}\x{0626}\x{0628}\x{062A}-\x{062E}\x{0633}-\x{063F}\x{0641}-\x{0647}\x{0649}-\x{064A}\x{066E}-\x{066F}\x{0678}-\x{0687}\x{069A}-\x{06BF}\x{06C1}-\x{06C2}\x{06CC}\x{06CE}\x{06D0}-\x{06D1}\x{06FA}-\x{06FC}\x{06FF}\x{0712}-\x{0714}\x{071A}-\x{071D}\x{071F}-\x{0727}\x{0729}\x{072B}\x{072D}-\x{072E}\x{074E}-\x{0758}\x{075C}-\x{076A}\x{076D}-\x{0770}\x{0772}\x{0775}-\x{0777}\x{077A}-\x{077F}\x{07CA}-\x{07EA}\x{0841}-\x{0845}\x{0848}\x{084A}-\x{0853}\x{0855}\x{0860}\x{0862}-\x{0865}\x{0868}\x{08A0}-\x{08A9}\x{08AF}-\x{08B0}\x{08B3}-\x{08B4}\x{08B6}-\x{08B8}\x{08BA}-\x{08C7}\x{1807}\x{1820}-\x{1842}\x{1843}\x{1844}-\x{1878}\x{1887}-\x{18A8}\x{18AA}\x{A840}-\x{A871}\x{10AC0}-\x{10AC4}\x{10AD3}-\x{10AD6}\x{10AD8}-\x{10ADC}\x{10ADE}-\x{10AE0}\x{10AEB}-\x{10AEE}\x{10B80}\x{10B82}\x{10B86}-\x{10B88}\x{10B8A}-\x{10B8B}\x{10B8D}\x{10B90}\x{10BAD}-\x{10BAE}\x{10D01}-\x{10D21}\x{10D23}\x{10F30}-\x{10F32}\x{10F34}-\x{10F44}\x{10F51}-\x{10F53}\x{10FB0}\x{10FB2}-\x{10FB3}\x{10FB8}\x{10FBB}-\x{10FBC}\x{10FBE}-\x{10FBF}\x{10FC1}\x{10FC4}\x{10FCA}\x{1E900}-\x{1E943}][\x{00AD}\x{0300}-\x{036F}\x{0483}-\x{0487}\x{0488}-\x{0489}\x{0591}-\x{05BD}\x{05BF}\x{05C1}-\x{05C2}\x{05C4}-\x{05C5}\x{05C7}\x{0610}-\x{061A}\x{061C}\x{064B}-\x{065F}\x{0670}\x{06D6}-\x{06DC}\x{06DF}-\x{06E4}\x{06E7}-\x{06E8}\x{06EA}-\x{06ED}\x{070F}\x{0711}\x{0730}-\x{074A}\x{07A6}-\x{07B0}\x{07EB}-\x{07F3}\x{07FD}\x{0816}-\x{0819}\x{081B}-\x{0823}\x{0825}-\x{0827}\x{0829}-\x{082D}\x{0859}-\x{085B}\x{08D3}-\x{08E1}\x{08E3}-\x{0902}\x{093A}\x{093C}\x{0941}-\x{0948}\x{094D}\x{0951}-\x{0957}\x{0962}-\x{0963}\x{0981}\x{09BC}\x{09C1}-\x{09C4}\x{09CD}\x{09E2}-\x{09E3}\x{09FE}\x{0A01}-\x{0A02}\x{0A3C}\x{0A41}-\x{0A42}\x{0A47}-\x{0A48}\x{0A4B}-\x{0A4D}\x{0A51}\x{0A70}-\x{0A71}\x{0A75}\x{0A81}-\x{0A82}\x{0ABC}\x{0AC1}-\x{0AC5}\x{0AC7}-\x{0AC8}\x{0ACD}\x{0AE2}-\x{0AE3}\x{0AFA}-\x{0AFF}\x{0B01}\x{0B3C}\x{0B3F}\x{0B41}-\x{0B44}\x{0B4D}\x{0B55}-\x{0B56}\x{0B62}-\x{0B63}\x{0B82}\x{0BC0}\x{0BCD}\x{0C00}\x{0C04}\x{0C3E}-\x{0C40}\x{0C46}-\x{0C48}\x{0C4A}-\x{0C4D}\x{0C55}-\x{0C56}\x{0C62}-\x{0C63}\x{0C81}\x{0CBC}\x{0CBF}\x{0CC6}\x{0CCC}-\x{0CCD}\x{0CE2}-\x{0CE3}\x{0D00}-\x{0D01}\x{0D3B}-\x{0D3C}\x{0D41}-\x{0D44}\x{0D4D}\x{0D62}-\x{0D63}\x{0D81}\x{0DCA}\x{0DD2}-\x{0DD4}\x{0DD6}\x{0E31}\x{0E34}-\x{0E3A}\x{0E47}-\x{0E4E}\x{0EB1}\x{0EB4}-\x{0EBC}\x{0EC8}-\x{0ECD}\x{0F18}-\x{0F19}\x{0F35}\x{0F37}\x{0F39}\x{0F71}-\x{0F7E}\x{0F80}-\x{0F84}\x{0F86}-\x{0F87}\x{0F8D}-\x{0F97}\x{0F99}-\x{0FBC}\x{0FC6}\x{102D}-\x{1030}\x{1032}-\x{1037}\x{1039}-\x{103A}\x{103D}-\x{103E}\x{1058}-\x{1059}\x{105E}-\x{1060}\x{1071}-\x{1074}\x{1082}\x{1085}-\x{1086}\x{108D}\x{109D}\x{135D}-\x{135F}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}-\x{1753}\x{1772}-\x{1773}\x{17B4}-\x{17B5}\x{17B7}-\x{17BD}\x{17C6}\x{17C9}-\x{17D3}\x{17DD}\x{180B}-\x{180D}\x{1885}-\x{1886}\x{18A9}\x{1920}-\x{1922}\x{1927}-\x{1928}\x{1932}\x{1939}-\x{193B}\x{1A17}-\x{1A18}\x{1A1B}\x{1A56}\x{1A58}-\x{1A5E}\x{1A60}\x{1A62}\x{1A65}-\x{1A6C}\x{1A73}-\x{1A7C}\x{1A7F}\x{1AB0}-\x{1ABD}\x{1ABE}\x{1ABF}-\x{1AC0}\x{1B00}-\x{1B03}\x{1B34}\x{1B36}-\x{1B3A}\x{1B3C}\x{1B42}\x{1B6B}-\x{1B73}\x{1B80}-\x{1B81}\x{1BA2}-\x{1BA5}\x{1BA8}-\x{1BA9}\x{1BAB}-\x{1BAD}\x{1BE6}\x{1BE8}-\x{1BE9}\x{1BED}\x{1BEF}-\x{1BF1}\x{1C2C}-\x{1C33}\x{1C36}-\x{1C37}\x{1CD0}-\x{1CD2}\x{1CD4}-\x{1CE0}\x{1CE2}-\x{1CE8}\x{1CED}\x{1CF4}\x{1CF8}-\x{1CF9}\x{1DC0}-\x{1DF9}\x{1DFB}-\x{1DFF}\x{200B}\x{200E}-\x{200F}\x{202A}-\x{202E}\x{2060}-\x{2064}\x{206A}-\x{206F}\x{20D0}-\x{20DC}\x{20DD}-\x{20E0}\x{20E1}\x{20E2}-\x{20E4}\x{20E5}-\x{20F0}\x{2CEF}-\x{2CF1}\x{2D7F}\x{2DE0}-\x{2DFF}\x{302A}-\x{302D}\x{3099}-\x{309A}\x{A66F}\x{A670}-\x{A672}\x{A674}-\x{A67D}\x{A69E}-\x{A69F}\x{A6F0}-\x{A6F1}\x{A802}\x{A806}\x{A80B}\x{A825}-\x{A826}\x{A82C}\x{A8C4}-\x{A8C5}\x{A8E0}-\x{A8F1}\x{A8FF}\x{A926}-\x{A92D}\x{A947}-\x{A951}\x{A980}-\x{A982}\x{A9B3}\x{A9B6}-\x{A9B9}\x{A9BC}-\x{A9BD}\x{A9E5}\x{AA29}-\x{AA2E}\x{AA31}-\x{AA32}\x{AA35}-\x{AA36}\x{AA43}\x{AA4C}\x{AA7C}\x{AAB0}\x{AAB2}-\x{AAB4}\x{AAB7}-\x{AAB8}\x{AABE}-\x{AABF}\x{AAC1}\x{AAEC}-\x{AAED}\x{AAF6}\x{ABE5}\x{ABE8}\x{ABED}\x{FB1E}\x{FE00}-\x{FE0F}\x{FE20}-\x{FE2F}\x{FEFF}\x{FFF9}-\x{FFFB}\x{101FD}\x{102E0}\x{10376}-\x{1037A}\x{10A01}-\x{10A03}\x{10A05}-\x{10A06}\x{10A0C}-\x{10A0F}\x{10A38}-\x{10A3A}\x{10A3F}\x{10AE5}-\x{10AE6}\x{10D24}-\x{10D27}\x{10EAB}-\x{10EAC}\x{10F46}-\x{10F50}\x{11001}\x{11038}-\x{11046}\x{1107F}-\x{11081}\x{110B3}-\x{110B6}\x{110B9}-\x{110BA}\x{11100}-\x{11102}\x{11127}-\x{1112B}\x{1112D}-\x{11134}\x{11173}\x{11180}-\x{11181}\x{111B6}-\x{111BE}\x{111C9}-\x{111CC}\x{111CF}\x{1122F}-\x{11231}\x{11234}\x{11236}-\x{11237}\x{1123E}\x{112DF}\x{112E3}-\x{112EA}\x{11300}-\x{11301}\x{1133B}-\x{1133C}\x{11340}\x{11366}-\x{1136C}\x{11370}-\x{11374}\x{11438}-\x{1143F}\x{11442}-\x{11444}\x{11446}\x{1145E}\x{114B3}-\x{114B8}\x{114BA}\x{114BF}-\x{114C0}\x{114C2}-\x{114C3}\x{115B2}-\x{115B5}\x{115BC}-\x{115BD}\x{115BF}-\x{115C0}\x{115DC}-\x{115DD}\x{11633}-\x{1163A}\x{1163D}\x{1163F}-\x{11640}\x{116AB}\x{116AD}\x{116B0}-\x{116B5}\x{116B7}\x{1171D}-\x{1171F}\x{11722}-\x{11725}\x{11727}-\x{1172B}\x{1182F}-\x{11837}\x{11839}-\x{1183A}\x{1193B}-\x{1193C}\x{1193E}\x{11943}\x{119D4}-\x{119D7}\x{119DA}-\x{119DB}\x{119E0}\x{11A01}-\x{11A0A}\x{11A33}-\x{11A38}\x{11A3B}-\x{11A3E}\x{11A47}\x{11A51}-\x{11A56}\x{11A59}-\x{11A5B}\x{11A8A}-\x{11A96}\x{11A98}-\x{11A99}\x{11C30}-\x{11C36}\x{11C38}-\x{11C3D}\x{11C3F}\x{11C92}-\x{11CA7}\x{11CAA}-\x{11CB0}\x{11CB2}-\x{11CB3}\x{11CB5}-\x{11CB6}\x{11D31}-\x{11D36}\x{11D3A}\x{11D3C}-\x{11D3D}\x{11D3F}-\x{11D45}\x{11D47}\x{11D90}-\x{11D91}\x{11D95}\x{11D97}\x{11EF3}-\x{11EF4}\x{13430}-\x{13438}\x{16AF0}-\x{16AF4}\x{16B30}-\x{16B36}\x{16F4F}\x{16F8F}-\x{16F92}\x{16FE4}\x{1BC9D}-\x{1BC9E}\x{1BCA0}-\x{1BCA3}\x{1D167}-\x{1D169}\x{1D173}-\x{1D17A}\x{1D17B}-\x{1D182}\x{1D185}-\x{1D18B}\x{1D1AA}-\x{1D1AD}\x{1D242}-\x{1D244}\x{1DA00}-\x{1DA36}\x{1DA3B}-\x{1DA6C}\x{1DA75}\x{1DA84}\x{1DA9B}-\x{1DA9F}\x{1DAA1}-\x{1DAAF}\x{1E000}-\x{1E006}\x{1E008}-\x{1E018}\x{1E01B}-\x{1E021}\x{1E023}-\x{1E024}\x{1E026}-\x{1E02A}\x{1E130}-\x{1E136}\x{1E2EC}-\x{1E2EF}\x{1E8D0}-\x{1E8D6}\x{1E944}-\x{1E94A}\x{1E94B}\x{E0001}\x{E0020}-\x{E007F}\x{E0100}-\x{E01EF}]*\x{200C}[\x{00AD}\x{0300}-\x{036F}\x{0483}-\x{0487}\x{0488}-\x{0489}\x{0591}-\x{05BD}\x{05BF}\x{05C1}-\x{05C2}\x{05C4}-\x{05C5}\x{05C7}\x{0610}-\x{061A}\x{061C}\x{064B}-\x{065F}\x{0670}\x{06D6}-\x{06DC}\x{06DF}-\x{06E4}\x{06E7}-\x{06E8}\x{06EA}-\x{06ED}\x{070F}\x{0711}\x{0730}-\x{074A}\x{07A6}-\x{07B0}\x{07EB}-\x{07F3}\x{07FD}\x{0816}-\x{0819}\x{081B}-\x{0823}\x{0825}-\x{0827}\x{0829}-\x{082D}\x{0859}-\x{085B}\x{08D3}-\x{08E1}\x{08E3}-\x{0902}\x{093A}\x{093C}\x{0941}-\x{0948}\x{094D}\x{0951}-\x{0957}\x{0962}-\x{0963}\x{0981}\x{09BC}\x{09C1}-\x{09C4}\x{09CD}\x{09E2}-\x{09E3}\x{09FE}\x{0A01}-\x{0A02}\x{0A3C}\x{0A41}-\x{0A42}\x{0A47}-\x{0A48}\x{0A4B}-\x{0A4D}\x{0A51}\x{0A70}-\x{0A71}\x{0A75}\x{0A81}-\x{0A82}\x{0ABC}\x{0AC1}-\x{0AC5}\x{0AC7}-\x{0AC8}\x{0ACD}\x{0AE2}-\x{0AE3}\x{0AFA}-\x{0AFF}\x{0B01}\x{0B3C}\x{0B3F}\x{0B41}-\x{0B44}\x{0B4D}\x{0B55}-\x{0B56}\x{0B62}-\x{0B63}\x{0B82}\x{0BC0}\x{0BCD}\x{0C00}\x{0C04}\x{0C3E}-\x{0C40}\x{0C46}-\x{0C48}\x{0C4A}-\x{0C4D}\x{0C55}-\x{0C56}\x{0C62}-\x{0C63}\x{0C81}\x{0CBC}\x{0CBF}\x{0CC6}\x{0CCC}-\x{0CCD}\x{0CE2}-\x{0CE3}\x{0D00}-\x{0D01}\x{0D3B}-\x{0D3C}\x{0D41}-\x{0D44}\x{0D4D}\x{0D62}-\x{0D63}\x{0D81}\x{0DCA}\x{0DD2}-\x{0DD4}\x{0DD6}\x{0E31}\x{0E34}-\x{0E3A}\x{0E47}-\x{0E4E}\x{0EB1}\x{0EB4}-\x{0EBC}\x{0EC8}-\x{0ECD}\x{0F18}-\x{0F19}\x{0F35}\x{0F37}\x{0F39}\x{0F71}-\x{0F7E}\x{0F80}-\x{0F84}\x{0F86}-\x{0F87}\x{0F8D}-\x{0F97}\x{0F99}-\x{0FBC}\x{0FC6}\x{102D}-\x{1030}\x{1032}-\x{1037}\x{1039}-\x{103A}\x{103D}-\x{103E}\x{1058}-\x{1059}\x{105E}-\x{1060}\x{1071}-\x{1074}\x{1082}\x{1085}-\x{1086}\x{108D}\x{109D}\x{135D}-\x{135F}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}-\x{1753}\x{1772}-\x{1773}\x{17B4}-\x{17B5}\x{17B7}-\x{17BD}\x{17C6}\x{17C9}-\x{17D3}\x{17DD}\x{180B}-\x{180D}\x{1885}-\x{1886}\x{18A9}\x{1920}-\x{1922}\x{1927}-\x{1928}\x{1932}\x{1939}-\x{193B}\x{1A17}-\x{1A18}\x{1A1B}\x{1A56}\x{1A58}-\x{1A5E}\x{1A60}\x{1A62}\x{1A65}-\x{1A6C}\x{1A73}-\x{1A7C}\x{1A7F}\x{1AB0}-\x{1ABD}\x{1ABE}\x{1ABF}-\x{1AC0}\x{1B00}-\x{1B03}\x{1B34}\x{1B36}-\x{1B3A}\x{1B3C}\x{1B42}\x{1B6B}-\x{1B73}\x{1B80}-\x{1B81}\x{1BA2}-\x{1BA5}\x{1BA8}-\x{1BA9}\x{1BAB}-\x{1BAD}\x{1BE6}\x{1BE8}-\x{1BE9}\x{1BED}\x{1BEF}-\x{1BF1}\x{1C2C}-\x{1C33}\x{1C36}-\x{1C37}\x{1CD0}-\x{1CD2}\x{1CD4}-\x{1CE0}\x{1CE2}-\x{1CE8}\x{1CED}\x{1CF4}\x{1CF8}-\x{1CF9}\x{1DC0}-\x{1DF9}\x{1DFB}-\x{1DFF}\x{200B}\x{200E}-\x{200F}\x{202A}-\x{202E}\x{2060}-\x{2064}\x{206A}-\x{206F}\x{20D0}-\x{20DC}\x{20DD}-\x{20E0}\x{20E1}\x{20E2}-\x{20E4}\x{20E5}-\x{20F0}\x{2CEF}-\x{2CF1}\x{2D7F}\x{2DE0}-\x{2DFF}\x{302A}-\x{302D}\x{3099}-\x{309A}\x{A66F}\x{A670}-\x{A672}\x{A674}-\x{A67D}\x{A69E}-\x{A69F}\x{A6F0}-\x{A6F1}\x{A802}\x{A806}\x{A80B}\x{A825}-\x{A826}\x{A82C}\x{A8C4}-\x{A8C5}\x{A8E0}-\x{A8F1}\x{A8FF}\x{A926}-\x{A92D}\x{A947}-\x{A951}\x{A980}-\x{A982}\x{A9B3}\x{A9B6}-\x{A9B9}\x{A9BC}-\x{A9BD}\x{A9E5}\x{AA29}-\x{AA2E}\x{AA31}-\x{AA32}\x{AA35}-\x{AA36}\x{AA43}\x{AA4C}\x{AA7C}\x{AAB0}\x{AAB2}-\x{AAB4}\x{AAB7}-\x{AAB8}\x{AABE}-\x{AABF}\x{AAC1}\x{AAEC}-\x{AAED}\x{AAF6}\x{ABE5}\x{ABE8}\x{ABED}\x{FB1E}\x{FE00}-\x{FE0F}\x{FE20}-\x{FE2F}\x{FEFF}\x{FFF9}-\x{FFFB}\x{101FD}\x{102E0}\x{10376}-\x{1037A}\x{10A01}-\x{10A03}\x{10A05}-\x{10A06}\x{10A0C}-\x{10A0F}\x{10A38}-\x{10A3A}\x{10A3F}\x{10AE5}-\x{10AE6}\x{10D24}-\x{10D27}\x{10EAB}-\x{10EAC}\x{10F46}-\x{10F50}\x{11001}\x{11038}-\x{11046}\x{1107F}-\x{11081}\x{110B3}-\x{110B6}\x{110B9}-\x{110BA}\x{11100}-\x{11102}\x{11127}-\x{1112B}\x{1112D}-\x{11134}\x{11173}\x{11180}-\x{11181}\x{111B6}-\x{111BE}\x{111C9}-\x{111CC}\x{111CF}\x{1122F}-\x{11231}\x{11234}\x{11236}-\x{11237}\x{1123E}\x{112DF}\x{112E3}-\x{112EA}\x{11300}-\x{11301}\x{1133B}-\x{1133C}\x{11340}\x{11366}-\x{1136C}\x{11370}-\x{11374}\x{11438}-\x{1143F}\x{11442}-\x{11444}\x{11446}\x{1145E}\x{114B3}-\x{114B8}\x{114BA}\x{114BF}-\x{114C0}\x{114C2}-\x{114C3}\x{115B2}-\x{115B5}\x{115BC}-\x{115BD}\x{115BF}-\x{115C0}\x{115DC}-\x{115DD}\x{11633}-\x{1163A}\x{1163D}\x{1163F}-\x{11640}\x{116AB}\x{116AD}\x{116B0}-\x{116B5}\x{116B7}\x{1171D}-\x{1171F}\x{11722}-\x{11725}\x{11727}-\x{1172B}\x{1182F}-\x{11837}\x{11839}-\x{1183A}\x{1193B}-\x{1193C}\x{1193E}\x{11943}\x{119D4}-\x{119D7}\x{119DA}-\x{119DB}\x{119E0}\x{11A01}-\x{11A0A}\x{11A33}-\x{11A38}\x{11A3B}-\x{11A3E}\x{11A47}\x{11A51}-\x{11A56}\x{11A59}-\x{11A5B}\x{11A8A}-\x{11A96}\x{11A98}-\x{11A99}\x{11C30}-\x{11C36}\x{11C38}-\x{11C3D}\x{11C3F}\x{11C92}-\x{11CA7}\x{11CAA}-\x{11CB0}\x{11CB2}-\x{11CB3}\x{11CB5}-\x{11CB6}\x{11D31}-\x{11D36}\x{11D3A}\x{11D3C}-\x{11D3D}\x{11D3F}-\x{11D45}\x{11D47}\x{11D90}-\x{11D91}\x{11D95}\x{11D97}\x{11EF3}-\x{11EF4}\x{13430}-\x{13438}\x{16AF0}-\x{16AF4}\x{16B30}-\x{16B36}\x{16F4F}\x{16F8F}-\x{16F92}\x{16FE4}\x{1BC9D}-\x{1BC9E}\x{1BCA0}-\x{1BCA3}\x{1D167}-\x{1D169}\x{1D173}-\x{1D17A}\x{1D17B}-\x{1D182}\x{1D185}-\x{1D18B}\x{1D1AA}-\x{1D1AD}\x{1D242}-\x{1D244}\x{1DA00}-\x{1DA36}\x{1DA3B}-\x{1DA6C}\x{1DA75}\x{1DA84}\x{1DA9B}-\x{1DA9F}\x{1DAA1}-\x{1DAAF}\x{1E000}-\x{1E006}\x{1E008}-\x{1E018}\x{1E01B}-\x{1E021}\x{1E023}-\x{1E024}\x{1E026}-\x{1E02A}\x{1E130}-\x{1E136}\x{1E2EC}-\x{1E2EF}\x{1E8D0}-\x{1E8D6}\x{1E944}-\x{1E94A}\x{1E94B}\x{E0001}\x{E0020}-\x{E007F}\x{E0100}-\x{E01EF}]*)[\x{0622}-\x{0625}\x{0627}\x{0629}\x{062F}-\x{0632}\x{0648}\x{0671}-\x{0673}\x{0675}-\x{0677}\x{0688}-\x{0699}\x{06C0}\x{06C3}-\x{06CB}\x{06CD}\x{06CF}\x{06D2}-\x{06D3}\x{06D5}\x{06EE}-\x{06EF}\x{0710}\x{0715}-\x{0719}\x{071E}\x{0728}\x{072A}\x{072C}\x{072F}\x{074D}\x{0759}-\x{075B}\x{076B}-\x{076C}\x{0771}\x{0773}-\x{0774}\x{0778}-\x{0779}\x{0840}\x{0846}-\x{0847}\x{0849}\x{0854}\x{0856}-\x{0858}\x{0867}\x{0869}-\x{086A}\x{08AA}-\x{08AC}\x{08AE}\x{08B1}-\x{08B2}\x{08B9}\x{10AC5}\x{10AC7}\x{10AC9}-\x{10ACA}\x{10ACE}-\x{10AD2}\x{10ADD}\x{10AE1}\x{10AE4}\x{10AEF}\x{10B81}\x{10B83}-\x{10B85}\x{10B89}\x{10B8C}\x{10B8E}-\x{10B8F}\x{10B91}\x{10BA9}-\x{10BAC}\x{10D22}\x{10F33}\x{10F54}\x{10FB4}-\x{10FB6}\x{10FB9}-\x{10FBA}\x{10FBD}\x{10FC2}-\x{10FC3}\x{10FC9}\x{0620}\x{0626}\x{0628}\x{062A}-\x{062E}\x{0633}-\x{063F}\x{0641}-\x{0647}\x{0649}-\x{064A}\x{066E}-\x{066F}\x{0678}-\x{0687}\x{069A}-\x{06BF}\x{06C1}-\x{06C2}\x{06CC}\x{06CE}\x{06D0}-\x{06D1}\x{06FA}-\x{06FC}\x{06FF}\x{0712}-\x{0714}\x{071A}-\x{071D}\x{071F}-\x{0727}\x{0729}\x{072B}\x{072D}-\x{072E}\x{074E}-\x{0758}\x{075C}-\x{076A}\x{076D}-\x{0770}\x{0772}\x{0775}-\x{0777}\x{077A}-\x{077F}\x{07CA}-\x{07EA}\x{0841}-\x{0845}\x{0848}\x{084A}-\x{0853}\x{0855}\x{0860}\x{0862}-\x{0865}\x{0868}\x{08A0}-\x{08A9}\x{08AF}-\x{08B0}\x{08B3}-\x{08B4}\x{08B6}-\x{08B8}\x{08BA}-\x{08C7}\x{1807}\x{1820}-\x{1842}\x{1843}\x{1844}-\x{1878}\x{1887}-\x{18A8}\x{18AA}\x{A840}-\x{A871}\x{10AC0}-\x{10AC4}\x{10AD3}-\x{10AD6}\x{10AD8}-\x{10ADC}\x{10ADE}-\x{10AE0}\x{10AEB}-\x{10AEE}\x{10B80}\x{10B82}\x{10B86}-\x{10B88}\x{10B8A}-\x{10B8B}\x{10B8D}\x{10B90}\x{10BAD}-\x{10BAE}\x{10D01}-\x{10D21}\x{10D23}\x{10F30}-\x{10F32}\x{10F34}-\x{10F44}\x{10F51}-\x{10F53}\x{10FB0}\x{10FB2}-\x{10FB3}\x{10FB8}\x{10FBB}-\x{10FBC}\x{10FBE}-\x{10FBF}\x{10FC1}\x{10FC4}\x{10FCA}\x{1E900}-\x{1E943}]/u';
+}
diff --git a/vendor/symfony/polyfill-intl-idn/Resources/unidata/deviation.php b/vendor/symfony/polyfill-intl-idn/Resources/unidata/deviation.php
new file mode 100755
index 0000000..0bbd335
--- /dev/null
+++ b/vendor/symfony/polyfill-intl-idn/Resources/unidata/deviation.php
@@ -0,0 +1,8 @@
+<?php
+
+return array (
+  223 => 'ss',
+  962 => 'σ',
+  8204 => '',
+  8205 => '',
+);
diff --git a/vendor/symfony/polyfill-intl-idn/Resources/unidata/disallowed.php b/vendor/symfony/polyfill-intl-idn/Resources/unidata/disallowed.php
new file mode 100755
index 0000000..25a5f56
--- /dev/null
+++ b/vendor/symfony/polyfill-intl-idn/Resources/unidata/disallowed.php
@@ -0,0 +1,2638 @@
+<?php
+
+return array (
+  888 => true,
+  889 => true,
+  896 => true,
+  897 => true,
+  898 => true,
+  899 => true,
+  907 => true,
+  909 => true,
+  930 => true,
+  1216 => true,
+  1328 => true,
+  1367 => true,
+  1368 => true,
+  1419 => true,
+  1420 => true,
+  1424 => true,
+  1480 => true,
+  1481 => true,
+  1482 => true,
+  1483 => true,
+  1484 => true,
+  1485 => true,
+  1486 => true,
+  1487 => true,
+  1515 => true,
+  1516 => true,
+  1517 => true,
+  1518 => true,
+  1525 => true,
+  1526 => true,
+  1527 => true,
+  1528 => true,
+  1529 => true,
+  1530 => true,
+  1531 => true,
+  1532 => true,
+  1533 => true,
+  1534 => true,
+  1535 => true,
+  1536 => true,
+  1537 => true,
+  1538 => true,
+  1539 => true,
+  1540 => true,
+  1541 => true,
+  1564 => true,
+  1565 => true,
+  1757 => true,
+  1806 => true,
+  1807 => true,
+  1867 => true,
+  1868 => true,
+  1970 => true,
+  1971 => true,
+  1972 => true,
+  1973 => true,
+  1974 => true,
+  1975 => true,
+  1976 => true,
+  1977 => true,
+  1978 => true,
+  1979 => true,
+  1980 => true,
+  1981 => true,
+  1982 => true,
+  1983 => true,
+  2043 => true,
+  2044 => true,
+  2094 => true,
+  2095 => true,
+  2111 => true,
+  2140 => true,
+  2141 => true,
+  2143 => true,
+  2229 => true,
+  2248 => true,
+  2249 => true,
+  2250 => true,
+  2251 => true,
+  2252 => true,
+  2253 => true,
+  2254 => true,
+  2255 => true,
+  2256 => true,
+  2257 => true,
+  2258 => true,
+  2274 => true,
+  2436 => true,
+  2445 => true,
+  2446 => true,
+  2449 => true,
+  2450 => true,
+  2473 => true,
+  2481 => true,
+  2483 => true,
+  2484 => true,
+  2485 => true,
+  2490 => true,
+  2491 => true,
+  2501 => true,
+  2502 => true,
+  2505 => true,
+  2506 => true,
+  2511 => true,
+  2512 => true,
+  2513 => true,
+  2514 => true,
+  2515 => true,
+  2516 => true,
+  2517 => true,
+  2518 => true,
+  2520 => true,
+  2521 => true,
+  2522 => true,
+  2523 => true,
+  2526 => true,
+  2532 => true,
+  2533 => true,
+  2559 => true,
+  2560 => true,
+  2564 => true,
+  2571 => true,
+  2572 => true,
+  2573 => true,
+  2574 => true,
+  2577 => true,
+  2578 => true,
+  2601 => true,
+  2609 => true,
+  2612 => true,
+  2615 => true,
+  2618 => true,
+  2619 => true,
+  2621 => true,
+  2627 => true,
+  2628 => true,
+  2629 => true,
+  2630 => true,
+  2633 => true,
+  2634 => true,
+  2638 => true,
+  2639 => true,
+  2640 => true,
+  2642 => true,
+  2643 => true,
+  2644 => true,
+  2645 => true,
+  2646 => true,
+  2647 => true,
+  2648 => true,
+  2653 => true,
+  2655 => true,
+  2656 => true,
+  2657 => true,
+  2658 => true,
+  2659 => true,
+  2660 => true,
+  2661 => true,
+  2679 => true,
+  2680 => true,
+  2681 => true,
+  2682 => true,
+  2683 => true,
+  2684 => true,
+  2685 => true,
+  2686 => true,
+  2687 => true,
+  2688 => true,
+  2692 => true,
+  2702 => true,
+  2706 => true,
+  2729 => true,
+  2737 => true,
+  2740 => true,
+  2746 => true,
+  2747 => true,
+  2758 => true,
+  2762 => true,
+  2766 => true,
+  2767 => true,
+  2769 => true,
+  2770 => true,
+  2771 => true,
+  2772 => true,
+  2773 => true,
+  2774 => true,
+  2775 => true,
+  2776 => true,
+  2777 => true,
+  2778 => true,
+  2779 => true,
+  2780 => true,
+  2781 => true,
+  2782 => true,
+  2783 => true,
+  2788 => true,
+  2789 => true,
+  2802 => true,
+  2803 => true,
+  2804 => true,
+  2805 => true,
+  2806 => true,
+  2807 => true,
+  2808 => true,
+  2816 => true,
+  2820 => true,
+  2829 => true,
+  2830 => true,
+  2833 => true,
+  2834 => true,
+  2857 => true,
+  2865 => true,
+  2868 => true,
+  2874 => true,
+  2875 => true,
+  2885 => true,
+  2886 => true,
+  2889 => true,
+  2890 => true,
+  2894 => true,
+  2895 => true,
+  2896 => true,
+  2897 => true,
+  2898 => true,
+  2899 => true,
+  2900 => true,
+  2904 => true,
+  2905 => true,
+  2906 => true,
+  2907 => true,
+  2910 => true,
+  2916 => true,
+  2917 => true,
+  2936 => true,
+  2937 => true,
+  2938 => true,
+  2939 => true,
+  2940 => true,
+  2941 => true,
+  2942 => true,
+  2943 => true,
+  2944 => true,
+  2945 => true,
+  2948 => true,
+  2955 => true,
+  2956 => true,
+  2957 => true,
+  2961 => true,
+  2966 => true,
+  2967 => true,
+  2968 => true,
+  2971 => true,
+  2973 => true,
+  2976 => true,
+  2977 => true,
+  2978 => true,
+  2981 => true,
+  2982 => true,
+  2983 => true,
+  2987 => true,
+  2988 => true,
+  2989 => true,
+  3002 => true,
+  3003 => true,
+  3004 => true,
+  3005 => true,
+  3011 => true,
+  3012 => true,
+  3013 => true,
+  3017 => true,
+  3022 => true,
+  3023 => true,
+  3025 => true,
+  3026 => true,
+  3027 => true,
+  3028 => true,
+  3029 => true,
+  3030 => true,
+  3032 => true,
+  3033 => true,
+  3034 => true,
+  3035 => true,
+  3036 => true,
+  3037 => true,
+  3038 => true,
+  3039 => true,
+  3040 => true,
+  3041 => true,
+  3042 => true,
+  3043 => true,
+  3044 => true,
+  3045 => true,
+  3067 => true,
+  3068 => true,
+  3069 => true,
+  3070 => true,
+  3071 => true,
+  3085 => true,
+  3089 => true,
+  3113 => true,
+  3130 => true,
+  3131 => true,
+  3132 => true,
+  3141 => true,
+  3145 => true,
+  3150 => true,
+  3151 => true,
+  3152 => true,
+  3153 => true,
+  3154 => true,
+  3155 => true,
+  3156 => true,
+  3159 => true,
+  3163 => true,
+  3164 => true,
+  3165 => true,
+  3166 => true,
+  3167 => true,
+  3172 => true,
+  3173 => true,
+  3184 => true,
+  3185 => true,
+  3186 => true,
+  3187 => true,
+  3188 => true,
+  3189 => true,
+  3190 => true,
+  3213 => true,
+  3217 => true,
+  3241 => true,
+  3252 => true,
+  3258 => true,
+  3259 => true,
+  3269 => true,
+  3273 => true,
+  3278 => true,
+  3279 => true,
+  3280 => true,
+  3281 => true,
+  3282 => true,
+  3283 => true,
+  3284 => true,
+  3287 => true,
+  3288 => true,
+  3289 => true,
+  3290 => true,
+  3291 => true,
+  3292 => true,
+  3293 => true,
+  3295 => true,
+  3300 => true,
+  3301 => true,
+  3312 => true,
+  3315 => true,
+  3316 => true,
+  3317 => true,
+  3318 => true,
+  3319 => true,
+  3320 => true,
+  3321 => true,
+  3322 => true,
+  3323 => true,
+  3324 => true,
+  3325 => true,
+  3326 => true,
+  3327 => true,
+  3341 => true,
+  3345 => true,
+  3397 => true,
+  3401 => true,
+  3408 => true,
+  3409 => true,
+  3410 => true,
+  3411 => true,
+  3428 => true,
+  3429 => true,
+  3456 => true,
+  3460 => true,
+  3479 => true,
+  3480 => true,
+  3481 => true,
+  3506 => true,
+  3516 => true,
+  3518 => true,
+  3519 => true,
+  3527 => true,
+  3528 => true,
+  3529 => true,
+  3531 => true,
+  3532 => true,
+  3533 => true,
+  3534 => true,
+  3541 => true,
+  3543 => true,
+  3552 => true,
+  3553 => true,
+  3554 => true,
+  3555 => true,
+  3556 => true,
+  3557 => true,
+  3568 => true,
+  3569 => true,
+  3573 => true,
+  3574 => true,
+  3575 => true,
+  3576 => true,
+  3577 => true,
+  3578 => true,
+  3579 => true,
+  3580 => true,
+  3581 => true,
+  3582 => true,
+  3583 => true,
+  3584 => true,
+  3643 => true,
+  3644 => true,
+  3645 => true,
+  3646 => true,
+  3715 => true,
+  3717 => true,
+  3723 => true,
+  3748 => true,
+  3750 => true,
+  3774 => true,
+  3775 => true,
+  3781 => true,
+  3783 => true,
+  3790 => true,
+  3791 => true,
+  3802 => true,
+  3803 => true,
+  3912 => true,
+  3949 => true,
+  3950 => true,
+  3951 => true,
+  3952 => true,
+  3992 => true,
+  4029 => true,
+  4045 => true,
+  4294 => true,
+  4296 => true,
+  4297 => true,
+  4298 => true,
+  4299 => true,
+  4300 => true,
+  4302 => true,
+  4303 => true,
+  4447 => true,
+  4448 => true,
+  4681 => true,
+  4686 => true,
+  4687 => true,
+  4695 => true,
+  4697 => true,
+  4702 => true,
+  4703 => true,
+  4745 => true,
+  4750 => true,
+  4751 => true,
+  4785 => true,
+  4790 => true,
+  4791 => true,
+  4799 => true,
+  4801 => true,
+  4806 => true,
+  4807 => true,
+  4823 => true,
+  4881 => true,
+  4886 => true,
+  4887 => true,
+  4955 => true,
+  4956 => true,
+  4989 => true,
+  4990 => true,
+  4991 => true,
+  5018 => true,
+  5019 => true,
+  5020 => true,
+  5021 => true,
+  5022 => true,
+  5023 => true,
+  5110 => true,
+  5111 => true,
+  5118 => true,
+  5119 => true,
+  5760 => true,
+  5789 => true,
+  5790 => true,
+  5791 => true,
+  5881 => true,
+  5882 => true,
+  5883 => true,
+  5884 => true,
+  5885 => true,
+  5886 => true,
+  5887 => true,
+  5901 => true,
+  5909 => true,
+  5910 => true,
+  5911 => true,
+  5912 => true,
+  5913 => true,
+  5914 => true,
+  5915 => true,
+  5916 => true,
+  5917 => true,
+  5918 => true,
+  5919 => true,
+  5943 => true,
+  5944 => true,
+  5945 => true,
+  5946 => true,
+  5947 => true,
+  5948 => true,
+  5949 => true,
+  5950 => true,
+  5951 => true,
+  5972 => true,
+  5973 => true,
+  5974 => true,
+  5975 => true,
+  5976 => true,
+  5977 => true,
+  5978 => true,
+  5979 => true,
+  5980 => true,
+  5981 => true,
+  5982 => true,
+  5983 => true,
+  5997 => true,
+  6001 => true,
+  6004 => true,
+  6005 => true,
+  6006 => true,
+  6007 => true,
+  6008 => true,
+  6009 => true,
+  6010 => true,
+  6011 => true,
+  6012 => true,
+  6013 => true,
+  6014 => true,
+  6015 => true,
+  6068 => true,
+  6069 => true,
+  6110 => true,
+  6111 => true,
+  6122 => true,
+  6123 => true,
+  6124 => true,
+  6125 => true,
+  6126 => true,
+  6127 => true,
+  6138 => true,
+  6139 => true,
+  6140 => true,
+  6141 => true,
+  6142 => true,
+  6143 => true,
+  6150 => true,
+  6158 => true,
+  6159 => true,
+  6170 => true,
+  6171 => true,
+  6172 => true,
+  6173 => true,
+  6174 => true,
+  6175 => true,
+  6265 => true,
+  6266 => true,
+  6267 => true,
+  6268 => true,
+  6269 => true,
+  6270 => true,
+  6271 => true,
+  6315 => true,
+  6316 => true,
+  6317 => true,
+  6318 => true,
+  6319 => true,
+  6390 => true,
+  6391 => true,
+  6392 => true,
+  6393 => true,
+  6394 => true,
+  6395 => true,
+  6396 => true,
+  6397 => true,
+  6398 => true,
+  6399 => true,
+  6431 => true,
+  6444 => true,
+  6445 => true,
+  6446 => true,
+  6447 => true,
+  6460 => true,
+  6461 => true,
+  6462 => true,
+  6463 => true,
+  6465 => true,
+  6466 => true,
+  6467 => true,
+  6510 => true,
+  6511 => true,
+  6517 => true,
+  6518 => true,
+  6519 => true,
+  6520 => true,
+  6521 => true,
+  6522 => true,
+  6523 => true,
+  6524 => true,
+  6525 => true,
+  6526 => true,
+  6527 => true,
+  6572 => true,
+  6573 => true,
+  6574 => true,
+  6575 => true,
+  6602 => true,
+  6603 => true,
+  6604 => true,
+  6605 => true,
+  6606 => true,
+  6607 => true,
+  6619 => true,
+  6620 => true,
+  6621 => true,
+  6684 => true,
+  6685 => true,
+  6751 => true,
+  6781 => true,
+  6782 => true,
+  6794 => true,
+  6795 => true,
+  6796 => true,
+  6797 => true,
+  6798 => true,
+  6799 => true,
+  6810 => true,
+  6811 => true,
+  6812 => true,
+  6813 => true,
+  6814 => true,
+  6815 => true,
+  6830 => true,
+  6831 => true,
+  6988 => true,
+  6989 => true,
+  6990 => true,
+  6991 => true,
+  7037 => true,
+  7038 => true,
+  7039 => true,
+  7156 => true,
+  7157 => true,
+  7158 => true,
+  7159 => true,
+  7160 => true,
+  7161 => true,
+  7162 => true,
+  7163 => true,
+  7224 => true,
+  7225 => true,
+  7226 => true,
+  7242 => true,
+  7243 => true,
+  7244 => true,
+  7305 => true,
+  7306 => true,
+  7307 => true,
+  7308 => true,
+  7309 => true,
+  7310 => true,
+  7311 => true,
+  7355 => true,
+  7356 => true,
+  7368 => true,
+  7369 => true,
+  7370 => true,
+  7371 => true,
+  7372 => true,
+  7373 => true,
+  7374 => true,
+  7375 => true,
+  7419 => true,
+  7420 => true,
+  7421 => true,
+  7422 => true,
+  7423 => true,
+  7674 => true,
+  7958 => true,
+  7959 => true,
+  7966 => true,
+  7967 => true,
+  8006 => true,
+  8007 => true,
+  8014 => true,
+  8015 => true,
+  8024 => true,
+  8026 => true,
+  8028 => true,
+  8030 => true,
+  8062 => true,
+  8063 => true,
+  8117 => true,
+  8133 => true,
+  8148 => true,
+  8149 => true,
+  8156 => true,
+  8176 => true,
+  8177 => true,
+  8181 => true,
+  8191 => true,
+  8206 => true,
+  8207 => true,
+  8228 => true,
+  8229 => true,
+  8230 => true,
+  8232 => true,
+  8233 => true,
+  8234 => true,
+  8235 => true,
+  8236 => true,
+  8237 => true,
+  8238 => true,
+  8289 => true,
+  8290 => true,
+  8291 => true,
+  8293 => true,
+  8294 => true,
+  8295 => true,
+  8296 => true,
+  8297 => true,
+  8298 => true,
+  8299 => true,
+  8300 => true,
+  8301 => true,
+  8302 => true,
+  8303 => true,
+  8306 => true,
+  8307 => true,
+  8335 => true,
+  8349 => true,
+  8350 => true,
+  8351 => true,
+  8384 => true,
+  8385 => true,
+  8386 => true,
+  8387 => true,
+  8388 => true,
+  8389 => true,
+  8390 => true,
+  8391 => true,
+  8392 => true,
+  8393 => true,
+  8394 => true,
+  8395 => true,
+  8396 => true,
+  8397 => true,
+  8398 => true,
+  8399 => true,
+  8433 => true,
+  8434 => true,
+  8435 => true,
+  8436 => true,
+  8437 => true,
+  8438 => true,
+  8439 => true,
+  8440 => true,
+  8441 => true,
+  8442 => true,
+  8443 => true,
+  8444 => true,
+  8445 => true,
+  8446 => true,
+  8447 => true,
+  8498 => true,
+  8579 => true,
+  8588 => true,
+  8589 => true,
+  8590 => true,
+  8591 => true,
+  9255 => true,
+  9256 => true,
+  9257 => true,
+  9258 => true,
+  9259 => true,
+  9260 => true,
+  9261 => true,
+  9262 => true,
+  9263 => true,
+  9264 => true,
+  9265 => true,
+  9266 => true,
+  9267 => true,
+  9268 => true,
+  9269 => true,
+  9270 => true,
+  9271 => true,
+  9272 => true,
+  9273 => true,
+  9274 => true,
+  9275 => true,
+  9276 => true,
+  9277 => true,
+  9278 => true,
+  9279 => true,
+  9291 => true,
+  9292 => true,
+  9293 => true,
+  9294 => true,
+  9295 => true,
+  9296 => true,
+  9297 => true,
+  9298 => true,
+  9299 => true,
+  9300 => true,
+  9301 => true,
+  9302 => true,
+  9303 => true,
+  9304 => true,
+  9305 => true,
+  9306 => true,
+  9307 => true,
+  9308 => true,
+  9309 => true,
+  9310 => true,
+  9311 => true,
+  9352 => true,
+  9353 => true,
+  9354 => true,
+  9355 => true,
+  9356 => true,
+  9357 => true,
+  9358 => true,
+  9359 => true,
+  9360 => true,
+  9361 => true,
+  9362 => true,
+  9363 => true,
+  9364 => true,
+  9365 => true,
+  9366 => true,
+  9367 => true,
+  9368 => true,
+  9369 => true,
+  9370 => true,
+  9371 => true,
+  11124 => true,
+  11125 => true,
+  11158 => true,
+  11311 => true,
+  11359 => true,
+  11508 => true,
+  11509 => true,
+  11510 => true,
+  11511 => true,
+  11512 => true,
+  11558 => true,
+  11560 => true,
+  11561 => true,
+  11562 => true,
+  11563 => true,
+  11564 => true,
+  11566 => true,
+  11567 => true,
+  11624 => true,
+  11625 => true,
+  11626 => true,
+  11627 => true,
+  11628 => true,
+  11629 => true,
+  11630 => true,
+  11633 => true,
+  11634 => true,
+  11635 => true,
+  11636 => true,
+  11637 => true,
+  11638 => true,
+  11639 => true,
+  11640 => true,
+  11641 => true,
+  11642 => true,
+  11643 => true,
+  11644 => true,
+  11645 => true,
+  11646 => true,
+  11671 => true,
+  11672 => true,
+  11673 => true,
+  11674 => true,
+  11675 => true,
+  11676 => true,
+  11677 => true,
+  11678 => true,
+  11679 => true,
+  11687 => true,
+  11695 => true,
+  11703 => true,
+  11711 => true,
+  11719 => true,
+  11727 => true,
+  11735 => true,
+  11743 => true,
+  11930 => true,
+  12020 => true,
+  12021 => true,
+  12022 => true,
+  12023 => true,
+  12024 => true,
+  12025 => true,
+  12026 => true,
+  12027 => true,
+  12028 => true,
+  12029 => true,
+  12030 => true,
+  12031 => true,
+  12246 => true,
+  12247 => true,
+  12248 => true,
+  12249 => true,
+  12250 => true,
+  12251 => true,
+  12252 => true,
+  12253 => true,
+  12254 => true,
+  12255 => true,
+  12256 => true,
+  12257 => true,
+  12258 => true,
+  12259 => true,
+  12260 => true,
+  12261 => true,
+  12262 => true,
+  12263 => true,
+  12264 => true,
+  12265 => true,
+  12266 => true,
+  12267 => true,
+  12268 => true,
+  12269 => true,
+  12270 => true,
+  12271 => true,
+  12272 => true,
+  12273 => true,
+  12274 => true,
+  12275 => true,
+  12276 => true,
+  12277 => true,
+  12278 => true,
+  12279 => true,
+  12280 => true,
+  12281 => true,
+  12282 => true,
+  12283 => true,
+  12284 => true,
+  12285 => true,
+  12286 => true,
+  12287 => true,
+  12352 => true,
+  12439 => true,
+  12440 => true,
+  12544 => true,
+  12545 => true,
+  12546 => true,
+  12547 => true,
+  12548 => true,
+  12592 => true,
+  12644 => true,
+  12687 => true,
+  12772 => true,
+  12773 => true,
+  12774 => true,
+  12775 => true,
+  12776 => true,
+  12777 => true,
+  12778 => true,
+  12779 => true,
+  12780 => true,
+  12781 => true,
+  12782 => true,
+  12783 => true,
+  12831 => true,
+  13250 => true,
+  13255 => true,
+  13272 => true,
+  40957 => true,
+  40958 => true,
+  40959 => true,
+  42125 => true,
+  42126 => true,
+  42127 => true,
+  42183 => true,
+  42184 => true,
+  42185 => true,
+  42186 => true,
+  42187 => true,
+  42188 => true,
+  42189 => true,
+  42190 => true,
+  42191 => true,
+  42540 => true,
+  42541 => true,
+  42542 => true,
+  42543 => true,
+  42544 => true,
+  42545 => true,
+  42546 => true,
+  42547 => true,
+  42548 => true,
+  42549 => true,
+  42550 => true,
+  42551 => true,
+  42552 => true,
+  42553 => true,
+  42554 => true,
+  42555 => true,
+  42556 => true,
+  42557 => true,
+  42558 => true,
+  42559 => true,
+  42744 => true,
+  42745 => true,
+  42746 => true,
+  42747 => true,
+  42748 => true,
+  42749 => true,
+  42750 => true,
+  42751 => true,
+  42944 => true,
+  42945 => true,
+  43053 => true,
+  43054 => true,
+  43055 => true,
+  43066 => true,
+  43067 => true,
+  43068 => true,
+  43069 => true,
+  43070 => true,
+  43071 => true,
+  43128 => true,
+  43129 => true,
+  43130 => true,
+  43131 => true,
+  43132 => true,
+  43133 => true,
+  43134 => true,
+  43135 => true,
+  43206 => true,
+  43207 => true,
+  43208 => true,
+  43209 => true,
+  43210 => true,
+  43211 => true,
+  43212 => true,
+  43213 => true,
+  43226 => true,
+  43227 => true,
+  43228 => true,
+  43229 => true,
+  43230 => true,
+  43231 => true,
+  43348 => true,
+  43349 => true,
+  43350 => true,
+  43351 => true,
+  43352 => true,
+  43353 => true,
+  43354 => true,
+  43355 => true,
+  43356 => true,
+  43357 => true,
+  43358 => true,
+  43389 => true,
+  43390 => true,
+  43391 => true,
+  43470 => true,
+  43482 => true,
+  43483 => true,
+  43484 => true,
+  43485 => true,
+  43519 => true,
+  43575 => true,
+  43576 => true,
+  43577 => true,
+  43578 => true,
+  43579 => true,
+  43580 => true,
+  43581 => true,
+  43582 => true,
+  43583 => true,
+  43598 => true,
+  43599 => true,
+  43610 => true,
+  43611 => true,
+  43715 => true,
+  43716 => true,
+  43717 => true,
+  43718 => true,
+  43719 => true,
+  43720 => true,
+  43721 => true,
+  43722 => true,
+  43723 => true,
+  43724 => true,
+  43725 => true,
+  43726 => true,
+  43727 => true,
+  43728 => true,
+  43729 => true,
+  43730 => true,
+  43731 => true,
+  43732 => true,
+  43733 => true,
+  43734 => true,
+  43735 => true,
+  43736 => true,
+  43737 => true,
+  43738 => true,
+  43767 => true,
+  43768 => true,
+  43769 => true,
+  43770 => true,
+  43771 => true,
+  43772 => true,
+  43773 => true,
+  43774 => true,
+  43775 => true,
+  43776 => true,
+  43783 => true,
+  43784 => true,
+  43791 => true,
+  43792 => true,
+  43799 => true,
+  43800 => true,
+  43801 => true,
+  43802 => true,
+  43803 => true,
+  43804 => true,
+  43805 => true,
+  43806 => true,
+  43807 => true,
+  43815 => true,
+  43823 => true,
+  43884 => true,
+  43885 => true,
+  43886 => true,
+  43887 => true,
+  44014 => true,
+  44015 => true,
+  44026 => true,
+  44027 => true,
+  44028 => true,
+  44029 => true,
+  44030 => true,
+  44031 => true,
+  55204 => true,
+  55205 => true,
+  55206 => true,
+  55207 => true,
+  55208 => true,
+  55209 => true,
+  55210 => true,
+  55211 => true,
+  55212 => true,
+  55213 => true,
+  55214 => true,
+  55215 => true,
+  55239 => true,
+  55240 => true,
+  55241 => true,
+  55242 => true,
+  55292 => true,
+  55293 => true,
+  55294 => true,
+  55295 => true,
+  64110 => true,
+  64111 => true,
+  64263 => true,
+  64264 => true,
+  64265 => true,
+  64266 => true,
+  64267 => true,
+  64268 => true,
+  64269 => true,
+  64270 => true,
+  64271 => true,
+  64272 => true,
+  64273 => true,
+  64274 => true,
+  64280 => true,
+  64281 => true,
+  64282 => true,
+  64283 => true,
+  64284 => true,
+  64311 => true,
+  64317 => true,
+  64319 => true,
+  64322 => true,
+  64325 => true,
+  64450 => true,
+  64451 => true,
+  64452 => true,
+  64453 => true,
+  64454 => true,
+  64455 => true,
+  64456 => true,
+  64457 => true,
+  64458 => true,
+  64459 => true,
+  64460 => true,
+  64461 => true,
+  64462 => true,
+  64463 => true,
+  64464 => true,
+  64465 => true,
+  64466 => true,
+  64832 => true,
+  64833 => true,
+  64834 => true,
+  64835 => true,
+  64836 => true,
+  64837 => true,
+  64838 => true,
+  64839 => true,
+  64840 => true,
+  64841 => true,
+  64842 => true,
+  64843 => true,
+  64844 => true,
+  64845 => true,
+  64846 => true,
+  64847 => true,
+  64912 => true,
+  64913 => true,
+  64968 => true,
+  64969 => true,
+  64970 => true,
+  64971 => true,
+  64972 => true,
+  64973 => true,
+  64974 => true,
+  64975 => true,
+  65022 => true,
+  65023 => true,
+  65042 => true,
+  65049 => true,
+  65050 => true,
+  65051 => true,
+  65052 => true,
+  65053 => true,
+  65054 => true,
+  65055 => true,
+  65072 => true,
+  65106 => true,
+  65107 => true,
+  65127 => true,
+  65132 => true,
+  65133 => true,
+  65134 => true,
+  65135 => true,
+  65141 => true,
+  65277 => true,
+  65278 => true,
+  65280 => true,
+  65440 => true,
+  65471 => true,
+  65472 => true,
+  65473 => true,
+  65480 => true,
+  65481 => true,
+  65488 => true,
+  65489 => true,
+  65496 => true,
+  65497 => true,
+  65501 => true,
+  65502 => true,
+  65503 => true,
+  65511 => true,
+  65519 => true,
+  65520 => true,
+  65521 => true,
+  65522 => true,
+  65523 => true,
+  65524 => true,
+  65525 => true,
+  65526 => true,
+  65527 => true,
+  65528 => true,
+  65529 => true,
+  65530 => true,
+  65531 => true,
+  65532 => true,
+  65533 => true,
+  65534 => true,
+  65535 => true,
+  65548 => true,
+  65575 => true,
+  65595 => true,
+  65598 => true,
+  65614 => true,
+  65615 => true,
+  65787 => true,
+  65788 => true,
+  65789 => true,
+  65790 => true,
+  65791 => true,
+  65795 => true,
+  65796 => true,
+  65797 => true,
+  65798 => true,
+  65844 => true,
+  65845 => true,
+  65846 => true,
+  65935 => true,
+  65949 => true,
+  65950 => true,
+  65951 => true,
+  66205 => true,
+  66206 => true,
+  66207 => true,
+  66257 => true,
+  66258 => true,
+  66259 => true,
+  66260 => true,
+  66261 => true,
+  66262 => true,
+  66263 => true,
+  66264 => true,
+  66265 => true,
+  66266 => true,
+  66267 => true,
+  66268 => true,
+  66269 => true,
+  66270 => true,
+  66271 => true,
+  66300 => true,
+  66301 => true,
+  66302 => true,
+  66303 => true,
+  66340 => true,
+  66341 => true,
+  66342 => true,
+  66343 => true,
+  66344 => true,
+  66345 => true,
+  66346 => true,
+  66347 => true,
+  66348 => true,
+  66379 => true,
+  66380 => true,
+  66381 => true,
+  66382 => true,
+  66383 => true,
+  66427 => true,
+  66428 => true,
+  66429 => true,
+  66430 => true,
+  66431 => true,
+  66462 => true,
+  66500 => true,
+  66501 => true,
+  66502 => true,
+  66503 => true,
+  66718 => true,
+  66719 => true,
+  66730 => true,
+  66731 => true,
+  66732 => true,
+  66733 => true,
+  66734 => true,
+  66735 => true,
+  66772 => true,
+  66773 => true,
+  66774 => true,
+  66775 => true,
+  66812 => true,
+  66813 => true,
+  66814 => true,
+  66815 => true,
+  66856 => true,
+  66857 => true,
+  66858 => true,
+  66859 => true,
+  66860 => true,
+  66861 => true,
+  66862 => true,
+  66863 => true,
+  66916 => true,
+  66917 => true,
+  66918 => true,
+  66919 => true,
+  66920 => true,
+  66921 => true,
+  66922 => true,
+  66923 => true,
+  66924 => true,
+  66925 => true,
+  66926 => true,
+  67383 => true,
+  67384 => true,
+  67385 => true,
+  67386 => true,
+  67387 => true,
+  67388 => true,
+  67389 => true,
+  67390 => true,
+  67391 => true,
+  67414 => true,
+  67415 => true,
+  67416 => true,
+  67417 => true,
+  67418 => true,
+  67419 => true,
+  67420 => true,
+  67421 => true,
+  67422 => true,
+  67423 => true,
+  67590 => true,
+  67591 => true,
+  67593 => true,
+  67638 => true,
+  67641 => true,
+  67642 => true,
+  67643 => true,
+  67645 => true,
+  67646 => true,
+  67670 => true,
+  67743 => true,
+  67744 => true,
+  67745 => true,
+  67746 => true,
+  67747 => true,
+  67748 => true,
+  67749 => true,
+  67750 => true,
+  67827 => true,
+  67830 => true,
+  67831 => true,
+  67832 => true,
+  67833 => true,
+  67834 => true,
+  67868 => true,
+  67869 => true,
+  67870 => true,
+  67898 => true,
+  67899 => true,
+  67900 => true,
+  67901 => true,
+  67902 => true,
+  68024 => true,
+  68025 => true,
+  68026 => true,
+  68027 => true,
+  68048 => true,
+  68049 => true,
+  68100 => true,
+  68103 => true,
+  68104 => true,
+  68105 => true,
+  68106 => true,
+  68107 => true,
+  68116 => true,
+  68120 => true,
+  68150 => true,
+  68151 => true,
+  68155 => true,
+  68156 => true,
+  68157 => true,
+  68158 => true,
+  68169 => true,
+  68170 => true,
+  68171 => true,
+  68172 => true,
+  68173 => true,
+  68174 => true,
+  68175 => true,
+  68185 => true,
+  68186 => true,
+  68187 => true,
+  68188 => true,
+  68189 => true,
+  68190 => true,
+  68191 => true,
+  68327 => true,
+  68328 => true,
+  68329 => true,
+  68330 => true,
+  68343 => true,
+  68344 => true,
+  68345 => true,
+  68346 => true,
+  68347 => true,
+  68348 => true,
+  68349 => true,
+  68350 => true,
+  68351 => true,
+  68406 => true,
+  68407 => true,
+  68408 => true,
+  68438 => true,
+  68439 => true,
+  68467 => true,
+  68468 => true,
+  68469 => true,
+  68470 => true,
+  68471 => true,
+  68498 => true,
+  68499 => true,
+  68500 => true,
+  68501 => true,
+  68502 => true,
+  68503 => true,
+  68504 => true,
+  68509 => true,
+  68510 => true,
+  68511 => true,
+  68512 => true,
+  68513 => true,
+  68514 => true,
+  68515 => true,
+  68516 => true,
+  68517 => true,
+  68518 => true,
+  68519 => true,
+  68520 => true,
+  68787 => true,
+  68788 => true,
+  68789 => true,
+  68790 => true,
+  68791 => true,
+  68792 => true,
+  68793 => true,
+  68794 => true,
+  68795 => true,
+  68796 => true,
+  68797 => true,
+  68798 => true,
+  68799 => true,
+  68851 => true,
+  68852 => true,
+  68853 => true,
+  68854 => true,
+  68855 => true,
+  68856 => true,
+  68857 => true,
+  68904 => true,
+  68905 => true,
+  68906 => true,
+  68907 => true,
+  68908 => true,
+  68909 => true,
+  68910 => true,
+  68911 => true,
+  69247 => true,
+  69290 => true,
+  69294 => true,
+  69295 => true,
+  69416 => true,
+  69417 => true,
+  69418 => true,
+  69419 => true,
+  69420 => true,
+  69421 => true,
+  69422 => true,
+  69423 => true,
+  69580 => true,
+  69581 => true,
+  69582 => true,
+  69583 => true,
+  69584 => true,
+  69585 => true,
+  69586 => true,
+  69587 => true,
+  69588 => true,
+  69589 => true,
+  69590 => true,
+  69591 => true,
+  69592 => true,
+  69593 => true,
+  69594 => true,
+  69595 => true,
+  69596 => true,
+  69597 => true,
+  69598 => true,
+  69599 => true,
+  69623 => true,
+  69624 => true,
+  69625 => true,
+  69626 => true,
+  69627 => true,
+  69628 => true,
+  69629 => true,
+  69630 => true,
+  69631 => true,
+  69710 => true,
+  69711 => true,
+  69712 => true,
+  69713 => true,
+  69744 => true,
+  69745 => true,
+  69746 => true,
+  69747 => true,
+  69748 => true,
+  69749 => true,
+  69750 => true,
+  69751 => true,
+  69752 => true,
+  69753 => true,
+  69754 => true,
+  69755 => true,
+  69756 => true,
+  69757 => true,
+  69758 => true,
+  69821 => true,
+  69826 => true,
+  69827 => true,
+  69828 => true,
+  69829 => true,
+  69830 => true,
+  69831 => true,
+  69832 => true,
+  69833 => true,
+  69834 => true,
+  69835 => true,
+  69836 => true,
+  69837 => true,
+  69838 => true,
+  69839 => true,
+  69865 => true,
+  69866 => true,
+  69867 => true,
+  69868 => true,
+  69869 => true,
+  69870 => true,
+  69871 => true,
+  69882 => true,
+  69883 => true,
+  69884 => true,
+  69885 => true,
+  69886 => true,
+  69887 => true,
+  69941 => true,
+  69960 => true,
+  69961 => true,
+  69962 => true,
+  69963 => true,
+  69964 => true,
+  69965 => true,
+  69966 => true,
+  69967 => true,
+  70007 => true,
+  70008 => true,
+  70009 => true,
+  70010 => true,
+  70011 => true,
+  70012 => true,
+  70013 => true,
+  70014 => true,
+  70015 => true,
+  70112 => true,
+  70133 => true,
+  70134 => true,
+  70135 => true,
+  70136 => true,
+  70137 => true,
+  70138 => true,
+  70139 => true,
+  70140 => true,
+  70141 => true,
+  70142 => true,
+  70143 => true,
+  70162 => true,
+  70279 => true,
+  70281 => true,
+  70286 => true,
+  70302 => true,
+  70314 => true,
+  70315 => true,
+  70316 => true,
+  70317 => true,
+  70318 => true,
+  70319 => true,
+  70379 => true,
+  70380 => true,
+  70381 => true,
+  70382 => true,
+  70383 => true,
+  70394 => true,
+  70395 => true,
+  70396 => true,
+  70397 => true,
+  70398 => true,
+  70399 => true,
+  70404 => true,
+  70413 => true,
+  70414 => true,
+  70417 => true,
+  70418 => true,
+  70441 => true,
+  70449 => true,
+  70452 => true,
+  70458 => true,
+  70469 => true,
+  70470 => true,
+  70473 => true,
+  70474 => true,
+  70478 => true,
+  70479 => true,
+  70481 => true,
+  70482 => true,
+  70483 => true,
+  70484 => true,
+  70485 => true,
+  70486 => true,
+  70488 => true,
+  70489 => true,
+  70490 => true,
+  70491 => true,
+  70492 => true,
+  70500 => true,
+  70501 => true,
+  70509 => true,
+  70510 => true,
+  70511 => true,
+  70748 => true,
+  70754 => true,
+  70755 => true,
+  70756 => true,
+  70757 => true,
+  70758 => true,
+  70759 => true,
+  70760 => true,
+  70761 => true,
+  70762 => true,
+  70763 => true,
+  70764 => true,
+  70765 => true,
+  70766 => true,
+  70767 => true,
+  70768 => true,
+  70769 => true,
+  70770 => true,
+  70771 => true,
+  70772 => true,
+  70773 => true,
+  70774 => true,
+  70775 => true,
+  70776 => true,
+  70777 => true,
+  70778 => true,
+  70779 => true,
+  70780 => true,
+  70781 => true,
+  70782 => true,
+  70783 => true,
+  70856 => true,
+  70857 => true,
+  70858 => true,
+  70859 => true,
+  70860 => true,
+  70861 => true,
+  70862 => true,
+  70863 => true,
+  71094 => true,
+  71095 => true,
+  71237 => true,
+  71238 => true,
+  71239 => true,
+  71240 => true,
+  71241 => true,
+  71242 => true,
+  71243 => true,
+  71244 => true,
+  71245 => true,
+  71246 => true,
+  71247 => true,
+  71258 => true,
+  71259 => true,
+  71260 => true,
+  71261 => true,
+  71262 => true,
+  71263 => true,
+  71277 => true,
+  71278 => true,
+  71279 => true,
+  71280 => true,
+  71281 => true,
+  71282 => true,
+  71283 => true,
+  71284 => true,
+  71285 => true,
+  71286 => true,
+  71287 => true,
+  71288 => true,
+  71289 => true,
+  71290 => true,
+  71291 => true,
+  71292 => true,
+  71293 => true,
+  71294 => true,
+  71295 => true,
+  71353 => true,
+  71354 => true,
+  71355 => true,
+  71356 => true,
+  71357 => true,
+  71358 => true,
+  71359 => true,
+  71451 => true,
+  71452 => true,
+  71468 => true,
+  71469 => true,
+  71470 => true,
+  71471 => true,
+  71923 => true,
+  71924 => true,
+  71925 => true,
+  71926 => true,
+  71927 => true,
+  71928 => true,
+  71929 => true,
+  71930 => true,
+  71931 => true,
+  71932 => true,
+  71933 => true,
+  71934 => true,
+  71943 => true,
+  71944 => true,
+  71946 => true,
+  71947 => true,
+  71956 => true,
+  71959 => true,
+  71990 => true,
+  71993 => true,
+  71994 => true,
+  72007 => true,
+  72008 => true,
+  72009 => true,
+  72010 => true,
+  72011 => true,
+  72012 => true,
+  72013 => true,
+  72014 => true,
+  72015 => true,
+  72104 => true,
+  72105 => true,
+  72152 => true,
+  72153 => true,
+  72165 => true,
+  72166 => true,
+  72167 => true,
+  72168 => true,
+  72169 => true,
+  72170 => true,
+  72171 => true,
+  72172 => true,
+  72173 => true,
+  72174 => true,
+  72175 => true,
+  72176 => true,
+  72177 => true,
+  72178 => true,
+  72179 => true,
+  72180 => true,
+  72181 => true,
+  72182 => true,
+  72183 => true,
+  72184 => true,
+  72185 => true,
+  72186 => true,
+  72187 => true,
+  72188 => true,
+  72189 => true,
+  72190 => true,
+  72191 => true,
+  72264 => true,
+  72265 => true,
+  72266 => true,
+  72267 => true,
+  72268 => true,
+  72269 => true,
+  72270 => true,
+  72271 => true,
+  72355 => true,
+  72356 => true,
+  72357 => true,
+  72358 => true,
+  72359 => true,
+  72360 => true,
+  72361 => true,
+  72362 => true,
+  72363 => true,
+  72364 => true,
+  72365 => true,
+  72366 => true,
+  72367 => true,
+  72368 => true,
+  72369 => true,
+  72370 => true,
+  72371 => true,
+  72372 => true,
+  72373 => true,
+  72374 => true,
+  72375 => true,
+  72376 => true,
+  72377 => true,
+  72378 => true,
+  72379 => true,
+  72380 => true,
+  72381 => true,
+  72382 => true,
+  72383 => true,
+  72713 => true,
+  72759 => true,
+  72774 => true,
+  72775 => true,
+  72776 => true,
+  72777 => true,
+  72778 => true,
+  72779 => true,
+  72780 => true,
+  72781 => true,
+  72782 => true,
+  72783 => true,
+  72813 => true,
+  72814 => true,
+  72815 => true,
+  72848 => true,
+  72849 => true,
+  72872 => true,
+  72967 => true,
+  72970 => true,
+  73015 => true,
+  73016 => true,
+  73017 => true,
+  73019 => true,
+  73022 => true,
+  73032 => true,
+  73033 => true,
+  73034 => true,
+  73035 => true,
+  73036 => true,
+  73037 => true,
+  73038 => true,
+  73039 => true,
+  73050 => true,
+  73051 => true,
+  73052 => true,
+  73053 => true,
+  73054 => true,
+  73055 => true,
+  73062 => true,
+  73065 => true,
+  73103 => true,
+  73106 => true,
+  73113 => true,
+  73114 => true,
+  73115 => true,
+  73116 => true,
+  73117 => true,
+  73118 => true,
+  73119 => true,
+  73649 => true,
+  73650 => true,
+  73651 => true,
+  73652 => true,
+  73653 => true,
+  73654 => true,
+  73655 => true,
+  73656 => true,
+  73657 => true,
+  73658 => true,
+  73659 => true,
+  73660 => true,
+  73661 => true,
+  73662 => true,
+  73663 => true,
+  73714 => true,
+  73715 => true,
+  73716 => true,
+  73717 => true,
+  73718 => true,
+  73719 => true,
+  73720 => true,
+  73721 => true,
+  73722 => true,
+  73723 => true,
+  73724 => true,
+  73725 => true,
+  73726 => true,
+  74863 => true,
+  74869 => true,
+  74870 => true,
+  74871 => true,
+  74872 => true,
+  74873 => true,
+  74874 => true,
+  74875 => true,
+  74876 => true,
+  74877 => true,
+  74878 => true,
+  74879 => true,
+  78895 => true,
+  78896 => true,
+  78897 => true,
+  78898 => true,
+  78899 => true,
+  78900 => true,
+  78901 => true,
+  78902 => true,
+  78903 => true,
+  78904 => true,
+  92729 => true,
+  92730 => true,
+  92731 => true,
+  92732 => true,
+  92733 => true,
+  92734 => true,
+  92735 => true,
+  92767 => true,
+  92778 => true,
+  92779 => true,
+  92780 => true,
+  92781 => true,
+  92910 => true,
+  92911 => true,
+  92918 => true,
+  92919 => true,
+  92920 => true,
+  92921 => true,
+  92922 => true,
+  92923 => true,
+  92924 => true,
+  92925 => true,
+  92926 => true,
+  92927 => true,
+  92998 => true,
+  92999 => true,
+  93000 => true,
+  93001 => true,
+  93002 => true,
+  93003 => true,
+  93004 => true,
+  93005 => true,
+  93006 => true,
+  93007 => true,
+  93018 => true,
+  93026 => true,
+  93048 => true,
+  93049 => true,
+  93050 => true,
+  93051 => true,
+  93052 => true,
+  94027 => true,
+  94028 => true,
+  94029 => true,
+  94030 => true,
+  94088 => true,
+  94089 => true,
+  94090 => true,
+  94091 => true,
+  94092 => true,
+  94093 => true,
+  94094 => true,
+  94181 => true,
+  94182 => true,
+  94183 => true,
+  94184 => true,
+  94185 => true,
+  94186 => true,
+  94187 => true,
+  94188 => true,
+  94189 => true,
+  94190 => true,
+  94191 => true,
+  94194 => true,
+  94195 => true,
+  94196 => true,
+  94197 => true,
+  94198 => true,
+  94199 => true,
+  94200 => true,
+  94201 => true,
+  94202 => true,
+  94203 => true,
+  94204 => true,
+  94205 => true,
+  94206 => true,
+  94207 => true,
+  100344 => true,
+  100345 => true,
+  100346 => true,
+  100347 => true,
+  100348 => true,
+  100349 => true,
+  100350 => true,
+  100351 => true,
+  110931 => true,
+  110932 => true,
+  110933 => true,
+  110934 => true,
+  110935 => true,
+  110936 => true,
+  110937 => true,
+  110938 => true,
+  110939 => true,
+  110940 => true,
+  110941 => true,
+  110942 => true,
+  110943 => true,
+  110944 => true,
+  110945 => true,
+  110946 => true,
+  110947 => true,
+  110952 => true,
+  110953 => true,
+  110954 => true,
+  110955 => true,
+  110956 => true,
+  110957 => true,
+  110958 => true,
+  110959 => true,
+  113771 => true,
+  113772 => true,
+  113773 => true,
+  113774 => true,
+  113775 => true,
+  113789 => true,
+  113790 => true,
+  113791 => true,
+  113801 => true,
+  113802 => true,
+  113803 => true,
+  113804 => true,
+  113805 => true,
+  113806 => true,
+  113807 => true,
+  113818 => true,
+  113819 => true,
+  119030 => true,
+  119031 => true,
+  119032 => true,
+  119033 => true,
+  119034 => true,
+  119035 => true,
+  119036 => true,
+  119037 => true,
+  119038 => true,
+  119039 => true,
+  119079 => true,
+  119080 => true,
+  119155 => true,
+  119156 => true,
+  119157 => true,
+  119158 => true,
+  119159 => true,
+  119160 => true,
+  119161 => true,
+  119162 => true,
+  119273 => true,
+  119274 => true,
+  119275 => true,
+  119276 => true,
+  119277 => true,
+  119278 => true,
+  119279 => true,
+  119280 => true,
+  119281 => true,
+  119282 => true,
+  119283 => true,
+  119284 => true,
+  119285 => true,
+  119286 => true,
+  119287 => true,
+  119288 => true,
+  119289 => true,
+  119290 => true,
+  119291 => true,
+  119292 => true,
+  119293 => true,
+  119294 => true,
+  119295 => true,
+  119540 => true,
+  119541 => true,
+  119542 => true,
+  119543 => true,
+  119544 => true,
+  119545 => true,
+  119546 => true,
+  119547 => true,
+  119548 => true,
+  119549 => true,
+  119550 => true,
+  119551 => true,
+  119639 => true,
+  119640 => true,
+  119641 => true,
+  119642 => true,
+  119643 => true,
+  119644 => true,
+  119645 => true,
+  119646 => true,
+  119647 => true,
+  119893 => true,
+  119965 => true,
+  119968 => true,
+  119969 => true,
+  119971 => true,
+  119972 => true,
+  119975 => true,
+  119976 => true,
+  119981 => true,
+  119994 => true,
+  119996 => true,
+  120004 => true,
+  120070 => true,
+  120075 => true,
+  120076 => true,
+  120085 => true,
+  120093 => true,
+  120122 => true,
+  120127 => true,
+  120133 => true,
+  120135 => true,
+  120136 => true,
+  120137 => true,
+  120145 => true,
+  120486 => true,
+  120487 => true,
+  120780 => true,
+  120781 => true,
+  121484 => true,
+  121485 => true,
+  121486 => true,
+  121487 => true,
+  121488 => true,
+  121489 => true,
+  121490 => true,
+  121491 => true,
+  121492 => true,
+  121493 => true,
+  121494 => true,
+  121495 => true,
+  121496 => true,
+  121497 => true,
+  121498 => true,
+  121504 => true,
+  122887 => true,
+  122905 => true,
+  122906 => true,
+  122914 => true,
+  122917 => true,
+  123181 => true,
+  123182 => true,
+  123183 => true,
+  123198 => true,
+  123199 => true,
+  123210 => true,
+  123211 => true,
+  123212 => true,
+  123213 => true,
+  123642 => true,
+  123643 => true,
+  123644 => true,
+  123645 => true,
+  123646 => true,
+  125125 => true,
+  125126 => true,
+  125260 => true,
+  125261 => true,
+  125262 => true,
+  125263 => true,
+  125274 => true,
+  125275 => true,
+  125276 => true,
+  125277 => true,
+  126468 => true,
+  126496 => true,
+  126499 => true,
+  126501 => true,
+  126502 => true,
+  126504 => true,
+  126515 => true,
+  126520 => true,
+  126522 => true,
+  126524 => true,
+  126525 => true,
+  126526 => true,
+  126527 => true,
+  126528 => true,
+  126529 => true,
+  126531 => true,
+  126532 => true,
+  126533 => true,
+  126534 => true,
+  126536 => true,
+  126538 => true,
+  126540 => true,
+  126544 => true,
+  126547 => true,
+  126549 => true,
+  126550 => true,
+  126552 => true,
+  126554 => true,
+  126556 => true,
+  126558 => true,
+  126560 => true,
+  126563 => true,
+  126565 => true,
+  126566 => true,
+  126571 => true,
+  126579 => true,
+  126584 => true,
+  126589 => true,
+  126591 => true,
+  126602 => true,
+  126620 => true,
+  126621 => true,
+  126622 => true,
+  126623 => true,
+  126624 => true,
+  126628 => true,
+  126634 => true,
+  127020 => true,
+  127021 => true,
+  127022 => true,
+  127023 => true,
+  127124 => true,
+  127125 => true,
+  127126 => true,
+  127127 => true,
+  127128 => true,
+  127129 => true,
+  127130 => true,
+  127131 => true,
+  127132 => true,
+  127133 => true,
+  127134 => true,
+  127135 => true,
+  127151 => true,
+  127152 => true,
+  127168 => true,
+  127184 => true,
+  127222 => true,
+  127223 => true,
+  127224 => true,
+  127225 => true,
+  127226 => true,
+  127227 => true,
+  127228 => true,
+  127229 => true,
+  127230 => true,
+  127231 => true,
+  127232 => true,
+  127491 => true,
+  127492 => true,
+  127493 => true,
+  127494 => true,
+  127495 => true,
+  127496 => true,
+  127497 => true,
+  127498 => true,
+  127499 => true,
+  127500 => true,
+  127501 => true,
+  127502 => true,
+  127503 => true,
+  127548 => true,
+  127549 => true,
+  127550 => true,
+  127551 => true,
+  127561 => true,
+  127562 => true,
+  127563 => true,
+  127564 => true,
+  127565 => true,
+  127566 => true,
+  127567 => true,
+  127570 => true,
+  127571 => true,
+  127572 => true,
+  127573 => true,
+  127574 => true,
+  127575 => true,
+  127576 => true,
+  127577 => true,
+  127578 => true,
+  127579 => true,
+  127580 => true,
+  127581 => true,
+  127582 => true,
+  127583 => true,
+  128728 => true,
+  128729 => true,
+  128730 => true,
+  128731 => true,
+  128732 => true,
+  128733 => true,
+  128734 => true,
+  128735 => true,
+  128749 => true,
+  128750 => true,
+  128751 => true,
+  128765 => true,
+  128766 => true,
+  128767 => true,
+  128884 => true,
+  128885 => true,
+  128886 => true,
+  128887 => true,
+  128888 => true,
+  128889 => true,
+  128890 => true,
+  128891 => true,
+  128892 => true,
+  128893 => true,
+  128894 => true,
+  128895 => true,
+  128985 => true,
+  128986 => true,
+  128987 => true,
+  128988 => true,
+  128989 => true,
+  128990 => true,
+  128991 => true,
+  129004 => true,
+  129005 => true,
+  129006 => true,
+  129007 => true,
+  129008 => true,
+  129009 => true,
+  129010 => true,
+  129011 => true,
+  129012 => true,
+  129013 => true,
+  129014 => true,
+  129015 => true,
+  129016 => true,
+  129017 => true,
+  129018 => true,
+  129019 => true,
+  129020 => true,
+  129021 => true,
+  129022 => true,
+  129023 => true,
+  129036 => true,
+  129037 => true,
+  129038 => true,
+  129039 => true,
+  129096 => true,
+  129097 => true,
+  129098 => true,
+  129099 => true,
+  129100 => true,
+  129101 => true,
+  129102 => true,
+  129103 => true,
+  129114 => true,
+  129115 => true,
+  129116 => true,
+  129117 => true,
+  129118 => true,
+  129119 => true,
+  129160 => true,
+  129161 => true,
+  129162 => true,
+  129163 => true,
+  129164 => true,
+  129165 => true,
+  129166 => true,
+  129167 => true,
+  129198 => true,
+  129199 => true,
+  129401 => true,
+  129484 => true,
+  129620 => true,
+  129621 => true,
+  129622 => true,
+  129623 => true,
+  129624 => true,
+  129625 => true,
+  129626 => true,
+  129627 => true,
+  129628 => true,
+  129629 => true,
+  129630 => true,
+  129631 => true,
+  129646 => true,
+  129647 => true,
+  129653 => true,
+  129654 => true,
+  129655 => true,
+  129659 => true,
+  129660 => true,
+  129661 => true,
+  129662 => true,
+  129663 => true,
+  129671 => true,
+  129672 => true,
+  129673 => true,
+  129674 => true,
+  129675 => true,
+  129676 => true,
+  129677 => true,
+  129678 => true,
+  129679 => true,
+  129705 => true,
+  129706 => true,
+  129707 => true,
+  129708 => true,
+  129709 => true,
+  129710 => true,
+  129711 => true,
+  129719 => true,
+  129720 => true,
+  129721 => true,
+  129722 => true,
+  129723 => true,
+  129724 => true,
+  129725 => true,
+  129726 => true,
+  129727 => true,
+  129731 => true,
+  129732 => true,
+  129733 => true,
+  129734 => true,
+  129735 => true,
+  129736 => true,
+  129737 => true,
+  129738 => true,
+  129739 => true,
+  129740 => true,
+  129741 => true,
+  129742 => true,
+  129743 => true,
+  129939 => true,
+  131070 => true,
+  131071 => true,
+  177973 => true,
+  177974 => true,
+  177975 => true,
+  177976 => true,
+  177977 => true,
+  177978 => true,
+  177979 => true,
+  177980 => true,
+  177981 => true,
+  177982 => true,
+  177983 => true,
+  178206 => true,
+  178207 => true,
+  183970 => true,
+  183971 => true,
+  183972 => true,
+  183973 => true,
+  183974 => true,
+  183975 => true,
+  183976 => true,
+  183977 => true,
+  183978 => true,
+  183979 => true,
+  183980 => true,
+  183981 => true,
+  183982 => true,
+  183983 => true,
+  194664 => true,
+  194676 => true,
+  194847 => true,
+  194911 => true,
+  195007 => true,
+  196606 => true,
+  196607 => true,
+  262142 => true,
+  262143 => true,
+  327678 => true,
+  327679 => true,
+  393214 => true,
+  393215 => true,
+  458750 => true,
+  458751 => true,
+  524286 => true,
+  524287 => true,
+  589822 => true,
+  589823 => true,
+  655358 => true,
+  655359 => true,
+  720894 => true,
+  720895 => true,
+  786430 => true,
+  786431 => true,
+  851966 => true,
+  851967 => true,
+  917502 => true,
+  917503 => true,
+  917504 => true,
+  917505 => true,
+  917506 => true,
+  917507 => true,
+  917508 => true,
+  917509 => true,
+  917510 => true,
+  917511 => true,
+  917512 => true,
+  917513 => true,
+  917514 => true,
+  917515 => true,
+  917516 => true,
+  917517 => true,
+  917518 => true,
+  917519 => true,
+  917520 => true,
+  917521 => true,
+  917522 => true,
+  917523 => true,
+  917524 => true,
+  917525 => true,
+  917526 => true,
+  917527 => true,
+  917528 => true,
+  917529 => true,
+  917530 => true,
+  917531 => true,
+  917532 => true,
+  917533 => true,
+  917534 => true,
+  917535 => true,
+  983038 => true,
+  983039 => true,
+  1048574 => true,
+  1048575 => true,
+  1114110 => true,
+  1114111 => true,
+);
diff --git a/vendor/symfony/polyfill-intl-idn/Resources/unidata/disallowed_STD3_mapped.php b/vendor/symfony/polyfill-intl-idn/Resources/unidata/disallowed_STD3_mapped.php
new file mode 100755
index 0000000..54f21cc
--- /dev/null
+++ b/vendor/symfony/polyfill-intl-idn/Resources/unidata/disallowed_STD3_mapped.php
@@ -0,0 +1,308 @@
+<?php
+
+return array (
+  160 => ' ',
+  168 => ' ̈',
+  175 => ' ̄',
+  180 => ' ́',
+  184 => ' ̧',
+  728 => ' ̆',
+  729 => ' ̇',
+  730 => ' ̊',
+  731 => ' ̨',
+  732 => ' ̃',
+  733 => ' ̋',
+  890 => ' ι',
+  894 => ';',
+  900 => ' ́',
+  901 => ' ̈́',
+  8125 => ' ̓',
+  8127 => ' ̓',
+  8128 => ' ͂',
+  8129 => ' ̈͂',
+  8141 => ' ̓̀',
+  8142 => ' ̓́',
+  8143 => ' ̓͂',
+  8157 => ' ̔̀',
+  8158 => ' ̔́',
+  8159 => ' ̔͂',
+  8173 => ' ̈̀',
+  8174 => ' ̈́',
+  8175 => '`',
+  8189 => ' ́',
+  8190 => ' ̔',
+  8192 => ' ',
+  8193 => ' ',
+  8194 => ' ',
+  8195 => ' ',
+  8196 => ' ',
+  8197 => ' ',
+  8198 => ' ',
+  8199 => ' ',
+  8200 => ' ',
+  8201 => ' ',
+  8202 => ' ',
+  8215 => ' ̳',
+  8239 => ' ',
+  8252 => '!!',
+  8254 => ' ̅',
+  8263 => '??',
+  8264 => '?!',
+  8265 => '!?',
+  8287 => ' ',
+  8314 => '+',
+  8316 => '=',
+  8317 => '(',
+  8318 => ')',
+  8330 => '+',
+  8332 => '=',
+  8333 => '(',
+  8334 => ')',
+  8448 => 'a/c',
+  8449 => 'a/s',
+  8453 => 'c/o',
+  8454 => 'c/u',
+  9332 => '(1)',
+  9333 => '(2)',
+  9334 => '(3)',
+  9335 => '(4)',
+  9336 => '(5)',
+  9337 => '(6)',
+  9338 => '(7)',
+  9339 => '(8)',
+  9340 => '(9)',
+  9341 => '(10)',
+  9342 => '(11)',
+  9343 => '(12)',
+  9344 => '(13)',
+  9345 => '(14)',
+  9346 => '(15)',
+  9347 => '(16)',
+  9348 => '(17)',
+  9349 => '(18)',
+  9350 => '(19)',
+  9351 => '(20)',
+  9372 => '(a)',
+  9373 => '(b)',
+  9374 => '(c)',
+  9375 => '(d)',
+  9376 => '(e)',
+  9377 => '(f)',
+  9378 => '(g)',
+  9379 => '(h)',
+  9380 => '(i)',
+  9381 => '(j)',
+  9382 => '(k)',
+  9383 => '(l)',
+  9384 => '(m)',
+  9385 => '(n)',
+  9386 => '(o)',
+  9387 => '(p)',
+  9388 => '(q)',
+  9389 => '(r)',
+  9390 => '(s)',
+  9391 => '(t)',
+  9392 => '(u)',
+  9393 => '(v)',
+  9394 => '(w)',
+  9395 => '(x)',
+  9396 => '(y)',
+  9397 => '(z)',
+  10868 => '::=',
+  10869 => '==',
+  10870 => '===',
+  12288 => ' ',
+  12443 => ' ゙',
+  12444 => ' ゚',
+  12800 => '(ᄀ)',
+  12801 => '(ᄂ)',
+  12802 => '(ᄃ)',
+  12803 => '(ᄅ)',
+  12804 => '(ᄆ)',
+  12805 => '(ᄇ)',
+  12806 => '(ᄉ)',
+  12807 => '(ᄋ)',
+  12808 => '(ᄌ)',
+  12809 => '(ᄎ)',
+  12810 => '(ᄏ)',
+  12811 => '(ᄐ)',
+  12812 => '(ᄑ)',
+  12813 => '(ᄒ)',
+  12814 => '(가)',
+  12815 => '(나)',
+  12816 => '(다)',
+  12817 => '(라)',
+  12818 => '(마)',
+  12819 => '(바)',
+  12820 => '(사)',
+  12821 => '(아)',
+  12822 => '(자)',
+  12823 => '(차)',
+  12824 => '(카)',
+  12825 => '(타)',
+  12826 => '(파)',
+  12827 => '(하)',
+  12828 => '(주)',
+  12829 => '(오전)',
+  12830 => '(오후)',
+  12832 => '(一)',
+  12833 => '(二)',
+  12834 => '(三)',
+  12835 => '(四)',
+  12836 => '(五)',
+  12837 => '(六)',
+  12838 => '(七)',
+  12839 => '(八)',
+  12840 => '(九)',
+  12841 => '(十)',
+  12842 => '(月)',
+  12843 => '(火)',
+  12844 => '(水)',
+  12845 => '(木)',
+  12846 => '(金)',
+  12847 => '(土)',
+  12848 => '(日)',
+  12849 => '(株)',
+  12850 => '(有)',
+  12851 => '(社)',
+  12852 => '(名)',
+  12853 => '(特)',
+  12854 => '(財)',
+  12855 => '(祝)',
+  12856 => '(労)',
+  12857 => '(代)',
+  12858 => '(呼)',
+  12859 => '(学)',
+  12860 => '(監)',
+  12861 => '(企)',
+  12862 => '(資)',
+  12863 => '(協)',
+  12864 => '(祭)',
+  12865 => '(休)',
+  12866 => '(自)',
+  12867 => '(至)',
+  64297 => '+',
+  64606 => ' ٌّ',
+  64607 => ' ٍّ',
+  64608 => ' َّ',
+  64609 => ' ُّ',
+  64610 => ' ِّ',
+  64611 => ' ّٰ',
+  65018 => 'صلى الله عليه وسلم',
+  65019 => 'جل جلاله',
+  65040 => ',',
+  65043 => ':',
+  65044 => ';',
+  65045 => '!',
+  65046 => '?',
+  65075 => '_',
+  65076 => '_',
+  65077 => '(',
+  65078 => ')',
+  65079 => '{',
+  65080 => '}',
+  65095 => '[',
+  65096 => ']',
+  65097 => ' ̅',
+  65098 => ' ̅',
+  65099 => ' ̅',
+  65100 => ' ̅',
+  65101 => '_',
+  65102 => '_',
+  65103 => '_',
+  65104 => ',',
+  65108 => ';',
+  65109 => ':',
+  65110 => '?',
+  65111 => '!',
+  65113 => '(',
+  65114 => ')',
+  65115 => '{',
+  65116 => '}',
+  65119 => '#',
+  65120 => '&',
+  65121 => '*',
+  65122 => '+',
+  65124 => '<',
+  65125 => '>',
+  65126 => '=',
+  65128 => '\\',
+  65129 => '$',
+  65130 => '%',
+  65131 => '@',
+  65136 => ' ً',
+  65138 => ' ٌ',
+  65140 => ' ٍ',
+  65142 => ' َ',
+  65144 => ' ُ',
+  65146 => ' ِ',
+  65148 => ' ّ',
+  65150 => ' ْ',
+  65281 => '!',
+  65282 => '"',
+  65283 => '#',
+  65284 => '$',
+  65285 => '%',
+  65286 => '&',
+  65287 => '\'',
+  65288 => '(',
+  65289 => ')',
+  65290 => '*',
+  65291 => '+',
+  65292 => ',',
+  65295 => '/',
+  65306 => ':',
+  65307 => ';',
+  65308 => '<',
+  65309 => '=',
+  65310 => '>',
+  65311 => '?',
+  65312 => '@',
+  65339 => '[',
+  65340 => '\\',
+  65341 => ']',
+  65342 => '^',
+  65343 => '_',
+  65344 => '`',
+  65371 => '{',
+  65372 => '|',
+  65373 => '}',
+  65374 => '~',
+  65507 => ' ̄',
+  127233 => '0,',
+  127234 => '1,',
+  127235 => '2,',
+  127236 => '3,',
+  127237 => '4,',
+  127238 => '5,',
+  127239 => '6,',
+  127240 => '7,',
+  127241 => '8,',
+  127242 => '9,',
+  127248 => '(a)',
+  127249 => '(b)',
+  127250 => '(c)',
+  127251 => '(d)',
+  127252 => '(e)',
+  127253 => '(f)',
+  127254 => '(g)',
+  127255 => '(h)',
+  127256 => '(i)',
+  127257 => '(j)',
+  127258 => '(k)',
+  127259 => '(l)',
+  127260 => '(m)',
+  127261 => '(n)',
+  127262 => '(o)',
+  127263 => '(p)',
+  127264 => '(q)',
+  127265 => '(r)',
+  127266 => '(s)',
+  127267 => '(t)',
+  127268 => '(u)',
+  127269 => '(v)',
+  127270 => '(w)',
+  127271 => '(x)',
+  127272 => '(y)',
+  127273 => '(z)',
+);
diff --git a/vendor/symfony/polyfill-intl-idn/Resources/unidata/disallowed_STD3_valid.php b/vendor/symfony/polyfill-intl-idn/Resources/unidata/disallowed_STD3_valid.php
new file mode 100755
index 0000000..223396e
--- /dev/null
+++ b/vendor/symfony/polyfill-intl-idn/Resources/unidata/disallowed_STD3_valid.php
@@ -0,0 +1,71 @@
+<?php
+
+return array (
+  0 => true,
+  1 => true,
+  2 => true,
+  3 => true,
+  4 => true,
+  5 => true,
+  6 => true,
+  7 => true,
+  8 => true,
+  9 => true,
+  10 => true,
+  11 => true,
+  12 => true,
+  13 => true,
+  14 => true,
+  15 => true,
+  16 => true,
+  17 => true,
+  18 => true,
+  19 => true,
+  20 => true,
+  21 => true,
+  22 => true,
+  23 => true,
+  24 => true,
+  25 => true,
+  26 => true,
+  27 => true,
+  28 => true,
+  29 => true,
+  30 => true,
+  31 => true,
+  32 => true,
+  33 => true,
+  34 => true,
+  35 => true,
+  36 => true,
+  37 => true,
+  38 => true,
+  39 => true,
+  40 => true,
+  41 => true,
+  42 => true,
+  43 => true,
+  44 => true,
+  47 => true,
+  58 => true,
+  59 => true,
+  60 => true,
+  61 => true,
+  62 => true,
+  63 => true,
+  64 => true,
+  91 => true,
+  92 => true,
+  93 => true,
+  94 => true,
+  95 => true,
+  96 => true,
+  123 => true,
+  124 => true,
+  125 => true,
+  126 => true,
+  127 => true,
+  8800 => true,
+  8814 => true,
+  8815 => true,
+);
diff --git a/vendor/symfony/polyfill-intl-idn/Resources/unidata/ignored.php b/vendor/symfony/polyfill-intl-idn/Resources/unidata/ignored.php
new file mode 100755
index 0000000..b377844
--- /dev/null
+++ b/vendor/symfony/polyfill-intl-idn/Resources/unidata/ignored.php
@@ -0,0 +1,273 @@
+<?php
+
+return array (
+  173 => true,
+  847 => true,
+  6155 => true,
+  6156 => true,
+  6157 => true,
+  8203 => true,
+  8288 => true,
+  8292 => true,
+  65024 => true,
+  65025 => true,
+  65026 => true,
+  65027 => true,
+  65028 => true,
+  65029 => true,
+  65030 => true,
+  65031 => true,
+  65032 => true,
+  65033 => true,
+  65034 => true,
+  65035 => true,
+  65036 => true,
+  65037 => true,
+  65038 => true,
+  65039 => true,
+  65279 => true,
+  113824 => true,
+  113825 => true,
+  113826 => true,
+  113827 => true,
+  917760 => true,
+  917761 => true,
+  917762 => true,
+  917763 => true,
+  917764 => true,
+  917765 => true,
+  917766 => true,
+  917767 => true,
+  917768 => true,
+  917769 => true,
+  917770 => true,
+  917771 => true,
+  917772 => true,
+  917773 => true,
+  917774 => true,
+  917775 => true,
+  917776 => true,
+  917777 => true,
+  917778 => true,
+  917779 => true,
+  917780 => true,
+  917781 => true,
+  917782 => true,
+  917783 => true,
+  917784 => true,
+  917785 => true,
+  917786 => true,
+  917787 => true,
+  917788 => true,
+  917789 => true,
+  917790 => true,
+  917791 => true,
+  917792 => true,
+  917793 => true,
+  917794 => true,
+  917795 => true,
+  917796 => true,
+  917797 => true,
+  917798 => true,
+  917799 => true,
+  917800 => true,
+  917801 => true,
+  917802 => true,
+  917803 => true,
+  917804 => true,
+  917805 => true,
+  917806 => true,
+  917807 => true,
+  917808 => true,
+  917809 => true,
+  917810 => true,
+  917811 => true,
+  917812 => true,
+  917813 => true,
+  917814 => true,
+  917815 => true,
+  917816 => true,
+  917817 => true,
+  917818 => true,
+  917819 => true,
+  917820 => true,
+  917821 => true,
+  917822 => true,
+  917823 => true,
+  917824 => true,
+  917825 => true,
+  917826 => true,
+  917827 => true,
+  917828 => true,
+  917829 => true,
+  917830 => true,
+  917831 => true,
+  917832 => true,
+  917833 => true,
+  917834 => true,
+  917835 => true,
+  917836 => true,
+  917837 => true,
+  917838 => true,
+  917839 => true,
+  917840 => true,
+  917841 => true,
+  917842 => true,
+  917843 => true,
+  917844 => true,
+  917845 => true,
+  917846 => true,
+  917847 => true,
+  917848 => true,
+  917849 => true,
+  917850 => true,
+  917851 => true,
+  917852 => true,
+  917853 => true,
+  917854 => true,
+  917855 => true,
+  917856 => true,
+  917857 => true,
+  917858 => true,
+  917859 => true,
+  917860 => true,
+  917861 => true,
+  917862 => true,
+  917863 => true,
+  917864 => true,
+  917865 => true,
+  917866 => true,
+  917867 => true,
+  917868 => true,
+  917869 => true,
+  917870 => true,
+  917871 => true,
+  917872 => true,
+  917873 => true,
+  917874 => true,
+  917875 => true,
+  917876 => true,
+  917877 => true,
+  917878 => true,
+  917879 => true,
+  917880 => true,
+  917881 => true,
+  917882 => true,
+  917883 => true,
+  917884 => true,
+  917885 => true,
+  917886 => true,
+  917887 => true,
+  917888 => true,
+  917889 => true,
+  917890 => true,
+  917891 => true,
+  917892 => true,
+  917893 => true,
+  917894 => true,
+  917895 => true,
+  917896 => true,
+  917897 => true,
+  917898 => true,
+  917899 => true,
+  917900 => true,
+  917901 => true,
+  917902 => true,
+  917903 => true,
+  917904 => true,
+  917905 => true,
+  917906 => true,
+  917907 => true,
+  917908 => true,
+  917909 => true,
+  917910 => true,
+  917911 => true,
+  917912 => true,
+  917913 => true,
+  917914 => true,
+  917915 => true,
+  917916 => true,
+  917917 => true,
+  917918 => true,
+  917919 => true,
+  917920 => true,
+  917921 => true,
+  917922 => true,
+  917923 => true,
+  917924 => true,
+  917925 => true,
+  917926 => true,
+  917927 => true,
+  917928 => true,
+  917929 => true,
+  917930 => true,
+  917931 => true,
+  917932 => true,
+  917933 => true,
+  917934 => true,
+  917935 => true,
+  917936 => true,
+  917937 => true,
+  917938 => true,
+  917939 => true,
+  917940 => true,
+  917941 => true,
+  917942 => true,
+  917943 => true,
+  917944 => true,
+  917945 => true,
+  917946 => true,
+  917947 => true,
+  917948 => true,
+  917949 => true,
+  917950 => true,
+  917951 => true,
+  917952 => true,
+  917953 => true,
+  917954 => true,
+  917955 => true,
+  917956 => true,
+  917957 => true,
+  917958 => true,
+  917959 => true,
+  917960 => true,
+  917961 => true,
+  917962 => true,
+  917963 => true,
+  917964 => true,
+  917965 => true,
+  917966 => true,
+  917967 => true,
+  917968 => true,
+  917969 => true,
+  917970 => true,
+  917971 => true,
+  917972 => true,
+  917973 => true,
+  917974 => true,
+  917975 => true,
+  917976 => true,
+  917977 => true,
+  917978 => true,
+  917979 => true,
+  917980 => true,
+  917981 => true,
+  917982 => true,
+  917983 => true,
+  917984 => true,
+  917985 => true,
+  917986 => true,
+  917987 => true,
+  917988 => true,
+  917989 => true,
+  917990 => true,
+  917991 => true,
+  917992 => true,
+  917993 => true,
+  917994 => true,
+  917995 => true,
+  917996 => true,
+  917997 => true,
+  917998 => true,
+  917999 => true,
+);
diff --git a/vendor/symfony/polyfill-intl-idn/Resources/unidata/mapped.php b/vendor/symfony/polyfill-intl-idn/Resources/unidata/mapped.php
new file mode 100755
index 0000000..9b85fe9
--- /dev/null
+++ b/vendor/symfony/polyfill-intl-idn/Resources/unidata/mapped.php
@@ -0,0 +1,5778 @@
+<?php
+
+return array (
+  65 => 'a',
+  66 => 'b',
+  67 => 'c',
+  68 => 'd',
+  69 => 'e',
+  70 => 'f',
+  71 => 'g',
+  72 => 'h',
+  73 => 'i',
+  74 => 'j',
+  75 => 'k',
+  76 => 'l',
+  77 => 'm',
+  78 => 'n',
+  79 => 'o',
+  80 => 'p',
+  81 => 'q',
+  82 => 'r',
+  83 => 's',
+  84 => 't',
+  85 => 'u',
+  86 => 'v',
+  87 => 'w',
+  88 => 'x',
+  89 => 'y',
+  90 => 'z',
+  170 => 'a',
+  178 => '2',
+  179 => '3',
+  181 => 'μ',
+  185 => '1',
+  186 => 'o',
+  188 => '1⁄4',
+  189 => '1⁄2',
+  190 => '3⁄4',
+  192 => 'à',
+  193 => 'á',
+  194 => 'â',
+  195 => 'ã',
+  196 => 'ä',
+  197 => 'å',
+  198 => 'æ',
+  199 => 'ç',
+  200 => 'è',
+  201 => 'é',
+  202 => 'ê',
+  203 => 'ë',
+  204 => 'ì',
+  205 => 'í',
+  206 => 'î',
+  207 => 'ï',
+  208 => 'ð',
+  209 => 'ñ',
+  210 => 'ò',
+  211 => 'ó',
+  212 => 'ô',
+  213 => 'õ',
+  214 => 'ö',
+  216 => 'ø',
+  217 => 'ù',
+  218 => 'ú',
+  219 => 'û',
+  220 => 'ü',
+  221 => 'ý',
+  222 => 'þ',
+  256 => 'ā',
+  258 => 'ă',
+  260 => 'ą',
+  262 => 'ć',
+  264 => 'ĉ',
+  266 => 'ċ',
+  268 => 'č',
+  270 => 'ď',
+  272 => 'đ',
+  274 => 'ē',
+  276 => 'ĕ',
+  278 => 'ė',
+  280 => 'ę',
+  282 => 'ě',
+  284 => 'ĝ',
+  286 => 'ğ',
+  288 => 'ġ',
+  290 => 'ģ',
+  292 => 'ĥ',
+  294 => 'ħ',
+  296 => 'ĩ',
+  298 => 'ī',
+  300 => 'ĭ',
+  302 => 'į',
+  304 => 'i̇',
+  306 => 'ij',
+  307 => 'ij',
+  308 => 'ĵ',
+  310 => 'ķ',
+  313 => 'ĺ',
+  315 => 'ļ',
+  317 => 'ľ',
+  319 => 'l·',
+  320 => 'l·',
+  321 => 'ł',
+  323 => 'ń',
+  325 => 'ņ',
+  327 => 'ň',
+  329 => 'ʼn',
+  330 => 'ŋ',
+  332 => 'ō',
+  334 => 'ŏ',
+  336 => 'ő',
+  338 => 'œ',
+  340 => 'ŕ',
+  342 => 'ŗ',
+  344 => 'ř',
+  346 => 'ś',
+  348 => 'ŝ',
+  350 => 'ş',
+  352 => 'š',
+  354 => 'ţ',
+  356 => 'ť',
+  358 => 'ŧ',
+  360 => 'ũ',
+  362 => 'ū',
+  364 => 'ŭ',
+  366 => 'ů',
+  368 => 'ű',
+  370 => 'ų',
+  372 => 'ŵ',
+  374 => 'ŷ',
+  376 => 'ÿ',
+  377 => 'ź',
+  379 => 'ż',
+  381 => 'ž',
+  383 => 's',
+  385 => 'ɓ',
+  386 => 'ƃ',
+  388 => 'ƅ',
+  390 => 'ɔ',
+  391 => 'ƈ',
+  393 => 'ɖ',
+  394 => 'ɗ',
+  395 => 'ƌ',
+  398 => 'ǝ',
+  399 => 'ə',
+  400 => 'ɛ',
+  401 => 'ƒ',
+  403 => 'ɠ',
+  404 => 'ɣ',
+  406 => 'ɩ',
+  407 => 'ɨ',
+  408 => 'ƙ',
+  412 => 'ɯ',
+  413 => 'ɲ',
+  415 => 'ɵ',
+  416 => 'ơ',
+  418 => 'ƣ',
+  420 => 'ƥ',
+  422 => 'ʀ',
+  423 => 'ƨ',
+  425 => 'ʃ',
+  428 => 'ƭ',
+  430 => 'ʈ',
+  431 => 'ư',
+  433 => 'ʊ',
+  434 => 'ʋ',
+  435 => 'ƴ',
+  437 => 'ƶ',
+  439 => 'ʒ',
+  440 => 'ƹ',
+  444 => 'ƽ',
+  452 => 'dž',
+  453 => 'dž',
+  454 => 'dž',
+  455 => 'lj',
+  456 => 'lj',
+  457 => 'lj',
+  458 => 'nj',
+  459 => 'nj',
+  460 => 'nj',
+  461 => 'ǎ',
+  463 => 'ǐ',
+  465 => 'ǒ',
+  467 => 'ǔ',
+  469 => 'ǖ',
+  471 => 'ǘ',
+  473 => 'ǚ',
+  475 => 'ǜ',
+  478 => 'ǟ',
+  480 => 'ǡ',
+  482 => 'ǣ',
+  484 => 'ǥ',
+  486 => 'ǧ',
+  488 => 'ǩ',
+  490 => 'ǫ',
+  492 => 'ǭ',
+  494 => 'ǯ',
+  497 => 'dz',
+  498 => 'dz',
+  499 => 'dz',
+  500 => 'ǵ',
+  502 => 'ƕ',
+  503 => 'ƿ',
+  504 => 'ǹ',
+  506 => 'ǻ',
+  508 => 'ǽ',
+  510 => 'ǿ',
+  512 => 'ȁ',
+  514 => 'ȃ',
+  516 => 'ȅ',
+  518 => 'ȇ',
+  520 => 'ȉ',
+  522 => 'ȋ',
+  524 => 'ȍ',
+  526 => 'ȏ',
+  528 => 'ȑ',
+  530 => 'ȓ',
+  532 => 'ȕ',
+  534 => 'ȗ',
+  536 => 'ș',
+  538 => 'ț',
+  540 => 'ȝ',
+  542 => 'ȟ',
+  544 => 'ƞ',
+  546 => 'ȣ',
+  548 => 'ȥ',
+  550 => 'ȧ',
+  552 => 'ȩ',
+  554 => 'ȫ',
+  556 => 'ȭ',
+  558 => 'ȯ',
+  560 => 'ȱ',
+  562 => 'ȳ',
+  570 => 'ⱥ',
+  571 => 'ȼ',
+  573 => 'ƚ',
+  574 => 'ⱦ',
+  577 => 'ɂ',
+  579 => 'ƀ',
+  580 => 'ʉ',
+  581 => 'ʌ',
+  582 => 'ɇ',
+  584 => 'ɉ',
+  586 => 'ɋ',
+  588 => 'ɍ',
+  590 => 'ɏ',
+  688 => 'h',
+  689 => 'ɦ',
+  690 => 'j',
+  691 => 'r',
+  692 => 'ɹ',
+  693 => 'ɻ',
+  694 => 'ʁ',
+  695 => 'w',
+  696 => 'y',
+  736 => 'ɣ',
+  737 => 'l',
+  738 => 's',
+  739 => 'x',
+  740 => 'ʕ',
+  832 => '̀',
+  833 => '́',
+  835 => '̓',
+  836 => '̈́',
+  837 => 'ι',
+  880 => 'ͱ',
+  882 => 'ͳ',
+  884 => 'ʹ',
+  886 => 'ͷ',
+  895 => 'ϳ',
+  902 => 'ά',
+  903 => '·',
+  904 => 'έ',
+  905 => 'ή',
+  906 => 'ί',
+  908 => 'ό',
+  910 => 'ύ',
+  911 => 'ώ',
+  913 => 'α',
+  914 => 'β',
+  915 => 'γ',
+  916 => 'δ',
+  917 => 'ε',
+  918 => 'ζ',
+  919 => 'η',
+  920 => 'θ',
+  921 => 'ι',
+  922 => 'κ',
+  923 => 'λ',
+  924 => 'μ',
+  925 => 'ν',
+  926 => 'ξ',
+  927 => 'ο',
+  928 => 'π',
+  929 => 'ρ',
+  931 => 'σ',
+  932 => 'τ',
+  933 => 'υ',
+  934 => 'φ',
+  935 => 'χ',
+  936 => 'ψ',
+  937 => 'ω',
+  938 => 'ϊ',
+  939 => 'ϋ',
+  975 => 'ϗ',
+  976 => 'β',
+  977 => 'θ',
+  978 => 'υ',
+  979 => 'ύ',
+  980 => 'ϋ',
+  981 => 'φ',
+  982 => 'π',
+  984 => 'ϙ',
+  986 => 'ϛ',
+  988 => 'ϝ',
+  990 => 'ϟ',
+  992 => 'ϡ',
+  994 => 'ϣ',
+  996 => 'ϥ',
+  998 => 'ϧ',
+  1000 => 'ϩ',
+  1002 => 'ϫ',
+  1004 => 'ϭ',
+  1006 => 'ϯ',
+  1008 => 'κ',
+  1009 => 'ρ',
+  1010 => 'σ',
+  1012 => 'θ',
+  1013 => 'ε',
+  1015 => 'ϸ',
+  1017 => 'σ',
+  1018 => 'ϻ',
+  1021 => 'ͻ',
+  1022 => 'ͼ',
+  1023 => 'ͽ',
+  1024 => 'ѐ',
+  1025 => 'ё',
+  1026 => 'ђ',
+  1027 => 'ѓ',
+  1028 => 'є',
+  1029 => 'ѕ',
+  1030 => 'і',
+  1031 => 'ї',
+  1032 => 'ј',
+  1033 => 'љ',
+  1034 => 'њ',
+  1035 => 'ћ',
+  1036 => 'ќ',
+  1037 => 'ѝ',
+  1038 => 'ў',
+  1039 => 'џ',
+  1040 => 'а',
+  1041 => 'б',
+  1042 => 'в',
+  1043 => 'г',
+  1044 => 'д',
+  1045 => 'е',
+  1046 => 'ж',
+  1047 => 'з',
+  1048 => 'и',
+  1049 => 'й',
+  1050 => 'к',
+  1051 => 'л',
+  1052 => 'м',
+  1053 => 'н',
+  1054 => 'о',
+  1055 => 'п',
+  1056 => 'р',
+  1057 => 'с',
+  1058 => 'т',
+  1059 => 'у',
+  1060 => 'ф',
+  1061 => 'х',
+  1062 => 'ц',
+  1063 => 'ч',
+  1064 => 'ш',
+  1065 => 'щ',
+  1066 => 'ъ',
+  1067 => 'ы',
+  1068 => 'ь',
+  1069 => 'э',
+  1070 => 'ю',
+  1071 => 'я',
+  1120 => 'ѡ',
+  1122 => 'ѣ',
+  1124 => 'ѥ',
+  1126 => 'ѧ',
+  1128 => 'ѩ',
+  1130 => 'ѫ',
+  1132 => 'ѭ',
+  1134 => 'ѯ',
+  1136 => 'ѱ',
+  1138 => 'ѳ',
+  1140 => 'ѵ',
+  1142 => 'ѷ',
+  1144 => 'ѹ',
+  1146 => 'ѻ',
+  1148 => 'ѽ',
+  1150 => 'ѿ',
+  1152 => 'ҁ',
+  1162 => 'ҋ',
+  1164 => 'ҍ',
+  1166 => 'ҏ',
+  1168 => 'ґ',
+  1170 => 'ғ',
+  1172 => 'ҕ',
+  1174 => 'җ',
+  1176 => 'ҙ',
+  1178 => 'қ',
+  1180 => 'ҝ',
+  1182 => 'ҟ',
+  1184 => 'ҡ',
+  1186 => 'ң',
+  1188 => 'ҥ',
+  1190 => 'ҧ',
+  1192 => 'ҩ',
+  1194 => 'ҫ',
+  1196 => 'ҭ',
+  1198 => 'ү',
+  1200 => 'ұ',
+  1202 => 'ҳ',
+  1204 => 'ҵ',
+  1206 => 'ҷ',
+  1208 => 'ҹ',
+  1210 => 'һ',
+  1212 => 'ҽ',
+  1214 => 'ҿ',
+  1217 => 'ӂ',
+  1219 => 'ӄ',
+  1221 => 'ӆ',
+  1223 => 'ӈ',
+  1225 => 'ӊ',
+  1227 => 'ӌ',
+  1229 => 'ӎ',
+  1232 => 'ӑ',
+  1234 => 'ӓ',
+  1236 => 'ӕ',
+  1238 => 'ӗ',
+  1240 => 'ә',
+  1242 => 'ӛ',
+  1244 => 'ӝ',
+  1246 => 'ӟ',
+  1248 => 'ӡ',
+  1250 => 'ӣ',
+  1252 => 'ӥ',
+  1254 => 'ӧ',
+  1256 => 'ө',
+  1258 => 'ӫ',
+  1260 => 'ӭ',
+  1262 => 'ӯ',
+  1264 => 'ӱ',
+  1266 => 'ӳ',
+  1268 => 'ӵ',
+  1270 => 'ӷ',
+  1272 => 'ӹ',
+  1274 => 'ӻ',
+  1276 => 'ӽ',
+  1278 => 'ӿ',
+  1280 => 'ԁ',
+  1282 => 'ԃ',
+  1284 => 'ԅ',
+  1286 => 'ԇ',
+  1288 => 'ԉ',
+  1290 => 'ԋ',
+  1292 => 'ԍ',
+  1294 => 'ԏ',
+  1296 => 'ԑ',
+  1298 => 'ԓ',
+  1300 => 'ԕ',
+  1302 => 'ԗ',
+  1304 => 'ԙ',
+  1306 => 'ԛ',
+  1308 => 'ԝ',
+  1310 => 'ԟ',
+  1312 => 'ԡ',
+  1314 => 'ԣ',
+  1316 => 'ԥ',
+  1318 => 'ԧ',
+  1320 => 'ԩ',
+  1322 => 'ԫ',
+  1324 => 'ԭ',
+  1326 => 'ԯ',
+  1329 => 'ա',
+  1330 => 'բ',
+  1331 => 'գ',
+  1332 => 'դ',
+  1333 => 'ե',
+  1334 => 'զ',
+  1335 => 'է',
+  1336 => 'ը',
+  1337 => 'թ',
+  1338 => 'ժ',
+  1339 => 'ի',
+  1340 => 'լ',
+  1341 => 'խ',
+  1342 => 'ծ',
+  1343 => 'կ',
+  1344 => 'հ',
+  1345 => 'ձ',
+  1346 => 'ղ',
+  1347 => 'ճ',
+  1348 => 'մ',
+  1349 => 'յ',
+  1350 => 'ն',
+  1351 => 'շ',
+  1352 => 'ո',
+  1353 => 'չ',
+  1354 => 'պ',
+  1355 => 'ջ',
+  1356 => 'ռ',
+  1357 => 'ս',
+  1358 => 'վ',
+  1359 => 'տ',
+  1360 => 'ր',
+  1361 => 'ց',
+  1362 => 'ւ',
+  1363 => 'փ',
+  1364 => 'ք',
+  1365 => 'օ',
+  1366 => 'ֆ',
+  1415 => 'եւ',
+  1653 => 'اٴ',
+  1654 => 'وٴ',
+  1655 => 'ۇٴ',
+  1656 => 'يٴ',
+  2392 => 'क़',
+  2393 => 'ख़',
+  2394 => 'ग़',
+  2395 => 'ज़',
+  2396 => 'ड़',
+  2397 => 'ढ़',
+  2398 => 'फ़',
+  2399 => 'य़',
+  2524 => 'ড়',
+  2525 => 'ঢ়',
+  2527 => 'য়',
+  2611 => 'ਲ਼',
+  2614 => 'ਸ਼',
+  2649 => 'ਖ਼',
+  2650 => 'ਗ਼',
+  2651 => 'ਜ਼',
+  2654 => 'ਫ਼',
+  2908 => 'ଡ଼',
+  2909 => 'ଢ଼',
+  3635 => 'ํา',
+  3763 => 'ໍາ',
+  3804 => 'ຫນ',
+  3805 => 'ຫມ',
+  3852 => '་',
+  3907 => 'གྷ',
+  3917 => 'ཌྷ',
+  3922 => 'དྷ',
+  3927 => 'བྷ',
+  3932 => 'ཛྷ',
+  3945 => 'ཀྵ',
+  3955 => 'ཱི',
+  3957 => 'ཱུ',
+  3958 => 'ྲྀ',
+  3959 => 'ྲཱྀ',
+  3960 => 'ླྀ',
+  3961 => 'ླཱྀ',
+  3969 => 'ཱྀ',
+  3987 => 'ྒྷ',
+  3997 => 'ྜྷ',
+  4002 => 'ྡྷ',
+  4007 => 'ྦྷ',
+  4012 => 'ྫྷ',
+  4025 => 'ྐྵ',
+  4295 => 'ⴧ',
+  4301 => 'ⴭ',
+  4348 => 'ნ',
+  5112 => 'Ᏸ',
+  5113 => 'Ᏹ',
+  5114 => 'Ᏺ',
+  5115 => 'Ᏻ',
+  5116 => 'Ᏼ',
+  5117 => 'Ᏽ',
+  7296 => 'в',
+  7297 => 'д',
+  7298 => 'о',
+  7299 => 'с',
+  7300 => 'т',
+  7301 => 'т',
+  7302 => 'ъ',
+  7303 => 'ѣ',
+  7304 => 'ꙋ',
+  7312 => 'ა',
+  7313 => 'ბ',
+  7314 => 'გ',
+  7315 => 'დ',
+  7316 => 'ე',
+  7317 => 'ვ',
+  7318 => 'ზ',
+  7319 => 'თ',
+  7320 => 'ი',
+  7321 => 'კ',
+  7322 => 'ლ',
+  7323 => 'მ',
+  7324 => 'ნ',
+  7325 => 'ო',
+  7326 => 'პ',
+  7327 => 'ჟ',
+  7328 => 'რ',
+  7329 => 'ს',
+  7330 => 'ტ',
+  7331 => 'უ',
+  7332 => 'ფ',
+  7333 => 'ქ',
+  7334 => 'ღ',
+  7335 => 'ყ',
+  7336 => 'შ',
+  7337 => 'ჩ',
+  7338 => 'ც',
+  7339 => 'ძ',
+  7340 => 'წ',
+  7341 => 'ჭ',
+  7342 => 'ხ',
+  7343 => 'ჯ',
+  7344 => 'ჰ',
+  7345 => 'ჱ',
+  7346 => 'ჲ',
+  7347 => 'ჳ',
+  7348 => 'ჴ',
+  7349 => 'ჵ',
+  7350 => 'ჶ',
+  7351 => 'ჷ',
+  7352 => 'ჸ',
+  7353 => 'ჹ',
+  7354 => 'ჺ',
+  7357 => 'ჽ',
+  7358 => 'ჾ',
+  7359 => 'ჿ',
+  7468 => 'a',
+  7469 => 'æ',
+  7470 => 'b',
+  7472 => 'd',
+  7473 => 'e',
+  7474 => 'ǝ',
+  7475 => 'g',
+  7476 => 'h',
+  7477 => 'i',
+  7478 => 'j',
+  7479 => 'k',
+  7480 => 'l',
+  7481 => 'm',
+  7482 => 'n',
+  7484 => 'o',
+  7485 => 'ȣ',
+  7486 => 'p',
+  7487 => 'r',
+  7488 => 't',
+  7489 => 'u',
+  7490 => 'w',
+  7491 => 'a',
+  7492 => 'ɐ',
+  7493 => 'ɑ',
+  7494 => 'ᴂ',
+  7495 => 'b',
+  7496 => 'd',
+  7497 => 'e',
+  7498 => 'ə',
+  7499 => 'ɛ',
+  7500 => 'ɜ',
+  7501 => 'g',
+  7503 => 'k',
+  7504 => 'm',
+  7505 => 'ŋ',
+  7506 => 'o',
+  7507 => 'ɔ',
+  7508 => 'ᴖ',
+  7509 => 'ᴗ',
+  7510 => 'p',
+  7511 => 't',
+  7512 => 'u',
+  7513 => 'ᴝ',
+  7514 => 'ɯ',
+  7515 => 'v',
+  7516 => 'ᴥ',
+  7517 => 'β',
+  7518 => 'γ',
+  7519 => 'δ',
+  7520 => 'φ',
+  7521 => 'χ',
+  7522 => 'i',
+  7523 => 'r',
+  7524 => 'u',
+  7525 => 'v',
+  7526 => 'β',
+  7527 => 'γ',
+  7528 => 'ρ',
+  7529 => 'φ',
+  7530 => 'χ',
+  7544 => 'н',
+  7579 => 'ɒ',
+  7580 => 'c',
+  7581 => 'ɕ',
+  7582 => 'ð',
+  7583 => 'ɜ',
+  7584 => 'f',
+  7585 => 'ɟ',
+  7586 => 'ɡ',
+  7587 => 'ɥ',
+  7588 => 'ɨ',
+  7589 => 'ɩ',
+  7590 => 'ɪ',
+  7591 => 'ᵻ',
+  7592 => 'ʝ',
+  7593 => 'ɭ',
+  7594 => 'ᶅ',
+  7595 => 'ʟ',
+  7596 => 'ɱ',
+  7597 => 'ɰ',
+  7598 => 'ɲ',
+  7599 => 'ɳ',
+  7600 => 'ɴ',
+  7601 => 'ɵ',
+  7602 => 'ɸ',
+  7603 => 'ʂ',
+  7604 => 'ʃ',
+  7605 => 'ƫ',
+  7606 => 'ʉ',
+  7607 => 'ʊ',
+  7608 => 'ᴜ',
+  7609 => 'ʋ',
+  7610 => 'ʌ',
+  7611 => 'z',
+  7612 => 'ʐ',
+  7613 => 'ʑ',
+  7614 => 'ʒ',
+  7615 => 'θ',
+  7680 => 'ḁ',
+  7682 => 'ḃ',
+  7684 => 'ḅ',
+  7686 => 'ḇ',
+  7688 => 'ḉ',
+  7690 => 'ḋ',
+  7692 => 'ḍ',
+  7694 => 'ḏ',
+  7696 => 'ḑ',
+  7698 => 'ḓ',
+  7700 => 'ḕ',
+  7702 => 'ḗ',
+  7704 => 'ḙ',
+  7706 => 'ḛ',
+  7708 => 'ḝ',
+  7710 => 'ḟ',
+  7712 => 'ḡ',
+  7714 => 'ḣ',
+  7716 => 'ḥ',
+  7718 => 'ḧ',
+  7720 => 'ḩ',
+  7722 => 'ḫ',
+  7724 => 'ḭ',
+  7726 => 'ḯ',
+  7728 => 'ḱ',
+  7730 => 'ḳ',
+  7732 => 'ḵ',
+  7734 => 'ḷ',
+  7736 => 'ḹ',
+  7738 => 'ḻ',
+  7740 => 'ḽ',
+  7742 => 'ḿ',
+  7744 => 'ṁ',
+  7746 => 'ṃ',
+  7748 => 'ṅ',
+  7750 => 'ṇ',
+  7752 => 'ṉ',
+  7754 => 'ṋ',
+  7756 => 'ṍ',
+  7758 => 'ṏ',
+  7760 => 'ṑ',
+  7762 => 'ṓ',
+  7764 => 'ṕ',
+  7766 => 'ṗ',
+  7768 => 'ṙ',
+  7770 => 'ṛ',
+  7772 => 'ṝ',
+  7774 => 'ṟ',
+  7776 => 'ṡ',
+  7778 => 'ṣ',
+  7780 => 'ṥ',
+  7782 => 'ṧ',
+  7784 => 'ṩ',
+  7786 => 'ṫ',
+  7788 => 'ṭ',
+  7790 => 'ṯ',
+  7792 => 'ṱ',
+  7794 => 'ṳ',
+  7796 => 'ṵ',
+  7798 => 'ṷ',
+  7800 => 'ṹ',
+  7802 => 'ṻ',
+  7804 => 'ṽ',
+  7806 => 'ṿ',
+  7808 => 'ẁ',
+  7810 => 'ẃ',
+  7812 => 'ẅ',
+  7814 => 'ẇ',
+  7816 => 'ẉ',
+  7818 => 'ẋ',
+  7820 => 'ẍ',
+  7822 => 'ẏ',
+  7824 => 'ẑ',
+  7826 => 'ẓ',
+  7828 => 'ẕ',
+  7834 => 'aʾ',
+  7835 => 'ṡ',
+  7838 => 'ss',
+  7840 => 'ạ',
+  7842 => 'ả',
+  7844 => 'ấ',
+  7846 => 'ầ',
+  7848 => 'ẩ',
+  7850 => 'ẫ',
+  7852 => 'ậ',
+  7854 => 'ắ',
+  7856 => 'ằ',
+  7858 => 'ẳ',
+  7860 => 'ẵ',
+  7862 => 'ặ',
+  7864 => 'ẹ',
+  7866 => 'ẻ',
+  7868 => 'ẽ',
+  7870 => 'ế',
+  7872 => 'ề',
+  7874 => 'ể',
+  7876 => 'ễ',
+  7878 => 'ệ',
+  7880 => 'ỉ',
+  7882 => 'ị',
+  7884 => 'ọ',
+  7886 => 'ỏ',
+  7888 => 'ố',
+  7890 => 'ồ',
+  7892 => 'ổ',
+  7894 => 'ỗ',
+  7896 => 'ộ',
+  7898 => 'ớ',
+  7900 => 'ờ',
+  7902 => 'ở',
+  7904 => 'ỡ',
+  7906 => 'ợ',
+  7908 => 'ụ',
+  7910 => 'ủ',
+  7912 => 'ứ',
+  7914 => 'ừ',
+  7916 => 'ử',
+  7918 => 'ữ',
+  7920 => 'ự',
+  7922 => 'ỳ',
+  7924 => 'ỵ',
+  7926 => 'ỷ',
+  7928 => 'ỹ',
+  7930 => 'ỻ',
+  7932 => 'ỽ',
+  7934 => 'ỿ',
+  7944 => 'ἀ',
+  7945 => 'ἁ',
+  7946 => 'ἂ',
+  7947 => 'ἃ',
+  7948 => 'ἄ',
+  7949 => 'ἅ',
+  7950 => 'ἆ',
+  7951 => 'ἇ',
+  7960 => 'ἐ',
+  7961 => 'ἑ',
+  7962 => 'ἒ',
+  7963 => 'ἓ',
+  7964 => 'ἔ',
+  7965 => 'ἕ',
+  7976 => 'ἠ',
+  7977 => 'ἡ',
+  7978 => 'ἢ',
+  7979 => 'ἣ',
+  7980 => 'ἤ',
+  7981 => 'ἥ',
+  7982 => 'ἦ',
+  7983 => 'ἧ',
+  7992 => 'ἰ',
+  7993 => 'ἱ',
+  7994 => 'ἲ',
+  7995 => 'ἳ',
+  7996 => 'ἴ',
+  7997 => 'ἵ',
+  7998 => 'ἶ',
+  7999 => 'ἷ',
+  8008 => 'ὀ',
+  8009 => 'ὁ',
+  8010 => 'ὂ',
+  8011 => 'ὃ',
+  8012 => 'ὄ',
+  8013 => 'ὅ',
+  8025 => 'ὑ',
+  8027 => 'ὓ',
+  8029 => 'ὕ',
+  8031 => 'ὗ',
+  8040 => 'ὠ',
+  8041 => 'ὡ',
+  8042 => 'ὢ',
+  8043 => 'ὣ',
+  8044 => 'ὤ',
+  8045 => 'ὥ',
+  8046 => 'ὦ',
+  8047 => 'ὧ',
+  8049 => 'ά',
+  8051 => 'έ',
+  8053 => 'ή',
+  8055 => 'ί',
+  8057 => 'ό',
+  8059 => 'ύ',
+  8061 => 'ώ',
+  8064 => 'ἀι',
+  8065 => 'ἁι',
+  8066 => 'ἂι',
+  8067 => 'ἃι',
+  8068 => 'ἄι',
+  8069 => 'ἅι',
+  8070 => 'ἆι',
+  8071 => 'ἇι',
+  8072 => 'ἀι',
+  8073 => 'ἁι',
+  8074 => 'ἂι',
+  8075 => 'ἃι',
+  8076 => 'ἄι',
+  8077 => 'ἅι',
+  8078 => 'ἆι',
+  8079 => 'ἇι',
+  8080 => 'ἠι',
+  8081 => 'ἡι',
+  8082 => 'ἢι',
+  8083 => 'ἣι',
+  8084 => 'ἤι',
+  8085 => 'ἥι',
+  8086 => 'ἦι',
+  8087 => 'ἧι',
+  8088 => 'ἠι',
+  8089 => 'ἡι',
+  8090 => 'ἢι',
+  8091 => 'ἣι',
+  8092 => 'ἤι',
+  8093 => 'ἥι',
+  8094 => 'ἦι',
+  8095 => 'ἧι',
+  8096 => 'ὠι',
+  8097 => 'ὡι',
+  8098 => 'ὢι',
+  8099 => 'ὣι',
+  8100 => 'ὤι',
+  8101 => 'ὥι',
+  8102 => 'ὦι',
+  8103 => 'ὧι',
+  8104 => 'ὠι',
+  8105 => 'ὡι',
+  8106 => 'ὢι',
+  8107 => 'ὣι',
+  8108 => 'ὤι',
+  8109 => 'ὥι',
+  8110 => 'ὦι',
+  8111 => 'ὧι',
+  8114 => 'ὰι',
+  8115 => 'αι',
+  8116 => 'άι',
+  8119 => 'ᾶι',
+  8120 => 'ᾰ',
+  8121 => 'ᾱ',
+  8122 => 'ὰ',
+  8123 => 'ά',
+  8124 => 'αι',
+  8126 => 'ι',
+  8130 => 'ὴι',
+  8131 => 'ηι',
+  8132 => 'ήι',
+  8135 => 'ῆι',
+  8136 => 'ὲ',
+  8137 => 'έ',
+  8138 => 'ὴ',
+  8139 => 'ή',
+  8140 => 'ηι',
+  8147 => 'ΐ',
+  8152 => 'ῐ',
+  8153 => 'ῑ',
+  8154 => 'ὶ',
+  8155 => 'ί',
+  8163 => 'ΰ',
+  8168 => 'ῠ',
+  8169 => 'ῡ',
+  8170 => 'ὺ',
+  8171 => 'ύ',
+  8172 => 'ῥ',
+  8178 => 'ὼι',
+  8179 => 'ωι',
+  8180 => 'ώι',
+  8183 => 'ῶι',
+  8184 => 'ὸ',
+  8185 => 'ό',
+  8186 => 'ὼ',
+  8187 => 'ώ',
+  8188 => 'ωι',
+  8209 => '‐',
+  8243 => '′′',
+  8244 => '′′′',
+  8246 => '‵‵',
+  8247 => '‵‵‵',
+  8279 => '′′′′',
+  8304 => '0',
+  8305 => 'i',
+  8308 => '4',
+  8309 => '5',
+  8310 => '6',
+  8311 => '7',
+  8312 => '8',
+  8313 => '9',
+  8315 => '−',
+  8319 => 'n',
+  8320 => '0',
+  8321 => '1',
+  8322 => '2',
+  8323 => '3',
+  8324 => '4',
+  8325 => '5',
+  8326 => '6',
+  8327 => '7',
+  8328 => '8',
+  8329 => '9',
+  8331 => '−',
+  8336 => 'a',
+  8337 => 'e',
+  8338 => 'o',
+  8339 => 'x',
+  8340 => 'ə',
+  8341 => 'h',
+  8342 => 'k',
+  8343 => 'l',
+  8344 => 'm',
+  8345 => 'n',
+  8346 => 'p',
+  8347 => 's',
+  8348 => 't',
+  8360 => 'rs',
+  8450 => 'c',
+  8451 => '°c',
+  8455 => 'ɛ',
+  8457 => '°f',
+  8458 => 'g',
+  8459 => 'h',
+  8460 => 'h',
+  8461 => 'h',
+  8462 => 'h',
+  8463 => 'ħ',
+  8464 => 'i',
+  8465 => 'i',
+  8466 => 'l',
+  8467 => 'l',
+  8469 => 'n',
+  8470 => 'no',
+  8473 => 'p',
+  8474 => 'q',
+  8475 => 'r',
+  8476 => 'r',
+  8477 => 'r',
+  8480 => 'sm',
+  8481 => 'tel',
+  8482 => 'tm',
+  8484 => 'z',
+  8486 => 'ω',
+  8488 => 'z',
+  8490 => 'k',
+  8491 => 'å',
+  8492 => 'b',
+  8493 => 'c',
+  8495 => 'e',
+  8496 => 'e',
+  8497 => 'f',
+  8499 => 'm',
+  8500 => 'o',
+  8501 => 'א',
+  8502 => 'ב',
+  8503 => 'ג',
+  8504 => 'ד',
+  8505 => 'i',
+  8507 => 'fax',
+  8508 => 'π',
+  8509 => 'γ',
+  8510 => 'γ',
+  8511 => 'π',
+  8512 => '∑',
+  8517 => 'd',
+  8518 => 'd',
+  8519 => 'e',
+  8520 => 'i',
+  8521 => 'j',
+  8528 => '1⁄7',
+  8529 => '1⁄9',
+  8530 => '1⁄10',
+  8531 => '1⁄3',
+  8532 => '2⁄3',
+  8533 => '1⁄5',
+  8534 => '2⁄5',
+  8535 => '3⁄5',
+  8536 => '4⁄5',
+  8537 => '1⁄6',
+  8538 => '5⁄6',
+  8539 => '1⁄8',
+  8540 => '3⁄8',
+  8541 => '5⁄8',
+  8542 => '7⁄8',
+  8543 => '1⁄',
+  8544 => 'i',
+  8545 => 'ii',
+  8546 => 'iii',
+  8547 => 'iv',
+  8548 => 'v',
+  8549 => 'vi',
+  8550 => 'vii',
+  8551 => 'viii',
+  8552 => 'ix',
+  8553 => 'x',
+  8554 => 'xi',
+  8555 => 'xii',
+  8556 => 'l',
+  8557 => 'c',
+  8558 => 'd',
+  8559 => 'm',
+  8560 => 'i',
+  8561 => 'ii',
+  8562 => 'iii',
+  8563 => 'iv',
+  8564 => 'v',
+  8565 => 'vi',
+  8566 => 'vii',
+  8567 => 'viii',
+  8568 => 'ix',
+  8569 => 'x',
+  8570 => 'xi',
+  8571 => 'xii',
+  8572 => 'l',
+  8573 => 'c',
+  8574 => 'd',
+  8575 => 'm',
+  8585 => '0⁄3',
+  8748 => '∫∫',
+  8749 => '∫∫∫',
+  8751 => '∮∮',
+  8752 => '∮∮∮',
+  9001 => '〈',
+  9002 => '〉',
+  9312 => '1',
+  9313 => '2',
+  9314 => '3',
+  9315 => '4',
+  9316 => '5',
+  9317 => '6',
+  9318 => '7',
+  9319 => '8',
+  9320 => '9',
+  9321 => '10',
+  9322 => '11',
+  9323 => '12',
+  9324 => '13',
+  9325 => '14',
+  9326 => '15',
+  9327 => '16',
+  9328 => '17',
+  9329 => '18',
+  9330 => '19',
+  9331 => '20',
+  9398 => 'a',
+  9399 => 'b',
+  9400 => 'c',
+  9401 => 'd',
+  9402 => 'e',
+  9403 => 'f',
+  9404 => 'g',
+  9405 => 'h',
+  9406 => 'i',
+  9407 => 'j',
+  9408 => 'k',
+  9409 => 'l',
+  9410 => 'm',
+  9411 => 'n',
+  9412 => 'o',
+  9413 => 'p',
+  9414 => 'q',
+  9415 => 'r',
+  9416 => 's',
+  9417 => 't',
+  9418 => 'u',
+  9419 => 'v',
+  9420 => 'w',
+  9421 => 'x',
+  9422 => 'y',
+  9423 => 'z',
+  9424 => 'a',
+  9425 => 'b',
+  9426 => 'c',
+  9427 => 'd',
+  9428 => 'e',
+  9429 => 'f',
+  9430 => 'g',
+  9431 => 'h',
+  9432 => 'i',
+  9433 => 'j',
+  9434 => 'k',
+  9435 => 'l',
+  9436 => 'm',
+  9437 => 'n',
+  9438 => 'o',
+  9439 => 'p',
+  9440 => 'q',
+  9441 => 'r',
+  9442 => 's',
+  9443 => 't',
+  9444 => 'u',
+  9445 => 'v',
+  9446 => 'w',
+  9447 => 'x',
+  9448 => 'y',
+  9449 => 'z',
+  9450 => '0',
+  10764 => '∫∫∫∫',
+  10972 => '⫝̸',
+  11264 => 'ⰰ',
+  11265 => 'ⰱ',
+  11266 => 'ⰲ',
+  11267 => 'ⰳ',
+  11268 => 'ⰴ',
+  11269 => 'ⰵ',
+  11270 => 'ⰶ',
+  11271 => 'ⰷ',
+  11272 => 'ⰸ',
+  11273 => 'ⰹ',
+  11274 => 'ⰺ',
+  11275 => 'ⰻ',
+  11276 => 'ⰼ',
+  11277 => 'ⰽ',
+  11278 => 'ⰾ',
+  11279 => 'ⰿ',
+  11280 => 'ⱀ',
+  11281 => 'ⱁ',
+  11282 => 'ⱂ',
+  11283 => 'ⱃ',
+  11284 => 'ⱄ',
+  11285 => 'ⱅ',
+  11286 => 'ⱆ',
+  11287 => 'ⱇ',
+  11288 => 'ⱈ',
+  11289 => 'ⱉ',
+  11290 => 'ⱊ',
+  11291 => 'ⱋ',
+  11292 => 'ⱌ',
+  11293 => 'ⱍ',
+  11294 => 'ⱎ',
+  11295 => 'ⱏ',
+  11296 => 'ⱐ',
+  11297 => 'ⱑ',
+  11298 => 'ⱒ',
+  11299 => 'ⱓ',
+  11300 => 'ⱔ',
+  11301 => 'ⱕ',
+  11302 => 'ⱖ',
+  11303 => 'ⱗ',
+  11304 => 'ⱘ',
+  11305 => 'ⱙ',
+  11306 => 'ⱚ',
+  11307 => 'ⱛ',
+  11308 => 'ⱜ',
+  11309 => 'ⱝ',
+  11310 => 'ⱞ',
+  11360 => 'ⱡ',
+  11362 => 'ɫ',
+  11363 => 'ᵽ',
+  11364 => 'ɽ',
+  11367 => 'ⱨ',
+  11369 => 'ⱪ',
+  11371 => 'ⱬ',
+  11373 => 'ɑ',
+  11374 => 'ɱ',
+  11375 => 'ɐ',
+  11376 => 'ɒ',
+  11378 => 'ⱳ',
+  11381 => 'ⱶ',
+  11388 => 'j',
+  11389 => 'v',
+  11390 => 'ȿ',
+  11391 => 'ɀ',
+  11392 => 'ⲁ',
+  11394 => 'ⲃ',
+  11396 => 'ⲅ',
+  11398 => 'ⲇ',
+  11400 => 'ⲉ',
+  11402 => 'ⲋ',
+  11404 => 'ⲍ',
+  11406 => 'ⲏ',
+  11408 => 'ⲑ',
+  11410 => 'ⲓ',
+  11412 => 'ⲕ',
+  11414 => 'ⲗ',
+  11416 => 'ⲙ',
+  11418 => 'ⲛ',
+  11420 => 'ⲝ',
+  11422 => 'ⲟ',
+  11424 => 'ⲡ',
+  11426 => 'ⲣ',
+  11428 => 'ⲥ',
+  11430 => 'ⲧ',
+  11432 => 'ⲩ',
+  11434 => 'ⲫ',
+  11436 => 'ⲭ',
+  11438 => 'ⲯ',
+  11440 => 'ⲱ',
+  11442 => 'ⲳ',
+  11444 => 'ⲵ',
+  11446 => 'ⲷ',
+  11448 => 'ⲹ',
+  11450 => 'ⲻ',
+  11452 => 'ⲽ',
+  11454 => 'ⲿ',
+  11456 => 'ⳁ',
+  11458 => 'ⳃ',
+  11460 => 'ⳅ',
+  11462 => 'ⳇ',
+  11464 => 'ⳉ',
+  11466 => 'ⳋ',
+  11468 => 'ⳍ',
+  11470 => 'ⳏ',
+  11472 => 'ⳑ',
+  11474 => 'ⳓ',
+  11476 => 'ⳕ',
+  11478 => 'ⳗ',
+  11480 => 'ⳙ',
+  11482 => 'ⳛ',
+  11484 => 'ⳝ',
+  11486 => 'ⳟ',
+  11488 => 'ⳡ',
+  11490 => 'ⳣ',
+  11499 => 'ⳬ',
+  11501 => 'ⳮ',
+  11506 => 'ⳳ',
+  11631 => 'ⵡ',
+  11935 => '母',
+  12019 => '龟',
+  12032 => '一',
+  12033 => '丨',
+  12034 => '丶',
+  12035 => '丿',
+  12036 => '乙',
+  12037 => '亅',
+  12038 => '二',
+  12039 => '亠',
+  12040 => '人',
+  12041 => '儿',
+  12042 => '入',
+  12043 => '八',
+  12044 => '冂',
+  12045 => '冖',
+  12046 => '冫',
+  12047 => '几',
+  12048 => '凵',
+  12049 => '刀',
+  12050 => '力',
+  12051 => '勹',
+  12052 => '匕',
+  12053 => '匚',
+  12054 => '匸',
+  12055 => '十',
+  12056 => '卜',
+  12057 => '卩',
+  12058 => '厂',
+  12059 => '厶',
+  12060 => '又',
+  12061 => '口',
+  12062 => '囗',
+  12063 => '土',
+  12064 => '士',
+  12065 => '夂',
+  12066 => '夊',
+  12067 => '夕',
+  12068 => '大',
+  12069 => '女',
+  12070 => '子',
+  12071 => '宀',
+  12072 => '寸',
+  12073 => '小',
+  12074 => '尢',
+  12075 => '尸',
+  12076 => '屮',
+  12077 => '山',
+  12078 => '巛',
+  12079 => '工',
+  12080 => '己',
+  12081 => '巾',
+  12082 => '干',
+  12083 => '幺',
+  12084 => '广',
+  12085 => '廴',
+  12086 => '廾',
+  12087 => '弋',
+  12088 => '弓',
+  12089 => '彐',
+  12090 => '彡',
+  12091 => '彳',
+  12092 => '心',
+  12093 => '戈',
+  12094 => '戶',
+  12095 => '手',
+  12096 => '支',
+  12097 => '攴',
+  12098 => '文',
+  12099 => '斗',
+  12100 => '斤',
+  12101 => '方',
+  12102 => '无',
+  12103 => '日',
+  12104 => '曰',
+  12105 => '月',
+  12106 => '木',
+  12107 => '欠',
+  12108 => '止',
+  12109 => '歹',
+  12110 => '殳',
+  12111 => '毋',
+  12112 => '比',
+  12113 => '毛',
+  12114 => '氏',
+  12115 => '气',
+  12116 => '水',
+  12117 => '火',
+  12118 => '爪',
+  12119 => '父',
+  12120 => '爻',
+  12121 => '爿',
+  12122 => '片',
+  12123 => '牙',
+  12124 => '牛',
+  12125 => '犬',
+  12126 => '玄',
+  12127 => '玉',
+  12128 => '瓜',
+  12129 => '瓦',
+  12130 => '甘',
+  12131 => '生',
+  12132 => '用',
+  12133 => '田',
+  12134 => '疋',
+  12135 => '疒',
+  12136 => '癶',
+  12137 => '白',
+  12138 => '皮',
+  12139 => '皿',
+  12140 => '目',
+  12141 => '矛',
+  12142 => '矢',
+  12143 => '石',
+  12144 => '示',
+  12145 => '禸',
+  12146 => '禾',
+  12147 => '穴',
+  12148 => '立',
+  12149 => '竹',
+  12150 => '米',
+  12151 => '糸',
+  12152 => '缶',
+  12153 => '网',
+  12154 => '羊',
+  12155 => '羽',
+  12156 => '老',
+  12157 => '而',
+  12158 => '耒',
+  12159 => '耳',
+  12160 => '聿',
+  12161 => '肉',
+  12162 => '臣',
+  12163 => '自',
+  12164 => '至',
+  12165 => '臼',
+  12166 => '舌',
+  12167 => '舛',
+  12168 => '舟',
+  12169 => '艮',
+  12170 => '色',
+  12171 => '艸',
+  12172 => '虍',
+  12173 => '虫',
+  12174 => '血',
+  12175 => '行',
+  12176 => '衣',
+  12177 => '襾',
+  12178 => '見',
+  12179 => '角',
+  12180 => '言',
+  12181 => '谷',
+  12182 => '豆',
+  12183 => '豕',
+  12184 => '豸',
+  12185 => '貝',
+  12186 => '赤',
+  12187 => '走',
+  12188 => '足',
+  12189 => '身',
+  12190 => '車',
+  12191 => '辛',
+  12192 => '辰',
+  12193 => '辵',
+  12194 => '邑',
+  12195 => '酉',
+  12196 => '釆',
+  12197 => '里',
+  12198 => '金',
+  12199 => '長',
+  12200 => '門',
+  12201 => '阜',
+  12202 => '隶',
+  12203 => '隹',
+  12204 => '雨',
+  12205 => '靑',
+  12206 => '非',
+  12207 => '面',
+  12208 => '革',
+  12209 => '韋',
+  12210 => '韭',
+  12211 => '音',
+  12212 => '頁',
+  12213 => '風',
+  12214 => '飛',
+  12215 => '食',
+  12216 => '首',
+  12217 => '香',
+  12218 => '馬',
+  12219 => '骨',
+  12220 => '高',
+  12221 => '髟',
+  12222 => '鬥',
+  12223 => '鬯',
+  12224 => '鬲',
+  12225 => '鬼',
+  12226 => '魚',
+  12227 => '鳥',
+  12228 => '鹵',
+  12229 => '鹿',
+  12230 => '麥',
+  12231 => '麻',
+  12232 => '黃',
+  12233 => '黍',
+  12234 => '黑',
+  12235 => '黹',
+  12236 => '黽',
+  12237 => '鼎',
+  12238 => '鼓',
+  12239 => '鼠',
+  12240 => '鼻',
+  12241 => '齊',
+  12242 => '齒',
+  12243 => '龍',
+  12244 => '龜',
+  12245 => '龠',
+  12290 => '.',
+  12342 => '〒',
+  12344 => '十',
+  12345 => '卄',
+  12346 => '卅',
+  12447 => 'より',
+  12543 => 'コト',
+  12593 => 'ᄀ',
+  12594 => 'ᄁ',
+  12595 => 'ᆪ',
+  12596 => 'ᄂ',
+  12597 => 'ᆬ',
+  12598 => 'ᆭ',
+  12599 => 'ᄃ',
+  12600 => 'ᄄ',
+  12601 => 'ᄅ',
+  12602 => 'ᆰ',
+  12603 => 'ᆱ',
+  12604 => 'ᆲ',
+  12605 => 'ᆳ',
+  12606 => 'ᆴ',
+  12607 => 'ᆵ',
+  12608 => 'ᄚ',
+  12609 => 'ᄆ',
+  12610 => 'ᄇ',
+  12611 => 'ᄈ',
+  12612 => 'ᄡ',
+  12613 => 'ᄉ',
+  12614 => 'ᄊ',
+  12615 => 'ᄋ',
+  12616 => 'ᄌ',
+  12617 => 'ᄍ',
+  12618 => 'ᄎ',
+  12619 => 'ᄏ',
+  12620 => 'ᄐ',
+  12621 => 'ᄑ',
+  12622 => 'ᄒ',
+  12623 => 'ᅡ',
+  12624 => 'ᅢ',
+  12625 => 'ᅣ',
+  12626 => 'ᅤ',
+  12627 => 'ᅥ',
+  12628 => 'ᅦ',
+  12629 => 'ᅧ',
+  12630 => 'ᅨ',
+  12631 => 'ᅩ',
+  12632 => 'ᅪ',
+  12633 => 'ᅫ',
+  12634 => 'ᅬ',
+  12635 => 'ᅭ',
+  12636 => 'ᅮ',
+  12637 => 'ᅯ',
+  12638 => 'ᅰ',
+  12639 => 'ᅱ',
+  12640 => 'ᅲ',
+  12641 => 'ᅳ',
+  12642 => 'ᅴ',
+  12643 => 'ᅵ',
+  12645 => 'ᄔ',
+  12646 => 'ᄕ',
+  12647 => 'ᇇ',
+  12648 => 'ᇈ',
+  12649 => 'ᇌ',
+  12650 => 'ᇎ',
+  12651 => 'ᇓ',
+  12652 => 'ᇗ',
+  12653 => 'ᇙ',
+  12654 => 'ᄜ',
+  12655 => 'ᇝ',
+  12656 => 'ᇟ',
+  12657 => 'ᄝ',
+  12658 => 'ᄞ',
+  12659 => 'ᄠ',
+  12660 => 'ᄢ',
+  12661 => 'ᄣ',
+  12662 => 'ᄧ',
+  12663 => 'ᄩ',
+  12664 => 'ᄫ',
+  12665 => 'ᄬ',
+  12666 => 'ᄭ',
+  12667 => 'ᄮ',
+  12668 => 'ᄯ',
+  12669 => 'ᄲ',
+  12670 => 'ᄶ',
+  12671 => 'ᅀ',
+  12672 => 'ᅇ',
+  12673 => 'ᅌ',
+  12674 => 'ᇱ',
+  12675 => 'ᇲ',
+  12676 => 'ᅗ',
+  12677 => 'ᅘ',
+  12678 => 'ᅙ',
+  12679 => 'ᆄ',
+  12680 => 'ᆅ',
+  12681 => 'ᆈ',
+  12682 => 'ᆑ',
+  12683 => 'ᆒ',
+  12684 => 'ᆔ',
+  12685 => 'ᆞ',
+  12686 => 'ᆡ',
+  12690 => '一',
+  12691 => '二',
+  12692 => '三',
+  12693 => '四',
+  12694 => '上',
+  12695 => '中',
+  12696 => '下',
+  12697 => '甲',
+  12698 => '乙',
+  12699 => '丙',
+  12700 => '丁',
+  12701 => '天',
+  12702 => '地',
+  12703 => '人',
+  12868 => '問',
+  12869 => '幼',
+  12870 => '文',
+  12871 => '箏',
+  12880 => 'pte',
+  12881 => '21',
+  12882 => '22',
+  12883 => '23',
+  12884 => '24',
+  12885 => '25',
+  12886 => '26',
+  12887 => '27',
+  12888 => '28',
+  12889 => '29',
+  12890 => '30',
+  12891 => '31',
+  12892 => '32',
+  12893 => '33',
+  12894 => '34',
+  12895 => '35',
+  12896 => 'ᄀ',
+  12897 => 'ᄂ',
+  12898 => 'ᄃ',
+  12899 => 'ᄅ',
+  12900 => 'ᄆ',
+  12901 => 'ᄇ',
+  12902 => 'ᄉ',
+  12903 => 'ᄋ',
+  12904 => 'ᄌ',
+  12905 => 'ᄎ',
+  12906 => 'ᄏ',
+  12907 => 'ᄐ',
+  12908 => 'ᄑ',
+  12909 => 'ᄒ',
+  12910 => '가',
+  12911 => '나',
+  12912 => '다',
+  12913 => '라',
+  12914 => '마',
+  12915 => '바',
+  12916 => '사',
+  12917 => '아',
+  12918 => '자',
+  12919 => '차',
+  12920 => '카',
+  12921 => '타',
+  12922 => '파',
+  12923 => '하',
+  12924 => '참고',
+  12925 => '주의',
+  12926 => '우',
+  12928 => '一',
+  12929 => '二',
+  12930 => '三',
+  12931 => '四',
+  12932 => '五',
+  12933 => '六',
+  12934 => '七',
+  12935 => '八',
+  12936 => '九',
+  12937 => '十',
+  12938 => '月',
+  12939 => '火',
+  12940 => '水',
+  12941 => '木',
+  12942 => '金',
+  12943 => '土',
+  12944 => '日',
+  12945 => '株',
+  12946 => '有',
+  12947 => '社',
+  12948 => '名',
+  12949 => '特',
+  12950 => '財',
+  12951 => '祝',
+  12952 => '労',
+  12953 => '秘',
+  12954 => '男',
+  12955 => '女',
+  12956 => '適',
+  12957 => '優',
+  12958 => '印',
+  12959 => '注',
+  12960 => '項',
+  12961 => '休',
+  12962 => '写',
+  12963 => '正',
+  12964 => '上',
+  12965 => '中',
+  12966 => '下',
+  12967 => '左',
+  12968 => '右',
+  12969 => '医',
+  12970 => '宗',
+  12971 => '学',
+  12972 => '監',
+  12973 => '企',
+  12974 => '資',
+  12975 => '協',
+  12976 => '夜',
+  12977 => '36',
+  12978 => '37',
+  12979 => '38',
+  12980 => '39',
+  12981 => '40',
+  12982 => '41',
+  12983 => '42',
+  12984 => '43',
+  12985 => '44',
+  12986 => '45',
+  12987 => '46',
+  12988 => '47',
+  12989 => '48',
+  12990 => '49',
+  12991 => '50',
+  12992 => '1月',
+  12993 => '2月',
+  12994 => '3月',
+  12995 => '4月',
+  12996 => '5月',
+  12997 => '6月',
+  12998 => '7月',
+  12999 => '8月',
+  13000 => '9月',
+  13001 => '10月',
+  13002 => '11月',
+  13003 => '12月',
+  13004 => 'hg',
+  13005 => 'erg',
+  13006 => 'ev',
+  13007 => 'ltd',
+  13008 => 'ア',
+  13009 => 'イ',
+  13010 => 'ウ',
+  13011 => 'エ',
+  13012 => 'オ',
+  13013 => 'カ',
+  13014 => 'キ',
+  13015 => 'ク',
+  13016 => 'ケ',
+  13017 => 'コ',
+  13018 => 'サ',
+  13019 => 'シ',
+  13020 => 'ス',
+  13021 => 'セ',
+  13022 => 'ソ',
+  13023 => 'タ',
+  13024 => 'チ',
+  13025 => 'ツ',
+  13026 => 'テ',
+  13027 => 'ト',
+  13028 => 'ナ',
+  13029 => 'ニ',
+  13030 => 'ヌ',
+  13031 => 'ネ',
+  13032 => 'ノ',
+  13033 => 'ハ',
+  13034 => 'ヒ',
+  13035 => 'フ',
+  13036 => 'ヘ',
+  13037 => 'ホ',
+  13038 => 'マ',
+  13039 => 'ミ',
+  13040 => 'ム',
+  13041 => 'メ',
+  13042 => 'モ',
+  13043 => 'ヤ',
+  13044 => 'ユ',
+  13045 => 'ヨ',
+  13046 => 'ラ',
+  13047 => 'リ',
+  13048 => 'ル',
+  13049 => 'レ',
+  13050 => 'ロ',
+  13051 => 'ワ',
+  13052 => 'ヰ',
+  13053 => 'ヱ',
+  13054 => 'ヲ',
+  13055 => '令和',
+  13056 => 'アパート',
+  13057 => 'アルファ',
+  13058 => 'アンペア',
+  13059 => 'アール',
+  13060 => 'イニング',
+  13061 => 'インチ',
+  13062 => 'ウォン',
+  13063 => 'エスクード',
+  13064 => 'エーカー',
+  13065 => 'オンス',
+  13066 => 'オーム',
+  13067 => 'カイリ',
+  13068 => 'カラット',
+  13069 => 'カロリー',
+  13070 => 'ガロン',
+  13071 => 'ガンマ',
+  13072 => 'ギガ',
+  13073 => 'ギニー',
+  13074 => 'キュリー',
+  13075 => 'ギルダー',
+  13076 => 'キロ',
+  13077 => 'キログラム',
+  13078 => 'キロメートル',
+  13079 => 'キロワット',
+  13080 => 'グラム',
+  13081 => 'グラムトン',
+  13082 => 'クルゼイロ',
+  13083 => 'クローネ',
+  13084 => 'ケース',
+  13085 => 'コルナ',
+  13086 => 'コーポ',
+  13087 => 'サイクル',
+  13088 => 'サンチーム',
+  13089 => 'シリング',
+  13090 => 'センチ',
+  13091 => 'セント',
+  13092 => 'ダース',
+  13093 => 'デシ',
+  13094 => 'ドル',
+  13095 => 'トン',
+  13096 => 'ナノ',
+  13097 => 'ノット',
+  13098 => 'ハイツ',
+  13099 => 'パーセント',
+  13100 => 'パーツ',
+  13101 => 'バーレル',
+  13102 => 'ピアストル',
+  13103 => 'ピクル',
+  13104 => 'ピコ',
+  13105 => 'ビル',
+  13106 => 'ファラッド',
+  13107 => 'フィート',
+  13108 => 'ブッシェル',
+  13109 => 'フラン',
+  13110 => 'ヘクタール',
+  13111 => 'ペソ',
+  13112 => 'ペニヒ',
+  13113 => 'ヘルツ',
+  13114 => 'ペンス',
+  13115 => 'ページ',
+  13116 => 'ベータ',
+  13117 => 'ポイント',
+  13118 => 'ボルト',
+  13119 => 'ホン',
+  13120 => 'ポンド',
+  13121 => 'ホール',
+  13122 => 'ホーン',
+  13123 => 'マイクロ',
+  13124 => 'マイル',
+  13125 => 'マッハ',
+  13126 => 'マルク',
+  13127 => 'マンション',
+  13128 => 'ミクロン',
+  13129 => 'ミリ',
+  13130 => 'ミリバール',
+  13131 => 'メガ',
+  13132 => 'メガトン',
+  13133 => 'メートル',
+  13134 => 'ヤード',
+  13135 => 'ヤール',
+  13136 => 'ユアン',
+  13137 => 'リットル',
+  13138 => 'リラ',
+  13139 => 'ルピー',
+  13140 => 'ルーブル',
+  13141 => 'レム',
+  13142 => 'レントゲン',
+  13143 => 'ワット',
+  13144 => '0点',
+  13145 => '1点',
+  13146 => '2点',
+  13147 => '3点',
+  13148 => '4点',
+  13149 => '5点',
+  13150 => '6点',
+  13151 => '7点',
+  13152 => '8点',
+  13153 => '9点',
+  13154 => '10点',
+  13155 => '11点',
+  13156 => '12点',
+  13157 => '13点',
+  13158 => '14点',
+  13159 => '15点',
+  13160 => '16点',
+  13161 => '17点',
+  13162 => '18点',
+  13163 => '19点',
+  13164 => '20点',
+  13165 => '21点',
+  13166 => '22点',
+  13167 => '23点',
+  13168 => '24点',
+  13169 => 'hpa',
+  13170 => 'da',
+  13171 => 'au',
+  13172 => 'bar',
+  13173 => 'ov',
+  13174 => 'pc',
+  13175 => 'dm',
+  13176 => 'dm2',
+  13177 => 'dm3',
+  13178 => 'iu',
+  13179 => '平成',
+  13180 => '昭和',
+  13181 => '大正',
+  13182 => '明治',
+  13183 => '株式会社',
+  13184 => 'pa',
+  13185 => 'na',
+  13186 => 'μa',
+  13187 => 'ma',
+  13188 => 'ka',
+  13189 => 'kb',
+  13190 => 'mb',
+  13191 => 'gb',
+  13192 => 'cal',
+  13193 => 'kcal',
+  13194 => 'pf',
+  13195 => 'nf',
+  13196 => 'μf',
+  13197 => 'μg',
+  13198 => 'mg',
+  13199 => 'kg',
+  13200 => 'hz',
+  13201 => 'khz',
+  13202 => 'mhz',
+  13203 => 'ghz',
+  13204 => 'thz',
+  13205 => 'μl',
+  13206 => 'ml',
+  13207 => 'dl',
+  13208 => 'kl',
+  13209 => 'fm',
+  13210 => 'nm',
+  13211 => 'μm',
+  13212 => 'mm',
+  13213 => 'cm',
+  13214 => 'km',
+  13215 => 'mm2',
+  13216 => 'cm2',
+  13217 => 'm2',
+  13218 => 'km2',
+  13219 => 'mm3',
+  13220 => 'cm3',
+  13221 => 'm3',
+  13222 => 'km3',
+  13223 => 'm∕s',
+  13224 => 'm∕s2',
+  13225 => 'pa',
+  13226 => 'kpa',
+  13227 => 'mpa',
+  13228 => 'gpa',
+  13229 => 'rad',
+  13230 => 'rad∕s',
+  13231 => 'rad∕s2',
+  13232 => 'ps',
+  13233 => 'ns',
+  13234 => 'μs',
+  13235 => 'ms',
+  13236 => 'pv',
+  13237 => 'nv',
+  13238 => 'μv',
+  13239 => 'mv',
+  13240 => 'kv',
+  13241 => 'mv',
+  13242 => 'pw',
+  13243 => 'nw',
+  13244 => 'μw',
+  13245 => 'mw',
+  13246 => 'kw',
+  13247 => 'mw',
+  13248 => 'kω',
+  13249 => 'mω',
+  13251 => 'bq',
+  13252 => 'cc',
+  13253 => 'cd',
+  13254 => 'c∕kg',
+  13256 => 'db',
+  13257 => 'gy',
+  13258 => 'ha',
+  13259 => 'hp',
+  13260 => 'in',
+  13261 => 'kk',
+  13262 => 'km',
+  13263 => 'kt',
+  13264 => 'lm',
+  13265 => 'ln',
+  13266 => 'log',
+  13267 => 'lx',
+  13268 => 'mb',
+  13269 => 'mil',
+  13270 => 'mol',
+  13271 => 'ph',
+  13273 => 'ppm',
+  13274 => 'pr',
+  13275 => 'sr',
+  13276 => 'sv',
+  13277 => 'wb',
+  13278 => 'v∕m',
+  13279 => 'a∕m',
+  13280 => '1日',
+  13281 => '2日',
+  13282 => '3日',
+  13283 => '4日',
+  13284 => '5日',
+  13285 => '6日',
+  13286 => '7日',
+  13287 => '8日',
+  13288 => '9日',
+  13289 => '10日',
+  13290 => '11日',
+  13291 => '12日',
+  13292 => '13日',
+  13293 => '14日',
+  13294 => '15日',
+  13295 => '16日',
+  13296 => '17日',
+  13297 => '18日',
+  13298 => '19日',
+  13299 => '20日',
+  13300 => '21日',
+  13301 => '22日',
+  13302 => '23日',
+  13303 => '24日',
+  13304 => '25日',
+  13305 => '26日',
+  13306 => '27日',
+  13307 => '28日',
+  13308 => '29日',
+  13309 => '30日',
+  13310 => '31日',
+  13311 => 'gal',
+  42560 => 'ꙁ',
+  42562 => 'ꙃ',
+  42564 => 'ꙅ',
+  42566 => 'ꙇ',
+  42568 => 'ꙉ',
+  42570 => 'ꙋ',
+  42572 => 'ꙍ',
+  42574 => 'ꙏ',
+  42576 => 'ꙑ',
+  42578 => 'ꙓ',
+  42580 => 'ꙕ',
+  42582 => 'ꙗ',
+  42584 => 'ꙙ',
+  42586 => 'ꙛ',
+  42588 => 'ꙝ',
+  42590 => 'ꙟ',
+  42592 => 'ꙡ',
+  42594 => 'ꙣ',
+  42596 => 'ꙥ',
+  42598 => 'ꙧ',
+  42600 => 'ꙩ',
+  42602 => 'ꙫ',
+  42604 => 'ꙭ',
+  42624 => 'ꚁ',
+  42626 => 'ꚃ',
+  42628 => 'ꚅ',
+  42630 => 'ꚇ',
+  42632 => 'ꚉ',
+  42634 => 'ꚋ',
+  42636 => 'ꚍ',
+  42638 => 'ꚏ',
+  42640 => 'ꚑ',
+  42642 => 'ꚓ',
+  42644 => 'ꚕ',
+  42646 => 'ꚗ',
+  42648 => 'ꚙ',
+  42650 => 'ꚛ',
+  42652 => 'ъ',
+  42653 => 'ь',
+  42786 => 'ꜣ',
+  42788 => 'ꜥ',
+  42790 => 'ꜧ',
+  42792 => 'ꜩ',
+  42794 => 'ꜫ',
+  42796 => 'ꜭ',
+  42798 => 'ꜯ',
+  42802 => 'ꜳ',
+  42804 => 'ꜵ',
+  42806 => 'ꜷ',
+  42808 => 'ꜹ',
+  42810 => 'ꜻ',
+  42812 => 'ꜽ',
+  42814 => 'ꜿ',
+  42816 => 'ꝁ',
+  42818 => 'ꝃ',
+  42820 => 'ꝅ',
+  42822 => 'ꝇ',
+  42824 => 'ꝉ',
+  42826 => 'ꝋ',
+  42828 => 'ꝍ',
+  42830 => 'ꝏ',
+  42832 => 'ꝑ',
+  42834 => 'ꝓ',
+  42836 => 'ꝕ',
+  42838 => 'ꝗ',
+  42840 => 'ꝙ',
+  42842 => 'ꝛ',
+  42844 => 'ꝝ',
+  42846 => 'ꝟ',
+  42848 => 'ꝡ',
+  42850 => 'ꝣ',
+  42852 => 'ꝥ',
+  42854 => 'ꝧ',
+  42856 => 'ꝩ',
+  42858 => 'ꝫ',
+  42860 => 'ꝭ',
+  42862 => 'ꝯ',
+  42864 => 'ꝯ',
+  42873 => 'ꝺ',
+  42875 => 'ꝼ',
+  42877 => 'ᵹ',
+  42878 => 'ꝿ',
+  42880 => 'ꞁ',
+  42882 => 'ꞃ',
+  42884 => 'ꞅ',
+  42886 => 'ꞇ',
+  42891 => 'ꞌ',
+  42893 => 'ɥ',
+  42896 => 'ꞑ',
+  42898 => 'ꞓ',
+  42902 => 'ꞗ',
+  42904 => 'ꞙ',
+  42906 => 'ꞛ',
+  42908 => 'ꞝ',
+  42910 => 'ꞟ',
+  42912 => 'ꞡ',
+  42914 => 'ꞣ',
+  42916 => 'ꞥ',
+  42918 => 'ꞧ',
+  42920 => 'ꞩ',
+  42922 => 'ɦ',
+  42923 => 'ɜ',
+  42924 => 'ɡ',
+  42925 => 'ɬ',
+  42926 => 'ɪ',
+  42928 => 'ʞ',
+  42929 => 'ʇ',
+  42930 => 'ʝ',
+  42931 => 'ꭓ',
+  42932 => 'ꞵ',
+  42934 => 'ꞷ',
+  42936 => 'ꞹ',
+  42938 => 'ꞻ',
+  42940 => 'ꞽ',
+  42942 => 'ꞿ',
+  42946 => 'ꟃ',
+  42948 => 'ꞔ',
+  42949 => 'ʂ',
+  42950 => 'ᶎ',
+  42951 => 'ꟈ',
+  42953 => 'ꟊ',
+  42997 => 'ꟶ',
+  43000 => 'ħ',
+  43001 => 'œ',
+  43868 => 'ꜧ',
+  43869 => 'ꬷ',
+  43870 => 'ɫ',
+  43871 => 'ꭒ',
+  43881 => 'ʍ',
+  43888 => 'Ꭰ',
+  43889 => 'Ꭱ',
+  43890 => 'Ꭲ',
+  43891 => 'Ꭳ',
+  43892 => 'Ꭴ',
+  43893 => 'Ꭵ',
+  43894 => 'Ꭶ',
+  43895 => 'Ꭷ',
+  43896 => 'Ꭸ',
+  43897 => 'Ꭹ',
+  43898 => 'Ꭺ',
+  43899 => 'Ꭻ',
+  43900 => 'Ꭼ',
+  43901 => 'Ꭽ',
+  43902 => 'Ꭾ',
+  43903 => 'Ꭿ',
+  43904 => 'Ꮀ',
+  43905 => 'Ꮁ',
+  43906 => 'Ꮂ',
+  43907 => 'Ꮃ',
+  43908 => 'Ꮄ',
+  43909 => 'Ꮅ',
+  43910 => 'Ꮆ',
+  43911 => 'Ꮇ',
+  43912 => 'Ꮈ',
+  43913 => 'Ꮉ',
+  43914 => 'Ꮊ',
+  43915 => 'Ꮋ',
+  43916 => 'Ꮌ',
+  43917 => 'Ꮍ',
+  43918 => 'Ꮎ',
+  43919 => 'Ꮏ',
+  43920 => 'Ꮐ',
+  43921 => 'Ꮑ',
+  43922 => 'Ꮒ',
+  43923 => 'Ꮓ',
+  43924 => 'Ꮔ',
+  43925 => 'Ꮕ',
+  43926 => 'Ꮖ',
+  43927 => 'Ꮗ',
+  43928 => 'Ꮘ',
+  43929 => 'Ꮙ',
+  43930 => 'Ꮚ',
+  43931 => 'Ꮛ',
+  43932 => 'Ꮜ',
+  43933 => 'Ꮝ',
+  43934 => 'Ꮞ',
+  43935 => 'Ꮟ',
+  43936 => 'Ꮠ',
+  43937 => 'Ꮡ',
+  43938 => 'Ꮢ',
+  43939 => 'Ꮣ',
+  43940 => 'Ꮤ',
+  43941 => 'Ꮥ',
+  43942 => 'Ꮦ',
+  43943 => 'Ꮧ',
+  43944 => 'Ꮨ',
+  43945 => 'Ꮩ',
+  43946 => 'Ꮪ',
+  43947 => 'Ꮫ',
+  43948 => 'Ꮬ',
+  43949 => 'Ꮭ',
+  43950 => 'Ꮮ',
+  43951 => 'Ꮯ',
+  43952 => 'Ꮰ',
+  43953 => 'Ꮱ',
+  43954 => 'Ꮲ',
+  43955 => 'Ꮳ',
+  43956 => 'Ꮴ',
+  43957 => 'Ꮵ',
+  43958 => 'Ꮶ',
+  43959 => 'Ꮷ',
+  43960 => 'Ꮸ',
+  43961 => 'Ꮹ',
+  43962 => 'Ꮺ',
+  43963 => 'Ꮻ',
+  43964 => 'Ꮼ',
+  43965 => 'Ꮽ',
+  43966 => 'Ꮾ',
+  43967 => 'Ꮿ',
+  63744 => '豈',
+  63745 => '更',
+  63746 => '車',
+  63747 => '賈',
+  63748 => '滑',
+  63749 => '串',
+  63750 => '句',
+  63751 => '龜',
+  63752 => '龜',
+  63753 => '契',
+  63754 => '金',
+  63755 => '喇',
+  63756 => '奈',
+  63757 => '懶',
+  63758 => '癩',
+  63759 => '羅',
+  63760 => '蘿',
+  63761 => '螺',
+  63762 => '裸',
+  63763 => '邏',
+  63764 => '樂',
+  63765 => '洛',
+  63766 => '烙',
+  63767 => '珞',
+  63768 => '落',
+  63769 => '酪',
+  63770 => '駱',
+  63771 => '亂',
+  63772 => '卵',
+  63773 => '欄',
+  63774 => '爛',
+  63775 => '蘭',
+  63776 => '鸞',
+  63777 => '嵐',
+  63778 => '濫',
+  63779 => '藍',
+  63780 => '襤',
+  63781 => '拉',
+  63782 => '臘',
+  63783 => '蠟',
+  63784 => '廊',
+  63785 => '朗',
+  63786 => '浪',
+  63787 => '狼',
+  63788 => '郎',
+  63789 => '來',
+  63790 => '冷',
+  63791 => '勞',
+  63792 => '擄',
+  63793 => '櫓',
+  63794 => '爐',
+  63795 => '盧',
+  63796 => '老',
+  63797 => '蘆',
+  63798 => '虜',
+  63799 => '路',
+  63800 => '露',
+  63801 => '魯',
+  63802 => '鷺',
+  63803 => '碌',
+  63804 => '祿',
+  63805 => '綠',
+  63806 => '菉',
+  63807 => '錄',
+  63808 => '鹿',
+  63809 => '論',
+  63810 => '壟',
+  63811 => '弄',
+  63812 => '籠',
+  63813 => '聾',
+  63814 => '牢',
+  63815 => '磊',
+  63816 => '賂',
+  63817 => '雷',
+  63818 => '壘',
+  63819 => '屢',
+  63820 => '樓',
+  63821 => '淚',
+  63822 => '漏',
+  63823 => '累',
+  63824 => '縷',
+  63825 => '陋',
+  63826 => '勒',
+  63827 => '肋',
+  63828 => '凜',
+  63829 => '凌',
+  63830 => '稜',
+  63831 => '綾',
+  63832 => '菱',
+  63833 => '陵',
+  63834 => '讀',
+  63835 => '拏',
+  63836 => '樂',
+  63837 => '諾',
+  63838 => '丹',
+  63839 => '寧',
+  63840 => '怒',
+  63841 => '率',
+  63842 => '異',
+  63843 => '北',
+  63844 => '磻',
+  63845 => '便',
+  63846 => '復',
+  63847 => '不',
+  63848 => '泌',
+  63849 => '數',
+  63850 => '索',
+  63851 => '參',
+  63852 => '塞',
+  63853 => '省',
+  63854 => '葉',
+  63855 => '說',
+  63856 => '殺',
+  63857 => '辰',
+  63858 => '沈',
+  63859 => '拾',
+  63860 => '若',
+  63861 => '掠',
+  63862 => '略',
+  63863 => '亮',
+  63864 => '兩',
+  63865 => '凉',
+  63866 => '梁',
+  63867 => '糧',
+  63868 => '良',
+  63869 => '諒',
+  63870 => '量',
+  63871 => '勵',
+  63872 => '呂',
+  63873 => '女',
+  63874 => '廬',
+  63875 => '旅',
+  63876 => '濾',
+  63877 => '礪',
+  63878 => '閭',
+  63879 => '驪',
+  63880 => '麗',
+  63881 => '黎',
+  63882 => '力',
+  63883 => '曆',
+  63884 => '歷',
+  63885 => '轢',
+  63886 => '年',
+  63887 => '憐',
+  63888 => '戀',
+  63889 => '撚',
+  63890 => '漣',
+  63891 => '煉',
+  63892 => '璉',
+  63893 => '秊',
+  63894 => '練',
+  63895 => '聯',
+  63896 => '輦',
+  63897 => '蓮',
+  63898 => '連',
+  63899 => '鍊',
+  63900 => '列',
+  63901 => '劣',
+  63902 => '咽',
+  63903 => '烈',
+  63904 => '裂',
+  63905 => '說',
+  63906 => '廉',
+  63907 => '念',
+  63908 => '捻',
+  63909 => '殮',
+  63910 => '簾',
+  63911 => '獵',
+  63912 => '令',
+  63913 => '囹',
+  63914 => '寧',
+  63915 => '嶺',
+  63916 => '怜',
+  63917 => '玲',
+  63918 => '瑩',
+  63919 => '羚',
+  63920 => '聆',
+  63921 => '鈴',
+  63922 => '零',
+  63923 => '靈',
+  63924 => '領',
+  63925 => '例',
+  63926 => '禮',
+  63927 => '醴',
+  63928 => '隸',
+  63929 => '惡',
+  63930 => '了',
+  63931 => '僚',
+  63932 => '寮',
+  63933 => '尿',
+  63934 => '料',
+  63935 => '樂',
+  63936 => '燎',
+  63937 => '療',
+  63938 => '蓼',
+  63939 => '遼',
+  63940 => '龍',
+  63941 => '暈',
+  63942 => '阮',
+  63943 => '劉',
+  63944 => '杻',
+  63945 => '柳',
+  63946 => '流',
+  63947 => '溜',
+  63948 => '琉',
+  63949 => '留',
+  63950 => '硫',
+  63951 => '紐',
+  63952 => '類',
+  63953 => '六',
+  63954 => '戮',
+  63955 => '陸',
+  63956 => '倫',
+  63957 => '崙',
+  63958 => '淪',
+  63959 => '輪',
+  63960 => '律',
+  63961 => '慄',
+  63962 => '栗',
+  63963 => '率',
+  63964 => '隆',
+  63965 => '利',
+  63966 => '吏',
+  63967 => '履',
+  63968 => '易',
+  63969 => '李',
+  63970 => '梨',
+  63971 => '泥',
+  63972 => '理',
+  63973 => '痢',
+  63974 => '罹',
+  63975 => '裏',
+  63976 => '裡',
+  63977 => '里',
+  63978 => '離',
+  63979 => '匿',
+  63980 => '溺',
+  63981 => '吝',
+  63982 => '燐',
+  63983 => '璘',
+  63984 => '藺',
+  63985 => '隣',
+  63986 => '鱗',
+  63987 => '麟',
+  63988 => '林',
+  63989 => '淋',
+  63990 => '臨',
+  63991 => '立',
+  63992 => '笠',
+  63993 => '粒',
+  63994 => '狀',
+  63995 => '炙',
+  63996 => '識',
+  63997 => '什',
+  63998 => '茶',
+  63999 => '刺',
+  64000 => '切',
+  64001 => '度',
+  64002 => '拓',
+  64003 => '糖',
+  64004 => '宅',
+  64005 => '洞',
+  64006 => '暴',
+  64007 => '輻',
+  64008 => '行',
+  64009 => '降',
+  64010 => '見',
+  64011 => '廓',
+  64012 => '兀',
+  64013 => '嗀',
+  64016 => '塚',
+  64018 => '晴',
+  64021 => '凞',
+  64022 => '猪',
+  64023 => '益',
+  64024 => '礼',
+  64025 => '神',
+  64026 => '祥',
+  64027 => '福',
+  64028 => '靖',
+  64029 => '精',
+  64030 => '羽',
+  64032 => '蘒',
+  64034 => '諸',
+  64037 => '逸',
+  64038 => '都',
+  64042 => '飯',
+  64043 => '飼',
+  64044 => '館',
+  64045 => '鶴',
+  64046 => '郞',
+  64047 => '隷',
+  64048 => '侮',
+  64049 => '僧',
+  64050 => '免',
+  64051 => '勉',
+  64052 => '勤',
+  64053 => '卑',
+  64054 => '喝',
+  64055 => '嘆',
+  64056 => '器',
+  64057 => '塀',
+  64058 => '墨',
+  64059 => '層',
+  64060 => '屮',
+  64061 => '悔',
+  64062 => '慨',
+  64063 => '憎',
+  64064 => '懲',
+  64065 => '敏',
+  64066 => '既',
+  64067 => '暑',
+  64068 => '梅',
+  64069 => '海',
+  64070 => '渚',
+  64071 => '漢',
+  64072 => '煮',
+  64073 => '爫',
+  64074 => '琢',
+  64075 => '碑',
+  64076 => '社',
+  64077 => '祉',
+  64078 => '祈',
+  64079 => '祐',
+  64080 => '祖',
+  64081 => '祝',
+  64082 => '禍',
+  64083 => '禎',
+  64084 => '穀',
+  64085 => '突',
+  64086 => '節',
+  64087 => '練',
+  64088 => '縉',
+  64089 => '繁',
+  64090 => '署',
+  64091 => '者',
+  64092 => '臭',
+  64093 => '艹',
+  64094 => '艹',
+  64095 => '著',
+  64096 => '褐',
+  64097 => '視',
+  64098 => '謁',
+  64099 => '謹',
+  64100 => '賓',
+  64101 => '贈',
+  64102 => '辶',
+  64103 => '逸',
+  64104 => '難',
+  64105 => '響',
+  64106 => '頻',
+  64107 => '恵',
+  64108 => '𤋮',
+  64109 => '舘',
+  64112 => '並',
+  64113 => '况',
+  64114 => '全',
+  64115 => '侀',
+  64116 => '充',
+  64117 => '冀',
+  64118 => '勇',
+  64119 => '勺',
+  64120 => '喝',
+  64121 => '啕',
+  64122 => '喙',
+  64123 => '嗢',
+  64124 => '塚',
+  64125 => '墳',
+  64126 => '奄',
+  64127 => '奔',
+  64128 => '婢',
+  64129 => '嬨',
+  64130 => '廒',
+  64131 => '廙',
+  64132 => '彩',
+  64133 => '徭',
+  64134 => '惘',
+  64135 => '慎',
+  64136 => '愈',
+  64137 => '憎',
+  64138 => '慠',
+  64139 => '懲',
+  64140 => '戴',
+  64141 => '揄',
+  64142 => '搜',
+  64143 => '摒',
+  64144 => '敖',
+  64145 => '晴',
+  64146 => '朗',
+  64147 => '望',
+  64148 => '杖',
+  64149 => '歹',
+  64150 => '殺',
+  64151 => '流',
+  64152 => '滛',
+  64153 => '滋',
+  64154 => '漢',
+  64155 => '瀞',
+  64156 => '煮',
+  64157 => '瞧',
+  64158 => '爵',
+  64159 => '犯',
+  64160 => '猪',
+  64161 => '瑱',
+  64162 => '甆',
+  64163 => '画',
+  64164 => '瘝',
+  64165 => '瘟',
+  64166 => '益',
+  64167 => '盛',
+  64168 => '直',
+  64169 => '睊',
+  64170 => '着',
+  64171 => '磌',
+  64172 => '窱',
+  64173 => '節',
+  64174 => '类',
+  64175 => '絛',
+  64176 => '練',
+  64177 => '缾',
+  64178 => '者',
+  64179 => '荒',
+  64180 => '華',
+  64181 => '蝹',
+  64182 => '襁',
+  64183 => '覆',
+  64184 => '視',
+  64185 => '調',
+  64186 => '諸',
+  64187 => '請',
+  64188 => '謁',
+  64189 => '諾',
+  64190 => '諭',
+  64191 => '謹',
+  64192 => '變',
+  64193 => '贈',
+  64194 => '輸',
+  64195 => '遲',
+  64196 => '醙',
+  64197 => '鉶',
+  64198 => '陼',
+  64199 => '難',
+  64200 => '靖',
+  64201 => '韛',
+  64202 => '響',
+  64203 => '頋',
+  64204 => '頻',
+  64205 => '鬒',
+  64206 => '龜',
+  64207 => '𢡊',
+  64208 => '𢡄',
+  64209 => '𣏕',
+  64210 => '㮝',
+  64211 => '䀘',
+  64212 => '䀹',
+  64213 => '𥉉',
+  64214 => '𥳐',
+  64215 => '𧻓',
+  64216 => '齃',
+  64217 => '龎',
+  64256 => 'ff',
+  64257 => 'fi',
+  64258 => 'fl',
+  64259 => 'ffi',
+  64260 => 'ffl',
+  64261 => 'st',
+  64262 => 'st',
+  64275 => 'մն',
+  64276 => 'մե',
+  64277 => 'մի',
+  64278 => 'վն',
+  64279 => 'մխ',
+  64285 => 'יִ',
+  64287 => 'ײַ',
+  64288 => 'ע',
+  64289 => 'א',
+  64290 => 'ד',
+  64291 => 'ה',
+  64292 => 'כ',
+  64293 => 'ל',
+  64294 => 'ם',
+  64295 => 'ר',
+  64296 => 'ת',
+  64298 => 'שׁ',
+  64299 => 'שׂ',
+  64300 => 'שּׁ',
+  64301 => 'שּׂ',
+  64302 => 'אַ',
+  64303 => 'אָ',
+  64304 => 'אּ',
+  64305 => 'בּ',
+  64306 => 'גּ',
+  64307 => 'דּ',
+  64308 => 'הּ',
+  64309 => 'וּ',
+  64310 => 'זּ',
+  64312 => 'טּ',
+  64313 => 'יּ',
+  64314 => 'ךּ',
+  64315 => 'כּ',
+  64316 => 'לּ',
+  64318 => 'מּ',
+  64320 => 'נּ',
+  64321 => 'סּ',
+  64323 => 'ףּ',
+  64324 => 'פּ',
+  64326 => 'צּ',
+  64327 => 'קּ',
+  64328 => 'רּ',
+  64329 => 'שּ',
+  64330 => 'תּ',
+  64331 => 'וֹ',
+  64332 => 'בֿ',
+  64333 => 'כֿ',
+  64334 => 'פֿ',
+  64335 => 'אל',
+  64336 => 'ٱ',
+  64337 => 'ٱ',
+  64338 => 'ٻ',
+  64339 => 'ٻ',
+  64340 => 'ٻ',
+  64341 => 'ٻ',
+  64342 => 'پ',
+  64343 => 'پ',
+  64344 => 'پ',
+  64345 => 'پ',
+  64346 => 'ڀ',
+  64347 => 'ڀ',
+  64348 => 'ڀ',
+  64349 => 'ڀ',
+  64350 => 'ٺ',
+  64351 => 'ٺ',
+  64352 => 'ٺ',
+  64353 => 'ٺ',
+  64354 => 'ٿ',
+  64355 => 'ٿ',
+  64356 => 'ٿ',
+  64357 => 'ٿ',
+  64358 => 'ٹ',
+  64359 => 'ٹ',
+  64360 => 'ٹ',
+  64361 => 'ٹ',
+  64362 => 'ڤ',
+  64363 => 'ڤ',
+  64364 => 'ڤ',
+  64365 => 'ڤ',
+  64366 => 'ڦ',
+  64367 => 'ڦ',
+  64368 => 'ڦ',
+  64369 => 'ڦ',
+  64370 => 'ڄ',
+  64371 => 'ڄ',
+  64372 => 'ڄ',
+  64373 => 'ڄ',
+  64374 => 'ڃ',
+  64375 => 'ڃ',
+  64376 => 'ڃ',
+  64377 => 'ڃ',
+  64378 => 'چ',
+  64379 => 'چ',
+  64380 => 'چ',
+  64381 => 'چ',
+  64382 => 'ڇ',
+  64383 => 'ڇ',
+  64384 => 'ڇ',
+  64385 => 'ڇ',
+  64386 => 'ڍ',
+  64387 => 'ڍ',
+  64388 => 'ڌ',
+  64389 => 'ڌ',
+  64390 => 'ڎ',
+  64391 => 'ڎ',
+  64392 => 'ڈ',
+  64393 => 'ڈ',
+  64394 => 'ژ',
+  64395 => 'ژ',
+  64396 => 'ڑ',
+  64397 => 'ڑ',
+  64398 => 'ک',
+  64399 => 'ک',
+  64400 => 'ک',
+  64401 => 'ک',
+  64402 => 'گ',
+  64403 => 'گ',
+  64404 => 'گ',
+  64405 => 'گ',
+  64406 => 'ڳ',
+  64407 => 'ڳ',
+  64408 => 'ڳ',
+  64409 => 'ڳ',
+  64410 => 'ڱ',
+  64411 => 'ڱ',
+  64412 => 'ڱ',
+  64413 => 'ڱ',
+  64414 => 'ں',
+  64415 => 'ں',
+  64416 => 'ڻ',
+  64417 => 'ڻ',
+  64418 => 'ڻ',
+  64419 => 'ڻ',
+  64420 => 'ۀ',
+  64421 => 'ۀ',
+  64422 => 'ہ',
+  64423 => 'ہ',
+  64424 => 'ہ',
+  64425 => 'ہ',
+  64426 => 'ھ',
+  64427 => 'ھ',
+  64428 => 'ھ',
+  64429 => 'ھ',
+  64430 => 'ے',
+  64431 => 'ے',
+  64432 => 'ۓ',
+  64433 => 'ۓ',
+  64467 => 'ڭ',
+  64468 => 'ڭ',
+  64469 => 'ڭ',
+  64470 => 'ڭ',
+  64471 => 'ۇ',
+  64472 => 'ۇ',
+  64473 => 'ۆ',
+  64474 => 'ۆ',
+  64475 => 'ۈ',
+  64476 => 'ۈ',
+  64477 => 'ۇٴ',
+  64478 => 'ۋ',
+  64479 => 'ۋ',
+  64480 => 'ۅ',
+  64481 => 'ۅ',
+  64482 => 'ۉ',
+  64483 => 'ۉ',
+  64484 => 'ې',
+  64485 => 'ې',
+  64486 => 'ې',
+  64487 => 'ې',
+  64488 => 'ى',
+  64489 => 'ى',
+  64490 => 'ئا',
+  64491 => 'ئا',
+  64492 => 'ئە',
+  64493 => 'ئە',
+  64494 => 'ئو',
+  64495 => 'ئو',
+  64496 => 'ئۇ',
+  64497 => 'ئۇ',
+  64498 => 'ئۆ',
+  64499 => 'ئۆ',
+  64500 => 'ئۈ',
+  64501 => 'ئۈ',
+  64502 => 'ئې',
+  64503 => 'ئې',
+  64504 => 'ئې',
+  64505 => 'ئى',
+  64506 => 'ئى',
+  64507 => 'ئى',
+  64508 => 'ی',
+  64509 => 'ی',
+  64510 => 'ی',
+  64511 => 'ی',
+  64512 => 'ئج',
+  64513 => 'ئح',
+  64514 => 'ئم',
+  64515 => 'ئى',
+  64516 => 'ئي',
+  64517 => 'بج',
+  64518 => 'بح',
+  64519 => 'بخ',
+  64520 => 'بم',
+  64521 => 'بى',
+  64522 => 'بي',
+  64523 => 'تج',
+  64524 => 'تح',
+  64525 => 'تخ',
+  64526 => 'تم',
+  64527 => 'تى',
+  64528 => 'تي',
+  64529 => 'ثج',
+  64530 => 'ثم',
+  64531 => 'ثى',
+  64532 => 'ثي',
+  64533 => 'جح',
+  64534 => 'جم',
+  64535 => 'حج',
+  64536 => 'حم',
+  64537 => 'خج',
+  64538 => 'خح',
+  64539 => 'خم',
+  64540 => 'سج',
+  64541 => 'سح',
+  64542 => 'سخ',
+  64543 => 'سم',
+  64544 => 'صح',
+  64545 => 'صم',
+  64546 => 'ضج',
+  64547 => 'ضح',
+  64548 => 'ضخ',
+  64549 => 'ضم',
+  64550 => 'طح',
+  64551 => 'طم',
+  64552 => 'ظم',
+  64553 => 'عج',
+  64554 => 'عم',
+  64555 => 'غج',
+  64556 => 'غم',
+  64557 => 'فج',
+  64558 => 'فح',
+  64559 => 'فخ',
+  64560 => 'فم',
+  64561 => 'فى',
+  64562 => 'في',
+  64563 => 'قح',
+  64564 => 'قم',
+  64565 => 'قى',
+  64566 => 'قي',
+  64567 => 'كا',
+  64568 => 'كج',
+  64569 => 'كح',
+  64570 => 'كخ',
+  64571 => 'كل',
+  64572 => 'كم',
+  64573 => 'كى',
+  64574 => 'كي',
+  64575 => 'لج',
+  64576 => 'لح',
+  64577 => 'لخ',
+  64578 => 'لم',
+  64579 => 'لى',
+  64580 => 'لي',
+  64581 => 'مج',
+  64582 => 'مح',
+  64583 => 'مخ',
+  64584 => 'مم',
+  64585 => 'مى',
+  64586 => 'مي',
+  64587 => 'نج',
+  64588 => 'نح',
+  64589 => 'نخ',
+  64590 => 'نم',
+  64591 => 'نى',
+  64592 => 'ني',
+  64593 => 'هج',
+  64594 => 'هم',
+  64595 => 'هى',
+  64596 => 'هي',
+  64597 => 'يج',
+  64598 => 'يح',
+  64599 => 'يخ',
+  64600 => 'يم',
+  64601 => 'يى',
+  64602 => 'يي',
+  64603 => 'ذٰ',
+  64604 => 'رٰ',
+  64605 => 'ىٰ',
+  64612 => 'ئر',
+  64613 => 'ئز',
+  64614 => 'ئم',
+  64615 => 'ئن',
+  64616 => 'ئى',
+  64617 => 'ئي',
+  64618 => 'بر',
+  64619 => 'بز',
+  64620 => 'بم',
+  64621 => 'بن',
+  64622 => 'بى',
+  64623 => 'بي',
+  64624 => 'تر',
+  64625 => 'تز',
+  64626 => 'تم',
+  64627 => 'تن',
+  64628 => 'تى',
+  64629 => 'تي',
+  64630 => 'ثر',
+  64631 => 'ثز',
+  64632 => 'ثم',
+  64633 => 'ثن',
+  64634 => 'ثى',
+  64635 => 'ثي',
+  64636 => 'فى',
+  64637 => 'في',
+  64638 => 'قى',
+  64639 => 'قي',
+  64640 => 'كا',
+  64641 => 'كل',
+  64642 => 'كم',
+  64643 => 'كى',
+  64644 => 'كي',
+  64645 => 'لم',
+  64646 => 'لى',
+  64647 => 'لي',
+  64648 => 'ما',
+  64649 => 'مم',
+  64650 => 'نر',
+  64651 => 'نز',
+  64652 => 'نم',
+  64653 => 'نن',
+  64654 => 'نى',
+  64655 => 'ني',
+  64656 => 'ىٰ',
+  64657 => 'ير',
+  64658 => 'يز',
+  64659 => 'يم',
+  64660 => 'ين',
+  64661 => 'يى',
+  64662 => 'يي',
+  64663 => 'ئج',
+  64664 => 'ئح',
+  64665 => 'ئخ',
+  64666 => 'ئم',
+  64667 => 'ئه',
+  64668 => 'بج',
+  64669 => 'بح',
+  64670 => 'بخ',
+  64671 => 'بم',
+  64672 => 'به',
+  64673 => 'تج',
+  64674 => 'تح',
+  64675 => 'تخ',
+  64676 => 'تم',
+  64677 => 'ته',
+  64678 => 'ثم',
+  64679 => 'جح',
+  64680 => 'جم',
+  64681 => 'حج',
+  64682 => 'حم',
+  64683 => 'خج',
+  64684 => 'خم',
+  64685 => 'سج',
+  64686 => 'سح',
+  64687 => 'سخ',
+  64688 => 'سم',
+  64689 => 'صح',
+  64690 => 'صخ',
+  64691 => 'صم',
+  64692 => 'ضج',
+  64693 => 'ضح',
+  64694 => 'ضخ',
+  64695 => 'ضم',
+  64696 => 'طح',
+  64697 => 'ظم',
+  64698 => 'عج',
+  64699 => 'عم',
+  64700 => 'غج',
+  64701 => 'غم',
+  64702 => 'فج',
+  64703 => 'فح',
+  64704 => 'فخ',
+  64705 => 'فم',
+  64706 => 'قح',
+  64707 => 'قم',
+  64708 => 'كج',
+  64709 => 'كح',
+  64710 => 'كخ',
+  64711 => 'كل',
+  64712 => 'كم',
+  64713 => 'لج',
+  64714 => 'لح',
+  64715 => 'لخ',
+  64716 => 'لم',
+  64717 => 'له',
+  64718 => 'مج',
+  64719 => 'مح',
+  64720 => 'مخ',
+  64721 => 'مم',
+  64722 => 'نج',
+  64723 => 'نح',
+  64724 => 'نخ',
+  64725 => 'نم',
+  64726 => 'نه',
+  64727 => 'هج',
+  64728 => 'هم',
+  64729 => 'هٰ',
+  64730 => 'يج',
+  64731 => 'يح',
+  64732 => 'يخ',
+  64733 => 'يم',
+  64734 => 'يه',
+  64735 => 'ئم',
+  64736 => 'ئه',
+  64737 => 'بم',
+  64738 => 'به',
+  64739 => 'تم',
+  64740 => 'ته',
+  64741 => 'ثم',
+  64742 => 'ثه',
+  64743 => 'سم',
+  64744 => 'سه',
+  64745 => 'شم',
+  64746 => 'شه',
+  64747 => 'كل',
+  64748 => 'كم',
+  64749 => 'لم',
+  64750 => 'نم',
+  64751 => 'نه',
+  64752 => 'يم',
+  64753 => 'يه',
+  64754 => 'ـَّ',
+  64755 => 'ـُّ',
+  64756 => 'ـِّ',
+  64757 => 'طى',
+  64758 => 'طي',
+  64759 => 'عى',
+  64760 => 'عي',
+  64761 => 'غى',
+  64762 => 'غي',
+  64763 => 'سى',
+  64764 => 'سي',
+  64765 => 'شى',
+  64766 => 'شي',
+  64767 => 'حى',
+  64768 => 'حي',
+  64769 => 'جى',
+  64770 => 'جي',
+  64771 => 'خى',
+  64772 => 'خي',
+  64773 => 'صى',
+  64774 => 'صي',
+  64775 => 'ضى',
+  64776 => 'ضي',
+  64777 => 'شج',
+  64778 => 'شح',
+  64779 => 'شخ',
+  64780 => 'شم',
+  64781 => 'شر',
+  64782 => 'سر',
+  64783 => 'صر',
+  64784 => 'ضر',
+  64785 => 'طى',
+  64786 => 'طي',
+  64787 => 'عى',
+  64788 => 'عي',
+  64789 => 'غى',
+  64790 => 'غي',
+  64791 => 'سى',
+  64792 => 'سي',
+  64793 => 'شى',
+  64794 => 'شي',
+  64795 => 'حى',
+  64796 => 'حي',
+  64797 => 'جى',
+  64798 => 'جي',
+  64799 => 'خى',
+  64800 => 'خي',
+  64801 => 'صى',
+  64802 => 'صي',
+  64803 => 'ضى',
+  64804 => 'ضي',
+  64805 => 'شج',
+  64806 => 'شح',
+  64807 => 'شخ',
+  64808 => 'شم',
+  64809 => 'شر',
+  64810 => 'سر',
+  64811 => 'صر',
+  64812 => 'ضر',
+  64813 => 'شج',
+  64814 => 'شح',
+  64815 => 'شخ',
+  64816 => 'شم',
+  64817 => 'سه',
+  64818 => 'شه',
+  64819 => 'طم',
+  64820 => 'سج',
+  64821 => 'سح',
+  64822 => 'سخ',
+  64823 => 'شج',
+  64824 => 'شح',
+  64825 => 'شخ',
+  64826 => 'طم',
+  64827 => 'ظم',
+  64828 => 'اً',
+  64829 => 'اً',
+  64848 => 'تجم',
+  64849 => 'تحج',
+  64850 => 'تحج',
+  64851 => 'تحم',
+  64852 => 'تخم',
+  64853 => 'تمج',
+  64854 => 'تمح',
+  64855 => 'تمخ',
+  64856 => 'جمح',
+  64857 => 'جمح',
+  64858 => 'حمي',
+  64859 => 'حمى',
+  64860 => 'سحج',
+  64861 => 'سجح',
+  64862 => 'سجى',
+  64863 => 'سمح',
+  64864 => 'سمح',
+  64865 => 'سمج',
+  64866 => 'سمم',
+  64867 => 'سمم',
+  64868 => 'صحح',
+  64869 => 'صحح',
+  64870 => 'صمم',
+  64871 => 'شحم',
+  64872 => 'شحم',
+  64873 => 'شجي',
+  64874 => 'شمخ',
+  64875 => 'شمخ',
+  64876 => 'شمم',
+  64877 => 'شمم',
+  64878 => 'ضحى',
+  64879 => 'ضخم',
+  64880 => 'ضخم',
+  64881 => 'طمح',
+  64882 => 'طمح',
+  64883 => 'طمم',
+  64884 => 'طمي',
+  64885 => 'عجم',
+  64886 => 'عمم',
+  64887 => 'عمم',
+  64888 => 'عمى',
+  64889 => 'غمم',
+  64890 => 'غمي',
+  64891 => 'غمى',
+  64892 => 'فخم',
+  64893 => 'فخم',
+  64894 => 'قمح',
+  64895 => 'قمم',
+  64896 => 'لحم',
+  64897 => 'لحي',
+  64898 => 'لحى',
+  64899 => 'لجج',
+  64900 => 'لجج',
+  64901 => 'لخم',
+  64902 => 'لخم',
+  64903 => 'لمح',
+  64904 => 'لمح',
+  64905 => 'محج',
+  64906 => 'محم',
+  64907 => 'محي',
+  64908 => 'مجح',
+  64909 => 'مجم',
+  64910 => 'مخج',
+  64911 => 'مخم',
+  64914 => 'مجخ',
+  64915 => 'همج',
+  64916 => 'همم',
+  64917 => 'نحم',
+  64918 => 'نحى',
+  64919 => 'نجم',
+  64920 => 'نجم',
+  64921 => 'نجى',
+  64922 => 'نمي',
+  64923 => 'نمى',
+  64924 => 'يمم',
+  64925 => 'يمم',
+  64926 => 'بخي',
+  64927 => 'تجي',
+  64928 => 'تجى',
+  64929 => 'تخي',
+  64930 => 'تخى',
+  64931 => 'تمي',
+  64932 => 'تمى',
+  64933 => 'جمي',
+  64934 => 'جحى',
+  64935 => 'جمى',
+  64936 => 'سخى',
+  64937 => 'صحي',
+  64938 => 'شحي',
+  64939 => 'ضحي',
+  64940 => 'لجي',
+  64941 => 'لمي',
+  64942 => 'يحي',
+  64943 => 'يجي',
+  64944 => 'يمي',
+  64945 => 'ممي',
+  64946 => 'قمي',
+  64947 => 'نحي',
+  64948 => 'قمح',
+  64949 => 'لحم',
+  64950 => 'عمي',
+  64951 => 'كمي',
+  64952 => 'نجح',
+  64953 => 'مخي',
+  64954 => 'لجم',
+  64955 => 'كمم',
+  64956 => 'لجم',
+  64957 => 'نجح',
+  64958 => 'جحي',
+  64959 => 'حجي',
+  64960 => 'مجي',
+  64961 => 'فمي',
+  64962 => 'بحي',
+  64963 => 'كمم',
+  64964 => 'عجم',
+  64965 => 'صمم',
+  64966 => 'سخي',
+  64967 => 'نجي',
+  65008 => 'صلے',
+  65009 => 'قلے',
+  65010 => 'الله',
+  65011 => 'اكبر',
+  65012 => 'محمد',
+  65013 => 'صلعم',
+  65014 => 'رسول',
+  65015 => 'عليه',
+  65016 => 'وسلم',
+  65017 => 'صلى',
+  65020 => 'ریال',
+  65041 => '、',
+  65047 => '〖',
+  65048 => '〗',
+  65073 => '—',
+  65074 => '–',
+  65081 => '〔',
+  65082 => '〕',
+  65083 => '【',
+  65084 => '】',
+  65085 => '《',
+  65086 => '》',
+  65087 => '〈',
+  65088 => '〉',
+  65089 => '「',
+  65090 => '」',
+  65091 => '『',
+  65092 => '』',
+  65105 => '、',
+  65112 => '—',
+  65117 => '〔',
+  65118 => '〕',
+  65123 => '-',
+  65137 => 'ـً',
+  65143 => 'ـَ',
+  65145 => 'ـُ',
+  65147 => 'ـِ',
+  65149 => 'ـّ',
+  65151 => 'ـْ',
+  65152 => 'ء',
+  65153 => 'آ',
+  65154 => 'آ',
+  65155 => 'أ',
+  65156 => 'أ',
+  65157 => 'ؤ',
+  65158 => 'ؤ',
+  65159 => 'إ',
+  65160 => 'إ',
+  65161 => 'ئ',
+  65162 => 'ئ',
+  65163 => 'ئ',
+  65164 => 'ئ',
+  65165 => 'ا',
+  65166 => 'ا',
+  65167 => 'ب',
+  65168 => 'ب',
+  65169 => 'ب',
+  65170 => 'ب',
+  65171 => 'ة',
+  65172 => 'ة',
+  65173 => 'ت',
+  65174 => 'ت',
+  65175 => 'ت',
+  65176 => 'ت',
+  65177 => 'ث',
+  65178 => 'ث',
+  65179 => 'ث',
+  65180 => 'ث',
+  65181 => 'ج',
+  65182 => 'ج',
+  65183 => 'ج',
+  65184 => 'ج',
+  65185 => 'ح',
+  65186 => 'ح',
+  65187 => 'ح',
+  65188 => 'ح',
+  65189 => 'خ',
+  65190 => 'خ',
+  65191 => 'خ',
+  65192 => 'خ',
+  65193 => 'د',
+  65194 => 'د',
+  65195 => 'ذ',
+  65196 => 'ذ',
+  65197 => 'ر',
+  65198 => 'ر',
+  65199 => 'ز',
+  65200 => 'ز',
+  65201 => 'س',
+  65202 => 'س',
+  65203 => 'س',
+  65204 => 'س',
+  65205 => 'ش',
+  65206 => 'ش',
+  65207 => 'ش',
+  65208 => 'ش',
+  65209 => 'ص',
+  65210 => 'ص',
+  65211 => 'ص',
+  65212 => 'ص',
+  65213 => 'ض',
+  65214 => 'ض',
+  65215 => 'ض',
+  65216 => 'ض',
+  65217 => 'ط',
+  65218 => 'ط',
+  65219 => 'ط',
+  65220 => 'ط',
+  65221 => 'ظ',
+  65222 => 'ظ',
+  65223 => 'ظ',
+  65224 => 'ظ',
+  65225 => 'ع',
+  65226 => 'ع',
+  65227 => 'ع',
+  65228 => 'ع',
+  65229 => 'غ',
+  65230 => 'غ',
+  65231 => 'غ',
+  65232 => 'غ',
+  65233 => 'ف',
+  65234 => 'ف',
+  65235 => 'ف',
+  65236 => 'ف',
+  65237 => 'ق',
+  65238 => 'ق',
+  65239 => 'ق',
+  65240 => 'ق',
+  65241 => 'ك',
+  65242 => 'ك',
+  65243 => 'ك',
+  65244 => 'ك',
+  65245 => 'ل',
+  65246 => 'ل',
+  65247 => 'ل',
+  65248 => 'ل',
+  65249 => 'م',
+  65250 => 'م',
+  65251 => 'م',
+  65252 => 'م',
+  65253 => 'ن',
+  65254 => 'ن',
+  65255 => 'ن',
+  65256 => 'ن',
+  65257 => 'ه',
+  65258 => 'ه',
+  65259 => 'ه',
+  65260 => 'ه',
+  65261 => 'و',
+  65262 => 'و',
+  65263 => 'ى',
+  65264 => 'ى',
+  65265 => 'ي',
+  65266 => 'ي',
+  65267 => 'ي',
+  65268 => 'ي',
+  65269 => 'لآ',
+  65270 => 'لآ',
+  65271 => 'لأ',
+  65272 => 'لأ',
+  65273 => 'لإ',
+  65274 => 'لإ',
+  65275 => 'لا',
+  65276 => 'لا',
+  65293 => '-',
+  65294 => '.',
+  65296 => '0',
+  65297 => '1',
+  65298 => '2',
+  65299 => '3',
+  65300 => '4',
+  65301 => '5',
+  65302 => '6',
+  65303 => '7',
+  65304 => '8',
+  65305 => '9',
+  65313 => 'a',
+  65314 => 'b',
+  65315 => 'c',
+  65316 => 'd',
+  65317 => 'e',
+  65318 => 'f',
+  65319 => 'g',
+  65320 => 'h',
+  65321 => 'i',
+  65322 => 'j',
+  65323 => 'k',
+  65324 => 'l',
+  65325 => 'm',
+  65326 => 'n',
+  65327 => 'o',
+  65328 => 'p',
+  65329 => 'q',
+  65330 => 'r',
+  65331 => 's',
+  65332 => 't',
+  65333 => 'u',
+  65334 => 'v',
+  65335 => 'w',
+  65336 => 'x',
+  65337 => 'y',
+  65338 => 'z',
+  65345 => 'a',
+  65346 => 'b',
+  65347 => 'c',
+  65348 => 'd',
+  65349 => 'e',
+  65350 => 'f',
+  65351 => 'g',
+  65352 => 'h',
+  65353 => 'i',
+  65354 => 'j',
+  65355 => 'k',
+  65356 => 'l',
+  65357 => 'm',
+  65358 => 'n',
+  65359 => 'o',
+  65360 => 'p',
+  65361 => 'q',
+  65362 => 'r',
+  65363 => 's',
+  65364 => 't',
+  65365 => 'u',
+  65366 => 'v',
+  65367 => 'w',
+  65368 => 'x',
+  65369 => 'y',
+  65370 => 'z',
+  65375 => '⦅',
+  65376 => '⦆',
+  65377 => '.',
+  65378 => '「',
+  65379 => '」',
+  65380 => '、',
+  65381 => '・',
+  65382 => 'ヲ',
+  65383 => 'ァ',
+  65384 => 'ィ',
+  65385 => 'ゥ',
+  65386 => 'ェ',
+  65387 => 'ォ',
+  65388 => 'ャ',
+  65389 => 'ュ',
+  65390 => 'ョ',
+  65391 => 'ッ',
+  65392 => 'ー',
+  65393 => 'ア',
+  65394 => 'イ',
+  65395 => 'ウ',
+  65396 => 'エ',
+  65397 => 'オ',
+  65398 => 'カ',
+  65399 => 'キ',
+  65400 => 'ク',
+  65401 => 'ケ',
+  65402 => 'コ',
+  65403 => 'サ',
+  65404 => 'シ',
+  65405 => 'ス',
+  65406 => 'セ',
+  65407 => 'ソ',
+  65408 => 'タ',
+  65409 => 'チ',
+  65410 => 'ツ',
+  65411 => 'テ',
+  65412 => 'ト',
+  65413 => 'ナ',
+  65414 => 'ニ',
+  65415 => 'ヌ',
+  65416 => 'ネ',
+  65417 => 'ノ',
+  65418 => 'ハ',
+  65419 => 'ヒ',
+  65420 => 'フ',
+  65421 => 'ヘ',
+  65422 => 'ホ',
+  65423 => 'マ',
+  65424 => 'ミ',
+  65425 => 'ム',
+  65426 => 'メ',
+  65427 => 'モ',
+  65428 => 'ヤ',
+  65429 => 'ユ',
+  65430 => 'ヨ',
+  65431 => 'ラ',
+  65432 => 'リ',
+  65433 => 'ル',
+  65434 => 'レ',
+  65435 => 'ロ',
+  65436 => 'ワ',
+  65437 => 'ン',
+  65438 => '゙',
+  65439 => '゚',
+  65441 => 'ᄀ',
+  65442 => 'ᄁ',
+  65443 => 'ᆪ',
+  65444 => 'ᄂ',
+  65445 => 'ᆬ',
+  65446 => 'ᆭ',
+  65447 => 'ᄃ',
+  65448 => 'ᄄ',
+  65449 => 'ᄅ',
+  65450 => 'ᆰ',
+  65451 => 'ᆱ',
+  65452 => 'ᆲ',
+  65453 => 'ᆳ',
+  65454 => 'ᆴ',
+  65455 => 'ᆵ',
+  65456 => 'ᄚ',
+  65457 => 'ᄆ',
+  65458 => 'ᄇ',
+  65459 => 'ᄈ',
+  65460 => 'ᄡ',
+  65461 => 'ᄉ',
+  65462 => 'ᄊ',
+  65463 => 'ᄋ',
+  65464 => 'ᄌ',
+  65465 => 'ᄍ',
+  65466 => 'ᄎ',
+  65467 => 'ᄏ',
+  65468 => 'ᄐ',
+  65469 => 'ᄑ',
+  65470 => 'ᄒ',
+  65474 => 'ᅡ',
+  65475 => 'ᅢ',
+  65476 => 'ᅣ',
+  65477 => 'ᅤ',
+  65478 => 'ᅥ',
+  65479 => 'ᅦ',
+  65482 => 'ᅧ',
+  65483 => 'ᅨ',
+  65484 => 'ᅩ',
+  65485 => 'ᅪ',
+  65486 => 'ᅫ',
+  65487 => 'ᅬ',
+  65490 => 'ᅭ',
+  65491 => 'ᅮ',
+  65492 => 'ᅯ',
+  65493 => 'ᅰ',
+  65494 => 'ᅱ',
+  65495 => 'ᅲ',
+  65498 => 'ᅳ',
+  65499 => 'ᅴ',
+  65500 => 'ᅵ',
+  65504 => '¢',
+  65505 => '£',
+  65506 => '¬',
+  65508 => '¦',
+  65509 => '¥',
+  65510 => '₩',
+  65512 => '│',
+  65513 => '←',
+  65514 => '↑',
+  65515 => '→',
+  65516 => '↓',
+  65517 => '■',
+  65518 => '○',
+  66560 => '𐐨',
+  66561 => '𐐩',
+  66562 => '𐐪',
+  66563 => '𐐫',
+  66564 => '𐐬',
+  66565 => '𐐭',
+  66566 => '𐐮',
+  66567 => '𐐯',
+  66568 => '𐐰',
+  66569 => '𐐱',
+  66570 => '𐐲',
+  66571 => '𐐳',
+  66572 => '𐐴',
+  66573 => '𐐵',
+  66574 => '𐐶',
+  66575 => '𐐷',
+  66576 => '𐐸',
+  66577 => '𐐹',
+  66578 => '𐐺',
+  66579 => '𐐻',
+  66580 => '𐐼',
+  66581 => '𐐽',
+  66582 => '𐐾',
+  66583 => '𐐿',
+  66584 => '𐑀',
+  66585 => '𐑁',
+  66586 => '𐑂',
+  66587 => '𐑃',
+  66588 => '𐑄',
+  66589 => '𐑅',
+  66590 => '𐑆',
+  66591 => '𐑇',
+  66592 => '𐑈',
+  66593 => '𐑉',
+  66594 => '𐑊',
+  66595 => '𐑋',
+  66596 => '𐑌',
+  66597 => '𐑍',
+  66598 => '𐑎',
+  66599 => '𐑏',
+  66736 => '𐓘',
+  66737 => '𐓙',
+  66738 => '𐓚',
+  66739 => '𐓛',
+  66740 => '𐓜',
+  66741 => '𐓝',
+  66742 => '𐓞',
+  66743 => '𐓟',
+  66744 => '𐓠',
+  66745 => '𐓡',
+  66746 => '𐓢',
+  66747 => '𐓣',
+  66748 => '𐓤',
+  66749 => '𐓥',
+  66750 => '𐓦',
+  66751 => '𐓧',
+  66752 => '𐓨',
+  66753 => '𐓩',
+  66754 => '𐓪',
+  66755 => '𐓫',
+  66756 => '𐓬',
+  66757 => '𐓭',
+  66758 => '𐓮',
+  66759 => '𐓯',
+  66760 => '𐓰',
+  66761 => '𐓱',
+  66762 => '𐓲',
+  66763 => '𐓳',
+  66764 => '𐓴',
+  66765 => '𐓵',
+  66766 => '𐓶',
+  66767 => '𐓷',
+  66768 => '𐓸',
+  66769 => '𐓹',
+  66770 => '𐓺',
+  66771 => '𐓻',
+  68736 => '𐳀',
+  68737 => '𐳁',
+  68738 => '𐳂',
+  68739 => '𐳃',
+  68740 => '𐳄',
+  68741 => '𐳅',
+  68742 => '𐳆',
+  68743 => '𐳇',
+  68744 => '𐳈',
+  68745 => '𐳉',
+  68746 => '𐳊',
+  68747 => '𐳋',
+  68748 => '𐳌',
+  68749 => '𐳍',
+  68750 => '𐳎',
+  68751 => '𐳏',
+  68752 => '𐳐',
+  68753 => '𐳑',
+  68754 => '𐳒',
+  68755 => '𐳓',
+  68756 => '𐳔',
+  68757 => '𐳕',
+  68758 => '𐳖',
+  68759 => '𐳗',
+  68760 => '𐳘',
+  68761 => '𐳙',
+  68762 => '𐳚',
+  68763 => '𐳛',
+  68764 => '𐳜',
+  68765 => '𐳝',
+  68766 => '𐳞',
+  68767 => '𐳟',
+  68768 => '𐳠',
+  68769 => '𐳡',
+  68770 => '𐳢',
+  68771 => '𐳣',
+  68772 => '𐳤',
+  68773 => '𐳥',
+  68774 => '𐳦',
+  68775 => '𐳧',
+  68776 => '𐳨',
+  68777 => '𐳩',
+  68778 => '𐳪',
+  68779 => '𐳫',
+  68780 => '𐳬',
+  68781 => '𐳭',
+  68782 => '𐳮',
+  68783 => '𐳯',
+  68784 => '𐳰',
+  68785 => '𐳱',
+  68786 => '𐳲',
+  71840 => '𑣀',
+  71841 => '𑣁',
+  71842 => '𑣂',
+  71843 => '𑣃',
+  71844 => '𑣄',
+  71845 => '𑣅',
+  71846 => '𑣆',
+  71847 => '𑣇',
+  71848 => '𑣈',
+  71849 => '𑣉',
+  71850 => '𑣊',
+  71851 => '𑣋',
+  71852 => '𑣌',
+  71853 => '𑣍',
+  71854 => '𑣎',
+  71855 => '𑣏',
+  71856 => '𑣐',
+  71857 => '𑣑',
+  71858 => '𑣒',
+  71859 => '𑣓',
+  71860 => '𑣔',
+  71861 => '𑣕',
+  71862 => '𑣖',
+  71863 => '𑣗',
+  71864 => '𑣘',
+  71865 => '𑣙',
+  71866 => '𑣚',
+  71867 => '𑣛',
+  71868 => '𑣜',
+  71869 => '𑣝',
+  71870 => '𑣞',
+  71871 => '𑣟',
+  93760 => '𖹠',
+  93761 => '𖹡',
+  93762 => '𖹢',
+  93763 => '𖹣',
+  93764 => '𖹤',
+  93765 => '𖹥',
+  93766 => '𖹦',
+  93767 => '𖹧',
+  93768 => '𖹨',
+  93769 => '𖹩',
+  93770 => '𖹪',
+  93771 => '𖹫',
+  93772 => '𖹬',
+  93773 => '𖹭',
+  93774 => '𖹮',
+  93775 => '𖹯',
+  93776 => '𖹰',
+  93777 => '𖹱',
+  93778 => '𖹲',
+  93779 => '𖹳',
+  93780 => '𖹴',
+  93781 => '𖹵',
+  93782 => '𖹶',
+  93783 => '𖹷',
+  93784 => '𖹸',
+  93785 => '𖹹',
+  93786 => '𖹺',
+  93787 => '𖹻',
+  93788 => '𖹼',
+  93789 => '𖹽',
+  93790 => '𖹾',
+  93791 => '𖹿',
+  119134 => '𝅗𝅥',
+  119135 => '𝅘𝅥',
+  119136 => '𝅘𝅥𝅮',
+  119137 => '𝅘𝅥𝅯',
+  119138 => '𝅘𝅥𝅰',
+  119139 => '𝅘𝅥𝅱',
+  119140 => '𝅘𝅥𝅲',
+  119227 => '𝆹𝅥',
+  119228 => '𝆺𝅥',
+  119229 => '𝆹𝅥𝅮',
+  119230 => '𝆺𝅥𝅮',
+  119231 => '𝆹𝅥𝅯',
+  119232 => '𝆺𝅥𝅯',
+  119808 => 'a',
+  119809 => 'b',
+  119810 => 'c',
+  119811 => 'd',
+  119812 => 'e',
+  119813 => 'f',
+  119814 => 'g',
+  119815 => 'h',
+  119816 => 'i',
+  119817 => 'j',
+  119818 => 'k',
+  119819 => 'l',
+  119820 => 'm',
+  119821 => 'n',
+  119822 => 'o',
+  119823 => 'p',
+  119824 => 'q',
+  119825 => 'r',
+  119826 => 's',
+  119827 => 't',
+  119828 => 'u',
+  119829 => 'v',
+  119830 => 'w',
+  119831 => 'x',
+  119832 => 'y',
+  119833 => 'z',
+  119834 => 'a',
+  119835 => 'b',
+  119836 => 'c',
+  119837 => 'd',
+  119838 => 'e',
+  119839 => 'f',
+  119840 => 'g',
+  119841 => 'h',
+  119842 => 'i',
+  119843 => 'j',
+  119844 => 'k',
+  119845 => 'l',
+  119846 => 'm',
+  119847 => 'n',
+  119848 => 'o',
+  119849 => 'p',
+  119850 => 'q',
+  119851 => 'r',
+  119852 => 's',
+  119853 => 't',
+  119854 => 'u',
+  119855 => 'v',
+  119856 => 'w',
+  119857 => 'x',
+  119858 => 'y',
+  119859 => 'z',
+  119860 => 'a',
+  119861 => 'b',
+  119862 => 'c',
+  119863 => 'd',
+  119864 => 'e',
+  119865 => 'f',
+  119866 => 'g',
+  119867 => 'h',
+  119868 => 'i',
+  119869 => 'j',
+  119870 => 'k',
+  119871 => 'l',
+  119872 => 'm',
+  119873 => 'n',
+  119874 => 'o',
+  119875 => 'p',
+  119876 => 'q',
+  119877 => 'r',
+  119878 => 's',
+  119879 => 't',
+  119880 => 'u',
+  119881 => 'v',
+  119882 => 'w',
+  119883 => 'x',
+  119884 => 'y',
+  119885 => 'z',
+  119886 => 'a',
+  119887 => 'b',
+  119888 => 'c',
+  119889 => 'd',
+  119890 => 'e',
+  119891 => 'f',
+  119892 => 'g',
+  119894 => 'i',
+  119895 => 'j',
+  119896 => 'k',
+  119897 => 'l',
+  119898 => 'm',
+  119899 => 'n',
+  119900 => 'o',
+  119901 => 'p',
+  119902 => 'q',
+  119903 => 'r',
+  119904 => 's',
+  119905 => 't',
+  119906 => 'u',
+  119907 => 'v',
+  119908 => 'w',
+  119909 => 'x',
+  119910 => 'y',
+  119911 => 'z',
+  119912 => 'a',
+  119913 => 'b',
+  119914 => 'c',
+  119915 => 'd',
+  119916 => 'e',
+  119917 => 'f',
+  119918 => 'g',
+  119919 => 'h',
+  119920 => 'i',
+  119921 => 'j',
+  119922 => 'k',
+  119923 => 'l',
+  119924 => 'm',
+  119925 => 'n',
+  119926 => 'o',
+  119927 => 'p',
+  119928 => 'q',
+  119929 => 'r',
+  119930 => 's',
+  119931 => 't',
+  119932 => 'u',
+  119933 => 'v',
+  119934 => 'w',
+  119935 => 'x',
+  119936 => 'y',
+  119937 => 'z',
+  119938 => 'a',
+  119939 => 'b',
+  119940 => 'c',
+  119941 => 'd',
+  119942 => 'e',
+  119943 => 'f',
+  119944 => 'g',
+  119945 => 'h',
+  119946 => 'i',
+  119947 => 'j',
+  119948 => 'k',
+  119949 => 'l',
+  119950 => 'm',
+  119951 => 'n',
+  119952 => 'o',
+  119953 => 'p',
+  119954 => 'q',
+  119955 => 'r',
+  119956 => 's',
+  119957 => 't',
+  119958 => 'u',
+  119959 => 'v',
+  119960 => 'w',
+  119961 => 'x',
+  119962 => 'y',
+  119963 => 'z',
+  119964 => 'a',
+  119966 => 'c',
+  119967 => 'd',
+  119970 => 'g',
+  119973 => 'j',
+  119974 => 'k',
+  119977 => 'n',
+  119978 => 'o',
+  119979 => 'p',
+  119980 => 'q',
+  119982 => 's',
+  119983 => 't',
+  119984 => 'u',
+  119985 => 'v',
+  119986 => 'w',
+  119987 => 'x',
+  119988 => 'y',
+  119989 => 'z',
+  119990 => 'a',
+  119991 => 'b',
+  119992 => 'c',
+  119993 => 'd',
+  119995 => 'f',
+  119997 => 'h',
+  119998 => 'i',
+  119999 => 'j',
+  120000 => 'k',
+  120001 => 'l',
+  120002 => 'm',
+  120003 => 'n',
+  120005 => 'p',
+  120006 => 'q',
+  120007 => 'r',
+  120008 => 's',
+  120009 => 't',
+  120010 => 'u',
+  120011 => 'v',
+  120012 => 'w',
+  120013 => 'x',
+  120014 => 'y',
+  120015 => 'z',
+  120016 => 'a',
+  120017 => 'b',
+  120018 => 'c',
+  120019 => 'd',
+  120020 => 'e',
+  120021 => 'f',
+  120022 => 'g',
+  120023 => 'h',
+  120024 => 'i',
+  120025 => 'j',
+  120026 => 'k',
+  120027 => 'l',
+  120028 => 'm',
+  120029 => 'n',
+  120030 => 'o',
+  120031 => 'p',
+  120032 => 'q',
+  120033 => 'r',
+  120034 => 's',
+  120035 => 't',
+  120036 => 'u',
+  120037 => 'v',
+  120038 => 'w',
+  120039 => 'x',
+  120040 => 'y',
+  120041 => 'z',
+  120042 => 'a',
+  120043 => 'b',
+  120044 => 'c',
+  120045 => 'd',
+  120046 => 'e',
+  120047 => 'f',
+  120048 => 'g',
+  120049 => 'h',
+  120050 => 'i',
+  120051 => 'j',
+  120052 => 'k',
+  120053 => 'l',
+  120054 => 'm',
+  120055 => 'n',
+  120056 => 'o',
+  120057 => 'p',
+  120058 => 'q',
+  120059 => 'r',
+  120060 => 's',
+  120061 => 't',
+  120062 => 'u',
+  120063 => 'v',
+  120064 => 'w',
+  120065 => 'x',
+  120066 => 'y',
+  120067 => 'z',
+  120068 => 'a',
+  120069 => 'b',
+  120071 => 'd',
+  120072 => 'e',
+  120073 => 'f',
+  120074 => 'g',
+  120077 => 'j',
+  120078 => 'k',
+  120079 => 'l',
+  120080 => 'm',
+  120081 => 'n',
+  120082 => 'o',
+  120083 => 'p',
+  120084 => 'q',
+  120086 => 's',
+  120087 => 't',
+  120088 => 'u',
+  120089 => 'v',
+  120090 => 'w',
+  120091 => 'x',
+  120092 => 'y',
+  120094 => 'a',
+  120095 => 'b',
+  120096 => 'c',
+  120097 => 'd',
+  120098 => 'e',
+  120099 => 'f',
+  120100 => 'g',
+  120101 => 'h',
+  120102 => 'i',
+  120103 => 'j',
+  120104 => 'k',
+  120105 => 'l',
+  120106 => 'm',
+  120107 => 'n',
+  120108 => 'o',
+  120109 => 'p',
+  120110 => 'q',
+  120111 => 'r',
+  120112 => 's',
+  120113 => 't',
+  120114 => 'u',
+  120115 => 'v',
+  120116 => 'w',
+  120117 => 'x',
+  120118 => 'y',
+  120119 => 'z',
+  120120 => 'a',
+  120121 => 'b',
+  120123 => 'd',
+  120124 => 'e',
+  120125 => 'f',
+  120126 => 'g',
+  120128 => 'i',
+  120129 => 'j',
+  120130 => 'k',
+  120131 => 'l',
+  120132 => 'm',
+  120134 => 'o',
+  120138 => 's',
+  120139 => 't',
+  120140 => 'u',
+  120141 => 'v',
+  120142 => 'w',
+  120143 => 'x',
+  120144 => 'y',
+  120146 => 'a',
+  120147 => 'b',
+  120148 => 'c',
+  120149 => 'd',
+  120150 => 'e',
+  120151 => 'f',
+  120152 => 'g',
+  120153 => 'h',
+  120154 => 'i',
+  120155 => 'j',
+  120156 => 'k',
+  120157 => 'l',
+  120158 => 'm',
+  120159 => 'n',
+  120160 => 'o',
+  120161 => 'p',
+  120162 => 'q',
+  120163 => 'r',
+  120164 => 's',
+  120165 => 't',
+  120166 => 'u',
+  120167 => 'v',
+  120168 => 'w',
+  120169 => 'x',
+  120170 => 'y',
+  120171 => 'z',
+  120172 => 'a',
+  120173 => 'b',
+  120174 => 'c',
+  120175 => 'd',
+  120176 => 'e',
+  120177 => 'f',
+  120178 => 'g',
+  120179 => 'h',
+  120180 => 'i',
+  120181 => 'j',
+  120182 => 'k',
+  120183 => 'l',
+  120184 => 'm',
+  120185 => 'n',
+  120186 => 'o',
+  120187 => 'p',
+  120188 => 'q',
+  120189 => 'r',
+  120190 => 's',
+  120191 => 't',
+  120192 => 'u',
+  120193 => 'v',
+  120194 => 'w',
+  120195 => 'x',
+  120196 => 'y',
+  120197 => 'z',
+  120198 => 'a',
+  120199 => 'b',
+  120200 => 'c',
+  120201 => 'd',
+  120202 => 'e',
+  120203 => 'f',
+  120204 => 'g',
+  120205 => 'h',
+  120206 => 'i',
+  120207 => 'j',
+  120208 => 'k',
+  120209 => 'l',
+  120210 => 'm',
+  120211 => 'n',
+  120212 => 'o',
+  120213 => 'p',
+  120214 => 'q',
+  120215 => 'r',
+  120216 => 's',
+  120217 => 't',
+  120218 => 'u',
+  120219 => 'v',
+  120220 => 'w',
+  120221 => 'x',
+  120222 => 'y',
+  120223 => 'z',
+  120224 => 'a',
+  120225 => 'b',
+  120226 => 'c',
+  120227 => 'd',
+  120228 => 'e',
+  120229 => 'f',
+  120230 => 'g',
+  120231 => 'h',
+  120232 => 'i',
+  120233 => 'j',
+  120234 => 'k',
+  120235 => 'l',
+  120236 => 'm',
+  120237 => 'n',
+  120238 => 'o',
+  120239 => 'p',
+  120240 => 'q',
+  120241 => 'r',
+  120242 => 's',
+  120243 => 't',
+  120244 => 'u',
+  120245 => 'v',
+  120246 => 'w',
+  120247 => 'x',
+  120248 => 'y',
+  120249 => 'z',
+  120250 => 'a',
+  120251 => 'b',
+  120252 => 'c',
+  120253 => 'd',
+  120254 => 'e',
+  120255 => 'f',
+  120256 => 'g',
+  120257 => 'h',
+  120258 => 'i',
+  120259 => 'j',
+  120260 => 'k',
+  120261 => 'l',
+  120262 => 'm',
+  120263 => 'n',
+  120264 => 'o',
+  120265 => 'p',
+  120266 => 'q',
+  120267 => 'r',
+  120268 => 's',
+  120269 => 't',
+  120270 => 'u',
+  120271 => 'v',
+  120272 => 'w',
+  120273 => 'x',
+  120274 => 'y',
+  120275 => 'z',
+  120276 => 'a',
+  120277 => 'b',
+  120278 => 'c',
+  120279 => 'd',
+  120280 => 'e',
+  120281 => 'f',
+  120282 => 'g',
+  120283 => 'h',
+  120284 => 'i',
+  120285 => 'j',
+  120286 => 'k',
+  120287 => 'l',
+  120288 => 'm',
+  120289 => 'n',
+  120290 => 'o',
+  120291 => 'p',
+  120292 => 'q',
+  120293 => 'r',
+  120294 => 's',
+  120295 => 't',
+  120296 => 'u',
+  120297 => 'v',
+  120298 => 'w',
+  120299 => 'x',
+  120300 => 'y',
+  120301 => 'z',
+  120302 => 'a',
+  120303 => 'b',
+  120304 => 'c',
+  120305 => 'd',
+  120306 => 'e',
+  120307 => 'f',
+  120308 => 'g',
+  120309 => 'h',
+  120310 => 'i',
+  120311 => 'j',
+  120312 => 'k',
+  120313 => 'l',
+  120314 => 'm',
+  120315 => 'n',
+  120316 => 'o',
+  120317 => 'p',
+  120318 => 'q',
+  120319 => 'r',
+  120320 => 's',
+  120321 => 't',
+  120322 => 'u',
+  120323 => 'v',
+  120324 => 'w',
+  120325 => 'x',
+  120326 => 'y',
+  120327 => 'z',
+  120328 => 'a',
+  120329 => 'b',
+  120330 => 'c',
+  120331 => 'd',
+  120332 => 'e',
+  120333 => 'f',
+  120334 => 'g',
+  120335 => 'h',
+  120336 => 'i',
+  120337 => 'j',
+  120338 => 'k',
+  120339 => 'l',
+  120340 => 'm',
+  120341 => 'n',
+  120342 => 'o',
+  120343 => 'p',
+  120344 => 'q',
+  120345 => 'r',
+  120346 => 's',
+  120347 => 't',
+  120348 => 'u',
+  120349 => 'v',
+  120350 => 'w',
+  120351 => 'x',
+  120352 => 'y',
+  120353 => 'z',
+  120354 => 'a',
+  120355 => 'b',
+  120356 => 'c',
+  120357 => 'd',
+  120358 => 'e',
+  120359 => 'f',
+  120360 => 'g',
+  120361 => 'h',
+  120362 => 'i',
+  120363 => 'j',
+  120364 => 'k',
+  120365 => 'l',
+  120366 => 'm',
+  120367 => 'n',
+  120368 => 'o',
+  120369 => 'p',
+  120370 => 'q',
+  120371 => 'r',
+  120372 => 's',
+  120373 => 't',
+  120374 => 'u',
+  120375 => 'v',
+  120376 => 'w',
+  120377 => 'x',
+  120378 => 'y',
+  120379 => 'z',
+  120380 => 'a',
+  120381 => 'b',
+  120382 => 'c',
+  120383 => 'd',
+  120384 => 'e',
+  120385 => 'f',
+  120386 => 'g',
+  120387 => 'h',
+  120388 => 'i',
+  120389 => 'j',
+  120390 => 'k',
+  120391 => 'l',
+  120392 => 'm',
+  120393 => 'n',
+  120394 => 'o',
+  120395 => 'p',
+  120396 => 'q',
+  120397 => 'r',
+  120398 => 's',
+  120399 => 't',
+  120400 => 'u',
+  120401 => 'v',
+  120402 => 'w',
+  120403 => 'x',
+  120404 => 'y',
+  120405 => 'z',
+  120406 => 'a',
+  120407 => 'b',
+  120408 => 'c',
+  120409 => 'd',
+  120410 => 'e',
+  120411 => 'f',
+  120412 => 'g',
+  120413 => 'h',
+  120414 => 'i',
+  120415 => 'j',
+  120416 => 'k',
+  120417 => 'l',
+  120418 => 'm',
+  120419 => 'n',
+  120420 => 'o',
+  120421 => 'p',
+  120422 => 'q',
+  120423 => 'r',
+  120424 => 's',
+  120425 => 't',
+  120426 => 'u',
+  120427 => 'v',
+  120428 => 'w',
+  120429 => 'x',
+  120430 => 'y',
+  120431 => 'z',
+  120432 => 'a',
+  120433 => 'b',
+  120434 => 'c',
+  120435 => 'd',
+  120436 => 'e',
+  120437 => 'f',
+  120438 => 'g',
+  120439 => 'h',
+  120440 => 'i',
+  120441 => 'j',
+  120442 => 'k',
+  120443 => 'l',
+  120444 => 'm',
+  120445 => 'n',
+  120446 => 'o',
+  120447 => 'p',
+  120448 => 'q',
+  120449 => 'r',
+  120450 => 's',
+  120451 => 't',
+  120452 => 'u',
+  120453 => 'v',
+  120454 => 'w',
+  120455 => 'x',
+  120456 => 'y',
+  120457 => 'z',
+  120458 => 'a',
+  120459 => 'b',
+  120460 => 'c',
+  120461 => 'd',
+  120462 => 'e',
+  120463 => 'f',
+  120464 => 'g',
+  120465 => 'h',
+  120466 => 'i',
+  120467 => 'j',
+  120468 => 'k',
+  120469 => 'l',
+  120470 => 'm',
+  120471 => 'n',
+  120472 => 'o',
+  120473 => 'p',
+  120474 => 'q',
+  120475 => 'r',
+  120476 => 's',
+  120477 => 't',
+  120478 => 'u',
+  120479 => 'v',
+  120480 => 'w',
+  120481 => 'x',
+  120482 => 'y',
+  120483 => 'z',
+  120484 => 'ı',
+  120485 => 'ȷ',
+  120488 => 'α',
+  120489 => 'β',
+  120490 => 'γ',
+  120491 => 'δ',
+  120492 => 'ε',
+  120493 => 'ζ',
+  120494 => 'η',
+  120495 => 'θ',
+  120496 => 'ι',
+  120497 => 'κ',
+  120498 => 'λ',
+  120499 => 'μ',
+  120500 => 'ν',
+  120501 => 'ξ',
+  120502 => 'ο',
+  120503 => 'π',
+  120504 => 'ρ',
+  120505 => 'θ',
+  120506 => 'σ',
+  120507 => 'τ',
+  120508 => 'υ',
+  120509 => 'φ',
+  120510 => 'χ',
+  120511 => 'ψ',
+  120512 => 'ω',
+  120513 => '∇',
+  120514 => 'α',
+  120515 => 'β',
+  120516 => 'γ',
+  120517 => 'δ',
+  120518 => 'ε',
+  120519 => 'ζ',
+  120520 => 'η',
+  120521 => 'θ',
+  120522 => 'ι',
+  120523 => 'κ',
+  120524 => 'λ',
+  120525 => 'μ',
+  120526 => 'ν',
+  120527 => 'ξ',
+  120528 => 'ο',
+  120529 => 'π',
+  120530 => 'ρ',
+  120531 => 'σ',
+  120532 => 'σ',
+  120533 => 'τ',
+  120534 => 'υ',
+  120535 => 'φ',
+  120536 => 'χ',
+  120537 => 'ψ',
+  120538 => 'ω',
+  120539 => '∂',
+  120540 => 'ε',
+  120541 => 'θ',
+  120542 => 'κ',
+  120543 => 'φ',
+  120544 => 'ρ',
+  120545 => 'π',
+  120546 => 'α',
+  120547 => 'β',
+  120548 => 'γ',
+  120549 => 'δ',
+  120550 => 'ε',
+  120551 => 'ζ',
+  120552 => 'η',
+  120553 => 'θ',
+  120554 => 'ι',
+  120555 => 'κ',
+  120556 => 'λ',
+  120557 => 'μ',
+  120558 => 'ν',
+  120559 => 'ξ',
+  120560 => 'ο',
+  120561 => 'π',
+  120562 => 'ρ',
+  120563 => 'θ',
+  120564 => 'σ',
+  120565 => 'τ',
+  120566 => 'υ',
+  120567 => 'φ',
+  120568 => 'χ',
+  120569 => 'ψ',
+  120570 => 'ω',
+  120571 => '∇',
+  120572 => 'α',
+  120573 => 'β',
+  120574 => 'γ',
+  120575 => 'δ',
+  120576 => 'ε',
+  120577 => 'ζ',
+  120578 => 'η',
+  120579 => 'θ',
+  120580 => 'ι',
+  120581 => 'κ',
+  120582 => 'λ',
+  120583 => 'μ',
+  120584 => 'ν',
+  120585 => 'ξ',
+  120586 => 'ο',
+  120587 => 'π',
+  120588 => 'ρ',
+  120589 => 'σ',
+  120590 => 'σ',
+  120591 => 'τ',
+  120592 => 'υ',
+  120593 => 'φ',
+  120594 => 'χ',
+  120595 => 'ψ',
+  120596 => 'ω',
+  120597 => '∂',
+  120598 => 'ε',
+  120599 => 'θ',
+  120600 => 'κ',
+  120601 => 'φ',
+  120602 => 'ρ',
+  120603 => 'π',
+  120604 => 'α',
+  120605 => 'β',
+  120606 => 'γ',
+  120607 => 'δ',
+  120608 => 'ε',
+  120609 => 'ζ',
+  120610 => 'η',
+  120611 => 'θ',
+  120612 => 'ι',
+  120613 => 'κ',
+  120614 => 'λ',
+  120615 => 'μ',
+  120616 => 'ν',
+  120617 => 'ξ',
+  120618 => 'ο',
+  120619 => 'π',
+  120620 => 'ρ',
+  120621 => 'θ',
+  120622 => 'σ',
+  120623 => 'τ',
+  120624 => 'υ',
+  120625 => 'φ',
+  120626 => 'χ',
+  120627 => 'ψ',
+  120628 => 'ω',
+  120629 => '∇',
+  120630 => 'α',
+  120631 => 'β',
+  120632 => 'γ',
+  120633 => 'δ',
+  120634 => 'ε',
+  120635 => 'ζ',
+  120636 => 'η',
+  120637 => 'θ',
+  120638 => 'ι',
+  120639 => 'κ',
+  120640 => 'λ',
+  120641 => 'μ',
+  120642 => 'ν',
+  120643 => 'ξ',
+  120644 => 'ο',
+  120645 => 'π',
+  120646 => 'ρ',
+  120647 => 'σ',
+  120648 => 'σ',
+  120649 => 'τ',
+  120650 => 'υ',
+  120651 => 'φ',
+  120652 => 'χ',
+  120653 => 'ψ',
+  120654 => 'ω',
+  120655 => '∂',
+  120656 => 'ε',
+  120657 => 'θ',
+  120658 => 'κ',
+  120659 => 'φ',
+  120660 => 'ρ',
+  120661 => 'π',
+  120662 => 'α',
+  120663 => 'β',
+  120664 => 'γ',
+  120665 => 'δ',
+  120666 => 'ε',
+  120667 => 'ζ',
+  120668 => 'η',
+  120669 => 'θ',
+  120670 => 'ι',
+  120671 => 'κ',
+  120672 => 'λ',
+  120673 => 'μ',
+  120674 => 'ν',
+  120675 => 'ξ',
+  120676 => 'ο',
+  120677 => 'π',
+  120678 => 'ρ',
+  120679 => 'θ',
+  120680 => 'σ',
+  120681 => 'τ',
+  120682 => 'υ',
+  120683 => 'φ',
+  120684 => 'χ',
+  120685 => 'ψ',
+  120686 => 'ω',
+  120687 => '∇',
+  120688 => 'α',
+  120689 => 'β',
+  120690 => 'γ',
+  120691 => 'δ',
+  120692 => 'ε',
+  120693 => 'ζ',
+  120694 => 'η',
+  120695 => 'θ',
+  120696 => 'ι',
+  120697 => 'κ',
+  120698 => 'λ',
+  120699 => 'μ',
+  120700 => 'ν',
+  120701 => 'ξ',
+  120702 => 'ο',
+  120703 => 'π',
+  120704 => 'ρ',
+  120705 => 'σ',
+  120706 => 'σ',
+  120707 => 'τ',
+  120708 => 'υ',
+  120709 => 'φ',
+  120710 => 'χ',
+  120711 => 'ψ',
+  120712 => 'ω',
+  120713 => '∂',
+  120714 => 'ε',
+  120715 => 'θ',
+  120716 => 'κ',
+  120717 => 'φ',
+  120718 => 'ρ',
+  120719 => 'π',
+  120720 => 'α',
+  120721 => 'β',
+  120722 => 'γ',
+  120723 => 'δ',
+  120724 => 'ε',
+  120725 => 'ζ',
+  120726 => 'η',
+  120727 => 'θ',
+  120728 => 'ι',
+  120729 => 'κ',
+  120730 => 'λ',
+  120731 => 'μ',
+  120732 => 'ν',
+  120733 => 'ξ',
+  120734 => 'ο',
+  120735 => 'π',
+  120736 => 'ρ',
+  120737 => 'θ',
+  120738 => 'σ',
+  120739 => 'τ',
+  120740 => 'υ',
+  120741 => 'φ',
+  120742 => 'χ',
+  120743 => 'ψ',
+  120744 => 'ω',
+  120745 => '∇',
+  120746 => 'α',
+  120747 => 'β',
+  120748 => 'γ',
+  120749 => 'δ',
+  120750 => 'ε',
+  120751 => 'ζ',
+  120752 => 'η',
+  120753 => 'θ',
+  120754 => 'ι',
+  120755 => 'κ',
+  120756 => 'λ',
+  120757 => 'μ',
+  120758 => 'ν',
+  120759 => 'ξ',
+  120760 => 'ο',
+  120761 => 'π',
+  120762 => 'ρ',
+  120763 => 'σ',
+  120764 => 'σ',
+  120765 => 'τ',
+  120766 => 'υ',
+  120767 => 'φ',
+  120768 => 'χ',
+  120769 => 'ψ',
+  120770 => 'ω',
+  120771 => '∂',
+  120772 => 'ε',
+  120773 => 'θ',
+  120774 => 'κ',
+  120775 => 'φ',
+  120776 => 'ρ',
+  120777 => 'π',
+  120778 => 'ϝ',
+  120779 => 'ϝ',
+  120782 => '0',
+  120783 => '1',
+  120784 => '2',
+  120785 => '3',
+  120786 => '4',
+  120787 => '5',
+  120788 => '6',
+  120789 => '7',
+  120790 => '8',
+  120791 => '9',
+  120792 => '0',
+  120793 => '1',
+  120794 => '2',
+  120795 => '3',
+  120796 => '4',
+  120797 => '5',
+  120798 => '6',
+  120799 => '7',
+  120800 => '8',
+  120801 => '9',
+  120802 => '0',
+  120803 => '1',
+  120804 => '2',
+  120805 => '3',
+  120806 => '4',
+  120807 => '5',
+  120808 => '6',
+  120809 => '7',
+  120810 => '8',
+  120811 => '9',
+  120812 => '0',
+  120813 => '1',
+  120814 => '2',
+  120815 => '3',
+  120816 => '4',
+  120817 => '5',
+  120818 => '6',
+  120819 => '7',
+  120820 => '8',
+  120821 => '9',
+  120822 => '0',
+  120823 => '1',
+  120824 => '2',
+  120825 => '3',
+  120826 => '4',
+  120827 => '5',
+  120828 => '6',
+  120829 => '7',
+  120830 => '8',
+  120831 => '9',
+  125184 => '𞤢',
+  125185 => '𞤣',
+  125186 => '𞤤',
+  125187 => '𞤥',
+  125188 => '𞤦',
+  125189 => '𞤧',
+  125190 => '𞤨',
+  125191 => '𞤩',
+  125192 => '𞤪',
+  125193 => '𞤫',
+  125194 => '𞤬',
+  125195 => '𞤭',
+  125196 => '𞤮',
+  125197 => '𞤯',
+  125198 => '𞤰',
+  125199 => '𞤱',
+  125200 => '𞤲',
+  125201 => '𞤳',
+  125202 => '𞤴',
+  125203 => '𞤵',
+  125204 => '𞤶',
+  125205 => '𞤷',
+  125206 => '𞤸',
+  125207 => '𞤹',
+  125208 => '𞤺',
+  125209 => '𞤻',
+  125210 => '𞤼',
+  125211 => '𞤽',
+  125212 => '𞤾',
+  125213 => '𞤿',
+  125214 => '𞥀',
+  125215 => '𞥁',
+  125216 => '𞥂',
+  125217 => '𞥃',
+  126464 => 'ا',
+  126465 => 'ب',
+  126466 => 'ج',
+  126467 => 'د',
+  126469 => 'و',
+  126470 => 'ز',
+  126471 => 'ح',
+  126472 => 'ط',
+  126473 => 'ي',
+  126474 => 'ك',
+  126475 => 'ل',
+  126476 => 'م',
+  126477 => 'ن',
+  126478 => 'س',
+  126479 => 'ع',
+  126480 => 'ف',
+  126481 => 'ص',
+  126482 => 'ق',
+  126483 => 'ر',
+  126484 => 'ش',
+  126485 => 'ت',
+  126486 => 'ث',
+  126487 => 'خ',
+  126488 => 'ذ',
+  126489 => 'ض',
+  126490 => 'ظ',
+  126491 => 'غ',
+  126492 => 'ٮ',
+  126493 => 'ں',
+  126494 => 'ڡ',
+  126495 => 'ٯ',
+  126497 => 'ب',
+  126498 => 'ج',
+  126500 => 'ه',
+  126503 => 'ح',
+  126505 => 'ي',
+  126506 => 'ك',
+  126507 => 'ل',
+  126508 => 'م',
+  126509 => 'ن',
+  126510 => 'س',
+  126511 => 'ع',
+  126512 => 'ف',
+  126513 => 'ص',
+  126514 => 'ق',
+  126516 => 'ش',
+  126517 => 'ت',
+  126518 => 'ث',
+  126519 => 'خ',
+  126521 => 'ض',
+  126523 => 'غ',
+  126530 => 'ج',
+  126535 => 'ح',
+  126537 => 'ي',
+  126539 => 'ل',
+  126541 => 'ن',
+  126542 => 'س',
+  126543 => 'ع',
+  126545 => 'ص',
+  126546 => 'ق',
+  126548 => 'ش',
+  126551 => 'خ',
+  126553 => 'ض',
+  126555 => 'غ',
+  126557 => 'ں',
+  126559 => 'ٯ',
+  126561 => 'ب',
+  126562 => 'ج',
+  126564 => 'ه',
+  126567 => 'ح',
+  126568 => 'ط',
+  126569 => 'ي',
+  126570 => 'ك',
+  126572 => 'م',
+  126573 => 'ن',
+  126574 => 'س',
+  126575 => 'ع',
+  126576 => 'ف',
+  126577 => 'ص',
+  126578 => 'ق',
+  126580 => 'ش',
+  126581 => 'ت',
+  126582 => 'ث',
+  126583 => 'خ',
+  126585 => 'ض',
+  126586 => 'ظ',
+  126587 => 'غ',
+  126588 => 'ٮ',
+  126590 => 'ڡ',
+  126592 => 'ا',
+  126593 => 'ب',
+  126594 => 'ج',
+  126595 => 'د',
+  126596 => 'ه',
+  126597 => 'و',
+  126598 => 'ز',
+  126599 => 'ح',
+  126600 => 'ط',
+  126601 => 'ي',
+  126603 => 'ل',
+  126604 => 'م',
+  126605 => 'ن',
+  126606 => 'س',
+  126607 => 'ع',
+  126608 => 'ف',
+  126609 => 'ص',
+  126610 => 'ق',
+  126611 => 'ر',
+  126612 => 'ش',
+  126613 => 'ت',
+  126614 => 'ث',
+  126615 => 'خ',
+  126616 => 'ذ',
+  126617 => 'ض',
+  126618 => 'ظ',
+  126619 => 'غ',
+  126625 => 'ب',
+  126626 => 'ج',
+  126627 => 'د',
+  126629 => 'و',
+  126630 => 'ز',
+  126631 => 'ح',
+  126632 => 'ط',
+  126633 => 'ي',
+  126635 => 'ل',
+  126636 => 'م',
+  126637 => 'ن',
+  126638 => 'س',
+  126639 => 'ع',
+  126640 => 'ف',
+  126641 => 'ص',
+  126642 => 'ق',
+  126643 => 'ر',
+  126644 => 'ش',
+  126645 => 'ت',
+  126646 => 'ث',
+  126647 => 'خ',
+  126648 => 'ذ',
+  126649 => 'ض',
+  126650 => 'ظ',
+  126651 => 'غ',
+  127274 => '〔s〕',
+  127275 => 'c',
+  127276 => 'r',
+  127277 => 'cd',
+  127278 => 'wz',
+  127280 => 'a',
+  127281 => 'b',
+  127282 => 'c',
+  127283 => 'd',
+  127284 => 'e',
+  127285 => 'f',
+  127286 => 'g',
+  127287 => 'h',
+  127288 => 'i',
+  127289 => 'j',
+  127290 => 'k',
+  127291 => 'l',
+  127292 => 'm',
+  127293 => 'n',
+  127294 => 'o',
+  127295 => 'p',
+  127296 => 'q',
+  127297 => 'r',
+  127298 => 's',
+  127299 => 't',
+  127300 => 'u',
+  127301 => 'v',
+  127302 => 'w',
+  127303 => 'x',
+  127304 => 'y',
+  127305 => 'z',
+  127306 => 'hv',
+  127307 => 'mv',
+  127308 => 'sd',
+  127309 => 'ss',
+  127310 => 'ppv',
+  127311 => 'wc',
+  127338 => 'mc',
+  127339 => 'md',
+  127340 => 'mr',
+  127376 => 'dj',
+  127488 => 'ほか',
+  127489 => 'ココ',
+  127490 => 'サ',
+  127504 => '手',
+  127505 => '字',
+  127506 => '双',
+  127507 => 'デ',
+  127508 => '二',
+  127509 => '多',
+  127510 => '解',
+  127511 => '天',
+  127512 => '交',
+  127513 => '映',
+  127514 => '無',
+  127515 => '料',
+  127516 => '前',
+  127517 => '後',
+  127518 => '再',
+  127519 => '新',
+  127520 => '初',
+  127521 => '終',
+  127522 => '生',
+  127523 => '販',
+  127524 => '声',
+  127525 => '吹',
+  127526 => '演',
+  127527 => '投',
+  127528 => '捕',
+  127529 => '一',
+  127530 => '三',
+  127531 => '遊',
+  127532 => '左',
+  127533 => '中',
+  127534 => '右',
+  127535 => '指',
+  127536 => '走',
+  127537 => '打',
+  127538 => '禁',
+  127539 => '空',
+  127540 => '合',
+  127541 => '満',
+  127542 => '有',
+  127543 => '月',
+  127544 => '申',
+  127545 => '割',
+  127546 => '営',
+  127547 => '配',
+  127552 => '〔本〕',
+  127553 => '〔三〕',
+  127554 => '〔二〕',
+  127555 => '〔安〕',
+  127556 => '〔点〕',
+  127557 => '〔打〕',
+  127558 => '〔盗〕',
+  127559 => '〔勝〕',
+  127560 => '〔敗〕',
+  127568 => '得',
+  127569 => '可',
+  130032 => '0',
+  130033 => '1',
+  130034 => '2',
+  130035 => '3',
+  130036 => '4',
+  130037 => '5',
+  130038 => '6',
+  130039 => '7',
+  130040 => '8',
+  130041 => '9',
+  194560 => '丽',
+  194561 => '丸',
+  194562 => '乁',
+  194563 => '𠄢',
+  194564 => '你',
+  194565 => '侮',
+  194566 => '侻',
+  194567 => '倂',
+  194568 => '偺',
+  194569 => '備',
+  194570 => '僧',
+  194571 => '像',
+  194572 => '㒞',
+  194573 => '𠘺',
+  194574 => '免',
+  194575 => '兔',
+  194576 => '兤',
+  194577 => '具',
+  194578 => '𠔜',
+  194579 => '㒹',
+  194580 => '內',
+  194581 => '再',
+  194582 => '𠕋',
+  194583 => '冗',
+  194584 => '冤',
+  194585 => '仌',
+  194586 => '冬',
+  194587 => '况',
+  194588 => '𩇟',
+  194589 => '凵',
+  194590 => '刃',
+  194591 => '㓟',
+  194592 => '刻',
+  194593 => '剆',
+  194594 => '割',
+  194595 => '剷',
+  194596 => '㔕',
+  194597 => '勇',
+  194598 => '勉',
+  194599 => '勤',
+  194600 => '勺',
+  194601 => '包',
+  194602 => '匆',
+  194603 => '北',
+  194604 => '卉',
+  194605 => '卑',
+  194606 => '博',
+  194607 => '即',
+  194608 => '卽',
+  194609 => '卿',
+  194610 => '卿',
+  194611 => '卿',
+  194612 => '𠨬',
+  194613 => '灰',
+  194614 => '及',
+  194615 => '叟',
+  194616 => '𠭣',
+  194617 => '叫',
+  194618 => '叱',
+  194619 => '吆',
+  194620 => '咞',
+  194621 => '吸',
+  194622 => '呈',
+  194623 => '周',
+  194624 => '咢',
+  194625 => '哶',
+  194626 => '唐',
+  194627 => '啓',
+  194628 => '啣',
+  194629 => '善',
+  194630 => '善',
+  194631 => '喙',
+  194632 => '喫',
+  194633 => '喳',
+  194634 => '嗂',
+  194635 => '圖',
+  194636 => '嘆',
+  194637 => '圗',
+  194638 => '噑',
+  194639 => '噴',
+  194640 => '切',
+  194641 => '壮',
+  194642 => '城',
+  194643 => '埴',
+  194644 => '堍',
+  194645 => '型',
+  194646 => '堲',
+  194647 => '報',
+  194648 => '墬',
+  194649 => '𡓤',
+  194650 => '売',
+  194651 => '壷',
+  194652 => '夆',
+  194653 => '多',
+  194654 => '夢',
+  194655 => '奢',
+  194656 => '𡚨',
+  194657 => '𡛪',
+  194658 => '姬',
+  194659 => '娛',
+  194660 => '娧',
+  194661 => '姘',
+  194662 => '婦',
+  194663 => '㛮',
+  194665 => '嬈',
+  194666 => '嬾',
+  194667 => '嬾',
+  194668 => '𡧈',
+  194669 => '寃',
+  194670 => '寘',
+  194671 => '寧',
+  194672 => '寳',
+  194673 => '𡬘',
+  194674 => '寿',
+  194675 => '将',
+  194677 => '尢',
+  194678 => '㞁',
+  194679 => '屠',
+  194680 => '屮',
+  194681 => '峀',
+  194682 => '岍',
+  194683 => '𡷤',
+  194684 => '嵃',
+  194685 => '𡷦',
+  194686 => '嵮',
+  194687 => '嵫',
+  194688 => '嵼',
+  194689 => '巡',
+  194690 => '巢',
+  194691 => '㠯',
+  194692 => '巽',
+  194693 => '帨',
+  194694 => '帽',
+  194695 => '幩',
+  194696 => '㡢',
+  194697 => '𢆃',
+  194698 => '㡼',
+  194699 => '庰',
+  194700 => '庳',
+  194701 => '庶',
+  194702 => '廊',
+  194703 => '𪎒',
+  194704 => '廾',
+  194705 => '𢌱',
+  194706 => '𢌱',
+  194707 => '舁',
+  194708 => '弢',
+  194709 => '弢',
+  194710 => '㣇',
+  194711 => '𣊸',
+  194712 => '𦇚',
+  194713 => '形',
+  194714 => '彫',
+  194715 => '㣣',
+  194716 => '徚',
+  194717 => '忍',
+  194718 => '志',
+  194719 => '忹',
+  194720 => '悁',
+  194721 => '㤺',
+  194722 => '㤜',
+  194723 => '悔',
+  194724 => '𢛔',
+  194725 => '惇',
+  194726 => '慈',
+  194727 => '慌',
+  194728 => '慎',
+  194729 => '慌',
+  194730 => '慺',
+  194731 => '憎',
+  194732 => '憲',
+  194733 => '憤',
+  194734 => '憯',
+  194735 => '懞',
+  194736 => '懲',
+  194737 => '懶',
+  194738 => '成',
+  194739 => '戛',
+  194740 => '扝',
+  194741 => '抱',
+  194742 => '拔',
+  194743 => '捐',
+  194744 => '𢬌',
+  194745 => '挽',
+  194746 => '拼',
+  194747 => '捨',
+  194748 => '掃',
+  194749 => '揤',
+  194750 => '𢯱',
+  194751 => '搢',
+  194752 => '揅',
+  194753 => '掩',
+  194754 => '㨮',
+  194755 => '摩',
+  194756 => '摾',
+  194757 => '撝',
+  194758 => '摷',
+  194759 => '㩬',
+  194760 => '敏',
+  194761 => '敬',
+  194762 => '𣀊',
+  194763 => '旣',
+  194764 => '書',
+  194765 => '晉',
+  194766 => '㬙',
+  194767 => '暑',
+  194768 => '㬈',
+  194769 => '㫤',
+  194770 => '冒',
+  194771 => '冕',
+  194772 => '最',
+  194773 => '暜',
+  194774 => '肭',
+  194775 => '䏙',
+  194776 => '朗',
+  194777 => '望',
+  194778 => '朡',
+  194779 => '杞',
+  194780 => '杓',
+  194781 => '𣏃',
+  194782 => '㭉',
+  194783 => '柺',
+  194784 => '枅',
+  194785 => '桒',
+  194786 => '梅',
+  194787 => '𣑭',
+  194788 => '梎',
+  194789 => '栟',
+  194790 => '椔',
+  194791 => '㮝',
+  194792 => '楂',
+  194793 => '榣',
+  194794 => '槪',
+  194795 => '檨',
+  194796 => '𣚣',
+  194797 => '櫛',
+  194798 => '㰘',
+  194799 => '次',
+  194800 => '𣢧',
+  194801 => '歔',
+  194802 => '㱎',
+  194803 => '歲',
+  194804 => '殟',
+  194805 => '殺',
+  194806 => '殻',
+  194807 => '𣪍',
+  194808 => '𡴋',
+  194809 => '𣫺',
+  194810 => '汎',
+  194811 => '𣲼',
+  194812 => '沿',
+  194813 => '泍',
+  194814 => '汧',
+  194815 => '洖',
+  194816 => '派',
+  194817 => '海',
+  194818 => '流',
+  194819 => '浩',
+  194820 => '浸',
+  194821 => '涅',
+  194822 => '𣴞',
+  194823 => '洴',
+  194824 => '港',
+  194825 => '湮',
+  194826 => '㴳',
+  194827 => '滋',
+  194828 => '滇',
+  194829 => '𣻑',
+  194830 => '淹',
+  194831 => '潮',
+  194832 => '𣽞',
+  194833 => '𣾎',
+  194834 => '濆',
+  194835 => '瀹',
+  194836 => '瀞',
+  194837 => '瀛',
+  194838 => '㶖',
+  194839 => '灊',
+  194840 => '災',
+  194841 => '灷',
+  194842 => '炭',
+  194843 => '𠔥',
+  194844 => '煅',
+  194845 => '𤉣',
+  194846 => '熜',
+  194848 => '爨',
+  194849 => '爵',
+  194850 => '牐',
+  194851 => '𤘈',
+  194852 => '犀',
+  194853 => '犕',
+  194854 => '𤜵',
+  194855 => '𤠔',
+  194856 => '獺',
+  194857 => '王',
+  194858 => '㺬',
+  194859 => '玥',
+  194860 => '㺸',
+  194861 => '㺸',
+  194862 => '瑇',
+  194863 => '瑜',
+  194864 => '瑱',
+  194865 => '璅',
+  194866 => '瓊',
+  194867 => '㼛',
+  194868 => '甤',
+  194869 => '𤰶',
+  194870 => '甾',
+  194871 => '𤲒',
+  194872 => '異',
+  194873 => '𢆟',
+  194874 => '瘐',
+  194875 => '𤾡',
+  194876 => '𤾸',
+  194877 => '𥁄',
+  194878 => '㿼',
+  194879 => '䀈',
+  194880 => '直',
+  194881 => '𥃳',
+  194882 => '𥃲',
+  194883 => '𥄙',
+  194884 => '𥄳',
+  194885 => '眞',
+  194886 => '真',
+  194887 => '真',
+  194888 => '睊',
+  194889 => '䀹',
+  194890 => '瞋',
+  194891 => '䁆',
+  194892 => '䂖',
+  194893 => '𥐝',
+  194894 => '硎',
+  194895 => '碌',
+  194896 => '磌',
+  194897 => '䃣',
+  194898 => '𥘦',
+  194899 => '祖',
+  194900 => '𥚚',
+  194901 => '𥛅',
+  194902 => '福',
+  194903 => '秫',
+  194904 => '䄯',
+  194905 => '穀',
+  194906 => '穊',
+  194907 => '穏',
+  194908 => '𥥼',
+  194909 => '𥪧',
+  194910 => '𥪧',
+  194912 => '䈂',
+  194913 => '𥮫',
+  194914 => '篆',
+  194915 => '築',
+  194916 => '䈧',
+  194917 => '𥲀',
+  194918 => '糒',
+  194919 => '䊠',
+  194920 => '糨',
+  194921 => '糣',
+  194922 => '紀',
+  194923 => '𥾆',
+  194924 => '絣',
+  194925 => '䌁',
+  194926 => '緇',
+  194927 => '縂',
+  194928 => '繅',
+  194929 => '䌴',
+  194930 => '𦈨',
+  194931 => '𦉇',
+  194932 => '䍙',
+  194933 => '𦋙',
+  194934 => '罺',
+  194935 => '𦌾',
+  194936 => '羕',
+  194937 => '翺',
+  194938 => '者',
+  194939 => '𦓚',
+  194940 => '𦔣',
+  194941 => '聠',
+  194942 => '𦖨',
+  194943 => '聰',
+  194944 => '𣍟',
+  194945 => '䏕',
+  194946 => '育',
+  194947 => '脃',
+  194948 => '䐋',
+  194949 => '脾',
+  194950 => '媵',
+  194951 => '𦞧',
+  194952 => '𦞵',
+  194953 => '𣎓',
+  194954 => '𣎜',
+  194955 => '舁',
+  194956 => '舄',
+  194957 => '辞',
+  194958 => '䑫',
+  194959 => '芑',
+  194960 => '芋',
+  194961 => '芝',
+  194962 => '劳',
+  194963 => '花',
+  194964 => '芳',
+  194965 => '芽',
+  194966 => '苦',
+  194967 => '𦬼',
+  194968 => '若',
+  194969 => '茝',
+  194970 => '荣',
+  194971 => '莭',
+  194972 => '茣',
+  194973 => '莽',
+  194974 => '菧',
+  194975 => '著',
+  194976 => '荓',
+  194977 => '菊',
+  194978 => '菌',
+  194979 => '菜',
+  194980 => '𦰶',
+  194981 => '𦵫',
+  194982 => '𦳕',
+  194983 => '䔫',
+  194984 => '蓱',
+  194985 => '蓳',
+  194986 => '蔖',
+  194987 => '𧏊',
+  194988 => '蕤',
+  194989 => '𦼬',
+  194990 => '䕝',
+  194991 => '䕡',
+  194992 => '𦾱',
+  194993 => '𧃒',
+  194994 => '䕫',
+  194995 => '虐',
+  194996 => '虜',
+  194997 => '虧',
+  194998 => '虩',
+  194999 => '蚩',
+  195000 => '蚈',
+  195001 => '蜎',
+  195002 => '蛢',
+  195003 => '蝹',
+  195004 => '蜨',
+  195005 => '蝫',
+  195006 => '螆',
+  195008 => '蟡',
+  195009 => '蠁',
+  195010 => '䗹',
+  195011 => '衠',
+  195012 => '衣',
+  195013 => '𧙧',
+  195014 => '裗',
+  195015 => '裞',
+  195016 => '䘵',
+  195017 => '裺',
+  195018 => '㒻',
+  195019 => '𧢮',
+  195020 => '𧥦',
+  195021 => '䚾',
+  195022 => '䛇',
+  195023 => '誠',
+  195024 => '諭',
+  195025 => '變',
+  195026 => '豕',
+  195027 => '𧲨',
+  195028 => '貫',
+  195029 => '賁',
+  195030 => '贛',
+  195031 => '起',
+  195032 => '𧼯',
+  195033 => '𠠄',
+  195034 => '跋',
+  195035 => '趼',
+  195036 => '跰',
+  195037 => '𠣞',
+  195038 => '軔',
+  195039 => '輸',
+  195040 => '𨗒',
+  195041 => '𨗭',
+  195042 => '邔',
+  195043 => '郱',
+  195044 => '鄑',
+  195045 => '𨜮',
+  195046 => '鄛',
+  195047 => '鈸',
+  195048 => '鋗',
+  195049 => '鋘',
+  195050 => '鉼',
+  195051 => '鏹',
+  195052 => '鐕',
+  195053 => '𨯺',
+  195054 => '開',
+  195055 => '䦕',
+  195056 => '閷',
+  195057 => '𨵷',
+  195058 => '䧦',
+  195059 => '雃',
+  195060 => '嶲',
+  195061 => '霣',
+  195062 => '𩅅',
+  195063 => '𩈚',
+  195064 => '䩮',
+  195065 => '䩶',
+  195066 => '韠',
+  195067 => '𩐊',
+  195068 => '䪲',
+  195069 => '𩒖',
+  195070 => '頋',
+  195071 => '頋',
+  195072 => '頩',
+  195073 => '𩖶',
+  195074 => '飢',
+  195075 => '䬳',
+  195076 => '餩',
+  195077 => '馧',
+  195078 => '駂',
+  195079 => '駾',
+  195080 => '䯎',
+  195081 => '𩬰',
+  195082 => '鬒',
+  195083 => '鱀',
+  195084 => '鳽',
+  195085 => '䳎',
+  195086 => '䳭',
+  195087 => '鵧',
+  195088 => '𪃎',
+  195089 => '䳸',
+  195090 => '𪄅',
+  195091 => '𪈎',
+  195092 => '𪊑',
+  195093 => '麻',
+  195094 => '䵖',
+  195095 => '黹',
+  195096 => '黾',
+  195097 => '鼅',
+  195098 => '鼏',
+  195099 => '鼖',
+  195100 => '鼻',
+  195101 => '𪘀',
+);
diff --git a/vendor/symfony/polyfill-intl-idn/Resources/unidata/virama.php b/vendor/symfony/polyfill-intl-idn/Resources/unidata/virama.php
new file mode 100755
index 0000000..1958e37
--- /dev/null
+++ b/vendor/symfony/polyfill-intl-idn/Resources/unidata/virama.php
@@ -0,0 +1,65 @@
+<?php
+
+return array (
+  2381 => 9,
+  2509 => 9,
+  2637 => 9,
+  2765 => 9,
+  2893 => 9,
+  3021 => 9,
+  3149 => 9,
+  3277 => 9,
+  3387 => 9,
+  3388 => 9,
+  3405 => 9,
+  3530 => 9,
+  3642 => 9,
+  3770 => 9,
+  3972 => 9,
+  4153 => 9,
+  4154 => 9,
+  5908 => 9,
+  5940 => 9,
+  6098 => 9,
+  6752 => 9,
+  6980 => 9,
+  7082 => 9,
+  7083 => 9,
+  7154 => 9,
+  7155 => 9,
+  11647 => 9,
+  43014 => 9,
+  43052 => 9,
+  43204 => 9,
+  43347 => 9,
+  43456 => 9,
+  43766 => 9,
+  44013 => 9,
+  68159 => 9,
+  69702 => 9,
+  69759 => 9,
+  69817 => 9,
+  69939 => 9,
+  69940 => 9,
+  70080 => 9,
+  70197 => 9,
+  70378 => 9,
+  70477 => 9,
+  70722 => 9,
+  70850 => 9,
+  71103 => 9,
+  71231 => 9,
+  71350 => 9,
+  71467 => 9,
+  71737 => 9,
+  71997 => 9,
+  71998 => 9,
+  72160 => 9,
+  72244 => 9,
+  72263 => 9,
+  72345 => 9,
+  72767 => 9,
+  73028 => 9,
+  73029 => 9,
+  73111 => 9,
+);
diff --git a/vendor/symfony/polyfill-intl-idn/bootstrap.php b/vendor/symfony/polyfill-intl-idn/bootstrap.php
new file mode 100755
index 0000000..f02d5de
--- /dev/null
+++ b/vendor/symfony/polyfill-intl-idn/bootstrap.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.
+ */
+
+use Symfony\Polyfill\Intl\Idn as p;
+
+if (extension_loaded('intl')) {
+    return;
+}
+
+if (!defined('U_IDNA_PROHIBITED_ERROR')) {
+    define('U_IDNA_PROHIBITED_ERROR', 66560);
+}
+if (!defined('U_IDNA_ERROR_START')) {
+    define('U_IDNA_ERROR_START', 66560);
+}
+if (!defined('U_IDNA_UNASSIGNED_ERROR')) {
+    define('U_IDNA_UNASSIGNED_ERROR', 66561);
+}
+if (!defined('U_IDNA_CHECK_BIDI_ERROR')) {
+    define('U_IDNA_CHECK_BIDI_ERROR', 66562);
+}
+if (!defined('U_IDNA_STD3_ASCII_RULES_ERROR')) {
+    define('U_IDNA_STD3_ASCII_RULES_ERROR', 66563);
+}
+if (!defined('U_IDNA_ACE_PREFIX_ERROR')) {
+    define('U_IDNA_ACE_PREFIX_ERROR', 66564);
+}
+if (!defined('U_IDNA_VERIFICATION_ERROR')) {
+    define('U_IDNA_VERIFICATION_ERROR', 66565);
+}
+if (!defined('U_IDNA_LABEL_TOO_LONG_ERROR')) {
+    define('U_IDNA_LABEL_TOO_LONG_ERROR', 66566);
+}
+if (!defined('U_IDNA_ZERO_LENGTH_LABEL_ERROR')) {
+    define('U_IDNA_ZERO_LENGTH_LABEL_ERROR', 66567);
+}
+if (!defined('U_IDNA_DOMAIN_NAME_TOO_LONG_ERROR')) {
+    define('U_IDNA_DOMAIN_NAME_TOO_LONG_ERROR', 66568);
+}
+if (!defined('U_IDNA_ERROR_LIMIT')) {
+    define('U_IDNA_ERROR_LIMIT', 66569);
+}
+if (!defined('U_STRINGPREP_PROHIBITED_ERROR')) {
+    define('U_STRINGPREP_PROHIBITED_ERROR', 66560);
+}
+if (!defined('U_STRINGPREP_UNASSIGNED_ERROR')) {
+    define('U_STRINGPREP_UNASSIGNED_ERROR', 66561);
+}
+if (!defined('U_STRINGPREP_CHECK_BIDI_ERROR')) {
+    define('U_STRINGPREP_CHECK_BIDI_ERROR', 66562);
+}
+if (!defined('IDNA_DEFAULT')) {
+    define('IDNA_DEFAULT', 0);
+}
+if (!defined('IDNA_ALLOW_UNASSIGNED')) {
+    define('IDNA_ALLOW_UNASSIGNED', 1);
+}
+if (!defined('IDNA_USE_STD3_RULES')) {
+    define('IDNA_USE_STD3_RULES', 2);
+}
+if (!defined('IDNA_CHECK_BIDI')) {
+    define('IDNA_CHECK_BIDI', 4);
+}
+if (!defined('IDNA_CHECK_CONTEXTJ')) {
+    define('IDNA_CHECK_CONTEXTJ', 8);
+}
+if (!defined('IDNA_NONTRANSITIONAL_TO_ASCII')) {
+    define('IDNA_NONTRANSITIONAL_TO_ASCII', 16);
+}
+if (!defined('IDNA_NONTRANSITIONAL_TO_UNICODE')) {
+    define('IDNA_NONTRANSITIONAL_TO_UNICODE', 32);
+}
+if (!defined('INTL_IDNA_VARIANT_2003')) {
+    define('INTL_IDNA_VARIANT_2003', 0);
+}
+if (!defined('INTL_IDNA_VARIANT_UTS46')) {
+    define('INTL_IDNA_VARIANT_UTS46', 1);
+}
+if (!defined('IDNA_ERROR_EMPTY_LABEL')) {
+    define('IDNA_ERROR_EMPTY_LABEL', 1);
+}
+if (!defined('IDNA_ERROR_LABEL_TOO_LONG')) {
+    define('IDNA_ERROR_LABEL_TOO_LONG', 2);
+}
+if (!defined('IDNA_ERROR_DOMAIN_NAME_TOO_LONG')) {
+    define('IDNA_ERROR_DOMAIN_NAME_TOO_LONG', 4);
+}
+if (!defined('IDNA_ERROR_LEADING_HYPHEN')) {
+    define('IDNA_ERROR_LEADING_HYPHEN', 8);
+}
+if (!defined('IDNA_ERROR_TRAILING_HYPHEN')) {
+    define('IDNA_ERROR_TRAILING_HYPHEN', 16);
+}
+if (!defined('IDNA_ERROR_HYPHEN_3_4')) {
+    define('IDNA_ERROR_HYPHEN_3_4', 32);
+}
+if (!defined('IDNA_ERROR_LEADING_COMBINING_MARK')) {
+    define('IDNA_ERROR_LEADING_COMBINING_MARK', 64);
+}
+if (!defined('IDNA_ERROR_DISALLOWED')) {
+    define('IDNA_ERROR_DISALLOWED', 128);
+}
+if (!defined('IDNA_ERROR_PUNYCODE')) {
+    define('IDNA_ERROR_PUNYCODE', 256);
+}
+if (!defined('IDNA_ERROR_LABEL_HAS_DOT')) {
+    define('IDNA_ERROR_LABEL_HAS_DOT', 512);
+}
+if (!defined('IDNA_ERROR_INVALID_ACE_LABEL')) {
+    define('IDNA_ERROR_INVALID_ACE_LABEL', 1024);
+}
+if (!defined('IDNA_ERROR_BIDI')) {
+    define('IDNA_ERROR_BIDI', 2048);
+}
+if (!defined('IDNA_ERROR_CONTEXTJ')) {
+    define('IDNA_ERROR_CONTEXTJ', 4096);
+}
+
+if (PHP_VERSION_ID < 70400) {
+    if (!function_exists('idn_to_ascii')) {
+        function idn_to_ascii($domain, $options = IDNA_DEFAULT, $variant = INTL_IDNA_VARIANT_2003, &$idna_info = array()) { return p\Idn::idn_to_ascii($domain, $options, $variant, $idna_info); }
+    }
+    if (!function_exists('idn_to_utf8')) {
+        function idn_to_utf8($domain, $options = IDNA_DEFAULT, $variant = INTL_IDNA_VARIANT_2003, &$idna_info = array()) { return p\Idn::idn_to_utf8($domain, $options, $variant, $idna_info); }
+    }
+} else {
+    if (!function_exists('idn_to_ascii')) {
+        function idn_to_ascii($domain, $options = IDNA_DEFAULT, $variant = INTL_IDNA_VARIANT_UTS46, &$idna_info = array()) { return p\Idn::idn_to_ascii($domain, $options, $variant, $idna_info); }
+    }
+    if (!function_exists('idn_to_utf8')) {
+        function idn_to_utf8($domain, $options = IDNA_DEFAULT, $variant = INTL_IDNA_VARIANT_UTS46, &$idna_info = array()) { return p\Idn::idn_to_utf8($domain, $options, $variant, $idna_info); }
+    }
+}
diff --git a/vendor/symfony/polyfill-intl-idn/composer.json b/vendor/symfony/polyfill-intl-idn/composer.json
new file mode 100755
index 0000000..af34193
--- /dev/null
+++ b/vendor/symfony/polyfill-intl-idn/composer.json
@@ -0,0 +1,45 @@
+{
+    "name": "symfony/polyfill-intl-idn",
+    "type": "library",
+    "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
+    "keywords": ["polyfill", "shim", "compatibility", "portable", "intl", "idn"],
+    "homepage": "https://symfony.com",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Laurent Bassin",
+            "email": "laurent@bassin.info"
+        },
+        {
+            "name": "Trevor Rowbotham",
+            "email": "trevor.rowbotham@pm.me"
+        },
+        {
+            "name": "Symfony Community",
+            "homepage": "https://symfony.com/contributors"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.3",
+        "symfony/polyfill-intl-normalizer": "^1.10",
+        "symfony/polyfill-php70": "^1.10",
+        "symfony/polyfill-php72": "^1.10"
+    },
+    "autoload": {
+        "psr-4": { "Symfony\\Polyfill\\Intl\\Idn\\": "" },
+        "files": [ "bootstrap.php" ]
+    },
+    "suggest": {
+        "ext-intl": "For best performance"
+    },
+    "minimum-stability": "dev",
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.18-dev"
+        },
+        "thanks": {
+            "name": "symfony/polyfill",
+            "url": "https://github.com/symfony/polyfill"
+        }
+    }
+}
diff --git a/vendor/symfony/polyfill-intl-normalizer/LICENSE b/vendor/symfony/polyfill-intl-normalizer/LICENSE
new file mode 100755
index 0000000..4cd8bdd
--- /dev/null
+++ b/vendor/symfony/polyfill-intl-normalizer/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015-2019 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.
diff --git a/vendor/symfony/polyfill-intl-normalizer/Normalizer.php b/vendor/symfony/polyfill-intl-normalizer/Normalizer.php
new file mode 100755
index 0000000..a60fae6
--- /dev/null
+++ b/vendor/symfony/polyfill-intl-normalizer/Normalizer.php
@@ -0,0 +1,308 @@
+<?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\Polyfill\Intl\Normalizer;
+
+/**
+ * Normalizer is a PHP fallback implementation of the Normalizer class provided by the intl extension.
+ *
+ * It has been validated with Unicode 6.3 Normalization Conformance Test.
+ * See http://www.unicode.org/reports/tr15/ for detailed info about Unicode normalizations.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ *
+ * @internal
+ */
+class Normalizer
+{
+    const FORM_D = \Normalizer::FORM_D;
+    const FORM_KD = \Normalizer::FORM_KD;
+    const FORM_C = \Normalizer::FORM_C;
+    const FORM_KC = \Normalizer::FORM_KC;
+    const NFD = \Normalizer::NFD;
+    const NFKD = \Normalizer::NFKD;
+    const NFC = \Normalizer::NFC;
+    const NFKC = \Normalizer::NFKC;
+
+    private static $C;
+    private static $D;
+    private static $KD;
+    private static $cC;
+    private static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4);
+    private static $ASCII = "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F";
+
+    public static function isNormalized($s, $form = self::NFC)
+    {
+        if (!\in_array($form, array(self::NFD, self::NFKD, self::NFC, self::NFKC))) {
+            return false;
+        }
+        $s = (string) $s;
+        if (!isset($s[strspn($s, self::$ASCII)])) {
+            return true;
+        }
+        if (self::NFC == $form && preg_match('//u', $s) && !preg_match('/[^\x00-\x{2FF}]/u', $s)) {
+            return true;
+        }
+
+        return self::normalize($s, $form) === $s;
+    }
+
+    public static function normalize($s, $form = self::NFC)
+    {
+        $s = (string) $s;
+        if (!preg_match('//u', $s)) {
+            return false;
+        }
+
+        switch ($form) {
+            case self::NFC: $C = true; $K = false; break;
+            case self::NFD: $C = false; $K = false; break;
+            case self::NFKC: $C = true; $K = true; break;
+            case self::NFKD: $C = false; $K = true; break;
+            default:
+                if (\defined('Normalizer::NONE') && \Normalizer::NONE == $form) {
+                    return $s;
+                }
+
+                return false;
+        }
+
+        if ('' === $s) {
+            return '';
+        }
+
+        if ($K && null === self::$KD) {
+            self::$KD = self::getData('compatibilityDecomposition');
+        }
+
+        if (null === self::$D) {
+            self::$D = self::getData('canonicalDecomposition');
+            self::$cC = self::getData('combiningClass');
+        }
+
+        if (null !== $mbEncoding = (2 /* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) ? mb_internal_encoding() : null) {
+            mb_internal_encoding('8bit');
+        }
+
+        $r = self::decompose($s, $K);
+
+        if ($C) {
+            if (null === self::$C) {
+                self::$C = self::getData('canonicalComposition');
+            }
+
+            $r = self::recompose($r);
+        }
+        if (null !== $mbEncoding) {
+            mb_internal_encoding($mbEncoding);
+        }
+
+        return $r;
+    }
+
+    private static function recompose($s)
+    {
+        $ASCII = self::$ASCII;
+        $compMap = self::$C;
+        $combClass = self::$cC;
+        $ulenMask = self::$ulenMask;
+
+        $result = $tail = '';
+
+        $i = $s[0] < "\x80" ? 1 : $ulenMask[$s[0] & "\xF0"];
+        $len = \strlen($s);
+
+        $lastUchr = substr($s, 0, $i);
+        $lastUcls = isset($combClass[$lastUchr]) ? 256 : 0;
+
+        while ($i < $len) {
+            if ($s[$i] < "\x80") {
+                // ASCII chars
+
+                if ($tail) {
+                    $lastUchr .= $tail;
+                    $tail = '';
+                }
+
+                if ($j = strspn($s, $ASCII, $i + 1)) {
+                    $lastUchr .= substr($s, $i, $j);
+                    $i += $j;
+                }
+
+                $result .= $lastUchr;
+                $lastUchr = $s[$i];
+                $lastUcls = 0;
+                ++$i;
+                continue;
+            }
+
+            $ulen = $ulenMask[$s[$i] & "\xF0"];
+            $uchr = substr($s, $i, $ulen);
+
+            if ($lastUchr < "\xE1\x84\x80" || "\xE1\x84\x92" < $lastUchr
+                || $uchr < "\xE1\x85\xA1" || "\xE1\x85\xB5" < $uchr
+                || $lastUcls) {
+                // Table lookup and combining chars composition
+
+                $ucls = isset($combClass[$uchr]) ? $combClass[$uchr] : 0;
+
+                if (isset($compMap[$lastUchr.$uchr]) && (!$lastUcls || $lastUcls < $ucls)) {
+                    $lastUchr = $compMap[$lastUchr.$uchr];
+                } elseif ($lastUcls = $ucls) {
+                    $tail .= $uchr;
+                } else {
+                    if ($tail) {
+                        $lastUchr .= $tail;
+                        $tail = '';
+                    }
+
+                    $result .= $lastUchr;
+                    $lastUchr = $uchr;
+                }
+            } else {
+                // Hangul chars
+
+                $L = \ord($lastUchr[2]) - 0x80;
+                $V = \ord($uchr[2]) - 0xA1;
+                $T = 0;
+
+                $uchr = substr($s, $i + $ulen, 3);
+
+                if ("\xE1\x86\xA7" <= $uchr && $uchr <= "\xE1\x87\x82") {
+                    $T = \ord($uchr[2]) - 0xA7;
+                    0 > $T && $T += 0x40;
+                    $ulen += 3;
+                }
+
+                $L = 0xAC00 + ($L * 21 + $V) * 28 + $T;
+                $lastUchr = \chr(0xE0 | $L >> 12).\chr(0x80 | $L >> 6 & 0x3F).\chr(0x80 | $L & 0x3F);
+            }
+
+            $i += $ulen;
+        }
+
+        return $result.$lastUchr.$tail;
+    }
+
+    private static function decompose($s, $c)
+    {
+        $result = '';
+
+        $ASCII = self::$ASCII;
+        $decompMap = self::$D;
+        $combClass = self::$cC;
+        $ulenMask = self::$ulenMask;
+        if ($c) {
+            $compatMap = self::$KD;
+        }
+
+        $c = array();
+        $i = 0;
+        $len = \strlen($s);
+
+        while ($i < $len) {
+            if ($s[$i] < "\x80") {
+                // ASCII chars
+
+                if ($c) {
+                    ksort($c);
+                    $result .= implode('', $c);
+                    $c = array();
+                }
+
+                $j = 1 + strspn($s, $ASCII, $i + 1);
+                $result .= substr($s, $i, $j);
+                $i += $j;
+                continue;
+            }
+
+            $ulen = $ulenMask[$s[$i] & "\xF0"];
+            $uchr = substr($s, $i, $ulen);
+            $i += $ulen;
+
+            if ($uchr < "\xEA\xB0\x80" || "\xED\x9E\xA3" < $uchr) {
+                // Table lookup
+
+                if ($uchr !== $j = isset($compatMap[$uchr]) ? $compatMap[$uchr] : (isset($decompMap[$uchr]) ? $decompMap[$uchr] : $uchr)) {
+                    $uchr = $j;
+
+                    $j = \strlen($uchr);
+                    $ulen = $uchr[0] < "\x80" ? 1 : $ulenMask[$uchr[0] & "\xF0"];
+
+                    if ($ulen != $j) {
+                        // Put trailing chars in $s
+
+                        $j -= $ulen;
+                        $i -= $j;
+
+                        if (0 > $i) {
+                            $s = str_repeat(' ', -$i).$s;
+                            $len -= $i;
+                            $i = 0;
+                        }
+
+                        while ($j--) {
+                            $s[$i + $j] = $uchr[$ulen + $j];
+                        }
+
+                        $uchr = substr($uchr, 0, $ulen);
+                    }
+                }
+                if (isset($combClass[$uchr])) {
+                    // Combining chars, for sorting
+
+                    if (!isset($c[$combClass[$uchr]])) {
+                        $c[$combClass[$uchr]] = '';
+                    }
+                    $c[$combClass[$uchr]] .= $uchr;
+                    continue;
+                }
+            } else {
+                // Hangul chars
+
+                $uchr = unpack('C*', $uchr);
+                $j = (($uchr[1] - 224) << 12) + (($uchr[2] - 128) << 6) + $uchr[3] - 0xAC80;
+
+                $uchr = "\xE1\x84".\chr(0x80 + (int) ($j / 588))
+                       ."\xE1\x85".\chr(0xA1 + (int) (($j % 588) / 28));
+
+                if ($j %= 28) {
+                    $uchr .= $j < 25
+                        ? ("\xE1\x86".\chr(0xA7 + $j))
+                        : ("\xE1\x87".\chr(0x67 + $j));
+                }
+            }
+            if ($c) {
+                ksort($c);
+                $result .= implode('', $c);
+                $c = array();
+            }
+
+            $result .= $uchr;
+        }
+
+        if ($c) {
+            ksort($c);
+            $result .= implode('', $c);
+        }
+
+        return $result;
+    }
+
+    private static function getData($file)
+    {
+        if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) {
+            return require $file;
+        }
+
+        return false;
+    }
+}
diff --git a/vendor/symfony/polyfill-intl-normalizer/README.md b/vendor/symfony/polyfill-intl-normalizer/README.md
new file mode 100755
index 0000000..15060c5
--- /dev/null
+++ b/vendor/symfony/polyfill-intl-normalizer/README.md
@@ -0,0 +1,14 @@
+Symfony Polyfill / Intl: Normalizer
+===================================
+
+This component provides a fallback implementation for the
+[`Normalizer`](https://php.net/Normalizer) class provided
+by the [Intl](https://php.net/intl) extension.
+
+More information can be found in the
+[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md).
+
+License
+=======
+
+This library is released under the [MIT license](LICENSE).
diff --git a/vendor/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php b/vendor/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php
new file mode 100755
index 0000000..ca18eff
--- /dev/null
+++ b/vendor/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php
@@ -0,0 +1,17 @@
+<?php
+
+class Normalizer extends Symfony\Polyfill\Intl\Normalizer\Normalizer
+{
+    /**
+     * @deprecated since ICU 56 and removed in PHP 8
+     */
+    const NONE = 1;
+    const FORM_D = 2;
+    const FORM_KD = 3;
+    const FORM_C = 4;
+    const FORM_KC = 5;
+    const NFD = 2;
+    const NFKD = 3;
+    const NFC = 4;
+    const NFKC = 5;
+}
diff --git a/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalComposition.php b/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalComposition.php
new file mode 100755
index 0000000..db47644
--- /dev/null
+++ b/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalComposition.php
@@ -0,0 +1,945 @@
+<?php
+
+return array (
+  'À' => 'À',
+  'Á' => 'Á',
+  'Â' => 'Â',
+  'Ã' => 'Ã',
+  'Ä' => 'Ä',
+  'Å' => 'Å',
+  'Ç' => 'Ç',
+  'È' => 'È',
+  'É' => 'É',
+  'Ê' => 'Ê',
+  'Ë' => 'Ë',
+  'Ì' => 'Ì',
+  'Í' => 'Í',
+  'Î' => 'Î',
+  'Ï' => 'Ï',
+  'Ñ' => 'Ñ',
+  'Ò' => 'Ò',
+  'Ó' => 'Ó',
+  'Ô' => 'Ô',
+  'Õ' => 'Õ',
+  'Ö' => 'Ö',
+  'Ù' => 'Ù',
+  'Ú' => 'Ú',
+  'Û' => 'Û',
+  'Ü' => 'Ü',
+  'Ý' => 'Ý',
+  'à' => 'à',
+  'á' => 'á',
+  'â' => 'â',
+  'ã' => 'ã',
+  'ä' => 'ä',
+  'å' => 'å',
+  'ç' => 'ç',
+  'è' => 'è',
+  'é' => 'é',
+  'ê' => 'ê',
+  'ë' => 'ë',
+  'ì' => 'ì',
+  'í' => 'í',
+  'î' => 'î',
+  'ï' => 'ï',
+  'ñ' => 'ñ',
+  'ò' => 'ò',
+  'ó' => 'ó',
+  'ô' => 'ô',
+  'õ' => 'õ',
+  'ö' => 'ö',
+  'ù' => 'ù',
+  'ú' => 'ú',
+  'û' => 'û',
+  'ü' => 'ü',
+  'ý' => 'ý',
+  'ÿ' => 'ÿ',
+  'Ā' => 'Ā',
+  'ā' => 'ā',
+  'Ă' => 'Ă',
+  'ă' => 'ă',
+  'Ą' => 'Ą',
+  'ą' => 'ą',
+  'Ć' => 'Ć',
+  'ć' => 'ć',
+  'Ĉ' => 'Ĉ',
+  'ĉ' => 'ĉ',
+  'Ċ' => 'Ċ',
+  'ċ' => 'ċ',
+  'Č' => 'Č',
+  'č' => 'č',
+  'Ď' => 'Ď',
+  'ď' => 'ď',
+  'Ē' => 'Ē',
+  'ē' => 'ē',
+  'Ĕ' => 'Ĕ',
+  'ĕ' => 'ĕ',
+  'Ė' => 'Ė',
+  'ė' => 'ė',
+  'Ę' => 'Ę',
+  'ę' => 'ę',
+  'Ě' => 'Ě',
+  'ě' => 'ě',
+  'Ĝ' => 'Ĝ',
+  'ĝ' => 'ĝ',
+  'Ğ' => 'Ğ',
+  'ğ' => 'ğ',
+  'Ġ' => 'Ġ',
+  'ġ' => 'ġ',
+  'Ģ' => 'Ģ',
+  'ģ' => 'ģ',
+  'Ĥ' => 'Ĥ',
+  'ĥ' => 'ĥ',
+  'Ĩ' => 'Ĩ',
+  'ĩ' => 'ĩ',
+  'Ī' => 'Ī',
+  'ī' => 'ī',
+  'Ĭ' => 'Ĭ',
+  'ĭ' => 'ĭ',
+  'Į' => 'Į',
+  'į' => 'į',
+  'İ' => 'İ',
+  'Ĵ' => 'Ĵ',
+  'ĵ' => 'ĵ',
+  'Ķ' => 'Ķ',
+  'ķ' => 'ķ',
+  'Ĺ' => 'Ĺ',
+  'ĺ' => 'ĺ',
+  'Ļ' => 'Ļ',
+  'ļ' => 'ļ',
+  'Ľ' => 'Ľ',
+  'ľ' => 'ľ',
+  'Ń' => 'Ń',
+  'ń' => 'ń',
+  'Ņ' => 'Ņ',
+  'ņ' => 'ņ',
+  'Ň' => 'Ň',
+  'ň' => 'ň',
+  'Ō' => 'Ō',
+  'ō' => 'ō',
+  'Ŏ' => 'Ŏ',
+  'ŏ' => 'ŏ',
+  'Ő' => 'Ő',
+  'ő' => 'ő',
+  'Ŕ' => 'Ŕ',
+  'ŕ' => 'ŕ',
+  'Ŗ' => 'Ŗ',
+  'ŗ' => 'ŗ',
+  'Ř' => 'Ř',
+  'ř' => 'ř',
+  'Ś' => 'Ś',
+  'ś' => 'ś',
+  'Ŝ' => 'Ŝ',
+  'ŝ' => 'ŝ',
+  'Ş' => 'Ş',
+  'ş' => 'ş',
+  'Š' => 'Š',
+  'š' => 'š',
+  'Ţ' => 'Ţ',
+  'ţ' => 'ţ',
+  'Ť' => 'Ť',
+  'ť' => 'ť',
+  'Ũ' => 'Ũ',
+  'ũ' => 'ũ',
+  'Ū' => 'Ū',
+  'ū' => 'ū',
+  'Ŭ' => 'Ŭ',
+  'ŭ' => 'ŭ',
+  'Ů' => 'Ů',
+  'ů' => 'ů',
+  'Ű' => 'Ű',
+  'ű' => 'ű',
+  'Ų' => 'Ų',
+  'ų' => 'ų',
+  'Ŵ' => 'Ŵ',
+  'ŵ' => 'ŵ',
+  'Ŷ' => 'Ŷ',
+  'ŷ' => 'ŷ',
+  'Ÿ' => 'Ÿ',
+  'Ź' => 'Ź',
+  'ź' => 'ź',
+  'Ż' => 'Ż',
+  'ż' => 'ż',
+  'Ž' => 'Ž',
+  'ž' => 'ž',
+  'Ơ' => 'Ơ',
+  'ơ' => 'ơ',
+  'Ư' => 'Ư',
+  'ư' => 'ư',
+  'Ǎ' => 'Ǎ',
+  'ǎ' => 'ǎ',
+  'Ǐ' => 'Ǐ',
+  'ǐ' => 'ǐ',
+  'Ǒ' => 'Ǒ',
+  'ǒ' => 'ǒ',
+  'Ǔ' => 'Ǔ',
+  'ǔ' => 'ǔ',
+  'Ǖ' => 'Ǖ',
+  'ǖ' => 'ǖ',
+  'Ǘ' => 'Ǘ',
+  'ǘ' => 'ǘ',
+  'Ǚ' => 'Ǚ',
+  'ǚ' => 'ǚ',
+  'Ǜ' => 'Ǜ',
+  'ǜ' => 'ǜ',
+  'Ǟ' => 'Ǟ',
+  'ǟ' => 'ǟ',
+  'Ǡ' => 'Ǡ',
+  'ǡ' => 'ǡ',
+  'Ǣ' => 'Ǣ',
+  'ǣ' => 'ǣ',
+  'Ǧ' => 'Ǧ',
+  'ǧ' => 'ǧ',
+  'Ǩ' => 'Ǩ',
+  'ǩ' => 'ǩ',
+  'Ǫ' => 'Ǫ',
+  'ǫ' => 'ǫ',
+  'Ǭ' => 'Ǭ',
+  'ǭ' => 'ǭ',
+  'Ǯ' => 'Ǯ',
+  'ǯ' => 'ǯ',
+  'ǰ' => 'ǰ',
+  'Ǵ' => 'Ǵ',
+  'ǵ' => 'ǵ',
+  'Ǹ' => 'Ǹ',
+  'ǹ' => 'ǹ',
+  'Ǻ' => 'Ǻ',
+  'ǻ' => 'ǻ',
+  'Ǽ' => 'Ǽ',
+  'ǽ' => 'ǽ',
+  'Ǿ' => 'Ǿ',
+  'ǿ' => 'ǿ',
+  'Ȁ' => 'Ȁ',
+  'ȁ' => 'ȁ',
+  'Ȃ' => 'Ȃ',
+  'ȃ' => 'ȃ',
+  'Ȅ' => 'Ȅ',
+  'ȅ' => 'ȅ',
+  'Ȇ' => 'Ȇ',
+  'ȇ' => 'ȇ',
+  'Ȉ' => 'Ȉ',
+  'ȉ' => 'ȉ',
+  'Ȋ' => 'Ȋ',
+  'ȋ' => 'ȋ',
+  'Ȍ' => 'Ȍ',
+  'ȍ' => 'ȍ',
+  'Ȏ' => 'Ȏ',
+  'ȏ' => 'ȏ',
+  'Ȑ' => 'Ȑ',
+  'ȑ' => 'ȑ',
+  'Ȓ' => 'Ȓ',
+  'ȓ' => 'ȓ',
+  'Ȕ' => 'Ȕ',
+  'ȕ' => 'ȕ',
+  'Ȗ' => 'Ȗ',
+  'ȗ' => 'ȗ',
+  'Ș' => 'Ș',
+  'ș' => 'ș',
+  'Ț' => 'Ț',
+  'ț' => 'ț',
+  'Ȟ' => 'Ȟ',
+  'ȟ' => 'ȟ',
+  'Ȧ' => 'Ȧ',
+  'ȧ' => 'ȧ',
+  'Ȩ' => 'Ȩ',
+  'ȩ' => 'ȩ',
+  'Ȫ' => 'Ȫ',
+  'ȫ' => 'ȫ',
+  'Ȭ' => 'Ȭ',
+  'ȭ' => 'ȭ',
+  'Ȯ' => 'Ȯ',
+  'ȯ' => 'ȯ',
+  'Ȱ' => 'Ȱ',
+  'ȱ' => 'ȱ',
+  'Ȳ' => 'Ȳ',
+  'ȳ' => 'ȳ',
+  '΅' => '΅',
+  'Ά' => 'Ά',
+  'Έ' => 'Έ',
+  'Ή' => 'Ή',
+  'Ί' => 'Ί',
+  'Ό' => 'Ό',
+  'Ύ' => 'Ύ',
+  'Ώ' => 'Ώ',
+  'ΐ' => 'ΐ',
+  'Ϊ' => 'Ϊ',
+  'Ϋ' => 'Ϋ',
+  'ά' => 'ά',
+  'έ' => 'έ',
+  'ή' => 'ή',
+  'ί' => 'ί',
+  'ΰ' => 'ΰ',
+  'ϊ' => 'ϊ',
+  'ϋ' => 'ϋ',
+  'ό' => 'ό',
+  'ύ' => 'ύ',
+  'ώ' => 'ώ',
+  'ϓ' => 'ϓ',
+  'ϔ' => 'ϔ',
+  'Ѐ' => 'Ѐ',
+  'Ё' => 'Ё',
+  'Ѓ' => 'Ѓ',
+  'Ї' => 'Ї',
+  'Ќ' => 'Ќ',
+  'Ѝ' => 'Ѝ',
+  'Ў' => 'Ў',
+  'Й' => 'Й',
+  'й' => 'й',
+  'ѐ' => 'ѐ',
+  'ё' => 'ё',
+  'ѓ' => 'ѓ',
+  'ї' => 'ї',
+  'ќ' => 'ќ',
+  'ѝ' => 'ѝ',
+  'ў' => 'ў',
+  'Ѷ' => 'Ѷ',
+  'ѷ' => 'ѷ',
+  'Ӂ' => 'Ӂ',
+  'ӂ' => 'ӂ',
+  'Ӑ' => 'Ӑ',
+  'ӑ' => 'ӑ',
+  'Ӓ' => 'Ӓ',
+  'ӓ' => 'ӓ',
+  'Ӗ' => 'Ӗ',
+  'ӗ' => 'ӗ',
+  'Ӛ' => 'Ӛ',
+  'ӛ' => 'ӛ',
+  'Ӝ' => 'Ӝ',
+  'ӝ' => 'ӝ',
+  'Ӟ' => 'Ӟ',
+  'ӟ' => 'ӟ',
+  'Ӣ' => 'Ӣ',
+  'ӣ' => 'ӣ',
+  'Ӥ' => 'Ӥ',
+  'ӥ' => 'ӥ',
+  'Ӧ' => 'Ӧ',
+  'ӧ' => 'ӧ',
+  'Ӫ' => 'Ӫ',
+  'ӫ' => 'ӫ',
+  'Ӭ' => 'Ӭ',
+  'ӭ' => 'ӭ',
+  'Ӯ' => 'Ӯ',
+  'ӯ' => 'ӯ',
+  'Ӱ' => 'Ӱ',
+  'ӱ' => 'ӱ',
+  'Ӳ' => 'Ӳ',
+  'ӳ' => 'ӳ',
+  'Ӵ' => 'Ӵ',
+  'ӵ' => 'ӵ',
+  'Ӹ' => 'Ӹ',
+  'ӹ' => 'ӹ',
+  'آ' => 'آ',
+  'أ' => 'أ',
+  'ؤ' => 'ؤ',
+  'إ' => 'إ',
+  'ئ' => 'ئ',
+  'ۀ' => 'ۀ',
+  'ۂ' => 'ۂ',
+  'ۓ' => 'ۓ',
+  'ऩ' => 'ऩ',
+  'ऱ' => 'ऱ',
+  'ऴ' => 'ऴ',
+  'ো' => 'ো',
+  'ৌ' => 'ৌ',
+  'ୈ' => 'ୈ',
+  'ୋ' => 'ୋ',
+  'ୌ' => 'ୌ',
+  'ஔ' => 'ஔ',
+  'ொ' => 'ொ',
+  'ோ' => 'ோ',
+  'ௌ' => 'ௌ',
+  'ై' => 'ై',
+  'ೀ' => 'ೀ',
+  'ೇ' => 'ೇ',
+  'ೈ' => 'ೈ',
+  'ೊ' => 'ೊ',
+  'ೋ' => 'ೋ',
+  'ൊ' => 'ൊ',
+  'ോ' => 'ോ',
+  'ൌ' => 'ൌ',
+  'ේ' => 'ේ',
+  'ො' => 'ො',
+  'ෝ' => 'ෝ',
+  'ෞ' => 'ෞ',
+  'ဦ' => 'ဦ',
+  'ᬆ' => 'ᬆ',
+  'ᬈ' => 'ᬈ',
+  'ᬊ' => 'ᬊ',
+  'ᬌ' => 'ᬌ',
+  'ᬎ' => 'ᬎ',
+  'ᬒ' => 'ᬒ',
+  'ᬻ' => 'ᬻ',
+  'ᬽ' => 'ᬽ',
+  'ᭀ' => 'ᭀ',
+  'ᭁ' => 'ᭁ',
+  'ᭃ' => 'ᭃ',
+  'Ḁ' => 'Ḁ',
+  'ḁ' => 'ḁ',
+  'Ḃ' => 'Ḃ',
+  'ḃ' => 'ḃ',
+  'Ḅ' => 'Ḅ',
+  'ḅ' => 'ḅ',
+  'Ḇ' => 'Ḇ',
+  'ḇ' => 'ḇ',
+  'Ḉ' => 'Ḉ',
+  'ḉ' => 'ḉ',
+  'Ḋ' => 'Ḋ',
+  'ḋ' => 'ḋ',
+  'Ḍ' => 'Ḍ',
+  'ḍ' => 'ḍ',
+  'Ḏ' => 'Ḏ',
+  'ḏ' => 'ḏ',
+  'Ḑ' => 'Ḑ',
+  'ḑ' => 'ḑ',
+  'Ḓ' => 'Ḓ',
+  'ḓ' => 'ḓ',
+  'Ḕ' => 'Ḕ',
+  'ḕ' => 'ḕ',
+  'Ḗ' => 'Ḗ',
+  'ḗ' => 'ḗ',
+  'Ḙ' => 'Ḙ',
+  'ḙ' => 'ḙ',
+  'Ḛ' => 'Ḛ',
+  'ḛ' => 'ḛ',
+  'Ḝ' => 'Ḝ',
+  'ḝ' => 'ḝ',
+  'Ḟ' => 'Ḟ',
+  'ḟ' => 'ḟ',
+  'Ḡ' => 'Ḡ',
+  'ḡ' => 'ḡ',
+  'Ḣ' => 'Ḣ',
+  'ḣ' => 'ḣ',
+  'Ḥ' => 'Ḥ',
+  'ḥ' => 'ḥ',
+  'Ḧ' => 'Ḧ',
+  'ḧ' => 'ḧ',
+  'Ḩ' => 'Ḩ',
+  'ḩ' => 'ḩ',
+  'Ḫ' => 'Ḫ',
+  'ḫ' => 'ḫ',
+  'Ḭ' => 'Ḭ',
+  'ḭ' => 'ḭ',
+  'Ḯ' => 'Ḯ',
+  'ḯ' => 'ḯ',
+  'Ḱ' => 'Ḱ',
+  'ḱ' => 'ḱ',
+  'Ḳ' => 'Ḳ',
+  'ḳ' => 'ḳ',
+  'Ḵ' => 'Ḵ',
+  'ḵ' => 'ḵ',
+  'Ḷ' => 'Ḷ',
+  'ḷ' => 'ḷ',
+  'Ḹ' => 'Ḹ',
+  'ḹ' => 'ḹ',
+  'Ḻ' => 'Ḻ',
+  'ḻ' => 'ḻ',
+  'Ḽ' => 'Ḽ',
+  'ḽ' => 'ḽ',
+  'Ḿ' => 'Ḿ',
+  'ḿ' => 'ḿ',
+  'Ṁ' => 'Ṁ',
+  'ṁ' => 'ṁ',
+  'Ṃ' => 'Ṃ',
+  'ṃ' => 'ṃ',
+  'Ṅ' => 'Ṅ',
+  'ṅ' => 'ṅ',
+  'Ṇ' => 'Ṇ',
+  'ṇ' => 'ṇ',
+  'Ṉ' => 'Ṉ',
+  'ṉ' => 'ṉ',
+  'Ṋ' => 'Ṋ',
+  'ṋ' => 'ṋ',
+  'Ṍ' => 'Ṍ',
+  'ṍ' => 'ṍ',
+  'Ṏ' => 'Ṏ',
+  'ṏ' => 'ṏ',
+  'Ṑ' => 'Ṑ',
+  'ṑ' => 'ṑ',
+  'Ṓ' => 'Ṓ',
+  'ṓ' => 'ṓ',
+  'Ṕ' => 'Ṕ',
+  'ṕ' => 'ṕ',
+  'Ṗ' => 'Ṗ',
+  'ṗ' => 'ṗ',
+  'Ṙ' => 'Ṙ',
+  'ṙ' => 'ṙ',
+  'Ṛ' => 'Ṛ',
+  'ṛ' => 'ṛ',
+  'Ṝ' => 'Ṝ',
+  'ṝ' => 'ṝ',
+  'Ṟ' => 'Ṟ',
+  'ṟ' => 'ṟ',
+  'Ṡ' => 'Ṡ',
+  'ṡ' => 'ṡ',
+  'Ṣ' => 'Ṣ',
+  'ṣ' => 'ṣ',
+  'Ṥ' => 'Ṥ',
+  'ṥ' => 'ṥ',
+  'Ṧ' => 'Ṧ',
+  'ṧ' => 'ṧ',
+  'Ṩ' => 'Ṩ',
+  'ṩ' => 'ṩ',
+  'Ṫ' => 'Ṫ',
+  'ṫ' => 'ṫ',
+  'Ṭ' => 'Ṭ',
+  'ṭ' => 'ṭ',
+  'Ṯ' => 'Ṯ',
+  'ṯ' => 'ṯ',
+  'Ṱ' => 'Ṱ',
+  'ṱ' => 'ṱ',
+  'Ṳ' => 'Ṳ',
+  'ṳ' => 'ṳ',
+  'Ṵ' => 'Ṵ',
+  'ṵ' => 'ṵ',
+  'Ṷ' => 'Ṷ',
+  'ṷ' => 'ṷ',
+  'Ṹ' => 'Ṹ',
+  'ṹ' => 'ṹ',
+  'Ṻ' => 'Ṻ',
+  'ṻ' => 'ṻ',
+  'Ṽ' => 'Ṽ',
+  'ṽ' => 'ṽ',
+  'Ṿ' => 'Ṿ',
+  'ṿ' => 'ṿ',
+  'Ẁ' => 'Ẁ',
+  'ẁ' => 'ẁ',
+  'Ẃ' => 'Ẃ',
+  'ẃ' => 'ẃ',
+  'Ẅ' => 'Ẅ',
+  'ẅ' => 'ẅ',
+  'Ẇ' => 'Ẇ',
+  'ẇ' => 'ẇ',
+  'Ẉ' => 'Ẉ',
+  'ẉ' => 'ẉ',
+  'Ẋ' => 'Ẋ',
+  'ẋ' => 'ẋ',
+  'Ẍ' => 'Ẍ',
+  'ẍ' => 'ẍ',
+  'Ẏ' => 'Ẏ',
+  'ẏ' => 'ẏ',
+  'Ẑ' => 'Ẑ',
+  'ẑ' => 'ẑ',
+  'Ẓ' => 'Ẓ',
+  'ẓ' => 'ẓ',
+  'Ẕ' => 'Ẕ',
+  'ẕ' => 'ẕ',
+  'ẖ' => 'ẖ',
+  'ẗ' => 'ẗ',
+  'ẘ' => 'ẘ',
+  'ẙ' => 'ẙ',
+  'ẛ' => 'ẛ',
+  'Ạ' => 'Ạ',
+  'ạ' => 'ạ',
+  'Ả' => 'Ả',
+  'ả' => 'ả',
+  'Ấ' => 'Ấ',
+  'ấ' => 'ấ',
+  'Ầ' => 'Ầ',
+  'ầ' => 'ầ',
+  'Ẩ' => 'Ẩ',
+  'ẩ' => 'ẩ',
+  'Ẫ' => 'Ẫ',
+  'ẫ' => 'ẫ',
+  'Ậ' => 'Ậ',
+  'ậ' => 'ậ',
+  'Ắ' => 'Ắ',
+  'ắ' => 'ắ',
+  'Ằ' => 'Ằ',
+  'ằ' => 'ằ',
+  'Ẳ' => 'Ẳ',
+  'ẳ' => 'ẳ',
+  'Ẵ' => 'Ẵ',
+  'ẵ' => 'ẵ',
+  'Ặ' => 'Ặ',
+  'ặ' => 'ặ',
+  'Ẹ' => 'Ẹ',
+  'ẹ' => 'ẹ',
+  'Ẻ' => 'Ẻ',
+  'ẻ' => 'ẻ',
+  'Ẽ' => 'Ẽ',
+  'ẽ' => 'ẽ',
+  'Ế' => 'Ế',
+  'ế' => 'ế',
+  'Ề' => 'Ề',
+  'ề' => 'ề',
+  'Ể' => 'Ể',
+  'ể' => 'ể',
+  'Ễ' => 'Ễ',
+  'ễ' => 'ễ',
+  'Ệ' => 'Ệ',
+  'ệ' => 'ệ',
+  'Ỉ' => 'Ỉ',
+  'ỉ' => 'ỉ',
+  'Ị' => 'Ị',
+  'ị' => 'ị',
+  'Ọ' => 'Ọ',
+  'ọ' => 'ọ',
+  'Ỏ' => 'Ỏ',
+  'ỏ' => 'ỏ',
+  'Ố' => 'Ố',
+  'ố' => 'ố',
+  'Ồ' => 'Ồ',
+  'ồ' => 'ồ',
+  'Ổ' => 'Ổ',
+  'ổ' => 'ổ',
+  'Ỗ' => 'Ỗ',
+  'ỗ' => 'ỗ',
+  'Ộ' => 'Ộ',
+  'ộ' => 'ộ',
+  'Ớ' => 'Ớ',
+  'ớ' => 'ớ',
+  'Ờ' => 'Ờ',
+  'ờ' => 'ờ',
+  'Ở' => 'Ở',
+  'ở' => 'ở',
+  'Ỡ' => 'Ỡ',
+  'ỡ' => 'ỡ',
+  'Ợ' => 'Ợ',
+  'ợ' => 'ợ',
+  'Ụ' => 'Ụ',
+  'ụ' => 'ụ',
+  'Ủ' => 'Ủ',
+  'ủ' => 'ủ',
+  'Ứ' => 'Ứ',
+  'ứ' => 'ứ',
+  'Ừ' => 'Ừ',
+  'ừ' => 'ừ',
+  'Ử' => 'Ử',
+  'ử' => 'ử',
+  'Ữ' => 'Ữ',
+  'ữ' => 'ữ',
+  'Ự' => 'Ự',
+  'ự' => 'ự',
+  'Ỳ' => 'Ỳ',
+  'ỳ' => 'ỳ',
+  'Ỵ' => 'Ỵ',
+  'ỵ' => 'ỵ',
+  'Ỷ' => 'Ỷ',
+  'ỷ' => 'ỷ',
+  'Ỹ' => 'Ỹ',
+  'ỹ' => 'ỹ',
+  'ἀ' => 'ἀ',
+  'ἁ' => 'ἁ',
+  'ἂ' => 'ἂ',
+  'ἃ' => 'ἃ',
+  'ἄ' => 'ἄ',
+  'ἅ' => 'ἅ',
+  'ἆ' => 'ἆ',
+  'ἇ' => 'ἇ',
+  'Ἀ' => 'Ἀ',
+  'Ἁ' => 'Ἁ',
+  'Ἂ' => 'Ἂ',
+  'Ἃ' => 'Ἃ',
+  'Ἄ' => 'Ἄ',
+  'Ἅ' => 'Ἅ',
+  'Ἆ' => 'Ἆ',
+  'Ἇ' => 'Ἇ',
+  'ἐ' => 'ἐ',
+  'ἑ' => 'ἑ',
+  'ἒ' => 'ἒ',
+  'ἓ' => 'ἓ',
+  'ἔ' => 'ἔ',
+  'ἕ' => 'ἕ',
+  'Ἐ' => 'Ἐ',
+  'Ἑ' => 'Ἑ',
+  'Ἒ' => 'Ἒ',
+  'Ἓ' => 'Ἓ',
+  'Ἔ' => 'Ἔ',
+  'Ἕ' => 'Ἕ',
+  'ἠ' => 'ἠ',
+  'ἡ' => 'ἡ',
+  'ἢ' => 'ἢ',
+  'ἣ' => 'ἣ',
+  'ἤ' => 'ἤ',
+  'ἥ' => 'ἥ',
+  'ἦ' => 'ἦ',
+  'ἧ' => 'ἧ',
+  'Ἠ' => 'Ἠ',
+  'Ἡ' => 'Ἡ',
+  'Ἢ' => 'Ἢ',
+  'Ἣ' => 'Ἣ',
+  'Ἤ' => 'Ἤ',
+  'Ἥ' => 'Ἥ',
+  'Ἦ' => 'Ἦ',
+  'Ἧ' => 'Ἧ',
+  'ἰ' => 'ἰ',
+  'ἱ' => 'ἱ',
+  'ἲ' => 'ἲ',
+  'ἳ' => 'ἳ',
+  'ἴ' => 'ἴ',
+  'ἵ' => 'ἵ',
+  'ἶ' => 'ἶ',
+  'ἷ' => 'ἷ',
+  'Ἰ' => 'Ἰ',
+  'Ἱ' => 'Ἱ',
+  'Ἲ' => 'Ἲ',
+  'Ἳ' => 'Ἳ',
+  'Ἴ' => 'Ἴ',
+  'Ἵ' => 'Ἵ',
+  'Ἶ' => 'Ἶ',
+  'Ἷ' => 'Ἷ',
+  'ὀ' => 'ὀ',
+  'ὁ' => 'ὁ',
+  'ὂ' => 'ὂ',
+  'ὃ' => 'ὃ',
+  'ὄ' => 'ὄ',
+  'ὅ' => 'ὅ',
+  'Ὀ' => 'Ὀ',
+  'Ὁ' => 'Ὁ',
+  'Ὂ' => 'Ὂ',
+  'Ὃ' => 'Ὃ',
+  'Ὄ' => 'Ὄ',
+  'Ὅ' => 'Ὅ',
+  'ὐ' => 'ὐ',
+  'ὑ' => 'ὑ',
+  'ὒ' => 'ὒ',
+  'ὓ' => 'ὓ',
+  'ὔ' => 'ὔ',
+  'ὕ' => 'ὕ',
+  'ὖ' => 'ὖ',
+  'ὗ' => 'ὗ',
+  'Ὑ' => 'Ὑ',
+  'Ὓ' => 'Ὓ',
+  'Ὕ' => 'Ὕ',
+  'Ὗ' => 'Ὗ',
+  'ὠ' => 'ὠ',
+  'ὡ' => 'ὡ',
+  'ὢ' => 'ὢ',
+  'ὣ' => 'ὣ',
+  'ὤ' => 'ὤ',
+  'ὥ' => 'ὥ',
+  'ὦ' => 'ὦ',
+  'ὧ' => 'ὧ',
+  'Ὠ' => 'Ὠ',
+  'Ὡ' => 'Ὡ',
+  'Ὢ' => 'Ὢ',
+  'Ὣ' => 'Ὣ',
+  'Ὤ' => 'Ὤ',
+  'Ὥ' => 'Ὥ',
+  'Ὦ' => 'Ὦ',
+  'Ὧ' => 'Ὧ',
+  'ὰ' => 'ὰ',
+  'ὲ' => 'ὲ',
+  'ὴ' => 'ὴ',
+  'ὶ' => 'ὶ',
+  'ὸ' => 'ὸ',
+  'ὺ' => 'ὺ',
+  'ὼ' => 'ὼ',
+  'ᾀ' => 'ᾀ',
+  'ᾁ' => 'ᾁ',
+  'ᾂ' => 'ᾂ',
+  'ᾃ' => 'ᾃ',
+  'ᾄ' => 'ᾄ',
+  'ᾅ' => 'ᾅ',
+  'ᾆ' => 'ᾆ',
+  'ᾇ' => 'ᾇ',
+  'ᾈ' => 'ᾈ',
+  'ᾉ' => 'ᾉ',
+  'ᾊ' => 'ᾊ',
+  'ᾋ' => 'ᾋ',
+  'ᾌ' => 'ᾌ',
+  'ᾍ' => 'ᾍ',
+  'ᾎ' => 'ᾎ',
+  'ᾏ' => 'ᾏ',
+  'ᾐ' => 'ᾐ',
+  'ᾑ' => 'ᾑ',
+  'ᾒ' => 'ᾒ',
+  'ᾓ' => 'ᾓ',
+  'ᾔ' => 'ᾔ',
+  'ᾕ' => 'ᾕ',
+  'ᾖ' => 'ᾖ',
+  'ᾗ' => 'ᾗ',
+  'ᾘ' => 'ᾘ',
+  'ᾙ' => 'ᾙ',
+  'ᾚ' => 'ᾚ',
+  'ᾛ' => 'ᾛ',
+  'ᾜ' => 'ᾜ',
+  'ᾝ' => 'ᾝ',
+  'ᾞ' => 'ᾞ',
+  'ᾟ' => 'ᾟ',
+  'ᾠ' => 'ᾠ',
+  'ᾡ' => 'ᾡ',
+  'ᾢ' => 'ᾢ',
+  'ᾣ' => 'ᾣ',
+  'ᾤ' => 'ᾤ',
+  'ᾥ' => 'ᾥ',
+  'ᾦ' => 'ᾦ',
+  'ᾧ' => 'ᾧ',
+  'ᾨ' => 'ᾨ',
+  'ᾩ' => 'ᾩ',
+  'ᾪ' => 'ᾪ',
+  'ᾫ' => 'ᾫ',
+  'ᾬ' => 'ᾬ',
+  'ᾭ' => 'ᾭ',
+  'ᾮ' => 'ᾮ',
+  'ᾯ' => 'ᾯ',
+  'ᾰ' => 'ᾰ',
+  'ᾱ' => 'ᾱ',
+  'ᾲ' => 'ᾲ',
+  'ᾳ' => 'ᾳ',
+  'ᾴ' => 'ᾴ',
+  'ᾶ' => 'ᾶ',
+  'ᾷ' => 'ᾷ',
+  'Ᾰ' => 'Ᾰ',
+  'Ᾱ' => 'Ᾱ',
+  'Ὰ' => 'Ὰ',
+  'ᾼ' => 'ᾼ',
+  '῁' => '῁',
+  'ῂ' => 'ῂ',
+  'ῃ' => 'ῃ',
+  'ῄ' => 'ῄ',
+  'ῆ' => 'ῆ',
+  'ῇ' => 'ῇ',
+  'Ὲ' => 'Ὲ',
+  'Ὴ' => 'Ὴ',
+  'ῌ' => 'ῌ',
+  '῍' => '῍',
+  '῎' => '῎',
+  '῏' => '῏',
+  'ῐ' => 'ῐ',
+  'ῑ' => 'ῑ',
+  'ῒ' => 'ῒ',
+  'ῖ' => 'ῖ',
+  'ῗ' => 'ῗ',
+  'Ῐ' => 'Ῐ',
+  'Ῑ' => 'Ῑ',
+  'Ὶ' => 'Ὶ',
+  '῝' => '῝',
+  '῞' => '῞',
+  '῟' => '῟',
+  'ῠ' => 'ῠ',
+  'ῡ' => 'ῡ',
+  'ῢ' => 'ῢ',
+  'ῤ' => 'ῤ',
+  'ῥ' => 'ῥ',
+  'ῦ' => 'ῦ',
+  'ῧ' => 'ῧ',
+  'Ῠ' => 'Ῠ',
+  'Ῡ' => 'Ῡ',
+  'Ὺ' => 'Ὺ',
+  'Ῥ' => 'Ῥ',
+  '῭' => '῭',
+  'ῲ' => 'ῲ',
+  'ῳ' => 'ῳ',
+  'ῴ' => 'ῴ',
+  'ῶ' => 'ῶ',
+  'ῷ' => 'ῷ',
+  'Ὸ' => 'Ὸ',
+  'Ὼ' => 'Ὼ',
+  'ῼ' => 'ῼ',
+  '↚' => '↚',
+  '↛' => '↛',
+  '↮' => '↮',
+  '⇍' => '⇍',
+  '⇎' => '⇎',
+  '⇏' => '⇏',
+  '∄' => '∄',
+  '∉' => '∉',
+  '∌' => '∌',
+  '∤' => '∤',
+  '∦' => '∦',
+  '≁' => '≁',
+  '≄' => '≄',
+  '≇' => '≇',
+  '≉' => '≉',
+  '≠' => '≠',
+  '≢' => '≢',
+  '≭' => '≭',
+  '≮' => '≮',
+  '≯' => '≯',
+  '≰' => '≰',
+  '≱' => '≱',
+  '≴' => '≴',
+  '≵' => '≵',
+  '≸' => '≸',
+  '≹' => '≹',
+  '⊀' => '⊀',
+  '⊁' => '⊁',
+  '⊄' => '⊄',
+  '⊅' => '⊅',
+  '⊈' => '⊈',
+  '⊉' => '⊉',
+  '⊬' => '⊬',
+  '⊭' => '⊭',
+  '⊮' => '⊮',
+  '⊯' => '⊯',
+  '⋠' => '⋠',
+  '⋡' => '⋡',
+  '⋢' => '⋢',
+  '⋣' => '⋣',
+  '⋪' => '⋪',
+  '⋫' => '⋫',
+  '⋬' => '⋬',
+  '⋭' => '⋭',
+  'が' => 'が',
+  'ぎ' => 'ぎ',
+  'ぐ' => 'ぐ',
+  'げ' => 'げ',
+  'ご' => 'ご',
+  'ざ' => 'ざ',
+  'じ' => 'じ',
+  'ず' => 'ず',
+  'ぜ' => 'ぜ',
+  'ぞ' => 'ぞ',
+  'だ' => 'だ',
+  'ぢ' => 'ぢ',
+  'づ' => 'づ',
+  'で' => 'で',
+  'ど' => 'ど',
+  'ば' => 'ば',
+  'ぱ' => 'ぱ',
+  'び' => 'び',
+  'ぴ' => 'ぴ',
+  'ぶ' => 'ぶ',
+  'ぷ' => 'ぷ',
+  'べ' => 'べ',
+  'ぺ' => 'ぺ',
+  'ぼ' => 'ぼ',
+  'ぽ' => 'ぽ',
+  'ゔ' => 'ゔ',
+  'ゞ' => 'ゞ',
+  'ガ' => 'ガ',
+  'ギ' => 'ギ',
+  'グ' => 'グ',
+  'ゲ' => 'ゲ',
+  'ゴ' => 'ゴ',
+  'ザ' => 'ザ',
+  'ジ' => 'ジ',
+  'ズ' => 'ズ',
+  'ゼ' => 'ゼ',
+  'ゾ' => 'ゾ',
+  'ダ' => 'ダ',
+  'ヂ' => 'ヂ',
+  'ヅ' => 'ヅ',
+  'デ' => 'デ',
+  'ド' => 'ド',
+  'バ' => 'バ',
+  'パ' => 'パ',
+  'ビ' => 'ビ',
+  'ピ' => 'ピ',
+  'ブ' => 'ブ',
+  'プ' => 'プ',
+  'ベ' => 'ベ',
+  'ペ' => 'ペ',
+  'ボ' => 'ボ',
+  'ポ' => 'ポ',
+  'ヴ' => 'ヴ',
+  'ヷ' => 'ヷ',
+  'ヸ' => 'ヸ',
+  'ヹ' => 'ヹ',
+  'ヺ' => 'ヺ',
+  'ヾ' => 'ヾ',
+  '𑂚' => '𑂚',
+  '𑂜' => '𑂜',
+  '𑂫' => '𑂫',
+  '𑄮' => '𑄮',
+  '𑄯' => '𑄯',
+  '𑍋' => '𑍋',
+  '𑍌' => '𑍌',
+  '𑒻' => '𑒻',
+  '𑒼' => '𑒼',
+  '𑒾' => '𑒾',
+  '𑖺' => '𑖺',
+  '𑖻' => '𑖻',
+  '𑤸' => '𑤸',
+);
diff --git a/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalDecomposition.php b/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalDecomposition.php
new file mode 100755
index 0000000..5a3e8e0
--- /dev/null
+++ b/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalDecomposition.php
@@ -0,0 +1,2065 @@
+<?php
+
+return array (
+  'À' => 'À',
+  'Á' => 'Á',
+  'Â' => 'Â',
+  'Ã' => 'Ã',
+  'Ä' => 'Ä',
+  'Å' => 'Å',
+  'Ç' => 'Ç',
+  'È' => 'È',
+  'É' => 'É',
+  'Ê' => 'Ê',
+  'Ë' => 'Ë',
+  'Ì' => 'Ì',
+  'Í' => 'Í',
+  'Î' => 'Î',
+  'Ï' => 'Ï',
+  'Ñ' => 'Ñ',
+  'Ò' => 'Ò',
+  'Ó' => 'Ó',
+  'Ô' => 'Ô',
+  'Õ' => 'Õ',
+  'Ö' => 'Ö',
+  'Ù' => 'Ù',
+  'Ú' => 'Ú',
+  'Û' => 'Û',
+  'Ü' => 'Ü',
+  'Ý' => 'Ý',
+  'à' => 'à',
+  'á' => 'á',
+  'â' => 'â',
+  'ã' => 'ã',
+  'ä' => 'ä',
+  'å' => 'å',
+  'ç' => 'ç',
+  'è' => 'è',
+  'é' => 'é',
+  'ê' => 'ê',
+  'ë' => 'ë',
+  'ì' => 'ì',
+  'í' => 'í',
+  'î' => 'î',
+  'ï' => 'ï',
+  'ñ' => 'ñ',
+  'ò' => 'ò',
+  'ó' => 'ó',
+  'ô' => 'ô',
+  'õ' => 'õ',
+  'ö' => 'ö',
+  'ù' => 'ù',
+  'ú' => 'ú',
+  'û' => 'û',
+  'ü' => 'ü',
+  'ý' => 'ý',
+  'ÿ' => 'ÿ',
+  'Ā' => 'Ā',
+  'ā' => 'ā',
+  'Ă' => 'Ă',
+  'ă' => 'ă',
+  'Ą' => 'Ą',
+  'ą' => 'ą',
+  'Ć' => 'Ć',
+  'ć' => 'ć',
+  'Ĉ' => 'Ĉ',
+  'ĉ' => 'ĉ',
+  'Ċ' => 'Ċ',
+  'ċ' => 'ċ',
+  'Č' => 'Č',
+  'č' => 'č',
+  'Ď' => 'Ď',
+  'ď' => 'ď',
+  'Ē' => 'Ē',
+  'ē' => 'ē',
+  'Ĕ' => 'Ĕ',
+  'ĕ' => 'ĕ',
+  'Ė' => 'Ė',
+  'ė' => 'ė',
+  'Ę' => 'Ę',
+  'ę' => 'ę',
+  'Ě' => 'Ě',
+  'ě' => 'ě',
+  'Ĝ' => 'Ĝ',
+  'ĝ' => 'ĝ',
+  'Ğ' => 'Ğ',
+  'ğ' => 'ğ',
+  'Ġ' => 'Ġ',
+  'ġ' => 'ġ',
+  'Ģ' => 'Ģ',
+  'ģ' => 'ģ',
+  'Ĥ' => 'Ĥ',
+  'ĥ' => 'ĥ',
+  'Ĩ' => 'Ĩ',
+  'ĩ' => 'ĩ',
+  'Ī' => 'Ī',
+  'ī' => 'ī',
+  'Ĭ' => 'Ĭ',
+  'ĭ' => 'ĭ',
+  'Į' => 'Į',
+  'į' => 'į',
+  'İ' => 'İ',
+  'Ĵ' => 'Ĵ',
+  'ĵ' => 'ĵ',
+  'Ķ' => 'Ķ',
+  'ķ' => 'ķ',
+  'Ĺ' => 'Ĺ',
+  'ĺ' => 'ĺ',
+  'Ļ' => 'Ļ',
+  'ļ' => 'ļ',
+  'Ľ' => 'Ľ',
+  'ľ' => 'ľ',
+  'Ń' => 'Ń',
+  'ń' => 'ń',
+  'Ņ' => 'Ņ',
+  'ņ' => 'ņ',
+  'Ň' => 'Ň',
+  'ň' => 'ň',
+  'Ō' => 'Ō',
+  'ō' => 'ō',
+  'Ŏ' => 'Ŏ',
+  'ŏ' => 'ŏ',
+  'Ő' => 'Ő',
+  'ő' => 'ő',
+  'Ŕ' => 'Ŕ',
+  'ŕ' => 'ŕ',
+  'Ŗ' => 'Ŗ',
+  'ŗ' => 'ŗ',
+  'Ř' => 'Ř',
+  'ř' => 'ř',
+  'Ś' => 'Ś',
+  'ś' => 'ś',
+  'Ŝ' => 'Ŝ',
+  'ŝ' => 'ŝ',
+  'Ş' => 'Ş',
+  'ş' => 'ş',
+  'Š' => 'Š',
+  'š' => 'š',
+  'Ţ' => 'Ţ',
+  'ţ' => 'ţ',
+  'Ť' => 'Ť',
+  'ť' => 'ť',
+  'Ũ' => 'Ũ',
+  'ũ' => 'ũ',
+  'Ū' => 'Ū',
+  'ū' => 'ū',
+  'Ŭ' => 'Ŭ',
+  'ŭ' => 'ŭ',
+  'Ů' => 'Ů',
+  'ů' => 'ů',
+  'Ű' => 'Ű',
+  'ű' => 'ű',
+  'Ų' => 'Ų',
+  'ų' => 'ų',
+  'Ŵ' => 'Ŵ',
+  'ŵ' => 'ŵ',
+  'Ŷ' => 'Ŷ',
+  'ŷ' => 'ŷ',
+  'Ÿ' => 'Ÿ',
+  'Ź' => 'Ź',
+  'ź' => 'ź',
+  'Ż' => 'Ż',
+  'ż' => 'ż',
+  'Ž' => 'Ž',
+  'ž' => 'ž',
+  'Ơ' => 'Ơ',
+  'ơ' => 'ơ',
+  'Ư' => 'Ư',
+  'ư' => 'ư',
+  'Ǎ' => 'Ǎ',
+  'ǎ' => 'ǎ',
+  'Ǐ' => 'Ǐ',
+  'ǐ' => 'ǐ',
+  'Ǒ' => 'Ǒ',
+  'ǒ' => 'ǒ',
+  'Ǔ' => 'Ǔ',
+  'ǔ' => 'ǔ',
+  'Ǖ' => 'Ǖ',
+  'ǖ' => 'ǖ',
+  'Ǘ' => 'Ǘ',
+  'ǘ' => 'ǘ',
+  'Ǚ' => 'Ǚ',
+  'ǚ' => 'ǚ',
+  'Ǜ' => 'Ǜ',
+  'ǜ' => 'ǜ',
+  'Ǟ' => 'Ǟ',
+  'ǟ' => 'ǟ',
+  'Ǡ' => 'Ǡ',
+  'ǡ' => 'ǡ',
+  'Ǣ' => 'Ǣ',
+  'ǣ' => 'ǣ',
+  'Ǧ' => 'Ǧ',
+  'ǧ' => 'ǧ',
+  'Ǩ' => 'Ǩ',
+  'ǩ' => 'ǩ',
+  'Ǫ' => 'Ǫ',
+  'ǫ' => 'ǫ',
+  'Ǭ' => 'Ǭ',
+  'ǭ' => 'ǭ',
+  'Ǯ' => 'Ǯ',
+  'ǯ' => 'ǯ',
+  'ǰ' => 'ǰ',
+  'Ǵ' => 'Ǵ',
+  'ǵ' => 'ǵ',
+  'Ǹ' => 'Ǹ',
+  'ǹ' => 'ǹ',
+  'Ǻ' => 'Ǻ',
+  'ǻ' => 'ǻ',
+  'Ǽ' => 'Ǽ',
+  'ǽ' => 'ǽ',
+  'Ǿ' => 'Ǿ',
+  'ǿ' => 'ǿ',
+  'Ȁ' => 'Ȁ',
+  'ȁ' => 'ȁ',
+  'Ȃ' => 'Ȃ',
+  'ȃ' => 'ȃ',
+  'Ȅ' => 'Ȅ',
+  'ȅ' => 'ȅ',
+  'Ȇ' => 'Ȇ',
+  'ȇ' => 'ȇ',
+  'Ȉ' => 'Ȉ',
+  'ȉ' => 'ȉ',
+  'Ȋ' => 'Ȋ',
+  'ȋ' => 'ȋ',
+  'Ȍ' => 'Ȍ',
+  'ȍ' => 'ȍ',
+  'Ȏ' => 'Ȏ',
+  'ȏ' => 'ȏ',
+  'Ȑ' => 'Ȑ',
+  'ȑ' => 'ȑ',
+  'Ȓ' => 'Ȓ',
+  'ȓ' => 'ȓ',
+  'Ȕ' => 'Ȕ',
+  'ȕ' => 'ȕ',
+  'Ȗ' => 'Ȗ',
+  'ȗ' => 'ȗ',
+  'Ș' => 'Ș',
+  'ș' => 'ș',
+  'Ț' => 'Ț',
+  'ț' => 'ț',
+  'Ȟ' => 'Ȟ',
+  'ȟ' => 'ȟ',
+  'Ȧ' => 'Ȧ',
+  'ȧ' => 'ȧ',
+  'Ȩ' => 'Ȩ',
+  'ȩ' => 'ȩ',
+  'Ȫ' => 'Ȫ',
+  'ȫ' => 'ȫ',
+  'Ȭ' => 'Ȭ',
+  'ȭ' => 'ȭ',
+  'Ȯ' => 'Ȯ',
+  'ȯ' => 'ȯ',
+  'Ȱ' => 'Ȱ',
+  'ȱ' => 'ȱ',
+  'Ȳ' => 'Ȳ',
+  'ȳ' => 'ȳ',
+  '̀' => '̀',
+  '́' => '́',
+  '̓' => '̓',
+  '̈́' => '̈́',
+  'ʹ' => 'ʹ',
+  ';' => ';',
+  '΅' => '΅',
+  'Ά' => 'Ά',
+  '·' => '·',
+  'Έ' => 'Έ',
+  'Ή' => 'Ή',
+  'Ί' => 'Ί',
+  'Ό' => 'Ό',
+  'Ύ' => 'Ύ',
+  'Ώ' => 'Ώ',
+  'ΐ' => 'ΐ',
+  'Ϊ' => 'Ϊ',
+  'Ϋ' => 'Ϋ',
+  'ά' => 'ά',
+  'έ' => 'έ',
+  'ή' => 'ή',
+  'ί' => 'ί',
+  'ΰ' => 'ΰ',
+  'ϊ' => 'ϊ',
+  'ϋ' => 'ϋ',
+  'ό' => 'ό',
+  'ύ' => 'ύ',
+  'ώ' => 'ώ',
+  'ϓ' => 'ϓ',
+  'ϔ' => 'ϔ',
+  'Ѐ' => 'Ѐ',
+  'Ё' => 'Ё',
+  'Ѓ' => 'Ѓ',
+  'Ї' => 'Ї',
+  'Ќ' => 'Ќ',
+  'Ѝ' => 'Ѝ',
+  'Ў' => 'Ў',
+  'Й' => 'Й',
+  'й' => 'й',
+  'ѐ' => 'ѐ',
+  'ё' => 'ё',
+  'ѓ' => 'ѓ',
+  'ї' => 'ї',
+  'ќ' => 'ќ',
+  'ѝ' => 'ѝ',
+  'ў' => 'ў',
+  'Ѷ' => 'Ѷ',
+  'ѷ' => 'ѷ',
+  'Ӂ' => 'Ӂ',
+  'ӂ' => 'ӂ',
+  'Ӑ' => 'Ӑ',
+  'ӑ' => 'ӑ',
+  'Ӓ' => 'Ӓ',
+  'ӓ' => 'ӓ',
+  'Ӗ' => 'Ӗ',
+  'ӗ' => 'ӗ',
+  'Ӛ' => 'Ӛ',
+  'ӛ' => 'ӛ',
+  'Ӝ' => 'Ӝ',
+  'ӝ' => 'ӝ',
+  'Ӟ' => 'Ӟ',
+  'ӟ' => 'ӟ',
+  'Ӣ' => 'Ӣ',
+  'ӣ' => 'ӣ',
+  'Ӥ' => 'Ӥ',
+  'ӥ' => 'ӥ',
+  'Ӧ' => 'Ӧ',
+  'ӧ' => 'ӧ',
+  'Ӫ' => 'Ӫ',
+  'ӫ' => 'ӫ',
+  'Ӭ' => 'Ӭ',
+  'ӭ' => 'ӭ',
+  'Ӯ' => 'Ӯ',
+  'ӯ' => 'ӯ',
+  'Ӱ' => 'Ӱ',
+  'ӱ' => 'ӱ',
+  'Ӳ' => 'Ӳ',
+  'ӳ' => 'ӳ',
+  'Ӵ' => 'Ӵ',
+  'ӵ' => 'ӵ',
+  'Ӹ' => 'Ӹ',
+  'ӹ' => 'ӹ',
+  'آ' => 'آ',
+  'أ' => 'أ',
+  'ؤ' => 'ؤ',
+  'إ' => 'إ',
+  'ئ' => 'ئ',
+  'ۀ' => 'ۀ',
+  'ۂ' => 'ۂ',
+  'ۓ' => 'ۓ',
+  'ऩ' => 'ऩ',
+  'ऱ' => 'ऱ',
+  'ऴ' => 'ऴ',
+  'क़' => 'क़',
+  'ख़' => 'ख़',
+  'ग़' => 'ग़',
+  'ज़' => 'ज़',
+  'ड़' => 'ड़',
+  'ढ़' => 'ढ़',
+  'फ़' => 'फ़',
+  'य़' => 'य़',
+  'ো' => 'ো',
+  'ৌ' => 'ৌ',
+  'ড়' => 'ড়',
+  'ঢ়' => 'ঢ়',
+  'য়' => 'য়',
+  'ਲ਼' => 'ਲ਼',
+  'ਸ਼' => 'ਸ਼',
+  'ਖ਼' => 'ਖ਼',
+  'ਗ਼' => 'ਗ਼',
+  'ਜ਼' => 'ਜ਼',
+  'ਫ਼' => 'ਫ਼',
+  'ୈ' => 'ୈ',
+  'ୋ' => 'ୋ',
+  'ୌ' => 'ୌ',
+  'ଡ଼' => 'ଡ଼',
+  'ଢ଼' => 'ଢ଼',
+  'ஔ' => 'ஔ',
+  'ொ' => 'ொ',
+  'ோ' => 'ோ',
+  'ௌ' => 'ௌ',
+  'ై' => 'ై',
+  'ೀ' => 'ೀ',
+  'ೇ' => 'ೇ',
+  'ೈ' => 'ೈ',
+  'ೊ' => 'ೊ',
+  'ೋ' => 'ೋ',
+  'ൊ' => 'ൊ',
+  'ോ' => 'ോ',
+  'ൌ' => 'ൌ',
+  'ේ' => 'ේ',
+  'ො' => 'ො',
+  'ෝ' => 'ෝ',
+  'ෞ' => 'ෞ',
+  'གྷ' => 'གྷ',
+  'ཌྷ' => 'ཌྷ',
+  'དྷ' => 'དྷ',
+  'བྷ' => 'བྷ',
+  'ཛྷ' => 'ཛྷ',
+  'ཀྵ' => 'ཀྵ',
+  'ཱི' => 'ཱི',
+  'ཱུ' => 'ཱུ',
+  'ྲྀ' => 'ྲྀ',
+  'ླྀ' => 'ླྀ',
+  'ཱྀ' => 'ཱྀ',
+  'ྒྷ' => 'ྒྷ',
+  'ྜྷ' => 'ྜྷ',
+  'ྡྷ' => 'ྡྷ',
+  'ྦྷ' => 'ྦྷ',
+  'ྫྷ' => 'ྫྷ',
+  'ྐྵ' => 'ྐྵ',
+  'ဦ' => 'ဦ',
+  'ᬆ' => 'ᬆ',
+  'ᬈ' => 'ᬈ',
+  'ᬊ' => 'ᬊ',
+  'ᬌ' => 'ᬌ',
+  'ᬎ' => 'ᬎ',
+  'ᬒ' => 'ᬒ',
+  'ᬻ' => 'ᬻ',
+  'ᬽ' => 'ᬽ',
+  'ᭀ' => 'ᭀ',
+  'ᭁ' => 'ᭁ',
+  'ᭃ' => 'ᭃ',
+  'Ḁ' => 'Ḁ',
+  'ḁ' => 'ḁ',
+  'Ḃ' => 'Ḃ',
+  'ḃ' => 'ḃ',
+  'Ḅ' => 'Ḅ',
+  'ḅ' => 'ḅ',
+  'Ḇ' => 'Ḇ',
+  'ḇ' => 'ḇ',
+  'Ḉ' => 'Ḉ',
+  'ḉ' => 'ḉ',
+  'Ḋ' => 'Ḋ',
+  'ḋ' => 'ḋ',
+  'Ḍ' => 'Ḍ',
+  'ḍ' => 'ḍ',
+  'Ḏ' => 'Ḏ',
+  'ḏ' => 'ḏ',
+  'Ḑ' => 'Ḑ',
+  'ḑ' => 'ḑ',
+  'Ḓ' => 'Ḓ',
+  'ḓ' => 'ḓ',
+  'Ḕ' => 'Ḕ',
+  'ḕ' => 'ḕ',
+  'Ḗ' => 'Ḗ',
+  'ḗ' => 'ḗ',
+  'Ḙ' => 'Ḙ',
+  'ḙ' => 'ḙ',
+  'Ḛ' => 'Ḛ',
+  'ḛ' => 'ḛ',
+  'Ḝ' => 'Ḝ',
+  'ḝ' => 'ḝ',
+  'Ḟ' => 'Ḟ',
+  'ḟ' => 'ḟ',
+  'Ḡ' => 'Ḡ',
+  'ḡ' => 'ḡ',
+  'Ḣ' => 'Ḣ',
+  'ḣ' => 'ḣ',
+  'Ḥ' => 'Ḥ',
+  'ḥ' => 'ḥ',
+  'Ḧ' => 'Ḧ',
+  'ḧ' => 'ḧ',
+  'Ḩ' => 'Ḩ',
+  'ḩ' => 'ḩ',
+  'Ḫ' => 'Ḫ',
+  'ḫ' => 'ḫ',
+  'Ḭ' => 'Ḭ',
+  'ḭ' => 'ḭ',
+  'Ḯ' => 'Ḯ',
+  'ḯ' => 'ḯ',
+  'Ḱ' => 'Ḱ',
+  'ḱ' => 'ḱ',
+  'Ḳ' => 'Ḳ',
+  'ḳ' => 'ḳ',
+  'Ḵ' => 'Ḵ',
+  'ḵ' => 'ḵ',
+  'Ḷ' => 'Ḷ',
+  'ḷ' => 'ḷ',
+  'Ḹ' => 'Ḹ',
+  'ḹ' => 'ḹ',
+  'Ḻ' => 'Ḻ',
+  'ḻ' => 'ḻ',
+  'Ḽ' => 'Ḽ',
+  'ḽ' => 'ḽ',
+  'Ḿ' => 'Ḿ',
+  'ḿ' => 'ḿ',
+  'Ṁ' => 'Ṁ',
+  'ṁ' => 'ṁ',
+  'Ṃ' => 'Ṃ',
+  'ṃ' => 'ṃ',
+  'Ṅ' => 'Ṅ',
+  'ṅ' => 'ṅ',
+  'Ṇ' => 'Ṇ',
+  'ṇ' => 'ṇ',
+  'Ṉ' => 'Ṉ',
+  'ṉ' => 'ṉ',
+  'Ṋ' => 'Ṋ',
+  'ṋ' => 'ṋ',
+  'Ṍ' => 'Ṍ',
+  'ṍ' => 'ṍ',
+  'Ṏ' => 'Ṏ',
+  'ṏ' => 'ṏ',
+  'Ṑ' => 'Ṑ',
+  'ṑ' => 'ṑ',
+  'Ṓ' => 'Ṓ',
+  'ṓ' => 'ṓ',
+  'Ṕ' => 'Ṕ',
+  'ṕ' => 'ṕ',
+  'Ṗ' => 'Ṗ',
+  'ṗ' => 'ṗ',
+  'Ṙ' => 'Ṙ',
+  'ṙ' => 'ṙ',
+  'Ṛ' => 'Ṛ',
+  'ṛ' => 'ṛ',
+  'Ṝ' => 'Ṝ',
+  'ṝ' => 'ṝ',
+  'Ṟ' => 'Ṟ',
+  'ṟ' => 'ṟ',
+  'Ṡ' => 'Ṡ',
+  'ṡ' => 'ṡ',
+  'Ṣ' => 'Ṣ',
+  'ṣ' => 'ṣ',
+  'Ṥ' => 'Ṥ',
+  'ṥ' => 'ṥ',
+  'Ṧ' => 'Ṧ',
+  'ṧ' => 'ṧ',
+  'Ṩ' => 'Ṩ',
+  'ṩ' => 'ṩ',
+  'Ṫ' => 'Ṫ',
+  'ṫ' => 'ṫ',
+  'Ṭ' => 'Ṭ',
+  'ṭ' => 'ṭ',
+  'Ṯ' => 'Ṯ',
+  'ṯ' => 'ṯ',
+  'Ṱ' => 'Ṱ',
+  'ṱ' => 'ṱ',
+  'Ṳ' => 'Ṳ',
+  'ṳ' => 'ṳ',
+  'Ṵ' => 'Ṵ',
+  'ṵ' => 'ṵ',
+  'Ṷ' => 'Ṷ',
+  'ṷ' => 'ṷ',
+  'Ṹ' => 'Ṹ',
+  'ṹ' => 'ṹ',
+  'Ṻ' => 'Ṻ',
+  'ṻ' => 'ṻ',
+  'Ṽ' => 'Ṽ',
+  'ṽ' => 'ṽ',
+  'Ṿ' => 'Ṿ',
+  'ṿ' => 'ṿ',
+  'Ẁ' => 'Ẁ',
+  'ẁ' => 'ẁ',
+  'Ẃ' => 'Ẃ',
+  'ẃ' => 'ẃ',
+  'Ẅ' => 'Ẅ',
+  'ẅ' => 'ẅ',
+  'Ẇ' => 'Ẇ',
+  'ẇ' => 'ẇ',
+  'Ẉ' => 'Ẉ',
+  'ẉ' => 'ẉ',
+  'Ẋ' => 'Ẋ',
+  'ẋ' => 'ẋ',
+  'Ẍ' => 'Ẍ',
+  'ẍ' => 'ẍ',
+  'Ẏ' => 'Ẏ',
+  'ẏ' => 'ẏ',
+  'Ẑ' => 'Ẑ',
+  'ẑ' => 'ẑ',
+  'Ẓ' => 'Ẓ',
+  'ẓ' => 'ẓ',
+  'Ẕ' => 'Ẕ',
+  'ẕ' => 'ẕ',
+  'ẖ' => 'ẖ',
+  'ẗ' => 'ẗ',
+  'ẘ' => 'ẘ',
+  'ẙ' => 'ẙ',
+  'ẛ' => 'ẛ',
+  'Ạ' => 'Ạ',
+  'ạ' => 'ạ',
+  'Ả' => 'Ả',
+  'ả' => 'ả',
+  'Ấ' => 'Ấ',
+  'ấ' => 'ấ',
+  'Ầ' => 'Ầ',
+  'ầ' => 'ầ',
+  'Ẩ' => 'Ẩ',
+  'ẩ' => 'ẩ',
+  'Ẫ' => 'Ẫ',
+  'ẫ' => 'ẫ',
+  'Ậ' => 'Ậ',
+  'ậ' => 'ậ',
+  'Ắ' => 'Ắ',
+  'ắ' => 'ắ',
+  'Ằ' => 'Ằ',
+  'ằ' => 'ằ',
+  'Ẳ' => 'Ẳ',
+  'ẳ' => 'ẳ',
+  'Ẵ' => 'Ẵ',
+  'ẵ' => 'ẵ',
+  'Ặ' => 'Ặ',
+  'ặ' => 'ặ',
+  'Ẹ' => 'Ẹ',
+  'ẹ' => 'ẹ',
+  'Ẻ' => 'Ẻ',
+  'ẻ' => 'ẻ',
+  'Ẽ' => 'Ẽ',
+  'ẽ' => 'ẽ',
+  'Ế' => 'Ế',
+  'ế' => 'ế',
+  'Ề' => 'Ề',
+  'ề' => 'ề',
+  'Ể' => 'Ể',
+  'ể' => 'ể',
+  'Ễ' => 'Ễ',
+  'ễ' => 'ễ',
+  'Ệ' => 'Ệ',
+  'ệ' => 'ệ',
+  'Ỉ' => 'Ỉ',
+  'ỉ' => 'ỉ',
+  'Ị' => 'Ị',
+  'ị' => 'ị',
+  'Ọ' => 'Ọ',
+  'ọ' => 'ọ',
+  'Ỏ' => 'Ỏ',
+  'ỏ' => 'ỏ',
+  'Ố' => 'Ố',
+  'ố' => 'ố',
+  'Ồ' => 'Ồ',
+  'ồ' => 'ồ',
+  'Ổ' => 'Ổ',
+  'ổ' => 'ổ',
+  'Ỗ' => 'Ỗ',
+  'ỗ' => 'ỗ',
+  'Ộ' => 'Ộ',
+  'ộ' => 'ộ',
+  'Ớ' => 'Ớ',
+  'ớ' => 'ớ',
+  'Ờ' => 'Ờ',
+  'ờ' => 'ờ',
+  'Ở' => 'Ở',
+  'ở' => 'ở',
+  'Ỡ' => 'Ỡ',
+  'ỡ' => 'ỡ',
+  'Ợ' => 'Ợ',
+  'ợ' => 'ợ',
+  'Ụ' => 'Ụ',
+  'ụ' => 'ụ',
+  'Ủ' => 'Ủ',
+  'ủ' => 'ủ',
+  'Ứ' => 'Ứ',
+  'ứ' => 'ứ',
+  'Ừ' => 'Ừ',
+  'ừ' => 'ừ',
+  'Ử' => 'Ử',
+  'ử' => 'ử',
+  'Ữ' => 'Ữ',
+  'ữ' => 'ữ',
+  'Ự' => 'Ự',
+  'ự' => 'ự',
+  'Ỳ' => 'Ỳ',
+  'ỳ' => 'ỳ',
+  'Ỵ' => 'Ỵ',
+  'ỵ' => 'ỵ',
+  'Ỷ' => 'Ỷ',
+  'ỷ' => 'ỷ',
+  'Ỹ' => 'Ỹ',
+  'ỹ' => 'ỹ',
+  'ἀ' => 'ἀ',
+  'ἁ' => 'ἁ',
+  'ἂ' => 'ἂ',
+  'ἃ' => 'ἃ',
+  'ἄ' => 'ἄ',
+  'ἅ' => 'ἅ',
+  'ἆ' => 'ἆ',
+  'ἇ' => 'ἇ',
+  'Ἀ' => 'Ἀ',
+  'Ἁ' => 'Ἁ',
+  'Ἂ' => 'Ἂ',
+  'Ἃ' => 'Ἃ',
+  'Ἄ' => 'Ἄ',
+  'Ἅ' => 'Ἅ',
+  'Ἆ' => 'Ἆ',
+  'Ἇ' => 'Ἇ',
+  'ἐ' => 'ἐ',
+  'ἑ' => 'ἑ',
+  'ἒ' => 'ἒ',
+  'ἓ' => 'ἓ',
+  'ἔ' => 'ἔ',
+  'ἕ' => 'ἕ',
+  'Ἐ' => 'Ἐ',
+  'Ἑ' => 'Ἑ',
+  'Ἒ' => 'Ἒ',
+  'Ἓ' => 'Ἓ',
+  'Ἔ' => 'Ἔ',
+  'Ἕ' => 'Ἕ',
+  'ἠ' => 'ἠ',
+  'ἡ' => 'ἡ',
+  'ἢ' => 'ἢ',
+  'ἣ' => 'ἣ',
+  'ἤ' => 'ἤ',
+  'ἥ' => 'ἥ',
+  'ἦ' => 'ἦ',
+  'ἧ' => 'ἧ',
+  'Ἠ' => 'Ἠ',
+  'Ἡ' => 'Ἡ',
+  'Ἢ' => 'Ἢ',
+  'Ἣ' => 'Ἣ',
+  'Ἤ' => 'Ἤ',
+  'Ἥ' => 'Ἥ',
+  'Ἦ' => 'Ἦ',
+  'Ἧ' => 'Ἧ',
+  'ἰ' => 'ἰ',
+  'ἱ' => 'ἱ',
+  'ἲ' => 'ἲ',
+  'ἳ' => 'ἳ',
+  'ἴ' => 'ἴ',
+  'ἵ' => 'ἵ',
+  'ἶ' => 'ἶ',
+  'ἷ' => 'ἷ',
+  'Ἰ' => 'Ἰ',
+  'Ἱ' => 'Ἱ',
+  'Ἲ' => 'Ἲ',
+  'Ἳ' => 'Ἳ',
+  'Ἴ' => 'Ἴ',
+  'Ἵ' => 'Ἵ',
+  'Ἶ' => 'Ἶ',
+  'Ἷ' => 'Ἷ',
+  'ὀ' => 'ὀ',
+  'ὁ' => 'ὁ',
+  'ὂ' => 'ὂ',
+  'ὃ' => 'ὃ',
+  'ὄ' => 'ὄ',
+  'ὅ' => 'ὅ',
+  'Ὀ' => 'Ὀ',
+  'Ὁ' => 'Ὁ',
+  'Ὂ' => 'Ὂ',
+  'Ὃ' => 'Ὃ',
+  'Ὄ' => 'Ὄ',
+  'Ὅ' => 'Ὅ',
+  'ὐ' => 'ὐ',
+  'ὑ' => 'ὑ',
+  'ὒ' => 'ὒ',
+  'ὓ' => 'ὓ',
+  'ὔ' => 'ὔ',
+  'ὕ' => 'ὕ',
+  'ὖ' => 'ὖ',
+  'ὗ' => 'ὗ',
+  'Ὑ' => 'Ὑ',
+  'Ὓ' => 'Ὓ',
+  'Ὕ' => 'Ὕ',
+  'Ὗ' => 'Ὗ',
+  'ὠ' => 'ὠ',
+  'ὡ' => 'ὡ',
+  'ὢ' => 'ὢ',
+  'ὣ' => 'ὣ',
+  'ὤ' => 'ὤ',
+  'ὥ' => 'ὥ',
+  'ὦ' => 'ὦ',
+  'ὧ' => 'ὧ',
+  'Ὠ' => 'Ὠ',
+  'Ὡ' => 'Ὡ',
+  'Ὢ' => 'Ὢ',
+  'Ὣ' => 'Ὣ',
+  'Ὤ' => 'Ὤ',
+  'Ὥ' => 'Ὥ',
+  'Ὦ' => 'Ὦ',
+  'Ὧ' => 'Ὧ',
+  'ὰ' => 'ὰ',
+  'ά' => 'ά',
+  'ὲ' => 'ὲ',
+  'έ' => 'έ',
+  'ὴ' => 'ὴ',
+  'ή' => 'ή',
+  'ὶ' => 'ὶ',
+  'ί' => 'ί',
+  'ὸ' => 'ὸ',
+  'ό' => 'ό',
+  'ὺ' => 'ὺ',
+  'ύ' => 'ύ',
+  'ὼ' => 'ὼ',
+  'ώ' => 'ώ',
+  'ᾀ' => 'ᾀ',
+  'ᾁ' => 'ᾁ',
+  'ᾂ' => 'ᾂ',
+  'ᾃ' => 'ᾃ',
+  'ᾄ' => 'ᾄ',
+  'ᾅ' => 'ᾅ',
+  'ᾆ' => 'ᾆ',
+  'ᾇ' => 'ᾇ',
+  'ᾈ' => 'ᾈ',
+  'ᾉ' => 'ᾉ',
+  'ᾊ' => 'ᾊ',
+  'ᾋ' => 'ᾋ',
+  'ᾌ' => 'ᾌ',
+  'ᾍ' => 'ᾍ',
+  'ᾎ' => 'ᾎ',
+  'ᾏ' => 'ᾏ',
+  'ᾐ' => 'ᾐ',
+  'ᾑ' => 'ᾑ',
+  'ᾒ' => 'ᾒ',
+  'ᾓ' => 'ᾓ',
+  'ᾔ' => 'ᾔ',
+  'ᾕ' => 'ᾕ',
+  'ᾖ' => 'ᾖ',
+  'ᾗ' => 'ᾗ',
+  'ᾘ' => 'ᾘ',
+  'ᾙ' => 'ᾙ',
+  'ᾚ' => 'ᾚ',
+  'ᾛ' => 'ᾛ',
+  'ᾜ' => 'ᾜ',
+  'ᾝ' => 'ᾝ',
+  'ᾞ' => 'ᾞ',
+  'ᾟ' => 'ᾟ',
+  'ᾠ' => 'ᾠ',
+  'ᾡ' => 'ᾡ',
+  'ᾢ' => 'ᾢ',
+  'ᾣ' => 'ᾣ',
+  'ᾤ' => 'ᾤ',
+  'ᾥ' => 'ᾥ',
+  'ᾦ' => 'ᾦ',
+  'ᾧ' => 'ᾧ',
+  'ᾨ' => 'ᾨ',
+  'ᾩ' => 'ᾩ',
+  'ᾪ' => 'ᾪ',
+  'ᾫ' => 'ᾫ',
+  'ᾬ' => 'ᾬ',
+  'ᾭ' => 'ᾭ',
+  'ᾮ' => 'ᾮ',
+  'ᾯ' => 'ᾯ',
+  'ᾰ' => 'ᾰ',
+  'ᾱ' => 'ᾱ',
+  'ᾲ' => 'ᾲ',
+  'ᾳ' => 'ᾳ',
+  'ᾴ' => 'ᾴ',
+  'ᾶ' => 'ᾶ',
+  'ᾷ' => 'ᾷ',
+  'Ᾰ' => 'Ᾰ',
+  'Ᾱ' => 'Ᾱ',
+  'Ὰ' => 'Ὰ',
+  'Ά' => 'Ά',
+  'ᾼ' => 'ᾼ',
+  'ι' => 'ι',
+  '῁' => '῁',
+  'ῂ' => 'ῂ',
+  'ῃ' => 'ῃ',
+  'ῄ' => 'ῄ',
+  'ῆ' => 'ῆ',
+  'ῇ' => 'ῇ',
+  'Ὲ' => 'Ὲ',
+  'Έ' => 'Έ',
+  'Ὴ' => 'Ὴ',
+  'Ή' => 'Ή',
+  'ῌ' => 'ῌ',
+  '῍' => '῍',
+  '῎' => '῎',
+  '῏' => '῏',
+  'ῐ' => 'ῐ',
+  'ῑ' => 'ῑ',
+  'ῒ' => 'ῒ',
+  'ΐ' => 'ΐ',
+  'ῖ' => 'ῖ',
+  'ῗ' => 'ῗ',
+  'Ῐ' => 'Ῐ',
+  'Ῑ' => 'Ῑ',
+  'Ὶ' => 'Ὶ',
+  'Ί' => 'Ί',
+  '῝' => '῝',
+  '῞' => '῞',
+  '῟' => '῟',
+  'ῠ' => 'ῠ',
+  'ῡ' => 'ῡ',
+  'ῢ' => 'ῢ',
+  'ΰ' => 'ΰ',
+  'ῤ' => 'ῤ',
+  'ῥ' => 'ῥ',
+  'ῦ' => 'ῦ',
+  'ῧ' => 'ῧ',
+  'Ῠ' => 'Ῠ',
+  'Ῡ' => 'Ῡ',
+  'Ὺ' => 'Ὺ',
+  'Ύ' => 'Ύ',
+  'Ῥ' => 'Ῥ',
+  '῭' => '῭',
+  '΅' => '΅',
+  '`' => '`',
+  'ῲ' => 'ῲ',
+  'ῳ' => 'ῳ',
+  'ῴ' => 'ῴ',
+  'ῶ' => 'ῶ',
+  'ῷ' => 'ῷ',
+  'Ὸ' => 'Ὸ',
+  'Ό' => 'Ό',
+  'Ὼ' => 'Ὼ',
+  'Ώ' => 'Ώ',
+  'ῼ' => 'ῼ',
+  '´' => '´',
+  ' ' => ' ',
+  ' ' => ' ',
+  'Ω' => 'Ω',
+  'K' => 'K',
+  'Å' => 'Å',
+  '↚' => '↚',
+  '↛' => '↛',
+  '↮' => '↮',
+  '⇍' => '⇍',
+  '⇎' => '⇎',
+  '⇏' => '⇏',
+  '∄' => '∄',
+  '∉' => '∉',
+  '∌' => '∌',
+  '∤' => '∤',
+  '∦' => '∦',
+  '≁' => '≁',
+  '≄' => '≄',
+  '≇' => '≇',
+  '≉' => '≉',
+  '≠' => '≠',
+  '≢' => '≢',
+  '≭' => '≭',
+  '≮' => '≮',
+  '≯' => '≯',
+  '≰' => '≰',
+  '≱' => '≱',
+  '≴' => '≴',
+  '≵' => '≵',
+  '≸' => '≸',
+  '≹' => '≹',
+  '⊀' => '⊀',
+  '⊁' => '⊁',
+  '⊄' => '⊄',
+  '⊅' => '⊅',
+  '⊈' => '⊈',
+  '⊉' => '⊉',
+  '⊬' => '⊬',
+  '⊭' => '⊭',
+  '⊮' => '⊮',
+  '⊯' => '⊯',
+  '⋠' => '⋠',
+  '⋡' => '⋡',
+  '⋢' => '⋢',
+  '⋣' => '⋣',
+  '⋪' => '⋪',
+  '⋫' => '⋫',
+  '⋬' => '⋬',
+  '⋭' => '⋭',
+  '〈' => '〈',
+  '〉' => '〉',
+  '⫝̸' => '⫝̸',
+  'が' => 'が',
+  'ぎ' => 'ぎ',
+  'ぐ' => 'ぐ',
+  'げ' => 'げ',
+  'ご' => 'ご',
+  'ざ' => 'ざ',
+  'じ' => 'じ',
+  'ず' => 'ず',
+  'ぜ' => 'ぜ',
+  'ぞ' => 'ぞ',
+  'だ' => 'だ',
+  'ぢ' => 'ぢ',
+  'づ' => 'づ',
+  'で' => 'で',
+  'ど' => 'ど',
+  'ば' => 'ば',
+  'ぱ' => 'ぱ',
+  'び' => 'び',
+  'ぴ' => 'ぴ',
+  'ぶ' => 'ぶ',
+  'ぷ' => 'ぷ',
+  'べ' => 'べ',
+  'ぺ' => 'ぺ',
+  'ぼ' => 'ぼ',
+  'ぽ' => 'ぽ',
+  'ゔ' => 'ゔ',
+  'ゞ' => 'ゞ',
+  'ガ' => 'ガ',
+  'ギ' => 'ギ',
+  'グ' => 'グ',
+  'ゲ' => 'ゲ',
+  'ゴ' => 'ゴ',
+  'ザ' => 'ザ',
+  'ジ' => 'ジ',
+  'ズ' => 'ズ',
+  'ゼ' => 'ゼ',
+  'ゾ' => 'ゾ',
+  'ダ' => 'ダ',
+  'ヂ' => 'ヂ',
+  'ヅ' => 'ヅ',
+  'デ' => 'デ',
+  'ド' => 'ド',
+  'バ' => 'バ',
+  'パ' => 'パ',
+  'ビ' => 'ビ',
+  'ピ' => 'ピ',
+  'ブ' => 'ブ',
+  'プ' => 'プ',
+  'ベ' => 'ベ',
+  'ペ' => 'ペ',
+  'ボ' => 'ボ',
+  'ポ' => 'ポ',
+  'ヴ' => 'ヴ',
+  'ヷ' => 'ヷ',
+  'ヸ' => 'ヸ',
+  'ヹ' => 'ヹ',
+  'ヺ' => 'ヺ',
+  'ヾ' => 'ヾ',
+  '豈' => '豈',
+  '更' => '更',
+  '車' => '車',
+  '賈' => '賈',
+  '滑' => '滑',
+  '串' => '串',
+  '句' => '句',
+  '龜' => '龜',
+  '龜' => '龜',
+  '契' => '契',
+  '金' => '金',
+  '喇' => '喇',
+  '奈' => '奈',
+  '懶' => '懶',
+  '癩' => '癩',
+  '羅' => '羅',
+  '蘿' => '蘿',
+  '螺' => '螺',
+  '裸' => '裸',
+  '邏' => '邏',
+  '樂' => '樂',
+  '洛' => '洛',
+  '烙' => '烙',
+  '珞' => '珞',
+  '落' => '落',
+  '酪' => '酪',
+  '駱' => '駱',
+  '亂' => '亂',
+  '卵' => '卵',
+  '欄' => '欄',
+  '爛' => '爛',
+  '蘭' => '蘭',
+  '鸞' => '鸞',
+  '嵐' => '嵐',
+  '濫' => '濫',
+  '藍' => '藍',
+  '襤' => '襤',
+  '拉' => '拉',
+  '臘' => '臘',
+  '蠟' => '蠟',
+  '廊' => '廊',
+  '朗' => '朗',
+  '浪' => '浪',
+  '狼' => '狼',
+  '郎' => '郎',
+  '來' => '來',
+  '冷' => '冷',
+  '勞' => '勞',
+  '擄' => '擄',
+  '櫓' => '櫓',
+  '爐' => '爐',
+  '盧' => '盧',
+  '老' => '老',
+  '蘆' => '蘆',
+  '虜' => '虜',
+  '路' => '路',
+  '露' => '露',
+  '魯' => '魯',
+  '鷺' => '鷺',
+  '碌' => '碌',
+  '祿' => '祿',
+  '綠' => '綠',
+  '菉' => '菉',
+  '錄' => '錄',
+  '鹿' => '鹿',
+  '論' => '論',
+  '壟' => '壟',
+  '弄' => '弄',
+  '籠' => '籠',
+  '聾' => '聾',
+  '牢' => '牢',
+  '磊' => '磊',
+  '賂' => '賂',
+  '雷' => '雷',
+  '壘' => '壘',
+  '屢' => '屢',
+  '樓' => '樓',
+  '淚' => '淚',
+  '漏' => '漏',
+  '累' => '累',
+  '縷' => '縷',
+  '陋' => '陋',
+  '勒' => '勒',
+  '肋' => '肋',
+  '凜' => '凜',
+  '凌' => '凌',
+  '稜' => '稜',
+  '綾' => '綾',
+  '菱' => '菱',
+  '陵' => '陵',
+  '讀' => '讀',
+  '拏' => '拏',
+  '樂' => '樂',
+  '諾' => '諾',
+  '丹' => '丹',
+  '寧' => '寧',
+  '怒' => '怒',
+  '率' => '率',
+  '異' => '異',
+  '北' => '北',
+  '磻' => '磻',
+  '便' => '便',
+  '復' => '復',
+  '不' => '不',
+  '泌' => '泌',
+  '數' => '數',
+  '索' => '索',
+  '參' => '參',
+  '塞' => '塞',
+  '省' => '省',
+  '葉' => '葉',
+  '說' => '說',
+  '殺' => '殺',
+  '辰' => '辰',
+  '沈' => '沈',
+  '拾' => '拾',
+  '若' => '若',
+  '掠' => '掠',
+  '略' => '略',
+  '亮' => '亮',
+  '兩' => '兩',
+  '凉' => '凉',
+  '梁' => '梁',
+  '糧' => '糧',
+  '良' => '良',
+  '諒' => '諒',
+  '量' => '量',
+  '勵' => '勵',
+  '呂' => '呂',
+  '女' => '女',
+  '廬' => '廬',
+  '旅' => '旅',
+  '濾' => '濾',
+  '礪' => '礪',
+  '閭' => '閭',
+  '驪' => '驪',
+  '麗' => '麗',
+  '黎' => '黎',
+  '力' => '力',
+  '曆' => '曆',
+  '歷' => '歷',
+  '轢' => '轢',
+  '年' => '年',
+  '憐' => '憐',
+  '戀' => '戀',
+  '撚' => '撚',
+  '漣' => '漣',
+  '煉' => '煉',
+  '璉' => '璉',
+  '秊' => '秊',
+  '練' => '練',
+  '聯' => '聯',
+  '輦' => '輦',
+  '蓮' => '蓮',
+  '連' => '連',
+  '鍊' => '鍊',
+  '列' => '列',
+  '劣' => '劣',
+  '咽' => '咽',
+  '烈' => '烈',
+  '裂' => '裂',
+  '說' => '說',
+  '廉' => '廉',
+  '念' => '念',
+  '捻' => '捻',
+  '殮' => '殮',
+  '簾' => '簾',
+  '獵' => '獵',
+  '令' => '令',
+  '囹' => '囹',
+  '寧' => '寧',
+  '嶺' => '嶺',
+  '怜' => '怜',
+  '玲' => '玲',
+  '瑩' => '瑩',
+  '羚' => '羚',
+  '聆' => '聆',
+  '鈴' => '鈴',
+  '零' => '零',
+  '靈' => '靈',
+  '領' => '領',
+  '例' => '例',
+  '禮' => '禮',
+  '醴' => '醴',
+  '隸' => '隸',
+  '惡' => '惡',
+  '了' => '了',
+  '僚' => '僚',
+  '寮' => '寮',
+  '尿' => '尿',
+  '料' => '料',
+  '樂' => '樂',
+  '燎' => '燎',
+  '療' => '療',
+  '蓼' => '蓼',
+  '遼' => '遼',
+  '龍' => '龍',
+  '暈' => '暈',
+  '阮' => '阮',
+  '劉' => '劉',
+  '杻' => '杻',
+  '柳' => '柳',
+  '流' => '流',
+  '溜' => '溜',
+  '琉' => '琉',
+  '留' => '留',
+  '硫' => '硫',
+  '紐' => '紐',
+  '類' => '類',
+  '六' => '六',
+  '戮' => '戮',
+  '陸' => '陸',
+  '倫' => '倫',
+  '崙' => '崙',
+  '淪' => '淪',
+  '輪' => '輪',
+  '律' => '律',
+  '慄' => '慄',
+  '栗' => '栗',
+  '率' => '率',
+  '隆' => '隆',
+  '利' => '利',
+  '吏' => '吏',
+  '履' => '履',
+  '易' => '易',
+  '李' => '李',
+  '梨' => '梨',
+  '泥' => '泥',
+  '理' => '理',
+  '痢' => '痢',
+  '罹' => '罹',
+  '裏' => '裏',
+  '裡' => '裡',
+  '里' => '里',
+  '離' => '離',
+  '匿' => '匿',
+  '溺' => '溺',
+  '吝' => '吝',
+  '燐' => '燐',
+  '璘' => '璘',
+  '藺' => '藺',
+  '隣' => '隣',
+  '鱗' => '鱗',
+  '麟' => '麟',
+  '林' => '林',
+  '淋' => '淋',
+  '臨' => '臨',
+  '立' => '立',
+  '笠' => '笠',
+  '粒' => '粒',
+  '狀' => '狀',
+  '炙' => '炙',
+  '識' => '識',
+  '什' => '什',
+  '茶' => '茶',
+  '刺' => '刺',
+  '切' => '切',
+  '度' => '度',
+  '拓' => '拓',
+  '糖' => '糖',
+  '宅' => '宅',
+  '洞' => '洞',
+  '暴' => '暴',
+  '輻' => '輻',
+  '行' => '行',
+  '降' => '降',
+  '見' => '見',
+  '廓' => '廓',
+  '兀' => '兀',
+  '嗀' => '嗀',
+  '塚' => '塚',
+  '晴' => '晴',
+  '凞' => '凞',
+  '猪' => '猪',
+  '益' => '益',
+  '礼' => '礼',
+  '神' => '神',
+  '祥' => '祥',
+  '福' => '福',
+  '靖' => '靖',
+  '精' => '精',
+  '羽' => '羽',
+  '蘒' => '蘒',
+  '諸' => '諸',
+  '逸' => '逸',
+  '都' => '都',
+  '飯' => '飯',
+  '飼' => '飼',
+  '館' => '館',
+  '鶴' => '鶴',
+  '郞' => '郞',
+  '隷' => '隷',
+  '侮' => '侮',
+  '僧' => '僧',
+  '免' => '免',
+  '勉' => '勉',
+  '勤' => '勤',
+  '卑' => '卑',
+  '喝' => '喝',
+  '嘆' => '嘆',
+  '器' => '器',
+  '塀' => '塀',
+  '墨' => '墨',
+  '層' => '層',
+  '屮' => '屮',
+  '悔' => '悔',
+  '慨' => '慨',
+  '憎' => '憎',
+  '懲' => '懲',
+  '敏' => '敏',
+  '既' => '既',
+  '暑' => '暑',
+  '梅' => '梅',
+  '海' => '海',
+  '渚' => '渚',
+  '漢' => '漢',
+  '煮' => '煮',
+  '爫' => '爫',
+  '琢' => '琢',
+  '碑' => '碑',
+  '社' => '社',
+  '祉' => '祉',
+  '祈' => '祈',
+  '祐' => '祐',
+  '祖' => '祖',
+  '祝' => '祝',
+  '禍' => '禍',
+  '禎' => '禎',
+  '穀' => '穀',
+  '突' => '突',
+  '節' => '節',
+  '練' => '練',
+  '縉' => '縉',
+  '繁' => '繁',
+  '署' => '署',
+  '者' => '者',
+  '臭' => '臭',
+  '艹' => '艹',
+  '艹' => '艹',
+  '著' => '著',
+  '褐' => '褐',
+  '視' => '視',
+  '謁' => '謁',
+  '謹' => '謹',
+  '賓' => '賓',
+  '贈' => '贈',
+  '辶' => '辶',
+  '逸' => '逸',
+  '難' => '難',
+  '響' => '響',
+  '頻' => '頻',
+  '恵' => '恵',
+  '𤋮' => '𤋮',
+  '舘' => '舘',
+  '並' => '並',
+  '况' => '况',
+  '全' => '全',
+  '侀' => '侀',
+  '充' => '充',
+  '冀' => '冀',
+  '勇' => '勇',
+  '勺' => '勺',
+  '喝' => '喝',
+  '啕' => '啕',
+  '喙' => '喙',
+  '嗢' => '嗢',
+  '塚' => '塚',
+  '墳' => '墳',
+  '奄' => '奄',
+  '奔' => '奔',
+  '婢' => '婢',
+  '嬨' => '嬨',
+  '廒' => '廒',
+  '廙' => '廙',
+  '彩' => '彩',
+  '徭' => '徭',
+  '惘' => '惘',
+  '慎' => '慎',
+  '愈' => '愈',
+  '憎' => '憎',
+  '慠' => '慠',
+  '懲' => '懲',
+  '戴' => '戴',
+  '揄' => '揄',
+  '搜' => '搜',
+  '摒' => '摒',
+  '敖' => '敖',
+  '晴' => '晴',
+  '朗' => '朗',
+  '望' => '望',
+  '杖' => '杖',
+  '歹' => '歹',
+  '殺' => '殺',
+  '流' => '流',
+  '滛' => '滛',
+  '滋' => '滋',
+  '漢' => '漢',
+  '瀞' => '瀞',
+  '煮' => '煮',
+  '瞧' => '瞧',
+  '爵' => '爵',
+  '犯' => '犯',
+  '猪' => '猪',
+  '瑱' => '瑱',
+  '甆' => '甆',
+  '画' => '画',
+  '瘝' => '瘝',
+  '瘟' => '瘟',
+  '益' => '益',
+  '盛' => '盛',
+  '直' => '直',
+  '睊' => '睊',
+  '着' => '着',
+  '磌' => '磌',
+  '窱' => '窱',
+  '節' => '節',
+  '类' => '类',
+  '絛' => '絛',
+  '練' => '練',
+  '缾' => '缾',
+  '者' => '者',
+  '荒' => '荒',
+  '華' => '華',
+  '蝹' => '蝹',
+  '襁' => '襁',
+  '覆' => '覆',
+  '視' => '視',
+  '調' => '調',
+  '諸' => '諸',
+  '請' => '請',
+  '謁' => '謁',
+  '諾' => '諾',
+  '諭' => '諭',
+  '謹' => '謹',
+  '變' => '變',
+  '贈' => '贈',
+  '輸' => '輸',
+  '遲' => '遲',
+  '醙' => '醙',
+  '鉶' => '鉶',
+  '陼' => '陼',
+  '難' => '難',
+  '靖' => '靖',
+  '韛' => '韛',
+  '響' => '響',
+  '頋' => '頋',
+  '頻' => '頻',
+  '鬒' => '鬒',
+  '龜' => '龜',
+  '𢡊' => '𢡊',
+  '𢡄' => '𢡄',
+  '𣏕' => '𣏕',
+  '㮝' => '㮝',
+  '䀘' => '䀘',
+  '䀹' => '䀹',
+  '𥉉' => '𥉉',
+  '𥳐' => '𥳐',
+  '𧻓' => '𧻓',
+  '齃' => '齃',
+  '龎' => '龎',
+  'יִ' => 'יִ',
+  'ײַ' => 'ײַ',
+  'שׁ' => 'שׁ',
+  'שׂ' => 'שׂ',
+  'שּׁ' => 'שּׁ',
+  'שּׂ' => 'שּׂ',
+  'אַ' => 'אַ',
+  'אָ' => 'אָ',
+  'אּ' => 'אּ',
+  'בּ' => 'בּ',
+  'גּ' => 'גּ',
+  'דּ' => 'דּ',
+  'הּ' => 'הּ',
+  'וּ' => 'וּ',
+  'זּ' => 'זּ',
+  'טּ' => 'טּ',
+  'יּ' => 'יּ',
+  'ךּ' => 'ךּ',
+  'כּ' => 'כּ',
+  'לּ' => 'לּ',
+  'מּ' => 'מּ',
+  'נּ' => 'נּ',
+  'סּ' => 'סּ',
+  'ףּ' => 'ףּ',
+  'פּ' => 'פּ',
+  'צּ' => 'צּ',
+  'קּ' => 'קּ',
+  'רּ' => 'רּ',
+  'שּ' => 'שּ',
+  'תּ' => 'תּ',
+  'וֹ' => 'וֹ',
+  'בֿ' => 'בֿ',
+  'כֿ' => 'כֿ',
+  'פֿ' => 'פֿ',
+  '𑂚' => '𑂚',
+  '𑂜' => '𑂜',
+  '𑂫' => '𑂫',
+  '𑄮' => '𑄮',
+  '𑄯' => '𑄯',
+  '𑍋' => '𑍋',
+  '𑍌' => '𑍌',
+  '𑒻' => '𑒻',
+  '𑒼' => '𑒼',
+  '𑒾' => '𑒾',
+  '𑖺' => '𑖺',
+  '𑖻' => '𑖻',
+  '𑤸' => '𑤸',
+  '𝅗𝅥' => '𝅗𝅥',
+  '𝅘𝅥' => '𝅘𝅥',
+  '𝅘𝅥𝅮' => '𝅘𝅥𝅮',
+  '𝅘𝅥𝅯' => '𝅘𝅥𝅯',
+  '𝅘𝅥𝅰' => '𝅘𝅥𝅰',
+  '𝅘𝅥𝅱' => '𝅘𝅥𝅱',
+  '𝅘𝅥𝅲' => '𝅘𝅥𝅲',
+  '𝆹𝅥' => '𝆹𝅥',
+  '𝆺𝅥' => '𝆺𝅥',
+  '𝆹𝅥𝅮' => '𝆹𝅥𝅮',
+  '𝆺𝅥𝅮' => '𝆺𝅥𝅮',
+  '𝆹𝅥𝅯' => '𝆹𝅥𝅯',
+  '𝆺𝅥𝅯' => '𝆺𝅥𝅯',
+  '丽' => '丽',
+  '丸' => '丸',
+  '乁' => '乁',
+  '𠄢' => '𠄢',
+  '你' => '你',
+  '侮' => '侮',
+  '侻' => '侻',
+  '倂' => '倂',
+  '偺' => '偺',
+  '備' => '備',
+  '僧' => '僧',
+  '像' => '像',
+  '㒞' => '㒞',
+  '𠘺' => '𠘺',
+  '免' => '免',
+  '兔' => '兔',
+  '兤' => '兤',
+  '具' => '具',
+  '𠔜' => '𠔜',
+  '㒹' => '㒹',
+  '內' => '內',
+  '再' => '再',
+  '𠕋' => '𠕋',
+  '冗' => '冗',
+  '冤' => '冤',
+  '仌' => '仌',
+  '冬' => '冬',
+  '况' => '况',
+  '𩇟' => '𩇟',
+  '凵' => '凵',
+  '刃' => '刃',
+  '㓟' => '㓟',
+  '刻' => '刻',
+  '剆' => '剆',
+  '割' => '割',
+  '剷' => '剷',
+  '㔕' => '㔕',
+  '勇' => '勇',
+  '勉' => '勉',
+  '勤' => '勤',
+  '勺' => '勺',
+  '包' => '包',
+  '匆' => '匆',
+  '北' => '北',
+  '卉' => '卉',
+  '卑' => '卑',
+  '博' => '博',
+  '即' => '即',
+  '卽' => '卽',
+  '卿' => '卿',
+  '卿' => '卿',
+  '卿' => '卿',
+  '𠨬' => '𠨬',
+  '灰' => '灰',
+  '及' => '及',
+  '叟' => '叟',
+  '𠭣' => '𠭣',
+  '叫' => '叫',
+  '叱' => '叱',
+  '吆' => '吆',
+  '咞' => '咞',
+  '吸' => '吸',
+  '呈' => '呈',
+  '周' => '周',
+  '咢' => '咢',
+  '哶' => '哶',
+  '唐' => '唐',
+  '啓' => '啓',
+  '啣' => '啣',
+  '善' => '善',
+  '善' => '善',
+  '喙' => '喙',
+  '喫' => '喫',
+  '喳' => '喳',
+  '嗂' => '嗂',
+  '圖' => '圖',
+  '嘆' => '嘆',
+  '圗' => '圗',
+  '噑' => '噑',
+  '噴' => '噴',
+  '切' => '切',
+  '壮' => '壮',
+  '城' => '城',
+  '埴' => '埴',
+  '堍' => '堍',
+  '型' => '型',
+  '堲' => '堲',
+  '報' => '報',
+  '墬' => '墬',
+  '𡓤' => '𡓤',
+  '売' => '売',
+  '壷' => '壷',
+  '夆' => '夆',
+  '多' => '多',
+  '夢' => '夢',
+  '奢' => '奢',
+  '𡚨' => '𡚨',
+  '𡛪' => '𡛪',
+  '姬' => '姬',
+  '娛' => '娛',
+  '娧' => '娧',
+  '姘' => '姘',
+  '婦' => '婦',
+  '㛮' => '㛮',
+  '㛼' => '㛼',
+  '嬈' => '嬈',
+  '嬾' => '嬾',
+  '嬾' => '嬾',
+  '𡧈' => '𡧈',
+  '寃' => '寃',
+  '寘' => '寘',
+  '寧' => '寧',
+  '寳' => '寳',
+  '𡬘' => '𡬘',
+  '寿' => '寿',
+  '将' => '将',
+  '当' => '当',
+  '尢' => '尢',
+  '㞁' => '㞁',
+  '屠' => '屠',
+  '屮' => '屮',
+  '峀' => '峀',
+  '岍' => '岍',
+  '𡷤' => '𡷤',
+  '嵃' => '嵃',
+  '𡷦' => '𡷦',
+  '嵮' => '嵮',
+  '嵫' => '嵫',
+  '嵼' => '嵼',
+  '巡' => '巡',
+  '巢' => '巢',
+  '㠯' => '㠯',
+  '巽' => '巽',
+  '帨' => '帨',
+  '帽' => '帽',
+  '幩' => '幩',
+  '㡢' => '㡢',
+  '𢆃' => '𢆃',
+  '㡼' => '㡼',
+  '庰' => '庰',
+  '庳' => '庳',
+  '庶' => '庶',
+  '廊' => '廊',
+  '𪎒' => '𪎒',
+  '廾' => '廾',
+  '𢌱' => '𢌱',
+  '𢌱' => '𢌱',
+  '舁' => '舁',
+  '弢' => '弢',
+  '弢' => '弢',
+  '㣇' => '㣇',
+  '𣊸' => '𣊸',
+  '𦇚' => '𦇚',
+  '形' => '形',
+  '彫' => '彫',
+  '㣣' => '㣣',
+  '徚' => '徚',
+  '忍' => '忍',
+  '志' => '志',
+  '忹' => '忹',
+  '悁' => '悁',
+  '㤺' => '㤺',
+  '㤜' => '㤜',
+  '悔' => '悔',
+  '𢛔' => '𢛔',
+  '惇' => '惇',
+  '慈' => '慈',
+  '慌' => '慌',
+  '慎' => '慎',
+  '慌' => '慌',
+  '慺' => '慺',
+  '憎' => '憎',
+  '憲' => '憲',
+  '憤' => '憤',
+  '憯' => '憯',
+  '懞' => '懞',
+  '懲' => '懲',
+  '懶' => '懶',
+  '成' => '成',
+  '戛' => '戛',
+  '扝' => '扝',
+  '抱' => '抱',
+  '拔' => '拔',
+  '捐' => '捐',
+  '𢬌' => '𢬌',
+  '挽' => '挽',
+  '拼' => '拼',
+  '捨' => '捨',
+  '掃' => '掃',
+  '揤' => '揤',
+  '𢯱' => '𢯱',
+  '搢' => '搢',
+  '揅' => '揅',
+  '掩' => '掩',
+  '㨮' => '㨮',
+  '摩' => '摩',
+  '摾' => '摾',
+  '撝' => '撝',
+  '摷' => '摷',
+  '㩬' => '㩬',
+  '敏' => '敏',
+  '敬' => '敬',
+  '𣀊' => '𣀊',
+  '旣' => '旣',
+  '書' => '書',
+  '晉' => '晉',
+  '㬙' => '㬙',
+  '暑' => '暑',
+  '㬈' => '㬈',
+  '㫤' => '㫤',
+  '冒' => '冒',
+  '冕' => '冕',
+  '最' => '最',
+  '暜' => '暜',
+  '肭' => '肭',
+  '䏙' => '䏙',
+  '朗' => '朗',
+  '望' => '望',
+  '朡' => '朡',
+  '杞' => '杞',
+  '杓' => '杓',
+  '𣏃' => '𣏃',
+  '㭉' => '㭉',
+  '柺' => '柺',
+  '枅' => '枅',
+  '桒' => '桒',
+  '梅' => '梅',
+  '𣑭' => '𣑭',
+  '梎' => '梎',
+  '栟' => '栟',
+  '椔' => '椔',
+  '㮝' => '㮝',
+  '楂' => '楂',
+  '榣' => '榣',
+  '槪' => '槪',
+  '檨' => '檨',
+  '𣚣' => '𣚣',
+  '櫛' => '櫛',
+  '㰘' => '㰘',
+  '次' => '次',
+  '𣢧' => '𣢧',
+  '歔' => '歔',
+  '㱎' => '㱎',
+  '歲' => '歲',
+  '殟' => '殟',
+  '殺' => '殺',
+  '殻' => '殻',
+  '𣪍' => '𣪍',
+  '𡴋' => '𡴋',
+  '𣫺' => '𣫺',
+  '汎' => '汎',
+  '𣲼' => '𣲼',
+  '沿' => '沿',
+  '泍' => '泍',
+  '汧' => '汧',
+  '洖' => '洖',
+  '派' => '派',
+  '海' => '海',
+  '流' => '流',
+  '浩' => '浩',
+  '浸' => '浸',
+  '涅' => '涅',
+  '𣴞' => '𣴞',
+  '洴' => '洴',
+  '港' => '港',
+  '湮' => '湮',
+  '㴳' => '㴳',
+  '滋' => '滋',
+  '滇' => '滇',
+  '𣻑' => '𣻑',
+  '淹' => '淹',
+  '潮' => '潮',
+  '𣽞' => '𣽞',
+  '𣾎' => '𣾎',
+  '濆' => '濆',
+  '瀹' => '瀹',
+  '瀞' => '瀞',
+  '瀛' => '瀛',
+  '㶖' => '㶖',
+  '灊' => '灊',
+  '災' => '災',
+  '灷' => '灷',
+  '炭' => '炭',
+  '𠔥' => '𠔥',
+  '煅' => '煅',
+  '𤉣' => '𤉣',
+  '熜' => '熜',
+  '𤎫' => '𤎫',
+  '爨' => '爨',
+  '爵' => '爵',
+  '牐' => '牐',
+  '𤘈' => '𤘈',
+  '犀' => '犀',
+  '犕' => '犕',
+  '𤜵' => '𤜵',
+  '𤠔' => '𤠔',
+  '獺' => '獺',
+  '王' => '王',
+  '㺬' => '㺬',
+  '玥' => '玥',
+  '㺸' => '㺸',
+  '㺸' => '㺸',
+  '瑇' => '瑇',
+  '瑜' => '瑜',
+  '瑱' => '瑱',
+  '璅' => '璅',
+  '瓊' => '瓊',
+  '㼛' => '㼛',
+  '甤' => '甤',
+  '𤰶' => '𤰶',
+  '甾' => '甾',
+  '𤲒' => '𤲒',
+  '異' => '異',
+  '𢆟' => '𢆟',
+  '瘐' => '瘐',
+  '𤾡' => '𤾡',
+  '𤾸' => '𤾸',
+  '𥁄' => '𥁄',
+  '㿼' => '㿼',
+  '䀈' => '䀈',
+  '直' => '直',
+  '𥃳' => '𥃳',
+  '𥃲' => '𥃲',
+  '𥄙' => '𥄙',
+  '𥄳' => '𥄳',
+  '眞' => '眞',
+  '真' => '真',
+  '真' => '真',
+  '睊' => '睊',
+  '䀹' => '䀹',
+  '瞋' => '瞋',
+  '䁆' => '䁆',
+  '䂖' => '䂖',
+  '𥐝' => '𥐝',
+  '硎' => '硎',
+  '碌' => '碌',
+  '磌' => '磌',
+  '䃣' => '䃣',
+  '𥘦' => '𥘦',
+  '祖' => '祖',
+  '𥚚' => '𥚚',
+  '𥛅' => '𥛅',
+  '福' => '福',
+  '秫' => '秫',
+  '䄯' => '䄯',
+  '穀' => '穀',
+  '穊' => '穊',
+  '穏' => '穏',
+  '𥥼' => '𥥼',
+  '𥪧' => '𥪧',
+  '𥪧' => '𥪧',
+  '竮' => '竮',
+  '䈂' => '䈂',
+  '𥮫' => '𥮫',
+  '篆' => '篆',
+  '築' => '築',
+  '䈧' => '䈧',
+  '𥲀' => '𥲀',
+  '糒' => '糒',
+  '䊠' => '䊠',
+  '糨' => '糨',
+  '糣' => '糣',
+  '紀' => '紀',
+  '𥾆' => '𥾆',
+  '絣' => '絣',
+  '䌁' => '䌁',
+  '緇' => '緇',
+  '縂' => '縂',
+  '繅' => '繅',
+  '䌴' => '䌴',
+  '𦈨' => '𦈨',
+  '𦉇' => '𦉇',
+  '䍙' => '䍙',
+  '𦋙' => '𦋙',
+  '罺' => '罺',
+  '𦌾' => '𦌾',
+  '羕' => '羕',
+  '翺' => '翺',
+  '者' => '者',
+  '𦓚' => '𦓚',
+  '𦔣' => '𦔣',
+  '聠' => '聠',
+  '𦖨' => '𦖨',
+  '聰' => '聰',
+  '𣍟' => '𣍟',
+  '䏕' => '䏕',
+  '育' => '育',
+  '脃' => '脃',
+  '䐋' => '䐋',
+  '脾' => '脾',
+  '媵' => '媵',
+  '𦞧' => '𦞧',
+  '𦞵' => '𦞵',
+  '𣎓' => '𣎓',
+  '𣎜' => '𣎜',
+  '舁' => '舁',
+  '舄' => '舄',
+  '辞' => '辞',
+  '䑫' => '䑫',
+  '芑' => '芑',
+  '芋' => '芋',
+  '芝' => '芝',
+  '劳' => '劳',
+  '花' => '花',
+  '芳' => '芳',
+  '芽' => '芽',
+  '苦' => '苦',
+  '𦬼' => '𦬼',
+  '若' => '若',
+  '茝' => '茝',
+  '荣' => '荣',
+  '莭' => '莭',
+  '茣' => '茣',
+  '莽' => '莽',
+  '菧' => '菧',
+  '著' => '著',
+  '荓' => '荓',
+  '菊' => '菊',
+  '菌' => '菌',
+  '菜' => '菜',
+  '𦰶' => '𦰶',
+  '𦵫' => '𦵫',
+  '𦳕' => '𦳕',
+  '䔫' => '䔫',
+  '蓱' => '蓱',
+  '蓳' => '蓳',
+  '蔖' => '蔖',
+  '𧏊' => '𧏊',
+  '蕤' => '蕤',
+  '𦼬' => '𦼬',
+  '䕝' => '䕝',
+  '䕡' => '䕡',
+  '𦾱' => '𦾱',
+  '𧃒' => '𧃒',
+  '䕫' => '䕫',
+  '虐' => '虐',
+  '虜' => '虜',
+  '虧' => '虧',
+  '虩' => '虩',
+  '蚩' => '蚩',
+  '蚈' => '蚈',
+  '蜎' => '蜎',
+  '蛢' => '蛢',
+  '蝹' => '蝹',
+  '蜨' => '蜨',
+  '蝫' => '蝫',
+  '螆' => '螆',
+  '䗗' => '䗗',
+  '蟡' => '蟡',
+  '蠁' => '蠁',
+  '䗹' => '䗹',
+  '衠' => '衠',
+  '衣' => '衣',
+  '𧙧' => '𧙧',
+  '裗' => '裗',
+  '裞' => '裞',
+  '䘵' => '䘵',
+  '裺' => '裺',
+  '㒻' => '㒻',
+  '𧢮' => '𧢮',
+  '𧥦' => '𧥦',
+  '䚾' => '䚾',
+  '䛇' => '䛇',
+  '誠' => '誠',
+  '諭' => '諭',
+  '變' => '變',
+  '豕' => '豕',
+  '𧲨' => '𧲨',
+  '貫' => '貫',
+  '賁' => '賁',
+  '贛' => '贛',
+  '起' => '起',
+  '𧼯' => '𧼯',
+  '𠠄' => '𠠄',
+  '跋' => '跋',
+  '趼' => '趼',
+  '跰' => '跰',
+  '𠣞' => '𠣞',
+  '軔' => '軔',
+  '輸' => '輸',
+  '𨗒' => '𨗒',
+  '𨗭' => '𨗭',
+  '邔' => '邔',
+  '郱' => '郱',
+  '鄑' => '鄑',
+  '𨜮' => '𨜮',
+  '鄛' => '鄛',
+  '鈸' => '鈸',
+  '鋗' => '鋗',
+  '鋘' => '鋘',
+  '鉼' => '鉼',
+  '鏹' => '鏹',
+  '鐕' => '鐕',
+  '𨯺' => '𨯺',
+  '開' => '開',
+  '䦕' => '䦕',
+  '閷' => '閷',
+  '𨵷' => '𨵷',
+  '䧦' => '䧦',
+  '雃' => '雃',
+  '嶲' => '嶲',
+  '霣' => '霣',
+  '𩅅' => '𩅅',
+  '𩈚' => '𩈚',
+  '䩮' => '䩮',
+  '䩶' => '䩶',
+  '韠' => '韠',
+  '𩐊' => '𩐊',
+  '䪲' => '䪲',
+  '𩒖' => '𩒖',
+  '頋' => '頋',
+  '頋' => '頋',
+  '頩' => '頩',
+  '𩖶' => '𩖶',
+  '飢' => '飢',
+  '䬳' => '䬳',
+  '餩' => '餩',
+  '馧' => '馧',
+  '駂' => '駂',
+  '駾' => '駾',
+  '䯎' => '䯎',
+  '𩬰' => '𩬰',
+  '鬒' => '鬒',
+  '鱀' => '鱀',
+  '鳽' => '鳽',
+  '䳎' => '䳎',
+  '䳭' => '䳭',
+  '鵧' => '鵧',
+  '𪃎' => '𪃎',
+  '䳸' => '䳸',
+  '𪄅' => '𪄅',
+  '𪈎' => '𪈎',
+  '𪊑' => '𪊑',
+  '麻' => '麻',
+  '䵖' => '䵖',
+  '黹' => '黹',
+  '黾' => '黾',
+  '鼅' => '鼅',
+  '鼏' => '鼏',
+  '鼖' => '鼖',
+  '鼻' => '鼻',
+  '𪘀' => '𪘀',
+);
diff --git a/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/combiningClass.php b/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/combiningClass.php
new file mode 100755
index 0000000..ec90f36
--- /dev/null
+++ b/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/combiningClass.php
@@ -0,0 +1,876 @@
+<?php
+
+return array (
+  '̀' => 230,
+  '́' => 230,
+  '̂' => 230,
+  '̃' => 230,
+  '̄' => 230,
+  '̅' => 230,
+  '̆' => 230,
+  '̇' => 230,
+  '̈' => 230,
+  '̉' => 230,
+  '̊' => 230,
+  '̋' => 230,
+  '̌' => 230,
+  '̍' => 230,
+  '̎' => 230,
+  '̏' => 230,
+  '̐' => 230,
+  '̑' => 230,
+  '̒' => 230,
+  '̓' => 230,
+  '̔' => 230,
+  '̕' => 232,
+  '̖' => 220,
+  '̗' => 220,
+  '̘' => 220,
+  '̙' => 220,
+  '̚' => 232,
+  '̛' => 216,
+  '̜' => 220,
+  '̝' => 220,
+  '̞' => 220,
+  '̟' => 220,
+  '̠' => 220,
+  '̡' => 202,
+  '̢' => 202,
+  '̣' => 220,
+  '̤' => 220,
+  '̥' => 220,
+  '̦' => 220,
+  '̧' => 202,
+  '̨' => 202,
+  '̩' => 220,
+  '̪' => 220,
+  '̫' => 220,
+  '̬' => 220,
+  '̭' => 220,
+  '̮' => 220,
+  '̯' => 220,
+  '̰' => 220,
+  '̱' => 220,
+  '̲' => 220,
+  '̳' => 220,
+  '̴' => 1,
+  '̵' => 1,
+  '̶' => 1,
+  '̷' => 1,
+  '̸' => 1,
+  '̹' => 220,
+  '̺' => 220,
+  '̻' => 220,
+  '̼' => 220,
+  '̽' => 230,
+  '̾' => 230,
+  '̿' => 230,
+  '̀' => 230,
+  '́' => 230,
+  '͂' => 230,
+  '̓' => 230,
+  '̈́' => 230,
+  'ͅ' => 240,
+  '͆' => 230,
+  '͇' => 220,
+  '͈' => 220,
+  '͉' => 220,
+  '͊' => 230,
+  '͋' => 230,
+  '͌' => 230,
+  '͍' => 220,
+  '͎' => 220,
+  '͐' => 230,
+  '͑' => 230,
+  '͒' => 230,
+  '͓' => 220,
+  '͔' => 220,
+  '͕' => 220,
+  '͖' => 220,
+  '͗' => 230,
+  '͘' => 232,
+  '͙' => 220,
+  '͚' => 220,
+  '͛' => 230,
+  '͜' => 233,
+  '͝' => 234,
+  '͞' => 234,
+  '͟' => 233,
+  '͠' => 234,
+  '͡' => 234,
+  '͢' => 233,
+  'ͣ' => 230,
+  'ͤ' => 230,
+  'ͥ' => 230,
+  'ͦ' => 230,
+  'ͧ' => 230,
+  'ͨ' => 230,
+  'ͩ' => 230,
+  'ͪ' => 230,
+  'ͫ' => 230,
+  'ͬ' => 230,
+  'ͭ' => 230,
+  'ͮ' => 230,
+  'ͯ' => 230,
+  '҃' => 230,
+  '҄' => 230,
+  '҅' => 230,
+  '҆' => 230,
+  '҇' => 230,
+  '֑' => 220,
+  '֒' => 230,
+  '֓' => 230,
+  '֔' => 230,
+  '֕' => 230,
+  '֖' => 220,
+  '֗' => 230,
+  '֘' => 230,
+  '֙' => 230,
+  '֚' => 222,
+  '֛' => 220,
+  '֜' => 230,
+  '֝' => 230,
+  '֞' => 230,
+  '֟' => 230,
+  '֠' => 230,
+  '֡' => 230,
+  '֢' => 220,
+  '֣' => 220,
+  '֤' => 220,
+  '֥' => 220,
+  '֦' => 220,
+  '֧' => 220,
+  '֨' => 230,
+  '֩' => 230,
+  '֪' => 220,
+  '֫' => 230,
+  '֬' => 230,
+  '֭' => 222,
+  '֮' => 228,
+  '֯' => 230,
+  'ְ' => 10,
+  'ֱ' => 11,
+  'ֲ' => 12,
+  'ֳ' => 13,
+  'ִ' => 14,
+  'ֵ' => 15,
+  'ֶ' => 16,
+  'ַ' => 17,
+  'ָ' => 18,
+  'ֹ' => 19,
+  'ֺ' => 19,
+  'ֻ' => 20,
+  'ּ' => 21,
+  'ֽ' => 22,
+  'ֿ' => 23,
+  'ׁ' => 24,
+  'ׂ' => 25,
+  'ׄ' => 230,
+  'ׅ' => 220,
+  'ׇ' => 18,
+  'ؐ' => 230,
+  'ؑ' => 230,
+  'ؒ' => 230,
+  'ؓ' => 230,
+  'ؔ' => 230,
+  'ؕ' => 230,
+  'ؖ' => 230,
+  'ؗ' => 230,
+  'ؘ' => 30,
+  'ؙ' => 31,
+  'ؚ' => 32,
+  'ً' => 27,
+  'ٌ' => 28,
+  'ٍ' => 29,
+  'َ' => 30,
+  'ُ' => 31,
+  'ِ' => 32,
+  'ّ' => 33,
+  'ْ' => 34,
+  'ٓ' => 230,
+  'ٔ' => 230,
+  'ٕ' => 220,
+  'ٖ' => 220,
+  'ٗ' => 230,
+  '٘' => 230,
+  'ٙ' => 230,
+  'ٚ' => 230,
+  'ٛ' => 230,
+  'ٜ' => 220,
+  'ٝ' => 230,
+  'ٞ' => 230,
+  'ٟ' => 220,
+  'ٰ' => 35,
+  'ۖ' => 230,
+  'ۗ' => 230,
+  'ۘ' => 230,
+  'ۙ' => 230,
+  'ۚ' => 230,
+  'ۛ' => 230,
+  'ۜ' => 230,
+  '۟' => 230,
+  '۠' => 230,
+  'ۡ' => 230,
+  'ۢ' => 230,
+  'ۣ' => 220,
+  'ۤ' => 230,
+  'ۧ' => 230,
+  'ۨ' => 230,
+  '۪' => 220,
+  '۫' => 230,
+  '۬' => 230,
+  'ۭ' => 220,
+  'ܑ' => 36,
+  'ܰ' => 230,
+  'ܱ' => 220,
+  'ܲ' => 230,
+  'ܳ' => 230,
+  'ܴ' => 220,
+  'ܵ' => 230,
+  'ܶ' => 230,
+  'ܷ' => 220,
+  'ܸ' => 220,
+  'ܹ' => 220,
+  'ܺ' => 230,
+  'ܻ' => 220,
+  'ܼ' => 220,
+  'ܽ' => 230,
+  'ܾ' => 220,
+  'ܿ' => 230,
+  '݀' => 230,
+  '݁' => 230,
+  '݂' => 220,
+  '݃' => 230,
+  '݄' => 220,
+  '݅' => 230,
+  '݆' => 220,
+  '݇' => 230,
+  '݈' => 220,
+  '݉' => 230,
+  '݊' => 230,
+  '߫' => 230,
+  '߬' => 230,
+  '߭' => 230,
+  '߮' => 230,
+  '߯' => 230,
+  '߰' => 230,
+  '߱' => 230,
+  '߲' => 220,
+  '߳' => 230,
+  '߽' => 220,
+  'ࠖ' => 230,
+  'ࠗ' => 230,
+  '࠘' => 230,
+  '࠙' => 230,
+  'ࠛ' => 230,
+  'ࠜ' => 230,
+  'ࠝ' => 230,
+  'ࠞ' => 230,
+  'ࠟ' => 230,
+  'ࠠ' => 230,
+  'ࠡ' => 230,
+  'ࠢ' => 230,
+  'ࠣ' => 230,
+  'ࠥ' => 230,
+  'ࠦ' => 230,
+  'ࠧ' => 230,
+  'ࠩ' => 230,
+  'ࠪ' => 230,
+  'ࠫ' => 230,
+  'ࠬ' => 230,
+  '࠭' => 230,
+  '࡙' => 220,
+  '࡚' => 220,
+  '࡛' => 220,
+  '࣓' => 220,
+  'ࣔ' => 230,
+  'ࣕ' => 230,
+  'ࣖ' => 230,
+  'ࣗ' => 230,
+  'ࣘ' => 230,
+  'ࣙ' => 230,
+  'ࣚ' => 230,
+  'ࣛ' => 230,
+  'ࣜ' => 230,
+  'ࣝ' => 230,
+  'ࣞ' => 230,
+  'ࣟ' => 230,
+  '࣠' => 230,
+  '࣡' => 230,
+  'ࣣ' => 220,
+  'ࣤ' => 230,
+  'ࣥ' => 230,
+  'ࣦ' => 220,
+  'ࣧ' => 230,
+  'ࣨ' => 230,
+  'ࣩ' => 220,
+  '࣪' => 230,
+  '࣫' => 230,
+  '࣬' => 230,
+  '࣭' => 220,
+  '࣮' => 220,
+  '࣯' => 220,
+  'ࣰ' => 27,
+  'ࣱ' => 28,
+  'ࣲ' => 29,
+  'ࣳ' => 230,
+  'ࣴ' => 230,
+  'ࣵ' => 230,
+  'ࣶ' => 220,
+  'ࣷ' => 230,
+  'ࣸ' => 230,
+  'ࣹ' => 220,
+  'ࣺ' => 220,
+  'ࣻ' => 230,
+  'ࣼ' => 230,
+  'ࣽ' => 230,
+  'ࣾ' => 230,
+  'ࣿ' => 230,
+  '़' => 7,
+  '्' => 9,
+  '॑' => 230,
+  '॒' => 220,
+  '॓' => 230,
+  '॔' => 230,
+  '়' => 7,
+  '্' => 9,
+  '৾' => 230,
+  '਼' => 7,
+  '੍' => 9,
+  '઼' => 7,
+  '્' => 9,
+  '଼' => 7,
+  '୍' => 9,
+  '்' => 9,
+  '్' => 9,
+  'ౕ' => 84,
+  'ౖ' => 91,
+  '಼' => 7,
+  '್' => 9,
+  '഻' => 9,
+  '഼' => 9,
+  '്' => 9,
+  '්' => 9,
+  'ุ' => 103,
+  'ู' => 103,
+  'ฺ' => 9,
+  '่' => 107,
+  '้' => 107,
+  '๊' => 107,
+  '๋' => 107,
+  'ຸ' => 118,
+  'ູ' => 118,
+  '຺' => 9,
+  '່' => 122,
+  '້' => 122,
+  '໊' => 122,
+  '໋' => 122,
+  '༘' => 220,
+  '༙' => 220,
+  '༵' => 220,
+  '༷' => 220,
+  '༹' => 216,
+  'ཱ' => 129,
+  'ི' => 130,
+  'ུ' => 132,
+  'ེ' => 130,
+  'ཻ' => 130,
+  'ོ' => 130,
+  'ཽ' => 130,
+  'ྀ' => 130,
+  'ྂ' => 230,
+  'ྃ' => 230,
+  '྄' => 9,
+  '྆' => 230,
+  '྇' => 230,
+  '࿆' => 220,
+  '့' => 7,
+  '္' => 9,
+  '်' => 9,
+  'ႍ' => 220,
+  '፝' => 230,
+  '፞' => 230,
+  '፟' => 230,
+  '᜔' => 9,
+  '᜴' => 9,
+  '្' => 9,
+  '៝' => 230,
+  'ᢩ' => 228,
+  '᤹' => 222,
+  '᤺' => 230,
+  '᤻' => 220,
+  'ᨗ' => 230,
+  'ᨘ' => 220,
+  '᩠' => 9,
+  '᩵' => 230,
+  '᩶' => 230,
+  '᩷' => 230,
+  '᩸' => 230,
+  '᩹' => 230,
+  '᩺' => 230,
+  '᩻' => 230,
+  '᩼' => 230,
+  '᩿' => 220,
+  '᪰' => 230,
+  '᪱' => 230,
+  '᪲' => 230,
+  '᪳' => 230,
+  '᪴' => 230,
+  '᪵' => 220,
+  '᪶' => 220,
+  '᪷' => 220,
+  '᪸' => 220,
+  '᪹' => 220,
+  '᪺' => 220,
+  '᪻' => 230,
+  '᪼' => 230,
+  '᪽' => 220,
+  'ᪿ' => 220,
+  'ᫀ' => 220,
+  '᬴' => 7,
+  '᭄' => 9,
+  '᭫' => 230,
+  '᭬' => 220,
+  '᭭' => 230,
+  '᭮' => 230,
+  '᭯' => 230,
+  '᭰' => 230,
+  '᭱' => 230,
+  '᭲' => 230,
+  '᭳' => 230,
+  '᮪' => 9,
+  '᮫' => 9,
+  '᯦' => 7,
+  '᯲' => 9,
+  '᯳' => 9,
+  '᰷' => 7,
+  '᳐' => 230,
+  '᳑' => 230,
+  '᳒' => 230,
+  '᳔' => 1,
+  '᳕' => 220,
+  '᳖' => 220,
+  '᳗' => 220,
+  '᳘' => 220,
+  '᳙' => 220,
+  '᳚' => 230,
+  '᳛' => 230,
+  '᳜' => 220,
+  '᳝' => 220,
+  '᳞' => 220,
+  '᳟' => 220,
+  '᳠' => 230,
+  '᳢' => 1,
+  '᳣' => 1,
+  '᳤' => 1,
+  '᳥' => 1,
+  '᳦' => 1,
+  '᳧' => 1,
+  '᳨' => 1,
+  '᳭' => 220,
+  '᳴' => 230,
+  '᳸' => 230,
+  '᳹' => 230,
+  '᷀' => 230,
+  '᷁' => 230,
+  '᷂' => 220,
+  '᷃' => 230,
+  '᷄' => 230,
+  '᷅' => 230,
+  '᷆' => 230,
+  '᷇' => 230,
+  '᷈' => 230,
+  '᷉' => 230,
+  '᷊' => 220,
+  '᷋' => 230,
+  '᷌' => 230,
+  '᷍' => 234,
+  '᷎' => 214,
+  '᷏' => 220,
+  '᷐' => 202,
+  '᷑' => 230,
+  '᷒' => 230,
+  'ᷓ' => 230,
+  'ᷔ' => 230,
+  'ᷕ' => 230,
+  'ᷖ' => 230,
+  'ᷗ' => 230,
+  'ᷘ' => 230,
+  'ᷙ' => 230,
+  'ᷚ' => 230,
+  'ᷛ' => 230,
+  'ᷜ' => 230,
+  'ᷝ' => 230,
+  'ᷞ' => 230,
+  'ᷟ' => 230,
+  'ᷠ' => 230,
+  'ᷡ' => 230,
+  'ᷢ' => 230,
+  'ᷣ' => 230,
+  'ᷤ' => 230,
+  'ᷥ' => 230,
+  'ᷦ' => 230,
+  'ᷧ' => 230,
+  'ᷨ' => 230,
+  'ᷩ' => 230,
+  'ᷪ' => 230,
+  'ᷫ' => 230,
+  'ᷬ' => 230,
+  'ᷭ' => 230,
+  'ᷮ' => 230,
+  'ᷯ' => 230,
+  'ᷰ' => 230,
+  'ᷱ' => 230,
+  'ᷲ' => 230,
+  'ᷳ' => 230,
+  'ᷴ' => 230,
+  '᷵' => 230,
+  '᷶' => 232,
+  '᷷' => 228,
+  '᷸' => 228,
+  '᷹' => 220,
+  '᷻' => 230,
+  '᷼' => 233,
+  '᷽' => 220,
+  '᷾' => 230,
+  '᷿' => 220,
+  '⃐' => 230,
+  '⃑' => 230,
+  '⃒' => 1,
+  '⃓' => 1,
+  '⃔' => 230,
+  '⃕' => 230,
+  '⃖' => 230,
+  '⃗' => 230,
+  '⃘' => 1,
+  '⃙' => 1,
+  '⃚' => 1,
+  '⃛' => 230,
+  '⃜' => 230,
+  '⃡' => 230,
+  '⃥' => 1,
+  '⃦' => 1,
+  '⃧' => 230,
+  '⃨' => 220,
+  '⃩' => 230,
+  '⃪' => 1,
+  '⃫' => 1,
+  '⃬' => 220,
+  '⃭' => 220,
+  '⃮' => 220,
+  '⃯' => 220,
+  '⃰' => 230,
+  '⳯' => 230,
+  '⳰' => 230,
+  '⳱' => 230,
+  '⵿' => 9,
+  'ⷠ' => 230,
+  'ⷡ' => 230,
+  'ⷢ' => 230,
+  'ⷣ' => 230,
+  'ⷤ' => 230,
+  'ⷥ' => 230,
+  'ⷦ' => 230,
+  'ⷧ' => 230,
+  'ⷨ' => 230,
+  'ⷩ' => 230,
+  'ⷪ' => 230,
+  'ⷫ' => 230,
+  'ⷬ' => 230,
+  'ⷭ' => 230,
+  'ⷮ' => 230,
+  'ⷯ' => 230,
+  'ⷰ' => 230,
+  'ⷱ' => 230,
+  'ⷲ' => 230,
+  'ⷳ' => 230,
+  'ⷴ' => 230,
+  'ⷵ' => 230,
+  'ⷶ' => 230,
+  'ⷷ' => 230,
+  'ⷸ' => 230,
+  'ⷹ' => 230,
+  'ⷺ' => 230,
+  'ⷻ' => 230,
+  'ⷼ' => 230,
+  'ⷽ' => 230,
+  'ⷾ' => 230,
+  'ⷿ' => 230,
+  '〪' => 218,
+  '〫' => 228,
+  '〬' => 232,
+  '〭' => 222,
+  '〮' => 224,
+  '〯' => 224,
+  '゙' => 8,
+  '゚' => 8,
+  '꙯' => 230,
+  'ꙴ' => 230,
+  'ꙵ' => 230,
+  'ꙶ' => 230,
+  'ꙷ' => 230,
+  'ꙸ' => 230,
+  'ꙹ' => 230,
+  'ꙺ' => 230,
+  'ꙻ' => 230,
+  '꙼' => 230,
+  '꙽' => 230,
+  'ꚞ' => 230,
+  'ꚟ' => 230,
+  '꛰' => 230,
+  '꛱' => 230,
+  '꠆' => 9,
+  '꠬' => 9,
+  '꣄' => 9,
+  '꣠' => 230,
+  '꣡' => 230,
+  '꣢' => 230,
+  '꣣' => 230,
+  '꣤' => 230,
+  '꣥' => 230,
+  '꣦' => 230,
+  '꣧' => 230,
+  '꣨' => 230,
+  '꣩' => 230,
+  '꣪' => 230,
+  '꣫' => 230,
+  '꣬' => 230,
+  '꣭' => 230,
+  '꣮' => 230,
+  '꣯' => 230,
+  '꣰' => 230,
+  '꣱' => 230,
+  '꤫' => 220,
+  '꤬' => 220,
+  '꤭' => 220,
+  '꥓' => 9,
+  '꦳' => 7,
+  '꧀' => 9,
+  'ꪰ' => 230,
+  'ꪲ' => 230,
+  'ꪳ' => 230,
+  'ꪴ' => 220,
+  'ꪷ' => 230,
+  'ꪸ' => 230,
+  'ꪾ' => 230,
+  '꪿' => 230,
+  '꫁' => 230,
+  '꫶' => 9,
+  '꯭' => 9,
+  'ﬞ' => 26,
+  '︠' => 230,
+  '︡' => 230,
+  '︢' => 230,
+  '︣' => 230,
+  '︤' => 230,
+  '︥' => 230,
+  '︦' => 230,
+  '︧' => 220,
+  '︨' => 220,
+  '︩' => 220,
+  '︪' => 220,
+  '︫' => 220,
+  '︬' => 220,
+  '︭' => 220,
+  '︮' => 230,
+  '︯' => 230,
+  '𐇽' => 220,
+  '𐋠' => 220,
+  '𐍶' => 230,
+  '𐍷' => 230,
+  '𐍸' => 230,
+  '𐍹' => 230,
+  '𐍺' => 230,
+  '𐨍' => 220,
+  '𐨏' => 230,
+  '𐨸' => 230,
+  '𐨹' => 1,
+  '𐨺' => 220,
+  '𐨿' => 9,
+  '𐫥' => 230,
+  '𐫦' => 220,
+  '𐴤' => 230,
+  '𐴥' => 230,
+  '𐴦' => 230,
+  '𐴧' => 230,
+  '𐺫' => 230,
+  '𐺬' => 230,
+  '𐽆' => 220,
+  '𐽇' => 220,
+  '𐽈' => 230,
+  '𐽉' => 230,
+  '𐽊' => 230,
+  '𐽋' => 220,
+  '𐽌' => 230,
+  '𐽍' => 220,
+  '𐽎' => 220,
+  '𐽏' => 220,
+  '𐽐' => 220,
+  '𑁆' => 9,
+  '𑁿' => 9,
+  '𑂹' => 9,
+  '𑂺' => 7,
+  '𑄀' => 230,
+  '𑄁' => 230,
+  '𑄂' => 230,
+  '𑄳' => 9,
+  '𑄴' => 9,
+  '𑅳' => 7,
+  '𑇀' => 9,
+  '𑇊' => 7,
+  '𑈵' => 9,
+  '𑈶' => 7,
+  '𑋩' => 7,
+  '𑋪' => 9,
+  '𑌻' => 7,
+  '𑌼' => 7,
+  '𑍍' => 9,
+  '𑍦' => 230,
+  '𑍧' => 230,
+  '𑍨' => 230,
+  '𑍩' => 230,
+  '𑍪' => 230,
+  '𑍫' => 230,
+  '𑍬' => 230,
+  '𑍰' => 230,
+  '𑍱' => 230,
+  '𑍲' => 230,
+  '𑍳' => 230,
+  '𑍴' => 230,
+  '𑑂' => 9,
+  '𑑆' => 7,
+  '𑑞' => 230,
+  '𑓂' => 9,
+  '𑓃' => 7,
+  '𑖿' => 9,
+  '𑗀' => 7,
+  '𑘿' => 9,
+  '𑚶' => 9,
+  '𑚷' => 7,
+  '𑜫' => 9,
+  '𑠹' => 9,
+  '𑠺' => 7,
+  '𑤽' => 9,
+  '𑤾' => 9,
+  '𑥃' => 7,
+  '𑧠' => 9,
+  '𑨴' => 9,
+  '𑩇' => 9,
+  '𑪙' => 9,
+  '𑰿' => 9,
+  '𑵂' => 7,
+  '𑵄' => 9,
+  '𑵅' => 9,
+  '𑶗' => 9,
+  '𖫰' => 1,
+  '𖫱' => 1,
+  '𖫲' => 1,
+  '𖫳' => 1,
+  '𖫴' => 1,
+  '𖬰' => 230,
+  '𖬱' => 230,
+  '𖬲' => 230,
+  '𖬳' => 230,
+  '𖬴' => 230,
+  '𖬵' => 230,
+  '𖬶' => 230,
+  '𖿰' => 6,
+  '𖿱' => 6,
+  '𛲞' => 1,
+  '𝅥' => 216,
+  '𝅦' => 216,
+  '𝅧' => 1,
+  '𝅨' => 1,
+  '𝅩' => 1,
+  '𝅭' => 226,
+  '𝅮' => 216,
+  '𝅯' => 216,
+  '𝅰' => 216,
+  '𝅱' => 216,
+  '𝅲' => 216,
+  '𝅻' => 220,
+  '𝅼' => 220,
+  '𝅽' => 220,
+  '𝅾' => 220,
+  '𝅿' => 220,
+  '𝆀' => 220,
+  '𝆁' => 220,
+  '𝆂' => 220,
+  '𝆅' => 230,
+  '𝆆' => 230,
+  '𝆇' => 230,
+  '𝆈' => 230,
+  '𝆉' => 230,
+  '𝆊' => 220,
+  '𝆋' => 220,
+  '𝆪' => 230,
+  '𝆫' => 230,
+  '𝆬' => 230,
+  '𝆭' => 230,
+  '𝉂' => 230,
+  '𝉃' => 230,
+  '𝉄' => 230,
+  '𞀀' => 230,
+  '𞀁' => 230,
+  '𞀂' => 230,
+  '𞀃' => 230,
+  '𞀄' => 230,
+  '𞀅' => 230,
+  '𞀆' => 230,
+  '𞀈' => 230,
+  '𞀉' => 230,
+  '𞀊' => 230,
+  '𞀋' => 230,
+  '𞀌' => 230,
+  '𞀍' => 230,
+  '𞀎' => 230,
+  '𞀏' => 230,
+  '𞀐' => 230,
+  '𞀑' => 230,
+  '𞀒' => 230,
+  '𞀓' => 230,
+  '𞀔' => 230,
+  '𞀕' => 230,
+  '𞀖' => 230,
+  '𞀗' => 230,
+  '𞀘' => 230,
+  '𞀛' => 230,
+  '𞀜' => 230,
+  '𞀝' => 230,
+  '𞀞' => 230,
+  '𞀟' => 230,
+  '𞀠' => 230,
+  '𞀡' => 230,
+  '𞀣' => 230,
+  '𞀤' => 230,
+  '𞀦' => 230,
+  '𞀧' => 230,
+  '𞀨' => 230,
+  '𞀩' => 230,
+  '𞀪' => 230,
+  '𞄰' => 230,
+  '𞄱' => 230,
+  '𞄲' => 230,
+  '𞄳' => 230,
+  '𞄴' => 230,
+  '𞄵' => 230,
+  '𞄶' => 230,
+  '𞋬' => 230,
+  '𞋭' => 230,
+  '𞋮' => 230,
+  '𞋯' => 230,
+  '𞣐' => 220,
+  '𞣑' => 220,
+  '𞣒' => 220,
+  '𞣓' => 220,
+  '𞣔' => 220,
+  '𞣕' => 220,
+  '𞣖' => 220,
+  '𞥄' => 230,
+  '𞥅' => 230,
+  '𞥆' => 230,
+  '𞥇' => 230,
+  '𞥈' => 230,
+  '𞥉' => 230,
+  '𞥊' => 7,
+);
diff --git a/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/compatibilityDecomposition.php b/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/compatibilityDecomposition.php
new file mode 100755
index 0000000..1574902
--- /dev/null
+++ b/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/compatibilityDecomposition.php
@@ -0,0 +1,3695 @@
+<?php
+
+return array (
+  ' ' => ' ',
+  '¨' => ' ̈',
+  'ª' => 'a',
+  '¯' => ' ̄',
+  '²' => '2',
+  '³' => '3',
+  '´' => ' ́',
+  'µ' => 'μ',
+  '¸' => ' ̧',
+  '¹' => '1',
+  'º' => 'o',
+  '¼' => '1⁄4',
+  '½' => '1⁄2',
+  '¾' => '3⁄4',
+  'IJ' => 'IJ',
+  'ij' => 'ij',
+  'Ŀ' => 'L·',
+  'ŀ' => 'l·',
+  'ʼn' => 'ʼn',
+  'ſ' => 's',
+  'DŽ' => 'DŽ',
+  'Dž' => 'Dž',
+  'dž' => 'dž',
+  'LJ' => 'LJ',
+  'Lj' => 'Lj',
+  'lj' => 'lj',
+  'NJ' => 'NJ',
+  'Nj' => 'Nj',
+  'nj' => 'nj',
+  'DZ' => 'DZ',
+  'Dz' => 'Dz',
+  'dz' => 'dz',
+  'ʰ' => 'h',
+  'ʱ' => 'ɦ',
+  'ʲ' => 'j',
+  'ʳ' => 'r',
+  'ʴ' => 'ɹ',
+  'ʵ' => 'ɻ',
+  'ʶ' => 'ʁ',
+  'ʷ' => 'w',
+  'ʸ' => 'y',
+  '˘' => ' ̆',
+  '˙' => ' ̇',
+  '˚' => ' ̊',
+  '˛' => ' ̨',
+  '˜' => ' ̃',
+  '˝' => ' ̋',
+  'ˠ' => 'ɣ',
+  'ˡ' => 'l',
+  'ˢ' => 's',
+  'ˣ' => 'x',
+  'ˤ' => 'ʕ',
+  'ͺ' => ' ͅ',
+  '΄' => ' ́',
+  '΅' => ' ̈́',
+  'ϐ' => 'β',
+  'ϑ' => 'θ',
+  'ϒ' => 'Υ',
+  'ϓ' => 'Ύ',
+  'ϔ' => 'Ϋ',
+  'ϕ' => 'φ',
+  'ϖ' => 'π',
+  'ϰ' => 'κ',
+  'ϱ' => 'ρ',
+  'ϲ' => 'ς',
+  'ϴ' => 'Θ',
+  'ϵ' => 'ε',
+  'Ϲ' => 'Σ',
+  'և' => 'եւ',
+  'ٵ' => 'اٴ',
+  'ٶ' => 'وٴ',
+  'ٷ' => 'ۇٴ',
+  'ٸ' => 'يٴ',
+  'ำ' => 'ํา',
+  'ຳ' => 'ໍາ',
+  'ໜ' => 'ຫນ',
+  'ໝ' => 'ຫມ',
+  '༌' => '་',
+  'ཷ' => 'ྲཱྀ',
+  'ཹ' => 'ླཱྀ',
+  'ჼ' => 'ნ',
+  'ᴬ' => 'A',
+  'ᴭ' => 'Æ',
+  'ᴮ' => 'B',
+  'ᴰ' => 'D',
+  'ᴱ' => 'E',
+  'ᴲ' => 'Ǝ',
+  'ᴳ' => 'G',
+  'ᴴ' => 'H',
+  'ᴵ' => 'I',
+  'ᴶ' => 'J',
+  'ᴷ' => 'K',
+  'ᴸ' => 'L',
+  'ᴹ' => 'M',
+  'ᴺ' => 'N',
+  'ᴼ' => 'O',
+  'ᴽ' => 'Ȣ',
+  'ᴾ' => 'P',
+  'ᴿ' => 'R',
+  'ᵀ' => 'T',
+  'ᵁ' => 'U',
+  'ᵂ' => 'W',
+  'ᵃ' => 'a',
+  'ᵄ' => 'ɐ',
+  'ᵅ' => 'ɑ',
+  'ᵆ' => 'ᴂ',
+  'ᵇ' => 'b',
+  'ᵈ' => 'd',
+  'ᵉ' => 'e',
+  'ᵊ' => 'ə',
+  'ᵋ' => 'ɛ',
+  'ᵌ' => 'ɜ',
+  'ᵍ' => 'g',
+  'ᵏ' => 'k',
+  'ᵐ' => 'm',
+  'ᵑ' => 'ŋ',
+  'ᵒ' => 'o',
+  'ᵓ' => 'ɔ',
+  'ᵔ' => 'ᴖ',
+  'ᵕ' => 'ᴗ',
+  'ᵖ' => 'p',
+  'ᵗ' => 't',
+  'ᵘ' => 'u',
+  'ᵙ' => 'ᴝ',
+  'ᵚ' => 'ɯ',
+  'ᵛ' => 'v',
+  'ᵜ' => 'ᴥ',
+  'ᵝ' => 'β',
+  'ᵞ' => 'γ',
+  'ᵟ' => 'δ',
+  'ᵠ' => 'φ',
+  'ᵡ' => 'χ',
+  'ᵢ' => 'i',
+  'ᵣ' => 'r',
+  'ᵤ' => 'u',
+  'ᵥ' => 'v',
+  'ᵦ' => 'β',
+  'ᵧ' => 'γ',
+  'ᵨ' => 'ρ',
+  'ᵩ' => 'φ',
+  'ᵪ' => 'χ',
+  'ᵸ' => 'н',
+  'ᶛ' => 'ɒ',
+  'ᶜ' => 'c',
+  'ᶝ' => 'ɕ',
+  'ᶞ' => 'ð',
+  'ᶟ' => 'ɜ',
+  'ᶠ' => 'f',
+  'ᶡ' => 'ɟ',
+  'ᶢ' => 'ɡ',
+  'ᶣ' => 'ɥ',
+  'ᶤ' => 'ɨ',
+  'ᶥ' => 'ɩ',
+  'ᶦ' => 'ɪ',
+  'ᶧ' => 'ᵻ',
+  'ᶨ' => 'ʝ',
+  'ᶩ' => 'ɭ',
+  'ᶪ' => 'ᶅ',
+  'ᶫ' => 'ʟ',
+  'ᶬ' => 'ɱ',
+  'ᶭ' => 'ɰ',
+  'ᶮ' => 'ɲ',
+  'ᶯ' => 'ɳ',
+  'ᶰ' => 'ɴ',
+  'ᶱ' => 'ɵ',
+  'ᶲ' => 'ɸ',
+  'ᶳ' => 'ʂ',
+  'ᶴ' => 'ʃ',
+  'ᶵ' => 'ƫ',
+  'ᶶ' => 'ʉ',
+  'ᶷ' => 'ʊ',
+  'ᶸ' => 'ᴜ',
+  'ᶹ' => 'ʋ',
+  'ᶺ' => 'ʌ',
+  'ᶻ' => 'z',
+  'ᶼ' => 'ʐ',
+  'ᶽ' => 'ʑ',
+  'ᶾ' => 'ʒ',
+  'ᶿ' => 'θ',
+  'ẚ' => 'aʾ',
+  'ẛ' => 'ṡ',
+  '᾽' => ' ̓',
+  '᾿' => ' ̓',
+  '῀' => ' ͂',
+  '῁' => ' ̈͂',
+  '῍' => ' ̓̀',
+  '῎' => ' ̓́',
+  '῏' => ' ̓͂',
+  '῝' => ' ̔̀',
+  '῞' => ' ̔́',
+  '῟' => ' ̔͂',
+  '῭' => ' ̈̀',
+  '΅' => ' ̈́',
+  '´' => ' ́',
+  '῾' => ' ̔',
+  ' ' => ' ',
+  ' ' => ' ',
+  ' ' => ' ',
+  ' ' => ' ',
+  ' ' => ' ',
+  ' ' => ' ',
+  ' ' => ' ',
+  ' ' => ' ',
+  ' ' => ' ',
+  ' ' => ' ',
+  ' ' => ' ',
+  '‑' => '‐',
+  '‗' => ' ̳',
+  '․' => '.',
+  '‥' => '..',
+  '…' => '...',
+  ' ' => ' ',
+  '″' => '′′',
+  '‴' => '′′′',
+  '‶' => '‵‵',
+  '‷' => '‵‵‵',
+  '‼' => '!!',
+  '‾' => ' ̅',
+  '⁇' => '??',
+  '⁈' => '?!',
+  '⁉' => '!?',
+  '⁗' => '′′′′',
+  ' ' => ' ',
+  '⁰' => '0',
+  'ⁱ' => 'i',
+  '⁴' => '4',
+  '⁵' => '5',
+  '⁶' => '6',
+  '⁷' => '7',
+  '⁸' => '8',
+  '⁹' => '9',
+  '⁺' => '+',
+  '⁻' => '−',
+  '⁼' => '=',
+  '⁽' => '(',
+  '⁾' => ')',
+  'ⁿ' => 'n',
+  '₀' => '0',
+  '₁' => '1',
+  '₂' => '2',
+  '₃' => '3',
+  '₄' => '4',
+  '₅' => '5',
+  '₆' => '6',
+  '₇' => '7',
+  '₈' => '8',
+  '₉' => '9',
+  '₊' => '+',
+  '₋' => '−',
+  '₌' => '=',
+  '₍' => '(',
+  '₎' => ')',
+  'ₐ' => 'a',
+  'ₑ' => 'e',
+  'ₒ' => 'o',
+  'ₓ' => 'x',
+  'ₔ' => 'ə',
+  'ₕ' => 'h',
+  'ₖ' => 'k',
+  'ₗ' => 'l',
+  'ₘ' => 'm',
+  'ₙ' => 'n',
+  'ₚ' => 'p',
+  'ₛ' => 's',
+  'ₜ' => 't',
+  '₨' => 'Rs',
+  '℀' => 'a/c',
+  '℁' => 'a/s',
+  'ℂ' => 'C',
+  '℃' => '°C',
+  '℅' => 'c/o',
+  '℆' => 'c/u',
+  'ℇ' => 'Ɛ',
+  '℉' => '°F',
+  'ℊ' => 'g',
+  'ℋ' => 'H',
+  'ℌ' => 'H',
+  'ℍ' => 'H',
+  'ℎ' => 'h',
+  'ℏ' => 'ħ',
+  'ℐ' => 'I',
+  'ℑ' => 'I',
+  'ℒ' => 'L',
+  'ℓ' => 'l',
+  'ℕ' => 'N',
+  '№' => 'No',
+  'ℙ' => 'P',
+  'ℚ' => 'Q',
+  'ℛ' => 'R',
+  'ℜ' => 'R',
+  'ℝ' => 'R',
+  '℠' => 'SM',
+  '℡' => 'TEL',
+  '™' => 'TM',
+  'ℤ' => 'Z',
+  'ℨ' => 'Z',
+  'ℬ' => 'B',
+  'ℭ' => 'C',
+  'ℯ' => 'e',
+  'ℰ' => 'E',
+  'ℱ' => 'F',
+  'ℳ' => 'M',
+  'ℴ' => 'o',
+  'ℵ' => 'א',
+  'ℶ' => 'ב',
+  'ℷ' => 'ג',
+  'ℸ' => 'ד',
+  'ℹ' => 'i',
+  '℻' => 'FAX',
+  'ℼ' => 'π',
+  'ℽ' => 'γ',
+  'ℾ' => 'Γ',
+  'ℿ' => 'Π',
+  '⅀' => '∑',
+  'ⅅ' => 'D',
+  'ⅆ' => 'd',
+  'ⅇ' => 'e',
+  'ⅈ' => 'i',
+  'ⅉ' => 'j',
+  '⅐' => '1⁄7',
+  '⅑' => '1⁄9',
+  '⅒' => '1⁄10',
+  '⅓' => '1⁄3',
+  '⅔' => '2⁄3',
+  '⅕' => '1⁄5',
+  '⅖' => '2⁄5',
+  '⅗' => '3⁄5',
+  '⅘' => '4⁄5',
+  '⅙' => '1⁄6',
+  '⅚' => '5⁄6',
+  '⅛' => '1⁄8',
+  '⅜' => '3⁄8',
+  '⅝' => '5⁄8',
+  '⅞' => '7⁄8',
+  '⅟' => '1⁄',
+  'Ⅰ' => 'I',
+  'Ⅱ' => 'II',
+  'Ⅲ' => 'III',
+  'Ⅳ' => 'IV',
+  'Ⅴ' => 'V',
+  'Ⅵ' => 'VI',
+  'Ⅶ' => 'VII',
+  'Ⅷ' => 'VIII',
+  'Ⅸ' => 'IX',
+  'Ⅹ' => 'X',
+  'Ⅺ' => 'XI',
+  'Ⅻ' => 'XII',
+  'Ⅼ' => 'L',
+  'Ⅽ' => 'C',
+  'Ⅾ' => 'D',
+  'Ⅿ' => 'M',
+  'ⅰ' => 'i',
+  'ⅱ' => 'ii',
+  'ⅲ' => 'iii',
+  'ⅳ' => 'iv',
+  'ⅴ' => 'v',
+  'ⅵ' => 'vi',
+  'ⅶ' => 'vii',
+  'ⅷ' => 'viii',
+  'ⅸ' => 'ix',
+  'ⅹ' => 'x',
+  'ⅺ' => 'xi',
+  'ⅻ' => 'xii',
+  'ⅼ' => 'l',
+  'ⅽ' => 'c',
+  'ⅾ' => 'd',
+  'ⅿ' => 'm',
+  '↉' => '0⁄3',
+  '∬' => '∫∫',
+  '∭' => '∫∫∫',
+  '∯' => '∮∮',
+  '∰' => '∮∮∮',
+  '①' => '1',
+  '②' => '2',
+  '③' => '3',
+  '④' => '4',
+  '⑤' => '5',
+  '⑥' => '6',
+  '⑦' => '7',
+  '⑧' => '8',
+  '⑨' => '9',
+  '⑩' => '10',
+  '⑪' => '11',
+  '⑫' => '12',
+  '⑬' => '13',
+  '⑭' => '14',
+  '⑮' => '15',
+  '⑯' => '16',
+  '⑰' => '17',
+  '⑱' => '18',
+  '⑲' => '19',
+  '⑳' => '20',
+  '⑴' => '(1)',
+  '⑵' => '(2)',
+  '⑶' => '(3)',
+  '⑷' => '(4)',
+  '⑸' => '(5)',
+  '⑹' => '(6)',
+  '⑺' => '(7)',
+  '⑻' => '(8)',
+  '⑼' => '(9)',
+  '⑽' => '(10)',
+  '⑾' => '(11)',
+  '⑿' => '(12)',
+  '⒀' => '(13)',
+  '⒁' => '(14)',
+  '⒂' => '(15)',
+  '⒃' => '(16)',
+  '⒄' => '(17)',
+  '⒅' => '(18)',
+  '⒆' => '(19)',
+  '⒇' => '(20)',
+  '⒈' => '1.',
+  '⒉' => '2.',
+  '⒊' => '3.',
+  '⒋' => '4.',
+  '⒌' => '5.',
+  '⒍' => '6.',
+  '⒎' => '7.',
+  '⒏' => '8.',
+  '⒐' => '9.',
+  '⒑' => '10.',
+  '⒒' => '11.',
+  '⒓' => '12.',
+  '⒔' => '13.',
+  '⒕' => '14.',
+  '⒖' => '15.',
+  '⒗' => '16.',
+  '⒘' => '17.',
+  '⒙' => '18.',
+  '⒚' => '19.',
+  '⒛' => '20.',
+  '⒜' => '(a)',
+  '⒝' => '(b)',
+  '⒞' => '(c)',
+  '⒟' => '(d)',
+  '⒠' => '(e)',
+  '⒡' => '(f)',
+  '⒢' => '(g)',
+  '⒣' => '(h)',
+  '⒤' => '(i)',
+  '⒥' => '(j)',
+  '⒦' => '(k)',
+  '⒧' => '(l)',
+  '⒨' => '(m)',
+  '⒩' => '(n)',
+  '⒪' => '(o)',
+  '⒫' => '(p)',
+  '⒬' => '(q)',
+  '⒭' => '(r)',
+  '⒮' => '(s)',
+  '⒯' => '(t)',
+  '⒰' => '(u)',
+  '⒱' => '(v)',
+  '⒲' => '(w)',
+  '⒳' => '(x)',
+  '⒴' => '(y)',
+  '⒵' => '(z)',
+  'Ⓐ' => 'A',
+  'Ⓑ' => 'B',
+  'Ⓒ' => 'C',
+  'Ⓓ' => 'D',
+  'Ⓔ' => 'E',
+  'Ⓕ' => 'F',
+  'Ⓖ' => 'G',
+  'Ⓗ' => 'H',
+  'Ⓘ' => 'I',
+  'Ⓙ' => 'J',
+  'Ⓚ' => 'K',
+  'Ⓛ' => 'L',
+  'Ⓜ' => 'M',
+  'Ⓝ' => 'N',
+  'Ⓞ' => 'O',
+  'Ⓟ' => 'P',
+  'Ⓠ' => 'Q',
+  'Ⓡ' => 'R',
+  'Ⓢ' => 'S',
+  'Ⓣ' => 'T',
+  'Ⓤ' => 'U',
+  'Ⓥ' => 'V',
+  'Ⓦ' => 'W',
+  'Ⓧ' => 'X',
+  'Ⓨ' => 'Y',
+  'Ⓩ' => 'Z',
+  'ⓐ' => 'a',
+  'ⓑ' => 'b',
+  'ⓒ' => 'c',
+  'ⓓ' => 'd',
+  'ⓔ' => 'e',
+  'ⓕ' => 'f',
+  'ⓖ' => 'g',
+  'ⓗ' => 'h',
+  'ⓘ' => 'i',
+  'ⓙ' => 'j',
+  'ⓚ' => 'k',
+  'ⓛ' => 'l',
+  'ⓜ' => 'm',
+  'ⓝ' => 'n',
+  'ⓞ' => 'o',
+  'ⓟ' => 'p',
+  'ⓠ' => 'q',
+  'ⓡ' => 'r',
+  'ⓢ' => 's',
+  'ⓣ' => 't',
+  'ⓤ' => 'u',
+  'ⓥ' => 'v',
+  'ⓦ' => 'w',
+  'ⓧ' => 'x',
+  'ⓨ' => 'y',
+  'ⓩ' => 'z',
+  '⓪' => '0',
+  '⨌' => '∫∫∫∫',
+  '⩴' => '::=',
+  '⩵' => '==',
+  '⩶' => '===',
+  'ⱼ' => 'j',
+  'ⱽ' => 'V',
+  'ⵯ' => 'ⵡ',
+  '⺟' => '母',
+  '⻳' => '龟',
+  '⼀' => '一',
+  '⼁' => '丨',
+  '⼂' => '丶',
+  '⼃' => '丿',
+  '⼄' => '乙',
+  '⼅' => '亅',
+  '⼆' => '二',
+  '⼇' => '亠',
+  '⼈' => '人',
+  '⼉' => '儿',
+  '⼊' => '入',
+  '⼋' => '八',
+  '⼌' => '冂',
+  '⼍' => '冖',
+  '⼎' => '冫',
+  '⼏' => '几',
+  '⼐' => '凵',
+  '⼑' => '刀',
+  '⼒' => '力',
+  '⼓' => '勹',
+  '⼔' => '匕',
+  '⼕' => '匚',
+  '⼖' => '匸',
+  '⼗' => '十',
+  '⼘' => '卜',
+  '⼙' => '卩',
+  '⼚' => '厂',
+  '⼛' => '厶',
+  '⼜' => '又',
+  '⼝' => '口',
+  '⼞' => '囗',
+  '⼟' => '土',
+  '⼠' => '士',
+  '⼡' => '夂',
+  '⼢' => '夊',
+  '⼣' => '夕',
+  '⼤' => '大',
+  '⼥' => '女',
+  '⼦' => '子',
+  '⼧' => '宀',
+  '⼨' => '寸',
+  '⼩' => '小',
+  '⼪' => '尢',
+  '⼫' => '尸',
+  '⼬' => '屮',
+  '⼭' => '山',
+  '⼮' => '巛',
+  '⼯' => '工',
+  '⼰' => '己',
+  '⼱' => '巾',
+  '⼲' => '干',
+  '⼳' => '幺',
+  '⼴' => '广',
+  '⼵' => '廴',
+  '⼶' => '廾',
+  '⼷' => '弋',
+  '⼸' => '弓',
+  '⼹' => '彐',
+  '⼺' => '彡',
+  '⼻' => '彳',
+  '⼼' => '心',
+  '⼽' => '戈',
+  '⼾' => '戶',
+  '⼿' => '手',
+  '⽀' => '支',
+  '⽁' => '攴',
+  '⽂' => '文',
+  '⽃' => '斗',
+  '⽄' => '斤',
+  '⽅' => '方',
+  '⽆' => '无',
+  '⽇' => '日',
+  '⽈' => '曰',
+  '⽉' => '月',
+  '⽊' => '木',
+  '⽋' => '欠',
+  '⽌' => '止',
+  '⽍' => '歹',
+  '⽎' => '殳',
+  '⽏' => '毋',
+  '⽐' => '比',
+  '⽑' => '毛',
+  '⽒' => '氏',
+  '⽓' => '气',
+  '⽔' => '水',
+  '⽕' => '火',
+  '⽖' => '爪',
+  '⽗' => '父',
+  '⽘' => '爻',
+  '⽙' => '爿',
+  '⽚' => '片',
+  '⽛' => '牙',
+  '⽜' => '牛',
+  '⽝' => '犬',
+  '⽞' => '玄',
+  '⽟' => '玉',
+  '⽠' => '瓜',
+  '⽡' => '瓦',
+  '⽢' => '甘',
+  '⽣' => '生',
+  '⽤' => '用',
+  '⽥' => '田',
+  '⽦' => '疋',
+  '⽧' => '疒',
+  '⽨' => '癶',
+  '⽩' => '白',
+  '⽪' => '皮',
+  '⽫' => '皿',
+  '⽬' => '目',
+  '⽭' => '矛',
+  '⽮' => '矢',
+  '⽯' => '石',
+  '⽰' => '示',
+  '⽱' => '禸',
+  '⽲' => '禾',
+  '⽳' => '穴',
+  '⽴' => '立',
+  '⽵' => '竹',
+  '⽶' => '米',
+  '⽷' => '糸',
+  '⽸' => '缶',
+  '⽹' => '网',
+  '⽺' => '羊',
+  '⽻' => '羽',
+  '⽼' => '老',
+  '⽽' => '而',
+  '⽾' => '耒',
+  '⽿' => '耳',
+  '⾀' => '聿',
+  '⾁' => '肉',
+  '⾂' => '臣',
+  '⾃' => '自',
+  '⾄' => '至',
+  '⾅' => '臼',
+  '⾆' => '舌',
+  '⾇' => '舛',
+  '⾈' => '舟',
+  '⾉' => '艮',
+  '⾊' => '色',
+  '⾋' => '艸',
+  '⾌' => '虍',
+  '⾍' => '虫',
+  '⾎' => '血',
+  '⾏' => '行',
+  '⾐' => '衣',
+  '⾑' => '襾',
+  '⾒' => '見',
+  '⾓' => '角',
+  '⾔' => '言',
+  '⾕' => '谷',
+  '⾖' => '豆',
+  '⾗' => '豕',
+  '⾘' => '豸',
+  '⾙' => '貝',
+  '⾚' => '赤',
+  '⾛' => '走',
+  '⾜' => '足',
+  '⾝' => '身',
+  '⾞' => '車',
+  '⾟' => '辛',
+  '⾠' => '辰',
+  '⾡' => '辵',
+  '⾢' => '邑',
+  '⾣' => '酉',
+  '⾤' => '釆',
+  '⾥' => '里',
+  '⾦' => '金',
+  '⾧' => '長',
+  '⾨' => '門',
+  '⾩' => '阜',
+  '⾪' => '隶',
+  '⾫' => '隹',
+  '⾬' => '雨',
+  '⾭' => '靑',
+  '⾮' => '非',
+  '⾯' => '面',
+  '⾰' => '革',
+  '⾱' => '韋',
+  '⾲' => '韭',
+  '⾳' => '音',
+  '⾴' => '頁',
+  '⾵' => '風',
+  '⾶' => '飛',
+  '⾷' => '食',
+  '⾸' => '首',
+  '⾹' => '香',
+  '⾺' => '馬',
+  '⾻' => '骨',
+  '⾼' => '高',
+  '⾽' => '髟',
+  '⾾' => '鬥',
+  '⾿' => '鬯',
+  '⿀' => '鬲',
+  '⿁' => '鬼',
+  '⿂' => '魚',
+  '⿃' => '鳥',
+  '⿄' => '鹵',
+  '⿅' => '鹿',
+  '⿆' => '麥',
+  '⿇' => '麻',
+  '⿈' => '黃',
+  '⿉' => '黍',
+  '⿊' => '黑',
+  '⿋' => '黹',
+  '⿌' => '黽',
+  '⿍' => '鼎',
+  '⿎' => '鼓',
+  '⿏' => '鼠',
+  '⿐' => '鼻',
+  '⿑' => '齊',
+  '⿒' => '齒',
+  '⿓' => '龍',
+  '⿔' => '龜',
+  '⿕' => '龠',
+  ' ' => ' ',
+  '〶' => '〒',
+  '〸' => '十',
+  '〹' => '卄',
+  '〺' => '卅',
+  '゛' => ' ゙',
+  '゜' => ' ゚',
+  'ゟ' => 'より',
+  'ヿ' => 'コト',
+  'ㄱ' => 'ᄀ',
+  'ㄲ' => 'ᄁ',
+  'ㄳ' => 'ᆪ',
+  'ㄴ' => 'ᄂ',
+  'ㄵ' => 'ᆬ',
+  'ㄶ' => 'ᆭ',
+  'ㄷ' => 'ᄃ',
+  'ㄸ' => 'ᄄ',
+  'ㄹ' => 'ᄅ',
+  'ㄺ' => 'ᆰ',
+  'ㄻ' => 'ᆱ',
+  'ㄼ' => 'ᆲ',
+  'ㄽ' => 'ᆳ',
+  'ㄾ' => 'ᆴ',
+  'ㄿ' => 'ᆵ',
+  'ㅀ' => 'ᄚ',
+  'ㅁ' => 'ᄆ',
+  'ㅂ' => 'ᄇ',
+  'ㅃ' => 'ᄈ',
+  'ㅄ' => 'ᄡ',
+  'ㅅ' => 'ᄉ',
+  'ㅆ' => 'ᄊ',
+  'ㅇ' => 'ᄋ',
+  'ㅈ' => 'ᄌ',
+  'ㅉ' => 'ᄍ',
+  'ㅊ' => 'ᄎ',
+  'ㅋ' => 'ᄏ',
+  'ㅌ' => 'ᄐ',
+  'ㅍ' => 'ᄑ',
+  'ㅎ' => 'ᄒ',
+  'ㅏ' => 'ᅡ',
+  'ㅐ' => 'ᅢ',
+  'ㅑ' => 'ᅣ',
+  'ㅒ' => 'ᅤ',
+  'ㅓ' => 'ᅥ',
+  'ㅔ' => 'ᅦ',
+  'ㅕ' => 'ᅧ',
+  'ㅖ' => 'ᅨ',
+  'ㅗ' => 'ᅩ',
+  'ㅘ' => 'ᅪ',
+  'ㅙ' => 'ᅫ',
+  'ㅚ' => 'ᅬ',
+  'ㅛ' => 'ᅭ',
+  'ㅜ' => 'ᅮ',
+  'ㅝ' => 'ᅯ',
+  'ㅞ' => 'ᅰ',
+  'ㅟ' => 'ᅱ',
+  'ㅠ' => 'ᅲ',
+  'ㅡ' => 'ᅳ',
+  'ㅢ' => 'ᅴ',
+  'ㅣ' => 'ᅵ',
+  'ㅤ' => 'ᅠ',
+  'ㅥ' => 'ᄔ',
+  'ㅦ' => 'ᄕ',
+  'ㅧ' => 'ᇇ',
+  'ㅨ' => 'ᇈ',
+  'ㅩ' => 'ᇌ',
+  'ㅪ' => 'ᇎ',
+  'ㅫ' => 'ᇓ',
+  'ㅬ' => 'ᇗ',
+  'ㅭ' => 'ᇙ',
+  'ㅮ' => 'ᄜ',
+  'ㅯ' => 'ᇝ',
+  'ㅰ' => 'ᇟ',
+  'ㅱ' => 'ᄝ',
+  'ㅲ' => 'ᄞ',
+  'ㅳ' => 'ᄠ',
+  'ㅴ' => 'ᄢ',
+  'ㅵ' => 'ᄣ',
+  'ㅶ' => 'ᄧ',
+  'ㅷ' => 'ᄩ',
+  'ㅸ' => 'ᄫ',
+  'ㅹ' => 'ᄬ',
+  'ㅺ' => 'ᄭ',
+  'ㅻ' => 'ᄮ',
+  'ㅼ' => 'ᄯ',
+  'ㅽ' => 'ᄲ',
+  'ㅾ' => 'ᄶ',
+  'ㅿ' => 'ᅀ',
+  'ㆀ' => 'ᅇ',
+  'ㆁ' => 'ᅌ',
+  'ㆂ' => 'ᇱ',
+  'ㆃ' => 'ᇲ',
+  'ㆄ' => 'ᅗ',
+  'ㆅ' => 'ᅘ',
+  'ㆆ' => 'ᅙ',
+  'ㆇ' => 'ᆄ',
+  'ㆈ' => 'ᆅ',
+  'ㆉ' => 'ᆈ',
+  'ㆊ' => 'ᆑ',
+  'ㆋ' => 'ᆒ',
+  'ㆌ' => 'ᆔ',
+  'ㆍ' => 'ᆞ',
+  'ㆎ' => 'ᆡ',
+  '㆒' => '一',
+  '㆓' => '二',
+  '㆔' => '三',
+  '㆕' => '四',
+  '㆖' => '上',
+  '㆗' => '中',
+  '㆘' => '下',
+  '㆙' => '甲',
+  '㆚' => '乙',
+  '㆛' => '丙',
+  '㆜' => '丁',
+  '㆝' => '天',
+  '㆞' => '地',
+  '㆟' => '人',
+  '㈀' => '(ᄀ)',
+  '㈁' => '(ᄂ)',
+  '㈂' => '(ᄃ)',
+  '㈃' => '(ᄅ)',
+  '㈄' => '(ᄆ)',
+  '㈅' => '(ᄇ)',
+  '㈆' => '(ᄉ)',
+  '㈇' => '(ᄋ)',
+  '㈈' => '(ᄌ)',
+  '㈉' => '(ᄎ)',
+  '㈊' => '(ᄏ)',
+  '㈋' => '(ᄐ)',
+  '㈌' => '(ᄑ)',
+  '㈍' => '(ᄒ)',
+  '㈎' => '(가)',
+  '㈏' => '(나)',
+  '㈐' => '(다)',
+  '㈑' => '(라)',
+  '㈒' => '(마)',
+  '㈓' => '(바)',
+  '㈔' => '(사)',
+  '㈕' => '(아)',
+  '㈖' => '(자)',
+  '㈗' => '(차)',
+  '㈘' => '(카)',
+  '㈙' => '(타)',
+  '㈚' => '(파)',
+  '㈛' => '(하)',
+  '㈜' => '(주)',
+  '㈝' => '(오전)',
+  '㈞' => '(오후)',
+  '㈠' => '(一)',
+  '㈡' => '(二)',
+  '㈢' => '(三)',
+  '㈣' => '(四)',
+  '㈤' => '(五)',
+  '㈥' => '(六)',
+  '㈦' => '(七)',
+  '㈧' => '(八)',
+  '㈨' => '(九)',
+  '㈩' => '(十)',
+  '㈪' => '(月)',
+  '㈫' => '(火)',
+  '㈬' => '(水)',
+  '㈭' => '(木)',
+  '㈮' => '(金)',
+  '㈯' => '(土)',
+  '㈰' => '(日)',
+  '㈱' => '(株)',
+  '㈲' => '(有)',
+  '㈳' => '(社)',
+  '㈴' => '(名)',
+  '㈵' => '(特)',
+  '㈶' => '(財)',
+  '㈷' => '(祝)',
+  '㈸' => '(労)',
+  '㈹' => '(代)',
+  '㈺' => '(呼)',
+  '㈻' => '(学)',
+  '㈼' => '(監)',
+  '㈽' => '(企)',
+  '㈾' => '(資)',
+  '㈿' => '(協)',
+  '㉀' => '(祭)',
+  '㉁' => '(休)',
+  '㉂' => '(自)',
+  '㉃' => '(至)',
+  '㉄' => '問',
+  '㉅' => '幼',
+  '㉆' => '文',
+  '㉇' => '箏',
+  '㉐' => 'PTE',
+  '㉑' => '21',
+  '㉒' => '22',
+  '㉓' => '23',
+  '㉔' => '24',
+  '㉕' => '25',
+  '㉖' => '26',
+  '㉗' => '27',
+  '㉘' => '28',
+  '㉙' => '29',
+  '㉚' => '30',
+  '㉛' => '31',
+  '㉜' => '32',
+  '㉝' => '33',
+  '㉞' => '34',
+  '㉟' => '35',
+  '㉠' => 'ᄀ',
+  '㉡' => 'ᄂ',
+  '㉢' => 'ᄃ',
+  '㉣' => 'ᄅ',
+  '㉤' => 'ᄆ',
+  '㉥' => 'ᄇ',
+  '㉦' => 'ᄉ',
+  '㉧' => 'ᄋ',
+  '㉨' => 'ᄌ',
+  '㉩' => 'ᄎ',
+  '㉪' => 'ᄏ',
+  '㉫' => 'ᄐ',
+  '㉬' => 'ᄑ',
+  '㉭' => 'ᄒ',
+  '㉮' => '가',
+  '㉯' => '나',
+  '㉰' => '다',
+  '㉱' => '라',
+  '㉲' => '마',
+  '㉳' => '바',
+  '㉴' => '사',
+  '㉵' => '아',
+  '㉶' => '자',
+  '㉷' => '차',
+  '㉸' => '카',
+  '㉹' => '타',
+  '㉺' => '파',
+  '㉻' => '하',
+  '㉼' => '참고',
+  '㉽' => '주의',
+  '㉾' => '우',
+  '㊀' => '一',
+  '㊁' => '二',
+  '㊂' => '三',
+  '㊃' => '四',
+  '㊄' => '五',
+  '㊅' => '六',
+  '㊆' => '七',
+  '㊇' => '八',
+  '㊈' => '九',
+  '㊉' => '十',
+  '㊊' => '月',
+  '㊋' => '火',
+  '㊌' => '水',
+  '㊍' => '木',
+  '㊎' => '金',
+  '㊏' => '土',
+  '㊐' => '日',
+  '㊑' => '株',
+  '㊒' => '有',
+  '㊓' => '社',
+  '㊔' => '名',
+  '㊕' => '特',
+  '㊖' => '財',
+  '㊗' => '祝',
+  '㊘' => '労',
+  '㊙' => '秘',
+  '㊚' => '男',
+  '㊛' => '女',
+  '㊜' => '適',
+  '㊝' => '優',
+  '㊞' => '印',
+  '㊟' => '注',
+  '㊠' => '項',
+  '㊡' => '休',
+  '㊢' => '写',
+  '㊣' => '正',
+  '㊤' => '上',
+  '㊥' => '中',
+  '㊦' => '下',
+  '㊧' => '左',
+  '㊨' => '右',
+  '㊩' => '医',
+  '㊪' => '宗',
+  '㊫' => '学',
+  '㊬' => '監',
+  '㊭' => '企',
+  '㊮' => '資',
+  '㊯' => '協',
+  '㊰' => '夜',
+  '㊱' => '36',
+  '㊲' => '37',
+  '㊳' => '38',
+  '㊴' => '39',
+  '㊵' => '40',
+  '㊶' => '41',
+  '㊷' => '42',
+  '㊸' => '43',
+  '㊹' => '44',
+  '㊺' => '45',
+  '㊻' => '46',
+  '㊼' => '47',
+  '㊽' => '48',
+  '㊾' => '49',
+  '㊿' => '50',
+  '㋀' => '1月',
+  '㋁' => '2月',
+  '㋂' => '3月',
+  '㋃' => '4月',
+  '㋄' => '5月',
+  '㋅' => '6月',
+  '㋆' => '7月',
+  '㋇' => '8月',
+  '㋈' => '9月',
+  '㋉' => '10月',
+  '㋊' => '11月',
+  '㋋' => '12月',
+  '㋌' => 'Hg',
+  '㋍' => 'erg',
+  '㋎' => 'eV',
+  '㋏' => 'LTD',
+  '㋐' => 'ア',
+  '㋑' => 'イ',
+  '㋒' => 'ウ',
+  '㋓' => 'エ',
+  '㋔' => 'オ',
+  '㋕' => 'カ',
+  '㋖' => 'キ',
+  '㋗' => 'ク',
+  '㋘' => 'ケ',
+  '㋙' => 'コ',
+  '㋚' => 'サ',
+  '㋛' => 'シ',
+  '㋜' => 'ス',
+  '㋝' => 'セ',
+  '㋞' => 'ソ',
+  '㋟' => 'タ',
+  '㋠' => 'チ',
+  '㋡' => 'ツ',
+  '㋢' => 'テ',
+  '㋣' => 'ト',
+  '㋤' => 'ナ',
+  '㋥' => 'ニ',
+  '㋦' => 'ヌ',
+  '㋧' => 'ネ',
+  '㋨' => 'ノ',
+  '㋩' => 'ハ',
+  '㋪' => 'ヒ',
+  '㋫' => 'フ',
+  '㋬' => 'ヘ',
+  '㋭' => 'ホ',
+  '㋮' => 'マ',
+  '㋯' => 'ミ',
+  '㋰' => 'ム',
+  '㋱' => 'メ',
+  '㋲' => 'モ',
+  '㋳' => 'ヤ',
+  '㋴' => 'ユ',
+  '㋵' => 'ヨ',
+  '㋶' => 'ラ',
+  '㋷' => 'リ',
+  '㋸' => 'ル',
+  '㋹' => 'レ',
+  '㋺' => 'ロ',
+  '㋻' => 'ワ',
+  '㋼' => 'ヰ',
+  '㋽' => 'ヱ',
+  '㋾' => 'ヲ',
+  '㋿' => '令和',
+  '㌀' => 'アパート',
+  '㌁' => 'アルファ',
+  '㌂' => 'アンペア',
+  '㌃' => 'アール',
+  '㌄' => 'イニング',
+  '㌅' => 'インチ',
+  '㌆' => 'ウォン',
+  '㌇' => 'エスクード',
+  '㌈' => 'エーカー',
+  '㌉' => 'オンス',
+  '㌊' => 'オーム',
+  '㌋' => 'カイリ',
+  '㌌' => 'カラット',
+  '㌍' => 'カロリー',
+  '㌎' => 'ガロン',
+  '㌏' => 'ガンマ',
+  '㌐' => 'ギガ',
+  '㌑' => 'ギニー',
+  '㌒' => 'キュリー',
+  '㌓' => 'ギルダー',
+  '㌔' => 'キロ',
+  '㌕' => 'キログラム',
+  '㌖' => 'キロメートル',
+  '㌗' => 'キロワット',
+  '㌘' => 'グラム',
+  '㌙' => 'グラムトン',
+  '㌚' => 'クルゼイロ',
+  '㌛' => 'クローネ',
+  '㌜' => 'ケース',
+  '㌝' => 'コルナ',
+  '㌞' => 'コーポ',
+  '㌟' => 'サイクル',
+  '㌠' => 'サンチーム',
+  '㌡' => 'シリング',
+  '㌢' => 'センチ',
+  '㌣' => 'セント',
+  '㌤' => 'ダース',
+  '㌥' => 'デシ',
+  '㌦' => 'ドル',
+  '㌧' => 'トン',
+  '㌨' => 'ナノ',
+  '㌩' => 'ノット',
+  '㌪' => 'ハイツ',
+  '㌫' => 'パーセント',
+  '㌬' => 'パーツ',
+  '㌭' => 'バーレル',
+  '㌮' => 'ピアストル',
+  '㌯' => 'ピクル',
+  '㌰' => 'ピコ',
+  '㌱' => 'ビル',
+  '㌲' => 'ファラッド',
+  '㌳' => 'フィート',
+  '㌴' => 'ブッシェル',
+  '㌵' => 'フラン',
+  '㌶' => 'ヘクタール',
+  '㌷' => 'ペソ',
+  '㌸' => 'ペニヒ',
+  '㌹' => 'ヘルツ',
+  '㌺' => 'ペンス',
+  '㌻' => 'ページ',
+  '㌼' => 'ベータ',
+  '㌽' => 'ポイント',
+  '㌾' => 'ボルト',
+  '㌿' => 'ホン',
+  '㍀' => 'ポンド',
+  '㍁' => 'ホール',
+  '㍂' => 'ホーン',
+  '㍃' => 'マイクロ',
+  '㍄' => 'マイル',
+  '㍅' => 'マッハ',
+  '㍆' => 'マルク',
+  '㍇' => 'マンション',
+  '㍈' => 'ミクロン',
+  '㍉' => 'ミリ',
+  '㍊' => 'ミリバール',
+  '㍋' => 'メガ',
+  '㍌' => 'メガトン',
+  '㍍' => 'メートル',
+  '㍎' => 'ヤード',
+  '㍏' => 'ヤール',
+  '㍐' => 'ユアン',
+  '㍑' => 'リットル',
+  '㍒' => 'リラ',
+  '㍓' => 'ルピー',
+  '㍔' => 'ルーブル',
+  '㍕' => 'レム',
+  '㍖' => 'レントゲン',
+  '㍗' => 'ワット',
+  '㍘' => '0点',
+  '㍙' => '1点',
+  '㍚' => '2点',
+  '㍛' => '3点',
+  '㍜' => '4点',
+  '㍝' => '5点',
+  '㍞' => '6点',
+  '㍟' => '7点',
+  '㍠' => '8点',
+  '㍡' => '9点',
+  '㍢' => '10点',
+  '㍣' => '11点',
+  '㍤' => '12点',
+  '㍥' => '13点',
+  '㍦' => '14点',
+  '㍧' => '15点',
+  '㍨' => '16点',
+  '㍩' => '17点',
+  '㍪' => '18点',
+  '㍫' => '19点',
+  '㍬' => '20点',
+  '㍭' => '21点',
+  '㍮' => '22点',
+  '㍯' => '23点',
+  '㍰' => '24点',
+  '㍱' => 'hPa',
+  '㍲' => 'da',
+  '㍳' => 'AU',
+  '㍴' => 'bar',
+  '㍵' => 'oV',
+  '㍶' => 'pc',
+  '㍷' => 'dm',
+  '㍸' => 'dm2',
+  '㍹' => 'dm3',
+  '㍺' => 'IU',
+  '㍻' => '平成',
+  '㍼' => '昭和',
+  '㍽' => '大正',
+  '㍾' => '明治',
+  '㍿' => '株式会社',
+  '㎀' => 'pA',
+  '㎁' => 'nA',
+  '㎂' => 'μA',
+  '㎃' => 'mA',
+  '㎄' => 'kA',
+  '㎅' => 'KB',
+  '㎆' => 'MB',
+  '㎇' => 'GB',
+  '㎈' => 'cal',
+  '㎉' => 'kcal',
+  '㎊' => 'pF',
+  '㎋' => 'nF',
+  '㎌' => 'μF',
+  '㎍' => 'μg',
+  '㎎' => 'mg',
+  '㎏' => 'kg',
+  '㎐' => 'Hz',
+  '㎑' => 'kHz',
+  '㎒' => 'MHz',
+  '㎓' => 'GHz',
+  '㎔' => 'THz',
+  '㎕' => 'μl',
+  '㎖' => 'ml',
+  '㎗' => 'dl',
+  '㎘' => 'kl',
+  '㎙' => 'fm',
+  '㎚' => 'nm',
+  '㎛' => 'μm',
+  '㎜' => 'mm',
+  '㎝' => 'cm',
+  '㎞' => 'km',
+  '㎟' => 'mm2',
+  '㎠' => 'cm2',
+  '㎡' => 'm2',
+  '㎢' => 'km2',
+  '㎣' => 'mm3',
+  '㎤' => 'cm3',
+  '㎥' => 'm3',
+  '㎦' => 'km3',
+  '㎧' => 'm∕s',
+  '㎨' => 'm∕s2',
+  '㎩' => 'Pa',
+  '㎪' => 'kPa',
+  '㎫' => 'MPa',
+  '㎬' => 'GPa',
+  '㎭' => 'rad',
+  '㎮' => 'rad∕s',
+  '㎯' => 'rad∕s2',
+  '㎰' => 'ps',
+  '㎱' => 'ns',
+  '㎲' => 'μs',
+  '㎳' => 'ms',
+  '㎴' => 'pV',
+  '㎵' => 'nV',
+  '㎶' => 'μV',
+  '㎷' => 'mV',
+  '㎸' => 'kV',
+  '㎹' => 'MV',
+  '㎺' => 'pW',
+  '㎻' => 'nW',
+  '㎼' => 'μW',
+  '㎽' => 'mW',
+  '㎾' => 'kW',
+  '㎿' => 'MW',
+  '㏀' => 'kΩ',
+  '㏁' => 'MΩ',
+  '㏂' => 'a.m.',
+  '㏃' => 'Bq',
+  '㏄' => 'cc',
+  '㏅' => 'cd',
+  '㏆' => 'C∕kg',
+  '㏇' => 'Co.',
+  '㏈' => 'dB',
+  '㏉' => 'Gy',
+  '㏊' => 'ha',
+  '㏋' => 'HP',
+  '㏌' => 'in',
+  '㏍' => 'KK',
+  '㏎' => 'KM',
+  '㏏' => 'kt',
+  '㏐' => 'lm',
+  '㏑' => 'ln',
+  '㏒' => 'log',
+  '㏓' => 'lx',
+  '㏔' => 'mb',
+  '㏕' => 'mil',
+  '㏖' => 'mol',
+  '㏗' => 'PH',
+  '㏘' => 'p.m.',
+  '㏙' => 'PPM',
+  '㏚' => 'PR',
+  '㏛' => 'sr',
+  '㏜' => 'Sv',
+  '㏝' => 'Wb',
+  '㏞' => 'V∕m',
+  '㏟' => 'A∕m',
+  '㏠' => '1日',
+  '㏡' => '2日',
+  '㏢' => '3日',
+  '㏣' => '4日',
+  '㏤' => '5日',
+  '㏥' => '6日',
+  '㏦' => '7日',
+  '㏧' => '8日',
+  '㏨' => '9日',
+  '㏩' => '10日',
+  '㏪' => '11日',
+  '㏫' => '12日',
+  '㏬' => '13日',
+  '㏭' => '14日',
+  '㏮' => '15日',
+  '㏯' => '16日',
+  '㏰' => '17日',
+  '㏱' => '18日',
+  '㏲' => '19日',
+  '㏳' => '20日',
+  '㏴' => '21日',
+  '㏵' => '22日',
+  '㏶' => '23日',
+  '㏷' => '24日',
+  '㏸' => '25日',
+  '㏹' => '26日',
+  '㏺' => '27日',
+  '㏻' => '28日',
+  '㏼' => '29日',
+  '㏽' => '30日',
+  '㏾' => '31日',
+  '㏿' => 'gal',
+  'ꚜ' => 'ъ',
+  'ꚝ' => 'ь',
+  'ꝰ' => 'ꝯ',
+  'ꟸ' => 'Ħ',
+  'ꟹ' => 'œ',
+  'ꭜ' => 'ꜧ',
+  'ꭝ' => 'ꬷ',
+  'ꭞ' => 'ɫ',
+  'ꭟ' => 'ꭒ',
+  'ꭩ' => 'ʍ',
+  'ff' => 'ff',
+  'fi' => 'fi',
+  'fl' => 'fl',
+  'ffi' => 'ffi',
+  'ffl' => 'ffl',
+  'ſt' => 'st',
+  'st' => 'st',
+  'ﬓ' => 'մն',
+  'ﬔ' => 'մե',
+  'ﬕ' => 'մի',
+  'ﬖ' => 'վն',
+  'ﬗ' => 'մխ',
+  'ﬠ' => 'ע',
+  'ﬡ' => 'א',
+  'ﬢ' => 'ד',
+  'ﬣ' => 'ה',
+  'ﬤ' => 'כ',
+  'ﬥ' => 'ל',
+  'ﬦ' => 'ם',
+  'ﬧ' => 'ר',
+  'ﬨ' => 'ת',
+  '﬩' => '+',
+  'ﭏ' => 'אל',
+  'ﭐ' => 'ٱ',
+  'ﭑ' => 'ٱ',
+  'ﭒ' => 'ٻ',
+  'ﭓ' => 'ٻ',
+  'ﭔ' => 'ٻ',
+  'ﭕ' => 'ٻ',
+  'ﭖ' => 'پ',
+  'ﭗ' => 'پ',
+  'ﭘ' => 'پ',
+  'ﭙ' => 'پ',
+  'ﭚ' => 'ڀ',
+  'ﭛ' => 'ڀ',
+  'ﭜ' => 'ڀ',
+  'ﭝ' => 'ڀ',
+  'ﭞ' => 'ٺ',
+  'ﭟ' => 'ٺ',
+  'ﭠ' => 'ٺ',
+  'ﭡ' => 'ٺ',
+  'ﭢ' => 'ٿ',
+  'ﭣ' => 'ٿ',
+  'ﭤ' => 'ٿ',
+  'ﭥ' => 'ٿ',
+  'ﭦ' => 'ٹ',
+  'ﭧ' => 'ٹ',
+  'ﭨ' => 'ٹ',
+  'ﭩ' => 'ٹ',
+  'ﭪ' => 'ڤ',
+  'ﭫ' => 'ڤ',
+  'ﭬ' => 'ڤ',
+  'ﭭ' => 'ڤ',
+  'ﭮ' => 'ڦ',
+  'ﭯ' => 'ڦ',
+  'ﭰ' => 'ڦ',
+  'ﭱ' => 'ڦ',
+  'ﭲ' => 'ڄ',
+  'ﭳ' => 'ڄ',
+  'ﭴ' => 'ڄ',
+  'ﭵ' => 'ڄ',
+  'ﭶ' => 'ڃ',
+  'ﭷ' => 'ڃ',
+  'ﭸ' => 'ڃ',
+  'ﭹ' => 'ڃ',
+  'ﭺ' => 'چ',
+  'ﭻ' => 'چ',
+  'ﭼ' => 'چ',
+  'ﭽ' => 'چ',
+  'ﭾ' => 'ڇ',
+  'ﭿ' => 'ڇ',
+  'ﮀ' => 'ڇ',
+  'ﮁ' => 'ڇ',
+  'ﮂ' => 'ڍ',
+  'ﮃ' => 'ڍ',
+  'ﮄ' => 'ڌ',
+  'ﮅ' => 'ڌ',
+  'ﮆ' => 'ڎ',
+  'ﮇ' => 'ڎ',
+  'ﮈ' => 'ڈ',
+  'ﮉ' => 'ڈ',
+  'ﮊ' => 'ژ',
+  'ﮋ' => 'ژ',
+  'ﮌ' => 'ڑ',
+  'ﮍ' => 'ڑ',
+  'ﮎ' => 'ک',
+  'ﮏ' => 'ک',
+  'ﮐ' => 'ک',
+  'ﮑ' => 'ک',
+  'ﮒ' => 'گ',
+  'ﮓ' => 'گ',
+  'ﮔ' => 'گ',
+  'ﮕ' => 'گ',
+  'ﮖ' => 'ڳ',
+  'ﮗ' => 'ڳ',
+  'ﮘ' => 'ڳ',
+  'ﮙ' => 'ڳ',
+  'ﮚ' => 'ڱ',
+  'ﮛ' => 'ڱ',
+  'ﮜ' => 'ڱ',
+  'ﮝ' => 'ڱ',
+  'ﮞ' => 'ں',
+  'ﮟ' => 'ں',
+  'ﮠ' => 'ڻ',
+  'ﮡ' => 'ڻ',
+  'ﮢ' => 'ڻ',
+  'ﮣ' => 'ڻ',
+  'ﮤ' => 'ۀ',
+  'ﮥ' => 'ۀ',
+  'ﮦ' => 'ہ',
+  'ﮧ' => 'ہ',
+  'ﮨ' => 'ہ',
+  'ﮩ' => 'ہ',
+  'ﮪ' => 'ھ',
+  'ﮫ' => 'ھ',
+  'ﮬ' => 'ھ',
+  'ﮭ' => 'ھ',
+  'ﮮ' => 'ے',
+  'ﮯ' => 'ے',
+  'ﮰ' => 'ۓ',
+  'ﮱ' => 'ۓ',
+  'ﯓ' => 'ڭ',
+  'ﯔ' => 'ڭ',
+  'ﯕ' => 'ڭ',
+  'ﯖ' => 'ڭ',
+  'ﯗ' => 'ۇ',
+  'ﯘ' => 'ۇ',
+  'ﯙ' => 'ۆ',
+  'ﯚ' => 'ۆ',
+  'ﯛ' => 'ۈ',
+  'ﯜ' => 'ۈ',
+  'ﯝ' => 'ۇٴ',
+  'ﯞ' => 'ۋ',
+  'ﯟ' => 'ۋ',
+  'ﯠ' => 'ۅ',
+  'ﯡ' => 'ۅ',
+  'ﯢ' => 'ۉ',
+  'ﯣ' => 'ۉ',
+  'ﯤ' => 'ې',
+  'ﯥ' => 'ې',
+  'ﯦ' => 'ې',
+  'ﯧ' => 'ې',
+  'ﯨ' => 'ى',
+  'ﯩ' => 'ى',
+  'ﯪ' => 'ئا',
+  'ﯫ' => 'ئا',
+  'ﯬ' => 'ئە',
+  'ﯭ' => 'ئە',
+  'ﯮ' => 'ئو',
+  'ﯯ' => 'ئو',
+  'ﯰ' => 'ئۇ',
+  'ﯱ' => 'ئۇ',
+  'ﯲ' => 'ئۆ',
+  'ﯳ' => 'ئۆ',
+  'ﯴ' => 'ئۈ',
+  'ﯵ' => 'ئۈ',
+  'ﯶ' => 'ئې',
+  'ﯷ' => 'ئې',
+  'ﯸ' => 'ئې',
+  'ﯹ' => 'ئى',
+  'ﯺ' => 'ئى',
+  'ﯻ' => 'ئى',
+  'ﯼ' => 'ی',
+  'ﯽ' => 'ی',
+  'ﯾ' => 'ی',
+  'ﯿ' => 'ی',
+  'ﰀ' => 'ئج',
+  'ﰁ' => 'ئح',
+  'ﰂ' => 'ئم',
+  'ﰃ' => 'ئى',
+  'ﰄ' => 'ئي',
+  'ﰅ' => 'بج',
+  'ﰆ' => 'بح',
+  'ﰇ' => 'بخ',
+  'ﰈ' => 'بم',
+  'ﰉ' => 'بى',
+  'ﰊ' => 'بي',
+  'ﰋ' => 'تج',
+  'ﰌ' => 'تح',
+  'ﰍ' => 'تخ',
+  'ﰎ' => 'تم',
+  'ﰏ' => 'تى',
+  'ﰐ' => 'تي',
+  'ﰑ' => 'ثج',
+  'ﰒ' => 'ثم',
+  'ﰓ' => 'ثى',
+  'ﰔ' => 'ثي',
+  'ﰕ' => 'جح',
+  'ﰖ' => 'جم',
+  'ﰗ' => 'حج',
+  'ﰘ' => 'حم',
+  'ﰙ' => 'خج',
+  'ﰚ' => 'خح',
+  'ﰛ' => 'خم',
+  'ﰜ' => 'سج',
+  'ﰝ' => 'سح',
+  'ﰞ' => 'سخ',
+  'ﰟ' => 'سم',
+  'ﰠ' => 'صح',
+  'ﰡ' => 'صم',
+  'ﰢ' => 'ضج',
+  'ﰣ' => 'ضح',
+  'ﰤ' => 'ضخ',
+  'ﰥ' => 'ضم',
+  'ﰦ' => 'طح',
+  'ﰧ' => 'طم',
+  'ﰨ' => 'ظم',
+  'ﰩ' => 'عج',
+  'ﰪ' => 'عم',
+  'ﰫ' => 'غج',
+  'ﰬ' => 'غم',
+  'ﰭ' => 'فج',
+  'ﰮ' => 'فح',
+  'ﰯ' => 'فخ',
+  'ﰰ' => 'فم',
+  'ﰱ' => 'فى',
+  'ﰲ' => 'في',
+  'ﰳ' => 'قح',
+  'ﰴ' => 'قم',
+  'ﰵ' => 'قى',
+  'ﰶ' => 'قي',
+  'ﰷ' => 'كا',
+  'ﰸ' => 'كج',
+  'ﰹ' => 'كح',
+  'ﰺ' => 'كخ',
+  'ﰻ' => 'كل',
+  'ﰼ' => 'كم',
+  'ﰽ' => 'كى',
+  'ﰾ' => 'كي',
+  'ﰿ' => 'لج',
+  'ﱀ' => 'لح',
+  'ﱁ' => 'لخ',
+  'ﱂ' => 'لم',
+  'ﱃ' => 'لى',
+  'ﱄ' => 'لي',
+  'ﱅ' => 'مج',
+  'ﱆ' => 'مح',
+  'ﱇ' => 'مخ',
+  'ﱈ' => 'مم',
+  'ﱉ' => 'مى',
+  'ﱊ' => 'مي',
+  'ﱋ' => 'نج',
+  'ﱌ' => 'نح',
+  'ﱍ' => 'نخ',
+  'ﱎ' => 'نم',
+  'ﱏ' => 'نى',
+  'ﱐ' => 'ني',
+  'ﱑ' => 'هج',
+  'ﱒ' => 'هم',
+  'ﱓ' => 'هى',
+  'ﱔ' => 'هي',
+  'ﱕ' => 'يج',
+  'ﱖ' => 'يح',
+  'ﱗ' => 'يخ',
+  'ﱘ' => 'يم',
+  'ﱙ' => 'يى',
+  'ﱚ' => 'يي',
+  'ﱛ' => 'ذٰ',
+  'ﱜ' => 'رٰ',
+  'ﱝ' => 'ىٰ',
+  'ﱞ' => ' ٌّ',
+  'ﱟ' => ' ٍّ',
+  'ﱠ' => ' َّ',
+  'ﱡ' => ' ُّ',
+  'ﱢ' => ' ِّ',
+  'ﱣ' => ' ّٰ',
+  'ﱤ' => 'ئر',
+  'ﱥ' => 'ئز',
+  'ﱦ' => 'ئم',
+  'ﱧ' => 'ئن',
+  'ﱨ' => 'ئى',
+  'ﱩ' => 'ئي',
+  'ﱪ' => 'بر',
+  'ﱫ' => 'بز',
+  'ﱬ' => 'بم',
+  'ﱭ' => 'بن',
+  'ﱮ' => 'بى',
+  'ﱯ' => 'بي',
+  'ﱰ' => 'تر',
+  'ﱱ' => 'تز',
+  'ﱲ' => 'تم',
+  'ﱳ' => 'تن',
+  'ﱴ' => 'تى',
+  'ﱵ' => 'تي',
+  'ﱶ' => 'ثر',
+  'ﱷ' => 'ثز',
+  'ﱸ' => 'ثم',
+  'ﱹ' => 'ثن',
+  'ﱺ' => 'ثى',
+  'ﱻ' => 'ثي',
+  'ﱼ' => 'فى',
+  'ﱽ' => 'في',
+  'ﱾ' => 'قى',
+  'ﱿ' => 'قي',
+  'ﲀ' => 'كا',
+  'ﲁ' => 'كل',
+  'ﲂ' => 'كم',
+  'ﲃ' => 'كى',
+  'ﲄ' => 'كي',
+  'ﲅ' => 'لم',
+  'ﲆ' => 'لى',
+  'ﲇ' => 'لي',
+  'ﲈ' => 'ما',
+  'ﲉ' => 'مم',
+  'ﲊ' => 'نر',
+  'ﲋ' => 'نز',
+  'ﲌ' => 'نم',
+  'ﲍ' => 'نن',
+  'ﲎ' => 'نى',
+  'ﲏ' => 'ني',
+  'ﲐ' => 'ىٰ',
+  'ﲑ' => 'ير',
+  'ﲒ' => 'يز',
+  'ﲓ' => 'يم',
+  'ﲔ' => 'ين',
+  'ﲕ' => 'يى',
+  'ﲖ' => 'يي',
+  'ﲗ' => 'ئج',
+  'ﲘ' => 'ئح',
+  'ﲙ' => 'ئخ',
+  'ﲚ' => 'ئم',
+  'ﲛ' => 'ئه',
+  'ﲜ' => 'بج',
+  'ﲝ' => 'بح',
+  'ﲞ' => 'بخ',
+  'ﲟ' => 'بم',
+  'ﲠ' => 'به',
+  'ﲡ' => 'تج',
+  'ﲢ' => 'تح',
+  'ﲣ' => 'تخ',
+  'ﲤ' => 'تم',
+  'ﲥ' => 'ته',
+  'ﲦ' => 'ثم',
+  'ﲧ' => 'جح',
+  'ﲨ' => 'جم',
+  'ﲩ' => 'حج',
+  'ﲪ' => 'حم',
+  'ﲫ' => 'خج',
+  'ﲬ' => 'خم',
+  'ﲭ' => 'سج',
+  'ﲮ' => 'سح',
+  'ﲯ' => 'سخ',
+  'ﲰ' => 'سم',
+  'ﲱ' => 'صح',
+  'ﲲ' => 'صخ',
+  'ﲳ' => 'صم',
+  'ﲴ' => 'ضج',
+  'ﲵ' => 'ضح',
+  'ﲶ' => 'ضخ',
+  'ﲷ' => 'ضم',
+  'ﲸ' => 'طح',
+  'ﲹ' => 'ظم',
+  'ﲺ' => 'عج',
+  'ﲻ' => 'عم',
+  'ﲼ' => 'غج',
+  'ﲽ' => 'غم',
+  'ﲾ' => 'فج',
+  'ﲿ' => 'فح',
+  'ﳀ' => 'فخ',
+  'ﳁ' => 'فم',
+  'ﳂ' => 'قح',
+  'ﳃ' => 'قم',
+  'ﳄ' => 'كج',
+  'ﳅ' => 'كح',
+  'ﳆ' => 'كخ',
+  'ﳇ' => 'كل',
+  'ﳈ' => 'كم',
+  'ﳉ' => 'لج',
+  'ﳊ' => 'لح',
+  'ﳋ' => 'لخ',
+  'ﳌ' => 'لم',
+  'ﳍ' => 'له',
+  'ﳎ' => 'مج',
+  'ﳏ' => 'مح',
+  'ﳐ' => 'مخ',
+  'ﳑ' => 'مم',
+  'ﳒ' => 'نج',
+  'ﳓ' => 'نح',
+  'ﳔ' => 'نخ',
+  'ﳕ' => 'نم',
+  'ﳖ' => 'نه',
+  'ﳗ' => 'هج',
+  'ﳘ' => 'هم',
+  'ﳙ' => 'هٰ',
+  'ﳚ' => 'يج',
+  'ﳛ' => 'يح',
+  'ﳜ' => 'يخ',
+  'ﳝ' => 'يم',
+  'ﳞ' => 'يه',
+  'ﳟ' => 'ئم',
+  'ﳠ' => 'ئه',
+  'ﳡ' => 'بم',
+  'ﳢ' => 'به',
+  'ﳣ' => 'تم',
+  'ﳤ' => 'ته',
+  'ﳥ' => 'ثم',
+  'ﳦ' => 'ثه',
+  'ﳧ' => 'سم',
+  'ﳨ' => 'سه',
+  'ﳩ' => 'شم',
+  'ﳪ' => 'شه',
+  'ﳫ' => 'كل',
+  'ﳬ' => 'كم',
+  'ﳭ' => 'لم',
+  'ﳮ' => 'نم',
+  'ﳯ' => 'نه',
+  'ﳰ' => 'يم',
+  'ﳱ' => 'يه',
+  'ﳲ' => 'ـَّ',
+  'ﳳ' => 'ـُّ',
+  'ﳴ' => 'ـِّ',
+  'ﳵ' => 'طى',
+  'ﳶ' => 'طي',
+  'ﳷ' => 'عى',
+  'ﳸ' => 'عي',
+  'ﳹ' => 'غى',
+  'ﳺ' => 'غي',
+  'ﳻ' => 'سى',
+  'ﳼ' => 'سي',
+  'ﳽ' => 'شى',
+  'ﳾ' => 'شي',
+  'ﳿ' => 'حى',
+  'ﴀ' => 'حي',
+  'ﴁ' => 'جى',
+  'ﴂ' => 'جي',
+  'ﴃ' => 'خى',
+  'ﴄ' => 'خي',
+  'ﴅ' => 'صى',
+  'ﴆ' => 'صي',
+  'ﴇ' => 'ضى',
+  'ﴈ' => 'ضي',
+  'ﴉ' => 'شج',
+  'ﴊ' => 'شح',
+  'ﴋ' => 'شخ',
+  'ﴌ' => 'شم',
+  'ﴍ' => 'شر',
+  'ﴎ' => 'سر',
+  'ﴏ' => 'صر',
+  'ﴐ' => 'ضر',
+  'ﴑ' => 'طى',
+  'ﴒ' => 'طي',
+  'ﴓ' => 'عى',
+  'ﴔ' => 'عي',
+  'ﴕ' => 'غى',
+  'ﴖ' => 'غي',
+  'ﴗ' => 'سى',
+  'ﴘ' => 'سي',
+  'ﴙ' => 'شى',
+  'ﴚ' => 'شي',
+  'ﴛ' => 'حى',
+  'ﴜ' => 'حي',
+  'ﴝ' => 'جى',
+  'ﴞ' => 'جي',
+  'ﴟ' => 'خى',
+  'ﴠ' => 'خي',
+  'ﴡ' => 'صى',
+  'ﴢ' => 'صي',
+  'ﴣ' => 'ضى',
+  'ﴤ' => 'ضي',
+  'ﴥ' => 'شج',
+  'ﴦ' => 'شح',
+  'ﴧ' => 'شخ',
+  'ﴨ' => 'شم',
+  'ﴩ' => 'شر',
+  'ﴪ' => 'سر',
+  'ﴫ' => 'صر',
+  'ﴬ' => 'ضر',
+  'ﴭ' => 'شج',
+  'ﴮ' => 'شح',
+  'ﴯ' => 'شخ',
+  'ﴰ' => 'شم',
+  'ﴱ' => 'سه',
+  'ﴲ' => 'شه',
+  'ﴳ' => 'طم',
+  'ﴴ' => 'سج',
+  'ﴵ' => 'سح',
+  'ﴶ' => 'سخ',
+  'ﴷ' => 'شج',
+  'ﴸ' => 'شح',
+  'ﴹ' => 'شخ',
+  'ﴺ' => 'طم',
+  'ﴻ' => 'ظم',
+  'ﴼ' => 'اً',
+  'ﴽ' => 'اً',
+  'ﵐ' => 'تجم',
+  'ﵑ' => 'تحج',
+  'ﵒ' => 'تحج',
+  'ﵓ' => 'تحم',
+  'ﵔ' => 'تخم',
+  'ﵕ' => 'تمج',
+  'ﵖ' => 'تمح',
+  'ﵗ' => 'تمخ',
+  'ﵘ' => 'جمح',
+  'ﵙ' => 'جمح',
+  'ﵚ' => 'حمي',
+  'ﵛ' => 'حمى',
+  'ﵜ' => 'سحج',
+  'ﵝ' => 'سجح',
+  'ﵞ' => 'سجى',
+  'ﵟ' => 'سمح',
+  'ﵠ' => 'سمح',
+  'ﵡ' => 'سمج',
+  'ﵢ' => 'سمم',
+  'ﵣ' => 'سمم',
+  'ﵤ' => 'صحح',
+  'ﵥ' => 'صحح',
+  'ﵦ' => 'صمم',
+  'ﵧ' => 'شحم',
+  'ﵨ' => 'شحم',
+  'ﵩ' => 'شجي',
+  'ﵪ' => 'شمخ',
+  'ﵫ' => 'شمخ',
+  'ﵬ' => 'شمم',
+  'ﵭ' => 'شمم',
+  'ﵮ' => 'ضحى',
+  'ﵯ' => 'ضخم',
+  'ﵰ' => 'ضخم',
+  'ﵱ' => 'طمح',
+  'ﵲ' => 'طمح',
+  'ﵳ' => 'طمم',
+  'ﵴ' => 'طمي',
+  'ﵵ' => 'عجم',
+  'ﵶ' => 'عمم',
+  'ﵷ' => 'عمم',
+  'ﵸ' => 'عمى',
+  'ﵹ' => 'غمم',
+  'ﵺ' => 'غمي',
+  'ﵻ' => 'غمى',
+  'ﵼ' => 'فخم',
+  'ﵽ' => 'فخم',
+  'ﵾ' => 'قمح',
+  'ﵿ' => 'قمم',
+  'ﶀ' => 'لحم',
+  'ﶁ' => 'لحي',
+  'ﶂ' => 'لحى',
+  'ﶃ' => 'لجج',
+  'ﶄ' => 'لجج',
+  'ﶅ' => 'لخم',
+  'ﶆ' => 'لخم',
+  'ﶇ' => 'لمح',
+  'ﶈ' => 'لمح',
+  'ﶉ' => 'محج',
+  'ﶊ' => 'محم',
+  'ﶋ' => 'محي',
+  'ﶌ' => 'مجح',
+  'ﶍ' => 'مجم',
+  'ﶎ' => 'مخج',
+  'ﶏ' => 'مخم',
+  'ﶒ' => 'مجخ',
+  'ﶓ' => 'همج',
+  'ﶔ' => 'همم',
+  'ﶕ' => 'نحم',
+  'ﶖ' => 'نحى',
+  'ﶗ' => 'نجم',
+  'ﶘ' => 'نجم',
+  'ﶙ' => 'نجى',
+  'ﶚ' => 'نمي',
+  'ﶛ' => 'نمى',
+  'ﶜ' => 'يمم',
+  'ﶝ' => 'يمم',
+  'ﶞ' => 'بخي',
+  'ﶟ' => 'تجي',
+  'ﶠ' => 'تجى',
+  'ﶡ' => 'تخي',
+  'ﶢ' => 'تخى',
+  'ﶣ' => 'تمي',
+  'ﶤ' => 'تمى',
+  'ﶥ' => 'جمي',
+  'ﶦ' => 'جحى',
+  'ﶧ' => 'جمى',
+  'ﶨ' => 'سخى',
+  'ﶩ' => 'صحي',
+  'ﶪ' => 'شحي',
+  'ﶫ' => 'ضحي',
+  'ﶬ' => 'لجي',
+  'ﶭ' => 'لمي',
+  'ﶮ' => 'يحي',
+  'ﶯ' => 'يجي',
+  'ﶰ' => 'يمي',
+  'ﶱ' => 'ممي',
+  'ﶲ' => 'قمي',
+  'ﶳ' => 'نحي',
+  'ﶴ' => 'قمح',
+  'ﶵ' => 'لحم',
+  'ﶶ' => 'عمي',
+  'ﶷ' => 'كمي',
+  'ﶸ' => 'نجح',
+  'ﶹ' => 'مخي',
+  'ﶺ' => 'لجم',
+  'ﶻ' => 'كمم',
+  'ﶼ' => 'لجم',
+  'ﶽ' => 'نجح',
+  'ﶾ' => 'جحي',
+  'ﶿ' => 'حجي',
+  'ﷀ' => 'مجي',
+  'ﷁ' => 'فمي',
+  'ﷂ' => 'بحي',
+  'ﷃ' => 'كمم',
+  'ﷄ' => 'عجم',
+  'ﷅ' => 'صمم',
+  'ﷆ' => 'سخي',
+  'ﷇ' => 'نجي',
+  'ﷰ' => 'صلے',
+  'ﷱ' => 'قلے',
+  'ﷲ' => 'الله',
+  'ﷳ' => 'اكبر',
+  'ﷴ' => 'محمد',
+  'ﷵ' => 'صلعم',
+  'ﷶ' => 'رسول',
+  'ﷷ' => 'عليه',
+  'ﷸ' => 'وسلم',
+  'ﷹ' => 'صلى',
+  'ﷺ' => 'صلى الله عليه وسلم',
+  'ﷻ' => 'جل جلاله',
+  '﷼' => 'ریال',
+  '︐' => ',',
+  '︑' => '、',
+  '︒' => '。',
+  '︓' => ':',
+  '︔' => ';',
+  '︕' => '!',
+  '︖' => '?',
+  '︗' => '〖',
+  '︘' => '〗',
+  '︙' => '...',
+  '︰' => '..',
+  '︱' => '—',
+  '︲' => '–',
+  '︳' => '_',
+  '︴' => '_',
+  '︵' => '(',
+  '︶' => ')',
+  '︷' => '{',
+  '︸' => '}',
+  '︹' => '〔',
+  '︺' => '〕',
+  '︻' => '【',
+  '︼' => '】',
+  '︽' => '《',
+  '︾' => '》',
+  '︿' => '〈',
+  '﹀' => '〉',
+  '﹁' => '「',
+  '﹂' => '」',
+  '﹃' => '『',
+  '﹄' => '』',
+  '﹇' => '[',
+  '﹈' => ']',
+  '﹉' => ' ̅',
+  '﹊' => ' ̅',
+  '﹋' => ' ̅',
+  '﹌' => ' ̅',
+  '﹍' => '_',
+  '﹎' => '_',
+  '﹏' => '_',
+  '﹐' => ',',
+  '﹑' => '、',
+  '﹒' => '.',
+  '﹔' => ';',
+  '﹕' => ':',
+  '﹖' => '?',
+  '﹗' => '!',
+  '﹘' => '—',
+  '﹙' => '(',
+  '﹚' => ')',
+  '﹛' => '{',
+  '﹜' => '}',
+  '﹝' => '〔',
+  '﹞' => '〕',
+  '﹟' => '#',
+  '﹠' => '&',
+  '﹡' => '*',
+  '﹢' => '+',
+  '﹣' => '-',
+  '﹤' => '<',
+  '﹥' => '>',
+  '﹦' => '=',
+  '﹨' => '\\',
+  '﹩' => '$',
+  '﹪' => '%',
+  '﹫' => '@',
+  'ﹰ' => ' ً',
+  'ﹱ' => 'ـً',
+  'ﹲ' => ' ٌ',
+  'ﹴ' => ' ٍ',
+  'ﹶ' => ' َ',
+  'ﹷ' => 'ـَ',
+  'ﹸ' => ' ُ',
+  'ﹹ' => 'ـُ',
+  'ﹺ' => ' ِ',
+  'ﹻ' => 'ـِ',
+  'ﹼ' => ' ّ',
+  'ﹽ' => 'ـّ',
+  'ﹾ' => ' ْ',
+  'ﹿ' => 'ـْ',
+  'ﺀ' => 'ء',
+  'ﺁ' => 'آ',
+  'ﺂ' => 'آ',
+  'ﺃ' => 'أ',
+  'ﺄ' => 'أ',
+  'ﺅ' => 'ؤ',
+  'ﺆ' => 'ؤ',
+  'ﺇ' => 'إ',
+  'ﺈ' => 'إ',
+  'ﺉ' => 'ئ',
+  'ﺊ' => 'ئ',
+  'ﺋ' => 'ئ',
+  'ﺌ' => 'ئ',
+  'ﺍ' => 'ا',
+  'ﺎ' => 'ا',
+  'ﺏ' => 'ب',
+  'ﺐ' => 'ب',
+  'ﺑ' => 'ب',
+  'ﺒ' => 'ب',
+  'ﺓ' => 'ة',
+  'ﺔ' => 'ة',
+  'ﺕ' => 'ت',
+  'ﺖ' => 'ت',
+  'ﺗ' => 'ت',
+  'ﺘ' => 'ت',
+  'ﺙ' => 'ث',
+  'ﺚ' => 'ث',
+  'ﺛ' => 'ث',
+  'ﺜ' => 'ث',
+  'ﺝ' => 'ج',
+  'ﺞ' => 'ج',
+  'ﺟ' => 'ج',
+  'ﺠ' => 'ج',
+  'ﺡ' => 'ح',
+  'ﺢ' => 'ح',
+  'ﺣ' => 'ح',
+  'ﺤ' => 'ح',
+  'ﺥ' => 'خ',
+  'ﺦ' => 'خ',
+  'ﺧ' => 'خ',
+  'ﺨ' => 'خ',
+  'ﺩ' => 'د',
+  'ﺪ' => 'د',
+  'ﺫ' => 'ذ',
+  'ﺬ' => 'ذ',
+  'ﺭ' => 'ر',
+  'ﺮ' => 'ر',
+  'ﺯ' => 'ز',
+  'ﺰ' => 'ز',
+  'ﺱ' => 'س',
+  'ﺲ' => 'س',
+  'ﺳ' => 'س',
+  'ﺴ' => 'س',
+  'ﺵ' => 'ش',
+  'ﺶ' => 'ش',
+  'ﺷ' => 'ش',
+  'ﺸ' => 'ش',
+  'ﺹ' => 'ص',
+  'ﺺ' => 'ص',
+  'ﺻ' => 'ص',
+  'ﺼ' => 'ص',
+  'ﺽ' => 'ض',
+  'ﺾ' => 'ض',
+  'ﺿ' => 'ض',
+  'ﻀ' => 'ض',
+  'ﻁ' => 'ط',
+  'ﻂ' => 'ط',
+  'ﻃ' => 'ط',
+  'ﻄ' => 'ط',
+  'ﻅ' => 'ظ',
+  'ﻆ' => 'ظ',
+  'ﻇ' => 'ظ',
+  'ﻈ' => 'ظ',
+  'ﻉ' => 'ع',
+  'ﻊ' => 'ع',
+  'ﻋ' => 'ع',
+  'ﻌ' => 'ع',
+  'ﻍ' => 'غ',
+  'ﻎ' => 'غ',
+  'ﻏ' => 'غ',
+  'ﻐ' => 'غ',
+  'ﻑ' => 'ف',
+  'ﻒ' => 'ف',
+  'ﻓ' => 'ف',
+  'ﻔ' => 'ف',
+  'ﻕ' => 'ق',
+  'ﻖ' => 'ق',
+  'ﻗ' => 'ق',
+  'ﻘ' => 'ق',
+  'ﻙ' => 'ك',
+  'ﻚ' => 'ك',
+  'ﻛ' => 'ك',
+  'ﻜ' => 'ك',
+  'ﻝ' => 'ل',
+  'ﻞ' => 'ل',
+  'ﻟ' => 'ل',
+  'ﻠ' => 'ل',
+  'ﻡ' => 'م',
+  'ﻢ' => 'م',
+  'ﻣ' => 'م',
+  'ﻤ' => 'م',
+  'ﻥ' => 'ن',
+  'ﻦ' => 'ن',
+  'ﻧ' => 'ن',
+  'ﻨ' => 'ن',
+  'ﻩ' => 'ه',
+  'ﻪ' => 'ه',
+  'ﻫ' => 'ه',
+  'ﻬ' => 'ه',
+  'ﻭ' => 'و',
+  'ﻮ' => 'و',
+  'ﻯ' => 'ى',
+  'ﻰ' => 'ى',
+  'ﻱ' => 'ي',
+  'ﻲ' => 'ي',
+  'ﻳ' => 'ي',
+  'ﻴ' => 'ي',
+  'ﻵ' => 'لآ',
+  'ﻶ' => 'لآ',
+  'ﻷ' => 'لأ',
+  'ﻸ' => 'لأ',
+  'ﻹ' => 'لإ',
+  'ﻺ' => 'لإ',
+  'ﻻ' => 'لا',
+  'ﻼ' => 'لا',
+  '!' => '!',
+  '"' => '"',
+  '#' => '#',
+  '$' => '$',
+  '%' => '%',
+  '&' => '&',
+  ''' => '\'',
+  '(' => '(',
+  ')' => ')',
+  '*' => '*',
+  '+' => '+',
+  ',' => ',',
+  '-' => '-',
+  '.' => '.',
+  '/' => '/',
+  '0' => '0',
+  '1' => '1',
+  '2' => '2',
+  '3' => '3',
+  '4' => '4',
+  '5' => '5',
+  '6' => '6',
+  '7' => '7',
+  '8' => '8',
+  '9' => '9',
+  ':' => ':',
+  ';' => ';',
+  '<' => '<',
+  '=' => '=',
+  '>' => '>',
+  '?' => '?',
+  '@' => '@',
+  'A' => 'A',
+  'B' => 'B',
+  'C' => 'C',
+  'D' => 'D',
+  'E' => 'E',
+  'F' => 'F',
+  'G' => 'G',
+  'H' => 'H',
+  'I' => 'I',
+  'J' => 'J',
+  'K' => 'K',
+  'L' => 'L',
+  'M' => 'M',
+  'N' => 'N',
+  'O' => 'O',
+  'P' => 'P',
+  'Q' => 'Q',
+  'R' => 'R',
+  'S' => 'S',
+  'T' => 'T',
+  'U' => 'U',
+  'V' => 'V',
+  'W' => 'W',
+  'X' => 'X',
+  'Y' => 'Y',
+  'Z' => 'Z',
+  '[' => '[',
+  '\' => '\\',
+  ']' => ']',
+  '^' => '^',
+  '_' => '_',
+  '`' => '`',
+  'a' => 'a',
+  'b' => 'b',
+  'c' => 'c',
+  'd' => 'd',
+  'e' => 'e',
+  'f' => 'f',
+  'g' => 'g',
+  'h' => 'h',
+  'i' => 'i',
+  'j' => 'j',
+  'k' => 'k',
+  'l' => 'l',
+  'm' => 'm',
+  'n' => 'n',
+  'o' => 'o',
+  'p' => 'p',
+  'q' => 'q',
+  'r' => 'r',
+  's' => 's',
+  't' => 't',
+  'u' => 'u',
+  'v' => 'v',
+  'w' => 'w',
+  'x' => 'x',
+  'y' => 'y',
+  'z' => 'z',
+  '{' => '{',
+  '|' => '|',
+  '}' => '}',
+  '~' => '~',
+  '⦅' => '⦅',
+  '⦆' => '⦆',
+  '。' => '。',
+  '「' => '「',
+  '」' => '」',
+  '、' => '、',
+  '・' => '・',
+  'ヲ' => 'ヲ',
+  'ァ' => 'ァ',
+  'ィ' => 'ィ',
+  'ゥ' => 'ゥ',
+  'ェ' => 'ェ',
+  'ォ' => 'ォ',
+  'ャ' => 'ャ',
+  'ュ' => 'ュ',
+  'ョ' => 'ョ',
+  'ッ' => 'ッ',
+  'ー' => 'ー',
+  'ア' => 'ア',
+  'イ' => 'イ',
+  'ウ' => 'ウ',
+  'エ' => 'エ',
+  'オ' => 'オ',
+  'カ' => 'カ',
+  'キ' => 'キ',
+  'ク' => 'ク',
+  'ケ' => 'ケ',
+  'コ' => 'コ',
+  'サ' => 'サ',
+  'シ' => 'シ',
+  'ス' => 'ス',
+  'セ' => 'セ',
+  'ソ' => 'ソ',
+  'タ' => 'タ',
+  'チ' => 'チ',
+  'ツ' => 'ツ',
+  'テ' => 'テ',
+  'ト' => 'ト',
+  'ナ' => 'ナ',
+  'ニ' => 'ニ',
+  'ヌ' => 'ヌ',
+  'ネ' => 'ネ',
+  'ノ' => 'ノ',
+  'ハ' => 'ハ',
+  'ヒ' => 'ヒ',
+  'フ' => 'フ',
+  'ヘ' => 'ヘ',
+  'ホ' => 'ホ',
+  'マ' => 'マ',
+  'ミ' => 'ミ',
+  'ム' => 'ム',
+  'メ' => 'メ',
+  'モ' => 'モ',
+  'ヤ' => 'ヤ',
+  'ユ' => 'ユ',
+  'ヨ' => 'ヨ',
+  'ラ' => 'ラ',
+  'リ' => 'リ',
+  'ル' => 'ル',
+  'レ' => 'レ',
+  'ロ' => 'ロ',
+  'ワ' => 'ワ',
+  'ン' => 'ン',
+  '゙' => '゙',
+  '゚' => '゚',
+  'ᅠ' => 'ᅠ',
+  'ᄀ' => 'ᄀ',
+  'ᄁ' => 'ᄁ',
+  'ᆪ' => 'ᆪ',
+  'ᄂ' => 'ᄂ',
+  'ᆬ' => 'ᆬ',
+  'ᆭ' => 'ᆭ',
+  'ᄃ' => 'ᄃ',
+  'ᄄ' => 'ᄄ',
+  'ᄅ' => 'ᄅ',
+  'ᆰ' => 'ᆰ',
+  'ᆱ' => 'ᆱ',
+  'ᆲ' => 'ᆲ',
+  'ᆳ' => 'ᆳ',
+  'ᆴ' => 'ᆴ',
+  'ᆵ' => 'ᆵ',
+  'ᄚ' => 'ᄚ',
+  'ᄆ' => 'ᄆ',
+  'ᄇ' => 'ᄇ',
+  'ᄈ' => 'ᄈ',
+  'ᄡ' => 'ᄡ',
+  'ᄉ' => 'ᄉ',
+  'ᄊ' => 'ᄊ',
+  'ᄋ' => 'ᄋ',
+  'ᄌ' => 'ᄌ',
+  'ᄍ' => 'ᄍ',
+  'ᄎ' => 'ᄎ',
+  'ᄏ' => 'ᄏ',
+  'ᄐ' => 'ᄐ',
+  'ᄑ' => 'ᄑ',
+  'ᄒ' => 'ᄒ',
+  'ᅡ' => 'ᅡ',
+  'ᅢ' => 'ᅢ',
+  'ᅣ' => 'ᅣ',
+  'ᅤ' => 'ᅤ',
+  'ᅥ' => 'ᅥ',
+  'ᅦ' => 'ᅦ',
+  'ᅧ' => 'ᅧ',
+  'ᅨ' => 'ᅨ',
+  'ᅩ' => 'ᅩ',
+  'ᅪ' => 'ᅪ',
+  'ᅫ' => 'ᅫ',
+  'ᅬ' => 'ᅬ',
+  'ᅭ' => 'ᅭ',
+  'ᅮ' => 'ᅮ',
+  'ᅯ' => 'ᅯ',
+  'ᅰ' => 'ᅰ',
+  'ᅱ' => 'ᅱ',
+  'ᅲ' => 'ᅲ',
+  'ᅳ' => 'ᅳ',
+  'ᅴ' => 'ᅴ',
+  'ᅵ' => 'ᅵ',
+  '¢' => '¢',
+  '£' => '£',
+  '¬' => '¬',
+  ' ̄' => ' ̄',
+  '¦' => '¦',
+  '¥' => '¥',
+  '₩' => '₩',
+  '│' => '│',
+  '←' => '←',
+  '↑' => '↑',
+  '→' => '→',
+  '↓' => '↓',
+  '■' => '■',
+  '○' => '○',
+  '𝐀' => 'A',
+  '𝐁' => 'B',
+  '𝐂' => 'C',
+  '𝐃' => 'D',
+  '𝐄' => 'E',
+  '𝐅' => 'F',
+  '𝐆' => 'G',
+  '𝐇' => 'H',
+  '𝐈' => 'I',
+  '𝐉' => 'J',
+  '𝐊' => 'K',
+  '𝐋' => 'L',
+  '𝐌' => 'M',
+  '𝐍' => 'N',
+  '𝐎' => 'O',
+  '𝐏' => 'P',
+  '𝐐' => 'Q',
+  '𝐑' => 'R',
+  '𝐒' => 'S',
+  '𝐓' => 'T',
+  '𝐔' => 'U',
+  '𝐕' => 'V',
+  '𝐖' => 'W',
+  '𝐗' => 'X',
+  '𝐘' => 'Y',
+  '𝐙' => 'Z',
+  '𝐚' => 'a',
+  '𝐛' => 'b',
+  '𝐜' => 'c',
+  '𝐝' => 'd',
+  '𝐞' => 'e',
+  '𝐟' => 'f',
+  '𝐠' => 'g',
+  '𝐡' => 'h',
+  '𝐢' => 'i',
+  '𝐣' => 'j',
+  '𝐤' => 'k',
+  '𝐥' => 'l',
+  '𝐦' => 'm',
+  '𝐧' => 'n',
+  '𝐨' => 'o',
+  '𝐩' => 'p',
+  '𝐪' => 'q',
+  '𝐫' => 'r',
+  '𝐬' => 's',
+  '𝐭' => 't',
+  '𝐮' => 'u',
+  '𝐯' => 'v',
+  '𝐰' => 'w',
+  '𝐱' => 'x',
+  '𝐲' => 'y',
+  '𝐳' => 'z',
+  '𝐴' => 'A',
+  '𝐵' => 'B',
+  '𝐶' => 'C',
+  '𝐷' => 'D',
+  '𝐸' => 'E',
+  '𝐹' => 'F',
+  '𝐺' => 'G',
+  '𝐻' => 'H',
+  '𝐼' => 'I',
+  '𝐽' => 'J',
+  '𝐾' => 'K',
+  '𝐿' => 'L',
+  '𝑀' => 'M',
+  '𝑁' => 'N',
+  '𝑂' => 'O',
+  '𝑃' => 'P',
+  '𝑄' => 'Q',
+  '𝑅' => 'R',
+  '𝑆' => 'S',
+  '𝑇' => 'T',
+  '𝑈' => 'U',
+  '𝑉' => 'V',
+  '𝑊' => 'W',
+  '𝑋' => 'X',
+  '𝑌' => 'Y',
+  '𝑍' => 'Z',
+  '𝑎' => 'a',
+  '𝑏' => 'b',
+  '𝑐' => 'c',
+  '𝑑' => 'd',
+  '𝑒' => 'e',
+  '𝑓' => 'f',
+  '𝑔' => 'g',
+  '𝑖' => 'i',
+  '𝑗' => 'j',
+  '𝑘' => 'k',
+  '𝑙' => 'l',
+  '𝑚' => 'm',
+  '𝑛' => 'n',
+  '𝑜' => 'o',
+  '𝑝' => 'p',
+  '𝑞' => 'q',
+  '𝑟' => 'r',
+  '𝑠' => 's',
+  '𝑡' => 't',
+  '𝑢' => 'u',
+  '𝑣' => 'v',
+  '𝑤' => 'w',
+  '𝑥' => 'x',
+  '𝑦' => 'y',
+  '𝑧' => 'z',
+  '𝑨' => 'A',
+  '𝑩' => 'B',
+  '𝑪' => 'C',
+  '𝑫' => 'D',
+  '𝑬' => 'E',
+  '𝑭' => 'F',
+  '𝑮' => 'G',
+  '𝑯' => 'H',
+  '𝑰' => 'I',
+  '𝑱' => 'J',
+  '𝑲' => 'K',
+  '𝑳' => 'L',
+  '𝑴' => 'M',
+  '𝑵' => 'N',
+  '𝑶' => 'O',
+  '𝑷' => 'P',
+  '𝑸' => 'Q',
+  '𝑹' => 'R',
+  '𝑺' => 'S',
+  '𝑻' => 'T',
+  '𝑼' => 'U',
+  '𝑽' => 'V',
+  '𝑾' => 'W',
+  '𝑿' => 'X',
+  '𝒀' => 'Y',
+  '𝒁' => 'Z',
+  '𝒂' => 'a',
+  '𝒃' => 'b',
+  '𝒄' => 'c',
+  '𝒅' => 'd',
+  '𝒆' => 'e',
+  '𝒇' => 'f',
+  '𝒈' => 'g',
+  '𝒉' => 'h',
+  '𝒊' => 'i',
+  '𝒋' => 'j',
+  '𝒌' => 'k',
+  '𝒍' => 'l',
+  '𝒎' => 'm',
+  '𝒏' => 'n',
+  '𝒐' => 'o',
+  '𝒑' => 'p',
+  '𝒒' => 'q',
+  '𝒓' => 'r',
+  '𝒔' => 's',
+  '𝒕' => 't',
+  '𝒖' => 'u',
+  '𝒗' => 'v',
+  '𝒘' => 'w',
+  '𝒙' => 'x',
+  '𝒚' => 'y',
+  '𝒛' => 'z',
+  '𝒜' => 'A',
+  '𝒞' => 'C',
+  '𝒟' => 'D',
+  '𝒢' => 'G',
+  '𝒥' => 'J',
+  '𝒦' => 'K',
+  '𝒩' => 'N',
+  '𝒪' => 'O',
+  '𝒫' => 'P',
+  '𝒬' => 'Q',
+  '𝒮' => 'S',
+  '𝒯' => 'T',
+  '𝒰' => 'U',
+  '𝒱' => 'V',
+  '𝒲' => 'W',
+  '𝒳' => 'X',
+  '𝒴' => 'Y',
+  '𝒵' => 'Z',
+  '𝒶' => 'a',
+  '𝒷' => 'b',
+  '𝒸' => 'c',
+  '𝒹' => 'd',
+  '𝒻' => 'f',
+  '𝒽' => 'h',
+  '𝒾' => 'i',
+  '𝒿' => 'j',
+  '𝓀' => 'k',
+  '𝓁' => 'l',
+  '𝓂' => 'm',
+  '𝓃' => 'n',
+  '𝓅' => 'p',
+  '𝓆' => 'q',
+  '𝓇' => 'r',
+  '𝓈' => 's',
+  '𝓉' => 't',
+  '𝓊' => 'u',
+  '𝓋' => 'v',
+  '𝓌' => 'w',
+  '𝓍' => 'x',
+  '𝓎' => 'y',
+  '𝓏' => 'z',
+  '𝓐' => 'A',
+  '𝓑' => 'B',
+  '𝓒' => 'C',
+  '𝓓' => 'D',
+  '𝓔' => 'E',
+  '𝓕' => 'F',
+  '𝓖' => 'G',
+  '𝓗' => 'H',
+  '𝓘' => 'I',
+  '𝓙' => 'J',
+  '𝓚' => 'K',
+  '𝓛' => 'L',
+  '𝓜' => 'M',
+  '𝓝' => 'N',
+  '𝓞' => 'O',
+  '𝓟' => 'P',
+  '𝓠' => 'Q',
+  '𝓡' => 'R',
+  '𝓢' => 'S',
+  '𝓣' => 'T',
+  '𝓤' => 'U',
+  '𝓥' => 'V',
+  '𝓦' => 'W',
+  '𝓧' => 'X',
+  '𝓨' => 'Y',
+  '𝓩' => 'Z',
+  '𝓪' => 'a',
+  '𝓫' => 'b',
+  '𝓬' => 'c',
+  '𝓭' => 'd',
+  '𝓮' => 'e',
+  '𝓯' => 'f',
+  '𝓰' => 'g',
+  '𝓱' => 'h',
+  '𝓲' => 'i',
+  '𝓳' => 'j',
+  '𝓴' => 'k',
+  '𝓵' => 'l',
+  '𝓶' => 'm',
+  '𝓷' => 'n',
+  '𝓸' => 'o',
+  '𝓹' => 'p',
+  '𝓺' => 'q',
+  '𝓻' => 'r',
+  '𝓼' => 's',
+  '𝓽' => 't',
+  '𝓾' => 'u',
+  '𝓿' => 'v',
+  '𝔀' => 'w',
+  '𝔁' => 'x',
+  '𝔂' => 'y',
+  '𝔃' => 'z',
+  '𝔄' => 'A',
+  '𝔅' => 'B',
+  '𝔇' => 'D',
+  '𝔈' => 'E',
+  '𝔉' => 'F',
+  '𝔊' => 'G',
+  '𝔍' => 'J',
+  '𝔎' => 'K',
+  '𝔏' => 'L',
+  '𝔐' => 'M',
+  '𝔑' => 'N',
+  '𝔒' => 'O',
+  '𝔓' => 'P',
+  '𝔔' => 'Q',
+  '𝔖' => 'S',
+  '𝔗' => 'T',
+  '𝔘' => 'U',
+  '𝔙' => 'V',
+  '𝔚' => 'W',
+  '𝔛' => 'X',
+  '𝔜' => 'Y',
+  '𝔞' => 'a',
+  '𝔟' => 'b',
+  '𝔠' => 'c',
+  '𝔡' => 'd',
+  '𝔢' => 'e',
+  '𝔣' => 'f',
+  '𝔤' => 'g',
+  '𝔥' => 'h',
+  '𝔦' => 'i',
+  '𝔧' => 'j',
+  '𝔨' => 'k',
+  '𝔩' => 'l',
+  '𝔪' => 'm',
+  '𝔫' => 'n',
+  '𝔬' => 'o',
+  '𝔭' => 'p',
+  '𝔮' => 'q',
+  '𝔯' => 'r',
+  '𝔰' => 's',
+  '𝔱' => 't',
+  '𝔲' => 'u',
+  '𝔳' => 'v',
+  '𝔴' => 'w',
+  '𝔵' => 'x',
+  '𝔶' => 'y',
+  '𝔷' => 'z',
+  '𝔸' => 'A',
+  '𝔹' => 'B',
+  '𝔻' => 'D',
+  '𝔼' => 'E',
+  '𝔽' => 'F',
+  '𝔾' => 'G',
+  '𝕀' => 'I',
+  '𝕁' => 'J',
+  '𝕂' => 'K',
+  '𝕃' => 'L',
+  '𝕄' => 'M',
+  '𝕆' => 'O',
+  '𝕊' => 'S',
+  '𝕋' => 'T',
+  '𝕌' => 'U',
+  '𝕍' => 'V',
+  '𝕎' => 'W',
+  '𝕏' => 'X',
+  '𝕐' => 'Y',
+  '𝕒' => 'a',
+  '𝕓' => 'b',
+  '𝕔' => 'c',
+  '𝕕' => 'd',
+  '𝕖' => 'e',
+  '𝕗' => 'f',
+  '𝕘' => 'g',
+  '𝕙' => 'h',
+  '𝕚' => 'i',
+  '𝕛' => 'j',
+  '𝕜' => 'k',
+  '𝕝' => 'l',
+  '𝕞' => 'm',
+  '𝕟' => 'n',
+  '𝕠' => 'o',
+  '𝕡' => 'p',
+  '𝕢' => 'q',
+  '𝕣' => 'r',
+  '𝕤' => 's',
+  '𝕥' => 't',
+  '𝕦' => 'u',
+  '𝕧' => 'v',
+  '𝕨' => 'w',
+  '𝕩' => 'x',
+  '𝕪' => 'y',
+  '𝕫' => 'z',
+  '𝕬' => 'A',
+  '𝕭' => 'B',
+  '𝕮' => 'C',
+  '𝕯' => 'D',
+  '𝕰' => 'E',
+  '𝕱' => 'F',
+  '𝕲' => 'G',
+  '𝕳' => 'H',
+  '𝕴' => 'I',
+  '𝕵' => 'J',
+  '𝕶' => 'K',
+  '𝕷' => 'L',
+  '𝕸' => 'M',
+  '𝕹' => 'N',
+  '𝕺' => 'O',
+  '𝕻' => 'P',
+  '𝕼' => 'Q',
+  '𝕽' => 'R',
+  '𝕾' => 'S',
+  '𝕿' => 'T',
+  '𝖀' => 'U',
+  '𝖁' => 'V',
+  '𝖂' => 'W',
+  '𝖃' => 'X',
+  '𝖄' => 'Y',
+  '𝖅' => 'Z',
+  '𝖆' => 'a',
+  '𝖇' => 'b',
+  '𝖈' => 'c',
+  '𝖉' => 'd',
+  '𝖊' => 'e',
+  '𝖋' => 'f',
+  '𝖌' => 'g',
+  '𝖍' => 'h',
+  '𝖎' => 'i',
+  '𝖏' => 'j',
+  '𝖐' => 'k',
+  '𝖑' => 'l',
+  '𝖒' => 'm',
+  '𝖓' => 'n',
+  '𝖔' => 'o',
+  '𝖕' => 'p',
+  '𝖖' => 'q',
+  '𝖗' => 'r',
+  '𝖘' => 's',
+  '𝖙' => 't',
+  '𝖚' => 'u',
+  '𝖛' => 'v',
+  '𝖜' => 'w',
+  '𝖝' => 'x',
+  '𝖞' => 'y',
+  '𝖟' => 'z',
+  '𝖠' => 'A',
+  '𝖡' => 'B',
+  '𝖢' => 'C',
+  '𝖣' => 'D',
+  '𝖤' => 'E',
+  '𝖥' => 'F',
+  '𝖦' => 'G',
+  '𝖧' => 'H',
+  '𝖨' => 'I',
+  '𝖩' => 'J',
+  '𝖪' => 'K',
+  '𝖫' => 'L',
+  '𝖬' => 'M',
+  '𝖭' => 'N',
+  '𝖮' => 'O',
+  '𝖯' => 'P',
+  '𝖰' => 'Q',
+  '𝖱' => 'R',
+  '𝖲' => 'S',
+  '𝖳' => 'T',
+  '𝖴' => 'U',
+  '𝖵' => 'V',
+  '𝖶' => 'W',
+  '𝖷' => 'X',
+  '𝖸' => 'Y',
+  '𝖹' => 'Z',
+  '𝖺' => 'a',
+  '𝖻' => 'b',
+  '𝖼' => 'c',
+  '𝖽' => 'd',
+  '𝖾' => 'e',
+  '𝖿' => 'f',
+  '𝗀' => 'g',
+  '𝗁' => 'h',
+  '𝗂' => 'i',
+  '𝗃' => 'j',
+  '𝗄' => 'k',
+  '𝗅' => 'l',
+  '𝗆' => 'm',
+  '𝗇' => 'n',
+  '𝗈' => 'o',
+  '𝗉' => 'p',
+  '𝗊' => 'q',
+  '𝗋' => 'r',
+  '𝗌' => 's',
+  '𝗍' => 't',
+  '𝗎' => 'u',
+  '𝗏' => 'v',
+  '𝗐' => 'w',
+  '𝗑' => 'x',
+  '𝗒' => 'y',
+  '𝗓' => 'z',
+  '𝗔' => 'A',
+  '𝗕' => 'B',
+  '𝗖' => 'C',
+  '𝗗' => 'D',
+  '𝗘' => 'E',
+  '𝗙' => 'F',
+  '𝗚' => 'G',
+  '𝗛' => 'H',
+  '𝗜' => 'I',
+  '𝗝' => 'J',
+  '𝗞' => 'K',
+  '𝗟' => 'L',
+  '𝗠' => 'M',
+  '𝗡' => 'N',
+  '𝗢' => 'O',
+  '𝗣' => 'P',
+  '𝗤' => 'Q',
+  '𝗥' => 'R',
+  '𝗦' => 'S',
+  '𝗧' => 'T',
+  '𝗨' => 'U',
+  '𝗩' => 'V',
+  '𝗪' => 'W',
+  '𝗫' => 'X',
+  '𝗬' => 'Y',
+  '𝗭' => 'Z',
+  '𝗮' => 'a',
+  '𝗯' => 'b',
+  '𝗰' => 'c',
+  '𝗱' => 'd',
+  '𝗲' => 'e',
+  '𝗳' => 'f',
+  '𝗴' => 'g',
+  '𝗵' => 'h',
+  '𝗶' => 'i',
+  '𝗷' => 'j',
+  '𝗸' => 'k',
+  '𝗹' => 'l',
+  '𝗺' => 'm',
+  '𝗻' => 'n',
+  '𝗼' => 'o',
+  '𝗽' => 'p',
+  '𝗾' => 'q',
+  '𝗿' => 'r',
+  '𝘀' => 's',
+  '𝘁' => 't',
+  '𝘂' => 'u',
+  '𝘃' => 'v',
+  '𝘄' => 'w',
+  '𝘅' => 'x',
+  '𝘆' => 'y',
+  '𝘇' => 'z',
+  '𝘈' => 'A',
+  '𝘉' => 'B',
+  '𝘊' => 'C',
+  '𝘋' => 'D',
+  '𝘌' => 'E',
+  '𝘍' => 'F',
+  '𝘎' => 'G',
+  '𝘏' => 'H',
+  '𝘐' => 'I',
+  '𝘑' => 'J',
+  '𝘒' => 'K',
+  '𝘓' => 'L',
+  '𝘔' => 'M',
+  '𝘕' => 'N',
+  '𝘖' => 'O',
+  '𝘗' => 'P',
+  '𝘘' => 'Q',
+  '𝘙' => 'R',
+  '𝘚' => 'S',
+  '𝘛' => 'T',
+  '𝘜' => 'U',
+  '𝘝' => 'V',
+  '𝘞' => 'W',
+  '𝘟' => 'X',
+  '𝘠' => 'Y',
+  '𝘡' => 'Z',
+  '𝘢' => 'a',
+  '𝘣' => 'b',
+  '𝘤' => 'c',
+  '𝘥' => 'd',
+  '𝘦' => 'e',
+  '𝘧' => 'f',
+  '𝘨' => 'g',
+  '𝘩' => 'h',
+  '𝘪' => 'i',
+  '𝘫' => 'j',
+  '𝘬' => 'k',
+  '𝘭' => 'l',
+  '𝘮' => 'm',
+  '𝘯' => 'n',
+  '𝘰' => 'o',
+  '𝘱' => 'p',
+  '𝘲' => 'q',
+  '𝘳' => 'r',
+  '𝘴' => 's',
+  '𝘵' => 't',
+  '𝘶' => 'u',
+  '𝘷' => 'v',
+  '𝘸' => 'w',
+  '𝘹' => 'x',
+  '𝘺' => 'y',
+  '𝘻' => 'z',
+  '𝘼' => 'A',
+  '𝘽' => 'B',
+  '𝘾' => 'C',
+  '𝘿' => 'D',
+  '𝙀' => 'E',
+  '𝙁' => 'F',
+  '𝙂' => 'G',
+  '𝙃' => 'H',
+  '𝙄' => 'I',
+  '𝙅' => 'J',
+  '𝙆' => 'K',
+  '𝙇' => 'L',
+  '𝙈' => 'M',
+  '𝙉' => 'N',
+  '𝙊' => 'O',
+  '𝙋' => 'P',
+  '𝙌' => 'Q',
+  '𝙍' => 'R',
+  '𝙎' => 'S',
+  '𝙏' => 'T',
+  '𝙐' => 'U',
+  '𝙑' => 'V',
+  '𝙒' => 'W',
+  '𝙓' => 'X',
+  '𝙔' => 'Y',
+  '𝙕' => 'Z',
+  '𝙖' => 'a',
+  '𝙗' => 'b',
+  '𝙘' => 'c',
+  '𝙙' => 'd',
+  '𝙚' => 'e',
+  '𝙛' => 'f',
+  '𝙜' => 'g',
+  '𝙝' => 'h',
+  '𝙞' => 'i',
+  '𝙟' => 'j',
+  '𝙠' => 'k',
+  '𝙡' => 'l',
+  '𝙢' => 'm',
+  '𝙣' => 'n',
+  '𝙤' => 'o',
+  '𝙥' => 'p',
+  '𝙦' => 'q',
+  '𝙧' => 'r',
+  '𝙨' => 's',
+  '𝙩' => 't',
+  '𝙪' => 'u',
+  '𝙫' => 'v',
+  '𝙬' => 'w',
+  '𝙭' => 'x',
+  '𝙮' => 'y',
+  '𝙯' => 'z',
+  '𝙰' => 'A',
+  '𝙱' => 'B',
+  '𝙲' => 'C',
+  '𝙳' => 'D',
+  '𝙴' => 'E',
+  '𝙵' => 'F',
+  '𝙶' => 'G',
+  '𝙷' => 'H',
+  '𝙸' => 'I',
+  '𝙹' => 'J',
+  '𝙺' => 'K',
+  '𝙻' => 'L',
+  '𝙼' => 'M',
+  '𝙽' => 'N',
+  '𝙾' => 'O',
+  '𝙿' => 'P',
+  '𝚀' => 'Q',
+  '𝚁' => 'R',
+  '𝚂' => 'S',
+  '𝚃' => 'T',
+  '𝚄' => 'U',
+  '𝚅' => 'V',
+  '𝚆' => 'W',
+  '𝚇' => 'X',
+  '𝚈' => 'Y',
+  '𝚉' => 'Z',
+  '𝚊' => 'a',
+  '𝚋' => 'b',
+  '𝚌' => 'c',
+  '𝚍' => 'd',
+  '𝚎' => 'e',
+  '𝚏' => 'f',
+  '𝚐' => 'g',
+  '𝚑' => 'h',
+  '𝚒' => 'i',
+  '𝚓' => 'j',
+  '𝚔' => 'k',
+  '𝚕' => 'l',
+  '𝚖' => 'm',
+  '𝚗' => 'n',
+  '𝚘' => 'o',
+  '𝚙' => 'p',
+  '𝚚' => 'q',
+  '𝚛' => 'r',
+  '𝚜' => 's',
+  '𝚝' => 't',
+  '𝚞' => 'u',
+  '𝚟' => 'v',
+  '𝚠' => 'w',
+  '𝚡' => 'x',
+  '𝚢' => 'y',
+  '𝚣' => 'z',
+  '𝚤' => 'ı',
+  '𝚥' => 'ȷ',
+  '𝚨' => 'Α',
+  '𝚩' => 'Β',
+  '𝚪' => 'Γ',
+  '𝚫' => 'Δ',
+  '𝚬' => 'Ε',
+  '𝚭' => 'Ζ',
+  '𝚮' => 'Η',
+  '𝚯' => 'Θ',
+  '𝚰' => 'Ι',
+  '𝚱' => 'Κ',
+  '𝚲' => 'Λ',
+  '𝚳' => 'Μ',
+  '𝚴' => 'Ν',
+  '𝚵' => 'Ξ',
+  '𝚶' => 'Ο',
+  '𝚷' => 'Π',
+  '𝚸' => 'Ρ',
+  '𝚹' => 'Θ',
+  '𝚺' => 'Σ',
+  '𝚻' => 'Τ',
+  '𝚼' => 'Υ',
+  '𝚽' => 'Φ',
+  '𝚾' => 'Χ',
+  '𝚿' => 'Ψ',
+  '𝛀' => 'Ω',
+  '𝛁' => '∇',
+  '𝛂' => 'α',
+  '𝛃' => 'β',
+  '𝛄' => 'γ',
+  '𝛅' => 'δ',
+  '𝛆' => 'ε',
+  '𝛇' => 'ζ',
+  '𝛈' => 'η',
+  '𝛉' => 'θ',
+  '𝛊' => 'ι',
+  '𝛋' => 'κ',
+  '𝛌' => 'λ',
+  '𝛍' => 'μ',
+  '𝛎' => 'ν',
+  '𝛏' => 'ξ',
+  '𝛐' => 'ο',
+  '𝛑' => 'π',
+  '𝛒' => 'ρ',
+  '𝛓' => 'ς',
+  '𝛔' => 'σ',
+  '𝛕' => 'τ',
+  '𝛖' => 'υ',
+  '𝛗' => 'φ',
+  '𝛘' => 'χ',
+  '𝛙' => 'ψ',
+  '𝛚' => 'ω',
+  '𝛛' => '∂',
+  '𝛜' => 'ε',
+  '𝛝' => 'θ',
+  '𝛞' => 'κ',
+  '𝛟' => 'φ',
+  '𝛠' => 'ρ',
+  '𝛡' => 'π',
+  '𝛢' => 'Α',
+  '𝛣' => 'Β',
+  '𝛤' => 'Γ',
+  '𝛥' => 'Δ',
+  '𝛦' => 'Ε',
+  '𝛧' => 'Ζ',
+  '𝛨' => 'Η',
+  '𝛩' => 'Θ',
+  '𝛪' => 'Ι',
+  '𝛫' => 'Κ',
+  '𝛬' => 'Λ',
+  '𝛭' => 'Μ',
+  '𝛮' => 'Ν',
+  '𝛯' => 'Ξ',
+  '𝛰' => 'Ο',
+  '𝛱' => 'Π',
+  '𝛲' => 'Ρ',
+  '𝛳' => 'Θ',
+  '𝛴' => 'Σ',
+  '𝛵' => 'Τ',
+  '𝛶' => 'Υ',
+  '𝛷' => 'Φ',
+  '𝛸' => 'Χ',
+  '𝛹' => 'Ψ',
+  '𝛺' => 'Ω',
+  '𝛻' => '∇',
+  '𝛼' => 'α',
+  '𝛽' => 'β',
+  '𝛾' => 'γ',
+  '𝛿' => 'δ',
+  '𝜀' => 'ε',
+  '𝜁' => 'ζ',
+  '𝜂' => 'η',
+  '𝜃' => 'θ',
+  '𝜄' => 'ι',
+  '𝜅' => 'κ',
+  '𝜆' => 'λ',
+  '𝜇' => 'μ',
+  '𝜈' => 'ν',
+  '𝜉' => 'ξ',
+  '𝜊' => 'ο',
+  '𝜋' => 'π',
+  '𝜌' => 'ρ',
+  '𝜍' => 'ς',
+  '𝜎' => 'σ',
+  '𝜏' => 'τ',
+  '𝜐' => 'υ',
+  '𝜑' => 'φ',
+  '𝜒' => 'χ',
+  '𝜓' => 'ψ',
+  '𝜔' => 'ω',
+  '𝜕' => '∂',
+  '𝜖' => 'ε',
+  '𝜗' => 'θ',
+  '𝜘' => 'κ',
+  '𝜙' => 'φ',
+  '𝜚' => 'ρ',
+  '𝜛' => 'π',
+  '𝜜' => 'Α',
+  '𝜝' => 'Β',
+  '𝜞' => 'Γ',
+  '𝜟' => 'Δ',
+  '𝜠' => 'Ε',
+  '𝜡' => 'Ζ',
+  '𝜢' => 'Η',
+  '𝜣' => 'Θ',
+  '𝜤' => 'Ι',
+  '𝜥' => 'Κ',
+  '𝜦' => 'Λ',
+  '𝜧' => 'Μ',
+  '𝜨' => 'Ν',
+  '𝜩' => 'Ξ',
+  '𝜪' => 'Ο',
+  '𝜫' => 'Π',
+  '𝜬' => 'Ρ',
+  '𝜭' => 'Θ',
+  '𝜮' => 'Σ',
+  '𝜯' => 'Τ',
+  '𝜰' => 'Υ',
+  '𝜱' => 'Φ',
+  '𝜲' => 'Χ',
+  '𝜳' => 'Ψ',
+  '𝜴' => 'Ω',
+  '𝜵' => '∇',
+  '𝜶' => 'α',
+  '𝜷' => 'β',
+  '𝜸' => 'γ',
+  '𝜹' => 'δ',
+  '𝜺' => 'ε',
+  '𝜻' => 'ζ',
+  '𝜼' => 'η',
+  '𝜽' => 'θ',
+  '𝜾' => 'ι',
+  '𝜿' => 'κ',
+  '𝝀' => 'λ',
+  '𝝁' => 'μ',
+  '𝝂' => 'ν',
+  '𝝃' => 'ξ',
+  '𝝄' => 'ο',
+  '𝝅' => 'π',
+  '𝝆' => 'ρ',
+  '𝝇' => 'ς',
+  '𝝈' => 'σ',
+  '𝝉' => 'τ',
+  '𝝊' => 'υ',
+  '𝝋' => 'φ',
+  '𝝌' => 'χ',
+  '𝝍' => 'ψ',
+  '𝝎' => 'ω',
+  '𝝏' => '∂',
+  '𝝐' => 'ε',
+  '𝝑' => 'θ',
+  '𝝒' => 'κ',
+  '𝝓' => 'φ',
+  '𝝔' => 'ρ',
+  '𝝕' => 'π',
+  '𝝖' => 'Α',
+  '𝝗' => 'Β',
+  '𝝘' => 'Γ',
+  '𝝙' => 'Δ',
+  '𝝚' => 'Ε',
+  '𝝛' => 'Ζ',
+  '𝝜' => 'Η',
+  '𝝝' => 'Θ',
+  '𝝞' => 'Ι',
+  '𝝟' => 'Κ',
+  '𝝠' => 'Λ',
+  '𝝡' => 'Μ',
+  '𝝢' => 'Ν',
+  '𝝣' => 'Ξ',
+  '𝝤' => 'Ο',
+  '𝝥' => 'Π',
+  '𝝦' => 'Ρ',
+  '𝝧' => 'Θ',
+  '𝝨' => 'Σ',
+  '𝝩' => 'Τ',
+  '𝝪' => 'Υ',
+  '𝝫' => 'Φ',
+  '𝝬' => 'Χ',
+  '𝝭' => 'Ψ',
+  '𝝮' => 'Ω',
+  '𝝯' => '∇',
+  '𝝰' => 'α',
+  '𝝱' => 'β',
+  '𝝲' => 'γ',
+  '𝝳' => 'δ',
+  '𝝴' => 'ε',
+  '𝝵' => 'ζ',
+  '𝝶' => 'η',
+  '𝝷' => 'θ',
+  '𝝸' => 'ι',
+  '𝝹' => 'κ',
+  '𝝺' => 'λ',
+  '𝝻' => 'μ',
+  '𝝼' => 'ν',
+  '𝝽' => 'ξ',
+  '𝝾' => 'ο',
+  '𝝿' => 'π',
+  '𝞀' => 'ρ',
+  '𝞁' => 'ς',
+  '𝞂' => 'σ',
+  '𝞃' => 'τ',
+  '𝞄' => 'υ',
+  '𝞅' => 'φ',
+  '𝞆' => 'χ',
+  '𝞇' => 'ψ',
+  '𝞈' => 'ω',
+  '𝞉' => '∂',
+  '𝞊' => 'ε',
+  '𝞋' => 'θ',
+  '𝞌' => 'κ',
+  '𝞍' => 'φ',
+  '𝞎' => 'ρ',
+  '𝞏' => 'π',
+  '𝞐' => 'Α',
+  '𝞑' => 'Β',
+  '𝞒' => 'Γ',
+  '𝞓' => 'Δ',
+  '𝞔' => 'Ε',
+  '𝞕' => 'Ζ',
+  '𝞖' => 'Η',
+  '𝞗' => 'Θ',
+  '𝞘' => 'Ι',
+  '𝞙' => 'Κ',
+  '𝞚' => 'Λ',
+  '𝞛' => 'Μ',
+  '𝞜' => 'Ν',
+  '𝞝' => 'Ξ',
+  '𝞞' => 'Ο',
+  '𝞟' => 'Π',
+  '𝞠' => 'Ρ',
+  '𝞡' => 'Θ',
+  '𝞢' => 'Σ',
+  '𝞣' => 'Τ',
+  '𝞤' => 'Υ',
+  '𝞥' => 'Φ',
+  '𝞦' => 'Χ',
+  '𝞧' => 'Ψ',
+  '𝞨' => 'Ω',
+  '𝞩' => '∇',
+  '𝞪' => 'α',
+  '𝞫' => 'β',
+  '𝞬' => 'γ',
+  '𝞭' => 'δ',
+  '𝞮' => 'ε',
+  '𝞯' => 'ζ',
+  '𝞰' => 'η',
+  '𝞱' => 'θ',
+  '𝞲' => 'ι',
+  '𝞳' => 'κ',
+  '𝞴' => 'λ',
+  '𝞵' => 'μ',
+  '𝞶' => 'ν',
+  '𝞷' => 'ξ',
+  '𝞸' => 'ο',
+  '𝞹' => 'π',
+  '𝞺' => 'ρ',
+  '𝞻' => 'ς',
+  '𝞼' => 'σ',
+  '𝞽' => 'τ',
+  '𝞾' => 'υ',
+  '𝞿' => 'φ',
+  '𝟀' => 'χ',
+  '𝟁' => 'ψ',
+  '𝟂' => 'ω',
+  '𝟃' => '∂',
+  '𝟄' => 'ε',
+  '𝟅' => 'θ',
+  '𝟆' => 'κ',
+  '𝟇' => 'φ',
+  '𝟈' => 'ρ',
+  '𝟉' => 'π',
+  '𝟊' => 'Ϝ',
+  '𝟋' => 'ϝ',
+  '𝟎' => '0',
+  '𝟏' => '1',
+  '𝟐' => '2',
+  '𝟑' => '3',
+  '𝟒' => '4',
+  '𝟓' => '5',
+  '𝟔' => '6',
+  '𝟕' => '7',
+  '𝟖' => '8',
+  '𝟗' => '9',
+  '𝟘' => '0',
+  '𝟙' => '1',
+  '𝟚' => '2',
+  '𝟛' => '3',
+  '𝟜' => '4',
+  '𝟝' => '5',
+  '𝟞' => '6',
+  '𝟟' => '7',
+  '𝟠' => '8',
+  '𝟡' => '9',
+  '𝟢' => '0',
+  '𝟣' => '1',
+  '𝟤' => '2',
+  '𝟥' => '3',
+  '𝟦' => '4',
+  '𝟧' => '5',
+  '𝟨' => '6',
+  '𝟩' => '7',
+  '𝟪' => '8',
+  '𝟫' => '9',
+  '𝟬' => '0',
+  '𝟭' => '1',
+  '𝟮' => '2',
+  '𝟯' => '3',
+  '𝟰' => '4',
+  '𝟱' => '5',
+  '𝟲' => '6',
+  '𝟳' => '7',
+  '𝟴' => '8',
+  '𝟵' => '9',
+  '𝟶' => '0',
+  '𝟷' => '1',
+  '𝟸' => '2',
+  '𝟹' => '3',
+  '𝟺' => '4',
+  '𝟻' => '5',
+  '𝟼' => '6',
+  '𝟽' => '7',
+  '𝟾' => '8',
+  '𝟿' => '9',
+  '𞸀' => 'ا',
+  '𞸁' => 'ب',
+  '𞸂' => 'ج',
+  '𞸃' => 'د',
+  '𞸅' => 'و',
+  '𞸆' => 'ز',
+  '𞸇' => 'ح',
+  '𞸈' => 'ط',
+  '𞸉' => 'ي',
+  '𞸊' => 'ك',
+  '𞸋' => 'ل',
+  '𞸌' => 'م',
+  '𞸍' => 'ن',
+  '𞸎' => 'س',
+  '𞸏' => 'ع',
+  '𞸐' => 'ف',
+  '𞸑' => 'ص',
+  '𞸒' => 'ق',
+  '𞸓' => 'ر',
+  '𞸔' => 'ش',
+  '𞸕' => 'ت',
+  '𞸖' => 'ث',
+  '𞸗' => 'خ',
+  '𞸘' => 'ذ',
+  '𞸙' => 'ض',
+  '𞸚' => 'ظ',
+  '𞸛' => 'غ',
+  '𞸜' => 'ٮ',
+  '𞸝' => 'ں',
+  '𞸞' => 'ڡ',
+  '𞸟' => 'ٯ',
+  '𞸡' => 'ب',
+  '𞸢' => 'ج',
+  '𞸤' => 'ه',
+  '𞸧' => 'ح',
+  '𞸩' => 'ي',
+  '𞸪' => 'ك',
+  '𞸫' => 'ل',
+  '𞸬' => 'م',
+  '𞸭' => 'ن',
+  '𞸮' => 'س',
+  '𞸯' => 'ع',
+  '𞸰' => 'ف',
+  '𞸱' => 'ص',
+  '𞸲' => 'ق',
+  '𞸴' => 'ش',
+  '𞸵' => 'ت',
+  '𞸶' => 'ث',
+  '𞸷' => 'خ',
+  '𞸹' => 'ض',
+  '𞸻' => 'غ',
+  '𞹂' => 'ج',
+  '𞹇' => 'ح',
+  '𞹉' => 'ي',
+  '𞹋' => 'ل',
+  '𞹍' => 'ن',
+  '𞹎' => 'س',
+  '𞹏' => 'ع',
+  '𞹑' => 'ص',
+  '𞹒' => 'ق',
+  '𞹔' => 'ش',
+  '𞹗' => 'خ',
+  '𞹙' => 'ض',
+  '𞹛' => 'غ',
+  '𞹝' => 'ں',
+  '𞹟' => 'ٯ',
+  '𞹡' => 'ب',
+  '𞹢' => 'ج',
+  '𞹤' => 'ه',
+  '𞹧' => 'ح',
+  '𞹨' => 'ط',
+  '𞹩' => 'ي',
+  '𞹪' => 'ك',
+  '𞹬' => 'م',
+  '𞹭' => 'ن',
+  '𞹮' => 'س',
+  '𞹯' => 'ع',
+  '𞹰' => 'ف',
+  '𞹱' => 'ص',
+  '𞹲' => 'ق',
+  '𞹴' => 'ش',
+  '𞹵' => 'ت',
+  '𞹶' => 'ث',
+  '𞹷' => 'خ',
+  '𞹹' => 'ض',
+  '𞹺' => 'ظ',
+  '𞹻' => 'غ',
+  '𞹼' => 'ٮ',
+  '𞹾' => 'ڡ',
+  '𞺀' => 'ا',
+  '𞺁' => 'ب',
+  '𞺂' => 'ج',
+  '𞺃' => 'د',
+  '𞺄' => 'ه',
+  '𞺅' => 'و',
+  '𞺆' => 'ز',
+  '𞺇' => 'ح',
+  '𞺈' => 'ط',
+  '𞺉' => 'ي',
+  '𞺋' => 'ل',
+  '𞺌' => 'م',
+  '𞺍' => 'ن',
+  '𞺎' => 'س',
+  '𞺏' => 'ع',
+  '𞺐' => 'ف',
+  '𞺑' => 'ص',
+  '𞺒' => 'ق',
+  '𞺓' => 'ر',
+  '𞺔' => 'ش',
+  '𞺕' => 'ت',
+  '𞺖' => 'ث',
+  '𞺗' => 'خ',
+  '𞺘' => 'ذ',
+  '𞺙' => 'ض',
+  '𞺚' => 'ظ',
+  '𞺛' => 'غ',
+  '𞺡' => 'ب',
+  '𞺢' => 'ج',
+  '𞺣' => 'د',
+  '𞺥' => 'و',
+  '𞺦' => 'ز',
+  '𞺧' => 'ح',
+  '𞺨' => 'ط',
+  '𞺩' => 'ي',
+  '𞺫' => 'ل',
+  '𞺬' => 'م',
+  '𞺭' => 'ن',
+  '𞺮' => 'س',
+  '𞺯' => 'ع',
+  '𞺰' => 'ف',
+  '𞺱' => 'ص',
+  '𞺲' => 'ق',
+  '𞺳' => 'ر',
+  '𞺴' => 'ش',
+  '𞺵' => 'ت',
+  '𞺶' => 'ث',
+  '𞺷' => 'خ',
+  '𞺸' => 'ذ',
+  '𞺹' => 'ض',
+  '𞺺' => 'ظ',
+  '𞺻' => 'غ',
+  '🄀' => '0.',
+  '🄁' => '0,',
+  '🄂' => '1,',
+  '🄃' => '2,',
+  '🄄' => '3,',
+  '🄅' => '4,',
+  '🄆' => '5,',
+  '🄇' => '6,',
+  '🄈' => '7,',
+  '🄉' => '8,',
+  '🄊' => '9,',
+  '🄐' => '(A)',
+  '🄑' => '(B)',
+  '🄒' => '(C)',
+  '🄓' => '(D)',
+  '🄔' => '(E)',
+  '🄕' => '(F)',
+  '🄖' => '(G)',
+  '🄗' => '(H)',
+  '🄘' => '(I)',
+  '🄙' => '(J)',
+  '🄚' => '(K)',
+  '🄛' => '(L)',
+  '🄜' => '(M)',
+  '🄝' => '(N)',
+  '🄞' => '(O)',
+  '🄟' => '(P)',
+  '🄠' => '(Q)',
+  '🄡' => '(R)',
+  '🄢' => '(S)',
+  '🄣' => '(T)',
+  '🄤' => '(U)',
+  '🄥' => '(V)',
+  '🄦' => '(W)',
+  '🄧' => '(X)',
+  '🄨' => '(Y)',
+  '🄩' => '(Z)',
+  '🄪' => '〔S〕',
+  '🄫' => 'C',
+  '🄬' => 'R',
+  '🄭' => 'CD',
+  '🄮' => 'WZ',
+  '🄰' => 'A',
+  '🄱' => 'B',
+  '🄲' => 'C',
+  '🄳' => 'D',
+  '🄴' => 'E',
+  '🄵' => 'F',
+  '🄶' => 'G',
+  '🄷' => 'H',
+  '🄸' => 'I',
+  '🄹' => 'J',
+  '🄺' => 'K',
+  '🄻' => 'L',
+  '🄼' => 'M',
+  '🄽' => 'N',
+  '🄾' => 'O',
+  '🄿' => 'P',
+  '🅀' => 'Q',
+  '🅁' => 'R',
+  '🅂' => 'S',
+  '🅃' => 'T',
+  '🅄' => 'U',
+  '🅅' => 'V',
+  '🅆' => 'W',
+  '🅇' => 'X',
+  '🅈' => 'Y',
+  '🅉' => 'Z',
+  '🅊' => 'HV',
+  '🅋' => 'MV',
+  '🅌' => 'SD',
+  '🅍' => 'SS',
+  '🅎' => 'PPV',
+  '🅏' => 'WC',
+  '🅪' => 'MC',
+  '🅫' => 'MD',
+  '🅬' => 'MR',
+  '🆐' => 'DJ',
+  '🈀' => 'ほか',
+  '🈁' => 'ココ',
+  '🈂' => 'サ',
+  '🈐' => '手',
+  '🈑' => '字',
+  '🈒' => '双',
+  '🈓' => 'デ',
+  '🈔' => '二',
+  '🈕' => '多',
+  '🈖' => '解',
+  '🈗' => '天',
+  '🈘' => '交',
+  '🈙' => '映',
+  '🈚' => '無',
+  '🈛' => '料',
+  '🈜' => '前',
+  '🈝' => '後',
+  '🈞' => '再',
+  '🈟' => '新',
+  '🈠' => '初',
+  '🈡' => '終',
+  '🈢' => '生',
+  '🈣' => '販',
+  '🈤' => '声',
+  '🈥' => '吹',
+  '🈦' => '演',
+  '🈧' => '投',
+  '🈨' => '捕',
+  '🈩' => '一',
+  '🈪' => '三',
+  '🈫' => '遊',
+  '🈬' => '左',
+  '🈭' => '中',
+  '🈮' => '右',
+  '🈯' => '指',
+  '🈰' => '走',
+  '🈱' => '打',
+  '🈲' => '禁',
+  '🈳' => '空',
+  '🈴' => '合',
+  '🈵' => '満',
+  '🈶' => '有',
+  '🈷' => '月',
+  '🈸' => '申',
+  '🈹' => '割',
+  '🈺' => '営',
+  '🈻' => '配',
+  '🉀' => '〔本〕',
+  '🉁' => '〔三〕',
+  '🉂' => '〔二〕',
+  '🉃' => '〔安〕',
+  '🉄' => '〔点〕',
+  '🉅' => '〔打〕',
+  '🉆' => '〔盗〕',
+  '🉇' => '〔勝〕',
+  '🉈' => '〔敗〕',
+  '🉐' => '得',
+  '🉑' => '可',
+  '🯰' => '0',
+  '🯱' => '1',
+  '🯲' => '2',
+  '🯳' => '3',
+  '🯴' => '4',
+  '🯵' => '5',
+  '🯶' => '6',
+  '🯷' => '7',
+  '🯸' => '8',
+  '🯹' => '9',
+);
diff --git a/vendor/symfony/polyfill-intl-normalizer/bootstrap.php b/vendor/symfony/polyfill-intl-normalizer/bootstrap.php
new file mode 100755
index 0000000..ba62006
--- /dev/null
+++ b/vendor/symfony/polyfill-intl-normalizer/bootstrap.php
@@ -0,0 +1,19 @@
+<?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.
+ */
+
+use Symfony\Polyfill\Intl\Normalizer as p;
+
+if (!function_exists('normalizer_is_normalized')) {
+    function normalizer_is_normalized($s, $form = p\Normalizer::NFC) { return p\Normalizer::isNormalized($s, $form); }
+}
+if (!function_exists('normalizer_normalize')) {
+    function normalizer_normalize($s, $form = p\Normalizer::NFC) { return p\Normalizer::normalize($s, $form); }
+}
diff --git a/vendor/symfony/polyfill-intl-normalizer/composer.json b/vendor/symfony/polyfill-intl-normalizer/composer.json
new file mode 100755
index 0000000..b0986a8
--- /dev/null
+++ b/vendor/symfony/polyfill-intl-normalizer/composer.json
@@ -0,0 +1,39 @@
+{
+    "name": "symfony/polyfill-intl-normalizer",
+    "type": "library",
+    "description": "Symfony polyfill for intl's Normalizer class and related functions",
+    "keywords": ["polyfill", "shim", "compatibility", "portable", "intl", "normalizer"],
+    "homepage": "https://symfony.com",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Nicolas Grekas",
+            "email": "p@tchwork.com"
+        },
+        {
+            "name": "Symfony Community",
+            "homepage": "https://symfony.com/contributors"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.3"
+    },
+    "autoload": {
+        "psr-4": { "Symfony\\Polyfill\\Intl\\Normalizer\\": "" },
+        "files": [ "bootstrap.php" ],
+        "classmap": [ "Resources/stubs" ]
+    },
+    "suggest": {
+        "ext-intl": "For best performance"
+    },
+    "minimum-stability": "dev",
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.18-dev"
+        },
+        "thanks": {
+            "name": "symfony/polyfill",
+            "url": "https://github.com/symfony/polyfill"
+        }
+    }
+}
diff --git a/vendor/symfony/polyfill-mbstring/LICENSE b/vendor/symfony/polyfill-mbstring/LICENSE
new file mode 100755
index 0000000..4cd8bdd
--- /dev/null
+++ b/vendor/symfony/polyfill-mbstring/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015-2019 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.
diff --git a/vendor/symfony/polyfill-mbstring/Mbstring.php b/vendor/symfony/polyfill-mbstring/Mbstring.php
new file mode 100755
index 0000000..a5e4a8f
--- /dev/null
+++ b/vendor/symfony/polyfill-mbstring/Mbstring.php
@@ -0,0 +1,800 @@
+<?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\Polyfill\Mbstring;
+
+/**
+ * Partial mbstring implementation in PHP, iconv based, UTF-8 centric.
+ *
+ * Implemented:
+ * - mb_chr                  - Returns a specific character from its Unicode code point
+ * - mb_convert_encoding     - Convert character encoding
+ * - mb_convert_variables    - Convert character code in variable(s)
+ * - mb_decode_mimeheader    - Decode string in MIME header field
+ * - mb_encode_mimeheader    - Encode string for MIME header XXX NATIVE IMPLEMENTATION IS REALLY BUGGED
+ * - mb_decode_numericentity - Decode HTML numeric string reference to character
+ * - mb_encode_numericentity - Encode character to HTML numeric string reference
+ * - mb_convert_case         - Perform case folding on a string
+ * - mb_detect_encoding      - Detect character encoding
+ * - mb_get_info             - Get internal settings of mbstring
+ * - mb_http_input           - Detect HTTP input character encoding
+ * - mb_http_output          - Set/Get HTTP output character encoding
+ * - mb_internal_encoding    - Set/Get internal character encoding
+ * - mb_list_encodings       - Returns an array of all supported encodings
+ * - mb_ord                  - Returns the Unicode code point of a character
+ * - mb_output_handler       - Callback function converts character encoding in output buffer
+ * - mb_scrub                - Replaces ill-formed byte sequences with substitute characters
+ * - mb_strlen               - Get string length
+ * - mb_strpos               - Find position of first occurrence of string in a string
+ * - mb_strrpos              - Find position of last occurrence of a string in a string
+ * - mb_strtolower           - Make a string lowercase
+ * - mb_strtoupper           - Make a string uppercase
+ * - mb_substitute_character - Set/Get substitution character
+ * - mb_substr               - Get part of string
+ * - mb_stripos              - Finds position of first occurrence of a string within another, case insensitive
+ * - mb_stristr              - Finds first occurrence of a string within another, case insensitive
+ * - mb_strrchr              - Finds the last occurrence of a character in a string within another
+ * - mb_strrichr             - Finds the last occurrence of a character in a string within another, case insensitive
+ * - mb_strripos             - Finds position of last occurrence of a string within another, case insensitive
+ * - mb_strstr               - Finds first occurrence of a string within another
+ * - mb_strwidth             - Return width of string
+ * - mb_substr_count         - Count the number of substring occurrences
+ *
+ * Not implemented:
+ * - mb_convert_kana         - Convert "kana" one from another ("zen-kaku", "han-kaku" and more)
+ * - mb_ereg_*               - Regular expression with multibyte support
+ * - mb_parse_str            - Parse GET/POST/COOKIE data and set global variable
+ * - mb_preferred_mime_name  - Get MIME charset string
+ * - mb_regex_encoding       - Returns current encoding for multibyte regex as string
+ * - mb_regex_set_options    - Set/Get the default options for mbregex functions
+ * - mb_send_mail            - Send encoded mail
+ * - mb_split                - Split multibyte string using regular expression
+ * - mb_strcut               - Get part of string
+ * - mb_strimwidth           - Get truncated string with specified width
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ *
+ * @internal
+ */
+final class Mbstring
+{
+    const MB_CASE_FOLD = PHP_INT_MAX;
+
+    private static $encodingList = array('ASCII', 'UTF-8');
+    private static $language = 'neutral';
+    private static $internalEncoding = 'UTF-8';
+    private static $caseFold = array(
+        array('µ', 'ſ', "\xCD\x85", 'ς', "\xCF\x90", "\xCF\x91", "\xCF\x95", "\xCF\x96", "\xCF\xB0", "\xCF\xB1", "\xCF\xB5", "\xE1\xBA\x9B", "\xE1\xBE\xBE"),
+        array('μ', 's', 'ι',        'σ', 'β',        'θ',        'φ',        'π',        'κ',        'ρ',        'ε',        "\xE1\xB9\xA1", 'ι'),
+    );
+
+    public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null)
+    {
+        if (\is_array($fromEncoding) || false !== strpos($fromEncoding, ',')) {
+            $fromEncoding = self::mb_detect_encoding($s, $fromEncoding);
+        } else {
+            $fromEncoding = self::getEncoding($fromEncoding);
+        }
+
+        $toEncoding = self::getEncoding($toEncoding);
+
+        if ('BASE64' === $fromEncoding) {
+            $s = base64_decode($s);
+            $fromEncoding = $toEncoding;
+        }
+
+        if ('BASE64' === $toEncoding) {
+            return base64_encode($s);
+        }
+
+        if ('HTML-ENTITIES' === $toEncoding || 'HTML' === $toEncoding) {
+            if ('HTML-ENTITIES' === $fromEncoding || 'HTML' === $fromEncoding) {
+                $fromEncoding = 'Windows-1252';
+            }
+            if ('UTF-8' !== $fromEncoding) {
+                $s = iconv($fromEncoding, 'UTF-8//IGNORE', $s);
+            }
+
+            return preg_replace_callback('/[\x80-\xFF]+/', array(__CLASS__, 'html_encoding_callback'), $s);
+        }
+
+        if ('HTML-ENTITIES' === $fromEncoding) {
+            $s = html_entity_decode($s, ENT_COMPAT, 'UTF-8');
+            $fromEncoding = 'UTF-8';
+        }
+
+        return iconv($fromEncoding, $toEncoding.'//IGNORE', $s);
+    }
+
+    public static function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null)
+    {
+        $vars = array(&$a, &$b, &$c, &$d, &$e, &$f);
+
+        $ok = true;
+        array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) {
+            if (false === $v = Mbstring::mb_convert_encoding($v, $toEncoding, $fromEncoding)) {
+                $ok = false;
+            }
+        });
+
+        return $ok ? $fromEncoding : false;
+    }
+
+    public static function mb_decode_mimeheader($s)
+    {
+        return iconv_mime_decode($s, 2, self::$internalEncoding);
+    }
+
+    public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null)
+    {
+        trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', E_USER_WARNING);
+    }
+
+    public static function mb_decode_numericentity($s, $convmap, $encoding = null)
+    {
+        if (null !== $s && !\is_scalar($s) && !(\is_object($s) && \method_exists($s, '__toString'))) {
+            trigger_error('mb_decode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', E_USER_WARNING);
+
+            return null;
+        }
+
+        if (!\is_array($convmap) || !$convmap) {
+            return false;
+        }
+
+        if (null !== $encoding && !\is_scalar($encoding)) {
+            trigger_error('mb_decode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', E_USER_WARNING);
+
+            return '';  // Instead of null (cf. mb_encode_numericentity).
+        }
+
+        $s = (string) $s;
+        if ('' === $s) {
+            return '';
+        }
+
+        $encoding = self::getEncoding($encoding);
+
+        if ('UTF-8' === $encoding) {
+            $encoding = null;
+            if (!preg_match('//u', $s)) {
+                $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
+            }
+        } else {
+            $s = iconv($encoding, 'UTF-8//IGNORE', $s);
+        }
+
+        $cnt = floor(\count($convmap) / 4) * 4;
+
+        for ($i = 0; $i < $cnt; $i += 4) {
+            // collector_decode_htmlnumericentity ignores $convmap[$i + 3]
+            $convmap[$i] += $convmap[$i + 2];
+            $convmap[$i + 1] += $convmap[$i + 2];
+        }
+
+        $s = preg_replace_callback('/&#(?:0*([0-9]+)|x0*([0-9a-fA-F]+))(?!&);?/', function (array $m) use ($cnt, $convmap) {
+            $c = isset($m[2]) ? (int) hexdec($m[2]) : $m[1];
+            for ($i = 0; $i < $cnt; $i += 4) {
+                if ($c >= $convmap[$i] && $c <= $convmap[$i + 1]) {
+                    return Mbstring::mb_chr($c - $convmap[$i + 2]);
+                }
+            }
+
+            return $m[0];
+        }, $s);
+
+        if (null === $encoding) {
+            return $s;
+        }
+
+        return iconv('UTF-8', $encoding.'//IGNORE', $s);
+    }
+
+    public static function mb_encode_numericentity($s, $convmap, $encoding = null, $is_hex = false)
+    {
+        if (null !== $s && !\is_scalar($s) && !(\is_object($s) && \method_exists($s, '__toString'))) {
+            trigger_error('mb_encode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', E_USER_WARNING);
+
+            return null;
+        }
+
+        if (!\is_array($convmap) || !$convmap) {
+            return false;
+        }
+
+        if (null !== $encoding && !\is_scalar($encoding)) {
+            trigger_error('mb_encode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', E_USER_WARNING);
+
+            return null;  // Instead of '' (cf. mb_decode_numericentity).
+        }
+
+        if (null !== $is_hex && !\is_scalar($is_hex)) {
+            trigger_error('mb_encode_numericentity() expects parameter 4 to be boolean, '.\gettype($s).' given', E_USER_WARNING);
+
+            return null;
+        }
+
+        $s = (string) $s;
+        if ('' === $s) {
+            return '';
+        }
+
+        $encoding = self::getEncoding($encoding);
+
+        if ('UTF-8' === $encoding) {
+            $encoding = null;
+            if (!preg_match('//u', $s)) {
+                $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
+            }
+        } else {
+            $s = iconv($encoding, 'UTF-8//IGNORE', $s);
+        }
+
+        static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4);
+
+        $cnt = floor(\count($convmap) / 4) * 4;
+        $i = 0;
+        $len = \strlen($s);
+        $result = '';
+
+        while ($i < $len) {
+            $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"];
+            $uchr = substr($s, $i, $ulen);
+            $i += $ulen;
+            $c = self::mb_ord($uchr);
+
+            for ($j = 0; $j < $cnt; $j += 4) {
+                if ($c >= $convmap[$j] && $c <= $convmap[$j + 1]) {
+                    $cOffset = ($c + $convmap[$j + 2]) & $convmap[$j + 3];
+                    $result .= $is_hex ? sprintf('&#x%X;', $cOffset) : '&#'.$cOffset.';';
+                    continue 2;
+                }
+            }
+            $result .= $uchr;
+        }
+
+        if (null === $encoding) {
+            return $result;
+        }
+
+        return iconv('UTF-8', $encoding.'//IGNORE', $result);
+    }
+
+    public static function mb_convert_case($s, $mode, $encoding = null)
+    {
+        $s = (string) $s;
+        if ('' === $s) {
+            return '';
+        }
+
+        $encoding = self::getEncoding($encoding);
+
+        if ('UTF-8' === $encoding) {
+            $encoding = null;
+            if (!preg_match('//u', $s)) {
+                $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
+            }
+        } else {
+            $s = iconv($encoding, 'UTF-8//IGNORE', $s);
+        }
+
+        if (MB_CASE_TITLE == $mode) {
+            static $titleRegexp = null;
+            if (null === $titleRegexp) {
+                $titleRegexp = self::getData('titleCaseRegexp');
+            }
+            $s = preg_replace_callback($titleRegexp, array(__CLASS__, 'title_case'), $s);
+        } else {
+            if (MB_CASE_UPPER == $mode) {
+                static $upper = null;
+                if (null === $upper) {
+                    $upper = self::getData('upperCase');
+                }
+                $map = $upper;
+            } else {
+                if (self::MB_CASE_FOLD === $mode) {
+                    $s = str_replace(self::$caseFold[0], self::$caseFold[1], $s);
+                }
+
+                static $lower = null;
+                if (null === $lower) {
+                    $lower = self::getData('lowerCase');
+                }
+                $map = $lower;
+            }
+
+            static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4);
+
+            $i = 0;
+            $len = \strlen($s);
+
+            while ($i < $len) {
+                $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"];
+                $uchr = substr($s, $i, $ulen);
+                $i += $ulen;
+
+                if (isset($map[$uchr])) {
+                    $uchr = $map[$uchr];
+                    $nlen = \strlen($uchr);
+
+                    if ($nlen == $ulen) {
+                        $nlen = $i;
+                        do {
+                            $s[--$nlen] = $uchr[--$ulen];
+                        } while ($ulen);
+                    } else {
+                        $s = substr_replace($s, $uchr, $i - $ulen, $ulen);
+                        $len += $nlen - $ulen;
+                        $i += $nlen - $ulen;
+                    }
+                }
+            }
+        }
+
+        if (null === $encoding) {
+            return $s;
+        }
+
+        return iconv('UTF-8', $encoding.'//IGNORE', $s);
+    }
+
+    public static function mb_internal_encoding($encoding = null)
+    {
+        if (null === $encoding) {
+            return self::$internalEncoding;
+        }
+
+        $encoding = self::getEncoding($encoding);
+
+        if ('UTF-8' === $encoding || false !== @iconv($encoding, $encoding, ' ')) {
+            self::$internalEncoding = $encoding;
+
+            return true;
+        }
+
+        return false;
+    }
+
+    public static function mb_language($lang = null)
+    {
+        if (null === $lang) {
+            return self::$language;
+        }
+
+        switch ($lang = strtolower($lang)) {
+            case 'uni':
+            case 'neutral':
+                self::$language = $lang;
+
+                return true;
+        }
+
+        return false;
+    }
+
+    public static function mb_list_encodings()
+    {
+        return array('UTF-8');
+    }
+
+    public static function mb_encoding_aliases($encoding)
+    {
+        switch (strtoupper($encoding)) {
+            case 'UTF8':
+            case 'UTF-8':
+                return array('utf8');
+        }
+
+        return false;
+    }
+
+    public static function mb_check_encoding($var = null, $encoding = null)
+    {
+        if (null === $encoding) {
+            if (null === $var) {
+                return false;
+            }
+            $encoding = self::$internalEncoding;
+        }
+
+        return self::mb_detect_encoding($var, array($encoding)) || false !== @iconv($encoding, $encoding, $var);
+    }
+
+    public static function mb_detect_encoding($str, $encodingList = null, $strict = false)
+    {
+        if (null === $encodingList) {
+            $encodingList = self::$encodingList;
+        } else {
+            if (!\is_array($encodingList)) {
+                $encodingList = array_map('trim', explode(',', $encodingList));
+            }
+            $encodingList = array_map('strtoupper', $encodingList);
+        }
+
+        foreach ($encodingList as $enc) {
+            switch ($enc) {
+                case 'ASCII':
+                    if (!preg_match('/[\x80-\xFF]/', $str)) {
+                        return $enc;
+                    }
+                    break;
+
+                case 'UTF8':
+                case 'UTF-8':
+                    if (preg_match('//u', $str)) {
+                        return 'UTF-8';
+                    }
+                    break;
+
+                default:
+                    if (0 === strncmp($enc, 'ISO-8859-', 9)) {
+                        return $enc;
+                    }
+            }
+        }
+
+        return false;
+    }
+
+    public static function mb_detect_order($encodingList = null)
+    {
+        if (null === $encodingList) {
+            return self::$encodingList;
+        }
+
+        if (!\is_array($encodingList)) {
+            $encodingList = array_map('trim', explode(',', $encodingList));
+        }
+        $encodingList = array_map('strtoupper', $encodingList);
+
+        foreach ($encodingList as $enc) {
+            switch ($enc) {
+                default:
+                    if (strncmp($enc, 'ISO-8859-', 9)) {
+                        return false;
+                    }
+                    // no break
+                case 'ASCII':
+                case 'UTF8':
+                case 'UTF-8':
+            }
+        }
+
+        self::$encodingList = $encodingList;
+
+        return true;
+    }
+
+    public static function mb_strlen($s, $encoding = null)
+    {
+        $encoding = self::getEncoding($encoding);
+        if ('CP850' === $encoding || 'ASCII' === $encoding) {
+            return \strlen($s);
+        }
+
+        return @iconv_strlen($s, $encoding);
+    }
+
+    public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null)
+    {
+        $encoding = self::getEncoding($encoding);
+        if ('CP850' === $encoding || 'ASCII' === $encoding) {
+            return strpos($haystack, $needle, $offset);
+        }
+
+        $needle = (string) $needle;
+        if ('' === $needle) {
+            trigger_error(__METHOD__.': Empty delimiter', E_USER_WARNING);
+
+            return false;
+        }
+
+        return iconv_strpos($haystack, $needle, $offset, $encoding);
+    }
+
+    public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null)
+    {
+        $encoding = self::getEncoding($encoding);
+        if ('CP850' === $encoding || 'ASCII' === $encoding) {
+            return strrpos($haystack, $needle, $offset);
+        }
+
+        if ($offset != (int) $offset) {
+            $offset = 0;
+        } elseif ($offset = (int) $offset) {
+            if ($offset < 0) {
+                $haystack = self::mb_substr($haystack, 0, $offset, $encoding);
+                $offset = 0;
+            } else {
+                $haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding);
+            }
+        }
+
+        $pos = iconv_strrpos($haystack, $needle, $encoding);
+
+        return false !== $pos ? $offset + $pos : false;
+    }
+
+    public static function mb_strtolower($s, $encoding = null)
+    {
+        return self::mb_convert_case($s, MB_CASE_LOWER, $encoding);
+    }
+
+    public static function mb_strtoupper($s, $encoding = null)
+    {
+        return self::mb_convert_case($s, MB_CASE_UPPER, $encoding);
+    }
+
+    public static function mb_substitute_character($c = null)
+    {
+        if (0 === strcasecmp($c, 'none')) {
+            return true;
+        }
+
+        return null !== $c ? false : 'none';
+    }
+
+    public static function mb_substr($s, $start, $length = null, $encoding = null)
+    {
+        $encoding = self::getEncoding($encoding);
+        if ('CP850' === $encoding || 'ASCII' === $encoding) {
+            return substr($s, $start, null === $length ? 2147483647 : $length);
+        }
+
+        if ($start < 0) {
+            $start = iconv_strlen($s, $encoding) + $start;
+            if ($start < 0) {
+                $start = 0;
+            }
+        }
+
+        if (null === $length) {
+            $length = 2147483647;
+        } elseif ($length < 0) {
+            $length = iconv_strlen($s, $encoding) + $length - $start;
+            if ($length < 0) {
+                return '';
+            }
+        }
+
+        return (string) iconv_substr($s, $start, $length, $encoding);
+    }
+
+    public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null)
+    {
+        $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding);
+        $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding);
+
+        return self::mb_strpos($haystack, $needle, $offset, $encoding);
+    }
+
+    public static function mb_stristr($haystack, $needle, $part = false, $encoding = null)
+    {
+        $pos = self::mb_stripos($haystack, $needle, 0, $encoding);
+
+        return self::getSubpart($pos, $part, $haystack, $encoding);
+    }
+
+    public static function mb_strrchr($haystack, $needle, $part = false, $encoding = null)
+    {
+        $encoding = self::getEncoding($encoding);
+        if ('CP850' === $encoding || 'ASCII' === $encoding) {
+            return strrchr($haystack, $needle, $part);
+        }
+        $needle = self::mb_substr($needle, 0, 1, $encoding);
+        $pos = iconv_strrpos($haystack, $needle, $encoding);
+
+        return self::getSubpart($pos, $part, $haystack, $encoding);
+    }
+
+    public static function mb_strrichr($haystack, $needle, $part = false, $encoding = null)
+    {
+        $needle = self::mb_substr($needle, 0, 1, $encoding);
+        $pos = self::mb_strripos($haystack, $needle, $encoding);
+
+        return self::getSubpart($pos, $part, $haystack, $encoding);
+    }
+
+    public static function mb_strripos($haystack, $needle, $offset = 0, $encoding = null)
+    {
+        $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding);
+        $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding);
+
+        return self::mb_strrpos($haystack, $needle, $offset, $encoding);
+    }
+
+    public static function mb_strstr($haystack, $needle, $part = false, $encoding = null)
+    {
+        $pos = strpos($haystack, $needle);
+        if (false === $pos) {
+            return false;
+        }
+        if ($part) {
+            return substr($haystack, 0, $pos);
+        }
+
+        return substr($haystack, $pos);
+    }
+
+    public static function mb_get_info($type = 'all')
+    {
+        $info = array(
+            'internal_encoding' => self::$internalEncoding,
+            'http_output' => 'pass',
+            'http_output_conv_mimetypes' => '^(text/|application/xhtml\+xml)',
+            'func_overload' => 0,
+            'func_overload_list' => 'no overload',
+            'mail_charset' => 'UTF-8',
+            'mail_header_encoding' => 'BASE64',
+            'mail_body_encoding' => 'BASE64',
+            'illegal_chars' => 0,
+            'encoding_translation' => 'Off',
+            'language' => self::$language,
+            'detect_order' => self::$encodingList,
+            'substitute_character' => 'none',
+            'strict_detection' => 'Off',
+        );
+
+        if ('all' === $type) {
+            return $info;
+        }
+        if (isset($info[$type])) {
+            return $info[$type];
+        }
+
+        return false;
+    }
+
+    public static function mb_http_input($type = '')
+    {
+        return false;
+    }
+
+    public static function mb_http_output($encoding = null)
+    {
+        return null !== $encoding ? 'pass' === $encoding : 'pass';
+    }
+
+    public static function mb_strwidth($s, $encoding = null)
+    {
+        $encoding = self::getEncoding($encoding);
+
+        if ('UTF-8' !== $encoding) {
+            $s = iconv($encoding, 'UTF-8//IGNORE', $s);
+        }
+
+        $s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide);
+
+        return ($wide << 1) + iconv_strlen($s, 'UTF-8');
+    }
+
+    public static function mb_substr_count($haystack, $needle, $encoding = null)
+    {
+        return substr_count($haystack, $needle);
+    }
+
+    public static function mb_output_handler($contents, $status)
+    {
+        return $contents;
+    }
+
+    public static function mb_chr($code, $encoding = null)
+    {
+        if (0x80 > $code %= 0x200000) {
+            $s = \chr($code);
+        } elseif (0x800 > $code) {
+            $s = \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F);
+        } elseif (0x10000 > $code) {
+            $s = \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F);
+        } else {
+            $s = \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F);
+        }
+
+        if ('UTF-8' !== $encoding = self::getEncoding($encoding)) {
+            $s = mb_convert_encoding($s, $encoding, 'UTF-8');
+        }
+
+        return $s;
+    }
+
+    public static function mb_ord($s, $encoding = null)
+    {
+        if ('UTF-8' !== $encoding = self::getEncoding($encoding)) {
+            $s = mb_convert_encoding($s, 'UTF-8', $encoding);
+        }
+
+        if (1 === \strlen($s)) {
+            return \ord($s);
+        }
+
+        $code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0;
+        if (0xF0 <= $code) {
+            return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80;
+        }
+        if (0xE0 <= $code) {
+            return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80;
+        }
+        if (0xC0 <= $code) {
+            return (($code - 0xC0) << 6) + $s[2] - 0x80;
+        }
+
+        return $code;
+    }
+
+    private static function getSubpart($pos, $part, $haystack, $encoding)
+    {
+        if (false === $pos) {
+            return false;
+        }
+        if ($part) {
+            return self::mb_substr($haystack, 0, $pos, $encoding);
+        }
+
+        return self::mb_substr($haystack, $pos, null, $encoding);
+    }
+
+    private static function html_encoding_callback(array $m)
+    {
+        $i = 1;
+        $entities = '';
+        $m = unpack('C*', htmlentities($m[0], ENT_COMPAT, 'UTF-8'));
+
+        while (isset($m[$i])) {
+            if (0x80 > $m[$i]) {
+                $entities .= \chr($m[$i++]);
+                continue;
+            }
+            if (0xF0 <= $m[$i]) {
+                $c = (($m[$i++] - 0xF0) << 18) + (($m[$i++] - 0x80) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
+            } elseif (0xE0 <= $m[$i]) {
+                $c = (($m[$i++] - 0xE0) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
+            } else {
+                $c = (($m[$i++] - 0xC0) << 6) + $m[$i++] - 0x80;
+            }
+
+            $entities .= '&#'.$c.';';
+        }
+
+        return $entities;
+    }
+
+    private static function title_case(array $s)
+    {
+        return self::mb_convert_case($s[1], MB_CASE_UPPER, 'UTF-8').self::mb_convert_case($s[2], MB_CASE_LOWER, 'UTF-8');
+    }
+
+    private static function getData($file)
+    {
+        if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) {
+            return require $file;
+        }
+
+        return false;
+    }
+
+    private static function getEncoding($encoding)
+    {
+        if (null === $encoding) {
+            return self::$internalEncoding;
+        }
+
+        $encoding = strtoupper($encoding);
+
+        if ('8BIT' === $encoding || 'BINARY' === $encoding) {
+            return 'CP850';
+        }
+        if ('UTF8' === $encoding) {
+            return 'UTF-8';
+        }
+
+        return $encoding;
+    }
+}
diff --git a/vendor/symfony/polyfill-mbstring/README.md b/vendor/symfony/polyfill-mbstring/README.md
new file mode 100755
index 0000000..342e828
--- /dev/null
+++ b/vendor/symfony/polyfill-mbstring/README.md
@@ -0,0 +1,13 @@
+Symfony Polyfill / Mbstring
+===========================
+
+This component provides a partial, native PHP implementation for the
+[Mbstring](http://php.net/mbstring) extension.
+
+More information can be found in the
+[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md).
+
+License
+=======
+
+This library is released under the [MIT license](LICENSE).
diff --git a/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php b/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php
new file mode 100755
index 0000000..e6fbfa6
--- /dev/null
+++ b/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php
@@ -0,0 +1,1096 @@
+<?php
+
+return array(
+  'A' => 'a',
+  'B' => 'b',
+  'C' => 'c',
+  'D' => 'd',
+  'E' => 'e',
+  'F' => 'f',
+  'G' => 'g',
+  'H' => 'h',
+  'I' => 'i',
+  'J' => 'j',
+  'K' => 'k',
+  'L' => 'l',
+  'M' => 'm',
+  'N' => 'n',
+  'O' => 'o',
+  'P' => 'p',
+  'Q' => 'q',
+  'R' => 'r',
+  'S' => 's',
+  'T' => 't',
+  'U' => 'u',
+  'V' => 'v',
+  'W' => 'w',
+  'X' => 'x',
+  'Y' => 'y',
+  'Z' => 'z',
+  'À' => 'à',
+  'Á' => 'á',
+  'Â' => 'â',
+  'Ã' => 'ã',
+  'Ä' => 'ä',
+  'Å' => 'å',
+  'Æ' => 'æ',
+  'Ç' => 'ç',
+  'È' => 'è',
+  'É' => 'é',
+  'Ê' => 'ê',
+  'Ë' => 'ë',
+  'Ì' => 'ì',
+  'Í' => 'í',
+  'Î' => 'î',
+  'Ï' => 'ï',
+  'Ð' => 'ð',
+  'Ñ' => 'ñ',
+  'Ò' => 'ò',
+  'Ó' => 'ó',
+  'Ô' => 'ô',
+  'Õ' => 'õ',
+  'Ö' => 'ö',
+  'Ø' => 'ø',
+  'Ù' => 'ù',
+  'Ú' => 'ú',
+  'Û' => 'û',
+  'Ü' => 'ü',
+  'Ý' => 'ý',
+  'Þ' => 'þ',
+  'Ā' => 'ā',
+  'Ă' => 'ă',
+  'Ą' => 'ą',
+  'Ć' => 'ć',
+  'Ĉ' => 'ĉ',
+  'Ċ' => 'ċ',
+  'Č' => 'č',
+  'Ď' => 'ď',
+  'Đ' => 'đ',
+  'Ē' => 'ē',
+  'Ĕ' => 'ĕ',
+  'Ė' => 'ė',
+  'Ę' => 'ę',
+  'Ě' => 'ě',
+  'Ĝ' => 'ĝ',
+  'Ğ' => 'ğ',
+  'Ġ' => 'ġ',
+  'Ģ' => 'ģ',
+  'Ĥ' => 'ĥ',
+  'Ħ' => 'ħ',
+  'Ĩ' => 'ĩ',
+  'Ī' => 'ī',
+  'Ĭ' => 'ĭ',
+  'Į' => 'į',
+  'İ' => 'i',
+  'IJ' => 'ij',
+  'Ĵ' => 'ĵ',
+  'Ķ' => 'ķ',
+  'Ĺ' => 'ĺ',
+  'Ļ' => 'ļ',
+  'Ľ' => 'ľ',
+  'Ŀ' => 'ŀ',
+  'Ł' => 'ł',
+  'Ń' => 'ń',
+  'Ņ' => 'ņ',
+  'Ň' => 'ň',
+  'Ŋ' => 'ŋ',
+  'Ō' => 'ō',
+  'Ŏ' => 'ŏ',
+  'Ő' => 'ő',
+  'Œ' => 'œ',
+  'Ŕ' => 'ŕ',
+  'Ŗ' => 'ŗ',
+  'Ř' => 'ř',
+  'Ś' => 'ś',
+  'Ŝ' => 'ŝ',
+  'Ş' => 'ş',
+  'Š' => 'š',
+  'Ţ' => 'ţ',
+  'Ť' => 'ť',
+  'Ŧ' => 'ŧ',
+  'Ũ' => 'ũ',
+  'Ū' => 'ū',
+  'Ŭ' => 'ŭ',
+  'Ů' => 'ů',
+  'Ű' => 'ű',
+  'Ų' => 'ų',
+  'Ŵ' => 'ŵ',
+  'Ŷ' => 'ŷ',
+  'Ÿ' => 'ÿ',
+  'Ź' => 'ź',
+  'Ż' => 'ż',
+  'Ž' => 'ž',
+  'Ɓ' => 'ɓ',
+  'Ƃ' => 'ƃ',
+  'Ƅ' => 'ƅ',
+  'Ɔ' => 'ɔ',
+  'Ƈ' => 'ƈ',
+  'Ɖ' => 'ɖ',
+  'Ɗ' => 'ɗ',
+  'Ƌ' => 'ƌ',
+  'Ǝ' => 'ǝ',
+  'Ə' => 'ə',
+  'Ɛ' => 'ɛ',
+  'Ƒ' => 'ƒ',
+  'Ɠ' => 'ɠ',
+  'Ɣ' => 'ɣ',
+  'Ɩ' => 'ɩ',
+  'Ɨ' => 'ɨ',
+  'Ƙ' => 'ƙ',
+  'Ɯ' => 'ɯ',
+  'Ɲ' => 'ɲ',
+  'Ɵ' => 'ɵ',
+  'Ơ' => 'ơ',
+  'Ƣ' => 'ƣ',
+  'Ƥ' => 'ƥ',
+  'Ʀ' => 'ʀ',
+  'Ƨ' => 'ƨ',
+  'Ʃ' => 'ʃ',
+  'Ƭ' => 'ƭ',
+  'Ʈ' => 'ʈ',
+  'Ư' => 'ư',
+  'Ʊ' => 'ʊ',
+  'Ʋ' => 'ʋ',
+  'Ƴ' => 'ƴ',
+  'Ƶ' => 'ƶ',
+  'Ʒ' => 'ʒ',
+  'Ƹ' => 'ƹ',
+  'Ƽ' => 'ƽ',
+  'DŽ' => 'dž',
+  'Dž' => 'dž',
+  'LJ' => 'lj',
+  'Lj' => 'lj',
+  'NJ' => 'nj',
+  'Nj' => 'nj',
+  'Ǎ' => 'ǎ',
+  'Ǐ' => 'ǐ',
+  'Ǒ' => 'ǒ',
+  'Ǔ' => 'ǔ',
+  'Ǖ' => 'ǖ',
+  'Ǘ' => 'ǘ',
+  'Ǚ' => 'ǚ',
+  'Ǜ' => 'ǜ',
+  'Ǟ' => 'ǟ',
+  'Ǡ' => 'ǡ',
+  'Ǣ' => 'ǣ',
+  'Ǥ' => 'ǥ',
+  'Ǧ' => 'ǧ',
+  'Ǩ' => 'ǩ',
+  'Ǫ' => 'ǫ',
+  'Ǭ' => 'ǭ',
+  'Ǯ' => 'ǯ',
+  'DZ' => 'dz',
+  'Dz' => 'dz',
+  'Ǵ' => 'ǵ',
+  'Ƕ' => 'ƕ',
+  'Ƿ' => 'ƿ',
+  'Ǹ' => 'ǹ',
+  'Ǻ' => 'ǻ',
+  'Ǽ' => 'ǽ',
+  'Ǿ' => 'ǿ',
+  'Ȁ' => 'ȁ',
+  'Ȃ' => 'ȃ',
+  'Ȅ' => 'ȅ',
+  'Ȇ' => 'ȇ',
+  'Ȉ' => 'ȉ',
+  'Ȋ' => 'ȋ',
+  'Ȍ' => 'ȍ',
+  'Ȏ' => 'ȏ',
+  'Ȑ' => 'ȑ',
+  'Ȓ' => 'ȓ',
+  'Ȕ' => 'ȕ',
+  'Ȗ' => 'ȗ',
+  'Ș' => 'ș',
+  'Ț' => 'ț',
+  'Ȝ' => 'ȝ',
+  'Ȟ' => 'ȟ',
+  'Ƞ' => 'ƞ',
+  'Ȣ' => 'ȣ',
+  'Ȥ' => 'ȥ',
+  'Ȧ' => 'ȧ',
+  'Ȩ' => 'ȩ',
+  'Ȫ' => 'ȫ',
+  'Ȭ' => 'ȭ',
+  'Ȯ' => 'ȯ',
+  'Ȱ' => 'ȱ',
+  'Ȳ' => 'ȳ',
+  'Ⱥ' => 'ⱥ',
+  'Ȼ' => 'ȼ',
+  'Ƚ' => 'ƚ',
+  'Ⱦ' => 'ⱦ',
+  'Ɂ' => 'ɂ',
+  'Ƀ' => 'ƀ',
+  'Ʉ' => 'ʉ',
+  'Ʌ' => 'ʌ',
+  'Ɇ' => 'ɇ',
+  'Ɉ' => 'ɉ',
+  'Ɋ' => 'ɋ',
+  'Ɍ' => 'ɍ',
+  'Ɏ' => 'ɏ',
+  'Ͱ' => 'ͱ',
+  'Ͳ' => 'ͳ',
+  'Ͷ' => 'ͷ',
+  'Ϳ' => 'ϳ',
+  'Ά' => 'ά',
+  'Έ' => 'έ',
+  'Ή' => 'ή',
+  'Ί' => 'ί',
+  'Ό' => 'ό',
+  'Ύ' => 'ύ',
+  'Ώ' => 'ώ',
+  'Α' => 'α',
+  'Β' => 'β',
+  'Γ' => 'γ',
+  'Δ' => 'δ',
+  'Ε' => 'ε',
+  'Ζ' => 'ζ',
+  'Η' => 'η',
+  'Θ' => 'θ',
+  'Ι' => 'ι',
+  'Κ' => 'κ',
+  'Λ' => 'λ',
+  'Μ' => 'μ',
+  'Ν' => 'ν',
+  'Ξ' => 'ξ',
+  'Ο' => 'ο',
+  'Π' => 'π',
+  'Ρ' => 'ρ',
+  'Σ' => 'σ',
+  'Τ' => 'τ',
+  'Υ' => 'υ',
+  'Φ' => 'φ',
+  'Χ' => 'χ',
+  'Ψ' => 'ψ',
+  'Ω' => 'ω',
+  'Ϊ' => 'ϊ',
+  'Ϋ' => 'ϋ',
+  'Ϗ' => 'ϗ',
+  'Ϙ' => 'ϙ',
+  'Ϛ' => 'ϛ',
+  'Ϝ' => 'ϝ',
+  'Ϟ' => 'ϟ',
+  'Ϡ' => 'ϡ',
+  'Ϣ' => 'ϣ',
+  'Ϥ' => 'ϥ',
+  'Ϧ' => 'ϧ',
+  'Ϩ' => 'ϩ',
+  'Ϫ' => 'ϫ',
+  'Ϭ' => 'ϭ',
+  'Ϯ' => 'ϯ',
+  'ϴ' => 'θ',
+  'Ϸ' => 'ϸ',
+  'Ϲ' => 'ϲ',
+  'Ϻ' => 'ϻ',
+  'Ͻ' => 'ͻ',
+  'Ͼ' => 'ͼ',
+  'Ͽ' => 'ͽ',
+  'Ѐ' => 'ѐ',
+  'Ё' => 'ё',
+  'Ђ' => 'ђ',
+  'Ѓ' => 'ѓ',
+  'Є' => 'є',
+  'Ѕ' => 'ѕ',
+  'І' => 'і',
+  'Ї' => 'ї',
+  'Ј' => 'ј',
+  'Љ' => 'љ',
+  'Њ' => 'њ',
+  'Ћ' => 'ћ',
+  'Ќ' => 'ќ',
+  'Ѝ' => 'ѝ',
+  'Ў' => 'ў',
+  'Џ' => 'џ',
+  'А' => 'а',
+  'Б' => 'б',
+  'В' => 'в',
+  'Г' => 'г',
+  'Д' => 'д',
+  'Е' => 'е',
+  'Ж' => 'ж',
+  'З' => 'з',
+  'И' => 'и',
+  'Й' => 'й',
+  'К' => 'к',
+  'Л' => 'л',
+  'М' => 'м',
+  'Н' => 'н',
+  'О' => 'о',
+  'П' => 'п',
+  'Р' => 'р',
+  'С' => 'с',
+  'Т' => 'т',
+  'У' => 'у',
+  'Ф' => 'ф',
+  'Х' => 'х',
+  'Ц' => 'ц',
+  'Ч' => 'ч',
+  'Ш' => 'ш',
+  'Щ' => 'щ',
+  'Ъ' => 'ъ',
+  'Ы' => 'ы',
+  'Ь' => 'ь',
+  'Э' => 'э',
+  'Ю' => 'ю',
+  'Я' => 'я',
+  'Ѡ' => 'ѡ',
+  'Ѣ' => 'ѣ',
+  'Ѥ' => 'ѥ',
+  'Ѧ' => 'ѧ',
+  'Ѩ' => 'ѩ',
+  'Ѫ' => 'ѫ',
+  'Ѭ' => 'ѭ',
+  'Ѯ' => 'ѯ',
+  'Ѱ' => 'ѱ',
+  'Ѳ' => 'ѳ',
+  'Ѵ' => 'ѵ',
+  'Ѷ' => 'ѷ',
+  'Ѹ' => 'ѹ',
+  'Ѻ' => 'ѻ',
+  'Ѽ' => 'ѽ',
+  'Ѿ' => 'ѿ',
+  'Ҁ' => 'ҁ',
+  'Ҋ' => 'ҋ',
+  'Ҍ' => 'ҍ',
+  'Ҏ' => 'ҏ',
+  'Ґ' => 'ґ',
+  'Ғ' => 'ғ',
+  'Ҕ' => 'ҕ',
+  'Җ' => 'җ',
+  'Ҙ' => 'ҙ',
+  'Қ' => 'қ',
+  'Ҝ' => 'ҝ',
+  'Ҟ' => 'ҟ',
+  'Ҡ' => 'ҡ',
+  'Ң' => 'ң',
+  'Ҥ' => 'ҥ',
+  'Ҧ' => 'ҧ',
+  'Ҩ' => 'ҩ',
+  'Ҫ' => 'ҫ',
+  'Ҭ' => 'ҭ',
+  'Ү' => 'ү',
+  'Ұ' => 'ұ',
+  'Ҳ' => 'ҳ',
+  'Ҵ' => 'ҵ',
+  'Ҷ' => 'ҷ',
+  'Ҹ' => 'ҹ',
+  'Һ' => 'һ',
+  'Ҽ' => 'ҽ',
+  'Ҿ' => 'ҿ',
+  'Ӏ' => 'ӏ',
+  'Ӂ' => 'ӂ',
+  'Ӄ' => 'ӄ',
+  'Ӆ' => 'ӆ',
+  'Ӈ' => 'ӈ',
+  'Ӊ' => 'ӊ',
+  'Ӌ' => 'ӌ',
+  'Ӎ' => 'ӎ',
+  'Ӑ' => 'ӑ',
+  'Ӓ' => 'ӓ',
+  'Ӕ' => 'ӕ',
+  'Ӗ' => 'ӗ',
+  'Ә' => 'ә',
+  'Ӛ' => 'ӛ',
+  'Ӝ' => 'ӝ',
+  'Ӟ' => 'ӟ',
+  'Ӡ' => 'ӡ',
+  'Ӣ' => 'ӣ',
+  'Ӥ' => 'ӥ',
+  'Ӧ' => 'ӧ',
+  'Ө' => 'ө',
+  'Ӫ' => 'ӫ',
+  'Ӭ' => 'ӭ',
+  'Ӯ' => 'ӯ',
+  'Ӱ' => 'ӱ',
+  'Ӳ' => 'ӳ',
+  'Ӵ' => 'ӵ',
+  'Ӷ' => 'ӷ',
+  'Ӹ' => 'ӹ',
+  'Ӻ' => 'ӻ',
+  'Ӽ' => 'ӽ',
+  'Ӿ' => 'ӿ',
+  'Ԁ' => 'ԁ',
+  'Ԃ' => 'ԃ',
+  'Ԅ' => 'ԅ',
+  'Ԇ' => 'ԇ',
+  'Ԉ' => 'ԉ',
+  'Ԋ' => 'ԋ',
+  'Ԍ' => 'ԍ',
+  'Ԏ' => 'ԏ',
+  'Ԑ' => 'ԑ',
+  'Ԓ' => 'ԓ',
+  'Ԕ' => 'ԕ',
+  'Ԗ' => 'ԗ',
+  'Ԙ' => 'ԙ',
+  'Ԛ' => 'ԛ',
+  'Ԝ' => 'ԝ',
+  'Ԟ' => 'ԟ',
+  'Ԡ' => 'ԡ',
+  'Ԣ' => 'ԣ',
+  'Ԥ' => 'ԥ',
+  'Ԧ' => 'ԧ',
+  'Ԩ' => 'ԩ',
+  'Ԫ' => 'ԫ',
+  'Ԭ' => 'ԭ',
+  'Ԯ' => 'ԯ',
+  'Ա' => 'ա',
+  'Բ' => 'բ',
+  'Գ' => 'գ',
+  'Դ' => 'դ',
+  'Ե' => 'ե',
+  'Զ' => 'զ',
+  'Է' => 'է',
+  'Ը' => 'ը',
+  'Թ' => 'թ',
+  'Ժ' => 'ժ',
+  'Ի' => 'ի',
+  'Լ' => 'լ',
+  'Խ' => 'խ',
+  'Ծ' => 'ծ',
+  'Կ' => 'կ',
+  'Հ' => 'հ',
+  'Ձ' => 'ձ',
+  'Ղ' => 'ղ',
+  'Ճ' => 'ճ',
+  'Մ' => 'մ',
+  'Յ' => 'յ',
+  'Ն' => 'ն',
+  'Շ' => 'շ',
+  'Ո' => 'ո',
+  'Չ' => 'չ',
+  'Պ' => 'պ',
+  'Ջ' => 'ջ',
+  'Ռ' => 'ռ',
+  'Ս' => 'ս',
+  'Վ' => 'վ',
+  'Տ' => 'տ',
+  'Ր' => 'ր',
+  'Ց' => 'ց',
+  'Ւ' => 'ւ',
+  'Փ' => 'փ',
+  'Ք' => 'ք',
+  'Օ' => 'օ',
+  'Ֆ' => 'ֆ',
+  'Ⴀ' => 'ⴀ',
+  'Ⴁ' => 'ⴁ',
+  'Ⴂ' => 'ⴂ',
+  'Ⴃ' => 'ⴃ',
+  'Ⴄ' => 'ⴄ',
+  'Ⴅ' => 'ⴅ',
+  'Ⴆ' => 'ⴆ',
+  'Ⴇ' => 'ⴇ',
+  'Ⴈ' => 'ⴈ',
+  'Ⴉ' => 'ⴉ',
+  'Ⴊ' => 'ⴊ',
+  'Ⴋ' => 'ⴋ',
+  'Ⴌ' => 'ⴌ',
+  'Ⴍ' => 'ⴍ',
+  'Ⴎ' => 'ⴎ',
+  'Ⴏ' => 'ⴏ',
+  'Ⴐ' => 'ⴐ',
+  'Ⴑ' => 'ⴑ',
+  'Ⴒ' => 'ⴒ',
+  'Ⴓ' => 'ⴓ',
+  'Ⴔ' => 'ⴔ',
+  'Ⴕ' => 'ⴕ',
+  'Ⴖ' => 'ⴖ',
+  'Ⴗ' => 'ⴗ',
+  'Ⴘ' => 'ⴘ',
+  'Ⴙ' => 'ⴙ',
+  'Ⴚ' => 'ⴚ',
+  'Ⴛ' => 'ⴛ',
+  'Ⴜ' => 'ⴜ',
+  'Ⴝ' => 'ⴝ',
+  'Ⴞ' => 'ⴞ',
+  'Ⴟ' => 'ⴟ',
+  'Ⴠ' => 'ⴠ',
+  'Ⴡ' => 'ⴡ',
+  'Ⴢ' => 'ⴢ',
+  'Ⴣ' => 'ⴣ',
+  'Ⴤ' => 'ⴤ',
+  'Ⴥ' => 'ⴥ',
+  'Ⴧ' => 'ⴧ',
+  'Ⴭ' => 'ⴭ',
+  'Ḁ' => 'ḁ',
+  'Ḃ' => 'ḃ',
+  'Ḅ' => 'ḅ',
+  'Ḇ' => 'ḇ',
+  'Ḉ' => 'ḉ',
+  'Ḋ' => 'ḋ',
+  'Ḍ' => 'ḍ',
+  'Ḏ' => 'ḏ',
+  'Ḑ' => 'ḑ',
+  'Ḓ' => 'ḓ',
+  'Ḕ' => 'ḕ',
+  'Ḗ' => 'ḗ',
+  'Ḙ' => 'ḙ',
+  'Ḛ' => 'ḛ',
+  'Ḝ' => 'ḝ',
+  'Ḟ' => 'ḟ',
+  'Ḡ' => 'ḡ',
+  'Ḣ' => 'ḣ',
+  'Ḥ' => 'ḥ',
+  'Ḧ' => 'ḧ',
+  'Ḩ' => 'ḩ',
+  'Ḫ' => 'ḫ',
+  'Ḭ' => 'ḭ',
+  'Ḯ' => 'ḯ',
+  'Ḱ' => 'ḱ',
+  'Ḳ' => 'ḳ',
+  'Ḵ' => 'ḵ',
+  'Ḷ' => 'ḷ',
+  'Ḹ' => 'ḹ',
+  'Ḻ' => 'ḻ',
+  'Ḽ' => 'ḽ',
+  'Ḿ' => 'ḿ',
+  'Ṁ' => 'ṁ',
+  'Ṃ' => 'ṃ',
+  'Ṅ' => 'ṅ',
+  'Ṇ' => 'ṇ',
+  'Ṉ' => 'ṉ',
+  'Ṋ' => 'ṋ',
+  'Ṍ' => 'ṍ',
+  'Ṏ' => 'ṏ',
+  'Ṑ' => 'ṑ',
+  'Ṓ' => 'ṓ',
+  'Ṕ' => 'ṕ',
+  'Ṗ' => 'ṗ',
+  'Ṙ' => 'ṙ',
+  'Ṛ' => 'ṛ',
+  'Ṝ' => 'ṝ',
+  'Ṟ' => 'ṟ',
+  'Ṡ' => 'ṡ',
+  'Ṣ' => 'ṣ',
+  'Ṥ' => 'ṥ',
+  'Ṧ' => 'ṧ',
+  'Ṩ' => 'ṩ',
+  'Ṫ' => 'ṫ',
+  'Ṭ' => 'ṭ',
+  'Ṯ' => 'ṯ',
+  'Ṱ' => 'ṱ',
+  'Ṳ' => 'ṳ',
+  'Ṵ' => 'ṵ',
+  'Ṷ' => 'ṷ',
+  'Ṹ' => 'ṹ',
+  'Ṻ' => 'ṻ',
+  'Ṽ' => 'ṽ',
+  'Ṿ' => 'ṿ',
+  'Ẁ' => 'ẁ',
+  'Ẃ' => 'ẃ',
+  'Ẅ' => 'ẅ',
+  'Ẇ' => 'ẇ',
+  'Ẉ' => 'ẉ',
+  'Ẋ' => 'ẋ',
+  'Ẍ' => 'ẍ',
+  'Ẏ' => 'ẏ',
+  'Ẑ' => 'ẑ',
+  'Ẓ' => 'ẓ',
+  'Ẕ' => 'ẕ',
+  'ẞ' => 'ß',
+  'Ạ' => 'ạ',
+  'Ả' => 'ả',
+  'Ấ' => 'ấ',
+  'Ầ' => 'ầ',
+  'Ẩ' => 'ẩ',
+  'Ẫ' => 'ẫ',
+  'Ậ' => 'ậ',
+  'Ắ' => 'ắ',
+  'Ằ' => 'ằ',
+  'Ẳ' => 'ẳ',
+  'Ẵ' => 'ẵ',
+  'Ặ' => 'ặ',
+  'Ẹ' => 'ẹ',
+  'Ẻ' => 'ẻ',
+  'Ẽ' => 'ẽ',
+  'Ế' => 'ế',
+  'Ề' => 'ề',
+  'Ể' => 'ể',
+  'Ễ' => 'ễ',
+  'Ệ' => 'ệ',
+  'Ỉ' => 'ỉ',
+  'Ị' => 'ị',
+  'Ọ' => 'ọ',
+  'Ỏ' => 'ỏ',
+  'Ố' => 'ố',
+  'Ồ' => 'ồ',
+  'Ổ' => 'ổ',
+  'Ỗ' => 'ỗ',
+  'Ộ' => 'ộ',
+  'Ớ' => 'ớ',
+  'Ờ' => 'ờ',
+  'Ở' => 'ở',
+  'Ỡ' => 'ỡ',
+  'Ợ' => 'ợ',
+  'Ụ' => 'ụ',
+  'Ủ' => 'ủ',
+  'Ứ' => 'ứ',
+  'Ừ' => 'ừ',
+  'Ử' => 'ử',
+  'Ữ' => 'ữ',
+  'Ự' => 'ự',
+  'Ỳ' => 'ỳ',
+  'Ỵ' => 'ỵ',
+  'Ỷ' => 'ỷ',
+  'Ỹ' => 'ỹ',
+  'Ỻ' => 'ỻ',
+  'Ỽ' => 'ỽ',
+  'Ỿ' => 'ỿ',
+  'Ἀ' => 'ἀ',
+  'Ἁ' => 'ἁ',
+  'Ἂ' => 'ἂ',
+  'Ἃ' => 'ἃ',
+  'Ἄ' => 'ἄ',
+  'Ἅ' => 'ἅ',
+  'Ἆ' => 'ἆ',
+  'Ἇ' => 'ἇ',
+  'Ἐ' => 'ἐ',
+  'Ἑ' => 'ἑ',
+  'Ἒ' => 'ἒ',
+  'Ἓ' => 'ἓ',
+  'Ἔ' => 'ἔ',
+  'Ἕ' => 'ἕ',
+  'Ἠ' => 'ἠ',
+  'Ἡ' => 'ἡ',
+  'Ἢ' => 'ἢ',
+  'Ἣ' => 'ἣ',
+  'Ἤ' => 'ἤ',
+  'Ἥ' => 'ἥ',
+  'Ἦ' => 'ἦ',
+  'Ἧ' => 'ἧ',
+  'Ἰ' => 'ἰ',
+  'Ἱ' => 'ἱ',
+  'Ἲ' => 'ἲ',
+  'Ἳ' => 'ἳ',
+  'Ἴ' => 'ἴ',
+  'Ἵ' => 'ἵ',
+  'Ἶ' => 'ἶ',
+  'Ἷ' => 'ἷ',
+  'Ὀ' => 'ὀ',
+  'Ὁ' => 'ὁ',
+  'Ὂ' => 'ὂ',
+  'Ὃ' => 'ὃ',
+  'Ὄ' => 'ὄ',
+  'Ὅ' => 'ὅ',
+  'Ὑ' => 'ὑ',
+  'Ὓ' => 'ὓ',
+  'Ὕ' => 'ὕ',
+  'Ὗ' => 'ὗ',
+  'Ὠ' => 'ὠ',
+  'Ὡ' => 'ὡ',
+  'Ὢ' => 'ὢ',
+  'Ὣ' => 'ὣ',
+  'Ὤ' => 'ὤ',
+  'Ὥ' => 'ὥ',
+  'Ὦ' => 'ὦ',
+  'Ὧ' => 'ὧ',
+  'ᾈ' => 'ᾀ',
+  'ᾉ' => 'ᾁ',
+  'ᾊ' => 'ᾂ',
+  'ᾋ' => 'ᾃ',
+  'ᾌ' => 'ᾄ',
+  'ᾍ' => 'ᾅ',
+  'ᾎ' => 'ᾆ',
+  'ᾏ' => 'ᾇ',
+  'ᾘ' => 'ᾐ',
+  'ᾙ' => 'ᾑ',
+  'ᾚ' => 'ᾒ',
+  'ᾛ' => 'ᾓ',
+  'ᾜ' => 'ᾔ',
+  'ᾝ' => 'ᾕ',
+  'ᾞ' => 'ᾖ',
+  'ᾟ' => 'ᾗ',
+  'ᾨ' => 'ᾠ',
+  'ᾩ' => 'ᾡ',
+  'ᾪ' => 'ᾢ',
+  'ᾫ' => 'ᾣ',
+  'ᾬ' => 'ᾤ',
+  'ᾭ' => 'ᾥ',
+  'ᾮ' => 'ᾦ',
+  'ᾯ' => 'ᾧ',
+  'Ᾰ' => 'ᾰ',
+  'Ᾱ' => 'ᾱ',
+  'Ὰ' => 'ὰ',
+  'Ά' => 'ά',
+  'ᾼ' => 'ᾳ',
+  'Ὲ' => 'ὲ',
+  'Έ' => 'έ',
+  'Ὴ' => 'ὴ',
+  'Ή' => 'ή',
+  'ῌ' => 'ῃ',
+  'Ῐ' => 'ῐ',
+  'Ῑ' => 'ῑ',
+  'Ὶ' => 'ὶ',
+  'Ί' => 'ί',
+  'Ῠ' => 'ῠ',
+  'Ῡ' => 'ῡ',
+  'Ὺ' => 'ὺ',
+  'Ύ' => 'ύ',
+  'Ῥ' => 'ῥ',
+  'Ὸ' => 'ὸ',
+  'Ό' => 'ό',
+  'Ὼ' => 'ὼ',
+  'Ώ' => 'ώ',
+  'ῼ' => 'ῳ',
+  'Ω' => 'ω',
+  'K' => 'k',
+  'Å' => 'å',
+  'Ⅎ' => 'ⅎ',
+  'Ⅰ' => 'ⅰ',
+  'Ⅱ' => 'ⅱ',
+  'Ⅲ' => 'ⅲ',
+  'Ⅳ' => 'ⅳ',
+  'Ⅴ' => 'ⅴ',
+  'Ⅵ' => 'ⅵ',
+  'Ⅶ' => 'ⅶ',
+  'Ⅷ' => 'ⅷ',
+  'Ⅸ' => 'ⅸ',
+  'Ⅹ' => 'ⅹ',
+  'Ⅺ' => 'ⅺ',
+  'Ⅻ' => 'ⅻ',
+  'Ⅼ' => 'ⅼ',
+  'Ⅽ' => 'ⅽ',
+  'Ⅾ' => 'ⅾ',
+  'Ⅿ' => 'ⅿ',
+  'Ↄ' => 'ↄ',
+  'Ⓐ' => 'ⓐ',
+  'Ⓑ' => 'ⓑ',
+  'Ⓒ' => 'ⓒ',
+  'Ⓓ' => 'ⓓ',
+  'Ⓔ' => 'ⓔ',
+  'Ⓕ' => 'ⓕ',
+  'Ⓖ' => 'ⓖ',
+  'Ⓗ' => 'ⓗ',
+  'Ⓘ' => 'ⓘ',
+  'Ⓙ' => 'ⓙ',
+  'Ⓚ' => 'ⓚ',
+  'Ⓛ' => 'ⓛ',
+  'Ⓜ' => 'ⓜ',
+  'Ⓝ' => 'ⓝ',
+  'Ⓞ' => 'ⓞ',
+  'Ⓟ' => 'ⓟ',
+  'Ⓠ' => 'ⓠ',
+  'Ⓡ' => 'ⓡ',
+  'Ⓢ' => 'ⓢ',
+  'Ⓣ' => 'ⓣ',
+  'Ⓤ' => 'ⓤ',
+  'Ⓥ' => 'ⓥ',
+  'Ⓦ' => 'ⓦ',
+  'Ⓧ' => 'ⓧ',
+  'Ⓨ' => 'ⓨ',
+  'Ⓩ' => 'ⓩ',
+  'Ⰰ' => 'ⰰ',
+  'Ⰱ' => 'ⰱ',
+  'Ⰲ' => 'ⰲ',
+  'Ⰳ' => 'ⰳ',
+  'Ⰴ' => 'ⰴ',
+  'Ⰵ' => 'ⰵ',
+  'Ⰶ' => 'ⰶ',
+  'Ⰷ' => 'ⰷ',
+  'Ⰸ' => 'ⰸ',
+  'Ⰹ' => 'ⰹ',
+  'Ⰺ' => 'ⰺ',
+  'Ⰻ' => 'ⰻ',
+  'Ⰼ' => 'ⰼ',
+  'Ⰽ' => 'ⰽ',
+  'Ⰾ' => 'ⰾ',
+  'Ⰿ' => 'ⰿ',
+  'Ⱀ' => 'ⱀ',
+  'Ⱁ' => 'ⱁ',
+  'Ⱂ' => 'ⱂ',
+  'Ⱃ' => 'ⱃ',
+  'Ⱄ' => 'ⱄ',
+  'Ⱅ' => 'ⱅ',
+  'Ⱆ' => 'ⱆ',
+  'Ⱇ' => 'ⱇ',
+  'Ⱈ' => 'ⱈ',
+  'Ⱉ' => 'ⱉ',
+  'Ⱊ' => 'ⱊ',
+  'Ⱋ' => 'ⱋ',
+  'Ⱌ' => 'ⱌ',
+  'Ⱍ' => 'ⱍ',
+  'Ⱎ' => 'ⱎ',
+  'Ⱏ' => 'ⱏ',
+  'Ⱐ' => 'ⱐ',
+  'Ⱑ' => 'ⱑ',
+  'Ⱒ' => 'ⱒ',
+  'Ⱓ' => 'ⱓ',
+  'Ⱔ' => 'ⱔ',
+  'Ⱕ' => 'ⱕ',
+  'Ⱖ' => 'ⱖ',
+  'Ⱗ' => 'ⱗ',
+  'Ⱘ' => 'ⱘ',
+  'Ⱙ' => 'ⱙ',
+  'Ⱚ' => 'ⱚ',
+  'Ⱛ' => 'ⱛ',
+  'Ⱜ' => 'ⱜ',
+  'Ⱝ' => 'ⱝ',
+  'Ⱞ' => 'ⱞ',
+  'Ⱡ' => 'ⱡ',
+  'Ɫ' => 'ɫ',
+  'Ᵽ' => 'ᵽ',
+  'Ɽ' => 'ɽ',
+  'Ⱨ' => 'ⱨ',
+  'Ⱪ' => 'ⱪ',
+  'Ⱬ' => 'ⱬ',
+  'Ɑ' => 'ɑ',
+  'Ɱ' => 'ɱ',
+  'Ɐ' => 'ɐ',
+  'Ɒ' => 'ɒ',
+  'Ⱳ' => 'ⱳ',
+  'Ⱶ' => 'ⱶ',
+  'Ȿ' => 'ȿ',
+  'Ɀ' => 'ɀ',
+  'Ⲁ' => 'ⲁ',
+  'Ⲃ' => 'ⲃ',
+  'Ⲅ' => 'ⲅ',
+  'Ⲇ' => 'ⲇ',
+  'Ⲉ' => 'ⲉ',
+  'Ⲋ' => 'ⲋ',
+  'Ⲍ' => 'ⲍ',
+  'Ⲏ' => 'ⲏ',
+  'Ⲑ' => 'ⲑ',
+  'Ⲓ' => 'ⲓ',
+  'Ⲕ' => 'ⲕ',
+  'Ⲗ' => 'ⲗ',
+  'Ⲙ' => 'ⲙ',
+  'Ⲛ' => 'ⲛ',
+  'Ⲝ' => 'ⲝ',
+  'Ⲟ' => 'ⲟ',
+  'Ⲡ' => 'ⲡ',
+  'Ⲣ' => 'ⲣ',
+  'Ⲥ' => 'ⲥ',
+  'Ⲧ' => 'ⲧ',
+  'Ⲩ' => 'ⲩ',
+  'Ⲫ' => 'ⲫ',
+  'Ⲭ' => 'ⲭ',
+  'Ⲯ' => 'ⲯ',
+  'Ⲱ' => 'ⲱ',
+  'Ⲳ' => 'ⲳ',
+  'Ⲵ' => 'ⲵ',
+  'Ⲷ' => 'ⲷ',
+  'Ⲹ' => 'ⲹ',
+  'Ⲻ' => 'ⲻ',
+  'Ⲽ' => 'ⲽ',
+  'Ⲿ' => 'ⲿ',
+  'Ⳁ' => 'ⳁ',
+  'Ⳃ' => 'ⳃ',
+  'Ⳅ' => 'ⳅ',
+  'Ⳇ' => 'ⳇ',
+  'Ⳉ' => 'ⳉ',
+  'Ⳋ' => 'ⳋ',
+  'Ⳍ' => 'ⳍ',
+  'Ⳏ' => 'ⳏ',
+  'Ⳑ' => 'ⳑ',
+  'Ⳓ' => 'ⳓ',
+  'Ⳕ' => 'ⳕ',
+  'Ⳗ' => 'ⳗ',
+  'Ⳙ' => 'ⳙ',
+  'Ⳛ' => 'ⳛ',
+  'Ⳝ' => 'ⳝ',
+  'Ⳟ' => 'ⳟ',
+  'Ⳡ' => 'ⳡ',
+  'Ⳣ' => 'ⳣ',
+  'Ⳬ' => 'ⳬ',
+  'Ⳮ' => 'ⳮ',
+  'Ⳳ' => 'ⳳ',
+  'Ꙁ' => 'ꙁ',
+  'Ꙃ' => 'ꙃ',
+  'Ꙅ' => 'ꙅ',
+  'Ꙇ' => 'ꙇ',
+  'Ꙉ' => 'ꙉ',
+  'Ꙋ' => 'ꙋ',
+  'Ꙍ' => 'ꙍ',
+  'Ꙏ' => 'ꙏ',
+  'Ꙑ' => 'ꙑ',
+  'Ꙓ' => 'ꙓ',
+  'Ꙕ' => 'ꙕ',
+  'Ꙗ' => 'ꙗ',
+  'Ꙙ' => 'ꙙ',
+  'Ꙛ' => 'ꙛ',
+  'Ꙝ' => 'ꙝ',
+  'Ꙟ' => 'ꙟ',
+  'Ꙡ' => 'ꙡ',
+  'Ꙣ' => 'ꙣ',
+  'Ꙥ' => 'ꙥ',
+  'Ꙧ' => 'ꙧ',
+  'Ꙩ' => 'ꙩ',
+  'Ꙫ' => 'ꙫ',
+  'Ꙭ' => 'ꙭ',
+  'Ꚁ' => 'ꚁ',
+  'Ꚃ' => 'ꚃ',
+  'Ꚅ' => 'ꚅ',
+  'Ꚇ' => 'ꚇ',
+  'Ꚉ' => 'ꚉ',
+  'Ꚋ' => 'ꚋ',
+  'Ꚍ' => 'ꚍ',
+  'Ꚏ' => 'ꚏ',
+  'Ꚑ' => 'ꚑ',
+  'Ꚓ' => 'ꚓ',
+  'Ꚕ' => 'ꚕ',
+  'Ꚗ' => 'ꚗ',
+  'Ꚙ' => 'ꚙ',
+  'Ꚛ' => 'ꚛ',
+  'Ꜣ' => 'ꜣ',
+  'Ꜥ' => 'ꜥ',
+  'Ꜧ' => 'ꜧ',
+  'Ꜩ' => 'ꜩ',
+  'Ꜫ' => 'ꜫ',
+  'Ꜭ' => 'ꜭ',
+  'Ꜯ' => 'ꜯ',
+  'Ꜳ' => 'ꜳ',
+  'Ꜵ' => 'ꜵ',
+  'Ꜷ' => 'ꜷ',
+  'Ꜹ' => 'ꜹ',
+  'Ꜻ' => 'ꜻ',
+  'Ꜽ' => 'ꜽ',
+  'Ꜿ' => 'ꜿ',
+  'Ꝁ' => 'ꝁ',
+  'Ꝃ' => 'ꝃ',
+  'Ꝅ' => 'ꝅ',
+  'Ꝇ' => 'ꝇ',
+  'Ꝉ' => 'ꝉ',
+  'Ꝋ' => 'ꝋ',
+  'Ꝍ' => 'ꝍ',
+  'Ꝏ' => 'ꝏ',
+  'Ꝑ' => 'ꝑ',
+  'Ꝓ' => 'ꝓ',
+  'Ꝕ' => 'ꝕ',
+  'Ꝗ' => 'ꝗ',
+  'Ꝙ' => 'ꝙ',
+  'Ꝛ' => 'ꝛ',
+  'Ꝝ' => 'ꝝ',
+  'Ꝟ' => 'ꝟ',
+  'Ꝡ' => 'ꝡ',
+  'Ꝣ' => 'ꝣ',
+  'Ꝥ' => 'ꝥ',
+  'Ꝧ' => 'ꝧ',
+  'Ꝩ' => 'ꝩ',
+  'Ꝫ' => 'ꝫ',
+  'Ꝭ' => 'ꝭ',
+  'Ꝯ' => 'ꝯ',
+  'Ꝺ' => 'ꝺ',
+  'Ꝼ' => 'ꝼ',
+  'Ᵹ' => 'ᵹ',
+  'Ꝿ' => 'ꝿ',
+  'Ꞁ' => 'ꞁ',
+  'Ꞃ' => 'ꞃ',
+  'Ꞅ' => 'ꞅ',
+  'Ꞇ' => 'ꞇ',
+  'Ꞌ' => 'ꞌ',
+  'Ɥ' => 'ɥ',
+  'Ꞑ' => 'ꞑ',
+  'Ꞓ' => 'ꞓ',
+  'Ꞗ' => 'ꞗ',
+  'Ꞙ' => 'ꞙ',
+  'Ꞛ' => 'ꞛ',
+  'Ꞝ' => 'ꞝ',
+  'Ꞟ' => 'ꞟ',
+  'Ꞡ' => 'ꞡ',
+  'Ꞣ' => 'ꞣ',
+  'Ꞥ' => 'ꞥ',
+  'Ꞧ' => 'ꞧ',
+  'Ꞩ' => 'ꞩ',
+  'Ɦ' => 'ɦ',
+  'Ɜ' => 'ɜ',
+  'Ɡ' => 'ɡ',
+  'Ɬ' => 'ɬ',
+  'Ʞ' => 'ʞ',
+  'Ʇ' => 'ʇ',
+  'A' => 'a',
+  'B' => 'b',
+  'C' => 'c',
+  'D' => 'd',
+  'E' => 'e',
+  'F' => 'f',
+  'G' => 'g',
+  'H' => 'h',
+  'I' => 'i',
+  'J' => 'j',
+  'K' => 'k',
+  'L' => 'l',
+  'M' => 'm',
+  'N' => 'n',
+  'O' => 'o',
+  'P' => 'p',
+  'Q' => 'q',
+  'R' => 'r',
+  'S' => 's',
+  'T' => 't',
+  'U' => 'u',
+  'V' => 'v',
+  'W' => 'w',
+  'X' => 'x',
+  'Y' => 'y',
+  'Z' => 'z',
+  '𐐀' => '𐐨',
+  '𐐁' => '𐐩',
+  '𐐂' => '𐐪',
+  '𐐃' => '𐐫',
+  '𐐄' => '𐐬',
+  '𐐅' => '𐐭',
+  '𐐆' => '𐐮',
+  '𐐇' => '𐐯',
+  '𐐈' => '𐐰',
+  '𐐉' => '𐐱',
+  '𐐊' => '𐐲',
+  '𐐋' => '𐐳',
+  '𐐌' => '𐐴',
+  '𐐍' => '𐐵',
+  '𐐎' => '𐐶',
+  '𐐏' => '𐐷',
+  '𐐐' => '𐐸',
+  '𐐑' => '𐐹',
+  '𐐒' => '𐐺',
+  '𐐓' => '𐐻',
+  '𐐔' => '𐐼',
+  '𐐕' => '𐐽',
+  '𐐖' => '𐐾',
+  '𐐗' => '𐐿',
+  '𐐘' => '𐑀',
+  '𐐙' => '𐑁',
+  '𐐚' => '𐑂',
+  '𐐛' => '𐑃',
+  '𐐜' => '𐑄',
+  '𐐝' => '𐑅',
+  '𐐞' => '𐑆',
+  '𐐟' => '𐑇',
+  '𐐠' => '𐑈',
+  '𐐡' => '𐑉',
+  '𐐢' => '𐑊',
+  '𐐣' => '𐑋',
+  '𐐤' => '𐑌',
+  '𐐥' => '𐑍',
+  '𐐦' => '𐑎',
+  '𐐧' => '𐑏',
+  '𑢠' => '𑣀',
+  '𑢡' => '𑣁',
+  '𑢢' => '𑣂',
+  '𑢣' => '𑣃',
+  '𑢤' => '𑣄',
+  '𑢥' => '𑣅',
+  '𑢦' => '𑣆',
+  '𑢧' => '𑣇',
+  '𑢨' => '𑣈',
+  '𑢩' => '𑣉',
+  '𑢪' => '𑣊',
+  '𑢫' => '𑣋',
+  '𑢬' => '𑣌',
+  '𑢭' => '𑣍',
+  '𑢮' => '𑣎',
+  '𑢯' => '𑣏',
+  '𑢰' => '𑣐',
+  '𑢱' => '𑣑',
+  '𑢲' => '𑣒',
+  '𑢳' => '𑣓',
+  '𑢴' => '𑣔',
+  '𑢵' => '𑣕',
+  '𑢶' => '𑣖',
+  '𑢷' => '𑣗',
+  '𑢸' => '𑣘',
+  '𑢹' => '𑣙',
+  '𑢺' => '𑣚',
+  '𑢻' => '𑣛',
+  '𑢼' => '𑣜',
+  '𑢽' => '𑣝',
+  '𑢾' => '𑣞',
+  '𑢿' => '𑣟',
+);
diff --git a/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php b/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php
new file mode 100755
index 0000000..2a8f6e7
--- /dev/null
+++ b/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php
@@ -0,0 +1,5 @@
+<?php
+
+// from Case_Ignorable in https://unicode.org/Public/UNIDATA/DerivedCoreProperties.txt
+
+return '/(?<![\x{0027}\x{002E}\x{003A}\x{005E}\x{0060}\x{00A8}\x{00AD}\x{00AF}\x{00B4}\x{00B7}\x{00B8}\x{02B0}-\x{02C1}\x{02C2}-\x{02C5}\x{02C6}-\x{02D1}\x{02D2}-\x{02DF}\x{02E0}-\x{02E4}\x{02E5}-\x{02EB}\x{02EC}\x{02ED}\x{02EE}\x{02EF}-\x{02FF}\x{0300}-\x{036F}\x{0374}\x{0375}\x{037A}\x{0384}-\x{0385}\x{0387}\x{0483}-\x{0487}\x{0488}-\x{0489}\x{0559}\x{0591}-\x{05BD}\x{05BF}\x{05C1}-\x{05C2}\x{05C4}-\x{05C5}\x{05C7}\x{05F4}\x{0600}-\x{0605}\x{0610}-\x{061A}\x{061C}\x{0640}\x{064B}-\x{065F}\x{0670}\x{06D6}-\x{06DC}\x{06DD}\x{06DF}-\x{06E4}\x{06E5}-\x{06E6}\x{06E7}-\x{06E8}\x{06EA}-\x{06ED}\x{070F}\x{0711}\x{0730}-\x{074A}\x{07A6}-\x{07B0}\x{07EB}-\x{07F3}\x{07F4}-\x{07F5}\x{07FA}\x{07FD}\x{0816}-\x{0819}\x{081A}\x{081B}-\x{0823}\x{0824}\x{0825}-\x{0827}\x{0828}\x{0829}-\x{082D}\x{0859}-\x{085B}\x{08D3}-\x{08E1}\x{08E2}\x{08E3}-\x{0902}\x{093A}\x{093C}\x{0941}-\x{0948}\x{094D}\x{0951}-\x{0957}\x{0962}-\x{0963}\x{0971}\x{0981}\x{09BC}\x{09C1}-\x{09C4}\x{09CD}\x{09E2}-\x{09E3}\x{09FE}\x{0A01}-\x{0A02}\x{0A3C}\x{0A41}-\x{0A42}\x{0A47}-\x{0A48}\x{0A4B}-\x{0A4D}\x{0A51}\x{0A70}-\x{0A71}\x{0A75}\x{0A81}-\x{0A82}\x{0ABC}\x{0AC1}-\x{0AC5}\x{0AC7}-\x{0AC8}\x{0ACD}\x{0AE2}-\x{0AE3}\x{0AFA}-\x{0AFF}\x{0B01}\x{0B3C}\x{0B3F}\x{0B41}-\x{0B44}\x{0B4D}\x{0B56}\x{0B62}-\x{0B63}\x{0B82}\x{0BC0}\x{0BCD}\x{0C00}\x{0C04}\x{0C3E}-\x{0C40}\x{0C46}-\x{0C48}\x{0C4A}-\x{0C4D}\x{0C55}-\x{0C56}\x{0C62}-\x{0C63}\x{0C81}\x{0CBC}\x{0CBF}\x{0CC6}\x{0CCC}-\x{0CCD}\x{0CE2}-\x{0CE3}\x{0D00}-\x{0D01}\x{0D3B}-\x{0D3C}\x{0D41}-\x{0D44}\x{0D4D}\x{0D62}-\x{0D63}\x{0DCA}\x{0DD2}-\x{0DD4}\x{0DD6}\x{0E31}\x{0E34}-\x{0E3A}\x{0E46}\x{0E47}-\x{0E4E}\x{0EB1}\x{0EB4}-\x{0EB9}\x{0EBB}-\x{0EBC}\x{0EC6}\x{0EC8}-\x{0ECD}\x{0F18}-\x{0F19}\x{0F35}\x{0F37}\x{0F39}\x{0F71}-\x{0F7E}\x{0F80}-\x{0F84}\x{0F86}-\x{0F87}\x{0F8D}-\x{0F97}\x{0F99}-\x{0FBC}\x{0FC6}\x{102D}-\x{1030}\x{1032}-\x{1037}\x{1039}-\x{103A}\x{103D}-\x{103E}\x{1058}-\x{1059}\x{105E}-\x{1060}\x{1071}-\x{1074}\x{1082}\x{1085}-\x{1086}\x{108D}\x{109D}\x{10FC}\x{135D}-\x{135F}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}-\x{1753}\x{1772}-\x{1773}\x{17B4}-\x{17B5}\x{17B7}-\x{17BD}\x{17C6}\x{17C9}-\x{17D3}\x{17D7}\x{17DD}\x{180B}-\x{180D}\x{180E}\x{1843}\x{1885}-\x{1886}\x{18A9}\x{1920}-\x{1922}\x{1927}-\x{1928}\x{1932}\x{1939}-\x{193B}\x{1A17}-\x{1A18}\x{1A1B}\x{1A56}\x{1A58}-\x{1A5E}\x{1A60}\x{1A62}\x{1A65}-\x{1A6C}\x{1A73}-\x{1A7C}\x{1A7F}\x{1AA7}\x{1AB0}-\x{1ABD}\x{1ABE}\x{1B00}-\x{1B03}\x{1B34}\x{1B36}-\x{1B3A}\x{1B3C}\x{1B42}\x{1B6B}-\x{1B73}\x{1B80}-\x{1B81}\x{1BA2}-\x{1BA5}\x{1BA8}-\x{1BA9}\x{1BAB}-\x{1BAD}\x{1BE6}\x{1BE8}-\x{1BE9}\x{1BED}\x{1BEF}-\x{1BF1}\x{1C2C}-\x{1C33}\x{1C36}-\x{1C37}\x{1C78}-\x{1C7D}\x{1CD0}-\x{1CD2}\x{1CD4}-\x{1CE0}\x{1CE2}-\x{1CE8}\x{1CED}\x{1CF4}\x{1CF8}-\x{1CF9}\x{1D2C}-\x{1D6A}\x{1D78}\x{1D9B}-\x{1DBF}\x{1DC0}-\x{1DF9}\x{1DFB}-\x{1DFF}\x{1FBD}\x{1FBF}-\x{1FC1}\x{1FCD}-\x{1FCF}\x{1FDD}-\x{1FDF}\x{1FED}-\x{1FEF}\x{1FFD}-\x{1FFE}\x{200B}-\x{200F}\x{2018}\x{2019}\x{2024}\x{2027}\x{202A}-\x{202E}\x{2060}-\x{2064}\x{2066}-\x{206F}\x{2071}\x{207F}\x{2090}-\x{209C}\x{20D0}-\x{20DC}\x{20DD}-\x{20E0}\x{20E1}\x{20E2}-\x{20E4}\x{20E5}-\x{20F0}\x{2C7C}-\x{2C7D}\x{2CEF}-\x{2CF1}\x{2D6F}\x{2D7F}\x{2DE0}-\x{2DFF}\x{2E2F}\x{3005}\x{302A}-\x{302D}\x{3031}-\x{3035}\x{303B}\x{3099}-\x{309A}\x{309B}-\x{309C}\x{309D}-\x{309E}\x{30FC}-\x{30FE}\x{A015}\x{A4F8}-\x{A4FD}\x{A60C}\x{A66F}\x{A670}-\x{A672}\x{A674}-\x{A67D}\x{A67F}\x{A69C}-\x{A69D}\x{A69E}-\x{A69F}\x{A6F0}-\x{A6F1}\x{A700}-\x{A716}\x{A717}-\x{A71F}\x{A720}-\x{A721}\x{A770}\x{A788}\x{A789}-\x{A78A}\x{A7F8}-\x{A7F9}\x{A802}\x{A806}\x{A80B}\x{A825}-\x{A826}\x{A8C4}-\x{A8C5}\x{A8E0}-\x{A8F1}\x{A8FF}\x{A926}-\x{A92D}\x{A947}-\x{A951}\x{A980}-\x{A982}\x{A9B3}\x{A9B6}-\x{A9B9}\x{A9BC}\x{A9CF}\x{A9E5}\x{A9E6}\x{AA29}-\x{AA2E}\x{AA31}-\x{AA32}\x{AA35}-\x{AA36}\x{AA43}\x{AA4C}\x{AA70}\x{AA7C}\x{AAB0}\x{AAB2}-\x{AAB4}\x{AAB7}-\x{AAB8}\x{AABE}-\x{AABF}\x{AAC1}\x{AADD}\x{AAEC}-\x{AAED}\x{AAF3}-\x{AAF4}\x{AAF6}\x{AB5B}\x{AB5C}-\x{AB5F}\x{ABE5}\x{ABE8}\x{ABED}\x{FB1E}\x{FBB2}-\x{FBC1}\x{FE00}-\x{FE0F}\x{FE13}\x{FE20}-\x{FE2F}\x{FE52}\x{FE55}\x{FEFF}\x{FF07}\x{FF0E}\x{FF1A}\x{FF3E}\x{FF40}\x{FF70}\x{FF9E}-\x{FF9F}\x{FFE3}\x{FFF9}-\x{FFFB}\x{101FD}\x{102E0}\x{10376}-\x{1037A}\x{10A01}-\x{10A03}\x{10A05}-\x{10A06}\x{10A0C}-\x{10A0F}\x{10A38}-\x{10A3A}\x{10A3F}\x{10AE5}-\x{10AE6}\x{10D24}-\x{10D27}\x{10F46}-\x{10F50}\x{11001}\x{11038}-\x{11046}\x{1107F}-\x{11081}\x{110B3}-\x{110B6}\x{110B9}-\x{110BA}\x{110BD}\x{110CD}\x{11100}-\x{11102}\x{11127}-\x{1112B}\x{1112D}-\x{11134}\x{11173}\x{11180}-\x{11181}\x{111B6}-\x{111BE}\x{111C9}-\x{111CC}\x{1122F}-\x{11231}\x{11234}\x{11236}-\x{11237}\x{1123E}\x{112DF}\x{112E3}-\x{112EA}\x{11300}-\x{11301}\x{1133B}-\x{1133C}\x{11340}\x{11366}-\x{1136C}\x{11370}-\x{11374}\x{11438}-\x{1143F}\x{11442}-\x{11444}\x{11446}\x{1145E}\x{114B3}-\x{114B8}\x{114BA}\x{114BF}-\x{114C0}\x{114C2}-\x{114C3}\x{115B2}-\x{115B5}\x{115BC}-\x{115BD}\x{115BF}-\x{115C0}\x{115DC}-\x{115DD}\x{11633}-\x{1163A}\x{1163D}\x{1163F}-\x{11640}\x{116AB}\x{116AD}\x{116B0}-\x{116B5}\x{116B7}\x{1171D}-\x{1171F}\x{11722}-\x{11725}\x{11727}-\x{1172B}\x{1182F}-\x{11837}\x{11839}-\x{1183A}\x{11A01}-\x{11A0A}\x{11A33}-\x{11A38}\x{11A3B}-\x{11A3E}\x{11A47}\x{11A51}-\x{11A56}\x{11A59}-\x{11A5B}\x{11A8A}-\x{11A96}\x{11A98}-\x{11A99}\x{11C30}-\x{11C36}\x{11C38}-\x{11C3D}\x{11C3F}\x{11C92}-\x{11CA7}\x{11CAA}-\x{11CB0}\x{11CB2}-\x{11CB3}\x{11CB5}-\x{11CB6}\x{11D31}-\x{11D36}\x{11D3A}\x{11D3C}-\x{11D3D}\x{11D3F}-\x{11D45}\x{11D47}\x{11D90}-\x{11D91}\x{11D95}\x{11D97}\x{11EF3}-\x{11EF4}\x{16AF0}-\x{16AF4}\x{16B30}-\x{16B36}\x{16B40}-\x{16B43}\x{16F8F}-\x{16F92}\x{16F93}-\x{16F9F}\x{16FE0}-\x{16FE1}\x{1BC9D}-\x{1BC9E}\x{1BCA0}-\x{1BCA3}\x{1D167}-\x{1D169}\x{1D173}-\x{1D17A}\x{1D17B}-\x{1D182}\x{1D185}-\x{1D18B}\x{1D1AA}-\x{1D1AD}\x{1D242}-\x{1D244}\x{1DA00}-\x{1DA36}\x{1DA3B}-\x{1DA6C}\x{1DA75}\x{1DA84}\x{1DA9B}-\x{1DA9F}\x{1DAA1}-\x{1DAAF}\x{1E000}-\x{1E006}\x{1E008}-\x{1E018}\x{1E01B}-\x{1E021}\x{1E023}-\x{1E024}\x{1E026}-\x{1E02A}\x{1E8D0}-\x{1E8D6}\x{1E944}-\x{1E94A}\x{1F3FB}-\x{1F3FF}\x{E0001}\x{E0020}-\x{E007F}\x{E0100}-\x{E01EF}])(\pL)(\pL*+)/u';
diff --git a/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php b/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php
new file mode 100755
index 0000000..b8103b2
--- /dev/null
+++ b/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php
@@ -0,0 +1,1104 @@
+<?php
+
+return array(
+  'a' => 'A',
+  'b' => 'B',
+  'c' => 'C',
+  'd' => 'D',
+  'e' => 'E',
+  'f' => 'F',
+  'g' => 'G',
+  'h' => 'H',
+  'i' => 'I',
+  'j' => 'J',
+  'k' => 'K',
+  'l' => 'L',
+  'm' => 'M',
+  'n' => 'N',
+  'o' => 'O',
+  'p' => 'P',
+  'q' => 'Q',
+  'r' => 'R',
+  's' => 'S',
+  't' => 'T',
+  'u' => 'U',
+  'v' => 'V',
+  'w' => 'W',
+  'x' => 'X',
+  'y' => 'Y',
+  'z' => 'Z',
+  'µ' => 'Μ',
+  'à' => 'À',
+  'á' => 'Á',
+  'â' => 'Â',
+  'ã' => 'Ã',
+  'ä' => 'Ä',
+  'å' => 'Å',
+  'æ' => 'Æ',
+  'ç' => 'Ç',
+  'è' => 'È',
+  'é' => 'É',
+  'ê' => 'Ê',
+  'ë' => 'Ë',
+  'ì' => 'Ì',
+  'í' => 'Í',
+  'î' => 'Î',
+  'ï' => 'Ï',
+  'ð' => 'Ð',
+  'ñ' => 'Ñ',
+  'ò' => 'Ò',
+  'ó' => 'Ó',
+  'ô' => 'Ô',
+  'õ' => 'Õ',
+  'ö' => 'Ö',
+  'ø' => 'Ø',
+  'ù' => 'Ù',
+  'ú' => 'Ú',
+  'û' => 'Û',
+  'ü' => 'Ü',
+  'ý' => 'Ý',
+  'þ' => 'Þ',
+  'ÿ' => 'Ÿ',
+  'ā' => 'Ā',
+  'ă' => 'Ă',
+  'ą' => 'Ą',
+  'ć' => 'Ć',
+  'ĉ' => 'Ĉ',
+  'ċ' => 'Ċ',
+  'č' => 'Č',
+  'ď' => 'Ď',
+  'đ' => 'Đ',
+  'ē' => 'Ē',
+  'ĕ' => 'Ĕ',
+  'ė' => 'Ė',
+  'ę' => 'Ę',
+  'ě' => 'Ě',
+  'ĝ' => 'Ĝ',
+  'ğ' => 'Ğ',
+  'ġ' => 'Ġ',
+  'ģ' => 'Ģ',
+  'ĥ' => 'Ĥ',
+  'ħ' => 'Ħ',
+  'ĩ' => 'Ĩ',
+  'ī' => 'Ī',
+  'ĭ' => 'Ĭ',
+  'į' => 'Į',
+  'ı' => 'I',
+  'ij' => 'IJ',
+  'ĵ' => 'Ĵ',
+  'ķ' => 'Ķ',
+  'ĺ' => 'Ĺ',
+  'ļ' => 'Ļ',
+  'ľ' => 'Ľ',
+  'ŀ' => 'Ŀ',
+  'ł' => 'Ł',
+  'ń' => 'Ń',
+  'ņ' => 'Ņ',
+  'ň' => 'Ň',
+  'ŋ' => 'Ŋ',
+  'ō' => 'Ō',
+  'ŏ' => 'Ŏ',
+  'ő' => 'Ő',
+  'œ' => 'Œ',
+  'ŕ' => 'Ŕ',
+  'ŗ' => 'Ŗ',
+  'ř' => 'Ř',
+  'ś' => 'Ś',
+  'ŝ' => 'Ŝ',
+  'ş' => 'Ş',
+  'š' => 'Š',
+  'ţ' => 'Ţ',
+  'ť' => 'Ť',
+  'ŧ' => 'Ŧ',
+  'ũ' => 'Ũ',
+  'ū' => 'Ū',
+  'ŭ' => 'Ŭ',
+  'ů' => 'Ů',
+  'ű' => 'Ű',
+  'ų' => 'Ų',
+  'ŵ' => 'Ŵ',
+  'ŷ' => 'Ŷ',
+  'ź' => 'Ź',
+  'ż' => 'Ż',
+  'ž' => 'Ž',
+  'ſ' => 'S',
+  'ƀ' => 'Ƀ',
+  'ƃ' => 'Ƃ',
+  'ƅ' => 'Ƅ',
+  'ƈ' => 'Ƈ',
+  'ƌ' => 'Ƌ',
+  'ƒ' => 'Ƒ',
+  'ƕ' => 'Ƕ',
+  'ƙ' => 'Ƙ',
+  'ƚ' => 'Ƚ',
+  'ƞ' => 'Ƞ',
+  'ơ' => 'Ơ',
+  'ƣ' => 'Ƣ',
+  'ƥ' => 'Ƥ',
+  'ƨ' => 'Ƨ',
+  'ƭ' => 'Ƭ',
+  'ư' => 'Ư',
+  'ƴ' => 'Ƴ',
+  'ƶ' => 'Ƶ',
+  'ƹ' => 'Ƹ',
+  'ƽ' => 'Ƽ',
+  'ƿ' => 'Ƿ',
+  'Dž' => 'DŽ',
+  'dž' => 'DŽ',
+  'Lj' => 'LJ',
+  'lj' => 'LJ',
+  'Nj' => 'NJ',
+  'nj' => 'NJ',
+  'ǎ' => 'Ǎ',
+  'ǐ' => 'Ǐ',
+  'ǒ' => 'Ǒ',
+  'ǔ' => 'Ǔ',
+  'ǖ' => 'Ǖ',
+  'ǘ' => 'Ǘ',
+  'ǚ' => 'Ǚ',
+  'ǜ' => 'Ǜ',
+  'ǝ' => 'Ǝ',
+  'ǟ' => 'Ǟ',
+  'ǡ' => 'Ǡ',
+  'ǣ' => 'Ǣ',
+  'ǥ' => 'Ǥ',
+  'ǧ' => 'Ǧ',
+  'ǩ' => 'Ǩ',
+  'ǫ' => 'Ǫ',
+  'ǭ' => 'Ǭ',
+  'ǯ' => 'Ǯ',
+  'Dz' => 'DZ',
+  'dz' => 'DZ',
+  'ǵ' => 'Ǵ',
+  'ǹ' => 'Ǹ',
+  'ǻ' => 'Ǻ',
+  'ǽ' => 'Ǽ',
+  'ǿ' => 'Ǿ',
+  'ȁ' => 'Ȁ',
+  'ȃ' => 'Ȃ',
+  'ȅ' => 'Ȅ',
+  'ȇ' => 'Ȇ',
+  'ȉ' => 'Ȉ',
+  'ȋ' => 'Ȋ',
+  'ȍ' => 'Ȍ',
+  'ȏ' => 'Ȏ',
+  'ȑ' => 'Ȑ',
+  'ȓ' => 'Ȓ',
+  'ȕ' => 'Ȕ',
+  'ȗ' => 'Ȗ',
+  'ș' => 'Ș',
+  'ț' => 'Ț',
+  'ȝ' => 'Ȝ',
+  'ȟ' => 'Ȟ',
+  'ȣ' => 'Ȣ',
+  'ȥ' => 'Ȥ',
+  'ȧ' => 'Ȧ',
+  'ȩ' => 'Ȩ',
+  'ȫ' => 'Ȫ',
+  'ȭ' => 'Ȭ',
+  'ȯ' => 'Ȯ',
+  'ȱ' => 'Ȱ',
+  'ȳ' => 'Ȳ',
+  'ȼ' => 'Ȼ',
+  'ȿ' => 'Ȿ',
+  'ɀ' => 'Ɀ',
+  'ɂ' => 'Ɂ',
+  'ɇ' => 'Ɇ',
+  'ɉ' => 'Ɉ',
+  'ɋ' => 'Ɋ',
+  'ɍ' => 'Ɍ',
+  'ɏ' => 'Ɏ',
+  'ɐ' => 'Ɐ',
+  'ɑ' => 'Ɑ',
+  'ɒ' => 'Ɒ',
+  'ɓ' => 'Ɓ',
+  'ɔ' => 'Ɔ',
+  'ɖ' => 'Ɖ',
+  'ɗ' => 'Ɗ',
+  'ə' => 'Ə',
+  'ɛ' => 'Ɛ',
+  'ɜ' => 'Ɜ',
+  'ɠ' => 'Ɠ',
+  'ɡ' => 'Ɡ',
+  'ɣ' => 'Ɣ',
+  'ɥ' => 'Ɥ',
+  'ɦ' => 'Ɦ',
+  'ɨ' => 'Ɨ',
+  'ɩ' => 'Ɩ',
+  'ɫ' => 'Ɫ',
+  'ɬ' => 'Ɬ',
+  'ɯ' => 'Ɯ',
+  'ɱ' => 'Ɱ',
+  'ɲ' => 'Ɲ',
+  'ɵ' => 'Ɵ',
+  'ɽ' => 'Ɽ',
+  'ʀ' => 'Ʀ',
+  'ʃ' => 'Ʃ',
+  'ʇ' => 'Ʇ',
+  'ʈ' => 'Ʈ',
+  'ʉ' => 'Ʉ',
+  'ʊ' => 'Ʊ',
+  'ʋ' => 'Ʋ',
+  'ʌ' => 'Ʌ',
+  'ʒ' => 'Ʒ',
+  'ʞ' => 'Ʞ',
+  'ͅ' => 'Ι',
+  'ͱ' => 'Ͱ',
+  'ͳ' => 'Ͳ',
+  'ͷ' => 'Ͷ',
+  'ͻ' => 'Ͻ',
+  'ͼ' => 'Ͼ',
+  'ͽ' => 'Ͽ',
+  'ά' => 'Ά',
+  'έ' => 'Έ',
+  'ή' => 'Ή',
+  'ί' => 'Ί',
+  'α' => 'Α',
+  'β' => 'Β',
+  'γ' => 'Γ',
+  'δ' => 'Δ',
+  'ε' => 'Ε',
+  'ζ' => 'Ζ',
+  'η' => 'Η',
+  'θ' => 'Θ',
+  'ι' => 'Ι',
+  'κ' => 'Κ',
+  'λ' => 'Λ',
+  'μ' => 'Μ',
+  'ν' => 'Ν',
+  'ξ' => 'Ξ',
+  'ο' => 'Ο',
+  'π' => 'Π',
+  'ρ' => 'Ρ',
+  'ς' => 'Σ',
+  'σ' => 'Σ',
+  'τ' => 'Τ',
+  'υ' => 'Υ',
+  'φ' => 'Φ',
+  'χ' => 'Χ',
+  'ψ' => 'Ψ',
+  'ω' => 'Ω',
+  'ϊ' => 'Ϊ',
+  'ϋ' => 'Ϋ',
+  'ό' => 'Ό',
+  'ύ' => 'Ύ',
+  'ώ' => 'Ώ',
+  'ϐ' => 'Β',
+  'ϑ' => 'Θ',
+  'ϕ' => 'Φ',
+  'ϖ' => 'Π',
+  'ϗ' => 'Ϗ',
+  'ϙ' => 'Ϙ',
+  'ϛ' => 'Ϛ',
+  'ϝ' => 'Ϝ',
+  'ϟ' => 'Ϟ',
+  'ϡ' => 'Ϡ',
+  'ϣ' => 'Ϣ',
+  'ϥ' => 'Ϥ',
+  'ϧ' => 'Ϧ',
+  'ϩ' => 'Ϩ',
+  'ϫ' => 'Ϫ',
+  'ϭ' => 'Ϭ',
+  'ϯ' => 'Ϯ',
+  'ϰ' => 'Κ',
+  'ϱ' => 'Ρ',
+  'ϲ' => 'Ϲ',
+  'ϳ' => 'Ϳ',
+  'ϵ' => 'Ε',
+  'ϸ' => 'Ϸ',
+  'ϻ' => 'Ϻ',
+  'а' => 'А',
+  'б' => 'Б',
+  'в' => 'В',
+  'г' => 'Г',
+  'д' => 'Д',
+  'е' => 'Е',
+  'ж' => 'Ж',
+  'з' => 'З',
+  'и' => 'И',
+  'й' => 'Й',
+  'к' => 'К',
+  'л' => 'Л',
+  'м' => 'М',
+  'н' => 'Н',
+  'о' => 'О',
+  'п' => 'П',
+  'р' => 'Р',
+  'с' => 'С',
+  'т' => 'Т',
+  'у' => 'У',
+  'ф' => 'Ф',
+  'х' => 'Х',
+  'ц' => 'Ц',
+  'ч' => 'Ч',
+  'ш' => 'Ш',
+  'щ' => 'Щ',
+  'ъ' => 'Ъ',
+  'ы' => 'Ы',
+  'ь' => 'Ь',
+  'э' => 'Э',
+  'ю' => 'Ю',
+  'я' => 'Я',
+  'ѐ' => 'Ѐ',
+  'ё' => 'Ё',
+  'ђ' => 'Ђ',
+  'ѓ' => 'Ѓ',
+  'є' => 'Є',
+  'ѕ' => 'Ѕ',
+  'і' => 'І',
+  'ї' => 'Ї',
+  'ј' => 'Ј',
+  'љ' => 'Љ',
+  'њ' => 'Њ',
+  'ћ' => 'Ћ',
+  'ќ' => 'Ќ',
+  'ѝ' => 'Ѝ',
+  'ў' => 'Ў',
+  'џ' => 'Џ',
+  'ѡ' => 'Ѡ',
+  'ѣ' => 'Ѣ',
+  'ѥ' => 'Ѥ',
+  'ѧ' => 'Ѧ',
+  'ѩ' => 'Ѩ',
+  'ѫ' => 'Ѫ',
+  'ѭ' => 'Ѭ',
+  'ѯ' => 'Ѯ',
+  'ѱ' => 'Ѱ',
+  'ѳ' => 'Ѳ',
+  'ѵ' => 'Ѵ',
+  'ѷ' => 'Ѷ',
+  'ѹ' => 'Ѹ',
+  'ѻ' => 'Ѻ',
+  'ѽ' => 'Ѽ',
+  'ѿ' => 'Ѿ',
+  'ҁ' => 'Ҁ',
+  'ҋ' => 'Ҋ',
+  'ҍ' => 'Ҍ',
+  'ҏ' => 'Ҏ',
+  'ґ' => 'Ґ',
+  'ғ' => 'Ғ',
+  'ҕ' => 'Ҕ',
+  'җ' => 'Җ',
+  'ҙ' => 'Ҙ',
+  'қ' => 'Қ',
+  'ҝ' => 'Ҝ',
+  'ҟ' => 'Ҟ',
+  'ҡ' => 'Ҡ',
+  'ң' => 'Ң',
+  'ҥ' => 'Ҥ',
+  'ҧ' => 'Ҧ',
+  'ҩ' => 'Ҩ',
+  'ҫ' => 'Ҫ',
+  'ҭ' => 'Ҭ',
+  'ү' => 'Ү',
+  'ұ' => 'Ұ',
+  'ҳ' => 'Ҳ',
+  'ҵ' => 'Ҵ',
+  'ҷ' => 'Ҷ',
+  'ҹ' => 'Ҹ',
+  'һ' => 'Һ',
+  'ҽ' => 'Ҽ',
+  'ҿ' => 'Ҿ',
+  'ӂ' => 'Ӂ',
+  'ӄ' => 'Ӄ',
+  'ӆ' => 'Ӆ',
+  'ӈ' => 'Ӈ',
+  'ӊ' => 'Ӊ',
+  'ӌ' => 'Ӌ',
+  'ӎ' => 'Ӎ',
+  'ӏ' => 'Ӏ',
+  'ӑ' => 'Ӑ',
+  'ӓ' => 'Ӓ',
+  'ӕ' => 'Ӕ',
+  'ӗ' => 'Ӗ',
+  'ә' => 'Ә',
+  'ӛ' => 'Ӛ',
+  'ӝ' => 'Ӝ',
+  'ӟ' => 'Ӟ',
+  'ӡ' => 'Ӡ',
+  'ӣ' => 'Ӣ',
+  'ӥ' => 'Ӥ',
+  'ӧ' => 'Ӧ',
+  'ө' => 'Ө',
+  'ӫ' => 'Ӫ',
+  'ӭ' => 'Ӭ',
+  'ӯ' => 'Ӯ',
+  'ӱ' => 'Ӱ',
+  'ӳ' => 'Ӳ',
+  'ӵ' => 'Ӵ',
+  'ӷ' => 'Ӷ',
+  'ӹ' => 'Ӹ',
+  'ӻ' => 'Ӻ',
+  'ӽ' => 'Ӽ',
+  'ӿ' => 'Ӿ',
+  'ԁ' => 'Ԁ',
+  'ԃ' => 'Ԃ',
+  'ԅ' => 'Ԅ',
+  'ԇ' => 'Ԇ',
+  'ԉ' => 'Ԉ',
+  'ԋ' => 'Ԋ',
+  'ԍ' => 'Ԍ',
+  'ԏ' => 'Ԏ',
+  'ԑ' => 'Ԑ',
+  'ԓ' => 'Ԓ',
+  'ԕ' => 'Ԕ',
+  'ԗ' => 'Ԗ',
+  'ԙ' => 'Ԙ',
+  'ԛ' => 'Ԛ',
+  'ԝ' => 'Ԝ',
+  'ԟ' => 'Ԟ',
+  'ԡ' => 'Ԡ',
+  'ԣ' => 'Ԣ',
+  'ԥ' => 'Ԥ',
+  'ԧ' => 'Ԧ',
+  'ԩ' => 'Ԩ',
+  'ԫ' => 'Ԫ',
+  'ԭ' => 'Ԭ',
+  'ԯ' => 'Ԯ',
+  'ա' => 'Ա',
+  'բ' => 'Բ',
+  'գ' => 'Գ',
+  'դ' => 'Դ',
+  'ե' => 'Ե',
+  'զ' => 'Զ',
+  'է' => 'Է',
+  'ը' => 'Ը',
+  'թ' => 'Թ',
+  'ժ' => 'Ժ',
+  'ի' => 'Ի',
+  'լ' => 'Լ',
+  'խ' => 'Խ',
+  'ծ' => 'Ծ',
+  'կ' => 'Կ',
+  'հ' => 'Հ',
+  'ձ' => 'Ձ',
+  'ղ' => 'Ղ',
+  'ճ' => 'Ճ',
+  'մ' => 'Մ',
+  'յ' => 'Յ',
+  'ն' => 'Ն',
+  'շ' => 'Շ',
+  'ո' => 'Ո',
+  'չ' => 'Չ',
+  'պ' => 'Պ',
+  'ջ' => 'Ջ',
+  'ռ' => 'Ռ',
+  'ս' => 'Ս',
+  'վ' => 'Վ',
+  'տ' => 'Տ',
+  'ր' => 'Ր',
+  'ց' => 'Ց',
+  'ւ' => 'Ւ',
+  'փ' => 'Փ',
+  'ք' => 'Ք',
+  'օ' => 'Օ',
+  'ֆ' => 'Ֆ',
+  'ᵹ' => 'Ᵹ',
+  'ᵽ' => 'Ᵽ',
+  'ḁ' => 'Ḁ',
+  'ḃ' => 'Ḃ',
+  'ḅ' => 'Ḅ',
+  'ḇ' => 'Ḇ',
+  'ḉ' => 'Ḉ',
+  'ḋ' => 'Ḋ',
+  'ḍ' => 'Ḍ',
+  'ḏ' => 'Ḏ',
+  'ḑ' => 'Ḑ',
+  'ḓ' => 'Ḓ',
+  'ḕ' => 'Ḕ',
+  'ḗ' => 'Ḗ',
+  'ḙ' => 'Ḙ',
+  'ḛ' => 'Ḛ',
+  'ḝ' => 'Ḝ',
+  'ḟ' => 'Ḟ',
+  'ḡ' => 'Ḡ',
+  'ḣ' => 'Ḣ',
+  'ḥ' => 'Ḥ',
+  'ḧ' => 'Ḧ',
+  'ḩ' => 'Ḩ',
+  'ḫ' => 'Ḫ',
+  'ḭ' => 'Ḭ',
+  'ḯ' => 'Ḯ',
+  'ḱ' => 'Ḱ',
+  'ḳ' => 'Ḳ',
+  'ḵ' => 'Ḵ',
+  'ḷ' => 'Ḷ',
+  'ḹ' => 'Ḹ',
+  'ḻ' => 'Ḻ',
+  'ḽ' => 'Ḽ',
+  'ḿ' => 'Ḿ',
+  'ṁ' => 'Ṁ',
+  'ṃ' => 'Ṃ',
+  'ṅ' => 'Ṅ',
+  'ṇ' => 'Ṇ',
+  'ṉ' => 'Ṉ',
+  'ṋ' => 'Ṋ',
+  'ṍ' => 'Ṍ',
+  'ṏ' => 'Ṏ',
+  'ṑ' => 'Ṑ',
+  'ṓ' => 'Ṓ',
+  'ṕ' => 'Ṕ',
+  'ṗ' => 'Ṗ',
+  'ṙ' => 'Ṙ',
+  'ṛ' => 'Ṛ',
+  'ṝ' => 'Ṝ',
+  'ṟ' => 'Ṟ',
+  'ṡ' => 'Ṡ',
+  'ṣ' => 'Ṣ',
+  'ṥ' => 'Ṥ',
+  'ṧ' => 'Ṧ',
+  'ṩ' => 'Ṩ',
+  'ṫ' => 'Ṫ',
+  'ṭ' => 'Ṭ',
+  'ṯ' => 'Ṯ',
+  'ṱ' => 'Ṱ',
+  'ṳ' => 'Ṳ',
+  'ṵ' => 'Ṵ',
+  'ṷ' => 'Ṷ',
+  'ṹ' => 'Ṹ',
+  'ṻ' => 'Ṻ',
+  'ṽ' => 'Ṽ',
+  'ṿ' => 'Ṿ',
+  'ẁ' => 'Ẁ',
+  'ẃ' => 'Ẃ',
+  'ẅ' => 'Ẅ',
+  'ẇ' => 'Ẇ',
+  'ẉ' => 'Ẉ',
+  'ẋ' => 'Ẋ',
+  'ẍ' => 'Ẍ',
+  'ẏ' => 'Ẏ',
+  'ẑ' => 'Ẑ',
+  'ẓ' => 'Ẓ',
+  'ẕ' => 'Ẕ',
+  'ẛ' => 'Ṡ',
+  'ạ' => 'Ạ',
+  'ả' => 'Ả',
+  'ấ' => 'Ấ',
+  'ầ' => 'Ầ',
+  'ẩ' => 'Ẩ',
+  'ẫ' => 'Ẫ',
+  'ậ' => 'Ậ',
+  'ắ' => 'Ắ',
+  'ằ' => 'Ằ',
+  'ẳ' => 'Ẳ',
+  'ẵ' => 'Ẵ',
+  'ặ' => 'Ặ',
+  'ẹ' => 'Ẹ',
+  'ẻ' => 'Ẻ',
+  'ẽ' => 'Ẽ',
+  'ế' => 'Ế',
+  'ề' => 'Ề',
+  'ể' => 'Ể',
+  'ễ' => 'Ễ',
+  'ệ' => 'Ệ',
+  'ỉ' => 'Ỉ',
+  'ị' => 'Ị',
+  'ọ' => 'Ọ',
+  'ỏ' => 'Ỏ',
+  'ố' => 'Ố',
+  'ồ' => 'Ồ',
+  'ổ' => 'Ổ',
+  'ỗ' => 'Ỗ',
+  'ộ' => 'Ộ',
+  'ớ' => 'Ớ',
+  'ờ' => 'Ờ',
+  'ở' => 'Ở',
+  'ỡ' => 'Ỡ',
+  'ợ' => 'Ợ',
+  'ụ' => 'Ụ',
+  'ủ' => 'Ủ',
+  'ứ' => 'Ứ',
+  'ừ' => 'Ừ',
+  'ử' => 'Ử',
+  'ữ' => 'Ữ',
+  'ự' => 'Ự',
+  'ỳ' => 'Ỳ',
+  'ỵ' => 'Ỵ',
+  'ỷ' => 'Ỷ',
+  'ỹ' => 'Ỹ',
+  'ỻ' => 'Ỻ',
+  'ỽ' => 'Ỽ',
+  'ỿ' => 'Ỿ',
+  'ἀ' => 'Ἀ',
+  'ἁ' => 'Ἁ',
+  'ἂ' => 'Ἂ',
+  'ἃ' => 'Ἃ',
+  'ἄ' => 'Ἄ',
+  'ἅ' => 'Ἅ',
+  'ἆ' => 'Ἆ',
+  'ἇ' => 'Ἇ',
+  'ἐ' => 'Ἐ',
+  'ἑ' => 'Ἑ',
+  'ἒ' => 'Ἒ',
+  'ἓ' => 'Ἓ',
+  'ἔ' => 'Ἔ',
+  'ἕ' => 'Ἕ',
+  'ἠ' => 'Ἠ',
+  'ἡ' => 'Ἡ',
+  'ἢ' => 'Ἢ',
+  'ἣ' => 'Ἣ',
+  'ἤ' => 'Ἤ',
+  'ἥ' => 'Ἥ',
+  'ἦ' => 'Ἦ',
+  'ἧ' => 'Ἧ',
+  'ἰ' => 'Ἰ',
+  'ἱ' => 'Ἱ',
+  'ἲ' => 'Ἲ',
+  'ἳ' => 'Ἳ',
+  'ἴ' => 'Ἴ',
+  'ἵ' => 'Ἵ',
+  'ἶ' => 'Ἶ',
+  'ἷ' => 'Ἷ',
+  'ὀ' => 'Ὀ',
+  'ὁ' => 'Ὁ',
+  'ὂ' => 'Ὂ',
+  'ὃ' => 'Ὃ',
+  'ὄ' => 'Ὄ',
+  'ὅ' => 'Ὅ',
+  'ὑ' => 'Ὑ',
+  'ὓ' => 'Ὓ',
+  'ὕ' => 'Ὕ',
+  'ὗ' => 'Ὗ',
+  'ὠ' => 'Ὠ',
+  'ὡ' => 'Ὡ',
+  'ὢ' => 'Ὢ',
+  'ὣ' => 'Ὣ',
+  'ὤ' => 'Ὤ',
+  'ὥ' => 'Ὥ',
+  'ὦ' => 'Ὦ',
+  'ὧ' => 'Ὧ',
+  'ὰ' => 'Ὰ',
+  'ά' => 'Ά',
+  'ὲ' => 'Ὲ',
+  'έ' => 'Έ',
+  'ὴ' => 'Ὴ',
+  'ή' => 'Ή',
+  'ὶ' => 'Ὶ',
+  'ί' => 'Ί',
+  'ὸ' => 'Ὸ',
+  'ό' => 'Ό',
+  'ὺ' => 'Ὺ',
+  'ύ' => 'Ύ',
+  'ὼ' => 'Ὼ',
+  'ώ' => 'Ώ',
+  'ᾀ' => 'ᾈ',
+  'ᾁ' => 'ᾉ',
+  'ᾂ' => 'ᾊ',
+  'ᾃ' => 'ᾋ',
+  'ᾄ' => 'ᾌ',
+  'ᾅ' => 'ᾍ',
+  'ᾆ' => 'ᾎ',
+  'ᾇ' => 'ᾏ',
+  'ᾐ' => 'ᾘ',
+  'ᾑ' => 'ᾙ',
+  'ᾒ' => 'ᾚ',
+  'ᾓ' => 'ᾛ',
+  'ᾔ' => 'ᾜ',
+  'ᾕ' => 'ᾝ',
+  'ᾖ' => 'ᾞ',
+  'ᾗ' => 'ᾟ',
+  'ᾠ' => 'ᾨ',
+  'ᾡ' => 'ᾩ',
+  'ᾢ' => 'ᾪ',
+  'ᾣ' => 'ᾫ',
+  'ᾤ' => 'ᾬ',
+  'ᾥ' => 'ᾭ',
+  'ᾦ' => 'ᾮ',
+  'ᾧ' => 'ᾯ',
+  'ᾰ' => 'Ᾰ',
+  'ᾱ' => 'Ᾱ',
+  'ᾳ' => 'ᾼ',
+  'ι' => 'Ι',
+  'ῃ' => 'ῌ',
+  'ῐ' => 'Ῐ',
+  'ῑ' => 'Ῑ',
+  'ῠ' => 'Ῠ',
+  'ῡ' => 'Ῡ',
+  'ῥ' => 'Ῥ',
+  'ῳ' => 'ῼ',
+  'ⅎ' => 'Ⅎ',
+  'ⅰ' => 'Ⅰ',
+  'ⅱ' => 'Ⅱ',
+  'ⅲ' => 'Ⅲ',
+  'ⅳ' => 'Ⅳ',
+  'ⅴ' => 'Ⅴ',
+  'ⅵ' => 'Ⅵ',
+  'ⅶ' => 'Ⅶ',
+  'ⅷ' => 'Ⅷ',
+  'ⅸ' => 'Ⅸ',
+  'ⅹ' => 'Ⅹ',
+  'ⅺ' => 'Ⅺ',
+  'ⅻ' => 'Ⅻ',
+  'ⅼ' => 'Ⅼ',
+  'ⅽ' => 'Ⅽ',
+  'ⅾ' => 'Ⅾ',
+  'ⅿ' => 'Ⅿ',
+  'ↄ' => 'Ↄ',
+  'ⓐ' => 'Ⓐ',
+  'ⓑ' => 'Ⓑ',
+  'ⓒ' => 'Ⓒ',
+  'ⓓ' => 'Ⓓ',
+  'ⓔ' => 'Ⓔ',
+  'ⓕ' => 'Ⓕ',
+  'ⓖ' => 'Ⓖ',
+  'ⓗ' => 'Ⓗ',
+  'ⓘ' => 'Ⓘ',
+  'ⓙ' => 'Ⓙ',
+  'ⓚ' => 'Ⓚ',
+  'ⓛ' => 'Ⓛ',
+  'ⓜ' => 'Ⓜ',
+  'ⓝ' => 'Ⓝ',
+  'ⓞ' => 'Ⓞ',
+  'ⓟ' => 'Ⓟ',
+  'ⓠ' => 'Ⓠ',
+  'ⓡ' => 'Ⓡ',
+  'ⓢ' => 'Ⓢ',
+  'ⓣ' => 'Ⓣ',
+  'ⓤ' => 'Ⓤ',
+  'ⓥ' => 'Ⓥ',
+  'ⓦ' => 'Ⓦ',
+  'ⓧ' => 'Ⓧ',
+  'ⓨ' => 'Ⓨ',
+  'ⓩ' => 'Ⓩ',
+  'ⰰ' => 'Ⰰ',
+  'ⰱ' => 'Ⰱ',
+  'ⰲ' => 'Ⰲ',
+  'ⰳ' => 'Ⰳ',
+  'ⰴ' => 'Ⰴ',
+  'ⰵ' => 'Ⰵ',
+  'ⰶ' => 'Ⰶ',
+  'ⰷ' => 'Ⰷ',
+  'ⰸ' => 'Ⰸ',
+  'ⰹ' => 'Ⰹ',
+  'ⰺ' => 'Ⰺ',
+  'ⰻ' => 'Ⰻ',
+  'ⰼ' => 'Ⰼ',
+  'ⰽ' => 'Ⰽ',
+  'ⰾ' => 'Ⰾ',
+  'ⰿ' => 'Ⰿ',
+  'ⱀ' => 'Ⱀ',
+  'ⱁ' => 'Ⱁ',
+  'ⱂ' => 'Ⱂ',
+  'ⱃ' => 'Ⱃ',
+  'ⱄ' => 'Ⱄ',
+  'ⱅ' => 'Ⱅ',
+  'ⱆ' => 'Ⱆ',
+  'ⱇ' => 'Ⱇ',
+  'ⱈ' => 'Ⱈ',
+  'ⱉ' => 'Ⱉ',
+  'ⱊ' => 'Ⱊ',
+  'ⱋ' => 'Ⱋ',
+  'ⱌ' => 'Ⱌ',
+  'ⱍ' => 'Ⱍ',
+  'ⱎ' => 'Ⱎ',
+  'ⱏ' => 'Ⱏ',
+  'ⱐ' => 'Ⱐ',
+  'ⱑ' => 'Ⱑ',
+  'ⱒ' => 'Ⱒ',
+  'ⱓ' => 'Ⱓ',
+  'ⱔ' => 'Ⱔ',
+  'ⱕ' => 'Ⱕ',
+  'ⱖ' => 'Ⱖ',
+  'ⱗ' => 'Ⱗ',
+  'ⱘ' => 'Ⱘ',
+  'ⱙ' => 'Ⱙ',
+  'ⱚ' => 'Ⱚ',
+  'ⱛ' => 'Ⱛ',
+  'ⱜ' => 'Ⱜ',
+  'ⱝ' => 'Ⱝ',
+  'ⱞ' => 'Ⱞ',
+  'ⱡ' => 'Ⱡ',
+  'ⱥ' => 'Ⱥ',
+  'ⱦ' => 'Ⱦ',
+  'ⱨ' => 'Ⱨ',
+  'ⱪ' => 'Ⱪ',
+  'ⱬ' => 'Ⱬ',
+  'ⱳ' => 'Ⱳ',
+  'ⱶ' => 'Ⱶ',
+  'ⲁ' => 'Ⲁ',
+  'ⲃ' => 'Ⲃ',
+  'ⲅ' => 'Ⲅ',
+  'ⲇ' => 'Ⲇ',
+  'ⲉ' => 'Ⲉ',
+  'ⲋ' => 'Ⲋ',
+  'ⲍ' => 'Ⲍ',
+  'ⲏ' => 'Ⲏ',
+  'ⲑ' => 'Ⲑ',
+  'ⲓ' => 'Ⲓ',
+  'ⲕ' => 'Ⲕ',
+  'ⲗ' => 'Ⲗ',
+  'ⲙ' => 'Ⲙ',
+  'ⲛ' => 'Ⲛ',
+  'ⲝ' => 'Ⲝ',
+  'ⲟ' => 'Ⲟ',
+  'ⲡ' => 'Ⲡ',
+  'ⲣ' => 'Ⲣ',
+  'ⲥ' => 'Ⲥ',
+  'ⲧ' => 'Ⲧ',
+  'ⲩ' => 'Ⲩ',
+  'ⲫ' => 'Ⲫ',
+  'ⲭ' => 'Ⲭ',
+  'ⲯ' => 'Ⲯ',
+  'ⲱ' => 'Ⲱ',
+  'ⲳ' => 'Ⲳ',
+  'ⲵ' => 'Ⲵ',
+  'ⲷ' => 'Ⲷ',
+  'ⲹ' => 'Ⲹ',
+  'ⲻ' => 'Ⲻ',
+  'ⲽ' => 'Ⲽ',
+  'ⲿ' => 'Ⲿ',
+  'ⳁ' => 'Ⳁ',
+  'ⳃ' => 'Ⳃ',
+  'ⳅ' => 'Ⳅ',
+  'ⳇ' => 'Ⳇ',
+  'ⳉ' => 'Ⳉ',
+  'ⳋ' => 'Ⳋ',
+  'ⳍ' => 'Ⳍ',
+  'ⳏ' => 'Ⳏ',
+  'ⳑ' => 'Ⳑ',
+  'ⳓ' => 'Ⳓ',
+  'ⳕ' => 'Ⳕ',
+  'ⳗ' => 'Ⳗ',
+  'ⳙ' => 'Ⳙ',
+  'ⳛ' => 'Ⳛ',
+  'ⳝ' => 'Ⳝ',
+  'ⳟ' => 'Ⳟ',
+  'ⳡ' => 'Ⳡ',
+  'ⳣ' => 'Ⳣ',
+  'ⳬ' => 'Ⳬ',
+  'ⳮ' => 'Ⳮ',
+  'ⳳ' => 'Ⳳ',
+  'ⴀ' => 'Ⴀ',
+  'ⴁ' => 'Ⴁ',
+  'ⴂ' => 'Ⴂ',
+  'ⴃ' => 'Ⴃ',
+  'ⴄ' => 'Ⴄ',
+  'ⴅ' => 'Ⴅ',
+  'ⴆ' => 'Ⴆ',
+  'ⴇ' => 'Ⴇ',
+  'ⴈ' => 'Ⴈ',
+  'ⴉ' => 'Ⴉ',
+  'ⴊ' => 'Ⴊ',
+  'ⴋ' => 'Ⴋ',
+  'ⴌ' => 'Ⴌ',
+  'ⴍ' => 'Ⴍ',
+  'ⴎ' => 'Ⴎ',
+  'ⴏ' => 'Ⴏ',
+  'ⴐ' => 'Ⴐ',
+  'ⴑ' => 'Ⴑ',
+  'ⴒ' => 'Ⴒ',
+  'ⴓ' => 'Ⴓ',
+  'ⴔ' => 'Ⴔ',
+  'ⴕ' => 'Ⴕ',
+  'ⴖ' => 'Ⴖ',
+  'ⴗ' => 'Ⴗ',
+  'ⴘ' => 'Ⴘ',
+  'ⴙ' => 'Ⴙ',
+  'ⴚ' => 'Ⴚ',
+  'ⴛ' => 'Ⴛ',
+  'ⴜ' => 'Ⴜ',
+  'ⴝ' => 'Ⴝ',
+  'ⴞ' => 'Ⴞ',
+  'ⴟ' => 'Ⴟ',
+  'ⴠ' => 'Ⴠ',
+  'ⴡ' => 'Ⴡ',
+  'ⴢ' => 'Ⴢ',
+  'ⴣ' => 'Ⴣ',
+  'ⴤ' => 'Ⴤ',
+  'ⴥ' => 'Ⴥ',
+  'ⴧ' => 'Ⴧ',
+  'ⴭ' => 'Ⴭ',
+  'ꙁ' => 'Ꙁ',
+  'ꙃ' => 'Ꙃ',
+  'ꙅ' => 'Ꙅ',
+  'ꙇ' => 'Ꙇ',
+  'ꙉ' => 'Ꙉ',
+  'ꙋ' => 'Ꙋ',
+  'ꙍ' => 'Ꙍ',
+  'ꙏ' => 'Ꙏ',
+  'ꙑ' => 'Ꙑ',
+  'ꙓ' => 'Ꙓ',
+  'ꙕ' => 'Ꙕ',
+  'ꙗ' => 'Ꙗ',
+  'ꙙ' => 'Ꙙ',
+  'ꙛ' => 'Ꙛ',
+  'ꙝ' => 'Ꙝ',
+  'ꙟ' => 'Ꙟ',
+  'ꙡ' => 'Ꙡ',
+  'ꙣ' => 'Ꙣ',
+  'ꙥ' => 'Ꙥ',
+  'ꙧ' => 'Ꙧ',
+  'ꙩ' => 'Ꙩ',
+  'ꙫ' => 'Ꙫ',
+  'ꙭ' => 'Ꙭ',
+  'ꚁ' => 'Ꚁ',
+  'ꚃ' => 'Ꚃ',
+  'ꚅ' => 'Ꚅ',
+  'ꚇ' => 'Ꚇ',
+  'ꚉ' => 'Ꚉ',
+  'ꚋ' => 'Ꚋ',
+  'ꚍ' => 'Ꚍ',
+  'ꚏ' => 'Ꚏ',
+  'ꚑ' => 'Ꚑ',
+  'ꚓ' => 'Ꚓ',
+  'ꚕ' => 'Ꚕ',
+  'ꚗ' => 'Ꚗ',
+  'ꚙ' => 'Ꚙ',
+  'ꚛ' => 'Ꚛ',
+  'ꜣ' => 'Ꜣ',
+  'ꜥ' => 'Ꜥ',
+  'ꜧ' => 'Ꜧ',
+  'ꜩ' => 'Ꜩ',
+  'ꜫ' => 'Ꜫ',
+  'ꜭ' => 'Ꜭ',
+  'ꜯ' => 'Ꜯ',
+  'ꜳ' => 'Ꜳ',
+  'ꜵ' => 'Ꜵ',
+  'ꜷ' => 'Ꜷ',
+  'ꜹ' => 'Ꜹ',
+  'ꜻ' => 'Ꜻ',
+  'ꜽ' => 'Ꜽ',
+  'ꜿ' => 'Ꜿ',
+  'ꝁ' => 'Ꝁ',
+  'ꝃ' => 'Ꝃ',
+  'ꝅ' => 'Ꝅ',
+  'ꝇ' => 'Ꝇ',
+  'ꝉ' => 'Ꝉ',
+  'ꝋ' => 'Ꝋ',
+  'ꝍ' => 'Ꝍ',
+  'ꝏ' => 'Ꝏ',
+  'ꝑ' => 'Ꝑ',
+  'ꝓ' => 'Ꝓ',
+  'ꝕ' => 'Ꝕ',
+  'ꝗ' => 'Ꝗ',
+  'ꝙ' => 'Ꝙ',
+  'ꝛ' => 'Ꝛ',
+  'ꝝ' => 'Ꝝ',
+  'ꝟ' => 'Ꝟ',
+  'ꝡ' => 'Ꝡ',
+  'ꝣ' => 'Ꝣ',
+  'ꝥ' => 'Ꝥ',
+  'ꝧ' => 'Ꝧ',
+  'ꝩ' => 'Ꝩ',
+  'ꝫ' => 'Ꝫ',
+  'ꝭ' => 'Ꝭ',
+  'ꝯ' => 'Ꝯ',
+  'ꝺ' => 'Ꝺ',
+  'ꝼ' => 'Ꝼ',
+  'ꝿ' => 'Ꝿ',
+  'ꞁ' => 'Ꞁ',
+  'ꞃ' => 'Ꞃ',
+  'ꞅ' => 'Ꞅ',
+  'ꞇ' => 'Ꞇ',
+  'ꞌ' => 'Ꞌ',
+  'ꞑ' => 'Ꞑ',
+  'ꞓ' => 'Ꞓ',
+  'ꞗ' => 'Ꞗ',
+  'ꞙ' => 'Ꞙ',
+  'ꞛ' => 'Ꞛ',
+  'ꞝ' => 'Ꞝ',
+  'ꞟ' => 'Ꞟ',
+  'ꞡ' => 'Ꞡ',
+  'ꞣ' => 'Ꞣ',
+  'ꞥ' => 'Ꞥ',
+  'ꞧ' => 'Ꞧ',
+  'ꞩ' => 'Ꞩ',
+  'a' => 'A',
+  'b' => 'B',
+  'c' => 'C',
+  'd' => 'D',
+  'e' => 'E',
+  'f' => 'F',
+  'g' => 'G',
+  'h' => 'H',
+  'i' => 'I',
+  'j' => 'J',
+  'k' => 'K',
+  'l' => 'L',
+  'm' => 'M',
+  'n' => 'N',
+  'o' => 'O',
+  'p' => 'P',
+  'q' => 'Q',
+  'r' => 'R',
+  's' => 'S',
+  't' => 'T',
+  'u' => 'U',
+  'v' => 'V',
+  'w' => 'W',
+  'x' => 'X',
+  'y' => 'Y',
+  'z' => 'Z',
+  '𐐨' => '𐐀',
+  '𐐩' => '𐐁',
+  '𐐪' => '𐐂',
+  '𐐫' => '𐐃',
+  '𐐬' => '𐐄',
+  '𐐭' => '𐐅',
+  '𐐮' => '𐐆',
+  '𐐯' => '𐐇',
+  '𐐰' => '𐐈',
+  '𐐱' => '𐐉',
+  '𐐲' => '𐐊',
+  '𐐳' => '𐐋',
+  '𐐴' => '𐐌',
+  '𐐵' => '𐐍',
+  '𐐶' => '𐐎',
+  '𐐷' => '𐐏',
+  '𐐸' => '𐐐',
+  '𐐹' => '𐐑',
+  '𐐺' => '𐐒',
+  '𐐻' => '𐐓',
+  '𐐼' => '𐐔',
+  '𐐽' => '𐐕',
+  '𐐾' => '𐐖',
+  '𐐿' => '𐐗',
+  '𐑀' => '𐐘',
+  '𐑁' => '𐐙',
+  '𐑂' => '𐐚',
+  '𐑃' => '𐐛',
+  '𐑄' => '𐐜',
+  '𐑅' => '𐐝',
+  '𐑆' => '𐐞',
+  '𐑇' => '𐐟',
+  '𐑈' => '𐐠',
+  '𐑉' => '𐐡',
+  '𐑊' => '𐐢',
+  '𐑋' => '𐐣',
+  '𐑌' => '𐐤',
+  '𐑍' => '𐐥',
+  '𐑎' => '𐐦',
+  '𐑏' => '𐐧',
+  '𑣀' => '𑢠',
+  '𑣁' => '𑢡',
+  '𑣂' => '𑢢',
+  '𑣃' => '𑢣',
+  '𑣄' => '𑢤',
+  '𑣅' => '𑢥',
+  '𑣆' => '𑢦',
+  '𑣇' => '𑢧',
+  '𑣈' => '𑢨',
+  '𑣉' => '𑢩',
+  '𑣊' => '𑢪',
+  '𑣋' => '𑢫',
+  '𑣌' => '𑢬',
+  '𑣍' => '𑢭',
+  '𑣎' => '𑢮',
+  '𑣏' => '𑢯',
+  '𑣐' => '𑢰',
+  '𑣑' => '𑢱',
+  '𑣒' => '𑢲',
+  '𑣓' => '𑢳',
+  '𑣔' => '𑢴',
+  '𑣕' => '𑢵',
+  '𑣖' => '𑢶',
+  '𑣗' => '𑢷',
+  '𑣘' => '𑢸',
+  '𑣙' => '𑢹',
+  '𑣚' => '𑢺',
+  '𑣛' => '𑢻',
+  '𑣜' => '𑢼',
+  '𑣝' => '𑢽',
+  '𑣞' => '𑢾',
+  '𑣟' => '𑢿',
+);
diff --git a/vendor/symfony/polyfill-mbstring/bootstrap.php b/vendor/symfony/polyfill-mbstring/bootstrap.php
new file mode 100755
index 0000000..2fdcc5a
--- /dev/null
+++ b/vendor/symfony/polyfill-mbstring/bootstrap.php
@@ -0,0 +1,58 @@
+<?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.
+ */
+
+use Symfony\Polyfill\Mbstring as p;
+
+if (!function_exists('mb_strlen')) {
+    define('MB_CASE_UPPER', 0);
+    define('MB_CASE_LOWER', 1);
+    define('MB_CASE_TITLE', 2);
+
+    function mb_convert_encoding($s, $to, $from = null) { return p\Mbstring::mb_convert_encoding($s, $to, $from); }
+    function mb_decode_mimeheader($s) { return p\Mbstring::mb_decode_mimeheader($s); }
+    function mb_encode_mimeheader($s, $charset = null, $transferEnc = null, $lf = null, $indent = null) { return p\Mbstring::mb_encode_mimeheader($s, $charset, $transferEnc, $lf, $indent); }
+    function mb_decode_numericentity($s, $convmap, $enc = null) { return p\Mbstring::mb_decode_numericentity($s, $convmap, $enc); }
+    function mb_encode_numericentity($s, $convmap, $enc = null, $is_hex = false) { return p\Mbstring::mb_encode_numericentity($s, $convmap, $enc, $is_hex); }
+    function mb_convert_case($s, $mode, $enc = null) { return p\Mbstring::mb_convert_case($s, $mode, $enc); }
+    function mb_internal_encoding($enc = null) { return p\Mbstring::mb_internal_encoding($enc); }
+    function mb_language($lang = null) { return p\Mbstring::mb_language($lang); }
+    function mb_list_encodings() { return p\Mbstring::mb_list_encodings(); }
+    function mb_encoding_aliases($encoding) { return p\Mbstring::mb_encoding_aliases($encoding); }
+    function mb_check_encoding($var = null, $encoding = null) { return p\Mbstring::mb_check_encoding($var, $encoding); }
+    function mb_detect_encoding($str, $encodingList = null, $strict = false) { return p\Mbstring::mb_detect_encoding($str, $encodingList, $strict); }
+    function mb_detect_order($encodingList = null) { return p\Mbstring::mb_detect_order($encodingList); }
+    function mb_parse_str($s, &$result = array()) { parse_str($s, $result); }
+    function mb_strlen($s, $enc = null) { return p\Mbstring::mb_strlen($s, $enc); }
+    function mb_strpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strpos($s, $needle, $offset, $enc); }
+    function mb_strtolower($s, $enc = null) { return p\Mbstring::mb_strtolower($s, $enc); }
+    function mb_strtoupper($s, $enc = null) { return p\Mbstring::mb_strtoupper($s, $enc); }
+    function mb_substitute_character($char = null) { return p\Mbstring::mb_substitute_character($char); }
+    function mb_substr($s, $start, $length = 2147483647, $enc = null) { return p\Mbstring::mb_substr($s, $start, $length, $enc); }
+    function mb_stripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_stripos($s, $needle, $offset, $enc); }
+    function mb_stristr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_stristr($s, $needle, $part, $enc); }
+    function mb_strrchr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrchr($s, $needle, $part, $enc); }
+    function mb_strrichr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrichr($s, $needle, $part, $enc); }
+    function mb_strripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strripos($s, $needle, $offset, $enc); }
+    function mb_strrpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strrpos($s, $needle, $offset, $enc); }
+    function mb_strstr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strstr($s, $needle, $part, $enc); }
+    function mb_get_info($type = 'all') { return p\Mbstring::mb_get_info($type); }
+    function mb_http_output($enc = null) { return p\Mbstring::mb_http_output($enc); }
+    function mb_strwidth($s, $enc = null) { return p\Mbstring::mb_strwidth($s, $enc); }
+    function mb_substr_count($haystack, $needle, $enc = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $enc); }
+    function mb_output_handler($contents, $status) { return p\Mbstring::mb_output_handler($contents, $status); }
+    function mb_http_input($type = '') { return p\Mbstring::mb_http_input($type); }
+    function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) { return p\Mbstring::mb_convert_variables($toEncoding, $fromEncoding, $a, $b, $c, $d, $e, $f); }
+}
+if (!function_exists('mb_chr')) {
+    function mb_ord($s, $enc = null) { return p\Mbstring::mb_ord($s, $enc); }
+    function mb_chr($code, $enc = null) { return p\Mbstring::mb_chr($code, $enc); }
+    function mb_scrub($s, $enc = null) { $enc = null === $enc ? mb_internal_encoding() : $enc; return mb_convert_encoding($s, $enc, $enc); }
+}
diff --git a/vendor/symfony/polyfill-mbstring/composer.json b/vendor/symfony/polyfill-mbstring/composer.json
new file mode 100755
index 0000000..0bce087
--- /dev/null
+++ b/vendor/symfony/polyfill-mbstring/composer.json
@@ -0,0 +1,34 @@
+{
+    "name": "symfony/polyfill-mbstring",
+    "type": "library",
+    "description": "Symfony polyfill for the Mbstring extension",
+    "keywords": ["polyfill", "shim", "compatibility", "portable", "mbstring"],
+    "homepage": "https://symfony.com",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Nicolas Grekas",
+            "email": "p@tchwork.com"
+        },
+        {
+            "name": "Symfony Community",
+            "homepage": "https://symfony.com/contributors"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.3"
+    },
+    "autoload": {
+        "psr-4": { "Symfony\\Polyfill\\Mbstring\\": "" },
+        "files": [ "bootstrap.php" ]
+    },
+    "suggest": {
+        "ext-mbstring": "For best performance"
+    },
+    "minimum-stability": "dev",
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.11-dev"
+        }
+    }
+}
diff --git a/vendor/symfony/polyfill-php70/LICENSE b/vendor/symfony/polyfill-php70/LICENSE
new file mode 100755
index 0000000..4cd8bdd
--- /dev/null
+++ b/vendor/symfony/polyfill-php70/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015-2019 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.
diff --git a/vendor/symfony/polyfill-php70/Php70.php b/vendor/symfony/polyfill-php70/Php70.php
new file mode 100755
index 0000000..7f1ad08
--- /dev/null
+++ b/vendor/symfony/polyfill-php70/Php70.php
@@ -0,0 +1,74 @@
+<?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\Polyfill\Php70;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ *
+ * @internal
+ */
+final class Php70
+{
+    public static function intdiv($dividend, $divisor)
+    {
+        $dividend = self::intArg($dividend, __FUNCTION__, 1);
+        $divisor = self::intArg($divisor, __FUNCTION__, 2);
+
+        if (0 === $divisor) {
+            throw new \DivisionByZeroError('Division by zero');
+        }
+        if (-1 === $divisor && ~PHP_INT_MAX === $dividend) {
+            throw new \ArithmeticError('Division of PHP_INT_MIN by -1 is not an integer');
+        }
+
+        return ($dividend - ($dividend % $divisor)) / $divisor;
+    }
+
+    public static function preg_replace_callback_array(array $patterns, $subject, $limit = -1, &$count = 0)
+    {
+        $count = 0;
+        $result = (string) $subject;
+        if (0 === $limit = self::intArg($limit, __FUNCTION__, 3)) {
+            return $result;
+        }
+
+        foreach ($patterns as $pattern => $callback) {
+            $result = preg_replace_callback($pattern, $callback, $result, $limit, $c);
+            $count += $c;
+        }
+
+        return $result;
+    }
+
+    public static function error_clear_last()
+    {
+        static $handler;
+        if (!$handler) {
+            $handler = function () { return false; };
+        }
+        set_error_handler($handler);
+        @trigger_error('');
+        restore_error_handler();
+    }
+
+    private static function intArg($value, $caller, $pos)
+    {
+        if (\is_int($value)) {
+            return $value;
+        }
+        if (!\is_numeric($value) || PHP_INT_MAX <= ($value += 0) || ~PHP_INT_MAX >= $value) {
+            throw new \TypeError(sprintf('%s() expects parameter %d to be integer, %s given', $caller, $pos, \gettype($value)));
+        }
+
+        return (int) $value;
+    }
+}
diff --git a/vendor/symfony/polyfill-php70/README.md b/vendor/symfony/polyfill-php70/README.md
new file mode 100755
index 0000000..04988c6
--- /dev/null
+++ b/vendor/symfony/polyfill-php70/README.md
@@ -0,0 +1,28 @@
+Symfony Polyfill / Php70
+========================
+
+This component provides features unavailable in releases prior to PHP 7.0:
+
+- [`intdiv`](http://php.net/intdiv)
+- [`preg_replace_callback_array`](http://php.net/preg_replace_callback_array)
+- [`error_clear_last`](http://php.net/error_clear_last)
+- `random_bytes` and `random_int` (from [paragonie/random_compat](https://github.com/paragonie/random_compat))
+- [`*Error` throwable classes](http://php.net/Error)
+- [`PHP_INT_MIN`](http://php.net/manual/en/reserved.constants.php#constant.php-int-min)
+- `SessionUpdateTimestampHandlerInterface`
+
+More information can be found in the
+[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md).
+
+Compatibility notes
+===================
+
+To write portable code between PHP5 and PHP7, some care must be taken:
+- `\*Error` exceptions must be caught before `\Exception`;
+- after calling `error_clear_last()`, the result of `$e = error_get_last()` must be
+  verified using `isset($e['message'][0])` instead of `null !== $e`.
+
+License
+=======
+
+This library is released under the [MIT license](LICENSE).
diff --git a/vendor/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php b/vendor/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php
new file mode 100755
index 0000000..6819124
--- /dev/null
+++ b/vendor/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php
@@ -0,0 +1,5 @@
+<?php
+
+class ArithmeticError extends Error
+{
+}
diff --git a/vendor/symfony/polyfill-php70/Resources/stubs/AssertionError.php b/vendor/symfony/polyfill-php70/Resources/stubs/AssertionError.php
new file mode 100755
index 0000000..acb1250
--- /dev/null
+++ b/vendor/symfony/polyfill-php70/Resources/stubs/AssertionError.php
@@ -0,0 +1,5 @@
+<?php
+
+class AssertionError extends Error
+{
+}
diff --git a/vendor/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php b/vendor/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php
new file mode 100755
index 0000000..c99278b
--- /dev/null
+++ b/vendor/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php
@@ -0,0 +1,5 @@
+<?php
+
+class DivisionByZeroError extends Error
+{
+}
diff --git a/vendor/symfony/polyfill-php70/Resources/stubs/Error.php b/vendor/symfony/polyfill-php70/Resources/stubs/Error.php
new file mode 100755
index 0000000..405847f
--- /dev/null
+++ b/vendor/symfony/polyfill-php70/Resources/stubs/Error.php
@@ -0,0 +1,5 @@
+<?php
+
+class Error extends Exception
+{
+}
diff --git a/vendor/symfony/polyfill-php70/Resources/stubs/ParseError.php b/vendor/symfony/polyfill-php70/Resources/stubs/ParseError.php
new file mode 100755
index 0000000..2dd447d
--- /dev/null
+++ b/vendor/symfony/polyfill-php70/Resources/stubs/ParseError.php
@@ -0,0 +1,5 @@
+<?php
+
+class ParseError extends Error
+{
+}
diff --git a/vendor/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php b/vendor/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php
new file mode 100755
index 0000000..0cc02c8
--- /dev/null
+++ b/vendor/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php
@@ -0,0 +1,23 @@
+<?php
+
+interface SessionUpdateTimestampHandlerInterface
+{
+    /**
+     * Checks if a session identifier already exists or not.
+     *
+     * @param string $key
+     *
+     * @return bool
+     */
+    public function validateId($key);
+
+    /**
+     * Updates the timestamp of a session when its data didn't change.
+     *
+     * @param string $key
+     * @param string $val
+     *
+     * @return bool
+     */
+    public function updateTimestamp($key, $val);
+}
diff --git a/vendor/symfony/polyfill-php70/Resources/stubs/TypeError.php b/vendor/symfony/polyfill-php70/Resources/stubs/TypeError.php
new file mode 100755
index 0000000..2bed1b4
--- /dev/null
+++ b/vendor/symfony/polyfill-php70/Resources/stubs/TypeError.php
@@ -0,0 +1,5 @@
+<?php
+
+class TypeError extends Error
+{
+}
diff --git a/vendor/symfony/polyfill-php70/bootstrap.php b/vendor/symfony/polyfill-php70/bootstrap.php
new file mode 100755
index 0000000..445c398
--- /dev/null
+++ b/vendor/symfony/polyfill-php70/bootstrap.php
@@ -0,0 +1,27 @@
+<?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.
+ */
+
+use Symfony\Polyfill\Php70 as p;
+
+if (PHP_VERSION_ID < 70000) {
+    if (!defined('PHP_INT_MIN')) {
+        define('PHP_INT_MIN', ~PHP_INT_MAX);
+    }
+    if (!function_exists('intdiv')) {
+        function intdiv($dividend, $divisor) { return p\Php70::intdiv($dividend, $divisor); }
+    }
+    if (!function_exists('preg_replace_callback_array')) {
+        function preg_replace_callback_array(array $patterns, $subject, $limit = -1, &$count = 0) { return p\Php70::preg_replace_callback_array($patterns, $subject, $limit, $count); }
+    }
+    if (!function_exists('error_clear_last')) {
+        function error_clear_last() { return p\Php70::error_clear_last(); }
+    }
+}
diff --git a/vendor/symfony/polyfill-php70/composer.json b/vendor/symfony/polyfill-php70/composer.json
new file mode 100755
index 0000000..923f309
--- /dev/null
+++ b/vendor/symfony/polyfill-php70/composer.json
@@ -0,0 +1,33 @@
+{
+    "name": "symfony/polyfill-php70",
+    "type": "library",
+    "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions",
+    "keywords": ["polyfill", "shim", "compatibility", "portable"],
+    "homepage": "https://symfony.com",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Nicolas Grekas",
+            "email": "p@tchwork.com"
+        },
+        {
+            "name": "Symfony Community",
+            "homepage": "https://symfony.com/contributors"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.3",
+        "paragonie/random_compat": "~1.0|~2.0|~9.99"
+    },
+    "autoload": {
+        "psr-4": { "Symfony\\Polyfill\\Php70\\": "" },
+        "files": [ "bootstrap.php" ],
+        "classmap": [ "Resources/stubs" ]
+    },
+    "minimum-stability": "dev",
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.11-dev"
+        }
+    }
+}
diff --git a/vendor/symfony/polyfill-php72/LICENSE b/vendor/symfony/polyfill-php72/LICENSE
new file mode 100755
index 0000000..4cd8bdd
--- /dev/null
+++ b/vendor/symfony/polyfill-php72/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015-2019 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.
diff --git a/vendor/symfony/polyfill-php72/Php72.php b/vendor/symfony/polyfill-php72/Php72.php
new file mode 100755
index 0000000..d531e84
--- /dev/null
+++ b/vendor/symfony/polyfill-php72/Php72.php
@@ -0,0 +1,216 @@
+<?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\Polyfill\Php72;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
+ *
+ * @internal
+ */
+final class Php72
+{
+    private static $hashMask;
+
+    public static function utf8_encode($s)
+    {
+        $s .= $s;
+        $len = \strlen($s);
+
+        for ($i = $len >> 1, $j = 0; $i < $len; ++$i, ++$j) {
+            switch (true) {
+                case $s[$i] < "\x80": $s[$j] = $s[$i]; break;
+                case $s[$i] < "\xC0": $s[$j] = "\xC2"; $s[++$j] = $s[$i]; break;
+                default: $s[$j] = "\xC3"; $s[++$j] = \chr(\ord($s[$i]) - 64); break;
+            }
+        }
+
+        return substr($s, 0, $j);
+    }
+
+    public static function utf8_decode($s)
+    {
+        $s = (string) $s;
+        $len = \strlen($s);
+
+        for ($i = 0, $j = 0; $i < $len; ++$i, ++$j) {
+            switch ($s[$i] & "\xF0") {
+                case "\xC0":
+                case "\xD0":
+                    $c = (\ord($s[$i] & "\x1F") << 6) | \ord($s[++$i] & "\x3F");
+                    $s[$j] = $c < 256 ? \chr($c) : '?';
+                    break;
+
+                case "\xF0":
+                    ++$i;
+                    // no break
+
+                case "\xE0":
+                    $s[$j] = '?';
+                    $i += 2;
+                    break;
+
+                default:
+                    $s[$j] = $s[$i];
+            }
+        }
+
+        return substr($s, 0, $j);
+    }
+
+    public static function php_os_family()
+    {
+        if ('\\' === \DIRECTORY_SEPARATOR) {
+            return 'Windows';
+        }
+
+        $map = array(
+            'Darwin' => 'Darwin',
+            'DragonFly' => 'BSD',
+            'FreeBSD' => 'BSD',
+            'NetBSD' => 'BSD',
+            'OpenBSD' => 'BSD',
+            'Linux' => 'Linux',
+            'SunOS' => 'Solaris',
+        );
+
+        return isset($map[PHP_OS]) ? $map[PHP_OS] : 'Unknown';
+    }
+
+    public static function spl_object_id($object)
+    {
+        if (null === self::$hashMask) {
+            self::initHashMask();
+        }
+        if (null === $hash = spl_object_hash($object)) {
+            return;
+        }
+
+        return self::$hashMask ^ hexdec(substr($hash, 16 - \PHP_INT_SIZE, \PHP_INT_SIZE));
+    }
+
+    public static function sapi_windows_vt100_support($stream, $enable = null)
+    {
+        if (!\is_resource($stream)) {
+            trigger_error('sapi_windows_vt100_support() expects parameter 1 to be resource, '.\gettype($stream).' given', E_USER_WARNING);
+
+            return false;
+        }
+
+        $meta = stream_get_meta_data($stream);
+
+        if ('STDIO' !== $meta['stream_type']) {
+            trigger_error('sapi_windows_vt100_support() was not able to analyze the specified stream', E_USER_WARNING);
+
+            return false;
+        }
+
+        // We cannot actually disable vt100 support if it is set
+        if (false === $enable || !self::stream_isatty($stream)) {
+            return false;
+        }
+
+        // The native function does not apply to stdin
+        $meta = array_map('strtolower', $meta);
+        $stdin = 'php://stdin' === $meta['uri'] || 'php://fd/0' === $meta['uri'];
+
+        return !$stdin
+            && (false !== getenv('ANSICON')
+            || 'ON' === getenv('ConEmuANSI')
+            || 'xterm' === getenv('TERM')
+            || 'Hyper' === getenv('TERM_PROGRAM'));
+    }
+
+    public static function stream_isatty($stream)
+    {
+        if (!\is_resource($stream)) {
+            trigger_error('stream_isatty() expects parameter 1 to be resource, '.\gettype($stream).' given', E_USER_WARNING);
+
+            return false;
+        }
+
+        if ('\\' === \DIRECTORY_SEPARATOR) {
+            $stat = @fstat($stream);
+            // Check if formatted mode is S_IFCHR
+            return $stat ? 0020000 === ($stat['mode'] & 0170000) : false;
+        }
+
+        return \function_exists('posix_isatty') && @posix_isatty($stream);
+    }
+
+    private static function initHashMask()
+    {
+        $obj = (object) array();
+        self::$hashMask = -1;
+
+        // check if we are nested in an output buffering handler to prevent a fatal error with ob_start() below
+        $obFuncs = array('ob_clean', 'ob_end_clean', 'ob_flush', 'ob_end_flush', 'ob_get_contents', 'ob_get_flush');
+        foreach (debug_backtrace(\PHP_VERSION_ID >= 50400 ? DEBUG_BACKTRACE_IGNORE_ARGS : false) as $frame) {
+            if (isset($frame['function'][0]) && !isset($frame['class']) && 'o' === $frame['function'][0] && \in_array($frame['function'], $obFuncs)) {
+                $frame['line'] = 0;
+                break;
+            }
+        }
+        if (!empty($frame['line'])) {
+            ob_start();
+            debug_zval_dump($obj);
+            self::$hashMask = (int) substr(ob_get_clean(), 17);
+        }
+
+        self::$hashMask ^= hexdec(substr(spl_object_hash($obj), 16 - \PHP_INT_SIZE, \PHP_INT_SIZE));
+    }
+
+    public static function mb_chr($code, $encoding = null)
+    {
+        if (0x80 > $code %= 0x200000) {
+            $s = \chr($code);
+        } elseif (0x800 > $code) {
+            $s = \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F);
+        } elseif (0x10000 > $code) {
+            $s = \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F);
+        } else {
+            $s = \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F);
+        }
+
+        if ('UTF-8' !== $encoding) {
+            $s = mb_convert_encoding($s, $encoding, 'UTF-8');
+        }
+
+        return $s;
+    }
+
+    public static function mb_ord($s, $encoding = null)
+    {
+        if (null == $encoding) {
+            $s = mb_convert_encoding($s, 'UTF-8');
+        } elseif ('UTF-8' !== $encoding) {
+            $s = mb_convert_encoding($s, 'UTF-8', $encoding);
+        }
+
+        if (1 === \strlen($s)) {
+            return \ord($s);
+        }
+
+        $code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0;
+        if (0xF0 <= $code) {
+            return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80;
+        }
+        if (0xE0 <= $code) {
+            return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80;
+        }
+        if (0xC0 <= $code) {
+            return (($code - 0xC0) << 6) + $s[2] - 0x80;
+        }
+
+        return $code;
+    }
+}
diff --git a/vendor/symfony/polyfill-php72/README.md b/vendor/symfony/polyfill-php72/README.md
new file mode 100755
index 0000000..82c45f7
--- /dev/null
+++ b/vendor/symfony/polyfill-php72/README.md
@@ -0,0 +1,27 @@
+Symfony Polyfill / Php72
+========================
+
+This component provides functions added to PHP 7.2 core:
+
+- [`spl_object_id`](https://php.net/spl_object_id)
+- [`stream_isatty`](https://php.net/stream_isatty)
+
+On Windows only:
+
+- [`sapi_windows_vt100_support`](https://php.net/sapi_windows_vt100_support)
+
+Moved to core since 7.2 (was in the optional XML extension earlier):
+
+- [`utf8_encode`](https://php.net/utf8_encode)
+- [`utf8_decode`](https://php.net/utf8_decode)
+
+Also, it provides a constant added to PHP 7.2:
+- [`PHP_OS_FAMILY`](http://php.net/manual/en/reserved.constants.php#constant.php-os-family)
+
+More information can be found in the
+[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md).
+
+License
+=======
+
+This library is released under the [MIT license](LICENSE).
diff --git a/vendor/symfony/polyfill-php72/bootstrap.php b/vendor/symfony/polyfill-php72/bootstrap.php
new file mode 100755
index 0000000..519056d
--- /dev/null
+++ b/vendor/symfony/polyfill-php72/bootstrap.php
@@ -0,0 +1,36 @@
+<?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.
+ */
+
+use Symfony\Polyfill\Php72 as p;
+
+if (PHP_VERSION_ID < 70200) {
+    if ('\\' === DIRECTORY_SEPARATOR && !function_exists('sapi_windows_vt100_support')) {
+        function sapi_windows_vt100_support($stream, $enable = null) { return p\Php72::sapi_windows_vt100_support($stream, $enable); }
+    }
+    if (!function_exists('stream_isatty')) {
+        function stream_isatty($stream) { return p\Php72::stream_isatty($stream); }
+    }
+    if (!function_exists('utf8_encode')) {
+        function utf8_encode($s) { return p\Php72::utf8_encode($s); }
+        function utf8_decode($s) { return p\Php72::utf8_decode($s); }
+    }
+    if (!function_exists('spl_object_id')) {
+        function spl_object_id($s) { return p\Php72::spl_object_id($s); }
+    }
+    if (!defined('PHP_OS_FAMILY')) {
+        define('PHP_OS_FAMILY', p\Php72::php_os_family());
+    }
+    if (!function_exists('mb_chr')) {
+        function mb_ord($s, $enc = null) { return p\Php72::mb_ord($s, $enc); }
+        function mb_chr($code, $enc = null) { return p\Php72::mb_chr($code, $enc); }
+        function mb_scrub($s, $enc = null) { $enc = null === $enc ? mb_internal_encoding() : $enc; return mb_convert_encoding($s, $enc, $enc); }
+    }
+}
diff --git a/vendor/symfony/polyfill-php72/composer.json b/vendor/symfony/polyfill-php72/composer.json
new file mode 100755
index 0000000..8282555
--- /dev/null
+++ b/vendor/symfony/polyfill-php72/composer.json
@@ -0,0 +1,31 @@
+{
+    "name": "symfony/polyfill-php72",
+    "type": "library",
+    "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
+    "keywords": ["polyfill", "shim", "compatibility", "portable"],
+    "homepage": "https://symfony.com",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Nicolas Grekas",
+            "email": "p@tchwork.com"
+        },
+        {
+            "name": "Symfony Community",
+            "homepage": "https://symfony.com/contributors"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.3"
+    },
+    "autoload": {
+        "psr-4": { "Symfony\\Polyfill\\Php72\\": "" },
+        "files": [ "bootstrap.php" ]
+    },
+    "minimum-stability": "dev",
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.11-dev"
+        }
+    }
+}
diff --git a/vendor/symfony/process/.gitignore b/vendor/symfony/process/.gitignore
new file mode 100755
index 0000000..c49a5d8
--- /dev/null
+++ b/vendor/symfony/process/.gitignore
@@ -0,0 +1,3 @@
+vendor/
+composer.lock
+phpunit.xml
diff --git a/vendor/symfony/process/CHANGELOG.md b/vendor/symfony/process/CHANGELOG.md
new file mode 100755
index 0000000..31d0638
--- /dev/null
+++ b/vendor/symfony/process/CHANGELOG.md
@@ -0,0 +1,90 @@
+CHANGELOG
+=========
+
+4.2.0
+-----
+
+ * added the `Process::fromShellCommandline()` to run commands in a shell wrapper
+ * deprecated passing a command as string when creating a `Process` instance
+ * deprecated the `Process::setCommandline()` and the `PhpProcess::setPhpBinary()` methods
+ * added the `Process::waitUntil()` method to wait for the process only for a
+   specific output, then continue the normal execution of your application
+
+4.1.0
+-----
+
+ * added the `Process::isTtySupported()` method that allows to check for TTY support
+ * made `PhpExecutableFinder` look for the `PHP_BINARY` env var when searching the php binary
+ * added the `ProcessSignaledException` class to properly catch signaled process errors
+
+4.0.0
+-----
+
+ * environment variables will always be inherited
+ * added a second `array $env = []` argument to the `start()`, `run()`,
+   `mustRun()`, and `restart()` methods of the `Process` class
+ * added a second `array $env = []` argument to the `start()` method of the
+   `PhpProcess` class
+ * the `ProcessUtils::escapeArgument()` method has been removed
+ * the `areEnvironmentVariablesInherited()`, `getOptions()`, and `setOptions()`
+   methods of the `Process` class have been removed
+ * support for passing `proc_open()` options has been removed
+ * removed the `ProcessBuilder` class, use the `Process` class instead
+ * removed the `getEnhanceWindowsCompatibility()` and `setEnhanceWindowsCompatibility()` methods of the `Process` class
+ * passing a not existing working directory to the constructor of the `Symfony\Component\Process\Process` class is not
+   supported anymore
+
+3.4.0
+-----
+
+ * deprecated the ProcessBuilder class
+ * deprecated calling `Process::start()` without setting a valid working directory beforehand (via `setWorkingDirectory()` or constructor)
+
+3.3.0
+-----
+
+ * added command line arrays in the `Process` class
+ * added `$env` argument to `Process::start()`, `run()`, `mustRun()` and `restart()` methods
+ * deprecated the `ProcessUtils::escapeArgument()` method
+ * deprecated not inheriting environment variables
+ * deprecated configuring `proc_open()` options
+ * deprecated configuring enhanced Windows compatibility
+ * deprecated configuring enhanced sigchild compatibility
+
+2.5.0
+-----
+
+ * added support for PTY mode
+ * added the convenience method "mustRun"
+ * deprecation: Process::setStdin() is deprecated in favor of Process::setInput()
+ * deprecation: Process::getStdin() is deprecated in favor of Process::getInput()
+ * deprecation: Process::setInput() and ProcessBuilder::setInput() do not accept non-scalar types
+
+2.4.0
+-----
+
+ * added the ability to define an idle timeout
+
+2.3.0
+-----
+
+ * added ProcessUtils::escapeArgument() to fix the bug in escapeshellarg() function on Windows
+ * added Process::signal()
+ * added Process::getPid()
+ * added support for a TTY mode
+
+2.2.0
+-----
+
+ * added ProcessBuilder::setArguments() to reset the arguments on a builder
+ * added a way to retrieve the standard and error output incrementally
+ * added Process:restart()
+
+2.1.0
+-----
+
+ * added support for non-blocking processes (start(), wait(), isRunning(), stop())
+ * enhanced Windows compatibility
+ * added Process::getExitCodeText() that returns a string representation for
+   the exit code returned by the process
+ * added ProcessBuilder
diff --git a/vendor/symfony/process/Exception/ExceptionInterface.php b/vendor/symfony/process/Exception/ExceptionInterface.php
new file mode 100755
index 0000000..bd4a604
--- /dev/null
+++ b/vendor/symfony/process/Exception/ExceptionInterface.php
@@ -0,0 +1,21 @@
+<?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\Process\Exception;
+
+/**
+ * Marker Interface for the Process Component.
+ *
+ * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ */
+interface ExceptionInterface extends \Throwable
+{
+}
diff --git a/vendor/symfony/process/Exception/InvalidArgumentException.php b/vendor/symfony/process/Exception/InvalidArgumentException.php
new file mode 100755
index 0000000..926ee21
--- /dev/null
+++ b/vendor/symfony/process/Exception/InvalidArgumentException.php
@@ -0,0 +1,21 @@
+<?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\Process\Exception;
+
+/**
+ * InvalidArgumentException for the Process Component.
+ *
+ * @author Romain Neutron <imprec@gmail.com>
+ */
+class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
diff --git a/vendor/symfony/process/Exception/LogicException.php b/vendor/symfony/process/Exception/LogicException.php
new file mode 100755
index 0000000..be3d490
--- /dev/null
+++ b/vendor/symfony/process/Exception/LogicException.php
@@ -0,0 +1,21 @@
+<?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\Process\Exception;
+
+/**
+ * LogicException for the Process Component.
+ *
+ * @author Romain Neutron <imprec@gmail.com>
+ */
+class LogicException extends \LogicException implements ExceptionInterface
+{
+}
diff --git a/vendor/symfony/process/Exception/ProcessFailedException.php b/vendor/symfony/process/Exception/ProcessFailedException.php
new file mode 100755
index 0000000..328acfd
--- /dev/null
+++ b/vendor/symfony/process/Exception/ProcessFailedException.php
@@ -0,0 +1,54 @@
+<?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\Process\Exception;
+
+use Symfony\Component\Process\Process;
+
+/**
+ * Exception for failed processes.
+ *
+ * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ */
+class ProcessFailedException extends RuntimeException
+{
+    private $process;
+
+    public function __construct(Process $process)
+    {
+        if ($process->isSuccessful()) {
+            throw new InvalidArgumentException('Expected a failed process, but the given process was successful.');
+        }
+
+        $error = sprintf('The command "%s" failed.'."\n\nExit Code: %s(%s)\n\nWorking directory: %s",
+            $process->getCommandLine(),
+            $process->getExitCode(),
+            $process->getExitCodeText(),
+            $process->getWorkingDirectory()
+        );
+
+        if (!$process->isOutputDisabled()) {
+            $error .= sprintf("\n\nOutput:\n================\n%s\n\nError Output:\n================\n%s",
+                $process->getOutput(),
+                $process->getErrorOutput()
+            );
+        }
+
+        parent::__construct($error);
+
+        $this->process = $process;
+    }
+
+    public function getProcess()
+    {
+        return $this->process;
+    }
+}
diff --git a/vendor/symfony/process/Exception/ProcessSignaledException.php b/vendor/symfony/process/Exception/ProcessSignaledException.php
new file mode 100755
index 0000000..d4d3227
--- /dev/null
+++ b/vendor/symfony/process/Exception/ProcessSignaledException.php
@@ -0,0 +1,41 @@
+<?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\Process\Exception;
+
+use Symfony\Component\Process\Process;
+
+/**
+ * Exception that is thrown when a process has been signaled.
+ *
+ * @author Sullivan Senechal <soullivaneuh@gmail.com>
+ */
+final class ProcessSignaledException extends RuntimeException
+{
+    private $process;
+
+    public function __construct(Process $process)
+    {
+        $this->process = $process;
+
+        parent::__construct(sprintf('The process has been signaled with signal "%s".', $process->getTermSignal()));
+    }
+
+    public function getProcess(): Process
+    {
+        return $this->process;
+    }
+
+    public function getSignal(): int
+    {
+        return $this->getProcess()->getTermSignal();
+    }
+}
diff --git a/vendor/symfony/process/Exception/ProcessTimedOutException.php b/vendor/symfony/process/Exception/ProcessTimedOutException.php
new file mode 100755
index 0000000..e1f6445
--- /dev/null
+++ b/vendor/symfony/process/Exception/ProcessTimedOutException.php
@@ -0,0 +1,69 @@
+<?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\Process\Exception;
+
+use Symfony\Component\Process\Process;
+
+/**
+ * Exception that is thrown when a process times out.
+ *
+ * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ */
+class ProcessTimedOutException extends RuntimeException
+{
+    const TYPE_GENERAL = 1;
+    const TYPE_IDLE = 2;
+
+    private $process;
+    private $timeoutType;
+
+    public function __construct(Process $process, int $timeoutType)
+    {
+        $this->process = $process;
+        $this->timeoutType = $timeoutType;
+
+        parent::__construct(sprintf(
+            'The process "%s" exceeded the timeout of %s seconds.',
+            $process->getCommandLine(),
+            $this->getExceededTimeout()
+        ));
+    }
+
+    public function getProcess()
+    {
+        return $this->process;
+    }
+
+    public function isGeneralTimeout()
+    {
+        return self::TYPE_GENERAL === $this->timeoutType;
+    }
+
+    public function isIdleTimeout()
+    {
+        return self::TYPE_IDLE === $this->timeoutType;
+    }
+
+    public function getExceededTimeout()
+    {
+        switch ($this->timeoutType) {
+            case self::TYPE_GENERAL:
+                return $this->process->getTimeout();
+
+            case self::TYPE_IDLE:
+                return $this->process->getIdleTimeout();
+
+            default:
+                throw new \LogicException(sprintf('Unknown timeout type "%d".', $this->timeoutType));
+        }
+    }
+}
diff --git a/vendor/symfony/process/Exception/RuntimeException.php b/vendor/symfony/process/Exception/RuntimeException.php
new file mode 100755
index 0000000..adead25
--- /dev/null
+++ b/vendor/symfony/process/Exception/RuntimeException.php
@@ -0,0 +1,21 @@
+<?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\Process\Exception;
+
+/**
+ * RuntimeException for the Process Component.
+ *
+ * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ */
+class RuntimeException extends \RuntimeException implements ExceptionInterface
+{
+}
diff --git a/vendor/symfony/process/ExecutableFinder.php b/vendor/symfony/process/ExecutableFinder.php
new file mode 100755
index 0000000..cb4345e
--- /dev/null
+++ b/vendor/symfony/process/ExecutableFinder.php
@@ -0,0 +1,88 @@
+<?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\Process;
+
+/**
+ * Generic executable finder.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ */
+class ExecutableFinder
+{
+    private $suffixes = ['.exe', '.bat', '.cmd', '.com'];
+
+    /**
+     * Replaces default suffixes of executable.
+     */
+    public function setSuffixes(array $suffixes)
+    {
+        $this->suffixes = $suffixes;
+    }
+
+    /**
+     * Adds new possible suffix to check for executable.
+     *
+     * @param string $suffix
+     */
+    public function addSuffix($suffix)
+    {
+        $this->suffixes[] = $suffix;
+    }
+
+    /**
+     * Finds an executable by name.
+     *
+     * @param string      $name      The executable name (without the extension)
+     * @param string|null $default   The default to return if no executable is found
+     * @param array       $extraDirs Additional dirs to check into
+     *
+     * @return string|null The executable path or default value
+     */
+    public function find($name, $default = null, array $extraDirs = [])
+    {
+        if (ini_get('open_basedir')) {
+            $searchPath = array_merge(explode(PATH_SEPARATOR, ini_get('open_basedir')), $extraDirs);
+            $dirs = [];
+            foreach ($searchPath as $path) {
+                // Silencing against https://bugs.php.net/69240
+                if (@is_dir($path)) {
+                    $dirs[] = $path;
+                } else {
+                    if (basename($path) == $name && @is_executable($path)) {
+                        return $path;
+                    }
+                }
+            }
+        } else {
+            $dirs = array_merge(
+                explode(PATH_SEPARATOR, getenv('PATH') ?: getenv('Path')),
+                $extraDirs
+            );
+        }
+
+        $suffixes = [''];
+        if ('\\' === \DIRECTORY_SEPARATOR) {
+            $pathExt = getenv('PATHEXT');
+            $suffixes = array_merge($pathExt ? explode(PATH_SEPARATOR, $pathExt) : $this->suffixes, $suffixes);
+        }
+        foreach ($suffixes as $suffix) {
+            foreach ($dirs as $dir) {
+                if (@is_file($file = $dir.\DIRECTORY_SEPARATOR.$name.$suffix) && ('\\' === \DIRECTORY_SEPARATOR || @is_executable($file))) {
+                    return $file;
+                }
+            }
+        }
+
+        return $default;
+    }
+}
diff --git a/vendor/symfony/process/InputStream.php b/vendor/symfony/process/InputStream.php
new file mode 100755
index 0000000..426ffa3
--- /dev/null
+++ b/vendor/symfony/process/InputStream.php
@@ -0,0 +1,90 @@
+<?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\Process;
+
+use Symfony\Component\Process\Exception\RuntimeException;
+
+/**
+ * Provides a way to continuously write to the input of a Process until the InputStream is closed.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class InputStream implements \IteratorAggregate
+{
+    /** @var callable|null */
+    private $onEmpty = null;
+    private $input = [];
+    private $open = true;
+
+    /**
+     * Sets a callback that is called when the write buffer becomes empty.
+     */
+    public function onEmpty(callable $onEmpty = null)
+    {
+        $this->onEmpty = $onEmpty;
+    }
+
+    /**
+     * Appends an input to the write buffer.
+     *
+     * @param resource|string|int|float|bool|\Traversable|null $input The input to append as scalar,
+     *                                                                stream resource or \Traversable
+     */
+    public function write($input)
+    {
+        if (null === $input) {
+            return;
+        }
+        if ($this->isClosed()) {
+            throw new RuntimeException(sprintf('%s is closed', static::class));
+        }
+        $this->input[] = ProcessUtils::validateInput(__METHOD__, $input);
+    }
+
+    /**
+     * Closes the write buffer.
+     */
+    public function close()
+    {
+        $this->open = false;
+    }
+
+    /**
+     * Tells whether the write buffer is closed or not.
+     */
+    public function isClosed()
+    {
+        return !$this->open;
+    }
+
+    public function getIterator()
+    {
+        $this->open = true;
+
+        while ($this->open || $this->input) {
+            if (!$this->input) {
+                yield '';
+                continue;
+            }
+            $current = array_shift($this->input);
+
+            if ($current instanceof \Iterator) {
+                yield from $current;
+            } else {
+                yield $current;
+            }
+            if (!$this->input && $this->open && null !== $onEmpty = $this->onEmpty) {
+                $this->write($onEmpty($this));
+            }
+        }
+    }
+}
diff --git a/vendor/symfony/process/LICENSE b/vendor/symfony/process/LICENSE
new file mode 100755
index 0000000..a677f43
--- /dev/null
+++ b/vendor/symfony/process/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2004-2019 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.
diff --git a/vendor/symfony/process/PhpExecutableFinder.php b/vendor/symfony/process/PhpExecutableFinder.php
new file mode 100755
index 0000000..461ea13
--- /dev/null
+++ b/vendor/symfony/process/PhpExecutableFinder.php
@@ -0,0 +1,101 @@
+<?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\Process;
+
+/**
+ * An executable finder specifically designed for the PHP executable.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ */
+class PhpExecutableFinder
+{
+    private $executableFinder;
+
+    public function __construct()
+    {
+        $this->executableFinder = new ExecutableFinder();
+    }
+
+    /**
+     * Finds The PHP executable.
+     *
+     * @param bool $includeArgs Whether or not include command arguments
+     *
+     * @return string|false The PHP executable path or false if it cannot be found
+     */
+    public function find($includeArgs = true)
+    {
+        if ($php = getenv('PHP_BINARY')) {
+            if (!is_executable($php)) {
+                $command = '\\' === \DIRECTORY_SEPARATOR ? 'where' : 'command -v';
+                if ($php = strtok(exec($command.' '.escapeshellarg($php)), PHP_EOL)) {
+                    if (!is_executable($php)) {
+                        return false;
+                    }
+                } else {
+                    return false;
+                }
+            }
+
+            return $php;
+        }
+
+        $args = $this->findArguments();
+        $args = $includeArgs && $args ? ' '.implode(' ', $args) : '';
+
+        // PHP_BINARY return the current sapi executable
+        if (PHP_BINARY && \in_array(\PHP_SAPI, ['cli', 'cli-server', 'phpdbg'], true)) {
+            return PHP_BINARY.$args;
+        }
+
+        if ($php = getenv('PHP_PATH')) {
+            if (!@is_executable($php)) {
+                return false;
+            }
+
+            return $php;
+        }
+
+        if ($php = getenv('PHP_PEAR_PHP_BIN')) {
+            if (@is_executable($php)) {
+                return $php;
+            }
+        }
+
+        if (@is_executable($php = PHP_BINDIR.('\\' === \DIRECTORY_SEPARATOR ? '\\php.exe' : '/php'))) {
+            return $php;
+        }
+
+        $dirs = [PHP_BINDIR];
+        if ('\\' === \DIRECTORY_SEPARATOR) {
+            $dirs[] = 'C:\xampp\php\\';
+        }
+
+        return $this->executableFinder->find('php', false, $dirs);
+    }
+
+    /**
+     * Finds the PHP executable arguments.
+     *
+     * @return array The PHP executable arguments
+     */
+    public function findArguments()
+    {
+        $arguments = [];
+        if ('phpdbg' === \PHP_SAPI) {
+            $arguments[] = '-qrr';
+        }
+
+        return $arguments;
+    }
+}
diff --git a/vendor/symfony/process/PhpProcess.php b/vendor/symfony/process/PhpProcess.php
new file mode 100755
index 0000000..126d9b7
--- /dev/null
+++ b/vendor/symfony/process/PhpProcess.php
@@ -0,0 +1,75 @@
+<?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\Process;
+
+use Symfony\Component\Process\Exception\RuntimeException;
+
+/**
+ * PhpProcess runs a PHP script in an independent process.
+ *
+ *     $p = new PhpProcess('<?php echo "foo"; ?>');
+ *     $p->run();
+ *     print $p->getOutput()."\n";
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class PhpProcess extends Process
+{
+    /**
+     * @param string      $script  The PHP script to run (as a string)
+     * @param string|null $cwd     The working directory or null to use the working dir of the current PHP process
+     * @param array|null  $env     The environment variables or null to use the same environment as the current PHP process
+     * @param int         $timeout The timeout in seconds
+     * @param array|null  $php     Path to the PHP binary to use with any additional arguments
+     */
+    public function __construct(string $script, string $cwd = null, array $env = null, int $timeout = 60, array $php = null)
+    {
+        if (null === $php) {
+            $executableFinder = new PhpExecutableFinder();
+            $php = $executableFinder->find(false);
+            $php = false === $php ? null : array_merge([$php], $executableFinder->findArguments());
+        }
+        if ('phpdbg' === \PHP_SAPI) {
+            $file = tempnam(sys_get_temp_dir(), 'dbg');
+            file_put_contents($file, $script);
+            register_shutdown_function('unlink', $file);
+            $php[] = $file;
+            $script = null;
+        }
+
+        parent::__construct($php, $cwd, $env, $script, $timeout);
+    }
+
+    /**
+     * Sets the path to the PHP binary to use.
+     *
+     * @deprecated since Symfony 4.2, use the $php argument of the constructor instead.
+     */
+    public function setPhpBinary($php)
+    {
+        @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2, use the $php argument of the constructor instead.', __METHOD__), E_USER_DEPRECATED);
+
+        $this->setCommandLine($php);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function start(callable $callback = null, array $env = [])
+    {
+        if (null === $this->getCommandLine()) {
+            throw new RuntimeException('Unable to find the PHP executable.');
+        }
+
+        parent::start($callback, $env);
+    }
+}
diff --git a/vendor/symfony/process/Pipes/AbstractPipes.php b/vendor/symfony/process/Pipes/AbstractPipes.php
new file mode 100755
index 0000000..23886b6
--- /dev/null
+++ b/vendor/symfony/process/Pipes/AbstractPipes.php
@@ -0,0 +1,178 @@
+<?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\Process\Pipes;
+
+use Symfony\Component\Process\Exception\InvalidArgumentException;
+
+/**
+ * @author Romain Neutron <imprec@gmail.com>
+ *
+ * @internal
+ */
+abstract class AbstractPipes implements PipesInterface
+{
+    public $pipes = [];
+
+    private $inputBuffer = '';
+    private $input;
+    private $blocked = true;
+    private $lastError;
+
+    /**
+     * @param resource|string|int|float|bool|\Iterator|null $input
+     */
+    public function __construct($input)
+    {
+        if (\is_resource($input) || $input instanceof \Iterator) {
+            $this->input = $input;
+        } elseif (\is_string($input)) {
+            $this->inputBuffer = $input;
+        } else {
+            $this->inputBuffer = (string) $input;
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function close()
+    {
+        foreach ($this->pipes as $pipe) {
+            fclose($pipe);
+        }
+        $this->pipes = [];
+    }
+
+    /**
+     * Returns true if a system call has been interrupted.
+     *
+     * @return bool
+     */
+    protected function hasSystemCallBeenInterrupted()
+    {
+        $lastError = $this->lastError;
+        $this->lastError = null;
+
+        // stream_select returns false when the `select` system call is interrupted by an incoming signal
+        return null !== $lastError && false !== stripos($lastError, 'interrupted system call');
+    }
+
+    /**
+     * Unblocks streams.
+     */
+    protected function unblock()
+    {
+        if (!$this->blocked) {
+            return;
+        }
+
+        foreach ($this->pipes as $pipe) {
+            stream_set_blocking($pipe, 0);
+        }
+        if (\is_resource($this->input)) {
+            stream_set_blocking($this->input, 0);
+        }
+
+        $this->blocked = false;
+    }
+
+    /**
+     * Writes input to stdin.
+     *
+     * @throws InvalidArgumentException When an input iterator yields a non supported value
+     */
+    protected function write()
+    {
+        if (!isset($this->pipes[0])) {
+            return;
+        }
+        $input = $this->input;
+
+        if ($input instanceof \Iterator) {
+            if (!$input->valid()) {
+                $input = null;
+            } elseif (\is_resource($input = $input->current())) {
+                stream_set_blocking($input, 0);
+            } elseif (!isset($this->inputBuffer[0])) {
+                if (!\is_string($input)) {
+                    if (!is_scalar($input)) {
+                        throw new InvalidArgumentException(sprintf('%s yielded a value of type "%s", but only scalars and stream resources are supported', \get_class($this->input), \gettype($input)));
+                    }
+                    $input = (string) $input;
+                }
+                $this->inputBuffer = $input;
+                $this->input->next();
+                $input = null;
+            } else {
+                $input = null;
+            }
+        }
+
+        $r = $e = [];
+        $w = [$this->pipes[0]];
+
+        // let's have a look if something changed in streams
+        if (false === @stream_select($r, $w, $e, 0, 0)) {
+            return;
+        }
+
+        foreach ($w as $stdin) {
+            if (isset($this->inputBuffer[0])) {
+                $written = fwrite($stdin, $this->inputBuffer);
+                $this->inputBuffer = substr($this->inputBuffer, $written);
+                if (isset($this->inputBuffer[0])) {
+                    return [$this->pipes[0]];
+                }
+            }
+
+            if ($input) {
+                for (;;) {
+                    $data = fread($input, self::CHUNK_SIZE);
+                    if (!isset($data[0])) {
+                        break;
+                    }
+                    $written = fwrite($stdin, $data);
+                    $data = substr($data, $written);
+                    if (isset($data[0])) {
+                        $this->inputBuffer = $data;
+
+                        return [$this->pipes[0]];
+                    }
+                }
+                if (feof($input)) {
+                    if ($this->input instanceof \Iterator) {
+                        $this->input->next();
+                    } else {
+                        $this->input = null;
+                    }
+                }
+            }
+        }
+
+        // no input to read on resource, buffer is empty
+        if (!isset($this->inputBuffer[0]) && !($this->input instanceof \Iterator ? $this->input->valid() : $this->input)) {
+            $this->input = null;
+            fclose($this->pipes[0]);
+            unset($this->pipes[0]);
+        } elseif (!$w) {
+            return [$this->pipes[0]];
+        }
+    }
+
+    /**
+     * @internal
+     */
+    public function handleError($type, $msg)
+    {
+        $this->lastError = $msg;
+    }
+}
diff --git a/vendor/symfony/process/Pipes/PipesInterface.php b/vendor/symfony/process/Pipes/PipesInterface.php
new file mode 100755
index 0000000..52bbe76
--- /dev/null
+++ b/vendor/symfony/process/Pipes/PipesInterface.php
@@ -0,0 +1,67 @@
+<?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\Process\Pipes;
+
+/**
+ * PipesInterface manages descriptors and pipes for the use of proc_open.
+ *
+ * @author Romain Neutron <imprec@gmail.com>
+ *
+ * @internal
+ */
+interface PipesInterface
+{
+    const CHUNK_SIZE = 16384;
+
+    /**
+     * Returns an array of descriptors for the use of proc_open.
+     *
+     * @return array
+     */
+    public function getDescriptors();
+
+    /**
+     * Returns an array of filenames indexed by their related stream in case these pipes use temporary files.
+     *
+     * @return string[]
+     */
+    public function getFiles();
+
+    /**
+     * Reads data in file handles and pipes.
+     *
+     * @param bool $blocking Whether to use blocking calls or not
+     * @param bool $close    Whether to close pipes if they've reached EOF
+     *
+     * @return string[] An array of read data indexed by their fd
+     */
+    public function readAndWrite($blocking, $close = false);
+
+    /**
+     * Returns if the current state has open file handles or pipes.
+     *
+     * @return bool
+     */
+    public function areOpen();
+
+    /**
+     * Returns if pipes are able to read output.
+     *
+     * @return bool
+     */
+    public function haveReadSupport();
+
+    /**
+     * Closes file handles and pipes.
+     */
+    public function close();
+}
diff --git a/vendor/symfony/process/Pipes/UnixPipes.php b/vendor/symfony/process/Pipes/UnixPipes.php
new file mode 100755
index 0000000..875ee6a
--- /dev/null
+++ b/vendor/symfony/process/Pipes/UnixPipes.php
@@ -0,0 +1,153 @@
+<?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\Process\Pipes;
+
+use Symfony\Component\Process\Process;
+
+/**
+ * UnixPipes implementation uses unix pipes as handles.
+ *
+ * @author Romain Neutron <imprec@gmail.com>
+ *
+ * @internal
+ */
+class UnixPipes extends AbstractPipes
+{
+    private $ttyMode;
+    private $ptyMode;
+    private $haveReadSupport;
+
+    public function __construct(?bool $ttyMode, bool $ptyMode, $input, bool $haveReadSupport)
+    {
+        $this->ttyMode = $ttyMode;
+        $this->ptyMode = $ptyMode;
+        $this->haveReadSupport = $haveReadSupport;
+
+        parent::__construct($input);
+    }
+
+    public function __destruct()
+    {
+        $this->close();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDescriptors()
+    {
+        if (!$this->haveReadSupport) {
+            $nullstream = fopen('/dev/null', 'c');
+
+            return [
+                ['pipe', 'r'],
+                $nullstream,
+                $nullstream,
+            ];
+        }
+
+        if ($this->ttyMode) {
+            return [
+                ['file', '/dev/tty', 'r'],
+                ['file', '/dev/tty', 'w'],
+                ['file', '/dev/tty', 'w'],
+            ];
+        }
+
+        if ($this->ptyMode && Process::isPtySupported()) {
+            return [
+                ['pty'],
+                ['pty'],
+                ['pty'],
+            ];
+        }
+
+        return [
+            ['pipe', 'r'],
+            ['pipe', 'w'], // stdout
+            ['pipe', 'w'], // stderr
+        ];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getFiles()
+    {
+        return [];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function readAndWrite($blocking, $close = false)
+    {
+        $this->unblock();
+        $w = $this->write();
+
+        $read = $e = [];
+        $r = $this->pipes;
+        unset($r[0]);
+
+        // let's have a look if something changed in streams
+        set_error_handler([$this, 'handleError']);
+        if (($r || $w) && false === stream_select($r, $w, $e, 0, $blocking ? Process::TIMEOUT_PRECISION * 1E6 : 0)) {
+            restore_error_handler();
+            // if a system call has been interrupted, forget about it, let's try again
+            // otherwise, an error occurred, let's reset pipes
+            if (!$this->hasSystemCallBeenInterrupted()) {
+                $this->pipes = [];
+            }
+
+            return $read;
+        }
+        restore_error_handler();
+
+        foreach ($r as $pipe) {
+            // prior PHP 5.4 the array passed to stream_select is modified and
+            // lose key association, we have to find back the key
+            $read[$type = array_search($pipe, $this->pipes, true)] = '';
+
+            do {
+                $data = fread($pipe, self::CHUNK_SIZE);
+                $read[$type] .= $data;
+            } while (isset($data[0]) && ($close || isset($data[self::CHUNK_SIZE - 1])));
+
+            if (!isset($read[$type][0])) {
+                unset($read[$type]);
+            }
+
+            if ($close && feof($pipe)) {
+                fclose($pipe);
+                unset($this->pipes[$type]);
+            }
+        }
+
+        return $read;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function haveReadSupport()
+    {
+        return $this->haveReadSupport;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function areOpen()
+    {
+        return (bool) $this->pipes;
+    }
+}
diff --git a/vendor/symfony/process/Pipes/WindowsPipes.php b/vendor/symfony/process/Pipes/WindowsPipes.php
new file mode 100755
index 0000000..f44f33b
--- /dev/null
+++ b/vendor/symfony/process/Pipes/WindowsPipes.php
@@ -0,0 +1,191 @@
+<?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\Process\Pipes;
+
+use Symfony\Component\Process\Exception\RuntimeException;
+use Symfony\Component\Process\Process;
+
+/**
+ * WindowsPipes implementation uses temporary files as handles.
+ *
+ * @see https://bugs.php.net/bug.php?id=51800
+ * @see https://bugs.php.net/bug.php?id=65650
+ *
+ * @author Romain Neutron <imprec@gmail.com>
+ *
+ * @internal
+ */
+class WindowsPipes extends AbstractPipes
+{
+    private $files = [];
+    private $fileHandles = [];
+    private $lockHandles = [];
+    private $readBytes = [
+        Process::STDOUT => 0,
+        Process::STDERR => 0,
+    ];
+    private $haveReadSupport;
+
+    public function __construct($input, bool $haveReadSupport)
+    {
+        $this->haveReadSupport = $haveReadSupport;
+
+        if ($this->haveReadSupport) {
+            // Fix for PHP bug #51800: reading from STDOUT pipe hangs forever on Windows if the output is too big.
+            // Workaround for this problem is to use temporary files instead of pipes on Windows platform.
+            //
+            // @see https://bugs.php.net/bug.php?id=51800
+            $pipes = [
+                Process::STDOUT => Process::OUT,
+                Process::STDERR => Process::ERR,
+            ];
+            $tmpDir = sys_get_temp_dir();
+            $lastError = 'unknown reason';
+            set_error_handler(function ($type, $msg) use (&$lastError) { $lastError = $msg; });
+            for ($i = 0;; ++$i) {
+                foreach ($pipes as $pipe => $name) {
+                    $file = sprintf('%s\\sf_proc_%02X.%s', $tmpDir, $i, $name);
+
+                    if (!$h = fopen($file.'.lock', 'w')) {
+                        restore_error_handler();
+                        throw new RuntimeException(sprintf('A temporary file could not be opened to write the process output: %s', $lastError));
+                    }
+                    if (!flock($h, LOCK_EX | LOCK_NB)) {
+                        continue 2;
+                    }
+                    if (isset($this->lockHandles[$pipe])) {
+                        flock($this->lockHandles[$pipe], LOCK_UN);
+                        fclose($this->lockHandles[$pipe]);
+                    }
+                    $this->lockHandles[$pipe] = $h;
+
+                    if (!fclose(fopen($file, 'w')) || !$h = fopen($file, 'r')) {
+                        flock($this->lockHandles[$pipe], LOCK_UN);
+                        fclose($this->lockHandles[$pipe]);
+                        unset($this->lockHandles[$pipe]);
+                        continue 2;
+                    }
+                    $this->fileHandles[$pipe] = $h;
+                    $this->files[$pipe] = $file;
+                }
+                break;
+            }
+            restore_error_handler();
+        }
+
+        parent::__construct($input);
+    }
+
+    public function __destruct()
+    {
+        $this->close();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDescriptors()
+    {
+        if (!$this->haveReadSupport) {
+            $nullstream = fopen('NUL', 'c');
+
+            return [
+                ['pipe', 'r'],
+                $nullstream,
+                $nullstream,
+            ];
+        }
+
+        // We're not using pipe on Windows platform as it hangs (https://bugs.php.net/bug.php?id=51800)
+        // We're not using file handles as it can produce corrupted output https://bugs.php.net/bug.php?id=65650
+        // So we redirect output within the commandline and pass the nul device to the process
+        return [
+            ['pipe', 'r'],
+            ['file', 'NUL', 'w'],
+            ['file', 'NUL', 'w'],
+        ];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getFiles()
+    {
+        return $this->files;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function readAndWrite($blocking, $close = false)
+    {
+        $this->unblock();
+        $w = $this->write();
+        $read = $r = $e = [];
+
+        if ($blocking) {
+            if ($w) {
+                @stream_select($r, $w, $e, 0, Process::TIMEOUT_PRECISION * 1E6);
+            } elseif ($this->fileHandles) {
+                usleep(Process::TIMEOUT_PRECISION * 1E6);
+            }
+        }
+        foreach ($this->fileHandles as $type => $fileHandle) {
+            $data = stream_get_contents($fileHandle, -1, $this->readBytes[$type]);
+
+            if (isset($data[0])) {
+                $this->readBytes[$type] += \strlen($data);
+                $read[$type] = $data;
+            }
+            if ($close) {
+                ftruncate($fileHandle, 0);
+                fclose($fileHandle);
+                flock($this->lockHandles[$type], LOCK_UN);
+                fclose($this->lockHandles[$type]);
+                unset($this->fileHandles[$type], $this->lockHandles[$type]);
+            }
+        }
+
+        return $read;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function haveReadSupport()
+    {
+        return $this->haveReadSupport;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function areOpen()
+    {
+        return $this->pipes && $this->fileHandles;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function close()
+    {
+        parent::close();
+        foreach ($this->fileHandles as $type => $handle) {
+            ftruncate($handle, 0);
+            fclose($handle);
+            flock($this->lockHandles[$type], LOCK_UN);
+            fclose($this->lockHandles[$type]);
+        }
+        $this->fileHandles = $this->lockHandles = [];
+    }
+}
diff --git a/vendor/symfony/process/Process.php b/vendor/symfony/process/Process.php
new file mode 100755
index 0000000..5e3993d
--- /dev/null
+++ b/vendor/symfony/process/Process.php
@@ -0,0 +1,1653 @@
+<?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\Process;
+
+use Symfony\Component\Process\Exception\InvalidArgumentException;
+use Symfony\Component\Process\Exception\LogicException;
+use Symfony\Component\Process\Exception\ProcessFailedException;
+use Symfony\Component\Process\Exception\ProcessSignaledException;
+use Symfony\Component\Process\Exception\ProcessTimedOutException;
+use Symfony\Component\Process\Exception\RuntimeException;
+use Symfony\Component\Process\Pipes\PipesInterface;
+use Symfony\Component\Process\Pipes\UnixPipes;
+use Symfony\Component\Process\Pipes\WindowsPipes;
+
+/**
+ * Process is a thin wrapper around proc_* functions to easily
+ * start independent PHP processes.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Romain Neutron <imprec@gmail.com>
+ */
+class Process implements \IteratorAggregate
+{
+    const ERR = 'err';
+    const OUT = 'out';
+
+    const STATUS_READY = 'ready';
+    const STATUS_STARTED = 'started';
+    const STATUS_TERMINATED = 'terminated';
+
+    const STDIN = 0;
+    const STDOUT = 1;
+    const STDERR = 2;
+
+    // Timeout Precision in seconds.
+    const TIMEOUT_PRECISION = 0.2;
+
+    const ITER_NON_BLOCKING = 1; // By default, iterating over outputs is a blocking call, use this flag to make it non-blocking
+    const ITER_KEEP_OUTPUT = 2;  // By default, outputs are cleared while iterating, use this flag to keep them in memory
+    const ITER_SKIP_OUT = 4;     // Use this flag to skip STDOUT while iterating
+    const ITER_SKIP_ERR = 8;     // Use this flag to skip STDERR while iterating
+
+    private $callback;
+    private $hasCallback = false;
+    private $commandline;
+    private $cwd;
+    private $env;
+    private $input;
+    private $starttime;
+    private $lastOutputTime;
+    private $timeout;
+    private $idleTimeout;
+    private $exitcode;
+    private $fallbackStatus = [];
+    private $processInformation;
+    private $outputDisabled = false;
+    private $stdout;
+    private $stderr;
+    private $process;
+    private $status = self::STATUS_READY;
+    private $incrementalOutputOffset = 0;
+    private $incrementalErrorOutputOffset = 0;
+    private $tty;
+    private $pty;
+
+    private $useFileHandles = false;
+    /** @var PipesInterface */
+    private $processPipes;
+
+    private $latestSignal;
+
+    private static $sigchild;
+
+    /**
+     * Exit codes translation table.
+     *
+     * User-defined errors must use exit codes in the 64-113 range.
+     */
+    public static $exitCodes = [
+        0 => 'OK',
+        1 => 'General error',
+        2 => 'Misuse of shell builtins',
+
+        126 => 'Invoked command cannot execute',
+        127 => 'Command not found',
+        128 => 'Invalid exit argument',
+
+        // signals
+        129 => 'Hangup',
+        130 => 'Interrupt',
+        131 => 'Quit and dump core',
+        132 => 'Illegal instruction',
+        133 => 'Trace/breakpoint trap',
+        134 => 'Process aborted',
+        135 => 'Bus error: "access to undefined portion of memory object"',
+        136 => 'Floating point exception: "erroneous arithmetic operation"',
+        137 => 'Kill (terminate immediately)',
+        138 => 'User-defined 1',
+        139 => 'Segmentation violation',
+        140 => 'User-defined 2',
+        141 => 'Write to pipe with no one reading',
+        142 => 'Signal raised by alarm',
+        143 => 'Termination (request to terminate)',
+        // 144 - not defined
+        145 => 'Child process terminated, stopped (or continued*)',
+        146 => 'Continue if stopped',
+        147 => 'Stop executing temporarily',
+        148 => 'Terminal stop signal',
+        149 => 'Background process attempting to read from tty ("in")',
+        150 => 'Background process attempting to write to tty ("out")',
+        151 => 'Urgent data available on socket',
+        152 => 'CPU time limit exceeded',
+        153 => 'File size limit exceeded',
+        154 => 'Signal raised by timer counting virtual time: "virtual timer expired"',
+        155 => 'Profiling timer expired',
+        // 156 - not defined
+        157 => 'Pollable event',
+        // 158 - not defined
+        159 => 'Bad syscall',
+    ];
+
+    /**
+     * @param array          $command The command to run and its arguments listed as separate entries
+     * @param string|null    $cwd     The working directory or null to use the working dir of the current PHP process
+     * @param array|null     $env     The environment variables or null to use the same environment as the current PHP process
+     * @param mixed|null     $input   The input as stream resource, scalar or \Traversable, or null for no input
+     * @param int|float|null $timeout The timeout in seconds or null to disable
+     *
+     * @throws RuntimeException When proc_open is not installed
+     */
+    public function __construct($command, string $cwd = null, array $env = null, $input = null, ?float $timeout = 60)
+    {
+        if (!\function_exists('proc_open')) {
+            throw new LogicException('The Process class relies on proc_open, which is not available on your PHP installation.');
+        }
+
+        if (!\is_array($command)) {
+            @trigger_error(sprintf('Passing a command as string when creating a "%s" instance is deprecated since Symfony 4.2, pass it as an array of its arguments instead, or use the "Process::fromShellCommandline()" constructor if you need features provided by the shell.', __CLASS__), E_USER_DEPRECATED);
+        }
+
+        $this->commandline = $command;
+        $this->cwd = $cwd;
+
+        // on Windows, if the cwd changed via chdir(), proc_open defaults to the dir where PHP was started
+        // on Gnu/Linux, PHP builds with --enable-maintainer-zts are also affected
+        // @see : https://bugs.php.net/bug.php?id=51800
+        // @see : https://bugs.php.net/bug.php?id=50524
+        if (null === $this->cwd && (\defined('ZEND_THREAD_SAFE') || '\\' === \DIRECTORY_SEPARATOR)) {
+            $this->cwd = getcwd();
+        }
+        if (null !== $env) {
+            $this->setEnv($env);
+        }
+
+        $this->setInput($input);
+        $this->setTimeout($timeout);
+        $this->useFileHandles = '\\' === \DIRECTORY_SEPARATOR;
+        $this->pty = false;
+    }
+
+    /**
+     * Creates a Process instance as a command-line to be run in a shell wrapper.
+     *
+     * Command-lines are parsed by the shell of your OS (/bin/sh on Unix-like, cmd.exe on Windows.)
+     * This allows using e.g. pipes or conditional execution. In this mode, signals are sent to the
+     * shell wrapper and not to your commands.
+     *
+     * In order to inject dynamic values into command-lines, we strongly recommend using placeholders.
+     * This will save escaping values, which is not portable nor secure anyway:
+     *
+     *   $process = Process::fromShellCommandline('my_command "$MY_VAR"');
+     *   $process->run(null, ['MY_VAR' => $theValue]);
+     *
+     * @param string         $command The command line to pass to the shell of the OS
+     * @param string|null    $cwd     The working directory or null to use the working dir of the current PHP process
+     * @param array|null     $env     The environment variables or null to use the same environment as the current PHP process
+     * @param mixed|null     $input   The input as stream resource, scalar or \Traversable, or null for no input
+     * @param int|float|null $timeout The timeout in seconds or null to disable
+     *
+     * @throws RuntimeException When proc_open is not installed
+     */
+    public static function fromShellCommandline(string $command, string $cwd = null, array $env = null, $input = null, ?float $timeout = 60)
+    {
+        $process = new static([], $cwd, $env, $input, $timeout);
+        $process->commandline = $command;
+
+        return $process;
+    }
+
+    public function __destruct()
+    {
+        $this->stop(0);
+    }
+
+    public function __clone()
+    {
+        $this->resetProcessData();
+    }
+
+    /**
+     * Runs the process.
+     *
+     * The callback receives the type of output (out or err) and
+     * some bytes from the output in real-time. It allows to have feedback
+     * from the independent process during execution.
+     *
+     * The STDOUT and STDERR are also available after the process is finished
+     * via the getOutput() and getErrorOutput() methods.
+     *
+     * @param callable|null $callback A PHP callback to run whenever there is some
+     *                                output available on STDOUT or STDERR
+     * @param array         $env      An array of additional env vars to set when running the process
+     *
+     * @return int The exit status code
+     *
+     * @throws RuntimeException When process can't be launched
+     * @throws RuntimeException When process stopped after receiving signal
+     * @throws LogicException   In case a callback is provided and output has been disabled
+     *
+     * @final
+     */
+    public function run(callable $callback = null, array $env = []): int
+    {
+        $this->start($callback, $env);
+
+        return $this->wait();
+    }
+
+    /**
+     * Runs the process.
+     *
+     * This is identical to run() except that an exception is thrown if the process
+     * exits with a non-zero exit code.
+     *
+     * @param callable|null $callback
+     * @param array         $env      An array of additional env vars to set when running the process
+     *
+     * @return self
+     *
+     * @throws ProcessFailedException if the process didn't terminate successfully
+     *
+     * @final
+     */
+    public function mustRun(callable $callback = null, array $env = [])
+    {
+        if (0 !== $this->run($callback, $env)) {
+            throw new ProcessFailedException($this);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Starts the process and returns after writing the input to STDIN.
+     *
+     * This method blocks until all STDIN data is sent to the process then it
+     * returns while the process runs in the background.
+     *
+     * The termination of the process can be awaited with wait().
+     *
+     * The callback receives the type of output (out or err) and some bytes from
+     * the output in real-time while writing the standard input to the process.
+     * It allows to have feedback from the independent process during execution.
+     *
+     * @param callable|null $callback A PHP callback to run whenever there is some
+     *                                output available on STDOUT or STDERR
+     * @param array         $env      An array of additional env vars to set when running the process
+     *
+     * @throws RuntimeException When process can't be launched
+     * @throws RuntimeException When process is already running
+     * @throws LogicException   In case a callback is provided and output has been disabled
+     */
+    public function start(callable $callback = null, array $env = [])
+    {
+        if ($this->isRunning()) {
+            throw new RuntimeException('Process is already running');
+        }
+
+        $this->resetProcessData();
+        $this->starttime = $this->lastOutputTime = microtime(true);
+        $this->callback = $this->buildCallback($callback);
+        $this->hasCallback = null !== $callback;
+        $descriptors = $this->getDescriptors();
+
+        if (\is_array($commandline = $this->commandline)) {
+            $commandline = implode(' ', array_map([$this, 'escapeArgument'], $commandline));
+
+            if ('\\' !== \DIRECTORY_SEPARATOR) {
+                // exec is mandatory to deal with sending a signal to the process
+                $commandline = 'exec '.$commandline;
+            }
+        }
+
+        if ($this->env) {
+            $env += $this->env;
+        }
+        $env += $this->getDefaultEnv();
+
+        $options = ['suppress_errors' => true];
+
+        if ('\\' === \DIRECTORY_SEPARATOR) {
+            $options['bypass_shell'] = true;
+            $commandline = $this->prepareWindowsCommandLine($commandline, $env);
+        } elseif (!$this->useFileHandles && $this->isSigchildEnabled()) {
+            // last exit code is output on the fourth pipe and caught to work around --enable-sigchild
+            $descriptors[3] = ['pipe', 'w'];
+
+            // See https://unix.stackexchange.com/questions/71205/background-process-pipe-input
+            $commandline = '{ ('.$commandline.') <&3 3<&- 3>/dev/null & } 3<&0;';
+            $commandline .= 'pid=$!; echo $pid >&3; wait $pid; code=$?; echo $code >&3; exit $code';
+
+            // Workaround for the bug, when PTS functionality is enabled.
+            // @see : https://bugs.php.net/69442
+            $ptsWorkaround = fopen(__FILE__, 'r');
+        }
+
+        $envPairs = [];
+        foreach ($env as $k => $v) {
+            if (false !== $v) {
+                $envPairs[] = $k.'='.$v;
+            }
+        }
+
+        if (!is_dir($this->cwd)) {
+            throw new RuntimeException(sprintf('The provided cwd "%s" does not exist.', $this->cwd));
+        }
+
+        $this->process = proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $envPairs, $options);
+
+        if (!\is_resource($this->process)) {
+            throw new RuntimeException('Unable to launch a new process.');
+        }
+        $this->status = self::STATUS_STARTED;
+
+        if (isset($descriptors[3])) {
+            $this->fallbackStatus['pid'] = (int) fgets($this->processPipes->pipes[3]);
+        }
+
+        if ($this->tty) {
+            return;
+        }
+
+        $this->updateStatus(false);
+        $this->checkTimeout();
+    }
+
+    /**
+     * Restarts the process.
+     *
+     * Be warned that the process is cloned before being started.
+     *
+     * @param callable|null $callback A PHP callback to run whenever there is some
+     *                                output available on STDOUT or STDERR
+     * @param array         $env      An array of additional env vars to set when running the process
+     *
+     * @return $this
+     *
+     * @throws RuntimeException When process can't be launched
+     * @throws RuntimeException When process is already running
+     *
+     * @see start()
+     *
+     * @final
+     */
+    public function restart(callable $callback = null, array $env = [])
+    {
+        if ($this->isRunning()) {
+            throw new RuntimeException('Process is already running');
+        }
+
+        $process = clone $this;
+        $process->start($callback, $env);
+
+        return $process;
+    }
+
+    /**
+     * Waits for the process to terminate.
+     *
+     * The callback receives the type of output (out or err) and some bytes
+     * from the output in real-time while writing the standard input to the process.
+     * It allows to have feedback from the independent process during execution.
+     *
+     * @param callable|null $callback A valid PHP callback
+     *
+     * @return int The exitcode of the process
+     *
+     * @throws RuntimeException When process timed out
+     * @throws RuntimeException When process stopped after receiving signal
+     * @throws LogicException   When process is not yet started
+     */
+    public function wait(callable $callback = null)
+    {
+        $this->requireProcessIsStarted(__FUNCTION__);
+
+        $this->updateStatus(false);
+
+        if (null !== $callback) {
+            if (!$this->processPipes->haveReadSupport()) {
+                $this->stop(0);
+                throw new \LogicException('Pass the callback to the "Process::start" method or call enableOutput to use a callback with "Process::wait"');
+            }
+            $this->callback = $this->buildCallback($callback);
+        }
+
+        do {
+            $this->checkTimeout();
+            $running = '\\' === \DIRECTORY_SEPARATOR ? $this->isRunning() : $this->processPipes->areOpen();
+            $this->readPipes($running, '\\' !== \DIRECTORY_SEPARATOR || !$running);
+        } while ($running);
+
+        while ($this->isRunning()) {
+            $this->checkTimeout();
+            usleep(1000);
+        }
+
+        if ($this->processInformation['signaled'] && $this->processInformation['termsig'] !== $this->latestSignal) {
+            throw new ProcessSignaledException($this);
+        }
+
+        return $this->exitcode;
+    }
+
+    /**
+     * Waits until the callback returns true.
+     *
+     * The callback receives the type of output (out or err) and some bytes
+     * from the output in real-time while writing the standard input to the process.
+     * It allows to have feedback from the independent process during execution.
+     *
+     * @throws RuntimeException When process timed out
+     * @throws LogicException   When process is not yet started
+     */
+    public function waitUntil(callable $callback): bool
+    {
+        $this->requireProcessIsStarted(__FUNCTION__);
+        $this->updateStatus(false);
+
+        if (!$this->processPipes->haveReadSupport()) {
+            $this->stop(0);
+            throw new \LogicException('Pass the callback to the "Process::start" method or call enableOutput to use a callback with "Process::waitUntil".');
+        }
+        $callback = $this->buildCallback($callback);
+
+        $ready = false;
+        while (true) {
+            $this->checkTimeout();
+            $running = '\\' === \DIRECTORY_SEPARATOR ? $this->isRunning() : $this->processPipes->areOpen();
+            $output = $this->processPipes->readAndWrite($running, '\\' !== \DIRECTORY_SEPARATOR || !$running);
+
+            foreach ($output as $type => $data) {
+                if (3 !== $type) {
+                    $ready = $callback(self::STDOUT === $type ? self::OUT : self::ERR, $data) || $ready;
+                } elseif (!isset($this->fallbackStatus['signaled'])) {
+                    $this->fallbackStatus['exitcode'] = (int) $data;
+                }
+            }
+            if ($ready) {
+                return true;
+            }
+            if (!$running) {
+                return false;
+            }
+
+            usleep(1000);
+        }
+    }
+
+    /**
+     * Returns the Pid (process identifier), if applicable.
+     *
+     * @return int|null The process id if running, null otherwise
+     */
+    public function getPid()
+    {
+        return $this->isRunning() ? $this->processInformation['pid'] : null;
+    }
+
+    /**
+     * Sends a POSIX signal to the process.
+     *
+     * @param int $signal A valid POSIX signal (see http://www.php.net/manual/en/pcntl.constants.php)
+     *
+     * @return $this
+     *
+     * @throws LogicException   In case the process is not running
+     * @throws RuntimeException In case --enable-sigchild is activated and the process can't be killed
+     * @throws RuntimeException In case of failure
+     */
+    public function signal($signal)
+    {
+        $this->doSignal($signal, true);
+
+        return $this;
+    }
+
+    /**
+     * Disables fetching output and error output from the underlying process.
+     *
+     * @return $this
+     *
+     * @throws RuntimeException In case the process is already running
+     * @throws LogicException   if an idle timeout is set
+     */
+    public function disableOutput()
+    {
+        if ($this->isRunning()) {
+            throw new RuntimeException('Disabling output while the process is running is not possible.');
+        }
+        if (null !== $this->idleTimeout) {
+            throw new LogicException('Output can not be disabled while an idle timeout is set.');
+        }
+
+        $this->outputDisabled = true;
+
+        return $this;
+    }
+
+    /**
+     * Enables fetching output and error output from the underlying process.
+     *
+     * @return $this
+     *
+     * @throws RuntimeException In case the process is already running
+     */
+    public function enableOutput()
+    {
+        if ($this->isRunning()) {
+            throw new RuntimeException('Enabling output while the process is running is not possible.');
+        }
+
+        $this->outputDisabled = false;
+
+        return $this;
+    }
+
+    /**
+     * Returns true in case the output is disabled, false otherwise.
+     *
+     * @return bool
+     */
+    public function isOutputDisabled()
+    {
+        return $this->outputDisabled;
+    }
+
+    /**
+     * Returns the current output of the process (STDOUT).
+     *
+     * @return string The process output
+     *
+     * @throws LogicException in case the output has been disabled
+     * @throws LogicException In case the process is not started
+     */
+    public function getOutput()
+    {
+        $this->readPipesForOutput(__FUNCTION__);
+
+        if (false === $ret = stream_get_contents($this->stdout, -1, 0)) {
+            return '';
+        }
+
+        return $ret;
+    }
+
+    /**
+     * Returns the output incrementally.
+     *
+     * In comparison with the getOutput method which always return the whole
+     * output, this one returns the new output since the last call.
+     *
+     * @return string The process output since the last call
+     *
+     * @throws LogicException in case the output has been disabled
+     * @throws LogicException In case the process is not started
+     */
+    public function getIncrementalOutput()
+    {
+        $this->readPipesForOutput(__FUNCTION__);
+
+        $latest = stream_get_contents($this->stdout, -1, $this->incrementalOutputOffset);
+        $this->incrementalOutputOffset = ftell($this->stdout);
+
+        if (false === $latest) {
+            return '';
+        }
+
+        return $latest;
+    }
+
+    /**
+     * Returns an iterator to the output of the process, with the output type as keys (Process::OUT/ERR).
+     *
+     * @param int $flags A bit field of Process::ITER_* flags
+     *
+     * @throws LogicException in case the output has been disabled
+     * @throws LogicException In case the process is not started
+     *
+     * @return \Generator
+     */
+    public function getIterator($flags = 0)
+    {
+        $this->readPipesForOutput(__FUNCTION__, false);
+
+        $clearOutput = !(self::ITER_KEEP_OUTPUT & $flags);
+        $blocking = !(self::ITER_NON_BLOCKING & $flags);
+        $yieldOut = !(self::ITER_SKIP_OUT & $flags);
+        $yieldErr = !(self::ITER_SKIP_ERR & $flags);
+
+        while (null !== $this->callback || ($yieldOut && !feof($this->stdout)) || ($yieldErr && !feof($this->stderr))) {
+            if ($yieldOut) {
+                $out = stream_get_contents($this->stdout, -1, $this->incrementalOutputOffset);
+
+                if (isset($out[0])) {
+                    if ($clearOutput) {
+                        $this->clearOutput();
+                    } else {
+                        $this->incrementalOutputOffset = ftell($this->stdout);
+                    }
+
+                    yield self::OUT => $out;
+                }
+            }
+
+            if ($yieldErr) {
+                $err = stream_get_contents($this->stderr, -1, $this->incrementalErrorOutputOffset);
+
+                if (isset($err[0])) {
+                    if ($clearOutput) {
+                        $this->clearErrorOutput();
+                    } else {
+                        $this->incrementalErrorOutputOffset = ftell($this->stderr);
+                    }
+
+                    yield self::ERR => $err;
+                }
+            }
+
+            if (!$blocking && !isset($out[0]) && !isset($err[0])) {
+                yield self::OUT => '';
+            }
+
+            $this->checkTimeout();
+            $this->readPipesForOutput(__FUNCTION__, $blocking);
+        }
+    }
+
+    /**
+     * Clears the process output.
+     *
+     * @return $this
+     */
+    public function clearOutput()
+    {
+        ftruncate($this->stdout, 0);
+        fseek($this->stdout, 0);
+        $this->incrementalOutputOffset = 0;
+
+        return $this;
+    }
+
+    /**
+     * Returns the current error output of the process (STDERR).
+     *
+     * @return string The process error output
+     *
+     * @throws LogicException in case the output has been disabled
+     * @throws LogicException In case the process is not started
+     */
+    public function getErrorOutput()
+    {
+        $this->readPipesForOutput(__FUNCTION__);
+
+        if (false === $ret = stream_get_contents($this->stderr, -1, 0)) {
+            return '';
+        }
+
+        return $ret;
+    }
+
+    /**
+     * Returns the errorOutput incrementally.
+     *
+     * In comparison with the getErrorOutput method which always return the
+     * whole error output, this one returns the new error output since the last
+     * call.
+     *
+     * @return string The process error output since the last call
+     *
+     * @throws LogicException in case the output has been disabled
+     * @throws LogicException In case the process is not started
+     */
+    public function getIncrementalErrorOutput()
+    {
+        $this->readPipesForOutput(__FUNCTION__);
+
+        $latest = stream_get_contents($this->stderr, -1, $this->incrementalErrorOutputOffset);
+        $this->incrementalErrorOutputOffset = ftell($this->stderr);
+
+        if (false === $latest) {
+            return '';
+        }
+
+        return $latest;
+    }
+
+    /**
+     * Clears the process output.
+     *
+     * @return $this
+     */
+    public function clearErrorOutput()
+    {
+        ftruncate($this->stderr, 0);
+        fseek($this->stderr, 0);
+        $this->incrementalErrorOutputOffset = 0;
+
+        return $this;
+    }
+
+    /**
+     * Returns the exit code returned by the process.
+     *
+     * @return int|null The exit status code, null if the Process is not terminated
+     */
+    public function getExitCode()
+    {
+        $this->updateStatus(false);
+
+        return $this->exitcode;
+    }
+
+    /**
+     * Returns a string representation for the exit code returned by the process.
+     *
+     * This method relies on the Unix exit code status standardization
+     * and might not be relevant for other operating systems.
+     *
+     * @return string|null A string representation for the exit status code, null if the Process is not terminated
+     *
+     * @see http://tldp.org/LDP/abs/html/exitcodes.html
+     * @see http://en.wikipedia.org/wiki/Unix_signal
+     */
+    public function getExitCodeText()
+    {
+        if (null === $exitcode = $this->getExitCode()) {
+            return;
+        }
+
+        return isset(self::$exitCodes[$exitcode]) ? self::$exitCodes[$exitcode] : 'Unknown error';
+    }
+
+    /**
+     * Checks if the process ended successfully.
+     *
+     * @return bool true if the process ended successfully, false otherwise
+     */
+    public function isSuccessful()
+    {
+        return 0 === $this->getExitCode();
+    }
+
+    /**
+     * Returns true if the child process has been terminated by an uncaught signal.
+     *
+     * It always returns false on Windows.
+     *
+     * @return bool
+     *
+     * @throws LogicException In case the process is not terminated
+     */
+    public function hasBeenSignaled()
+    {
+        $this->requireProcessIsTerminated(__FUNCTION__);
+
+        return $this->processInformation['signaled'];
+    }
+
+    /**
+     * Returns the number of the signal that caused the child process to terminate its execution.
+     *
+     * It is only meaningful if hasBeenSignaled() returns true.
+     *
+     * @return int
+     *
+     * @throws RuntimeException In case --enable-sigchild is activated
+     * @throws LogicException   In case the process is not terminated
+     */
+    public function getTermSignal()
+    {
+        $this->requireProcessIsTerminated(__FUNCTION__);
+
+        if ($this->isSigchildEnabled() && -1 === $this->processInformation['termsig']) {
+            throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.');
+        }
+
+        return $this->processInformation['termsig'];
+    }
+
+    /**
+     * Returns true if the child process has been stopped by a signal.
+     *
+     * It always returns false on Windows.
+     *
+     * @return bool
+     *
+     * @throws LogicException In case the process is not terminated
+     */
+    public function hasBeenStopped()
+    {
+        $this->requireProcessIsTerminated(__FUNCTION__);
+
+        return $this->processInformation['stopped'];
+    }
+
+    /**
+     * Returns the number of the signal that caused the child process to stop its execution.
+     *
+     * It is only meaningful if hasBeenStopped() returns true.
+     *
+     * @return int
+     *
+     * @throws LogicException In case the process is not terminated
+     */
+    public function getStopSignal()
+    {
+        $this->requireProcessIsTerminated(__FUNCTION__);
+
+        return $this->processInformation['stopsig'];
+    }
+
+    /**
+     * Checks if the process is currently running.
+     *
+     * @return bool true if the process is currently running, false otherwise
+     */
+    public function isRunning()
+    {
+        if (self::STATUS_STARTED !== $this->status) {
+            return false;
+        }
+
+        $this->updateStatus(false);
+
+        return $this->processInformation['running'];
+    }
+
+    /**
+     * Checks if the process has been started with no regard to the current state.
+     *
+     * @return bool true if status is ready, false otherwise
+     */
+    public function isStarted()
+    {
+        return self::STATUS_READY != $this->status;
+    }
+
+    /**
+     * Checks if the process is terminated.
+     *
+     * @return bool true if process is terminated, false otherwise
+     */
+    public function isTerminated()
+    {
+        $this->updateStatus(false);
+
+        return self::STATUS_TERMINATED == $this->status;
+    }
+
+    /**
+     * Gets the process status.
+     *
+     * The status is one of: ready, started, terminated.
+     *
+     * @return string The current process status
+     */
+    public function getStatus()
+    {
+        $this->updateStatus(false);
+
+        return $this->status;
+    }
+
+    /**
+     * Stops the process.
+     *
+     * @param int|float $timeout The timeout in seconds
+     * @param int       $signal  A POSIX signal to send in case the process has not stop at timeout, default is SIGKILL (9)
+     *
+     * @return int The exit-code of the process
+     */
+    public function stop($timeout = 10, $signal = null)
+    {
+        $timeoutMicro = microtime(true) + $timeout;
+        if ($this->isRunning()) {
+            // given SIGTERM may not be defined and that "proc_terminate" uses the constant value and not the constant itself, we use the same here
+            $this->doSignal(15, false);
+            do {
+                usleep(1000);
+            } while ($this->isRunning() && microtime(true) < $timeoutMicro);
+
+            if ($this->isRunning()) {
+                // Avoid exception here: process is supposed to be running, but it might have stopped just
+                // after this line. In any case, let's silently discard the error, we cannot do anything.
+                $this->doSignal($signal ?: 9, false);
+            }
+        }
+
+        if ($this->isRunning()) {
+            if (isset($this->fallbackStatus['pid'])) {
+                unset($this->fallbackStatus['pid']);
+
+                return $this->stop(0, $signal);
+            }
+            $this->close();
+        }
+
+        return $this->exitcode;
+    }
+
+    /**
+     * Adds a line to the STDOUT stream.
+     *
+     * @internal
+     */
+    public function addOutput(string $line)
+    {
+        $this->lastOutputTime = microtime(true);
+
+        fseek($this->stdout, 0, SEEK_END);
+        fwrite($this->stdout, $line);
+        fseek($this->stdout, $this->incrementalOutputOffset);
+    }
+
+    /**
+     * Adds a line to the STDERR stream.
+     *
+     * @internal
+     */
+    public function addErrorOutput(string $line)
+    {
+        $this->lastOutputTime = microtime(true);
+
+        fseek($this->stderr, 0, SEEK_END);
+        fwrite($this->stderr, $line);
+        fseek($this->stderr, $this->incrementalErrorOutputOffset);
+    }
+
+    /**
+     * Gets the command line to be executed.
+     *
+     * @return string The command to execute
+     */
+    public function getCommandLine()
+    {
+        return \is_array($this->commandline) ? implode(' ', array_map([$this, 'escapeArgument'], $this->commandline)) : $this->commandline;
+    }
+
+    /**
+     * Sets the command line to be executed.
+     *
+     * @param string|array $commandline The command to execute
+     *
+     * @return self The current Process instance
+     *
+     * @deprecated since Symfony 4.2.
+     */
+    public function setCommandLine($commandline)
+    {
+        @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2.', __METHOD__), E_USER_DEPRECATED);
+
+        $this->commandline = $commandline;
+
+        return $this;
+    }
+
+    /**
+     * Gets the process timeout (max. runtime).
+     *
+     * @return float|null The timeout in seconds or null if it's disabled
+     */
+    public function getTimeout()
+    {
+        return $this->timeout;
+    }
+
+    /**
+     * Gets the process idle timeout (max. time since last output).
+     *
+     * @return float|null The timeout in seconds or null if it's disabled
+     */
+    public function getIdleTimeout()
+    {
+        return $this->idleTimeout;
+    }
+
+    /**
+     * Sets the process timeout (max. runtime).
+     *
+     * To disable the timeout, set this value to null.
+     *
+     * @param int|float|null $timeout The timeout in seconds
+     *
+     * @return self The current Process instance
+     *
+     * @throws InvalidArgumentException if the timeout is negative
+     */
+    public function setTimeout($timeout)
+    {
+        $this->timeout = $this->validateTimeout($timeout);
+
+        return $this;
+    }
+
+    /**
+     * Sets the process idle timeout (max. time since last output).
+     *
+     * To disable the timeout, set this value to null.
+     *
+     * @param int|float|null $timeout The timeout in seconds
+     *
+     * @return self The current Process instance
+     *
+     * @throws LogicException           if the output is disabled
+     * @throws InvalidArgumentException if the timeout is negative
+     */
+    public function setIdleTimeout($timeout)
+    {
+        if (null !== $timeout && $this->outputDisabled) {
+            throw new LogicException('Idle timeout can not be set while the output is disabled.');
+        }
+
+        $this->idleTimeout = $this->validateTimeout($timeout);
+
+        return $this;
+    }
+
+    /**
+     * Enables or disables the TTY mode.
+     *
+     * @param bool $tty True to enabled and false to disable
+     *
+     * @return self The current Process instance
+     *
+     * @throws RuntimeException In case the TTY mode is not supported
+     */
+    public function setTty($tty)
+    {
+        if ('\\' === \DIRECTORY_SEPARATOR && $tty) {
+            throw new RuntimeException('TTY mode is not supported on Windows platform.');
+        }
+
+        if ($tty && !self::isTtySupported()) {
+            throw new RuntimeException('TTY mode requires /dev/tty to be read/writable.');
+        }
+
+        $this->tty = (bool) $tty;
+
+        return $this;
+    }
+
+    /**
+     * Checks if the TTY mode is enabled.
+     *
+     * @return bool true if the TTY mode is enabled, false otherwise
+     */
+    public function isTty()
+    {
+        return $this->tty;
+    }
+
+    /**
+     * Sets PTY mode.
+     *
+     * @param bool $bool
+     *
+     * @return self
+     */
+    public function setPty($bool)
+    {
+        $this->pty = (bool) $bool;
+
+        return $this;
+    }
+
+    /**
+     * Returns PTY state.
+     *
+     * @return bool
+     */
+    public function isPty()
+    {
+        return $this->pty;
+    }
+
+    /**
+     * Gets the working directory.
+     *
+     * @return string|null The current working directory or null on failure
+     */
+    public function getWorkingDirectory()
+    {
+        if (null === $this->cwd) {
+            // getcwd() will return false if any one of the parent directories does not have
+            // the readable or search mode set, even if the current directory does
+            return getcwd() ?: null;
+        }
+
+        return $this->cwd;
+    }
+
+    /**
+     * Sets the current working directory.
+     *
+     * @param string $cwd The new working directory
+     *
+     * @return self The current Process instance
+     */
+    public function setWorkingDirectory($cwd)
+    {
+        $this->cwd = $cwd;
+
+        return $this;
+    }
+
+    /**
+     * Gets the environment variables.
+     *
+     * @return array The current environment variables
+     */
+    public function getEnv()
+    {
+        return $this->env;
+    }
+
+    /**
+     * Sets the environment variables.
+     *
+     * Each environment variable value should be a string.
+     * If it is an array, the variable is ignored.
+     * If it is false or null, it will be removed when
+     * env vars are otherwise inherited.
+     *
+     * That happens in PHP when 'argv' is registered into
+     * the $_ENV array for instance.
+     *
+     * @param array $env The new environment variables
+     *
+     * @return self The current Process instance
+     */
+    public function setEnv(array $env)
+    {
+        // Process can not handle env values that are arrays
+        $env = array_filter($env, function ($value) {
+            return !\is_array($value);
+        });
+
+        $this->env = $env;
+
+        return $this;
+    }
+
+    /**
+     * Gets the Process input.
+     *
+     * @return resource|string|\Iterator|null The Process input
+     */
+    public function getInput()
+    {
+        return $this->input;
+    }
+
+    /**
+     * Sets the input.
+     *
+     * This content will be passed to the underlying process standard input.
+     *
+     * @param string|int|float|bool|resource|\Traversable|null $input The content
+     *
+     * @return self The current Process instance
+     *
+     * @throws LogicException In case the process is running
+     */
+    public function setInput($input)
+    {
+        if ($this->isRunning()) {
+            throw new LogicException('Input can not be set while the process is running.');
+        }
+
+        $this->input = ProcessUtils::validateInput(__METHOD__, $input);
+
+        return $this;
+    }
+
+    /**
+     * Sets whether environment variables will be inherited or not.
+     *
+     * @param bool $inheritEnv
+     *
+     * @return self The current Process instance
+     */
+    public function inheritEnvironmentVariables($inheritEnv = true)
+    {
+        if (!$inheritEnv) {
+            throw new InvalidArgumentException('Not inheriting environment variables is not supported.');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Performs a check between the timeout definition and the time the process started.
+     *
+     * In case you run a background process (with the start method), you should
+     * trigger this method regularly to ensure the process timeout
+     *
+     * @throws ProcessTimedOutException In case the timeout was reached
+     */
+    public function checkTimeout()
+    {
+        if (self::STATUS_STARTED !== $this->status) {
+            return;
+        }
+
+        if (null !== $this->timeout && $this->timeout < microtime(true) - $this->starttime) {
+            $this->stop(0);
+
+            throw new ProcessTimedOutException($this, ProcessTimedOutException::TYPE_GENERAL);
+        }
+
+        if (null !== $this->idleTimeout && $this->idleTimeout < microtime(true) - $this->lastOutputTime) {
+            $this->stop(0);
+
+            throw new ProcessTimedOutException($this, ProcessTimedOutException::TYPE_IDLE);
+        }
+    }
+
+    /**
+     * Returns whether TTY is supported on the current operating system.
+     */
+    public static function isTtySupported(): bool
+    {
+        static $isTtySupported;
+
+        if (null === $isTtySupported) {
+            $isTtySupported = (bool) @proc_open('echo 1 >/dev/null', [['file', '/dev/tty', 'r'], ['file', '/dev/tty', 'w'], ['file', '/dev/tty', 'w']], $pipes);
+        }
+
+        return $isTtySupported;
+    }
+
+    /**
+     * Returns whether PTY is supported on the current operating system.
+     *
+     * @return bool
+     */
+    public static function isPtySupported()
+    {
+        static $result;
+
+        if (null !== $result) {
+            return $result;
+        }
+
+        if ('\\' === \DIRECTORY_SEPARATOR) {
+            return $result = false;
+        }
+
+        return $result = (bool) @proc_open('echo 1 >/dev/null', [['pty'], ['pty'], ['pty']], $pipes);
+    }
+
+    /**
+     * Creates the descriptors needed by the proc_open.
+     */
+    private function getDescriptors(): array
+    {
+        if ($this->input instanceof \Iterator) {
+            $this->input->rewind();
+        }
+        if ('\\' === \DIRECTORY_SEPARATOR) {
+            $this->processPipes = new WindowsPipes($this->input, !$this->outputDisabled || $this->hasCallback);
+        } else {
+            $this->processPipes = new UnixPipes($this->isTty(), $this->isPty(), $this->input, !$this->outputDisabled || $this->hasCallback);
+        }
+
+        return $this->processPipes->getDescriptors();
+    }
+
+    /**
+     * Builds up the callback used by wait().
+     *
+     * The callbacks adds all occurred output to the specific buffer and calls
+     * the user callback (if present) with the received output.
+     *
+     * @param callable|null $callback The user defined PHP callback
+     *
+     * @return \Closure A PHP closure
+     */
+    protected function buildCallback(callable $callback = null)
+    {
+        if ($this->outputDisabled) {
+            return function ($type, $data) use ($callback) {
+                if (null !== $callback) {
+                    return $callback($type, $data);
+                }
+            };
+        }
+
+        $out = self::OUT;
+
+        return function ($type, $data) use ($callback, $out) {
+            if ($out == $type) {
+                $this->addOutput($data);
+            } else {
+                $this->addErrorOutput($data);
+            }
+
+            if (null !== $callback) {
+                return $callback($type, $data);
+            }
+        };
+    }
+
+    /**
+     * Updates the status of the process, reads pipes.
+     *
+     * @param bool $blocking Whether to use a blocking read call
+     */
+    protected function updateStatus($blocking)
+    {
+        if (self::STATUS_STARTED !== $this->status) {
+            return;
+        }
+
+        $this->processInformation = proc_get_status($this->process);
+        $running = $this->processInformation['running'];
+
+        $this->readPipes($running && $blocking, '\\' !== \DIRECTORY_SEPARATOR || !$running);
+
+        if ($this->fallbackStatus && $this->isSigchildEnabled()) {
+            $this->processInformation = $this->fallbackStatus + $this->processInformation;
+        }
+
+        if (!$running) {
+            $this->close();
+        }
+    }
+
+    /**
+     * Returns whether PHP has been compiled with the '--enable-sigchild' option or not.
+     *
+     * @return bool
+     */
+    protected function isSigchildEnabled()
+    {
+        if (null !== self::$sigchild) {
+            return self::$sigchild;
+        }
+
+        if (!\function_exists('phpinfo')) {
+            return self::$sigchild = false;
+        }
+
+        ob_start();
+        phpinfo(INFO_GENERAL);
+
+        return self::$sigchild = false !== strpos(ob_get_clean(), '--enable-sigchild');
+    }
+
+    /**
+     * Reads pipes for the freshest output.
+     *
+     * @param string $caller   The name of the method that needs fresh outputs
+     * @param bool   $blocking Whether to use blocking calls or not
+     *
+     * @throws LogicException in case output has been disabled or process is not started
+     */
+    private function readPipesForOutput(string $caller, bool $blocking = false)
+    {
+        if ($this->outputDisabled) {
+            throw new LogicException('Output has been disabled.');
+        }
+
+        $this->requireProcessIsStarted($caller);
+
+        $this->updateStatus($blocking);
+    }
+
+    /**
+     * Validates and returns the filtered timeout.
+     *
+     * @throws InvalidArgumentException if the given timeout is a negative number
+     */
+    private function validateTimeout(?float $timeout): ?float
+    {
+        $timeout = (float) $timeout;
+
+        if (0.0 === $timeout) {
+            $timeout = null;
+        } elseif ($timeout < 0) {
+            throw new InvalidArgumentException('The timeout value must be a valid positive integer or float number.');
+        }
+
+        return $timeout;
+    }
+
+    /**
+     * Reads pipes, executes callback.
+     *
+     * @param bool $blocking Whether to use blocking calls or not
+     * @param bool $close    Whether to close file handles or not
+     */
+    private function readPipes(bool $blocking, bool $close)
+    {
+        $result = $this->processPipes->readAndWrite($blocking, $close);
+
+        $callback = $this->callback;
+        foreach ($result as $type => $data) {
+            if (3 !== $type) {
+                $callback(self::STDOUT === $type ? self::OUT : self::ERR, $data);
+            } elseif (!isset($this->fallbackStatus['signaled'])) {
+                $this->fallbackStatus['exitcode'] = (int) $data;
+            }
+        }
+    }
+
+    /**
+     * Closes process resource, closes file handles, sets the exitcode.
+     *
+     * @return int The exitcode
+     */
+    private function close(): int
+    {
+        $this->processPipes->close();
+        if (\is_resource($this->process)) {
+            proc_close($this->process);
+        }
+        $this->exitcode = $this->processInformation['exitcode'];
+        $this->status = self::STATUS_TERMINATED;
+
+        if (-1 === $this->exitcode) {
+            if ($this->processInformation['signaled'] && 0 < $this->processInformation['termsig']) {
+                // if process has been signaled, no exitcode but a valid termsig, apply Unix convention
+                $this->exitcode = 128 + $this->processInformation['termsig'];
+            } elseif ($this->isSigchildEnabled()) {
+                $this->processInformation['signaled'] = true;
+                $this->processInformation['termsig'] = -1;
+            }
+        }
+
+        // Free memory from self-reference callback created by buildCallback
+        // Doing so in other contexts like __destruct or by garbage collector is ineffective
+        // Now pipes are closed, so the callback is no longer necessary
+        $this->callback = null;
+
+        return $this->exitcode;
+    }
+
+    /**
+     * Resets data related to the latest run of the process.
+     */
+    private function resetProcessData()
+    {
+        $this->starttime = null;
+        $this->callback = null;
+        $this->exitcode = null;
+        $this->fallbackStatus = [];
+        $this->processInformation = null;
+        $this->stdout = fopen('php://temp/maxmemory:'.(1024 * 1024), 'w+b');
+        $this->stderr = fopen('php://temp/maxmemory:'.(1024 * 1024), 'w+b');
+        $this->process = null;
+        $this->latestSignal = null;
+        $this->status = self::STATUS_READY;
+        $this->incrementalOutputOffset = 0;
+        $this->incrementalErrorOutputOffset = 0;
+    }
+
+    /**
+     * Sends a POSIX signal to the process.
+     *
+     * @param int  $signal         A valid POSIX signal (see http://www.php.net/manual/en/pcntl.constants.php)
+     * @param bool $throwException Whether to throw exception in case signal failed
+     *
+     * @return bool True if the signal was sent successfully, false otherwise
+     *
+     * @throws LogicException   In case the process is not running
+     * @throws RuntimeException In case --enable-sigchild is activated and the process can't be killed
+     * @throws RuntimeException In case of failure
+     */
+    private function doSignal(int $signal, bool $throwException): bool
+    {
+        if (null === $pid = $this->getPid()) {
+            if ($throwException) {
+                throw new LogicException('Can not send signal on a non running process.');
+            }
+
+            return false;
+        }
+
+        if ('\\' === \DIRECTORY_SEPARATOR) {
+            exec(sprintf('taskkill /F /T /PID %d 2>&1', $pid), $output, $exitCode);
+            if ($exitCode && $this->isRunning()) {
+                if ($throwException) {
+                    throw new RuntimeException(sprintf('Unable to kill the process (%s).', implode(' ', $output)));
+                }
+
+                return false;
+            }
+        } else {
+            if (!$this->isSigchildEnabled()) {
+                $ok = @proc_terminate($this->process, $signal);
+            } elseif (\function_exists('posix_kill')) {
+                $ok = @posix_kill($pid, $signal);
+            } elseif ($ok = proc_open(sprintf('kill -%d %d', $signal, $pid), [2 => ['pipe', 'w']], $pipes)) {
+                $ok = false === fgets($pipes[2]);
+            }
+            if (!$ok) {
+                if ($throwException) {
+                    throw new RuntimeException(sprintf('Error while sending signal "%s".', $signal));
+                }
+
+                return false;
+            }
+        }
+
+        $this->latestSignal = $signal;
+        $this->fallbackStatus['signaled'] = true;
+        $this->fallbackStatus['exitcode'] = -1;
+        $this->fallbackStatus['termsig'] = $this->latestSignal;
+
+        return true;
+    }
+
+    private function prepareWindowsCommandLine(string $cmd, array &$env)
+    {
+        $uid = uniqid('', true);
+        $varCount = 0;
+        $varCache = [];
+        $cmd = preg_replace_callback(
+            '/"(?:(
+                [^"%!^]*+
+                (?:
+                    (?: !LF! | "(?:\^[%!^])?+" )
+                    [^"%!^]*+
+                )++
+            ) | [^"]*+ )"/x',
+            function ($m) use (&$env, &$varCache, &$varCount, $uid) {
+                if (!isset($m[1])) {
+                    return $m[0];
+                }
+                if (isset($varCache[$m[0]])) {
+                    return $varCache[$m[0]];
+                }
+                if (false !== strpos($value = $m[1], "\0")) {
+                    $value = str_replace("\0", '?', $value);
+                }
+                if (false === strpbrk($value, "\"%!\n")) {
+                    return '"'.$value.'"';
+                }
+
+                $value = str_replace(['!LF!', '"^!"', '"^%"', '"^^"', '""'], ["\n", '!', '%', '^', '"'], $value);
+                $value = '"'.preg_replace('/(\\\\*)"/', '$1$1\\"', $value).'"';
+                $var = $uid.++$varCount;
+
+                $env[$var] = $value;
+
+                return $varCache[$m[0]] = '!'.$var.'!';
+            },
+            $cmd
+        );
+
+        $cmd = 'cmd /V:ON /E:ON /D /C ('.str_replace("\n", ' ', $cmd).')';
+        foreach ($this->processPipes->getFiles() as $offset => $filename) {
+            $cmd .= ' '.$offset.'>"'.$filename.'"';
+        }
+
+        return $cmd;
+    }
+
+    /**
+     * Ensures the process is running or terminated, throws a LogicException if the process has a not started.
+     *
+     * @throws LogicException if the process has not run
+     */
+    private function requireProcessIsStarted(string $functionName)
+    {
+        if (!$this->isStarted()) {
+            throw new LogicException(sprintf('Process must be started before calling %s.', $functionName));
+        }
+    }
+
+    /**
+     * Ensures the process is terminated, throws a LogicException if the process has a status different than "terminated".
+     *
+     * @throws LogicException if the process is not yet terminated
+     */
+    private function requireProcessIsTerminated(string $functionName)
+    {
+        if (!$this->isTerminated()) {
+            throw new LogicException(sprintf('Process must be terminated before calling %s.', $functionName));
+        }
+    }
+
+    /**
+     * Escapes a string to be used as a shell argument.
+     */
+    private function escapeArgument(?string $argument): string
+    {
+        if ('' === $argument || null === $argument) {
+            return '""';
+        }
+        if ('\\' !== \DIRECTORY_SEPARATOR) {
+            return "'".str_replace("'", "'\\''", $argument)."'";
+        }
+        if (false !== strpos($argument, "\0")) {
+            $argument = str_replace("\0", '?', $argument);
+        }
+        if (!preg_match('/[\/()%!^"<>&|\s]/', $argument)) {
+            return $argument;
+        }
+        $argument = preg_replace('/(\\\\+)$/', '$1$1', $argument);
+
+        return '"'.str_replace(['"', '^', '%', '!', "\n"], ['""', '"^^"', '"^%"', '"^!"', '!LF!'], $argument).'"';
+    }
+
+    private function getDefaultEnv()
+    {
+        $env = [];
+
+        foreach ($_SERVER as $k => $v) {
+            if (\is_string($v) && false !== $v = getenv($k)) {
+                $env[$k] = $v;
+            }
+        }
+
+        foreach ($_ENV as $k => $v) {
+            if (\is_string($v)) {
+                $env[$k] = $v;
+            }
+        }
+
+        return $env;
+    }
+}
diff --git a/vendor/symfony/process/ProcessUtils.php b/vendor/symfony/process/ProcessUtils.php
new file mode 100755
index 0000000..2f9c4be
--- /dev/null
+++ b/vendor/symfony/process/ProcessUtils.php
@@ -0,0 +1,69 @@
+<?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\Process;
+
+use Symfony\Component\Process\Exception\InvalidArgumentException;
+
+/**
+ * ProcessUtils is a bunch of utility methods.
+ *
+ * This class contains static methods only and is not meant to be instantiated.
+ *
+ * @author Martin Hasoň <martin.hason@gmail.com>
+ */
+class ProcessUtils
+{
+    /**
+     * This class should not be instantiated.
+     */
+    private function __construct()
+    {
+    }
+
+    /**
+     * Validates and normalizes a Process input.
+     *
+     * @param string $caller The name of method call that validates the input
+     * @param mixed  $input  The input to validate
+     *
+     * @return mixed The validated input
+     *
+     * @throws InvalidArgumentException In case the input is not valid
+     */
+    public static function validateInput($caller, $input)
+    {
+        if (null !== $input) {
+            if (\is_resource($input)) {
+                return $input;
+            }
+            if (\is_string($input)) {
+                return $input;
+            }
+            if (is_scalar($input)) {
+                return (string) $input;
+            }
+            if ($input instanceof Process) {
+                return $input->getIterator($input::ITER_SKIP_ERR);
+            }
+            if ($input instanceof \Iterator) {
+                return $input;
+            }
+            if ($input instanceof \Traversable) {
+                return new \IteratorIterator($input);
+            }
+
+            throw new InvalidArgumentException(sprintf('%s only accepts strings, Traversable objects or stream resources.', $caller));
+        }
+
+        return $input;
+    }
+}
diff --git a/vendor/symfony/process/README.md b/vendor/symfony/process/README.md
new file mode 100755
index 0000000..b7ca5b4
--- /dev/null
+++ b/vendor/symfony/process/README.md
@@ -0,0 +1,13 @@
+Process Component
+=================
+
+The Process component executes commands in sub-processes.
+
+Resources
+---------
+
+  * [Documentation](https://symfony.com/doc/current/components/process.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)
diff --git a/vendor/symfony/process/Tests/ErrorProcessInitiator.php b/vendor/symfony/process/Tests/ErrorProcessInitiator.php
new file mode 100755
index 0000000..c37aeb5
--- /dev/null
+++ b/vendor/symfony/process/Tests/ErrorProcessInitiator.php
@@ -0,0 +1,36 @@
+<?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\Process\Tests;
+
+use Symfony\Component\Process\Exception\ProcessTimedOutException;
+use Symfony\Component\Process\Process;
+
+require \dirname(__DIR__).'/vendor/autoload.php';
+
+list('e' => $php) = getopt('e:') + ['e' => 'php'];
+
+try {
+    $process = new Process("exec $php -r \"echo 'ready'; trigger_error('error', E_USER_ERROR);\"");
+    $process->start();
+    $process->setTimeout(0.5);
+    while (false === strpos($process->getOutput(), 'ready')) {
+        usleep(1000);
+    }
+    $process->signal(SIGSTOP);
+    $process->wait();
+
+    return $process->getExitCode();
+} catch (ProcessTimedOutException $t) {
+    echo $t->getMessage().PHP_EOL;
+
+    return 1;
+}
diff --git a/vendor/symfony/process/Tests/ExecutableFinderTest.php b/vendor/symfony/process/Tests/ExecutableFinderTest.php
new file mode 100755
index 0000000..6d69a77
--- /dev/null
+++ b/vendor/symfony/process/Tests/ExecutableFinderTest.php
@@ -0,0 +1,178 @@
+<?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\Process\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Process\ExecutableFinder;
+
+/**
+ * @author Chris Smith <chris@cs278.org>
+ */
+class ExecutableFinderTest extends TestCase
+{
+    private $path;
+
+    protected function tearDown()
+    {
+        if ($this->path) {
+            // Restore path if it was changed.
+            putenv('PATH='.$this->path);
+        }
+    }
+
+    private function setPath($path)
+    {
+        $this->path = getenv('PATH');
+        putenv('PATH='.$path);
+    }
+
+    public function testFind()
+    {
+        if (ini_get('open_basedir')) {
+            $this->markTestSkipped('Cannot test when open_basedir is set');
+        }
+
+        $this->setPath(\dirname(PHP_BINARY));
+
+        $finder = new ExecutableFinder();
+        $result = $finder->find($this->getPhpBinaryName());
+
+        $this->assertSamePath(PHP_BINARY, $result);
+    }
+
+    public function testFindWithDefault()
+    {
+        if (ini_get('open_basedir')) {
+            $this->markTestSkipped('Cannot test when open_basedir is set');
+        }
+
+        $expected = 'defaultValue';
+
+        $this->setPath('');
+
+        $finder = new ExecutableFinder();
+        $result = $finder->find('foo', $expected);
+
+        $this->assertEquals($expected, $result);
+    }
+
+    public function testFindWithNullAsDefault()
+    {
+        if (ini_get('open_basedir')) {
+            $this->markTestSkipped('Cannot test when open_basedir is set');
+        }
+
+        $this->setPath('');
+
+        $finder = new ExecutableFinder();
+
+        $result = $finder->find('foo');
+
+        $this->assertNull($result);
+    }
+
+    public function testFindWithExtraDirs()
+    {
+        if (ini_get('open_basedir')) {
+            $this->markTestSkipped('Cannot test when open_basedir is set');
+        }
+
+        $this->setPath('');
+
+        $extraDirs = [\dirname(PHP_BINARY)];
+
+        $finder = new ExecutableFinder();
+        $result = $finder->find($this->getPhpBinaryName(), null, $extraDirs);
+
+        $this->assertSamePath(PHP_BINARY, $result);
+    }
+
+    public function testFindWithOpenBaseDir()
+    {
+        if ('\\' === \DIRECTORY_SEPARATOR) {
+            $this->markTestSkipped('Cannot run test on windows');
+        }
+
+        if (ini_get('open_basedir')) {
+            $this->markTestSkipped('Cannot test when open_basedir is set');
+        }
+
+        $this->iniSet('open_basedir', \dirname(PHP_BINARY).PATH_SEPARATOR.'/');
+
+        $finder = new ExecutableFinder();
+        $result = $finder->find($this->getPhpBinaryName());
+
+        $this->assertSamePath(PHP_BINARY, $result);
+    }
+
+    public function testFindProcessInOpenBasedir()
+    {
+        if (ini_get('open_basedir')) {
+            $this->markTestSkipped('Cannot test when open_basedir is set');
+        }
+        if ('\\' === \DIRECTORY_SEPARATOR) {
+            $this->markTestSkipped('Cannot run test on windows');
+        }
+
+        $this->setPath('');
+        $this->iniSet('open_basedir', PHP_BINARY.PATH_SEPARATOR.'/');
+
+        $finder = new ExecutableFinder();
+        $result = $finder->find($this->getPhpBinaryName(), false);
+
+        $this->assertSamePath(PHP_BINARY, $result);
+    }
+
+    /**
+     * @requires PHP 5.4
+     */
+    public function testFindBatchExecutableOnWindows()
+    {
+        if (ini_get('open_basedir')) {
+            $this->markTestSkipped('Cannot test when open_basedir is set');
+        }
+        if ('\\' !== \DIRECTORY_SEPARATOR) {
+            $this->markTestSkipped('Can be only tested on windows');
+        }
+
+        $target = tempnam(sys_get_temp_dir(), 'example-windows-executable');
+
+        touch($target);
+        touch($target.'.BAT');
+
+        $this->assertFalse(is_executable($target));
+
+        $this->setPath(sys_get_temp_dir());
+
+        $finder = new ExecutableFinder();
+        $result = $finder->find(basename($target), false);
+
+        unlink($target);
+        unlink($target.'.BAT');
+
+        $this->assertSamePath($target.'.BAT', $result);
+    }
+
+    private function assertSamePath($expected, $tested)
+    {
+        if ('\\' === \DIRECTORY_SEPARATOR) {
+            $this->assertEquals(strtolower($expected), strtolower($tested));
+        } else {
+            $this->assertEquals($expected, $tested);
+        }
+    }
+
+    private function getPhpBinaryName()
+    {
+        return basename(PHP_BINARY, '\\' === \DIRECTORY_SEPARATOR ? '.exe' : '');
+    }
+}
diff --git a/vendor/symfony/process/Tests/KillableProcessWithOutput.php b/vendor/symfony/process/Tests/KillableProcessWithOutput.php
new file mode 100755
index 0000000..28a6a27
--- /dev/null
+++ b/vendor/symfony/process/Tests/KillableProcessWithOutput.php
@@ -0,0 +1,25 @@
+<?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.
+ */
+
+$outputs = [
+    'First iteration output',
+    'Second iteration output',
+    'One more iteration output',
+    'This took more time',
+];
+
+$iterationTime = 10000;
+
+foreach ($outputs as $output) {
+    usleep($iterationTime);
+    $iterationTime *= 10;
+    echo $output."\n";
+}
diff --git a/vendor/symfony/process/Tests/NonStopableProcess.php b/vendor/symfony/process/Tests/NonStopableProcess.php
new file mode 100755
index 0000000..5643259
--- /dev/null
+++ b/vendor/symfony/process/Tests/NonStopableProcess.php
@@ -0,0 +1,47 @@
+<?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.
+ */
+
+/**
+ * Runs a PHP script that can be stopped only with a SIGKILL (9) signal for 3 seconds.
+ *
+ * @args duration Run this script with a custom duration
+ *
+ * @example `php NonStopableProcess.php 42` will run the script for 42 seconds
+ */
+function handleSignal($signal)
+{
+    switch ($signal) {
+        case SIGTERM:
+            $name = 'SIGTERM';
+            break;
+        case SIGINT:
+            $name = 'SIGINT';
+            break;
+        default:
+            $name = $signal.' (unknown)';
+            break;
+    }
+
+    echo "signal $name\n";
+}
+
+pcntl_signal(SIGTERM, 'handleSignal');
+pcntl_signal(SIGINT, 'handleSignal');
+
+echo 'received ';
+
+$duration = isset($argv[1]) ? (int) $argv[1] : 3;
+$start = microtime(true);
+
+while ($duration > (microtime(true) - $start)) {
+    usleep(10000);
+    pcntl_signal_dispatch();
+}
diff --git a/vendor/symfony/process/Tests/PhpExecutableFinderTest.php b/vendor/symfony/process/Tests/PhpExecutableFinderTest.php
new file mode 100755
index 0000000..338bb4e
--- /dev/null
+++ b/vendor/symfony/process/Tests/PhpExecutableFinderTest.php
@@ -0,0 +1,49 @@
+<?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\Process\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Process\PhpExecutableFinder;
+
+/**
+ * @author Robert Schönthal <seroscho@googlemail.com>
+ */
+class PhpExecutableFinderTest extends TestCase
+{
+    /**
+     * tests find() with the constant PHP_BINARY.
+     */
+    public function testFind()
+    {
+        $f = new PhpExecutableFinder();
+
+        $current = PHP_BINARY;
+        $args = 'phpdbg' === \PHP_SAPI ? ' -qrr' : '';
+
+        $this->assertEquals($current.$args, $f->find(), '::find() returns the executable PHP');
+        $this->assertEquals($current, $f->find(false), '::find() returns the executable PHP');
+    }
+
+    /**
+     * tests find() with the env var PHP_PATH.
+     */
+    public function testFindArguments()
+    {
+        $f = new PhpExecutableFinder();
+
+        if ('phpdbg' === \PHP_SAPI) {
+            $this->assertEquals($f->findArguments(), ['-qrr'], '::findArguments() returns phpdbg arguments');
+        } else {
+            $this->assertEquals($f->findArguments(), [], '::findArguments() returns no arguments');
+        }
+    }
+}
diff --git a/vendor/symfony/process/Tests/PhpProcessTest.php b/vendor/symfony/process/Tests/PhpProcessTest.php
new file mode 100755
index 0000000..0355c85
--- /dev/null
+++ b/vendor/symfony/process/Tests/PhpProcessTest.php
@@ -0,0 +1,63 @@
+<?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\Process\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Process\PhpExecutableFinder;
+use Symfony\Component\Process\PhpProcess;
+
+class PhpProcessTest extends TestCase
+{
+    public function testNonBlockingWorks()
+    {
+        $expected = 'hello world!';
+        $process = new PhpProcess(<<<PHP
+<?php echo '$expected';
+PHP
+        );
+        $process->start();
+        $process->wait();
+        $this->assertEquals($expected, $process->getOutput());
+    }
+
+    public function testCommandLine()
+    {
+        $process = new PhpProcess(<<<'PHP'
+<?php echo phpversion().PHP_SAPI;
+PHP
+        );
+
+        $commandLine = $process->getCommandLine();
+
+        $process->start();
+        $this->assertContains($commandLine, $process->getCommandLine(), '::getCommandLine() returns the command line of PHP after start');
+
+        $process->wait();
+        $this->assertContains($commandLine, $process->getCommandLine(), '::getCommandLine() returns the command line of PHP after wait');
+
+        $this->assertSame(PHP_VERSION.\PHP_SAPI, $process->getOutput());
+    }
+
+    public function testPassingPhpExplicitly()
+    {
+        $finder = new PhpExecutableFinder();
+        $php = array_merge([$finder->find(false)], $finder->findArguments());
+
+        $expected = 'hello world!';
+        $script = <<<PHP
+<?php echo '$expected';
+PHP;
+        $process = new PhpProcess($script, null, null, 60, $php);
+        $process->run();
+        $this->assertEquals($expected, $process->getOutput());
+    }
+}
diff --git a/vendor/symfony/process/Tests/PipeStdinInStdoutStdErrStreamSelect.php b/vendor/symfony/process/Tests/PipeStdinInStdoutStdErrStreamSelect.php
new file mode 100755
index 0000000..9ea8981
--- /dev/null
+++ b/vendor/symfony/process/Tests/PipeStdinInStdoutStdErrStreamSelect.php
@@ -0,0 +1,72 @@
+<?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.
+ */
+
+define('ERR_SELECT_FAILED', 1);
+define('ERR_TIMEOUT', 2);
+define('ERR_READ_FAILED', 3);
+define('ERR_WRITE_FAILED', 4);
+
+$read = [STDIN];
+$write = [STDOUT, STDERR];
+
+stream_set_blocking(STDIN, 0);
+stream_set_blocking(STDOUT, 0);
+stream_set_blocking(STDERR, 0);
+
+$out = $err = '';
+while ($read || $write) {
+    $r = $read;
+    $w = $write;
+    $e = null;
+    $n = stream_select($r, $w, $e, 5);
+
+    if (false === $n) {
+        die(ERR_SELECT_FAILED);
+    } elseif ($n < 1) {
+        die(ERR_TIMEOUT);
+    }
+
+    if (in_array(STDOUT, $w) && strlen($out) > 0) {
+        $written = fwrite(STDOUT, (string) $out, 32768);
+        if (false === $written) {
+            die(ERR_WRITE_FAILED);
+        }
+        $out = (string) substr($out, $written);
+    }
+    if (null === $read && '' === $out) {
+        $write = array_diff($write, [STDOUT]);
+    }
+
+    if (in_array(STDERR, $w) && strlen($err) > 0) {
+        $written = fwrite(STDERR, (string) $err, 32768);
+        if (false === $written) {
+            die(ERR_WRITE_FAILED);
+        }
+        $err = (string) substr($err, $written);
+    }
+    if (null === $read && '' === $err) {
+        $write = array_diff($write, [STDERR]);
+    }
+
+    if ($r) {
+        $str = fread(STDIN, 32768);
+        if (false !== $str) {
+            $out .= $str;
+            $err .= $str;
+        }
+        if (false === $str || feof(STDIN)) {
+            $read = null;
+            if (!feof(STDIN)) {
+                die(ERR_READ_FAILED);
+            }
+        }
+    }
+}
diff --git a/vendor/symfony/process/Tests/ProcessFailedExceptionTest.php b/vendor/symfony/process/Tests/ProcessFailedExceptionTest.php
new file mode 100755
index 0000000..ba085bd
--- /dev/null
+++ b/vendor/symfony/process/Tests/ProcessFailedExceptionTest.php
@@ -0,0 +1,137 @@
+<?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\Process\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Process\Exception\ProcessFailedException;
+
+/**
+ * @author Sebastian Marek <proofek@gmail.com>
+ */
+class ProcessFailedExceptionTest extends TestCase
+{
+    /**
+     * tests ProcessFailedException throws exception if the process was successful.
+     */
+    public function testProcessFailedExceptionThrowsException()
+    {
+        $process = $this->getMockBuilder('Symfony\Component\Process\Process')->setMethods(['isSuccessful'])->setConstructorArgs([['php']])->getMock();
+        $process->expects($this->once())
+            ->method('isSuccessful')
+            ->willReturn(true);
+
+        if (method_exists($this, 'expectException')) {
+            $this->expectException(\InvalidArgumentException::class);
+            $this->expectExceptionMessage('Expected a failed process, but the given process was successful.');
+        } else {
+            $this->setExpectedException(\InvalidArgumentException::class, 'Expected a failed process, but the given process was successful.');
+        }
+
+        new ProcessFailedException($process);
+    }
+
+    /**
+     * tests ProcessFailedException uses information from process output
+     * to generate exception message.
+     */
+    public function testProcessFailedExceptionPopulatesInformationFromProcessOutput()
+    {
+        $cmd = 'php';
+        $exitCode = 1;
+        $exitText = 'General error';
+        $output = 'Command output';
+        $errorOutput = 'FATAL: Unexpected error';
+        $workingDirectory = getcwd();
+
+        $process = $this->getMockBuilder('Symfony\Component\Process\Process')->setMethods(['isSuccessful', 'getOutput', 'getErrorOutput', 'getExitCode', 'getExitCodeText', 'isOutputDisabled', 'getWorkingDirectory'])->setConstructorArgs([[$cmd]])->getMock();
+        $process->expects($this->once())
+            ->method('isSuccessful')
+            ->willReturn(false);
+
+        $process->expects($this->once())
+            ->method('getOutput')
+            ->willReturn($output);
+
+        $process->expects($this->once())
+            ->method('getErrorOutput')
+            ->willReturn($errorOutput);
+
+        $process->expects($this->once())
+            ->method('getExitCode')
+            ->willReturn($exitCode);
+
+        $process->expects($this->once())
+            ->method('getExitCodeText')
+            ->willReturn($exitText);
+
+        $process->expects($this->once())
+            ->method('isOutputDisabled')
+            ->willReturn(false);
+
+        $process->expects($this->once())
+            ->method('getWorkingDirectory')
+            ->willReturn($workingDirectory);
+
+        $exception = new ProcessFailedException($process);
+
+        $this->assertEquals(
+            "The command \"$cmd\" failed.\n\nExit Code: $exitCode($exitText)\n\nWorking directory: {$workingDirectory}\n\nOutput:\n================\n{$output}\n\nError Output:\n================\n{$errorOutput}",
+            str_replace("'php'", 'php', $exception->getMessage())
+        );
+    }
+
+    /**
+     * Tests that ProcessFailedException does not extract information from
+     * process output if it was previously disabled.
+     */
+    public function testDisabledOutputInFailedExceptionDoesNotPopulateOutput()
+    {
+        $cmd = 'php';
+        $exitCode = 1;
+        $exitText = 'General error';
+        $workingDirectory = getcwd();
+
+        $process = $this->getMockBuilder('Symfony\Component\Process\Process')->setMethods(['isSuccessful', 'isOutputDisabled', 'getExitCode', 'getExitCodeText', 'getOutput', 'getErrorOutput', 'getWorkingDirectory'])->setConstructorArgs([[$cmd]])->getMock();
+        $process->expects($this->once())
+            ->method('isSuccessful')
+            ->willReturn(false);
+
+        $process->expects($this->never())
+            ->method('getOutput');
+
+        $process->expects($this->never())
+            ->method('getErrorOutput');
+
+        $process->expects($this->once())
+            ->method('getExitCode')
+            ->willReturn($exitCode);
+
+        $process->expects($this->once())
+            ->method('getExitCodeText')
+            ->willReturn($exitText);
+
+        $process->expects($this->once())
+            ->method('isOutputDisabled')
+            ->willReturn(true);
+
+        $process->expects($this->once())
+            ->method('getWorkingDirectory')
+            ->willReturn($workingDirectory);
+
+        $exception = new ProcessFailedException($process);
+
+        $this->assertEquals(
+            "The command \"$cmd\" failed.\n\nExit Code: $exitCode($exitText)\n\nWorking directory: {$workingDirectory}",
+            str_replace("'php'", 'php', $exception->getMessage())
+        );
+    }
+}
diff --git a/vendor/symfony/process/Tests/ProcessTest.php b/vendor/symfony/process/Tests/ProcessTest.php
new file mode 100755
index 0000000..8ae8d4c
--- /dev/null
+++ b/vendor/symfony/process/Tests/ProcessTest.php
@@ -0,0 +1,1557 @@
+<?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\Process\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Process\Exception\LogicException;
+use Symfony\Component\Process\Exception\ProcessTimedOutException;
+use Symfony\Component\Process\Exception\RuntimeException;
+use Symfony\Component\Process\InputStream;
+use Symfony\Component\Process\PhpExecutableFinder;
+use Symfony\Component\Process\Pipes\PipesInterface;
+use Symfony\Component\Process\Process;
+
+/**
+ * @author Robert Schönthal <seroscho@googlemail.com>
+ */
+class ProcessTest extends TestCase
+{
+    private static $phpBin;
+    private static $process;
+    private static $sigchild;
+
+    public static function setUpBeforeClass()
+    {
+        $phpBin = new PhpExecutableFinder();
+        self::$phpBin = getenv('SYMFONY_PROCESS_PHP_TEST_BINARY') ?: ('phpdbg' === \PHP_SAPI ? 'php' : $phpBin->find());
+
+        ob_start();
+        phpinfo(INFO_GENERAL);
+        self::$sigchild = false !== strpos(ob_get_clean(), '--enable-sigchild');
+    }
+
+    protected function tearDown()
+    {
+        if (self::$process) {
+            self::$process->stop(0);
+            self::$process = null;
+        }
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Process\Exception\RuntimeException
+     * @expectedExceptionMessage The provided cwd "
+     * @expectedExceptionMessage "does not exist.
+     */
+    public function testInvalidCwd()
+    {
+        try {
+            // Check that it works fine if the CWD exists
+            $cmd = new Process(['echo', 'test'], __DIR__);
+            $cmd->run();
+        } catch (\Exception $e) {
+            $this->fail($e);
+        }
+
+        $cmd = new Process(['echo', 'test'], __DIR__.'/notfound/');
+        $cmd->run();
+    }
+
+    public function testThatProcessDoesNotThrowWarningDuringRun()
+    {
+        if ('\\' === \DIRECTORY_SEPARATOR) {
+            $this->markTestSkipped('This test is transient on Windows');
+        }
+        @trigger_error('Test Error', E_USER_NOTICE);
+        $process = $this->getProcessForCode('sleep(3)');
+        $process->run();
+        $actualError = error_get_last();
+        $this->assertEquals('Test Error', $actualError['message']);
+        $this->assertEquals(E_USER_NOTICE, $actualError['type']);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException
+     */
+    public function testNegativeTimeoutFromConstructor()
+    {
+        $this->getProcess('', null, null, null, -1);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException
+     */
+    public function testNegativeTimeoutFromSetter()
+    {
+        $p = $this->getProcess('');
+        $p->setTimeout(-1);
+    }
+
+    public function testFloatAndNullTimeout()
+    {
+        $p = $this->getProcess('');
+
+        $p->setTimeout(10);
+        $this->assertSame(10.0, $p->getTimeout());
+
+        $p->setTimeout(null);
+        $this->assertNull($p->getTimeout());
+
+        $p->setTimeout(0.0);
+        $this->assertNull($p->getTimeout());
+    }
+
+    /**
+     * @requires extension pcntl
+     */
+    public function testStopWithTimeoutIsActuallyWorking()
+    {
+        $p = $this->getProcess([self::$phpBin, __DIR__.'/NonStopableProcess.php', 30]);
+        $p->start();
+
+        while ($p->isRunning() && false === strpos($p->getOutput(), 'received')) {
+            usleep(1000);
+        }
+
+        if (!$p->isRunning()) {
+            throw new \LogicException('Process is not running: '.$p->getErrorOutput());
+        }
+
+        $start = microtime(true);
+        $p->stop(0.1);
+
+        $p->wait();
+
+        $this->assertLessThan(15, microtime(true) - $start);
+    }
+
+    public function testWaitUntilSpecificOutput()
+    {
+        if ('\\' === \DIRECTORY_SEPARATOR) {
+            $this->markTestIncomplete('This test is too transient on Windows, help wanted to improve it');
+        }
+
+        $p = $this->getProcess([self::$phpBin, __DIR__.'/KillableProcessWithOutput.php']);
+        $p->start();
+
+        $start = microtime(true);
+
+        $completeOutput = '';
+        $result = $p->waitUntil(function ($type, $output) use (&$completeOutput) {
+            return false !== strpos($completeOutput .= $output, 'One more');
+        });
+        $this->assertTrue($result);
+        $this->assertLessThan(20, microtime(true) - $start);
+        $this->assertStringStartsWith("First iteration output\nSecond iteration output\nOne more", $completeOutput);
+        $p->stop();
+    }
+
+    public function testWaitUntilCanReturnFalse()
+    {
+        $p = $this->getProcess('echo foo');
+        $p->start();
+        $this->assertFalse($p->waitUntil(function () { return false; }));
+    }
+
+    public function testAllOutputIsActuallyReadOnTermination()
+    {
+        // this code will result in a maximum of 2 reads of 8192 bytes by calling
+        // start() and isRunning().  by the time getOutput() is called the process
+        // has terminated so the internal pipes array is already empty. normally
+        // the call to start() will not read any data as the process will not have
+        // generated output, but this is non-deterministic so we must count it as
+        // a possibility.  therefore we need 2 * PipesInterface::CHUNK_SIZE plus
+        // another byte which will never be read.
+        $expectedOutputSize = PipesInterface::CHUNK_SIZE * 2 + 2;
+
+        $code = sprintf('echo str_repeat(\'*\', %d);', $expectedOutputSize);
+        $p = $this->getProcessForCode($code);
+
+        $p->start();
+
+        // Don't call Process::run nor Process::wait to avoid any read of pipes
+        $h = new \ReflectionProperty($p, 'process');
+        $h->setAccessible(true);
+        $h = $h->getValue($p);
+        $s = @proc_get_status($h);
+
+        while (!empty($s['running'])) {
+            usleep(1000);
+            $s = proc_get_status($h);
+        }
+
+        $o = $p->getOutput();
+
+        $this->assertEquals($expectedOutputSize, \strlen($o));
+    }
+
+    public function testCallbacksAreExecutedWithStart()
+    {
+        $process = $this->getProcess('echo foo');
+        $process->start(function ($type, $buffer) use (&$data) {
+            $data .= $buffer;
+        });
+
+        $process->wait();
+
+        $this->assertSame('foo'.PHP_EOL, $data);
+    }
+
+    /**
+     * tests results from sub processes.
+     *
+     * @dataProvider responsesCodeProvider
+     */
+    public function testProcessResponses($expected, $getter, $code)
+    {
+        $p = $this->getProcessForCode($code);
+        $p->run();
+
+        $this->assertSame($expected, $p->$getter());
+    }
+
+    /**
+     * tests results from sub processes.
+     *
+     * @dataProvider pipesCodeProvider
+     */
+    public function testProcessPipes($code, $size)
+    {
+        $expected = str_repeat(str_repeat('*', 1024), $size).'!';
+        $expectedLength = (1024 * $size) + 1;
+
+        $p = $this->getProcessForCode($code);
+        $p->setInput($expected);
+        $p->run();
+
+        $this->assertEquals($expectedLength, \strlen($p->getOutput()));
+        $this->assertEquals($expectedLength, \strlen($p->getErrorOutput()));
+    }
+
+    /**
+     * @dataProvider pipesCodeProvider
+     */
+    public function testSetStreamAsInput($code, $size)
+    {
+        $expected = str_repeat(str_repeat('*', 1024), $size).'!';
+        $expectedLength = (1024 * $size) + 1;
+
+        $stream = fopen('php://temporary', 'w+');
+        fwrite($stream, $expected);
+        rewind($stream);
+
+        $p = $this->getProcessForCode($code);
+        $p->setInput($stream);
+        $p->run();
+
+        fclose($stream);
+
+        $this->assertEquals($expectedLength, \strlen($p->getOutput()));
+        $this->assertEquals($expectedLength, \strlen($p->getErrorOutput()));
+    }
+
+    public function testLiveStreamAsInput()
+    {
+        $stream = fopen('php://memory', 'r+');
+        fwrite($stream, 'hello');
+        rewind($stream);
+
+        $p = $this->getProcessForCode('stream_copy_to_stream(STDIN, STDOUT);');
+        $p->setInput($stream);
+        $p->start(function ($type, $data) use ($stream) {
+            if ('hello' === $data) {
+                fclose($stream);
+            }
+        });
+        $p->wait();
+
+        $this->assertSame('hello', $p->getOutput());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Process\Exception\LogicException
+     * @expectedExceptionMessage Input can not be set while the process is running.
+     */
+    public function testSetInputWhileRunningThrowsAnException()
+    {
+        $process = $this->getProcessForCode('sleep(30);');
+        $process->start();
+        try {
+            $process->setInput('foobar');
+            $process->stop();
+            $this->fail('A LogicException should have been raised.');
+        } catch (LogicException $e) {
+        }
+        $process->stop();
+
+        throw $e;
+    }
+
+    /**
+     * @dataProvider provideInvalidInputValues
+     * @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException
+     * @expectedExceptionMessage Symfony\Component\Process\Process::setInput only accepts strings, Traversable objects or stream resources.
+     */
+    public function testInvalidInput($value)
+    {
+        $process = $this->getProcess('foo');
+        $process->setInput($value);
+    }
+
+    public function provideInvalidInputValues()
+    {
+        return [
+            [[]],
+            [new NonStringifiable()],
+        ];
+    }
+
+    /**
+     * @dataProvider provideInputValues
+     */
+    public function testValidInput($expected, $value)
+    {
+        $process = $this->getProcess('foo');
+        $process->setInput($value);
+        $this->assertSame($expected, $process->getInput());
+    }
+
+    public function provideInputValues()
+    {
+        return [
+            [null, null],
+            ['24.5', 24.5],
+            ['input data', 'input data'],
+        ];
+    }
+
+    public function chainedCommandsOutputProvider()
+    {
+        if ('\\' === \DIRECTORY_SEPARATOR) {
+            return [
+                ["2 \r\n2\r\n", '&&', '2'],
+            ];
+        }
+
+        return [
+            ["1\n1\n", ';', '1'],
+            ["2\n2\n", '&&', '2'],
+        ];
+    }
+
+    /**
+     * @dataProvider chainedCommandsOutputProvider
+     */
+    public function testChainedCommandsOutput($expected, $operator, $input)
+    {
+        $process = $this->getProcess(sprintf('echo %s %s echo %s', $input, $operator, $input));
+        $process->run();
+        $this->assertEquals($expected, $process->getOutput());
+    }
+
+    public function testCallbackIsExecutedForOutput()
+    {
+        $p = $this->getProcessForCode('echo \'foo\';');
+
+        $called = false;
+        $p->run(function ($type, $buffer) use (&$called) {
+            $called = 'foo' === $buffer;
+        });
+
+        $this->assertTrue($called, 'The callback should be executed with the output');
+    }
+
+    public function testCallbackIsExecutedForOutputWheneverOutputIsDisabled()
+    {
+        $p = $this->getProcessForCode('echo \'foo\';');
+        $p->disableOutput();
+
+        $called = false;
+        $p->run(function ($type, $buffer) use (&$called) {
+            $called = 'foo' === $buffer;
+        });
+
+        $this->assertTrue($called, 'The callback should be executed with the output');
+    }
+
+    public function testGetErrorOutput()
+    {
+        $p = $this->getProcessForCode('$n = 0; while ($n < 3) { file_put_contents(\'php://stderr\', \'ERROR\'); $n++; }');
+
+        $p->run();
+        $this->assertEquals(3, preg_match_all('/ERROR/', $p->getErrorOutput(), $matches));
+    }
+
+    public function testFlushErrorOutput()
+    {
+        $p = $this->getProcessForCode('$n = 0; while ($n < 3) { file_put_contents(\'php://stderr\', \'ERROR\'); $n++; }');
+
+        $p->run();
+        $p->clearErrorOutput();
+        $this->assertEmpty($p->getErrorOutput());
+    }
+
+    /**
+     * @dataProvider provideIncrementalOutput
+     */
+    public function testIncrementalOutput($getOutput, $getIncrementalOutput, $uri)
+    {
+        $lock = tempnam(sys_get_temp_dir(), __FUNCTION__);
+
+        $p = $this->getProcessForCode('file_put_contents($s = \''.$uri.'\', \'foo\'); flock(fopen('.var_export($lock, true).', \'r\'), LOCK_EX); file_put_contents($s, \'bar\');');
+
+        $h = fopen($lock, 'w');
+        flock($h, LOCK_EX);
+
+        $p->start();
+
+        foreach (['foo', 'bar'] as $s) {
+            while (false === strpos($p->$getOutput(), $s)) {
+                usleep(1000);
+            }
+
+            $this->assertSame($s, $p->$getIncrementalOutput());
+            $this->assertSame('', $p->$getIncrementalOutput());
+
+            flock($h, LOCK_UN);
+        }
+
+        fclose($h);
+    }
+
+    public function provideIncrementalOutput()
+    {
+        return [
+            ['getOutput', 'getIncrementalOutput', 'php://stdout'],
+            ['getErrorOutput', 'getIncrementalErrorOutput', 'php://stderr'],
+        ];
+    }
+
+    public function testGetOutput()
+    {
+        $p = $this->getProcessForCode('$n = 0; while ($n < 3) { echo \' foo \'; $n++; }');
+
+        $p->run();
+        $this->assertEquals(3, preg_match_all('/foo/', $p->getOutput(), $matches));
+    }
+
+    public function testFlushOutput()
+    {
+        $p = $this->getProcessForCode('$n=0;while ($n<3) {echo \' foo \';$n++;}');
+
+        $p->run();
+        $p->clearOutput();
+        $this->assertEmpty($p->getOutput());
+    }
+
+    public function testZeroAsOutput()
+    {
+        if ('\\' === \DIRECTORY_SEPARATOR) {
+            // see http://stackoverflow.com/questions/7105433/windows-batch-echo-without-new-line
+            $p = $this->getProcess('echo | set /p dummyName=0');
+        } else {
+            $p = $this->getProcess('printf 0');
+        }
+
+        $p->run();
+        $this->assertSame('0', $p->getOutput());
+    }
+
+    public function testExitCodeCommandFailed()
+    {
+        if ('\\' === \DIRECTORY_SEPARATOR) {
+            $this->markTestSkipped('Windows does not support POSIX exit code');
+        }
+
+        // such command run in bash return an exitcode 127
+        $process = $this->getProcess('nonexistingcommandIhopeneversomeonewouldnameacommandlikethis');
+        $process->run();
+
+        $this->assertGreaterThan(0, $process->getExitCode());
+    }
+
+    public function testTTYCommand()
+    {
+        if ('\\' === \DIRECTORY_SEPARATOR) {
+            $this->markTestSkipped('Windows does not have /dev/tty support');
+        }
+
+        $process = $this->getProcess('echo "foo" >> /dev/null && '.$this->getProcessForCode('usleep(100000);')->getCommandLine());
+        $process->setTty(true);
+        $process->start();
+        $this->assertTrue($process->isRunning());
+        $process->wait();
+
+        $this->assertSame(Process::STATUS_TERMINATED, $process->getStatus());
+    }
+
+    public function testTTYCommandExitCode()
+    {
+        if ('\\' === \DIRECTORY_SEPARATOR) {
+            $this->markTestSkipped('Windows does have /dev/tty support');
+        }
+
+        $process = $this->getProcess('echo "foo" >> /dev/null');
+        $process->setTty(true);
+        $process->run();
+
+        $this->assertTrue($process->isSuccessful());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Process\Exception\RuntimeException
+     * @expectedExceptionMessage TTY mode is not supported on Windows platform.
+     */
+    public function testTTYInWindowsEnvironment()
+    {
+        if ('\\' !== \DIRECTORY_SEPARATOR) {
+            $this->markTestSkipped('This test is for Windows platform only');
+        }
+
+        $process = $this->getProcess('echo "foo" >> /dev/null');
+        $process->setTty(false);
+        $process->setTty(true);
+    }
+
+    public function testExitCodeTextIsNullWhenExitCodeIsNull()
+    {
+        $process = $this->getProcess('');
+        $this->assertNull($process->getExitCodeText());
+    }
+
+    public function testPTYCommand()
+    {
+        if (!Process::isPtySupported()) {
+            $this->markTestSkipped('PTY is not supported on this operating system.');
+        }
+
+        $process = $this->getProcess('echo "foo"');
+        $process->setPty(true);
+        $process->run();
+
+        $this->assertSame(Process::STATUS_TERMINATED, $process->getStatus());
+        $this->assertEquals("foo\r\n", $process->getOutput());
+    }
+
+    public function testMustRun()
+    {
+        $process = $this->getProcess('echo foo');
+
+        $this->assertSame($process, $process->mustRun());
+        $this->assertEquals('foo'.PHP_EOL, $process->getOutput());
+    }
+
+    public function testSuccessfulMustRunHasCorrectExitCode()
+    {
+        $process = $this->getProcess('echo foo')->mustRun();
+        $this->assertEquals(0, $process->getExitCode());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Process\Exception\ProcessFailedException
+     */
+    public function testMustRunThrowsException()
+    {
+        $process = $this->getProcess('exit 1');
+        $process->mustRun();
+    }
+
+    public function testExitCodeText()
+    {
+        $process = $this->getProcess('');
+        $r = new \ReflectionObject($process);
+        $p = $r->getProperty('exitcode');
+        $p->setAccessible(true);
+
+        $p->setValue($process, 2);
+        $this->assertEquals('Misuse of shell builtins', $process->getExitCodeText());
+    }
+
+    public function testStartIsNonBlocking()
+    {
+        $process = $this->getProcessForCode('usleep(500000);');
+        $start = microtime(true);
+        $process->start();
+        $end = microtime(true);
+        $this->assertLessThan(0.4, $end - $start);
+        $process->stop();
+    }
+
+    public function testUpdateStatus()
+    {
+        $process = $this->getProcess('echo foo');
+        $process->run();
+        $this->assertGreaterThan(0, \strlen($process->getOutput()));
+    }
+
+    public function testGetExitCodeIsNullOnStart()
+    {
+        $process = $this->getProcessForCode('usleep(100000);');
+        $this->assertNull($process->getExitCode());
+        $process->start();
+        $this->assertNull($process->getExitCode());
+        $process->wait();
+        $this->assertEquals(0, $process->getExitCode());
+    }
+
+    public function testGetExitCodeIsNullOnWhenStartingAgain()
+    {
+        $process = $this->getProcessForCode('usleep(100000);');
+        $process->run();
+        $this->assertEquals(0, $process->getExitCode());
+        $process->start();
+        $this->assertNull($process->getExitCode());
+        $process->wait();
+        $this->assertEquals(0, $process->getExitCode());
+    }
+
+    public function testGetExitCode()
+    {
+        $process = $this->getProcess('echo foo');
+        $process->run();
+        $this->assertSame(0, $process->getExitCode());
+    }
+
+    public function testStatus()
+    {
+        $process = $this->getProcessForCode('usleep(100000);');
+        $this->assertFalse($process->isRunning());
+        $this->assertFalse($process->isStarted());
+        $this->assertFalse($process->isTerminated());
+        $this->assertSame(Process::STATUS_READY, $process->getStatus());
+        $process->start();
+        $this->assertTrue($process->isRunning());
+        $this->assertTrue($process->isStarted());
+        $this->assertFalse($process->isTerminated());
+        $this->assertSame(Process::STATUS_STARTED, $process->getStatus());
+        $process->wait();
+        $this->assertFalse($process->isRunning());
+        $this->assertTrue($process->isStarted());
+        $this->assertTrue($process->isTerminated());
+        $this->assertSame(Process::STATUS_TERMINATED, $process->getStatus());
+    }
+
+    public function testStop()
+    {
+        $process = $this->getProcessForCode('sleep(31);');
+        $process->start();
+        $this->assertTrue($process->isRunning());
+        $process->stop();
+        $this->assertFalse($process->isRunning());
+    }
+
+    public function testIsSuccessful()
+    {
+        $process = $this->getProcess('echo foo');
+        $process->run();
+        $this->assertTrue($process->isSuccessful());
+    }
+
+    public function testIsSuccessfulOnlyAfterTerminated()
+    {
+        $process = $this->getProcessForCode('usleep(100000);');
+        $process->start();
+
+        $this->assertFalse($process->isSuccessful());
+
+        $process->wait();
+
+        $this->assertTrue($process->isSuccessful());
+    }
+
+    public function testIsNotSuccessful()
+    {
+        $process = $this->getProcessForCode('throw new \Exception(\'BOUM\');');
+        $process->run();
+        $this->assertFalse($process->isSuccessful());
+    }
+
+    public function testProcessIsNotSignaled()
+    {
+        if ('\\' === \DIRECTORY_SEPARATOR) {
+            $this->markTestSkipped('Windows does not support POSIX signals');
+        }
+
+        $process = $this->getProcess('echo foo');
+        $process->run();
+        $this->assertFalse($process->hasBeenSignaled());
+    }
+
+    public function testProcessWithoutTermSignal()
+    {
+        if ('\\' === \DIRECTORY_SEPARATOR) {
+            $this->markTestSkipped('Windows does not support POSIX signals');
+        }
+
+        $process = $this->getProcess('echo foo');
+        $process->run();
+        $this->assertEquals(0, $process->getTermSignal());
+    }
+
+    public function testProcessIsSignaledIfStopped()
+    {
+        if ('\\' === \DIRECTORY_SEPARATOR) {
+            $this->markTestSkipped('Windows does not support POSIX signals');
+        }
+
+        $process = $this->getProcessForCode('sleep(32);');
+        $process->start();
+        $process->stop();
+        $this->assertTrue($process->hasBeenSignaled());
+        $this->assertEquals(15, $process->getTermSignal()); // SIGTERM
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Process\Exception\ProcessSignaledException
+     * @expectedExceptionMessage The process has been signaled with signal "9".
+     */
+    public function testProcessThrowsExceptionWhenExternallySignaled()
+    {
+        if (!\function_exists('posix_kill')) {
+            $this->markTestSkipped('Function posix_kill is required.');
+        }
+
+        if (self::$sigchild) {
+            $this->markTestSkipped('PHP is compiled with --enable-sigchild.');
+        }
+
+        $process = $this->getProcessForCode('sleep(32.1);');
+        $process->start();
+        posix_kill($process->getPid(), 9); // SIGKILL
+
+        $process->wait();
+    }
+
+    public function testRestart()
+    {
+        $process1 = $this->getProcessForCode('echo getmypid();');
+        $process1->run();
+        $process2 = $process1->restart();
+
+        $process2->wait(); // wait for output
+
+        // Ensure that both processed finished and the output is numeric
+        $this->assertFalse($process1->isRunning());
+        $this->assertFalse($process2->isRunning());
+        $this->assertInternalType('numeric', $process1->getOutput());
+        $this->assertInternalType('numeric', $process2->getOutput());
+
+        // Ensure that restart returned a new process by check that the output is different
+        $this->assertNotEquals($process1->getOutput(), $process2->getOutput());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Process\Exception\ProcessTimedOutException
+     * @expectedExceptionMessage exceeded the timeout of 0.1 seconds.
+     */
+    public function testRunProcessWithTimeout()
+    {
+        $process = $this->getProcessForCode('sleep(30);');
+        $process->setTimeout(0.1);
+        $start = microtime(true);
+        try {
+            $process->run();
+            $this->fail('A RuntimeException should have been raised');
+        } catch (RuntimeException $e) {
+        }
+
+        $this->assertLessThan(15, microtime(true) - $start);
+
+        throw $e;
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Process\Exception\ProcessTimedOutException
+     * @expectedExceptionMessage exceeded the timeout of 0.1 seconds.
+     */
+    public function testIterateOverProcessWithTimeout()
+    {
+        $process = $this->getProcessForCode('sleep(30);');
+        $process->setTimeout(0.1);
+        $start = microtime(true);
+        try {
+            $process->start();
+            foreach ($process as $buffer);
+            $this->fail('A RuntimeException should have been raised');
+        } catch (RuntimeException $e) {
+        }
+
+        $this->assertLessThan(15, microtime(true) - $start);
+
+        throw $e;
+    }
+
+    public function testCheckTimeoutOnNonStartedProcess()
+    {
+        $process = $this->getProcess('echo foo');
+        $this->assertNull($process->checkTimeout());
+    }
+
+    public function testCheckTimeoutOnTerminatedProcess()
+    {
+        $process = $this->getProcess('echo foo');
+        $process->run();
+        $this->assertNull($process->checkTimeout());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Process\Exception\ProcessTimedOutException
+     * @expectedExceptionMessage exceeded the timeout of 0.1 seconds.
+     */
+    public function testCheckTimeoutOnStartedProcess()
+    {
+        $process = $this->getProcessForCode('sleep(33);');
+        $process->setTimeout(0.1);
+
+        $process->start();
+        $start = microtime(true);
+
+        try {
+            while ($process->isRunning()) {
+                $process->checkTimeout();
+                usleep(100000);
+            }
+            $this->fail('A ProcessTimedOutException should have been raised');
+        } catch (ProcessTimedOutException $e) {
+        }
+
+        $this->assertLessThan(15, microtime(true) - $start);
+
+        throw $e;
+    }
+
+    public function testIdleTimeout()
+    {
+        $process = $this->getProcessForCode('sleep(34);');
+        $process->setTimeout(60);
+        $process->setIdleTimeout(0.1);
+
+        try {
+            $process->run();
+
+            $this->fail('A timeout exception was expected.');
+        } catch (ProcessTimedOutException $e) {
+            $this->assertTrue($e->isIdleTimeout());
+            $this->assertFalse($e->isGeneralTimeout());
+            $this->assertEquals(0.1, $e->getExceededTimeout());
+        }
+    }
+
+    public function testIdleTimeoutNotExceededWhenOutputIsSent()
+    {
+        $process = $this->getProcessForCode('while (true) {echo \'foo \'; usleep(1000);}');
+        $process->setTimeout(1);
+        $process->start();
+
+        while (false === strpos($process->getOutput(), 'foo')) {
+            usleep(1000);
+        }
+
+        $process->setIdleTimeout(0.5);
+
+        try {
+            $process->wait();
+            $this->fail('A timeout exception was expected.');
+        } catch (ProcessTimedOutException $e) {
+            $this->assertTrue($e->isGeneralTimeout(), 'A general timeout is expected.');
+            $this->assertFalse($e->isIdleTimeout(), 'No idle timeout is expected.');
+            $this->assertEquals(1, $e->getExceededTimeout());
+        }
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Process\Exception\ProcessTimedOutException
+     * @expectedExceptionMessage exceeded the timeout of 0.1 seconds.
+     */
+    public function testStartAfterATimeout()
+    {
+        $process = $this->getProcessForCode('sleep(35);');
+        $process->setTimeout(0.1);
+
+        try {
+            $process->run();
+            $this->fail('A ProcessTimedOutException should have been raised.');
+        } catch (ProcessTimedOutException $e) {
+        }
+        $this->assertFalse($process->isRunning());
+        $process->start();
+        $this->assertTrue($process->isRunning());
+        $process->stop(0);
+
+        throw $e;
+    }
+
+    public function testGetPid()
+    {
+        $process = $this->getProcessForCode('sleep(36);');
+        $process->start();
+        $this->assertGreaterThan(0, $process->getPid());
+        $process->stop(0);
+    }
+
+    public function testGetPidIsNullBeforeStart()
+    {
+        $process = $this->getProcess('foo');
+        $this->assertNull($process->getPid());
+    }
+
+    public function testGetPidIsNullAfterRun()
+    {
+        $process = $this->getProcess('echo foo');
+        $process->run();
+        $this->assertNull($process->getPid());
+    }
+
+    /**
+     * @requires extension pcntl
+     */
+    public function testSignal()
+    {
+        $process = $this->getProcess([self::$phpBin, __DIR__.'/SignalListener.php']);
+        $process->start();
+
+        while (false === strpos($process->getOutput(), 'Caught')) {
+            usleep(1000);
+        }
+        $process->signal(SIGUSR1);
+        $process->wait();
+
+        $this->assertEquals('Caught SIGUSR1', $process->getOutput());
+    }
+
+    /**
+     * @requires extension pcntl
+     */
+    public function testExitCodeIsAvailableAfterSignal()
+    {
+        $process = $this->getProcess('sleep 4');
+        $process->start();
+        $process->signal(SIGKILL);
+
+        while ($process->isRunning()) {
+            usleep(10000);
+        }
+
+        $this->assertFalse($process->isRunning());
+        $this->assertTrue($process->hasBeenSignaled());
+        $this->assertFalse($process->isSuccessful());
+        $this->assertEquals(137, $process->getExitCode());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Process\Exception\LogicException
+     * @expectedExceptionMessage Can not send signal on a non running process.
+     */
+    public function testSignalProcessNotRunning()
+    {
+        $process = $this->getProcess('foo');
+        $process->signal(1); // SIGHUP
+    }
+
+    /**
+     * @dataProvider provideMethodsThatNeedARunningProcess
+     */
+    public function testMethodsThatNeedARunningProcess($method)
+    {
+        $process = $this->getProcess('foo');
+
+        if (method_exists($this, 'expectException')) {
+            $this->expectException('Symfony\Component\Process\Exception\LogicException');
+            $this->expectExceptionMessage(sprintf('Process must be started before calling %s.', $method));
+        } else {
+            $this->setExpectedException('Symfony\Component\Process\Exception\LogicException', sprintf('Process must be started before calling %s.', $method));
+        }
+
+        $process->{$method}();
+    }
+
+    public function provideMethodsThatNeedARunningProcess()
+    {
+        return [
+            ['getOutput'],
+            ['getIncrementalOutput'],
+            ['getErrorOutput'],
+            ['getIncrementalErrorOutput'],
+            ['wait'],
+        ];
+    }
+
+    /**
+     * @dataProvider provideMethodsThatNeedATerminatedProcess
+     * @expectedException \Symfony\Component\Process\Exception\LogicException
+     * @expectedExceptionMessage Process must be terminated before calling
+     */
+    public function testMethodsThatNeedATerminatedProcess($method)
+    {
+        $process = $this->getProcessForCode('sleep(37);');
+        $process->start();
+        try {
+            $process->{$method}();
+            $process->stop(0);
+            $this->fail('A LogicException must have been thrown');
+        } catch (\Exception $e) {
+        }
+        $process->stop(0);
+
+        throw $e;
+    }
+
+    public function provideMethodsThatNeedATerminatedProcess()
+    {
+        return [
+            ['hasBeenSignaled'],
+            ['getTermSignal'],
+            ['hasBeenStopped'],
+            ['getStopSignal'],
+        ];
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Process\Exception\RuntimeException
+     */
+    public function testWrongSignal()
+    {
+        if ('\\' === \DIRECTORY_SEPARATOR) {
+            $this->markTestSkipped('POSIX signals do not work on Windows');
+        }
+
+        $process = $this->getProcessForCode('sleep(38);');
+        $process->start();
+        try {
+            $process->signal(-4);
+            $this->fail('A RuntimeException must have been thrown');
+        } catch (RuntimeException $e) {
+            $process->stop(0);
+        }
+
+        throw $e;
+    }
+
+    public function testDisableOutputDisablesTheOutput()
+    {
+        $p = $this->getProcess('foo');
+        $this->assertFalse($p->isOutputDisabled());
+        $p->disableOutput();
+        $this->assertTrue($p->isOutputDisabled());
+        $p->enableOutput();
+        $this->assertFalse($p->isOutputDisabled());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Process\Exception\RuntimeException
+     * @expectedExceptionMessage Disabling output while the process is running is not possible.
+     */
+    public function testDisableOutputWhileRunningThrowsException()
+    {
+        $p = $this->getProcessForCode('sleep(39);');
+        $p->start();
+        $p->disableOutput();
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Process\Exception\RuntimeException
+     * @expectedExceptionMessage Enabling output while the process is running is not possible.
+     */
+    public function testEnableOutputWhileRunningThrowsException()
+    {
+        $p = $this->getProcessForCode('sleep(40);');
+        $p->disableOutput();
+        $p->start();
+        $p->enableOutput();
+    }
+
+    public function testEnableOrDisableOutputAfterRunDoesNotThrowException()
+    {
+        $p = $this->getProcess('echo foo');
+        $p->disableOutput();
+        $p->run();
+        $p->enableOutput();
+        $p->disableOutput();
+        $this->assertTrue($p->isOutputDisabled());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Process\Exception\LogicException
+     * @expectedExceptionMessage Output can not be disabled while an idle timeout is set.
+     */
+    public function testDisableOutputWhileIdleTimeoutIsSet()
+    {
+        $process = $this->getProcess('foo');
+        $process->setIdleTimeout(1);
+        $process->disableOutput();
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Process\Exception\LogicException
+     * @expectedExceptionMessage timeout can not be set while the output is disabled.
+     */
+    public function testSetIdleTimeoutWhileOutputIsDisabled()
+    {
+        $process = $this->getProcess('foo');
+        $process->disableOutput();
+        $process->setIdleTimeout(1);
+    }
+
+    public function testSetNullIdleTimeoutWhileOutputIsDisabled()
+    {
+        $process = $this->getProcess('foo');
+        $process->disableOutput();
+        $this->assertSame($process, $process->setIdleTimeout(null));
+    }
+
+    /**
+     * @dataProvider provideOutputFetchingMethods
+     * @expectedException \Symfony\Component\Process\Exception\LogicException
+     * @expectedExceptionMessage Output has been disabled.
+     */
+    public function testGetOutputWhileDisabled($fetchMethod)
+    {
+        $p = $this->getProcessForCode('sleep(41);');
+        $p->disableOutput();
+        $p->start();
+        $p->{$fetchMethod}();
+    }
+
+    public function provideOutputFetchingMethods()
+    {
+        return [
+            ['getOutput'],
+            ['getIncrementalOutput'],
+            ['getErrorOutput'],
+            ['getIncrementalErrorOutput'],
+        ];
+    }
+
+    public function testStopTerminatesProcessCleanly()
+    {
+        $process = $this->getProcessForCode('echo 123; sleep(42);');
+        $process->run(function () use ($process) {
+            $process->stop();
+        });
+        $this->assertTrue(true, 'A call to stop() is not expected to cause wait() to throw a RuntimeException');
+    }
+
+    public function testKillSignalTerminatesProcessCleanly()
+    {
+        $process = $this->getProcessForCode('echo 123; sleep(43);');
+        $process->run(function () use ($process) {
+            $process->signal(9); // SIGKILL
+        });
+        $this->assertTrue(true, 'A call to signal() is not expected to cause wait() to throw a RuntimeException');
+    }
+
+    public function testTermSignalTerminatesProcessCleanly()
+    {
+        $process = $this->getProcessForCode('echo 123; sleep(44);');
+        $process->run(function () use ($process) {
+            $process->signal(15); // SIGTERM
+        });
+        $this->assertTrue(true, 'A call to signal() is not expected to cause wait() to throw a RuntimeException');
+    }
+
+    public function responsesCodeProvider()
+    {
+        return [
+            //expected output / getter / code to execute
+            // [1,'getExitCode','exit(1);'],
+            // [true,'isSuccessful','exit();'],
+            ['output', 'getOutput', 'echo \'output\';'],
+        ];
+    }
+
+    public function pipesCodeProvider()
+    {
+        $variations = [
+            'fwrite(STDOUT, $in = file_get_contents(\'php://stdin\')); fwrite(STDERR, $in);',
+            'include \''.__DIR__.'/PipeStdinInStdoutStdErrStreamSelect.php\';',
+        ];
+
+        if ('\\' === \DIRECTORY_SEPARATOR) {
+            // Avoid XL buffers on Windows because of https://bugs.php.net/bug.php?id=65650
+            $sizes = [1, 2, 4, 8];
+        } else {
+            $sizes = [1, 16, 64, 1024, 4096];
+        }
+
+        $codes = [];
+        foreach ($sizes as $size) {
+            foreach ($variations as $code) {
+                $codes[] = [$code, $size];
+            }
+        }
+
+        return $codes;
+    }
+
+    /**
+     * @dataProvider provideVariousIncrementals
+     */
+    public function testIncrementalOutputDoesNotRequireAnotherCall($stream, $method)
+    {
+        $process = $this->getProcessForCode('$n = 0; while ($n < 3) { file_put_contents(\''.$stream.'\', $n, 1); $n++; usleep(1000); }', null, null, null, null);
+        $process->start();
+        $result = '';
+        $limit = microtime(true) + 3;
+        $expected = '012';
+
+        while ($result !== $expected && microtime(true) < $limit) {
+            $result .= $process->$method();
+        }
+
+        $this->assertSame($expected, $result);
+        $process->stop();
+    }
+
+    public function provideVariousIncrementals()
+    {
+        return [
+            ['php://stdout', 'getIncrementalOutput'],
+            ['php://stderr', 'getIncrementalErrorOutput'],
+        ];
+    }
+
+    public function testIteratorInput()
+    {
+        $input = function () {
+            yield 'ping';
+            yield 'pong';
+        };
+
+        $process = $this->getProcessForCode('stream_copy_to_stream(STDIN, STDOUT);', null, null, $input());
+        $process->run();
+        $this->assertSame('pingpong', $process->getOutput());
+    }
+
+    public function testSimpleInputStream()
+    {
+        $input = new InputStream();
+
+        $process = $this->getProcessForCode('echo \'ping\'; echo fread(STDIN, 4); echo fread(STDIN, 4);');
+        $process->setInput($input);
+
+        $process->start(function ($type, $data) use ($input) {
+            if ('ping' === $data) {
+                $input->write('pang');
+            } elseif (!$input->isClosed()) {
+                $input->write('pong');
+                $input->close();
+            }
+        });
+
+        $process->wait();
+        $this->assertSame('pingpangpong', $process->getOutput());
+    }
+
+    public function testInputStreamWithCallable()
+    {
+        $i = 0;
+        $stream = fopen('php://memory', 'w+');
+        $stream = function () use ($stream, &$i) {
+            if ($i < 3) {
+                rewind($stream);
+                fwrite($stream, ++$i);
+                rewind($stream);
+
+                return $stream;
+            }
+        };
+
+        $input = new InputStream();
+        $input->onEmpty($stream);
+        $input->write($stream());
+
+        $process = $this->getProcessForCode('echo fread(STDIN, 3);');
+        $process->setInput($input);
+        $process->start(function ($type, $data) use ($input) {
+            $input->close();
+        });
+
+        $process->wait();
+        $this->assertSame('123', $process->getOutput());
+    }
+
+    public function testInputStreamWithGenerator()
+    {
+        $input = new InputStream();
+        $input->onEmpty(function ($input) {
+            yield 'pong';
+            $input->close();
+        });
+
+        $process = $this->getProcessForCode('stream_copy_to_stream(STDIN, STDOUT);');
+        $process->setInput($input);
+        $process->start();
+        $input->write('ping');
+        $process->wait();
+        $this->assertSame('pingpong', $process->getOutput());
+    }
+
+    public function testInputStreamOnEmpty()
+    {
+        $i = 0;
+        $input = new InputStream();
+        $input->onEmpty(function () use (&$i) { ++$i; });
+
+        $process = $this->getProcessForCode('echo 123; echo fread(STDIN, 1); echo 456;');
+        $process->setInput($input);
+        $process->start(function ($type, $data) use ($input) {
+            if ('123' === $data) {
+                $input->close();
+            }
+        });
+        $process->wait();
+
+        $this->assertSame(0, $i, 'InputStream->onEmpty callback should be called only when the input *becomes* empty');
+        $this->assertSame('123456', $process->getOutput());
+    }
+
+    public function testIteratorOutput()
+    {
+        $input = new InputStream();
+
+        $process = $this->getProcessForCode('fwrite(STDOUT, 123); fwrite(STDERR, 234); flush(); usleep(10000); fwrite(STDOUT, fread(STDIN, 3)); fwrite(STDERR, 456);');
+        $process->setInput($input);
+        $process->start();
+        $output = [];
+
+        foreach ($process as $type => $data) {
+            $output[] = [$type, $data];
+            break;
+        }
+        $expectedOutput = [
+            [$process::OUT, '123'],
+        ];
+        $this->assertSame($expectedOutput, $output);
+
+        $input->write(345);
+
+        foreach ($process as $type => $data) {
+            $output[] = [$type, $data];
+        }
+
+        $this->assertSame('', $process->getOutput());
+        $this->assertFalse($process->isRunning());
+
+        $expectedOutput = [
+            [$process::OUT, '123'],
+            [$process::ERR, '234'],
+            [$process::OUT, '345'],
+            [$process::ERR, '456'],
+        ];
+        $this->assertSame($expectedOutput, $output);
+    }
+
+    public function testNonBlockingNorClearingIteratorOutput()
+    {
+        $input = new InputStream();
+
+        $process = $this->getProcessForCode('fwrite(STDOUT, fread(STDIN, 3));');
+        $process->setInput($input);
+        $process->start();
+        $output = [];
+
+        foreach ($process->getIterator($process::ITER_NON_BLOCKING | $process::ITER_KEEP_OUTPUT) as $type => $data) {
+            $output[] = [$type, $data];
+            break;
+        }
+        $expectedOutput = [
+            [$process::OUT, ''],
+        ];
+        $this->assertSame($expectedOutput, $output);
+
+        $input->write(123);
+
+        foreach ($process->getIterator($process::ITER_NON_BLOCKING | $process::ITER_KEEP_OUTPUT) as $type => $data) {
+            if ('' !== $data) {
+                $output[] = [$type, $data];
+            }
+        }
+
+        $this->assertSame('123', $process->getOutput());
+        $this->assertFalse($process->isRunning());
+
+        $expectedOutput = [
+            [$process::OUT, ''],
+            [$process::OUT, '123'],
+        ];
+        $this->assertSame($expectedOutput, $output);
+    }
+
+    public function testChainedProcesses()
+    {
+        $p1 = $this->getProcessForCode('fwrite(STDERR, 123); fwrite(STDOUT, 456);');
+        $p2 = $this->getProcessForCode('stream_copy_to_stream(STDIN, STDOUT);');
+        $p2->setInput($p1);
+
+        $p1->start();
+        $p2->run();
+
+        $this->assertSame('123', $p1->getErrorOutput());
+        $this->assertSame('', $p1->getOutput());
+        $this->assertSame('', $p2->getErrorOutput());
+        $this->assertSame('456', $p2->getOutput());
+    }
+
+    public function testSetBadEnv()
+    {
+        $process = $this->getProcess('echo hello');
+        $process->setEnv(['bad%%' => '123']);
+        $process->inheritEnvironmentVariables(true);
+
+        $process->run();
+
+        $this->assertSame('hello'.PHP_EOL, $process->getOutput());
+        $this->assertSame('', $process->getErrorOutput());
+    }
+
+    public function testEnvBackupDoesNotDeleteExistingVars()
+    {
+        putenv('existing_var=foo');
+        $_ENV['existing_var'] = 'foo';
+        $process = $this->getProcess('php -r "echo getenv(\'new_test_var\');"');
+        $process->setEnv(['existing_var' => 'bar', 'new_test_var' => 'foo']);
+        $process->inheritEnvironmentVariables();
+
+        $process->run();
+
+        $this->assertSame('foo', $process->getOutput());
+        $this->assertSame('foo', getenv('existing_var'));
+        $this->assertFalse(getenv('new_test_var'));
+
+        putenv('existing_var');
+        unset($_ENV['existing_var']);
+    }
+
+    public function testEnvIsInherited()
+    {
+        $process = $this->getProcessForCode('echo serialize($_SERVER);', null, ['BAR' => 'BAZ', 'EMPTY' => '']);
+
+        putenv('FOO=BAR');
+        $_ENV['FOO'] = 'BAR';
+
+        $process->run();
+
+        $expected = ['BAR' => 'BAZ', 'EMPTY' => '', 'FOO' => 'BAR'];
+        $env = array_intersect_key(unserialize($process->getOutput()), $expected);
+
+        $this->assertEquals($expected, $env);
+
+        putenv('FOO');
+        unset($_ENV['FOO']);
+    }
+
+    public function testGetCommandLine()
+    {
+        $p = new Process(['/usr/bin/php']);
+
+        $expected = '\\' === \DIRECTORY_SEPARATOR ? '"/usr/bin/php"' : "'/usr/bin/php'";
+        $this->assertSame($expected, $p->getCommandLine());
+    }
+
+    /**
+     * @dataProvider provideEscapeArgument
+     */
+    public function testEscapeArgument($arg)
+    {
+        $p = new Process([self::$phpBin, '-r', 'echo $argv[1];', $arg]);
+        $p->run();
+
+        $this->assertSame((string) $arg, $p->getOutput());
+    }
+
+    public function testRawCommandLine()
+    {
+        $p = Process::fromShellCommandline(sprintf('"%s" -r %s "a" "" "b"', self::$phpBin, escapeshellarg('print_r($argv);')));
+        $p->run();
+
+        $expected = <<<EOTXT
+Array
+(
+    [0] => -
+    [1] => a
+    [2] => 
+    [3] => b
+)
+
+EOTXT;
+        $this->assertSame($expected, str_replace('Standard input code', '-', $p->getOutput()));
+    }
+
+    public function provideEscapeArgument()
+    {
+        yield ['a"b%c%'];
+        yield ['a"b^c^'];
+        yield ["a\nb'c"];
+        yield ['a^b c!'];
+        yield ["a!b\tc"];
+        yield ['a\\\\"\\"'];
+        yield ['éÉèÈàÀöä'];
+        yield [null];
+        yield [1];
+        yield [1.1];
+    }
+
+    public function testEnvArgument()
+    {
+        $env = ['FOO' => 'Foo', 'BAR' => 'Bar'];
+        $cmd = '\\' === \DIRECTORY_SEPARATOR ? 'echo !FOO! !BAR! !BAZ!' : 'echo $FOO $BAR $BAZ';
+        $p = Process::fromShellCommandline($cmd, null, $env);
+        $p->run(null, ['BAR' => 'baR', 'BAZ' => 'baZ']);
+
+        $this->assertSame('Foo baR baZ', rtrim($p->getOutput()));
+        $this->assertSame($env, $p->getEnv());
+    }
+
+    public function testWaitStoppedDeadProcess()
+    {
+        $process = $this->getProcess(self::$phpBin.' '.__DIR__.'/ErrorProcessInitiator.php -e '.self::$phpBin);
+        $process->start();
+        $process->setTimeout(2);
+        $process->wait();
+        $this->assertFalse($process->isRunning());
+    }
+
+    /**
+     * @param string      $commandline
+     * @param string|null $cwd
+     * @param array|null  $env
+     * @param string|null $input
+     * @param int         $timeout
+     * @param array       $options
+     *
+     * @return Process
+     */
+    private function getProcess($commandline, string $cwd = null, array $env = null, $input = null, ?int $timeout = 60): Process
+    {
+        if (\is_string($commandline)) {
+            $process = Process::fromShellCommandline($commandline, $cwd, $env, $input, $timeout);
+        } else {
+            $process = new Process($commandline, $cwd, $env, $input, $timeout);
+        }
+        $process->inheritEnvironmentVariables();
+
+        if (self::$process) {
+            self::$process->stop(0);
+        }
+
+        return self::$process = $process;
+    }
+
+    private function getProcessForCode(string $code, string $cwd = null, array $env = null, $input = null, ?int $timeout = 60): Process
+    {
+        return $this->getProcess([self::$phpBin, '-r', $code], $cwd, $env, $input, $timeout);
+    }
+}
+
+class NonStringifiable
+{
+}
diff --git a/vendor/symfony/process/Tests/SignalListener.php b/vendor/symfony/process/Tests/SignalListener.php
new file mode 100755
index 0000000..9e30ce3
--- /dev/null
+++ b/vendor/symfony/process/Tests/SignalListener.php
@@ -0,0 +1,21 @@
+<?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.
+ */
+
+pcntl_signal(SIGUSR1, function () { echo 'SIGUSR1'; exit; });
+
+echo 'Caught ';
+
+$n = 0;
+
+while ($n++ < 400) {
+    usleep(10000);
+    pcntl_signal_dispatch();
+}
diff --git a/vendor/symfony/process/composer.json b/vendor/symfony/process/composer.json
new file mode 100755
index 0000000..d3efd02
--- /dev/null
+++ b/vendor/symfony/process/composer.json
@@ -0,0 +1,33 @@
+{
+    "name": "symfony/process",
+    "type": "library",
+    "description": "Symfony Process Component",
+    "keywords": [],
+    "homepage": "https://symfony.com",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Fabien Potencier",
+            "email": "fabien@symfony.com"
+        },
+        {
+            "name": "Symfony Community",
+            "homepage": "https://symfony.com/contributors"
+        }
+    ],
+    "require": {
+        "php": "^7.1.3"
+    },
+    "autoload": {
+        "psr-4": { "Symfony\\Component\\Process\\": "" },
+        "exclude-from-classmap": [
+            "/Tests/"
+        ]
+    },
+    "minimum-stability": "dev",
+    "extra": {
+        "branch-alias": {
+            "dev-master": "4.3-dev"
+        }
+    }
+}
diff --git a/vendor/symfony/process/phpunit.xml.dist b/vendor/symfony/process/phpunit.xml.dist
new file mode 100755
index 0000000..c32f251
--- /dev/null
+++ b/vendor/symfony/process/phpunit.xml.dist
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/5.2/phpunit.xsd"
+         backupGlobals="false"
+         colors="true"
+         bootstrap="vendor/autoload.php"
+         failOnRisky="true"
+         failOnWarning="true"
+>
+    <php>
+        <ini name="error_reporting" value="-1" />
+    </php>
+
+    <testsuites>
+        <testsuite name="Symfony Process Component Test Suite">
+            <directory>./Tests/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./</directory>
+            <exclude>
+                <directory>./Tests</directory>
+                <directory>./vendor</directory>
+            </exclude>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/vendor/symfony/psr-http-message-bridge/.gitignore b/vendor/symfony/psr-http-message-bridge/.gitignore
new file mode 100755
index 0000000..027924f
--- /dev/null
+++ b/vendor/symfony/psr-http-message-bridge/.gitignore
@@ -0,0 +1,4 @@
+vendor/
+composer.lock
+phpunit.xml
+.php_cs.cache
diff --git a/vendor/symfony/psr-http-message-bridge/.php_cs.dist b/vendor/symfony/psr-http-message-bridge/.php_cs.dist
new file mode 100755
index 0000000..d741d39
--- /dev/null
+++ b/vendor/symfony/psr-http-message-bridge/.php_cs.dist
@@ -0,0 +1,24 @@
+<?php
+
+return PhpCsFixer\Config::create()
+    ->setRules([
+        '@Symfony' => true,
+        '@Symfony:risky' => true,
+        '@PHPUnit48Migration:risky' => true,
+        'php_unit_no_expectation_annotation' => false, // part of `PHPUnitXYMigration:risky` ruleset, to be enabled when PHPUnit 4.x support will be dropped, as we don't want to rewrite exceptions handling twice
+        'array_syntax' => ['syntax' => 'short'],
+        'fopen_flags' => false,
+        'ordered_imports' => true,
+        'protected_to_private' => false,
+        // Part of @Symfony:risky in PHP-CS-Fixer 2.13.0. To be removed from the config file once upgrading
+        'native_function_invocation' => ['include' => ['@compiler_optimized'], 'scope' => 'namespaced'],
+        // Part of future @Symfony ruleset in PHP-CS-Fixer To be removed from the config file once upgrading
+        'phpdoc_types_order' => ['null_adjustment' => 'always_last', 'sort_algorithm' => 'none'],
+    ])
+    ->setRiskyAllowed(true)
+    ->setFinder(
+        PhpCsFixer\Finder::create()
+            ->in(__DIR__)
+            ->name('*.php')
+    )
+;
diff --git a/vendor/symfony/psr-http-message-bridge/.travis.yml b/vendor/symfony/psr-http-message-bridge/.travis.yml
new file mode 100755
index 0000000..a247c61
--- /dev/null
+++ b/vendor/symfony/psr-http-message-bridge/.travis.yml
@@ -0,0 +1,47 @@
+language: php
+sudo: false
+cache:
+    directories:
+        - $HOME/.composer/cache/files
+        - $HOME/symfony-bridge/.phpunit
+
+env:
+    global:
+        - PHPUNIT_FLAGS="-v"
+        - SYMFONY_PHPUNIT_DIR="$HOME/symfony-bridge/.phpunit"
+
+matrix:
+    fast_finish: true
+    include:
+          # Minimum supported dependencies with the latest and oldest PHP version
+        - php: 7.2
+          env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" SYMFONY_DEPRECATIONS_HELPER="weak_vendors"
+
+        - php: 7.1
+        - php: 7.2
+          env: COVERAGE=true PHPUNIT_FLAGS="-v --coverage-text"
+
+          # Latest commit to master
+        - php: 7.2
+          env: STABILITY="dev"
+
+    allow_failures:
+          # Dev-master is allowed to fail.
+        - env: STABILITY="dev"
+
+before_install:
+    - if [[ $COVERAGE != true ]]; then phpenv config-rm xdebug.ini || true; fi
+    - if ! [ -z "$STABILITY" ]; then composer config minimum-stability ${STABILITY}; fi;
+    - if ! [ -v "$DEPENDENCIES" ]; then composer require --no-update ${DEPENDENCIES}; fi;
+
+install:
+    # To be removed when this issue will be resolved: https://github.com/composer/composer/issues/5355
+    - if [[ "$COMPOSER_FLAGS" == *"--prefer-lowest"* ]]; then composer update --prefer-dist --no-interaction --prefer-stable --quiet; fi
+    - composer update ${COMPOSER_FLAGS} --prefer-dist --no-interaction
+    - ./vendor/bin/simple-phpunit install
+
+script:
+    - composer validate --strict --no-check-lock
+    # simple-phpunit is the PHPUnit wrapper provided by the PHPUnit Bridge component and
+    # it helps with testing legacy code and deprecations (composer require symfony/phpunit-bridge)
+    - ./vendor/bin/simple-phpunit $PHPUNIT_FLAGS
diff --git a/vendor/symfony/psr-http-message-bridge/CHANGELOG.md b/vendor/symfony/psr-http-message-bridge/CHANGELOG.md
new file mode 100755
index 0000000..70f3b82
--- /dev/null
+++ b/vendor/symfony/psr-http-message-bridge/CHANGELOG.md
@@ -0,0 +1,18 @@
+CHANGELOG
+=========
+
+* 1.1.0 (2018-08-30)
+
+  * Added support for creating PSR-7 messages using PSR-17 factories
+
+* 1.0.2 (2017-12-19)
+
+  * Fixed request target in PSR7 Request (mtibben)
+
+* 1.0.1 (2017-12-04)
+
+  * Added support for Symfony 4 (dunglas)
+
+* 1.0.0 (2016-09-14)
+
+  * Initial release
diff --git a/vendor/symfony/psr-http-message-bridge/Factory/DiactorosFactory.php b/vendor/symfony/psr-http-message-bridge/Factory/DiactorosFactory.php
new file mode 100755
index 0000000..d3e70b5
--- /dev/null
+++ b/vendor/symfony/psr-http-message-bridge/Factory/DiactorosFactory.php
@@ -0,0 +1,175 @@
+<?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\Bridge\PsrHttpMessage\Factory;
+
+@trigger_error(sprintf('The "%s" class is deprecated since symfony/psr-http-message-bridge 1.2, use PsrHttpFactory instead.', DiactorosFactory::class), E_USER_DEPRECATED);
+
+use Psr\Http\Message\UploadedFileInterface;
+use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface;
+use Symfony\Component\HttpFoundation\BinaryFileResponse;
+use Symfony\Component\HttpFoundation\File\UploadedFile;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpFoundation\StreamedResponse;
+use Zend\Diactoros\Response as DiactorosResponse;
+use Zend\Diactoros\ServerRequest;
+use Zend\Diactoros\ServerRequestFactory as DiactorosRequestFactory;
+use Zend\Diactoros\Stream as DiactorosStream;
+use Zend\Diactoros\UploadedFile as DiactorosUploadedFile;
+
+/**
+ * Builds Psr\HttpMessage instances using the Zend Diactoros implementation.
+ *
+ * @author Kévin Dunglas <dunglas@gmail.com>
+ *
+ * @deprecated since symfony/psr-http-message-bridge 1.2, use PsrHttpFactory instead
+ */
+class DiactorosFactory implements HttpMessageFactoryInterface
+{
+    public function __construct()
+    {
+        if (!class_exists('Zend\Diactoros\ServerRequestFactory')) {
+            throw new \RuntimeException('Zend Diactoros must be installed to use the DiactorosFactory.');
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createRequest(Request $symfonyRequest)
+    {
+        $server = method_exists('Zend\Diactoros\ServerRequestFactory', 'normalizeServer')
+            ? DiactorosRequestFactory::normalizeServer($symfonyRequest->server->all())
+            : \Zend\Diactoros\normalizeServer($symfonyRequest->server->all());
+        $headers = $symfonyRequest->headers->all();
+
+        $body = new DiactorosStream($symfonyRequest->getContent(true));
+
+        $files = method_exists('Zend\Diactoros\ServerRequestFactory', 'normalizeFiles')
+            ? DiactorosRequestFactory::normalizeFiles($this->getFiles($symfonyRequest->files->all()))
+            : \Zend\Diactoros\normalizeUploadedFiles($this->getFiles($symfonyRequest->files->all()));
+
+        $request = new ServerRequest(
+            $server,
+            $files,
+            $symfonyRequest->getSchemeAndHttpHost().$symfonyRequest->getRequestUri(),
+            $symfonyRequest->getMethod(),
+            $body,
+            $headers
+        );
+
+        $request = $request
+            ->withCookieParams($symfonyRequest->cookies->all())
+            ->withQueryParams($symfonyRequest->query->all())
+            ->withParsedBody($symfonyRequest->request->all())
+            ->withRequestTarget($symfonyRequest->getRequestUri())
+        ;
+
+        foreach ($symfonyRequest->attributes->all() as $key => $value) {
+            $request = $request->withAttribute($key, $value);
+        }
+
+        return $request;
+    }
+
+    /**
+     * Converts Symfony uploaded files array to the PSR one.
+     *
+     * @param array $uploadedFiles
+     *
+     * @return array
+     */
+    private function getFiles(array $uploadedFiles)
+    {
+        $files = [];
+
+        foreach ($uploadedFiles as $key => $value) {
+            if (null === $value) {
+                $files[$key] = new DiactorosUploadedFile(null, 0, UPLOAD_ERR_NO_FILE, null, null);
+                continue;
+            }
+            if ($value instanceof UploadedFile) {
+                $files[$key] = $this->createUploadedFile($value);
+            } else {
+                $files[$key] = $this->getFiles($value);
+            }
+        }
+
+        return $files;
+    }
+
+    /**
+     * Creates a PSR-7 UploadedFile instance from a Symfony one.
+     *
+     * @param UploadedFile $symfonyUploadedFile
+     *
+     * @return UploadedFileInterface
+     */
+    private function createUploadedFile(UploadedFile $symfonyUploadedFile)
+    {
+        return new DiactorosUploadedFile(
+            $symfonyUploadedFile->getRealPath(),
+            (int) $symfonyUploadedFile->getSize(),
+            $symfonyUploadedFile->getError(),
+            $symfonyUploadedFile->getClientOriginalName(),
+            $symfonyUploadedFile->getClientMimeType()
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createResponse(Response $symfonyResponse)
+    {
+        if ($symfonyResponse instanceof BinaryFileResponse) {
+            $stream = new DiactorosStream($symfonyResponse->getFile()->getPathname(), 'r');
+        } else {
+            $stream = new DiactorosStream('php://temp', 'wb+');
+            if ($symfonyResponse instanceof StreamedResponse) {
+                ob_start(function ($buffer) use ($stream) {
+                    $stream->write($buffer);
+
+                    return '';
+                });
+
+                $symfonyResponse->sendContent();
+                ob_end_clean();
+            } else {
+                $stream->write($symfonyResponse->getContent());
+            }
+        }
+
+        $headers = $symfonyResponse->headers->all();
+        if (!isset($headers['Set-Cookie']) && !isset($headers['set-cookie'])) {
+            $cookies = $symfonyResponse->headers->getCookies();
+            if (!empty($cookies)) {
+                $headers['Set-Cookie'] = [];
+                foreach ($cookies as $cookie) {
+                    $headers['Set-Cookie'][] = $cookie->__toString();
+                }
+            }
+        }
+
+        $response = new DiactorosResponse(
+            $stream,
+            $symfonyResponse->getStatusCode(),
+            $headers
+        );
+
+        $protocolVersion = $symfonyResponse->getProtocolVersion();
+        if ('1.1' !== $protocolVersion) {
+            $response = $response->withProtocolVersion($protocolVersion);
+        }
+
+        return $response;
+    }
+}
diff --git a/vendor/symfony/psr-http-message-bridge/Factory/HttpFoundationFactory.php b/vendor/symfony/psr-http-message-bridge/Factory/HttpFoundationFactory.php
new file mode 100755
index 0000000..4be0aff
--- /dev/null
+++ b/vendor/symfony/psr-http-message-bridge/Factory/HttpFoundationFactory.php
@@ -0,0 +1,280 @@
+<?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\Bridge\PsrHttpMessage\Factory;
+
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\StreamInterface;
+use Psr\Http\Message\UploadedFileInterface;
+use Psr\Http\Message\UriInterface;
+use Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface;
+use Symfony\Component\HttpFoundation\Cookie;
+use Symfony\Component\HttpFoundation\File\UploadedFile;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpFoundation\StreamedResponse;
+
+/**
+ * {@inheritdoc}
+ *
+ * @author Kévin Dunglas <dunglas@gmail.com>
+ */
+class HttpFoundationFactory implements HttpFoundationFactoryInterface
+{
+    /**
+     * @var int The maximum output buffering size for each iteration when sending the response
+     */
+    private $responseBufferMaxLength;
+
+    public function __construct(int $responseBufferMaxLength = 16372)
+    {
+        $this->responseBufferMaxLength = $responseBufferMaxLength;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createRequest(ServerRequestInterface $psrRequest)
+    {
+        $server = [];
+        $uri = $psrRequest->getUri();
+
+        if ($uri instanceof UriInterface) {
+            $server['SERVER_NAME'] = $uri->getHost();
+            $server['SERVER_PORT'] = $uri->getPort();
+            $server['REQUEST_URI'] = $uri->getPath();
+            $server['QUERY_STRING'] = $uri->getQuery();
+        }
+
+        $server['REQUEST_METHOD'] = $psrRequest->getMethod();
+
+        $server = array_replace($server, $psrRequest->getServerParams());
+
+        $parsedBody = $psrRequest->getParsedBody();
+        $parsedBody = \is_array($parsedBody) ? $parsedBody : [];
+
+        $request = new Request(
+            $psrRequest->getQueryParams(),
+            $parsedBody,
+            $psrRequest->getAttributes(),
+            $psrRequest->getCookieParams(),
+            $this->getFiles($psrRequest->getUploadedFiles()),
+            $server,
+            $psrRequest->getBody()->__toString()
+        );
+        $request->headers->replace($psrRequest->getHeaders());
+
+        return $request;
+    }
+
+    /**
+     * Converts to the input array to $_FILES structure.
+     *
+     * @param array $uploadedFiles
+     *
+     * @return array
+     */
+    private function getFiles(array $uploadedFiles)
+    {
+        $files = [];
+
+        foreach ($uploadedFiles as $key => $value) {
+            if ($value instanceof UploadedFileInterface) {
+                $files[$key] = $this->createUploadedFile($value);
+            } else {
+                $files[$key] = $this->getFiles($value);
+            }
+        }
+
+        return $files;
+    }
+
+    /**
+     * Creates Symfony UploadedFile instance from PSR-7 ones.
+     *
+     * @param UploadedFileInterface $psrUploadedFile
+     *
+     * @return UploadedFile
+     */
+    private function createUploadedFile(UploadedFileInterface $psrUploadedFile)
+    {
+        $temporaryPath = '';
+        $clientFileName = '';
+        if (UPLOAD_ERR_NO_FILE !== $psrUploadedFile->getError()) {
+            $temporaryPath = $this->getTemporaryPath();
+            $psrUploadedFile->moveTo($temporaryPath);
+
+            $clientFileName = $psrUploadedFile->getClientFilename();
+        }
+
+        if (class_exists('Symfony\Component\HttpFoundation\HeaderUtils')) {
+            // Symfony 4.1+
+            return new UploadedFile(
+                $temporaryPath,
+                null === $clientFileName ? '' : $clientFileName,
+                $psrUploadedFile->getClientMediaType(),
+                $psrUploadedFile->getError(),
+                true
+            );
+        }
+
+        return new UploadedFile(
+            $temporaryPath,
+            null === $clientFileName ? '' : $clientFileName,
+            $psrUploadedFile->getClientMediaType(),
+            $psrUploadedFile->getSize(),
+            $psrUploadedFile->getError(),
+            true
+        );
+    }
+
+    /**
+     * Gets a temporary file path.
+     *
+     * @return string
+     */
+    protected function getTemporaryPath()
+    {
+        return tempnam(sys_get_temp_dir(), uniqid('symfony', true));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createResponse(ResponseInterface $psrResponse, bool $streamed = false)
+    {
+        $cookies = $psrResponse->getHeader('Set-Cookie');
+        $psrResponse = $psrResponse->withoutHeader('Set-Cookie');
+
+        if ($streamed) {
+            $response = new StreamedResponse(
+                $this->createStreamedResponseCallback($psrResponse->getBody()),
+                $psrResponse->getStatusCode(),
+                $psrResponse->getHeaders()
+            );
+        } else {
+            $response = new Response(
+                $psrResponse->getBody()->__toString(),
+                $psrResponse->getStatusCode(),
+                $psrResponse->getHeaders()
+            );
+        }
+
+        $response->setProtocolVersion($psrResponse->getProtocolVersion());
+
+        foreach ($cookies as $cookie) {
+            $response->headers->setCookie($this->createCookie($cookie));
+        }
+
+        return $response;
+    }
+
+    /**
+     * Creates a Cookie instance from a cookie string.
+     *
+     * Some snippets have been taken from the Guzzle project: https://github.com/guzzle/guzzle/blob/5.3/src/Cookie/SetCookie.php#L34
+     *
+     * @param string $cookie
+     *
+     * @return Cookie
+     *
+     * @throws \InvalidArgumentException
+     */
+    private function createCookie($cookie)
+    {
+        foreach (explode(';', $cookie) as $part) {
+            $part = trim($part);
+
+            $data = explode('=', $part, 2);
+            $name = $data[0];
+            $value = isset($data[1]) ? trim($data[1], " \n\r\t\0\x0B\"") : null;
+
+            if (!isset($cookieName)) {
+                $cookieName = $name;
+                $cookieValue = $value;
+
+                continue;
+            }
+
+            if ('expires' === strtolower($name) && null !== $value) {
+                $cookieExpire = new \DateTime($value);
+
+                continue;
+            }
+
+            if ('path' === strtolower($name) && null !== $value) {
+                $cookiePath = $value;
+
+                continue;
+            }
+
+            if ('domain' === strtolower($name) && null !== $value) {
+                $cookieDomain = $value;
+
+                continue;
+            }
+
+            if ('secure' === strtolower($name)) {
+                $cookieSecure = true;
+
+                continue;
+            }
+
+            if ('httponly' === strtolower($name)) {
+                $cookieHttpOnly = true;
+
+                continue;
+            }
+
+            if ('samesite' === strtolower($name) && null !== $value) {
+                $samesite = $value;
+
+                continue;
+            }
+        }
+
+        if (!isset($cookieName)) {
+            throw new \InvalidArgumentException('The value of the Set-Cookie header is malformed.');
+        }
+
+        return new Cookie(
+            $cookieName,
+            $cookieValue,
+            isset($cookieExpire) ? $cookieExpire : 0,
+            isset($cookiePath) ? $cookiePath : '/',
+            isset($cookieDomain) ? $cookieDomain : null,
+            isset($cookieSecure),
+            isset($cookieHttpOnly),
+            false,
+            isset($samesite) ? $samesite : null
+        );
+    }
+
+    private function createStreamedResponseCallback(StreamInterface $body): callable
+    {
+        return function () use ($body) {
+            if ($body->isSeekable()) {
+                $body->rewind();
+            }
+
+            if (!$body->isReadable()) {
+                echo $body;
+
+                return;
+            }
+
+            while (!$body->eof()) {
+                echo $body->read($this->responseBufferMaxLength);
+            }
+        };
+    }
+}
diff --git a/vendor/symfony/psr-http-message-bridge/Factory/PsrHttpFactory.php b/vendor/symfony/psr-http-message-bridge/Factory/PsrHttpFactory.php
new file mode 100755
index 0000000..a37e1e9
--- /dev/null
+++ b/vendor/symfony/psr-http-message-bridge/Factory/PsrHttpFactory.php
@@ -0,0 +1,172 @@
+<?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\Bridge\PsrHttpMessage\Factory;
+
+use Psr\Http\Message\ResponseFactoryInterface;
+use Psr\Http\Message\ServerRequestFactoryInterface;
+use Psr\Http\Message\StreamFactoryInterface;
+use Psr\Http\Message\UploadedFileFactoryInterface;
+use Psr\Http\Message\UploadedFileInterface;
+use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface;
+use Symfony\Component\HttpFoundation\BinaryFileResponse;
+use Symfony\Component\HttpFoundation\File\UploadedFile;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpFoundation\StreamedResponse;
+
+/**
+ * Builds Psr\HttpMessage instances using a PSR-17 implementation.
+ *
+ * @author Antonio J. García Lagar <aj@garcialagar.es>
+ */
+class PsrHttpFactory implements HttpMessageFactoryInterface
+{
+    private $serverRequestFactory;
+    private $streamFactory;
+    private $uploadedFileFactory;
+    private $responseFactory;
+
+    public function __construct(ServerRequestFactoryInterface $serverRequestFactory, StreamFactoryInterface $streamFactory, UploadedFileFactoryInterface $uploadedFileFactory, ResponseFactoryInterface $responseFactory)
+    {
+        $this->serverRequestFactory = $serverRequestFactory;
+        $this->streamFactory = $streamFactory;
+        $this->uploadedFileFactory = $uploadedFileFactory;
+        $this->responseFactory = $responseFactory;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createRequest(Request $symfonyRequest)
+    {
+        $request = $this->serverRequestFactory->createServerRequest(
+            $symfonyRequest->getMethod(),
+            $symfonyRequest->getUri(),
+            $symfonyRequest->server->all()
+        );
+
+        foreach ($symfonyRequest->headers->all() as $name => $value) {
+            $request = $request->withHeader($name, $value);
+        }
+
+        $body = $this->streamFactory->createStreamFromResource($symfonyRequest->getContent(true));
+
+        $request = $request
+            ->withBody($body)
+            ->withUploadedFiles($this->getFiles($symfonyRequest->files->all()))
+            ->withCookieParams($symfonyRequest->cookies->all())
+            ->withQueryParams($symfonyRequest->query->all())
+            ->withParsedBody($symfonyRequest->request->all())
+        ;
+
+        foreach ($symfonyRequest->attributes->all() as $key => $value) {
+            $request = $request->withAttribute($key, $value);
+        }
+
+        return $request;
+    }
+
+    /**
+     * Converts Symfony uploaded files array to the PSR one.
+     *
+     * @param array $uploadedFiles
+     *
+     * @return array
+     */
+    private function getFiles(array $uploadedFiles)
+    {
+        $files = [];
+
+        foreach ($uploadedFiles as $key => $value) {
+            if (null === $value) {
+                $files[$key] = $this->uploadedFileFactory->createUploadedFile($this->streamFactory->createStream(), 0, UPLOAD_ERR_NO_FILE);
+                continue;
+            }
+            if ($value instanceof UploadedFile) {
+                $files[$key] = $this->createUploadedFile($value);
+            } else {
+                $files[$key] = $this->getFiles($value);
+            }
+        }
+
+        return $files;
+    }
+
+    /**
+     * Creates a PSR-7 UploadedFile instance from a Symfony one.
+     *
+     * @param UploadedFile $symfonyUploadedFile
+     *
+     * @return UploadedFileInterface
+     */
+    private function createUploadedFile(UploadedFile $symfonyUploadedFile)
+    {
+        return $this->uploadedFileFactory->createUploadedFile(
+            $this->streamFactory->createStreamFromFile(
+                $symfonyUploadedFile->getRealPath()
+            ),
+            (int) $symfonyUploadedFile->getSize(),
+            $symfonyUploadedFile->getError(),
+            $symfonyUploadedFile->getClientOriginalName(),
+            $symfonyUploadedFile->getClientMimeType()
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createResponse(Response $symfonyResponse)
+    {
+        $response = $this->responseFactory->createResponse($symfonyResponse->getStatusCode(), Response::$statusTexts[$symfonyResponse->getStatusCode()] ?? '');
+
+        if ($symfonyResponse instanceof BinaryFileResponse) {
+            $stream = $this->streamFactory->createStreamFromFile(
+                $symfonyResponse->getFile()->getPathname()
+            );
+        } else {
+            $stream = $this->streamFactory->createStreamFromFile('php://temp', 'wb+');
+            if ($symfonyResponse instanceof StreamedResponse) {
+                ob_start(function ($buffer) use ($stream) {
+                    $stream->write($buffer);
+
+                    return '';
+                });
+
+                $symfonyResponse->sendContent();
+                ob_end_clean();
+            } else {
+                $stream->write($symfonyResponse->getContent());
+            }
+        }
+
+        $response = $response->withBody($stream);
+
+        $headers = $symfonyResponse->headers->all();
+        $cookies = $symfonyResponse->headers->getCookies();
+        if (!empty($cookies)) {
+            $headers['Set-Cookie'] = [];
+
+            foreach ($cookies as $cookie) {
+                $headers['Set-Cookie'][] = $cookie->__toString();
+            }
+        }
+
+        foreach ($headers as $name => $value) {
+            $response = $response->withHeader($name, $value);
+        }
+
+        $protocolVersion = $symfonyResponse->getProtocolVersion();
+        $response = $response->withProtocolVersion($protocolVersion);
+
+        return $response;
+    }
+}
diff --git a/vendor/symfony/psr-http-message-bridge/HttpFoundationFactoryInterface.php b/vendor/symfony/psr-http-message-bridge/HttpFoundationFactoryInterface.php
new file mode 100755
index 0000000..79ec40f
--- /dev/null
+++ b/vendor/symfony/psr-http-message-bridge/HttpFoundationFactoryInterface.php
@@ -0,0 +1,43 @@
+<?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\Bridge\PsrHttpMessage;
+
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * Creates Symfony Request and Response instances from PSR-7 ones.
+ *
+ * @author Kévin Dunglas <dunglas@gmail.com>
+ */
+interface HttpFoundationFactoryInterface
+{
+    /**
+     * Creates a Symfony Request instance from a PSR-7 one.
+     *
+     * @param ServerRequestInterface $psrRequest
+     *
+     * @return Request
+     */
+    public function createRequest(ServerRequestInterface $psrRequest);
+
+    /**
+     * Creates a Symfony Response instance from a PSR-7 one.
+     *
+     * @param ResponseInterface $psrResponse
+     *
+     * @return Response
+     */
+    public function createResponse(ResponseInterface $psrResponse);
+}
diff --git a/vendor/symfony/psr-http-message-bridge/HttpMessageFactoryInterface.php b/vendor/symfony/psr-http-message-bridge/HttpMessageFactoryInterface.php
new file mode 100755
index 0000000..b7960e8
--- /dev/null
+++ b/vendor/symfony/psr-http-message-bridge/HttpMessageFactoryInterface.php
@@ -0,0 +1,43 @@
+<?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\Bridge\PsrHttpMessage;
+
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * Creates PSR HTTP Request and Response instances from Symfony ones.
+ *
+ * @author Kévin Dunglas <dunglas@gmail.com>
+ */
+interface HttpMessageFactoryInterface
+{
+    /**
+     * Creates a PSR-7 Request instance from a Symfony one.
+     *
+     * @param Request $symfonyRequest
+     *
+     * @return ServerRequestInterface
+     */
+    public function createRequest(Request $symfonyRequest);
+
+    /**
+     * Creates a PSR-7 Response instance from a Symfony one.
+     *
+     * @param Response $symfonyResponse
+     *
+     * @return ResponseInterface
+     */
+    public function createResponse(Response $symfonyResponse);
+}
diff --git a/vendor/symfony/psr-http-message-bridge/LICENSE b/vendor/symfony/psr-http-message-bridge/LICENSE
new file mode 100755
index 0000000..12a7453
--- /dev/null
+++ b/vendor/symfony/psr-http-message-bridge/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2004-2016 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.
diff --git a/vendor/symfony/psr-http-message-bridge/README.md b/vendor/symfony/psr-http-message-bridge/README.md
new file mode 100755
index 0000000..87fbd43
--- /dev/null
+++ b/vendor/symfony/psr-http-message-bridge/README.md
@@ -0,0 +1,20 @@
+PSR-7 Bridge
+============
+
+Provides integration for PSR7.
+
+Resources
+---------
+
+  * [Documentation](https://symfony.com/doc/current/components/psr7.html)
+  * [SensioFrameworkExtraBundle](https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/index.html#psr-7-support)
+
+Running the tests
+-----------------
+
+If you want to run the unit tests, install dev dependencies before
+running PHPUnit:
+
+    $ cd path/to/Symfony/Bridge/PsrHttpMessage/
+    $ composer.phar install
+    $ phpunit
diff --git a/vendor/symfony/psr-http-message-bridge/Tests/Factory/AbstractHttpMessageFactoryTest.php b/vendor/symfony/psr-http-message-bridge/Tests/Factory/AbstractHttpMessageFactoryTest.php
new file mode 100755
index 0000000..4b8bb97
--- /dev/null
+++ b/vendor/symfony/psr-http-message-bridge/Tests/Factory/AbstractHttpMessageFactoryTest.php
@@ -0,0 +1,229 @@
+<?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\Bridge\PsrHttpMessage\Tests\Factory;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface;
+use Symfony\Component\HttpFoundation\BinaryFileResponse;
+use Symfony\Component\HttpFoundation\Cookie;
+use Symfony\Component\HttpFoundation\File\UploadedFile;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpFoundation\StreamedResponse;
+
+/**
+ * @author Kévin Dunglas <dunglas@gmail.com>
+ * @author Antonio J. García Lagar <aj@garcialagar.es>
+ */
+abstract class AbstractHttpMessageFactoryTest extends TestCase
+{
+    private $factory;
+    private $tmpDir;
+
+    /**
+     * @return HttpMessageFactoryInterface
+     */
+    abstract protected function buildHttpMessageFactory();
+
+    public function setup()
+    {
+        $this->factory = $this->buildHttpMessageFactory();
+        $this->tmpDir = sys_get_temp_dir();
+    }
+
+    public function testCreateRequest()
+    {
+        $stdClass = new \stdClass();
+        $request = new Request(
+            [
+                'bar' => ['baz' => '42'],
+                'foo' => '1',
+            ],
+            [
+                'twitter' => [
+                    '@dunglas' => 'Kévin Dunglas',
+                    '@coopTilleuls' => 'Les-Tilleuls.coop',
+                ],
+                'baz' => '2',
+            ],
+            [
+                'a1' => $stdClass,
+                'a2' => ['foo' => 'bar'],
+            ],
+            [
+                'c1' => 'foo',
+                'c2' => ['c3' => 'bar'],
+            ],
+            [
+                'f1' => $this->createUploadedFile('F1', 'f1.txt', 'text/plain', UPLOAD_ERR_OK),
+                'foo' => ['f2' => $this->createUploadedFile('F2', 'f2.txt', 'text/plain', UPLOAD_ERR_OK)],
+            ],
+            [
+                'REQUEST_METHOD' => 'POST',
+                'HTTP_HOST' => 'dunglas.fr',
+                'HTTP_X_SYMFONY' => '2.8',
+                'REQUEST_URI' => '/testCreateRequest?bar[baz]=42&foo=1',
+                'QUERY_STRING' => 'bar[baz]=42&foo=1',
+            ],
+            'Content'
+        );
+
+        $psrRequest = $this->factory->createRequest($request);
+
+        $this->assertEquals('Content', $psrRequest->getBody()->__toString());
+
+        $queryParams = $psrRequest->getQueryParams();
+        $this->assertEquals('1', $queryParams['foo']);
+        $this->assertEquals('42', $queryParams['bar']['baz']);
+
+        $requestTarget = $psrRequest->getRequestTarget();
+        $this->assertEquals('/testCreateRequest?bar[baz]=42&foo=1', urldecode($requestTarget));
+
+        $parsedBody = $psrRequest->getParsedBody();
+        $this->assertEquals('Kévin Dunglas', $parsedBody['twitter']['@dunglas']);
+        $this->assertEquals('Les-Tilleuls.coop', $parsedBody['twitter']['@coopTilleuls']);
+        $this->assertEquals('2', $parsedBody['baz']);
+
+        $attributes = $psrRequest->getAttributes();
+        $this->assertEquals($stdClass, $attributes['a1']);
+        $this->assertEquals('bar', $attributes['a2']['foo']);
+
+        $cookies = $psrRequest->getCookieParams();
+        $this->assertEquals('foo', $cookies['c1']);
+        $this->assertEquals('bar', $cookies['c2']['c3']);
+
+        $uploadedFiles = $psrRequest->getUploadedFiles();
+        $this->assertEquals('F1', $uploadedFiles['f1']->getStream()->__toString());
+        $this->assertEquals('f1.txt', $uploadedFiles['f1']->getClientFilename());
+        $this->assertEquals('text/plain', $uploadedFiles['f1']->getClientMediaType());
+        $this->assertEquals(UPLOAD_ERR_OK, $uploadedFiles['f1']->getError());
+
+        $this->assertEquals('F2', $uploadedFiles['foo']['f2']->getStream()->__toString());
+        $this->assertEquals('f2.txt', $uploadedFiles['foo']['f2']->getClientFilename());
+        $this->assertEquals('text/plain', $uploadedFiles['foo']['f2']->getClientMediaType());
+        $this->assertEquals(UPLOAD_ERR_OK, $uploadedFiles['foo']['f2']->getError());
+
+        $serverParams = $psrRequest->getServerParams();
+        $this->assertEquals('POST', $serverParams['REQUEST_METHOD']);
+        $this->assertEquals('2.8', $serverParams['HTTP_X_SYMFONY']);
+        $this->assertEquals('POST', $psrRequest->getMethod());
+        $this->assertEquals(['2.8'], $psrRequest->getHeader('X-Symfony'));
+    }
+
+    public function testGetContentCanBeCalledAfterRequestCreation()
+    {
+        $header = ['HTTP_HOST' => 'dunglas.fr'];
+        $request = new Request([], [], [], [], [], $header, 'Content');
+
+        $psrRequest = $this->factory->createRequest($request);
+
+        $this->assertEquals('Content', $psrRequest->getBody()->__toString());
+        $this->assertEquals('Content', $request->getContent());
+    }
+
+    private function createUploadedFile($content, $originalName, $mimeType, $error)
+    {
+        $path = tempnam($this->tmpDir, uniqid());
+        file_put_contents($path, $content);
+
+        if (class_exists('Symfony\Component\HttpFoundation\HeaderUtils')) {
+            // Symfony 4.1+
+            return new UploadedFile($path, $originalName, $mimeType, $error, true);
+        }
+
+        return new UploadedFile($path, $originalName, $mimeType, filesize($path), $error, true);
+    }
+
+    public function testCreateResponse()
+    {
+        $response = new Response(
+            'Response content.',
+            202,
+            ['X-Symfony' => ['3.4']]
+        );
+        $response->headers->setCookie(new Cookie('city', 'Lille', new \DateTime('Wed, 13 Jan 2021 22:23:01 GMT'), '/', null, false, true, false, 'lax'));
+
+        $psrResponse = $this->factory->createResponse($response);
+        $this->assertEquals('Response content.', $psrResponse->getBody()->__toString());
+        $this->assertEquals(202, $psrResponse->getStatusCode());
+        $this->assertEquals(['3.4'], $psrResponse->getHeader('X-Symfony'));
+
+        $cookieHeader = $psrResponse->getHeader('Set-Cookie');
+        $this->assertInternalType('array', $cookieHeader);
+        $this->assertCount(1, $cookieHeader);
+        $this->assertRegExp('{city=Lille; expires=Wed, 13-Jan-2021 22:23:01 GMT;( max-age=\d+;)? path=/; httponly}i', $cookieHeader[0]);
+    }
+
+    public function testCreateResponseFromStreamed()
+    {
+        $response = new StreamedResponse(function () {
+            echo "Line 1\n";
+            flush();
+
+            echo "Line 2\n";
+            flush();
+        });
+
+        $psrResponse = $this->factory->createResponse($response);
+
+        $this->assertEquals("Line 1\nLine 2\n", $psrResponse->getBody()->__toString());
+    }
+
+    public function testCreateResponseFromBinaryFile()
+    {
+        $path = tempnam($this->tmpDir, uniqid());
+        file_put_contents($path, 'Binary');
+
+        $response = new BinaryFileResponse($path);
+
+        $psrResponse = $this->factory->createResponse($response);
+
+        $this->assertEquals('Binary', $psrResponse->getBody()->__toString());
+    }
+
+    public function testUploadErrNoFile()
+    {
+        if (class_exists('Symfony\Component\HttpFoundation\HeaderUtils')) {
+            // Symfony 4.1+
+            $file = new UploadedFile('', '', null, UPLOAD_ERR_NO_FILE, true);
+        } else {
+            $file = new UploadedFile('', '', null, 0, UPLOAD_ERR_NO_FILE, true);
+        }
+        $this->assertEquals(0, $file->getSize());
+        $this->assertEquals(UPLOAD_ERR_NO_FILE, $file->getError());
+        $this->assertFalse($file->getSize(), 'SplFile::getSize() returns false on error');
+
+        $request = new Request(
+            [],
+            [],
+            [],
+            [],
+            [
+            'f1' => $file,
+            'f2' => ['name' => null, 'type' => null, 'tmp_name' => null, 'error' => UPLOAD_ERR_NO_FILE, 'size' => 0],
+          ],
+            [
+            'REQUEST_METHOD' => 'POST',
+            'HTTP_HOST' => 'dunglas.fr',
+            'HTTP_X_SYMFONY' => '2.8',
+          ],
+            'Content'
+        );
+
+        $psrRequest = $this->factory->createRequest($request);
+
+        $uploadedFiles = $psrRequest->getUploadedFiles();
+
+        $this->assertEquals(UPLOAD_ERR_NO_FILE, $uploadedFiles['f1']->getError());
+        $this->assertEquals(UPLOAD_ERR_NO_FILE, $uploadedFiles['f2']->getError());
+    }
+}
diff --git a/vendor/symfony/psr-http-message-bridge/Tests/Factory/DiactorosFactoryTest.php b/vendor/symfony/psr-http-message-bridge/Tests/Factory/DiactorosFactoryTest.php
new file mode 100755
index 0000000..5ebe7a9
--- /dev/null
+++ b/vendor/symfony/psr-http-message-bridge/Tests/Factory/DiactorosFactoryTest.php
@@ -0,0 +1,32 @@
+<?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\Bridge\PsrHttpMessage\Tests\Factory;
+
+use Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory;
+
+/**
+ * @author Kévin Dunglas <dunglas@gmail.com>
+ * @author Antonio J. García Lagar <aj@garcialagar.es>
+ *
+ * @group legacy
+ */
+class DiactorosFactoryTest extends AbstractHttpMessageFactoryTest
+{
+    protected function buildHttpMessageFactory()
+    {
+        if (!class_exists('Zend\Diactoros\ServerRequestFactory')) {
+            $this->markTestSkipped('Zend Diactoros is not installed.');
+        }
+
+        return new DiactorosFactory();
+    }
+}
diff --git a/vendor/symfony/psr-http-message-bridge/Tests/Factory/HttpFoundationFactoryTest.php b/vendor/symfony/psr-http-message-bridge/Tests/Factory/HttpFoundationFactoryTest.php
new file mode 100755
index 0000000..27b84c0
--- /dev/null
+++ b/vendor/symfony/psr-http-message-bridge/Tests/Factory/HttpFoundationFactoryTest.php
@@ -0,0 +1,250 @@
+<?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\Bridge\PsrHttpMessage\Tests\Factory;
+
+use PHPUnit\Framework\TestCase;
+use Psr\Http\Message\UploadedFileInterface;
+use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
+use Symfony\Bridge\PsrHttpMessage\Tests\Fixtures\Response;
+use Symfony\Bridge\PsrHttpMessage\Tests\Fixtures\ServerRequest;
+use Symfony\Bridge\PsrHttpMessage\Tests\Fixtures\Stream;
+use Symfony\Bridge\PsrHttpMessage\Tests\Fixtures\UploadedFile;
+use Symfony\Bridge\PsrHttpMessage\Tests\Fixtures\Uri;
+use Symfony\Component\HttpFoundation\Cookie;
+
+/**
+ * @author Kévin Dunglas <dunglas@gmail.com>
+ */
+class HttpFoundationFactoryTest extends TestCase
+{
+    /** @var HttpFoundationFactory */
+    private $factory;
+
+    /** @var string */
+    private $tmpDir;
+
+    public function setup()
+    {
+        $this->factory = new HttpFoundationFactory();
+        $this->tmpDir = sys_get_temp_dir();
+    }
+
+    public function testCreateRequest()
+    {
+        $stdClass = new \stdClass();
+        $serverRequest = new ServerRequest(
+            '1.1',
+            [
+                'X-Dunglas-API-Platform' => '1.0',
+                'X-data' => ['a', 'b'],
+            ],
+            new Stream('The body'),
+            '/about/kevin',
+            'GET',
+            'http://les-tilleuls.coop/about/kevin',
+            ['country' => 'France'],
+            ['city' => 'Lille'],
+            ['url' => 'http://les-tilleuls.coop'],
+            [
+                'doc1' => $this->createUploadedFile('Doc 1', UPLOAD_ERR_OK, 'doc1.txt', 'text/plain'),
+                'nested' => [
+                    'docs' => [
+                        $this->createUploadedFile('Doc 2', UPLOAD_ERR_OK, 'doc2.txt', 'text/plain'),
+                        $this->createUploadedFile('Doc 3', UPLOAD_ERR_OK, 'doc3.txt', 'text/plain'),
+                    ],
+                ],
+            ],
+            ['url' => 'http://dunglas.fr'],
+            ['custom' => $stdClass]
+        );
+
+        $symfonyRequest = $this->factory->createRequest($serverRequest);
+        $files = $symfonyRequest->files->all();
+
+        $this->assertEquals('http://les-tilleuls.coop', $symfonyRequest->query->get('url'));
+        $this->assertEquals('doc1.txt', $files['doc1']->getClientOriginalName());
+        $this->assertEquals('doc2.txt', $files['nested']['docs'][0]->getClientOriginalName());
+        $this->assertEquals('doc3.txt', $files['nested']['docs'][1]->getClientOriginalName());
+        $this->assertEquals('http://dunglas.fr', $symfonyRequest->request->get('url'));
+        $this->assertEquals($stdClass, $symfonyRequest->attributes->get('custom'));
+        $this->assertEquals('Lille', $symfonyRequest->cookies->get('city'));
+        $this->assertEquals('France', $symfonyRequest->server->get('country'));
+        $this->assertEquals('The body', $symfonyRequest->getContent());
+        $this->assertEquals('1.0', $symfonyRequest->headers->get('X-Dunglas-API-Platform'));
+        $this->assertEquals(['a', 'b'], $symfonyRequest->headers->get('X-data', null, false));
+    }
+
+    public function testCreateRequestWithNullParsedBody()
+    {
+        $serverRequest = new ServerRequest(
+            '1.1',
+            [],
+            new Stream(),
+            '/',
+            'GET',
+            null,
+            [],
+            [],
+            [],
+            [],
+            null,
+            []
+        );
+
+        $this->assertCount(0, $this->factory->createRequest($serverRequest)->request);
+    }
+
+    public function testCreateRequestWithObjectParsedBody()
+    {
+        $serverRequest = new ServerRequest(
+            '1.1',
+            [],
+            new Stream(),
+            '/',
+            'GET',
+            null,
+            [],
+            [],
+            [],
+            [],
+            new \stdClass(),
+            []
+        );
+
+        $this->assertCount(0, $this->factory->createRequest($serverRequest)->request);
+    }
+
+    public function testCreateRequestWithUri()
+    {
+        $serverRequest = new ServerRequest(
+            '1.1',
+            [],
+            new Stream(),
+            '/',
+            'GET',
+            new Uri('http://les-tilleuls.coop/about/kevin'),
+            [],
+            [],
+            [],
+            [],
+            null,
+            []
+        );
+
+        $this->assertEquals('/about/kevin', $this->factory->createRequest($serverRequest)->getPathInfo());
+    }
+
+    public function testCreateUploadedFile()
+    {
+        $uploadedFile = $this->createUploadedFile('An uploaded file.', UPLOAD_ERR_OK, 'myfile.txt', 'text/plain');
+        $symfonyUploadedFile = $this->callCreateUploadedFile($uploadedFile);
+        $size = $symfonyUploadedFile->getSize();
+
+        $uniqid = uniqid();
+        $symfonyUploadedFile->move($this->tmpDir, $uniqid);
+
+        $this->assertEquals($uploadedFile->getSize(), $size);
+        $this->assertEquals(UPLOAD_ERR_OK, $symfonyUploadedFile->getError());
+        $this->assertEquals('myfile.txt', $symfonyUploadedFile->getClientOriginalName());
+        $this->assertEquals('txt', $symfonyUploadedFile->getClientOriginalExtension());
+        $this->assertEquals('text/plain', $symfonyUploadedFile->getClientMimeType());
+        $this->assertEquals('An uploaded file.', file_get_contents($this->tmpDir.'/'.$uniqid));
+    }
+
+    /**
+     * @expectedException        \Symfony\Component\HttpFoundation\File\Exception\FileException
+     * @expectedExceptionMessage The file "e" could not be written on disk.
+     */
+    public function testCreateUploadedFileWithError()
+    {
+        $uploadedFile = $this->createUploadedFile('Error.', UPLOAD_ERR_CANT_WRITE, 'e', 'text/plain');
+        $symfonyUploadedFile = $this->callCreateUploadedFile($uploadedFile);
+
+        $this->assertEquals(UPLOAD_ERR_CANT_WRITE, $symfonyUploadedFile->getError());
+
+        $symfonyUploadedFile->move($this->tmpDir, 'shouldFail.txt');
+    }
+
+    private function createUploadedFile($content, $error, $clientFileName, $clientMediaType)
+    {
+        $filePath = tempnam($this->tmpDir, uniqid());
+        file_put_contents($filePath, $content);
+
+        return new UploadedFile($filePath, filesize($filePath), $error, $clientFileName, $clientMediaType);
+    }
+
+    private function callCreateUploadedFile(UploadedFileInterface $uploadedFile)
+    {
+        $reflection = new \ReflectionClass($this->factory);
+        $createUploadedFile = $reflection->getMethod('createUploadedFile');
+        $createUploadedFile->setAccessible(true);
+
+        return $createUploadedFile->invokeArgs($this->factory, [$uploadedFile]);
+    }
+
+    public function testCreateResponse()
+    {
+        $response = new Response(
+            '1.0',
+            [
+                'X-Symfony' => ['2.8'],
+                'Set-Cookie' => [
+                    'theme=light',
+                    'test',
+                    'ABC=AeD; Domain=dunglas.fr; Path=/kevin; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Secure; HttpOnly; SameSite=Strict',
+                ],
+            ],
+            new Stream('The response body'),
+            200
+        );
+
+        $symfonyResponse = $this->factory->createResponse($response);
+
+        $this->assertEquals('1.0', $symfonyResponse->getProtocolVersion());
+        $this->assertEquals('2.8', $symfonyResponse->headers->get('X-Symfony'));
+
+        $cookies = $symfonyResponse->headers->getCookies();
+        $this->assertEquals('theme', $cookies[0]->getName());
+        $this->assertEquals('light', $cookies[0]->getValue());
+        $this->assertEquals(0, $cookies[0]->getExpiresTime());
+        $this->assertNull($cookies[0]->getDomain());
+        $this->assertEquals('/', $cookies[0]->getPath());
+        $this->assertFalse($cookies[0]->isSecure());
+        $this->assertFalse($cookies[0]->isHttpOnly());
+
+        $this->assertEquals('test', $cookies[1]->getName());
+        $this->assertNull($cookies[1]->getValue());
+
+        $this->assertEquals('ABC', $cookies[2]->getName());
+        $this->assertEquals('AeD', $cookies[2]->getValue());
+        $this->assertEquals(strtotime('Wed, 13 Jan 2021 22:23:01 GMT'), $cookies[2]->getExpiresTime());
+        $this->assertEquals('dunglas.fr', $cookies[2]->getDomain());
+        $this->assertEquals('/kevin', $cookies[2]->getPath());
+        $this->assertTrue($cookies[2]->isSecure());
+        $this->assertTrue($cookies[2]->isHttpOnly());
+        if (\defined('Symfony\Component\HttpFoundation\Cookie::SAMESITE_STRICT')) {
+            $this->assertEquals(Cookie::SAMESITE_STRICT, $cookies[2]->getSameSite());
+        }
+
+        $this->assertEquals('The response body', $symfonyResponse->getContent());
+        $this->assertEquals(200, $symfonyResponse->getStatusCode());
+
+        $symfonyResponse = $this->factory->createResponse($response, true);
+
+        ob_start();
+        $symfonyResponse->sendContent();
+        $sentContent = ob_get_clean();
+
+        $this->assertEquals('The response body', $sentContent);
+        $this->assertEquals(200, $symfonyResponse->getStatusCode());
+    }
+}
diff --git a/vendor/symfony/psr-http-message-bridge/Tests/Factory/PsrHttpFactoryTest.php b/vendor/symfony/psr-http-message-bridge/Tests/Factory/PsrHttpFactoryTest.php
new file mode 100755
index 0000000..da12ea7
--- /dev/null
+++ b/vendor/symfony/psr-http-message-bridge/Tests/Factory/PsrHttpFactoryTest.php
@@ -0,0 +1,29 @@
+<?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\Bridge\PsrHttpMessage\Tests\Factory;
+
+use Nyholm\Psr7\Factory\Psr17Factory;
+use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory;
+
+/**
+ * @author Kévin Dunglas <dunglas@gmail.com>
+ * @author Antonio J. García Lagar <aj@garcialagar.es>
+ */
+class PsrHttpFactoryTest extends AbstractHttpMessageFactoryTest
+{
+    protected function buildHttpMessageFactory()
+    {
+        $factory = new Psr17Factory();
+
+        return new PsrHttpFactory($factory, $factory, $factory, $factory);
+    }
+}
diff --git a/vendor/symfony/psr-http-message-bridge/Tests/Fixtures/Message.php b/vendor/symfony/psr-http-message-bridge/Tests/Fixtures/Message.php
new file mode 100755
index 0000000..0cda6fc
--- /dev/null
+++ b/vendor/symfony/psr-http-message-bridge/Tests/Fixtures/Message.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\Bridge\PsrHttpMessage\Tests\Fixtures;
+
+use Psr\Http\Message\MessageInterface;
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Message.
+ *
+ * @author Kévin Dunglas <dunglas@gmail.com>
+ */
+class Message implements MessageInterface
+{
+    private $version = '1.1';
+    private $headers = [];
+    private $body;
+
+    public function __construct($version = '1.1', array $headers = [], StreamInterface $body = null)
+    {
+        $this->version = $version;
+        $this->headers = $headers;
+        $this->body = null === $body ? new Stream() : $body;
+    }
+
+    public function getProtocolVersion()
+    {
+        return $this->version;
+    }
+
+    public function withProtocolVersion($version)
+    {
+        throw new \BadMethodCallException('Not implemented.');
+    }
+
+    public function getHeaders()
+    {
+        return $this->headers;
+    }
+
+    public function hasHeader($name)
+    {
+        return isset($this->headers[$name]);
+    }
+
+    public function getHeader($name)
+    {
+        return $this->hasHeader($name) ? $this->headers[$name] : [];
+    }
+
+    public function getHeaderLine($name)
+    {
+        return $this->hasHeader($name) ? implode(',', $this->headers[$name]) : '';
+    }
+
+    public function withHeader($name, $value)
+    {
+        $this->headers[$name] = (array) $value;
+
+        return $this;
+    }
+
+    public function withAddedHeader($name, $value)
+    {
+        throw new \BadMethodCallException('Not implemented.');
+    }
+
+    public function withoutHeader($name)
+    {
+        unset($this->headers[$name]);
+
+        return $this;
+    }
+
+    public function getBody()
+    {
+        return $this->body;
+    }
+
+    public function withBody(StreamInterface $body)
+    {
+        throw new \BadMethodCallException('Not implemented.');
+    }
+}
diff --git a/vendor/symfony/psr-http-message-bridge/Tests/Fixtures/Response.php b/vendor/symfony/psr-http-message-bridge/Tests/Fixtures/Response.php
new file mode 100755
index 0000000..a890792
--- /dev/null
+++ b/vendor/symfony/psr-http-message-bridge/Tests/Fixtures/Response.php
@@ -0,0 +1,45 @@
+<?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\Bridge\PsrHttpMessage\Tests\Fixtures;
+
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * @author Kévin Dunglas <dunglas@gmail.com>
+ */
+class Response extends Message implements ResponseInterface
+{
+    private $statusCode;
+
+    public function __construct($version = '1.1', array $headers = [], StreamInterface $body = null, $statusCode = 200)
+    {
+        parent::__construct($version, $headers, $body);
+
+        $this->statusCode = $statusCode;
+    }
+
+    public function getStatusCode()
+    {
+        return $this->statusCode;
+    }
+
+    public function withStatus($code, $reasonPhrase = '')
+    {
+        throw new \BadMethodCallException('Not implemented.');
+    }
+
+    public function getReasonPhrase()
+    {
+        throw new \BadMethodCallException('Not implemented.');
+    }
+}
diff --git a/vendor/symfony/psr-http-message-bridge/Tests/Fixtures/ServerRequest.php b/vendor/symfony/psr-http-message-bridge/Tests/Fixtures/ServerRequest.php
new file mode 100755
index 0000000..88ec984
--- /dev/null
+++ b/vendor/symfony/psr-http-message-bridge/Tests/Fixtures/ServerRequest.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\Bridge\PsrHttpMessage\Tests\Fixtures;
+
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\StreamInterface;
+use Psr\Http\Message\UriInterface;
+
+/**
+ * @author Kévin Dunglas <dunglas@gmail.com>
+ */
+class ServerRequest extends Message implements ServerRequestInterface
+{
+    private $requestTarget;
+    private $method;
+    private $uri;
+    private $server;
+    private $cookies;
+    private $query;
+    private $uploadedFiles;
+    private $data;
+    private $attributes;
+
+    public function __construct($version = '1.1', array $headers = [], StreamInterface $body = null, $requestTarget = '/', $method = 'GET', $uri = null, array $server = [], array $cookies = [], array $query = [], array $uploadedFiles = [], $data = null, array $attributes = [])
+    {
+        parent::__construct($version, $headers, $body);
+
+        $this->requestTarget = $requestTarget;
+        $this->method = $method;
+        $this->uri = $uri;
+        $this->server = $server;
+        $this->cookies = $cookies;
+        $this->query = $query;
+        $this->uploadedFiles = $uploadedFiles;
+        $this->data = $data;
+        $this->attributes = $attributes;
+    }
+
+    public function getRequestTarget()
+    {
+        return $this->requestTarget;
+    }
+
+    public function withRequestTarget($requestTarget)
+    {
+        throw new \BadMethodCallException('Not implemented.');
+    }
+
+    public function getMethod()
+    {
+        return $this->method;
+    }
+
+    public function withMethod($method)
+    {
+    }
+
+    public function getUri()
+    {
+        return $this->uri;
+    }
+
+    public function withUri(UriInterface $uri, $preserveHost = false)
+    {
+        throw new \BadMethodCallException('Not implemented.');
+    }
+
+    public function getServerParams()
+    {
+        return $this->server;
+    }
+
+    public function getCookieParams()
+    {
+        return $this->cookies;
+    }
+
+    public function withCookieParams(array $cookies)
+    {
+        throw new \BadMethodCallException('Not implemented.');
+    }
+
+    public function getQueryParams()
+    {
+        return $this->query;
+    }
+
+    public function withQueryParams(array $query)
+    {
+        throw new \BadMethodCallException('Not implemented.');
+    }
+
+    public function getUploadedFiles()
+    {
+        return $this->uploadedFiles;
+    }
+
+    public function withUploadedFiles(array $uploadedFiles)
+    {
+        throw new \BadMethodCallException('Not implemented.');
+    }
+
+    public function getParsedBody()
+    {
+        return $this->data;
+    }
+
+    public function withParsedBody($data)
+    {
+        throw new \BadMethodCallException('Not implemented.');
+    }
+
+    public function getAttributes()
+    {
+        return $this->attributes;
+    }
+
+    public function getAttribute($name, $default = null)
+    {
+        return isset($this->attributes[$name]) ? $this->attributes[$name] : $default;
+    }
+
+    public function withAttribute($name, $value)
+    {
+        throw new \BadMethodCallException('Not implemented.');
+    }
+
+    public function withoutAttribute($name)
+    {
+        throw new \BadMethodCallException('Not implemented.');
+    }
+}
diff --git a/vendor/symfony/psr-http-message-bridge/Tests/Fixtures/Stream.php b/vendor/symfony/psr-http-message-bridge/Tests/Fixtures/Stream.php
new file mode 100755
index 0000000..aa0ba24
--- /dev/null
+++ b/vendor/symfony/psr-http-message-bridge/Tests/Fixtures/Stream.php
@@ -0,0 +1,99 @@
+<?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\Bridge\PsrHttpMessage\Tests\Fixtures;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * @author Kévin Dunglas <dunglas@gmail.com>
+ */
+class Stream implements StreamInterface
+{
+    private $stringContent;
+    private $eof = true;
+
+    public function __construct($stringContent = '')
+    {
+        $this->stringContent = $stringContent;
+    }
+
+    public function __toString()
+    {
+        return $this->stringContent;
+    }
+
+    public function close()
+    {
+    }
+
+    public function detach()
+    {
+    }
+
+    public function getSize()
+    {
+    }
+
+    public function tell()
+    {
+        return 0;
+    }
+
+    public function eof()
+    {
+        return $this->eof;
+    }
+
+    public function isSeekable()
+    {
+        return true;
+    }
+
+    public function seek($offset, $whence = SEEK_SET)
+    {
+    }
+
+    public function rewind()
+    {
+        $this->eof = false;
+    }
+
+    public function isWritable()
+    {
+        return false;
+    }
+
+    public function write($string)
+    {
+    }
+
+    public function isReadable()
+    {
+        return true;
+    }
+
+    public function read($length)
+    {
+        $this->eof = true;
+
+        return $this->stringContent;
+    }
+
+    public function getContents()
+    {
+        return $this->stringContent;
+    }
+
+    public function getMetadata($key = null)
+    {
+    }
+}
diff --git a/vendor/symfony/psr-http-message-bridge/Tests/Fixtures/UploadedFile.php b/vendor/symfony/psr-http-message-bridge/Tests/Fixtures/UploadedFile.php
new file mode 100755
index 0000000..4cfa98b
--- /dev/null
+++ b/vendor/symfony/psr-http-message-bridge/Tests/Fixtures/UploadedFile.php
@@ -0,0 +1,65 @@
+<?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\Bridge\PsrHttpMessage\Tests\Fixtures;
+
+use Psr\Http\Message\UploadedFileInterface;
+
+/**
+ * @author Kévin Dunglas <dunglas@gmail.com>
+ */
+class UploadedFile implements UploadedFileInterface
+{
+    private $filePath;
+    private $size;
+    private $error;
+    private $clientFileName;
+    private $clientMediaType;
+
+    public function __construct($filePath, $size = null, $error = UPLOAD_ERR_OK, $clientFileName = null, $clientMediaType = null)
+    {
+        $this->filePath = $filePath;
+        $this->size = $size;
+        $this->error = $error;
+        $this->clientFileName = $clientFileName;
+        $this->clientMediaType = $clientMediaType;
+    }
+
+    public function getStream()
+    {
+        throw new \RuntimeException('No stream is available.');
+    }
+
+    public function moveTo($targetPath)
+    {
+        rename($this->filePath, $targetPath);
+    }
+
+    public function getSize()
+    {
+        return $this->size;
+    }
+
+    public function getError()
+    {
+        return $this->error;
+    }
+
+    public function getClientFilename()
+    {
+        return $this->clientFileName;
+    }
+
+    public function getClientMediaType()
+    {
+        return $this->clientMediaType;
+    }
+}
diff --git a/vendor/symfony/psr-http-message-bridge/Tests/Fixtures/Uri.php b/vendor/symfony/psr-http-message-bridge/Tests/Fixtures/Uri.php
new file mode 100755
index 0000000..f11c7e5
--- /dev/null
+++ b/vendor/symfony/psr-http-message-bridge/Tests/Fixtures/Uri.php
@@ -0,0 +1,135 @@
+<?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\Bridge\PsrHttpMessage\Tests\Fixtures;
+
+use Psr\Http\Message\UriInterface;
+
+/**
+ * @author Rougin Royce Gutib <rougingutib@gmail.com>
+ */
+class Uri implements UriInterface
+{
+    private $scheme = '';
+    private $userInfo = '';
+    private $host = '';
+    private $port;
+    private $path = '';
+    private $query = '';
+    private $fragment = '';
+    private $uriString;
+
+    public function __construct($uri = '')
+    {
+        $parts = parse_url($uri);
+
+        $this->scheme = isset($parts['scheme']) ? $parts['scheme'] : '';
+        $this->userInfo = isset($parts['user']) ? $parts['user'] : '';
+        $this->host = isset($parts['host']) ? $parts['host'] : '';
+        $this->port = isset($parts['port']) ? $parts['port'] : null;
+        $this->path = isset($parts['path']) ? $parts['path'] : '';
+        $this->query = isset($parts['query']) ? $parts['query'] : '';
+        $this->fragment = isset($parts['fragment']) ? $parts['fragment'] : '';
+        $this->uriString = $uri;
+    }
+
+    public function getScheme()
+    {
+        return $this->scheme;
+    }
+
+    public function getAuthority()
+    {
+        if (empty($this->host)) {
+            return '';
+        }
+
+        $authority = $this->host;
+
+        if (!empty($this->userInfo)) {
+            $authority = $this->userInfo.'@'.$authority;
+        }
+
+        $authority .= ':'.$this->port;
+
+        return $authority;
+    }
+
+    public function getUserInfo()
+    {
+        return $this->userInfo;
+    }
+
+    public function getHost()
+    {
+        return $this->host;
+    }
+
+    public function getPort()
+    {
+        return $this->port;
+    }
+
+    public function getPath()
+    {
+        return $this->path;
+    }
+
+    public function getQuery()
+    {
+        return $this->query;
+    }
+
+    public function getFragment()
+    {
+        return $this->fragment;
+    }
+
+    public function withScheme($scheme)
+    {
+        throw new \BadMethodCallException('Not implemented.');
+    }
+
+    public function withUserInfo($user, $password = null)
+    {
+        throw new \BadMethodCallException('Not implemented.');
+    }
+
+    public function withHost($host)
+    {
+        throw new \BadMethodCallException('Not implemented.');
+    }
+
+    public function withPort($port)
+    {
+        throw new \BadMethodCallException('Not implemented.');
+    }
+
+    public function withPath($path)
+    {
+        throw new \BadMethodCallException('Not implemented.');
+    }
+
+    public function withQuery($query)
+    {
+        throw new \BadMethodCallException('Not implemented.');
+    }
+
+    public function withFragment($fragment)
+    {
+        throw new \BadMethodCallException('Not implemented.');
+    }
+
+    public function __toString()
+    {
+        return $this->uriString;
+    }
+}
diff --git a/vendor/symfony/psr-http-message-bridge/Tests/Functional/CovertTest.php b/vendor/symfony/psr-http-message-bridge/Tests/Functional/CovertTest.php
new file mode 100755
index 0000000..33ec34f
--- /dev/null
+++ b/vendor/symfony/psr-http-message-bridge/Tests/Functional/CovertTest.php
@@ -0,0 +1,239 @@
+<?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\Bridge\PsrHttpMessage\Tests\Functional;
+
+use Nyholm\Psr7\Factory\Psr17Factory;
+use Nyholm\Psr7\Response as Psr7Response;
+use Nyholm\Psr7\ServerRequest as Psr7Request;
+use Nyholm\Psr7\Stream as Psr7Stream;
+use PHPUnit\Framework\TestCase;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
+use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory;
+use Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface;
+use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface;
+use Symfony\Component\HttpFoundation\Cookie;
+use Symfony\Component\HttpFoundation\File\UploadedFile;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * Test to convert a request/response back and forth to make sure we do not loose data.
+ *
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ */
+class CovertTest extends TestCase
+{
+    private $tmpDir;
+
+    public function setup()
+    {
+        if (!class_exists('Nyholm\Psr7\ServerRequest')) {
+            $this->markTestSkipped('nyholm/psr7 is not installed.');
+        }
+
+        $this->tmpDir = sys_get_temp_dir();
+    }
+
+    /**
+     * @dataProvider requestProvider
+     *
+     * @param Request|ServerRequestInterface                             $request
+     * @param HttpFoundationFactoryInterface|HttpMessageFactoryInterface $firstFactory
+     * @param HttpFoundationFactoryInterface|HttpMessageFactoryInterface $secondFactory
+     */
+    public function testConvertRequestMultipleTimes($request, $firstFactory, $secondFactory)
+    {
+        $temporaryRequest = $firstFactory->createRequest($request);
+        $finalRequest = $secondFactory->createRequest($temporaryRequest);
+
+        if ($finalRequest instanceof Request) {
+            $this->assertEquals($request->getBasePath(), $finalRequest->getBasePath());
+            $this->assertEquals($request->getBaseUrl(), $finalRequest->getBaseUrl());
+            $this->assertEquals($request->getContent(), $finalRequest->getContent());
+            $this->assertEquals($request->getEncodings(), $finalRequest->getEncodings());
+            $this->assertEquals($request->getETags(), $finalRequest->getETags());
+            $this->assertEquals($request->getHost(), $finalRequest->getHost());
+            $this->assertEquals($request->getHttpHost(), $finalRequest->getHttpHost());
+            $this->assertEquals($request->getMethod(), $finalRequest->getMethod());
+            $this->assertEquals($request->getPassword(), $finalRequest->getPassword());
+            $this->assertEquals($request->getPathInfo(), $finalRequest->getPathInfo());
+            $this->assertEquals($request->getPort(), $finalRequest->getPort());
+            $this->assertEquals($request->getProtocolVersion(), $finalRequest->getProtocolVersion());
+            $this->assertEquals($request->getQueryString(), $finalRequest->getQueryString());
+            $this->assertEquals($request->getRequestUri(), $finalRequest->getRequestUri());
+            $this->assertEquals($request->getScheme(), $finalRequest->getScheme());
+            $this->assertEquals($request->getSchemeAndHttpHost(), $finalRequest->getSchemeAndHttpHost());
+            $this->assertEquals($request->getScriptName(), $finalRequest->getScriptName());
+            $this->assertEquals($request->getUri(), $finalRequest->getUri());
+            $this->assertEquals($request->getUser(), $finalRequest->getUser());
+            $this->assertEquals($request->getUserInfo(), $finalRequest->getUserInfo());
+        } elseif ($finalRequest instanceof ServerRequestInterface) {
+            $strToLower = function ($arr) {
+                foreach ($arr as $key => $value) {
+                    yield strtolower($key) => $value;
+                }
+            };
+            $this->assertEquals($request->getAttributes(), $finalRequest->getAttributes());
+            $this->assertEquals($request->getCookieParams(), $finalRequest->getCookieParams());
+            $this->assertEquals((array) $request->getParsedBody(), (array) $finalRequest->getParsedBody());
+            $this->assertEquals($request->getQueryParams(), $finalRequest->getQueryParams());
+            // PSR7 does not define a "withServerParams" so this is impossible to implement without knowing the PSR7 implementation.
+            //$this->assertEquals($request->getServerParams(), $finalRequest->getServerParams());
+            $this->assertEquals($request->getUploadedFiles(), $finalRequest->getUploadedFiles());
+            $this->assertEquals($request->getMethod(), $finalRequest->getMethod());
+            $this->assertEquals($request->getRequestTarget(), $finalRequest->getRequestTarget());
+            $this->assertEquals((string) $request->getUri(), (string) $finalRequest->getUri());
+            $this->assertEquals((string) $request->getBody(), (string) $finalRequest->getBody());
+            $this->assertEquals($strToLower($request->getHeaders()), $strToLower($finalRequest->getHeaders()));
+            $this->assertEquals($request->getProtocolVersion(), $finalRequest->getProtocolVersion());
+        } else {
+            $this->fail('$finalRequest must be an instance of PSR7 or a HTTPFoundation request');
+        }
+    }
+
+    public function requestProvider()
+    {
+        $sfRequest = new Request(
+            [
+                'foo' => '1',
+                'bar' => ['baz' => '42'],
+            ],
+            [
+                'twitter' => [
+                    '@dunglas' => 'Kévin Dunglas',
+                    '@coopTilleuls' => 'Les-Tilleuls.coop',
+                ],
+                'baz' => '2',
+            ],
+            [
+                'a2' => ['foo' => 'bar'],
+            ],
+            [
+                'c1' => 'foo',
+                'c2' => ['c3' => 'bar'],
+            ],
+            [
+                'f1' => $this->createUploadedFile('F1', 'f1.txt', 'text/plain', UPLOAD_ERR_OK),
+                'foo' => ['f2' => $this->createUploadedFile('F2', 'f2.txt', 'text/plain', UPLOAD_ERR_OK)],
+            ],
+            [
+                'REQUEST_METHOD' => 'POST',
+                'HTTP_HOST' => 'dunglas.fr',
+                'SERVER_NAME' => 'dunglas.fr',
+                'SERVER_PORT' => null,
+                'HTTP_X_SYMFONY' => '2.8',
+                'REQUEST_URI' => '/testCreateRequest?bar[baz]=42&foo=1',
+                'QUERY_STRING' => 'foo=1&bar[baz]=42',
+            ],
+            'Content'
+        );
+
+        $psr7Request = (new Psr7Request('POST', 'http://tnyholm.se/foo/?bar=biz'))
+            ->withQueryParams(['bar' => 'biz']);
+
+        $nyholmFactory = new Psr17Factory();
+        $psr17Factory = new PsrHttpFactory($nyholmFactory, $nyholmFactory, $nyholmFactory, $nyholmFactory);
+        $symfonyFactory = new HttpFoundationFactory();
+
+        return [
+            [$sfRequest, $psr17Factory, $symfonyFactory],
+            [$psr7Request, $symfonyFactory, $psr17Factory],
+        ];
+    }
+
+    /**
+     * @dataProvider responseProvider
+     *
+     * @param Response|ResponseInterface                                 $response
+     * @param HttpFoundationFactoryInterface|HttpMessageFactoryInterface $firstFactory
+     * @param HttpFoundationFactoryInterface|HttpMessageFactoryInterface $secondFactory
+     */
+    public function testConvertResponseMultipleTimes($response, $firstFactory, $secondFactory)
+    {
+        $temporaryResponse = $firstFactory->createResponse($response);
+        $finalResponse = $secondFactory->createResponse($temporaryResponse);
+
+        if ($finalResponse instanceof Response) {
+            $this->assertEquals($response->getAge(), $finalResponse->getAge());
+            $this->assertEquals($response->getCharset(), $finalResponse->getCharset());
+            $this->assertEquals($response->getContent(), $finalResponse->getContent());
+            $this->assertEquals($response->getDate(), $finalResponse->getDate());
+            $this->assertEquals($response->getEtag(), $finalResponse->getEtag());
+            $this->assertEquals($response->getExpires(), $finalResponse->getExpires());
+            $this->assertEquals($response->getLastModified(), $finalResponse->getLastModified());
+            $this->assertEquals($response->getMaxAge(), $finalResponse->getMaxAge());
+            $this->assertEquals($response->getProtocolVersion(), $finalResponse->getProtocolVersion());
+            $this->assertEquals($response->getStatusCode(), $finalResponse->getStatusCode());
+            $this->assertEquals($response->getTtl(), $finalResponse->getTtl());
+        } elseif ($finalResponse instanceof ResponseInterface) {
+            $strToLower = function ($arr) {
+                foreach ($arr as $key => $value) {
+                    yield strtolower($key) => $value;
+                }
+            };
+            $this->assertEquals($response->getStatusCode(), $finalResponse->getStatusCode());
+            $this->assertEquals($response->getReasonPhrase(), $finalResponse->getReasonPhrase());
+            $this->assertEquals((string) $response->getBody(), (string) $finalResponse->getBody());
+            $this->assertEquals($strToLower($response->getHeaders()), $strToLower($finalResponse->getHeaders()));
+            $this->assertEquals($response->getProtocolVersion(), $finalResponse->getProtocolVersion());
+        } else {
+            $this->fail('$finalResponse must be an instance of PSR7 or a HTTPFoundation response');
+        }
+    }
+
+    public function responseProvider()
+    {
+        $sfResponse = new Response(
+            'Response content.',
+            202,
+            ['x-symfony' => ['3.4']]
+        );
+
+        if (method_exists(Cookie::class, 'create')) {
+            $cookie = Cookie::create('city', 'Lille', new \DateTime('Wed, 13 Jan 2021 22:23:01 GMT'));
+        } else {
+            $cookie = new Cookie('city', 'Lille', new \DateTime('Wed, 13 Jan 2021 22:23:01 GMT'));
+        }
+
+        $sfResponse->headers->setCookie($cookie);
+        $body = Psr7Stream::create();
+        $status = 302;
+        $headers = [
+            'location' => ['http://example.com/'],
+        ];
+        $zendResponse = new Psr7Response($status, $headers, $body);
+
+        $nyholmFactory = new Psr17Factory();
+        $psr17Factory = new PsrHttpFactory($nyholmFactory, $nyholmFactory, $nyholmFactory, $nyholmFactory);
+        $symfonyFactory = new HttpFoundationFactory();
+
+        return [
+            [$sfResponse, $psr17Factory, $symfonyFactory],
+            [$zendResponse, $symfonyFactory, $psr17Factory],
+        ];
+    }
+
+    private function createUploadedFile($content, $originalName, $mimeType, $error)
+    {
+        $path = tempnam($this->tmpDir, uniqid());
+        file_put_contents($path, $content);
+
+        if (class_exists('Symfony\Component\HttpFoundation\HeaderUtils')) {
+            // Symfony 4.1+
+            return new UploadedFile($path, $originalName, $mimeType, $error, true);
+        }
+
+        return new UploadedFile($path, $originalName, $mimeType, filesize($path), $error, true);
+    }
+}
diff --git a/vendor/symfony/psr-http-message-bridge/composer.json b/vendor/symfony/psr-http-message-bridge/composer.json
new file mode 100755
index 0000000..51bb655
--- /dev/null
+++ b/vendor/symfony/psr-http-message-bridge/composer.json
@@ -0,0 +1,42 @@
+{
+    "name": "symfony/psr-http-message-bridge",
+    "type": "symfony-bridge",
+    "description": "PSR HTTP message bridge",
+    "keywords": ["http", "psr-7", "psr-17", "http-message"],
+    "homepage": "http://symfony.com",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Fabien Potencier",
+            "email": "fabien@symfony.com"
+        },
+        {
+            "name": "Symfony Community",
+            "homepage": "http://symfony.com/contributors"
+        }
+    ],
+    "require": {
+        "php": "^7.1",
+        "psr/http-message": "^1.0",
+        "symfony/http-foundation": "^3.4 || ^4.0"
+    },
+    "require-dev": {
+        "symfony/phpunit-bridge": "^3.4.20 || ^4.0",
+        "nyholm/psr7": "^1.1",
+        "zendframework/zend-diactoros": "^1.4.1 || ^2.0"
+    },
+    "suggest": {
+        "nyholm/psr7": "For a super lightweight PSR-7/17 implementation"
+    },
+    "autoload": {
+        "psr-4": { "Symfony\\Bridge\\PsrHttpMessage\\": "" },
+        "exclude-from-classmap": [
+            "/Tests/"
+        ]
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.2-dev"
+        }
+    }
+}
diff --git a/vendor/symfony/psr-http-message-bridge/phpunit.xml.dist b/vendor/symfony/psr-http-message-bridge/phpunit.xml.dist
new file mode 100755
index 0000000..9a6e477
--- /dev/null
+++ b/vendor/symfony/psr-http-message-bridge/phpunit.xml.dist
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit backupGlobals="false"
+         backupStaticAttributes="false"
+         colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         processIsolation="false"
+         stopOnFailure="false"
+         syntaxCheck="false"
+         bootstrap="vendor/autoload.php"
+>
+    <testsuites>
+        <testsuite name="Symfony PSR-7 HTTP message Bridge Test Suite">
+            <directory>./Tests/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./</directory>
+            <exclude>
+                <directory>./Resources</directory>
+                <directory>./Tests</directory>
+                <directory>./vendor</directory>
+            </exclude>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/vendor/symfony/translation-contracts/LICENSE b/vendor/symfony/translation-contracts/LICENSE
new file mode 100755
index 0000000..3f853aa
--- /dev/null
+++ b/vendor/symfony/translation-contracts/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2018-2019 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.
diff --git a/vendor/symfony/translation-contracts/LocaleAwareInterface.php b/vendor/symfony/translation-contracts/LocaleAwareInterface.php
new file mode 100755
index 0000000..dbd8894
--- /dev/null
+++ b/vendor/symfony/translation-contracts/LocaleAwareInterface.php
@@ -0,0 +1,31 @@
+<?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\Contracts\Translation;
+
+interface LocaleAwareInterface
+{
+    /**
+     * Sets the current locale.
+     *
+     * @param string $locale The locale
+     *
+     * @throws \InvalidArgumentException If the locale contains invalid characters
+     */
+    public function setLocale($locale);
+
+    /**
+     * Returns the current locale.
+     *
+     * @return string The locale
+     */
+    public function getLocale();
+}
diff --git a/vendor/symfony/translation-contracts/README.md b/vendor/symfony/translation-contracts/README.md
new file mode 100755
index 0000000..6c693ce
--- /dev/null
+++ b/vendor/symfony/translation-contracts/README.md
@@ -0,0 +1,9 @@
+Symfony Translation Contracts
+=============================
+
+A set of abstractions extracted out of the Symfony components.
+
+Can be used to build on semantics that the Symfony components proved useful - and
+that already have battle tested implementations.
+
+See https://github.com/symfony/contracts/blob/master/README.md for more information.
diff --git a/vendor/symfony/translation-contracts/Test/TranslatorTest.php b/vendor/symfony/translation-contracts/Test/TranslatorTest.php
new file mode 100755
index 0000000..4846630
--- /dev/null
+++ b/vendor/symfony/translation-contracts/Test/TranslatorTest.php
@@ -0,0 +1,353 @@
+<?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\Contracts\Translation\Test;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Contracts\Translation\TranslatorInterface;
+use Symfony\Contracts\Translation\TranslatorTrait;
+
+/**
+ * Test should cover all languages mentioned on http://translate.sourceforge.net/wiki/l10n/pluralforms
+ * and Plural forms mentioned on http://www.gnu.org/software/gettext/manual/gettext.html#Plural-forms.
+ *
+ * See also https://developer.mozilla.org/en/Localization_and_Plurals which mentions 15 rules having a maximum of 6 forms.
+ * The mozilla code is also interesting to check for.
+ *
+ * As mentioned by chx http://drupal.org/node/1273968 we can cover all by testing number from 0 to 199
+ *
+ * The goal to cover all languages is to far fetched so this test case is smaller.
+ *
+ * @author Clemens Tolboom clemens@build2be.nl
+ */
+class TranslatorTest extends TestCase
+{
+    public function getTranslator()
+    {
+        return new class() implements TranslatorInterface {
+            use TranslatorTrait;
+        };
+    }
+
+    /**
+     * @dataProvider getTransTests
+     */
+    public function testTrans($expected, $id, $parameters)
+    {
+        $translator = $this->getTranslator();
+
+        $this->assertEquals($expected, $translator->trans($id, $parameters));
+    }
+
+    /**
+     * @dataProvider getTransChoiceTests
+     */
+    public function testTransChoiceWithExplicitLocale($expected, $id, $number)
+    {
+        $translator = $this->getTranslator();
+        $translator->setLocale('en');
+
+        $this->assertEquals($expected, $translator->trans($id, ['%count%' => $number]));
+    }
+
+    /**
+     * @dataProvider getTransChoiceTests
+     */
+    public function testTransChoiceWithDefaultLocale($expected, $id, $number)
+    {
+        \Locale::setDefault('en');
+
+        $translator = $this->getTranslator();
+
+        $this->assertEquals($expected, $translator->trans($id, ['%count%' => $number]));
+    }
+
+    public function testGetSetLocale()
+    {
+        $translator = $this->getTranslator();
+        $translator->setLocale('en');
+
+        $this->assertEquals('en', $translator->getLocale());
+    }
+
+    /**
+     * @requires extension intl
+     */
+    public function testGetLocaleReturnsDefaultLocaleIfNotSet()
+    {
+        $translator = $this->getTranslator();
+
+        \Locale::setDefault('pt_BR');
+        $this->assertEquals('pt_BR', $translator->getLocale());
+
+        \Locale::setDefault('en');
+        $this->assertEquals('en', $translator->getLocale());
+    }
+
+    public function getTransTests()
+    {
+        return [
+            ['Symfony is great!', 'Symfony is great!', []],
+            ['Symfony is awesome!', 'Symfony is %what%!', ['%what%' => 'awesome']],
+        ];
+    }
+
+    public function getTransChoiceTests()
+    {
+        return [
+            ['There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0],
+            ['There is one apple', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 1],
+            ['There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10],
+            ['There are 0 apples', 'There is 1 apple|There are %count% apples', 0],
+            ['There is 1 apple', 'There is 1 apple|There are %count% apples', 1],
+            ['There are 10 apples', 'There is 1 apple|There are %count% apples', 10],
+            // custom validation messages may be coded with a fixed value
+            ['There are 2 apples', 'There are 2 apples', 2],
+        ];
+    }
+
+    /**
+     * @dataProvider getInternal
+     */
+    public function testInterval($expected, $number, $interval)
+    {
+        $translator = $this->getTranslator();
+
+        $this->assertEquals($expected, $translator->trans($interval.' foo|[1,Inf[ bar', ['%count%' => $number]));
+    }
+
+    public function getInternal()
+    {
+        return [
+            ['foo', 3, '{1,2, 3 ,4}'],
+            ['bar', 10, '{1,2, 3 ,4}'],
+            ['bar', 3, '[1,2]'],
+            ['foo', 1, '[1,2]'],
+            ['foo', 2, '[1,2]'],
+            ['bar', 1, ']1,2['],
+            ['bar', 2, ']1,2['],
+            ['foo', log(0), '[-Inf,2['],
+            ['foo', -log(0), '[-2,+Inf]'],
+        ];
+    }
+
+    /**
+     * @dataProvider getChooseTests
+     */
+    public function testChoose($expected, $id, $number)
+    {
+        $translator = $this->getTranslator();
+
+        $this->assertEquals($expected, $translator->trans($id, ['%count%' => $number]));
+    }
+
+    public function testReturnMessageIfExactlyOneStandardRuleIsGiven()
+    {
+        $translator = $this->getTranslator();
+
+        $this->assertEquals('There are two apples', $translator->trans('There are two apples', ['%count%' => 2]));
+    }
+
+    /**
+     * @dataProvider getNonMatchingMessages
+     * @expectedException \InvalidArgumentException
+     */
+    public function testThrowExceptionIfMatchingMessageCannotBeFound($id, $number)
+    {
+        $translator = $this->getTranslator();
+
+        $translator->trans($id, ['%count%' => $number]);
+    }
+
+    public function getNonMatchingMessages()
+    {
+        return [
+            ['{0} There are no apples|{1} There is one apple', 2],
+            ['{1} There is one apple|]1,Inf] There are %count% apples', 0],
+            ['{1} There is one apple|]2,Inf] There are %count% apples', 2],
+            ['{0} There are no apples|There is one apple', 2],
+        ];
+    }
+
+    public function getChooseTests()
+    {
+        return [
+            ['There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0],
+            ['There are no apples', '{0}     There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0],
+            ['There are no apples', '{0}There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0],
+
+            ['There is one apple', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 1],
+
+            ['There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10],
+            ['There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf]There are %count% apples', 10],
+            ['There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf]     There are %count% apples', 10],
+
+            ['There are 0 apples', 'There is one apple|There are %count% apples', 0],
+            ['There is one apple', 'There is one apple|There are %count% apples', 1],
+            ['There are 10 apples', 'There is one apple|There are %count% apples', 10],
+
+            ['There are 0 apples', 'one: There is one apple|more: There are %count% apples', 0],
+            ['There is one apple', 'one: There is one apple|more: There are %count% apples', 1],
+            ['There are 10 apples', 'one: There is one apple|more: There are %count% apples', 10],
+
+            ['There are no apples', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 0],
+            ['There is one apple', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 1],
+            ['There are 10 apples', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 10],
+
+            ['', '{0}|{1} There is one apple|]1,Inf] There are %count% apples', 0],
+            ['', '{0} There are no apples|{1}|]1,Inf] There are %count% apples', 1],
+
+            // Indexed only tests which are Gettext PoFile* compatible strings.
+            ['There are 0 apples', 'There is one apple|There are %count% apples', 0],
+            ['There is one apple', 'There is one apple|There are %count% apples', 1],
+            ['There are 2 apples', 'There is one apple|There are %count% apples', 2],
+
+            // Tests for float numbers
+            ['There is almost one apple', '{0} There are no apples|]0,1[ There is almost one apple|{1} There is one apple|[1,Inf] There is more than one apple', 0.7],
+            ['There is one apple', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 1],
+            ['There is more than one apple', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 1.7],
+            ['There are no apples', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0],
+            ['There are no apples', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0.0],
+            ['There are no apples', '{0.0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0],
+
+            // Test texts with new-lines
+            // with double-quotes and \n in id & double-quotes and actual newlines in text
+            ["This is a text with a\n            new-line in it. Selector = 0.", '{0}This is a text with a
+            new-line in it. Selector = 0.|{1}This is a text with a
+            new-line in it. Selector = 1.|[1,Inf]This is a text with a
+            new-line in it. Selector > 1.', 0],
+            // with double-quotes and \n in id and single-quotes and actual newlines in text
+            ["This is a text with a\n            new-line in it. Selector = 1.", '{0}This is a text with a
+            new-line in it. Selector = 0.|{1}This is a text with a
+            new-line in it. Selector = 1.|[1,Inf]This is a text with a
+            new-line in it. Selector > 1.', 1],
+            ["This is a text with a\n            new-line in it. Selector > 1.", '{0}This is a text with a
+            new-line in it. Selector = 0.|{1}This is a text with a
+            new-line in it. Selector = 1.|[1,Inf]This is a text with a
+            new-line in it. Selector > 1.', 5],
+            // with double-quotes and id split accros lines
+            ['This is a text with a
+            new-line in it. Selector = 1.', '{0}This is a text with a
+            new-line in it. Selector = 0.|{1}This is a text with a
+            new-line in it. Selector = 1.|[1,Inf]This is a text with a
+            new-line in it. Selector > 1.', 1],
+            // with single-quotes and id split accros lines
+            ['This is a text with a
+            new-line in it. Selector > 1.', '{0}This is a text with a
+            new-line in it. Selector = 0.|{1}This is a text with a
+            new-line in it. Selector = 1.|[1,Inf]This is a text with a
+            new-line in it. Selector > 1.', 5],
+            // with single-quotes and \n in text
+            ['This is a text with a\nnew-line in it. Selector = 0.', '{0}This is a text with a\nnew-line in it. Selector = 0.|{1}This is a text with a\nnew-line in it. Selector = 1.|[1,Inf]This is a text with a\nnew-line in it. Selector > 1.', 0],
+            // with double-quotes and id split accros lines
+            ["This is a text with a\nnew-line in it. Selector = 1.", "{0}This is a text with a\nnew-line in it. Selector = 0.|{1}This is a text with a\nnew-line in it. Selector = 1.|[1,Inf]This is a text with a\nnew-line in it. Selector > 1.", 1],
+            // esacape pipe
+            ['This is a text with | in it. Selector = 0.', '{0}This is a text with || in it. Selector = 0.|{1}This is a text with || in it. Selector = 1.', 0],
+            // Empty plural set (2 plural forms) from a .PO file
+            ['', '|', 1],
+            // Empty plural set (3 plural forms) from a .PO file
+            ['', '||', 1],
+        ];
+    }
+
+    /**
+     * @dataProvider failingLangcodes
+     */
+    public function testFailedLangcodes($nplural, $langCodes)
+    {
+        $matrix = $this->generateTestData($langCodes);
+        $this->validateMatrix($nplural, $matrix, false);
+    }
+
+    /**
+     * @dataProvider successLangcodes
+     */
+    public function testLangcodes($nplural, $langCodes)
+    {
+        $matrix = $this->generateTestData($langCodes);
+        $this->validateMatrix($nplural, $matrix);
+    }
+
+    /**
+     * This array should contain all currently known langcodes.
+     *
+     * As it is impossible to have this ever complete we should try as hard as possible to have it almost complete.
+     *
+     * @return array
+     */
+    public function successLangcodes()
+    {
+        return [
+            ['1', ['ay', 'bo', 'cgg', 'dz', 'id', 'ja', 'jbo', 'ka', 'kk', 'km', 'ko', 'ky']],
+            ['2', ['nl', 'fr', 'en', 'de', 'de_GE', 'hy', 'hy_AM']],
+            ['3', ['be', 'bs', 'cs', 'hr']],
+            ['4', ['cy', 'mt', 'sl']],
+            ['6', ['ar']],
+        ];
+    }
+
+    /**
+     * This array should be at least empty within the near future.
+     *
+     * This both depends on a complete list trying to add above as understanding
+     * the plural rules of the current failing languages.
+     *
+     * @return array with nplural together with langcodes
+     */
+    public function failingLangcodes()
+    {
+        return [
+            ['1', ['fa']],
+            ['2', ['jbo']],
+            ['3', ['cbs']],
+            ['4', ['gd', 'kw']],
+            ['5', ['ga']],
+        ];
+    }
+
+    /**
+     * We validate only on the plural coverage. Thus the real rules is not tested.
+     *
+     * @param string $nplural       Plural expected
+     * @param array  $matrix        Containing langcodes and their plural index values
+     * @param bool   $expectSuccess
+     */
+    protected function validateMatrix($nplural, $matrix, $expectSuccess = true)
+    {
+        foreach ($matrix as $langCode => $data) {
+            $indexes = array_flip($data);
+            if ($expectSuccess) {
+                $this->assertEquals($nplural, \count($indexes), "Langcode '$langCode' has '$nplural' plural forms.");
+            } else {
+                $this->assertNotEquals((int) $nplural, \count($indexes), "Langcode '$langCode' has '$nplural' plural forms.");
+            }
+        }
+    }
+
+    protected function generateTestData($langCodes)
+    {
+        $translator = new class() {
+            use TranslatorTrait {
+                getPluralizationRule as public;
+            }
+        };
+
+        $matrix = [];
+        foreach ($langCodes as $langCode) {
+            for ($count = 0; $count < 200; ++$count) {
+                $plural = $translator->getPluralizationRule($count, $langCode);
+                $matrix[$langCode][$count] = $plural;
+            }
+        }
+
+        return $matrix;
+    }
+}
diff --git a/vendor/symfony/translation-contracts/TranslatorInterface.php b/vendor/symfony/translation-contracts/TranslatorInterface.php
new file mode 100755
index 0000000..d867637
--- /dev/null
+++ b/vendor/symfony/translation-contracts/TranslatorInterface.php
@@ -0,0 +1,65 @@
+<?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\Contracts\Translation;
+
+/**
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface TranslatorInterface
+{
+    /**
+     * Translates the given message.
+     *
+     * When a number is provided as a parameter named "%count%", the message is parsed for plural
+     * forms and a translation is chosen according to this number using the following rules:
+     *
+     * Given a message with different plural translations separated by a
+     * pipe (|), this method returns the correct portion of the message based
+     * on the given number, locale and the pluralization rules in the message
+     * itself.
+     *
+     * The message supports two different types of pluralization rules:
+     *
+     * interval: {0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples
+     * indexed:  There is one apple|There are %count% apples
+     *
+     * The indexed solution can also contain labels (e.g. one: There is one apple).
+     * This is purely for making the translations more clear - it does not
+     * affect the functionality.
+     *
+     * The two methods can also be mixed:
+     *     {0} There are no apples|one: There is one apple|more: There are %count% apples
+     *
+     * An interval can represent a finite set of numbers:
+     *  {1,2,3,4}
+     *
+     * An interval can represent numbers between two numbers:
+     *  [1, +Inf]
+     *  ]-1,2[
+     *
+     * The left delimiter can be [ (inclusive) or ] (exclusive).
+     * The right delimiter can be [ (exclusive) or ] (inclusive).
+     * Beside numbers, you can use -Inf and +Inf for the infinite.
+     *
+     * @see https://en.wikipedia.org/wiki/ISO_31-11
+     *
+     * @param string      $id         The message id (may also be an object that can be cast to string)
+     * @param array       $parameters An array of parameters for the message
+     * @param string|null $domain     The domain for the message or null to use the default
+     * @param string|null $locale     The locale or null to use the default
+     *
+     * @return string The translated string
+     *
+     * @throws \InvalidArgumentException If the locale contains invalid characters
+     */
+    public function trans($id, array $parameters = [], $domain = null, $locale = null);
+}
diff --git a/vendor/symfony/translation-contracts/TranslatorTrait.php b/vendor/symfony/translation-contracts/TranslatorTrait.php
new file mode 100755
index 0000000..488b4f4
--- /dev/null
+++ b/vendor/symfony/translation-contracts/TranslatorTrait.php
@@ -0,0 +1,255 @@
+<?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\Contracts\Translation;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+
+/**
+ * A trait to help implement TranslatorInterface and LocaleAwareInterface.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+trait TranslatorTrait
+{
+    private $locale;
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setLocale($locale)
+    {
+        $this->locale = (string) $locale;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getLocale()
+    {
+        return $this->locale ?: \Locale::getDefault();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function trans($id, array $parameters = [], $domain = null, $locale = null)
+    {
+        $id = (string) $id;
+
+        if (!isset($parameters['%count%']) || !is_numeric($parameters['%count%'])) {
+            return strtr($id, $parameters);
+        }
+
+        $number = (float) $parameters['%count%'];
+        $locale = (string) $locale ?: $this->getLocale();
+
+        $parts = [];
+        if (preg_match('/^\|++$/', $id)) {
+            $parts = explode('|', $id);
+        } elseif (preg_match_all('/(?:\|\||[^\|])++/', $id, $matches)) {
+            $parts = $matches[0];
+        }
+
+        $intervalRegexp = <<<'EOF'
+/^(?P<interval>
+    ({\s*
+        (\-?\d+(\.\d+)?[\s*,\s*\-?\d+(\.\d+)?]*)
+    \s*})
+
+        |
+
+    (?P<left_delimiter>[\[\]])
+        \s*
+        (?P<left>-Inf|\-?\d+(\.\d+)?)
+        \s*,\s*
+        (?P<right>\+?Inf|\-?\d+(\.\d+)?)
+        \s*
+    (?P<right_delimiter>[\[\]])
+)\s*(?P<message>.*?)$/xs
+EOF;
+
+        $standardRules = [];
+        foreach ($parts as $part) {
+            $part = trim(str_replace('||', '|', $part));
+
+            // try to match an explicit rule, then fallback to the standard ones
+            if (preg_match($intervalRegexp, $part, $matches)) {
+                if ($matches[2]) {
+                    foreach (explode(',', $matches[3]) as $n) {
+                        if ($number == $n) {
+                            return strtr($matches['message'], $parameters);
+                        }
+                    }
+                } else {
+                    $leftNumber = '-Inf' === $matches['left'] ? -INF : (float) $matches['left'];
+                    $rightNumber = is_numeric($matches['right']) ? (float) $matches['right'] : INF;
+
+                    if (('[' === $matches['left_delimiter'] ? $number >= $leftNumber : $number > $leftNumber)
+                        && (']' === $matches['right_delimiter'] ? $number <= $rightNumber : $number < $rightNumber)
+                    ) {
+                        return strtr($matches['message'], $parameters);
+                    }
+                }
+            } elseif (preg_match('/^\w+\:\s*(.*?)$/', $part, $matches)) {
+                $standardRules[] = $matches[1];
+            } else {
+                $standardRules[] = $part;
+            }
+        }
+
+        $position = $this->getPluralizationRule($number, $locale);
+
+        if (!isset($standardRules[$position])) {
+            // when there's exactly one rule given, and that rule is a standard
+            // rule, use this rule
+            if (1 === \count($parts) && isset($standardRules[0])) {
+                return strtr($standardRules[0], $parameters);
+            }
+
+            $message = sprintf('Unable to choose a translation for "%s" with locale "%s" for value "%d". Double check that this translation has the correct plural options (e.g. "There is one apple|There are %%count%% apples").', $id, $locale, $number);
+
+            if (class_exists(InvalidArgumentException::class)) {
+                throw new InvalidArgumentException($message);
+            }
+
+            throw new \InvalidArgumentException($message);
+        }
+
+        return strtr($standardRules[$position], $parameters);
+    }
+
+    /**
+     * Returns the plural position to use for the given locale and number.
+     *
+     * The plural rules are derived from code of the Zend Framework (2010-09-25),
+     * which is subject to the new BSD license (http://framework.zend.com/license/new-bsd).
+     * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+     */
+    private function getPluralizationRule(int $number, string $locale): int
+    {
+        switch ('pt_BR' !== $locale && \strlen($locale) > 3 ? substr($locale, 0, strrpos($locale, '_')) : $locale) {
+            case 'af':
+            case 'bn':
+            case 'bg':
+            case 'ca':
+            case 'da':
+            case 'de':
+            case 'el':
+            case 'en':
+            case 'eo':
+            case 'es':
+            case 'et':
+            case 'eu':
+            case 'fa':
+            case 'fi':
+            case 'fo':
+            case 'fur':
+            case 'fy':
+            case 'gl':
+            case 'gu':
+            case 'ha':
+            case 'he':
+            case 'hu':
+            case 'is':
+            case 'it':
+            case 'ku':
+            case 'lb':
+            case 'ml':
+            case 'mn':
+            case 'mr':
+            case 'nah':
+            case 'nb':
+            case 'ne':
+            case 'nl':
+            case 'nn':
+            case 'no':
+            case 'oc':
+            case 'om':
+            case 'or':
+            case 'pa':
+            case 'pap':
+            case 'ps':
+            case 'pt':
+            case 'so':
+            case 'sq':
+            case 'sv':
+            case 'sw':
+            case 'ta':
+            case 'te':
+            case 'tk':
+            case 'ur':
+            case 'zu':
+                return (1 == $number) ? 0 : 1;
+
+            case 'am':
+            case 'bh':
+            case 'fil':
+            case 'fr':
+            case 'gun':
+            case 'hi':
+            case 'hy':
+            case 'ln':
+            case 'mg':
+            case 'nso':
+            case 'pt_BR':
+            case 'ti':
+            case 'wa':
+                return ((0 == $number) || (1 == $number)) ? 0 : 1;
+
+            case 'be':
+            case 'bs':
+            case 'hr':
+            case 'ru':
+            case 'sh':
+            case 'sr':
+            case 'uk':
+                return ((1 == $number % 10) && (11 != $number % 100)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
+
+            case 'cs':
+            case 'sk':
+                return (1 == $number) ? 0 : ((($number >= 2) && ($number <= 4)) ? 1 : 2);
+
+            case 'ga':
+                return (1 == $number) ? 0 : ((2 == $number) ? 1 : 2);
+
+            case 'lt':
+                return ((1 == $number % 10) && (11 != $number % 100)) ? 0 : ((($number % 10 >= 2) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
+
+            case 'sl':
+                return (1 == $number % 100) ? 0 : ((2 == $number % 100) ? 1 : (((3 == $number % 100) || (4 == $number % 100)) ? 2 : 3));
+
+            case 'mk':
+                return (1 == $number % 10) ? 0 : 1;
+
+            case 'mt':
+                return (1 == $number) ? 0 : (((0 == $number) || (($number % 100 > 1) && ($number % 100 < 11))) ? 1 : ((($number % 100 > 10) && ($number % 100 < 20)) ? 2 : 3));
+
+            case 'lv':
+                return (0 == $number) ? 0 : (((1 == $number % 10) && (11 != $number % 100)) ? 1 : 2);
+
+            case 'pl':
+                return (1 == $number) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 12) || ($number % 100 > 14))) ? 1 : 2);
+
+            case 'cy':
+                return (1 == $number) ? 0 : ((2 == $number) ? 1 : (((8 == $number) || (11 == $number)) ? 2 : 3));
+
+            case 'ro':
+                return (1 == $number) ? 0 : (((0 == $number) || (($number % 100 > 0) && ($number % 100 < 20))) ? 1 : 2);
+
+            case 'ar':
+                return (0 == $number) ? 0 : ((1 == $number) ? 1 : ((2 == $number) ? 2 : ((($number % 100 >= 3) && ($number % 100 <= 10)) ? 3 : ((($number % 100 >= 11) && ($number % 100 <= 99)) ? 4 : 5))));
+
+            default:
+                return 0;
+        }
+    }
+}
diff --git a/vendor/symfony/translation-contracts/composer.json b/vendor/symfony/translation-contracts/composer.json
new file mode 100755
index 0000000..09749d3
--- /dev/null
+++ b/vendor/symfony/translation-contracts/composer.json
@@ -0,0 +1,33 @@
+{
+    "name": "symfony/translation-contracts",
+    "type": "library",
+    "description": "Generic abstractions related to translation",
+    "keywords": ["abstractions", "contracts", "decoupling", "interfaces", "interoperability", "standards"],
+    "homepage": "https://symfony.com",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Nicolas Grekas",
+            "email": "p@tchwork.com"
+        },
+        {
+            "name": "Symfony Community",
+            "homepage": "https://symfony.com/contributors"
+        }
+    ],
+    "require": {
+        "php": "^7.1.3"
+    },
+    "suggest": {
+        "symfony/translation-implementation": ""
+    },
+    "autoload": {
+        "psr-4": { "Symfony\\Contracts\\Translation\\": "" }
+    },
+    "minimum-stability": "dev",
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.1-dev"
+        }
+    }
+}
diff --git a/vendor/symfony/translation/.gitignore b/vendor/symfony/translation/.gitignore
new file mode 100755
index 0000000..c49a5d8
--- /dev/null
+++ b/vendor/symfony/translation/.gitignore
@@ -0,0 +1,3 @@
+vendor/
+composer.lock
+phpunit.xml
diff --git a/vendor/symfony/translation/CHANGELOG.md b/vendor/symfony/translation/CHANGELOG.md
new file mode 100755
index 0000000..c807168
--- /dev/null
+++ b/vendor/symfony/translation/CHANGELOG.md
@@ -0,0 +1,126 @@
+CHANGELOG
+=========
+
+4.3.0
+-----
+
+ * Improved Xliff 1.2 loader to load the original file's metadata
+ * Added `TranslatorPathsPass`
+
+4.2.0
+-----
+
+ * Started using ICU parent locales as fallback locales.
+ * allow using the ICU message format using domains with the "+intl-icu" suffix
+ * deprecated `Translator::transChoice()` in favor of using `Translator::trans()` with a `%count%` parameter
+ * deprecated `TranslatorInterface` in favor of `Symfony\Contracts\Translation\TranslatorInterface`
+ * deprecated `MessageSelector`, `Interval` and `PluralizationRules`; use `IdentityTranslator` instead
+ * Added `IntlFormatter` and `IntlFormatterInterface`
+ * added support for multiple files and directories in `XliffLintCommand`
+ * Marked `Translator::getFallbackLocales()` and `TranslationDataCollector::getFallbackLocales()` as internal
+
+4.1.0
+-----
+
+ * The `FileDumper::setBackup()` method is deprecated.
+ * The `TranslationWriter::disableBackup()` method is deprecated.
+ * The `XliffFileDumper` will write "name" on the "unit" node when dumping XLIFF 2.0.
+
+4.0.0
+-----
+
+ * removed the backup feature of the `FileDumper` class
+ * removed `TranslationWriter::writeTranslations()` method
+ * removed support for passing `MessageSelector` instances to the constructor of the `Translator` class
+
+3.4.0
+-----
+
+ * Added `TranslationDumperPass`
+ * Added `TranslationExtractorPass`
+ * Added `TranslatorPass`
+ * Added `TranslationReader` and `TranslationReaderInterface`
+ * Added `<notes>` section to the Xliff 2.0 dumper.
+ * Improved Xliff 2.0 loader to load `<notes>` section.
+ * Added `TranslationWriterInterface`
+ * Deprecated `TranslationWriter::writeTranslations` in favor of `TranslationWriter::write`
+ * added support for adding custom message formatter and decoupling the default one.
+ * Added `PhpExtractor`
+ * Added `PhpStringTokenParser`
+
+3.2.0
+-----
+
+ * Added support for escaping `|` in plural translations with double pipe.
+
+3.1.0
+-----
+
+ * Deprecated the backup feature of the file dumper classes.
+
+3.0.0
+-----
+
+ * removed `FileDumper::format()` method.
+ * Changed the visibility of the locale property in `Translator` from protected to private.
+
+2.8.0
+-----
+
+ * deprecated FileDumper::format(), overwrite FileDumper::formatCatalogue() instead.
+ * deprecated Translator::getMessages(), rely on TranslatorBagInterface::getCatalogue() instead.
+ * added `FileDumper::formatCatalogue` which allows format the catalogue without dumping it into file.
+ * added option `json_encoding` to JsonFileDumper
+ * added options `as_tree`, `inline` to YamlFileDumper
+ * added support for XLIFF 2.0.
+ * added support for XLIFF target and tool attributes.
+ * added message parameters to DataCollectorTranslator.
+ * [DEPRECATION] The `DiffOperation` class has been deprecated and
+   will be removed in Symfony 3.0, since its operation has nothing to do with 'diff',
+   so the class name is misleading. The `TargetOperation` class should be used for
+   this use-case instead.
+
+2.7.0
+-----
+
+ * added DataCollectorTranslator for collecting the translated messages.
+
+2.6.0
+-----
+
+ * added possibility to cache catalogues
+ * added TranslatorBagInterface
+ * added LoggingTranslator
+ * added Translator::getMessages() for retrieving the message catalogue as an array
+
+2.5.0
+-----
+
+ * added relative file path template to the file dumpers
+ * added optional backup to the file dumpers
+ * changed IcuResFileDumper to extend FileDumper
+
+2.3.0
+-----
+
+ * added classes to make operations on catalogues (like making a diff or a merge on 2 catalogues)
+ * added Translator::getFallbackLocales()
+ * deprecated Translator::setFallbackLocale() in favor of the new Translator::setFallbackLocales() method
+
+2.2.0
+-----
+
+ * QtTranslationsLoader class renamed to QtFileLoader. QtTranslationsLoader is deprecated and will be removed in 2.3.
+ * [BC BREAK] uniformized the exception thrown by the load() method when an error occurs. The load() method now
+   throws Symfony\Component\Translation\Exception\NotFoundResourceException when a resource cannot be found
+   and Symfony\Component\Translation\Exception\InvalidResourceException when a resource is invalid.
+ * changed the exception class thrown by some load() methods from \RuntimeException to \InvalidArgumentException
+   (IcuDatFileLoader, IcuResFileLoader and QtFileLoader)
+
+2.1.0
+-----
+
+ * added support for more than one fallback locale
+ * added support for extracting translation messages from templates (Twig and PHP)
+ * added dumpers for translation catalogs
+ * added support for QT, gettext, and ResourceBundles
diff --git a/vendor/symfony/translation/Catalogue/AbstractOperation.php b/vendor/symfony/translation/Catalogue/AbstractOperation.php
new file mode 100755
index 0000000..919bab8
--- /dev/null
+++ b/vendor/symfony/translation/Catalogue/AbstractOperation.php
@@ -0,0 +1,157 @@
+<?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\Translation\Catalogue;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+use Symfony\Component\Translation\Exception\LogicException;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\MessageCatalogueInterface;
+
+/**
+ * Base catalogues binary operation class.
+ *
+ * A catalogue binary operation performs operation on
+ * source (the left argument) and target (the right argument) catalogues.
+ *
+ * @author Jean-François Simon <contact@jfsimon.fr>
+ */
+abstract class AbstractOperation implements OperationInterface
+{
+    protected $source;
+    protected $target;
+    protected $result;
+
+    /**
+     * @var array|null The domains affected by this operation
+     */
+    private $domains;
+
+    /**
+     * This array stores 'all', 'new' and 'obsolete' messages for all valid domains.
+     *
+     * The data structure of this array is as follows:
+     *
+     *     [
+     *         'domain 1' => [
+     *             'all' => [...],
+     *             'new' => [...],
+     *             'obsolete' => [...]
+     *         ],
+     *         'domain 2' => [
+     *             'all' => [...],
+     *             'new' => [...],
+     *             'obsolete' => [...]
+     *         ],
+     *         ...
+     *     ]
+     *
+     * @var array The array that stores 'all', 'new' and 'obsolete' messages
+     */
+    protected $messages;
+
+    /**
+     * @throws LogicException
+     */
+    public function __construct(MessageCatalogueInterface $source, MessageCatalogueInterface $target)
+    {
+        if ($source->getLocale() !== $target->getLocale()) {
+            throw new LogicException('Operated catalogues must belong to the same locale.');
+        }
+
+        $this->source = $source;
+        $this->target = $target;
+        $this->result = new MessageCatalogue($source->getLocale());
+        $this->messages = [];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDomains()
+    {
+        if (null === $this->domains) {
+            $this->domains = array_values(array_unique(array_merge($this->source->getDomains(), $this->target->getDomains())));
+        }
+
+        return $this->domains;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getMessages($domain)
+    {
+        if (!\in_array($domain, $this->getDomains())) {
+            throw new InvalidArgumentException(sprintf('Invalid domain: %s.', $domain));
+        }
+
+        if (!isset($this->messages[$domain]['all'])) {
+            $this->processDomain($domain);
+        }
+
+        return $this->messages[$domain]['all'];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getNewMessages($domain)
+    {
+        if (!\in_array($domain, $this->getDomains())) {
+            throw new InvalidArgumentException(sprintf('Invalid domain: %s.', $domain));
+        }
+
+        if (!isset($this->messages[$domain]['new'])) {
+            $this->processDomain($domain);
+        }
+
+        return $this->messages[$domain]['new'];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getObsoleteMessages($domain)
+    {
+        if (!\in_array($domain, $this->getDomains())) {
+            throw new InvalidArgumentException(sprintf('Invalid domain: %s.', $domain));
+        }
+
+        if (!isset($this->messages[$domain]['obsolete'])) {
+            $this->processDomain($domain);
+        }
+
+        return $this->messages[$domain]['obsolete'];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getResult()
+    {
+        foreach ($this->getDomains() as $domain) {
+            if (!isset($this->messages[$domain])) {
+                $this->processDomain($domain);
+            }
+        }
+
+        return $this->result;
+    }
+
+    /**
+     * Performs operation on source and target catalogues for the given domain and
+     * stores the results.
+     *
+     * @param string $domain The domain which the operation will be performed for
+     */
+    abstract protected function processDomain($domain);
+}
diff --git a/vendor/symfony/translation/Catalogue/MergeOperation.php b/vendor/symfony/translation/Catalogue/MergeOperation.php
new file mode 100755
index 0000000..d2f4abd
--- /dev/null
+++ b/vendor/symfony/translation/Catalogue/MergeOperation.php
@@ -0,0 +1,58 @@
+<?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\Translation\Catalogue;
+
+use Symfony\Component\Translation\MessageCatalogueInterface;
+
+/**
+ * Merge operation between two catalogues as follows:
+ * all = source ∪ target = {x: x ∈ source ∨ x ∈ target}
+ * new = all ∖ source = {x: x ∈ target ∧ x ∉ source}
+ * obsolete = source ∖ all = {x: x ∈ source ∧ x ∉ source ∧ x ∉ target} = ∅
+ * Basically, the result contains messages from both catalogues.
+ *
+ * @author Jean-François Simon <contact@jfsimon.fr>
+ */
+class MergeOperation extends AbstractOperation
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function processDomain($domain)
+    {
+        $this->messages[$domain] = [
+            'all' => [],
+            'new' => [],
+            'obsolete' => [],
+        ];
+        $intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX;
+
+        foreach ($this->source->all($domain) as $id => $message) {
+            $this->messages[$domain]['all'][$id] = $message;
+            $this->result->add([$id => $message], $this->source->defines($id, $intlDomain) ? $intlDomain : $domain);
+            if (null !== $keyMetadata = $this->source->getMetadata($id, $domain)) {
+                $this->result->setMetadata($id, $keyMetadata, $domain);
+            }
+        }
+
+        foreach ($this->target->all($domain) as $id => $message) {
+            if (!$this->source->has($id, $domain)) {
+                $this->messages[$domain]['all'][$id] = $message;
+                $this->messages[$domain]['new'][$id] = $message;
+                $this->result->add([$id => $message], $this->target->defines($id, $intlDomain) ? $intlDomain : $domain);
+                if (null !== $keyMetadata = $this->target->getMetadata($id, $domain)) {
+                    $this->result->setMetadata($id, $keyMetadata, $domain);
+                }
+            }
+        }
+    }
+}
diff --git a/vendor/symfony/translation/Catalogue/OperationInterface.php b/vendor/symfony/translation/Catalogue/OperationInterface.php
new file mode 100755
index 0000000..87d888e
--- /dev/null
+++ b/vendor/symfony/translation/Catalogue/OperationInterface.php
@@ -0,0 +1,77 @@
+<?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\Translation\Catalogue;
+
+use Symfony\Component\Translation\MessageCatalogueInterface;
+
+/**
+ * Represents an operation on catalogue(s).
+ *
+ * An instance of this interface performs an operation on one or more catalogues and
+ * stores intermediate and final results of the operation.
+ *
+ * The first catalogue in its argument(s) is called the 'source catalogue' or 'source' and
+ * the following results are stored:
+ *
+ * Messages: also called 'all', are valid messages for the given domain after the operation is performed.
+ *
+ * New Messages: also called 'new' (new = all ∖ source = {x: x ∈ all ∧ x ∉ source}).
+ *
+ * Obsolete Messages: also called 'obsolete' (obsolete = source ∖ all = {x: x ∈ source ∧ x ∉ all}).
+ *
+ * Result: also called 'result', is the resulting catalogue for the given domain that holds the same messages as 'all'.
+ *
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
+ */
+interface OperationInterface
+{
+    /**
+     * Returns domains affected by operation.
+     *
+     * @return array
+     */
+    public function getDomains();
+
+    /**
+     * Returns all valid messages ('all') after operation.
+     *
+     * @param string $domain
+     *
+     * @return array
+     */
+    public function getMessages($domain);
+
+    /**
+     * Returns new messages ('new') after operation.
+     *
+     * @param string $domain
+     *
+     * @return array
+     */
+    public function getNewMessages($domain);
+
+    /**
+     * Returns obsolete messages ('obsolete') after operation.
+     *
+     * @param string $domain
+     *
+     * @return array
+     */
+    public function getObsoleteMessages($domain);
+
+    /**
+     * Returns resulting catalogue ('result').
+     *
+     * @return MessageCatalogueInterface
+     */
+    public function getResult();
+}
diff --git a/vendor/symfony/translation/Catalogue/TargetOperation.php b/vendor/symfony/translation/Catalogue/TargetOperation.php
new file mode 100755
index 0000000..22aa9a3
--- /dev/null
+++ b/vendor/symfony/translation/Catalogue/TargetOperation.php
@@ -0,0 +1,72 @@
+<?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\Translation\Catalogue;
+
+use Symfony\Component\Translation\MessageCatalogueInterface;
+
+/**
+ * Target operation between two catalogues:
+ * intersection = source ∩ target = {x: x ∈ source ∧ x ∈ target}
+ * all = intersection ∪ (target ∖ intersection) = target
+ * new = all ∖ source = {x: x ∈ target ∧ x ∉ source}
+ * obsolete = source ∖ all = source ∖ target = {x: x ∈ source ∧ x ∉ target}
+ * Basically, the result contains messages from the target catalogue.
+ *
+ * @author Michael Lee <michael.lee@zerustech.com>
+ */
+class TargetOperation extends AbstractOperation
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function processDomain($domain)
+    {
+        $this->messages[$domain] = [
+            'all' => [],
+            'new' => [],
+            'obsolete' => [],
+        ];
+        $intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX;
+
+        // For 'all' messages, the code can't be simplified as ``$this->messages[$domain]['all'] = $target->all($domain);``,
+        // because doing so will drop messages like {x: x ∈ source ∧ x ∉ target.all ∧ x ∈ target.fallback}
+        //
+        // For 'new' messages, the code can't be simplified as ``array_diff_assoc($this->target->all($domain), $this->source->all($domain));``
+        // because doing so will not exclude messages like {x: x ∈ target ∧ x ∉ source.all ∧ x ∈ source.fallback}
+        //
+        // For 'obsolete' messages, the code can't be simplified as ``array_diff_assoc($this->source->all($domain), $this->target->all($domain))``
+        // because doing so will not exclude messages like {x: x ∈ source ∧ x ∉ target.all ∧ x ∈ target.fallback}
+
+        foreach ($this->source->all($domain) as $id => $message) {
+            if ($this->target->has($id, $domain)) {
+                $this->messages[$domain]['all'][$id] = $message;
+                $this->result->add([$id => $message], $this->target->defines($id, $intlDomain) ? $intlDomain : $domain);
+                if (null !== $keyMetadata = $this->source->getMetadata($id, $domain)) {
+                    $this->result->setMetadata($id, $keyMetadata, $domain);
+                }
+            } else {
+                $this->messages[$domain]['obsolete'][$id] = $message;
+            }
+        }
+
+        foreach ($this->target->all($domain) as $id => $message) {
+            if (!$this->source->has($id, $domain)) {
+                $this->messages[$domain]['all'][$id] = $message;
+                $this->messages[$domain]['new'][$id] = $message;
+                $this->result->add([$id => $message], $this->target->defines($id, $intlDomain) ? $intlDomain : $domain);
+                if (null !== $keyMetadata = $this->target->getMetadata($id, $domain)) {
+                    $this->result->setMetadata($id, $keyMetadata, $domain);
+                }
+            }
+        }
+    }
+}
diff --git a/vendor/symfony/translation/Command/XliffLintCommand.php b/vendor/symfony/translation/Command/XliffLintCommand.php
new file mode 100755
index 0000000..3c2cc9e
--- /dev/null
+++ b/vendor/symfony/translation/Command/XliffLintCommand.php
@@ -0,0 +1,279 @@
+<?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\Translation\Command;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Exception\RuntimeException;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+use Symfony\Component\Translation\Util\XliffUtils;
+
+/**
+ * Validates XLIFF files syntax and outputs encountered errors.
+ *
+ * @author Grégoire Pineau <lyrixx@lyrixx.info>
+ * @author Robin Chalas <robin.chalas@gmail.com>
+ * @author Javier Eguiluz <javier.eguiluz@gmail.com>
+ */
+class XliffLintCommand extends Command
+{
+    protected static $defaultName = 'lint:xliff';
+
+    private $format;
+    private $displayCorrectFiles;
+    private $directoryIteratorProvider;
+    private $isReadableProvider;
+    private $requireStrictFileNames;
+
+    public function __construct(string $name = null, callable $directoryIteratorProvider = null, callable $isReadableProvider = null, bool $requireStrictFileNames = true)
+    {
+        parent::__construct($name);
+
+        $this->directoryIteratorProvider = $directoryIteratorProvider;
+        $this->isReadableProvider = $isReadableProvider;
+        $this->requireStrictFileNames = $requireStrictFileNames;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function configure()
+    {
+        $this
+            ->setDescription('Lints a XLIFF file and outputs encountered errors')
+            ->addArgument('filename', InputArgument::IS_ARRAY, 'A file or a directory or STDIN')
+            ->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt')
+            ->setHelp(<<<EOF
+The <info>%command.name%</info> command lints a XLIFF file and outputs to STDOUT
+the first encountered syntax error.
+
+You can validates XLIFF contents passed from STDIN:
+
+  <info>cat filename | php %command.full_name%</info>
+
+You can also validate the syntax of a file:
+
+  <info>php %command.full_name% filename</info>
+
+Or of a whole directory:
+
+  <info>php %command.full_name% dirname</info>
+  <info>php %command.full_name% dirname --format=json</info>
+
+EOF
+            )
+        ;
+    }
+
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        $io = new SymfonyStyle($input, $output);
+        $filenames = (array) $input->getArgument('filename');
+        $this->format = $input->getOption('format');
+        $this->displayCorrectFiles = $output->isVerbose();
+
+        if (0 === \count($filenames)) {
+            if (!$stdin = $this->getStdin()) {
+                throw new RuntimeException('Please provide a filename or pipe file content to STDIN.');
+            }
+
+            return $this->display($io, [$this->validate($stdin)]);
+        }
+
+        $filesInfo = [];
+        foreach ($filenames as $filename) {
+            if (!$this->isReadable($filename)) {
+                throw new RuntimeException(sprintf('File or directory "%s" is not readable.', $filename));
+            }
+
+            foreach ($this->getFiles($filename) as $file) {
+                $filesInfo[] = $this->validate(file_get_contents($file), $file);
+            }
+        }
+
+        return $this->display($io, $filesInfo);
+    }
+
+    private function validate($content, $file = null)
+    {
+        $errors = [];
+
+        // Avoid: Warning DOMDocument::loadXML(): Empty string supplied as input
+        if ('' === trim($content)) {
+            return ['file' => $file, 'valid' => true];
+        }
+
+        $internal = libxml_use_internal_errors(true);
+
+        $document = new \DOMDocument();
+        $document->loadXML($content);
+
+        if (null !== $targetLanguage = $this->getTargetLanguageFromFile($document)) {
+            $normalizedLocale = preg_quote(str_replace('-', '_', $targetLanguage), '/');
+            // strict file names require translation files to be named '____.locale.xlf'
+            // otherwise, both '____.locale.xlf' and 'locale.____.xlf' are allowed
+            // also, the regexp matching must be case-insensitive, as defined for 'target-language' values
+            // http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#target-language
+            $expectedFilenamePattern = $this->requireStrictFileNames ? sprintf('/^.*\.(?i:%s)\.xlf/', $normalizedLocale) : sprintf('/^(.*\.(?i:%s)\.xlf|(?i:%s)\..*\.xlf)/', $normalizedLocale, $normalizedLocale);
+
+            if (0 === preg_match($expectedFilenamePattern, basename($file))) {
+                $errors[] = [
+                    'line' => -1,
+                    'column' => -1,
+                    'message' => sprintf('There is a mismatch between the language included in the file name ("%s") and the "%s" value used in the "target-language" attribute of the file.', basename($file), $targetLanguage),
+                ];
+            }
+        }
+
+        foreach (XliffUtils::validateSchema($document) as $xmlError) {
+            $errors[] = [
+                'line' => $xmlError['line'],
+                'column' => $xmlError['column'],
+                'message' => $xmlError['message'],
+            ];
+        }
+
+        libxml_clear_errors();
+        libxml_use_internal_errors($internal);
+
+        return ['file' => $file, 'valid' => 0 === \count($errors), 'messages' => $errors];
+    }
+
+    private function display(SymfonyStyle $io, array $files)
+    {
+        switch ($this->format) {
+            case 'txt':
+                return $this->displayTxt($io, $files);
+            case 'json':
+                return $this->displayJson($io, $files);
+            default:
+                throw new InvalidArgumentException(sprintf('The format "%s" is not supported.', $this->format));
+        }
+    }
+
+    private function displayTxt(SymfonyStyle $io, array $filesInfo)
+    {
+        $countFiles = \count($filesInfo);
+        $erroredFiles = 0;
+
+        foreach ($filesInfo as $info) {
+            if ($info['valid'] && $this->displayCorrectFiles) {
+                $io->comment('<info>OK</info>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
+            } elseif (!$info['valid']) {
+                ++$erroredFiles;
+                $io->text('<error> ERROR </error>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
+                $io->listing(array_map(function ($error) {
+                    // general document errors have a '-1' line number
+                    return -1 === $error['line'] ? $error['message'] : sprintf('Line %d, Column %d: %s', $error['line'], $error['column'], $error['message']);
+                }, $info['messages']));
+            }
+        }
+
+        if (0 === $erroredFiles) {
+            $io->success(sprintf('All %d XLIFF files contain valid syntax.', $countFiles));
+        } else {
+            $io->warning(sprintf('%d XLIFF files have valid syntax and %d contain errors.', $countFiles - $erroredFiles, $erroredFiles));
+        }
+
+        return min($erroredFiles, 1);
+    }
+
+    private function displayJson(SymfonyStyle $io, array $filesInfo)
+    {
+        $errors = 0;
+
+        array_walk($filesInfo, function (&$v) use (&$errors) {
+            $v['file'] = (string) $v['file'];
+            if (!$v['valid']) {
+                ++$errors;
+            }
+        });
+
+        $io->writeln(json_encode($filesInfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
+
+        return min($errors, 1);
+    }
+
+    private function getFiles($fileOrDirectory)
+    {
+        if (is_file($fileOrDirectory)) {
+            yield new \SplFileInfo($fileOrDirectory);
+
+            return;
+        }
+
+        foreach ($this->getDirectoryIterator($fileOrDirectory) as $file) {
+            if (!\in_array($file->getExtension(), ['xlf', 'xliff'])) {
+                continue;
+            }
+
+            yield $file;
+        }
+    }
+
+    private function getStdin()
+    {
+        if (0 !== ftell(STDIN)) {
+            return;
+        }
+
+        $inputs = '';
+        while (!feof(STDIN)) {
+            $inputs .= fread(STDIN, 1024);
+        }
+
+        return $inputs;
+    }
+
+    private function getDirectoryIterator($directory)
+    {
+        $default = function ($directory) {
+            return new \RecursiveIteratorIterator(
+                new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS),
+                \RecursiveIteratorIterator::LEAVES_ONLY
+            );
+        };
+
+        if (null !== $this->directoryIteratorProvider) {
+            return ($this->directoryIteratorProvider)($directory, $default);
+        }
+
+        return $default($directory);
+    }
+
+    private function isReadable($fileOrDirectory)
+    {
+        $default = function ($fileOrDirectory) {
+            return is_readable($fileOrDirectory);
+        };
+
+        if (null !== $this->isReadableProvider) {
+            return ($this->isReadableProvider)($fileOrDirectory, $default);
+        }
+
+        return $default($fileOrDirectory);
+    }
+
+    private function getTargetLanguageFromFile(\DOMDocument $xliffContents): ?string
+    {
+        foreach ($xliffContents->getElementsByTagName('file')[0]->attributes ?? [] as $attribute) {
+            if ('target-language' === $attribute->nodeName) {
+                return $attribute->nodeValue;
+            }
+        }
+
+        return null;
+    }
+}
diff --git a/vendor/symfony/translation/DataCollector/TranslationDataCollector.php b/vendor/symfony/translation/DataCollector/TranslationDataCollector.php
new file mode 100755
index 0000000..35dfc0e
--- /dev/null
+++ b/vendor/symfony/translation/DataCollector/TranslationDataCollector.php
@@ -0,0 +1,169 @@
+<?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\Translation\DataCollector;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\DataCollector\DataCollector;
+use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
+use Symfony\Component\Translation\DataCollectorTranslator;
+
+/**
+ * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
+ */
+class TranslationDataCollector extends DataCollector implements LateDataCollectorInterface
+{
+    private $translator;
+
+    public function __construct(DataCollectorTranslator $translator)
+    {
+        $this->translator = $translator;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function lateCollect()
+    {
+        $messages = $this->sanitizeCollectedMessages($this->translator->getCollectedMessages());
+
+        $this->data += $this->computeCount($messages);
+        $this->data['messages'] = $messages;
+
+        $this->data = $this->cloneVar($this->data);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function collect(Request $request, Response $response, \Exception $exception = null)
+    {
+        $this->data['locale'] = $this->translator->getLocale();
+        $this->data['fallback_locales'] = $this->translator->getFallbackLocales();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function reset()
+    {
+        $this->data = [];
+    }
+
+    /**
+     * @return array
+     */
+    public function getMessages()
+    {
+        return isset($this->data['messages']) ? $this->data['messages'] : [];
+    }
+
+    /**
+     * @return int
+     */
+    public function getCountMissings()
+    {
+        return isset($this->data[DataCollectorTranslator::MESSAGE_MISSING]) ? $this->data[DataCollectorTranslator::MESSAGE_MISSING] : 0;
+    }
+
+    /**
+     * @return int
+     */
+    public function getCountFallbacks()
+    {
+        return isset($this->data[DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK]) ? $this->data[DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK] : 0;
+    }
+
+    /**
+     * @return int
+     */
+    public function getCountDefines()
+    {
+        return isset($this->data[DataCollectorTranslator::MESSAGE_DEFINED]) ? $this->data[DataCollectorTranslator::MESSAGE_DEFINED] : 0;
+    }
+
+    public function getLocale()
+    {
+        return !empty($this->data['locale']) ? $this->data['locale'] : null;
+    }
+
+    /**
+     * @internal since Symfony 4.2
+     */
+    public function getFallbackLocales()
+    {
+        return (isset($this->data['fallback_locales']) && \count($this->data['fallback_locales']) > 0) ? $this->data['fallback_locales'] : [];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getName()
+    {
+        return 'translation';
+    }
+
+    private function sanitizeCollectedMessages($messages)
+    {
+        $result = [];
+        foreach ($messages as $key => $message) {
+            $messageId = $message['locale'].$message['domain'].$message['id'];
+
+            if (!isset($result[$messageId])) {
+                $message['count'] = 1;
+                $message['parameters'] = !empty($message['parameters']) ? [$message['parameters']] : [];
+                $messages[$key]['translation'] = $this->sanitizeString($message['translation']);
+                $result[$messageId] = $message;
+            } else {
+                if (!empty($message['parameters'])) {
+                    $result[$messageId]['parameters'][] = $message['parameters'];
+                }
+
+                ++$result[$messageId]['count'];
+            }
+
+            unset($messages[$key]);
+        }
+
+        return $result;
+    }
+
+    private function computeCount($messages)
+    {
+        $count = [
+            DataCollectorTranslator::MESSAGE_DEFINED => 0,
+            DataCollectorTranslator::MESSAGE_MISSING => 0,
+            DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK => 0,
+        ];
+
+        foreach ($messages as $message) {
+            ++$count[$message['state']];
+        }
+
+        return $count;
+    }
+
+    private function sanitizeString($string, $length = 80)
+    {
+        $string = trim(preg_replace('/\s+/', ' ', $string));
+
+        if (false !== $encoding = mb_detect_encoding($string, null, true)) {
+            if (mb_strlen($string, $encoding) > $length) {
+                return mb_substr($string, 0, $length - 3, $encoding).'...';
+            }
+        } elseif (\strlen($string) > $length) {
+            return substr($string, 0, $length - 3).'...';
+        }
+
+        return $string;
+    }
+}
diff --git a/vendor/symfony/translation/DataCollectorTranslator.php b/vendor/symfony/translation/DataCollectorTranslator.php
new file mode 100755
index 0000000..0284b77
--- /dev/null
+++ b/vendor/symfony/translation/DataCollectorTranslator.php
@@ -0,0 +1,188 @@
+<?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\Translation;
+
+use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface;
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface;
+use Symfony\Contracts\Translation\LocaleAwareInterface;
+use Symfony\Contracts\Translation\TranslatorInterface;
+
+/**
+ * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
+ */
+class DataCollectorTranslator implements LegacyTranslatorInterface, TranslatorInterface, TranslatorBagInterface, WarmableInterface
+{
+    const MESSAGE_DEFINED = 0;
+    const MESSAGE_MISSING = 1;
+    const MESSAGE_EQUALS_FALLBACK = 2;
+
+    /**
+     * @var TranslatorInterface|TranslatorBagInterface
+     */
+    private $translator;
+
+    private $messages = [];
+
+    /**
+     * @param TranslatorInterface $translator The translator must implement TranslatorBagInterface
+     */
+    public function __construct($translator)
+    {
+        if (!$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) {
+            throw new \TypeError(sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator)));
+        }
+        if (!$translator instanceof TranslatorBagInterface || !$translator instanceof LocaleAwareInterface) {
+            throw new InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface, TranslatorBagInterface and LocaleAwareInterface.', \get_class($translator)));
+        }
+
+        $this->translator = $translator;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function trans($id, array $parameters = [], $domain = null, $locale = null)
+    {
+        $trans = $this->translator->trans($id, $parameters, $domain, $locale);
+        $this->collectMessage($locale, $domain, $id, $trans, $parameters);
+
+        return $trans;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @deprecated since Symfony 4.2, use the trans() method instead with a %count% parameter
+     */
+    public function transChoice($id, $number, array $parameters = [], $domain = null, $locale = null)
+    {
+        if ($this->translator instanceof TranslatorInterface) {
+            $trans = $this->translator->trans($id, ['%count%' => $number] + $parameters, $domain, $locale);
+        } else {
+            $trans = $this->translator->transChoice($id, $number, $parameters, $domain, $locale);
+        }
+
+        $this->collectMessage($locale, $domain, $id, $trans, ['%count%' => $number] + $parameters);
+
+        return $trans;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setLocale($locale)
+    {
+        $this->translator->setLocale($locale);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getLocale()
+    {
+        return $this->translator->getLocale();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getCatalogue($locale = null)
+    {
+        return $this->translator->getCatalogue($locale);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function warmUp($cacheDir)
+    {
+        if ($this->translator instanceof WarmableInterface) {
+            $this->translator->warmUp($cacheDir);
+        }
+    }
+
+    /**
+     * Gets the fallback locales.
+     *
+     * @return array The fallback locales
+     */
+    public function getFallbackLocales()
+    {
+        if ($this->translator instanceof Translator || method_exists($this->translator, 'getFallbackLocales')) {
+            return $this->translator->getFallbackLocales();
+        }
+
+        return [];
+    }
+
+    /**
+     * Passes through all unknown calls onto the translator object.
+     */
+    public function __call($method, $args)
+    {
+        return $this->translator->{$method}(...$args);
+    }
+
+    /**
+     * @return array
+     */
+    public function getCollectedMessages()
+    {
+        return $this->messages;
+    }
+
+    /**
+     * @param string|null $locale
+     * @param string|null $domain
+     * @param string      $id
+     * @param string      $translation
+     * @param array|null  $parameters
+     */
+    private function collectMessage($locale, $domain, $id, $translation, $parameters = [])
+    {
+        if (null === $domain) {
+            $domain = 'messages';
+        }
+
+        $id = (string) $id;
+        $catalogue = $this->translator->getCatalogue($locale);
+        $locale = $catalogue->getLocale();
+        if ($catalogue->defines($id, $domain)) {
+            $state = self::MESSAGE_DEFINED;
+        } elseif ($catalogue->has($id, $domain)) {
+            $state = self::MESSAGE_EQUALS_FALLBACK;
+
+            $fallbackCatalogue = $catalogue->getFallbackCatalogue();
+            while ($fallbackCatalogue) {
+                if ($fallbackCatalogue->defines($id, $domain)) {
+                    $locale = $fallbackCatalogue->getLocale();
+                    break;
+                }
+
+                $fallbackCatalogue = $fallbackCatalogue->getFallbackCatalogue();
+            }
+        } else {
+            $state = self::MESSAGE_MISSING;
+        }
+
+        $this->messages[] = [
+            'locale' => $locale,
+            'domain' => $domain,
+            'id' => $id,
+            'translation' => $translation,
+            'parameters' => $parameters,
+            'state' => $state,
+            'transChoiceNumber' => isset($parameters['%count%']) && is_numeric($parameters['%count%']) ? $parameters['%count%'] : null,
+        ];
+    }
+}
diff --git a/vendor/symfony/translation/DependencyInjection/TranslationDumperPass.php b/vendor/symfony/translation/DependencyInjection/TranslationDumperPass.php
new file mode 100755
index 0000000..930f36d
--- /dev/null
+++ b/vendor/symfony/translation/DependencyInjection/TranslationDumperPass.php
@@ -0,0 +1,44 @@
+<?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\Translation\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * Adds tagged translation.formatter services to translation writer.
+ */
+class TranslationDumperPass implements CompilerPassInterface
+{
+    private $writerServiceId;
+    private $dumperTag;
+
+    public function __construct(string $writerServiceId = 'translation.writer', string $dumperTag = 'translation.dumper')
+    {
+        $this->writerServiceId = $writerServiceId;
+        $this->dumperTag = $dumperTag;
+    }
+
+    public function process(ContainerBuilder $container)
+    {
+        if (!$container->hasDefinition($this->writerServiceId)) {
+            return;
+        }
+
+        $definition = $container->getDefinition($this->writerServiceId);
+
+        foreach ($container->findTaggedServiceIds($this->dumperTag, true) as $id => $attributes) {
+            $definition->addMethodCall('addDumper', [$attributes[0]['alias'], new Reference($id)]);
+        }
+    }
+}
diff --git a/vendor/symfony/translation/DependencyInjection/TranslationExtractorPass.php b/vendor/symfony/translation/DependencyInjection/TranslationExtractorPass.php
new file mode 100755
index 0000000..d08b2ba
--- /dev/null
+++ b/vendor/symfony/translation/DependencyInjection/TranslationExtractorPass.php
@@ -0,0 +1,49 @@
+<?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\Translation\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * Adds tagged translation.extractor services to translation extractor.
+ */
+class TranslationExtractorPass implements CompilerPassInterface
+{
+    private $extractorServiceId;
+    private $extractorTag;
+
+    public function __construct(string $extractorServiceId = 'translation.extractor', string $extractorTag = 'translation.extractor')
+    {
+        $this->extractorServiceId = $extractorServiceId;
+        $this->extractorTag = $extractorTag;
+    }
+
+    public function process(ContainerBuilder $container)
+    {
+        if (!$container->hasDefinition($this->extractorServiceId)) {
+            return;
+        }
+
+        $definition = $container->getDefinition($this->extractorServiceId);
+
+        foreach ($container->findTaggedServiceIds($this->extractorTag, true) as $id => $attributes) {
+            if (!isset($attributes[0]['alias'])) {
+                throw new RuntimeException(sprintf('The alias for the tag "translation.extractor" of service "%s" must be set.', $id));
+            }
+
+            $definition->addMethodCall('addExtractor', [$attributes[0]['alias'], new Reference($id)]);
+        }
+    }
+}
diff --git a/vendor/symfony/translation/DependencyInjection/TranslatorPass.php b/vendor/symfony/translation/DependencyInjection/TranslatorPass.php
new file mode 100755
index 0000000..ed4a840
--- /dev/null
+++ b/vendor/symfony/translation/DependencyInjection/TranslatorPass.php
@@ -0,0 +1,89 @@
+<?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\Translation\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+
+class TranslatorPass implements CompilerPassInterface
+{
+    private $translatorServiceId;
+    private $readerServiceId;
+    private $loaderTag;
+    private $debugCommandServiceId;
+    private $updateCommandServiceId;
+
+    public function __construct(string $translatorServiceId = 'translator.default', string $readerServiceId = 'translation.reader', string $loaderTag = 'translation.loader', string $debugCommandServiceId = 'console.command.translation_debug', string $updateCommandServiceId = 'console.command.translation_update')
+    {
+        $this->translatorServiceId = $translatorServiceId;
+        $this->readerServiceId = $readerServiceId;
+        $this->loaderTag = $loaderTag;
+        $this->debugCommandServiceId = $debugCommandServiceId;
+        $this->updateCommandServiceId = $updateCommandServiceId;
+    }
+
+    public function process(ContainerBuilder $container)
+    {
+        if (!$container->hasDefinition($this->translatorServiceId)) {
+            return;
+        }
+
+        $loaders = [];
+        $loaderRefs = [];
+        foreach ($container->findTaggedServiceIds($this->loaderTag, true) as $id => $attributes) {
+            $loaderRefs[$id] = new Reference($id);
+            $loaders[$id][] = $attributes[0]['alias'];
+            if (isset($attributes[0]['legacy-alias'])) {
+                $loaders[$id][] = $attributes[0]['legacy-alias'];
+            }
+        }
+
+        if ($container->hasDefinition($this->readerServiceId)) {
+            $definition = $container->getDefinition($this->readerServiceId);
+            foreach ($loaders as $id => $formats) {
+                foreach ($formats as $format) {
+                    $definition->addMethodCall('addLoader', [$format, $loaderRefs[$id]]);
+                }
+            }
+        }
+
+        $container
+            ->findDefinition($this->translatorServiceId)
+            ->replaceArgument(0, ServiceLocatorTagPass::register($container, $loaderRefs))
+            ->replaceArgument(3, $loaders)
+        ;
+
+        if (!$container->hasParameter('twig.default_path')) {
+            return;
+        }
+
+        $paths = array_keys($container->getDefinition('twig.template_iterator')->getArgument(2));
+        if ($container->hasDefinition($this->debugCommandServiceId)) {
+            $definition = $container->getDefinition($this->debugCommandServiceId);
+            $definition->replaceArgument(4, $container->getParameter('twig.default_path'));
+
+            if (\count($definition->getArguments()) > 6) {
+                $definition->replaceArgument(6, $paths);
+            }
+        }
+        if ($container->hasDefinition($this->updateCommandServiceId)) {
+            $definition = $container->getDefinition($this->updateCommandServiceId);
+            $definition->replaceArgument(5, $container->getParameter('twig.default_path'));
+
+            if (\count($definition->getArguments()) > 7) {
+                $definition->replaceArgument(7, $paths);
+            }
+        }
+    }
+}
diff --git a/vendor/symfony/translation/DependencyInjection/TranslatorPathsPass.php b/vendor/symfony/translation/DependencyInjection/TranslatorPathsPass.php
new file mode 100755
index 0000000..8ff2587
--- /dev/null
+++ b/vendor/symfony/translation/DependencyInjection/TranslatorPathsPass.php
@@ -0,0 +1,144 @@
+<?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\Translation\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\Compiler\AbstractRecursivePass;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\ServiceLocator;
+
+/**
+ * @author Yonel Ceruto <yonelceruto@gmail.com>
+ */
+class TranslatorPathsPass extends AbstractRecursivePass
+{
+    private $translatorServiceId;
+    private $debugCommandServiceId;
+    private $updateCommandServiceId;
+    private $resolverServiceId;
+    private $level = 0;
+    private $paths = [];
+    private $definitions = [];
+    private $controllers = [];
+
+    public function __construct(string $translatorServiceId = 'translator', string $debugCommandServiceId = 'console.command.translation_debug', string $updateCommandServiceId = 'console.command.translation_update', string $resolverServiceId = 'argument_resolver.service')
+    {
+        $this->translatorServiceId = $translatorServiceId;
+        $this->debugCommandServiceId = $debugCommandServiceId;
+        $this->updateCommandServiceId = $updateCommandServiceId;
+        $this->resolverServiceId = $resolverServiceId;
+    }
+
+    public function process(ContainerBuilder $container)
+    {
+        if (!$container->hasDefinition($this->translatorServiceId)) {
+            return;
+        }
+
+        foreach ($this->findControllerArguments($container) as $controller => $argument) {
+            $id = substr($controller, 0, strpos($controller, ':') ?: \strlen($controller));
+            if ($container->hasDefinition($id)) {
+                list($locatorRef) = $argument->getValues();
+                $this->controllers[(string) $locatorRef][$container->getDefinition($id)->getClass()] = true;
+            }
+        }
+
+        try {
+            parent::process($container);
+
+            $paths = [];
+            foreach ($this->paths as $class => $_) {
+                if (($r = $container->getReflectionClass($class)) && !$r->isInterface()) {
+                    $paths[] = $r->getFileName();
+                }
+            }
+            if ($paths) {
+                if ($container->hasDefinition($this->debugCommandServiceId)) {
+                    $definition = $container->getDefinition($this->debugCommandServiceId);
+                    $definition->replaceArgument(6, array_merge($definition->getArgument(6), $paths));
+                }
+                if ($container->hasDefinition($this->updateCommandServiceId)) {
+                    $definition = $container->getDefinition($this->updateCommandServiceId);
+                    $definition->replaceArgument(7, array_merge($definition->getArgument(7), $paths));
+                }
+            }
+        } finally {
+            $this->level = 0;
+            $this->paths = [];
+            $this->definitions = [];
+        }
+    }
+
+    protected function processValue($value, $isRoot = false)
+    {
+        if ($value instanceof Reference) {
+            if ((string) $value === $this->translatorServiceId) {
+                for ($i = $this->level - 1; $i >= 0; --$i) {
+                    $class = $this->definitions[$i]->getClass();
+
+                    if (ServiceLocator::class === $class) {
+                        if (!isset($this->controllers[$this->currentId])) {
+                            continue;
+                        }
+                        foreach ($this->controllers[$this->currentId] as $class => $_) {
+                            $this->paths[$class] = true;
+                        }
+                    } else {
+                        $this->paths[$class] = true;
+                    }
+
+                    break;
+                }
+            }
+
+            return $value;
+        }
+
+        if ($value instanceof Definition) {
+            $this->definitions[$this->level++] = $value;
+            $value = parent::processValue($value, $isRoot);
+            unset($this->definitions[--$this->level]);
+
+            return $value;
+        }
+
+        return parent::processValue($value, $isRoot);
+    }
+
+    private function findControllerArguments(ContainerBuilder $container): array
+    {
+        if ($container->hasDefinition($this->resolverServiceId)) {
+            $argument = $container->getDefinition($this->resolverServiceId)->getArgument(0);
+            if ($argument instanceof Reference) {
+                $argument = $container->getDefinition($argument);
+            }
+
+            return $argument->getArgument(0);
+        }
+
+        if ($container->hasDefinition('debug.'.$this->resolverServiceId)) {
+            $argument = $container->getDefinition('debug.'.$this->resolverServiceId)->getArgument(0);
+            if ($argument instanceof Reference) {
+                $argument = $container->getDefinition($argument);
+            }
+            $argument = $argument->getArgument(0);
+            if ($argument instanceof Reference) {
+                $argument = $container->getDefinition($argument);
+            }
+
+            return $argument->getArgument(0);
+        }
+
+        return [];
+    }
+}
diff --git a/vendor/symfony/translation/Dumper/CsvFileDumper.php b/vendor/symfony/translation/Dumper/CsvFileDumper.php
new file mode 100755
index 0000000..bfa8db6
--- /dev/null
+++ b/vendor/symfony/translation/Dumper/CsvFileDumper.php
@@ -0,0 +1,63 @@
+<?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\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * CsvFileDumper generates a csv formatted string representation of a message catalogue.
+ *
+ * @author Stealth35
+ */
+class CsvFileDumper extends FileDumper
+{
+    private $delimiter = ';';
+    private $enclosure = '"';
+
+    /**
+     * {@inheritdoc}
+     */
+    public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = [])
+    {
+        $handle = fopen('php://memory', 'r+b');
+
+        foreach ($messages->all($domain) as $source => $target) {
+            fputcsv($handle, [$source, $target], $this->delimiter, $this->enclosure);
+        }
+
+        rewind($handle);
+        $output = stream_get_contents($handle);
+        fclose($handle);
+
+        return $output;
+    }
+
+    /**
+     * Sets the delimiter and escape character for CSV.
+     *
+     * @param string $delimiter Delimiter character
+     * @param string $enclosure Enclosure character
+     */
+    public function setCsvControl($delimiter = ';', $enclosure = '"')
+    {
+        $this->delimiter = $delimiter;
+        $this->enclosure = $enclosure;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExtension()
+    {
+        return 'csv';
+    }
+}
diff --git a/vendor/symfony/translation/Dumper/DumperInterface.php b/vendor/symfony/translation/Dumper/DumperInterface.php
new file mode 100755
index 0000000..9965e80
--- /dev/null
+++ b/vendor/symfony/translation/Dumper/DumperInterface.php
@@ -0,0 +1,31 @@
+<?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\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * DumperInterface is the interface implemented by all translation dumpers.
+ * There is no common option.
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+interface DumperInterface
+{
+    /**
+     * Dumps the message catalogue.
+     *
+     * @param MessageCatalogue $messages The message catalogue
+     * @param array            $options  Options that are used by the dumper
+     */
+    public function dump(MessageCatalogue $messages, $options = []);
+}
diff --git a/vendor/symfony/translation/Dumper/FileDumper.php b/vendor/symfony/translation/Dumper/FileDumper.php
new file mode 100755
index 0000000..8457ecc
--- /dev/null
+++ b/vendor/symfony/translation/Dumper/FileDumper.php
@@ -0,0 +1,132 @@
+<?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\Translation\Dumper;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+use Symfony\Component\Translation\Exception\RuntimeException;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * FileDumper is an implementation of DumperInterface that dump a message catalogue to file(s).
+ *
+ * Options:
+ * - path (mandatory): the directory where the files should be saved
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+abstract class FileDumper implements DumperInterface
+{
+    /**
+     * A template for the relative paths to files.
+     *
+     * @var string
+     */
+    protected $relativePathTemplate = '%domain%.%locale%.%extension%';
+
+    /**
+     * Sets the template for the relative paths to files.
+     *
+     * @param string $relativePathTemplate A template for the relative paths to files
+     */
+    public function setRelativePathTemplate($relativePathTemplate)
+    {
+        $this->relativePathTemplate = $relativePathTemplate;
+    }
+
+    /**
+     * Sets backup flag.
+     *
+     * @param bool $backup
+     *
+     * @deprecated since Symfony 4.1
+     */
+    public function setBackup($backup)
+    {
+        @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1.', __METHOD__), E_USER_DEPRECATED);
+
+        if (false !== $backup) {
+            throw new \LogicException('The backup feature is no longer supported.');
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dump(MessageCatalogue $messages, $options = [])
+    {
+        if (!\array_key_exists('path', $options)) {
+            throw new InvalidArgumentException('The file dumper needs a path option.');
+        }
+
+        // save a file for each domain
+        foreach ($messages->getDomains() as $domain) {
+            $fullpath = $options['path'].'/'.$this->getRelativePath($domain, $messages->getLocale());
+            if (!file_exists($fullpath)) {
+                $directory = \dirname($fullpath);
+                if (!file_exists($directory) && !@mkdir($directory, 0777, true)) {
+                    throw new RuntimeException(sprintf('Unable to create directory "%s".', $directory));
+                }
+            }
+
+            $intlDomain = $domain.MessageCatalogue::INTL_DOMAIN_SUFFIX;
+            $intlMessages = $messages->all($intlDomain);
+
+            if ($intlMessages) {
+                $intlPath = $options['path'].'/'.$this->getRelativePath($intlDomain, $messages->getLocale());
+                file_put_contents($intlPath, $this->formatCatalogue($messages, $intlDomain, $options));
+
+                $messages->replace([], $intlDomain);
+
+                try {
+                    if ($messages->all($domain)) {
+                        file_put_contents($fullpath, $this->formatCatalogue($messages, $domain, $options));
+                    }
+                    continue;
+                } finally {
+                    $messages->replace($intlMessages, $intlDomain);
+                }
+            }
+
+            file_put_contents($fullpath, $this->formatCatalogue($messages, $domain, $options));
+        }
+    }
+
+    /**
+     * Transforms a domain of a message catalogue to its string representation.
+     *
+     * @param MessageCatalogue $messages
+     * @param string           $domain
+     * @param array            $options
+     *
+     * @return string representation
+     */
+    abstract public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = []);
+
+    /**
+     * Gets the file extension of the dumper.
+     *
+     * @return string file extension
+     */
+    abstract protected function getExtension();
+
+    /**
+     * Gets the relative file path using the template.
+     */
+    private function getRelativePath(string $domain, string $locale): string
+    {
+        return strtr($this->relativePathTemplate, [
+            '%domain%' => $domain,
+            '%locale%' => $locale,
+            '%extension%' => $this->getExtension(),
+        ]);
+    }
+}
diff --git a/vendor/symfony/translation/Dumper/IcuResFileDumper.php b/vendor/symfony/translation/Dumper/IcuResFileDumper.php
new file mode 100755
index 0000000..48d0bef
--- /dev/null
+++ b/vendor/symfony/translation/Dumper/IcuResFileDumper.php
@@ -0,0 +1,106 @@
+<?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\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * IcuResDumper generates an ICU ResourceBundle formatted string representation of a message catalogue.
+ *
+ * @author Stealth35
+ */
+class IcuResFileDumper extends FileDumper
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected $relativePathTemplate = '%domain%/%locale%.%extension%';
+
+    /**
+     * {@inheritdoc}
+     */
+    public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = [])
+    {
+        $data = $indexes = $resources = '';
+
+        foreach ($messages->all($domain) as $source => $target) {
+            $indexes .= pack('v', \strlen($data) + 28);
+            $data .= $source."\0";
+        }
+
+        $data .= $this->writePadding($data);
+
+        $keyTop = $this->getPosition($data);
+
+        foreach ($messages->all($domain) as $source => $target) {
+            $resources .= pack('V', $this->getPosition($data));
+
+            $data .= pack('V', \strlen($target))
+                .mb_convert_encoding($target."\0", 'UTF-16LE', 'UTF-8')
+                .$this->writePadding($data)
+                  ;
+        }
+
+        $resOffset = $this->getPosition($data);
+
+        $data .= pack('v', \count($messages->all($domain)))
+            .$indexes
+            .$this->writePadding($data)
+            .$resources
+              ;
+
+        $bundleTop = $this->getPosition($data);
+
+        $root = pack('V7',
+            $resOffset + (2 << 28), // Resource Offset + Resource Type
+            6,                      // Index length
+            $keyTop,                        // Index keys top
+            $bundleTop,                     // Index resources top
+            $bundleTop,                     // Index bundle top
+            \count($messages->all($domain)), // Index max table length
+            0                               // Index attributes
+        );
+
+        $header = pack('vC2v4C12@32',
+            32,                     // Header size
+            0xDA, 0x27,             // Magic number 1 and 2
+            20, 0, 0, 2,            // Rest of the header, ..., Size of a char
+            0x52, 0x65, 0x73, 0x42, // Data format identifier
+            1, 2, 0, 0,             // Data version
+            1, 4, 0, 0              // Unicode version
+        );
+
+        return $header.$root.$data;
+    }
+
+    private function writePadding($data)
+    {
+        $padding = \strlen($data) % 4;
+
+        if ($padding) {
+            return str_repeat("\xAA", 4 - $padding);
+        }
+    }
+
+    private function getPosition($data)
+    {
+        return (\strlen($data) + 28) / 4;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExtension()
+    {
+        return 'res';
+    }
+}
diff --git a/vendor/symfony/translation/Dumper/IniFileDumper.php b/vendor/symfony/translation/Dumper/IniFileDumper.php
new file mode 100755
index 0000000..45ff961
--- /dev/null
+++ b/vendor/symfony/translation/Dumper/IniFileDumper.php
@@ -0,0 +1,45 @@
+<?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\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * IniFileDumper generates an ini formatted string representation of a message catalogue.
+ *
+ * @author Stealth35
+ */
+class IniFileDumper extends FileDumper
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = [])
+    {
+        $output = '';
+
+        foreach ($messages->all($domain) as $source => $target) {
+            $escapeTarget = str_replace('"', '\"', $target);
+            $output .= $source.'="'.$escapeTarget."\"\n";
+        }
+
+        return $output;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExtension()
+    {
+        return 'ini';
+    }
+}
diff --git a/vendor/symfony/translation/Dumper/JsonFileDumper.php b/vendor/symfony/translation/Dumper/JsonFileDumper.php
new file mode 100755
index 0000000..2af8231
--- /dev/null
+++ b/vendor/symfony/translation/Dumper/JsonFileDumper.php
@@ -0,0 +1,40 @@
+<?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\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * JsonFileDumper generates an json formatted string representation of a message catalogue.
+ *
+ * @author singles
+ */
+class JsonFileDumper extends FileDumper
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = [])
+    {
+        $flags = $options['json_encoding'] ?? JSON_PRETTY_PRINT;
+
+        return json_encode($messages->all($domain), $flags);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExtension()
+    {
+        return 'json';
+    }
+}
diff --git a/vendor/symfony/translation/Dumper/MoFileDumper.php b/vendor/symfony/translation/Dumper/MoFileDumper.php
new file mode 100755
index 0000000..27be16d
--- /dev/null
+++ b/vendor/symfony/translation/Dumper/MoFileDumper.php
@@ -0,0 +1,82 @@
+<?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\Translation\Dumper;
+
+use Symfony\Component\Translation\Loader\MoFileLoader;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * MoFileDumper generates a gettext formatted string representation of a message catalogue.
+ *
+ * @author Stealth35
+ */
+class MoFileDumper extends FileDumper
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = [])
+    {
+        $sources = $targets = $sourceOffsets = $targetOffsets = '';
+        $offsets = [];
+        $size = 0;
+
+        foreach ($messages->all($domain) as $source => $target) {
+            $offsets[] = array_map('strlen', [$sources, $source, $targets, $target]);
+            $sources .= "\0".$source;
+            $targets .= "\0".$target;
+            ++$size;
+        }
+
+        $header = [
+            'magicNumber' => MoFileLoader::MO_LITTLE_ENDIAN_MAGIC,
+            'formatRevision' => 0,
+            'count' => $size,
+            'offsetId' => MoFileLoader::MO_HEADER_SIZE,
+            'offsetTranslated' => MoFileLoader::MO_HEADER_SIZE + (8 * $size),
+            'sizeHashes' => 0,
+            'offsetHashes' => MoFileLoader::MO_HEADER_SIZE + (16 * $size),
+        ];
+
+        $sourcesSize = \strlen($sources);
+        $sourcesStart = $header['offsetHashes'] + 1;
+
+        foreach ($offsets as $offset) {
+            $sourceOffsets .= $this->writeLong($offset[1])
+                          .$this->writeLong($offset[0] + $sourcesStart);
+            $targetOffsets .= $this->writeLong($offset[3])
+                          .$this->writeLong($offset[2] + $sourcesStart + $sourcesSize);
+        }
+
+        $output = implode('', array_map([$this, 'writeLong'], $header))
+               .$sourceOffsets
+               .$targetOffsets
+               .$sources
+               .$targets
+                ;
+
+        return $output;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExtension()
+    {
+        return 'mo';
+    }
+
+    private function writeLong($str)
+    {
+        return pack('V*', $str);
+    }
+}
diff --git a/vendor/symfony/translation/Dumper/PhpFileDumper.php b/vendor/symfony/translation/Dumper/PhpFileDumper.php
new file mode 100755
index 0000000..e77afc2
--- /dev/null
+++ b/vendor/symfony/translation/Dumper/PhpFileDumper.php
@@ -0,0 +1,38 @@
+<?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\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * PhpFileDumper generates PHP files from a message catalogue.
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+class PhpFileDumper extends FileDumper
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = [])
+    {
+        return "<?php\n\nreturn ".var_export($messages->all($domain), true).";\n";
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExtension()
+    {
+        return 'php';
+    }
+}
diff --git a/vendor/symfony/translation/Dumper/PoFileDumper.php b/vendor/symfony/translation/Dumper/PoFileDumper.php
new file mode 100755
index 0000000..5f60086
--- /dev/null
+++ b/vendor/symfony/translation/Dumper/PoFileDumper.php
@@ -0,0 +1,84 @@
+<?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\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * PoFileDumper generates a gettext formatted string representation of a message catalogue.
+ *
+ * @author Stealth35
+ */
+class PoFileDumper extends FileDumper
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = [])
+    {
+        $output = 'msgid ""'."\n";
+        $output .= 'msgstr ""'."\n";
+        $output .= '"Content-Type: text/plain; charset=UTF-8\n"'."\n";
+        $output .= '"Content-Transfer-Encoding: 8bit\n"'."\n";
+        $output .= '"Language: '.$messages->getLocale().'\n"'."\n";
+        $output .= "\n";
+
+        $newLine = false;
+        foreach ($messages->all($domain) as $source => $target) {
+            if ($newLine) {
+                $output .= "\n";
+            } else {
+                $newLine = true;
+            }
+            $metadata = $messages->getMetadata($source, $domain);
+
+            if (isset($metadata['comments'])) {
+                $output .= $this->formatComments($metadata['comments']);
+            }
+            if (isset($metadata['flags'])) {
+                $output .= $this->formatComments(implode(',', (array) $metadata['flags']), ',');
+            }
+            if (isset($metadata['sources'])) {
+                $output .= $this->formatComments(implode(' ', (array) $metadata['sources']), ':');
+            }
+
+            $output .= sprintf('msgid "%s"'."\n", $this->escape($source));
+            $output .= sprintf('msgstr "%s"'."\n", $this->escape($target));
+        }
+
+        return $output;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExtension()
+    {
+        return 'po';
+    }
+
+    private function escape($str)
+    {
+        return addcslashes($str, "\0..\37\42\134");
+    }
+
+    private function formatComments($comments, string $prefix = ''): ?string
+    {
+        $output = null;
+
+        foreach ((array) $comments as $comment) {
+            $output .= sprintf('#%s %s'."\n", $prefix, $comment);
+        }
+
+        return $output;
+    }
+}
diff --git a/vendor/symfony/translation/Dumper/QtFileDumper.php b/vendor/symfony/translation/Dumper/QtFileDumper.php
new file mode 100755
index 0000000..79a64b2
--- /dev/null
+++ b/vendor/symfony/translation/Dumper/QtFileDumper.php
@@ -0,0 +1,61 @@
+<?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\Translation\Dumper;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * QtFileDumper generates ts files from a message catalogue.
+ *
+ * @author Benjamin Eberlei <kontakt@beberlei.de>
+ */
+class QtFileDumper extends FileDumper
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = [])
+    {
+        $dom = new \DOMDocument('1.0', 'utf-8');
+        $dom->formatOutput = true;
+        $ts = $dom->appendChild($dom->createElement('TS'));
+        $context = $ts->appendChild($dom->createElement('context'));
+        $context->appendChild($dom->createElement('name', $domain));
+
+        foreach ($messages->all($domain) as $source => $target) {
+            $message = $context->appendChild($dom->createElement('message'));
+            $metadata = $messages->getMetadata($source, $domain);
+            if (isset($metadata['sources'])) {
+                foreach ((array) $metadata['sources'] as $location) {
+                    $loc = explode(':', $location, 2);
+                    $location = $message->appendChild($dom->createElement('location'));
+                    $location->setAttribute('filename', $loc[0]);
+                    if (isset($loc[1])) {
+                        $location->setAttribute('line', $loc[1]);
+                    }
+                }
+            }
+            $message->appendChild($dom->createElement('source', $source));
+            $message->appendChild($dom->createElement('translation', $target));
+        }
+
+        return $dom->saveXML();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExtension()
+    {
+        return 'ts';
+    }
+}
diff --git a/vendor/symfony/translation/Dumper/XliffFileDumper.php b/vendor/symfony/translation/Dumper/XliffFileDumper.php
new file mode 100755
index 0000000..8d2694f
--- /dev/null
+++ b/vendor/symfony/translation/Dumper/XliffFileDumper.php
@@ -0,0 +1,209 @@
+<?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\Translation\Dumper;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * XliffFileDumper generates xliff files from a message catalogue.
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+class XliffFileDumper extends FileDumper
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = [])
+    {
+        $xliffVersion = '1.2';
+        if (\array_key_exists('xliff_version', $options)) {
+            $xliffVersion = $options['xliff_version'];
+        }
+
+        if (\array_key_exists('default_locale', $options)) {
+            $defaultLocale = $options['default_locale'];
+        } else {
+            $defaultLocale = \Locale::getDefault();
+        }
+
+        if ('1.2' === $xliffVersion) {
+            return $this->dumpXliff1($defaultLocale, $messages, $domain, $options);
+        }
+        if ('2.0' === $xliffVersion) {
+            return $this->dumpXliff2($defaultLocale, $messages, $domain, $options);
+        }
+
+        throw new InvalidArgumentException(sprintf('No support implemented for dumping XLIFF version "%s".', $xliffVersion));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExtension()
+    {
+        return 'xlf';
+    }
+
+    private function dumpXliff1($defaultLocale, MessageCatalogue $messages, $domain, array $options = [])
+    {
+        $toolInfo = ['tool-id' => 'symfony', 'tool-name' => 'Symfony'];
+        if (\array_key_exists('tool_info', $options)) {
+            $toolInfo = array_merge($toolInfo, $options['tool_info']);
+        }
+
+        $dom = new \DOMDocument('1.0', 'utf-8');
+        $dom->formatOutput = true;
+
+        $xliff = $dom->appendChild($dom->createElement('xliff'));
+        $xliff->setAttribute('version', '1.2');
+        $xliff->setAttribute('xmlns', 'urn:oasis:names:tc:xliff:document:1.2');
+
+        $xliffFile = $xliff->appendChild($dom->createElement('file'));
+        $xliffFile->setAttribute('source-language', str_replace('_', '-', $defaultLocale));
+        $xliffFile->setAttribute('target-language', str_replace('_', '-', $messages->getLocale()));
+        $xliffFile->setAttribute('datatype', 'plaintext');
+        $xliffFile->setAttribute('original', 'file.ext');
+
+        $xliffHead = $xliffFile->appendChild($dom->createElement('header'));
+        $xliffTool = $xliffHead->appendChild($dom->createElement('tool'));
+        foreach ($toolInfo as $id => $value) {
+            $xliffTool->setAttribute($id, $value);
+        }
+
+        $xliffBody = $xliffFile->appendChild($dom->createElement('body'));
+        foreach ($messages->all($domain) as $source => $target) {
+            $translation = $dom->createElement('trans-unit');
+
+            $translation->setAttribute('id', strtr(substr(base64_encode(hash('sha256', $source, true)), 0, 7), '/+', '._'));
+            $translation->setAttribute('resname', $source);
+
+            $s = $translation->appendChild($dom->createElement('source'));
+            $s->appendChild($dom->createTextNode($source));
+
+            // Does the target contain characters requiring a CDATA section?
+            $text = 1 === preg_match('/[&<>]/', $target) ? $dom->createCDATASection($target) : $dom->createTextNode($target);
+
+            $targetElement = $dom->createElement('target');
+            $metadata = $messages->getMetadata($source, $domain);
+            if ($this->hasMetadataArrayInfo('target-attributes', $metadata)) {
+                foreach ($metadata['target-attributes'] as $name => $value) {
+                    $targetElement->setAttribute($name, $value);
+                }
+            }
+            $t = $translation->appendChild($targetElement);
+            $t->appendChild($text);
+
+            if ($this->hasMetadataArrayInfo('notes', $metadata)) {
+                foreach ($metadata['notes'] as $note) {
+                    if (!isset($note['content'])) {
+                        continue;
+                    }
+
+                    $n = $translation->appendChild($dom->createElement('note'));
+                    $n->appendChild($dom->createTextNode($note['content']));
+
+                    if (isset($note['priority'])) {
+                        $n->setAttribute('priority', $note['priority']);
+                    }
+
+                    if (isset($note['from'])) {
+                        $n->setAttribute('from', $note['from']);
+                    }
+                }
+            }
+
+            $xliffBody->appendChild($translation);
+        }
+
+        return $dom->saveXML();
+    }
+
+    private function dumpXliff2($defaultLocale, MessageCatalogue $messages, $domain, array $options = [])
+    {
+        $dom = new \DOMDocument('1.0', 'utf-8');
+        $dom->formatOutput = true;
+
+        $xliff = $dom->appendChild($dom->createElement('xliff'));
+        $xliff->setAttribute('xmlns', 'urn:oasis:names:tc:xliff:document:2.0');
+        $xliff->setAttribute('version', '2.0');
+        $xliff->setAttribute('srcLang', str_replace('_', '-', $defaultLocale));
+        $xliff->setAttribute('trgLang', str_replace('_', '-', $messages->getLocale()));
+
+        $xliffFile = $xliff->appendChild($dom->createElement('file'));
+        if (MessageCatalogue::INTL_DOMAIN_SUFFIX === substr($domain, -($suffixLength = \strlen(MessageCatalogue::INTL_DOMAIN_SUFFIX)))) {
+            $xliffFile->setAttribute('id', substr($domain, 0, -$suffixLength).'.'.$messages->getLocale());
+        } else {
+            $xliffFile->setAttribute('id', $domain.'.'.$messages->getLocale());
+        }
+
+        foreach ($messages->all($domain) as $source => $target) {
+            $translation = $dom->createElement('unit');
+            $translation->setAttribute('id', strtr(substr(base64_encode(hash('sha256', $source, true)), 0, 7), '/+', '._'));
+            $name = $source;
+            if (\strlen($source) > 80) {
+                $name = substr(md5($source), -7);
+            }
+            $translation->setAttribute('name', $name);
+            $metadata = $messages->getMetadata($source, $domain);
+
+            // Add notes section
+            if ($this->hasMetadataArrayInfo('notes', $metadata)) {
+                $notesElement = $dom->createElement('notes');
+                foreach ($metadata['notes'] as $note) {
+                    $n = $dom->createElement('note');
+                    $n->appendChild($dom->createTextNode(isset($note['content']) ? $note['content'] : ''));
+                    unset($note['content']);
+
+                    foreach ($note as $name => $value) {
+                        $n->setAttribute($name, $value);
+                    }
+                    $notesElement->appendChild($n);
+                }
+                $translation->appendChild($notesElement);
+            }
+
+            $segment = $translation->appendChild($dom->createElement('segment'));
+
+            $s = $segment->appendChild($dom->createElement('source'));
+            $s->appendChild($dom->createTextNode($source));
+
+            // Does the target contain characters requiring a CDATA section?
+            $text = 1 === preg_match('/[&<>]/', $target) ? $dom->createCDATASection($target) : $dom->createTextNode($target);
+
+            $targetElement = $dom->createElement('target');
+            if ($this->hasMetadataArrayInfo('target-attributes', $metadata)) {
+                foreach ($metadata['target-attributes'] as $name => $value) {
+                    $targetElement->setAttribute($name, $value);
+                }
+            }
+            $t = $segment->appendChild($targetElement);
+            $t->appendChild($text);
+
+            $xliffFile->appendChild($translation);
+        }
+
+        return $dom->saveXML();
+    }
+
+    /**
+     * @param string     $key
+     * @param array|null $metadata
+     *
+     * @return bool
+     */
+    private function hasMetadataArrayInfo($key, $metadata = null)
+    {
+        return null !== $metadata && \array_key_exists($key, $metadata) && ($metadata[$key] instanceof \Traversable || \is_array($metadata[$key]));
+    }
+}
diff --git a/vendor/symfony/translation/Dumper/YamlFileDumper.php b/vendor/symfony/translation/Dumper/YamlFileDumper.php
new file mode 100755
index 0000000..d6e4af8
--- /dev/null
+++ b/vendor/symfony/translation/Dumper/YamlFileDumper.php
@@ -0,0 +1,62 @@
+<?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\Translation\Dumper;
+
+use Symfony\Component\Translation\Exception\LogicException;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Util\ArrayConverter;
+use Symfony\Component\Yaml\Yaml;
+
+/**
+ * YamlFileDumper generates yaml files from a message catalogue.
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+class YamlFileDumper extends FileDumper
+{
+    private $extension;
+
+    public function __construct(string $extension = 'yml')
+    {
+        $this->extension = $extension;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = [])
+    {
+        if (!class_exists('Symfony\Component\Yaml\Yaml')) {
+            throw new LogicException('Dumping translations in the YAML format requires the Symfony Yaml component.');
+        }
+
+        $data = $messages->all($domain);
+
+        if (isset($options['as_tree']) && $options['as_tree']) {
+            $data = ArrayConverter::expandToTree($data);
+        }
+
+        if (isset($options['inline']) && ($inline = (int) $options['inline']) > 0) {
+            return Yaml::dump($data, $inline);
+        }
+
+        return Yaml::dump($data);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExtension()
+    {
+        return $this->extension;
+    }
+}
diff --git a/vendor/symfony/translation/Exception/ExceptionInterface.php b/vendor/symfony/translation/Exception/ExceptionInterface.php
new file mode 100755
index 0000000..8f9c54e
--- /dev/null
+++ b/vendor/symfony/translation/Exception/ExceptionInterface.php
@@ -0,0 +1,21 @@
+<?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\Translation\Exception;
+
+/**
+ * Exception interface for all exceptions thrown by the component.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface ExceptionInterface extends \Throwable
+{
+}
diff --git a/vendor/symfony/translation/Exception/InvalidArgumentException.php b/vendor/symfony/translation/Exception/InvalidArgumentException.php
new file mode 100755
index 0000000..90d0669
--- /dev/null
+++ b/vendor/symfony/translation/Exception/InvalidArgumentException.php
@@ -0,0 +1,21 @@
+<?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\Translation\Exception;
+
+/**
+ * Base InvalidArgumentException for the Translation component.
+ *
+ * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
+ */
+class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
diff --git a/vendor/symfony/translation/Exception/InvalidResourceException.php b/vendor/symfony/translation/Exception/InvalidResourceException.php
new file mode 100755
index 0000000..cf07943
--- /dev/null
+++ b/vendor/symfony/translation/Exception/InvalidResourceException.php
@@ -0,0 +1,21 @@
+<?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\Translation\Exception;
+
+/**
+ * Thrown when a resource cannot be loaded.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class InvalidResourceException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
diff --git a/vendor/symfony/translation/Exception/LogicException.php b/vendor/symfony/translation/Exception/LogicException.php
new file mode 100755
index 0000000..9019c7e
--- /dev/null
+++ b/vendor/symfony/translation/Exception/LogicException.php
@@ -0,0 +1,21 @@
+<?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\Translation\Exception;
+
+/**
+ * Base LogicException for Translation component.
+ *
+ * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
+ */
+class LogicException extends \LogicException implements ExceptionInterface
+{
+}
diff --git a/vendor/symfony/translation/Exception/NotFoundResourceException.php b/vendor/symfony/translation/Exception/NotFoundResourceException.php
new file mode 100755
index 0000000..cff73ae
--- /dev/null
+++ b/vendor/symfony/translation/Exception/NotFoundResourceException.php
@@ -0,0 +1,21 @@
+<?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\Translation\Exception;
+
+/**
+ * Thrown when a resource does not exist.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class NotFoundResourceException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
diff --git a/vendor/symfony/translation/Exception/RuntimeException.php b/vendor/symfony/translation/Exception/RuntimeException.php
new file mode 100755
index 0000000..dcd7940
--- /dev/null
+++ b/vendor/symfony/translation/Exception/RuntimeException.php
@@ -0,0 +1,21 @@
+<?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\Translation\Exception;
+
+/**
+ * Base RuntimeException for the Translation component.
+ *
+ * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
+ */
+class RuntimeException extends \RuntimeException implements ExceptionInterface
+{
+}
diff --git a/vendor/symfony/translation/Extractor/AbstractFileExtractor.php b/vendor/symfony/translation/Extractor/AbstractFileExtractor.php
new file mode 100755
index 0000000..8d722da
--- /dev/null
+++ b/vendor/symfony/translation/Extractor/AbstractFileExtractor.php
@@ -0,0 +1,80 @@
+<?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\Translation\Extractor;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+
+/**
+ * Base class used by classes that extract translation messages from files.
+ *
+ * @author Marcos D. Sánchez <marcosdsanchez@gmail.com>
+ */
+abstract class AbstractFileExtractor
+{
+    /**
+     * @param string|array $resource Files, a file or a directory
+     *
+     * @return array
+     */
+    protected function extractFiles($resource)
+    {
+        if (\is_array($resource) || $resource instanceof \Traversable) {
+            $files = [];
+            foreach ($resource as $file) {
+                if ($this->canBeExtracted($file)) {
+                    $files[] = $this->toSplFileInfo($file);
+                }
+            }
+        } elseif (is_file($resource)) {
+            $files = $this->canBeExtracted($resource) ? [$this->toSplFileInfo($resource)] : [];
+        } else {
+            $files = $this->extractFromDirectory($resource);
+        }
+
+        return $files;
+    }
+
+    private function toSplFileInfo(string $file): \SplFileInfo
+    {
+        return new \SplFileInfo($file);
+    }
+
+    /**
+     * @param string $file
+     *
+     * @return bool
+     *
+     * @throws InvalidArgumentException
+     */
+    protected function isFile($file)
+    {
+        if (!is_file($file)) {
+            throw new InvalidArgumentException(sprintf('The "%s" file does not exist.', $file));
+        }
+
+        return true;
+    }
+
+    /**
+     * @param string $file
+     *
+     * @return bool
+     */
+    abstract protected function canBeExtracted($file);
+
+    /**
+     * @param string|array $resource Files, a file or a directory
+     *
+     * @return array files to be extracted
+     */
+    abstract protected function extractFromDirectory($resource);
+}
diff --git a/vendor/symfony/translation/Extractor/ChainExtractor.php b/vendor/symfony/translation/Extractor/ChainExtractor.php
new file mode 100755
index 0000000..69ee2df
--- /dev/null
+++ b/vendor/symfony/translation/Extractor/ChainExtractor.php
@@ -0,0 +1,60 @@
+<?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\Translation\Extractor;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * ChainExtractor extracts translation messages from template files.
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+class ChainExtractor implements ExtractorInterface
+{
+    /**
+     * The extractors.
+     *
+     * @var ExtractorInterface[]
+     */
+    private $extractors = [];
+
+    /**
+     * Adds a loader to the translation extractor.
+     *
+     * @param string             $format    The format of the loader
+     * @param ExtractorInterface $extractor The loader
+     */
+    public function addExtractor($format, ExtractorInterface $extractor)
+    {
+        $this->extractors[$format] = $extractor;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setPrefix($prefix)
+    {
+        foreach ($this->extractors as $extractor) {
+            $extractor->setPrefix($prefix);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function extract($directory, MessageCatalogue $catalogue)
+    {
+        foreach ($this->extractors as $extractor) {
+            $extractor->extract($directory, $catalogue);
+        }
+    }
+}
diff --git a/vendor/symfony/translation/Extractor/ExtractorInterface.php b/vendor/symfony/translation/Extractor/ExtractorInterface.php
new file mode 100755
index 0000000..b4534fa
--- /dev/null
+++ b/vendor/symfony/translation/Extractor/ExtractorInterface.php
@@ -0,0 +1,38 @@
+<?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\Translation\Extractor;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * Extracts translation messages from a directory or files to the catalogue.
+ * New found messages are injected to the catalogue using the prefix.
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+interface ExtractorInterface
+{
+    /**
+     * Extracts translation messages from files, a file or a directory to the catalogue.
+     *
+     * @param string|array     $resource  Files, a file or a directory
+     * @param MessageCatalogue $catalogue The catalogue
+     */
+    public function extract($resource, MessageCatalogue $catalogue);
+
+    /**
+     * Sets the prefix that should be used for new found messages.
+     *
+     * @param string $prefix The prefix
+     */
+    public function setPrefix($prefix);
+}
diff --git a/vendor/symfony/translation/Extractor/PhpExtractor.php b/vendor/symfony/translation/Extractor/PhpExtractor.php
new file mode 100755
index 0000000..3e1a152
--- /dev/null
+++ b/vendor/symfony/translation/Extractor/PhpExtractor.php
@@ -0,0 +1,278 @@
+<?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\Translation\Extractor;
+
+use Symfony\Component\Finder\Finder;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * PhpExtractor extracts translation messages from a PHP template.
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface
+{
+    const MESSAGE_TOKEN = 300;
+    const METHOD_ARGUMENTS_TOKEN = 1000;
+    const DOMAIN_TOKEN = 1001;
+
+    /**
+     * Prefix for new found message.
+     *
+     * @var string
+     */
+    private $prefix = '';
+
+    /**
+     * The sequence that captures translation messages.
+     *
+     * @var array
+     */
+    protected $sequences = [
+        [
+            '->',
+            'trans',
+            '(',
+            self::MESSAGE_TOKEN,
+            ',',
+            self::METHOD_ARGUMENTS_TOKEN,
+            ',',
+            self::DOMAIN_TOKEN,
+        ],
+        [
+            '->',
+            'transChoice',
+            '(',
+            self::MESSAGE_TOKEN,
+            ',',
+            self::METHOD_ARGUMENTS_TOKEN,
+            ',',
+            self::METHOD_ARGUMENTS_TOKEN,
+            ',',
+            self::DOMAIN_TOKEN,
+        ],
+        [
+            '->',
+            'trans',
+            '(',
+            self::MESSAGE_TOKEN,
+        ],
+        [
+            '->',
+            'transChoice',
+            '(',
+            self::MESSAGE_TOKEN,
+        ],
+    ];
+
+    /**
+     * {@inheritdoc}
+     */
+    public function extract($resource, MessageCatalogue $catalog)
+    {
+        $files = $this->extractFiles($resource);
+        foreach ($files as $file) {
+            $this->parseTokens(token_get_all(file_get_contents($file)), $catalog, $file);
+
+            gc_mem_caches();
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setPrefix($prefix)
+    {
+        $this->prefix = $prefix;
+    }
+
+    /**
+     * Normalizes a token.
+     *
+     * @param mixed $token
+     *
+     * @return string
+     */
+    protected function normalizeToken($token)
+    {
+        if (isset($token[1]) && 'b"' !== $token) {
+            return $token[1];
+        }
+
+        return $token;
+    }
+
+    /**
+     * Seeks to a non-whitespace token.
+     */
+    private function seekToNextRelevantToken(\Iterator $tokenIterator)
+    {
+        for (; $tokenIterator->valid(); $tokenIterator->next()) {
+            $t = $tokenIterator->current();
+            if (T_WHITESPACE !== $t[0]) {
+                break;
+            }
+        }
+    }
+
+    private function skipMethodArgument(\Iterator $tokenIterator)
+    {
+        $openBraces = 0;
+
+        for (; $tokenIterator->valid(); $tokenIterator->next()) {
+            $t = $tokenIterator->current();
+
+            if ('[' === $t[0] || '(' === $t[0]) {
+                ++$openBraces;
+            }
+
+            if (']' === $t[0] || ')' === $t[0]) {
+                --$openBraces;
+            }
+
+            if ((0 === $openBraces && ',' === $t[0]) || (-1 === $openBraces && ')' === $t[0])) {
+                break;
+            }
+        }
+    }
+
+    /**
+     * Extracts the message from the iterator while the tokens
+     * match allowed message tokens.
+     */
+    private function getValue(\Iterator $tokenIterator)
+    {
+        $message = '';
+        $docToken = '';
+        $docPart = '';
+
+        for (; $tokenIterator->valid(); $tokenIterator->next()) {
+            $t = $tokenIterator->current();
+            if ('.' === $t) {
+                // Concatenate with next token
+                continue;
+            }
+            if (!isset($t[1])) {
+                break;
+            }
+
+            switch ($t[0]) {
+                case T_START_HEREDOC:
+                    $docToken = $t[1];
+                    break;
+                case T_ENCAPSED_AND_WHITESPACE:
+                case T_CONSTANT_ENCAPSED_STRING:
+                    if ('' === $docToken) {
+                        $message .= PhpStringTokenParser::parse($t[1]);
+                    } else {
+                        $docPart = $t[1];
+                    }
+                    break;
+                case T_END_HEREDOC:
+                    $message .= PhpStringTokenParser::parseDocString($docToken, $docPart);
+                    $docToken = '';
+                    $docPart = '';
+                    break;
+                case T_WHITESPACE:
+                    break;
+                default:
+                    break 2;
+            }
+        }
+
+        return $message;
+    }
+
+    /**
+     * Extracts trans message from PHP tokens.
+     *
+     * @param array            $tokens
+     * @param MessageCatalogue $catalog
+     * @param string           $filename
+     */
+    protected function parseTokens($tokens, MessageCatalogue $catalog/*, string $filename*/)
+    {
+        if (\func_num_args() < 3 && __CLASS__ !== \get_class($this) && __CLASS__ !== (new \ReflectionMethod($this, __FUNCTION__))->getDeclaringClass()->getName() && !$this instanceof \PHPUnit\Framework\MockObject\MockObject && !$this instanceof \Prophecy\Prophecy\ProphecySubjectInterface) {
+            @trigger_error(sprintf('The "%s()" method will have a new "string $filename" argument in version 5.0, not defining it is deprecated since Symfony 4.3.', __METHOD__), E_USER_DEPRECATED);
+        }
+        $filename = 2 < \func_num_args() ? func_get_arg(2) : '';
+
+        $tokenIterator = new \ArrayIterator($tokens);
+
+        for ($key = 0; $key < $tokenIterator->count(); ++$key) {
+            foreach ($this->sequences as $sequence) {
+                $message = '';
+                $domain = 'messages';
+                $tokenIterator->seek($key);
+
+                foreach ($sequence as $sequenceKey => $item) {
+                    $this->seekToNextRelevantToken($tokenIterator);
+
+                    if ($this->normalizeToken($tokenIterator->current()) === $item) {
+                        $tokenIterator->next();
+                        continue;
+                    } elseif (self::MESSAGE_TOKEN === $item) {
+                        $message = $this->getValue($tokenIterator);
+
+                        if (\count($sequence) === ($sequenceKey + 1)) {
+                            break;
+                        }
+                    } elseif (self::METHOD_ARGUMENTS_TOKEN === $item) {
+                        $this->skipMethodArgument($tokenIterator);
+                    } elseif (self::DOMAIN_TOKEN === $item) {
+                        $domainToken = $this->getValue($tokenIterator);
+                        if ('' !== $domainToken) {
+                            $domain = $domainToken;
+                        }
+
+                        break;
+                    } else {
+                        break;
+                    }
+                }
+
+                if ($message) {
+                    $catalog->set($message, $this->prefix.$message, $domain);
+                    $metadata = $catalog->getMetadata($message, $domain) ?? [];
+                    $normalizedFilename = preg_replace('{[\\\\/]+}', '/', $filename);
+                    $metadata['sources'][] = $normalizedFilename.':'.$tokens[$key][2];
+                    $catalog->setMetadata($message, $metadata, $domain);
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
+     * @param string $file
+     *
+     * @return bool
+     *
+     * @throws \InvalidArgumentException
+     */
+    protected function canBeExtracted($file)
+    {
+        return $this->isFile($file) && 'php' === pathinfo($file, PATHINFO_EXTENSION);
+    }
+
+    /**
+     * @param string|array $directory
+     *
+     * @return array
+     */
+    protected function extractFromDirectory($directory)
+    {
+        $finder = new Finder();
+
+        return $finder->files()->name('*.php')->in($directory);
+    }
+}
diff --git a/vendor/symfony/translation/Extractor/PhpStringTokenParser.php b/vendor/symfony/translation/Extractor/PhpStringTokenParser.php
new file mode 100755
index 0000000..8a8ccb1
--- /dev/null
+++ b/vendor/symfony/translation/Extractor/PhpStringTokenParser.php
@@ -0,0 +1,142 @@
+<?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\Translation\Extractor;
+
+/*
+ * The following is derived from code at http://github.com/nikic/PHP-Parser
+ *
+ * Copyright (c) 2011 by Nikita Popov
+ *
+ * Some rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *
+ *     * The names of the contributors may not be used to endorse or
+ *       promote products derived from this software without specific
+ *       prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class PhpStringTokenParser
+{
+    protected static $replacements = [
+        '\\' => '\\',
+        '$' => '$',
+        'n' => "\n",
+        'r' => "\r",
+        't' => "\t",
+        'f' => "\f",
+        'v' => "\v",
+        'e' => "\x1B",
+    ];
+
+    /**
+     * Parses a string token.
+     *
+     * @param string $str String token content
+     *
+     * @return string The parsed string
+     */
+    public static function parse($str)
+    {
+        $bLength = 0;
+        if ('b' === $str[0]) {
+            $bLength = 1;
+        }
+
+        if ('\'' === $str[$bLength]) {
+            return str_replace(
+                ['\\\\', '\\\''],
+                ['\\', '\''],
+                substr($str, $bLength + 1, -1)
+            );
+        } else {
+            return self::parseEscapeSequences(substr($str, $bLength + 1, -1), '"');
+        }
+    }
+
+    /**
+     * Parses escape sequences in strings (all string types apart from single quoted).
+     *
+     * @param string      $str   String without quotes
+     * @param string|null $quote Quote type
+     *
+     * @return string String with escape sequences parsed
+     */
+    public static function parseEscapeSequences($str, $quote)
+    {
+        if (null !== $quote) {
+            $str = str_replace('\\'.$quote, $quote, $str);
+        }
+
+        return preg_replace_callback(
+            '~\\\\([\\\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3})~',
+            [__CLASS__, 'parseCallback'],
+            $str
+        );
+    }
+
+    private static function parseCallback($matches)
+    {
+        $str = $matches[1];
+
+        if (isset(self::$replacements[$str])) {
+            return self::$replacements[$str];
+        } elseif ('x' === $str[0] || 'X' === $str[0]) {
+            return \chr(hexdec($str));
+        } else {
+            return \chr(octdec($str));
+        }
+    }
+
+    /**
+     * Parses a constant doc string.
+     *
+     * @param string $startToken Doc string start token content (<<<SMTHG)
+     * @param string $str        String token content
+     *
+     * @return string Parsed string
+     */
+    public static function parseDocString($startToken, $str)
+    {
+        // strip last newline (thanks tokenizer for sticking it into the string!)
+        $str = preg_replace('~(\r\n|\n|\r)$~', '', $str);
+
+        // nowdoc string
+        if (false !== strpos($startToken, '\'')) {
+            return $str;
+        }
+
+        return self::parseEscapeSequences($str, null);
+    }
+}
diff --git a/vendor/symfony/translation/Formatter/ChoiceMessageFormatterInterface.php b/vendor/symfony/translation/Formatter/ChoiceMessageFormatterInterface.php
new file mode 100755
index 0000000..621dbb2
--- /dev/null
+++ b/vendor/symfony/translation/Formatter/ChoiceMessageFormatterInterface.php
@@ -0,0 +1,32 @@
+<?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\Translation\Formatter;
+
+/**
+ * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
+ *
+ * @deprecated since Symfony 4.2, use MessageFormatterInterface::format() with a %count% parameter instead
+ */
+interface ChoiceMessageFormatterInterface
+{
+    /**
+     * Formats a localized message pattern with given arguments.
+     *
+     * @param string $message    The message (may also be an object that can be cast to string)
+     * @param int    $number     The number to use to find the indice of the message
+     * @param string $locale     The message locale
+     * @param array  $parameters An array of parameters for the message
+     *
+     * @return string
+     */
+    public function choiceFormat($message, $number, $locale, array $parameters = []);
+}
diff --git a/vendor/symfony/translation/Formatter/IntlFormatter.php b/vendor/symfony/translation/Formatter/IntlFormatter.php
new file mode 100755
index 0000000..1d5f468
--- /dev/null
+++ b/vendor/symfony/translation/Formatter/IntlFormatter.php
@@ -0,0 +1,55 @@
+<?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\Translation\Formatter;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+use Symfony\Component\Translation\Exception\LogicException;
+
+/**
+ * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
+ * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
+ */
+class IntlFormatter implements IntlFormatterInterface
+{
+    private $hasMessageFormatter;
+    private $cache = [];
+
+    /**
+     * {@inheritdoc}
+     */
+    public function formatIntl(string $message, string $locale, array $parameters = []): string
+    {
+        if (!$formatter = $this->cache[$locale][$message] ?? null) {
+            if (!($this->hasMessageFormatter ?? $this->hasMessageFormatter = class_exists(\MessageFormatter::class))) {
+                throw new LogicException('Cannot parse message translation: please install the "intl" PHP extension or the "symfony/polyfill-intl-messageformatter" package.');
+            }
+            try {
+                $this->cache[$locale][$message] = $formatter = new \MessageFormatter($locale, $message);
+            } catch (\IntlException $e) {
+                throw new InvalidArgumentException(sprintf('Invalid message format (error #%d): %s.', intl_get_error_code(), intl_get_error_message()), 0, $e);
+            }
+        }
+
+        foreach ($parameters as $key => $value) {
+            if (\in_array($key[0] ?? null, ['%', '{'], true)) {
+                unset($parameters[$key]);
+                $parameters[trim($key, '%{ }')] = $value;
+            }
+        }
+
+        if (false === $message = $formatter->format($parameters)) {
+            throw new InvalidArgumentException(sprintf('Unable to format message (error #%s): %s.', $formatter->getErrorCode(), $formatter->getErrorMessage()));
+        }
+
+        return $message;
+    }
+}
diff --git a/vendor/symfony/translation/Formatter/IntlFormatterInterface.php b/vendor/symfony/translation/Formatter/IntlFormatterInterface.php
new file mode 100755
index 0000000..02fc6ac
--- /dev/null
+++ b/vendor/symfony/translation/Formatter/IntlFormatterInterface.php
@@ -0,0 +1,27 @@
+<?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\Translation\Formatter;
+
+/**
+ * Formats ICU message patterns.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+interface IntlFormatterInterface
+{
+    /**
+     * Formats a localized message using rules defined by ICU MessageFormat.
+     *
+     * @see http://icu-project.org/apiref/icu4c/classMessageFormat.html#details
+     */
+    public function formatIntl(string $message, string $locale, array $parameters = []): string;
+}
diff --git a/vendor/symfony/translation/Formatter/MessageFormatter.php b/vendor/symfony/translation/Formatter/MessageFormatter.php
new file mode 100755
index 0000000..030d7a5
--- /dev/null
+++ b/vendor/symfony/translation/Formatter/MessageFormatter.php
@@ -0,0 +1,79 @@
+<?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\Translation\Formatter;
+
+use Symfony\Component\Translation\IdentityTranslator;
+use Symfony\Component\Translation\MessageSelector;
+use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface;
+use Symfony\Contracts\Translation\TranslatorInterface;
+
+/**
+ * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
+ */
+class MessageFormatter implements MessageFormatterInterface, IntlFormatterInterface, ChoiceMessageFormatterInterface
+{
+    private $translator;
+    private $intlFormatter;
+
+    /**
+     * @param TranslatorInterface|null $translator An identity translator to use as selector for pluralization
+     */
+    public function __construct($translator = null, IntlFormatterInterface $intlFormatter = null)
+    {
+        if ($translator instanceof MessageSelector) {
+            $translator = new IdentityTranslator($translator);
+        } elseif (null !== $translator && !$translator instanceof TranslatorInterface && !$translator instanceof LegacyTranslatorInterface) {
+            throw new \TypeError(sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator)));
+        }
+
+        $this->translator = $translator ?? new IdentityTranslator();
+        $this->intlFormatter = $intlFormatter ?? new IntlFormatter();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function format($message, $locale, array $parameters = [])
+    {
+        if ($this->translator instanceof TranslatorInterface) {
+            return $this->translator->trans($message, $parameters, null, $locale);
+        }
+
+        return strtr($message, $parameters);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function formatIntl(string $message, string $locale, array $parameters = []): string
+    {
+        return $this->intlFormatter->formatIntl($message, $locale, $parameters);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @deprecated since Symfony 4.2, use format() with a %count% parameter instead
+     */
+    public function choiceFormat($message, $number, $locale, array $parameters = [])
+    {
+        @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2, use the format() one instead with a %%count%% parameter.', __METHOD__), E_USER_DEPRECATED);
+
+        $parameters = ['%count%' => $number] + $parameters;
+
+        if ($this->translator instanceof TranslatorInterface) {
+            return $this->format($message, $locale, $parameters);
+        }
+
+        return $this->format($this->translator->transChoice($message, $number, [], null, $locale), $locale, $parameters);
+    }
+}
diff --git a/vendor/symfony/translation/Formatter/MessageFormatterInterface.php b/vendor/symfony/translation/Formatter/MessageFormatterInterface.php
new file mode 100755
index 0000000..370c055
--- /dev/null
+++ b/vendor/symfony/translation/Formatter/MessageFormatterInterface.php
@@ -0,0 +1,30 @@
+<?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\Translation\Formatter;
+
+/**
+ * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
+ * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
+ */
+interface MessageFormatterInterface
+{
+    /**
+     * Formats a localized message pattern with given arguments.
+     *
+     * @param string $message    The message (may also be an object that can be cast to string)
+     * @param string $locale     The message locale
+     * @param array  $parameters An array of parameters for the message
+     *
+     * @return string
+     */
+    public function format($message, $locale, array $parameters = []);
+}
diff --git a/vendor/symfony/translation/IdentityTranslator.php b/vendor/symfony/translation/IdentityTranslator.php
new file mode 100755
index 0000000..b15792a
--- /dev/null
+++ b/vendor/symfony/translation/IdentityTranslator.php
@@ -0,0 +1,61 @@
+<?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\Translation;
+
+use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface;
+use Symfony\Contracts\Translation\TranslatorInterface;
+use Symfony\Contracts\Translation\TranslatorTrait;
+
+/**
+ * IdentityTranslator does not translate anything.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class IdentityTranslator implements LegacyTranslatorInterface, TranslatorInterface
+{
+    use TranslatorTrait;
+
+    private $selector;
+
+    /**
+     * @param MessageSelector|null $selector The message selector for pluralization
+     */
+    public function __construct(MessageSelector $selector = null)
+    {
+        $this->selector = $selector;
+
+        if (__CLASS__ !== \get_class($this)) {
+            @trigger_error(sprintf('Calling "%s()" is deprecated since Symfony 4.2.', __METHOD__), E_USER_DEPRECATED);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @deprecated since Symfony 4.2, use the trans() method instead with a %count% parameter
+     */
+    public function transChoice($id, $number, array $parameters = [], $domain = null, $locale = null)
+    {
+        @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2, use the trans() one instead with a "%%count%%" parameter.', __METHOD__), E_USER_DEPRECATED);
+
+        if ($this->selector) {
+            return strtr($this->selector->choose((string) $id, $number, $locale ?: $this->getLocale()), $parameters);
+        }
+
+        return $this->trans($id, ['%count%' => $number] + $parameters, $domain, $locale);
+    }
+
+    private function getPluralizationRule(int $number, string $locale): int
+    {
+        return PluralizationRules::get($number, $locale, false);
+    }
+}
diff --git a/vendor/symfony/translation/Interval.php b/vendor/symfony/translation/Interval.php
new file mode 100755
index 0000000..a0e484d
--- /dev/null
+++ b/vendor/symfony/translation/Interval.php
@@ -0,0 +1,112 @@
+<?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\Translation;
+
+@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.2, use IdentityTranslator instead.', Interval::class), E_USER_DEPRECATED);
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+
+/**
+ * Tests if a given number belongs to a given math interval.
+ *
+ * An interval can represent a finite set of numbers:
+ *
+ *  {1,2,3,4}
+ *
+ * An interval can represent numbers between two numbers:
+ *
+ *  [1, +Inf]
+ *  ]-1,2[
+ *
+ * The left delimiter can be [ (inclusive) or ] (exclusive).
+ * The right delimiter can be [ (exclusive) or ] (inclusive).
+ * Beside numbers, you can use -Inf and +Inf for the infinite.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @see    http://en.wikipedia.org/wiki/Interval_%28mathematics%29#The_ISO_notation
+ * @deprecated since Symfony 4.2, use IdentityTranslator instead
+ */
+class Interval
+{
+    /**
+     * Tests if the given number is in the math interval.
+     *
+     * @param int    $number   A number
+     * @param string $interval An interval
+     *
+     * @return bool
+     *
+     * @throws InvalidArgumentException
+     */
+    public static function test($number, $interval)
+    {
+        $interval = trim($interval);
+
+        if (!preg_match('/^'.self::getIntervalRegexp().'$/x', $interval, $matches)) {
+            throw new InvalidArgumentException(sprintf('"%s" is not a valid interval.', $interval));
+        }
+
+        if ($matches[1]) {
+            foreach (explode(',', $matches[2]) as $n) {
+                if ($number == $n) {
+                    return true;
+                }
+            }
+        } else {
+            $leftNumber = self::convertNumber($matches['left']);
+            $rightNumber = self::convertNumber($matches['right']);
+
+            return
+                ('[' === $matches['left_delimiter'] ? $number >= $leftNumber : $number > $leftNumber)
+                && (']' === $matches['right_delimiter'] ? $number <= $rightNumber : $number < $rightNumber)
+            ;
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns a Regexp that matches valid intervals.
+     *
+     * @return string A Regexp (without the delimiters)
+     */
+    public static function getIntervalRegexp()
+    {
+        return <<<EOF
+        ({\s*
+            (\-?\d+(\.\d+)?[\s*,\s*\-?\d+(\.\d+)?]*)
+        \s*})
+
+            |
+
+        (?P<left_delimiter>[\[\]])
+            \s*
+            (?P<left>-Inf|\-?\d+(\.\d+)?)
+            \s*,\s*
+            (?P<right>\+?Inf|\-?\d+(\.\d+)?)
+            \s*
+        (?P<right_delimiter>[\[\]])
+EOF;
+    }
+
+    private static function convertNumber($number)
+    {
+        if ('-Inf' === $number) {
+            return log(0);
+        } elseif ('+Inf' === $number || 'Inf' === $number) {
+            return -log(0);
+        }
+
+        return (float) $number;
+    }
+}
diff --git a/vendor/symfony/translation/LICENSE b/vendor/symfony/translation/LICENSE
new file mode 100755
index 0000000..a677f43
--- /dev/null
+++ b/vendor/symfony/translation/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2004-2019 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.
diff --git a/vendor/symfony/translation/Loader/ArrayLoader.php b/vendor/symfony/translation/Loader/ArrayLoader.php
new file mode 100755
index 0000000..0a6f9f0
--- /dev/null
+++ b/vendor/symfony/translation/Loader/ArrayLoader.php
@@ -0,0 +1,66 @@
+<?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\Translation\Loader;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * ArrayLoader loads translations from a PHP array.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class ArrayLoader implements LoaderInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function load($resource, $locale, $domain = 'messages')
+    {
+        $this->flatten($resource);
+        $catalogue = new MessageCatalogue($locale);
+        $catalogue->add($resource, $domain);
+
+        return $catalogue;
+    }
+
+    /**
+     * Flattens an nested array of translations.
+     *
+     * The scheme used is:
+     *   'key' => ['key2' => ['key3' => 'value']]
+     * Becomes:
+     *   'key.key2.key3' => 'value'
+     *
+     * This function takes an array by reference and will modify it
+     *
+     * @param array  &$messages The array that will be flattened
+     * @param array  $subnode   Current subnode being parsed, used internally for recursive calls
+     * @param string $path      Current path being parsed, used internally for recursive calls
+     */
+    private function flatten(array &$messages, array $subnode = null, $path = null)
+    {
+        if (null === $subnode) {
+            $subnode = &$messages;
+        }
+        foreach ($subnode as $key => $value) {
+            if (\is_array($value)) {
+                $nodePath = $path ? $path.'.'.$key : $key;
+                $this->flatten($messages, $value, $nodePath);
+                if (null === $path) {
+                    unset($messages[$key]);
+                }
+            } elseif (null !== $path) {
+                $messages[$path.'.'.$key] = $value;
+            }
+        }
+    }
+}
diff --git a/vendor/symfony/translation/Loader/CsvFileLoader.php b/vendor/symfony/translation/Loader/CsvFileLoader.php
new file mode 100755
index 0000000..18cc83e
--- /dev/null
+++ b/vendor/symfony/translation/Loader/CsvFileLoader.php
@@ -0,0 +1,65 @@
+<?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\Translation\Loader;
+
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+
+/**
+ * CsvFileLoader loads translations from CSV files.
+ *
+ * @author Saša Stamenković <umpirsky@gmail.com>
+ */
+class CsvFileLoader extends FileLoader
+{
+    private $delimiter = ';';
+    private $enclosure = '"';
+    private $escape = '\\';
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function loadResource($resource)
+    {
+        $messages = [];
+
+        try {
+            $file = new \SplFileObject($resource, 'rb');
+        } catch (\RuntimeException $e) {
+            throw new NotFoundResourceException(sprintf('Error opening file "%s".', $resource), 0, $e);
+        }
+
+        $file->setFlags(\SplFileObject::READ_CSV | \SplFileObject::SKIP_EMPTY);
+        $file->setCsvControl($this->delimiter, $this->enclosure, $this->escape);
+
+        foreach ($file as $data) {
+            if ('#' !== substr($data[0], 0, 1) && isset($data[1]) && 2 === \count($data)) {
+                $messages[$data[0]] = $data[1];
+            }
+        }
+
+        return $messages;
+    }
+
+    /**
+     * Sets the delimiter, enclosure, and escape character for CSV.
+     *
+     * @param string $delimiter Delimiter character
+     * @param string $enclosure Enclosure character
+     * @param string $escape    Escape character
+     */
+    public function setCsvControl($delimiter = ';', $enclosure = '"', $escape = '\\')
+    {
+        $this->delimiter = $delimiter;
+        $this->enclosure = $enclosure;
+        $this->escape = $escape;
+    }
+}
diff --git a/vendor/symfony/translation/Loader/FileLoader.php b/vendor/symfony/translation/Loader/FileLoader.php
new file mode 100755
index 0000000..7ec54a3
--- /dev/null
+++ b/vendor/symfony/translation/Loader/FileLoader.php
@@ -0,0 +1,65 @@
+<?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\Translation\Loader;
+
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+
+/**
+ * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
+ */
+abstract class FileLoader extends ArrayLoader
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function load($resource, $locale, $domain = 'messages')
+    {
+        if (!stream_is_local($resource)) {
+            throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+        }
+
+        if (!file_exists($resource)) {
+            throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+        }
+
+        $messages = $this->loadResource($resource);
+
+        // empty resource
+        if (null === $messages) {
+            $messages = [];
+        }
+
+        // not an array
+        if (!\is_array($messages)) {
+            throw new InvalidResourceException(sprintf('Unable to load file "%s".', $resource));
+        }
+
+        $catalogue = parent::load($messages, $locale, $domain);
+
+        if (class_exists('Symfony\Component\Config\Resource\FileResource')) {
+            $catalogue->addResource(new FileResource($resource));
+        }
+
+        return $catalogue;
+    }
+
+    /**
+     * @param string $resource
+     *
+     * @return array
+     *
+     * @throws InvalidResourceException if stream content has an invalid format
+     */
+    abstract protected function loadResource($resource);
+}
diff --git a/vendor/symfony/translation/Loader/IcuDatFileLoader.php b/vendor/symfony/translation/Loader/IcuDatFileLoader.php
new file mode 100755
index 0000000..7bbf4c2
--- /dev/null
+++ b/vendor/symfony/translation/Loader/IcuDatFileLoader.php
@@ -0,0 +1,61 @@
+<?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\Translation\Loader;
+
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * IcuResFileLoader loads translations from a resource bundle.
+ *
+ * @author stealth35
+ */
+class IcuDatFileLoader extends IcuResFileLoader
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function load($resource, $locale, $domain = 'messages')
+    {
+        if (!stream_is_local($resource.'.dat')) {
+            throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+        }
+
+        if (!file_exists($resource.'.dat')) {
+            throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+        }
+
+        try {
+            $rb = new \ResourceBundle($locale, $resource);
+        } catch (\Exception $e) {
+            $rb = null;
+        }
+
+        if (!$rb) {
+            throw new InvalidResourceException(sprintf('Cannot load resource "%s"', $resource));
+        } elseif (intl_is_failure($rb->getErrorCode())) {
+            throw new InvalidResourceException($rb->getErrorMessage(), $rb->getErrorCode());
+        }
+
+        $messages = $this->flatten($rb);
+        $catalogue = new MessageCatalogue($locale);
+        $catalogue->add($messages, $domain);
+
+        if (class_exists('Symfony\Component\Config\Resource\FileResource')) {
+            $catalogue->addResource(new FileResource($resource.'.dat'));
+        }
+
+        return $catalogue;
+    }
+}
diff --git a/vendor/symfony/translation/Loader/IcuResFileLoader.php b/vendor/symfony/translation/Loader/IcuResFileLoader.php
new file mode 100755
index 0000000..005f260
--- /dev/null
+++ b/vendor/symfony/translation/Loader/IcuResFileLoader.php
@@ -0,0 +1,91 @@
+<?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\Translation\Loader;
+
+use Symfony\Component\Config\Resource\DirectoryResource;
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * IcuResFileLoader loads translations from a resource bundle.
+ *
+ * @author stealth35
+ */
+class IcuResFileLoader implements LoaderInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function load($resource, $locale, $domain = 'messages')
+    {
+        if (!stream_is_local($resource)) {
+            throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+        }
+
+        if (!is_dir($resource)) {
+            throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+        }
+
+        try {
+            $rb = new \ResourceBundle($locale, $resource);
+        } catch (\Exception $e) {
+            $rb = null;
+        }
+
+        if (!$rb) {
+            throw new InvalidResourceException(sprintf('Cannot load resource "%s"', $resource));
+        } elseif (intl_is_failure($rb->getErrorCode())) {
+            throw new InvalidResourceException($rb->getErrorMessage(), $rb->getErrorCode());
+        }
+
+        $messages = $this->flatten($rb);
+        $catalogue = new MessageCatalogue($locale);
+        $catalogue->add($messages, $domain);
+
+        if (class_exists('Symfony\Component\Config\Resource\DirectoryResource')) {
+            $catalogue->addResource(new DirectoryResource($resource));
+        }
+
+        return $catalogue;
+    }
+
+    /**
+     * Flattens an ResourceBundle.
+     *
+     * The scheme used is:
+     *   key { key2 { key3 { "value" } } }
+     * Becomes:
+     *   'key.key2.key3' => 'value'
+     *
+     * This function takes an array by reference and will modify it
+     *
+     * @param \ResourceBundle $rb       The ResourceBundle that will be flattened
+     * @param array           $messages Used internally for recursive calls
+     * @param string          $path     Current path being parsed, used internally for recursive calls
+     *
+     * @return array the flattened ResourceBundle
+     */
+    protected function flatten(\ResourceBundle $rb, array &$messages = [], $path = null)
+    {
+        foreach ($rb as $key => $value) {
+            $nodePath = $path ? $path.'.'.$key : $key;
+            if ($value instanceof \ResourceBundle) {
+                $this->flatten($value, $messages, $nodePath);
+            } else {
+                $messages[$nodePath] = $value;
+            }
+        }
+
+        return $messages;
+    }
+}
diff --git a/vendor/symfony/translation/Loader/IniFileLoader.php b/vendor/symfony/translation/Loader/IniFileLoader.php
new file mode 100755
index 0000000..11d9b27
--- /dev/null
+++ b/vendor/symfony/translation/Loader/IniFileLoader.php
@@ -0,0 +1,28 @@
+<?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\Translation\Loader;
+
+/**
+ * IniFileLoader loads translations from an ini file.
+ *
+ * @author stealth35
+ */
+class IniFileLoader extends FileLoader
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function loadResource($resource)
+    {
+        return parse_ini_file($resource, true);
+    }
+}
diff --git a/vendor/symfony/translation/Loader/JsonFileLoader.php b/vendor/symfony/translation/Loader/JsonFileLoader.php
new file mode 100755
index 0000000..5267212
--- /dev/null
+++ b/vendor/symfony/translation/Loader/JsonFileLoader.php
@@ -0,0 +1,64 @@
+<?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\Translation\Loader;
+
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+
+/**
+ * JsonFileLoader loads translations from an json file.
+ *
+ * @author singles
+ */
+class JsonFileLoader extends FileLoader
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function loadResource($resource)
+    {
+        $messages = [];
+        if ($data = file_get_contents($resource)) {
+            $messages = json_decode($data, true);
+
+            if (0 < $errorCode = json_last_error()) {
+                throw new InvalidResourceException(sprintf('Error parsing JSON - %s', $this->getJSONErrorMessage($errorCode)));
+            }
+        }
+
+        return $messages;
+    }
+
+    /**
+     * Translates JSON_ERROR_* constant into meaningful message.
+     *
+     * @param int $errorCode Error code returned by json_last_error() call
+     *
+     * @return string Message string
+     */
+    private function getJSONErrorMessage($errorCode)
+    {
+        switch ($errorCode) {
+            case JSON_ERROR_DEPTH:
+                return 'Maximum stack depth exceeded';
+            case JSON_ERROR_STATE_MISMATCH:
+                return 'Underflow or the modes mismatch';
+            case JSON_ERROR_CTRL_CHAR:
+                return 'Unexpected control character found';
+            case JSON_ERROR_SYNTAX:
+                return 'Syntax error, malformed JSON';
+            case JSON_ERROR_UTF8:
+                return 'Malformed UTF-8 characters, possibly incorrectly encoded';
+            default:
+                return 'Unknown error';
+        }
+    }
+}
diff --git a/vendor/symfony/translation/Loader/LoaderInterface.php b/vendor/symfony/translation/Loader/LoaderInterface.php
new file mode 100755
index 0000000..1785402
--- /dev/null
+++ b/vendor/symfony/translation/Loader/LoaderInterface.php
@@ -0,0 +1,38 @@
+<?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\Translation\Loader;
+
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * LoaderInterface is the interface implemented by all translation loaders.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface LoaderInterface
+{
+    /**
+     * Loads a locale.
+     *
+     * @param mixed  $resource A resource
+     * @param string $locale   A locale
+     * @param string $domain   The domain
+     *
+     * @return MessageCatalogue A MessageCatalogue instance
+     *
+     * @throws NotFoundResourceException when the resource cannot be found
+     * @throws InvalidResourceException  when the resource cannot be loaded
+     */
+    public function load($resource, $locale, $domain = 'messages');
+}
diff --git a/vendor/symfony/translation/Loader/MoFileLoader.php b/vendor/symfony/translation/Loader/MoFileLoader.php
new file mode 100755
index 0000000..16a3dfc
--- /dev/null
+++ b/vendor/symfony/translation/Loader/MoFileLoader.php
@@ -0,0 +1,145 @@
+<?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\Translation\Loader;
+
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+
+/**
+ * @copyright Copyright (c) 2010, Union of RAD http://union-of-rad.org (http://lithify.me/)
+ */
+class MoFileLoader extends FileLoader
+{
+    /**
+     * Magic used for validating the format of a MO file as well as
+     * detecting if the machine used to create that file was little endian.
+     */
+    const MO_LITTLE_ENDIAN_MAGIC = 0x950412de;
+
+    /**
+     * Magic used for validating the format of a MO file as well as
+     * detecting if the machine used to create that file was big endian.
+     */
+    const MO_BIG_ENDIAN_MAGIC = 0xde120495;
+
+    /**
+     * The size of the header of a MO file in bytes.
+     */
+    const MO_HEADER_SIZE = 28;
+
+    /**
+     * Parses machine object (MO) format, independent of the machine's endian it
+     * was created on. Both 32bit and 64bit systems are supported.
+     *
+     * {@inheritdoc}
+     */
+    protected function loadResource($resource)
+    {
+        $stream = fopen($resource, 'r');
+
+        $stat = fstat($stream);
+
+        if ($stat['size'] < self::MO_HEADER_SIZE) {
+            throw new InvalidResourceException('MO stream content has an invalid format.');
+        }
+        $magic = unpack('V1', fread($stream, 4));
+        $magic = hexdec(substr(dechex(current($magic)), -8));
+
+        if (self::MO_LITTLE_ENDIAN_MAGIC == $magic) {
+            $isBigEndian = false;
+        } elseif (self::MO_BIG_ENDIAN_MAGIC == $magic) {
+            $isBigEndian = true;
+        } else {
+            throw new InvalidResourceException('MO stream content has an invalid format.');
+        }
+
+        // formatRevision
+        $this->readLong($stream, $isBigEndian);
+        $count = $this->readLong($stream, $isBigEndian);
+        $offsetId = $this->readLong($stream, $isBigEndian);
+        $offsetTranslated = $this->readLong($stream, $isBigEndian);
+        // sizeHashes
+        $this->readLong($stream, $isBigEndian);
+        // offsetHashes
+        $this->readLong($stream, $isBigEndian);
+
+        $messages = [];
+
+        for ($i = 0; $i < $count; ++$i) {
+            $pluralId = null;
+            $translated = null;
+
+            fseek($stream, $offsetId + $i * 8);
+
+            $length = $this->readLong($stream, $isBigEndian);
+            $offset = $this->readLong($stream, $isBigEndian);
+
+            if ($length < 1) {
+                continue;
+            }
+
+            fseek($stream, $offset);
+            $singularId = fread($stream, $length);
+
+            if (false !== strpos($singularId, "\000")) {
+                list($singularId, $pluralId) = explode("\000", $singularId);
+            }
+
+            fseek($stream, $offsetTranslated + $i * 8);
+            $length = $this->readLong($stream, $isBigEndian);
+            $offset = $this->readLong($stream, $isBigEndian);
+
+            if ($length < 1) {
+                continue;
+            }
+
+            fseek($stream, $offset);
+            $translated = fread($stream, $length);
+
+            if (false !== strpos($translated, "\000")) {
+                $translated = explode("\000", $translated);
+            }
+
+            $ids = ['singular' => $singularId, 'plural' => $pluralId];
+            $item = compact('ids', 'translated');
+
+            if (\is_array($item['translated'])) {
+                $messages[$item['ids']['singular']] = stripcslashes($item['translated'][0]);
+                if (isset($item['ids']['plural'])) {
+                    $plurals = [];
+                    foreach ($item['translated'] as $plural => $translated) {
+                        $plurals[] = sprintf('{%d} %s', $plural, $translated);
+                    }
+                    $messages[$item['ids']['plural']] = stripcslashes(implode('|', $plurals));
+                }
+            } elseif (!empty($item['ids']['singular'])) {
+                $messages[$item['ids']['singular']] = stripcslashes($item['translated']);
+            }
+        }
+
+        fclose($stream);
+
+        return array_filter($messages);
+    }
+
+    /**
+     * Reads an unsigned long from stream respecting endianness.
+     *
+     * @param resource $stream
+     */
+    private function readLong($stream, bool $isBigEndian): int
+    {
+        $result = unpack($isBigEndian ? 'N1' : 'V1', fread($stream, 4));
+        $result = current($result);
+
+        return (int) substr($result, -8);
+    }
+}
diff --git a/vendor/symfony/translation/Loader/PhpFileLoader.php b/vendor/symfony/translation/Loader/PhpFileLoader.php
new file mode 100755
index 0000000..a0050e8
--- /dev/null
+++ b/vendor/symfony/translation/Loader/PhpFileLoader.php
@@ -0,0 +1,28 @@
+<?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\Translation\Loader;
+
+/**
+ * PhpFileLoader loads translations from PHP files returning an array of translations.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class PhpFileLoader extends FileLoader
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function loadResource($resource)
+    {
+        return require $resource;
+    }
+}
diff --git a/vendor/symfony/translation/Loader/PoFileLoader.php b/vendor/symfony/translation/Loader/PoFileLoader.php
new file mode 100755
index 0000000..1412a78
--- /dev/null
+++ b/vendor/symfony/translation/Loader/PoFileLoader.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\Translation\Loader;
+
+/**
+ * @copyright Copyright (c) 2010, Union of RAD http://union-of-rad.org (http://lithify.me/)
+ * @copyright Copyright (c) 2012, Clemens Tolboom
+ */
+class PoFileLoader extends FileLoader
+{
+    /**
+     * Parses portable object (PO) format.
+     *
+     * From http://www.gnu.org/software/gettext/manual/gettext.html#PO-Files
+     * we should be able to parse files having:
+     *
+     * white-space
+     * #  translator-comments
+     * #. extracted-comments
+     * #: reference...
+     * #, flag...
+     * #| msgid previous-untranslated-string
+     * msgid untranslated-string
+     * msgstr translated-string
+     *
+     * extra or different lines are:
+     *
+     * #| msgctxt previous-context
+     * #| msgid previous-untranslated-string
+     * msgctxt context
+     *
+     * #| msgid previous-untranslated-string-singular
+     * #| msgid_plural previous-untranslated-string-plural
+     * msgid untranslated-string-singular
+     * msgid_plural untranslated-string-plural
+     * msgstr[0] translated-string-case-0
+     * ...
+     * msgstr[N] translated-string-case-n
+     *
+     * The definition states:
+     * - white-space and comments are optional.
+     * - msgid "" that an empty singleline defines a header.
+     *
+     * This parser sacrifices some features of the reference implementation the
+     * differences to that implementation are as follows.
+     * - No support for comments spanning multiple lines.
+     * - Translator and extracted comments are treated as being the same type.
+     * - Message IDs are allowed to have other encodings as just US-ASCII.
+     *
+     * Items with an empty id are ignored.
+     *
+     * {@inheritdoc}
+     */
+    protected function loadResource($resource)
+    {
+        $stream = fopen($resource, 'r');
+
+        $defaults = [
+            'ids' => [],
+            'translated' => null,
+        ];
+
+        $messages = [];
+        $item = $defaults;
+        $flags = [];
+
+        while ($line = fgets($stream)) {
+            $line = trim($line);
+
+            if ('' === $line) {
+                // Whitespace indicated current item is done
+                if (!\in_array('fuzzy', $flags)) {
+                    $this->addMessage($messages, $item);
+                }
+                $item = $defaults;
+                $flags = [];
+            } elseif ('#,' === substr($line, 0, 2)) {
+                $flags = array_map('trim', explode(',', substr($line, 2)));
+            } elseif ('msgid "' === substr($line, 0, 7)) {
+                // We start a new msg so save previous
+                // TODO: this fails when comments or contexts are added
+                $this->addMessage($messages, $item);
+                $item = $defaults;
+                $item['ids']['singular'] = substr($line, 7, -1);
+            } elseif ('msgstr "' === substr($line, 0, 8)) {
+                $item['translated'] = substr($line, 8, -1);
+            } elseif ('"' === $line[0]) {
+                $continues = isset($item['translated']) ? 'translated' : 'ids';
+
+                if (\is_array($item[$continues])) {
+                    end($item[$continues]);
+                    $item[$continues][key($item[$continues])] .= substr($line, 1, -1);
+                } else {
+                    $item[$continues] .= substr($line, 1, -1);
+                }
+            } elseif ('msgid_plural "' === substr($line, 0, 14)) {
+                $item['ids']['plural'] = substr($line, 14, -1);
+            } elseif ('msgstr[' === substr($line, 0, 7)) {
+                $size = strpos($line, ']');
+                $item['translated'][(int) substr($line, 7, 1)] = substr($line, $size + 3, -1);
+            }
+        }
+        // save last item
+        if (!\in_array('fuzzy', $flags)) {
+            $this->addMessage($messages, $item);
+        }
+        fclose($stream);
+
+        return $messages;
+    }
+
+    /**
+     * Save a translation item to the messages.
+     *
+     * A .po file could contain by error missing plural indexes. We need to
+     * fix these before saving them.
+     */
+    private function addMessage(array &$messages, array $item)
+    {
+        if (\is_array($item['translated'])) {
+            $messages[stripcslashes($item['ids']['singular'])] = stripcslashes($item['translated'][0]);
+            if (isset($item['ids']['plural'])) {
+                $plurals = $item['translated'];
+                // PO are by definition indexed so sort by index.
+                ksort($plurals);
+                // Make sure every index is filled.
+                end($plurals);
+                $count = key($plurals);
+                // Fill missing spots with '-'.
+                $empties = array_fill(0, $count + 1, '-');
+                $plurals += $empties;
+                ksort($plurals);
+                $messages[stripcslashes($item['ids']['plural'])] = stripcslashes(implode('|', $plurals));
+            }
+        } elseif (!empty($item['ids']['singular'])) {
+            $messages[stripcslashes($item['ids']['singular'])] = stripcslashes($item['translated']);
+        }
+    }
+}
diff --git a/vendor/symfony/translation/Loader/QtFileLoader.php b/vendor/symfony/translation/Loader/QtFileLoader.php
new file mode 100755
index 0000000..2d4a4c0
--- /dev/null
+++ b/vendor/symfony/translation/Loader/QtFileLoader.php
@@ -0,0 +1,77 @@
+<?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\Translation\Loader;
+
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Config\Util\XmlUtils;
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * QtFileLoader loads translations from QT Translations XML files.
+ *
+ * @author Benjamin Eberlei <kontakt@beberlei.de>
+ */
+class QtFileLoader implements LoaderInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function load($resource, $locale, $domain = 'messages')
+    {
+        if (!stream_is_local($resource)) {
+            throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+        }
+
+        if (!file_exists($resource)) {
+            throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+        }
+
+        try {
+            $dom = XmlUtils::loadFile($resource);
+        } catch (\InvalidArgumentException $e) {
+            throw new InvalidResourceException(sprintf('Unable to load "%s".', $resource), $e->getCode(), $e);
+        }
+
+        $internalErrors = libxml_use_internal_errors(true);
+        libxml_clear_errors();
+
+        $xpath = new \DOMXPath($dom);
+        $nodes = $xpath->evaluate('//TS/context/name[text()="'.$domain.'"]');
+
+        $catalogue = new MessageCatalogue($locale);
+        if (1 == $nodes->length) {
+            $translations = $nodes->item(0)->nextSibling->parentNode->parentNode->getElementsByTagName('message');
+            foreach ($translations as $translation) {
+                $translationValue = (string) $translation->getElementsByTagName('translation')->item(0)->nodeValue;
+
+                if (!empty($translationValue)) {
+                    $catalogue->set(
+                        (string) $translation->getElementsByTagName('source')->item(0)->nodeValue,
+                        $translationValue,
+                        $domain
+                    );
+                }
+                $translation = $translation->nextSibling;
+            }
+
+            if (class_exists('Symfony\Component\Config\Resource\FileResource')) {
+                $catalogue->addResource(new FileResource($resource));
+            }
+        }
+
+        libxml_use_internal_errors($internalErrors);
+
+        return $catalogue;
+    }
+}
diff --git a/vendor/symfony/translation/Loader/XliffFileLoader.php b/vendor/symfony/translation/Loader/XliffFileLoader.php
new file mode 100755
index 0000000..6e01a71
--- /dev/null
+++ b/vendor/symfony/translation/Loader/XliffFileLoader.php
@@ -0,0 +1,212 @@
+<?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\Translation\Loader;
+
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Config\Util\XmlUtils;
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Util\XliffUtils;
+
+/**
+ * XliffFileLoader loads translations from XLIFF files.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class XliffFileLoader implements LoaderInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function load($resource, $locale, $domain = 'messages')
+    {
+        if (!stream_is_local($resource)) {
+            throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+        }
+
+        if (!file_exists($resource)) {
+            throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+        }
+
+        $catalogue = new MessageCatalogue($locale);
+        $this->extract($resource, $catalogue, $domain);
+
+        if (class_exists('Symfony\Component\Config\Resource\FileResource')) {
+            $catalogue->addResource(new FileResource($resource));
+        }
+
+        return $catalogue;
+    }
+
+    private function extract($resource, MessageCatalogue $catalogue, $domain)
+    {
+        try {
+            $dom = XmlUtils::loadFile($resource);
+        } catch (\InvalidArgumentException $e) {
+            throw new InvalidResourceException(sprintf('Unable to load "%s": %s', $resource, $e->getMessage()), $e->getCode(), $e);
+        }
+
+        $xliffVersion = XliffUtils::getVersionNumber($dom);
+        if ($errors = XliffUtils::validateSchema($dom)) {
+            throw new InvalidResourceException(sprintf('Invalid resource provided: "%s"; Errors: %s', $resource, XliffUtils::getErrorsAsString($errors)));
+        }
+
+        if ('1.2' === $xliffVersion) {
+            $this->extractXliff1($dom, $catalogue, $domain);
+        }
+
+        if ('2.0' === $xliffVersion) {
+            $this->extractXliff2($dom, $catalogue, $domain);
+        }
+    }
+
+    /**
+     * Extract messages and metadata from DOMDocument into a MessageCatalogue.
+     *
+     * @param \DOMDocument     $dom       Source to extract messages and metadata
+     * @param MessageCatalogue $catalogue Catalogue where we'll collect messages and metadata
+     * @param string           $domain    The domain
+     */
+    private function extractXliff1(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain)
+    {
+        $xml = simplexml_import_dom($dom);
+        $encoding = strtoupper($dom->encoding);
+
+        $namespace = 'urn:oasis:names:tc:xliff:document:1.2';
+        $xml->registerXPathNamespace('xliff', $namespace);
+
+        foreach ($xml->xpath('//xliff:file') as $file) {
+            $fileAttributes = $file->attributes();
+
+            $file->registerXPathNamespace('xliff', $namespace);
+
+            foreach ($file->xpath('.//xliff:trans-unit') as $translation) {
+                $attributes = $translation->attributes();
+
+                if (!(isset($attributes['resname']) || isset($translation->source))) {
+                    continue;
+                }
+
+                $source = isset($attributes['resname']) && $attributes['resname'] ? $attributes['resname'] : $translation->source;
+                // If the xlf file has another encoding specified, try to convert it because
+                // simple_xml will always return utf-8 encoded values
+                $target = $this->utf8ToCharset((string) ($translation->target ?? $translation->source), $encoding);
+
+                $catalogue->set((string) $source, $target, $domain);
+
+                $metadata = [
+                    'source' => (string) $translation->source,
+                    'file' => [
+                        'original' => (string) $fileAttributes['original'],
+                    ],
+                ];
+                if ($notes = $this->parseNotesMetadata($translation->note, $encoding)) {
+                    $metadata['notes'] = $notes;
+                }
+
+                if (isset($translation->target) && $translation->target->attributes()) {
+                    $metadata['target-attributes'] = [];
+                    foreach ($translation->target->attributes() as $key => $value) {
+                        $metadata['target-attributes'][$key] = (string) $value;
+                    }
+                }
+
+                if (isset($attributes['id'])) {
+                    $metadata['id'] = (string) $attributes['id'];
+                }
+
+                $catalogue->setMetadata((string) $source, $metadata, $domain);
+            }
+        }
+    }
+
+    private function extractXliff2(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain)
+    {
+        $xml = simplexml_import_dom($dom);
+        $encoding = strtoupper($dom->encoding);
+
+        $xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:2.0');
+
+        foreach ($xml->xpath('//xliff:unit') as $unit) {
+            foreach ($unit->segment as $segment) {
+                $source = $segment->source;
+
+                // If the xlf file has another encoding specified, try to convert it because
+                // simple_xml will always return utf-8 encoded values
+                $target = $this->utf8ToCharset((string) (isset($segment->target) ? $segment->target : $source), $encoding);
+
+                $catalogue->set((string) $source, $target, $domain);
+
+                $metadata = [];
+                if (isset($segment->target) && $segment->target->attributes()) {
+                    $metadata['target-attributes'] = [];
+                    foreach ($segment->target->attributes() as $key => $value) {
+                        $metadata['target-attributes'][$key] = (string) $value;
+                    }
+                }
+
+                if (isset($unit->notes)) {
+                    $metadata['notes'] = [];
+                    foreach ($unit->notes->note as $noteNode) {
+                        $note = [];
+                        foreach ($noteNode->attributes() as $key => $value) {
+                            $note[$key] = (string) $value;
+                        }
+                        $note['content'] = (string) $noteNode;
+                        $metadata['notes'][] = $note;
+                    }
+                }
+
+                $catalogue->setMetadata((string) $source, $metadata, $domain);
+            }
+        }
+    }
+
+    /**
+     * Convert a UTF8 string to the specified encoding.
+     */
+    private function utf8ToCharset(string $content, string $encoding = null): string
+    {
+        if ('UTF-8' !== $encoding && !empty($encoding)) {
+            return mb_convert_encoding($content, $encoding, 'UTF-8');
+        }
+
+        return $content;
+    }
+
+    private function parseNotesMetadata(\SimpleXMLElement $noteElement = null, string $encoding = null): array
+    {
+        $notes = [];
+
+        if (null === $noteElement) {
+            return $notes;
+        }
+
+        /** @var \SimpleXMLElement $xmlNote */
+        foreach ($noteElement as $xmlNote) {
+            $noteAttributes = $xmlNote->attributes();
+            $note = ['content' => $this->utf8ToCharset((string) $xmlNote, $encoding)];
+            if (isset($noteAttributes['priority'])) {
+                $note['priority'] = (int) $noteAttributes['priority'];
+            }
+
+            if (isset($noteAttributes['from'])) {
+                $note['from'] = (string) $noteAttributes['from'];
+            }
+
+            $notes[] = $note;
+        }
+
+        return $notes;
+    }
+}
diff --git a/vendor/symfony/translation/Loader/YamlFileLoader.php b/vendor/symfony/translation/Loader/YamlFileLoader.php
new file mode 100755
index 0000000..584a055
--- /dev/null
+++ b/vendor/symfony/translation/Loader/YamlFileLoader.php
@@ -0,0 +1,50 @@
+<?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\Translation\Loader;
+
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\LogicException;
+use Symfony\Component\Yaml\Exception\ParseException;
+use Symfony\Component\Yaml\Parser as YamlParser;
+use Symfony\Component\Yaml\Yaml;
+
+/**
+ * YamlFileLoader loads translations from Yaml files.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class YamlFileLoader extends FileLoader
+{
+    private $yamlParser;
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function loadResource($resource)
+    {
+        if (null === $this->yamlParser) {
+            if (!class_exists('Symfony\Component\Yaml\Parser')) {
+                throw new LogicException('Loading translations from the YAML format requires the Symfony Yaml component.');
+            }
+
+            $this->yamlParser = new YamlParser();
+        }
+
+        try {
+            $messages = $this->yamlParser->parseFile($resource, Yaml::PARSE_CONSTANT);
+        } catch (ParseException $e) {
+            throw new InvalidResourceException(sprintf('Error parsing YAML, invalid file "%s"', $resource), 0, $e);
+        }
+
+        return $messages;
+    }
+}
diff --git a/vendor/symfony/translation/LoggingTranslator.php b/vendor/symfony/translation/LoggingTranslator.php
new file mode 100755
index 0000000..2996167
--- /dev/null
+++ b/vendor/symfony/translation/LoggingTranslator.php
@@ -0,0 +1,157 @@
+<?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\Translation;
+
+use Psr\Log\LoggerInterface;
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface;
+use Symfony\Contracts\Translation\LocaleAwareInterface;
+use Symfony\Contracts\Translation\TranslatorInterface;
+
+/**
+ * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
+ */
+class LoggingTranslator implements TranslatorInterface, LegacyTranslatorInterface, TranslatorBagInterface
+{
+    /**
+     * @var TranslatorInterface|TranslatorBagInterface
+     */
+    private $translator;
+
+    private $logger;
+
+    /**
+     * @param TranslatorInterface $translator The translator must implement TranslatorBagInterface
+     * @param LoggerInterface     $logger
+     */
+    public function __construct($translator, LoggerInterface $logger)
+    {
+        if (!$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) {
+            throw new \TypeError(sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator)));
+        }
+        if (!$translator instanceof TranslatorBagInterface || !$translator instanceof LocaleAwareInterface) {
+            throw new InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface, TranslatorBagInterface and LocaleAwareInterface.', \get_class($translator)));
+        }
+
+        $this->translator = $translator;
+        $this->logger = $logger;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function trans($id, array $parameters = [], $domain = null, $locale = null)
+    {
+        $trans = $this->translator->trans($id, $parameters, $domain, $locale);
+        $this->log($id, $domain, $locale);
+
+        return $trans;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @deprecated since Symfony 4.2, use the trans() method instead with a %count% parameter
+     */
+    public function transChoice($id, $number, array $parameters = [], $domain = null, $locale = null)
+    {
+        @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2, use the trans() one instead with a "%%count%%" parameter.', __METHOD__), E_USER_DEPRECATED);
+
+        if ($this->translator instanceof TranslatorInterface) {
+            $trans = $this->translator->trans($id, ['%count%' => $number] + $parameters, $domain, $locale);
+        } else {
+            $trans = $this->translator->transChoice($id, $number, $parameters, $domain, $locale);
+        }
+
+        $this->log($id, $domain, $locale);
+
+        return $trans;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setLocale($locale)
+    {
+        $prev = $this->translator->getLocale();
+        $this->translator->setLocale($locale);
+        if ($prev === $locale) {
+            return;
+        }
+
+        $this->logger->debug(sprintf('The locale of the translator has changed from "%s" to "%s".', $prev, $locale));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getLocale()
+    {
+        return $this->translator->getLocale();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getCatalogue($locale = null)
+    {
+        return $this->translator->getCatalogue($locale);
+    }
+
+    /**
+     * Gets the fallback locales.
+     *
+     * @return array The fallback locales
+     */
+    public function getFallbackLocales()
+    {
+        if ($this->translator instanceof Translator || method_exists($this->translator, 'getFallbackLocales')) {
+            return $this->translator->getFallbackLocales();
+        }
+
+        return [];
+    }
+
+    /**
+     * Passes through all unknown calls onto the translator object.
+     */
+    public function __call($method, $args)
+    {
+        return $this->translator->{$method}(...$args);
+    }
+
+    /**
+     * Logs for missing translations.
+     *
+     * @param string      $id
+     * @param string|null $domain
+     * @param string|null $locale
+     */
+    private function log($id, $domain, $locale)
+    {
+        if (null === $domain) {
+            $domain = 'messages';
+        }
+
+        $id = (string) $id;
+        $catalogue = $this->translator->getCatalogue($locale);
+        if ($catalogue->defines($id, $domain)) {
+            return;
+        }
+
+        if ($catalogue->has($id, $domain)) {
+            $this->logger->debug('Translation use fallback catalogue.', ['id' => $id, 'domain' => $domain, 'locale' => $catalogue->getLocale()]);
+        } else {
+            $this->logger->warning('Translation not found.', ['id' => $id, 'domain' => $domain, 'locale' => $catalogue->getLocale()]);
+        }
+    }
+}
diff --git a/vendor/symfony/translation/MessageCatalogue.php b/vendor/symfony/translation/MessageCatalogue.php
new file mode 100755
index 0000000..19afb90
--- /dev/null
+++ b/vendor/symfony/translation/MessageCatalogue.php
@@ -0,0 +1,301 @@
+<?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\Translation;
+
+use Symfony\Component\Config\Resource\ResourceInterface;
+use Symfony\Component\Translation\Exception\LogicException;
+
+/**
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterface
+{
+    private $messages = [];
+    private $metadata = [];
+    private $resources = [];
+    private $locale;
+    private $fallbackCatalogue;
+    private $parent;
+
+    /**
+     * @param string $locale   The locale
+     * @param array  $messages An array of messages classified by domain
+     */
+    public function __construct(?string $locale, array $messages = [])
+    {
+        $this->locale = $locale;
+        $this->messages = $messages;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getLocale()
+    {
+        return $this->locale;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDomains()
+    {
+        $domains = [];
+        $suffixLength = \strlen(self::INTL_DOMAIN_SUFFIX);
+
+        foreach ($this->messages as $domain => $messages) {
+            if (\strlen($domain) > $suffixLength && false !== $i = strpos($domain, self::INTL_DOMAIN_SUFFIX, -$suffixLength)) {
+                $domain = substr($domain, 0, $i);
+            }
+            $domains[$domain] = $domain;
+        }
+
+        return array_values($domains);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function all($domain = null)
+    {
+        if (null !== $domain) {
+            return ($this->messages[$domain.self::INTL_DOMAIN_SUFFIX] ?? []) + ($this->messages[$domain] ?? []);
+        }
+
+        $allMessages = [];
+        $suffixLength = \strlen(self::INTL_DOMAIN_SUFFIX);
+
+        foreach ($this->messages as $domain => $messages) {
+            if (\strlen($domain) > $suffixLength && false !== $i = strpos($domain, self::INTL_DOMAIN_SUFFIX, -$suffixLength)) {
+                $domain = substr($domain, 0, $i);
+                $allMessages[$domain] = $messages + ($allMessages[$domain] ?? []);
+            } else {
+                $allMessages[$domain] = ($allMessages[$domain] ?? []) + $messages;
+            }
+        }
+
+        return $allMessages;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function set($id, $translation, $domain = 'messages')
+    {
+        $this->add([$id => $translation], $domain);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function has($id, $domain = 'messages')
+    {
+        if (isset($this->messages[$domain][$id]) || isset($this->messages[$domain.self::INTL_DOMAIN_SUFFIX][$id])) {
+            return true;
+        }
+
+        if (null !== $this->fallbackCatalogue) {
+            return $this->fallbackCatalogue->has($id, $domain);
+        }
+
+        return false;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function defines($id, $domain = 'messages')
+    {
+        return isset($this->messages[$domain][$id]) || isset($this->messages[$domain.self::INTL_DOMAIN_SUFFIX][$id]);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function get($id, $domain = 'messages')
+    {
+        if (isset($this->messages[$domain.self::INTL_DOMAIN_SUFFIX][$id])) {
+            return $this->messages[$domain.self::INTL_DOMAIN_SUFFIX][$id];
+        }
+
+        if (isset($this->messages[$domain][$id])) {
+            return $this->messages[$domain][$id];
+        }
+
+        if (null !== $this->fallbackCatalogue) {
+            return $this->fallbackCatalogue->get($id, $domain);
+        }
+
+        return $id;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function replace($messages, $domain = 'messages')
+    {
+        unset($this->messages[$domain], $this->messages[$domain.self::INTL_DOMAIN_SUFFIX]);
+
+        $this->add($messages, $domain);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function add($messages, $domain = 'messages')
+    {
+        if (!isset($this->messages[$domain])) {
+            $this->messages[$domain] = $messages;
+        } else {
+            $this->messages[$domain] = array_replace($this->messages[$domain], $messages);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addCatalogue(MessageCatalogueInterface $catalogue)
+    {
+        if ($catalogue->getLocale() !== $this->locale) {
+            throw new LogicException(sprintf('Cannot add a catalogue for locale "%s" as the current locale for this catalogue is "%s"', $catalogue->getLocale(), $this->locale));
+        }
+
+        foreach ($catalogue->all() as $domain => $messages) {
+            if ($intlMessages = $catalogue->all($domain.self::INTL_DOMAIN_SUFFIX)) {
+                $this->add($intlMessages, $domain.self::INTL_DOMAIN_SUFFIX);
+                $messages = array_diff_key($messages, $intlMessages);
+            }
+            $this->add($messages, $domain);
+        }
+
+        foreach ($catalogue->getResources() as $resource) {
+            $this->addResource($resource);
+        }
+
+        if ($catalogue instanceof MetadataAwareInterface) {
+            $metadata = $catalogue->getMetadata('', '');
+            $this->addMetadata($metadata);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addFallbackCatalogue(MessageCatalogueInterface $catalogue)
+    {
+        // detect circular references
+        $c = $catalogue;
+        while ($c = $c->getFallbackCatalogue()) {
+            if ($c->getLocale() === $this->getLocale()) {
+                throw new LogicException(sprintf('Circular reference detected when adding a fallback catalogue for locale "%s".', $catalogue->getLocale()));
+            }
+        }
+
+        $c = $this;
+        do {
+            if ($c->getLocale() === $catalogue->getLocale()) {
+                throw new LogicException(sprintf('Circular reference detected when adding a fallback catalogue for locale "%s".', $catalogue->getLocale()));
+            }
+
+            foreach ($catalogue->getResources() as $resource) {
+                $c->addResource($resource);
+            }
+        } while ($c = $c->parent);
+
+        $catalogue->parent = $this;
+        $this->fallbackCatalogue = $catalogue;
+
+        foreach ($catalogue->getResources() as $resource) {
+            $this->addResource($resource);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getFallbackCatalogue()
+    {
+        return $this->fallbackCatalogue;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getResources()
+    {
+        return array_values($this->resources);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addResource(ResourceInterface $resource)
+    {
+        $this->resources[$resource->__toString()] = $resource;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getMetadata($key = '', $domain = 'messages')
+    {
+        if ('' == $domain) {
+            return $this->metadata;
+        }
+
+        if (isset($this->metadata[$domain])) {
+            if ('' == $key) {
+                return $this->metadata[$domain];
+            }
+
+            if (isset($this->metadata[$domain][$key])) {
+                return $this->metadata[$domain][$key];
+            }
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setMetadata($key, $value, $domain = 'messages')
+    {
+        $this->metadata[$domain][$key] = $value;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function deleteMetadata($key = '', $domain = 'messages')
+    {
+        if ('' == $domain) {
+            $this->metadata = [];
+        } elseif ('' == $key) {
+            unset($this->metadata[$domain]);
+        } else {
+            unset($this->metadata[$domain][$key]);
+        }
+    }
+
+    /**
+     * Adds current values with the new values.
+     *
+     * @param array $values Values to add
+     */
+    private function addMetadata(array $values)
+    {
+        foreach ($values as $domain => $keys) {
+            foreach ($keys as $key => $value) {
+                $this->setMetadata($key, $value, $domain);
+            }
+        }
+    }
+}
diff --git a/vendor/symfony/translation/MessageCatalogueInterface.php b/vendor/symfony/translation/MessageCatalogueInterface.php
new file mode 100755
index 0000000..f3d3f5e
--- /dev/null
+++ b/vendor/symfony/translation/MessageCatalogueInterface.php
@@ -0,0 +1,138 @@
+<?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\Translation;
+
+use Symfony\Component\Config\Resource\ResourceInterface;
+
+/**
+ * MessageCatalogueInterface.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface MessageCatalogueInterface
+{
+    const INTL_DOMAIN_SUFFIX = '+intl-icu';
+
+    /**
+     * Gets the catalogue locale.
+     *
+     * @return string The locale
+     */
+    public function getLocale();
+
+    /**
+     * Gets the domains.
+     *
+     * @return array An array of domains
+     */
+    public function getDomains();
+
+    /**
+     * Gets the messages within a given domain.
+     *
+     * If $domain is null, it returns all messages.
+     *
+     * @param string $domain The domain name
+     *
+     * @return array An array of messages
+     */
+    public function all($domain = null);
+
+    /**
+     * Sets a message translation.
+     *
+     * @param string $id          The message id
+     * @param string $translation The messages translation
+     * @param string $domain      The domain name
+     */
+    public function set($id, $translation, $domain = 'messages');
+
+    /**
+     * Checks if a message has a translation.
+     *
+     * @param string $id     The message id
+     * @param string $domain The domain name
+     *
+     * @return bool true if the message has a translation, false otherwise
+     */
+    public function has($id, $domain = 'messages');
+
+    /**
+     * Checks if a message has a translation (it does not take into account the fallback mechanism).
+     *
+     * @param string $id     The message id
+     * @param string $domain The domain name
+     *
+     * @return bool true if the message has a translation, false otherwise
+     */
+    public function defines($id, $domain = 'messages');
+
+    /**
+     * Gets a message translation.
+     *
+     * @param string $id     The message id
+     * @param string $domain The domain name
+     *
+     * @return string The message translation
+     */
+    public function get($id, $domain = 'messages');
+
+    /**
+     * Sets translations for a given domain.
+     *
+     * @param array  $messages An array of translations
+     * @param string $domain   The domain name
+     */
+    public function replace($messages, $domain = 'messages');
+
+    /**
+     * Adds translations for a given domain.
+     *
+     * @param array  $messages An array of translations
+     * @param string $domain   The domain name
+     */
+    public function add($messages, $domain = 'messages');
+
+    /**
+     * Merges translations from the given Catalogue into the current one.
+     *
+     * The two catalogues must have the same locale.
+     */
+    public function addCatalogue(self $catalogue);
+
+    /**
+     * Merges translations from the given Catalogue into the current one
+     * only when the translation does not exist.
+     *
+     * This is used to provide default translations when they do not exist for the current locale.
+     */
+    public function addFallbackCatalogue(self $catalogue);
+
+    /**
+     * Gets the fallback catalogue.
+     *
+     * @return self|null A MessageCatalogueInterface instance or null when no fallback has been set
+     */
+    public function getFallbackCatalogue();
+
+    /**
+     * Returns an array of resources loaded to build this collection.
+     *
+     * @return ResourceInterface[] An array of resources
+     */
+    public function getResources();
+
+    /**
+     * Adds a resource for this collection.
+     */
+    public function addResource(ResourceInterface $resource);
+}
diff --git a/vendor/symfony/translation/MessageSelector.php b/vendor/symfony/translation/MessageSelector.php
new file mode 100755
index 0000000..0f0febb
--- /dev/null
+++ b/vendor/symfony/translation/MessageSelector.php
@@ -0,0 +1,98 @@
+<?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\Translation;
+
+@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.2, use IdentityTranslator instead.', MessageSelector::class), E_USER_DEPRECATED);
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+
+/**
+ * MessageSelector.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ *
+ * @deprecated since Symfony 4.2, use IdentityTranslator instead.
+ */
+class MessageSelector
+{
+    /**
+     * Given a message with different plural translations separated by a
+     * pipe (|), this method returns the correct portion of the message based
+     * on the given number, locale and the pluralization rules in the message
+     * itself.
+     *
+     * The message supports two different types of pluralization rules:
+     *
+     * interval: {0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples
+     * indexed:  There is one apple|There are %count% apples
+     *
+     * The indexed solution can also contain labels (e.g. one: There is one apple).
+     * This is purely for making the translations more clear - it does not
+     * affect the functionality.
+     *
+     * The two methods can also be mixed:
+     *     {0} There are no apples|one: There is one apple|more: There are %count% apples
+     *
+     * @param string    $message The message being translated
+     * @param int|float $number  The number of items represented for the message
+     * @param string    $locale  The locale to use for choosing
+     *
+     * @return string
+     *
+     * @throws InvalidArgumentException
+     */
+    public function choose($message, $number, $locale)
+    {
+        $parts = [];
+        if (preg_match('/^\|++$/', $message)) {
+            $parts = explode('|', $message);
+        } elseif (preg_match_all('/(?:\|\||[^\|])++/', $message, $matches)) {
+            $parts = $matches[0];
+        }
+
+        $explicitRules = [];
+        $standardRules = [];
+        foreach ($parts as $part) {
+            $part = trim(str_replace('||', '|', $part));
+
+            if (preg_match('/^(?P<interval>'.Interval::getIntervalRegexp().')\s*(?P<message>.*?)$/xs', $part, $matches)) {
+                $explicitRules[$matches['interval']] = $matches['message'];
+            } elseif (preg_match('/^\w+\:\s*(.*?)$/', $part, $matches)) {
+                $standardRules[] = $matches[1];
+            } else {
+                $standardRules[] = $part;
+            }
+        }
+
+        // try to match an explicit rule, then fallback to the standard ones
+        foreach ($explicitRules as $interval => $m) {
+            if (Interval::test($number, $interval)) {
+                return $m;
+            }
+        }
+
+        $position = PluralizationRules::get($number, $locale);
+
+        if (!isset($standardRules[$position])) {
+            // when there's exactly one rule given, and that rule is a standard
+            // rule, use this rule
+            if (1 === \count($parts) && isset($standardRules[0])) {
+                return $standardRules[0];
+            }
+
+            throw new InvalidArgumentException(sprintf('Unable to choose a translation for "%s" with locale "%s" for value "%d". Double check that this translation has the correct plural options (e.g. "There is one apple|There are %%count%% apples").', $message, $locale, $number));
+        }
+
+        return $standardRules[$position];
+    }
+}
diff --git a/vendor/symfony/translation/MetadataAwareInterface.php b/vendor/symfony/translation/MetadataAwareInterface.php
new file mode 100755
index 0000000..e93c6fb
--- /dev/null
+++ b/vendor/symfony/translation/MetadataAwareInterface.php
@@ -0,0 +1,54 @@
+<?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\Translation;
+
+/**
+ * MetadataAwareInterface.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface MetadataAwareInterface
+{
+    /**
+     * Gets metadata for the given domain and key.
+     *
+     * Passing an empty domain will return an array with all metadata indexed by
+     * domain and then by key. Passing an empty key will return an array with all
+     * metadata for the given domain.
+     *
+     * @param string $key    The key
+     * @param string $domain The domain name
+     *
+     * @return mixed The value that was set or an array with the domains/keys or null
+     */
+    public function getMetadata($key = '', $domain = 'messages');
+
+    /**
+     * Adds metadata to a message domain.
+     *
+     * @param string $key    The key
+     * @param mixed  $value  The value
+     * @param string $domain The domain name
+     */
+    public function setMetadata($key, $value, $domain = 'messages');
+
+    /**
+     * Deletes metadata for the given key and domain.
+     *
+     * Passing an empty domain will delete all metadata. Passing an empty key will
+     * delete all metadata for the given domain.
+     *
+     * @param string $key    The key
+     * @param string $domain The domain name
+     */
+    public function deleteMetadata($key = '', $domain = 'messages');
+}
diff --git a/vendor/symfony/translation/PluralizationRules.php b/vendor/symfony/translation/PluralizationRules.php
new file mode 100755
index 0000000..77c2760
--- /dev/null
+++ b/vendor/symfony/translation/PluralizationRules.php
@@ -0,0 +1,218 @@
+<?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\Translation;
+
+/**
+ * Returns the plural rules for a given locale.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @deprecated since Symfony 4.2, use IdentityTranslator instead
+ */
+class PluralizationRules
+{
+    private static $rules = [];
+
+    /**
+     * Returns the plural position to use for the given locale and number.
+     *
+     * @param int    $number The number
+     * @param string $locale The locale
+     *
+     * @return int The plural position
+     */
+    public static function get($number, $locale/*, bool $triggerDeprecation = true*/)
+    {
+        if (3 > \func_num_args() || func_get_arg(2)) {
+            @trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.2.', __CLASS__), E_USER_DEPRECATED);
+        }
+
+        if ('pt_BR' === $locale) {
+            // temporary set a locale for brazilian
+            $locale = 'xbr';
+        }
+
+        if (\strlen($locale) > 3) {
+            $locale = substr($locale, 0, -\strlen(strrchr($locale, '_')));
+        }
+
+        if (isset(self::$rules[$locale])) {
+            $return = self::$rules[$locale]($number);
+
+            if (!\is_int($return) || $return < 0) {
+                return 0;
+            }
+
+            return $return;
+        }
+
+        /*
+         * The plural rules are derived from code of the Zend Framework (2010-09-25),
+         * which is subject to the new BSD license (http://framework.zend.com/license/new-bsd).
+         * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+         */
+        switch ($locale) {
+            case 'az':
+            case 'bo':
+            case 'dz':
+            case 'id':
+            case 'ja':
+            case 'jv':
+            case 'ka':
+            case 'km':
+            case 'kn':
+            case 'ko':
+            case 'ms':
+            case 'th':
+            case 'tr':
+            case 'vi':
+            case 'zh':
+                return 0;
+
+            case 'af':
+            case 'bn':
+            case 'bg':
+            case 'ca':
+            case 'da':
+            case 'de':
+            case 'el':
+            case 'en':
+            case 'eo':
+            case 'es':
+            case 'et':
+            case 'eu':
+            case 'fa':
+            case 'fi':
+            case 'fo':
+            case 'fur':
+            case 'fy':
+            case 'gl':
+            case 'gu':
+            case 'ha':
+            case 'he':
+            case 'hu':
+            case 'is':
+            case 'it':
+            case 'ku':
+            case 'lb':
+            case 'ml':
+            case 'mn':
+            case 'mr':
+            case 'nah':
+            case 'nb':
+            case 'ne':
+            case 'nl':
+            case 'nn':
+            case 'no':
+            case 'oc':
+            case 'om':
+            case 'or':
+            case 'pa':
+            case 'pap':
+            case 'ps':
+            case 'pt':
+            case 'so':
+            case 'sq':
+            case 'sv':
+            case 'sw':
+            case 'ta':
+            case 'te':
+            case 'tk':
+            case 'ur':
+            case 'zu':
+                return (1 == $number) ? 0 : 1;
+
+            case 'am':
+            case 'bh':
+            case 'fil':
+            case 'fr':
+            case 'gun':
+            case 'hi':
+            case 'hy':
+            case 'ln':
+            case 'mg':
+            case 'nso':
+            case 'xbr':
+            case 'ti':
+            case 'wa':
+                return ((0 == $number) || (1 == $number)) ? 0 : 1;
+
+            case 'be':
+            case 'bs':
+            case 'hr':
+            case 'ru':
+            case 'sh':
+            case 'sr':
+            case 'uk':
+                return ((1 == $number % 10) && (11 != $number % 100)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
+
+            case 'cs':
+            case 'sk':
+                return (1 == $number) ? 0 : ((($number >= 2) && ($number <= 4)) ? 1 : 2);
+
+            case 'ga':
+                return (1 == $number) ? 0 : ((2 == $number) ? 1 : 2);
+
+            case 'lt':
+                return ((1 == $number % 10) && (11 != $number % 100)) ? 0 : ((($number % 10 >= 2) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
+
+            case 'sl':
+                return (1 == $number % 100) ? 0 : ((2 == $number % 100) ? 1 : (((3 == $number % 100) || (4 == $number % 100)) ? 2 : 3));
+
+            case 'mk':
+                return (1 == $number % 10) ? 0 : 1;
+
+            case 'mt':
+                return (1 == $number) ? 0 : (((0 == $number) || (($number % 100 > 1) && ($number % 100 < 11))) ? 1 : ((($number % 100 > 10) && ($number % 100 < 20)) ? 2 : 3));
+
+            case 'lv':
+                return (0 == $number) ? 0 : (((1 == $number % 10) && (11 != $number % 100)) ? 1 : 2);
+
+            case 'pl':
+                return (1 == $number) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 12) || ($number % 100 > 14))) ? 1 : 2);
+
+            case 'cy':
+                return (1 == $number) ? 0 : ((2 == $number) ? 1 : (((8 == $number) || (11 == $number)) ? 2 : 3));
+
+            case 'ro':
+                return (1 == $number) ? 0 : (((0 == $number) || (($number % 100 > 0) && ($number % 100 < 20))) ? 1 : 2);
+
+            case 'ar':
+                return (0 == $number) ? 0 : ((1 == $number) ? 1 : ((2 == $number) ? 2 : ((($number % 100 >= 3) && ($number % 100 <= 10)) ? 3 : ((($number % 100 >= 11) && ($number % 100 <= 99)) ? 4 : 5))));
+
+            default:
+                return 0;
+        }
+    }
+
+    /**
+     * Overrides the default plural rule for a given locale.
+     *
+     * @param callable $rule   A PHP callable
+     * @param string   $locale The locale
+     */
+    public static function set(callable $rule, $locale)
+    {
+        @trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.2.', __CLASS__), E_USER_DEPRECATED);
+
+        if ('pt_BR' === $locale) {
+            // temporary set a locale for brazilian
+            $locale = 'xbr';
+        }
+
+        if (\strlen($locale) > 3) {
+            $locale = substr($locale, 0, -\strlen(strrchr($locale, '_')));
+        }
+
+        self::$rules[$locale] = $rule;
+    }
+}
diff --git a/vendor/symfony/translation/README.md b/vendor/symfony/translation/README.md
new file mode 100755
index 0000000..46f3d1f
--- /dev/null
+++ b/vendor/symfony/translation/README.md
@@ -0,0 +1,13 @@
+Translation Component
+=====================
+
+The Translation component provides tools to internationalize your application.
+
+Resources
+---------
+
+  * [Documentation](https://symfony.com/doc/current/components/translation/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)
diff --git a/vendor/symfony/translation/Reader/TranslationReader.php b/vendor/symfony/translation/Reader/TranslationReader.php
new file mode 100755
index 0000000..e4554f3
--- /dev/null
+++ b/vendor/symfony/translation/Reader/TranslationReader.php
@@ -0,0 +1,63 @@
+<?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\Translation\Reader;
+
+use Symfony\Component\Finder\Finder;
+use Symfony\Component\Translation\Loader\LoaderInterface;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * TranslationReader reads translation messages from translation files.
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+class TranslationReader implements TranslationReaderInterface
+{
+    /**
+     * Loaders used for import.
+     *
+     * @var array
+     */
+    private $loaders = [];
+
+    /**
+     * Adds a loader to the translation extractor.
+     *
+     * @param string          $format The format of the loader
+     * @param LoaderInterface $loader
+     */
+    public function addLoader($format, LoaderInterface $loader)
+    {
+        $this->loaders[$format] = $loader;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function read($directory, MessageCatalogue $catalogue)
+    {
+        if (!is_dir($directory)) {
+            return;
+        }
+
+        foreach ($this->loaders as $format => $loader) {
+            // load any existing translation files
+            $finder = new Finder();
+            $extension = $catalogue->getLocale().'.'.$format;
+            $files = $finder->files()->name('*.'.$extension)->in($directory);
+            foreach ($files as $file) {
+                $domain = substr($file->getFilename(), 0, -1 * \strlen($extension) - 1);
+                $catalogue->addCatalogue($loader->load($file->getPathname(), $catalogue->getLocale(), $domain));
+            }
+        }
+    }
+}
diff --git a/vendor/symfony/translation/Reader/TranslationReaderInterface.php b/vendor/symfony/translation/Reader/TranslationReaderInterface.php
new file mode 100755
index 0000000..0aa55c6
--- /dev/null
+++ b/vendor/symfony/translation/Reader/TranslationReaderInterface.php
@@ -0,0 +1,30 @@
+<?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\Translation\Reader;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * TranslationReader reads translation messages from translation files.
+ *
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ */
+interface TranslationReaderInterface
+{
+    /**
+     * Reads translation messages from a directory to the catalogue.
+     *
+     * @param string           $directory
+     * @param MessageCatalogue $catalogue
+     */
+    public function read($directory, MessageCatalogue $catalogue);
+}
diff --git a/vendor/symfony/translation/Resources/bin/translation-status.php b/vendor/symfony/translation/Resources/bin/translation-status.php
new file mode 100755
index 0000000..96ccc10
--- /dev/null
+++ b/vendor/symfony/translation/Resources/bin/translation-status.php
@@ -0,0 +1,200 @@
+<?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.
+ */
+
+$usageInstructions = <<<END
+
+  Usage instructions
+  -------------------------------------------------------------------------------
+
+  $ cd symfony-code-root-directory/
+
+  # show the translation status of all locales
+  $ php translation-status.php
+
+  # show the translation status of all locales and all their missing translations
+  $ php translation-status.php -v
+
+  # show the status of a single locale
+  $ php translation-status.php fr
+
+  # show the status of a single locale and all its missing translations
+  $ php translation-status.php fr -v
+
+END;
+
+$config = [
+    // if TRUE, the full list of missing translations is displayed
+    'verbose_output' => false,
+    // NULL = analyze all locales
+    'locale_to_analyze' => null,
+    // the reference files all the other translations are compared to
+    'original_files' => [
+        'src/Symfony/Component/Form/Resources/translations/validators.en.xlf',
+        'src/Symfony/Component/Security/Core/Resources/translations/security.en.xlf',
+        'src/Symfony/Component/Validator/Resources/translations/validators.en.xlf',
+    ],
+];
+
+$argc = $_SERVER['argc'];
+$argv = $_SERVER['argv'];
+
+if ($argc > 3) {
+    echo str_replace('translation-status.php', $argv[0], $usageInstructions);
+    exit(1);
+}
+
+foreach (array_slice($argv, 1) as $argumentOrOption) {
+    if (0 === strpos($argumentOrOption, '-')) {
+        $config['verbose_output'] = true;
+    } else {
+        $config['locale_to_analyze'] = $argumentOrOption;
+    }
+}
+
+foreach ($config['original_files'] as $originalFilePath) {
+    if (!file_exists($originalFilePath)) {
+        echo sprintf('The following file does not exist. Make sure that you execute this command at the root dir of the Symfony code repository.%s  %s', PHP_EOL, $originalFilePath);
+        exit(1);
+    }
+}
+
+$totalMissingTranslations = 0;
+
+foreach ($config['original_files'] as $originalFilePath) {
+    $translationFilePaths = findTranslationFiles($originalFilePath, $config['locale_to_analyze']);
+    $translationStatus = calculateTranslationStatus($originalFilePath, $translationFilePaths);
+
+    $totalMissingTranslations += array_sum(array_map(function ($translation) {
+        return count($translation['missingKeys']);
+    }, array_values($translationStatus)));
+
+    printTranslationStatus($originalFilePath, $translationStatus, $config['verbose_output']);
+}
+
+exit($totalMissingTranslations > 0 ? 1 : 0);
+
+function findTranslationFiles($originalFilePath, $localeToAnalyze)
+{
+    $translations = [];
+
+    $translationsDir = dirname($originalFilePath);
+    $originalFileName = basename($originalFilePath);
+    $translationFileNamePattern = str_replace('.en.', '.*.', $originalFileName);
+
+    $translationFiles = glob($translationsDir.'/'.$translationFileNamePattern);
+    foreach ($translationFiles as $filePath) {
+        $locale = extractLocaleFromFilePath($filePath);
+
+        if (null !== $localeToAnalyze && $locale !== $localeToAnalyze) {
+            continue;
+        }
+
+        $translations[$locale] = $filePath;
+    }
+
+    return $translations;
+}
+
+function calculateTranslationStatus($originalFilePath, $translationFilePaths)
+{
+    $translationStatus = [];
+    $allTranslationKeys = extractTranslationKeys($originalFilePath);
+
+    foreach ($translationFilePaths as $locale => $translationPath) {
+        $translatedKeys = extractTranslationKeys($translationPath);
+        $missingKeys = array_diff_key($allTranslationKeys, $translatedKeys);
+
+        $translationStatus[$locale] = [
+            'total' => count($allTranslationKeys),
+            'translated' => count($translatedKeys),
+            'missingKeys' => $missingKeys,
+        ];
+    }
+
+    return $translationStatus;
+}
+
+function printTranslationStatus($originalFilePath, $translationStatus, $verboseOutput)
+{
+    printTitle($originalFilePath);
+    printTable($translationStatus, $verboseOutput);
+    echo PHP_EOL.PHP_EOL;
+}
+
+function extractLocaleFromFilePath($filePath)
+{
+    $parts = explode('.', $filePath);
+
+    return $parts[count($parts) - 2];
+}
+
+function extractTranslationKeys($filePath)
+{
+    $translationKeys = [];
+    $contents = new \SimpleXMLElement(file_get_contents($filePath));
+
+    foreach ($contents->file->body->{'trans-unit'} as $translationKey) {
+        $translationId = (string) $translationKey['id'];
+        $translationKey = (string) $translationKey->source;
+
+        $translationKeys[$translationId] = $translationKey;
+    }
+
+    return $translationKeys;
+}
+
+function printTitle($title)
+{
+    echo $title.PHP_EOL;
+    echo str_repeat('=', strlen($title)).PHP_EOL.PHP_EOL;
+}
+
+function printTable($translations, $verboseOutput)
+{
+    if (0 === count($translations)) {
+        echo 'No translations found';
+
+        return;
+    }
+    $longestLocaleNameLength = max(array_map('strlen', array_keys($translations)));
+
+    foreach ($translations as $locale => $translation) {
+        $isTranslationCompleted = $translation['translated'] === $translation['total'];
+        if ($isTranslationCompleted) {
+            textColorGreen();
+        }
+
+        echo sprintf('| Locale: %-'.$longestLocaleNameLength.'s | Translated: %d/%d', $locale, $translation['translated'], $translation['total']).PHP_EOL;
+
+        textColorNormal();
+
+        if (true === $verboseOutput && count($translation['missingKeys']) > 0) {
+            echo str_repeat('-', 80).PHP_EOL;
+            echo '| Missing Translations:'.PHP_EOL;
+
+            foreach ($translation['missingKeys'] as $id => $content) {
+                echo sprintf('|   (id=%s) %s', $id, $content).PHP_EOL;
+            }
+
+            echo str_repeat('-', 80).PHP_EOL;
+        }
+    }
+}
+
+function textColorGreen()
+{
+    echo "\033[32m";
+}
+
+function textColorNormal()
+{
+    echo "\033[0m";
+}
diff --git a/vendor/symfony/translation/Resources/data/parents.json b/vendor/symfony/translation/Resources/data/parents.json
new file mode 100755
index 0000000..6e45d9f
--- /dev/null
+++ b/vendor/symfony/translation/Resources/data/parents.json
@@ -0,0 +1,136 @@
+{
+    "az_Cyrl": "root",
+    "bs_Cyrl": "root",
+    "en_150": "en_001",
+    "en_AG": "en_001",
+    "en_AI": "en_001",
+    "en_AT": "en_150",
+    "en_AU": "en_001",
+    "en_BB": "en_001",
+    "en_BE": "en_150",
+    "en_BM": "en_001",
+    "en_BS": "en_001",
+    "en_BW": "en_001",
+    "en_BZ": "en_001",
+    "en_CA": "en_001",
+    "en_CC": "en_001",
+    "en_CH": "en_150",
+    "en_CK": "en_001",
+    "en_CM": "en_001",
+    "en_CX": "en_001",
+    "en_CY": "en_001",
+    "en_DE": "en_150",
+    "en_DG": "en_001",
+    "en_DK": "en_150",
+    "en_DM": "en_001",
+    "en_ER": "en_001",
+    "en_FI": "en_150",
+    "en_FJ": "en_001",
+    "en_FK": "en_001",
+    "en_FM": "en_001",
+    "en_GB": "en_001",
+    "en_GD": "en_001",
+    "en_GG": "en_001",
+    "en_GH": "en_001",
+    "en_GI": "en_001",
+    "en_GM": "en_001",
+    "en_GY": "en_001",
+    "en_HK": "en_001",
+    "en_IE": "en_001",
+    "en_IL": "en_001",
+    "en_IM": "en_001",
+    "en_IN": "en_001",
+    "en_IO": "en_001",
+    "en_JE": "en_001",
+    "en_JM": "en_001",
+    "en_KE": "en_001",
+    "en_KI": "en_001",
+    "en_KN": "en_001",
+    "en_KY": "en_001",
+    "en_LC": "en_001",
+    "en_LR": "en_001",
+    "en_LS": "en_001",
+    "en_MG": "en_001",
+    "en_MO": "en_001",
+    "en_MS": "en_001",
+    "en_MT": "en_001",
+    "en_MU": "en_001",
+    "en_MW": "en_001",
+    "en_MY": "en_001",
+    "en_NA": "en_001",
+    "en_NF": "en_001",
+    "en_NG": "en_001",
+    "en_NL": "en_150",
+    "en_NR": "en_001",
+    "en_NU": "en_001",
+    "en_NZ": "en_001",
+    "en_PG": "en_001",
+    "en_PH": "en_001",
+    "en_PK": "en_001",
+    "en_PN": "en_001",
+    "en_PW": "en_001",
+    "en_RW": "en_001",
+    "en_SB": "en_001",
+    "en_SC": "en_001",
+    "en_SD": "en_001",
+    "en_SE": "en_150",
+    "en_SG": "en_001",
+    "en_SH": "en_001",
+    "en_SI": "en_150",
+    "en_SL": "en_001",
+    "en_SS": "en_001",
+    "en_SX": "en_001",
+    "en_SZ": "en_001",
+    "en_TC": "en_001",
+    "en_TK": "en_001",
+    "en_TO": "en_001",
+    "en_TT": "en_001",
+    "en_TV": "en_001",
+    "en_TZ": "en_001",
+    "en_UG": "en_001",
+    "en_VC": "en_001",
+    "en_VG": "en_001",
+    "en_VU": "en_001",
+    "en_WS": "en_001",
+    "en_ZA": "en_001",
+    "en_ZM": "en_001",
+    "en_ZW": "en_001",
+    "es_AR": "es_419",
+    "es_BO": "es_419",
+    "es_BR": "es_419",
+    "es_BZ": "es_419",
+    "es_CL": "es_419",
+    "es_CO": "es_419",
+    "es_CR": "es_419",
+    "es_CU": "es_419",
+    "es_DO": "es_419",
+    "es_EC": "es_419",
+    "es_GT": "es_419",
+    "es_HN": "es_419",
+    "es_MX": "es_419",
+    "es_NI": "es_419",
+    "es_PA": "es_419",
+    "es_PE": "es_419",
+    "es_PR": "es_419",
+    "es_PY": "es_419",
+    "es_SV": "es_419",
+    "es_US": "es_419",
+    "es_UY": "es_419",
+    "es_VE": "es_419",
+    "pa_Arab": "root",
+    "pt_AO": "pt_PT",
+    "pt_CH": "pt_PT",
+    "pt_CV": "pt_PT",
+    "pt_GQ": "pt_PT",
+    "pt_GW": "pt_PT",
+    "pt_LU": "pt_PT",
+    "pt_MO": "pt_PT",
+    "pt_MZ": "pt_PT",
+    "pt_ST": "pt_PT",
+    "pt_TL": "pt_PT",
+    "sr_Latn": "root",
+    "uz_Arab": "root",
+    "uz_Cyrl": "root",
+    "zh_Hant": "root",
+    "zh_Hant_MO": "zh_Hant_HK"
+}
diff --git a/vendor/symfony/translation/Resources/schemas/xliff-core-1.2-strict.xsd b/vendor/symfony/translation/Resources/schemas/xliff-core-1.2-strict.xsd
new file mode 100755
index 0000000..dface62
--- /dev/null
+++ b/vendor/symfony/translation/Resources/schemas/xliff-core-1.2-strict.xsd
@@ -0,0 +1,2223 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+
+May-19-2004:
+- Changed the <choice> for ElemType_header, moving minOccurs="0" maxOccurs="unbounded" from its elements
+to <choice> itself.
+- Added <choice> for ElemType_trans-unit to allow "any order" for <context-group>, <count-group>, <prop-group>, <note>, and
+<alt-trans>.
+
+Oct-2005
+- updated version info to 1.2
+- equiv-trans attribute to <trans-unit> element
+- merged-trans attribute for <group> element
+- Add the <seg-source> element as optional in the <trans-unit> and <alt-trans> content models, at the same level as <source>
+- Create a new value "seg" for the mtype attribute of the <mrk> element
+- Add mid as an optional attribute for the <alt-trans> element
+
+Nov-14-2005
+- Changed name attribute for <context-group> from required to optional
+- Added extension point at <xliff>
+
+Jan-9-2006
+- Added alttranstype type attribute to <alt-trans>, and values
+
+Jan-10-2006
+- Corrected error with overwritten purposeValueList
+- Corrected name="AttrType_Version",  attribute should have been "name"
+
+-->
+<xsd:schema xmlns:xlf="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="urn:oasis:names:tc:xliff:document:1.2" xml:lang="en">
+  <!-- Import for xml:lang and xml:space -->
+  <xsd:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+  <!-- Attributes Lists -->
+  <xsd:simpleType name="XTend">
+    <xsd:restriction base="xsd:string">
+      <xsd:pattern value="x-[^\s]+"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="context-typeValueList">
+    <xsd:annotation>
+      <xsd:documentation>Values for the attribute 'context-type'.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:string">
+      <xsd:enumeration value="database">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a database content.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="element">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the content of an element within an XML document.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="elementtitle">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the name of an element within an XML document.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="linenumber">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the line number from the sourcefile (see context-type="sourcefile") where the &lt;source&gt; is found.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="numparams">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a the number of parameters contained within the &lt;source&gt;.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="paramnotes">
+        <xsd:annotation>
+          <xsd:documentation>Indicates notes pertaining to the parameters in the &lt;source&gt;.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="record">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the content of a record within a database.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="recordtitle">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the name of a record within a database.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="sourcefile">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the original source file in the case that multiple files are merged to form the original file from which the XLIFF file is created. This differs from the original &lt;file&gt; attribute in that this sourcefile is one of many that make up that file.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="count-typeValueList">
+    <xsd:annotation>
+      <xsd:documentation>Values for the attribute 'count-type'.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="num-usages">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the count units are items that are used X times in a certain context; example: this is a reusable text unit which is used 42 times in other texts.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="repetition">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the count units are translation units existing already in the same document.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="total">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a total count.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="InlineDelimitersValueList">
+    <xsd:annotation>
+      <xsd:documentation>Values for the attribute 'ctype' when used other elements than &lt;ph&gt; or &lt;x&gt;.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="bold">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a run of bolded text.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="italic">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a run of text in italics.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="underlined">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a run of underlined text.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="link">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a run of hyper-text.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="InlinePlaceholdersValueList">
+    <xsd:annotation>
+      <xsd:documentation>Values for the attribute 'ctype' when used with &lt;ph&gt; or &lt;x&gt;.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="image">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a inline image.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="pb">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a page break.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="lb">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a line break.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="mime-typeValueList">
+    <xsd:restriction base="xsd:string">
+      <xsd:pattern value="(text|multipart|message|application|image|audio|video|model)(/.+)*"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="datatypeValueList">
+    <xsd:annotation>
+      <xsd:documentation>Values for the attribute 'datatype'.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="asp">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Active Server Page data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="c">
+        <xsd:annotation>
+          <xsd:documentation>Indicates C source file data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="cdf">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Channel Definition Format (CDF) data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="cfm">
+        <xsd:annotation>
+          <xsd:documentation>Indicates ColdFusion data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="cpp">
+        <xsd:annotation>
+          <xsd:documentation>Indicates C++ source file data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="csharp">
+        <xsd:annotation>
+          <xsd:documentation>Indicates C-Sharp data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="cstring">
+        <xsd:annotation>
+          <xsd:documentation>Indicates strings from C, ASM, and driver files data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="csv">
+        <xsd:annotation>
+          <xsd:documentation>Indicates comma-separated values data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="database">
+        <xsd:annotation>
+          <xsd:documentation>Indicates database data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="documentfooter">
+        <xsd:annotation>
+          <xsd:documentation>Indicates portions of document that follows data and contains metadata.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="documentheader">
+        <xsd:annotation>
+          <xsd:documentation>Indicates portions of document that precedes data and contains metadata.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="filedialog">
+        <xsd:annotation>
+          <xsd:documentation>Indicates data from standard UI file operations dialogs (e.g., Open, Save, Save As, Export, Import).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="form">
+        <xsd:annotation>
+          <xsd:documentation>Indicates standard user input screen data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="html">
+        <xsd:annotation>
+          <xsd:documentation>Indicates HyperText Markup Language (HTML) data - document instance.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="htmlbody">
+        <xsd:annotation>
+          <xsd:documentation>Indicates content within an HTML document’s &lt;body&gt; element.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="ini">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Windows INI file data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="interleaf">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Interleaf data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="javaclass">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Java source file data (extension '.java').</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="javapropertyresourcebundle">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Java property resource bundle data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="javalistresourcebundle">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Java list resource bundle data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="javascript">
+        <xsd:annotation>
+          <xsd:documentation>Indicates JavaScript source file data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="jscript">
+        <xsd:annotation>
+          <xsd:documentation>Indicates JScript source file data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="layout">
+        <xsd:annotation>
+          <xsd:documentation>Indicates information relating to formatting.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="lisp">
+        <xsd:annotation>
+          <xsd:documentation>Indicates LISP source file data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="margin">
+        <xsd:annotation>
+          <xsd:documentation>Indicates information relating to margin formats.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="menufile">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a file containing menu.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="messagefile">
+        <xsd:annotation>
+          <xsd:documentation>Indicates numerically identified string table.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="mif">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Maker Interchange Format (MIF) data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="mimetype">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that the datatype attribute value is a MIME Type value and is defined in the mime-type attribute.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="mo">
+        <xsd:annotation>
+          <xsd:documentation>Indicates GNU Machine Object data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="msglib">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Message Librarian strings created by Novell's Message Librarian Tool.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="pagefooter">
+        <xsd:annotation>
+          <xsd:documentation>Indicates information to be displayed at the bottom of each page of a document.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="pageheader">
+        <xsd:annotation>
+          <xsd:documentation>Indicates information to be displayed at the top of each page of a document.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="parameters">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a list of property values (e.g., settings within INI files or preferences dialog).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="pascal">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Pascal source file data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="php">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Hypertext Preprocessor data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="plaintext">
+        <xsd:annotation>
+          <xsd:documentation>Indicates plain text file (no formatting other than, possibly, wrapping).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="po">
+        <xsd:annotation>
+          <xsd:documentation>Indicates GNU Portable Object file.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="report">
+        <xsd:annotation>
+          <xsd:documentation>Indicates dynamically generated user defined document. e.g. Oracle Report, Crystal Report, etc.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="resources">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Windows .NET binary resources.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="resx">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Windows .NET Resources.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="rtf">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Rich Text Format (RTF) data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="sgml">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Standard Generalized Markup Language (SGML) data - document instance.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="sgmldtd">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Standard Generalized Markup Language (SGML) data - Document Type Definition (DTD).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="svg">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Scalable Vector Graphic (SVG) data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="vbscript">
+        <xsd:annotation>
+          <xsd:documentation>Indicates VisualBasic Script source file.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="warning">
+        <xsd:annotation>
+          <xsd:documentation>Indicates warning message.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="winres">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Windows (Win32) resources (i.e. resources extracted from an RC script, a message file, or a compiled file).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="xhtml">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Extensible HyperText Markup Language (XHTML) data - document instance.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="xml">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Extensible Markup Language (XML) data - document instance.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="xmldtd">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Extensible Markup Language (XML) data - Document Type Definition (DTD).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="xsl">
+        <xsd:annotation>
+          <xsd:documentation>Indicates Extensible Stylesheet Language (XSL) data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="xul">
+        <xsd:annotation>
+          <xsd:documentation>Indicates XUL elements.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="mtypeValueList">
+    <xsd:annotation>
+      <xsd:documentation>Values for the attribute 'mtype'.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="abbrev">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the marked text is an abbreviation.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="abbreviated-form">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.8: A term resulting from the omission of any part of the full term while designating the same concept.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="abbreviation">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.8.1: An abbreviated form of a simple term resulting from the omission of some of its letters (e.g. 'adj.' for 'adjective').</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="acronym">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.8.4: An abbreviated form of a term made up of letters from the full form of a multiword term strung together into a sequence pronounced only syllabically (e.g. 'radar' for 'radio detecting and ranging').</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="appellation">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620: A proper-name term, such as the name of an agency or other proper entity.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="collocation">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.18.1: A recurrent word combination characterized by cohesion in that the components of the collocation must co-occur within an utterance or series of utterances, even though they do not necessarily have to maintain immediate proximity to one another.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="common-name">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.5: A synonym for an international scientific term that is used in general discourse in a given language.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="datetime">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the marked text is a date and/or time.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="equation">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.15: An expression used to represent a concept based on a statement that two mathematical expressions are, for instance, equal as identified by the equal sign (=), or assigned to one another by a similar sign.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="expanded-form">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.7: The complete representation of a term for which there is an abbreviated form.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="formula">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.14: Figures, symbols or the like used to express a concept briefly, such as a mathematical or chemical formula.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="head-term">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.1: The concept designation that has been chosen to head a terminological record.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="initialism">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.8.3: An abbreviated form of a term consisting of some of the initial letters of the words making up a multiword term or the term elements making up a compound term when these letters are pronounced individually (e.g. 'BSE' for 'bovine spongiform encephalopathy').</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="international-scientific-term">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.4: A term that is part of an international scientific nomenclature as adopted by an appropriate scientific body.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="internationalism">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.6: A term that has the same or nearly identical orthographic or phonemic form in many languages.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="logical-expression">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.16: An expression used to represent a concept based on mathematical or logical relations, such as statements of inequality, set relationships, Boolean operations, and the like.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="materials-management-unit">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.17: A unit to track object.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="name">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the marked text is a name.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="near-synonym">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.3: A term that represents the same or a very similar concept as another term in the same language, but for which interchangeability is limited to some contexts and inapplicable in others.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="part-number">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.17.2: A unique alphanumeric designation assigned to an object in a manufacturing system.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="phrase">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the marked text is a phrase.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="phraseological-unit">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.18: Any group of two or more words that form a unit, the meaning of which frequently cannot be deduced based on the combined sense of the words making up the phrase.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="protected">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the marked text should not be translated.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="romanized-form">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.12: A form of a term resulting from an operation whereby non-Latin writing systems are converted to the Latin alphabet.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="seg">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that the marked text represents a segment.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="set-phrase">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.18.2: A fixed, lexicalized phrase.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="short-form">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.8.2: A variant of a multiword term that includes fewer words than the full form of the term (e.g. 'Group of Twenty-four' for 'Intergovernmental Group of Twenty-four on International Monetary Affairs').</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="sku">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.17.1: Stock keeping unit, an inventory item identified by a unique alphanumeric designation assigned to an object in an inventory control system.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="standard-text">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.19: A fixed chunk of recurring text.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="symbol">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.13: A designation of a concept by letters, numerals, pictograms or any combination thereof.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="synonym">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.2: Any term that represents the same or a very similar concept as the main entry term in a term entry.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="synonymous-phrase">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.18.3: Phraseological unit in a language that expresses the same semantic content as another phrase in that same language.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="term">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the marked text is a term.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="transcribed-form">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.11: A form of a term resulting from an operation whereby the characters of one writing system are represented by characters from another writing system, taking into account the pronunciation of the characters converted.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="transliterated-form">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.10: A form of a term resulting from an operation whereby the characters of an alphabetic writing system are represented by characters from another alphabetic writing system.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="truncated-term">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.8.5: An abbreviated form of a term resulting from the omission of one or more term elements or syllables (e.g. 'flu' for 'influenza').</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="variant">
+        <xsd:annotation>
+          <xsd:documentation>ISO-12620 2.1.9: One of the alternate forms of a term.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="restypeValueList">
+    <xsd:annotation>
+      <xsd:documentation>Values for the attribute 'restype'.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="auto3state">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC AUTO3STATE control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="autocheckbox">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC AUTOCHECKBOX control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="autoradiobutton">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC AUTORADIOBUTTON control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="bedit">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC BEDIT control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="bitmap">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a bitmap, for example a BITMAP resource in Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="button">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a button object, for example a BUTTON control Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="caption">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a caption, such as the caption of a dialog box.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="cell">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the cell in a table, for example the content of the &lt;td&gt; element in HTML.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="checkbox">
+        <xsd:annotation>
+          <xsd:documentation>Indicates check box object, for example a CHECKBOX control in Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="checkboxmenuitem">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a menu item with an associated checkbox.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="checkedlistbox">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a list box, but with a check-box for each item.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="colorchooser">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a color selection dialog.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="combobox">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a combination of edit box and listbox object, for example a COMBOBOX control in Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="comboboxexitem">
+        <xsd:annotation>
+          <xsd:documentation>Indicates an initialization entry of an extended combobox DLGINIT resource block. (code 0x1234).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="comboboxitem">
+        <xsd:annotation>
+          <xsd:documentation>Indicates an initialization entry of a combobox DLGINIT resource block (code 0x0403).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="component">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a UI base class element that cannot be represented by any other element.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="contextmenu">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a context menu.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="ctext">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC CTEXT control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="cursor">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a cursor, for example a CURSOR resource in Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="datetimepicker">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a date/time picker.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="defpushbutton">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC DEFPUSHBUTTON control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="dialog">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a dialog box.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="dlginit">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC DLGINIT resource block.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="edit">
+        <xsd:annotation>
+          <xsd:documentation>Indicates an edit box object, for example an EDIT control in Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="file">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a filename.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="filechooser">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a file dialog.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="fn">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a footnote.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="font">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a font name.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="footer">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a footer.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="frame">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a frame object.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="grid">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a XUL grid element.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="groupbox">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a groupbox object, for example a GROUPBOX control in Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="header">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a header item.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="heading">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a heading, such has the content of &lt;h1&gt;, &lt;h2&gt;, etc. in HTML.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="hedit">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC HEDIT control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="hscrollbar">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a horizontal scrollbar.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="icon">
+        <xsd:annotation>
+          <xsd:documentation>Indicates an icon, for example an ICON resource in Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="iedit">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC IEDIT control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="keywords">
+        <xsd:annotation>
+          <xsd:documentation>Indicates keyword list, such as the content of the Keywords meta-data in HTML, or a K footnote in WinHelp RTF.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="label">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a label object.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="linklabel">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a label that is also a HTML link (not necessarily a URL).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="list">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a list (a group of list-items, for example an &lt;ol&gt; or &lt;ul&gt; element in HTML).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="listbox">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a listbox object, for example an LISTBOX control in Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="listitem">
+        <xsd:annotation>
+          <xsd:documentation>Indicates an list item (an entry in a list).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="ltext">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC LTEXT control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="menu">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a menu (a group of menu-items).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="menubar">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a toolbar containing one or more tope level menus.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="menuitem">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a menu item (an entry in a menu).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="menuseparator">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a XUL menuseparator element.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="message">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a message, for example an entry in a MESSAGETABLE resource in Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="monthcalendar">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a calendar control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="numericupdown">
+        <xsd:annotation>
+          <xsd:documentation>Indicates an edit box beside a spin control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="panel">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a catch all for rectangular areas.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="popupmenu">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a standalone menu not necessarily associated with a menubar.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="pushbox">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a pushbox object, for example a PUSHBOX control in Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="pushbutton">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC PUSHBUTTON control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="radio">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a radio button object.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="radiobuttonmenuitem">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a menuitem with associated radio button.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="rcdata">
+        <xsd:annotation>
+          <xsd:documentation>Indicates raw data resources for an application.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="row">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a row in a table.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="rtext">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC RTEXT control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="scrollpane">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a user navigable container used to show a portion of a document.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="separator">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a generic divider object (e.g. menu group separator).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="shortcut">
+        <xsd:annotation>
+          <xsd:documentation>Windows accelerators, shortcuts in resource or property files.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="spinner">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a UI control to indicate process activity but not progress.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="splitter">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a splitter bar.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="state3">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC STATE3 control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="statusbar">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a window for providing feedback to the users, like 'read-only', etc.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="string">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a string, for example an entry in a STRINGTABLE resource in Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="tabcontrol">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a layers of controls with a tab to select layers.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="table">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a display and edits regular two-dimensional tables of cells.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="textbox">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a XUL textbox element.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="togglebutton">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a UI button that can be toggled to on or off state.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="toolbar">
+        <xsd:annotation>
+          <xsd:documentation>Indicates an array of controls, usually buttons.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="tooltip">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a pop up tool tip text.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="trackbar">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a bar with a pointer indicating a position within a certain range.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="tree">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a control that displays a set of hierarchical data.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="uri">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a URI (URN or URL).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="userbutton">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a Windows RC USERBUTTON control.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="usercontrol">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a user-defined control like CONTROL control in Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="var">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the text of a variable.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="versioninfo">
+        <xsd:annotation>
+          <xsd:documentation>Indicates version information about a resource like VERSIONINFO in Windows.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="vscrollbar">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a vertical scrollbar.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="window">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a graphical window.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="size-unitValueList">
+    <xsd:annotation>
+      <xsd:documentation>Values for the attribute 'size-unit'.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="byte">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in 8-bit bytes.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="char">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in Unicode characters.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="col">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in columns. Used for HTML text area.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="cm">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in centimeters.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="dlgunit">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in dialog units, as defined in Windows resources.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="em">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in 'font-size' units (as defined in CSS).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="ex">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in 'x-height' units (as defined in CSS).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="glyph">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in glyphs. A glyph is considered to be one or more combined Unicode characters that represent a single displayable text character. Sometimes referred to as a 'grapheme cluster'</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="in">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in inches.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="mm">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in millimeters.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="percent">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in percentage.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="pixel">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in pixels.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="point">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in point.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="row">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a size in rows. Used for HTML text area.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="stateValueList">
+    <xsd:annotation>
+      <xsd:documentation>Values for the attribute 'state'.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="final">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the terminating state.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="needs-adaptation">
+        <xsd:annotation>
+          <xsd:documentation>Indicates only non-textual information needs adaptation.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="needs-l10n">
+        <xsd:annotation>
+          <xsd:documentation>Indicates both text and non-textual information needs adaptation.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="needs-review-adaptation">
+        <xsd:annotation>
+          <xsd:documentation>Indicates only non-textual information needs review.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="needs-review-l10n">
+        <xsd:annotation>
+          <xsd:documentation>Indicates both text and non-textual information needs review.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="needs-review-translation">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that only the text of the item needs to be reviewed.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="needs-translation">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that the item needs to be translated.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="new">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that the item is new. For example, translation units that were not in a previous version of the document.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="signed-off">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that changes are reviewed and approved.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="translated">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that the item has been translated.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="state-qualifierValueList">
+    <xsd:annotation>
+      <xsd:documentation>Values for the attribute 'state-qualifier'.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="exact-match">
+        <xsd:annotation>
+          <xsd:documentation>Indicates an exact match. An exact match occurs when a source text of a segment is exactly the same as the source text of a segment that was translated previously.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="fuzzy-match">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a fuzzy match. A fuzzy match occurs when a source text of a segment is very similar to the source text of a segment that was translated previously (e.g. when the difference is casing, a few changed words, white-space discripancy, etc.).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="id-match">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a match based on matching IDs (in addition to matching text).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="leveraged-glossary">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a translation derived from a glossary.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="leveraged-inherited">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a translation derived from existing translation.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="leveraged-mt">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a translation derived from machine translation.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="leveraged-repository">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a translation derived from a translation repository.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="leveraged-tm">
+        <xsd:annotation>
+          <xsd:documentation>Indicates a translation derived from a translation memory.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="mt-suggestion">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the translation is suggested by machine translation.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="rejected-grammar">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that the item has been rejected because of incorrect grammar.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="rejected-inaccurate">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that the item has been rejected because it is incorrect.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="rejected-length">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that the item has been rejected because it is too long or too short.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="rejected-spelling">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that the item has been rejected because of incorrect spelling.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="tm-suggestion">
+        <xsd:annotation>
+          <xsd:documentation>Indicates the translation is suggested by translation memory.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="unitValueList">
+    <xsd:annotation>
+      <xsd:documentation>Values for the attribute 'unit'.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="word">
+        <xsd:annotation>
+          <xsd:documentation>Refers to words.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="page">
+        <xsd:annotation>
+          <xsd:documentation>Refers to pages.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="trans-unit">
+        <xsd:annotation>
+          <xsd:documentation>Refers to &lt;trans-unit&gt; elements.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="bin-unit">
+        <xsd:annotation>
+          <xsd:documentation>Refers to &lt;bin-unit&gt; elements.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="glyph">
+        <xsd:annotation>
+          <xsd:documentation>Refers to glyphs.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="item">
+        <xsd:annotation>
+          <xsd:documentation>Refers to &lt;trans-unit&gt; and/or &lt;bin-unit&gt; elements.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="instance">
+        <xsd:annotation>
+          <xsd:documentation>Refers to the occurrences of instances defined by the count-type value.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="character">
+        <xsd:annotation>
+          <xsd:documentation>Refers to characters.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="line">
+        <xsd:annotation>
+          <xsd:documentation>Refers to lines.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="sentence">
+        <xsd:annotation>
+          <xsd:documentation>Refers to sentences.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="paragraph">
+        <xsd:annotation>
+          <xsd:documentation>Refers to paragraphs.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="segment">
+        <xsd:annotation>
+          <xsd:documentation>Refers to segments.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="placeable">
+        <xsd:annotation>
+          <xsd:documentation>Refers to placeables (inline elements).</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="priorityValueList">
+    <xsd:annotation>
+      <xsd:documentation>Values for the attribute 'priority'.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:positiveInteger">
+      <xsd:enumeration value="1">
+        <xsd:annotation>
+          <xsd:documentation>Highest priority.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="2">
+        <xsd:annotation>
+          <xsd:documentation>High priority.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="3">
+        <xsd:annotation>
+          <xsd:documentation>High priority, but not as important as 2.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="4">
+        <xsd:annotation>
+          <xsd:documentation>High priority, but not as important as 3.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="5">
+        <xsd:annotation>
+          <xsd:documentation>Medium priority, but more important than 6.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="6">
+        <xsd:annotation>
+          <xsd:documentation>Medium priority, but less important than 5.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="7">
+        <xsd:annotation>
+          <xsd:documentation>Low priority, but more important than 8.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="8">
+        <xsd:annotation>
+          <xsd:documentation>Low priority, but more important than 9.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="9">
+        <xsd:annotation>
+          <xsd:documentation>Low priority.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="10">
+        <xsd:annotation>
+          <xsd:documentation>Lowest priority.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="reformatValueYesNo">
+    <xsd:restriction base="xsd:string">
+      <xsd:enumeration value="yes">
+        <xsd:annotation>
+          <xsd:documentation>This value indicates that all properties can be reformatted. This value must be used alone.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="no">
+        <xsd:annotation>
+          <xsd:documentation>This value indicates that no properties should be reformatted. This value must be used alone.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="reformatValueList">
+    <xsd:list>
+      <xsd:simpleType>
+        <xsd:union memberTypes="xlf:XTend">
+          <xsd:simpleType>
+            <xsd:restriction base="xsd:string">
+              <xsd:enumeration value="coord">
+                <xsd:annotation>
+                  <xsd:documentation>This value indicates that all information in the coord attribute can be modified.</xsd:documentation>
+                </xsd:annotation>
+              </xsd:enumeration>
+              <xsd:enumeration value="coord-x">
+                <xsd:annotation>
+                  <xsd:documentation>This value indicates that the x information in the coord attribute can be modified.</xsd:documentation>
+                </xsd:annotation>
+              </xsd:enumeration>
+              <xsd:enumeration value="coord-y">
+                <xsd:annotation>
+                  <xsd:documentation>This value indicates that the y information in the coord attribute can be modified.</xsd:documentation>
+                </xsd:annotation>
+              </xsd:enumeration>
+              <xsd:enumeration value="coord-cx">
+                <xsd:annotation>
+                  <xsd:documentation>This value indicates that the cx information in the coord attribute can be modified.</xsd:documentation>
+                </xsd:annotation>
+              </xsd:enumeration>
+              <xsd:enumeration value="coord-cy">
+                <xsd:annotation>
+                  <xsd:documentation>This value indicates that the cy information in the coord attribute can be modified.</xsd:documentation>
+                </xsd:annotation>
+              </xsd:enumeration>
+              <xsd:enumeration value="font">
+                <xsd:annotation>
+                  <xsd:documentation>This value indicates that all the information in the font attribute can be modified.</xsd:documentation>
+                </xsd:annotation>
+              </xsd:enumeration>
+              <xsd:enumeration value="font-name">
+                <xsd:annotation>
+                  <xsd:documentation>This value indicates that the name information in the font attribute can be modified.</xsd:documentation>
+                </xsd:annotation>
+              </xsd:enumeration>
+              <xsd:enumeration value="font-size">
+                <xsd:annotation>
+                  <xsd:documentation>This value indicates that the size information in the font attribute can be modified.</xsd:documentation>
+                </xsd:annotation>
+              </xsd:enumeration>
+              <xsd:enumeration value="font-weight">
+                <xsd:annotation>
+                  <xsd:documentation>This value indicates that the weight information in the font attribute can be modified.</xsd:documentation>
+                </xsd:annotation>
+              </xsd:enumeration>
+              <xsd:enumeration value="css-style">
+                <xsd:annotation>
+                  <xsd:documentation>This value indicates that the information in the css-style attribute can be modified.</xsd:documentation>
+                </xsd:annotation>
+              </xsd:enumeration>
+              <xsd:enumeration value="style">
+                <xsd:annotation>
+                  <xsd:documentation>This value indicates that the information in the style attribute can be modified.</xsd:documentation>
+                </xsd:annotation>
+              </xsd:enumeration>
+              <xsd:enumeration value="ex-style">
+                <xsd:annotation>
+                  <xsd:documentation>This value indicates that the information in the exstyle attribute can be modified.</xsd:documentation>
+                </xsd:annotation>
+              </xsd:enumeration>
+            </xsd:restriction>
+          </xsd:simpleType>
+        </xsd:union>
+      </xsd:simpleType>
+    </xsd:list>
+  </xsd:simpleType>
+  <xsd:simpleType name="purposeValueList">
+    <xsd:restriction base="xsd:string">
+      <xsd:enumeration value="information">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that the context is informational in nature, specifying for example, how a term should be translated. Thus, should be displayed to anyone editing the XLIFF document.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="location">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that the context-group is used to specify where the term was found in the translatable source. Thus, it is not displayed.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="match">
+        <xsd:annotation>
+          <xsd:documentation>Indicates that the context information should be used during translation memory lookups. Thus, it is not displayed.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="alttranstypeValueList">
+    <xsd:restriction base="xsd:string">
+      <xsd:enumeration value="proposal">
+        <xsd:annotation>
+          <xsd:documentation>Represents a translation proposal from a translation memory or other resource.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="previous-version">
+        <xsd:annotation>
+          <xsd:documentation>Represents a previous version of the target element.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="rejected">
+        <xsd:annotation>
+          <xsd:documentation>Represents a rejected version of the target element.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="reference">
+        <xsd:annotation>
+          <xsd:documentation>Represents a translation to be used for reference purposes only, for example from a related product or a different language.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+      <xsd:enumeration value="accepted">
+        <xsd:annotation>
+          <xsd:documentation>Represents a proposed translation that was used for the translation of the trans-unit, possibly modified.</xsd:documentation>
+        </xsd:annotation>
+      </xsd:enumeration>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <!-- Other Types -->
+  <xsd:complexType name="ElemType_ExternalReference">
+    <xsd:choice>
+      <xsd:element ref="xlf:internal-file"/>
+      <xsd:element ref="xlf:external-file"/>
+    </xsd:choice>
+  </xsd:complexType>
+  <xsd:simpleType name="AttrType_purpose">
+    <xsd:list>
+      <xsd:simpleType>
+        <xsd:union memberTypes="xlf:purposeValueList xlf:XTend"/>
+      </xsd:simpleType>
+    </xsd:list>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_datatype">
+    <xsd:union memberTypes="xlf:datatypeValueList xlf:XTend"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_restype">
+    <xsd:union memberTypes="xlf:restypeValueList xlf:XTend"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_alttranstype">
+    <xsd:union memberTypes="xlf:alttranstypeValueList xlf:XTend"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_context-type">
+    <xsd:union memberTypes="xlf:context-typeValueList xlf:XTend"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_state">
+    <xsd:union memberTypes="xlf:stateValueList xlf:XTend"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_state-qualifier">
+    <xsd:union memberTypes="xlf:state-qualifierValueList xlf:XTend"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_count-type">
+    <xsd:union memberTypes="xlf:restypeValueList xlf:count-typeValueList xlf:datatypeValueList xlf:stateValueList xlf:state-qualifierValueList xlf:XTend"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_InlineDelimiters">
+    <xsd:union memberTypes="xlf:InlineDelimitersValueList xlf:XTend"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_InlinePlaceholders">
+    <xsd:union memberTypes="xlf:InlinePlaceholdersValueList xlf:XTend"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_size-unit">
+    <xsd:union memberTypes="xlf:size-unitValueList xlf:XTend"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_mtype">
+    <xsd:union memberTypes="xlf:mtypeValueList xlf:XTend"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_unit">
+    <xsd:union memberTypes="xlf:unitValueList xlf:XTend"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_priority">
+    <xsd:union memberTypes="xlf:priorityValueList"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_reformat">
+    <xsd:union memberTypes="xlf:reformatValueYesNo xlf:reformatValueList"/>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_YesNo">
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="yes"/>
+      <xsd:enumeration value="no"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_Position">
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="open"/>
+      <xsd:enumeration value="close"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_assoc">
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="preceding"/>
+      <xsd:enumeration value="following"/>
+      <xsd:enumeration value="both"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_annotates">
+    <xsd:restriction base="xsd:NMTOKEN">
+      <xsd:enumeration value="source"/>
+      <xsd:enumeration value="target"/>
+      <xsd:enumeration value="general"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_Coordinates">
+    <xsd:annotation>
+      <xsd:documentation>Values for the attribute 'coord'.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:string">
+      <xsd:pattern value="(-?\d+|#);(-?\d+|#);(-?\d+|#);(-?\d+|#)"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <xsd:simpleType name="AttrType_Version">
+    <xsd:annotation>
+      <xsd:documentation>Version values: 1.0 and 1.1 are allowed for backward compatibility.</xsd:documentation>
+    </xsd:annotation>
+    <xsd:restriction base="xsd:string">
+      <xsd:enumeration value="1.2"/>
+      <xsd:enumeration value="1.1"/>
+      <xsd:enumeration value="1.0"/>
+    </xsd:restriction>
+  </xsd:simpleType>
+  <!-- Groups -->
+  <xsd:group name="ElemGroup_TextContent">
+    <xsd:choice>
+      <xsd:element ref="xlf:g"/>
+      <xsd:element ref="xlf:bpt"/>
+      <xsd:element ref="xlf:ept"/>
+      <xsd:element ref="xlf:ph"/>
+      <xsd:element ref="xlf:it"/>
+      <xsd:element ref="xlf:mrk"/>
+      <xsd:element ref="xlf:x"/>
+      <xsd:element ref="xlf:bx"/>
+      <xsd:element ref="xlf:ex"/>
+    </xsd:choice>
+  </xsd:group>
+  <xsd:attributeGroup name="AttrGroup_TextContent">
+    <xsd:attribute name="id" type="xsd:string" use="required"/>
+    <xsd:attribute name="xid" type="xsd:string" use="optional"/>
+    <xsd:attribute name="equiv-text" type="xsd:string" use="optional"/>
+    <xsd:anyAttribute namespace="##other" processContents="strict"/>
+  </xsd:attributeGroup>
+  <!-- XLIFF Structure -->
+  <xsd:element name="xliff">
+    <xsd:complexType>
+      <xsd:sequence maxOccurs="unbounded">
+        <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="strict"/>
+        <xsd:element ref="xlf:file"/>
+      </xsd:sequence>
+      <xsd:attribute name="version" type="xlf:AttrType_Version" use="required"/>
+      <xsd:attribute ref="xml:lang" use="optional"/>
+      <xsd:anyAttribute namespace="##other" processContents="strict"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="file">
+    <xsd:complexType>
+      <xsd:sequence>
+        <xsd:element minOccurs="0" ref="xlf:header"/>
+        <xsd:element ref="xlf:body"/>
+      </xsd:sequence>
+      <xsd:attribute name="original" type="xsd:string" use="required"/>
+      <xsd:attribute name="source-language" type="xsd:language" use="required"/>
+      <xsd:attribute name="datatype" type="xlf:AttrType_datatype" use="required"/>
+      <xsd:attribute name="tool-id" type="xsd:string" use="optional"/>
+      <xsd:attribute name="date" type="xsd:dateTime" use="optional"/>
+      <xsd:attribute ref="xml:space" use="optional"/>
+      <xsd:attribute name="category" type="xsd:string" use="optional"/>
+      <xsd:attribute name="target-language" type="xsd:language" use="optional"/>
+      <xsd:attribute name="product-name" type="xsd:string" use="optional"/>
+      <xsd:attribute name="product-version" type="xsd:string" use="optional"/>
+      <xsd:attribute name="build-num" type="xsd:string" use="optional"/>
+      <xsd:anyAttribute namespace="##other" processContents="strict"/>
+    </xsd:complexType>
+    <xsd:unique name="U_group_id">
+      <xsd:selector xpath=".//xlf:group"/>
+      <xsd:field xpath="@id"/>
+    </xsd:unique>
+    <xsd:key name="K_unit_id">
+      <xsd:selector xpath=".//xlf:trans-unit|.//xlf:bin-unit"/>
+      <xsd:field xpath="@id"/>
+    </xsd:key>
+    <xsd:keyref name="KR_unit_id" refer="xlf:K_unit_id">
+      <xsd:selector xpath=".//bpt|.//ept|.//it|.//ph|.//g|.//x|.//bx|.//ex|.//sub"/>
+      <xsd:field xpath="@xid"/>
+    </xsd:keyref>
+    <xsd:key name="K_tool-id">
+      <xsd:selector xpath="xlf:header/xlf:tool"/>
+      <xsd:field xpath="@tool-id"/>
+    </xsd:key>
+    <xsd:keyref name="KR_file_tool-id" refer="xlf:K_tool-id">
+      <xsd:selector xpath="."/>
+      <xsd:field xpath="@tool-id"/>
+    </xsd:keyref>
+    <xsd:keyref name="KR_phase_tool-id" refer="xlf:K_tool-id">
+      <xsd:selector xpath="xlf:header/xlf:phase-group/xlf:phase"/>
+      <xsd:field xpath="@tool-id"/>
+    </xsd:keyref>
+    <xsd:keyref name="KR_alt-trans_tool-id" refer="xlf:K_tool-id">
+      <xsd:selector xpath=".//xlf:trans-unit/xlf:alt-trans"/>
+      <xsd:field xpath="@tool-id"/>
+    </xsd:keyref>
+    <xsd:key name="K_count-group_name">
+      <xsd:selector xpath=".//xlf:count-group"/>
+      <xsd:field xpath="@name"/>
+    </xsd:key>
+    <xsd:unique name="U_context-group_name">
+      <xsd:selector xpath=".//xlf:context-group"/>
+      <xsd:field xpath="@name"/>
+    </xsd:unique>
+    <xsd:key name="K_phase-name">
+      <xsd:selector xpath="xlf:header/xlf:phase-group/xlf:phase"/>
+      <xsd:field xpath="@phase-name"/>
+    </xsd:key>
+    <xsd:keyref name="KR_phase-name" refer="xlf:K_phase-name">
+      <xsd:selector xpath=".//xlf:count|.//xlf:trans-unit|.//xlf:target|.//bin-unit|.//bin-target"/>
+      <xsd:field xpath="@phase-name"/>
+    </xsd:keyref>
+    <xsd:unique name="U_uid">
+      <xsd:selector xpath=".//xlf:external-file"/>
+      <xsd:field xpath="@uid"/>
+    </xsd:unique>
+  </xsd:element>
+  <xsd:element name="header">
+    <xsd:complexType>
+      <xsd:sequence>
+        <xsd:element minOccurs="0" name="skl" type="xlf:ElemType_ExternalReference"/>
+        <xsd:element minOccurs="0" ref="xlf:phase-group"/>
+        <xsd:choice maxOccurs="unbounded" minOccurs="0">
+          <xsd:element name="glossary" type="xlf:ElemType_ExternalReference"/>
+          <xsd:element name="reference" type="xlf:ElemType_ExternalReference"/>
+          <xsd:element ref="xlf:count-group"/>
+          <xsd:element ref="xlf:note"/>
+          <xsd:element ref="xlf:tool"/>
+        </xsd:choice>
+        <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="strict"/>
+      </xsd:sequence>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="internal-file">
+    <xsd:complexType>
+      <xsd:simpleContent>
+        <xsd:extension base="xsd:string">
+          <xsd:attribute name="form" type="xsd:string"/>
+          <xsd:attribute name="crc" type="xsd:NMTOKEN"/>
+        </xsd:extension>
+      </xsd:simpleContent>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="external-file">
+    <xsd:complexType>
+      <xsd:attribute name="href" type="xsd:string" use="required"/>
+      <xsd:attribute name="crc" type="xsd:NMTOKEN"/>
+      <xsd:attribute name="uid" type="xsd:NMTOKEN"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="note">
+    <xsd:complexType>
+      <xsd:simpleContent>
+        <xsd:extension base="xsd:string">
+          <xsd:attribute ref="xml:lang" use="optional"/>
+          <xsd:attribute default="1" name="priority" type="xlf:AttrType_priority" use="optional"/>
+          <xsd:attribute name="from" type="xsd:string" use="optional"/>
+          <xsd:attribute default="general" name="annotates" type="xlf:AttrType_annotates" use="optional"/>
+        </xsd:extension>
+      </xsd:simpleContent>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="phase-group">
+    <xsd:complexType>
+      <xsd:sequence maxOccurs="unbounded">
+        <xsd:element ref="xlf:phase"/>
+      </xsd:sequence>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="phase">
+    <xsd:complexType>
+      <xsd:sequence maxOccurs="unbounded" minOccurs="0">
+        <xsd:element ref="xlf:note"/>
+      </xsd:sequence>
+      <xsd:attribute name="phase-name" type="xsd:string" use="required"/>
+      <xsd:attribute name="process-name" type="xsd:string" use="required"/>
+      <xsd:attribute name="company-name" type="xsd:string" use="optional"/>
+      <xsd:attribute name="tool-id" type="xsd:string" use="optional"/>
+      <xsd:attribute name="date" type="xsd:dateTime" use="optional"/>
+      <xsd:attribute name="job-id" type="xsd:string" use="optional"/>
+      <xsd:attribute name="contact-name" type="xsd:string" use="optional"/>
+      <xsd:attribute name="contact-email" type="xsd:string" use="optional"/>
+      <xsd:attribute name="contact-phone" type="xsd:string" use="optional"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="count-group">
+    <xsd:complexType>
+      <xsd:sequence maxOccurs="unbounded" minOccurs="0">
+        <xsd:element ref="xlf:count"/>
+      </xsd:sequence>
+      <xsd:attribute name="name" type="xsd:string" use="required"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="count">
+    <xsd:complexType>
+      <xsd:simpleContent>
+        <xsd:extension base="xsd:string">
+          <xsd:attribute name="count-type" type="xlf:AttrType_count-type" use="optional"/>
+          <xsd:attribute name="phase-name" type="xsd:string" use="optional"/>
+          <xsd:attribute default="word" name="unit" type="xlf:AttrType_unit" use="optional"/>
+        </xsd:extension>
+      </xsd:simpleContent>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="context-group">
+    <xsd:complexType>
+      <xsd:sequence maxOccurs="unbounded">
+        <xsd:element ref="xlf:context"/>
+      </xsd:sequence>
+      <xsd:attribute name="name" type="xsd:string" use="optional"/>
+      <xsd:attribute name="crc" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="purpose" type="xlf:AttrType_purpose" use="optional"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="context">
+    <xsd:complexType>
+      <xsd:simpleContent>
+        <xsd:extension base="xsd:string">
+          <xsd:attribute name="context-type" type="xlf:AttrType_context-type" use="required"/>
+          <xsd:attribute default="no" name="match-mandatory" type="xlf:AttrType_YesNo" use="optional"/>
+          <xsd:attribute name="crc" type="xsd:NMTOKEN" use="optional"/>
+        </xsd:extension>
+      </xsd:simpleContent>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="tool">
+    <xsd:complexType mixed="true">
+      <xsd:sequence>
+        <xsd:any namespace="##any" processContents="strict" minOccurs="0" maxOccurs="unbounded"/>
+      </xsd:sequence>
+      <xsd:attribute name="tool-id" type="xsd:string" use="required"/>
+      <xsd:attribute name="tool-name" type="xsd:string" use="required"/>
+      <xsd:attribute name="tool-version" type="xsd:string" use="optional"/>
+      <xsd:attribute name="tool-company" type="xsd:string" use="optional"/>
+      <xsd:anyAttribute namespace="##other" processContents="strict"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="body">
+    <xsd:complexType>
+      <xsd:choice maxOccurs="unbounded" minOccurs="0">
+        <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:group"/>
+        <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:trans-unit"/>
+        <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:bin-unit"/>
+      </xsd:choice>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="group">
+    <xsd:complexType>
+      <xsd:sequence>
+        <xsd:sequence>
+          <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:context-group"/>
+          <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:count-group"/>
+          <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:note"/>
+          <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="strict"/>
+        </xsd:sequence>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:group"/>
+          <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:trans-unit"/>
+          <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:bin-unit"/>
+        </xsd:choice>
+      </xsd:sequence>
+      <xsd:attribute name="id" type="xsd:string" use="optional"/>
+      <xsd:attribute name="datatype" type="xlf:AttrType_datatype" use="optional"/>
+      <xsd:attribute default="default" ref="xml:space" use="optional"/>
+      <xsd:attribute name="restype" type="xlf:AttrType_restype" use="optional"/>
+      <xsd:attribute name="resname" type="xsd:string" use="optional"/>
+      <xsd:attribute name="extradata" type="xsd:string" use="optional"/>
+      <xsd:attribute name="extype" type="xsd:string" use="optional"/>
+      <xsd:attribute name="help-id" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="menu" type="xsd:string" use="optional"/>
+      <xsd:attribute name="menu-option" type="xsd:string" use="optional"/>
+      <xsd:attribute name="menu-name" type="xsd:string" use="optional"/>
+      <xsd:attribute name="coord" type="xlf:AttrType_Coordinates" use="optional"/>
+      <xsd:attribute name="font" type="xsd:string" use="optional"/>
+      <xsd:attribute name="css-style" type="xsd:string" use="optional"/>
+      <xsd:attribute name="style" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="exstyle" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute default="yes" name="translate" type="xlf:AttrType_YesNo" use="optional"/>
+      <xsd:attribute default="yes" name="reformat" type="xlf:AttrType_reformat" use="optional"/>
+      <xsd:attribute default="pixel" name="size-unit" type="xlf:AttrType_size-unit" use="optional"/>
+      <xsd:attribute name="maxwidth" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="minwidth" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="maxheight" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="minheight" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="maxbytes" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="minbytes" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="charclass" type="xsd:string" use="optional"/>
+      <xsd:attribute default="no" name="merged-trans" type="xlf:AttrType_YesNo" use="optional"/>
+      <xsd:anyAttribute namespace="##other" processContents="strict"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="trans-unit">
+    <xsd:complexType>
+      <xsd:sequence>
+        <xsd:element ref="xlf:source"/>
+        <xsd:element minOccurs="0" ref="xlf:seg-source"/>
+        <xsd:element minOccurs="0" ref="xlf:target"/>
+        <xsd:choice maxOccurs="unbounded" minOccurs="0">
+          <xsd:element ref="xlf:context-group"/>
+          <xsd:element ref="xlf:count-group"/>
+          <xsd:element ref="xlf:note"/>
+          <xsd:element ref="xlf:alt-trans"/>
+        </xsd:choice>
+        <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="strict"/>
+      </xsd:sequence>
+      <xsd:attribute name="id" type="xsd:string" use="required"/>
+      <xsd:attribute name="approved" type="xlf:AttrType_YesNo" use="optional"/>
+      <xsd:attribute default="yes" name="translate" type="xlf:AttrType_YesNo" use="optional"/>
+      <xsd:attribute default="yes" name="reformat" type="xlf:AttrType_reformat" use="optional"/>
+      <xsd:attribute default="default" ref="xml:space" use="optional"/>
+      <xsd:attribute name="datatype" type="xlf:AttrType_datatype" use="optional"/>
+      <xsd:attribute name="phase-name" type="xsd:string" use="optional"/>
+      <xsd:attribute name="restype" type="xlf:AttrType_restype" use="optional"/>
+      <xsd:attribute name="resname" type="xsd:string" use="optional"/>
+      <xsd:attribute name="extradata" type="xsd:string" use="optional"/>
+      <xsd:attribute name="extype" type="xsd:string" use="optional"/>
+      <xsd:attribute name="help-id" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="menu" type="xsd:string" use="optional"/>
+      <xsd:attribute name="menu-option" type="xsd:string" use="optional"/>
+      <xsd:attribute name="menu-name" type="xsd:string" use="optional"/>
+      <xsd:attribute name="coord" type="xlf:AttrType_Coordinates" use="optional"/>
+      <xsd:attribute name="font" type="xsd:string" use="optional"/>
+      <xsd:attribute name="css-style" type="xsd:string" use="optional"/>
+      <xsd:attribute name="style" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="exstyle" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute default="pixel" name="size-unit" type="xlf:AttrType_size-unit" use="optional"/>
+      <xsd:attribute name="maxwidth" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="minwidth" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="maxheight" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="minheight" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="maxbytes" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="minbytes" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="charclass" type="xsd:string" use="optional"/>
+      <xsd:attribute default="yes" name="merged-trans" type="xlf:AttrType_YesNo" use="optional"/>
+      <xsd:anyAttribute namespace="##other" processContents="strict"/>
+    </xsd:complexType>
+    <xsd:unique name="U_tu_segsrc_mid">
+      <xsd:selector xpath="./xlf:seg-source/xlf:mrk"/>
+      <xsd:field xpath="@mid"/>
+    </xsd:unique>
+    <xsd:keyref name="KR_tu_segsrc_mid" refer="xlf:U_tu_segsrc_mid">
+      <xsd:selector xpath="./xlf:target/xlf:mrk|./xlf:alt-trans"/>
+      <xsd:field xpath="@mid"/>
+    </xsd:keyref>
+  </xsd:element>
+  <xsd:element name="source">
+    <xsd:complexType mixed="true">
+      <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/>
+      <xsd:attribute ref="xml:lang" use="optional"/>
+      <xsd:anyAttribute namespace="##other" processContents="strict"/>
+    </xsd:complexType>
+    <xsd:unique name="U_source_bpt_rid">
+      <xsd:selector xpath=".//xlf:bpt"/>
+      <xsd:field xpath="@rid"/>
+    </xsd:unique>
+    <xsd:keyref name="KR_source_ept_rid" refer="xlf:U_source_bpt_rid">
+      <xsd:selector xpath=".//xlf:ept"/>
+      <xsd:field xpath="@rid"/>
+    </xsd:keyref>
+    <xsd:unique name="U_source_bx_rid">
+      <xsd:selector xpath=".//xlf:bx"/>
+      <xsd:field xpath="@rid"/>
+    </xsd:unique>
+    <xsd:keyref name="KR_source_ex_rid" refer="xlf:U_source_bx_rid">
+      <xsd:selector xpath=".//xlf:ex"/>
+      <xsd:field xpath="@rid"/>
+    </xsd:keyref>
+  </xsd:element>
+  <xsd:element name="seg-source">
+    <xsd:complexType mixed="true">
+      <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/>
+      <xsd:attribute ref="xml:lang" use="optional"/>
+      <xsd:anyAttribute namespace="##other" processContents="strict"/>
+    </xsd:complexType>
+    <xsd:unique name="U_segsrc_bpt_rid">
+      <xsd:selector xpath=".//xlf:bpt"/>
+      <xsd:field xpath="@rid"/>
+    </xsd:unique>
+    <xsd:keyref name="KR_segsrc_ept_rid" refer="xlf:U_segsrc_bpt_rid">
+      <xsd:selector xpath=".//xlf:ept"/>
+      <xsd:field xpath="@rid"/>
+    </xsd:keyref>
+    <xsd:unique name="U_segsrc_bx_rid">
+      <xsd:selector xpath=".//xlf:bx"/>
+      <xsd:field xpath="@rid"/>
+    </xsd:unique>
+    <xsd:keyref name="KR_segsrc_ex_rid" refer="xlf:U_segsrc_bx_rid">
+      <xsd:selector xpath=".//xlf:ex"/>
+      <xsd:field xpath="@rid"/>
+    </xsd:keyref>
+  </xsd:element>
+  <xsd:element name="target">
+    <xsd:complexType mixed="true">
+      <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/>
+      <xsd:attribute name="state" type="xlf:AttrType_state" use="optional"/>
+      <xsd:attribute name="state-qualifier" type="xlf:AttrType_state-qualifier" use="optional"/>
+      <xsd:attribute name="phase-name" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute ref="xml:lang" use="optional"/>
+      <xsd:attribute name="resname" type="xsd:string" use="optional"/>
+      <xsd:attribute name="coord" type="xlf:AttrType_Coordinates" use="optional"/>
+      <xsd:attribute name="font" type="xsd:string" use="optional"/>
+      <xsd:attribute name="css-style" type="xsd:string" use="optional"/>
+      <xsd:attribute name="style" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="exstyle" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute default="yes" name="equiv-trans" type="xlf:AttrType_YesNo" use="optional"/>
+      <xsd:anyAttribute namespace="##other" processContents="strict"/>
+    </xsd:complexType>
+    <xsd:unique name="U_target_bpt_rid">
+      <xsd:selector xpath=".//xlf:bpt"/>
+      <xsd:field xpath="@rid"/>
+    </xsd:unique>
+    <xsd:keyref name="KR_target_ept_rid" refer="xlf:U_target_bpt_rid">
+      <xsd:selector xpath=".//xlf:ept"/>
+      <xsd:field xpath="@rid"/>
+    </xsd:keyref>
+    <xsd:unique name="U_target_bx_rid">
+      <xsd:selector xpath=".//xlf:bx"/>
+      <xsd:field xpath="@rid"/>
+    </xsd:unique>
+    <xsd:keyref name="KR_target_ex_rid" refer="xlf:U_target_bx_rid">
+      <xsd:selector xpath=".//xlf:ex"/>
+      <xsd:field xpath="@rid"/>
+    </xsd:keyref>
+  </xsd:element>
+  <xsd:element name="alt-trans">
+    <xsd:complexType>
+      <xsd:sequence>
+        <xsd:element minOccurs="0" ref="xlf:source"/>
+        <xsd:element minOccurs="0" ref="xlf:seg-source"/>
+        <xsd:element maxOccurs="1" ref="xlf:target"/>
+        <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:context-group"/>
+        <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:note"/>
+        <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="strict"/>
+      </xsd:sequence>
+      <xsd:attribute name="match-quality" type="xsd:string" use="optional"/>
+      <xsd:attribute name="tool-id" type="xsd:string" use="optional"/>
+      <xsd:attribute name="crc" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute ref="xml:lang" use="optional"/>
+      <xsd:attribute name="origin" type="xsd:string" use="optional"/>
+      <xsd:attribute name="datatype" type="xlf:AttrType_datatype" use="optional"/>
+      <xsd:attribute default="default" ref="xml:space" use="optional"/>
+      <xsd:attribute name="restype" type="xlf:AttrType_restype" use="optional"/>
+      <xsd:attribute name="resname" type="xsd:string" use="optional"/>
+      <xsd:attribute name="extradata" type="xsd:string" use="optional"/>
+      <xsd:attribute name="extype" type="xsd:string" use="optional"/>
+      <xsd:attribute name="help-id" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="menu" type="xsd:string" use="optional"/>
+      <xsd:attribute name="menu-option" type="xsd:string" use="optional"/>
+      <xsd:attribute name="menu-name" type="xsd:string" use="optional"/>
+      <xsd:attribute name="mid" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="coord" type="xlf:AttrType_Coordinates" use="optional"/>
+      <xsd:attribute name="font" type="xsd:string" use="optional"/>
+      <xsd:attribute name="css-style" type="xsd:string" use="optional"/>
+      <xsd:attribute name="style" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="exstyle" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="phase-name" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute default="proposal" name="alttranstype" type="xlf:AttrType_alttranstype" use="optional"/>
+      <xsd:anyAttribute namespace="##other" processContents="strict"/>
+    </xsd:complexType>
+    <xsd:unique name="U_at_segsrc_mid">
+      <xsd:selector xpath="./xlf:seg-source/xlf:mrk"/>
+      <xsd:field xpath="@mid"/>
+    </xsd:unique>
+    <xsd:keyref name="KR_at_segsrc_mid" refer="xlf:U_at_segsrc_mid">
+      <xsd:selector xpath="./xlf:target/xlf:mrk"/>
+      <xsd:field xpath="@mid"/>
+    </xsd:keyref>
+  </xsd:element>
+  <xsd:element name="bin-unit">
+    <xsd:complexType>
+      <xsd:sequence>
+        <xsd:element ref="xlf:bin-source"/>
+        <xsd:element minOccurs="0" ref="xlf:bin-target"/>
+        <xsd:choice maxOccurs="unbounded" minOccurs="0">
+          <xsd:element ref="xlf:context-group"/>
+          <xsd:element ref="xlf:count-group"/>
+          <xsd:element ref="xlf:note"/>
+          <xsd:element ref="xlf:trans-unit"/>
+        </xsd:choice>
+        <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="strict"/>
+      </xsd:sequence>
+      <xsd:attribute name="id" type="xsd:string" use="required"/>
+      <xsd:attribute name="mime-type" type="xlf:mime-typeValueList" use="required"/>
+      <xsd:attribute name="approved" type="xlf:AttrType_YesNo" use="optional"/>
+      <xsd:attribute default="yes" name="translate" type="xlf:AttrType_YesNo" use="optional"/>
+      <xsd:attribute default="yes" name="reformat" type="xlf:AttrType_reformat" use="optional"/>
+      <xsd:attribute name="restype" type="xlf:AttrType_restype" use="optional"/>
+      <xsd:attribute name="resname" type="xsd:string" use="optional"/>
+      <xsd:attribute name="phase-name" type="xsd:string" use="optional"/>
+      <xsd:anyAttribute namespace="##other" processContents="strict"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="bin-source">
+    <xsd:complexType>
+      <xsd:choice>
+        <xsd:element ref="xlf:internal-file"/>
+        <xsd:element ref="xlf:external-file"/>
+      </xsd:choice>
+      <xsd:anyAttribute namespace="##other" processContents="strict"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="bin-target">
+    <xsd:complexType>
+      <xsd:choice>
+        <xsd:element ref="xlf:internal-file"/>
+        <xsd:element ref="xlf:external-file"/>
+      </xsd:choice>
+      <xsd:attribute name="mime-type" type="xlf:mime-typeValueList" use="optional"/>
+      <xsd:attribute name="state" type="xlf:AttrType_state" use="optional"/>
+      <xsd:attribute name="state-qualifier" type="xlf:AttrType_state-qualifier" use="optional"/>
+      <xsd:attribute name="phase-name" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="restype" type="xlf:AttrType_restype" use="optional"/>
+      <xsd:attribute name="resname" type="xsd:string" use="optional"/>
+      <xsd:anyAttribute namespace="##other" processContents="strict"/>
+    </xsd:complexType>
+  </xsd:element>
+  <!-- Element for inline codes -->
+  <xsd:element name="g">
+    <xsd:complexType mixed="true">
+      <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/>
+      <xsd:attribute name="ctype" type="xlf:AttrType_InlineDelimiters" use="optional"/>
+      <xsd:attribute default="yes" name="clone" type="xlf:AttrType_YesNo" use="optional"/>
+      <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="x">
+    <xsd:complexType>
+      <xsd:attribute name="ctype" type="xlf:AttrType_InlinePlaceholders" use="optional"/>
+      <xsd:attribute default="yes" name="clone" type="xlf:AttrType_YesNo" use="optional"/>
+      <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="bx">
+    <xsd:complexType>
+      <xsd:attribute name="rid" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="ctype" type="xlf:AttrType_InlineDelimiters" use="optional"/>
+      <xsd:attribute default="yes" name="clone" type="xlf:AttrType_YesNo" use="optional"/>
+      <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="ex">
+    <xsd:complexType>
+      <xsd:attribute name="rid" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="ph">
+    <xsd:complexType mixed="true">
+      <xsd:sequence maxOccurs="unbounded" minOccurs="0">
+        <xsd:element ref="xlf:sub"/>
+      </xsd:sequence>
+      <xsd:attribute name="ctype" type="xlf:AttrType_InlinePlaceholders" use="optional"/>
+      <xsd:attribute name="crc" type="xsd:string" use="optional"/>
+      <xsd:attribute name="assoc" type="xlf:AttrType_assoc" use="optional"/>
+      <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="bpt">
+    <xsd:complexType mixed="true">
+      <xsd:sequence maxOccurs="unbounded" minOccurs="0">
+        <xsd:element ref="xlf:sub"/>
+      </xsd:sequence>
+      <xsd:attribute name="rid" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="ctype" type="xlf:AttrType_InlineDelimiters" use="optional"/>
+      <xsd:attribute name="crc" type="xsd:string" use="optional"/>
+      <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="ept">
+    <xsd:complexType mixed="true">
+      <xsd:sequence maxOccurs="unbounded" minOccurs="0">
+        <xsd:element ref="xlf:sub"/>
+      </xsd:sequence>
+      <xsd:attribute name="rid" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="crc" type="xsd:string" use="optional"/>
+      <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="it">
+    <xsd:complexType mixed="true">
+      <xsd:sequence maxOccurs="unbounded" minOccurs="0">
+        <xsd:element ref="xlf:sub"/>
+      </xsd:sequence>
+      <xsd:attribute name="pos" type="xlf:AttrType_Position" use="required"/>
+      <xsd:attribute name="rid" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="ctype" type="xlf:AttrType_InlineDelimiters" use="optional"/>
+      <xsd:attribute name="crc" type="xsd:string" use="optional"/>
+      <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="sub">
+    <xsd:complexType mixed="true">
+      <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/>
+      <xsd:attribute name="datatype" type="xlf:AttrType_datatype" use="optional"/>
+      <xsd:attribute name="ctype" type="xlf:AttrType_InlineDelimiters" use="optional"/>
+      <xsd:attribute name="xid" type="xsd:string" use="optional"/>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:element name="mrk">
+    <xsd:complexType mixed="true">
+      <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/>
+      <xsd:attribute name="mtype" type="xlf:AttrType_mtype" use="required"/>
+      <xsd:attribute name="mid" type="xsd:NMTOKEN" use="optional"/>
+      <xsd:attribute name="comment" type="xsd:string" use="optional"/>
+      <xsd:anyAttribute namespace="##other" processContents="strict"/>
+    </xsd:complexType>
+  </xsd:element>
+</xsd:schema>
diff --git a/vendor/symfony/translation/Resources/schemas/xliff-core-2.0.xsd b/vendor/symfony/translation/Resources/schemas/xliff-core-2.0.xsd
new file mode 100755
index 0000000..963232f
--- /dev/null
+++ b/vendor/symfony/translation/Resources/schemas/xliff-core-2.0.xsd
@@ -0,0 +1,411 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    XLIFF Version 2.0
+    OASIS Standard
+    05 August 2014
+    Copyright (c) OASIS Open 2014. All rights reserved.
+    Source: http://docs.oasis-open.org/xliff/xliff-core/v2.0/os/schemas/
+     -->
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+    elementFormDefault="qualified"
+    xmlns:xlf="urn:oasis:names:tc:xliff:document:2.0"
+    targetNamespace="urn:oasis:names:tc:xliff:document:2.0">
+
+  <!-- Import -->
+
+  <xs:import namespace="http://www.w3.org/XML/1998/namespace"
+      schemaLocation="informativeCopiesOf3rdPartySchemas/w3c/xml.xsd"/>
+
+  <!-- Element Group -->
+
+  <xs:group name="inline">
+    <xs:choice>
+      <xs:element ref="xlf:cp"/>
+      <xs:element ref="xlf:ph"/>
+      <xs:element ref="xlf:pc"/>
+      <xs:element ref="xlf:sc"/>
+      <xs:element ref="xlf:ec"/>
+      <xs:element ref="xlf:mrk"/>
+      <xs:element ref="xlf:sm"/>
+      <xs:element ref="xlf:em"/>
+    </xs:choice>
+  </xs:group>
+
+  <!-- Attribute Types -->
+
+  <xs:simpleType name="yesNo">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="yes"/>
+      <xs:enumeration value="no"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="yesNoFirstNo">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="yes"/>
+      <xs:enumeration value="firstNo"/>
+      <xs:enumeration value="no"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="dirValue">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="ltr"/>
+      <xs:enumeration value="rtl"/>
+      <xs:enumeration value="auto"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="appliesTo">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="source"/>
+      <xs:enumeration value="target"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="userDefinedValue">
+    <xs:restriction base="xs:string">
+      <xs:pattern value="[^\s:]+:[^\s:]+"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="attrType_type">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="fmt"/>
+      <xs:enumeration value="ui"/>
+      <xs:enumeration value="quote"/>
+      <xs:enumeration value="link"/>
+      <xs:enumeration value="image"/>
+      <xs:enumeration value="other"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="typeForMrkValues">
+    <xs:restriction base="xs:NMTOKEN">
+      <xs:enumeration value="generic"/>
+      <xs:enumeration value="comment"/>
+      <xs:enumeration value="term"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="attrType_typeForMrk">
+    <xs:union memberTypes="xlf:typeForMrkValues xlf:userDefinedValue"/>
+  </xs:simpleType>
+
+  <xs:simpleType name="priorityValue">
+    <xs:restriction base="xs:positiveInteger">
+      <xs:minInclusive value="1"/>
+      <xs:maxInclusive value="10"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="stateType">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="initial"/>
+      <xs:enumeration value="translated"/>
+      <xs:enumeration value="reviewed"/>
+      <xs:enumeration value="final"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <!-- Structural Elements -->
+
+  <xs:element name="xliff">
+    <xs:complexType mixed="false">
+      <xs:sequence>
+        <xs:element minOccurs="1" maxOccurs="unbounded" ref="xlf:file"/>
+      </xs:sequence>
+      <xs:attribute name="version" use="required"/>
+      <xs:attribute name="srcLang" use="required"/>
+      <xs:attribute name="trgLang" use="optional"/>
+      <xs:attribute ref="xml:space" use="optional" default="default"/>
+      <xs:anyAttribute namespace="##other" processContents="lax"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="file">
+    <xs:complexType mixed="false">
+      <xs:sequence>
+        <xs:element minOccurs="0" maxOccurs="1" ref="xlf:skeleton"/>
+        <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"
+            processContents="lax"/>
+        <xs:element minOccurs="0" maxOccurs="1" ref="xlf:notes"/>
+        <xs:choice minOccurs="1" maxOccurs="unbounded">
+          <xs:element ref="xlf:unit"/>
+          <xs:element ref="xlf:group"/>
+        </xs:choice>
+      </xs:sequence>
+      <xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
+      <xs:attribute name="canResegment" use="optional" type="xlf:yesNo" default="yes"/>
+      <xs:attribute name="original" use="optional"/>
+      <xs:attribute name="translate" use="optional" type="xlf:yesNo" default="yes"/>
+      <xs:attribute name="srcDir" use="optional" type="xlf:dirValue" default="auto"/>
+      <xs:attribute name="trgDir" use="optional" type="xlf:dirValue" default="auto"/>
+      <xs:attribute ref="xml:space" use="optional"/>
+      <xs:anyAttribute namespace="##other" processContents="lax"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="skeleton">
+    <xs:complexType mixed="true">
+      <xs:sequence>
+        <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"
+            processContents="lax"/>
+      </xs:sequence>
+      <xs:attribute name="href" use="optional"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="group">
+    <xs:complexType mixed="false">
+      <xs:sequence>
+        <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"
+            processContents="lax"/>
+        <xs:element minOccurs="0" maxOccurs="1" ref="xlf:notes"/>
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element ref="xlf:unit"/>
+          <xs:element ref="xlf:group"/>
+        </xs:choice>
+      </xs:sequence>
+      <xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
+      <xs:attribute name="name" use="optional"/>
+      <xs:attribute name="canResegment" use="optional" type="xlf:yesNo"/>
+      <xs:attribute name="translate" use="optional" type="xlf:yesNo"/>
+      <xs:attribute name="srcDir" use="optional" type="xlf:dirValue"/>
+      <xs:attribute name="trgDir" use="optional" type="xlf:dirValue"/>
+      <xs:attribute name="type" use="optional" type="xlf:userDefinedValue"/>
+      <xs:attribute ref="xml:space" use="optional"/>
+      <xs:anyAttribute namespace="##other" processContents="lax"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="unit">
+    <xs:complexType mixed="false">
+      <xs:sequence>
+        <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"
+            processContents="lax"/>
+        <xs:element minOccurs="0" maxOccurs="1" ref="xlf:notes"/>
+        <xs:element minOccurs="0" maxOccurs="1" ref="xlf:originalData"/>
+        <xs:choice minOccurs="1" maxOccurs="unbounded">
+          <xs:element ref="xlf:segment"/>
+          <xs:element ref="xlf:ignorable"/>
+        </xs:choice>
+      </xs:sequence>
+      <xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
+      <xs:attribute name="name" use="optional"/>
+      <xs:attribute name="canResegment" use="optional" type="xlf:yesNo"/>
+      <xs:attribute name="translate" use="optional" type="xlf:yesNo"/>
+      <xs:attribute name="srcDir" use="optional" type="xlf:dirValue"/>
+      <xs:attribute name="trgDir" use="optional" type="xlf:dirValue"/>
+      <xs:attribute ref="xml:space" use="optional"/>
+      <xs:attribute name="type" use="optional" type="xlf:userDefinedValue"/>
+      <xs:anyAttribute namespace="##other" processContents="lax"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="segment">
+    <xs:complexType mixed="false">
+      <xs:sequence>
+        <xs:element minOccurs="1" maxOccurs="1" ref="xlf:source"/>
+        <xs:element minOccurs="0" maxOccurs="1" ref="xlf:target"/>
+      </xs:sequence>
+      <xs:attribute name="id" use="optional" type="xs:NMTOKEN"/>
+      <xs:attribute name="canResegment" use="optional" type="xlf:yesNo"/>
+      <xs:attribute name="state" use="optional" type="xlf:stateType" default="initial"/>
+      <xs:attribute name="subState" use="optional"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="ignorable">
+    <xs:complexType mixed="false">
+      <xs:sequence>
+        <xs:element minOccurs="1" maxOccurs="1" ref="xlf:source"/>
+        <xs:element minOccurs="0" maxOccurs="1" ref="xlf:target"/>
+      </xs:sequence>
+      <xs:attribute name="id" use="optional" type="xs:NMTOKEN"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="notes">
+    <xs:complexType mixed="false">
+      <xs:sequence>
+        <xs:element minOccurs="1" maxOccurs="unbounded" ref="xlf:note"/>
+      </xs:sequence>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="note">
+    <xs:complexType mixed="true">
+      <xs:attribute name="id" use="optional" type="xs:NMTOKEN"/>
+      <xs:attribute name="appliesTo" use="optional" type="xlf:appliesTo"/>
+      <xs:attribute name="category" use="optional"/>
+      <xs:attribute name="priority" use="optional" type="xlf:priorityValue" default="1"/>
+      <xs:anyAttribute namespace="##other" processContents="lax"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="originalData">
+    <xs:complexType mixed="false">
+      <xs:sequence>
+        <xs:element minOccurs="1" maxOccurs="unbounded" ref="xlf:data"/>
+      </xs:sequence>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="data">
+    <xs:complexType mixed="true">
+      <xs:sequence>
+        <xs:element minOccurs="0" maxOccurs="unbounded" ref="xlf:cp"/>
+      </xs:sequence>
+      <xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
+      <xs:attribute name="dir" use="optional" type="xlf:dirValue" default="auto"/>
+      <xs:attribute ref="xml:space" use="optional" fixed="preserve"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="source">
+    <xs:complexType mixed="true">
+      <xs:group ref="xlf:inline" minOccurs="0" maxOccurs="unbounded"/>
+      <xs:attribute ref="xml:lang" use="optional"/>
+      <xs:attribute ref="xml:space" use="optional"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="target">
+    <xs:complexType mixed="true">
+      <xs:group ref="xlf:inline" minOccurs="0" maxOccurs="unbounded"/>
+      <xs:attribute ref="xml:lang" use="optional"/>
+      <xs:attribute ref="xml:space" use="optional"/>
+      <xs:attribute name="order" use="optional" type="xs:positiveInteger"/>
+    </xs:complexType>
+  </xs:element>
+
+  <!-- Inline Elements -->
+
+  <xs:element name="cp">
+    <!-- Code Point -->
+    <xs:complexType mixed="false">
+      <xs:attribute name="hex" use="required" type="xs:hexBinary"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="ph">
+    <!-- Placeholder -->
+    <xs:complexType mixed="false">
+      <xs:attribute name="canCopy" use="optional" type="xlf:yesNo" default="yes"/>
+      <xs:attribute name="canDelete" use="optional" type="xlf:yesNo" default="yes"/>
+      <xs:attribute name="canReorder" use="optional" type="xlf:yesNoFirstNo" default="yes"/>
+      <xs:attribute name="copyOf" use="optional" type="xs:NMTOKEN"/>
+      <xs:attribute name="disp" use="optional"/>
+      <xs:attribute name="equiv" use="optional"/>
+      <xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
+      <xs:attribute name="dataRef" use="optional" type="xs:NMTOKEN"/>
+      <xs:attribute name="subFlows" use="optional" type="xs:NMTOKENS"/>
+      <xs:attribute name="subType" use="optional" type="xlf:userDefinedValue"/>
+      <xs:attribute name="type" use="optional" type="xlf:attrType_type"/>
+      <xs:anyAttribute namespace="##other" processContents="lax"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="pc">
+    <!-- Paired Code -->
+    <xs:complexType mixed="true">
+      <xs:group ref="xlf:inline" minOccurs="0" maxOccurs="unbounded"/>
+      <xs:attribute name="canCopy" use="optional" type="xlf:yesNo" default="yes"/>
+      <xs:attribute name="canDelete" use="optional" type="xlf:yesNo" default="yes"/>
+      <xs:attribute name="canOverlap" use="optional" type="xlf:yesNo"/>
+      <xs:attribute name="canReorder" use="optional" type="xlf:yesNoFirstNo" default="yes"/>
+      <xs:attribute name="copyOf" use="optional" type="xs:NMTOKEN"/>
+      <xs:attribute name="dispEnd" use="optional"/>
+      <xs:attribute name="dispStart" use="optional"/>
+      <xs:attribute name="equivEnd" use="optional"/>
+      <xs:attribute name="equivStart" use="optional"/>
+      <xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
+      <xs:attribute name="dataRefEnd" use="optional" type="xs:NMTOKEN"/>
+      <xs:attribute name="dataRefStart" use="optional" type="xs:NMTOKEN"/>
+      <xs:attribute name="subFlowsEnd" use="optional" type="xs:NMTOKENS"/>
+      <xs:attribute name="subFlowsStart" use="optional" type="xs:NMTOKENS"/>
+      <xs:attribute name="subType" use="optional" type="xlf:userDefinedValue"/>
+      <xs:attribute name="type" use="optional" type="xlf:attrType_type"/>
+      <xs:attribute name="dir" use="optional" type="xlf:dirValue"/>
+      <xs:anyAttribute namespace="##other" processContents="lax"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="sc">
+    <!-- Start Code -->
+    <xs:complexType mixed="false">
+      <xs:attribute name="canCopy" use="optional" type="xlf:yesNo" default="yes"/>
+      <xs:attribute name="canDelete" use="optional" type="xlf:yesNo" default="yes"/>
+      <xs:attribute name="canOverlap" use="optional" type="xlf:yesNo" default="yes"/>
+      <xs:attribute name="canReorder" use="optional" type="xlf:yesNoFirstNo" default="yes"/>
+      <xs:attribute name="copyOf" use="optional" type="xs:NMTOKEN"/>
+      <xs:attribute name="dataRef" use="optional" type="xs:NMTOKEN"/>
+      <xs:attribute name="dir" use="optional" type="xlf:dirValue"/>
+      <xs:attribute name="disp" use="optional"/>
+      <xs:attribute name="equiv" use="optional"/>
+      <xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
+      <xs:attribute name="isolated" use="optional" type="xlf:yesNo" default="no"/>
+      <xs:attribute name="subFlows" use="optional" type="xs:NMTOKENS"/>
+      <xs:attribute name="subType" use="optional" type="xlf:userDefinedValue"/>
+      <xs:attribute name="type" use="optional" type="xlf:attrType_type"/>
+      <xs:anyAttribute namespace="##other" processContents="lax"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="ec">
+    <!-- End Code -->
+    <xs:complexType mixed="false">
+      <xs:attribute name="canCopy" use="optional" type="xlf:yesNo" default="yes"/>
+      <xs:attribute name="canDelete" use="optional" type="xlf:yesNo" default="yes"/>
+      <xs:attribute name="canOverlap" use="optional" type="xlf:yesNo" default="yes"/>
+      <xs:attribute name="canReorder" use="optional" type="xlf:yesNoFirstNo" default="yes"/>
+      <xs:attribute name="copyOf" use="optional" type="xs:NMTOKEN"/>
+      <xs:attribute name="dataRef" use="optional" type="xs:NMTOKEN"/>
+      <xs:attribute name="dir" use="optional" type="xlf:dirValue"/>
+      <xs:attribute name="disp" use="optional"/>
+      <xs:attribute name="equiv" use="optional"/>
+      <xs:attribute name="id" use="optional" type="xs:NMTOKEN"/>
+      <xs:attribute name="isolated" use="optional" type="xlf:yesNo" default="no"/>
+      <xs:attribute name="startRef" use="optional" type="xs:NMTOKEN"/>
+      <xs:attribute name="subFlows" use="optional" type="xs:NMTOKENS"/>
+      <xs:attribute name="subType" use="optional" type="xlf:userDefinedValue"/>
+      <xs:attribute name="type" use="optional" type="xlf:attrType_type"/>
+      <xs:anyAttribute namespace="##other" processContents="lax"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="mrk">
+    <!-- Annotation Marker -->
+    <xs:complexType mixed="true">
+      <xs:group ref="xlf:inline" minOccurs="0" maxOccurs="unbounded"/>
+      <xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
+      <xs:attribute name="translate" use="optional" type="xlf:yesNo"/>
+      <xs:attribute name="type" use="optional" type="xlf:attrType_typeForMrk"/>
+      <xs:attribute name="ref" use="optional" type="xs:anyURI"/>
+      <xs:attribute name="value" use="optional"/>
+      <xs:anyAttribute namespace="##other" processContents="lax"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="sm">
+    <!-- Start Annotation Marker -->
+    <xs:complexType mixed="false">
+      <xs:attribute name="id" use="required" type="xs:NMTOKEN"/>
+      <xs:attribute name="translate" use="optional" type="xlf:yesNo"/>
+      <xs:attribute name="type" use="optional" type="xlf:attrType_typeForMrk"/>
+      <xs:attribute name="ref" use="optional" type="xs:anyURI"/>
+      <xs:attribute name="value" use="optional"/>
+      <xs:anyAttribute namespace="##other" processContents="lax"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="em">
+    <!-- End Annotation Marker -->
+    <xs:complexType mixed="false">
+      <xs:attribute name="startRef" use="required" type="xs:NMTOKEN"/>
+    </xs:complexType>
+  </xs:element>
+
+</xs:schema>
diff --git a/vendor/symfony/translation/Resources/schemas/xml.xsd b/vendor/symfony/translation/Resources/schemas/xml.xsd
new file mode 100755
index 0000000..a46162a
--- /dev/null
+++ b/vendor/symfony/translation/Resources/schemas/xml.xsd
@@ -0,0 +1,309 @@
+<?xml version='1.0'?>
+<?xml-stylesheet href="../2008/09/xsd.xsl" type="text/xsl"?>
+<xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace" 
+  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
+  xmlns ="http://www.w3.org/1999/xhtml"
+  xml:lang="en">
+
+ <xs:annotation>
+  <xs:documentation>
+   <div>
+    <h1>About the XML namespace</h1>
+
+    <div class="bodytext">
+     <p>
+
+      This schema document describes the XML namespace, in a form
+      suitable for import by other schema documents.
+     </p>
+     <p>
+      See <a href="http://www.w3.org/XML/1998/namespace.html">
+      http://www.w3.org/XML/1998/namespace.html</a> and
+      <a href="http://www.w3.org/TR/REC-xml">
+      http://www.w3.org/TR/REC-xml</a> for information 
+      about this namespace.
+     </p>
+
+     <p>
+      Note that local names in this namespace are intended to be
+      defined only by the World Wide Web Consortium or its subgroups.
+      The names currently defined in this namespace are listed below.
+      They should not be used with conflicting semantics by any Working
+      Group, specification, or document instance.
+     </p>
+     <p>   
+      See further below in this document for more information about <a
+      href="#usage">how to refer to this schema document from your own
+      XSD schema documents</a> and about <a href="#nsversioning">the
+      namespace-versioning policy governing this schema document</a>.
+     </p>
+    </div>
+   </div>
+
+  </xs:documentation>
+ </xs:annotation>
+
+ <xs:attribute name="lang">
+  <xs:annotation>
+   <xs:documentation>
+    <div>
+     
+      <h3>lang (as an attribute name)</h3>
+      <p>
+
+       denotes an attribute whose value
+       is a language code for the natural language of the content of
+       any element; its value is inherited.  This name is reserved
+       by virtue of its definition in the XML specification.</p>
+     
+    </div>
+    <div>
+     <h4>Notes</h4>
+     <p>
+      Attempting to install the relevant ISO 2- and 3-letter
+      codes as the enumerated possible values is probably never
+      going to be a realistic possibility.  
+     </p>
+     <p>
+
+      See BCP 47 at <a href="http://www.rfc-editor.org/rfc/bcp/bcp47.txt">
+       http://www.rfc-editor.org/rfc/bcp/bcp47.txt</a>
+      and the IANA language subtag registry at
+      <a href="http://www.iana.org/assignments/language-subtag-registry">
+       http://www.iana.org/assignments/language-subtag-registry</a>
+      for further information.
+     </p>
+     <p>
+
+      The union allows for the 'un-declaration' of xml:lang with
+      the empty string.
+     </p>
+    </div>
+   </xs:documentation>
+  </xs:annotation>
+  <xs:simpleType>
+   <xs:union memberTypes="xs:language">
+    <xs:simpleType>    
+     <xs:restriction base="xs:string">
+      <xs:enumeration value=""/>
+
+     </xs:restriction>
+    </xs:simpleType>
+   </xs:union>
+  </xs:simpleType>
+ </xs:attribute>
+
+ <xs:attribute name="space">
+  <xs:annotation>
+   <xs:documentation>
+
+    <div>
+     
+      <h3>space (as an attribute name)</h3>
+      <p>
+       denotes an attribute whose
+       value is a keyword indicating what whitespace processing
+       discipline is intended for the content of the element; its
+       value is inherited.  This name is reserved by virtue of its
+       definition in the XML specification.</p>
+     
+    </div>
+   </xs:documentation>
+  </xs:annotation>
+  <xs:simpleType>
+
+   <xs:restriction base="xs:NCName">
+    <xs:enumeration value="default"/>
+    <xs:enumeration value="preserve"/>
+   </xs:restriction>
+  </xs:simpleType>
+ </xs:attribute>
+ 
+ <xs:attribute name="base" type="xs:anyURI"> <xs:annotation>
+   <xs:documentation>
+
+    <div>
+     
+      <h3>base (as an attribute name)</h3>
+      <p>
+       denotes an attribute whose value
+       provides a URI to be used as the base for interpreting any
+       relative URIs in the scope of the element on which it
+       appears; its value is inherited.  This name is reserved
+       by virtue of its definition in the XML Base specification.</p>
+     
+     <p>
+      See <a
+      href="http://www.w3.org/TR/xmlbase/">http://www.w3.org/TR/xmlbase/</a>
+      for information about this attribute.
+     </p>
+
+    </div>
+   </xs:documentation>
+  </xs:annotation>
+ </xs:attribute>
+ 
+ <xs:attribute name="id" type="xs:ID">
+  <xs:annotation>
+   <xs:documentation>
+    <div>
+     
+      <h3>id (as an attribute name)</h3> 
+      <p>
+
+       denotes an attribute whose value
+       should be interpreted as if declared to be of type ID.
+       This name is reserved by virtue of its definition in the
+       xml:id specification.</p>
+     
+     <p>
+      See <a
+      href="http://www.w3.org/TR/xml-id/">http://www.w3.org/TR/xml-id/</a>
+      for information about this attribute.
+     </p>
+    </div>
+   </xs:documentation>
+  </xs:annotation>
+
+ </xs:attribute>
+
+ <xs:attributeGroup name="specialAttrs">
+  <xs:attribute ref="xml:base"/>
+  <xs:attribute ref="xml:lang"/>
+  <xs:attribute ref="xml:space"/>
+  <xs:attribute ref="xml:id"/>
+ </xs:attributeGroup>
+
+ <xs:annotation>
+
+  <xs:documentation>
+   <div>
+   
+    <h3>Father (in any context at all)</h3> 
+
+    <div class="bodytext">
+     <p>
+      denotes Jon Bosak, the chair of 
+      the original XML Working Group.  This name is reserved by 
+      the following decision of the W3C XML Plenary and 
+      XML Coordination groups:
+     </p>
+     <blockquote>
+       <p>
+
+	In appreciation for his vision, leadership and
+	dedication the W3C XML Plenary on this 10th day of
+	February, 2000, reserves for Jon Bosak in perpetuity
+	the XML name "xml:Father".
+       </p>
+     </blockquote>
+    </div>
+   </div>
+  </xs:documentation>
+ </xs:annotation>
+
+ <xs:annotation>
+  <xs:documentation>
+
+   <div xml:id="usage" id="usage">
+    <h2><a name="usage">About this schema document</a></h2>
+
+    <div class="bodytext">
+     <p>
+      This schema defines attributes and an attribute group suitable
+      for use by schemas wishing to allow <code>xml:base</code>,
+      <code>xml:lang</code>, <code>xml:space</code> or
+      <code>xml:id</code> attributes on elements they define.
+     </p>
+
+     <p>
+      To enable this, such a schema must import this schema for
+      the XML namespace, e.g. as follows:
+     </p>
+     <pre>
+          &lt;schema.. .>
+          .. .
+           &lt;import namespace="http://www.w3.org/XML/1998/namespace"
+                      schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+     </pre>
+     <p>
+      or
+     </p>
+     <pre>
+
+           &lt;import namespace="http://www.w3.org/XML/1998/namespace"
+                      schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
+     </pre>
+     <p>
+      Subsequently, qualified reference to any of the attributes or the
+      group defined below will have the desired effect, e.g.
+     </p>
+     <pre>
+          &lt;type.. .>
+          .. .
+           &lt;attributeGroup ref="xml:specialAttrs"/>
+     </pre>
+     <p>
+      will define a type which will schema-validate an instance element
+      with any of those attributes.
+     </p>
+
+    </div>
+   </div>
+  </xs:documentation>
+ </xs:annotation>
+
+ <xs:annotation>
+  <xs:documentation>
+   <div id="nsversioning" xml:id="nsversioning">
+    <h2><a name="nsversioning">Versioning policy for this schema document</a></h2>
+
+    <div class="bodytext">
+     <p>
+      In keeping with the XML Schema WG's standard versioning
+      policy, this schema document will persist at
+      <a href="http://www.w3.org/2009/01/xml.xsd">
+       http://www.w3.org/2009/01/xml.xsd</a>.
+     </p>
+     <p>
+      At the date of issue it can also be found at
+      <a href="http://www.w3.org/2001/xml.xsd">
+       http://www.w3.org/2001/xml.xsd</a>.
+     </p>
+
+     <p>
+      The schema document at that URI may however change in the future,
+      in order to remain compatible with the latest version of XML
+      Schema itself, or with the XML namespace itself.  In other words,
+      if the XML Schema or XML namespaces change, the version of this
+      document at <a href="http://www.w3.org/2001/xml.xsd">
+       http://www.w3.org/2001/xml.xsd 
+      </a> 
+      will change accordingly; the version at 
+      <a href="http://www.w3.org/2009/01/xml.xsd">
+       http://www.w3.org/2009/01/xml.xsd 
+      </a> 
+      will not change.
+     </p>
+     <p>
+
+      Previous dated (and unchanging) versions of this schema 
+      document are at:
+     </p>
+     <ul>
+      <li><a href="http://www.w3.org/2009/01/xml.xsd">
+	http://www.w3.org/2009/01/xml.xsd</a></li>
+      <li><a href="http://www.w3.org/2007/08/xml.xsd">
+	http://www.w3.org/2007/08/xml.xsd</a></li>
+      <li><a href="http://www.w3.org/2004/10/xml.xsd">
+
+	http://www.w3.org/2004/10/xml.xsd</a></li>
+      <li><a href="http://www.w3.org/2001/03/xml.xsd">
+	http://www.w3.org/2001/03/xml.xsd</a></li>
+     </ul>
+    </div>
+   </div>
+  </xs:documentation>
+ </xs:annotation>
+
+</xs:schema>
diff --git a/vendor/symfony/translation/Tests/Catalogue/AbstractOperationTest.php b/vendor/symfony/translation/Tests/Catalogue/AbstractOperationTest.php
new file mode 100755
index 0000000..f82b18f
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Catalogue/AbstractOperationTest.php
@@ -0,0 +1,74 @@
+<?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\Translation\Tests\Catalogue;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\MessageCatalogueInterface;
+
+abstract class AbstractOperationTest extends TestCase
+{
+    public function testGetEmptyDomains()
+    {
+        $this->assertEquals(
+            [],
+            $this->createOperation(
+                new MessageCatalogue('en'),
+                new MessageCatalogue('en')
+            )->getDomains()
+        );
+    }
+
+    public function testGetMergedDomains()
+    {
+        $this->assertEquals(
+            ['a', 'b', 'c'],
+            $this->createOperation(
+                new MessageCatalogue('en', ['a' => [], 'b' => []]),
+                new MessageCatalogue('en', ['b' => [], 'c' => []])
+            )->getDomains()
+        );
+    }
+
+    public function testGetMessagesFromUnknownDomain()
+    {
+        $this->expectException('InvalidArgumentException');
+        $this->createOperation(
+            new MessageCatalogue('en'),
+            new MessageCatalogue('en')
+        )->getMessages('domain');
+    }
+
+    public function testGetEmptyMessages()
+    {
+        $this->assertEquals(
+            [],
+            $this->createOperation(
+                new MessageCatalogue('en', ['a' => []]),
+                new MessageCatalogue('en')
+            )->getMessages('a')
+        );
+    }
+
+    public function testGetEmptyResult()
+    {
+        $this->assertEquals(
+            new MessageCatalogue('en'),
+            $this->createOperation(
+                new MessageCatalogue('en'),
+                new MessageCatalogue('en')
+            )->getResult()
+        );
+    }
+
+    abstract protected function createOperation(MessageCatalogueInterface $source, MessageCatalogueInterface $target);
+}
diff --git a/vendor/symfony/translation/Tests/Catalogue/MergeOperationTest.php b/vendor/symfony/translation/Tests/Catalogue/MergeOperationTest.php
new file mode 100755
index 0000000..22af86e
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Catalogue/MergeOperationTest.php
@@ -0,0 +1,97 @@
+<?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\Translation\Tests\Catalogue;
+
+use Symfony\Component\Translation\Catalogue\MergeOperation;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\MessageCatalogueInterface;
+
+class MergeOperationTest extends AbstractOperationTest
+{
+    public function testGetMessagesFromSingleDomain()
+    {
+        $operation = $this->createOperation(
+            new MessageCatalogue('en', ['messages' => ['a' => 'old_a', 'b' => 'old_b']]),
+            new MessageCatalogue('en', ['messages' => ['a' => 'new_a', 'c' => 'new_c']])
+        );
+
+        $this->assertEquals(
+            ['a' => 'old_a', 'b' => 'old_b', 'c' => 'new_c'],
+            $operation->getMessages('messages')
+        );
+
+        $this->assertEquals(
+            ['c' => 'new_c'],
+            $operation->getNewMessages('messages')
+        );
+
+        $this->assertEquals(
+            [],
+            $operation->getObsoleteMessages('messages')
+        );
+    }
+
+    public function testGetResultFromSingleDomain()
+    {
+        $this->assertEquals(
+            new MessageCatalogue('en', [
+                'messages' => ['a' => 'old_a', 'b' => 'old_b', 'c' => 'new_c'],
+            ]),
+            $this->createOperation(
+                new MessageCatalogue('en', ['messages' => ['a' => 'old_a', 'b' => 'old_b']]),
+                new MessageCatalogue('en', ['messages' => ['a' => 'new_a', 'c' => 'new_c']])
+            )->getResult()
+        );
+    }
+
+    public function testGetResultFromIntlDomain()
+    {
+        $this->assertEquals(
+            new MessageCatalogue('en', [
+                'messages' => ['a' => 'old_a', 'b' => 'old_b'],
+                'messages+intl-icu' => ['d' => 'old_d', 'c' => 'new_c'],
+            ]),
+            $this->createOperation(
+                new MessageCatalogue('en', ['messages' => ['a' => 'old_a', 'b' => 'old_b'], 'messages+intl-icu' => ['d' => 'old_d']]),
+                new MessageCatalogue('en', ['messages+intl-icu' => ['a' => 'new_a', 'c' => 'new_c']])
+            )->getResult()
+        );
+    }
+
+    public function testGetResultWithMetadata()
+    {
+        $leftCatalogue = new MessageCatalogue('en', ['messages' => ['a' => 'old_a', 'b' => 'old_b']]);
+        $leftCatalogue->setMetadata('a', 'foo', 'messages');
+        $leftCatalogue->setMetadata('b', 'bar', 'messages');
+        $rightCatalogue = new MessageCatalogue('en', ['messages' => ['b' => 'new_b', 'c' => 'new_c']]);
+        $rightCatalogue->setMetadata('b', 'baz', 'messages');
+        $rightCatalogue->setMetadata('c', 'qux', 'messages');
+
+        $mergedCatalogue = new MessageCatalogue('en', ['messages' => ['a' => 'old_a', 'b' => 'old_b', 'c' => 'new_c']]);
+        $mergedCatalogue->setMetadata('a', 'foo', 'messages');
+        $mergedCatalogue->setMetadata('b', 'bar', 'messages');
+        $mergedCatalogue->setMetadata('c', 'qux', 'messages');
+
+        $this->assertEquals(
+            $mergedCatalogue,
+            $this->createOperation(
+                $leftCatalogue,
+                $rightCatalogue
+            )->getResult()
+        );
+    }
+
+    protected function createOperation(MessageCatalogueInterface $source, MessageCatalogueInterface $target)
+    {
+        return new MergeOperation($source, $target);
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Catalogue/TargetOperationTest.php b/vendor/symfony/translation/Tests/Catalogue/TargetOperationTest.php
new file mode 100755
index 0000000..570b503
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Catalogue/TargetOperationTest.php
@@ -0,0 +1,96 @@
+<?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\Translation\Tests\Catalogue;
+
+use Symfony\Component\Translation\Catalogue\TargetOperation;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\MessageCatalogueInterface;
+
+class TargetOperationTest extends AbstractOperationTest
+{
+    public function testGetMessagesFromSingleDomain()
+    {
+        $operation = $this->createOperation(
+            new MessageCatalogue('en', ['messages' => ['a' => 'old_a', 'b' => 'old_b']]),
+            new MessageCatalogue('en', ['messages' => ['a' => 'new_a', 'c' => 'new_c']])
+        );
+
+        $this->assertEquals(
+            ['a' => 'old_a', 'c' => 'new_c'],
+            $operation->getMessages('messages')
+        );
+
+        $this->assertEquals(
+            ['c' => 'new_c'],
+            $operation->getNewMessages('messages')
+        );
+
+        $this->assertEquals(
+            ['b' => 'old_b'],
+            $operation->getObsoleteMessages('messages')
+        );
+    }
+
+    public function testGetResultFromSingleDomain()
+    {
+        $this->assertEquals(
+            new MessageCatalogue('en', [
+                'messages' => ['a' => 'old_a', 'c' => 'new_c'],
+            ]),
+            $this->createOperation(
+                new MessageCatalogue('en', ['messages' => ['a' => 'old_a', 'b' => 'old_b']]),
+                new MessageCatalogue('en', ['messages' => ['a' => 'new_a', 'c' => 'new_c']])
+            )->getResult()
+        );
+    }
+
+    public function testGetResultFromIntlDomain()
+    {
+        $this->assertEquals(
+            new MessageCatalogue('en', [
+                'messages' => ['a' => 'old_a'],
+                'messages+intl-icu' => ['c' => 'new_c'],
+            ]),
+            $this->createOperation(
+                new MessageCatalogue('en', ['messages' => ['a' => 'old_a'], 'messages+intl-icu' => ['b' => 'old_b']]),
+                new MessageCatalogue('en', ['messages' => ['a' => 'new_a'], 'messages+intl-icu' => ['c' => 'new_c']])
+            )->getResult()
+        );
+    }
+
+    public function testGetResultWithMetadata()
+    {
+        $leftCatalogue = new MessageCatalogue('en', ['messages' => ['a' => 'old_a', 'b' => 'old_b']]);
+        $leftCatalogue->setMetadata('a', 'foo', 'messages');
+        $leftCatalogue->setMetadata('b', 'bar', 'messages');
+        $rightCatalogue = new MessageCatalogue('en', ['messages' => ['b' => 'new_b', 'c' => 'new_c']]);
+        $rightCatalogue->setMetadata('b', 'baz', 'messages');
+        $rightCatalogue->setMetadata('c', 'qux', 'messages');
+
+        $diffCatalogue = new MessageCatalogue('en', ['messages' => ['b' => 'old_b', 'c' => 'new_c']]);
+        $diffCatalogue->setMetadata('b', 'bar', 'messages');
+        $diffCatalogue->setMetadata('c', 'qux', 'messages');
+
+        $this->assertEquals(
+            $diffCatalogue,
+            $this->createOperation(
+                $leftCatalogue,
+                $rightCatalogue
+            )->getResult()
+        );
+    }
+
+    protected function createOperation(MessageCatalogueInterface $source, MessageCatalogueInterface $target)
+    {
+        return new TargetOperation($source, $target);
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Command/XliffLintCommandTest.php b/vendor/symfony/translation/Tests/Command/XliffLintCommandTest.php
new file mode 100755
index 0000000..df2e2f0
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Command/XliffLintCommandTest.php
@@ -0,0 +1,218 @@
+<?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\Translation\Tests\Command;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Console\Application;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Tester\CommandTester;
+use Symfony\Component\Translation\Command\XliffLintCommand;
+
+/**
+ * Tests the XliffLintCommand.
+ *
+ * @author Javier Eguiluz <javier.eguiluz@gmail.com>
+ */
+class XliffLintCommandTest extends TestCase
+{
+    private $files;
+
+    public function testLintCorrectFile()
+    {
+        $tester = $this->createCommandTester();
+        $filename = $this->createFile();
+
+        $tester->execute(
+            ['filename' => $filename],
+            ['verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false]
+        );
+
+        $this->assertEquals(0, $tester->getStatusCode(), 'Returns 0 in case of success');
+        $this->assertContains('OK', trim($tester->getDisplay()));
+    }
+
+    public function testLintCorrectFiles()
+    {
+        $tester = $this->createCommandTester();
+        $filename1 = $this->createFile();
+        $filename2 = $this->createFile();
+
+        $tester->execute(
+            ['filename' => [$filename1, $filename2]],
+            ['verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false]
+        );
+
+        $this->assertEquals(0, $tester->getStatusCode(), 'Returns 0 in case of success');
+        $this->assertContains('OK', trim($tester->getDisplay()));
+    }
+
+    /**
+     * @dataProvider provideStrictFilenames
+     */
+    public function testStrictFilenames($requireStrictFileNames, $fileNamePattern, $targetLanguage, $mustFail)
+    {
+        $tester = $this->createCommandTester($requireStrictFileNames);
+        $filename = $this->createFile('note', $targetLanguage, $fileNamePattern);
+
+        $tester->execute(
+            ['filename' => $filename],
+            ['verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false]
+        );
+
+        $this->assertEquals($mustFail ? 1 : 0, $tester->getStatusCode());
+        $this->assertContains($mustFail ? '[WARNING] 0 XLIFF files have valid syntax and 1 contain errors.' : '[OK] All 1 XLIFF files contain valid syntax.', $tester->getDisplay());
+    }
+
+    public function testLintIncorrectXmlSyntax()
+    {
+        $tester = $this->createCommandTester();
+        $filename = $this->createFile('note <target>');
+
+        $tester->execute(['filename' => $filename], ['decorated' => false]);
+
+        $this->assertEquals(1, $tester->getStatusCode(), 'Returns 1 in case of error');
+        $this->assertContains('Opening and ending tag mismatch: target line 6 and source', trim($tester->getDisplay()));
+    }
+
+    public function testLintIncorrectTargetLanguage()
+    {
+        $tester = $this->createCommandTester();
+        $filename = $this->createFile('note', 'es');
+
+        $tester->execute(['filename' => $filename], ['decorated' => false]);
+
+        $this->assertEquals(1, $tester->getStatusCode(), 'Returns 1 in case of error');
+        $this->assertContains('There is a mismatch between the language included in the file name ("messages.en.xlf") and the "es" value used in the "target-language" attribute of the file.', trim($tester->getDisplay()));
+    }
+
+    public function testLintTargetLanguageIsCaseInsensitive()
+    {
+        $tester = $this->createCommandTester();
+        $filename = $this->createFile('note', 'zh-cn', 'messages.zh_CN.xlf');
+
+        $tester->execute(['filename' => $filename], ['decorated' => false]);
+
+        $this->assertEquals(0, $tester->getStatusCode());
+        $this->assertContains('[OK] All 1 XLIFF files contain valid syntax.', trim($tester->getDisplay()));
+    }
+
+    /**
+     * @expectedException \RuntimeException
+     */
+    public function testLintFileNotReadable()
+    {
+        $tester = $this->createCommandTester();
+        $filename = $this->createFile();
+        unlink($filename);
+
+        $tester->execute(['filename' => $filename], ['decorated' => false]);
+    }
+
+    public function testGetHelp()
+    {
+        $command = new XliffLintCommand();
+        $expected = <<<EOF
+The <info>%command.name%</info> command lints a XLIFF file and outputs to STDOUT
+the first encountered syntax error.
+
+You can validates XLIFF contents passed from STDIN:
+
+  <info>cat filename | php %command.full_name%</info>
+
+You can also validate the syntax of a file:
+
+  <info>php %command.full_name% filename</info>
+
+Or of a whole directory:
+
+  <info>php %command.full_name% dirname</info>
+  <info>php %command.full_name% dirname --format=json</info>
+
+EOF;
+
+        $this->assertEquals($expected, $command->getHelp());
+    }
+
+    /**
+     * @return string Path to the new file
+     */
+    private function createFile($sourceContent = 'note', $targetLanguage = 'en', $fileNamePattern = 'messages.%locale%.xlf')
+    {
+        $xliffContent = <<<XLIFF
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" target-language="$targetLanguage" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="note">
+                <source>$sourceContent</source>
+                <target>NOTE</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
+XLIFF;
+
+        $filename = sprintf('%s/translation-xliff-lint-test/%s', sys_get_temp_dir(), str_replace('%locale%', 'en', $fileNamePattern));
+        file_put_contents($filename, $xliffContent);
+
+        $this->files[] = $filename;
+
+        return $filename;
+    }
+
+    /**
+     * @return CommandTester
+     */
+    private function createCommandTester($requireStrictFileNames = true, $application = null)
+    {
+        if (!$application) {
+            $application = new Application();
+            $application->add(new XliffLintCommand(null, null, null, $requireStrictFileNames));
+        }
+
+        $command = $application->find('lint:xliff');
+
+        if ($application) {
+            $command->setApplication($application);
+        }
+
+        return new CommandTester($command);
+    }
+
+    protected function setUp()
+    {
+        $this->files = [];
+        @mkdir(sys_get_temp_dir().'/translation-xliff-lint-test');
+    }
+
+    protected function tearDown()
+    {
+        foreach ($this->files as $file) {
+            if (file_exists($file)) {
+                unlink($file);
+            }
+        }
+        rmdir(sys_get_temp_dir().'/translation-xliff-lint-test');
+    }
+
+    public function provideStrictFilenames()
+    {
+        yield [false, 'messages.%locale%.xlf', 'en', false];
+        yield [false, 'messages.%locale%.xlf', 'es', true];
+        yield [false, '%locale%.messages.xlf', 'en', false];
+        yield [false, '%locale%.messages.xlf', 'es', true];
+        yield [true, 'messages.%locale%.xlf', 'en', false];
+        yield [true, 'messages.%locale%.xlf', 'es', true];
+        yield [true, '%locale%.messages.xlf', 'en', true];
+        yield [true, '%locale%.messages.xlf', 'es', true];
+    }
+}
diff --git a/vendor/symfony/translation/Tests/DataCollector/TranslationDataCollectorTest.php b/vendor/symfony/translation/Tests/DataCollector/TranslationDataCollectorTest.php
new file mode 100755
index 0000000..bd97a24
--- /dev/null
+++ b/vendor/symfony/translation/Tests/DataCollector/TranslationDataCollectorTest.php
@@ -0,0 +1,150 @@
+<?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\Translation\Tests\DataCollector;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\DataCollector\TranslationDataCollector;
+use Symfony\Component\Translation\DataCollectorTranslator;
+
+class TranslationDataCollectorTest extends TestCase
+{
+    protected function setUp()
+    {
+        if (!class_exists('Symfony\Component\HttpKernel\DataCollector\DataCollector')) {
+            $this->markTestSkipped('The "DataCollector" is not available');
+        }
+    }
+
+    public function testCollectEmptyMessages()
+    {
+        $translator = $this->getTranslator();
+        $translator->expects($this->any())->method('getCollectedMessages')->willReturn([]);
+
+        $dataCollector = new TranslationDataCollector($translator);
+        $dataCollector->lateCollect();
+
+        $this->assertEquals(0, $dataCollector->getCountMissings());
+        $this->assertEquals(0, $dataCollector->getCountFallbacks());
+        $this->assertEquals(0, $dataCollector->getCountDefines());
+        $this->assertEquals([], $dataCollector->getMessages()->getValue());
+    }
+
+    public function testCollect()
+    {
+        $collectedMessages = [
+            [
+                'id' => 'foo',
+                'translation' => 'foo (en)',
+                'locale' => 'en',
+                'domain' => 'messages',
+                'state' => DataCollectorTranslator::MESSAGE_DEFINED,
+                'parameters' => [],
+                'transChoiceNumber' => null,
+            ],
+            [
+                'id' => 'bar',
+                'translation' => 'bar (fr)',
+                'locale' => 'fr',
+                'domain' => 'messages',
+                'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK,
+                'parameters' => [],
+                'transChoiceNumber' => null,
+            ],
+            [
+                'id' => 'choice',
+                'translation' => 'choice',
+                'locale' => 'en',
+                'domain' => 'messages',
+                'state' => DataCollectorTranslator::MESSAGE_MISSING,
+                'parameters' => ['%count%' => 3],
+                'transChoiceNumber' => 3,
+            ],
+            [
+                'id' => 'choice',
+                'translation' => 'choice',
+                'locale' => 'en',
+                'domain' => 'messages',
+                'state' => DataCollectorTranslator::MESSAGE_MISSING,
+                'parameters' => ['%count%' => 3],
+                'transChoiceNumber' => 3,
+            ],
+            [
+                'id' => 'choice',
+                'translation' => 'choice',
+                'locale' => 'en',
+                'domain' => 'messages',
+                'state' => DataCollectorTranslator::MESSAGE_MISSING,
+                'parameters' => ['%count%' => 4, '%foo%' => 'bar'],
+                'transChoiceNumber' => 4,
+            ],
+        ];
+        $expectedMessages = [
+            [
+                'id' => 'foo',
+                'translation' => 'foo (en)',
+                'locale' => 'en',
+                'domain' => 'messages',
+                'state' => DataCollectorTranslator::MESSAGE_DEFINED,
+                'count' => 1,
+                'parameters' => [],
+                'transChoiceNumber' => null,
+            ],
+            [
+                'id' => 'bar',
+                'translation' => 'bar (fr)',
+                'locale' => 'fr',
+                'domain' => 'messages',
+                'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK,
+                'count' => 1,
+                'parameters' => [],
+                'transChoiceNumber' => null,
+            ],
+            [
+                'id' => 'choice',
+                'translation' => 'choice',
+                'locale' => 'en',
+                'domain' => 'messages',
+                'state' => DataCollectorTranslator::MESSAGE_MISSING,
+                'count' => 3,
+                'parameters' => [
+                    ['%count%' => 3],
+                    ['%count%' => 3],
+                    ['%count%' => 4, '%foo%' => 'bar'],
+                ],
+                'transChoiceNumber' => 3,
+            ],
+        ];
+
+        $translator = $this->getTranslator();
+        $translator->expects($this->any())->method('getCollectedMessages')->willReturn($collectedMessages);
+
+        $dataCollector = new TranslationDataCollector($translator);
+        $dataCollector->lateCollect();
+
+        $this->assertEquals(1, $dataCollector->getCountMissings());
+        $this->assertEquals(1, $dataCollector->getCountFallbacks());
+        $this->assertEquals(1, $dataCollector->getCountDefines());
+
+        $this->assertEquals($expectedMessages, array_values($dataCollector->getMessages()->getValue(true)));
+    }
+
+    private function getTranslator()
+    {
+        $translator = $this
+            ->getMockBuilder('Symfony\Component\Translation\DataCollectorTranslator')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        return $translator;
+    }
+}
diff --git a/vendor/symfony/translation/Tests/DataCollectorTranslatorTest.php b/vendor/symfony/translation/Tests/DataCollectorTranslatorTest.php
new file mode 100755
index 0000000..138172e
--- /dev/null
+++ b/vendor/symfony/translation/Tests/DataCollectorTranslatorTest.php
@@ -0,0 +1,116 @@
+<?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\Translation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\DataCollectorTranslator;
+use Symfony\Component\Translation\Loader\ArrayLoader;
+use Symfony\Component\Translation\Translator;
+
+class DataCollectorTranslatorTest extends TestCase
+{
+    public function testCollectMessages()
+    {
+        $collector = $this->createCollector();
+        $collector->setFallbackLocales(['fr', 'ru']);
+
+        $collector->trans('foo');
+        $collector->trans('bar');
+        $collector->trans('choice', ['%count%' => 0]);
+        $collector->trans('bar_ru');
+        $collector->trans('bar_ru', ['foo' => 'bar']);
+
+        $expectedMessages = [];
+        $expectedMessages[] = [
+            'id' => 'foo',
+            'translation' => 'foo (en)',
+            'locale' => 'en',
+            'domain' => 'messages',
+            'state' => DataCollectorTranslator::MESSAGE_DEFINED,
+            'parameters' => [],
+            'transChoiceNumber' => null,
+        ];
+        $expectedMessages[] = [
+            'id' => 'bar',
+            'translation' => 'bar (fr)',
+            'locale' => 'fr',
+            'domain' => 'messages',
+            'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK,
+            'parameters' => [],
+            'transChoiceNumber' => null,
+        ];
+        $expectedMessages[] = [
+            'id' => 'choice',
+            'translation' => 'choice',
+            'locale' => 'en',
+            'domain' => 'messages',
+            'state' => DataCollectorTranslator::MESSAGE_MISSING,
+            'parameters' => ['%count%' => 0],
+            'transChoiceNumber' => 0,
+        ];
+        $expectedMessages[] = [
+            'id' => 'bar_ru',
+            'translation' => 'bar (ru)',
+            'locale' => 'ru',
+            'domain' => 'messages',
+            'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK,
+            'parameters' => [],
+            'transChoiceNumber' => null,
+        ];
+        $expectedMessages[] = [
+            'id' => 'bar_ru',
+            'translation' => 'bar (ru)',
+            'locale' => 'ru',
+            'domain' => 'messages',
+            'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK,
+            'parameters' => ['foo' => 'bar'],
+            'transChoiceNumber' => null,
+        ];
+
+        $this->assertEquals($expectedMessages, $collector->getCollectedMessages());
+    }
+
+    /**
+     * @group legacy
+     */
+    public function testCollectMessagesTransChoice()
+    {
+        $collector = $this->createCollector();
+        $collector->setFallbackLocales(['fr', 'ru']);
+        $collector->transChoice('choice', 0);
+
+        $expectedMessages = [];
+
+        $expectedMessages[] = [
+              'id' => 'choice',
+              'translation' => 'choice',
+              'locale' => 'en',
+              'domain' => 'messages',
+              'state' => DataCollectorTranslator::MESSAGE_MISSING,
+              'parameters' => ['%count%' => 0],
+              'transChoiceNumber' => 0,
+        ];
+
+        $this->assertEquals($expectedMessages, $collector->getCollectedMessages());
+    }
+
+    private function createCollector()
+    {
+        $translator = new Translator('en');
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', ['foo' => 'foo (en)'], 'en');
+        $translator->addResource('array', ['bar' => 'bar (fr)'], 'fr');
+        $translator->addResource('array', ['bar_ru' => 'bar (ru)'], 'ru');
+
+        return new DataCollectorTranslator($translator);
+    }
+}
diff --git a/vendor/symfony/translation/Tests/DependencyInjection/TranslationDumperPassTest.php b/vendor/symfony/translation/Tests/DependencyInjection/TranslationDumperPassTest.php
new file mode 100755
index 0000000..94769e9
--- /dev/null
+++ b/vendor/symfony/translation/Tests/DependencyInjection/TranslationDumperPassTest.php
@@ -0,0 +1,48 @@
+<?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\Translation\Tests\DependencyInjection;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\Translation\DependencyInjection\TranslationDumperPass;
+
+class TranslationDumperPassTest extends TestCase
+{
+    public function testProcess()
+    {
+        $container = new ContainerBuilder();
+        $writerDefinition = $container->register('translation.writer');
+        $container->register('foo.id')
+            ->addTag('translation.dumper', ['alias' => 'bar.alias']);
+
+        $translationDumperPass = new TranslationDumperPass();
+        $translationDumperPass->process($container);
+
+        $this->assertEquals([['addDumper', ['bar.alias', new Reference('foo.id')]]], $writerDefinition->getMethodCalls());
+    }
+
+    public function testProcessNoDefinitionFound()
+    {
+        $container = new ContainerBuilder();
+
+        $definitionsBefore = \count($container->getDefinitions());
+        $aliasesBefore = \count($container->getAliases());
+
+        $translationDumperPass = new TranslationDumperPass();
+        $translationDumperPass->process($container);
+
+        // the container is untouched (i.e. no new definitions or aliases)
+        $this->assertCount($definitionsBefore, $container->getDefinitions());
+        $this->assertCount($aliasesBefore, $container->getAliases());
+    }
+}
diff --git a/vendor/symfony/translation/Tests/DependencyInjection/TranslationExtractorPassTest.php b/vendor/symfony/translation/Tests/DependencyInjection/TranslationExtractorPassTest.php
new file mode 100755
index 0000000..a638498
--- /dev/null
+++ b/vendor/symfony/translation/Tests/DependencyInjection/TranslationExtractorPassTest.php
@@ -0,0 +1,66 @@
+<?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\Translation\Tests\DependencyInjection;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\Translation\DependencyInjection\TranslationExtractorPass;
+
+class TranslationExtractorPassTest extends TestCase
+{
+    public function testProcess()
+    {
+        $container = new ContainerBuilder();
+        $extractorDefinition = $container->register('translation.extractor');
+        $container->register('foo.id')
+            ->addTag('translation.extractor', ['alias' => 'bar.alias']);
+
+        $translationDumperPass = new TranslationExtractorPass();
+        $translationDumperPass->process($container);
+
+        $this->assertEquals([['addExtractor', ['bar.alias', new Reference('foo.id')]]], $extractorDefinition->getMethodCalls());
+    }
+
+    public function testProcessNoDefinitionFound()
+    {
+        $container = new ContainerBuilder();
+
+        $definitionsBefore = \count($container->getDefinitions());
+        $aliasesBefore = \count($container->getAliases());
+
+        $translationDumperPass = new TranslationExtractorPass();
+        $translationDumperPass->process($container);
+
+        // the container is untouched (i.e. no new definitions or aliases)
+        $this->assertCount($definitionsBefore, $container->getDefinitions());
+        $this->assertCount($aliasesBefore, $container->getAliases());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
+     * @expectedExceptionMessage The alias for the tag "translation.extractor" of service "foo.id" must be set.
+     */
+    public function testProcessMissingAlias()
+    {
+        $definition = $this->getMockBuilder('Symfony\Component\DependencyInjection\Definition')->disableOriginalConstructor()->getMock();
+        $container = new ContainerBuilder();
+        $container->register('translation.extractor');
+        $container->register('foo.id')
+            ->addTag('translation.extractor', []);
+
+        $definition->expects($this->never())->method('addMethodCall');
+
+        $translationDumperPass = new TranslationExtractorPass();
+        $translationDumperPass->process($container);
+    }
+}
diff --git a/vendor/symfony/translation/Tests/DependencyInjection/TranslationPassTest.php b/vendor/symfony/translation/Tests/DependencyInjection/TranslationPassTest.php
new file mode 100755
index 0000000..f62fc85
--- /dev/null
+++ b/vendor/symfony/translation/Tests/DependencyInjection/TranslationPassTest.php
@@ -0,0 +1,122 @@
+<?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\Translation\Tests\DependencyInjection;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\Translation\DependencyInjection\TranslatorPass;
+
+class TranslationPassTest extends TestCase
+{
+    public function testValidCollector()
+    {
+        $loader = (new Definition())
+            ->addTag('translation.loader', ['alias' => 'xliff', 'legacy-alias' => 'xlf']);
+
+        $reader = new Definition();
+
+        $translator = (new Definition())
+            ->setArguments([null, null, null, null]);
+
+        $container = new ContainerBuilder();
+        $container->setDefinition('translator.default', $translator);
+        $container->setDefinition('translation.reader', $reader);
+        $container->setDefinition('translation.xliff_loader', $loader);
+
+        $pass = new TranslatorPass('translator.default', 'translation.reader');
+        $pass->process($container);
+
+        $expectedReader = (new Definition())
+            ->addMethodCall('addLoader', ['xliff', new Reference('translation.xliff_loader')])
+            ->addMethodCall('addLoader', ['xlf', new Reference('translation.xliff_loader')])
+        ;
+        $this->assertEquals($expectedReader, $reader);
+
+        $expectedLoader = (new Definition())
+            ->addTag('translation.loader', ['alias' => 'xliff', 'legacy-alias' => 'xlf'])
+        ;
+        $this->assertEquals($expectedLoader, $loader);
+
+        $this->assertSame(['translation.xliff_loader' => ['xliff', 'xlf']], $translator->getArgument(3));
+
+        $expected = ['translation.xliff_loader' => new ServiceClosureArgument(new Reference('translation.xliff_loader'))];
+        $this->assertEquals($expected, $container->getDefinition((string) $translator->getArgument(0))->getArgument(0));
+    }
+
+    public function testValidCommandsViewPathsArgument()
+    {
+        $container = new ContainerBuilder();
+        $container->register('translator.default')
+            ->setArguments([null, null, null, null])
+        ;
+        $debugCommand = $container->register('console.command.translation_debug')
+            ->setArguments([null, null, null, null, null, [], []])
+        ;
+        $updateCommand = $container->register('console.command.translation_update')
+            ->setArguments([null, null, null, null, null, null, [], []])
+        ;
+        $container->register('twig.template_iterator')
+            ->setArguments([null, null, ['other/templates' => null, 'tpl' => 'App']])
+        ;
+        $container->setParameter('twig.default_path', 'templates');
+
+        $pass = new TranslatorPass('translator.default');
+        $pass->process($container);
+
+        $expectedViewPaths = ['other/templates', 'tpl'];
+
+        $this->assertSame('templates', $debugCommand->getArgument(4));
+        $this->assertSame('templates', $updateCommand->getArgument(5));
+        $this->assertSame($expectedViewPaths, $debugCommand->getArgument(6));
+        $this->assertSame($expectedViewPaths, $updateCommand->getArgument(7));
+    }
+
+    public function testCommandsViewPathsArgumentsAreIgnoredWithOldServiceDefinitions()
+    {
+        $container = new ContainerBuilder();
+        $container->register('translator.default')
+            ->setArguments([null, null, null, null])
+        ;
+        $debugCommand = $container->register('console.command.translation_debug')
+            ->setArguments([
+                new Reference('translator'),
+                new Reference('translation.reader'),
+                new Reference('translation.extractor'),
+                '%translator.default_path%',
+                null,
+            ])
+        ;
+        $updateCommand = $container->register('console.command.translation_update')
+            ->setArguments([
+                new Reference('translation.writer'),
+                new Reference('translation.reader'),
+                new Reference('translation.extractor'),
+                '%kernel.default_locale%',
+                '%translator.default_path%',
+                null,
+            ])
+        ;
+        $container->register('twig.template_iterator')
+            ->setArguments([null, null, ['other/templates' => null, 'tpl' => 'App']])
+        ;
+        $container->setParameter('twig.default_path', 'templates');
+
+        $pass = new TranslatorPass('translator.default');
+        $pass->process($container);
+
+        $this->assertSame('templates', $debugCommand->getArgument(4));
+        $this->assertSame('templates', $updateCommand->getArgument(5));
+    }
+}
diff --git a/vendor/symfony/translation/Tests/DependencyInjection/TranslationPathsPassTest.php b/vendor/symfony/translation/Tests/DependencyInjection/TranslationPathsPassTest.php
new file mode 100755
index 0000000..42ab398
--- /dev/null
+++ b/vendor/symfony/translation/Tests/DependencyInjection/TranslationPathsPassTest.php
@@ -0,0 +1,89 @@
+<?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\Translation\Tests\DependencyInjection;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\ServiceLocator;
+use Symfony\Component\Translation\DependencyInjection\TranslatorPathsPass;
+use Symfony\Component\Translation\Tests\DependencyInjection\fixtures\ControllerArguments;
+use Symfony\Component\Translation\Tests\DependencyInjection\fixtures\ServiceArguments;
+use Symfony\Component\Translation\Tests\DependencyInjection\fixtures\ServiceMethodCalls;
+use Symfony\Component\Translation\Tests\DependencyInjection\fixtures\ServiceProperties;
+use Symfony\Component\Translation\Tests\DependencyInjection\fixtures\ServiceSubscriber;
+
+class TranslationPathsPassTest extends TestCase
+{
+    public function testProcess()
+    {
+        $container = new ContainerBuilder();
+        $container->register('translator');
+        $debugCommand = $container->register('console.command.translation_debug')
+            ->setArguments([null, null, null, null, null, [], []])
+        ;
+        $updateCommand = $container->register('console.command.translation_update')
+            ->setArguments([null, null, null, null, null, null, [], []])
+        ;
+        $container->register(ControllerArguments::class, ControllerArguments::class)
+            ->setTags(['controller.service_arguments'])
+        ;
+        $container->register(ServiceArguments::class, ServiceArguments::class)
+            ->setArguments([new Reference('translator')])
+        ;
+        $container->register(ServiceProperties::class, ServiceProperties::class)
+            ->setProperties([new Reference('translator')])
+        ;
+        $container->register(ServiceMethodCalls::class, ServiceMethodCalls::class)
+            ->setMethodCalls([['setTranslator', [new Reference('translator')]]])
+        ;
+        $container->register('service_rc')
+            ->setArguments([new Definition(), new Reference(ServiceMethodCalls::class)])
+        ;
+        $serviceLocator1 = $container->register('.service_locator.foo', ServiceLocator::class)
+            ->setArguments([new ServiceClosureArgument(new Reference('translator'))])
+        ;
+        $serviceLocator2 = (new Definition(ServiceLocator::class))
+            ->setArguments([ServiceSubscriber::class, new Reference('service_container')])
+            ->setFactory([$serviceLocator1, 'withContext'])
+        ;
+        $container->register('service_subscriber', ServiceSubscriber::class)
+            ->setArguments([$serviceLocator2])
+        ;
+        $container->register('.service_locator.bar', ServiceLocator::class)
+            ->setArguments([[
+                ControllerArguments::class.'::index' => new ServiceClosureArgument(new Reference('.service_locator.foo')),
+                ControllerArguments::class.'::__invoke' => new ServiceClosureArgument(new Reference('.service_locator.foo')),
+                ControllerArguments::class => new ServiceClosureArgument(new Reference('.service_locator.foo')),
+            ]])
+        ;
+        $container->register('argument_resolver.service')
+            ->setArguments([new Reference('.service_locator.bar')])
+        ;
+
+        $pass = new TranslatorPathsPass('translator', 'console.command.translation_debug', 'console.command.translation_update', 'argument_resolver.service');
+        $pass->process($container);
+
+        $expectedPaths = [
+            $container->getReflectionClass(ServiceArguments::class)->getFileName(),
+            $container->getReflectionClass(ServiceProperties::class)->getFileName(),
+            $container->getReflectionClass(ServiceMethodCalls::class)->getFileName(),
+            $container->getReflectionClass(ControllerArguments::class)->getFileName(),
+            $container->getReflectionClass(ServiceSubscriber::class)->getFileName(),
+        ];
+
+        $this->assertSame($expectedPaths, $debugCommand->getArgument(6));
+        $this->assertSame($expectedPaths, $updateCommand->getArgument(7));
+    }
+}
diff --git a/vendor/symfony/translation/Tests/DependencyInjection/fixtures/ControllerArguments.php b/vendor/symfony/translation/Tests/DependencyInjection/fixtures/ControllerArguments.php
new file mode 100755
index 0000000..97a53fa
--- /dev/null
+++ b/vendor/symfony/translation/Tests/DependencyInjection/fixtures/ControllerArguments.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Symfony\Component\Translation\Tests\DependencyInjection\fixtures;
+
+use Symfony\Contracts\Translation\TranslatorInterface;
+
+class ControllerArguments
+{
+    public function __invoke(TranslatorInterface $translator)
+    {
+    }
+
+    public function index(TranslatorInterface $translator)
+    {
+    }
+}
diff --git a/vendor/symfony/translation/Tests/DependencyInjection/fixtures/ServiceArguments.php b/vendor/symfony/translation/Tests/DependencyInjection/fixtures/ServiceArguments.php
new file mode 100755
index 0000000..80c629d
--- /dev/null
+++ b/vendor/symfony/translation/Tests/DependencyInjection/fixtures/ServiceArguments.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace Symfony\Component\Translation\Tests\DependencyInjection\fixtures;
+
+use Symfony\Contracts\Translation\TranslatorInterface;
+
+class ServiceArguments
+{
+    public function __construct(TranslatorInterface $translator)
+    {
+    }
+}
diff --git a/vendor/symfony/translation/Tests/DependencyInjection/fixtures/ServiceMethodCalls.php b/vendor/symfony/translation/Tests/DependencyInjection/fixtures/ServiceMethodCalls.php
new file mode 100755
index 0000000..8998d89
--- /dev/null
+++ b/vendor/symfony/translation/Tests/DependencyInjection/fixtures/ServiceMethodCalls.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace Symfony\Component\Translation\Tests\DependencyInjection\fixtures;
+
+use Symfony\Contracts\Translation\TranslatorInterface;
+
+class ServiceMethodCalls
+{
+    public function setTranslator(TranslatorInterface $translator)
+    {
+    }
+}
diff --git a/vendor/symfony/translation/Tests/DependencyInjection/fixtures/ServiceProperties.php b/vendor/symfony/translation/Tests/DependencyInjection/fixtures/ServiceProperties.php
new file mode 100755
index 0000000..f5098e9
--- /dev/null
+++ b/vendor/symfony/translation/Tests/DependencyInjection/fixtures/ServiceProperties.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace Symfony\Component\Translation\Tests\DependencyInjection\fixtures;
+
+class ServiceProperties
+{
+    public $translator;
+}
diff --git a/vendor/symfony/translation/Tests/DependencyInjection/fixtures/ServiceSubscriber.php b/vendor/symfony/translation/Tests/DependencyInjection/fixtures/ServiceSubscriber.php
new file mode 100755
index 0000000..65b60d6
--- /dev/null
+++ b/vendor/symfony/translation/Tests/DependencyInjection/fixtures/ServiceSubscriber.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Symfony\Component\Translation\Tests\DependencyInjection\fixtures;
+
+use Psr\Container\ContainerInterface;
+use Symfony\Contracts\Service\ServiceSubscriberInterface;
+use Symfony\Contracts\Translation\TranslatorInterface;
+
+class ServiceSubscriber implements ServiceSubscriberInterface
+{
+    public function __construct(ContainerInterface $container)
+    {
+    }
+
+    public static function getSubscribedServices()
+    {
+        return ['translator' => TranslatorInterface::class];
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Dumper/CsvFileDumperTest.php b/vendor/symfony/translation/Tests/Dumper/CsvFileDumperTest.php
new file mode 100755
index 0000000..0d1cf2c
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Dumper/CsvFileDumperTest.php
@@ -0,0 +1,30 @@
+<?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\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Dumper\CsvFileDumper;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class CsvFileDumperTest extends TestCase
+{
+    public function testFormatCatalogue()
+    {
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->add(['foo' => 'bar', 'bar' => 'foo
+foo', 'foo;foo' => 'bar']);
+
+        $dumper = new CsvFileDumper();
+
+        $this->assertStringEqualsFile(__DIR__.'/../fixtures/valid.csv', $dumper->formatCatalogue($catalogue, 'messages'));
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Dumper/FileDumperTest.php b/vendor/symfony/translation/Tests/Dumper/FileDumperTest.php
new file mode 100755
index 0000000..20fa918
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Dumper/FileDumperTest.php
@@ -0,0 +1,90 @@
+<?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\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Dumper\FileDumper;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class FileDumperTest extends TestCase
+{
+    public function testDump()
+    {
+        $tempDir = sys_get_temp_dir();
+
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->add(['foo' => 'bar']);
+
+        $dumper = new ConcreteFileDumper();
+        $dumper->dump($catalogue, ['path' => $tempDir]);
+
+        $this->assertFileExists($tempDir.'/messages.en.concrete');
+
+        @unlink($tempDir.'/messages.en.concrete');
+    }
+
+    public function testDumpIntl()
+    {
+        $tempDir = sys_get_temp_dir();
+
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->add(['foo' => 'bar'], 'd1');
+        $catalogue->add(['bar' => 'foo'], 'd1+intl-icu');
+        $catalogue->add(['bar' => 'foo'], 'd2+intl-icu');
+
+        $dumper = new ConcreteFileDumper();
+        @unlink($tempDir.'/d2.en.concrete');
+        $dumper->dump($catalogue, ['path' => $tempDir]);
+
+        $this->assertStringEqualsFile($tempDir.'/d1.en.concrete', 'foo=bar');
+        @unlink($tempDir.'/d1.en.concrete');
+
+        $this->assertStringEqualsFile($tempDir.'/d1+intl-icu.en.concrete', 'bar=foo');
+        @unlink($tempDir.'/d1+intl-icu.en.concrete');
+
+        $this->assertFileNotExists($tempDir.'/d2.en.concrete');
+        $this->assertStringEqualsFile($tempDir.'/d2+intl-icu.en.concrete', 'bar=foo');
+        @unlink($tempDir.'/d2+intl-icu.en.concrete');
+    }
+
+    public function testDumpCreatesNestedDirectoriesAndFile()
+    {
+        $tempDir = sys_get_temp_dir();
+        $translationsDir = $tempDir.'/test/translations';
+        $file = $translationsDir.'/messages.en.concrete';
+
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->add(['foo' => 'bar']);
+
+        $dumper = new ConcreteFileDumper();
+        $dumper->setRelativePathTemplate('test/translations/%domain%.%locale%.%extension%');
+        $dumper->dump($catalogue, ['path' => $tempDir]);
+
+        $this->assertFileExists($file);
+
+        @unlink($file);
+        @rmdir($translationsDir);
+    }
+}
+
+class ConcreteFileDumper extends FileDumper
+{
+    public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = [])
+    {
+        return http_build_query($messages->all($domain), '', '&');
+    }
+
+    protected function getExtension()
+    {
+        return 'concrete';
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Dumper/IcuResFileDumperTest.php b/vendor/symfony/translation/Tests/Dumper/IcuResFileDumperTest.php
new file mode 100755
index 0000000..dcb9c2c
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Dumper/IcuResFileDumperTest.php
@@ -0,0 +1,29 @@
+<?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\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Dumper\IcuResFileDumper;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class IcuResFileDumperTest extends TestCase
+{
+    public function testFormatCatalogue()
+    {
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->add(['foo' => 'bar']);
+
+        $dumper = new IcuResFileDumper();
+
+        $this->assertStringEqualsFile(__DIR__.'/../fixtures/resourcebundle/res/en.res', $dumper->formatCatalogue($catalogue, 'messages'));
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Dumper/IniFileDumperTest.php b/vendor/symfony/translation/Tests/Dumper/IniFileDumperTest.php
new file mode 100755
index 0000000..1ed168b
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Dumper/IniFileDumperTest.php
@@ -0,0 +1,29 @@
+<?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\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Dumper\IniFileDumper;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class IniFileDumperTest extends TestCase
+{
+    public function testFormatCatalogue()
+    {
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->add(['foo' => 'bar']);
+
+        $dumper = new IniFileDumper();
+
+        $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.ini', $dumper->formatCatalogue($catalogue, 'messages'));
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Dumper/JsonFileDumperTest.php b/vendor/symfony/translation/Tests/Dumper/JsonFileDumperTest.php
new file mode 100755
index 0000000..04e3d86
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Dumper/JsonFileDumperTest.php
@@ -0,0 +1,39 @@
+<?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\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Dumper\JsonFileDumper;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class JsonFileDumperTest extends TestCase
+{
+    public function testFormatCatalogue()
+    {
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->add(['foo' => 'bar']);
+
+        $dumper = new JsonFileDumper();
+
+        $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.json', $dumper->formatCatalogue($catalogue, 'messages'));
+    }
+
+    public function testDumpWithCustomEncoding()
+    {
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->add(['foo' => '"bar"']);
+
+        $dumper = new JsonFileDumper();
+
+        $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.dump.json', $dumper->formatCatalogue($catalogue, 'messages', ['json_encoding' => JSON_HEX_QUOT]));
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Dumper/MoFileDumperTest.php b/vendor/symfony/translation/Tests/Dumper/MoFileDumperTest.php
new file mode 100755
index 0000000..fbbd75d
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Dumper/MoFileDumperTest.php
@@ -0,0 +1,29 @@
+<?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\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Dumper\MoFileDumper;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class MoFileDumperTest extends TestCase
+{
+    public function testFormatCatalogue()
+    {
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->add(['foo' => 'bar']);
+
+        $dumper = new MoFileDumper();
+
+        $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.mo', $dumper->formatCatalogue($catalogue, 'messages'));
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Dumper/PhpFileDumperTest.php b/vendor/symfony/translation/Tests/Dumper/PhpFileDumperTest.php
new file mode 100755
index 0000000..00e535d
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Dumper/PhpFileDumperTest.php
@@ -0,0 +1,29 @@
+<?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\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Dumper\PhpFileDumper;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class PhpFileDumperTest extends TestCase
+{
+    public function testFormatCatalogue()
+    {
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->add(['foo' => 'bar']);
+
+        $dumper = new PhpFileDumper();
+
+        $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.php', $dumper->formatCatalogue($catalogue, 'messages'));
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Dumper/PoFileDumperTest.php b/vendor/symfony/translation/Tests/Dumper/PoFileDumperTest.php
new file mode 100755
index 0000000..46df869
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Dumper/PoFileDumperTest.php
@@ -0,0 +1,48 @@
+<?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\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Dumper\PoFileDumper;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class PoFileDumperTest extends TestCase
+{
+    public function testFormatCatalogue()
+    {
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->add(['foo' => 'bar', 'bar' => 'foo', 'foo_bar' => 'foobar', 'bar_foo' => 'barfoo']);
+        $catalogue->setMetadata('foo_bar', [
+            'comments' => [
+                'Comment 1',
+                'Comment 2',
+            ],
+            'flags' => [
+                'fuzzy',
+                'another',
+            ],
+            'sources' => [
+                'src/file_1',
+                'src/file_2:50',
+            ],
+        ]);
+        $catalogue->setMetadata('bar_foo', [
+            'comments' => 'Comment',
+            'flags' => 'fuzzy',
+            'sources' => 'src/file_1',
+        ]);
+
+        $dumper = new PoFileDumper();
+
+        $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.po', $dumper->formatCatalogue($catalogue, 'messages'));
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Dumper/QtFileDumperTest.php b/vendor/symfony/translation/Tests/Dumper/QtFileDumperTest.php
new file mode 100755
index 0000000..6c4b559
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Dumper/QtFileDumperTest.php
@@ -0,0 +1,48 @@
+<?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\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Dumper\QtFileDumper;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class QtFileDumperTest extends TestCase
+{
+    public function testFormatCatalogue()
+    {
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->add(['foo' => 'bar', 'foo_bar' => 'foobar', 'bar_foo' => 'barfoo'], 'resources');
+        $catalogue->setMetadata('foo_bar', [
+            'comments' => [
+                'Comment 1',
+                'Comment 2',
+            ],
+            'flags' => [
+                'fuzzy',
+                'another',
+            ],
+            'sources' => [
+                'src/file_1',
+                'src/file_2:50',
+            ],
+        ], 'resources');
+        $catalogue->setMetadata('bar_foo', [
+            'comments' => 'Comment',
+            'flags' => 'fuzzy',
+            'sources' => 'src/file_1',
+        ], 'resources');
+
+        $dumper = new QtFileDumper();
+
+        $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.ts', $dumper->formatCatalogue($catalogue, 'resources'));
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Dumper/XliffFileDumperTest.php b/vendor/symfony/translation/Tests/Dumper/XliffFileDumperTest.php
new file mode 100755
index 0000000..6377132
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Dumper/XliffFileDumperTest.php
@@ -0,0 +1,130 @@
+<?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\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Dumper\XliffFileDumper;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class XliffFileDumperTest extends TestCase
+{
+    public function testFormatCatalogue()
+    {
+        $catalogue = new MessageCatalogue('en_US');
+        $catalogue->add([
+            'foo' => 'bar',
+            'key' => '',
+            'key.with.cdata' => '<source> & <target>',
+        ]);
+        $catalogue->setMetadata('foo', ['notes' => [['priority' => 1, 'from' => 'bar', 'content' => 'baz']]]);
+        $catalogue->setMetadata('key', ['notes' => [['content' => 'baz'], ['content' => 'qux']]]);
+
+        $dumper = new XliffFileDumper();
+
+        $this->assertStringEqualsFile(
+            __DIR__.'/../fixtures/resources-clean.xlf',
+            $dumper->formatCatalogue($catalogue, 'messages', ['default_locale' => 'fr_FR'])
+        );
+    }
+
+    public function testFormatCatalogueXliff2()
+    {
+        $catalogue = new MessageCatalogue('en_US');
+        $catalogue->add([
+            'foo' => 'bar',
+            'key' => '',
+            'key.with.cdata' => '<source> & <target>',
+        ]);
+        $catalogue->setMetadata('key', ['target-attributes' => ['order' => 1]]);
+
+        $dumper = new XliffFileDumper();
+
+        $this->assertStringEqualsFile(
+            __DIR__.'/../fixtures/resources-2.0-clean.xlf',
+            $dumper->formatCatalogue($catalogue, 'messages', ['default_locale' => 'fr_FR', 'xliff_version' => '2.0'])
+        );
+    }
+
+    public function testFormatIcuCatalogueXliff2()
+    {
+        $catalogue = new MessageCatalogue('en_US');
+        $catalogue->add([
+            'foo' => 'bar',
+        ], 'messages'.MessageCatalogue::INTL_DOMAIN_SUFFIX);
+
+        $dumper = new XliffFileDumper();
+
+        $this->assertStringEqualsFile(
+            __DIR__.'/../fixtures/resources-2.0+intl-icu.xlf',
+            $dumper->formatCatalogue($catalogue, 'messages'.MessageCatalogue::INTL_DOMAIN_SUFFIX, ['default_locale' => 'fr_FR', 'xliff_version' => '2.0'])
+        );
+    }
+
+    public function testFormatCatalogueWithCustomToolInfo()
+    {
+        $options = [
+            'default_locale' => 'en_US',
+            'tool_info' => ['tool-id' => 'foo', 'tool-name' => 'foo', 'tool-version' => '0.0', 'tool-company' => 'Foo'],
+        ];
+
+        $catalogue = new MessageCatalogue('en_US');
+        $catalogue->add(['foo' => 'bar']);
+
+        $dumper = new XliffFileDumper();
+
+        $this->assertStringEqualsFile(
+            __DIR__.'/../fixtures/resources-tool-info.xlf',
+            $dumper->formatCatalogue($catalogue, 'messages', $options)
+        );
+    }
+
+    public function testFormatCatalogueWithTargetAttributesMetadata()
+    {
+        $catalogue = new MessageCatalogue('en_US');
+        $catalogue->add([
+            'foo' => 'bar',
+        ]);
+        $catalogue->setMetadata('foo', ['target-attributes' => ['state' => 'needs-translation']]);
+
+        $dumper = new XliffFileDumper();
+
+        $this->assertStringEqualsFile(
+            __DIR__.'/../fixtures/resources-target-attributes.xlf',
+            $dumper->formatCatalogue($catalogue, 'messages', ['default_locale' => 'fr_FR'])
+        );
+    }
+
+    public function testFormatCatalogueWithNotesMetadata()
+    {
+        $catalogue = new MessageCatalogue('en_US');
+        $catalogue->add([
+            'foo' => 'bar',
+            'baz' => 'biz',
+        ]);
+        $catalogue->setMetadata('foo', ['notes' => [
+            ['category' => 'state', 'content' => 'new'],
+            ['category' => 'approved', 'content' => 'true'],
+            ['category' => 'section', 'content' => 'user login', 'priority' => '1'],
+        ]]);
+        $catalogue->setMetadata('baz', ['notes' => [
+            ['id' => 'x', 'content' => 'x_content'],
+            ['appliesTo' => 'target', 'category' => 'quality', 'content' => 'Fuzzy'],
+        ]]);
+
+        $dumper = new XliffFileDumper();
+
+        $this->assertStringEqualsFile(
+            __DIR__.'/../fixtures/resources-notes-meta.xlf',
+            $dumper->formatCatalogue($catalogue, 'messages', ['default_locale' => 'fr_FR', 'xliff_version' => '2.0'])
+        );
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Dumper/YamlFileDumperTest.php b/vendor/symfony/translation/Tests/Dumper/YamlFileDumperTest.php
new file mode 100755
index 0000000..e46da5a
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Dumper/YamlFileDumperTest.php
@@ -0,0 +1,45 @@
+<?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\Translation\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Dumper\YamlFileDumper;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class YamlFileDumperTest extends TestCase
+{
+    public function testTreeFormatCatalogue()
+    {
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->add([
+            'foo.bar1' => 'value1',
+            'foo.bar2' => 'value2',
+        ]);
+
+        $dumper = new YamlFileDumper();
+
+        $this->assertStringEqualsFile(__DIR__.'/../fixtures/messages.yml', $dumper->formatCatalogue($catalogue, 'messages', ['as_tree' => true, 'inline' => 999]));
+    }
+
+    public function testLinearFormatCatalogue()
+    {
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->add([
+            'foo.bar1' => 'value1',
+            'foo.bar2' => 'value2',
+        ]);
+
+        $dumper = new YamlFileDumper();
+
+        $this->assertStringEqualsFile(__DIR__.'/../fixtures/messages_linear.yml', $dumper->formatCatalogue($catalogue, 'messages'));
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Extractor/PhpExtractorTest.php b/vendor/symfony/translation/Tests/Extractor/PhpExtractorTest.php
new file mode 100755
index 0000000..a6d7c50
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Extractor/PhpExtractorTest.php
@@ -0,0 +1,101 @@
+<?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\Translation\Tests\Extractor;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Extractor\PhpExtractor;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class PhpExtractorTest extends TestCase
+{
+    /**
+     * @dataProvider resourcesProvider
+     *
+     * @param array|string $resource
+     */
+    public function testExtraction($resource)
+    {
+        // Arrange
+        $extractor = new PhpExtractor();
+        $extractor->setPrefix('prefix');
+        $catalogue = new MessageCatalogue('en');
+
+        // Act
+        $extractor->extract($resource, $catalogue);
+
+        $expectedHeredoc = <<<EOF
+heredoc key with whitespace and escaped \$\n sequences
+EOF;
+        $expectedNowdoc = <<<'EOF'
+nowdoc key with whitespace and nonescaped \$\n sequences
+EOF;
+        // Assert
+        $expectedCatalogue = [
+            'messages' => [
+                'single-quoted key' => 'prefixsingle-quoted key',
+                'double-quoted key' => 'prefixdouble-quoted key',
+                'heredoc key' => 'prefixheredoc key',
+                'nowdoc key' => 'prefixnowdoc key',
+                "double-quoted key with whitespace and escaped \$\n\" sequences" => "prefixdouble-quoted key with whitespace and escaped \$\n\" sequences",
+                'single-quoted key with whitespace and nonescaped \$\n\' sequences' => 'prefixsingle-quoted key with whitespace and nonescaped \$\n\' sequences',
+                'single-quoted key with "quote mark at the end"' => 'prefixsingle-quoted key with "quote mark at the end"',
+                $expectedHeredoc => 'prefix'.$expectedHeredoc,
+                $expectedNowdoc => 'prefix'.$expectedNowdoc,
+                '{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples' => 'prefix{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples',
+                'concatenated message with heredoc and nowdoc' => 'prefixconcatenated message with heredoc and nowdoc',
+                'default domain' => 'prefixdefault domain',
+            ],
+            'not_messages' => [
+                'other-domain-test-no-params-short-array' => 'prefixother-domain-test-no-params-short-array',
+                'other-domain-test-no-params-long-array' => 'prefixother-domain-test-no-params-long-array',
+                'other-domain-test-params-short-array' => 'prefixother-domain-test-params-short-array',
+                'other-domain-test-params-long-array' => 'prefixother-domain-test-params-long-array',
+                'other-domain-test-trans-choice-short-array-%count%' => 'prefixother-domain-test-trans-choice-short-array-%count%',
+                'other-domain-test-trans-choice-long-array-%count%' => 'prefixother-domain-test-trans-choice-long-array-%count%',
+                'typecast' => 'prefixtypecast',
+                'msg1' => 'prefixmsg1',
+                'msg2' => 'prefixmsg2',
+            ],
+        ];
+        $actualCatalogue = $catalogue->all();
+
+        $this->assertEquals($expectedCatalogue, $actualCatalogue);
+
+        $filename = str_replace(\DIRECTORY_SEPARATOR, '/', __DIR__).'/../fixtures/extractor/translation.html.php';
+        $this->assertEquals(['sources' => [$filename.':2']], $catalogue->getMetadata('single-quoted key'));
+        $this->assertEquals(['sources' => [$filename.':43']], $catalogue->getMetadata('other-domain-test-no-params-short-array', 'not_messages'));
+    }
+
+    public function resourcesProvider()
+    {
+        $directory = __DIR__.'/../fixtures/extractor/';
+        $splFiles = [];
+        foreach (new \DirectoryIterator($directory) as $fileInfo) {
+            if ($fileInfo->isDot()) {
+                continue;
+            }
+            if ('translation.html.php' === $fileInfo->getBasename()) {
+                $phpFile = $fileInfo->getPathname();
+            }
+            $splFiles[] = $fileInfo->getFileInfo();
+        }
+
+        return [
+            [$directory],
+            [$phpFile],
+            [glob($directory.'*')],
+            [$splFiles],
+            [new \ArrayObject(glob($directory.'*'))],
+            [new \ArrayObject($splFiles)],
+        ];
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Formatter/IntlFormatterTest.php b/vendor/symfony/translation/Tests/Formatter/IntlFormatterTest.php
new file mode 100755
index 0000000..45ce6d4
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Formatter/IntlFormatterTest.php
@@ -0,0 +1,96 @@
+<?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\Translation\Tests\Formatter;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+use Symfony\Component\Translation\Formatter\IntlFormatter;
+use Symfony\Component\Translation\Formatter\IntlFormatterInterface;
+
+/**
+ * @requires extension intl
+ */
+class IntlFormatterTest extends \PHPUnit\Framework\TestCase
+{
+    /**
+     * @dataProvider provideDataForFormat
+     */
+    public function testFormat($expected, $message, $arguments)
+    {
+        $this->assertEquals($expected, trim((new IntlFormatter())->formatIntl($message, 'en', $arguments)));
+    }
+
+    public function testInvalidFormat()
+    {
+        $this->expectException(InvalidArgumentException::class);
+        (new IntlFormatter())->formatIntl('{foo', 'en', [2]);
+    }
+
+    public function testFormatWithNamedArguments()
+    {
+        if (version_compare(INTL_ICU_VERSION, '4.8', '<')) {
+            $this->markTestSkipped('Format with named arguments can only be run with ICU 4.8 or higher and PHP >= 5.5');
+        }
+
+        $chooseMessage = <<<'_MSG_'
+{gender_of_host, select,
+  female {{num_guests, plural, offset:1
+      =0 {{host} does not give a party.}
+      =1 {{host} invites {guest} to her party.}
+      =2 {{host} invites {guest} and one other person to her party.}
+     other {{host} invites {guest} as one of the # people invited to her party.}}}
+  male   {{num_guests, plural, offset:1
+      =0 {{host} does not give a party.}
+      =1 {{host} invites {guest} to his party.}
+      =2 {{host} invites {guest} and one other person to his party.}
+     other {{host} invites {guest} as one of the # people invited to his party.}}}
+  other {{num_guests, plural, offset:1
+      =0 {{host} does not give a party.}
+      =1 {{host} invites {guest} to their party.}
+      =2 {{host} invites {guest} and one other person to their party.}
+     other {{host} invites {guest} as one of the # people invited to their party.}}}}
+_MSG_;
+
+        $message = (new IntlFormatter())->formatIntl($chooseMessage, 'en', [
+            'gender_of_host' => 'male',
+            'num_guests' => 10,
+            'host' => 'Fabien',
+            'guest' => 'Guilherme',
+        ]);
+
+        $this->assertEquals('Fabien invites Guilherme as one of the 9 people invited to his party.', $message);
+    }
+
+    public function provideDataForFormat()
+    {
+        return [
+            [
+                'There is one apple',
+                'There is one apple',
+                [],
+            ],
+            [
+                '4,560 monkeys on 123 trees make 37.073 monkeys per tree',
+                '{0,number,integer} monkeys on {1,number,integer} trees make {2,number} monkeys per tree',
+                [4560, 123, 4560 / 123],
+            ],
+        ];
+    }
+
+    public function testPercentsAndBracketsAreTrimmed()
+    {
+        $formatter = new IntlFormatter();
+        $this->assertInstanceof(IntlFormatterInterface::class, $formatter);
+        $this->assertSame('Hello Fab', $formatter->formatIntl('Hello {name}', 'en', ['name' => 'Fab']));
+        $this->assertSame('Hello Fab', $formatter->formatIntl('Hello {name}', 'en', ['%name%' => 'Fab']));
+        $this->assertSame('Hello Fab', $formatter->formatIntl('Hello {name}', 'en', ['{{ name }}' => 'Fab']));
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Formatter/MessageFormatterTest.php b/vendor/symfony/translation/Tests/Formatter/MessageFormatterTest.php
new file mode 100755
index 0000000..232bcf9
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Formatter/MessageFormatterTest.php
@@ -0,0 +1,83 @@
+<?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\Translation\Tests\Formatter;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Formatter\MessageFormatter;
+
+class MessageFormatterTest extends TestCase
+{
+    /**
+     * @dataProvider getTransMessages
+     */
+    public function testFormat($expected, $message, $parameters = [])
+    {
+        $this->assertEquals($expected, $this->getMessageFormatter()->format($message, 'en', $parameters));
+    }
+
+    /**
+     * @dataProvider getTransChoiceMessages
+     * @group legacy
+     */
+    public function testFormatPlural($expected, $message, $number, $parameters)
+    {
+        $this->assertEquals($expected, $this->getMessageFormatter()->choiceFormat($message, $number, 'fr', $parameters));
+    }
+
+    public function getTransMessages()
+    {
+        return [
+            [
+                'There is one apple',
+                'There is one apple',
+            ],
+            [
+                'There are 5 apples',
+                'There are %count% apples',
+                ['%count%' => 5],
+            ],
+            [
+                'There are 5 apples',
+                'There are {{count}} apples',
+                ['{{count}}' => 5],
+            ],
+        ];
+    }
+
+    public function getTransChoiceMessages()
+    {
+        return [
+            ['Il y a 0 pomme', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, ['%count%' => 0]],
+            ['Il y a 1 pomme', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 1, ['%count%' => 1]],
+            ['Il y a 10 pommes', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 10, ['%count%' => 10]],
+
+            ['Il y a 0 pomme', 'Il y a %count% pomme|Il y a %count% pommes', 0, ['%count%' => 0]],
+            ['Il y a 1 pomme', 'Il y a %count% pomme|Il y a %count% pommes', 1, ['%count%' => 1]],
+            ['Il y a 10 pommes', 'Il y a %count% pomme|Il y a %count% pommes', 10, ['%count%' => 10]],
+
+            ['Il y a 0 pomme', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 0, ['%count%' => 0]],
+            ['Il y a 1 pomme', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 1, ['%count%' => 1]],
+            ['Il y a 10 pommes', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 10, ['%count%' => 10]],
+
+            ['Il n\'y a aucune pomme', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 0, ['%count%' => 0]],
+            ['Il y a 1 pomme', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 1, ['%count%' => 1]],
+            ['Il y a 10 pommes', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 10, ['%count%' => 10]],
+
+            ['Il y a 0 pomme', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, ['%count%' => 0]],
+        ];
+    }
+
+    private function getMessageFormatter()
+    {
+        return new MessageFormatter();
+    }
+}
diff --git a/vendor/symfony/translation/Tests/IdentityTranslatorTest.php b/vendor/symfony/translation/Tests/IdentityTranslatorTest.php
new file mode 100755
index 0000000..cf618d9
--- /dev/null
+++ b/vendor/symfony/translation/Tests/IdentityTranslatorTest.php
@@ -0,0 +1,23 @@
+<?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\Translation\Tests;
+
+use Symfony\Component\Translation\IdentityTranslator;
+use Symfony\Contracts\Translation\Test\TranslatorTest;
+
+class IdentityTranslatorTest extends TranslatorTest
+{
+    public function getTranslator()
+    {
+        return new IdentityTranslator();
+    }
+}
diff --git a/vendor/symfony/translation/Tests/IntervalTest.php b/vendor/symfony/translation/Tests/IntervalTest.php
new file mode 100755
index 0000000..bfd90a2
--- /dev/null
+++ b/vendor/symfony/translation/Tests/IntervalTest.php
@@ -0,0 +1,52 @@
+<?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\Translation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Interval;
+
+/**
+ * @group legacy
+ */
+class IntervalTest extends TestCase
+{
+    /**
+     * @dataProvider getTests
+     */
+    public function testTest($expected, $number, $interval)
+    {
+        $this->assertEquals($expected, Interval::test($number, $interval));
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
+     */
+    public function testTestException()
+    {
+        Interval::test(1, 'foobar');
+    }
+
+    public function getTests()
+    {
+        return [
+            [true, 3, '{1,2, 3 ,4}'],
+            [false, 10, '{1,2, 3 ,4}'],
+            [false, 3, '[1,2]'],
+            [true, 1, '[1,2]'],
+            [true, 2, '[1,2]'],
+            [false, 1, ']1,2['],
+            [false, 2, ']1,2['],
+            [true, log(0), '[-Inf,2['],
+            [true, -log(0), '[-2,+Inf]'],
+        ];
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Loader/CsvFileLoaderTest.php b/vendor/symfony/translation/Tests/Loader/CsvFileLoaderTest.php
new file mode 100755
index 0000000..4fd5752
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Loader/CsvFileLoaderTest.php
@@ -0,0 +1,61 @@
+<?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\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Translation\Loader\CsvFileLoader;
+
+class CsvFileLoaderTest extends TestCase
+{
+    public function testLoad()
+    {
+        $loader = new CsvFileLoader();
+        $resource = __DIR__.'/../fixtures/resources.csv';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(['foo' => 'bar'], $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals([new FileResource($resource)], $catalogue->getResources());
+    }
+
+    public function testLoadDoesNothingIfEmpty()
+    {
+        $loader = new CsvFileLoader();
+        $resource = __DIR__.'/../fixtures/empty.csv';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals([], $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals([new FileResource($resource)], $catalogue->getResources());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+     */
+    public function testLoadNonExistingResource()
+    {
+        $loader = new CsvFileLoader();
+        $resource = __DIR__.'/../fixtures/not-exists.csv';
+        $loader->load($resource, 'en', 'domain1');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+     */
+    public function testLoadNonLocalResource()
+    {
+        $loader = new CsvFileLoader();
+        $resource = 'http://example.com/resources.csv';
+        $loader->load($resource, 'en', 'domain1');
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Loader/IcuDatFileLoaderTest.php b/vendor/symfony/translation/Tests/Loader/IcuDatFileLoaderTest.php
new file mode 100755
index 0000000..601680a
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Loader/IcuDatFileLoaderTest.php
@@ -0,0 +1,64 @@
+<?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\Translation\Tests\Loader;
+
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Translation\Loader\IcuDatFileLoader;
+
+/**
+ * @requires extension intl
+ */
+class IcuDatFileLoaderTest extends LocalizedTestCase
+{
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+     */
+    public function testLoadInvalidResource()
+    {
+        $loader = new IcuDatFileLoader();
+        $loader->load(__DIR__.'/../fixtures/resourcebundle/corrupted/resources', 'es', 'domain2');
+    }
+
+    public function testDatEnglishLoad()
+    {
+        // bundled resource is build using pkgdata command which at least in ICU 4.2 comes in extremely! buggy form
+        // you must specify an temporary build directory which is not the same as current directory and
+        // MUST reside on the same partition. pkgdata -p resources -T /srv -d.packagelist.txt
+        $loader = new IcuDatFileLoader();
+        $resource = __DIR__.'/../fixtures/resourcebundle/dat/resources';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(['symfony' => 'Symfony 2 is great'], $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals([new FileResource($resource.'.dat')], $catalogue->getResources());
+    }
+
+    public function testDatFrenchLoad()
+    {
+        $loader = new IcuDatFileLoader();
+        $resource = __DIR__.'/../fixtures/resourcebundle/dat/resources';
+        $catalogue = $loader->load($resource, 'fr', 'domain1');
+
+        $this->assertEquals(['symfony' => 'Symfony 2 est génial'], $catalogue->all('domain1'));
+        $this->assertEquals('fr', $catalogue->getLocale());
+        $this->assertEquals([new FileResource($resource.'.dat')], $catalogue->getResources());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+     */
+    public function testLoadNonExistingResource()
+    {
+        $loader = new IcuDatFileLoader();
+        $loader->load(__DIR__.'/../fixtures/non-existing.txt', 'en', 'domain1');
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Loader/IcuResFileLoaderTest.php b/vendor/symfony/translation/Tests/Loader/IcuResFileLoaderTest.php
new file mode 100755
index 0000000..962c3af
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Loader/IcuResFileLoaderTest.php
@@ -0,0 +1,51 @@
+<?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\Translation\Tests\Loader;
+
+use Symfony\Component\Config\Resource\DirectoryResource;
+use Symfony\Component\Translation\Loader\IcuResFileLoader;
+
+/**
+ * @requires extension intl
+ */
+class IcuResFileLoaderTest extends LocalizedTestCase
+{
+    public function testLoad()
+    {
+        // resource is build using genrb command
+        $loader = new IcuResFileLoader();
+        $resource = __DIR__.'/../fixtures/resourcebundle/res';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(['foo' => 'bar'], $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals([new DirectoryResource($resource)], $catalogue->getResources());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+     */
+    public function testLoadNonExistingResource()
+    {
+        $loader = new IcuResFileLoader();
+        $loader->load(__DIR__.'/../fixtures/non-existing.txt', 'en', 'domain1');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+     */
+    public function testLoadInvalidResource()
+    {
+        $loader = new IcuResFileLoader();
+        $loader->load(__DIR__.'/../fixtures/resourcebundle/corrupted', 'en', 'domain1');
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Loader/IniFileLoaderTest.php b/vendor/symfony/translation/Tests/Loader/IniFileLoaderTest.php
new file mode 100755
index 0000000..e0d8b2f
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Loader/IniFileLoaderTest.php
@@ -0,0 +1,51 @@
+<?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\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Translation\Loader\IniFileLoader;
+
+class IniFileLoaderTest extends TestCase
+{
+    public function testLoad()
+    {
+        $loader = new IniFileLoader();
+        $resource = __DIR__.'/../fixtures/resources.ini';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(['foo' => 'bar'], $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals([new FileResource($resource)], $catalogue->getResources());
+    }
+
+    public function testLoadDoesNothingIfEmpty()
+    {
+        $loader = new IniFileLoader();
+        $resource = __DIR__.'/../fixtures/empty.ini';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals([], $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals([new FileResource($resource)], $catalogue->getResources());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+     */
+    public function testLoadNonExistingResource()
+    {
+        $loader = new IniFileLoader();
+        $resource = __DIR__.'/../fixtures/non-existing.ini';
+        $loader->load($resource, 'en', 'domain1');
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Loader/JsonFileLoaderTest.php b/vendor/symfony/translation/Tests/Loader/JsonFileLoaderTest.php
new file mode 100755
index 0000000..4c507da
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Loader/JsonFileLoaderTest.php
@@ -0,0 +1,62 @@
+<?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\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Translation\Loader\JsonFileLoader;
+
+class JsonFileLoaderTest extends TestCase
+{
+    public function testLoad()
+    {
+        $loader = new JsonFileLoader();
+        $resource = __DIR__.'/../fixtures/resources.json';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(['foo' => 'bar'], $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals([new FileResource($resource)], $catalogue->getResources());
+    }
+
+    public function testLoadDoesNothingIfEmpty()
+    {
+        $loader = new JsonFileLoader();
+        $resource = __DIR__.'/../fixtures/empty.json';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals([], $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals([new FileResource($resource)], $catalogue->getResources());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+     */
+    public function testLoadNonExistingResource()
+    {
+        $loader = new JsonFileLoader();
+        $resource = __DIR__.'/../fixtures/non-existing.json';
+        $loader->load($resource, 'en', 'domain1');
+    }
+
+    /**
+     * @expectedException           \Symfony\Component\Translation\Exception\InvalidResourceException
+     * @expectedExceptionMessage    Error parsing JSON - Syntax error, malformed JSON
+     */
+    public function testParseException()
+    {
+        $loader = new JsonFileLoader();
+        $resource = __DIR__.'/../fixtures/malformed.json';
+        $loader->load($resource, 'en', 'domain1');
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Loader/LocalizedTestCase.php b/vendor/symfony/translation/Tests/Loader/LocalizedTestCase.php
new file mode 100755
index 0000000..279e9fd
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Loader/LocalizedTestCase.php
@@ -0,0 +1,24 @@
+<?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\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+
+abstract class LocalizedTestCase extends TestCase
+{
+    protected function setUp()
+    {
+        if (!\extension_loaded('intl')) {
+            $this->markTestSkipped('Extension intl is required.');
+        }
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Loader/MoFileLoaderTest.php b/vendor/symfony/translation/Tests/Loader/MoFileLoaderTest.php
new file mode 100755
index 0000000..63de5ce
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Loader/MoFileLoaderTest.php
@@ -0,0 +1,72 @@
+<?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\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Translation\Loader\MoFileLoader;
+
+class MoFileLoaderTest extends TestCase
+{
+    public function testLoad()
+    {
+        $loader = new MoFileLoader();
+        $resource = __DIR__.'/../fixtures/resources.mo';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(['foo' => 'bar'], $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals([new FileResource($resource)], $catalogue->getResources());
+    }
+
+    public function testLoadPlurals()
+    {
+        $loader = new MoFileLoader();
+        $resource = __DIR__.'/../fixtures/plurals.mo';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(['foo' => 'bar', 'foos' => '{0} bar|{1} bars'], $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals([new FileResource($resource)], $catalogue->getResources());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+     */
+    public function testLoadNonExistingResource()
+    {
+        $loader = new MoFileLoader();
+        $resource = __DIR__.'/../fixtures/non-existing.mo';
+        $loader->load($resource, 'en', 'domain1');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+     */
+    public function testLoadInvalidResource()
+    {
+        $loader = new MoFileLoader();
+        $resource = __DIR__.'/../fixtures/empty.mo';
+        $loader->load($resource, 'en', 'domain1');
+    }
+
+    public function testLoadEmptyTranslation()
+    {
+        $loader = new MoFileLoader();
+        $resource = __DIR__.'/../fixtures/empty-translation.mo';
+        $catalogue = $loader->load($resource, 'en', 'message');
+
+        $this->assertEquals([], $catalogue->all('message'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals([new FileResource($resource)], $catalogue->getResources());
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Loader/PhpFileLoaderTest.php b/vendor/symfony/translation/Tests/Loader/PhpFileLoaderTest.php
new file mode 100755
index 0000000..68cb2d0
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Loader/PhpFileLoaderTest.php
@@ -0,0 +1,50 @@
+<?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\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Translation\Loader\PhpFileLoader;
+
+class PhpFileLoaderTest extends TestCase
+{
+    public function testLoad()
+    {
+        $loader = new PhpFileLoader();
+        $resource = __DIR__.'/../fixtures/resources.php';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(['foo' => 'bar'], $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals([new FileResource($resource)], $catalogue->getResources());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+     */
+    public function testLoadNonExistingResource()
+    {
+        $loader = new PhpFileLoader();
+        $resource = __DIR__.'/../fixtures/non-existing.php';
+        $loader->load($resource, 'en', 'domain1');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+     */
+    public function testLoadThrowsAnExceptionIfFileNotLocal()
+    {
+        $loader = new PhpFileLoader();
+        $resource = 'http://example.com/resources.php';
+        $loader->load($resource, 'en', 'domain1');
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Loader/PoFileLoaderTest.php b/vendor/symfony/translation/Tests/Loader/PoFileLoaderTest.php
new file mode 100755
index 0000000..d8e2c19
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Loader/PoFileLoaderTest.php
@@ -0,0 +1,109 @@
+<?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\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Translation\Loader\PoFileLoader;
+
+class PoFileLoaderTest extends TestCase
+{
+    public function testLoad()
+    {
+        $loader = new PoFileLoader();
+        $resource = __DIR__.'/../fixtures/resources.po';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(['foo' => 'bar', 'bar' => 'foo'], $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals([new FileResource($resource)], $catalogue->getResources());
+    }
+
+    public function testLoadPlurals()
+    {
+        $loader = new PoFileLoader();
+        $resource = __DIR__.'/../fixtures/plurals.po';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(['foo' => 'bar', 'foos' => 'bar|bars'], $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals([new FileResource($resource)], $catalogue->getResources());
+    }
+
+    public function testLoadDoesNothingIfEmpty()
+    {
+        $loader = new PoFileLoader();
+        $resource = __DIR__.'/../fixtures/empty.po';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals([], $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals([new FileResource($resource)], $catalogue->getResources());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+     */
+    public function testLoadNonExistingResource()
+    {
+        $loader = new PoFileLoader();
+        $resource = __DIR__.'/../fixtures/non-existing.po';
+        $loader->load($resource, 'en', 'domain1');
+    }
+
+    public function testLoadEmptyTranslation()
+    {
+        $loader = new PoFileLoader();
+        $resource = __DIR__.'/../fixtures/empty-translation.po';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(['foo' => ''], $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals([new FileResource($resource)], $catalogue->getResources());
+    }
+
+    public function testEscapedId()
+    {
+        $loader = new PoFileLoader();
+        $resource = __DIR__.'/../fixtures/escaped-id.po';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $messages = $catalogue->all('domain1');
+        $this->assertArrayHasKey('escaped "foo"', $messages);
+        $this->assertEquals('escaped "bar"', $messages['escaped "foo"']);
+    }
+
+    public function testEscapedIdPlurals()
+    {
+        $loader = new PoFileLoader();
+        $resource = __DIR__.'/../fixtures/escaped-id-plurals.po';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $messages = $catalogue->all('domain1');
+        $this->assertArrayHasKey('escaped "foo"', $messages);
+        $this->assertArrayHasKey('escaped "foos"', $messages);
+        $this->assertEquals('escaped "bar"', $messages['escaped "foo"']);
+        $this->assertEquals('escaped "bar"|escaped "bars"', $messages['escaped "foos"']);
+    }
+
+    public function testSkipFuzzyTranslations()
+    {
+        $loader = new PoFileLoader();
+        $resource = __DIR__.'/../fixtures/fuzzy-translations.po';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $messages = $catalogue->all('domain1');
+        $this->assertArrayHasKey('foo1', $messages);
+        $this->assertArrayNotHasKey('foo2', $messages);
+        $this->assertArrayHasKey('foo3', $messages);
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Loader/QtFileLoaderTest.php b/vendor/symfony/translation/Tests/Loader/QtFileLoaderTest.php
new file mode 100755
index 0000000..f149b8c
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Loader/QtFileLoaderTest.php
@@ -0,0 +1,79 @@
+<?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\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Translation\Loader\QtFileLoader;
+
+class QtFileLoaderTest extends TestCase
+{
+    public function testLoad()
+    {
+        $loader = new QtFileLoader();
+        $resource = __DIR__.'/../fixtures/resources.ts';
+        $catalogue = $loader->load($resource, 'en', 'resources');
+
+        $this->assertEquals([
+            'foo' => 'bar',
+            'foo_bar' => 'foobar',
+            'bar_foo' => 'barfoo',
+        ], $catalogue->all('resources'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals([new FileResource($resource)], $catalogue->getResources());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+     */
+    public function testLoadNonExistingResource()
+    {
+        $loader = new QtFileLoader();
+        $resource = __DIR__.'/../fixtures/non-existing.ts';
+        $loader->load($resource, 'en', 'domain1');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+     */
+    public function testLoadNonLocalResource()
+    {
+        $loader = new QtFileLoader();
+        $resource = 'http://domain1.com/resources.ts';
+        $loader->load($resource, 'en', 'domain1');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+     */
+    public function testLoadInvalidResource()
+    {
+        $loader = new QtFileLoader();
+        $resource = __DIR__.'/../fixtures/invalid-xml-resources.xlf';
+        $loader->load($resource, 'en', 'domain1');
+    }
+
+    public function testLoadEmptyResource()
+    {
+        $loader = new QtFileLoader();
+        $resource = __DIR__.'/../fixtures/empty.xlf';
+
+        if (method_exists($this, 'expectException')) {
+            $this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException');
+            $this->expectExceptionMessage(sprintf('Unable to load "%s".', $resource));
+        } else {
+            $this->setExpectedException('Symfony\Component\Translation\Exception\InvalidResourceException', sprintf('Unable to load "%s".', $resource));
+        }
+
+        $loader->load($resource, 'en', 'domain1');
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Loader/XliffFileLoaderTest.php b/vendor/symfony/translation/Tests/Loader/XliffFileLoaderTest.php
new file mode 100755
index 0000000..1ca8336
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Loader/XliffFileLoaderTest.php
@@ -0,0 +1,331 @@
+<?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\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Translation\Loader\XliffFileLoader;
+
+class XliffFileLoaderTest extends TestCase
+{
+    public function testLoad()
+    {
+        $loader = new XliffFileLoader();
+        $resource = __DIR__.'/../fixtures/resources.xlf';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals([new FileResource($resource)], $catalogue->getResources());
+        $this->assertSame([], libxml_get_errors());
+        $this->assertContainsOnly('string', $catalogue->all('domain1'));
+    }
+
+    public function testLoadWithInternalErrorsEnabled()
+    {
+        $internalErrors = libxml_use_internal_errors(true);
+
+        $this->assertSame([], libxml_get_errors());
+
+        $loader = new XliffFileLoader();
+        $resource = __DIR__.'/../fixtures/resources.xlf';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals([new FileResource($resource)], $catalogue->getResources());
+        $this->assertSame([], libxml_get_errors());
+
+        libxml_clear_errors();
+        libxml_use_internal_errors($internalErrors);
+    }
+
+    public function testLoadWithExternalEntitiesDisabled()
+    {
+        $disableEntities = libxml_disable_entity_loader(true);
+
+        $loader = new XliffFileLoader();
+        $resource = __DIR__.'/../fixtures/resources.xlf';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        libxml_disable_entity_loader($disableEntities);
+
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals([new FileResource($resource)], $catalogue->getResources());
+    }
+
+    public function testLoadWithResname()
+    {
+        $loader = new XliffFileLoader();
+        $catalogue = $loader->load(__DIR__.'/../fixtures/resname.xlf', 'en', 'domain1');
+
+        $this->assertEquals(['foo' => 'bar', 'bar' => 'baz', 'baz' => 'foo', 'qux' => 'qux source'], $catalogue->all('domain1'));
+    }
+
+    public function testIncompleteResource()
+    {
+        $loader = new XliffFileLoader();
+        $catalogue = $loader->load(__DIR__.'/../fixtures/resources.xlf', 'en', 'domain1');
+
+        $this->assertEquals(['foo' => 'bar', 'extra' => 'extra', 'key' => '', 'test' => 'with'], $catalogue->all('domain1'));
+    }
+
+    public function testEncoding()
+    {
+        $loader = new XliffFileLoader();
+        $catalogue = $loader->load(__DIR__.'/../fixtures/encoding.xlf', 'en', 'domain1');
+
+        $this->assertEquals(utf8_decode('föö'), $catalogue->get('bar', 'domain1'));
+        $this->assertEquals(utf8_decode('bär'), $catalogue->get('foo', 'domain1'));
+        $this->assertEquals(
+            [
+                'source' => 'foo',
+                'notes' => [['content' => utf8_decode('bäz')]],
+                'id' => '1',
+                'file' => [
+                    'original' => 'file.ext',
+                ],
+            ],
+            $catalogue->getMetadata('foo', 'domain1')
+        );
+    }
+
+    public function testTargetAttributesAreStoredCorrectly()
+    {
+        $loader = new XliffFileLoader();
+        $catalogue = $loader->load(__DIR__.'/../fixtures/with-attributes.xlf', 'en', 'domain1');
+
+        $metadata = $catalogue->getMetadata('foo', 'domain1');
+        $this->assertEquals('translated', $metadata['target-attributes']['state']);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+     */
+    public function testLoadInvalidResource()
+    {
+        $loader = new XliffFileLoader();
+        $loader->load(__DIR__.'/../fixtures/resources.php', 'en', 'domain1');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+     */
+    public function testLoadResourceDoesNotValidate()
+    {
+        $loader = new XliffFileLoader();
+        $loader->load(__DIR__.'/../fixtures/non-valid.xlf', 'en', 'domain1');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+     */
+    public function testLoadNonExistingResource()
+    {
+        $loader = new XliffFileLoader();
+        $resource = __DIR__.'/../fixtures/non-existing.xlf';
+        $loader->load($resource, 'en', 'domain1');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+     */
+    public function testLoadThrowsAnExceptionIfFileNotLocal()
+    {
+        $loader = new XliffFileLoader();
+        $resource = 'http://example.com/resources.xlf';
+        $loader->load($resource, 'en', 'domain1');
+    }
+
+    /**
+     * @expectedException        \Symfony\Component\Translation\Exception\InvalidResourceException
+     * @expectedExceptionMessage Document types are not allowed.
+     */
+    public function testDocTypeIsNotAllowed()
+    {
+        $loader = new XliffFileLoader();
+        $loader->load(__DIR__.'/../fixtures/withdoctype.xlf', 'en', 'domain1');
+    }
+
+    public function testParseEmptyFile()
+    {
+        $loader = new XliffFileLoader();
+        $resource = __DIR__.'/../fixtures/empty.xlf';
+
+        if (method_exists($this, 'expectException')) {
+            $this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException');
+            $this->expectExceptionMessage(sprintf('Unable to load "%s":', $resource));
+        } else {
+            $this->setExpectedException('Symfony\Component\Translation\Exception\InvalidResourceException', sprintf('Unable to load "%s":', $resource));
+        }
+
+        $loader->load($resource, 'en', 'domain1');
+    }
+
+    public function testLoadNotes()
+    {
+        $loader = new XliffFileLoader();
+        $catalogue = $loader->load(__DIR__.'/../fixtures/withnote.xlf', 'en', 'domain1');
+
+        $this->assertEquals(
+            [
+                'source' => 'foo',
+                'notes' => [['priority' => 1, 'content' => 'foo']],
+                'id' => '1',
+                'file' => [
+                    'original' => 'file.ext',
+                ],
+            ],
+            $catalogue->getMetadata('foo', 'domain1')
+        );
+        // message without target
+        $this->assertEquals(
+            [
+                'source' => 'extrasource',
+                'notes' => [['content' => 'bar', 'from' => 'foo']],
+                'id' => '2',
+                'file' => [
+                    'original' => 'file.ext',
+                ],
+            ],
+            $catalogue->getMetadata('extra', 'domain1')
+        );
+        // message with empty target
+        $this->assertEquals(
+            [
+                'source' => 'key',
+                'notes' => [
+                    ['content' => 'baz'],
+                    ['priority' => 2, 'from' => 'bar', 'content' => 'qux'],
+                ],
+                'id' => '123',
+                'file' => [
+                    'original' => 'file.ext',
+                ],
+            ],
+            $catalogue->getMetadata('key', 'domain1')
+        );
+    }
+
+    public function testLoadVersion2()
+    {
+        $loader = new XliffFileLoader();
+        $resource = __DIR__.'/../fixtures/resources-2.0.xlf';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals([new FileResource($resource)], $catalogue->getResources());
+        $this->assertSame([], libxml_get_errors());
+
+        $domains = $catalogue->all();
+        $this->assertCount(3, $domains['domain1']);
+        $this->assertContainsOnly('string', $catalogue->all('domain1'));
+
+        // target attributes
+        $this->assertEquals(['target-attributes' => ['order' => 1]], $catalogue->getMetadata('bar', 'domain1'));
+    }
+
+    public function testLoadVersion2WithNoteMeta()
+    {
+        $loader = new XliffFileLoader();
+        $resource = __DIR__.'/../fixtures/resources-notes-meta.xlf';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals([new FileResource($resource)], $catalogue->getResources());
+        $this->assertSame([], libxml_get_errors());
+
+        // test for "foo" metadata
+        $this->assertTrue($catalogue->defines('foo', 'domain1'));
+        $metadata = $catalogue->getMetadata('foo', 'domain1');
+        $this->assertNotEmpty($metadata);
+        $this->assertCount(3, $metadata['notes']);
+
+        $this->assertEquals('state', $metadata['notes'][0]['category']);
+        $this->assertEquals('new', $metadata['notes'][0]['content']);
+
+        $this->assertEquals('approved', $metadata['notes'][1]['category']);
+        $this->assertEquals('true', $metadata['notes'][1]['content']);
+
+        $this->assertEquals('section', $metadata['notes'][2]['category']);
+        $this->assertEquals('1', $metadata['notes'][2]['priority']);
+        $this->assertEquals('user login', $metadata['notes'][2]['content']);
+
+        // test for "baz" metadata
+        $this->assertTrue($catalogue->defines('baz', 'domain1'));
+        $metadata = $catalogue->getMetadata('baz', 'domain1');
+        $this->assertNotEmpty($metadata);
+        $this->assertCount(2, $metadata['notes']);
+
+        $this->assertEquals('x', $metadata['notes'][0]['id']);
+        $this->assertEquals('x_content', $metadata['notes'][0]['content']);
+
+        $this->assertEquals('target', $metadata['notes'][1]['appliesTo']);
+        $this->assertEquals('quality', $metadata['notes'][1]['category']);
+        $this->assertEquals('Fuzzy', $metadata['notes'][1]['content']);
+    }
+
+    public function testLoadVersion2WithMultiSegmentUnit()
+    {
+        $loader = new XliffFileLoader();
+        $resource = __DIR__.'/../fixtures/resources-2.0-multi-segment-unit.xlf';
+        $catalog = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertSame('en', $catalog->getLocale());
+        $this->assertEquals([new FileResource($resource)], $catalog->getResources());
+        $this->assertFalse(libxml_get_last_error());
+
+        // test for "foo" metadata
+        $this->assertTrue($catalog->defines('foo', 'domain1'));
+        $metadata = $catalog->getMetadata('foo', 'domain1');
+        $this->assertNotEmpty($metadata);
+        $this->assertCount(1, $metadata['notes']);
+
+        $this->assertSame('processed', $metadata['notes'][0]['category']);
+        $this->assertSame('true', $metadata['notes'][0]['content']);
+
+        // test for "bar" metadata
+        $this->assertTrue($catalog->defines('bar', 'domain1'));
+        $metadata = $catalog->getMetadata('bar', 'domain1');
+        $this->assertNotEmpty($metadata);
+        $this->assertCount(1, $metadata['notes']);
+
+        $this->assertSame('processed', $metadata['notes'][0]['category']);
+        $this->assertSame('true', $metadata['notes'][0]['content']);
+    }
+
+    public function testLoadWithMultipleFileNodes()
+    {
+        $loader = new XliffFileLoader();
+        $catalogue = $loader->load(__DIR__.'/../fixtures/resources-multi-files.xlf', 'en', 'domain1');
+
+        $this->assertEquals(
+            [
+                'source' => 'foo',
+                'id' => '1',
+                'file' => [
+                    'original' => 'file.ext',
+                ],
+            ],
+            $catalogue->getMetadata('foo', 'domain1')
+        );
+        $this->assertEquals(
+            [
+                'source' => 'test',
+                'notes' => [['content' => 'note']],
+                'id' => '4',
+                'file' => [
+                    'original' => 'otherfile.ext',
+                ],
+            ],
+            $catalogue->getMetadata('test', 'domain1')
+        );
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Loader/YamlFileLoaderTest.php b/vendor/symfony/translation/Tests/Loader/YamlFileLoaderTest.php
new file mode 100755
index 0000000..a535db5
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Loader/YamlFileLoaderTest.php
@@ -0,0 +1,71 @@
+<?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\Translation\Tests\Loader;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Translation\Loader\YamlFileLoader;
+
+class YamlFileLoaderTest extends TestCase
+{
+    public function testLoad()
+    {
+        $loader = new YamlFileLoader();
+        $resource = __DIR__.'/../fixtures/resources.yml';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals(['foo' => 'bar'], $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals([new FileResource($resource)], $catalogue->getResources());
+    }
+
+    public function testLoadDoesNothingIfEmpty()
+    {
+        $loader = new YamlFileLoader();
+        $resource = __DIR__.'/../fixtures/empty.yml';
+        $catalogue = $loader->load($resource, 'en', 'domain1');
+
+        $this->assertEquals([], $catalogue->all('domain1'));
+        $this->assertEquals('en', $catalogue->getLocale());
+        $this->assertEquals([new FileResource($resource)], $catalogue->getResources());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+     */
+    public function testLoadNonExistingResource()
+    {
+        $loader = new YamlFileLoader();
+        $resource = __DIR__.'/../fixtures/non-existing.yml';
+        $loader->load($resource, 'en', 'domain1');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+     */
+    public function testLoadThrowsAnExceptionIfFileNotLocal()
+    {
+        $loader = new YamlFileLoader();
+        $resource = 'http://example.com/resources.yml';
+        $loader->load($resource, 'en', 'domain1');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException
+     */
+    public function testLoadThrowsAnExceptionIfNotAnArray()
+    {
+        $loader = new YamlFileLoader();
+        $resource = __DIR__.'/../fixtures/non-valid.yml';
+        $loader->load($resource, 'en', 'domain1');
+    }
+}
diff --git a/vendor/symfony/translation/Tests/LoggingTranslatorTest.php b/vendor/symfony/translation/Tests/LoggingTranslatorTest.php
new file mode 100755
index 0000000..450e060
--- /dev/null
+++ b/vendor/symfony/translation/Tests/LoggingTranslatorTest.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\Translation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Loader\ArrayLoader;
+use Symfony\Component\Translation\LoggingTranslator;
+use Symfony\Component\Translation\Translator;
+
+class LoggingTranslatorTest extends TestCase
+{
+    public function testTransWithNoTranslationIsLogged()
+    {
+        $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
+        $logger->expects($this->exactly(1))
+            ->method('warning')
+            ->with('Translation not found.')
+        ;
+
+        $translator = new Translator('ar');
+        $loggableTranslator = new LoggingTranslator($translator, $logger);
+        $loggableTranslator->trans('bar');
+    }
+
+    /**
+     * @group legacy
+     */
+    public function testTransChoiceFallbackIsLogged()
+    {
+        $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
+        $logger->expects($this->once())
+            ->method('debug')
+            ->with('Translation use fallback catalogue.')
+        ;
+
+        $translator = new Translator('ar');
+        $translator->setFallbackLocales(['en']);
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', ['some_message2' => 'one thing|%count% things'], 'en');
+        $loggableTranslator = new LoggingTranslator($translator, $logger);
+        $loggableTranslator->transChoice('some_message2', 10, ['%count%' => 10]);
+    }
+
+    /**
+     * @group legacy
+     */
+    public function testTransChoiceWithNoTranslationIsLogged()
+    {
+        $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
+        $logger->expects($this->exactly(1))
+            ->method('warning')
+            ->with('Translation not found.')
+        ;
+
+        $translator = new Translator('ar');
+        $loggableTranslator = new LoggingTranslator($translator, $logger);
+        $loggableTranslator->transChoice('some_message2', 10, ['%count%' => 10]);
+    }
+}
diff --git a/vendor/symfony/translation/Tests/MessageCatalogueTest.php b/vendor/symfony/translation/Tests/MessageCatalogueTest.php
new file mode 100755
index 0000000..cf0dd1a
--- /dev/null
+++ b/vendor/symfony/translation/Tests/MessageCatalogueTest.php
@@ -0,0 +1,243 @@
+<?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\Translation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class MessageCatalogueTest extends TestCase
+{
+    public function testGetLocale()
+    {
+        $catalogue = new MessageCatalogue('en');
+
+        $this->assertEquals('en', $catalogue->getLocale());
+    }
+
+    public function testGetDomains()
+    {
+        $catalogue = new MessageCatalogue('en', ['domain1' => [], 'domain2' => [], 'domain2+intl-icu' => [], 'domain3+intl-icu' => []]);
+
+        $this->assertEquals(['domain1', 'domain2', 'domain3'], $catalogue->getDomains());
+    }
+
+    public function testAll()
+    {
+        $catalogue = new MessageCatalogue('en', $messages = ['domain1' => ['foo' => 'foo'], 'domain2' => ['bar' => 'bar']]);
+
+        $this->assertEquals(['foo' => 'foo'], $catalogue->all('domain1'));
+        $this->assertEquals([], $catalogue->all('domain88'));
+        $this->assertEquals($messages, $catalogue->all());
+
+        $messages = ['domain1+intl-icu' => ['foo' => 'bar']] + $messages + [
+            'domain2+intl-icu' => ['bar' => 'foo'],
+            'domain3+intl-icu' => ['biz' => 'biz'],
+        ];
+        $catalogue = new MessageCatalogue('en', $messages);
+
+        $this->assertEquals(['foo' => 'bar'], $catalogue->all('domain1'));
+        $this->assertEquals(['bar' => 'foo'], $catalogue->all('domain2'));
+        $this->assertEquals(['biz' => 'biz'], $catalogue->all('domain3'));
+
+        $messages = [
+            'domain1' => ['foo' => 'bar'],
+            'domain2' => ['bar' => 'foo'],
+            'domain3' => ['biz' => 'biz'],
+        ];
+        $this->assertEquals($messages, $catalogue->all());
+    }
+
+    public function testHas()
+    {
+        $catalogue = new MessageCatalogue('en', ['domain1' => ['foo' => 'foo'], 'domain2+intl-icu' => ['bar' => 'bar']]);
+
+        $this->assertTrue($catalogue->has('foo', 'domain1'));
+        $this->assertTrue($catalogue->has('bar', 'domain2'));
+        $this->assertFalse($catalogue->has('bar', 'domain1'));
+        $this->assertFalse($catalogue->has('foo', 'domain88'));
+    }
+
+    public function testGetSet()
+    {
+        $catalogue = new MessageCatalogue('en', ['domain1' => ['foo' => 'foo'], 'domain2' => ['bar' => 'bar'], 'domain2+intl-icu' => ['bar' => 'foo']]);
+        $catalogue->set('foo1', 'foo1', 'domain1');
+
+        $this->assertEquals('foo', $catalogue->get('foo', 'domain1'));
+        $this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
+        $this->assertEquals('foo', $catalogue->get('bar', 'domain2'));
+    }
+
+    public function testAdd()
+    {
+        $catalogue = new MessageCatalogue('en', ['domain1' => ['foo' => 'foo'], 'domain2' => ['bar' => 'bar']]);
+        $catalogue->add(['foo1' => 'foo1'], 'domain1');
+
+        $this->assertEquals('foo', $catalogue->get('foo', 'domain1'));
+        $this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
+
+        $catalogue->add(['foo' => 'bar'], 'domain1');
+        $this->assertEquals('bar', $catalogue->get('foo', 'domain1'));
+        $this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
+
+        $catalogue->add(['foo' => 'bar'], 'domain88');
+        $this->assertEquals('bar', $catalogue->get('foo', 'domain88'));
+    }
+
+    public function testReplace()
+    {
+        $catalogue = new MessageCatalogue('en', ['domain1' => ['foo' => 'foo'], 'domain1+intl-icu' => ['bar' => 'bar']]);
+        $catalogue->replace($messages = ['foo1' => 'foo1'], 'domain1');
+
+        $this->assertEquals($messages, $catalogue->all('domain1'));
+    }
+
+    public function testAddCatalogue()
+    {
+        $r = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
+        $r->expects($this->any())->method('__toString')->willReturn('r');
+
+        $r1 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
+        $r1->expects($this->any())->method('__toString')->willReturn('r1');
+
+        $catalogue = new MessageCatalogue('en', ['domain1' => ['foo' => 'foo']]);
+        $catalogue->addResource($r);
+
+        $catalogue1 = new MessageCatalogue('en', ['domain1' => ['foo1' => 'foo1'], 'domain2+intl-icu' => ['bar' => 'bar']]);
+        $catalogue1->addResource($r1);
+
+        $catalogue->addCatalogue($catalogue1);
+
+        $this->assertEquals('foo', $catalogue->get('foo', 'domain1'));
+        $this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
+        $this->assertEquals('bar', $catalogue->get('bar', 'domain2'));
+        $this->assertEquals('bar', $catalogue->get('bar', 'domain2+intl-icu'));
+
+        $this->assertEquals([$r, $r1], $catalogue->getResources());
+    }
+
+    public function testAddFallbackCatalogue()
+    {
+        $r = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
+        $r->expects($this->any())->method('__toString')->willReturn('r');
+
+        $r1 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
+        $r1->expects($this->any())->method('__toString')->willReturn('r1');
+
+        $r2 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
+        $r2->expects($this->any())->method('__toString')->willReturn('r2');
+
+        $catalogue = new MessageCatalogue('fr_FR', ['domain1' => ['foo' => 'foo'], 'domain2' => ['bar' => 'bar']]);
+        $catalogue->addResource($r);
+
+        $catalogue1 = new MessageCatalogue('fr', ['domain1' => ['foo' => 'bar', 'foo1' => 'foo1']]);
+        $catalogue1->addResource($r1);
+
+        $catalogue2 = new MessageCatalogue('en');
+        $catalogue2->addResource($r2);
+
+        $catalogue->addFallbackCatalogue($catalogue1);
+        $catalogue1->addFallbackCatalogue($catalogue2);
+
+        $this->assertEquals('foo', $catalogue->get('foo', 'domain1'));
+        $this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
+
+        $this->assertEquals([$r, $r1, $r2], $catalogue->getResources());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\LogicException
+     */
+    public function testAddFallbackCatalogueWithParentCircularReference()
+    {
+        $main = new MessageCatalogue('en_US');
+        $fallback = new MessageCatalogue('fr_FR');
+
+        $fallback->addFallbackCatalogue($main);
+        $main->addFallbackCatalogue($fallback);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\LogicException
+     */
+    public function testAddFallbackCatalogueWithFallbackCircularReference()
+    {
+        $fr = new MessageCatalogue('fr');
+        $en = new MessageCatalogue('en');
+        $es = new MessageCatalogue('es');
+
+        $fr->addFallbackCatalogue($en);
+        $es->addFallbackCatalogue($en);
+        $en->addFallbackCatalogue($fr);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\LogicException
+     */
+    public function testAddCatalogueWhenLocaleIsNotTheSameAsTheCurrentOne()
+    {
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->addCatalogue(new MessageCatalogue('fr', []));
+    }
+
+    public function testGetAddResource()
+    {
+        $catalogue = new MessageCatalogue('en');
+        $r = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
+        $r->expects($this->any())->method('__toString')->willReturn('r');
+        $catalogue->addResource($r);
+        $catalogue->addResource($r);
+        $r1 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
+        $r1->expects($this->any())->method('__toString')->willReturn('r1');
+        $catalogue->addResource($r1);
+
+        $this->assertEquals([$r, $r1], $catalogue->getResources());
+    }
+
+    public function testMetadataDelete()
+    {
+        $catalogue = new MessageCatalogue('en');
+        $this->assertEquals([], $catalogue->getMetadata('', ''), 'Metadata is empty');
+        $catalogue->deleteMetadata('key', 'messages');
+        $catalogue->deleteMetadata('', 'messages');
+        $catalogue->deleteMetadata();
+    }
+
+    public function testMetadataSetGetDelete()
+    {
+        $catalogue = new MessageCatalogue('en');
+        $catalogue->setMetadata('key', 'value');
+        $this->assertEquals('value', $catalogue->getMetadata('key', 'messages'), "Metadata 'key' = 'value'");
+
+        $catalogue->setMetadata('key2', []);
+        $this->assertEquals([], $catalogue->getMetadata('key2', 'messages'), 'Metadata key2 is array');
+
+        $catalogue->deleteMetadata('key2', 'messages');
+        $this->assertNull($catalogue->getMetadata('key2', 'messages'), 'Metadata key2 should is deleted.');
+
+        $catalogue->deleteMetadata('key2', 'domain');
+        $this->assertNull($catalogue->getMetadata('key2', 'domain'), 'Metadata key2 should is deleted.');
+    }
+
+    public function testMetadataMerge()
+    {
+        $cat1 = new MessageCatalogue('en');
+        $cat1->setMetadata('a', 'b');
+        $this->assertEquals(['messages' => ['a' => 'b']], $cat1->getMetadata('', ''), 'Cat1 contains messages metadata.');
+
+        $cat2 = new MessageCatalogue('en');
+        $cat2->setMetadata('b', 'c', 'domain');
+        $this->assertEquals(['domain' => ['b' => 'c']], $cat2->getMetadata('', ''), 'Cat2 contains domain metadata.');
+
+        $cat1->addCatalogue($cat2);
+        $this->assertEquals(['messages' => ['a' => 'b'], 'domain' => ['b' => 'c']], $cat1->getMetadata('', ''), 'Cat1 contains merged metadata.');
+    }
+}
diff --git a/vendor/symfony/translation/Tests/MessageSelectorTest.php b/vendor/symfony/translation/Tests/MessageSelectorTest.php
new file mode 100755
index 0000000..f099716
--- /dev/null
+++ b/vendor/symfony/translation/Tests/MessageSelectorTest.php
@@ -0,0 +1,140 @@
+<?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\Translation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\MessageSelector;
+
+/**
+ * @group legacy
+ */
+class MessageSelectorTest extends TestCase
+{
+    /**
+     * @dataProvider getChooseTests
+     */
+    public function testChoose($expected, $id, $number)
+    {
+        $selector = new MessageSelector();
+
+        $this->assertEquals($expected, $selector->choose($id, $number, 'en'));
+    }
+
+    public function testReturnMessageIfExactlyOneStandardRuleIsGiven()
+    {
+        $selector = new MessageSelector();
+
+        $this->assertEquals('There are two apples', $selector->choose('There are two apples', 2, 'en'));
+    }
+
+    /**
+     * @dataProvider getNonMatchingMessages
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
+     */
+    public function testThrowExceptionIfMatchingMessageCannotBeFound($id, $number)
+    {
+        $selector = new MessageSelector();
+
+        $selector->choose($id, $number, 'en');
+    }
+
+    public function getNonMatchingMessages()
+    {
+        return [
+            ['{0} There are no apples|{1} There is one apple', 2],
+            ['{1} There is one apple|]1,Inf] There are %count% apples', 0],
+            ['{1} There is one apple|]2,Inf] There are %count% apples', 2],
+            ['{0} There are no apples|There is one apple', 2],
+        ];
+    }
+
+    public function getChooseTests()
+    {
+        return [
+            ['There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0],
+            ['There are no apples', '{0}     There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0],
+            ['There are no apples', '{0}There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0],
+
+            ['There is one apple', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 1],
+
+            ['There are %count% apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10],
+            ['There are %count% apples', '{0} There are no apples|{1} There is one apple|]1,Inf]There are %count% apples', 10],
+            ['There are %count% apples', '{0} There are no apples|{1} There is one apple|]1,Inf]     There are %count% apples', 10],
+
+            ['There are %count% apples', 'There is one apple|There are %count% apples', 0],
+            ['There is one apple', 'There is one apple|There are %count% apples', 1],
+            ['There are %count% apples', 'There is one apple|There are %count% apples', 10],
+
+            ['There are %count% apples', 'one: There is one apple|more: There are %count% apples', 0],
+            ['There is one apple', 'one: There is one apple|more: There are %count% apples', 1],
+            ['There are %count% apples', 'one: There is one apple|more: There are %count% apples', 10],
+
+            ['There are no apples', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 0],
+            ['There is one apple', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 1],
+            ['There are %count% apples', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 10],
+
+            ['', '{0}|{1} There is one apple|]1,Inf] There are %count% apples', 0],
+            ['', '{0} There are no apples|{1}|]1,Inf] There are %count% apples', 1],
+
+            // Indexed only tests which are Gettext PoFile* compatible strings.
+            ['There are %count% apples', 'There is one apple|There are %count% apples', 0],
+            ['There is one apple', 'There is one apple|There are %count% apples', 1],
+            ['There are %count% apples', 'There is one apple|There are %count% apples', 2],
+
+            // Tests for float numbers
+            ['There is almost one apple', '{0} There are no apples|]0,1[ There is almost one apple|{1} There is one apple|[1,Inf] There is more than one apple', 0.7],
+            ['There is one apple', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 1],
+            ['There is more than one apple', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 1.7],
+            ['There are no apples', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0],
+            ['There are no apples', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0.0],
+            ['There are no apples', '{0.0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0],
+
+            // Test texts with new-lines
+            // with double-quotes and \n in id & double-quotes and actual newlines in text
+            ["This is a text with a\n            new-line in it. Selector = 0.", '{0}This is a text with a
+            new-line in it. Selector = 0.|{1}This is a text with a
+            new-line in it. Selector = 1.|[1,Inf]This is a text with a
+            new-line in it. Selector > 1.', 0],
+            // with double-quotes and \n in id and single-quotes and actual newlines in text
+            ["This is a text with a\n            new-line in it. Selector = 1.", '{0}This is a text with a
+            new-line in it. Selector = 0.|{1}This is a text with a
+            new-line in it. Selector = 1.|[1,Inf]This is a text with a
+            new-line in it. Selector > 1.', 1],
+            ["This is a text with a\n            new-line in it. Selector > 1.", '{0}This is a text with a
+            new-line in it. Selector = 0.|{1}This is a text with a
+            new-line in it. Selector = 1.|[1,Inf]This is a text with a
+            new-line in it. Selector > 1.', 5],
+            // with double-quotes and id split accros lines
+            ['This is a text with a
+            new-line in it. Selector = 1.', '{0}This is a text with a
+            new-line in it. Selector = 0.|{1}This is a text with a
+            new-line in it. Selector = 1.|[1,Inf]This is a text with a
+            new-line in it. Selector > 1.', 1],
+            // with single-quotes and id split accros lines
+            ['This is a text with a
+            new-line in it. Selector > 1.', '{0}This is a text with a
+            new-line in it. Selector = 0.|{1}This is a text with a
+            new-line in it. Selector = 1.|[1,Inf]This is a text with a
+            new-line in it. Selector > 1.', 5],
+            // with single-quotes and \n in text
+            ['This is a text with a\nnew-line in it. Selector = 0.', '{0}This is a text with a\nnew-line in it. Selector = 0.|{1}This is a text with a\nnew-line in it. Selector = 1.|[1,Inf]This is a text with a\nnew-line in it. Selector > 1.', 0],
+            // with double-quotes and id split accros lines
+            ["This is a text with a\nnew-line in it. Selector = 1.", "{0}This is a text with a\nnew-line in it. Selector = 0.|{1}This is a text with a\nnew-line in it. Selector = 1.|[1,Inf]This is a text with a\nnew-line in it. Selector > 1.", 1],
+            // esacape pipe
+            ['This is a text with | in it. Selector = 0.', '{0}This is a text with || in it. Selector = 0.|{1}This is a text with || in it. Selector = 1.', 0],
+            // Empty plural set (2 plural forms) from a .PO file
+            ['', '|', 1],
+            // Empty plural set (3 plural forms) from a .PO file
+            ['', '||', 1],
+        ];
+    }
+}
diff --git a/vendor/symfony/translation/Tests/PluralizationRulesTest.php b/vendor/symfony/translation/Tests/PluralizationRulesTest.php
new file mode 100755
index 0000000..696c92b
--- /dev/null
+++ b/vendor/symfony/translation/Tests/PluralizationRulesTest.php
@@ -0,0 +1,124 @@
+<?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\Translation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\PluralizationRules;
+
+/**
+ * Test should cover all languages mentioned on http://translate.sourceforge.net/wiki/l10n/pluralforms
+ * and Plural forms mentioned on http://www.gnu.org/software/gettext/manual/gettext.html#Plural-forms.
+ *
+ * See also https://developer.mozilla.org/en/Localization_and_Plurals which mentions 15 rules having a maximum of 6 forms.
+ * The mozilla code is also interesting to check for.
+ *
+ * As mentioned by chx http://drupal.org/node/1273968 we can cover all by testing number from 0 to 199
+ *
+ * The goal to cover all languages is to far fetched so this test case is smaller.
+ *
+ * @author Clemens Tolboom clemens@build2be.nl
+ *
+ * @group legacy
+ */
+class PluralizationRulesTest extends TestCase
+{
+    /**
+     * We test failed langcode here.
+     *
+     * TODO: The languages mentioned in the data provide need to get fixed somehow within PluralizationRules.
+     *
+     * @dataProvider failingLangcodes
+     */
+    public function testFailedLangcodes($nplural, $langCodes)
+    {
+        $matrix = $this->generateTestData($langCodes);
+        $this->validateMatrix($nplural, $matrix, false);
+    }
+
+    /**
+     * @dataProvider successLangcodes
+     */
+    public function testLangcodes($nplural, $langCodes)
+    {
+        $matrix = $this->generateTestData($langCodes);
+        $this->validateMatrix($nplural, $matrix);
+    }
+
+    /**
+     * This array should contain all currently known langcodes.
+     *
+     * As it is impossible to have this ever complete we should try as hard as possible to have it almost complete.
+     *
+     * @return array
+     */
+    public function successLangcodes()
+    {
+        return [
+            ['1', ['ay', 'bo', 'cgg', 'dz', 'id', 'ja', 'jbo', 'ka', 'kk', 'km', 'ko', 'ky']],
+            ['2', ['nl', 'fr', 'en', 'de', 'de_GE', 'hy', 'hy_AM']],
+            ['3', ['be', 'bs', 'cs', 'hr']],
+            ['4', ['cy', 'mt', 'sl']],
+            ['6', ['ar']],
+        ];
+    }
+
+    /**
+     * This array should be at least empty within the near future.
+     *
+     * This both depends on a complete list trying to add above as understanding
+     * the plural rules of the current failing languages.
+     *
+     * @return array with nplural together with langcodes
+     */
+    public function failingLangcodes()
+    {
+        return [
+            ['1', ['fa']],
+            ['2', ['jbo']],
+            ['3', ['cbs']],
+            ['4', ['gd', 'kw']],
+            ['5', ['ga']],
+        ];
+    }
+
+    /**
+     * We validate only on the plural coverage. Thus the real rules is not tested.
+     *
+     * @param string $nplural       Plural expected
+     * @param array  $matrix        Containing langcodes and their plural index values
+     * @param bool   $expectSuccess
+     */
+    protected function validateMatrix($nplural, $matrix, $expectSuccess = true)
+    {
+        foreach ($matrix as $langCode => $data) {
+            $indexes = array_flip($data);
+            if ($expectSuccess) {
+                $this->assertEquals($nplural, \count($indexes), "Langcode '$langCode' has '$nplural' plural forms.");
+            } else {
+                $this->assertNotEquals((int) $nplural, \count($indexes), "Langcode '$langCode' has '$nplural' plural forms.");
+            }
+        }
+    }
+
+    protected function generateTestData($langCodes)
+    {
+        $matrix = [];
+        foreach ($langCodes as $langCode) {
+            for ($count = 0; $count < 200; ++$count) {
+                $plural = PluralizationRules::get($count, $langCode);
+                $matrix[$langCode][$count] = $plural;
+            }
+        }
+
+        return $matrix;
+    }
+}
diff --git a/vendor/symfony/translation/Tests/TranslatorCacheTest.php b/vendor/symfony/translation/Tests/TranslatorCacheTest.php
new file mode 100755
index 0000000..5d437ff
--- /dev/null
+++ b/vendor/symfony/translation/Tests/TranslatorCacheTest.php
@@ -0,0 +1,318 @@
+<?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\Translation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Resource\SelfCheckingResourceInterface;
+use Symfony\Component\Translation\Loader\ArrayLoader;
+use Symfony\Component\Translation\Loader\LoaderInterface;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Translator;
+
+class TranslatorCacheTest extends TestCase
+{
+    protected $tmpDir;
+
+    protected function setUp()
+    {
+        $this->tmpDir = sys_get_temp_dir().'/sf_translation';
+        $this->deleteTmpDir();
+    }
+
+    protected function tearDown()
+    {
+        $this->deleteTmpDir();
+    }
+
+    protected function deleteTmpDir()
+    {
+        if (!file_exists($dir = $this->tmpDir)) {
+            return;
+        }
+
+        $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->tmpDir), \RecursiveIteratorIterator::CHILD_FIRST);
+        foreach ($iterator as $path) {
+            if (preg_match('#[/\\\\]\.\.?$#', $path->__toString())) {
+                continue;
+            }
+            if ($path->isDir()) {
+                rmdir($path->__toString());
+            } else {
+                unlink($path->__toString());
+            }
+        }
+        rmdir($this->tmpDir);
+    }
+
+    /**
+     * @dataProvider runForDebugAndProduction
+     */
+    public function testThatACacheIsUsed($debug)
+    {
+        $locale = 'any_locale';
+        $format = 'some_format';
+        $msgid = 'test';
+
+        // Prime the cache
+        $translator = new Translator($locale, null, $this->tmpDir, $debug);
+        $translator->addLoader($format, new ArrayLoader());
+        $translator->addResource($format, [$msgid => 'OK'], $locale);
+        $translator->addResource($format, [$msgid.'+intl' => 'OK'], $locale, 'messages+intl-icu');
+        $translator->trans($msgid);
+        $translator->trans($msgid.'+intl', [], 'messages+intl-icu');
+
+        // Try again and see we get a valid result whilst no loader can be used
+        $translator = new Translator($locale, null, $this->tmpDir, $debug);
+        $translator->addLoader($format, $this->createFailingLoader());
+        $translator->addResource($format, [$msgid => 'OK'], $locale);
+        $translator->addResource($format, [$msgid.'+intl' => 'OK'], $locale, 'messages+intl-icu');
+        $this->assertEquals('OK', $translator->trans($msgid), '-> caching does not work in '.($debug ? 'debug' : 'production'));
+        $this->assertEquals('OK', $translator->trans($msgid.'+intl', [], 'messages+intl-icu'));
+    }
+
+    public function testCatalogueIsReloadedWhenResourcesAreNoLongerFresh()
+    {
+        /*
+         * The testThatACacheIsUsed() test showed that we don't need the loader as long as the cache
+         * is fresh.
+         *
+         * Now we add a Resource that is never fresh and make sure that the
+         * cache is discarded (the loader is called twice).
+         *
+         * We need to run this for debug=true only because in production the cache
+         * will never be revalidated.
+         */
+
+        $locale = 'any_locale';
+        $format = 'some_format';
+        $msgid = 'test';
+
+        $catalogue = new MessageCatalogue($locale, []);
+        $catalogue->addResource(new StaleResource()); // better use a helper class than a mock, because it gets serialized in the cache and re-loaded
+
+        /** @var LoaderInterface|\PHPUnit_Framework_MockObject_MockObject $loader */
+        $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock();
+        $loader
+            ->expects($this->exactly(2))
+            ->method('load')
+            ->willReturn($catalogue)
+        ;
+
+        // 1st pass
+        $translator = new Translator($locale, null, $this->tmpDir, true);
+        $translator->addLoader($format, $loader);
+        $translator->addResource($format, null, $locale);
+        $translator->trans($msgid);
+
+        // 2nd pass
+        $translator = new Translator($locale, null, $this->tmpDir, true);
+        $translator->addLoader($format, $loader);
+        $translator->addResource($format, null, $locale);
+        $translator->trans($msgid);
+    }
+
+    /**
+     * @dataProvider runForDebugAndProduction
+     */
+    public function testDifferentTranslatorsForSameLocaleDoNotOverwriteEachOthersCache($debug)
+    {
+        /*
+         * Similar to the previous test. After we used the second translator, make
+         * sure there's still a usable cache for the first one.
+         */
+
+        $locale = 'any_locale';
+        $format = 'some_format';
+        $msgid = 'test';
+
+        // Create a Translator and prime its cache
+        $translator = new Translator($locale, null, $this->tmpDir, $debug);
+        $translator->addLoader($format, new ArrayLoader());
+        $translator->addResource($format, [$msgid => 'OK'], $locale);
+        $translator->trans($msgid);
+
+        // Create another Translator with a different catalogue for the same locale
+        $translator = new Translator($locale, null, $this->tmpDir, $debug);
+        $translator->addLoader($format, new ArrayLoader());
+        $translator->addResource($format, [$msgid => 'FAIL'], $locale);
+        $translator->trans($msgid);
+
+        // Now the first translator must still have a usable cache.
+        $translator = new Translator($locale, null, $this->tmpDir, $debug);
+        $translator->addLoader($format, $this->createFailingLoader());
+        $translator->addResource($format, [$msgid => 'OK'], $locale);
+        $this->assertEquals('OK', $translator->trans($msgid), '-> the cache was overwritten by another translator instance in '.($debug ? 'debug' : 'production'));
+    }
+
+    public function testGeneratedCacheFilesAreOnlyBelongRequestedLocales()
+    {
+        $translator = new Translator('a', null, $this->tmpDir);
+        $translator->setFallbackLocales(['b']);
+        $translator->trans('bar');
+
+        $cachedFiles = glob($this->tmpDir.'/*.php');
+
+        $this->assertCount(1, $cachedFiles);
+    }
+
+    public function testDifferentCacheFilesAreUsedForDifferentSetsOfFallbackLocales()
+    {
+        /*
+         * Because the cache file contains a catalogue including all of its fallback
+         * catalogues, we must take the set of fallback locales into consideration when
+         * loading a catalogue from the cache.
+         */
+        $translator = new Translator('a', null, $this->tmpDir);
+        $translator->setFallbackLocales(['b']);
+
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', ['foo' => 'foo (a)'], 'a');
+        $translator->addResource('array', ['bar' => 'bar (b)'], 'b');
+
+        $this->assertEquals('bar (b)', $translator->trans('bar'));
+
+        // Remove fallback locale
+        $translator->setFallbackLocales([]);
+        $this->assertEquals('bar', $translator->trans('bar'));
+
+        // Use a fresh translator with no fallback locales, result should be the same
+        $translator = new Translator('a', null, $this->tmpDir);
+
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', ['foo' => 'foo (a)'], 'a');
+        $translator->addResource('array', ['bar' => 'bar (b)'], 'b');
+
+        $this->assertEquals('bar', $translator->trans('bar'));
+    }
+
+    public function testPrimaryAndFallbackCataloguesContainTheSameMessagesRegardlessOfCaching()
+    {
+        /*
+         * As a safeguard against potential BC breaks, make sure that primary and fallback
+         * catalogues (reachable via getFallbackCatalogue()) always contain the full set of
+         * messages provided by the loader. This must also be the case when these catalogues
+         * are (internally) read from a cache.
+         *
+         * Optimizations inside the translator must not change this behavior.
+         */
+
+        /*
+         * Create a translator that loads two catalogues for two different locales.
+         * The catalogues contain distinct sets of messages.
+         */
+        $translator = new Translator('a', null, $this->tmpDir);
+        $translator->setFallbackLocales(['b']);
+
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', ['foo' => 'foo (a)'], 'a');
+        $translator->addResource('array', ['foo' => 'foo (b)'], 'b');
+        $translator->addResource('array', ['bar' => 'bar (b)'], 'b');
+        $translator->addResource('array', ['baz' => 'baz (b)'], 'b', 'messages+intl-icu');
+
+        $catalogue = $translator->getCatalogue('a');
+        $this->assertFalse($catalogue->defines('bar')); // Sure, the "a" catalogue does not contain that message.
+
+        $fallback = $catalogue->getFallbackCatalogue();
+        $this->assertTrue($fallback->defines('foo')); // "foo" is present in "a" and "b"
+
+        /*
+         * Now, repeat the same test.
+         * Behind the scenes, the cache is used. But that should not matter, right?
+         */
+        $translator = new Translator('a', null, $this->tmpDir);
+        $translator->setFallbackLocales(['b']);
+
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', ['foo' => 'foo (a)'], 'a');
+        $translator->addResource('array', ['foo' => 'foo (b)'], 'b');
+        $translator->addResource('array', ['bar' => 'bar (b)'], 'b');
+        $translator->addResource('array', ['baz' => 'baz (b)'], 'b', 'messages+intl-icu');
+
+        $catalogue = $translator->getCatalogue('a');
+        $this->assertFalse($catalogue->defines('bar'));
+
+        $fallback = $catalogue->getFallbackCatalogue();
+        $this->assertTrue($fallback->defines('foo'));
+        $this->assertTrue($fallback->defines('baz', 'messages+intl-icu'));
+    }
+
+    public function testRefreshCacheWhenResourcesAreNoLongerFresh()
+    {
+        $resource = $this->getMockBuilder('Symfony\Component\Config\Resource\SelfCheckingResourceInterface')->getMock();
+        $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock();
+        $resource->method('isFresh')->willReturn(false);
+        $loader
+            ->expects($this->exactly(2))
+            ->method('load')
+            ->willReturn($this->getCatalogue('fr', [], [$resource]));
+
+        // prime the cache
+        $translator = new Translator('fr', null, $this->tmpDir, true);
+        $translator->addLoader('loader', $loader);
+        $translator->addResource('loader', 'foo', 'fr');
+        $translator->trans('foo');
+
+        // prime the cache second time
+        $translator = new Translator('fr', null, $this->tmpDir, true);
+        $translator->addLoader('loader', $loader);
+        $translator->addResource('loader', 'foo', 'fr');
+        $translator->trans('foo');
+    }
+
+    protected function getCatalogue($locale, $messages, $resources = [])
+    {
+        $catalogue = new MessageCatalogue($locale);
+        foreach ($messages as $key => $translation) {
+            $catalogue->set($key, $translation);
+        }
+        foreach ($resources as $resource) {
+            $catalogue->addResource($resource);
+        }
+
+        return $catalogue;
+    }
+
+    public function runForDebugAndProduction()
+    {
+        return [[true], [false]];
+    }
+
+    /**
+     * @return LoaderInterface
+     */
+    private function createFailingLoader()
+    {
+        $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock();
+        $loader
+            ->expects($this->never())
+            ->method('load');
+
+        return $loader;
+    }
+}
+
+class StaleResource implements SelfCheckingResourceInterface
+{
+    public function isFresh($timestamp)
+    {
+        return false;
+    }
+
+    public function getResource()
+    {
+    }
+
+    public function __toString()
+    {
+        return '';
+    }
+}
diff --git a/vendor/symfony/translation/Tests/TranslatorTest.php b/vendor/symfony/translation/Tests/TranslatorTest.php
new file mode 100755
index 0000000..51c4a0a
--- /dev/null
+++ b/vendor/symfony/translation/Tests/TranslatorTest.php
@@ -0,0 +1,612 @@
+<?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\Translation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Loader\ArrayLoader;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Translator;
+
+class TranslatorTest extends TestCase
+{
+    /**
+     * @dataProvider      getInvalidLocalesTests
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
+     */
+    public function testConstructorInvalidLocale($locale)
+    {
+        new Translator($locale);
+    }
+
+    /**
+     * @dataProvider getValidLocalesTests
+     */
+    public function testConstructorValidLocale($locale)
+    {
+        $translator = new Translator($locale);
+
+        $this->assertEquals($locale, $translator->getLocale());
+    }
+
+    public function testConstructorWithoutLocale()
+    {
+        $translator = new Translator(null);
+
+        $this->assertNull($translator->getLocale());
+    }
+
+    public function testSetGetLocale()
+    {
+        $translator = new Translator('en');
+
+        $this->assertEquals('en', $translator->getLocale());
+
+        $translator->setLocale('fr');
+        $this->assertEquals('fr', $translator->getLocale());
+    }
+
+    /**
+     * @dataProvider      getInvalidLocalesTests
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
+     */
+    public function testSetInvalidLocale($locale)
+    {
+        $translator = new Translator('fr');
+        $translator->setLocale($locale);
+    }
+
+    /**
+     * @dataProvider getValidLocalesTests
+     */
+    public function testSetValidLocale($locale)
+    {
+        $translator = new Translator($locale);
+        $translator->setLocale($locale);
+
+        $this->assertEquals($locale, $translator->getLocale());
+    }
+
+    public function testGetCatalogue()
+    {
+        $translator = new Translator('en');
+
+        $this->assertEquals(new MessageCatalogue('en'), $translator->getCatalogue());
+
+        $translator->setLocale('fr');
+        $this->assertEquals(new MessageCatalogue('fr'), $translator->getCatalogue('fr'));
+    }
+
+    public function testGetCatalogueReturnsConsolidatedCatalogue()
+    {
+        /*
+         * This will be useful once we refactor so that different domains will be loaded lazily (on-demand).
+         * In that case, getCatalogue() will probably have to load all missing domains in order to return
+         * one complete catalogue.
+         */
+
+        $locale = 'whatever';
+        $translator = new Translator($locale);
+        $translator->addLoader('loader-a', new ArrayLoader());
+        $translator->addLoader('loader-b', new ArrayLoader());
+        $translator->addResource('loader-a', ['foo' => 'foofoo'], $locale, 'domain-a');
+        $translator->addResource('loader-b', ['bar' => 'foobar'], $locale, 'domain-b');
+
+        /*
+         * Test that we get a single catalogue comprising messages
+         * from different loaders and different domains
+         */
+        $catalogue = $translator->getCatalogue($locale);
+        $this->assertTrue($catalogue->defines('foo', 'domain-a'));
+        $this->assertTrue($catalogue->defines('bar', 'domain-b'));
+    }
+
+    public function testSetFallbackLocales()
+    {
+        $translator = new Translator('en');
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', ['foo' => 'foofoo'], 'en');
+        $translator->addResource('array', ['bar' => 'foobar'], 'fr');
+
+        // force catalogue loading
+        $translator->trans('bar');
+
+        $translator->setFallbackLocales(['fr']);
+        $this->assertEquals('foobar', $translator->trans('bar'));
+    }
+
+    public function testSetFallbackLocalesMultiple()
+    {
+        $translator = new Translator('en');
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', ['foo' => 'foo (en)'], 'en');
+        $translator->addResource('array', ['bar' => 'bar (fr)'], 'fr');
+
+        // force catalogue loading
+        $translator->trans('bar');
+
+        $translator->setFallbackLocales(['fr_FR', 'fr']);
+        $this->assertEquals('bar (fr)', $translator->trans('bar'));
+    }
+
+    /**
+     * @dataProvider      getInvalidLocalesTests
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
+     */
+    public function testSetFallbackInvalidLocales($locale)
+    {
+        $translator = new Translator('fr');
+        $translator->setFallbackLocales(['fr', $locale]);
+    }
+
+    /**
+     * @dataProvider getValidLocalesTests
+     */
+    public function testSetFallbackValidLocales($locale)
+    {
+        $translator = new Translator($locale);
+        $translator->setFallbackLocales(['fr', $locale]);
+        // no assertion. this method just asserts that no exception is thrown
+        $this->addToAssertionCount(1);
+    }
+
+    public function testTransWithFallbackLocale()
+    {
+        $translator = new Translator('fr_FR');
+        $translator->setFallbackLocales(['en']);
+
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', ['bar' => 'foobar'], 'en');
+
+        $this->assertEquals('foobar', $translator->trans('bar'));
+    }
+
+    /**
+     * @dataProvider      getInvalidLocalesTests
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
+     */
+    public function testAddResourceInvalidLocales($locale)
+    {
+        $translator = new Translator('fr');
+        $translator->addResource('array', ['foo' => 'foofoo'], $locale);
+    }
+
+    /**
+     * @dataProvider getValidLocalesTests
+     */
+    public function testAddResourceValidLocales($locale)
+    {
+        $translator = new Translator('fr');
+        $translator->addResource('array', ['foo' => 'foofoo'], $locale);
+        // no assertion. this method just asserts that no exception is thrown
+        $this->addToAssertionCount(1);
+    }
+
+    public function testAddResourceAfterTrans()
+    {
+        $translator = new Translator('fr');
+        $translator->addLoader('array', new ArrayLoader());
+
+        $translator->setFallbackLocales(['en']);
+
+        $translator->addResource('array', ['foo' => 'foofoo'], 'en');
+        $this->assertEquals('foofoo', $translator->trans('foo'));
+
+        $translator->addResource('array', ['bar' => 'foobar'], 'en');
+        $this->assertEquals('foobar', $translator->trans('bar'));
+    }
+
+    /**
+     * @dataProvider      getTransFileTests
+     * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException
+     */
+    public function testTransWithoutFallbackLocaleFile($format, $loader)
+    {
+        $loaderClass = 'Symfony\\Component\\Translation\\Loader\\'.$loader;
+        $translator = new Translator('en');
+        $translator->addLoader($format, new $loaderClass());
+        $translator->addResource($format, __DIR__.'/fixtures/non-existing', 'en');
+        $translator->addResource($format, __DIR__.'/fixtures/resources.'.$format, 'en');
+
+        // force catalogue loading
+        $translator->trans('foo');
+    }
+
+    /**
+     * @dataProvider getTransFileTests
+     */
+    public function testTransWithFallbackLocaleFile($format, $loader)
+    {
+        $loaderClass = 'Symfony\\Component\\Translation\\Loader\\'.$loader;
+        $translator = new Translator('en_GB');
+        $translator->addLoader($format, new $loaderClass());
+        $translator->addResource($format, __DIR__.'/fixtures/non-existing', 'en_GB');
+        $translator->addResource($format, __DIR__.'/fixtures/resources.'.$format, 'en', 'resources');
+
+        $this->assertEquals('bar', $translator->trans('foo', [], 'resources'));
+    }
+
+    public function testTransWithIcuFallbackLocale()
+    {
+        $translator = new Translator('en_GB');
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', ['foo' => 'foofoo'], 'en_GB');
+        $translator->addResource('array', ['bar' => 'foobar'], 'en_001');
+        $translator->addResource('array', ['baz' => 'foobaz'], 'en');
+        $this->assertSame('foofoo', $translator->trans('foo'));
+        $this->assertSame('foobar', $translator->trans('bar'));
+        $this->assertSame('foobaz', $translator->trans('baz'));
+    }
+
+    public function testTransWithIcuVariantFallbackLocale()
+    {
+        $translator = new Translator('en_GB_scouse');
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', ['foo' => 'foofoo'], 'en_GB_scouse');
+        $translator->addResource('array', ['bar' => 'foobar'], 'en_GB');
+        $translator->addResource('array', ['baz' => 'foobaz'], 'en_001');
+        $translator->addResource('array', ['qux' => 'fooqux'], 'en');
+        $this->assertSame('foofoo', $translator->trans('foo'));
+        $this->assertSame('foobar', $translator->trans('bar'));
+        $this->assertSame('foobaz', $translator->trans('baz'));
+        $this->assertSame('fooqux', $translator->trans('qux'));
+    }
+
+    public function testTransWithIcuRootFallbackLocale()
+    {
+        $translator = new Translator('az_Cyrl');
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', ['foo' => 'foofoo'], 'az_Cyrl');
+        $translator->addResource('array', ['bar' => 'foobar'], 'az');
+        $this->assertSame('foofoo', $translator->trans('foo'));
+        $this->assertSame('bar', $translator->trans('bar'));
+    }
+
+    public function testTransWithFallbackLocaleBis()
+    {
+        $translator = new Translator('en_US');
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', ['foo' => 'foofoo'], 'en_US');
+        $translator->addResource('array', ['bar' => 'foobar'], 'en');
+        $this->assertEquals('foobar', $translator->trans('bar'));
+    }
+
+    public function testTransWithFallbackLocaleTer()
+    {
+        $translator = new Translator('fr_FR');
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', ['foo' => 'foo (en_US)'], 'en_US');
+        $translator->addResource('array', ['bar' => 'bar (en)'], 'en');
+
+        $translator->setFallbackLocales(['en_US', 'en']);
+
+        $this->assertEquals('foo (en_US)', $translator->trans('foo'));
+        $this->assertEquals('bar (en)', $translator->trans('bar'));
+    }
+
+    public function testTransNonExistentWithFallback()
+    {
+        $translator = new Translator('fr');
+        $translator->setFallbackLocales(['en']);
+        $translator->addLoader('array', new ArrayLoader());
+        $this->assertEquals('non-existent', $translator->trans('non-existent'));
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Translation\Exception\RuntimeException
+     */
+    public function testWhenAResourceHasNoRegisteredLoader()
+    {
+        $translator = new Translator('en');
+        $translator->addResource('array', ['foo' => 'foofoo'], 'en');
+
+        $translator->trans('foo');
+    }
+
+    public function testNestedFallbackCatalogueWhenUsingMultipleLocales()
+    {
+        $translator = new Translator('fr');
+        $translator->setFallbackLocales(['ru', 'en']);
+
+        $translator->getCatalogue('fr');
+
+        $this->assertNotNull($translator->getCatalogue('ru')->getFallbackCatalogue());
+    }
+
+    public function testFallbackCatalogueResources()
+    {
+        $translator = new Translator('en_GB');
+        $translator->addLoader('yml', new \Symfony\Component\Translation\Loader\YamlFileLoader());
+        $translator->addResource('yml', __DIR__.'/fixtures/empty.yml', 'en_GB');
+        $translator->addResource('yml', __DIR__.'/fixtures/resources.yml', 'en');
+
+        // force catalogue loading
+        $this->assertEquals('bar', $translator->trans('foo', []));
+
+        $resources = $translator->getCatalogue('en')->getResources();
+        $this->assertCount(1, $resources);
+        $this->assertContains(__DIR__.\DIRECTORY_SEPARATOR.'fixtures'.\DIRECTORY_SEPARATOR.'resources.yml', $resources);
+
+        $resources = $translator->getCatalogue('en_GB')->getResources();
+        $this->assertCount(2, $resources);
+        $this->assertContains(__DIR__.\DIRECTORY_SEPARATOR.'fixtures'.\DIRECTORY_SEPARATOR.'empty.yml', $resources);
+        $this->assertContains(__DIR__.\DIRECTORY_SEPARATOR.'fixtures'.\DIRECTORY_SEPARATOR.'resources.yml', $resources);
+    }
+
+    /**
+     * @dataProvider getTransTests
+     */
+    public function testTrans($expected, $id, $translation, $parameters, $locale, $domain)
+    {
+        $translator = new Translator('en');
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', [(string) $id => $translation], $locale, $domain);
+
+        $this->assertEquals($expected, $translator->trans($id, $parameters, $domain, $locale));
+    }
+
+    /**
+     * @dataProvider      getInvalidLocalesTests
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
+     */
+    public function testTransInvalidLocale($locale)
+    {
+        $translator = new Translator('en');
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', ['foo' => 'foofoo'], 'en');
+
+        $translator->trans('foo', [], '', $locale);
+    }
+
+    /**
+     * @dataProvider      getValidLocalesTests
+     */
+    public function testTransValidLocale($locale)
+    {
+        $translator = new Translator($locale);
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', ['test' => 'OK'], $locale);
+
+        $this->assertEquals('OK', $translator->trans('test'));
+        $this->assertEquals('OK', $translator->trans('test', [], null, $locale));
+    }
+
+    /**
+     * @dataProvider getFlattenedTransTests
+     */
+    public function testFlattenedTrans($expected, $messages, $id)
+    {
+        $translator = new Translator('en');
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', $messages, 'fr', '');
+
+        $this->assertEquals($expected, $translator->trans($id, [], '', 'fr'));
+    }
+
+    /**
+     * @dataProvider getTransChoiceTests
+     * @group legacy
+     */
+    public function testTransChoice($expected, $id, $translation, $number, $parameters, $locale, $domain)
+    {
+        $translator = new Translator('en');
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', [(string) $id => $translation], $locale, $domain);
+
+        $this->assertEquals($expected, $translator->transChoice($id, $number, $parameters, $domain, $locale));
+    }
+
+    /**
+     * @dataProvider      getInvalidLocalesTests
+     * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException
+     * @group legacy
+     */
+    public function testTransChoiceInvalidLocale($locale)
+    {
+        $translator = new Translator('en');
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', ['foo' => 'foofoo'], 'en');
+
+        $translator->transChoice('foo', 1, [], '', $locale);
+    }
+
+    /**
+     * @dataProvider      getValidLocalesTests
+     * @group legacy
+     */
+    public function testTransChoiceValidLocale($locale)
+    {
+        $translator = new Translator('en');
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', ['foo' => 'foofoo'], 'en');
+
+        $translator->transChoice('foo', 1, [], '', $locale);
+        // no assertion. this method just asserts that no exception is thrown
+        $this->addToAssertionCount(1);
+    }
+
+    public function getTransFileTests()
+    {
+        return [
+            ['csv', 'CsvFileLoader'],
+            ['ini', 'IniFileLoader'],
+            ['mo', 'MoFileLoader'],
+            ['po', 'PoFileLoader'],
+            ['php', 'PhpFileLoader'],
+            ['ts', 'QtFileLoader'],
+            ['xlf', 'XliffFileLoader'],
+            ['yml', 'YamlFileLoader'],
+            ['json', 'JsonFileLoader'],
+        ];
+    }
+
+    public function getTransTests()
+    {
+        return [
+            ['Symfony est super !', 'Symfony is great!', 'Symfony est super !', [], 'fr', ''],
+            ['Symfony est awesome !', 'Symfony is %what%!', 'Symfony est %what% !', ['%what%' => 'awesome'], 'fr', ''],
+            ['Symfony est super !', new StringClass('Symfony is great!'), 'Symfony est super !', [], 'fr', ''],
+        ];
+    }
+
+    public function getFlattenedTransTests()
+    {
+        $messages = [
+            'symfony' => [
+                'is' => [
+                    'great' => 'Symfony est super!',
+                ],
+            ],
+            'foo' => [
+                'bar' => [
+                    'baz' => 'Foo Bar Baz',
+                ],
+                'baz' => 'Foo Baz',
+            ],
+        ];
+
+        return [
+            ['Symfony est super!', $messages, 'symfony.is.great'],
+            ['Foo Bar Baz', $messages, 'foo.bar.baz'],
+            ['Foo Baz', $messages, 'foo.baz'],
+        ];
+    }
+
+    public function getTransChoiceTests()
+    {
+        return [
+            ['Il y a 0 pomme', '{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, [], 'fr', ''],
+            ['Il y a 1 pomme', '{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 1, [], 'fr', ''],
+            ['Il y a 10 pommes', '{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 10, [], 'fr', ''],
+
+            ['Il y a 0 pomme', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 0, [], 'fr', ''],
+            ['Il y a 1 pomme', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 1, [], 'fr', ''],
+            ['Il y a 10 pommes', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 10, [], 'fr', ''],
+
+            ['Il y a 0 pomme', 'one: There is one apple|more: There is %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 0, [], 'fr', ''],
+            ['Il y a 1 pomme', 'one: There is one apple|more: There is %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 1, [], 'fr', ''],
+            ['Il y a 10 pommes', 'one: There is one apple|more: There is %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 10, [], 'fr', ''],
+
+            ['Il n\'y a aucune pomme', '{0} There are no apples|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 0, [], 'fr', ''],
+            ['Il y a 1 pomme', '{0} There are no apples|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 1, [], 'fr', ''],
+            ['Il y a 10 pommes', '{0} There are no apples|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 10, [], 'fr', ''],
+
+            ['Il y a 0 pomme', new StringClass('{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples'), '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, [], 'fr', ''],
+
+            // Override %count% with a custom value
+            ['Il y a quelques pommes', 'one: There is one apple|more: There are %count% apples', 'one: Il y a %count% pomme|more: Il y a quelques pommes', 2, ['%count%' => 'quelques'], 'fr', ''],
+        ];
+    }
+
+    public function getInvalidLocalesTests()
+    {
+        return [
+            ['fr FR'],
+            ['français'],
+            ['fr+en'],
+            ['utf#8'],
+            ['fr&en'],
+            ['fr~FR'],
+            [' fr'],
+            ['fr '],
+            ['fr*'],
+            ['fr/FR'],
+            ['fr\\FR'],
+        ];
+    }
+
+    public function getValidLocalesTests()
+    {
+        return [
+            [''],
+            [null],
+            ['fr'],
+            ['francais'],
+            ['FR'],
+            ['frFR'],
+            ['fr-FR'],
+            ['fr_FR'],
+            ['fr.FR'],
+            ['fr-FR.UTF8'],
+            ['sr@latin'],
+        ];
+    }
+
+    /**
+     * @requires extension intl
+     */
+    public function testIntlFormattedDomain()
+    {
+        $translator = new Translator('en');
+        $translator->addLoader('array', new ArrayLoader());
+
+        $translator->addResource('array', ['some_message' => 'Hello %name%'], 'en');
+        $this->assertSame('Hello Bob', $translator->trans('some_message', ['%name%' => 'Bob']));
+
+        $translator->addResource('array', ['some_message' => 'Hi {name}'], 'en', 'messages+intl-icu');
+        $this->assertSame('Hi Bob', $translator->trans('some_message', ['%name%' => 'Bob']));
+    }
+
+    /**
+     * @group legacy
+     */
+    public function testTransChoiceFallback()
+    {
+        $translator = new Translator('ru');
+        $translator->setFallbackLocales(['en']);
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', ['some_message2' => 'one thing|%count% things'], 'en');
+
+        $this->assertEquals('10 things', $translator->transChoice('some_message2', 10, ['%count%' => 10]));
+    }
+
+    /**
+     * @group legacy
+     */
+    public function testTransChoiceFallbackBis()
+    {
+        $translator = new Translator('ru');
+        $translator->setFallbackLocales(['en_US', 'en']);
+        $translator->addLoader('array', new ArrayLoader());
+        $translator->addResource('array', ['some_message2' => 'one thing|%count% things'], 'en_US');
+
+        $this->assertEquals('10 things', $translator->transChoice('some_message2', 10, ['%count%' => 10]));
+    }
+
+    /**
+     * @group legacy
+     */
+    public function testTransChoiceFallbackWithNoTranslation()
+    {
+        $translator = new Translator('ru');
+        $translator->setFallbackLocales(['en']);
+        $translator->addLoader('array', new ArrayLoader());
+
+        // consistent behavior with Translator::trans(), which returns the string
+        // unchanged if it can't be found
+        $this->assertEquals('some_message2', $translator->transChoice('some_message2', 10, ['%count%' => 10]));
+    }
+}
+
+class StringClass
+{
+    protected $str;
+
+    public function __construct($str)
+    {
+        $this->str = $str;
+    }
+
+    public function __toString()
+    {
+        return $this->str;
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Util/ArrayConverterTest.php b/vendor/symfony/translation/Tests/Util/ArrayConverterTest.php
new file mode 100755
index 0000000..b033541
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Util/ArrayConverterTest.php
@@ -0,0 +1,74 @@
+<?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\Translation\Tests\Util;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Util\ArrayConverter;
+
+class ArrayConverterTest extends TestCase
+{
+    /**
+     * @dataProvider messagesData
+     */
+    public function testDump($input, $expectedOutput)
+    {
+        $this->assertEquals($expectedOutput, ArrayConverter::expandToTree($input));
+    }
+
+    public function messagesData()
+    {
+        return [
+            [
+                // input
+                [
+                    'foo1' => 'bar',
+                    'foo.bar' => 'value',
+                ],
+                // expected output
+                [
+                    'foo1' => 'bar',
+                    'foo' => ['bar' => 'value'],
+                ],
+            ],
+            [
+                // input
+                [
+                    'foo.bar' => 'value1',
+                    'foo.bar.test' => 'value2',
+                ],
+                // expected output
+                [
+                    'foo' => [
+                        'bar' => 'value1',
+                        'bar.test' => 'value2',
+                    ],
+                ],
+            ],
+            [
+                // input
+                [
+                    'foo.level2.level3.level4' => 'value1',
+                    'foo.level2' => 'value2',
+                    'foo.bar' => 'value3',
+                ],
+                // expected output
+                [
+                    'foo' => [
+                        'level2' => 'value2',
+                        'level2.level3.level4' => 'value1',
+                        'bar' => 'value3',
+                    ],
+                ],
+            ],
+        ];
+    }
+}
diff --git a/vendor/symfony/translation/Tests/Writer/TranslationWriterTest.php b/vendor/symfony/translation/Tests/Writer/TranslationWriterTest.php
new file mode 100755
index 0000000..d3b6754
--- /dev/null
+++ b/vendor/symfony/translation/Tests/Writer/TranslationWriterTest.php
@@ -0,0 +1,69 @@
+<?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\Translation\Tests\Writer;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\Dumper\DumperInterface;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Writer\TranslationWriter;
+
+class TranslationWriterTest extends TestCase
+{
+    public function testWrite()
+    {
+        $dumper = $this->getMockBuilder('Symfony\Component\Translation\Dumper\DumperInterface')->getMock();
+        $dumper
+            ->expects($this->once())
+            ->method('dump');
+
+        $writer = new TranslationWriter();
+        $writer->addDumper('test', $dumper);
+        $writer->write(new MessageCatalogue('en'), 'test');
+    }
+
+    /**
+     * @group legacy
+     */
+    public function testDisableBackup()
+    {
+        $nonBackupDumper = new NonBackupDumper();
+        $backupDumper = new BackupDumper();
+
+        $writer = new TranslationWriter();
+        $writer->addDumper('non_backup', $nonBackupDumper);
+        $writer->addDumper('backup', $backupDumper);
+        $writer->disableBackup();
+
+        $this->assertFalse($backupDumper->backup, 'backup can be disabled if setBackup() method does exist');
+    }
+}
+
+class NonBackupDumper implements DumperInterface
+{
+    public function dump(MessageCatalogue $messages, $options = [])
+    {
+    }
+}
+
+class BackupDumper implements DumperInterface
+{
+    public $backup = true;
+
+    public function dump(MessageCatalogue $messages, $options = [])
+    {
+    }
+
+    public function setBackup($backup)
+    {
+        $this->backup = $backup;
+    }
+}
diff --git a/vendor/symfony/translation/Tests/fixtures/empty-translation.mo b/vendor/symfony/translation/Tests/fixtures/empty-translation.mo
new file mode 100755
index 0000000..ed01000
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/empty-translation.mo
Binary files differ
diff --git a/vendor/symfony/translation/Tests/fixtures/empty-translation.po b/vendor/symfony/translation/Tests/fixtures/empty-translation.po
new file mode 100755
index 0000000..ff6f22a
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/empty-translation.po
@@ -0,0 +1,3 @@
+msgid "foo"
+msgstr ""
+
diff --git a/vendor/symfony/translation/Tests/fixtures/empty.csv b/vendor/symfony/translation/Tests/fixtures/empty.csv
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/empty.csv
diff --git a/vendor/symfony/translation/Tests/fixtures/empty.ini b/vendor/symfony/translation/Tests/fixtures/empty.ini
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/empty.ini
diff --git a/vendor/symfony/translation/Tests/fixtures/empty.json b/vendor/symfony/translation/Tests/fixtures/empty.json
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/empty.json
diff --git a/vendor/symfony/translation/Tests/fixtures/empty.mo b/vendor/symfony/translation/Tests/fixtures/empty.mo
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/empty.mo
diff --git a/vendor/symfony/translation/Tests/fixtures/empty.po b/vendor/symfony/translation/Tests/fixtures/empty.po
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/empty.po
diff --git a/vendor/symfony/translation/Tests/fixtures/empty.xlf b/vendor/symfony/translation/Tests/fixtures/empty.xlf
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/empty.xlf
diff --git a/vendor/symfony/translation/Tests/fixtures/empty.yml b/vendor/symfony/translation/Tests/fixtures/empty.yml
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/empty.yml
diff --git a/vendor/symfony/translation/Tests/fixtures/encoding.xlf b/vendor/symfony/translation/Tests/fixtures/encoding.xlf
new file mode 100755
index 0000000..0a88f92
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/encoding.xlf
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
+  <file source-language="en" datatype="plaintext" original="file.ext">
+    <body>
+      <trans-unit id="1" resname="foo">
+        <source>foo</source>
+        <target>b�r</target>
+        <note>b�z</note>
+      </trans-unit>
+      <trans-unit id="2" resname="bar">
+        <source>bar</source>
+        <target>f��</target>
+      </trans-unit>
+    </body>
+  </file>
+</xliff>
diff --git a/vendor/symfony/translation/Tests/fixtures/escaped-id-plurals.po b/vendor/symfony/translation/Tests/fixtures/escaped-id-plurals.po
new file mode 100755
index 0000000..c412aa2
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/escaped-id-plurals.po
@@ -0,0 +1,10 @@
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: en\n"
+
+msgid "escaped \"foo\""
+msgid_plural "escaped \"foos\""
+msgstr[0] "escaped \"bar\""
+msgstr[1] "escaped \"bars\""
diff --git a/vendor/symfony/translation/Tests/fixtures/escaped-id.po b/vendor/symfony/translation/Tests/fixtures/escaped-id.po
new file mode 100755
index 0000000..308eadd
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/escaped-id.po
@@ -0,0 +1,8 @@
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: en\n"
+
+msgid "escaped \"foo\""
+msgstr "escaped \"bar\""
diff --git a/vendor/symfony/translation/Tests/fixtures/extractor/resource.format.engine b/vendor/symfony/translation/Tests/fixtures/extractor/resource.format.engine
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/extractor/resource.format.engine
diff --git a/vendor/symfony/translation/Tests/fixtures/extractor/this.is.a.template.format.engine b/vendor/symfony/translation/Tests/fixtures/extractor/this.is.a.template.format.engine
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/extractor/this.is.a.template.format.engine
diff --git a/vendor/symfony/translation/Tests/fixtures/extractor/translation.html.php b/vendor/symfony/translation/Tests/fixtures/extractor/translation.html.php
new file mode 100755
index 0000000..5085eab
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/extractor/translation.html.php
@@ -0,0 +1,59 @@
+This template is used for translation message extraction tests
+<?php echo $view['translator']->trans('single-quoted key'); ?>
+<?php echo $view['translator']->trans('double-quoted key'); ?>
+<?php echo $view['translator']->trans(<<<EOF
+heredoc key
+EOF
+); ?>
+<?php echo $view['translator']->trans(<<<'EOF'
+nowdoc key
+EOF
+); ?>
+<?php echo $view['translator']->trans(
+    "double-quoted key with whitespace and escaped \$\n\" sequences"
+); ?>
+<?php echo $view['translator']->trans(
+    'single-quoted key with whitespace and nonescaped \$\n\' sequences'
+); ?>
+<?php echo $view['translator']->trans(<<<EOF
+heredoc key with whitespace and escaped \$\n sequences
+EOF
+); ?>
+<?php echo $view['translator']->trans(<<<'EOF'
+nowdoc key with whitespace and nonescaped \$\n sequences
+EOF
+); ?>
+
+<?php echo $view['translator']->trans('single-quoted key with "quote mark at the end"'); ?>
+
+<?php echo $view['translator']->transChoice(
+    '{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples',
+    10,
+    ['%count%' => 10]
+); ?>
+
+<?php echo $view['translator']->trans('concatenated'.' message'.<<<EOF
+ with heredoc
+EOF
+.<<<'EOF'
+ and nowdoc
+EOF
+); ?>
+
+<?php echo $view['translator']->trans('other-domain-test-no-params-short-array', [], 'not_messages'); ?>
+
+<?php echo $view['translator']->trans('other-domain-test-no-params-long-array', [], 'not_messages'); ?>
+
+<?php echo $view['translator']->trans('other-domain-test-params-short-array', ['foo' => 'bar'], 'not_messages'); ?>
+
+<?php echo $view['translator']->trans('other-domain-test-params-long-array', ['foo' => 'bar'], 'not_messages'); ?>
+
+<?php echo $view['translator']->transChoice('other-domain-test-trans-choice-short-array-%count%', 10, ['%count%' => 10], 'not_messages'); ?>
+
+<?php echo $view['translator']->transChoice('other-domain-test-trans-choice-long-array-%count%', 10, ['%count%' => 10], 'not_messages'); ?>
+
+<?php echo $view['translator']->trans('typecast', ['a' => (int) '123'], 'not_messages'); ?>
+<?php echo $view['translator']->transChoice('msg1', 10 + 1, [], 'not_messages'); ?>
+<?php echo $view['translator']->transChoice('msg2', ceil(4.5), [], 'not_messages'); ?>
+
+<?php echo $view['translator']->trans('default domain', [], null); ?>
diff --git a/vendor/symfony/translation/Tests/fixtures/fuzzy-translations.po b/vendor/symfony/translation/Tests/fixtures/fuzzy-translations.po
new file mode 100755
index 0000000..04d4047
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/fuzzy-translations.po
@@ -0,0 +1,10 @@
+#, php-format
+msgid "foo1"
+msgstr "bar1"
+
+#, fuzzy, php-format
+msgid "foo2"
+msgstr "fuzzy bar2"
+
+msgid "foo3"
+msgstr "bar3"
diff --git a/vendor/symfony/translation/Tests/fixtures/invalid-xml-resources.xlf b/vendor/symfony/translation/Tests/fixtures/invalid-xml-resources.xlf
new file mode 100755
index 0000000..7bf6c98
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/invalid-xml-resources.xlf
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="1">
+                <source>foo</source>
+                <target>bar
+            </trans-unit>
+            <trans-unit id="2">
+                <source>extra</source>
+            </trans-unit>
+            <trans-unit id="3">
+                <source>key</source>
+                <target></target>
+            </trans-unit>
+            <trans-unit id="4">
+                <source>test</source>
+                <target>with</target>
+                <note>note</note>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
diff --git a/vendor/symfony/translation/Tests/fixtures/malformed.json b/vendor/symfony/translation/Tests/fixtures/malformed.json
new file mode 100755
index 0000000..4563ec6
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/malformed.json
@@ -0,0 +1,3 @@
+{
+    "foo" "bar"
+}
\ No newline at end of file
diff --git a/vendor/symfony/translation/Tests/fixtures/messages.yml b/vendor/symfony/translation/Tests/fixtures/messages.yml
new file mode 100755
index 0000000..d4f82d7
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/messages.yml
@@ -0,0 +1,3 @@
+foo:
+    bar1: value1
+    bar2: value2
diff --git a/vendor/symfony/translation/Tests/fixtures/messages_linear.yml b/vendor/symfony/translation/Tests/fixtures/messages_linear.yml
new file mode 100755
index 0000000..6c1687d
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/messages_linear.yml
@@ -0,0 +1,2 @@
+foo.bar1: value1
+foo.bar2: value2
diff --git a/vendor/symfony/translation/Tests/fixtures/non-valid.xlf b/vendor/symfony/translation/Tests/fixtures/non-valid.xlf
new file mode 100755
index 0000000..734fc97
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/non-valid.xlf
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit>
+                <source>foo</source>
+                <target>bar</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
diff --git a/vendor/symfony/translation/Tests/fixtures/non-valid.yml b/vendor/symfony/translation/Tests/fixtures/non-valid.yml
new file mode 100755
index 0000000..257cc56
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/non-valid.yml
@@ -0,0 +1 @@
+foo
diff --git a/vendor/symfony/translation/Tests/fixtures/plurals.mo b/vendor/symfony/translation/Tests/fixtures/plurals.mo
new file mode 100755
index 0000000..6445e77
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/plurals.mo
Binary files differ
diff --git a/vendor/symfony/translation/Tests/fixtures/plurals.po b/vendor/symfony/translation/Tests/fixtures/plurals.po
new file mode 100755
index 0000000..439c41a
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/plurals.po
@@ -0,0 +1,5 @@
+msgid "foo"
+msgid_plural "foos"
+msgstr[0] "bar"
+msgstr[1] "bars"
+
diff --git a/vendor/symfony/translation/Tests/fixtures/resname.xlf b/vendor/symfony/translation/Tests/fixtures/resname.xlf
new file mode 100755
index 0000000..4fa5c00
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resname.xlf
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
+  <file source-language="en" datatype="plaintext" original="file.ext">
+    <body>
+      <trans-unit id="1" resname="foo">
+        <source></source>
+        <target>bar</target>
+      </trans-unit>
+      <trans-unit id="2" resname="bar">
+        <source>bar source</source>
+        <target>baz</target>
+      </trans-unit>
+      <trans-unit id="3">
+        <source>baz</source>
+        <target>foo</target>
+      </trans-unit>
+      <trans-unit id="4" resname="qux">
+        <source>qux source</source>
+      </trans-unit>
+    </body>
+  </file>
+</xliff>
diff --git a/vendor/symfony/translation/Tests/fixtures/resourcebundle/corrupted/resources.dat b/vendor/symfony/translation/Tests/fixtures/resourcebundle/corrupted/resources.dat
new file mode 100755
index 0000000..391250c
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resourcebundle/corrupted/resources.dat
@@ -0,0 +1 @@
+XXX
\ No newline at end of file
diff --git a/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/en.res b/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/en.res
new file mode 100755
index 0000000..1fc1436
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/en.res
Binary files differ
diff --git a/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/en.txt b/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/en.txt
new file mode 100755
index 0000000..3d9e9ea
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/en.txt
@@ -0,0 +1,3 @@
+en{
+    symfony{"Symfony is great"}
+}
\ No newline at end of file
diff --git a/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/fr.res b/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/fr.res
new file mode 100755
index 0000000..f584160
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/fr.res
Binary files differ
diff --git a/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/fr.txt b/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/fr.txt
new file mode 100755
index 0000000..182d0a0
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/fr.txt
@@ -0,0 +1,3 @@
+fr{
+    symfony{"Symfony est génial"}
+}
\ No newline at end of file
diff --git a/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/packagelist.txt b/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/packagelist.txt
new file mode 100755
index 0000000..c5783ed
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/packagelist.txt
@@ -0,0 +1,2 @@
+en.res
+fr.res
diff --git a/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/resources.dat b/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/resources.dat
new file mode 100755
index 0000000..563b0ea
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/resources.dat
Binary files differ
diff --git a/vendor/symfony/translation/Tests/fixtures/resourcebundle/res/en.res b/vendor/symfony/translation/Tests/fixtures/resourcebundle/res/en.res
new file mode 100755
index 0000000..ad894a9
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resourcebundle/res/en.res
Binary files differ
diff --git a/vendor/symfony/translation/Tests/fixtures/resources-2.0+intl-icu.xlf b/vendor/symfony/translation/Tests/fixtures/resources-2.0+intl-icu.xlf
new file mode 100755
index 0000000..6294f16
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resources-2.0+intl-icu.xlf
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" version="2.0" srcLang="fr-FR" trgLang="en-US">
+  <file id="messages.en_US">
+    <unit id="LCa0a2j" name="foo">
+      <segment>
+        <source>foo</source>
+        <target>bar</target>
+      </segment>
+    </unit>
+  </file>
+</xliff>
diff --git a/vendor/symfony/translation/Tests/fixtures/resources-2.0-clean.xlf b/vendor/symfony/translation/Tests/fixtures/resources-2.0-clean.xlf
new file mode 100755
index 0000000..efa69b2
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resources-2.0-clean.xlf
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" version="2.0" srcLang="fr-FR" trgLang="en-US">
+  <file id="messages.en_US">
+    <unit id="LCa0a2j" name="foo">
+      <segment>
+        <source>foo</source>
+        <target>bar</target>
+      </segment>
+    </unit>
+    <unit id="LHDhK3o" name="key">
+      <segment>
+        <source>key</source>
+        <target order="1"></target>
+      </segment>
+    </unit>
+    <unit id="2DA_bnh" name="key.with.cdata">
+      <segment>
+        <source>key.with.cdata</source>
+        <target><![CDATA[<source> & <target>]]></target>
+      </segment>
+    </unit>
+  </file>
+</xliff>
diff --git a/vendor/symfony/translation/Tests/fixtures/resources-2.0-multi-segment-unit.xlf b/vendor/symfony/translation/Tests/fixtures/resources-2.0-multi-segment-unit.xlf
new file mode 100755
index 0000000..d0dc2a8
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resources-2.0-multi-segment-unit.xlf
@@ -0,0 +1,17 @@
+<xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" version="2.0" srcLang="en-US" trgLang="en-US">
+    <file id="f1">
+        <unit id="1">
+            <notes>
+                <note category="processed">true</note>
+            </notes>
+            <segment id="id-foo">
+                <source>foo</source>
+                <target>foo (translated)</target>
+            </segment>
+            <segment id="id-bar">
+                <source>bar</source>
+                <target>bar (translated)</target>
+            </segment>
+        </unit>
+    </file>
+</xliff>
diff --git a/vendor/symfony/translation/Tests/fixtures/resources-2.0.xlf b/vendor/symfony/translation/Tests/fixtures/resources-2.0.xlf
new file mode 100755
index 0000000..166172a
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resources-2.0.xlf
@@ -0,0 +1,25 @@
+<xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" version="2.0" srcLang="en-US" trgLang="ja-JP">
+    <file id="f1" original="Graphic Example.psd">
+        <skeleton href="Graphic Example.psd.skl"/>
+        <unit id="1">
+            <segment>
+                <source>Quetzal</source>
+                <target>Quetzal</target>
+            </segment>
+        </unit>
+        <group id="1">
+            <unit id="2">
+                <segment>
+                    <source>foo</source>
+                    <target>XLIFF 文書を編集、または処理 するアプリケーションです。</target>
+                </segment>
+            </unit>
+            <unit id="3">
+                <segment>
+                    <source>bar</source>
+                    <target order="1">XLIFF データ・マネージャ</target>
+                </segment>
+            </unit>
+        </group>
+    </file>
+</xliff>
diff --git a/vendor/symfony/translation/Tests/fixtures/resources-clean.xlf b/vendor/symfony/translation/Tests/fixtures/resources-clean.xlf
new file mode 100755
index 0000000..00c8a5c
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resources-clean.xlf
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
+  <file source-language="fr-FR" target-language="en-US" datatype="plaintext" original="file.ext">
+    <header>
+      <tool tool-id="symfony" tool-name="Symfony"/>
+    </header>
+    <body>
+      <trans-unit id="LCa0a2j" resname="foo">
+        <source>foo</source>
+        <target>bar</target>
+        <note priority="1" from="bar">baz</note>
+      </trans-unit>
+      <trans-unit id="LHDhK3o" resname="key">
+        <source>key</source>
+        <target></target>
+        <note>baz</note>
+        <note>qux</note>
+      </trans-unit>
+      <trans-unit id="2DA_bnh" resname="key.with.cdata">
+        <source>key.with.cdata</source>
+        <target><![CDATA[<source> & <target>]]></target>
+      </trans-unit>
+    </body>
+  </file>
+</xliff>
diff --git a/vendor/symfony/translation/Tests/fixtures/resources-multi-files.xlf b/vendor/symfony/translation/Tests/fixtures/resources-multi-files.xlf
new file mode 100755
index 0000000..5f45150
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resources-multi-files.xlf
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
+  <file source-language="en" datatype="plaintext" original="file.ext">
+    <body>
+      <trans-unit id="1">
+        <source>foo</source>
+        <target>bar</target>
+      </trans-unit>
+    </body>
+  </file>
+  <file source-language="en" datatype="plaintext" original="otherfile.ext">
+    <body>
+      <trans-unit id="2">
+        <source>extra</source>
+      </trans-unit>
+      <trans-unit id="3">
+        <source>key</source>
+        <target></target>
+      </trans-unit>
+      <trans-unit id="4">
+        <source>test</source>
+        <target>with</target>
+        <note>note</note>
+      </trans-unit>
+    </body>
+  </file>
+</xliff>
diff --git a/vendor/symfony/translation/Tests/fixtures/resources-notes-meta.xlf b/vendor/symfony/translation/Tests/fixtures/resources-notes-meta.xlf
new file mode 100755
index 0000000..7d5bbd4
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resources-notes-meta.xlf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" version="2.0" srcLang="fr-FR" trgLang="en-US">
+  <file id="messages.en_US">
+    <unit id="LCa0a2j" name="foo">
+      <notes>
+        <note category="state">new</note>
+        <note category="approved">true</note>
+        <note category="section" priority="1">user login</note>
+      </notes>
+      <segment>
+        <source>foo</source>
+        <target>bar</target>
+      </segment>
+    </unit>
+    <unit id="uqWglk0" name="baz">
+      <notes>
+        <note id="x">x_content</note>
+        <note appliesTo="target" category="quality">Fuzzy</note>
+      </notes>
+      <segment>
+        <source>baz</source>
+        <target>biz</target>
+      </segment>
+    </unit>
+  </file>
+</xliff>
diff --git a/vendor/symfony/translation/Tests/fixtures/resources-target-attributes.xlf b/vendor/symfony/translation/Tests/fixtures/resources-target-attributes.xlf
new file mode 100755
index 0000000..700d281
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resources-target-attributes.xlf
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
+  <file source-language="fr-FR" target-language="en-US" datatype="plaintext" original="file.ext">
+    <header>
+      <tool tool-id="symfony" tool-name="Symfony"/>
+    </header>
+    <body>
+      <trans-unit id="LCa0a2j" resname="foo">
+        <source>foo</source>
+        <target state="needs-translation">bar</target>
+      </trans-unit>
+    </body>
+  </file>
+</xliff>
diff --git a/vendor/symfony/translation/Tests/fixtures/resources-tool-info.xlf b/vendor/symfony/translation/Tests/fixtures/resources-tool-info.xlf
new file mode 100755
index 0000000..1c2ae95
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resources-tool-info.xlf
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
+  <file source-language="en-US" target-language="en-US" datatype="plaintext" original="file.ext">
+    <header>
+      <tool tool-id="foo" tool-name="foo" tool-version="0.0" tool-company="Foo"/>
+    </header>
+    <body>
+      <trans-unit id="LCa0a2j" resname="foo">
+        <source>foo</source>
+        <target>bar</target>
+      </trans-unit>
+    </body>
+  </file>
+</xliff>
diff --git a/vendor/symfony/translation/Tests/fixtures/resources.csv b/vendor/symfony/translation/Tests/fixtures/resources.csv
new file mode 100755
index 0000000..374b9eb
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resources.csv
@@ -0,0 +1,4 @@
+"foo"; "bar"
+#"bar"; "foo"
+"incorrect"; "number"; "columns"; "will"; "be"; "ignored"
+"incorrect"
\ No newline at end of file
diff --git a/vendor/symfony/translation/Tests/fixtures/resources.dump.json b/vendor/symfony/translation/Tests/fixtures/resources.dump.json
new file mode 100755
index 0000000..335965d
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resources.dump.json
@@ -0,0 +1 @@
+{"foo":"\u0022bar\u0022"}
\ No newline at end of file
diff --git a/vendor/symfony/translation/Tests/fixtures/resources.ini b/vendor/symfony/translation/Tests/fixtures/resources.ini
new file mode 100755
index 0000000..4953062
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resources.ini
@@ -0,0 +1 @@
+foo="bar"
diff --git a/vendor/symfony/translation/Tests/fixtures/resources.json b/vendor/symfony/translation/Tests/fixtures/resources.json
new file mode 100755
index 0000000..8a79687
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resources.json
@@ -0,0 +1,3 @@
+{
+    "foo": "bar"
+}
\ No newline at end of file
diff --git a/vendor/symfony/translation/Tests/fixtures/resources.mo b/vendor/symfony/translation/Tests/fixtures/resources.mo
new file mode 100755
index 0000000..0a96602
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resources.mo
Binary files differ
diff --git a/vendor/symfony/translation/Tests/fixtures/resources.php b/vendor/symfony/translation/Tests/fixtures/resources.php
new file mode 100755
index 0000000..c291398
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resources.php
@@ -0,0 +1,5 @@
+<?php
+
+return array (
+  'foo' => 'bar',
+);
diff --git a/vendor/symfony/translation/Tests/fixtures/resources.po b/vendor/symfony/translation/Tests/fixtures/resources.po
new file mode 100755
index 0000000..68e0f2d
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resources.po
@@ -0,0 +1,24 @@
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: en\n"
+
+msgid "foo"
+msgstr "bar"
+
+msgid "bar"
+msgstr "foo"
+
+# Comment 1
+# Comment 2
+#, fuzzy,another
+#: src/file_1 src/file_2:50
+msgid "foo_bar"
+msgstr "foobar"
+
+# Comment
+#, fuzzy
+#: src/file_1
+msgid "bar_foo"
+msgstr "barfoo"
diff --git a/vendor/symfony/translation/Tests/fixtures/resources.ts b/vendor/symfony/translation/Tests/fixtures/resources.ts
new file mode 100755
index 0000000..29e6a6f
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resources.ts
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TS>
+  <context>
+    <name>resources</name>
+    <message>
+      <source>foo</source>
+      <translation>bar</translation>
+    </message>
+    <message>
+      <location filename="src/file_1"/>
+      <location filename="src/file_2" line="50"/>
+      <source>foo_bar</source>
+      <translation>foobar</translation>
+    </message>
+    <message>
+      <location filename="src/file_1"/>
+      <source>bar_foo</source>
+      <translation>barfoo</translation>
+    </message>
+  </context>
+</TS>
diff --git a/vendor/symfony/translation/Tests/fixtures/resources.xlf b/vendor/symfony/translation/Tests/fixtures/resources.xlf
new file mode 100755
index 0000000..b0e5988
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resources.xlf
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
+  <file source-language="en" datatype="plaintext" original="file.ext">
+    <body>
+      <trans-unit id="1">
+        <source>foo</source>
+        <target>bar</target>
+      </trans-unit>
+      <trans-unit id="2">
+        <source>extra</source>
+      </trans-unit>
+      <trans-unit id="3">
+        <source>key</source>
+        <target></target>
+      </trans-unit>
+      <trans-unit id="4">
+        <source>test</source>
+        <target>with</target>
+        <note>note</note>
+      </trans-unit>
+    </body>
+  </file>
+</xliff>
diff --git a/vendor/symfony/translation/Tests/fixtures/resources.yml b/vendor/symfony/translation/Tests/fixtures/resources.yml
new file mode 100755
index 0000000..20e9ff3
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resources.yml
@@ -0,0 +1 @@
+foo: bar
diff --git a/vendor/symfony/translation/Tests/fixtures/valid.csv b/vendor/symfony/translation/Tests/fixtures/valid.csv
new file mode 100755
index 0000000..59882e5
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/valid.csv
@@ -0,0 +1,4 @@
+foo;bar
+bar;"foo
+foo"
+"foo;foo";bar
diff --git a/vendor/symfony/translation/Tests/fixtures/with-attributes.xlf b/vendor/symfony/translation/Tests/fixtures/with-attributes.xlf
new file mode 100755
index 0000000..7873062
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/with-attributes.xlf
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="1">
+                <source>foo</source>
+                <target state="translated">bar</target>
+            </trans-unit>
+            <trans-unit id="2">
+                <source>extra</source>
+                <target state="needs-translation">bar</target>
+            </trans-unit>
+            <trans-unit id="3">
+                <source>key</source>
+                <target></target>
+                <note>baz</note>
+                <note priority="2" from="bar">qux</note>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
diff --git a/vendor/symfony/translation/Tests/fixtures/withdoctype.xlf b/vendor/symfony/translation/Tests/fixtures/withdoctype.xlf
new file mode 100755
index 0000000..f83e834
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/withdoctype.xlf
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<!DOCTYPE foo>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="en" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="1">
+                <source>foo</source>
+                <target>bar</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
diff --git a/vendor/symfony/translation/Tests/fixtures/withnote.xlf b/vendor/symfony/translation/Tests/fixtures/withnote.xlf
new file mode 100755
index 0000000..f98cf7f
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/withnote.xlf
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
+  <file source-language="en" datatype="plaintext" original="file.ext">
+    <body>
+      <trans-unit id="1">
+        <source>foo</source>
+        <target>bar</target>
+        <note priority="1">foo</note>
+      </trans-unit>
+      <trans-unit id="2" resname="extra">
+        <source>extrasource</source>
+        <note from="foo">bar</note>
+      </trans-unit>
+      <trans-unit id="123">
+        <source>key</source>
+        <target></target>
+        <note>baz</note>
+        <note priority="2" from="bar">qux</note>
+      </trans-unit>
+    </body>
+  </file>
+</xliff>
diff --git a/vendor/symfony/translation/Translator.php b/vendor/symfony/translation/Translator.php
new file mode 100755
index 0000000..9846c83
--- /dev/null
+++ b/vendor/symfony/translation/Translator.php
@@ -0,0 +1,510 @@
+<?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\Translation;
+
+use Symfony\Component\Config\ConfigCacheFactory;
+use Symfony\Component\Config\ConfigCacheFactoryInterface;
+use Symfony\Component\Config\ConfigCacheInterface;
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+use Symfony\Component\Translation\Exception\LogicException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Translation\Exception\RuntimeException;
+use Symfony\Component\Translation\Formatter\ChoiceMessageFormatterInterface;
+use Symfony\Component\Translation\Formatter\IntlFormatterInterface;
+use Symfony\Component\Translation\Formatter\MessageFormatter;
+use Symfony\Component\Translation\Formatter\MessageFormatterInterface;
+use Symfony\Component\Translation\Loader\LoaderInterface;
+use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface;
+use Symfony\Contracts\Translation\TranslatorInterface;
+
+/**
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class Translator implements LegacyTranslatorInterface, TranslatorInterface, TranslatorBagInterface
+{
+    /**
+     * @var MessageCatalogueInterface[]
+     */
+    protected $catalogues = [];
+
+    /**
+     * @var string
+     */
+    private $locale;
+
+    /**
+     * @var array
+     */
+    private $fallbackLocales = [];
+
+    /**
+     * @var LoaderInterface[]
+     */
+    private $loaders = [];
+
+    /**
+     * @var array
+     */
+    private $resources = [];
+
+    /**
+     * @var MessageFormatterInterface
+     */
+    private $formatter;
+
+    /**
+     * @var string
+     */
+    private $cacheDir;
+
+    /**
+     * @var bool
+     */
+    private $debug;
+
+    /**
+     * @var ConfigCacheFactoryInterface|null
+     */
+    private $configCacheFactory;
+
+    /**
+     * @var array|null
+     */
+    private $parentLocales;
+
+    private $hasIntlFormatter;
+
+    /**
+     * @throws InvalidArgumentException If a locale contains invalid characters
+     */
+    public function __construct(?string $locale, MessageFormatterInterface $formatter = null, string $cacheDir = null, bool $debug = false)
+    {
+        $this->setLocale($locale);
+
+        if (null === $formatter) {
+            $formatter = new MessageFormatter();
+        }
+
+        $this->formatter = $formatter;
+        $this->cacheDir = $cacheDir;
+        $this->debug = $debug;
+        $this->hasIntlFormatter = $formatter instanceof IntlFormatterInterface;
+    }
+
+    public function setConfigCacheFactory(ConfigCacheFactoryInterface $configCacheFactory)
+    {
+        $this->configCacheFactory = $configCacheFactory;
+    }
+
+    /**
+     * Adds a Loader.
+     *
+     * @param string          $format The name of the loader (@see addResource())
+     * @param LoaderInterface $loader A LoaderInterface instance
+     */
+    public function addLoader($format, LoaderInterface $loader)
+    {
+        $this->loaders[$format] = $loader;
+    }
+
+    /**
+     * Adds a Resource.
+     *
+     * @param string $format   The name of the loader (@see addLoader())
+     * @param mixed  $resource The resource name
+     * @param string $locale   The locale
+     * @param string $domain   The domain
+     *
+     * @throws InvalidArgumentException If the locale contains invalid characters
+     */
+    public function addResource($format, $resource, $locale, $domain = null)
+    {
+        if (null === $domain) {
+            $domain = 'messages';
+        }
+
+        $this->assertValidLocale($locale);
+
+        $this->resources[$locale][] = [$format, $resource, $domain];
+
+        if (\in_array($locale, $this->fallbackLocales)) {
+            $this->catalogues = [];
+        } else {
+            unset($this->catalogues[$locale]);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setLocale($locale)
+    {
+        $this->assertValidLocale($locale);
+        $this->locale = $locale;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getLocale()
+    {
+        return $this->locale;
+    }
+
+    /**
+     * Sets the fallback locales.
+     *
+     * @param array $locales The fallback locales
+     *
+     * @throws InvalidArgumentException If a locale contains invalid characters
+     */
+    public function setFallbackLocales(array $locales)
+    {
+        // needed as the fallback locales are linked to the already loaded catalogues
+        $this->catalogues = [];
+
+        foreach ($locales as $locale) {
+            $this->assertValidLocale($locale);
+        }
+
+        $this->fallbackLocales = $locales;
+    }
+
+    /**
+     * Gets the fallback locales.
+     *
+     * @internal since Symfony 4.2
+     *
+     * @return array The fallback locales
+     */
+    public function getFallbackLocales()
+    {
+        return $this->fallbackLocales;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function trans($id, array $parameters = [], $domain = null, $locale = null)
+    {
+        if (null === $domain) {
+            $domain = 'messages';
+        }
+
+        $id = (string) $id;
+        $catalogue = $this->getCatalogue($locale);
+        $locale = $catalogue->getLocale();
+        while (!$catalogue->defines($id, $domain)) {
+            if ($cat = $catalogue->getFallbackCatalogue()) {
+                $catalogue = $cat;
+                $locale = $catalogue->getLocale();
+            } else {
+                break;
+            }
+        }
+
+        if ($this->hasIntlFormatter && $catalogue->defines($id, $domain.MessageCatalogue::INTL_DOMAIN_SUFFIX)) {
+            return $this->formatter->formatIntl($catalogue->get($id, $domain), $locale, $parameters);
+        }
+
+        return $this->formatter->format($catalogue->get($id, $domain), $locale, $parameters);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @deprecated since Symfony 4.2, use the trans() method instead with a %count% parameter
+     */
+    public function transChoice($id, $number, array $parameters = [], $domain = null, $locale = null)
+    {
+        @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2, use the trans() one instead with a "%%count%%" parameter.', __METHOD__), E_USER_DEPRECATED);
+
+        if (!$this->formatter instanceof ChoiceMessageFormatterInterface) {
+            throw new LogicException(sprintf('The formatter "%s" does not support plural translations.', \get_class($this->formatter)));
+        }
+
+        if (null === $domain) {
+            $domain = 'messages';
+        }
+
+        $id = (string) $id;
+        $catalogue = $this->getCatalogue($locale);
+        $locale = $catalogue->getLocale();
+        while (!$catalogue->defines($id, $domain)) {
+            if ($cat = $catalogue->getFallbackCatalogue()) {
+                $catalogue = $cat;
+                $locale = $catalogue->getLocale();
+            } else {
+                break;
+            }
+        }
+
+        if ($this->hasIntlFormatter && $catalogue->defines($id, $domain.MessageCatalogue::INTL_DOMAIN_SUFFIX)) {
+            return $this->formatter->formatIntl($catalogue->get($id, $domain), $locale, ['%count%' => $number] + $parameters);
+        }
+
+        return $this->formatter->choiceFormat($catalogue->get($id, $domain), $number, $locale, $parameters);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getCatalogue($locale = null)
+    {
+        if (null === $locale) {
+            $locale = $this->getLocale();
+        } else {
+            $this->assertValidLocale($locale);
+        }
+
+        if (!isset($this->catalogues[$locale])) {
+            $this->loadCatalogue($locale);
+        }
+
+        return $this->catalogues[$locale];
+    }
+
+    /**
+     * Gets the loaders.
+     *
+     * @return array LoaderInterface[]
+     */
+    protected function getLoaders()
+    {
+        return $this->loaders;
+    }
+
+    /**
+     * @param string $locale
+     */
+    protected function loadCatalogue($locale)
+    {
+        if (null === $this->cacheDir) {
+            $this->initializeCatalogue($locale);
+        } else {
+            $this->initializeCacheCatalogue($locale);
+        }
+    }
+
+    /**
+     * @param string $locale
+     */
+    protected function initializeCatalogue($locale)
+    {
+        $this->assertValidLocale($locale);
+
+        try {
+            $this->doLoadCatalogue($locale);
+        } catch (NotFoundResourceException $e) {
+            if (!$this->computeFallbackLocales($locale)) {
+                throw $e;
+            }
+        }
+        $this->loadFallbackCatalogues($locale);
+    }
+
+    private function initializeCacheCatalogue(string $locale): void
+    {
+        if (isset($this->catalogues[$locale])) {
+            /* Catalogue already initialized. */
+            return;
+        }
+
+        $this->assertValidLocale($locale);
+        $cache = $this->getConfigCacheFactory()->cache($this->getCatalogueCachePath($locale),
+            function (ConfigCacheInterface $cache) use ($locale) {
+                $this->dumpCatalogue($locale, $cache);
+            }
+        );
+
+        if (isset($this->catalogues[$locale])) {
+            /* Catalogue has been initialized as it was written out to cache. */
+            return;
+        }
+
+        /* Read catalogue from cache. */
+        $this->catalogues[$locale] = include $cache->getPath();
+    }
+
+    private function dumpCatalogue($locale, ConfigCacheInterface $cache): void
+    {
+        $this->initializeCatalogue($locale);
+        $fallbackContent = $this->getFallbackContent($this->catalogues[$locale]);
+
+        $content = sprintf(<<<EOF
+<?php
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+\$catalogue = new MessageCatalogue('%s', %s);
+
+%s
+return \$catalogue;
+
+EOF
+            ,
+            $locale,
+            var_export($this->getAllMessages($this->catalogues[$locale]), true),
+            $fallbackContent
+        );
+
+        $cache->write($content, $this->catalogues[$locale]->getResources());
+    }
+
+    private function getFallbackContent(MessageCatalogue $catalogue): string
+    {
+        $fallbackContent = '';
+        $current = '';
+        $replacementPattern = '/[^a-z0-9_]/i';
+        $fallbackCatalogue = $catalogue->getFallbackCatalogue();
+        while ($fallbackCatalogue) {
+            $fallback = $fallbackCatalogue->getLocale();
+            $fallbackSuffix = ucfirst(preg_replace($replacementPattern, '_', $fallback));
+            $currentSuffix = ucfirst(preg_replace($replacementPattern, '_', $current));
+
+            $fallbackContent .= sprintf(<<<'EOF'
+$catalogue%s = new MessageCatalogue('%s', %s);
+$catalogue%s->addFallbackCatalogue($catalogue%s);
+
+EOF
+                ,
+                $fallbackSuffix,
+                $fallback,
+                var_export($this->getAllMessages($fallbackCatalogue), true),
+                $currentSuffix,
+                $fallbackSuffix
+            );
+            $current = $fallbackCatalogue->getLocale();
+            $fallbackCatalogue = $fallbackCatalogue->getFallbackCatalogue();
+        }
+
+        return $fallbackContent;
+    }
+
+    private function getCatalogueCachePath($locale)
+    {
+        return $this->cacheDir.'/catalogue.'.$locale.'.'.strtr(substr(base64_encode(hash('sha256', serialize($this->fallbackLocales), true)), 0, 7), '/', '_').'.php';
+    }
+
+    /**
+     * @internal
+     */
+    protected function doLoadCatalogue($locale): void
+    {
+        $this->catalogues[$locale] = new MessageCatalogue($locale);
+
+        if (isset($this->resources[$locale])) {
+            foreach ($this->resources[$locale] as $resource) {
+                if (!isset($this->loaders[$resource[0]])) {
+                    throw new RuntimeException(sprintf('The "%s" translation loader is not registered.', $resource[0]));
+                }
+                $this->catalogues[$locale]->addCatalogue($this->loaders[$resource[0]]->load($resource[1], $locale, $resource[2]));
+            }
+        }
+    }
+
+    private function loadFallbackCatalogues($locale): void
+    {
+        $current = $this->catalogues[$locale];
+
+        foreach ($this->computeFallbackLocales($locale) as $fallback) {
+            if (!isset($this->catalogues[$fallback])) {
+                $this->initializeCatalogue($fallback);
+            }
+
+            $fallbackCatalogue = new MessageCatalogue($fallback, $this->getAllMessages($this->catalogues[$fallback]));
+            foreach ($this->catalogues[$fallback]->getResources() as $resource) {
+                $fallbackCatalogue->addResource($resource);
+            }
+            $current->addFallbackCatalogue($fallbackCatalogue);
+            $current = $fallbackCatalogue;
+        }
+    }
+
+    protected function computeFallbackLocales($locale)
+    {
+        if (null === $this->parentLocales) {
+            $parentLocales = json_decode(file_get_contents(__DIR__.'/Resources/data/parents.json'), true);
+        }
+
+        $locales = [];
+        foreach ($this->fallbackLocales as $fallback) {
+            if ($fallback === $locale) {
+                continue;
+            }
+
+            $locales[] = $fallback;
+        }
+
+        while ($locale) {
+            $parent = $parentLocales[$locale] ?? null;
+
+            if (!$parent && false !== strrchr($locale, '_')) {
+                $locale = substr($locale, 0, -\strlen(strrchr($locale, '_')));
+            } elseif ('root' !== $parent) {
+                $locale = $parent;
+            } else {
+                $locale = null;
+            }
+
+            if (null !== $locale) {
+                array_unshift($locales, $locale);
+            }
+        }
+
+        return array_unique($locales);
+    }
+
+    /**
+     * Asserts that the locale is valid, throws an Exception if not.
+     *
+     * @param string $locale Locale to tests
+     *
+     * @throws InvalidArgumentException If the locale contains invalid characters
+     */
+    protected function assertValidLocale($locale)
+    {
+        if (1 !== preg_match('/^[a-z0-9@_\\.\\-]*$/i', $locale)) {
+            throw new InvalidArgumentException(sprintf('Invalid "%s" locale.', $locale));
+        }
+    }
+
+    /**
+     * Provides the ConfigCache factory implementation, falling back to a
+     * default implementation if necessary.
+     */
+    private function getConfigCacheFactory(): ConfigCacheFactoryInterface
+    {
+        if (!$this->configCacheFactory) {
+            $this->configCacheFactory = new ConfigCacheFactory($this->debug);
+        }
+
+        return $this->configCacheFactory;
+    }
+
+    private function getAllMessages(MessageCatalogueInterface $catalogue): array
+    {
+        $allMessages = [];
+
+        foreach ($catalogue->all() as $domain => $messages) {
+            if ($intlMessages = $catalogue->all($domain.MessageCatalogue::INTL_DOMAIN_SUFFIX)) {
+                $allMessages[$domain.MessageCatalogue::INTL_DOMAIN_SUFFIX] = $intlMessages;
+                $messages = array_diff_key($messages, $intlMessages);
+            }
+            if ($messages) {
+                $allMessages[$domain] = $messages;
+            }
+        }
+
+        return $allMessages;
+    }
+}
diff --git a/vendor/symfony/translation/TranslatorBagInterface.php b/vendor/symfony/translation/TranslatorBagInterface.php
new file mode 100755
index 0000000..5e49e2d
--- /dev/null
+++ b/vendor/symfony/translation/TranslatorBagInterface.php
@@ -0,0 +1,33 @@
+<?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\Translation;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+
+/**
+ * TranslatorBagInterface.
+ *
+ * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
+ */
+interface TranslatorBagInterface
+{
+    /**
+     * Gets the catalogue by locale.
+     *
+     * @param string|null $locale The locale or null to use the default
+     *
+     * @return MessageCatalogueInterface
+     *
+     * @throws InvalidArgumentException If the locale contains invalid characters
+     */
+    public function getCatalogue($locale = null);
+}
diff --git a/vendor/symfony/translation/TranslatorInterface.php b/vendor/symfony/translation/TranslatorInterface.php
new file mode 100755
index 0000000..f677d24
--- /dev/null
+++ b/vendor/symfony/translation/TranslatorInterface.php
@@ -0,0 +1,70 @@
+<?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\Translation;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+use Symfony\Contracts\Translation\LocaleAwareInterface;
+
+/**
+ * TranslatorInterface.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @deprecated since Symfony 4.2, use Symfony\Contracts\Translation\TranslatorInterface instead
+ */
+interface TranslatorInterface extends LocaleAwareInterface
+{
+    /**
+     * Translates the given message.
+     *
+     * @param string      $id         The message id (may also be an object that can be cast to string)
+     * @param array       $parameters An array of parameters for the message
+     * @param string|null $domain     The domain for the message or null to use the default
+     * @param string|null $locale     The locale or null to use the default
+     *
+     * @return string The translated string
+     *
+     * @throws InvalidArgumentException If the locale contains invalid characters
+     */
+    public function trans($id, array $parameters = [], $domain = null, $locale = null);
+
+    /**
+     * Translates the given choice message by choosing a translation according to a number.
+     *
+     * @param string      $id         The message id (may also be an object that can be cast to string)
+     * @param int         $number     The number to use to find the index of the message
+     * @param array       $parameters An array of parameters for the message
+     * @param string|null $domain     The domain for the message or null to use the default
+     * @param string|null $locale     The locale or null to use the default
+     *
+     * @return string The translated string
+     *
+     * @throws InvalidArgumentException If the locale contains invalid characters
+     */
+    public function transChoice($id, $number, array $parameters = [], $domain = null, $locale = null);
+
+    /**
+     * Sets the current locale.
+     *
+     * @param string $locale The locale
+     *
+     * @throws InvalidArgumentException If the locale contains invalid characters
+     */
+    public function setLocale($locale);
+
+    /**
+     * Returns the current locale.
+     *
+     * @return string The locale
+     */
+    public function getLocale();
+}
diff --git a/vendor/symfony/translation/Util/ArrayConverter.php b/vendor/symfony/translation/Util/ArrayConverter.php
new file mode 100755
index 0000000..22c602e
--- /dev/null
+++ b/vendor/symfony/translation/Util/ArrayConverter.php
@@ -0,0 +1,99 @@
+<?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\Translation\Util;
+
+/**
+ * ArrayConverter generates tree like structure from a message catalogue.
+ * e.g. this
+ *   'foo.bar1' => 'test1',
+ *   'foo.bar2' => 'test2'
+ * converts to follows:
+ *   foo:
+ *     bar1: test1
+ *     bar2: test2.
+ *
+ * @author Gennady Telegin <gtelegin@gmail.com>
+ */
+class ArrayConverter
+{
+    /**
+     * Converts linear messages array to tree-like array.
+     * For example this array('foo.bar' => 'value') will be converted to ['foo' => ['bar' => 'value']].
+     *
+     * @param array $messages Linear messages array
+     *
+     * @return array Tree-like messages array
+     */
+    public static function expandToTree(array $messages)
+    {
+        $tree = [];
+
+        foreach ($messages as $id => $value) {
+            $referenceToElement = &self::getElementByPath($tree, explode('.', $id));
+
+            $referenceToElement = $value;
+
+            unset($referenceToElement);
+        }
+
+        return $tree;
+    }
+
+    private static function &getElementByPath(array &$tree, array $parts)
+    {
+        $elem = &$tree;
+        $parentOfElem = null;
+
+        foreach ($parts as $i => $part) {
+            if (isset($elem[$part]) && \is_string($elem[$part])) {
+                /* Process next case:
+                 *    'foo': 'test1',
+                 *    'foo.bar': 'test2'
+                 *
+                 * $tree['foo'] was string before we found array {bar: test2}.
+                 *  Treat new element as string too, e.g. add $tree['foo.bar'] = 'test2';
+                 */
+                $elem = &$elem[implode('.', \array_slice($parts, $i))];
+                break;
+            }
+            $parentOfElem = &$elem;
+            $elem = &$elem[$part];
+        }
+
+        if ($elem && \is_array($elem) && $parentOfElem) {
+            /* Process next case:
+             *    'foo.bar': 'test1'
+             *    'foo': 'test2'
+             *
+             * $tree['foo'] was array = {bar: 'test1'} before we found string constant `foo`.
+             * Cancel treating $tree['foo'] as array and cancel back it expansion,
+             *  e.g. make it $tree['foo.bar'] = 'test1' again.
+             */
+            self::cancelExpand($parentOfElem, $part, $elem);
+        }
+
+        return $elem;
+    }
+
+    private static function cancelExpand(array &$tree, $prefix, array $node)
+    {
+        $prefix .= '.';
+
+        foreach ($node as $id => $value) {
+            if (\is_string($value)) {
+                $tree[$prefix.$id] = $value;
+            } else {
+                self::cancelExpand($tree, $prefix.$id, $value);
+            }
+        }
+    }
+}
diff --git a/vendor/symfony/translation/Util/XliffUtils.php b/vendor/symfony/translation/Util/XliffUtils.php
new file mode 100755
index 0000000..3ace285
--- /dev/null
+++ b/vendor/symfony/translation/Util/XliffUtils.php
@@ -0,0 +1,163 @@
+<?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\Translation\Util;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+
+/**
+ * Provides some utility methods for XLIFF translation files, such as validating
+ * their contents according to the XSD schema.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class XliffUtils
+{
+    /**
+     * Gets xliff file version based on the root "version" attribute.
+     *
+     * Defaults to 1.2 for backwards compatibility.
+     *
+     * @throws InvalidArgumentException
+     */
+    public static function getVersionNumber(\DOMDocument $dom): string
+    {
+        /** @var \DOMNode $xliff */
+        foreach ($dom->getElementsByTagName('xliff') as $xliff) {
+            $version = $xliff->attributes->getNamedItem('version');
+            if ($version) {
+                return $version->nodeValue;
+            }
+
+            $namespace = $xliff->attributes->getNamedItem('xmlns');
+            if ($namespace) {
+                if (0 !== substr_compare('urn:oasis:names:tc:xliff:document:', $namespace->nodeValue, 0, 34)) {
+                    throw new InvalidArgumentException(sprintf('Not a valid XLIFF namespace "%s"', $namespace));
+                }
+
+                return substr($namespace, 34);
+            }
+        }
+
+        // Falls back to v1.2
+        return '1.2';
+    }
+
+    /**
+     * Validates and parses the given file into a DOMDocument.
+     *
+     * @throws InvalidResourceException
+     */
+    public static function validateSchema(\DOMDocument $dom): array
+    {
+        $xliffVersion = static::getVersionNumber($dom);
+        $internalErrors = libxml_use_internal_errors(true);
+        $disableEntities = libxml_disable_entity_loader(false);
+
+        $isValid = @$dom->schemaValidateSource(self::getSchema($xliffVersion));
+        if (!$isValid) {
+            libxml_disable_entity_loader($disableEntities);
+
+            return self::getXmlErrors($internalErrors);
+        }
+
+        libxml_disable_entity_loader($disableEntities);
+
+        $dom->normalizeDocument();
+
+        libxml_clear_errors();
+        libxml_use_internal_errors($internalErrors);
+
+        return [];
+    }
+
+    public static function getErrorsAsString(array $xmlErrors): string
+    {
+        $errorsAsString = '';
+
+        foreach ($xmlErrors as $error) {
+            $errorsAsString .= sprintf("[%s %s] %s (in %s - line %d, column %d)\n",
+                LIBXML_ERR_WARNING === $error['level'] ? 'WARNING' : 'ERROR',
+                $error['code'],
+                $error['message'],
+                $error['file'],
+                $error['line'],
+                $error['column']
+            );
+        }
+
+        return $errorsAsString;
+    }
+
+    private static function getSchema(string $xliffVersion): string
+    {
+        if ('1.2' === $xliffVersion) {
+            $schemaSource = file_get_contents(__DIR__.'/../Resources/schemas/xliff-core-1.2-strict.xsd');
+            $xmlUri = 'http://www.w3.org/2001/xml.xsd';
+        } elseif ('2.0' === $xliffVersion) {
+            $schemaSource = file_get_contents(__DIR__.'/../Resources/schemas/xliff-core-2.0.xsd');
+            $xmlUri = 'informativeCopiesOf3rdPartySchemas/w3c/xml.xsd';
+        } else {
+            throw new InvalidArgumentException(sprintf('No support implemented for loading XLIFF version "%s".', $xliffVersion));
+        }
+
+        return self::fixXmlLocation($schemaSource, $xmlUri);
+    }
+
+    /**
+     * Internally changes the URI of a dependent xsd to be loaded locally.
+     */
+    private static function fixXmlLocation(string $schemaSource, string $xmlUri): string
+    {
+        $newPath = str_replace('\\', '/', __DIR__).'/../Resources/schemas/xml.xsd';
+        $parts = explode('/', $newPath);
+        $locationstart = 'file:///';
+        if (0 === stripos($newPath, 'phar://')) {
+            $tmpfile = tempnam(sys_get_temp_dir(), 'symfony');
+            if ($tmpfile) {
+                copy($newPath, $tmpfile);
+                $parts = explode('/', str_replace('\\', '/', $tmpfile));
+            } else {
+                array_shift($parts);
+                $locationstart = 'phar:///';
+            }
+        }
+
+        $drive = '\\' === \DIRECTORY_SEPARATOR ? array_shift($parts).'/' : '';
+        $newPath = $locationstart.$drive.implode('/', array_map('rawurlencode', $parts));
+
+        return str_replace($xmlUri, $newPath, $schemaSource);
+    }
+
+    /**
+     * Returns the XML errors of the internal XML parser.
+     */
+    private static function getXmlErrors(bool $internalErrors): array
+    {
+        $errors = [];
+        foreach (libxml_get_errors() as $error) {
+            $errors[] = [
+                'level' => LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',
+                'code' => $error->code,
+                'message' => trim($error->message),
+                'file' => $error->file ?: 'n/a',
+                'line' => $error->line,
+                'column' => $error->column,
+            ];
+        }
+
+        libxml_clear_errors();
+        libxml_use_internal_errors($internalErrors);
+
+        return $errors;
+    }
+}
diff --git a/vendor/symfony/translation/Writer/TranslationWriter.php b/vendor/symfony/translation/Writer/TranslationWriter.php
new file mode 100755
index 0000000..a44d24c
--- /dev/null
+++ b/vendor/symfony/translation/Writer/TranslationWriter.php
@@ -0,0 +1,90 @@
+<?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\Translation\Writer;
+
+use Symfony\Component\Translation\Dumper\DumperInterface;
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+use Symfony\Component\Translation\Exception\RuntimeException;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * TranslationWriter writes translation messages.
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+class TranslationWriter implements TranslationWriterInterface
+{
+    private $dumpers = [];
+
+    /**
+     * Adds a dumper to the writer.
+     *
+     * @param string          $format The format of the dumper
+     * @param DumperInterface $dumper The dumper
+     */
+    public function addDumper($format, DumperInterface $dumper)
+    {
+        $this->dumpers[$format] = $dumper;
+    }
+
+    /**
+     * Disables dumper backup.
+     *
+     * @deprecated since Symfony 4.1
+     */
+    public function disableBackup()
+    {
+        @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1.', __METHOD__), E_USER_DEPRECATED);
+
+        foreach ($this->dumpers as $dumper) {
+            if (method_exists($dumper, 'setBackup')) {
+                $dumper->setBackup(false);
+            }
+        }
+    }
+
+    /**
+     * Obtains the list of supported formats.
+     *
+     * @return array
+     */
+    public function getFormats()
+    {
+        return array_keys($this->dumpers);
+    }
+
+    /**
+     * Writes translation from the catalogue according to the selected format.
+     *
+     * @param MessageCatalogue $catalogue The message catalogue to write
+     * @param string           $format    The format to use to dump the messages
+     * @param array            $options   Options that are passed to the dumper
+     *
+     * @throws InvalidArgumentException
+     */
+    public function write(MessageCatalogue $catalogue, $format, $options = [])
+    {
+        if (!isset($this->dumpers[$format])) {
+            throw new InvalidArgumentException(sprintf('There is no dumper associated with format "%s".', $format));
+        }
+
+        // get the right dumper
+        $dumper = $this->dumpers[$format];
+
+        if (isset($options['path']) && !is_dir($options['path']) && !@mkdir($options['path'], 0777, true) && !is_dir($options['path'])) {
+            throw new RuntimeException(sprintf('Translation Writer was not able to create directory "%s"', $options['path']));
+        }
+
+        // save
+        $dumper->dump($catalogue, $options);
+    }
+}
diff --git a/vendor/symfony/translation/Writer/TranslationWriterInterface.php b/vendor/symfony/translation/Writer/TranslationWriterInterface.php
new file mode 100755
index 0000000..b07c08e
--- /dev/null
+++ b/vendor/symfony/translation/Writer/TranslationWriterInterface.php
@@ -0,0 +1,34 @@
+<?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\Translation\Writer;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * TranslationWriter writes translation messages.
+ *
+ * @author Michel Salib <michelsalib@hotmail.com>
+ */
+interface TranslationWriterInterface
+{
+    /**
+     * Writes translation from the catalogue according to the selected format.
+     *
+     * @param MessageCatalogue $catalogue The message catalogue to write
+     * @param string           $format    The format to use to dump the messages
+     * @param array            $options   Options that are passed to the dumper
+     *
+     * @throws InvalidArgumentException
+     */
+    public function write(MessageCatalogue $catalogue, $format, $options = []);
+}
diff --git a/vendor/symfony/translation/composer.json b/vendor/symfony/translation/composer.json
new file mode 100755
index 0000000..fd25a4c
--- /dev/null
+++ b/vendor/symfony/translation/composer.json
@@ -0,0 +1,60 @@
+{
+    "name": "symfony/translation",
+    "type": "library",
+    "description": "Symfony Translation Component",
+    "keywords": [],
+    "homepage": "https://symfony.com",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Fabien Potencier",
+            "email": "fabien@symfony.com"
+        },
+        {
+            "name": "Symfony Community",
+            "homepage": "https://symfony.com/contributors"
+        }
+    ],
+    "require": {
+        "php": "^7.1.3",
+        "symfony/polyfill-mbstring": "~1.0",
+        "symfony/translation-contracts": "^1.1.2"
+    },
+    "require-dev": {
+        "symfony/config": "~3.4|~4.0",
+        "symfony/console": "~3.4|~4.0",
+        "symfony/dependency-injection": "~3.4|~4.0",
+        "symfony/http-kernel": "~3.4|~4.0",
+        "symfony/intl": "~3.4|~4.0",
+        "symfony/service-contracts": "^1.1.2",
+        "symfony/var-dumper": "~3.4|~4.0",
+        "symfony/yaml": "~3.4|~4.0",
+        "symfony/finder": "~2.8|~3.0|~4.0",
+        "psr/log": "~1.0"
+    },
+    "conflict": {
+        "symfony/config": "<3.4",
+        "symfony/dependency-injection": "<3.4",
+        "symfony/yaml": "<3.4"
+    },
+    "provide": {
+        "symfony/translation-implementation": "1.0"
+    },
+    "suggest": {
+        "symfony/config": "",
+        "symfony/yaml": "",
+        "psr/log-implementation": "To use logging capability in translator"
+    },
+    "autoload": {
+        "psr-4": { "Symfony\\Component\\Translation\\": "" },
+        "exclude-from-classmap": [
+            "/Tests/"
+        ]
+    },
+    "minimum-stability": "dev",
+    "extra": {
+        "branch-alias": {
+            "dev-master": "4.3-dev"
+        }
+    }
+}
diff --git a/vendor/symfony/translation/phpunit.xml.dist b/vendor/symfony/translation/phpunit.xml.dist
new file mode 100755
index 0000000..21d3246
--- /dev/null
+++ b/vendor/symfony/translation/phpunit.xml.dist
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/5.2/phpunit.xsd"
+         backupGlobals="false"
+         colors="true"
+         bootstrap="vendor/autoload.php"
+         failOnRisky="true"
+         failOnWarning="true"
+>
+    <php>
+        <ini name="error_reporting" value="-1" />
+    </php>
+
+    <testsuites>
+        <testsuite name="Symfony Translation Component Test Suite">
+            <directory>./Tests/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./</directory>
+            <exclude>
+                <directory>./Tests</directory>
+                <directory>./vendor</directory>
+            </exclude>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/vendor/symfony/var-dumper/.gitignore b/vendor/symfony/var-dumper/.gitignore
new file mode 100755
index 0000000..5414c2c
--- /dev/null
+++ b/vendor/symfony/var-dumper/.gitignore
@@ -0,0 +1,3 @@
+composer.lock
+phpunit.xml
+vendor/
diff --git a/vendor/symfony/var-dumper/CHANGELOG.md b/vendor/symfony/var-dumper/CHANGELOG.md
new file mode 100755
index 0000000..5f88029
--- /dev/null
+++ b/vendor/symfony/var-dumper/CHANGELOG.md
@@ -0,0 +1,42 @@
+CHANGELOG
+=========
+
+4.3.0
+-----
+
+ * added `DsCaster` to support dumping the contents of data structures from the Ds extension
+
+4.2.0
+-----
+
+ * support selecting the format to use by setting the environment variable `VAR_DUMPER_FORMAT` to `html` or `cli`
+
+4.1.0
+-----
+
+ * added a `ServerDumper` to send serialized Data clones to a server
+ * added a `ServerDumpCommand` and `DumpServer` to run a server collecting
+   and displaying dumps on a single place with multiple formats support
+ * added `CliDescriptor` and `HtmlDescriptor` descriptors for `server:dump` CLI and HTML formats support
+
+4.0.0
+-----
+
+ * support for passing `\ReflectionClass` instances to the `Caster::castObject()`
+   method has been dropped, pass class names as strings instead
+ * the `Data::getRawData()` method has been removed
+ * the `VarDumperTestTrait::assertDumpEquals()` method expects a 3rd `$context = null`
+   argument and moves `$message = ''` argument at 4th position.
+ * the `VarDumperTestTrait::assertDumpMatchesFormat()` method expects a 3rd `$context = null`
+   argument and moves `$message = ''` argument at 4th position.
+
+3.4.0
+-----
+
+ * added `AbstractCloner::setMinDepth()` function to ensure minimum tree depth
+ * deprecated `MongoCaster`
+
+2.7.0
+-----
+
+ * deprecated `Cloner\Data::getLimitedClone()`. Use `withMaxDepth`, `withMaxItemsPerDepth` or `withRefHandles` instead.
diff --git a/vendor/symfony/var-dumper/Caster/AmqpCaster.php b/vendor/symfony/var-dumper/Caster/AmqpCaster.php
new file mode 100755
index 0000000..19bdc29
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/AmqpCaster.php
@@ -0,0 +1,210 @@
+<?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\VarDumper\Caster;
+
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * Casts Amqp related classes to array representation.
+ *
+ * @author Grégoire Pineau <lyrixx@lyrixx.info>
+ */
+class AmqpCaster
+{
+    private static $flags = [
+        AMQP_DURABLE => 'AMQP_DURABLE',
+        AMQP_PASSIVE => 'AMQP_PASSIVE',
+        AMQP_EXCLUSIVE => 'AMQP_EXCLUSIVE',
+        AMQP_AUTODELETE => 'AMQP_AUTODELETE',
+        AMQP_INTERNAL => 'AMQP_INTERNAL',
+        AMQP_NOLOCAL => 'AMQP_NOLOCAL',
+        AMQP_AUTOACK => 'AMQP_AUTOACK',
+        AMQP_IFEMPTY => 'AMQP_IFEMPTY',
+        AMQP_IFUNUSED => 'AMQP_IFUNUSED',
+        AMQP_MANDATORY => 'AMQP_MANDATORY',
+        AMQP_IMMEDIATE => 'AMQP_IMMEDIATE',
+        AMQP_MULTIPLE => 'AMQP_MULTIPLE',
+        AMQP_NOWAIT => 'AMQP_NOWAIT',
+        AMQP_REQUEUE => 'AMQP_REQUEUE',
+    ];
+
+    private static $exchangeTypes = [
+        AMQP_EX_TYPE_DIRECT => 'AMQP_EX_TYPE_DIRECT',
+        AMQP_EX_TYPE_FANOUT => 'AMQP_EX_TYPE_FANOUT',
+        AMQP_EX_TYPE_TOPIC => 'AMQP_EX_TYPE_TOPIC',
+        AMQP_EX_TYPE_HEADERS => 'AMQP_EX_TYPE_HEADERS',
+    ];
+
+    public static function castConnection(\AMQPConnection $c, array $a, Stub $stub, $isNested)
+    {
+        $prefix = Caster::PREFIX_VIRTUAL;
+
+        $a += [
+            $prefix.'is_connected' => $c->isConnected(),
+        ];
+
+        // Recent version of the extension already expose private properties
+        if (isset($a["\x00AMQPConnection\x00login"])) {
+            return $a;
+        }
+
+        // BC layer in the amqp lib
+        if (method_exists($c, 'getReadTimeout')) {
+            $timeout = $c->getReadTimeout();
+        } else {
+            $timeout = $c->getTimeout();
+        }
+
+        $a += [
+            $prefix.'is_connected' => $c->isConnected(),
+            $prefix.'login' => $c->getLogin(),
+            $prefix.'password' => $c->getPassword(),
+            $prefix.'host' => $c->getHost(),
+            $prefix.'vhost' => $c->getVhost(),
+            $prefix.'port' => $c->getPort(),
+            $prefix.'read_timeout' => $timeout,
+        ];
+
+        return $a;
+    }
+
+    public static function castChannel(\AMQPChannel $c, array $a, Stub $stub, $isNested)
+    {
+        $prefix = Caster::PREFIX_VIRTUAL;
+
+        $a += [
+            $prefix.'is_connected' => $c->isConnected(),
+            $prefix.'channel_id' => $c->getChannelId(),
+        ];
+
+        // Recent version of the extension already expose private properties
+        if (isset($a["\x00AMQPChannel\x00connection"])) {
+            return $a;
+        }
+
+        $a += [
+            $prefix.'connection' => $c->getConnection(),
+            $prefix.'prefetch_size' => $c->getPrefetchSize(),
+            $prefix.'prefetch_count' => $c->getPrefetchCount(),
+        ];
+
+        return $a;
+    }
+
+    public static function castQueue(\AMQPQueue $c, array $a, Stub $stub, $isNested)
+    {
+        $prefix = Caster::PREFIX_VIRTUAL;
+
+        $a += [
+            $prefix.'flags' => self::extractFlags($c->getFlags()),
+        ];
+
+        // Recent version of the extension already expose private properties
+        if (isset($a["\x00AMQPQueue\x00name"])) {
+            return $a;
+        }
+
+        $a += [
+            $prefix.'connection' => $c->getConnection(),
+            $prefix.'channel' => $c->getChannel(),
+            $prefix.'name' => $c->getName(),
+            $prefix.'arguments' => $c->getArguments(),
+        ];
+
+        return $a;
+    }
+
+    public static function castExchange(\AMQPExchange $c, array $a, Stub $stub, $isNested)
+    {
+        $prefix = Caster::PREFIX_VIRTUAL;
+
+        $a += [
+            $prefix.'flags' => self::extractFlags($c->getFlags()),
+        ];
+
+        $type = isset(self::$exchangeTypes[$c->getType()]) ? new ConstStub(self::$exchangeTypes[$c->getType()], $c->getType()) : $c->getType();
+
+        // Recent version of the extension already expose private properties
+        if (isset($a["\x00AMQPExchange\x00name"])) {
+            $a["\x00AMQPExchange\x00type"] = $type;
+
+            return $a;
+        }
+
+        $a += [
+            $prefix.'connection' => $c->getConnection(),
+            $prefix.'channel' => $c->getChannel(),
+            $prefix.'name' => $c->getName(),
+            $prefix.'type' => $type,
+            $prefix.'arguments' => $c->getArguments(),
+        ];
+
+        return $a;
+    }
+
+    public static function castEnvelope(\AMQPEnvelope $c, array $a, Stub $stub, $isNested, $filter = 0)
+    {
+        $prefix = Caster::PREFIX_VIRTUAL;
+
+        $deliveryMode = new ConstStub($c->getDeliveryMode().(2 === $c->getDeliveryMode() ? ' (persistent)' : ' (non-persistent)'), $c->getDeliveryMode());
+
+        // Recent version of the extension already expose private properties
+        if (isset($a["\x00AMQPEnvelope\x00body"])) {
+            $a["\0AMQPEnvelope\0delivery_mode"] = $deliveryMode;
+
+            return $a;
+        }
+
+        if (!($filter & Caster::EXCLUDE_VERBOSE)) {
+            $a += [$prefix.'body' => $c->getBody()];
+        }
+
+        $a += [
+            $prefix.'delivery_tag' => $c->getDeliveryTag(),
+            $prefix.'is_redelivery' => $c->isRedelivery(),
+            $prefix.'exchange_name' => $c->getExchangeName(),
+            $prefix.'routing_key' => $c->getRoutingKey(),
+            $prefix.'content_type' => $c->getContentType(),
+            $prefix.'content_encoding' => $c->getContentEncoding(),
+            $prefix.'headers' => $c->getHeaders(),
+            $prefix.'delivery_mode' => $deliveryMode,
+            $prefix.'priority' => $c->getPriority(),
+            $prefix.'correlation_id' => $c->getCorrelationId(),
+            $prefix.'reply_to' => $c->getReplyTo(),
+            $prefix.'expiration' => $c->getExpiration(),
+            $prefix.'message_id' => $c->getMessageId(),
+            $prefix.'timestamp' => $c->getTimeStamp(),
+            $prefix.'type' => $c->getType(),
+            $prefix.'user_id' => $c->getUserId(),
+            $prefix.'app_id' => $c->getAppId(),
+        ];
+
+        return $a;
+    }
+
+    private static function extractFlags($flags)
+    {
+        $flagsArray = [];
+
+        foreach (self::$flags as $value => $name) {
+            if ($flags & $value) {
+                $flagsArray[] = $name;
+            }
+        }
+
+        if (!$flagsArray) {
+            $flagsArray = ['AMQP_NOPARAM'];
+        }
+
+        return new ConstStub(implode('|', $flagsArray), $flags);
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/ArgsStub.php b/vendor/symfony/var-dumper/Caster/ArgsStub.php
new file mode 100755
index 0000000..1ca7a86
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/ArgsStub.php
@@ -0,0 +1,80 @@
+<?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\VarDumper\Caster;
+
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * Represents a list of function arguments.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class ArgsStub extends EnumStub
+{
+    private static $parameters = [];
+
+    public function __construct(array $args, string $function, ?string $class)
+    {
+        list($variadic, $params) = self::getParameters($function, $class);
+
+        $values = [];
+        foreach ($args as $k => $v) {
+            $values[$k] = !is_scalar($v) && !$v instanceof Stub ? new CutStub($v) : $v;
+        }
+        if (null === $params) {
+            parent::__construct($values, false);
+
+            return;
+        }
+        if (\count($values) < \count($params)) {
+            $params = \array_slice($params, 0, \count($values));
+        } elseif (\count($values) > \count($params)) {
+            $values[] = new EnumStub(array_splice($values, \count($params)), false);
+            $params[] = $variadic;
+        }
+        if (['...'] === $params) {
+            $this->dumpKeys = false;
+            $this->value = $values[0]->value;
+        } else {
+            $this->value = array_combine($params, $values);
+        }
+    }
+
+    private static function getParameters($function, $class)
+    {
+        if (isset(self::$parameters[$k = $class.'::'.$function])) {
+            return self::$parameters[$k];
+        }
+
+        try {
+            $r = null !== $class ? new \ReflectionMethod($class, $function) : new \ReflectionFunction($function);
+        } catch (\ReflectionException $e) {
+            return [null, null];
+        }
+
+        $variadic = '...';
+        $params = [];
+        foreach ($r->getParameters() as $v) {
+            $k = '$'.$v->name;
+            if ($v->isPassedByReference()) {
+                $k = '&'.$k;
+            }
+            if ($v->isVariadic()) {
+                $variadic .= $k;
+            } else {
+                $params[] = $k;
+            }
+        }
+
+        return self::$parameters[$k] = [$variadic, $params];
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/Caster.php b/vendor/symfony/var-dumper/Caster/Caster.php
new file mode 100755
index 0000000..292bd67
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/Caster.php
@@ -0,0 +1,157 @@
+<?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\VarDumper\Caster;
+
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * Helper for filtering out properties in casters.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ *
+ * @final
+ */
+class Caster
+{
+    const EXCLUDE_VERBOSE = 1;
+    const EXCLUDE_VIRTUAL = 2;
+    const EXCLUDE_DYNAMIC = 4;
+    const EXCLUDE_PUBLIC = 8;
+    const EXCLUDE_PROTECTED = 16;
+    const EXCLUDE_PRIVATE = 32;
+    const EXCLUDE_NULL = 64;
+    const EXCLUDE_EMPTY = 128;
+    const EXCLUDE_NOT_IMPORTANT = 256;
+    const EXCLUDE_STRICT = 512;
+
+    const PREFIX_VIRTUAL = "\0~\0";
+    const PREFIX_DYNAMIC = "\0+\0";
+    const PREFIX_PROTECTED = "\0*\0";
+
+    /**
+     * Casts objects to arrays and adds the dynamic property prefix.
+     *
+     * @param object $obj          The object to cast
+     * @param string $class        The class of the object
+     * @param bool   $hasDebugInfo Whether the __debugInfo method exists on $obj or not
+     *
+     * @return array The array-cast of the object, with prefixed dynamic properties
+     */
+    public static function castObject($obj, $class, $hasDebugInfo = false)
+    {
+        if ($hasDebugInfo) {
+            $a = $obj->__debugInfo();
+        } elseif ($obj instanceof \Closure) {
+            $a = [];
+        } else {
+            $a = (array) $obj;
+        }
+        if ($obj instanceof \__PHP_Incomplete_Class) {
+            return $a;
+        }
+
+        if ($a) {
+            static $publicProperties = [];
+
+            $i = 0;
+            $prefixedKeys = [];
+            foreach ($a as $k => $v) {
+                if (isset($k[0]) ? "\0" !== $k[0] : \PHP_VERSION_ID >= 70200) {
+                    if (!isset($publicProperties[$class])) {
+                        foreach (get_class_vars($class) as $prop => $v) {
+                            $publicProperties[$class][$prop] = true;
+                        }
+                    }
+                    if (!isset($publicProperties[$class][$k])) {
+                        $prefixedKeys[$i] = self::PREFIX_DYNAMIC.$k;
+                    }
+                } elseif (isset($k[16]) && "\0" === $k[16] && 0 === strpos($k, "\0class@anonymous\0")) {
+                    $prefixedKeys[$i] = "\0".get_parent_class($class).'@anonymous'.strrchr($k, "\0");
+                }
+                ++$i;
+            }
+            if ($prefixedKeys) {
+                $keys = array_keys($a);
+                foreach ($prefixedKeys as $i => $k) {
+                    $keys[$i] = $k;
+                }
+                $a = array_combine($keys, $a);
+            }
+        }
+
+        return $a;
+    }
+
+    /**
+     * Filters out the specified properties.
+     *
+     * By default, a single match in the $filter bit field filters properties out, following an "or" logic.
+     * When EXCLUDE_STRICT is set, an "and" logic is applied: all bits must match for a property to be removed.
+     *
+     * @param array    $a                The array containing the properties to filter
+     * @param int      $filter           A bit field of Caster::EXCLUDE_* constants specifying which properties to filter out
+     * @param string[] $listedProperties List of properties to exclude when Caster::EXCLUDE_VERBOSE is set, and to preserve when Caster::EXCLUDE_NOT_IMPORTANT is set
+     * @param int      &$count           Set to the number of removed properties
+     *
+     * @return array The filtered array
+     */
+    public static function filter(array $a, $filter, array $listedProperties = [], &$count = 0)
+    {
+        $count = 0;
+
+        foreach ($a as $k => $v) {
+            $type = self::EXCLUDE_STRICT & $filter;
+
+            if (null === $v) {
+                $type |= self::EXCLUDE_NULL & $filter;
+                $type |= self::EXCLUDE_EMPTY & $filter;
+            } elseif (false === $v || '' === $v || '0' === $v || 0 === $v || 0.0 === $v || [] === $v) {
+                $type |= self::EXCLUDE_EMPTY & $filter;
+            }
+            if ((self::EXCLUDE_NOT_IMPORTANT & $filter) && !\in_array($k, $listedProperties, true)) {
+                $type |= self::EXCLUDE_NOT_IMPORTANT;
+            }
+            if ((self::EXCLUDE_VERBOSE & $filter) && \in_array($k, $listedProperties, true)) {
+                $type |= self::EXCLUDE_VERBOSE;
+            }
+
+            if (!isset($k[1]) || "\0" !== $k[0]) {
+                $type |= self::EXCLUDE_PUBLIC & $filter;
+            } elseif ('~' === $k[1]) {
+                $type |= self::EXCLUDE_VIRTUAL & $filter;
+            } elseif ('+' === $k[1]) {
+                $type |= self::EXCLUDE_DYNAMIC & $filter;
+            } elseif ('*' === $k[1]) {
+                $type |= self::EXCLUDE_PROTECTED & $filter;
+            } else {
+                $type |= self::EXCLUDE_PRIVATE & $filter;
+            }
+
+            if ((self::EXCLUDE_STRICT & $filter) ? $type === $filter : $type) {
+                unset($a[$k]);
+                ++$count;
+            }
+        }
+
+        return $a;
+    }
+
+    public static function castPhpIncompleteClass(\__PHP_Incomplete_Class $c, array $a, Stub $stub, $isNested)
+    {
+        if (isset($a['__PHP_Incomplete_Class_Name'])) {
+            $stub->class .= '('.$a['__PHP_Incomplete_Class_Name'].')';
+            unset($a['__PHP_Incomplete_Class_Name']);
+        }
+
+        return $a;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/ClassStub.php b/vendor/symfony/var-dumper/Caster/ClassStub.php
new file mode 100755
index 0000000..b655ae9
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/ClassStub.php
@@ -0,0 +1,106 @@
+<?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\VarDumper\Caster;
+
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * Represents a PHP class identifier.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class ClassStub extends ConstStub
+{
+    /**
+     * @param string   $identifier A PHP identifier, e.g. a class, method, interface, etc. name
+     * @param callable $callable   The callable targeted by the identifier when it is ambiguous or not a real PHP identifier
+     */
+    public function __construct(string $identifier, $callable = null)
+    {
+        $this->value = $identifier;
+
+        try {
+            if (null !== $callable) {
+                if ($callable instanceof \Closure) {
+                    $r = new \ReflectionFunction($callable);
+                } elseif (\is_object($callable)) {
+                    $r = [$callable, '__invoke'];
+                } elseif (\is_array($callable)) {
+                    $r = $callable;
+                } elseif (false !== $i = strpos($callable, '::')) {
+                    $r = [substr($callable, 0, $i), substr($callable, 2 + $i)];
+                } else {
+                    $r = new \ReflectionFunction($callable);
+                }
+            } elseif (0 < $i = strpos($identifier, '::') ?: strpos($identifier, '->')) {
+                $r = [substr($identifier, 0, $i), substr($identifier, 2 + $i)];
+            } else {
+                $r = new \ReflectionClass($identifier);
+            }
+
+            if (\is_array($r)) {
+                try {
+                    $r = new \ReflectionMethod($r[0], $r[1]);
+                } catch (\ReflectionException $e) {
+                    $r = new \ReflectionClass($r[0]);
+                }
+            }
+
+            if (false !== strpos($identifier, "class@anonymous\0")) {
+                $this->value = $identifier = preg_replace_callback('/class@anonymous\x00.*?\.php0x?[0-9a-fA-F]++/', function ($m) {
+                    return \class_exists($m[0], false) ? get_parent_class($m[0]).'@anonymous' : $m[0];
+                }, $identifier);
+            }
+
+            if (null !== $callable && $r instanceof \ReflectionFunctionAbstract) {
+                $s = ReflectionCaster::castFunctionAbstract($r, [], new Stub(), true);
+                $s = ReflectionCaster::getSignature($s);
+
+                if ('()' === substr($identifier, -2)) {
+                    $this->value = substr_replace($identifier, $s, -2);
+                } else {
+                    $this->value .= $s;
+                }
+            }
+        } catch (\ReflectionException $e) {
+            return;
+        } finally {
+            if (0 < $i = strrpos($this->value, '\\')) {
+                $this->attr['ellipsis'] = \strlen($this->value) - $i;
+                $this->attr['ellipsis-type'] = 'class';
+                $this->attr['ellipsis-tail'] = 1;
+            }
+        }
+
+        if ($f = $r->getFileName()) {
+            $this->attr['file'] = $f;
+            $this->attr['line'] = $r->getStartLine();
+        }
+    }
+
+    public static function wrapCallable($callable)
+    {
+        if (\is_object($callable) || !\is_callable($callable)) {
+            return $callable;
+        }
+
+        if (!\is_array($callable)) {
+            $callable = new static($callable, $callable);
+        } elseif (\is_string($callable[0])) {
+            $callable[0] = new static($callable[0], $callable);
+        } else {
+            $callable[1] = new static($callable[1], $callable);
+        }
+
+        return $callable;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/ConstStub.php b/vendor/symfony/var-dumper/Caster/ConstStub.php
new file mode 100755
index 0000000..15868b0
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/ConstStub.php
@@ -0,0 +1,33 @@
+<?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\VarDumper\Caster;
+
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * Represents a PHP constant and its value.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class ConstStub extends Stub
+{
+    public function __construct(string $name, $value = null)
+    {
+        $this->class = $name;
+        $this->value = 1 < \func_num_args() ? $value : $name;
+    }
+
+    public function __toString()
+    {
+        return (string) $this->value;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/CutArrayStub.php b/vendor/symfony/var-dumper/Caster/CutArrayStub.php
new file mode 100755
index 0000000..0e4fb36
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/CutArrayStub.php
@@ -0,0 +1,30 @@
+<?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\VarDumper\Caster;
+
+/**
+ * Represents a cut array.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class CutArrayStub extends CutStub
+{
+    public $preservedSubset;
+
+    public function __construct(array $value, array $preservedKeys)
+    {
+        parent::__construct($value);
+
+        $this->preservedSubset = array_intersect_key($value, array_flip($preservedKeys));
+        $this->cut -= \count($this->preservedSubset);
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/CutStub.php b/vendor/symfony/var-dumper/Caster/CutStub.php
new file mode 100755
index 0000000..690338f
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/CutStub.php
@@ -0,0 +1,59 @@
+<?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\VarDumper\Caster;
+
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * Represents the main properties of a PHP variable, pre-casted by a caster.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class CutStub extends Stub
+{
+    public function __construct($value)
+    {
+        $this->value = $value;
+
+        switch (\gettype($value)) {
+            case 'object':
+                $this->type = self::TYPE_OBJECT;
+                $this->class = \get_class($value);
+                $this->cut = -1;
+                break;
+
+            case 'array':
+                $this->type = self::TYPE_ARRAY;
+                $this->class = self::ARRAY_ASSOC;
+                $this->cut = $this->value = \count($value);
+                break;
+
+            case 'resource':
+            case 'unknown type':
+            case 'resource (closed)':
+                $this->type = self::TYPE_RESOURCE;
+                $this->handle = (int) $value;
+                if ('Unknown' === $this->class = @get_resource_type($value)) {
+                    $this->class = 'Closed';
+                }
+                $this->cut = -1;
+                break;
+
+            case 'string':
+                $this->type = self::TYPE_STRING;
+                $this->class = preg_match('//u', $value) ? self::STRING_UTF8 : self::STRING_BINARY;
+                $this->cut = self::STRING_BINARY === $this->class ? \strlen($value) : mb_strlen($value, 'UTF-8');
+                $this->value = '';
+                break;
+        }
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/DOMCaster.php b/vendor/symfony/var-dumper/Caster/DOMCaster.php
new file mode 100755
index 0000000..65151b4
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/DOMCaster.php
@@ -0,0 +1,302 @@
+<?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\VarDumper\Caster;
+
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * Casts DOM related classes to array representation.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class DOMCaster
+{
+    private static $errorCodes = [
+        DOM_PHP_ERR => 'DOM_PHP_ERR',
+        DOM_INDEX_SIZE_ERR => 'DOM_INDEX_SIZE_ERR',
+        DOMSTRING_SIZE_ERR => 'DOMSTRING_SIZE_ERR',
+        DOM_HIERARCHY_REQUEST_ERR => 'DOM_HIERARCHY_REQUEST_ERR',
+        DOM_WRONG_DOCUMENT_ERR => 'DOM_WRONG_DOCUMENT_ERR',
+        DOM_INVALID_CHARACTER_ERR => 'DOM_INVALID_CHARACTER_ERR',
+        DOM_NO_DATA_ALLOWED_ERR => 'DOM_NO_DATA_ALLOWED_ERR',
+        DOM_NO_MODIFICATION_ALLOWED_ERR => 'DOM_NO_MODIFICATION_ALLOWED_ERR',
+        DOM_NOT_FOUND_ERR => 'DOM_NOT_FOUND_ERR',
+        DOM_NOT_SUPPORTED_ERR => 'DOM_NOT_SUPPORTED_ERR',
+        DOM_INUSE_ATTRIBUTE_ERR => 'DOM_INUSE_ATTRIBUTE_ERR',
+        DOM_INVALID_STATE_ERR => 'DOM_INVALID_STATE_ERR',
+        DOM_SYNTAX_ERR => 'DOM_SYNTAX_ERR',
+        DOM_INVALID_MODIFICATION_ERR => 'DOM_INVALID_MODIFICATION_ERR',
+        DOM_NAMESPACE_ERR => 'DOM_NAMESPACE_ERR',
+        DOM_INVALID_ACCESS_ERR => 'DOM_INVALID_ACCESS_ERR',
+        DOM_VALIDATION_ERR => 'DOM_VALIDATION_ERR',
+    ];
+
+    private static $nodeTypes = [
+        XML_ELEMENT_NODE => 'XML_ELEMENT_NODE',
+        XML_ATTRIBUTE_NODE => 'XML_ATTRIBUTE_NODE',
+        XML_TEXT_NODE => 'XML_TEXT_NODE',
+        XML_CDATA_SECTION_NODE => 'XML_CDATA_SECTION_NODE',
+        XML_ENTITY_REF_NODE => 'XML_ENTITY_REF_NODE',
+        XML_ENTITY_NODE => 'XML_ENTITY_NODE',
+        XML_PI_NODE => 'XML_PI_NODE',
+        XML_COMMENT_NODE => 'XML_COMMENT_NODE',
+        XML_DOCUMENT_NODE => 'XML_DOCUMENT_NODE',
+        XML_DOCUMENT_TYPE_NODE => 'XML_DOCUMENT_TYPE_NODE',
+        XML_DOCUMENT_FRAG_NODE => 'XML_DOCUMENT_FRAG_NODE',
+        XML_NOTATION_NODE => 'XML_NOTATION_NODE',
+        XML_HTML_DOCUMENT_NODE => 'XML_HTML_DOCUMENT_NODE',
+        XML_DTD_NODE => 'XML_DTD_NODE',
+        XML_ELEMENT_DECL_NODE => 'XML_ELEMENT_DECL_NODE',
+        XML_ATTRIBUTE_DECL_NODE => 'XML_ATTRIBUTE_DECL_NODE',
+        XML_ENTITY_DECL_NODE => 'XML_ENTITY_DECL_NODE',
+        XML_NAMESPACE_DECL_NODE => 'XML_NAMESPACE_DECL_NODE',
+    ];
+
+    public static function castException(\DOMException $e, array $a, Stub $stub, $isNested)
+    {
+        $k = Caster::PREFIX_PROTECTED.'code';
+        if (isset($a[$k], self::$errorCodes[$a[$k]])) {
+            $a[$k] = new ConstStub(self::$errorCodes[$a[$k]], $a[$k]);
+        }
+
+        return $a;
+    }
+
+    public static function castLength($dom, array $a, Stub $stub, $isNested)
+    {
+        $a += [
+            'length' => $dom->length,
+        ];
+
+        return $a;
+    }
+
+    public static function castImplementation($dom, array $a, Stub $stub, $isNested)
+    {
+        $a += [
+            Caster::PREFIX_VIRTUAL.'Core' => '1.0',
+            Caster::PREFIX_VIRTUAL.'XML' => '2.0',
+        ];
+
+        return $a;
+    }
+
+    public static function castNode(\DOMNode $dom, array $a, Stub $stub, $isNested)
+    {
+        $a += [
+            'nodeName' => $dom->nodeName,
+            'nodeValue' => new CutStub($dom->nodeValue),
+            'nodeType' => new ConstStub(self::$nodeTypes[$dom->nodeType], $dom->nodeType),
+            'parentNode' => new CutStub($dom->parentNode),
+            'childNodes' => $dom->childNodes,
+            'firstChild' => new CutStub($dom->firstChild),
+            'lastChild' => new CutStub($dom->lastChild),
+            'previousSibling' => new CutStub($dom->previousSibling),
+            'nextSibling' => new CutStub($dom->nextSibling),
+            'attributes' => $dom->attributes,
+            'ownerDocument' => new CutStub($dom->ownerDocument),
+            'namespaceURI' => $dom->namespaceURI,
+            'prefix' => $dom->prefix,
+            'localName' => $dom->localName,
+            'baseURI' => $dom->baseURI ? new LinkStub($dom->baseURI) : $dom->baseURI,
+            'textContent' => new CutStub($dom->textContent),
+        ];
+
+        return $a;
+    }
+
+    public static function castNameSpaceNode(\DOMNameSpaceNode $dom, array $a, Stub $stub, $isNested)
+    {
+        $a += [
+            'nodeName' => $dom->nodeName,
+            'nodeValue' => new CutStub($dom->nodeValue),
+            'nodeType' => new ConstStub(self::$nodeTypes[$dom->nodeType], $dom->nodeType),
+            'prefix' => $dom->prefix,
+            'localName' => $dom->localName,
+            'namespaceURI' => $dom->namespaceURI,
+            'ownerDocument' => new CutStub($dom->ownerDocument),
+            'parentNode' => new CutStub($dom->parentNode),
+        ];
+
+        return $a;
+    }
+
+    public static function castDocument(\DOMDocument $dom, array $a, Stub $stub, $isNested, $filter = 0)
+    {
+        $a += [
+            'doctype' => $dom->doctype,
+            'implementation' => $dom->implementation,
+            'documentElement' => new CutStub($dom->documentElement),
+            'actualEncoding' => $dom->actualEncoding,
+            'encoding' => $dom->encoding,
+            'xmlEncoding' => $dom->xmlEncoding,
+            'standalone' => $dom->standalone,
+            'xmlStandalone' => $dom->xmlStandalone,
+            'version' => $dom->version,
+            'xmlVersion' => $dom->xmlVersion,
+            'strictErrorChecking' => $dom->strictErrorChecking,
+            'documentURI' => $dom->documentURI ? new LinkStub($dom->documentURI) : $dom->documentURI,
+            'config' => $dom->config,
+            'formatOutput' => $dom->formatOutput,
+            'validateOnParse' => $dom->validateOnParse,
+            'resolveExternals' => $dom->resolveExternals,
+            'preserveWhiteSpace' => $dom->preserveWhiteSpace,
+            'recover' => $dom->recover,
+            'substituteEntities' => $dom->substituteEntities,
+        ];
+
+        if (!($filter & Caster::EXCLUDE_VERBOSE)) {
+            $formatOutput = $dom->formatOutput;
+            $dom->formatOutput = true;
+            $a += [Caster::PREFIX_VIRTUAL.'xml' => $dom->saveXML()];
+            $dom->formatOutput = $formatOutput;
+        }
+
+        return $a;
+    }
+
+    public static function castCharacterData(\DOMCharacterData $dom, array $a, Stub $stub, $isNested)
+    {
+        $a += [
+            'data' => $dom->data,
+            'length' => $dom->length,
+        ];
+
+        return $a;
+    }
+
+    public static function castAttr(\DOMAttr $dom, array $a, Stub $stub, $isNested)
+    {
+        $a += [
+            'name' => $dom->name,
+            'specified' => $dom->specified,
+            'value' => $dom->value,
+            'ownerElement' => $dom->ownerElement,
+            'schemaTypeInfo' => $dom->schemaTypeInfo,
+        ];
+
+        return $a;
+    }
+
+    public static function castElement(\DOMElement $dom, array $a, Stub $stub, $isNested)
+    {
+        $a += [
+            'tagName' => $dom->tagName,
+            'schemaTypeInfo' => $dom->schemaTypeInfo,
+        ];
+
+        return $a;
+    }
+
+    public static function castText(\DOMText $dom, array $a, Stub $stub, $isNested)
+    {
+        $a += [
+            'wholeText' => $dom->wholeText,
+        ];
+
+        return $a;
+    }
+
+    public static function castTypeinfo(\DOMTypeinfo $dom, array $a, Stub $stub, $isNested)
+    {
+        $a += [
+            'typeName' => $dom->typeName,
+            'typeNamespace' => $dom->typeNamespace,
+        ];
+
+        return $a;
+    }
+
+    public static function castDomError(\DOMDomError $dom, array $a, Stub $stub, $isNested)
+    {
+        $a += [
+            'severity' => $dom->severity,
+            'message' => $dom->message,
+            'type' => $dom->type,
+            'relatedException' => $dom->relatedException,
+            'related_data' => $dom->related_data,
+            'location' => $dom->location,
+        ];
+
+        return $a;
+    }
+
+    public static function castLocator(\DOMLocator $dom, array $a, Stub $stub, $isNested)
+    {
+        $a += [
+            'lineNumber' => $dom->lineNumber,
+            'columnNumber' => $dom->columnNumber,
+            'offset' => $dom->offset,
+            'relatedNode' => $dom->relatedNode,
+            'uri' => $dom->uri ? new LinkStub($dom->uri, $dom->lineNumber) : $dom->uri,
+        ];
+
+        return $a;
+    }
+
+    public static function castDocumentType(\DOMDocumentType $dom, array $a, Stub $stub, $isNested)
+    {
+        $a += [
+            'name' => $dom->name,
+            'entities' => $dom->entities,
+            'notations' => $dom->notations,
+            'publicId' => $dom->publicId,
+            'systemId' => $dom->systemId,
+            'internalSubset' => $dom->internalSubset,
+        ];
+
+        return $a;
+    }
+
+    public static function castNotation(\DOMNotation $dom, array $a, Stub $stub, $isNested)
+    {
+        $a += [
+            'publicId' => $dom->publicId,
+            'systemId' => $dom->systemId,
+        ];
+
+        return $a;
+    }
+
+    public static function castEntity(\DOMEntity $dom, array $a, Stub $stub, $isNested)
+    {
+        $a += [
+            'publicId' => $dom->publicId,
+            'systemId' => $dom->systemId,
+            'notationName' => $dom->notationName,
+            'actualEncoding' => $dom->actualEncoding,
+            'encoding' => $dom->encoding,
+            'version' => $dom->version,
+        ];
+
+        return $a;
+    }
+
+    public static function castProcessingInstruction(\DOMProcessingInstruction $dom, array $a, Stub $stub, $isNested)
+    {
+        $a += [
+            'target' => $dom->target,
+            'data' => $dom->data,
+        ];
+
+        return $a;
+    }
+
+    public static function castXPath(\DOMXPath $dom, array $a, Stub $stub, $isNested)
+    {
+        $a += [
+            'document' => $dom->document,
+        ];
+
+        return $a;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/DateCaster.php b/vendor/symfony/var-dumper/Caster/DateCaster.php
new file mode 100755
index 0000000..29c123d
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/DateCaster.php
@@ -0,0 +1,122 @@
+<?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\VarDumper\Caster;
+
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * Casts DateTimeInterface related classes to array representation.
+ *
+ * @author Dany Maillard <danymaillard93b@gmail.com>
+ */
+class DateCaster
+{
+    private const PERIOD_LIMIT = 3;
+
+    public static function castDateTime(\DateTimeInterface $d, array $a, Stub $stub, $isNested, $filter)
+    {
+        $prefix = Caster::PREFIX_VIRTUAL;
+        $location = $d->getTimezone()->getLocation();
+        $fromNow = (new \DateTime())->diff($d);
+
+        $title = $d->format('l, F j, Y')
+            ."\n".self::formatInterval($fromNow).' from now'
+            .($location ? ($d->format('I') ? "\nDST On" : "\nDST Off") : '')
+        ;
+
+        $a = [];
+        $a[$prefix.'date'] = new ConstStub(self::formatDateTime($d, $location ? ' e (P)' : ' P'), $title);
+
+        $stub->class .= $d->format(' @U');
+
+        return $a;
+    }
+
+    public static function castInterval(\DateInterval $interval, array $a, Stub $stub, $isNested, $filter)
+    {
+        $now = new \DateTimeImmutable();
+        $numberOfSeconds = $now->add($interval)->getTimestamp() - $now->getTimestamp();
+        $title = number_format($numberOfSeconds, 0, '.', ' ').'s';
+
+        $i = [Caster::PREFIX_VIRTUAL.'interval' => new ConstStub(self::formatInterval($interval), $title)];
+
+        return $filter & Caster::EXCLUDE_VERBOSE ? $i : $i + $a;
+    }
+
+    private static function formatInterval(\DateInterval $i)
+    {
+        $format = '%R ';
+
+        if (0 === $i->y && 0 === $i->m && ($i->h >= 24 || $i->i >= 60 || $i->s >= 60)) {
+            $i = date_diff($d = new \DateTime(), date_add(clone $d, $i)); // recalculate carry over points
+            $format .= 0 < $i->days ? '%ad ' : '';
+        } else {
+            $format .= ($i->y ? '%yy ' : '').($i->m ? '%mm ' : '').($i->d ? '%dd ' : '');
+        }
+
+        $format .= $i->h || $i->i || $i->s || $i->f ? '%H:%I:'.self::formatSeconds($i->s, substr($i->f, 2)) : '';
+        $format = '%R ' === $format ? '0s' : $format;
+
+        return $i->format(rtrim($format));
+    }
+
+    public static function castTimeZone(\DateTimeZone $timeZone, array $a, Stub $stub, $isNested, $filter)
+    {
+        $location = $timeZone->getLocation();
+        $formatted = (new \DateTime('now', $timeZone))->format($location ? 'e (P)' : 'P');
+        $title = $location && \extension_loaded('intl') ? \Locale::getDisplayRegion('-'.$location['country_code']) : '';
+
+        $z = [Caster::PREFIX_VIRTUAL.'timezone' => new ConstStub($formatted, $title)];
+
+        return $filter & Caster::EXCLUDE_VERBOSE ? $z : $z + $a;
+    }
+
+    public static function castPeriod(\DatePeriod $p, array $a, Stub $stub, $isNested, $filter)
+    {
+        $dates = [];
+        if (\PHP_VERSION_ID >= 70107) { // see https://bugs.php.net/bug.php?id=74639
+            foreach (clone $p as $i => $d) {
+                if (self::PERIOD_LIMIT === $i) {
+                    $now = new \DateTimeImmutable();
+                    $dates[] = sprintf('%s more', ($end = $p->getEndDate())
+                        ? ceil(($end->format('U.u') - $d->format('U.u')) / ($now->add($p->getDateInterval())->format('U.u') - $now->format('U.u')))
+                        : $p->recurrences - $i
+                    );
+                    break;
+                }
+                $dates[] = sprintf('%s) %s', $i + 1, self::formatDateTime($d));
+            }
+        }
+
+        $period = sprintf(
+            'every %s, from %s (%s) %s',
+            self::formatInterval($p->getDateInterval()),
+            self::formatDateTime($p->getStartDate()),
+            $p->include_start_date ? 'included' : 'excluded',
+            ($end = $p->getEndDate()) ? 'to '.self::formatDateTime($end) : 'recurring '.$p->recurrences.' time/s'
+        );
+
+        $p = [Caster::PREFIX_VIRTUAL.'period' => new ConstStub($period, implode("\n", $dates))];
+
+        return $filter & Caster::EXCLUDE_VERBOSE ? $p : $p + $a;
+    }
+
+    private static function formatDateTime(\DateTimeInterface $d, $extra = '')
+    {
+        return $d->format('Y-m-d H:i:'.self::formatSeconds($d->format('s'), $d->format('u')).$extra);
+    }
+
+    private static function formatSeconds($s, $us)
+    {
+        return sprintf('%02d.%s', $s, 0 === ($len = \strlen($t = rtrim($us, '0'))) ? '0' : ($len <= 3 ? str_pad($t, 3, '0') : $us));
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/DoctrineCaster.php b/vendor/symfony/var-dumper/Caster/DoctrineCaster.php
new file mode 100755
index 0000000..696b878
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/DoctrineCaster.php
@@ -0,0 +1,60 @@
+<?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\VarDumper\Caster;
+
+use Doctrine\Common\Proxy\Proxy as CommonProxy;
+use Doctrine\ORM\PersistentCollection;
+use Doctrine\ORM\Proxy\Proxy as OrmProxy;
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * Casts Doctrine related classes to array representation.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class DoctrineCaster
+{
+    public static function castCommonProxy(CommonProxy $proxy, array $a, Stub $stub, $isNested)
+    {
+        foreach (['__cloner__', '__initializer__'] as $k) {
+            if (\array_key_exists($k, $a)) {
+                unset($a[$k]);
+                ++$stub->cut;
+            }
+        }
+
+        return $a;
+    }
+
+    public static function castOrmProxy(OrmProxy $proxy, array $a, Stub $stub, $isNested)
+    {
+        foreach (['_entityPersister', '_identifier'] as $k) {
+            if (\array_key_exists($k = "\0Doctrine\\ORM\\Proxy\\Proxy\0".$k, $a)) {
+                unset($a[$k]);
+                ++$stub->cut;
+            }
+        }
+
+        return $a;
+    }
+
+    public static function castPersistentCollection(PersistentCollection $coll, array $a, Stub $stub, $isNested)
+    {
+        foreach (['snapshot', 'association', 'typeClass'] as $k) {
+            if (\array_key_exists($k = "\0Doctrine\\ORM\\PersistentCollection\0".$k, $a)) {
+                $a[$k] = new CutStub($a[$k]);
+            }
+        }
+
+        return $a;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/DsCaster.php b/vendor/symfony/var-dumper/Caster/DsCaster.php
new file mode 100755
index 0000000..467aadf
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/DsCaster.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\VarDumper\Caster;
+
+use Ds\Collection;
+use Ds\Map;
+use Ds\Pair;
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * Casts Ds extension classes to array representation.
+ *
+ * @author Jáchym Toušek <enumag@gmail.com>
+ */
+class DsCaster
+{
+    public static function castCollection(Collection $c, array $a, Stub $stub, bool $isNested): array
+    {
+        $a[Caster::PREFIX_VIRTUAL.'count'] = $c->count();
+        $a[Caster::PREFIX_VIRTUAL.'capacity'] = $c->capacity();
+
+        if (!$c instanceof Map) {
+            $a += $c->toArray();
+        }
+
+        return $a;
+    }
+
+    public static function castMap(Map $c, array $a, Stub $stub, bool $isNested): array
+    {
+        foreach ($c as $k => $v) {
+            $a[] = new DsPairStub($k, $v);
+        }
+
+        return $a;
+    }
+
+    public static function castPair(Pair $c, array $a, Stub $stub, bool $isNested): array
+    {
+        foreach ($c->toArray() as $k => $v) {
+            $a[Caster::PREFIX_VIRTUAL.$k] = $v;
+        }
+
+        return $a;
+    }
+
+    public static function castPairStub(DsPairStub $c, array $a, Stub $stub, bool $isNested): array
+    {
+        if ($isNested) {
+            $stub->class = Pair::class;
+            $stub->value = null;
+            $stub->handle = 0;
+
+            $a = $c->value;
+        }
+
+        return $a;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/DsPairStub.php b/vendor/symfony/var-dumper/Caster/DsPairStub.php
new file mode 100755
index 0000000..a1dcc15
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/DsPairStub.php
@@ -0,0 +1,28 @@
+<?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\VarDumper\Caster;
+
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class DsPairStub extends Stub
+{
+    public function __construct($key, $value)
+    {
+        $this->value = [
+            Caster::PREFIX_VIRTUAL.'key' => $key,
+            Caster::PREFIX_VIRTUAL.'value' => $value,
+        ];
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/EnumStub.php b/vendor/symfony/var-dumper/Caster/EnumStub.php
new file mode 100755
index 0000000..7a4e98a
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/EnumStub.php
@@ -0,0 +1,30 @@
+<?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\VarDumper\Caster;
+
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * Represents an enumeration of values.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class EnumStub extends Stub
+{
+    public $dumpKeys = true;
+
+    public function __construct(array $values, bool $dumpKeys = true)
+    {
+        $this->value = $values;
+        $this->dumpKeys = $dumpKeys;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/ExceptionCaster.php b/vendor/symfony/var-dumper/Caster/ExceptionCaster.php
new file mode 100755
index 0000000..54f0ba1
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/ExceptionCaster.php
@@ -0,0 +1,357 @@
+<?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\VarDumper\Caster;
+
+use Symfony\Component\Debug\Exception\SilencedErrorContext;
+use Symfony\Component\VarDumper\Cloner\Stub;
+use Symfony\Component\VarDumper\Exception\ThrowingCasterException;
+
+/**
+ * Casts common Exception classes to array representation.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class ExceptionCaster
+{
+    public static $srcContext = 1;
+    public static $traceArgs = true;
+    public static $errorTypes = [
+        E_DEPRECATED => 'E_DEPRECATED',
+        E_USER_DEPRECATED => 'E_USER_DEPRECATED',
+        E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR',
+        E_ERROR => 'E_ERROR',
+        E_WARNING => 'E_WARNING',
+        E_PARSE => 'E_PARSE',
+        E_NOTICE => 'E_NOTICE',
+        E_CORE_ERROR => 'E_CORE_ERROR',
+        E_CORE_WARNING => 'E_CORE_WARNING',
+        E_COMPILE_ERROR => 'E_COMPILE_ERROR',
+        E_COMPILE_WARNING => 'E_COMPILE_WARNING',
+        E_USER_ERROR => 'E_USER_ERROR',
+        E_USER_WARNING => 'E_USER_WARNING',
+        E_USER_NOTICE => 'E_USER_NOTICE',
+        E_STRICT => 'E_STRICT',
+    ];
+
+    private static $framesCache = [];
+
+    public static function castError(\Error $e, array $a, Stub $stub, $isNested, $filter = 0)
+    {
+        return self::filterExceptionArray($stub->class, $a, "\0Error\0", $filter);
+    }
+
+    public static function castException(\Exception $e, array $a, Stub $stub, $isNested, $filter = 0)
+    {
+        return self::filterExceptionArray($stub->class, $a, "\0Exception\0", $filter);
+    }
+
+    public static function castErrorException(\ErrorException $e, array $a, Stub $stub, $isNested)
+    {
+        if (isset($a[$s = Caster::PREFIX_PROTECTED.'severity'], self::$errorTypes[$a[$s]])) {
+            $a[$s] = new ConstStub(self::$errorTypes[$a[$s]], $a[$s]);
+        }
+
+        return $a;
+    }
+
+    public static function castThrowingCasterException(ThrowingCasterException $e, array $a, Stub $stub, $isNested)
+    {
+        $trace = Caster::PREFIX_VIRTUAL.'trace';
+        $prefix = Caster::PREFIX_PROTECTED;
+        $xPrefix = "\0Exception\0";
+
+        if (isset($a[$xPrefix.'previous'], $a[$trace]) && $a[$xPrefix.'previous'] instanceof \Exception) {
+            $b = (array) $a[$xPrefix.'previous'];
+            $class = \get_class($a[$xPrefix.'previous']);
+            $class = 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class;
+            self::traceUnshift($b[$xPrefix.'trace'], $class, $b[$prefix.'file'], $b[$prefix.'line']);
+            $a[$trace] = new TraceStub($b[$xPrefix.'trace'], false, 0, -\count($a[$trace]->value));
+        }
+
+        unset($a[$xPrefix.'previous'], $a[$prefix.'code'], $a[$prefix.'file'], $a[$prefix.'line']);
+
+        return $a;
+    }
+
+    public static function castSilencedErrorContext(SilencedErrorContext $e, array $a, Stub $stub, $isNested)
+    {
+        $sPrefix = "\0".SilencedErrorContext::class."\0";
+
+        if (!isset($a[$s = $sPrefix.'severity'])) {
+            return $a;
+        }
+
+        if (isset(self::$errorTypes[$a[$s]])) {
+            $a[$s] = new ConstStub(self::$errorTypes[$a[$s]], $a[$s]);
+        }
+
+        $trace = [[
+            'file' => $a[$sPrefix.'file'],
+            'line' => $a[$sPrefix.'line'],
+        ]];
+
+        if (isset($a[$sPrefix.'trace'])) {
+            $trace = array_merge($trace, $a[$sPrefix.'trace']);
+        }
+
+        unset($a[$sPrefix.'file'], $a[$sPrefix.'line'], $a[$sPrefix.'trace']);
+        $a[Caster::PREFIX_VIRTUAL.'trace'] = new TraceStub($trace, self::$traceArgs);
+
+        return $a;
+    }
+
+    public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, $isNested)
+    {
+        if (!$isNested) {
+            return $a;
+        }
+        $stub->class = '';
+        $stub->handle = 0;
+        $frames = $trace->value;
+        $prefix = Caster::PREFIX_VIRTUAL;
+
+        $a = [];
+        $j = \count($frames);
+        if (0 > $i = $trace->sliceOffset) {
+            $i = max(0, $j + $i);
+        }
+        if (!isset($trace->value[$i])) {
+            return [];
+        }
+        $lastCall = isset($frames[$i]['function']) ? (isset($frames[$i]['class']) ? $frames[0]['class'].$frames[$i]['type'] : '').$frames[$i]['function'].'()' : '';
+        $frames[] = ['function' => ''];
+        $collapse = false;
+
+        for ($j += $trace->numberingOffset - $i++; isset($frames[$i]); ++$i, --$j) {
+            $f = $frames[$i];
+            $call = isset($f['function']) ? (isset($f['class']) ? $f['class'].$f['type'] : '').$f['function'] : '???';
+
+            $frame = new FrameStub(
+                [
+                    'object' => isset($f['object']) ? $f['object'] : null,
+                    'class' => isset($f['class']) ? $f['class'] : null,
+                    'type' => isset($f['type']) ? $f['type'] : null,
+                    'function' => isset($f['function']) ? $f['function'] : null,
+                ] + $frames[$i - 1],
+                false,
+                true
+            );
+            $f = self::castFrameStub($frame, [], $frame, true);
+            if (isset($f[$prefix.'src'])) {
+                foreach ($f[$prefix.'src']->value as $label => $frame) {
+                    if (0 === strpos($label, "\0~collapse=0")) {
+                        if ($collapse) {
+                            $label = substr_replace($label, '1', 11, 1);
+                        } else {
+                            $collapse = true;
+                        }
+                    }
+                    $label = substr_replace($label, "title=Stack level $j.&", 2, 0);
+                }
+                $f = $frames[$i - 1];
+                if ($trace->keepArgs && !empty($f['args']) && $frame instanceof EnumStub) {
+                    $frame->value['arguments'] = new ArgsStub($f['args'], isset($f['function']) ? $f['function'] : null, isset($f['class']) ? $f['class'] : null);
+                }
+            } elseif ('???' !== $lastCall) {
+                $label = new ClassStub($lastCall);
+                if (isset($label->attr['ellipsis'])) {
+                    $label->attr['ellipsis'] += 2;
+                    $label = substr_replace($prefix, "ellipsis-type=class&ellipsis={$label->attr['ellipsis']}&ellipsis-tail=1&title=Stack level $j.", 2, 0).$label->value.'()';
+                } else {
+                    $label = substr_replace($prefix, "title=Stack level $j.", 2, 0).$label->value.'()';
+                }
+            } else {
+                $label = substr_replace($prefix, "title=Stack level $j.", 2, 0).$lastCall;
+            }
+            $a[substr_replace($label, sprintf('separator=%s&', $frame instanceof EnumStub ? ' ' : ':'), 2, 0)] = $frame;
+
+            $lastCall = $call;
+        }
+        if (null !== $trace->sliceLength) {
+            $a = \array_slice($a, 0, $trace->sliceLength, true);
+        }
+
+        return $a;
+    }
+
+    public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, $isNested)
+    {
+        if (!$isNested) {
+            return $a;
+        }
+        $f = $frame->value;
+        $prefix = Caster::PREFIX_VIRTUAL;
+
+        if (isset($f['file'], $f['line'])) {
+            $cacheKey = $f;
+            unset($cacheKey['object'], $cacheKey['args']);
+            $cacheKey[] = self::$srcContext;
+            $cacheKey = implode('-', $cacheKey);
+
+            if (isset(self::$framesCache[$cacheKey])) {
+                $a[$prefix.'src'] = self::$framesCache[$cacheKey];
+            } else {
+                if (preg_match('/\((\d+)\)(?:\([\da-f]{32}\))? : (?:eval\(\)\'d code|runtime-created function)$/', $f['file'], $match)) {
+                    $f['file'] = substr($f['file'], 0, -\strlen($match[0]));
+                    $f['line'] = (int) $match[1];
+                }
+                $caller = isset($f['function']) ? sprintf('in %s() on line %d', (isset($f['class']) ? $f['class'].$f['type'] : '').$f['function'], $f['line']) : null;
+                $src = $f['line'];
+                $srcKey = $f['file'];
+                $ellipsis = new LinkStub($srcKey, 0);
+                $srcAttr = 'collapse='.(int) $ellipsis->inVendor;
+                $ellipsisTail = isset($ellipsis->attr['ellipsis-tail']) ? $ellipsis->attr['ellipsis-tail'] : 0;
+                $ellipsis = isset($ellipsis->attr['ellipsis']) ? $ellipsis->attr['ellipsis'] : 0;
+
+                if (file_exists($f['file']) && 0 <= self::$srcContext) {
+                    if (!empty($f['class']) && (is_subclass_of($f['class'], 'Twig\Template') || is_subclass_of($f['class'], 'Twig_Template')) && method_exists($f['class'], 'getDebugInfo')) {
+                        $template = isset($f['object']) ? $f['object'] : unserialize(sprintf('O:%d:"%s":0:{}', \strlen($f['class']), $f['class']));
+
+                        $ellipsis = 0;
+                        $templateSrc = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : '');
+                        $templateInfo = $template->getDebugInfo();
+                        if (isset($templateInfo[$f['line']])) {
+                            if (!method_exists($template, 'getSourceContext') || !file_exists($templatePath = $template->getSourceContext()->getPath())) {
+                                $templatePath = null;
+                            }
+                            if ($templateSrc) {
+                                $src = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext, $caller, 'twig', $templatePath);
+                                $srcKey = ($templatePath ?: $template->getTemplateName()).':'.$templateInfo[$f['line']];
+                            }
+                        }
+                    }
+                    if ($srcKey == $f['file']) {
+                        $src = self::extractSource(file_get_contents($f['file']), $f['line'], self::$srcContext, $caller, 'php', $f['file']);
+                        $srcKey .= ':'.$f['line'];
+                        if ($ellipsis) {
+                            $ellipsis += 1 + \strlen($f['line']);
+                        }
+                    }
+                    $srcAttr .= sprintf('&separator= &file=%s&line=%d', rawurlencode($f['file']), $f['line']);
+                } else {
+                    $srcAttr .= '&separator=:';
+                }
+                $srcAttr .= $ellipsis ? '&ellipsis-type=path&ellipsis='.$ellipsis.'&ellipsis-tail='.$ellipsisTail : '';
+                self::$framesCache[$cacheKey] = $a[$prefix.'src'] = new EnumStub(["\0~$srcAttr\0$srcKey" => $src]);
+            }
+        }
+
+        unset($a[$prefix.'args'], $a[$prefix.'line'], $a[$prefix.'file']);
+        if ($frame->inTraceStub) {
+            unset($a[$prefix.'class'], $a[$prefix.'type'], $a[$prefix.'function']);
+        }
+        foreach ($a as $k => $v) {
+            if (!$v) {
+                unset($a[$k]);
+            }
+        }
+        if ($frame->keepArgs && !empty($f['args'])) {
+            $a[$prefix.'arguments'] = new ArgsStub($f['args'], $f['function'], $f['class']);
+        }
+
+        return $a;
+    }
+
+    private static function filterExceptionArray($xClass, array $a, $xPrefix, $filter)
+    {
+        if (isset($a[$xPrefix.'trace'])) {
+            $trace = $a[$xPrefix.'trace'];
+            unset($a[$xPrefix.'trace']); // Ensures the trace is always last
+        } else {
+            $trace = [];
+        }
+
+        if (!($filter & Caster::EXCLUDE_VERBOSE) && $trace) {
+            if (isset($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line'])) {
+                self::traceUnshift($trace, $xClass, $a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line']);
+            }
+            $a[Caster::PREFIX_VIRTUAL.'trace'] = new TraceStub($trace, self::$traceArgs);
+        }
+        if (empty($a[$xPrefix.'previous'])) {
+            unset($a[$xPrefix.'previous']);
+        }
+        unset($a[$xPrefix.'string'], $a[Caster::PREFIX_DYNAMIC.'xdebug_message'], $a[Caster::PREFIX_DYNAMIC.'__destructorException']);
+
+        if (isset($a[Caster::PREFIX_PROTECTED.'message']) && false !== strpos($a[Caster::PREFIX_PROTECTED.'message'], "class@anonymous\0")) {
+            $a[Caster::PREFIX_PROTECTED.'message'] = preg_replace_callback('/class@anonymous\x00.*?\.php0x?[0-9a-fA-F]++/', function ($m) {
+                return \class_exists($m[0], false) ? get_parent_class($m[0]).'@anonymous' : $m[0];
+            }, $a[Caster::PREFIX_PROTECTED.'message']);
+        }
+
+        if (isset($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line'])) {
+            $a[Caster::PREFIX_PROTECTED.'file'] = new LinkStub($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line']);
+        }
+
+        return $a;
+    }
+
+    private static function traceUnshift(&$trace, $class, $file, $line)
+    {
+        if (isset($trace[0]['file'], $trace[0]['line']) && $trace[0]['file'] === $file && $trace[0]['line'] === $line) {
+            return;
+        }
+        array_unshift($trace, [
+            'function' => $class ? 'new '.$class : null,
+            'file' => $file,
+            'line' => $line,
+        ]);
+    }
+
+    private static function extractSource($srcLines, $line, $srcContext, $title, $lang, $file = null)
+    {
+        $srcLines = explode("\n", $srcLines);
+        $src = [];
+
+        for ($i = $line - 1 - $srcContext; $i <= $line - 1 + $srcContext; ++$i) {
+            $src[] = (isset($srcLines[$i]) ? $srcLines[$i] : '')."\n";
+        }
+
+        $srcLines = [];
+        $ltrim = 0;
+        do {
+            $pad = null;
+            for ($i = $srcContext << 1; $i >= 0; --$i) {
+                if (isset($src[$i][$ltrim]) && "\r" !== ($c = $src[$i][$ltrim]) && "\n" !== $c) {
+                    if (null === $pad) {
+                        $pad = $c;
+                    }
+                    if ((' ' !== $c && "\t" !== $c) || $pad !== $c) {
+                        break;
+                    }
+                }
+            }
+            ++$ltrim;
+        } while (0 > $i && null !== $pad);
+
+        --$ltrim;
+
+        foreach ($src as $i => $c) {
+            if ($ltrim) {
+                $c = isset($c[$ltrim]) && "\r" !== $c[$ltrim] ? substr($c, $ltrim) : ltrim($c, " \t");
+            }
+            $c = substr($c, 0, -1);
+            if ($i !== $srcContext) {
+                $c = new ConstStub('default', $c);
+            } else {
+                $c = new ConstStub($c, $title);
+                if (null !== $file) {
+                    $c->attr['file'] = $file;
+                    $c->attr['line'] = $line;
+                }
+            }
+            $c->attr['lang'] = $lang;
+            $srcLines[sprintf("\0~separator=› &%d\0", $i + $line - $srcContext)] = $c;
+        }
+
+        return new EnumStub($srcLines);
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/FrameStub.php b/vendor/symfony/var-dumper/Caster/FrameStub.php
new file mode 100755
index 0000000..8786755
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/FrameStub.php
@@ -0,0 +1,30 @@
+<?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\VarDumper\Caster;
+
+/**
+ * Represents a single backtrace frame as returned by debug_backtrace() or Exception->getTrace().
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class FrameStub extends EnumStub
+{
+    public $keepArgs;
+    public $inTraceStub;
+
+    public function __construct(array $frame, bool $keepArgs = true, bool $inTraceStub = false)
+    {
+        $this->value = $frame;
+        $this->keepArgs = $keepArgs;
+        $this->inTraceStub = $inTraceStub;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/GmpCaster.php b/vendor/symfony/var-dumper/Caster/GmpCaster.php
new file mode 100755
index 0000000..504dc07
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/GmpCaster.php
@@ -0,0 +1,30 @@
+<?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\VarDumper\Caster;
+
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * Casts GMP objects to array representation.
+ *
+ * @author Hamza Amrouche <hamza.simperfit@gmail.com>
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class GmpCaster
+{
+    public static function castGmp(\GMP $gmp, array $a, Stub $stub, $isNested, $filter): array
+    {
+        $a[Caster::PREFIX_VIRTUAL.'value'] = new ConstStub(gmp_strval($gmp), gmp_strval($gmp));
+
+        return $a;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/IntlCaster.php b/vendor/symfony/var-dumper/Caster/IntlCaster.php
new file mode 100755
index 0000000..31d5cb3
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/IntlCaster.php
@@ -0,0 +1,170 @@
+<?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\VarDumper\Caster;
+
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ * @author Jan Schädlich <jan.schaedlich@sensiolabs.de>
+ */
+class IntlCaster
+{
+    public static function castMessageFormatter(\MessageFormatter $c, array $a, Stub $stub, $isNested)
+    {
+        $a += [
+            Caster::PREFIX_VIRTUAL.'locale' => $c->getLocale(),
+            Caster::PREFIX_VIRTUAL.'pattern' => $c->getPattern(),
+        ];
+
+        return self::castError($c, $a);
+    }
+
+    public static function castNumberFormatter(\NumberFormatter $c, array $a, Stub $stub, $isNested, $filter = 0)
+    {
+        $a += [
+            Caster::PREFIX_VIRTUAL.'locale' => $c->getLocale(),
+            Caster::PREFIX_VIRTUAL.'pattern' => $c->getPattern(),
+        ];
+
+        if ($filter & Caster::EXCLUDE_VERBOSE) {
+            $stub->cut += 3;
+
+            return self::castError($c, $a);
+        }
+
+        $a += [
+            Caster::PREFIX_VIRTUAL.'attributes' => new EnumStub(
+                [
+                    'PARSE_INT_ONLY' => $c->getAttribute(\NumberFormatter::PARSE_INT_ONLY),
+                    'GROUPING_USED' => $c->getAttribute(\NumberFormatter::GROUPING_USED),
+                    'DECIMAL_ALWAYS_SHOWN' => $c->getAttribute(\NumberFormatter::DECIMAL_ALWAYS_SHOWN),
+                    'MAX_INTEGER_DIGITS' => $c->getAttribute(\NumberFormatter::MAX_INTEGER_DIGITS),
+                    'MIN_INTEGER_DIGITS' => $c->getAttribute(\NumberFormatter::MIN_INTEGER_DIGITS),
+                    'INTEGER_DIGITS' => $c->getAttribute(\NumberFormatter::INTEGER_DIGITS),
+                    'MAX_FRACTION_DIGITS' => $c->getAttribute(\NumberFormatter::MAX_FRACTION_DIGITS),
+                    'MIN_FRACTION_DIGITS' => $c->getAttribute(\NumberFormatter::MIN_FRACTION_DIGITS),
+                    'FRACTION_DIGITS' => $c->getAttribute(\NumberFormatter::FRACTION_DIGITS),
+                    'MULTIPLIER' => $c->getAttribute(\NumberFormatter::MULTIPLIER),
+                    'GROUPING_SIZE' => $c->getAttribute(\NumberFormatter::GROUPING_SIZE),
+                    'ROUNDING_MODE' => $c->getAttribute(\NumberFormatter::ROUNDING_MODE),
+                    'ROUNDING_INCREMENT' => $c->getAttribute(\NumberFormatter::ROUNDING_INCREMENT),
+                    'FORMAT_WIDTH' => $c->getAttribute(\NumberFormatter::FORMAT_WIDTH),
+                    'PADDING_POSITION' => $c->getAttribute(\NumberFormatter::PADDING_POSITION),
+                    'SECONDARY_GROUPING_SIZE' => $c->getAttribute(\NumberFormatter::SECONDARY_GROUPING_SIZE),
+                    'SIGNIFICANT_DIGITS_USED' => $c->getAttribute(\NumberFormatter::SIGNIFICANT_DIGITS_USED),
+                    'MIN_SIGNIFICANT_DIGITS' => $c->getAttribute(\NumberFormatter::MIN_SIGNIFICANT_DIGITS),
+                    'MAX_SIGNIFICANT_DIGITS' => $c->getAttribute(\NumberFormatter::MAX_SIGNIFICANT_DIGITS),
+                    'LENIENT_PARSE' => $c->getAttribute(\NumberFormatter::LENIENT_PARSE),
+                ]
+            ),
+            Caster::PREFIX_VIRTUAL.'text_attributes' => new EnumStub(
+                [
+                    'POSITIVE_PREFIX' => $c->getTextAttribute(\NumberFormatter::POSITIVE_PREFIX),
+                    'POSITIVE_SUFFIX' => $c->getTextAttribute(\NumberFormatter::POSITIVE_SUFFIX),
+                    'NEGATIVE_PREFIX' => $c->getTextAttribute(\NumberFormatter::NEGATIVE_PREFIX),
+                    'NEGATIVE_SUFFIX' => $c->getTextAttribute(\NumberFormatter::NEGATIVE_SUFFIX),
+                    'PADDING_CHARACTER' => $c->getTextAttribute(\NumberFormatter::PADDING_CHARACTER),
+                    'CURRENCY_CODE' => $c->getTextAttribute(\NumberFormatter::CURRENCY_CODE),
+                    'DEFAULT_RULESET' => $c->getTextAttribute(\NumberFormatter::DEFAULT_RULESET),
+                    'PUBLIC_RULESETS' => $c->getTextAttribute(\NumberFormatter::PUBLIC_RULESETS),
+                ]
+            ),
+            Caster::PREFIX_VIRTUAL.'symbols' => new EnumStub(
+                [
+                    'DECIMAL_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL),
+                    'GROUPING_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL),
+                    'PATTERN_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::PATTERN_SEPARATOR_SYMBOL),
+                    'PERCENT_SYMBOL' => $c->getSymbol(\NumberFormatter::PERCENT_SYMBOL),
+                    'ZERO_DIGIT_SYMBOL' => $c->getSymbol(\NumberFormatter::ZERO_DIGIT_SYMBOL),
+                    'DIGIT_SYMBOL' => $c->getSymbol(\NumberFormatter::DIGIT_SYMBOL),
+                    'MINUS_SIGN_SYMBOL' => $c->getSymbol(\NumberFormatter::MINUS_SIGN_SYMBOL),
+                    'PLUS_SIGN_SYMBOL' => $c->getSymbol(\NumberFormatter::PLUS_SIGN_SYMBOL),
+                    'CURRENCY_SYMBOL' => $c->getSymbol(\NumberFormatter::CURRENCY_SYMBOL),
+                    'INTL_CURRENCY_SYMBOL' => $c->getSymbol(\NumberFormatter::INTL_CURRENCY_SYMBOL),
+                    'MONETARY_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::MONETARY_SEPARATOR_SYMBOL),
+                    'EXPONENTIAL_SYMBOL' => $c->getSymbol(\NumberFormatter::EXPONENTIAL_SYMBOL),
+                    'PERMILL_SYMBOL' => $c->getSymbol(\NumberFormatter::PERMILL_SYMBOL),
+                    'PAD_ESCAPE_SYMBOL' => $c->getSymbol(\NumberFormatter::PAD_ESCAPE_SYMBOL),
+                    'INFINITY_SYMBOL' => $c->getSymbol(\NumberFormatter::INFINITY_SYMBOL),
+                    'NAN_SYMBOL' => $c->getSymbol(\NumberFormatter::NAN_SYMBOL),
+                    'SIGNIFICANT_DIGIT_SYMBOL' => $c->getSymbol(\NumberFormatter::SIGNIFICANT_DIGIT_SYMBOL),
+                    'MONETARY_GROUPING_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL),
+                ]
+             ),
+        ];
+
+        return self::castError($c, $a);
+    }
+
+    public static function castIntlTimeZone(\IntlTimeZone $c, array $a, Stub $stub, $isNested)
+    {
+        $a += [
+            Caster::PREFIX_VIRTUAL.'display_name' => $c->getDisplayName(),
+            Caster::PREFIX_VIRTUAL.'id' => $c->getID(),
+            Caster::PREFIX_VIRTUAL.'raw_offset' => $c->getRawOffset(),
+        ];
+
+        if ($c->useDaylightTime()) {
+            $a += [
+                Caster::PREFIX_VIRTUAL.'dst_savings' => $c->getDSTSavings(),
+            ];
+        }
+
+        return self::castError($c, $a);
+    }
+
+    public static function castIntlCalendar(\IntlCalendar $c, array $a, Stub $stub, $isNested, $filter = 0)
+    {
+        $a += [
+            Caster::PREFIX_VIRTUAL.'type' => $c->getType(),
+            Caster::PREFIX_VIRTUAL.'first_day_of_week' => $c->getFirstDayOfWeek(),
+            Caster::PREFIX_VIRTUAL.'minimal_days_in_first_week' => $c->getMinimalDaysInFirstWeek(),
+            Caster::PREFIX_VIRTUAL.'repeated_wall_time_option' => $c->getRepeatedWallTimeOption(),
+            Caster::PREFIX_VIRTUAL.'skipped_wall_time_option' => $c->getSkippedWallTimeOption(),
+            Caster::PREFIX_VIRTUAL.'time' => $c->getTime(),
+            Caster::PREFIX_VIRTUAL.'in_daylight_time' => $c->inDaylightTime(),
+            Caster::PREFIX_VIRTUAL.'is_lenient' => $c->isLenient(),
+            Caster::PREFIX_VIRTUAL.'time_zone' => ($filter & Caster::EXCLUDE_VERBOSE) ? new CutStub($c->getTimeZone()) : $c->getTimeZone(),
+        ];
+
+        return self::castError($c, $a);
+    }
+
+    public static function castIntlDateFormatter(\IntlDateFormatter $c, array $a, Stub $stub, $isNested, $filter = 0)
+    {
+        $a += [
+            Caster::PREFIX_VIRTUAL.'locale' => $c->getLocale(),
+            Caster::PREFIX_VIRTUAL.'pattern' => $c->getPattern(),
+            Caster::PREFIX_VIRTUAL.'calendar' => $c->getCalendar(),
+            Caster::PREFIX_VIRTUAL.'time_zone_id' => $c->getTimeZoneId(),
+            Caster::PREFIX_VIRTUAL.'time_type' => $c->getTimeType(),
+            Caster::PREFIX_VIRTUAL.'date_type' => $c->getDateType(),
+            Caster::PREFIX_VIRTUAL.'calendar_object' => ($filter & Caster::EXCLUDE_VERBOSE) ? new CutStub($c->getCalendarObject()) : $c->getCalendarObject(),
+            Caster::PREFIX_VIRTUAL.'time_zone' => ($filter & Caster::EXCLUDE_VERBOSE) ? new CutStub($c->getTimeZone()) : $c->getTimeZone(),
+        ];
+
+        return self::castError($c, $a);
+    }
+
+    private static function castError($c, array $a): array
+    {
+        if ($errorCode = $c->getErrorCode()) {
+            $a += [
+                Caster::PREFIX_VIRTUAL.'error_code' => $errorCode,
+                Caster::PREFIX_VIRTUAL.'error_message' => $c->getErrorMessage(),
+            ];
+        }
+
+        return $a;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/LinkStub.php b/vendor/symfony/var-dumper/Caster/LinkStub.php
new file mode 100755
index 0000000..84a8b10
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/LinkStub.php
@@ -0,0 +1,108 @@
+<?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\VarDumper\Caster;
+
+/**
+ * Represents a file or a URL.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class LinkStub extends ConstStub
+{
+    public $inVendor = false;
+
+    private static $vendorRoots;
+    private static $composerRoots;
+
+    public function __construct($label, int $line = 0, $href = null)
+    {
+        $this->value = $label;
+
+        if (null === $href) {
+            $href = $label;
+        }
+        if (!\is_string($href)) {
+            return;
+        }
+        if (0 === strpos($href, 'file://')) {
+            if ($href === $label) {
+                $label = substr($label, 7);
+            }
+            $href = substr($href, 7);
+        } elseif (false !== strpos($href, '://')) {
+            $this->attr['href'] = $href;
+
+            return;
+        }
+        if (!file_exists($href)) {
+            return;
+        }
+        if ($line) {
+            $this->attr['line'] = $line;
+        }
+        if ($label !== $this->attr['file'] = realpath($href) ?: $href) {
+            return;
+        }
+        if ($composerRoot = $this->getComposerRoot($href, $this->inVendor)) {
+            $this->attr['ellipsis'] = \strlen($href) - \strlen($composerRoot) + 1;
+            $this->attr['ellipsis-type'] = 'path';
+            $this->attr['ellipsis-tail'] = 1 + ($this->inVendor ? 2 + \strlen(implode('', \array_slice(explode(\DIRECTORY_SEPARATOR, substr($href, 1 - $this->attr['ellipsis'])), 0, 2))) : 0);
+        } elseif (3 < \count($ellipsis = explode(\DIRECTORY_SEPARATOR, $href))) {
+            $this->attr['ellipsis'] = 2 + \strlen(implode('', \array_slice($ellipsis, -2)));
+            $this->attr['ellipsis-type'] = 'path';
+            $this->attr['ellipsis-tail'] = 1;
+        }
+    }
+
+    private function getComposerRoot($file, &$inVendor)
+    {
+        if (null === self::$vendorRoots) {
+            self::$vendorRoots = [];
+
+            foreach (get_declared_classes() as $class) {
+                if ('C' === $class[0] && 0 === strpos($class, 'ComposerAutoloaderInit')) {
+                    $r = new \ReflectionClass($class);
+                    $v = \dirname(\dirname($r->getFileName()));
+                    if (file_exists($v.'/composer/installed.json')) {
+                        self::$vendorRoots[] = $v.\DIRECTORY_SEPARATOR;
+                    }
+                }
+            }
+        }
+        $inVendor = false;
+
+        if (isset(self::$composerRoots[$dir = \dirname($file)])) {
+            return self::$composerRoots[$dir];
+        }
+
+        foreach (self::$vendorRoots as $root) {
+            if ($inVendor = 0 === strpos($file, $root)) {
+                return $root;
+            }
+        }
+
+        $parent = $dir;
+        while (!@file_exists($parent.'/composer.json')) {
+            if (!@file_exists($parent)) {
+                // open_basedir restriction in effect
+                break;
+            }
+            if ($parent === \dirname($parent)) {
+                return self::$composerRoots[$dir] = false;
+            }
+
+            $parent = \dirname($parent);
+        }
+
+        return self::$composerRoots[$dir] = $parent.\DIRECTORY_SEPARATOR;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/MemcachedCaster.php b/vendor/symfony/var-dumper/Caster/MemcachedCaster.php
new file mode 100755
index 0000000..a326546
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/MemcachedCaster.php
@@ -0,0 +1,79 @@
+<?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\VarDumper\Caster;
+
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * @author Jan Schädlich <jan.schaedlich@sensiolabs.de>
+ */
+class MemcachedCaster
+{
+    private static $optionConstants;
+    private static $defaultOptions;
+
+    public static function castMemcached(\Memcached $c, array $a, Stub $stub, $isNested)
+    {
+        $a += [
+            Caster::PREFIX_VIRTUAL.'servers' => $c->getServerList(),
+            Caster::PREFIX_VIRTUAL.'options' => new EnumStub(
+                self::getNonDefaultOptions($c)
+            ),
+        ];
+
+        return $a;
+    }
+
+    private static function getNonDefaultOptions(\Memcached $c)
+    {
+        self::$defaultOptions = self::$defaultOptions ?? self::discoverDefaultOptions();
+        self::$optionConstants = self::$optionConstants ?? self::getOptionConstants();
+
+        $nonDefaultOptions = [];
+        foreach (self::$optionConstants as $constantKey => $value) {
+            if (self::$defaultOptions[$constantKey] !== $option = $c->getOption($value)) {
+                $nonDefaultOptions[$constantKey] = $option;
+            }
+        }
+
+        return $nonDefaultOptions;
+    }
+
+    private static function discoverDefaultOptions()
+    {
+        $defaultMemcached = new \Memcached();
+        $defaultMemcached->addServer('127.0.0.1', 11211);
+
+        $defaultOptions = [];
+        self::$optionConstants = self::$optionConstants ?? self::getOptionConstants();
+
+        foreach (self::$optionConstants as $constantKey => $value) {
+            $defaultOptions[$constantKey] = $defaultMemcached->getOption($value);
+        }
+
+        return $defaultOptions;
+    }
+
+    private static function getOptionConstants()
+    {
+        $reflectedMemcached = new \ReflectionClass(\Memcached::class);
+
+        $optionConstants = [];
+        foreach ($reflectedMemcached->getConstants() as $constantKey => $value) {
+            if (0 === strpos($constantKey, 'OPT_')) {
+                $optionConstants[$constantKey] = $value;
+            }
+        }
+
+        return $optionConstants;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/PdoCaster.php b/vendor/symfony/var-dumper/Caster/PdoCaster.php
new file mode 100755
index 0000000..8af5182
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/PdoCaster.php
@@ -0,0 +1,120 @@
+<?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\VarDumper\Caster;
+
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * Casts PDO related classes to array representation.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class PdoCaster
+{
+    private static $pdoAttributes = [
+        'CASE' => [
+            \PDO::CASE_LOWER => 'LOWER',
+            \PDO::CASE_NATURAL => 'NATURAL',
+            \PDO::CASE_UPPER => 'UPPER',
+        ],
+        'ERRMODE' => [
+            \PDO::ERRMODE_SILENT => 'SILENT',
+            \PDO::ERRMODE_WARNING => 'WARNING',
+            \PDO::ERRMODE_EXCEPTION => 'EXCEPTION',
+        ],
+        'TIMEOUT',
+        'PREFETCH',
+        'AUTOCOMMIT',
+        'PERSISTENT',
+        'DRIVER_NAME',
+        'SERVER_INFO',
+        'ORACLE_NULLS' => [
+            \PDO::NULL_NATURAL => 'NATURAL',
+            \PDO::NULL_EMPTY_STRING => 'EMPTY_STRING',
+            \PDO::NULL_TO_STRING => 'TO_STRING',
+        ],
+        'CLIENT_VERSION',
+        'SERVER_VERSION',
+        'STATEMENT_CLASS',
+        'EMULATE_PREPARES',
+        'CONNECTION_STATUS',
+        'STRINGIFY_FETCHES',
+        'DEFAULT_FETCH_MODE' => [
+            \PDO::FETCH_ASSOC => 'ASSOC',
+            \PDO::FETCH_BOTH => 'BOTH',
+            \PDO::FETCH_LAZY => 'LAZY',
+            \PDO::FETCH_NUM => 'NUM',
+            \PDO::FETCH_OBJ => 'OBJ',
+        ],
+    ];
+
+    public static function castPdo(\PDO $c, array $a, Stub $stub, $isNested)
+    {
+        $attr = [];
+        $errmode = $c->getAttribute(\PDO::ATTR_ERRMODE);
+        $c->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
+
+        foreach (self::$pdoAttributes as $k => $v) {
+            if (!isset($k[0])) {
+                $k = $v;
+                $v = [];
+            }
+
+            try {
+                $attr[$k] = 'ERRMODE' === $k ? $errmode : $c->getAttribute(\constant('PDO::ATTR_'.$k));
+                if ($v && isset($v[$attr[$k]])) {
+                    $attr[$k] = new ConstStub($v[$attr[$k]], $attr[$k]);
+                }
+            } catch (\Exception $e) {
+            }
+        }
+        if (isset($attr[$k = 'STATEMENT_CLASS'][1])) {
+            if ($attr[$k][1]) {
+                $attr[$k][1] = new ArgsStub($attr[$k][1], '__construct', $attr[$k][0]);
+            }
+            $attr[$k][0] = new ClassStub($attr[$k][0]);
+        }
+
+        $prefix = Caster::PREFIX_VIRTUAL;
+        $a += [
+            $prefix.'inTransaction' => method_exists($c, 'inTransaction'),
+            $prefix.'errorInfo' => $c->errorInfo(),
+            $prefix.'attributes' => new EnumStub($attr),
+        ];
+
+        if ($a[$prefix.'inTransaction']) {
+            $a[$prefix.'inTransaction'] = $c->inTransaction();
+        } else {
+            unset($a[$prefix.'inTransaction']);
+        }
+
+        if (!isset($a[$prefix.'errorInfo'][1], $a[$prefix.'errorInfo'][2])) {
+            unset($a[$prefix.'errorInfo']);
+        }
+
+        $c->setAttribute(\PDO::ATTR_ERRMODE, $errmode);
+
+        return $a;
+    }
+
+    public static function castPdoStatement(\PDOStatement $c, array $a, Stub $stub, $isNested)
+    {
+        $prefix = Caster::PREFIX_VIRTUAL;
+        $a[$prefix.'errorInfo'] = $c->errorInfo();
+
+        if (!isset($a[$prefix.'errorInfo'][1], $a[$prefix.'errorInfo'][2])) {
+            unset($a[$prefix.'errorInfo']);
+        }
+
+        return $a;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/PgSqlCaster.php b/vendor/symfony/var-dumper/Caster/PgSqlCaster.php
new file mode 100755
index 0000000..cd6bf5b
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/PgSqlCaster.php
@@ -0,0 +1,154 @@
+<?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\VarDumper\Caster;
+
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * Casts pqsql resources to array representation.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class PgSqlCaster
+{
+    private static $paramCodes = [
+        'server_encoding',
+        'client_encoding',
+        'is_superuser',
+        'session_authorization',
+        'DateStyle',
+        'TimeZone',
+        'IntervalStyle',
+        'integer_datetimes',
+        'application_name',
+        'standard_conforming_strings',
+    ];
+
+    private static $transactionStatus = [
+        PGSQL_TRANSACTION_IDLE => 'PGSQL_TRANSACTION_IDLE',
+        PGSQL_TRANSACTION_ACTIVE => 'PGSQL_TRANSACTION_ACTIVE',
+        PGSQL_TRANSACTION_INTRANS => 'PGSQL_TRANSACTION_INTRANS',
+        PGSQL_TRANSACTION_INERROR => 'PGSQL_TRANSACTION_INERROR',
+        PGSQL_TRANSACTION_UNKNOWN => 'PGSQL_TRANSACTION_UNKNOWN',
+    ];
+
+    private static $resultStatus = [
+        PGSQL_EMPTY_QUERY => 'PGSQL_EMPTY_QUERY',
+        PGSQL_COMMAND_OK => 'PGSQL_COMMAND_OK',
+        PGSQL_TUPLES_OK => 'PGSQL_TUPLES_OK',
+        PGSQL_COPY_OUT => 'PGSQL_COPY_OUT',
+        PGSQL_COPY_IN => 'PGSQL_COPY_IN',
+        PGSQL_BAD_RESPONSE => 'PGSQL_BAD_RESPONSE',
+        PGSQL_NONFATAL_ERROR => 'PGSQL_NONFATAL_ERROR',
+        PGSQL_FATAL_ERROR => 'PGSQL_FATAL_ERROR',
+    ];
+
+    private static $diagCodes = [
+        'severity' => PGSQL_DIAG_SEVERITY,
+        'sqlstate' => PGSQL_DIAG_SQLSTATE,
+        'message' => PGSQL_DIAG_MESSAGE_PRIMARY,
+        'detail' => PGSQL_DIAG_MESSAGE_DETAIL,
+        'hint' => PGSQL_DIAG_MESSAGE_HINT,
+        'statement position' => PGSQL_DIAG_STATEMENT_POSITION,
+        'internal position' => PGSQL_DIAG_INTERNAL_POSITION,
+        'internal query' => PGSQL_DIAG_INTERNAL_QUERY,
+        'context' => PGSQL_DIAG_CONTEXT,
+        'file' => PGSQL_DIAG_SOURCE_FILE,
+        'line' => PGSQL_DIAG_SOURCE_LINE,
+        'function' => PGSQL_DIAG_SOURCE_FUNCTION,
+    ];
+
+    public static function castLargeObject($lo, array $a, Stub $stub, $isNested)
+    {
+        $a['seek position'] = pg_lo_tell($lo);
+
+        return $a;
+    }
+
+    public static function castLink($link, array $a, Stub $stub, $isNested)
+    {
+        $a['status'] = pg_connection_status($link);
+        $a['status'] = new ConstStub(PGSQL_CONNECTION_OK === $a['status'] ? 'PGSQL_CONNECTION_OK' : 'PGSQL_CONNECTION_BAD', $a['status']);
+        $a['busy'] = pg_connection_busy($link);
+
+        $a['transaction'] = pg_transaction_status($link);
+        if (isset(self::$transactionStatus[$a['transaction']])) {
+            $a['transaction'] = new ConstStub(self::$transactionStatus[$a['transaction']], $a['transaction']);
+        }
+
+        $a['pid'] = pg_get_pid($link);
+        $a['last error'] = pg_last_error($link);
+        $a['last notice'] = pg_last_notice($link);
+        $a['host'] = pg_host($link);
+        $a['port'] = pg_port($link);
+        $a['dbname'] = pg_dbname($link);
+        $a['options'] = pg_options($link);
+        $a['version'] = pg_version($link);
+
+        foreach (self::$paramCodes as $v) {
+            if (false !== $s = pg_parameter_status($link, $v)) {
+                $a['param'][$v] = $s;
+            }
+        }
+
+        $a['param']['client_encoding'] = pg_client_encoding($link);
+        $a['param'] = new EnumStub($a['param']);
+
+        return $a;
+    }
+
+    public static function castResult($result, array $a, Stub $stub, $isNested)
+    {
+        $a['num rows'] = pg_num_rows($result);
+        $a['status'] = pg_result_status($result);
+        if (isset(self::$resultStatus[$a['status']])) {
+            $a['status'] = new ConstStub(self::$resultStatus[$a['status']], $a['status']);
+        }
+        $a['command-completion tag'] = pg_result_status($result, PGSQL_STATUS_STRING);
+
+        if (-1 === $a['num rows']) {
+            foreach (self::$diagCodes as $k => $v) {
+                $a['error'][$k] = pg_result_error_field($result, $v);
+            }
+        }
+
+        $a['affected rows'] = pg_affected_rows($result);
+        $a['last OID'] = pg_last_oid($result);
+
+        $fields = pg_num_fields($result);
+
+        for ($i = 0; $i < $fields; ++$i) {
+            $field = [
+                'name' => pg_field_name($result, $i),
+                'table' => sprintf('%s (OID: %s)', pg_field_table($result, $i), pg_field_table($result, $i, true)),
+                'type' => sprintf('%s (OID: %s)', pg_field_type($result, $i), pg_field_type_oid($result, $i)),
+                'nullable' => (bool) pg_field_is_null($result, $i),
+                'storage' => pg_field_size($result, $i).' bytes',
+                'display' => pg_field_prtlen($result, $i).' chars',
+            ];
+            if (' (OID: )' === $field['table']) {
+                $field['table'] = null;
+            }
+            if ('-1 bytes' === $field['storage']) {
+                $field['storage'] = 'variable size';
+            } elseif ('1 bytes' === $field['storage']) {
+                $field['storage'] = '1 byte';
+            }
+            if ('1 chars' === $field['display']) {
+                $field['display'] = '1 char';
+            }
+            $a['fields'][] = new EnumStub($field);
+        }
+
+        return $a;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/ProxyManagerCaster.php b/vendor/symfony/var-dumper/Caster/ProxyManagerCaster.php
new file mode 100755
index 0000000..da037b6
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/ProxyManagerCaster.php
@@ -0,0 +1,31 @@
+<?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\VarDumper\Caster;
+
+use ProxyManager\Proxy\ProxyInterface;
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class ProxyManagerCaster
+{
+    public static function castProxy(ProxyInterface $c, array $a, Stub $stub, $isNested)
+    {
+        if ($parent = \get_parent_class($c)) {
+            $stub->class .= ' - '.$parent;
+        }
+        $stub->class .= '@proxy';
+
+        return $a;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/RedisCaster.php b/vendor/symfony/var-dumper/Caster/RedisCaster.php
new file mode 100755
index 0000000..558a080
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/RedisCaster.php
@@ -0,0 +1,150 @@
+<?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\VarDumper\Caster;
+
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * Casts Redis class from ext-redis to array representation.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class RedisCaster
+{
+    private static $serializer = [
+        \Redis::SERIALIZER_NONE => 'NONE',
+        \Redis::SERIALIZER_PHP => 'PHP',
+        2 => 'IGBINARY', // Optional Redis::SERIALIZER_IGBINARY
+    ];
+
+    private static $mode = [
+        \Redis::ATOMIC => 'ATOMIC',
+        \Redis::MULTI => 'MULTI',
+        \Redis::PIPELINE => 'PIPELINE',
+    ];
+
+    private static $compression = [
+        0 => 'NONE', // Redis::COMPRESSION_NONE
+        1 => 'LZF',  // Redis::COMPRESSION_LZF
+    ];
+
+    private static $failover = [
+        \RedisCluster::FAILOVER_NONE => 'NONE',
+        \RedisCluster::FAILOVER_ERROR => 'ERROR',
+        \RedisCluster::FAILOVER_DISTRIBUTE => 'DISTRIBUTE',
+        \RedisCluster::FAILOVER_DISTRIBUTE_SLAVES => 'DISTRIBUTE_SLAVES',
+    ];
+
+    public static function castRedis(\Redis $c, array $a, Stub $stub, $isNested)
+    {
+        $prefix = Caster::PREFIX_VIRTUAL;
+
+        if (!$connected = $c->isConnected()) {
+            return $a + [
+                $prefix.'isConnected' => $connected,
+            ];
+        }
+
+        $mode = $c->getMode();
+
+        return $a + [
+            $prefix.'isConnected' => $connected,
+            $prefix.'host' => $c->getHost(),
+            $prefix.'port' => $c->getPort(),
+            $prefix.'auth' => $c->getAuth(),
+            $prefix.'mode' => isset(self::$mode[$mode]) ? new ConstStub(self::$mode[$mode], $mode) : $mode,
+            $prefix.'dbNum' => $c->getDbNum(),
+            $prefix.'timeout' => $c->getTimeout(),
+            $prefix.'lastError' => $c->getLastError(),
+            $prefix.'persistentId' => $c->getPersistentID(),
+            $prefix.'options' => self::getRedisOptions($c),
+        ];
+    }
+
+    public static function castRedisArray(\RedisArray $c, array $a, Stub $stub, $isNested)
+    {
+        $prefix = Caster::PREFIX_VIRTUAL;
+
+        return $a + [
+            $prefix.'hosts' => $c->_hosts(),
+            $prefix.'function' => ClassStub::wrapCallable($c->_function()),
+            $prefix.'lastError' => $c->getLastError(),
+            $prefix.'options' => self::getRedisOptions($c),
+        ];
+    }
+
+    public static function castRedisCluster(\RedisCluster $c, array $a, Stub $stub, $isNested)
+    {
+        $prefix = Caster::PREFIX_VIRTUAL;
+        $failover = $c->getOption(\RedisCluster::OPT_SLAVE_FAILOVER);
+
+        $a += [
+            $prefix.'_masters' => $c->_masters(),
+            $prefix.'_redir' => $c->_redir(),
+            $prefix.'mode' => new ConstStub($c->getMode() ? 'MULTI' : 'ATOMIC', $c->getMode()),
+            $prefix.'lastError' => $c->getLastError(),
+            $prefix.'options' => self::getRedisOptions($c, [
+                'SLAVE_FAILOVER' => isset(self::$failover[$failover]) ? new ConstStub(self::$failover[$failover], $failover) : $failover,
+            ]),
+        ];
+
+        return $a;
+    }
+
+    /**
+     * @param \Redis|\RedisArray|\RedisCluster $redis
+     */
+    private static function getRedisOptions($redis, array $options = []): EnumStub
+    {
+        $serializer = $redis->getOption(\Redis::OPT_SERIALIZER);
+        if (\is_array($serializer)) {
+            foreach ($serializer as &$v) {
+                if (isset(self::$serializer[$v])) {
+                    $v = new ConstStub(self::$serializer[$v], $v);
+                }
+            }
+        } elseif (isset(self::$serializer[$serializer])) {
+            $serializer = new ConstStub(self::$serializer[$serializer], $serializer);
+        }
+
+        $compression = \defined('Redis::OPT_COMPRESSION') ? $redis->getOption(\Redis::OPT_COMPRESSION) : 0;
+        if (\is_array($compression)) {
+            foreach ($compression as &$v) {
+                if (isset(self::$compression[$v])) {
+                    $v = new ConstStub(self::$compression[$v], $v);
+                }
+            }
+        } elseif (isset(self::$compression[$compression])) {
+            $compression = new ConstStub(self::$compression[$compression], $compression);
+        }
+
+        $retry = \defined('Redis::OPT_SCAN') ? $redis->getOption(\Redis::OPT_SCAN) : 0;
+        if (\is_array($retry)) {
+            foreach ($retry as &$v) {
+                $v = new ConstStub($v ? 'RETRY' : 'NORETRY', $v);
+            }
+        } else {
+            $retry = new ConstStub($retry ? 'RETRY' : 'NORETRY', $retry);
+        }
+
+        $options += [
+            'TCP_KEEPALIVE' => \defined('Redis::OPT_TCP_KEEPALIVE') ? $redis->getOption(\Redis::OPT_TCP_KEEPALIVE) : 0,
+            'READ_TIMEOUT' => $redis->getOption(\Redis::OPT_READ_TIMEOUT),
+            'COMPRESSION' => $compression,
+            'SERIALIZER' => $serializer,
+            'PREFIX' => $redis->getOption(\Redis::OPT_PREFIX),
+            'SCAN' => $retry,
+        ];
+
+        return new EnumStub($options);
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/ReflectionCaster.php b/vendor/symfony/var-dumper/Caster/ReflectionCaster.php
new file mode 100755
index 0000000..8dfe7ea
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/ReflectionCaster.php
@@ -0,0 +1,388 @@
+<?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\VarDumper\Caster;
+
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * Casts Reflector related classes to array representation.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class ReflectionCaster
+{
+    const UNSET_CLOSURE_FILE_INFO = ['Closure' => __CLASS__.'::unsetClosureFileInfo'];
+
+    private static $extraMap = [
+        'docComment' => 'getDocComment',
+        'extension' => 'getExtensionName',
+        'isDisabled' => 'isDisabled',
+        'isDeprecated' => 'isDeprecated',
+        'isInternal' => 'isInternal',
+        'isUserDefined' => 'isUserDefined',
+        'isGenerator' => 'isGenerator',
+        'isVariadic' => 'isVariadic',
+    ];
+
+    public static function castClosure(\Closure $c, array $a, Stub $stub, $isNested, $filter = 0)
+    {
+        $prefix = Caster::PREFIX_VIRTUAL;
+        $c = new \ReflectionFunction($c);
+
+        $a = static::castFunctionAbstract($c, $a, $stub, $isNested, $filter);
+
+        if (false === strpos($c->name, '{closure}')) {
+            $stub->class = isset($a[$prefix.'class']) ? $a[$prefix.'class']->value.'::'.$c->name : $c->name;
+            unset($a[$prefix.'class']);
+        }
+        unset($a[$prefix.'extra']);
+
+        $stub->class .= self::getSignature($a);
+
+        if ($f = $c->getFileName()) {
+            $stub->attr['file'] = $f;
+            $stub->attr['line'] = $c->getStartLine();
+        }
+
+        unset($a[$prefix.'parameters']);
+
+        if ($filter & Caster::EXCLUDE_VERBOSE) {
+            $stub->cut += ($c->getFileName() ? 2 : 0) + \count($a);
+
+            return [];
+        }
+
+        if ($f) {
+            $a[$prefix.'file'] = new LinkStub($f, $c->getStartLine());
+            $a[$prefix.'line'] = $c->getStartLine().' to '.$c->getEndLine();
+        }
+
+        return $a;
+    }
+
+    public static function unsetClosureFileInfo(\Closure $c, array $a)
+    {
+        unset($a[Caster::PREFIX_VIRTUAL.'file'], $a[Caster::PREFIX_VIRTUAL.'line']);
+
+        return $a;
+    }
+
+    public static function castGenerator(\Generator $c, array $a, Stub $stub, $isNested)
+    {
+        if (!class_exists('ReflectionGenerator', false)) {
+            return $a;
+        }
+
+        // Cannot create ReflectionGenerator based on a terminated Generator
+        try {
+            $reflectionGenerator = new \ReflectionGenerator($c);
+        } catch (\Exception $e) {
+            $a[Caster::PREFIX_VIRTUAL.'closed'] = true;
+
+            return $a;
+        }
+
+        return self::castReflectionGenerator($reflectionGenerator, $a, $stub, $isNested);
+    }
+
+    public static function castType(\ReflectionType $c, array $a, Stub $stub, $isNested)
+    {
+        $prefix = Caster::PREFIX_VIRTUAL;
+
+        $a += [
+            $prefix.'name' => $c->getName(),
+            $prefix.'allowsNull' => $c->allowsNull(),
+            $prefix.'isBuiltin' => $c->isBuiltin(),
+        ];
+
+        return $a;
+    }
+
+    public static function castReflectionGenerator(\ReflectionGenerator $c, array $a, Stub $stub, $isNested)
+    {
+        $prefix = Caster::PREFIX_VIRTUAL;
+
+        if ($c->getThis()) {
+            $a[$prefix.'this'] = new CutStub($c->getThis());
+        }
+        $function = $c->getFunction();
+        $frame = [
+            'class' => isset($function->class) ? $function->class : null,
+            'type' => isset($function->class) ? ($function->isStatic() ? '::' : '->') : null,
+            'function' => $function->name,
+            'file' => $c->getExecutingFile(),
+            'line' => $c->getExecutingLine(),
+        ];
+        if ($trace = $c->getTrace(DEBUG_BACKTRACE_IGNORE_ARGS)) {
+            $function = new \ReflectionGenerator($c->getExecutingGenerator());
+            array_unshift($trace, [
+                'function' => 'yield',
+                'file' => $function->getExecutingFile(),
+                'line' => $function->getExecutingLine() - 1,
+            ]);
+            $trace[] = $frame;
+            $a[$prefix.'trace'] = new TraceStub($trace, false, 0, -1, -1);
+        } else {
+            $function = new FrameStub($frame, false, true);
+            $function = ExceptionCaster::castFrameStub($function, [], $function, true);
+            $a[$prefix.'executing'] = new EnumStub([
+                "\0~separator= \0".$frame['class'].$frame['type'].$frame['function'].'()' => $function[$prefix.'src'],
+            ]);
+        }
+
+        $a[Caster::PREFIX_VIRTUAL.'closed'] = false;
+
+        return $a;
+    }
+
+    public static function castClass(\ReflectionClass $c, array $a, Stub $stub, $isNested, $filter = 0)
+    {
+        $prefix = Caster::PREFIX_VIRTUAL;
+
+        if ($n = \Reflection::getModifierNames($c->getModifiers())) {
+            $a[$prefix.'modifiers'] = implode(' ', $n);
+        }
+
+        self::addMap($a, $c, [
+            'extends' => 'getParentClass',
+            'implements' => 'getInterfaceNames',
+            'constants' => 'getConstants',
+        ]);
+
+        foreach ($c->getProperties() as $n) {
+            $a[$prefix.'properties'][$n->name] = $n;
+        }
+
+        foreach ($c->getMethods() as $n) {
+            $a[$prefix.'methods'][$n->name] = $n;
+        }
+
+        if (!($filter & Caster::EXCLUDE_VERBOSE) && !$isNested) {
+            self::addExtra($a, $c);
+        }
+
+        return $a;
+    }
+
+    public static function castFunctionAbstract(\ReflectionFunctionAbstract $c, array $a, Stub $stub, $isNested, $filter = 0)
+    {
+        $prefix = Caster::PREFIX_VIRTUAL;
+
+        self::addMap($a, $c, [
+            'returnsReference' => 'returnsReference',
+            'returnType' => 'getReturnType',
+            'class' => 'getClosureScopeClass',
+            'this' => 'getClosureThis',
+        ]);
+
+        if (isset($a[$prefix.'returnType'])) {
+            $v = $a[$prefix.'returnType'];
+            $v = $v->getName();
+            $a[$prefix.'returnType'] = new ClassStub($a[$prefix.'returnType']->allowsNull() ? '?'.$v : $v, [class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', '']);
+        }
+        if (isset($a[$prefix.'class'])) {
+            $a[$prefix.'class'] = new ClassStub($a[$prefix.'class']);
+        }
+        if (isset($a[$prefix.'this'])) {
+            $a[$prefix.'this'] = new CutStub($a[$prefix.'this']);
+        }
+
+        foreach ($c->getParameters() as $v) {
+            $k = '$'.$v->name;
+            if ($v->isVariadic()) {
+                $k = '...'.$k;
+            }
+            if ($v->isPassedByReference()) {
+                $k = '&'.$k;
+            }
+            $a[$prefix.'parameters'][$k] = $v;
+        }
+        if (isset($a[$prefix.'parameters'])) {
+            $a[$prefix.'parameters'] = new EnumStub($a[$prefix.'parameters']);
+        }
+
+        if ($v = $c->getStaticVariables()) {
+            foreach ($v as $k => &$v) {
+                if (\is_object($v)) {
+                    $a[$prefix.'use']['$'.$k] = new CutStub($v);
+                } else {
+                    $a[$prefix.'use']['$'.$k] = &$v;
+                }
+            }
+            unset($v);
+            $a[$prefix.'use'] = new EnumStub($a[$prefix.'use']);
+        }
+
+        if (!($filter & Caster::EXCLUDE_VERBOSE) && !$isNested) {
+            self::addExtra($a, $c);
+        }
+
+        return $a;
+    }
+
+    public static function castMethod(\ReflectionMethod $c, array $a, Stub $stub, $isNested)
+    {
+        $a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers()));
+
+        return $a;
+    }
+
+    public static function castParameter(\ReflectionParameter $c, array $a, Stub $stub, $isNested)
+    {
+        $prefix = Caster::PREFIX_VIRTUAL;
+
+        self::addMap($a, $c, [
+            'position' => 'getPosition',
+            'isVariadic' => 'isVariadic',
+            'byReference' => 'isPassedByReference',
+            'allowsNull' => 'allowsNull',
+        ]);
+
+        if ($v = $c->getType()) {
+            $a[$prefix.'typeHint'] = $v->getName();
+        }
+
+        if (isset($a[$prefix.'typeHint'])) {
+            $v = $a[$prefix.'typeHint'];
+            $a[$prefix.'typeHint'] = new ClassStub($v, [class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', '']);
+        } else {
+            unset($a[$prefix.'allowsNull']);
+        }
+
+        try {
+            $a[$prefix.'default'] = $v = $c->getDefaultValue();
+            if ($c->isDefaultValueConstant()) {
+                $a[$prefix.'default'] = new ConstStub($c->getDefaultValueConstantName(), $v);
+            }
+            if (null === $v) {
+                unset($a[$prefix.'allowsNull']);
+            }
+        } catch (\ReflectionException $e) {
+        }
+
+        return $a;
+    }
+
+    public static function castProperty(\ReflectionProperty $c, array $a, Stub $stub, $isNested)
+    {
+        $a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers()));
+        self::addExtra($a, $c);
+
+        return $a;
+    }
+
+    public static function castReference(\ReflectionReference $c, array $a, Stub $stub, $isNested)
+    {
+        $a[Caster::PREFIX_VIRTUAL.'id'] = $c->getId();
+
+        return $a;
+    }
+
+    public static function castExtension(\ReflectionExtension $c, array $a, Stub $stub, $isNested)
+    {
+        self::addMap($a, $c, [
+            'version' => 'getVersion',
+            'dependencies' => 'getDependencies',
+            'iniEntries' => 'getIniEntries',
+            'isPersistent' => 'isPersistent',
+            'isTemporary' => 'isTemporary',
+            'constants' => 'getConstants',
+            'functions' => 'getFunctions',
+            'classes' => 'getClasses',
+        ]);
+
+        return $a;
+    }
+
+    public static function castZendExtension(\ReflectionZendExtension $c, array $a, Stub $stub, $isNested)
+    {
+        self::addMap($a, $c, [
+            'version' => 'getVersion',
+            'author' => 'getAuthor',
+            'copyright' => 'getCopyright',
+            'url' => 'getURL',
+        ]);
+
+        return $a;
+    }
+
+    public static function getSignature(array $a)
+    {
+        $prefix = Caster::PREFIX_VIRTUAL;
+        $signature = '';
+
+        if (isset($a[$prefix.'parameters'])) {
+            foreach ($a[$prefix.'parameters']->value as $k => $param) {
+                $signature .= ', ';
+                if ($type = $param->getType()) {
+                    if (!$param->isOptional() && $param->allowsNull()) {
+                        $signature .= '?';
+                    }
+                    $signature .= substr(strrchr('\\'.$type->getName(), '\\'), 1).' ';
+                }
+                $signature .= $k;
+
+                if (!$param->isDefaultValueAvailable()) {
+                    continue;
+                }
+                $v = $param->getDefaultValue();
+                $signature .= ' = ';
+
+                if ($param->isDefaultValueConstant()) {
+                    $signature .= substr(strrchr('\\'.$param->getDefaultValueConstantName(), '\\'), 1);
+                } elseif (null === $v) {
+                    $signature .= 'null';
+                } elseif (\is_array($v)) {
+                    $signature .= $v ? '[…'.\count($v).']' : '[]';
+                } elseif (\is_string($v)) {
+                    $signature .= 10 > \strlen($v) && false === strpos($v, '\\') ? "'{$v}'" : "'…".\strlen($v)."'";
+                } elseif (\is_bool($v)) {
+                    $signature .= $v ? 'true' : 'false';
+                } else {
+                    $signature .= $v;
+                }
+            }
+        }
+        $signature = (empty($a[$prefix.'returnsReference']) ? '' : '&').'('.substr($signature, 2).')';
+
+        if (isset($a[$prefix.'returnType'])) {
+            $signature .= ': '.substr(strrchr('\\'.$a[$prefix.'returnType'], '\\'), 1);
+        }
+
+        return $signature;
+    }
+
+    private static function addExtra(&$a, \Reflector $c)
+    {
+        $x = isset($a[Caster::PREFIX_VIRTUAL.'extra']) ? $a[Caster::PREFIX_VIRTUAL.'extra']->value : [];
+
+        if (method_exists($c, 'getFileName') && $m = $c->getFileName()) {
+            $x['file'] = new LinkStub($m, $c->getStartLine());
+            $x['line'] = $c->getStartLine().' to '.$c->getEndLine();
+        }
+
+        self::addMap($x, $c, self::$extraMap, '');
+
+        if ($x) {
+            $a[Caster::PREFIX_VIRTUAL.'extra'] = new EnumStub($x);
+        }
+    }
+
+    private static function addMap(&$a, \Reflector $c, $map, $prefix = Caster::PREFIX_VIRTUAL)
+    {
+        foreach ($map as $k => $m) {
+            if (method_exists($c, $m) && false !== ($m = $c->$m()) && null !== $m) {
+                $a[$prefix.$k] = $m instanceof \Reflector ? $m->name : $m;
+            }
+        }
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/ResourceCaster.php b/vendor/symfony/var-dumper/Caster/ResourceCaster.php
new file mode 100755
index 0000000..5d9b80d
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/ResourceCaster.php
@@ -0,0 +1,98 @@
+<?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\VarDumper\Caster;
+
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * Casts common resource types to array representation.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class ResourceCaster
+{
+    public static function castCurl($h, array $a, Stub $stub, $isNested)
+    {
+        return curl_getinfo($h);
+    }
+
+    public static function castDba($dba, array $a, Stub $stub, $isNested)
+    {
+        $list = dba_list();
+        $a['file'] = $list[(int) $dba];
+
+        return $a;
+    }
+
+    public static function castProcess($process, array $a, Stub $stub, $isNested)
+    {
+        return proc_get_status($process);
+    }
+
+    public static function castStream($stream, array $a, Stub $stub, $isNested)
+    {
+        $a = stream_get_meta_data($stream) + static::castStreamContext($stream, $a, $stub, $isNested);
+        if (isset($a['uri'])) {
+            $a['uri'] = new LinkStub($a['uri']);
+        }
+
+        return $a;
+    }
+
+    public static function castStreamContext($stream, array $a, Stub $stub, $isNested)
+    {
+        return @stream_context_get_params($stream) ?: $a;
+    }
+
+    public static function castGd($gd, array $a, Stub $stub, $isNested)
+    {
+        $a['size'] = imagesx($gd).'x'.imagesy($gd);
+        $a['trueColor'] = imageistruecolor($gd);
+
+        return $a;
+    }
+
+    public static function castMysqlLink($h, array $a, Stub $stub, $isNested)
+    {
+        $a['host'] = mysql_get_host_info($h);
+        $a['protocol'] = mysql_get_proto_info($h);
+        $a['server'] = mysql_get_server_info($h);
+
+        return $a;
+    }
+
+    public static function castOpensslX509($h, array $a, Stub $stub, $isNested)
+    {
+        $stub->cut = -1;
+        $info = openssl_x509_parse($h, false);
+
+        $pin = openssl_pkey_get_public($h);
+        $pin = openssl_pkey_get_details($pin)['key'];
+        $pin = \array_slice(explode("\n", $pin), 1, -2);
+        $pin = base64_decode(implode('', $pin));
+        $pin = base64_encode(hash('sha256', $pin, true));
+
+        $a += [
+            'subject' => new EnumStub(array_intersect_key($info['subject'], ['organizationName' => true, 'commonName' => true])),
+            'issuer' => new EnumStub(array_intersect_key($info['issuer'], ['organizationName' => true, 'commonName' => true])),
+            'expiry' => new ConstStub(date(\DateTime::ISO8601, $info['validTo_time_t']), $info['validTo_time_t']),
+            'fingerprint' => new EnumStub([
+                'md5' => new ConstStub(wordwrap(strtoupper(openssl_x509_fingerprint($h, 'md5')), 2, ':', true)),
+                'sha1' => new ConstStub(wordwrap(strtoupper(openssl_x509_fingerprint($h, 'sha1')), 2, ':', true)),
+                'sha256' => new ConstStub(wordwrap(strtoupper(openssl_x509_fingerprint($h, 'sha256')), 2, ':', true)),
+                'pin-sha256' => new ConstStub($pin),
+            ]),
+        ];
+
+        return $a;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/SplCaster.php b/vendor/symfony/var-dumper/Caster/SplCaster.php
new file mode 100755
index 0000000..37260b5
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/SplCaster.php
@@ -0,0 +1,220 @@
+<?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\VarDumper\Caster;
+
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * Casts SPL related classes to array representation.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class SplCaster
+{
+    private static $splFileObjectFlags = [
+        \SplFileObject::DROP_NEW_LINE => 'DROP_NEW_LINE',
+        \SplFileObject::READ_AHEAD => 'READ_AHEAD',
+        \SplFileObject::SKIP_EMPTY => 'SKIP_EMPTY',
+        \SplFileObject::READ_CSV => 'READ_CSV',
+    ];
+
+    public static function castArrayObject(\ArrayObject $c, array $a, Stub $stub, $isNested)
+    {
+        return self::castSplArray($c, $a, $stub, $isNested);
+    }
+
+    public static function castArrayIterator(\ArrayIterator $c, array $a, Stub $stub, $isNested)
+    {
+        return self::castSplArray($c, $a, $stub, $isNested);
+    }
+
+    public static function castHeap(\Iterator $c, array $a, Stub $stub, $isNested)
+    {
+        $a += [
+            Caster::PREFIX_VIRTUAL.'heap' => iterator_to_array(clone $c),
+        ];
+
+        return $a;
+    }
+
+    public static function castDoublyLinkedList(\SplDoublyLinkedList $c, array $a, Stub $stub, $isNested)
+    {
+        $prefix = Caster::PREFIX_VIRTUAL;
+        $mode = $c->getIteratorMode();
+        $c->setIteratorMode(\SplDoublyLinkedList::IT_MODE_KEEP | $mode & ~\SplDoublyLinkedList::IT_MODE_DELETE);
+
+        $a += [
+            $prefix.'mode' => new ConstStub((($mode & \SplDoublyLinkedList::IT_MODE_LIFO) ? 'IT_MODE_LIFO' : 'IT_MODE_FIFO').' | '.(($mode & \SplDoublyLinkedList::IT_MODE_DELETE) ? 'IT_MODE_DELETE' : 'IT_MODE_KEEP'), $mode),
+            $prefix.'dllist' => iterator_to_array($c),
+        ];
+        $c->setIteratorMode($mode);
+
+        return $a;
+    }
+
+    public static function castFileInfo(\SplFileInfo $c, array $a, Stub $stub, $isNested)
+    {
+        static $map = [
+            'path' => 'getPath',
+            'filename' => 'getFilename',
+            'basename' => 'getBasename',
+            'pathname' => 'getPathname',
+            'extension' => 'getExtension',
+            'realPath' => 'getRealPath',
+            'aTime' => 'getATime',
+            'mTime' => 'getMTime',
+            'cTime' => 'getCTime',
+            'inode' => 'getInode',
+            'size' => 'getSize',
+            'perms' => 'getPerms',
+            'owner' => 'getOwner',
+            'group' => 'getGroup',
+            'type' => 'getType',
+            'writable' => 'isWritable',
+            'readable' => 'isReadable',
+            'executable' => 'isExecutable',
+            'file' => 'isFile',
+            'dir' => 'isDir',
+            'link' => 'isLink',
+            'linkTarget' => 'getLinkTarget',
+        ];
+
+        $prefix = Caster::PREFIX_VIRTUAL;
+
+        foreach ($map as $key => $accessor) {
+            try {
+                $a[$prefix.$key] = $c->$accessor();
+            } catch (\Exception $e) {
+            }
+        }
+
+        if (isset($a[$prefix.'realPath'])) {
+            $a[$prefix.'realPath'] = new LinkStub($a[$prefix.'realPath']);
+        }
+
+        if (isset($a[$prefix.'perms'])) {
+            $a[$prefix.'perms'] = new ConstStub(sprintf('0%o', $a[$prefix.'perms']), $a[$prefix.'perms']);
+        }
+
+        static $mapDate = ['aTime', 'mTime', 'cTime'];
+        foreach ($mapDate as $key) {
+            if (isset($a[$prefix.$key])) {
+                $a[$prefix.$key] = new ConstStub(date('Y-m-d H:i:s', $a[$prefix.$key]), $a[$prefix.$key]);
+            }
+        }
+
+        return $a;
+    }
+
+    public static function castFileObject(\SplFileObject $c, array $a, Stub $stub, $isNested)
+    {
+        static $map = [
+            'csvControl' => 'getCsvControl',
+            'flags' => 'getFlags',
+            'maxLineLen' => 'getMaxLineLen',
+            'fstat' => 'fstat',
+            'eof' => 'eof',
+            'key' => 'key',
+        ];
+
+        $prefix = Caster::PREFIX_VIRTUAL;
+
+        foreach ($map as $key => $accessor) {
+            try {
+                $a[$prefix.$key] = $c->$accessor();
+            } catch (\Exception $e) {
+            }
+        }
+
+        if (isset($a[$prefix.'flags'])) {
+            $flagsArray = [];
+            foreach (self::$splFileObjectFlags as $value => $name) {
+                if ($a[$prefix.'flags'] & $value) {
+                    $flagsArray[] = $name;
+                }
+            }
+            $a[$prefix.'flags'] = new ConstStub(implode('|', $flagsArray), $a[$prefix.'flags']);
+        }
+
+        if (isset($a[$prefix.'fstat'])) {
+            $a[$prefix.'fstat'] = new CutArrayStub($a[$prefix.'fstat'], ['dev', 'ino', 'nlink', 'rdev', 'blksize', 'blocks']);
+        }
+
+        return $a;
+    }
+
+    public static function castFixedArray(\SplFixedArray $c, array $a, Stub $stub, $isNested)
+    {
+        $a += [
+            Caster::PREFIX_VIRTUAL.'storage' => $c->toArray(),
+        ];
+
+        return $a;
+    }
+
+    public static function castObjectStorage(\SplObjectStorage $c, array $a, Stub $stub, $isNested)
+    {
+        $storage = [];
+        unset($a[Caster::PREFIX_DYNAMIC."\0gcdata"]); // Don't hit https://bugs.php.net/65967
+
+        $clone = clone $c;
+        foreach ($clone as $obj) {
+            $storage[] = [
+                'object' => $obj,
+                'info' => $clone->getInfo(),
+             ];
+        }
+
+        $a += [
+            Caster::PREFIX_VIRTUAL.'storage' => $storage,
+        ];
+
+        return $a;
+    }
+
+    public static function castOuterIterator(\OuterIterator $c, array $a, Stub $stub, $isNested)
+    {
+        $a[Caster::PREFIX_VIRTUAL.'innerIterator'] = $c->getInnerIterator();
+
+        return $a;
+    }
+
+    public static function castWeakReference(\WeakReference $c, array $a, Stub $stub, $isNested)
+    {
+        $a[Caster::PREFIX_VIRTUAL.'object'] = $c->get();
+
+        return $a;
+    }
+
+    private static function castSplArray($c, array $a, Stub $stub, $isNested)
+    {
+        $prefix = Caster::PREFIX_VIRTUAL;
+        $class = $stub->class;
+        $flags = $c->getFlags();
+
+        if (!($flags & \ArrayObject::STD_PROP_LIST)) {
+            $c->setFlags(\ArrayObject::STD_PROP_LIST);
+            $a = Caster::castObject($c, $class);
+            $c->setFlags($flags);
+        }
+        $a += [
+            $prefix.'flag::STD_PROP_LIST' => (bool) ($flags & \ArrayObject::STD_PROP_LIST),
+            $prefix.'flag::ARRAY_AS_PROPS' => (bool) ($flags & \ArrayObject::ARRAY_AS_PROPS),
+        ];
+        if ($c instanceof \ArrayObject) {
+            $a[$prefix.'iteratorClass'] = new ClassStub($c->getIteratorClass());
+        }
+        $a[$prefix.'storage'] = $c->getArrayCopy();
+
+        return $a;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/StubCaster.php b/vendor/symfony/var-dumper/Caster/StubCaster.php
new file mode 100755
index 0000000..9927d42
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/StubCaster.php
@@ -0,0 +1,82 @@
+<?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\VarDumper\Caster;
+
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * Casts a caster's Stub.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class StubCaster
+{
+    public static function castStub(Stub $c, array $a, Stub $stub, $isNested)
+    {
+        if ($isNested) {
+            $stub->type = $c->type;
+            $stub->class = $c->class;
+            $stub->value = $c->value;
+            $stub->handle = $c->handle;
+            $stub->cut = $c->cut;
+            $stub->attr = $c->attr;
+
+            if (Stub::TYPE_REF === $c->type && !$c->class && \is_string($c->value) && !preg_match('//u', $c->value)) {
+                $stub->type = Stub::TYPE_STRING;
+                $stub->class = Stub::STRING_BINARY;
+            }
+
+            $a = [];
+        }
+
+        return $a;
+    }
+
+    public static function castCutArray(CutArrayStub $c, array $a, Stub $stub, $isNested)
+    {
+        return $isNested ? $c->preservedSubset : $a;
+    }
+
+    public static function cutInternals($obj, array $a, Stub $stub, $isNested)
+    {
+        if ($isNested) {
+            $stub->cut += \count($a);
+
+            return [];
+        }
+
+        return $a;
+    }
+
+    public static function castEnum(EnumStub $c, array $a, Stub $stub, $isNested)
+    {
+        if ($isNested) {
+            $stub->class = $c->dumpKeys ? '' : null;
+            $stub->handle = 0;
+            $stub->value = null;
+            $stub->cut = $c->cut;
+            $stub->attr = $c->attr;
+
+            $a = [];
+
+            if ($c->value) {
+                foreach (array_keys($c->value) as $k) {
+                    $keys[] = !isset($k[0]) || "\0" !== $k[0] ? Caster::PREFIX_VIRTUAL.$k : $k;
+                }
+                // Preserve references with array_combine()
+                $a = array_combine($keys, $c->value);
+            }
+        }
+
+        return $a;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/SymfonyCaster.php b/vendor/symfony/var-dumper/Caster/SymfonyCaster.php
new file mode 100755
index 0000000..78acb90
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/SymfonyCaster.php
@@ -0,0 +1,51 @@
+<?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\VarDumper\Caster;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+class SymfonyCaster
+{
+    private static $requestGetters = [
+        'pathInfo' => 'getPathInfo',
+        'requestUri' => 'getRequestUri',
+        'baseUrl' => 'getBaseUrl',
+        'basePath' => 'getBasePath',
+        'method' => 'getMethod',
+        'format' => 'getRequestFormat',
+    ];
+
+    public static function castRequest(Request $request, array $a, Stub $stub, $isNested)
+    {
+        $clone = null;
+
+        foreach (self::$requestGetters as $prop => $getter) {
+            if (null === $a[Caster::PREFIX_PROTECTED.$prop]) {
+                if (null === $clone) {
+                    $clone = clone $request;
+                }
+                $a[Caster::PREFIX_VIRTUAL.$prop] = $clone->{$getter}();
+            }
+        }
+
+        return $a;
+    }
+
+    public static function castHttpClient($client, array $a, Stub $stub, $isNested)
+    {
+        $multiKey = sprintf("\0%s\0multi", \get_class($client));
+        $a[$multiKey] = new CutStub($a[$multiKey]);
+
+        return $a;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/TraceStub.php b/vendor/symfony/var-dumper/Caster/TraceStub.php
new file mode 100755
index 0000000..5eea1c8
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/TraceStub.php
@@ -0,0 +1,36 @@
+<?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\VarDumper\Caster;
+
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * Represents a backtrace as returned by debug_backtrace() or Exception->getTrace().
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class TraceStub extends Stub
+{
+    public $keepArgs;
+    public $sliceOffset;
+    public $sliceLength;
+    public $numberingOffset;
+
+    public function __construct(array $trace, bool $keepArgs = true, int $sliceOffset = 0, int $sliceLength = null, int $numberingOffset = 0)
+    {
+        $this->value = $trace;
+        $this->keepArgs = $keepArgs;
+        $this->sliceOffset = $sliceOffset;
+        $this->sliceLength = $sliceLength;
+        $this->numberingOffset = $numberingOffset;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/XmlReaderCaster.php b/vendor/symfony/var-dumper/Caster/XmlReaderCaster.php
new file mode 100755
index 0000000..3ae9ec0
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/XmlReaderCaster.php
@@ -0,0 +1,77 @@
+<?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\VarDumper\Caster;
+
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * Casts XmlReader class to array representation.
+ *
+ * @author Baptiste Clavié <clavie.b@gmail.com>
+ */
+class XmlReaderCaster
+{
+    private static $nodeTypes = [
+        \XMLReader::NONE => 'NONE',
+        \XMLReader::ELEMENT => 'ELEMENT',
+        \XMLReader::ATTRIBUTE => 'ATTRIBUTE',
+        \XMLReader::TEXT => 'TEXT',
+        \XMLReader::CDATA => 'CDATA',
+        \XMLReader::ENTITY_REF => 'ENTITY_REF',
+        \XMLReader::ENTITY => 'ENTITY',
+        \XMLReader::PI => 'PI (Processing Instruction)',
+        \XMLReader::COMMENT => 'COMMENT',
+        \XMLReader::DOC => 'DOC',
+        \XMLReader::DOC_TYPE => 'DOC_TYPE',
+        \XMLReader::DOC_FRAGMENT => 'DOC_FRAGMENT',
+        \XMLReader::NOTATION => 'NOTATION',
+        \XMLReader::WHITESPACE => 'WHITESPACE',
+        \XMLReader::SIGNIFICANT_WHITESPACE => 'SIGNIFICANT_WHITESPACE',
+        \XMLReader::END_ELEMENT => 'END_ELEMENT',
+        \XMLReader::END_ENTITY => 'END_ENTITY',
+        \XMLReader::XML_DECLARATION => 'XML_DECLARATION',
+    ];
+
+    public static function castXmlReader(\XMLReader $reader, array $a, Stub $stub, $isNested)
+    {
+        $props = Caster::PREFIX_VIRTUAL.'parserProperties';
+        $info = [
+            'localName' => $reader->localName,
+            'prefix' => $reader->prefix,
+            'nodeType' => new ConstStub(self::$nodeTypes[$reader->nodeType], $reader->nodeType),
+            'depth' => $reader->depth,
+            'isDefault' => $reader->isDefault,
+            'isEmptyElement' => \XMLReader::NONE === $reader->nodeType ? null : $reader->isEmptyElement,
+            'xmlLang' => $reader->xmlLang,
+            'attributeCount' => $reader->attributeCount,
+            'value' => $reader->value,
+            'namespaceURI' => $reader->namespaceURI,
+            'baseURI' => $reader->baseURI ? new LinkStub($reader->baseURI) : $reader->baseURI,
+            $props => [
+                'LOADDTD' => $reader->getParserProperty(\XMLReader::LOADDTD),
+                'DEFAULTATTRS' => $reader->getParserProperty(\XMLReader::DEFAULTATTRS),
+                'VALIDATE' => $reader->getParserProperty(\XMLReader::VALIDATE),
+                'SUBST_ENTITIES' => $reader->getParserProperty(\XMLReader::SUBST_ENTITIES),
+            ],
+        ];
+
+        if ($info[$props] = Caster::filter($info[$props], Caster::EXCLUDE_EMPTY, [], $count)) {
+            $info[$props] = new EnumStub($info[$props]);
+            $info[$props]->cut = $count;
+        }
+
+        $info = Caster::filter($info, Caster::EXCLUDE_EMPTY, [], $count);
+        // +2 because hasValue and hasAttributes are always filtered
+        $stub->cut += $count + 2;
+
+        return $a + $info;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Caster/XmlResourceCaster.php b/vendor/symfony/var-dumper/Caster/XmlResourceCaster.php
new file mode 100755
index 0000000..117138c
--- /dev/null
+++ b/vendor/symfony/var-dumper/Caster/XmlResourceCaster.php
@@ -0,0 +1,61 @@
+<?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\VarDumper\Caster;
+
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * Casts XML resources to array representation.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class XmlResourceCaster
+{
+    private static $xmlErrors = [
+        XML_ERROR_NONE => 'XML_ERROR_NONE',
+        XML_ERROR_NO_MEMORY => 'XML_ERROR_NO_MEMORY',
+        XML_ERROR_SYNTAX => 'XML_ERROR_SYNTAX',
+        XML_ERROR_NO_ELEMENTS => 'XML_ERROR_NO_ELEMENTS',
+        XML_ERROR_INVALID_TOKEN => 'XML_ERROR_INVALID_TOKEN',
+        XML_ERROR_UNCLOSED_TOKEN => 'XML_ERROR_UNCLOSED_TOKEN',
+        XML_ERROR_PARTIAL_CHAR => 'XML_ERROR_PARTIAL_CHAR',
+        XML_ERROR_TAG_MISMATCH => 'XML_ERROR_TAG_MISMATCH',
+        XML_ERROR_DUPLICATE_ATTRIBUTE => 'XML_ERROR_DUPLICATE_ATTRIBUTE',
+        XML_ERROR_JUNK_AFTER_DOC_ELEMENT => 'XML_ERROR_JUNK_AFTER_DOC_ELEMENT',
+        XML_ERROR_PARAM_ENTITY_REF => 'XML_ERROR_PARAM_ENTITY_REF',
+        XML_ERROR_UNDEFINED_ENTITY => 'XML_ERROR_UNDEFINED_ENTITY',
+        XML_ERROR_RECURSIVE_ENTITY_REF => 'XML_ERROR_RECURSIVE_ENTITY_REF',
+        XML_ERROR_ASYNC_ENTITY => 'XML_ERROR_ASYNC_ENTITY',
+        XML_ERROR_BAD_CHAR_REF => 'XML_ERROR_BAD_CHAR_REF',
+        XML_ERROR_BINARY_ENTITY_REF => 'XML_ERROR_BINARY_ENTITY_REF',
+        XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF => 'XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF',
+        XML_ERROR_MISPLACED_XML_PI => 'XML_ERROR_MISPLACED_XML_PI',
+        XML_ERROR_UNKNOWN_ENCODING => 'XML_ERROR_UNKNOWN_ENCODING',
+        XML_ERROR_INCORRECT_ENCODING => 'XML_ERROR_INCORRECT_ENCODING',
+        XML_ERROR_UNCLOSED_CDATA_SECTION => 'XML_ERROR_UNCLOSED_CDATA_SECTION',
+        XML_ERROR_EXTERNAL_ENTITY_HANDLING => 'XML_ERROR_EXTERNAL_ENTITY_HANDLING',
+    ];
+
+    public static function castXml($h, array $a, Stub $stub, $isNested)
+    {
+        $a['current_byte_index'] = xml_get_current_byte_index($h);
+        $a['current_column_number'] = xml_get_current_column_number($h);
+        $a['current_line_number'] = xml_get_current_line_number($h);
+        $a['error_code'] = xml_get_error_code($h);
+
+        if (isset(self::$xmlErrors[$a['error_code']])) {
+            $a['error_code'] = new ConstStub(self::$xmlErrors[$a['error_code']], $a['error_code']);
+        }
+
+        return $a;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Cloner/AbstractCloner.php b/vendor/symfony/var-dumper/Cloner/AbstractCloner.php
new file mode 100755
index 0000000..aacab2c
--- /dev/null
+++ b/vendor/symfony/var-dumper/Cloner/AbstractCloner.php
@@ -0,0 +1,360 @@
+<?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\VarDumper\Cloner;
+
+use Symfony\Component\VarDumper\Caster\Caster;
+use Symfony\Component\VarDumper\Exception\ThrowingCasterException;
+
+/**
+ * AbstractCloner implements a generic caster mechanism for objects and resources.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+abstract class AbstractCloner implements ClonerInterface
+{
+    public static $defaultCasters = [
+        '__PHP_Incomplete_Class' => ['Symfony\Component\VarDumper\Caster\Caster', 'castPhpIncompleteClass'],
+
+        'Symfony\Component\VarDumper\Caster\CutStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castStub'],
+        'Symfony\Component\VarDumper\Caster\CutArrayStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castCutArray'],
+        'Symfony\Component\VarDumper\Caster\ConstStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castStub'],
+        'Symfony\Component\VarDumper\Caster\EnumStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castEnum'],
+
+        'Closure' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClosure'],
+        'Generator' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castGenerator'],
+        'ReflectionType' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castType'],
+        'ReflectionGenerator' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castReflectionGenerator'],
+        'ReflectionClass' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClass'],
+        'ReflectionFunctionAbstract' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castFunctionAbstract'],
+        'ReflectionMethod' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castMethod'],
+        'ReflectionParameter' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castParameter'],
+        'ReflectionProperty' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castProperty'],
+        'ReflectionReference' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castReference'],
+        'ReflectionExtension' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castExtension'],
+        'ReflectionZendExtension' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castZendExtension'],
+
+        'Doctrine\Common\Persistence\ObjectManager' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'],
+        'Doctrine\Common\Proxy\Proxy' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castCommonProxy'],
+        'Doctrine\ORM\Proxy\Proxy' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castOrmProxy'],
+        'Doctrine\ORM\PersistentCollection' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castPersistentCollection'],
+
+        'DOMException' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castException'],
+        'DOMStringList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'],
+        'DOMNameList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'],
+        'DOMImplementation' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castImplementation'],
+        'DOMImplementationList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'],
+        'DOMNode' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castNode'],
+        'DOMNameSpaceNode' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castNameSpaceNode'],
+        'DOMDocument' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDocument'],
+        'DOMNodeList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'],
+        'DOMNamedNodeMap' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'],
+        'DOMCharacterData' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castCharacterData'],
+        'DOMAttr' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castAttr'],
+        'DOMElement' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castElement'],
+        'DOMText' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castText'],
+        'DOMTypeinfo' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castTypeinfo'],
+        'DOMDomError' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDomError'],
+        'DOMLocator' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLocator'],
+        'DOMDocumentType' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDocumentType'],
+        'DOMNotation' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castNotation'],
+        'DOMEntity' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castEntity'],
+        'DOMProcessingInstruction' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castProcessingInstruction'],
+        'DOMXPath' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castXPath'],
+
+        'XMLReader' => ['Symfony\Component\VarDumper\Caster\XmlReaderCaster', 'castXmlReader'],
+
+        'ErrorException' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castErrorException'],
+        'Exception' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castException'],
+        'Error' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castError'],
+        'Symfony\Component\DependencyInjection\ContainerInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'],
+        'Symfony\Component\HttpClient\CurlHttpClient' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'],
+        'Symfony\Component\HttpClient\NativeHttpClient' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'],
+        'Symfony\Component\HttpClient\Response\CurlResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'],
+        'Symfony\Component\HttpClient\Response\NativeResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'],
+        'Symfony\Component\HttpFoundation\Request' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castRequest'],
+        'Symfony\Component\VarDumper\Exception\ThrowingCasterException' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castThrowingCasterException'],
+        'Symfony\Component\VarDumper\Caster\TraceStub' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castTraceStub'],
+        'Symfony\Component\VarDumper\Caster\FrameStub' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castFrameStub'],
+        'Symfony\Component\VarDumper\Cloner\AbstractCloner' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'],
+        'Symfony\Component\Debug\Exception\SilencedErrorContext' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castSilencedErrorContext'],
+
+        'ProxyManager\Proxy\ProxyInterface' => ['Symfony\Component\VarDumper\Caster\ProxyManagerCaster', 'castProxy'],
+        'PHPUnit_Framework_MockObject_MockObject' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'],
+        'Prophecy\Prophecy\ProphecySubjectInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'],
+        'Mockery\MockInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'],
+
+        'PDO' => ['Symfony\Component\VarDumper\Caster\PdoCaster', 'castPdo'],
+        'PDOStatement' => ['Symfony\Component\VarDumper\Caster\PdoCaster', 'castPdoStatement'],
+
+        'AMQPConnection' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castConnection'],
+        'AMQPChannel' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castChannel'],
+        'AMQPQueue' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castQueue'],
+        'AMQPExchange' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castExchange'],
+        'AMQPEnvelope' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castEnvelope'],
+
+        'ArrayObject' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castArrayObject'],
+        'ArrayIterator' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castArrayIterator'],
+        'SplDoublyLinkedList' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castDoublyLinkedList'],
+        'SplFileInfo' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castFileInfo'],
+        'SplFileObject' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castFileObject'],
+        'SplFixedArray' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castFixedArray'],
+        'SplHeap' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castHeap'],
+        'SplObjectStorage' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castObjectStorage'],
+        'SplPriorityQueue' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castHeap'],
+        'OuterIterator' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castOuterIterator'],
+        'WeakReference' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castWeakReference'],
+
+        'Redis' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedis'],
+        'RedisArray' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedisArray'],
+        'RedisCluster' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedisCluster'],
+
+        'DateTimeInterface' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castDateTime'],
+        'DateInterval' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castInterval'],
+        'DateTimeZone' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castTimeZone'],
+        'DatePeriod' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castPeriod'],
+
+        'GMP' => ['Symfony\Component\VarDumper\Caster\GmpCaster', 'castGmp'],
+
+        'MessageFormatter' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castMessageFormatter'],
+        'NumberFormatter' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castNumberFormatter'],
+        'IntlTimeZone' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castIntlTimeZone'],
+        'IntlCalendar' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castIntlCalendar'],
+        'IntlDateFormatter' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castIntlDateFormatter'],
+
+        'Memcached' => ['Symfony\Component\VarDumper\Caster\MemcachedCaster', 'castMemcached'],
+
+        'Ds\Collection' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castCollection'],
+        'Ds\Map' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castMap'],
+        'Ds\Pair' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castPair'],
+        'Symfony\Component\VarDumper\Caster\DsPairStub' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castPairStub'],
+
+        ':curl' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castCurl'],
+        ':dba' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'],
+        ':dba persistent' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'],
+        ':gd' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castGd'],
+        ':mysql link' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castMysqlLink'],
+        ':pgsql large object' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLargeObject'],
+        ':pgsql link' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLink'],
+        ':pgsql link persistent' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLink'],
+        ':pgsql result' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castResult'],
+        ':process' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castProcess'],
+        ':stream' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStream'],
+        ':OpenSSL X.509' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castOpensslX509'],
+        ':persistent stream' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStream'],
+        ':stream-context' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStreamContext'],
+        ':xml' => ['Symfony\Component\VarDumper\Caster\XmlResourceCaster', 'castXml'],
+    ];
+
+    protected $maxItems = 2500;
+    protected $maxString = -1;
+    protected $minDepth = 1;
+
+    private $casters = [];
+    private $prevErrorHandler;
+    private $classInfo = [];
+    private $filter = 0;
+
+    /**
+     * @param callable[]|null $casters A map of casters
+     *
+     * @see addCasters
+     */
+    public function __construct(array $casters = null)
+    {
+        if (null === $casters) {
+            $casters = static::$defaultCasters;
+        }
+        $this->addCasters($casters);
+    }
+
+    /**
+     * Adds casters for resources and objects.
+     *
+     * Maps resources or objects types to a callback.
+     * Types are in the key, with a callable caster for value.
+     * Resource types are to be prefixed with a `:`,
+     * see e.g. static::$defaultCasters.
+     *
+     * @param callable[] $casters A map of casters
+     */
+    public function addCasters(array $casters)
+    {
+        foreach ($casters as $type => $callback) {
+            $this->casters[$type][] = $callback;
+        }
+    }
+
+    /**
+     * Sets the maximum number of items to clone past the minimum depth in nested structures.
+     *
+     * @param int $maxItems
+     */
+    public function setMaxItems($maxItems)
+    {
+        $this->maxItems = (int) $maxItems;
+    }
+
+    /**
+     * Sets the maximum cloned length for strings.
+     *
+     * @param int $maxString
+     */
+    public function setMaxString($maxString)
+    {
+        $this->maxString = (int) $maxString;
+    }
+
+    /**
+     * Sets the minimum tree depth where we are guaranteed to clone all the items.  After this
+     * depth is reached, only setMaxItems items will be cloned.
+     *
+     * @param int $minDepth
+     */
+    public function setMinDepth($minDepth)
+    {
+        $this->minDepth = (int) $minDepth;
+    }
+
+    /**
+     * Clones a PHP variable.
+     *
+     * @param mixed $var    Any PHP variable
+     * @param int   $filter A bit field of Caster::EXCLUDE_* constants
+     *
+     * @return Data The cloned variable represented by a Data object
+     */
+    public function cloneVar($var, $filter = 0)
+    {
+        $this->prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = []) {
+            if (E_RECOVERABLE_ERROR === $type || E_USER_ERROR === $type) {
+                // Cloner never dies
+                throw new \ErrorException($msg, 0, $type, $file, $line);
+            }
+
+            if ($this->prevErrorHandler) {
+                return ($this->prevErrorHandler)($type, $msg, $file, $line, $context);
+            }
+
+            return false;
+        });
+        $this->filter = $filter;
+
+        if ($gc = gc_enabled()) {
+            gc_disable();
+        }
+        try {
+            return new Data($this->doClone($var));
+        } finally {
+            if ($gc) {
+                gc_enable();
+            }
+            restore_error_handler();
+            $this->prevErrorHandler = null;
+        }
+    }
+
+    /**
+     * Effectively clones the PHP variable.
+     *
+     * @param mixed $var Any PHP variable
+     *
+     * @return array The cloned variable represented in an array
+     */
+    abstract protected function doClone($var);
+
+    /**
+     * Casts an object to an array representation.
+     *
+     * @param Stub $stub     The Stub for the casted object
+     * @param bool $isNested True if the object is nested in the dumped structure
+     *
+     * @return array The object casted as array
+     */
+    protected function castObject(Stub $stub, $isNested)
+    {
+        $obj = $stub->value;
+        $class = $stub->class;
+
+        if (isset($class[15]) && "\0" === $class[15] && 0 === strpos($class, "class@anonymous\x00")) {
+            $stub->class = get_parent_class($class).'@anonymous';
+        }
+        if (isset($this->classInfo[$class])) {
+            list($i, $parents, $hasDebugInfo, $fileInfo) = $this->classInfo[$class];
+        } else {
+            $i = 2;
+            $parents = [$class];
+            $hasDebugInfo = method_exists($class, '__debugInfo');
+
+            foreach (class_parents($class) as $p) {
+                $parents[] = $p;
+                ++$i;
+            }
+            foreach (class_implements($class) as $p) {
+                $parents[] = $p;
+                ++$i;
+            }
+            $parents[] = '*';
+
+            $r = new \ReflectionClass($class);
+            $fileInfo = $r->isInternal() || $r->isSubclassOf(Stub::class) ? [] : [
+                'file' => $r->getFileName(),
+                'line' => $r->getStartLine(),
+            ];
+
+            $this->classInfo[$class] = [$i, $parents, $hasDebugInfo, $fileInfo];
+        }
+
+        $stub->attr += $fileInfo;
+        $a = Caster::castObject($obj, $class, $hasDebugInfo);
+
+        try {
+            while ($i--) {
+                if (!empty($this->casters[$p = $parents[$i]])) {
+                    foreach ($this->casters[$p] as $callback) {
+                        $a = $callback($obj, $a, $stub, $isNested, $this->filter);
+                    }
+                }
+            }
+        } catch (\Exception $e) {
+            $a = [(Stub::TYPE_OBJECT === $stub->type ? Caster::PREFIX_VIRTUAL : '').'⚠' => new ThrowingCasterException($e)] + $a;
+        }
+
+        return $a;
+    }
+
+    /**
+     * Casts a resource to an array representation.
+     *
+     * @param Stub $stub     The Stub for the casted resource
+     * @param bool $isNested True if the object is nested in the dumped structure
+     *
+     * @return array The resource casted as array
+     */
+    protected function castResource(Stub $stub, $isNested)
+    {
+        $a = [];
+        $res = $stub->value;
+        $type = $stub->class;
+
+        try {
+            if (!empty($this->casters[':'.$type])) {
+                foreach ($this->casters[':'.$type] as $callback) {
+                    $a = $callback($res, $a, $stub, $isNested, $this->filter);
+                }
+            }
+        } catch (\Exception $e) {
+            $a = [(Stub::TYPE_OBJECT === $stub->type ? Caster::PREFIX_VIRTUAL : '').'⚠' => new ThrowingCasterException($e)] + $a;
+        }
+
+        return $a;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Cloner/ClonerInterface.php b/vendor/symfony/var-dumper/Cloner/ClonerInterface.php
new file mode 100755
index 0000000..7ed287a
--- /dev/null
+++ b/vendor/symfony/var-dumper/Cloner/ClonerInterface.php
@@ -0,0 +1,27 @@
+<?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\VarDumper\Cloner;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+interface ClonerInterface
+{
+    /**
+     * Clones a PHP variable.
+     *
+     * @param mixed $var Any PHP variable
+     *
+     * @return Data The cloned variable represented by a Data object
+     */
+    public function cloneVar($var);
+}
diff --git a/vendor/symfony/var-dumper/Cloner/Cursor.php b/vendor/symfony/var-dumper/Cloner/Cursor.php
new file mode 100755
index 0000000..5b0542f
--- /dev/null
+++ b/vendor/symfony/var-dumper/Cloner/Cursor.php
@@ -0,0 +1,43 @@
+<?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\VarDumper\Cloner;
+
+/**
+ * Represents the current state of a dumper while dumping.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class Cursor
+{
+    const HASH_INDEXED = Stub::ARRAY_INDEXED;
+    const HASH_ASSOC = Stub::ARRAY_ASSOC;
+    const HASH_OBJECT = Stub::TYPE_OBJECT;
+    const HASH_RESOURCE = Stub::TYPE_RESOURCE;
+
+    public $depth = 0;
+    public $refIndex = 0;
+    public $softRefTo = 0;
+    public $softRefCount = 0;
+    public $softRefHandle = 0;
+    public $hardRefTo = 0;
+    public $hardRefCount = 0;
+    public $hardRefHandle = 0;
+    public $hashType;
+    public $hashKey;
+    public $hashKeyIsBinary;
+    public $hashIndex = 0;
+    public $hashLength = 0;
+    public $hashCut = 0;
+    public $stop = false;
+    public $attr = [];
+    public $skipChildren = false;
+}
diff --git a/vendor/symfony/var-dumper/Cloner/Data.php b/vendor/symfony/var-dumper/Cloner/Data.php
new file mode 100755
index 0000000..838aeb0
--- /dev/null
+++ b/vendor/symfony/var-dumper/Cloner/Data.php
@@ -0,0 +1,423 @@
+<?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\VarDumper\Cloner;
+
+use Symfony\Component\VarDumper\Caster\Caster;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class Data implements \ArrayAccess, \Countable, \IteratorAggregate
+{
+    private $data;
+    private $position = 0;
+    private $key = 0;
+    private $maxDepth = 20;
+    private $maxItemsPerDepth = -1;
+    private $useRefHandles = -1;
+
+    /**
+     * @param array $data An array as returned by ClonerInterface::cloneVar()
+     */
+    public function __construct(array $data)
+    {
+        $this->data = $data;
+    }
+
+    /**
+     * @return string The type of the value
+     */
+    public function getType()
+    {
+        $item = $this->data[$this->position][$this->key];
+
+        if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) {
+            $item = $item->value;
+        }
+        if (!$item instanceof Stub) {
+            return \gettype($item);
+        }
+        if (Stub::TYPE_STRING === $item->type) {
+            return 'string';
+        }
+        if (Stub::TYPE_ARRAY === $item->type) {
+            return 'array';
+        }
+        if (Stub::TYPE_OBJECT === $item->type) {
+            return $item->class;
+        }
+        if (Stub::TYPE_RESOURCE === $item->type) {
+            return $item->class.' resource';
+        }
+    }
+
+    /**
+     * @param bool $recursive Whether values should be resolved recursively or not
+     *
+     * @return string|int|float|bool|array|Data[]|null A native representation of the original value
+     */
+    public function getValue($recursive = false)
+    {
+        $item = $this->data[$this->position][$this->key];
+
+        if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) {
+            $item = $item->value;
+        }
+        if (!($item = $this->getStub($item)) instanceof Stub) {
+            return $item;
+        }
+        if (Stub::TYPE_STRING === $item->type) {
+            return $item->value;
+        }
+
+        $children = $item->position ? $this->data[$item->position] : [];
+
+        foreach ($children as $k => $v) {
+            if ($recursive && !($v = $this->getStub($v)) instanceof Stub) {
+                continue;
+            }
+            $children[$k] = clone $this;
+            $children[$k]->key = $k;
+            $children[$k]->position = $item->position;
+
+            if ($recursive) {
+                if (Stub::TYPE_REF === $v->type && ($v = $this->getStub($v->value)) instanceof Stub) {
+                    $recursive = (array) $recursive;
+                    if (isset($recursive[$v->position])) {
+                        continue;
+                    }
+                    $recursive[$v->position] = true;
+                }
+                $children[$k] = $children[$k]->getValue($recursive);
+            }
+        }
+
+        return $children;
+    }
+
+    public function count()
+    {
+        return \count($this->getValue());
+    }
+
+    public function getIterator()
+    {
+        if (!\is_array($value = $this->getValue())) {
+            throw new \LogicException(sprintf('%s object holds non-iterable type "%s".', self::class, \gettype($value)));
+        }
+
+        yield from $value;
+    }
+
+    public function __get($key)
+    {
+        if (null !== $data = $this->seek($key)) {
+            $item = $this->getStub($data->data[$data->position][$data->key]);
+
+            return $item instanceof Stub || [] === $item ? $data : $item;
+        }
+    }
+
+    public function __isset($key)
+    {
+        return null !== $this->seek($key);
+    }
+
+    public function offsetExists($key)
+    {
+        return $this->__isset($key);
+    }
+
+    public function offsetGet($key)
+    {
+        return $this->__get($key);
+    }
+
+    public function offsetSet($key, $value)
+    {
+        throw new \BadMethodCallException(self::class.' objects are immutable.');
+    }
+
+    public function offsetUnset($key)
+    {
+        throw new \BadMethodCallException(self::class.' objects are immutable.');
+    }
+
+    public function __toString()
+    {
+        $value = $this->getValue();
+
+        if (!\is_array($value)) {
+            return (string) $value;
+        }
+
+        return sprintf('%s (count=%d)', $this->getType(), \count($value));
+    }
+
+    /**
+     * Returns a depth limited clone of $this.
+     *
+     * @param int $maxDepth The max dumped depth level
+     *
+     * @return self A clone of $this
+     */
+    public function withMaxDepth($maxDepth)
+    {
+        $data = clone $this;
+        $data->maxDepth = (int) $maxDepth;
+
+        return $data;
+    }
+
+    /**
+     * Limits the number of elements per depth level.
+     *
+     * @param int $maxItemsPerDepth The max number of items dumped per depth level
+     *
+     * @return self A clone of $this
+     */
+    public function withMaxItemsPerDepth($maxItemsPerDepth)
+    {
+        $data = clone $this;
+        $data->maxItemsPerDepth = (int) $maxItemsPerDepth;
+
+        return $data;
+    }
+
+    /**
+     * Enables/disables objects' identifiers tracking.
+     *
+     * @param bool $useRefHandles False to hide global ref. handles
+     *
+     * @return self A clone of $this
+     */
+    public function withRefHandles($useRefHandles)
+    {
+        $data = clone $this;
+        $data->useRefHandles = $useRefHandles ? -1 : 0;
+
+        return $data;
+    }
+
+    /**
+     * Seeks to a specific key in nested data structures.
+     *
+     * @param string|int $key The key to seek to
+     *
+     * @return self|null A clone of $this or null if the key is not set
+     */
+    public function seek($key)
+    {
+        $item = $this->data[$this->position][$this->key];
+
+        if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) {
+            $item = $item->value;
+        }
+        if (!($item = $this->getStub($item)) instanceof Stub || !$item->position) {
+            return;
+        }
+        $keys = [$key];
+
+        switch ($item->type) {
+            case Stub::TYPE_OBJECT:
+                $keys[] = Caster::PREFIX_DYNAMIC.$key;
+                $keys[] = Caster::PREFIX_PROTECTED.$key;
+                $keys[] = Caster::PREFIX_VIRTUAL.$key;
+                $keys[] = "\0$item->class\0$key";
+                // no break
+            case Stub::TYPE_ARRAY:
+            case Stub::TYPE_RESOURCE:
+                break;
+            default:
+                return;
+        }
+
+        $data = null;
+        $children = $this->data[$item->position];
+
+        foreach ($keys as $key) {
+            if (isset($children[$key]) || \array_key_exists($key, $children)) {
+                $data = clone $this;
+                $data->key = $key;
+                $data->position = $item->position;
+                break;
+            }
+        }
+
+        return $data;
+    }
+
+    /**
+     * Dumps data with a DumperInterface dumper.
+     */
+    public function dump(DumperInterface $dumper)
+    {
+        $refs = [0];
+        $this->dumpItem($dumper, new Cursor(), $refs, $this->data[$this->position][$this->key]);
+    }
+
+    /**
+     * Depth-first dumping of items.
+     *
+     * @param DumperInterface $dumper The dumper being used for dumping
+     * @param Cursor          $cursor A cursor used for tracking dumper state position
+     * @param array           &$refs  A map of all references discovered while dumping
+     * @param mixed           $item   A Stub object or the original value being dumped
+     */
+    private function dumpItem($dumper, $cursor, &$refs, $item)
+    {
+        $cursor->refIndex = 0;
+        $cursor->softRefTo = $cursor->softRefHandle = $cursor->softRefCount = 0;
+        $cursor->hardRefTo = $cursor->hardRefHandle = $cursor->hardRefCount = 0;
+        $firstSeen = true;
+
+        if (!$item instanceof Stub) {
+            $cursor->attr = [];
+            $type = \gettype($item);
+            if ($item && 'array' === $type) {
+                $item = $this->getStub($item);
+            }
+        } elseif (Stub::TYPE_REF === $item->type) {
+            if ($item->handle) {
+                if (!isset($refs[$r = $item->handle - (PHP_INT_MAX >> 1)])) {
+                    $cursor->refIndex = $refs[$r] = $cursor->refIndex ?: ++$refs[0];
+                } else {
+                    $firstSeen = false;
+                }
+                $cursor->hardRefTo = $refs[$r];
+                $cursor->hardRefHandle = $this->useRefHandles & $item->handle;
+                $cursor->hardRefCount = $item->refCount;
+            }
+            $cursor->attr = $item->attr;
+            $type = $item->class ?: \gettype($item->value);
+            $item = $this->getStub($item->value);
+        }
+        if ($item instanceof Stub) {
+            if ($item->refCount) {
+                if (!isset($refs[$r = $item->handle])) {
+                    $cursor->refIndex = $refs[$r] = $cursor->refIndex ?: ++$refs[0];
+                } else {
+                    $firstSeen = false;
+                }
+                $cursor->softRefTo = $refs[$r];
+            }
+            $cursor->softRefHandle = $this->useRefHandles & $item->handle;
+            $cursor->softRefCount = $item->refCount;
+            $cursor->attr = $item->attr;
+            $cut = $item->cut;
+
+            if ($item->position && $firstSeen) {
+                $children = $this->data[$item->position];
+
+                if ($cursor->stop) {
+                    if ($cut >= 0) {
+                        $cut += \count($children);
+                    }
+                    $children = [];
+                }
+            } else {
+                $children = [];
+            }
+            switch ($item->type) {
+                case Stub::TYPE_STRING:
+                    $dumper->dumpString($cursor, $item->value, Stub::STRING_BINARY === $item->class, $cut);
+                    break;
+
+                case Stub::TYPE_ARRAY:
+                    $item = clone $item;
+                    $item->type = $item->class;
+                    $item->class = $item->value;
+                    // no break
+                case Stub::TYPE_OBJECT:
+                case Stub::TYPE_RESOURCE:
+                    $withChildren = $children && $cursor->depth !== $this->maxDepth && $this->maxItemsPerDepth;
+                    $dumper->enterHash($cursor, $item->type, $item->class, $withChildren);
+                    if ($withChildren) {
+                        if ($cursor->skipChildren) {
+                            $withChildren = false;
+                            $cut = -1;
+                        } else {
+                            $cut = $this->dumpChildren($dumper, $cursor, $refs, $children, $cut, $item->type, null !== $item->class);
+                        }
+                    } elseif ($children && 0 <= $cut) {
+                        $cut += \count($children);
+                    }
+                    $cursor->skipChildren = false;
+                    $dumper->leaveHash($cursor, $item->type, $item->class, $withChildren, $cut);
+                    break;
+
+                default:
+                    throw new \RuntimeException(sprintf('Unexpected Stub type: %s', $item->type));
+            }
+        } elseif ('array' === $type) {
+            $dumper->enterHash($cursor, Cursor::HASH_INDEXED, 0, false);
+            $dumper->leaveHash($cursor, Cursor::HASH_INDEXED, 0, false, 0);
+        } elseif ('string' === $type) {
+            $dumper->dumpString($cursor, $item, false, 0);
+        } else {
+            $dumper->dumpScalar($cursor, $type, $item);
+        }
+    }
+
+    /**
+     * Dumps children of hash structures.
+     *
+     * @param DumperInterface $dumper
+     * @param Cursor          $parentCursor The cursor of the parent hash
+     * @param array           &$refs        A map of all references discovered while dumping
+     * @param array           $children     The children to dump
+     * @param int             $hashCut      The number of items removed from the original hash
+     * @param string          $hashType     A Cursor::HASH_* const
+     * @param bool            $dumpKeys     Whether keys should be dumped or not
+     *
+     * @return int The final number of removed items
+     */
+    private function dumpChildren($dumper, $parentCursor, &$refs, $children, $hashCut, $hashType, $dumpKeys)
+    {
+        $cursor = clone $parentCursor;
+        ++$cursor->depth;
+        $cursor->hashType = $hashType;
+        $cursor->hashIndex = 0;
+        $cursor->hashLength = \count($children);
+        $cursor->hashCut = $hashCut;
+        foreach ($children as $key => $child) {
+            $cursor->hashKeyIsBinary = isset($key[0]) && !preg_match('//u', $key);
+            $cursor->hashKey = $dumpKeys ? $key : null;
+            $this->dumpItem($dumper, $cursor, $refs, $child);
+            if (++$cursor->hashIndex === $this->maxItemsPerDepth || $cursor->stop) {
+                $parentCursor->stop = true;
+
+                return $hashCut >= 0 ? $hashCut + $cursor->hashLength - $cursor->hashIndex : $hashCut;
+            }
+        }
+
+        return $hashCut;
+    }
+
+    private function getStub($item)
+    {
+        if (!$item || !\is_array($item)) {
+            return $item;
+        }
+
+        $stub = new Stub();
+        $stub->type = Stub::TYPE_ARRAY;
+        foreach ($item as $stub->class => $stub->position) {
+        }
+        if (isset($item[0])) {
+            $stub->cut = $item[0];
+        }
+        $stub->value = $stub->cut + ($stub->position ? \count($this->data[$stub->position]) : 0);
+
+        return $stub;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Cloner/DumperInterface.php b/vendor/symfony/var-dumper/Cloner/DumperInterface.php
new file mode 100755
index 0000000..cb498ff
--- /dev/null
+++ b/vendor/symfony/var-dumper/Cloner/DumperInterface.php
@@ -0,0 +1,60 @@
+<?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\VarDumper\Cloner;
+
+/**
+ * DumperInterface used by Data objects.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+interface DumperInterface
+{
+    /**
+     * Dumps a scalar value.
+     *
+     * @param Cursor                $cursor The Cursor position in the dump
+     * @param string                $type   The PHP type of the value being dumped
+     * @param string|int|float|bool $value  The scalar value being dumped
+     */
+    public function dumpScalar(Cursor $cursor, $type, $value);
+
+    /**
+     * Dumps a string.
+     *
+     * @param Cursor $cursor The Cursor position in the dump
+     * @param string $str    The string being dumped
+     * @param bool   $bin    Whether $str is UTF-8 or binary encoded
+     * @param int    $cut    The number of characters $str has been cut by
+     */
+    public function dumpString(Cursor $cursor, $str, $bin, $cut);
+
+    /**
+     * Dumps while entering an hash.
+     *
+     * @param Cursor $cursor   The Cursor position in the dump
+     * @param int    $type     A Cursor::HASH_* const for the type of hash
+     * @param string $class    The object class, resource type or array count
+     * @param bool   $hasChild When the dump of the hash has child item
+     */
+    public function enterHash(Cursor $cursor, $type, $class, $hasChild);
+
+    /**
+     * Dumps while leaving an hash.
+     *
+     * @param Cursor $cursor   The Cursor position in the dump
+     * @param int    $type     A Cursor::HASH_* const for the type of hash
+     * @param string $class    The object class, resource type or array count
+     * @param bool   $hasChild When the dump of the hash has child item
+     * @param int    $cut      The number of items the hash has been cut by
+     */
+    public function leaveHash(Cursor $cursor, $type, $class, $hasChild, $cut);
+}
diff --git a/vendor/symfony/var-dumper/Cloner/Stub.php b/vendor/symfony/var-dumper/Cloner/Stub.php
new file mode 100755
index 0000000..27dd3ef
--- /dev/null
+++ b/vendor/symfony/var-dumper/Cloner/Stub.php
@@ -0,0 +1,67 @@
+<?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\VarDumper\Cloner;
+
+/**
+ * Represents the main properties of a PHP variable.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class Stub
+{
+    const TYPE_REF = 1;
+    const TYPE_STRING = 2;
+    const TYPE_ARRAY = 3;
+    const TYPE_OBJECT = 4;
+    const TYPE_RESOURCE = 5;
+
+    const STRING_BINARY = 1;
+    const STRING_UTF8 = 2;
+
+    const ARRAY_ASSOC = 1;
+    const ARRAY_INDEXED = 2;
+
+    public $type = self::TYPE_REF;
+    public $class = '';
+    public $value;
+    public $cut = 0;
+    public $handle = 0;
+    public $refCount = 0;
+    public $position = 0;
+    public $attr = [];
+
+    private static $defaultProperties = [];
+
+    /**
+     * @internal
+     */
+    public function __sleep()
+    {
+        $properties = [];
+
+        if (!isset(self::$defaultProperties[$c = \get_class($this)])) {
+            self::$defaultProperties[$c] = get_class_vars($c);
+
+            foreach ((new \ReflectionClass($c))->getStaticProperties() as $k => $v) {
+                unset(self::$defaultProperties[$c][$k]);
+            }
+        }
+
+        foreach (self::$defaultProperties[$c] as $k => $v) {
+            if ($this->$k !== $v) {
+                $properties[] = $k;
+            }
+        }
+
+        return $properties;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Cloner/VarCloner.php b/vendor/symfony/var-dumper/Cloner/VarCloner.php
new file mode 100755
index 0000000..b8318c5
--- /dev/null
+++ b/vendor/symfony/var-dumper/Cloner/VarCloner.php
@@ -0,0 +1,295 @@
+<?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\VarDumper\Cloner;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class VarCloner extends AbstractCloner
+{
+    private static $gid;
+    private static $arrayCache = [];
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function doClone($var)
+    {
+        $len = 1;                       // Length of $queue
+        $pos = 0;                       // Number of cloned items past the minimum depth
+        $refsCounter = 0;               // Hard references counter
+        $queue = [[$var]];    // This breadth-first queue is the return value
+        $indexedArrays = [];       // Map of queue indexes that hold numerically indexed arrays
+        $hardRefs = [];            // Map of original zval ids to stub objects
+        $objRefs = [];             // Map of original object handles to their stub object counterpart
+        $objects = [];             // Keep a ref to objects to ensure their handle cannot be reused while cloning
+        $resRefs = [];             // Map of original resource handles to their stub object counterpart
+        $values = [];              // Map of stub objects' ids to original values
+        $maxItems = $this->maxItems;
+        $maxString = $this->maxString;
+        $minDepth = $this->minDepth;
+        $currentDepth = 0;              // Current tree depth
+        $currentDepthFinalIndex = 0;    // Final $queue index for current tree depth
+        $minimumDepthReached = 0 === $minDepth; // Becomes true when minimum tree depth has been reached
+        $cookie = (object) [];     // Unique object used to detect hard references
+        $a = null;                      // Array cast for nested structures
+        $stub = null;                   // Stub capturing the main properties of an original item value
+                                        // or null if the original value is used directly
+
+        if (!$gid = self::$gid) {
+            $gid = self::$gid = uniqid(mt_rand(), true); // Unique string used to detect the special $GLOBALS variable
+        }
+        $arrayStub = new Stub();
+        $arrayStub->type = Stub::TYPE_ARRAY;
+        $fromObjCast = false;
+
+        for ($i = 0; $i < $len; ++$i) {
+            // Detect when we move on to the next tree depth
+            if ($i > $currentDepthFinalIndex) {
+                ++$currentDepth;
+                $currentDepthFinalIndex = $len - 1;
+                if ($currentDepth >= $minDepth) {
+                    $minimumDepthReached = true;
+                }
+            }
+
+            $refs = $vals = $queue[$i];
+            if (\PHP_VERSION_ID < 70200 && empty($indexedArrays[$i])) {
+                // see https://wiki.php.net/rfc/convert_numeric_keys_in_object_array_casts
+                foreach ($vals as $k => $v) {
+                    if (\is_int($k)) {
+                        continue;
+                    }
+                    foreach ([$k => true] as $gk => $gv) {
+                    }
+                    if ($gk !== $k) {
+                        $fromObjCast = true;
+                        $refs = $vals = \array_values($queue[$i]);
+                        break;
+                    }
+                }
+            }
+            foreach ($vals as $k => $v) {
+                // $v is the original value or a stub object in case of hard references
+                $refs[$k] = $cookie;
+                if ($zvalIsRef = $vals[$k] === $cookie) {
+                    $vals[$k] = &$stub;         // Break hard references to make $queue completely
+                    unset($stub);               // independent from the original structure
+                    if ($v instanceof Stub && isset($hardRefs[\spl_object_id($v)])) {
+                        $vals[$k] = $refs[$k] = $v;
+                        if ($v->value instanceof Stub && (Stub::TYPE_OBJECT === $v->value->type || Stub::TYPE_RESOURCE === $v->value->type)) {
+                            ++$v->value->refCount;
+                        }
+                        ++$v->refCount;
+                        continue;
+                    }
+                    $refs[$k] = $vals[$k] = new Stub();
+                    $refs[$k]->value = $v;
+                    $h = \spl_object_id($refs[$k]);
+                    $hardRefs[$h] = &$refs[$k];
+                    $values[$h] = $v;
+                    $vals[$k]->handle = ++$refsCounter;
+                }
+                // Create $stub when the original value $v can not be used directly
+                // If $v is a nested structure, put that structure in array $a
+                switch (true) {
+                    case null === $v:
+                    case \is_bool($v):
+                    case \is_int($v):
+                    case \is_float($v):
+                        continue 2;
+
+                    case \is_string($v):
+                        if ('' === $v) {
+                            continue 2;
+                        }
+                        if (!\preg_match('//u', $v)) {
+                            $stub = new Stub();
+                            $stub->type = Stub::TYPE_STRING;
+                            $stub->class = Stub::STRING_BINARY;
+                            if (0 <= $maxString && 0 < $cut = \strlen($v) - $maxString) {
+                                $stub->cut = $cut;
+                                $stub->value = \substr($v, 0, -$cut);
+                            } else {
+                                $stub->value = $v;
+                            }
+                        } elseif (0 <= $maxString && isset($v[1 + ($maxString >> 2)]) && 0 < $cut = \mb_strlen($v, 'UTF-8') - $maxString) {
+                            $stub = new Stub();
+                            $stub->type = Stub::TYPE_STRING;
+                            $stub->class = Stub::STRING_UTF8;
+                            $stub->cut = $cut;
+                            $stub->value = \mb_substr($v, 0, $maxString, 'UTF-8');
+                        } else {
+                            continue 2;
+                        }
+                        $a = null;
+                        break;
+
+                    case \is_array($v):
+                        if (!$v) {
+                            continue 2;
+                        }
+                        $stub = $arrayStub;
+                        $stub->class = Stub::ARRAY_INDEXED;
+
+                        $j = -1;
+                        foreach ($v as $gk => $gv) {
+                            if ($gk !== ++$j) {
+                                $stub->class = Stub::ARRAY_ASSOC;
+                                break;
+                            }
+                        }
+                        $a = $v;
+
+                        if (Stub::ARRAY_ASSOC === $stub->class) {
+                            // Copies of $GLOBALS have very strange behavior,
+                            // let's detect them with some black magic
+                            $a[$gid] = true;
+
+                            // Happens with copies of $GLOBALS
+                            if (isset($v[$gid])) {
+                                unset($v[$gid]);
+                                $a = [];
+                                foreach ($v as $gk => &$gv) {
+                                    $a[$gk] = &$gv;
+                                }
+                                unset($gv);
+                            } else {
+                                $a = $v;
+                            }
+                        } elseif (\PHP_VERSION_ID < 70200) {
+                            $indexedArrays[$len] = true;
+                        }
+                        break;
+
+                    case \is_object($v):
+                    case $v instanceof \__PHP_Incomplete_Class:
+                        if (empty($objRefs[$h = \spl_object_id($v)])) {
+                            $stub = new Stub();
+                            $stub->type = Stub::TYPE_OBJECT;
+                            $stub->class = \get_class($v);
+                            $stub->value = $v;
+                            $stub->handle = $h;
+                            $a = $this->castObject($stub, 0 < $i);
+                            if ($v !== $stub->value) {
+                                if (Stub::TYPE_OBJECT !== $stub->type || null === $stub->value) {
+                                    break;
+                                }
+                                $stub->handle = $h = \spl_object_id($stub->value);
+                            }
+                            $stub->value = null;
+                            if (0 <= $maxItems && $maxItems <= $pos && $minimumDepthReached) {
+                                $stub->cut = \count($a);
+                                $a = null;
+                            }
+                        }
+                        if (empty($objRefs[$h])) {
+                            $objRefs[$h] = $stub;
+                            $objects[] = $v;
+                        } else {
+                            $stub = $objRefs[$h];
+                            ++$stub->refCount;
+                            $a = null;
+                        }
+                        break;
+
+                    default: // resource
+                        if (empty($resRefs[$h = (int) $v])) {
+                            $stub = new Stub();
+                            $stub->type = Stub::TYPE_RESOURCE;
+                            if ('Unknown' === $stub->class = @\get_resource_type($v)) {
+                                $stub->class = 'Closed';
+                            }
+                            $stub->value = $v;
+                            $stub->handle = $h;
+                            $a = $this->castResource($stub, 0 < $i);
+                            $stub->value = null;
+                            if (0 <= $maxItems && $maxItems <= $pos && $minimumDepthReached) {
+                                $stub->cut = \count($a);
+                                $a = null;
+                            }
+                        }
+                        if (empty($resRefs[$h])) {
+                            $resRefs[$h] = $stub;
+                        } else {
+                            $stub = $resRefs[$h];
+                            ++$stub->refCount;
+                            $a = null;
+                        }
+                        break;
+                }
+
+                if ($a) {
+                    if (!$minimumDepthReached || 0 > $maxItems) {
+                        $queue[$len] = $a;
+                        $stub->position = $len++;
+                    } elseif ($pos < $maxItems) {
+                        if ($maxItems < $pos += \count($a)) {
+                            $a = \array_slice($a, 0, $maxItems - $pos);
+                            if ($stub->cut >= 0) {
+                                $stub->cut += $pos - $maxItems;
+                            }
+                        }
+                        $queue[$len] = $a;
+                        $stub->position = $len++;
+                    } elseif ($stub->cut >= 0) {
+                        $stub->cut += \count($a);
+                        $stub->position = 0;
+                    }
+                }
+
+                if ($arrayStub === $stub) {
+                    if ($arrayStub->cut) {
+                        $stub = [$arrayStub->cut, $arrayStub->class => $arrayStub->position];
+                        $arrayStub->cut = 0;
+                    } elseif (isset(self::$arrayCache[$arrayStub->class][$arrayStub->position])) {
+                        $stub = self::$arrayCache[$arrayStub->class][$arrayStub->position];
+                    } else {
+                        self::$arrayCache[$arrayStub->class][$arrayStub->position] = $stub = [$arrayStub->class => $arrayStub->position];
+                    }
+                }
+
+                if ($zvalIsRef) {
+                    $refs[$k]->value = $stub;
+                } else {
+                    $vals[$k] = $stub;
+                }
+            }
+
+            if ($fromObjCast) {
+                $fromObjCast = false;
+                $refs = $vals;
+                $vals = [];
+                $j = -1;
+                foreach ($queue[$i] as $k => $v) {
+                    foreach ([$k => true] as $gk => $gv) {
+                    }
+                    if ($gk !== $k) {
+                        $vals = (object) $vals;
+                        $vals->{$k} = $refs[++$j];
+                        $vals = (array) $vals;
+                    } else {
+                        $vals[$k] = $refs[++$j];
+                    }
+                }
+            }
+
+            $queue[$i] = $vals;
+        }
+
+        foreach ($values as $h => $v) {
+            $hardRefs[$h] = $v;
+        }
+
+        return $queue;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Command/Descriptor/CliDescriptor.php b/vendor/symfony/var-dumper/Command/Descriptor/CliDescriptor.php
new file mode 100755
index 0000000..dc77d03
--- /dev/null
+++ b/vendor/symfony/var-dumper/Command/Descriptor/CliDescriptor.php
@@ -0,0 +1,88 @@
+<?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\VarDumper\Command\Descriptor;
+
+use Symfony\Component\Console\Formatter\OutputFormatterStyle;
+use Symfony\Component\Console\Input\ArrayInput;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+use Symfony\Component\VarDumper\Cloner\Data;
+use Symfony\Component\VarDumper\Dumper\CliDumper;
+
+/**
+ * Describe collected data clones for cli output.
+ *
+ * @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
+ *
+ * @final
+ */
+class CliDescriptor implements DumpDescriptorInterface
+{
+    private $dumper;
+    private $lastIdentifier;
+    private $supportsHref;
+
+    public function __construct(CliDumper $dumper)
+    {
+        $this->dumper = $dumper;
+        $this->supportsHref = method_exists(OutputFormatterStyle::class, 'setHref');
+    }
+
+    public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void
+    {
+        $io = $output instanceof SymfonyStyle ? $output : new SymfonyStyle(new ArrayInput([]), $output);
+        $this->dumper->setColors($output->isDecorated());
+
+        $rows = [['date', date('r', $context['timestamp'])]];
+        $lastIdentifier = $this->lastIdentifier;
+        $this->lastIdentifier = $clientId;
+
+        $section = "Received from client #$clientId";
+        if (isset($context['request'])) {
+            $request = $context['request'];
+            $this->lastIdentifier = $request['identifier'];
+            $section = sprintf('%s %s', $request['method'], $request['uri']);
+            if ($controller = $request['controller']) {
+                $rows[] = ['controller', rtrim($this->dumper->dump($controller, true), "\n")];
+            }
+        } elseif (isset($context['cli'])) {
+            $this->lastIdentifier = $context['cli']['identifier'];
+            $section = '$ '.$context['cli']['command_line'];
+        }
+
+        if ($this->lastIdentifier !== $lastIdentifier) {
+            $io->section($section);
+        }
+
+        if (isset($context['source'])) {
+            $source = $context['source'];
+            $sourceInfo = sprintf('%s on line %d', $source['name'], $source['line']);
+            $fileLink = $source['file_link'] ?? null;
+            if ($this->supportsHref && $fileLink) {
+                $sourceInfo = sprintf('<href=%s>%s</>', $fileLink, $sourceInfo);
+            }
+            $rows[] = ['source', $sourceInfo];
+            $file = $source['file_relative'] ?? $source['file'];
+            $rows[] = ['file', $file];
+        }
+
+        $io->table([], $rows);
+
+        if (!$this->supportsHref && isset($fileLink)) {
+            $io->writeln(['<info>Open source in your IDE/browser:</info>', $fileLink]);
+            $io->newLine();
+        }
+
+        $this->dumper->dump($data);
+        $io->newLine();
+    }
+}
diff --git a/vendor/symfony/var-dumper/Command/Descriptor/DumpDescriptorInterface.php b/vendor/symfony/var-dumper/Command/Descriptor/DumpDescriptorInterface.php
new file mode 100755
index 0000000..267d27b
--- /dev/null
+++ b/vendor/symfony/var-dumper/Command/Descriptor/DumpDescriptorInterface.php
@@ -0,0 +1,23 @@
+<?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\VarDumper\Command\Descriptor;
+
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\VarDumper\Cloner\Data;
+
+/**
+ * @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
+ */
+interface DumpDescriptorInterface
+{
+    public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void;
+}
diff --git a/vendor/symfony/var-dumper/Command/Descriptor/HtmlDescriptor.php b/vendor/symfony/var-dumper/Command/Descriptor/HtmlDescriptor.php
new file mode 100755
index 0000000..35a203b
--- /dev/null
+++ b/vendor/symfony/var-dumper/Command/Descriptor/HtmlDescriptor.php
@@ -0,0 +1,119 @@
+<?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\VarDumper\Command\Descriptor;
+
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\VarDumper\Cloner\Data;
+use Symfony\Component\VarDumper\Dumper\HtmlDumper;
+
+/**
+ * Describe collected data clones for html output.
+ *
+ * @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
+ *
+ * @final
+ */
+class HtmlDescriptor implements DumpDescriptorInterface
+{
+    private $dumper;
+    private $initialized = false;
+
+    public function __construct(HtmlDumper $dumper)
+    {
+        $this->dumper = $dumper;
+    }
+
+    public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void
+    {
+        if (!$this->initialized) {
+            $styles = file_get_contents(__DIR__.'/../../Resources/css/htmlDescriptor.css');
+            $scripts = file_get_contents(__DIR__.'/../../Resources/js/htmlDescriptor.js');
+            $output->writeln("<style>$styles</style><script>$scripts</script>");
+            $this->initialized = true;
+        }
+
+        $title = '-';
+        if (isset($context['request'])) {
+            $request = $context['request'];
+            $controller = "<span class='dumped-tag'>{$this->dumper->dump($request['controller'], true, ['maxDepth' => 0])}</span>";
+            $title = sprintf('<code>%s</code> <a href="%s">%s</a>', $request['method'], $uri = $request['uri'], $uri);
+            $dedupIdentifier = $request['identifier'];
+        } elseif (isset($context['cli'])) {
+            $title = '<code>$ </code>'.$context['cli']['command_line'];
+            $dedupIdentifier = $context['cli']['identifier'];
+        } else {
+            $dedupIdentifier = uniqid('', true);
+        }
+
+        $sourceDescription = '';
+        if (isset($context['source'])) {
+            $source = $context['source'];
+            $projectDir = $source['project_dir'] ?? null;
+            $sourceDescription = sprintf('%s on line %d', $source['name'], $source['line']);
+            if (isset($source['file_link'])) {
+                $sourceDescription = sprintf('<a href="%s">%s</a>', $source['file_link'], $sourceDescription);
+            }
+        }
+
+        $isoDate = $this->extractDate($context, 'c');
+        $tags = array_filter([
+            'controller' => $controller ?? null,
+            'project dir' => $projectDir ?? null,
+        ]);
+
+        $output->writeln(<<<HTML
+<article data-dedup-id="$dedupIdentifier">
+    <header>
+        <div class="row">
+            <h2 class="col">$title</h2>
+            <time class="col text-small" title="$isoDate" datetime="$isoDate">
+                {$this->extractDate($context)}
+            </time>
+        </div>
+        {$this->renderTags($tags)}
+    </header>
+    <section class="body">
+        <p class="text-small">
+            $sourceDescription
+        </p>
+        {$this->dumper->dump($data, true)}
+    </section>
+</article>
+HTML
+        );
+    }
+
+    private function extractDate(array $context, string $format = 'r'): string
+    {
+        return date($format, $context['timestamp']);
+    }
+
+    private function renderTags(array $tags): string
+    {
+        if (!$tags) {
+            return '';
+        }
+
+        $renderedTags = '';
+        foreach ($tags as $key => $value) {
+            $renderedTags .= sprintf('<li><span class="badge">%s</span>%s</li>', $key, $value);
+        }
+
+        return <<<HTML
+<div class="row">
+    <ul class="tags">
+        $renderedTags
+    </ul>
+</div>
+HTML;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Command/ServerDumpCommand.php b/vendor/symfony/var-dumper/Command/ServerDumpCommand.php
new file mode 100755
index 0000000..eb807e3
--- /dev/null
+++ b/vendor/symfony/var-dumper/Command/ServerDumpCommand.php
@@ -0,0 +1,99 @@
+<?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\VarDumper\Command;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Exception\InvalidArgumentException;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+use Symfony\Component\VarDumper\Cloner\Data;
+use Symfony\Component\VarDumper\Command\Descriptor\CliDescriptor;
+use Symfony\Component\VarDumper\Command\Descriptor\DumpDescriptorInterface;
+use Symfony\Component\VarDumper\Command\Descriptor\HtmlDescriptor;
+use Symfony\Component\VarDumper\Dumper\CliDumper;
+use Symfony\Component\VarDumper\Dumper\HtmlDumper;
+use Symfony\Component\VarDumper\Server\DumpServer;
+
+/**
+ * Starts a dump server to collect and output dumps on a single place with multiple formats support.
+ *
+ * @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
+ *
+ * @final
+ */
+class ServerDumpCommand extends Command
+{
+    protected static $defaultName = 'server:dump';
+
+    private $server;
+
+    /** @var DumpDescriptorInterface[] */
+    private $descriptors;
+
+    public function __construct(DumpServer $server, array $descriptors = [])
+    {
+        $this->server = $server;
+        $this->descriptors = $descriptors + [
+            'cli' => new CliDescriptor(new CliDumper()),
+            'html' => new HtmlDescriptor(new HtmlDumper()),
+        ];
+
+        parent::__construct();
+    }
+
+    protected function configure()
+    {
+        $availableFormats = implode(', ', array_keys($this->descriptors));
+
+        $this
+            ->addOption('format', null, InputOption::VALUE_REQUIRED, sprintf('The output format (%s)', $availableFormats), 'cli')
+            ->setDescription('Starts a dump server that collects and displays dumps in a single place')
+            ->setHelp(<<<'EOF'
+<info>%command.name%</info> starts a dump server that collects and displays
+dumps in a single place for debugging you application:
+
+  <info>php %command.full_name%</info>
+
+You can consult dumped data in HTML format in your browser by providing the <comment>--format=html</comment> option
+and redirecting the output to a file:
+
+  <info>php %command.full_name% --format="html" > dump.html</info>
+
+EOF
+            )
+        ;
+    }
+
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        $io = new SymfonyStyle($input, $output);
+        $format = $input->getOption('format');
+
+        if (!$descriptor = $this->descriptors[$format] ?? null) {
+            throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $format));
+        }
+
+        $errorIo = $io->getErrorStyle();
+        $errorIo->title('Symfony Var Dumper Server');
+
+        $this->server->start();
+
+        $errorIo->success(sprintf('Server listening on %s', $this->server->getHost()));
+        $errorIo->comment('Quit the server with CONTROL-C.');
+
+        $this->server->listen(function (Data $data, array $context, int $clientId) use ($descriptor, $io) {
+            $descriptor->describe($io, $data, $context, $clientId);
+        });
+    }
+}
diff --git a/vendor/symfony/var-dumper/Dumper/AbstractDumper.php b/vendor/symfony/var-dumper/Dumper/AbstractDumper.php
new file mode 100755
index 0000000..9c0ff36
--- /dev/null
+++ b/vendor/symfony/var-dumper/Dumper/AbstractDumper.php
@@ -0,0 +1,211 @@
+<?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\VarDumper\Dumper;
+
+use Symfony\Component\VarDumper\Cloner\Data;
+use Symfony\Component\VarDumper\Cloner\DumperInterface;
+
+/**
+ * Abstract mechanism for dumping a Data object.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+abstract class AbstractDumper implements DataDumperInterface, DumperInterface
+{
+    const DUMP_LIGHT_ARRAY = 1;
+    const DUMP_STRING_LENGTH = 2;
+    const DUMP_COMMA_SEPARATOR = 4;
+    const DUMP_TRAILING_COMMA = 8;
+
+    public static $defaultOutput = 'php://output';
+
+    protected $line = '';
+    protected $lineDumper;
+    protected $outputStream;
+    protected $decimalPoint; // This is locale dependent
+    protected $indentPad = '  ';
+    protected $flags;
+
+    private $charset;
+
+    /**
+     * @param callable|resource|string|null $output  A line dumper callable, an opened stream or an output path, defaults to static::$defaultOutput
+     * @param string|null                   $charset The default character encoding to use for non-UTF8 strings
+     * @param int                           $flags   A bit field of static::DUMP_* constants to fine tune dumps representation
+     */
+    public function __construct($output = null, string $charset = null, int $flags = 0)
+    {
+        $this->flags = $flags;
+        $this->setCharset($charset ?: ini_get('php.output_encoding') ?: ini_get('default_charset') ?: 'UTF-8');
+        $this->decimalPoint = localeconv();
+        $this->decimalPoint = $this->decimalPoint['decimal_point'];
+        $this->setOutput($output ?: static::$defaultOutput);
+        if (!$output && \is_string(static::$defaultOutput)) {
+            static::$defaultOutput = $this->outputStream;
+        }
+    }
+
+    /**
+     * Sets the output destination of the dumps.
+     *
+     * @param callable|resource|string $output A line dumper callable, an opened stream or an output path
+     *
+     * @return callable|resource|string The previous output destination
+     */
+    public function setOutput($output)
+    {
+        $prev = null !== $this->outputStream ? $this->outputStream : $this->lineDumper;
+
+        if (\is_callable($output)) {
+            $this->outputStream = null;
+            $this->lineDumper = $output;
+        } else {
+            if (\is_string($output)) {
+                $output = fopen($output, 'wb');
+            }
+            $this->outputStream = $output;
+            $this->lineDumper = [$this, 'echoLine'];
+        }
+
+        return $prev;
+    }
+
+    /**
+     * Sets the default character encoding to use for non-UTF8 strings.
+     *
+     * @param string $charset The default character encoding to use for non-UTF8 strings
+     *
+     * @return string The previous charset
+     */
+    public function setCharset($charset)
+    {
+        $prev = $this->charset;
+
+        $charset = strtoupper($charset);
+        $charset = null === $charset || 'UTF-8' === $charset || 'UTF8' === $charset ? 'CP1252' : $charset;
+
+        $this->charset = $charset;
+
+        return $prev;
+    }
+
+    /**
+     * Sets the indentation pad string.
+     *
+     * @param string $pad A string that will be prepended to dumped lines, repeated by nesting level
+     *
+     * @return string The previous indent pad
+     */
+    public function setIndentPad($pad)
+    {
+        $prev = $this->indentPad;
+        $this->indentPad = $pad;
+
+        return $prev;
+    }
+
+    /**
+     * Dumps a Data object.
+     *
+     * @param Data                               $data   A Data object
+     * @param callable|resource|string|true|null $output A line dumper callable, an opened stream, an output path or true to return the dump
+     *
+     * @return string|null The dump as string when $output is true
+     */
+    public function dump(Data $data, $output = null)
+    {
+        $this->decimalPoint = localeconv();
+        $this->decimalPoint = $this->decimalPoint['decimal_point'];
+
+        if ($locale = $this->flags & (self::DUMP_COMMA_SEPARATOR | self::DUMP_TRAILING_COMMA) ? setlocale(LC_NUMERIC, 0) : null) {
+            setlocale(LC_NUMERIC, 'C');
+        }
+
+        if ($returnDump = true === $output) {
+            $output = fopen('php://memory', 'r+b');
+        }
+        if ($output) {
+            $prevOutput = $this->setOutput($output);
+        }
+        try {
+            $data->dump($this);
+            $this->dumpLine(-1);
+
+            if ($returnDump) {
+                $result = stream_get_contents($output, -1, 0);
+                fclose($output);
+
+                return $result;
+            }
+        } finally {
+            if ($output) {
+                $this->setOutput($prevOutput);
+            }
+            if ($locale) {
+                setlocale(LC_NUMERIC, $locale);
+            }
+        }
+    }
+
+    /**
+     * Dumps the current line.
+     *
+     * @param int $depth The recursive depth in the dumped structure for the line being dumped,
+     *                   or -1 to signal the end-of-dump to the line dumper callable
+     */
+    protected function dumpLine($depth)
+    {
+        ($this->lineDumper)($this->line, $depth, $this->indentPad);
+        $this->line = '';
+    }
+
+    /**
+     * Generic line dumper callback.
+     *
+     * @param string $line      The line to write
+     * @param int    $depth     The recursive depth in the dumped structure
+     * @param string $indentPad The line indent pad
+     */
+    protected function echoLine($line, $depth, $indentPad)
+    {
+        if (-1 !== $depth) {
+            fwrite($this->outputStream, str_repeat($indentPad, $depth).$line."\n");
+        }
+    }
+
+    /**
+     * Converts a non-UTF-8 string to UTF-8.
+     *
+     * @param string $s The non-UTF-8 string to convert
+     *
+     * @return string The string converted to UTF-8
+     */
+    protected function utf8Encode($s)
+    {
+        if (preg_match('//u', $s)) {
+            return $s;
+        }
+
+        if (!\function_exists('iconv')) {
+            throw new \RuntimeException('Unable to convert a non-UTF-8 string to UTF-8: required function iconv() does not exist. You should install ext-iconv or symfony/polyfill-iconv.');
+        }
+
+        if (false !== $c = @iconv($this->charset, 'UTF-8', $s)) {
+            return $c;
+        }
+        if ('CP1252' !== $this->charset && false !== $c = @iconv('CP1252', 'UTF-8', $s)) {
+            return $c;
+        }
+
+        return iconv('CP850', 'UTF-8', $s);
+    }
+}
diff --git a/vendor/symfony/var-dumper/Dumper/CliDumper.php b/vendor/symfony/var-dumper/Dumper/CliDumper.php
new file mode 100755
index 0000000..9b258f4
--- /dev/null
+++ b/vendor/symfony/var-dumper/Dumper/CliDumper.php
@@ -0,0 +1,643 @@
+<?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\VarDumper\Dumper;
+
+use Symfony\Component\VarDumper\Cloner\Cursor;
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * CliDumper dumps variables for command line output.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class CliDumper extends AbstractDumper
+{
+    public static $defaultColors;
+    public static $defaultOutput = 'php://stdout';
+
+    protected $colors;
+    protected $maxStringWidth = 0;
+    protected $styles = [
+        // See http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
+        'default' => '38;5;208',
+        'num' => '1;38;5;38',
+        'const' => '1;38;5;208',
+        'str' => '1;38;5;113',
+        'note' => '38;5;38',
+        'ref' => '38;5;247',
+        'public' => '',
+        'protected' => '',
+        'private' => '',
+        'meta' => '38;5;170',
+        'key' => '38;5;113',
+        'index' => '38;5;38',
+    ];
+
+    protected static $controlCharsRx = '/[\x00-\x1F\x7F]+/';
+    protected static $controlCharsMap = [
+        "\t" => '\t',
+        "\n" => '\n',
+        "\v" => '\v',
+        "\f" => '\f',
+        "\r" => '\r',
+        "\033" => '\e',
+    ];
+
+    protected $collapseNextHash = false;
+    protected $expandNextHash = false;
+
+    private $displayOptions = [
+        'fileLinkFormat' => null,
+    ];
+
+    private $handlesHrefGracefully;
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __construct($output = null, string $charset = null, int $flags = 0)
+    {
+        parent::__construct($output, $charset, $flags);
+
+        if ('\\' === \DIRECTORY_SEPARATOR && !$this->isWindowsTrueColor()) {
+            // Use only the base 16 xterm colors when using ANSICON or standard Windows 10 CLI
+            $this->setStyles([
+                'default' => '31',
+                'num' => '1;34',
+                'const' => '1;31',
+                'str' => '1;32',
+                'note' => '34',
+                'ref' => '1;30',
+                'meta' => '35',
+                'key' => '32',
+                'index' => '34',
+            ]);
+        }
+
+        $this->displayOptions['fileLinkFormat'] = ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format') ?: 'file://%f';
+    }
+
+    /**
+     * Enables/disables colored output.
+     *
+     * @param bool $colors
+     */
+    public function setColors($colors)
+    {
+        $this->colors = (bool) $colors;
+    }
+
+    /**
+     * Sets the maximum number of characters per line for dumped strings.
+     *
+     * @param int $maxStringWidth
+     */
+    public function setMaxStringWidth($maxStringWidth)
+    {
+        $this->maxStringWidth = (int) $maxStringWidth;
+    }
+
+    /**
+     * Configures styles.
+     *
+     * @param array $styles A map of style names to style definitions
+     */
+    public function setStyles(array $styles)
+    {
+        $this->styles = $styles + $this->styles;
+    }
+
+    /**
+     * Configures display options.
+     *
+     * @param array $displayOptions A map of display options to customize the behavior
+     */
+    public function setDisplayOptions(array $displayOptions)
+    {
+        $this->displayOptions = $displayOptions + $this->displayOptions;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dumpScalar(Cursor $cursor, $type, $value)
+    {
+        $this->dumpKey($cursor);
+
+        $style = 'const';
+        $attr = $cursor->attr;
+
+        switch ($type) {
+            case 'default':
+                $style = 'default';
+                break;
+
+            case 'integer':
+                $style = 'num';
+                break;
+
+            case 'double':
+                $style = 'num';
+
+                switch (true) {
+                    case INF === $value:  $value = 'INF'; break;
+                    case -INF === $value: $value = '-INF'; break;
+                    case is_nan($value):  $value = 'NAN'; break;
+                    default:
+                        $value = (string) $value;
+                        if (false === strpos($value, $this->decimalPoint)) {
+                            $value .= $this->decimalPoint.'0';
+                        }
+                        break;
+                }
+                break;
+
+            case 'NULL':
+                $value = 'null';
+                break;
+
+            case 'boolean':
+                $value = $value ? 'true' : 'false';
+                break;
+
+            default:
+                $attr += ['value' => $this->utf8Encode($value)];
+                $value = $this->utf8Encode($type);
+                break;
+        }
+
+        $this->line .= $this->style($style, $value, $attr);
+
+        $this->endValue($cursor);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dumpString(Cursor $cursor, $str, $bin, $cut)
+    {
+        $this->dumpKey($cursor);
+        $attr = $cursor->attr;
+
+        if ($bin) {
+            $str = $this->utf8Encode($str);
+        }
+        if ('' === $str) {
+            $this->line .= '""';
+            $this->endValue($cursor);
+        } else {
+            $attr += [
+                'length' => 0 <= $cut ? mb_strlen($str, 'UTF-8') + $cut : 0,
+                'binary' => $bin,
+            ];
+            $str = explode("\n", $str);
+            if (isset($str[1]) && !isset($str[2]) && !isset($str[1][0])) {
+                unset($str[1]);
+                $str[0] .= "\n";
+            }
+            $m = \count($str) - 1;
+            $i = $lineCut = 0;
+
+            if (self::DUMP_STRING_LENGTH & $this->flags) {
+                $this->line .= '('.$attr['length'].') ';
+            }
+            if ($bin) {
+                $this->line .= 'b';
+            }
+
+            if ($m) {
+                $this->line .= '"""';
+                $this->dumpLine($cursor->depth);
+            } else {
+                $this->line .= '"';
+            }
+
+            foreach ($str as $str) {
+                if ($i < $m) {
+                    $str .= "\n";
+                }
+                if (0 < $this->maxStringWidth && $this->maxStringWidth < $len = mb_strlen($str, 'UTF-8')) {
+                    $str = mb_substr($str, 0, $this->maxStringWidth, 'UTF-8');
+                    $lineCut = $len - $this->maxStringWidth;
+                }
+                if ($m && 0 < $cursor->depth) {
+                    $this->line .= $this->indentPad;
+                }
+                if ('' !== $str) {
+                    $this->line .= $this->style('str', $str, $attr);
+                }
+                if ($i++ == $m) {
+                    if ($m) {
+                        if ('' !== $str) {
+                            $this->dumpLine($cursor->depth);
+                            if (0 < $cursor->depth) {
+                                $this->line .= $this->indentPad;
+                            }
+                        }
+                        $this->line .= '"""';
+                    } else {
+                        $this->line .= '"';
+                    }
+                    if ($cut < 0) {
+                        $this->line .= '…';
+                        $lineCut = 0;
+                    } elseif ($cut) {
+                        $lineCut += $cut;
+                    }
+                }
+                if ($lineCut) {
+                    $this->line .= '…'.$lineCut;
+                    $lineCut = 0;
+                }
+
+                if ($i > $m) {
+                    $this->endValue($cursor);
+                } else {
+                    $this->dumpLine($cursor->depth);
+                }
+            }
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function enterHash(Cursor $cursor, $type, $class, $hasChild)
+    {
+        $this->dumpKey($cursor);
+        $attr = $cursor->attr;
+
+        if ($this->collapseNextHash) {
+            $cursor->skipChildren = true;
+            $this->collapseNextHash = $hasChild = false;
+        }
+
+        $class = $this->utf8Encode($class);
+        if (Cursor::HASH_OBJECT === $type) {
+            $prefix = $class && 'stdClass' !== $class ? $this->style('note', $class, $attr).' {' : '{';
+        } elseif (Cursor::HASH_RESOURCE === $type) {
+            $prefix = $this->style('note', $class.' resource', $attr).($hasChild ? ' {' : ' ');
+        } else {
+            $prefix = $class && !(self::DUMP_LIGHT_ARRAY & $this->flags) ? $this->style('note', 'array:'.$class, $attr).' [' : '[';
+        }
+
+        if ($cursor->softRefCount || 0 < $cursor->softRefHandle) {
+            $prefix .= $this->style('ref', (Cursor::HASH_RESOURCE === $type ? '@' : '#').(0 < $cursor->softRefHandle ? $cursor->softRefHandle : $cursor->softRefTo), ['count' => $cursor->softRefCount]);
+        } elseif ($cursor->hardRefTo && !$cursor->refIndex && $class) {
+            $prefix .= $this->style('ref', '&'.$cursor->hardRefTo, ['count' => $cursor->hardRefCount]);
+        } elseif (!$hasChild && Cursor::HASH_RESOURCE === $type) {
+            $prefix = substr($prefix, 0, -1);
+        }
+
+        $this->line .= $prefix;
+
+        if ($hasChild) {
+            $this->dumpLine($cursor->depth);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function leaveHash(Cursor $cursor, $type, $class, $hasChild, $cut)
+    {
+        $this->dumpEllipsis($cursor, $hasChild, $cut);
+        $this->line .= Cursor::HASH_OBJECT === $type ? '}' : (Cursor::HASH_RESOURCE !== $type ? ']' : ($hasChild ? '}' : ''));
+        $this->endValue($cursor);
+    }
+
+    /**
+     * Dumps an ellipsis for cut children.
+     *
+     * @param Cursor $cursor   The Cursor position in the dump
+     * @param bool   $hasChild When the dump of the hash has child item
+     * @param int    $cut      The number of items the hash has been cut by
+     */
+    protected function dumpEllipsis(Cursor $cursor, $hasChild, $cut)
+    {
+        if ($cut) {
+            $this->line .= ' …';
+            if (0 < $cut) {
+                $this->line .= $cut;
+            }
+            if ($hasChild) {
+                $this->dumpLine($cursor->depth + 1);
+            }
+        }
+    }
+
+    /**
+     * Dumps a key in a hash structure.
+     *
+     * @param Cursor $cursor The Cursor position in the dump
+     */
+    protected function dumpKey(Cursor $cursor)
+    {
+        if (null !== $key = $cursor->hashKey) {
+            if ($cursor->hashKeyIsBinary) {
+                $key = $this->utf8Encode($key);
+            }
+            $attr = ['binary' => $cursor->hashKeyIsBinary];
+            $bin = $cursor->hashKeyIsBinary ? 'b' : '';
+            $style = 'key';
+            switch ($cursor->hashType) {
+                default:
+                case Cursor::HASH_INDEXED:
+                    if (self::DUMP_LIGHT_ARRAY & $this->flags) {
+                        break;
+                    }
+                    $style = 'index';
+                    // no break
+                case Cursor::HASH_ASSOC:
+                    if (\is_int($key)) {
+                        $this->line .= $this->style($style, $key).' => ';
+                    } else {
+                        $this->line .= $bin.'"'.$this->style($style, $key).'" => ';
+                    }
+                    break;
+
+                case Cursor::HASH_RESOURCE:
+                    $key = "\0~\0".$key;
+                    // no break
+                case Cursor::HASH_OBJECT:
+                    if (!isset($key[0]) || "\0" !== $key[0]) {
+                        $this->line .= '+'.$bin.$this->style('public', $key).': ';
+                    } elseif (0 < strpos($key, "\0", 1)) {
+                        $key = explode("\0", substr($key, 1), 2);
+
+                        switch ($key[0][0]) {
+                            case '+': // User inserted keys
+                                $attr['dynamic'] = true;
+                                $this->line .= '+'.$bin.'"'.$this->style('public', $key[1], $attr).'": ';
+                                break 2;
+                            case '~':
+                                $style = 'meta';
+                                if (isset($key[0][1])) {
+                                    parse_str(substr($key[0], 1), $attr);
+                                    $attr += ['binary' => $cursor->hashKeyIsBinary];
+                                }
+                                break;
+                            case '*':
+                                $style = 'protected';
+                                $bin = '#'.$bin;
+                                break;
+                            default:
+                                $attr['class'] = $key[0];
+                                $style = 'private';
+                                $bin = '-'.$bin;
+                                break;
+                        }
+
+                        if (isset($attr['collapse'])) {
+                            if ($attr['collapse']) {
+                                $this->collapseNextHash = true;
+                            } else {
+                                $this->expandNextHash = true;
+                            }
+                        }
+
+                        $this->line .= $bin.$this->style($style, $key[1], $attr).(isset($attr['separator']) ? $attr['separator'] : ': ');
+                    } else {
+                        // This case should not happen
+                        $this->line .= '-'.$bin.'"'.$this->style('private', $key, ['class' => '']).'": ';
+                    }
+                    break;
+            }
+
+            if ($cursor->hardRefTo) {
+                $this->line .= $this->style('ref', '&'.($cursor->hardRefCount ? $cursor->hardRefTo : ''), ['count' => $cursor->hardRefCount]).' ';
+            }
+        }
+    }
+
+    /**
+     * Decorates a value with some style.
+     *
+     * @param string $style The type of style being applied
+     * @param string $value The value being styled
+     * @param array  $attr  Optional context information
+     *
+     * @return string The value with style decoration
+     */
+    protected function style($style, $value, $attr = [])
+    {
+        if (null === $this->colors) {
+            $this->colors = $this->supportsColors();
+        }
+
+        if (null === $this->handlesHrefGracefully) {
+            $this->handlesHrefGracefully = 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR') && !getenv('KONSOLE_VERSION');
+        }
+
+        if (isset($attr['ellipsis'], $attr['ellipsis-type'])) {
+            $prefix = substr($value, 0, -$attr['ellipsis']);
+            if ('cli' === \PHP_SAPI && 'path' === $attr['ellipsis-type'] && isset($_SERVER[$pwd = '\\' === \DIRECTORY_SEPARATOR ? 'CD' : 'PWD']) && 0 === strpos($prefix, $_SERVER[$pwd])) {
+                $prefix = '.'.substr($prefix, \strlen($_SERVER[$pwd]));
+            }
+            if (!empty($attr['ellipsis-tail'])) {
+                $prefix .= substr($value, -$attr['ellipsis'], $attr['ellipsis-tail']);
+                $value = substr($value, -$attr['ellipsis'] + $attr['ellipsis-tail']);
+            } else {
+                $value = substr($value, -$attr['ellipsis']);
+            }
+
+            $value = $this->style('default', $prefix).$this->style($style, $value);
+
+            goto href;
+        }
+
+        $map = static::$controlCharsMap;
+        $startCchr = $this->colors ? "\033[m\033[{$this->styles['default']}m" : '';
+        $endCchr = $this->colors ? "\033[m\033[{$this->styles[$style]}m" : '';
+        $value = preg_replace_callback(static::$controlCharsRx, function ($c) use ($map, $startCchr, $endCchr) {
+            $s = $startCchr;
+            $c = $c[$i = 0];
+            do {
+                $s .= isset($map[$c[$i]]) ? $map[$c[$i]] : sprintf('\x%02X', \ord($c[$i]));
+            } while (isset($c[++$i]));
+
+            return $s.$endCchr;
+        }, $value, -1, $cchrCount);
+
+        if ($this->colors) {
+            if ($cchrCount && "\033" === $value[0]) {
+                $value = substr($value, \strlen($startCchr));
+            } else {
+                $value = "\033[{$this->styles[$style]}m".$value;
+            }
+            if ($cchrCount && $endCchr === substr($value, -\strlen($endCchr))) {
+                $value = substr($value, 0, -\strlen($endCchr));
+            } else {
+                $value .= "\033[{$this->styles['default']}m";
+            }
+        }
+
+        href:
+        if ($this->colors && $this->handlesHrefGracefully) {
+            if (isset($attr['file']) && $href = $this->getSourceLink($attr['file'], isset($attr['line']) ? $attr['line'] : 0)) {
+                if ('note' === $style) {
+                    $value .= "\033]8;;{$href}\033\\^\033]8;;\033\\";
+                } else {
+                    $attr['href'] = $href;
+                }
+            }
+            if (isset($attr['href'])) {
+                $value = "\033]8;;{$attr['href']}\033\\{$value}\033]8;;\033\\";
+            }
+        }
+
+        return $value;
+    }
+
+    /**
+     * @return bool Tells if the current output stream supports ANSI colors or not
+     */
+    protected function supportsColors()
+    {
+        if ($this->outputStream !== static::$defaultOutput) {
+            return $this->hasColorSupport($this->outputStream);
+        }
+        if (null !== static::$defaultColors) {
+            return static::$defaultColors;
+        }
+        if (isset($_SERVER['argv'][1])) {
+            $colors = $_SERVER['argv'];
+            $i = \count($colors);
+            while (--$i > 0) {
+                if (isset($colors[$i][5])) {
+                    switch ($colors[$i]) {
+                        case '--ansi':
+                        case '--color':
+                        case '--color=yes':
+                        case '--color=force':
+                        case '--color=always':
+                            return static::$defaultColors = true;
+
+                        case '--no-ansi':
+                        case '--color=no':
+                        case '--color=none':
+                        case '--color=never':
+                            return static::$defaultColors = false;
+                    }
+                }
+            }
+        }
+
+        $h = stream_get_meta_data($this->outputStream) + ['wrapper_type' => null];
+        $h = 'Output' === $h['stream_type'] && 'PHP' === $h['wrapper_type'] ? fopen('php://stdout', 'wb') : $this->outputStream;
+
+        return static::$defaultColors = $this->hasColorSupport($h);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function dumpLine($depth, $endOfValue = false)
+    {
+        if ($this->colors) {
+            $this->line = sprintf("\033[%sm%s\033[m", $this->styles['default'], $this->line);
+        }
+        parent::dumpLine($depth);
+    }
+
+    protected function endValue(Cursor $cursor)
+    {
+        if (Stub::ARRAY_INDEXED === $cursor->hashType || Stub::ARRAY_ASSOC === $cursor->hashType) {
+            if (self::DUMP_TRAILING_COMMA & $this->flags && 0 < $cursor->depth) {
+                $this->line .= ',';
+            } elseif (self::DUMP_COMMA_SEPARATOR & $this->flags && 1 < $cursor->hashLength - $cursor->hashIndex) {
+                $this->line .= ',';
+            }
+        }
+
+        $this->dumpLine($cursor->depth, true);
+    }
+
+    /**
+     * Returns true if the stream supports colorization.
+     *
+     * Reference: Composer\XdebugHandler\Process::supportsColor
+     * https://github.com/composer/xdebug-handler
+     *
+     * @param mixed $stream A CLI output stream
+     *
+     * @return bool
+     */
+    private function hasColorSupport($stream)
+    {
+        if (!\is_resource($stream) || 'stream' !== get_resource_type($stream)) {
+            return false;
+        }
+
+        if ('Hyper' === getenv('TERM_PROGRAM')) {
+            return true;
+        }
+
+        if (\DIRECTORY_SEPARATOR === '\\') {
+            return (\function_exists('sapi_windows_vt100_support')
+                && @sapi_windows_vt100_support($stream))
+                || false !== getenv('ANSICON')
+                || 'ON' === getenv('ConEmuANSI')
+                || 'xterm' === getenv('TERM');
+        }
+
+        if (\function_exists('stream_isatty')) {
+            return @stream_isatty($stream);
+        }
+
+        if (\function_exists('posix_isatty')) {
+            return @posix_isatty($stream);
+        }
+
+        $stat = @fstat($stream);
+        // Check if formatted mode is S_IFCHR
+        return $stat ? 0020000 === ($stat['mode'] & 0170000) : false;
+    }
+
+    /**
+     * Returns true if the Windows terminal supports true color.
+     *
+     * Note that this does not check an output stream, but relies on environment
+     * variables from known implementations, or a PHP and Windows version that
+     * supports true color.
+     *
+     * @return bool
+     */
+    private function isWindowsTrueColor()
+    {
+        $result = 183 <= getenv('ANSICON_VER')
+            || 'ON' === getenv('ConEmuANSI')
+            || 'xterm' === getenv('TERM')
+            || 'Hyper' === getenv('TERM_PROGRAM');
+
+        if (!$result && \PHP_VERSION_ID >= 70200) {
+            $version = sprintf(
+                '%s.%s.%s',
+                PHP_WINDOWS_VERSION_MAJOR,
+                PHP_WINDOWS_VERSION_MINOR,
+                PHP_WINDOWS_VERSION_BUILD
+            );
+            $result = $version >= '10.0.15063';
+        }
+
+        return $result;
+    }
+
+    private function getSourceLink($file, $line)
+    {
+        if ($fmt = $this->displayOptions['fileLinkFormat']) {
+            return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : ($fmt->format($file, $line) ?: 'file://'.$file);
+        }
+
+        return false;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Dumper/ContextProvider/CliContextProvider.php b/vendor/symfony/var-dumper/Dumper/ContextProvider/CliContextProvider.php
new file mode 100755
index 0000000..e7f8ccf
--- /dev/null
+++ b/vendor/symfony/var-dumper/Dumper/ContextProvider/CliContextProvider.php
@@ -0,0 +1,32 @@
+<?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\VarDumper\Dumper\ContextProvider;
+
+/**
+ * Tries to provide context on CLI.
+ *
+ * @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
+ */
+final class CliContextProvider implements ContextProviderInterface
+{
+    public function getContext(): ?array
+    {
+        if ('cli' !== \PHP_SAPI) {
+            return null;
+        }
+
+        return [
+            'command_line' => $commandLine = implode(' ', $_SERVER['argv']),
+            'identifier' => hash('crc32b', $commandLine.$_SERVER['REQUEST_TIME_FLOAT']),
+        ];
+    }
+}
diff --git a/vendor/symfony/var-dumper/Dumper/ContextProvider/ContextProviderInterface.php b/vendor/symfony/var-dumper/Dumper/ContextProvider/ContextProviderInterface.php
new file mode 100755
index 0000000..38ef3b0
--- /dev/null
+++ b/vendor/symfony/var-dumper/Dumper/ContextProvider/ContextProviderInterface.php
@@ -0,0 +1,25 @@
+<?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\VarDumper\Dumper\ContextProvider;
+
+/**
+ * Interface to provide contextual data about dump data clones sent to a server.
+ *
+ * @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
+ */
+interface ContextProviderInterface
+{
+    /**
+     * @return array|null Context data or null if unable to provide any context
+     */
+    public function getContext(): ?array;
+}
diff --git a/vendor/symfony/var-dumper/Dumper/ContextProvider/RequestContextProvider.php b/vendor/symfony/var-dumper/Dumper/ContextProvider/RequestContextProvider.php
new file mode 100755
index 0000000..3684a47
--- /dev/null
+++ b/vendor/symfony/var-dumper/Dumper/ContextProvider/RequestContextProvider.php
@@ -0,0 +1,51 @@
+<?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\VarDumper\Dumper\ContextProvider;
+
+use Symfony\Component\HttpFoundation\RequestStack;
+use Symfony\Component\VarDumper\Caster\ReflectionCaster;
+use Symfony\Component\VarDumper\Cloner\VarCloner;
+
+/**
+ * Tries to provide context from a request.
+ *
+ * @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
+ */
+final class RequestContextProvider implements ContextProviderInterface
+{
+    private $requestStack;
+    private $cloner;
+
+    public function __construct(RequestStack $requestStack)
+    {
+        $this->requestStack = $requestStack;
+        $this->cloner = new VarCloner();
+        $this->cloner->setMaxItems(0);
+        $this->cloner->addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO);
+    }
+
+    public function getContext(): ?array
+    {
+        if (null === $request = $this->requestStack->getCurrentRequest()) {
+            return null;
+        }
+
+        $controller = $request->attributes->get('_controller');
+
+        return [
+            'uri' => $request->getUri(),
+            'method' => $request->getMethod(),
+            'controller' => $controller ? $this->cloner->cloneVar($controller) : $controller,
+            'identifier' => spl_object_hash($request),
+        ];
+    }
+}
diff --git a/vendor/symfony/var-dumper/Dumper/ContextProvider/SourceContextProvider.php b/vendor/symfony/var-dumper/Dumper/ContextProvider/SourceContextProvider.php
new file mode 100755
index 0000000..e43e19f
--- /dev/null
+++ b/vendor/symfony/var-dumper/Dumper/ContextProvider/SourceContextProvider.php
@@ -0,0 +1,126 @@
+<?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\VarDumper\Dumper\ContextProvider;
+
+use Symfony\Component\HttpKernel\Debug\FileLinkFormatter;
+use Symfony\Component\VarDumper\Cloner\VarCloner;
+use Symfony\Component\VarDumper\Dumper\HtmlDumper;
+use Symfony\Component\VarDumper\VarDumper;
+use Twig\Template;
+
+/**
+ * Tries to provide context from sources (class name, file, line, code excerpt, ...).
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ * @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
+ */
+final class SourceContextProvider implements ContextProviderInterface
+{
+    private $limit;
+    private $charset;
+    private $projectDir;
+    private $fileLinkFormatter;
+
+    public function __construct(string $charset = null, string $projectDir = null, FileLinkFormatter $fileLinkFormatter = null, int $limit = 9)
+    {
+        $this->charset = $charset;
+        $this->projectDir = $projectDir;
+        $this->fileLinkFormatter = $fileLinkFormatter;
+        $this->limit = $limit;
+    }
+
+    public function getContext(): ?array
+    {
+        $trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS, $this->limit);
+
+        $file = $trace[1]['file'];
+        $line = $trace[1]['line'];
+        $name = false;
+        $fileExcerpt = false;
+
+        for ($i = 2; $i < $this->limit; ++$i) {
+            if (isset($trace[$i]['class'], $trace[$i]['function'])
+                && 'dump' === $trace[$i]['function']
+                && VarDumper::class === $trace[$i]['class']
+            ) {
+                $file = $trace[$i]['file'];
+                $line = $trace[$i]['line'];
+
+                while (++$i < $this->limit) {
+                    if (isset($trace[$i]['function'], $trace[$i]['file']) && empty($trace[$i]['class']) && 0 !== strpos($trace[$i]['function'], 'call_user_func')) {
+                        $file = $trace[$i]['file'];
+                        $line = $trace[$i]['line'];
+
+                        break;
+                    } elseif (isset($trace[$i]['object']) && $trace[$i]['object'] instanceof Template) {
+                        $template = $trace[$i]['object'];
+                        $name = $template->getTemplateName();
+                        $src = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : false);
+                        $info = $template->getDebugInfo();
+                        if (isset($info[$trace[$i - 1]['line']])) {
+                            $line = $info[$trace[$i - 1]['line']];
+                            $file = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getPath() : null;
+
+                            if ($src) {
+                                $src = explode("\n", $src);
+                                $fileExcerpt = [];
+
+                                for ($i = max($line - 3, 1), $max = min($line + 3, \count($src)); $i <= $max; ++$i) {
+                                    $fileExcerpt[] = '<li'.($i === $line ? ' class="selected"' : '').'><code>'.$this->htmlEncode($src[$i - 1]).'</code></li>';
+                                }
+
+                                $fileExcerpt = '<ol start="'.max($line - 3, 1).'">'.implode("\n", $fileExcerpt).'</ol>';
+                            }
+                        }
+                        break;
+                    }
+                }
+                break;
+            }
+        }
+
+        if (false === $name) {
+            $name = str_replace('\\', '/', $file);
+            $name = substr($name, strrpos($name, '/') + 1);
+        }
+
+        $context = ['name' => $name, 'file' => $file, 'line' => $line];
+        $context['file_excerpt'] = $fileExcerpt;
+
+        if (null !== $this->projectDir) {
+            $context['project_dir'] = $this->projectDir;
+            if (0 === strpos($file, $this->projectDir)) {
+                $context['file_relative'] = ltrim(substr($file, \strlen($this->projectDir)), \DIRECTORY_SEPARATOR);
+            }
+        }
+
+        if ($this->fileLinkFormatter && $fileLink = $this->fileLinkFormatter->format($context['file'], $context['line'])) {
+            $context['file_link'] = $fileLink;
+        }
+
+        return $context;
+    }
+
+    private function htmlEncode(string $s): string
+    {
+        $html = '';
+
+        $dumper = new HtmlDumper(function ($line) use (&$html) { $html .= $line; }, $this->charset);
+        $dumper->setDumpHeader('');
+        $dumper->setDumpBoundaries('', '');
+
+        $cloner = new VarCloner();
+        $dumper->dump($cloner->cloneVar($s));
+
+        return substr(strip_tags($html), 1, -1);
+    }
+}
diff --git a/vendor/symfony/var-dumper/Dumper/DataDumperInterface.php b/vendor/symfony/var-dumper/Dumper/DataDumperInterface.php
new file mode 100755
index 0000000..b173bcc
--- /dev/null
+++ b/vendor/symfony/var-dumper/Dumper/DataDumperInterface.php
@@ -0,0 +1,24 @@
+<?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\VarDumper\Dumper;
+
+use Symfony\Component\VarDumper\Cloner\Data;
+
+/**
+ * DataDumperInterface for dumping Data objects.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+interface DataDumperInterface
+{
+    public function dump(Data $data);
+}
diff --git a/vendor/symfony/var-dumper/Dumper/HtmlDumper.php b/vendor/symfony/var-dumper/Dumper/HtmlDumper.php
new file mode 100755
index 0000000..b113fbb
--- /dev/null
+++ b/vendor/symfony/var-dumper/Dumper/HtmlDumper.php
@@ -0,0 +1,969 @@
+<?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\VarDumper\Dumper;
+
+use Symfony\Component\VarDumper\Cloner\Cursor;
+use Symfony\Component\VarDumper\Cloner\Data;
+
+/**
+ * HtmlDumper dumps variables as HTML.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class HtmlDumper extends CliDumper
+{
+    public static $defaultOutput = 'php://output';
+
+    protected static $themes = [
+        'dark' => [
+            'default' => 'background-color:#18171B; color:#FF8400; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: break-all',
+            'num' => 'font-weight:bold; color:#1299DA',
+            'const' => 'font-weight:bold',
+            'str' => 'font-weight:bold; color:#56DB3A',
+            'note' => 'color:#1299DA',
+            'ref' => 'color:#A0A0A0',
+            'public' => 'color:#FFFFFF',
+            'protected' => 'color:#FFFFFF',
+            'private' => 'color:#FFFFFF',
+            'meta' => 'color:#B729D9',
+            'key' => 'color:#56DB3A',
+            'index' => 'color:#1299DA',
+            'ellipsis' => 'color:#FF8400',
+            'ns' => 'user-select:none;',
+        ],
+        'light' => [
+            'default' => 'background:none; color:#CC7832; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: break-all',
+            'num' => 'font-weight:bold; color:#1299DA',
+            'const' => 'font-weight:bold',
+            'str' => 'font-weight:bold; color:#629755;',
+            'note' => 'color:#6897BB',
+            'ref' => 'color:#6E6E6E',
+            'public' => 'color:#262626',
+            'protected' => 'color:#262626',
+            'private' => 'color:#262626',
+            'meta' => 'color:#B729D9',
+            'key' => 'color:#789339',
+            'index' => 'color:#1299DA',
+            'ellipsis' => 'color:#CC7832',
+            'ns' => 'user-select:none;',
+        ],
+    ];
+
+    protected $dumpHeader;
+    protected $dumpPrefix = '<pre class=sf-dump id=%s data-indent-pad="%s">';
+    protected $dumpSuffix = '</pre><script>Sfdump(%s)</script>';
+    protected $dumpId = 'sf-dump';
+    protected $colors = true;
+    protected $headerIsDumped = false;
+    protected $lastDepth = -1;
+    protected $styles;
+
+    private $displayOptions = [
+        'maxDepth' => 1,
+        'maxStringLength' => 160,
+        'fileLinkFormat' => null,
+    ];
+    private $extraDisplayOptions = [];
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __construct($output = null, string $charset = null, int $flags = 0)
+    {
+        AbstractDumper::__construct($output, $charset, $flags);
+        $this->dumpId = 'sf-dump-'.mt_rand();
+        $this->displayOptions['fileLinkFormat'] = ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
+        $this->styles = static::$themes['dark'] ?? self::$themes['dark'];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setStyles(array $styles)
+    {
+        $this->headerIsDumped = false;
+        $this->styles = $styles + $this->styles;
+    }
+
+    public function setTheme(string $themeName)
+    {
+        if (!isset(static::$themes[$themeName])) {
+            throw new \InvalidArgumentException(sprintf('Theme "%s" does not exist in class "%s".', $themeName, static::class));
+        }
+
+        $this->setStyles(static::$themes[$themeName]);
+    }
+
+    /**
+     * Configures display options.
+     *
+     * @param array $displayOptions A map of display options to customize the behavior
+     */
+    public function setDisplayOptions(array $displayOptions)
+    {
+        $this->headerIsDumped = false;
+        $this->displayOptions = $displayOptions + $this->displayOptions;
+    }
+
+    /**
+     * Sets an HTML header that will be dumped once in the output stream.
+     *
+     * @param string $header An HTML string
+     */
+    public function setDumpHeader($header)
+    {
+        $this->dumpHeader = $header;
+    }
+
+    /**
+     * Sets an HTML prefix and suffix that will encapse every single dump.
+     *
+     * @param string $prefix The prepended HTML string
+     * @param string $suffix The appended HTML string
+     */
+    public function setDumpBoundaries($prefix, $suffix)
+    {
+        $this->dumpPrefix = $prefix;
+        $this->dumpSuffix = $suffix;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dump(Data $data, $output = null, array $extraDisplayOptions = [])
+    {
+        $this->extraDisplayOptions = $extraDisplayOptions;
+        $result = parent::dump($data, $output);
+        $this->dumpId = 'sf-dump-'.mt_rand();
+
+        return $result;
+    }
+
+    /**
+     * Dumps the HTML header.
+     */
+    protected function getDumpHeader()
+    {
+        $this->headerIsDumped = null !== $this->outputStream ? $this->outputStream : $this->lineDumper;
+
+        if (null !== $this->dumpHeader) {
+            return $this->dumpHeader;
+        }
+
+        $line = str_replace('{$options}', json_encode($this->displayOptions, JSON_FORCE_OBJECT), <<<'EOHTML'
+<script>
+Sfdump = window.Sfdump || (function (doc) {
+
+var refStyle = doc.createElement('style'),
+    rxEsc = /([.*+?^${}()|\[\]\/\\])/g,
+    idRx = /\bsf-dump-\d+-ref[012]\w+\b/,
+    keyHint = 0 <= navigator.platform.toUpperCase().indexOf('MAC') ? 'Cmd' : 'Ctrl',
+    addEventListener = function (e, n, cb) {
+        e.addEventListener(n, cb, false);
+    };
+
+(doc.documentElement.firstElementChild || doc.documentElement.children[0]).appendChild(refStyle);
+
+if (!doc.addEventListener) {
+    addEventListener = function (element, eventName, callback) {
+        element.attachEvent('on' + eventName, function (e) {
+            e.preventDefault = function () {e.returnValue = false;};
+            e.target = e.srcElement;
+            callback(e);
+        });
+    };
+}
+
+function toggle(a, recursive) {
+    var s = a.nextSibling || {}, oldClass = s.className, arrow, newClass;
+
+    if (/\bsf-dump-compact\b/.test(oldClass)) {
+        arrow = '▼';
+        newClass = 'sf-dump-expanded';
+    } else if (/\bsf-dump-expanded\b/.test(oldClass)) {
+        arrow = '▶';
+        newClass = 'sf-dump-compact';
+    } else {
+        return false;
+    }
+
+    if (doc.createEvent && s.dispatchEvent) {
+        var event = doc.createEvent('Event');
+        event.initEvent('sf-dump-expanded' === newClass ? 'sfbeforedumpexpand' : 'sfbeforedumpcollapse', true, false);
+
+        s.dispatchEvent(event);
+    }
+
+    a.lastChild.innerHTML = arrow;
+    s.className = s.className.replace(/\bsf-dump-(compact|expanded)\b/, newClass);
+
+    if (recursive) {
+        try {
+            a = s.querySelectorAll('.'+oldClass);
+            for (s = 0; s < a.length; ++s) {
+                if (-1 == a[s].className.indexOf(newClass)) {
+                    a[s].className = newClass;
+                    a[s].previousSibling.lastChild.innerHTML = arrow;
+                }
+            }
+        } catch (e) {
+        }
+    }
+
+    return true;
+};
+
+function collapse(a, recursive) {
+    var s = a.nextSibling || {}, oldClass = s.className;
+
+    if (/\bsf-dump-expanded\b/.test(oldClass)) {
+        toggle(a, recursive);
+
+        return true;
+    }
+
+    return false;
+};
+
+function expand(a, recursive) {
+    var s = a.nextSibling || {}, oldClass = s.className;
+
+    if (/\bsf-dump-compact\b/.test(oldClass)) {
+        toggle(a, recursive);
+
+        return true;
+    }
+
+    return false;
+};
+
+function collapseAll(root) {
+    var a = root.querySelector('a.sf-dump-toggle');
+    if (a) {
+        collapse(a, true);
+        expand(a);
+
+        return true;
+    }
+
+    return false;
+}
+
+function reveal(node) {
+    var previous, parents = [];
+
+    while ((node = node.parentNode || {}) && (previous = node.previousSibling) && 'A' === previous.tagName) {
+        parents.push(previous);
+    }
+
+    if (0 !== parents.length) {
+        parents.forEach(function (parent) {
+            expand(parent);
+        });
+
+        return true;
+    }
+
+    return false;
+}
+
+function highlight(root, activeNode, nodes) {
+    resetHighlightedNodes(root);
+
+    Array.from(nodes||[]).forEach(function (node) {
+        if (!/\bsf-dump-highlight\b/.test(node.className)) {
+            node.className = node.className + ' sf-dump-highlight';
+        }
+    });
+
+    if (!/\bsf-dump-highlight-active\b/.test(activeNode.className)) {
+        activeNode.className = activeNode.className + ' sf-dump-highlight-active';
+    }
+}
+
+function resetHighlightedNodes(root) {
+    Array.from(root.querySelectorAll('.sf-dump-str, .sf-dump-key, .sf-dump-public, .sf-dump-protected, .sf-dump-private')).forEach(function (strNode) {
+        strNode.className = strNode.className.replace(/\bsf-dump-highlight\b/, '');
+        strNode.className = strNode.className.replace(/\bsf-dump-highlight-active\b/, '');
+    });
+}
+
+return function (root, x) {
+    root = doc.getElementById(root);
+
+    var indentRx = new RegExp('^('+(root.getAttribute('data-indent-pad') || '  ').replace(rxEsc, '\\$1')+')+', 'm'),
+        options = {$options},
+        elt = root.getElementsByTagName('A'),
+        len = elt.length,
+        i = 0, s, h,
+        t = [];
+
+    while (i < len) t.push(elt[i++]);
+
+    for (i in x) {
+        options[i] = x[i];
+    }
+
+    function a(e, f) {
+        addEventListener(root, e, function (e, n) {
+            if ('A' == e.target.tagName) {
+                f(e.target, e);
+            } else if ('A' == e.target.parentNode.tagName) {
+                f(e.target.parentNode, e);
+            } else if ((n = e.target.nextElementSibling) && 'A' == n.tagName) {
+                if (!/\bsf-dump-toggle\b/.test(n.className)) {
+                    n = n.nextElementSibling;
+                }
+
+                f(n, e, true);
+            }
+        });
+    };
+    function isCtrlKey(e) {
+        return e.ctrlKey || e.metaKey;
+    }
+    function xpathString(str) {
+        var parts = str.match(/[^'"]+|['"]/g).map(function (part) {
+            if ("'" == part)  {
+                return '"\'"';
+            }
+            if ('"' == part) {
+                return "'\"'";
+            }
+
+            return "'" + part + "'";
+        });
+
+        return "concat(" + parts.join(",") + ", '')";
+    }
+    function xpathHasClass(className) {
+        return "contains(concat(' ', normalize-space(@class), ' '), ' " + className +" ')";
+    }
+    addEventListener(root, 'mouseover', function (e) {
+        if ('' != refStyle.innerHTML) {
+            refStyle.innerHTML = '';
+        }
+    });
+    a('mouseover', function (a, e, c) {
+        if (c) {
+            e.target.style.cursor = "pointer";
+        } else if (a = idRx.exec(a.className)) {
+            try {
+                refStyle.innerHTML = 'pre.sf-dump .'+a[0]+'{background-color: #B729D9; color: #FFF !important; border-radius: 2px}';
+            } catch (e) {
+            }
+        }
+    });
+    a('click', function (a, e, c) {
+        if (/\bsf-dump-toggle\b/.test(a.className)) {
+            e.preventDefault();
+            if (!toggle(a, isCtrlKey(e))) {
+                var r = doc.getElementById(a.getAttribute('href').substr(1)),
+                    s = r.previousSibling,
+                    f = r.parentNode,
+                    t = a.parentNode;
+                t.replaceChild(r, a);
+                f.replaceChild(a, s);
+                t.insertBefore(s, r);
+                f = f.firstChild.nodeValue.match(indentRx);
+                t = t.firstChild.nodeValue.match(indentRx);
+                if (f && t && f[0] !== t[0]) {
+                    r.innerHTML = r.innerHTML.replace(new RegExp('^'+f[0].replace(rxEsc, '\\$1'), 'mg'), t[0]);
+                }
+                if (/\bsf-dump-compact\b/.test(r.className)) {
+                    toggle(s, isCtrlKey(e));
+                }
+            }
+
+            if (c) {
+            } else if (doc.getSelection) {
+                try {
+                    doc.getSelection().removeAllRanges();
+                } catch (e) {
+                    doc.getSelection().empty();
+                }
+            } else {
+                doc.selection.empty();
+            }
+        } else if (/\bsf-dump-str-toggle\b/.test(a.className)) {
+            e.preventDefault();
+            e = a.parentNode.parentNode;
+            e.className = e.className.replace(/\bsf-dump-str-(expand|collapse)\b/, a.parentNode.className);
+        }
+    });
+
+    elt = root.getElementsByTagName('SAMP');
+    len = elt.length;
+    i = 0;
+
+    while (i < len) t.push(elt[i++]);
+    len = t.length;
+
+    for (i = 0; i < len; ++i) {
+        elt = t[i];
+        if ('SAMP' == elt.tagName) {
+            a = elt.previousSibling || {};
+            if ('A' != a.tagName) {
+                a = doc.createElement('A');
+                a.className = 'sf-dump-ref';
+                elt.parentNode.insertBefore(a, elt);
+            } else {
+                a.innerHTML += ' ';
+            }
+            a.title = (a.title ? a.title+'\n[' : '[')+keyHint+'+click] Expand all children';
+            a.innerHTML += '<span>▼</span>';
+            a.className += ' sf-dump-toggle';
+
+            x = 1;
+            if ('sf-dump' != elt.parentNode.className) {
+                x += elt.parentNode.getAttribute('data-depth')/1;
+            }
+            elt.setAttribute('data-depth', x);
+            var className = elt.className;
+            elt.className = 'sf-dump-expanded';
+            if (className ? 'sf-dump-expanded' !== className : (x > options.maxDepth)) {
+                toggle(a);
+            }
+        } else if (/\bsf-dump-ref\b/.test(elt.className) && (a = elt.getAttribute('href'))) {
+            a = a.substr(1);
+            elt.className += ' '+a;
+
+            if (/[\[{]$/.test(elt.previousSibling.nodeValue)) {
+                a = a != elt.nextSibling.id && doc.getElementById(a);
+                try {
+                    s = a.nextSibling;
+                    elt.appendChild(a);
+                    s.parentNode.insertBefore(a, s);
+                    if (/^[@#]/.test(elt.innerHTML)) {
+                        elt.innerHTML += ' <span>▶</span>';
+                    } else {
+                        elt.innerHTML = '<span>▶</span>';
+                        elt.className = 'sf-dump-ref';
+                    }
+                    elt.className += ' sf-dump-toggle';
+                } catch (e) {
+                    if ('&' == elt.innerHTML.charAt(0)) {
+                        elt.innerHTML = '…';
+                        elt.className = 'sf-dump-ref';
+                    }
+                }
+            }
+        }
+    }
+
+    if (doc.evaluate && Array.from && root.children.length > 1) {
+        root.setAttribute('tabindex', 0);
+
+        SearchState = function () {
+            this.nodes = [];
+            this.idx = 0;
+        };
+        SearchState.prototype = {
+            next: function () {
+                if (this.isEmpty()) {
+                    return this.current();
+                }
+                this.idx = this.idx < (this.nodes.length - 1) ? this.idx + 1 : 0;
+        
+                return this.current();
+            },
+            previous: function () {
+                if (this.isEmpty()) {
+                    return this.current();
+                }
+                this.idx = this.idx > 0 ? this.idx - 1 : (this.nodes.length - 1);
+        
+                return this.current();
+            },
+            isEmpty: function () {
+                return 0 === this.count();
+            },
+            current: function () {
+                if (this.isEmpty()) {
+                    return null;
+                }
+                return this.nodes[this.idx];
+            },
+            reset: function () {
+                this.nodes = [];
+                this.idx = 0;
+            },
+            count: function () {
+                return this.nodes.length;
+            },
+        };
+
+        function showCurrent(state)
+        {
+            var currentNode = state.current(), currentRect, searchRect;
+            if (currentNode) {
+                reveal(currentNode);
+                highlight(root, currentNode, state.nodes);
+                if ('scrollIntoView' in currentNode) {
+                    currentNode.scrollIntoView(true);
+                    currentRect = currentNode.getBoundingClientRect();
+                    searchRect = search.getBoundingClientRect();
+                    if (currentRect.top < (searchRect.top + searchRect.height)) {
+                        window.scrollBy(0, -(searchRect.top + searchRect.height + 5));
+                    }
+                }
+            }
+            counter.textContent = (state.isEmpty() ? 0 : state.idx + 1) + ' of ' + state.count();
+        }
+
+        var search = doc.createElement('div');
+        search.className = 'sf-dump-search-wrapper sf-dump-search-hidden';
+        search.innerHTML = '
+            <input type="text" class="sf-dump-search-input">
+            <span class="sf-dump-search-count">0 of 0<\/span>
+            <button type="button" class="sf-dump-search-input-previous" tabindex="-1">
+                <svg viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1683 1331l-166 165q-19 19-45 19t-45-19L896 965l-531 531q-19 19-45 19t-45-19l-166-165q-19-19-19-45.5t19-45.5l742-741q19-19 45-19t45 19l742 741q19 19 19 45.5t-19 45.5z"\/><\/svg>
+            <\/button>
+            <button type="button" class="sf-dump-search-input-next" tabindex="-1">
+                <svg viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1683 808l-742 741q-19 19-45 19t-45-19L109 808q-19-19-19-45.5t19-45.5l166-165q19-19 45-19t45 19l531 531 531-531q19-19 45-19t45 19l166 165q19 19 19 45.5t-19 45.5z"\/><\/svg>
+            <\/button>
+        ';
+        root.insertBefore(search, root.firstChild);
+
+        var state = new SearchState();
+        var searchInput = search.querySelector('.sf-dump-search-input');
+        var counter = search.querySelector('.sf-dump-search-count');
+        var searchInputTimer = 0;
+        var previousSearchQuery = '';
+
+        addEventListener(searchInput, 'keyup', function (e) {
+            var searchQuery = e.target.value;
+            /* Don't perform anything if the pressed key didn't change the query */
+            if (searchQuery === previousSearchQuery) {
+                return;
+            }
+            previousSearchQuery = searchQuery;
+            clearTimeout(searchInputTimer);
+            searchInputTimer = setTimeout(function () {
+                state.reset();
+                collapseAll(root);
+                resetHighlightedNodes(root);
+                if ('' === searchQuery) {
+                    counter.textContent = '0 of 0';
+
+                    return;
+                }
+
+                var classMatches = [
+                    "sf-dump-str",
+                    "sf-dump-key",
+                    "sf-dump-public",
+                    "sf-dump-protected",
+                    "sf-dump-private",
+                ].map(xpathHasClass).join(' or ');
+                
+                var xpathResult = doc.evaluate('.//span[' + classMatches + '][contains(translate(child::text(), ' + xpathString(searchQuery.toUpperCase()) + ', ' + xpathString(searchQuery.toLowerCase()) + '), ' + xpathString(searchQuery.toLowerCase()) + ')]', root, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
+
+                while (node = xpathResult.iterateNext()) state.nodes.push(node);
+                
+                showCurrent(state);
+            }, 400);
+        });
+
+        Array.from(search.querySelectorAll('.sf-dump-search-input-next, .sf-dump-search-input-previous')).forEach(function (btn) {
+            addEventListener(btn, 'click', function (e) {
+                e.preventDefault();
+                -1 !== e.target.className.indexOf('next') ? state.next() : state.previous();
+                searchInput.focus();
+                collapseAll(root);
+                showCurrent(state);
+            })
+        });
+
+        addEventListener(root, 'keydown', function (e) {
+            var isSearchActive = !/\bsf-dump-search-hidden\b/.test(search.className);
+            if ((114 === e.keyCode && !isSearchActive) || (isCtrlKey(e) && 70 === e.keyCode)) {
+                /* F3 or CMD/CTRL + F */
+                e.preventDefault();
+                search.className = search.className.replace(/\bsf-dump-search-hidden\b/, '');
+                searchInput.focus();
+            } else if (isSearchActive) {
+                if (27 === e.keyCode) {
+                    /* ESC key */
+                    search.className += ' sf-dump-search-hidden';
+                    e.preventDefault();
+                    resetHighlightedNodes(root);
+                    searchInput.value = '';
+                } else if (
+                    (isCtrlKey(e) && 71 === e.keyCode) /* CMD/CTRL + G */
+                    || 13 === e.keyCode /* Enter */
+                    || 114 === e.keyCode /* F3 */
+                ) {
+                    e.preventDefault();
+                    e.shiftKey ? state.previous() : state.next();
+                    collapseAll(root);
+                    showCurrent(state);
+                }
+            }
+        });
+    }
+
+    if (0 >= options.maxStringLength) {
+        return;
+    }
+    try {
+        elt = root.querySelectorAll('.sf-dump-str');
+        len = elt.length;
+        i = 0;
+        t = [];
+
+        while (i < len) t.push(elt[i++]);
+        len = t.length;
+
+        for (i = 0; i < len; ++i) {
+            elt = t[i];
+            s = elt.innerText || elt.textContent;
+            x = s.length - options.maxStringLength;
+            if (0 < x) {
+                h = elt.innerHTML;
+                elt[elt.innerText ? 'innerText' : 'textContent'] = s.substring(0, options.maxStringLength);
+                elt.className += ' sf-dump-str-collapse';
+                elt.innerHTML = '<span class=sf-dump-str-collapse>'+h+'<a class="sf-dump-ref sf-dump-str-toggle" title="Collapse"> ◀</a></span>'+
+                    '<span class=sf-dump-str-expand>'+elt.innerHTML+'<a class="sf-dump-ref sf-dump-str-toggle" title="'+x+' remaining characters"> ▶</a></span>';
+            }
+        }
+    } catch (e) {
+    }
+};
+
+})(document);
+</script><style>
+pre.sf-dump {
+    display: block;
+    white-space: pre;
+    padding: 5px;
+    overflow: initial !important;
+}
+pre.sf-dump:after {
+   content: "";
+   visibility: hidden;
+   display: block;
+   height: 0;
+   clear: both;
+}
+pre.sf-dump span {
+    display: inline;
+}
+pre.sf-dump .sf-dump-compact {
+    display: none;
+}
+pre.sf-dump abbr {
+    text-decoration: none;
+    border: none;
+    cursor: help;
+}
+pre.sf-dump a {
+    text-decoration: none;
+    cursor: pointer;
+    border: 0;
+    outline: none;
+    color: inherit;
+}
+pre.sf-dump .sf-dump-ellipsis {
+    display: inline-block;
+    overflow: visible;
+    text-overflow: ellipsis;
+    max-width: 5em;
+    white-space: nowrap;
+    overflow: hidden;
+    vertical-align: top;
+}
+pre.sf-dump .sf-dump-ellipsis+.sf-dump-ellipsis {
+    max-width: none;
+}
+pre.sf-dump code {
+    display:inline;
+    padding:0;
+    background:none;
+}
+.sf-dump-str-collapse .sf-dump-str-collapse {
+    display: none;
+}
+.sf-dump-str-expand .sf-dump-str-expand {
+    display: none;
+}
+.sf-dump-public.sf-dump-highlight,
+.sf-dump-protected.sf-dump-highlight,
+.sf-dump-private.sf-dump-highlight,
+.sf-dump-str.sf-dump-highlight,
+.sf-dump-key.sf-dump-highlight {
+    background: rgba(111, 172, 204, 0.3);
+    border: 1px solid #7DA0B1;
+    border-radius: 3px;
+}
+.sf-dump-public.sf-dump-highlight-active,
+.sf-dump-protected.sf-dump-highlight-active,
+.sf-dump-private.sf-dump-highlight-active,
+.sf-dump-str.sf-dump-highlight-active,
+.sf-dump-key.sf-dump-highlight-active {
+    background: rgba(253, 175, 0, 0.4);
+    border: 1px solid #ffa500;
+    border-radius: 3px;
+}
+pre.sf-dump .sf-dump-search-hidden {
+    display: none !important;
+}
+pre.sf-dump .sf-dump-search-wrapper {
+    font-size: 0;
+    white-space: nowrap;
+    margin-bottom: 5px;
+    display: flex;
+    position: -webkit-sticky;
+    position: sticky;
+    top: 5px;
+}
+pre.sf-dump .sf-dump-search-wrapper > * {
+    vertical-align: top;
+    box-sizing: border-box;
+    height: 21px;
+    font-weight: normal;
+    border-radius: 0;
+    background: #FFF;
+    color: #757575;
+    border: 1px solid #BBB;
+}
+pre.sf-dump .sf-dump-search-wrapper > input.sf-dump-search-input {
+    padding: 3px;
+    height: 21px;
+    font-size: 12px;
+    border-right: none;
+    border-top-left-radius: 3px;
+    border-bottom-left-radius: 3px;
+    color: #000;
+    min-width: 15px;
+    width: 100%;
+}
+pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-next,
+pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-previous {
+    background: #F2F2F2;
+    outline: none;
+    border-left: none;
+    font-size: 0;
+    line-height: 0;
+}
+pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-next {
+    border-top-right-radius: 3px;
+    border-bottom-right-radius: 3px;
+}
+pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-next > svg,
+pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-previous > svg {
+    pointer-events: none;
+    width: 12px;
+    height: 12px;
+}
+pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-count {
+    display: inline-block;
+    padding: 0 5px;
+    margin: 0;
+    border-left: none;
+    line-height: 21px;
+    font-size: 12px;
+}
+EOHTML
+        );
+
+        foreach ($this->styles as $class => $style) {
+            $line .= 'pre.sf-dump'.('default' === $class ? ', pre.sf-dump' : '').' .sf-dump-'.$class.'{'.$style.'}';
+        }
+
+        return $this->dumpHeader = preg_replace('/\s+/', ' ', $line).'</style>'.$this->dumpHeader;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function enterHash(Cursor $cursor, $type, $class, $hasChild)
+    {
+        parent::enterHash($cursor, $type, $class, false);
+
+        if ($cursor->skipChildren) {
+            $cursor->skipChildren = false;
+            $eol = ' class=sf-dump-compact>';
+        } elseif ($this->expandNextHash) {
+            $this->expandNextHash = false;
+            $eol = ' class=sf-dump-expanded>';
+        } else {
+            $eol = '>';
+        }
+
+        if ($hasChild) {
+            $this->line .= '<samp';
+            if ($cursor->refIndex) {
+                $r = Cursor::HASH_OBJECT !== $type ? 1 - (Cursor::HASH_RESOURCE !== $type) : 2;
+                $r .= $r && 0 < $cursor->softRefHandle ? $cursor->softRefHandle : $cursor->refIndex;
+
+                $this->line .= sprintf(' id=%s-ref%s', $this->dumpId, $r);
+            }
+            $this->line .= $eol;
+            $this->dumpLine($cursor->depth);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function leaveHash(Cursor $cursor, $type, $class, $hasChild, $cut)
+    {
+        $this->dumpEllipsis($cursor, $hasChild, $cut);
+        if ($hasChild) {
+            $this->line .= '</samp>';
+        }
+        parent::leaveHash($cursor, $type, $class, $hasChild, 0);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function style($style, $value, $attr = [])
+    {
+        if ('' === $value) {
+            return '';
+        }
+
+        $v = esc($value);
+
+        if ('ref' === $style) {
+            if (empty($attr['count'])) {
+                return sprintf('<a class=sf-dump-ref>%s</a>', $v);
+            }
+            $r = ('#' !== $v[0] ? 1 - ('@' !== $v[0]) : 2).substr($value, 1);
+
+            return sprintf('<a class=sf-dump-ref href=#%s-ref%s title="%d occurrences">%s</a>', $this->dumpId, $r, 1 + $attr['count'], $v);
+        }
+
+        if ('const' === $style && isset($attr['value'])) {
+            $style .= sprintf(' title="%s"', esc(is_scalar($attr['value']) ? $attr['value'] : json_encode($attr['value'])));
+        } elseif ('public' === $style) {
+            $style .= sprintf(' title="%s"', empty($attr['dynamic']) ? 'Public property' : 'Runtime added dynamic property');
+        } elseif ('str' === $style && 1 < $attr['length']) {
+            $style .= sprintf(' title="%d%s characters"', $attr['length'], $attr['binary'] ? ' binary or non-UTF-8' : '');
+        } elseif ('note' === $style && false !== $c = strrpos($v, '\\')) {
+            if (isset($attr['file']) && $link = $this->getSourceLink($attr['file'], isset($attr['line']) ? $attr['line'] : 0)) {
+                $link = sprintf('<a href="%s" rel="noopener noreferrer">^</a>', esc($this->utf8Encode($link)));
+            } else {
+                $link = '';
+            }
+
+            return sprintf('<abbr title="%s" class=sf-dump-%s>%s</abbr>%s', $v, $style, substr($v, $c + 1), $link);
+        } elseif ('protected' === $style) {
+            $style .= ' title="Protected property"';
+        } elseif ('meta' === $style && isset($attr['title'])) {
+            $style .= sprintf(' title="%s"', esc($this->utf8Encode($attr['title'])));
+        } elseif ('private' === $style) {
+            $style .= sprintf(' title="Private property defined in class:&#10;`%s`"', esc($this->utf8Encode($attr['class'])));
+        }
+        $map = static::$controlCharsMap;
+
+        if (isset($attr['ellipsis'])) {
+            $class = 'sf-dump-ellipsis';
+            if (isset($attr['ellipsis-type'])) {
+                $class = sprintf('"%s sf-dump-ellipsis-%s"', $class, $attr['ellipsis-type']);
+            }
+            $label = esc(substr($value, -$attr['ellipsis']));
+            $style = str_replace(' title="', " title=\"$v\n", $style);
+            $v = sprintf('<span class=%s>%s</span>', $class, substr($v, 0, -\strlen($label)));
+
+            if (!empty($attr['ellipsis-tail'])) {
+                $tail = \strlen(esc(substr($value, -$attr['ellipsis'], $attr['ellipsis-tail'])));
+                $v .= sprintf('<span class=sf-dump-ellipsis>%s</span>%s', substr($label, 0, $tail), substr($label, $tail));
+            } else {
+                $v .= $label;
+            }
+        }
+
+        $v = "<span class=sf-dump-{$style}>".preg_replace_callback(static::$controlCharsRx, function ($c) use ($map) {
+            $s = $b = '<span class="sf-dump-default';
+            $c = $c[$i = 0];
+            if ($ns = "\r" === $c[$i] || "\n" === $c[$i]) {
+                $s .= ' sf-dump-ns';
+            }
+            $s .= '">';
+            do {
+                if (("\r" === $c[$i] || "\n" === $c[$i]) !== $ns) {
+                    $s .= '</span>'.$b;
+                    if ($ns = !$ns) {
+                        $s .= ' sf-dump-ns';
+                    }
+                    $s .= '">';
+                }
+
+                $s .= isset($map[$c[$i]]) ? $map[$c[$i]] : sprintf('\x%02X', \ord($c[$i]));
+            } while (isset($c[++$i]));
+
+            return $s.'</span>';
+        }, $v).'</span>';
+
+        if (isset($attr['file']) && $href = $this->getSourceLink($attr['file'], isset($attr['line']) ? $attr['line'] : 0)) {
+            $attr['href'] = $href;
+        }
+        if (isset($attr['href'])) {
+            $target = isset($attr['file']) ? '' : ' target="_blank"';
+            $v = sprintf('<a href="%s"%s rel="noopener noreferrer">%s</a>', esc($this->utf8Encode($attr['href'])), $target, $v);
+        }
+        if (isset($attr['lang'])) {
+            $v = sprintf('<code class="%s">%s</code>', esc($attr['lang']), $v);
+        }
+
+        return $v;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function dumpLine($depth, $endOfValue = false)
+    {
+        if (-1 === $this->lastDepth) {
+            $this->line = sprintf($this->dumpPrefix, $this->dumpId, $this->indentPad).$this->line;
+        }
+        if ($this->headerIsDumped !== (null !== $this->outputStream ? $this->outputStream : $this->lineDumper)) {
+            $this->line = $this->getDumpHeader().$this->line;
+        }
+
+        if (-1 === $depth) {
+            $args = ['"'.$this->dumpId.'"'];
+            if ($this->extraDisplayOptions) {
+                $args[] = json_encode($this->extraDisplayOptions, JSON_FORCE_OBJECT);
+            }
+            // Replace is for BC
+            $this->line .= sprintf(str_replace('"%s"', '%s', $this->dumpSuffix), implode(', ', $args));
+        }
+        $this->lastDepth = $depth;
+
+        $this->line = mb_convert_encoding($this->line, 'HTML-ENTITIES', 'UTF-8');
+
+        if (-1 === $depth) {
+            AbstractDumper::dumpLine(0);
+        }
+        AbstractDumper::dumpLine($depth);
+    }
+
+    private function getSourceLink($file, $line)
+    {
+        $options = $this->extraDisplayOptions + $this->displayOptions;
+
+        if ($fmt = $options['fileLinkFormat']) {
+            return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : $fmt->format($file, $line);
+        }
+
+        return false;
+    }
+}
+
+function esc($str)
+{
+    return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
+}
diff --git a/vendor/symfony/var-dumper/Dumper/ServerDumper.php b/vendor/symfony/var-dumper/Dumper/ServerDumper.php
new file mode 100755
index 0000000..94795bf
--- /dev/null
+++ b/vendor/symfony/var-dumper/Dumper/ServerDumper.php
@@ -0,0 +1,53 @@
+<?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\VarDumper\Dumper;
+
+use Symfony\Component\VarDumper\Cloner\Data;
+use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface;
+use Symfony\Component\VarDumper\Server\Connection;
+
+/**
+ * ServerDumper forwards serialized Data clones to a server.
+ *
+ * @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
+ */
+class ServerDumper implements DataDumperInterface
+{
+    private $connection;
+    private $wrappedDumper;
+
+    /**
+     * @param string                     $host             The server host
+     * @param DataDumperInterface|null   $wrappedDumper    A wrapped instance used whenever we failed contacting the server
+     * @param ContextProviderInterface[] $contextProviders Context providers indexed by context name
+     */
+    public function __construct(string $host, DataDumperInterface $wrappedDumper = null, array $contextProviders = [])
+    {
+        $this->connection = new Connection($host, $contextProviders);
+        $this->wrappedDumper = $wrappedDumper;
+    }
+
+    public function getContextProviders(): array
+    {
+        return $this->connection->getContextProviders();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dump(Data $data)
+    {
+        if (!$this->connection->write($data) && $this->wrappedDumper) {
+            $this->wrappedDumper->dump($data);
+        }
+    }
+}
diff --git a/vendor/symfony/var-dumper/Exception/ThrowingCasterException.php b/vendor/symfony/var-dumper/Exception/ThrowingCasterException.php
new file mode 100755
index 0000000..af47753
--- /dev/null
+++ b/vendor/symfony/var-dumper/Exception/ThrowingCasterException.php
@@ -0,0 +1,26 @@
+<?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\VarDumper\Exception;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class ThrowingCasterException extends \Exception
+{
+    /**
+     * @param \Exception $prev The exception thrown from the caster
+     */
+    public function __construct(\Exception $prev)
+    {
+        parent::__construct('Unexpected '.\get_class($prev).' thrown from a caster: '.$prev->getMessage(), 0, $prev);
+    }
+}
diff --git a/vendor/symfony/var-dumper/LICENSE b/vendor/symfony/var-dumper/LICENSE
new file mode 100755
index 0000000..cf8b3eb
--- /dev/null
+++ b/vendor/symfony/var-dumper/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2014-2019 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.
diff --git a/vendor/symfony/var-dumper/README.md b/vendor/symfony/var-dumper/README.md
new file mode 100755
index 0000000..339f73e
--- /dev/null
+++ b/vendor/symfony/var-dumper/README.md
@@ -0,0 +1,15 @@
+VarDumper Component
+===================
+
+The VarDumper component provides mechanisms for walking through any arbitrary
+PHP variable. It provides a better `dump()` function that you can use instead
+of `var_dump`.
+
+Resources
+---------
+
+  * [Documentation](https://symfony.com/doc/current/components/var_dumper/introduction.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)
diff --git a/vendor/symfony/var-dumper/Resources/bin/var-dump-server b/vendor/symfony/var-dumper/Resources/bin/var-dump-server
new file mode 100755
index 0000000..98c813a
--- /dev/null
+++ b/vendor/symfony/var-dumper/Resources/bin/var-dump-server
@@ -0,0 +1,63 @@
+#!/usr/bin/env php
+<?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.
+ */
+
+/**
+ * Starts a dump server to collect and output dumps on a single place with multiple formats support.
+ *
+ * @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
+ */
+
+use Psr\Log\LoggerInterface;
+use Symfony\Component\Console\Application;
+use Symfony\Component\Console\Input\ArgvInput;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Logger\ConsoleLogger;
+use Symfony\Component\Console\Output\ConsoleOutput;
+use Symfony\Component\VarDumper\Command\ServerDumpCommand;
+use Symfony\Component\VarDumper\Server\DumpServer;
+
+function includeIfExists(string $file): bool
+{
+    return file_exists($file) && include $file;
+}
+
+if (
+    !includeIfExists(__DIR__ . '/../../../../autoload.php') &&
+    !includeIfExists(__DIR__ . '/../../vendor/autoload.php') &&
+    !includeIfExists(__DIR__ . '/../../../../../../vendor/autoload.php')
+) {
+    fwrite(STDERR, 'Install dependencies using Composer.'.PHP_EOL);
+    exit(1);
+}
+
+if (!class_exists(Application::class)) {
+    fwrite(STDERR, 'You need the "symfony/console" component in order to run the VarDumper server.'.PHP_EOL);
+    exit(1);
+}
+
+$input = new ArgvInput();
+$output = new ConsoleOutput();
+$defaultHost = '127.0.0.1:9912';
+$host = $input->getParameterOption(['--host'], $_SERVER['VAR_DUMPER_SERVER'] ?? $defaultHost, true);
+$logger = interface_exists(LoggerInterface::class) ? new ConsoleLogger($output->getErrorOutput()) : null;
+
+$app = new Application();
+
+$app->getDefinition()->addOption(
+    new InputOption('--host', null, InputOption::VALUE_REQUIRED, 'The address the server should listen to', $defaultHost)
+);
+
+$app->add($command = new ServerDumpCommand(new DumpServer($host, $logger)))
+    ->getApplication()
+    ->setDefaultCommand($command->getName(), true)
+    ->run($input, $output)
+;
diff --git a/vendor/symfony/var-dumper/Resources/css/htmlDescriptor.css b/vendor/symfony/var-dumper/Resources/css/htmlDescriptor.css
new file mode 100755
index 0000000..8f706d6
--- /dev/null
+++ b/vendor/symfony/var-dumper/Resources/css/htmlDescriptor.css
@@ -0,0 +1,130 @@
+body {
+    display: flex;
+    flex-direction: column-reverse;
+    justify-content: flex-end;
+    max-width: 1140px;
+    margin: auto;
+    padding: 15px;
+    word-wrap: break-word;
+    background-color: #F9F9F9;
+    color: #222;
+    font-family: Helvetica, Arial, sans-serif;
+    font-size: 14px;
+    line-height: 1.4;
+}
+p {
+    margin: 0;
+}
+a {
+    color: #218BC3;
+    text-decoration: none;
+}
+a:hover {
+    text-decoration: underline;
+}
+.text-small {
+    font-size: 12px !important;
+}
+article {
+    margin: 5px;
+    margin-bottom: 10px;
+}
+article > header > .row {
+    display: flex;
+    flex-direction: row;
+    align-items: baseline;
+    margin-bottom: 10px;
+}
+article > header > .row > .col {
+    flex: 1;
+    display: flex;
+    align-items: baseline;
+}
+article > header > .row > h2 {
+    font-size: 14px;
+    color: #222;
+    font-weight: normal;
+    font-family: "Lucida Console", monospace, sans-serif;
+    word-break: break-all;
+    margin: 20px 5px 0 0;
+    user-select: all;
+}
+article > header > .row > h2 > code {
+    white-space: nowrap;
+    user-select: none;
+    color: #cc2255;
+    background-color: #f7f7f9;
+    border: 1px solid #e1e1e8;
+    border-radius: 3px;
+    margin-right: 5px;
+    padding: 0 3px;
+}
+article > header > .row > time.col {
+    flex: 0;
+    text-align: right;
+    white-space: nowrap;
+    color: #999;
+    font-style: italic;
+}
+article > header ul.tags {
+    list-style: none;
+    padding: 0;
+    margin: 0;
+    font-size: 12px;
+}
+article > header ul.tags > li {
+    user-select: all;
+    margin-bottom: 2px;
+}
+article > header ul.tags > li > span.badge {
+    display: inline-block;
+    padding: .25em .4em;
+    margin-right: 5px;
+    border-radius: 4px;
+    background-color: #6c757d3b;
+    color: #524d4d;
+    font-size: 12px;
+    text-align: center;
+    font-weight: 700;
+    line-height: 1;
+    white-space: nowrap;
+    vertical-align: baseline;
+    user-select: none;
+}
+article > section.body {
+    border: 1px solid #d8d8d8;
+    background: #FFF;
+    padding: 10px;
+    border-radius: 3px;
+}
+pre.sf-dump {
+    border-radius: 3px;
+    margin-bottom: 0;
+}
+.hidden {
+    display: none !important;
+}
+.dumped-tag > .sf-dump {
+    display: inline-block;
+    margin: 0;
+    padding: 1px 5px;
+    line-height: 1.4;
+    vertical-align: top;
+    background-color: transparent;
+    user-select: auto;
+}
+.dumped-tag > pre.sf-dump,
+.dumped-tag > .sf-dump-default {
+    color: #CC7832;
+    background: none;
+}
+.dumped-tag > .sf-dump .sf-dump-str { color: #629755; }
+.dumped-tag > .sf-dump .sf-dump-private,
+.dumped-tag > .sf-dump .sf-dump-protected,
+.dumped-tag > .sf-dump .sf-dump-public { color: #262626; }
+.dumped-tag > .sf-dump .sf-dump-note { color: #6897BB; }
+.dumped-tag > .sf-dump .sf-dump-key { color: #789339; }
+.dumped-tag > .sf-dump .sf-dump-ref { color: #6E6E6E; }
+.dumped-tag > .sf-dump .sf-dump-ellipsis { color: #CC7832; max-width: 100em; }
+.dumped-tag > .sf-dump .sf-dump-ellipsis-path { max-width: 5em; }
+.dumped-tag > .sf-dump .sf-dump-ns { user-select: none; }
diff --git a/vendor/symfony/var-dumper/Resources/functions/dump.php b/vendor/symfony/var-dumper/Resources/functions/dump.php
new file mode 100755
index 0000000..e1543a8
--- /dev/null
+++ b/vendor/symfony/var-dumper/Resources/functions/dump.php
@@ -0,0 +1,43 @@
+<?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.
+ */
+
+use Symfony\Component\VarDumper\VarDumper;
+
+if (!function_exists('dump')) {
+    /**
+     * @author Nicolas Grekas <p@tchwork.com>
+     */
+    function dump($var, ...$moreVars)
+    {
+        VarDumper::dump($var);
+
+        foreach ($moreVars as $v) {
+            VarDumper::dump($v);
+        }
+
+        if (1 < func_num_args()) {
+            return func_get_args();
+        }
+
+        return $var;
+    }
+}
+
+if (!function_exists('dd')) {
+    function dd(...$vars)
+    {
+        foreach ($vars as $v) {
+            VarDumper::dump($v);
+        }
+
+        die(1);
+    }
+}
diff --git a/vendor/symfony/var-dumper/Resources/js/htmlDescriptor.js b/vendor/symfony/var-dumper/Resources/js/htmlDescriptor.js
new file mode 100755
index 0000000..63101e5
--- /dev/null
+++ b/vendor/symfony/var-dumper/Resources/js/htmlDescriptor.js
@@ -0,0 +1,10 @@
+document.addEventListener('DOMContentLoaded', function() {
+  let prev = null;
+  Array.from(document.getElementsByTagName('article')).reverse().forEach(function (article) {
+    const dedupId = article.dataset.dedupId;
+    if (dedupId === prev) {
+      article.getElementsByTagName('header')[0].classList.add('hidden');
+    }
+    prev = dedupId;
+  });
+});
diff --git a/vendor/symfony/var-dumper/Server/Connection.php b/vendor/symfony/var-dumper/Server/Connection.php
new file mode 100755
index 0000000..8b814cb
--- /dev/null
+++ b/vendor/symfony/var-dumper/Server/Connection.php
@@ -0,0 +1,95 @@
+<?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\VarDumper\Server;
+
+use Symfony\Component\VarDumper\Cloner\Data;
+use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface;
+
+/**
+ * Forwards serialized Data clones to a server.
+ *
+ * @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
+ */
+class Connection
+{
+    private $host;
+    private $contextProviders;
+    private $socket;
+
+    /**
+     * @param string                     $host             The server host
+     * @param ContextProviderInterface[] $contextProviders Context providers indexed by context name
+     */
+    public function __construct(string $host, array $contextProviders = [])
+    {
+        if (false === strpos($host, '://')) {
+            $host = 'tcp://'.$host;
+        }
+
+        $this->host = $host;
+        $this->contextProviders = $contextProviders;
+    }
+
+    public function getContextProviders(): array
+    {
+        return $this->contextProviders;
+    }
+
+    public function write(Data $data): bool
+    {
+        $socketIsFresh = !$this->socket;
+        if (!$this->socket = $this->socket ?: $this->createSocket()) {
+            return false;
+        }
+
+        $context = ['timestamp' => microtime(true)];
+        foreach ($this->contextProviders as $name => $provider) {
+            $context[$name] = $provider->getContext();
+        }
+        $context = array_filter($context);
+        $encodedPayload = base64_encode(serialize([$data, $context]))."\n";
+
+        set_error_handler([self::class, 'nullErrorHandler']);
+        try {
+            if (-1 !== stream_socket_sendto($this->socket, $encodedPayload)) {
+                return true;
+            }
+            if (!$socketIsFresh) {
+                stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR);
+                fclose($this->socket);
+                $this->socket = $this->createSocket();
+            }
+            if (-1 !== stream_socket_sendto($this->socket, $encodedPayload)) {
+                return true;
+            }
+        } finally {
+            restore_error_handler();
+        }
+
+        return false;
+    }
+
+    private static function nullErrorHandler($t, $m)
+    {
+        // no-op
+    }
+
+    private function createSocket()
+    {
+        set_error_handler([self::class, 'nullErrorHandler']);
+        try {
+            return stream_socket_client($this->host, $errno, $errstr, 3, STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT);
+        } finally {
+            restore_error_handler();
+        }
+    }
+}
diff --git a/vendor/symfony/var-dumper/Server/DumpServer.php b/vendor/symfony/var-dumper/Server/DumpServer.php
new file mode 100755
index 0000000..ad920bd
--- /dev/null
+++ b/vendor/symfony/var-dumper/Server/DumpServer.php
@@ -0,0 +1,107 @@
+<?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\VarDumper\Server;
+
+use Psr\Log\LoggerInterface;
+use Symfony\Component\VarDumper\Cloner\Data;
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * A server collecting Data clones sent by a ServerDumper.
+ *
+ * @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
+ *
+ * @final
+ */
+class DumpServer
+{
+    private $host;
+    private $socket;
+    private $logger;
+
+    public function __construct(string $host, LoggerInterface $logger = null)
+    {
+        if (false === strpos($host, '://')) {
+            $host = 'tcp://'.$host;
+        }
+
+        $this->host = $host;
+        $this->logger = $logger;
+    }
+
+    public function start(): void
+    {
+        if (!$this->socket = stream_socket_server($this->host, $errno, $errstr)) {
+            throw new \RuntimeException(sprintf('Server start failed on "%s": %s %s.', $this->host, $errstr, $errno));
+        }
+    }
+
+    public function listen(callable $callback): void
+    {
+        if (null === $this->socket) {
+            $this->start();
+        }
+
+        foreach ($this->getMessages() as $clientId => $message) {
+            $payload = @unserialize(base64_decode($message), ['allowed_classes' => [Data::class, Stub::class]]);
+
+            // Impossible to decode the message, give up.
+            if (false === $payload) {
+                if ($this->logger) {
+                    $this->logger->warning('Unable to decode a message from {clientId} client.', ['clientId' => $clientId]);
+                }
+
+                continue;
+            }
+
+            if (!\is_array($payload) || \count($payload) < 2 || !$payload[0] instanceof Data || !\is_array($payload[1])) {
+                if ($this->logger) {
+                    $this->logger->warning('Invalid payload from {clientId} client. Expected an array of two elements (Data $data, array $context)', ['clientId' => $clientId]);
+                }
+
+                continue;
+            }
+
+            list($data, $context) = $payload;
+
+            $callback($data, $context, $clientId);
+        }
+    }
+
+    public function getHost(): string
+    {
+        return $this->host;
+    }
+
+    private function getMessages(): iterable
+    {
+        $sockets = [(int) $this->socket => $this->socket];
+        $write = [];
+
+        while (true) {
+            $read = $sockets;
+            stream_select($read, $write, $write, null);
+
+            foreach ($read as $stream) {
+                if ($this->socket === $stream) {
+                    $stream = stream_socket_accept($this->socket);
+                    $sockets[(int) $stream] = $stream;
+                } elseif (feof($stream)) {
+                    unset($sockets[(int) $stream]);
+                    fclose($stream);
+                } else {
+                    yield (int) $stream => fgets($stream);
+                }
+            }
+        }
+    }
+}
diff --git a/vendor/symfony/var-dumper/Test/VarDumperTestTrait.php b/vendor/symfony/var-dumper/Test/VarDumperTestTrait.php
new file mode 100755
index 0000000..11c9b92
--- /dev/null
+++ b/vendor/symfony/var-dumper/Test/VarDumperTestTrait.php
@@ -0,0 +1,58 @@
+<?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\VarDumper\Test;
+
+use Symfony\Component\VarDumper\Cloner\VarCloner;
+use Symfony\Component\VarDumper\Dumper\CliDumper;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+trait VarDumperTestTrait
+{
+    public function assertDumpEquals($expected, $data, $filter = 0, $message = '')
+    {
+        $this->assertSame($this->prepareExpectation($expected, $filter), $this->getDump($data, null, $filter), $message);
+    }
+
+    public function assertDumpMatchesFormat($expected, $data, $filter = 0, $message = '')
+    {
+        $this->assertStringMatchesFormat($this->prepareExpectation($expected, $filter), $this->getDump($data, null, $filter), $message);
+    }
+
+    protected function getDump($data, $key = null, $filter = 0)
+    {
+        $flags = getenv('DUMP_LIGHT_ARRAY') ? CliDumper::DUMP_LIGHT_ARRAY : 0;
+        $flags |= getenv('DUMP_STRING_LENGTH') ? CliDumper::DUMP_STRING_LENGTH : 0;
+        $flags |= getenv('DUMP_COMMA_SEPARATOR') ? CliDumper::DUMP_COMMA_SEPARATOR : 0;
+
+        $cloner = new VarCloner();
+        $cloner->setMaxItems(-1);
+        $dumper = new CliDumper(null, null, $flags);
+        $dumper->setColors(false);
+        $data = $cloner->cloneVar($data, $filter)->withRefHandles(false);
+        if (null !== $key && null === $data = $data->seek($key)) {
+            return;
+        }
+
+        return rtrim($dumper->dump($data, true));
+    }
+
+    private function prepareExpectation($expected, $filter)
+    {
+        if (!\is_string($expected)) {
+            $expected = $this->getDump($expected, null, $filter);
+        }
+
+        return rtrim($expected);
+    }
+}
diff --git a/vendor/symfony/var-dumper/Tests/Caster/CasterTest.php b/vendor/symfony/var-dumper/Tests/Caster/CasterTest.php
new file mode 100755
index 0000000..2c2189c
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Caster/CasterTest.php
@@ -0,0 +1,178 @@
+<?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\VarDumper\Tests\Caster;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\VarDumper\Caster\Caster;
+use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class CasterTest extends TestCase
+{
+    use VarDumperTestTrait;
+
+    private $referenceArray = [
+        'null' => null,
+        'empty' => false,
+        'public' => 'pub',
+        "\0~\0virtual" => 'virt',
+        "\0+\0dynamic" => 'dyn',
+        "\0*\0protected" => 'prot',
+        "\0Foo\0private" => 'priv',
+    ];
+
+    /**
+     * @dataProvider provideFilter
+     */
+    public function testFilter($filter, $expectedDiff, $listedProperties = null)
+    {
+        if (null === $listedProperties) {
+            $filteredArray = Caster::filter($this->referenceArray, $filter);
+        } else {
+            $filteredArray = Caster::filter($this->referenceArray, $filter, $listedProperties);
+        }
+
+        $this->assertSame($expectedDiff, array_diff_assoc($this->referenceArray, $filteredArray));
+    }
+
+    public function provideFilter()
+    {
+        return [
+            [
+                0,
+                [],
+            ],
+            [
+                Caster::EXCLUDE_PUBLIC,
+                [
+                    'null' => null,
+                    'empty' => false,
+                    'public' => 'pub',
+                ],
+            ],
+            [
+                Caster::EXCLUDE_NULL,
+                [
+                    'null' => null,
+                ],
+            ],
+            [
+                Caster::EXCLUDE_EMPTY,
+                [
+                    'null' => null,
+                    'empty' => false,
+                ],
+            ],
+            [
+                Caster::EXCLUDE_VIRTUAL,
+                [
+                    "\0~\0virtual" => 'virt',
+                ],
+            ],
+            [
+                Caster::EXCLUDE_DYNAMIC,
+                [
+                    "\0+\0dynamic" => 'dyn',
+                ],
+            ],
+            [
+                Caster::EXCLUDE_PROTECTED,
+                [
+                    "\0*\0protected" => 'prot',
+                ],
+            ],
+            [
+                Caster::EXCLUDE_PRIVATE,
+                [
+                    "\0Foo\0private" => 'priv',
+                ],
+            ],
+            [
+                Caster::EXCLUDE_VERBOSE,
+                [
+                    'public' => 'pub',
+                    "\0*\0protected" => 'prot',
+                ],
+                ['public', "\0*\0protected"],
+            ],
+            [
+                Caster::EXCLUDE_NOT_IMPORTANT,
+                [
+                    'null' => null,
+                    'empty' => false,
+                    "\0~\0virtual" => 'virt',
+                    "\0+\0dynamic" => 'dyn',
+                    "\0Foo\0private" => 'priv',
+                ],
+                ['public', "\0*\0protected"],
+            ],
+            [
+                Caster::EXCLUDE_VIRTUAL | Caster::EXCLUDE_DYNAMIC,
+                [
+                    "\0~\0virtual" => 'virt',
+                    "\0+\0dynamic" => 'dyn',
+                ],
+            ],
+            [
+                Caster::EXCLUDE_NOT_IMPORTANT | Caster::EXCLUDE_VERBOSE,
+                $this->referenceArray,
+                ['public', "\0*\0protected"],
+            ],
+            [
+                Caster::EXCLUDE_NOT_IMPORTANT | Caster::EXCLUDE_EMPTY,
+                [
+                    'null' => null,
+                    'empty' => false,
+                    "\0~\0virtual" => 'virt',
+                    "\0+\0dynamic" => 'dyn',
+                    "\0*\0protected" => 'prot',
+                    "\0Foo\0private" => 'priv',
+                ],
+                ['public', 'empty'],
+            ],
+            [
+                Caster::EXCLUDE_VERBOSE | Caster::EXCLUDE_EMPTY | Caster::EXCLUDE_STRICT,
+                [
+                    'empty' => false,
+                ],
+                ['public', 'empty'],
+            ],
+        ];
+    }
+
+    public function testAnonymousClass()
+    {
+        $c = eval('return new class extends stdClass { private $foo = "foo"; };');
+
+        $this->assertDumpMatchesFormat(
+            <<<'EOTXT'
+stdClass@anonymous {
+  -foo: "foo"
+}
+EOTXT
+            , $c
+        );
+
+        $c = eval('return new class { private $foo = "foo"; };');
+
+        $this->assertDumpMatchesFormat(
+            <<<'EOTXT'
+@anonymous {
+  -foo: "foo"
+}
+EOTXT
+            , $c
+        );
+    }
+}
diff --git a/vendor/symfony/var-dumper/Tests/Caster/DateCasterTest.php b/vendor/symfony/var-dumper/Tests/Caster/DateCasterTest.php
new file mode 100755
index 0000000..dae13ef
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Caster/DateCasterTest.php
@@ -0,0 +1,390 @@
+<?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\VarDumper\Tests\Caster;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\VarDumper\Caster\Caster;
+use Symfony\Component\VarDumper\Caster\DateCaster;
+use Symfony\Component\VarDumper\Cloner\Stub;
+use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
+
+/**
+ * @author Dany Maillard <danymaillard93b@gmail.com>
+ */
+class DateCasterTest extends TestCase
+{
+    use VarDumperTestTrait;
+
+    /**
+     * @dataProvider provideDateTimes
+     */
+    public function testDumpDateTime($time, $timezone, $xDate, $xTimestamp)
+    {
+        $date = new \DateTime($time, new \DateTimeZone($timezone));
+
+        $xDump = <<<EODUMP
+DateTime @$xTimestamp {
+  date: $xDate
+}
+EODUMP;
+
+        $this->assertDumpEquals($xDump, $date);
+    }
+
+    /**
+     * @dataProvider provideDateTimes
+     */
+    public function testCastDateTime($time, $timezone, $xDate, $xTimestamp, $xInfos)
+    {
+        $stub = new Stub();
+        $date = new \DateTime($time, new \DateTimeZone($timezone));
+        $cast = DateCaster::castDateTime($date, ['foo' => 'bar'], $stub, false, 0);
+
+        $xDump = <<<EODUMP
+array:1 [
+  "\\x00~\\x00date" => $xDate
+]
+EODUMP;
+
+        $this->assertDumpEquals($xDump, $cast);
+
+        $xDump = <<<EODUMP
+Symfony\Component\VarDumper\Caster\ConstStub {
+  +type: 1
+  +class: "$xDate"
+  +value: "%A$xInfos%A"
+  +cut: 0
+  +handle: 0
+  +refCount: 0
+  +position: 0
+  +attr: []
+}
+EODUMP;
+
+        $this->assertDumpMatchesFormat($xDump, $cast["\0~\0date"]);
+    }
+
+    public function provideDateTimes()
+    {
+        return [
+            ['2017-04-30 00:00:00.000000', 'Europe/Zurich', '2017-04-30 00:00:00.0 Europe/Zurich (+02:00)', 1493503200, 'Sunday, April 30, 2017%Afrom now%ADST On'],
+            ['2017-12-31 00:00:00.000000', 'Europe/Zurich', '2017-12-31 00:00:00.0 Europe/Zurich (+01:00)', 1514674800, 'Sunday, December 31, 2017%Afrom now%ADST Off'],
+            ['2017-04-30 00:00:00.000000', '+02:00', '2017-04-30 00:00:00.0 +02:00', 1493503200, 'Sunday, April 30, 2017%Afrom now'],
+
+            ['2017-04-30 00:00:00.100000', '+00:00', '2017-04-30 00:00:00.100 +00:00', 1493510400, 'Sunday, April 30, 2017%Afrom now'],
+            ['2017-04-30 00:00:00.120000', '+00:00', '2017-04-30 00:00:00.120 +00:00', 1493510400, 'Sunday, April 30, 2017%Afrom now'],
+            ['2017-04-30 00:00:00.123000', '+00:00', '2017-04-30 00:00:00.123 +00:00', 1493510400, 'Sunday, April 30, 2017%Afrom now'],
+            ['2017-04-30 00:00:00.123400', '+00:00', '2017-04-30 00:00:00.123400 +00:00', 1493510400, 'Sunday, April 30, 2017%Afrom now'],
+            ['2017-04-30 00:00:00.123450', '+00:00', '2017-04-30 00:00:00.123450 +00:00', 1493510400, 'Sunday, April 30, 2017%Afrom now'],
+            ['2017-04-30 00:00:00.123456', '+00:00', '2017-04-30 00:00:00.123456 +00:00', 1493510400, 'Sunday, April 30, 2017%Afrom now'],
+        ];
+    }
+
+    /**
+     * @dataProvider provideIntervals
+     */
+    public function testDumpInterval($intervalSpec, $ms, $invert, $expected)
+    {
+        if ($ms && \PHP_VERSION_ID >= 70200 && version_compare(PHP_VERSION, '7.2.0rc3', '<=')) {
+            $this->markTestSkipped('Skipped on 7.2 before rc4 because of php bug #75354.');
+        }
+
+        $interval = $this->createInterval($intervalSpec, $ms, $invert);
+
+        $xDump = <<<EODUMP
+DateInterval {
+  interval: $expected
+%A}
+EODUMP;
+
+        $this->assertDumpMatchesFormat($xDump, $interval);
+    }
+
+    /**
+     * @dataProvider provideIntervals
+     */
+    public function testDumpIntervalExcludingVerbosity($intervalSpec, $ms, $invert, $expected)
+    {
+        if ($ms && \PHP_VERSION_ID >= 70200 && version_compare(PHP_VERSION, '7.2.0rc3', '<=')) {
+            $this->markTestSkipped('Skipped on 7.2 before rc4 because of php bug #75354.');
+        }
+
+        $interval = $this->createInterval($intervalSpec, $ms, $invert);
+
+        $xDump = <<<EODUMP
+DateInterval {
+  interval: $expected
+}
+EODUMP;
+
+        $this->assertDumpEquals($xDump, $interval, Caster::EXCLUDE_VERBOSE);
+    }
+
+    /**
+     * @dataProvider provideIntervals
+     */
+    public function testCastInterval($intervalSpec, $ms, $invert, $xInterval, $xSeconds)
+    {
+        if ($ms && \PHP_VERSION_ID >= 70200 && version_compare(PHP_VERSION, '7.2.0rc3', '<=')) {
+            $this->markTestSkipped('Skipped on 7.2 before rc4 because of php bug #75354.');
+        }
+
+        $interval = $this->createInterval($intervalSpec, $ms, $invert);
+        $stub = new Stub();
+
+        $cast = DateCaster::castInterval($interval, ['foo' => 'bar'], $stub, false, Caster::EXCLUDE_VERBOSE);
+
+        $xDump = <<<EODUMP
+array:1 [
+  "\\x00~\\x00interval" => $xInterval
+]
+EODUMP;
+
+        $this->assertDumpEquals($xDump, $cast);
+
+        if (null === $xSeconds) {
+            return;
+        }
+
+        $xDump = <<<EODUMP
+Symfony\Component\VarDumper\Caster\ConstStub {
+  +type: 1
+  +class: "$xInterval"
+  +value: "$xSeconds"
+  +cut: 0
+  +handle: 0
+  +refCount: 0
+  +position: 0
+  +attr: []
+}
+EODUMP;
+
+        $this->assertDumpMatchesFormat($xDump, $cast["\0~\0interval"]);
+    }
+
+    public function provideIntervals()
+    {
+        return [
+            ['PT0S', 0, 0, '0s', '0s'],
+            ['PT0S', 0.1, 0, '+ 00:00:00.100', '%is'],
+            ['PT1S', 0, 0, '+ 00:00:01.0', '%is'],
+            ['PT2M', 0, 0, '+ 00:02:00.0', '%is'],
+            ['PT3H', 0, 0, '+ 03:00:00.0', '%ss'],
+            ['P4D', 0, 0, '+ 4d', '%ss'],
+            ['P5M', 0, 0, '+ 5m', null],
+            ['P6Y', 0, 0, '+ 6y', null],
+            ['P1Y2M3DT4H5M6S', 0, 0, '+ 1y 2m 3d 04:05:06.0', null],
+            ['PT1M60S', 0, 0, '+ 00:02:00.0', null],
+            ['PT1H60M', 0, 0, '+ 02:00:00.0', null],
+            ['P1DT24H', 0, 0, '+ 2d', null],
+            ['P1M32D', 0, 0, '+ 1m 32d', null],
+
+            ['PT0S', 0, 1, '0s', '0s'],
+            ['PT0S', 0.1, 1, '- 00:00:00.100', '%is'],
+            ['PT1S', 0, 1, '- 00:00:01.0', '%is'],
+            ['PT2M', 0, 1, '- 00:02:00.0', '%is'],
+            ['PT3H', 0, 1, '- 03:00:00.0', '%ss'],
+            ['P4D', 0, 1, '- 4d', '%ss'],
+            ['P5M', 0, 1, '- 5m', null],
+            ['P6Y', 0, 1, '- 6y', null],
+            ['P1Y2M3DT4H5M6S', 0, 1, '- 1y 2m 3d 04:05:06.0', null],
+            ['PT1M60S', 0, 1, '- 00:02:00.0', null],
+            ['PT1H60M', 0, 1, '- 02:00:00.0', null],
+            ['P1DT24H', 0, 1, '- 2d', null],
+            ['P1M32D', 0, 1, '- 1m 32d', null],
+        ];
+    }
+
+    /**
+     * @dataProvider provideTimeZones
+     */
+    public function testDumpTimeZone($timezone, $expected)
+    {
+        $timezone = new \DateTimeZone($timezone);
+
+        $xDump = <<<EODUMP
+DateTimeZone {
+  timezone: $expected
+%A}
+EODUMP;
+
+        $this->assertDumpMatchesFormat($xDump, $timezone);
+    }
+
+    /**
+     * @dataProvider provideTimeZones
+     */
+    public function testDumpTimeZoneExcludingVerbosity($timezone, $expected)
+    {
+        $timezone = new \DateTimeZone($timezone);
+
+        $xDump = <<<EODUMP
+DateTimeZone {
+  timezone: $expected
+}
+EODUMP;
+
+        $this->assertDumpMatchesFormat($xDump, $timezone, Caster::EXCLUDE_VERBOSE);
+    }
+
+    /**
+     * @dataProvider provideTimeZones
+     */
+    public function testCastTimeZone($timezone, $xTimezone, $xRegion)
+    {
+        $timezone = new \DateTimeZone($timezone);
+        $stub = new Stub();
+
+        $cast = DateCaster::castTimeZone($timezone, ['foo' => 'bar'], $stub, false, Caster::EXCLUDE_VERBOSE);
+
+        $xDump = <<<EODUMP
+array:1 [
+  "\\x00~\\x00timezone" => $xTimezone
+]
+EODUMP;
+
+        $this->assertDumpMatchesFormat($xDump, $cast);
+
+        $xDump = <<<EODUMP
+Symfony\Component\VarDumper\Caster\ConstStub {
+  +type: 1
+  +class: "$xTimezone"
+  +value: "$xRegion"
+  +cut: 0
+  +handle: 0
+  +refCount: 0
+  +position: 0
+  +attr: []
+}
+EODUMP;
+
+        $this->assertDumpMatchesFormat($xDump, $cast["\0~\0timezone"]);
+    }
+
+    public function provideTimeZones()
+    {
+        $xRegion = \extension_loaded('intl') ? '%s' : '';
+
+        return [
+            // type 1 (UTC offset)
+            ['-12:00', '-12:00', ''],
+            ['+00:00', '+00:00', ''],
+            ['+14:00', '+14:00', ''],
+
+            // type 2 (timezone abbreviation)
+            ['GMT', '+00:00', ''],
+            ['a', '+01:00', ''],
+            ['b', '+02:00', ''],
+            ['z', '+00:00', ''],
+
+            // type 3 (timezone identifier)
+            ['Africa/Tunis', 'Africa/Tunis (%s:00)', $xRegion],
+            ['America/Panama', 'America/Panama (%s:00)', $xRegion],
+            ['Asia/Jerusalem', 'Asia/Jerusalem (%s:00)', $xRegion],
+            ['Atlantic/Canary', 'Atlantic/Canary (%s:00)', $xRegion],
+            ['Australia/Perth', 'Australia/Perth (%s:00)', $xRegion],
+            ['Europe/Zurich', 'Europe/Zurich (%s:00)', $xRegion],
+            ['Pacific/Tahiti', 'Pacific/Tahiti (%s:00)', $xRegion],
+        ];
+    }
+
+    /**
+     * @dataProvider providePeriods
+     */
+    public function testDumpPeriod($start, $interval, $end, $options, $expected)
+    {
+        $p = new \DatePeriod(new \DateTime($start), new \DateInterval($interval), \is_int($end) ? $end : new \DateTime($end), $options);
+
+        $xDump = <<<EODUMP
+DatePeriod {
+  period: $expected
+%A}
+EODUMP;
+
+        $this->assertDumpMatchesFormat($xDump, $p);
+    }
+
+    /**
+     * @dataProvider providePeriods
+     */
+    public function testCastPeriod($start, $interval, $end, $options, $xPeriod, $xDates)
+    {
+        $p = new \DatePeriod(new \DateTime($start), new \DateInterval($interval), \is_int($end) ? $end : new \DateTime($end), $options);
+        $stub = new Stub();
+
+        $cast = DateCaster::castPeriod($p, [], $stub, false, 0);
+
+        $xDump = <<<EODUMP
+array:1 [
+  "\\x00~\\x00period" => $xPeriod
+]
+EODUMP;
+
+        $this->assertDumpEquals($xDump, $cast);
+
+        $xDump = <<<EODUMP
+Symfony\Component\VarDumper\Caster\ConstStub {
+  +type: 1
+  +class: "$xPeriod"
+  +value: "%A$xDates%A"
+  +cut: 0
+  +handle: 0
+  +refCount: 0
+  +position: 0
+  +attr: []
+}
+EODUMP;
+
+        $this->assertDumpMatchesFormat($xDump, $cast["\0~\0period"]);
+    }
+
+    public function providePeriods()
+    {
+        $periods = [
+            ['2017-01-01', 'P1D', '2017-01-03', 0, 'every + 1d, from 2017-01-01 00:00:00.0 (included) to 2017-01-03 00:00:00.0', '1) 2017-01-01%a2) 2017-01-02'],
+            ['2017-01-01', 'P1D', 1, 0, 'every + 1d, from 2017-01-01 00:00:00.0 (included) recurring 2 time/s', '1) 2017-01-01%a2) 2017-01-02'],
+
+            ['2017-01-01', 'P1D', '2017-01-04', 0, 'every + 1d, from 2017-01-01 00:00:00.0 (included) to 2017-01-04 00:00:00.0', '1) 2017-01-01%a2) 2017-01-02%a3) 2017-01-03'],
+            ['2017-01-01', 'P1D', 2, 0, 'every + 1d, from 2017-01-01 00:00:00.0 (included) recurring 3 time/s', '1) 2017-01-01%a2) 2017-01-02%a3) 2017-01-03'],
+
+            ['2017-01-01', 'P1D', '2017-01-05', 0, 'every + 1d, from 2017-01-01 00:00:00.0 (included) to 2017-01-05 00:00:00.0', '1) 2017-01-01%a2) 2017-01-02%a1 more'],
+            ['2017-01-01', 'P1D', 3, 0, 'every + 1d, from 2017-01-01 00:00:00.0 (included) recurring 4 time/s', '1) 2017-01-01%a2) 2017-01-02%a3) 2017-01-03%a1 more'],
+
+            ['2017-01-01', 'P1D', '2017-01-21', 0, 'every + 1d, from 2017-01-01 00:00:00.0 (included) to 2017-01-21 00:00:00.0', '1) 2017-01-01%a17 more'],
+            ['2017-01-01', 'P1D', 19, 0, 'every + 1d, from 2017-01-01 00:00:00.0 (included) recurring 20 time/s', '1) 2017-01-01%a17 more'],
+
+            ['2017-01-01 01:00:00', 'P1D', '2017-01-03 01:00:00', 0, 'every + 1d, from 2017-01-01 01:00:00.0 (included) to 2017-01-03 01:00:00.0', '1) 2017-01-01 01:00:00.0%a2) 2017-01-02 01:00:00.0'],
+            ['2017-01-01 01:00:00', 'P1D', 1, 0, 'every + 1d, from 2017-01-01 01:00:00.0 (included) recurring 2 time/s', '1) 2017-01-01 01:00:00.0%a2) 2017-01-02 01:00:00.0'],
+
+            ['2017-01-01', 'P1DT1H', '2017-01-03', 0, 'every + 1d 01:00:00.0, from 2017-01-01 00:00:00.0 (included) to 2017-01-03 00:00:00.0', '1) 2017-01-01 00:00:00.0%a2) 2017-01-02 01:00:00.0'],
+            ['2017-01-01', 'P1DT1H', 1, 0, 'every + 1d 01:00:00.0, from 2017-01-01 00:00:00.0 (included) recurring 2 time/s', '1) 2017-01-01 00:00:00.0%a2) 2017-01-02 01:00:00.0'],
+
+            ['2017-01-01', 'P1D', '2017-01-04', \DatePeriod::EXCLUDE_START_DATE, 'every + 1d, from 2017-01-01 00:00:00.0 (excluded) to 2017-01-04 00:00:00.0', '1) 2017-01-02%a2) 2017-01-03'],
+            ['2017-01-01', 'P1D', 2, \DatePeriod::EXCLUDE_START_DATE, 'every + 1d, from 2017-01-01 00:00:00.0 (excluded) recurring 2 time/s', '1) 2017-01-02%a2) 2017-01-03'],
+        ];
+
+        if (\PHP_VERSION_ID < 70107) {
+            array_walk($periods, function (&$i) { $i[5] = ''; });
+        }
+
+        return $periods;
+    }
+
+    private function createInterval($intervalSpec, $ms, $invert)
+    {
+        $interval = new \DateInterval($intervalSpec);
+        $interval->f = $ms;
+        $interval->invert = $invert;
+
+        return $interval;
+    }
+}
diff --git a/vendor/symfony/var-dumper/Tests/Caster/ExceptionCasterTest.php b/vendor/symfony/var-dumper/Tests/Caster/ExceptionCasterTest.php
new file mode 100755
index 0000000..0b9c2ed
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Caster/ExceptionCasterTest.php
@@ -0,0 +1,247 @@
+<?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\VarDumper\Tests\Caster;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\VarDumper\Caster\Caster;
+use Symfony\Component\VarDumper\Caster\ExceptionCaster;
+use Symfony\Component\VarDumper\Caster\FrameStub;
+use Symfony\Component\VarDumper\Cloner\VarCloner;
+use Symfony\Component\VarDumper\Dumper\HtmlDumper;
+use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
+
+class ExceptionCasterTest extends TestCase
+{
+    use VarDumperTestTrait;
+
+    private function getTestException($msg, &$ref = null)
+    {
+        return new \Exception(''.$msg);
+    }
+
+    protected function tearDown()
+    {
+        ExceptionCaster::$srcContext = 1;
+        ExceptionCaster::$traceArgs = true;
+    }
+
+    public function testDefaultSettings()
+    {
+        $ref = ['foo'];
+        $e = $this->getTestException('foo', $ref);
+
+        $expectedDump = <<<'EODUMP'
+Exception {
+  #message: "foo"
+  #code: 0
+  #file: "%sExceptionCasterTest.php"
+  #line: 28
+  trace: {
+    %s%eTests%eCaster%eExceptionCasterTest.php:28 {
+      › {
+      ›     return new \Exception(''.$msg);
+      › }
+    }
+    %s%eTests%eCaster%eExceptionCasterTest.php:40 { …}
+    Symfony\Component\VarDumper\Tests\Caster\ExceptionCasterTest->testDefaultSettings() {}
+%A
+EODUMP;
+
+        $this->assertDumpMatchesFormat($expectedDump, $e);
+        $this->assertSame(['foo'], $ref);
+    }
+
+    public function testSeek()
+    {
+        $e = $this->getTestException(2);
+
+        $expectedDump = <<<'EODUMP'
+{
+  %s%eTests%eCaster%eExceptionCasterTest.php:28 {
+    › {
+    ›     return new \Exception(''.$msg);
+    › }
+  }
+  %s%eTests%eCaster%eExceptionCasterTest.php:65 { …}
+  Symfony\Component\VarDumper\Tests\Caster\ExceptionCasterTest->testSeek() {}
+%A
+EODUMP;
+
+        $this->assertStringMatchesFormat($expectedDump, $this->getDump($e, 'trace'));
+    }
+
+    public function testNoArgs()
+    {
+        $e = $this->getTestException(1);
+        ExceptionCaster::$traceArgs = false;
+
+        $expectedDump = <<<'EODUMP'
+Exception {
+  #message: "1"
+  #code: 0
+  #file: "%sExceptionCasterTest.php"
+  #line: 28
+  trace: {
+    %sExceptionCasterTest.php:28 {
+      › {
+      ›     return new \Exception(''.$msg);
+      › }
+    }
+    %s%eTests%eCaster%eExceptionCasterTest.php:84 { …}
+    Symfony\Component\VarDumper\Tests\Caster\ExceptionCasterTest->testNoArgs() {}
+%A
+EODUMP;
+
+        $this->assertDumpMatchesFormat($expectedDump, $e);
+    }
+
+    public function testNoSrcContext()
+    {
+        $e = $this->getTestException(1);
+        ExceptionCaster::$srcContext = -1;
+
+        $expectedDump = <<<'EODUMP'
+Exception {
+  #message: "1"
+  #code: 0
+  #file: "%sExceptionCasterTest.php"
+  #line: 28
+  trace: {
+    %s%eTests%eCaster%eExceptionCasterTest.php:28
+    %s%eTests%eCaster%eExceptionCasterTest.php:%d
+%A
+EODUMP;
+
+        $this->assertDumpMatchesFormat($expectedDump, $e);
+    }
+
+    public function testHtmlDump()
+    {
+        if (ini_get('xdebug.file_link_format') || get_cfg_var('xdebug.file_link_format')) {
+            $this->markTestSkipped('A custom file_link_format is defined.');
+        }
+
+        $e = $this->getTestException(1);
+        ExceptionCaster::$srcContext = -1;
+
+        $cloner = new VarCloner();
+        $cloner->setMaxItems(1);
+        $dumper = new HtmlDumper();
+        $dumper->setDumpHeader('<foo></foo>');
+        $dumper->setDumpBoundaries('<bar>', '</bar>');
+        $dump = $dumper->dump($cloner->cloneVar($e)->withRefHandles(false), true);
+
+        $expectedDump = <<<'EODUMP'
+<foo></foo><bar><span class=sf-dump-note>Exception</span> {<samp>
+  #<span class=sf-dump-protected title="Protected property">message</span>: "<span class=sf-dump-str>1</span>"
+  #<span class=sf-dump-protected title="Protected property">code</span>: <span class=sf-dump-num>0</span>
+  #<span class=sf-dump-protected title="Protected property">file</span>: "<span class=sf-dump-str title="%sExceptionCasterTest.php
+%d characters"><span class="sf-dump-ellipsis sf-dump-ellipsis-path">%s%eVarDumper</span><span class=sf-dump-ellipsis>%e</span>Tests%eCaster%eExceptionCasterTest.php</span>"
+  #<span class=sf-dump-protected title="Protected property">line</span>: <span class=sf-dump-num>28</span>
+  <span class=sf-dump-meta>trace</span>: {<samp>
+    <span class=sf-dump-meta title="%sExceptionCasterTest.php
+Stack level %d."><span class="sf-dump-ellipsis sf-dump-ellipsis-path">%s%eVarDumper</span><span class=sf-dump-ellipsis>%e</span>Tests%eCaster%eExceptionCasterTest.php</span>:<span class=sf-dump-num>28</span>
+     &hellip;%d
+  </samp>}
+</samp>}
+</bar>
+EODUMP;
+
+        $this->assertStringMatchesFormat($expectedDump, $dump);
+    }
+
+    /**
+     * @requires function Twig\Template::getSourceContext
+     */
+    public function testFrameWithTwig()
+    {
+        require_once \dirname(__DIR__).'/Fixtures/Twig.php';
+
+        $f = [
+            new FrameStub([
+                'file' => \dirname(__DIR__).'/Fixtures/Twig.php',
+                'line' => 20,
+                'class' => '__TwigTemplate_VarDumperFixture_u75a09',
+            ]),
+            new FrameStub([
+                'file' => \dirname(__DIR__).'/Fixtures/Twig.php',
+                'line' => 21,
+                'class' => '__TwigTemplate_VarDumperFixture_u75a09',
+                'object' => new \__TwigTemplate_VarDumperFixture_u75a09(null, __FILE__),
+            ]),
+        ];
+
+        $expectedDump = <<<'EODUMP'
+array:2 [
+  0 => {
+    class: "__TwigTemplate_VarDumperFixture_u75a09"
+    src: {
+      %sTwig.php:1 {
+        › 
+        › foo bar
+        ›   twig source
+      }
+    }
+  }
+  1 => {
+    class: "__TwigTemplate_VarDumperFixture_u75a09"
+    object: __TwigTemplate_VarDumperFixture_u75a09 {
+    %A
+    }
+    src: {
+      %sExceptionCasterTest.php:2 {
+        › foo bar
+        ›   twig source
+        › 
+      }
+    }
+  }
+]
+
+EODUMP;
+
+        $this->assertDumpMatchesFormat($expectedDump, $f);
+    }
+
+    public function testExcludeVerbosity()
+    {
+        $e = $this->getTestException('foo');
+
+        $expectedDump = <<<'EODUMP'
+Exception {
+  #message: "foo"
+  #code: 0
+  #file: "%sExceptionCasterTest.php"
+  #line: 28
+}
+EODUMP;
+
+        $this->assertDumpMatchesFormat($expectedDump, $e, Caster::EXCLUDE_VERBOSE);
+    }
+
+    public function testAnonymous()
+    {
+        $e = new \Exception(sprintf('Boo "%s" ba.', \get_class(new class('Foo') extends \Exception {
+        })));
+
+        $expectedDump = <<<'EODUMP'
+Exception {
+  #message: "Boo "Exception@anonymous" ba."
+  #code: 0
+  #file: "%sExceptionCasterTest.php"
+  #line: %d
+}
+EODUMP;
+
+        $this->assertDumpMatchesFormat($expectedDump, $e, Caster::EXCLUDE_VERBOSE);
+    }
+}
diff --git a/vendor/symfony/var-dumper/Tests/Caster/GmpCasterTest.php b/vendor/symfony/var-dumper/Tests/Caster/GmpCasterTest.php
new file mode 100755
index 0000000..eb758e8
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Caster/GmpCasterTest.php
@@ -0,0 +1,48 @@
+<?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\VarDumper\Tests\Caster;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\VarDumper\Caster\GmpCaster;
+use Symfony\Component\VarDumper\Cloner\Stub;
+use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
+
+class GmpCasterTest extends TestCase
+{
+    use VarDumperTestTrait;
+
+    /**
+     * @requires extension gmp
+     */
+    public function testCastGmp()
+    {
+        $gmpString = gmp_init('1234');
+        $gmpOctal = gmp_init(010);
+        $gmp = gmp_init('01101');
+        $gmpDump = <<<EODUMP
+array:1 [
+  "\\x00~\\x00value" => %s
+]
+EODUMP;
+        $this->assertDumpEquals(sprintf($gmpDump, $gmpString), GmpCaster::castGmp($gmpString, [], new Stub(), false, 0));
+        $this->assertDumpEquals(sprintf($gmpDump, $gmpOctal), GmpCaster::castGmp($gmpOctal, [], new Stub(), false, 0));
+        $this->assertDumpEquals(sprintf($gmpDump, $gmp), GmpCaster::castGmp($gmp, [], new Stub(), false, 0));
+
+        $dump = <<<EODUMP
+GMP {
+  value: 577
+}
+EODUMP;
+
+        $this->assertDumpEquals($dump, $gmp);
+    }
+}
diff --git a/vendor/symfony/var-dumper/Tests/Caster/IntlCasterTest.php b/vendor/symfony/var-dumper/Tests/Caster/IntlCasterTest.php
new file mode 100755
index 0000000..0bff5bf
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Caster/IntlCasterTest.php
@@ -0,0 +1,297 @@
+<?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\VarDumper\Tests\Caster;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
+
+/**
+ * @requires extension intl
+ */
+class IntlCasterTest extends TestCase
+{
+    use VarDumperTestTrait;
+
+    public function testMessageFormatter()
+    {
+        $var = new \MessageFormatter('en', 'Hello {name}');
+
+        $expected = <<<EOTXT
+MessageFormatter {
+  locale: "en"
+  pattern: "Hello {name}"
+}
+EOTXT;
+        $this->assertDumpEquals($expected, $var);
+    }
+
+    public function testCastNumberFormatter()
+    {
+        $var = new \NumberFormatter('en', \NumberFormatter::DECIMAL);
+
+        $expectedLocale = $var->getLocale();
+        $expectedPattern = $var->getPattern();
+
+        $expectedAttribute1 = $var->getAttribute(\NumberFormatter::PARSE_INT_ONLY);
+        $expectedAttribute2 = $var->getAttribute(\NumberFormatter::GROUPING_USED);
+        $expectedAttribute3 = $var->getAttribute(\NumberFormatter::DECIMAL_ALWAYS_SHOWN);
+        $expectedAttribute4 = $var->getAttribute(\NumberFormatter::MAX_INTEGER_DIGITS);
+        $expectedAttribute5 = $var->getAttribute(\NumberFormatter::MIN_INTEGER_DIGITS);
+        $expectedAttribute6 = $var->getAttribute(\NumberFormatter::INTEGER_DIGITS);
+        $expectedAttribute7 = $var->getAttribute(\NumberFormatter::MAX_FRACTION_DIGITS);
+        $expectedAttribute8 = $var->getAttribute(\NumberFormatter::MIN_FRACTION_DIGITS);
+        $expectedAttribute9 = $var->getAttribute(\NumberFormatter::FRACTION_DIGITS);
+        $expectedAttribute10 = $var->getAttribute(\NumberFormatter::MULTIPLIER);
+        $expectedAttribute11 = $var->getAttribute(\NumberFormatter::GROUPING_SIZE);
+        $expectedAttribute12 = $var->getAttribute(\NumberFormatter::ROUNDING_MODE);
+        $expectedAttribute13 = number_format($var->getAttribute(\NumberFormatter::ROUNDING_INCREMENT), 1);
+        $expectedAttribute14 = $this->getDump($var->getAttribute(\NumberFormatter::FORMAT_WIDTH));
+        $expectedAttribute15 = $var->getAttribute(\NumberFormatter::PADDING_POSITION);
+        $expectedAttribute16 = $var->getAttribute(\NumberFormatter::SECONDARY_GROUPING_SIZE);
+        $expectedAttribute17 = $var->getAttribute(\NumberFormatter::SIGNIFICANT_DIGITS_USED);
+        $expectedAttribute18 = $this->getDump($var->getAttribute(\NumberFormatter::MIN_SIGNIFICANT_DIGITS));
+        $expectedAttribute19 = $this->getDump($var->getAttribute(\NumberFormatter::MAX_SIGNIFICANT_DIGITS));
+        $expectedAttribute20 = $var->getAttribute(\NumberFormatter::LENIENT_PARSE);
+
+        $expectedTextAttribute1 = $var->getTextAttribute(\NumberFormatter::POSITIVE_PREFIX);
+        $expectedTextAttribute2 = $var->getTextAttribute(\NumberFormatter::POSITIVE_SUFFIX);
+        $expectedTextAttribute3 = $var->getTextAttribute(\NumberFormatter::NEGATIVE_PREFIX);
+        $expectedTextAttribute4 = $var->getTextAttribute(\NumberFormatter::NEGATIVE_SUFFIX);
+        $expectedTextAttribute5 = $var->getTextAttribute(\NumberFormatter::PADDING_CHARACTER);
+        $expectedTextAttribute6 = $var->getTextAttribute(\NumberFormatter::CURRENCY_CODE);
+        $expectedTextAttribute7 = $var->getTextAttribute(\NumberFormatter::DEFAULT_RULESET) ? 'true' : 'false';
+        $expectedTextAttribute8 = $var->getTextAttribute(\NumberFormatter::PUBLIC_RULESETS) ? 'true' : 'false';
+
+        $expectedSymbol1 = $var->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);
+        $expectedSymbol2 = $var->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL);
+        $expectedSymbol3 = $var->getSymbol(\NumberFormatter::PATTERN_SEPARATOR_SYMBOL);
+        $expectedSymbol4 = $var->getSymbol(\NumberFormatter::PERCENT_SYMBOL);
+        $expectedSymbol5 = $var->getSymbol(\NumberFormatter::ZERO_DIGIT_SYMBOL);
+        $expectedSymbol6 = $var->getSymbol(\NumberFormatter::DIGIT_SYMBOL);
+        $expectedSymbol7 = $var->getSymbol(\NumberFormatter::MINUS_SIGN_SYMBOL);
+        $expectedSymbol8 = $var->getSymbol(\NumberFormatter::PLUS_SIGN_SYMBOL);
+        $expectedSymbol9 = $var->getSymbol(\NumberFormatter::CURRENCY_SYMBOL);
+        $expectedSymbol10 = $var->getSymbol(\NumberFormatter::INTL_CURRENCY_SYMBOL);
+        $expectedSymbol11 = $var->getSymbol(\NumberFormatter::MONETARY_SEPARATOR_SYMBOL);
+        $expectedSymbol12 = $var->getSymbol(\NumberFormatter::EXPONENTIAL_SYMBOL);
+        $expectedSymbol13 = $var->getSymbol(\NumberFormatter::PERMILL_SYMBOL);
+        $expectedSymbol14 = $var->getSymbol(\NumberFormatter::PAD_ESCAPE_SYMBOL);
+        $expectedSymbol15 = $var->getSymbol(\NumberFormatter::INFINITY_SYMBOL);
+        $expectedSymbol16 = $var->getSymbol(\NumberFormatter::NAN_SYMBOL);
+        $expectedSymbol17 = $var->getSymbol(\NumberFormatter::SIGNIFICANT_DIGIT_SYMBOL);
+        $expectedSymbol18 = $var->getSymbol(\NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL);
+
+        $expected = <<<EOTXT
+NumberFormatter {
+  locale: "$expectedLocale"
+  pattern: "$expectedPattern"
+  attributes: {
+    PARSE_INT_ONLY: $expectedAttribute1
+    GROUPING_USED: $expectedAttribute2
+    DECIMAL_ALWAYS_SHOWN: $expectedAttribute3
+    MAX_INTEGER_DIGITS: $expectedAttribute4
+    MIN_INTEGER_DIGITS: $expectedAttribute5
+    INTEGER_DIGITS: $expectedAttribute6
+    MAX_FRACTION_DIGITS: $expectedAttribute7
+    MIN_FRACTION_DIGITS: $expectedAttribute8
+    FRACTION_DIGITS: $expectedAttribute9
+    MULTIPLIER: $expectedAttribute10
+    GROUPING_SIZE: $expectedAttribute11
+    ROUNDING_MODE: $expectedAttribute12
+    ROUNDING_INCREMENT: $expectedAttribute13
+    FORMAT_WIDTH: $expectedAttribute14
+    PADDING_POSITION: $expectedAttribute15
+    SECONDARY_GROUPING_SIZE: $expectedAttribute16
+    SIGNIFICANT_DIGITS_USED: $expectedAttribute17
+    MIN_SIGNIFICANT_DIGITS: $expectedAttribute18
+    MAX_SIGNIFICANT_DIGITS: $expectedAttribute19
+    LENIENT_PARSE: $expectedAttribute20
+  }
+  text_attributes: {
+    POSITIVE_PREFIX: "$expectedTextAttribute1"
+    POSITIVE_SUFFIX: "$expectedTextAttribute2"
+    NEGATIVE_PREFIX: "$expectedTextAttribute3"
+    NEGATIVE_SUFFIX: "$expectedTextAttribute4"
+    PADDING_CHARACTER: "$expectedTextAttribute5"
+    CURRENCY_CODE: "$expectedTextAttribute6"
+    DEFAULT_RULESET: $expectedTextAttribute7
+    PUBLIC_RULESETS: $expectedTextAttribute8
+  }
+  symbols: {
+    DECIMAL_SEPARATOR_SYMBOL: "$expectedSymbol1"
+    GROUPING_SEPARATOR_SYMBOL: "$expectedSymbol2"
+    PATTERN_SEPARATOR_SYMBOL: "$expectedSymbol3"
+    PERCENT_SYMBOL: "$expectedSymbol4"
+    ZERO_DIGIT_SYMBOL: "$expectedSymbol5"
+    DIGIT_SYMBOL: "$expectedSymbol6"
+    MINUS_SIGN_SYMBOL: "$expectedSymbol7"
+    PLUS_SIGN_SYMBOL: "$expectedSymbol8"
+    CURRENCY_SYMBOL: "$expectedSymbol9"
+    INTL_CURRENCY_SYMBOL: "$expectedSymbol10"
+    MONETARY_SEPARATOR_SYMBOL: "$expectedSymbol11"
+    EXPONENTIAL_SYMBOL: "$expectedSymbol12"
+    PERMILL_SYMBOL: "$expectedSymbol13"
+    PAD_ESCAPE_SYMBOL: "$expectedSymbol14"
+    INFINITY_SYMBOL: "$expectedSymbol15"
+    NAN_SYMBOL: "$expectedSymbol16"
+    SIGNIFICANT_DIGIT_SYMBOL: "$expectedSymbol17"
+    MONETARY_GROUPING_SEPARATOR_SYMBOL: "$expectedSymbol18"
+  }
+}
+EOTXT;
+        $this->assertDumpEquals($expected, $var);
+    }
+
+    public function testCastIntlTimeZoneWithDST()
+    {
+        $var = \IntlTimeZone::createTimeZone('America/Los_Angeles');
+
+        $expectedDisplayName = $var->getDisplayName();
+        $expectedDSTSavings = $var->getDSTSavings();
+        $expectedID = $var->getID();
+        $expectedRawOffset = $var->getRawOffset();
+
+        $expected = <<<EOTXT
+IntlTimeZone {
+  display_name: "$expectedDisplayName"
+  id: "$expectedID"
+  raw_offset: $expectedRawOffset
+  dst_savings: $expectedDSTSavings
+}
+EOTXT;
+        $this->assertDumpEquals($expected, $var);
+    }
+
+    public function testCastIntlTimeZoneWithoutDST()
+    {
+        $var = \IntlTimeZone::createTimeZone('Asia/Bangkok');
+
+        $expectedDisplayName = $var->getDisplayName();
+        $expectedID = $var->getID();
+        $expectedRawOffset = $var->getRawOffset();
+
+        $expected = <<<EOTXT
+IntlTimeZone {
+  display_name: "$expectedDisplayName"
+  id: "$expectedID"
+  raw_offset: $expectedRawOffset
+}
+EOTXT;
+        $this->assertDumpEquals($expected, $var);
+    }
+
+    public function testCastIntlCalendar()
+    {
+        $var = \IntlCalendar::createInstance('America/Los_Angeles', 'en');
+
+        $expectedType = $var->getType();
+        $expectedFirstDayOfWeek = $var->getFirstDayOfWeek();
+        $expectedMinimalDaysInFirstWeek = $var->getMinimalDaysInFirstWeek();
+        $expectedRepeatedWallTimeOption = $var->getRepeatedWallTimeOption();
+        $expectedSkippedWallTimeOption = $var->getSkippedWallTimeOption();
+        $expectedTime = $var->getTime().'.0';
+        $expectedInDaylightTime = $var->inDaylightTime() ? 'true' : 'false';
+        $expectedIsLenient = $var->isLenient() ? 'true' : 'false';
+
+        $expectedTimeZone = $var->getTimeZone();
+        $expectedTimeZoneDisplayName = $expectedTimeZone->getDisplayName();
+        $expectedTimeZoneID = $expectedTimeZone->getID();
+        $expectedTimeZoneRawOffset = $expectedTimeZone->getRawOffset();
+        $expectedTimeZoneDSTSavings = $expectedTimeZone->getDSTSavings();
+
+        $expected = <<<EOTXT
+IntlGregorianCalendar {
+  type: "$expectedType"
+  first_day_of_week: $expectedFirstDayOfWeek
+  minimal_days_in_first_week: $expectedMinimalDaysInFirstWeek
+  repeated_wall_time_option: $expectedRepeatedWallTimeOption
+  skipped_wall_time_option: $expectedSkippedWallTimeOption
+  time: $expectedTime
+  in_daylight_time: $expectedInDaylightTime
+  is_lenient: $expectedIsLenient
+  time_zone: IntlTimeZone {
+    display_name: "$expectedTimeZoneDisplayName"
+    id: "$expectedTimeZoneID"
+    raw_offset: $expectedTimeZoneRawOffset
+    dst_savings: $expectedTimeZoneDSTSavings
+  }
+}
+EOTXT;
+        $this->assertDumpEquals($expected, $var);
+    }
+
+    public function testCastDateFormatter()
+    {
+        $var = new \IntlDateFormatter('en', \IntlDateFormatter::TRADITIONAL, \IntlDateFormatter::TRADITIONAL);
+
+        $expectedLocale = $var->getLocale();
+        $expectedPattern = $var->getPattern();
+        $expectedCalendar = $var->getCalendar();
+        $expectedTimeZoneId = $var->getTimeZoneId();
+        $expectedTimeType = $var->getTimeType();
+        $expectedDateType = $var->getDateType();
+
+        $expectedTimeZone = $var->getTimeZone();
+        $expectedTimeZoneDisplayName = $expectedTimeZone->getDisplayName();
+        $expectedTimeZoneID = $expectedTimeZone->getID();
+        $expectedTimeZoneRawOffset = $expectedTimeZone->getRawOffset();
+        $expectedTimeZoneDSTSavings = $expectedTimeZone->useDaylightTime() ? "\n    dst_savings: ".$expectedTimeZone->getDSTSavings() : '';
+
+        $expectedCalendarObject = $var->getCalendarObject();
+        $expectedCalendarObjectType = $expectedCalendarObject->getType();
+        $expectedCalendarObjectFirstDayOfWeek = $expectedCalendarObject->getFirstDayOfWeek();
+        $expectedCalendarObjectMinimalDaysInFirstWeek = $expectedCalendarObject->getMinimalDaysInFirstWeek();
+        $expectedCalendarObjectRepeatedWallTimeOption = $expectedCalendarObject->getRepeatedWallTimeOption();
+        $expectedCalendarObjectSkippedWallTimeOption = $expectedCalendarObject->getSkippedWallTimeOption();
+        $expectedCalendarObjectTime = $expectedCalendarObject->getTime().'.0';
+        $expectedCalendarObjectInDaylightTime = $expectedCalendarObject->inDaylightTime() ? 'true' : 'false';
+        $expectedCalendarObjectIsLenient = $expectedCalendarObject->isLenient() ? 'true' : 'false';
+
+        $expectedCalendarObjectTimeZone = $expectedCalendarObject->getTimeZone();
+        $expectedCalendarObjectTimeZoneDisplayName = $expectedCalendarObjectTimeZone->getDisplayName();
+        $expectedCalendarObjectTimeZoneID = $expectedCalendarObjectTimeZone->getID();
+        $expectedCalendarObjectTimeZoneRawOffset = $expectedCalendarObjectTimeZone->getRawOffset();
+        $expectedCalendarObjectTimeZoneDSTSavings = $expectedTimeZone->useDaylightTime() ? "\n      dst_savings: ".$expectedCalendarObjectTimeZone->getDSTSavings() : '';
+
+        $expected = <<<EOTXT
+IntlDateFormatter {
+  locale: "$expectedLocale"
+  pattern: "$expectedPattern"
+  calendar: $expectedCalendar
+  time_zone_id: "$expectedTimeZoneId"
+  time_type: $expectedTimeType
+  date_type: $expectedDateType
+  calendar_object: IntlGregorianCalendar {
+    type: "$expectedCalendarObjectType"
+    first_day_of_week: $expectedCalendarObjectFirstDayOfWeek
+    minimal_days_in_first_week: $expectedCalendarObjectMinimalDaysInFirstWeek
+    repeated_wall_time_option: $expectedCalendarObjectRepeatedWallTimeOption
+    skipped_wall_time_option: $expectedCalendarObjectSkippedWallTimeOption
+    time: $expectedCalendarObjectTime
+    in_daylight_time: $expectedCalendarObjectInDaylightTime
+    is_lenient: $expectedCalendarObjectIsLenient
+    time_zone: IntlTimeZone {
+      display_name: "$expectedCalendarObjectTimeZoneDisplayName"
+      id: "$expectedCalendarObjectTimeZoneID"
+      raw_offset: $expectedCalendarObjectTimeZoneRawOffset$expectedCalendarObjectTimeZoneDSTSavings
+    }
+  }
+  time_zone: IntlTimeZone {
+    display_name: "$expectedTimeZoneDisplayName"
+    id: "$expectedTimeZoneID"
+    raw_offset: $expectedTimeZoneRawOffset$expectedTimeZoneDSTSavings
+  }
+}
+EOTXT;
+        $this->assertDumpEquals($expected, $var);
+    }
+}
diff --git a/vendor/symfony/var-dumper/Tests/Caster/MemcachedCasterTest.php b/vendor/symfony/var-dumper/Tests/Caster/MemcachedCasterTest.php
new file mode 100755
index 0000000..df48390
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Caster/MemcachedCasterTest.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\VarDumper\Tests\Caster;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
+
+/**
+ * @author Jan Schädlich <jan.schaedlich@sensiolabs.de>
+ */
+class MemcachedCasterTest extends TestCase
+{
+    use VarDumperTestTrait;
+
+    public function testCastMemcachedWithDefaultOptions()
+    {
+        if (!class_exists('Memcached')) {
+            $this->markTestSkipped('Memcached not available');
+        }
+
+        $var = new \Memcached();
+        $var->addServer('127.0.0.1', 11211);
+        $var->addServer('127.0.0.2', 11212);
+
+        $expected = <<<EOTXT
+Memcached {
+  servers: array:2 [
+    0 => array:3 [
+      "host" => "127.0.0.1"
+      "port" => 11211
+      "type" => "TCP"
+    ]
+    1 => array:3 [
+      "host" => "127.0.0.2"
+      "port" => 11212
+      "type" => "TCP"
+    ]
+  ]
+  options: {}
+}
+EOTXT;
+        $this->assertDumpEquals($expected, $var);
+    }
+
+    public function testCastMemcachedWithCustomOptions()
+    {
+        if (!class_exists('Memcached')) {
+            $this->markTestSkipped('Memcached not available');
+        }
+
+        $var = new \Memcached();
+        $var->addServer('127.0.0.1', 11211);
+        $var->addServer('127.0.0.2', 11212);
+
+        // set a subset of non default options to test boolean, string and integer output
+        $var->setOption(\Memcached::OPT_COMPRESSION, false);
+        $var->setOption(\Memcached::OPT_PREFIX_KEY, 'pre');
+        $var->setOption(\Memcached::OPT_DISTRIBUTION, \Memcached::DISTRIBUTION_CONSISTENT);
+
+        $expected = <<<'EOTXT'
+Memcached {
+  servers: array:2 [
+    0 => array:3 [
+      "host" => "127.0.0.1"
+      "port" => 11211
+      "type" => "TCP"
+    ]
+    1 => array:3 [
+      "host" => "127.0.0.2"
+      "port" => 11212
+      "type" => "TCP"
+    ]
+  ]
+  options: {
+    OPT_COMPRESSION: false
+    OPT_PREFIX_KEY: "pre"
+    OPT_DISTRIBUTION: 1
+  }
+}
+EOTXT;
+
+        $this->assertDumpEquals($expected, $var);
+    }
+}
diff --git a/vendor/symfony/var-dumper/Tests/Caster/PdoCasterTest.php b/vendor/symfony/var-dumper/Tests/Caster/PdoCasterTest.php
new file mode 100755
index 0000000..19bbe0f
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Caster/PdoCasterTest.php
@@ -0,0 +1,64 @@
+<?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\VarDumper\Tests\Caster;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\VarDumper\Caster\PdoCaster;
+use Symfony\Component\VarDumper\Cloner\Stub;
+use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class PdoCasterTest extends TestCase
+{
+    use VarDumperTestTrait;
+
+    /**
+     * @requires extension pdo_sqlite
+     */
+    public function testCastPdo()
+    {
+        $pdo = new \PDO('sqlite::memory:');
+        $pdo->setAttribute(\PDO::ATTR_STATEMENT_CLASS, ['PDOStatement', [$pdo]]);
+
+        $cast = PdoCaster::castPdo($pdo, [], new Stub(), false);
+
+        $this->assertInstanceOf('Symfony\Component\VarDumper\Caster\EnumStub', $cast["\0~\0attributes"]);
+
+        $attr = $cast["\0~\0attributes"] = $cast["\0~\0attributes"]->value;
+        $this->assertInstanceOf('Symfony\Component\VarDumper\Caster\ConstStub', $attr['CASE']);
+        $this->assertSame('NATURAL', $attr['CASE']->class);
+        $this->assertSame('BOTH', $attr['DEFAULT_FETCH_MODE']->class);
+
+        $xDump = <<<'EODUMP'
+array:2 [
+  "\x00~\x00inTransaction" => false
+  "\x00~\x00attributes" => array:9 [
+    "CASE" => NATURAL
+    "ERRMODE" => SILENT
+    "PERSISTENT" => false
+    "DRIVER_NAME" => "sqlite"
+    "ORACLE_NULLS" => NATURAL
+    "CLIENT_VERSION" => "%s"
+    "SERVER_VERSION" => "%s"
+    "STATEMENT_CLASS" => array:%d [
+      0 => "PDOStatement"%A
+    ]
+    "DEFAULT_FETCH_MODE" => BOTH
+  ]
+]
+EODUMP;
+
+        $this->assertDumpMatchesFormat($xDump, $cast);
+    }
+}
diff --git a/vendor/symfony/var-dumper/Tests/Caster/RedisCasterTest.php b/vendor/symfony/var-dumper/Tests/Caster/RedisCasterTest.php
new file mode 100755
index 0000000..3edbed6
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Caster/RedisCasterTest.php
@@ -0,0 +1,70 @@
+<?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\VarDumper\Tests\Caster;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ * @requires extension redis
+ */
+class RedisCasterTest extends TestCase
+{
+    use VarDumperTestTrait;
+
+    public function testNotConnected()
+    {
+        $redis = new \Redis();
+
+        $xCast = <<<'EODUMP'
+Redis {
+  isConnected: false
+}
+EODUMP;
+
+        $this->assertDumpMatchesFormat($xCast, $redis);
+    }
+
+    public function testConnected()
+    {
+        $redis = new \Redis();
+        if (!@$redis->connect('127.0.0.1')) {
+            $e = error_get_last();
+            self::markTestSkipped($e['message']);
+        }
+
+        $xCast = <<<'EODUMP'
+Redis {%A
+  isConnected: true
+  host: "127.0.0.1"
+  port: 6379
+  auth: null
+  mode: ATOMIC
+  dbNum: 0
+  timeout: 0.0
+  lastError: null
+  persistentId: null
+  options: {
+    TCP_KEEPALIVE: 0
+    READ_TIMEOUT: 0.0
+    COMPRESSION: NONE
+    SERIALIZER: NONE
+    PREFIX: null
+    SCAN: NORETRY
+  }
+}
+EODUMP;
+
+        $this->assertDumpMatchesFormat($xCast, $redis);
+    }
+}
diff --git a/vendor/symfony/var-dumper/Tests/Caster/ReflectionCasterTest.php b/vendor/symfony/var-dumper/Tests/Caster/ReflectionCasterTest.php
new file mode 100755
index 0000000..7e37dfe
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Caster/ReflectionCasterTest.php
@@ -0,0 +1,251 @@
+<?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\VarDumper\Tests\Caster;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\VarDumper\Caster\Caster;
+use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
+use Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo;
+use Symfony\Component\VarDumper\Tests\Fixtures\NotLoadableClass;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class ReflectionCasterTest extends TestCase
+{
+    use VarDumperTestTrait;
+
+    public function testReflectionCaster()
+    {
+        $var = new \ReflectionClass('ReflectionClass');
+
+        $this->assertDumpMatchesFormat(
+            <<<'EOTXT'
+ReflectionClass {
+  +name: "ReflectionClass"
+%Aimplements: array:%d [
+    0 => "Reflector"
+%A]
+  constants: array:3 [
+    "IS_IMPLICIT_ABSTRACT" => 16
+    "IS_EXPLICIT_ABSTRACT" => %d
+    "IS_FINAL" => %d
+  ]
+  properties: array:%d [
+    "name" => ReflectionProperty {
+%A    +name: "name"
+      +class: "ReflectionClass"
+%A    modifiers: "public"
+    }
+%A]
+  methods: array:%d [
+%A
+    "export" => ReflectionMethod {
+      +name: "export"
+      +class: "ReflectionClass"
+%A    parameters: {
+        $%s: ReflectionParameter {
+%A         position: 0
+%A
+}
+EOTXT
+            , $var
+        );
+    }
+
+    public function testClosureCaster()
+    {
+        $a = $b = 123;
+        $var = function ($x) use ($a, &$b) {};
+
+        $this->assertDumpMatchesFormat(
+            <<<'EOTXT'
+Closure($x) {
+%Ause: {
+    $a: 123
+    $b: & 123
+  }
+  file: "%sReflectionCasterTest.php"
+  line: "68 to 68"
+}
+EOTXT
+            , $var
+        );
+    }
+
+    public function testFromCallableClosureCaster()
+    {
+        if (\defined('HHVM_VERSION_ID')) {
+            $this->markTestSkipped('Not for HHVM.');
+        }
+        $var = [
+            (new \ReflectionMethod($this, __FUNCTION__))->getClosure($this),
+            (new \ReflectionMethod(__CLASS__, 'tearDownAfterClass'))->getClosure(),
+        ];
+
+        $this->assertDumpMatchesFormat(
+            <<<EOTXT
+array:2 [
+  0 => Symfony\Component\VarDumper\Tests\Caster\ReflectionCasterTest::testFromCallableClosureCaster() {
+    this: Symfony\Component\VarDumper\Tests\Caster\ReflectionCasterTest { …}
+    file: "%sReflectionCasterTest.php"
+    line: "%d to %d"
+  }
+  1 => %sTestCase::tearDownAfterClass() {
+    file: "%sTestCase.php"
+    line: "%d to %d"
+  }
+]
+EOTXT
+            , $var
+        );
+    }
+
+    public function testClosureCasterExcludingVerbosity()
+    {
+        $var = function &($a = 5) {};
+
+        $this->assertDumpEquals('Closure&($a = 5) { …5}', $var, Caster::EXCLUDE_VERBOSE);
+    }
+
+    public function testReflectionParameter()
+    {
+        $var = new \ReflectionParameter(__NAMESPACE__.'\reflectionParameterFixture', 0);
+
+        $this->assertDumpMatchesFormat(
+            <<<'EOTXT'
+ReflectionParameter {
+  +name: "arg1"
+  position: 0
+  typeHint: "Symfony\Component\VarDumper\Tests\Fixtures\NotLoadableClass"
+  default: null
+}
+EOTXT
+            , $var
+        );
+    }
+
+    public function testReflectionParameterScalar()
+    {
+        $f = eval('return function (int $a) {};');
+        $var = new \ReflectionParameter($f, 0);
+
+        $this->assertDumpMatchesFormat(
+            <<<'EOTXT'
+ReflectionParameter {
+  +name: "a"
+  position: 0
+  typeHint: "int"
+}
+EOTXT
+            , $var
+        );
+    }
+
+    public function testReturnType()
+    {
+        $f = eval('return function ():int {};');
+        $line = __LINE__ - 1;
+
+        $this->assertDumpMatchesFormat(
+            <<<EOTXT
+Closure(): int {
+  returnType: "int"
+  class: "Symfony\Component\VarDumper\Tests\Caster\ReflectionCasterTest"
+  this: Symfony\Component\VarDumper\Tests\Caster\ReflectionCasterTest { …}
+  file: "%sReflectionCasterTest.php($line) : eval()'d code"
+  line: "1 to 1"
+}
+EOTXT
+            , $f
+        );
+    }
+
+    public function testGenerator()
+    {
+        if (\extension_loaded('xdebug')) {
+            $this->markTestSkipped('xdebug is active');
+        }
+
+        $generator = new GeneratorDemo();
+        $generator = $generator->baz();
+
+        $expectedDump = <<<'EODUMP'
+Generator {
+  this: Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo { …}
+  executing: {
+    Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo->baz() {
+      %sGeneratorDemo.php:14 {
+        › {
+        ›     yield from bar();
+        › }
+      }
+    }
+  }
+  closed: false
+}
+EODUMP;
+
+        $this->assertDumpMatchesFormat($expectedDump, $generator);
+
+        foreach ($generator as $v) {
+            break;
+        }
+
+        $expectedDump = <<<'EODUMP'
+array:2 [
+  0 => ReflectionGenerator {
+    this: Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo { …}
+    trace: {
+      %s%eTests%eFixtures%eGeneratorDemo.php:9 {
+        › {
+        ›     yield 1;
+        › }
+      }
+      %s%eTests%eFixtures%eGeneratorDemo.php:20 { …}
+      %s%eTests%eFixtures%eGeneratorDemo.php:14 { …}
+    }
+    closed: false
+  }
+  1 => Generator {
+    executing: {
+      Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::foo() {
+        %sGeneratorDemo.php:10 {
+          ›     yield 1;
+          › }
+          › 
+        }
+      }
+    }
+    closed: false
+  }
+]
+EODUMP;
+
+        $r = new \ReflectionGenerator($generator);
+        $this->assertDumpMatchesFormat($expectedDump, [$r, $r->getExecutingGenerator()]);
+
+        foreach ($generator as $v) {
+        }
+
+        $expectedDump = <<<'EODUMP'
+Generator {
+  closed: true
+}
+EODUMP;
+        $this->assertDumpMatchesFormat($expectedDump, $generator);
+    }
+}
+
+function reflectionParameterFixture(NotLoadableClass $arg1 = null, $arg2)
+{
+}
diff --git a/vendor/symfony/var-dumper/Tests/Caster/SplCasterTest.php b/vendor/symfony/var-dumper/Tests/Caster/SplCasterTest.php
new file mode 100755
index 0000000..c7d7981
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Caster/SplCasterTest.php
@@ -0,0 +1,207 @@
+<?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\VarDumper\Tests\Caster;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
+
+/**
+ * @author Grégoire Pineau <lyrixx@lyrixx.info>
+ */
+class SplCasterTest extends TestCase
+{
+    use VarDumperTestTrait;
+
+    public function getCastFileInfoTests()
+    {
+        return [
+            [__FILE__, <<<'EOTXT'
+SplFileInfo {
+%Apath: "%sCaster"
+  filename: "SplCasterTest.php"
+  basename: "SplCasterTest.php"
+  pathname: "%sSplCasterTest.php"
+  extension: "php"
+  realPath: "%sSplCasterTest.php"
+  aTime: %s-%s-%d %d:%d:%d
+  mTime: %s-%s-%d %d:%d:%d
+  cTime: %s-%s-%d %d:%d:%d
+  inode: %i
+  size: %d
+  perms: 0%d
+  owner: %d
+  group: %d
+  type: "file"
+  writable: true
+  readable: true
+  executable: false
+  file: true
+  dir: false
+  link: false
+%A}
+EOTXT
+            ],
+            ['https://google.com/about', <<<'EOTXT'
+SplFileInfo {
+%Apath: "https://google.com"
+  filename: "about"
+  basename: "about"
+  pathname: "https://google.com/about"
+  extension: ""
+  realPath: false
+%A}
+EOTXT
+            ],
+        ];
+    }
+
+    /** @dataProvider getCastFileInfoTests */
+    public function testCastFileInfo($file, $dump)
+    {
+        $this->assertDumpMatchesFormat($dump, new \SplFileInfo($file));
+    }
+
+    public function testCastFileObject()
+    {
+        $var = new \SplFileObject(__FILE__);
+        $var->setFlags(\SplFileObject::DROP_NEW_LINE | \SplFileObject::SKIP_EMPTY);
+        $dump = <<<'EOTXT'
+SplFileObject {
+%Apath: "%sCaster"
+  filename: "SplCasterTest.php"
+  basename: "SplCasterTest.php"
+  pathname: "%sSplCasterTest.php"
+  extension: "php"
+  realPath: "%sSplCasterTest.php"
+  aTime: %s-%s-%d %d:%d:%d
+  mTime: %s-%s-%d %d:%d:%d
+  cTime: %s-%s-%d %d:%d:%d
+  inode: %i
+  size: %d
+  perms: 0%d
+  owner: %d
+  group: %d
+  type: "file"
+  writable: true
+  readable: true
+  executable: false
+  file: true
+  dir: false
+  link: false
+%AcsvControl: array:%d [
+    0 => ","
+    1 => """
+%A]
+  flags: DROP_NEW_LINE|SKIP_EMPTY
+  maxLineLen: 0
+  fstat: array:26 [
+    "dev" => %d
+    "ino" => %i
+    "nlink" => %d
+    "rdev" => 0
+    "blksize" => %i
+    "blocks" => %i
+     …20
+  ]
+  eof: false
+  key: 0
+}
+EOTXT;
+        $this->assertDumpMatchesFormat($dump, $var);
+    }
+
+    /**
+     * @dataProvider provideCastSplDoublyLinkedList
+     */
+    public function testCastSplDoublyLinkedList($modeValue, $modeDump)
+    {
+        $var = new \SplDoublyLinkedList();
+        $var->setIteratorMode($modeValue);
+        $dump = <<<EOTXT
+SplDoublyLinkedList {
+%Amode: $modeDump
+  dllist: []
+}
+EOTXT;
+        $this->assertDumpMatchesFormat($dump, $var);
+    }
+
+    public function provideCastSplDoublyLinkedList()
+    {
+        return [
+            [\SplDoublyLinkedList::IT_MODE_FIFO, 'IT_MODE_FIFO | IT_MODE_KEEP'],
+            [\SplDoublyLinkedList::IT_MODE_LIFO, 'IT_MODE_LIFO | IT_MODE_KEEP'],
+            [\SplDoublyLinkedList::IT_MODE_FIFO | \SplDoublyLinkedList::IT_MODE_DELETE, 'IT_MODE_FIFO | IT_MODE_DELETE'],
+            [\SplDoublyLinkedList::IT_MODE_LIFO | \SplDoublyLinkedList::IT_MODE_DELETE, 'IT_MODE_LIFO | IT_MODE_DELETE'],
+        ];
+    }
+
+    public function testCastObjectStorageIsntModified()
+    {
+        $var = new \SplObjectStorage();
+        $var->attach(new \stdClass());
+        $var->rewind();
+        $current = $var->current();
+
+        $this->assertDumpMatchesFormat('%A', $var);
+        $this->assertSame($current, $var->current());
+    }
+
+    public function testCastObjectStorageDumpsInfo()
+    {
+        $var = new \SplObjectStorage();
+        $var->attach(new \stdClass(), new \DateTime());
+
+        $this->assertDumpMatchesFormat('%ADateTime%A', $var);
+    }
+
+    public function testCastArrayObject()
+    {
+        $var = new \ArrayObject([123]);
+        $var->foo = 234;
+
+        $expected = <<<EOTXT
+ArrayObject {
+  +"foo": 234
+  flag::STD_PROP_LIST: false
+  flag::ARRAY_AS_PROPS: false
+  iteratorClass: "ArrayIterator"
+  storage: array:1 [
+    0 => 123
+  ]
+}
+EOTXT;
+        $this->assertDumpEquals($expected, $var);
+    }
+
+    public function testArrayIterator()
+    {
+        $var = new MyArrayIterator([234]);
+
+        $expected = <<<EOTXT
+Symfony\Component\VarDumper\Tests\Caster\MyArrayIterator {
+  -foo: 123
+  flag::STD_PROP_LIST: false
+  flag::ARRAY_AS_PROPS: false
+  storage: array:1 [
+    0 => 234
+  ]
+}
+EOTXT;
+        $this->assertDumpEquals($expected, $var);
+    }
+}
+
+class MyArrayIterator extends \ArrayIterator
+{
+    private $foo = 123;
+}
diff --git a/vendor/symfony/var-dumper/Tests/Caster/StubCasterTest.php b/vendor/symfony/var-dumper/Tests/Caster/StubCasterTest.php
new file mode 100755
index 0000000..8056f70
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Caster/StubCasterTest.php
@@ -0,0 +1,213 @@
+<?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\VarDumper\Tests\Caster;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\VarDumper\Caster\ArgsStub;
+use Symfony\Component\VarDumper\Caster\ClassStub;
+use Symfony\Component\VarDumper\Caster\LinkStub;
+use Symfony\Component\VarDumper\Cloner\VarCloner;
+use Symfony\Component\VarDumper\Dumper\HtmlDumper;
+use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
+use Symfony\Component\VarDumper\Tests\Fixtures\FooInterface;
+
+class StubCasterTest extends TestCase
+{
+    use VarDumperTestTrait;
+
+    public function testArgsStubWithDefaults($foo = 234, $bar = 456)
+    {
+        $args = [new ArgsStub([123], __FUNCTION__, __CLASS__)];
+
+        $expectedDump = <<<'EODUMP'
+array:1 [
+  0 => {
+    $foo: 123
+  }
+]
+EODUMP;
+
+        $this->assertDumpMatchesFormat($expectedDump, $args);
+    }
+
+    public function testArgsStubWithExtraArgs($foo = 234)
+    {
+        $args = [new ArgsStub([123, 456], __FUNCTION__, __CLASS__)];
+
+        $expectedDump = <<<'EODUMP'
+array:1 [
+  0 => {
+    $foo: 123
+    ...: {
+      456
+    }
+  }
+]
+EODUMP;
+
+        $this->assertDumpMatchesFormat($expectedDump, $args);
+    }
+
+    public function testArgsStubNoParamWithExtraArgs()
+    {
+        $args = [new ArgsStub([123], __FUNCTION__, __CLASS__)];
+
+        $expectedDump = <<<'EODUMP'
+array:1 [
+  0 => {
+    123
+  }
+]
+EODUMP;
+
+        $this->assertDumpMatchesFormat($expectedDump, $args);
+    }
+
+    public function testArgsStubWithClosure()
+    {
+        $args = [new ArgsStub([123], '{closure}', null)];
+
+        $expectedDump = <<<'EODUMP'
+array:1 [
+  0 => {
+    123
+  }
+]
+EODUMP;
+
+        $this->assertDumpMatchesFormat($expectedDump, $args);
+    }
+
+    public function testLinkStub()
+    {
+        $var = [new LinkStub(__CLASS__, 0, __FILE__)];
+
+        $cloner = new VarCloner();
+        $dumper = new HtmlDumper();
+        $dumper->setDumpHeader('<foo></foo>');
+        $dumper->setDumpBoundaries('<bar>', '</bar>');
+        $dumper->setDisplayOptions(['fileLinkFormat' => '%f:%l']);
+        $dump = $dumper->dump($cloner->cloneVar($var), true);
+
+        $expectedDump = <<<'EODUMP'
+<foo></foo><bar><span class=sf-dump-note>array:1</span> [<samp>
+  <span class=sf-dump-index>0</span> => "<a href="%sStubCasterTest.php:0" rel="noopener noreferrer"><span class=sf-dump-str title="55 characters">Symfony\Component\VarDumper\Tests\Caster\StubCasterTest</span></a>"
+</samp>]
+</bar>
+EODUMP;
+
+        $this->assertStringMatchesFormat($expectedDump, $dump);
+    }
+
+    public function testLinkStubWithNoFileLink()
+    {
+        $var = [new LinkStub('example.com', 0, 'http://example.com')];
+
+        $cloner = new VarCloner();
+        $dumper = new HtmlDumper();
+        $dumper->setDumpHeader('<foo></foo>');
+        $dumper->setDumpBoundaries('<bar>', '</bar>');
+        $dumper->setDisplayOptions(['fileLinkFormat' => '%f:%l']);
+        $dump = $dumper->dump($cloner->cloneVar($var), true);
+
+        $expectedDump = <<<'EODUMP'
+<foo></foo><bar><span class=sf-dump-note>array:1</span> [<samp>
+  <span class=sf-dump-index>0</span> => "<a href="http://example.com" target="_blank" rel="noopener noreferrer"><span class=sf-dump-str title="11 characters">example.com</span></a>"
+</samp>]
+</bar>
+EODUMP;
+
+        $this->assertStringMatchesFormat($expectedDump, $dump);
+    }
+
+    public function testClassStub()
+    {
+        $var = [new ClassStub('hello', [FooInterface::class, 'foo'])];
+
+        $cloner = new VarCloner();
+        $dumper = new HtmlDumper();
+        $dumper->setDumpHeader('<foo></foo>');
+        $dumper->setDumpBoundaries('<bar>', '</bar>');
+        $dump = $dumper->dump($cloner->cloneVar($var), true, ['fileLinkFormat' => '%f:%l']);
+
+        $expectedDump = <<<'EODUMP'
+<foo></foo><bar><span class=sf-dump-note>array:1</span> [<samp>
+  <span class=sf-dump-index>0</span> => "<a href="%sFooInterface.php:10" rel="noopener noreferrer"><span class=sf-dump-str title="39 characters">hello(?stdClass $a, stdClass $b = null)</span></a>"
+</samp>]
+</bar>
+EODUMP;
+
+        $this->assertStringMatchesFormat($expectedDump, $dump);
+    }
+
+    public function testClassStubWithNotExistingClass()
+    {
+        $var = [new ClassStub(NotExisting::class)];
+
+        $cloner = new VarCloner();
+        $dumper = new HtmlDumper();
+        $dumper->setDumpHeader('<foo></foo>');
+        $dumper->setDumpBoundaries('<bar>', '</bar>');
+        $dump = $dumper->dump($cloner->cloneVar($var), true);
+
+        $expectedDump = <<<'EODUMP'
+<foo></foo><bar><span class=sf-dump-note>array:1</span> [<samp>
+  <span class=sf-dump-index>0</span> => "<span class=sf-dump-str title="Symfony\Component\VarDumper\Tests\Caster\NotExisting
+52 characters"><span class="sf-dump-ellipsis sf-dump-ellipsis-class">Symfony\Component\VarDumper\Tests\Caster</span><span class=sf-dump-ellipsis>\</span>NotExisting</span>"
+</samp>]
+</bar>
+EODUMP;
+
+        $this->assertStringMatchesFormat($expectedDump, $dump);
+    }
+
+    public function testClassStubWithNotExistingMethod()
+    {
+        $var = [new ClassStub('hello', [FooInterface::class, 'missing'])];
+
+        $cloner = new VarCloner();
+        $dumper = new HtmlDumper();
+        $dumper->setDumpHeader('<foo></foo>');
+        $dumper->setDumpBoundaries('<bar>', '</bar>');
+        $dump = $dumper->dump($cloner->cloneVar($var), true, ['fileLinkFormat' => '%f:%l']);
+
+        $expectedDump = <<<'EODUMP'
+<foo></foo><bar><span class=sf-dump-note>array:1</span> [<samp>
+  <span class=sf-dump-index>0</span> => "<a href="%sFooInterface.php:5" rel="noopener noreferrer"><span class=sf-dump-str title="5 characters">hello</span></a>"
+</samp>]
+</bar>
+EODUMP;
+
+        $this->assertStringMatchesFormat($expectedDump, $dump);
+    }
+
+    public function testClassStubWithAnonymousClass()
+    {
+        $var = [new ClassStub(\get_class(new class() extends \Exception {
+        }))];
+
+        $cloner = new VarCloner();
+        $dumper = new HtmlDumper();
+        $dumper->setDumpHeader('<foo></foo>');
+        $dumper->setDumpBoundaries('<bar>', '</bar>');
+        $dump = $dumper->dump($cloner->cloneVar($var), true, ['fileLinkFormat' => '%f:%l']);
+
+        $expectedDump = <<<'EODUMP'
+<foo></foo><bar><span class=sf-dump-note>array:1</span> [<samp>
+  <span class=sf-dump-index>0</span> => "<a href="%sStubCasterTest.php:195" rel="noopener noreferrer"><span class=sf-dump-str title="19 characters">Exception@anonymous</span></a>"
+</samp>]
+</bar>
+EODUMP;
+
+        $this->assertStringMatchesFormat($expectedDump, $dump);
+    }
+}
diff --git a/vendor/symfony/var-dumper/Tests/Caster/XmlReaderCasterTest.php b/vendor/symfony/var-dumper/Tests/Caster/XmlReaderCasterTest.php
new file mode 100755
index 0000000..1d7b3f6
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Caster/XmlReaderCasterTest.php
@@ -0,0 +1,248 @@
+<?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\VarDumper\Tests\Caster;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
+
+/**
+ * @author Baptiste Clavié <clavie.b@gmail.com>
+ */
+class XmlReaderCasterTest extends TestCase
+{
+    use VarDumperTestTrait;
+
+    /** @var \XmlReader */
+    private $reader;
+
+    protected function setUp()
+    {
+        $this->reader = new \XmlReader();
+        $this->reader->open(__DIR__.'/../Fixtures/xml_reader.xml');
+    }
+
+    protected function tearDown()
+    {
+        $this->reader->close();
+    }
+
+    public function testParserProperty()
+    {
+        $this->reader->setParserProperty(\XMLReader::SUBST_ENTITIES, true);
+
+        $expectedDump = <<<'EODUMP'
+XMLReader {
+  +nodeType: NONE
+  parserProperties: {
+    SUBST_ENTITIES: true
+     …3
+  }
+   …12
+}
+EODUMP;
+
+        $this->assertDumpMatchesFormat($expectedDump, $this->reader);
+    }
+
+    /**
+     * @dataProvider provideNodes
+     */
+    public function testNodes($seek, $expectedDump)
+    {
+        while ($seek--) {
+            $this->reader->read();
+        }
+        $this->assertDumpMatchesFormat($expectedDump, $this->reader);
+    }
+
+    public function provideNodes()
+    {
+        return [
+            [0, <<<'EODUMP'
+XMLReader {
+  +nodeType: NONE
+   …13
+}
+EODUMP
+            ],
+            [1, <<<'EODUMP'
+XMLReader {
+  +localName: "foo"
+  +nodeType: ELEMENT
+  +baseURI: "%sxml_reader.xml"
+   …11
+}
+EODUMP
+            ],
+            [2, <<<'EODUMP'
+XMLReader {
+  +localName: "#text"
+  +nodeType: SIGNIFICANT_WHITESPACE
+  +depth: 1
+  +value: """
+    \n
+        
+    """
+  +baseURI: "%sxml_reader.xml"
+   …9
+}
+EODUMP
+            ],
+            [3, <<<'EODUMP'
+XMLReader {
+  +localName: "bar"
+  +nodeType: ELEMENT
+  +depth: 1
+  +baseURI: "%sxml_reader.xml"
+   …10
+}
+EODUMP
+            ],
+            [4, <<<'EODUMP'
+XMLReader {
+  +localName: "bar"
+  +nodeType: END_ELEMENT
+  +depth: 1
+  +baseURI: "%sxml_reader.xml"
+   …10
+}
+EODUMP
+            ],
+            [6, <<<'EODUMP'
+XMLReader {
+  +localName: "bar"
+  +nodeType: ELEMENT
+  +depth: 1
+  +isEmptyElement: true
+  +baseURI: "%sxml_reader.xml"
+   …9
+}
+EODUMP
+            ],
+            [9, <<<'EODUMP'
+XMLReader {
+  +localName: "#text"
+  +nodeType: TEXT
+  +depth: 2
+  +value: "With text"
+  +baseURI: "%sxml_reader.xml"
+   …9
+}
+EODUMP
+            ],
+            [12, <<<'EODUMP'
+XMLReader {
+  +localName: "bar"
+  +nodeType: ELEMENT
+  +depth: 1
+  +attributeCount: 2
+  +baseURI: "%sxml_reader.xml"
+   …9
+}
+EODUMP
+            ],
+            [13, <<<'EODUMP'
+XMLReader {
+  +localName: "bar"
+  +nodeType: END_ELEMENT
+  +depth: 1
+  +baseURI: "%sxml_reader.xml"
+   …10
+}
+EODUMP
+            ],
+            [15, <<<'EODUMP'
+XMLReader {
+  +localName: "bar"
+  +nodeType: ELEMENT
+  +depth: 1
+  +attributeCount: 1
+  +baseURI: "%sxml_reader.xml"
+   …9
+}
+EODUMP
+            ],
+            [16, <<<'EODUMP'
+XMLReader {
+  +localName: "#text"
+  +nodeType: SIGNIFICANT_WHITESPACE
+  +depth: 2
+  +value: """
+    \n
+            
+    """
+  +baseURI: "%sxml_reader.xml"
+   …9
+}
+EODUMP
+            ],
+            [17, <<<'EODUMP'
+XMLReader {
+  +localName: "baz"
+  +prefix: "baz"
+  +nodeType: ELEMENT
+  +depth: 2
+  +namespaceURI: "http://symfony.com"
+  +baseURI: "%sxml_reader.xml"
+   …8
+}
+EODUMP
+            ],
+            [18, <<<'EODUMP'
+XMLReader {
+  +localName: "baz"
+  +prefix: "baz"
+  +nodeType: END_ELEMENT
+  +depth: 2
+  +namespaceURI: "http://symfony.com"
+  +baseURI: "%sxml_reader.xml"
+   …8
+}
+EODUMP
+            ],
+            [19, <<<'EODUMP'
+XMLReader {
+  +localName: "#text"
+  +nodeType: SIGNIFICANT_WHITESPACE
+  +depth: 2
+  +value: """
+    \n
+        
+    """
+  +baseURI: "%sxml_reader.xml"
+   …9
+}
+EODUMP
+            ],
+            [21, <<<'EODUMP'
+XMLReader {
+  +localName: "#text"
+  +nodeType: SIGNIFICANT_WHITESPACE
+  +depth: 1
+  +value: "\n"
+  +baseURI: "%sxml_reader.xml"
+   …9
+}
+EODUMP
+            ],
+            [22, <<<'EODUMP'
+XMLReader {
+  +localName: "foo"
+  +nodeType: END_ELEMENT
+  +baseURI: "%sxml_reader.xml"
+   …11
+}
+EODUMP
+            ],
+        ];
+    }
+}
diff --git a/vendor/symfony/var-dumper/Tests/Cloner/DataTest.php b/vendor/symfony/var-dumper/Tests/Cloner/DataTest.php
new file mode 100755
index 0000000..7d20bce
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Cloner/DataTest.php
@@ -0,0 +1,115 @@
+<?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\VarDumper\Tests\Cloner;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\VarDumper\Caster\Caster;
+use Symfony\Component\VarDumper\Caster\ClassStub;
+use Symfony\Component\VarDumper\Cloner\Data;
+use Symfony\Component\VarDumper\Cloner\VarCloner;
+
+class DataTest extends TestCase
+{
+    public function testBasicData()
+    {
+        $values = [1 => 123, 4.5, 'abc', null, false];
+        $data = $this->cloneVar($values);
+        $clonedValues = [];
+
+        $this->assertInstanceOf(Data::class, $data);
+        $this->assertCount(\count($values), $data);
+        $this->assertFalse(isset($data->{0}));
+        $this->assertFalse(isset($data[0]));
+
+        foreach ($data as $k => $v) {
+            $this->assertTrue(isset($data->{$k}));
+            $this->assertTrue(isset($data[$k]));
+            $this->assertSame(\gettype($values[$k]), $data->seek($k)->getType());
+            $this->assertSame($values[$k], $data->seek($k)->getValue());
+            $this->assertSame($values[$k], $data->{$k});
+            $this->assertSame($values[$k], $data[$k]);
+            $this->assertSame((string) $values[$k], (string) $data->seek($k));
+
+            $clonedValues[$k] = $v->getValue();
+        }
+
+        $this->assertSame($values, $clonedValues);
+    }
+
+    public function testObject()
+    {
+        $data = $this->cloneVar(new \Exception('foo'));
+
+        $this->assertSame('Exception', $data->getType());
+
+        $this->assertSame('foo', $data->message);
+        $this->assertSame('foo', $data->{Caster::PREFIX_PROTECTED.'message'});
+
+        $this->assertSame('foo', $data['message']);
+        $this->assertSame('foo', $data[Caster::PREFIX_PROTECTED.'message']);
+
+        $this->assertStringMatchesFormat('Exception (count=%d)', (string) $data);
+    }
+
+    public function testArray()
+    {
+        $values = [[], [123]];
+        $data = $this->cloneVar($values);
+
+        $this->assertSame($values, $data->getValue(true));
+
+        $children = $data->getValue();
+
+        $this->assertInternalType('array', $children);
+
+        $this->assertInstanceOf(Data::class, $children[0]);
+        $this->assertInstanceOf(Data::class, $children[1]);
+
+        $this->assertEquals($children[0], $data[0]);
+        $this->assertEquals($children[1], $data[1]);
+
+        $this->assertSame($values[0], $children[0]->getValue(true));
+        $this->assertSame($values[1], $children[1]->getValue(true));
+    }
+
+    public function testStub()
+    {
+        $data = $this->cloneVar([new ClassStub('stdClass')]);
+        $data = $data[0];
+
+        $this->assertSame('string', $data->getType());
+        $this->assertSame('stdClass', $data->getValue());
+        $this->assertSame('stdClass', (string) $data);
+    }
+
+    public function testHardRefs()
+    {
+        $values = [[]];
+        $values[1] = &$values[0];
+        $values[2][0] = &$values[2];
+
+        $data = $this->cloneVar($values);
+
+        $this->assertSame([], $data[0]->getValue());
+        $this->assertSame([], $data[1]->getValue());
+        $this->assertEquals([$data[2]->getValue()], $data[2]->getValue(true));
+
+        $this->assertSame('array (count=3)', (string) $data);
+    }
+
+    private function cloneVar($value)
+    {
+        $cloner = new VarCloner();
+
+        return $cloner->cloneVar($value);
+    }
+}
diff --git a/vendor/symfony/var-dumper/Tests/Cloner/VarClonerTest.php b/vendor/symfony/var-dumper/Tests/Cloner/VarClonerTest.php
new file mode 100755
index 0000000..d3141c6
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Cloner/VarClonerTest.php
@@ -0,0 +1,439 @@
+<?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\VarDumper\Tests\Cloner;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\VarDumper\Cloner\VarCloner;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class VarClonerTest extends TestCase
+{
+    public function testMaxIntBoundary()
+    {
+        $data = [PHP_INT_MAX => 123];
+
+        $cloner = new VarCloner();
+        $clone = $cloner->cloneVar($data);
+
+        $expected = <<<EOTXT
+Symfony\Component\VarDumper\Cloner\Data Object
+(
+    [data:Symfony\Component\VarDumper\Cloner\Data:private] => Array
+        (
+            [0] => Array
+                (
+                    [0] => Array
+                        (
+                            [1] => 1
+                        )
+
+                )
+
+            [1] => Array
+                (
+                    [%s] => 123
+                )
+
+        )
+
+    [position:Symfony\Component\VarDumper\Cloner\Data:private] => 0
+    [key:Symfony\Component\VarDumper\Cloner\Data:private] => 0
+    [maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20
+    [maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1
+    [useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
+)
+
+EOTXT;
+        $this->assertSame(sprintf($expected, PHP_INT_MAX), print_r($clone, true));
+    }
+
+    public function testClone()
+    {
+        $json = json_decode('{"1":{"var":"val"},"2":{"var":"val"}}');
+
+        $cloner = new VarCloner();
+        $clone = $cloner->cloneVar($json);
+
+        $expected = <<<EOTXT
+Symfony\Component\VarDumper\Cloner\Data Object
+(
+    [data:Symfony\Component\VarDumper\Cloner\Data:private] => Array
+        (
+            [0] => Array
+                (
+                    [0] => Symfony\Component\VarDumper\Cloner\Stub Object
+                        (
+                            [type] => 4
+                            [class] => stdClass
+                            [value] => 
+                            [cut] => 0
+                            [handle] => %i
+                            [refCount] => 0
+                            [position] => 1
+                            [attr] => Array
+                                (
+                                )
+
+                        )
+
+                )
+
+            [1] => Array
+                (
+                    [\000+\0001] => Symfony\Component\VarDumper\Cloner\Stub Object
+                        (
+                            [type] => 4
+                            [class] => stdClass
+                            [value] => 
+                            [cut] => 0
+                            [handle] => %i
+                            [refCount] => 0
+                            [position] => 2
+                            [attr] => Array
+                                (
+                                )
+
+                        )
+
+                    [\000+\0002] => Symfony\Component\VarDumper\Cloner\Stub Object
+                        (
+                            [type] => 4
+                            [class] => stdClass
+                            [value] => 
+                            [cut] => 0
+                            [handle] => %i
+                            [refCount] => 0
+                            [position] => 3
+                            [attr] => Array
+                                (
+                                )
+
+                        )
+
+                )
+
+            [2] => Array
+                (
+                    [\000+\000var] => val
+                )
+
+            [3] => Array
+                (
+                    [\000+\000var] => val
+                )
+
+        )
+
+    [position:Symfony\Component\VarDumper\Cloner\Data:private] => 0
+    [key:Symfony\Component\VarDumper\Cloner\Data:private] => 0
+    [maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20
+    [maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1
+    [useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
+)
+
+EOTXT;
+        $this->assertStringMatchesFormat($expected, print_r($clone, true));
+    }
+
+    public function testLimits()
+    {
+        // Level 0:
+        $data = [
+            // Level 1:
+            [
+                // Level 2:
+                [
+                    // Level 3:
+                    'Level 3 Item 0',
+                    'Level 3 Item 1',
+                    'Level 3 Item 2',
+                    'Level 3 Item 3',
+                ],
+                [
+                    'Level 3 Item 4',
+                    'Level 3 Item 5',
+                    'Level 3 Item 6',
+                ],
+                [
+                    'Level 3 Item 7',
+                ],
+            ],
+            [
+                [
+                    'Level 3 Item 8',
+                ],
+                'Level 2 Item 0',
+            ],
+            [
+                'Level 2 Item 1',
+            ],
+            'Level 1 Item 0',
+            [
+                // Test setMaxString:
+                'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
+                'SHORT',
+            ],
+        ];
+
+        $cloner = new VarCloner();
+        $cloner->setMinDepth(2);
+        $cloner->setMaxItems(5);
+        $cloner->setMaxString(20);
+        $clone = $cloner->cloneVar($data);
+
+        $expected = <<<EOTXT
+Symfony\Component\VarDumper\Cloner\Data Object
+(
+    [data:Symfony\Component\VarDumper\Cloner\Data:private] => Array
+        (
+            [0] => Array
+                (
+                    [0] => Array
+                        (
+                            [2] => 1
+                        )
+
+                )
+
+            [1] => Array
+                (
+                    [0] => Array
+                        (
+                            [2] => 2
+                        )
+
+                    [1] => Array
+                        (
+                            [2] => 3
+                        )
+
+                    [2] => Array
+                        (
+                            [2] => 4
+                        )
+
+                    [3] => Level 1 Item 0
+                    [4] => Array
+                        (
+                            [2] => 5
+                        )
+
+                )
+
+            [2] => Array
+                (
+                    [0] => Array
+                        (
+                            [2] => 6
+                        )
+
+                    [1] => Array
+                        (
+                            [0] => 2
+                            [2] => 7
+                        )
+
+                    [2] => Array
+                        (
+                            [0] => 1
+                            [2] => 0
+                        )
+
+                )
+
+            [3] => Array
+                (
+                    [0] => Array
+                        (
+                            [0] => 1
+                            [2] => 0
+                        )
+
+                    [1] => Level 2 Item 0
+                )
+
+            [4] => Array
+                (
+                    [0] => Level 2 Item 1
+                )
+
+            [5] => Array
+                (
+                    [0] => Symfony\Component\VarDumper\Cloner\Stub Object
+                        (
+                            [type] => 2
+                            [class] => 2
+                            [value] => ABCDEFGHIJKLMNOPQRST
+                            [cut] => 6
+                            [handle] => 0
+                            [refCount] => 0
+                            [position] => 0
+                            [attr] => Array
+                                (
+                                )
+
+                        )
+
+                    [1] => SHORT
+                )
+
+            [6] => Array
+                (
+                    [0] => Level 3 Item 0
+                    [1] => Level 3 Item 1
+                    [2] => Level 3 Item 2
+                    [3] => Level 3 Item 3
+                )
+
+            [7] => Array
+                (
+                    [0] => Level 3 Item 4
+                )
+
+        )
+
+    [position:Symfony\Component\VarDumper\Cloner\Data:private] => 0
+    [key:Symfony\Component\VarDumper\Cloner\Data:private] => 0
+    [maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20
+    [maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1
+    [useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
+)
+
+EOTXT;
+        $this->assertStringMatchesFormat($expected, print_r($clone, true));
+    }
+
+    public function testJsonCast()
+    {
+        if (2 == ini_get('xdebug.overload_var_dump')) {
+            $this->markTestSkipped('xdebug is active');
+        }
+
+        $data = (array) json_decode('{"1":{}}');
+
+        $cloner = new VarCloner();
+        $clone = $cloner->cloneVar($data);
+
+        $expected = <<<'EOTXT'
+object(Symfony\Component\VarDumper\Cloner\Data)#%i (6) {
+  ["data":"Symfony\Component\VarDumper\Cloner\Data":private]=>
+  array(2) {
+    [0]=>
+    array(1) {
+      [0]=>
+      array(1) {
+        [1]=>
+        int(1)
+      }
+    }
+    [1]=>
+    array(1) {
+      ["1"]=>
+      object(Symfony\Component\VarDumper\Cloner\Stub)#%i (8) {
+        ["type"]=>
+        int(4)
+        ["class"]=>
+        string(8) "stdClass"
+        ["value"]=>
+        NULL
+        ["cut"]=>
+        int(0)
+        ["handle"]=>
+        int(%i)
+        ["refCount"]=>
+        int(0)
+        ["position"]=>
+        int(0)
+        ["attr"]=>
+        array(0) {
+        }
+      }
+    }
+  }
+  ["position":"Symfony\Component\VarDumper\Cloner\Data":private]=>
+  int(0)
+  ["key":"Symfony\Component\VarDumper\Cloner\Data":private]=>
+  int(0)
+  ["maxDepth":"Symfony\Component\VarDumper\Cloner\Data":private]=>
+  int(20)
+  ["maxItemsPerDepth":"Symfony\Component\VarDumper\Cloner\Data":private]=>
+  int(-1)
+  ["useRefHandles":"Symfony\Component\VarDumper\Cloner\Data":private]=>
+  int(-1)
+}
+
+EOTXT;
+        ob_start();
+        var_dump($clone);
+        $this->assertStringMatchesFormat(\PHP_VERSION_ID >= 70200 ? str_replace('"1"', '1', $expected) : $expected, ob_get_clean());
+    }
+
+    public function testCaster()
+    {
+        $cloner = new VarCloner([
+            '*' => function ($obj, $array) {
+                return ['foo' => 123];
+            },
+            __CLASS__ => function ($obj, $array) {
+                ++$array['foo'];
+
+                return $array;
+            },
+        ]);
+        $clone = $cloner->cloneVar($this);
+
+        $expected = <<<EOTXT
+Symfony\Component\VarDumper\Cloner\Data Object
+(
+    [data:Symfony\Component\VarDumper\Cloner\Data:private] => Array
+        (
+            [0] => Array
+                (
+                    [0] => Symfony\Component\VarDumper\Cloner\Stub Object
+                        (
+                            [type] => 4
+                            [class] => %s
+                            [value] => 
+                            [cut] => 0
+                            [handle] => %i
+                            [refCount] => 0
+                            [position] => 1
+                            [attr] => Array
+                                (
+                                    [file] => %a%eVarClonerTest.php
+                                    [line] => 20
+                                )
+
+                        )
+
+                )
+
+            [1] => Array
+                (
+                    [foo] => 124
+                )
+
+        )
+
+    [position:Symfony\Component\VarDumper\Cloner\Data:private] => 0
+    [key:Symfony\Component\VarDumper\Cloner\Data:private] => 0
+    [maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20
+    [maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1
+    [useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
+)
+
+EOTXT;
+        $this->assertStringMatchesFormat($expected, print_r($clone, true));
+    }
+}
diff --git a/vendor/symfony/var-dumper/Tests/Command/Descriptor/CliDescriptorTest.php b/vendor/symfony/var-dumper/Tests/Command/Descriptor/CliDescriptorTest.php
new file mode 100755
index 0000000..fef5dcd
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Command/Descriptor/CliDescriptorTest.php
@@ -0,0 +1,173 @@
+<?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\VarDumper\Tests\Command\Descriptor;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Console\Formatter\OutputFormatterStyle;
+use Symfony\Component\Console\Output\BufferedOutput;
+use Symfony\Component\VarDumper\Cloner\Data;
+use Symfony\Component\VarDumper\Command\Descriptor\CliDescriptor;
+use Symfony\Component\VarDumper\Dumper\CliDumper;
+
+class CliDescriptorTest extends TestCase
+{
+    private static $timezone;
+    private static $prevTerminalEmulator;
+
+    public static function setUpBeforeClass()
+    {
+        self::$timezone = date_default_timezone_get();
+        date_default_timezone_set('UTC');
+
+        self::$prevTerminalEmulator = getenv('TERMINAL_EMULATOR');
+        putenv('TERMINAL_EMULATOR');
+    }
+
+    public static function tearDownAfterClass()
+    {
+        date_default_timezone_set(self::$timezone);
+        putenv('TERMINAL_EMULATOR'.(self::$prevTerminalEmulator ? '='.self::$prevTerminalEmulator : ''));
+    }
+
+    /**
+     * @dataProvider provideContext
+     */
+    public function testDescribe(array $context, string $expectedOutput, bool $decorated = false)
+    {
+        $output = new BufferedOutput();
+        $output->setDecorated($decorated);
+        $descriptor = new CliDescriptor(new CliDumper(function ($s) {
+            return $s;
+        }));
+
+        $descriptor->describe($output, new Data([[123]]), $context + ['timestamp' => 1544804268.3668], 1);
+
+        $this->assertStringMatchesFormat(trim($expectedOutput), str_replace(PHP_EOL, "\n", trim($output->fetch())));
+    }
+
+    public function provideContext()
+    {
+        yield 'source' => [
+            [
+                'source' => [
+                    'name' => 'CliDescriptorTest.php',
+                    'line' => 30,
+                    'file' => '/Users/ogi/symfony/src/Symfony/Component/VarDumper/Tests/Command/Descriptor/CliDescriptorTest.php',
+                ],
+            ],
+            <<<TXT
+Received from client #1
+-----------------------
+
+ -------- --------------------------------------------------------------------------------------------------- 
+  date     Fri, 14 Dec 2018 16:17:48 +0000                                                                    
+  source   CliDescriptorTest.php on line 30                                                                   
+  file     /Users/ogi/symfony/src/Symfony/Component/VarDumper/Tests/Command/Descriptor/CliDescriptorTest.php  
+ -------- ---------------------------------------------------------------------------------------------------
+TXT
+        ];
+
+        yield 'source full' => [
+            [
+                'source' => [
+                    'name' => 'CliDescriptorTest.php',
+                    'line' => 30,
+                    'file_relative' => 'src/Symfony/Component/VarDumper/Tests/Command/Descriptor/CliDescriptorTest.php',
+                    'file' => '/Users/ogi/symfony/src/Symfony/Component/VarDumper/Tests/Command/Descriptor/CliDescriptorTest.php',
+                    'file_link' => 'phpstorm://open?file=/Users/ogi/symfony/src/Symfony/Component/VarDumper/Tests/Command/Descriptor/CliDescriptorTest.php&line=30',
+                ],
+            ],
+            method_exists(OutputFormatterStyle::class, 'setHref') ?
+                <<<TXT
+Received from client #1
+-----------------------
+
+ -------- -------------------------------------------------------------------------------- 
+  date     Fri, 14 Dec 2018 16:17:48 +0000                                                 
+  source   CliDescriptorTest.php on line 30                                                
+  file     src/Symfony/Component/VarDumper/Tests/Command/Descriptor/CliDescriptorTest.php  
+ -------- -------------------------------------------------------------------------------- 
+
+TXT
+                :
+                <<<TXT
+Received from client #1
+-----------------------
+
+ -------- -------------------------------------------------------------------------------- 
+  date     Fri, 14 Dec 2018 16:17:48 +0000                                                 
+  source   CliDescriptorTest.php on line 30                                                
+  file     src/Symfony/Component/VarDumper/Tests/Command/Descriptor/CliDescriptorTest.php  
+ -------- -------------------------------------------------------------------------------- 
+
+Open source in your IDE/browser:
+phpstorm://open?file=/Users/ogi/symfony/src/Symfony/Component/VarDumper/Tests/Command/Descriptor/CliDescriptorTest.php&line=30
+TXT
+        ];
+
+        if (method_exists(OutputFormatterStyle::class, 'setHref')) {
+            yield 'source with hyperlink' => [
+                [
+                    'source' => [
+                        'name' => 'CliDescriptorTest.php',
+                        'line' => 30,
+                        'file_relative' => 'src/Symfony/Component/VarDumper/Tests/Command/Descriptor/CliDescriptorTest.php',
+                        'file_link' => 'phpstorm://open?file=/Users/ogi/symfony/src/Symfony/Component/VarDumper/Tests/Command/Descriptor/CliDescriptorTest.php&line=30',
+                    ],
+                ],
+                <<<TXT
+%A
+  source   \033]8;;phpstorm://open?file=/Users/ogi/symfony/src/Symfony/Component/VarDumper/Tests/Command/Descriptor/CliDescriptorTest.php&line=30\033\CliDescriptorTest.php on line 30\033]8;;\033%A
+%A
+TXT
+                , true,
+            ];
+        }
+
+        yield 'cli' => [
+            [
+                'cli' => [
+                    'identifier' => 'd8bece1c',
+                    'command_line' => 'bin/phpunit',
+                ],
+            ],
+            <<<TXT
+$ bin/phpunit
+-------------
+
+ ------ --------------------------------- 
+  date   Fri, 14 Dec 2018 16:17:48 +0000  
+ ------ ---------------------------------
+TXT
+        ];
+
+        yield 'request' => [
+            [
+                'request' => [
+                    'identifier' => 'd8bece1c',
+                    'controller' => new Data([['FooController.php']]),
+                    'method' => 'GET',
+                    'uri' => 'http://localhost/foo',
+                ],
+            ],
+            <<<TXT
+GET http://localhost/foo
+------------------------
+
+ ------------ --------------------------------- 
+  date         Fri, 14 Dec 2018 16:17:48 +0000  
+  controller   "FooController.php"              
+ ------------ --------------------------------- 
+TXT
+        ];
+    }
+}
diff --git a/vendor/symfony/var-dumper/Tests/Command/Descriptor/HtmlDescriptorTest.php b/vendor/symfony/var-dumper/Tests/Command/Descriptor/HtmlDescriptorTest.php
new file mode 100755
index 0000000..f1febf7
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Command/Descriptor/HtmlDescriptorTest.php
@@ -0,0 +1,195 @@
+<?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\VarDumper\Tests\Command\Descriptor;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Console\Output\BufferedOutput;
+use Symfony\Component\VarDumper\Cloner\Data;
+use Symfony\Component\VarDumper\Command\Descriptor\HtmlDescriptor;
+use Symfony\Component\VarDumper\Dumper\HtmlDumper;
+
+class HtmlDescriptorTest extends TestCase
+{
+    private static $timezone;
+
+    public static function setUpBeforeClass()
+    {
+        self::$timezone = date_default_timezone_get();
+        date_default_timezone_set('UTC');
+    }
+
+    public static function tearDownAfterClass()
+    {
+        date_default_timezone_set(self::$timezone);
+    }
+
+    public function testItOutputsStylesAndScriptsOnFirstDescribeCall()
+    {
+        $output = new BufferedOutput();
+        $dumper = $this->createMock(HtmlDumper::class);
+        $dumper->method('dump')->willReturn('[DUMPED]');
+        $descriptor = new HtmlDescriptor($dumper);
+
+        $descriptor->describe($output, new Data([[123]]), ['timestamp' => 1544804268.3668], 1);
+
+        $this->assertStringMatchesFormat('<style>%A</style><script>%A</script>%A', $output->fetch(), 'styles & scripts are output');
+
+        $descriptor->describe($output, new Data([[123]]), ['timestamp' => 1544804268.3668], 1);
+
+        $this->assertStringNotMatchesFormat('<style>%A</style><script>%A</script>%A', $output->fetch(), 'styles & scripts are output only once');
+    }
+
+    /**
+     * @dataProvider provideContext
+     */
+    public function testDescribe(array $context, string $expectedOutput)
+    {
+        $output = new BufferedOutput();
+        $dumper = $this->createMock(HtmlDumper::class);
+        $dumper->method('dump')->willReturn('[DUMPED]');
+        $descriptor = new HtmlDescriptor($dumper);
+
+        $descriptor->describe($output, new Data([[123]]), $context + ['timestamp' => 1544804268.3668], 1);
+
+        $this->assertStringMatchesFormat(trim($expectedOutput), trim(preg_replace('@<style>.*</style><script>.*</script>@s', '', $output->fetch())));
+    }
+
+    public function provideContext()
+    {
+        yield 'source' => [
+            [
+                'source' => [
+                    'name' => 'CliDescriptorTest.php',
+                    'line' => 30,
+                    'file' => '/Users/ogi/symfony/src/Symfony/Component/VarDumper/Tests/Command/Descriptor/CliDescriptorTest.php',
+                ],
+            ],
+            <<<TXT
+<article data-dedup-id="%s">
+    <header>
+        <div class="row">
+            <h2 class="col">-</h2>
+            <time class="col text-small" title="2018-12-14T16:17:48+00:00" datetime="2018-12-14T16:17:48+00:00">
+                Fri, 14 Dec 2018 16:17:48 +0000
+            </time>
+        </div>
+        
+    </header>
+    <section class="body">
+        <p class="text-small">
+            CliDescriptorTest.php on line 30
+        </p>
+        [DUMPED]
+    </section>
+</article>
+TXT
+        ];
+
+        yield 'source full' => [
+            [
+                'source' => [
+                    'name' => 'CliDescriptorTest.php',
+                    'project_dir' => 'src/Symfony/',
+                    'line' => 30,
+                    'file_relative' => 'src/Symfony/Component/VarDumper/Tests/Command/Descriptor/CliDescriptorTest.php',
+                    'file' => '/Users/ogi/symfony/src/Symfony/Component/VarDumper/Tests/Command/Descriptor/CliDescriptorTest.php',
+                    'file_link' => 'phpstorm://open?file=/Users/ogi/symfony/src/Symfony/Component/VarDumper/Tests/Command/Descriptor/CliDescriptorTest.php&line=30',
+                ],
+            ],
+            <<<TXT
+<article data-dedup-id="%s">
+    <header>
+        <div class="row">
+            <h2 class="col">-</h2>
+            <time class="col text-small" title="2018-12-14T16:17:48+00:00" datetime="2018-12-14T16:17:48+00:00">
+                Fri, 14 Dec 2018 16:17:48 +0000
+            </time>
+        </div>
+        <div class="row">
+    <ul class="tags">
+        <li><span class="badge">project dir</span>src/Symfony/</li>
+    </ul>
+</div>
+    </header>
+    <section class="body">
+        <p class="text-small">
+            <a href="phpstorm://open?file=/Users/ogi/symfony/src/Symfony/Component/VarDumper/Tests/Command/Descriptor/CliDescriptorTest.php&line=30">CliDescriptorTest.php on line 30</a>
+        </p>
+        [DUMPED]
+    </section>
+</article>
+TXT
+        ];
+
+        yield 'cli' => [
+            [
+                'cli' => [
+                    'identifier' => 'd8bece1c',
+                    'command_line' => 'bin/phpunit',
+                ],
+            ],
+            <<<TXT
+<article data-dedup-id="d8bece1c">
+    <header>
+        <div class="row">
+            <h2 class="col"><code>$ </code>bin/phpunit</h2>
+            <time class="col text-small" title="2018-12-14T16:17:48+00:00" datetime="2018-12-14T16:17:48+00:00">
+                Fri, 14 Dec 2018 16:17:48 +0000
+            </time>
+        </div>
+        
+    </header>
+    <section class="body">
+        <p class="text-small">
+            
+        </p>
+        [DUMPED]
+    </section>
+</article>
+TXT
+        ];
+
+        yield 'request' => [
+            [
+                'request' => [
+                    'identifier' => 'd8bece1c',
+                    'controller' => new Data([['FooController.php']]),
+                    'method' => 'GET',
+                    'uri' => 'http://localhost/foo',
+                ],
+            ],
+            <<<TXT
+<article data-dedup-id="d8bece1c">
+    <header>
+        <div class="row">
+            <h2 class="col"><code>GET</code> <a href="http://localhost/foo">http://localhost/foo</a></h2>
+            <time class="col text-small" title="2018-12-14T16:17:48+00:00" datetime="2018-12-14T16:17:48+00:00">
+                Fri, 14 Dec 2018 16:17:48 +0000
+            </time>
+        </div>
+        <div class="row">
+    <ul class="tags">
+        <li><span class="badge">controller</span><span class='dumped-tag'>[DUMPED]</span></li>
+    </ul>
+</div>
+    </header>
+    <section class="body">
+        <p class="text-small">
+            
+        </p>
+        [DUMPED]
+    </section>
+</article>
+TXT
+        ];
+    }
+}
diff --git a/vendor/symfony/var-dumper/Tests/Dumper/CliDumperTest.php b/vendor/symfony/var-dumper/Tests/Dumper/CliDumperTest.php
new file mode 100755
index 0000000..fc62380
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Dumper/CliDumperTest.php
@@ -0,0 +1,534 @@
+<?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\VarDumper\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\VarDumper\Cloner\VarCloner;
+use Symfony\Component\VarDumper\Dumper\CliDumper;
+use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
+use Twig\Environment;
+use Twig\Loader\FilesystemLoader;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class CliDumperTest extends TestCase
+{
+    use VarDumperTestTrait;
+
+    public function testGet()
+    {
+        require __DIR__.'/../Fixtures/dumb-var.php';
+
+        $dumper = new CliDumper('php://output');
+        $dumper->setColors(false);
+        $cloner = new VarCloner();
+        $cloner->addCasters([
+            ':stream' => function ($res, $a) {
+                unset($a['uri'], $a['wrapper_data']);
+
+                return $a;
+            },
+        ]);
+        $data = $cloner->cloneVar($var);
+
+        ob_start();
+        $dumper->dump($data);
+        $out = ob_get_clean();
+        $out = preg_replace('/[ \t]+$/m', '', $out);
+        $intMax = PHP_INT_MAX;
+        $res = (int) $var['res'];
+
+        $this->assertStringMatchesFormat(
+            <<<EOTXT
+array:24 [
+  "number" => 1
+  0 => &1 null
+  "const" => 1.1
+  1 => true
+  2 => false
+  3 => NAN
+  4 => INF
+  5 => -INF
+  6 => {$intMax}
+  "str" => "déjà\\n"
+  7 => b"""
+    é\\x00test\\t\\n
+    ing
+    """
+  "[]" => []
+  "res" => stream resource {@{$res}
+%A  wrapper_type: "plainfile"
+    stream_type: "STDIO"
+    mode: "r"
+    unread_bytes: 0
+    seekable: true
+%A  options: []
+  }
+  "obj" => Symfony\Component\VarDumper\Tests\Fixture\DumbFoo {#%d
+    +foo: "foo"
+    +"bar": "bar"
+  }
+  "closure" => Closure(\$a, PDO &\$b = null) {#%d
+    class: "Symfony\Component\VarDumper\Tests\Dumper\CliDumperTest"
+    this: Symfony\Component\VarDumper\Tests\Dumper\CliDumperTest {#%d …}
+    file: "%s%eTests%eFixtures%edumb-var.php"
+    line: "{$var['line']} to {$var['line']}"
+  }
+  "line" => {$var['line']}
+  "nobj" => array:1 [
+    0 => &3 {#%d}
+  ]
+  "recurs" => &4 array:1 [
+    0 => &4 array:1 [&4]
+  ]
+  8 => &1 null
+  "sobj" => Symfony\Component\VarDumper\Tests\Fixture\DumbFoo {#%d}
+  "snobj" => &3 {#%d}
+  "snobj2" => {#%d}
+  "file" => "{$var['file']}"
+  b"bin-key-é" => ""
+]
+
+EOTXT
+            ,
+            $out
+        );
+    }
+
+    /**
+     * @dataProvider provideDumpWithCommaFlagTests
+     */
+    public function testDumpWithCommaFlag($expected, $flags)
+    {
+        $dumper = new CliDumper(null, null, $flags);
+        $dumper->setColors(false);
+        $cloner = new VarCloner();
+
+        $var = [
+            'array' => ['a', 'b'],
+            'string' => 'hello',
+            'multiline string' => "this\nis\na\multiline\nstring",
+        ];
+
+        $dump = $dumper->dump($cloner->cloneVar($var), true);
+
+        $this->assertSame($expected, $dump);
+    }
+
+    public function testDumpWithCommaFlagsAndExceptionCodeExcerpt()
+    {
+        $dumper = new CliDumper(null, null, CliDumper::DUMP_TRAILING_COMMA);
+        $dumper->setColors(false);
+        $cloner = new VarCloner();
+
+        $ex = new \RuntimeException('foo');
+
+        $dump = $dumper->dump($cloner->cloneVar($ex)->withRefHandles(false), true);
+
+        $this->assertStringMatchesFormat(<<<'EOTXT'
+RuntimeException {
+  #message: "foo"
+  #code: 0
+  #file: "%ACliDumperTest.php"
+  #line: %d
+  trace: {
+    %ACliDumperTest.php:%d {
+      › 
+      › $ex = new \RuntimeException('foo');
+      › 
+    }
+    %A
+  }
+}
+
+EOTXT
+            , $dump);
+    }
+
+    public function provideDumpWithCommaFlagTests()
+    {
+        $expected = <<<'EOTXT'
+array:3 [
+  "array" => array:2 [
+    0 => "a",
+    1 => "b"
+  ],
+  "string" => "hello",
+  "multiline string" => """
+    this\n
+    is\n
+    a\multiline\n
+    string
+    """
+]
+
+EOTXT;
+
+        yield [$expected, CliDumper::DUMP_COMMA_SEPARATOR];
+
+        $expected = <<<'EOTXT'
+array:3 [
+  "array" => array:2 [
+    0 => "a",
+    1 => "b",
+  ],
+  "string" => "hello",
+  "multiline string" => """
+    this\n
+    is\n
+    a\multiline\n
+    string
+    """,
+]
+
+EOTXT;
+
+        yield [$expected, CliDumper::DUMP_TRAILING_COMMA];
+    }
+
+    /**
+     * @requires extension xml
+     */
+    public function testXmlResource()
+    {
+        $var = xml_parser_create();
+
+        $this->assertDumpMatchesFormat(
+            <<<'EOTXT'
+xml resource {
+  current_byte_index: %i
+  current_column_number: %i
+  current_line_number: 1
+  error_code: XML_ERROR_NONE
+}
+EOTXT
+            ,
+            $var
+        );
+    }
+
+    public function testJsonCast()
+    {
+        $var = (array) json_decode('{"0":{},"1":null}');
+        foreach ($var as &$v) {
+        }
+        $var[] = &$v;
+        $var[''] = 2;
+
+        if (\PHP_VERSION_ID >= 70200) {
+            $this->assertDumpMatchesFormat(
+                <<<'EOTXT'
+array:4 [
+  0 => {}
+  1 => &1 null
+  2 => &1 null
+  "" => 2
+]
+EOTXT
+                ,
+                $var
+            );
+        } else {
+            $this->assertDumpMatchesFormat(
+                <<<'EOTXT'
+array:4 [
+  "0" => {}
+  "1" => &1 null
+  0 => &1 null
+  "" => 2
+]
+EOTXT
+                ,
+                $var
+            );
+        }
+    }
+
+    public function testObjectCast()
+    {
+        $var = (object) [1 => 1];
+        $var->{1} = 2;
+
+        if (\PHP_VERSION_ID >= 70200) {
+            $this->assertDumpMatchesFormat(
+                <<<'EOTXT'
+{
+  +"1": 2
+}
+EOTXT
+                ,
+                $var
+            );
+        } else {
+            $this->assertDumpMatchesFormat(
+                <<<'EOTXT'
+{
+  +1: 1
+  +"1": 2
+}
+EOTXT
+                ,
+                $var
+            );
+        }
+    }
+
+    public function testClosedResource()
+    {
+        $var = fopen(__FILE__, 'r');
+        fclose($var);
+
+        $dumper = new CliDumper('php://output');
+        $dumper->setColors(false);
+        $cloner = new VarCloner();
+        $data = $cloner->cloneVar($var);
+
+        ob_start();
+        $dumper->dump($data);
+        $out = ob_get_clean();
+        $res = (int) $var;
+
+        $this->assertStringMatchesFormat(
+            <<<EOTXT
+Closed resource @{$res}
+
+EOTXT
+            ,
+            $out
+        );
+    }
+
+    public function testFlags()
+    {
+        putenv('DUMP_LIGHT_ARRAY=1');
+        putenv('DUMP_STRING_LENGTH=1');
+
+        $var = [
+            range(1, 3),
+            ['foo', 2 => 'bar'],
+        ];
+
+        $this->assertDumpEquals(
+            <<<EOTXT
+[
+  [
+    1
+    2
+    3
+  ]
+  [
+    0 => (3) "foo"
+    2 => (3) "bar"
+  ]
+]
+EOTXT
+            ,
+            $var
+        );
+
+        putenv('DUMP_LIGHT_ARRAY=');
+        putenv('DUMP_STRING_LENGTH=');
+    }
+
+    /**
+     * @requires function Twig\Template::getSourceContext
+     */
+    public function testThrowingCaster()
+    {
+        $out = fopen('php://memory', 'r+b');
+
+        require_once __DIR__.'/../Fixtures/Twig.php';
+        $twig = new \__TwigTemplate_VarDumperFixture_u75a09(new Environment(new FilesystemLoader()));
+
+        $dumper = new CliDumper();
+        $dumper->setColors(false);
+        $cloner = new VarCloner();
+        $cloner->addCasters([
+            ':stream' => function ($res, $a) {
+                unset($a['wrapper_data']);
+
+                return $a;
+            },
+        ]);
+        $cloner->addCasters([
+            ':stream' => eval('return function () use ($twig) {
+                try {
+                    $twig->render([]);
+                } catch (\Twig\Error\RuntimeError $e) {
+                    throw $e->getPrevious();
+                }
+            };'),
+        ]);
+        $ref = (int) $out;
+
+        $data = $cloner->cloneVar($out);
+        $dumper->dump($data, $out);
+        $out = stream_get_contents($out, -1, 0);
+
+        $this->assertStringMatchesFormat(
+            <<<EOTXT
+stream resource {@{$ref}
+  ⚠: Symfony\Component\VarDumper\Exception\ThrowingCasterException {#%d
+    #message: "Unexpected Exception thrown from a caster: Foobar"
+    trace: {
+      %sTwig.php:2 {
+        › foo bar
+        ›   twig source
+        › 
+      }
+      %s%eTemplate.php:%d { …}
+      %s%eTemplate.php:%d { …}
+      %s%eTemplate.php:%d { …}
+      %s%eTests%eDumper%eCliDumperTest.php:%d { …}
+%A  }
+  }
+%Awrapper_type: "PHP"
+  stream_type: "MEMORY"
+  mode: "%s+b"
+  unread_bytes: 0
+  seekable: true
+  uri: "php://memory"
+%Aoptions: []
+}
+
+EOTXT
+            ,
+            $out
+        );
+    }
+
+    public function testRefsInProperties()
+    {
+        $var = (object) ['foo' => 'foo'];
+        $var->bar = &$var->foo;
+
+        $dumper = new CliDumper();
+        $dumper->setColors(false);
+        $cloner = new VarCloner();
+
+        $data = $cloner->cloneVar($var);
+        $out = $dumper->dump($data, true);
+
+        $this->assertStringMatchesFormat(
+            <<<EOTXT
+{#%d
+  +"foo": &1 "foo"
+  +"bar": &1 "foo"
+}
+
+EOTXT
+            ,
+            $out
+        );
+    }
+
+    /**
+     * @runInSeparateProcess
+     * @preserveGlobalState disabled
+     */
+    public function testSpecialVars56()
+    {
+        $var = $this->getSpecialVars();
+
+        $this->assertDumpEquals(
+            <<<'EOTXT'
+array:3 [
+  0 => array:1 [
+    0 => &1 array:1 [
+      0 => &1 array:1 [&1]
+    ]
+  ]
+  1 => array:1 [
+    "GLOBALS" => &2 array:1 [
+      "GLOBALS" => &2 array:1 [&2]
+    ]
+  ]
+  2 => &2 array:1 [&2]
+]
+EOTXT
+            ,
+            $var
+        );
+    }
+
+    /**
+     * @runInSeparateProcess
+     * @preserveGlobalState disabled
+     */
+    public function testGlobals()
+    {
+        $var = $this->getSpecialVars();
+        unset($var[0]);
+        $out = '';
+
+        $dumper = new CliDumper(function ($line, $depth) use (&$out) {
+            if ($depth >= 0) {
+                $out .= str_repeat('  ', $depth).$line."\n";
+            }
+        });
+        $dumper->setColors(false);
+        $cloner = new VarCloner();
+
+        $data = $cloner->cloneVar($var);
+        $dumper->dump($data);
+
+        $this->assertSame(
+            <<<'EOTXT'
+array:2 [
+  1 => array:1 [
+    "GLOBALS" => &1 array:1 [
+      "GLOBALS" => &1 array:1 [&1]
+    ]
+  ]
+  2 => &1 array:1 [&1]
+]
+
+EOTXT
+            ,
+            $out
+        );
+    }
+
+    public function testIncompleteClass()
+    {
+        $unserializeCallbackHandler = ini_set('unserialize_callback_func', null);
+        $var = unserialize('O:8:"Foo\Buzz":0:{}');
+        ini_set('unserialize_callback_func', $unserializeCallbackHandler);
+
+        $this->assertDumpMatchesFormat(
+            <<<EOTXT
+__PHP_Incomplete_Class(Foo\Buzz) {}
+EOTXT
+            ,
+            $var
+        );
+    }
+
+    private function getSpecialVars()
+    {
+        foreach (array_keys($GLOBALS) as $var) {
+            if ('GLOBALS' !== $var) {
+                unset($GLOBALS[$var]);
+            }
+        }
+
+        $var = function &() {
+            $var = [];
+            $var[] = &$var;
+
+            return $var;
+        };
+
+        return [$var(), $GLOBALS, &$GLOBALS];
+    }
+}
diff --git a/vendor/symfony/var-dumper/Tests/Dumper/FunctionsTest.php b/vendor/symfony/var-dumper/Tests/Dumper/FunctionsTest.php
new file mode 100755
index 0000000..0bd596c
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Dumper/FunctionsTest.php
@@ -0,0 +1,57 @@
+<?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\VarDumper\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\VarDumper\Cloner\VarCloner;
+use Symfony\Component\VarDumper\Dumper\CliDumper;
+use Symfony\Component\VarDumper\VarDumper;
+
+class FunctionsTest extends TestCase
+{
+    public function testDumpReturnsFirstArg()
+    {
+        $this->setupVarDumper();
+
+        $var1 = 'a';
+
+        ob_start();
+        $return = dump($var1);
+        $out = ob_get_clean();
+
+        $this->assertEquals($var1, $return);
+    }
+
+    public function testDumpReturnsAllArgsInArray()
+    {
+        $this->setupVarDumper();
+
+        $var1 = 'a';
+        $var2 = 'b';
+        $var3 = 'c';
+
+        ob_start();
+        $return = dump($var1, $var2, $var3);
+        $out = ob_get_clean();
+
+        $this->assertEquals([$var1, $var2, $var3], $return);
+    }
+
+    protected function setupVarDumper()
+    {
+        $cloner = new VarCloner();
+        $dumper = new CliDumper('php://output');
+        VarDumper::setHandler(function ($var) use ($cloner, $dumper) {
+            $dumper->dump($cloner->cloneVar($var));
+        });
+    }
+}
diff --git a/vendor/symfony/var-dumper/Tests/Dumper/HtmlDumperTest.php b/vendor/symfony/var-dumper/Tests/Dumper/HtmlDumperTest.php
new file mode 100755
index 0000000..ae4ee8e
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Dumper/HtmlDumperTest.php
@@ -0,0 +1,163 @@
+<?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\VarDumper\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\VarDumper\Cloner\VarCloner;
+use Symfony\Component\VarDumper\Dumper\HtmlDumper;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class HtmlDumperTest extends TestCase
+{
+    public function testGet()
+    {
+        if (ini_get('xdebug.file_link_format') || get_cfg_var('xdebug.file_link_format')) {
+            $this->markTestSkipped('A custom file_link_format is defined.');
+        }
+
+        require __DIR__.'/../Fixtures/dumb-var.php';
+
+        $dumper = new HtmlDumper('php://output');
+        $dumper->setDumpHeader('<foo></foo>');
+        $dumper->setDumpBoundaries('<bar>', '</bar>');
+        $cloner = new VarCloner();
+        $cloner->addCasters([
+            ':stream' => function ($res, $a) {
+                unset($a['uri'], $a['wrapper_data']);
+
+                return $a;
+            },
+        ]);
+        $data = $cloner->cloneVar($var);
+
+        ob_start();
+        $dumper->dump($data);
+        $out = ob_get_clean();
+        $out = preg_replace('/[ \t]+$/m', '', $out);
+        $var['file'] = htmlspecialchars($var['file'], ENT_QUOTES, 'UTF-8');
+        $intMax = PHP_INT_MAX;
+        preg_match('/sf-dump-\d+/', $out, $dumpId);
+        $dumpId = $dumpId[0];
+        $res = (int) $var['res'];
+
+        $this->assertStringMatchesFormat(
+            <<<EOTXT
+<foo></foo><bar><span class=sf-dump-note>array:24</span> [<samp>
+  "<span class=sf-dump-key>number</span>" => <span class=sf-dump-num>1</span>
+  <span class=sf-dump-key>0</span> => <a class=sf-dump-ref href=#{$dumpId}-ref01 title="2 occurrences">&amp;1</a> <span class=sf-dump-const>null</span>
+  "<span class=sf-dump-key>const</span>" => <span class=sf-dump-num>1.1</span>
+  <span class=sf-dump-key>1</span> => <span class=sf-dump-const>true</span>
+  <span class=sf-dump-key>2</span> => <span class=sf-dump-const>false</span>
+  <span class=sf-dump-key>3</span> => <span class=sf-dump-num>NAN</span>
+  <span class=sf-dump-key>4</span> => <span class=sf-dump-num>INF</span>
+  <span class=sf-dump-key>5</span> => <span class=sf-dump-num>-INF</span>
+  <span class=sf-dump-key>6</span> => <span class=sf-dump-num>{$intMax}</span>
+  "<span class=sf-dump-key>str</span>" => "<span class=sf-dump-str title="5 characters">d&%s;j&%s;<span class="sf-dump-default sf-dump-ns">\\n</span></span>"
+  <span class=sf-dump-key>7</span> => b"""
+    <span class=sf-dump-str title="11 binary or non-UTF-8 characters">&eacute;<span class="sf-dump-default">\\x00</span>test<span class="sf-dump-default">\\t</span><span class="sf-dump-default sf-dump-ns">\\n</span></span>
+    <span class=sf-dump-str title="11 binary or non-UTF-8 characters">ing</span>
+    """
+  "<span class=sf-dump-key>[]</span>" => []
+  "<span class=sf-dump-key>res</span>" => <span class=sf-dump-note>stream resource</span> <a class=sf-dump-ref>@{$res}</a><samp>
+%A  <span class=sf-dump-meta>wrapper_type</span>: "<span class=sf-dump-str title="9 characters">plainfile</span>"
+    <span class=sf-dump-meta>stream_type</span>: "<span class=sf-dump-str title="5 characters">STDIO</span>"
+    <span class=sf-dump-meta>mode</span>: "<span class=sf-dump-str>r</span>"
+    <span class=sf-dump-meta>unread_bytes</span>: <span class=sf-dump-num>0</span>
+    <span class=sf-dump-meta>seekable</span>: <span class=sf-dump-const>true</span>
+%A  <span class=sf-dump-meta>options</span>: []
+  </samp>}
+  "<span class=sf-dump-key>obj</span>" => <abbr title="Symfony\Component\VarDumper\Tests\Fixture\DumbFoo" class=sf-dump-note>DumbFoo</abbr> {<a class=sf-dump-ref href=#{$dumpId}-ref2%d title="2 occurrences">#%d</a><samp id={$dumpId}-ref2%d>
+    +<span class=sf-dump-public title="Public property">foo</span>: "<span class=sf-dump-str title="3 characters">foo</span>"
+    +"<span class=sf-dump-public title="Runtime added dynamic property">bar</span>": "<span class=sf-dump-str title="3 characters">bar</span>"
+  </samp>}
+  "<span class=sf-dump-key>closure</span>" => <span class=sf-dump-note>Closure(\$a, PDO &amp;\$b = null)</span> {<a class=sf-dump-ref>#%d</a><samp>
+    <span class=sf-dump-meta>class</span>: "<span class=sf-dump-str title="Symfony\Component\VarDumper\Tests\Dumper\HtmlDumperTest
+55 characters"><span class="sf-dump-ellipsis sf-dump-ellipsis-class">Symfony\Component\VarDumper\Tests\Dumper</span><span class=sf-dump-ellipsis>\</span>HtmlDumperTest</span>"
+    <span class=sf-dump-meta>this</span>: <abbr title="Symfony\Component\VarDumper\Tests\Dumper\HtmlDumperTest" class=sf-dump-note>HtmlDumperTest</abbr> {<a class=sf-dump-ref>#%d</a> &%s;}
+    <span class=sf-dump-meta>file</span>: "<span class=sf-dump-str title="{$var['file']}
+%d characters"><span class="sf-dump-ellipsis sf-dump-ellipsis-path">%s%eVarDumper</span><span class=sf-dump-ellipsis>%e</span>Tests%eFixtures%edumb-var.php</span>"
+    <span class=sf-dump-meta>line</span>: "<span class=sf-dump-str title="%d characters">{$var['line']} to {$var['line']}</span>"
+  </samp>}
+  "<span class=sf-dump-key>line</span>" => <span class=sf-dump-num>{$var['line']}</span>
+  "<span class=sf-dump-key>nobj</span>" => <span class=sf-dump-note>array:1</span> [<samp>
+    <span class=sf-dump-index>0</span> => <a class=sf-dump-ref href=#{$dumpId}-ref03 title="2 occurrences">&amp;3</a> {<a class=sf-dump-ref href=#{$dumpId}-ref2%d title="3 occurrences">#%d</a>}
+  </samp>]
+  "<span class=sf-dump-key>recurs</span>" => <a class=sf-dump-ref href=#{$dumpId}-ref04 title="2 occurrences">&amp;4</a> <span class=sf-dump-note>array:1</span> [<samp id={$dumpId}-ref04>
+    <span class=sf-dump-index>0</span> => <a class=sf-dump-ref href=#{$dumpId}-ref04 title="2 occurrences">&amp;4</a> <span class=sf-dump-note>array:1</span> [<a class=sf-dump-ref href=#{$dumpId}-ref04 title="2 occurrences">&amp;4</a>]
+  </samp>]
+  <span class=sf-dump-key>8</span> => <a class=sf-dump-ref href=#{$dumpId}-ref01 title="2 occurrences">&amp;1</a> <span class=sf-dump-const>null</span>
+  "<span class=sf-dump-key>sobj</span>" => <abbr title="Symfony\Component\VarDumper\Tests\Fixture\DumbFoo" class=sf-dump-note>DumbFoo</abbr> {<a class=sf-dump-ref href=#{$dumpId}-ref2%d title="2 occurrences">#%d</a>}
+  "<span class=sf-dump-key>snobj</span>" => <a class=sf-dump-ref href=#{$dumpId}-ref03 title="2 occurrences">&amp;3</a> {<a class=sf-dump-ref href=#{$dumpId}-ref2%d title="3 occurrences">#%d</a>}
+  "<span class=sf-dump-key>snobj2</span>" => {<a class=sf-dump-ref href=#{$dumpId}-ref2%d title="3 occurrences">#%d</a>}
+  "<span class=sf-dump-key>file</span>" => "<span class=sf-dump-str title="%d characters">{$var['file']}</span>"
+  b"<span class=sf-dump-key>bin-key-&%s;</span>" => ""
+</samp>]
+</bar>
+
+EOTXT
+            ,
+
+            $out
+        );
+    }
+
+    public function testCharset()
+    {
+        $var = mb_convert_encoding('Словарь', 'CP1251', 'UTF-8');
+
+        $dumper = new HtmlDumper('php://output', 'CP1251');
+        $dumper->setDumpHeader('<foo></foo>');
+        $dumper->setDumpBoundaries('<bar>', '</bar>');
+        $cloner = new VarCloner();
+
+        $data = $cloner->cloneVar($var);
+        $out = $dumper->dump($data, true);
+
+        $this->assertStringMatchesFormat(
+            <<<'EOTXT'
+<foo></foo><bar>b"<span class=sf-dump-str title="7 binary or non-UTF-8 characters">&#1057;&#1083;&#1086;&#1074;&#1072;&#1088;&#1100;</span>"
+</bar>
+
+EOTXT
+            ,
+            $out
+        );
+    }
+
+    public function testAppend()
+    {
+        $out = fopen('php://memory', 'r+b');
+
+        $dumper = new HtmlDumper();
+        $dumper->setDumpHeader('<foo></foo>');
+        $dumper->setDumpBoundaries('<bar>', '</bar>');
+        $cloner = new VarCloner();
+
+        $dumper->dump($cloner->cloneVar(123), $out);
+        $dumper->dump($cloner->cloneVar(456), $out);
+
+        $out = stream_get_contents($out, -1, 0);
+
+        $this->assertSame(<<<'EOTXT'
+<foo></foo><bar><span class=sf-dump-num>123</span>
+</bar>
+<bar><span class=sf-dump-num>456</span>
+</bar>
+
+EOTXT
+            ,
+            $out
+        );
+    }
+}
diff --git a/vendor/symfony/var-dumper/Tests/Dumper/ServerDumperTest.php b/vendor/symfony/var-dumper/Tests/Dumper/ServerDumperTest.php
new file mode 100755
index 0000000..b4bef49
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Dumper/ServerDumperTest.php
@@ -0,0 +1,95 @@
+<?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\VarDumper\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Process\PhpProcess;
+use Symfony\Component\Process\Process;
+use Symfony\Component\VarDumper\Cloner\VarCloner;
+use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface;
+use Symfony\Component\VarDumper\Dumper\DataDumperInterface;
+use Symfony\Component\VarDumper\Dumper\ServerDumper;
+
+class ServerDumperTest extends TestCase
+{
+    private const VAR_DUMPER_SERVER = 'tcp://127.0.0.1:9913';
+
+    public function testDumpForwardsToWrappedDumperWhenServerIsUnavailable()
+    {
+        $wrappedDumper = $this->getMockBuilder(DataDumperInterface::class)->getMock();
+
+        $dumper = new ServerDumper(self::VAR_DUMPER_SERVER, $wrappedDumper);
+
+        $cloner = new VarCloner();
+        $data = $cloner->cloneVar('foo');
+
+        $wrappedDumper->expects($this->once())->method('dump')->with($data);
+
+        $dumper->dump($data);
+    }
+
+    public function testDump()
+    {
+        $wrappedDumper = $this->getMockBuilder(DataDumperInterface::class)->getMock();
+        $wrappedDumper->expects($this->never())->method('dump'); // test wrapped dumper is not used
+
+        $cloner = new VarCloner();
+        $data = $cloner->cloneVar('foo');
+        $dumper = new ServerDumper(self::VAR_DUMPER_SERVER, $wrappedDumper, [
+            'foo_provider' => new class() implements ContextProviderInterface {
+                public function getContext(): ?array
+                {
+                    return ['foo'];
+                }
+            },
+        ]);
+
+        $dumped = null;
+        $process = $this->getServerProcess();
+        $process->start(function ($type, $buffer) use ($process, &$dumped, $dumper, $data) {
+            if (Process::ERR === $type) {
+                $process->stop();
+                $this->fail();
+            } elseif ("READY\n" === $buffer) {
+                $dumper->dump($data);
+            } else {
+                $dumped .= $buffer;
+            }
+        });
+
+        $process->wait();
+
+        $this->assertTrue($process->isSuccessful());
+        $this->assertStringMatchesFormat(<<<'DUMP'
+(3) "foo"
+[
+  "timestamp" => %d.%d
+  "foo_provider" => [
+    (3) "foo"
+  ]
+]
+%d
+DUMP
+        , $dumped);
+    }
+
+    private function getServerProcess(): Process
+    {
+        $process = new PhpProcess(file_get_contents(__DIR__.'/../Fixtures/dump_server.php'), null, [
+            'COMPONENT_ROOT' => __DIR__.'/../../',
+            'VAR_DUMPER_SERVER' => self::VAR_DUMPER_SERVER,
+        ]);
+        $process->inheritEnvironmentVariables(true);
+
+        return $process->setTimeout(9);
+    }
+}
diff --git a/vendor/symfony/var-dumper/Tests/Fixtures/FooInterface.php b/vendor/symfony/var-dumper/Tests/Fixtures/FooInterface.php
new file mode 100755
index 0000000..172958b
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Fixtures/FooInterface.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace Symfony\Component\VarDumper\Tests\Fixtures;
+
+interface FooInterface
+{
+    /**
+     * Hello.
+     */
+    public function foo(?\stdClass $a, \stdClass $b = null);
+}
diff --git a/vendor/symfony/var-dumper/Tests/Fixtures/GeneratorDemo.php b/vendor/symfony/var-dumper/Tests/Fixtures/GeneratorDemo.php
new file mode 100755
index 0000000..e2d24c8
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Fixtures/GeneratorDemo.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace Symfony\Component\VarDumper\Tests\Fixtures;
+
+class GeneratorDemo
+{
+    public static function foo()
+    {
+        yield 1;
+    }
+
+    public function baz()
+    {
+        yield from bar();
+    }
+}
+
+function bar()
+{
+    yield from GeneratorDemo::foo();
+}
diff --git a/vendor/symfony/var-dumper/Tests/Fixtures/NotLoadableClass.php b/vendor/symfony/var-dumper/Tests/Fixtures/NotLoadableClass.php
new file mode 100755
index 0000000..d8e0cb3
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Fixtures/NotLoadableClass.php
@@ -0,0 +1,7 @@
+<?php
+
+namespace Symfony\Component\VarDumper\Tests\Fixtures;
+
+class NotLoadableClass extends NotLoadableClass
+{
+}
diff --git a/vendor/symfony/var-dumper/Tests/Fixtures/Twig.php b/vendor/symfony/var-dumper/Tests/Fixtures/Twig.php
new file mode 100755
index 0000000..8b84d82
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Fixtures/Twig.php
@@ -0,0 +1,38 @@
+<?php
+
+/* foo.twig */
+class __TwigTemplate_VarDumperFixture_u75a09 extends Twig\Template
+{
+    private $path;
+
+    public function __construct(Twig\Environment $env = null, $path = null)
+    {
+        if (null !== $env) {
+            parent::__construct($env);
+        }
+        $this->parent = false;
+        $this->blocks = [];
+        $this->path = $path;
+    }
+
+    protected function doDisplay(array $context, array $blocks = [])
+    {
+        // line 2
+        throw new \Exception('Foobar');
+    }
+
+    public function getTemplateName()
+    {
+        return 'foo.twig';
+    }
+
+    public function getDebugInfo()
+    {
+        return [20 => 1, 21 => 2];
+    }
+
+    public function getSourceContext()
+    {
+        return new Twig\Source("   foo bar\n     twig source\n\n", 'foo.twig', $this->path ?: __FILE__);
+    }
+}
diff --git a/vendor/symfony/var-dumper/Tests/Fixtures/dumb-var.php b/vendor/symfony/var-dumper/Tests/Fixtures/dumb-var.php
new file mode 100755
index 0000000..dcce237
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Fixtures/dumb-var.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace Symfony\Component\VarDumper\Tests\Fixture;
+
+if (!class_exists('Symfony\Component\VarDumper\Tests\Fixture\DumbFoo')) {
+    class DumbFoo
+    {
+        public $foo = 'foo';
+    }
+}
+
+$foo = new DumbFoo();
+$foo->bar = 'bar';
+
+$g = fopen(__FILE__, 'r');
+
+$var = [
+    'number' => 1, null,
+    'const' => 1.1, true, false, NAN, INF, -INF, PHP_INT_MAX,
+    'str' => "déjà\n", "\xE9\x00test\t\ning",
+    '[]' => [],
+    'res' => $g,
+    'obj' => $foo,
+    'closure' => function ($a, \PDO &$b = null) {},
+    'line' => __LINE__ - 1,
+    'nobj' => [(object) []],
+];
+
+$r = [];
+$r[] = &$r;
+
+$var['recurs'] = &$r;
+$var[] = &$var[0];
+$var['sobj'] = $var['obj'];
+$var['snobj'] = &$var['nobj'][0];
+$var['snobj2'] = $var['nobj'][0];
+$var['file'] = __FILE__;
+$var["bin-key-\xE9"] = '';
+
+unset($g, $r);
diff --git a/vendor/symfony/var-dumper/Tests/Fixtures/dump_server.php b/vendor/symfony/var-dumper/Tests/Fixtures/dump_server.php
new file mode 100755
index 0000000..ed8bbfb
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Fixtures/dump_server.php
@@ -0,0 +1,38 @@
+<?php
+
+use Symfony\Component\VarDumper\Cloner\Data;
+use Symfony\Component\VarDumper\Cloner\VarCloner;
+use Symfony\Component\VarDumper\Dumper\CliDumper;
+use Symfony\Component\VarDumper\Server\DumpServer;
+use Symfony\Component\VarDumper\VarDumper;
+
+$componentRoot = $_SERVER['COMPONENT_ROOT'];
+
+if (!is_file($file = $componentRoot.'/vendor/autoload.php')) {
+    $file = $componentRoot.'/../../../../vendor/autoload.php';
+}
+
+require $file;
+
+$cloner = new VarCloner();
+$cloner->setMaxItems(-1);
+
+$dumper = new CliDumper(null, null, CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_STRING_LENGTH);
+$dumper->setColors(false);
+
+VarDumper::setHandler(function ($var) use ($cloner, $dumper) {
+    $data = $cloner->cloneVar($var)->withRefHandles(false);
+    $dumper->dump($data);
+});
+
+$server = new DumpServer(getenv('VAR_DUMPER_SERVER'));
+
+$server->start();
+
+echo "READY\n";
+
+$server->listen(function (Data $data, array $context, $clientId) {
+    dump((string) $data, $context, $clientId);
+
+    exit(0);
+});
diff --git a/vendor/symfony/var-dumper/Tests/Fixtures/xml_reader.xml b/vendor/symfony/var-dumper/Tests/Fixtures/xml_reader.xml
new file mode 100755
index 0000000..740c399
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Fixtures/xml_reader.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<foo>
+    <bar></bar>
+    <bar />
+    <bar>With text</bar>
+    <bar foo="bar" baz="fubar"></bar>
+    <bar xmlns:baz="http://symfony.com">
+        <baz:baz></baz:baz>
+    </bar>
+</foo>
diff --git a/vendor/symfony/var-dumper/Tests/Server/ConnectionTest.php b/vendor/symfony/var-dumper/Tests/Server/ConnectionTest.php
new file mode 100755
index 0000000..21902b5
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Server/ConnectionTest.php
@@ -0,0 +1,88 @@
+<?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\VarDumper\Tests\Server;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Process\PhpProcess;
+use Symfony\Component\Process\Process;
+use Symfony\Component\VarDumper\Cloner\VarCloner;
+use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface;
+use Symfony\Component\VarDumper\Server\Connection;
+
+class ConnectionTest extends TestCase
+{
+    private const VAR_DUMPER_SERVER = 'tcp://127.0.0.1:9913';
+
+    public function testDump()
+    {
+        $cloner = new VarCloner();
+        $data = $cloner->cloneVar('foo');
+        $connection = new Connection(self::VAR_DUMPER_SERVER, [
+            'foo_provider' => new class() implements ContextProviderInterface {
+                public function getContext(): ?array
+                {
+                    return ['foo'];
+                }
+            },
+        ]);
+
+        $dumped = null;
+        $process = $this->getServerProcess();
+        $process->start(function ($type, $buffer) use ($process, &$dumped, $connection, $data) {
+            if (Process::ERR === $type) {
+                $process->stop();
+                $this->fail();
+            } elseif ("READY\n" === $buffer) {
+                $connection->write($data);
+            } else {
+                $dumped .= $buffer;
+            }
+        });
+
+        $process->wait();
+
+        $this->assertTrue($process->isSuccessful());
+        $this->assertStringMatchesFormat(<<<'DUMP'
+(3) "foo"
+[
+  "timestamp" => %d.%d
+  "foo_provider" => [
+    (3) "foo"
+  ]
+]
+%d
+
+DUMP
+        , $dumped);
+    }
+
+    public function testNoServer()
+    {
+        $cloner = new VarCloner();
+        $data = $cloner->cloneVar('foo');
+        $connection = new Connection(self::VAR_DUMPER_SERVER);
+        $start = microtime(true);
+        $this->assertFalse($connection->write($data));
+        $this->assertLessThan(1, microtime(true) - $start);
+    }
+
+    private function getServerProcess(): Process
+    {
+        $process = new PhpProcess(file_get_contents(__DIR__.'/../Fixtures/dump_server.php'), null, [
+            'COMPONENT_ROOT' => __DIR__.'/../../',
+            'VAR_DUMPER_SERVER' => self::VAR_DUMPER_SERVER,
+        ]);
+        $process->inheritEnvironmentVariables(true);
+
+        return $process->setTimeout(9);
+    }
+}
diff --git a/vendor/symfony/var-dumper/Tests/Test/VarDumperTestTraitTest.php b/vendor/symfony/var-dumper/Tests/Test/VarDumperTestTraitTest.php
new file mode 100755
index 0000000..a4d489c
--- /dev/null
+++ b/vendor/symfony/var-dumper/Tests/Test/VarDumperTestTraitTest.php
@@ -0,0 +1,46 @@
+<?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\VarDumper\Tests\Test;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
+
+class VarDumperTestTraitTest extends TestCase
+{
+    use VarDumperTestTrait;
+
+    public function testItComparesLargeData()
+    {
+        $howMany = 700;
+        $data = array_fill_keys(range(0, $howMany), ['a', 'b', 'c', 'd']);
+
+        $expected = sprintf("array:%d [\n", $howMany + 1);
+        for ($i = 0; $i <= $howMany; ++$i) {
+            $expected .= <<<EODUMP
+  $i => array:4 [
+    0 => "a"
+    1 => "b"
+    2 => "c"
+    3 => "d"
+  ]\n
+EODUMP;
+        }
+        $expected .= "]\n";
+
+        $this->assertDumpEquals($expected, $data);
+    }
+
+    public function testAllowsNonScalarExpectation()
+    {
+        $this->assertDumpEquals(new \ArrayObject(['bim' => 'bam']), new \ArrayObject(['bim' => 'bam']));
+    }
+}
diff --git a/vendor/symfony/var-dumper/VarDumper.php b/vendor/symfony/var-dumper/VarDumper.php
new file mode 100755
index 0000000..009f662
--- /dev/null
+++ b/vendor/symfony/var-dumper/VarDumper.php
@@ -0,0 +1,56 @@
+<?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\VarDumper;
+
+use Symfony\Component\VarDumper\Caster\ReflectionCaster;
+use Symfony\Component\VarDumper\Cloner\VarCloner;
+use Symfony\Component\VarDumper\Dumper\CliDumper;
+use Symfony\Component\VarDumper\Dumper\HtmlDumper;
+
+// Load the global dump() function
+require_once __DIR__.'/Resources/functions/dump.php';
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class VarDumper
+{
+    private static $handler;
+
+    public static function dump($var)
+    {
+        if (null === self::$handler) {
+            $cloner = new VarCloner();
+            $cloner->addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO);
+
+            if (isset($_SERVER['VAR_DUMPER_FORMAT'])) {
+                $dumper = 'html' === $_SERVER['VAR_DUMPER_FORMAT'] ? new HtmlDumper() : new CliDumper();
+            } else {
+                $dumper = \in_array(\PHP_SAPI, ['cli', 'phpdbg']) ? new CliDumper() : new HtmlDumper();
+            }
+
+            self::$handler = function ($var) use ($cloner, $dumper) {
+                $dumper->dump($cloner->cloneVar($var));
+            };
+        }
+
+        return (self::$handler)($var);
+    }
+
+    public static function setHandler(callable $callable = null)
+    {
+        $prevHandler = self::$handler;
+        self::$handler = $callable;
+
+        return $prevHandler;
+    }
+}
diff --git a/vendor/symfony/var-dumper/composer.json b/vendor/symfony/var-dumper/composer.json
new file mode 100755
index 0000000..b0c0273
--- /dev/null
+++ b/vendor/symfony/var-dumper/composer.json
@@ -0,0 +1,54 @@
+{
+    "name": "symfony/var-dumper",
+    "type": "library",
+    "description": "Symfony mechanism for exploring and dumping PHP variables",
+    "keywords": ["dump", "debug"],
+    "homepage": "https://symfony.com",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Nicolas Grekas",
+            "email": "p@tchwork.com"
+        },
+        {
+            "name": "Symfony Community",
+            "homepage": "https://symfony.com/contributors"
+        }
+    ],
+    "require": {
+        "php": "^7.1.3",
+        "symfony/polyfill-mbstring": "~1.0",
+        "symfony/polyfill-php72": "~1.5"
+    },
+    "require-dev": {
+        "ext-iconv": "*",
+        "symfony/console": "~3.4|~4.0",
+        "symfony/process": "~3.4|~4.0",
+        "twig/twig": "~1.34|~2.4"
+    },
+    "conflict": {
+        "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0",
+        "symfony/console": "<3.4"
+    },
+    "suggest": {
+        "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).",
+        "ext-intl": "To show region name in time zone dump",
+        "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script"
+    },
+    "autoload": {
+        "files": [ "Resources/functions/dump.php" ],
+        "psr-4": { "Symfony\\Component\\VarDumper\\": "" },
+        "exclude-from-classmap": [
+            "/Tests/"
+        ]
+    },
+    "bin": [
+        "Resources/bin/var-dump-server"
+    ],
+    "minimum-stability": "dev",
+    "extra": {
+        "branch-alias": {
+            "dev-master": "4.3-dev"
+        }
+    }
+}
diff --git a/vendor/symfony/var-dumper/phpunit.xml.dist b/vendor/symfony/var-dumper/phpunit.xml.dist
new file mode 100755
index 0000000..3243fcd
--- /dev/null
+++ b/vendor/symfony/var-dumper/phpunit.xml.dist
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/5.2/phpunit.xsd"
+         backupGlobals="false"
+         colors="true"
+         bootstrap="vendor/autoload.php"
+         failOnRisky="true"
+         failOnWarning="true"
+>
+    <php>
+        <ini name="error_reporting" value="-1" />
+        <env name="DUMP_LIGHT_ARRAY" value="" />
+        <env name="DUMP_STRING_LENGTH" value="" />
+    </php>
+
+    <testsuites>
+        <testsuite name="Symfony VarDumper Component Test Suite">
+            <directory>./Tests/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./</directory>
+            <exclude>
+                <directory>./Resources</directory>
+                <directory>./Tests</directory>
+                <directory>./vendor</directory>
+            </exclude>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/vendor/topthink/framework/.gitignore b/vendor/topthink/framework/.gitignore
new file mode 100755
index 0000000..b2fd3b9
--- /dev/null
+++ b/vendor/topthink/framework/.gitignore
@@ -0,0 +1,10 @@
+/vendor
+composer.phar
+composer.lock
+.DS_Store
+Thumbs.db
+/.idea
+/.vscode
+/.settings
+/.buildpath
+/.project
\ No newline at end of file
diff --git a/vendor/topthink/framework/.travis.yml b/vendor/topthink/framework/.travis.yml
new file mode 100755
index 0000000..abaa271
--- /dev/null
+++ b/vendor/topthink/framework/.travis.yml
@@ -0,0 +1,35 @@
+dist: xenial
+language: php
+
+matrix:
+  fast_finish: true
+  include:
+    - php: 7.2
+    - php: 7.3
+    - php: 8.0
+
+cache:
+  directories:
+    - $HOME/.composer/cache
+
+services:
+  - memcached
+  - redis-server
+  - mysql
+
+before_install:
+  - echo "extension = memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
+  - echo 'xdebug.mode = coverage' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
+  - printf "\n" | pecl install -f redis
+  - travis_retry composer self-update
+  - mysql -e 'CREATE DATABASE test;'
+
+install:
+  - travis_retry composer update --prefer-dist --no-interaction --prefer-stable --no-suggest
+
+script:
+  - vendor/bin/phpunit --coverage-clover build/logs/coverage.xml
+
+after_script:
+  - travis_retry wget https://scrutinizer-ci.com/ocular.phar
+  - php ocular.phar code-coverage:upload --format=php-clover build/logs/coverage.xml
diff --git a/vendor/topthink/framework/CONTRIBUTING.md b/vendor/topthink/framework/CONTRIBUTING.md
new file mode 100755
index 0000000..efa3ad9
--- /dev/null
+++ b/vendor/topthink/framework/CONTRIBUTING.md
@@ -0,0 +1,119 @@
+如何贡献我的源代码
+===
+
+此文档介绍了 ThinkPHP 团队的组成以及运转机制,您提交的代码将给 ThinkPHP 项目带来什么好处,以及如何才能加入我们的行列。
+
+## 通过 Github 贡献代码
+
+ThinkPHP 目前使用 Git 来控制程序版本,如果你想为 ThinkPHP 贡献源代码,请先大致了解 Git 的使用方法。我们目前把项目托管在 GitHub 上,任何 GitHub 用户都可以向我们贡献代码。
+
+参与的方式很简单,`fork`一份 ThinkPHP 的代码到你的仓库中,修改后提交,并向我们发起`pull request`申请,我们会及时对代码进行审查并处理你的申请并。审查通过后,你的代码将被`merge`进我们的仓库中,这样你就会自动出现在贡献者名单里了,非常方便。
+
+我们希望你贡献的代码符合:
+
+* ThinkPHP 的编码规范
+* 适当的注释,能让其他人读懂
+* 遵循 Apache2 开源协议
+
+**如果想要了解更多细节或有任何疑问,请继续阅读下面的内容**
+
+### 注意事项
+
+* 本项目代码格式化标准选用 [**PSR-2**](http://www.kancloud.cn/thinkphp/php-fig-psr/3141);
+* 类名和类文件名遵循 [**PSR-4**](http://www.kancloud.cn/thinkphp/php-fig-psr/3144);
+* 对于 Issues 的处理,请使用诸如 `fix #xxx(Issue ID)` 的 commit title 直接关闭 issue。
+* 系统会自动在 PHP 7.1 ~ 7.3 上测试修改,请确保你的修改符合 PHP 7.1 ~ 7.3 的语法规范;
+* 管理员不会合并造成 CI faild 的修改,若出现 CI faild 请检查自己的源代码或修改相应的[单元测试文件](tests);
+
+## GitHub Issue
+
+GitHub 提供了 Issue 功能,该功能可以用于:
+
+* 提出 bug
+* 提出功能改进
+* 反馈使用体验
+
+该功能不应该用于:
+
+ * 提出修改意见(涉及代码署名和修订追溯问题)
+ * 不友善的言论
+
+## 快速修改
+
+**GitHub 提供了快速编辑文件的功能**
+
+1. 登录 GitHub 帐号;
+2. 浏览项目文件,找到要进行修改的文件;
+3. 点击右上角铅笔图标进行修改;
+4. 填写 `Commit changes` 相关内容(Title 必填);
+5. 提交修改,等待 CI 验证和管理员合并。
+
+**若您需要一次提交大量修改,请继续阅读下面的内容**
+
+## 完整流程
+
+1. `fork`本项目;
+2. 克隆(`clone`)你 `fork` 的项目到本地;
+3. 新建分支(`branch`)并检出(`checkout`)新分支;
+4. 添加本项目到你的本地 git 仓库作为上游(`upstream`);
+5. 进行修改,若你的修改包含方法或函数的增减,请记得修改[单元测试文件](tests);
+6. 变基(衍合 `rebase`)你的分支到上游 master 分支;
+7. `push` 你的本地仓库到 GitHub;
+8. 提交 `pull request`;
+9. 等待 CI 验证(若不通过则重复 5~7,GitHub 会自动更新你的 `pull request`);
+10. 等待管理员处理,并及时 `rebase` 你的分支到上游 master 分支(若上游 master 分支有修改)。
+
+*若有必要,可以 `git push -f` 强行推送 rebase 后的分支到自己的 `fork`*
+
+*绝对不可以使用 `git push -f` 强行推送修改到上游*
+
+### 注意事项
+
+* 若对上述流程有任何不清楚的地方,请查阅 GIT 教程,如 [这个](http://backlogtool.com/git-guide/cn/);
+* 对于代码**不同方面**的修改,请在自己 `fork` 的项目中**创建不同的分支**(原因参见`完整流程`第9条备注部分);
+* 变基及交互式变基操作参见 [Git 交互式变基](http://pakchoi.me/2015/03/17/git-interactive-rebase/)
+
+## 推荐资源
+
+### 开发环境
+
+* XAMPP for Windows 5.5.x
+* WampServer (for Windows)
+* upupw Apache PHP5.4 ( for Windows)
+
+或自行安装
+
+- Apache / Nginx
+- PHP 7.1 ~ 7.3
+- MySQL / MariaDB
+
+*Windows 用户推荐添加 PHP bin 目录到 PATH,方便使用 composer*
+
+*Linux 用户自行配置环境, Mac 用户推荐使用内置 Apache 配合 Homebrew 安装 PHP 和 MariaDB*
+
+### 编辑器
+
+Sublime Text 3 + phpfmt 插件
+
+phpfmt 插件参数
+
+```json
+{
+	"autocomplete": true,
+	"enable_auto_align": true,
+	"format_on_save": true,
+	"indent_with_space": true,
+	"psr1_naming": false,
+	"psr2": true,
+	"version": 4
+}
+```
+
+或其他 编辑器 / IDE 配合 PSR2 自动格式化工具
+
+### Git GUI
+
+* SourceTree
+* GitHub Desktop
+
+或其他 Git 图形界面客户端
diff --git a/vendor/topthink/framework/LICENSE.txt b/vendor/topthink/framework/LICENSE.txt
new file mode 100755
index 0000000..4e910bb
--- /dev/null
+++ b/vendor/topthink/framework/LICENSE.txt
@@ -0,0 +1,32 @@
+
+ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
+版权所有Copyright © 2006-2019 by ThinkPHP (http://thinkphp.cn)
+All rights reserved。
+ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
+
+Apache Licence是著名的非盈利开源组织Apache采用的协议。
+该协议和BSD类似,鼓励代码共享和尊重原作者的著作权,
+允许代码修改,再作为开源或商业软件发布。需要满足
+的条件: 
+1. 需要给代码的用户一份Apache Licence ;
+2. 如果你修改了代码,需要在被修改的文件中说明;
+3. 在延伸的代码中(修改和有源代码衍生的代码中)需要
+带有原来代码中的协议,商标,专利声明和其他原来作者规
+定需要包含的说明;
+4. 如果再发布的产品中包含一个Notice文件,则在Notice文
+件中需要带有本协议内容。你可以在Notice中增加自己的
+许可,但不可以表现为对Apache Licence构成更改。 
+具体的协议参考:http://www.apache.org/licenses/LICENSE-2.0
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/topthink/framework/README.md b/vendor/topthink/framework/README.md
new file mode 100755
index 0000000..dd38e34
--- /dev/null
+++ b/vendor/topthink/framework/README.md
@@ -0,0 +1,84 @@
+![](https://box.kancloud.cn/5a0aaa69a5ff42657b5c4715f3d49221) 
+
+ThinkPHP 6.1
+===============
+
+[![Build Status](https://travis-ci.org/top-think/framework.svg?branch=6.0)](https://travis-ci.org/top-think/framework)
+[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/top-think/framework/badges/quality-score.png?b=6.0)](https://scrutinizer-ci.com/g/top-think/framework/?branch=6.0)
+[![Code Coverage](https://scrutinizer-ci.com/g/top-think/framework/badges/coverage.png?b=6.0)](https://scrutinizer-ci.com/g/top-think/framework/?branch=6.0)
+[![Total Downloads](https://poser.pugx.org/topthink/framework/downloads)](https://packagist.org/packages/topthink/framework)
+[![Latest Stable Version](https://poser.pugx.org/topthink/framework/v/stable)](https://packagist.org/packages/topthink/framework)
+[![PHP Version](https://img.shields.io/badge/php-%3E%3D7.1-8892BF.svg)](http://www.php.net/)
+[![License](https://poser.pugx.org/topthink/framework/license)](https://packagist.org/packages/topthink/framework)
+
+
+[官方服务](https://www.topthink.com) | [`ThinkAPI`——官方统一API](https://doc.topthink.com/think-api)
+
+## 主要新特性
+
+* 采用`PHP7`强类型(严格模式)
+* 支持更多的`PSR`规范
+* 原生多应用支持
+* 系统服务注入支持
+* ORM作为独立组件使用
+* 全新的事件系统
+* 模板引擎分离出核心
+* 内部功能中间件化
+* SESSION机制改进
+* 日志多通道支持
+* 规范扩展接口
+* 更强大的控制台
+* 对Swoole以及协程支持改进
+* 对IDE更加友好
+* 统一和精简大量用法
+
+
+> ThinkPHP6.1的运行环境要求PHP7.2.5+,最高兼容PHP8.2
+
+## 安装
+
+~~~
+composer create-project topthink/think tp
+~~~
+
+启动服务
+
+~~~
+cd tp
+php think run
+~~~
+
+然后就可以在浏览器中访问
+
+~~~
+http://localhost:8000
+~~~
+
+如果需要更新框架使用
+~~~
+composer update topthink/framework
+~~~
+
+## 文档
+
+[完全开发手册](https://www.kancloud.cn/manual/thinkphp6_0/content)
+
+## 命名规范
+
+`ThinkPHP6`遵循PSR-2命名规范和PSR-4自动加载规范。
+
+## 参与开发
+
+直接提交PR或者Issue即可
+
+## 版权信息
+
+ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
+
+本项目包含的第三方源码和二进制文件之版权信息另行标注。
+
+版权所有Copyright © 2006-2021 by ThinkPHP (http://thinkphp.cn) All rights reserved。
+
+ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
+
+更多细节参阅 [LICENSE.txt](LICENSE.txt)
diff --git a/vendor/topthink/framework/composer.json b/vendor/topthink/framework/composer.json
new file mode 100755
index 0000000..2d9685e
--- /dev/null
+++ b/vendor/topthink/framework/composer.json
@@ -0,0 +1,54 @@
+{
+    "name": "topthink/framework",
+    "description": "The ThinkPHP Framework.",
+    "keywords": [
+        "framework",
+        "thinkphp",
+        "ORM"
+    ],
+    "homepage": "http://thinkphp.cn/",
+    "license": "Apache-2.0",
+    "authors": [
+        {
+            "name": "liu21st",
+            "email": "liu21st@gmail.com"
+        },
+        {
+            "name": "yunwuxin",
+            "email": "448901948@qq.com"
+        }
+    ],
+    "require": {
+        "php": ">=7.2.5",
+        "ext-json": "*",
+        "ext-mbstring": "*",
+        "psr/log": "~1.0",
+        "psr/container": "~1.0",
+        "psr/simple-cache": "^1.0",
+        "psr/http-message": "^1.0",
+        "topthink/think-orm": "^2.0|^3.0",
+        "topthink/think-helper": "^3.1.1"
+    },
+    "require-dev": {
+        "mikey179/vfsstream": "^1.6",
+        "mockery/mockery": "^1.2",
+        "phpunit/phpunit": "^7.0",
+        "guzzlehttp/psr7": "^2.1.0"
+    },
+    "autoload": {
+        "files": [],
+        "psr-4": {
+            "think\\": "src/think/"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "think\\tests\\": "tests/"
+        }
+    },
+    "minimum-stability": "dev",
+    "prefer-stable": true,
+    "config": {
+        "sort-packages": true
+    }
+}
diff --git a/vendor/topthink/framework/logo.png b/vendor/topthink/framework/logo.png
new file mode 100755
index 0000000..25fd059
--- /dev/null
+++ b/vendor/topthink/framework/logo.png
Binary files differ
diff --git a/vendor/topthink/framework/phpunit.xml.dist b/vendor/topthink/framework/phpunit.xml.dist
new file mode 100755
index 0000000..e20a133
--- /dev/null
+++ b/vendor/topthink/framework/phpunit.xml.dist
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit backupGlobals="false"
+         backupStaticAttributes="false"
+         beStrictAboutTestsThatDoNotTestAnything="false"
+         bootstrap="tests/bootstrap.php"
+         colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         processIsolation="false"
+         stopOnError="false"
+         stopOnFailure="false"
+         verbose="true"
+>
+    <testsuites>
+        <testsuite name="ThinkPHP Test Suite">
+            <directory suffix="Test.php">./tests</directory>
+        </testsuite>
+    </testsuites>
+    <filter>
+        <whitelist processUncoveredFilesFromWhitelist="true">
+            <directory suffix=".php">./src/think</directory>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/vendor/topthink/framework/src/helper.php b/vendor/topthink/framework/src/helper.php
new file mode 100755
index 0000000..caba0ae
--- /dev/null
+++ b/vendor/topthink/framework/src/helper.php
@@ -0,0 +1,664 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+//------------------------
+// ThinkPHP 助手函数
+//-------------------------
+
+use think\App;
+use think\Container;
+use think\exception\HttpException;
+use think\exception\HttpResponseException;
+use think\facade\Cache;
+use think\facade\Config;
+use think\facade\Cookie;
+use think\facade\Env;
+use think\facade\Event;
+use think\facade\Lang;
+use think\facade\Log;
+use think\facade\Request;
+use think\facade\Route;
+use think\facade\Session;
+use think\Response;
+use think\response\File;
+use think\response\Json;
+use think\response\Jsonp;
+use think\response\Redirect;
+use think\response\View;
+use think\response\Xml;
+use think\route\Url as UrlBuild;
+use think\Validate;
+
+if (!function_exists('abort')) {
+    /**
+     * 抛出HTTP异常
+     * @param integer|Response $code    状态码 或者 Response对象实例
+     * @param string           $message 错误信息
+     * @param array            $header  参数
+     */
+    function abort($code, string $message = '', array $header = [])
+    {
+        if ($code instanceof Response) {
+            throw new HttpResponseException($code);
+        } else {
+            throw new HttpException($code, $message, null, $header);
+        }
+    }
+}
+
+if (!function_exists('app')) {
+    /**
+     * 快速获取容器中的实例 支持依赖注入
+     * @template T
+     * @param string|class-string<T> $name        类名或标识 默认获取当前应用实例
+     * @param array                  $args        参数
+     * @param bool                   $newInstance 是否每次创建新的实例
+     * @return T|object|App
+     */
+    function app(string $name = '', array $args = [], bool $newInstance = false)
+    {
+        return Container::getInstance()->make($name ?: App::class, $args, $newInstance);
+    }
+}
+
+if (!function_exists('bind')) {
+    /**
+     * 绑定一个类到容器
+     * @param string|array $abstract 类标识、接口(支持批量绑定)
+     * @param mixed        $concrete 要绑定的类、闭包或者实例
+     * @return Container
+     */
+    function bind($abstract, $concrete = null)
+    {
+        return Container::getInstance()->bind($abstract, $concrete);
+    }
+}
+
+if (!function_exists('cache')) {
+    /**
+     * 缓存管理
+     * @param string $name    缓存名称
+     * @param mixed  $value   缓存值
+     * @param mixed  $options 缓存参数
+     * @param string $tag     缓存标签
+     * @return mixed
+     */
+    function cache(string $name = null, $value = '', $options = null, $tag = null)
+    {
+        if (is_null($name)) {
+            return app('cache');
+        }
+
+        if ('' === $value) {
+            // 获取缓存
+            return 0 === strpos($name, '?') ? Cache::has(substr($name, 1)) : Cache::get($name);
+        } elseif (is_null($value)) {
+            // 删除缓存
+            return Cache::delete($name);
+        }
+
+        // 缓存数据
+        if (is_array($options)) {
+            $expire = $options['expire'] ?? null; //修复查询缓存无法设置过期时间
+        } else {
+            $expire = $options;
+        }
+
+        if (is_null($tag)) {
+            return Cache::set($name, $value, $expire);
+        } else {
+            return Cache::tag($tag)->set($name, $value, $expire);
+        }
+    }
+}
+
+if (!function_exists('config')) {
+    /**
+     * 获取和设置配置参数
+     * @param string|array $name  参数名
+     * @param mixed        $value 参数值
+     * @return mixed
+     */
+    function config($name = '', $value = null)
+    {
+        if (is_array($name)) {
+            return Config::set($name, $value);
+        }
+
+        return 0 === strpos($name, '?') ? Config::has(substr($name, 1)) : Config::get($name, $value);
+    }
+}
+
+if (!function_exists('cookie')) {
+    /**
+     * Cookie管理
+     * @param string $name   cookie名称
+     * @param mixed  $value  cookie值
+     * @param mixed  $option 参数
+     * @return mixed
+     */
+    function cookie(string $name, $value = '', $option = null)
+    {
+        if (is_null($value)) {
+            // 删除
+            Cookie::delete($name, $option ?: []);
+        } elseif ('' === $value) {
+            // 获取
+            return 0 === strpos($name, '?') ? Cookie::has(substr($name, 1)) : Cookie::get($name);
+        } else {
+            // 设置
+            return Cookie::set($name, $value, $option);
+        }
+    }
+}
+
+if (!function_exists('download')) {
+    /**
+     * 获取\think\response\Download对象实例
+     * @param string $filename 要下载的文件
+     * @param string $name     显示文件名
+     * @param bool   $content  是否为内容
+     * @param int    $expire   有效期(秒)
+     * @return \think\response\File
+     */
+    function download(string $filename, string $name = '', bool $content = false, int $expire = 180): File
+    {
+        return Response::create($filename, 'file')->name($name)->isContent($content)->expire($expire);
+    }
+}
+
+if (!function_exists('dump')) {
+    /**
+     * 浏览器友好的变量输出
+     * @param mixed $vars 要输出的变量
+     * @return void
+     */
+    function dump(...$vars)
+    {
+        ob_start();
+        var_dump(...$vars);
+
+        $output = ob_get_clean();
+        $output = preg_replace('/\]\=\>\n(\s+)/m', '] => ', $output);
+
+        if (PHP_SAPI == 'cli') {
+            $output = PHP_EOL . $output . PHP_EOL;
+        } else {
+            if (!extension_loaded('xdebug')) {
+                $output = htmlspecialchars($output, ENT_SUBSTITUTE);
+            }
+            $output = '<pre>' . $output . '</pre>';
+        }
+
+        echo $output;
+    }
+}
+
+if (!function_exists('env')) {
+    /**
+     * 获取环境变量值
+     * @access public
+     * @param string $name    环境变量名(支持二级 .号分割)
+     * @param string $default 默认值
+     * @return mixed
+     */
+    function env(string $name = null, $default = null)
+    {
+        return Env::get($name, $default);
+    }
+}
+
+if (!function_exists('event')) {
+    /**
+     * 触发事件
+     * @param mixed $event 事件名(或者类名)
+     * @param mixed $args  参数
+     * @return mixed
+     */
+    function event($event, $args = null)
+    {
+        return Event::trigger($event, $args);
+    }
+}
+
+if (!function_exists('halt')) {
+    /**
+     * 调试变量并且中断输出
+     * @param mixed $vars 调试变量或者信息
+     */
+    function halt(...$vars)
+    {
+        dump(...$vars);
+
+        throw new HttpResponseException(Response::create());
+    }
+}
+
+if (!function_exists('input')) {
+    /**
+     * 获取输入数据 支持默认值和过滤
+     * @param string $key     获取的变量名
+     * @param mixed  $default 默认值
+     * @param string $filter  过滤方法
+     * @return mixed
+     */
+    function input(string $key = '', $default = null, $filter = '')
+    {
+        if (0 === strpos($key, '?')) {
+            $key = substr($key, 1);
+            $has = true;
+        }
+
+        if ($pos = strpos($key, '.')) {
+            // 指定参数来源
+            $method = substr($key, 0, $pos);
+            if (in_array($method, ['get', 'post', 'put', 'patch', 'delete', 'route', 'param', 'request', 'session', 'cookie', 'server', 'env', 'path', 'file'])) {
+                $key = substr($key, $pos + 1);
+                if ('server' == $method && is_null($default)) {
+                    $default = '';
+                }
+            } else {
+                $method = 'param';
+            }
+        } else {
+            // 默认为自动判断
+            $method = 'param';
+        }
+
+        return isset($has) ?
+        request()->has($key, $method) :
+        request()->$method($key, $default, $filter);
+    }
+}
+
+if (!function_exists('invoke')) {
+    /**
+     * 调用反射实例化对象或者执行方法 支持依赖注入
+     * @param mixed $call 类名或者callable
+     * @param array $args 参数
+     * @return mixed
+     */
+    function invoke($call, array $args = [])
+    {
+        if (is_callable($call)) {
+            return Container::getInstance()->invoke($call, $args);
+        }
+
+        return Container::getInstance()->invokeClass($call, $args);
+    }
+}
+
+if (!function_exists('json')) {
+    /**
+     * 获取\think\response\Json对象实例
+     * @param mixed $data    返回的数据
+     * @param int   $code    状态码
+     * @param array $header  头部
+     * @param array $options 参数
+     * @return \think\response\Json
+     */
+    function json($data = [], $code = 200, $header = [], $options = []): Json
+    {
+        return Response::create($data, 'json', $code)->header($header)->options($options);
+    }
+}
+
+if (!function_exists('jsonp')) {
+    /**
+     * 获取\think\response\Jsonp对象实例
+     * @param mixed $data    返回的数据
+     * @param int   $code    状态码
+     * @param array $header  头部
+     * @param array $options 参数
+     * @return \think\response\Jsonp
+     */
+    function jsonp($data = [], $code = 200, $header = [], $options = []): Jsonp
+    {
+        return Response::create($data, 'jsonp', $code)->header($header)->options($options);
+    }
+}
+
+if (!function_exists('lang')) {
+    /**
+     * 获取语言变量值
+     * @param string $name 语言变量名
+     * @param array  $vars 动态变量值
+     * @param string $lang 语言
+     * @return mixed
+     */
+    function lang(string $name, array $vars = [], string $lang = '')
+    {
+        return Lang::get($name, $vars, $lang);
+    }
+}
+
+if (!function_exists('parse_name')) {
+    /**
+     * 字符串命名风格转换
+     * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格
+     * @param string $name    字符串
+     * @param int    $type    转换类型
+     * @param bool   $ucfirst 首字母是否大写(驼峰规则)
+     * @return string
+     */
+    function parse_name(string $name, int $type = 0, bool $ucfirst = true): string
+    {
+        if ($type) {
+            $name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {
+                return strtoupper($match[1]);
+            }, $name);
+
+            return $ucfirst ? ucfirst($name) : lcfirst($name);
+        }
+
+        return strtolower(trim(preg_replace('/[A-Z]/', '_\\0', $name), '_'));
+    }
+}
+
+if (!function_exists('redirect')) {
+    /**
+     * 获取\think\response\Redirect对象实例
+     * @param string $url  重定向地址
+     * @param int    $code 状态码
+     * @return \think\response\Redirect
+     */
+    function redirect(string $url = '', int $code = 302): Redirect
+    {
+        return Response::create($url, 'redirect', $code);
+    }
+}
+
+if (!function_exists('request')) {
+    /**
+     * 获取当前Request对象实例
+     * @return Request
+     */
+    function request(): \think\Request
+    {
+        return app('request');
+    }
+}
+
+if (!function_exists('response')) {
+    /**
+     * 创建普通 Response 对象实例
+     * @param mixed      $data   输出数据
+     * @param int|string $code   状态码
+     * @param array      $header 头信息
+     * @param string     $type
+     * @return Response
+     */
+    function response($data = '', $code = 200, $header = [], $type = 'html'): Response
+    {
+        return Response::create($data, $type, $code)->header($header);
+    }
+}
+
+if (!function_exists('session')) {
+    /**
+     * Session管理
+     * @param string $name  session名称
+     * @param mixed  $value session值
+     * @return mixed
+     */
+    function session($name = '', $value = '')
+    {
+        if (is_null($name)) {
+            // 清除
+            Session::clear();
+        } elseif ('' === $name) {
+            return Session::all();
+        } elseif (is_null($value)) {
+            // 删除
+            Session::delete($name);
+        } elseif ('' === $value) {
+            // 判断或获取
+            return 0 === strpos($name, '?') ? Session::has(substr($name, 1)) : Session::get($name);
+        } else {
+            // 设置
+            Session::set($name, $value);
+        }
+    }
+}
+
+if (!function_exists('token')) {
+    /**
+     * 获取Token令牌
+     * @param string $name 令牌名称
+     * @param mixed  $type 令牌生成方法
+     * @return string
+     */
+    function token(string $name = '__token__', string $type = 'md5'): string
+    {
+        return Request::buildToken($name, $type);
+    }
+}
+
+if (!function_exists('token_field')) {
+    /**
+     * 生成令牌隐藏表单
+     * @param string $name 令牌名称
+     * @param mixed  $type 令牌生成方法
+     * @return string
+     */
+    function token_field(string $name = '__token__', string $type = 'md5'): string
+    {
+        $token = Request::buildToken($name, $type);
+
+        return '<input type="hidden" name="' . $name . '" value="' . $token . '" />';
+    }
+}
+
+if (!function_exists('token_meta')) {
+    /**
+     * 生成令牌meta
+     * @param string $name 令牌名称
+     * @param mixed  $type 令牌生成方法
+     * @return string
+     */
+    function token_meta(string $name = '__token__', string $type = 'md5'): string
+    {
+        $token = Request::buildToken($name, $type);
+
+        return '<meta name="csrf-token" content="' . $token . '">';
+    }
+}
+
+if (!function_exists('trace')) {
+    /**
+     * 记录日志信息
+     * @param mixed  $log   log信息 支持字符串和数组
+     * @param string $level 日志级别
+     * @return array|void
+     */
+    function trace($log = '[think]', string $level = 'log')
+    {
+        if ('[think]' === $log) {
+            return Log::getLog();
+        }
+
+        Log::record($log, $level);
+    }
+}
+
+if (!function_exists('url')) {
+    /**
+     * Url生成
+     * @param string      $url    路由地址
+     * @param array       $vars   变量
+     * @param bool|string $suffix 生成的URL后缀
+     * @param bool|string $domain 域名
+     * @return UrlBuild
+     */
+    function url(string $url = '', array $vars = [], $suffix = true, $domain = false): UrlBuild
+    {
+        return Route::buildUrl($url, $vars)->suffix($suffix)->domain($domain);
+    }
+}
+
+if (!function_exists('validate')) {
+    /**
+     * 生成验证对象
+     * @param string|array $validate      验证器类名或者验证规则数组
+     * @param array        $message       错误提示信息
+     * @param bool         $batch         是否批量验证
+     * @param bool         $failException 是否抛出异常
+     * @return Validate
+     */
+    function validate($validate = '', array $message = [], bool $batch = false, bool $failException = true): Validate
+    {
+        if (is_array($validate) || '' === $validate) {
+            $v = new Validate();
+            if (is_array($validate)) {
+                $v->rule($validate);
+            }
+        } else {
+            if (strpos($validate, '.')) {
+                // 支持场景
+                [$validate, $scene] = explode('.', $validate);
+            }
+
+            $class = false !== strpos($validate, '\\') ? $validate : app()->parseClass('validate', $validate);
+
+            $v = new $class();
+
+            if (!empty($scene)) {
+                $v->scene($scene);
+            }
+        }
+
+        return $v->message($message)->batch($batch)->failException($failException);
+    }
+}
+
+if (!function_exists('view')) {
+    /**
+     * 渲染模板输出
+     * @param string   $template 模板文件
+     * @param array    $vars     模板变量
+     * @param int      $code     状态码
+     * @param callable $filter   内容过滤
+     * @return \think\response\View
+     */
+    function view(string $template = '', $vars = [], $code = 200, $filter = null): View
+    {
+        return Response::create($template, 'view', $code)->assign($vars)->filter($filter);
+    }
+}
+
+if (!function_exists('display')) {
+    /**
+     * 渲染模板输出
+     * @param string   $content 渲染内容
+     * @param array    $vars    模板变量
+     * @param int      $code    状态码
+     * @param callable $filter  内容过滤
+     * @return \think\response\View
+     */
+    function display(string $content, $vars = [], $code = 200, $filter = null): View
+    {
+        return Response::create($content, 'view', $code)->isContent(true)->assign($vars)->filter($filter);
+    }
+}
+
+if (!function_exists('xml')) {
+    /**
+     * 获取\think\response\Xml对象实例
+     * @param mixed $data    返回的数据
+     * @param int   $code    状态码
+     * @param array $header  头部
+     * @param array $options 参数
+     * @return \think\response\Xml
+     */
+    function xml($data = [], $code = 200, $header = [], $options = []): Xml
+    {
+        return Response::create($data, 'xml', $code)->header($header)->options($options);
+    }
+}
+
+if (!function_exists('app_path')) {
+    /**
+     * 获取当前应用目录
+     *
+     * @param string $path
+     * @return string
+     */
+    function app_path($path = '')
+    {
+        return app()->getAppPath() . ($path ? $path . DIRECTORY_SEPARATOR : $path);
+    }
+}
+
+if (!function_exists('base_path')) {
+    /**
+     * 获取应用基础目录
+     *
+     * @param string $path
+     * @return string
+     */
+    function base_path($path = '')
+    {
+        return app()->getBasePath() . ($path ? $path . DIRECTORY_SEPARATOR : $path);
+    }
+}
+
+if (!function_exists('config_path')) {
+    /**
+     * 获取应用配置目录
+     *
+     * @param string $path
+     * @return string
+     */
+    function config_path($path = '')
+    {
+        return app()->getConfigPath() . ($path ? $path . DIRECTORY_SEPARATOR : $path);
+    }
+}
+
+if (!function_exists('public_path')) {
+    /**
+     * 获取web根目录
+     *
+     * @param string $path
+     * @return string
+     */
+    function public_path($path = '')
+    {
+        return app()->getRootPath() . 'public' . DIRECTORY_SEPARATOR . ($path ? ltrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR : $path);
+    }
+}
+
+if (!function_exists('runtime_path')) {
+    /**
+     * 获取应用运行时目录
+     *
+     * @param string $path
+     * @return string
+     */
+    function runtime_path($path = '')
+    {
+        return app()->getRuntimePath() . ($path ? $path . DIRECTORY_SEPARATOR : $path);
+    }
+}
+
+if (!function_exists('root_path')) {
+    /**
+     * 获取项目根目录
+     *
+     * @param string $path
+     * @return string
+     */
+    function root_path($path = '')
+    {
+        return app()->getRootPath() . ($path ? $path . DIRECTORY_SEPARATOR : $path);
+    }
+}
diff --git a/vendor/topthink/framework/src/lang/zh-cn.php b/vendor/topthink/framework/src/lang/zh-cn.php
new file mode 100755
index 0000000..a546330
--- /dev/null
+++ b/vendor/topthink/framework/src/lang/zh-cn.php
@@ -0,0 +1,148 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+// 核心中文语言包
+return [
+    // 系统错误提示
+    'Undefined variable'                                        => '未定义变量',
+    'Undefined index'                                           => '未定义数组索引',
+    'Undefined offset'                                          => '未定义数组下标',
+    'Parse error'                                               => '语法解析错误',
+    'Type error'                                                => '类型错误',
+    'Fatal error'                                               => '致命错误',
+    'syntax error'                                              => '语法错误',
+
+    // 框架核心错误提示
+    'dispatch type not support'                                 => '不支持的调度类型',
+    'method param miss'                                         => '方法参数错误',
+    'method not exists'                                         => '方法不存在',
+    'function not exists'                                       => '函数不存在',
+    'app not exists'                                            => '应用不存在',
+    'controller not exists'                                     => '控制器不存在',
+    'class not exists'                                          => '类不存在',
+    'property not exists'                                       => '类的属性不存在',
+    'template not exists'                                       => '模板文件不存在',
+    'illegal controller name'                                   => '非法的控制器名称',
+    'illegal action name'                                       => '非法的操作名称',
+    'url suffix deny'                                           => '禁止的URL后缀访问',
+    'Undefined cache config'                                    => '缓存配置未定义',
+    'Route Not Found'                                           => '当前访问路由未定义或不匹配',
+    'Undefined db config'                                       => '数据库配置未定义',
+    'Undefined log config'                                      => '日志配置未定义',
+    'Undefined db type'                                         => '未定义数据库类型',
+    'variable type error'                                       => '变量类型错误',
+    'PSR-4 error'                                               => 'PSR-4 规范错误',
+    'not support type'                                          => '不支持的分页索引字段类型',
+    'not support total'                                         => '简洁模式下不能获取数据总数',
+    'not support last'                                          => '简洁模式下不能获取最后一页',
+    'error session handler'                                     => '错误的SESSION处理器类',
+    'not allow php tag'                                         => '模板不允许使用PHP语法',
+    'not support'                                               => '不支持',
+    'database config error'                                     => '数据库配置信息错误',
+    'redisd master'                                             => 'Redisd 主服务器错误',
+    'redisd slave'                                              => 'Redisd 从服务器错误',
+    'must run at sae'                                           => '必须在SAE运行',
+    'memcache init error'                                       => '未开通Memcache服务,请在SAE管理平台初始化Memcache服务',
+    'KVDB init error'                                           => '没有初始化KVDB,请在SAE管理平台初始化KVDB服务',
+    'fields not exists'                                         => '数据表字段不存在',
+    'where express error'                                       => '查询表达式错误',
+    'no data to update'                                         => '没有任何数据需要更新',
+    'miss data to insert'                                       => '缺少需要写入的数据',
+    'miss complex primary data'                                 => '缺少复合主键数据',
+    'miss update condition'                                     => '缺少更新条件',
+    'model data Not Found'                                      => '模型数据不存在',
+    'table data not Found'                                      => '表数据不存在',
+    'delete without condition'                                  => '没有条件不会执行删除操作',
+    'miss relation data'                                        => '缺少关联表数据',
+    'tag attr must'                                             => '模板标签属性必须',
+    'tag error'                                                 => '模板标签错误',
+    'cache write error'                                         => '缓存写入失败',
+    'sae mc write error'                                        => 'SAE mc 写入错误',
+    'route name not exists'                                     => '路由标识不存在(或参数不够)',
+    'invalid request'                                           => '非法请求',
+    'bind attr has exists'                                      => '模型的属性已经存在',
+    'relation data not exists'                                  => '关联数据不存在',
+    'relation not support'                                      => '关联不支持',
+    'chunk not support order'                                   => 'Chunk不支持调用order方法',
+    'route pattern error'                                       => '路由变量规则定义错误',
+    'route behavior will not support'                           => '路由行为废弃(使用中间件替代)',
+    'closure not support cache(true)'                           => '使用闭包查询不支持cache(true),请指定缓存Key',
+
+    // 上传错误信息
+    'unknown upload error'                                      => '未知上传错误!',
+    'file write error'                                          => '文件写入失败!',
+    'upload temp dir not found'                                 => '找不到临时文件夹!',
+    'no file to uploaded'                                       => '没有文件被上传!',
+    'only the portion of file is uploaded'                      => '文件只有部分被上传!',
+    'upload File size exceeds the maximum value'                => '上传文件大小超过了最大值!',
+    'upload write error'                                        => '文件上传保存错误!',
+    'has the same filename: {:filename}'                        => '存在同名文件:{:filename}',
+    'upload illegal files'                                      => '非法上传文件',
+    'illegal image files'                                       => '非法图片文件',
+    'extensions to upload is not allowed'                       => '上传文件后缀不允许',
+    'mimetype to upload is not allowed'                         => '上传文件MIME类型不允许!',
+    'filesize not match'                                        => '上传文件大小不符!',
+    'directory {:path} creation failed'                         => '目录 {:path} 创建失败!',
+
+    'The middleware must return Response instance'              => '中间件方法必须返回Response对象实例',
+    'The queue was exhausted, with no response returned'        => '中间件队列为空',
+    // Validate Error Message
+    ':attribute require'                                        => ':attribute不能为空',
+    ':attribute must'                                           => ':attribute必须',
+    ':attribute must be numeric'                                => ':attribute必须是数字',
+    ':attribute must be integer'                                => ':attribute必须是整数',
+    ':attribute must be float'                                  => ':attribute必须是浮点数',
+    ':attribute must be bool'                                   => ':attribute必须是布尔值',
+    ':attribute not a valid email address'                      => ':attribute格式不符',
+    ':attribute not a valid mobile'                             => ':attribute格式不符',
+    ':attribute must be a array'                                => ':attribute必须是数组',
+    ':attribute must be yes,on or 1'                            => ':attribute必须是yes、on或者1',
+    ':attribute not a valid datetime'                           => ':attribute不是一个有效的日期或时间格式',
+    ':attribute not a valid file'                               => ':attribute不是有效的上传文件',
+    ':attribute not a valid image'                              => ':attribute不是有效的图像文件',
+    ':attribute must be alpha'                                  => ':attribute只能是字母',
+    ':attribute must be alpha-numeric'                          => ':attribute只能是字母和数字',
+    ':attribute must be alpha-numeric, dash, underscore'        => ':attribute只能是字母、数字和下划线_及破折号-',
+    ':attribute not a valid domain or ip'                       => ':attribute不是有效的域名或者IP',
+    ':attribute must be chinese'                                => ':attribute只能是汉字',
+    ':attribute must be chinese or alpha'                       => ':attribute只能是汉字、字母',
+    ':attribute must be chinese,alpha-numeric'                  => ':attribute只能是汉字、字母和数字',
+    ':attribute must be chinese,alpha-numeric,underscore, dash' => ':attribute只能是汉字、字母、数字和下划线_及破折号-',
+    ':attribute not a valid url'                                => ':attribute不是有效的URL地址',
+    ':attribute not a valid ip'                                 => ':attribute不是有效的IP地址',
+    ':attribute must be dateFormat of :rule'                    => ':attribute必须使用日期格式 :rule',
+    ':attribute must be in :rule'                               => ':attribute必须在 :rule 范围内',
+    ':attribute be notin :rule'                                 => ':attribute不能在 :rule 范围内',
+    ':attribute must between :1 - :2'                           => ':attribute只能在 :1 - :2 之间',
+    ':attribute not between :1 - :2'                            => ':attribute不能在 :1 - :2 之间',
+    'size of :attribute must be :rule'                          => ':attribute长度不符合要求 :rule',
+    'max size of :attribute must be :rule'                      => ':attribute长度不能超过 :rule',
+    'min size of :attribute must be :rule'                      => ':attribute长度不能小于 :rule',
+    ':attribute cannot be less than :rule'                      => ':attribute日期不能小于 :rule',
+    ':attribute cannot exceed :rule'                            => ':attribute日期不能超过 :rule',
+    ':attribute not within :rule'                               => '不在有效期内 :rule',
+    'access IP is not allowed'                                  => '不允许的IP访问',
+    'access IP denied'                                          => '禁止的IP访问',
+    ':attribute out of accord with :2'                          => ':attribute和确认字段:2不一致',
+    ':attribute cannot be same with :2'                         => ':attribute和比较字段:2不能相同',
+    ':attribute must greater than or equal :rule'               => ':attribute必须大于等于 :rule',
+    ':attribute must greater than :rule'                        => ':attribute必须大于 :rule',
+    ':attribute must less than or equal :rule'                  => ':attribute必须小于等于 :rule',
+    ':attribute must less than :rule'                           => ':attribute必须小于 :rule',
+    ':attribute must equal :rule'                               => ':attribute必须等于 :rule',
+    ':attribute has exists'                                     => ':attribute已存在',
+    ':attribute not conform to the rules'                       => ':attribute不符合指定规则',
+    'invalid Request method'                                    => '无效的请求类型',
+    'invalid token'                                             => '令牌数据无效',
+    'not conform to the rules'                                  => '规则错误',
+
+    'record has update'                                         => '记录已经被更新了',
+];
diff --git a/vendor/topthink/framework/src/think/App.php b/vendor/topthink/framework/src/think/App.php
new file mode 100755
index 0000000..9f309c0
--- /dev/null
+++ b/vendor/topthink/framework/src/think/App.php
@@ -0,0 +1,620 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+use think\event\AppInit;
+use think\helper\Str;
+use think\initializer\BootService;
+use think\initializer\Error;
+use think\initializer\RegisterService;
+
+/**
+ * App 基础类
+ * @property Route      $route
+ * @property Config     $config
+ * @property Cache      $cache
+ * @property Request    $request
+ * @property Http       $http
+ * @property Console    $console
+ * @property Env        $env
+ * @property Event      $event
+ * @property Middleware $middleware
+ * @property Log        $log
+ * @property Lang       $lang
+ * @property Db         $db
+ * @property Cookie     $cookie
+ * @property Session    $session
+ * @property Validate   $validate
+ */
+class App extends Container
+{
+    const VERSION = '6.1.1';
+
+    /**
+     * 应用调试模式
+     * @var bool
+     */
+    protected $appDebug = false;
+
+    /**
+     * 环境变量标识
+     * @var string
+     */
+    protected $envName = '';
+
+    /**
+     * 应用开始时间
+     * @var float
+     */
+    protected $beginTime;
+
+    /**
+     * 应用内存初始占用
+     * @var integer
+     */
+    protected $beginMem;
+
+    /**
+     * 当前应用类库命名空间
+     * @var string
+     */
+    protected $namespace = 'app';
+
+    /**
+     * 应用根目录
+     * @var string
+     */
+    protected $rootPath = '';
+
+    /**
+     * 框架目录
+     * @var string
+     */
+    protected $thinkPath = '';
+
+    /**
+     * 应用目录
+     * @var string
+     */
+    protected $appPath = '';
+
+    /**
+     * Runtime目录
+     * @var string
+     */
+    protected $runtimePath = '';
+
+    /**
+     * 路由定义目录
+     * @var string
+     */
+    protected $routePath = '';
+
+    /**
+     * 配置后缀
+     * @var string
+     */
+    protected $configExt = '.php';
+
+    /**
+     * 应用初始化器
+     * @var array
+     */
+    protected $initializers = [
+        Error::class,
+        RegisterService::class,
+        BootService::class,
+    ];
+
+    /**
+     * 注册的系统服务
+     * @var array
+     */
+    protected $services = [];
+
+    /**
+     * 初始化
+     * @var bool
+     */
+    protected $initialized = false;
+
+    /**
+     * 容器绑定标识
+     * @var array
+     */
+    protected $bind = [
+        'app'                     => App::class,
+        'cache'                   => Cache::class,
+        'config'                  => Config::class,
+        'console'                 => Console::class,
+        'cookie'                  => Cookie::class,
+        'db'                      => Db::class,
+        'env'                     => Env::class,
+        'event'                   => Event::class,
+        'http'                    => Http::class,
+        'lang'                    => Lang::class,
+        'log'                     => Log::class,
+        'middleware'              => Middleware::class,
+        'request'                 => Request::class,
+        'response'                => Response::class,
+        'route'                   => Route::class,
+        'session'                 => Session::class,
+        'validate'                => Validate::class,
+        'view'                    => View::class,
+        'think\DbManager'         => Db::class,
+        'think\LogManager'        => Log::class,
+        'think\CacheManager'      => Cache::class,
+
+        // 接口依赖注入
+        'Psr\Log\LoggerInterface' => Log::class,
+    ];
+
+    /**
+     * 架构方法
+     * @access public
+     * @param string $rootPath 应用根目录
+     */
+    public function __construct(string $rootPath = '')
+    {
+        $this->thinkPath   = realpath(dirname(__DIR__)) . DIRECTORY_SEPARATOR;
+        $this->rootPath    = $rootPath ? rtrim($rootPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR : $this->getDefaultRootPath();
+        $this->appPath     = $this->rootPath . 'app' . DIRECTORY_SEPARATOR;
+        $this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR;
+
+        if (is_file($this->appPath . 'provider.php')) {
+            $this->bind(include $this->appPath . 'provider.php');
+        }
+
+        static::setInstance($this);
+
+        $this->instance('app', $this);
+        $this->instance('think\Container', $this);
+    }
+
+    /**
+     * 注册服务
+     * @access public
+     * @param Service|string $service 服务
+     * @param bool           $force   强制重新注册
+     * @return Service|null
+     */
+    public function register($service, bool $force = false)
+    {
+        $registered = $this->getService($service);
+
+        if ($registered && !$force) {
+            return $registered;
+        }
+
+        if (is_string($service)) {
+            $service = new $service($this);
+        }
+
+        if (method_exists($service, 'register')) {
+            $service->register();
+        }
+
+        if (property_exists($service, 'bind')) {
+            $this->bind($service->bind);
+        }
+
+        $this->services[] = $service;
+    }
+
+    /**
+     * 执行服务
+     * @access public
+     * @param Service $service 服务
+     * @return mixed
+     */
+    public function bootService($service)
+    {
+        if (method_exists($service, 'boot')) {
+            return $this->invoke([$service, 'boot']);
+        }
+    }
+
+    /**
+     * 获取服务
+     * @param string|Service $service
+     * @return Service|null
+     */
+    public function getService($service)
+    {
+        $name = is_string($service) ? $service : get_class($service);
+        return array_values(array_filter($this->services, function ($value) use ($name) {
+            return $value instanceof $name;
+        }, ARRAY_FILTER_USE_BOTH))[0] ?? null;
+    }
+
+    /**
+     * 开启应用调试模式
+     * @access public
+     * @param bool $debug 开启应用调试模式
+     * @return $this
+     */
+    public function debug(bool $debug = true)
+    {
+        $this->appDebug = $debug;
+        return $this;
+    }
+
+    /**
+     * 是否为调试模式
+     * @access public
+     * @return bool
+     */
+    public function isDebug(): bool
+    {
+        return $this->appDebug;
+    }
+
+    /**
+     * 设置应用命名空间
+     * @access public
+     * @param string $namespace 应用命名空间
+     * @return $this
+     */
+    public function setNamespace(string $namespace)
+    {
+        $this->namespace = $namespace;
+        return $this;
+    }
+
+    /**
+     * 获取应用类库命名空间
+     * @access public
+     * @return string
+     */
+    public function getNamespace(): string
+    {
+        return $this->namespace;
+    }
+
+    /**
+     * 设置环境变量标识
+     * @access public
+     * @param string $name 环境标识
+     * @return $this
+     */
+    public function setEnvName(string $name)
+    {
+        $this->envName = $name;
+        return $this;
+    }
+
+    /**
+     * 获取框架版本
+     * @access public
+     * @return string
+     */
+    public function version(): string
+    {
+        return static::VERSION;
+    }
+
+    /**
+     * 获取应用根目录
+     * @access public
+     * @return string
+     */
+    public function getRootPath(): string
+    {
+        return $this->rootPath;
+    }
+
+    /**
+     * 获取应用基础目录
+     * @access public
+     * @return string
+     */
+    public function getBasePath(): string
+    {
+        return $this->rootPath . 'app' . DIRECTORY_SEPARATOR;
+    }
+
+    /**
+     * 获取当前应用目录
+     * @access public
+     * @return string
+     */
+    public function getAppPath(): string
+    {
+        return $this->appPath;
+    }
+
+    /**
+     * 设置应用目录
+     * @param string $path 应用目录
+     */
+    public function setAppPath(string $path)
+    {
+        $this->appPath = $path;
+    }
+
+    /**
+     * 获取应用运行时目录
+     * @access public
+     * @return string
+     */
+    public function getRuntimePath(): string
+    {
+        return $this->runtimePath;
+    }
+
+    /**
+     * 设置runtime目录
+     * @param string $path 定义目录
+     */
+    public function setRuntimePath(string $path): void
+    {
+        $this->runtimePath = $path;
+    }
+
+    /**
+     * 获取核心框架目录
+     * @access public
+     * @return string
+     */
+    public function getThinkPath(): string
+    {
+        return $this->thinkPath;
+    }
+
+    /**
+     * 获取应用配置目录
+     * @access public
+     * @return string
+     */
+    public function getConfigPath(): string
+    {
+        return $this->rootPath . 'config' . DIRECTORY_SEPARATOR;
+    }
+
+    /**
+     * 获取配置后缀
+     * @access public
+     * @return string
+     */
+    public function getConfigExt(): string
+    {
+        return $this->configExt;
+    }
+
+    /**
+     * 获取应用开启时间
+     * @access public
+     * @return float
+     */
+    public function getBeginTime(): float
+    {
+        return $this->beginTime;
+    }
+
+    /**
+     * 获取应用初始内存占用
+     * @access public
+     * @return integer
+     */
+    public function getBeginMem(): int
+    {
+        return $this->beginMem;
+    }
+
+    /**
+     * 加载环境变量定义
+     * @access public
+     * @param string $envName 环境标识
+     * @return void
+     */
+    public function loadEnv(string $envName = ''): void
+    {
+        // 加载环境变量
+        $envFile = $envName ? $this->rootPath . '.env.' . $envName : $this->rootPath . '.env';
+
+        if (is_file($envFile)) {
+            $this->env->load($envFile);
+        }
+    }
+
+    /**
+     * 初始化应用
+     * @access public
+     * @return $this
+     */
+    public function initialize()
+    {
+        $this->initialized = true;
+
+        $this->beginTime = microtime(true);
+        $this->beginMem  = memory_get_usage();
+
+        $this->loadEnv($this->envName);
+
+        $this->configExt = $this->env->get('config_ext', '.php');
+
+        $this->debugModeInit();
+
+        // 加载全局初始化文件
+        $this->load();
+
+        // 加载应用默认语言包
+        $this->loadLangPack();
+
+        // 监听AppInit
+        $this->event->trigger(AppInit::class);
+
+        date_default_timezone_set($this->config->get('app.default_timezone', 'Asia/Shanghai'));
+
+        // 初始化
+        foreach ($this->initializers as $initializer) {
+            $this->make($initializer)->init($this);
+        }
+
+        return $this;
+    }
+
+    /**
+     * 是否初始化过
+     * @return bool
+     */
+    public function initialized()
+    {
+        return $this->initialized;
+    }
+
+    /**
+     * 加载语言包
+     * @return void
+     */
+    public function loadLangPack()
+    {
+        // 加载默认语言包
+        $langSet = $this->lang->defaultLangSet();
+        $this->lang->switchLangSet($langSet);
+    }
+
+    /**
+     * 引导应用
+     * @access public
+     * @return void
+     */
+    public function boot(): void
+    {
+        array_walk($this->services, function ($service) {
+            $this->bootService($service);
+        });
+    }
+
+    /**
+     * 加载应用文件和配置
+     * @access protected
+     * @return void
+     */
+    protected function load(): void
+    {
+        $appPath = $this->getAppPath();
+
+        if (is_file($appPath . 'common.php')) {
+            include_once $appPath . 'common.php';
+        }
+
+        include_once $this->thinkPath . 'helper.php';
+
+        $configPath = $this->getConfigPath();
+
+        $files = [];
+
+        if (is_dir($configPath)) {
+            $files = glob($configPath . '*' . $this->configExt);
+        }
+
+        foreach ($files as $file) {
+            $this->config->load($file, pathinfo($file, PATHINFO_FILENAME));
+        }
+
+        if (is_file($appPath . 'event.php')) {
+            $this->loadEvent(include $appPath . 'event.php');
+        }
+
+        if (is_file($appPath . 'service.php')) {
+            $services = include $appPath . 'service.php';
+            foreach ($services as $service) {
+                $this->register($service);
+            }
+        }
+    }
+
+    /**
+     * 调试模式设置
+     * @access protected
+     * @return void
+     */
+    protected function debugModeInit(): void
+    {
+        // 应用调试模式
+        if (!$this->appDebug) {
+            $this->appDebug = $this->env->get('app_debug') ? true : false;
+            ini_set('display_errors', 'Off');
+        }
+
+        if (!$this->runningInConsole()) {
+            //重新申请一块比较大的buffer
+            if (ob_get_level() > 0) {
+                $output = ob_get_clean();
+            }
+            ob_start();
+            if (!empty($output)) {
+                echo $output;
+            }
+        }
+    }
+
+    /**
+     * 注册应用事件
+     * @access protected
+     * @param array $event 事件数据
+     * @return void
+     */
+    public function loadEvent(array $event): void
+    {
+        if (isset($event['bind'])) {
+            $this->event->bind($event['bind']);
+        }
+
+        if (isset($event['listen'])) {
+            $this->event->listenEvents($event['listen']);
+        }
+
+        if (isset($event['subscribe'])) {
+            $this->event->subscribe($event['subscribe']);
+        }
+    }
+
+    /**
+     * 解析应用类的类名
+     * @access public
+     * @param string $layer 层名 controller model ...
+     * @param string $name  类名
+     * @return string
+     */
+    public function parseClass(string $layer, string $name): string
+    {
+        $name  = str_replace(['/', '.'], '\\', $name);
+        $array = explode('\\', $name);
+        $class = Str::studly(array_pop($array));
+        $path  = $array ? implode('\\', $array) . '\\' : '';
+
+        return $this->namespace . '\\' . $layer . '\\' . $path . $class;
+    }
+
+    /**
+     * 是否运行在命令行下
+     * @return bool
+     */
+    public function runningInConsole(): bool
+    {
+        return php_sapi_name() === 'cli' || php_sapi_name() === 'phpdbg';
+    }
+
+    /**
+     * 获取应用根目录
+     * @access protected
+     * @return string
+     */
+    protected function getDefaultRootPath(): string
+    {
+        return dirname($this->thinkPath, 4) . DIRECTORY_SEPARATOR;
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/Cache.php b/vendor/topthink/framework/src/think/Cache.php
new file mode 100755
index 0000000..f802b55
--- /dev/null
+++ b/vendor/topthink/framework/src/think/Cache.php
@@ -0,0 +1,197 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+use Psr\SimpleCache\CacheInterface;
+use think\cache\Driver;
+use think\cache\TagSet;
+use think\exception\InvalidArgumentException;
+use think\helper\Arr;
+
+/**
+ * 缓存管理类
+ * @mixin Driver
+ * @mixin \think\cache\driver\File
+ */
+class Cache extends Manager implements CacheInterface
+{
+
+    protected $namespace = '\\think\\cache\\driver\\';
+
+    /**
+     * 默认驱动
+     * @return string|null
+     */
+    public function getDefaultDriver()
+    {
+        return $this->getConfig('default');
+    }
+
+    /**
+     * 获取缓存配置
+     * @access public
+     * @param null|string $name    名称
+     * @param mixed       $default 默认值
+     * @return mixed
+     */
+    public function getConfig(string $name = null, $default = null)
+    {
+        if (!is_null($name)) {
+            return $this->app->config->get('cache.' . $name, $default);
+        }
+
+        return $this->app->config->get('cache');
+    }
+
+    /**
+     * 获取驱动配置
+     * @param string $store
+     * @param string $name
+     * @param null   $default
+     * @return array
+     */
+    public function getStoreConfig(string $store, string $name = null, $default = null)
+    {
+        if ($config = $this->getConfig("stores.{$store}")) {
+            return Arr::get($config, $name, $default);
+        }
+
+        throw new \InvalidArgumentException("Store [$store] not found.");
+    }
+
+    protected function resolveType(string $name)
+    {
+        return $this->getStoreConfig($name, 'type', 'file');
+    }
+
+    protected function resolveConfig(string $name)
+    {
+        return $this->getStoreConfig($name);
+    }
+
+    /**
+     * 连接或者切换缓存
+     * @access public
+     * @param string $name 连接配置名
+     * @return Driver
+     */
+    public function store(string $name = null)
+    {
+        return $this->driver($name);
+    }
+
+    /**
+     * 清空缓冲池
+     * @access public
+     * @return bool
+     */
+    public function clear(): bool
+    {
+        return $this->store()->clear();
+    }
+
+    /**
+     * 读取缓存
+     * @access public
+     * @param string $key     缓存变量名
+     * @param mixed  $default 默认值
+     * @return mixed
+     */
+    public function get($key, $default = null)
+    {
+        return $this->store()->get($key, $default);
+    }
+
+    /**
+     * 写入缓存
+     * @access public
+     * @param string        $key   缓存变量名
+     * @param mixed         $value 存储数据
+     * @param int|\DateTime $ttl   有效时间 0为永久
+     * @return bool
+     */
+    public function set($key, $value, $ttl = null): bool
+    {
+        return $this->store()->set($key, $value, $ttl);
+    }
+
+    /**
+     * 删除缓存
+     * @access public
+     * @param string $key 缓存变量名
+     * @return bool
+     */
+    public function delete($key): bool
+    {
+        return $this->store()->delete($key);
+    }
+
+    /**
+     * 读取缓存
+     * @access public
+     * @param iterable $keys    缓存变量名
+     * @param mixed    $default 默认值
+     * @return iterable
+     * @throws InvalidArgumentException
+     */
+    public function getMultiple($keys, $default = null): iterable
+    {
+        return $this->store()->getMultiple($keys, $default);
+    }
+
+    /**
+     * 写入缓存
+     * @access public
+     * @param iterable               $values 缓存数据
+     * @param null|int|\DateInterval $ttl    有效时间 0为永久
+     * @return bool
+     */
+    public function setMultiple($values, $ttl = null): bool
+    {
+        return $this->store()->setMultiple($values, $ttl);
+    }
+
+    /**
+     * 删除缓存
+     * @access public
+     * @param iterable $keys 缓存变量名
+     * @return bool
+     * @throws InvalidArgumentException
+     */
+    public function deleteMultiple($keys): bool
+    {
+        return $this->store()->deleteMultiple($keys);
+    }
+
+    /**
+     * 判断缓存是否存在
+     * @access public
+     * @param string $key 缓存变量名
+     * @return bool
+     */
+    public function has($key): bool
+    {
+        return $this->store()->has($key);
+    }
+
+    /**
+     * 缓存标签
+     * @access public
+     * @param string|array $name 标签名
+     * @return TagSet
+     */
+    public function tag($name): TagSet
+    {
+        return $this->store()->tag($name);
+    }
+}
diff --git a/vendor/topthink/framework/src/think/Config.php b/vendor/topthink/framework/src/think/Config.php
new file mode 100755
index 0000000..9162e82
--- /dev/null
+++ b/vendor/topthink/framework/src/think/Config.php
@@ -0,0 +1,197 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+/**
+ * 配置管理类
+ * @package think
+ */
+class Config
+{
+    /**
+     * 配置参数
+     * @var array
+     */
+    protected $config = [];
+
+    /**
+     * 配置文件目录
+     * @var string
+     */
+    protected $path;
+
+    /**
+     * 配置文件后缀
+     * @var string
+     */
+    protected $ext;
+
+    /**
+     * 构造方法
+     * @access public
+     */
+    public function __construct(string $path = null, string $ext = '.php')
+    {
+        $this->path = $path ?: '';
+        $this->ext  = $ext;
+    }
+
+    public static function __make(App $app)
+    {
+        $path = $app->getConfigPath();
+        $ext  = $app->getConfigExt();
+
+        return new static($path, $ext);
+    }
+
+    /**
+     * 加载配置文件(多种格式)
+     * @access public
+     * @param  string $file 配置文件名
+     * @param  string $name 一级配置名
+     * @return array
+     */
+    public function load(string $file, string $name = ''): array
+    {
+        if (is_file($file)) {
+            $filename = $file;
+        } elseif (is_file($this->path . $file . $this->ext)) {
+            $filename = $this->path . $file . $this->ext;
+        }
+
+        if (isset($filename)) {
+            return $this->parse($filename, $name);
+        }
+
+        return $this->config;
+    }
+
+    /**
+     * 解析配置文件
+     * @access public
+     * @param  string $file 配置文件名
+     * @param  string $name 一级配置名
+     * @return array
+     */
+    protected function parse(string $file, string $name): array
+    {
+        $type   = pathinfo($file, PATHINFO_EXTENSION);
+        $config = [];
+        switch ($type) {
+            case 'php':
+                $config = include $file;
+                break;
+            case 'yml':
+            case 'yaml':
+                if (function_exists('yaml_parse_file')) {
+                    $config = yaml_parse_file($file);
+                }
+                break;
+            case 'ini':
+                $config = parse_ini_file($file, true, INI_SCANNER_TYPED) ?: [];
+                break;
+            case 'json':
+                $config = json_decode(file_get_contents($file), true);
+                break;
+        }
+
+        return is_array($config) ? $this->set($config, strtolower($name)) : [];
+    }
+
+    /**
+     * 检测配置是否存在
+     * @access public
+     * @param  string $name 配置参数名(支持多级配置 .号分割)
+     * @return bool
+     */
+    public function has(string $name): bool
+    {
+        if (false === strpos($name, '.') && !isset($this->config[strtolower($name)])) {
+            return false;
+        }
+
+        return !is_null($this->get($name));
+    }
+
+    /**
+     * 获取一级配置
+     * @access protected
+     * @param  string $name 一级配置名
+     * @return array
+     */
+    protected function pull(string $name): array
+    {
+        $name = strtolower($name);
+
+        return $this->config[$name] ?? [];
+    }
+
+    /**
+     * 获取配置参数 为空则获取所有配置
+     * @access public
+     * @param  string $name    配置参数名(支持多级配置 .号分割)
+     * @param  mixed  $default 默认值
+     * @return mixed
+     */
+    public function get(string $name = null, $default = null)
+    {
+        // 无参数时获取所有
+        if (empty($name)) {
+            return $this->config;
+        }
+
+        if (false === strpos($name, '.')) {
+            return $this->pull($name);
+        }
+
+        $name    = explode('.', $name);
+        $name[0] = strtolower($name[0]);
+        $config  = $this->config;
+
+        // 按.拆分成多维数组进行判断
+        foreach ($name as $val) {
+            if (isset($config[$val])) {
+                $config = $config[$val];
+            } else {
+                return $default;
+            }
+        }
+
+        return $config;
+    }
+
+    /**
+     * 设置配置参数 name为数组则为批量设置
+     * @access public
+     * @param  array  $config 配置参数
+     * @param  string $name 配置名
+     * @return array
+     */
+    public function set(array $config, string $name = null): array
+    {
+        if (!empty($name)) {
+            if (isset($this->config[$name])) {
+                $result = array_merge($this->config[$name], $config);
+            } else {
+                $result = $config;
+            }
+
+            $this->config[$name] = $result;
+        } else {
+            $result = $this->config = array_merge($this->config, array_change_key_case($config));
+        }
+
+        return $result;
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/Console.php b/vendor/topthink/framework/src/think/Console.php
new file mode 100755
index 0000000..27f12ba
--- /dev/null
+++ b/vendor/topthink/framework/src/think/Console.php
@@ -0,0 +1,786 @@
+<?php
+// +----------------------------------------------------------------------
+// | TopThink [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2015 http://www.topthink.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Author: zhangyajun <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+use Closure;
+use InvalidArgumentException;
+use LogicException;
+use think\console\Command;
+use think\console\command\Clear;
+use think\console\command\Help;
+use think\console\command\Help as HelpCommand;
+use think\console\command\Lists;
+use think\console\command\make\Command as MakeCommand;
+use think\console\command\make\Controller;
+use think\console\command\make\Event;
+use think\console\command\make\Listener;
+use think\console\command\make\Middleware;
+use think\console\command\make\Model;
+use think\console\command\make\Service;
+use think\console\command\make\Subscribe;
+use think\console\command\make\Validate;
+use think\console\command\optimize\Route;
+use think\console\command\optimize\Schema;
+use think\console\command\RouteList;
+use think\console\command\RunServer;
+use think\console\command\ServiceDiscover;
+use think\console\command\VendorPublish;
+use think\console\command\Version;
+use think\console\Input;
+use think\console\input\Argument as InputArgument;
+use think\console\input\Definition as InputDefinition;
+use think\console\input\Option as InputOption;
+use think\console\Output;
+use think\console\output\driver\Buffer;
+
+/**
+ * 控制台应用管理类
+ */
+class Console
+{
+
+    protected $app;
+
+    /** @var Command[] */
+    protected $commands = [];
+
+    protected $wantHelps = false;
+
+    protected $catchExceptions = true;
+    protected $autoExit        = true;
+    protected $definition;
+    protected $defaultCommand  = 'list';
+
+    protected $defaultCommands = [
+        'help'             => Help::class,
+        'list'             => Lists::class,
+        'clear'            => Clear::class,
+        'make:command'     => MakeCommand::class,
+        'make:controller'  => Controller::class,
+        'make:model'       => Model::class,
+        'make:middleware'  => Middleware::class,
+        'make:validate'    => Validate::class,
+        'make:event'       => Event::class,
+        'make:listener'    => Listener::class,
+        'make:service'     => Service::class,
+        'make:subscribe'   => Subscribe::class,
+        'optimize:route'   => Route::class,
+        'optimize:schema'  => Schema::class,
+        'run'              => RunServer::class,
+        'version'          => Version::class,
+        'route:list'       => RouteList::class,
+        'service:discover' => ServiceDiscover::class,
+        'vendor:publish'   => VendorPublish::class,
+    ];
+
+    /**
+     * 启动器
+     * @var array
+     */
+    protected static $startCallbacks = [];
+
+    public function __construct(App $app)
+    {
+        $this->app = $app;
+
+        $this->initialize();
+
+        $this->definition = $this->getDefaultInputDefinition();
+
+        //加载指令
+        $this->loadCommands();
+
+        $this->start();
+    }
+
+    /**
+     * 初始化
+     */
+    protected function initialize()
+    {
+        if (!$this->app->initialized()) {
+            $this->app->initialize();
+        }
+        $this->makeRequest();
+    }
+
+    /**
+     * 构造request
+     */
+    protected function makeRequest()
+    {
+        $url = $this->app->config->get('app.url', 'http://localhost');
+
+        $components = parse_url($url);
+
+        $server = $_SERVER;
+
+        if (isset($components['path'])) {
+            $server = array_merge($server, [
+                'SCRIPT_FILENAME' => $components['path'],
+                'SCRIPT_NAME'     => $components['path'],
+                'REQUEST_URI'     => $components['path'],
+            ]);
+        }
+
+        if (isset($components['host'])) {
+            $server['SERVER_NAME'] = $components['host'];
+            $server['HTTP_HOST']   = $components['host'];
+        }
+
+        if (isset($components['scheme'])) {
+            if ('https' === $components['scheme']) {
+                $server['HTTPS']       = 'on';
+                $server['SERVER_PORT'] = 443;
+            } else {
+                unset($server['HTTPS']);
+                $server['SERVER_PORT'] = 80;
+            }
+        }
+
+        if (isset($components['port'])) {
+            $server['SERVER_PORT'] = $components['port'];
+            $server['HTTP_HOST'] .= ':' . $components['port'];
+        }
+
+        /** @var Request $request */
+        $request = $this->app->make('request');
+
+        $request->withServer($server);
+    }
+
+    /**
+     * 添加初始化器
+     * @param Closure $callback
+     */
+    public static function starting(Closure $callback): void
+    {
+        static::$startCallbacks[] = $callback;
+    }
+
+    /**
+     * 清空启动器
+     */
+    public static function flushStartCallbacks(): void
+    {
+        static::$startCallbacks = [];
+    }
+
+    /**
+     * 设置执行用户
+     * @param $user
+     */
+    public static function setUser(string $user): void
+    {
+        if (extension_loaded('posix')) {
+            $user = posix_getpwnam($user);
+
+            if (!empty($user)) {
+                posix_setgid($user['gid']);
+                posix_setuid($user['uid']);
+            }
+        }
+    }
+
+    /**
+     * 启动
+     */
+    protected function start(): void
+    {
+        foreach (static::$startCallbacks as $callback) {
+            $callback($this);
+        }
+    }
+
+    /**
+     * 加载指令
+     * @access protected
+     */
+    protected function loadCommands(): void
+    {
+        $commands = $this->app->config->get('console.commands', []);
+        $commands = array_merge($this->defaultCommands, $commands);
+
+        $this->addCommands($commands);
+    }
+
+    /**
+     * @access public
+     * @param string $command
+     * @param array $parameters
+     * @param string $driver
+     * @return Output|Buffer
+     */
+    public function call(string $command, array $parameters = [], string $driver = 'buffer')
+    {
+        array_unshift($parameters, $command);
+
+        $input  = new Input($parameters);
+        $output = new Output($driver);
+
+        $this->setCatchExceptions(false);
+        $this->find($command)->run($input, $output);
+
+        return $output;
+    }
+
+    /**
+     * 执行当前的指令
+     * @access public
+     * @return int
+     * @throws \Exception
+     * @api
+     */
+    public function run()
+    {
+        $input  = new Input();
+        $output = new Output();
+
+        $this->configureIO($input, $output);
+
+        try {
+            $exitCode = $this->doRun($input, $output);
+        } catch (\Exception $e) {
+            if (!$this->catchExceptions) {
+                throw $e;
+            }
+
+            $output->renderException($e);
+
+            $exitCode = $e->getCode();
+            if (is_numeric($exitCode)) {
+                $exitCode = (int) $exitCode;
+                if (0 === $exitCode) {
+                    $exitCode = 1;
+                }
+            } else {
+                $exitCode = 1;
+            }
+        }
+
+        if ($this->autoExit) {
+            if ($exitCode > 255) {
+                $exitCode = 255;
+            }
+
+            exit($exitCode);
+        }
+
+        return $exitCode;
+    }
+
+    /**
+     * 执行指令
+     * @access public
+     * @param Input $input
+     * @param Output $output
+     * @return int
+     */
+    public function doRun(Input $input, Output $output)
+    {
+        if (true === $input->hasParameterOption(['--version', '-V'])) {
+            $output->writeln($this->getLongVersion());
+
+            return 0;
+        }
+
+        $name = $this->getCommandName($input);
+
+        if (true === $input->hasParameterOption(['--help', '-h'])) {
+            if (!$name) {
+                $name  = 'help';
+                $input = new Input(['help']);
+            } else {
+                $this->wantHelps = true;
+            }
+        }
+
+        if (!$name) {
+            $name  = $this->defaultCommand;
+            $input = new Input([$this->defaultCommand]);
+        }
+
+        $command = $this->find($name);
+
+        return $this->doRunCommand($command, $input, $output);
+    }
+
+    /**
+     * 设置输入参数定义
+     * @access public
+     * @param InputDefinition $definition
+     */
+    public function setDefinition(InputDefinition $definition): void
+    {
+        $this->definition = $definition;
+    }
+
+    /**
+     * 获取输入参数定义
+     * @access public
+     * @return InputDefinition The InputDefinition instance
+     */
+    public function getDefinition(): InputDefinition
+    {
+        return $this->definition;
+    }
+
+    /**
+     * Gets the help message.
+     * @access public
+     * @return string A help message.
+     */
+    public function getHelp(): string
+    {
+        return $this->getLongVersion();
+    }
+
+    /**
+     * 是否捕获异常
+     * @access public
+     * @param bool $boolean
+     * @api
+     */
+    public function setCatchExceptions(bool $boolean): void
+    {
+        $this->catchExceptions = $boolean;
+    }
+
+    /**
+     * 是否自动退出
+     * @access public
+     * @param bool $boolean
+     * @api
+     */
+    public function setAutoExit(bool $boolean): void
+    {
+        $this->autoExit = $boolean;
+    }
+
+    /**
+     * 获取完整的版本号
+     * @access public
+     * @return string
+     */
+    public function getLongVersion(): string
+    {
+        if ($this->app->version()) {
+            return sprintf('version <comment>%s</comment>', $this->app->version());
+        }
+
+        return '<info>Console Tool</info>';
+    }
+
+    /**
+     * 添加指令集
+     * @access public
+     * @param array $commands
+     */
+    public function addCommands(array $commands): void
+    {
+        foreach ($commands as $key => $command) {
+            if (is_subclass_of($command, Command::class)) {
+                // 注册指令
+                $this->addCommand($command, is_numeric($key) ? '' : $key);
+            }
+        }
+    }
+
+    /**
+     * 添加一个指令
+     * @access public
+     * @param string|Command $command 指令对象或者指令类名
+     * @param string $name 指令名 留空则自动获取
+     * @return Command|void
+     */
+    public function addCommand($command, string $name = '')
+    {
+        if ($name) {
+            $this->commands[$name] = $command;
+            return;
+        }
+
+        if (is_string($command)) {
+            $command = $this->app->invokeClass($command);
+        }
+
+        $command->setConsole($this);
+
+        if (!$command->isEnabled()) {
+            $command->setConsole(null);
+            return;
+        }
+
+        $command->setApp($this->app);
+
+        if (null === $command->getDefinition()) {
+            throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', get_class($command)));
+        }
+
+        $this->commands[$command->getName()] = $command;
+
+        foreach ($command->getAliases() as $alias) {
+            $this->commands[$alias] = $command;
+        }
+
+        return $command;
+    }
+
+    /**
+     * 获取指令
+     * @access public
+     * @param string $name 指令名称
+     * @return Command
+     * @throws InvalidArgumentException
+     */
+    public function getCommand(string $name): Command
+    {
+        if (!isset($this->commands[$name])) {
+            throw new InvalidArgumentException(sprintf('The command "%s" does not exist.', $name));
+        }
+
+        $command = $this->commands[$name];
+
+        if (is_string($command)) {
+            $command = $this->app->invokeClass($command);
+            /** @var Command $command */
+            $command->setConsole($this);
+            $command->setApp($this->app);
+        }
+
+        if ($this->wantHelps) {
+            $this->wantHelps = false;
+
+            /** @var HelpCommand $helpCommand */
+            $helpCommand = $this->getCommand('help');
+            $helpCommand->setCommand($command);
+
+            return $helpCommand;
+        }
+
+        return $command;
+    }
+
+    /**
+     * 某个指令是否存在
+     * @access public
+     * @param string $name 指令名称
+     * @return bool
+     */
+    public function hasCommand(string $name): bool
+    {
+        return isset($this->commands[$name]);
+    }
+
+    /**
+     * 获取所有的命名空间
+     * @access public
+     * @return array
+     */
+    public function getNamespaces(): array
+    {
+        $namespaces = [];
+        foreach ($this->commands as $key => $command) {
+            if (is_string($command)) {
+                $namespaces = array_merge($namespaces, $this->extractAllNamespaces($key));
+            } else {
+                $namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName()));
+
+                foreach ($command->getAliases() as $alias) {
+                    $namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias));
+                }
+            }
+        }
+
+        return array_values(array_unique(array_filter($namespaces)));
+    }
+
+    /**
+     * 查找注册命名空间中的名称或缩写。
+     * @access public
+     * @param string $namespace
+     * @return string
+     * @throws InvalidArgumentException
+     */
+    public function findNamespace(string $namespace): string
+    {
+        $allNamespaces = $this->getNamespaces();
+        $expr          = preg_replace_callback('{([^:]+|)}', function ($matches) {
+            return preg_quote($matches[1]) . '[^:]*';
+        }, $namespace);
+        $namespaces    = preg_grep('{^' . $expr . '}', $allNamespaces);
+
+        if (empty($namespaces)) {
+            $message = sprintf('There are no commands defined in the "%s" namespace.', $namespace);
+
+            if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) {
+                if (1 == count($alternatives)) {
+                    $message .= "\n\nDid you mean this?\n    ";
+                } else {
+                    $message .= "\n\nDid you mean one of these?\n    ";
+                }
+
+                $message .= implode("\n    ", $alternatives);
+            }
+
+            throw new InvalidArgumentException($message);
+        }
+
+        $exact = in_array($namespace, $namespaces, true);
+        if (count($namespaces) > 1 && !$exact) {
+            throw new InvalidArgumentException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))));
+        }
+
+        return $exact ? $namespace : reset($namespaces);
+    }
+
+    /**
+     * 查找指令
+     * @access public
+     * @param string $name 名称或者别名
+     * @return Command
+     * @throws InvalidArgumentException
+     */
+    public function find(string $name): Command
+    {
+        $allCommands = array_keys($this->commands);
+
+        $expr = preg_replace_callback('{([^:]+|)}', function ($matches) {
+            return preg_quote($matches[1]) . '[^:]*';
+        }, $name);
+
+        $commands = preg_grep('{^' . $expr . '}', $allCommands);
+
+        if (empty($commands) || count(preg_grep('{^' . $expr . '$}', $commands)) < 1) {
+            if (false !== $pos = strrpos($name, ':')) {
+                $this->findNamespace(substr($name, 0, $pos));
+            }
+
+            $message = sprintf('Command "%s" is not defined.', $name);
+
+            if ($alternatives = $this->findAlternatives($name, $allCommands)) {
+                if (1 == count($alternatives)) {
+                    $message .= "\n\nDid you mean this?\n    ";
+                } else {
+                    $message .= "\n\nDid you mean one of these?\n    ";
+                }
+                $message .= implode("\n    ", $alternatives);
+            }
+
+            throw new InvalidArgumentException($message);
+        }
+
+        $exact = in_array($name, $commands, true);
+        if (count($commands) > 1 && !$exact) {
+            $suggestions = $this->getAbbreviationSuggestions(array_values($commands));
+
+            throw new InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions));
+        }
+
+        return $this->getCommand($exact ? $name : reset($commands));
+    }
+
+    /**
+     * 获取所有的指令
+     * @access public
+     * @param string $namespace 命名空间
+     * @return Command[]
+     * @api
+     */
+    public function all(string $namespace = null): array
+    {
+        if (null === $namespace) {
+            return $this->commands;
+        }
+
+        $commands = [];
+        foreach ($this->commands as $name => $command) {
+            if ($this->extractNamespace($name, substr_count($namespace, ':') + 1) === $namespace) {
+                $commands[$name] = $command;
+            }
+        }
+
+        return $commands;
+    }
+
+    /**
+     * 配置基于用户的参数和选项的输入和输出实例。
+     * @access protected
+     * @param Input $input 输入实例
+     * @param Output $output 输出实例
+     */
+    protected function configureIO(Input $input, Output $output): void
+    {
+        if (true === $input->hasParameterOption(['--ansi'])) {
+            $output->setDecorated(true);
+        } elseif (true === $input->hasParameterOption(['--no-ansi'])) {
+            $output->setDecorated(false);
+        }
+
+        if (true === $input->hasParameterOption(['--no-interaction', '-n'])) {
+            $input->setInteractive(false);
+        }
+
+        if (true === $input->hasParameterOption(['--quiet', '-q'])) {
+            $output->setVerbosity(Output::VERBOSITY_QUIET);
+        } elseif ($input->hasParameterOption('-vvv') || $input->hasParameterOption('--verbose=3') || $input->getParameterOption('--verbose') === 3) {
+            $output->setVerbosity(Output::VERBOSITY_DEBUG);
+        } elseif ($input->hasParameterOption('-vv') || $input->hasParameterOption('--verbose=2') || $input->getParameterOption('--verbose') === 2) {
+            $output->setVerbosity(Output::VERBOSITY_VERY_VERBOSE);
+        } elseif ($input->hasParameterOption('-v') || $input->hasParameterOption('--verbose=1') || $input->hasParameterOption('--verbose') || $input->getParameterOption('--verbose')) {
+            $output->setVerbosity(Output::VERBOSITY_VERBOSE);
+        }
+    }
+
+    /**
+     * 执行指令
+     * @access protected
+     * @param Command $command 指令实例
+     * @param Input $input 输入实例
+     * @param Output $output 输出实例
+     * @return int
+     * @throws \Exception
+     */
+    protected function doRunCommand(Command $command, Input $input, Output $output)
+    {
+        return $command->run($input, $output);
+    }
+
+    /**
+     * 获取指令的基础名称
+     * @access protected
+     * @param Input $input
+     * @return string
+     */
+    protected function getCommandName(Input $input): string
+    {
+        return $input->getFirstArgument() ?: '';
+    }
+
+    /**
+     * 获取默认输入定义
+     * @access protected
+     * @return InputDefinition
+     */
+    protected function getDefaultInputDefinition(): InputDefinition
+    {
+        return new InputDefinition([
+            new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
+            new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message'),
+            new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this console version'),
+            new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'),
+            new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'),
+            new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'),
+            new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'),
+            new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'),
+        ]);
+    }
+
+    /**
+     * 获取可能的建议
+     * @access private
+     * @param array $abbrevs
+     * @return string
+     */
+    private function getAbbreviationSuggestions(array $abbrevs): string
+    {
+        return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : '');
+    }
+
+    /**
+     * 返回命名空间部分
+     * @access public
+     * @param string $name 指令
+     * @param int $limit 部分的命名空间的最大数量
+     * @return string
+     */
+    public function extractNamespace(string $name, int $limit = 0): string
+    {
+        $parts = explode(':', $name);
+        array_pop($parts);
+
+        return implode(':', 0 === $limit ? $parts : array_slice($parts, 0, $limit));
+    }
+
+    /**
+     * 查找可替代的建议
+     * @access private
+     * @param string $name
+     * @param array|\Traversable $collection
+     * @return array
+     */
+    private function findAlternatives(string $name, $collection): array
+    {
+        $threshold    = 1e3;
+        $alternatives = [];
+
+        $collectionParts = [];
+        foreach ($collection as $item) {
+            $collectionParts[$item] = explode(':', $item);
+        }
+
+        foreach (explode(':', $name) as $i => $subname) {
+            foreach ($collectionParts as $collectionName => $parts) {
+                $exists = isset($alternatives[$collectionName]);
+                if (!isset($parts[$i]) && $exists) {
+                    $alternatives[$collectionName] += $threshold;
+                    continue;
+                } elseif (!isset($parts[$i])) {
+                    continue;
+                }
+
+                $lev = levenshtein($subname, $parts[$i]);
+                if ($lev <= strlen($subname) / 3 || '' !== $subname && false !== strpos($parts[$i], $subname)) {
+                    $alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev;
+                } elseif ($exists) {
+                    $alternatives[$collectionName] += $threshold;
+                }
+            }
+        }
+
+        foreach ($collection as $item) {
+            $lev = levenshtein($name, $item);
+            if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
+                $alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev;
+            }
+        }
+
+        $alternatives = array_filter($alternatives, function ($lev) use ($threshold) {
+            return $lev < 2 * $threshold;
+        });
+        asort($alternatives);
+
+        return array_keys($alternatives);
+    }
+
+    /**
+     * 返回所有的命名空间
+     * @access private
+     * @param string $name
+     * @return array
+     */
+    private function extractAllNamespaces(string $name): array
+    {
+        $parts      = explode(':', $name, -1);
+        $namespaces = [];
+
+        foreach ($parts as $part) {
+            if (count($namespaces)) {
+                $namespaces[] = end($namespaces) . ':' . $part;
+            } else {
+                $namespaces[] = $part;
+            }
+        }
+
+        return $namespaces;
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/Container.php b/vendor/topthink/framework/src/think/Container.php
new file mode 100755
index 0000000..f286c73
--- /dev/null
+++ b/vendor/topthink/framework/src/think/Container.php
@@ -0,0 +1,561 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+use ArrayAccess;
+use ArrayIterator;
+use Closure;
+use Countable;
+use InvalidArgumentException;
+use IteratorAggregate;
+use Psr\Container\ContainerInterface;
+use ReflectionClass;
+use ReflectionException;
+use ReflectionFunction;
+use ReflectionFunctionAbstract;
+use ReflectionMethod;
+use think\exception\ClassNotFoundException;
+use think\exception\FuncNotFoundException;
+use think\helper\Str;
+use Traversable;
+
+/**
+ * 容器管理类 支持PSR-11
+ */
+class Container implements ContainerInterface, ArrayAccess, IteratorAggregate, Countable
+{
+    /**
+     * 容器对象实例
+     * @var Container|Closure
+     */
+    protected static $instance;
+
+    /**
+     * 容器中的对象实例
+     * @var array
+     */
+    protected $instances = [];
+
+    /**
+     * 容器绑定标识
+     * @var array
+     */
+    protected $bind = [];
+
+    /**
+     * 容器回调
+     * @var array
+     */
+    protected $invokeCallback = [];
+
+    /**
+     * 获取当前容器的实例(单例)
+     * @access public
+     * @return static
+     */
+    public static function getInstance()
+    {
+        if (is_null(static::$instance)) {
+            static::$instance = new static;
+        }
+
+        if (static::$instance instanceof Closure) {
+            return (static::$instance)();
+        }
+
+        return static::$instance;
+    }
+
+    /**
+     * 设置当前容器的实例
+     * @access public
+     * @param object|Closure $instance
+     * @return void
+     */
+    public static function setInstance($instance): void
+    {
+        static::$instance = $instance;
+    }
+
+    /**
+     * 注册一个容器对象回调
+     *
+     * @param string|Closure $abstract
+     * @param Closure|null   $callback
+     * @return void
+     */
+    public function resolving($abstract, Closure $callback = null): void
+    {
+        if ($abstract instanceof Closure) {
+            $this->invokeCallback['*'][] = $abstract;
+            return;
+        }
+
+        $abstract = $this->getAlias($abstract);
+
+        $this->invokeCallback[$abstract][] = $callback;
+    }
+
+    /**
+     * 获取容器中的对象实例 不存在则创建
+     * @template T
+     * @param string|class-string<T> $abstract    类名或者标识
+     * @param array                  $vars        变量
+     * @param bool                   $newInstance 是否每次创建新的实例
+     * @return T|object
+     */
+    public static function pull(string $abstract, array $vars = [], bool $newInstance = false)
+    {
+        return static::getInstance()->make($abstract, $vars, $newInstance);
+    }
+
+    /**
+     * 获取容器中的对象实例
+     * @template T
+     * @param string|class-string<T> $abstract 类名或者标识
+     * @return T|object
+     */
+    public function get($abstract)
+    {
+        if ($this->has($abstract)) {
+            return $this->make($abstract);
+        }
+
+        throw new ClassNotFoundException('class not exists: ' . $abstract, $abstract);
+    }
+
+    /**
+     * 绑定一个类、闭包、实例、接口实现到容器
+     * @access public
+     * @param string|array $abstract 类标识、接口
+     * @param mixed        $concrete 要绑定的类、闭包或者实例
+     * @return $this
+     */
+    public function bind($abstract, $concrete = null)
+    {
+        if (is_array($abstract)) {
+            foreach ($abstract as $key => $val) {
+                $this->bind($key, $val);
+            }
+        } elseif ($concrete instanceof Closure) {
+            $this->bind[$abstract] = $concrete;
+        } elseif (is_object($concrete)) {
+            $this->instance($abstract, $concrete);
+        } else {
+            $abstract = $this->getAlias($abstract);
+            if ($abstract != $concrete) {
+                $this->bind[$abstract] = $concrete;
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * 根据别名获取真实类名
+     * @param  string $abstract
+     * @return string
+     */
+    public function getAlias(string $abstract): string
+    {
+        if (isset($this->bind[$abstract])) {
+            $bind = $this->bind[$abstract];
+
+            if (is_string($bind)) {
+                return $this->getAlias($bind);
+            }
+        }
+
+        return $abstract;
+    }
+
+    /**
+     * 绑定一个类实例到容器
+     * @access public
+     * @param string $abstract 类名或者标识
+     * @param object $instance 类的实例
+     * @return $this
+     */
+    public function instance(string $abstract, $instance)
+    {
+        $abstract = $this->getAlias($abstract);
+
+        $this->instances[$abstract] = $instance;
+
+        return $this;
+    }
+
+    /**
+     * 判断容器中是否存在类及标识
+     * @access public
+     * @param string $abstract 类名或者标识
+     * @return bool
+     */
+    public function bound(string $abstract): bool
+    {
+        return isset($this->bind[$abstract]) || isset($this->instances[$abstract]);
+    }
+
+    /**
+     * 判断容器中是否存在类及标识
+     * @access public
+     * @param string $name 类名或者标识
+     * @return bool
+     */
+    public function has($name): bool
+    {
+        return $this->bound($name);
+    }
+
+    /**
+     * 判断容器中是否存在对象实例
+     * @access public
+     * @param string $abstract 类名或者标识
+     * @return bool
+     */
+    public function exists(string $abstract): bool
+    {
+        $abstract = $this->getAlias($abstract);
+
+        return isset($this->instances[$abstract]);
+    }
+
+    /**
+     * 创建类的实例 已经存在则直接获取
+     * @template T
+     * @param string|class-string<T> $abstract    类名或者标识
+     * @param array                  $vars        变量
+     * @param bool                   $newInstance 是否每次创建新的实例
+     * @return T|object
+     */
+    public function make(string $abstract, array $vars = [], bool $newInstance = false)
+    {
+        $abstract = $this->getAlias($abstract);
+
+        if (isset($this->instances[$abstract]) && !$newInstance) {
+            return $this->instances[$abstract];
+        }
+
+        if (isset($this->bind[$abstract]) && $this->bind[$abstract] instanceof Closure) {
+            $object = $this->invokeFunction($this->bind[$abstract], $vars);
+        } else {
+            $object = $this->invokeClass($abstract, $vars);
+        }
+
+        if (!$newInstance) {
+            $this->instances[$abstract] = $object;
+        }
+
+        return $object;
+    }
+
+    /**
+     * 删除容器中的对象实例
+     * @access public
+     * @param string $name 类名或者标识
+     * @return void
+     */
+    public function delete($name)
+    {
+        $name = $this->getAlias($name);
+
+        if (isset($this->instances[$name])) {
+            unset($this->instances[$name]);
+        }
+    }
+
+    /**
+     * 执行函数或者闭包方法 支持参数调用
+     * @access public
+     * @param string|Closure $function 函数或者闭包
+     * @param array          $vars     参数
+     * @return mixed
+     */
+    public function invokeFunction($function, array $vars = [])
+    {
+        try {
+            $reflect = new ReflectionFunction($function);
+        } catch (ReflectionException $e) {
+            throw new FuncNotFoundException("function not exists: {$function}()", $function, $e);
+        }
+
+        $args = $this->bindParams($reflect, $vars);
+
+        return $function(...$args);
+    }
+
+    /**
+     * 调用反射执行类的方法 支持参数绑定
+     * @access public
+     * @param mixed $method     方法
+     * @param array $vars       参数
+     * @param bool  $accessible 设置是否可访问
+     * @return mixed
+     */
+    public function invokeMethod($method, array $vars = [], bool $accessible = false)
+    {
+        if (is_array($method)) {
+            [$class, $method] = $method;
+
+            $class = is_object($class) ? $class : $this->invokeClass($class);
+        } else {
+            // 静态方法
+            [$class, $method] = explode('::', $method);
+        }
+
+        try {
+            $reflect = new ReflectionMethod($class, $method);
+        } catch (ReflectionException $e) {
+            $class = is_object($class) ? get_class($class) : $class;
+            throw new FuncNotFoundException('method not exists: ' . $class . '::' . $method . '()', "{$class}::{$method}", $e);
+        }
+
+        $args = $this->bindParams($reflect, $vars);
+
+        if ($accessible) {
+            $reflect->setAccessible($accessible);
+        }
+
+        return $reflect->invokeArgs(is_object($class) ? $class : null, $args);
+    }
+
+    /**
+     * 调用反射执行类的方法 支持参数绑定
+     * @access public
+     * @param object $instance 对象实例
+     * @param mixed  $reflect  反射类
+     * @param array  $vars     参数
+     * @return mixed
+     */
+    public function invokeReflectMethod($instance, $reflect, array $vars = [])
+    {
+        $args = $this->bindParams($reflect, $vars);
+
+        return $reflect->invokeArgs($instance, $args);
+    }
+
+    /**
+     * 调用反射执行callable 支持参数绑定
+     * @access public
+     * @param mixed $callable
+     * @param array $vars       参数
+     * @param bool  $accessible 设置是否可访问
+     * @return mixed
+     */
+    public function invoke($callable, array $vars = [], bool $accessible = false)
+    {
+        if ($callable instanceof Closure) {
+            return $this->invokeFunction($callable, $vars);
+        } elseif (is_string($callable) && false === strpos($callable, '::')) {
+            return $this->invokeFunction($callable, $vars);
+        } else {
+            return $this->invokeMethod($callable, $vars, $accessible);
+        }
+    }
+
+    /**
+     * 调用反射执行类的实例化 支持依赖注入
+     * @access public
+     * @param string $class 类名
+     * @param array  $vars  参数
+     * @return mixed
+     */
+    public function invokeClass(string $class, array $vars = [])
+    {
+        try {
+            $reflect = new ReflectionClass($class);
+        } catch (ReflectionException $e) {
+            throw new ClassNotFoundException('class not exists: ' . $class, $class, $e);
+        }
+
+        if ($reflect->hasMethod('__make')) {
+            $method = $reflect->getMethod('__make');
+            if ($method->isPublic() && $method->isStatic()) {
+                $args   = $this->bindParams($method, $vars);
+                $object = $method->invokeArgs(null, $args);
+                $this->invokeAfter($class, $object);
+                return $object;
+            }
+        }
+
+        $constructor = $reflect->getConstructor();
+
+        $args = $constructor ? $this->bindParams($constructor, $vars) : [];
+
+        $object = $reflect->newInstanceArgs($args);
+
+        $this->invokeAfter($class, $object);
+
+        return $object;
+    }
+
+    /**
+     * 执行invokeClass回调
+     * @access protected
+     * @param string $class  对象类名
+     * @param object $object 容器对象实例
+     * @return void
+     */
+    protected function invokeAfter(string $class, $object): void
+    {
+        if (isset($this->invokeCallback['*'])) {
+            foreach ($this->invokeCallback['*'] as $callback) {
+                $callback($object, $this);
+            }
+        }
+
+        if (isset($this->invokeCallback[$class])) {
+            foreach ($this->invokeCallback[$class] as $callback) {
+                $callback($object, $this);
+            }
+        }
+    }
+
+    /**
+     * 绑定参数
+     * @access protected
+     * @param ReflectionFunctionAbstract $reflect 反射类
+     * @param array                      $vars    参数
+     * @return array
+     */
+    protected function bindParams(ReflectionFunctionAbstract $reflect, array $vars = []): array
+    {
+        if ($reflect->getNumberOfParameters() == 0) {
+            return [];
+        }
+
+        // 判断数组类型 数字数组时按顺序绑定参数
+        reset($vars);
+        $type   = key($vars) === 0 ? 1 : 0;
+        $params = $reflect->getParameters();
+        $args   = [];
+
+        foreach ($params as $param) {
+            $name           = $param->getName();
+            $lowerName      = Str::snake($name);
+            $reflectionType = $param->getType();
+
+            if ($param->isVariadic()) {
+                return array_merge($args, array_values($vars));
+            } elseif ($reflectionType && $reflectionType instanceof \ReflectionNamedType && $reflectionType->isBuiltin() === false) {
+                $args[] = $this->getObjectParam($reflectionType->getName(), $vars);
+            } elseif (1 == $type && !empty($vars)) {
+                $args[] = array_shift($vars);
+            } elseif (0 == $type && array_key_exists($name, $vars)) {
+                $args[] = $vars[$name];
+            } elseif (0 == $type && array_key_exists($lowerName, $vars)) {
+                $args[] = $vars[$lowerName];
+            } elseif ($param->isDefaultValueAvailable()) {
+                $args[] = $param->getDefaultValue();
+            } else {
+                throw new InvalidArgumentException('method param miss:' . $name);
+            }
+        }
+
+        return $args;
+    }
+
+    /**
+     * 创建工厂对象实例
+     * @param string $name      工厂类名
+     * @param string $namespace 默认命名空间
+     * @param array  $args
+     * @return mixed
+     * @deprecated
+     * @access public
+     */
+    public static function factory(string $name, string $namespace = '', ...$args)
+    {
+        $class = false !== strpos($name, '\\') ? $name : $namespace . ucwords($name);
+
+        return Container::getInstance()->invokeClass($class, $args);
+    }
+
+    /**
+     * 获取对象类型的参数值
+     * @access protected
+     * @param string $className 类名
+     * @param array  $vars      参数
+     * @return mixed
+     */
+    protected function getObjectParam(string $className, array &$vars)
+    {
+        $array = $vars;
+        $value = array_shift($array);
+
+        if ($value instanceof $className) {
+            $result = $value;
+            array_shift($vars);
+        } else {
+            $result = $this->make($className);
+        }
+
+        return $result;
+    }
+
+    public function __set($name, $value)
+    {
+        $this->bind($name, $value);
+    }
+
+    public function __get($name)
+    {
+        return $this->get($name);
+    }
+
+    public function __isset($name): bool
+    {
+        return $this->exists($name);
+    }
+
+    public function __unset($name)
+    {
+        $this->delete($name);
+    }
+
+    #[\ReturnTypeWillChange]
+    public function offsetExists($key): bool
+    {
+        return $this->exists($key);
+    }
+
+    #[\ReturnTypeWillChange]
+    public function offsetGet($key)
+    {
+        return $this->make($key);
+    }
+
+    #[\ReturnTypeWillChange]
+    public function offsetSet($key, $value)
+    {
+        $this->bind($key, $value);
+    }
+
+    #[\ReturnTypeWillChange]
+    public function offsetUnset($key)
+    {
+        $this->delete($key);
+    }
+
+    //Countable
+    public function count(): int
+    {
+        return count($this->instances);
+    }
+
+    //IteratorAggregate
+    public function getIterator(): Traversable
+    {
+        return new ArrayIterator($this->instances);
+    }
+}
diff --git a/vendor/topthink/framework/src/think/Cookie.php b/vendor/topthink/framework/src/think/Cookie.php
new file mode 100755
index 0000000..04774a6
--- /dev/null
+++ b/vendor/topthink/framework/src/think/Cookie.php
@@ -0,0 +1,232 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+use DateTimeInterface;
+
+/**
+ * Cookie管理类
+ * @package think
+ */
+class Cookie
+{
+    /**
+     * 配置参数
+     * @var array
+     */
+    protected $config = [
+        // cookie 保存时间
+        'expire'   => 0,
+        // cookie 保存路径
+        'path'     => '/',
+        // cookie 有效域名
+        'domain'   => '',
+        //  cookie 启用安全传输
+        'secure'   => false,
+        // httponly设置
+        'httponly' => false,
+        // samesite 设置,支持 'strict' 'lax'
+        'samesite' => '',
+    ];
+
+    /**
+     * Cookie写入数据
+     * @var array
+     */
+    protected $cookie = [];
+
+    /**
+     * 当前Request对象
+     * @var Request
+     */
+    protected $request;
+
+    /**
+     * 构造方法
+     * @access public
+     */
+    public function __construct(Request $request, array $config = [])
+    {
+        $this->request = $request;
+        $this->config  = array_merge($this->config, array_change_key_case($config));
+    }
+
+    public static function __make(Request $request, Config $config)
+    {
+        return new static($request, $config->get('cookie'));
+    }
+
+    /**
+     * 获取cookie
+     * @access public
+     * @param  mixed  $name 数据名称
+     * @param  string $default 默认值
+     * @return mixed
+     */
+    public function get(string $name = '', $default = null)
+    {
+        return $this->request->cookie($name, $default);
+    }
+
+    /**
+     * 是否存在Cookie参数
+     * @access public
+     * @param  string $name 变量名
+     * @return bool
+     */
+    public function has(string $name): bool
+    {
+        return $this->request->has($name, 'cookie');
+    }
+
+    /**
+     * Cookie 设置
+     *
+     * @access public
+     * @param  string $name  cookie名称
+     * @param  string $value cookie值
+     * @param  mixed  $option 可选参数
+     * @return void
+     */
+    public function set(string $name, string $value, $option = null): void
+    {
+        // 参数设置(会覆盖黙认设置)
+        if (!is_null($option)) {
+            if (is_numeric($option) || $option instanceof DateTimeInterface) {
+                $option = ['expire' => $option];
+            }
+
+            $config = array_merge($this->config, array_change_key_case($option));
+        } else {
+            $config = $this->config;
+        }
+
+        if ($config['expire'] instanceof DateTimeInterface) {
+            $expire = $config['expire']->getTimestamp();
+        } else {
+            $expire = !empty($config['expire']) ? time() + intval($config['expire']) : 0;
+        }
+
+        $this->setCookie($name, $value, $expire, $config);
+    }
+
+    /**
+     * Cookie 保存
+     *
+     * @access public
+     * @param  string $name  cookie名称
+     * @param  string $value cookie值
+     * @param  int    $expire 有效期
+     * @param  array  $option 可选参数
+     * @return void
+     */
+    protected function setCookie(string $name, string $value, int $expire, array $option = []): void
+    {
+        $this->cookie[$name] = [$value, $expire, $option];
+    }
+
+    /**
+     * 永久保存Cookie数据
+     * @access public
+     * @param  string $name  cookie名称
+     * @param  string $value cookie值
+     * @param  mixed  $option 可选参数 可能会是 null|integer|string
+     * @return void
+     */
+    public function forever(string $name, string $value = '', $option = null): void
+    {
+        if (is_null($option) || is_numeric($option)) {
+            $option = [];
+        }
+
+        $option['expire'] = 315360000;
+
+        $this->set($name, $value, $option);
+    }
+
+    /**
+     * Cookie删除
+     * @access public
+     * @param  string $name cookie名称
+     * @param  array  $options cookie参数
+     * @return void
+     */
+    public function delete(string $name, array $options = []): void
+    {
+        $config = array_merge($this->config, array_change_key_case($options));
+        $this->setCookie($name, '', time() - 3600, $config);
+    }
+
+    /**
+     * 获取cookie保存数据
+     * @access public
+     * @return array
+     */
+    public function getCookie(): array
+    {
+        return $this->cookie;
+    }
+
+    /**
+     * 保存Cookie
+     * @access public
+     * @return void
+     */
+    public function save(): void
+    {
+        foreach ($this->cookie as $name => $val) {
+            [$value, $expire, $option] = $val;
+
+            $this->saveCookie(
+                $name,
+                $value,
+                $expire,
+                $option['path'],
+                $option['domain'],
+                $option['secure'] ? true : false,
+                $option['httponly'] ? true : false,
+                $option['samesite']
+            );
+        }
+    }
+
+    /**
+     * 保存Cookie
+     * @access public
+     * @param  string $name cookie名称
+     * @param  string $value cookie值
+     * @param  int    $expire cookie过期时间
+     * @param  string $path 有效的服务器路径
+     * @param  string $domain 有效域名/子域名
+     * @param  bool   $secure 是否仅仅通过HTTPS
+     * @param  bool   $httponly 仅可通过HTTP访问
+     * @param  string $samesite 防止CSRF攻击和用户追踪
+     * @return void
+     */
+    protected function saveCookie(string $name, string $value, int $expire, string $path, string $domain, bool $secure, bool $httponly, string $samesite): void
+    {
+        if (version_compare(PHP_VERSION, '7.3.0', '>=')) {
+            setcookie($name, $value, [
+                'expires'  => $expire,
+                'path'     => $path,
+                'domain'   => $domain,
+                'secure'   => $secure,
+                'httponly' => $httponly,
+                'samesite' => $samesite,
+            ]);
+        } else {
+            setcookie($name, $value, $expire, $path, $domain, $secure, $httponly);
+        }
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/Db.php b/vendor/topthink/framework/src/think/Db.php
new file mode 100755
index 0000000..0048874
--- /dev/null
+++ b/vendor/topthink/framework/src/think/Db.php
@@ -0,0 +1,117 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+/**
+ * 数据库管理类
+ * @package think
+ * @property Config $config
+ */
+class Db extends DbManager
+{
+    /**
+     * @param Event  $event
+     * @param Config $config
+     * @param Log    $log
+     * @param Cache  $cache
+     * @return Db
+     * @codeCoverageIgnore
+     */
+    public static function __make(Event $event, Config $config, Log $log, Cache $cache)
+    {
+        $db = new static();
+        $db->setConfig($config);
+        $db->setEvent($event);
+        $db->setLog($log);
+
+        $store = $db->getConfig('cache_store');
+        $db->setCache($cache->store($store));
+        $db->triggerSql();
+
+        return $db;
+    }
+
+    /**
+     * 注入模型对象
+     * @access public
+     * @return void
+     */
+    protected function modelMaker()
+    {
+    }
+
+    /**
+     * 设置配置对象
+     * @access public
+     * @param Config $config 配置对象
+     * @return void
+     */
+    public function setConfig($config): void
+    {
+        $this->config = $config;
+    }
+
+    /**
+     * 获取配置参数
+     * @access public
+     * @param string $name    配置参数
+     * @param mixed  $default 默认值
+     * @return mixed
+     */
+    public function getConfig(string $name = '', $default = null)
+    {
+        if ('' !== $name) {
+            return $this->config->get('database.' . $name, $default);
+        }
+
+        return $this->config->get('database', []);
+    }
+
+    /**
+     * 设置Event对象
+     * @param Event $event
+     */
+    public function setEvent(Event $event): void
+    {
+        $this->event = $event;
+    }
+
+    /**
+     * 注册回调方法
+     * @access public
+     * @param string   $event    事件名
+     * @param callable $callback 回调方法
+     * @return void
+     */
+    public function event(string $event, callable $callback): void
+    {
+        if ($this->event) {
+            $this->event->listen('db.' . $event, $callback);
+        }
+    }
+
+    /**
+     * 触发事件
+     * @access public
+     * @param string $event  事件名
+     * @param mixed  $params 传入参数
+     * @param bool   $once
+     * @return mixed
+     */
+    public function trigger(string $event, $params = null, bool $once = false)
+    {
+        if ($this->event) {
+            return $this->event->trigger('db.' . $event, $params, $once);
+        }
+    }
+}
diff --git a/vendor/topthink/framework/src/think/Env.php b/vendor/topthink/framework/src/think/Env.php
new file mode 100755
index 0000000..9e1b7f4
--- /dev/null
+++ b/vendor/topthink/framework/src/think/Env.php
@@ -0,0 +1,201 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+use ArrayAccess;
+
+/**
+ * Env管理类
+ * @package think
+ */
+class Env implements ArrayAccess
+{
+    /**
+     * 环境变量数据
+     * @var array
+     */
+    protected $data = [];
+
+    /**
+     * 数据转换映射
+     * @var array
+     */
+    protected $convert = [
+        'true'  => true,
+        'false' => false,
+        'off'   => false,
+        'on'    => true,
+    ];
+
+    public function __construct()
+    {
+        $this->data = $_ENV;
+    }
+
+    /**
+     * 读取环境变量定义文件
+     * @access public
+     * @param string $file 环境变量定义文件
+     * @return void
+     */
+    public function load(string $file): void
+    {
+        $env = parse_ini_file($file, true, INI_SCANNER_RAW) ?: [];
+        $this->set($env);
+    }
+
+    /**
+     * 获取环境变量值
+     * @access public
+     * @param string $name    环境变量名
+     * @param mixed  $default 默认值
+     * @return mixed
+     */
+    public function get(string $name = null, $default = null)
+    {
+        if (is_null($name)) {
+            return $this->data;
+        }
+
+        $name = strtoupper(str_replace('.', '_', $name));
+        if (isset($this->data[$name])) {
+            $result = $this->data[$name];
+
+            if (is_string($result) && isset($this->convert[$result])) {
+                return $this->convert[$result];
+            }
+
+            return $result;
+        }
+
+        return $this->getEnv($name, $default);
+    }
+
+    protected function getEnv(string $name, $default = null)
+    {
+        $result = getenv('PHP_' . $name);
+
+        if (false === $result) {
+            return $default;
+        }
+
+        if ('false' === $result) {
+            $result = false;
+        } elseif ('true' === $result) {
+            $result = true;
+        }
+
+        if (!isset($this->data[$name])) {
+            $this->data[$name] = $result;
+        }
+
+        return $result;
+    }
+
+    /**
+     * 设置环境变量值
+     * @access public
+     * @param string|array $env   环境变量
+     * @param mixed        $value 值
+     * @return void
+     */
+    public function set($env, $value = null): void
+    {
+        if (is_array($env)) {
+            $env = array_change_key_case($env, CASE_UPPER);
+
+            foreach ($env as $key => $val) {
+                if (is_array($val)) {
+                    foreach ($val as $k => $v) {
+                        $this->data[$key . '_' . strtoupper($k)] = $v;
+                    }
+                } else {
+                    $this->data[$key] = $val;
+                }
+            }
+        } else {
+            $name = strtoupper(str_replace('.', '_', $env));
+
+            $this->data[$name] = $value;
+        }
+    }
+
+    /**
+     * 检测是否存在环境变量
+     * @access public
+     * @param string $name 参数名
+     * @return bool
+     */
+    public function has(string $name): bool
+    {
+        return !is_null($this->get($name));
+    }
+
+    /**
+     * 设置环境变量
+     * @access public
+     * @param string $name  参数名
+     * @param mixed  $value 值
+     */
+    public function __set(string $name, $value): void
+    {
+        $this->set($name, $value);
+    }
+
+    /**
+     * 获取环境变量
+     * @access public
+     * @param string $name 参数名
+     * @return mixed
+     */
+    public function __get(string $name)
+    {
+        return $this->get($name);
+    }
+
+    /**
+     * 检测是否存在环境变量
+     * @access public
+     * @param string $name 参数名
+     * @return bool
+     */
+    public function __isset(string $name): bool
+    {
+        return $this->has($name);
+    }
+
+    // ArrayAccess
+    #[\ReturnTypeWillChange]
+    public function offsetSet($name, $value): void
+    {
+        $this->set($name, $value);
+    }
+
+    #[\ReturnTypeWillChange]
+    public function offsetExists($name): bool
+    {
+        return $this->__isset($name);
+    }
+
+    #[\ReturnTypeWillChange]
+    public function offsetUnset($name): void
+    {
+        throw new Exception('not support: unset');
+    }
+
+    #[\ReturnTypeWillChange]
+    public function offsetGet($name)
+    {
+        return $this->get($name);
+    }
+}
diff --git a/vendor/topthink/framework/src/think/Event.php b/vendor/topthink/framework/src/think/Event.php
new file mode 100755
index 0000000..3c70aad
--- /dev/null
+++ b/vendor/topthink/framework/src/think/Event.php
@@ -0,0 +1,272 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+use ReflectionClass;
+use ReflectionMethod;
+
+/**
+ * 事件管理类
+ * @package think
+ */
+class Event
+{
+    /**
+     * 监听者
+     * @var array
+     */
+    protected $listener = [];
+
+    /**
+     * 事件别名
+     * @var array
+     */
+    protected $bind = [
+        'AppInit'     => event\AppInit::class,
+        'HttpRun'     => event\HttpRun::class,
+        'HttpEnd'     => event\HttpEnd::class,
+        'RouteLoaded' => event\RouteLoaded::class,
+        'LogWrite'    => event\LogWrite::class,
+        'LogRecord'   => event\LogRecord::class,
+    ];
+
+    /**
+     * 应用对象
+     * @var App
+     */
+    protected $app;
+
+    public function __construct(App $app)
+    {
+        $this->app = $app;
+    }
+
+    /**
+     * 批量注册事件监听
+     * @access public
+     * @param array $events 事件定义
+     * @return $this
+     */
+    public function listenEvents(array $events)
+    {
+        foreach ($events as $event => $listeners) {
+            if (isset($this->bind[$event])) {
+                $event = $this->bind[$event];
+            }
+
+            $this->listener[$event] = array_merge($this->listener[$event] ?? [], $listeners);
+        }
+
+        return $this;
+    }
+
+    /**
+     * 注册事件监听
+     * @access public
+     * @param string $event    事件名称
+     * @param mixed  $listener 监听操作(或者类名)
+     * @param bool   $first    是否优先执行
+     * @return $this
+     */
+    public function listen(string $event, $listener, bool $first = false)
+    {
+        if (isset($this->bind[$event])) {
+            $event = $this->bind[$event];
+        }
+
+        if ($first && isset($this->listener[$event])) {
+            array_unshift($this->listener[$event], $listener);
+        } else {
+            $this->listener[$event][] = $listener;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 是否存在事件监听
+     * @access public
+     * @param string $event 事件名称
+     * @return bool
+     */
+    public function hasListener(string $event): bool
+    {
+        if (isset($this->bind[$event])) {
+            $event = $this->bind[$event];
+        }
+
+        return isset($this->listener[$event]);
+    }
+
+    /**
+     * 移除事件监听
+     * @access public
+     * @param string $event 事件名称
+     * @return void
+     */
+    public function remove(string $event): void
+    {
+        if (isset($this->bind[$event])) {
+            $event = $this->bind[$event];
+        }
+
+        unset($this->listener[$event]);
+    }
+
+    /**
+     * 指定事件别名标识 便于调用
+     * @access public
+     * @param array $events 事件别名
+     * @return $this
+     */
+    public function bind(array $events)
+    {
+        $this->bind = array_merge($this->bind, $events);
+
+        return $this;
+    }
+
+    /**
+     * 注册事件订阅者
+     * @access public
+     * @param mixed $subscriber 订阅者
+     * @return $this
+     */
+    public function subscribe($subscriber)
+    {
+        $subscribers = (array) $subscriber;
+
+        foreach ($subscribers as $subscriber) {
+            if (is_string($subscriber)) {
+                $subscriber = $this->app->make($subscriber);
+            }
+
+            if (method_exists($subscriber, 'subscribe')) {
+                // 手动订阅
+                $subscriber->subscribe($this);
+            } else {
+                // 智能订阅
+                $this->observe($subscriber);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * 自动注册事件观察者
+     * @access public
+     * @param string|object $observer 观察者
+     * @param null|string   $prefix   事件名前缀
+     * @return $this
+     */
+    public function observe($observer, string $prefix = '')
+    {
+        if (is_string($observer)) {
+            $observer = $this->app->make($observer);
+        }
+
+        $reflect = new ReflectionClass($observer);
+        $methods = $reflect->getMethods(ReflectionMethod::IS_PUBLIC);
+
+        if (empty($prefix) && $reflect->hasProperty('eventPrefix')) {
+            $reflectProperty = $reflect->getProperty('eventPrefix');
+            $reflectProperty->setAccessible(true);
+            $prefix = $reflectProperty->getValue($observer);
+        }
+
+        foreach ($methods as $method) {
+            $name = $method->getName();
+            if (0 === strpos($name, 'on')) {
+                $this->listen($prefix . substr($name, 2), [$observer, $name]);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * 触发事件
+     * @access public
+     * @param string|object $event  事件名称
+     * @param mixed         $params 传入参数
+     * @param bool          $once   只获取一个有效返回值
+     * @return mixed
+     */
+    public function trigger($event, $params = null, bool $once = false)
+    {
+        if (is_object($event)) {
+            $params = $event;
+            $event  = get_class($event);
+        }
+
+        if (isset($this->bind[$event])) {
+            $event = $this->bind[$event];
+        }
+
+        $result    = [];
+        $listeners = $this->listener[$event] ?? [];
+
+        if (strpos($event, '.')) {
+            [$prefix, $event] = explode('.', $event, 2);
+            if (isset($this->listener[$prefix . '.*'])) {
+                $listeners = array_merge($listeners, $this->listener[$prefix . '.*']);
+            }
+        }
+
+        $listeners = array_unique($listeners, SORT_REGULAR);
+
+        foreach ($listeners as $key => $listener) {
+            $result[$key] = $this->dispatch($listener, $params);
+
+            if (false === $result[$key] || (!is_null($result[$key]) && $once)) {
+                break;
+            }
+        }
+
+        return $once ? end($result) : $result;
+    }
+
+    /**
+     * 触发事件(只获取一个有效返回值)
+     * @param      $event
+     * @param null $params
+     * @return mixed
+     */
+    public function until($event, $params = null)
+    {
+        return $this->trigger($event, $params, true);
+    }
+
+    /**
+     * 执行事件调度
+     * @access protected
+     * @param mixed $event  事件方法
+     * @param mixed $params 参数
+     * @return mixed
+     */
+    protected function dispatch($event, $params = null)
+    {
+        if (!is_string($event)) {
+            $call = $event;
+        } elseif (strpos($event, '::')) {
+            $call = $event;
+        } else {
+            $obj  = $this->app->make($event);
+            $call = [$obj, 'handle'];
+        }
+
+        return $this->app->invoke($call, [$params]);
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/Exception.php b/vendor/topthink/framework/src/think/Exception.php
new file mode 100755
index 0000000..5cf7954
--- /dev/null
+++ b/vendor/topthink/framework/src/think/Exception.php
@@ -0,0 +1,60 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+/**
+ * 异常基础类
+ * @package think
+ */
+class Exception extends \Exception
+{
+    /**
+     * 保存异常页面显示的额外Debug数据
+     * @var array
+     */
+    protected $data = [];
+
+    /**
+     * 设置异常额外的Debug数据
+     * 数据将会显示为下面的格式
+     *
+     * Exception Data
+     * --------------------------------------------------
+     * Label 1
+     *   key1      value1
+     *   key2      value2
+     * Label 2
+     *   key1      value1
+     *   key2      value2
+     *
+     * @access protected
+     * @param  string $label 数据分类,用于异常页面显示
+     * @param  array  $data  需要显示的数据,必须为关联数组
+     */
+    final protected function setData(string $label, array $data)
+    {
+        $this->data[$label] = $data;
+    }
+
+    /**
+     * 获取异常额外Debug数据
+     * 主要用于输出到异常页面便于调试
+     * @access public
+     * @return array 由setData设置的Debug数据
+     */
+    final public function getData()
+    {
+        return $this->data;
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/Facade.php b/vendor/topthink/framework/src/think/Facade.php
new file mode 100755
index 0000000..9a0e333
--- /dev/null
+++ b/vendor/topthink/framework/src/think/Facade.php
@@ -0,0 +1,98 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+namespace think;
+
+/**
+ * Facade管理类
+ */
+class Facade
+{
+    /**
+     * 始终创建新的对象实例
+     * @var bool
+     */
+    protected static $alwaysNewInstance;
+
+    /**
+     * 创建Facade实例
+     * @static
+     * @access protected
+     * @param  string $class       类名或标识
+     * @param  array  $args        变量
+     * @param  bool   $newInstance 是否每次创建新的实例
+     * @return object
+     */
+    protected static function createFacade(string $class = '', array $args = [], bool $newInstance = false)
+    {
+        $class = $class ?: static::class;
+
+        $facadeClass = static::getFacadeClass();
+
+        if ($facadeClass) {
+            $class = $facadeClass;
+        }
+
+        if (static::$alwaysNewInstance) {
+            $newInstance = true;
+        }
+
+        return Container::getInstance()->make($class, $args, $newInstance);
+    }
+
+    /**
+     * 获取当前Facade对应类名
+     * @access protected
+     * @return string
+     */
+    protected static function getFacadeClass()
+    {}
+
+    /**
+     * 带参数实例化当前Facade类
+     * @access public
+     * @return object
+     */
+    public static function instance(...$args)
+    {
+        if (__CLASS__ != static::class) {
+            return self::createFacade('', $args);
+        }
+    }
+
+    /**
+     * 调用类的实例
+     * @access public
+     * @param  string     $class       类名或者标识
+     * @param  array|true $args        变量
+     * @param  bool       $newInstance 是否每次创建新的实例
+     * @return object
+     */
+    public static function make(string $class, $args = [], $newInstance = false)
+    {
+        if (__CLASS__ != static::class) {
+            return self::__callStatic('make', func_get_args());
+        }
+
+        if (true === $args) {
+            // 总是创建新的实例化对象
+            $newInstance = true;
+            $args        = [];
+        }
+
+        return self::createFacade($class, $args, $newInstance);
+    }
+
+    // 调用实际类的方法
+    public static function __callStatic($method, $params)
+    {
+        return call_user_func_array([static::createFacade(), $method], $params);
+    }
+}
diff --git a/vendor/topthink/framework/src/think/File.php b/vendor/topthink/framework/src/think/File.php
new file mode 100755
index 0000000..59230b5
--- /dev/null
+++ b/vendor/topthink/framework/src/think/File.php
@@ -0,0 +1,204 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+use SplFileInfo;
+use think\exception\FileException;
+
+/**
+ * 文件上传类
+ * @package think
+ */
+class File extends SplFileInfo
+{
+
+    /**
+     * 文件hash规则
+     * @var array
+     */
+    protected $hash = [];
+
+    protected $hashName;
+
+    /**
+     * 保存的文件后缀
+     * @var string
+     */
+    protected $extension;
+
+    public function __construct(string $path, bool $checkPath = true)
+    {
+        if ($checkPath && !is_file($path)) {
+            throw new FileException(sprintf('The file "%s" does not exist', $path));
+        }
+
+        parent::__construct($path);
+    }
+
+    /**
+     * 获取文件的哈希散列值
+     * @access public
+     * @param string $type
+     * @return string
+     */
+    public function hash(string $type = 'sha1'): string
+    {
+        if (!isset($this->hash[$type])) {
+            $this->hash[$type] = hash_file($type, $this->getPathname());
+        }
+
+        return $this->hash[$type];
+    }
+
+    /**
+     * 获取文件的MD5值
+     * @access public
+     * @return string
+     */
+    public function md5(): string
+    {
+        return $this->hash('md5');
+    }
+
+    /**
+     * 获取文件的SHA1值
+     * @access public
+     * @return string
+     */
+    public function sha1(): string
+    {
+        return $this->hash('sha1');
+    }
+
+    /**
+     * 获取文件类型信息
+     * @access public
+     * @return string
+     */
+    public function getMime(): string
+    {
+        $finfo = finfo_open(FILEINFO_MIME_TYPE);
+
+        return finfo_file($finfo, $this->getPathname());
+    }
+
+    /**
+     * 移动文件
+     * @access public
+     * @param string      $directory 保存路径
+     * @param string|null $name      保存的文件名
+     * @return File
+     */
+    public function move(string $directory, string $name = null): File
+    {
+        $target = $this->getTargetFile($directory, $name);
+
+        set_error_handler(function ($type, $msg) use (&$error) {
+            $error = $msg;
+        });
+        $renamed = rename($this->getPathname(), (string) $target);
+        restore_error_handler();
+        if (!$renamed) {
+            throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error)));
+        }
+
+        @chmod((string) $target, 0666 & ~umask());
+
+        return $target;
+    }
+
+    /**
+     * 实例化一个新文件
+     * @param string      $directory
+     * @param null|string $name
+     * @return File
+     */
+    protected function getTargetFile(string $directory, string $name = null): File
+    {
+        if (!is_dir($directory)) {
+            if (false === @mkdir($directory, 0777, true) && !is_dir($directory)) {
+                throw new FileException(sprintf('Unable to create the "%s" directory', $directory));
+            }
+        } elseif (!is_writable($directory)) {
+            throw new FileException(sprintf('Unable to write in the "%s" directory', $directory));
+        }
+
+        $target = rtrim($directory, '/\\') . \DIRECTORY_SEPARATOR . (null === $name ? $this->getBasename() : $this->getName($name));
+
+        return new self($target, false);
+    }
+
+    /**
+     * 获取文件名
+     * @param string $name
+     * @return string
+     */
+    protected function getName(string $name): string
+    {
+        $originalName = str_replace('\\', '/', $name);
+        $pos          = strrpos($originalName, '/');
+        $originalName = false === $pos ? $originalName : substr($originalName, $pos + 1);
+
+        return $originalName;
+    }
+
+    /**
+     * 文件扩展名
+     * @return string
+     */
+    public function extension(): string
+    {
+        return $this->getExtension();
+    }
+
+    /**
+     * 指定保存文件的扩展名
+     * @param string $extension
+     * @return void
+     */
+    public function setExtension(string $extension): void
+    {
+        $this->extension = $extension;
+    }
+
+    /**
+     * 自动生成文件名
+     * @access public
+     * @param string|\Closure $rule
+     * @return string
+     */
+    public function hashName($rule = ''): string
+    {
+        if (!$this->hashName) {
+            if ($rule instanceof \Closure) {
+                $this->hashName = call_user_func_array($rule, [$this]);
+            } else {
+                switch (true) {
+                    case in_array($rule, hash_algos()):
+                        $hash           = $this->hash($rule);
+                        $this->hashName = substr($hash, 0, 2) . DIRECTORY_SEPARATOR . substr($hash, 2);
+                        break;
+                    case is_callable($rule):
+                        $this->hashName = call_user_func($rule);
+                        break;
+                    default:
+                        $this->hashName = date('Ymd') . DIRECTORY_SEPARATOR . md5(microtime(true) . $this->getPathname());
+                        break;
+                }
+            }
+        }
+
+        $extension = $this->extension ?? $this->extension();
+        return $this->hashName . ($extension ? '.' . $extension : '');
+    }
+}
diff --git a/vendor/topthink/framework/src/think/Http.php b/vendor/topthink/framework/src/think/Http.php
new file mode 100755
index 0000000..4e49c88
--- /dev/null
+++ b/vendor/topthink/framework/src/think/Http.php
@@ -0,0 +1,288 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+use think\event\HttpEnd;
+use think\event\HttpRun;
+use think\event\RouteLoaded;
+use think\exception\Handle;
+use Throwable;
+
+/**
+ * Web应用管理类
+ * @package think
+ */
+class Http
+{
+
+    /**
+     * @var App
+     */
+    protected $app;
+
+    /**
+     * 应用名称
+     * @var string
+     */
+    protected $name;
+
+    /**
+     * 应用路径
+     * @var string
+     */
+    protected $path;
+
+    /**
+     * 路由路径
+     * @var string
+     */
+    protected $routePath;
+
+    /**
+     * 是否绑定应用
+     * @var bool
+     */
+    protected $isBind = false;
+
+    public function __construct(App $app)
+    {
+        $this->app = $app;
+
+        $this->routePath = $this->app->getRootPath() . 'route' . DIRECTORY_SEPARATOR;
+    }
+
+    /**
+     * 设置应用名称
+     * @access public
+     * @param string $name 应用名称
+     * @return $this
+     */
+    public function name(string $name)
+    {
+        $this->name = $name;
+        return $this;
+    }
+
+    /**
+     * 获取应用名称
+     * @access public
+     * @return string
+     */
+    public function getName(): string
+    {
+        return $this->name ?: '';
+    }
+
+    /**
+     * 设置应用目录
+     * @access public
+     * @param string $path 应用目录
+     * @return $this
+     */
+    public function path(string $path)
+    {
+        if (substr($path, -1) != DIRECTORY_SEPARATOR) {
+            $path .= DIRECTORY_SEPARATOR;
+        }
+
+        $this->path = $path;
+        return $this;
+    }
+
+    /**
+     * 获取应用路径
+     * @access public
+     * @return string
+     */
+    public function getPath(): string
+    {
+        return $this->path ?: '';
+    }
+
+    /**
+     * 获取路由目录
+     * @access public
+     * @return string
+     */
+    public function getRoutePath(): string
+    {
+        return $this->routePath;
+    }
+
+    /**
+     * 设置路由目录
+     * @access public
+     * @param string $path 路由定义目录
+     */
+    public function setRoutePath(string $path): void
+    {
+        $this->routePath = $path;
+    }
+
+    /**
+     * 设置应用绑定
+     * @access public
+     * @param bool $bind 是否绑定
+     * @return $this
+     */
+    public function setBind(bool $bind = true)
+    {
+        $this->isBind = $bind;
+        return $this;
+    }
+
+    /**
+     * 是否绑定应用
+     * @access public
+     * @return bool
+     */
+    public function isBind(): bool
+    {
+        return $this->isBind;
+    }
+
+    /**
+     * 执行应用程序
+     * @access public
+     * @param Request|null $request
+     * @return Response
+     */
+    public function run(Request $request = null): Response
+    {
+        //初始化
+        $this->initialize();
+
+        //自动创建request对象
+        $request = $request ?? $this->app->make('request', [], true);
+        $this->app->instance('request', $request);
+
+        try {
+            $response = $this->runWithRequest($request);
+        } catch (Throwable $e) {
+            $this->reportException($e);
+
+            $response = $this->renderException($request, $e);
+        }
+
+        return $response;
+    }
+
+    /**
+     * 初始化
+     */
+    protected function initialize()
+    {
+        if (!$this->app->initialized()) {
+            $this->app->initialize();
+        }
+    }
+
+    /**
+     * 执行应用程序
+     * @param Request $request
+     * @return mixed
+     */
+    protected function runWithRequest(Request $request)
+    {
+        // 加载全局中间件
+        $this->loadMiddleware();
+
+        // 监听HttpRun
+        $this->app->event->trigger(HttpRun::class);
+
+        return $this->app->middleware->pipeline()
+            ->send($request)
+            ->then(function ($request) {
+                return $this->dispatchToRoute($request);
+            });
+    }
+
+    protected function dispatchToRoute($request)
+    {
+        $withRoute = $this->app->config->get('app.with_route', true) ? function () {
+            $this->loadRoutes();
+        } : null;
+
+        return $this->app->route->dispatch($request, $withRoute);
+    }
+
+    /**
+     * 加载全局中间件
+     */
+    protected function loadMiddleware(): void
+    {
+        if (is_file($this->app->getBasePath() . 'middleware.php')) {
+            $this->app->middleware->import(include $this->app->getBasePath() . 'middleware.php');
+        }
+    }
+
+    /**
+     * 加载路由
+     * @access protected
+     * @return void
+     */
+    protected function loadRoutes(): void
+    {
+        // 加载路由定义
+        $routePath = $this->getRoutePath();
+
+        if (is_dir($routePath)) {
+            $files = glob($routePath . '*.php');
+            foreach ($files as $file) {
+                include $file;
+            }
+        }
+
+        $this->app->event->trigger(RouteLoaded::class);
+    }
+
+    /**
+     * Report the exception to the exception handler.
+     *
+     * @param Throwable $e
+     * @return void
+     */
+    protected function reportException(Throwable $e)
+    {
+        $this->app->make(Handle::class)->report($e);
+    }
+
+    /**
+     * Render the exception to a response.
+     *
+     * @param Request   $request
+     * @param Throwable $e
+     * @return Response
+     */
+    protected function renderException($request, Throwable $e)
+    {
+        return $this->app->make(Handle::class)->render($request, $e);
+    }
+
+    /**
+     * HttpEnd
+     * @param Response $response
+     * @return void
+     */
+    public function end(Response $response): void
+    {
+        $this->app->event->trigger(HttpEnd::class, $response);
+
+        //执行中间件
+        $this->app->middleware->end($response);
+
+        // 写入日志
+        $this->app->log->save();
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/Lang.php b/vendor/topthink/framework/src/think/Lang.php
new file mode 100755
index 0000000..b891084
--- /dev/null
+++ b/vendor/topthink/framework/src/think/Lang.php
@@ -0,0 +1,290 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+/**
+ * 多语言管理类
+ * @package think
+ */
+class Lang
+{
+    protected $app;
+
+    /**
+     * 配置参数
+     * @var array
+     */
+    protected $config = [
+        // 默认语言
+        'default_lang'    => 'zh-cn',
+        // 允许的语言列表
+        'allow_lang_list' => [],
+        // 是否使用Cookie记录
+        'use_cookie'      => true,
+        // 扩展语言包
+        'extend_list'     => [],
+        // 多语言cookie变量
+        'cookie_var'      => 'think_lang',
+        // 多语言header变量
+        'header_var'      => 'think-lang',
+        // 多语言自动侦测变量名
+        'detect_var'      => 'lang',
+        // Accept-Language转义为对应语言包名称
+        'accept_language' => [
+            'zh-hans-cn' => 'zh-cn',
+        ],
+        // 是否支持语言分组
+        'allow_group'     => false,
+    ];
+
+    /**
+     * 多语言信息
+     * @var array
+     */
+    private $lang = [];
+
+    /**
+     * 当前语言
+     * @var string
+     */
+    private $range = 'zh-cn';
+
+    /**
+     * 构造方法
+     * @access public
+     * @param array $config
+     */
+    public function __construct(App $app, array $config = [])
+    {
+        $this->config = array_merge($this->config, array_change_key_case($config));
+        $this->range  = $this->config['default_lang'];
+        $this->app    = $app;
+    }
+
+    public static function __make(App $app, Config $config)
+    {
+        return new static($app, $config->get('lang'));
+    }
+
+    /**
+     * 获取当前语言配置
+     * @access public
+     * @return array
+     */
+    public function getConfig(): array
+    {
+        return $this->config;
+    }
+
+    /**
+     * 设置当前语言
+     * @access public
+     * @param string $lang 语言
+     * @return void
+     */
+    public function setLangSet(string $lang): void
+    {
+        $this->range = $lang;
+    }
+
+    /**
+     * 获取当前语言
+     * @access public
+     * @return string
+     */
+    public function getLangSet(): string
+    {
+        return $this->range;
+    }
+
+    /**
+     * 获取默认语言
+     * @access public
+     * @return string
+     */
+    public function defaultLangSet()
+    {
+        return $this->config['default_lang'];
+    }
+
+    /**
+     * 切换语言
+     * @access public
+     * @param string $langset 语言
+     * @return void
+     */
+    public function switchLangSet(string $langset)
+    {
+        if (empty($langset)) {
+            return;
+        }
+
+        $this->setLangSet($langset);
+
+        // 加载系统语言包
+        $this->load([
+            $this->app->getThinkPath() . 'lang' . DIRECTORY_SEPARATOR . $langset . '.php',
+        ]);
+
+        // 加载系统语言包
+        $files = glob($this->app->getAppPath() . 'lang' . DIRECTORY_SEPARATOR . $langset . '.*');
+        $this->load($files);
+
+        // 加载扩展(自定义)语言包
+        $list = $this->app->config->get('lang.extend_list', []);
+
+        if (isset($list[$langset])) {
+            $this->load($list[$langset]);
+        }
+    }
+
+    /**
+     * 加载语言定义(不区分大小写)
+     * @access public
+     * @param string|array $file  语言文件
+     * @param string       $range 语言作用域
+     * @return array
+     */
+    public function load($file, $range = ''): array
+    {
+        $range = $range ?: $this->range;
+        if (!isset($this->lang[$range])) {
+            $this->lang[$range] = [];
+        }
+
+        $lang = [];
+
+        foreach ((array) $file as $name) {
+            if (is_file($name)) {
+                $result = $this->parse($name);
+                $lang   = array_change_key_case($result) + $lang;
+            }
+        }
+
+        if (!empty($lang)) {
+            $this->lang[$range] = $lang + $this->lang[$range];
+        }
+
+        return $this->lang[$range];
+    }
+
+    /**
+     * 解析语言文件
+     * @access protected
+     * @param string $file 语言文件名
+     * @return array
+     */
+    protected function parse(string $file): array
+    {
+        $type = pathinfo($file, PATHINFO_EXTENSION);
+
+        switch ($type) {
+            case 'php':
+                $result = include $file;
+                break;
+            case 'yml':
+            case 'yaml':
+                if (function_exists('yaml_parse_file')) {
+                    $result = yaml_parse_file($file);
+                }
+                break;
+            case 'json':
+                $data = file_get_contents($file);
+
+                if (false !== $data) {
+                    $data = json_decode($data, true);
+
+                    if (json_last_error() === JSON_ERROR_NONE) {
+                        $result = $data;
+                    }
+                }
+
+                break;
+        }
+
+        return isset($result) && is_array($result) ? $result : [];
+    }
+
+    /**
+     * 判断是否存在语言定义(不区分大小写)
+     * @access public
+     * @param string|null $name  语言变量
+     * @param string      $range 语言作用域
+     * @return bool
+     */
+    public function has(string $name, string $range = ''): bool
+    {
+        $range = $range ?: $this->range;
+
+        if ($this->config['allow_group'] && strpos($name, '.')) {
+            [$name1, $name2] = explode('.', $name, 2);
+            return isset($this->lang[$range][strtolower($name1)][$name2]);
+        }
+
+        return isset($this->lang[$range][strtolower($name)]);
+    }
+
+    /**
+     * 获取语言定义(不区分大小写)
+     * @access public
+     * @param string|null $name  语言变量
+     * @param array       $vars  变量替换
+     * @param string      $range 语言作用域
+     * @return mixed
+     */
+    public function get(string $name = null, array $vars = [], string $range = '')
+    {
+        $range = $range ?: $this->range;
+
+        if (!isset($this->lang[$range])) {
+            $this->switchLangSet($range);
+        }
+
+        // 空参数返回所有定义
+        if (is_null($name)) {
+            return $this->lang[$range] ?? [];
+        }
+
+        if ($this->config['allow_group'] && strpos($name, '.')) {
+            [$name1, $name2] = explode('.', $name, 2);
+
+            $value = $this->lang[$range][strtolower($name1)][$name2] ?? $name;
+        } else {
+            $value = $this->lang[$range][strtolower($name)] ?? $name;
+        }
+
+        // 变量解析
+        if (!empty($vars) && is_array($vars)) {
+            /**
+             * Notes:
+             * 为了检测的方便,数字索引的判断仅仅是参数数组的第一个元素的key为数字0
+             * 数字索引采用的是系统的 sprintf 函数替换,用法请参考 sprintf 函数
+             */
+            if (key($vars) === 0) {
+                // 数字索引解析
+                array_unshift($vars, $value);
+                $value = call_user_func_array('sprintf', $vars);
+            } else {
+                // 关联索引解析
+                $replace = array_keys($vars);
+                foreach ($replace as &$v) {
+                    $v = "{:{$v}}";
+                }
+                $value = str_replace($replace, $vars, $value);
+            }
+        }
+
+        return $value;
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/Log.php b/vendor/topthink/framework/src/think/Log.php
new file mode 100755
index 0000000..c31210c
--- /dev/null
+++ b/vendor/topthink/framework/src/think/Log.php
@@ -0,0 +1,342 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+use InvalidArgumentException;
+use Psr\Log\LoggerInterface;
+use think\event\LogWrite;
+use think\helper\Arr;
+use think\log\Channel;
+use think\log\ChannelSet;
+
+/**
+ * 日志管理类
+ * @package think
+ * @mixin Channel
+ */
+class Log extends Manager implements LoggerInterface
+{
+    const EMERGENCY = 'emergency';
+    const ALERT     = 'alert';
+    const CRITICAL  = 'critical';
+    const ERROR     = 'error';
+    const WARNING   = 'warning';
+    const NOTICE    = 'notice';
+    const INFO      = 'info';
+    const DEBUG     = 'debug';
+    const SQL       = 'sql';
+
+    protected $namespace = '\\think\\log\\driver\\';
+
+    /**
+     * 默认驱动
+     * @return string|null
+     */
+    public function getDefaultDriver()
+    {
+        return $this->getConfig('default');
+    }
+
+    /**
+     * 获取日志配置
+     * @access public
+     * @param null|string $name    名称
+     * @param mixed       $default 默认值
+     * @return mixed
+     */
+    public function getConfig(string $name = null, $default = null)
+    {
+        if (!is_null($name)) {
+            return $this->app->config->get('log.' . $name, $default);
+        }
+
+        return $this->app->config->get('log');
+    }
+
+    /**
+     * 获取渠道配置
+     * @param string $channel
+     * @param null   $name
+     * @param null   $default
+     * @return array
+     */
+    public function getChannelConfig($channel, $name = null, $default = null)
+    {
+        if ($config = $this->getConfig("channels.{$channel}")) {
+            return Arr::get($config, $name, $default);
+        }
+
+        throw new InvalidArgumentException("Channel [$channel] not found.");
+    }
+
+    /**
+     * driver()的别名
+     * @param string|array $name 渠道名
+     * @return Channel|ChannelSet
+     */
+    public function channel($name = null)
+    {
+        if (is_array($name)) {
+            return new ChannelSet($this, $name);
+        }
+
+        return $this->driver($name);
+    }
+
+    protected function resolveType(string $name)
+    {
+        return $this->getChannelConfig($name, 'type', 'file');
+    }
+
+    public function createDriver(string $name)
+    {
+        $driver = parent::createDriver($name);
+
+        $lazy  = !$this->getChannelConfig($name, "realtime_write", false) && !$this->app->runningInConsole();
+        $allow = array_merge($this->getConfig("level", []), $this->getChannelConfig($name, "level", []));
+
+        return new Channel($name, $driver, $allow, $lazy, $this->app->event);
+    }
+
+    protected function resolveConfig(string $name)
+    {
+        return $this->getChannelConfig($name);
+    }
+
+    /**
+     * 清空日志信息
+     * @access public
+     * @param string|array $channel 日志通道名
+     * @return $this
+     */
+    public function clear($channel = '*')
+    {
+        if ('*' == $channel) {
+            $channel = array_keys($this->drivers);
+        }
+
+        $this->channel($channel)->clear();
+
+        return $this;
+    }
+
+    /**
+     * 关闭本次请求日志写入
+     * @access public
+     * @param string|array $channel 日志通道名
+     * @return $this
+     */
+    public function close($channel = '*')
+    {
+        if ('*' == $channel) {
+            $channel = array_keys($this->drivers);
+        }
+
+        $this->channel($channel)->close();
+
+        return $this;
+    }
+
+    /**
+     * 获取日志信息
+     * @access public
+     * @param string $channel 日志通道名
+     * @return array
+     */
+    public function getLog(string $channel = null): array
+    {
+        return $this->channel($channel)->getLog();
+    }
+
+    /**
+     * 保存日志信息
+     * @access public
+     * @return bool
+     */
+    public function save(): bool
+    {
+        /** @var Channel $channel */
+        foreach ($this->drivers as $channel) {
+            $channel->save();
+        }
+
+        return true;
+    }
+
+    /**
+     * 记录日志信息
+     * @access public
+     * @param mixed  $msg     日志信息
+     * @param string $type    日志级别
+     * @param array  $context 替换内容
+     * @param bool   $lazy
+     * @return $this
+     */
+    public function record($msg, string $type = 'info', array $context = [], bool $lazy = true)
+    {
+        $channel = $this->getConfig('type_channel.' . $type);
+
+        $this->channel($channel)->record($msg, $type, $context, $lazy);
+
+        return $this;
+    }
+
+    /**
+     * 实时写入日志信息
+     * @access public
+     * @param mixed  $msg     调试信息
+     * @param string $type    日志级别
+     * @param array  $context 替换内容
+     * @return $this
+     */
+    public function write($msg, string $type = 'info', array $context = [])
+    {
+        return $this->record($msg, $type, $context, false);
+    }
+
+    /**
+     * 注册日志写入事件监听
+     * @param $listener
+     * @return Event
+     */
+    public function listen($listener)
+    {
+        return $this->app->event->listen(LogWrite::class, $listener);
+    }
+
+    /**
+     * 记录日志信息
+     * @access public
+     * @param string $level   日志级别
+     * @param mixed  $message 日志信息
+     * @param array  $context 替换内容
+     * @return void
+     */
+    public function log($level, $message, array $context = []): void
+    {
+        $this->record($message, $level, $context);
+    }
+
+    /**
+     * 记录emergency信息
+     * @access public
+     * @param mixed $message 日志信息
+     * @param array $context 替换内容
+     * @return void
+     */
+    public function emergency($message, array $context = []): void
+    {
+        $this->log(__FUNCTION__, $message, $context);
+    }
+
+    /**
+     * 记录警报信息
+     * @access public
+     * @param mixed $message 日志信息
+     * @param array $context 替换内容
+     * @return void
+     */
+    public function alert($message, array $context = []): void
+    {
+        $this->log(__FUNCTION__, $message, $context);
+    }
+
+    /**
+     * 记录紧急情况
+     * @access public
+     * @param mixed $message 日志信息
+     * @param array $context 替换内容
+     * @return void
+     */
+    public function critical($message, array $context = []): void
+    {
+        $this->log(__FUNCTION__, $message, $context);
+    }
+
+    /**
+     * 记录错误信息
+     * @access public
+     * @param mixed $message 日志信息
+     * @param array $context 替换内容
+     * @return void
+     */
+    public function error($message, array $context = []): void
+    {
+        $this->log(__FUNCTION__, $message, $context);
+    }
+
+    /**
+     * 记录warning信息
+     * @access public
+     * @param mixed $message 日志信息
+     * @param array $context 替换内容
+     * @return void
+     */
+    public function warning($message, array $context = []): void
+    {
+        $this->log(__FUNCTION__, $message, $context);
+    }
+
+    /**
+     * 记录notice信息
+     * @access public
+     * @param mixed $message 日志信息
+     * @param array $context 替换内容
+     * @return void
+     */
+    public function notice($message, array $context = []): void
+    {
+        $this->log(__FUNCTION__, $message, $context);
+    }
+
+    /**
+     * 记录一般信息
+     * @access public
+     * @param mixed $message 日志信息
+     * @param array $context 替换内容
+     * @return void
+     */
+    public function info($message, array $context = []): void
+    {
+        $this->log(__FUNCTION__, $message, $context);
+    }
+
+    /**
+     * 记录调试信息
+     * @access public
+     * @param mixed $message 日志信息
+     * @param array $context 替换内容
+     * @return void
+     */
+    public function debug($message, array $context = []): void
+    {
+        $this->log(__FUNCTION__, $message, $context);
+    }
+
+    /**
+     * 记录sql信息
+     * @access public
+     * @param mixed $message 日志信息
+     * @param array $context 替换内容
+     * @return void
+     */
+    public function sql($message, array $context = []): void
+    {
+        $this->log(__FUNCTION__, $message, $context);
+    }
+
+    public function __call($method, $parameters)
+    {
+        $this->log($method, ...$parameters);
+    }
+}
diff --git a/vendor/topthink/framework/src/think/Manager.php b/vendor/topthink/framework/src/think/Manager.php
new file mode 100755
index 0000000..ca3f6a5
--- /dev/null
+++ b/vendor/topthink/framework/src/think/Manager.php
@@ -0,0 +1,177 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+use InvalidArgumentException;
+use think\helper\Str;
+
+abstract class Manager
+{
+    /** @var App */
+    protected $app;
+
+    /**
+     * 驱动
+     * @var array
+     */
+    protected $drivers = [];
+
+    /**
+     * 驱动的命名空间
+     * @var string
+     */
+    protected $namespace = null;
+
+    public function __construct(App $app)
+    {
+        $this->app = $app;
+    }
+
+    /**
+     * 获取驱动实例
+     * @param null|string $name
+     * @return mixed
+     */
+    protected function driver(string $name = null)
+    {
+        $name = $name ?: $this->getDefaultDriver();
+
+        if (is_null($name)) {
+            throw new InvalidArgumentException(sprintf(
+                'Unable to resolve NULL driver for [%s].',
+                static::class
+            ));
+        }
+
+        return $this->drivers[$name] = $this->getDriver($name);
+    }
+
+    /**
+     * 获取驱动实例
+     * @param string $name
+     * @return mixed
+     */
+    protected function getDriver(string $name)
+    {
+        return $this->drivers[$name] ?? $this->createDriver($name);
+    }
+
+    /**
+     * 获取驱动类型
+     * @param string $name
+     * @return mixed
+     */
+    protected function resolveType(string $name)
+    {
+        return $name;
+    }
+
+    /**
+     * 获取驱动配置
+     * @param string $name
+     * @return mixed
+     */
+    protected function resolveConfig(string $name)
+    {
+        return $name;
+    }
+
+    /**
+     * 获取驱动类
+     * @param string $type
+     * @return string
+     */
+    protected function resolveClass(string $type): string
+    {
+        if ($this->namespace || false !== strpos($type, '\\')) {
+            $class = false !== strpos($type, '\\') ? $type : $this->namespace . Str::studly($type);
+
+            if (class_exists($class)) {
+                return $class;
+            }
+        }
+
+        throw new InvalidArgumentException("Driver [$type] not supported.");
+    }
+
+    /**
+     * 获取驱动参数
+     * @param $name
+     * @return array
+     */
+    protected function resolveParams($name): array
+    {
+        $config = $this->resolveConfig($name);
+        return [$config];
+    }
+
+    /**
+     * 创建驱动
+     *
+     * @param string $name
+     * @return mixed
+     *
+     */
+    protected function createDriver(string $name)
+    {
+        $type = $this->resolveType($name);
+
+        $method = 'create' . Str::studly($type) . 'Driver';
+
+        $params = $this->resolveParams($name);
+
+        if (method_exists($this, $method)) {
+            return $this->$method(...$params);
+        }
+
+        $class = $this->resolveClass($type);
+
+        return $this->app->invokeClass($class, $params);
+    }
+
+    /**
+     * 移除一个驱动实例
+     *
+     * @param array|string|null $name
+     * @return $this
+     */
+    public function forgetDriver($name = null)
+    {
+        $name = $name ?? $this->getDefaultDriver();
+
+        foreach ((array) $name as $cacheName) {
+            if (isset($this->drivers[$cacheName])) {
+                unset($this->drivers[$cacheName]);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * 默认驱动
+     * @return string|null
+     */
+    abstract public function getDefaultDriver();
+
+    /**
+     * 动态调用
+     * @param string $method
+     * @param array  $parameters
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        return $this->driver()->$method(...$parameters);
+    }
+}
diff --git a/vendor/topthink/framework/src/think/Middleware.php b/vendor/topthink/framework/src/think/Middleware.php
new file mode 100755
index 0000000..a3db0f2
--- /dev/null
+++ b/vendor/topthink/framework/src/think/Middleware.php
@@ -0,0 +1,257 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: Slince <taosikai@yeah.net>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+use Closure;
+use InvalidArgumentException;
+use LogicException;
+use think\exception\Handle;
+use Throwable;
+
+/**
+ * 中间件管理类
+ * @package think
+ */
+class Middleware
+{
+    /**
+     * 中间件执行队列
+     * @var array
+     */
+    protected $queue = [];
+
+    /**
+     * 应用对象
+     * @var App
+     */
+    protected $app;
+
+    public function __construct(App $app)
+    {
+        $this->app = $app;
+    }
+
+    /**
+     * 导入中间件
+     * @access public
+     * @param array  $middlewares
+     * @param string $type 中间件类型
+     * @return void
+     */
+    public function import(array $middlewares = [], string $type = 'global'): void
+    {
+        foreach ($middlewares as $middleware) {
+            $this->add($middleware, $type);
+        }
+    }
+
+    /**
+     * 注册中间件
+     * @access public
+     * @param mixed  $middleware
+     * @param string $type 中间件类型
+     * @return void
+     */
+    public function add($middleware, string $type = 'global'): void
+    {
+        $middleware = $this->buildMiddleware($middleware, $type);
+
+        if (!empty($middleware)) {
+            $this->queue[$type][] = $middleware;
+            $this->queue[$type]   = array_unique($this->queue[$type], SORT_REGULAR);
+        }
+    }
+
+    /**
+     * 注册路由中间件
+     * @access public
+     * @param mixed $middleware
+     * @return void
+     */
+    public function route($middleware): void
+    {
+        $this->add($middleware, 'route');
+    }
+
+    /**
+     * 注册控制器中间件
+     * @access public
+     * @param mixed $middleware
+     * @return void
+     */
+    public function controller($middleware): void
+    {
+        $this->add($middleware, 'controller');
+    }
+
+    /**
+     * 注册中间件到开始位置
+     * @access public
+     * @param mixed  $middleware
+     * @param string $type 中间件类型
+     */
+    public function unshift($middleware, string $type = 'global')
+    {
+        $middleware = $this->buildMiddleware($middleware, $type);
+
+        if (!empty($middleware)) {
+            if (!isset($this->queue[$type])) {
+                $this->queue[$type] = [];
+            }
+
+            array_unshift($this->queue[$type], $middleware);
+        }
+    }
+
+    /**
+     * 获取注册的中间件
+     * @access public
+     * @param string $type 中间件类型
+     * @return array
+     */
+    public function all(string $type = 'global'): array
+    {
+        return $this->queue[$type] ?? [];
+    }
+
+    /**
+     * 调度管道
+     * @access public
+     * @param string $type 中间件类型
+     * @return Pipeline
+     */
+    public function pipeline(string $type = 'global')
+    {
+        return (new Pipeline())
+            ->through(array_map(function ($middleware) {
+                return function ($request, $next) use ($middleware) {
+                    [$call, $params] = $middleware;
+                    if (is_array($call) && is_string($call[0])) {
+                        $call = [$this->app->make($call[0]), $call[1]];
+                    }
+                    $response = call_user_func($call, $request, $next, ...$params);
+
+                    if (!$response instanceof Response) {
+                        throw new LogicException('The middleware must return Response instance');
+                    }
+                    return $response;
+                };
+            }, $this->sortMiddleware($this->queue[$type] ?? [])))
+            ->whenException([$this, 'handleException']);
+    }
+
+    /**
+     * 结束调度
+     * @param Response $response
+     */
+    public function end(Response $response)
+    {
+        foreach ($this->queue as $queue) {
+            foreach ($queue as $middleware) {
+                [$call] = $middleware;
+                if (is_array($call) && is_string($call[0])) {
+                    $instance = $this->app->make($call[0]);
+                    if (method_exists($instance, 'end')) {
+                        $instance->end($response);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * 异常处理
+     * @param Request   $passable
+     * @param Throwable $e
+     * @return Response
+     */
+    public function handleException($passable, Throwable $e)
+    {
+        /** @var Handle $handler */
+        $handler = $this->app->make(Handle::class);
+
+        $handler->report($e);
+
+        return $handler->render($passable, $e);
+    }
+
+    /**
+     * 解析中间件
+     * @access protected
+     * @param mixed  $middleware
+     * @param string $type 中间件类型
+     * @return array
+     */
+    protected function buildMiddleware($middleware, string $type): array
+    {
+        if (is_array($middleware)) {
+            [$middleware, $params] = $middleware;
+        }
+
+        if ($middleware instanceof Closure) {
+            return [$middleware, $params ?? []];
+        }
+
+        if (!is_string($middleware)) {
+            throw new InvalidArgumentException('The middleware is invalid');
+        }
+
+        //中间件别名检查
+        $alias = $this->app->config->get('middleware.alias', []);
+
+        if (isset($alias[$middleware])) {
+            $middleware = $alias[$middleware];
+        }
+
+        if (is_array($middleware)) {
+            $this->import($middleware, $type);
+            return [];
+        }
+
+        return [[$middleware, 'handle'], $params ?? []];
+    }
+
+    /**
+     * 中间件排序
+     * @param array $middlewares
+     * @return array
+     */
+    protected function sortMiddleware(array $middlewares)
+    {
+        $priority = $this->app->config->get('middleware.priority', []);
+        uasort($middlewares, function ($a, $b) use ($priority) {
+            $aPriority = $this->getMiddlewarePriority($priority, $a);
+            $bPriority = $this->getMiddlewarePriority($priority, $b);
+            return $bPriority - $aPriority;
+        });
+
+        return $middlewares;
+    }
+
+    /**
+     * 获取中间件优先级
+     * @param $priority
+     * @param $middleware
+     * @return int
+     */
+    protected function getMiddlewarePriority($priority, $middleware)
+    {
+        [$call] = $middleware;
+        if (is_array($call) && is_string($call[0])) {
+            $index = array_search($call[0], array_reverse($priority));
+            return false === $index ? -1 : $index;
+        }
+        return -1;
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/Pipeline.php b/vendor/topthink/framework/src/think/Pipeline.php
new file mode 100755
index 0000000..77151f3
--- /dev/null
+++ b/vendor/topthink/framework/src/think/Pipeline.php
@@ -0,0 +1,107 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+namespace think;
+
+use Closure;
+use Exception;
+use Throwable;
+
+class Pipeline
+{
+    protected $passable;
+
+    protected $pipes = [];
+
+    protected $exceptionHandler;
+
+    /**
+     * 初始数据
+     * @param $passable
+     * @return $this
+     */
+    public function send($passable)
+    {
+        $this->passable = $passable;
+        return $this;
+    }
+
+    /**
+     * 调用栈
+     * @param $pipes
+     * @return $this
+     */
+    public function through($pipes)
+    {
+        $this->pipes = is_array($pipes) ? $pipes : func_get_args();
+        return $this;
+    }
+
+    /**
+     * 执行
+     * @param Closure $destination
+     * @return mixed
+     */
+    public function then(Closure $destination)
+    {
+        $pipeline = array_reduce(
+            array_reverse($this->pipes),
+            $this->carry(),
+            function ($passable) use ($destination) {
+                try {
+                    return $destination($passable);
+                } catch (Throwable | Exception $e) {
+                    return $this->handleException($passable, $e);
+                }
+            }
+        );
+
+        return $pipeline($this->passable);
+    }
+
+    /**
+     * 设置异常处理器
+     * @param callable $handler
+     * @return $this
+     */
+    public function whenException($handler)
+    {
+        $this->exceptionHandler = $handler;
+        return $this;
+    }
+
+    protected function carry()
+    {
+        return function ($stack, $pipe) {
+            return function ($passable) use ($stack, $pipe) {
+                try {
+                    return $pipe($passable, $stack);
+                } catch (Throwable | Exception $e) {
+                    return $this->handleException($passable, $e);
+                }
+            };
+        };
+    }
+
+    /**
+     * 异常处理
+     * @param $passable
+     * @param $e
+     * @return mixed
+     */
+    protected function handleException($passable, Throwable $e)
+    {
+        if ($this->exceptionHandler) {
+            return call_user_func($this->exceptionHandler, $passable, $e);
+        }
+        throw $e;
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/Request.php b/vendor/topthink/framework/src/think/Request.php
new file mode 100755
index 0000000..99f4f80
--- /dev/null
+++ b/vendor/topthink/framework/src/think/Request.php
@@ -0,0 +1,2178 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+use ArrayAccess;
+use think\facade\Lang;
+use think\file\UploadedFile;
+use think\route\Rule;
+
+/**
+ * 请求管理类
+ * @package think
+ */
+class Request implements ArrayAccess
+{
+    /**
+     * 兼容PATH_INFO获取
+     * @var array
+     */
+    protected $pathinfoFetch = ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'];
+
+    /**
+     * PATHINFO变量名 用于兼容模式
+     * @var string
+     */
+    protected $varPathinfo = 's';
+
+    /**
+     * 请求类型
+     * @var string
+     */
+    protected $varMethod = '_method';
+
+    /**
+     * 表单ajax伪装变量
+     * @var string
+     */
+    protected $varAjax = '_ajax';
+
+    /**
+     * 表单pjax伪装变量
+     * @var string
+     */
+    protected $varPjax = '_pjax';
+
+    /**
+     * 域名根
+     * @var string
+     */
+    protected $rootDomain = '';
+
+    /**
+     * HTTPS代理标识
+     * @var string
+     */
+    protected $httpsAgentName = '';
+
+    /**
+     * 前端代理服务器IP
+     * @var array
+     */
+    protected $proxyServerIp = [];
+
+    /**
+     * 前端代理服务器真实IP头
+     * @var array
+     */
+    protected $proxyServerIpHeader = ['HTTP_X_REAL_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP'];
+
+    /**
+     * 请求类型
+     * @var string
+     */
+    protected $method;
+
+    /**
+     * 域名(含协议及端口)
+     * @var string
+     */
+    protected $domain;
+
+    /**
+     * HOST(含端口)
+     * @var string
+     */
+    protected $host;
+
+    /**
+     * 子域名
+     * @var string
+     */
+    protected $subDomain;
+
+    /**
+     * 泛域名
+     * @var string
+     */
+    protected $panDomain;
+
+    /**
+     * 当前URL地址
+     * @var string
+     */
+    protected $url;
+
+    /**
+     * 基础URL
+     * @var string
+     */
+    protected $baseUrl;
+
+    /**
+     * 当前执行的文件
+     * @var string
+     */
+    protected $baseFile;
+
+    /**
+     * 访问的ROOT地址
+     * @var string
+     */
+    protected $root;
+
+    /**
+     * pathinfo
+     * @var string
+     */
+    protected $pathinfo;
+
+    /**
+     * pathinfo(不含后缀)
+     * @var string
+     */
+    protected $path;
+
+    /**
+     * 当前请求的IP地址
+     * @var string
+     */
+    protected $realIP;
+
+    /**
+     * 当前控制器名
+     * @var string
+     */
+    protected $controller;
+
+    /**
+     * 当前操作名
+     * @var string
+     */
+    protected $action;
+
+    /**
+     * 当前请求参数
+     * @var array
+     */
+    protected $param = [];
+
+    /**
+     * 当前GET参数
+     * @var array
+     */
+    protected $get = [];
+
+    /**
+     * 当前POST参数
+     * @var array
+     */
+    protected $post = [];
+
+    /**
+     * 当前REQUEST参数
+     * @var array
+     */
+    protected $request = [];
+
+    /**
+     * 当前路由对象
+     * @var Rule
+     */
+    protected $rule;
+
+    /**
+     * 当前ROUTE参数
+     * @var array
+     */
+    protected $route = [];
+
+    /**
+     * 中间件传递的参数
+     * @var array
+     */
+    protected $middleware = [];
+
+    /**
+     * 当前PUT参数
+     * @var array
+     */
+    protected $put;
+
+    /**
+     * SESSION对象
+     * @var Session
+     */
+    protected $session;
+
+    /**
+     * COOKIE数据
+     * @var array
+     */
+    protected $cookie = [];
+
+    /**
+     * ENV对象
+     * @var Env
+     */
+    protected $env;
+
+    /**
+     * 当前SERVER参数
+     * @var array
+     */
+    protected $server = [];
+
+    /**
+     * 当前FILE参数
+     * @var array
+     */
+    protected $file = [];
+
+    /**
+     * 当前HEADER参数
+     * @var array
+     */
+    protected $header = [];
+
+    /**
+     * 资源类型定义
+     * @var array
+     */
+    protected $mimeType = [
+        'xml'   => 'application/xml,text/xml,application/x-xml',
+        'json'  => 'application/json,text/x-json,application/jsonrequest,text/json',
+        'js'    => 'text/javascript,application/javascript,application/x-javascript',
+        'css'   => 'text/css',
+        'rss'   => 'application/rss+xml',
+        'yaml'  => 'application/x-yaml,text/yaml',
+        'atom'  => 'application/atom+xml',
+        'pdf'   => 'application/pdf',
+        'text'  => 'text/plain',
+        'image' => 'image/png,image/jpg,image/jpeg,image/pjpeg,image/gif,image/webp,image/*',
+        'csv'   => 'text/csv',
+        'html'  => 'text/html,application/xhtml+xml,*/*',
+    ];
+
+    /**
+     * 当前请求内容
+     * @var string
+     */
+    protected $content;
+
+    /**
+     * 全局过滤规则
+     * @var array
+     */
+    protected $filter;
+
+    /**
+     * php://input内容
+     * @var string
+     */
+    // php://input
+    protected $input;
+
+    /**
+     * 请求安全Key
+     * @var string
+     */
+    protected $secureKey;
+
+    /**
+     * 是否合并Param
+     * @var bool
+     */
+    protected $mergeParam = false;
+
+    /**
+     * 架构函数
+     * @access public
+     */
+    public function __construct()
+    {
+        // 保存 php://input
+        $this->input = file_get_contents('php://input');
+    }
+
+    public static function __make(App $app)
+    {
+        $request = new static();
+
+        if (function_exists('apache_request_headers') && $result = apache_request_headers()) {
+            $header = $result;
+        } else {
+            $header = [];
+            $server = $_SERVER;
+            foreach ($server as $key => $val) {
+                if (0 === strpos($key, 'HTTP_')) {
+                    $key          = str_replace('_', '-', strtolower(substr($key, 5)));
+                    $header[$key] = $val;
+                }
+            }
+            if (isset($server['CONTENT_TYPE'])) {
+                $header['content-type'] = $server['CONTENT_TYPE'];
+            }
+            if (isset($server['CONTENT_LENGTH'])) {
+                $header['content-length'] = $server['CONTENT_LENGTH'];
+            }
+        }
+
+        $request->header = array_change_key_case($header);
+        $request->server = $_SERVER;
+        $request->env    = $app->env;
+
+        $inputData = $request->getInputData($request->input);
+
+        $request->get     = $_GET;
+        $request->post    = $_POST ?: $inputData;
+        $request->put     = $inputData;
+        $request->request = $_REQUEST;
+        $request->cookie  = $_COOKIE;
+        $request->file    = $_FILES ?? [];
+
+        return $request;
+    }
+
+    /**
+     * 设置当前包含协议的域名
+     * @access public
+     * @param  string $domain 域名
+     * @return $this
+     */
+    public function setDomain(string $domain)
+    {
+        $this->domain = $domain;
+        return $this;
+    }
+
+    /**
+     * 获取当前包含协议的域名
+     * @access public
+     * @param  bool $port 是否需要去除端口号
+     * @return string
+     */
+    public function domain(bool $port = false): string
+    {
+        return $this->scheme() . '://' . $this->host($port);
+    }
+
+    /**
+     * 获取当前根域名
+     * @access public
+     * @return string
+     */
+    public function rootDomain(): string
+    {
+        $root = $this->rootDomain;
+
+        if (!$root) {
+            $item  = explode('.', $this->host());
+            $count = count($item);
+            $root  = $count > 1 ? $item[$count - 2] . '.' . $item[$count - 1] : $item[0];
+        }
+
+        return $root;
+    }
+
+    /**
+     * 设置当前泛域名的值
+     * @access public
+     * @param  string $domain 域名
+     * @return $this
+     */
+    public function setSubDomain(string $domain)
+    {
+        $this->subDomain = $domain;
+        return $this;
+    }
+
+    /**
+     * 获取当前子域名
+     * @access public
+     * @return string
+     */
+    public function subDomain(): string
+    {
+        if (is_null($this->subDomain)) {
+            // 获取当前主域名
+            $rootDomain = $this->rootDomain();
+
+            if ($rootDomain) {
+                $sub             = stristr($this->host(), $rootDomain, true);
+                $this->subDomain = $sub ? rtrim($sub, '.') : '';
+            } else {
+                $this->subDomain = '';
+            }
+        }
+
+        return $this->subDomain;
+    }
+
+    /**
+     * 设置当前泛域名的值
+     * @access public
+     * @param  string $domain 域名
+     * @return $this
+     */
+    public function setPanDomain(string $domain)
+    {
+        $this->panDomain = $domain;
+        return $this;
+    }
+
+    /**
+     * 获取当前泛域名的值
+     * @access public
+     * @return string
+     */
+    public function panDomain(): string
+    {
+        return $this->panDomain ?: '';
+    }
+
+    /**
+     * 设置当前完整URL 包括QUERY_STRING
+     * @access public
+     * @param  string $url URL地址
+     * @return $this
+     */
+    public function setUrl(string $url)
+    {
+        $this->url = $url;
+        return $this;
+    }
+
+    /**
+     * 获取当前完整URL 包括QUERY_STRING
+     * @access public
+     * @param  bool $complete 是否包含完整域名
+     * @return string
+     */
+    public function url(bool $complete = false): string
+    {
+        if ($this->url) {
+            $url = $this->url;
+        } elseif ($this->server('HTTP_X_REWRITE_URL')) {
+            $url = $this->server('HTTP_X_REWRITE_URL');
+        } elseif ($this->server('REQUEST_URI')) {
+            $url = $this->server('REQUEST_URI');
+        } elseif ($this->server('ORIG_PATH_INFO')) {
+            $url = $this->server('ORIG_PATH_INFO') . (!empty($this->server('QUERY_STRING')) ? '?' . $this->server('QUERY_STRING') : '');
+        } elseif (isset($_SERVER['argv'][1])) {
+            $url = $_SERVER['argv'][1];
+        } else {
+            $url = '';
+        }
+
+        return $complete ? $this->domain() . $url : $url;
+    }
+
+    /**
+     * 设置当前URL 不含QUERY_STRING
+     * @access public
+     * @param  string $url URL地址
+     * @return $this
+     */
+    public function setBaseUrl(string $url)
+    {
+        $this->baseUrl = $url;
+        return $this;
+    }
+
+    /**
+     * 获取当前URL 不含QUERY_STRING
+     * @access public
+     * @param  bool $complete 是否包含完整域名
+     * @return string
+     */
+    public function baseUrl(bool $complete = false): string
+    {
+        if (!$this->baseUrl) {
+            $str           = $this->url();
+            $this->baseUrl = strpos($str, '?') ? strstr($str, '?', true) : $str;
+        }
+
+        return $complete ? $this->domain() . $this->baseUrl : $this->baseUrl;
+    }
+
+    /**
+     * 获取当前执行的文件 SCRIPT_NAME
+     * @access public
+     * @param  bool $complete 是否包含完整域名
+     * @return string
+     */
+    public function baseFile(bool $complete = false): string
+    {
+        if (!$this->baseFile) {
+            $url = '';
+            if (!$this->isCli()) {
+                $script_name = basename($this->server('SCRIPT_FILENAME'));
+                if (basename($this->server('SCRIPT_NAME')) === $script_name) {
+                    $url = $this->server('SCRIPT_NAME');
+                } elseif (basename($this->server('PHP_SELF')) === $script_name) {
+                    $url = $this->server('PHP_SELF');
+                } elseif (basename($this->server('ORIG_SCRIPT_NAME')) === $script_name) {
+                    $url = $this->server('ORIG_SCRIPT_NAME');
+                } elseif (($pos = strpos($this->server('PHP_SELF'), '/' . $script_name)) !== false) {
+                    $url = substr($this->server('SCRIPT_NAME'), 0, $pos) . '/' . $script_name;
+                } elseif ($this->server('DOCUMENT_ROOT') && strpos($this->server('SCRIPT_FILENAME'), $this->server('DOCUMENT_ROOT')) === 0) {
+                    $url = str_replace('\\', '/', str_replace($this->server('DOCUMENT_ROOT'), '', $this->server('SCRIPT_FILENAME')));
+                }
+            }
+            $this->baseFile = $url;
+        }
+
+        return $complete ? $this->domain() . $this->baseFile : $this->baseFile;
+    }
+
+    /**
+     * 设置URL访问根地址
+     * @access public
+     * @param  string $url URL地址
+     * @return $this
+     */
+    public function setRoot(string $url)
+    {
+        $this->root = $url;
+        return $this;
+    }
+
+    /**
+     * 获取URL访问根地址
+     * @access public
+     * @param  bool $complete 是否包含完整域名
+     * @return string
+     */
+    public function root(bool $complete = false): string
+    {
+        if (!$this->root) {
+            $file = $this->baseFile();
+            if ($file && 0 !== strpos($this->url(), $file)) {
+                $file = str_replace('\\', '/', dirname($file));
+            }
+            $this->root = rtrim($file, '/');
+        }
+
+        return $complete ? $this->domain() . $this->root : $this->root;
+    }
+
+    /**
+     * 获取URL访问根目录
+     * @access public
+     * @return string
+     */
+    public function rootUrl(): string
+    {
+        $base = $this->root();
+        $root = strpos($base, '.') ? ltrim(dirname($base), DIRECTORY_SEPARATOR) : $base;
+
+        if ('' != $root) {
+            $root = '/' . ltrim($root, '/');
+        }
+
+        return $root;
+    }
+
+    /**
+     * 设置当前请求的pathinfo
+     * @access public
+     * @param  string $pathinfo
+     * @return $this
+     */
+    public function setPathinfo(string $pathinfo)
+    {
+        $this->pathinfo = $pathinfo;
+        return $this;
+    }
+
+    /**
+     * 获取当前请求URL的pathinfo信息(含URL后缀)
+     * @access public
+     * @return string
+     */
+    public function pathinfo(): string
+    {
+        if (is_null($this->pathinfo)) {
+            if (isset($_GET[$this->varPathinfo])) {
+                // 判断URL里面是否有兼容模式参数
+                $pathinfo = $_GET[$this->varPathinfo];
+                unset($_GET[$this->varPathinfo]);
+                unset($this->get[$this->varPathinfo]);
+            } elseif ($this->server('PATH_INFO')) {
+                $pathinfo = $this->server('PATH_INFO');
+            } elseif (false !== strpos(PHP_SAPI, 'cli')) {
+                $pathinfo = strpos($this->server('REQUEST_URI'), '?') ? strstr($this->server('REQUEST_URI'), '?', true) : $this->server('REQUEST_URI');
+            }
+
+            // 分析PATHINFO信息
+            if (!isset($pathinfo)) {
+                foreach ($this->pathinfoFetch as $type) {
+                    if ($this->server($type)) {
+                        $pathinfo = (0 === strpos($this->server($type), $this->server('SCRIPT_NAME'))) ?
+                        substr($this->server($type), strlen($this->server('SCRIPT_NAME'))) : $this->server($type);
+                        break;
+                    }
+                }
+            }
+
+            if (!empty($pathinfo)) {
+                unset($this->get[$pathinfo], $this->request[$pathinfo]);
+            }
+
+            $this->pathinfo = empty($pathinfo) || '/' == $pathinfo ? '' : ltrim($pathinfo, '/');
+        }
+
+        return $this->pathinfo;
+    }
+
+    /**
+     * 当前URL的访问后缀
+     * @access public
+     * @return string
+     */
+    public function ext(): string
+    {
+        return pathinfo($this->pathinfo(), PATHINFO_EXTENSION);
+    }
+
+    /**
+     * 获取当前请求的时间
+     * @access public
+     * @param  bool $float 是否使用浮点类型
+     * @return integer|float
+     */
+    public function time(bool $float = false)
+    {
+        return $float ? $this->server('REQUEST_TIME_FLOAT') : $this->server('REQUEST_TIME');
+    }
+
+    /**
+     * 当前请求的资源类型
+     * @access public
+     * @return string
+     */
+    public function type(): string
+    {
+        $accept = $this->server('HTTP_ACCEPT');
+
+        if (empty($accept)) {
+            return '';
+        }
+
+        foreach ($this->mimeType as $key => $val) {
+            $array = explode(',', $val);
+            foreach ($array as $k => $v) {
+                if (stristr($accept, $v)) {
+                    return $key;
+                }
+            }
+        }
+
+        return '';
+    }
+
+    /**
+     * 设置资源类型
+     * @access public
+     * @param  string|array $type 资源类型名
+     * @param  string       $val 资源类型
+     * @return void
+     */
+    public function mimeType($type, $val = ''): void
+    {
+        if (is_array($type)) {
+            $this->mimeType = array_merge($this->mimeType, $type);
+        } else {
+            $this->mimeType[$type] = $val;
+        }
+    }
+
+    /**
+     * 设置请求类型
+     * @access public
+     * @param  string $method 请求类型
+     * @return $this
+     */
+    public function setMethod(string $method)
+    {
+        $this->method = strtoupper($method);
+        return $this;
+    }
+
+    /**
+     * 当前的请求类型
+     * @access public
+     * @param  bool $origin 是否获取原始请求类型
+     * @return string
+     */
+    public function method(bool $origin = false): string
+    {
+        if ($origin) {
+            // 获取原始请求类型
+            return $this->server('REQUEST_METHOD') ?: 'GET';
+        } elseif (!$this->method) {
+            if (isset($this->post[$this->varMethod])) {
+                $method = strtolower($this->post[$this->varMethod]);
+                if (in_array($method, ['get', 'post', 'put', 'patch', 'delete'])) {
+                    $this->method    = strtoupper($method);
+                    $this->{$method} = $this->post;
+                } else {
+                    $this->method = 'POST';
+                }
+                unset($this->post[$this->varMethod]);
+            } elseif ($this->server('HTTP_X_HTTP_METHOD_OVERRIDE')) {
+                $this->method = strtoupper($this->server('HTTP_X_HTTP_METHOD_OVERRIDE'));
+            } else {
+                $this->method = $this->server('REQUEST_METHOD') ?: 'GET';
+            }
+        }
+
+        return $this->method;
+    }
+
+    /**
+     * 是否为GET请求
+     * @access public
+     * @return bool
+     */
+    public function isGet(): bool
+    {
+        return $this->method() == 'GET';
+    }
+
+    /**
+     * 是否为POST请求
+     * @access public
+     * @return bool
+     */
+    public function isPost(): bool
+    {
+        return $this->method() == 'POST';
+    }
+
+    /**
+     * 是否为PUT请求
+     * @access public
+     * @return bool
+     */
+    public function isPut(): bool
+    {
+        return $this->method() == 'PUT';
+    }
+
+    /**
+     * 是否为DELTE请求
+     * @access public
+     * @return bool
+     */
+    public function isDelete(): bool
+    {
+        return $this->method() == 'DELETE';
+    }
+
+    /**
+     * 是否为HEAD请求
+     * @access public
+     * @return bool
+     */
+    public function isHead(): bool
+    {
+        return $this->method() == 'HEAD';
+    }
+
+    /**
+     * 是否为PATCH请求
+     * @access public
+     * @return bool
+     */
+    public function isPatch(): bool
+    {
+        return $this->method() == 'PATCH';
+    }
+
+    /**
+     * 是否为OPTIONS请求
+     * @access public
+     * @return bool
+     */
+    public function isOptions(): bool
+    {
+        return $this->method() == 'OPTIONS';
+    }
+
+    /**
+     * 是否为cli
+     * @access public
+     * @return bool
+     */
+    public function isCli(): bool
+    {
+        return PHP_SAPI == 'cli';
+    }
+
+    /**
+     * 是否为cgi
+     * @access public
+     * @return bool
+     */
+    public function isCgi(): bool
+    {
+        return strpos(PHP_SAPI, 'cgi') === 0;
+    }
+
+    /**
+     * 获取当前请求的参数
+     * @access public
+     * @param  string|array $name 变量名
+     * @param  mixed        $default 默认值
+     * @param  string|array $filter 过滤方法
+     * @return mixed
+     */
+    public function param($name = '', $default = null, $filter = '')
+    {
+        if (empty($this->mergeParam)) {
+            $method = $this->method(true);
+
+            // 自动获取请求变量
+            switch ($method) {
+                case 'POST':
+                    $vars = $this->post(false);
+                    break;
+                case 'PUT':
+                case 'DELETE':
+                case 'PATCH':
+                    $vars = $this->put(false);
+                    break;
+                default:
+                    $vars = [];
+            }
+
+            // 当前请求参数和URL地址中的参数合并
+            $this->param = array_merge($this->param, $this->get(false), $vars, $this->route(false));
+
+            $this->mergeParam = true;
+        }
+
+        if (is_array($name)) {
+            return $this->only($name, $this->param, $filter);
+        }
+
+        return $this->input($this->param, $name, $default, $filter);
+    }
+
+    /**
+     * 获取包含文件在内的请求参数
+     * @access public
+     * @param  string|array $name 变量名
+     * @param  string|array $filter 过滤方法
+     * @return mixed
+     */
+    public function all($name = '', $filter = '')
+    {
+        $data = array_merge($this->param(), $this->file() ?: []);
+
+        if (is_array($name)) {
+            $data = $this->only($name, $data, $filter);
+        } elseif ($name) {
+            $data = $data[$name] ?? null;
+        }
+
+        return $data;
+    }
+
+    /**
+     * 设置路由变量
+     * @access public
+     * @param  Rule $rule 路由对象
+     * @return $this
+     */
+    public function setRule(Rule $rule)
+    {
+        $this->rule = $rule;
+        return $this;
+    }
+
+    /**
+     * 获取当前路由对象
+     * @access public
+     * @return Rule|null
+     */
+    public function rule()
+    {
+        return $this->rule;
+    }
+
+    /**
+     * 设置路由变量
+     * @access public
+     * @param  array $route 路由变量
+     * @return $this
+     */
+    public function setRoute(array $route)
+    {
+        $this->route      = array_merge($this->route, $route);
+        $this->mergeParam = false;
+        return $this;
+    }
+
+    /**
+     * 获取路由参数
+     * @access public
+     * @param  string|array $name 变量名
+     * @param  mixed        $default 默认值
+     * @param  string|array $filter 过滤方法
+     * @return mixed
+     */
+    public function route($name = '', $default = null, $filter = '')
+    {
+        if (is_array($name)) {
+            return $this->only($name, $this->route, $filter);
+        }
+
+        return $this->input($this->route, $name, $default, $filter);
+    }
+
+    /**
+     * 获取GET参数
+     * @access public
+     * @param  string|array $name 变量名
+     * @param  mixed        $default 默认值
+     * @param  string|array $filter 过滤方法
+     * @return mixed
+     */
+    public function get($name = '', $default = null, $filter = '')
+    {
+        if (is_array($name)) {
+            return $this->only($name, $this->get, $filter);
+        }
+
+        return $this->input($this->get, $name, $default, $filter);
+    }
+
+    /**
+     * 获取中间件传递的参数
+     * @access public
+     * @param  mixed $name 变量名
+     * @param  mixed $default 默认值
+     * @return mixed
+     */
+    public function middleware($name, $default = null)
+    {
+        return $this->middleware[$name] ?? $default;
+    }
+
+    /**
+     * 获取POST参数
+     * @access public
+     * @param  string|array $name 变量名
+     * @param  mixed        $default 默认值
+     * @param  string|array $filter 过滤方法
+     * @return mixed
+     */
+    public function post($name = '', $default = null, $filter = '')
+    {
+        if (is_array($name)) {
+            return $this->only($name, $this->post, $filter);
+        }
+
+        return $this->input($this->post, $name, $default, $filter);
+    }
+
+    /**
+     * 获取PUT参数
+     * @access public
+     * @param  string|array $name 变量名
+     * @param  mixed        $default 默认值
+     * @param  string|array $filter 过滤方法
+     * @return mixed
+     */
+    public function put($name = '', $default = null, $filter = '')
+    {
+        if (is_array($name)) {
+            return $this->only($name, $this->put, $filter);
+        }
+
+        return $this->input($this->put, $name, $default, $filter);
+    }
+
+    protected function getInputData($content): array
+    {
+        $contentType = $this->contentType();
+        if ('application/x-www-form-urlencoded' == $contentType) {
+            parse_str($content, $data);
+            return $data;
+        } elseif (false !== strpos($contentType, 'json')) {
+            return (array) json_decode($content, true);
+        }
+
+        return [];
+    }
+
+    /**
+     * 设置获取DELETE参数
+     * @access public
+     * @param  mixed        $name 变量名
+     * @param  mixed        $default 默认值
+     * @param  string|array $filter 过滤方法
+     * @return mixed
+     */
+    public function delete($name = '', $default = null, $filter = '')
+    {
+        return $this->put($name, $default, $filter);
+    }
+
+    /**
+     * 设置获取PATCH参数
+     * @access public
+     * @param  mixed        $name 变量名
+     * @param  mixed        $default 默认值
+     * @param  string|array $filter 过滤方法
+     * @return mixed
+     */
+    public function patch($name = '', $default = null, $filter = '')
+    {
+        return $this->put($name, $default, $filter);
+    }
+
+    /**
+     * 获取request变量
+     * @access public
+     * @param  string|array $name 数据名称
+     * @param  mixed        $default 默认值
+     * @param  string|array $filter 过滤方法
+     * @return mixed
+     */
+    public function request($name = '', $default = null, $filter = '')
+    {
+        if (is_array($name)) {
+            return $this->only($name, $this->request, $filter);
+        }
+
+        return $this->input($this->request, $name, $default, $filter);
+    }
+
+    /**
+     * 获取环境变量
+     * @access public
+     * @param  string $name 数据名称
+     * @param  string $default 默认值
+     * @return mixed
+     */
+    public function env(string $name = '', string $default = null)
+    {
+        if (empty($name)) {
+            return $this->env->get();
+        } else {
+            $name = strtoupper($name);
+        }
+
+        return $this->env->get($name, $default);
+    }
+
+    /**
+     * 获取session数据
+     * @access public
+     * @param  string $name 数据名称
+     * @param  string $default 默认值
+     * @return mixed
+     */
+    public function session(string $name = '', $default = null)
+    {
+        if ('' === $name) {
+            return $this->session->all();
+        }
+        return $this->session->get($name, $default);
+    }
+
+    /**
+     * 获取cookie参数
+     * @access public
+     * @param  mixed        $name 数据名称
+     * @param  string       $default 默认值
+     * @param  string|array $filter 过滤方法
+     * @return mixed
+     */
+    public function cookie(string $name = '', $default = null, $filter = '')
+    {
+        if (!empty($name)) {
+            $data = $this->getData($this->cookie, $name, $default);
+        } else {
+            $data = $this->cookie;
+        }
+
+        // 解析过滤器
+        $filter = $this->getFilter($filter, $default);
+
+        if (is_array($data)) {
+            array_walk_recursive($data, [$this, 'filterValue'], $filter);
+        } else {
+            $this->filterValue($data, $name, $filter);
+        }
+
+        return $data;
+    }
+
+    /**
+     * 获取server参数
+     * @access public
+     * @param  string $name 数据名称
+     * @param  string $default 默认值
+     * @return mixed
+     */
+    public function server(string $name = '', string $default = '')
+    {
+        if (empty($name)) {
+            return $this->server;
+        } else {
+            $name = strtoupper($name);
+        }
+
+        return $this->server[$name] ?? $default;
+    }
+
+    /**
+     * 获取上传的文件信息
+     * @access public
+     * @param  string $name 名称
+     * @return null|array|UploadedFile
+     */
+    public function file(string $name = '')
+    {
+        $files = $this->file;
+        if (!empty($files)) {
+            if (strpos($name, '.')) {
+                [$name, $sub] = explode('.', $name);
+            }
+
+            // 处理上传文件
+            $array = $this->dealUploadFile($files, $name);
+
+            if ('' === $name) {
+                // 获取全部文件
+                return $array;
+            } elseif (isset($sub) && isset($array[$name][$sub])) {
+                return $array[$name][$sub];
+            } elseif (isset($array[$name])) {
+                return $array[$name];
+            }
+        }
+    }
+
+    protected function dealUploadFile(array $files, string $name): array
+    {
+        $array = [];
+        foreach ($files as $key => $file) {
+            if (is_array($file['name'])) {
+                $item  = [];
+                $keys  = array_keys($file);
+                $count = count($file['name']);
+
+                for ($i = 0; $i < $count; $i++) {
+                    if ($file['error'][$i] > 0) {
+                        if ($name == $key) {
+                            $this->throwUploadFileError($file['error'][$i]);
+                        } else {
+                            continue;
+                        }
+                    }
+
+                    $temp['key'] = $key;
+
+                    foreach ($keys as $_key) {
+                        $temp[$_key] = $file[$_key][$i];
+                    }
+
+                    $item[] = new UploadedFile($temp['tmp_name'], $temp['name'], $temp['type'], $temp['error']);
+                }
+
+                $array[$key] = $item;
+            } else {
+                if ($file instanceof File) {
+                    $array[$key] = $file;
+                } else {
+                    if ($file['error'] > 0) {
+                        if ($key == $name) {
+                            $this->throwUploadFileError($file['error']);
+                        } else {
+                            continue;
+                        }
+                    }
+
+                    $array[$key] = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['error']);
+                }
+            }
+        }
+
+        return $array;
+    }
+
+    protected function throwUploadFileError($error)
+    {
+        static $fileUploadErrors = [
+            1 => 'upload File size exceeds the maximum value',
+            2 => 'upload File size exceeds the maximum value',
+            3 => 'only the portion of file is uploaded',
+            4 => 'no file to uploaded',
+            6 => 'upload temp dir not found',
+            7 => 'file write error',
+        ];
+
+        $msg = Lang::get($fileUploadErrors[$error]);
+        throw new Exception($msg, $error);
+    }
+
+    /**
+     * 设置或者获取当前的Header
+     * @access public
+     * @param  string $name header名称
+     * @param  string $default 默认值
+     * @return string|array|null
+     */
+    public function header(string $name = '', string $default = null)
+    {
+        if ('' === $name) {
+            return $this->header;
+        }
+
+        $name = str_replace('_', '-', strtolower($name));
+
+        return $this->header[$name] ?? $default;
+    }
+
+    /**
+     * 获取变量 支持过滤和默认值
+     * @access public
+     * @param  array        $data 数据源
+     * @param  string|false $name 字段名
+     * @param  mixed        $default 默认值
+     * @param  string|array $filter 过滤函数
+     * @return mixed
+     */
+    public function input(array $data = [], $name = '', $default = null, $filter = '')
+    {
+        if (false === $name) {
+            // 获取原始数据
+            return $data;
+        }
+
+        $name = (string) $name;
+        if ('' != $name) {
+            // 解析name
+            if (strpos($name, '/')) {
+                [$name, $type] = explode('/', $name);
+            }
+
+            $data = $this->getData($data, $name);
+
+            if (is_null($data)) {
+                return $default;
+            }
+
+            if (is_object($data)) {
+                return $data;
+            }
+        }
+
+        $data = $this->filterData($data, $filter, $name, $default);
+
+        if (isset($type) && $data !== $default) {
+            // 强制类型转换
+            $this->typeCast($data, $type);
+        }
+
+        return $data;
+    }
+
+    protected function filterData($data, $filter, $name, $default)
+    {
+        // 解析过滤器
+        $filter = $this->getFilter($filter, $default);
+
+        if (is_array($data)) {
+            array_walk_recursive($data, [$this, 'filterValue'], $filter);
+        } else {
+            $this->filterValue($data, $name, $filter);
+        }
+
+        return $data;
+    }
+
+    /**
+     * 强制类型转换
+     * @access protected
+     * @param  mixed  $data
+     * @param  string $type
+     * @return mixed
+     */
+    protected function typeCast(&$data, string $type)
+    {
+        switch (strtolower($type)) {
+            // 数组
+            case 'a':
+                $data = (array) $data;
+                break;
+            // 数字
+            case 'd':
+                $data = (int) $data;
+                break;
+            // 浮点
+            case 'f':
+                $data = (float) $data;
+                break;
+            // 布尔
+            case 'b':
+                $data = (boolean) $data;
+                break;
+            // 字符串
+            case 's':
+                if (is_scalar($data)) {
+                    $data = (string) $data;
+                } else {
+                    throw new \InvalidArgumentException('variable type error:' . gettype($data));
+                }
+                break;
+        }
+    }
+
+    /**
+     * 获取数据
+     * @access protected
+     * @param  array  $data 数据源
+     * @param  string $name 字段名
+     * @param  mixed  $default 默认值
+     * @return mixed
+     */
+    protected function getData(array $data, string $name, $default = null)
+    {
+        foreach (explode('.', $name) as $val) {
+            if (isset($data[$val])) {
+                $data = $data[$val];
+            } else {
+                return $default;
+            }
+        }
+
+        return $data;
+    }
+
+    /**
+     * 设置或获取当前的过滤规则
+     * @access public
+     * @param  mixed $filter 过滤规则
+     * @return mixed
+     */
+    public function filter($filter = null)
+    {
+        if (is_null($filter)) {
+            return $this->filter;
+        }
+
+        $this->filter = $filter;
+
+        return $this;
+    }
+
+    protected function getFilter($filter, $default): array
+    {
+        if (is_null($filter)) {
+            $filter = [];
+        } else {
+            $filter = $filter ?: $this->filter;
+            if (is_string($filter) && false === strpos($filter, '/')) {
+                $filter = explode(',', $filter);
+            } else {
+                $filter = (array) $filter;
+            }
+        }
+
+        $filter[] = $default;
+
+        return $filter;
+    }
+
+    /**
+     * 递归过滤给定的值
+     * @access public
+     * @param  mixed $value 键值
+     * @param  mixed $key 键名
+     * @param  array $filters 过滤方法+默认值
+     * @return mixed
+     */
+    public function filterValue(&$value, $key, $filters)
+    {
+        $default = array_pop($filters);
+
+        foreach ($filters as $filter) {
+            if (is_callable($filter)) {
+                // 调用函数或者方法过滤
+                if (is_null($value)) {
+                    continue;
+                }
+
+                $value = call_user_func($filter, $value);
+            } elseif (is_scalar($value)) {
+                if (is_string($filter) && false !== strpos($filter, '/')) {
+                    // 正则过滤
+                    if (!preg_match($filter, $value)) {
+                        // 匹配不成功返回默认值
+                        $value = $default;
+                        break;
+                    }
+                } elseif (!empty($filter)) {
+                    // filter函数不存在时, 则使用filter_var进行过滤
+                    // filter为非整形值时, 调用filter_id取得过滤id
+                    $value = filter_var($value, is_int($filter) ? $filter : filter_id($filter));
+                    if (false === $value) {
+                        $value = $default;
+                        break;
+                    }
+                }
+            }
+        }
+
+        return $value;
+    }
+
+    /**
+     * 是否存在某个请求参数
+     * @access public
+     * @param  string $name 变量名
+     * @param  string $type 变量类型
+     * @param  bool   $checkEmpty 是否检测空值
+     * @return bool
+     */
+    public function has(string $name, string $type = 'param', bool $checkEmpty = false): bool
+    {
+        if (!in_array($type, ['param', 'get', 'post', 'put', 'patch', 'route', 'delete', 'cookie', 'session', 'env', 'request', 'server', 'header', 'file'])) {
+            return false;
+        }
+
+        $param = empty($this->$type) ? $this->$type() : $this->$type;
+
+        if (is_object($param)) {
+            return $param->has($name);
+        }
+
+        // 按.拆分成多维数组进行判断
+        foreach (explode('.', $name) as $val) {
+            if (isset($param[$val])) {
+                $param = $param[$val];
+            } else {
+                return false;
+            }
+        }
+
+        return ($checkEmpty && '' === $param) ? false : true;
+    }
+
+    /**
+     * 获取指定的参数
+     * @access public
+     * @param  array        $name 变量名
+     * @param  mixed        $data 数据或者变量类型
+     * @param  string|array $filter 过滤方法
+     * @return array
+     */
+    public function only(array $name, $data = 'param', $filter = ''): array
+    {
+        $data = is_array($data) ? $data : $this->$data();
+
+        $item = [];
+        foreach ($name as $key => $val) {
+
+            if (is_int($key)) {
+                $default = null;
+                $key     = $val;
+                if (!key_exists($key, $data)) {
+                    continue;
+                }
+            } else {
+                $default = $val;
+            }
+
+            $item[$key] = $this->filterData($data[$key] ?? $default, $filter, $key, $default);
+        }
+
+        return $item;
+    }
+
+    /**
+     * 排除指定参数获取
+     * @access public
+     * @param  array  $name 变量名
+     * @param  string $type 变量类型
+     * @return mixed
+     */
+    public function except(array $name, string $type = 'param'): array
+    {
+        $param = $this->$type();
+
+        foreach ($name as $key) {
+            if (isset($param[$key])) {
+                unset($param[$key]);
+            }
+        }
+
+        return $param;
+    }
+
+    /**
+     * 当前是否ssl
+     * @access public
+     * @return bool
+     */
+    public function isSsl(): bool
+    {
+        if ($this->server('HTTPS') && ('1' == $this->server('HTTPS') || 'on' == strtolower($this->server('HTTPS')))) {
+            return true;
+        } elseif ('https' == $this->server('REQUEST_SCHEME')) {
+            return true;
+        } elseif ('443' == $this->server('SERVER_PORT')) {
+            return true;
+        } elseif ('https' == $this->server('HTTP_X_FORWARDED_PROTO')) {
+            return true;
+        } elseif ($this->httpsAgentName && $this->server($this->httpsAgentName)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * 当前是否JSON请求
+     * @access public
+     * @return bool
+     */
+    public function isJson(): bool
+    {
+        $acceptType = $this->type();
+
+        return false !== strpos($acceptType, 'json');
+    }
+
+    /**
+     * 当前是否Ajax请求
+     * @access public
+     * @param  bool $ajax true 获取原始ajax请求
+     * @return bool
+     */
+    public function isAjax(bool $ajax = false): bool
+    {
+        $value  = $this->server('HTTP_X_REQUESTED_WITH');
+        $result = $value && 'xmlhttprequest' == strtolower($value) ? true : false;
+
+        if (true === $ajax) {
+            return $result;
+        }
+
+        return $this->param($this->varAjax) ? true : $result;
+    }
+
+    /**
+     * 当前是否Pjax请求
+     * @access public
+     * @param  bool $pjax true 获取原始pjax请求
+     * @return bool
+     */
+    public function isPjax(bool $pjax = false): bool
+    {
+        $result = !empty($this->server('HTTP_X_PJAX')) ? true : false;
+
+        if (true === $pjax) {
+            return $result;
+        }
+
+        return $this->param($this->varPjax) ? true : $result;
+    }
+
+    /**
+     * 获取客户端IP地址
+     * @access public
+     * @return string
+     */
+    public function ip(): string
+    {
+        if (!empty($this->realIP)) {
+            return $this->realIP;
+        }
+
+        $this->realIP = $this->server('REMOTE_ADDR', '');
+
+        // 如果指定了前端代理服务器IP以及其会发送的IP头
+        // 则尝试获取前端代理服务器发送过来的真实IP
+        $proxyIp       = $this->proxyServerIp;
+        $proxyIpHeader = $this->proxyServerIpHeader;
+
+        if (count($proxyIp) > 0 && count($proxyIpHeader) > 0) {
+            // 从指定的HTTP头中依次尝试获取IP地址
+            // 直到获取到一个合法的IP地址
+            foreach ($proxyIpHeader as $header) {
+                $tempIP = $this->server($header);
+
+                if (empty($tempIP)) {
+                    continue;
+                }
+
+                $tempIP = trim(explode(',', $tempIP)[0]);
+
+                if (!$this->isValidIP($tempIP)) {
+                    $tempIP = null;
+                } else {
+                    break;
+                }
+            }
+
+            // tempIP不为空,说明获取到了一个IP地址
+            // 这时我们检查 REMOTE_ADDR 是不是指定的前端代理服务器之一
+            // 如果是的话说明该 IP头 是由前端代理服务器设置的
+            // 否则则是伪装的
+            if (!empty($tempIP)) {
+                $realIPBin = $this->ip2bin($this->realIP);
+
+                foreach ($proxyIp as $ip) {
+                    $serverIPElements = explode('/', $ip);
+                    $serverIP         = $serverIPElements[0];
+                    $serverIPPrefix   = $serverIPElements[1] ?? 128;
+                    $serverIPBin      = $this->ip2bin($serverIP);
+
+                    // IP类型不符
+                    if (strlen($realIPBin) !== strlen($serverIPBin)) {
+                        continue;
+                    }
+
+                    if (strncmp($realIPBin, $serverIPBin, (int) $serverIPPrefix) === 0) {
+                        $this->realIP = $tempIP;
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (!$this->isValidIP($this->realIP)) {
+            $this->realIP = '0.0.0.0';
+        }
+
+        return $this->realIP;
+    }
+
+    /**
+     * 检测是否是合法的IP地址
+     *
+     * @param string $ip   IP地址
+     * @param string $type IP地址类型 (ipv4, ipv6)
+     *
+     * @return boolean
+     */
+    public function isValidIP(string $ip, string $type = ''): bool
+    {
+        switch (strtolower($type)) {
+            case 'ipv4':
+                $flag = FILTER_FLAG_IPV4;
+                break;
+            case 'ipv6':
+                $flag = FILTER_FLAG_IPV6;
+                break;
+            default:
+                $flag = 0;
+                break;
+        }
+
+        return boolval(filter_var($ip, FILTER_VALIDATE_IP, $flag));
+    }
+
+    /**
+     * 将IP地址转换为二进制字符串
+     *
+     * @param string $ip
+     *
+     * @return string
+     */
+    public function ip2bin(string $ip): string
+    {
+        if ($this->isValidIP($ip, 'ipv6')) {
+            $IPHex = str_split(bin2hex(inet_pton($ip)), 4);
+            foreach ($IPHex as $key => $value) {
+                $IPHex[$key] = intval($value, 16);
+            }
+            $IPBin = vsprintf('%016b%016b%016b%016b%016b%016b%016b%016b', $IPHex);
+        } else {
+            $IPHex = str_split(bin2hex(inet_pton($ip)), 2);
+            foreach ($IPHex as $key => $value) {
+                $IPHex[$key] = intval($value, 16);
+            }
+            $IPBin = vsprintf('%08b%08b%08b%08b', $IPHex);
+        }
+
+        return $IPBin;
+    }
+
+    /**
+     * 检测是否使用手机访问
+     * @access public
+     * @return bool
+     */
+    public function isMobile(): bool
+    {
+        if ($this->server('HTTP_VIA') && stristr($this->server('HTTP_VIA'), "wap")) {
+            return true;
+        } elseif ($this->server('HTTP_ACCEPT') && strpos(strtoupper($this->server('HTTP_ACCEPT')), "VND.WAP.WML")) {
+            return true;
+        } elseif ($this->server('HTTP_X_WAP_PROFILE') || $this->server('HTTP_PROFILE')) {
+            return true;
+        } elseif ($this->server('HTTP_USER_AGENT') && preg_match('/(blackberry|configuration\/cldc|hp |hp-|htc |htc_|htc-|iemobile|kindle|midp|mmp|motorola|mobile|nokia|opera mini|opera |Googlebot-Mobile|YahooSeeker\/M1A1-R2D2|android|iphone|ipod|mobi|palm|palmos|pocket|portalmmm|ppc;|smartphone|sonyericsson|sqh|spv|symbian|treo|up.browser|up.link|vodafone|windows ce|xda |xda_)/i', $this->server('HTTP_USER_AGENT'))) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * 当前URL地址中的scheme参数
+     * @access public
+     * @return string
+     */
+    public function scheme(): string
+    {
+        return $this->isSsl() ? 'https' : 'http';
+    }
+
+    /**
+     * 当前请求URL地址中的query参数
+     * @access public
+     * @return string
+     */
+    public function query(): string
+    {
+        return $this->server('QUERY_STRING', '');
+    }
+
+    /**
+     * 设置当前请求的host(包含端口)
+     * @access public
+     * @param  string $host 主机名(含端口)
+     * @return $this
+     */
+    public function setHost(string $host)
+    {
+        $this->host = $host;
+
+        return $this;
+    }
+
+    /**
+     * 当前请求的host
+     * @access public
+     * @param bool $strict  true 仅仅获取HOST
+     * @return string
+     */
+    public function host(bool $strict = false): string
+    {
+        if ($this->host) {
+            $host = $this->host;
+        } else {
+            $host = strval($this->server('HTTP_X_FORWARDED_HOST') ?: $this->server('HTTP_HOST'));
+        }
+
+        return true === $strict && strpos($host, ':') ? strstr($host, ':', true) : $host;
+    }
+
+    /**
+     * 当前请求URL地址中的port参数
+     * @access public
+     * @return int
+     */
+    public function port(): int
+    {
+        return (int) ($this->server('HTTP_X_FORWARDED_PORT') ?: $this->server('SERVER_PORT', ''));
+    }
+
+    /**
+     * 当前请求 SERVER_PROTOCOL
+     * @access public
+     * @return string
+     */
+    public function protocol(): string
+    {
+        return $this->server('SERVER_PROTOCOL', '');
+    }
+
+    /**
+     * 当前请求 REMOTE_PORT
+     * @access public
+     * @return int
+     */
+    public function remotePort(): int
+    {
+        return (int) $this->server('REMOTE_PORT', '');
+    }
+
+    /**
+     * 当前请求 HTTP_CONTENT_TYPE
+     * @access public
+     * @return string
+     */
+    public function contentType(): string
+    {
+        $contentType = $this->header('Content-Type');
+
+        if ($contentType) {
+            if (strpos($contentType, ';')) {
+                [$type] = explode(';', $contentType);
+            } else {
+                $type = $contentType;
+            }
+            return trim($type);
+        }
+
+        return '';
+    }
+
+    /**
+     * 获取当前请求的安全Key
+     * @access public
+     * @return string
+     */
+    public function secureKey(): string
+    {
+        if (is_null($this->secureKey)) {
+            $this->secureKey = uniqid('', true);
+        }
+
+        return $this->secureKey;
+    }
+
+    /**
+     * 设置当前的控制器名
+     * @access public
+     * @param  string $controller 控制器名
+     * @return $this
+     */
+    public function setController(string $controller)
+    {
+        $this->controller = $controller;
+        return $this;
+    }
+
+    /**
+     * 设置当前的操作名
+     * @access public
+     * @param  string $action 操作名
+     * @return $this
+     */
+    public function setAction(string $action)
+    {
+        $this->action = $action;
+        return $this;
+    }
+
+    /**
+     * 获取当前的控制器名
+     * @access public
+     * @param  bool $convert 转换为小写
+     * @return string
+     */
+    public function controller(bool $convert = false): string
+    {
+        $name = $this->controller ?: '';
+        return $convert ? strtolower($name) : $name;
+    }
+
+    /**
+     * 获取当前的操作名
+     * @access public
+     * @param  bool $convert 转换为小写
+     * @return string
+     */
+    public function action(bool $convert = false): string
+    {
+        $name = $this->action ?: '';
+        return $convert ? strtolower($name) : $name;
+    }
+
+    /**
+     * 设置或者获取当前请求的content
+     * @access public
+     * @return string
+     */
+    public function getContent(): string
+    {
+        if (is_null($this->content)) {
+            $this->content = $this->input;
+        }
+
+        return $this->content;
+    }
+
+    /**
+     * 获取当前请求的php://input
+     * @access public
+     * @return string
+     */
+    public function getInput(): string
+    {
+        return $this->input;
+    }
+
+    /**
+     * 生成请求令牌
+     * @access public
+     * @param  string $name 令牌名称
+     * @param  mixed  $type 令牌生成方法
+     * @return string
+     */
+    public function buildToken(string $name = '__token__', $type = 'md5'): string
+    {
+        $type  = is_callable($type) ? $type : 'md5';
+        $token = call_user_func($type, $this->server('REQUEST_TIME_FLOAT'));
+
+        $this->session->set($name, $token);
+
+        return $token;
+    }
+
+    /**
+     * 检查请求令牌
+     * @access public
+     * @param  string $token 令牌名称
+     * @param  array  $data  表单数据
+     * @return bool
+     */
+    public function checkToken(string $token = '__token__', array $data = []): bool
+    {
+        if (in_array($this->method(), ['GET', 'HEAD', 'OPTIONS'], true)) {
+            return true;
+        }
+
+        if (!$this->session->has($token)) {
+            // 令牌数据无效
+            return false;
+        }
+
+        // Header验证
+        if ($this->header('X-CSRF-TOKEN') && $this->session->get($token) === $this->header('X-CSRF-TOKEN')) {
+            // 防止重复提交
+            $this->session->delete($token); // 验证完成销毁session
+            return true;
+        }
+
+        if (empty($data)) {
+            $data = $this->post();
+        }
+
+        // 令牌验证
+        if (isset($data[$token]) && $this->session->get($token) === $data[$token]) {
+            // 防止重复提交
+            $this->session->delete($token); // 验证完成销毁session
+            return true;
+        }
+
+        // 开启TOKEN重置
+        $this->session->delete($token);
+        return false;
+    }
+
+    /**
+     * 设置在中间件传递的数据
+     * @access public
+     * @param  array $middleware 数据
+     * @return $this
+     */
+    public function withMiddleware(array $middleware)
+    {
+        $this->middleware = array_merge($this->middleware, $middleware);
+        return $this;
+    }
+
+    /**
+     * 设置GET数据
+     * @access public
+     * @param  array $get 数据
+     * @return $this
+     */
+    public function withGet(array $get)
+    {
+        $this->get = $get;
+        return $this;
+    }
+
+    /**
+     * 设置POST数据
+     * @access public
+     * @param  array $post 数据
+     * @return $this
+     */
+    public function withPost(array $post)
+    {
+        $this->post = $post;
+        return $this;
+    }
+
+    /**
+     * 设置COOKIE数据
+     * @access public
+     * @param array $cookie 数据
+     * @return $this
+     */
+    public function withCookie(array $cookie)
+    {
+        $this->cookie = $cookie;
+        return $this;
+    }
+
+    /**
+     * 设置SESSION数据
+     * @access public
+     * @param Session $session 数据
+     * @return $this
+     */
+    public function withSession(Session $session)
+    {
+        $this->session = $session;
+        return $this;
+    }
+
+    /**
+     * 设置SERVER数据
+     * @access public
+     * @param  array $server 数据
+     * @return $this
+     */
+    public function withServer(array $server)
+    {
+        $this->server = array_change_key_case($server, CASE_UPPER);
+        return $this;
+    }
+
+    /**
+     * 设置HEADER数据
+     * @access public
+     * @param  array $header 数据
+     * @return $this
+     */
+    public function withHeader(array $header)
+    {
+        $this->header = array_change_key_case($header);
+        return $this;
+    }
+
+    /**
+     * 设置ENV数据
+     * @access public
+     * @param Env $env 数据
+     * @return $this
+     */
+    public function withEnv(Env $env)
+    {
+        $this->env = $env;
+        return $this;
+    }
+
+    /**
+     * 设置php://input数据
+     * @access public
+     * @param string $input RAW数据
+     * @return $this
+     */
+    public function withInput(string $input)
+    {
+        $this->input = $input;
+        if (!empty($input)) {
+            $inputData = $this->getInputData($input);
+            if (!empty($inputData)) {
+                $this->post = $inputData;
+                $this->put  = $inputData;
+            }
+        }
+        return $this;
+    }
+
+    /**
+     * 设置文件上传数据
+     * @access public
+     * @param  array $files 上传信息
+     * @return $this
+     */
+    public function withFiles(array $files)
+    {
+        $this->file = $files;
+        return $this;
+    }
+
+    /**
+     * 设置ROUTE变量
+     * @access public
+     * @param  array $route 数据
+     * @return $this
+     */
+    public function withRoute(array $route)
+    {
+        $this->route = $route;
+        return $this;
+    }
+
+    /**
+     * 设置中间传递数据
+     * @access public
+     * @param  string    $name  参数名
+     * @param  mixed     $value 值
+     */
+    public function __set(string $name, $value)
+    {
+        $this->middleware[$name] = $value;
+    }
+
+    /**
+     * 获取中间传递数据的值
+     * @access public
+     * @param  string $name 名称
+     * @return mixed
+     */
+    public function __get(string $name)
+    {
+        return $this->middleware($name);
+    }
+
+    /**
+     * 检测中间传递数据的值
+     * @access public
+     * @param  string $name 名称
+     * @return boolean
+     */
+    public function __isset(string $name): bool
+    {
+        return isset($this->middleware[$name]);
+    }
+
+    // ArrayAccess
+    #[\ReturnTypeWillChange]
+    public function offsetExists($name): bool
+    {
+        return $this->has($name);
+    }
+
+    #[\ReturnTypeWillChange]
+    public function offsetGet($name)
+    {
+        return $this->param($name);
+    }
+
+    #[\ReturnTypeWillChange]
+    public function offsetSet($name, $value)
+    {}
+
+    #[\ReturnTypeWillChange]
+    public function offsetUnset($name)
+    {}
+
+}
diff --git a/vendor/topthink/framework/src/think/Response.php b/vendor/topthink/framework/src/think/Response.php
new file mode 100755
index 0000000..49f26dc
--- /dev/null
+++ b/vendor/topthink/framework/src/think/Response.php
@@ -0,0 +1,413 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+/**
+ * 响应输出基础类
+ * @package think
+ */
+abstract class Response
+{
+    /**
+     * 原始数据
+     * @var mixed
+     */
+    protected $data;
+
+    /**
+     * 当前contentType
+     * @var string
+     */
+    protected $contentType = 'text/html';
+
+    /**
+     * 字符集
+     * @var string
+     */
+    protected $charset = 'utf-8';
+
+    /**
+     * 状态码
+     * @var integer
+     */
+    protected $code = 200;
+
+    /**
+     * 是否允许请求缓存
+     * @var bool
+     */
+    protected $allowCache = true;
+
+    /**
+     * 输出参数
+     * @var array
+     */
+    protected $options = [];
+
+    /**
+     * header参数
+     * @var array
+     */
+    protected $header = [];
+
+    /**
+     * 输出内容
+     * @var string
+     */
+    protected $content = null;
+
+    /**
+     * Cookie对象
+     * @var Cookie
+     */
+    protected $cookie;
+
+    /**
+     * Session对象
+     * @var Session
+     */
+    protected $session;
+
+    /**
+     * 初始化
+     * @access protected
+     * @param  mixed  $data 输出数据
+     * @param  int    $code 状态码
+     */
+    protected function init($data = '', int $code = 200)
+    {
+        $this->data($data);
+        $this->code = $code;
+
+        $this->contentType($this->contentType, $this->charset);
+    }
+
+    /**
+     * 创建Response对象
+     * @access public
+     * @param  mixed  $data 输出数据
+     * @param  string $type 输出类型
+     * @param  int    $code 状态码
+     * @return Response
+     */
+    public static function create($data = '', string $type = 'html', int $code = 200): Response
+    {
+        $class = false !== strpos($type, '\\') ? $type : '\\think\\response\\' . ucfirst(strtolower($type));
+
+        return Container::getInstance()->invokeClass($class, [$data, $code]);
+    }
+
+    /**
+     * 设置Session对象
+     * @access public
+     * @param  Session $session Session对象
+     * @return $this
+     */
+    public function setSession(Session $session)
+    {
+        $this->session = $session;
+        return $this;
+    }
+
+    /**
+     * 发送数据到客户端
+     * @access public
+     * @return void
+     * @throws \InvalidArgumentException
+     */
+    public function send(): void
+    {
+        // 处理输出数据
+        $data = $this->getContent();
+
+        if (!headers_sent()) {
+            if (!empty($this->header)) {
+                // 发送状态码
+                http_response_code($this->code);
+                // 发送头部信息
+                foreach ($this->header as $name => $val) {
+                    header($name . (!is_null($val) ? ':' . $val : ''));
+                }
+            }
+
+            if ($this->cookie) {
+                $this->cookie->save();
+            }
+        }
+
+        $this->sendData($data);
+
+        if (function_exists('fastcgi_finish_request')) {
+            // 提高页面响应
+            fastcgi_finish_request();
+        }
+    }
+
+    /**
+     * 处理数据
+     * @access protected
+     * @param  mixed $data 要处理的数据
+     * @return mixed
+     */
+    protected function output($data)
+    {
+        return $data;
+    }
+
+    /**
+     * 输出数据
+     * @access protected
+     * @param string $data 要处理的数据
+     * @return void
+     */
+    protected function sendData(string $data): void
+    {
+        echo $data;
+    }
+
+    /**
+     * 输出的参数
+     * @access public
+     * @param  mixed $options 输出参数
+     * @return $this
+     */
+    public function options(array $options = [])
+    {
+        $this->options = array_merge($this->options, $options);
+
+        return $this;
+    }
+
+    /**
+     * 输出数据设置
+     * @access public
+     * @param  mixed $data 输出数据
+     * @return $this
+     */
+    public function data($data)
+    {
+        $this->data = $data;
+
+        return $this;
+    }
+
+    /**
+     * 是否允许请求缓存
+     * @access public
+     * @param  bool $cache 允许请求缓存
+     * @return $this
+     */
+    public function allowCache(bool $cache)
+    {
+        $this->allowCache = $cache;
+
+        return $this;
+    }
+
+    /**
+     * 是否允许请求缓存
+     * @access public
+     * @return bool
+     */
+    public function isAllowCache()
+    {
+        return $this->allowCache;
+    }
+
+    /**
+     * 设置Cookie
+     * @access public
+     * @param  string $name  cookie名称
+     * @param  string $value cookie值
+     * @param  mixed  $option 可选参数
+     * @return $this
+     */
+    public function cookie(string $name, string $value, $option = null)
+    {
+        $this->cookie->set($name, $value, $option);
+
+        return $this;
+    }
+
+    /**
+     * 设置响应头
+     * @access public
+     * @param  array $header  参数
+     * @return $this
+     */
+    public function header(array $header = [])
+    {
+        $this->header = array_merge($this->header, $header);
+
+        return $this;
+    }
+
+    /**
+     * 设置页面输出内容
+     * @access public
+     * @param  mixed $content
+     * @return $this
+     */
+    public function content($content)
+    {
+        if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable([
+            $content,
+            '__toString',
+        ])
+        ) {
+            throw new \InvalidArgumentException(sprintf('variable type error: %s', gettype($content)));
+        }
+
+        $this->content = (string) $content;
+
+        return $this;
+    }
+
+    /**
+     * 发送HTTP状态
+     * @access public
+     * @param  integer $code 状态码
+     * @return $this
+     */
+    public function code(int $code)
+    {
+        $this->code = $code;
+
+        return $this;
+    }
+
+    /**
+     * LastModified
+     * @access public
+     * @param  string $time
+     * @return $this
+     */
+    public function lastModified(string $time)
+    {
+        $this->header['Last-Modified'] = $time;
+
+        return $this;
+    }
+
+    /**
+     * Expires
+     * @access public
+     * @param  string $time
+     * @return $this
+     */
+    public function expires(string $time)
+    {
+        $this->header['Expires'] = $time;
+
+        return $this;
+    }
+
+    /**
+     * ETag
+     * @access public
+     * @param  string $eTag
+     * @return $this
+     */
+    public function eTag(string $eTag)
+    {
+        $this->header['ETag'] = $eTag;
+
+        return $this;
+    }
+
+    /**
+     * 页面缓存控制
+     * @access public
+     * @param  string $cache 状态码
+     * @return $this
+     */
+    public function cacheControl(string $cache)
+    {
+        $this->header['Cache-control'] = $cache;
+
+        return $this;
+    }
+
+    /**
+     * 页面输出类型
+     * @access public
+     * @param  string $contentType 输出类型
+     * @param  string $charset     输出编码
+     * @return $this
+     */
+    public function contentType(string $contentType, string $charset = 'utf-8')
+    {
+        $this->header['Content-Type'] = $contentType . '; charset=' . $charset;
+
+        return $this;
+    }
+
+    /**
+     * 获取头部信息
+     * @access public
+     * @param  string $name 头部名称
+     * @return mixed
+     */
+    public function getHeader(string $name = '')
+    {
+        if (!empty($name)) {
+            return $this->header[$name] ?? null;
+        }
+
+        return $this->header;
+    }
+
+    /**
+     * 获取原始数据
+     * @access public
+     * @return mixed
+     */
+    public function getData()
+    {
+        return $this->data;
+    }
+
+    /**
+     * 获取输出数据
+     * @access public
+     * @return string
+     */
+    public function getContent(): string
+    {
+        if (null == $this->content) {
+            $content = $this->output($this->data);
+
+            if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable([
+                $content,
+                '__toString',
+            ])
+            ) {
+                throw new \InvalidArgumentException(sprintf('variable type error: %s', gettype($content)));
+            }
+
+            $this->content = (string) $content;
+        }
+
+        return $this->content;
+    }
+
+    /**
+     * 获取状态码
+     * @access public
+     * @return integer
+     */
+    public function getCode(): int
+    {
+        return $this->code;
+    }
+}
diff --git a/vendor/topthink/framework/src/think/Route.php b/vendor/topthink/framework/src/think/Route.php
new file mode 100755
index 0000000..e45cb33
--- /dev/null
+++ b/vendor/topthink/framework/src/think/Route.php
@@ -0,0 +1,939 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+use Closure;
+use think\exception\RouteNotFoundException;
+use think\route\Dispatch;
+use think\route\dispatch\Callback;
+use think\route\dispatch\Url as UrlDispatch;
+use think\route\Domain;
+use think\route\Resource;
+use think\route\Rule;
+use think\route\RuleGroup;
+use think\route\RuleItem;
+use think\route\RuleName;
+use think\route\Url as UrlBuild;
+
+/**
+ * 路由管理类
+ * @package think
+ */
+class Route
+{
+    /**
+     * REST定义
+     * @var array
+     */
+    protected $rest = [
+        'index'  => ['get', '', 'index'],
+        'create' => ['get', '/create', 'create'],
+        'edit'   => ['get', '/<id>/edit', 'edit'],
+        'read'   => ['get', '/<id>', 'read'],
+        'save'   => ['post', '', 'save'],
+        'update' => ['put', '/<id>', 'update'],
+        'delete' => ['delete', '/<id>', 'delete'],
+    ];
+
+    /**
+     * 配置参数
+     * @var array
+     */
+    protected $config = [
+        // pathinfo分隔符
+        'pathinfo_depr'         => '/',
+        // 是否开启路由延迟解析
+        'url_lazy_route'        => false,
+        // 是否强制使用路由
+        'url_route_must'        => false,
+        // 合并路由规则
+        'route_rule_merge'      => false,
+        // 路由是否完全匹配
+        'route_complete_match'  => false,
+        // 去除斜杠
+        'remove_slash'          => false,
+        // 使用注解路由
+        'route_annotation'      => false,
+        // 默认的路由变量规则
+        'default_route_pattern' => '[\w\.]+',
+        // URL伪静态后缀
+        'url_html_suffix'       => 'html',
+        // 访问控制器层名称
+        'controller_layer'      => 'controller',
+        // 空控制器名
+        'empty_controller'      => 'Error',
+        // 是否使用控制器后缀
+        'controller_suffix'     => false,
+        // 默认控制器名
+        'default_controller'    => 'Index',
+        // 默认操作名
+        'default_action'        => 'index',
+        // 操作方法后缀
+        'action_suffix'         => '',
+        // 非路由变量是否使用普通参数方式(用于URL生成)
+        'url_common_param'      => true,
+    ];
+
+    /**
+     * 当前应用
+     * @var App
+     */
+    protected $app;
+
+    /**
+     * 请求对象
+     * @var Request
+     */
+    protected $request;
+
+    /**
+     * @var RuleName
+     */
+    protected $ruleName;
+
+    /**
+     * 当前HOST
+     * @var string
+     */
+    protected $host;
+
+    /**
+     * 当前分组对象
+     * @var RuleGroup
+     */
+    protected $group;
+
+    /**
+     * 路由绑定
+     * @var array
+     */
+    protected $bind = [];
+
+    /**
+     * 域名对象
+     * @var Domain[]
+     */
+    protected $domains = [];
+
+    /**
+     * 跨域路由规则
+     * @var RuleGroup
+     */
+    protected $cross;
+
+    /**
+     * 路由是否延迟解析
+     * @var bool
+     */
+    protected $lazy = false;
+
+    /**
+     * 路由是否测试模式
+     * @var bool
+     */
+    protected $isTest = false;
+
+    /**
+     * (分组)路由规则是否合并解析
+     * @var bool
+     */
+    protected $mergeRuleRegex = false;
+
+    /**
+     * 是否去除URL最后的斜线
+     * @var bool
+     */
+    protected $removeSlash = false;
+
+    public function __construct(App $app)
+    {
+        $this->app      = $app;
+        $this->ruleName = new RuleName();
+        $this->setDefaultDomain();
+
+        if (is_file($this->app->getRuntimePath() . 'route.php')) {
+            // 读取路由映射文件
+            $this->import(include $this->app->getRuntimePath() . 'route.php');
+        }
+
+        $this->config = array_merge($this->config, $this->app->config->get('route'));
+
+        $this->init();
+    }
+
+    protected function init()
+    {
+        if (!empty($this->config['middleware'])) {
+            $this->app->middleware->import($this->config['middleware'], 'route');
+        }
+
+        $this->lazy($this->config['url_lazy_route']);
+        $this->mergeRuleRegex = $this->config['route_rule_merge'];
+        $this->removeSlash    = $this->config['remove_slash'];
+
+        $this->group->removeSlash($this->removeSlash);
+    }
+
+    public function config(string $name = null)
+    {
+        if (is_null($name)) {
+            return $this->config;
+        }
+
+        return $this->config[$name] ?? null;
+    }
+
+    /**
+     * 设置路由域名及分组(包括资源路由)是否延迟解析
+     * @access public
+     * @param bool $lazy 路由是否延迟解析
+     * @return $this
+     */
+    public function lazy(bool $lazy = true)
+    {
+        $this->lazy = $lazy;
+        return $this;
+    }
+
+    /**
+     * 设置路由为测试模式
+     * @access public
+     * @param bool $test 路由是否测试模式
+     * @return void
+     */
+    public function setTestMode(bool $test): void
+    {
+        $this->isTest = $test;
+    }
+
+    /**
+     * 检查路由是否为测试模式
+     * @access public
+     * @return bool
+     */
+    public function isTest(): bool
+    {
+        return $this->isTest;
+    }
+
+    /**
+     * 设置路由域名及分组(包括资源路由)是否合并解析
+     * @access public
+     * @param bool $merge 路由是否合并解析
+     * @return $this
+     */
+    public function mergeRuleRegex(bool $merge = true)
+    {
+        $this->mergeRuleRegex = $merge;
+        $this->group->mergeRuleRegex($merge);
+
+        return $this;
+    }
+
+    /**
+     * 初始化默认域名
+     * @access protected
+     * @return void
+     */
+    protected function setDefaultDomain(): void
+    {
+        // 注册默认域名
+        $domain = new Domain($this);
+
+        $this->domains['-'] = $domain;
+
+        // 默认分组
+        $this->group = $domain;
+    }
+
+    /**
+     * 设置当前分组
+     * @access public
+     * @param RuleGroup $group 域名
+     * @return void
+     */
+    public function setGroup(RuleGroup $group): void
+    {
+        $this->group = $group;
+    }
+
+    /**
+     * 获取指定标识的路由分组 不指定则获取当前分组
+     * @access public
+     * @param string $name 分组标识
+     * @return RuleGroup
+     */
+    public function getGroup(string $name = null)
+    {
+        return $name ? $this->ruleName->getGroup($name) : $this->group;
+    }
+
+    /**
+     * 注册变量规则
+     * @access public
+     * @param array $pattern 变量规则
+     * @return $this
+     */
+    public function pattern(array $pattern)
+    {
+        $this->group->pattern($pattern);
+
+        return $this;
+    }
+
+    /**
+     * 注册路由参数
+     * @access public
+     * @param array $option 参数
+     * @return $this
+     */
+    public function option(array $option)
+    {
+        $this->group->option($option);
+
+        return $this;
+    }
+
+    /**
+     * 注册域名路由
+     * @access public
+     * @param string|array $name 子域名
+     * @param mixed        $rule 路由规则
+     * @return Domain
+     */
+    public function domain($name, $rule = null): Domain
+    {
+        // 支持多个域名使用相同路由规则
+        $domainName = is_array($name) ? array_shift($name) : $name;
+
+        if (!isset($this->domains[$domainName])) {
+            $domain = (new Domain($this, $domainName, $rule))
+                ->lazy($this->lazy)
+                ->removeSlash($this->removeSlash)
+                ->mergeRuleRegex($this->mergeRuleRegex);
+
+            $this->domains[$domainName] = $domain;
+        } else {
+            $domain = $this->domains[$domainName];
+            $domain->parseGroupRule($rule);
+        }
+
+        if (is_array($name) && !empty($name)) {
+            foreach ($name as $item) {
+                $this->domains[$item] = $domainName;
+            }
+        }
+
+        // 返回域名对象
+        return $domain;
+    }
+
+    /**
+     * 获取域名
+     * @access public
+     * @return array
+     */
+    public function getDomains(): array
+    {
+        return $this->domains;
+    }
+
+    /**
+     * 获取RuleName对象
+     * @access public
+     * @return RuleName
+     */
+    public function getRuleName(): RuleName
+    {
+        return $this->ruleName;
+    }
+
+    /**
+     * 设置路由绑定
+     * @access public
+     * @param string $bind   绑定信息
+     * @param string $domain 域名
+     * @return $this
+     */
+    public function bind(string $bind, string $domain = null)
+    {
+        $domain = is_null($domain) ? '-' : $domain;
+
+        $this->bind[$domain] = $bind;
+
+        return $this;
+    }
+
+    /**
+     * 读取路由绑定信息
+     * @access public
+     * @return array
+     */
+    public function getBind(): array
+    {
+        return $this->bind;
+    }
+
+    /**
+     * 读取路由绑定
+     * @access public
+     * @param string $domain 域名
+     * @return string|null
+     */
+    public function getDomainBind(string $domain = null)
+    {
+        if (is_null($domain)) {
+            $domain = $this->host;
+        } elseif (false === strpos($domain, '.') && $this->request) {
+            $domain .= '.' . $this->request->rootDomain();
+        }
+
+        if ($this->request) {
+            $subDomain = $this->request->subDomain();
+
+            if (strpos($subDomain, '.')) {
+                $name = '*' . strstr($subDomain, '.');
+            }
+        }
+
+        if (isset($this->bind[$domain])) {
+            $result = $this->bind[$domain];
+        } elseif (isset($name) && isset($this->bind[$name])) {
+            $result = $this->bind[$name];
+        } elseif (!empty($subDomain) && isset($this->bind['*'])) {
+            $result = $this->bind['*'];
+        } else {
+            $result = null;
+        }
+
+        return $result;
+    }
+
+    /**
+     * 读取路由标识
+     * @access public
+     * @param string $name   路由标识
+     * @param string $domain 域名
+     * @param string $method 请求类型
+     * @return array
+     */
+    public function getName(string $name = null, string $domain = null, string $method = '*'): array
+    {
+        return $this->ruleName->getName($name, $domain, $method);
+    }
+
+    /**
+     * 批量导入路由标识
+     * @access public
+     * @param array $name 路由标识
+     * @return void
+     */
+    public function import(array $name): void
+    {
+        $this->ruleName->import($name);
+    }
+
+    /**
+     * 注册路由标识
+     * @access public
+     * @param string   $name     路由标识
+     * @param RuleItem $ruleItem 路由规则
+     * @param bool     $first    是否优先
+     * @return void
+     */
+    public function setName(string $name, RuleItem $ruleItem, bool $first = false): void
+    {
+        $this->ruleName->setName($name, $ruleItem, $first);
+    }
+
+    /**
+     * 保存路由规则
+     * @access public
+     * @param string   $rule     路由规则
+     * @param RuleItem $ruleItem RuleItem对象
+     * @return void
+     */
+    public function setRule(string $rule, RuleItem $ruleItem = null): void
+    {
+        $this->ruleName->setRule($rule, $ruleItem);
+    }
+
+    /**
+     * 读取路由
+     * @access public
+     * @param string $rule 路由规则
+     * @return RuleItem[]
+     */
+    public function getRule(string $rule): array
+    {
+        return $this->ruleName->getRule($rule);
+    }
+
+    /**
+     * 读取路由列表
+     * @access public
+     * @return array
+     */
+    public function getRuleList(): array
+    {
+        return $this->ruleName->getRuleList();
+    }
+
+    /**
+     * 清空路由规则
+     * @access public
+     * @return void
+     */
+    public function clear(): void
+    {
+        $this->ruleName->clear();
+
+        if ($this->group) {
+            $this->group->clear();
+        }
+    }
+
+    /**
+     * 注册路由规则
+     * @access public
+     * @param string $rule   路由规则
+     * @param mixed  $route  路由地址
+     * @param string $method 请求类型
+     * @return RuleItem
+     */
+    public function rule(string $rule, $route = null, string $method = '*'): RuleItem
+    {
+        if ($route instanceof Response) {
+            // 兼容之前的路由到响应对象,感觉不需要,使用场景很少,闭包就能实现
+            $route = function () use ($route) {
+                return $route;
+            };
+        }
+        return $this->group->addRule($rule, $route, $method);
+    }
+
+    /**
+     * 设置跨域有效路由规则
+     * @access public
+     * @param Rule   $rule   路由规则
+     * @param string $method 请求类型
+     * @return $this
+     */
+    public function setCrossDomainRule(Rule $rule, string $method = '*')
+    {
+        if (!isset($this->cross)) {
+            $this->cross = (new RuleGroup($this))->mergeRuleRegex($this->mergeRuleRegex);
+        }
+
+        $this->cross->addRuleItem($rule, $method);
+
+        return $this;
+    }
+
+    /**
+     * 注册路由分组
+     * @access public
+     * @param string|\Closure $name  分组名称或者参数
+     * @param mixed           $route 分组路由
+     * @return RuleGroup
+     */
+    public function group($name, $route = null): RuleGroup
+    {
+        if ($name instanceof Closure) {
+            $route = $name;
+            $name  = '';
+        }
+
+        return (new RuleGroup($this, $this->group, $name, $route))
+            ->lazy($this->lazy)
+            ->removeSlash($this->removeSlash)
+            ->mergeRuleRegex($this->mergeRuleRegex);
+    }
+
+    /**
+     * 注册路由
+     * @access public
+     * @param string $rule  路由规则
+     * @param mixed  $route 路由地址
+     * @return RuleItem
+     */
+    public function any(string $rule, $route): RuleItem
+    {
+        return $this->rule($rule, $route, '*');
+    }
+
+    /**
+     * 注册GET路由
+     * @access public
+     * @param string $rule  路由规则
+     * @param mixed  $route 路由地址
+     * @return RuleItem
+     */
+    public function get(string $rule, $route): RuleItem
+    {
+        return $this->rule($rule, $route, 'GET');
+    }
+
+    /**
+     * 注册POST路由
+     * @access public
+     * @param string $rule  路由规则
+     * @param mixed  $route 路由地址
+     * @return RuleItem
+     */
+    public function post(string $rule, $route): RuleItem
+    {
+        return $this->rule($rule, $route, 'POST');
+    }
+
+    /**
+     * 注册PUT路由
+     * @access public
+     * @param string $rule  路由规则
+     * @param mixed  $route 路由地址
+     * @return RuleItem
+     */
+    public function put(string $rule, $route): RuleItem
+    {
+        return $this->rule($rule, $route, 'PUT');
+    }
+
+    /**
+     * 注册DELETE路由
+     * @access public
+     * @param string $rule  路由规则
+     * @param mixed  $route 路由地址
+     * @return RuleItem
+     */
+    public function delete(string $rule, $route): RuleItem
+    {
+        return $this->rule($rule, $route, 'DELETE');
+    }
+
+    /**
+     * 注册PATCH路由
+     * @access public
+     * @param string $rule  路由规则
+     * @param mixed  $route 路由地址
+     * @return RuleItem
+     */
+    public function patch(string $rule, $route): RuleItem
+    {
+        return $this->rule($rule, $route, 'PATCH');
+    }
+
+    /**
+     * 注册HEAD路由
+     * @access public
+     * @param string $rule  路由规则
+     * @param mixed  $route 路由地址
+     * @return RuleItem
+     */
+    public function head(string $rule, $route): RuleItem
+    {
+        return $this->rule($rule, $route, 'HEAD');
+    }
+
+    /**
+     * 注册OPTIONS路由
+     * @access public
+     * @param string $rule  路由规则
+     * @param mixed  $route 路由地址
+     * @return RuleItem
+     */
+    public function options(string $rule, $route): RuleItem
+    {
+        return $this->rule($rule, $route, 'OPTIONS');
+    }
+
+    /**
+     * 注册资源路由
+     * @access public
+     * @param string $rule  路由规则
+     * @param string $route 路由地址
+     * @return Resource
+     */
+    public function resource(string $rule, string $route): Resource
+    {
+        return (new Resource($this, $this->group, $rule, $route, $this->rest))
+            ->lazy($this->lazy);
+    }
+
+    /**
+     * 注册视图路由
+     * @access public
+     * @param string $rule     路由规则
+     * @param string $template 路由模板地址
+     * @param array  $vars     模板变量
+     * @return RuleItem
+     */
+    public function view(string $rule, string $template = '', array $vars = []): RuleItem
+    {
+        return $this->rule($rule, function () use ($vars, $template) {
+            return Response::create($template, 'view')->assign($vars);
+        }, 'GET');
+    }
+
+    /**
+     * 注册重定向路由
+     * @access public
+     * @param string $rule   路由规则
+     * @param string $route  路由地址
+     * @param int    $status 状态码
+     * @return RuleItem
+     */
+    public function redirect(string $rule, string $route = '', int $status = 301): RuleItem
+    {
+        return $this->rule($rule, function (Request $request) use ($status, $route) {
+            $search  = $replace  = [];
+            $matches = $request->rule()->getVars();
+
+            foreach ($matches as $key => $value) {
+                $search[]  = '<' . $key . '>';
+                $replace[] = $value;
+
+                $search[]  = ':' . $key;
+                $replace[] = $value;
+            }
+
+            $route = str_replace($search, $replace, $route);
+            return Response::create($route, 'redirect')->code($status);
+        }, '*');
+    }
+
+    /**
+     * rest方法定义和修改
+     * @access public
+     * @param string|array $name     方法名称
+     * @param array|bool   $resource 资源
+     * @return $this
+     */
+    public function rest($name, $resource = [])
+    {
+        if (is_array($name)) {
+            $this->rest = $resource ? $name : array_merge($this->rest, $name);
+        } else {
+            $this->rest[$name] = $resource;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 获取rest方法定义的参数
+     * @access public
+     * @param string $name 方法名称
+     * @return array|null
+     */
+    public function getRest(string $name = null)
+    {
+        if (is_null($name)) {
+            return $this->rest;
+        }
+
+        return $this->rest[$name] ?? null;
+    }
+
+    /**
+     * 注册未匹配路由规则后的处理
+     * @access public
+     * @param string|Closure $route  路由地址
+     * @param string         $method 请求类型
+     * @return RuleItem
+     */
+    public function miss($route, string $method = '*'): RuleItem
+    {
+        return $this->group->miss($route, $method);
+    }
+
+    /**
+     * 路由调度
+     * @param Request $request
+     * @param Closure|bool $withRoute
+     * @return Response
+     */
+    public function dispatch(Request $request, $withRoute = true)
+    {
+        $this->request = $request;
+        $this->host    = $this->request->host(true);
+
+        if ($withRoute) {
+            //加载路由
+            if ($withRoute instanceof Closure) {
+                $withRoute();
+            }
+            $dispatch = $this->check();
+        } else {
+            $dispatch = $this->url($this->path());
+        }
+
+        $dispatch->init($this->app);
+
+        return $this->app->middleware->pipeline('route')
+            ->send($request)
+            ->then(function () use ($dispatch) {
+                return $dispatch->run();
+            });
+    }
+
+    /**
+     * 检测URL路由
+     * @access public
+     * @return Dispatch|false
+     * @throws RouteNotFoundException
+     */
+    public function check()
+    {
+        // 自动检测域名路由
+        $url = str_replace($this->config['pathinfo_depr'], '|', $this->path());
+
+        $completeMatch = $this->config['route_complete_match'];
+
+        $result = $this->checkDomain()->check($this->request, $url, $completeMatch);
+
+        if (false === $result && !empty($this->cross)) {
+            // 检测跨域路由
+            $result = $this->cross->check($this->request, $url, $completeMatch);
+        }
+
+        if (false !== $result) {
+            return $result;
+        } elseif ($this->config['url_route_must']) {
+            throw new RouteNotFoundException();
+        }
+
+        return $this->url($url);
+    }
+
+    /**
+     * 获取当前请求URL的pathinfo信息(不含URL后缀)
+     * @access protected
+     * @return string
+     */
+    protected function path(): string
+    {
+        $suffix   = $this->config['url_html_suffix'];
+        $pathinfo = $this->request->pathinfo();
+
+        if (false === $suffix) {
+            // 禁止伪静态访问
+            $path = $pathinfo;
+        } elseif ($suffix) {
+            // 去除正常的URL后缀
+            $path = preg_replace('/\.(' . ltrim($suffix, '.') . ')$/i', '', $pathinfo);
+        } else {
+            // 允许任何后缀访问
+            $path = preg_replace('/\.' . $this->request->ext() . '$/i', '', $pathinfo);
+        }
+
+        return $path;
+    }
+
+    /**
+     * 默认URL解析
+     * @access public
+     * @param string $url URL地址
+     * @return Dispatch
+     */
+    public function url(string $url): Dispatch
+    {
+        if ($this->request->method() == 'OPTIONS') {
+            // 自动响应options请求
+            return new Callback($this->request, $this->group, function () {
+                return Response::create('', 'html', 204)->header(['Allow' => 'GET, POST, PUT, DELETE']);
+            });
+        }
+
+        return new UrlDispatch($this->request, $this->group, $url);
+    }
+
+    /**
+     * 检测域名的路由规则
+     * @access protected
+     * @return Domain
+     */
+    protected function checkDomain(): Domain
+    {
+        $item = false;
+
+        if (count($this->domains) > 1) {
+            // 获取当前子域名
+            $subDomain = $this->request->subDomain();
+
+            $domain  = $subDomain ? explode('.', $subDomain) : [];
+            $domain2 = $domain ? array_pop($domain) : '';
+
+            if ($domain) {
+                // 存在三级域名
+                $domain3 = array_pop($domain);
+            }
+
+            if (isset($this->domains[$this->host])) {
+                // 子域名配置
+                $item = $this->domains[$this->host];
+            } elseif (isset($this->domains[$subDomain])) {
+                $item = $this->domains[$subDomain];
+            } elseif (isset($this->domains['*.' . $domain2]) && !empty($domain3)) {
+                // 泛三级域名
+                $item      = $this->domains['*.' . $domain2];
+                $panDomain = $domain3;
+            } elseif (isset($this->domains['*']) && !empty($domain2)) {
+                // 泛二级域名
+                if ('www' != $domain2) {
+                    $item      = $this->domains['*'];
+                    $panDomain = $domain2;
+                }
+            }
+
+            if (isset($panDomain)) {
+                // 保存当前泛域名
+                $this->request->setPanDomain($panDomain);
+            }
+        }
+
+        if (false === $item) {
+            // 检测全局域名规则
+            $item = $this->domains['-'];
+        }
+
+        if (is_string($item)) {
+            $item = $this->domains[$item];
+        }
+
+        return $item;
+    }
+
+    /**
+     * URL生成 支持路由反射
+     * @access public
+     * @param string $url  路由地址
+     * @param array  $vars 参数 ['a'=>'val1', 'b'=>'val2']
+     * @return UrlBuild
+     */
+    public function buildUrl(string $url = '', array $vars = []): UrlBuild
+    {
+        return $this->app->make(UrlBuild::class, [$this, $this->app, $url, $vars], true);
+    }
+
+    /**
+     * 设置全局的路由分组参数
+     * @access public
+     * @param string $method 方法名
+     * @param array  $args   调用参数
+     * @return RuleGroup
+     */
+    public function __call($method, $args)
+    {
+        return call_user_func_array([$this->group, $method], $args);
+    }
+}
diff --git a/vendor/topthink/framework/src/think/Service.php b/vendor/topthink/framework/src/think/Service.php
new file mode 100755
index 0000000..d9e8960
--- /dev/null
+++ b/vendor/topthink/framework/src/think/Service.php
@@ -0,0 +1,66 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+use Closure;
+use think\event\RouteLoaded;
+
+/**
+ * 系统服务基础类
+ * @method void register()
+ * @method void boot()
+ */
+abstract class Service
+{
+    protected $app;
+
+    public function __construct(App $app)
+    {
+        $this->app = $app;
+    }
+
+    /**
+     * 加载路由
+     * @access protected
+     * @param string $path 路由路径
+     */
+    protected function loadRoutesFrom($path)
+    {
+        $this->registerRoutes(function () use ($path) {
+            include $path;
+        });
+    }
+
+    /**
+     * 注册路由
+     * @param Closure $closure
+     */
+    protected function registerRoutes(Closure $closure)
+    {
+        $this->app->event->listen(RouteLoaded::class, $closure);
+    }
+
+    /**
+     * 添加指令
+     * @access protected
+     * @param array|string $commands 指令
+     */
+    protected function commands($commands)
+    {
+        $commands = is_array($commands) ? $commands : func_get_args();
+
+        Console::starting(function (Console $console) use ($commands) {
+            $console->addCommands($commands);
+        });
+    }
+}
diff --git a/vendor/topthink/framework/src/think/Session.php b/vendor/topthink/framework/src/think/Session.php
new file mode 100755
index 0000000..6c84faf
--- /dev/null
+++ b/vendor/topthink/framework/src/think/Session.php
@@ -0,0 +1,65 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+use think\helper\Arr;
+use think\session\Store;
+
+/**
+ * Session管理类
+ * @package think
+ * @mixin Store
+ */
+class Session extends Manager
+{
+    protected $namespace = '\\think\\session\\driver\\';
+
+    protected function createDriver(string $name)
+    {
+        $handler = parent::createDriver($name);
+
+        return new Store($this->getConfig('name') ?: 'PHPSESSID', $handler, $this->getConfig('serialize'));
+    }
+
+    /**
+     * 获取Session配置
+     * @access public
+     * @param null|string $name    名称
+     * @param mixed       $default 默认值
+     * @return mixed
+     */
+    public function getConfig(string $name = null, $default = null)
+    {
+        if (!is_null($name)) {
+            return $this->app->config->get('session.' . $name, $default);
+        }
+
+        return $this->app->config->get('session');
+    }
+
+    protected function resolveConfig(string $name)
+    {
+        $config = $this->app->config->get('session', []);
+        Arr::forget($config, 'type');
+        return $config;
+    }
+
+    /**
+     * 默认驱动
+     * @return string|null
+     */
+    public function getDefaultDriver()
+    {
+        return $this->app->config->get('session.type', 'file');
+    }
+}
diff --git a/vendor/topthink/framework/src/think/Validate.php b/vendor/topthink/framework/src/think/Validate.php
new file mode 100755
index 0000000..0788406
--- /dev/null
+++ b/vendor/topthink/framework/src/think/Validate.php
@@ -0,0 +1,1688 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+use Closure;
+use think\exception\ValidateException;
+use think\helper\Str;
+use think\validate\ValidateRule;
+
+/**
+ * 数据验证类
+ * @package think
+ */
+class Validate
+{
+    /**
+     * 自定义验证类型
+     * @var array
+     */
+    protected $type = [];
+
+    /**
+     * 验证类型别名
+     * @var array
+     */
+    protected $alias = [
+        '>' => 'gt', '>=' => 'egt', '<' => 'lt', '<=' => 'elt', '=' => 'eq', 'same' => 'eq',
+    ];
+
+    /**
+     * 当前验证规则
+     * @var array
+     */
+    protected $rule = [];
+
+    /**
+     * 验证提示信息
+     * @var array
+     */
+    protected $message = [];
+
+    /**
+     * 验证字段描述
+     * @var array
+     */
+    protected $field = [];
+
+    /**
+     * 默认规则提示
+     * @var array
+     */
+    protected $typeMsg = [
+        'require'     => ':attribute require',
+        'must'        => ':attribute must',
+        'number'      => ':attribute must be numeric',
+        'integer'     => ':attribute must be integer',
+        'float'       => ':attribute must be float',
+        'boolean'     => ':attribute must be bool',
+        'email'       => ':attribute not a valid email address',
+        'mobile'      => ':attribute not a valid mobile',
+        'array'       => ':attribute must be a array',
+        'accepted'    => ':attribute must be yes,on or 1',
+        'date'        => ':attribute not a valid datetime',
+        'file'        => ':attribute not a valid file',
+        'image'       => ':attribute not a valid image',
+        'alpha'       => ':attribute must be alpha',
+        'alphaNum'    => ':attribute must be alpha-numeric',
+        'alphaDash'   => ':attribute must be alpha-numeric, dash, underscore',
+        'activeUrl'   => ':attribute not a valid domain or ip',
+        'chs'         => ':attribute must be chinese',
+        'chsAlpha'    => ':attribute must be chinese or alpha',
+        'chsAlphaNum' => ':attribute must be chinese,alpha-numeric',
+        'chsDash'     => ':attribute must be chinese,alpha-numeric,underscore, dash',
+        'url'         => ':attribute not a valid url',
+        'ip'          => ':attribute not a valid ip',
+        'dateFormat'  => ':attribute must be dateFormat of :rule',
+        'in'          => ':attribute must be in :rule',
+        'notIn'       => ':attribute be notin :rule',
+        'between'     => ':attribute must between :1 - :2',
+        'notBetween'  => ':attribute not between :1 - :2',
+        'length'      => 'size of :attribute must be :rule',
+        'max'         => 'max size of :attribute must be :rule',
+        'min'         => 'min size of :attribute must be :rule',
+        'after'       => ':attribute cannot be less than :rule',
+        'before'      => ':attribute cannot exceed :rule',
+        'expire'      => ':attribute not within :rule',
+        'allowIp'     => 'access IP is not allowed',
+        'denyIp'      => 'access IP denied',
+        'confirm'     => ':attribute out of accord with :2',
+        'different'   => ':attribute cannot be same with :2',
+        'egt'         => ':attribute must greater than or equal :rule',
+        'gt'          => ':attribute must greater than :rule',
+        'elt'         => ':attribute must less than or equal :rule',
+        'lt'          => ':attribute must less than :rule',
+        'eq'          => ':attribute must equal :rule',
+        'unique'      => ':attribute has exists',
+        'regex'       => ':attribute not conform to the rules',
+        'method'      => 'invalid Request method',
+        'token'       => 'invalid token',
+        'fileSize'    => 'filesize not match',
+        'fileExt'     => 'extensions to upload is not allowed',
+        'fileMime'    => 'mimetype to upload is not allowed',
+    ];
+
+    /**
+     * 当前验证场景
+     * @var string
+     */
+    protected $currentScene;
+
+    /**
+     * 内置正则验证规则
+     * @var array
+     */
+    protected $defaultRegex = [
+        'alpha'       => '/^[A-Za-z]+$/',
+        'alphaNum'    => '/^[A-Za-z0-9]+$/',
+        'alphaDash'   => '/^[A-Za-z0-9\-\_]+$/',
+        'chs'         => '/^[\x{4e00}-\x{9fa5}\x{9fa6}-\x{9fef}\x{3400}-\x{4db5}\x{20000}-\x{2ebe0}]+$/u',
+        'chsAlpha'    => '/^[\x{4e00}-\x{9fa5}\x{9fa6}-\x{9fef}\x{3400}-\x{4db5}\x{20000}-\x{2ebe0}a-zA-Z]+$/u',
+        'chsAlphaNum' => '/^[\x{4e00}-\x{9fa5}\x{9fa6}-\x{9fef}\x{3400}-\x{4db5}\x{20000}-\x{2ebe0}a-zA-Z0-9]+$/u',
+        'chsDash'     => '/^[\x{4e00}-\x{9fa5}\x{9fa6}-\x{9fef}\x{3400}-\x{4db5}\x{20000}-\x{2ebe0}a-zA-Z0-9\_\-]+$/u',
+        'mobile'      => '/^1[3-9]\d{9}$/',
+        'idCard'      => '/(^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$)/',
+        'zip'         => '/\d{6}/',
+    ];
+
+    /**
+     * Filter_var 规则
+     * @var array
+     */
+    protected $filter = [
+        'email'   => FILTER_VALIDATE_EMAIL,
+        'ip'      => [FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6],
+        'integer' => FILTER_VALIDATE_INT,
+        'url'     => FILTER_VALIDATE_URL,
+        'macAddr' => FILTER_VALIDATE_MAC,
+        'float'   => FILTER_VALIDATE_FLOAT,
+    ];
+
+    /**
+     * 验证场景定义
+     * @var array
+     */
+    protected $scene = [];
+
+    /**
+     * 验证失败错误信息
+     * @var string|array
+     */
+    protected $error = [];
+
+    /**
+     * 是否批量验证
+     * @var bool
+     */
+    protected $batch = false;
+
+    /**
+     * 验证失败是否抛出异常
+     * @var bool
+     */
+    protected $failException = false;
+
+    /**
+     * 场景需要验证的规则
+     * @var array
+     */
+    protected $only = [];
+
+    /**
+     * 场景需要移除的验证规则
+     * @var array
+     */
+    protected $remove = [];
+
+    /**
+     * 场景需要追加的验证规则
+     * @var array
+     */
+    protected $append = [];
+
+    /**
+     * 验证正则定义
+     * @var array
+     */
+    protected $regex = [];
+
+    /**
+     * Db对象
+     * @var Db
+     */
+    protected $db;
+
+    /**
+     * 语言对象
+     * @var Lang
+     */
+    protected $lang;
+
+    /**
+     * 请求对象
+     * @var Request
+     */
+    protected $request;
+
+    /**
+     * @var Closure[]
+     */
+    protected static $maker = [];
+
+    /**
+     * 构造方法
+     * @access public
+     */
+    public function __construct()
+    {
+        if (!empty(static::$maker)) {
+            foreach (static::$maker as $maker) {
+                call_user_func($maker, $this);
+            }
+        }
+    }
+
+    /**
+     * 设置服务注入
+     * @access public
+     * @param Closure $maker
+     * @return void
+     */
+    public static function maker(Closure $maker)
+    {
+        static::$maker[] = $maker;
+    }
+
+    /**
+     * 设置Lang对象
+     * @access public
+     * @param Lang $lang Lang对象
+     * @return void
+     */
+    public function setLang(Lang $lang)
+    {
+        $this->lang = $lang;
+    }
+
+    /**
+     * 设置Db对象
+     * @access public
+     * @param Db $db Db对象
+     * @return void
+     */
+    public function setDb(Db $db)
+    {
+        $this->db = $db;
+    }
+
+    /**
+     * 设置Request对象
+     * @access public
+     * @param Request $request Request对象
+     * @return void
+     */
+    public function setRequest(Request $request)
+    {
+        $this->request = $request;
+    }
+
+    /**
+     * 添加字段验证规则
+     * @access protected
+     * @param string|array $name 字段名称或者规则数组
+     * @param mixed        $rule 验证规则或者字段描述信息
+     * @return $this
+     */
+    public function rule($name, $rule = '')
+    {
+        if (is_array($name)) {
+            $this->rule = $name + $this->rule;
+            if (is_array($rule)) {
+                $this->field = array_merge($this->field, $rule);
+            }
+        } else {
+            $this->rule[$name] = $rule;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 注册验证(类型)规则
+     * @access public
+     * @param string   $type     验证规则类型
+     * @param callable $callback callback方法(或闭包)
+     * @param string   $message  验证失败提示信息
+     * @return $this
+     */
+    public function extend(string $type, callable $callback = null, string $message = null)
+    {
+        $this->type[$type] = $callback;
+
+        if ($message) {
+            $this->typeMsg[$type] = $message;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 设置验证规则的默认提示信息
+     * @access public
+     * @param string|array $type 验证规则类型名称或者数组
+     * @param string       $msg  验证提示信息
+     * @return void
+     */
+    public function setTypeMsg($type, string $msg = null): void
+    {
+        if (is_array($type)) {
+            $this->typeMsg = array_merge($this->typeMsg, $type);
+        } else {
+            $this->typeMsg[$type] = $msg;
+        }
+    }
+
+    /**
+     * 设置提示信息
+     * @access public
+     * @param array $message 错误信息
+     * @return Validate
+     */
+    public function message(array $message)
+    {
+        $this->message = array_merge($this->message, $message);
+
+        return $this;
+    }
+
+    /**
+     * 设置验证场景
+     * @access public
+     * @param string $name 场景名
+     * @return $this
+     */
+    public function scene(string $name)
+    {
+        // 设置当前场景
+        $this->currentScene = $name;
+
+        return $this;
+    }
+
+    /**
+     * 判断是否存在某个验证场景
+     * @access public
+     * @param string $name 场景名
+     * @return bool
+     */
+    public function hasScene(string $name): bool
+    {
+        return isset($this->scene[$name]) || method_exists($this, 'scene' . $name);
+    }
+
+    /**
+     * 设置批量验证
+     * @access public
+     * @param bool $batch 是否批量验证
+     * @return $this
+     */
+    public function batch(bool $batch = true)
+    {
+        $this->batch = $batch;
+
+        return $this;
+    }
+
+    /**
+     * 设置验证失败后是否抛出异常
+     * @access protected
+     * @param bool $fail 是否抛出异常
+     * @return $this
+     */
+    public function failException(bool $fail = true)
+    {
+        $this->failException = $fail;
+
+        return $this;
+    }
+
+    /**
+     * 指定需要验证的字段列表
+     * @access public
+     * @param array $fields 字段名
+     * @return $this
+     */
+    public function only(array $fields)
+    {
+        $this->only = $fields;
+
+        return $this;
+    }
+
+    /**
+     * 移除某个字段的验证规则
+     * @access public
+     * @param string|array $field 字段名
+     * @param mixed        $rule  验证规则 true 移除所有规则
+     * @return $this
+     */
+    public function remove($field, $rule = null)
+    {
+        if (is_array($field)) {
+            foreach ($field as $key => $rule) {
+                if (is_int($key)) {
+                    $this->remove($rule);
+                } else {
+                    $this->remove($key, $rule);
+                }
+            }
+        } else {
+            if (is_string($rule)) {
+                $rule = explode('|', $rule);
+            }
+
+            $this->remove[$field] = $rule;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 追加某个字段的验证规则
+     * @access public
+     * @param string|array $field 字段名
+     * @param mixed        $rule  验证规则
+     * @return $this
+     */
+    public function append($field, $rule = null)
+    {
+        if (is_array($field)) {
+            foreach ($field as $key => $rule) {
+                $this->append($key, $rule);
+            }
+        } else {
+            if (is_string($rule)) {
+                $rule = explode('|', $rule);
+            }
+
+            $this->append[$field] = $rule;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 数据自动验证
+     * @access public
+     * @param array $data  数据
+     * @param array $rules 验证规则
+     * @return bool
+     */
+    public function check(array $data, array $rules = []): bool
+    {
+        $this->error = [];
+
+        if ($this->currentScene) {
+            $this->getScene($this->currentScene);
+        }
+
+        if (empty($rules)) {
+            // 读取验证规则
+            $rules = $this->rule;
+        }
+
+        foreach ($this->append as $key => $rule) {
+            if (!isset($rules[$key])) {
+                $rules[$key] = $rule;
+                unset($this->append[$key]);
+            }
+        }
+
+        foreach ($rules as $key => $rule) {
+            // field => 'rule1|rule2...' field => ['rule1','rule2',...]
+            if (strpos($key, '|')) {
+                // 字段|描述 用于指定属性名称
+                [$key, $title] = explode('|', $key);
+            } else {
+                $title = $this->field[$key] ?? $key;
+            }
+
+            // 场景检测
+            if (!empty($this->only) && !in_array($key, $this->only)) {
+                continue;
+            }
+
+            // 获取数据 支持二维数组
+            $value = $this->getDataValue($data, $key);
+
+            // 字段验证
+            if ($rule instanceof Closure) {
+                $result = call_user_func_array($rule, [$value, $data]);
+            } elseif ($rule instanceof ValidateRule) {
+                //  验证因子
+                $result = $this->checkItem($key, $value, $rule->getRule(), $data, $rule->getTitle() ?: $title, $rule->getMsg());
+            } else {
+                $result = $this->checkItem($key, $value, $rule, $data, $title);
+            }
+
+            if (true !== $result) {
+                // 没有返回true 则表示验证失败
+                if (!empty($this->batch)) {
+                    // 批量验证
+                    $this->error[$key] = $result;
+                } elseif ($this->failException) {
+                    throw new ValidateException($result);
+                } else {
+                    $this->error = $result;
+                    return false;
+                }
+            }
+        }
+
+        if (!empty($this->error)) {
+            if ($this->failException) {
+                throw new ValidateException($this->error);
+            }
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * 根据验证规则验证数据
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rules 验证规则
+     * @return bool
+     */
+    public function checkRule($value, $rules): bool
+    {
+        if ($rules instanceof Closure) {
+            return call_user_func_array($rules, [$value]);
+        } elseif ($rules instanceof ValidateRule) {
+            $rules = $rules->getRule();
+        } elseif (is_string($rules)) {
+            $rules = explode('|', $rules);
+        }
+
+        foreach ($rules as $key => $rule) {
+            if ($rule instanceof Closure) {
+                $result = call_user_func_array($rule, [$value]);
+            } else {
+                // 判断验证类型
+                [$type, $rule] = $this->getValidateType($key, $rule);
+
+                $callback = $this->type[$type] ?? [$this, $type];
+
+                $result = call_user_func_array($callback, [$value, $rule]);
+            }
+
+            if (true !== $result) {
+                if ($this->failException) {
+                    throw new ValidateException($result);
+                }
+
+                return $result;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * 验证单个字段规则
+     * @access protected
+     * @param string $field 字段名
+     * @param mixed  $value 字段值
+     * @param mixed  $rules 验证规则
+     * @param array  $data  数据
+     * @param string $title 字段描述
+     * @param array  $msg   提示信息
+     * @return mixed
+     */
+    protected function checkItem(string $field, $value, $rules, $data, string $title = '', array $msg = [])
+    {
+        if (isset($this->remove[$field]) && true === $this->remove[$field] && empty($this->append[$field])) {
+            // 字段已经移除 无需验证
+            return true;
+        }
+
+        // 支持多规则验证 require|in:a,b,c|... 或者 ['require','in'=>'a,b,c',...]
+        if (is_string($rules)) {
+            $rules = explode('|', $rules);
+        }
+
+        if (isset($this->append[$field])) {
+            // 追加额外的验证规则
+            $rules = array_unique(array_merge($rules, $this->append[$field]), SORT_REGULAR);
+            unset($this->append[$field]);
+        }
+
+        if (empty($rules)) {
+            return true;
+        }
+
+        $i = 0;
+        foreach ($rules as $key => $rule) {
+            if ($rule instanceof Closure) {
+                $result = call_user_func_array($rule, [$value, $data]);
+                $info   = is_numeric($key) ? '' : $key;
+            } else {
+                // 判断验证类型
+                [$type, $rule, $info] = $this->getValidateType($key, $rule);
+
+                if (isset($this->append[$field]) && in_array($info, $this->append[$field])) {
+                } elseif (isset($this->remove[$field]) && in_array($info, $this->remove[$field])) {
+                    // 规则已经移除
+                    $i++;
+                    continue;
+                }
+
+                if (isset($this->type[$type])) {
+                    $result = call_user_func_array($this->type[$type], [$value, $rule, $data, $field, $title]);
+                } elseif ('must' == $info || 0 === strpos($info, 'require') || (!is_null($value) && '' !== $value)) {
+                    $result = call_user_func_array([$this, $type], [$value, $rule, $data, $field, $title]);
+                } else {
+                    $result = true;
+                }
+            }
+
+            if (false === $result) {
+                // 验证失败 返回错误信息
+                if (!empty($msg[$i])) {
+                    $message = $msg[$i];
+                    if (is_string($message) && strpos($message, '{%') === 0) {
+                        $message = $this->lang->get(substr($message, 2, -1));
+                    }
+                } else {
+                    $message = $this->getRuleMsg($field, $title, $info, $rule);
+                }
+
+                return $message;
+            } elseif (true !== $result) {
+                // 返回自定义错误信息
+                if (is_string($result) && false !== strpos($result, ':')) {
+                    $result = str_replace(':attribute', $title, $result);
+
+                    if (strpos($result, ':rule') && is_scalar($rule)) {
+                        $result = str_replace(':rule', (string) $rule, $result);
+                    }
+                }
+
+                return $result;
+            }
+            $i++;
+        }
+
+        return $result ?? true;
+    }
+
+    /**
+     * 获取当前验证类型及规则
+     * @access public
+     * @param mixed $key
+     * @param mixed $rule
+     * @return array
+     */
+    protected function getValidateType($key, $rule): array
+    {
+        // 判断验证类型
+        if (!is_numeric($key)) {
+            if (isset($this->alias[$key])) {
+                // 判断别名
+                $key = $this->alias[$key];
+            }
+            return [$key, $rule, $key];
+        }
+
+        if (strpos($rule, ':')) {
+            [$type, $rule] = explode(':', $rule, 2);
+            if (isset($this->alias[$type])) {
+                // 判断别名
+                $type = $this->alias[$type];
+            }
+            $info = $type;
+        } elseif (method_exists($this, $rule)) {
+            $type = $rule;
+            $info = $rule;
+            $rule = '';
+        } else {
+            $type = 'is';
+            $info = $rule;
+        }
+
+        return [$type, $rule, $info];
+    }
+
+    /**
+     * 验证是否和某个字段的值一致
+     * @access public
+     * @param mixed  $value 字段值
+     * @param mixed  $rule  验证规则
+     * @param array  $data  数据
+     * @param string $field 字段名
+     * @return bool
+     */
+    public function confirm($value, $rule, array $data = [], string $field = ''): bool
+    {
+        if ('' == $rule) {
+            if (strpos($field, '_confirm')) {
+                $rule = strstr($field, '_confirm', true);
+            } else {
+                $rule = $field . '_confirm';
+            }
+        }
+
+        return $this->getDataValue($data, $rule) === $value;
+    }
+
+    /**
+     * 验证是否和某个字段的值是否不同
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @param array $data  数据
+     * @return bool
+     */
+    public function different($value, $rule, array $data = []): bool
+    {
+        return $this->getDataValue($data, $rule) != $value;
+    }
+
+    /**
+     * 验证是否大于等于某个值
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @param array $data  数据
+     * @return bool
+     */
+    public function egt($value, $rule, array $data = []): bool
+    {
+        return $value >= $this->getDataValue($data, $rule);
+    }
+
+    /**
+     * 验证是否大于某个值
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @param array $data  数据
+     * @return bool
+     */
+    public function gt($value, $rule, array $data = []): bool
+    {
+        return $value > $this->getDataValue($data, $rule);
+    }
+
+    /**
+     * 验证是否小于等于某个值
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @param array $data  数据
+     * @return bool
+     */
+    public function elt($value, $rule, array $data = []): bool
+    {
+        return $value <= $this->getDataValue($data, $rule);
+    }
+
+    /**
+     * 验证是否小于某个值
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @param array $data  数据
+     * @return bool
+     */
+    public function lt($value, $rule, array $data = []): bool
+    {
+        return $value < $this->getDataValue($data, $rule);
+    }
+
+    /**
+     * 验证是否等于某个值
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @return bool
+     */
+    public function eq($value, $rule): bool
+    {
+        return $value == $rule;
+    }
+
+    /**
+     * 必须验证
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @return bool
+     */
+    public function must($value, $rule = null): bool
+    {
+        return !empty($value) || '0' == $value;
+    }
+
+    /**
+     * 验证字段值是否为有效格式
+     * @access public
+     * @param mixed  $value 字段值
+     * @param string $rule  验证规则
+     * @param array  $data  数据
+     * @return bool
+     */
+    public function is($value, string $rule, array $data = []): bool
+    {
+        switch (Str::camel($rule)) {
+            case 'require':
+                // 必须
+                $result = !empty($value) || '0' == $value;
+                break;
+            case 'accepted':
+                // 接受
+                $result = in_array($value, ['1', 'on', 'yes']);
+                break;
+            case 'date':
+                // 是否是一个有效日期
+                $result = false !== strtotime($value);
+                break;
+            case 'activeUrl':
+                // 是否为有效的网址
+                $result = checkdnsrr($value);
+                break;
+            case 'boolean':
+            case 'bool':
+                // 是否为布尔值
+                $result = in_array($value, [true, false, 0, 1, '0', '1'], true);
+                break;
+            case 'number':
+                $result = ctype_digit((string) $value);
+                break;
+            case 'alphaNum':
+                $result = ctype_alnum($value);
+                break;
+            case 'array':
+                // 是否为数组
+                $result = is_array($value);
+                break;
+            case 'file':
+                $result = $value instanceof File;
+                break;
+            case 'image':
+                $result = $value instanceof File && in_array($this->getImageType($value->getRealPath()), [1, 2, 3, 6]);
+                break;
+            case 'token':
+                $result = $this->token($value, '__token__', $data);
+                break;
+            default:
+                if (isset($this->type[$rule])) {
+                    // 注册的验证规则
+                    $result = call_user_func_array($this->type[$rule], [$value]);
+                } elseif (function_exists('ctype_' . $rule)) {
+                    // ctype验证规则
+                    $ctypeFun = 'ctype_' . $rule;
+                    $result   = $ctypeFun($value);
+                } elseif (isset($this->filter[$rule])) {
+                    // Filter_var验证规则
+                    $result = $this->filter($value, $this->filter[$rule]);
+                } else {
+                    // 正则验证
+                    $result = $this->regex($value, $rule);
+                }
+        }
+
+        return $result;
+    }
+
+    // 判断图像类型
+    protected function getImageType($image)
+    {
+        if (function_exists('exif_imagetype')) {
+            return exif_imagetype($image);
+        }
+
+        try {
+            $info = getimagesize($image);
+            return $info ? $info[2] : false;
+        } catch (\Exception $e) {
+            return false;
+        }
+    }
+
+    /**
+     * 验证表单令牌
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @param array $data  数据
+     * @return bool
+     */
+    public function token($value, string $rule, array $data): bool
+    {
+        $rule = !empty($rule) ? $rule : '__token__';
+        return $this->request->checkToken($rule, $data);
+    }
+
+    /**
+     * 验证是否为合格的域名或者IP 支持A,MX,NS,SOA,PTR,CNAME,AAAA,A6, SRV,NAPTR,TXT 或者 ANY类型
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @return bool
+     */
+    public function activeUrl(string $value, string $rule = 'MX'): bool
+    {
+        if (!in_array($rule, ['A', 'MX', 'NS', 'SOA', 'PTR', 'CNAME', 'AAAA', 'A6', 'SRV', 'NAPTR', 'TXT', 'ANY'])) {
+            $rule = 'MX';
+        }
+
+        return checkdnsrr($value, $rule);
+    }
+
+    /**
+     * 验证是否有效IP
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则 ipv4 ipv6
+     * @return bool
+     */
+    public function ip($value, string $rule = 'ipv4'): bool
+    {
+        if (!in_array($rule, ['ipv4', 'ipv6'])) {
+            $rule = 'ipv4';
+        }
+
+        return $this->filter($value, [FILTER_VALIDATE_IP, 'ipv6' == $rule ? FILTER_FLAG_IPV6 : FILTER_FLAG_IPV4]);
+    }
+
+    /**
+     * 检测上传文件后缀
+     * @access public
+     * @param File         $file
+     * @param array|string $ext 允许后缀
+     * @return bool
+     */
+    protected function checkExt(File $file, $ext): bool
+    {
+        if (is_string($ext)) {
+            $ext = explode(',', $ext);
+        }
+
+        return in_array(strtolower($file->extension()), $ext);
+    }
+
+    /**
+     * 检测上传文件大小
+     * @access public
+     * @param File    $file
+     * @param integer $size 最大大小
+     * @return bool
+     */
+    protected function checkSize(File $file, $size): bool
+    {
+        return $file->getSize() <= (int) $size;
+    }
+
+    /**
+     * 检测上传文件类型
+     * @access public
+     * @param File         $file
+     * @param array|string $mime 允许类型
+     * @return bool
+     */
+    protected function checkMime(File $file, $mime): bool
+    {
+        if (is_string($mime)) {
+            $mime = explode(',', $mime);
+        }
+
+        return in_array(strtolower($file->getMime()), $mime);
+    }
+
+    /**
+     * 验证上传文件后缀
+     * @access public
+     * @param mixed $file 上传文件
+     * @param mixed $rule 验证规则
+     * @return bool
+     */
+    public function fileExt($file, $rule): bool
+    {
+        if (is_array($file)) {
+            foreach ($file as $item) {
+                if (!($item instanceof File) || !$this->checkExt($item, $rule)) {
+                    return false;
+                }
+            }
+            return true;
+        } elseif ($file instanceof File) {
+            return $this->checkExt($file, $rule);
+        }
+
+        return false;
+    }
+
+    /**
+     * 验证上传文件类型
+     * @access public
+     * @param mixed $file 上传文件
+     * @param mixed $rule 验证规则
+     * @return bool
+     */
+    public function fileMime($file, $rule): bool
+    {
+        if (is_array($file)) {
+            foreach ($file as $item) {
+                if (!($item instanceof File) || !$this->checkMime($item, $rule)) {
+                    return false;
+                }
+            }
+            return true;
+        } elseif ($file instanceof File) {
+            return $this->checkMime($file, $rule);
+        }
+
+        return false;
+    }
+
+    /**
+     * 验证上传文件大小
+     * @access public
+     * @param mixed $file 上传文件
+     * @param mixed $rule 验证规则
+     * @return bool
+     */
+    public function fileSize($file, $rule): bool
+    {
+        if (is_array($file)) {
+            foreach ($file as $item) {
+                if (!($item instanceof File) || !$this->checkSize($item, $rule)) {
+                    return false;
+                }
+            }
+            return true;
+        } elseif ($file instanceof File) {
+            return $this->checkSize($file, $rule);
+        }
+
+        return false;
+    }
+
+    /**
+     * 验证图片的宽高及类型
+     * @access public
+     * @param mixed $file 上传文件
+     * @param mixed $rule 验证规则
+     * @return bool
+     */
+    public function image($file, $rule): bool
+    {
+        if (!($file instanceof File)) {
+            return false;
+        }
+
+        if ($rule) {
+            $rule = explode(',', $rule);
+
+            [$width, $height, $type] = getimagesize($file->getRealPath());
+
+            if (isset($rule[2])) {
+                $imageType = strtolower($rule[2]);
+
+                if ('jpg' == $imageType) {
+                    $imageType = 'jpeg';
+                }
+
+                if (image_type_to_extension($type, false) != $imageType) {
+                    return false;
+                }
+            }
+
+            [$w, $h] = $rule;
+
+            return $w == $width && $h == $height;
+        }
+
+        return in_array($this->getImageType($file->getRealPath()), [1, 2, 3, 6]);
+    }
+
+    /**
+     * 验证时间和日期是否符合指定格式
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @return bool
+     */
+    public function dateFormat($value, $rule): bool
+    {
+        $info = date_parse_from_format($rule, $value);
+        return 0 == $info['warning_count'] && 0 == $info['error_count'];
+    }
+
+    /**
+     * 验证是否唯一
+     * @access public
+     * @param mixed  $value 字段值
+     * @param mixed  $rule  验证规则 格式:数据表,字段名,排除ID,主键名
+     * @param array  $data  数据
+     * @param string $field 验证字段名
+     * @return bool
+     */
+    public function unique($value, $rule, array $data = [], string $field = ''): bool
+    {
+        if (is_string($rule)) {
+            $rule = explode(',', $rule);
+        }
+
+        if (false !== strpos($rule[0], '\\')) {
+            // 指定模型类
+            $db = new $rule[0];
+        } else {
+            $db = $this->db->name($rule[0]);
+        }
+
+        $key = $rule[1] ?? $field;
+        $map = [];
+
+        if (strpos($key, '^')) {
+            // 支持多个字段验证
+            $fields = explode('^', $key);
+            foreach ($fields as $key) {
+                if (isset($data[$key])) {
+                    $map[] = [$key, '=', $data[$key]];
+                }
+            }
+        } elseif (isset($data[$field])) {
+            $map[] = [$key, '=', $data[$field]];
+        } else {
+            $map = [];
+        }
+
+        $pk = !empty($rule[3]) ? $rule[3] : $db->getPk();
+
+        if (is_string($pk)) {
+            if (isset($rule[2])) {
+                $map[] = [$pk, '<>', $rule[2]];
+            } elseif (isset($data[$pk])) {
+                $map[] = [$pk, '<>', $data[$pk]];
+            }
+        }
+
+        if ($db->where($map)->field($pk)->find()) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * 使用filter_var方式验证
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @return bool
+     */
+    public function filter($value, $rule): bool
+    {
+        if (is_string($rule) && strpos($rule, ',')) {
+            [$rule, $param] = explode(',', $rule);
+        } elseif (is_array($rule)) {
+            $param = $rule[1] ?? 0;
+            $rule  = $rule[0];
+        } else {
+            $param = 0;
+        }
+
+        return false !== filter_var($value, is_int($rule) ? $rule : filter_id($rule), $param);
+    }
+
+    /**
+     * 验证某个字段等于某个值的时候必须
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @param array $data  数据
+     * @return bool
+     */
+    public function requireIf($value, $rule, array $data = []): bool
+    {
+        [$field, $val] = explode(',', $rule);
+
+        if ($this->getDataValue($data, $field) == $val) {
+            return !empty($value) || '0' == $value;
+        }
+
+        return true;
+    }
+
+    /**
+     * 通过回调方法验证某个字段是否必须
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @param array $data  数据
+     * @return bool
+     */
+    public function requireCallback($value, $rule, array $data = []): bool
+    {
+        $result = call_user_func_array([$this, $rule], [$value, $data]);
+
+        if ($result) {
+            return !empty($value) || '0' == $value;
+        }
+
+        return true;
+    }
+
+    /**
+     * 验证某个字段有值的情况下必须
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @param array $data  数据
+     * @return bool
+     */
+    public function requireWith($value, $rule, array $data = []): bool
+    {
+        $val = $this->getDataValue($data, $rule);
+
+        if (!empty($val)) {
+            return !empty($value) || '0' == $value;
+        }
+
+        return true;
+    }
+
+    /**
+     * 验证某个字段没有值的情况下必须
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @param array $data  数据
+     * @return bool
+     */
+    public function requireWithout($value, $rule, array $data = []): bool
+    {
+        $val = $this->getDataValue($data, $rule);
+
+        if (empty($val)) {
+            return !empty($value) || '0' == $value;
+        }
+
+        return true;
+    }
+
+    /**
+     * 验证是否在范围内
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @return bool
+     */
+    public function in($value, $rule): bool
+    {
+        return in_array($value, is_array($rule) ? $rule : explode(',', $rule));
+    }
+
+    /**
+     * 验证是否不在某个范围
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @return bool
+     */
+    public function notIn($value, $rule): bool
+    {
+        return !in_array($value, is_array($rule) ? $rule : explode(',', $rule));
+    }
+
+    /**
+     * between验证数据
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @return bool
+     */
+    public function between($value, $rule): bool
+    {
+        if (is_string($rule)) {
+            $rule = explode(',', $rule);
+        }
+        [$min, $max] = $rule;
+
+        return $value >= $min && $value <= $max;
+    }
+
+    /**
+     * 使用notbetween验证数据
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @return bool
+     */
+    public function notBetween($value, $rule): bool
+    {
+        if (is_string($rule)) {
+            $rule = explode(',', $rule);
+        }
+        [$min, $max] = $rule;
+
+        return $value < $min || $value > $max;
+    }
+
+    /**
+     * 验证数据长度
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @return bool
+     */
+    public function length($value, $rule): bool
+    {
+        if (is_array($value)) {
+            $length = count($value);
+        } elseif ($value instanceof File) {
+            $length = $value->getSize();
+        } else {
+            $length = mb_strlen((string) $value);
+        }
+
+        if (is_string($rule) && strpos($rule, ',')) {
+            // 长度区间
+            [$min, $max] = explode(',', $rule);
+            return $length >= $min && $length <= $max;
+        }
+
+        // 指定长度
+        return $length == $rule;
+    }
+
+    /**
+     * 验证数据最大长度
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @return bool
+     */
+    public function max($value, $rule): bool
+    {
+        if (is_array($value)) {
+            $length = count($value);
+        } elseif ($value instanceof File) {
+            $length = $value->getSize();
+        } else {
+            $length = mb_strlen((string) $value);
+        }
+
+        return $length <= $rule;
+    }
+
+    /**
+     * 验证数据最小长度
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @return bool
+     */
+    public function min($value, $rule): bool
+    {
+        if (is_array($value)) {
+            $length = count($value);
+        } elseif ($value instanceof File) {
+            $length = $value->getSize();
+        } else {
+            $length = mb_strlen((string) $value);
+        }
+
+        return $length >= $rule;
+    }
+
+    /**
+     * 验证日期
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @param array $data  数据
+     * @return bool
+     */
+    public function after($value, $rule, array $data = []): bool
+    {
+        return strtotime($value) >= strtotime($rule);
+    }
+
+    /**
+     * 验证日期
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @param array $data  数据
+     * @return bool
+     */
+    public function before($value, $rule, array $data = []): bool
+    {
+        return strtotime($value) <= strtotime($rule);
+    }
+
+    /**
+     * 验证日期
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @param array $data  数据
+     * @return bool
+     */
+    public function afterWith($value, $rule, array $data = []): bool
+    {
+        $rule = $this->getDataValue($data, $rule);
+        return !is_null($rule) && strtotime($value) >= strtotime($rule);
+    }
+
+    /**
+     * 验证日期
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @param array $data  数据
+     * @return bool
+     */
+    public function beforeWith($value, $rule, array $data = []): bool
+    {
+        $rule = $this->getDataValue($data, $rule);
+        return !is_null($rule) && strtotime($value) <= strtotime($rule);
+    }
+
+    /**
+     * 验证有效期
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @return bool
+     */
+    public function expire($value, $rule): bool
+    {
+        if (is_string($rule)) {
+            $rule = explode(',', $rule);
+        }
+
+        [$start, $end] = $rule;
+
+        if (!is_numeric($start)) {
+            $start = strtotime($start);
+        }
+
+        if (!is_numeric($end)) {
+            $end = strtotime($end);
+        }
+
+        return time() >= $start && time() <= $end;
+    }
+
+    /**
+     * 验证IP许可
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @return bool
+     */
+    public function allowIp($value, $rule): bool
+    {
+        return in_array($value, is_array($rule) ? $rule : explode(',', $rule));
+    }
+
+    /**
+     * 验证IP禁用
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则
+     * @return bool
+     */
+    public function denyIp($value, $rule): bool
+    {
+        return !in_array($value, is_array($rule) ? $rule : explode(',', $rule));
+    }
+
+    /**
+     * 使用正则验证数据
+     * @access public
+     * @param mixed $value 字段值
+     * @param mixed $rule  验证规则 正则规则或者预定义正则名
+     * @return bool
+     */
+    public function regex($value, $rule): bool
+    {
+        if (isset($this->regex[$rule])) {
+            $rule = $this->regex[$rule];
+        } elseif (isset($this->defaultRegex[$rule])) {
+            $rule = $this->defaultRegex[$rule];
+        }
+
+        if (is_string($rule) && 0 !== strpos($rule, '/') && !preg_match('/\/[imsU]{0,4}$/', $rule)) {
+            // 不是正则表达式则两端补上/
+            $rule = '/^' . $rule . '$/';
+        }
+
+        return is_scalar($value) && 1 === preg_match($rule, (string) $value);
+    }
+
+    /**
+     * 获取错误信息
+     * @return array|string
+     */
+    public function getError()
+    {
+        return $this->error;
+    }
+
+    /**
+     * 获取数据值
+     * @access protected
+     * @param array  $data 数据
+     * @param string $key  数据标识 支持二维
+     * @return mixed
+     */
+    protected function getDataValue(array $data, $key)
+    {
+        if (is_numeric($key)) {
+            $value = $key;
+        } elseif (is_string($key) && strpos($key, '.')) {
+            // 支持多维数组验证
+            foreach (explode('.', $key) as $key) {
+                if (!isset($data[$key])) {
+                    $value = null;
+                    break;
+                }
+                $value = $data = $data[$key];
+            }
+        } else {
+            $value = $data[$key] ?? null;
+        }
+
+        return $value;
+    }
+
+    /**
+     * 获取验证规则的错误提示信息
+     * @access protected
+     * @param string $attribute 字段英文名
+     * @param string $title     字段描述名
+     * @param string $type      验证规则名称
+     * @param mixed  $rule      验证规则数据
+     * @return string|array
+     */
+    protected function getRuleMsg(string $attribute, string $title, string $type, $rule)
+    {
+        if (isset($this->message[$attribute . '.' . $type])) {
+            $msg = $this->message[$attribute . '.' . $type];
+        } elseif (isset($this->message[$attribute][$type])) {
+            $msg = $this->message[$attribute][$type];
+        } elseif (isset($this->message[$attribute])) {
+            $msg = $this->message[$attribute];
+        } elseif (isset($this->typeMsg[$type])) {
+            $msg = $this->typeMsg[$type];
+        } elseif (0 === strpos($type, 'require')) {
+            $msg = $this->typeMsg['require'];
+        } else {
+            $msg = $title . $this->lang->get('not conform to the rules');
+        }
+
+        if (is_array($msg)) {
+            return $this->errorMsgIsArray($msg, $rule, $title);
+        }
+
+        return $this->parseErrorMsg($msg, $rule, $title);
+    }
+
+    /**
+     * 获取验证规则的错误提示信息
+     * @access protected
+     * @param string $msg   错误信息
+     * @param mixed  $rule  验证规则数据
+     * @param string $title 字段描述名
+     * @return string|array
+     */
+    protected function parseErrorMsg(string $msg, $rule, string $title)
+    {
+        if (0 === strpos($msg, '{%')) {
+            $msg = $this->lang->get(substr($msg, 2, -1));
+        } elseif ($this->lang->has($msg)) {
+            $msg = $this->lang->get($msg);
+        }
+
+        if (is_array($msg)) {
+            return $this->errorMsgIsArray($msg, $rule, $title);
+        }
+
+        // rule若是数组则转为字符串
+        if (is_array($rule)) {
+            $rule = implode(',', $rule);
+        }
+
+        if (is_scalar($rule) && false !== strpos($msg, ':')) {
+            // 变量替换
+            if (is_string($rule) && strpos($rule, ',')) {
+                $array = array_pad(explode(',', $rule), 3, '');
+            } else {
+                $array = array_pad([], 3, '');
+            }
+
+            $msg = str_replace(
+                [':attribute', ':1', ':2', ':3'],
+                [$title, $array[0], $array[1], $array[2]],
+                $msg
+            );
+
+            if (strpos($msg, ':rule')) {
+                $msg = str_replace(':rule', (string) $rule, $msg);
+            }
+        }
+
+        return $msg;
+    }
+
+    /**
+     * 错误信息数组处理
+     * @access protected
+     * @param array $msg   错误信息
+     * @param mixed  $rule  验证规则数据
+     * @param string $title 字段描述名
+     * @return array
+     */
+    protected function errorMsgIsArray(array $msg, $rule, string $title)
+    {
+        foreach ($msg as $key => $val) {
+            if (is_string($val)) {
+                $msg[$key] = $this->parseErrorMsg($val, $rule, $title);
+            }
+        }
+        return $msg;
+    }
+
+    /**
+     * 获取数据验证的场景
+     * @access protected
+     * @param string $scene 验证场景
+     * @return void
+     */
+    protected function getScene(string $scene): void
+    {
+        $this->only = $this->append = $this->remove = [];
+
+        if (method_exists($this, 'scene' . $scene)) {
+            call_user_func([$this, 'scene' . $scene]);
+        } elseif (isset($this->scene[$scene])) {
+            // 如果设置了验证适用场景
+            $this->only = $this->scene[$scene];
+        }
+    }
+
+    /**
+     * 动态方法 直接调用is方法进行验证
+     * @access public
+     * @param string $method 方法名
+     * @param array  $args   调用参数
+     * @return bool
+     */
+    public function __call($method, $args)
+    {
+        if ('is' == strtolower(substr($method, 0, 2))) {
+            $method = substr($method, 2);
+        }
+
+        array_push($args, lcfirst($method));
+
+        return call_user_func_array([$this, 'is'], $args);
+    }
+}
diff --git a/vendor/topthink/framework/src/think/View.php b/vendor/topthink/framework/src/think/View.php
new file mode 100755
index 0000000..2e71088
--- /dev/null
+++ b/vendor/topthink/framework/src/think/View.php
@@ -0,0 +1,191 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+use think\helper\Arr;
+
+/**
+ * 视图类
+ * @package think
+ */
+class View extends Manager
+{
+
+    protected $namespace = '\\think\\view\\driver\\';
+
+    /**
+     * 模板变量
+     * @var array
+     */
+    protected $data = [];
+
+    /**
+     * 内容过滤
+     * @var mixed
+     */
+    protected $filter;
+
+    /**
+     * 获取模板引擎
+     * @access public
+     * @param string $type 模板引擎类型
+     * @return $this
+     */
+    public function engine(string $type = null)
+    {
+        return $this->driver($type);
+    }
+
+    /**
+     * 模板变量赋值
+     * @access public
+     * @param string|array $name  模板变量
+     * @param mixed        $value 变量值
+     * @return $this
+     */
+    public function assign($name, $value = null)
+    {
+        if (is_array($name)) {
+            $this->data = array_merge($this->data, $name);
+        } else {
+            $this->data[$name] = $value;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 视图过滤
+     * @access public
+     * @param Callable $filter 过滤方法或闭包
+     * @return $this
+     */
+    public function filter(callable $filter = null)
+    {
+        $this->filter = $filter;
+        return $this;
+    }
+
+    /**
+     * 解析和获取模板内容 用于输出
+     * @access public
+     * @param string $template 模板文件名或者内容
+     * @param array  $vars     模板变量
+     * @return string
+     * @throws \Exception
+     */
+    public function fetch(string $template = '', array $vars = []): string
+    {
+        return $this->getContent(function () use ($vars, $template) {
+            $this->engine()->fetch($template, array_merge($this->data, $vars));
+        });
+    }
+
+    /**
+     * 渲染内容输出
+     * @access public
+     * @param string $content 内容
+     * @param array  $vars    模板变量
+     * @return string
+     */
+    public function display(string $content, array $vars = []): string
+    {
+        return $this->getContent(function () use ($vars, $content) {
+            $this->engine()->display($content, array_merge($this->data, $vars));
+        });
+    }
+
+    /**
+     * 获取模板引擎渲染内容
+     * @param $callback
+     * @return string
+     * @throws \Exception
+     */
+    protected function getContent($callback): string
+    {
+        // 页面缓存
+        ob_start();
+        if (PHP_VERSION > 8.0) {
+            ob_implicit_flush(false);
+        } else {
+            ob_implicit_flush(0);
+        }
+
+        // 渲染输出
+        try {
+            $callback();
+        } catch (\Exception $e) {
+            ob_end_clean();
+            throw $e;
+        }
+
+        // 获取并清空缓存
+        $content = ob_get_clean();
+
+        if ($this->filter) {
+            $content = call_user_func_array($this->filter, [$content]);
+        }
+
+        return $content;
+    }
+
+    /**
+     * 模板变量赋值
+     * @access public
+     * @param string $name  变量名
+     * @param mixed  $value 变量值
+     */
+    public function __set($name, $value)
+    {
+        $this->data[$name] = $value;
+    }
+
+    /**
+     * 取得模板显示变量的值
+     * @access protected
+     * @param string $name 模板变量
+     * @return mixed
+     */
+    public function __get($name)
+    {
+        return $this->data[$name];
+    }
+
+    /**
+     * 检测模板变量是否设置
+     * @access public
+     * @param string $name 模板变量名
+     * @return bool
+     */
+    public function __isset($name)
+    {
+        return isset($this->data[$name]);
+    }
+
+    protected function resolveConfig(string $name)
+    {
+        $config = $this->app->config->get('view', []);
+        Arr::forget($config, 'type');
+        return $config;
+    }
+
+    /**
+     * 默认驱动
+     * @return string|null
+     */
+    public function getDefaultDriver()
+    {
+        return $this->app->config->get('view.type', 'php');
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/cache/Driver.php b/vendor/topthink/framework/src/think/cache/Driver.php
new file mode 100755
index 0000000..7dc0d30
--- /dev/null
+++ b/vendor/topthink/framework/src/think/cache/Driver.php
@@ -0,0 +1,359 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\cache;
+
+use Closure;
+use DateInterval;
+use DateTime;
+use DateTimeInterface;
+use Exception;
+use Psr\SimpleCache\CacheInterface;
+use think\Container;
+use think\contract\CacheHandlerInterface;
+use think\exception\InvalidArgumentException;
+use throwable;
+
+/**
+ * 缓存基础类
+ */
+abstract class Driver implements CacheInterface, CacheHandlerInterface
+{
+    /**
+     * 驱动句柄
+     * @var object
+     */
+    protected $handler = null;
+
+    /**
+     * 缓存读取次数
+     * @var integer
+     */
+    protected $readTimes = 0;
+
+    /**
+     * 缓存写入次数
+     * @var integer
+     */
+    protected $writeTimes = 0;
+
+    /**
+     * 缓存参数
+     * @var array
+     */
+    protected $options = [];
+
+    /**
+     * 缓存标签
+     * @var array
+     */
+    protected $tag = [];
+
+    /**
+     * 获取有效期
+     * @access protected
+     * @param integer|DateTimeInterface|DateInterval $expire 有效期
+     * @return int
+     */
+    protected function getExpireTime($expire): int
+    {
+        if ($expire instanceof DateTimeInterface) {
+            $expire = $expire->getTimestamp() - time();
+        } elseif ($expire instanceof DateInterval) {
+            $expire = DateTime::createFromFormat('U', (string) time())
+                ->add($expire)
+                ->format('U') - time();
+        }
+
+        return (int) $expire;
+    }
+
+    /**
+     * 获取实际的缓存标识
+     * @access public
+     * @param string $name 缓存名
+     * @return string
+     */
+    public function getCacheKey(string $name): string
+    {
+        return $this->options['prefix'] . $name;
+    }
+
+    /**
+     * 读取缓存并删除
+     * @access public
+     * @param string $name 缓存变量名
+     * @return mixed
+     */
+    public function pull(string $name)
+    {
+        $result = $this->get($name, false);
+
+        if ($result) {
+            $this->delete($name);
+            return $result;
+        }
+    }
+
+    /**
+     * 追加(数组)缓存
+     * @access public
+     * @param string $name  缓存变量名
+     * @param mixed  $value 存储数据
+     * @return void
+     */
+    public function push(string $name, $value): void
+    {
+        $item = $this->get($name, []);
+
+        if (!is_array($item)) {
+            throw new InvalidArgumentException('only array cache can be push');
+        }
+
+        $item[] = $value;
+
+        if (count($item) > 1000) {
+            array_shift($item);
+        }
+
+        $item = array_unique($item);
+
+        $this->set($name, $item);
+    }
+
+    /**
+     * 追加TagSet数据
+     * @access public
+     * @param string $name  缓存变量名
+     * @param mixed  $value 存储数据
+     * @return void
+     */
+    public function append(string $name, $value): void
+    {
+        $this->push($name, $value);
+    }
+
+    /**
+     * 如果不存在则写入缓存
+     * @access public
+     * @param string $name   缓存变量名
+     * @param mixed  $value  存储数据
+     * @param int    $expire 有效时间 0为永久
+     * @return mixed
+     */
+    public function remember(string $name, $value, $expire = null)
+    {
+        if ($this->has($name)) {
+            if (($hit = $this->get($name)) !== null) {
+                return $hit;
+            }
+        }
+
+        $time = time();
+
+        while ($time + 5 > time() && $this->has($name . '_lock')) {
+            // 存在锁定则等待
+            usleep(200000);
+        }
+
+        try {
+            // 锁定
+            $this->set($name . '_lock', true);
+
+            if ($value instanceof Closure) {
+                // 获取缓存数据
+                $value = Container::getInstance()->invokeFunction($value);
+            }
+
+            // 缓存数据
+            $this->set($name, $value, $expire);
+
+            // 解锁
+            $this->delete($name . '_lock');
+        } catch (Exception | throwable $e) {
+            $this->delete($name . '_lock');
+            throw $e;
+        }
+
+        return $value;
+    }
+
+    /**
+     * 缓存标签
+     * @access public
+     * @param string|array $name 标签名
+     * @return TagSet
+     */
+    public function tag($name): TagSet
+    {
+        $name = (array) $name;
+        $key  = implode('-', $name);
+
+        if (!isset($this->tag[$key])) {
+            $this->tag[$key] = new TagSet($name, $this);
+        }
+
+        return $this->tag[$key];
+    }
+
+    /**
+     * 获取标签包含的缓存标识
+     * @access public
+     * @param string $tag 标签标识
+     * @return array
+     */
+    public function getTagItems(string $tag): array
+    {
+        $name = $this->getTagKey($tag);
+        return $this->get($name, []);
+    }
+
+    /**
+     * 获取实际标签名
+     * @access public
+     * @param string $tag 标签名
+     * @return string
+     */
+    public function getTagKey(string $tag): string
+    {
+        return $this->options['tag_prefix'] . md5($tag);
+    }
+
+    /**
+     * 序列化数据
+     * @access protected
+     * @param mixed $data 缓存数据
+     * @return string
+     */
+    protected function serialize($data): string
+    {
+        if (is_numeric($data)) {
+            return (string) $data;
+        }
+
+        $serialize = $this->options['serialize'][0] ?? "serialize";
+
+        return $serialize($data);
+    }
+
+    /**
+     * 反序列化数据
+     * @access protected
+     * @param string $data 缓存数据
+     * @return mixed
+     */
+    protected function unserialize($data)
+    {
+        if (is_numeric($data)) {
+            return $data;
+        }
+
+        $unserialize = $this->options['serialize'][1] ?? "unserialize";
+
+        return $unserialize($data);
+    }
+
+    /**
+     * 返回句柄对象,可执行其它高级方法
+     *
+     * @access public
+     * @return object
+     */
+    public function handler()
+    {
+        return $this->handler;
+    }
+
+    /**
+     * 返回缓存读取次数
+     * @access public
+     * @return int
+     */
+    public function getReadTimes(): int
+    {
+        return $this->readTimes;
+    }
+
+    /**
+     * 返回缓存写入次数
+     * @access public
+     * @return int
+     */
+    public function getWriteTimes(): int
+    {
+        return $this->writeTimes;
+    }
+
+    /**
+     * 读取缓存
+     * @access public
+     * @param iterable $keys    缓存变量名
+     * @param mixed    $default 默认值
+     * @return iterable
+     * @throws InvalidArgumentException
+     */
+    public function getMultiple($keys, $default = null): iterable
+    {
+        $result = [];
+
+        foreach ($keys as $key) {
+            $result[$key] = $this->get($key, $default);
+        }
+
+        return $result;
+    }
+
+    /**
+     * 写入缓存
+     * @access public
+     * @param iterable               $values 缓存数据
+     * @param null|int|\DateInterval $ttl    有效时间 0为永久
+     * @return bool
+     */
+    public function setMultiple($values, $ttl = null): bool
+    {
+        foreach ($values as $key => $val) {
+            $result = $this->set($key, $val, $ttl);
+
+            if (false === $result) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * 删除缓存
+     * @access public
+     * @param iterable $keys 缓存变量名
+     * @return bool
+     * @throws InvalidArgumentException
+     */
+    public function deleteMultiple($keys): bool
+    {
+        foreach ($keys as $key) {
+            $result = $this->delete($key);
+
+            if (false === $result) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    public function __call($method, $args)
+    {
+        return call_user_func_array([$this->handler, $method], $args);
+    }
+}
diff --git a/vendor/topthink/framework/src/think/cache/TagSet.php b/vendor/topthink/framework/src/think/cache/TagSet.php
new file mode 100755
index 0000000..5ba2076
--- /dev/null
+++ b/vendor/topthink/framework/src/think/cache/TagSet.php
@@ -0,0 +1,132 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\cache;
+
+/**
+ * 标签集合
+ */
+class TagSet
+{
+    /**
+     * 标签的缓存Key
+     * @var array
+     */
+    protected $tag;
+
+    /**
+     * 缓存句柄
+     * @var Driver
+     */
+    protected $handler;
+
+    /**
+     * 架构函数
+     * @access public
+     * @param array  $tag   缓存标签
+     * @param Driver $cache 缓存对象
+     */
+    public function __construct(array $tag, Driver $cache)
+    {
+        $this->tag     = $tag;
+        $this->handler = $cache;
+    }
+
+    /**
+     * 写入缓存
+     * @access public
+     * @param string            $name   缓存变量名
+     * @param mixed             $value  存储数据
+     * @param integer|\DateTime $expire 有效时间(秒)
+     * @return bool
+     */
+    public function set(string $name, $value, $expire = null): bool
+    {
+        $this->handler->set($name, $value, $expire);
+
+        $this->append($name);
+
+        return true;
+    }
+
+    /**
+     * 追加缓存标识到标签
+     * @access public
+     * @param string $name 缓存变量名
+     * @return void
+     */
+    public function append(string $name): void
+    {
+        $name = $this->handler->getCacheKey($name);
+
+        foreach ($this->tag as $tag) {
+            $key = $this->handler->getTagKey($tag);
+            $this->handler->append($key, $name);
+        }
+    }
+
+    /**
+     * 写入缓存
+     * @access public
+     * @param iterable               $values 缓存数据
+     * @param null|int|\DateInterval $ttl    有效时间 0为永久
+     * @return bool
+     */
+    public function setMultiple($values, $ttl = null): bool
+    {
+        foreach ($values as $key => $val) {
+            $result = $this->set($key, $val, $ttl);
+
+            if (false === $result) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * 如果不存在则写入缓存
+     * @access public
+     * @param string $name   缓存变量名
+     * @param mixed  $value  存储数据
+     * @param int    $expire 有效时间 0为永久
+     * @return mixed
+     */
+    public function remember(string $name, $value, $expire = null)
+    {
+        $result = $this->handler->remember($name, $value, $expire);
+
+        $this->append($name);
+
+        return $result;
+    }
+
+    /**
+     * 清除缓存
+     * @access public
+     * @return bool
+     */
+    public function clear(): bool
+    {
+        // 指定标签清除
+        foreach ($this->tag as $tag) {
+            $names = $this->handler->getTagItems($tag);
+            $this->handler->clearTag($names);
+
+            $key = $this->handler->getTagKey($tag);
+            $this->handler->delete($key);
+        }
+
+        return true;
+    }
+}
diff --git a/vendor/topthink/framework/src/think/cache/driver/File.php b/vendor/topthink/framework/src/think/cache/driver/File.php
new file mode 100755
index 0000000..b36b069
--- /dev/null
+++ b/vendor/topthink/framework/src/think/cache/driver/File.php
@@ -0,0 +1,304 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\cache\driver;
+
+use FilesystemIterator;
+use think\App;
+use think\cache\Driver;
+
+/**
+ * 文件缓存类
+ */
+class File extends Driver
+{
+    /**
+     * 配置参数
+     * @var array
+     */
+    protected $options = [
+        'expire'        => 0,
+        'cache_subdir'  => true,
+        'prefix'        => '',
+        'path'          => '',
+        'hash_type'     => 'md5',
+        'data_compress' => false,
+        'tag_prefix'    => 'tag:',
+        'serialize'     => [],
+    ];
+
+    /**
+     * 架构函数
+     * @param App   $app
+     * @param array $options 参数
+     */
+    public function __construct(App $app, array $options = [])
+    {
+        if (!empty($options)) {
+            $this->options = array_merge($this->options, $options);
+        }
+
+        if (empty($this->options['path'])) {
+            $this->options['path'] = $app->getRuntimePath() . 'cache';
+        }
+
+        if (substr($this->options['path'], -1) != DIRECTORY_SEPARATOR) {
+            $this->options['path'] .= DIRECTORY_SEPARATOR;
+        }
+    }
+
+    /**
+     * 取得变量的存储文件名
+     * @access public
+     * @param string $name 缓存变量名
+     * @return string
+     */
+    public function getCacheKey(string $name): string
+    {
+        $name = hash($this->options['hash_type'], $name);
+
+        if ($this->options['cache_subdir']) {
+            // 使用子目录
+            $name = substr($name, 0, 2) . DIRECTORY_SEPARATOR . substr($name, 2);
+        }
+
+        if ($this->options['prefix']) {
+            $name = $this->options['prefix'] . DIRECTORY_SEPARATOR . $name;
+        }
+
+        return $this->options['path'] . $name . '.php';
+    }
+
+    /**
+     * 获取缓存数据
+     * @param string $name 缓存标识名
+     * @return array|null
+     */
+    protected function getRaw(string $name)
+    {
+        $filename = $this->getCacheKey($name);
+
+        if (!is_file($filename)) {
+            return;
+        }
+
+        $content = @file_get_contents($filename);
+
+        if (false !== $content) {
+            $expire = (int) substr($content, 8, 12);
+            if (0 != $expire && time() - $expire > filemtime($filename)) {
+                //缓存过期删除缓存文件
+                $this->unlink($filename);
+                return;
+            }
+
+            $content = substr($content, 32);
+
+            if ($this->options['data_compress'] && function_exists('gzcompress')) {
+                //启用数据压缩
+                $content = gzuncompress($content);
+            }
+
+            return is_string($content) ? ['content' => $content, 'expire' => $expire] : null;
+        }
+    }
+
+    /**
+     * 判断缓存是否存在
+     * @access public
+     * @param string $name 缓存变量名
+     * @return bool
+     */
+    public function has($name): bool
+    {
+        return $this->getRaw($name) !== null;
+    }
+
+    /**
+     * 读取缓存
+     * @access public
+     * @param string $name    缓存变量名
+     * @param mixed  $default 默认值
+     * @return mixed
+     */
+    public function get($name, $default = null)
+    {
+        $this->readTimes++;
+
+        $raw = $this->getRaw($name);
+
+        return is_null($raw) ? $default : $this->unserialize($raw['content']);
+    }
+
+    /**
+     * 写入缓存
+     * @access public
+     * @param string        $name   缓存变量名
+     * @param mixed         $value  存储数据
+     * @param int|\DateTime $expire 有效时间 0为永久
+     * @return bool
+     */
+    public function set($name, $value, $expire = null): bool
+    {
+        $this->writeTimes++;
+
+        if (is_null($expire)) {
+            $expire = $this->options['expire'];
+        }
+
+        $expire   = $this->getExpireTime($expire);
+        $filename = $this->getCacheKey($name);
+
+        $dir = dirname($filename);
+
+        if (!is_dir($dir)) {
+            try {
+                mkdir($dir, 0755, true);
+            } catch (\Exception $e) {
+                // 创建失败
+            }
+        }
+
+        $data = $this->serialize($value);
+
+        if ($this->options['data_compress'] && function_exists('gzcompress')) {
+            //数据压缩
+            $data = gzcompress($data, 3);
+        }
+
+        $data   = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;
+        $result = file_put_contents($filename, $data);
+
+        if ($result) {
+            clearstatcache();
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * 自增缓存(针对数值缓存)
+     * @access public
+     * @param string $name 缓存变量名
+     * @param int    $step 步长
+     * @return false|int
+     */
+    public function inc(string $name, int $step = 1)
+    {
+        if ($raw = $this->getRaw($name)) {
+            $value  = $this->unserialize($raw['content']) + $step;
+            $expire = $raw['expire'];
+        } else {
+            $value  = $step;
+            $expire = 0;
+        }
+
+        return $this->set($name, $value, $expire) ? $value : false;
+    }
+
+    /**
+     * 自减缓存(针对数值缓存)
+     * @access public
+     * @param string $name 缓存变量名
+     * @param int    $step 步长
+     * @return false|int
+     */
+    public function dec(string $name, int $step = 1)
+    {
+        return $this->inc($name, -$step);
+    }
+
+    /**
+     * 删除缓存
+     * @access public
+     * @param string $name 缓存变量名
+     * @return bool
+     */
+    public function delete($name): bool
+    {
+        $this->writeTimes++;
+
+        return $this->unlink($this->getCacheKey($name));
+    }
+
+    /**
+     * 清除缓存
+     * @access public
+     * @return bool
+     */
+    public function clear(): bool
+    {
+        $this->writeTimes++;
+
+        $dirname = $this->options['path'] . $this->options['prefix'];
+
+        $this->rmdir($dirname);
+
+        return true;
+    }
+
+    /**
+     * 删除缓存标签
+     * @access public
+     * @param array $keys 缓存标识列表
+     * @return void
+     */
+    public function clearTag(array $keys): void
+    {
+        foreach ($keys as $key) {
+            $this->unlink($key);
+        }
+    }
+
+    /**
+     * 判断文件是否存在后,删除
+     * @access private
+     * @param string $path
+     * @return bool
+     */
+    private function unlink(string $path): bool
+    {
+        try {
+            return is_file($path) && unlink($path);
+        } catch (\Exception $e) {
+            return false;
+        }
+    }
+
+    /**
+     * 删除文件夹
+     * @param $dirname
+     * @return bool
+     */
+    private function rmdir($dirname)
+    {
+        if (!is_dir($dirname)) {
+            return false;
+        }
+
+        $items = new FilesystemIterator($dirname);
+
+        foreach ($items as $item) {
+            if ($item->isDir() && !$item->isLink()) {
+                $this->rmdir($item->getPathname());
+            } else {
+                $this->unlink($item->getPathname());
+            }
+        }
+
+        @rmdir($dirname);
+
+        return true;
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/cache/driver/Memcache.php b/vendor/topthink/framework/src/think/cache/driver/Memcache.php
new file mode 100755
index 0000000..2fbbb9c
--- /dev/null
+++ b/vendor/topthink/framework/src/think/cache/driver/Memcache.php
@@ -0,0 +1,209 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\cache\driver;
+
+use think\cache\Driver;
+
+/**
+ * Memcache缓存类
+ */
+class Memcache extends Driver
+{
+    /**
+     * 配置参数
+     * @var array
+     */
+    protected $options = [
+        'host'       => '127.0.0.1',
+        'port'       => 11211,
+        'expire'     => 0,
+        'timeout'    => 0, // 超时时间(单位:毫秒)
+        'persistent' => true,
+        'prefix'     => '',
+        'tag_prefix' => 'tag:',
+        'serialize'  => [],
+    ];
+
+    /**
+     * 架构函数
+     * @access public
+     * @param array $options 缓存参数
+     * @throws \BadFunctionCallException
+     */
+    public function __construct(array $options = [])
+    {
+        if (!extension_loaded('memcache')) {
+            throw new \BadFunctionCallException('not support: memcache');
+        }
+
+        if (!empty($options)) {
+            $this->options = array_merge($this->options, $options);
+        }
+
+        $this->handler = new \Memcache;
+
+        // 支持集群
+        $hosts = (array) $this->options['host'];
+        $ports = (array) $this->options['port'];
+
+        if (empty($ports[0])) {
+            $ports[0] = 11211;
+        }
+
+        // 建立连接
+        foreach ($hosts as $i => $host) {
+            $port = $ports[$i] ?? $ports[0];
+            $this->options['timeout'] > 0 ?
+            $this->handler->addServer($host, (int) $port, $this->options['persistent'], 1, (int) $this->options['timeout']) :
+            $this->handler->addServer($host, (int) $port, $this->options['persistent'], 1);
+        }
+    }
+
+    /**
+     * 判断缓存
+     * @access public
+     * @param string $name 缓存变量名
+     * @return bool
+     */
+    public function has($name): bool
+    {
+        $key = $this->getCacheKey($name);
+
+        return false !== $this->handler->get($key);
+    }
+
+    /**
+     * 读取缓存
+     * @access public
+     * @param string $name    缓存变量名
+     * @param mixed  $default 默认值
+     * @return mixed
+     */
+    public function get($name, $default = null)
+    {
+        $this->readTimes++;
+
+        $result = $this->handler->get($this->getCacheKey($name));
+
+        return false !== $result ? $this->unserialize($result) : $default;
+    }
+
+    /**
+     * 写入缓存
+     * @access public
+     * @param string        $name   缓存变量名
+     * @param mixed         $value  存储数据
+     * @param int|\DateTime $expire 有效时间(秒)
+     * @return bool
+     */
+    public function set($name, $value, $expire = null): bool
+    {
+        $this->writeTimes++;
+
+        if (is_null($expire)) {
+            $expire = $this->options['expire'];
+        }
+
+        $key    = $this->getCacheKey($name);
+        $expire = $this->getExpireTime($expire);
+        $value  = $this->serialize($value);
+
+        if ($this->handler->set($key, $value, 0, $expire)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * 自增缓存(针对数值缓存)
+     * @access public
+     * @param string $name 缓存变量名
+     * @param int    $step 步长
+     * @return false|int
+     */
+    public function inc(string $name, int $step = 1)
+    {
+        $this->writeTimes++;
+
+        $key = $this->getCacheKey($name);
+
+        if ($this->handler->get($key)) {
+            return $this->handler->increment($key, $step);
+        }
+
+        return $this->handler->set($key, $step);
+    }
+
+    /**
+     * 自减缓存(针对数值缓存)
+     * @access public
+     * @param string $name 缓存变量名
+     * @param int    $step 步长
+     * @return false|int
+     */
+    public function dec(string $name, int $step = 1)
+    {
+        $this->writeTimes++;
+
+        $key   = $this->getCacheKey($name);
+        $value = $this->handler->get($key) - $step;
+        $res   = $this->handler->set($key, $value);
+
+        return !$res ? false : $value;
+    }
+
+    /**
+     * 删除缓存
+     * @access public
+     * @param string     $name 缓存变量名
+     * @param bool|false $ttl
+     * @return bool
+     */
+    public function delete($name, $ttl = false): bool
+    {
+        $this->writeTimes++;
+
+        $key = $this->getCacheKey($name);
+
+        return false === $ttl ?
+        $this->handler->delete($key) :
+        $this->handler->delete($key, $ttl);
+    }
+
+    /**
+     * 清除缓存
+     * @access public
+     * @return bool
+     */
+    public function clear(): bool
+    {
+        $this->writeTimes++;
+
+        return $this->handler->flush();
+    }
+
+    /**
+     * 删除缓存标签
+     * @access public
+     * @param array $keys 缓存标识列表
+     * @return void
+     */
+    public function clearTag(array $keys): void
+    {
+        foreach ($keys as $key) {
+            $this->handler->delete($key);
+        }
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/cache/driver/Memcached.php b/vendor/topthink/framework/src/think/cache/driver/Memcached.php
new file mode 100755
index 0000000..71edb05
--- /dev/null
+++ b/vendor/topthink/framework/src/think/cache/driver/Memcached.php
@@ -0,0 +1,221 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\cache\driver;
+
+use think\cache\Driver;
+
+/**
+ * Memcached缓存类
+ */
+class Memcached extends Driver
+{
+    /**
+     * 配置参数
+     * @var array
+     */
+    protected $options = [
+        'host'       => '127.0.0.1',
+        'port'       => 11211,
+        'expire'     => 0,
+        'timeout'    => 0, // 超时时间(单位:毫秒)
+        'prefix'     => '',
+        'username'   => '', //账号
+        'password'   => '', //密码
+        'option'     => [],
+        'tag_prefix' => 'tag:',
+        'serialize'  => [],
+    ];
+
+    /**
+     * 架构函数
+     * @access public
+     * @param array $options 缓存参数
+     */
+    public function __construct(array $options = [])
+    {
+        if (!extension_loaded('memcached')) {
+            throw new \BadFunctionCallException('not support: memcached');
+        }
+
+        if (!empty($options)) {
+            $this->options = array_merge($this->options, $options);
+        }
+
+        $this->handler = new \Memcached;
+
+        if (!empty($this->options['option'])) {
+            $this->handler->setOptions($this->options['option']);
+        }
+
+        // 设置连接超时时间(单位:毫秒)
+        if ($this->options['timeout'] > 0) {
+            $this->handler->setOption(\Memcached::OPT_CONNECT_TIMEOUT, $this->options['timeout']);
+        }
+
+        // 支持集群
+        $hosts = (array) $this->options['host'];
+        $ports = (array) $this->options['port'];
+        if (empty($ports[0])) {
+            $ports[0] = 11211;
+        }
+
+        // 建立连接
+        $servers = [];
+        foreach ($hosts as $i => $host) {
+            $servers[] = [$host, $ports[$i] ?? $ports[0], 1];
+        }
+
+        $this->handler->addServers($servers);
+
+        if ('' != $this->options['username']) {
+            $this->handler->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
+            $this->handler->setSaslAuthData($this->options['username'], $this->options['password']);
+        }
+    }
+
+    /**
+     * 判断缓存
+     * @access public
+     * @param string $name 缓存变量名
+     * @return bool
+     */
+    public function has($name): bool
+    {
+        $key = $this->getCacheKey($name);
+
+        return $this->handler->get($key) ? true : false;
+    }
+
+    /**
+     * 读取缓存
+     * @access public
+     * @param string $name    缓存变量名
+     * @param mixed  $default 默认值
+     * @return mixed
+     */
+    public function get($name, $default = null)
+    {
+        $this->readTimes++;
+
+        $result = $this->handler->get($this->getCacheKey($name));
+
+        return false !== $result ? $this->unserialize($result) : $default;
+    }
+
+    /**
+     * 写入缓存
+     * @access public
+     * @param string            $name   缓存变量名
+     * @param mixed             $value  存储数据
+     * @param integer|\DateTime $expire 有效时间(秒)
+     * @return bool
+     */
+    public function set($name, $value, $expire = null): bool
+    {
+        $this->writeTimes++;
+
+        if (is_null($expire)) {
+            $expire = $this->options['expire'];
+        }
+
+        $key    = $this->getCacheKey($name);
+        $expire = $this->getExpireTime($expire);
+        $value  = $this->serialize($value);
+
+        if ($this->handler->set($key, $value, $expire)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * 自增缓存(针对数值缓存)
+     * @access public
+     * @param string $name 缓存变量名
+     * @param int    $step 步长
+     * @return false|int
+     */
+    public function inc(string $name, int $step = 1)
+    {
+        $this->writeTimes++;
+
+        $key = $this->getCacheKey($name);
+
+        if ($this->handler->get($key)) {
+            return $this->handler->increment($key, $step);
+        }
+
+        return $this->handler->set($key, $step);
+    }
+
+    /**
+     * 自减缓存(针对数值缓存)
+     * @access public
+     * @param string $name 缓存变量名
+     * @param int    $step 步长
+     * @return false|int
+     */
+    public function dec(string $name, int $step = 1)
+    {
+        $this->writeTimes++;
+
+        $key   = $this->getCacheKey($name);
+        $value = $this->handler->get($key) - $step;
+        $res   = $this->handler->set($key, $value);
+
+        return !$res ? false : $value;
+    }
+
+    /**
+     * 删除缓存
+     * @access public
+     * @param string $name 缓存变量名
+     * @param bool|false $ttl
+     * @return bool
+     */
+    public function delete($name, $ttl = false): bool
+    {
+        $this->writeTimes++;
+
+        $key = $this->getCacheKey($name);
+
+        return false === $ttl ?
+        $this->handler->delete($key) :
+        $this->handler->delete($key, $ttl);
+    }
+
+    /**
+     * 清除缓存
+     * @access public
+     * @return bool
+     */
+    public function clear(): bool
+    {
+        $this->writeTimes++;
+
+        return $this->handler->flush();
+    }
+
+    /**
+     * 删除缓存标签
+     * @access public
+     * @param array $keys 缓存标识列表
+     * @return void
+     */
+    public function clearTag(array $keys): void
+    {
+        $this->handler->deleteMulti($keys);
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/cache/driver/Redis.php b/vendor/topthink/framework/src/think/cache/driver/Redis.php
new file mode 100755
index 0000000..791b27b
--- /dev/null
+++ b/vendor/topthink/framework/src/think/cache/driver/Redis.php
@@ -0,0 +1,249 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\cache\driver;
+
+use think\cache\Driver;
+
+/**
+ * Redis缓存驱动,适合单机部署、有前端代理实现高可用的场景,性能最好
+ * 有需要在业务层实现读写分离、或者使用RedisCluster的需求,请使用Redisd驱动
+ *
+ * 要求安装phpredis扩展:https://github.com/nicolasff/phpredis
+ * @author    尘缘 <130775@qq.com>
+ */
+class Redis extends Driver
+{
+    /** @var \Predis\Client|\Redis */
+    protected $handler;
+
+    /**
+     * 配置参数
+     * @var array
+     */
+    protected $options = [
+        'host'       => '127.0.0.1',
+        'port'       => 6379,
+        'password'   => '',
+        'select'     => 0,
+        'timeout'    => 0,
+        'expire'     => 0,
+        'persistent' => false,
+        'prefix'     => '',
+        'tag_prefix' => 'tag:',
+        'serialize'  => [],
+    ];
+
+    /**
+     * 架构函数
+     * @access public
+     * @param array $options 缓存参数
+     */
+    public function __construct(array $options = [])
+    {
+        if (!empty($options)) {
+            $this->options = array_merge($this->options, $options);
+        }
+
+        if (extension_loaded('redis')) {
+            $this->handler = new \Redis;
+
+            if ($this->options['persistent']) {
+                $this->handler->pconnect($this->options['host'], (int) $this->options['port'], (int) $this->options['timeout'], 'persistent_id_' . $this->options['select']);
+            } else {
+                $this->handler->connect($this->options['host'], (int) $this->options['port'], (int) $this->options['timeout']);
+            }
+
+            if ('' != $this->options['password']) {
+                $this->handler->auth($this->options['password']);
+            }
+        } elseif (class_exists('\Predis\Client')) {
+            $params = [];
+            foreach ($this->options as $key => $val) {
+                if (in_array($key, ['aggregate', 'cluster', 'connections', 'exceptions', 'prefix', 'profile', 'replication', 'parameters'])) {
+                    $params[$key] = $val;
+                    unset($this->options[$key]);
+                }
+            }
+
+            if ('' == $this->options['password']) {
+                unset($this->options['password']);
+            }
+
+            $this->handler = new \Predis\Client($this->options, $params);
+
+            $this->options['prefix'] = '';
+        } else {
+            throw new \BadFunctionCallException('not support: redis');
+        }
+
+        if (0 != $this->options['select']) {
+            $this->handler->select((int) $this->options['select']);
+        }
+    }
+
+    /**
+     * 判断缓存
+     * @access public
+     * @param string $name 缓存变量名
+     * @return bool
+     */
+    public function has($name): bool
+    {
+        return $this->handler->exists($this->getCacheKey($name)) ? true : false;
+    }
+
+    /**
+     * 读取缓存
+     * @access public
+     * @param string $name    缓存变量名
+     * @param mixed  $default 默认值
+     * @return mixed
+     */
+    public function get($name, $default = null)
+    {
+        $this->readTimes++;
+        $key   = $this->getCacheKey($name);
+        $value = $this->handler->get($key);
+
+        if (false === $value || is_null($value)) {
+            return $default;
+        }
+
+        return $this->unserialize($value);
+    }
+
+    /**
+     * 写入缓存
+     * @access public
+     * @param string            $name   缓存变量名
+     * @param mixed             $value  存储数据
+     * @param integer|\DateTime $expire 有效时间(秒)
+     * @return bool
+     */
+    public function set($name, $value, $expire = null): bool
+    {
+        $this->writeTimes++;
+
+        if (is_null($expire)) {
+            $expire = $this->options['expire'];
+        }
+
+        $key    = $this->getCacheKey($name);
+        $expire = $this->getExpireTime($expire);
+        $value  = $this->serialize($value);
+
+        if ($expire) {
+            $this->handler->setex($key, $expire, $value);
+        } else {
+            $this->handler->set($key, $value);
+        }
+
+        return true;
+    }
+
+    /**
+     * 自增缓存(针对数值缓存)
+     * @access public
+     * @param string $name 缓存变量名
+     * @param int    $step 步长
+     * @return false|int
+     */
+    public function inc(string $name, int $step = 1)
+    {
+        $this->writeTimes++;
+        $key = $this->getCacheKey($name);
+
+        return $this->handler->incrby($key, $step);
+    }
+
+    /**
+     * 自减缓存(针对数值缓存)
+     * @access public
+     * @param string $name 缓存变量名
+     * @param int    $step 步长
+     * @return false|int
+     */
+    public function dec(string $name, int $step = 1)
+    {
+        $this->writeTimes++;
+        $key = $this->getCacheKey($name);
+
+        return $this->handler->decrby($key, $step);
+    }
+
+    /**
+     * 删除缓存
+     * @access public
+     * @param string $name 缓存变量名
+     * @return bool
+     */
+    public function delete($name): bool
+    {
+        $this->writeTimes++;
+
+        $key    = $this->getCacheKey($name);
+        $result = $this->handler->del($key);
+        return $result > 0;
+    }
+
+    /**
+     * 清除缓存
+     * @access public
+     * @return bool
+     */
+    public function clear(): bool
+    {
+        $this->writeTimes++;
+        $this->handler->flushDB();
+        return true;
+    }
+
+    /**
+     * 删除缓存标签
+     * @access public
+     * @param array $keys 缓存标识列表
+     * @return void
+     */
+    public function clearTag(array $keys): void
+    {
+        // 指定标签清除
+        $this->handler->del($keys);
+    }
+
+    /**
+     * 追加TagSet数据
+     * @access public
+     * @param string $name  缓存标识
+     * @param mixed  $value 数据
+     * @return void
+     */
+    public function append(string $name, $value): void
+    {
+        $key = $this->getCacheKey($name);
+        $this->handler->sAdd($key, $value);
+    }
+
+    /**
+     * 获取标签包含的缓存标识
+     * @access public
+     * @param string $tag 缓存标签
+     * @return array
+     */
+    public function getTagItems(string $tag): array
+    {
+        $name = $this->getTagKey($tag);
+        $key  = $this->getCacheKey($name);
+        return $this->handler->sMembers($key);
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/cache/driver/Wincache.php b/vendor/topthink/framework/src/think/cache/driver/Wincache.php
new file mode 100755
index 0000000..8b3e8b8
--- /dev/null
+++ b/vendor/topthink/framework/src/think/cache/driver/Wincache.php
@@ -0,0 +1,175 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\cache\driver;
+
+use think\cache\Driver;
+
+/**
+ * Wincache缓存驱动
+ */
+class Wincache extends Driver
+{
+    /**
+     * 配置参数
+     * @var array
+     */
+    protected $options = [
+        'prefix'     => '',
+        'expire'     => 0,
+        'tag_prefix' => 'tag:',
+        'serialize'  => [],
+    ];
+
+    /**
+     * 架构函数
+     * @access public
+     * @param array $options 缓存参数
+     * @throws \BadFunctionCallException
+     */
+    public function __construct(array $options = [])
+    {
+        if (!function_exists('wincache_ucache_info')) {
+            throw new \BadFunctionCallException('not support: WinCache');
+        }
+
+        if (!empty($options)) {
+            $this->options = array_merge($this->options, $options);
+        }
+    }
+
+    /**
+     * 判断缓存
+     * @access public
+     * @param string $name 缓存变量名
+     * @return bool
+     */
+    public function has($name): bool
+    {
+        $this->readTimes++;
+
+        $key = $this->getCacheKey($name);
+
+        return wincache_ucache_exists($key);
+    }
+
+    /**
+     * 读取缓存
+     * @access public
+     * @param string $name    缓存变量名
+     * @param mixed  $default 默认值
+     * @return mixed
+     */
+    public function get($name, $default = null)
+    {
+        $this->readTimes++;
+
+        $key = $this->getCacheKey($name);
+
+        return wincache_ucache_exists($key) ? $this->unserialize(wincache_ucache_get($key)) : $default;
+    }
+
+    /**
+     * 写入缓存
+     * @access public
+     * @param string            $name   缓存变量名
+     * @param mixed             $value  存储数据
+     * @param integer|\DateTime $expire 有效时间(秒)
+     * @return bool
+     */
+    public function set($name, $value, $expire = null): bool
+    {
+        $this->writeTimes++;
+
+        if (is_null($expire)) {
+            $expire = $this->options['expire'];
+        }
+
+        $key    = $this->getCacheKey($name);
+        $expire = $this->getExpireTime($expire);
+        $value  = $this->serialize($value);
+
+        if (wincache_ucache_set($key, $value, $expire)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * 自增缓存(针对数值缓存)
+     * @access public
+     * @param string $name 缓存变量名
+     * @param int    $step 步长
+     * @return false|int
+     */
+    public function inc(string $name, int $step = 1)
+    {
+        $this->writeTimes++;
+
+        $key = $this->getCacheKey($name);
+
+        return wincache_ucache_inc($key, $step);
+    }
+
+    /**
+     * 自减缓存(针对数值缓存)
+     * @access public
+     * @param string $name 缓存变量名
+     * @param int    $step 步长
+     * @return false|int
+     */
+    public function dec(string $name, int $step = 1)
+    {
+        $this->writeTimes++;
+
+        $key = $this->getCacheKey($name);
+
+        return wincache_ucache_dec($key, $step);
+    }
+
+    /**
+     * 删除缓存
+     * @access public
+     * @param string $name 缓存变量名
+     * @return bool
+     */
+    public function delete($name): bool
+    {
+        $this->writeTimes++;
+
+        return wincache_ucache_delete($this->getCacheKey($name));
+    }
+
+    /**
+     * 清除缓存
+     * @access public
+     * @return bool
+     */
+    public function clear(): bool
+    {
+        $this->writeTimes++;
+        return wincache_ucache_clear();
+    }
+
+    /**
+     * 删除缓存标签
+     * @access public
+     * @param array $keys 缓存标识列表
+     * @return void
+     */
+    public function clearTag(array $keys): void
+    {
+        wincache_ucache_delete($keys);
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/console/Command.php b/vendor/topthink/framework/src/think/console/Command.php
new file mode 100755
index 0000000..bd3fb20
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/Command.php
@@ -0,0 +1,504 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\console;
+
+use Exception;
+use InvalidArgumentException;
+use LogicException;
+use think\App;
+use think\Console;
+use think\console\input\Argument;
+use think\console\input\Definition;
+use think\console\input\Option;
+
+abstract class Command
+{
+
+    /** @var  Console */
+    private $console;
+    private $name;
+    private $processTitle;
+    private $aliases = [];
+    private $definition;
+    private $help;
+    private $description;
+    private $ignoreValidationErrors          = false;
+    private $consoleDefinitionMerged         = false;
+    private $consoleDefinitionMergedWithArgs = false;
+    private $synopsis                        = [];
+    private $usages                          = [];
+
+    /** @var  Input */
+    protected $input;
+
+    /** @var  Output */
+    protected $output;
+
+    /** @var App */
+    protected $app;
+
+    /**
+     * 构造方法
+     * @throws LogicException
+     * @api
+     */
+    public function __construct()
+    {
+        $this->definition = new Definition();
+
+        $this->configure();
+
+        if (!$this->name) {
+            throw new LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_class($this)));
+        }
+    }
+
+    /**
+     * 忽略验证错误
+     */
+    public function ignoreValidationErrors(): void
+    {
+        $this->ignoreValidationErrors = true;
+    }
+
+    /**
+     * 设置控制台
+     * @param Console $console
+     */
+    public function setConsole(Console $console = null): void
+    {
+        $this->console = $console;
+    }
+
+    /**
+     * 获取控制台
+     * @return Console
+     * @api
+     */
+    public function getConsole(): Console
+    {
+        return $this->console;
+    }
+
+    /**
+     * 设置app
+     * @param App $app
+     */
+    public function setApp(App $app)
+    {
+        $this->app = $app;
+    }
+
+    /**
+     * 获取app
+     * @return App
+     */
+    public function getApp()
+    {
+        return $this->app;
+    }
+
+    /**
+     * 是否有效
+     * @return bool
+     */
+    public function isEnabled(): bool
+    {
+        return true;
+    }
+
+    /**
+     * 配置指令
+     */
+    protected function configure()
+    {
+    }
+
+    /**
+     * 执行指令
+     * @param Input  $input
+     * @param Output $output
+     * @return null|int
+     * @throws LogicException
+     * @see setCode()
+     */
+    protected function execute(Input $input, Output $output)
+    {
+        return $this->app->invoke([$this, 'handle']);
+    }
+
+    /**
+     * 用户验证
+     * @param Input  $input
+     * @param Output $output
+     */
+    protected function interact(Input $input, Output $output)
+    {
+    }
+
+    /**
+     * 初始化
+     * @param Input  $input  An InputInterface instance
+     * @param Output $output An OutputInterface instance
+     */
+    protected function initialize(Input $input, Output $output)
+    {
+    }
+
+    /**
+     * 执行
+     * @param Input  $input
+     * @param Output $output
+     * @return int
+     * @throws Exception
+     * @see setCode()
+     * @see execute()
+     */
+    public function run(Input $input, Output $output): int
+    {
+        $this->input  = $input;
+        $this->output = $output;
+
+        $this->getSynopsis(true);
+        $this->getSynopsis(false);
+
+        $this->mergeConsoleDefinition();
+
+        try {
+            $input->bind($this->definition);
+        } catch (Exception $e) {
+            if (!$this->ignoreValidationErrors) {
+                throw $e;
+            }
+        }
+
+        $this->initialize($input, $output);
+
+        if (null !== $this->processTitle) {
+            if (function_exists('cli_set_process_title')) {
+                if (false === @cli_set_process_title($this->processTitle)) {
+                    if ('Darwin' === PHP_OS) {
+                        $output->writeln('<comment>Running "cli_get_process_title" as an unprivileged user is not supported on MacOS.</comment>');
+                    } else {
+                        $error = error_get_last();
+                        trigger_error($error['message'], E_USER_WARNING);
+                    }
+                }
+            } elseif (function_exists('setproctitle')) {
+                setproctitle($this->processTitle);
+            } elseif (Output::VERBOSITY_VERY_VERBOSE === $output->getVerbosity()) {
+                $output->writeln('<comment>Install the proctitle PECL to be able to change the process title.</comment>');
+            }
+        }
+
+        if ($input->isInteractive()) {
+            $this->interact($input, $output);
+        }
+
+        $input->validate();
+
+        $statusCode = $this->execute($input, $output);
+
+        return is_numeric($statusCode) ? (int) $statusCode : 0;
+    }
+
+    /**
+     * 合并参数定义
+     * @param bool $mergeArgs
+     */
+    public function mergeConsoleDefinition(bool $mergeArgs = true)
+    {
+        if (null === $this->console
+            || (true === $this->consoleDefinitionMerged
+                && ($this->consoleDefinitionMergedWithArgs || !$mergeArgs))
+        ) {
+            return;
+        }
+
+        if ($mergeArgs) {
+            $currentArguments = $this->definition->getArguments();
+            $this->definition->setArguments($this->console->getDefinition()->getArguments());
+            $this->definition->addArguments($currentArguments);
+        }
+
+        $this->definition->addOptions($this->console->getDefinition()->getOptions());
+
+        $this->consoleDefinitionMerged = true;
+        if ($mergeArgs) {
+            $this->consoleDefinitionMergedWithArgs = true;
+        }
+    }
+
+    /**
+     * 设置参数定义
+     * @param array|Definition $definition
+     * @return Command
+     * @api
+     */
+    public function setDefinition($definition)
+    {
+        if ($definition instanceof Definition) {
+            $this->definition = $definition;
+        } else {
+            $this->definition->setDefinition($definition);
+        }
+
+        $this->consoleDefinitionMerged = false;
+
+        return $this;
+    }
+
+    /**
+     * 获取参数定义
+     * @return Definition
+     * @api
+     */
+    public function getDefinition(): Definition
+    {
+        return $this->definition;
+    }
+
+    /**
+     * 获取当前指令的参数定义
+     * @return Definition
+     */
+    public function getNativeDefinition(): Definition
+    {
+        return $this->getDefinition();
+    }
+
+    /**
+     * 添加参数
+     * @param string $name        名称
+     * @param int    $mode        类型
+     * @param string $description 描述
+     * @param mixed  $default     默认值
+     * @return Command
+     */
+    public function addArgument(string $name, int $mode = null, string $description = '', $default = null)
+    {
+        $this->definition->addArgument(new Argument($name, $mode, $description, $default));
+
+        return $this;
+    }
+
+    /**
+     * 添加选项
+     * @param string $name        选项名称
+     * @param string $shortcut    别名
+     * @param int    $mode        类型
+     * @param string $description 描述
+     * @param mixed  $default     默认值
+     * @return Command
+     */
+    public function addOption(string $name, string $shortcut = null, int $mode = null, string $description = '', $default = null)
+    {
+        $this->definition->addOption(new Option($name, $shortcut, $mode, $description, $default));
+
+        return $this;
+    }
+
+    /**
+     * 设置指令名称
+     * @param string $name
+     * @return Command
+     * @throws InvalidArgumentException
+     */
+    public function setName(string $name)
+    {
+        $this->validateName($name);
+
+        $this->name = $name;
+
+        return $this;
+    }
+
+    /**
+     * 设置进程名称
+     *
+     * PHP 5.5+ or the proctitle PECL library is required
+     *
+     * @param string $title The process title
+     *
+     * @return $this
+     */
+    public function setProcessTitle($title)
+    {
+        $this->processTitle = $title;
+
+        return $this;
+    }
+
+    /**
+     * 获取指令名称
+     * @return string
+     */
+    public function getName(): string
+    {
+        return $this->name ?: '';
+    }
+
+    /**
+     * 设置描述
+     * @param string $description
+     * @return Command
+     */
+    public function setDescription(string $description)
+    {
+        $this->description = $description;
+
+        return $this;
+    }
+
+    /**
+     *  获取描述
+     * @return string
+     */
+    public function getDescription(): string
+    {
+        return $this->description ?: '';
+    }
+
+    /**
+     * 设置帮助信息
+     * @param string $help
+     * @return Command
+     */
+    public function setHelp(string $help)
+    {
+        $this->help = $help;
+
+        return $this;
+    }
+
+    /**
+     * 获取帮助信息
+     * @return string
+     */
+    public function getHelp(): string
+    {
+        return $this->help ?: '';
+    }
+
+    /**
+     * 描述信息
+     * @return string
+     */
+    public function getProcessedHelp(): string
+    {
+        $name = $this->name;
+
+        $placeholders = [
+            '%command.name%',
+            '%command.full_name%',
+        ];
+        $replacements = [
+            $name,
+            $_SERVER['PHP_SELF'] . ' ' . $name,
+        ];
+
+        return str_replace($placeholders, $replacements, $this->getHelp());
+    }
+
+    /**
+     * 设置别名
+     * @param string[] $aliases
+     * @return Command
+     * @throws InvalidArgumentException
+     */
+    public function setAliases(iterable $aliases)
+    {
+        foreach ($aliases as $alias) {
+            $this->validateName($alias);
+        }
+
+        $this->aliases = $aliases;
+
+        return $this;
+    }
+
+    /**
+     * 获取别名
+     * @return array
+     */
+    public function getAliases(): array
+    {
+        return $this->aliases;
+    }
+
+    /**
+     * 获取简介
+     * @param bool $short 是否简单的
+     * @return string
+     */
+    public function getSynopsis(bool $short = false): string
+    {
+        $key = $short ? 'short' : 'long';
+
+        if (!isset($this->synopsis[$key])) {
+            $this->synopsis[$key] = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis($short)));
+        }
+
+        return $this->synopsis[$key];
+    }
+
+    /**
+     * 添加用法介绍
+     * @param string $usage
+     * @return $this
+     */
+    public function addUsage(string $usage)
+    {
+        if (0 !== strpos($usage, $this->name)) {
+            $usage = sprintf('%s %s', $this->name, $usage);
+        }
+
+        $this->usages[] = $usage;
+
+        return $this;
+    }
+
+    /**
+     * 获取用法介绍
+     * @return array
+     */
+    public function getUsages(): array
+    {
+        return $this->usages;
+    }
+
+    /**
+     * 验证指令名称
+     * @param string $name
+     * @throws InvalidArgumentException
+     */
+    private function validateName(string $name)
+    {
+        if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) {
+            throw new InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name));
+        }
+    }
+
+    /**
+     * 输出表格
+     * @param Table $table
+     * @return string
+     */
+    protected function table(Table $table): string
+    {
+        $content = $table->render();
+        $this->output->writeln($content);
+        return $content;
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/console/Input.php b/vendor/topthink/framework/src/think/console/Input.php
new file mode 100755
index 0000000..9ae9077
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/Input.php
@@ -0,0 +1,465 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\console;
+
+use think\console\input\Argument;
+use think\console\input\Definition;
+use think\console\input\Option;
+
+class Input
+{
+
+    /**
+     * @var Definition
+     */
+    protected $definition;
+
+    /**
+     * @var Option[]
+     */
+    protected $options = [];
+
+    /**
+     * @var Argument[]
+     */
+    protected $arguments = [];
+
+    protected $interactive = true;
+
+    private $tokens;
+    private $parsed;
+
+    public function __construct($argv = null)
+    {
+        if (null === $argv) {
+            $argv = $_SERVER['argv'];
+            // 去除命令名
+            array_shift($argv);
+        }
+
+        $this->tokens = $argv;
+
+        $this->definition = new Definition();
+    }
+
+    protected function setTokens(array $tokens)
+    {
+        $this->tokens = $tokens;
+    }
+
+    /**
+     * 绑定实例
+     * @param Definition $definition A InputDefinition instance
+     */
+    public function bind(Definition $definition): void
+    {
+        $this->arguments  = [];
+        $this->options    = [];
+        $this->definition = $definition;
+
+        $this->parse();
+    }
+
+    /**
+     * 解析参数
+     */
+    protected function parse(): void
+    {
+        $parseOptions = true;
+        $this->parsed = $this->tokens;
+        while (null !== $token = array_shift($this->parsed)) {
+            if ($parseOptions && '' == $token) {
+                $this->parseArgument($token);
+            } elseif ($parseOptions && '--' == $token) {
+                $parseOptions = false;
+            } elseif ($parseOptions && 0 === strpos($token, '--')) {
+                $this->parseLongOption($token);
+            } elseif ($parseOptions && '-' === $token[0] && '-' !== $token) {
+                $this->parseShortOption($token);
+            } else {
+                $this->parseArgument($token);
+            }
+        }
+    }
+
+    /**
+     * 解析短选项
+     * @param string $token 当前的指令.
+     */
+    private function parseShortOption(string $token): void
+    {
+        $name = substr($token, 1);
+
+        if (strlen($name) > 1) {
+            if ($this->definition->hasShortcut($name[0])
+                && $this->definition->getOptionForShortcut($name[0])->acceptValue()
+            ) {
+                $this->addShortOption($name[0], substr($name, 1));
+            } else {
+                $this->parseShortOptionSet($name);
+            }
+        } else {
+            $this->addShortOption($name, null);
+        }
+    }
+
+    /**
+     * 解析短选项
+     * @param string $name 当前指令
+     * @throws \RuntimeException
+     */
+    private function parseShortOptionSet(string $name): void
+    {
+        $len = strlen($name);
+        for ($i = 0; $i < $len; ++$i) {
+            if (!$this->definition->hasShortcut($name[$i])) {
+                throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i]));
+            }
+
+            $option = $this->definition->getOptionForShortcut($name[$i]);
+            if ($option->acceptValue()) {
+                $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1));
+
+                break;
+            } else {
+                $this->addLongOption($option->getName(), null);
+            }
+        }
+    }
+
+    /**
+     * 解析完整选项
+     * @param string $token 当前指令
+     */
+    private function parseLongOption(string $token): void
+    {
+        $name = substr($token, 2);
+
+        if (false !== $pos = strpos($name, '=')) {
+            $this->addLongOption(substr($name, 0, $pos), substr($name, $pos + 1));
+        } else {
+            $this->addLongOption($name, null);
+        }
+    }
+
+    /**
+     * 解析参数
+     * @param string $token 当前指令
+     * @throws \RuntimeException
+     */
+    private function parseArgument(string $token): void
+    {
+        $c = count($this->arguments);
+
+        if ($this->definition->hasArgument($c)) {
+            $arg = $this->definition->getArgument($c);
+
+            $this->arguments[$arg->getName()] = $arg->isArray() ? [$token] : $token;
+
+        } elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) {
+            $arg = $this->definition->getArgument($c - 1);
+
+            $this->arguments[$arg->getName()][] = $token;
+        } else {
+            throw new \RuntimeException('Too many arguments.');
+        }
+    }
+
+    /**
+     * 添加一个短选项的值
+     * @param string $shortcut 短名称
+     * @param mixed  $value    值
+     * @throws \RuntimeException
+     */
+    private function addShortOption(string $shortcut, $value): void
+    {
+        if (!$this->definition->hasShortcut($shortcut)) {
+            throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut));
+        }
+
+        $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
+    }
+
+    /**
+     * 添加一个完整选项的值
+     * @param string $name  选项名
+     * @param mixed  $value 值
+     * @throws \RuntimeException
+     */
+    private function addLongOption(string $name, $value): void
+    {
+        if (!$this->definition->hasOption($name)) {
+            throw new \RuntimeException(sprintf('The "--%s" option does not exist.', $name));
+        }
+
+        $option = $this->definition->getOption($name);
+
+        if (false === $value) {
+            $value = null;
+        }
+
+        if (null !== $value && !$option->acceptValue()) {
+            throw new \RuntimeException(sprintf('The "--%s" option does not accept a value.', $name, $value));
+        }
+
+        if (null === $value && $option->acceptValue() && count($this->parsed)) {
+            $next = array_shift($this->parsed);
+            if (isset($next[0]) && '-' !== $next[0]) {
+                $value = $next;
+            } elseif (empty($next)) {
+                $value = '';
+            } else {
+                array_unshift($this->parsed, $next);
+            }
+        }
+
+        if (null === $value) {
+            if ($option->isValueRequired()) {
+                throw new \RuntimeException(sprintf('The "--%s" option requires a value.', $name));
+            }
+
+            if (!$option->isArray()) {
+                $value = $option->isValueOptional() ? $option->getDefault() : true;
+            }
+        }
+
+        if ($option->isArray()) {
+            $this->options[$name][] = $value;
+        } else {
+            $this->options[$name] = $value;
+        }
+    }
+
+    /**
+     * 获取第一个参数
+     * @return string|null
+     */
+    public function getFirstArgument()
+    {
+        foreach ($this->tokens as $token) {
+            if ($token && '-' === $token[0]) {
+                continue;
+            }
+
+            return $token;
+        }
+        return;
+    }
+
+    /**
+     * 检查原始参数是否包含某个值
+     * @param string|array $values 需要检查的值
+     * @return bool
+     */
+    public function hasParameterOption($values): bool
+    {
+        $values = (array) $values;
+
+        foreach ($this->tokens as $token) {
+            foreach ($values as $value) {
+                if ($token === $value || 0 === strpos($token, $value . '=')) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * 获取原始选项的值
+     * @param string|array $values  需要检查的值
+     * @param mixed        $default 默认值
+     * @return mixed The option value
+     */
+    public function getParameterOption($values, $default = false)
+    {
+        $values = (array) $values;
+        $tokens = $this->tokens;
+
+        while (0 < count($tokens)) {
+            $token = array_shift($tokens);
+
+            foreach ($values as $value) {
+                if ($token === $value || 0 === strpos($token, $value . '=')) {
+                    if (false !== $pos = strpos($token, '=')) {
+                        return substr($token, $pos + 1);
+                    }
+
+                    return array_shift($tokens);
+                }
+            }
+        }
+
+        return $default;
+    }
+
+    /**
+     * 验证输入
+     * @throws \RuntimeException
+     */
+    public function validate()
+    {
+        if (count($this->arguments) < $this->definition->getArgumentRequiredCount()) {
+            throw new \RuntimeException('Not enough arguments.');
+        }
+    }
+
+    /**
+     * 检查输入是否是交互的
+     * @return bool
+     */
+    public function isInteractive(): bool
+    {
+        return $this->interactive;
+    }
+
+    /**
+     * 设置输入的交互
+     * @param bool
+     */
+    public function setInteractive(bool $interactive): void
+    {
+        $this->interactive = $interactive;
+    }
+
+    /**
+     * 获取所有的参数
+     * @return Argument[]
+     */
+    public function getArguments(): array
+    {
+        return array_merge($this->definition->getArgumentDefaults(), $this->arguments);
+    }
+
+    /**
+     * 根据名称获取参数
+     * @param string $name 参数名
+     * @return mixed
+     * @throws \InvalidArgumentException
+     */
+    public function getArgument(string $name)
+    {
+        if (!$this->definition->hasArgument($name)) {
+            throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
+        }
+
+        return $this->arguments[$name] ?? $this->definition->getArgument($name)
+            ->getDefault();
+    }
+
+    /**
+     * 设置参数的值
+     * @param string $name  参数名
+     * @param string $value 值
+     * @throws \InvalidArgumentException
+     */
+    public function setArgument(string $name, $value)
+    {
+        if (!$this->definition->hasArgument($name)) {
+            throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
+        }
+
+        $this->arguments[$name] = $value;
+    }
+
+    /**
+     * 检查是否存在某个参数
+     * @param string|int $name 参数名或位置
+     * @return bool
+     */
+    public function hasArgument($name): bool
+    {
+        return $this->definition->hasArgument($name);
+    }
+
+    /**
+     * 获取所有的选项
+     * @return Option[]
+     */
+    public function getOptions(): array
+    {
+        return array_merge($this->definition->getOptionDefaults(), $this->options);
+    }
+
+    /**
+     * 获取选项值
+     * @param string $name 选项名称
+     * @return mixed
+     * @throws \InvalidArgumentException
+     */
+    public function getOption(string $name)
+    {
+        if (!$this->definition->hasOption($name)) {
+            throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
+        }
+
+        return $this->options[$name] ?? $this->definition->getOption($name)->getDefault();
+    }
+
+    /**
+     * 设置选项值
+     * @param string      $name  选项名
+     * @param string|bool $value 值
+     * @throws \InvalidArgumentException
+     */
+    public function setOption(string $name, $value): void
+    {
+        if (!$this->definition->hasOption($name)) {
+            throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
+        }
+
+        $this->options[$name] = $value;
+    }
+
+    /**
+     * 是否有某个选项
+     * @param string $name 选项名
+     * @return bool
+     */
+    public function hasOption(string $name): bool
+    {
+        return $this->definition->hasOption($name) && isset($this->options[$name]);
+    }
+
+    /**
+     * 转义指令
+     * @param string $token
+     * @return string
+     */
+    public function escapeToken(string $token): string
+    {
+        return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token);
+    }
+
+    /**
+     * 返回传递给命令的参数的字符串
+     * @return string
+     */
+    public function __toString()
+    {
+        $tokens = array_map(function ($token) {
+            if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) {
+                return $match[1] . $this->escapeToken($match[2]);
+            }
+
+            if ($token && '-' !== $token[0]) {
+                return $this->escapeToken($token);
+            }
+
+            return $token;
+        }, $this->tokens);
+
+        return implode(' ', $tokens);
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/LICENSE b/vendor/topthink/framework/src/think/console/LICENSE
new file mode 100755
index 0000000..0abe056
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2004-2016 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.
\ No newline at end of file
diff --git a/vendor/topthink/framework/src/think/console/Output.php b/vendor/topthink/framework/src/think/console/Output.php
new file mode 100755
index 0000000..294c4b8
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/Output.php
@@ -0,0 +1,231 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2020 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\console;
+
+use Exception;
+use think\console\output\Ask;
+use think\console\output\Descriptor;
+use think\console\output\driver\Buffer;
+use think\console\output\driver\Console;
+use think\console\output\driver\Nothing;
+use think\console\output\Question;
+use think\console\output\question\Choice;
+use think\console\output\question\Confirmation;
+use Throwable;
+
+/**
+ * Class Output
+ * @package think\console
+ *
+ * @see     \think\console\output\driver\Console::setDecorated
+ * @method void setDecorated($decorated)
+ *
+ * @see     \think\console\output\driver\Buffer::fetch
+ * @method string fetch()
+ *
+ * @method void info($message)
+ * @method void error($message)
+ * @method void comment($message)
+ * @method void warning($message)
+ * @method void highlight($message)
+ * @method void question($message)
+ */
+class Output
+{
+    // 不显示信息(静默)
+    const VERBOSITY_QUIET        = 0;
+    // 正常信息
+    const VERBOSITY_NORMAL       = 1;
+    // 详细信息
+    const VERBOSITY_VERBOSE      = 2;
+    // 非常详细的信息
+    const VERBOSITY_VERY_VERBOSE = 3;
+    // 调试信息
+    const VERBOSITY_DEBUG        = 4;
+
+    const OUTPUT_NORMAL = 0;
+    const OUTPUT_RAW    = 1;
+    const OUTPUT_PLAIN  = 2;
+
+    // 输出信息级别
+    private $verbosity = self::VERBOSITY_NORMAL;
+
+    /** @var Buffer|Console|Nothing */
+    private $handle = null;
+
+    protected $styles = [
+        'info',
+        'error',
+        'comment',
+        'question',
+        'highlight',
+        'warning',
+    ];
+
+    public function __construct($driver = 'console')
+    {
+        $class = '\\think\\console\\output\\driver\\' . ucwords($driver);
+
+        $this->handle = new $class($this);
+    }
+
+    public function ask(Input $input, $question, $default = null, $validator = null)
+    {
+        $question = new Question($question, $default);
+        $question->setValidator($validator);
+
+        return $this->askQuestion($input, $question);
+    }
+
+    public function askHidden(Input $input, $question, $validator = null)
+    {
+        $question = new Question($question);
+
+        $question->setHidden(true);
+        $question->setValidator($validator);
+
+        return $this->askQuestion($input, $question);
+    }
+
+    public function confirm(Input $input, $question, $default = true)
+    {
+        return $this->askQuestion($input, new Confirmation($question, $default));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function choice(Input $input, $question, array $choices, $default = null)
+    {
+        if (null !== $default) {
+            $values  = array_flip($choices);
+            $default = $values[$default];
+        }
+
+        return $this->askQuestion($input, new Choice($question, $choices, $default));
+    }
+
+    protected function askQuestion(Input $input, Question $question)
+    {
+        $ask    = new Ask($input, $this, $question);
+        $answer = $ask->run();
+
+        if ($input->isInteractive()) {
+            $this->newLine();
+        }
+
+        return $answer;
+    }
+
+    protected function block(string $style, string $message): void
+    {
+        $this->writeln("<{$style}>{$message}</$style>");
+    }
+
+    /**
+     * 输出空行
+     * @param int $count
+     */
+    public function newLine(int $count = 1): void
+    {
+        $this->write(str_repeat(PHP_EOL, $count));
+    }
+
+    /**
+     * 输出信息并换行
+     * @param string $messages
+     * @param int    $type
+     */
+    public function writeln(string $messages, int $type = 0): void
+    {
+        $this->write($messages, true, $type);
+    }
+
+    /**
+     * 输出信息
+     * @param string $messages
+     * @param bool   $newline
+     * @param int    $type
+     */
+    public function write(string $messages, bool $newline = false, int $type = 0): void
+    {
+        $this->handle->write($messages, $newline, $type);
+    }
+
+    public function renderException(Throwable $e): void
+    {
+        $this->handle->renderException($e);
+    }
+
+    /**
+     * 设置输出信息级别
+     * @param int $level 输出信息级别
+     */
+    public function setVerbosity(int $level)
+    {
+        $this->verbosity = $level;
+    }
+
+    /**
+     * 获取输出信息级别
+     * @return int
+     */
+    public function getVerbosity(): int
+    {
+        return $this->verbosity;
+    }
+
+    public function isQuiet(): bool
+    {
+        return self::VERBOSITY_QUIET === $this->verbosity;
+    }
+
+    public function isVerbose(): bool
+    {
+        return self::VERBOSITY_VERBOSE <= $this->verbosity;
+    }
+
+    public function isVeryVerbose(): bool
+    {
+        return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity;
+    }
+
+    public function isDebug(): bool
+    {
+        return self::VERBOSITY_DEBUG <= $this->verbosity;
+    }
+
+    public function describe($object, array $options = []): void
+    {
+        $descriptor = new Descriptor();
+        $options    = array_merge([
+            'raw_text' => false,
+        ], $options);
+
+        $descriptor->describe($this, $object, $options);
+    }
+
+    public function __call($method, $args)
+    {
+        if (in_array($method, $this->styles)) {
+            array_unshift($args, $method);
+            return call_user_func_array([$this, 'block'], $args);
+        }
+
+        if ($this->handle && method_exists($this->handle, $method)) {
+            return call_user_func_array([$this->handle, $method], $args);
+        } else {
+            throw new Exception('method not exists:' . __CLASS__ . '->' . $method);
+        }
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/Table.php b/vendor/topthink/framework/src/think/console/Table.php
new file mode 100755
index 0000000..5a861d7
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/Table.php
@@ -0,0 +1,300 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\console;
+
+class Table
+{
+    const ALIGN_LEFT   = 1;
+    const ALIGN_RIGHT  = 0;
+    const ALIGN_CENTER = 2;
+
+    /**
+     * 头信息数据
+     * @var array
+     */
+    protected $header = [];
+
+    /**
+     * 头部对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER
+     * @var int
+     */
+    protected $headerAlign = 1;
+
+    /**
+     * 表格数据(二维数组)
+     * @var array
+     */
+    protected $rows = [];
+
+    /**
+     * 单元格对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER
+     * @var int
+     */
+    protected $cellAlign = 1;
+
+    /**
+     * 单元格宽度信息
+     * @var array
+     */
+    protected $colWidth = [];
+
+    /**
+     * 表格输出样式
+     * @var string
+     */
+    protected $style = 'default';
+
+    /**
+     * 表格样式定义
+     * @var array
+     */
+    protected $format = [
+        'compact'    => [],
+        'default'    => [
+            'top'          => ['+', '-', '+', '+'],
+            'cell'         => ['|', ' ', '|', '|'],
+            'middle'       => ['+', '-', '+', '+'],
+            'bottom'       => ['+', '-', '+', '+'],
+            'cross-top'    => ['+', '-', '-', '+'],
+            'cross-bottom' => ['+', '-', '-', '+'],
+        ],
+        'markdown'   => [
+            'top'          => [' ', ' ', ' ', ' '],
+            'cell'         => ['|', ' ', '|', '|'],
+            'middle'       => ['|', '-', '|', '|'],
+            'bottom'       => [' ', ' ', ' ', ' '],
+            'cross-top'    => ['|', ' ', ' ', '|'],
+            'cross-bottom' => ['|', ' ', ' ', '|'],
+        ],
+        'borderless' => [
+            'top'          => ['=', '=', ' ', '='],
+            'cell'         => [' ', ' ', ' ', ' '],
+            'middle'       => ['=', '=', ' ', '='],
+            'bottom'       => ['=', '=', ' ', '='],
+            'cross-top'    => ['=', '=', ' ', '='],
+            'cross-bottom' => ['=', '=', ' ', '='],
+        ],
+        'box'        => [
+            'top'          => ['┌', '─', '┬', '┐'],
+            'cell'         => ['│', ' ', '│', '│'],
+            'middle'       => ['├', '─', '┼', '┤'],
+            'bottom'       => ['└', '─', '┴', '┘'],
+            'cross-top'    => ['├', '─', '┴', '┤'],
+            'cross-bottom' => ['├', '─', '┬', '┤'],
+        ],
+        'box-double' => [
+            'top'          => ['╔', '═', '╤', '╗'],
+            'cell'         => ['║', ' ', '│', '║'],
+            'middle'       => ['╠', '─', '╪', '╣'],
+            'bottom'       => ['╚', '═', '╧', '╝'],
+            'cross-top'    => ['╠', '═', '╧', '╣'],
+            'cross-bottom' => ['╠', '═', '╤', '╣'],
+        ],
+    ];
+
+    /**
+     * 设置表格头信息 以及对齐方式
+     * @access public
+     * @param array $header     要输出的Header信息
+     * @param int   $align      对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER
+     * @return void
+     */
+    public function setHeader(array $header, int $align = 1): void
+    {
+        $this->header      = $header;
+        $this->headerAlign = $align;
+
+        $this->checkColWidth($header);
+    }
+
+    /**
+     * 设置输出表格数据 及对齐方式
+     * @access public
+     * @param array $rows       要输出的表格数据(二维数组)
+     * @param int   $align      对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER
+     * @return void
+     */
+    public function setRows(array $rows, int $align = 1): void
+    {
+        $this->rows      = $rows;
+        $this->cellAlign = $align;
+
+        foreach ($rows as $row) {
+            $this->checkColWidth($row);
+        }
+    }
+
+    /**
+     * 设置全局单元格对齐方式
+     * @param int $align 对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER
+     * @return $this
+     */
+    public function setCellAlign(int $align = 1)
+    {
+        $this->cellAlign = $align;
+        return $this;
+    }
+
+    /**
+     * 检查列数据的显示宽度
+     * @access public
+     * @param  mixed $row       行数据
+     * @return void
+     */
+    protected function checkColWidth($row): void
+    {
+        if (is_array($row)) {
+            foreach ($row as $key => $cell) {
+                $width = mb_strwidth((string) $cell);
+                if (!isset($this->colWidth[$key]) || $width > $this->colWidth[$key]) {
+                    $this->colWidth[$key] = $width;
+                }
+            }
+        }
+    }
+
+    /**
+     * 增加一行表格数据
+     * @access public
+     * @param  mixed $row       行数据
+     * @param  bool  $first     是否在开头插入
+     * @return void
+     */
+    public function addRow($row, bool $first = false): void
+    {
+        if ($first) {
+            array_unshift($this->rows, $row);
+        } else {
+            $this->rows[] = $row;
+        }
+
+        $this->checkColWidth($row);
+    }
+
+    /**
+     * 设置输出表格的样式
+     * @access public
+     * @param  string $style       样式名
+     * @return void
+     */
+    public function setStyle(string $style): void
+    {
+        $this->style = isset($this->format[$style]) ? $style : 'default';
+    }
+
+    /**
+     * 输出分隔行
+     * @access public
+     * @param  string $pos       位置
+     * @return string
+     */
+    protected function renderSeparator(string $pos): string
+    {
+        $style = $this->getStyle($pos);
+        $array = [];
+
+        foreach ($this->colWidth as $width) {
+            $array[] = str_repeat($style[1], $width + 2);
+        }
+
+        return $style[0] . implode($style[2], $array) . $style[3] . PHP_EOL;
+    }
+
+    /**
+     * 输出表格头部
+     * @access public
+     * @return string
+     */
+    protected function renderHeader(): string
+    {
+        $style   = $this->getStyle('cell');
+        $content = $this->renderSeparator('top');
+
+        foreach ($this->header as $key => $header) {
+            $array[] = ' ' . str_pad($header, $this->colWidth[$key], $style[1], $this->headerAlign);
+        }
+
+        if (!empty($array)) {
+            $content .= $style[0] . implode(' ' . $style[2], $array) . ' ' . $style[3] . PHP_EOL;
+
+            if (!empty($this->rows)) {
+                $content .= $this->renderSeparator('middle');
+            }
+        }
+
+        return $content;
+    }
+
+    protected function getStyle(string $style): array
+    {
+        if ($this->format[$this->style]) {
+            $style = $this->format[$this->style][$style];
+        } else {
+            $style = [' ', ' ', ' ', ' '];
+        }
+
+        return $style;
+    }
+
+    /**
+     * 输出表格
+     * @access public
+     * @param  array $dataList       表格数据
+     * @return string
+     */
+    public function render(array $dataList = []): string
+    {
+        if (!empty($dataList)) {
+            $this->setRows($dataList);
+        }
+
+        // 输出头部
+        $content = $this->renderHeader();
+        $style   = $this->getStyle('cell');
+
+        if (!empty($this->rows)) {
+            foreach ($this->rows as $row) {
+                if (is_string($row) && '-' === $row) {
+                    $content .= $this->renderSeparator('middle');
+                } elseif (is_scalar($row)) {
+                    $content .= $this->renderSeparator('cross-top');
+                    $width = 3 * (count($this->colWidth) - 1) + array_reduce($this->colWidth, function ($a, $b) {
+                        return $a + $b;
+                    });
+                    $array = str_pad($row, $width);
+
+                    $content .= $style[0] . ' ' . $array . ' ' . $style[3] . PHP_EOL;
+                    $content .= $this->renderSeparator('cross-bottom');
+                } else {
+                    $array = [];
+
+                    foreach ($row as $key => $val) {
+                        $width = $this->colWidth[$key];
+                        // form https://github.com/symfony/console/blob/20c9821c8d1c2189f287dcee709b2f86353ea08f/Helper/Table.php#L467
+                        // str_pad won't work properly with multi-byte strings, we need to fix the padding
+                        if (false !== $encoding = mb_detect_encoding((string) $val, null, true)) {
+                            $width += strlen((string) $val) - mb_strwidth((string) $val, $encoding);
+                        }
+                        $array[] = ' ' . str_pad((string) $val, $width, ' ', $this->cellAlign);
+                    }
+
+                    $content .= $style[0] . implode(' ' . $style[2], $array) . ' ' . $style[3] . PHP_EOL;
+                }
+            }
+        }
+
+        $content .= $this->renderSeparator('bottom');
+
+        return $content;
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/bin/README.md b/vendor/topthink/framework/src/think/console/bin/README.md
new file mode 100755
index 0000000..9acc52f
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/bin/README.md
@@ -0,0 +1 @@
+console 工具使用 hiddeninput.exe 在 windows 上隐藏密码输入,该二进制文件由第三方提供,相关源码和其他细节可以在 [Hidden Input](https://github.com/Seldaek/hidden-input) 找到。
diff --git a/vendor/topthink/framework/src/think/console/bin/hiddeninput.exe b/vendor/topthink/framework/src/think/console/bin/hiddeninput.exe
new file mode 100755
index 0000000..c8cf65e
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/bin/hiddeninput.exe
Binary files differ
diff --git a/vendor/topthink/framework/src/think/console/command/Clear.php b/vendor/topthink/framework/src/think/console/command/Clear.php
new file mode 100755
index 0000000..da70b35
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/Clear.php
@@ -0,0 +1,85 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+namespace think\console\command;
+
+use think\console\Command;
+use think\console\Input;
+use think\console\input\Option;
+use think\console\Output;
+
+class Clear extends Command
+{
+    protected function configure()
+    {
+        // 指令配置
+        $this->setName('clear')
+            ->addOption('path', 'd', Option::VALUE_OPTIONAL, 'path to clear', null)
+            ->addOption('cache', 'c', Option::VALUE_NONE, 'clear cache file')
+            ->addOption('log', 'l', Option::VALUE_NONE, 'clear log file')
+            ->addOption('dir', 'r', Option::VALUE_NONE, 'clear empty dir')
+            ->addOption('expire', 'e', Option::VALUE_NONE, 'clear cache file if cache has expired')
+            ->setDescription('Clear runtime file');
+    }
+
+    protected function execute(Input $input, Output $output)
+    {
+        $runtimePath = $this->app->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR;
+
+        if ($input->getOption('cache')) {
+            $path = $runtimePath . 'cache';
+        } elseif ($input->getOption('log')) {
+            $path = $runtimePath . 'log';
+        } else {
+            $path = $input->getOption('path') ?: $runtimePath;
+        }
+
+        $rmdir = $input->getOption('dir') ? true : false;
+        // --expire 仅当 --cache 时生效
+        $cache_expire = $input->getOption('expire') && $input->getOption('cache') ? true : false;
+        $this->clear(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR, $rmdir, $cache_expire);
+
+        $output->writeln("<info>Clear Successed</info>");
+    }
+
+    protected function clear(string $path, bool $rmdir, bool $cache_expire): void
+    {
+        $files = is_dir($path) ? scandir($path) : [];
+
+        foreach ($files as $file) {
+            if ('.' != $file && '..' != $file && is_dir($path . $file)) {
+                $this->clear($path . $file . DIRECTORY_SEPARATOR, $rmdir, $cache_expire);
+                if ($rmdir) {
+                    @rmdir($path . $file);
+                }
+            } elseif ('.gitignore' != $file && is_file($path . $file)) {
+                if ($cache_expire) {
+                    if ($this->cacheHasExpired($path . $file)) {
+                        unlink($path . $file);
+                    }
+                } else {
+                    unlink($path . $file);
+                }
+            }
+        }
+    }
+
+    /**
+     * 缓存文件是否已过期
+     * @param $filename string 文件路径
+     * @return bool
+     */
+    protected function cacheHasExpired($filename) {
+        $content = file_get_contents($filename);
+        $expire = (int) substr($content, 8, 12);
+        return 0 != $expire && time() - $expire > filemtime($filename);
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/console/command/Help.php b/vendor/topthink/framework/src/think/console/command/Help.php
new file mode 100755
index 0000000..2e4f2ca
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/Help.php
@@ -0,0 +1,70 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\console\command;
+
+use think\console\Command;
+use think\console\Input;
+use think\console\input\Argument as InputArgument;
+use think\console\input\Option as InputOption;
+use think\console\Output;
+
+class Help extends Command
+{
+
+    private $command;
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function configure()
+    {
+        $this->ignoreValidationErrors();
+
+        $this->setName('help')->setDefinition([
+            new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'),
+            new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'),
+        ])->setDescription('Displays help for a command')->setHelp(
+            <<<EOF
+The <info>%command.name%</info> command displays help for a given command:
+
+  <info>php %command.full_name% list</info>
+
+To display the list of available commands, please use the <info>list</info> command.
+EOF
+        );
+    }
+
+    /**
+     * Sets the command.
+     * @param Command $command The command to set
+     */
+    public function setCommand(Command $command): void
+    {
+        $this->command = $command;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function execute(Input $input, Output $output)
+    {
+        if (null === $this->command) {
+            $this->command = $this->getConsole()->find($input->getArgument('command_name'));
+        }
+
+        $output->describe($this->command, [
+            'raw_text' => $input->getOption('raw'),
+        ]);
+
+        $this->command = null;
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/command/Lists.php b/vendor/topthink/framework/src/think/console/command/Lists.php
new file mode 100755
index 0000000..d20fc75
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/Lists.php
@@ -0,0 +1,74 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\console\command;
+
+use think\console\Command;
+use think\console\Input;
+use think\console\input\Argument as InputArgument;
+use think\console\input\Definition as InputDefinition;
+use think\console\input\Option as InputOption;
+use think\console\Output;
+
+class Lists extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function configure()
+    {
+        $this->setName('list')->setDefinition($this->createDefinition())->setDescription('Lists commands')->setHelp(
+            <<<EOF
+The <info>%command.name%</info> command lists all commands:
+
+  <info>php %command.full_name%</info>
+
+You can also display the commands for a specific namespace:
+
+  <info>php %command.full_name% test</info>
+
+It's also possible to get raw list of commands (useful for embedding command runner):
+
+  <info>php %command.full_name% --raw</info>
+EOF
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getNativeDefinition(): InputDefinition
+    {
+        return $this->createDefinition();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function execute(Input $input, Output $output)
+    {
+        $output->describe($this->getConsole(), [
+            'raw_text'  => $input->getOption('raw'),
+            'namespace' => $input->getArgument('namespace'),
+        ]);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    private function createDefinition(): InputDefinition
+    {
+        return new InputDefinition([
+            new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'),
+            new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'),
+        ]);
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/command/Make.php b/vendor/topthink/framework/src/think/console/command/Make.php
new file mode 100755
index 0000000..a74e9e8
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/Make.php
@@ -0,0 +1,99 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: 刘志淳 <chun@engineer.com>
+// +----------------------------------------------------------------------
+
+namespace think\console\command;
+
+use think\console\Command;
+use think\console\Input;
+use think\console\input\Argument;
+use think\console\Output;
+
+abstract class Make extends Command
+{
+    protected $type;
+
+    abstract protected function getStub();
+
+    protected function configure()
+    {
+        $this->addArgument('name', Argument::REQUIRED, "The name of the class");
+    }
+
+    protected function execute(Input $input, Output $output)
+    {
+        $name = trim($input->getArgument('name'));
+
+        $classname = $this->getClassName($name);
+
+        $pathname = $this->getPathName($classname);
+
+        if (is_file($pathname)) {
+            $output->writeln('<error>' . $this->type . ':' . $classname . ' already exists!</error>');
+            return false;
+        }
+
+        if (!is_dir(dirname($pathname))) {
+            mkdir(dirname($pathname), 0755, true);
+        }
+
+        file_put_contents($pathname, $this->buildClass($classname));
+
+        $output->writeln('<info>' . $this->type . ':' . $classname . ' created successfully.</info>');
+    }
+
+    protected function buildClass(string $name)
+    {
+        $stub = file_get_contents($this->getStub());
+
+        $namespace = trim(implode('\\', array_slice(explode('\\', $name), 0, -1)), '\\');
+
+        $class = str_replace($namespace . '\\', '', $name);
+
+        return str_replace(['{%className%}', '{%actionSuffix%}', '{%namespace%}', '{%app_namespace%}'], [
+            $class,
+            $this->app->config->get('route.action_suffix'),
+            $namespace,
+            $this->app->getNamespace(),
+        ], $stub);
+    }
+
+    protected function getPathName(string $name): string
+    {
+        $name = substr($name, 4);
+
+        return $this->app->getBasePath() . ltrim(str_replace('\\', '/', $name), '/') . '.php';
+    }
+
+    protected function getClassName(string $name): string
+    {
+        if (strpos($name, '\\') !== false) {
+            return $name;
+        }
+
+        if (strpos($name, '@')) {
+            [$app, $name] = explode('@', $name);
+        } else {
+            $app = '';
+        }
+
+        if (strpos($name, '/') !== false) {
+            $name = str_replace('/', '\\', $name);
+        }
+
+        return $this->getNamespace($app) . '\\' . $name;
+    }
+
+    protected function getNamespace(string $app): string
+    {
+        return 'app' . ($app ? '\\' . $app : '');
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/console/command/RouteList.php b/vendor/topthink/framework/src/think/console/command/RouteList.php
new file mode 100755
index 0000000..e9d4c5d
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/RouteList.php
@@ -0,0 +1,128 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+namespace think\console\command;
+
+use think\console\Command;
+use think\console\Input;
+use think\console\input\Argument;
+use think\console\input\Option;
+use think\console\Output;
+use think\console\Table;
+use think\event\RouteLoaded;
+
+class RouteList extends Command
+{
+    protected $sortBy = [
+        'rule'   => 0,
+        'route'  => 1,
+        'method' => 2,
+        'name'   => 3,
+        'domain' => 4,
+    ];
+
+    protected function configure()
+    {
+        $this->setName('route:list')
+            ->addArgument('dir', Argument::OPTIONAL, 'dir name .')
+            ->addArgument('style', Argument::OPTIONAL, "the style of the table.", 'default')
+            ->addOption('sort', 's', Option::VALUE_OPTIONAL, 'order by rule name.', 0)
+            ->addOption('more', 'm', Option::VALUE_NONE, 'show route options.')
+            ->setDescription('show route list.');
+    }
+
+    protected function execute(Input $input, Output $output)
+    {
+        $dir = $input->getArgument('dir') ?: '';
+
+        $filename = $this->app->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR . ($dir ? $dir . DIRECTORY_SEPARATOR : '') . 'route_list.php';
+
+        if (is_file($filename)) {
+            unlink($filename);
+        } elseif (!is_dir(dirname($filename))) {
+            mkdir(dirname($filename), 0755);
+        }
+
+        $content = $this->getRouteList($dir);
+        file_put_contents($filename, 'Route List' . PHP_EOL . $content);
+    }
+
+    protected function getRouteList(string $dir = null): string
+    {
+        $this->app->route->setTestMode(true);
+        $this->app->route->clear();
+
+        if ($dir) {
+            $path = $this->app->getRootPath() . 'route' . DIRECTORY_SEPARATOR . $dir . DIRECTORY_SEPARATOR;
+        } else {
+            $path = $this->app->getRootPath() . 'route' . DIRECTORY_SEPARATOR;
+        }
+
+        $files = is_dir($path) ? scandir($path) : [];
+
+        foreach ($files as $file) {
+            if (strpos($file, '.php')) {
+                include $path . $file;
+            }
+        }
+
+        //触发路由载入完成事件
+        $this->app->event->trigger(RouteLoaded::class);
+
+        $table = new Table();
+
+        if ($this->input->hasOption('more')) {
+            $header = ['Rule', 'Route', 'Method', 'Name', 'Domain', 'Option', 'Pattern'];
+        } else {
+            $header = ['Rule', 'Route', 'Method', 'Name'];
+        }
+
+        $table->setHeader($header);
+
+        $routeList = $this->app->route->getRuleList();
+        $rows      = [];
+
+        foreach ($routeList as $item) {
+            $item['route'] = $item['route'] instanceof \Closure ? '<Closure>' : $item['route'];
+            $row           = [$item['rule'], $item['route'], $item['method'], $item['name']];
+
+            if ($this->input->hasOption('more')) {
+                array_push($row, $item['domain'], json_encode($item['option']), json_encode($item['pattern']));
+            }
+
+            $rows[] = $row;
+        }
+
+        if ($this->input->getOption('sort')) {
+            $sort = strtolower($this->input->getOption('sort'));
+
+            if (isset($this->sortBy[$sort])) {
+                $sort = $this->sortBy[$sort];
+            }
+
+            uasort($rows, function ($a, $b) use ($sort) {
+                $itemA = $a[$sort] ?? null;
+                $itemB = $b[$sort] ?? null;
+
+                return strcasecmp($itemA, $itemB);
+            });
+        }
+
+        $table->setRows($rows);
+
+        if ($this->input->getArgument('style')) {
+            $style = $this->input->getArgument('style');
+            $table->setStyle($style);
+        }
+
+        return $this->table($table);
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/console/command/RunServer.php b/vendor/topthink/framework/src/think/console/command/RunServer.php
new file mode 100755
index 0000000..d507c1d
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/RunServer.php
@@ -0,0 +1,72 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: Slince <taosikai@yeah.net>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\console\command;
+
+use think\console\Command;
+use think\console\Input;
+use think\console\input\Option;
+use think\console\Output;
+
+class RunServer extends Command
+{
+    public function configure()
+    {
+        $this->setName('run')
+            ->addOption(
+                'host',
+                'H',
+                Option::VALUE_OPTIONAL,
+                'The host to server the application on',
+                '0.0.0.0'
+            )
+            ->addOption(
+                'port',
+                'p',
+                Option::VALUE_OPTIONAL,
+                'The port to server the application on',
+                8000
+            )
+            ->addOption(
+                'root',
+                'r',
+                Option::VALUE_OPTIONAL,
+                'The document root of the application',
+                ''
+            )
+            ->setDescription('PHP Built-in Server for ThinkPHP');
+    }
+
+    public function execute(Input $input, Output $output)
+    {
+        $host = $input->getOption('host');
+        $port = $input->getOption('port');
+        $root = $input->getOption('root');
+        if (empty($root)) {
+            $root = $this->app->getRootPath() . 'public';
+        }
+
+        $command = sprintf(
+            'php -S %s:%d -t %s %s',
+            $host,
+            $port,
+            escapeshellarg($root),
+            escapeshellarg($root . DIRECTORY_SEPARATOR . 'router.php')
+        );
+
+        $output->writeln(sprintf('ThinkPHP Development server is started On <http://%s:%s/>', $host, $port));
+        $output->writeln(sprintf('You can exit with <info>`CTRL-C`</info>'));
+        $output->writeln(sprintf('Document root is: %s', $root));
+        passthru($command);
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/console/command/ServiceDiscover.php b/vendor/topthink/framework/src/think/console/command/ServiceDiscover.php
new file mode 100755
index 0000000..e90f433
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/ServiceDiscover.php
@@ -0,0 +1,52 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\console\command;
+
+use think\console\Command;
+use think\console\Input;
+use think\console\Output;
+
+class ServiceDiscover extends Command
+{
+    public function configure()
+    {
+        $this->setName('service:discover')
+            ->setDescription('Discover Services for ThinkPHP');
+    }
+
+    public function execute(Input $input, Output $output)
+    {
+        if (is_file($path = $this->app->getRootPath() . 'vendor/composer/installed.json')) {
+            $packages = json_decode(@file_get_contents($path), true);
+            // Compatibility with Composer 2.0
+            if (isset($packages['packages'])) {
+                $packages = $packages['packages'];
+            }
+
+            $services = [];
+            foreach ($packages as $package) {
+                if (!empty($package['extra']['think']['services'])) {
+                    $services = array_merge($services, (array) $package['extra']['think']['services']);
+                }
+            }
+
+            $header = '// This file is automatically generated at:' . date('Y-m-d H:i:s') . PHP_EOL . 'declare (strict_types = 1);' . PHP_EOL;
+
+            $content = '<?php ' . PHP_EOL . $header . "return " . var_export($services, true) . ';';
+
+            file_put_contents($this->app->getRootPath() . 'vendor/services.php', $content);
+
+            $output->writeln('<info>Succeed!</info>');
+        }
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/command/VendorPublish.php b/vendor/topthink/framework/src/think/console/command/VendorPublish.php
new file mode 100755
index 0000000..3998765
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/VendorPublish.php
@@ -0,0 +1,69 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\console\command;
+
+use think\console\Command;
+use think\console\input\Option;
+
+class VendorPublish extends Command
+{
+    public function configure()
+    {
+        $this->setName('vendor:publish')
+            ->addOption('force', 'f', Option::VALUE_NONE, 'Overwrite any existing files')
+            ->setDescription('Publish any publishable assets from vendor packages');
+    }
+
+    public function handle()
+    {
+
+        $force = $this->input->getOption('force');
+
+        if (is_file($path = $this->app->getRootPath() . 'vendor/composer/installed.json')) {
+            $packages = json_decode(@file_get_contents($path), true);
+            // Compatibility with Composer 2.0
+            if (isset($packages['packages'])) {
+                $packages = $packages['packages'];
+            }
+            foreach ($packages as $package) {
+                //配置
+                $configDir = $this->app->getConfigPath();
+
+                if (!empty($package['extra']['think']['config'])) {
+
+                    $installPath = $this->app->getRootPath() . 'vendor/' . $package['name'] . DIRECTORY_SEPARATOR;
+
+                    foreach ((array) $package['extra']['think']['config'] as $name => $file) {
+
+                        $target = $configDir . $name . '.php';
+                        $source = $installPath . $file;
+
+                        if (is_file($target) && !$force) {
+                            $this->output->info("File {$target} exist!");
+                            continue;
+                        }
+
+                        if (!is_file($source)) {
+                            $this->output->info("File {$source} not exist!");
+                            continue;
+                        }
+
+                        copy($source, $target);
+                    }
+                }
+            }
+
+            $this->output->writeln('<info>Succeed!</info>');
+        }
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/command/Version.php b/vendor/topthink/framework/src/think/console/command/Version.php
new file mode 100755
index 0000000..beb49d2
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/Version.php
@@ -0,0 +1,33 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\console\command;
+
+use think\console\Command;
+use think\console\Input;
+use think\console\Output;
+
+class Version extends Command
+{
+    protected function configure()
+    {
+        // 指令配置
+        $this->setName('version')
+            ->setDescription('show thinkphp framework version');
+    }
+
+    protected function execute(Input $input, Output $output)
+    {
+        $output->writeln('v' . $this->app->version());
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/console/command/make/Command.php b/vendor/topthink/framework/src/think/console/command/make/Command.php
new file mode 100755
index 0000000..9549a02
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/make/Command.php
@@ -0,0 +1,55 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\console\command\make;
+
+use think\console\command\Make;
+use think\console\input\Argument;
+
+class Command extends Make
+{
+    protected $type = "Command";
+
+    protected function configure()
+    {
+        parent::configure();
+        $this->setName('make:command')
+            ->addArgument('commandName', Argument::OPTIONAL, "The name of the command")
+            ->setDescription('Create a new command class');
+    }
+
+    protected function buildClass(string $name): string
+    {
+        $commandName = $this->input->getArgument('commandName') ?: strtolower(basename($name));
+        $namespace   = trim(implode('\\', array_slice(explode('\\', $name), 0, -1)), '\\');
+
+        $class = str_replace($namespace . '\\', '', $name);
+        $stub  = file_get_contents($this->getStub());
+
+        return str_replace(['{%commandName%}', '{%className%}', '{%namespace%}', '{%app_namespace%}'], [
+            $commandName,
+            $class,
+            $namespace,
+            $this->app->getNamespace(),
+        ], $stub);
+    }
+
+    protected function getStub(): string
+    {
+        return __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'command.stub';
+    }
+
+    protected function getNamespace(string $app): string
+    {
+        return parent::getNamespace($app) . '\\command';
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/console/command/make/Controller.php b/vendor/topthink/framework/src/think/console/command/make/Controller.php
new file mode 100755
index 0000000..4a8d226
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/make/Controller.php
@@ -0,0 +1,56 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\console\command\make;
+
+use think\console\command\Make;
+use think\console\input\Option;
+
+class Controller extends Make
+{
+
+    protected $type = "Controller";
+
+    protected function configure()
+    {
+        parent::configure();
+        $this->setName('make:controller')
+            ->addOption('api', null, Option::VALUE_NONE, 'Generate an api controller class.')
+            ->addOption('plain', null, Option::VALUE_NONE, 'Generate an empty controller class.')
+            ->setDescription('Create a new resource controller class');
+    }
+
+    protected function getStub(): string
+    {
+        $stubPath = __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR;
+
+        if ($this->input->getOption('api')) {
+            return $stubPath . 'controller.api.stub';
+        }
+
+        if ($this->input->getOption('plain')) {
+            return $stubPath . 'controller.plain.stub';
+        }
+
+        return $stubPath . 'controller.stub';
+    }
+
+    protected function getClassName(string $name): string
+    {
+        return parent::getClassName($name) . ($this->app->config->get('route.controller_suffix') ? 'Controller' : '');
+    }
+
+    protected function getNamespace(string $app): string
+    {
+        return parent::getNamespace($app) . '\\controller';
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/console/command/make/Event.php b/vendor/topthink/framework/src/think/console/command/make/Event.php
new file mode 100755
index 0000000..6b16689
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/make/Event.php
@@ -0,0 +1,35 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+namespace think\console\command\make;
+
+use think\console\command\Make;
+
+class Event extends Make
+{
+    protected $type = "Event";
+
+    protected function configure()
+    {
+        parent::configure();
+        $this->setName('make:event')
+            ->setDescription('Create a new event class');
+    }
+
+    protected function getStub(): string
+    {
+        return __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'event.stub';
+    }
+
+    protected function getNamespace(string $app): string
+    {
+        return parent::getNamespace($app) . '\\event';
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/command/make/Listener.php b/vendor/topthink/framework/src/think/console/command/make/Listener.php
new file mode 100755
index 0000000..5c92673
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/make/Listener.php
@@ -0,0 +1,35 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+namespace think\console\command\make;
+
+use think\console\command\Make;
+
+class Listener extends Make
+{
+    protected $type = "Listener";
+
+    protected function configure()
+    {
+        parent::configure();
+        $this->setName('make:listener')
+            ->setDescription('Create a new listener class');
+    }
+
+    protected function getStub(): string
+    {
+        return __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'listener.stub';
+    }
+
+    protected function getNamespace(string $app): string
+    {
+        return parent::getNamespace($app) . '\\listener';
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/command/make/Middleware.php b/vendor/topthink/framework/src/think/console/command/make/Middleware.php
new file mode 100755
index 0000000..3b68b4a
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/make/Middleware.php
@@ -0,0 +1,36 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\console\command\make;
+
+use think\console\command\Make;
+
+class Middleware extends Make
+{
+    protected $type = "Middleware";
+
+    protected function configure()
+    {
+        parent::configure();
+        $this->setName('make:middleware')
+            ->setDescription('Create a new middleware class');
+    }
+
+    protected function getStub(): string
+    {
+        return __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'middleware.stub';
+    }
+
+    protected function getNamespace(string $app): string
+    {
+        return parent::getNamespace($app) . '\\middleware';
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/command/make/Model.php b/vendor/topthink/framework/src/think/console/command/make/Model.php
new file mode 100755
index 0000000..cb7a23c
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/make/Model.php
@@ -0,0 +1,36 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\console\command\make;
+
+use think\console\command\Make;
+
+class Model extends Make
+{
+    protected $type = "Model";
+
+    protected function configure()
+    {
+        parent::configure();
+        $this->setName('make:model')
+            ->setDescription('Create a new model class');
+    }
+
+    protected function getStub(): string
+    {
+        return __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'model.stub';
+    }
+
+    protected function getNamespace(string $app): string
+    {
+        return parent::getNamespace($app) . '\\model';
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/command/make/Service.php b/vendor/topthink/framework/src/think/console/command/make/Service.php
new file mode 100755
index 0000000..c4bbaa0
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/make/Service.php
@@ -0,0 +1,36 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\console\command\make;
+
+use think\console\command\Make;
+
+class Service extends Make
+{
+    protected $type = "Service";
+
+    protected function configure()
+    {
+        parent::configure();
+        $this->setName('make:service')
+            ->setDescription('Create a new Service class');
+    }
+
+    protected function getStub(): string
+    {
+        return __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'service.stub';
+    }
+
+    protected function getNamespace(string $app): string
+    {
+        return parent::getNamespace($app) . '\\service';
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/command/make/Subscribe.php b/vendor/topthink/framework/src/think/console/command/make/Subscribe.php
new file mode 100755
index 0000000..a1dc2a8
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/make/Subscribe.php
@@ -0,0 +1,35 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+namespace think\console\command\make;
+
+use think\console\command\Make;
+
+class Subscribe extends Make
+{
+    protected $type = "Subscribe";
+
+    protected function configure()
+    {
+        parent::configure();
+        $this->setName('make:subscribe')
+            ->setDescription('Create a new subscribe class');
+    }
+
+    protected function getStub(): string
+    {
+        return __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'subscribe.stub';
+    }
+
+    protected function getNamespace(string $app): string
+    {
+        return parent::getNamespace($app) . '\\subscribe';
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/command/make/Validate.php b/vendor/topthink/framework/src/think/console/command/make/Validate.php
new file mode 100755
index 0000000..8d36431
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/make/Validate.php
@@ -0,0 +1,39 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\console\command\make;
+
+use think\console\command\Make;
+
+class Validate extends Make
+{
+    protected $type = "Validate";
+
+    protected function configure()
+    {
+        parent::configure();
+        $this->setName('make:validate')
+            ->setDescription('Create a validate class');
+    }
+
+    protected function getStub(): string
+    {
+        $stubPath = __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR;
+
+        return $stubPath . 'validate.stub';
+    }
+
+    protected function getNamespace(string $app): string
+    {
+        return parent::getNamespace($app) . '\\validate';
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/console/command/make/stubs/command.stub b/vendor/topthink/framework/src/think/console/command/make/stubs/command.stub
new file mode 100755
index 0000000..3ee2b1c
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/make/stubs/command.stub
@@ -0,0 +1,26 @@
+<?php
+declare (strict_types = 1);
+
+namespace {%namespace%};
+
+use think\console\Command;
+use think\console\Input;
+use think\console\input\Argument;
+use think\console\input\Option;
+use think\console\Output;
+
+class {%className%} extends Command
+{
+    protected function configure()
+    {
+        // 指令配置
+        $this->setName('{%commandName%}')
+            ->setDescription('the {%commandName%} command');
+    }
+
+    protected function execute(Input $input, Output $output)
+    {
+        // 指令输出
+        $output->writeln('{%commandName%}');
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/command/make/stubs/controller.api.stub b/vendor/topthink/framework/src/think/console/command/make/stubs/controller.api.stub
new file mode 100755
index 0000000..5d3383d
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/make/stubs/controller.api.stub
@@ -0,0 +1,64 @@
+<?php
+declare (strict_types = 1);
+
+namespace {%namespace%};
+
+use think\Request;
+
+class {%className%}
+{
+    /**
+     * 显示资源列表
+     *
+     * @return \think\Response
+     */
+    public function index{%actionSuffix%}()
+    {
+        //
+    }
+
+    /**
+     * 保存新建的资源
+     *
+     * @param  \think\Request  $request
+     * @return \think\Response
+     */
+    public function save{%actionSuffix%}(Request $request)
+    {
+        //
+    }
+
+    /**
+     * 显示指定的资源
+     *
+     * @param  int  $id
+     * @return \think\Response
+     */
+    public function read{%actionSuffix%}($id)
+    {
+        //
+    }
+
+    /**
+     * 保存更新的资源
+     *
+     * @param  \think\Request  $request
+     * @param  int  $id
+     * @return \think\Response
+     */
+    public function update{%actionSuffix%}(Request $request, $id)
+    {
+        //
+    }
+
+    /**
+     * 删除指定资源
+     *
+     * @param  int  $id
+     * @return \think\Response
+     */
+    public function delete{%actionSuffix%}($id)
+    {
+        //
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/command/make/stubs/controller.plain.stub b/vendor/topthink/framework/src/think/console/command/make/stubs/controller.plain.stub
new file mode 100755
index 0000000..347c209
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/make/stubs/controller.plain.stub
@@ -0,0 +1,9 @@
+<?php
+declare (strict_types = 1);
+
+namespace {%namespace%};
+
+class {%className%}
+{
+    //
+}
diff --git a/vendor/topthink/framework/src/think/console/command/make/stubs/controller.stub b/vendor/topthink/framework/src/think/console/command/make/stubs/controller.stub
new file mode 100755
index 0000000..8e5d82d
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/make/stubs/controller.stub
@@ -0,0 +1,85 @@
+<?php
+declare (strict_types = 1);
+
+namespace {%namespace%};
+
+use think\Request;
+
+class {%className%}
+{
+    /**
+     * 显示资源列表
+     *
+     * @return \think\Response
+     */
+    public function index{%actionSuffix%}()
+    {
+        //
+    }
+
+    /**
+     * 显示创建资源表单页.
+     *
+     * @return \think\Response
+     */
+    public function create{%actionSuffix%}()
+    {
+        //
+    }
+
+    /**
+     * 保存新建的资源
+     *
+     * @param  \think\Request  $request
+     * @return \think\Response
+     */
+    public function save{%actionSuffix%}(Request $request)
+    {
+        //
+    }
+
+    /**
+     * 显示指定的资源
+     *
+     * @param  int  $id
+     * @return \think\Response
+     */
+    public function read{%actionSuffix%}($id)
+    {
+        //
+    }
+
+    /**
+     * 显示编辑资源表单页.
+     *
+     * @param  int  $id
+     * @return \think\Response
+     */
+    public function edit{%actionSuffix%}($id)
+    {
+        //
+    }
+
+    /**
+     * 保存更新的资源
+     *
+     * @param  \think\Request  $request
+     * @param  int  $id
+     * @return \think\Response
+     */
+    public function update{%actionSuffix%}(Request $request, $id)
+    {
+        //
+    }
+
+    /**
+     * 删除指定资源
+     *
+     * @param  int  $id
+     * @return \think\Response
+     */
+    public function delete{%actionSuffix%}($id)
+    {
+        //
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/command/make/stubs/event.stub b/vendor/topthink/framework/src/think/console/command/make/stubs/event.stub
new file mode 100755
index 0000000..a173089
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/make/stubs/event.stub
@@ -0,0 +1,8 @@
+<?php
+declare (strict_types = 1);
+
+namespace {%namespace%};
+
+class {%className%}
+{
+}
diff --git a/vendor/topthink/framework/src/think/console/command/make/stubs/listener.stub b/vendor/topthink/framework/src/think/console/command/make/stubs/listener.stub
new file mode 100755
index 0000000..0372438
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/make/stubs/listener.stub
@@ -0,0 +1,17 @@
+<?php
+declare (strict_types = 1);
+
+namespace {%namespace%};
+
+class {%className%}
+{
+    /**
+     * 事件监听处理
+     *
+     * @return mixed
+     */
+    public function handle($event)
+    {
+        //
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/command/make/stubs/middleware.stub b/vendor/topthink/framework/src/think/console/command/make/stubs/middleware.stub
new file mode 100755
index 0000000..38cf5f2
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/make/stubs/middleware.stub
@@ -0,0 +1,19 @@
+<?php
+declare (strict_types = 1);
+
+namespace {%namespace%};
+
+class {%className%}
+{
+    /**
+     * 处理请求
+     *
+     * @param \think\Request $request
+     * @param \Closure       $next
+     * @return Response
+     */
+    public function handle($request, \Closure $next)
+    {
+        //
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/command/make/stubs/model.stub b/vendor/topthink/framework/src/think/console/command/make/stubs/model.stub
new file mode 100755
index 0000000..bb94a38
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/make/stubs/model.stub
@@ -0,0 +1,14 @@
+<?php
+declare (strict_types = 1);
+
+namespace {%namespace%};
+
+use think\Model;
+
+/**
+ * @mixin \think\Model
+ */
+class {%className%} extends Model
+{
+    //
+}
diff --git a/vendor/topthink/framework/src/think/console/command/make/stubs/service.stub b/vendor/topthink/framework/src/think/console/command/make/stubs/service.stub
new file mode 100755
index 0000000..5d74d15
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/make/stubs/service.stub
@@ -0,0 +1,27 @@
+<?php
+declare (strict_types = 1);
+
+namespace {%namespace%};
+
+class {%className%} extends \think\Service
+{
+    /**
+     * 注册服务
+     *
+     * @return mixed
+     */
+    public function register()
+    {
+    	//
+    }
+
+    /**
+     * 执行服务
+     *
+     * @return mixed
+     */
+    public function boot()
+    {
+        //
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/command/make/stubs/subscribe.stub b/vendor/topthink/framework/src/think/console/command/make/stubs/subscribe.stub
new file mode 100755
index 0000000..a173089
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/make/stubs/subscribe.stub
@@ -0,0 +1,8 @@
+<?php
+declare (strict_types = 1);
+
+namespace {%namespace%};
+
+class {%className%}
+{
+}
diff --git a/vendor/topthink/framework/src/think/console/command/make/stubs/validate.stub b/vendor/topthink/framework/src/think/console/command/make/stubs/validate.stub
new file mode 100755
index 0000000..d1fedd8
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/make/stubs/validate.stub
@@ -0,0 +1,25 @@
+<?php
+declare (strict_types = 1);
+
+namespace {%namespace%};
+
+use think\Validate;
+
+class {%className%} extends Validate
+{
+    /**
+     * 定义验证规则
+     * 格式:'字段名' =>  ['规则1','规则2'...]
+     *
+     * @var array
+     */
+    protected $rule = [];
+
+    /**
+     * 定义错误信息
+     * 格式:'字段名.规则名' =>  '错误信息'
+     *
+     * @var array
+     */
+    protected $message = [];
+}
diff --git a/vendor/topthink/framework/src/think/console/command/optimize/Route.php b/vendor/topthink/framework/src/think/console/command/optimize/Route.php
new file mode 100755
index 0000000..56f7f5a
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/optimize/Route.php
@@ -0,0 +1,66 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+namespace think\console\command\optimize;
+
+use think\console\Command;
+use think\console\Input;
+use think\console\input\Argument;
+use think\console\Output;
+use think\event\RouteLoaded;
+
+class Route extends Command
+{
+    protected function configure()
+    {
+        $this->setName('optimize:route')
+            ->addArgument('dir', Argument::OPTIONAL, 'dir name .')
+            ->setDescription('Build app route cache.');
+    }
+
+    protected function execute(Input $input, Output $output)
+    {
+        $dir = $input->getArgument('dir') ?: '';
+
+        $path = $this->app->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR . ($dir ? $dir . DIRECTORY_SEPARATOR : '');
+
+        $filename = $path . 'route.php';
+        if (is_file($filename)) {
+            unlink($filename);
+        }
+
+        file_put_contents($filename, $this->buildRouteCache($dir));
+        $output->writeln('<info>Succeed!</info>');
+    }
+
+    protected function buildRouteCache(string $dir = null): string
+    {
+        $this->app->route->clear();
+        $this->app->route->lazy(false);
+
+        // 路由检测
+        $path = $this->app->getRootPath() . ($dir ? 'app' . DIRECTORY_SEPARATOR . $dir . DIRECTORY_SEPARATOR : '') . 'route' . DIRECTORY_SEPARATOR;
+
+        $files = is_dir($path) ? scandir($path) : [];
+
+        foreach ($files as $file) {
+            if (strpos($file, '.php')) {
+                include $path . $file;
+            }
+        }
+
+        //触发路由载入完成事件
+        $this->app->event->trigger(RouteLoaded::class);
+        $rules = $this->app->route->getName();
+
+        return '<?php ' . PHP_EOL . 'return unserialize(\'' . serialize($rules) . '\');';
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/console/command/optimize/Schema.php b/vendor/topthink/framework/src/think/console/command/optimize/Schema.php
new file mode 100755
index 0000000..6ce3e64
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/command/optimize/Schema.php
@@ -0,0 +1,104 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+namespace think\console\command\optimize;
+
+use Exception;
+use think\console\Command;
+use think\console\Input;
+use think\console\input\Argument;
+use think\console\input\Option;
+use think\console\Output;
+use think\db\PDOConnection;
+
+class Schema extends Command
+{
+    protected function configure()
+    {
+        $this->setName('optimize:schema')
+            ->addArgument('dir', Argument::OPTIONAL, 'dir name .')
+            ->addOption('connection', null, Option::VALUE_REQUIRED, 'connection name .')
+            ->addOption('table', null, Option::VALUE_REQUIRED, 'table name .')
+            ->setDescription('Build database schema cache.');
+    }
+
+    protected function execute(Input $input, Output $output)
+    {
+        $dir = $input->getArgument('dir') ?: '';
+
+        if ($input->hasOption('table')) {
+            $connection = $this->app->db->connect($input->getOption('connection'));
+            if (!$connection instanceof PDOConnection) {
+                $output->error("only PDO connection support schema cache!");
+                return;
+            }
+            $table = $input->getOption('table');
+            if (false === strpos($table, '.')) {
+                $dbName = $connection->getConfig('database');
+            } else {
+                [$dbName, $table] = explode('.', $table);
+            }
+
+            if ($table == '*') {
+                $table = $connection->getTables($dbName);
+            }
+
+            $this->buildDataBaseSchema($connection, (array) $table, $dbName);
+        } else {
+            if ($dir) {
+                $appPath   = $this->app->getBasePath() . $dir . DIRECTORY_SEPARATOR;
+                $namespace = 'app\\' . $dir;
+            } else {
+                $appPath   = $this->app->getBasePath();
+                $namespace = 'app';
+            }
+
+            $path = $appPath . 'model';
+            $list = is_dir($path) ? scandir($path) : [];
+
+            foreach ($list as $file) {
+                if (0 === strpos($file, '.')) {
+                    continue;
+                }
+                $class = '\\' . $namespace . '\\model\\' . pathinfo($file, PATHINFO_FILENAME);
+                $this->buildModelSchema($class);
+            }
+        }
+
+        $output->writeln('<info>Succeed!</info>');
+    }
+
+    protected function buildModelSchema(string $class): void
+    {
+        $reflect = new \ReflectionClass($class);
+        if (!$reflect->isAbstract() && $reflect->isSubclassOf('\think\Model')) {
+            try {
+                /** @var \think\Model $model */
+                $model      = new $class;
+                $connection = $model->db()->getConnection();
+                if ($connection instanceof PDOConnection) {
+                    $table = $model->getTable();
+                    //预读字段信息
+                    $connection->getSchemaInfo($table, true);
+                }
+            } catch (Exception $e) {
+
+            }
+        }
+    }
+
+    protected function buildDataBaseSchema(PDOConnection $connection, array $tables, string $dbName): void
+    {
+        foreach ($tables as $table) {
+            //预读字段信息
+            $connection->getSchemaInfo("{$dbName}.{$table}", true);
+        }
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/input/Argument.php b/vendor/topthink/framework/src/think/console/input/Argument.php
new file mode 100755
index 0000000..86cca36
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/input/Argument.php
@@ -0,0 +1,138 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\console\input;
+
+class Argument
+{
+    // 必传参数
+    const REQUIRED = 1;
+
+    // 可选参数
+    const OPTIONAL = 2;
+
+    // 数组参数
+    const IS_ARRAY = 4;
+
+    /**
+     * 参数名
+     * @var string
+     */
+    private $name;
+
+    /**
+     * 参数类型
+     * @var int
+     */
+    private $mode;
+
+    /**
+     * 参数默认值
+     * @var mixed
+     */
+    private $default;
+
+    /**
+     * 参数描述
+     * @var string
+     */
+    private $description;
+
+    /**
+     * 构造方法
+     * @param string $name        参数名
+     * @param int    $mode        参数类型: self::REQUIRED 或者 self::OPTIONAL
+     * @param string $description 描述
+     * @param mixed  $default     默认值 (仅 self::OPTIONAL 类型有效)
+     * @throws \InvalidArgumentException
+     */
+    public function __construct(string $name, int $mode = null, string $description = '', $default = null)
+    {
+        if (null === $mode) {
+            $mode = self::OPTIONAL;
+        } elseif (!is_int($mode) || $mode > 7 || $mode < 1) {
+            throw new \InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode));
+        }
+
+        $this->name        = $name;
+        $this->mode        = $mode;
+        $this->description = $description;
+
+        $this->setDefault($default);
+    }
+
+    /**
+     * 获取参数名
+     * @return string
+     */
+    public function getName(): string
+    {
+        return $this->name;
+    }
+
+    /**
+     * 是否必须
+     * @return bool
+     */
+    public function isRequired(): bool
+    {
+        return self::REQUIRED === (self::REQUIRED & $this->mode);
+    }
+
+    /**
+     * 该参数是否接受数组
+     * @return bool
+     */
+    public function isArray(): bool
+    {
+        return self::IS_ARRAY === (self::IS_ARRAY & $this->mode);
+    }
+
+    /**
+     * 设置默认值
+     * @param mixed $default 默认值
+     * @throws \LogicException
+     */
+    public function setDefault($default = null): void
+    {
+        if (self::REQUIRED === $this->mode && null !== $default) {
+            throw new \LogicException('Cannot set a default value except for InputArgument::OPTIONAL mode.');
+        }
+
+        if ($this->isArray()) {
+            if (null === $default) {
+                $default = [];
+            } elseif (!is_array($default)) {
+                throw new \LogicException('A default value for an array argument must be an array.');
+            }
+        }
+
+        $this->default = $default;
+    }
+
+    /**
+     * 获取默认值
+     * @return mixed
+     */
+    public function getDefault()
+    {
+        return $this->default;
+    }
+
+    /**
+     * 获取描述
+     * @return string
+     */
+    public function getDescription(): string
+    {
+        return $this->description;
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/input/Definition.php b/vendor/topthink/framework/src/think/console/input/Definition.php
new file mode 100755
index 0000000..ccf02a0
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/input/Definition.php
@@ -0,0 +1,375 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\console\input;
+
+class Definition
+{
+
+    /**
+     * @var Argument[]
+     */
+    private $arguments;
+
+    private $requiredCount;
+    private $hasAnArrayArgument = false;
+    private $hasOptional;
+
+    /**
+     * @var Option[]
+     */
+    private $options;
+    private $shortcuts;
+
+    /**
+     * 构造方法
+     * @param array $definition
+     * @api
+     */
+    public function __construct(array $definition = [])
+    {
+        $this->setDefinition($definition);
+    }
+
+    /**
+     * 设置指令的定义
+     * @param array $definition 定义的数组
+     */
+    public function setDefinition(array $definition): void
+    {
+        $arguments = [];
+        $options   = [];
+        foreach ($definition as $item) {
+            if ($item instanceof Option) {
+                $options[] = $item;
+            } else {
+                $arguments[] = $item;
+            }
+        }
+
+        $this->setArguments($arguments);
+        $this->setOptions($options);
+    }
+
+    /**
+     * 设置参数
+     * @param Argument[] $arguments 参数数组
+     */
+    public function setArguments(array $arguments = []): void
+    {
+        $this->arguments          = [];
+        $this->requiredCount      = 0;
+        $this->hasOptional        = false;
+        $this->hasAnArrayArgument = false;
+        $this->addArguments($arguments);
+    }
+
+    /**
+     * 添加参数
+     * @param Argument[] $arguments 参数数组
+     * @api
+     */
+    public function addArguments(array $arguments = []): void
+    {
+        if (null !== $arguments) {
+            foreach ($arguments as $argument) {
+                $this->addArgument($argument);
+            }
+        }
+    }
+
+    /**
+     * 添加一个参数
+     * @param Argument $argument 参数
+     * @throws \LogicException
+     */
+    public function addArgument(Argument $argument): void
+    {
+        if (isset($this->arguments[$argument->getName()])) {
+            throw new \LogicException(sprintf('An argument with name "%s" already exists.', $argument->getName()));
+        }
+
+        if ($this->hasAnArrayArgument) {
+            throw new \LogicException('Cannot add an argument after an array argument.');
+        }
+
+        if ($argument->isRequired() && $this->hasOptional) {
+            throw new \LogicException('Cannot add a required argument after an optional one.');
+        }
+
+        if ($argument->isArray()) {
+            $this->hasAnArrayArgument = true;
+        }
+
+        if ($argument->isRequired()) {
+            ++$this->requiredCount;
+        } else {
+            $this->hasOptional = true;
+        }
+
+        $this->arguments[$argument->getName()] = $argument;
+    }
+
+    /**
+     * 根据名称或者位置获取参数
+     * @param string|int $name 参数名或者位置
+     * @return Argument 参数
+     * @throws \InvalidArgumentException
+     */
+    public function getArgument($name): Argument
+    {
+        if (!$this->hasArgument($name)) {
+            throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
+        }
+
+        $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments;
+
+        return $arguments[$name];
+    }
+
+    /**
+     * 根据名称或位置检查是否具有某个参数
+     * @param string|int $name 参数名或者位置
+     * @return bool
+     * @api
+     */
+    public function hasArgument($name): bool
+    {
+        $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments;
+
+        return isset($arguments[$name]);
+    }
+
+    /**
+     * 获取所有的参数
+     * @return Argument[] 参数数组
+     */
+    public function getArguments(): array
+    {
+        return $this->arguments;
+    }
+
+    /**
+     * 获取参数数量
+     * @return int
+     */
+    public function getArgumentCount(): int
+    {
+        return $this->hasAnArrayArgument ? PHP_INT_MAX : count($this->arguments);
+    }
+
+    /**
+     * 获取必填的参数的数量
+     * @return int
+     */
+    public function getArgumentRequiredCount(): int
+    {
+        return $this->requiredCount;
+    }
+
+    /**
+     * 获取参数默认值
+     * @return array
+     */
+    public function getArgumentDefaults(): array
+    {
+        $values = [];
+        foreach ($this->arguments as $argument) {
+            $values[$argument->getName()] = $argument->getDefault();
+        }
+
+        return $values;
+    }
+
+    /**
+     * 设置选项
+     * @param Option[] $options 选项数组
+     */
+    public function setOptions(array $options = []): void
+    {
+        $this->options   = [];
+        $this->shortcuts = [];
+        $this->addOptions($options);
+    }
+
+    /**
+     * 添加选项
+     * @param Option[] $options 选项数组
+     * @api
+     */
+    public function addOptions(array $options = []): void
+    {
+        foreach ($options as $option) {
+            $this->addOption($option);
+        }
+    }
+
+    /**
+     * 添加一个选项
+     * @param Option $option 选项
+     * @throws \LogicException
+     * @api
+     */
+    public function addOption(Option $option): void
+    {
+        if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) {
+            throw new \LogicException(sprintf('An option named "%s" already exists.', $option->getName()));
+        }
+
+        if ($option->getShortcut()) {
+            foreach (explode('|', $option->getShortcut()) as $shortcut) {
+                if (isset($this->shortcuts[$shortcut])
+                    && !$option->equals($this->options[$this->shortcuts[$shortcut]])
+                ) {
+                    throw new \LogicException(sprintf('An option with shortcut "%s" already exists.', $shortcut));
+                }
+            }
+        }
+
+        $this->options[$option->getName()] = $option;
+        if ($option->getShortcut()) {
+            foreach (explode('|', $option->getShortcut()) as $shortcut) {
+                $this->shortcuts[$shortcut] = $option->getName();
+            }
+        }
+    }
+
+    /**
+     * 根据名称获取选项
+     * @param string $name 选项名
+     * @return Option
+     * @throws \InvalidArgumentException
+     * @api
+     */
+    public function getOption(string $name): Option
+    {
+        if (!$this->hasOption($name)) {
+            throw new \InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name));
+        }
+
+        return $this->options[$name];
+    }
+
+    /**
+     * 根据名称检查是否有这个选项
+     * @param string $name 选项名
+     * @return bool
+     * @api
+     */
+    public function hasOption(string $name): bool
+    {
+        return isset($this->options[$name]);
+    }
+
+    /**
+     * 获取所有选项
+     * @return Option[]
+     * @api
+     */
+    public function getOptions(): array
+    {
+        return $this->options;
+    }
+
+    /**
+     * 根据名称检查某个选项是否有短名称
+     * @param string $name 短名称
+     * @return bool
+     */
+    public function hasShortcut(string $name): bool
+    {
+        return isset($this->shortcuts[$name]);
+    }
+
+    /**
+     * 根据短名称获取选项
+     * @param string $shortcut 短名称
+     * @return Option
+     */
+    public function getOptionForShortcut(string $shortcut): Option
+    {
+        return $this->getOption($this->shortcutToName($shortcut));
+    }
+
+    /**
+     * 获取所有选项的默认值
+     * @return array
+     */
+    public function getOptionDefaults(): array
+    {
+        $values = [];
+        foreach ($this->options as $option) {
+            $values[$option->getName()] = $option->getDefault();
+        }
+
+        return $values;
+    }
+
+    /**
+     * 根据短名称获取选项名
+     * @param string $shortcut 短名称
+     * @return string
+     * @throws \InvalidArgumentException
+     */
+    private function shortcutToName(string $shortcut): string
+    {
+        if (!isset($this->shortcuts[$shortcut])) {
+            throw new \InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut));
+        }
+
+        return $this->shortcuts[$shortcut];
+    }
+
+    /**
+     * 获取该指令的介绍
+     * @param bool $short 是否简洁介绍
+     * @return string
+     */
+    public function getSynopsis(bool $short = false): string
+    {
+        $elements = [];
+
+        if ($short && $this->getOptions()) {
+            $elements[] = '[options]';
+        } elseif (!$short) {
+            foreach ($this->getOptions() as $option) {
+                $value = '';
+                if ($option->acceptValue()) {
+                    $value = sprintf(' %s%s%s', $option->isValueOptional() ? '[' : '', strtoupper($option->getName()), $option->isValueOptional() ? ']' : '');
+                }
+
+                $shortcut   = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : '';
+                $elements[] = sprintf('[%s--%s%s]', $shortcut, $option->getName(), $value);
+            }
+        }
+
+        if (count($elements) && $this->getArguments()) {
+            $elements[] = '[--]';
+        }
+
+        foreach ($this->getArguments() as $argument) {
+            $element = '<' . $argument->getName() . '>';
+            if (!$argument->isRequired()) {
+                $element = '[' . $element . ']';
+            } elseif ($argument->isArray()) {
+                $element .= ' (' . $element . ')';
+            }
+
+            if ($argument->isArray()) {
+                $element .= '...';
+            }
+
+            $elements[] = $element;
+        }
+
+        return implode(' ', $elements);
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/input/Option.php b/vendor/topthink/framework/src/think/console/input/Option.php
new file mode 100755
index 0000000..d7e6514
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/input/Option.php
@@ -0,0 +1,221 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\console\input;
+
+/**
+ * 命令行选项
+ * @package think\console\input
+ */
+class Option
+{
+    // 无需传值
+    const VALUE_NONE = 1;
+    // 必须传值
+    const VALUE_REQUIRED = 2;
+    // 可选传值
+    const VALUE_OPTIONAL = 4;
+    // 传数组值
+    const VALUE_IS_ARRAY = 8;
+
+    /**
+     * 选项名
+     * @var string
+     */
+    private $name = '';
+
+    /**
+     * 选项短名称
+     * @var string
+     */
+    private $shortcut = '';
+
+    /**
+     * 选项类型
+     * @var int
+     */
+    private $mode;
+
+    /**
+     * 选项默认值
+     * @var mixed
+     */
+    private $default;
+
+    /**
+     * 选项描述
+     * @var string
+     */
+    private $description = '';
+
+    /**
+     * 构造方法
+     * @param string       $name        选项名
+     * @param string|array $shortcut    短名称,多个用|隔开或者使用数组
+     * @param int          $mode        选项类型(可选类型为 self::VALUE_*)
+     * @param string       $description 描述
+     * @param mixed        $default     默认值 (类型为 self::VALUE_REQUIRED 或者 self::VALUE_NONE 的时候必须为null)
+     * @throws \InvalidArgumentException
+     */
+    public function __construct($name, $shortcut = null, $mode = null, $description = '', $default = null)
+    {
+        if (0 === strpos($name, '--')) {
+            $name = substr($name, 2);
+        }
+
+        if (empty($name)) {
+            throw new \InvalidArgumentException('An option name cannot be empty.');
+        }
+
+        if (empty($shortcut)) {
+            $shortcut = '';
+        }
+
+        if ('' !== $shortcut) {
+            if (is_array($shortcut)) {
+                $shortcut = implode('|', $shortcut);
+            }
+            $shortcuts = preg_split('{(\|)-?}', ltrim($shortcut, '-'));
+            $shortcuts = array_filter($shortcuts);
+            $shortcut  = implode('|', $shortcuts);
+
+            if (empty($shortcut)) {
+                throw new \InvalidArgumentException('An option shortcut cannot be empty.');
+            }
+        }
+
+        if (null === $mode) {
+            $mode = self::VALUE_NONE;
+        } elseif (!is_int($mode) || $mode > 15 || $mode < 1) {
+            throw new \InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode));
+        }
+
+        $this->name        = $name;
+        $this->shortcut    = $shortcut;
+        $this->mode        = $mode;
+        $this->description = $description;
+
+        if ($this->isArray() && !$this->acceptValue()) {
+            throw new \InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.');
+        }
+
+        $this->setDefault($default);
+    }
+
+    /**
+     * 获取短名称
+     * @return string
+     */
+    public function getShortcut(): string
+    {
+        return $this->shortcut;
+    }
+
+    /**
+     * 获取选项名
+     * @return string
+     */
+    public function getName(): string
+    {
+        return $this->name;
+    }
+
+    /**
+     * 是否可以设置值
+     * @return bool 类型不是 self::VALUE_NONE 的时候返回true,其他均返回false
+     */
+    public function acceptValue(): bool
+    {
+        return $this->isValueRequired() || $this->isValueOptional();
+    }
+
+    /**
+     * 是否必须
+     * @return bool 类型是 self::VALUE_REQUIRED 的时候返回true,其他均返回false
+     */
+    public function isValueRequired(): bool
+    {
+        return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode);
+    }
+
+    /**
+     * 是否可选
+     * @return bool 类型是 self::VALUE_OPTIONAL 的时候返回true,其他均返回false
+     */
+    public function isValueOptional(): bool
+    {
+        return self::VALUE_OPTIONAL === (self::VALUE_OPTIONAL & $this->mode);
+    }
+
+    /**
+     * 选项值是否接受数组
+     * @return bool 类型是 self::VALUE_IS_ARRAY 的时候返回true,其他均返回false
+     */
+    public function isArray(): bool
+    {
+        return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode);
+    }
+
+    /**
+     * 设置默认值
+     * @param mixed $default 默认值
+     * @throws \LogicException
+     */
+    public function setDefault($default = null)
+    {
+        if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) {
+            throw new \LogicException('Cannot set a default value when using InputOption::VALUE_NONE mode.');
+        }
+
+        if ($this->isArray()) {
+            if (null === $default) {
+                $default = [];
+            } elseif (!is_array($default)) {
+                throw new \LogicException('A default value for an array option must be an array.');
+            }
+        }
+
+        $this->default = $this->acceptValue() ? $default : false;
+    }
+
+    /**
+     * 获取默认值
+     * @return mixed
+     */
+    public function getDefault()
+    {
+        return $this->default;
+    }
+
+    /**
+     * 获取描述文字
+     * @return string
+     */
+    public function getDescription(): string
+    {
+        return $this->description;
+    }
+
+    /**
+     * 检查所给选项是否是当前这个
+     * @param Option $option
+     * @return bool
+     */
+    public function equals(Option $option): bool
+    {
+        return $option->getName() === $this->getName()
+        && $option->getShortcut() === $this->getShortcut()
+        && $option->getDefault() === $this->getDefault()
+        && $option->isArray() === $this->isArray()
+        && $option->isValueRequired() === $this->isValueRequired()
+        && $option->isValueOptional() === $this->isValueOptional();
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/output/Ask.php b/vendor/topthink/framework/src/think/console/output/Ask.php
new file mode 100755
index 0000000..0058d05
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/output/Ask.php
@@ -0,0 +1,336 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\console\output;
+
+use think\console\Input;
+use think\console\Output;
+use think\console\output\question\Choice;
+use think\console\output\question\Confirmation;
+
+class Ask
+{
+    private static $stty;
+
+    private static $shell;
+
+    /** @var  Input */
+    protected $input;
+
+    /** @var  Output */
+    protected $output;
+
+    /** @var  Question */
+    protected $question;
+
+    public function __construct(Input $input, Output $output, Question $question)
+    {
+        $this->input    = $input;
+        $this->output   = $output;
+        $this->question = $question;
+    }
+
+    public function run()
+    {
+        if (!$this->input->isInteractive()) {
+            return $this->question->getDefault();
+        }
+
+        if (!$this->question->getValidator()) {
+            return $this->doAsk();
+        }
+
+        $that = $this;
+
+        $interviewer = function () use ($that) {
+            return $that->doAsk();
+        };
+
+        return $this->validateAttempts($interviewer);
+    }
+
+    protected function doAsk()
+    {
+        $this->writePrompt();
+
+        $inputStream  = STDIN;
+        $autocomplete = $this->question->getAutocompleterValues();
+
+        if (null === $autocomplete || !$this->hasSttyAvailable()) {
+            $ret = false;
+            if ($this->question->isHidden()) {
+                try {
+                    $ret = trim($this->getHiddenResponse($inputStream));
+                } catch (\RuntimeException $e) {
+                    if (!$this->question->isHiddenFallback()) {
+                        throw $e;
+                    }
+                }
+            }
+
+            if (false === $ret) {
+                $ret = fgets($inputStream, 4096);
+                if (false === $ret) {
+                    throw new \RuntimeException('Aborted');
+                }
+                $ret = trim($ret);
+            }
+        } else {
+            $ret = trim($this->autocomplete($inputStream));
+        }
+
+        $ret = strlen($ret) > 0 ? $ret : $this->question->getDefault();
+
+        if ($normalizer = $this->question->getNormalizer()) {
+            return $normalizer($ret);
+        }
+
+        return $ret;
+    }
+
+    private function autocomplete($inputStream)
+    {
+        $autocomplete = $this->question->getAutocompleterValues();
+        $ret          = '';
+
+        $i          = 0;
+        $ofs        = -1;
+        $matches    = $autocomplete;
+        $numMatches = count($matches);
+
+        $sttyMode = shell_exec('stty -g');
+
+        shell_exec('stty -icanon -echo');
+
+        while (!feof($inputStream)) {
+            $c = fread($inputStream, 1);
+
+            if ("\177" === $c) {
+                if (0 === $numMatches && 0 !== $i) {
+                    --$i;
+                    $this->output->write("\033[1D");
+                }
+
+                if ($i === 0) {
+                    $ofs        = -1;
+                    $matches    = $autocomplete;
+                    $numMatches = count($matches);
+                } else {
+                    $numMatches = 0;
+                }
+
+                $ret = substr($ret, 0, $i);
+            } elseif ("\033" === $c) {
+                $c .= fread($inputStream, 2);
+
+                if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) {
+                    if ('A' === $c[2] && -1 === $ofs) {
+                        $ofs = 0;
+                    }
+
+                    if (0 === $numMatches) {
+                        continue;
+                    }
+
+                    $ofs += ('A' === $c[2]) ? -1 : 1;
+                    $ofs = ($numMatches + $ofs) % $numMatches;
+                }
+            } elseif (ord($c) < 32) {
+                if ("\t" === $c || "\n" === $c) {
+                    if ($numMatches > 0 && -1 !== $ofs) {
+                        $ret = $matches[$ofs];
+                        $this->output->write(substr($ret, $i));
+                        $i = strlen($ret);
+                    }
+
+                    if ("\n" === $c) {
+                        $this->output->write($c);
+                        break;
+                    }
+
+                    $numMatches = 0;
+                }
+
+                continue;
+            } else {
+                $this->output->write($c);
+                $ret .= $c;
+                ++$i;
+
+                $numMatches = 0;
+                $ofs        = 0;
+
+                foreach ($autocomplete as $value) {
+                    if (0 === strpos($value, $ret) && $i !== strlen($value)) {
+                        $matches[$numMatches++] = $value;
+                    }
+                }
+            }
+
+            $this->output->write("\033[K");
+
+            if ($numMatches > 0 && -1 !== $ofs) {
+                $this->output->write("\0337");
+                $this->output->highlight(substr($matches[$ofs], $i));
+                $this->output->write("\0338");
+            }
+        }
+
+        shell_exec(sprintf('stty %s', $sttyMode));
+
+        return $ret;
+    }
+
+    protected function getHiddenResponse($inputStream)
+    {
+        if ('\\' === DIRECTORY_SEPARATOR) {
+            $exe = __DIR__ . '/../bin/hiddeninput.exe';
+
+            $value = rtrim(shell_exec($exe));
+            $this->output->writeln('');
+
+            return $value;
+        }
+
+        if ($this->hasSttyAvailable()) {
+            $sttyMode = shell_exec('stty -g');
+
+            shell_exec('stty -echo');
+            $value = fgets($inputStream, 4096);
+            shell_exec(sprintf('stty %s', $sttyMode));
+
+            if (false === $value) {
+                throw new \RuntimeException('Aborted');
+            }
+
+            $value = trim($value);
+            $this->output->writeln('');
+
+            return $value;
+        }
+
+        if (false !== $shell = $this->getShell()) {
+            $readCmd = $shell === 'csh' ? 'set mypassword = $<' : 'read -r mypassword';
+            $command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd);
+            $value   = rtrim(shell_exec($command));
+            $this->output->writeln('');
+
+            return $value;
+        }
+
+        throw new \RuntimeException('Unable to hide the response.');
+    }
+
+    protected function validateAttempts($interviewer)
+    {
+        /** @var \Exception $error */
+        $error    = null;
+        $attempts = $this->question->getMaxAttempts();
+        while (null === $attempts || $attempts--) {
+            if (null !== $error) {
+                $this->output->error($error->getMessage());
+            }
+
+            try {
+                return call_user_func($this->question->getValidator(), $interviewer());
+            } catch (\Exception $error) {
+            }
+        }
+
+        throw $error;
+    }
+
+    /**
+     * 显示问题的提示信息
+     */
+    protected function writePrompt()
+    {
+        $text    = $this->question->getQuestion();
+        $default = $this->question->getDefault();
+
+        switch (true) {
+            case null === $default:
+                $text = sprintf(' <info>%s</info>:', $text);
+
+                break;
+
+            case $this->question instanceof Confirmation:
+                $text = sprintf(' <info>%s (yes/no)</info> [<comment>%s</comment>]:', $text, $default ? 'yes' : 'no');
+
+                break;
+
+            case $this->question instanceof Choice && $this->question->isMultiselect():
+                $choices = $this->question->getChoices();
+                $default = explode(',', $default);
+
+                foreach ($default as $key => $value) {
+                    $default[$key] = $choices[trim($value)];
+                }
+
+                $text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, implode(', ', $default));
+
+                break;
+
+            case $this->question instanceof Choice:
+                $choices = $this->question->getChoices();
+                $text    = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, $choices[$default]);
+
+                break;
+
+            default:
+                $text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, $default);
+        }
+
+        $this->output->writeln($text);
+
+        if ($this->question instanceof Choice) {
+            $width = max(array_map('strlen', array_keys($this->question->getChoices())));
+
+            foreach ($this->question->getChoices() as $key => $value) {
+                $this->output->writeln(sprintf("  [<comment>%-{$width}s</comment>] %s", $key, $value));
+            }
+        }
+
+        $this->output->write(' > ');
+    }
+
+    private function getShell()
+    {
+        if (null !== self::$shell) {
+            return self::$shell;
+        }
+
+        self::$shell = false;
+
+        if (file_exists('/usr/bin/env')) {
+            $test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null";
+            foreach (['bash', 'zsh', 'ksh', 'csh'] as $sh) {
+                if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) {
+                    self::$shell = $sh;
+                    break;
+                }
+            }
+        }
+
+        return self::$shell;
+    }
+
+    private function hasSttyAvailable()
+    {
+        if (null !== self::$stty) {
+            return self::$stty;
+        }
+
+        exec('stty 2>&1', $output, $exitcode);
+
+        return self::$stty = $exitcode === 0;
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/output/Descriptor.php b/vendor/topthink/framework/src/think/console/output/Descriptor.php
new file mode 100755
index 0000000..84d487c
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/output/Descriptor.php
@@ -0,0 +1,323 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\console\output;
+
+use think\Console;
+use think\console\Command;
+use think\console\input\Argument as InputArgument;
+use think\console\input\Definition as InputDefinition;
+use think\console\input\Option as InputOption;
+use think\console\Output;
+use think\console\output\descriptor\Console as ConsoleDescription;
+
+class Descriptor
+{
+
+    /**
+     * @var Output
+     */
+    protected $output;
+
+    /**
+     * {@inheritdoc}
+     */
+    public function describe(Output $output, $object, array $options = [])
+    {
+        $this->output = $output;
+
+        switch (true) {
+            case $object instanceof InputArgument:
+                $this->describeInputArgument($object, $options);
+                break;
+            case $object instanceof InputOption:
+                $this->describeInputOption($object, $options);
+                break;
+            case $object instanceof InputDefinition:
+                $this->describeInputDefinition($object, $options);
+                break;
+            case $object instanceof Command:
+                $this->describeCommand($object, $options);
+                break;
+            case $object instanceof Console:
+                $this->describeConsole($object, $options);
+                break;
+            default:
+                throw new \InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_class($object)));
+        }
+    }
+
+    /**
+     * 输出内容
+     * @param string $content
+     * @param bool   $decorated
+     */
+    protected function write($content, $decorated = false)
+    {
+        $this->output->write($content, false, $decorated ? Output::OUTPUT_NORMAL : Output::OUTPUT_RAW);
+    }
+
+    /**
+     * 描述参数
+     * @param InputArgument $argument
+     * @param array         $options
+     * @return string|mixed
+     */
+    protected function describeInputArgument(InputArgument $argument, array $options = [])
+    {
+        if (null !== $argument->getDefault()
+            && (!is_array($argument->getDefault())
+                || count($argument->getDefault()))
+        ) {
+            $default = sprintf('<comment> [default: %s]</comment>', $this->formatDefaultValue($argument->getDefault()));
+        } else {
+            $default = '';
+        }
+
+        $totalWidth   = $options['total_width'] ?? strlen($argument->getName());
+        $spacingWidth = $totalWidth - strlen($argument->getName()) + 2;
+
+        $this->writeText(sprintf("  <info>%s</info>%s%s%s", $argument->getName(), str_repeat(' ', $spacingWidth), // + 17 = 2 spaces + <info> + </info> + 2 spaces
+            preg_replace('/\s*\R\s*/', PHP_EOL . str_repeat(' ', $totalWidth + 17), $argument->getDescription()), $default), $options);
+    }
+
+    /**
+     * 描述选项
+     * @param InputOption $option
+     * @param array       $options
+     * @return string|mixed
+     */
+    protected function describeInputOption(InputOption $option, array $options = [])
+    {
+        if ($option->acceptValue() && null !== $option->getDefault()
+            && (!is_array($option->getDefault())
+                || count($option->getDefault()))
+        ) {
+            $default = sprintf('<comment> [default: %s]</comment>', $this->formatDefaultValue($option->getDefault()));
+        } else {
+            $default = '';
+        }
+
+        $value = '';
+        if ($option->acceptValue()) {
+            $value = '=' . strtoupper($option->getName());
+
+            if ($option->isValueOptional()) {
+                $value = '[' . $value . ']';
+            }
+        }
+
+        $totalWidth = $options['total_width'] ?? $this->calculateTotalWidthForOptions([$option]);
+        $synopsis   = sprintf('%s%s', $option->getShortcut() ? sprintf('-%s, ', $option->getShortcut()) : '    ', sprintf('--%s%s', $option->getName(), $value));
+
+        $spacingWidth = $totalWidth - strlen($synopsis) + 2;
+
+        $this->writeText(sprintf("  <info>%s</info>%s%s%s%s", $synopsis, str_repeat(' ', $spacingWidth), // + 17 = 2 spaces + <info> + </info> + 2 spaces
+            preg_replace('/\s*\R\s*/', "\n" . str_repeat(' ', $totalWidth + 17), $option->getDescription()), $default, $option->isArray() ? '<comment> (multiple values allowed)</comment>' : ''), $options);
+    }
+
+    /**
+     * 描述输入
+     * @param InputDefinition $definition
+     * @param array           $options
+     * @return string|mixed
+     */
+    protected function describeInputDefinition(InputDefinition $definition, array $options = [])
+    {
+        $totalWidth = $this->calculateTotalWidthForOptions($definition->getOptions());
+        foreach ($definition->getArguments() as $argument) {
+            $totalWidth = max($totalWidth, strlen($argument->getName()));
+        }
+
+        if ($definition->getArguments()) {
+            $this->writeText('<comment>Arguments:</comment>', $options);
+            $this->writeText("\n");
+            foreach ($definition->getArguments() as $argument) {
+                $this->describeInputArgument($argument, array_merge($options, ['total_width' => $totalWidth]));
+                $this->writeText("\n");
+            }
+        }
+
+        if ($definition->getArguments() && $definition->getOptions()) {
+            $this->writeText("\n");
+        }
+
+        if ($definition->getOptions()) {
+            $laterOptions = [];
+
+            $this->writeText('<comment>Options:</comment>', $options);
+            foreach ($definition->getOptions() as $option) {
+                if (strlen($option->getShortcut()) > 1) {
+                    $laterOptions[] = $option;
+                    continue;
+                }
+                $this->writeText("\n");
+                $this->describeInputOption($option, array_merge($options, ['total_width' => $totalWidth]));
+            }
+            foreach ($laterOptions as $option) {
+                $this->writeText("\n");
+                $this->describeInputOption($option, array_merge($options, ['total_width' => $totalWidth]));
+            }
+        }
+    }
+
+    /**
+     * 描述指令
+     * @param Command $command
+     * @param array   $options
+     * @return string|mixed
+     */
+    protected function describeCommand(Command $command, array $options = [])
+    {
+        $command->getSynopsis(true);
+        $command->getSynopsis(false);
+        $command->mergeConsoleDefinition(false);
+
+        $this->writeText('<comment>Usage:</comment>', $options);
+        foreach (array_merge([$command->getSynopsis(true)], $command->getAliases(), $command->getUsages()) as $usage) {
+            $this->writeText("\n");
+            $this->writeText('  ' . $usage, $options);
+        }
+        $this->writeText("\n");
+
+        $definition = $command->getNativeDefinition();
+        if ($definition->getOptions() || $definition->getArguments()) {
+            $this->writeText("\n");
+            $this->describeInputDefinition($definition, $options);
+            $this->writeText("\n");
+        }
+
+        if ($help = $command->getProcessedHelp()) {
+            $this->writeText("\n");
+            $this->writeText('<comment>Help:</comment>', $options);
+            $this->writeText("\n");
+            $this->writeText(' ' . str_replace("\n", "\n ", $help), $options);
+            $this->writeText("\n");
+        }
+    }
+
+    /**
+     * 描述控制台
+     * @param Console $console
+     * @param array   $options
+     * @return string|mixed
+     */
+    protected function describeConsole(Console $console, array $options = [])
+    {
+        $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null;
+        $description        = new ConsoleDescription($console, $describedNamespace);
+
+        if (isset($options['raw_text']) && $options['raw_text']) {
+            $width = $this->getColumnWidth($description->getNamespaces());
+
+            foreach ($description->getCommands() as $command) {
+                $this->writeText(sprintf("%-{$width}s %s", $command->getName(), $command->getDescription()), $options);
+                $this->writeText("\n");
+            }
+        } else {
+            if ('' != $help = $console->getHelp()) {
+                $this->writeText("$help\n\n", $options);
+            }
+
+            $this->writeText("<comment>Usage:</comment>\n", $options);
+            $this->writeText("  command [options] [arguments]\n\n", $options);
+
+            $this->describeInputDefinition(new InputDefinition($console->getDefinition()->getOptions()), $options);
+
+            $this->writeText("\n");
+            $this->writeText("\n");
+
+            $width = $this->getColumnWidth($description->getNamespaces());
+
+            if ($describedNamespace) {
+                $this->writeText(sprintf('<comment>Available commands for the "%s" namespace:</comment>', $describedNamespace), $options);
+            } else {
+                $this->writeText('<comment>Available commands:</comment>', $options);
+            }
+
+            // add commands by namespace
+            foreach ($description->getNamespaces() as $namespace) {
+                if (!$describedNamespace && ConsoleDescription::GLOBAL_NAMESPACE !== $namespace['id']) {
+                    $this->writeText("\n");
+                    $this->writeText(' <comment>' . $namespace['id'] . '</comment>', $options);
+                }
+
+                foreach ($namespace['commands'] as $name) {
+                    $this->writeText("\n");
+                    $spacingWidth = $width - strlen($name);
+                    $this->writeText(sprintf("  <info>%s</info>%s%s", $name, str_repeat(' ', $spacingWidth), $description->getCommand($name)
+                            ->getDescription()), $options);
+                }
+            }
+
+            $this->writeText("\n");
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    private function writeText($content, array $options = [])
+    {
+        $this->write(isset($options['raw_text'])
+            && $options['raw_text'] ? strip_tags($content) : $content, isset($options['raw_output']) ? !$options['raw_output'] : true);
+    }
+
+    /**
+     * 格式化
+     * @param mixed $default
+     * @return string
+     */
+    private function formatDefaultValue($default)
+    {
+        return json_encode($default, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
+    }
+
+    /**
+     * @param Namespaces[] $namespaces
+     * @return int
+     */
+    private function getColumnWidth(array $namespaces)
+    {
+        $width = 0;
+        foreach ($namespaces as $namespace) {
+            foreach ($namespace['commands'] as $name) {
+                if (strlen($name) > $width) {
+                    $width = strlen($name);
+                }
+            }
+        }
+
+        return $width + 2;
+    }
+
+    /**
+     * @param InputOption[] $options
+     * @return int
+     */
+    private function calculateTotalWidthForOptions($options)
+    {
+        $totalWidth = 0;
+        foreach ($options as $option) {
+            $nameLength = 4 + strlen($option->getName()) + 2; // - + shortcut + , + whitespace + name + --
+
+            if ($option->acceptValue()) {
+                $valueLength = 1 + strlen($option->getName()); // = + value
+                $valueLength += $option->isValueOptional() ? 2 : 0; // [ + ]
+
+                $nameLength += $valueLength;
+            }
+            $totalWidth = max($totalWidth, $nameLength);
+        }
+
+        return $totalWidth;
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/output/Formatter.php b/vendor/topthink/framework/src/think/console/output/Formatter.php
new file mode 100755
index 0000000..1b97ca3
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/output/Formatter.php
@@ -0,0 +1,198 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+namespace think\console\output;
+
+use think\console\output\formatter\Stack as StyleStack;
+use think\console\output\formatter\Style;
+
+class Formatter
+{
+
+    private $decorated = false;
+    private $styles    = [];
+    private $styleStack;
+
+    /**
+     * 转义
+     * @param string $text
+     * @return string
+     */
+    public static function escape($text)
+    {
+        return preg_replace('/([^\\\\]?)</is', '$1\\<', $text);
+    }
+
+    /**
+     * 初始化命令行输出格式
+     */
+    public function __construct()
+    {
+        $this->setStyle('error', new Style('white', 'red'));
+        $this->setStyle('info', new Style('green'));
+        $this->setStyle('comment', new Style('yellow'));
+        $this->setStyle('question', new Style('black', 'cyan'));
+        $this->setStyle('highlight', new Style('red'));
+        $this->setStyle('warning', new Style('black', 'yellow'));
+
+        $this->styleStack = new StyleStack();
+    }
+
+    /**
+     * 设置外观标识
+     * @param bool $decorated 是否美化文字
+     */
+    public function setDecorated($decorated)
+    {
+        $this->decorated = (bool) $decorated;
+    }
+
+    /**
+     * 获取外观标识
+     * @return bool
+     */
+    public function isDecorated()
+    {
+        return $this->decorated;
+    }
+
+    /**
+     * 添加一个新样式
+     * @param string $name  样式名
+     * @param Style  $style 样式实例
+     */
+    public function setStyle($name, Style $style)
+    {
+        $this->styles[strtolower($name)] = $style;
+    }
+
+    /**
+     * 是否有这个样式
+     * @param string $name
+     * @return bool
+     */
+    public function hasStyle($name)
+    {
+        return isset($this->styles[strtolower($name)]);
+    }
+
+    /**
+     * 获取样式
+     * @param string $name
+     * @return Style
+     * @throws \InvalidArgumentException
+     */
+    public function getStyle($name)
+    {
+        if (!$this->hasStyle($name)) {
+            throw new \InvalidArgumentException(sprintf('Undefined style: %s', $name));
+        }
+
+        return $this->styles[strtolower($name)];
+    }
+
+    /**
+     * 使用所给的样式格式化文字
+     * @param string $message 文字
+     * @return string
+     */
+    public function format($message)
+    {
+        $offset   = 0;
+        $output   = '';
+        $tagRegex = '[a-z][a-z0-9_=;-]*';
+        preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#isx", $message, $matches, PREG_OFFSET_CAPTURE);
+        foreach ($matches[0] as $i => $match) {
+            $pos  = $match[1];
+            $text = $match[0];
+
+            if (0 != $pos && '\\' == $message[$pos - 1]) {
+                continue;
+            }
+
+            $output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset));
+            $offset = $pos + strlen($text);
+
+            if ($open = '/' != $text[1]) {
+                $tag = $matches[1][$i][0];
+            } else {
+                $tag = $matches[3][$i][0] ?? '';
+            }
+
+            if (!$open && !$tag) {
+                // </>
+                $this->styleStack->pop();
+            } elseif (false === $style = $this->createStyleFromString(strtolower($tag))) {
+                $output .= $this->applyCurrentStyle($text);
+            } elseif ($open) {
+                $this->styleStack->push($style);
+            } else {
+                $this->styleStack->pop($style);
+            }
+        }
+
+        $output .= $this->applyCurrentStyle(substr($message, $offset));
+
+        return str_replace('\\<', '<', $output);
+    }
+
+    /**
+     * @return StyleStack
+     */
+    public function getStyleStack()
+    {
+        return $this->styleStack;
+    }
+
+    /**
+     * 根据字符串创建新的样式实例
+     * @param string $string
+     * @return Style|bool
+     */
+    private function createStyleFromString($string)
+    {
+        if (isset($this->styles[$string])) {
+            return $this->styles[$string];
+        }
+
+        if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', strtolower($string), $matches, PREG_SET_ORDER)) {
+            return false;
+        }
+
+        $style = new Style();
+        foreach ($matches as $match) {
+            array_shift($match);
+
+            if ('fg' == $match[0]) {
+                $style->setForeground($match[1]);
+            } elseif ('bg' == $match[0]) {
+                $style->setBackground($match[1]);
+            } else {
+                try {
+                    $style->setOption($match[1]);
+                } catch (\InvalidArgumentException $e) {
+                    return false;
+                }
+            }
+        }
+
+        return $style;
+    }
+
+    /**
+     * 从堆栈应用样式到文字
+     * @param string $text 文字
+     * @return string
+     */
+    private function applyCurrentStyle($text)
+    {
+        return $this->isDecorated() && strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text;
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/output/Question.php b/vendor/topthink/framework/src/think/console/output/Question.php
new file mode 100755
index 0000000..03975f2
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/output/Question.php
@@ -0,0 +1,211 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\console\output;
+
+class Question
+{
+
+    private $question;
+    private $attempts;
+    private $hidden         = false;
+    private $hiddenFallback = true;
+    private $autocompleterValues;
+    private $validator;
+    private $default;
+    private $normalizer;
+
+    /**
+     * 构造方法
+     * @param string $question 问题
+     * @param mixed  $default  默认答案
+     */
+    public function __construct($question, $default = null)
+    {
+        $this->question = $question;
+        $this->default  = $default;
+    }
+
+    /**
+     * 获取问题
+     * @return string
+     */
+    public function getQuestion()
+    {
+        return $this->question;
+    }
+
+    /**
+     * 获取默认答案
+     * @return mixed
+     */
+    public function getDefault()
+    {
+        return $this->default;
+    }
+
+    /**
+     * 是否隐藏答案
+     * @return bool
+     */
+    public function isHidden()
+    {
+        return $this->hidden;
+    }
+
+    /**
+     * 隐藏答案
+     * @param bool $hidden
+     * @return Question
+     */
+    public function setHidden($hidden)
+    {
+        if ($this->autocompleterValues) {
+            throw new \LogicException('A hidden question cannot use the autocompleter.');
+        }
+
+        $this->hidden = (bool) $hidden;
+
+        return $this;
+    }
+
+    /**
+     * 不能被隐藏是否撤销
+     * @return bool
+     */
+    public function isHiddenFallback()
+    {
+        return $this->hiddenFallback;
+    }
+
+    /**
+     * 设置不能被隐藏的时候的操作
+     * @param bool $fallback
+     * @return Question
+     */
+    public function setHiddenFallback($fallback)
+    {
+        $this->hiddenFallback = (bool) $fallback;
+
+        return $this;
+    }
+
+    /**
+     * 获取自动完成
+     * @return null|array|\Traversable
+     */
+    public function getAutocompleterValues()
+    {
+        return $this->autocompleterValues;
+    }
+
+    /**
+     * 设置自动完成的值
+     * @param null|array|\Traversable $values
+     * @return Question
+     * @throws \InvalidArgumentException
+     * @throws \LogicException
+     */
+    public function setAutocompleterValues($values)
+    {
+        if (is_array($values) && $this->isAssoc($values)) {
+            $values = array_merge(array_keys($values), array_values($values));
+        }
+
+        if (null !== $values && !is_array($values)) {
+            if (!$values instanceof \Traversable || $values instanceof \Countable) {
+                throw new \InvalidArgumentException('Autocompleter values can be either an array, `null` or an object implementing both `Countable` and `Traversable` interfaces.');
+            }
+        }
+
+        if ($this->hidden) {
+            throw new \LogicException('A hidden question cannot use the autocompleter.');
+        }
+
+        $this->autocompleterValues = $values;
+
+        return $this;
+    }
+
+    /**
+     * 设置答案的验证器
+     * @param null|callable $validator
+     * @return Question The current instance
+     */
+    public function setValidator($validator)
+    {
+        $this->validator = $validator;
+
+        return $this;
+    }
+
+    /**
+     * 获取验证器
+     * @return null|callable
+     */
+    public function getValidator()
+    {
+        return $this->validator;
+    }
+
+    /**
+     * 设置最大重试次数
+     * @param null|int $attempts
+     * @return Question
+     * @throws \InvalidArgumentException
+     */
+    public function setMaxAttempts($attempts)
+    {
+        if (null !== $attempts && $attempts < 1) {
+            throw new \InvalidArgumentException('Maximum number of attempts must be a positive value.');
+        }
+
+        $this->attempts = $attempts;
+
+        return $this;
+    }
+
+    /**
+     * 获取最大重试次数
+     * @return null|int
+     */
+    public function getMaxAttempts()
+    {
+        return $this->attempts;
+    }
+
+    /**
+     * 设置响应的回调
+     * @param string|\Closure $normalizer
+     * @return Question
+     */
+    public function setNormalizer($normalizer)
+    {
+        $this->normalizer = $normalizer;
+
+        return $this;
+    }
+
+    /**
+     * 获取响应回调
+     * The normalizer can ba a callable (a string), a closure or a class implementing __invoke.
+     * @return string|\Closure
+     */
+    public function getNormalizer()
+    {
+        return $this->normalizer;
+    }
+
+    protected function isAssoc($array)
+    {
+        return (bool) count(array_filter(array_keys($array), 'is_string'));
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/output/descriptor/Console.php b/vendor/topthink/framework/src/think/console/output/descriptor/Console.php
new file mode 100755
index 0000000..ff9f464
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/output/descriptor/Console.php
@@ -0,0 +1,153 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\console\output\descriptor;
+
+use think\Console as ThinkConsole;
+use think\console\Command;
+
+class Console
+{
+
+    const GLOBAL_NAMESPACE = '_global';
+
+    /**
+     * @var ThinkConsole
+     */
+    private $console;
+
+    /**
+     * @var null|string
+     */
+    private $namespace;
+
+    /**
+     * @var array
+     */
+    private $namespaces;
+
+    /**
+     * @var Command[]
+     */
+    private $commands;
+
+    /**
+     * @var Command[]
+     */
+    private $aliases;
+
+    /**
+     * 构造方法
+     * @param ThinkConsole $console
+     * @param string|null  $namespace
+     */
+    public function __construct(ThinkConsole $console, $namespace = null)
+    {
+        $this->console   = $console;
+        $this->namespace = $namespace;
+    }
+
+    /**
+     * @return array
+     */
+    public function getNamespaces(): array
+    {
+        if (null === $this->namespaces) {
+            $this->inspectConsole();
+        }
+
+        return $this->namespaces;
+    }
+
+    /**
+     * @return Command[]
+     */
+    public function getCommands(): array
+    {
+        if (null === $this->commands) {
+            $this->inspectConsole();
+        }
+
+        return $this->commands;
+    }
+
+    /**
+     * @param string $name
+     * @return Command
+     * @throws \InvalidArgumentException
+     */
+    public function getCommand(string $name): Command
+    {
+        if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) {
+            throw new \InvalidArgumentException(sprintf('Command %s does not exist.', $name));
+        }
+
+        return $this->commands[$name] ?? $this->aliases[$name];
+    }
+
+    private function inspectConsole(): void
+    {
+        $this->commands   = [];
+        $this->namespaces = [];
+
+        $all = $this->console->all($this->namespace ? $this->console->findNamespace($this->namespace) : null);
+        foreach ($this->sortCommands($all) as $namespace => $commands) {
+            $names = [];
+
+            /** @var Command $command */
+            foreach ($commands as $name => $command) {
+                if (is_string($command)) {
+                    $command = new $command();
+                }
+
+                if (!$command->getName()) {
+                    continue;
+                }
+
+                if ($command->getName() === $name) {
+                    $this->commands[$name] = $command;
+                } else {
+                    $this->aliases[$name] = $command;
+                }
+
+                $names[] = $name;
+            }
+
+            $this->namespaces[$namespace] = ['id' => $namespace, 'commands' => $names];
+        }
+    }
+
+    /**
+     * @param array $commands
+     * @return array
+     */
+    private function sortCommands(array $commands): array
+    {
+        $namespacedCommands = [];
+        foreach ($commands as $name => $command) {
+            $key = $this->console->extractNamespace($name, 1);
+            if (!$key) {
+                $key = self::GLOBAL_NAMESPACE;
+            }
+
+            $namespacedCommands[$key][$name] = $command;
+        }
+        ksort($namespacedCommands);
+
+        foreach ($namespacedCommands as &$commandsSet) {
+            ksort($commandsSet);
+        }
+        // unset reference to keep scope clear
+        unset($commandsSet);
+
+        return $namespacedCommands;
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/output/driver/Buffer.php b/vendor/topthink/framework/src/think/console/output/driver/Buffer.php
new file mode 100755
index 0000000..576f31a
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/output/driver/Buffer.php
@@ -0,0 +1,52 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\console\output\driver;
+
+use think\console\Output;
+
+class Buffer
+{
+    /**
+     * @var string
+     */
+    private $buffer = '';
+
+    public function __construct(Output $output)
+    {
+        // do nothing
+    }
+
+    public function fetch()
+    {
+        $content      = $this->buffer;
+        $this->buffer = '';
+        return $content;
+    }
+
+    public function write($messages, bool $newline = false, int $options = 0)
+    {
+        $messages = (array) $messages;
+
+        foreach ($messages as $message) {
+            $this->buffer .= $message;
+        }
+        if ($newline) {
+            $this->buffer .= "\n";
+        }
+    }
+
+    public function renderException(\Throwable $e)
+    {
+        // do nothing
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/console/output/driver/Console.php b/vendor/topthink/framework/src/think/console/output/driver/Console.php
new file mode 100755
index 0000000..3061f55
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/output/driver/Console.php
@@ -0,0 +1,369 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\console\output\driver;
+
+use think\console\Output;
+use think\console\output\Formatter;
+
+class Console
+{
+
+    /** @var  Resource */
+    private $stdout;
+
+    /** @var  Formatter */
+    private $formatter;
+
+    private $terminalDimensions;
+
+    /** @var  Output */
+    private $output;
+
+    public function __construct(Output $output)
+    {
+        $this->output    = $output;
+        $this->formatter = new Formatter();
+        $this->stdout    = $this->openOutputStream();
+        $decorated       = $this->hasColorSupport($this->stdout);
+        $this->formatter->setDecorated($decorated);
+    }
+
+    public function setDecorated($decorated)
+    {
+        $this->formatter->setDecorated($decorated);
+    }
+
+    public function write($messages, bool $newline = false, int $type = 0, $stream = null)
+    {
+        if (Output::VERBOSITY_QUIET === $this->output->getVerbosity()) {
+            return;
+        }
+
+        $messages = (array) $messages;
+
+        foreach ($messages as $message) {
+            switch ($type) {
+                case Output::OUTPUT_NORMAL:
+                    $message = $this->formatter->format($message);
+                    break;
+                case Output::OUTPUT_RAW:
+                    break;
+                case Output::OUTPUT_PLAIN:
+                    $message = strip_tags($this->formatter->format($message));
+                    break;
+                default:
+                    throw new \InvalidArgumentException(sprintf('Unknown output type given (%s)', $type));
+            }
+
+            $this->doWrite($message, $newline, $stream);
+        }
+    }
+
+    public function renderException(\Throwable $e)
+    {
+        $stderr    = $this->openErrorStream();
+        $decorated = $this->hasColorSupport($stderr);
+        $this->formatter->setDecorated($decorated);
+
+        do {
+            $title = sprintf('  [%s]  ', get_class($e));
+
+            $len = $this->stringWidth($title);
+
+            $width = $this->getTerminalWidth() ? $this->getTerminalWidth() - 1 : PHP_INT_MAX;
+
+            if (defined('HHVM_VERSION') && $width > 1 << 31) {
+                $width = 1 << 31;
+            }
+            $lines = [];
+            foreach (preg_split('/\r?\n/', $e->getMessage()) as $line) {
+                foreach ($this->splitStringByWidth($line, $width - 4) as $line) {
+
+                    $lineLength = $this->stringWidth(preg_replace('/\[[^m]*m/', '', $line)) + 4;
+                    $lines[]    = [$line, $lineLength];
+
+                    $len = max($lineLength, $len);
+                }
+            }
+
+            $messages   = ['', ''];
+            $messages[] = $emptyLine = sprintf('<error>%s</error>', str_repeat(' ', $len));
+            $messages[] = sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - $this->stringWidth($title))));
+            foreach ($lines as $line) {
+                $messages[] = sprintf('<error>  %s  %s</error>', $line[0], str_repeat(' ', $len - $line[1]));
+            }
+            $messages[] = $emptyLine;
+            $messages[] = '';
+            $messages[] = '';
+
+            $this->write($messages, true, Output::OUTPUT_NORMAL, $stderr);
+
+            if (Output::VERBOSITY_VERBOSE <= $this->output->getVerbosity()) {
+                $this->write('<comment>Exception trace:</comment>', true, Output::OUTPUT_NORMAL, $stderr);
+
+                // exception related properties
+                $trace = $e->getTrace();
+                array_unshift($trace, [
+                    'function' => '',
+                    'file'     => $e->getFile() !== null ? $e->getFile() : 'n/a',
+                    'line'     => $e->getLine() !== null ? $e->getLine() : 'n/a',
+                    'args'     => [],
+                ]);
+
+                for ($i = 0, $count = count($trace); $i < $count; ++$i) {
+                    $class    = isset($trace[$i]['class']) ? $trace[$i]['class'] : '';
+                    $type     = isset($trace[$i]['type']) ? $trace[$i]['type'] : '';
+                    $function = $trace[$i]['function'];
+                    $file     = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a';
+                    $line     = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a';
+
+                    $this->write(sprintf(' %s%s%s() at <info>%s:%s</info>', $class, $type, $function, $file, $line), true, Output::OUTPUT_NORMAL, $stderr);
+                }
+
+                $this->write('', true, Output::OUTPUT_NORMAL, $stderr);
+                $this->write('', true, Output::OUTPUT_NORMAL, $stderr);
+            }
+        } while ($e = $e->getPrevious());
+
+    }
+
+    /**
+     * 获取终端宽度
+     * @return int|null
+     */
+    protected function getTerminalWidth()
+    {
+        $dimensions = $this->getTerminalDimensions();
+
+        return $dimensions[0];
+    }
+
+    /**
+     * 获取终端高度
+     * @return int|null
+     */
+    protected function getTerminalHeight()
+    {
+        $dimensions = $this->getTerminalDimensions();
+
+        return $dimensions[1];
+    }
+
+    /**
+     * 获取当前终端的尺寸
+     * @return array
+     */
+    public function getTerminalDimensions(): array
+    {
+        if ($this->terminalDimensions) {
+            return $this->terminalDimensions;
+        }
+
+        if ('\\' === DIRECTORY_SEPARATOR) {
+            if (preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim(getenv('ANSICON')), $matches)) {
+                return [(int) $matches[1], (int) $matches[2]];
+            }
+            if (preg_match('/^(\d+)x(\d+)$/', $this->getMode(), $matches)) {
+                return [(int) $matches[1], (int) $matches[2]];
+            }
+        }
+
+        if ($sttyString = $this->getSttyColumns()) {
+            if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) {
+                return [(int) $matches[2], (int) $matches[1]];
+            }
+            if (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) {
+                return [(int) $matches[2], (int) $matches[1]];
+            }
+        }
+
+        return [null, null];
+    }
+
+    /**
+     * 获取stty列数
+     * @return string
+     */
+    private function getSttyColumns()
+    {
+        if (!function_exists('proc_open')) {
+            return;
+        }
+
+        $descriptorspec = [1 => ['pipe', 'w'], 2 => ['pipe', 'w']];
+        $process        = proc_open('stty -a | grep columns', $descriptorspec, $pipes, null, null, ['suppress_errors' => true]);
+        if (is_resource($process)) {
+            $info = stream_get_contents($pipes[1]);
+            fclose($pipes[1]);
+            fclose($pipes[2]);
+            proc_close($process);
+
+            return $info;
+        }
+        return;
+    }
+
+    /**
+     * 获取终端模式
+     * @return string <width>x<height>
+     */
+    private function getMode()
+    {
+        if (!function_exists('proc_open')) {
+            return '';
+        }
+
+        $descriptorspec = [1 => ['pipe', 'w'], 2 => ['pipe', 'w']];
+        $process        = proc_open('mode CON', $descriptorspec, $pipes, null, null, ['suppress_errors' => true]);
+        if (is_resource($process)) {
+            $info = stream_get_contents($pipes[1]);
+            fclose($pipes[1]);
+            fclose($pipes[2]);
+            proc_close($process);
+
+            if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) {
+                return $matches[2] . 'x' . $matches[1];
+            }
+        }
+
+        return '';
+    }
+
+    private function stringWidth(string $string): int
+    {
+        if (!function_exists('mb_strwidth')) {
+            return strlen($string);
+        }
+
+        if (false === $encoding = mb_detect_encoding($string)) {
+            return strlen($string);
+        }
+
+        return mb_strwidth($string, $encoding);
+    }
+
+    private function splitStringByWidth(string $string, int $width): array
+    {
+        if (!function_exists('mb_strwidth')) {
+            return str_split($string, $width);
+        }
+
+        if (false === $encoding = mb_detect_encoding($string)) {
+            return str_split($string, $width);
+        }
+
+        $utf8String = mb_convert_encoding($string, 'utf8', $encoding);
+        $lines      = [];
+        $line       = '';
+        foreach (preg_split('//u', $utf8String) as $char) {
+            if (mb_strwidth($line . $char, 'utf8') <= $width) {
+                $line .= $char;
+                continue;
+            }
+            $lines[] = str_pad($line, $width);
+            $line    = $char;
+        }
+        if (strlen($line)) {
+            $lines[] = count($lines) ? str_pad($line, $width) : $line;
+        }
+
+        mb_convert_variables($encoding, 'utf8', $lines);
+
+        return $lines;
+    }
+
+    private function isRunningOS400(): bool
+    {
+        $checks = [
+            function_exists('php_uname') ? php_uname('s') : '',
+            getenv('OSTYPE'),
+            PHP_OS,
+        ];
+        return false !== stripos(implode(';', $checks), 'OS400');
+    }
+
+    /**
+     * 当前环境是否支持写入控制台输出到stdout.
+     *
+     * @return bool
+     */
+    protected function hasStdoutSupport(): bool
+    {
+        return false === $this->isRunningOS400();
+    }
+
+    /**
+     * 当前环境是否支持写入控制台输出到stderr.
+     *
+     * @return bool
+     */
+    protected function hasStderrSupport(): bool
+    {
+        return false === $this->isRunningOS400();
+    }
+
+    /**
+     * @return resource
+     */
+    private function openOutputStream()
+    {
+        if (!$this->hasStdoutSupport()) {
+            return fopen('php://output', 'w');
+        }
+        return @fopen('php://stdout', 'w') ?: fopen('php://output', 'w');
+    }
+
+    /**
+     * @return resource
+     */
+    private function openErrorStream()
+    {
+        return fopen($this->hasStderrSupport() ? 'php://stderr' : 'php://output', 'w');
+    }
+
+    /**
+     * 将消息写入到输出。
+     * @param string $message 消息
+     * @param bool   $newline 是否另起一行
+     * @param null   $stream
+     */
+    protected function doWrite($message, $newline, $stream = null)
+    {
+        if (null === $stream) {
+            $stream = $this->stdout;
+        }
+        if (false === @fwrite($stream, $message . ($newline ? PHP_EOL : ''))) {
+            throw new \RuntimeException('Unable to write output.');
+        }
+
+        fflush($stream);
+    }
+
+    /**
+     * 是否支持着色
+     * @param $stream
+     * @return bool
+     */
+    protected function hasColorSupport($stream): bool
+    {
+        if (DIRECTORY_SEPARATOR === '\\') {
+            return
+            '10.0.10586' === PHP_WINDOWS_VERSION_MAJOR . '.' . PHP_WINDOWS_VERSION_MINOR . '.' . PHP_WINDOWS_VERSION_BUILD
+            || false !== getenv('ANSICON')
+            || 'ON' === getenv('ConEmuANSI')
+            || 'xterm' === getenv('TERM');
+        }
+
+        return function_exists('posix_isatty') && @posix_isatty($stream);
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/console/output/driver/Nothing.php b/vendor/topthink/framework/src/think/console/output/driver/Nothing.php
new file mode 100755
index 0000000..a7cc49e
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/output/driver/Nothing.php
@@ -0,0 +1,33 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\console\output\driver;
+
+use think\console\Output;
+
+class Nothing
+{
+
+    public function __construct(Output $output)
+    {
+        // do nothing
+    }
+
+    public function write($messages, bool $newline = false, int $options = 0)
+    {
+        // do nothing
+    }
+
+    public function renderException(\Throwable $e)
+    {
+        // do nothing
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/output/formatter/Stack.php b/vendor/topthink/framework/src/think/console/output/formatter/Stack.php
new file mode 100755
index 0000000..5366259
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/output/formatter/Stack.php
@@ -0,0 +1,116 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\console\output\formatter;
+
+class Stack
+{
+
+    /**
+     * @var Style[]
+     */
+    private $styles;
+
+    /**
+     * @var Style
+     */
+    private $emptyStyle;
+
+    /**
+     * 构造方法
+     * @param Style|null $emptyStyle
+     */
+    public function __construct(Style $emptyStyle = null)
+    {
+        $this->emptyStyle = $emptyStyle ?: new Style();
+        $this->reset();
+    }
+
+    /**
+     * 重置堆栈
+     */
+    public function reset(): void
+    {
+        $this->styles = [];
+    }
+
+    /**
+     * 推一个样式进入堆栈
+     * @param Style $style
+     */
+    public function push(Style $style): void
+    {
+        $this->styles[] = $style;
+    }
+
+    /**
+     * 从堆栈中弹出一个样式
+     * @param Style|null $style
+     * @return Style
+     * @throws \InvalidArgumentException
+     */
+    public function pop(Style $style = null): Style
+    {
+        if (empty($this->styles)) {
+            return $this->emptyStyle;
+        }
+
+        if (null === $style) {
+            return array_pop($this->styles);
+        }
+
+        /**
+         * @var int   $index
+         * @var Style $stackedStyle
+         */
+        foreach (array_reverse($this->styles, true) as $index => $stackedStyle) {
+            if ($style->apply('') === $stackedStyle->apply('')) {
+                $this->styles = array_slice($this->styles, 0, $index);
+
+                return $stackedStyle;
+            }
+        }
+
+        throw new \InvalidArgumentException('Incorrectly nested style tag found.');
+    }
+
+    /**
+     * 计算堆栈的当前样式。
+     * @return Style
+     */
+    public function getCurrent(): Style
+    {
+        if (empty($this->styles)) {
+            return $this->emptyStyle;
+        }
+
+        return $this->styles[count($this->styles) - 1];
+    }
+
+    /**
+     * @param Style $emptyStyle
+     * @return Stack
+     */
+    public function setEmptyStyle(Style $emptyStyle)
+    {
+        $this->emptyStyle = $emptyStyle;
+
+        return $this;
+    }
+
+    /**
+     * @return Style
+     */
+    public function getEmptyStyle(): Style
+    {
+        return $this->emptyStyle;
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/output/formatter/Style.php b/vendor/topthink/framework/src/think/console/output/formatter/Style.php
new file mode 100755
index 0000000..2aae768
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/output/formatter/Style.php
@@ -0,0 +1,190 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\console\output\formatter;
+
+class Style
+{
+    protected static $availableForegroundColors = [
+        'black'   => ['set' => 30, 'unset' => 39],
+        'red'     => ['set' => 31, 'unset' => 39],
+        'green'   => ['set' => 32, 'unset' => 39],
+        'yellow'  => ['set' => 33, 'unset' => 39],
+        'blue'    => ['set' => 34, 'unset' => 39],
+        'magenta' => ['set' => 35, 'unset' => 39],
+        'cyan'    => ['set' => 36, 'unset' => 39],
+        'white'   => ['set' => 37, 'unset' => 39],
+    ];
+
+    protected static $availableBackgroundColors = [
+        'black'   => ['set' => 40, 'unset' => 49],
+        'red'     => ['set' => 41, 'unset' => 49],
+        'green'   => ['set' => 42, 'unset' => 49],
+        'yellow'  => ['set' => 43, 'unset' => 49],
+        'blue'    => ['set' => 44, 'unset' => 49],
+        'magenta' => ['set' => 45, 'unset' => 49],
+        'cyan'    => ['set' => 46, 'unset' => 49],
+        'white'   => ['set' => 47, 'unset' => 49],
+    ];
+
+    protected static $availableOptions = [
+        'bold'       => ['set' => 1, 'unset' => 22],
+        'underscore' => ['set' => 4, 'unset' => 24],
+        'blink'      => ['set' => 5, 'unset' => 25],
+        'reverse'    => ['set' => 7, 'unset' => 27],
+        'conceal'    => ['set' => 8, 'unset' => 28],
+    ];
+
+    private $foreground;
+    private $background;
+    private $options = [];
+
+    /**
+     * 初始化输出的样式
+     * @param string|null $foreground 字体颜色
+     * @param string|null $background 背景色
+     * @param array       $options    格式
+     * @api
+     */
+    public function __construct($foreground = null, $background = null, array $options = [])
+    {
+        if (null !== $foreground) {
+            $this->setForeground($foreground);
+        }
+        if (null !== $background) {
+            $this->setBackground($background);
+        }
+        if (count($options)) {
+            $this->setOptions($options);
+        }
+    }
+
+    /**
+     * 设置字体颜色
+     * @param string|null $color 颜色名
+     * @throws \InvalidArgumentException
+     * @api
+     */
+    public function setForeground($color = null)
+    {
+        if (null === $color) {
+            $this->foreground = null;
+
+            return;
+        }
+
+        if (!isset(static::$availableForegroundColors[$color])) {
+            throw new \InvalidArgumentException(sprintf('Invalid foreground color specified: "%s". Expected one of (%s)', $color, implode(', ', array_keys(static::$availableForegroundColors))));
+        }
+
+        $this->foreground = static::$availableForegroundColors[$color];
+    }
+
+    /**
+     * 设置背景色
+     * @param string|null $color 颜色名
+     * @throws \InvalidArgumentException
+     * @api
+     */
+    public function setBackground($color = null)
+    {
+        if (null === $color) {
+            $this->background = null;
+
+            return;
+        }
+
+        if (!isset(static::$availableBackgroundColors[$color])) {
+            throw new \InvalidArgumentException(sprintf('Invalid background color specified: "%s". Expected one of (%s)', $color, implode(', ', array_keys(static::$availableBackgroundColors))));
+        }
+
+        $this->background = static::$availableBackgroundColors[$color];
+    }
+
+    /**
+     * 设置字体格式
+     * @param string $option 格式名
+     * @throws \InvalidArgumentException When the option name isn't defined
+     * @api
+     */
+    public function setOption(string $option): void
+    {
+        if (!isset(static::$availableOptions[$option])) {
+            throw new \InvalidArgumentException(sprintf('Invalid option specified: "%s". Expected one of (%s)', $option, implode(', ', array_keys(static::$availableOptions))));
+        }
+
+        if (!in_array(static::$availableOptions[$option], $this->options)) {
+            $this->options[] = static::$availableOptions[$option];
+        }
+    }
+
+    /**
+     * 重置字体格式
+     * @param string $option 格式名
+     * @throws \InvalidArgumentException
+     */
+    public function unsetOption(string $option): void
+    {
+        if (!isset(static::$availableOptions[$option])) {
+            throw new \InvalidArgumentException(sprintf('Invalid option specified: "%s". Expected one of (%s)', $option, implode(', ', array_keys(static::$availableOptions))));
+        }
+
+        $pos = array_search(static::$availableOptions[$option], $this->options);
+        if (false !== $pos) {
+            unset($this->options[$pos]);
+        }
+    }
+
+    /**
+     * 批量设置字体格式
+     * @param array $options
+     */
+    public function setOptions(array $options)
+    {
+        $this->options = [];
+
+        foreach ($options as $option) {
+            $this->setOption($option);
+        }
+    }
+
+    /**
+     * 应用样式到文字
+     * @param string $text 文字
+     * @return string
+     */
+    public function apply(string $text): string
+    {
+        $setCodes   = [];
+        $unsetCodes = [];
+
+        if (null !== $this->foreground) {
+            $setCodes[]   = $this->foreground['set'];
+            $unsetCodes[] = $this->foreground['unset'];
+        }
+        if (null !== $this->background) {
+            $setCodes[]   = $this->background['set'];
+            $unsetCodes[] = $this->background['unset'];
+        }
+        if (count($this->options)) {
+            foreach ($this->options as $option) {
+                $setCodes[]   = $option['set'];
+                $unsetCodes[] = $option['unset'];
+            }
+        }
+
+        if (0 === count($setCodes)) {
+            return $text;
+        }
+
+        return sprintf("\033[%sm%s\033[%sm", implode(';', $setCodes), $text, implode(';', $unsetCodes));
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/output/question/Choice.php b/vendor/topthink/framework/src/think/console/output/question/Choice.php
new file mode 100755
index 0000000..1da1750
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/output/question/Choice.php
@@ -0,0 +1,163 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\console\output\question;
+
+use think\console\output\Question;
+
+class Choice extends Question
+{
+
+    private $choices;
+    private $multiselect  = false;
+    private $prompt       = ' > ';
+    private $errorMessage = 'Value "%s" is invalid';
+
+    /**
+     * 构造方法
+     * @param string $question 问题
+     * @param array  $choices  选项
+     * @param mixed  $default  默认答案
+     */
+    public function __construct($question, array $choices, $default = null)
+    {
+        parent::__construct($question, $default);
+
+        $this->choices = $choices;
+        $this->setValidator($this->getDefaultValidator());
+        $this->setAutocompleterValues($choices);
+    }
+
+    /**
+     * 可选项
+     * @return array
+     */
+    public function getChoices(): array
+    {
+        return $this->choices;
+    }
+
+    /**
+     * 设置可否多选
+     * @param bool $multiselect
+     * @return self
+     */
+    public function setMultiselect(bool $multiselect)
+    {
+        $this->multiselect = $multiselect;
+        $this->setValidator($this->getDefaultValidator());
+
+        return $this;
+    }
+
+    public function isMultiselect(): bool
+    {
+        return $this->multiselect;
+    }
+
+    /**
+     * 获取提示
+     * @return string
+     */
+    public function getPrompt(): string
+    {
+        return $this->prompt;
+    }
+
+    /**
+     * 设置提示
+     * @param string $prompt
+     * @return self
+     */
+    public function setPrompt(string $prompt)
+    {
+        $this->prompt = $prompt;
+
+        return $this;
+    }
+
+    /**
+     * 设置错误提示信息
+     * @param string $errorMessage
+     * @return self
+     */
+    public function setErrorMessage(string $errorMessage)
+    {
+        $this->errorMessage = $errorMessage;
+        $this->setValidator($this->getDefaultValidator());
+
+        return $this;
+    }
+
+    /**
+     * 获取默认的验证方法
+     * @return callable
+     */
+    private function getDefaultValidator()
+    {
+        $choices      = $this->choices;
+        $errorMessage = $this->errorMessage;
+        $multiselect  = $this->multiselect;
+        $isAssoc      = $this->isAssoc($choices);
+
+        return function ($selected) use ($choices, $errorMessage, $multiselect, $isAssoc) {
+            // Collapse all spaces.
+            $selectedChoices = str_replace(' ', '', $selected);
+
+            if ($multiselect) {
+                // Check for a separated comma values
+                if (!preg_match('/^[a-zA-Z0-9_-]+(?:,[a-zA-Z0-9_-]+)*$/', $selectedChoices, $matches)) {
+                    throw new \InvalidArgumentException(sprintf($errorMessage, $selected));
+                }
+                $selectedChoices = explode(',', $selectedChoices);
+            } else {
+                $selectedChoices = [$selected];
+            }
+
+            $multiselectChoices = [];
+            foreach ($selectedChoices as $value) {
+                $results = [];
+                foreach ($choices as $key => $choice) {
+                    if ($choice === $value) {
+                        $results[] = $key;
+                    }
+                }
+
+                if (count($results) > 1) {
+                    throw new \InvalidArgumentException(sprintf('The provided answer is ambiguous. Value should be one of %s.', implode(' or ', $results)));
+                }
+
+                $result = array_search($value, $choices);
+
+                if (!$isAssoc) {
+                    if (!empty($result)) {
+                        $result = $choices[$result];
+                    } elseif (isset($choices[$value])) {
+                        $result = $choices[$value];
+                    }
+                } elseif (empty($result) && array_key_exists($value, $choices)) {
+                    $result = $value;
+                }
+
+                if (false === $result) {
+                    throw new \InvalidArgumentException(sprintf($errorMessage, $value));
+                }
+                array_push($multiselectChoices, $result);
+            }
+
+            if ($multiselect) {
+                return $multiselectChoices;
+            }
+
+            return current($multiselectChoices);
+        };
+    }
+}
diff --git a/vendor/topthink/framework/src/think/console/output/question/Confirmation.php b/vendor/topthink/framework/src/think/console/output/question/Confirmation.php
new file mode 100755
index 0000000..bf71b5d
--- /dev/null
+++ b/vendor/topthink/framework/src/think/console/output/question/Confirmation.php
@@ -0,0 +1,57 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\console\output\question;
+
+use think\console\output\Question;
+
+class Confirmation extends Question
+{
+
+    private $trueAnswerRegex;
+
+    /**
+     * 构造方法
+     * @param string $question        问题
+     * @param bool   $default         默认答案
+     * @param string $trueAnswerRegex 验证正则
+     */
+    public function __construct(string $question, bool $default = true, string $trueAnswerRegex = '/^y/i')
+    {
+        parent::__construct($question, (bool) $default);
+
+        $this->trueAnswerRegex = $trueAnswerRegex;
+        $this->setNormalizer($this->getDefaultNormalizer());
+    }
+
+    /**
+     * 获取默认的答案回调
+     * @return callable
+     */
+    private function getDefaultNormalizer()
+    {
+        $default = $this->getDefault();
+        $regex   = $this->trueAnswerRegex;
+
+        return function ($answer) use ($default, $regex) {
+            if (is_bool($answer)) {
+                return $answer;
+            }
+
+            $answerIsTrue = (bool) preg_match($regex, $answer);
+            if (false === $default) {
+                return $answer && $answerIsTrue;
+            }
+
+            return !$answer || $answerIsTrue;
+        };
+    }
+}
diff --git a/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php b/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php
new file mode 100755
index 0000000..da5e696
--- /dev/null
+++ b/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php
@@ -0,0 +1,88 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\contract;
+
+/**
+ * 缓存驱动接口
+ */
+interface CacheHandlerInterface
+{
+    /**
+     * 判断缓存
+     * @access public
+     * @param string $name 缓存变量名
+     * @return bool
+     */
+    public function has($name);
+
+    /**
+     * 读取缓存
+     * @access public
+     * @param string $name    缓存变量名
+     * @param mixed  $default 默认值
+     * @return mixed
+     */
+    public function get($name, $default = null);
+
+    /**
+     * 写入缓存
+     * @access public
+     * @param string            $name   缓存变量名
+     * @param mixed             $value  存储数据
+     * @param integer|\DateTime $expire 有效时间(秒)
+     * @return bool
+     */
+    public function set($name, $value, $expire = null);
+
+    /**
+     * 自增缓存(针对数值缓存)
+     * @access public
+     * @param string $name 缓存变量名
+     * @param int    $step 步长
+     * @return false|int
+     */
+    public function inc(string $name, int $step = 1);
+
+    /**
+     * 自减缓存(针对数值缓存)
+     * @access public
+     * @param string $name 缓存变量名
+     * @param int    $step 步长
+     * @return false|int
+     */
+    public function dec(string $name, int $step = 1);
+
+    /**
+     * 删除缓存
+     * @access public
+     * @param string $name 缓存变量名
+     * @return bool
+     */
+    public function delete($name);
+
+    /**
+     * 清除缓存
+     * @access public
+     * @return bool
+     */
+    public function clear();
+
+    /**
+     * 删除缓存标签
+     * @access public
+     * @param array $keys 缓存标识列表
+     * @return void
+     */
+    public function clearTag(array $keys);
+
+}
diff --git a/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php b/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php
new file mode 100755
index 0000000..896ac29
--- /dev/null
+++ b/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php
@@ -0,0 +1,28 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\contract;
+
+/**
+ * 日志驱动接口
+ */
+interface LogHandlerInterface
+{
+    /**
+     * 日志写入接口
+     * @access public
+     * @param  array $log 日志信息
+     * @return bool
+     */
+    public function save(array $log): bool;
+
+}
diff --git a/vendor/topthink/framework/src/think/contract/ModelRelationInterface.php b/vendor/topthink/framework/src/think/contract/ModelRelationInterface.php
new file mode 100755
index 0000000..1f6f994
--- /dev/null
+++ b/vendor/topthink/framework/src/think/contract/ModelRelationInterface.php
@@ -0,0 +1,99 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\contract;
+
+use Closure;
+use think\Collection;
+use think\db\Query;
+use think\Model;
+
+/**
+ * 模型关联接口
+ */
+interface ModelRelationInterface
+{
+    /**
+     * 延迟获取关联数据
+     * @access public
+     * @param  array   $subRelation 子关联
+     * @param  Closure $closure     闭包查询条件
+     * @return Collection
+     */
+    public function getRelation(array $subRelation = [], Closure $closure = null): Collection;
+
+    /**
+     * 预载入关联查询
+     * @access public
+     * @param  array   $resultSet   数据集
+     * @param  string  $relation    当前关联名
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包条件
+     * @return void
+     */
+    public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null): void;
+
+    /**
+     * 预载入关联查询
+     * @access public
+     * @param  Model   $result      数据对象
+     * @param  string  $relation    当前关联名
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包条件
+     * @return void
+     */
+    public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null): void;
+
+    /**
+     * 关联统计
+     * @access public
+     * @param  Model   $result  模型对象
+     * @param  Closure $closure 闭包
+     * @param  string  $aggregate 聚合查询方法
+     * @param  string  $field 字段
+     * @param  string  $name 统计字段别名
+     * @return integer
+     */
+    public function relationCount(Model $result, Closure $closure, string $aggregate = 'count', string $field = '*', string &$name = null);
+
+    /**
+     * 创建关联统计子查询
+     * @access public
+     * @param  Closure $closure 闭包
+     * @param  string  $aggregate 聚合查询方法
+     * @param  string  $field 字段
+     * @param  string  $name 统计字段别名
+     * @return string
+     */
+    public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string;
+
+    /**
+     * 根据关联条件查询当前模型
+     * @access public
+     * @param  string  $operator 比较操作符
+     * @param  integer $count    个数
+     * @param  string  $id       关联表的统计字段
+     * @param  string  $joinType JOIN类型
+     * @return Query
+     */
+    public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = 'INNER'): Query;
+
+    /**
+     * 根据关联条件查询当前模型
+     * @access public
+     * @param  mixed  $where 查询条件(数组或者闭包)
+     * @param  mixed  $fields 字段
+     * @param  string $joinType JOIN类型
+     * @return Query
+     */
+    public function hasWhere($where = [], $fields = null, string $joinType = ''): Query;
+}
diff --git a/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php b/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php
new file mode 100755
index 0000000..0b2e414
--- /dev/null
+++ b/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php
@@ -0,0 +1,23 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\contract;
+
+/**
+ * Session驱动接口
+ */
+interface SessionHandlerInterface
+{
+    public function read(string $sessionId): string;
+    public function delete(string $sessionId): bool;
+    public function write(string $sessionId, string $data): bool;
+}
diff --git a/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php b/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php
new file mode 100755
index 0000000..9be93d2
--- /dev/null
+++ b/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php
@@ -0,0 +1,61 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\contract;
+
+/**
+ * 视图驱动接口
+ */
+interface TemplateHandlerInterface
+{
+    /**
+     * 检测是否存在模板文件
+     * @access public
+     * @param  string $template 模板文件或者模板规则
+     * @return bool
+     */
+    public function exists(string $template): bool;
+
+    /**
+     * 渲染模板文件
+     * @access public
+     * @param  string $template 模板文件
+     * @param  array  $data 模板变量
+     * @return void
+     */
+    public function fetch(string $template, array $data = []): void;
+
+    /**
+     * 渲染模板内容
+     * @access public
+     * @param  string $content 模板内容
+     * @param  array  $data 模板变量
+     * @return void
+     */
+    public function display(string $content, array $data = []): void;
+
+    /**
+     * 配置模板引擎
+     * @access private
+     * @param  array $config 参数
+     * @return void
+     */
+    public function config(array $config): void;
+
+    /**
+     * 获取模板引擎配置
+     * @access public
+     * @param  string $name 参数名
+     * @return void
+     */
+    public function getConfig(string $name);
+}
diff --git a/vendor/topthink/framework/src/think/event/AppInit.php b/vendor/topthink/framework/src/think/event/AppInit.php
new file mode 100755
index 0000000..dda820b
--- /dev/null
+++ b/vendor/topthink/framework/src/think/event/AppInit.php
@@ -0,0 +1,19 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\event;
+
+/**
+ * AppInit事件类
+ */
+class AppInit
+{}
diff --git a/vendor/topthink/framework/src/think/event/HttpEnd.php b/vendor/topthink/framework/src/think/event/HttpEnd.php
new file mode 100755
index 0000000..c40da57
--- /dev/null
+++ b/vendor/topthink/framework/src/think/event/HttpEnd.php
@@ -0,0 +1,19 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\event;
+
+/**
+ * HttpEnd事件类
+ */
+class HttpEnd
+{}
diff --git a/vendor/topthink/framework/src/think/event/HttpRun.php b/vendor/topthink/framework/src/think/event/HttpRun.php
new file mode 100755
index 0000000..ce67e93
--- /dev/null
+++ b/vendor/topthink/framework/src/think/event/HttpRun.php
@@ -0,0 +1,19 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\event;
+
+/**
+ * HttpRun事件类
+ */
+class HttpRun
+{}
diff --git a/vendor/topthink/framework/src/think/event/LogRecord.php b/vendor/topthink/framework/src/think/event/LogRecord.php
new file mode 100755
index 0000000..237468d
--- /dev/null
+++ b/vendor/topthink/framework/src/think/event/LogRecord.php
@@ -0,0 +1,29 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+namespace think\event;
+
+/**
+ * LogRecord事件类
+ */
+class LogRecord
+{
+    /** @var string */
+    public $type;
+
+    /** @var string */
+    public $message;
+
+    public function __construct($type, $message)
+    {
+        $this->type    = $type;
+        $this->message = $message;
+    }
+}
diff --git a/vendor/topthink/framework/src/think/event/LogWrite.php b/vendor/topthink/framework/src/think/event/LogWrite.php
new file mode 100755
index 0000000..a787301
--- /dev/null
+++ b/vendor/topthink/framework/src/think/event/LogWrite.php
@@ -0,0 +1,31 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\event;
+
+/**
+ * LogWrite事件类
+ */
+class LogWrite
+{
+    /** @var string */
+    public $channel;
+
+    /** @var array */
+    public $log;
+
+    public function __construct($channel, $log)
+    {
+        $this->channel = $channel;
+        $this->log     = $log;
+    }
+}
diff --git a/vendor/topthink/framework/src/think/event/RouteLoaded.php b/vendor/topthink/framework/src/think/event/RouteLoaded.php
new file mode 100755
index 0000000..ace7992
--- /dev/null
+++ b/vendor/topthink/framework/src/think/event/RouteLoaded.php
@@ -0,0 +1,21 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\event;
+
+/**
+ * 路由加载完成事件
+ */
+class RouteLoaded
+{
+
+}
diff --git a/vendor/topthink/framework/src/think/exception/ClassNotFoundException.php b/vendor/topthink/framework/src/think/exception/ClassNotFoundException.php
new file mode 100755
index 0000000..c4cda77
--- /dev/null
+++ b/vendor/topthink/framework/src/think/exception/ClassNotFoundException.php
@@ -0,0 +1,39 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\exception;
+
+use Psr\Container\NotFoundExceptionInterface;
+use RuntimeException;
+use Throwable;
+
+class ClassNotFoundException extends RuntimeException implements NotFoundExceptionInterface
+{
+    protected $class;
+
+    public function __construct(string $message, string $class = '', Throwable $previous = null)
+    {
+        $this->message = $message;
+        $this->class   = $class;
+
+        parent::__construct($message, 0, $previous);
+    }
+
+    /**
+     * 获取类名
+     * @access public
+     * @return string
+     */
+    public function getClass()
+    {
+        return $this->class;
+    }
+}
diff --git a/vendor/topthink/framework/src/think/exception/ErrorException.php b/vendor/topthink/framework/src/think/exception/ErrorException.php
new file mode 100755
index 0000000..d1a2378
--- /dev/null
+++ b/vendor/topthink/framework/src/think/exception/ErrorException.php
@@ -0,0 +1,57 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\exception;
+
+use think\Exception;
+
+/**
+ * ThinkPHP错误异常
+ * 主要用于封装 set_error_handler 和 register_shutdown_function 得到的错误
+ * 除开从 think\Exception 继承的功能
+ * 其他和PHP系统\ErrorException功能基本一样
+ */
+class ErrorException extends Exception
+{
+    /**
+     * 用于保存错误级别
+     * @var integer
+     */
+    protected $severity;
+
+    /**
+     * 错误异常构造函数
+     * @access public
+     * @param  integer $severity 错误级别
+     * @param  string  $message  错误详细信息
+     * @param  string  $file     出错文件路径
+     * @param  integer $line     出错行号
+     */
+    public function __construct(int $severity, string $message, string $file, int $line)
+    {
+        $this->severity = $severity;
+        $this->message  = $message;
+        $this->file     = $file;
+        $this->line     = $line;
+        $this->code     = 0;
+    }
+
+    /**
+     * 获取错误级别
+     * @access public
+     * @return integer 错误级别
+     */
+    final public function getSeverity()
+    {
+        return $this->severity;
+    }
+}
diff --git a/vendor/topthink/framework/src/think/exception/FileException.php b/vendor/topthink/framework/src/think/exception/FileException.php
new file mode 100755
index 0000000..228a189
--- /dev/null
+++ b/vendor/topthink/framework/src/think/exception/FileException.php
@@ -0,0 +1,17 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\exception;
+
+class FileException extends \RuntimeException
+{
+}
diff --git a/vendor/topthink/framework/src/think/exception/FuncNotFoundException.php b/vendor/topthink/framework/src/think/exception/FuncNotFoundException.php
new file mode 100755
index 0000000..ee2bcad
--- /dev/null
+++ b/vendor/topthink/framework/src/think/exception/FuncNotFoundException.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace think\exception;
+
+use Psr\Container\NotFoundExceptionInterface;
+use RuntimeException;
+use Throwable;
+
+class FuncNotFoundException extends RuntimeException implements NotFoundExceptionInterface
+{
+    protected $func;
+
+    public function __construct(string $message, string $func = '', Throwable $previous = null)
+    {
+        $this->message = $message;
+        $this->func   = $func;
+
+        parent::__construct($message, 0, $previous);
+    }
+
+    /**
+     * 获取方法名
+     * @access public
+     * @return string
+     */
+    public function getFunc()
+    {
+        return $this->func;
+    }
+}
diff --git a/vendor/topthink/framework/src/think/exception/Handle.php b/vendor/topthink/framework/src/think/exception/Handle.php
new file mode 100755
index 0000000..1f783bc
--- /dev/null
+++ b/vendor/topthink/framework/src/think/exception/Handle.php
@@ -0,0 +1,332 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\exception;
+
+use Exception;
+use think\App;
+use think\console\Output;
+use think\db\exception\DataNotFoundException;
+use think\db\exception\ModelNotFoundException;
+use think\Request;
+use think\Response;
+use Throwable;
+
+/**
+ * 系统异常处理类
+ */
+class Handle
+{
+    /** @var App */
+    protected $app;
+
+    protected $ignoreReport = [
+        HttpException::class,
+        HttpResponseException::class,
+        ModelNotFoundException::class,
+        DataNotFoundException::class,
+        ValidateException::class,
+    ];
+
+    protected $isJson = false;
+
+    public function __construct(App $app)
+    {
+        $this->app = $app;
+    }
+
+    /**
+     * Report or log an exception.
+     *
+     * @access public
+     * @param Throwable $exception
+     * @return void
+     */
+    public function report(Throwable $exception): void
+    {
+        if (!$this->isIgnoreReport($exception)) {
+            // 收集异常数据
+            if ($this->app->isDebug()) {
+                $data = [
+                    'file'    => $exception->getFile(),
+                    'line'    => $exception->getLine(),
+                    'message' => $this->getMessage($exception),
+                    'code'    => $this->getCode($exception),
+                ];
+                $log = "[{$data['code']}]{$data['message']}[{$data['file']}:{$data['line']}]";
+            } else {
+                $data = [
+                    'code'    => $this->getCode($exception),
+                    'message' => $this->getMessage($exception),
+                ];
+                $log = "[{$data['code']}]{$data['message']}";
+            }
+
+            if ($this->app->config->get('log.record_trace')) {
+                $log .= PHP_EOL . $exception->getTraceAsString();
+            }
+
+            try {
+                $this->app->log->record($log, 'error');
+            } catch (Exception $e) {}
+        }
+    }
+
+    protected function isIgnoreReport(Throwable $exception): bool
+    {
+        foreach ($this->ignoreReport as $class) {
+            if ($exception instanceof $class) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Render an exception into an HTTP response.
+     *
+     * @access public
+     * @param Request   $request
+     * @param Throwable $e
+     * @return Response
+     */
+    public function render($request, Throwable $e): Response
+    {
+        $this->isJson = $request->isJson();
+        if ($e instanceof HttpResponseException) {
+            return $e->getResponse();
+        } elseif ($e instanceof HttpException) {
+            return $this->renderHttpException($e);
+        } else {
+            return $this->convertExceptionToResponse($e);
+        }
+    }
+
+    /**
+     * @access public
+     * @param Output    $output
+     * @param Throwable $e
+     */
+    public function renderForConsole(Output $output, Throwable $e): void
+    {
+        if ($this->app->isDebug()) {
+            $output->setVerbosity(Output::VERBOSITY_DEBUG);
+        }
+
+        $output->renderException($e);
+    }
+
+    /**
+     * @access protected
+     * @param HttpException $e
+     * @return Response
+     */
+    protected function renderHttpException(HttpException $e): Response
+    {
+        $status   = $e->getStatusCode();
+        $template = $this->app->config->get('app.http_exception_template');
+
+        if (!$this->app->isDebug() && !empty($template[$status])) {
+            return Response::create($template[$status], 'view', $status)->assign(['e' => $e]);
+        } else {
+            return $this->convertExceptionToResponse($e);
+        }
+    }
+
+    /**
+     * 收集异常数据
+     * @param Throwable $exception
+     * @return array
+     */
+    protected function convertExceptionToArray(Throwable $exception): array
+    {
+        if ($this->app->isDebug()) {
+            // 调试模式,获取详细的错误信息
+            $traces        = [];
+            $nextException = $exception;
+            do {
+                $traces[] = [
+                    'name'    => get_class($nextException),
+                    'file'    => $nextException->getFile(),
+                    'line'    => $nextException->getLine(),
+                    'code'    => $this->getCode($nextException),
+                    'message' => $this->getMessage($nextException),
+                    'trace'   => $nextException->getTrace(),
+                    'source'  => $this->getSourceCode($nextException),
+                ];
+            } while ($nextException = $nextException->getPrevious());
+            $data = [
+                'code'    => $this->getCode($exception),
+                'message' => $this->getMessage($exception),
+                'traces'  => $traces,
+                'datas'   => $this->getExtendData($exception),
+                'tables'  => [
+                    'GET Data'            => $this->app->request->get(),
+                    'POST Data'           => $this->app->request->post(),
+                    'Files'               => $this->app->request->file(),
+                    'Cookies'             => $this->app->request->cookie(),
+                    'Session'             => $this->app->exists('session') ? $this->app->session->all() : [],
+                    'Server/Request Data' => $this->app->request->server(),
+                ],
+            ];
+        } else {
+            // 部署模式仅显示 Code 和 Message
+            $data = [
+                'code'    => $this->getCode($exception),
+                'message' => $this->getMessage($exception),
+            ];
+
+            if (!$this->app->config->get('app.show_error_msg')) {
+                // 不显示详细错误信息
+                $data['message'] = $this->app->config->get('app.error_message');
+            }
+        }
+
+        return $data;
+    }
+
+    /**
+     * @access protected
+     * @param Throwable $exception
+     * @return Response
+     */
+    protected function convertExceptionToResponse(Throwable $exception): Response
+    {
+        if (!$this->isJson) {
+            $response = Response::create($this->renderExceptionContent($exception));
+        } else {
+            $response = Response::create($this->convertExceptionToArray($exception), 'json');
+        }
+
+        if ($exception instanceof HttpException) {
+            $statusCode = $exception->getStatusCode();
+            $response->header($exception->getHeaders());
+        }
+
+        return $response->code($statusCode ?? 500);
+    }
+
+    protected function renderExceptionContent(Throwable $exception): string
+    {
+        ob_start();
+        $data = $this->convertExceptionToArray($exception);
+        extract($data);
+        include $this->app->config->get('app.exception_tmpl') ?: __DIR__ . '/../../tpl/think_exception.tpl';
+
+        return ob_get_clean();
+    }
+
+    /**
+     * 获取错误编码
+     * ErrorException则使用错误级别作为错误编码
+     * @access protected
+     * @param Throwable $exception
+     * @return integer                错误编码
+     */
+    protected function getCode(Throwable $exception)
+    {
+        $code = $exception->getCode();
+
+        if (!$code && $exception instanceof ErrorException) {
+            $code = $exception->getSeverity();
+        }
+
+        return $code;
+    }
+
+    /**
+     * 获取错误信息
+     * ErrorException则使用错误级别作为错误编码
+     * @access protected
+     * @param Throwable $exception
+     * @return string                错误信息
+     */
+    protected function getMessage(Throwable $exception): string
+    {
+        $message = $exception->getMessage();
+
+        if ($this->app->runningInConsole()) {
+            return $message;
+        }
+
+        $lang = $this->app->lang;
+
+        if (strpos($message, ':')) {
+            $name    = strstr($message, ':', true);
+            $message = $lang->has($name) ? $lang->get($name) . strstr($message, ':') : $message;
+        } elseif (strpos($message, ',')) {
+            $name    = strstr($message, ',', true);
+            $message = $lang->has($name) ? $lang->get($name) . ':' . substr(strstr($message, ','), 1) : $message;
+        } elseif ($lang->has($message)) {
+            $message = $lang->get($message);
+        }
+
+        return $message;
+    }
+
+    /**
+     * 获取出错文件内容
+     * 获取错误的前9行和后9行
+     * @access protected
+     * @param Throwable $exception
+     * @return array                 错误文件内容
+     */
+    protected function getSourceCode(Throwable $exception): array
+    {
+        // 读取前9行和后9行
+        $line  = $exception->getLine();
+        $first = ($line - 9 > 0) ? $line - 9 : 1;
+
+        try {
+            $contents = file($exception->getFile()) ?: [];
+            $source   = [
+                'first'  => $first,
+                'source' => array_slice($contents, $first - 1, 19),
+            ];
+        } catch (Exception $e) {
+            $source = [];
+        }
+
+        return $source;
+    }
+
+    /**
+     * 获取异常扩展信息
+     * 用于非调试模式html返回类型显示
+     * @access protected
+     * @param Throwable $exception
+     * @return array                 异常类定义的扩展数据
+     */
+    protected function getExtendData(Throwable $exception): array
+    {
+        $data = [];
+
+        if ($exception instanceof \think\Exception) {
+            $data = $exception->getData();
+        }
+
+        return $data;
+    }
+
+    /**
+     * 获取常量列表
+     * @access protected
+     * @return array 常量列表
+     */
+    protected function getConst(): array
+    {
+        $const = get_defined_constants(true);
+
+        return $const['user'] ?? [];
+    }
+}
diff --git a/vendor/topthink/framework/src/think/exception/HttpException.php b/vendor/topthink/framework/src/think/exception/HttpException.php
new file mode 100755
index 0000000..45302e5
--- /dev/null
+++ b/vendor/topthink/framework/src/think/exception/HttpException.php
@@ -0,0 +1,42 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\exception;
+
+use Exception;
+
+/**
+ * HTTP异常
+ */
+class HttpException extends \RuntimeException
+{
+    private $statusCode;
+    private $headers;
+
+    public function __construct(int $statusCode, string $message = '', Exception $previous = null, array $headers = [], $code = 0)
+    {
+        $this->statusCode = $statusCode;
+        $this->headers    = $headers;
+
+        parent::__construct($message, $code, $previous);
+    }
+
+    public function getStatusCode()
+    {
+        return $this->statusCode;
+    }
+
+    public function getHeaders()
+    {
+        return $this->headers;
+    }
+}
diff --git a/vendor/topthink/framework/src/think/exception/HttpResponseException.php b/vendor/topthink/framework/src/think/exception/HttpResponseException.php
new file mode 100755
index 0000000..607813d
--- /dev/null
+++ b/vendor/topthink/framework/src/think/exception/HttpResponseException.php
@@ -0,0 +1,37 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\exception;
+
+use think\Response;
+
+/**
+ * HTTP响应异常
+ */
+class HttpResponseException extends \RuntimeException
+{
+    /**
+     * @var Response
+     */
+    protected $response;
+
+    public function __construct(Response $response)
+    {
+        $this->response = $response;
+    }
+
+    public function getResponse()
+    {
+        return $this->response;
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/exception/InvalidArgumentException.php b/vendor/topthink/framework/src/think/exception/InvalidArgumentException.php
new file mode 100755
index 0000000..8ccd6f6
--- /dev/null
+++ b/vendor/topthink/framework/src/think/exception/InvalidArgumentException.php
@@ -0,0 +1,22 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+namespace think\exception;
+
+use Psr\Cache\InvalidArgumentException as Psr6CacheInvalidArgumentInterface;
+use Psr\SimpleCache\InvalidArgumentException as SimpleCacheInvalidArgumentInterface;
+
+/**
+ * 非法数据异常
+ */
+class InvalidArgumentException extends \InvalidArgumentException implements Psr6CacheInvalidArgumentInterface, SimpleCacheInvalidArgumentInterface
+{
+}
diff --git a/vendor/topthink/framework/src/think/exception/RouteNotFoundException.php b/vendor/topthink/framework/src/think/exception/RouteNotFoundException.php
new file mode 100755
index 0000000..7a2ee87
--- /dev/null
+++ b/vendor/topthink/framework/src/think/exception/RouteNotFoundException.php
@@ -0,0 +1,26 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\exception;
+
+/**
+ * 路由未定义异常
+ */
+class RouteNotFoundException extends HttpException
+{
+
+    public function __construct()
+    {
+        parent::__construct(404, 'Route Not Found');
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/exception/ValidateException.php b/vendor/topthink/framework/src/think/exception/ValidateException.php
new file mode 100755
index 0000000..89b4e4d
--- /dev/null
+++ b/vendor/topthink/framework/src/think/exception/ValidateException.php
@@ -0,0 +1,37 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\exception;
+
+/**
+ * 数据验证异常
+ */
+class ValidateException extends \RuntimeException
+{
+    protected $error;
+
+    public function __construct($error)
+    {
+        $this->error   = $error;
+        $this->message = is_array($error) ? implode(PHP_EOL, $error) : $error;
+    }
+
+    /**
+     * 获取验证错误信息
+     * @access public
+     * @return array|string
+     */
+    public function getError()
+    {
+        return $this->error;
+    }
+}
diff --git a/vendor/topthink/framework/src/think/facade/App.php b/vendor/topthink/framework/src/think/facade/App.php
new file mode 100755
index 0000000..e9f8105
--- /dev/null
+++ b/vendor/topthink/framework/src/think/facade/App.php
@@ -0,0 +1,59 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\facade;
+
+use think\Facade;
+
+/**
+ * @see \think\App
+ * @package think\facade
+ * @mixin \think\App
+ * @method static \think\Service|null register(\think\Service|string $service, bool $force = false) 注册服务
+ * @method static mixed bootService(\think\Service $service) 执行服务
+ * @method static \think\Service|null getService(string|\think\Service $service) 获取服务
+ * @method static \think\App debug(bool $debug = true) 开启应用调试模式
+ * @method static bool isDebug() 是否为调试模式
+ * @method static \think\App setNamespace(string $namespace) 设置应用命名空间
+ * @method static string getNamespace() 获取应用类库命名空间
+ * @method static string version() 获取框架版本
+ * @method static string getRootPath() 获取应用根目录
+ * @method static string getBasePath() 获取应用基础目录
+ * @method static string getAppPath() 获取当前应用目录
+ * @method static mixed setAppPath(string $path) 设置应用目录
+ * @method static string getRuntimePath() 获取应用运行时目录
+ * @method static void setRuntimePath(string $path) 设置runtime目录
+ * @method static string getThinkPath() 获取核心框架目录
+ * @method static string getConfigPath() 获取应用配置目录
+ * @method static string getConfigExt() 获取配置后缀
+ * @method static float getBeginTime() 获取应用开启时间
+ * @method static integer getBeginMem() 获取应用初始内存占用
+ * @method static \think\App initialize() 初始化应用
+ * @method static bool initialized() 是否初始化过
+ * @method static void loadLangPack(string $langset) 加载语言包
+ * @method static void boot() 引导应用
+ * @method static void loadEvent(array $event) 注册应用事件
+ * @method static string parseClass(string $layer, string $name) 解析应用类的类名
+ * @method static bool runningInConsole() 是否运行在命令行下
+ */
+class App extends Facade
+{
+    /**
+     * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+     * @access protected
+     * @return string
+     */
+    protected static function getFacadeClass()
+    {
+        return 'app';
+    }
+}
diff --git a/vendor/topthink/framework/src/think/facade/Cache.php b/vendor/topthink/framework/src/think/facade/Cache.php
new file mode 100755
index 0000000..aac105d
--- /dev/null
+++ b/vendor/topthink/framework/src/think/facade/Cache.php
@@ -0,0 +1,48 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\facade;
+
+use think\cache\Driver;
+use think\cache\TagSet;
+use think\Facade;
+
+/**
+ * @see \think\Cache
+ * @package think\facade
+ * @mixin \think\Cache
+ * @method static string|null getDefaultDriver() 默认驱动
+ * @method static mixed getConfig(null|string $name = null, mixed $default = null) 获取缓存配置
+ * @method static array getStoreConfig(string $store, string $name = null, null $default = null) 获取驱动配置
+ * @method static Driver store(string $name = null) 连接或者切换缓存
+ * @method static bool clear() 清空缓冲池
+ * @method static mixed get(string $key, mixed $default = null) 读取缓存
+ * @method static bool set(string $key, mixed $value, int|\DateTime $ttl = null) 写入缓存
+ * @method static bool delete(string $key) 删除缓存
+ * @method static iterable getMultiple(iterable $keys, mixed $default = null) 读取缓存
+ * @method static bool setMultiple(iterable $values, null|int|\DateInterval $ttl = null) 写入缓存
+ * @method static bool deleteMultiple(iterable $keys) 删除缓存
+ * @method static bool has(string $key) 判断缓存是否存在
+ * @method static TagSet tag(string|array $name) 缓存标签
+ */
+class Cache extends Facade
+{
+    /**
+     * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+     * @access protected
+     * @return string
+     */
+    protected static function getFacadeClass()
+    {
+        return 'cache';
+    }
+}
diff --git a/vendor/topthink/framework/src/think/facade/Config.php b/vendor/topthink/framework/src/think/facade/Config.php
new file mode 100755
index 0000000..4ce73dd
--- /dev/null
+++ b/vendor/topthink/framework/src/think/facade/Config.php
@@ -0,0 +1,37 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\facade;
+
+use think\Facade;
+
+/**
+ * @see \think\Config
+ * @package think\facade
+ * @mixin \think\Config
+ * @method static array load(string $file, string $name = '') 加载配置文件(多种格式)
+ * @method static bool has(string $name) 检测配置是否存在
+ * @method static mixed get(string $name = null, mixed $default = null) 获取配置参数 为空则获取所有配置
+ * @method static array set(array $config, string $name = null) 设置配置参数 name为数组则为批量设置
+ */
+class Config extends Facade
+{
+    /**
+     * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+     * @access protected
+     * @return string
+     */
+    protected static function getFacadeClass()
+    {
+        return 'config';
+    }
+}
diff --git a/vendor/topthink/framework/src/think/facade/Console.php b/vendor/topthink/framework/src/think/facade/Console.php
new file mode 100755
index 0000000..30dd935
--- /dev/null
+++ b/vendor/topthink/framework/src/think/facade/Console.php
@@ -0,0 +1,56 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\facade;
+
+use think\console\Command;
+use think\console\Input;
+use think\console\input\Definition as InputDefinition;
+use think\console\Output;
+use think\console\output\driver\Buffer;
+use think\Facade;
+
+/**
+ * Class Console
+ * @package think\facade
+ * @mixin \think\Console
+ * @method static Output|Buffer call(string $command, array $parameters = [], string $driver = 'buffer')
+ * @method static int run() 执行当前的指令
+ * @method static int doRun(Input $input, Output $output) 执行指令
+ * @method static void setDefinition(InputDefinition $definition) 设置输入参数定义
+ * @method static InputDefinition The InputDefinition instance getDefinition() 获取输入参数定义
+ * @method static string A help message. getHelp() Gets the help message.
+ * @method static void setCatchExceptions(bool $boolean) 是否捕获异常
+ * @method static void setAutoExit(bool $boolean) 是否自动退出
+ * @method static string getLongVersion() 获取完整的版本号
+ * @method static void addCommands(array $commands) 添加指令集
+ * @method static Command|void addCommand(string|Command $command, string $name = '') 添加一个指令
+ * @method static Command getCommand(string $name) 获取指令
+ * @method static bool hasCommand(string $name) 某个指令是否存在
+ * @method static array getNamespaces() 获取所有的命名空间
+ * @method static string findNamespace(string $namespace) 查找注册命名空间中的名称或缩写。
+ * @method static Command find(string $name) 查找指令
+ * @method static Command[] all(string $namespace = null) 获取所有的指令
+ * @method static string extractNamespace(string $name, int $limit = 0) 返回命名空间部分
+ */
+class Console extends Facade
+{
+    /**
+     * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+     * @access protected
+     * @return string
+     */
+    protected static function getFacadeClass()
+    {
+        return 'console';
+    }
+}
diff --git a/vendor/topthink/framework/src/think/facade/Cookie.php b/vendor/topthink/framework/src/think/facade/Cookie.php
new file mode 100755
index 0000000..960f4a3
--- /dev/null
+++ b/vendor/topthink/framework/src/think/facade/Cookie.php
@@ -0,0 +1,40 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\facade;
+
+use think\Facade;
+
+/**
+ * @see \think\Cookie
+ * @package think\facade
+ * @mixin \think\Cookie
+ * @method static mixed get(mixed $name = '', string $default = null) 获取cookie
+ * @method static bool has(string $name) 是否存在Cookie参数
+ * @method static void set(string $name, string $value, mixed $option = null) Cookie 设置
+ * @method static void forever(string $name, string $value = '', mixed $option = null) 永久保存Cookie数据
+ * @method static void delete(string $name) Cookie删除
+ * @method static array getCookie() 获取cookie保存数据
+ * @method static void save() 保存Cookie
+ */
+class Cookie extends Facade
+{
+    /**
+     * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+     * @access protected
+     * @return string
+     */
+    protected static function getFacadeClass()
+    {
+        return 'cookie';
+    }
+}
diff --git a/vendor/topthink/framework/src/think/facade/Env.php b/vendor/topthink/framework/src/think/facade/Env.php
new file mode 100755
index 0000000..bed2538
--- /dev/null
+++ b/vendor/topthink/framework/src/think/facade/Env.php
@@ -0,0 +1,44 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\facade;
+
+use think\Facade;
+
+/**
+ * @see \think\Env
+ * @package think\facade
+ * @mixin \think\Env
+ * @method static void load(string $file) 读取环境变量定义文件
+ * @method static mixed get(string $name = null, mixed $default = null) 获取环境变量值
+ * @method static void set(string|array $env, mixed $value = null) 设置环境变量值
+ * @method static bool has(string $name) 检测是否存在环境变量
+ * @method static void __set(string $name, mixed $value) 设置环境变量
+ * @method static mixed __get(string $name) 获取环境变量
+ * @method static bool __isset(string $name) 检测是否存在环境变量
+ * @method static void offsetSet($name, $value)
+ * @method static bool offsetExists($name)
+ * @method static mixed offsetUnset($name)
+ * @method static mixed offsetGet($name)
+ */
+class Env extends Facade
+{
+    /**
+     * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+     * @access protected
+     * @return string
+     */
+    protected static function getFacadeClass()
+    {
+        return 'env';
+    }
+}
diff --git a/vendor/topthink/framework/src/think/facade/Event.php b/vendor/topthink/framework/src/think/facade/Event.php
new file mode 100755
index 0000000..c09d816
--- /dev/null
+++ b/vendor/topthink/framework/src/think/facade/Event.php
@@ -0,0 +1,42 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\facade;
+
+use think\Facade;
+
+/**
+ * @see \think\Event
+ * @package think\facade
+ * @mixin \think\Event
+ * @method static \think\Event listenEvents(array $events) 批量注册事件监听
+ * @method static \think\Event listen(string $event, mixed $listener, bool $first = false) 注册事件监听
+ * @method static bool hasListener(string $event) 是否存在事件监听
+ * @method static void remove(string $event) 移除事件监听
+ * @method static \think\Event bind(array $events) 指定事件别名标识 便于调用
+ * @method static \think\Event subscribe(mixed $subscriber) 注册事件订阅者
+ * @method static \think\Event observe(string|object $observer, null|string $prefix = '') 自动注册事件观察者
+ * @method static mixed trigger(string|object $event, mixed $params = null, bool $once = false) 触发事件
+ * @method static mixed until($event, $params = null) 触发事件(只获取一个有效返回值)
+ */
+class Event extends Facade
+{
+    /**
+     * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+     * @access protected
+     * @return string
+     */
+    protected static function getFacadeClass()
+    {
+        return 'event';
+    }
+}
diff --git a/vendor/topthink/framework/src/think/facade/Lang.php b/vendor/topthink/framework/src/think/facade/Lang.php
new file mode 100755
index 0000000..b460fe2
--- /dev/null
+++ b/vendor/topthink/framework/src/think/facade/Lang.php
@@ -0,0 +1,41 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\facade;
+
+use think\Facade;
+
+/**
+ * @see \think\Lang
+ * @package think\facade
+ * @mixin \think\Lang
+ * @method static void setLangSet(string $lang) 设置当前语言
+ * @method static string getLangSet() 获取当前语言
+ * @method static string defaultLangSet() 获取默认语言
+ * @method static array load(string|array $file, string $range = '') 加载语言定义(不区分大小写)
+ * @method static bool has(string|null $name, string $range = '') 判断是否存在语言定义(不区分大小写)
+ * @method static mixed get(string|null $name = null, array $vars = [], string $range = '') 获取语言定义(不区分大小写)
+ * @method static string detect(\think\Request $request) 自动侦测设置获取语言选择
+ * @method static void saveToCookie(\think\Cookie $cookie) 保存当前语言到Cookie
+ */
+class Lang extends Facade
+{
+    /**
+     * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+     * @access protected
+     * @return string
+     */
+    protected static function getFacadeClass()
+    {
+        return 'lang';
+    }
+}
diff --git a/vendor/topthink/framework/src/think/facade/Log.php b/vendor/topthink/framework/src/think/facade/Log.php
new file mode 100755
index 0000000..7c43d37
--- /dev/null
+++ b/vendor/topthink/framework/src/think/facade/Log.php
@@ -0,0 +1,58 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\facade;
+
+use think\Facade;
+use think\log\Channel;
+use think\log\ChannelSet;
+
+/**
+ * @see \think\Log
+ * @package think\facade
+ * @mixin \think\Log
+ * @method static string|null getDefaultDriver() 默认驱动
+ * @method static mixed getConfig(null|string $name = null, mixed $default = null) 获取日志配置
+ * @method static array getChannelConfig(string $channel, null $name = null, null $default = null) 获取渠道配置
+ * @method static Channel|ChannelSet channel(string|array $name = null) driver() 的别名
+ * @method static mixed createDriver(string $name)
+ * @method static \think\Log clear(string|array $channel = '*') 清空日志信息
+ * @method static \think\Log close(string|array $channel = '*') 关闭本次请求日志写入
+ * @method static array getLog(string $channel = null) 获取日志信息
+ * @method static bool save() 保存日志信息
+ * @method static \think\Log record(mixed $msg, string $type = 'info', array $context = [], bool $lazy = true) 记录日志信息
+ * @method static \think\Log write(mixed $msg, string $type = 'info', array $context = []) 实时写入日志信息
+ * @method static Event listen($listener) 注册日志写入事件监听
+ * @method static void log(string $level, mixed $message, array $context = []) 记录日志信息
+ * @method static void emergency(mixed $message, array $context = []) 记录emergency信息
+ * @method static void alert(mixed $message, array $context = []) 记录警报信息
+ * @method static void critical(mixed $message, array $context = []) 记录紧急情况
+ * @method static void error(mixed $message, array $context = []) 记录错误信息
+ * @method static void warning(mixed $message, array $context = []) 记录warning信息
+ * @method static void notice(mixed $message, array $context = []) 记录notice信息
+ * @method static void info(mixed $message, array $context = []) 记录一般信息
+ * @method static void debug(mixed $message, array $context = []) 记录调试信息
+ * @method static void sql(mixed $message, array $context = []) 记录sql信息
+ * @method static mixed __call($method, $parameters)
+ */
+class Log extends Facade
+{
+    /**
+     * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+     * @access protected
+     * @return string
+     */
+    protected static function getFacadeClass()
+    {
+        return 'log';
+    }
+}
diff --git a/vendor/topthink/framework/src/think/facade/Middleware.php b/vendor/topthink/framework/src/think/facade/Middleware.php
new file mode 100755
index 0000000..4203f82
--- /dev/null
+++ b/vendor/topthink/framework/src/think/facade/Middleware.php
@@ -0,0 +1,42 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\facade;
+
+use think\Facade;
+
+/**
+ * @see \think\Middleware
+ * @package think\facade
+ * @mixin \think\Middleware
+ * @method static void import(array $middlewares = [], string $type = 'global') 导入中间件
+ * @method static void add(mixed $middleware, string $type = 'global') 注册中间件
+ * @method static void route(mixed $middleware) 注册路由中间件
+ * @method static void controller(mixed $middleware) 注册控制器中间件
+ * @method static mixed unshift(mixed $middleware, string $type = 'global') 注册中间件到开始位置
+ * @method static array all(string $type = 'global') 获取注册的中间件
+ * @method static Pipeline pipeline(string $type = 'global') 调度管道
+ * @method static mixed end(\think\Response $response) 结束调度
+ * @method static \think\Response handleException(\think\Request $passable, \Throwable $e) 异常处理
+ */
+class Middleware extends Facade
+{
+    /**
+     * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+     * @access protected
+     * @return string
+     */
+    protected static function getFacadeClass()
+    {
+        return 'middleware';
+    }
+}
diff --git a/vendor/topthink/framework/src/think/facade/Request.php b/vendor/topthink/framework/src/think/facade/Request.php
new file mode 100755
index 0000000..6531f46
--- /dev/null
+++ b/vendor/topthink/framework/src/think/facade/Request.php
@@ -0,0 +1,134 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\facade;
+
+use think\Facade;
+use think\file\UploadedFile;
+use think\route\Rule;
+
+/**
+ * @see \think\Request
+ * @package think\facade
+ * @mixin \think\Request
+ * @method static \think\Request setDomain(string $domain) 设置当前包含协议的域名
+ * @method static string domain(bool $port = false) 获取当前包含协议的域名
+ * @method static string rootDomain() 获取当前根域名
+ * @method static \think\Request setSubDomain(string $domain) 设置当前泛域名的值
+ * @method static string subDomain() 获取当前子域名
+ * @method static \think\Request setPanDomain(string $domain) 设置当前泛域名的值
+ * @method static string panDomain() 获取当前泛域名的值
+ * @method static \think\Request setUrl(string $url) 设置当前完整URL 包括QUERY_STRING
+ * @method static string url(bool $complete = false) 获取当前完整URL 包括QUERY_STRING
+ * @method static \think\Request setBaseUrl(string $url) 设置当前URL 不含QUERY_STRING
+ * @method static string baseUrl(bool $complete = false) 获取当前URL 不含QUERY_STRING
+ * @method static string baseFile(bool $complete = false) 获取当前执行的文件 SCRIPT_NAME
+ * @method static \think\Request setRoot(string $url) 设置URL访问根地址
+ * @method static string root(bool $complete = false) 获取URL访问根地址
+ * @method static string rootUrl() 获取URL访问根目录
+ * @method static \think\Request setPathinfo(string $pathinfo) 设置当前请求的pathinfo
+ * @method static string pathinfo() 获取当前请求URL的pathinfo信息(含URL后缀)
+ * @method static string ext() 当前URL的访问后缀
+ * @method static integer|float time(bool $float = false) 获取当前请求的时间
+ * @method static string type() 当前请求的资源类型
+ * @method static void mimeType(string|array $type, string $val = '') 设置资源类型
+ * @method static \think\Request setMethod(string $method) 设置请求类型
+ * @method static string method(bool $origin = false) 当前的请求类型
+ * @method static bool isGet() 是否为GET请求
+ * @method static bool isPost() 是否为POST请求
+ * @method static bool isPut() 是否为PUT请求
+ * @method static bool isDelete() 是否为DELTE请求
+ * @method static bool isHead() 是否为HEAD请求
+ * @method static bool isPatch() 是否为PATCH请求
+ * @method static bool isOptions() 是否为OPTIONS请求
+ * @method static bool isCli() 是否为cli
+ * @method static bool isCgi() 是否为cgi
+ * @method static mixed param(string|array $name = '', mixed $default = null, string|array $filter = '') 获取当前请求的参数
+ * @method static \think\Request setRule(Rule $rule) 设置路由变量
+ * @method static Rule|null rule() 获取当前路由对象
+ * @method static \think\Request setRoute(array $route) 设置路由变量
+ * @method static mixed route(string|array $name = '', mixed $default = null, string|array $filter = '') 获取路由参数
+ * @method static mixed get(string|array $name = '', mixed $default = null, string|array $filter = '') 获取GET参数
+ * @method static mixed middleware(mixed $name, mixed $default = null) 获取中间件传递的参数
+ * @method static mixed post(string|array $name = '', mixed $default = null, string|array $filter = '') 获取POST参数
+ * @method static mixed put(string|array $name = '', mixed $default = null, string|array $filter = '') 获取PUT参数
+ * @method static mixed delete(mixed $name = '', mixed $default = null, string|array $filter = '') 设置获取DELETE参数
+ * @method static mixed patch(mixed $name = '', mixed $default = null, string|array $filter = '') 设置获取PATCH参数
+ * @method static mixed request(string|array $name = '', mixed $default = null, string|array $filter = '') 获取request变量
+ * @method static mixed env(string $name = '', string $default = null) 获取环境变量
+ * @method static mixed session(string $name = '', string $default = null) 获取session数据
+ * @method static mixed cookie(mixed $name = '', string $default = null, string|array $filter = '') 获取cookie参数
+ * @method static mixed server(string $name = '', string $default = '') 获取server参数
+ * @method static null|array|UploadedFile file(string $name = '') 获取上传的文件信息
+ * @method static string|array header(string $name = '', string $default = null) 设置或者获取当前的Header
+ * @method static mixed input(array $data = [], string|false $name = '', mixed $default = null, string|array $filter = '') 获取变量 支持过滤和默认值
+ * @method static mixed filter(mixed $filter = null) 设置或获取当前的过滤规则
+ * @method static mixed filterValue(mixed &$value, mixed $key, array $filters) 递归过滤给定的值
+ * @method static bool has(string $name, string $type = 'param', bool $checkEmpty = false) 是否存在某个请求参数
+ * @method static array only(array $name, mixed $data = 'param', string|array $filter = '') 获取指定的参数
+ * @method static mixed except(array $name, string $type = 'param') 排除指定参数获取
+ * @method static bool isSsl() 当前是否ssl
+ * @method static bool isJson() 当前是否JSON请求
+ * @method static bool isAjax(bool $ajax = false) 当前是否Ajax请求
+ * @method static bool isPjax(bool $pjax = false) 当前是否Pjax请求
+ * @method static string ip() 获取客户端IP地址
+ * @method static boolean isValidIP(string $ip, string $type = '') 检测是否是合法的IP地址
+ * @method static string ip2bin(string $ip) 将IP地址转换为二进制字符串
+ * @method static bool isMobile() 检测是否使用手机访问
+ * @method static string scheme() 当前URL地址中的scheme参数
+ * @method static string query() 当前请求URL地址中的query参数
+ * @method static \think\Request setHost(string $host) 设置当前请求的host(包含端口)
+ * @method static string host(bool $strict = false) 当前请求的host
+ * @method static int port() 当前请求URL地址中的port参数
+ * @method static string protocol() 当前请求 SERVER_PROTOCOL
+ * @method static int remotePort() 当前请求 REMOTE_PORT
+ * @method static string contentType() 当前请求 HTTP_CONTENT_TYPE
+ * @method static string secureKey() 获取当前请求的安全Key
+ * @method static \think\Request setController(string $controller) 设置当前的控制器名
+ * @method static \think\Request setAction(string $action) 设置当前的操作名
+ * @method static string controller(bool $convert = false) 获取当前的控制器名
+ * @method static string action(bool $convert = false) 获取当前的操作名
+ * @method static string getContent() 设置或者获取当前请求的content
+ * @method static string getInput() 获取当前请求的php://input
+ * @method static string buildToken(string $name = '__token__', mixed $type = 'md5') 生成请求令牌
+ * @method static bool checkToken(string $token = '__token__', array $data = []) 检查请求令牌
+ * @method static \think\Request withMiddleware(array $middleware) 设置在中间件传递的数据
+ * @method static \think\Request withGet(array $get) 设置GET数据
+ * @method static \think\Request withPost(array $post) 设置POST数据
+ * @method static \think\Request withCookie(array $cookie) 设置COOKIE数据
+ * @method static \think\Request withSession(Session $session) 设置SESSION数据
+ * @method static \think\Request withServer(array $server) 设置SERVER数据
+ * @method static \think\Request withHeader(array $header) 设置HEADER数据
+ * @method static \think\Request withEnv(Env $env) 设置ENV数据
+ * @method static \think\Request withInput(string $input) 设置php://input数据
+ * @method static \think\Request withFiles(array $files) 设置文件上传数据
+ * @method static \think\Request withRoute(array $route) 设置ROUTE变量
+ * @method static mixed __set(string $name, mixed $value) 设置中间传递数据
+ * @method static mixed __get(string $name) 获取中间传递数据的值
+ * @method static boolean __isset(string $name) 检测中间传递数据的值
+ * @method static bool offsetExists($name)
+ * @method static mixed offsetGet($name)
+ * @method static mixed offsetSet($name, $value)
+ * @method static mixed offsetUnset($name)
+ */
+class Request extends Facade
+{
+    /**
+     * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+     * @access protected
+     * @return string
+     */
+    protected static function getFacadeClass()
+    {
+        return 'request';
+    }
+}
diff --git a/vendor/topthink/framework/src/think/facade/Route.php b/vendor/topthink/framework/src/think/facade/Route.php
new file mode 100755
index 0000000..37504ce
--- /dev/null
+++ b/vendor/topthink/framework/src/think/facade/Route.php
@@ -0,0 +1,84 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\facade;
+
+use think\Facade;
+use think\route\Dispatch;
+use think\route\Domain;
+use think\route\Rule;
+use think\route\RuleGroup;
+use think\route\RuleItem;
+use think\route\RuleName;
+use think\route\Url as UrlBuild;
+
+/**
+ * @see \think\Route
+ * @package think\facade
+ * @mixin \think\Route
+ * @method static mixed config(string $name = null)
+ * @method static \think\Route lazy(bool $lazy = true) 设置路由域名及分组(包括资源路由)是否延迟解析
+ * @method static void setTestMode(bool $test) 设置路由为测试模式
+ * @method static bool isTest() 检查路由是否为测试模式
+ * @method static \think\Route mergeRuleRegex(bool $merge = true) 设置路由域名及分组(包括资源路由)是否合并解析
+ * @method static void setGroup(RuleGroup $group) 设置当前分组
+ * @method static RuleGroup getGroup(string $name = null) 获取指定标识的路由分组 不指定则获取当前分组
+ * @method static \think\Route pattern(array $pattern) 注册变量规则
+ * @method static \think\Route option(array $option) 注册路由参数
+ * @method static Domain domain(string|array $name, mixed $rule = null) 注册域名路由
+ * @method static array getDomains() 获取域名
+ * @method static RuleName getRuleName() 获取RuleName对象
+ * @method static \think\Route bind(string $bind, string $domain = null) 设置路由绑定
+ * @method static array getBind() 读取路由绑定信息
+ * @method static string|null getDomainBind(string $domain = null) 读取路由绑定
+ * @method static RuleItem[] getName(string $name = null, string $domain = null, string $method = '*') 读取路由标识
+ * @method static void import(array $name) 批量导入路由标识
+ * @method static void setName(string $name, RuleItem $ruleItem, bool $first = false) 注册路由标识
+ * @method static void setRule(string $rule, RuleItem $ruleItem = null) 保存路由规则
+ * @method static RuleItem[] getRule(string $rule) 读取路由
+ * @method static array getRuleList() 读取路由列表
+ * @method static void clear() 清空路由规则
+ * @method static RuleItem rule(string $rule, mixed $route = null, string $method = '*') 注册路由规则
+ * @method static \think\Route setCrossDomainRule(Rule $rule, string $method = '*') 设置跨域有效路由规则
+ * @method static RuleGroup group(string|\Closure $name, mixed $route = null) 注册路由分组
+ * @method static RuleItem any(string $rule, mixed $route) 注册路由
+ * @method static RuleItem get(string $rule, mixed $route) 注册GET路由
+ * @method static RuleItem post(string $rule, mixed $route) 注册POST路由
+ * @method static RuleItem put(string $rule, mixed $route) 注册PUT路由
+ * @method static RuleItem delete(string $rule, mixed $route) 注册DELETE路由
+ * @method static RuleItem patch(string $rule, mixed $route) 注册PATCH路由
+ * @method static RuleItem head(string $rule, mixed $route) 注册HEAD路由
+ * @method static RuleItem options(string $rule, mixed $route) 注册OPTIONS路由
+ * @method static Resource resource(string $rule, string $route) 注册资源路由
+ * @method static RuleItem view(string $rule, string $template = '', array $vars = []) 注册视图路由
+ * @method static RuleItem redirect(string $rule, string $route = '', int $status = 301) 注册重定向路由
+ * @method static \think\Route rest(string|array $name, array|bool $resource = []) rest方法定义和修改
+ * @method static array|null getRest(string $name = null) 获取rest方法定义的参数
+ * @method static RuleItem miss(string|\Closure $route, string $method = '*') 注册未匹配路由规则后的处理
+ * @method static Response dispatch(\think\Request $request, Closure|bool $withRoute = true) 路由调度
+ * @method static Dispatch|false check() 检测URL路由
+ * @method static Dispatch url(string $url) 默认URL解析
+ * @method static UrlBuild buildUrl(string $url = '', array $vars = []) URL生成 支持路由反射
+ * @method static RuleGroup __call(string $method, array $args) 设置全局的路由分组参数
+ */
+class Route extends Facade
+{
+    /**
+     * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+     * @access protected
+     * @return string
+     */
+    protected static function getFacadeClass()
+    {
+        return 'route';
+    }
+}
diff --git a/vendor/topthink/framework/src/think/facade/Session.php b/vendor/topthink/framework/src/think/facade/Session.php
new file mode 100755
index 0000000..68bf993
--- /dev/null
+++ b/vendor/topthink/framework/src/think/facade/Session.php
@@ -0,0 +1,35 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\facade;
+
+use think\Facade;
+
+/**
+ * @see \think\Session
+ * @package think\facade
+ * @mixin \think\Session
+ * @method static mixed getConfig(null|string $name = null, mixed $default = null) 获取Session配置
+ * @method static string|null getDefaultDriver() 默认驱动
+ */
+class Session extends Facade
+{
+    /**
+     * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+     * @access protected
+     * @return string
+     */
+    protected static function getFacadeClass()
+    {
+        return 'session';
+    }
+}
diff --git a/vendor/topthink/framework/src/think/facade/Validate.php b/vendor/topthink/framework/src/think/facade/Validate.php
new file mode 100755
index 0000000..6db6d34
--- /dev/null
+++ b/vendor/topthink/framework/src/think/facade/Validate.php
@@ -0,0 +1,95 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\facade;
+
+use think\Facade;
+
+/**
+ * @see \think\Validate
+ * @package think\facade
+ * @mixin \think\Validate
+ * @method static void setLang(\think\Lang $lang) 设置Lang对象
+ * @method static void setDb(\think\Db $db) 设置Db对象
+ * @method static void setRequest(\think\Request $request) 设置Request对象
+ * @method static \think\Validate rule(string|array $name, mixed $rule = '') 添加字段验证规则
+ * @method static \think\Validate extend(string $type, callable $callback = null, string $message = null) 注册验证(类型)规则
+ * @method static void setTypeMsg(string|array $type, string $msg = null) 设置验证规则的默认提示信息
+ * @method static Validate message(array $message) 设置提示信息
+ * @method static \think\Validate scene(string $name) 设置验证场景
+ * @method static bool hasScene(string $name) 判断是否存在某个验证场景
+ * @method static \think\Validate batch(bool $batch = true) 设置批量验证
+ * @method static \think\Validate failException(bool $fail = true) 设置验证失败后是否抛出异常
+ * @method static \think\Validate only(array $fields) 指定需要验证的字段列表
+ * @method static \think\Validate remove(string|array $field, mixed $rule = null) 移除某个字段的验证规则
+ * @method static \think\Validate append(string|array $field, mixed $rule = null) 追加某个字段的验证规则
+ * @method static bool check(array $data, array $rules = []) 数据自动验证
+ * @method static bool checkRule(mixed $value, mixed $rules) 根据验证规则验证数据
+ * @method static bool confirm(mixed $value, mixed $rule, array $data = [], string $field = '') 验证是否和某个字段的值一致
+ * @method static bool different(mixed $value, mixed $rule, array $data = []) 验证是否和某个字段的值是否不同
+ * @method static bool egt(mixed $value, mixed $rule, array $data = []) 验证是否大于等于某个值
+ * @method static bool gt(mixed $value, mixed $rule, array $data = []) 验证是否大于某个值
+ * @method static bool elt(mixed $value, mixed $rule, array $data = []) 验证是否小于等于某个值
+ * @method static bool lt(mixed $value, mixed $rule, array $data = []) 验证是否小于某个值
+ * @method static bool eq(mixed $value, mixed $rule) 验证是否等于某个值
+ * @method static bool must(mixed $value, mixed $rule = null) 必须验证
+ * @method static bool is(mixed $value, string $rule, array $data = []) 验证字段值是否为有效格式
+ * @method static bool token(mixed $value, mixed $rule, array $data) 验证表单令牌
+ * @method static bool activeUrl(mixed $value, mixed $rule = 'MX') 验证是否为合格的域名或者IP 支持A,MX,NS,SOA,PTR,CNAME,AAAA,A6, SRV,NAPTR,TXT 或者 ANY类型
+ * @method static bool ip(mixed $value, mixed $rule = 'ipv4') 验证是否有效IP
+ * @method static bool fileExt(mixed $file, mixed $rule) 验证上传文件后缀
+ * @method static bool fileMime(mixed $file, mixed $rule) 验证上传文件类型
+ * @method static bool fileSize(mixed $file, mixed $rule) 验证上传文件大小
+ * @method static bool image(mixed $file, mixed $rule) 验证图片的宽高及类型
+ * @method static bool dateFormat(mixed $value, mixed $rule) 验证时间和日期是否符合指定格式
+ * @method static bool unique(mixed $value, mixed $rule, array $data = [], string $field = '') 验证是否唯一
+ * @method static bool filter(mixed $value, mixed $rule) 使用filter_var方式验证
+ * @method static bool requireIf(mixed $value, mixed $rule, array $data = []) 验证某个字段等于某个值的时候必须
+ * @method static bool requireCallback(mixed $value, mixed $rule, array $data = []) 通过回调方法验证某个字段是否必须
+ * @method static bool requireWith(mixed $value, mixed $rule, array $data = []) 验证某个字段有值的情况下必须
+ * @method static bool requireWithout(mixed $value, mixed $rule, array $data = []) 验证某个字段没有值的情况下必须
+ * @method static bool in(mixed $value, mixed $rule) 验证是否在范围内
+ * @method static bool notIn(mixed $value, mixed $rule) 验证是否不在某个范围
+ * @method static bool between(mixed $value, mixed $rule) between验证数据
+ * @method static bool notBetween(mixed $value, mixed $rule) 使用notbetween验证数据
+ * @method static bool length(mixed $value, mixed $rule) 验证数据长度
+ * @method static bool max(mixed $value, mixed $rule) 验证数据最大长度
+ * @method static bool min(mixed $value, mixed $rule) 验证数据最小长度
+ * @method static bool after(mixed $value, mixed $rule, array $data = []) 验证日期
+ * @method static bool before(mixed $value, mixed $rule, array $data = []) 验证日期
+ * @method static bool afterWith(mixed $value, mixed $rule, array $data = []) 验证日期
+ * @method static bool beforeWith(mixed $value, mixed $rule, array $data = []) 验证日期
+ * @method static bool expire(mixed $value, mixed $rule) 验证有效期
+ * @method static bool allowIp(mixed $value, mixed $rule) 验证IP许可
+ * @method static bool denyIp(mixed $value, mixed $rule) 验证IP禁用
+ * @method static bool regex(mixed $value, mixed $rule) 使用正则验证数据
+ * @method static array|string getError() 获取错误信息
+ * @method static bool __call(string $method, array $args) 动态方法 直接调用is方法进行验证
+ */
+class Validate extends Facade
+{
+    /**
+     * 始终创建新的对象实例
+     * @var bool
+     */
+    protected static $alwaysNewInstance = true;
+
+    /**
+     * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+     * @access protected
+     * @return string
+     */
+    protected static function getFacadeClass()
+    {
+        return 'validate';
+    }
+}
diff --git a/vendor/topthink/framework/src/think/facade/View.php b/vendor/topthink/framework/src/think/facade/View.php
new file mode 100755
index 0000000..acde3b5
--- /dev/null
+++ b/vendor/topthink/framework/src/think/facade/View.php
@@ -0,0 +1,42 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\facade;
+
+use think\Facade;
+
+/**
+ * @see \think\View
+ * @package think\facade
+ * @mixin \think\View
+ * @method static \think\View engine(string $type = null) 获取模板引擎
+ * @method static \think\View assign(string|array $name, mixed $value = null) 模板变量赋值
+ * @method static \think\View filter(\think\Callable $filter = null) 视图过滤
+ * @method static string fetch(string $template = '', array $vars = []) 解析和获取模板内容 用于输出
+ * @method static string display(string $content, array $vars = []) 渲染内容输出
+ * @method static mixed __set(string $name, mixed $value) 模板变量赋值
+ * @method static mixed __get(string $name) 取得模板显示变量的值
+ * @method static bool __isset(string $name) 检测模板变量是否设置
+ * @method static string|null getDefaultDriver() 默认驱动
+ */
+class View extends Facade
+{
+    /**
+     * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+     * @access protected
+     * @return string
+     */
+    protected static function getFacadeClass()
+    {
+        return 'view';
+    }
+}
diff --git a/vendor/topthink/framework/src/think/file/UploadedFile.php b/vendor/topthink/framework/src/think/file/UploadedFile.php
new file mode 100755
index 0000000..7dff766
--- /dev/null
+++ b/vendor/topthink/framework/src/think/file/UploadedFile.php
@@ -0,0 +1,143 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\file;
+
+use think\exception\FileException;
+use think\File;
+
+class UploadedFile extends File
+{
+
+    private $test = false;
+    private $originalName;
+    private $mimeType;
+    private $error;
+
+    public function __construct(string $path, string $originalName, string $mimeType = null, int $error = null, bool $test = false)
+    {
+        $this->originalName = $originalName;
+        $this->mimeType     = $mimeType ?: 'application/octet-stream';
+        $this->test         = $test;
+        $this->error        = $error ?: UPLOAD_ERR_OK;
+
+        parent::__construct($path, UPLOAD_ERR_OK === $this->error);
+    }
+
+    public function isValid(): bool
+    {
+        $isOk = UPLOAD_ERR_OK === $this->error;
+
+        return $this->test ? $isOk : $isOk && is_uploaded_file($this->getPathname());
+    }
+
+    /**
+     * 上传文件
+     * @access public
+     * @param string      $directory 保存路径
+     * @param string|null $name      保存的文件名
+     * @return File
+     */
+    public function move(string $directory, string $name = null): File
+    {
+        if ($this->isValid()) {
+            if ($this->test) {
+                return parent::move($directory, $name);
+            }
+
+            $target = $this->getTargetFile($directory, $name);
+
+            set_error_handler(function ($type, $msg) use (&$error) {
+                $error = $msg;
+            });
+
+            $moved = move_uploaded_file($this->getPathname(), (string) $target);
+            restore_error_handler();
+            if (!$moved) {
+                throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error)));
+            }
+
+            @chmod((string) $target, 0666 & ~umask());
+
+            return $target;
+        }
+
+        throw new FileException($this->getErrorMessage());
+    }
+
+    /**
+     * 获取错误信息
+     * @access public
+     * @return string
+     */
+    protected function getErrorMessage(): string
+    {
+        switch ($this->error) {
+            case 1:
+            case 2:
+                $message = 'upload File size exceeds the maximum value';
+                break;
+            case 3:
+                $message = 'only the portion of file is uploaded';
+                break;
+            case 4:
+                $message = 'no file to uploaded';
+                break;
+            case 6:
+                $message = 'upload temp dir not found';
+                break;
+            case 7:
+                $message = 'file write error';
+                break;
+            default:
+                $message = 'unknown upload error';
+        }
+
+        return $message;
+    }
+
+    /**
+     * 获取上传文件类型信息
+     * @return string
+     */
+    public function getOriginalMime(): string
+    {
+        return $this->mimeType;
+    }
+
+    /**
+     * 上传文件名
+     * @return string
+     */
+    public function getOriginalName(): string
+    {
+        return $this->originalName;
+    }
+
+    /**
+     * 获取上传文件扩展名
+     * @return string
+     */
+    public function getOriginalExtension(): string
+    {
+        return pathinfo($this->originalName, PATHINFO_EXTENSION);
+    }
+
+    /**
+     * 获取文件扩展名
+     * @return string
+     */
+    public function extension(): string
+    {
+        return $this->getOriginalExtension();
+    }
+}
diff --git a/vendor/topthink/framework/src/think/initializer/BootService.php b/vendor/topthink/framework/src/think/initializer/BootService.php
new file mode 100755
index 0000000..bab6d39
--- /dev/null
+++ b/vendor/topthink/framework/src/think/initializer/BootService.php
@@ -0,0 +1,26 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\initializer;
+
+use think\App;
+
+/**
+ * 启动系统服务
+ */
+class BootService
+{
+    public function init(App $app)
+    {
+        $app->boot();
+    }
+}
diff --git a/vendor/topthink/framework/src/think/initializer/Error.php b/vendor/topthink/framework/src/think/initializer/Error.php
new file mode 100755
index 0000000..201d947
--- /dev/null
+++ b/vendor/topthink/framework/src/think/initializer/Error.php
@@ -0,0 +1,117 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\initializer;
+
+use think\App;
+use think\console\Output as ConsoleOutput;
+use think\exception\ErrorException;
+use think\exception\Handle;
+use Throwable;
+
+/**
+ * 错误和异常处理
+ */
+class Error
+{
+    /** @var App */
+    protected $app;
+
+    /**
+     * 注册异常处理
+     * @access public
+     * @param App $app
+     * @return void
+     */
+    public function init(App $app)
+    {
+        $this->app = $app;
+        error_reporting(E_ALL);
+        set_error_handler([$this, 'appError']);
+        set_exception_handler([$this, 'appException']);
+        register_shutdown_function([$this, 'appShutdown']);
+    }
+
+    /**
+     * Exception Handler
+     * @access public
+     * @param \Throwable $e
+     */
+    public function appException(Throwable $e): void
+    {
+        $handler = $this->getExceptionHandler();
+
+        $handler->report($e);
+
+        if ($this->app->runningInConsole()) {
+            $handler->renderForConsole(new ConsoleOutput, $e);
+        } else {
+            $handler->render($this->app->request, $e)->send();
+        }
+    }
+
+    /**
+     * Error Handler
+     * @access public
+     * @param integer $errno   错误编号
+     * @param string  $errstr  详细错误信息
+     * @param string  $errfile 出错的文件
+     * @param integer $errline 出错行号
+     * @throws ErrorException
+     */
+    public function appError(int $errno, string $errstr, string $errfile = '', int $errline = 0): void
+    {
+        $exception = new ErrorException($errno, $errstr, $errfile, $errline);
+
+        if (error_reporting() & $errno) {
+            // 将错误信息托管至 think\exception\ErrorException
+            throw $exception;
+        }
+    }
+
+    /**
+     * Shutdown Handler
+     * @access public
+     */
+    public function appShutdown(): void
+    {
+        if (!is_null($error = error_get_last()) && $this->isFatal($error['type'])) {
+            // 将错误信息托管至think\ErrorException
+            $exception = new ErrorException($error['type'], $error['message'], $error['file'], $error['line']);
+
+            $this->appException($exception);
+        }
+    }
+
+    /**
+     * 确定错误类型是否致命
+     *
+     * @access protected
+     * @param int $type
+     * @return bool
+     */
+    protected function isFatal(int $type): bool
+    {
+        return in_array($type, [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE]);
+    }
+
+    /**
+     * Get an instance of the exception handler.
+     *
+     * @access protected
+     * @return Handle
+     */
+    protected function getExceptionHandler()
+    {
+        return $this->app->make(Handle::class);
+    }
+}
diff --git a/vendor/topthink/framework/src/think/initializer/RegisterService.php b/vendor/topthink/framework/src/think/initializer/RegisterService.php
new file mode 100755
index 0000000..b682a0b
--- /dev/null
+++ b/vendor/topthink/framework/src/think/initializer/RegisterService.php
@@ -0,0 +1,48 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\initializer;
+
+use think\App;
+use think\service\ModelService;
+use think\service\PaginatorService;
+use think\service\ValidateService;
+
+/**
+ * 注册系统服务
+ */
+class RegisterService
+{
+
+    protected $services = [
+        PaginatorService::class,
+        ValidateService::class,
+        ModelService::class,
+    ];
+
+    public function init(App $app)
+    {
+        $file = $app->getRootPath() . 'vendor/services.php';
+
+        $services = $this->services;
+
+        if (is_file($file)) {
+            $services = array_merge($services, include $file);
+        }
+
+        foreach ($services as $service) {
+            if (class_exists($service)) {
+                $app->register($service);
+            }
+        }
+    }
+}
diff --git a/vendor/topthink/framework/src/think/log/Channel.php b/vendor/topthink/framework/src/think/log/Channel.php
new file mode 100755
index 0000000..1de96f1
--- /dev/null
+++ b/vendor/topthink/framework/src/think/log/Channel.php
@@ -0,0 +1,286 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\log;
+
+use Psr\Log\LoggerInterface;
+use think\contract\LogHandlerInterface;
+use think\Event;
+use think\event\LogRecord;
+use think\event\LogWrite;
+
+class Channel implements LoggerInterface
+{
+    protected $name;
+    protected $logger;
+    protected $event;
+
+    protected $lazy = true;
+    /**
+     * 日志信息
+     * @var array
+     */
+    protected $log = [];
+
+    /**
+     * 关闭日志
+     * @var array
+     */
+    protected $close = false;
+
+    /**
+     * 允许写入类型
+     * @var array
+     */
+    protected $allow = [];
+
+    public function __construct(string $name, LogHandlerInterface $logger, array $allow, bool $lazy = true, Event $event = null)
+    {
+        $this->name   = $name;
+        $this->logger = $logger;
+        $this->allow  = $allow;
+        $this->lazy   = $lazy;
+        $this->event  = $event;
+    }
+
+    /**
+     * 关闭通道
+     */
+    public function close()
+    {
+        $this->clear();
+        $this->close = true;
+    }
+
+    /**
+     * 清空日志
+     */
+    public function clear()
+    {
+        $this->log = [];
+    }
+
+    /**
+     * 记录日志信息
+     * @access public
+     * @param mixed  $msg     日志信息
+     * @param string $type    日志级别
+     * @param array  $context 替换内容
+     * @param bool   $lazy
+     * @return $this
+     */
+    public function record($msg, string $type = 'info', array $context = [], bool $lazy = true)
+    {
+        if ($this->close || (!empty($this->allow) && !in_array($type, $this->allow))) {
+            return $this;
+        }
+
+        if (is_string($msg) && !empty($context)) {
+            $replace = [];
+            foreach ($context as $key => $val) {
+                $replace['{' . $key . '}'] = $val;
+            }
+
+            $msg = strtr($msg, $replace);
+        }
+
+        if (!empty($msg) || 0 === $msg) {
+            $this->log[$type][] = $msg;
+            if ($this->event) {
+                $this->event->trigger(new LogRecord($type, $msg));
+            }
+        }
+
+        if (!$this->lazy || !$lazy) {
+            $this->save();
+        }
+
+        return $this;
+    }
+
+    /**
+     * 实时写入日志信息
+     * @access public
+     * @param mixed  $msg     调试信息
+     * @param string $type    日志级别
+     * @param array  $context 替换内容
+     * @return $this
+     */
+    public function write($msg, string $type = 'info', array $context = [])
+    {
+        return $this->record($msg, $type, $context, false);
+    }
+
+    /**
+     * 获取日志信息
+     * @return array
+     */
+    public function getLog(): array
+    {
+        return $this->log;
+    }
+
+    /**
+     * 保存日志
+     * @return bool
+     */
+    public function save(): bool
+    {
+        $log = $this->log;
+        if ($this->event) {
+            $event = new LogWrite($this->name, $log);
+            $this->event->trigger($event);
+            $log = $event->log;
+        }
+
+        if ($this->logger->save($log)) {
+            $this->clear();
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * System is unusable.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function emergency($message, array $context = [])
+    {
+        $this->log(__FUNCTION__, $message, $context);
+    }
+
+    /**
+     * Action must be taken immediately.
+     *
+     * Example: Entire website down, database unavailable, etc. This should
+     * trigger the SMS alerts and wake you up.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function alert($message, array $context = [])
+    {
+        $this->log(__FUNCTION__, $message, $context);
+    }
+
+    /**
+     * Critical conditions.
+     *
+     * Example: Application component unavailable, unexpected exception.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function critical($message, array $context = [])
+    {
+        $this->log(__FUNCTION__, $message, $context);
+    }
+
+    /**
+     * Runtime errors that do not require immediate action but should typically
+     * be logged and monitored.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function error($message, array $context = [])
+    {
+        $this->log(__FUNCTION__, $message, $context);
+    }
+
+    /**
+     * Exceptional occurrences that are not errors.
+     *
+     * Example: Use of deprecated APIs, poor use of an API, undesirable things
+     * that are not necessarily wrong.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function warning($message, array $context = [])
+    {
+        $this->log(__FUNCTION__, $message, $context);
+    }
+
+    /**
+     * Normal but significant events.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function notice($message, array $context = [])
+    {
+        $this->log(__FUNCTION__, $message, $context);
+    }
+
+    /**
+     * Interesting events.
+     *
+     * Example: User logs in, SQL logs.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function info($message, array $context = [])
+    {
+        $this->log(__FUNCTION__, $message, $context);
+    }
+
+    /**
+     * Detailed debug information.
+     *
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function debug($message, array $context = [])
+    {
+        $this->log(__FUNCTION__, $message, $context);
+    }
+
+    /**
+     * Logs with an arbitrary level.
+     *
+     * @param mixed  $level
+     * @param string $message
+     * @param array  $context
+     *
+     * @return void
+     */
+    public function log($level, $message, array $context = [])
+    {
+        $this->record($message, $level, $context);
+    }
+
+    public function __call($method, $parameters)
+    {
+        $this->log($method, ...$parameters);
+    }
+}
diff --git a/vendor/topthink/framework/src/think/log/ChannelSet.php b/vendor/topthink/framework/src/think/log/ChannelSet.php
new file mode 100755
index 0000000..6dcb0bd
--- /dev/null
+++ b/vendor/topthink/framework/src/think/log/ChannelSet.php
@@ -0,0 +1,39 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\log;
+
+use think\Log;
+
+/**
+ * Class ChannelSet
+ * @package think\log
+ * @mixin Channel
+ */
+class ChannelSet
+{
+    protected $log;
+    protected $channels;
+
+    public function __construct(Log $log, array $channels)
+    {
+        $this->log      = $log;
+        $this->channels = $channels;
+    }
+
+    public function __call($method, $arguments)
+    {
+        foreach ($this->channels as $channel) {
+            $this->log->channel($channel)->{$method}(...$arguments);
+        }
+    }
+}
diff --git a/vendor/topthink/framework/src/think/log/driver/File.php b/vendor/topthink/framework/src/think/log/driver/File.php
new file mode 100755
index 0000000..1f726b0
--- /dev/null
+++ b/vendor/topthink/framework/src/think/log/driver/File.php
@@ -0,0 +1,207 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\log\driver;
+
+use think\App;
+use think\contract\LogHandlerInterface;
+
+/**
+ * 本地化调试输出到文件
+ */
+class File implements LogHandlerInterface
+{
+    /**
+     * 配置参数
+     * @var array
+     */
+    protected $config = [
+        'time_format'  => 'c',
+        'single'       => false,
+        'file_size'    => 2097152,
+        'path'         => '',
+        'apart_level'  => [],
+        'max_files'    => 0,
+        'json'         => false,
+        'json_options' => JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES,
+        'format'       => '[%s][%s] %s',
+    ];
+
+    // 实例化并传入参数
+    public function __construct(App $app, $config = [])
+    {
+        if (is_array($config)) {
+            $this->config = array_merge($this->config, $config);
+        }
+
+        if (empty($this->config['format'])) {
+            $this->config['format'] = '[%s][%s] %s';
+        }
+
+        if (empty($this->config['path'])) {
+            $this->config['path'] = $app->getRuntimePath() . 'log';
+        }
+
+        if (substr($this->config['path'], -1) != DIRECTORY_SEPARATOR) {
+            $this->config['path'] .= DIRECTORY_SEPARATOR;
+        }
+    }
+
+    /**
+     * 日志写入接口
+     * @access public
+     * @param array $log 日志信息
+     * @return bool
+     */
+    public function save(array $log): bool
+    {
+        $destination = $this->getMasterLogFile();
+
+        $path = dirname($destination);
+        !is_dir($path) && mkdir($path, 0755, true);
+
+        $info = [];
+
+        // 日志信息封装
+        $time = \DateTime::createFromFormat('0.u00 U', microtime())->setTimezone(new \DateTimeZone(date_default_timezone_get()))->format($this->config['time_format']);
+
+        foreach ($log as $type => $val) {
+            $message = [];
+            foreach ($val as $msg) {
+                if (!is_string($msg)) {
+                    $msg = var_export($msg, true);
+                }
+
+                $message[] = $this->config['json'] ?
+                json_encode(['time' => $time, 'type' => $type, 'msg' => $msg], $this->config['json_options']) :
+                sprintf($this->config['format'], $time, $type, $msg);
+            }
+
+            if (true === $this->config['apart_level'] || in_array($type, $this->config['apart_level'])) {
+                // 独立记录的日志级别
+                $filename = $this->getApartLevelFile($path, $type);
+                $this->write($message, $filename);
+                continue;
+            }
+
+            $info[$type] = $message;
+        }
+
+        if ($info) {
+            return $this->write($info, $destination);
+        }
+
+        return true;
+    }
+
+    /**
+     * 日志写入
+     * @access protected
+     * @param array  $message     日志信息
+     * @param string $destination 日志文件
+     * @return bool
+     */
+    protected function write(array $message, string $destination): bool
+    {
+        // 检测日志文件大小,超过配置大小则备份日志文件重新生成
+        $this->checkLogSize($destination);
+
+        $info = [];
+
+        foreach ($message as $type => $msg) {
+            $info[$type] = is_array($msg) ? implode(PHP_EOL, $msg) : $msg;
+        }
+
+        $message = implode(PHP_EOL, $info) . PHP_EOL;
+
+        return error_log($message, 3, $destination);
+    }
+
+    /**
+     * 获取主日志文件名
+     * @access public
+     * @return string
+     */
+    protected function getMasterLogFile(): string
+    {
+
+        if ($this->config['max_files']) {
+            $files = glob($this->config['path'] . '*.log');
+
+            try {
+                if (count($files) > $this->config['max_files']) {
+                    set_error_handler(function ($errno, $errstr, $errfile, $errline) {});
+                    unlink($files[0]);
+                    restore_error_handler();
+                }
+            } catch (\Exception $e) {
+                //
+            }
+        }
+
+        if ($this->config['single']) {
+            $name        = is_string($this->config['single']) ? $this->config['single'] : 'single';
+            $destination = $this->config['path'] . $name . '.log';
+        } else {
+
+            if ($this->config['max_files']) {
+                $filename = date('Ymd') . '.log';
+            } else {
+                $filename = date('Ym') . DIRECTORY_SEPARATOR . date('d') . '.log';
+            }
+
+            $destination = $this->config['path'] . $filename;
+        }
+
+        return $destination;
+    }
+
+    /**
+     * 获取独立日志文件名
+     * @access public
+     * @param string $path 日志目录
+     * @param string $type 日志类型
+     * @return string
+     */
+    protected function getApartLevelFile(string $path, string $type): string
+    {
+
+        if ($this->config['single']) {
+            $name = is_string($this->config['single']) ? $this->config['single'] : 'single';
+
+            $name .= '_' . $type;
+        } elseif ($this->config['max_files']) {
+            $name = date('Ymd') . '_' . $type;
+        } else {
+            $name = date('d') . '_' . $type;
+        }
+
+        return $path . DIRECTORY_SEPARATOR . $name . '.log';
+    }
+
+    /**
+     * 检查日志文件大小并自动生成备份文件
+     * @access protected
+     * @param string $destination 日志文件
+     * @return void
+     */
+    protected function checkLogSize(string $destination): void
+    {
+        if (is_file($destination) && floor($this->config['file_size']) <= filesize($destination)) {
+            try {
+                rename($destination, dirname($destination) . DIRECTORY_SEPARATOR . time() . '-' . basename($destination));
+            } catch (\Exception $e) {
+                //
+            }
+        }
+    }
+}
diff --git a/vendor/topthink/framework/src/think/log/driver/Socket.php b/vendor/topthink/framework/src/think/log/driver/Socket.php
new file mode 100755
index 0000000..2cfb943
--- /dev/null
+++ b/vendor/topthink/framework/src/think/log/driver/Socket.php
@@ -0,0 +1,311 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: luofei614 <weibo.com/luofei614>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\log\driver;
+
+use Psr\Container\NotFoundExceptionInterface;
+use think\App;
+use think\contract\LogHandlerInterface;
+
+/**
+ * github: https://github.com/luofei614/SocketLog
+ * @author luofei614<weibo.com/luofei614>
+ */
+class Socket implements LogHandlerInterface
+{
+    protected $app;
+
+    protected $config = [
+        // socket服务器地址
+        'host'                => 'localhost',
+        // socket服务器端口
+        'port'                => 1116,
+        // 是否显示加载的文件列表
+        'show_included_files' => false,
+        // 日志强制记录到配置的client_id
+        'force_client_ids'    => [],
+        // 限制允许读取日志的client_id
+        'allow_client_ids'    => [],
+        // 调试开关
+        'debug'               => false,
+        // 输出到浏览器时默认展开的日志级别
+        'expand_level'        => ['debug'],
+        // 日志头渲染回调
+        'format_head'         => null,
+        // curl opt
+        'curl_opt'            => [
+            CURLOPT_CONNECTTIMEOUT => 1,
+            CURLOPT_TIMEOUT        => 10,
+        ],
+    ];
+
+    protected $css = [
+        'sql'      => 'color:#009bb4;',
+        'sql_warn' => 'color:#009bb4;font-size:14px;',
+        'error'    => 'color:#f4006b;font-size:14px;',
+        'page'     => 'color:#40e2ff;background:#171717;',
+        'big'      => 'font-size:20px;color:red;',
+    ];
+
+    protected $allowForceClientIds = []; //配置强制推送且被授权的client_id
+
+    protected $clientArg = [];
+
+    /**
+     * 架构函数
+     * @access public
+     * @param App   $app
+     * @param array $config 缓存参数
+     */
+    public function __construct(App $app, array $config = [])
+    {
+        $this->app = $app;
+
+        if (!empty($config)) {
+            $this->config = array_merge($this->config, $config);
+        }
+
+        if (!isset($config['debug'])) {
+            $this->config['debug'] = $app->isDebug();
+        }
+    }
+
+    /**
+     * 调试输出接口
+     * @access public
+     * @param array $log 日志信息
+     * @return bool
+     */
+    public function save(array $log = []): bool
+    {
+        if (!$this->check()) {
+            return false;
+        }
+
+        $trace = [];
+
+        if ($this->config['debug']) {
+            if ($this->app->exists('request')) {
+                $currentUri = $this->app->request->url(true);
+            } else {
+                $currentUri = 'cmd:' . implode(' ', $_SERVER['argv'] ?? []);
+            }
+
+            if (!empty($this->config['format_head'])) {
+                try {
+                    $currentUri = $this->app->invoke($this->config['format_head'], [$currentUri]);
+                } catch (NotFoundExceptionInterface $notFoundException) {
+                    // Ignore exception
+                }
+            }
+
+            // 基本信息
+            $trace[] = [
+                'type' => 'group',
+                'msg'  => $currentUri,
+                'css'  => $this->css['page'],
+            ];
+        }
+
+        $expandLevel = array_flip($this->config['expand_level']);
+
+        foreach ($log as $type => $val) {
+            $trace[] = [
+                'type' => isset($expandLevel[$type]) ? 'group' : 'groupCollapsed',
+                'msg'  => '[ ' . $type . ' ]',
+                'css'  => $this->css[$type] ?? '',
+            ];
+
+            foreach ($val as $msg) {
+                if (!is_string($msg)) {
+                    $msg = var_export($msg, true);
+                }
+                $trace[] = [
+                    'type' => 'log',
+                    'msg'  => $msg,
+                    'css'  => '',
+                ];
+            }
+
+            $trace[] = [
+                'type' => 'groupEnd',
+                'msg'  => '',
+                'css'  => '',
+            ];
+        }
+
+        if ($this->config['show_included_files']) {
+            $trace[] = [
+                'type' => 'groupCollapsed',
+                'msg'  => '[ file ]',
+                'css'  => '',
+            ];
+
+            $trace[] = [
+                'type' => 'log',
+                'msg'  => implode("\n", get_included_files()),
+                'css'  => '',
+            ];
+
+            $trace[] = [
+                'type' => 'groupEnd',
+                'msg'  => '',
+                'css'  => '',
+            ];
+        }
+
+        $trace[] = [
+            'type' => 'groupEnd',
+            'msg'  => '',
+            'css'  => '',
+        ];
+
+        $tabid = $this->getClientArg('tabid');
+
+        if (!$clientId = $this->getClientArg('client_id')) {
+            $clientId = '';
+        }
+
+        if (!empty($this->allowForceClientIds)) {
+            //强制推送到多个client_id
+            foreach ($this->allowForceClientIds as $forceClientId) {
+                $clientId = $forceClientId;
+                $this->sendToClient($tabid, $clientId, $trace, $forceClientId);
+            }
+        } else {
+            $this->sendToClient($tabid, $clientId, $trace, '');
+        }
+
+        return true;
+    }
+
+    /**
+     * 发送给指定客户端
+     * @access protected
+     * @author Zjmainstay
+     * @param  $tabid
+     * @param  $clientId
+     * @param  $logs
+     * @param  $forceClientId
+     */
+    protected function sendToClient($tabid, $clientId, $logs, $forceClientId)
+    {
+        $logs = [
+            'tabid'           => $tabid,
+            'client_id'       => $clientId,
+            'logs'            => $logs,
+            'force_client_id' => $forceClientId,
+        ];
+
+        $msg     = json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PARTIAL_OUTPUT_ON_ERROR);
+        $address = '/' . $clientId; //将client_id作为地址, server端通过地址判断将日志发布给谁
+
+        $this->send($this->config['host'], $this->config['port'], $msg, $address);
+    }
+
+    /**
+     * 检测客户授权
+     * @access protected
+     * @return bool
+     */
+    protected function check()
+    {
+        $tabid = $this->getClientArg('tabid');
+
+        //是否记录日志的检查
+        if (!$tabid && !$this->config['force_client_ids']) {
+            return false;
+        }
+
+        //用户认证
+        $allowClientIds = $this->config['allow_client_ids'];
+
+        if (!empty($allowClientIds)) {
+            //通过数组交集得出授权强制推送的client_id
+            $this->allowForceClientIds = array_intersect($allowClientIds, $this->config['force_client_ids']);
+            if (!$tabid && count($this->allowForceClientIds)) {
+                return true;
+            }
+
+            $clientId = $this->getClientArg('client_id');
+            if (!in_array($clientId, $allowClientIds)) {
+                return false;
+            }
+        } else {
+            $this->allowForceClientIds = $this->config['force_client_ids'];
+        }
+
+        return true;
+    }
+
+    /**
+     * 获取客户参数
+     * @access protected
+     * @param string $name
+     * @return string
+     */
+    protected function getClientArg(string $name)
+    {
+        if (!$this->app->exists('request')) {
+            return '';
+        }
+
+        if (empty($this->clientArg)) {
+            if (empty($socketLog = $this->app->request->header('socketlog'))) {
+                if (empty($socketLog = $this->app->request->header('User-Agent'))) {
+                    return '';
+                }
+            }
+
+            if (!preg_match('/SocketLog\((.*?)\)/', $socketLog, $match)) {
+                $this->clientArg = ['tabid' => null, 'client_id' => null];
+                return '';
+            }
+            parse_str($match[1] ?? '', $this->clientArg);
+        }
+
+        if (isset($this->clientArg[$name])) {
+            return $this->clientArg[$name];
+        }
+
+        return '';
+    }
+
+    /**
+     * @access protected
+     * @param string $host    - $host of socket server
+     * @param int    $port    - $port of socket server
+     * @param string $message - 发送的消息
+     * @param string $address - 地址
+     * @return bool
+     */
+    protected function send($host, $port, $message = '', $address = '/')
+    {
+        $url = 'http://' . $host . ':' . $port . $address;
+        $ch  = curl_init();
+
+        curl_setopt($ch, CURLOPT_URL, $url);
+        curl_setopt($ch, CURLOPT_POST, true);
+        curl_setopt($ch, CURLOPT_POSTFIELDS, $message);
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->config['curl_opt'][CURLOPT_CONNECTTIMEOUT] ?? 1);
+        curl_setopt($ch, CURLOPT_TIMEOUT, $this->config['curl_opt'][CURLOPT_TIMEOUT] ?? 10);
+
+        $headers = [
+            "Content-Type: application/json;charset=UTF-8",
+        ];
+
+        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); //设置header
+
+        return curl_exec($ch);
+    }
+}
diff --git a/vendor/topthink/framework/src/think/middleware/AllowCrossDomain.php b/vendor/topthink/framework/src/think/middleware/AllowCrossDomain.php
new file mode 100755
index 0000000..b7ab842
--- /dev/null
+++ b/vendor/topthink/framework/src/think/middleware/AllowCrossDomain.php
@@ -0,0 +1,63 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\middleware;
+
+use Closure;
+use think\Config;
+use think\Request;
+use think\Response;
+
+/**
+ * 跨域请求支持
+ */
+class AllowCrossDomain
+{
+    protected $cookieDomain;
+
+    protected $header = [
+        'Access-Control-Allow-Credentials' => 'true',
+        'Access-Control-Max-Age'           => 1800,
+        'Access-Control-Allow-Methods'     => 'GET, POST, PATCH, PUT, DELETE, OPTIONS',
+        'Access-Control-Allow-Headers'     => 'Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-CSRF-TOKEN, X-Requested-With',
+    ];
+
+    public function __construct(Config $config)
+    {
+        $this->cookieDomain = $config->get('cookie.domain', '');
+    }
+
+    /**
+     * 允许跨域请求
+     * @access public
+     * @param Request $request
+     * @param Closure $next
+     * @param array   $header
+     * @return Response
+     */
+    public function handle($request, Closure $next, ? array $header = [])
+    {
+        $header = !empty($header) ? array_merge($this->header, $header) : $this->header;
+
+        if (!isset($header['Access-Control-Allow-Origin'])) {
+            $origin = $request->header('origin');
+
+            if ($origin && ('' == $this->cookieDomain || strpos($origin, $this->cookieDomain))) {
+                $header['Access-Control-Allow-Origin'] = $origin;
+            } else {
+                $header['Access-Control-Allow-Origin'] = '*';
+            }
+        }
+
+        return $next($request)->header($header);
+    }
+}
diff --git a/vendor/topthink/framework/src/think/middleware/CheckRequestCache.php b/vendor/topthink/framework/src/think/middleware/CheckRequestCache.php
new file mode 100755
index 0000000..b114351
--- /dev/null
+++ b/vendor/topthink/framework/src/think/middleware/CheckRequestCache.php
@@ -0,0 +1,183 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\middleware;
+
+use Closure;
+use think\Cache;
+use think\Config;
+use think\Request;
+use think\Response;
+
+/**
+ * 请求缓存处理
+ */
+class CheckRequestCache
+{
+    /**
+     * 缓存对象
+     * @var Cache
+     */
+    protected $cache;
+
+    /**
+     * 配置参数
+     * @var array
+     */
+    protected $config = [
+        // 请求缓存规则 true为自动规则
+        'request_cache_key'    => true,
+        // 请求缓存有效期
+        'request_cache_expire' => null,
+        // 全局请求缓存排除规则
+        'request_cache_except' => [],
+        // 请求缓存的Tag
+        'request_cache_tag'    => '',
+    ];
+
+    public function __construct(Cache $cache, Config $config)
+    {
+        $this->cache  = $cache;
+        $this->config = array_merge($this->config, $config->get('route'));
+    }
+
+    /**
+     * 设置当前地址的请求缓存
+     * @access public
+     * @param Request $request
+     * @param Closure $next
+     * @param mixed   $cache
+     * @return Response
+     */
+    public function handle($request, Closure $next, $cache = null)
+    {
+        if ($request->isGet() && false !== $cache) {
+            if (false === $this->config['request_cache_key']) {
+                // 关闭当前缓存
+                $cache = false;
+            }
+
+            $cache = $cache ?? $this->getRequestCache($request);
+
+            if ($cache) {
+                if (is_array($cache)) {
+                    [$key, $expire, $tag] = array_pad($cache, 3, null);
+                } else {
+                    $key    = md5($request->url(true));
+                    $expire = $cache;
+                    $tag    = null;
+                }
+
+                $key = $this->parseCacheKey($request, $key);
+
+                if (strtotime($request->server('HTTP_IF_MODIFIED_SINCE', '')) + $expire > $request->server('REQUEST_TIME')) {
+                    // 读取缓存
+                    return Response::create()->code(304);
+                } elseif (($hit = $this->cache->get($key)) !== null) {
+                    [$content, $header, $when] = $hit;
+                    if (null === $expire || $when + $expire > $request->server('REQUEST_TIME')) {
+                        return Response::create($content)->header($header);
+                    }
+                }
+            }
+        }
+
+        $response = $next($request);
+
+        if (isset($key) && 200 == $response->getCode() && $response->isAllowCache()) {
+            $header                  = $response->getHeader();
+            $header['Cache-Control'] = 'max-age=' . $expire . ',must-revalidate';
+            $header['Last-Modified'] = gmdate('D, d M Y H:i:s') . ' GMT';
+            $header['Expires']       = gmdate('D, d M Y H:i:s', time() + $expire) . ' GMT';
+
+            $this->cache->tag($tag)->set($key, [$response->getContent(), $header, time()], $expire);
+        }
+
+        return $response;
+    }
+
+    /**
+     * 读取当前地址的请求缓存信息
+     * @access protected
+     * @param Request $request
+     * @return mixed
+     */
+    protected function getRequestCache($request)
+    {
+        $key    = $this->config['request_cache_key'];
+        $expire = $this->config['request_cache_expire'];
+        $except = $this->config['request_cache_except'];
+        $tag    = $this->config['request_cache_tag'];
+
+        foreach ($except as $rule) {
+            if (0 === stripos($request->url(), $rule)) {
+                return;
+            }
+        }
+
+        return [$key, $expire, $tag];
+    }
+
+    /**
+     * 读取当前地址的请求缓存信息
+     * @access protected
+     * @param Request $request
+     * @param mixed   $key
+     * @return null|string
+     */
+    protected function parseCacheKey($request, $key)
+    {
+        if ($key instanceof \Closure) {
+            $key = call_user_func($key, $request);
+        }
+
+        if (false === $key) {
+            // 关闭当前缓存
+            return;
+        }
+
+        if (true === $key) {
+            // 自动缓存功能
+            $key = '__URL__';
+        } elseif (strpos($key, '|')) {
+            [$key, $fun] = explode('|', $key);
+        }
+
+        // 特殊规则替换
+        if (false !== strpos($key, '__')) {
+            $key = str_replace(['__CONTROLLER__', '__ACTION__', '__URL__'], [$request->controller(), $request->action(), md5($request->url(true))], $key);
+        }
+
+        if (false !== strpos($key, ':')) {
+            $param = $request->param();
+
+            foreach ($param as $item => $val) {
+                if (is_string($val) && false !== strpos($key, ':' . $item)) {
+                    $key = str_replace(':' . $item, (string) $val, $key);
+                }
+            }
+        } elseif (strpos($key, ']')) {
+            if ('[' . $request->ext() . ']' == $key) {
+                // 缓存某个后缀的请求
+                $key = md5($request->url());
+            } else {
+                return;
+            }
+        }
+
+        if (isset($fun)) {
+            $key = $fun($key);
+        }
+
+        return $key;
+    }
+}
diff --git a/vendor/topthink/framework/src/think/middleware/FormTokenCheck.php b/vendor/topthink/framework/src/think/middleware/FormTokenCheck.php
new file mode 100755
index 0000000..efbb77b
--- /dev/null
+++ b/vendor/topthink/framework/src/think/middleware/FormTokenCheck.php
@@ -0,0 +1,45 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\middleware;
+
+use Closure;
+use think\exception\ValidateException;
+use think\Request;
+use think\Response;
+
+/**
+ * 表单令牌支持
+ */
+class FormTokenCheck
+{
+
+    /**
+     * 表单令牌检测
+     * @access public
+     * @param Request $request
+     * @param Closure $next
+     * @param string  $token 表单令牌Token名称
+     * @return Response
+     */
+    public function handle(Request $request, Closure $next, string $token = null)
+    {
+        $check = $request->checkToken($token ?: '__token__');
+
+        if (false === $check) {
+            throw new ValidateException('invalid token');
+        }
+
+        return $next($request);
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/middleware/LoadLangPack.php b/vendor/topthink/framework/src/think/middleware/LoadLangPack.php
new file mode 100755
index 0000000..af0324b
--- /dev/null
+++ b/vendor/topthink/framework/src/think/middleware/LoadLangPack.php
@@ -0,0 +1,118 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\middleware;
+
+use Closure;
+use think\App;
+use think\Config;
+use think\Cookie;
+use think\Lang;
+use think\Request;
+use think\Response;
+
+/**
+ * 多语言加载
+ */
+class LoadLangPack
+{
+    protected $app;
+    protected $lang;
+    protected $config;
+
+    public function __construct(App $app, Lang $lang, Config $config)
+    {
+        $this->app    = $app;
+        $this->lang   = $lang;
+        $this->config = $lang->getConfig();
+    }
+
+    /**
+     * 路由初始化(路由规则注册)
+     * @access public
+     * @param Request $request
+     * @param Closure $next
+     * @return Response
+     */
+    public function handle($request, Closure $next)
+    {
+        // 自动侦测当前语言
+        $langset = $this->detect($request);
+
+        if ($this->lang->defaultLangSet() != $langset) {
+            $this->lang->switchLangSet($langset);
+        }
+
+        $this->saveToCookie($this->app->cookie, $langset);
+
+        return $next($request);
+    }
+
+    /**
+     * 自动侦测设置获取语言选择
+     * @access protected
+     * @param Request $request
+     * @return string
+     */
+    protected function detect(Request $request): string
+    {
+        // 自动侦测设置获取语言选择
+        $langSet = '';
+
+        if ($request->get($this->config['detect_var'])) {
+            // url中设置了语言变量
+            $langSet = $request->get($this->config['detect_var']);
+        } elseif ($request->header($this->config['header_var'])) {
+            // Header中设置了语言变量
+            $langSet = $request->header($this->config['header_var']);
+        } elseif ($request->cookie($this->config['cookie_var'])) {
+            // Cookie中设置了语言变量
+            $langSet = $request->cookie($this->config['cookie_var']);
+        } elseif ($request->server('HTTP_ACCEPT_LANGUAGE')) {
+            // 自动侦测浏览器语言
+            $langSet = $request->server('HTTP_ACCEPT_LANGUAGE');
+        }
+
+        if (preg_match('/^([a-z\d\-]+)/i', $langSet, $matches)) {
+            $langSet = strtolower($matches[1]);
+            if (isset($this->config['accept_language'][$langSet])) {
+                $langSet = $this->config['accept_language'][$langSet];
+            }
+        } else {
+            $langSet = $this->lang->getLangSet();
+        }
+
+        if (empty($this->config['allow_lang_list']) || in_array($langSet, $this->config['allow_lang_list'])) {
+            // 合法的语言
+            $this->lang->setLangSet($langSet);
+        } else {
+            $langSet = $this->lang->getLangSet();
+        }
+
+        return $langSet;
+    }
+
+    /**
+     * 保存当前语言到Cookie
+     * @access protected
+     * @param Cookie $cookie Cookie对象
+     * @param string $langSet 语言
+     * @return void
+     */
+    protected function saveToCookie(Cookie $cookie, string $langSet)
+    {
+        if ($this->config['use_cookie']) {
+            $cookie->set($this->config['cookie_var'], $langSet);
+        }
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/middleware/SessionInit.php b/vendor/topthink/framework/src/think/middleware/SessionInit.php
new file mode 100755
index 0000000..3cb2fad
--- /dev/null
+++ b/vendor/topthink/framework/src/think/middleware/SessionInit.php
@@ -0,0 +1,80 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\middleware;
+
+use Closure;
+use think\App;
+use think\Request;
+use think\Response;
+use think\Session;
+
+/**
+ * Session初始化
+ */
+class SessionInit
+{
+
+    /** @var App */
+    protected $app;
+
+    /** @var Session */
+    protected $session;
+
+    public function __construct(App $app, Session $session)
+    {
+        $this->app     = $app;
+        $this->session = $session;
+    }
+
+    /**
+     * Session初始化
+     * @access public
+     * @param Request $request
+     * @param Closure $next
+     * @return Response
+     */
+    public function handle($request, Closure $next)
+    {
+        // Session初始化
+        $varSessionId = $this->app->config->get('session.var_session_id');
+        $cookieName   = $this->session->getName();
+
+        if ($varSessionId && $request->request($varSessionId)) {
+            $sessionId = $request->request($varSessionId);
+        } else {
+            $sessionId = $request->cookie($cookieName);
+        }
+
+        if ($sessionId) {
+            $this->session->setId($sessionId);
+        }
+
+        $this->session->init();
+
+        $request->withSession($this->session);
+
+        /** @var Response $response */
+        $response = $next($request);
+
+        $response->setSession($this->session);
+
+        $this->app->cookie->set($cookieName, $this->session->getId());
+
+        return $response;
+    }
+
+    public function end(Response $response)
+    {
+        $this->session->save();
+    }
+}
diff --git a/vendor/topthink/framework/src/think/response/File.php b/vendor/topthink/framework/src/think/response/File.php
new file mode 100755
index 0000000..1e45f2f
--- /dev/null
+++ b/vendor/topthink/framework/src/think/response/File.php
@@ -0,0 +1,160 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\response;
+
+use think\Exception;
+use think\Response;
+
+/**
+ * File Response
+ */
+class File extends Response
+{
+    protected $expire = 360;
+    protected $name;
+    protected $mimeType;
+    protected $isContent = false;
+    protected $force     = true;
+
+    public function __construct($data = '', int $code = 200)
+    {
+        $this->init($data, $code);
+    }
+
+    /**
+     * 处理数据
+     * @access protected
+     * @param  mixed $data 要处理的数据
+     * @return mixed
+     * @throws \Exception
+     */
+    protected function output($data)
+    {
+        if (!$this->isContent && !is_file($data)) {
+            throw new Exception('file not exists:' . $data);
+        }
+
+        while (ob_get_level() > 0) {
+            ob_end_clean();
+        }
+
+        if (!empty($this->name)) {
+            $name = $this->name;
+        } else {
+            $name = !$this->isContent ? pathinfo($data, PATHINFO_BASENAME) : '';
+        }
+
+        if ($this->isContent) {
+            $mimeType = $this->mimeType;
+            $size     = strlen($data);
+        } else {
+            $mimeType = $this->getMimeType($data);
+            $size     = filesize($data);
+        }
+
+        $this->header['Pragma']                    = 'public';
+        $this->header['Content-Type']              = $mimeType ?: 'application/octet-stream';
+        $this->header['Cache-control']             = 'max-age=' . $this->expire;
+        $this->header['Content-Disposition']       = ($this->force ? 'attachment; ' : '') . 'filename="' . $name . '"';
+        $this->header['Content-Length']            = $size;
+        $this->header['Content-Transfer-Encoding'] = 'binary';
+        $this->header['Expires']                   = gmdate("D, d M Y H:i:s", time() + $this->expire) . ' GMT';
+
+        $this->lastModified(gmdate('D, d M Y H:i:s', time()) . ' GMT');
+
+        return $this->isContent ? $data : file_get_contents($data);
+    }
+
+    /**
+     * 设置是否为内容 必须配合mimeType方法使用
+     * @access public
+     * @param  bool $content
+     * @return $this
+     */
+    public function isContent(bool $content = true)
+    {
+        $this->isContent = $content;
+        return $this;
+    }
+
+    /**
+     * 设置有效期
+     * @access public
+     * @param  integer $expire 有效期
+     * @return $this
+     */
+    public function expire(int $expire)
+    {
+        $this->expire = $expire;
+        return $this;
+    }
+
+    /**
+     * 设置文件类型
+     * @access public
+     * @param  string $filename 文件名
+     * @return $this
+     */
+    public function mimeType(string $mimeType)
+    {
+        $this->mimeType = $mimeType;
+        return $this;
+    }
+
+    /**
+     * 设置文件强制下载
+     * @access public
+     * @param  bool $force 强制浏览器下载
+     * @return $this
+     */
+    public function force(bool $force)
+    {
+        $this->force = $force;
+        return $this;
+    }
+
+    /**
+     * 获取文件类型信息
+     * @access public
+     * @param  string $filename 文件名
+     * @return string
+     */
+    protected function getMimeType(string $filename): string
+    {
+        if (!empty($this->mimeType)) {
+            return $this->mimeType;
+        }
+
+        $finfo = finfo_open(FILEINFO_MIME_TYPE);
+
+        return finfo_file($finfo, $filename);
+    }
+
+    /**
+     * 设置下载文件的显示名称
+     * @access public
+     * @param  string $filename 文件名
+     * @param  bool   $extension 后缀自动识别
+     * @return $this
+     */
+    public function name(string $filename, bool $extension = true)
+    {
+        $this->name = $filename;
+
+        if ($extension && false === strpos($filename, '.')) {
+            $this->name .= '.' . pathinfo($this->data, PATHINFO_EXTENSION);
+        }
+
+        return $this;
+    }
+}
diff --git a/vendor/topthink/framework/src/think/response/Html.php b/vendor/topthink/framework/src/think/response/Html.php
new file mode 100755
index 0000000..c158f78
--- /dev/null
+++ b/vendor/topthink/framework/src/think/response/Html.php
@@ -0,0 +1,34 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\response;
+
+use think\Cookie;
+use think\Response;
+
+/**
+ * Html Response
+ */
+class Html extends Response
+{
+    /**
+     * 输出type
+     * @var string
+     */
+    protected $contentType = 'text/html';
+
+    public function __construct(Cookie $cookie, $data = '', int $code = 200)
+    {
+        $this->init($data, $code);
+        $this->cookie = $cookie;
+    }
+}
diff --git a/vendor/topthink/framework/src/think/response/Json.php b/vendor/topthink/framework/src/think/response/Json.php
new file mode 100755
index 0000000..a84501f
--- /dev/null
+++ b/vendor/topthink/framework/src/think/response/Json.php
@@ -0,0 +1,62 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\response;
+
+use think\Cookie;
+use think\Response;
+
+/**
+ * Json Response
+ */
+class Json extends Response
+{
+    // 输出参数
+    protected $options = [
+        'json_encode_param' => JSON_UNESCAPED_UNICODE,
+    ];
+
+    protected $contentType = 'application/json';
+
+    public function __construct(Cookie $cookie, $data = '', int $code = 200)
+    {
+        $this->init($data, $code);
+        $this->cookie = $cookie;
+    }
+
+    /**
+     * 处理数据
+     * @access protected
+     * @param  mixed $data 要处理的数据
+     * @return string
+     * @throws \Exception
+     */
+    protected function output($data): string
+    {
+        try {
+            // 返回JSON数据格式到客户端 包含状态信息
+            $data = json_encode($data, $this->options['json_encode_param']);
+
+            if (false === $data) {
+                throw new \InvalidArgumentException(json_last_error_msg());
+            }
+
+            return $data;
+        } catch (\Exception $e) {
+            if ($e->getPrevious()) {
+                throw $e->getPrevious();
+            }
+            throw $e;
+        }
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/response/Jsonp.php b/vendor/topthink/framework/src/think/response/Jsonp.php
new file mode 100755
index 0000000..81d3a06
--- /dev/null
+++ b/vendor/topthink/framework/src/think/response/Jsonp.php
@@ -0,0 +1,74 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\response;
+
+use think\Cookie;
+use think\Request;
+use think\Response;
+
+/**
+ * Jsonp Response
+ */
+class Jsonp extends Response
+{
+    // 输出参数
+    protected $options = [
+        'var_jsonp_handler'     => 'callback',
+        'default_jsonp_handler' => 'jsonpReturn',
+        'json_encode_param'     => JSON_UNESCAPED_UNICODE,
+    ];
+
+    protected $contentType = 'application/javascript';
+
+    protected $request;
+
+    public function __construct(Cookie $cookie, Request $request, $data = '', int $code = 200)
+    {
+        $this->init($data, $code);
+
+        $this->cookie  = $cookie;
+        $this->request = $request;
+    }
+
+    /**
+     * 处理数据
+     * @access protected
+     * @param  mixed $data 要处理的数据
+     * @return string
+     * @throws \Exception
+     */
+    protected function output($data): string
+    {
+        try {
+            // 返回JSON数据格式到客户端 包含状态信息 [当url_common_param为false时是无法获取到$_GET的数据的,故使用Request来获取<xiaobo.sun@qq.com>]
+            $varJsonpHandler = $this->request->param($this->options['var_jsonp_handler'], "");
+            $handler         = !empty($varJsonpHandler) ? $varJsonpHandler : $this->options['default_jsonp_handler'];
+
+            $data = json_encode($data, $this->options['json_encode_param']);
+
+            if (false === $data) {
+                throw new \InvalidArgumentException(json_last_error_msg());
+            }
+
+            $data = $handler . '(' . $data . ');';
+
+            return $data;
+        } catch (\Exception $e) {
+            if ($e->getPrevious()) {
+                throw $e->getPrevious();
+            }
+            throw $e;
+        }
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/response/Redirect.php b/vendor/topthink/framework/src/think/response/Redirect.php
new file mode 100755
index 0000000..ee067be
--- /dev/null
+++ b/vendor/topthink/framework/src/think/response/Redirect.php
@@ -0,0 +1,98 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\response;
+
+use think\Cookie;
+use think\Request;
+use think\Response;
+use think\Session;
+
+/**
+ * Redirect Response
+ */
+class Redirect extends Response
+{
+
+    protected $request;
+
+    public function __construct(Cookie $cookie, Request $request, Session $session, $data = '', int $code = 302)
+    {
+        $this->init((string) $data, $code);
+
+        $this->cookie  = $cookie;
+        $this->request = $request;
+        $this->session = $session;
+
+        $this->cacheControl('no-cache,must-revalidate');
+    }
+
+    /**
+     * 处理数据
+     * @access protected
+     * @param  mixed $data 要处理的数据
+     * @return string
+     */
+    protected function output($data): string
+    {
+        $this->header['Location'] = $data;
+
+        return '';
+    }
+
+    /**
+     * 重定向传值(通过Session)
+     * @access protected
+     * @param  string|array  $name 变量名或者数组
+     * @param  mixed         $value 值
+     * @return $this
+     */
+    public function with($name, $value = null)
+    {
+        if (is_array($name)) {
+            foreach ($name as $key => $val) {
+                $this->session->flash($key, $val);
+            }
+        } else {
+            $this->session->flash($name, $value);
+        }
+
+        return $this;
+    }
+
+    /**
+     * 记住当前url后跳转
+     * @access public
+     * @return $this
+     */
+    public function remember($complete = false)
+    {
+        $this->session->set('redirect_url', $this->request->url($complete));
+
+        return $this;
+    }
+
+    /**
+     * 跳转到上次记住的url
+     * @access public
+     * @return $this
+     */
+    public function restore()
+    {
+        if ($this->session->has('redirect_url')) {
+            $this->data = $this->session->get('redirect_url');
+            $this->session->delete('redirect_url');
+        }
+
+        return $this;
+    }
+}
diff --git a/vendor/topthink/framework/src/think/response/View.php b/vendor/topthink/framework/src/think/response/View.php
new file mode 100755
index 0000000..2c116c7
--- /dev/null
+++ b/vendor/topthink/framework/src/think/response/View.php
@@ -0,0 +1,151 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\response;
+
+use think\Cookie;
+use think\Response;
+use think\View as BaseView;
+
+/**
+ * View Response
+ */
+class View extends Response
+{
+    /**
+     * 输出参数
+     * @var array
+     */
+    protected $options = [];
+
+    /**
+     * 输出变量
+     * @var array
+     */
+    protected $vars = [];
+
+    /**
+     * 输出过滤
+     * @var mixed
+     */
+    protected $filter;
+
+    /**
+     * 输出type
+     * @var string
+     */
+    protected $contentType = 'text/html';
+
+    /**
+     * View对象
+     * @var BaseView
+     */
+    protected $view;
+
+    /**
+     * 是否内容渲染
+     * @var bool
+     */
+    protected $isContent = false;
+
+    public function __construct(Cookie $cookie, BaseView $view, $data = '', int $code = 200)
+    {
+        $this->init($data, $code);
+
+        $this->cookie = $cookie;
+        $this->view   = $view;
+    }
+
+    /**
+     * 设置是否为内容渲染
+     * @access public
+     * @param  bool $content
+     * @return $this
+     */
+    public function isContent(bool $content = true)
+    {
+        $this->isContent = $content;
+        return $this;
+    }
+
+    /**
+     * 处理数据
+     * @access protected
+     * @param  mixed $data 要处理的数据
+     * @return string
+     */
+    protected function output($data): string
+    {
+        // 渲染模板输出
+        $this->view->filter($this->filter);
+        return $this->isContent ?
+        $this->view->display($data, $this->vars) :
+        $this->view->fetch($data, $this->vars);
+    }
+
+    /**
+     * 获取视图变量
+     * @access public
+     * @param  string $name 模板变量
+     * @return mixed
+     */
+    public function getVars(string $name = null)
+    {
+        if (is_null($name)) {
+            return $this->vars;
+        } else {
+            return $this->vars[$name] ?? null;
+        }
+    }
+
+    /**
+     * 模板变量赋值
+     * @access public
+     * @param  string|array $name  模板变量
+     * @param  mixed        $value 变量值
+     * @return $this
+     */
+    public function assign($name, $value = null)
+    {
+        if (is_array($name)) {
+            $this->vars = array_merge($this->vars, $name);
+        } else {
+            $this->vars[$name] = $value;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 视图内容过滤
+     * @access public
+     * @param callable $filter
+     * @return $this
+     */
+    public function filter(callable $filter = null)
+    {
+        $this->filter = $filter;
+        return $this;
+    }
+
+    /**
+     * 检查模板是否存在
+     * @access public
+     * @param  string  $name 模板名
+     * @return bool
+     */
+    public function exists(string $name): bool
+    {
+        return $this->view->exists($name);
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/response/Xml.php b/vendor/topthink/framework/src/think/response/Xml.php
new file mode 100755
index 0000000..bddbb48
--- /dev/null
+++ b/vendor/topthink/framework/src/think/response/Xml.php
@@ -0,0 +1,127 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\response;
+
+use think\Collection;
+use think\Cookie;
+use think\Model;
+use think\Response;
+
+/**
+ * XML Response
+ */
+class Xml extends Response
+{
+    // 输出参数
+    protected $options = [
+        // 根节点名
+        'root_node' => 'think',
+        // 根节点属性
+        'root_attr' => '',
+        //数字索引的子节点名
+        'item_node' => 'item',
+        // 数字索引子节点key转换的属性名
+        'item_key'  => 'id',
+        // 数据编码
+        'encoding'  => 'utf-8',
+    ];
+
+    protected $contentType = 'text/xml';
+
+    public function __construct(Cookie $cookie, $data = '', int $code = 200)
+    {
+        $this->init($data, $code);
+        $this->cookie = $cookie;
+    }
+
+    /**
+     * 处理数据
+     * @access protected
+     * @param  mixed $data 要处理的数据
+     * @return mixed
+     */
+    protected function output($data): string
+    {
+        if (is_string($data)) {
+            if (0 !== strpos($data, '<?xml')) {
+                $encoding = $this->options['encoding'];
+                $xml      = "<?xml version=\"1.0\" encoding=\"{$encoding}\"?>";
+                $data     = $xml . $data;
+            }
+            return $data;
+        }
+
+        // XML数据转换
+        return $this->xmlEncode($data, $this->options['root_node'], $this->options['item_node'], $this->options['root_attr'], $this->options['item_key'], $this->options['encoding']);
+    }
+
+    /**
+     * XML编码
+     * @access protected
+     * @param  mixed $data 数据
+     * @param  string $root 根节点名
+     * @param  string $item 数字索引的子节点名
+     * @param  mixed  $attr 根节点属性
+     * @param  string $id   数字索引子节点key转换的属性名
+     * @param  string $encoding 数据编码
+     * @return string
+     */
+    protected function xmlEncode($data, string $root, string $item, $attr, string $id, string $encoding): string
+    {
+        if (is_array($attr)) {
+            $array = [];
+            foreach ($attr as $key => $value) {
+                $array[] = "{$key}=\"{$value}\"";
+            }
+            $attr = implode(' ', $array);
+        }
+
+        $attr = trim($attr);
+        $attr = empty($attr) ? '' : " {$attr}";
+        $xml  = "<?xml version=\"1.0\" encoding=\"{$encoding}\"?>";
+        $xml .= "<{$root}{$attr}>";
+        $xml .= $this->dataToXml($data, $item, $id);
+        $xml .= "</{$root}>";
+
+        return $xml;
+    }
+
+    /**
+     * 数据XML编码
+     * @access protected
+     * @param  mixed  $data 数据
+     * @param  string $item 数字索引时的节点名称
+     * @param  string $id   数字索引key转换为的属性名
+     * @return string
+     */
+    protected function dataToXml($data, string $item, string $id): string
+    {
+        $xml = $attr = '';
+
+        if ($data instanceof Collection || $data instanceof Model) {
+            $data = $data->toArray();
+        }
+
+        foreach ($data as $key => $val) {
+            if (is_numeric($key)) {
+                $id && $attr = " {$id}=\"{$key}\"";
+                $key         = $item;
+            }
+            $xml .= "<{$key}{$attr}>";
+            $xml .= (is_array($val) || is_object($val)) ? $this->dataToXml($val, $item, $id) : $val;
+            $xml .= "</{$key}>";
+        }
+
+        return $xml;
+    }
+}
diff --git a/vendor/topthink/framework/src/think/route/Dispatch.php b/vendor/topthink/framework/src/think/route/Dispatch.php
new file mode 100755
index 0000000..58666b5
--- /dev/null
+++ b/vendor/topthink/framework/src/think/route/Dispatch.php
@@ -0,0 +1,264 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\route;
+
+use Psr\Http\Message\ResponseInterface;
+use think\App;
+use think\Container;
+use think\Request;
+use think\Response;
+use think\Validate;
+
+/**
+ * 路由调度基础类
+ */
+abstract class Dispatch
+{
+    /**
+     * 应用对象
+     * @var \think\App
+     */
+    protected $app;
+
+    /**
+     * 请求对象
+     * @var Request
+     */
+    protected $request;
+
+    /**
+     * 路由规则
+     * @var Rule
+     */
+    protected $rule;
+
+    /**
+     * 调度信息
+     * @var mixed
+     */
+    protected $dispatch;
+
+    /**
+     * 路由变量
+     * @var array
+     */
+    protected $param;
+
+    public function __construct(Request $request, Rule $rule, $dispatch, array $param = [])
+    {
+        $this->request  = $request;
+        $this->rule     = $rule;
+        $this->dispatch = $dispatch;
+        $this->param    = $param;
+    }
+
+    public function init(App $app)
+    {
+        $this->app = $app;
+
+        // 执行路由后置操作
+        $this->doRouteAfter();
+    }
+
+    /**
+     * 执行路由调度
+     * @access public
+     * @return mixed
+     */
+    public function run(): Response
+    {
+        if ($this->rule instanceof RuleItem && $this->request->method() == 'OPTIONS' && $this->rule->isAutoOptions()) {
+            $rules = $this->rule->getRouter()->getRule($this->rule->getRule());
+            $allow = [];
+            foreach ($rules as $item) {
+                $allow[] = strtoupper($item->getMethod());
+            }
+
+            return Response::create('', 'html', 204)->header(['Allow' => implode(', ', $allow)]);
+        }
+
+        $data = $this->exec();
+        return $this->autoResponse($data);
+    }
+
+    protected function autoResponse($data): Response
+    {
+        if ($data instanceof Response) {
+            $response = $data;
+        } elseif ($data instanceof ResponseInterface) {
+            $response = Response::create((string) $data->getBody(), 'html', $data->getStatusCode());
+
+            foreach ($data->getHeaders() as $header => $values) {
+                $response->header([$header => implode(", ", $values)]);
+            }
+        } elseif (!is_null($data)) {
+            // 默认自动识别响应输出类型
+            $type     = $this->request->isJson() ? 'json' : 'html';
+            $response = Response::create($data, $type);
+        } else {
+            $data = ob_get_clean();
+
+            $content  = false === $data ? '' : $data;
+            $status   = '' === $content && $this->request->isJson() ? 204 : 200;
+            $response = Response::create($content, 'html', $status);
+        }
+
+        return $response;
+    }
+
+    /**
+     * 检查路由后置操作
+     * @access protected
+     * @return void
+     */
+    protected function doRouteAfter(): void
+    {
+        $option = $this->rule->getOption();
+
+        // 添加中间件
+        if (!empty($option['middleware'])) {
+            $this->app->middleware->import($option['middleware'], 'route');
+        }
+
+        if (!empty($option['append'])) {
+            $this->param = array_merge($this->param, $option['append']);
+        }
+
+        // 绑定模型数据
+        if (!empty($option['model'])) {
+            $this->createBindModel($option['model'], $this->param);
+        }
+
+        // 记录当前请求的路由规则
+        $this->request->setRule($this->rule);
+
+        // 记录路由变量
+        $this->request->setRoute($this->param);
+
+        // 数据自动验证
+        if (isset($option['validate'])) {
+            $this->autoValidate($option['validate']);
+        }
+    }
+
+    /**
+     * 路由绑定模型实例
+     * @access protected
+     * @param array $bindModel 绑定模型
+     * @param array $matches   路由变量
+     * @return void
+     */
+    protected function createBindModel(array $bindModel, array $matches): void
+    {
+        foreach ($bindModel as $key => $val) {
+            if ($val instanceof \Closure) {
+                $result = $this->app->invokeFunction($val, $matches);
+            } else {
+                $fields = explode('&', $key);
+
+                if (is_array($val)) {
+                    [$model, $exception] = $val;
+                } else {
+                    $model     = $val;
+                    $exception = true;
+                }
+
+                $where = [];
+                $match = true;
+
+                foreach ($fields as $field) {
+                    if (!isset($matches[$field])) {
+                        $match = false;
+                        break;
+                    } else {
+                        $where[] = [$field, '=', $matches[$field]];
+                    }
+                }
+
+                if ($match) {
+                    $result = $model::where($where)->failException($exception)->find();
+                }
+            }
+
+            if (!empty($result)) {
+                // 注入容器
+                $this->app->instance(get_class($result), $result);
+            }
+        }
+    }
+
+    /**
+     * 验证数据
+     * @access protected
+     * @param array $option
+     * @return void
+     * @throws \think\exception\ValidateException
+     */
+    protected function autoValidate(array $option): void
+    {
+        [$validate, $scene, $message, $batch] = $option;
+
+        if (is_array($validate)) {
+            // 指定验证规则
+            $v = new Validate();
+            $v->rule($validate);
+        } else {
+            // 调用验证器
+            $class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
+
+            $v = new $class();
+
+            if (!empty($scene)) {
+                $v->scene($scene);
+            }
+        }
+
+        /** @var Validate $v */
+        $v->message($message)
+            ->batch($batch)
+            ->failException(true)
+            ->check($this->request->param());
+    }
+
+    public function getDispatch()
+    {
+        return $this->dispatch;
+    }
+
+    public function getParam(): array
+    {
+        return $this->param;
+    }
+
+    abstract public function exec();
+
+    public function __sleep()
+    {
+        return ['rule', 'dispatch', 'param', 'controller', 'actionName'];
+    }
+
+    public function __wakeup()
+    {
+        $this->app     = Container::pull('app');
+        $this->request = $this->app->request;
+    }
+
+    public function __debugInfo()
+    {
+        return [
+            'dispatch' => $this->dispatch,
+            'param'    => $this->param,
+            'rule'     => $this->rule,
+        ];
+    }
+}
diff --git a/vendor/topthink/framework/src/think/route/Domain.php b/vendor/topthink/framework/src/think/route/Domain.php
new file mode 100755
index 0000000..84f1d46
--- /dev/null
+++ b/vendor/topthink/framework/src/think/route/Domain.php
@@ -0,0 +1,183 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\route;
+
+use think\helper\Str;
+use think\Request;
+use think\Route;
+use think\route\dispatch\Callback as CallbackDispatch;
+use think\route\dispatch\Controller as ControllerDispatch;
+
+/**
+ * 域名路由
+ */
+class Domain extends RuleGroup
+{
+    /**
+     * 架构函数
+     * @access public
+     * @param  Route       $router   路由对象
+     * @param  string      $name     路由域名
+     * @param  mixed       $rule     域名路由
+     */
+    public function __construct(Route $router, string $name = null, $rule = null)
+    {
+        $this->router = $router;
+        $this->domain = $name;
+        $this->rule   = $rule;
+    }
+
+    /**
+     * 检测域名路由
+     * @access public
+     * @param  Request      $request  请求对象
+     * @param  string       $url      访问地址
+     * @param  bool         $completeMatch   路由是否完全匹配
+     * @return Dispatch|false
+     */
+    public function check(Request $request, string $url, bool $completeMatch = false)
+    {
+        // 检测URL绑定
+        $result = $this->checkUrlBind($request, $url);
+
+        if (!empty($this->option['append'])) {
+            $request->setRoute($this->option['append']);
+            unset($this->option['append']);
+        }
+
+        if (false !== $result) {
+            return $result;
+        }
+
+        return parent::check($request, $url, $completeMatch);
+    }
+
+    /**
+     * 设置路由绑定
+     * @access public
+     * @param  string     $bind 绑定信息
+     * @return $this
+     */
+    public function bind(string $bind)
+    {
+        $this->router->bind($bind, $this->domain);
+
+        return $this;
+    }
+
+    /**
+     * 检测URL绑定
+     * @access private
+     * @param  Request   $request
+     * @param  string    $url URL地址
+     * @return Dispatch|false
+     */
+    private function checkUrlBind(Request $request, string $url)
+    {
+        $bind = $this->router->getDomainBind($this->domain);
+
+        if ($bind) {
+            $this->parseBindAppendParam($bind);
+
+            // 如果有URL绑定 则进行绑定检测
+            $type = substr($bind, 0, 1);
+            $bind = substr($bind, 1);
+
+            $bindTo = [
+                '\\' => 'bindToClass',
+                '@'  => 'bindToController',
+                ':'  => 'bindToNamespace',
+            ];
+
+            if (isset($bindTo[$type])) {
+                return $this->{$bindTo[$type]}($request, $url, $bind);
+            }
+        }
+
+        return false;
+    }
+
+    protected function parseBindAppendParam(string &$bind): void
+    {
+        if (false !== strpos($bind, '?')) {
+            [$bind, $query] = explode('?', $bind);
+            parse_str($query, $vars);
+            $this->append($vars);
+        }
+    }
+
+    /**
+     * 绑定到类
+     * @access protected
+     * @param  Request   $request
+     * @param  string    $url URL地址
+     * @param  string    $class 类名(带命名空间)
+     * @return CallbackDispatch
+     */
+    protected function bindToClass(Request $request, string $url, string $class): CallbackDispatch
+    {
+        $array  = explode('|', $url, 2);
+        $action = !empty($array[0]) ? $array[0] : $this->router->config('default_action');
+        $param  = [];
+
+        if (!empty($array[1])) {
+            $this->parseUrlParams($array[1], $param);
+        }
+
+        return new CallbackDispatch($request, $this, [$class, $action], $param);
+    }
+
+    /**
+     * 绑定到命名空间
+     * @access protected
+     * @param  Request   $request
+     * @param  string    $url URL地址
+     * @param  string    $namespace 命名空间
+     * @return CallbackDispatch
+     */
+    protected function bindToNamespace(Request $request, string $url, string $namespace): CallbackDispatch
+    {
+        $array  = explode('|', $url, 3);
+        $class  = !empty($array[0]) ? $array[0] : $this->router->config('default_controller');
+        $method = !empty($array[1]) ? $array[1] : $this->router->config('default_action');
+        $param  = [];
+
+        if (!empty($array[2])) {
+            $this->parseUrlParams($array[2], $param);
+        }
+
+        return new CallbackDispatch($request, $this, [$namespace . '\\' . Str::studly($class), $method], $param);
+    }
+
+    /**
+     * 绑定到控制器
+     * @access protected
+     * @param  Request   $request
+     * @param  string    $url URL地址
+     * @param  string    $controller 控制器名
+     * @return ControllerDispatch
+     */
+    protected function bindToController(Request $request, string $url, string $controller): ControllerDispatch
+    {
+        $array  = explode('|', $url, 2);
+        $action = !empty($array[0]) ? $array[0] : $this->router->config('default_action');
+        $param  = [];
+
+        if (!empty($array[1])) {
+            $this->parseUrlParams($array[1], $param);
+        }
+
+        return new ControllerDispatch($request, $this, $controller . '/' . $action, $param);
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/route/Resource.php b/vendor/topthink/framework/src/think/route/Resource.php
new file mode 100755
index 0000000..5185cdc
--- /dev/null
+++ b/vendor/topthink/framework/src/think/route/Resource.php
@@ -0,0 +1,251 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\route;
+
+use think\Route;
+
+/**
+ * 资源路由类
+ */
+class Resource extends RuleGroup
+{
+    /**
+     * 资源路由名称
+     * @var string
+     */
+    protected $resource;
+
+    /**
+     * 资源路由地址
+     * @var string
+     */
+    protected $route;
+
+    /**
+     * REST方法定义
+     * @var array
+     */
+    protected $rest = [];
+
+    /**
+     * 模型绑定
+     * @var array
+     */
+    protected $model = [];
+
+    /**
+     * 数据验证
+     * @var array
+     */
+    protected $validate = [];
+
+    /**
+     * 中间件
+     * @var array
+     */
+    protected $middleware = [];
+
+    /**
+     * 架构函数
+     * @access public
+     * @param  Route         $router     路由对象
+     * @param  RuleGroup     $parent     上级对象
+     * @param  string        $name       资源名称
+     * @param  string        $route      路由地址
+     * @param  array         $rest       资源定义
+     */
+    public function __construct(Route $router, RuleGroup $parent = null, string $name = '', string $route = '', array $rest = [])
+    {
+        $name           = ltrim($name, '/');
+        $this->router   = $router;
+        $this->parent   = $parent;
+        $this->resource = $name;
+        $this->route    = $route;
+        $this->name     = strpos($name, '.') ? strstr($name, '.', true) : $name;
+
+        $this->setFullName();
+
+        // 资源路由默认为完整匹配
+        $this->option['complete_match'] = true;
+
+        $this->rest = $rest;
+
+        if ($this->parent) {
+            $this->domain = $this->parent->getDomain();
+            $this->parent->addRuleItem($this);
+        }
+
+        if ($router->isTest()) {
+            $this->buildResourceRule();
+        }
+    }
+
+    /**
+     * 生成资源路由规则
+     * @access protected
+     * @return void
+     */
+    protected function buildResourceRule(): void
+    {
+        $rule   = $this->resource;
+        $option = $this->option;
+        $origin = $this->router->getGroup();
+        $this->router->setGroup($this);
+
+        if (strpos($rule, '.')) {
+            // 注册嵌套资源路由
+            $array = explode('.', $rule);
+            $last  = array_pop($array);
+            $item  = [];
+
+            foreach ($array as $val) {
+                $item[] = $val . '/<' . ($option['var'][$val] ?? $val . '_id') . '>';
+            }
+
+            $rule = implode('/', $item) . '/' . $last;
+        }
+
+        $prefix = substr($rule, strlen($this->name) + 1);
+
+        // 注册资源路由
+        foreach ($this->rest as $key => $val) {
+            if ((isset($option['only']) && !in_array($key, $option['only']))
+                || (isset($option['except']) && in_array($key, $option['except']))) {
+                continue;
+            }
+
+            if (isset($last) && strpos($val[1], '<id>') && isset($option['var'][$last])) {
+                $val[1] = str_replace('<id>', '<' . $option['var'][$last] . '>', $val[1]);
+            } elseif (strpos($val[1], '<id>') && isset($option['var'][$rule])) {
+                $val[1] = str_replace('<id>', '<' . $option['var'][$rule] . '>', $val[1]);
+            }
+
+            $ruleItem = $this->addRule(trim($prefix . $val[1], '/'), $this->route . '/' . $val[2], $val[0]);
+
+            foreach (['model', 'validate', 'middleware', 'pattern'] as $name) {
+                if (isset($this->$name[$key])) {
+                    call_user_func_array([$ruleItem, $name], (array) $this->$name[$key]);
+                }
+
+            }
+        }
+
+        $this->router->setGroup($origin);
+    }
+
+    /**
+     * 设置资源允许
+     * @access public
+     * @param  array $only 资源允许
+     * @return $this
+     */
+    public function only(array $only)
+    {
+        return $this->setOption('only', $only);
+    }
+
+    /**
+     * 设置资源排除
+     * @access public
+     * @param  array $except 排除资源
+     * @return $this
+     */
+    public function except(array $except)
+    {
+        return $this->setOption('except', $except);
+    }
+
+    /**
+     * 设置资源路由的变量
+     * @access public
+     * @param  array $vars 资源变量
+     * @return $this
+     */
+    public function vars(array $vars)
+    {
+        return $this->setOption('var', $vars);
+    }
+
+    /**
+     * 绑定资源验证
+     * @access public
+     * @param  array|string $name 资源类型或者验证信息
+     * @param  array|string $validate 验证信息
+     * @return $this
+     */
+    public function withValidate($name, $validate = [])
+    {
+        if (is_array($name)) {
+            $this->validate = array_merge($this->validate, $name);
+        } else {
+            $this->validate[$name] = $validate;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 绑定资源模型
+     * @access public
+     * @param  array|string $name 资源类型或者模型绑定
+     * @param  array|string $model 模型绑定
+     * @return $this
+     */
+    public function withModel($name, $model = [])
+    {
+        if (is_array($name)) {
+            $this->model = array_merge($this->model, $name);
+        } else {
+            $this->model[$name] = $model;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 绑定资源中间件
+     * @access public
+     * @param  array|string $name 资源类型或者中间件定义
+     * @param  array|string $middleware 中间件定义
+     * @return $this
+     */
+    public function withMiddleware($name, $middleware = [])
+    {
+        if (is_array($name)) {
+            $this->middleware = array_merge($this->middleware, $name);
+        } else {
+            $this->middleware[$name] = $middleware;
+        }
+
+        return $this;
+    }
+
+    /**
+     * rest方法定义和修改
+     * @access public
+     * @param  array|string  $name 方法名称
+     * @param  array|bool    $resource 资源
+     * @return $this
+     */
+    public function rest($name, $resource = [])
+    {
+        if (is_array($name)) {
+            $this->rest = $resource ? $name : array_merge($this->rest, $name);
+        } else {
+            $this->rest[$name] = $resource;
+        }
+
+        return $this;
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/route/Rule.php b/vendor/topthink/framework/src/think/route/Rule.php
new file mode 100755
index 0000000..2d864cd
--- /dev/null
+++ b/vendor/topthink/framework/src/think/route/Rule.php
@@ -0,0 +1,923 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\route;
+
+use Closure;
+use think\Container;
+use think\middleware\AllowCrossDomain;
+use think\middleware\CheckRequestCache;
+use think\middleware\FormTokenCheck;
+use think\Request;
+use think\Route;
+use think\route\dispatch\Callback as CallbackDispatch;
+use think\route\dispatch\Controller as ControllerDispatch;
+
+/**
+ * 路由规则基础类
+ */
+abstract class Rule
+{
+    /**
+     * 路由标识
+     * @var string
+     */
+    protected $name;
+
+    /**
+     * 所在域名
+     * @var string
+     */
+    protected $domain;
+
+    /**
+     * 路由对象
+     * @var Route
+     */
+    protected $router;
+
+    /**
+     * 路由所属分组
+     * @var RuleGroup
+     */
+    protected $parent;
+
+    /**
+     * 路由规则
+     * @var mixed
+     */
+    protected $rule;
+
+    /**
+     * 路由地址
+     * @var string|Closure
+     */
+    protected $route;
+
+    /**
+     * 请求类型
+     * @var string
+     */
+    protected $method;
+
+    /**
+     * 路由变量
+     * @var array
+     */
+    protected $vars = [];
+
+    /**
+     * 路由参数
+     * @var array
+     */
+    protected $option = [];
+
+    /**
+     * 路由变量规则
+     * @var array
+     */
+    protected $pattern = [];
+
+    /**
+     * 需要和分组合并的路由参数
+     * @var array
+     */
+    protected $mergeOptions = ['model', 'append', 'middleware'];
+
+    abstract public function check(Request $request, string $url, bool $completeMatch = false);
+
+    /**
+     * 设置路由参数
+     * @access public
+     * @param  array $option 参数
+     * @return $this
+     */
+    public function option(array $option)
+    {
+        $this->option = array_merge($this->option, $option);
+
+        return $this;
+    }
+
+    /**
+     * 设置单个路由参数
+     * @access public
+     * @param  string $name  参数名
+     * @param  mixed  $value 值
+     * @return $this
+     */
+    public function setOption(string $name, $value)
+    {
+        $this->option[$name] = $value;
+
+        return $this;
+    }
+
+    /**
+     * 注册变量规则
+     * @access public
+     * @param  array $pattern 变量规则
+     * @return $this
+     */
+    public function pattern(array $pattern)
+    {
+        $this->pattern = array_merge($this->pattern, $pattern);
+
+        return $this;
+    }
+
+    /**
+     * 设置标识
+     * @access public
+     * @param  string $name 标识名
+     * @return $this
+     */
+    public function name(string $name)
+    {
+        $this->name = $name;
+
+        return $this;
+    }
+
+    /**
+     * 获取路由对象
+     * @access public
+     * @return Route
+     */
+    public function getRouter(): Route
+    {
+        return $this->router;
+    }
+
+    /**
+     * 获取Name
+     * @access public
+     * @return string
+     */
+    public function getName(): string
+    {
+        return $this->name ?: '';
+    }
+
+    /**
+     * 获取当前路由规则
+     * @access public
+     * @return mixed
+     */
+    public function getRule()
+    {
+        return $this->rule;
+    }
+
+    /**
+     * 获取当前路由地址
+     * @access public
+     * @return mixed
+     */
+    public function getRoute()
+    {
+        return $this->route;
+    }
+
+    /**
+     * 获取当前路由的变量
+     * @access public
+     * @return array
+     */
+    public function getVars(): array
+    {
+        return $this->vars;
+    }
+
+    /**
+     * 获取Parent对象
+     * @access public
+     * @return $this|null
+     */
+    public function getParent()
+    {
+        return $this->parent;
+    }
+
+    /**
+     * 获取路由所在域名
+     * @access public
+     * @return string
+     */
+    public function getDomain(): string
+    {
+        return $this->domain ?: $this->parent->getDomain();
+    }
+
+    /**
+     * 获取路由参数
+     * @access public
+     * @param  string $name 变量名
+     * @return mixed
+     */
+    public function config(string $name = '')
+    {
+        return $this->router->config($name);
+    }
+
+    /**
+     * 获取变量规则定义
+     * @access public
+     * @param  string $name 变量名
+     * @return mixed
+     */
+    public function getPattern(string $name = '')
+    {
+        $pattern = $this->pattern;
+
+        if ($this->parent) {
+            $pattern = array_merge($this->parent->getPattern(), $pattern);
+        }
+
+        if ('' === $name) {
+            return $pattern;
+        }
+
+        return $pattern[$name] ?? null;
+    }
+
+    /**
+     * 获取路由参数定义
+     * @access public
+     * @param  string $name 参数名
+     * @param  mixed  $default 默认值
+     * @return mixed
+     */
+    public function getOption(string $name = '', $default = null)
+    {
+        $option = $this->option;
+
+        if ($this->parent) {
+            $parentOption = $this->parent->getOption();
+
+            // 合并分组参数
+            foreach ($this->mergeOptions as $item) {
+                if (isset($parentOption[$item]) && isset($option[$item])) {
+                    $option[$item] = array_merge($parentOption[$item], $option[$item]);
+                }
+            }
+
+            $option = array_merge($parentOption, $option);
+        }
+
+        if ('' === $name) {
+            return $option;
+        }
+
+        return $option[$name] ?? $default;
+    }
+
+    /**
+     * 获取当前路由的请求类型
+     * @access public
+     * @return string
+     */
+    public function getMethod(): string
+    {
+        return strtolower($this->method);
+    }
+
+    /**
+     * 设置路由请求类型
+     * @access public
+     * @param  string $method 请求类型
+     * @return $this
+     */
+    public function method(string $method)
+    {
+        return $this->setOption('method', strtolower($method));
+    }
+
+    /**
+     * 检查后缀
+     * @access public
+     * @param  string $ext URL后缀
+     * @return $this
+     */
+    public function ext(string $ext = '')
+    {
+        return $this->setOption('ext', $ext);
+    }
+
+    /**
+     * 检查禁止后缀
+     * @access public
+     * @param  string $ext URL后缀
+     * @return $this
+     */
+    public function denyExt(string $ext = '')
+    {
+        return $this->setOption('deny_ext', $ext);
+    }
+
+    /**
+     * 检查域名
+     * @access public
+     * @param  string $domain 域名
+     * @return $this
+     */
+    public function domain(string $domain)
+    {
+        $this->domain = $domain;
+        return $this->setOption('domain', $domain);
+    }
+
+    /**
+     * 设置参数过滤检查
+     * @access public
+     * @param  array $filter 参数过滤
+     * @return $this
+     */
+    public function filter(array $filter)
+    {
+        $this->option['filter'] = $filter;
+
+        return $this;
+    }
+
+    /**
+     * 绑定模型
+     * @access public
+     * @param  array|string|Closure $var  路由变量名 多个使用 & 分割
+     * @param  string|Closure       $model 绑定模型类
+     * @param  bool                  $exception 是否抛出异常
+     * @return $this
+     */
+    public function model($var, $model = null, bool $exception = true)
+    {
+        if ($var instanceof Closure) {
+            $this->option['model'][] = $var;
+        } elseif (is_array($var)) {
+            $this->option['model'] = $var;
+        } elseif (is_null($model)) {
+            $this->option['model']['id'] = [$var, true];
+        } else {
+            $this->option['model'][$var] = [$model, $exception];
+        }
+
+        return $this;
+    }
+
+    /**
+     * 附加路由隐式参数
+     * @access public
+     * @param  array $append 追加参数
+     * @return $this
+     */
+    public function append(array $append = [])
+    {
+        $this->option['append'] = $append;
+
+        return $this;
+    }
+
+    /**
+     * 绑定验证
+     * @access public
+     * @param  mixed  $validate 验证器类
+     * @param  string $scene 验证场景
+     * @param  array  $message 验证提示
+     * @param  bool   $batch 批量验证
+     * @return $this
+     */
+    public function validate($validate, string $scene = null, array $message = [], bool $batch = false)
+    {
+        $this->option['validate'] = [$validate, $scene, $message, $batch];
+
+        return $this;
+    }
+
+    /**
+     * 指定路由中间件
+     * @access public
+     * @param string|array|Closure $middleware 中间件
+     * @param mixed $params 参数
+     * @return $this
+     */
+    public function middleware($middleware, ...$params)
+    {
+        if (empty($params) && is_array($middleware)) {
+            $this->option['middleware'] = $middleware;
+        } else {
+            foreach ((array) $middleware as $item) {
+                $this->option['middleware'][] = [$item, $params];
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * 允许跨域
+     * @access public
+     * @param  array $header 自定义Header
+     * @return $this
+     */
+    public function allowCrossDomain(array $header = [])
+    {
+        return $this->middleware(AllowCrossDomain::class, $header);
+    }
+
+    /**
+     * 表单令牌验证
+     * @access public
+     * @param  string $token 表单令牌token名称
+     * @return $this
+     */
+    public function token(string $token = '__token__')
+    {
+        return $this->middleware(FormTokenCheck::class, $token);
+    }
+
+    /**
+     * 设置路由缓存
+     * @access public
+     * @param  array|string $cache 缓存
+     * @return $this
+     */
+    public function cache($cache)
+    {
+        return $this->middleware(CheckRequestCache::class, $cache);
+    }
+
+    /**
+     * 检查URL分隔符
+     * @access public
+     * @param  string $depr URL分隔符
+     * @return $this
+     */
+    public function depr(string $depr)
+    {
+        return $this->setOption('param_depr', $depr);
+    }
+
+    /**
+     * 设置需要合并的路由参数
+     * @access public
+     * @param  array $option 路由参数
+     * @return $this
+     */
+    public function mergeOptions(array $option = [])
+    {
+        $this->mergeOptions = array_merge($this->mergeOptions, $option);
+        return $this;
+    }
+
+    /**
+     * 检查是否为HTTPS请求
+     * @access public
+     * @param  bool $https 是否为HTTPS
+     * @return $this
+     */
+    public function https(bool $https = true)
+    {
+        return $this->setOption('https', $https);
+    }
+
+    /**
+     * 检查是否为JSON请求
+     * @access public
+     * @param  bool $json 是否为JSON
+     * @return $this
+     */
+    public function json(bool $json = true)
+    {
+        return $this->setOption('json', $json);
+    }
+
+    /**
+     * 检查是否为AJAX请求
+     * @access public
+     * @param  bool $ajax 是否为AJAX
+     * @return $this
+     */
+    public function ajax(bool $ajax = true)
+    {
+        return $this->setOption('ajax', $ajax);
+    }
+
+    /**
+     * 检查是否为PJAX请求
+     * @access public
+     * @param  bool $pjax 是否为PJAX
+     * @return $this
+     */
+    public function pjax(bool $pjax = true)
+    {
+        return $this->setOption('pjax', $pjax);
+    }
+
+    /**
+     * 路由到一个模板地址 需要额外传入的模板变量
+     * @access public
+     * @param  array $view 视图
+     * @return $this
+     */
+    public function view(array $view = [])
+    {
+        return $this->setOption('view', $view);
+    }
+
+    /**
+     * 通过闭包检查路由是否匹配
+     * @access public
+     * @param  callable $match 闭包
+     * @return $this
+     */
+    public function match(callable $match)
+    {
+        return $this->setOption('match', $match);
+    }
+
+    /**
+     * 设置路由完整匹配
+     * @access public
+     * @param  bool $match 是否完整匹配
+     * @return $this
+     */
+    public function completeMatch(bool $match = true)
+    {
+        return $this->setOption('complete_match', $match);
+    }
+
+    /**
+     * 是否去除URL最后的斜线
+     * @access public
+     * @param  bool $remove 是否去除最后斜线
+     * @return $this
+     */
+    public function removeSlash(bool $remove = true)
+    {
+        return $this->setOption('remove_slash', $remove);
+    }
+
+    /**
+     * 设置路由规则全局有效
+     * @access public
+     * @return $this
+     */
+    public function crossDomainRule()
+    {
+        if ($this instanceof RuleGroup) {
+            $method = '*';
+        } else {
+            $method = $this->method;
+        }
+
+        $this->router->setCrossDomainRule($this, $method);
+
+        return $this;
+    }
+
+    /**
+     * 解析匹配到的规则路由
+     * @access public
+     * @param  Request $request 请求对象
+     * @param  string  $rule 路由规则
+     * @param  mixed   $route 路由地址
+     * @param  string  $url URL地址
+     * @param  array   $option 路由参数
+     * @param  array   $matches 匹配的变量
+     * @return Dispatch
+     */
+    public function parseRule(Request $request, string $rule, $route, string $url, array $option = [], array $matches = []): Dispatch
+    {
+        if (is_string($route) && isset($option['prefix'])) {
+            // 路由地址前缀
+            $route = $option['prefix'] . $route;
+        }
+
+        // 替换路由地址中的变量
+        $extraParams = true;
+        $search      = $replace      = [];
+        $depr        = $this->router->config('pathinfo_depr');
+        foreach ($matches as $key => $value) {
+            $search[]  = '<' . $key . '>';
+            $replace[] = $value;
+
+            $search[]  = ':' . $key;
+            $replace[] = $value;
+
+            if (strpos($value, $depr)) {
+                $extraParams = false;
+            }
+        }
+
+        if (is_string($route)) {
+            $route = str_replace($search, $replace, $route);
+        }
+
+        // 解析额外参数
+        if ($extraParams) {
+            $count = substr_count($rule, '/');
+            $url   = array_slice(explode('|', $url), $count + 1);
+            $this->parseUrlParams(implode('|', $url), $matches);
+        }
+
+        $this->vars = $matches;
+
+        // 发起路由调度
+        return $this->dispatch($request, $route, $option);
+    }
+
+    /**
+     * 发起路由调度
+     * @access protected
+     * @param  Request $request Request对象
+     * @param  mixed   $route  路由地址
+     * @param  array   $option 路由参数
+     * @return Dispatch
+     */
+    protected function dispatch(Request $request, $route, array $option): Dispatch
+    {
+        if (is_subclass_of($route, Dispatch::class)) {
+            $result = new $route($request, $this, $route, $this->vars);
+        } elseif ($route instanceof Closure) {
+            // 执行闭包
+            $result = new CallbackDispatch($request, $this, $route, $this->vars);
+        } elseif (false !== strpos($route, '@') || false !== strpos($route, '::') || false !== strpos($route, '\\')) {
+            // 路由到类的方法
+            $route  = str_replace('::', '@', $route);
+            $result = $this->dispatchMethod($request, $route);
+        } else {
+            // 路由到控制器/操作
+            $result = $this->dispatchController($request, $route);
+        }
+
+        return $result;
+    }
+
+    /**
+     * 解析URL地址为 模块/控制器/操作
+     * @access protected
+     * @param  Request $request Request对象
+     * @param  string  $route 路由地址
+     * @return CallbackDispatch
+     */
+    protected function dispatchMethod(Request $request, string $route): CallbackDispatch
+    {
+        $path = $this->parseUrlPath($route);
+
+        $route  = str_replace('/', '@', implode('/', $path));
+        $method = strpos($route, '@') ? explode('@', $route) : $route;
+
+        return new CallbackDispatch($request, $this, $method, $this->vars);
+    }
+
+    /**
+     * 解析URL地址为 模块/控制器/操作
+     * @access protected
+     * @param  Request $request Request对象
+     * @param  string  $route 路由地址
+     * @return ControllerDispatch
+     */
+    protected function dispatchController(Request $request, string $route): ControllerDispatch
+    {
+        $path = $this->parseUrlPath($route);
+
+        $action     = array_pop($path);
+        $controller = !empty($path) ? array_pop($path) : null;
+
+        // 路由到模块/控制器/操作
+        return new ControllerDispatch($request, $this, [$controller, $action], $this->vars);
+    }
+
+    /**
+     * 路由检查
+     * @access protected
+     * @param  array   $option 路由参数
+     * @param  Request $request Request对象
+     * @return bool
+     */
+    protected function checkOption(array $option, Request $request): bool
+    {
+        // 检查当前路由是否匹配
+        if (isset($option['match']) && is_callable($option['match'])) {
+            if (false === $option['match']($this, $request)) {
+                return false;
+            }
+        }
+
+        // 请求类型检测
+        if (!empty($option['method'])) {
+            if (is_string($option['method']) && false === stripos($option['method'], $request->method())) {
+                return false;
+            }
+        }
+
+        // AJAX PJAX 请求检查
+        foreach (['ajax', 'pjax', 'json'] as $item) {
+            if (isset($option[$item])) {
+                $call = 'is' . $item;
+                if ($option[$item] && !$request->$call() || !$option[$item] && $request->$call()) {
+                    return false;
+                }
+            }
+        }
+
+        // 伪静态后缀检测
+        if ($request->url() != '/' && ((isset($option['ext']) && false === stripos('|' . $option['ext'] . '|', '|' . $request->ext() . '|'))
+            || (isset($option['deny_ext']) && false !== stripos('|' . $option['deny_ext'] . '|', '|' . $request->ext() . '|')))) {
+            return false;
+        }
+
+        // 域名检查
+        if ((isset($option['domain']) && !in_array($option['domain'], [$request->host(true), $request->subDomain()]))) {
+            return false;
+        }
+
+        // HTTPS检查
+        if ((isset($option['https']) && $option['https'] && !$request->isSsl())
+            || (isset($option['https']) && !$option['https'] && $request->isSsl())) {
+            return false;
+        }
+
+        // 请求参数检查
+        if (isset($option['filter'])) {
+            foreach ($option['filter'] as $name => $value) {
+                if ($request->param($name, '', null) != $value) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * 解析URL地址中的参数Request对象
+     * @access protected
+     * @param  string $rule 路由规则
+     * @param  array  $var 变量
+     * @return void
+     */
+    protected function parseUrlParams(string $url, array &$var = []): void
+    {
+        if ($url) {
+            preg_replace_callback('/(\w+)\|([^\|]+)/', function ($match) use (&$var) {
+                $var[$match[1]] = strip_tags($match[2]);
+            }, $url);
+        }
+    }
+
+    /**
+     * 解析URL的pathinfo参数
+     * @access public
+     * @param  string $url URL地址
+     * @return array
+     */
+    public function parseUrlPath(string $url): array
+    {
+        // 分隔符替换 确保路由定义使用统一的分隔符
+        $url = str_replace('|', '/', $url);
+        $url = trim($url, '/');
+
+        if (strpos($url, '/')) {
+            // [控制器/操作]
+            $path = explode('/', $url);
+        } else {
+            $path = [$url];
+        }
+
+        return $path;
+    }
+
+    /**
+     * 生成路由的正则规则
+     * @access protected
+     * @param  string $rule 路由规则
+     * @param  array  $match 匹配的变量
+     * @param  array  $pattern   路由变量规则
+     * @param  array  $option    路由参数
+     * @param  bool   $completeMatch   路由是否完全匹配
+     * @param  string $suffix   路由正则变量后缀
+     * @return string
+     */
+    protected function buildRuleRegex(string $rule, array $match, array $pattern = [], array $option = [], bool $completeMatch = false, string $suffix = ''): string
+    {
+        foreach ($match as $name) {
+            $value = $this->buildNameRegex($name, $pattern, $suffix);
+            if ($value) {
+                $origin[]  = $name;
+                $replace[] = $value;
+            }
+        }
+
+        // 是否区分 / 地址访问
+        if ('/' != $rule) {
+            if (!empty($option['remove_slash'])) {
+                $rule = rtrim($rule, '/');
+            } elseif (substr($rule, -1) == '/') {
+                $rule     = rtrim($rule, '/');
+                $hasSlash = true;
+            }
+        }
+
+        $regex = isset($replace) ? str_replace($origin, $replace, $rule) : $rule;
+        $regex = str_replace([')?/', ')?-'], [')/', ')-'], $regex);
+
+        if (isset($hasSlash)) {
+            $regex .= '/';
+        }
+
+        return $regex . ($completeMatch ? '$' : '');
+    }
+
+    /**
+     * 生成路由变量的正则规则
+     * @access protected
+     * @param  string $name    路由变量
+     * @param  array  $pattern 变量规则
+     * @param  string $suffix  路由正则变量后缀
+     * @return string
+     */
+    protected function buildNameRegex(string $name, array $pattern, string $suffix): string
+    {
+        $optional = '';
+        $slash    = substr($name, 0, 1);
+
+        if (in_array($slash, ['/', '-'])) {
+            $prefix = $slash;
+            $name   = substr($name, 1);
+            $slash  = substr($name, 0, 1);
+        } else {
+            $prefix = '';
+        }
+
+        if ('<' != $slash) {
+            return '';
+        }
+
+        if (strpos($name, '?')) {
+            $name     = substr($name, 1, -2);
+            $optional = '?';
+        } elseif (strpos($name, '>')) {
+            $name = substr($name, 1, -1);
+        }
+
+        if (isset($pattern[$name])) {
+            $nameRule = $pattern[$name];
+            if (0 === strpos($nameRule, '/') && '/' == substr($nameRule, -1)) {
+                $nameRule = substr($nameRule, 1, -1);
+            }
+        } else {
+            $nameRule = $this->router->config('default_route_pattern');
+        }
+
+        return '(' . $prefix . '(?<' . $name . $suffix . '>' . $nameRule . '))' . $optional;
+    }
+
+    /**
+     * 设置路由参数
+     * @access public
+     * @param  string $method 方法名
+     * @param  array  $args   调用参数
+     * @return $this
+     */
+    public function __call($method, $args)
+    {
+        if (count($args) > 1) {
+            $args[0] = $args;
+        }
+        array_unshift($args, $method);
+
+        return call_user_func_array([$this, 'setOption'], $args);
+    }
+
+    public function __sleep()
+    {
+        return ['name', 'rule', 'route', 'method', 'vars', 'option', 'pattern'];
+    }
+
+    public function __wakeup()
+    {
+        $this->router = Container::pull('route');
+    }
+
+    public function __debugInfo()
+    {
+        return [
+            'name'    => $this->name,
+            'rule'    => $this->rule,
+            'route'   => $this->route,
+            'method'  => $this->method,
+            'vars'    => $this->vars,
+            'option'  => $this->option,
+            'pattern' => $this->pattern,
+        ];
+    }
+}
diff --git a/vendor/topthink/framework/src/think/route/RuleGroup.php b/vendor/topthink/framework/src/think/route/RuleGroup.php
new file mode 100755
index 0000000..cd9ddbd
--- /dev/null
+++ b/vendor/topthink/framework/src/think/route/RuleGroup.php
@@ -0,0 +1,523 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\route;
+
+use Closure;
+use think\Container;
+use think\Exception;
+use think\Request;
+use think\Route;
+
+/**
+ * 路由分组类
+ */
+class RuleGroup extends Rule
+{
+    /**
+     * 分组路由(包括子分组)
+     * @var array
+     */
+    protected $rules = [];
+
+    /**
+     * 分组路由规则
+     * @var mixed
+     */
+    protected $rule;
+
+    /**
+     * MISS路由
+     * @var RuleItem
+     */
+    protected $miss;
+
+    /**
+     * 完整名称
+     * @var string
+     */
+    protected $fullName;
+
+    /**
+     * 分组别名
+     * @var string
+     */
+    protected $alias;
+
+    /**
+     * 架构函数
+     * @access public
+     * @param  Route     $router 路由对象
+     * @param  RuleGroup $parent 上级对象
+     * @param  string    $name   分组名称
+     * @param  mixed     $rule   分组路由
+     */
+    public function __construct(Route $router, RuleGroup $parent = null, string $name = '', $rule = null)
+    {
+        $this->router = $router;
+        $this->parent = $parent;
+        $this->rule   = $rule;
+        $this->name   = trim($name, '/');
+
+        $this->setFullName();
+
+        if ($this->parent) {
+            $this->domain = $this->parent->getDomain();
+            $this->parent->addRuleItem($this);
+        }
+
+        if ($router->isTest()) {
+            $this->lazy(false);
+        }
+    }
+
+    /**
+     * 设置分组的路由规则
+     * @access public
+     * @return void
+     */
+    protected function setFullName(): void
+    {
+        if (false !== strpos($this->name, ':')) {
+            $this->name = preg_replace(['/\[\:(\w+)\]/', '/\:(\w+)/'], ['<\1?>', '<\1>'], $this->name);
+        }
+
+        if ($this->parent && $this->parent->getFullName()) {
+            $this->fullName = $this->parent->getFullName() . ($this->name ? '/' . $this->name : '');
+        } else {
+            $this->fullName = $this->name;
+        }
+
+        if ($this->name) {
+            $this->router->getRuleName()->setGroup($this->name, $this);
+        }
+    }
+
+    /**
+     * 获取所属域名
+     * @access public
+     * @return string
+     */
+    public function getDomain(): string
+    {
+        return $this->domain ?: '-';
+    }
+
+    /**
+     * 获取分组别名
+     * @access public
+     * @return string
+     */
+    public function getAlias(): string
+    {
+        return $this->alias ?: '';
+    }
+
+    /**
+     * 检测分组路由
+     * @access public
+     * @param  Request $request       请求对象
+     * @param  string  $url           访问地址
+     * @param  bool    $completeMatch 路由是否完全匹配
+     * @return Dispatch|false
+     */
+    public function check(Request $request, string $url, bool $completeMatch = false)
+    {
+        // 检查分组有效性
+        if (!$this->checkOption($this->option, $request) || !$this->checkUrl($url)) {
+            return false;
+        }
+
+        // 解析分组路由
+        if ($this instanceof Resource) {
+            $this->buildResourceRule();
+        } else {
+            $this->parseGroupRule($this->rule);
+        }
+
+        // 获取当前路由规则
+        $method = strtolower($request->method());
+        $rules  = $this->getRules($method);
+        $option = $this->getOption();
+
+        if (isset($option['complete_match'])) {
+            $completeMatch = $option['complete_match'];
+        }
+
+        if (!empty($option['merge_rule_regex'])) {
+            // 合并路由正则规则进行路由匹配检查
+            $result = $this->checkMergeRuleRegex($request, $rules, $url, $completeMatch);
+
+            if (false !== $result) {
+                return $result;
+            }
+        }
+
+        // 检查分组路由
+        foreach ($rules as $key => $item) {
+            $result = $item[1]->check($request, $url, $completeMatch);
+
+            if (false !== $result) {
+                return $result;
+            }
+        }
+
+        if (!empty($option['dispatcher'])) {
+            $result = $this->parseRule($request, '', $option['dispatcher'], $url, $option);
+        } elseif ($this->miss && in_array($this->miss->getMethod(), ['*', $method])) {
+            // 未匹配所有路由的路由规则处理
+            $result = $this->parseRule($request, '', $this->miss->getRoute(), $url, $this->miss->getOption());
+        } else {
+            $result = false;
+        }
+
+        return $result;
+    }
+
+    /**
+     * 分组URL匹配检查
+     * @access protected
+     * @param  string $url URL
+     * @return bool
+     */
+    protected function checkUrl(string $url): bool
+    {
+        if ($this->fullName) {
+            $pos = strpos($this->fullName, '<');
+
+            if (false !== $pos) {
+                $str = substr($this->fullName, 0, $pos);
+            } else {
+                $str = $this->fullName;
+            }
+
+            if ($str && 0 !== stripos(str_replace('|', '/', $url), $str)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * 设置路由分组别名
+     * @access public
+     * @param  string $alias 路由分组别名
+     * @return $this
+     */
+    public function alias(string $alias)
+    {
+        $this->alias = $alias;
+        $this->router->getRuleName()->setGroup($alias, $this);
+
+        return $this;
+    }
+
+    /**
+     * 延迟解析分组的路由规则
+     * @access public
+     * @param  bool $lazy 路由是否延迟解析
+     * @return $this
+     */
+    public function lazy(bool $lazy = true)
+    {
+        if (!$lazy) {
+            $this->parseGroupRule($this->rule);
+            $this->rule = null;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 解析分组和域名的路由规则及绑定
+     * @access public
+     * @param  mixed $rule 路由规则
+     * @return void
+     */
+    public function parseGroupRule($rule): void
+    {
+        if (is_string($rule) && is_subclass_of($rule, Dispatch::class)) {
+            $this->dispatcher($rule);
+            return;
+        }
+
+        $origin = $this->router->getGroup();
+        $this->router->setGroup($this);
+
+        if ($rule instanceof \Closure) {
+            Container::getInstance()->invokeFunction($rule);
+        } elseif (is_string($rule) && $rule) {
+            $this->router->bind($rule, $this->domain);
+        }
+
+        $this->router->setGroup($origin);
+    }
+
+    /**
+     * 检测分组路由
+     * @access public
+     * @param  Request $request       请求对象
+     * @param  array   $rules         路由规则
+     * @param  string  $url           访问地址
+     * @param  bool    $completeMatch 路由是否完全匹配
+     * @return Dispatch|false
+     */
+    protected function checkMergeRuleRegex(Request $request, array &$rules, string $url, bool $completeMatch)
+    {
+        $depr  = $this->router->config('pathinfo_depr');
+        $url   = $depr . str_replace('|', $depr, $url);
+        $regex = [];
+        $items = [];
+
+        foreach ($rules as $key => $val) {
+            $item = $val[1];
+            if ($item instanceof RuleItem) {
+                $rule = $depr . str_replace('/', $depr, $item->getRule());
+                if ($depr == $rule && $depr != $url) {
+                    unset($rules[$key]);
+                    continue;
+                }
+
+                $complete = $item->getOption('complete_match', $completeMatch);
+
+                if (false === strpos($rule, '<')) {
+                    if (0 === strcasecmp($rule, $url) || (!$complete && 0 === strncasecmp($rule, $url, strlen($rule)))) {
+                        return $item->checkRule($request, $url, []);
+                    }
+
+                    unset($rules[$key]);
+                    continue;
+                }
+
+                $slash = preg_quote('/-' . $depr, '/');
+
+                if ($matchRule = preg_split('/[' . $slash . ']<\w+\??>/', $rule, 2)) {
+                    if ($matchRule[0] && 0 !== strncasecmp($rule, $url, strlen($matchRule[0]))) {
+                        unset($rules[$key]);
+                        continue;
+                    }
+                }
+
+                if (preg_match_all('/[' . $slash . ']?<?\w+\??>?/', $rule, $matches)) {
+                    unset($rules[$key]);
+                    $pattern = array_merge($this->getPattern(), $item->getPattern());
+                    $option  = array_merge($this->getOption(), $item->getOption());
+
+                    $regex[$key] = $this->buildRuleRegex($rule, $matches[0], $pattern, $option, $complete, '_THINK_' . $key);
+                    $items[$key] = $item;
+                }
+            }
+        }
+
+        if (empty($regex)) {
+            return false;
+        }
+
+        try {
+            $result = preg_match('~^(?:' . implode('|', $regex) . ')~u', $url, $match);
+        } catch (\Exception $e) {
+            throw new Exception('route pattern error');
+        }
+
+        if ($result) {
+            $var = [];
+            foreach ($match as $key => $val) {
+                if (is_string($key) && '' !== $val) {
+                    [$name, $pos] = explode('_THINK_', $key);
+
+                    $var[$name] = $val;
+                }
+            }
+
+            if (!isset($pos)) {
+                foreach ($regex as $key => $item) {
+                    if (0 === strpos(str_replace(['\/', '\-', '\\' . $depr], ['/', '-', $depr], $item), $match[0])) {
+                        $pos = $key;
+                        break;
+                    }
+                }
+            }
+
+            $rule  = $items[$pos]->getRule();
+            $array = $this->router->getRule($rule);
+
+            foreach ($array as $item) {
+                if (in_array($item->getMethod(), ['*', strtolower($request->method())])) {
+                    $result = $item->checkRule($request, $url, $var);
+
+                    if (false !== $result) {
+                        return $result;
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * 获取分组的MISS路由
+     * @access public
+     * @return RuleItem|null
+     */
+    public function getMissRule():  ? RuleItem
+    {
+        return $this->miss;
+    }
+
+    /**
+     * 注册MISS路由
+     * @access public
+     * @param  string|Closure $route  路由地址
+     * @param  string         $method 请求类型
+     * @return RuleItem
+     */
+    public function miss($route, string $method = '*') : RuleItem
+    {
+        // 创建路由规则实例
+        $ruleItem = new RuleItem($this->router, $this, null, '', $route, strtolower($method));
+
+        $ruleItem->setMiss();
+        $this->miss = $ruleItem;
+
+        return $ruleItem;
+    }
+
+    /**
+     * 添加分组下的路由规则
+     * @access public
+     * @param  string $rule   路由规则
+     * @param  mixed  $route  路由地址
+     * @param  string $method 请求类型
+     * @return RuleItem
+     */
+    public function addRule(string $rule, $route = null, string $method = '*'): RuleItem
+    {
+        // 读取路由标识
+        if (is_string($route)) {
+            $name = $route;
+        } else {
+            $name = null;
+        }
+
+        $method = strtolower($method);
+
+        if ('' === $rule || '/' === $rule) {
+            $rule .= '$';
+        }
+
+        // 创建路由规则实例
+        $ruleItem = new RuleItem($this->router, $this, $name, $rule, $route, $method);
+
+        $this->addRuleItem($ruleItem, $method);
+
+        return $ruleItem;
+    }
+
+    /**
+     * 注册分组下的路由规则
+     * @access public
+     * @param  Rule   $rule   路由规则
+     * @param  string $method 请求类型
+     * @return $this
+     */
+    public function addRuleItem(Rule $rule, string $method = '*')
+    {
+        if (strpos($method, '|')) {
+            $rule->method($method);
+            $method = '*';
+        }
+
+        $this->rules[] = [$method, $rule];
+
+        if ($rule instanceof RuleItem && 'options' != $method) {
+            $this->rules[] = ['options', $rule->setAutoOptions()];
+        }
+
+        return $this;
+    }
+
+    /**
+     * 设置分组的路由前缀
+     * @access public
+     * @param  string $prefix 路由前缀
+     * @return $this
+     */
+    public function prefix(string $prefix)
+    {
+        if ($this->parent && $this->parent->getOption('prefix')) {
+            $prefix = $this->parent->getOption('prefix') . $prefix;
+        }
+
+        return $this->setOption('prefix', $prefix);
+    }
+
+    /**
+     * 合并分组的路由规则正则
+     * @access public
+     * @param  bool $merge 是否合并
+     * @return $this
+     */
+    public function mergeRuleRegex(bool $merge = true)
+    {
+        return $this->setOption('merge_rule_regex', $merge);
+    }
+
+    /**
+     * 设置分组的Dispatch调度
+     * @access public
+     * @param  string $dispatch 调度类
+     * @return $this
+     */
+    public function dispatcher(string $dispatch)
+    {
+        return $this->setOption('dispatcher', $dispatch);
+    }
+
+    /**
+     * 获取完整分组Name
+     * @access public
+     * @return string
+     */
+    public function getFullName(): string
+    {
+        return $this->fullName ?: '';
+    }
+
+    /**
+     * 获取分组的路由规则
+     * @access public
+     * @param  string $method 请求类型
+     * @return array
+     */
+    public function getRules(string $method = ''): array
+    {
+        if ('' === $method) {
+            return $this->rules;
+        }
+
+        return array_filter($this->rules, function ($item) use ($method) {
+            return $method == $item[0] || '*' == $item[0];
+        });
+    }
+
+    /**
+     * 清空分组下的路由规则
+     * @access public
+     * @return void
+     */
+    public function clear(): void
+    {
+        $this->rules = [];
+    }
+}
diff --git a/vendor/topthink/framework/src/think/route/RuleItem.php b/vendor/topthink/framework/src/think/route/RuleItem.php
new file mode 100755
index 0000000..e4e3246
--- /dev/null
+++ b/vendor/topthink/framework/src/think/route/RuleItem.php
@@ -0,0 +1,330 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\route;
+
+use think\Exception;
+use think\Request;
+use think\Route;
+
+/**
+ * 路由规则类
+ */
+class RuleItem extends Rule
+{
+    /**
+     * 是否为MISS规则
+     * @var bool
+     */
+    protected $miss = false;
+
+    /**
+     * 是否为额外自动注册的OPTIONS规则
+     * @var bool
+     */
+    protected $autoOption = false;
+
+    /**
+     * 架构函数
+     * @access public
+     * @param  Route             $router 路由实例
+     * @param  RuleGroup         $parent 上级对象
+     * @param  string            $name 路由标识
+     * @param  string            $rule 路由规则
+     * @param  string|\Closure   $route 路由地址
+     * @param  string            $method 请求类型
+     */
+    public function __construct(Route $router, RuleGroup $parent, string $name = null, string $rule = '', $route = null, string $method = '*')
+    {
+        $this->router = $router;
+        $this->parent = $parent;
+        $this->name   = $name;
+        $this->route  = $route;
+        $this->method = $method;
+
+        $this->setRule($rule);
+
+        $this->router->setRule($this->rule, $this);
+    }
+
+    /**
+     * 设置当前路由规则为MISS路由
+     * @access public
+     * @return $this
+     */
+    public function setMiss()
+    {
+        $this->miss = true;
+        return $this;
+    }
+
+    /**
+     * 判断当前路由规则是否为MISS路由
+     * @access public
+     * @return bool
+     */
+    public function isMiss(): bool
+    {
+        return $this->miss;
+    }
+
+    /**
+     * 设置当前路由为自动注册OPTIONS
+     * @access public
+     * @return $this
+     */
+    public function setAutoOptions()
+    {
+        $this->autoOption = true;
+        return $this;
+    }
+
+    /**
+     * 判断当前路由规则是否为自动注册的OPTIONS路由
+     * @access public
+     * @return bool
+     */
+    public function isAutoOptions(): bool
+    {
+        return $this->autoOption;
+    }
+
+    /**
+     * 获取当前路由的URL后缀
+     * @access public
+     * @return string|null
+     */
+    public function getSuffix()
+    {
+        if (isset($this->option['ext'])) {
+            $suffix = $this->option['ext'];
+        } elseif ($this->parent->getOption('ext')) {
+            $suffix = $this->parent->getOption('ext');
+        } else {
+            $suffix = null;
+        }
+
+        return $suffix;
+    }
+
+    /**
+     * 路由规则预处理
+     * @access public
+     * @param  string      $rule     路由规则
+     * @return void
+     */
+    public function setRule(string $rule): void
+    {
+        if ('$' == substr($rule, -1, 1)) {
+            // 是否完整匹配
+            $rule = substr($rule, 0, -1);
+
+            $this->option['complete_match'] = true;
+        }
+
+        $rule = '/' != $rule ? ltrim($rule, '/') : '';
+
+        if ($this->parent && $prefix = $this->parent->getFullName()) {
+            $rule = $prefix . ($rule ? '/' . ltrim($rule, '/') : '');
+        }
+
+        if (false !== strpos($rule, ':')) {
+            $this->rule = preg_replace(['/\[\:(\w+)\]/', '/\:(\w+)/'], ['<\1?>', '<\1>'], $rule);
+        } else {
+            $this->rule = $rule;
+        }
+
+        // 生成路由标识的快捷访问
+        $this->setRuleName();
+    }
+
+    /**
+     * 设置别名
+     * @access public
+     * @param  string     $name
+     * @return $this
+     */
+    public function name(string $name)
+    {
+        $this->name = $name;
+        $this->setRuleName(true);
+
+        return $this;
+    }
+
+    /**
+     * 设置路由标识 用于URL反解生成
+     * @access protected
+     * @param  bool $first 是否插入开头
+     * @return void
+     */
+    protected function setRuleName(bool $first = false): void
+    {
+        if ($this->name) {
+            $this->router->setName($this->name, $this, $first);
+        }
+    }
+
+    /**
+     * 检测路由
+     * @access public
+     * @param  Request      $request  请求对象
+     * @param  string       $url      访问地址
+     * @param  array        $match    匹配路由变量
+     * @param  bool         $completeMatch   路由是否完全匹配
+     * @return Dispatch|false
+     */
+    public function checkRule(Request $request, string $url, $match = null, bool $completeMatch = false)
+    {
+        // 检查参数有效性
+        if (!$this->checkOption($this->option, $request)) {
+            return false;
+        }
+
+        // 合并分组参数
+        $option  = $this->getOption();
+        $pattern = $this->getPattern();
+        $url     = $this->urlSuffixCheck($request, $url, $option);
+
+        if (is_null($match)) {
+            $match = $this->checkMatch($url, $option, $pattern, $completeMatch);
+        }
+
+        if (false !== $match) {
+            return $this->parseRule($request, $this->rule, $this->route, $url, $option, $match);
+        }
+
+        return false;
+    }
+
+    /**
+     * 检测路由(含路由匹配)
+     * @access public
+     * @param  Request      $request  请求对象
+     * @param  string       $url      访问地址
+     * @param  bool         $completeMatch   路由是否完全匹配
+     * @return Dispatch|false
+     */
+    public function check(Request $request, string $url, bool $completeMatch = false)
+    {
+        return $this->checkRule($request, $url, null, $completeMatch);
+    }
+
+    /**
+     * URL后缀及Slash检查
+     * @access protected
+     * @param  Request      $request  请求对象
+     * @param  string       $url      访问地址
+     * @param  array        $option   路由参数
+     * @return string
+     */
+    protected function urlSuffixCheck(Request $request, string $url, array $option = []): string
+    {
+        // 是否区分 / 地址访问
+        if (!empty($option['remove_slash']) && '/' != $this->rule) {
+            $this->rule = rtrim($this->rule, '/');
+            $url        = rtrim($url, '|');
+        }
+
+        if (isset($option['ext'])) {
+            // 路由ext参数 优先于系统配置的URL伪静态后缀参数
+            $url = preg_replace('/\.(' . $request->ext() . ')$/i', '', $url);
+        }
+
+        return $url;
+    }
+
+    /**
+     * 检测URL和规则路由是否匹配
+     * @access private
+     * @param  string    $url URL地址
+     * @param  array     $option    路由参数
+     * @param  array     $pattern   变量规则
+     * @param  bool      $completeMatch   是否完全匹配
+     * @return array|false
+     */
+    private function checkMatch(string $url, array $option, array $pattern, bool $completeMatch)
+    {
+        if (isset($option['complete_match'])) {
+            $completeMatch = $option['complete_match'];
+        }
+
+        $depr = $this->router->config('pathinfo_depr');
+
+        // 检查完整规则定义
+        if (isset($pattern['__url__']) && !preg_match(0 === strpos($pattern['__url__'], '/') ? $pattern['__url__'] : '/^' . $pattern['__url__'] . ($completeMatch ? '$' : '') . '/', str_replace('|', $depr, $url))) {
+            return false;
+        }
+
+        $var  = [];
+        $url  = $depr . str_replace('|', $depr, $url);
+        $rule = $depr . str_replace('/', $depr, $this->rule);
+
+        if ($depr == $rule && $depr != $url) {
+            return false;
+        }
+
+        if (false === strpos($rule, '<')) {
+            if (0 === strcasecmp($rule, $url) || (!$completeMatch && 0 === strncasecmp($rule . $depr, $url . $depr, strlen($rule . $depr)))) {
+                return $var;
+            }
+            return false;
+        }
+
+        $slash = preg_quote('/-' . $depr, '/');
+
+        if ($matchRule = preg_split('/[' . $slash . ']?<\w+\??>/', $rule, 2)) {
+            if ($matchRule[0] && 0 !== strncasecmp($rule, $url, strlen($matchRule[0]))) {
+                return false;
+            }
+        }
+
+        if (preg_match_all('/[' . $slash . ']?<?\w+\??>?/', $rule, $matches)) {
+            $regex = $this->buildRuleRegex($rule, $matches[0], $pattern, $option, $completeMatch);
+
+            try {
+                if (!preg_match('~^' . $regex . '~u', $url, $match)) {
+                    return false;
+                }
+            } catch (\Exception $e) {
+                throw new Exception('route pattern error');
+            }
+
+            foreach ($match as $key => $val) {
+                if (is_string($key)) {
+                    $var[$key] = $val;
+                }
+            }
+        }
+
+        // 成功匹配后返回URL中的动态变量数组
+        return $var;
+    }
+
+    /**
+     * 设置路由所属分组(用于注解路由)
+     * @access public
+     * @param  string $name 分组名称或者标识
+     * @return $this
+     */
+    public function group(string $name)
+    {
+        $group = $this->router->getRuleName()->getGroup($name);
+
+        if ($group) {
+            $this->parent = $group;
+            $this->setRule($this->rule);
+        }
+
+        return $this;
+    }
+}
diff --git a/vendor/topthink/framework/src/think/route/RuleName.php b/vendor/topthink/framework/src/think/route/RuleName.php
new file mode 100755
index 0000000..0684367
--- /dev/null
+++ b/vendor/topthink/framework/src/think/route/RuleName.php
@@ -0,0 +1,211 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\route;
+
+/**
+ * 路由标识管理类
+ */
+class RuleName
+{
+    /**
+     * 路由标识
+     * @var array
+     */
+    protected $item = [];
+
+    /**
+     * 路由规则
+     * @var array
+     */
+    protected $rule = [];
+
+    /**
+     * 路由分组
+     * @var array
+     */
+    protected $group = [];
+
+    /**
+     * 注册路由标识
+     * @access public
+     * @param  string   $name  路由标识
+     * @param  RuleItem $ruleItem 路由规则
+     * @param  bool     $first 是否优先
+     * @return void
+     */
+    public function setName(string $name, RuleItem $ruleItem, bool $first = false): void
+    {
+        $name = strtolower($name);
+        $item = $this->getRuleItemInfo($ruleItem);
+        if ($first && isset($this->item[$name])) {
+            array_unshift($this->item[$name], $item);
+        } else {
+            $this->item[$name][] = $item;
+        }
+    }
+
+    /**
+     * 注册路由分组标识
+     * @access public
+     * @param  string    $name  路由分组标识
+     * @param  RuleGroup $group 路由分组
+     * @return void
+     */
+    public function setGroup(string $name, RuleGroup $group): void
+    {
+        $this->group[strtolower($name)] = $group;
+    }
+
+    /**
+     * 注册路由规则
+     * @access public
+     * @param  string   $rule  路由规则
+     * @param  RuleItem $ruleItem 路由
+     * @return void
+     */
+    public function setRule(string $rule, RuleItem $ruleItem): void
+    {
+        $route = $ruleItem->getRoute();
+
+        if (is_string($route)) {
+            $this->rule[$rule][$route] = $ruleItem;
+        } else {
+            $this->rule[$rule][] = $ruleItem;
+        }
+    }
+
+    /**
+     * 根据路由规则获取路由对象(列表)
+     * @access public
+     * @param  string $rule   路由标识
+     * @return RuleItem[]
+     */
+    public function getRule(string $rule): array
+    {
+        return $this->rule[$rule] ?? [];
+    }
+
+    /**
+     * 根据路由分组标识获取分组
+     * @access public
+     * @param  string $name 路由分组标识
+     * @return RuleGroup|null
+     */
+    public function getGroup(string $name)
+    {
+        return $this->group[strtolower($name)] ?? null;
+    }
+
+    /**
+     * 清空路由规则
+     * @access public
+     * @return void
+     */
+    public function clear(): void
+    {
+        $this->item = [];
+        $this->rule = [];
+    }
+
+    /**
+     * 获取全部路由列表
+     * @access public
+     * @return array
+     */
+    public function getRuleList(): array
+    {
+        $list = [];
+
+        foreach ($this->rule as $rule => $rules) {
+            foreach ($rules as $item) {
+                $val = [];
+
+                foreach (['method', 'rule', 'name', 'route', 'domain', 'pattern', 'option'] as $param) {
+                    $call        = 'get' . $param;
+                    $val[$param] = $item->$call();
+                }
+
+                if ($item->isMiss()) {
+                    $val['rule'] .= '<MISS>';
+                }
+
+                $list[] = $val;
+            }
+        }
+
+        return $list;
+    }
+
+    /**
+     * 导入路由标识
+     * @access public
+     * @param  array $item 路由标识
+     * @return void
+     */
+    public function import(array $item): void
+    {
+        $this->item = $item;
+    }
+
+    /**
+     * 根据路由标识获取路由信息(用于URL生成)
+     * @access public
+     * @param  string $name   路由标识
+     * @param  string $domain 域名
+     * @param  string $method 请求类型
+     * @return array
+     */
+    public function getName(string $name = null, string $domain = null, string $method = '*'): array
+    {
+        if (is_null($name)) {
+            return $this->item;
+        }
+
+        $name   = strtolower($name);
+        $method = strtolower($method);
+        $result = [];
+
+        if (isset($this->item[$name])) {
+            if (is_null($domain)) {
+                $result = $this->item[$name];
+            } else {
+                foreach ($this->item[$name] as $item) {
+                    $itemDomain = $item['domain'];
+                    $itemMethod = $item['method'];
+
+                    if (($itemDomain == $domain || '-' == $itemDomain) && ('*' == $itemMethod || '*' == $method || $method == $itemMethod)) {
+                        $result[] = $item;
+                    }
+                }
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * 获取路由信息
+     * @access protected
+     * @param  RuleItem $item 路由规则
+     * @return array
+     */
+    protected function getRuleItemInfo(RuleItem $item): array
+    {
+        return [
+            'rule'   => $item->getRule(),
+            'domain' => $item->getDomain(),
+            'method' => $item->getMethod(),
+            'suffix' => $item->getSuffix(),
+        ];
+    }
+}
diff --git a/vendor/topthink/framework/src/think/route/Url.php b/vendor/topthink/framework/src/think/route/Url.php
new file mode 100755
index 0000000..b3f8b9f
--- /dev/null
+++ b/vendor/topthink/framework/src/think/route/Url.php
@@ -0,0 +1,517 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\route;
+
+use think\App;
+use think\Route;
+
+/**
+ * 路由地址生成
+ */
+class Url
+{
+    /**
+     * 应用对象
+     * @var App
+     */
+    protected $app;
+
+    /**
+     * 路由对象
+     * @var Route
+     */
+    protected $route;
+
+    /**
+     * URL变量
+     * @var array
+     */
+    protected $vars = [];
+
+    /**
+     * 路由URL
+     * @var string
+     */
+    protected $url;
+
+    /**
+     * URL 根地址
+     * @var string
+     */
+    protected $root = '';
+
+    /**
+     * HTTPS
+     * @var bool
+     */
+    protected $https;
+
+    /**
+     * URL后缀
+     * @var string|bool
+     */
+    protected $suffix = true;
+
+    /**
+     * URL域名
+     * @var string|bool
+     */
+    protected $domain = false;
+
+    /**
+     * 架构函数
+     * @access public
+     * @param  string $url URL地址
+     * @param  array  $vars 参数
+     */
+    public function __construct(Route $route, App $app, string $url = '', array $vars = [])
+    {
+        $this->route = $route;
+        $this->app   = $app;
+        $this->url   = $url;
+        $this->vars  = $vars;
+    }
+
+    /**
+     * 设置URL参数
+     * @access public
+     * @param  array $vars URL参数
+     * @return $this
+     */
+    public function vars(array $vars = [])
+    {
+        $this->vars = $vars;
+        return $this;
+    }
+
+    /**
+     * 设置URL后缀
+     * @access public
+     * @param  string|bool $suffix URL后缀
+     * @return $this
+     */
+    public function suffix($suffix)
+    {
+        $this->suffix = $suffix;
+        return $this;
+    }
+
+    /**
+     * 设置URL域名(或者子域名)
+     * @access public
+     * @param  string|bool $domain URL域名
+     * @return $this
+     */
+    public function domain($domain)
+    {
+        $this->domain = $domain;
+        return $this;
+    }
+
+    /**
+     * 设置URL 根地址
+     * @access public
+     * @param  string $root URL root
+     * @return $this
+     */
+    public function root(string $root)
+    {
+        $this->root = $root;
+        return $this;
+    }
+
+    /**
+     * 设置是否使用HTTPS
+     * @access public
+     * @param  bool $https
+     * @return $this
+     */
+    public function https(bool $https = true)
+    {
+        $this->https = $https;
+        return $this;
+    }
+
+    /**
+     * 检测域名
+     * @access protected
+     * @param  string      $url URL
+     * @param  string|true $domain 域名
+     * @return string
+     */
+    protected function parseDomain(string &$url, $domain): string
+    {
+        if (!$domain) {
+            return '';
+        }
+
+        $request    = $this->app->request;
+        $rootDomain = $request->rootDomain();
+
+        if (true === $domain) {
+            // 自动判断域名
+            $domain  = $request->host();
+            $domains = $this->route->getDomains();
+
+            if (!empty($domains)) {
+                $routeDomain = array_keys($domains);
+                foreach ($routeDomain as $domainPrefix) {
+                    if (0 === strpos($domainPrefix, '*.') && strpos($domain, ltrim($domainPrefix, '*.')) !== false) {
+                        foreach ($domains as $key => $rule) {
+                            $rule = is_array($rule) ? $rule[0] : $rule;
+                            if (is_string($rule) && false === strpos($key, '*') && 0 === strpos($url, $rule)) {
+                                $url    = ltrim($url, $rule);
+                                $domain = $key;
+
+                                // 生成对应子域名
+                                if (!empty($rootDomain)) {
+                                    $domain .= $rootDomain;
+                                }
+                                break;
+                            } elseif (false !== strpos($key, '*')) {
+                                if (!empty($rootDomain)) {
+                                    $domain .= $rootDomain;
+                                }
+
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+        } elseif (false === strpos($domain, '.') && 0 !== strpos($domain, $rootDomain)) {
+            $domain .= '.' . $rootDomain;
+        }
+
+        if (false !== strpos($domain, '://')) {
+            $scheme = '';
+        } else {
+            $scheme = $this->https || $request->isSsl() ? 'https://' : 'http://';
+        }
+
+        return $scheme . $domain;
+    }
+
+    /**
+     * 解析URL后缀
+     * @access protected
+     * @param  string|bool $suffix 后缀
+     * @return string
+     */
+    protected function parseSuffix($suffix): string
+    {
+        if ($suffix) {
+            $suffix = true === $suffix ? $this->route->config('url_html_suffix') : $suffix;
+
+            if (is_string($suffix) && $pos = strpos($suffix, '|')) {
+                $suffix = substr($suffix, 0, $pos);
+            }
+        }
+
+        return (empty($suffix) || 0 === strpos($suffix, '.')) ? (string) $suffix : '.' . $suffix;
+    }
+
+    /**
+     * 直接解析URL地址
+     * @access protected
+     * @param  string      $url URL
+     * @param  string|bool $domain Domain
+     * @return string
+     */
+    protected function parseUrl(string $url, &$domain): string
+    {
+        $request = $this->app->request;
+
+        if (0 === strpos($url, '/')) {
+            // 直接作为路由地址解析
+            $url = substr($url, 1);
+        } elseif (false !== strpos($url, '\\')) {
+            // 解析到类
+            $url = ltrim(str_replace('\\', '/', $url), '/');
+        } elseif (0 === strpos($url, '@')) {
+            // 解析到控制器
+            $url = substr($url, 1);
+        } elseif ('' === $url) {
+            $url = $request->controller() . '/' . $request->action();
+        } else {
+            $controller = $request->controller();
+
+            $path       = explode('/', $url);
+            $action     = array_pop($path);
+            $controller = empty($path) ? $controller : array_pop($path);
+
+            $url = $controller . '/' . $action;
+        }
+
+        return $url;
+    }
+
+    /**
+     * 分析路由规则中的变量
+     * @access protected
+     * @param  string $rule 路由规则
+     * @return array
+     */
+    protected function parseVar(string $rule): array
+    {
+        // 提取路由规则中的变量
+        $var = [];
+
+        if (preg_match_all('/<\w+\??>/', $rule, $matches)) {
+            foreach ($matches[0] as $name) {
+                $optional = false;
+
+                if (strpos($name, '?')) {
+                    $name     = substr($name, 1, -2);
+                    $optional = true;
+                } else {
+                    $name = substr($name, 1, -1);
+                }
+
+                $var[$name] = $optional ? 2 : 1;
+            }
+        }
+
+        return $var;
+    }
+
+    /**
+     * 匹配路由地址
+     * @access protected
+     * @param  array $rule 路由规则
+     * @param  array $vars 路由变量
+     * @param  mixed $allowDomain 允许域名
+     * @return array
+     */
+    protected function getRuleUrl(array $rule, array &$vars = [], $allowDomain = ''): array
+    {
+        $request = $this->app->request;
+        if (is_string($allowDomain) && false === strpos($allowDomain, '.')) {
+            $allowDomain .= '.' . $request->rootDomain();
+        }
+        $port = $request->port();
+
+        foreach ($rule as $item) {
+            $url     = $item['rule'];
+            $pattern = $this->parseVar($url);
+            $domain  = $item['domain'];
+            $suffix  = $item['suffix'];
+
+            if ('-' == $domain) {
+                $domain = is_string($allowDomain) ? $allowDomain : $request->host(true);
+            }
+
+            if (is_string($allowDomain) && $domain != $allowDomain) {
+                continue;
+            }
+
+            if ($port && !in_array($port, [80, 443])) {
+                $domain .= ':' . $port;
+            }
+
+            if (empty($pattern)) {
+                return [rtrim($url, '?-'), $domain, $suffix];
+            }
+
+            $type = $this->route->config('url_common_param');
+            $keys = [];
+
+            foreach ($pattern as $key => $val) {
+                if (isset($vars[$key])) {
+                    $url    = str_replace(['[:' . $key . ']', '<' . $key . '?>', ':' . $key, '<' . $key . '>'], $type ? (string) $vars[$key] : urlencode((string) $vars[$key]), $url);
+                    $keys[] = $key;
+                    $url    = str_replace(['/?', '-?'], ['/', '-'], $url);
+                    $result = [rtrim($url, '?-'), $domain, $suffix];
+                } elseif (2 == $val) {
+                    $url    = str_replace(['/[:' . $key . ']', '[:' . $key . ']', '<' . $key . '?>'], '', $url);
+                    $url    = str_replace(['/?', '-?'], ['/', '-'], $url);
+                    $result = [rtrim($url, '?-'), $domain, $suffix];
+                } else {
+                    $result = null;
+                    $keys   = [];
+                    break;
+                }
+            }
+
+            $vars = array_diff_key($vars, array_flip($keys));
+
+            if (isset($result)) {
+                return $result;
+            }
+        }
+
+        return [];
+    }
+
+    /**
+     * 生成URL地址
+     * @access public
+     * @return string
+     */
+    public function build()
+    {
+        // 解析URL
+        $url     = $this->url;
+        $suffix  = $this->suffix;
+        $domain  = $this->domain;
+        $request = $this->app->request;
+        $vars    = $this->vars;
+
+        if (0 === strpos($url, '[') && $pos = strpos($url, ']')) {
+            // [name] 表示使用路由命名标识生成URL
+            $name = substr($url, 1, $pos - 1);
+            $url  = 'name' . substr($url, $pos + 1);
+        }
+
+        if (false === strpos($url, '://') && 0 !== strpos($url, '/')) {
+            $info = parse_url($url);
+            $url  = !empty($info['path']) ? $info['path'] : '';
+
+            if (isset($info['fragment'])) {
+                // 解析锚点
+                $anchor = $info['fragment'];
+
+                if (false !== strpos($anchor, '?')) {
+                    // 解析参数
+                    [$anchor, $info['query']] = explode('?', $anchor, 2);
+                }
+
+                if (false !== strpos($anchor, '@')) {
+                    // 解析域名
+                    [$anchor, $domain] = explode('@', $anchor, 2);
+                }
+            } elseif (strpos($url, '@') && false === strpos($url, '\\')) {
+                // 解析域名
+                [$url, $domain] = explode('@', $url, 2);
+            }
+        }
+
+        if ($url) {
+            $checkName   = isset($name) ? $name : $url . (isset($info['query']) ? '?' . $info['query'] : '');
+            $checkDomain = $domain && is_string($domain) ? $domain : null;
+
+            $rule = $this->route->getName($checkName, $checkDomain);
+
+            if (empty($rule) && isset($info['query'])) {
+                $rule = $this->route->getName($url, $checkDomain);
+                // 解析地址里面参数 合并到vars
+                parse_str($info['query'], $params);
+                $vars = array_merge($params, $vars);
+                unset($info['query']);
+            }
+        }
+
+        if (!empty($rule) && $match = $this->getRuleUrl($rule, $vars, $domain)) {
+            // 匹配路由命名标识
+            $url = $match[0];
+
+            if ($domain && !empty($match[1])) {
+                $domain = $match[1];
+            }
+
+            if (!is_null($match[2])) {
+                $suffix = $match[2];
+            }
+        } elseif (!empty($rule) && isset($name)) {
+            throw new \InvalidArgumentException('route name not exists:' . $name);
+        } else {
+            // 检测URL绑定
+            $bind = $this->route->getDomainBind($domain && is_string($domain) ? $domain : null);
+
+            if ($bind && 0 === strpos($url, $bind)) {
+                $url = substr($url, strlen($bind) + 1);
+            } else {
+                $binds = $this->route->getBind();
+
+                foreach ($binds as $key => $val) {
+                    if (is_string($val) && 0 === strpos($url, $val) && substr_count($val, '/') > 1) {
+                        $url    = substr($url, strlen($val) + 1);
+                        $domain = $key;
+                        break;
+                    }
+                }
+            }
+
+            // 路由标识不存在 直接解析
+            $url = $this->parseUrl($url, $domain);
+
+            if (isset($info['query'])) {
+                // 解析地址里面参数 合并到vars
+                parse_str($info['query'], $params);
+                $vars = array_merge($params, $vars);
+            }
+        }
+
+        // 还原URL分隔符
+        $depr = $this->route->config('pathinfo_depr');
+        $url  = str_replace('/', $depr, $url);
+
+        $file = $request->baseFile();
+        if ($file && 0 !== strpos($request->url(), $file)) {
+            $file = str_replace('\\', '/', dirname($file));
+        }
+
+        $url = rtrim($file, '/') . '/' . $url;
+
+        // URL后缀
+        if ('/' == substr($url, -1) || '' == $url) {
+            $suffix = '';
+        } else {
+            $suffix = $this->parseSuffix($suffix);
+        }
+
+        // 锚点
+        $anchor = !empty($anchor) ? '#' . $anchor : '';
+
+        // 参数组装
+        if (!empty($vars)) {
+            // 添加参数
+            if ($this->route->config('url_common_param')) {
+                $vars = http_build_query($vars);
+                $url .= $suffix . ($vars ? '?' . $vars : '') . $anchor;
+            } else {
+                foreach ($vars as $var => $val) {
+                    $val = (string) $val;
+                    if ('' !== $val) {
+                        $url .= $depr . $var . $depr . urlencode($val);
+                    }
+                }
+
+                $url .= $suffix . $anchor;
+            }
+        } else {
+            $url .= $suffix . $anchor;
+        }
+
+        // 检测域名
+        $domain = $this->parseDomain($url, $domain);
+
+        // URL组装
+        return $domain . rtrim($this->root, '/') . '/' . ltrim($url, '/');
+    }
+
+    public function __toString()
+    {
+        return $this->build();
+    }
+
+    public function __debugInfo()
+    {
+        return [
+            'url'    => $this->url,
+            'vars'   => $this->vars,
+            'suffix' => $this->suffix,
+            'domain' => $this->domain,
+        ];
+    }
+}
diff --git a/vendor/topthink/framework/src/think/route/dispatch/Callback.php b/vendor/topthink/framework/src/think/route/dispatch/Callback.php
new file mode 100755
index 0000000..2044ef8
--- /dev/null
+++ b/vendor/topthink/framework/src/think/route/dispatch/Callback.php
@@ -0,0 +1,30 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\route\dispatch;
+
+use think\route\Dispatch;
+
+/**
+ * Callback Dispatcher
+ */
+class Callback extends Dispatch
+{
+    public function exec()
+    {
+        // 执行回调方法
+        $vars = array_merge($this->request->param(), $this->param);
+
+        return $this->app->invoke($this->dispatch, $vars);
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/route/dispatch/Controller.php b/vendor/topthink/framework/src/think/route/dispatch/Controller.php
new file mode 100755
index 0000000..f18512c
--- /dev/null
+++ b/vendor/topthink/framework/src/think/route/dispatch/Controller.php
@@ -0,0 +1,194 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\route\dispatch;
+
+use ReflectionClass;
+use ReflectionException;
+use ReflectionMethod;
+use think\App;
+use think\exception\ClassNotFoundException;
+use think\exception\HttpException;
+use think\helper\Str;
+use think\route\Dispatch;
+
+/**
+ * Controller Dispatcher
+ */
+class Controller extends Dispatch
+{
+    /**
+     * 控制器名
+     * @var string
+     */
+    protected $controller;
+
+    /**
+     * 操作名
+     * @var string
+     */
+    protected $actionName;
+
+    public function init(App $app)
+    {
+        parent::init($app);
+
+        $result = $this->dispatch;
+
+        if (is_string($result)) {
+            $result = explode('/', $result);
+        }
+
+        // 获取控制器名
+        $controller = strip_tags($result[0] ?: $this->rule->config('default_controller'));
+
+        if (strpos($controller, '.')) {
+            $pos              = strrpos($controller, '.');
+            $this->controller = substr($controller, 0, $pos) . '.' . Str::studly(substr($controller, $pos + 1));
+        } else {
+            $this->controller = Str::studly($controller);
+        }
+
+        // 获取操作名
+        $this->actionName = strip_tags($result[1] ?: $this->rule->config('default_action'));
+
+        // 设置当前请求的控制器、操作
+        $this->request
+            ->setController($this->controller)
+            ->setAction($this->actionName);
+    }
+
+    public function exec()
+    {
+        try {
+            // 实例化控制器
+            $instance = $this->controller($this->controller);
+        } catch (ClassNotFoundException $e) {
+            throw new HttpException(404, 'controller not exists:' . $e->getClass());
+        }
+
+        // 注册控制器中间件
+        $this->registerControllerMiddleware($instance);
+
+        return $this->app->middleware->pipeline('controller')
+            ->send($this->request)
+            ->then(function () use ($instance) {
+                // 获取当前操作名
+                $suffix = $this->rule->config('action_suffix');
+                $action = $this->actionName . $suffix;
+
+                if (is_callable([$instance, $action])) {
+                    $vars = $this->request->param();
+                    try {
+                        $reflect = new ReflectionMethod($instance, $action);
+                        // 严格获取当前操作方法名
+                        $actionName = $reflect->getName();
+                        if ($suffix) {
+                            $actionName = substr($actionName, 0, -strlen($suffix));
+                        }
+
+                        $this->request->setAction($actionName);
+                    } catch (ReflectionException $e) {
+                        $reflect = new ReflectionMethod($instance, '__call');
+                        $vars    = [$action, $vars];
+                        $this->request->setAction($action);
+                    }
+                } else {
+                    // 操作不存在
+                    throw new HttpException(404, 'method not exists:' . get_class($instance) . '->' . $action . '()');
+                }
+
+                $data = $this->app->invokeReflectMethod($instance, $reflect, $vars);
+
+                return $this->autoResponse($data);
+            });
+    }
+
+    protected function parseActions($actions)
+    {
+        return array_map(function ($item) {
+            return strtolower($item);
+        }, is_string($actions) ? explode(",", $actions) : $actions);
+    }
+
+    /**
+     * 使用反射机制注册控制器中间件
+     * @access public
+     * @param object $controller 控制器实例
+     * @return void
+     */
+    protected function registerControllerMiddleware($controller): void
+    {
+        $class = new ReflectionClass($controller);
+
+        if ($class->hasProperty('middleware')) {
+            $reflectionProperty = $class->getProperty('middleware');
+            $reflectionProperty->setAccessible(true);
+
+            $middlewares = $reflectionProperty->getValue($controller);
+            $action      = $this->request->action(true);
+
+            foreach ($middlewares as $key => $val) {
+                if (!is_int($key)) {
+                    $middleware = $key;
+                    $options    = $val;
+                } elseif (isset($val['middleware'])) {
+                    $middleware = $val['middleware'];
+                    $options    = $val['options'] ?? [];
+                } else {
+                    $middleware = $val;
+                    $options    = [];
+                }
+
+                if (isset($options['only']) && !in_array($action, $this->parseActions($options['only']))) {
+                    continue;
+                } elseif (isset($options['except']) && in_array($action, $this->parseActions($options['except']))) {
+                    continue;
+                }
+
+                if (is_string($middleware) && strpos($middleware, ':')) {
+                    $middleware = explode(':', $middleware);
+                    if (count($middleware) > 1) {
+                        $middleware = [$middleware[0], array_slice($middleware, 1)];
+                    }
+                }
+
+                $this->app->middleware->controller($middleware);
+            }
+        }
+    }
+
+    /**
+     * 实例化访问控制器
+     * @access public
+     * @param string $name 资源地址
+     * @return object
+     * @throws ClassNotFoundException
+     */
+    public function controller(string $name)
+    {
+        $suffix = $this->rule->config('controller_suffix') ? 'Controller' : '';
+
+        $controllerLayer = $this->rule->config('controller_layer') ?: 'controller';
+        $emptyController = $this->rule->config('empty_controller') ?: 'Error';
+
+        $class = $this->app->parseClass($controllerLayer, $name . $suffix);
+
+        if (class_exists($class)) {
+            return $this->app->make($class, [], true);
+        } elseif ($emptyController && class_exists($emptyClass = $this->app->parseClass($controllerLayer, $emptyController . $suffix))) {
+            return $this->app->make($emptyClass, [], true);
+        }
+
+        throw new ClassNotFoundException('class not exists:' . $class, $class);
+    }
+}
diff --git a/vendor/topthink/framework/src/think/route/dispatch/Url.php b/vendor/topthink/framework/src/think/route/dispatch/Url.php
new file mode 100755
index 0000000..147f5cb
--- /dev/null
+++ b/vendor/topthink/framework/src/think/route/dispatch/Url.php
@@ -0,0 +1,118 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\route\dispatch;
+
+use think\exception\HttpException;
+use think\helper\Str;
+use think\Request;
+use think\route\Rule;
+
+/**
+ * Url Dispatcher
+ */
+class Url extends Controller
+{
+
+    public function __construct(Request $request, Rule $rule, $dispatch)
+    {
+        $this->request = $request;
+        $this->rule    = $rule;
+        // 解析默认的URL规则
+        $dispatch = $this->parseUrl($dispatch);
+
+        parent::__construct($request, $rule, $dispatch, $this->param);
+    }
+
+    /**
+     * 解析URL地址
+     * @access protected
+     * @param  string $url URL
+     * @return array
+     */
+    protected function parseUrl(string $url): array
+    {
+        $depr = $this->rule->config('pathinfo_depr');
+        $bind = $this->rule->getRouter()->getDomainBind();
+
+        if ($bind && preg_match('/^[a-z]/is', $bind)) {
+            $bind = str_replace('/', $depr, $bind);
+            // 如果有域名绑定
+            $url = $bind . ('.' != substr($bind, -1) ? $depr : '') . ltrim($url, $depr);
+        }
+
+        $path = $this->rule->parseUrlPath($url);
+        if (empty($path)) {
+            return [null, null];
+        }
+
+        // 解析控制器
+        $controller = !empty($path) ? array_shift($path) : null;
+
+        if ($controller && !preg_match('/^[A-Za-z0-9][\w|\.]*$/', $controller)) {
+            throw new HttpException(404, 'controller not exists:' . $controller);
+        }
+
+        // 解析操作
+        $action = !empty($path) ? array_shift($path) : null;
+        $var    = [];
+
+        // 解析额外参数
+        if ($path) {
+            preg_replace_callback('/(\w+)\|([^\|]+)/', function ($match) use (&$var) {
+                $var[$match[1]] = strip_tags($match[2]);
+            }, implode('|', $path));
+        }
+
+        $panDomain = $this->request->panDomain();
+        if ($panDomain && $key = array_search('*', $var)) {
+            // 泛域名赋值
+            $var[$key] = $panDomain;
+        }
+
+        // 设置当前请求的参数
+        $this->param = $var;
+
+        // 封装路由
+        $route = [$controller, $action];
+
+        if ($this->hasDefinedRoute($route)) {
+            throw new HttpException(404, 'invalid request:' . str_replace('|', $depr, $url));
+        }
+
+        return $route;
+    }
+
+    /**
+     * 检查URL是否已经定义过路由
+     * @access protected
+     * @param  array $route 路由信息
+     * @return bool
+     */
+    protected function hasDefinedRoute(array $route): bool
+    {
+        [$controller, $action] = $route;
+
+        // 检查地址是否被定义过路由
+        $name = strtolower(Str::studly($controller) . '/' . $action);
+
+        $host   = $this->request->host(true);
+        $method = $this->request->method();
+
+        if ($this->rule->getRouter()->getName($name, $host, $method)) {
+            return true;
+        }
+
+        return false;
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/service/ModelService.php b/vendor/topthink/framework/src/think/service/ModelService.php
new file mode 100755
index 0000000..b517c4e
--- /dev/null
+++ b/vendor/topthink/framework/src/think/service/ModelService.php
@@ -0,0 +1,53 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\service;
+
+use think\Model;
+use think\Service;
+
+/**
+ * 模型服务类
+ */
+class ModelService extends Service
+{
+    public function boot()
+    {
+        Model::setDb($this->app->db);
+        Model::setEvent($this->app->event);
+        Model::setInvoker([$this->app, 'invoke']);
+        Model::maker(function (Model $model) {
+            $config = $this->app->config;
+
+            $isAutoWriteTimestamp = $model->getAutoWriteTimestamp();
+
+            if (is_null($isAutoWriteTimestamp)) {
+                // 自动写入时间戳
+                $model->isAutoWriteTimestamp($config->get('database.auto_timestamp', 'timestamp'));
+            }
+
+            $dateFormat = $model->getDateFormat();
+
+            if (is_null($dateFormat)) {
+                // 设置时间戳格式
+                $model->setDateFormat($config->get('database.datetime_format', 'Y-m-d H:i:s'));
+            }
+
+            $timeField = $config->get('database.datetime_field');
+            if (!empty($timeField)) {
+                [$createTime, $updateTime] = explode(',', $timeField);
+                $model->setTimeField($createTime, $updateTime);
+            }
+
+        });
+    }
+}
diff --git a/vendor/topthink/framework/src/think/service/PaginatorService.php b/vendor/topthink/framework/src/think/service/PaginatorService.php
new file mode 100755
index 0000000..a01977d
--- /dev/null
+++ b/vendor/topthink/framework/src/think/service/PaginatorService.php
@@ -0,0 +1,52 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\service;
+
+use think\Paginator;
+use think\paginator\driver\Bootstrap;
+use think\Service;
+
+/**
+ * 分页服务类
+ */
+class PaginatorService extends Service
+{
+    public function register()
+    {
+        if (!$this->app->bound(Paginator::class)) {
+            $this->app->bind(Paginator::class, Bootstrap::class);
+        }
+    }
+
+    public function boot()
+    {
+        Paginator::maker(function (...$args) {
+            return $this->app->make(Paginator::class, $args, true);
+        });
+
+        Paginator::currentPathResolver(function () {
+            return $this->app->request->baseUrl();
+        });
+
+        Paginator::currentPageResolver(function ($varPage = 'page') {
+
+            $page = $this->app->request->param($varPage);
+
+            if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int) $page >= 1) {
+                return (int) $page;
+            }
+
+            return 1;
+        });
+    }
+}
diff --git a/vendor/topthink/framework/src/think/service/ValidateService.php b/vendor/topthink/framework/src/think/service/ValidateService.php
new file mode 100755
index 0000000..94d7638
--- /dev/null
+++ b/vendor/topthink/framework/src/think/service/ValidateService.php
@@ -0,0 +1,31 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\service;
+
+use think\Service;
+use think\Validate;
+
+/**
+ * 验证服务类
+ */
+class ValidateService extends Service
+{
+    public function boot()
+    {
+        Validate::maker(function (Validate $validate) {
+            $validate->setLang($this->app->lang);
+            $validate->setDb($this->app->db);
+            $validate->setRequest($this->app->request);
+        });
+    }
+}
diff --git a/vendor/topthink/framework/src/think/session/Store.php b/vendor/topthink/framework/src/think/session/Store.php
new file mode 100755
index 0000000..49e1ba9
--- /dev/null
+++ b/vendor/topthink/framework/src/think/session/Store.php
@@ -0,0 +1,340 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\session;
+
+use think\contract\SessionHandlerInterface;
+use think\helper\Arr;
+
+class Store
+{
+
+    /**
+     * Session数据
+     * @var array
+     */
+    protected $data = [];
+
+    /**
+     * 是否初始化
+     * @var bool
+     */
+    protected $init = null;
+
+    /**
+     * 记录Session name
+     * @var string
+     */
+    protected $name = 'PHPSESSID';
+
+    /**
+     * 记录Session Id
+     * @var string
+     */
+    protected $id;
+
+    /**
+     * @var SessionHandlerInterface
+     */
+    protected $handler;
+
+    /** @var array */
+    protected $serialize = [];
+
+    public function __construct($name, SessionHandlerInterface $handler, array $serialize = null)
+    {
+        $this->name    = $name;
+        $this->handler = $handler;
+
+        if (!empty($serialize)) {
+            $this->serialize = $serialize;
+        }
+
+        $this->setId();
+    }
+
+    /**
+     * 设置数据
+     * @access public
+     * @param array $data
+     * @return void
+     */
+    public function setData(array $data): void
+    {
+        $this->data = $data;
+    }
+
+    /**
+     * session初始化
+     * @access public
+     * @return void
+     */
+    public function init(): void
+    {
+        // 读取缓存数据
+        $data = $this->handler->read($this->getId());
+
+        if (!empty($data)) {
+            $this->data = array_merge($this->data, $this->unserialize($data));
+        }
+
+        $this->init = true;
+    }
+
+    /**
+     * 设置SessionName
+     * @access public
+     * @param string $name session_name
+     * @return void
+     */
+    public function setName(string $name): void
+    {
+        $this->name = $name;
+    }
+
+    /**
+     * 获取sessionName
+     * @access public
+     * @return string
+     */
+    public function getName(): string
+    {
+        return $this->name;
+    }
+
+    /**
+     * session_id设置
+     * @access public
+     * @param string $id session_id
+     * @return void
+     */
+    public function setId($id = null): void
+    {
+        $this->id = is_string($id) && strlen($id) === 32 && ctype_alnum($id) ? $id : md5(microtime(true) . session_create_id());
+    }
+
+    /**
+     * 获取session_id
+     * @access public
+     * @return string
+     */
+    public function getId(): string
+    {
+        return $this->id;
+    }
+
+    /**
+     * 获取所有数据
+     * @return array
+     */
+    public function all(): array
+    {
+        return $this->data;
+    }
+
+    /**
+     * session设置
+     * @access public
+     * @param string $name  session名称
+     * @param mixed  $value session值
+     * @return void
+     */
+    public function set(string $name, $value): void
+    {
+        Arr::set($this->data, $name, $value);
+    }
+
+    /**
+     * session获取
+     * @access public
+     * @param string $name    session名称
+     * @param mixed  $default 默认值
+     * @return mixed
+     */
+    public function get(string $name, $default = null)
+    {
+        return Arr::get($this->data, $name, $default);
+    }
+
+    /**
+     * session获取并删除
+     * @access public
+     * @param string $name session名称
+     * @return mixed
+     */
+    public function pull(string $name)
+    {
+        return Arr::pull($this->data, $name);
+    }
+
+    /**
+     * 添加数据到一个session数组
+     * @access public
+     * @param string $key
+     * @param mixed  $value
+     * @return void
+     */
+    public function push(string $key, $value): void
+    {
+        $array = $this->get($key, []);
+
+        $array[] = $value;
+
+        $this->set($key, $array);
+    }
+
+    /**
+     * 判断session数据
+     * @access public
+     * @param string $name session名称
+     * @return bool
+     */
+    public function has(string $name): bool
+    {
+        return Arr::has($this->data, $name);
+    }
+
+    /**
+     * 删除session数据
+     * @access public
+     * @param string $name session名称
+     * @return void
+     */
+    public function delete(string $name): void
+    {
+        Arr::forget($this->data, $name);
+    }
+
+    /**
+     * 清空session数据
+     * @access public
+     * @return void
+     */
+    public function clear(): void
+    {
+        $this->data = [];
+    }
+
+    /**
+     * 销毁session
+     */
+    public function destroy(): void
+    {
+        $this->clear();
+
+        $this->regenerate(true);
+    }
+
+    /**
+     * 重新生成session id
+     * @param bool $destroy
+     */
+    public function regenerate(bool $destroy = false): void
+    {
+        if ($destroy) {
+            $this->handler->delete($this->getId());
+        }
+
+        $this->setId();
+    }
+
+    /**
+     * 保存session数据
+     * @access public
+     * @return void
+     */
+    public function save(): void
+    {
+        $this->clearFlashData();
+
+        $sessionId = $this->getId();
+
+        if (!empty($this->data)) {
+            $data = $this->serialize($this->data);
+
+            $this->handler->write($sessionId, $data);
+        } else {
+            $this->handler->delete($sessionId);
+        }
+
+        $this->init = false;
+    }
+
+    /**
+     * session设置 下一次请求有效
+     * @access public
+     * @param string $name  session名称
+     * @param mixed  $value session值
+     * @return void
+     */
+    public function flash(string $name, $value): void
+    {
+        $this->set($name, $value);
+        $this->push('__flash__.__next__', $name);
+        $this->set('__flash__.__current__', Arr::except($this->get('__flash__.__current__', []), $name));
+    }
+
+    /**
+     * 将本次闪存数据推迟到下次请求
+     *
+     * @return void
+     */
+    public function reflash(): void
+    {
+        $keys   = $this->get('__flash__.__current__', []);
+        $values = array_unique(array_merge($this->get('__flash__.__next__', []), $keys));
+        $this->set('__flash__.__next__', $values);
+        $this->set('__flash__.__current__', []);
+    }
+
+    /**
+     * 清空当前请求的session数据
+     * @access public
+     * @return void
+     */
+    public function clearFlashData(): void
+    {
+        Arr::forget($this->data, $this->get('__flash__.__current__', []));
+        if (!empty($next = $this->get('__flash__.__next__', []))) {
+            $this->set('__flash__.__current__', $next);
+        } else {
+            $this->delete('__flash__.__current__');
+        }
+        $this->delete('__flash__.__next__');
+    }
+
+    /**
+     * 序列化数据
+     * @access protected
+     * @param mixed $data
+     * @return string
+     */
+    protected function serialize($data): string
+    {
+        $serialize = $this->serialize[0] ?? 'serialize';
+
+        return $serialize($data);
+    }
+
+    /**
+     * 反序列化数据
+     * @access protected
+     * @param string $data
+     * @return array
+     */
+    protected function unserialize(string $data): array
+    {
+        $unserialize = $this->serialize[1] ?? 'unserialize';
+
+        return (array) $unserialize($data);
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/session/driver/Cache.php b/vendor/topthink/framework/src/think/session/driver/Cache.php
new file mode 100755
index 0000000..4fabc79
--- /dev/null
+++ b/vendor/topthink/framework/src/think/session/driver/Cache.php
@@ -0,0 +1,50 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+namespace think\session\driver;
+
+use Psr\SimpleCache\CacheInterface;
+use think\contract\SessionHandlerInterface;
+use think\helper\Arr;
+
+class Cache implements SessionHandlerInterface
+{
+
+    /** @var CacheInterface */
+    protected $handler;
+
+    /** @var integer */
+    protected $expire;
+
+    /** @var string */
+    protected $prefix;
+
+    public function __construct(\think\Cache $cache, array $config = [])
+    {
+        $this->handler = $cache->store(Arr::get($config, 'store'));
+        $this->expire  = Arr::get($config, 'expire', 1440);
+        $this->prefix  = Arr::get($config, 'prefix', '');
+    }
+
+    public function read(string $sessionId): string
+    {
+        return (string) $this->handler->get($this->prefix . $sessionId);
+    }
+
+    public function delete(string $sessionId): bool
+    {
+        return $this->handler->delete($this->prefix . $sessionId);
+    }
+
+    public function write(string $sessionId, string $data): bool
+    {
+        return $this->handler->set($this->prefix . $sessionId, $data, $this->expire);
+    }
+}
diff --git a/vendor/topthink/framework/src/think/session/driver/File.php b/vendor/topthink/framework/src/think/session/driver/File.php
new file mode 100755
index 0000000..788f323
--- /dev/null
+++ b/vendor/topthink/framework/src/think/session/driver/File.php
@@ -0,0 +1,249 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\session\driver;
+
+use Closure;
+use Exception;
+use FilesystemIterator;
+use Generator;
+use SplFileInfo;
+use think\App;
+use think\contract\SessionHandlerInterface;
+
+/**
+ * Session 文件驱动
+ */
+class File implements SessionHandlerInterface
+{
+    protected $config = [
+        'path'           => '',
+        'expire'         => 1440,
+        'prefix'         => '',
+        'data_compress'  => false,
+        'gc_probability' => 1,
+        'gc_divisor'     => 100,
+    ];
+
+    public function __construct(App $app, array $config = [])
+    {
+        $this->config = array_merge($this->config, $config);
+
+        if (empty($this->config['path'])) {
+            $this->config['path'] = $app->getRuntimePath() . 'session' . DIRECTORY_SEPARATOR;
+        } elseif (substr($this->config['path'], -1) != DIRECTORY_SEPARATOR) {
+            $this->config['path'] .= DIRECTORY_SEPARATOR;
+        }
+
+        $this->init();
+    }
+
+    /**
+     * 打开Session
+     * @access protected
+     * @throws Exception
+     */
+    protected function init(): void
+    {
+        try {
+            !is_dir($this->config['path']) && mkdir($this->config['path'], 0755, true);
+        } catch (\Exception $e) {
+            // 写入失败
+        }
+
+        // 垃圾回收
+        if (random_int(1, $this->config['gc_divisor']) <= $this->config['gc_probability']) {
+            $this->gc();
+        }
+    }
+
+    /**
+     * Session 垃圾回收
+     * @access public
+     * @return void
+     */
+    public function gc(): void
+    {
+        $lifetime = $this->config['expire'];
+        $now      = time();
+
+        $files = $this->findFiles($this->config['path'], function (SplFileInfo $item) use ($lifetime, $now) {
+            return $now - $lifetime > $item->getMTime();
+        });
+
+        foreach ($files as $file) {
+            $this->unlink($file->getPathname());
+        }
+    }
+
+    /**
+     * 查找文件
+     * @param string  $root
+     * @param Closure $filter
+     * @return Generator
+     */
+    protected function findFiles(string $root, Closure $filter)
+    {
+        $items = new FilesystemIterator($root);
+
+        /** @var SplFileInfo $item */
+        foreach ($items as $item) {
+            if ($item->isDir() && !$item->isLink()) {
+                yield from $this->findFiles($item->getPathname(), $filter);
+            } else {
+                if ($filter($item)) {
+                    yield $item;
+                }
+            }
+        }
+    }
+
+    /**
+     * 取得变量的存储文件名
+     * @access protected
+     * @param string $name 缓存变量名
+     * @param bool   $auto 是否自动创建目录
+     * @return string
+     */
+    protected function getFileName(string $name, bool $auto = false): string
+    {
+        if ($this->config['prefix']) {
+            // 使用子目录
+            $name = $this->config['prefix'] . DIRECTORY_SEPARATOR . 'sess_' . $name;
+        } else {
+            $name = 'sess_' . $name;
+        }
+
+        $filename = $this->config['path'] . $name;
+        $dir      = dirname($filename);
+
+        if ($auto && !is_dir($dir)) {
+            try {
+                mkdir($dir, 0755, true);
+            } catch (\Exception $e) {
+                // 创建失败
+            }
+        }
+
+        return $filename;
+    }
+
+    /**
+     * 读取Session
+     * @access public
+     * @param string $sessID
+     * @return string
+     */
+    public function read(string $sessID): string
+    {
+        $filename = $this->getFileName($sessID);
+
+        if (is_file($filename) && filemtime($filename) >= time() - $this->config['expire']) {
+            $content = $this->readFile($filename);
+
+            if ($this->config['data_compress'] && function_exists('gzcompress')) {
+                //启用数据压缩
+                $content = (string) gzuncompress($content);
+            }
+
+            return $content;
+        }
+
+        return '';
+    }
+
+    /**
+     * 写文件(加锁)
+     * @param $path
+     * @param $content
+     * @return bool
+     */
+    protected function writeFile($path, $content): bool
+    {
+        return (bool) file_put_contents($path, $content, LOCK_EX);
+    }
+
+    /**
+     * 读取文件内容(加锁)
+     * @param $path
+     * @return string
+     */
+    protected function readFile($path): string
+    {
+        $contents = '';
+
+        $handle = fopen($path, 'rb');
+
+        if ($handle) {
+            try {
+                if (flock($handle, LOCK_SH)) {
+                    clearstatcache(true, $path);
+
+                    $contents = fread($handle, filesize($path) ?: 1);
+
+                    flock($handle, LOCK_UN);
+                }
+            } finally {
+                fclose($handle);
+            }
+        }
+
+        return $contents;
+    }
+
+    /**
+     * 写入Session
+     * @access public
+     * @param string $sessID
+     * @param string $sessData
+     * @return bool
+     */
+    public function write(string $sessID, string $sessData): bool
+    {
+        $filename = $this->getFileName($sessID, true);
+        $data     = $sessData;
+
+        if ($this->config['data_compress'] && function_exists('gzcompress')) {
+            //数据压缩
+            $data = gzcompress($data, 3);
+        }
+
+        return $this->writeFile($filename, $data);
+    }
+
+    /**
+     * 删除Session
+     * @access public
+     * @param string $sessID
+     * @return bool
+     */
+    public function delete(string $sessID): bool
+    {
+        try {
+            return $this->unlink($this->getFileName($sessID));
+        } catch (\Exception $e) {
+            return false;
+        }
+    }
+
+    /**
+     * 判断文件是否存在后,删除
+     * @access private
+     * @param string $file
+     * @return bool
+     */
+    private function unlink(string $file): bool
+    {
+        return is_file($file) && unlink($file);
+    }
+
+}
diff --git a/vendor/topthink/framework/src/think/validate/ValidateRule.php b/vendor/topthink/framework/src/think/validate/ValidateRule.php
new file mode 100755
index 0000000..18e5ea3
--- /dev/null
+++ b/vendor/topthink/framework/src/think/validate/ValidateRule.php
@@ -0,0 +1,172 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\validate;
+
+/**
+ * Class ValidateRule
+ * @package think\validate
+ * @method static ValidateRule confirm(mixed $rule, string $msg = '') 验证是否和某个字段的值一致
+ * @method static ValidateRule different(mixed $rule, string $msg = '') 验证是否和某个字段的值是否不同
+ * @method static ValidateRule egt(mixed $rule, string $msg = '') 验证是否大于等于某个值
+ * @method static ValidateRule gt(mixed $rule, string $msg = '') 验证是否大于某个值
+ * @method static ValidateRule elt(mixed $rule, string $msg = '') 验证是否小于等于某个值
+ * @method static ValidateRule lt(mixed $rule, string $msg = '') 验证是否小于某个值
+ * @method static ValidateRule eg(mixed $rule, string $msg = '') 验证是否等于某个值
+ * @method static ValidateRule in(mixed $rule, string $msg = '') 验证是否在范围内
+ * @method static ValidateRule notIn(mixed $rule, string $msg = '')  验证是否不在某个范围
+ * @method static ValidateRule between(mixed $rule, string $msg = '')  验证是否在某个区间
+ * @method static ValidateRule notBetween(mixed $rule, string $msg = '')  验证是否不在某个区间
+ * @method static ValidateRule length(mixed $rule, string $msg = '')  验证数据长度
+ * @method static ValidateRule max(mixed $rule, string $msg = '')  验证数据最大长度
+ * @method static ValidateRule min(mixed $rule, string $msg = '')  验证数据最小长度
+ * @method static ValidateRule after(mixed $rule, string $msg = '')  验证日期
+ * @method static ValidateRule before(mixed $rule, string $msg = '')  验证日期
+ * @method static ValidateRule expire(mixed $rule, string $msg = '')  验证有效期
+ * @method static ValidateRule allowIp(mixed $rule, string $msg = '')  验证IP许可
+ * @method static ValidateRule denyIp(mixed $rule, string $msg = '')  验证IP禁用
+ * @method static ValidateRule regex(mixed $rule, string $msg = '')  使用正则验证数据
+ * @method static ValidateRule token(mixed $rule='__token__', string $msg = '')  验证表单令牌
+ * @method static ValidateRule is(mixed $rule, string $msg = '')  验证字段值是否为有效格式
+ * @method static ValidateRule isRequire(mixed $rule = null, string $msg = '')  验证字段必须
+ * @method static ValidateRule isNumber(mixed $rule = null, string $msg = '')  验证字段值是否为数字
+ * @method static ValidateRule isArray(mixed $rule = null, string $msg = '')  验证字段值是否为数组
+ * @method static ValidateRule isInteger(mixed $rule = null, string $msg = '')  验证字段值是否为整形
+ * @method static ValidateRule isFloat(mixed $rule = null, string $msg = '')  验证字段值是否为浮点数
+ * @method static ValidateRule isMobile(mixed $rule = null, string $msg = '')  验证字段值是否为手机
+ * @method static ValidateRule isIdCard(mixed $rule = null, string $msg = '')  验证字段值是否为身份证号码
+ * @method static ValidateRule isChs(mixed $rule = null, string $msg = '')  验证字段值是否为中文
+ * @method static ValidateRule isChsDash(mixed $rule = null, string $msg = '')  验证字段值是否为中文字母及下划线
+ * @method static ValidateRule isChsAlpha(mixed $rule = null, string $msg = '')  验证字段值是否为中文和字母
+ * @method static ValidateRule isChsAlphaNum(mixed $rule = null, string $msg = '')  验证字段值是否为中文字母和数字
+ * @method static ValidateRule isDate(mixed $rule = null, string $msg = '')  验证字段值是否为有效格式
+ * @method static ValidateRule isBool(mixed $rule = null, string $msg = '')  验证字段值是否为布尔值
+ * @method static ValidateRule isAlpha(mixed $rule = null, string $msg = '')  验证字段值是否为字母
+ * @method static ValidateRule isAlphaDash(mixed $rule = null, string $msg = '')  验证字段值是否为字母和下划线
+ * @method static ValidateRule isAlphaNum(mixed $rule = null, string $msg = '')  验证字段值是否为字母和数字
+ * @method static ValidateRule isAccepted(mixed $rule = null, string $msg = '')  验证字段值是否为yes, on, 或是 1
+ * @method static ValidateRule isEmail(mixed $rule = null, string $msg = '')  验证字段值是否为有效邮箱格式
+ * @method static ValidateRule isUrl(mixed $rule = null, string $msg = '')  验证字段值是否为有效URL地址
+ * @method static ValidateRule activeUrl(mixed $rule, string $msg = '')  验证是否为合格的域名或者IP
+ * @method static ValidateRule ip(mixed $rule, string $msg = '')  验证是否有效IP
+ * @method static ValidateRule fileExt(mixed $rule, string $msg = '')  验证文件后缀
+ * @method static ValidateRule fileMime(mixed $rule, string $msg = '')  验证文件类型
+ * @method static ValidateRule fileSize(mixed $rule, string $msg = '')  验证文件大小
+ * @method static ValidateRule image(mixed $rule, string $msg = '')  验证图像文件
+ * @method static ValidateRule method(mixed $rule, string $msg = '')  验证请求类型
+ * @method static ValidateRule dateFormat(mixed $rule, string $msg = '')  验证时间和日期是否符合指定格式
+ * @method static ValidateRule unique(mixed $rule, string $msg = '')  验证是否唯一
+ * @method static ValidateRule behavior(mixed $rule, string $msg = '')  使用行为类验证
+ * @method static ValidateRule filter(mixed $rule, string $msg = '')  使用filter_var方式验证
+ * @method static ValidateRule requireIf(mixed $rule, string $msg = '')  验证某个字段等于某个值的时候必须
+ * @method static ValidateRule requireCallback(mixed $rule, string $msg = '')  通过回调方法验证某个字段是否必须
+ * @method static ValidateRule requireWith(mixed $rule, string $msg = '')  验证某个字段有值的情况下必须
+ * @method static ValidateRule must(mixed $rule = null, string $msg = '')  必须验证
+ */
+class ValidateRule
+{
+    // 验证字段的名称
+    protected $title;
+
+    // 当前验证规则
+    protected $rule = [];
+
+    // 验证提示信息
+    protected $message = [];
+
+    /**
+     * 添加验证因子
+     * @access protected
+     * @param  string    $name  验证名称
+     * @param  mixed     $rule  验证规则
+     * @param  string    $msg   提示信息
+     * @return $this
+     */
+    protected function addItem(string $name, $rule = null, string $msg = '')
+    {
+        if ($rule || 0 === $rule) {
+            $this->rule[$name] = $rule;
+        } else {
+            $this->rule[] = $name;
+        }
+
+        $this->message[] = $msg;
+
+        return $this;
+    }
+
+    /**
+     * 获取验证规则
+     * @access public
+     * @return array
+     */
+    public function getRule(): array
+    {
+        return $this->rule;
+    }
+
+    /**
+     * 获取验证字段名称
+     * @access public
+     * @return string
+     */
+    public function getTitle(): string
+    {
+        return $this->title ?: '';
+    }
+
+    /**
+     * 获取验证提示
+     * @access public
+     * @return array
+     */
+    public function getMsg(): array
+    {
+        return $this->message;
+    }
+
+    /**
+     * 设置验证字段名称
+     * @access public
+     * @return $this
+     */
+    public function title(string $title)
+    {
+        $this->title = $title;
+
+        return $this;
+    }
+
+    public function __call($method, $args)
+    {
+        if ('is' == strtolower(substr($method, 0, 2))) {
+            $method = substr($method, 2);
+        }
+
+        array_unshift($args, lcfirst($method));
+
+        return call_user_func_array([$this, 'addItem'], $args);
+    }
+
+    public static function __callStatic($method, $args)
+    {
+        $rule = new static();
+
+        if ('is' == strtolower(substr($method, 0, 2))) {
+            $method = substr($method, 2);
+        }
+
+        array_unshift($args, lcfirst($method));
+
+        return call_user_func_array([$rule, 'addItem'], $args);
+    }
+}
diff --git a/vendor/topthink/framework/src/think/view/driver/Php.php b/vendor/topthink/framework/src/think/view/driver/Php.php
new file mode 100755
index 0000000..9e6e54a
--- /dev/null
+++ b/vendor/topthink/framework/src/think/view/driver/Php.php
@@ -0,0 +1,191 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\view\driver;
+
+use RuntimeException;
+use think\App;
+use think\contract\TemplateHandlerInterface;
+use think\helper\Str;
+
+/**
+ * PHP原生模板驱动
+ */
+class Php implements TemplateHandlerInterface
+{
+    protected $template;
+    protected $content;
+    protected $app;
+
+    // 模板引擎参数
+    protected $config = [
+        // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 3 保持操作方法
+        'auto_rule'     => 1,
+        // 视图目录名
+        'view_dir_name' => 'view',
+        // 应用模板路径
+        'view_path'     => '',
+        // 模板文件后缀
+        'view_suffix'   => 'php',
+        // 模板文件名分隔符
+        'view_depr'     => DIRECTORY_SEPARATOR,
+    ];
+
+    public function __construct(App $app, array $config = [])
+    {
+        $this->app    = $app;
+        $this->config = array_merge($this->config, (array) $config);
+    }
+
+    /**
+     * 检测是否存在模板文件
+     * @access public
+     * @param string $template 模板文件或者模板规则
+     * @return bool
+     */
+    public function exists(string $template): bool
+    {
+        if ('' == pathinfo($template, PATHINFO_EXTENSION)) {
+            // 获取模板文件名
+            $template = $this->parseTemplate($template);
+        }
+
+        return is_file($template);
+    }
+
+    /**
+     * 渲染模板文件
+     * @access public
+     * @param string $template 模板文件
+     * @param array  $data     模板变量
+     * @return void
+     */
+    public function fetch(string $template, array $data = []): void
+    {
+        if ('' == pathinfo($template, PATHINFO_EXTENSION)) {
+            // 获取模板文件名
+            $template = $this->parseTemplate($template);
+        }
+
+        // 模板不存在 抛出异常
+        if (!is_file($template)) {
+            throw new RuntimeException('template not exists:' . $template);
+        }
+
+        $this->template = $template;
+
+        extract($data, EXTR_OVERWRITE);
+
+        include $this->template;
+    }
+
+    /**
+     * 渲染模板内容
+     * @access public
+     * @param string $content 模板内容
+     * @param array  $data    模板变量
+     * @return void
+     */
+    public function display(string $content, array $data = []): void
+    {
+        $this->content = $content;
+
+        extract($data, EXTR_OVERWRITE);
+        eval('?>' . $this->content);
+    }
+
+    /**
+     * 自动定位模板文件
+     * @access private
+     * @param string $template 模板文件规则
+     * @return string
+     */
+    private function parseTemplate(string $template): string
+    {
+        $request = $this->app->request;
+
+        // 获取视图根目录
+        if (strpos($template, '@')) {
+            // 跨应用调用
+            [$app, $template] = explode('@', $template);
+        }
+
+        if ($this->config['view_path'] && !isset($app)) {
+            $path = $this->config['view_path'];
+        } else {
+            $appName = isset($app) ? $app : $this->app->http->getName();
+            $view    = $this->config['view_dir_name'];
+
+            if (is_dir($this->app->getAppPath() . $view)) {
+                $path = isset($app) ? $this->app->getBasePath() . ($appName ? $appName . DIRECTORY_SEPARATOR : '') . $view . DIRECTORY_SEPARATOR : $this->app->getAppPath() . $view . DIRECTORY_SEPARATOR;
+            } else {
+                $path = $this->app->getRootPath() . $view . DIRECTORY_SEPARATOR . ($appName ? $appName . DIRECTORY_SEPARATOR : '');
+            }
+        }
+
+        $depr = $this->config['view_depr'];
+
+        if (0 !== strpos($template, '/')) {
+            $template   = str_replace(['/', ':'], $depr, $template);
+            $controller = $request->controller();
+            if (strpos($controller, '.')) {
+                $pos        = strrpos($controller, '.');
+                $controller = substr($controller, 0, $pos) . '.' . Str::snake(substr($controller, $pos + 1));
+            } else {
+                $controller = Str::snake($controller);
+            }
+
+            if ($controller) {
+                if ('' == $template) {
+                    // 如果模板文件名为空 按照默认规则定位
+                    if (2 == $this->config['auto_rule']) {
+                        $template = $request->action(true);
+                    } elseif (3 == $this->config['auto_rule']) {
+                        $template = $request->action();
+                    } else {
+                        $template = Str::snake($request->action());
+                    }
+
+                    $template = str_replace('.', DIRECTORY_SEPARATOR, $controller) . $depr . $template;
+                } elseif (false === strpos($template, $depr)) {
+                    $template = str_replace('.', DIRECTORY_SEPARATOR, $controller) . $depr . $template;
+                }
+            }
+        } else {
+            $template = str_replace(['/', ':'], $depr, substr($template, 1));
+        }
+
+        return $path . ltrim($template, '/') . '.' . ltrim($this->config['view_suffix'], '.');
+    }
+
+    /**
+     * 配置模板引擎
+     * @access private
+     * @param array $config 参数
+     * @return void
+     */
+    public function config(array $config): void
+    {
+        $this->config = array_merge($this->config, $config);
+    }
+
+    /**
+     * 获取模板引擎配置
+     * @access public
+     * @param string $name 参数名
+     * @return mixed
+     */
+    public function getConfig(string $name)
+    {
+        return $this->config[$name] ?? null;
+    }
+}
diff --git a/vendor/topthink/framework/src/tpl/think_exception.tpl b/vendor/topthink/framework/src/tpl/think_exception.tpl
new file mode 100755
index 0000000..6ea9677
--- /dev/null
+++ b/vendor/topthink/framework/src/tpl/think_exception.tpl
@@ -0,0 +1,502 @@
+<?php
+/** @var array $traces */
+if (!function_exists('parse_padding')) {
+    function parse_padding($source)
+    {
+        $length  = strlen(strval(count($source['source']) + $source['first']));
+        return 40 + ($length - 1) * 8;
+    }
+}
+
+if (!function_exists('parse_class')) {
+    function parse_class($name)
+    {
+        $names = explode('\\', $name);
+        return '<abbr title="'.$name.'">'.end($names).'</abbr>';
+    }
+}
+
+if (!function_exists('parse_file')) {
+    function parse_file($file, $line)
+    {
+        return '<a class="toggle" title="'."{$file} line {$line}".'">'.basename($file)." line {$line}".'</a>';
+    }
+}
+
+if (!function_exists('parse_args')) {
+    function parse_args($args)
+    {
+        $result = [];
+        foreach ($args as $key => $item) {
+            switch (true) {
+                case is_object($item):
+                    $value = sprintf('<em>object</em>(%s)', parse_class(get_class($item)));
+                    break;
+                case is_array($item):
+                    if (count($item) > 3) {
+                        $value = sprintf('[%s, ...]', parse_args(array_slice($item, 0, 3)));
+                    } else {
+                        $value = sprintf('[%s]', parse_args($item));
+                    }
+                    break;
+                case is_string($item):
+                    if (strlen($item) > 20) {
+                        $value = sprintf(
+                            '\'<a class="toggle" title="%s">%s...</a>\'',
+                            htmlentities($item),
+                            htmlentities(substr($item, 0, 20))
+                        );
+                    } else {
+                        $value = sprintf("'%s'", htmlentities($item));
+                    }
+                    break;
+                case is_int($item):
+                case is_float($item):
+                    $value = $item;
+                    break;
+                case is_null($item):
+                    $value = '<em>null</em>';
+                    break;
+                case is_bool($item):
+                    $value = '<em>' . ($item ? 'true' : 'false') . '</em>';
+                    break;
+                case is_resource($item):
+                    $value = '<em>resource</em>';
+                    break;
+                default:
+                    $value = htmlentities(str_replace("\n", '', var_export(strval($item), true)));
+                    break;
+            }
+
+            $result[] = is_int($key) ? $value : "'{$key}' => {$value}";
+        }
+
+        return implode(', ', $result);
+    }
+}
+if (!function_exists('echo_value')) {
+    function echo_value($val)
+    {
+        if (is_array($val) || is_object($val)) {
+            echo htmlentities(json_encode($val, JSON_PRETTY_PRINT));
+        } elseif (is_bool($val)) {
+            echo $val ? 'true' : 'false';
+        } elseif (is_scalar($val)) {
+            echo htmlentities($val);
+        } else {
+            echo 'Resource';
+        }
+    }
+}
+?>
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="UTF-8">
+    <title>系统发生错误</title>
+    <meta name="robots" content="noindex,nofollow" />
+    <style>
+        /* Base */
+        body {
+            color: #333;
+            font: 16px Verdana, "Helvetica Neue", helvetica, Arial, 'Microsoft YaHei', sans-serif;
+            margin: 0;
+            padding: 0 20px 20px;
+        }
+        h1{
+            margin: 10px 0 0;
+            font-size: 28px;
+            font-weight: 500;
+            line-height: 32px;
+        }
+        h2{
+            color: #4288ce;
+            font-weight: 400;
+            padding: 6px 0;
+            margin: 6px 0 0;
+            font-size: 18px;
+            border-bottom: 1px solid #eee;
+        }
+        h3{
+            margin: 12px;
+            font-size: 16px;
+            font-weight: bold;
+        }
+        abbr{
+            cursor: help;
+            text-decoration: underline;
+            text-decoration-style: dotted;
+        }
+        a{
+            color: #868686;
+            cursor: pointer;
+        }
+        a:hover{
+            text-decoration: underline;
+        }
+        .line-error{
+            background: #f8cbcb;
+        }
+        .echo table {
+            width: 100%;
+        }
+        .echo pre {
+            padding: 16px;
+            overflow: auto;
+            font-size: 85%;
+            line-height: 1.45;
+            background-color: #f7f7f7;
+            border: 0;
+            border-radius: 3px;
+            font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
+        }
+        .echo pre > pre {
+            padding: 0;
+            margin: 0;
+        }
+        /* Exception Info */
+        .exception {
+            margin-top: 20px;
+        }
+        .exception .message{
+            padding: 12px;
+            border: 1px solid #ddd;
+            border-bottom: 0 none;
+            line-height: 18px;
+            font-size:16px;
+            border-top-left-radius: 4px;
+            border-top-right-radius: 4px;
+            font-family: Consolas,"Liberation Mono",Courier,Verdana,"微软雅黑",serif;
+        }
+        .exception .code{
+            float: left;
+            text-align: center;
+            color: #fff;
+            margin-right: 12px;
+            padding: 16px;
+            border-radius: 4px;
+            background: #999;
+        }
+        .exception .source-code{
+            padding: 6px;
+            border: 1px solid #ddd;
+
+            background: #f9f9f9;
+            overflow-x: auto;
+
+        }
+        .exception .source-code pre{
+            margin: 0;
+        }
+        .exception .source-code pre ol{
+            margin: 0;
+            color: #4288ce;
+            display: inline-block;
+            min-width: 100%;
+            box-sizing: border-box;
+            font-size:14px;
+            font-family: "Century Gothic",Consolas,"Liberation Mono",Courier,Verdana,serif;
+            padding-left: <?php echo (isset($source) && !empty($source)) ? parse_padding($source) : 40;  ?>px;
+        }
+        .exception .source-code pre li{
+            border-left: 1px solid #ddd;
+            height: 18px;
+            line-height: 18px;
+        }
+        .exception .source-code pre code{
+            color: #333;
+            height: 100%;
+            display: inline-block;
+            border-left: 1px solid #fff;
+            font-size:14px;
+            font-family: Consolas,"Liberation Mono",Courier,Verdana,"微软雅黑",serif;
+        }
+        .exception .trace{
+            padding: 6px;
+            border: 1px solid #ddd;
+            border-top: 0 none;
+            line-height: 16px;
+            font-size:14px;
+            font-family: Consolas,"Liberation Mono",Courier,Verdana,"微软雅黑",serif;
+        }
+        .exception .trace h2:hover {
+            text-decoration: underline;
+            cursor: pointer;
+        }
+        .exception .trace ol{
+            margin: 12px;
+        }
+        .exception .trace ol li{
+            padding: 2px 4px;
+        }
+        .exception div:last-child{
+            border-bottom-left-radius: 4px;
+            border-bottom-right-radius: 4px;
+        }
+
+        /* Exception Variables */
+        .exception-var table{
+            width: 100%;
+            margin: 12px 0;
+            box-sizing: border-box;
+            table-layout:fixed;
+            word-wrap:break-word;
+        }
+        .exception-var table caption{
+            text-align: left;
+            font-size: 16px;
+            font-weight: bold;
+            padding: 6px 0;
+        }
+        .exception-var table caption small{
+            font-weight: 300;
+            display: inline-block;
+            margin-left: 10px;
+            color: #ccc;
+        }
+        .exception-var table tbody{
+            font-size: 13px;
+            font-family: Consolas, "Liberation Mono", Courier, "微软雅黑",serif;
+        }
+        .exception-var table td{
+            padding: 0 6px;
+            vertical-align: top;
+            word-break: break-all;
+        }
+        .exception-var table td:first-child{
+            width: 28%;
+            font-weight: bold;
+            white-space: nowrap;
+        }
+        .exception-var table td pre{
+            margin: 0;
+        }
+
+        /* Copyright Info */
+        .copyright{
+            margin-top: 24px;
+            padding: 12px 0;
+            border-top: 1px solid #eee;
+        }
+
+        /* SPAN elements with the classes below are added by prettyprint. */
+        pre.prettyprint .pln { color: #000 }  /* plain text */
+        pre.prettyprint .str { color: #080 }  /* string content */
+        pre.prettyprint .kwd { color: #008 }  /* a keyword */
+        pre.prettyprint .com { color: #800 }  /* a comment */
+        pre.prettyprint .typ { color: #606 }  /* a type name */
+        pre.prettyprint .lit { color: #066 }  /* a literal value */
+        /* punctuation, lisp open bracket, lisp close bracket */
+        pre.prettyprint .pun, pre.prettyprint .opn, pre.prettyprint .clo { color: #660 }
+        pre.prettyprint .tag { color: #008 }  /* a markup tag name */
+        pre.prettyprint .atn { color: #606 }  /* a markup attribute name */
+        pre.prettyprint .atv { color: #080 }  /* a markup attribute value */
+        pre.prettyprint .dec, pre.prettyprint .var { color: #606 }  /* a declaration; a variable name */
+        pre.prettyprint .fun { color: red }  /* a function name */
+    </style>
+</head>
+<body>
+    <?php if (\think\facade\App::isDebug()) { ?>
+        <?php foreach ($traces as $index => $trace) { ?>
+    <div class="exception">
+        <div class="message">
+            <div class="info">
+                <div>
+                    <h2><?php echo "#{$index} [{$trace['code']}]" . sprintf('%s in %s', parse_class($trace['name']), parse_file($trace['file'], $trace['line'])); ?></h2>
+                </div>
+                <div><h1><?php echo nl2br(htmlentities($trace['message'])); ?></h1></div>
+            </div>
+        </div>
+            <?php if (!empty($trace['source'])) { ?>
+                <div class="source-code">
+                    <pre class="prettyprint lang-php"><ol start="<?php echo $trace['source']['first']; ?>"><?php foreach ((array) $trace['source']['source'] as $key => $value) { ?><li class="line-<?php echo "{$index}-"; echo $key + $trace['source']['first']; echo $trace['line'] === $key + $trace['source']['first'] ? ' line-error' : ''; ?>"><code><?php echo htmlentities($value); ?></code></li><?php } ?></ol></pre>
+                </div>
+            <?php }?>
+        <div class="trace">
+            <h2 data-expand="<?php echo 0 === $index ? '1' : '0'; ?>">Call Stack</h2>
+            <ol>
+                <li><?php echo sprintf('in %s', parse_file($trace['file'], $trace['line'])); ?></li>
+                <?php foreach ((array) $trace['trace'] as $value) { ?>
+                    <li>
+                        <?php
+                        // Show Function
+                        if ($value['function']) {
+                            echo sprintf(
+                                'at %s%s%s(%s)',
+                                isset($value['class']) ? parse_class($value['class']) : '',
+                                isset($value['type'])  ? $value['type'] : '',
+                                $value['function'],
+                                isset($value['args'])?parse_args($value['args']):''
+                            );
+                        }
+
+                        // Show line
+                        if (isset($value['file']) && isset($value['line'])) {
+                            echo sprintf(' in %s', parse_file($value['file'], $value['line']));
+                        }
+                        ?>
+                    </li>
+                <?php } ?>
+            </ol>
+        </div>
+    </div>
+        <?php } ?>
+    <?php } else { ?>
+    <div class="exception">
+        <div class="info"><h1><?php echo htmlentities($message); ?></h1></div>
+    </div>
+    <?php } ?>
+    
+    <?php if (!empty($datas)) { ?>
+    <div class="exception-var">
+        <h2>Exception Datas</h2>
+        <?php foreach ((array) $datas as $label => $value) { ?>
+        <table>
+            <?php if (empty($value)) { ?>
+            <caption><?php echo $label; ?><small>empty</small></caption>
+            <?php } else { ?>
+            <caption><?php echo $label; ?></caption>
+            <tbody>
+                <?php foreach ((array) $value as $key => $val) { ?>
+                <tr>
+                    <td><?php echo htmlentities($key); ?></td>
+                    <td><?php echo_value($val); ?></td>
+                </tr>
+                <?php } ?>
+            </tbody>
+            <?php } ?>
+        </table>
+        <?php } ?>
+    </div>
+    <?php } ?>
+
+    <?php if (!empty($tables)) { ?>
+    <div class="exception-var">
+        <h2>Environment Variables</h2>
+        <?php foreach ((array) $tables as $label => $value) { ?>
+        <table>
+            <?php if (empty($value)) { ?>
+            <caption><?php echo $label; ?><small>empty</small></caption>
+            <?php } else { ?>
+            <caption><?php echo $label; ?></caption>
+            <tbody>
+                <?php foreach ((array) $value as $key => $val) { ?>
+                <tr>
+                    <td><?php echo htmlentities($key); ?></td>
+                    <td><?php echo_value($val); ?></td>
+                </tr>
+                <?php } ?>
+            </tbody>
+            <?php } ?>
+        </table>
+        <?php } ?>
+    </div>
+    <?php } ?>
+
+    <div class="copyright">
+        <a title="官方网站" href="http://www.thinkphp.cn">ThinkPHP</a> 
+        <span>V<?php echo \think\facade\App::version(); ?></span> 
+        <span>{ 十年磨一剑-为API开发设计的高性能框架 }</span>
+        <span>- <a title="官方手册" href="https://www.kancloud.cn/manual/thinkphp6_0/content">官方手册</a></span>
+    </div>
+    <?php if (\think\facade\App::isDebug()) { ?>
+    <script>
+        function $(selector, node){
+            var elements;
+
+            node = node || document;
+            if(document.querySelectorAll){
+                elements = node.querySelectorAll(selector);
+            } else {
+                switch(selector.substr(0, 1)){
+                    case '#':
+                        elements = [node.getElementById(selector.substr(1))];
+                        break;
+                    case '.':
+                        if(document.getElementsByClassName){
+                            elements = node.getElementsByClassName(selector.substr(1));
+                        } else {
+                            elements = get_elements_by_class(selector.substr(1), node);
+                        }
+                        break;
+                    default:
+                        elements = node.getElementsByTagName();
+                }
+            }
+            return elements;
+
+            function get_elements_by_class(search_class, node, tag) {
+                var elements = [], eles, 
+                    pattern  = new RegExp('(^|\\s)' + search_class + '(\\s|$)');
+
+                node = node || document;
+                tag  = tag  || '*';
+
+                eles = node.getElementsByTagName(tag);
+                for(var i = 0; i < eles.length; i++) {
+                    if(pattern.test(eles[i].className)) {
+                        elements.push(eles[i])
+                    }
+                }
+
+                return elements;
+            }
+        }
+
+        $.getScript = function(src, func){
+            var script = document.createElement('script');
+            
+            script.async  = 'async';
+            script.src    = src;
+            script.onload = func || function(){};
+            
+            $('head')[0].appendChild(script);
+        }
+
+        ;(function(){
+            var files = $('.toggle');
+            var ol    = $('ol', $('.prettyprint')[0]);
+            var li    = $('li', ol[0]);   
+
+            // 短路径和长路径变换
+            for(var i = 0; i < files.length; i++){
+                files[i].ondblclick = function(){
+                    var title = this.title;
+
+                    this.title = this.innerHTML;
+                    this.innerHTML = title;
+                }
+            }
+
+            (function () {
+                var expand = function (dom, expand) {
+                    var ol = $('ol', dom.parentNode)[0];
+                    expand = undefined === expand ? dom.attributes['data-expand'].value === '0' : undefined;
+                    if (expand) {
+                        dom.attributes['data-expand'].value = '1';
+                        ol.style.display = 'none';
+                        dom.innerText = 'Call Stack (展开)';
+                    } else {
+                        dom.attributes['data-expand'].value = '0';
+                        ol.style.display = 'block';
+                        dom.innerText = 'Call Stack (折叠)';
+                    }
+                };
+                var traces = $('.trace');
+                for (var i = 0; i < traces.length; i ++) {
+                    var h2 = $('h2', traces[i])[0];
+                    expand(h2);
+                    h2.onclick = function () {
+                        expand(this);
+                    };
+                }
+            })();
+
+            $.getScript('//cdn.bootcdn.net/ajax/libs/prettify/r298/prettify.min.js', function(){
+                prettyPrint();
+            });
+        })();
+    </script>
+    <?php } ?>
+</body>
+</html>
diff --git a/vendor/topthink/framework/tests/AppTest.php b/vendor/topthink/framework/tests/AppTest.php
new file mode 100755
index 0000000..6b86015
--- /dev/null
+++ b/vendor/topthink/framework/tests/AppTest.php
@@ -0,0 +1,215 @@
+<?php
+
+namespace think\tests;
+
+use Mockery as m;
+use org\bovigo\vfs\vfsStream;
+use org\bovigo\vfs\vfsStreamDirectory;
+use PHPUnit\Framework\TestCase;
+use stdClass;
+use think\App;
+use think\Env;
+use think\Event;
+use think\event\AppInit;
+use think\exception\ClassNotFoundException;
+use think\Service;
+
+class SomeService extends Service
+{
+    public $bind = [
+        'some' => 'class',
+    ];
+
+    public function register()
+    {
+
+    }
+
+    public function boot()
+    {
+
+    }
+}
+
+/**
+ * @property array initializers
+ */
+class AppTest extends TestCase
+{
+    /** @var App */
+    protected $app;
+
+    protected function setUp()
+    {
+        $this->app = new App();
+    }
+
+    protected function tearDown(): void
+    {
+        m::close();
+    }
+
+    public function testService()
+    {
+        $this->app->register(stdClass::class);
+
+        $this->assertInstanceOf(stdClass::class, $this->app->getService(stdClass::class));
+
+        $service = m::mock(SomeService::class);
+
+        $service->shouldReceive('register')->once();
+
+        $this->app->register($service);
+
+        $this->assertEquals($service, $this->app->getService(SomeService::class));
+
+        $service2 = m::mock(SomeService::class);
+
+        $service2->shouldReceive('register')->once();
+
+        $this->app->register($service2);
+
+        $this->assertEquals($service, $this->app->getService(SomeService::class));
+
+        $this->app->register($service2, true);
+
+        $this->assertEquals($service2, $this->app->getService(SomeService::class));
+
+        $service->shouldReceive('boot')->once();
+        $service2->shouldReceive('boot')->once();
+
+        $this->app->boot();
+    }
+
+    public function testDebug()
+    {
+        $this->app->debug(false);
+
+        $this->assertFalse($this->app->isDebug());
+
+        $this->app->debug(true);
+
+        $this->assertTrue($this->app->isDebug());
+    }
+
+    public function testNamespace()
+    {
+        $namespace = 'test';
+
+        $this->app->setNamespace($namespace);
+
+        $this->assertEquals($namespace, $this->app->getNamespace());
+    }
+
+    public function testVersion()
+    {
+        $this->assertEquals(App::VERSION, $this->app->version());
+    }
+
+    public function testPath()
+    {
+        $rootPath = __DIR__ . DIRECTORY_SEPARATOR;
+
+        $app = new App($rootPath);
+
+        $this->assertEquals($rootPath, $app->getRootPath());
+
+        $this->assertEquals(dirname(__DIR__) . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR, $app->getThinkPath());
+
+        $this->assertEquals($rootPath . 'app' . DIRECTORY_SEPARATOR, $app->getAppPath());
+
+        $appPath = $rootPath . 'app' . DIRECTORY_SEPARATOR . 'admin' . DIRECTORY_SEPARATOR;
+        $app->setAppPath($appPath);
+        $this->assertEquals($appPath, $app->getAppPath());
+
+        $this->assertEquals($rootPath . 'app' . DIRECTORY_SEPARATOR, $app->getBasePath());
+
+        $this->assertEquals($rootPath . 'config' . DIRECTORY_SEPARATOR, $app->getConfigPath());
+
+        $this->assertEquals($rootPath . 'runtime' . DIRECTORY_SEPARATOR, $app->getRuntimePath());
+
+        $runtimePath = $rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'admin' . DIRECTORY_SEPARATOR;
+        $app->setRuntimePath($runtimePath);
+        $this->assertEquals($runtimePath, $app->getRuntimePath());
+    }
+
+    /**
+     * @param vfsStreamDirectory $root
+     * @param bool               $debug
+     * @return App
+     */
+    protected function prepareAppForInitialize(vfsStreamDirectory $root, $debug = true)
+    {
+        $rootPath = $root->url() . DIRECTORY_SEPARATOR;
+
+        $app = new App($rootPath);
+
+        $initializer = m::mock();
+        $initializer->shouldReceive('init')->once()->with($app);
+
+        $app->instance($initializer->mockery_getName(), $initializer);
+
+        (function () use ($initializer) {
+            $this->initializers = [$initializer->mockery_getName()];
+        })->call($app);
+
+        $env = m::mock(Env::class);
+        $env->shouldReceive('load')->once()->with($rootPath . '.env');
+        $env->shouldReceive('get')->once()->with('config_ext', '.php')->andReturn('.php');
+        $env->shouldReceive('get')->once()->with('app_debug')->andReturn($debug);
+
+        $event = m::mock(Event::class);
+        $event->shouldReceive('trigger')->once()->with(AppInit::class);
+        $event->shouldReceive('bind')->once()->with([]);
+        $event->shouldReceive('listenEvents')->once()->with([]);
+        $event->shouldReceive('subscribe')->once()->with([]);
+
+        $app->instance('env', $env);
+        $app->instance('event', $event);
+
+        return $app;
+    }
+
+    public function testInitialize()
+    {
+        $root = vfsStream::setup('rootDir', null, [
+            '.env'   => '',
+            'app'    => [
+                'common.php'   => '',
+                'event.php'    => '<?php return ["bind"=>[],"listen"=>[],"subscribe"=>[]];',
+                'provider.php' => '<?php return [];',
+            ],
+            'config' => [
+                'app.php' => '<?php return [];',
+            ],
+        ]);
+
+        $app = $this->prepareAppForInitialize($root, true);
+
+        $app->debug(false);
+
+        $app->initialize();
+
+        $this->assertIsInt($app->getBeginMem());
+        $this->assertIsFloat($app->getBeginTime());
+
+        $this->assertTrue($app->initialized());
+    }
+
+    public function testFactory()
+    {
+        $this->assertInstanceOf(stdClass::class, App::factory(stdClass::class));
+
+        $this->expectException(ClassNotFoundException::class);
+
+        App::factory('SomeClass');
+    }
+
+    public function testParseClass()
+    {
+        $this->assertEquals('app\\controller\\SomeClass', $this->app->parseClass('controller', 'some_class'));
+        $this->app->setNamespace('app2');
+        $this->assertEquals('app2\\controller\\SomeClass', $this->app->parseClass('controller', 'some_class'));
+    }
+
+}
diff --git a/vendor/topthink/framework/tests/CacheTest.php b/vendor/topthink/framework/tests/CacheTest.php
new file mode 100755
index 0000000..5b5a13c
--- /dev/null
+++ b/vendor/topthink/framework/tests/CacheTest.php
@@ -0,0 +1,149 @@
+<?php
+
+namespace think\tests;
+
+use InvalidArgumentException;
+use Mockery as m;
+use Mockery\MockInterface;
+use org\bovigo\vfs\vfsStream;
+use PHPUnit\Framework\TestCase;
+use think\App;
+use think\Cache;
+use think\Config;
+use think\Container;
+
+class CacheTest extends TestCase
+{
+    /** @var App|MockInterface */
+    protected $app;
+
+    /** @var Cache|MockInterface */
+    protected $cache;
+
+    /** @var Config|MockInterface */
+    protected $config;
+
+    protected function tearDown(): void
+    {
+        m::close();
+    }
+
+    protected function setUp()
+    {
+        $this->app = m::mock(App::class)->makePartial();
+
+        Container::setInstance($this->app);
+        $this->app->shouldReceive('make')->with(App::class)->andReturn($this->app);
+        $this->config = m::mock(Config::class)->makePartial();
+        $this->app->shouldReceive('get')->with('config')->andReturn($this->config);
+
+        $this->cache = new Cache($this->app);
+    }
+
+    public function testGetConfig()
+    {
+        $config = [
+            'default' => 'file',
+        ];
+
+        $this->config->shouldReceive('get')->with('cache')->andReturn($config);
+
+        $this->assertEquals($config, $this->cache->getConfig());
+
+        $this->expectException(InvalidArgumentException::class);
+        $this->cache->getStoreConfig('foo');
+    }
+
+    public function testCacheManagerInstances()
+    {
+        $this->config->shouldReceive('get')->with("cache.stores.single", null)->andReturn(['type' => 'file']);
+
+        $channel1 = $this->cache->store('single');
+        $channel2 = $this->cache->store('single');
+
+        $this->assertSame($channel1, $channel2);
+    }
+
+    public function testFileCache()
+    {
+        $root = vfsStream::setup();
+
+        $this->config->shouldReceive('get')->with("cache.default", null)->andReturn('file');
+
+        $this->config->shouldReceive('get')->with("cache.stores.file", null)->andReturn(['type' => 'file', 'path' => $root->url()]);
+
+        $this->cache->set('foo', 5);
+        $this->cache->inc('foo');
+        $this->assertEquals(6, $this->cache->get('foo'));
+        $this->cache->dec('foo', 2);
+        $this->assertEquals(4, $this->cache->get('foo'));
+
+        $this->cache->set('bar', true);
+        $this->assertTrue($this->cache->get('bar'));
+
+        $this->cache->set('baz', null);
+        $this->assertNull($this->cache->get('baz'));
+
+        $this->assertTrue($this->cache->has('baz'));
+        $this->cache->delete('baz');
+        $this->assertFalse($this->cache->has('baz'));
+        $this->assertNull($this->cache->get('baz'));
+        $this->assertFalse($this->cache->get('baz', false));
+
+        $this->assertTrue($root->hasChildren());
+        $this->cache->clear();
+        $this->assertFalse($root->hasChildren());
+
+        //tags
+        $this->cache->tag('foo')->set('bar', 'foobar');
+        $this->assertEquals('foobar', $this->cache->get('bar'));
+        $this->cache->tag('foo')->clear();
+        $this->assertFalse($this->cache->has('bar'));
+
+        //multiple
+        $this->cache->setMultiple(['foo' => ['foobar', 'bar'], 'foobar' => ['foo', 'bar']]);
+        $this->assertEquals(['foo' => ['foobar', 'bar'], 'foobar' => ['foo', 'bar']], $this->cache->getMultiple(['foo', 'foobar']));
+        $this->assertTrue($this->cache->deleteMultiple(['foo', 'foobar']));
+    }
+
+    public function testRedisCache()
+    {
+        if (extension_loaded('redis')) {
+            return;
+        }
+        $this->config->shouldReceive('get')->with("cache.default", null)->andReturn('redis');
+        $this->config->shouldReceive('get')->with("cache.stores.redis", null)->andReturn(['type' => 'redis']);
+
+        $redis = m::mock('overload:\Predis\Client');
+
+        $redis->shouldReceive("set")->once()->with('foo', 5)->andReturnTrue();
+        $redis->shouldReceive("incrby")->once()->with('foo', 1)->andReturnTrue();
+        $redis->shouldReceive("decrby")->once()->with('foo', 2)->andReturnTrue();
+        $redis->shouldReceive("get")->once()->with('foo')->andReturn('6');
+        $redis->shouldReceive("get")->once()->with('foo')->andReturn('4');
+        $redis->shouldReceive("set")->once()->with('bar', serialize(true))->andReturnTrue();
+        $redis->shouldReceive("set")->once()->with('baz', serialize(null))->andReturnTrue();
+        $redis->shouldReceive("del")->once()->with('baz')->andReturnTrue();
+        $redis->shouldReceive("flushDB")->once()->andReturnTrue();
+        $redis->shouldReceive("set")->once()->with('bar', serialize('foobar'))->andReturnTrue();
+        $redis->shouldReceive("sAdd")->once()->with('tag:' . md5('foo'), 'bar')->andReturnTrue();
+        $redis->shouldReceive("sMembers")->once()->with('tag:' . md5('foo'))->andReturn(['bar']);
+        $redis->shouldReceive("del")->once()->with(['bar'])->andReturnTrue();
+        $redis->shouldReceive("del")->once()->with('tag:' . md5('foo'))->andReturnTrue();
+
+        $this->cache->set('foo', 5);
+        $this->cache->inc('foo');
+        $this->assertEquals(6, $this->cache->get('foo'));
+        $this->cache->dec('foo', 2);
+        $this->assertEquals(4, $this->cache->get('foo'));
+
+        $this->cache->set('bar', true);
+        $this->cache->set('baz', null);
+        $this->cache->delete('baz');
+        $this->cache->clear();
+
+        //tags
+        $this->cache->tag('foo')->set('bar', 'foobar');
+        $this->cache->tag('foo')->clear();
+    }
+}
diff --git a/vendor/topthink/framework/tests/ConfigTest.php b/vendor/topthink/framework/tests/ConfigTest.php
new file mode 100755
index 0000000..271a34f
--- /dev/null
+++ b/vendor/topthink/framework/tests/ConfigTest.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace think\tests;
+
+use org\bovigo\vfs\vfsStream;
+use PHPUnit\Framework\TestCase;
+use think\Config;
+
+class ConfigTest extends TestCase
+{
+    public function testLoad()
+    {
+        $root = vfsStream::setup();
+        $file = vfsStream::newFile('test.php')->setContent("<?php return ['key1'=> 'value1','key2'=>'value2'];");
+        $root->addChild($file);
+
+        $config = new Config();
+
+        $config->load($file->url(), 'test');
+
+        $this->assertEquals('value1', $config->get('test.key1'));
+        $this->assertEquals('value2', $config->get('test.key2'));
+
+        $this->assertSame(['key1' => 'value1', 'key2' => 'value2'], $config->get('test'));
+    }
+
+    public function testSetAndGet()
+    {
+        $config = new Config();
+
+        $config->set([
+            'key1' => 'value1',
+            'key2' => [
+                'key3' => 'value3',
+            ],
+        ], 'test');
+
+        $this->assertTrue($config->has('test.key1'));
+        $this->assertEquals('value1', $config->get('test.key1'));
+        $this->assertEquals('value3', $config->get('test.key2.key3'));
+
+        $this->assertEquals(['key3' => 'value3'], $config->get('test.key2'));
+        $this->assertFalse($config->has('test.key3'));
+        $this->assertEquals('none', $config->get('test.key3', 'none'));
+    }
+}
diff --git a/vendor/topthink/framework/tests/ContainerTest.php b/vendor/topthink/framework/tests/ContainerTest.php
new file mode 100755
index 0000000..e27deb0
--- /dev/null
+++ b/vendor/topthink/framework/tests/ContainerTest.php
@@ -0,0 +1,314 @@
+<?php
+
+namespace think\tests;
+
+use PHPUnit\Framework\TestCase;
+use ReflectionMethod;
+use stdClass;
+use think\Container;
+use think\exception\ClassNotFoundException;
+use think\exception\FuncNotFoundException;
+
+class Taylor
+{
+    public $name;
+
+    public function __construct($name)
+    {
+        $this->name = $name;
+    }
+
+    public function some(Container $container)
+    {
+    }
+
+    protected function protectionFun()
+    {
+        return true;
+    }
+
+    public static function test(Container $container)
+    {
+        return $container;
+    }
+
+    public static function __make()
+    {
+        return new self('Taylor');
+    }
+}
+
+class SomeClass
+{
+    public $container;
+
+    public $count = 0;
+
+    public function __construct(Container $container)
+    {
+        $this->container = $container;
+    }
+}
+
+class ContainerTest extends TestCase
+{
+    protected function tearDown(): void
+    {
+        Container::setInstance(null);
+    }
+
+    public function testClosureResolution()
+    {
+        $container = new Container;
+
+        Container::setInstance($container);
+
+        $container->bind('name', function () {
+            return 'Taylor';
+        });
+
+        $this->assertEquals('Taylor', $container->make('name'));
+
+        $this->assertEquals('Taylor', Container::pull('name'));
+    }
+
+    public function testGet()
+    {
+        $container = new Container;
+
+        $this->expectException(ClassNotFoundException::class);
+        $this->expectExceptionMessage('class not exists: name');
+        $container->get('name');
+
+        $container->bind('name', function () {
+            return 'Taylor';
+        });
+
+        $this->assertSame('Taylor', $container->get('name'));
+    }
+
+    public function testExist()
+    {
+        $container = new Container;
+
+        $container->bind('name', function () {
+            return 'Taylor';
+        });
+
+        $this->assertFalse($container->exists("name"));
+
+        $container->make('name');
+
+        $this->assertTrue($container->exists('name'));
+    }
+
+    public function testInstance()
+    {
+        $container = new Container;
+
+        $container->bind('name', function () {
+            return 'Taylor';
+        });
+
+        $this->assertEquals('Taylor', $container->get('name'));
+
+        $container->bind('name2', Taylor::class);
+
+        $object = new stdClass();
+
+        $this->assertFalse($container->exists('name2'));
+
+        $container->instance('name2', $object);
+
+        $this->assertTrue($container->exists('name2'));
+
+        $this->assertTrue($container->exists(Taylor::class));
+
+        $this->assertEquals($object, $container->make(Taylor::class));
+
+        unset($container->name1);
+
+        $this->assertFalse($container->exists('name1'));
+
+        $container->delete('name2');
+
+        $this->assertFalse($container->exists('name2'));
+
+        foreach ($container as $class => $instance) {
+
+        }
+    }
+
+    public function testBind()
+    {
+        $container = new Container;
+
+        $object = new stdClass();
+
+        $container->bind(['name' => Taylor::class]);
+
+        $container->bind('name2', $object);
+
+        $container->bind('name3', Taylor::class);
+        $container->bind('name3', Taylor::class);
+
+        $container->name4 = $object;
+
+        $container['name5'] = $object;
+
+        $this->assertTrue(isset($container->name4));
+
+        $this->assertTrue(isset($container['name5']));
+
+        $this->assertInstanceOf(Taylor::class, $container->get('name'));
+
+        $this->assertSame($object, $container->get('name2'));
+
+        $this->assertSame($object, $container->name4);
+
+        $this->assertSame($object, $container['name5']);
+
+        $this->assertInstanceOf(Taylor::class, $container->get('name3'));
+
+        unset($container['name']);
+
+        $this->assertFalse(isset($container['name']));
+
+        unset($container->name3);
+
+        $this->assertFalse(isset($container->name3));
+    }
+
+    public function testAutoConcreteResolution()
+    {
+        $container = new Container;
+
+        $taylor = $container->make(Taylor::class);
+
+        $this->assertInstanceOf(Taylor::class, $taylor);
+        $this->assertAttributeSame('Taylor', 'name', $taylor);
+    }
+
+    public function testGetAndSetInstance()
+    {
+        $this->assertInstanceOf(Container::class, Container::getInstance());
+
+        $object = new stdClass();
+
+        Container::setInstance($object);
+
+        $this->assertSame($object, Container::getInstance());
+
+        Container::setInstance(function () {
+            return $this;
+        });
+
+        $this->assertSame($this, Container::getInstance());
+    }
+
+    public function testResolving()
+    {
+        $container = new Container();
+        $container->bind(Container::class, $container);
+
+        $container->resolving(function (SomeClass $taylor, Container $container) {
+            $taylor->count++;
+        });
+        $container->resolving(SomeClass::class, function (SomeClass $taylor, Container $container) {
+            $taylor->count++;
+        });
+
+        /** @var SomeClass $someClass */
+        $someClass = $container->invokeClass(SomeClass::class);
+        $this->assertEquals(2, $someClass->count);
+    }
+
+    public function testInvokeFunctionWithoutMethodThrowsException()
+    {
+        $this->expectException(FuncNotFoundException::class);
+        $this->expectExceptionMessage('function not exists: ContainerTestCallStub()');
+        $container = new Container();
+        $container->invokeFunction('ContainerTestCallStub', []);
+    }
+
+    public function testInvokeProtectionMethod()
+    {
+        $container = new Container();
+        $this->assertTrue($container->invokeMethod([Taylor::class, 'protectionFun'], [], true));
+    }
+
+    public function testInvoke()
+    {
+        $container = new Container();
+
+        Container::setInstance($container);
+
+        $container->bind(Container::class, $container);
+
+        $stub = $this->createMock(Taylor::class);
+
+        $stub->expects($this->once())->method('some')->with($container)->will($this->returnSelf());
+
+        $container->invokeMethod([$stub, 'some']);
+
+        $this->assertEquals('48', $container->invoke('ord', ['0']));
+
+        $this->assertSame($container, $container->invoke(Taylor::class . '::test', []));
+
+        $this->assertSame($container, $container->invokeMethod(Taylor::class . '::test'));
+
+        $reflect = new ReflectionMethod($container, 'exists');
+
+        $this->assertTrue($container->invokeReflectMethod($container, $reflect, [Container::class]));
+
+        $this->assertSame($container, $container->invoke(function (Container $container) {
+            return $container;
+        }));
+
+        $this->assertSame($container, $container->invoke(Taylor::class . '::test'));
+
+        $object = $container->invokeClass(SomeClass::class);
+        $this->assertInstanceOf(SomeClass::class, $object);
+        $this->assertSame($container, $object->container);
+
+        $stdClass = new stdClass();
+
+        $container->invoke(function (Container $container, stdClass $stdObject, $key1, $lowKey, $key2 = 'default') use ($stdClass) {
+            $this->assertEquals('value1', $key1);
+            $this->assertEquals('default', $key2);
+            $this->assertEquals('value2', $lowKey);
+            $this->assertSame($stdClass, $stdObject);
+            return $container;
+        }, ['some' => $stdClass, 'key1' => 'value1', 'low_key' => 'value2']);
+    }
+
+    public function testInvokeMethodNotExists()
+    {
+        $container = $this->resolveContainer();
+        $this->expectException(FuncNotFoundException::class);
+
+        $container->invokeMethod([SomeClass::class, 'any']);
+    }
+
+    public function testInvokeClassNotExists()
+    {
+        $container = new Container();
+
+        Container::setInstance($container);
+
+        $container->bind(Container::class, $container);
+
+        $this->expectExceptionObject(new ClassNotFoundException('class not exists: SomeClass'));
+
+        $container->invokeClass('SomeClass');
+    }
+
+    protected function resolveContainer()
+    {
+        $container = new Container();
+
+        Container::setInstance($container);
+        return $container;
+    }
+
+}
diff --git a/vendor/topthink/framework/tests/DbTest.php b/vendor/topthink/framework/tests/DbTest.php
new file mode 100755
index 0000000..3bd0c1e
--- /dev/null
+++ b/vendor/topthink/framework/tests/DbTest.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace think\tests;
+
+use Mockery as m;
+use PHPUnit\Framework\TestCase;
+use think\Cache;
+use think\cache\Driver;
+use think\Config;
+use think\Db;
+use think\Event;
+use think\Log;
+
+class DbTest extends TestCase
+{
+    protected function tearDown(): void
+    {
+        m::close();
+    }
+
+    public function testMake()
+    {
+        $event  = m::mock(Event::class);
+        $config = m::mock(Config::class);
+        $log    = m::mock(Log::class);
+        $cache  = m::mock(Cache::class);
+        $store  = m::mock(Driver::class);
+
+        $config->shouldReceive('get')->with('database.cache_store', null)->andReturn(null);
+        $cache->shouldReceive('store')->with(null)->andReturn($store);
+
+        $db = Db::__make($event, $config, $log, $cache);
+
+        $config->shouldReceive('get')->with('database.foo', null)->andReturn('foo');
+        $this->assertEquals('foo', $db->getConfig('foo'));
+
+        $config->shouldReceive('get')->with('database', [])->andReturn([]);
+        $this->assertEquals([], $db->getConfig());
+
+        $callback = function () {
+        };
+        $event->shouldReceive('listen')->with('db.some', $callback);
+        $db->event('some', $callback);
+
+        $event->shouldReceive('trigger')->with('db.some', null, false);
+        $db->trigger('some');
+    }
+
+}
diff --git a/vendor/topthink/framework/tests/DispatchTest.php b/vendor/topthink/framework/tests/DispatchTest.php
new file mode 100755
index 0000000..2b9e413
--- /dev/null
+++ b/vendor/topthink/framework/tests/DispatchTest.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace think\tests;
+
+use GuzzleHttp\Psr7\Response;
+use Mockery;
+use PHPUnit\Framework\TestCase;
+use think\Request;
+use think\route\Dispatch;
+use think\route\Rule;
+
+class DispatchTest extends TestCase
+{
+    public function testPsr7Response()
+    {
+        $request = Mockery::mock(Request::class);
+        $rule = Mockery::mock(Rule::class);
+        $dispatch = new class($request, $rule, '') extends Dispatch {
+            public function exec()
+            {
+                return new Response(200, ['framework' => ['tp', 'thinkphp'], 'psr' => 'psr-7'], '123');
+            }
+        };
+
+        $response = $dispatch->run();
+
+        $this->assertInstanceOf(\think\Response::class, $response);
+        $this->assertEquals('123', $response->getContent());
+        $this->assertEquals('tp, thinkphp', $response->getHeader('framework'));
+        $this->assertEquals('psr-7', $response->getHeader('psr'));
+    }
+}
diff --git a/vendor/topthink/framework/tests/EnvTest.php b/vendor/topthink/framework/tests/EnvTest.php
new file mode 100755
index 0000000..9b3b615
--- /dev/null
+++ b/vendor/topthink/framework/tests/EnvTest.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace think\tests;
+
+use org\bovigo\vfs\vfsStream;
+use PHPUnit\Framework\TestCase;
+use think\Env;
+use think\Exception;
+
+class EnvTest extends TestCase
+{
+    public function testEnvFile()
+    {
+        $root    = vfsStream::setup();
+        $envFile = vfsStream::newFile('.env')->setContent("key1=value1\nkey2=value2");
+        $root->addChild($envFile);
+
+        $env = new Env();
+
+        $env->load($envFile->url());
+
+        $this->assertEquals('value1', $env->get('key1'));
+        $this->assertEquals('value2', $env->get('key2'));
+    }
+
+    public function testServerEnv()
+    {
+        $env = new Env();
+
+        $this->assertEquals('value2', $env->get('key2', 'value2'));
+
+        putenv('PHP_KEY7=value7');
+        putenv('PHP_KEY8=false');
+        putenv('PHP_KEY9=true');
+
+        $this->assertEquals('value7', $env->get('key7'));
+        $this->assertFalse($env->get('KEY8'));
+        $this->assertTrue($env->get('key9'));
+    }
+
+    public function testSetEnv()
+    {
+        $env = new Env();
+
+        $env->set([
+            'key1' => 'value1',
+            'key2' => [
+                'key1' => 'value1-2',
+            ],
+        ]);
+
+        $env->set('key3', 'value3');
+
+        $env->key4 = 'value4';
+
+        $env['key5'] = 'value5';
+
+        $this->assertEquals('value1', $env->get('key1'));
+        $this->assertEquals('value1-2', $env->get('key2.key1'));
+
+        $this->assertEquals('value3', $env->get('key3'));
+
+        $this->assertEquals('value4', $env->key4);
+
+        $this->assertEquals('value5', $env['key5']);
+
+        $this->expectException(Exception::class);
+
+        unset($env['key5']);
+    }
+
+    public function testHasEnv()
+    {
+        $env = new Env();
+        $env->set(['foo' => 'bar']);
+        $this->assertTrue($env->has('foo'));
+        $this->assertTrue(isset($env->foo));
+        $this->assertTrue($env->offsetExists('foo'));
+    }
+}
diff --git a/vendor/topthink/framework/tests/EventTest.php b/vendor/topthink/framework/tests/EventTest.php
new file mode 100755
index 0000000..ded5a36
--- /dev/null
+++ b/vendor/topthink/framework/tests/EventTest.php
@@ -0,0 +1,134 @@
+<?php
+
+namespace think\tests;
+
+use Mockery as m;
+use Mockery\MockInterface;
+use PHPUnit\Framework\TestCase;
+use think\App;
+use think\Config;
+use think\Container;
+use think\Event;
+
+class EventTest extends TestCase
+{
+    /** @var App|MockInterface */
+    protected $app;
+
+    /** @var Event|MockInterface */
+    protected $event;
+
+    /** @var Config|MockInterface */
+    protected $config;
+
+    protected function tearDown(): void
+    {
+        m::close();
+    }
+
+    protected function setUp()
+    {
+        $this->app = m::mock(App::class)->makePartial();
+
+        Container::setInstance($this->app);
+        $this->app->shouldReceive('make')->with(App::class)->andReturn($this->app);
+        $this->config = m::mock(Config::class)->makePartial();
+        $this->app->shouldReceive('get')->with('config')->andReturn($this->config);
+
+        $this->event = new Event($this->app);
+    }
+
+    public function testBasic()
+    {
+        $this->event->bind(['foo' => 'baz']);
+
+        $this->event->listen('foo', function ($bar) {
+            $this->assertEquals('bar', $bar);
+        });
+
+        $this->assertTrue($this->event->hasListener('foo'));
+
+        $this->event->trigger('baz', 'bar');
+
+        $this->event->remove('foo');
+
+        $this->assertFalse($this->event->hasListener('foo'));
+    }
+
+    public function testOnceEvent()
+    {
+        $this->event->listen('AppInit', function ($bar) {
+            $this->assertEquals('bar', $bar);
+            return 'foo';
+        });
+
+        $this->assertEquals('foo', $this->event->trigger('AppInit', 'bar', true));
+        $this->assertEquals(['foo'], $this->event->trigger('AppInit', 'bar'));
+    }
+
+    public function testClassListener()
+    {
+        $listener = m::mock("overload:SomeListener", TestListener::class);
+
+        $listener->shouldReceive('handle')->andReturnTrue();
+
+        $this->event->listen('some', "SomeListener");
+
+        $this->assertTrue($this->event->until('some'));
+    }
+
+    public function testSubscribe()
+    {
+        $listener = m::mock("overload:SomeListener", TestListener::class);
+
+        $listener->shouldReceive('subscribe')->andReturnUsing(function (Event $event) use ($listener) {
+
+            $listener->shouldReceive('onBar')->once()->andReturnFalse();
+
+            $event->listenEvents(['SomeListener::onBar' => [[$listener, 'onBar']]]);
+        });
+
+        $this->event->subscribe('SomeListener');
+
+        $this->assertTrue($this->event->hasListener('SomeListener::onBar'));
+
+        $this->event->trigger('SomeListener::onBar');
+    }
+
+    public function testAutoObserve()
+    {
+        $listener = m::mock("overload:SomeListener", TestListener::class);
+
+        $listener->shouldReceive('onBar')->once();
+
+        $this->app->shouldReceive('make')->with('SomeListener')->andReturn($listener);
+
+        $this->event->observe('SomeListener');
+
+        $this->event->trigger('bar');
+    }
+
+}
+
+class TestListener
+{
+    public function handle()
+    {
+
+    }
+
+    public function onBar()
+    {
+
+    }
+
+    public function onFoo()
+    {
+
+    }
+
+    public function subscribe()
+    {
+
+    }
+}
diff --git a/vendor/topthink/framework/tests/HttpTest.php b/vendor/topthink/framework/tests/HttpTest.php
new file mode 100755
index 0000000..c3e0abd
--- /dev/null
+++ b/vendor/topthink/framework/tests/HttpTest.php
@@ -0,0 +1,155 @@
+<?php
+
+namespace think\tests;
+
+use Mockery as m;
+use Mockery\MockInterface;
+use org\bovigo\vfs\vfsStream;
+use PHPUnit\Framework\TestCase;
+use think\App;
+use think\Console;
+use think\Event;
+use think\event\HttpEnd;
+use think\Exception;
+use think\exception\Handle;
+use think\Http;
+use think\Log;
+use think\Request;
+use think\Response;
+use think\Route;
+
+class HttpTest extends TestCase
+{
+    /** @var App|MockInterface */
+    protected $app;
+
+    /** @var Http|MockInterface */
+    protected $http;
+
+    protected function tearDown(): void
+    {
+        m::close();
+    }
+
+    protected function setUp()
+    {
+        $this->app = m::mock(App::class)->makePartial();
+
+        $this->http = m::mock(Http::class, [$this->app])->shouldAllowMockingProtectedMethods()->makePartial();
+    }
+
+    protected function prepareApp($request, $response)
+    {
+        $this->app->shouldReceive('instance')->once()->with('request', $request);
+        $this->app->shouldReceive('initialized')->once()->andReturnFalse();
+        $this->app->shouldReceive('initialize')->once();
+        $this->app->shouldReceive('get')->with('request')->andReturn($request);
+
+        $route = m::mock(Route::class);
+
+        $route->shouldReceive('dispatch')->withArgs(function ($req, $withRoute) use ($request) {
+            if ($withRoute) {
+                $withRoute();
+            }
+            return $req === $request;
+        })->andReturn($response);
+
+        $route->shouldReceive('config')->with('route_annotation')->andReturn(true);
+
+        $this->app->shouldReceive('get')->with('route')->andReturn($route);
+
+        $console = m::mock(Console::class);
+
+        $console->shouldReceive('call');
+
+        $this->app->shouldReceive('get')->with('console')->andReturn($console);
+    }
+
+    public function testRun()
+    {
+        $root = vfsStream::setup('rootDir', null, [
+            'app'   => [
+                'controller'     => [],
+                'middleware.php' => '<?php return [];',
+            ],
+            'route' => [
+                'route.php' => '<?php return [];',
+            ],
+        ]);
+
+        $this->app->shouldReceive('getBasePath')->andReturn($root->getChild('app')->url() . DIRECTORY_SEPARATOR);
+        $this->app->shouldReceive('getRootPath')->andReturn($root->url() . DIRECTORY_SEPARATOR);
+
+        $request  = m::mock(Request::class)->makePartial();
+        $response = m::mock(Response::class)->makePartial();
+
+        $this->prepareApp($request, $response);
+
+        $this->assertEquals($response, $this->http->run($request));
+    }
+
+    public function multiAppRunProvider()
+    {
+        $request1 = m::mock(Request::class)->makePartial();
+        $request1->shouldReceive('subDomain')->andReturn('www');
+        $request1->shouldReceive('host')->andReturn('www.domain.com');
+
+        $request2 = m::mock(Request::class)->makePartial();
+        $request2->shouldReceive('subDomain')->andReturn('app2');
+        $request2->shouldReceive('host')->andReturn('app2.domain.com');
+
+        $request3 = m::mock(Request::class)->makePartial();
+        $request3->shouldReceive('pathinfo')->andReturn('some1/a/b/c');
+
+        $request4 = m::mock(Request::class)->makePartial();
+        $request4->shouldReceive('pathinfo')->andReturn('app3/a/b/c');
+
+        $request5 = m::mock(Request::class)->makePartial();
+        $request5->shouldReceive('pathinfo')->andReturn('some2/a/b/c');
+
+        return [
+            [$request1, true, 'app1'],
+            [$request2, true, 'app2'],
+            [$request3, true, 'app3'],
+            [$request4, true, null],
+            [$request5, true, 'some2', 'path'],
+            [$request1, false, 'some3'],
+        ];
+    }
+
+    public function testRunWithException()
+    {
+        $request  = m::mock(Request::class);
+        $response = m::mock(Response::class);
+
+        $this->app->shouldReceive('instance')->once()->with('request', $request);
+        $this->app->shouldReceive('initialize')->once();
+
+        $exception = new Exception();
+
+        $this->http->shouldReceive('runWithRequest')->once()->with($request)->andThrow($exception);
+
+        $handle = m::mock(Handle::class);
+
+        $handle->shouldReceive('report')->once()->with($exception);
+        $handle->shouldReceive('render')->once()->with($request, $exception)->andReturn($response);
+
+        $this->app->shouldReceive('make')->with(Handle::class)->andReturn($handle);
+
+        $this->assertEquals($response, $this->http->run($request));
+    }
+
+    public function testEnd()
+    {
+        $response = m::mock(Response::class);
+        $event    = m::mock(Event::class);
+        $event->shouldReceive('trigger')->once()->with(HttpEnd::class, $response);
+        $this->app->shouldReceive('get')->once()->with('event')->andReturn($event);
+        $log = m::mock(Log::class);
+        $log->shouldReceive('save')->once();
+        $this->app->shouldReceive('get')->once()->with('log')->andReturn($log);
+
+        $this->http->end($response);
+    }
+
+}
diff --git a/vendor/topthink/framework/tests/InteractsWithApp.php b/vendor/topthink/framework/tests/InteractsWithApp.php
new file mode 100755
index 0000000..f4fcf73
--- /dev/null
+++ b/vendor/topthink/framework/tests/InteractsWithApp.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace think\tests;
+
+use Mockery as m;
+use Mockery\MockInterface;
+use think\App;
+use think\Config;
+use think\Container;
+
+trait InteractsWithApp
+{
+    /** @var App|MockInterface */
+    protected $app;
+
+    /** @var Config|MockInterface */
+    protected $config;
+
+    protected function prepareApp()
+    {
+        $this->app = m::mock(App::class)->makePartial();
+        Container::setInstance($this->app);
+        $this->app->shouldReceive('make')->with(App::class)->andReturn($this->app);
+        $this->app->shouldReceive('isDebug')->andReturnTrue();
+        $this->config = m::mock(Config::class)->makePartial();
+        $this->config->shouldReceive('get')->with('app.show_error_msg')->andReturnTrue();
+        $this->app->shouldReceive('get')->with('config')->andReturn($this->config);
+        $this->app->shouldReceive('runningInConsole')->andReturn(false);
+    }
+}
diff --git a/vendor/topthink/framework/tests/LogTest.php b/vendor/topthink/framework/tests/LogTest.php
new file mode 100755
index 0000000..981110f
--- /dev/null
+++ b/vendor/topthink/framework/tests/LogTest.php
@@ -0,0 +1,130 @@
+<?php
+
+namespace think\tests;
+
+use InvalidArgumentException;
+use Mockery as m;
+use Mockery\MockInterface;
+use org\bovigo\vfs\vfsStream;
+use PHPUnit\Framework\TestCase;
+use think\Log;
+use think\log\ChannelSet;
+
+class LogTest extends TestCase
+{
+    use InteractsWithApp;
+
+    /** @var Log|MockInterface */
+    protected $log;
+
+    protected function tearDown(): void
+    {
+        m::close();
+    }
+
+    protected function setUp()
+    {
+        $this->prepareApp();
+
+        $this->log = new Log($this->app);
+    }
+
+    public function testGetConfig()
+    {
+        $config = [
+            'default' => 'file',
+        ];
+
+        $this->config->shouldReceive('get')->with('log')->andReturn($config);
+
+        $this->assertEquals($config, $this->log->getConfig());
+
+        $this->expectException(InvalidArgumentException::class);
+        $this->log->getChannelConfig('foo');
+    }
+
+    public function testChannel()
+    {
+        $this->assertInstanceOf(ChannelSet::class, $this->log->channel(['file', 'mail']));
+    }
+
+    public function testLogManagerInstances()
+    {
+        $this->config->shouldReceive('get')->with("log.channels.single", null)->andReturn(['type' => 'file']);
+
+        $channel1 = $this->log->channel('single');
+        $channel2 = $this->log->channel('single');
+
+        $this->assertSame($channel1, $channel2);
+    }
+
+    public function testFileLog()
+    {
+        $root = vfsStream::setup();
+
+        $this->config->shouldReceive('get')->with("log.default", null)->andReturn('file');
+
+        $this->config->shouldReceive('get')->with("log.channels.file", null)->andReturn(['type' => 'file', 'path' => $root->url()]);
+
+        $this->log->info('foo');
+
+        $this->assertEquals($this->log->getLog(), ['info' => ['foo']]);
+
+        $this->log->clear();
+
+        $this->assertEmpty($this->log->getLog());
+
+        $this->log->error('foo');
+        $this->assertArrayHasKey('error', $this->log->getLog());
+
+        $this->log->emergency('foo');
+        $this->assertArrayHasKey('emergency', $this->log->getLog());
+
+        $this->log->alert('foo');
+        $this->assertArrayHasKey('alert', $this->log->getLog());
+
+        $this->log->critical('foo');
+        $this->assertArrayHasKey('critical', $this->log->getLog());
+
+        $this->log->warning('foo');
+        $this->assertArrayHasKey('warning', $this->log->getLog());
+
+        $this->log->notice('foo');
+        $this->assertArrayHasKey('notice', $this->log->getLog());
+
+        $this->log->debug('foo');
+        $this->assertArrayHasKey('debug', $this->log->getLog());
+
+        $this->log->sql('foo');
+        $this->assertArrayHasKey('sql', $this->log->getLog());
+
+        $this->log->custom('foo');
+        $this->assertArrayHasKey('custom', $this->log->getLog());
+
+        $this->log->write('foo');
+        $this->assertTrue($root->hasChildren());
+        $this->assertEmpty($this->log->getLog());
+
+        $this->log->close();
+
+        $this->log->info('foo');
+
+        $this->assertEmpty($this->log->getLog());
+    }
+
+    public function testSave()
+    {
+        $root = vfsStream::setup();
+
+        $this->config->shouldReceive('get')->with("log.default", null)->andReturn('file');
+
+        $this->config->shouldReceive('get')->with("log.channels.file", null)->andReturn(['type' => 'file', 'path' => $root->url()]);
+
+        $this->log->info('foo');
+
+        $this->log->save();
+
+        $this->assertTrue($root->hasChildren());
+    }
+
+}
diff --git a/vendor/topthink/framework/tests/MiddlewareTest.php b/vendor/topthink/framework/tests/MiddlewareTest.php
new file mode 100755
index 0000000..aa53059
--- /dev/null
+++ b/vendor/topthink/framework/tests/MiddlewareTest.php
@@ -0,0 +1,108 @@
+<?php
+
+namespace think\tests;
+
+use Mockery as m;
+use Mockery\MockInterface;
+use PHPUnit\Framework\TestCase;
+use think\Exception;
+use think\exception\Handle;
+use think\Middleware;
+use think\Pipeline;
+use think\Request;
+use think\Response;
+
+class MiddlewareTest extends TestCase
+{
+    use InteractsWithApp;
+
+    /** @var Middleware|MockInterface */
+    protected $middleware;
+
+    protected function tearDown(): void
+    {
+        m::close();
+    }
+
+    protected function setUp()
+    {
+        $this->prepareApp();
+
+        $this->middleware = new Middleware($this->app);
+    }
+
+    public function testSetMiddleware()
+    {
+        $this->middleware->add('BarMiddleware', 'bar');
+
+        $this->assertEquals(1, count($this->middleware->all('bar')));
+
+        $this->middleware->controller('BarMiddleware');
+        $this->assertEquals(1, count($this->middleware->all('controller')));
+
+        $this->middleware->import(['FooMiddleware']);
+        $this->assertEquals(1, count($this->middleware->all()));
+
+        $this->middleware->unshift(['BazMiddleware', 'baz']);
+        $this->assertEquals(2, count($this->middleware->all()));
+        $this->assertEquals([['BazMiddleware', 'handle'], 'baz'], $this->middleware->all()[0]);
+
+        $this->config->shouldReceive('get')->with('middleware.alias', [])->andReturn(['foo' => ['FooMiddleware', 'FarMiddleware']]);
+
+        $this->middleware->add('foo');
+        $this->assertEquals(3, count($this->middleware->all()));
+        $this->middleware->add(function () {
+        });
+        $this->middleware->add(function () {
+        });
+        $this->assertEquals(5, count($this->middleware->all()));
+    }
+
+    public function testPipelineAndEnd()
+    {
+        $bar = m::mock("overload:BarMiddleware");
+        $foo = m::mock("overload:FooMiddleware", Foo::class);
+
+        $request  = m::mock(Request::class);
+        $response = m::mock(Response::class);
+
+        $e = new Exception();
+
+        $handle = m::mock(Handle::class);
+        $handle->shouldReceive('report')->with($e)->andReturnNull();
+        $handle->shouldReceive('render')->with($request, $e)->andReturn($response);
+
+        $foo->shouldReceive('handle')->once()->andReturnUsing(function ($request, $next) {
+            return $next($request);
+        });
+        $bar->shouldReceive('handle')->once()->andReturnUsing(function ($request, $next) use ($e) {
+            $next($request);
+            throw  $e;
+        });
+
+        $foo->shouldReceive('end')->once()->with($response)->andReturnNull();
+
+        $this->app->shouldReceive('make')->with(Handle::class)->andReturn($handle);
+
+        $this->config->shouldReceive('get')->once()->with('middleware.priority', [])->andReturn(['FooMiddleware', 'BarMiddleware']);
+
+        $this->middleware->import([function ($request, $next) {
+            return $next($request);
+        }, 'BarMiddleware', 'FooMiddleware']);
+
+        $this->assertInstanceOf(Pipeline::class, $pipeline = $this->middleware->pipeline());
+
+        $pipeline->send($request)->then(function ($request) use ($e, $response) {
+            throw $e;
+        });
+
+        $this->middleware->end($response);
+    }
+}
+
+class Foo
+{
+    public function end(Response $response)
+    {
+    }
+}
diff --git a/vendor/topthink/framework/tests/RouteTest.php b/vendor/topthink/framework/tests/RouteTest.php
new file mode 100755
index 0000000..6a7f8ee
--- /dev/null
+++ b/vendor/topthink/framework/tests/RouteTest.php
@@ -0,0 +1,291 @@
+<?php
+
+namespace think\tests;
+
+use Closure;
+use Mockery as m;
+use Mockery\MockInterface;
+use PHPUnit\Framework\TestCase;
+use think\helper\Str;
+use think\Request;
+use think\response\Redirect;
+use think\response\View;
+use think\Route;
+
+class RouteTest extends TestCase
+{
+    use InteractsWithApp;
+
+    /** @var Route|MockInterface */
+    protected $route;
+
+    protected function tearDown(): void
+    {
+        m::close();
+    }
+
+    protected function setUp()
+    {
+        $this->prepareApp();
+        $this->route = new Route($this->app);
+    }
+
+    /**
+     * @param $path
+     * @param string $method
+     * @param string $host
+     * @return m\Mock|Request
+     */
+    protected function makeRequest($path, $method = 'GET', $host = 'localhost')
+    {
+        $request = m::mock(Request::class)->makePartial();
+        $request->shouldReceive('host')->andReturn($host);
+        $request->shouldReceive('pathinfo')->andReturn($path);
+        $request->shouldReceive('url')->andReturn('/' . $path);
+        $request->shouldReceive('method')->andReturn(strtoupper($method));
+        return $request;
+    }
+
+    public function testSimpleRequest()
+    {
+        $this->route->get('foo', function () {
+            return 'get-foo';
+        });
+
+        $this->route->put('foo', function () {
+            return 'put-foo';
+        });
+
+        $this->route->group(function () {
+            $this->route->post('foo', function () {
+                return 'post-foo';
+            });
+        });
+
+        $request  = $this->makeRequest('foo', 'post');
+        $response = $this->route->dispatch($request);
+        $this->assertEquals(200, $response->getCode());
+        $this->assertEquals('post-foo', $response->getContent());
+
+        $request  = $this->makeRequest('foo', 'get');
+        $response = $this->route->dispatch($request);
+        $this->assertEquals(200, $response->getCode());
+        $this->assertEquals('get-foo', $response->getContent());
+    }
+
+    public function testOptionsRequest()
+    {
+        $this->route->get('foo', function () {
+            return 'get-foo';
+        });
+
+        $this->route->put('foo', function () {
+            return 'put-foo';
+        });
+
+        $this->route->group(function () {
+            $this->route->post('foo', function () {
+                return 'post-foo';
+            });
+        });
+        $this->route->group('abc', function () {
+            $this->route->post('foo/:id', function () {
+                return 'post-abc-foo';
+            });
+        });
+
+        $this->route->post('foo/:id', function () {
+            return 'post-abc-foo';
+        });
+
+        $this->route->resource('bar', 'SomeClass');
+
+        $request  = $this->makeRequest('foo', 'options');
+        $response = $this->route->dispatch($request);
+        $this->assertEquals(204, $response->getCode());
+        $this->assertEquals('GET, PUT, POST', $response->getHeader('Allow'));
+
+        $request  = $this->makeRequest('bar', 'options');
+        $response = $this->route->dispatch($request);
+        $this->assertEquals(204, $response->getCode());
+        $this->assertEquals('GET, POST', $response->getHeader('Allow'));
+
+        $request  = $this->makeRequest('bar/1', 'options');
+        $response = $this->route->dispatch($request);
+        $this->assertEquals(204, $response->getCode());
+        $this->assertEquals('GET, PUT, DELETE', $response->getHeader('Allow'));
+
+        $request  = $this->makeRequest('xxxx', 'options');
+        $response = $this->route->dispatch($request);
+        $this->assertEquals(204, $response->getCode());
+        $this->assertEquals('GET, POST, PUT, DELETE', $response->getHeader('Allow'));
+    }
+
+    public function testAllowCrossDomain()
+    {
+        $this->route->get('foo', function () {
+            return 'get-foo';
+        })->allowCrossDomain(['some' => 'bar']);
+
+        $request  = $this->makeRequest('foo', 'get');
+        $response = $this->route->dispatch($request);
+
+        $this->assertEquals('bar', $response->getHeader('some'));
+        $this->assertArrayHasKey('Access-Control-Allow-Credentials', $response->getHeader());
+
+        $request  = $this->makeRequest('foo2', 'options');
+        $response = $this->route->dispatch($request);
+
+        $this->assertEquals(204, $response->getCode());
+        $this->assertArrayHasKey('Access-Control-Allow-Credentials', $response->getHeader());
+        $this->assertEquals('GET, POST, PUT, DELETE', $response->getHeader('Allow'));
+    }
+
+    public function testControllerDispatch()
+    {
+        $this->route->get('foo', 'foo/bar');
+
+        $controller = m::Mock(\stdClass::class);
+
+        $this->app->shouldReceive('parseClass')->with('controller', 'Foo')->andReturn($controller->mockery_getName());
+        $this->app->shouldReceive('make')->with($controller->mockery_getName(), [], true)->andReturn($controller);
+
+        $controller->shouldReceive('bar')->andReturn('bar');
+
+        $request  = $this->makeRequest('foo');
+        $response = $this->route->dispatch($request);
+        $this->assertEquals('bar', $response->getContent());
+    }
+
+    public function testEmptyControllerDispatch()
+    {
+        $this->route->get('foo', 'foo/bar');
+
+        $controller = m::Mock(\stdClass::class);
+
+        $this->app->shouldReceive('parseClass')->with('controller', 'Error')->andReturn($controller->mockery_getName());
+        $this->app->shouldReceive('make')->with($controller->mockery_getName(), [], true)->andReturn($controller);
+
+        $controller->shouldReceive('bar')->andReturn('bar');
+
+        $request  = $this->makeRequest('foo');
+        $response = $this->route->dispatch($request);
+        $this->assertEquals('bar', $response->getContent());
+    }
+
+    protected function createMiddleware($times = 1)
+    {
+        $middleware = m::mock(Str::random(5));
+        $middleware->shouldReceive('handle')->times($times)->andReturnUsing(function ($request, Closure $next) {
+            return $next($request);
+        });
+        $this->app->shouldReceive('make')->with($middleware->mockery_getName())->andReturn($middleware);
+
+        return $middleware;
+    }
+
+    public function testControllerWithMiddleware()
+    {
+        $this->route->get('foo', 'foo/bar');
+
+        $controller = m::mock(FooClass::class);
+
+        $controller->middleware = [
+            $this->createMiddleware()->mockery_getName() . ":params1:params2",
+            $this->createMiddleware(0)->mockery_getName() => ['except' => 'bar'],
+            $this->createMiddleware()->mockery_getName()  => ['only' => 'bar'],
+            [
+                'middleware' => [$this->createMiddleware()->mockery_getName(), [new \stdClass()]],
+                'options'    => ['only' => 'bar'],
+            ],
+        ];
+
+        $this->app->shouldReceive('parseClass')->with('controller', 'Foo')->andReturn($controller->mockery_getName());
+        $this->app->shouldReceive('make')->with($controller->mockery_getName(), [], true)->andReturn($controller);
+
+        $controller->shouldReceive('bar')->once()->andReturn('bar');
+
+        $request  = $this->makeRequest('foo');
+        $response = $this->route->dispatch($request);
+        $this->assertEquals('bar', $response->getContent());
+    }
+
+    public function testUrlDispatch()
+    {
+        $controller = m::mock(FooClass::class);
+        $controller->shouldReceive('index')->andReturn('bar');
+
+        $this->app->shouldReceive('parseClass')->once()->with('controller', 'Foo')
+            ->andReturn($controller->mockery_getName());
+        $this->app->shouldReceive('make')->with($controller->mockery_getName(), [], true)->andReturn($controller);
+
+        $request  = $this->makeRequest('foo');
+        $response = $this->route->dispatch($request);
+        $this->assertEquals('bar', $response->getContent());
+    }
+
+    public function testRedirectDispatch()
+    {
+        $this->route->redirect('foo', 'http://localhost', 302);
+
+        $request = $this->makeRequest('foo');
+        $this->app->shouldReceive('make')->with(Request::class)->andReturn($request);
+        $response = $this->route->dispatch($request);
+
+        $this->assertInstanceOf(Redirect::class, $response);
+        $this->assertEquals(302, $response->getCode());
+        $this->assertEquals('http://localhost', $response->getData());
+    }
+
+    public function testViewDispatch()
+    {
+        $this->route->view('foo', 'index/hello', ['city' => 'shanghai']);
+
+        $request  = $this->makeRequest('foo');
+        $response = $this->route->dispatch($request);
+
+        $this->assertInstanceOf(View::class, $response);
+        $this->assertEquals(['city' => 'shanghai'], $response->getVars());
+        $this->assertEquals('index/hello', $response->getData());
+    }
+
+    public function testResponseDispatch()
+    {
+        $this->route->get('hello/:name', response()
+            ->data('Hello,ThinkPHP')
+            ->code(200)
+            ->contentType('text/plain'));
+
+        $request  = $this->makeRequest('hello/some');
+        $response = $this->route->dispatch($request);
+
+        $this->assertEquals('Hello,ThinkPHP', $response->getContent());
+        $this->assertEquals(200, $response->getCode());
+    }
+
+    public function testDomainBindResponse()
+    {
+        $this->route->domain('test', function () {
+            $this->route->get('/', function () {
+                return 'Hello,ThinkPHP';
+            });
+        });
+
+        $request  = $this->makeRequest('', 'get', 'test.domain.com');
+        $response = $this->route->dispatch($request);
+
+        $this->assertEquals('Hello,ThinkPHP', $response->getContent());
+        $this->assertEquals(200, $response->getCode());
+    }
+
+}
+
+class FooClass
+{
+    public $middleware = [];
+
+    public function bar()
+    {
+
+    }
+}
diff --git a/vendor/topthink/framework/tests/SessionTest.php b/vendor/topthink/framework/tests/SessionTest.php
new file mode 100755
index 0000000..b3b48a7
--- /dev/null
+++ b/vendor/topthink/framework/tests/SessionTest.php
@@ -0,0 +1,225 @@
+<?php
+
+namespace think\tests;
+
+use Mockery as m;
+use Mockery\MockInterface;
+use org\bovigo\vfs\vfsStream;
+use PHPUnit\Framework\TestCase;
+use think\App;
+use think\cache\Driver;
+use think\Config;
+use think\Container;
+use think\contract\SessionHandlerInterface;
+use think\helper\Str;
+use think\Session;
+use think\session\driver\Cache;
+use think\session\driver\File;
+
+class SessionTest extends TestCase
+{
+    /** @var App|MockInterface */
+    protected $app;
+
+    /** @var Session|MockInterface */
+    protected $session;
+
+    /** @var Config|MockInterface */
+    protected $config;
+
+    protected $handler;
+
+    protected function tearDown(): void
+    {
+        m::close();
+    }
+
+    protected function setUp()
+    {
+        $this->app = m::mock(App::class)->makePartial();
+        Container::setInstance($this->app);
+
+        $this->app->shouldReceive('make')->with(App::class)->andReturn($this->app);
+        $this->config = m::mock(Config::class)->makePartial();
+
+        $this->app->shouldReceive('get')->with('config')->andReturn($this->config);
+        $handlerClass = "\\think\\session\\driver\\Test" . Str::random(10);
+        $this->config->shouldReceive("get")->with("session.type", "file")->andReturn($handlerClass);
+        $this->session = new Session($this->app);
+
+        $this->handler = m::mock('overload:' . $handlerClass, SessionHandlerInterface::class);
+    }
+
+    public function testLoadData()
+    {
+        $data = [
+            "bar" => 'foo',
+        ];
+
+        $id = md5(uniqid());
+
+        $this->handler->shouldReceive("read")->once()->with($id)->andReturn(serialize($data));
+
+        $this->session->setId($id);
+        $this->session->init();
+
+        $this->assertEquals('foo', $this->session->get('bar'));
+        $this->assertTrue($this->session->has('bar'));
+        $this->assertFalse($this->session->has('foo'));
+
+        $this->session->set('foo', 'bar');
+        $this->assertTrue($this->session->has('foo'));
+
+        $this->assertEquals('bar', $this->session->pull('foo'));
+        $this->assertFalse($this->session->has('foo'));
+    }
+
+    public function testSave()
+    {
+
+        $id = md5(uniqid());
+
+        $this->handler->shouldReceive('read')->once()->with($id)->andReturn("");
+
+        $this->handler->shouldReceive('write')->once()->with($id, serialize([
+            "bar" => 'foo',
+        ]))->andReturnTrue();
+
+        $this->session->setId($id);
+        $this->session->init();
+
+        $this->session->set('bar', 'foo');
+
+        $this->session->save();
+    }
+
+    public function testFlash()
+    {
+        $this->session->flash('foo', 'bar');
+        $this->session->flash('bar', 0);
+        $this->session->flash('baz', true);
+
+        $this->assertTrue($this->session->has('foo'));
+        $this->assertEquals('bar', $this->session->get('foo'));
+        $this->assertEquals(0, $this->session->get('bar'));
+        $this->assertTrue($this->session->get('baz'));
+
+        $this->session->clearFlashData();
+
+        $this->assertTrue($this->session->has('foo'));
+        $this->assertEquals('bar', $this->session->get('foo'));
+        $this->assertEquals(0, $this->session->get('bar'));
+
+        $this->session->clearFlashData();
+
+        $this->assertFalse($this->session->has('foo'));
+        $this->assertNull($this->session->get('foo'));
+
+        $this->session->flash('foo', 'bar');
+        $this->assertTrue($this->session->has('foo'));
+        $this->session->clearFlashData();
+        $this->session->reflash();
+        $this->session->clearFlashData();
+
+        $this->assertTrue($this->session->has('foo'));
+    }
+
+    public function testClear()
+    {
+        $this->session->set('bar', 'foo');
+        $this->assertEquals('foo', $this->session->get('bar'));
+        $this->session->clear();
+        $this->assertFalse($this->session->has('foo'));
+    }
+
+    public function testSetName()
+    {
+        $this->session->setName('foo');
+        $this->assertEquals('foo', $this->session->getName());
+    }
+
+    public function testDestroy()
+    {
+        $id = md5(uniqid());
+
+        $this->handler->shouldReceive('read')->once()->with($id)->andReturn("");
+        $this->handler->shouldReceive('delete')->once()->with($id)->andReturnTrue();
+
+        $this->session->setId($id);
+        $this->session->init();
+
+        $this->session->set('bar', 'foo');
+
+        $this->session->destroy();
+
+        $this->assertFalse($this->session->has('bar'));
+
+        $this->assertNotEquals($id, $this->session->getId());
+    }
+
+    public function testFileHandler()
+    {
+        $root = vfsStream::setup();
+
+        vfsStream::newFile('bar')
+            ->at($root)
+            ->lastModified(time());
+
+        vfsStream::newFile('bar')
+            ->at(vfsStream::newDirectory("foo")->at($root))
+            ->lastModified(100);
+
+        $this->assertTrue($root->hasChild("bar"));
+        $this->assertTrue($root->hasChild("foo/bar"));
+
+        $handler = new TestFileHandle($this->app, [
+            'path'           => $root->url(),
+            'gc_probability' => 1,
+            'gc_divisor'     => 1,
+        ]);
+
+        $this->assertTrue($root->hasChild("bar"));
+        $this->assertFalse($root->hasChild("foo/bar"));
+
+        $id = md5(uniqid());
+        $handler->write($id, "bar");
+
+        $this->assertTrue($root->hasChild("sess_{$id}"));
+
+        $this->assertEquals("bar", $handler->read($id));
+
+        $handler->delete($id);
+
+        $this->assertFalse($root->hasChild("sess_{$id}"));
+    }
+
+    public function testCacheHandler()
+    {
+        $id = md5(uniqid());
+
+        $cache = m::mock(\think\Cache::class);
+
+        $store = m::mock(Driver::class);
+
+        $cache->shouldReceive('store')->once()->with('redis')->andReturn($store);
+
+        $handler = new Cache($cache, ['store' => 'redis']);
+
+        $store->shouldReceive("set")->with($id, "bar", 1440)->once()->andReturnTrue();
+        $handler->write($id, "bar");
+
+        $store->shouldReceive("get")->with($id)->once()->andReturn("bar");
+        $this->assertEquals("bar", $handler->read($id));
+
+        $store->shouldReceive("delete")->with($id)->once()->andReturnTrue();
+        $handler->delete($id);
+    }
+}
+
+class TestFileHandle extends File
+{
+    protected function writeFile($path, $content): bool
+    {
+        return (bool) file_put_contents($path, $content);
+    }
+}
diff --git a/vendor/topthink/framework/tests/ViewTest.php b/vendor/topthink/framework/tests/ViewTest.php
new file mode 100755
index 0000000..e413510
--- /dev/null
+++ b/vendor/topthink/framework/tests/ViewTest.php
@@ -0,0 +1,127 @@
+<?php
+
+namespace think\tests;
+
+use Mockery\MockInterface;
+use PHPUnit\Framework\TestCase;
+use think\App;
+use think\Config;
+use think\Container;
+use think\contract\TemplateHandlerInterface;
+use think\View;
+use Mockery as m;
+
+class ViewTest extends TestCase
+{
+    /** @var App|MockInterface */
+    protected $app;
+
+    /** @var View|MockInterface */
+    protected $view;
+
+    /** @var Config|MockInterface */
+    protected $config;
+
+    protected function tearDown(): void
+    {
+        m::close();
+    }
+
+    protected function setUp()
+    {
+        $this->app = m::mock(App::class)->makePartial();
+        Container::setInstance($this->app);
+
+        $this->app->shouldReceive('make')->with(App::class)->andReturn($this->app);
+        $this->config = m::mock(Config::class)->makePartial();
+        $this->app->shouldReceive('get')->with('config')->andReturn($this->config);
+
+        $this->view = new View($this->app);
+    }
+
+    public function testAssignData()
+    {
+        $this->view->assign('foo', 'bar');
+        $this->view->assign(['baz' => 'boom']);
+        $this->view->qux = "corge";
+
+        $this->assertEquals('bar', $this->view->foo);
+        $this->assertEquals('boom', $this->view->baz);
+        $this->assertEquals('corge', $this->view->qux);
+        $this->assertTrue(isset($this->view->qux));
+    }
+
+    public function testRender()
+    {
+        $this->config->shouldReceive("get")->with("view.type", 'php')->andReturn(TestTemplate::class);
+
+        $this->view->filter(function ($content) {
+            return $content;
+        });
+
+        $this->assertEquals("fetch", $this->view->fetch('foo'));
+        $this->assertEquals("display", $this->view->display('foo'));
+    }
+
+}
+
+class TestTemplate implements TemplateHandlerInterface
+{
+
+    /**
+     * 检测是否存在模板文件
+     * @access public
+     * @param string $template 模板文件或者模板规则
+     * @return bool
+     */
+    public function exists(string $template): bool
+    {
+        return true;
+    }
+
+    /**
+     * 渲染模板文件
+     * @access public
+     * @param string $template 模板文件
+     * @param array  $data     模板变量
+     * @return void
+     */
+    public function fetch(string $template, array $data = []): void
+    {
+        echo "fetch";
+    }
+
+    /**
+     * 渲染模板内容
+     * @access public
+     * @param string $content 模板内容
+     * @param array  $data    模板变量
+     * @return void
+     */
+    public function display(string $content, array $data = []): void
+    {
+        echo "display";
+    }
+
+    /**
+     * 配置模板引擎
+     * @access private
+     * @param array $config 参数
+     * @return void
+     */
+    public function config(array $config): void
+    {
+        // TODO: Implement config() method.
+    }
+
+    /**
+     * 获取模板引擎配置
+     * @access public
+     * @param string $name 参数名
+     * @return void
+     */
+    public function getConfig(string $name)
+    {
+        // TODO: Implement getConfig() method.
+    }
+}
diff --git a/vendor/topthink/framework/tests/bootstrap.php b/vendor/topthink/framework/tests/bootstrap.php
new file mode 100755
index 0000000..3459061
--- /dev/null
+++ b/vendor/topthink/framework/tests/bootstrap.php
@@ -0,0 +1,3 @@
+<?php
+
+include __DIR__.'/../src/helper.php';
diff --git a/vendor/topthink/think-captcha/.gitignore b/vendor/topthink/think-captcha/.gitignore
new file mode 100755
index 0000000..85d49cb
--- /dev/null
+++ b/vendor/topthink/think-captcha/.gitignore
@@ -0,0 +1,3 @@
+/vendor/
+/composer.lock
+.idea
\ No newline at end of file
diff --git a/vendor/topthink/think-captcha/LICENSE b/vendor/topthink/think-captcha/LICENSE
new file mode 100755
index 0000000..835ce60
--- /dev/null
+++ b/vendor/topthink/think-captcha/LICENSE
@@ -0,0 +1,32 @@
+
+ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
+版权所有Copyright © 2006-2016 by ThinkPHP (http://thinkphp.cn)
+All rights reserved。
+ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
+
+Apache Licence是著名的非盈利开源组织Apache采用的协议。
+该协议和BSD类似,鼓励代码共享和尊重原作者的著作权,
+允许代码修改,再作为开源或商业软件发布。需要满足
+的条件:
+1. 需要给代码的用户一份Apache Licence ;
+2. 如果你修改了代码,需要在被修改的文件中说明;
+3. 在延伸的代码中(修改和有源代码衍生的代码中)需要
+带有原来代码中的协议,商标,专利声明和其他原来作者规
+定需要包含的说明;
+4. 如果再发布的产品中包含一个Notice文件,则在Notice文
+件中需要带有本协议内容。你可以在Notice中增加自己的
+许可,但不可以表现为对Apache Licence构成更改。
+具体的协议参考:http://www.apache.org/licenses/LICENSE-2.0
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/topthink/think-captcha/README.md b/vendor/topthink/think-captcha/README.md
new file mode 100755
index 0000000..cf1a06a
--- /dev/null
+++ b/vendor/topthink/think-captcha/README.md
@@ -0,0 +1,57 @@
+# think-captcha
+
+thinkphp6 验证码类库
+
+## 安装
+> composer require topthink/think-captcha
+
+
+
+## 使用
+
+### 在控制器中输出验证码
+
+在控制器的操作方法中使用
+
+~~~
+public function captcha($id = '')
+{
+	return captcha($id);
+}
+~~~
+然后注册对应的路由来输出验证码
+
+
+### 模板里输出验证码
+
+首先要在你应用的路由定义文件中,注册一个验证码路由规则。
+
+~~~
+\think\facade\Route::get('captcha/[:id]', "\\think\\captcha\\CaptchaController@index");
+~~~
+
+然后就可以在模板文件中使用
+~~~
+<div>{:captcha_img()}</div>
+~~~
+或者
+~~~
+<div><img src="{:captcha_src()}" alt="captcha" /></div>
+~~~
+> 上面两种的最终效果是一样的
+
+
+### 控制器里验证
+
+使用TP的内置验证功能即可
+~~~
+$this->validate($data,[
+    'captcha|验证码'=>'require|captcha'
+]);
+~~~
+或者手动验证
+~~~
+if(!captcha_check($captcha)){
+ //验证失败
+};
+~~~
\ No newline at end of file
diff --git a/vendor/topthink/think-captcha/assets/bgs/1.jpg b/vendor/topthink/think-captcha/assets/bgs/1.jpg
new file mode 100755
index 0000000..d417136
--- /dev/null
+++ b/vendor/topthink/think-captcha/assets/bgs/1.jpg
Binary files differ
diff --git a/vendor/topthink/think-captcha/assets/bgs/2.jpg b/vendor/topthink/think-captcha/assets/bgs/2.jpg
new file mode 100755
index 0000000..56640bd
--- /dev/null
+++ b/vendor/topthink/think-captcha/assets/bgs/2.jpg
Binary files differ
diff --git a/vendor/topthink/think-captcha/assets/bgs/3.jpg b/vendor/topthink/think-captcha/assets/bgs/3.jpg
new file mode 100755
index 0000000..83e5bd9
--- /dev/null
+++ b/vendor/topthink/think-captcha/assets/bgs/3.jpg
Binary files differ
diff --git a/vendor/topthink/think-captcha/assets/bgs/4.jpg b/vendor/topthink/think-captcha/assets/bgs/4.jpg
new file mode 100755
index 0000000..97a3721
--- /dev/null
+++ b/vendor/topthink/think-captcha/assets/bgs/4.jpg
Binary files differ
diff --git a/vendor/topthink/think-captcha/assets/bgs/5.jpg b/vendor/topthink/think-captcha/assets/bgs/5.jpg
new file mode 100755
index 0000000..220a17a
--- /dev/null
+++ b/vendor/topthink/think-captcha/assets/bgs/5.jpg
Binary files differ
diff --git a/vendor/topthink/think-captcha/assets/bgs/6.jpg b/vendor/topthink/think-captcha/assets/bgs/6.jpg
new file mode 100755
index 0000000..be53ea0
--- /dev/null
+++ b/vendor/topthink/think-captcha/assets/bgs/6.jpg
Binary files differ
diff --git a/vendor/topthink/think-captcha/assets/bgs/7.jpg b/vendor/topthink/think-captcha/assets/bgs/7.jpg
new file mode 100755
index 0000000..fbf537f
--- /dev/null
+++ b/vendor/topthink/think-captcha/assets/bgs/7.jpg
Binary files differ
diff --git a/vendor/topthink/think-captcha/assets/bgs/8.jpg b/vendor/topthink/think-captcha/assets/bgs/8.jpg
new file mode 100755
index 0000000..e10cf28
--- /dev/null
+++ b/vendor/topthink/think-captcha/assets/bgs/8.jpg
Binary files differ
diff --git a/vendor/topthink/think-captcha/assets/ttfs/1.ttf b/vendor/topthink/think-captcha/assets/ttfs/1.ttf
new file mode 100755
index 0000000..9eae6f2
--- /dev/null
+++ b/vendor/topthink/think-captcha/assets/ttfs/1.ttf
Binary files differ
diff --git a/vendor/topthink/think-captcha/assets/ttfs/2.ttf b/vendor/topthink/think-captcha/assets/ttfs/2.ttf
new file mode 100755
index 0000000..6386c6b
--- /dev/null
+++ b/vendor/topthink/think-captcha/assets/ttfs/2.ttf
Binary files differ
diff --git a/vendor/topthink/think-captcha/assets/ttfs/3.ttf b/vendor/topthink/think-captcha/assets/ttfs/3.ttf
new file mode 100755
index 0000000..678a491
--- /dev/null
+++ b/vendor/topthink/think-captcha/assets/ttfs/3.ttf
Binary files differ
diff --git a/vendor/topthink/think-captcha/assets/ttfs/4.ttf b/vendor/topthink/think-captcha/assets/ttfs/4.ttf
new file mode 100755
index 0000000..db43334
--- /dev/null
+++ b/vendor/topthink/think-captcha/assets/ttfs/4.ttf
Binary files differ
diff --git a/vendor/topthink/think-captcha/assets/ttfs/5.ttf b/vendor/topthink/think-captcha/assets/ttfs/5.ttf
new file mode 100755
index 0000000..8c082c8
--- /dev/null
+++ b/vendor/topthink/think-captcha/assets/ttfs/5.ttf
Binary files differ
diff --git a/vendor/topthink/think-captcha/assets/ttfs/6.ttf b/vendor/topthink/think-captcha/assets/ttfs/6.ttf
new file mode 100755
index 0000000..45a038b
--- /dev/null
+++ b/vendor/topthink/think-captcha/assets/ttfs/6.ttf
Binary files differ
diff --git a/vendor/topthink/think-captcha/assets/zhttfs/1.ttf b/vendor/topthink/think-captcha/assets/zhttfs/1.ttf
new file mode 100755
index 0000000..1c14f7f
--- /dev/null
+++ b/vendor/topthink/think-captcha/assets/zhttfs/1.ttf
Binary files differ
diff --git a/vendor/topthink/think-captcha/composer.json b/vendor/topthink/think-captcha/composer.json
new file mode 100755
index 0000000..8946486
--- /dev/null
+++ b/vendor/topthink/think-captcha/composer.json
@@ -0,0 +1,30 @@
+{
+    "name": "topthink/think-captcha",
+    "description": "captcha package for thinkphp",
+    "authors": [
+        {
+            "name": "yunwuxin",
+            "email": "448901948@qq.com"
+        }
+    ],
+    "license": "Apache-2.0",
+    "require": {
+        "topthink/framework": "^6.0.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "think\\captcha\\": "src/"
+        },
+        "files": [
+            "src/helper.php"
+        ]
+    },
+    "extra": {
+        "think": {
+            "services": [
+                "think\\captcha\\CaptchaService"
+            ]
+        }
+    },
+    "minimum-stability": "dev"
+}
diff --git a/vendor/topthink/think-captcha/src/Captcha.php b/vendor/topthink/think-captcha/src/Captcha.php
new file mode 100755
index 0000000..ea396ad
--- /dev/null
+++ b/vendor/topthink/think-captcha/src/Captcha.php
@@ -0,0 +1,339 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\captcha;
+
+use Exception;
+use think\Config;
+use think\Response;
+use think\Session;
+
+class Captcha
+{
+    private $im    = null; // 验证码图片实例
+    private $color = null; // 验证码字体颜色
+
+    /**
+     * @var Config|null
+     */
+    private $config = null;
+
+    /**
+     * @var Session|null
+     */
+    private $session = null;
+
+    // 验证码字符集合
+    protected $codeSet = '2345678abcdefhijkmnpqrstuvwxyzABCDEFGHJKLMNPQRTUVWXY';
+    // 验证码过期时间(s)
+    protected $expire = 1800;
+    // 使用中文验证码
+    protected $useZh = false;
+    // 中文验证码字符串
+    protected $zhSet = '们以我到他会作时要动国产的一是工就年阶义发成部民可出能方进在了不和有大这主中人上为来分生对于学下级地个用同行面说种过命度革而多子后自社加小机也经力线本电高量长党得实家定深法表着水理化争现所二起政三好十战无农使性前等反体合斗路图把结第里正新开论之物从当两些还天资事队批点育重其思与间内去因件日利相由压员气业代全组数果期导平各基或月毛然如应形想制心样干都向变关问比展那它最及外没看治提五解系林者米群头意只明四道马认次文通但条较克又公孔领军流入接席位情运器并飞原油放立题质指建区验活众很教决特此常石强极土少已根共直团统式转别造切九你取西持总料连任志观调七么山程百报更见必真保热委手改管处己将修支识病象几先老光专什六型具示复安带每东增则完风回南广劳轮科北打积车计给节做务被整联步类集号列温装即毫知轴研单色坚据速防史拉世设达尔场织历花受求传口断况采精金界品判参层止边清至万确究书术状厂须离再目海交权且儿青才证低越际八试规斯近注办布门铁需走议县兵固除般引齿千胜细影济白格效置推空配刀叶率述今选养德话查差半敌始片施响收华觉备名红续均药标记难存测士身紧液派准斤角降维板许破述技消底床田势端感往神便贺村构照容非搞亚磨族火段算适讲按值美态黄易彪服早班麦削信排台声该击素张密害侯草何树肥继右属市严径螺检左页抗苏显苦英快称坏移约巴材省黑武培著河帝仅针怎植京助升王眼她抓含苗副杂普谈围食射源例致酸旧却充足短划剂宣环落首尺波承粉践府鱼随考刻靠够满夫失包住促枝局菌杆周护岩师举曲春元超负砂封换太模贫减阳扬江析亩木言球朝医校古呢稻宋听唯输滑站另卫字鼓刚写刘微略范供阿块某功套友限项余倒卷创律雨让骨远帮初皮播优占死毒圈伟季训控激找叫云互跟裂粮粒母练塞钢顶策双留误础吸阻故寸盾晚丝女散焊功株亲院冷彻弹错散商视艺灭版烈零室轻血倍缺厘泵察绝富城冲喷壤简否柱李望盘磁雄似困巩益洲脱投送奴侧润盖挥距触星松送获兴独官混纪依未突架宽冬章湿偏纹吃执阀矿寨责熟稳夺硬价努翻奇甲预职评读背协损棉侵灰虽矛厚罗泥辟告卵箱掌氧恩爱停曾溶营终纲孟钱待尽俄缩沙退陈讨奋械载胞幼哪剥迫旋征槽倒握担仍呀鲜吧卡粗介钻逐弱脚怕盐末阴丰雾冠丙街莱贝辐肠付吉渗瑞惊顿挤秒悬姆烂森糖圣凹陶词迟蚕亿矩康遵牧遭幅园腔订香肉弟屋敏恢忘编印蜂急拿扩伤飞露核缘游振操央伍域甚迅辉异序免纸夜乡久隶缸夹念兰映沟乙吗儒杀汽磷艰晶插埃燃欢铁补咱芽永瓦倾阵碳演威附牙芽永瓦斜灌欧献顺猪洋腐请透司危括脉宜笑若尾束壮暴企菜穗楚汉愈绿拖牛份染既秋遍锻玉夏疗尖殖井费州访吹荣铜沿替滚客召旱悟刺脑措贯藏敢令隙炉壳硫煤迎铸粘探临薄旬善福纵择礼愿伏残雷延烟句纯渐耕跑泽慢栽鲁赤繁境潮横掉锥希池败船假亮谓托伙哲怀割摆贡呈劲财仪沉炼麻罪祖息车穿货销齐鼠抽画饲龙库守筑房歌寒喜哥洗蚀废纳腹乎录镜妇恶脂庄擦险赞钟摇典柄辩竹谷卖乱虚桥奥伯赶垂途额壁网截野遗静谋弄挂课镇妄盛耐援扎虑键归符庆聚绕摩忙舞遇索顾胶羊湖钉仁音迹碎伸灯避泛亡答勇频皇柳哈揭甘诺概宪浓岛袭谁洪谢炮浇斑讯懂灵蛋闭孩释乳巨徒私银伊景坦累匀霉杜乐勒隔弯绩招绍胡呼痛峰零柴簧午跳居尚丁秦稍追梁折耗碱殊岗挖氏刃剧堆赫荷胸衡勤膜篇登驻案刊秧缓凸役剪川雪链渔啦脸户洛孢勃盟买杨宗焦赛旗滤硅炭股坐蒸凝竟陷枪黎救冒暗洞犯筒您宋弧爆谬涂味津臂障褐陆啊健尊豆拔莫抵桑坡缝警挑污冰柬嘴啥饭塑寄赵喊垫丹渡耳刨虎笔稀昆浪萨茶滴浅拥穴覆伦娘吨浸袖珠雌妈紫戏塔锤震岁貌洁剖牢锋疑霸闪埔猛诉刷狠忽灾闹乔唐漏闻沈熔氯荒茎男凡抢像浆旁玻亦忠唱蒙予纷捕锁尤乘乌智淡允叛畜俘摸锈扫毕璃宝芯爷鉴秘净蒋钙肩腾枯抛轨堂拌爸循诱祝励肯酒绳穷塘燥泡袋朗喂铝软渠颗惯贸粪综墙趋彼届墨碍启逆卸航衣孙龄岭骗休借';
+    // 使用背景图片
+    protected $useImgBg = false;
+    // 验证码字体大小(px)
+    protected $fontSize = 25;
+    // 是否画混淆曲线
+    protected $useCurve = true;
+    // 是否添加杂点
+    protected $useNoise = true;
+    // 验证码图片高度
+    protected $imageH = 0;
+    // 验证码图片宽度
+    protected $imageW = 0;
+    // 验证码位数
+    protected $length = 5;
+    // 验证码字体,不设置随机获取
+    protected $fontttf = '';
+    // 背景颜色
+    protected $bg = [243, 251, 254];
+    //算术验证码
+    protected $math = false;
+
+    /**
+     * 架构方法 设置参数
+     * @access public
+     * @param Config  $config
+     * @param Session $session
+     */
+    public function __construct(Config $config, Session $session)
+    {
+        $this->config  = $config;
+        $this->session = $session;
+    }
+
+    /**
+     * 配置验证码
+     * @param string|null $config
+     */
+    protected function configure(string $config = null): void
+    {
+        if (is_null($config)) {
+            $config = $this->config->get('captcha', []);
+        } else {
+            $config = $this->config->get('captcha.' . $config, []);
+        }
+
+        foreach ($config as $key => $val) {
+            if (property_exists($this, $key)) {
+                $this->{$key} = $val;
+            }
+        }
+    }
+
+    /**
+     * 创建验证码
+     * @return array
+     * @throws Exception
+     */
+    protected function generate(): array
+    {
+        $bag = '';
+
+        if ($this->math) {
+            $this->useZh = false;
+
+            $x   = random_int(10, 30);
+            $y   = random_int(1, 9);
+            $bag = "{$x} + {$y} = ";
+            $key = $x + $y;
+            $key .= '';
+        } else {
+            if ($this->useZh) {
+                $characters = preg_split('/(?<!^)(?!$)/u', $this->zhSet);
+            } else {
+                $characters = str_split($this->codeSet);
+            }
+
+            for ($i = 0; $i < $this->length; $i++) {
+                $bag .= $characters[rand(0, count($characters) - 1)];
+            }
+
+            $key = mb_strtolower($bag, 'UTF-8');
+        }
+
+        $hash = password_hash($key, PASSWORD_BCRYPT, ['cost' => 10]);
+
+        $this->session->set('captcha', [
+            'key' => $hash,
+        ]);
+
+        return [
+            'value' => $bag,
+            'key'   => $hash,
+        ];
+    }
+
+    /**
+     * 验证验证码是否正确
+     * @access public
+     * @param string $code 用户验证码
+     * @return bool 用户验证码是否正确
+     */
+    public function check(string $code): bool
+    {
+        if (!$this->session->has('captcha')) {
+            return false;
+        }
+
+        $key = $this->session->get('captcha.key');
+
+        $code = mb_strtolower($code, 'UTF-8');
+
+        $res = password_verify($code, $key);
+
+        if ($res) {
+            $this->session->delete('captcha');
+        }
+
+        return $res;
+    }
+
+    /**
+     * 输出验证码并把验证码的值保存的session中
+     * @access public
+     * @param null|string $config
+     * @param bool        $api
+     * @return Response
+     */
+    public function create(string $config = null, bool $api = false): Response
+    {
+        $this->configure($config);
+
+        $generator = $this->generate();
+
+        // 图片宽(px)
+        $this->imageW || $this->imageW = $this->length * $this->fontSize * 1.5 + $this->length * $this->fontSize / 2;
+        // 图片高(px)
+        $this->imageH || $this->imageH = $this->fontSize * 2.5;
+        // 建立一幅 $this->imageW x $this->imageH 的图像
+        $this->im = imagecreate($this->imageW, $this->imageH);
+        // 设置背景
+        imagecolorallocate($this->im, $this->bg[0], $this->bg[1], $this->bg[2]);
+
+        // 验证码字体随机颜色
+        $this->color = imagecolorallocate($this->im, mt_rand(1, 150), mt_rand(1, 150), mt_rand(1, 150));
+
+        // 验证码使用随机字体
+        $ttfPath = __DIR__ . '/../assets/' . ($this->useZh ? 'zhttfs' : 'ttfs') . '/';
+
+        if (empty($this->fontttf)) {
+            $dir  = dir($ttfPath);
+            $ttfs = [];
+            while (false !== ($file = $dir->read())) {
+                if ('.' != $file[0] && substr($file, -4) == '.ttf') {
+                    $ttfs[] = $file;
+                }
+            }
+            $dir->close();
+            $this->fontttf = $ttfs[array_rand($ttfs)];
+        }
+
+        $fontttf = $ttfPath . $this->fontttf;
+
+        if ($this->useImgBg) {
+            $this->background();
+        }
+
+        if ($this->useNoise) {
+            // 绘杂点
+            $this->writeNoise();
+        }
+        if ($this->useCurve) {
+            // 绘干扰线
+            $this->writeCurve();
+        }
+
+        // 绘验证码
+        $text = $this->useZh ? preg_split('/(?<!^)(?!$)/u', $generator['value']) : str_split($generator['value']); // 验证码
+
+        foreach ($text as $index => $char) {
+
+            $x     = $this->fontSize * ($index + 1) * mt_rand(1.2, 1.6) * ($this->math ? 1 : 1.5);
+            $y     = $this->fontSize + mt_rand(10, 20);
+            $angle = $this->math ? 0 : mt_rand(-40, 40);
+
+            imagettftext($this->im, $this->fontSize, $angle, $x, $y, $this->color, $fontttf, $char);
+        }
+
+        ob_start();
+        // 输出图像
+        imagepng($this->im);
+        $content = ob_get_clean();
+        imagedestroy($this->im);
+
+        return response($content, 200, ['Content-Length' => strlen($content)])->contentType('image/png');
+    }
+
+    /**
+     * 画一条由两条连在一起构成的随机正弦函数曲线作干扰线(你可以改成更帅的曲线函数)
+     *
+     *      高中的数学公式咋都忘了涅,写出来
+     *        正弦型函数解析式:y=Asin(ωx+φ)+b
+     *      各常数值对函数图像的影响:
+     *        A:决定峰值(即纵向拉伸压缩的倍数)
+     *        b:表示波形在Y轴的位置关系或纵向移动距离(上加下减)
+     *        φ:决定波形与X轴位置关系或横向移动距离(左加右减)
+     *        ω:决定周期(最小正周期T=2π/∣ω∣)
+     *
+     */
+    protected function writeCurve(): void
+    {
+        $px = $py = 0;
+
+        // 曲线前部分
+        $A = mt_rand(1, $this->imageH / 2); // 振幅
+        $b = mt_rand(-$this->imageH / 4, $this->imageH / 4); // Y轴方向偏移量
+        $f = mt_rand(-$this->imageH / 4, $this->imageH / 4); // X轴方向偏移量
+        $T = mt_rand($this->imageH, $this->imageW * 2); // 周期
+        $w = (2 * M_PI) / $T;
+
+        $px1 = 0; // 曲线横坐标起始位置
+        $px2 = mt_rand($this->imageW / 2, $this->imageW * 0.8); // 曲线横坐标结束位置
+
+        for ($px = $px1; $px <= $px2; $px = $px + 1) {
+            if (0 != $w) {
+                $py = $A * sin($w * $px + $f) + $b + $this->imageH / 2; // y = Asin(ωx+φ) + b
+                $i  = (int) ($this->fontSize / 5);
+                while ($i > 0) {
+                    imagesetpixel($this->im, $px + $i, $py + $i, $this->color); // 这里(while)循环画像素点比imagettftext和imagestring用字体大小一次画出(不用这while循环)性能要好很多
+                    $i--;
+                }
+            }
+        }
+
+        // 曲线后部分
+        $A   = mt_rand(1, $this->imageH / 2); // 振幅
+        $f   = mt_rand(-$this->imageH / 4, $this->imageH / 4); // X轴方向偏移量
+        $T   = mt_rand($this->imageH, $this->imageW * 2); // 周期
+        $w   = (2 * M_PI) / $T;
+        $b   = $py - $A * sin($w * $px + $f) - $this->imageH / 2;
+        $px1 = $px2;
+        $px2 = $this->imageW;
+
+        for ($px = $px1; $px <= $px2; $px = $px + 1) {
+            if (0 != $w) {
+                $py = $A * sin($w * $px + $f) + $b + $this->imageH / 2; // y = Asin(ωx+φ) + b
+                $i  = (int) ($this->fontSize / 5);
+                while ($i > 0) {
+                    imagesetpixel($this->im, $px + $i, $py + $i, $this->color);
+                    $i--;
+                }
+            }
+        }
+    }
+
+    /**
+     * 画杂点
+     * 往图片上写不同颜色的字母或数字
+     */
+    protected function writeNoise(): void
+    {
+        $codeSet = '2345678abcdefhijkmnpqrstuvwxyz';
+        for ($i = 0; $i < 10; $i++) {
+            //杂点颜色
+            $noiseColor = imagecolorallocate($this->im, mt_rand(150, 225), mt_rand(150, 225), mt_rand(150, 225));
+            for ($j = 0; $j < 5; $j++) {
+                // 绘杂点
+                imagestring($this->im, 5, mt_rand(-10, $this->imageW), mt_rand(-10, $this->imageH), $codeSet[mt_rand(0, 29)], $noiseColor);
+            }
+        }
+    }
+
+    /**
+     * 绘制背景图片
+     * 注:如果验证码输出图片比较大,将占用比较多的系统资源
+     */
+    protected function background(): void
+    {
+        $path = __DIR__ . '/../assets/bgs/';
+        $dir  = dir($path);
+
+        $bgs = [];
+        while (false !== ($file = $dir->read())) {
+            if ('.' != $file[0] && substr($file, -4) == '.jpg') {
+                $bgs[] = $path . $file;
+            }
+        }
+        $dir->close();
+
+        $gb = $bgs[array_rand($bgs)];
+
+        list($width, $height) = @getimagesize($gb);
+        // Resample
+        $bgImage = @imagecreatefromjpeg($gb);
+        @imagecopyresampled($this->im, $bgImage, 0, 0, 0, 0, $this->imageW, $this->imageH, $width, $height);
+        @imagedestroy($bgImage);
+    }
+
+}
diff --git a/vendor/topthink/think-captcha/src/CaptchaController.php b/vendor/topthink/think-captcha/src/CaptchaController.php
new file mode 100755
index 0000000..2c3cf59
--- /dev/null
+++ b/vendor/topthink/think-captcha/src/CaptchaController.php
@@ -0,0 +1,20 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\captcha;
+
+class CaptchaController
+{
+    public function index(Captcha $captcha, $config = null)
+    {
+        return $captcha->create($config);
+    }
+}
diff --git a/vendor/topthink/think-captcha/src/CaptchaService.php b/vendor/topthink/think-captcha/src/CaptchaService.php
new file mode 100755
index 0000000..27f0ff0
--- /dev/null
+++ b/vendor/topthink/think-captcha/src/CaptchaService.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace think\captcha;
+
+use think\Route;
+use think\Service;
+use think\Validate;
+
+class CaptchaService extends Service
+{
+    public function boot(Route $route)
+    {
+        $route->get('captcha/[:config]', "\\think\\captcha\\CaptchaController@index");
+
+        Validate::maker(function ($validate) {
+            $validate->extend('captcha', function ($value) {
+                return captcha_check($value);
+            }, ':attribute错误!');
+        });
+    }
+}
diff --git a/vendor/topthink/think-captcha/src/facade/Captcha.php b/vendor/topthink/think-captcha/src/facade/Captcha.php
new file mode 100755
index 0000000..cd9f793
--- /dev/null
+++ b/vendor/topthink/think-captcha/src/facade/Captcha.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace think\captcha\facade;
+
+use think\Facade;
+
+/**
+ * Class Captcha
+ * @package think\captcha\facade
+ * @mixin \think\captcha\Captcha
+ */
+class Captcha extends Facade
+{
+    protected static function getFacadeClass()
+    {
+        return \think\captcha\Captcha::class;
+    }
+}
diff --git a/vendor/topthink/think-captcha/src/helper.php b/vendor/topthink/think-captcha/src/helper.php
new file mode 100755
index 0000000..e3c3ec2
--- /dev/null
+++ b/vendor/topthink/think-captcha/src/helper.php
@@ -0,0 +1,52 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+use think\captcha\facade\Captcha;
+use think\facade\Route;
+use think\Response;
+
+/**
+ * @param string $config
+ * @return \think\Response
+ */
+function captcha($config = null): Response
+{
+    return Captcha::create($config);
+}
+
+/**
+ * @param $config
+ * @return string
+ */
+function captcha_src($config = null): string
+{
+    return Route::buildUrl('/captcha' . ($config ? "/{$config}" : ''));
+}
+
+/**
+ * @param $id
+ * @return string
+ */
+function captcha_img($id = ''): string
+{
+    $src = captcha_src($id);
+
+    return "<img src='{$src}' alt='captcha' onclick='this.src=\"{$src}?s=\"+Math.random();' />";
+}
+
+/**
+ * @param string $value
+ * @return bool
+ */
+function captcha_check($value)
+{
+    return Captcha::check($value);
+}
diff --git a/vendor/topthink/think-factory/.gitignore b/vendor/topthink/think-factory/.gitignore
new file mode 100755
index 0000000..1ff7691
--- /dev/null
+++ b/vendor/topthink/think-factory/.gitignore
@@ -0,0 +1,4 @@
+# Created by .ignore support plugin (hsz.mobi)
+vendor
+composer.lock
+.idea
\ No newline at end of file
diff --git a/vendor/topthink/think-factory/LICENSE b/vendor/topthink/think-factory/LICENSE
new file mode 100755
index 0000000..8dada3e
--- /dev/null
+++ b/vendor/topthink/think-factory/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/vendor/topthink/think-factory/composer.json b/vendor/topthink/think-factory/composer.json
new file mode 100755
index 0000000..a764082
--- /dev/null
+++ b/vendor/topthink/think-factory/composer.json
@@ -0,0 +1,22 @@
+{
+    "name": "topthink/think-factory",
+    "type": "library",
+    "license": "Apache-2.0",
+    "authors": [
+        {
+            "name": "yunwuxin",
+            "email": "448901948@qq.com"
+        }
+    ],
+    "require": {
+        "topthink/think-helper": "^3.0.0",
+        "topthink/framework": "^6.0.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "think\\": "src"
+        }
+    },
+    "minimum-stability": "dev",
+    "prefer-stable": true
+}
diff --git a/vendor/topthink/think-factory/src/Factory.php b/vendor/topthink/think-factory/src/Factory.php
new file mode 100755
index 0000000..18d0a10
--- /dev/null
+++ b/vendor/topthink/think-factory/src/Factory.php
@@ -0,0 +1,115 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+use InvalidArgumentException;
+use think\helper\Str;
+
+abstract class Factory
+{
+    protected $app;
+
+    /**
+     * 驱动
+     * @var array
+     */
+    protected $drivers = [];
+
+    /**
+     * 驱动的命名空间
+     * @var string
+     */
+    protected $namespace = "";
+
+    public function __construct(App $app)
+    {
+        $this->app = $app;
+    }
+
+    /**
+     * 获取驱动实例
+     * @param null $driver
+     * @return mixed
+     */
+    public function driver($driver = null)
+    {
+        $driver = $driver ?: $this->getDefaultDriver();
+
+        if (is_null($driver)) {
+            throw new InvalidArgumentException(sprintf(
+                'Unable to resolve NULL driver for [%s].', static::class
+            ));
+        }
+
+        if (!isset($this->drivers[$driver])) {
+            $this->drivers[$driver] = $this->createDriver($driver);
+        }
+
+        return $this->drivers[$driver];
+    }
+
+    /**
+     * 获取驱动类
+     * @param $driver
+     * @return string
+     */
+    protected function resolveClass($driver)
+    {
+        if ($this->namespace || false !== strpos($driver, '\\')) {
+            $class = false !== strpos($driver, '\\') ? $driver : $this->namespace . Str::studly($driver);
+
+            if (class_exists($class)) {
+                return $class;
+            }
+        }
+
+        throw new InvalidArgumentException("Driver [$driver] not supported.");
+    }
+
+    /**
+     * 创建驱动
+     *
+     * @param string $driver
+     * @return mixed
+     *
+     */
+    protected function createDriver($driver)
+    {
+        $method = 'create' . Str::studly($driver) . 'Driver';
+
+        if (method_exists($this, $method)) {
+            return $this->$method();
+        }
+
+        $class = $this->resolveClass($driver);
+
+        return $this->app->make($class);
+    }
+
+    /**
+     * 默认驱动
+     * @return string
+     */
+    abstract public function getDefaultDriver();
+
+    /**
+     * 动态调用
+     * @param string $method
+     * @param array  $parameters
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        return $this->driver()->$method(...$parameters);
+    }
+}
diff --git a/vendor/topthink/think-filesystem/.gitignore b/vendor/topthink/think-filesystem/.gitignore
new file mode 100755
index 0000000..25ad74d
--- /dev/null
+++ b/vendor/topthink/think-filesystem/.gitignore
@@ -0,0 +1,3 @@
+composer.lock
+/.idea
+/vendor
diff --git a/vendor/topthink/think-filesystem/README.md b/vendor/topthink/think-filesystem/README.md
new file mode 100755
index 0000000..90977dd
--- /dev/null
+++ b/vendor/topthink/think-filesystem/README.md
@@ -0,0 +1,5 @@
+# think-filesystem for ThinkPHP6.1
+
+## 安装
+
+> composer require topthink/think-filesystem
diff --git a/vendor/topthink/think-filesystem/composer.json b/vendor/topthink/think-filesystem/composer.json
new file mode 100755
index 0000000..8c4f508
--- /dev/null
+++ b/vendor/topthink/think-filesystem/composer.json
@@ -0,0 +1,33 @@
+{
+    "name": "topthink/think-filesystem",
+    "description": "The ThinkPHP6.1 Filesystem Package",
+    "type": "library",
+    "license": "Apache-2.0",
+    "authors": [
+        {
+            "name": "yunwuxin",
+            "email": "448901948@qq.com"
+        }
+    ],
+    "autoload": {
+        "psr-4": {
+            "think\\": "src"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "think\\tests\\": "tests/"
+        }
+    },
+    "require": {
+        "topthink/framework": "^6.1|^8.0",
+        "league/flysystem": "^2.0"
+    },
+    "require-dev": {
+        "mikey179/vfsstream": "^1.6",
+        "mockery/mockery": "^1.2",
+        "phpunit/phpunit": "^8.0"
+    },
+    "minimum-stability": "dev",
+    "prefer-stable": true
+}
diff --git a/vendor/topthink/think-filesystem/phpunit.xml.dist b/vendor/topthink/think-filesystem/phpunit.xml.dist
new file mode 100755
index 0000000..e20a133
--- /dev/null
+++ b/vendor/topthink/think-filesystem/phpunit.xml.dist
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit backupGlobals="false"
+         backupStaticAttributes="false"
+         beStrictAboutTestsThatDoNotTestAnything="false"
+         bootstrap="tests/bootstrap.php"
+         colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         processIsolation="false"
+         stopOnError="false"
+         stopOnFailure="false"
+         verbose="true"
+>
+    <testsuites>
+        <testsuite name="ThinkPHP Test Suite">
+            <directory suffix="Test.php">./tests</directory>
+        </testsuite>
+    </testsuites>
+    <filter>
+        <whitelist processUncoveredFilesFromWhitelist="true">
+            <directory suffix=".php">./src/think</directory>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/vendor/topthink/think-filesystem/src/Filesystem.php b/vendor/topthink/think-filesystem/src/Filesystem.php
new file mode 100755
index 0000000..0aee929
--- /dev/null
+++ b/vendor/topthink/think-filesystem/src/Filesystem.php
@@ -0,0 +1,89 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+use InvalidArgumentException;
+use think\filesystem\Driver;
+use think\filesystem\driver\Local;
+use think\helper\Arr;
+
+/**
+ * Class Filesystem
+ * @package think
+ * @mixin Driver
+ * @mixin Local
+ */
+class Filesystem extends Manager
+{
+    protected $namespace = '\\think\\filesystem\\driver\\';
+
+    /**
+     * @param null|string $name
+     * @return Driver
+     */
+    public function disk(string $name = null): Driver
+    {
+        return $this->driver($name);
+    }
+
+    protected function resolveType(string $name)
+    {
+        return $this->getDiskConfig($name, 'type', 'local');
+    }
+
+    protected function resolveConfig(string $name)
+    {
+        return $this->getDiskConfig($name);
+    }
+
+    /**
+     * 获取缓存配置
+     * @access public
+     * @param null|string $name    名称
+     * @param mixed       $default 默认值
+     * @return mixed
+     */
+    public function getConfig(string $name = null, $default = null)
+    {
+        if (!is_null($name)) {
+            return $this->app->config->get('filesystem.' . $name, $default);
+        }
+
+        return $this->app->config->get('filesystem');
+    }
+
+    /**
+     * 获取磁盘配置
+     * @param string $disk
+     * @param null   $name
+     * @param null   $default
+     * @return array
+     */
+    public function getDiskConfig($disk, $name = null, $default = null)
+    {
+        if ($config = $this->getConfig("disks.{$disk}")) {
+            return Arr::get($config, $name, $default);
+        }
+
+        throw new InvalidArgumentException("Disk [$disk] not found.");
+    }
+
+    /**
+     * 默认驱动
+     * @return string|null
+     */
+    public function getDefaultDriver()
+    {
+        return $this->getConfig('default');
+    }
+}
diff --git a/vendor/topthink/think-filesystem/src/facade/Filesystem.php b/vendor/topthink/think-filesystem/src/facade/Filesystem.php
new file mode 100755
index 0000000..0e32c2c
--- /dev/null
+++ b/vendor/topthink/think-filesystem/src/facade/Filesystem.php
@@ -0,0 +1,33 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\facade;
+
+use think\Facade;
+use think\filesystem\Driver;
+
+/**
+ * Class Filesystem
+ * @package think\facade
+ * @mixin \think\Filesystem
+ * @method static Driver disk(string $name = null) , null|string
+ * @method static mixed getConfig(null|string $name = null, mixed $default = null) 获取缓存配置
+ * @method static array getDiskConfig(string $disk, null $name = null, null $default = null) 获取磁盘配置
+ * @method static string|null getDefaultDriver() 默认驱动
+ */
+class Filesystem extends Facade
+{
+    protected static function getFacadeClass()
+    {
+        return \think\Filesystem::class;
+    }
+}
diff --git a/vendor/topthink/think-filesystem/src/filesystem/Driver.php b/vendor/topthink/think-filesystem/src/filesystem/Driver.php
new file mode 100755
index 0000000..e804141
--- /dev/null
+++ b/vendor/topthink/think-filesystem/src/filesystem/Driver.php
@@ -0,0 +1,130 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\filesystem;
+
+use League\Flysystem\Filesystem;
+use League\Flysystem\FilesystemAdapter;
+use League\Flysystem\UnableToSetVisibility;
+use League\Flysystem\UnableToWriteFile;
+use RuntimeException;
+use think\Cache;
+use think\File;
+
+/**
+ * Class Driver
+ * @package think\filesystem
+ * @mixin Filesystem
+ */
+abstract class Driver
+{
+
+    /** @var Cache */
+    protected $cache;
+
+    /** @var Filesystem */
+    protected $filesystem;
+
+    /**
+     * 配置参数
+     * @var array
+     */
+    protected $config = [];
+
+    public function __construct(Cache $cache, array $config)
+    {
+        $this->cache  = $cache;
+        $this->config = array_merge($this->config, $config);
+
+        $adapter          = $this->createAdapter();
+        $this->filesystem = $this->createFilesystem($adapter);
+    }
+
+    abstract protected function createAdapter(): FilesystemAdapter;
+
+    protected function createFilesystem(FilesystemAdapter $adapter): Filesystem
+    {
+        $config = array_intersect_key($this->config, array_flip(['visibility', 'disable_asserts', 'url']));
+
+        return new Filesystem($adapter, $config);
+    }
+
+    /**
+     * 获取文件完整路径
+     * @param string $path
+     * @return string
+     */
+    public function path(string $path): string
+    {
+        return $path;
+    }
+
+    protected function concatPathToUrl($url, $path)
+    {
+        return rtrim($url, '/') . '/' . ltrim($path, '/');
+    }
+
+    public function url(string $path): string
+    {
+        throw new RuntimeException('This driver does not support retrieving URLs.');
+    }
+
+    /**
+     * 保存文件
+     * @param string $path 路径
+     * @param File $file 文件
+     * @param null|string|\Closure $rule 文件名规则
+     * @param array $options 参数
+     * @return bool|string
+     */
+    public function putFile(string $path, File $file, $rule = null, array $options = [])
+    {
+        return $this->putFileAs($path, $file, $file->hashName($rule), $options);
+    }
+
+    /**
+     * 指定文件名保存文件
+     * @param string $path 路径
+     * @param File $file 文件
+     * @param string $name 文件名
+     * @param array $options 参数
+     * @return bool|string
+     */
+    public function putFileAs(string $path, File $file, string $name, array $options = [])
+    {
+        $stream = fopen($file->getRealPath(), 'r');
+        $path   = trim($path . '/' . $name, '/');
+
+        $result = $this->put($path, $stream, $options);
+
+        if (is_resource($stream)) {
+            fclose($stream);
+        }
+
+        return $result ? $path : false;
+    }
+
+    protected function put(string $path, $contents, array $options = [])
+    {
+        try {
+            $this->writeStream($path, $contents, $options);
+        } catch (UnableToWriteFile|UnableToSetVisibility $e) {
+            return false;
+        }
+        return true;
+    }
+
+    public function __call($method, $parameters)
+    {
+        return $this->filesystem->$method(...$parameters);
+    }
+}
diff --git a/vendor/topthink/think-filesystem/src/filesystem/driver/Local.php b/vendor/topthink/think-filesystem/src/filesystem/driver/Local.php
new file mode 100755
index 0000000..3b1b64d
--- /dev/null
+++ b/vendor/topthink/think-filesystem/src/filesystem/driver/Local.php
@@ -0,0 +1,98 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\filesystem\driver;
+
+use League\Flysystem\FilesystemAdapter;
+use League\Flysystem\Local\LocalFilesystemAdapter;
+use League\Flysystem\PathNormalizer;
+use League\Flysystem\PathPrefixer;
+use League\Flysystem\UnixVisibility\PortableVisibilityConverter;
+use League\Flysystem\Visibility;
+use League\Flysystem\WhitespacePathNormalizer;
+use think\filesystem\Driver;
+
+class Local extends Driver
+{
+    /**
+     * 配置参数
+     * @var array
+     */
+    protected $config = [
+        'root' => '',
+    ];
+
+    /**
+     * @var PathPrefixer
+     */
+    protected $prefixer;
+
+    /**
+     * @var PathNormalizer
+     */
+    protected $normalizer;
+
+    protected function createAdapter(): FilesystemAdapter
+    {
+        $visibility = PortableVisibilityConverter::fromArray(
+            $this->config['permissions'] ?? [],
+            $this->config['visibility'] ?? Visibility::PRIVATE
+        );
+
+        $links = ($this->config['links'] ?? null) === 'skip'
+            ? LocalFilesystemAdapter::SKIP_LINKS
+            : LocalFilesystemAdapter::DISALLOW_LINKS;
+
+        return new LocalFilesystemAdapter(
+            $this->config['root'],
+            $visibility,
+            $this->config['lock'] ?? LOCK_EX,
+            $links
+        );
+    }
+
+    protected function prefixer()
+    {
+        if (!$this->prefixer) {
+            $this->prefixer = new PathPrefixer($this->config['root'], DIRECTORY_SEPARATOR);
+        }
+        return $this->prefixer;
+    }
+
+    protected function normalizer()
+    {
+        if (!$this->normalizer) {
+            $this->normalizer = new WhitespacePathNormalizer();
+        }
+        return $this->normalizer;
+    }
+
+    /**
+     * 获取文件访问地址
+     * @param string $path 文件路径
+     * @return string
+     */
+    public function url(string $path): string
+    {
+        $path = $this->normalizer()->normalizePath($path);
+
+        if (isset($this->config['url'])) {
+            return $this->concatPathToUrl($this->config['url'], $path);
+        }
+        return parent::url($path);
+    }
+
+    public function path(string $path): string
+    {
+        return $this->prefixer()->prefixPath($path);
+    }
+}
diff --git a/vendor/topthink/think-filesystem/tests/FilesystemTest.php b/vendor/topthink/think-filesystem/tests/FilesystemTest.php
new file mode 100755
index 0000000..cc9d94e
--- /dev/null
+++ b/vendor/topthink/think-filesystem/tests/FilesystemTest.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace think\tests;
+
+use Mockery as m;
+use Mockery\MockInterface;
+use org\bovigo\vfs\vfsStream;
+use org\bovigo\vfs\vfsStreamDirectory;
+use PHPUnit\Framework\TestCase;
+use think\App;
+use think\Config;
+use think\Container;
+use think\Filesystem;
+use think\filesystem\driver\Local;
+
+class FilesystemTest extends TestCase
+{
+    /** @var App|MockInterface */
+    protected $app;
+
+    /** @var Filesystem */
+    protected $filesystem;
+
+    /** @var Config|MockInterface */
+    protected $config;
+
+    /** @var vfsStreamDirectory */
+    protected $root;
+
+    protected function setUp(): void
+    {
+        $this->app = m::mock(App::class)->makePartial();
+        Container::setInstance($this->app);
+        $this->app->shouldReceive('make')->with(App::class)->andReturn($this->app);
+        $this->config = m::mock(Config::class);
+        $this->config->shouldReceive('get')->with('filesystem.default', null)->andReturn('local');
+        $this->app->shouldReceive('get')->with('config')->andReturn($this->config);
+        $this->filesystem = new Filesystem($this->app);
+
+        $this->root = vfsStream::setup('rootDir');
+    }
+
+    protected function tearDown(): void
+    {
+        m::close();
+    }
+
+    public function testDisk()
+    {
+        $this->config->shouldReceive('get')->with('filesystem.disks.local', null)->andReturn([
+            'type' => 'local',
+            'root' => $this->root->url(),
+        ]);
+
+        $this->config->shouldReceive('get')->with('filesystem.disks.foo', null)->andReturn([
+            'type' => 'local',
+            'root' => $this->root->url(),
+        ]);
+
+        $this->assertInstanceOf(Local::class, $this->filesystem->disk());
+
+        $this->assertInstanceOf(Local::class, $this->filesystem->disk('foo'));
+    }
+
+}
+
diff --git a/vendor/topthink/think-filesystem/tests/bootstrap.php b/vendor/topthink/think-filesystem/tests/bootstrap.php
new file mode 100755
index 0000000..a4abe2d
--- /dev/null
+++ b/vendor/topthink/think-filesystem/tests/bootstrap.php
@@ -0,0 +1,2 @@
+<?php
+
diff --git a/vendor/topthink/think-helper/.gitignore b/vendor/topthink/think-helper/.gitignore
new file mode 100755
index 0000000..d851bdb
--- /dev/null
+++ b/vendor/topthink/think-helper/.gitignore
@@ -0,0 +1,3 @@
+/vendor/
+/.idea/
+composer.lock
\ No newline at end of file
diff --git a/vendor/topthink/think-helper/LICENSE b/vendor/topthink/think-helper/LICENSE
new file mode 100755
index 0000000..8dada3e
--- /dev/null
+++ b/vendor/topthink/think-helper/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/vendor/topthink/think-helper/README.md b/vendor/topthink/think-helper/README.md
new file mode 100755
index 0000000..7baf8f7
--- /dev/null
+++ b/vendor/topthink/think-helper/README.md
@@ -0,0 +1,33 @@
+# thinkphp6 常用的一些扩展类库
+
+基于PHP7.1+
+
+> 以下类库都在`\\think\\helper`命名空间下
+
+## Str
+
+> 字符串操作
+
+```
+// 检查字符串中是否包含某些字符串
+Str::contains($haystack, $needles)
+
+// 检查字符串是否以某些字符串结尾
+Str::endsWith($haystack, $needles)
+
+// 获取指定长度的随机字母数字组合的字符串
+Str::random($length = 16)
+
+// 字符串转小写
+Str::lower($value)
+
+// 字符串转大写
+Str::upper($value)
+
+// 获取字符串的长度
+Str::length($value)
+
+// 截取字符串
+Str::substr($string, $start, $length = null)
+
+```
\ No newline at end of file
diff --git a/vendor/topthink/think-helper/composer.json b/vendor/topthink/think-helper/composer.json
new file mode 100755
index 0000000..b68c43b
--- /dev/null
+++ b/vendor/topthink/think-helper/composer.json
@@ -0,0 +1,22 @@
+{
+    "name": "topthink/think-helper",
+    "description": "The ThinkPHP6 Helper Package",
+    "license": "Apache-2.0",
+    "authors": [
+        {
+            "name": "yunwuxin",
+            "email": "448901948@qq.com"
+        }
+    ],
+    "require": {
+        "php": ">=7.1.0"
+    },    
+    "autoload": {
+        "psr-4": {
+            "think\\": "src"
+        },
+        "files": [
+            "src/helper.php"
+        ]
+    }
+}
diff --git a/vendor/topthink/think-helper/src/Collection.php b/vendor/topthink/think-helper/src/Collection.php
new file mode 100755
index 0000000..f3d0a83
--- /dev/null
+++ b/vendor/topthink/think-helper/src/Collection.php
@@ -0,0 +1,651 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: zhangyajun <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+use ArrayAccess;
+use ArrayIterator;
+use Countable;
+use IteratorAggregate;
+use JsonSerializable;
+use think\contract\Arrayable;
+use think\contract\Jsonable;
+use think\helper\Arr;
+
+/**
+ * 数据集管理类
+ */
+class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable, Arrayable, Jsonable
+{
+    /**
+     * 数据集数据
+     * @var array
+     */
+    protected $items = [];
+
+    public function __construct($items = [])
+    {
+        $this->items = $this->convertToArray($items);
+    }
+
+    public static function make($items = [])
+    {
+        return new static($items);
+    }
+
+    /**
+     * 是否为空
+     * @access public
+     * @return bool
+     */
+    public function isEmpty(): bool
+    {
+        return empty($this->items);
+    }
+
+    public function toArray(): array
+    {
+        return array_map(function ($value) {
+            return $value instanceof Arrayable ? $value->toArray() : $value;
+        }, $this->items);
+    }
+
+    public function all(): array
+    {
+        return $this->items;
+    }
+
+    /**
+     * 合并数组
+     *
+     * @access public
+     * @param mixed $items 数据
+     * @return static
+     */
+    public function merge($items)
+    {
+        return new static(array_merge($this->items, $this->convertToArray($items)));
+    }
+
+    /**
+     * 按指定键整理数据
+     *
+     * @access public
+     * @param mixed  $items    数据
+     * @param string $indexKey 键名
+     * @return array
+     */
+    public function dictionary($items = null, string &$indexKey = null)
+    {
+        if ($items instanceof self) {
+            $items = $items->all();
+        }
+
+        $items = is_null($items) ? $this->items : $items;
+
+        if ($items && empty($indexKey)) {
+            $indexKey = is_array($items[0]) ? 'id' : $items[0]->getPk();
+        }
+
+        if (isset($indexKey) && is_string($indexKey)) {
+            return array_column($items, null, $indexKey);
+        }
+
+        return $items;
+    }
+
+    /**
+     * 比较数组,返回差集
+     *
+     * @access public
+     * @param mixed  $items    数据
+     * @param string $indexKey 指定比较的键名
+     * @return static
+     */
+    public function diff($items, string $indexKey = null)
+    {
+        if ($this->isEmpty() || is_scalar($this->items[0])) {
+            return new static(array_diff($this->items, $this->convertToArray($items)));
+        }
+
+        $diff       = [];
+        $dictionary = $this->dictionary($items, $indexKey);
+
+        if (is_string($indexKey)) {
+            foreach ($this->items as $item) {
+                if (!isset($dictionary[$item[$indexKey]])) {
+                    $diff[] = $item;
+                }
+            }
+        }
+
+        return new static($diff);
+    }
+
+    /**
+     * 比较数组,返回交集
+     *
+     * @access public
+     * @param mixed  $items    数据
+     * @param string $indexKey 指定比较的键名
+     * @return static
+     */
+    public function intersect($items, string $indexKey = null)
+    {
+        if ($this->isEmpty() || is_scalar($this->items[0])) {
+            return new static(array_diff($this->items, $this->convertToArray($items)));
+        }
+
+        $intersect  = [];
+        $dictionary = $this->dictionary($items, $indexKey);
+
+        if (is_string($indexKey)) {
+            foreach ($this->items as $item) {
+                if (isset($dictionary[$item[$indexKey]])) {
+                    $intersect[] = $item;
+                }
+            }
+        }
+
+        return new static($intersect);
+    }
+
+    /**
+     * 交换数组中的键和值
+     *
+     * @access public
+     * @return static
+     */
+    public function flip()
+    {
+        return new static(array_flip($this->items));
+    }
+
+    /**
+     * 返回数组中所有的键名
+     *
+     * @access public
+     * @return static
+     */
+    public function keys()
+    {
+        return new static(array_keys($this->items));
+    }
+
+    /**
+     * 返回数组中所有的值组成的新 Collection 实例
+     * @access public
+     * @return static
+     */
+    public function values()
+    {
+        return new static(array_values($this->items));
+    }
+
+    /**
+     * 删除数组的最后一个元素(出栈)
+     *
+     * @access public
+     * @return mixed
+     */
+    public function pop()
+    {
+        return array_pop($this->items);
+    }
+
+    /**
+     * 通过使用用户自定义函数,以字符串返回数组
+     *
+     * @access public
+     * @param callable $callback 调用方法
+     * @param mixed    $initial
+     * @return mixed
+     */
+    public function reduce(callable $callback, $initial = null)
+    {
+        return array_reduce($this->items, $callback, $initial);
+    }
+
+    /**
+     * 以相反的顺序返回数组。
+     *
+     * @access public
+     * @return static
+     */
+    public function reverse()
+    {
+        return new static(array_reverse($this->items));
+    }
+
+    /**
+     * 删除数组中首个元素,并返回被删除元素的值
+     *
+     * @access public
+     * @return mixed
+     */
+    public function shift()
+    {
+        return array_shift($this->items);
+    }
+
+    /**
+     * 在数组结尾插入一个元素
+     * @access public
+     * @param mixed  $value 元素
+     * @param string $key   KEY
+     * @return void
+     */
+    public function push($value, string $key = null): void
+    {
+        if (is_null($key)) {
+            $this->items[] = $value;
+        } else {
+            $this->items[$key] = $value;
+        }
+    }
+
+    /**
+     * 把一个数组分割为新的数组块.
+     *
+     * @access public
+     * @param int  $size 块大小
+     * @param bool $preserveKeys
+     * @return static
+     */
+    public function chunk(int $size, bool $preserveKeys = false)
+    {
+        $chunks = [];
+
+        foreach (array_chunk($this->items, $size, $preserveKeys) as $chunk) {
+            $chunks[] = new static($chunk);
+        }
+
+        return new static($chunks);
+    }
+
+    /**
+     * 在数组开头插入一个元素
+     * @access public
+     * @param mixed  $value 元素
+     * @param string $key   KEY
+     * @return void
+     */
+    public function unshift($value, string $key = null): void
+    {
+        if (is_null($key)) {
+            array_unshift($this->items, $value);
+        } else {
+            $this->items = [$key => $value] + $this->items;
+        }
+    }
+
+    /**
+     * 给每个元素执行个回调
+     *
+     * @access public
+     * @param callable $callback 回调
+     * @return $this
+     */
+    public function each(callable $callback)
+    {
+        foreach ($this->items as $key => $item) {
+            $result = $callback($item, $key);
+
+            if (false === $result) {
+                break;
+            } elseif (!is_object($item)) {
+                $this->items[$key] = $result;
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * 用回调函数处理数组中的元素
+     * @access public
+     * @param callable|null $callback 回调
+     * @return static
+     */
+    public function map(callable $callback)
+    {
+        return new static(array_map($callback, $this->items));
+    }
+
+    /**
+     * 用回调函数过滤数组中的元素
+     * @access public
+     * @param callable|null $callback 回调
+     * @return static
+     */
+    public function filter(callable $callback = null)
+    {
+        if ($callback) {
+            return new static(array_filter($this->items, $callback));
+        }
+
+        return new static(array_filter($this->items));
+    }
+
+    /**
+     * 根据字段条件过滤数组中的元素
+     * @access public
+     * @param string $field    字段名
+     * @param mixed  $operator 操作符
+     * @param mixed  $value    数据
+     * @return static
+     */
+    public function where(string $field, $operator, $value = null)
+    {
+        if (is_null($value)) {
+            $value    = $operator;
+            $operator = '=';
+        }
+
+        return $this->filter(function ($data) use ($field, $operator, $value) {
+            if (strpos($field, '.')) {
+                list($field, $relation) = explode('.', $field);
+
+                $result = $data[$field][$relation] ?? null;
+            } else {
+                $result = $data[$field] ?? null;
+            }
+
+            switch (strtolower($operator)) {
+                case '===':
+                    return $result === $value;
+                case '!==':
+                    return $result !== $value;
+                case '!=':
+                case '<>':
+                    return $result != $value;
+                case '>':
+                    return $result > $value;
+                case '>=':
+                    return $result >= $value;
+                case '<':
+                    return $result < $value;
+                case '<=':
+                    return $result <= $value;
+                case 'like':
+                    return is_string($result) && false !== strpos($result, $value);
+                case 'not like':
+                    return is_string($result) && false === strpos($result, $value);
+                case 'in':
+                    return is_scalar($result) && in_array($result, $value, true);
+                case 'not in':
+                    return is_scalar($result) && !in_array($result, $value, true);
+                case 'between':
+                    list($min, $max) = is_string($value) ? explode(',', $value) : $value;
+                    return is_scalar($result) && $result >= $min && $result <= $max;
+                case 'not between':
+                    list($min, $max) = is_string($value) ? explode(',', $value) : $value;
+                    return is_scalar($result) && $result > $max || $result < $min;
+                case '==':
+                case '=':
+                default:
+                    return $result == $value;
+            }
+        });
+    }
+
+    /**
+     * LIKE过滤
+     * @access public
+     * @param string $field 字段名
+     * @param string $value 数据
+     * @return static
+     */
+    public function whereLike(string $field, string $value)
+    {
+        return $this->where($field, 'like', $value);
+    }
+
+    /**
+     * NOT LIKE过滤
+     * @access public
+     * @param string $field 字段名
+     * @param string $value 数据
+     * @return static
+     */
+    public function whereNotLike(string $field, string $value)
+    {
+        return $this->where($field, 'not like', $value);
+    }
+
+    /**
+     * IN过滤
+     * @access public
+     * @param string $field 字段名
+     * @param array  $value 数据
+     * @return static
+     */
+    public function whereIn(string $field, array $value)
+    {
+        return $this->where($field, 'in', $value);
+    }
+
+    /**
+     * NOT IN过滤
+     * @access public
+     * @param string $field 字段名
+     * @param array  $value 数据
+     * @return static
+     */
+    public function whereNotIn(string $field, array $value)
+    {
+        return $this->where($field, 'not in', $value);
+    }
+
+    /**
+     * BETWEEN 过滤
+     * @access public
+     * @param string $field 字段名
+     * @param mixed  $value 数据
+     * @return static
+     */
+    public function whereBetween(string $field, $value)
+    {
+        return $this->where($field, 'between', $value);
+    }
+
+    /**
+     * NOT BETWEEN 过滤
+     * @access public
+     * @param string $field 字段名
+     * @param mixed  $value 数据
+     * @return static
+     */
+    public function whereNotBetween(string $field, $value)
+    {
+        return $this->where($field, 'not between', $value);
+    }
+
+    /**
+     * 返回数据中指定的一列
+     * @access public
+     * @param string $columnKey 键名
+     * @param string $indexKey  作为索引值的列
+     * @return array
+     */
+    public function column(string $columnKey, string $indexKey = null)
+    {
+        return array_column($this->items, $columnKey, $indexKey);
+    }
+
+    /**
+     * 对数组排序
+     *
+     * @access public
+     * @param callable|null $callback 回调
+     * @return static
+     */
+    public function sort(callable $callback = null)
+    {
+        $items = $this->items;
+
+        $callback = $callback ?: function ($a, $b) {
+            return $a == $b ? 0 : (($a < $b) ? -1 : 1);
+        };
+
+        uasort($items, $callback);
+
+        return new static($items);
+    }
+
+    /**
+     * 指定字段排序
+     * @access public
+     * @param string $field 排序字段
+     * @param string $order 排序
+     * @return $this
+     */
+    public function order(string $field, string $order = null)
+    {
+        return $this->sort(function ($a, $b) use ($field, $order) {
+            $fieldA = $a[$field] ?? null;
+            $fieldB = $b[$field] ?? null;
+
+            return 'desc' == strtolower($order) ? strcmp($fieldB, $fieldA) : strcmp($fieldA, $fieldB);
+        });
+    }
+
+    /**
+     * 将数组打乱
+     *
+     * @access public
+     * @return static
+     */
+    public function shuffle()
+    {
+        $items = $this->items;
+
+        shuffle($items);
+
+        return new static($items);
+    }
+
+    /**
+     * 获取最后一个单元数据
+     *
+     * @access public
+     * @param callable|null $callback
+     * @param null          $default
+     * @return mixed
+     */
+    public function first(callable $callback = null, $default = null)
+    {
+        return Arr::first($this->items, $callback, $default);
+    }
+
+    /**
+     * 获取第一个单元数据
+     *
+     * @access public
+     * @param callable|null $callback
+     * @param null          $default
+     * @return mixed
+     */
+    public function last(callable $callback = null, $default = null)
+    {
+        return Arr::last($this->items, $callback, $default);
+    }
+
+    /**
+     * 截取数组
+     *
+     * @access public
+     * @param int  $offset       起始位置
+     * @param int  $length       截取长度
+     * @param bool $preserveKeys preserveKeys
+     * @return static
+     */
+    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
+    {
+        return new static(array_slice($this->items, $offset, $length, $preserveKeys));
+    }
+
+    // ArrayAccess
+    public function offsetExists($offset)
+    {
+        return array_key_exists($offset, $this->items);
+    }
+
+    public function offsetGet($offset)
+    {
+        return $this->items[$offset];
+    }
+
+    public function offsetSet($offset, $value)
+    {
+        if (is_null($offset)) {
+            $this->items[] = $value;
+        } else {
+            $this->items[$offset] = $value;
+        }
+    }
+
+    public function offsetUnset($offset)
+    {
+        unset($this->items[$offset]);
+    }
+
+    //Countable
+    public function count()
+    {
+        return count($this->items);
+    }
+
+    //IteratorAggregate
+    public function getIterator()
+    {
+        return new ArrayIterator($this->items);
+    }
+
+    //JsonSerializable
+    public function jsonSerialize()
+    {
+        return $this->toArray();
+    }
+
+    /**
+     * 转换当前数据集为JSON字符串
+     * @access public
+     * @param integer $options json参数
+     * @return string
+     */
+    public function toJson(int $options = JSON_UNESCAPED_UNICODE): string
+    {
+        return json_encode($this->toArray(), $options);
+    }
+
+    public function __toString()
+    {
+        return $this->toJson();
+    }
+
+    /**
+     * 转换成数组
+     *
+     * @access public
+     * @param mixed $items 数据
+     * @return array
+     */
+    protected function convertToArray($items): array
+    {
+        if ($items instanceof self) {
+            return $items->all();
+        }
+
+        return (array) $items;
+    }
+}
diff --git a/vendor/topthink/think-helper/src/contract/Arrayable.php b/vendor/topthink/think-helper/src/contract/Arrayable.php
new file mode 100755
index 0000000..7c6b992
--- /dev/null
+++ b/vendor/topthink/think-helper/src/contract/Arrayable.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace think\contract;
+
+interface Arrayable
+{
+    public function toArray(): array;
+}
diff --git a/vendor/topthink/think-helper/src/contract/Jsonable.php b/vendor/topthink/think-helper/src/contract/Jsonable.php
new file mode 100755
index 0000000..d86122d
--- /dev/null
+++ b/vendor/topthink/think-helper/src/contract/Jsonable.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace think\contract;
+
+interface Jsonable
+{
+    public function toJson(int $options = JSON_UNESCAPED_UNICODE): string;
+}
diff --git a/vendor/topthink/think-helper/src/helper.php b/vendor/topthink/think-helper/src/helper.php
new file mode 100755
index 0000000..0a14735
--- /dev/null
+++ b/vendor/topthink/think-helper/src/helper.php
@@ -0,0 +1,279 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+use think\Collection;
+use think\helper\Arr;
+
+if (!function_exists('throw_if')) {
+    /**
+     * 按条件抛异常
+     *
+     * @param mixed            $condition
+     * @param Throwable|string $exception
+     * @param array            ...$parameters
+     * @return mixed
+     *
+     * @throws Throwable
+     */
+    function throw_if($condition, $exception, ...$parameters)
+    {
+        if ($condition) {
+            throw (is_string($exception) ? new $exception(...$parameters) : $exception);
+        }
+
+        return $condition;
+    }
+}
+
+if (!function_exists('throw_unless')) {
+    /**
+     * 按条件抛异常
+     *
+     * @param mixed            $condition
+     * @param Throwable|string $exception
+     * @param array            ...$parameters
+     * @return mixed
+     * @throws Throwable
+     */
+    function throw_unless($condition, $exception, ...$parameters)
+    {
+        if (!$condition) {
+            throw (is_string($exception) ? new $exception(...$parameters) : $exception);
+        }
+
+        return $condition;
+    }
+}
+
+if (!function_exists('tap')) {
+    /**
+     * 对一个值调用给定的闭包,然后返回该值
+     *
+     * @param mixed         $value
+     * @param callable|null $callback
+     * @return mixed
+     */
+    function tap($value, $callback = null)
+    {
+        if (is_null($callback)) {
+            return $value;
+        }
+
+        $callback($value);
+
+        return $value;
+    }
+}
+
+if (!function_exists('value')) {
+    /**
+     * Return the default value of the given value.
+     *
+     * @param mixed $value
+     * @return mixed
+     */
+    function value($value)
+    {
+        return $value instanceof Closure ? $value() : $value;
+    }
+}
+
+if (!function_exists('collect')) {
+    /**
+     * Create a collection from the given value.
+     *
+     * @param mixed $value
+     * @return Collection
+     */
+    function collect($value = null)
+    {
+        return new Collection($value);
+    }
+}
+
+if (!function_exists('data_fill')) {
+    /**
+     * Fill in data where it's missing.
+     *
+     * @param mixed        $target
+     * @param string|array $key
+     * @param mixed        $value
+     * @return mixed
+     */
+    function data_fill(&$target, $key, $value)
+    {
+        return data_set($target, $key, $value, false);
+    }
+}
+
+if (!function_exists('data_get')) {
+    /**
+     * Get an item from an array or object using "dot" notation.
+     *
+     * @param mixed            $target
+     * @param string|array|int $key
+     * @param mixed            $default
+     * @return mixed
+     */
+    function data_get($target, $key, $default = null)
+    {
+        if (is_null($key)) {
+            return $target;
+        }
+
+        $key = is_array($key) ? $key : explode('.', $key);
+
+        while (!is_null($segment = array_shift($key))) {
+            if ('*' === $segment) {
+                if ($target instanceof Collection) {
+                    $target = $target->all();
+                } elseif (!is_array($target)) {
+                    return value($default);
+                }
+
+                $result = [];
+
+                foreach ($target as $item) {
+                    $result[] = data_get($item, $key);
+                }
+
+                return in_array('*', $key) ? Arr::collapse($result) : $result;
+            }
+
+            if (Arr::accessible($target) && Arr::exists($target, $segment)) {
+                $target = $target[$segment];
+            } elseif (is_object($target) && isset($target->{$segment})) {
+                $target = $target->{$segment};
+            } else {
+                return value($default);
+            }
+        }
+
+        return $target;
+    }
+}
+
+if (!function_exists('data_set')) {
+    /**
+     * Set an item on an array or object using dot notation.
+     *
+     * @param mixed        $target
+     * @param string|array $key
+     * @param mixed        $value
+     * @param bool         $overwrite
+     * @return mixed
+     */
+    function data_set(&$target, $key, $value, $overwrite = true)
+    {
+        $segments = is_array($key) ? $key : explode('.', $key);
+
+        if (($segment = array_shift($segments)) === '*') {
+            if (!Arr::accessible($target)) {
+                $target = [];
+            }
+
+            if ($segments) {
+                foreach ($target as &$inner) {
+                    data_set($inner, $segments, $value, $overwrite);
+                }
+            } elseif ($overwrite) {
+                foreach ($target as &$inner) {
+                    $inner = $value;
+                }
+            }
+        } elseif (Arr::accessible($target)) {
+            if ($segments) {
+                if (!Arr::exists($target, $segment)) {
+                    $target[$segment] = [];
+                }
+
+                data_set($target[$segment], $segments, $value, $overwrite);
+            } elseif ($overwrite || !Arr::exists($target, $segment)) {
+                $target[$segment] = $value;
+            }
+        } elseif (is_object($target)) {
+            if ($segments) {
+                if (!isset($target->{$segment})) {
+                    $target->{$segment} = [];
+                }
+
+                data_set($target->{$segment}, $segments, $value, $overwrite);
+            } elseif ($overwrite || !isset($target->{$segment})) {
+                $target->{$segment} = $value;
+            }
+        } else {
+            $target = [];
+
+            if ($segments) {
+                data_set($target[$segment], $segments, $value, $overwrite);
+            } elseif ($overwrite) {
+                $target[$segment] = $value;
+            }
+        }
+
+        return $target;
+    }
+}
+
+if (!function_exists('trait_uses_recursive')) {
+    /**
+     * 获取一个trait里所有引用到的trait
+     *
+     * @param string $trait Trait
+     * @return array
+     */
+    function trait_uses_recursive(string $trait): array
+    {
+        $traits = class_uses($trait);
+        foreach ($traits as $trait) {
+            $traits += trait_uses_recursive($trait);
+        }
+
+        return $traits;
+    }
+}
+
+if (!function_exists('class_basename')) {
+    /**
+     * 获取类名(不包含命名空间)
+     *
+     * @param mixed $class 类名
+     * @return string
+     */
+    function class_basename($class): string
+    {
+        $class = is_object($class) ? get_class($class) : $class;
+        return basename(str_replace('\\', '/', $class));
+    }
+}
+
+if (!function_exists('class_uses_recursive')) {
+    /**
+     *获取一个类里所有用到的trait,包括父类的
+     *
+     * @param mixed $class 类名
+     * @return array
+     */
+    function class_uses_recursive($class): array
+    {
+        if (is_object($class)) {
+            $class = get_class($class);
+        }
+
+        $results = [];
+        $classes = array_merge([$class => $class], class_parents($class));
+        foreach ($classes as $class) {
+            $results += trait_uses_recursive($class);
+        }
+
+        return array_unique($results);
+    }
+}
diff --git a/vendor/topthink/think-helper/src/helper/Arr.php b/vendor/topthink/think-helper/src/helper/Arr.php
new file mode 100755
index 0000000..ed4d6a9
--- /dev/null
+++ b/vendor/topthink/think-helper/src/helper/Arr.php
@@ -0,0 +1,634 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\helper;
+
+use ArrayAccess;
+use InvalidArgumentException;
+use think\Collection;
+
+class Arr
+{
+
+    /**
+     * Determine whether the given value is array accessible.
+     *
+     * @param mixed $value
+     * @return bool
+     */
+    public static function accessible($value)
+    {
+        return is_array($value) || $value instanceof ArrayAccess;
+    }
+
+    /**
+     * Add an element to an array using "dot" notation if it doesn't exist.
+     *
+     * @param array  $array
+     * @param string $key
+     * @param mixed  $value
+     * @return array
+     */
+    public static function add($array, $key, $value)
+    {
+        if (is_null(static::get($array, $key))) {
+            static::set($array, $key, $value);
+        }
+
+        return $array;
+    }
+
+    /**
+     * Collapse an array of arrays into a single array.
+     *
+     * @param array $array
+     * @return array
+     */
+    public static function collapse($array)
+    {
+        $results = [];
+
+        foreach ($array as $values) {
+            if ($values instanceof Collection) {
+                $values = $values->all();
+            } elseif (!is_array($values)) {
+                continue;
+            }
+
+            $results = array_merge($results, $values);
+        }
+
+        return $results;
+    }
+
+    /**
+     * Cross join the given arrays, returning all possible permutations.
+     *
+     * @param array ...$arrays
+     * @return array
+     */
+    public static function crossJoin(...$arrays)
+    {
+        $results = [[]];
+
+        foreach ($arrays as $index => $array) {
+            $append = [];
+
+            foreach ($results as $product) {
+                foreach ($array as $item) {
+                    $product[$index] = $item;
+
+                    $append[] = $product;
+                }
+            }
+
+            $results = $append;
+        }
+
+        return $results;
+    }
+
+    /**
+     * Divide an array into two arrays. One with keys and the other with values.
+     *
+     * @param array $array
+     * @return array
+     */
+    public static function divide($array)
+    {
+        return [array_keys($array), array_values($array)];
+    }
+
+    /**
+     * Flatten a multi-dimensional associative array with dots.
+     *
+     * @param array  $array
+     * @param string $prepend
+     * @return array
+     */
+    public static function dot($array, $prepend = '')
+    {
+        $results = [];
+
+        foreach ($array as $key => $value) {
+            if (is_array($value) && !empty($value)) {
+                $results = array_merge($results, static::dot($value, $prepend . $key . '.'));
+            } else {
+                $results[$prepend . $key] = $value;
+            }
+        }
+
+        return $results;
+    }
+
+    /**
+     * Get all of the given array except for a specified array of keys.
+     *
+     * @param array        $array
+     * @param array|string $keys
+     * @return array
+     */
+    public static function except($array, $keys)
+    {
+        static::forget($array, $keys);
+
+        return $array;
+    }
+
+    /**
+     * Determine if the given key exists in the provided array.
+     *
+     * @param \ArrayAccess|array $array
+     * @param string|int         $key
+     * @return bool
+     */
+    public static function exists($array, $key)
+    {
+        if ($array instanceof ArrayAccess) {
+            return $array->offsetExists($key);
+        }
+
+        return array_key_exists($key, $array);
+    }
+
+    /**
+     * Return the first element in an array passing a given truth test.
+     *
+     * @param array         $array
+     * @param callable|null $callback
+     * @param mixed         $default
+     * @return mixed
+     */
+    public static function first($array, callable $callback = null, $default = null)
+    {
+        if (is_null($callback)) {
+            if (empty($array)) {
+                return value($default);
+            }
+
+            foreach ($array as $item) {
+                return $item;
+            }
+        }
+
+        foreach ($array as $key => $value) {
+            if (call_user_func($callback, $value, $key)) {
+                return $value;
+            }
+        }
+
+        return value($default);
+    }
+
+    /**
+     * Return the last element in an array passing a given truth test.
+     *
+     * @param array         $array
+     * @param callable|null $callback
+     * @param mixed         $default
+     * @return mixed
+     */
+    public static function last($array, callable $callback = null, $default = null)
+    {
+        if (is_null($callback)) {
+            return empty($array) ? value($default) : end($array);
+        }
+
+        return static::first(array_reverse($array, true), $callback, $default);
+    }
+
+    /**
+     * Flatten a multi-dimensional array into a single level.
+     *
+     * @param array $array
+     * @param int   $depth
+     * @return array
+     */
+    public static function flatten($array, $depth = INF)
+    {
+        $result = [];
+
+        foreach ($array as $item) {
+            $item = $item instanceof Collection ? $item->all() : $item;
+
+            if (!is_array($item)) {
+                $result[] = $item;
+            } elseif ($depth === 1) {
+                $result = array_merge($result, array_values($item));
+            } else {
+                $result = array_merge($result, static::flatten($item, $depth - 1));
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Remove one or many array items from a given array using "dot" notation.
+     *
+     * @param array        $array
+     * @param array|string $keys
+     * @return void
+     */
+    public static function forget(&$array, $keys)
+    {
+        $original = &$array;
+
+        $keys = (array) $keys;
+
+        if (count($keys) === 0) {
+            return;
+        }
+
+        foreach ($keys as $key) {
+            // if the exact key exists in the top-level, remove it
+            if (static::exists($array, $key)) {
+                unset($array[$key]);
+
+                continue;
+            }
+
+            $parts = explode('.', $key);
+
+            // clean up before each pass
+            $array = &$original;
+
+            while (count($parts) > 1) {
+                $part = array_shift($parts);
+
+                if (isset($array[$part]) && is_array($array[$part])) {
+                    $array = &$array[$part];
+                } else {
+                    continue 2;
+                }
+            }
+
+            unset($array[array_shift($parts)]);
+        }
+    }
+
+    /**
+     * Get an item from an array using "dot" notation.
+     *
+     * @param \ArrayAccess|array $array
+     * @param string             $key
+     * @param mixed              $default
+     * @return mixed
+     */
+    public static function get($array, $key, $default = null)
+    {
+        if (!static::accessible($array)) {
+            return value($default);
+        }
+
+        if (is_null($key)) {
+            return $array;
+        }
+
+        if (static::exists($array, $key)) {
+            return $array[$key];
+        }
+
+        if (strpos($key, '.') === false) {
+            return $array[$key] ?? value($default);
+        }
+
+        foreach (explode('.', $key) as $segment) {
+            if (static::accessible($array) && static::exists($array, $segment)) {
+                $array = $array[$segment];
+            } else {
+                return value($default);
+            }
+        }
+
+        return $array;
+    }
+
+    /**
+     * Check if an item or items exist in an array using "dot" notation.
+     *
+     * @param \ArrayAccess|array $array
+     * @param string|array       $keys
+     * @return bool
+     */
+    public static function has($array, $keys)
+    {
+        $keys = (array) $keys;
+
+        if (!$array || $keys === []) {
+            return false;
+        }
+
+        foreach ($keys as $key) {
+            $subKeyArray = $array;
+
+            if (static::exists($array, $key)) {
+                continue;
+            }
+
+            foreach (explode('.', $key) as $segment) {
+                if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) {
+                    $subKeyArray = $subKeyArray[$segment];
+                } else {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Determines if an array is associative.
+     *
+     * An array is "associative" if it doesn't have sequential numerical keys beginning with zero.
+     *
+     * @param array $array
+     * @return bool
+     */
+    public static function isAssoc(array $array)
+    {
+        $keys = array_keys($array);
+
+        return array_keys($keys) !== $keys;
+    }
+
+    /**
+     * Get a subset of the items from the given array.
+     *
+     * @param array        $array
+     * @param array|string $keys
+     * @return array
+     */
+    public static function only($array, $keys)
+    {
+        return array_intersect_key($array, array_flip((array) $keys));
+    }
+
+    /**
+     * Pluck an array of values from an array.
+     *
+     * @param array             $array
+     * @param string|array      $value
+     * @param string|array|null $key
+     * @return array
+     */
+    public static function pluck($array, $value, $key = null)
+    {
+        $results = [];
+
+        [$value, $key] = static::explodePluckParameters($value, $key);
+
+        foreach ($array as $item) {
+            $itemValue = data_get($item, $value);
+
+            // If the key is "null", we will just append the value to the array and keep
+            // looping. Otherwise we will key the array using the value of the key we
+            // received from the developer. Then we'll return the final array form.
+            if (is_null($key)) {
+                $results[] = $itemValue;
+            } else {
+                $itemKey = data_get($item, $key);
+
+                if (is_object($itemKey) && method_exists($itemKey, '__toString')) {
+                    $itemKey = (string) $itemKey;
+                }
+
+                $results[$itemKey] = $itemValue;
+            }
+        }
+
+        return $results;
+    }
+
+    /**
+     * Explode the "value" and "key" arguments passed to "pluck".
+     *
+     * @param string|array      $value
+     * @param string|array|null $key
+     * @return array
+     */
+    protected static function explodePluckParameters($value, $key)
+    {
+        $value = is_string($value) ? explode('.', $value) : $value;
+
+        $key = is_null($key) || is_array($key) ? $key : explode('.', $key);
+
+        return [$value, $key];
+    }
+
+    /**
+     * Push an item onto the beginning of an array.
+     *
+     * @param array $array
+     * @param mixed $value
+     * @param mixed $key
+     * @return array
+     */
+    public static function prepend($array, $value, $key = null)
+    {
+        if (is_null($key)) {
+            array_unshift($array, $value);
+        } else {
+            $array = [$key => $value] + $array;
+        }
+
+        return $array;
+    }
+
+    /**
+     * Get a value from the array, and remove it.
+     *
+     * @param array  $array
+     * @param string $key
+     * @param mixed  $default
+     * @return mixed
+     */
+    public static function pull(&$array, $key, $default = null)
+    {
+        $value = static::get($array, $key, $default);
+
+        static::forget($array, $key);
+
+        return $value;
+    }
+
+    /**
+     * Get one or a specified number of random values from an array.
+     *
+     * @param array    $array
+     * @param int|null $number
+     * @return mixed
+     *
+     * @throws \InvalidArgumentException
+     */
+    public static function random($array, $number = null)
+    {
+        $requested = is_null($number) ? 1 : $number;
+
+        $count = count($array);
+
+        if ($requested > $count) {
+            throw new InvalidArgumentException(
+                "You requested {$requested} items, but there are only {$count} items available."
+            );
+        }
+
+        if (is_null($number)) {
+            return $array[array_rand($array)];
+        }
+
+        if ((int) $number === 0) {
+            return [];
+        }
+
+        $keys = array_rand($array, $number);
+
+        $results = [];
+
+        foreach ((array) $keys as $key) {
+            $results[] = $array[$key];
+        }
+
+        return $results;
+    }
+
+    /**
+     * Set an array item to a given value using "dot" notation.
+     *
+     * If no key is given to the method, the entire array will be replaced.
+     *
+     * @param array  $array
+     * @param string $key
+     * @param mixed  $value
+     * @return array
+     */
+    public static function set(&$array, $key, $value)
+    {
+        if (is_null($key)) {
+            return $array = $value;
+        }
+
+        $keys = explode('.', $key);
+
+        while (count($keys) > 1) {
+            $key = array_shift($keys);
+
+            // If the key doesn't exist at this depth, we will just create an empty array
+            // to hold the next value, allowing us to create the arrays to hold final
+            // values at the correct depth. Then we'll keep digging into the array.
+            if (!isset($array[$key]) || !is_array($array[$key])) {
+                $array[$key] = [];
+            }
+
+            $array = &$array[$key];
+        }
+
+        $array[array_shift($keys)] = $value;
+
+        return $array;
+    }
+
+    /**
+     * Shuffle the given array and return the result.
+     *
+     * @param array    $array
+     * @param int|null $seed
+     * @return array
+     */
+    public static function shuffle($array, $seed = null)
+    {
+        if (is_null($seed)) {
+            shuffle($array);
+        } else {
+            srand($seed);
+
+            usort($array, function () {
+                return rand(-1, 1);
+            });
+        }
+
+        return $array;
+    }
+
+    /**
+     * Sort the array using the given callback or "dot" notation.
+     *
+     * @param array                $array
+     * @param callable|string|null $callback
+     * @return array
+     */
+    public static function sort($array, $callback = null)
+    {
+        return Collection::make($array)->sort($callback)->all();
+    }
+
+    /**
+     * Recursively sort an array by keys and values.
+     *
+     * @param array $array
+     * @return array
+     */
+    public static function sortRecursive($array)
+    {
+        foreach ($array as &$value) {
+            if (is_array($value)) {
+                $value = static::sortRecursive($value);
+            }
+        }
+
+        if (static::isAssoc($array)) {
+            ksort($array);
+        } else {
+            sort($array);
+        }
+
+        return $array;
+    }
+
+    /**
+     * Convert the array into a query string.
+     *
+     * @param array $array
+     * @return string
+     */
+    public static function query($array)
+    {
+        return http_build_query($array, null, '&', PHP_QUERY_RFC3986);
+    }
+
+    /**
+     * Filter the array using the given callback.
+     *
+     * @param array    $array
+     * @param callable $callback
+     * @return array
+     */
+    public static function where($array, callable $callback)
+    {
+        return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH);
+    }
+
+    /**
+     * If the given value is not an array and not null, wrap it in one.
+     *
+     * @param mixed $value
+     * @return array
+     */
+    public static function wrap($value)
+    {
+        if (is_null($value)) {
+            return [];
+        }
+
+        return is_array($value) ? $value : [$value];
+    }
+}
\ No newline at end of file
diff --git a/vendor/topthink/think-helper/src/helper/Str.php b/vendor/topthink/think-helper/src/helper/Str.php
new file mode 100755
index 0000000..7391fbd
--- /dev/null
+++ b/vendor/topthink/think-helper/src/helper/Str.php
@@ -0,0 +1,234 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+namespace think\helper;
+
+class Str
+{
+
+    protected static $snakeCache = [];
+
+    protected static $camelCache = [];
+
+    protected static $studlyCache = [];
+
+    /**
+     * 检查字符串中是否包含某些字符串
+     * @param string       $haystack
+     * @param string|array $needles
+     * @return bool
+     */
+    public static function contains(string $haystack, $needles): bool
+    {
+        foreach ((array) $needles as $needle) {
+            if ('' != $needle && mb_strpos($haystack, $needle) !== false) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * 检查字符串是否以某些字符串结尾
+     *
+     * @param  string       $haystack
+     * @param  string|array $needles
+     * @return bool
+     */
+    public static function endsWith(string $haystack, $needles): bool
+    {
+        foreach ((array) $needles as $needle) {
+            if ((string) $needle === static::substr($haystack, -static::length($needle))) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * 检查字符串是否以某些字符串开头
+     *
+     * @param  string       $haystack
+     * @param  string|array $needles
+     * @return bool
+     */
+    public static function startsWith(string $haystack, $needles): bool
+    {
+        foreach ((array) $needles as $needle) {
+            if ('' != $needle && mb_strpos($haystack, $needle) === 0) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * 获取指定长度的随机字母数字组合的字符串
+     *
+     * @param  int $length
+     * @param  int $type
+     * @param  string $addChars
+     * @return string
+     */
+    public static function random(int $length = 6, int $type = null, string $addChars = ''): string
+    {
+        $str = '';
+        switch ($type) {
+            case 0:
+                $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' . $addChars;
+                break;
+            case 1:
+                $chars = str_repeat('0123456789', 3);
+                break;
+            case 2:
+                $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' . $addChars;
+                break;
+            case 3:
+                $chars = 'abcdefghijklmnopqrstuvwxyz' . $addChars;
+                break;
+            case 4:
+                $chars = "们以我到他会作时要动国产的一是工就年阶义发成部民可出能方进在了不和有大这主中人上为来分生对于学下级地个用同行面说种过命度革而多子后自社加小机也经力线本电高量长党得实家定深法表着水理化争现所二起政三好十战无农使性前等反体合斗路图把结第里正新开论之物从当两些还天资事队批点育重其思与间内去因件日利相由压员气业代全组数果期导平各基或月毛然如应形想制心样干都向变关问比展那它最及外没看治提五解系林者米群头意只明四道马认次文通但条较克又公孔领军流入接席位情运器并飞原油放立题质指建区验活众很教决特此常石强极土少已根共直团统式转别造切九你取西持总料连任志观调七么山程百报更见必真保热委手改管处己将修支识病象几先老光专什六型具示复安带每东增则完风回南广劳轮科北打积车计给节做务被整联步类集号列温装即毫知轴研单色坚据速防史拉世设达尔场织历花受求传口断况采精金界品判参层止边清至万确究书" . $addChars;
+                break;
+            default:
+                $chars = 'ABCDEFGHIJKMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789' . $addChars;
+                break;
+        }
+        if ($length > 10) {
+            $chars = $type == 1 ? str_repeat($chars, $length) : str_repeat($chars, 5);
+        }
+        if ($type != 4) {
+            $chars = str_shuffle($chars);
+            $str = substr($chars, 0, $length);
+        } else {
+            for ($i = 0; $i < $length; $i++) {
+                $str .= mb_substr($chars, floor(mt_rand(0, mb_strlen($chars, 'utf-8') - 1)), 1);
+            }
+        }
+        return $str;
+    }
+
+    /**
+     * 字符串转小写
+     *
+     * @param  string $value
+     * @return string
+     */
+    public static function lower(string $value): string
+    {
+        return mb_strtolower($value, 'UTF-8');
+    }
+
+    /**
+     * 字符串转大写
+     *
+     * @param  string $value
+     * @return string
+     */
+    public static function upper(string $value): string
+    {
+        return mb_strtoupper($value, 'UTF-8');
+    }
+
+    /**
+     * 获取字符串的长度
+     *
+     * @param  string $value
+     * @return int
+     */
+    public static function length(string $value): int
+    {
+        return mb_strlen($value);
+    }
+
+    /**
+     * 截取字符串
+     *
+     * @param  string   $string
+     * @param  int      $start
+     * @param  int|null $length
+     * @return string
+     */
+    public static function substr(string $string, int $start, int $length = null): string
+    {
+        return mb_substr($string, $start, $length, 'UTF-8');
+    }
+
+    /**
+     * 驼峰转下划线
+     *
+     * @param  string $value
+     * @param  string $delimiter
+     * @return string
+     */
+    public static function snake(string $value, string $delimiter = '_'): string
+    {
+        $key = $value;
+
+        if (isset(static::$snakeCache[$key][$delimiter])) {
+            return static::$snakeCache[$key][$delimiter];
+        }
+
+        if (!ctype_lower($value)) {
+            $value = preg_replace('/\s+/u', '', $value);
+
+            $value = static::lower(preg_replace('/(.)(?=[A-Z])/u', '$1' . $delimiter, $value));
+        }
+
+        return static::$snakeCache[$key][$delimiter] = $value;
+    }
+
+    /**
+     * 下划线转驼峰(首字母小写)
+     *
+     * @param  string $value
+     * @return string
+     */
+    public static function camel(string $value): string
+    {
+        if (isset(static::$camelCache[$value])) {
+            return static::$camelCache[$value];
+        }
+
+        return static::$camelCache[$value] = lcfirst(static::studly($value));
+    }
+
+    /**
+     * 下划线转驼峰(首字母大写)
+     *
+     * @param  string $value
+     * @return string
+     */
+    public static function studly(string $value): string
+    {
+        $key = $value;
+
+        if (isset(static::$studlyCache[$key])) {
+            return static::$studlyCache[$key];
+        }
+
+        $value = ucwords(str_replace(['-', '_'], ' ', $value));
+
+        return static::$studlyCache[$key] = str_replace(' ', '', $value);
+    }
+
+    /**
+     * 转为首字母大写的标题格式
+     *
+     * @param  string $value
+     * @return string
+     */
+    public static function title(string $value): string
+    {
+        return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8');
+    }
+}
diff --git a/vendor/topthink/think-image/.gitignore b/vendor/topthink/think-image/.gitignore
new file mode 100755
index 0000000..828149d
--- /dev/null
+++ b/vendor/topthink/think-image/.gitignore
@@ -0,0 +1,4 @@
+/vendor/
+/thinkphp/
+/composer.lock
+/.idea/
\ No newline at end of file
diff --git a/vendor/topthink/think-image/.travis.yml b/vendor/topthink/think-image/.travis.yml
new file mode 100755
index 0000000..28ef8fb
--- /dev/null
+++ b/vendor/topthink/think-image/.travis.yml
@@ -0,0 +1,22 @@
+language: php
+
+php:
+  - 5.4
+  - 5.5
+  - 5.6
+  - 7.0
+  - hhvm
+
+matrix:
+    allow_failures:
+        - php: 7.0
+        - php: hhvm
+
+before_script:
+  - composer self-update
+  - composer install --prefer-source --no-interaction --dev
+
+script: phpunit --coverage-clover=coverage.xml --configuration=phpunit.xml
+
+after_success:
+  - bash <(curl -s https://codecov.io/bash)
\ No newline at end of file
diff --git a/vendor/topthink/think-image/LICENSE b/vendor/topthink/think-image/LICENSE
new file mode 100755
index 0000000..8dada3e
--- /dev/null
+++ b/vendor/topthink/think-image/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/vendor/topthink/think-image/README.md b/vendor/topthink/think-image/README.md
new file mode 100755
index 0000000..866a0b4
--- /dev/null
+++ b/vendor/topthink/think-image/README.md
@@ -0,0 +1,29 @@
+# The ThinkPHP5 Image Package
+
+[![Build Status](https://img.shields.io/travis/top-think/think-image.svg)](https://travis-ci.org/top-think/think-image)
+[![Coverage Status](https://img.shields.io/codecov/c/github/top-think/think-image.svg)](https://codecov.io/github/top-think/think-image)
+[![Downloads](https://img.shields.io/github/downloads/top-think/think-image/total.svg)](https://github.com/top-think/think-image/releases)
+[![Releases](https://img.shields.io/github/release/top-think/think-image.svg)](https://github.com/top-think/think-image/releases/latest)
+[![Releases Downloads](https://img.shields.io/github/downloads/top-think/think-image/latest/total.svg)](https://github.com/top-think/think-image/releases/latest)
+[![Packagist Status](https://img.shields.io/packagist/v/top-think/think-image.svg)](https://packagist.org/packages/topthink/think-image)
+[![Packagist Downloads](https://img.shields.io/packagist/dt/top-think/think-image.svg)](https://packagist.org/packages/topthink/think-image)
+
+## 安装
+
+> composer require topthink/think-image
+
+## 使用
+
+~~~
+$image = \think\Image::open('./image.jpg');
+或者
+$image = \think\Image::open(request()->file('image'));
+
+
+$image->crop(...)
+    ->thumb(...)
+    ->water(...)
+    ->text(....)
+    ->save(..);
+
+~~~
\ No newline at end of file
diff --git a/vendor/topthink/think-image/composer.json b/vendor/topthink/think-image/composer.json
new file mode 100755
index 0000000..276d029
--- /dev/null
+++ b/vendor/topthink/think-image/composer.json
@@ -0,0 +1,26 @@
+{
+    "name": "topthink/think-image",
+    "description": "The ThinkPHP5 Image Package",
+    "license": "Apache-2.0",
+    "authors": [
+        {
+            "name": "yunwuxin",
+            "email": "448901948@qq.com"
+        }
+    ],
+    "require": {
+        "ext-gd": "*"
+    },
+    "require-dev": {
+        "topthink/framework": "^5.0",
+        "phpunit/phpunit": "4.8.*"
+    },
+    "config": {
+        "preferred-install": "dist"
+    },
+    "autoload": {
+        "psr-4": {
+            "think\\": "src"
+        }
+    }
+}
diff --git a/vendor/topthink/think-image/phpunit.xml b/vendor/topthink/think-image/phpunit.xml
new file mode 100755
index 0000000..ee93855
--- /dev/null
+++ b/vendor/topthink/think-image/phpunit.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit backupGlobals="false"
+         backupStaticAttributes="false"
+         bootstrap="tests/autoload.php"
+         colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         processIsolation="false"
+         stopOnFailure="false"
+         syntaxCheck="false">
+    <testsuites>
+        <testsuite name="Package Test Suite">
+            <directory suffix=".php">./tests/</directory>
+        </testsuite>
+    </testsuites>
+    <listeners>
+        <listener class="JohnKary\PHPUnit\Listener\SpeedTrapListener" />
+    </listeners>
+</phpunit>
diff --git a/vendor/topthink/think-image/src/Image.php b/vendor/topthink/think-image/src/Image.php
new file mode 100755
index 0000000..e28a13d
--- /dev/null
+++ b/vendor/topthink/think-image/src/Image.php
@@ -0,0 +1,610 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+use think\image\Exception as ImageException;
+use think\image\gif\Gif;
+
+class Image
+{
+
+    /* 缩略图相关常量定义 */
+    const THUMB_SCALING   = 1; //常量,标识缩略图等比例缩放类型
+    const THUMB_FILLED    = 2; //常量,标识缩略图缩放后填充类型
+    const THUMB_CENTER    = 3; //常量,标识缩略图居中裁剪类型
+    const THUMB_NORTHWEST = 4; //常量,标识缩略图左上角裁剪类型
+    const THUMB_SOUTHEAST = 5; //常量,标识缩略图右下角裁剪类型
+    const THUMB_FIXED     = 6; //常量,标识缩略图固定尺寸缩放类型
+    /* 水印相关常量定义 */
+    const WATER_NORTHWEST = 1; //常量,标识左上角水印
+    const WATER_NORTH     = 2; //常量,标识上居中水印
+    const WATER_NORTHEAST = 3; //常量,标识右上角水印
+    const WATER_WEST      = 4; //常量,标识左居中水印
+    const WATER_CENTER    = 5; //常量,标识居中水印
+    const WATER_EAST      = 6; //常量,标识右居中水印
+    const WATER_SOUTHWEST = 7; //常量,标识左下角水印
+    const WATER_SOUTH     = 8; //常量,标识下居中水印
+    const WATER_SOUTHEAST = 9; //常量,标识右下角水印
+    /* 翻转相关常量定义 */
+    const FLIP_X = 1; //X轴翻转
+    const FLIP_Y = 2; //Y轴翻转
+
+    /**
+     * 图像资源对象
+     *
+     * @var resource
+     */
+    protected $im;
+
+    /** @var  Gif */
+    protected $gif;
+
+    /**
+     * 图像信息,包括 width, height, type, mime, size
+     *
+     * @var array
+     */
+    protected $info;
+
+    protected function __construct(\SplFileInfo $file)
+    {
+        //获取图像信息
+        $info = @getimagesize($file->getPathname());
+
+        //检测图像合法性
+        if (false === $info || (IMAGETYPE_GIF === $info[2] && empty($info['bits']))) {
+            throw new ImageException('Illegal image file');
+        }
+
+        //设置图像信息
+        $this->info = [
+            'width'  => $info[0],
+            'height' => $info[1],
+            'type'   => image_type_to_extension($info[2], false),
+            'mime'   => $info['mime'],
+        ];
+
+        //打开图像
+        if ('gif' == $this->info['type']) {
+            $this->gif = new Gif($file->getPathname());
+            $this->im  = @imagecreatefromstring($this->gif->image());
+        } else {
+            $fun      = "imagecreatefrom{$this->info['type']}";
+            $this->im = @$fun($file->getPathname());
+        }
+
+        if (empty($this->im)) {
+            throw new ImageException('Failed to create image resources!');
+        }
+
+    }
+
+    /**
+     * 打开一个图片文件
+     * @param \SplFileInfo|string $file
+     * @return Image
+     */
+    public static function open($file)
+    {
+        if (is_string($file)) {
+            $file = new \SplFileInfo($file);
+        }
+        if (!$file->isFile()) {
+            throw new ImageException('image file not exist');
+        }
+        return new self($file);
+    }
+
+    /**
+     * 保存图像
+     * @param string      $pathname  图像保存路径名称
+     * @param null|string $type      图像类型
+     * @param int         $quality   图像质量
+     * @param bool        $interlace 是否对JPEG类型图像设置隔行扫描
+     * @return $this
+     */
+    public function save($pathname, $type = null, $quality = 80, $interlace = true)
+    {
+        //自动获取图像类型
+        if (is_null($type)) {
+            $type = $this->info['type'];
+        } else {
+            $type = strtolower($type);
+        }
+        //保存图像
+        if ('jpeg' == $type || 'jpg' == $type) {
+            //JPEG图像设置隔行扫描
+            imageinterlace($this->im, $interlace);
+            imagejpeg($this->im, $pathname, $quality);
+        } elseif ('gif' == $type && !empty($this->gif)) {
+            $this->gif->save($pathname);
+        } elseif ('png' == $type) {
+            //设定保存完整的 alpha 通道信息
+            imagesavealpha($this->im, true);
+            //ImagePNG生成图像的质量范围从0到9的
+            imagepng($this->im, $pathname, min((int) ($quality / 10), 9));
+        } else {
+            $fun = 'image' . $type;
+            $fun($this->im, $pathname);
+        }
+
+        return $this;
+    }
+
+    /**
+     * 返回图像宽度
+     * @return int 图像宽度
+     */
+    public function width()
+    {
+        return $this->info['width'];
+    }
+
+    /**
+     * 返回图像高度
+     * @return int 图像高度
+     */
+    public function height()
+    {
+        return $this->info['height'];
+    }
+
+    /**
+     * 返回图像类型
+     * @return string 图像类型
+     */
+    public function type()
+    {
+        return $this->info['type'];
+    }
+
+    /**
+     * 返回图像MIME类型
+     * @return string 图像MIME类型
+     */
+    public function mime()
+    {
+        return $this->info['mime'];
+    }
+
+    /**
+     * 返回图像尺寸数组 0 - 图像宽度,1 - 图像高度
+     * @return array 图像尺寸
+     */
+    public function size()
+    {
+        return [$this->info['width'], $this->info['height']];
+    }
+
+    /**
+     * 旋转图像
+     * @param int $degrees 顺时针旋转的度数
+     * @return $this
+     */
+    public function rotate($degrees = 90)
+    {
+        do {
+            $img = imagerotate($this->im, -$degrees, imagecolorallocatealpha($this->im, 0, 0, 0, 127));
+            imagedestroy($this->im);
+            $this->im = $img;
+        } while (!empty($this->gif) && $this->gifNext());
+
+        $this->info['width']  = imagesx($this->im);
+        $this->info['height'] = imagesy($this->im);
+
+        return $this;
+    }
+
+    /**
+     * 翻转图像
+     * @param integer $direction 翻转轴,X或者Y
+     * @return $this
+     */
+    public function flip($direction = self::FLIP_X)
+    {
+        //原图宽度和高度
+        $w = $this->info['width'];
+        $h = $this->info['height'];
+
+        do {
+
+            $img = imagecreatetruecolor($w, $h);
+
+            switch ($direction) {
+                case self::FLIP_X:
+                    for ($y = 0; $y < $h; $y++) {
+                        imagecopy($img, $this->im, 0, $h - $y - 1, 0, $y, $w, 1);
+                    }
+                    break;
+                case self::FLIP_Y:
+                    for ($x = 0; $x < $w; $x++) {
+                        imagecopy($img, $this->im, $w - $x - 1, 0, $x, 0, 1, $h);
+                    }
+                    break;
+                default:
+                    throw new ImageException('不支持的翻转类型');
+            }
+
+            imagedestroy($this->im);
+            $this->im = $img;
+
+        } while (!empty($this->gif) && $this->gifNext());
+
+        return $this;
+    }
+
+    /**
+     * 裁剪图像
+     *
+     * @param  integer $w      裁剪区域宽度
+     * @param  integer $h      裁剪区域高度
+     * @param  integer $x      裁剪区域x坐标
+     * @param  integer $y      裁剪区域y坐标
+     * @param  integer $width  图像保存宽度
+     * @param  integer $height 图像保存高度
+     *
+     * @return $this
+     */
+    public function crop($w, $h, $x = 0, $y = 0, $width = null, $height = null)
+    {
+        //设置保存尺寸
+        empty($width) && $width   = $w;
+        empty($height) && $height = $h;
+        do {
+            //创建新图像
+            $img = imagecreatetruecolor($width, $height);
+            // 调整默认颜色
+            $color = imagecolorallocate($img, 255, 255, 255);
+            imagefill($img, 0, 0, $color);
+            //裁剪
+            imagecopyresampled($img, $this->im, 0, 0, $x, $y, $width, $height, $w, $h);
+            imagedestroy($this->im); //销毁原图
+            //设置新图像
+            $this->im = $img;
+        } while (!empty($this->gif) && $this->gifNext());
+        $this->info['width']  = (int) $width;
+        $this->info['height'] = (int) $height;
+        return $this;
+    }
+
+    /**
+     * 生成缩略图
+     *
+     * @param  integer $width  缩略图最大宽度
+     * @param  integer $height 缩略图最大高度
+     * @param int      $type   缩略图裁剪类型
+     *
+     * @return $this
+     */
+    public function thumb($width, $height, $type = self::THUMB_SCALING)
+    {
+        //原图宽度和高度
+        $w = $this->info['width'];
+        $h = $this->info['height'];
+        /* 计算缩略图生成的必要参数 */
+        switch ($type) {
+            /* 等比例缩放 */
+            case self::THUMB_SCALING:
+                //原图尺寸小于缩略图尺寸则不进行缩略
+                if ($w < $width && $h < $height) {
+                    return $this;
+                }
+                //计算缩放比例
+                $scale = min($width / $w, $height / $h);
+                //设置缩略图的坐标及宽度和高度
+                $x      = $y      = 0;
+                $width  = $w * $scale;
+                $height = $h * $scale;
+                break;
+            /* 居中裁剪 */
+            case self::THUMB_CENTER:
+                //计算缩放比例
+                $scale = max($width / $w, $height / $h);
+                //设置缩略图的坐标及宽度和高度
+                $w = $width / $scale;
+                $h = $height / $scale;
+                $x = ($this->info['width'] - $w) / 2;
+                $y = ($this->info['height'] - $h) / 2;
+                break;
+            /* 左上角裁剪 */
+            case self::THUMB_NORTHWEST:
+                //计算缩放比例
+                $scale = max($width / $w, $height / $h);
+                //设置缩略图的坐标及宽度和高度
+                $x = $y = 0;
+                $w = $width / $scale;
+                $h = $height / $scale;
+                break;
+            /* 右下角裁剪 */
+            case self::THUMB_SOUTHEAST:
+                //计算缩放比例
+                $scale = max($width / $w, $height / $h);
+                //设置缩略图的坐标及宽度和高度
+                $w = $width / $scale;
+                $h = $height / $scale;
+                $x = $this->info['width'] - $w;
+                $y = $this->info['height'] - $h;
+                break;
+            /* 填充 */
+            case self::THUMB_FILLED:
+                //计算缩放比例
+                if ($w < $width && $h < $height) {
+                    $scale = 1;
+                } else {
+                    $scale = min($width / $w, $height / $h);
+                }
+                //设置缩略图的坐标及宽度和高度
+                $neww = $w * $scale;
+                $newh = $h * $scale;
+                $x    = $this->info['width'] - $w;
+                $y    = $this->info['height'] - $h;
+                $posx = ($width - $w * $scale) / 2;
+                $posy = ($height - $h * $scale) / 2;
+                do {
+                    //创建新图像
+                    $img = imagecreatetruecolor($width, $height);
+                    // 调整默认颜色
+                    $color = imagecolorallocate($img, 255, 255, 255);
+                    imagefill($img, 0, 0, $color);
+                    //裁剪
+                    imagecopyresampled($img, $this->im, $posx, $posy, $x, $y, $neww, $newh, $w, $h);
+                    imagedestroy($this->im); //销毁原图
+                    $this->im = $img;
+                } while (!empty($this->gif) && $this->gifNext());
+                $this->info['width']  = (int) $width;
+                $this->info['height'] = (int) $height;
+                return $this;
+            /* 固定 */
+            case self::THUMB_FIXED:
+                $x = $y = 0;
+                break;
+            default:
+                throw new ImageException('不支持的缩略图裁剪类型');
+        }
+        /* 裁剪图像 */
+        return $this->crop($w, $h, $x, $y, $width, $height);
+    }
+
+    /**
+     * 添加水印
+     *
+     * @param  string $source 水印图片路径
+     * @param int     $locate 水印位置
+     * @param int     $alpha  透明度
+     * @return $this
+     */
+    public function water($source, $locate = self::WATER_SOUTHEAST, $alpha = 100)
+    {
+        if (!is_file($source)) {
+            throw new ImageException('水印图像不存在');
+        }
+        //获取水印图像信息
+        $info = getimagesize($source);
+        if (false === $info || (IMAGETYPE_GIF === $info[2] && empty($info['bits']))) {
+            throw new ImageException('非法水印文件');
+        }
+        //创建水印图像资源
+        $fun   = 'imagecreatefrom' . image_type_to_extension($info[2], false);
+        $water = $fun($source);
+        //设定水印图像的混色模式
+        imagealphablending($water, true);
+        /* 设定水印位置 */
+        switch ($locate) {
+            /* 右下角水印 */
+            case self::WATER_SOUTHEAST:
+                $x = $this->info['width'] - $info[0];
+                $y = $this->info['height'] - $info[1];
+                break;
+            /* 左下角水印 */
+            case self::WATER_SOUTHWEST:
+                $x = 0;
+                $y = $this->info['height'] - $info[1];
+                break;
+            /* 左上角水印 */
+            case self::WATER_NORTHWEST:
+                $x = $y = 0;
+                break;
+            /* 右上角水印 */
+            case self::WATER_NORTHEAST:
+                $x = $this->info['width'] - $info[0];
+                $y = 0;
+                break;
+            /* 居中水印 */
+            case self::WATER_CENTER:
+                $x = ($this->info['width'] - $info[0]) / 2;
+                $y = ($this->info['height'] - $info[1]) / 2;
+                break;
+            /* 下居中水印 */
+            case self::WATER_SOUTH:
+                $x = ($this->info['width'] - $info[0]) / 2;
+                $y = $this->info['height'] - $info[1];
+                break;
+            /* 右居中水印 */
+            case self::WATER_EAST:
+                $x = $this->info['width'] - $info[0];
+                $y = ($this->info['height'] - $info[1]) / 2;
+                break;
+            /* 上居中水印 */
+            case self::WATER_NORTH:
+                $x = ($this->info['width'] - $info[0]) / 2;
+                $y = 0;
+                break;
+            /* 左居中水印 */
+            case self::WATER_WEST:
+                $x = 0;
+                $y = ($this->info['height'] - $info[1]) / 2;
+                break;
+            default:
+                /* 自定义水印坐标 */
+                if (is_array($locate)) {
+                    list($x, $y) = $locate;
+                } else {
+                    throw new ImageException('不支持的水印位置类型');
+                }
+        }
+        do {
+            //添加水印
+            $src = imagecreatetruecolor($info[0], $info[1]);
+            // 调整默认颜色
+            $color = imagecolorallocate($src, 255, 255, 255);
+            imagefill($src, 0, 0, $color);
+            imagecopy($src, $this->im, 0, 0, $x, $y, $info[0], $info[1]);
+            imagecopy($src, $water, 0, 0, 0, 0, $info[0], $info[1]);
+            imagecopymerge($this->im, $src, $x, $y, 0, 0, $info[0], $info[1], $alpha);
+            //销毁零时图片资源
+            imagedestroy($src);
+        } while (!empty($this->gif) && $this->gifNext());
+        //销毁水印资源
+        imagedestroy($water);
+        return $this;
+    }
+
+    /**
+     * 图像添加文字
+     *
+     * @param  string  $text   添加的文字
+     * @param  string  $font   字体路径
+     * @param  integer $size   字号
+     * @param  string  $color  文字颜色
+     * @param int      $locate 文字写入位置
+     * @param  integer $offset 文字相对当前位置的偏移量
+     * @param  integer $angle  文字倾斜角度
+     *
+     * @return $this
+     * @throws ImageException
+     */
+    public function text($text, $font, $size, $color = '#00000000',
+        $locate = self::WATER_SOUTHEAST, $offset = 0, $angle = 0) {
+
+        if (!is_file($font)) {
+            throw new ImageException("不存在的字体文件:{$font}");
+        }
+        //获取文字信息
+        $info = imagettfbbox($size, $angle, $font, $text);
+        $minx = min($info[0], $info[2], $info[4], $info[6]);
+        $maxx = max($info[0], $info[2], $info[4], $info[6]);
+        $miny = min($info[1], $info[3], $info[5], $info[7]);
+        $maxy = max($info[1], $info[3], $info[5], $info[7]);
+        /* 计算文字初始坐标和尺寸 */
+        $x = $minx;
+        $y = abs($miny);
+        $w = $maxx - $minx;
+        $h = $maxy - $miny;
+        /* 设定文字位置 */
+        switch ($locate) {
+            /* 右下角文字 */
+            case self::WATER_SOUTHEAST:
+                $x += $this->info['width'] - $w;
+                $y += $this->info['height'] - $h;
+                break;
+            /* 左下角文字 */
+            case self::WATER_SOUTHWEST:
+                $y += $this->info['height'] - $h;
+                break;
+            /* 左上角文字 */
+            case self::WATER_NORTHWEST:
+                // 起始坐标即为左上角坐标,无需调整
+                break;
+            /* 右上角文字 */
+            case self::WATER_NORTHEAST:
+                $x += $this->info['width'] - $w;
+                break;
+            /* 居中文字 */
+            case self::WATER_CENTER:
+                $x += ($this->info['width'] - $w) / 2;
+                $y += ($this->info['height'] - $h) / 2;
+                break;
+            /* 下居中文字 */
+            case self::WATER_SOUTH:
+                $x += ($this->info['width'] - $w) / 2;
+                $y += $this->info['height'] - $h;
+                break;
+            /* 右居中文字 */
+            case self::WATER_EAST:
+                $x += $this->info['width'] - $w;
+                $y += ($this->info['height'] - $h) / 2;
+                break;
+            /* 上居中文字 */
+            case self::WATER_NORTH:
+                $x += ($this->info['width'] - $w) / 2;
+                break;
+            /* 左居中文字 */
+            case self::WATER_WEST:
+                $y += ($this->info['height'] - $h) / 2;
+                break;
+            default:
+                /* 自定义文字坐标 */
+                if (is_array($locate)) {
+                    list($posx, $posy) = $locate;
+                    $x += $posx;
+                    $y += $posy;
+                } else {
+                    throw new ImageException('不支持的文字位置类型');
+                }
+        }
+        /* 设置偏移量 */
+        if (is_array($offset)) {
+            $offset        = array_map('intval', $offset);
+            list($ox, $oy) = $offset;
+        } else {
+            $offset = intval($offset);
+            $ox     = $oy     = $offset;
+        }
+        /* 设置颜色 */
+        if (is_string($color) && 0 === strpos($color, '#')) {
+            $color = str_split(substr($color, 1), 2);
+            $color = array_map('hexdec', $color);
+            if (empty($color[3]) || $color[3] > 127) {
+                $color[3] = 0;
+            }
+        } elseif (!is_array($color)) {
+            throw new ImageException('错误的颜色值');
+        }
+        do {
+            /* 写入文字 */
+            $col = imagecolorallocatealpha($this->im, $color[0], $color[1], $color[2], $color[3]);
+            imagettftext($this->im, $size, $angle, $x + $ox, $y + $oy, $col, $font, $text);
+        } while (!empty($this->gif) && $this->gifNext());
+        return $this;
+    }
+
+    /**
+     * 切换到GIF的下一帧并保存当前帧
+     */
+    protected function gifNext()
+    {
+        ob_start();
+        ob_implicit_flush(0);
+        imagegif($this->im);
+        $img = ob_get_clean();
+        $this->gif->image($img);
+        $next = $this->gif->nextImage();
+        if ($next) {
+            imagedestroy($this->im);
+            $this->im = imagecreatefromstring($next);
+            return $next;
+        } else {
+            imagedestroy($this->im);
+            $this->im = imagecreatefromstring($this->gif->image());
+            return false;
+        }
+    }
+
+    /**
+     * 析构方法,用于销毁图像资源
+     */
+    public function __destruct()
+    {
+        empty($this->im) || imagedestroy($this->im);
+    }
+
+}
diff --git a/vendor/topthink/think-image/src/image/Exception.php b/vendor/topthink/think-image/src/image/Exception.php
new file mode 100755
index 0000000..2ebafd8
--- /dev/null
+++ b/vendor/topthink/think-image/src/image/Exception.php
@@ -0,0 +1,18 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\image;
+
+
+class Exception extends \RuntimeException
+{
+
+}
\ No newline at end of file
diff --git a/vendor/topthink/think-image/src/image/gif/Decoder.php b/vendor/topthink/think-image/src/image/gif/Decoder.php
new file mode 100755
index 0000000..a563092
--- /dev/null
+++ b/vendor/topthink/think-image/src/image/gif/Decoder.php
@@ -0,0 +1,207 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\image\gif;
+
+
+class Decoder
+{
+    public $GIF_buffer = [];
+    public $GIF_arrays = [];
+    public $GIF_delays = [];
+    public $GIF_stream = "";
+    public $GIF_string = "";
+    public $GIF_bfseek = 0;
+    public $GIF_screen = [];
+    public $GIF_global = [];
+    public $GIF_sorted;
+    public $GIF_colorS;
+    public $GIF_colorC;
+    public $GIF_colorF;
+
+    /*
+    :::::::::::::::::::::::::::::::::::::::::::::::::::
+    ::
+    ::    GIFDecoder ( $GIF_pointer )
+    ::
+     */
+    public function __construct($GIF_pointer)
+    {
+        $this->GIF_stream = $GIF_pointer;
+        $this->getByte(6); // GIF89a
+        $this->getByte(7); // Logical Screen Descriptor
+        $this->GIF_screen = $this->GIF_buffer;
+        $this->GIF_colorF = $this->GIF_buffer[4] & 0x80 ? 1 : 0;
+        $this->GIF_sorted = $this->GIF_buffer[4] & 0x08 ? 1 : 0;
+        $this->GIF_colorC = $this->GIF_buffer[4] & 0x07;
+        $this->GIF_colorS = 2 << $this->GIF_colorC;
+        if (1 == $this->GIF_colorF) {
+            $this->getByte(3 * $this->GIF_colorS);
+            $this->GIF_global = $this->GIF_buffer;
+        }
+
+        for ($cycle = 1; $cycle;) {
+            if ($this->getByte(1)) {
+                switch ($this->GIF_buffer[0]) {
+                    case 0x21:
+                        $this->readExtensions();
+                        break;
+                    case 0x2C:
+                        $this->readDescriptor();
+                        break;
+                    case 0x3B:
+                        $cycle = 0;
+                        break;
+                }
+            } else {
+                $cycle = 0;
+            }
+        }
+    }
+
+    /*
+    :::::::::::::::::::::::::::::::::::::::::::::::::::
+    ::
+    ::    GIFReadExtension ( )
+    ::
+     */
+    public function readExtensions()
+    {
+        $this->getByte(1);
+        for (; ;) {
+            $this->getByte(1);
+            if (($u = $this->GIF_buffer[0]) == 0x00) {
+                break;
+            }
+            $this->getByte($u);
+            /*
+             * 07.05.2007.
+             * Implemented a new line for a new function
+             * to determine the originaly delays between
+             * frames.
+             *
+             */
+            if (4 == $u) {
+                $this->GIF_delays[] = ($this->GIF_buffer[1] | $this->GIF_buffer[2] << 8);
+            }
+        }
+    }
+
+    /*
+    :::::::::::::::::::::::::::::::::::::::::::::::::::
+    ::
+    ::    GIFReadExtension ( )
+    ::
+     */
+    public function readDescriptor()
+    {
+        $this->getByte(9);
+        $GIF_screen = $this->GIF_buffer;
+        $GIF_colorF = $this->GIF_buffer[8] & 0x80 ? 1 : 0;
+        if ($GIF_colorF) {
+            $GIF_code = $this->GIF_buffer[8] & 0x07;
+            $GIF_sort = $this->GIF_buffer[8] & 0x20 ? 1 : 0;
+        } else {
+            $GIF_code = $this->GIF_colorC;
+            $GIF_sort = $this->GIF_sorted;
+        }
+        $GIF_size = 2 << $GIF_code;
+        $this->GIF_screen[4] &= 0x70;
+        $this->GIF_screen[4] |= 0x80;
+        $this->GIF_screen[4] |= $GIF_code;
+        if ($GIF_sort) {
+            $this->GIF_screen[4] |= 0x08;
+        }
+        $this->GIF_string = "GIF87a";
+        $this->putByte($this->GIF_screen);
+        if (1 == $GIF_colorF) {
+            $this->getByte(3 * $GIF_size);
+            $this->putByte($this->GIF_buffer);
+        } else {
+            $this->putByte($this->GIF_global);
+        }
+        $this->GIF_string .= chr(0x2C);
+        $GIF_screen[8] &= 0x40;
+        $this->putByte($GIF_screen);
+        $this->getByte(1);
+        $this->putByte($this->GIF_buffer);
+        for (; ;) {
+            $this->getByte(1);
+            $this->putByte($this->GIF_buffer);
+            if (($u = $this->GIF_buffer[0]) == 0x00) {
+                break;
+            }
+            $this->getByte($u);
+            $this->putByte($this->GIF_buffer);
+        }
+        $this->GIF_string .= chr(0x3B);
+        /*
+        Add frames into $GIF_stream array...
+         */
+        $this->GIF_arrays[] = $this->GIF_string;
+    }
+
+    /*
+    :::::::::::::::::::::::::::::::::::::::::::::::::::
+    ::
+    ::    GIFGetByte ( $len )
+    ::
+    */
+    public function getByte($len)
+    {
+        $this->GIF_buffer = [];
+        for ($i = 0; $i < $len; $i++) {
+            if ($this->GIF_bfseek > strlen($this->GIF_stream)) {
+                return 0;
+            }
+            $this->GIF_buffer[] = ord($this->GIF_stream{$this->GIF_bfseek++});
+        }
+        return 1;
+    }
+
+    /*
+    :::::::::::::::::::::::::::::::::::::::::::::::::::
+    ::
+    ::    GIFPutByte ( $bytes )
+    ::
+     */
+    public function putByte($bytes)
+    {
+        for ($i = 0; $i < count($bytes); $i++) {
+            $this->GIF_string .= chr($bytes[$i]);
+        }
+    }
+
+    /*
+    :::::::::::::::::::::::::::::::::::::::::::::::::::
+    ::
+    ::    PUBLIC FUNCTIONS
+    ::
+    ::
+    ::    GIFGetFrames ( )
+    ::
+     */
+    public function getFrames()
+    {
+        return ($this->GIF_arrays);
+    }
+
+    /*
+    :::::::::::::::::::::::::::::::::::::::::::::::::::
+    ::
+    ::    GIFGetDelays ( )
+    ::
+     */
+    public function getDelays()
+    {
+        return ($this->GIF_delays);
+    }
+}
\ No newline at end of file
diff --git a/vendor/topthink/think-image/src/image/gif/Encoder.php b/vendor/topthink/think-image/src/image/gif/Encoder.php
new file mode 100755
index 0000000..688f7a0
--- /dev/null
+++ b/vendor/topthink/think-image/src/image/gif/Encoder.php
@@ -0,0 +1,222 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+namespace think\image\gif;
+
+class Encoder
+{
+    public $GIF = "GIF89a"; /* GIF header 6 bytes    */
+    public $VER = "GIFEncoder V2.05"; /* Encoder version        */
+    public $BUF = [];
+    public $LOP = 0;
+    public $DIS = 2;
+    public $COL = -1;
+    public $IMG = -1;
+    public $ERR = [
+        'ERR00' => "Does not supported function for only one image!",
+        'ERR01' => "Source is not a GIF image!",
+        'ERR02' => "Unintelligible flag ",
+        'ERR03' => "Does not make animation from animated GIF source",
+    ];
+
+    /*
+    :::::::::::::::::::::::::::::::::::::::::::::::::::
+    ::
+    ::    GIFEncoder...
+    ::
+     */
+    public function __construct(
+        $GIF_src, $GIF_dly, $GIF_lop, $GIF_dis,
+        $GIF_red, $GIF_grn, $GIF_blu, $GIF_mod
+    )
+    {
+        if (!is_array($GIF_src)) {
+            printf("%s: %s", $this->VER, $this->ERR['ERR00']);
+            exit(0);
+        }
+        $this->LOP = ($GIF_lop > -1) ? $GIF_lop : 0;
+        $this->DIS = ($GIF_dis > -1) ? (($GIF_dis < 3) ? $GIF_dis : 3) : 2;
+        $this->COL = ($GIF_red > -1 && $GIF_grn > -1 && $GIF_blu > -1) ?
+            ($GIF_red | ($GIF_grn << 8) | ($GIF_blu << 16)) : -1;
+        for ($i = 0; $i < count($GIF_src); $i++) {
+            if (strtolower($GIF_mod) == "url") {
+                $this->BUF[] = fread(fopen($GIF_src[$i], "rb"), filesize($GIF_src[$i]));
+            } else if (strtolower($GIF_mod) == "bin") {
+                $this->BUF[] = $GIF_src[$i];
+            } else {
+                printf("%s: %s ( %s )!", $this->VER, $this->ERR['ERR02'], $GIF_mod);
+                exit(0);
+            }
+            if (substr($this->BUF[$i], 0, 6) != "GIF87a" && substr($this->BUF[$i], 0, 6) != "GIF89a") {
+                printf("%s: %d %s", $this->VER, $i, $this->ERR['ERR01']);
+                exit(0);
+            }
+            for ($j = (13 + 3 * (2 << (ord($this->BUF[$i]{10}) & 0x07))), $k = true; $k; $j++) {
+                switch ($this->BUF[$i]{$j}) {
+                    case "!":
+                        if ((substr($this->BUF[$i], ($j + 3), 8)) == "NETSCAPE") {
+                            printf("%s: %s ( %s source )!", $this->VER, $this->ERR['ERR03'], ($i + 1));
+                            exit(0);
+                        }
+                        break;
+                    case ";":
+                        $k = false;
+                        break;
+                }
+            }
+        }
+        $this->addHeader();
+        for ($i = 0; $i < count($this->BUF); $i++) {
+            $this->addFrames($i, $GIF_dly[$i]);
+        }
+        $this->addFooter();
+    }
+
+    /*
+    :::::::::::::::::::::::::::::::::::::::::::::::::::
+    ::
+    ::    GIFAddHeader...
+    ::
+     */
+    public function addHeader()
+    {
+        if (ord($this->BUF[0]{10}) & 0x80) {
+            $cmap = 3 * (2 << (ord($this->BUF[0]{10}) & 0x07));
+            $this->GIF .= substr($this->BUF[0], 6, 7);
+            $this->GIF .= substr($this->BUF[0], 13, $cmap);
+            $this->GIF .= "!\377\13NETSCAPE2.0\3\1" . $this->word($this->LOP) . "\0";
+        }
+    }
+
+    /*
+    :::::::::::::::::::::::::::::::::::::::::::::::::::
+    ::
+    ::    GIFAddFrames...
+    ::
+     */
+    public function addFrames($i, $d)
+    {
+        $Locals_img = '';
+        $Locals_str = 13 + 3 * (2 << (ord($this->BUF[$i]{10}) & 0x07));
+        $Locals_end = strlen($this->BUF[$i]) - $Locals_str - 1;
+        $Locals_tmp = substr($this->BUF[$i], $Locals_str, $Locals_end);
+        $Global_len = 2 << (ord($this->BUF[0]{10}) & 0x07);
+        $Locals_len = 2 << (ord($this->BUF[$i]{10}) & 0x07);
+        $Global_rgb = substr($this->BUF[0], 13,
+            3 * (2 << (ord($this->BUF[0]{10}) & 0x07)));
+        $Locals_rgb = substr($this->BUF[$i], 13,
+            3 * (2 << (ord($this->BUF[$i]{10}) & 0x07)));
+        $Locals_ext = "!\xF9\x04" . chr(($this->DIS << 2) + 0) .
+            chr(($d >> 0) & 0xFF) . chr(($d >> 8) & 0xFF) . "\x0\x0";
+        if ($this->COL > -1 && ord($this->BUF[$i]{10}) & 0x80) {
+            for ($j = 0; $j < (2 << (ord($this->BUF[$i]{10}) & 0x07)); $j++) {
+                if (
+                    ord($Locals_rgb{3 * $j + 0}) == (($this->COL >> 16) & 0xFF) &&
+                    ord($Locals_rgb{3 * $j + 1}) == (($this->COL >> 8) & 0xFF) &&
+                    ord($Locals_rgb{3 * $j + 2}) == (($this->COL >> 0) & 0xFF)
+                ) {
+                    $Locals_ext = "!\xF9\x04" . chr(($this->DIS << 2) + 1) .
+                        chr(($d >> 0) & 0xFF) . chr(($d >> 8) & 0xFF) . chr($j) . "\x0";
+                    break;
+                }
+            }
+        }
+        switch ($Locals_tmp{0}) {
+            case "!":
+                /**
+                 * @var string $Locals_img ;
+                 */
+                $Locals_img = substr($Locals_tmp, 8, 10);
+                $Locals_tmp = substr($Locals_tmp, 18, strlen($Locals_tmp) - 18);
+                break;
+            case ",":
+                $Locals_img = substr($Locals_tmp, 0, 10);
+                $Locals_tmp = substr($Locals_tmp, 10, strlen($Locals_tmp) - 10);
+                break;
+        }
+        if (ord($this->BUF[$i]{10}) & 0x80 && $this->IMG > -1) {
+            if ($Global_len == $Locals_len) {
+                if ($this->blockCompare($Global_rgb, $Locals_rgb, $Global_len)) {
+                    $this->GIF .= ($Locals_ext . $Locals_img . $Locals_tmp);
+                } else {
+                    $byte = ord($Locals_img{9});
+                    $byte |= 0x80;
+                    $byte &= 0xF8;
+                    $byte |= (ord($this->BUF[0]{10}) & 0x07);
+                    $Locals_img{9} = chr($byte);
+                    $this->GIF .= ($Locals_ext . $Locals_img . $Locals_rgb . $Locals_tmp);
+                }
+            } else {
+                $byte = ord($Locals_img{9});
+                $byte |= 0x80;
+                $byte &= 0xF8;
+                $byte |= (ord($this->BUF[$i]{10}) & 0x07);
+                $Locals_img{9} = chr($byte);
+                $this->GIF .= ($Locals_ext . $Locals_img . $Locals_rgb . $Locals_tmp);
+            }
+        } else {
+            $this->GIF .= ($Locals_ext . $Locals_img . $Locals_tmp);
+        }
+        $this->IMG = 1;
+    }
+
+    /*
+    :::::::::::::::::::::::::::::::::::::::::::::::::::
+    ::
+    ::    GIFAddFooter...
+    ::
+     */
+    public function addFooter()
+    {
+        $this->GIF .= ";";
+    }
+
+    /*
+    :::::::::::::::::::::::::::::::::::::::::::::::::::
+    ::
+    ::    GIFBlockCompare...
+    ::
+     */
+    public function blockCompare($GlobalBlock, $LocalBlock, $Len)
+    {
+        for ($i = 0; $i < $Len; $i++) {
+            if (
+                $GlobalBlock{3 * $i + 0} != $LocalBlock{3 * $i + 0} ||
+                $GlobalBlock{3 * $i + 1} != $LocalBlock{3 * $i + 1} ||
+                $GlobalBlock{3 * $i + 2} != $LocalBlock{3 * $i + 2}
+            ) {
+                return (0);
+            }
+        }
+        return (1);
+    }
+
+    /*
+    :::::::::::::::::::::::::::::::::::::::::::::::::::
+    ::
+    ::    GIFWord...
+    ::
+     */
+    public function word($int)
+    {
+        return (chr($int & 0xFF) . chr(($int >> 8) & 0xFF));
+    }
+
+    /*
+    :::::::::::::::::::::::::::::::::::::::::::::::::::
+    ::
+    ::    GetAnimation...
+    ::
+     */
+    public function getAnimation()
+    {
+        return ($this->GIF);
+    }
+}
\ No newline at end of file
diff --git a/vendor/topthink/think-image/src/image/gif/Gif.php b/vendor/topthink/think-image/src/image/gif/Gif.php
new file mode 100755
index 0000000..b890915
--- /dev/null
+++ b/vendor/topthink/think-image/src/image/gif/Gif.php
@@ -0,0 +1,88 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\image\gif;
+
+class Gif
+{
+    /**
+     * GIF帧列表
+     *
+     * @var array
+     */
+    private $frames = [];
+    /**
+     * 每帧等待时间列表
+     *
+     * @var array
+     */
+    private $delays = [];
+
+    /**
+     * 构造方法,用于解码GIF图片
+     *
+     * @param string $src GIF图片数据
+     * @param string $mod 图片数据类型
+     * @throws \Exception
+     */
+    public function __construct($src = null, $mod = 'url')
+    {
+        if (!is_null($src)) {
+            if ('url' == $mod && is_file($src)) {
+                $src = file_get_contents($src);
+            }
+            /* 解码GIF图片 */
+            try {
+                $de           = new Decoder($src);
+                $this->frames = $de->getFrames();
+                $this->delays = $de->getDelays();
+            } catch (\Exception $e) {
+                throw new \Exception("解码GIF图片出错");
+            }
+        }
+    }
+
+    /**
+     * 设置或获取当前帧的数据
+     *
+     * @param  string $stream 二进制数据流
+     * @return mixed        获取到的数据
+     */
+    public function image($stream = null)
+    {
+        if (is_null($stream)) {
+            $current = current($this->frames);
+            return false === $current ? reset($this->frames) : $current;
+        }
+        $this->frames[key($this->frames)] = $stream;
+    }
+
+    /**
+     * 将当前帧移动到下一帧
+     *
+     * @return string 当前帧数据
+     */
+    public function nextImage()
+    {
+        return next($this->frames);
+    }
+
+    /**
+     * 编码并保存当前GIF图片
+     *
+     * @param  string $pathname 图片名称
+     */
+    public function save($pathname)
+    {
+        $gif = new Encoder($this->frames, $this->delays, 0, 2, 0, 0, 0, 'bin');
+        file_put_contents($pathname, $gif->getAnimation());
+    }
+}
\ No newline at end of file
diff --git a/vendor/topthink/think-image/tests/CropTest.php b/vendor/topthink/think-image/tests/CropTest.php
new file mode 100755
index 0000000..26a0531
--- /dev/null
+++ b/vendor/topthink/think-image/tests/CropTest.php
@@ -0,0 +1,67 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+namespace tests;
+
+use think\Image;
+
+class CropTest extends TestCase
+{
+    public function testJpeg()
+    {
+        $pathname = TEST_PATH . 'tmp/crop.jpg';
+        $image    = Image::open($this->getJpeg());
+
+        $image->crop(200, 200, 100, 100, 300, 300)->save($pathname);
+
+        $this->assertEquals(300, $image->width());
+        $this->assertEquals(300, $image->height());
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+    }
+
+    public function testPng()
+    {
+        $pathname = TEST_PATH . 'tmp/crop.png';
+        $image    = Image::open($this->getPng());
+
+        $image->crop(200, 200, 100, 100, 300, 300)->save($pathname);
+
+        $this->assertEquals(300, $image->width());
+        $this->assertEquals(300, $image->height());
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+    }
+
+    public function testGif()
+    {
+        $pathname = TEST_PATH . 'tmp/crop.gif';
+        $image    = Image::open($this->getGif());
+
+        $image->crop(200, 200, 100, 100, 300, 300)->save($pathname);
+
+        $this->assertEquals(300, $image->width());
+        $this->assertEquals(300, $image->height());
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+    }
+}
\ No newline at end of file
diff --git a/vendor/topthink/think-image/tests/FlipTest.php b/vendor/topthink/think-image/tests/FlipTest.php
new file mode 100755
index 0000000..bf7a5e2
--- /dev/null
+++ b/vendor/topthink/think-image/tests/FlipTest.php
@@ -0,0 +1,43 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+namespace tests;
+
+use think\Image;
+
+class FlipTest extends TestCase
+{
+    public function testJpeg()
+    {
+        $pathname = TEST_PATH . 'tmp/flip.jpg';
+        $image    = Image::open($this->getJpeg());
+        $image->flip()->save($pathname);
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+    }
+
+
+    public function testGif()
+    {
+        $pathname = TEST_PATH . 'tmp/flip.gif';
+        $image    = Image::open($this->getGif());
+        $image->flip(Image::FLIP_Y)->save($pathname);
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+    }
+}
\ No newline at end of file
diff --git a/vendor/topthink/think-image/tests/InfoTest.php b/vendor/topthink/think-image/tests/InfoTest.php
new file mode 100755
index 0000000..22132ca
--- /dev/null
+++ b/vendor/topthink/think-image/tests/InfoTest.php
@@ -0,0 +1,60 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+namespace tests;
+
+use think\Image;
+
+class InfoTest extends TestCase
+{
+
+    public function testOpen()
+    {
+        $this->setExpectedException("\\think\\image\\Exception");
+        Image::open('');
+    }
+
+    public function testIllegal()
+    {
+        $this->setExpectedException("\\think\\image\\Exception", 'Illegal image file');
+        Image::open(TEST_PATH . 'images/test.bmp');
+    }
+
+    public function testJpeg()
+    {
+        $image = Image::open($this->getJpeg());
+        $this->assertEquals(800, $image->width());
+        $this->assertEquals(600, $image->height());
+        $this->assertEquals('jpeg', $image->type());
+        $this->assertEquals('image/jpeg', $image->mime());
+        $this->assertEquals([800, 600], $image->size());
+    }
+
+
+    public function testPng()
+    {
+        $image = Image::open($this->getPng());
+        $this->assertEquals(800, $image->width());
+        $this->assertEquals(600, $image->height());
+        $this->assertEquals('png', $image->type());
+        $this->assertEquals('image/png', $image->mime());
+        $this->assertEquals([800, 600], $image->size());
+    }
+
+    public function testGif()
+    {
+        $image = Image::open($this->getGif());
+        $this->assertEquals(380, $image->width());
+        $this->assertEquals(216, $image->height());
+        $this->assertEquals('gif', $image->type());
+        $this->assertEquals('image/gif', $image->mime());
+        $this->assertEquals([380, 216], $image->size());
+    }
+}
\ No newline at end of file
diff --git a/vendor/topthink/think-image/tests/RotateTest.php b/vendor/topthink/think-image/tests/RotateTest.php
new file mode 100755
index 0000000..56572d2
--- /dev/null
+++ b/vendor/topthink/think-image/tests/RotateTest.php
@@ -0,0 +1,42 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+namespace tests;
+
+use think\Image;
+
+class RotateTest extends TestCase
+{
+    public function testJpeg()
+    {
+        $pathname = TEST_PATH . 'tmp/rotate.jpg';
+        $image    = Image::open($this->getJpeg());
+        $image->rotate(90)->save($pathname);
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+    }
+
+    public function testGif()
+    {
+        $pathname = TEST_PATH . 'tmp/rotate.gif';
+        $image    = Image::open($this->getGif());
+        $image->rotate(90)->save($pathname);
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+    }
+}
\ No newline at end of file
diff --git a/vendor/topthink/think-image/tests/TestCase.php b/vendor/topthink/think-image/tests/TestCase.php
new file mode 100755
index 0000000..89ba1b4
--- /dev/null
+++ b/vendor/topthink/think-image/tests/TestCase.php
@@ -0,0 +1,33 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace tests;
+
+use think\File;
+
+abstract class TestCase extends \PHPUnit_Framework_TestCase
+{
+
+    protected function getJpeg()
+    {
+        return new File(TEST_PATH . 'images/test.jpg');
+    }
+
+    protected function getPng()
+    {
+        return new File(TEST_PATH . 'images/test.png');
+    }
+
+    protected function getGif()
+    {
+        return new File(TEST_PATH . 'images/test.gif');
+    }
+}
\ No newline at end of file
diff --git a/vendor/topthink/think-image/tests/TextTest.php b/vendor/topthink/think-image/tests/TextTest.php
new file mode 100755
index 0000000..04506a2
--- /dev/null
+++ b/vendor/topthink/think-image/tests/TextTest.php
@@ -0,0 +1,58 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+namespace tests;
+
+use think\Image;
+
+class TextTest extends TestCase
+{
+    public function testJpeg()
+    {
+        $pathname = TEST_PATH . 'tmp/text.jpg';
+        $image    = Image::open($this->getJpeg());
+
+        $image->text('test', TEST_PATH . 'images/test.ttf', 12)->save($pathname);
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+    }
+
+    public function testPng()
+    {
+        $pathname = TEST_PATH . 'tmp/text.png';
+        $image    = Image::open($this->getPng());
+
+        $image->text('test', TEST_PATH . 'images/test.ttf', 12)->save($pathname);
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+    }
+
+    public function testGif()
+    {
+        $pathname = TEST_PATH . 'tmp/text.gif';
+        $image    = Image::open($this->getGif());
+
+        $image->text('test', TEST_PATH . 'images/test.ttf', 12)->save($pathname);
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+    }
+}
\ No newline at end of file
diff --git a/vendor/topthink/think-image/tests/ThumbTest.php b/vendor/topthink/think-image/tests/ThumbTest.php
new file mode 100755
index 0000000..07113c8
--- /dev/null
+++ b/vendor/topthink/think-image/tests/ThumbTest.php
@@ -0,0 +1,284 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+namespace tests;
+
+use think\Image;
+
+class ThumbTest extends TestCase
+{
+    public function testJpeg()
+    {
+        $pathname = TEST_PATH . 'tmp/thumb.jpg';
+        
+        //1
+        $image    = Image::open($this->getJpeg());
+
+        $image->thumb(200, 200, Image::THUMB_CENTER)->save($pathname);
+
+        $this->assertEquals(200, $image->width());
+        $this->assertEquals(200, $image->height());
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+
+        //2
+        $image    = Image::open($this->getJpeg());
+
+        $image->thumb(200, 200, Image::THUMB_SCALING)->save($pathname);
+
+        $this->assertEquals(200, $image->width());
+        $this->assertEquals(150, $image->height());
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+
+        //3
+        $image    = Image::open($this->getJpeg());
+
+        $image->thumb(200, 200, Image::THUMB_FILLED)->save($pathname);
+
+        $this->assertEquals(200, $image->width());
+        $this->assertEquals(200, $image->height());
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+
+        //4
+        $image    = Image::open($this->getJpeg());
+
+        $image->thumb(200, 200, Image::THUMB_NORTHWEST)->save($pathname);
+
+        $this->assertEquals(200, $image->width());
+        $this->assertEquals(200, $image->height());
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+
+        //5
+        $image    = Image::open($this->getJpeg());
+
+        $image->thumb(200, 200, Image::THUMB_SOUTHEAST)->save($pathname);
+
+        $this->assertEquals(200, $image->width());
+        $this->assertEquals(200, $image->height());
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+
+        //6
+        $image    = Image::open($this->getJpeg());
+
+        $image->thumb(200, 200, Image::THUMB_FIXED)->save($pathname);
+
+        $this->assertEquals(200, $image->width());
+        $this->assertEquals(200, $image->height());
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+    }
+
+
+    public function testPng()
+    {
+        $pathname = TEST_PATH . 'tmp/thumb.png';
+
+        //1
+        $image    = Image::open($this->getPng());
+
+        $image->thumb(200, 200, Image::THUMB_CENTER)->save($pathname);
+
+        $this->assertEquals(200, $image->width());
+        $this->assertEquals(200, $image->height());
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+
+        //2
+        $image    = Image::open($this->getPng());
+
+        $image->thumb(200, 200, Image::THUMB_SCALING)->save($pathname);
+
+        $this->assertEquals(200, $image->width());
+        $this->assertEquals(150, $image->height());
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+
+        //3
+        $image    = Image::open($this->getPng());
+
+        $image->thumb(200, 200, Image::THUMB_FILLED)->save($pathname);
+
+        $this->assertEquals(200, $image->width());
+        $this->assertEquals(200, $image->height());
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+
+        //4
+        $image    = Image::open($this->getPng());
+
+        $image->thumb(200, 200, Image::THUMB_NORTHWEST)->save($pathname);
+
+        $this->assertEquals(200, $image->width());
+        $this->assertEquals(200, $image->height());
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+
+        //5
+        $image    = Image::open($this->getPng());
+
+        $image->thumb(200, 200, Image::THUMB_SOUTHEAST)->save($pathname);
+
+        $this->assertEquals(200, $image->width());
+        $this->assertEquals(200, $image->height());
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+
+        //6
+        $image    = Image::open($this->getPng());
+
+        $image->thumb(200, 200, Image::THUMB_FIXED)->save($pathname);
+
+        $this->assertEquals(200, $image->width());
+        $this->assertEquals(200, $image->height());
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+    }
+
+    public function testGif()
+    {
+        $pathname = TEST_PATH . 'tmp/thumb.gif';
+
+        //1
+        $image    = Image::open($this->getGif());
+
+        $image->thumb(200, 200, Image::THUMB_CENTER)->save($pathname);
+
+        $this->assertEquals(200, $image->width());
+        $this->assertEquals(200, $image->height());
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+
+        //2
+        $image    = Image::open($this->getGif());
+
+        $image->thumb(200, 200, Image::THUMB_SCALING)->save($pathname);
+
+        $this->assertEquals(200, $image->width());
+        $this->assertEquals(113, $image->height());
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+
+        //3
+        $image    = Image::open($this->getGif());
+
+        $image->thumb(200, 200, Image::THUMB_FILLED)->save($pathname);
+
+        $this->assertEquals(200, $image->width());
+        $this->assertEquals(200, $image->height());
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+
+        //4
+        $image    = Image::open($this->getGif());
+
+        $image->thumb(200, 200, Image::THUMB_NORTHWEST)->save($pathname);
+
+        $this->assertEquals(200, $image->width());
+        $this->assertEquals(200, $image->height());
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+
+        //5
+        $image    = Image::open($this->getGif());
+
+        $image->thumb(200, 200, Image::THUMB_SOUTHEAST)->save($pathname);
+
+        $this->assertEquals(200, $image->width());
+        $this->assertEquals(200, $image->height());
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+
+        //6
+        $image    = Image::open($this->getGif());
+
+        $image->thumb(200, 200, Image::THUMB_FIXED)->save($pathname);
+
+        $this->assertEquals(200, $image->width());
+        $this->assertEquals(200, $image->height());
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+    }
+}
\ No newline at end of file
diff --git a/vendor/topthink/think-image/tests/WaterTest.php b/vendor/topthink/think-image/tests/WaterTest.php
new file mode 100755
index 0000000..b6a2bcc
--- /dev/null
+++ b/vendor/topthink/think-image/tests/WaterTest.php
@@ -0,0 +1,58 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+namespace tests;
+
+use think\Image;
+
+class WaterTest extends TestCase
+{
+    public function testJpeg()
+    {
+        $pathname = TEST_PATH . 'tmp/water.jpg';
+        $image    = Image::open($this->getJpeg());
+
+        $image->water(TEST_PATH . 'images/test.gif')->save($pathname);
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+    }
+
+    public function testPng()
+    {
+        $pathname = TEST_PATH . 'tmp/water.png';
+        $image    = Image::open($this->getPng());
+
+        $image->water(TEST_PATH . 'images/test.gif')->save($pathname);
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+    }
+
+    public function testGif()
+    {
+        $pathname = TEST_PATH . 'tmp/water.gif';
+        $image    = Image::open($this->getGif());
+
+        $image->water(TEST_PATH . 'images/test.jpg')->save($pathname);
+
+        $file = new \SplFileInfo($pathname);
+
+        $this->assertTrue($file->isFile());
+
+        @unlink($pathname);
+    }
+}
\ No newline at end of file
diff --git a/vendor/topthink/think-image/tests/autoload.php b/vendor/topthink/think-image/tests/autoload.php
new file mode 100755
index 0000000..f2e8aae
--- /dev/null
+++ b/vendor/topthink/think-image/tests/autoload.php
@@ -0,0 +1,15 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+define('TEST_PATH', __DIR__ . '/');
+// 加载框架基础文件
+require __DIR__ . '/../thinkphp/base.php';
+\think\Loader::addNamespace('tests', TEST_PATH);
+\think\Loader::addNamespace('think', __DIR__ . '/../src/');
\ No newline at end of file
diff --git a/vendor/topthink/think-image/tests/images/test.bmp b/vendor/topthink/think-image/tests/images/test.bmp
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/vendor/topthink/think-image/tests/images/test.bmp
diff --git a/vendor/topthink/think-image/tests/images/test.gif b/vendor/topthink/think-image/tests/images/test.gif
new file mode 100755
index 0000000..c6d5472
--- /dev/null
+++ b/vendor/topthink/think-image/tests/images/test.gif
Binary files differ
diff --git a/vendor/topthink/think-image/tests/images/test.jpg b/vendor/topthink/think-image/tests/images/test.jpg
new file mode 100755
index 0000000..4bb6549
--- /dev/null
+++ b/vendor/topthink/think-image/tests/images/test.jpg
Binary files differ
diff --git a/vendor/topthink/think-image/tests/images/test.png b/vendor/topthink/think-image/tests/images/test.png
new file mode 100755
index 0000000..f4830e3
--- /dev/null
+++ b/vendor/topthink/think-image/tests/images/test.png
Binary files differ
diff --git a/vendor/topthink/think-image/tests/images/test.ttf b/vendor/topthink/think-image/tests/images/test.ttf
new file mode 100755
index 0000000..4f985c8
--- /dev/null
+++ b/vendor/topthink/think-image/tests/images/test.ttf
Binary files differ
diff --git a/vendor/topthink/think-image/tests/tmp/.gitignore b/vendor/topthink/think-image/tests/tmp/.gitignore
new file mode 100755
index 0000000..c96a04f
--- /dev/null
+++ b/vendor/topthink/think-image/tests/tmp/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
\ No newline at end of file
diff --git a/vendor/topthink/think-migration/.gitignore b/vendor/topthink/think-migration/.gitignore
new file mode 100755
index 0000000..30babff
--- /dev/null
+++ b/vendor/topthink/think-migration/.gitignore
@@ -0,0 +1,3 @@
+/vendor/
+.idea
+composer.lock
diff --git a/vendor/topthink/think-migration/LICENSE b/vendor/topthink/think-migration/LICENSE
new file mode 100755
index 0000000..2c76a5a
--- /dev/null
+++ b/vendor/topthink/think-migration/LICENSE
@@ -0,0 +1,32 @@
+
+ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
+版权所有Copyright © 2006-2016 by ThinkPHP (http://thinkphp.cn)
+All rights reserved。
+ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
+
+Apache Licence是著名的非盈利开源组织Apache采用的协议。
+该协议和BSD类似,鼓励代码共享和尊重原作者的著作权,
+允许代码修改,再作为开源或商业软件发布。需要满足
+的条件: 
+1. 需要给代码的用户一份Apache Licence ;
+2. 如果你修改了代码,需要在被修改的文件中说明;
+3. 在延伸的代码中(修改和有源代码衍生的代码中)需要
+带有原来代码中的协议,商标,专利声明和其他原来作者规
+定需要包含的说明;
+4. 如果再发布的产品中包含一个Notice文件,则在Notice文
+件中需要带有本协议内容。你可以在Notice中增加自己的
+许可,但不可以表现为对Apache Licence构成更改。 
+具体的协议参考:http://www.apache.org/licenses/LICENSE-2.0
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/vendor/topthink/think-migration/README.md b/vendor/topthink/think-migration/README.md
new file mode 100755
index 0000000..39e6584
--- /dev/null
+++ b/vendor/topthink/think-migration/README.md
@@ -0,0 +1,6 @@
+# thinkphp6 数据库迁移工具
+
+## 安装
+~~~
+composer require topthink/think-migration
+~~~
diff --git a/vendor/topthink/think-migration/composer.json b/vendor/topthink/think-migration/composer.json
new file mode 100755
index 0000000..78af31f
--- /dev/null
+++ b/vendor/topthink/think-migration/composer.json
@@ -0,0 +1,34 @@
+{
+    "name": "topthink/think-migration",
+    "authors": [
+        {
+            "name": "yunwuxin",
+            "email": "448901948@qq.com"
+        }
+    ],
+    "license": "Apache-2.0",
+    "autoload": {
+        "psr-4": {
+            "Phinx\\": "phinx/src/Phinx",
+            "think\\migration\\": "src"
+        }
+    },
+    "extra": {
+        "think": {
+            "services": [
+                "think\\migration\\Service"
+            ]
+        }
+    },
+    "require": {
+        "topthink/framework": "^6.0",
+        "topthink/think-helper": "^3.0.3"
+    },
+    "minimum-stability": "dev",
+    "suggest": {
+        "fzaninotto/faker": "Required to use the factory builder (^1.8)."
+    },
+    "require-dev": {
+        "fzaninotto/faker": "^1.8"
+    }
+}
diff --git a/vendor/topthink/think-migration/phinx/CHANGELOG.md b/vendor/topthink/think-migration/phinx/CHANGELOG.md
new file mode 100755
index 0000000..df1c2e4
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/CHANGELOG.md
@@ -0,0 +1,370 @@
+# Version History
+
+**0.6.5** (Thursday, 27 October 2016)
+
+* Documentation updates
+* Pull requests
+  * [#831](https://github.com/robmorgan/phinx/pull/831) Typos
+  * [#929](https://github.com/robmorgan/phinx/pull/929) Support glob brace for seed paths
+  * [#949](https://github.com/robmorgan/phinx/pull/949) Fix for Config::getMigrationBaseClassName
+  * [#958](https://github.com/robmorgan/phinx/pull/958) Allow console input to be used within adapters
+
+**0.6.4** (Wednesday, 27th July 2016)
+
+* Documentation updates
+* Pull requests
+  * [#909](https://github.com/robmorgan/phinx/pull/909) Declare test class properties
+  * [#910](https://github.com/robmorgan/phinx/pull/910), [#916](https://github.com/robmorgan/phinx/pull/916) Remove unused variables
+  * [#912](https://github.com/robmorgan/phinx/pull/912) ConfigInterface usage consistency
+  * [#914](https://github.com/robmorgan/phinx/pull/914) Set return values and @return documentation
+  * [#918](https://github.com/robmorgan/phinx/pull/918) Docblock correction for Phinx\Migration\Manager::executeSeed()
+  * [#921](https://github.com/robmorgan/phinx/pull/921) Add Phinx\Wrapper\TextWrapper::getSeed()
+* Bug fixes
+  * [#908](https://github.com/robmorgan/phinx/pull/908) Fix setting options for Column, ForeignKey and Index
+  * [#922](https://github.com/robmorgan/phinx/pull/922) SQLite adapter drops table on changeColumn if there's a foreign key
+
+**0.6.3** (Monday, 18th July 2016)
+
+* New features
+  * [#707](https://github.com/robmorgan/phinx/pull/707/files) Add arguments for timestamps columns names
+* Documentation cleanup
+* Bug fixes
+  * [#884](https://github.com/robmorgan/phinx/pull/884) Only rollback 1 migration when only 2 migrations exist
+  * Input and Output are now correctly supplied to migration template creation classes
+
+**0.6.2** (Thursday, 23rd June 2016)
+
+* Fix breakpoint support for Postgres
+* HHVM now passes all tests
+
+**0.6.1** (Tuesday, 21st June 2016)
+
+* Fix rollback when only 1 migration
+
+**0.6.0** (Tuesday, 21st June 2016)
+
+* Backward incompatibility - see [UPGRADE_0.6](UPGRADE_0.6.md) document
+* Introduce Input and Output access to migrations and template creation
+* New breakpoint command
+* Moved version history to this CHANGELOG.md document
+* More tests
+
+**0.5.5** (Friday, 17th May 2016)
+
+* Fix support for running multiple seeders 
+* Bug fix for migration template source - defaults and command line
+* Bug fixes
+
+**0.5.4** (Monday, 25th April 2016)
+
+* Added support for running multiple seeders
+* Use `GLOB_BRACE` when finding migrations only if its available
+* Added support for MySQL `VARBINARY` column type
+* Minor bug fixes
+
+**0.5.3** (Monday, 7th March 2016)
+
+* Critical fix: allow `migration_name` to be `null`. Introduced in 0.5.2
+* Status command now shows migration start and end times
+* Bug fix for rolling back by date
+* Documentation improvements
+
+**0.5.2** (Tuesday, 1st March 2016)
+
+* Status command now includes missing migration names
+* Added support for Postgres table comments
+* Added `insert()` for the TablePrefixAdapter
+* Fixed the migration verbosity flag
+* Added MySQL 5.7 JSON support
+* Added support for MySQL `FULLTEXT` indexes
+* Postgres now supports `BIGSERIAL` for primary keys
+* Added support for MySQL index limits
+* Initial support for multiple migration paths (using glob)
+* Documentation improvements
+* Unit test enhancements
+
+**0.5.1** (Wednesday, 30th December 2015)
+
+* **PHP 5.3 is no longer supported!**
+* Add support for Symfony 3.0 components
+* Ensure that the `status` command returns the correct exit code
+* Allow `$version` to be passed into templates
+* Support for MySQL `YEAR` column type
+* Multiple documentation updates and corrections
+
+**0.5.0** (Monday, 30th November 2015)
+
+* Support for seeding data after database creation
+* The migration and seed directories are now nested under `db` by default
+* Moved `Phinx\Migration\Util` to `Phinx\Util\Util`
+* All `insert()` methods now have a slightly different method signature
+* Fixed key/insert operations for MySQL
+* Introduced `AdapterInterface::hasIndexByName()`
+* Improved `dropForeignKey()` handling for SQLite
+* Added support for the MySQL `binary` datatype. BLOBs now use the proper type.
+* The status command shows a count of pending migrations in JSON output
+* We are now testing against PHP 7
+
+**0.4.6** (Friday, 11th September 2015)
+
+* You can now set custom migration templates in the config files
+* Support for MySQL unsigned booleans
+* Support for Postgres `smallint` column types
+* Support for `AFTER` when using `changeColumn()` with MySQL
+* Support for `precision` and `scale` when using the Postgres `decimal` type
+* Fixed a bug where duplicate migration names could be used
+* The schema table is now created with a primary key
+* Fixed issues when using the MySQL `STRICT_TRANS_TABLE` mode
+* Improved the docs in the default migration template
+* Made Box PHAR ignore the bundled `phinx.yml` configuration file
+* Updated Box installer URL
+* Internal code improvements
+* Documentation improvements
+
+**0.4.5** (Tuesday, 1st September 2015)
+
+* The rollback command now supports a date argument
+* Fixed DBLIB DSN strings for Microsoft SQL Server
+* Postgres support for `jsonb` columns added
+* The `addTimestamps()` helper method no longer updates the `created_at` column
+* Fix for Postgres named foreign keys
+* Unit test improvements (including strict warnings)
+* Documentation improvements
+
+**0.4.4** (Sunday, 14th June 2015)
+
+* The `change` method is now the default
+* Added a generic adapter insert method. Warning: The implementation will change!
+* Updated Symfony depdencies to ~2.7
+* Support for MySQL `BLOB` column types
+* SQLite migration fixes
+* Documentation improvements
+
+**0.4.3** (Monday, 23rd Feburary 2015)
+
+* Postgres bugfix for modifying column DEFAULTs
+* MySQL bugfix for setting column INTEGER lengths
+* SQLite bugfix for creating multiple indexes with similar names
+
+**0.4.2.1** (Saturday, 7th Feburary 2015)
+
+* Proper release, updated docs
+
+**0.4.2** (Friday, 6th Feburary 2015)
+
+* Postgres support for `json` columns added
+* MySQL support for `enum` and `set` columns added
+* Allow setting `identity` option on columns
+* Template configuration and generation made more extensible
+* Created a base class for `ProxyAdapter` and `TablePrefixAdapter`
+* Switched to PSR-4
+
+**0.4.1** (Tuesday, 23rd December 2014)
+
+* MySQL support for reserved words in hasColumn and getColumns methods
+* Better MySQL Adapter test coverage and performance fixes
+* Updated dependent Symfony components to 2.6.x
+
+**0.4.0** (Sunday, 14th December 2014)
+
+* Adding initial support for running Phinx via a web interface
+* Support for table prefixes and suffixes
+* Bugfix for foreign key options
+* MySQL keeps column default when renaming columns
+* MySQL support for tiny/medium and longtext columns added
+* Changed SQL Server binary columns to varbinary
+* MySQL supports table comments
+* Postgres supports column comments
+* Empty strings are now supported for default column values
+* Booleans are now supported for default column values
+* Fixed SQL Server default constraint error when changing column types
+* Migration timestamps are now created in UTC
+* Locked Symfony Components to 2.5.0
+* Support for custom migration base classes
+* Cleaned up source code formatting
+* Migrations have access to the output stream
+* Support for custom PDO connections when a PHP config
+* Added support for Postgres UUID type
+* Fixed issue with Postgres dropping foreign keys
+
+**0.3.8** (Sunday, 5th October 2014)
+
+* Added new CHAR & Geospatial column types
+* Added MySQL unix socket support
+* Added precision & scale support for SQL Server
+* Several bug fixes for SQLite
+* Improved error messages
+* Overall code optimizations
+* Optimizations to MySQL hasTable method
+
+**0.3.7** (Tuesday, 12th August 2014)
+
+* Smarter configuration file support
+* Support for Postgres Schemas
+* Fixed charset support for Microsoft SQL Server
+* Fix for Unique indexes in all adapters
+* Improvements for MySQL foreign key migration syntax
+* Allow MySQL column types with extra info
+* Fixed SQLite autoincrement behaviour
+* PHPDoc improvements
+* Documentation improvements
+* Unit test improvements
+* Removing primary_key as a type
+
+**0.3.6** (Sunday, 29th June 2014)
+
+* Add custom adapter support
+* Fix PHP 5.3 compatibility for SQL Server
+
+**0.3.5** (Saturday, 21st June 2014)
+
+* Added Microsoft SQL Server support
+* Removed Primary Key column type
+* Cleaned up and optimized many methods
+* Updated Symfony dependencies to v2.5.0
+* PHPDoc improvements
+
+**0.3.4** (Sunday, 27th April 2014)
+
+* Added support MySQL unsigned integer, biginteger, float and decimal types
+* Added JSON output support for the status command
+* Fix a bug where Postgres couldnt rollback foreign keys
+* Moved Phinx type references to interface constants
+* Fixed a bug with SQLite in-memory databases
+
+**0.3.3** (Saturday, 22nd March 2014)
+
+* Added support for JSON configuration
+* Named index support for all adapters (thanks @archer308)
+* Updated Composer dependencies
+* Fix for SQLite Integer Type
+* Fix for MySQL port option
+
+**0.3.2** (Monday, 24th February 2014)
+
+* Adding better Postgres type support
+
+**0.3.1** (Sunday, 23rd February 2014)
+
+* Adding MySQL charset support to the YAML config
+* Removing trailing spaces
+
+**0.3.0** (Sunday, 2nd February 2014)
+
+* PSR-2 support
+* Method to add timestamps easily to tables
+* Support for column comments in the Postgres adapter
+* Fixes for MySQL driver options
+* Fixes for MySQL biginteger type
+
+**0.2.9** (Saturday, 16th November 2013)
+
+* Added SQLite Support
+* Improving the unit tests, especially on Windows
+
+**0.2.8** (Sunday, 25th August 2013)
+
+* Added PostgresSQL Support
+
+**0.2.7** (Saturday, 24th August 2013)
+
+* Critical fix for a token parsing bug
+* Removed legacy build system
+* Improving docs
+
+**0.2.6** (Saturday, 24th August 2013)
+
+* Added support for environment vars in config files
+* Added support for environment vars to set the Phinx Env
+* Improving docs
+* Fixed a bug with column names in indexes
+* Changes for developers in regards to the unit tests
+
+**0.2.5** (Sunday, 26th May 2013)
+
+* Added support for Box Phar Archive Packaging
+* Added support for MYSQL_ATTR driver options
+* Fixed a bug where foreign keys cannot be removed
+* Added support for MySQL table collation
+* Updated Composer dependencies
+* Removed verbosity options, now relies on Symfony instead
+* Improved unit tests
+
+**0.2.4** (Saturday, 20th April 2013)
+
+* The Rollback command supports the verbosity parameter
+* The Rollback command has more detailed output
+* Table::dropForeignKey now returns the table instance
+
+**0.2.3** (Saturday, 6th April 2013)
+
+* Fixed a reporting bug when Phinx couldn't connect to a database
+* Added support for the MySQL 'ON UPDATE' function
+* Phinx timestamp is now mapped to MySQL timestamp instead of datetime
+* Fixed a docs typo for the minimum PHP version
+* Added UTF8 support for migrations
+* Changed regex to handle migration names differently
+* Added support for custom MySQL table engines such as MyISAM
+* Added the change method to the migration template
+
+**0.2.2** (Sunday, 3rd March 2013)
+
+* Added a new verbosity parameter to see more output when migrating
+* Support for PHP config files
+
+**0.2.1** (Sunday, 3rd March 2013)
+
+* Broken Release. Do not use!
+* Unit tests no longer rely on the default phinx.yml file
+* Running migrate for the first time does not give php warnings
+* `default_migration_table` is now actually supported
+* Updated docblocks to 2013.
+
+**0.2.0** (Sunday, 13th January 2013)
+
+* First Birthday Release
+* Added Reversible Migrations
+* Removed options parameter from AdapterInterface::hasColumn()
+
+**0.1.7** (Tuesday, 8th January 2013)
+
+* Improved documentation on the YAML configuration file
+* Removed options parameter from AdapterInterface::dropIndex()
+
+**0.1.6** (Sunday, 9th December 2012)
+
+* Added foreign key support
+* Removed PEAR support
+* Support for auto_increment on custom id columns
+* Bugfix for column default value 0
+* Documentation improvements
+
+**0.1.5** (Sunday, 4th November 2012)
+
+* Added a test command
+* Added transactions for adapters that support it
+* Changing the Table API to use pending column methods
+* Fixed a bug when defining multiple indexes on a table
+
+**0.1.4** (Sunday, 21st October 2012)
+
+* Documentation Improvements
+
+**0.1.3** (Saturday, 20th October 2012)
+
+* Fixed broken composer support
+
+**0.1.2** (Saturday, 20th October 2012)
+
+* Added composer support
+* Now forces migrations to be in CamelCase format
+* Now specifies the database name when migrating
+* Creates the internal log table using its API instead of raw SQL
+
+**0.1.1** (Wednesday, 13th June 2012)
+
+* First point release. Ready for limited production use.
+
+**0.1.0** (Friday, 13th January 2012)
+
+* Initial public release.
diff --git a/vendor/topthink/think-migration/phinx/CONTRIBUTING.md b/vendor/topthink/think-migration/phinx/CONTRIBUTING.md
new file mode 100755
index 0000000..73b54ac
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/CONTRIBUTING.md
@@ -0,0 +1,66 @@
+# How to contribute to Phinx
+
+Phinx relies heavily on external contributions in order to make it the best database migration
+tool possible. Without the support of our 115+ contributors we wouldn't be where we are today!
+We encourage anyone to submit documentation enhancements and code.
+
+Issues, feature requests and bugs should be submitted using the Github issue tool:
+https://github.com/robmorgan/phinx/issues.
+
+This document briefly outlines the requirements to contribute code to Phinx.
+
+## Considerations
+
+Before you submit your pull request take a moment to answer the following questions.
+
+Answering '**YES**' to all questions will increase the likelihood of your PR being accepted!
+
+* Have I implemented my feature for as many database adapters as possible?
+* Does my new feature improve Phinx's performance or keep it consistent?
+* Does my feature fit within the database migration space?
+* Is the code entirely my own and free from any commercial licensing?
+* Am I happy to release my code under the MIT license?
+* Is my code formatted using the [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) coding standard?
+
+**Note:** We accept bug fixes much faster into our development branch than features.
+
+## Getting Started
+
+Great, so you want to contribute. Let's get started:
+
+1. Start by forking Phinx on GitHub: https://github.com/robmorgan/phinx
+
+1. Clone your repository to a local directory on your development box.
+
+1. If you do not have Composer set up already, install it:
+
+    ```
+    curl -sS https://getcomposer.org/installer | php
+    ```
+
+1. Change to your Phinx clone directory and pull the necessary dependencies:
+
+    ```
+    php composer.phar install
+    ```
+
+1. Copy the `phpunit.xml.dist` template to `phpunit.xml` and change the configuration to suit your environment. If you are not using any particular adapter you can disable it in the `phpunit.xml` file.
+
+1. Run the unit tests locally to ensure they pass:
+
+    ```
+    php vendor/bin/phpunit --config phpunit.xml
+    ```
+
+1. Write the code and unit tests for your bug fix or feature.
+
+1. Add any relevant documentation.
+
+1. Run the unit tests again and ensure they pass.
+
+1. Open a pull request on the Github project page. Ensure the code is being merged into the latest development branch (e.g: `0.5.x-dev`) and not `master`.
+
+## Documentation
+
+The Phinx documentation is stored in the **docs** directory using the [RestructedText](http://docutils.sourceforge.net/rst.html) format. All documentation merged to `master` is automatically published to the Phinx documentation site available
+at: http://docs.phinx.org. Keep this in mind when submitting your PR, or ask someone to merge the development branch back down to master.
diff --git a/vendor/topthink/think-migration/phinx/LICENSE b/vendor/topthink/think-migration/phinx/LICENSE
new file mode 100755
index 0000000..26ade90
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/LICENSE
@@ -0,0 +1,9 @@
+(The MIT license)
+
+Copyright (c) 2014 Rob Morgan
+
+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.
\ No newline at end of file
diff --git a/vendor/topthink/think-migration/phinx/README.md b/vendor/topthink/think-migration/phinx/README.md
new file mode 100755
index 0000000..ed1badc
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/README.md
@@ -0,0 +1,128 @@
+# [Phinx](https://phinx.org): Simple PHP Database Migrations
+
+[![Build Status](https://travis-ci.org/robmorgan/phinx.png?branch=master)](https://travis-ci.org/robmorgan/phinx)
+[![Build status](https://ci.appveyor.com/api/projects/status/9vag4892hfq6effr)](https://ci.appveyor.com/project/robmorgan/phinx)
+[![Code Coverage](https://scrutinizer-ci.com/g/robmorgan/phinx/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/robmorgan/phinx/)
+[![Latest Stable Version](https://poser.pugx.org/robmorgan/phinx/version.png)](https://packagist.org/packages/robmorgan/phinx)
+[![Total Downloads](https://poser.pugx.org/robmorgan/phinx/d/total.png)](https://packagist.org/packages/robmorgan/phinx)
+
+Phinx makes it ridiculously easy to manage the database migrations for your PHP app. In less than 5 minutes you can install Phinx and create your first database migration. Phinx is just about migrations without all the bloat of a database ORM system or framework.
+
+**Check out http://docs.phinx.org for the comprehensive documentation.**
+
+![phinxterm](https://cloud.githubusercontent.com/assets/178939/3887559/e6b5e524-21f2-11e4-8256-0ba6040725fc.gif)
+
+### Features
+
+* Write database migrations using database agnostic PHP code.
+* Migrate up and down.
+* Migrate on deployment.
+* Seed data after database creation.
+* Get going in less than 5 minutes.
+* Stop worrying about the state of your database.
+* Take advantage of SCM features such as branching.
+* Integrate with any app.
+
+### Supported Adapters
+
+Phinx natively supports the following database adapters:
+
+* MySQL
+* PostgreSQL
+* SQLite
+* Microsoft SQL Server
+
+## Install & Run
+
+### Composer
+
+The fastest way to install Phinx is to add it to your project using Composer (http://getcomposer.org/).
+
+1. Install Composer:
+
+    ```
+    curl -sS https://getcomposer.org/installer | php
+    ```
+
+1. Require Phinx as a dependency using Composer:
+
+    ```
+    php composer.phar require robmorgan/phinx
+    ```
+
+1. Install Phinx:
+
+    ```
+    php composer.phar install
+    ```
+
+1. Execute Phinx:
+
+    ```
+    php vendor/bin/phinx
+    ```
+
+### As a Phar
+
+You can also use the Box application to build Phinx as a Phar archive (https://box-project.github.io/box2/).
+
+1. Clone Phinx from GitHub
+
+    ```
+    git clone git://github.com/robmorgan/phinx.git
+    cd phinx
+    ```
+
+1. Install Composer
+
+    ```
+    curl -s https://getcomposer.org/installer | php
+    ```
+
+1. Install the Phinx dependencies
+
+    ```
+    php composer.phar install
+    ```
+
+1. Install Box:
+
+    ```
+    curl -LSs https://box-project.github.io/box2/installer.php | php
+    ```
+
+1. Create a Phar archive
+
+    ```
+    php box.phar build
+    ```
+
+## Documentation
+
+Check out http://docs.phinx.org for the comprehensive documentation.
+
+## Contributing
+
+Please read the [CONTRIBUTING](CONTRIBUTING.md) document.
+
+## News & Updates
+
+Follow Rob (@\_rjm\_) on Twitter to stay up to date (http://twitter.com/_rjm_)
+
+## Misc
+
+### Version History
+
+Please read the [CHANGELOG](CHANGELOG.md) document.
+
+### License
+
+(The MIT license)
+
+Copyright (c) 2016 Rob Morgan
+
+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.
diff --git a/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/AdapterFactory.php b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/AdapterFactory.php
new file mode 100755
index 0000000..90674f9
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/AdapterFactory.php
@@ -0,0 +1,181 @@
+<?php
+/**
+ * Phinx
+ *
+ * (The MIT license)
+ * Copyright (c) 2015 Rob Morgan
+ *
+ * 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.
+ *
+ * @package    Phinx
+ * @subpackage Phinx\Migration
+ */
+namespace Phinx\Db\Adapter;
+
+/**
+ * Adapter factory and registry.
+ *
+ * Used for registering adapters and creating instances of adapters.
+ *
+ * @author Woody Gilk <woody.gilk@gmail.com>
+ */
+class AdapterFactory
+{
+    /**
+     * @var AdapterFactory
+     */
+    protected static $instance;
+
+    /**
+     * Get the factory singleton instance.
+     *
+     * @return AdapterFactory
+     */
+    public static function instance()
+    {
+        if (!static::$instance) {
+            static::$instance = new static();
+        }
+        return static::$instance;
+    }
+
+    /**
+     * Class map of database adapters, indexed by PDO::ATTR_DRIVER_NAME.
+     *
+     * @var array
+     */
+    protected $adapters = array(
+        'mysql'  => 'Phinx\Db\Adapter\MysqlAdapter',
+        'pgsql'  => 'Phinx\Db\Adapter\PostgresAdapter',
+        'sqlite' => 'Phinx\Db\Adapter\SQLiteAdapter',
+        'sqlsrv' => 'Phinx\Db\Adapter\SqlServerAdapter',
+    );
+
+    /**
+     * Class map of adapters wrappers, indexed by name.
+     *
+     * @var array
+     */
+    protected $wrappers = array(
+        'prefix' => 'Phinx\Db\Adapter\TablePrefixAdapter',
+        'proxy'  => 'Phinx\Db\Adapter\ProxyAdapter',
+    );
+
+    /**
+     * Add or replace an adapter with a fully qualified class name.
+     *
+     * @throws \RuntimeException
+     * @param  string $name
+     * @param  string $class
+     * @return $this
+     */
+    public function registerAdapter($name, $class)
+    {
+        if (!is_subclass_of($class, 'Phinx\Db\Adapter\AdapterInterface')) {
+            throw new \RuntimeException(sprintf(
+                'Adapter class "%s" must implement Phinx\\Db\\Adapter\\AdapterInterface',
+                $class
+            ));
+        }
+        $this->adapters[$name] = $class;
+        return $this;
+    }
+
+    /**
+     * Get an adapter class by name.
+     *
+     * @throws \RuntimeException
+     * @param  string $name
+     * @return string
+     */
+    protected function getClass($name)
+    {
+        if (empty($this->adapters[$name])) {
+            throw new \RuntimeException(sprintf(
+                'Adapter "%s" has not been registered',
+                $name
+            ));
+        }
+        return $this->adapters[$name];
+    }
+
+    /**
+     * Get an adapter instance by name.
+     *
+     * @param  string $name
+     * @param  array  $options
+     * @return AdapterInterface
+     */
+    public function getAdapter($name, array $options)
+    {
+        $class = $this->getClass($name);
+        return new $class($options);
+    }
+
+    /**
+     * Add or replace a wrapper with a fully qualified class name.
+     *
+     * @throws \RuntimeException
+     * @param  string $name
+     * @param  string $class
+     * @return $this
+     */
+    public function registerWrapper($name, $class)
+    {
+        if (!is_subclass_of($class, 'Phinx\Db\Adapter\WrapperInterface')) {
+            throw new \RuntimeException(sprintf(
+                'Wrapper class "%s" must be implement Phinx\\Db\\Adapter\\WrapperInterface',
+                $class
+            ));
+        }
+        $this->wrappers[$name] = $class;
+        return $this;
+    }
+
+    /**
+     * Get a wrapper class by name.
+     *
+     * @throws \RuntimeException
+     * @param  string $name
+     * @return string
+     */
+    protected function getWrapperClass($name)
+    {
+        if (empty($this->wrappers[$name])) {
+            throw new \RuntimeException(sprintf(
+                'Wrapper "%s" has not been registered',
+                $name
+            ));
+        }
+        return $this->wrappers[$name];
+    }
+
+    /**
+     * Get a wrapper instance by name.
+     *
+     * @param  string $name
+     * @param  AdapterInterface $adapter
+     * @return AdapterInterface
+     */
+    public function getWrapper($name, AdapterInterface $adapter)
+    {
+        $class = $this->getWrapperClass($name);
+        return new $class($adapter);
+    }
+}
diff --git a/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/AdapterInterface.php b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/AdapterInterface.php
new file mode 100755
index 0000000..f2e9fe1
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/AdapterInterface.php
@@ -0,0 +1,510 @@
+<?php
+/**
+ * Phinx
+ *
+ * (The MIT license)
+ * Copyright (c) 2015 Rob Morgan
+ *
+ * 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.
+ *
+ * @package    Phinx
+ * @subpackage Phinx\Db\Adapter
+ */
+namespace Phinx\Db\Adapter;
+
+use think\console\Input as InputInterface;
+use think\console\Output as OutputInterface;
+use Phinx\Db\Table;
+use Phinx\Db\Table\Column;
+use Phinx\Db\Table\Index;
+use Phinx\Db\Table\ForeignKey;
+use Phinx\Migration\MigrationInterface;
+
+/**
+ * Adapter Interface.
+ *
+ * @author Rob Morgan <robbym@gmail.com>
+ */
+interface AdapterInterface
+{
+    const PHINX_TYPE_STRING         = 'string';
+    const PHINX_TYPE_CHAR           = 'char';
+    const PHINX_TYPE_TEXT           = 'text';
+    const PHINX_TYPE_INTEGER        = 'integer';
+    const PHINX_TYPE_BIG_INTEGER    = 'biginteger';
+    const PHINX_TYPE_FLOAT          = 'float';
+    const PHINX_TYPE_DECIMAL        = 'decimal';
+    const PHINX_TYPE_DATETIME       = 'datetime';
+    const PHINX_TYPE_TIMESTAMP      = 'timestamp';
+    const PHINX_TYPE_TIME           = 'time';
+    const PHINX_TYPE_DATE           = 'date';
+    const PHINX_TYPE_BINARY         = 'binary';
+    const PHINX_TYPE_VARBINARY      = 'varbinary';
+    const PHINX_TYPE_BLOB           = 'blob';
+    const PHINX_TYPE_BOOLEAN        = 'boolean';
+    const PHINX_TYPE_JSON           = 'json';
+    const PHINX_TYPE_JSONB          = 'jsonb';
+    const PHINX_TYPE_UUID           = 'uuid';
+    const PHINX_TYPE_FILESTREAM     = 'filestream';
+
+    // Geospatial database types
+    const PHINX_TYPE_GEOMETRY       = 'geometry';
+    const PHINX_TYPE_POINT          = 'point';
+    const PHINX_TYPE_LINESTRING     = 'linestring';
+    const PHINX_TYPE_POLYGON        = 'polygon';
+
+    // only for mysql so far
+    const PHINX_TYPE_ENUM           = 'enum';
+    const PHINX_TYPE_SET            = 'set';
+
+    /**
+     * Get all migrated version numbers.
+     *
+     * @return array
+     */
+    public function getVersions();
+
+    /**
+     * Get all migration log entries, indexed by version number.
+     *
+     * @return array
+     */
+    public function getVersionLog();
+
+    /**
+     * Set adapter configuration options.
+     *
+     * @param  array $options
+     * @return AdapterInterface
+     */
+    public function setOptions(array $options);
+
+    /**
+     * Get all adapter options.
+     *
+     * @return array
+     */
+    public function getOptions();
+
+    /**
+     * Check if an option has been set.
+     *
+     * @param  string $name
+     * @return boolean
+     */
+    public function hasOption($name);
+
+    /**
+     * Get a single adapter option, or null if the option does not exist.
+     *
+     * @param  string $name
+     * @return mixed
+     */
+    public function getOption($name);
+
+    /**
+     * Sets the console input.
+     *
+     * @param InputInterface $input Input
+     * @return AdapterInterface
+     */
+    public function setInput(InputInterface $input);
+
+    /**
+     * Gets the console input.
+     *
+     * @return InputInterface
+     */
+    public function getInput();
+
+    /**
+     * Sets the console output.
+     *
+     * @param OutputInterface $output Output
+     * @return AdapterInterface
+     */
+    public function setOutput(OutputInterface $output);
+
+    /**
+     * Gets the console output.
+     *
+     * @return OutputInterface
+     */
+    public function getOutput();
+
+    /**
+     * Records a migration being run.
+     *
+     * @param MigrationInterface $migration Migration
+     * @param string $direction Direction
+     * @param int $startTime Start Time
+     * @param int $endTime End Time
+     * @return AdapterInterface
+     */
+    public function migrated(MigrationInterface $migration, $direction, $startTime, $endTime);
+
+    /**
+     * Toggle a migration breakpoint.
+     *
+     * @param MigrationInterface $migration
+     *
+     * @return AdapterInterface
+     */
+    public function toggleBreakpoint(MigrationInterface $migration);
+
+    /**
+     * Reset all migration breakpoints.
+     *
+     * @return int The number of breakpoints reset
+     */
+    public function resetAllBreakpoints();
+
+    /**
+     * Does the schema table exist?
+     *
+     * @deprecated use hasTable instead.
+     * @return boolean
+     */
+    public function hasSchemaTable();
+
+    /**
+     * Creates the schema table.
+     *
+     * @return void
+     */
+    public function createSchemaTable();
+
+    /**
+     * Returns the adapter type.
+     *
+     * @return string
+     */
+    public function getAdapterType();
+
+    /**
+     * Initializes the database connection.
+     *
+     * @throws \RuntimeException When the requested database driver is not installed.
+     * @return void
+     */
+    public function connect();
+
+    /**
+     * Closes the database connection.
+     *
+     * @return void
+     */
+    public function disconnect();
+
+    /**
+     * Does the adapter support transactions?
+     *
+     * @return boolean
+     */
+    public function hasTransactions();
+
+    /**
+     * Begin a transaction.
+     *
+     * @return void
+     */
+    public function beginTransaction();
+
+    /**
+     * Commit a transaction.
+     *
+     * @return void
+     */
+    public function commitTransaction();
+
+    /**
+     * Rollback a transaction.
+     *
+     * @return void
+     */
+    public function rollbackTransaction();
+
+    /**
+     * Executes a SQL statement and returns the number of affected rows.
+     *
+     * @param string $sql SQL
+     * @return int
+     */
+    public function execute($sql);
+
+    /**
+     * Executes a SQL statement and returns the result as an array.
+     *
+     * @param string $sql SQL
+     * @return array
+     */
+    public function query($sql);
+
+    /**
+     * Executes a query and returns only one row as an array.
+     *
+     * @param string $sql SQL
+     * @return array
+     */
+    public function fetchRow($sql);
+
+    /**
+     * Executes a query and returns an array of rows.
+     *
+     * @param string $sql SQL
+     * @return array
+     */
+    public function fetchAll($sql);
+
+    /**
+     * Inserts data into a table.
+     *
+     * @param Table $table where to insert data
+     * @param array $row
+     * @return void
+     */
+    public function insert(Table $table, $row);
+
+    /**
+     * Quotes a table name for use in a query.
+     *
+     * @param string $tableName Table Name
+     * @return string
+     */
+    public function quoteTableName($tableName);
+
+    /**
+     * Quotes a column name for use in a query.
+     *
+     * @param string $columnName Table Name
+     * @return string
+     */
+    public function quoteColumnName($columnName);
+
+    /**
+     * Checks to see if a table exists.
+     *
+     * @param string $tableName Table Name
+     * @return boolean
+     */
+    public function hasTable($tableName);
+
+    /**
+     * Creates the specified database table.
+     *
+     * @param Table $table Table
+     * @return void
+     */
+    public function createTable(Table $table);
+
+    /**
+     * Renames the specified database table.
+     *
+     * @param string $tableName Table Name
+     * @param string $newName   New Name
+     * @return void
+     */
+    public function renameTable($tableName, $newName);
+
+    /**
+     * Drops the specified database table.
+     *
+     * @param string $tableName Table Name
+     * @return void
+     */
+    public function dropTable($tableName);
+
+    /**
+     * Returns table columns
+     *
+     * @param string $tableName Table Name
+     * @return Column[]
+     */
+    public function getColumns($tableName);
+
+    /**
+     * Checks to see if a column exists.
+     *
+     * @param string $tableName  Table Name
+     * @param string $columnName Column Name
+     * @return boolean
+     */
+    public function hasColumn($tableName, $columnName);
+
+    /**
+     * Adds the specified column to a database table.
+     *
+     * @param Table  $table  Table
+     * @param Column $column Column
+     * @return void
+     */
+    public function addColumn(Table $table, Column $column);
+
+    /**
+     * Renames the specified column.
+     *
+     * @param string $tableName Table Name
+     * @param string $columnName Column Name
+     * @param string $newColumnName New Column Name
+     * @return void
+     */
+    public function renameColumn($tableName, $columnName, $newColumnName);
+
+    /**
+     * Change a table column type.
+     *
+     * @param string $tableName  Table Name
+     * @param string $columnName Column Name
+     * @param Column $newColumn  New Column
+     * @return Table
+     */
+    public function changeColumn($tableName, $columnName, Column $newColumn);
+
+    /**
+     * Drops the specified column.
+     *
+     * @param string $tableName Table Name
+     * @param string $columnName Column Name
+     * @return void
+     */
+    public function dropColumn($tableName, $columnName);
+
+    /**
+     * Checks to see if an index exists.
+     *
+     * @param string $tableName Table Name
+     * @param mixed  $columns   Column(s)
+     * @return boolean
+     */
+    public function hasIndex($tableName, $columns);
+
+    /**
+     * Checks to see if an index specified by name exists.
+     *
+     * @param string $tableName Table Name
+     * @param string $indexName
+     * @return boolean
+     */
+    public function hasIndexByName($tableName, $indexName);
+
+    /**
+     * Adds the specified index to a database table.
+     *
+     * @param Table $table Table
+     * @param Index $index Index
+     * @return void
+     */
+    public function addIndex(Table $table, Index $index);
+
+    /**
+     * Drops the specified index from a database table.
+     *
+     * @param string $tableName
+     * @param mixed  $columns Column(s)
+     * @return void
+     */
+    public function dropIndex($tableName, $columns);
+
+    /**
+     * Drops the index specified by name from a database table.
+     *
+     * @param string $tableName
+     * @param string $indexName
+     * @return void
+     */
+    public function dropIndexByName($tableName, $indexName);
+
+    /**
+     * Checks to see if a foreign key exists.
+     *
+     * @param string   $tableName
+     * @param string[] $columns    Column(s)
+     * @param string   $constraint Constraint name
+     * @return boolean
+     */
+    public function hasForeignKey($tableName, $columns, $constraint = null);
+
+    /**
+     * Adds the specified foreign key to a database table.
+     *
+     * @param Table      $table
+     * @param ForeignKey $foreignKey
+     * @return void
+     */
+    public function addForeignKey(Table $table, ForeignKey $foreignKey);
+
+    /**
+     * Drops the specified foreign key from a database table.
+     *
+     * @param string   $tableName
+     * @param string[] $columns    Column(s)
+     * @param string   $constraint Constraint name
+     * @return void
+     */
+    public function dropForeignKey($tableName, $columns, $constraint = null);
+
+    /**
+     * Returns an array of the supported Phinx column types.
+     *
+     * @return array
+     */
+    public function getColumnTypes();
+
+    /**
+     * Checks that the given column is of a supported type.
+     *
+     * @param  Column $column
+     * @return boolean
+     */
+    public function isValidColumnType(Column $column);
+
+    /**
+     * Converts the Phinx logical type to the adapter's SQL type.
+     *
+     * @param string $type
+     * @param integer $limit
+     * @return string
+     */
+    public function getSqlType($type, $limit = null);
+
+    /**
+     * Creates a new database.
+     *
+     * @param string $name Database Name
+     * @param array $options Options
+     * @return void
+     */
+    public function createDatabase($name, $options = array());
+
+    /**
+     * Checks to see if a database exists.
+     *
+     * @param string $name Database Name
+     * @return boolean
+     */
+    public function hasDatabase($name);
+
+    /**
+     * Drops the specified database.
+     *
+     * @param string $name Database Name
+     * @return void
+     */
+    public function dropDatabase($name);
+}
diff --git a/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/AdapterWrapper.php b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/AdapterWrapper.php
new file mode 100755
index 0000000..ae5ec39
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/AdapterWrapper.php
@@ -0,0 +1,507 @@
+<?php
+/**
+ * Phinx
+ *
+ * (The MIT license)
+ * Copyright (c) 2015 Rob Morgan
+ *
+ * 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.
+ *
+ * @package    Phinx
+ * @subpackage Phinx\Db\Adapter
+ */
+namespace Phinx\Db\Adapter;
+
+use Phinx\Db\Table;
+use Phinx\Db\Table\Column;
+use Phinx\Db\Table\Index;
+use Phinx\Db\Table\ForeignKey;
+use Phinx\Migration\MigrationInterface;
+use think\console\Input as InputInterface;
+use think\console\Output as OutputInterface;
+
+/**
+ * Adapter Wrapper.
+ *
+ * Proxy commands through to another adapter, allowing modification of
+ * parameters during calls.
+ *
+ * @author Woody Gilk <woody.gilk@gmail.com>
+ */
+abstract class AdapterWrapper implements AdapterInterface, WrapperInterface
+{
+    /**
+     * @var AdapterInterface
+     */
+    protected $adapter;
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __construct(AdapterInterface $adapter)
+    {
+        $this->setAdapter($adapter);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setAdapter(AdapterInterface $adapter)
+    {
+        $this->adapter = $adapter;
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getAdapter()
+    {
+        return $this->adapter;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setOptions(array $options)
+    {
+        $this->adapter->setOptions($options);
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getOptions()
+    {
+        return $this->adapter->getOptions();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasOption($name)
+    {
+        return $this->adapter->hasOption($name);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getOption($name)
+    {
+        return $this->adapter->getOption($name);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setInput(InputInterface $input)
+    {
+        $this->adapter->setInput($input);
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getInput()
+    {
+        return $this->adapter->getInput();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setOutput(OutputInterface $output)
+    {
+        $this->adapter->setOutput($output);
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getOutput()
+    {
+        return $this->adapter->getOutput();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function connect()
+    {
+        return $this->getAdapter()->connect();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function disconnect()
+    {
+        return $this->getAdapter()->disconnect();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function execute($sql)
+    {
+        return $this->getAdapter()->execute($sql);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function query($sql)
+    {
+        return $this->getAdapter()->query($sql);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function insert(Table $table, $row)
+    {
+        return $this->getAdapter()->insert($table, $row);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function fetchRow($sql)
+    {
+        return $this->getAdapter()->fetchRow($sql);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function fetchAll($sql)
+    {
+        return $this->getAdapter()->fetchAll($sql);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getVersions()
+    {
+        return $this->getAdapter()->getVersions();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getVersionLog()
+    {
+        return $this->getAdapter()->getVersionLog();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function migrated(MigrationInterface $migration, $direction, $startTime, $endTime)
+    {
+        $this->getAdapter()->migrated($migration, $direction, $startTime, $endTime);
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function toggleBreakpoint(MigrationInterface $migration)
+    {
+        $this->getAdapter()->toggleBreakpoint($migration);
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function resetAllBreakpoints()
+    {
+        return $this->getAdapter()->resetAllBreakpoints();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasSchemaTable()
+    {
+        return $this->getAdapter()->hasSchemaTable();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createSchemaTable()
+    {
+        return $this->getAdapter()->createSchemaTable();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getColumnTypes()
+    {
+        return $this->getAdapter()->getColumnTypes();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isValidColumnType(Column $column)
+    {
+        return $this->getAdapter()->isValidColumnType($column);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasTransactions()
+    {
+        return $this->getAdapter()->hasTransactions();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function beginTransaction()
+    {
+        return $this->getAdapter()->beginTransaction();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function commitTransaction()
+    {
+        return $this->getAdapter()->commitTransaction();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function rollbackTransaction()
+    {
+        return $this->getAdapter()->rollbackTransaction();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function quoteTableName($tableName)
+    {
+        return $this->getAdapter()->quoteTableName($tableName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function quoteColumnName($columnName)
+    {
+        return $this->getAdapter()->quoteColumnName($columnName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasTable($tableName)
+    {
+        return $this->getAdapter()->hasTable($tableName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createTable(Table $table)
+    {
+        return $this->getAdapter()->createTable($table);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function renameTable($tableName, $newTableName)
+    {
+        return $this->getAdapter()->renameTable($tableName, $newTableName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropTable($tableName)
+    {
+        return $this->getAdapter()->dropTable($tableName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getColumns($tableName)
+    {
+        return $this->getAdapter()->getColumns($tableName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasColumn($tableName, $columnName)
+    {
+        return $this->getAdapter()->hasColumn($tableName, $columnName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addColumn(Table $table, Column $column)
+    {
+        return $this->getAdapter()->addColumn($table, $column);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function renameColumn($tableName, $columnName, $newColumnName)
+    {
+        return $this->getAdapter()->renameColumn($tableName, $columnName, $newColumnName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function changeColumn($tableName, $columnName, Column $newColumn)
+    {
+        return $this->getAdapter()->changeColumn($tableName, $columnName, $newColumn);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropColumn($tableName, $columnName)
+    {
+        return $this->getAdapter()->dropColumn($tableName, $columnName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasIndex($tableName, $columns)
+    {
+        return $this->getAdapter()->hasIndex($tableName, $columns);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasIndexByName($tableName, $indexName)
+    {
+        return $this->getAdapter()->hasIndexByName($tableName, $indexName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addIndex(Table $table, Index $index)
+    {
+        return $this->getAdapter()->addIndex($table, $index);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropIndex($tableName, $columns, $options = array())
+    {
+        return $this->getAdapter()->dropIndex($tableName, $columns, $options);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropIndexByName($tableName, $indexName)
+    {
+        return $this->getAdapter()->dropIndexByName($tableName, $indexName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasForeignKey($tableName, $columns, $constraint = null)
+    {
+        return $this->getAdapter()->hasForeignKey($tableName, $columns, $constraint);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addForeignKey(Table $table, ForeignKey $foreignKey)
+    {
+        return $this->getAdapter()->addForeignKey($table, $foreignKey);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropForeignKey($tableName, $columns, $constraint = null)
+    {
+        return $this->getAdapter()->dropForeignKey($tableName, $columns, $constraint);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSqlType($type, $limit = null)
+    {
+        return $this->getAdapter()->getSqlType($type, $limit);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createDatabase($name, $options = array())
+    {
+        return $this->getAdapter()->createDatabase($name, $options);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasDatabase($name)
+    {
+        return $this->getAdapter()->hasDatabase($name);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropDatabase($name)
+    {
+        return $this->getAdapter()->dropDatabase($name);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function castToBool($value)
+    {
+        return $this->getAdapter()->castToBool($value);
+    }
+}
diff --git a/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/MysqlAdapter.php b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/MysqlAdapter.php
new file mode 100755
index 0000000..f882fb5
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/MysqlAdapter.php
@@ -0,0 +1,1145 @@
+<?php
+/**
+ * Phinx
+ *
+ * (The MIT license)
+ * Copyright (c) 2015 Rob Morgan
+ *
+ * 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.
+ *
+ * @package    Phinx
+ * @subpackage Phinx\Db\Adapter
+ */
+namespace Phinx\Db\Adapter;
+
+use Phinx\Db\Table;
+use Phinx\Db\Table\Column;
+use Phinx\Db\Table\Index;
+use Phinx\Db\Table\ForeignKey;
+
+/**
+ * Phinx MySQL Adapter.
+ *
+ * @author Rob Morgan <robbym@gmail.com>
+ */
+class MysqlAdapter extends PdoAdapter implements AdapterInterface
+{
+
+    protected $signedColumnTypes = array('integer' => true, 'biginteger' => true, 'float' => true, 'decimal' => true, 'boolean' => true);
+
+    const TEXT_TINY    = 255;
+    const TEXT_SMALL   = 255; /* deprecated, alias of TEXT_TINY */
+    const TEXT_REGULAR = 65535;
+    const TEXT_MEDIUM  = 16777215;
+    const TEXT_LONG    = 4294967295;
+
+    // According to https://dev.mysql.com/doc/refman/5.0/en/blob.html BLOB sizes are the same as TEXT
+    const BLOB_TINY    = 255;
+    const BLOB_SMALL   = 255; /* deprecated, alias of BLOB_TINY */
+    const BLOB_REGULAR = 65535;
+    const BLOB_MEDIUM  = 16777215;
+    const BLOB_LONG    = 4294967295;
+
+    const INT_TINY    = 255;
+    const INT_SMALL   = 65535;
+    const INT_MEDIUM  = 16777215;
+    const INT_REGULAR = 4294967295;
+    const INT_BIG     = 18446744073709551615;
+
+    const TYPE_YEAR   = 'year';
+
+    /**
+     * {@inheritdoc}
+     */
+    public function connect()
+    {
+        if (null === $this->connection) {
+            if (!class_exists('PDO') || !in_array('mysql', \PDO::getAvailableDrivers(), true)) {
+                // @codeCoverageIgnoreStart
+                throw new \RuntimeException('You need to enable the PDO_Mysql extension for Phinx to run properly.');
+                // @codeCoverageIgnoreEnd
+            }
+
+            $db = null;
+            $options = $this->getOptions();
+
+            $dsn = 'mysql:';
+
+            if (!empty($options['unix_socket'])) {
+                // use socket connection
+                $dsn .= 'unix_socket=' . $options['unix_socket'];
+            } else {
+                // use network connection
+                $dsn .= 'host=' . $options['host'];
+                if (!empty($options['port'])) {
+                    $dsn .= ';port=' . $options['port'];
+                }
+            }
+
+            $dsn .= ';dbname=' . $options['name'];
+
+            // charset support
+            if (!empty($options['charset'])) {
+                $dsn .= ';charset=' . $options['charset'];
+            }
+
+            $driverOptions = array(\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION);
+
+            // support arbitrary \PDO::MYSQL_ATTR_* driver options and pass them to PDO
+            // http://php.net/manual/en/ref.pdo-mysql.php#pdo-mysql.constants
+            foreach ($options as $key => $option) {
+                if (strpos($key, 'mysql_attr_') === 0) {
+                    $driverOptions[constant('\PDO::' . strtoupper($key))] = $option;
+                }
+            }
+
+            try {
+                $db = new \PDO($dsn, $options['user'], $options['pass'], $driverOptions);
+            } catch (\PDOException $exception) {
+                throw new \InvalidArgumentException(sprintf(
+                    'There was a problem connecting to the database: %s',
+                    $exception->getMessage()
+                ));
+            }
+
+            $this->setConnection($db);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function disconnect()
+    {
+        $this->connection = null;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasTransactions()
+    {
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function beginTransaction()
+    {
+        $this->execute('START TRANSACTION');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function commitTransaction()
+    {
+        $this->execute('COMMIT');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function rollbackTransaction()
+    {
+        $this->execute('ROLLBACK');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function quoteTableName($tableName)
+    {
+        return str_replace('.', '`.`', $this->quoteColumnName($tableName));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function quoteColumnName($columnName)
+    {
+        return '`' . str_replace('`', '``', $columnName) . '`';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasTable($tableName)
+    {
+        $options = $this->getOptions();
+
+        $exists = $this->fetchRow(sprintf(
+            "SELECT TABLE_NAME
+            FROM INFORMATION_SCHEMA.TABLES
+            WHERE TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s'",
+            $options['name'], $tableName
+        ));
+
+        return !empty($exists);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createTable(Table $table)
+    {
+        $this->startCommandTimer();
+
+        // This method is based on the MySQL docs here: http://dev.mysql.com/doc/refman/5.1/en/create-index.html
+        $defaultOptions = array(
+            'engine' => 'InnoDB',
+            'collation' => 'utf8_general_ci'
+        );
+        $options = array_merge($defaultOptions, $table->getOptions());
+
+        // Add the default primary key
+        $columns = $table->getPendingColumns();
+
+        if (!isset($options['id']) || (isset($options['id']) && $options['id'] === true)) {
+            $options['id'] = 'id';
+        }
+
+        if (isset($options['id']) && is_string($options['id'])) {
+            // Handle id => "field_name" to support AUTO_INCREMENT
+            $column = new Column();
+            $column->setName($options['id'])
+                   ->setType('integer')
+                   ->setSigned(isset($options['signed']) ? $options['signed'] : true)
+                   ->setIdentity(true);
+
+            array_unshift($columns, $column);
+            $options['primary_key'] = $options['id'];
+        }
+
+        // TODO - process table options like collation etc
+
+        // process table engine (default to InnoDB)
+        $optionsStr = 'ENGINE = InnoDB';
+        if (isset($options['engine'])) {
+            $optionsStr = sprintf('ENGINE = %s', $options['engine']);
+        }
+
+        // process table collation
+        if (isset($options['collation'])) {
+            $charset = explode('_', $options['collation']);
+            $optionsStr .= sprintf(' CHARACTER SET %s', $charset[0]);
+            $optionsStr .= sprintf(' COLLATE %s', $options['collation']);
+        }
+
+        // set the table comment
+        if (isset($options['comment'])) {
+            $optionsStr .= sprintf(" COMMENT=%s ", $this->getConnection()->quote($options['comment']));
+        }
+
+        $sql = 'CREATE TABLE ';
+        $sql .= $this->quoteTableName($table->getName()) . ' (';
+        foreach ($columns as $column) {
+            $sql .= $this->quoteColumnName($column->getName()) . ' ' . $this->getColumnSqlDefinition($column) . ', ';
+        }
+
+        // set the primary key(s)
+        if (isset($options['primary_key'])) {
+            $sql = rtrim($sql);
+            $sql .= ' PRIMARY KEY (';
+            if (is_string($options['primary_key'])) {       // handle primary_key => 'id'
+                $sql .= $this->quoteColumnName($options['primary_key']);
+            } elseif (is_array($options['primary_key'])) { // handle primary_key => array('tag_id', 'resource_id')
+                // PHP 5.4 will allow access of $this, so we can call quoteColumnName() directly in the
+                // anonymous function, but for now just hard-code the adapter quotes
+                $sql .= implode(
+                    ',',
+                    array_map(
+                        function ($v) {
+                            return '`' . $v . '`';
+                        },
+                        $options['primary_key']
+                    )
+                );
+            }
+            $sql .= ')';
+        } else {
+            $sql = substr(rtrim($sql), 0, -1);              // no primary keys
+        }
+
+        // set the indexes
+        $indexes = $table->getIndexes();
+        if (!empty($indexes)) {
+            foreach ($indexes as $index) {
+                $sql .= ', ' . $this->getIndexSqlDefinition($index);
+            }
+        }
+
+        // set the foreign keys
+        $foreignKeys = $table->getForeignKeys();
+        if (!empty($foreignKeys)) {
+            foreach ($foreignKeys as $foreignKey) {
+                $sql .= ', ' . $this->getForeignKeySqlDefinition($foreignKey);
+            }
+        }
+
+        $sql .= ') ' . $optionsStr;
+        $sql = rtrim($sql) . ';';
+
+        // execute the sql
+        $this->writeCommand('createTable', array($table->getName()));
+        $this->execute($sql);
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function renameTable($tableName, $newTableName)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('renameTable', array($tableName, $newTableName));
+        $this->execute(sprintf('RENAME TABLE %s TO %s', $this->quoteTableName($tableName), $this->quoteTableName($newTableName)));
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropTable($tableName)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('dropTable', array($tableName));
+        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tableName)));
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getColumns($tableName)
+    {
+        $columns = array();
+        $rows = $this->fetchAll(sprintf('SHOW COLUMNS FROM %s', $this->quoteTableName($tableName)));
+        foreach ($rows as $columnInfo) {
+
+            $phinxType = $this->getPhinxType($columnInfo['Type']);
+
+            $column = new Column();
+            $column->setName($columnInfo['Field'])
+                   ->setNull($columnInfo['Null'] !== 'NO')
+                   ->setDefault($columnInfo['Default'])
+                   ->setType($phinxType['name'])
+                   ->setLimit($phinxType['limit']);
+
+            if ($columnInfo['Extra'] === 'auto_increment') {
+                $column->setIdentity(true);
+            }
+
+            $columns[] = $column;
+        }
+
+        return $columns;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasColumn($tableName, $columnName)
+    {
+        $rows = $this->fetchAll(sprintf('SHOW COLUMNS FROM %s', $this->quoteTableName($tableName)));
+        foreach ($rows as $column) {
+            if (strcasecmp($column['Field'], $columnName) === 0) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Get the defintion for a `DEFAULT` statement.
+     *
+     * @param  mixed $default
+     * @return string
+     */
+    protected function getDefaultValueDefinition($default)
+    {
+        if (is_string($default) && 'CURRENT_TIMESTAMP' !== $default) {
+            $default = $this->getConnection()->quote($default);
+        } elseif (is_bool($default)) {
+            $default = $this->castToBool($default);
+        }
+        return isset($default) ? ' DEFAULT ' . $default : '';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addColumn(Table $table, Column $column)
+    {
+        $this->startCommandTimer();
+        $sql = sprintf(
+            'ALTER TABLE %s ADD %s %s',
+            $this->quoteTableName($table->getName()),
+            $this->quoteColumnName($column->getName()),
+            $this->getColumnSqlDefinition($column)
+        );
+
+        if ($column->getAfter()) {
+            $sql .= ' AFTER ' . $this->quoteColumnName($column->getAfter());
+        }
+
+        $this->writeCommand('addColumn', array($table->getName(), $column->getName(), $column->getType()));
+        $this->execute($sql);
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function renameColumn($tableName, $columnName, $newColumnName)
+    {
+        $this->startCommandTimer();
+        $rows = $this->fetchAll(sprintf('DESCRIBE %s', $this->quoteTableName($tableName)));
+        foreach ($rows as $row) {
+            if (strcasecmp($row['Field'], $columnName) === 0) {
+                $null = ($row['Null'] == 'NO') ? 'NOT NULL' : 'NULL';
+                $extra = ' ' . strtoupper($row['Extra']);
+                if (!is_null($row['Default'])) {
+                    $extra .= $this->getDefaultValueDefinition($row['Default']);
+                }
+                $definition = $row['Type'] . ' ' . $null . $extra;
+
+                $this->writeCommand('renameColumn', array($tableName, $columnName, $newColumnName));
+                $this->execute(
+                    sprintf(
+                        'ALTER TABLE %s CHANGE COLUMN %s %s %s',
+                        $this->quoteTableName($tableName),
+                        $this->quoteColumnName($columnName),
+                        $this->quoteColumnName($newColumnName),
+                        $definition
+                    )
+                );
+                $this->endCommandTimer();
+                return;
+            }
+        }
+
+        throw new \InvalidArgumentException(sprintf(
+            'The specified column doesn\'t exist: '
+            . $columnName
+        ));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function changeColumn($tableName, $columnName, Column $newColumn)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('changeColumn', array($tableName, $columnName, $newColumn->getType()));
+        $after = $newColumn->getAfter() ? ' AFTER ' . $this->quoteColumnName($newColumn->getAfter()) : '';
+        $this->execute(
+            sprintf(
+                'ALTER TABLE %s CHANGE %s %s %s%s',
+                $this->quoteTableName($tableName),
+                $this->quoteColumnName($columnName),
+                $this->quoteColumnName($newColumn->getName()),
+                $this->getColumnSqlDefinition($newColumn),
+                $after
+            )
+        );
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropColumn($tableName, $columnName)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('dropColumn', array($tableName, $columnName));
+        $this->execute(
+            sprintf(
+                'ALTER TABLE %s DROP COLUMN %s',
+                $this->quoteTableName($tableName),
+                $this->quoteColumnName($columnName)
+            )
+        );
+        $this->endCommandTimer();
+    }
+
+    /**
+     * Get an array of indexes from a particular table.
+     *
+     * @param string $tableName Table Name
+     * @return array
+     */
+    protected function getIndexes($tableName)
+    {
+        $indexes = array();
+        $rows = $this->fetchAll(sprintf('SHOW INDEXES FROM %s', $this->quoteTableName($tableName)));
+        foreach ($rows as $row) {
+            if (!isset($indexes[$row['Key_name']])) {
+                $indexes[$row['Key_name']] = array('columns' => array());
+            }
+            $indexes[$row['Key_name']]['columns'][] = strtolower($row['Column_name']);
+        }
+        return $indexes;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasIndex($tableName, $columns)
+    {
+        if (is_string($columns)) {
+            $columns = array($columns); // str to array
+        }
+
+        $columns = array_map('strtolower', $columns);
+        $indexes = $this->getIndexes($tableName);
+
+        foreach ($indexes as $index) {
+            if ($columns == $index['columns']) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasIndexByName($tableName, $indexName)
+    {
+        $indexes = $this->getIndexes($tableName);
+
+        foreach ($indexes as $name => $index) {
+            if ($name === $indexName) {
+                 return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addIndex(Table $table, Index $index)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('addIndex', array($table->getName(), $index->getColumns()));
+        $this->execute(
+            sprintf(
+                'ALTER TABLE %s ADD %s',
+                $this->quoteTableName($table->getName()),
+                $this->getIndexSqlDefinition($index)
+            )
+        );
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropIndex($tableName, $columns)
+    {
+        $this->startCommandTimer();
+        if (is_string($columns)) {
+            $columns = array($columns); // str to array
+        }
+
+        $this->writeCommand('dropIndex', array($tableName, $columns));
+        $indexes = $this->getIndexes($tableName);
+        $columns = array_map('strtolower', $columns);
+
+        foreach ($indexes as $indexName => $index) {
+            if ($columns == $index['columns']) {
+                $this->execute(
+                    sprintf(
+                        'ALTER TABLE %s DROP INDEX %s',
+                        $this->quoteTableName($tableName),
+                        $this->quoteColumnName($indexName)
+                    )
+                );
+                $this->endCommandTimer();
+                return;
+            }
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropIndexByName($tableName, $indexName)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('dropIndexByName', array($tableName, $indexName));
+        $indexes = $this->getIndexes($tableName);
+
+        foreach ($indexes as $name => $index) {
+            //$a = array_diff($columns, $index['columns']);
+            if ($name === $indexName) {
+                $this->execute(
+                    sprintf(
+                        'ALTER TABLE %s DROP INDEX %s',
+                        $this->quoteTableName($tableName),
+                        $this->quoteColumnName($indexName)
+                    )
+                );
+                $this->endCommandTimer();
+                return;
+            }
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasForeignKey($tableName, $columns, $constraint = null)
+    {
+        if (is_string($columns)) {
+            $columns = array($columns); // str to array
+        }
+        $foreignKeys = $this->getForeignKeys($tableName);
+        if ($constraint) {
+            if (isset($foreignKeys[$constraint])) {
+                return !empty($foreignKeys[$constraint]);
+            }
+            return false;
+        } else {
+            foreach ($foreignKeys as $key) {
+                $a = array_diff($columns, $key['columns']);
+                if ($columns == $key['columns']) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Get an array of foreign keys from a particular table.
+     *
+     * @param string $tableName Table Name
+     * @return array
+     */
+    protected function getForeignKeys($tableName)
+    {
+        $foreignKeys = array();
+        $rows = $this->fetchAll(sprintf(
+            "SELECT
+              CONSTRAINT_NAME,
+              TABLE_NAME,
+              COLUMN_NAME,
+              REFERENCED_TABLE_NAME,
+              REFERENCED_COLUMN_NAME
+            FROM information_schema.KEY_COLUMN_USAGE
+            WHERE REFERENCED_TABLE_SCHEMA = DATABASE()
+              AND REFERENCED_TABLE_NAME IS NOT NULL
+              AND TABLE_NAME = '%s'
+            ORDER BY POSITION_IN_UNIQUE_CONSTRAINT",
+            $tableName
+        ));
+        foreach ($rows as $row) {
+            $foreignKeys[$row['CONSTRAINT_NAME']]['table'] = $row['TABLE_NAME'];
+            $foreignKeys[$row['CONSTRAINT_NAME']]['columns'][] = $row['COLUMN_NAME'];
+            $foreignKeys[$row['CONSTRAINT_NAME']]['referenced_table'] = $row['REFERENCED_TABLE_NAME'];
+            $foreignKeys[$row['CONSTRAINT_NAME']]['referenced_columns'][] = $row['REFERENCED_COLUMN_NAME'];
+        }
+        return $foreignKeys;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addForeignKey(Table $table, ForeignKey $foreignKey)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('addForeignKey', array($table->getName(), $foreignKey->getColumns()));
+        $this->execute(
+            sprintf(
+                'ALTER TABLE %s ADD %s',
+                $this->quoteTableName($table->getName()),
+                $this->getForeignKeySqlDefinition($foreignKey)
+            )
+        );
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropForeignKey($tableName, $columns, $constraint = null)
+    {
+        $this->startCommandTimer();
+        if (is_string($columns)) {
+            $columns = array($columns); // str to array
+        }
+
+        $this->writeCommand('dropForeignKey', array($tableName, $columns));
+
+        if ($constraint) {
+            $this->execute(
+                sprintf(
+                    'ALTER TABLE %s DROP FOREIGN KEY %s',
+                    $this->quoteTableName($tableName),
+                    $constraint
+                )
+            );
+            $this->endCommandTimer();
+            return;
+        } else {
+            foreach ($columns as $column) {
+                $rows = $this->fetchAll(sprintf(
+                    "SELECT
+                        CONSTRAINT_NAME
+                      FROM information_schema.KEY_COLUMN_USAGE
+                      WHERE REFERENCED_TABLE_SCHEMA = DATABASE()
+                        AND REFERENCED_TABLE_NAME IS NOT NULL
+                        AND TABLE_NAME = '%s'
+                        AND COLUMN_NAME = '%s'
+                      ORDER BY POSITION_IN_UNIQUE_CONSTRAINT",
+                    $tableName,
+                    $column
+                ));
+                foreach ($rows as $row) {
+                    $this->dropForeignKey($tableName, $columns, $row['CONSTRAINT_NAME']);
+                }
+            }
+        }
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSqlType($type, $limit = null)
+    {
+        switch ($type) {
+            case static::PHINX_TYPE_STRING:
+                return array('name' => 'varchar', 'limit' => $limit ? $limit : 255);
+                break;
+            case static::PHINX_TYPE_CHAR:
+                return array('name' => 'char', 'limit' => $limit ? $limit : 255);
+                break;
+            case static::PHINX_TYPE_TEXT:
+                if ($limit) {
+                    $sizes = array(
+                        // Order matters! Size must always be tested from longest to shortest!
+                        'longtext'   => static::TEXT_LONG,
+                        'mediumtext' => static::TEXT_MEDIUM,
+                        'text'       => static::TEXT_REGULAR,
+                        'tinytext'   => static::TEXT_SMALL,
+                    );
+                    foreach ($sizes as $name => $length) {
+                        if ($limit >= $length) {
+                            return array('name' => $name);
+                        }
+                    }
+                }
+                return array('name' => 'text');
+                break;
+            case static::PHINX_TYPE_BINARY:
+                return array('name' => 'binary', 'limit' => $limit ? $limit : 255);
+                break;
+            case static::PHINX_TYPE_VARBINARY:
+                return array('name' => 'varbinary', 'limit' => $limit ? $limit : 255);
+                break;
+            case static::PHINX_TYPE_BLOB:
+                if ($limit) {
+                    $sizes = array(
+                        // Order matters! Size must always be tested from longest to shortest!
+                        'longblob'   => static::BLOB_LONG,
+                        'mediumblob' => static::BLOB_MEDIUM,
+                        'blob'       => static::BLOB_REGULAR,
+                        'tinyblob'   => static::BLOB_SMALL,
+                    );
+                    foreach ($sizes as $name => $length) {
+                        if ($limit >= $length) {
+                            return array('name' => $name);
+                        }
+                    }
+                }
+                return array('name' => 'blob');
+                break;
+            case static::PHINX_TYPE_INTEGER:
+                if ($limit && $limit >= static::INT_TINY) {
+                    $sizes = array(
+                        // Order matters! Size must always be tested from longest to shortest!
+                        'bigint'    => static::INT_BIG,
+                        'int'       => static::INT_REGULAR,
+                        'mediumint' => static::INT_MEDIUM,
+                        'smallint'  => static::INT_SMALL,
+                        'tinyint'   => static::INT_TINY,
+                    );
+                    $limits = array(
+                        'int'    => 11,
+                        'bigint' => 20,
+                    );
+                    foreach ($sizes as $name => $length) {
+                        if ($limit >= $length) {
+                            $def = array('name' => $name);
+                            if (isset($limits[$name])) {
+                                $def['limit'] = $limits[$name];
+                            }
+                            return $def;
+                        }
+                    }
+                } elseif (!$limit) {
+                    $limit = 11;
+                }
+                return array('name' => 'int', 'limit' => $limit);
+                break;
+            case static::PHINX_TYPE_BIG_INTEGER:
+                return array('name' => 'bigint', 'limit' => 20);
+                break;
+            case static::PHINX_TYPE_FLOAT:
+                return array('name' => 'float');
+                break;
+            case static::PHINX_TYPE_DECIMAL:
+                return array('name' => 'decimal');
+                break;
+            case static::PHINX_TYPE_DATETIME:
+                return array('name' => 'datetime');
+                break;
+            case static::PHINX_TYPE_TIMESTAMP:
+                return array('name' => 'timestamp');
+                break;
+            case static::PHINX_TYPE_TIME:
+                return array('name' => 'time');
+                break;
+            case static::PHINX_TYPE_DATE:
+                return array('name' => 'date');
+                break;
+            case static::PHINX_TYPE_BOOLEAN:
+                return array('name' => 'tinyint', 'limit' => 1);
+                break;
+            case static::PHINX_TYPE_UUID:
+                return array('name' => 'char', 'limit' => 36);
+            // Geospatial database types
+            case static::PHINX_TYPE_GEOMETRY:
+            case static::PHINX_TYPE_POINT:
+            case static::PHINX_TYPE_LINESTRING:
+            case static::PHINX_TYPE_POLYGON:
+                return array('name' => $type);
+            case static::PHINX_TYPE_ENUM:
+                return array('name' => 'enum');
+                break;
+            case static::PHINX_TYPE_SET:
+                return array('name' => 'set');
+                break;
+            case static::TYPE_YEAR:
+                if (!$limit || in_array($limit, array(2, 4)))
+                    $limit = 4;
+                return array('name' => 'year', 'limit' => $limit);
+                break;
+            case static::PHINX_TYPE_JSON:
+                return array('name' => 'json');
+                break;
+            default:
+                throw new \RuntimeException('The type: "' . $type . '" is not supported.');
+        }
+    }
+
+    /**
+     * Returns Phinx type by SQL type
+     *
+     * @param string $sqlTypeDef
+     * @throws \RuntimeException
+     * @internal param string $sqlType SQL type
+     * @returns string Phinx type
+     */
+    public function getPhinxType($sqlTypeDef)
+    {
+        if (!preg_match('/^([\w]+)(\(([\d]+)*(,([\d]+))*\))*(.+)*$/', $sqlTypeDef, $matches)) {
+            throw new \RuntimeException('Column type ' . $sqlTypeDef . ' is not supported');
+        } else {
+            $limit = null;
+            $precision = null;
+            $type = $matches[1];
+            if (count($matches) > 2) {
+                $limit = $matches[3] ? (int) $matches[3] : null;
+            }
+            if (count($matches) > 4) {
+                $precision = (int) $matches[5];
+            }
+            if ($type === 'tinyint' && $limit === 1) {
+                $type = static::PHINX_TYPE_BOOLEAN;
+                $limit = null;
+            }
+            switch ($type) {
+                case 'varchar':
+                    $type = static::PHINX_TYPE_STRING;
+                    if ($limit === 255) {
+                        $limit = null;
+                    }
+                    break;
+                case 'char':
+                    $type = static::PHINX_TYPE_CHAR;
+                    if ($limit === 255) {
+                        $limit = null;
+                    }
+                    if ($limit === 36) {
+                        $type = static::PHINX_TYPE_UUID;
+                    }
+                    break;
+                case 'tinyint':
+                    $type  = static::PHINX_TYPE_INTEGER;
+                    $limit = static::INT_TINY;
+                    break;
+                case 'smallint':
+                    $type  = static::PHINX_TYPE_INTEGER;
+                    $limit = static::INT_SMALL;
+                    break;
+                case 'mediumint':
+                    $type  = static::PHINX_TYPE_INTEGER;
+                    $limit = static::INT_MEDIUM;
+                    break;
+                case 'int':
+                    $type = static::PHINX_TYPE_INTEGER;
+                    if ($limit === 11) {
+                        $limit = null;
+                    }
+                    break;
+                case 'bigint':
+                    if ($limit === 20) {
+                        $limit = null;
+                    }
+                    $type = static::PHINX_TYPE_BIG_INTEGER;
+                    break;
+                case 'blob':
+                    $type = static::PHINX_TYPE_BINARY;
+                    break;
+                case 'tinyblob':
+                    $type  = static::PHINX_TYPE_BINARY;
+                    $limit = static::BLOB_TINY;
+                    break;
+                case 'mediumblob':
+                    $type  = static::PHINX_TYPE_BINARY;
+                    $limit = static::BLOB_MEDIUM;
+                    break;
+                case 'longblob':
+                    $type  = static::PHINX_TYPE_BINARY;
+                    $limit = static::BLOB_LONG;
+                    break;
+                case 'tinytext':
+                    $type  = static::PHINX_TYPE_TEXT;
+                    $limit = static::TEXT_TINY;
+                    break;
+                case 'mediumtext':
+                    $type  = static::PHINX_TYPE_TEXT;
+                    $limit = static::TEXT_MEDIUM;
+                    break;
+                case 'longtext':
+                    $type  = static::PHINX_TYPE_TEXT;
+                    $limit = static::TEXT_LONG;
+                    break;
+            }
+
+            $this->getSqlType($type, $limit);
+
+            return array(
+                'name' => $type,
+                'limit' => $limit,
+                'precision' => $precision
+            );
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createDatabase($name, $options = array())
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('createDatabase', array($name));
+        $charset = isset($options['charset']) ? $options['charset'] : 'utf8';
+
+        if (isset($options['collation'])) {
+            $this->execute(sprintf('CREATE DATABASE `%s` DEFAULT CHARACTER SET `%s` COLLATE `%s`', $name, $charset, $options['collation']));
+        } else {
+            $this->execute(sprintf('CREATE DATABASE `%s` DEFAULT CHARACTER SET `%s`', $name, $charset));
+        }
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasDatabase($name)
+    {
+        $rows = $this->fetchAll(
+            sprintf(
+                'SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = \'%s\'',
+                $name
+            )
+        );
+
+        foreach ($rows as $row) {
+            if (!empty($row)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropDatabase($name)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('dropDatabase', array($name));
+        $this->execute(sprintf('DROP DATABASE IF EXISTS `%s`', $name));
+        $this->endCommandTimer();
+    }
+
+    /**
+     * Gets the MySQL Column Definition for a Column object.
+     *
+     * @param Column $column Column
+     * @return string
+     */
+    protected function getColumnSqlDefinition(Column $column)
+    {
+        $sqlType = $this->getSqlType($column->getType(), $column->getLimit());
+
+        $def = '';
+        $def .= strtoupper($sqlType['name']);
+        if ($column->getPrecision() && $column->getScale()) {
+            $def .= '(' . $column->getPrecision() . ',' . $column->getScale() . ')';
+        } elseif (isset($sqlType['limit'])) {
+            $def .= '(' . $sqlType['limit'] . ')';
+        }
+        if (($values = $column->getValues()) && is_array($values)) {
+            $def .= "('" . implode("', '", $values) . "')";
+        }
+        $def .= (!$column->isSigned() && isset($this->signedColumnTypes[$column->getType()])) ? ' unsigned' : '' ;
+        $def .= ($column->isNull() == false) ? ' NOT NULL' : ' NULL';
+        $def .= ($column->isIdentity()) ? ' AUTO_INCREMENT' : '';
+        $def .= $this->getDefaultValueDefinition($column->getDefault());
+
+        if ($column->getComment()) {
+            $def .= ' COMMENT ' . $this->getConnection()->quote($column->getComment());
+        }
+
+        if ($column->getUpdate()) {
+            $def .= ' ON UPDATE ' . $column->getUpdate();
+        }
+
+        return $def;
+    }
+
+    /**
+     * Gets the MySQL Index Definition for an Index object.
+     *
+     * @param Index $index Index
+     * @return string
+     */
+    protected function getIndexSqlDefinition(Index $index)
+    {
+        $def = '';
+        $limit = '';
+        if ($index->getLimit()) {
+            $limit = '(' . $index->getLimit() . ')';
+        }
+
+        if ($index->getType() == Index::UNIQUE) {
+            $def .= ' UNIQUE';
+        }
+
+        if ($index->getType() == Index::FULLTEXT) {
+            $def .= ' FULLTEXT';
+        }
+
+        $def .= ' KEY';
+
+        if (is_string($index->getName())) {
+            $def .= ' `' . $index->getName() . '`';
+        }
+
+        $def .= ' (`' . implode('`,`', $index->getColumns()) . '`' . $limit . ')';
+
+        return $def;
+    }
+
+    /**
+     * Gets the MySQL Foreign Key Definition for an ForeignKey object.
+     *
+     * @param ForeignKey $foreignKey
+     * @return string
+     */
+    protected function getForeignKeySqlDefinition(ForeignKey $foreignKey)
+    {
+        $def = '';
+        if ($foreignKey->getConstraint()) {
+            $def .= ' CONSTRAINT ' . $this->quoteColumnName($foreignKey->getConstraint());
+        }
+        $columnNames = array();
+        foreach ($foreignKey->getColumns() as $column) {
+            $columnNames[] = $this->quoteColumnName($column);
+        }
+        $def .= ' FOREIGN KEY (' . implode(',', $columnNames) . ')';
+        $refColumnNames = array();
+        foreach ($foreignKey->getReferencedColumns() as $column) {
+            $refColumnNames[] = $this->quoteColumnName($column);
+        }
+        $def .= ' REFERENCES ' . $this->quoteTableName($foreignKey->getReferencedTable()->getName()) . ' (' . implode(',', $refColumnNames) . ')';
+        if ($foreignKey->getOnDelete()) {
+            $def .= ' ON DELETE ' . $foreignKey->getOnDelete();
+        }
+        if ($foreignKey->getOnUpdate()) {
+            $def .= ' ON UPDATE ' . $foreignKey->getOnUpdate();
+        }
+        return $def;
+    }
+
+    /**
+     * Describes a database table. This is a MySQL adapter specific method.
+     *
+     * @param string $tableName Table name
+     * @return array
+     */
+    public function describeTable($tableName)
+    {
+        $options = $this->getOptions();
+
+        // mysql specific
+        $sql = sprintf(
+            "SELECT *
+             FROM information_schema.tables
+             WHERE table_schema = '%s'
+             AND table_name = '%s'",
+            $options['name'],
+            $tableName
+        );
+
+        return $this->fetchRow($sql);
+    }
+
+    /**
+     * Returns MySQL column types (inherited and MySQL specified).
+     * @return array
+     */
+    public function getColumnTypes()
+    {
+        return array_merge(parent::getColumnTypes(), array('enum', 'set', 'year', 'json'));
+    }
+}
diff --git a/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/PdoAdapter.php b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/PdoAdapter.php
new file mode 100755
index 0000000..9924c3a
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/PdoAdapter.php
@@ -0,0 +1,587 @@
+<?php
+/**
+ * Phinx
+ *
+ * (The MIT license)
+ * Copyright (c) 2015 Rob Morgan
+ *
+ * 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.
+ *
+ * @package    Phinx
+ * @subpackage Phinx\Db\Adapter
+ */
+namespace Phinx\Db\Adapter;
+
+use think\console\Input as InputInterface;
+use think\console\Output as OutputInterface;
+use Phinx\Db\Table;
+use Phinx\Db\Table\Column;
+use Phinx\Migration\MigrationInterface;
+
+/**
+ * Phinx PDO Adapter.
+ *
+ * @author Rob Morgan <robbym@gmail.com>
+ */
+abstract class PdoAdapter implements AdapterInterface
+{
+    /**
+     * @var array
+     */
+    protected $options = array();
+
+    /**
+     * @var InputInterface
+     */
+    protected $input;
+
+    /**
+     * @var OutputInterface
+     */
+    protected $output;
+
+    /**
+     * @var string
+     */
+    protected $schemaTableName = 'migrations';
+
+    /**
+     * @var \PDO
+     */
+    protected $connection;
+
+    /**
+     * @var float
+     */
+    protected $commandStartTime;
+
+    /**
+     * Class Constructor.
+     *
+     * @param array $options Options
+     * @param InputInterface $input Input Interface
+     * @param OutputInterface $output Output Interface
+     */
+    public function __construct(array $options, InputInterface $input = null, OutputInterface $output = null)
+    {
+        $this->setOptions($options);
+        if (null !== $input) {
+            $this->setInput($input);
+        }
+        if (null !== $output) {
+            $this->setOutput($output);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setOptions(array $options)
+    {
+        $this->options = $options;
+
+        if (isset($options['default_migration_table'])) {
+            $this->setSchemaTableName($options['default_migration_table']);
+        }
+
+        if (isset($options['connection'])) {
+            $this->setConnection($options['connection']);
+        }
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getOptions()
+    {
+        return $this->options;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasOption($name)
+    {
+        return isset($this->options[$name]);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getOption($name)
+    {
+        if (!$this->hasOption($name)) {
+            return;
+        }
+        return $this->options[$name];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setInput(InputInterface $input)
+    {
+        $this->input = $input;
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getInput()
+    {
+        return $this->input;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setOutput(OutputInterface $output)
+    {
+        $this->output = $output;
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getOutput()
+    {
+        if (null === $this->output) {
+            $output = new OutputInterface('nothing');
+            $this->setOutput($output);
+        }
+        return $this->output;
+    }
+
+    /**
+     * Sets the schema table name.
+     *
+     * @param string $schemaTableName Schema Table Name
+     * @return PdoAdapter
+     */
+    public function setSchemaTableName($schemaTableName)
+    {
+        $this->schemaTableName = $schemaTableName;
+        return $this;
+    }
+
+    /**
+     * Gets the schema table name.
+     *
+     * @return string
+     */
+    public function getSchemaTableName()
+    {
+        return $this->schemaTableName;
+    }
+
+    /**
+     * Sets the database connection.
+     *
+     * @param \PDO $connection Connection
+     * @return AdapterInterface
+     */
+    public function setConnection(\PDO $connection)
+    {
+        $this->connection = $connection;
+
+        // Create the schema table if it doesn't already exist
+        if (!$this->hasSchemaTable()) {
+            $this->createSchemaTable();
+        } else {
+            $table = new Table($this->getSchemaTableName(), array(), $this);
+            if (!$table->hasColumn('migration_name')) {
+                $table
+                    ->addColumn('migration_name', 'string',
+                        array('limit' => 100, 'after' => 'version', 'default' => null, 'null' => true)
+                    )
+                    ->save();
+            }
+            if (!$table->hasColumn('breakpoint')) {
+                $table
+                    ->addColumn('breakpoint', 'boolean', array('default' => false))
+                    ->save();
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Gets the database connection
+     *
+     * @return \PDO
+     */
+    public function getConnection()
+    {
+        if (null === $this->connection) {
+            $this->connect();
+        }
+        return $this->connection;
+    }
+
+    /**
+     * Sets the command start time
+     *
+     * @param int $time
+     * @return AdapterInterface
+     */
+    public function setCommandStartTime($time)
+    {
+        $this->commandStartTime = $time;
+        return $this;
+    }
+
+    /**
+     * Gets the command start time
+     *
+     * @return int
+     */
+    public function getCommandStartTime()
+    {
+        return $this->commandStartTime;
+    }
+
+    /**
+     * Start timing a command.
+     *
+     * @return void
+     */
+    public function startCommandTimer()
+    {
+        $this->setCommandStartTime(microtime(true));
+    }
+
+    /**
+     * Stop timing the current command and write the elapsed time to the
+     * output.
+     *
+     * @return void
+     */
+    public function endCommandTimer()
+    {
+        $end = microtime(true);
+        if (OutputInterface::VERBOSITY_VERBOSE <= $this->getOutput()->getVerbosity()) {
+            $this->getOutput()->writeln('    -> ' . sprintf('%.4fs', $end - $this->getCommandStartTime()));
+        }
+    }
+
+    /**
+     * Write a Phinx command to the output.
+     *
+     * @param string $command Command Name
+     * @param array  $args    Command Args
+     * @return void
+     */
+    public function writeCommand($command, $args = array())
+    {
+        if (OutputInterface::VERBOSITY_VERBOSE <= $this->getOutput()->getVerbosity()) {
+            if (count($args)) {
+                $outArr = array();
+                foreach ($args as $arg) {
+                    if (is_array($arg)) {
+                        $arg = array_map(function ($value) {
+                            return '\'' . $value . '\'';
+                        }, $arg);
+                        $outArr[] = '[' . implode(', ', $arg)  . ']';
+                        continue;
+                    }
+
+                    $outArr[] = '\'' . $arg . '\'';
+                }
+                $this->getOutput()->writeln(' -- ' . $command . '(' . implode(', ', $outArr) . ')');
+                return;
+            }
+            $this->getOutput()->writeln(' -- ' . $command);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function connect()
+    {
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function disconnect()
+    {
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function execute($sql)
+    {
+        return $this->getConnection()->exec($sql);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function query($sql)
+    {
+        return $this->getConnection()->query($sql);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function fetchRow($sql)
+    {
+        $result = $this->query($sql);
+        return $result->fetch();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function fetchAll($sql)
+    {
+        $rows = array();
+        $result = $this->query($sql);
+        while ($row = $result->fetch()) {
+            $rows[] = $row;
+        }
+        return $rows;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function insert(Table $table, $row)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('insert', array($table->getName()));
+
+        $sql = sprintf(
+            "INSERT INTO %s ",
+            $this->quoteTableName($table->getName())
+        );
+
+        $columns = array_keys($row);
+        $sql .= "(". implode(', ', array_map(array($this, 'quoteColumnName'), $columns)) . ")";
+        $sql .= " VALUES (" . implode(', ', array_fill(0, count($columns), '?')) . ")";
+
+        $stmt = $this->getConnection()->prepare($sql);
+        $stmt->execute(array_values($row));
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getVersions()
+    {
+        $rows = $this->getVersionLog();
+
+        return array_keys($rows);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getVersionLog()
+    {
+        $result = array();
+        $rows = $this->fetchAll(sprintf('SELECT * FROM %s ORDER BY version ASC', $this->getSchemaTableName()));
+        foreach ($rows as $version) {
+            $result[$version['version']] = $version;
+        }
+
+        return $result;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function migrated(MigrationInterface $migration, $direction, $startTime, $endTime)
+    {
+        if (strcasecmp($direction, MigrationInterface::UP) === 0) {
+            // up
+            $sql = sprintf(
+                "INSERT INTO %s (%s, %s, %s, %s, %s) VALUES ('%s', '%s', '%s', '%s', %s);",
+                $this->getSchemaTableName(),
+                $this->quoteColumnName('version'),
+                $this->quoteColumnName('migration_name'),
+                $this->quoteColumnName('start_time'),
+                $this->quoteColumnName('end_time'),
+                $this->quoteColumnName('breakpoint'),
+                $migration->getVersion(),
+                substr($migration->getName(), 0, 100),
+                $startTime,
+                $endTime,
+                $this->castToBool(false)
+            );
+
+            $this->query($sql);
+        } else {
+            // down
+            $sql = sprintf(
+                "DELETE FROM %s WHERE %s = '%s'",
+                $this->getSchemaTableName(),
+                $this->quoteColumnName('version'),
+                $migration->getVersion()
+            );
+
+            $this->query($sql);
+        }
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function toggleBreakpoint(MigrationInterface $migration)
+    {
+        $this->query(
+            sprintf(
+                'UPDATE %1$s SET %2$s = CASE %2$s WHEN %3$s THEN %4$s ELSE %3$s END WHERE %5$s = \'%6$s\';',
+                $this->getSchemaTableName(),
+                $this->quoteColumnName('breakpoint'),
+                $this->castToBool(true),
+                $this->castToBool(false),
+                $this->quoteColumnName('version'),
+                $migration->getVersion()
+            )
+        );
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function resetAllBreakpoints()
+    {
+        return $this->execute(
+            sprintf(
+                'UPDATE %1$s SET %2$s = %3$s WHERE %2$s <> %3$s;',
+                $this->getSchemaTableName(),
+                $this->quoteColumnName('breakpoint'),
+                $this->castToBool(false)
+            )
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasSchemaTable()
+    {
+        return $this->hasTable($this->getSchemaTableName());
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createSchemaTable()
+    {
+        try {
+            $options = array(
+                'id'          => false,
+                'primary_key' => 'version'
+            );
+
+            $table = new Table($this->getSchemaTableName(), $options, $this);
+
+            if ($this->getConnection()->getAttribute(\PDO::ATTR_DRIVER_NAME) === 'mysql'
+                && version_compare($this->getConnection()->getAttribute(\PDO::ATTR_SERVER_VERSION), '5.6.0', '>=')) {
+                $table->addColumn('version', 'biginteger', array('limit' => 14))
+                      ->addColumn('migration_name', 'string', array('limit' => 100, 'default' => null, 'null' => true))
+                      ->addColumn('start_time', 'timestamp', array('default' => 'CURRENT_TIMESTAMP'))
+                      ->addColumn('end_time', 'timestamp', array('default' => 'CURRENT_TIMESTAMP'))
+                      ->addColumn('breakpoint', 'boolean', array('default' => false))
+                      ->save();
+            } else {
+                $table->addColumn('version', 'biginteger')
+                      ->addColumn('migration_name', 'string', array('limit' => 100, 'default' => null, 'null' => true))
+                      ->addColumn('start_time', 'timestamp')
+                      ->addColumn('end_time', 'timestamp')
+                      ->addColumn('breakpoint', 'boolean', array('default' => false))
+                      ->save();
+            }
+        } catch (\Exception $exception) {
+            throw new \InvalidArgumentException('There was a problem creating the schema table: ' . $exception->getMessage());
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getAdapterType()
+    {
+        return $this->getOption('adapter');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getColumnTypes()
+    {
+        return array(
+            'string',
+            'char',
+            'text',
+            'integer',
+            'biginteger',
+            'float',
+            'decimal',
+            'datetime',
+            'timestamp',
+            'time',
+            'date',
+            'blob',
+            'binary',
+            'varbinary',
+            'boolean',
+            'uuid',
+            // Geospatial data types
+            'geometry',
+            'point',
+            'linestring',
+            'polygon',
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isValidColumnType(Column $column) {
+        return in_array($column->getType(), $this->getColumnTypes());
+    }
+
+    /**
+     * Cast a value to a boolean appropriate for the adapter.
+     *
+     * @param mixed $value The value to be cast
+     *
+     * @return mixed
+     */
+    public function castToBool($value)
+    {
+        return (bool) $value ? 1 : 0;
+    }
+}
diff --git a/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/PostgresAdapter.php b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/PostgresAdapter.php
new file mode 100755
index 0000000..3c506bd
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/PostgresAdapter.php
@@ -0,0 +1,1182 @@
+<?php
+/**
+ * Phinx
+ *
+ * (The MIT license)
+ * Copyright (c) 2015 Rob Morgan
+ *
+ * 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.
+ *
+ * @package    Phinx
+ * @subpackage Phinx\Db\Adapter
+ */
+namespace Phinx\Db\Adapter;
+
+use Phinx\Db\Table;
+use Phinx\Db\Table\Column;
+use Phinx\Db\Table\Index;
+use Phinx\Db\Table\ForeignKey;
+
+class PostgresAdapter extends PdoAdapter implements AdapterInterface
+{
+    const INT_SMALL = 65535;
+
+    /**
+     * Columns with comments
+     *
+     * @var array
+     */
+    protected $columnsWithComments = array();
+
+    /**
+     * {@inheritdoc}
+     */
+    public function connect()
+    {
+        if (null === $this->connection) {
+            if (!class_exists('PDO') || !in_array('pgsql', \PDO::getAvailableDrivers(), true)) {
+                // @codeCoverageIgnoreStart
+                throw new \RuntimeException('You need to enable the PDO_Pgsql extension for Phinx to run properly.');
+                // @codeCoverageIgnoreEnd
+            }
+
+            $db = null;
+            $options = $this->getOptions();
+
+            // if port is specified use it, otherwise use the PostgreSQL default
+            if (isset($options['port'])) {
+                $dsn = 'pgsql:host=' . $options['host'] . ';port=' . $options['port'] . ';dbname=' . $options['name'];
+            } else {
+                $dsn = 'pgsql:host=' . $options['host'] . ';dbname=' . $options['name'];
+            }
+
+            try {
+                $db = new \PDO($dsn, $options['user'], $options['pass'], array(\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION));
+            } catch (\PDOException $exception) {
+                throw new \InvalidArgumentException(sprintf(
+                    'There was a problem connecting to the database: %s',
+                    $exception->getMessage()
+                ));
+            }
+
+            $this->setConnection($db);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function disconnect()
+    {
+        $this->connection = null;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasTransactions()
+    {
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function beginTransaction()
+    {
+        $this->execute('BEGIN');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function commitTransaction()
+    {
+        $this->execute('COMMIT');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function rollbackTransaction()
+    {
+        $this->execute('ROLLBACK');
+    }
+
+    /**
+     * Quotes a schema name for use in a query.
+     *
+     * @param string $schemaName Schema Name
+     * @return string
+     */
+    public function quoteSchemaName($schemaName)
+    {
+        return $this->quoteColumnName($schemaName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function quoteTableName($tableName)
+    {
+        return $this->quoteSchemaName($this->getSchemaName()) . '.' . $this->quoteColumnName($tableName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function quoteColumnName($columnName)
+    {
+        return '"'. $columnName . '"';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasTable($tableName)
+    {
+        $result = $this->getConnection()->query(
+            sprintf(
+                'SELECT *
+                FROM information_schema.tables
+                WHERE table_schema = %s
+                AND lower(table_name) = lower(%s)',
+                $this->getConnection()->quote($this->getSchemaName()),
+                $this->getConnection()->quote($tableName)
+            )
+        );
+
+        return $result->rowCount() === 1;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createTable(Table $table)
+    {
+        $this->startCommandTimer();
+        $options = $table->getOptions();
+
+         // Add the default primary key
+        $columns = $table->getPendingColumns();
+        if (!isset($options['id']) || (isset($options['id']) && $options['id'] === true)) {
+            $column = new Column();
+            $column->setName('id')
+                   ->setType('integer')
+                   ->setIdentity(true);
+
+            array_unshift($columns, $column);
+            $options['primary_key'] = 'id';
+
+        } elseif (isset($options['id']) && is_string($options['id'])) {
+            // Handle id => "field_name" to support AUTO_INCREMENT
+            $column = new Column();
+            $column->setName($options['id'])
+                   ->setType('integer')
+                   ->setIdentity(true);
+
+            array_unshift($columns, $column);
+            $options['primary_key'] = $options['id'];
+        }
+
+        // TODO - process table options like collation etc
+        $sql = 'CREATE TABLE ';
+        $sql .= $this->quoteTableName($table->getName()) . ' (';
+
+        $this->columnsWithComments = array();
+        foreach ($columns as $column) {
+            $sql .= $this->quoteColumnName($column->getName()) . ' ' . $this->getColumnSqlDefinition($column) . ', ';
+
+            // set column comments, if needed
+            if ($column->getComment()) {
+                $this->columnsWithComments[] = $column;
+            }
+        }
+
+         // set the primary key(s)
+        if (isset($options['primary_key'])) {
+            $sql = rtrim($sql);
+            $sql .= sprintf(' CONSTRAINT %s_pkey PRIMARY KEY (', $table->getName());
+            if (is_string($options['primary_key'])) {       // handle primary_key => 'id'
+                $sql .= $this->quoteColumnName($options['primary_key']);
+            } elseif (is_array($options['primary_key'])) { // handle primary_key => array('tag_id', 'resource_id')
+                // PHP 5.4 will allow access of $this, so we can call quoteColumnName() directly in the anonymous function,
+                // but for now just hard-code the adapter quotes
+                $sql .= implode(
+                    ',',
+                    array_map(
+                        function ($v) {
+                            return '"' . $v . '"';
+                        },
+                        $options['primary_key']
+                    )
+                );
+            }
+            $sql .= ')';
+        } else {
+            $sql = substr(rtrim($sql), 0, -1);              // no primary keys
+        }
+
+        // set the foreign keys
+        $foreignKeys = $table->getForeignKeys();
+        if (!empty($foreignKeys)) {
+            foreach ($foreignKeys as $foreignKey) {
+                $sql .= ', ' . $this->getForeignKeySqlDefinition($foreignKey, $table->getName());
+            }
+        }
+
+        $sql .= ');';
+
+        // process column comments
+        if (!empty($this->columnsWithComments)) {
+            foreach ($this->columnsWithComments as $column) {
+                $sql .= $this->getColumnCommentSqlDefinition($column, $table->getName());
+            }
+        }
+
+        // set the indexes
+        $indexes = $table->getIndexes();
+        if (!empty($indexes)) {
+            foreach ($indexes as $index) {
+                $sql .= $this->getIndexSqlDefinition($index, $table->getName());
+            }
+        }
+
+        // execute the sql
+        $this->writeCommand('createTable', array($table->getName()));
+        $this->execute($sql);
+
+        // process table comments
+        if (isset($options['comment'])) {
+            $sql = sprintf(
+                'COMMENT ON TABLE %s IS %s',
+                $this->quoteTableName($table->getName()),
+                $this->getConnection()->quote($options['comment'])
+            );
+            $this->execute($sql);
+        }
+
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function renameTable($tableName, $newTableName)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('renameTable', array($tableName, $newTableName));
+        $sql = sprintf(
+            'ALTER TABLE %s RENAME TO %s',
+            $this->quoteTableName($tableName),
+            $this->quoteColumnName($newTableName)
+        );
+        $this->execute($sql);
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropTable($tableName)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('dropTable', array($tableName));
+        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tableName)));
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getColumns($tableName)
+    {
+        $columns = array();
+        $sql = sprintf(
+            "SELECT column_name, data_type, is_identity, is_nullable,
+             column_default, character_maximum_length, numeric_precision, numeric_scale
+             FROM information_schema.columns
+             WHERE table_name ='%s'",
+            $tableName
+        );
+        $columnsInfo = $this->fetchAll($sql);
+
+        foreach ($columnsInfo as $columnInfo) {
+            $column = new Column();
+            $column->setName($columnInfo['column_name'])
+                   ->setType($this->getPhinxType($columnInfo['data_type']))
+                   ->setNull($columnInfo['is_nullable'] === 'YES')
+                   ->setDefault($columnInfo['column_default'])
+                   ->setIdentity($columnInfo['is_identity'] === 'YES')
+                   ->setPrecision($columnInfo['numeric_precision'])
+                   ->setScale($columnInfo['numeric_scale']);
+
+            if (preg_match('/\bwith time zone$/', $columnInfo['data_type'])) {
+                $column->setTimezone(true);
+            }
+
+            if (isset($columnInfo['character_maximum_length'])) {
+                $column->setLimit($columnInfo['character_maximum_length']);
+            }
+            $columns[] = $column;
+        }
+        return $columns;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasColumn($tableName, $columnName, $options = array())
+    {
+        $sql = sprintf("SELECT count(*)
+            FROM information_schema.columns
+            WHERE table_schema = '%s' AND table_name = '%s' AND column_name = '%s'",
+            $this->getSchemaName(),
+            $tableName,
+            $columnName
+        );
+
+        $result = $this->fetchRow($sql);
+        return  $result['count'] > 0;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addColumn(Table $table, Column $column)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('addColumn', array($table->getName(), $column->getName(), $column->getType()));
+        $sql = sprintf(
+            'ALTER TABLE %s ADD %s %s',
+            $this->quoteTableName($table->getName()),
+            $this->quoteColumnName($column->getName()),
+            $this->getColumnSqlDefinition($column)
+        );
+
+        $this->execute($sql);
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function renameColumn($tableName, $columnName, $newColumnName)
+    {
+        $this->startCommandTimer();
+        $sql = sprintf(
+            "SELECT CASE WHEN COUNT(*) > 0 THEN 1 ELSE 0 END AS column_exists
+             FROM information_schema.columns
+             WHERE table_name ='%s' AND column_name = '%s'",
+            $tableName,
+            $columnName
+        );
+        $result = $this->fetchRow($sql);
+        if (!(bool) $result['column_exists']) {
+            throw new \InvalidArgumentException("The specified column does not exist: $columnName");
+        }
+        $this->writeCommand('renameColumn', array($tableName, $columnName, $newColumnName));
+        $this->execute(
+            sprintf(
+                'ALTER TABLE %s RENAME COLUMN %s TO %s',
+                $this->quoteTableName($tableName),
+                $this->quoteColumnName($columnName),
+                $newColumnName
+            )
+        );
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function changeColumn($tableName, $columnName, Column $newColumn)
+    {
+        // TODO - is it possible to merge these 3 queries into less?
+        $this->startCommandTimer();
+        $this->writeCommand('changeColumn', array($tableName, $columnName, $newColumn->getType()));
+        // change data type
+        $sql = sprintf(
+            'ALTER TABLE %s ALTER COLUMN %s TYPE %s',
+            $this->quoteTableName($tableName),
+            $this->quoteColumnName($columnName),
+            $this->getColumnSqlDefinition($newColumn)
+        );
+        //NULL and DEFAULT cannot be set while changing column type
+        $sql = preg_replace('/ NOT NULL/', '', $sql);
+        $sql = preg_replace('/ NULL/', '', $sql);
+        //If it is set, DEFAULT is the last definition
+        $sql = preg_replace('/DEFAULT .*/', '', $sql);
+        $this->execute($sql);
+        // process null
+        $sql = sprintf(
+            'ALTER TABLE %s ALTER COLUMN %s',
+            $this->quoteTableName($tableName),
+            $this->quoteColumnName($columnName)
+        );
+        if ($newColumn->isNull()) {
+            $sql .= ' DROP NOT NULL';
+        } else {
+            $sql .= ' SET NOT NULL';
+        }
+        $this->execute($sql);
+        if (!is_null($newColumn->getDefault())) {
+            //change default
+            $this->execute(
+                sprintf(
+                    'ALTER TABLE %s ALTER COLUMN %s SET %s',
+                    $this->quoteTableName($tableName),
+                    $this->quoteColumnName($columnName),
+                    $this->getDefaultValueDefinition($newColumn->getDefault())
+                )
+            );
+        }
+        else {
+            //drop default
+            $this->execute(
+                sprintf(
+                    'ALTER TABLE %s ALTER COLUMN %s DROP DEFAULT',
+                    $this->quoteTableName($tableName),
+                    $this->quoteColumnName($columnName)
+                )
+            );
+        }
+        // rename column
+        if ($columnName !== $newColumn->getName()) {
+            $this->execute(
+                sprintf(
+                    'ALTER TABLE %s RENAME COLUMN %s TO %s',
+                    $this->quoteTableName($tableName),
+                    $this->quoteColumnName($columnName),
+                    $this->quoteColumnName($newColumn->getName())
+                )
+            );
+        }
+
+        // change column comment if needed
+        if ($newColumn->getComment()) {
+            $sql = $this->getColumnCommentSqlDefinition($newColumn, $tableName);
+            $this->execute($sql);
+        }
+
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropColumn($tableName, $columnName)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('dropColumn', array($tableName, $columnName));
+        $this->execute(
+            sprintf(
+                'ALTER TABLE %s DROP COLUMN %s',
+                $this->quoteTableName($tableName),
+                $this->quoteColumnName($columnName)
+            )
+        );
+        $this->endCommandTimer();
+    }
+
+    /**
+     * Get an array of indexes from a particular table.
+     *
+     * @param string $tableName Table Name
+     * @return array
+     */
+    protected function getIndexes($tableName)
+    {
+        $indexes = array();
+        $sql = "SELECT
+            i.relname AS index_name,
+            a.attname AS column_name
+        FROM
+            pg_class t,
+            pg_class i,
+            pg_index ix,
+            pg_attribute a
+        WHERE
+            t.oid = ix.indrelid
+            AND i.oid = ix.indexrelid
+            AND a.attrelid = t.oid
+            AND a.attnum = ANY(ix.indkey)
+            AND t.relkind = 'r'
+            AND t.relname = '$tableName'
+        ORDER BY
+            t.relname,
+            i.relname;";
+        $rows = $this->fetchAll($sql);
+        foreach ($rows as $row) {
+            if (!isset($indexes[$row['index_name']])) {
+                $indexes[$row['index_name']] = array('columns' => array());
+            }
+            $indexes[$row['index_name']]['columns'][] = strtolower($row['column_name']);
+        }
+        return $indexes;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasIndex($tableName, $columns)
+    {
+        if (is_string($columns)) {
+            $columns = array($columns);
+        }
+        $columns = array_map('strtolower', $columns);
+        $indexes = $this->getIndexes($tableName);
+        foreach ($indexes as $index) {
+            if (array_diff($index['columns'], $columns) === array_diff($columns, $index['columns'])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+     /**
+      * {@inheritdoc}
+      */
+     public function hasIndexByName($tableName, $indexName)
+     {
+         $indexes = $this->getIndexes($tableName);
+         foreach ($indexes as $name => $index) {
+             if ($name === $indexName) {
+                 return true;
+             }
+         }
+         return false;
+     }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addIndex(Table $table, Index $index)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('addIndex', array($table->getName(), $index->getColumns()));
+        $sql = $this->getIndexSqlDefinition($index, $table->getName());
+        $this->execute($sql);
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropIndex($tableName, $columns)
+    {
+        $this->startCommandTimer();
+        if (is_string($columns)) {
+            $columns = array($columns); // str to array
+        }
+
+        $this->writeCommand('dropIndex', array($tableName, $columns));
+        $indexes = $this->getIndexes($tableName);
+        $columns = array_map('strtolower', $columns);
+
+        foreach ($indexes as $indexName => $index) {
+            $a = array_diff($columns, $index['columns']);
+            if (empty($a)) {
+                $this->execute(
+                    sprintf(
+                        'DROP INDEX IF EXISTS %s',
+                        $this->quoteColumnName($indexName)
+                    )
+                );
+                $this->endCommandTimer();
+                return;
+            }
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropIndexByName($tableName, $indexName)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('dropIndexByName', array($tableName, $indexName));
+        $sql = sprintf(
+            'DROP INDEX IF EXISTS %s',
+            $indexName
+        );
+        $this->execute($sql);
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasForeignKey($tableName, $columns, $constraint = null)
+    {
+        if (is_string($columns)) {
+            $columns = array($columns); // str to array
+        }
+        $foreignKeys = $this->getForeignKeys($tableName);
+        if ($constraint) {
+            if (isset($foreignKeys[$constraint])) {
+                return !empty($foreignKeys[$constraint]);
+            }
+            return false;
+        } else {
+            foreach ($foreignKeys as $key) {
+                $a = array_diff($columns, $key['columns']);
+                if (empty($a)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Get an array of foreign keys from a particular table.
+     *
+     * @param string $tableName Table Name
+     * @return array
+     */
+    protected function getForeignKeys($tableName)
+    {
+        $foreignKeys = array();
+        $rows = $this->fetchAll(sprintf(
+            "SELECT
+                    tc.constraint_name,
+                    tc.table_name, kcu.column_name,
+                    ccu.table_name AS referenced_table_name,
+                    ccu.column_name AS referenced_column_name
+                FROM
+                    information_schema.table_constraints AS tc
+                    JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name
+                    JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
+                WHERE constraint_type = 'FOREIGN KEY' AND tc.table_name = '%s'
+                ORDER BY kcu.position_in_unique_constraint",
+            $tableName
+        ));
+        foreach ($rows as $row) {
+            $foreignKeys[$row['constraint_name']]['table'] = $row['table_name'];
+            $foreignKeys[$row['constraint_name']]['columns'][] = $row['column_name'];
+            $foreignKeys[$row['constraint_name']]['referenced_table'] = $row['referenced_table_name'];
+            $foreignKeys[$row['constraint_name']]['referenced_columns'][] = $row['referenced_column_name'];
+        }
+        return $foreignKeys;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addForeignKey(Table $table, ForeignKey $foreignKey)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('addForeignKey', array($table->getName(), $foreignKey->getColumns()));
+        $sql = sprintf(
+            'ALTER TABLE %s ADD %s',
+            $this->quoteTableName($table->getName()),
+            $this->getForeignKeySqlDefinition($foreignKey, $table->getName())
+        );
+        $this->execute($sql);
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropForeignKey($tableName, $columns, $constraint = null)
+    {
+        $this->startCommandTimer();
+        if (is_string($columns)) {
+            $columns = array($columns); // str to array
+        }
+        $this->writeCommand('dropForeignKey', array($tableName, $columns));
+
+        if ($constraint) {
+            $this->execute(
+                sprintf(
+                    'ALTER TABLE %s DROP CONSTRAINT %s',
+                    $this->quoteTableName($tableName),
+                    $constraint
+                )
+            );
+        } else {
+            foreach ($columns as $column) {
+                $rows = $this->fetchAll(sprintf(
+                    "SELECT CONSTRAINT_NAME
+                      FROM information_schema.KEY_COLUMN_USAGE
+                      WHERE TABLE_SCHEMA = CURRENT_SCHEMA()
+                        AND TABLE_NAME IS NOT NULL
+                        AND TABLE_NAME = '%s'
+                        AND COLUMN_NAME = '%s'
+                      ORDER BY POSITION_IN_UNIQUE_CONSTRAINT",
+                    $tableName,
+                    $column
+                ));
+
+                foreach ($rows as $row) {
+                    $this->dropForeignKey($tableName, $columns, $row['constraint_name']);
+                }
+            }
+        }
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSqlType($type, $limit = null)
+    {
+        switch ($type) {
+            case static::PHINX_TYPE_INTEGER:
+                if ($limit && $limit == static::INT_SMALL) {
+                    return array(
+                        'name' => 'smallint',
+                        'limit' => static::INT_SMALL
+                    );
+                }
+                return array('name' => $type);
+            case static::PHINX_TYPE_TEXT:
+            case static::PHINX_TYPE_TIME:
+            case static::PHINX_TYPE_DATE:
+            case static::PHINX_TYPE_BOOLEAN:
+            case static::PHINX_TYPE_JSON:
+            case static::PHINX_TYPE_JSONB:
+            case static::PHINX_TYPE_UUID:
+                return array('name' => $type);
+            case static::PHINX_TYPE_DECIMAL:
+                return array('name' => $type, 'precision' => 18, 'scale' => 0);
+            case static::PHINX_TYPE_STRING:
+                return array('name' => 'character varying', 'limit' => 255);
+            case static::PHINX_TYPE_CHAR:
+                return array('name' => 'character', 'limit' => 255);
+            case static::PHINX_TYPE_BIG_INTEGER:
+                return array('name' => 'bigint');
+            case static::PHINX_TYPE_FLOAT:
+                return array('name' => 'real');
+            case static::PHINX_TYPE_DATETIME:
+            case static::PHINX_TYPE_TIMESTAMP:
+                return array('name' => 'timestamp');
+            case static::PHINX_TYPE_BLOB:
+            case static::PHINX_TYPE_BINARY:
+                return array('name' => 'bytea');
+            // Geospatial database types
+            // Spatial storage in Postgres is done via the PostGIS extension,
+            // which enables the use of the "geography" type in combination
+            // with SRID 4326.
+            case static::PHINX_TYPE_GEOMETRY:
+                return array('name' => 'geography', 'geometry', 4326);
+                break;
+            case static::PHINX_TYPE_POINT:
+                return array('name' => 'geography', 'point', 4326);
+                break;
+            case static::PHINX_TYPE_LINESTRING:
+                return array('name' => 'geography', 'linestring', 4326);
+                break;
+            case static::PHINX_TYPE_POLYGON:
+                return array('name' => 'geography', 'polygon', 4326);
+                break;
+            default:
+                if ($this->isArrayType($type)) {
+                    return array('name' => $type);
+                }
+                // Return array type
+                throw new \RuntimeException('The type: "' . $type . '" is not supported');
+        }
+    }
+
+    /**
+     * Returns Phinx type by SQL type
+     *
+     * @param string $sqlType SQL type
+     * @returns string Phinx type
+     */
+    public function getPhinxType($sqlType)
+    {
+        switch ($sqlType) {
+            case 'character varying':
+            case 'varchar':
+                return static::PHINX_TYPE_STRING;
+            case 'character':
+            case 'char':
+                return static::PHINX_TYPE_CHAR;
+            case 'text':
+                return static::PHINX_TYPE_TEXT;
+            case 'json':
+                return static::PHINX_TYPE_JSON;
+            case 'jsonb':
+                return static::PHINX_TYPE_JSONB;
+            case 'smallint':
+                return array(
+                    'name' => 'smallint',
+                    'limit' => static::INT_SMALL
+                );
+            case 'int':
+            case 'int4':
+            case 'integer':
+                return static::PHINX_TYPE_INTEGER;
+            case 'decimal':
+            case 'numeric':
+                return static::PHINX_TYPE_DECIMAL;
+            case 'bigint':
+            case 'int8':
+                return static::PHINX_TYPE_BIG_INTEGER;
+            case 'real':
+            case 'float4':
+                return static::PHINX_TYPE_FLOAT;
+            case 'bytea':
+                return static::PHINX_TYPE_BINARY;
+                break;
+            case 'time':
+            case 'timetz':
+            case 'time with time zone':
+            case 'time without time zone':
+                return static::PHINX_TYPE_TIME;
+            case 'date':
+                return static::PHINX_TYPE_DATE;
+            case 'timestamp':
+            case 'timestamptz':
+            case 'timestamp with time zone':
+            case 'timestamp without time zone':
+                return static::PHINX_TYPE_DATETIME;
+            case 'bool':
+            case 'boolean':
+                return static::PHINX_TYPE_BOOLEAN;
+            case 'uuid':
+                return static::PHINX_TYPE_UUID;
+            default:
+                throw new \RuntimeException('The PostgreSQL type: "' . $sqlType . '" is not supported');
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createDatabase($name, $options = array())
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('createDatabase', array($name));
+        $charset = isset($options['charset']) ? $options['charset'] : 'utf8';
+        $this->execute(sprintf("CREATE DATABASE %s WITH ENCODING = '%s'", $name, $charset));
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasDatabase($databaseName)
+    {
+        $sql = sprintf("SELECT count(*) FROM pg_database WHERE datname = '%s'", $databaseName);
+        $result = $this->fetchRow($sql);
+        return  $result['count'] > 0;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropDatabase($name)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('dropDatabase', array($name));
+        $this->disconnect();
+        $this->execute(sprintf('DROP DATABASE IF EXISTS %s', $name));
+        $this->connect();
+        $this->endCommandTimer();
+    }
+
+    /**
+     * Get the defintion for a `DEFAULT` statement.
+     *
+     * @param  mixed $default
+     * @return string
+     */
+    protected function getDefaultValueDefinition($default)
+    {
+        if (is_string($default) && 'CURRENT_TIMESTAMP' !== $default) {
+            $default = $this->getConnection()->quote($default);
+        } elseif (is_bool($default)) {
+            $default = $this->castToBool($default);
+        }
+        return isset($default) ? 'DEFAULT ' . $default : '';
+    }
+
+    /**
+     * Gets the PostgreSQL Column Definition for a Column object.
+     *
+     * @param Column $column Column
+     * @return string
+     */
+    protected function getColumnSqlDefinition(Column $column)
+    {
+        $buffer = array();
+        if ($column->isIdentity()) {
+            $buffer[] = $column->getType() == 'biginteger' ? 'BIGSERIAL' : 'SERIAL';
+        } else {
+            $sqlType = $this->getSqlType($column->getType(), $column->getLimit());
+            $buffer[] = strtoupper($sqlType['name']);
+            // integers cant have limits in postgres
+            if (static::PHINX_TYPE_DECIMAL === $sqlType['name'] && ($column->getPrecision() || $column->getScale())) {
+                $buffer[] = sprintf(
+                    '(%s, %s)',
+                    $column->getPrecision() ? $column->getPrecision() : $sqlType['precision'],
+                    $column->getScale() ? $column->getScale() : $sqlType['scale']
+                );
+            } elseif (!in_array($sqlType['name'], array('integer', 'smallint'))) {
+                if ($column->getLimit() || isset($sqlType['limit'])) {
+                    $buffer[] = sprintf('(%s)', $column->getLimit() ? $column->getLimit() : $sqlType['limit']);
+                }
+            }
+
+            $timeTypes = array(
+                'time',
+                'timestamp',
+            );
+            if (in_array($sqlType['name'], $timeTypes) && $column->isTimezone()) {
+                $buffer[] = strtoupper('with time zone');
+            }
+        }
+
+        $buffer[] = $column->isNull() ? 'NULL' : 'NOT NULL';
+
+        if (!is_null($column->getDefault())) {
+            $buffer[] = $this->getDefaultValueDefinition($column->getDefault());
+        }
+
+        return implode(' ', $buffer);
+    }
+
+    /**
+     * Gets the PostgreSQL Column Comment Defininition for a column object.
+     *
+     * @param Column $column Column
+     * @param string $tableName Table name
+     * @return string
+     */
+    protected function getColumnCommentSqlDefinition(Column $column, $tableName)
+    {
+        // passing 'null' is to remove column comment
+        $comment = (strcasecmp($column->getComment(), 'NULL') !== 0)
+                 ? $this->getConnection()->quote($column->getComment())
+                 : 'NULL';
+
+        return sprintf(
+            'COMMENT ON COLUMN %s.%s IS %s;',
+            $tableName,
+            $column->getName(),
+            $comment
+        );
+    }
+
+    /**
+     * Gets the PostgreSQL Index Definition for an Index object.
+     *
+     * @param Index  $index Index
+     * @param string $tableName Table name
+     * @return string
+     */
+    protected function getIndexSqlDefinition(Index $index, $tableName)
+    {
+        if (is_string($index->getName())) {
+            $indexName = $index->getName();
+        } else {
+            $columnNames = $index->getColumns();
+            if (is_string($columnNames)) {
+                $columnNames = array($columnNames);
+            }
+            $indexName = sprintf('%s_%s', $tableName, implode('_', $columnNames));
+        }
+        $def = sprintf(
+            "CREATE %s INDEX %s ON %s (%s);",
+            ($index->getType() === Index::UNIQUE ? 'UNIQUE' : ''),
+            $indexName,
+            $this->quoteTableName($tableName),
+            implode(',', $index->getColumns())
+        );
+        return $def;
+    }
+
+    /**
+     * Gets the MySQL Foreign Key Definition for an ForeignKey object.
+     *
+     * @param ForeignKey $foreignKey
+     * @param string     $tableName  Table name
+     * @return string
+     */
+    protected function getForeignKeySqlDefinition(ForeignKey $foreignKey, $tableName)
+    {
+        $constraintName = $foreignKey->getConstraint() ?: $tableName . '_' . implode('_', $foreignKey->getColumns());
+        $def = ' CONSTRAINT "' . $constraintName . '" FOREIGN KEY ("' . implode('", "', $foreignKey->getColumns()) . '")';
+        $def .= " REFERENCES {$this->quoteTableName($foreignKey->getReferencedTable()->getName())} (\"" . implode('", "', $foreignKey->getReferencedColumns()) . '")';
+        if ($foreignKey->getOnDelete()) {
+            $def .= " ON DELETE {$foreignKey->getOnDelete()}";
+        }
+        if ($foreignKey->getOnUpdate()) {
+            $def .= " ON UPDATE {$foreignKey->getOnUpdate()}";
+        }
+        return $def;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createSchemaTable()
+    {
+        // Create the public/custom schema if it doesn't already exist
+        if (false === $this->hasSchema($this->getSchemaName())) {
+            $this->createSchema($this->getSchemaName());
+        }
+
+        $this->fetchAll(sprintf('SET search_path TO %s', $this->getSchemaName()));
+
+        return parent::createSchemaTable();
+    }
+
+    /**
+     * Creates the specified schema.
+     *
+     * @param  string $schemaName Schema Name
+     * @return void
+     */
+    public function createSchema($schemaName = 'public')
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('addSchema', array($schemaName));
+        $sql = sprintf('CREATE SCHEMA %s;', $this->quoteSchemaName($schemaName)); // from postgres 9.3 we can use "CREATE SCHEMA IF NOT EXISTS schema_name"
+        $this->execute($sql);
+        $this->endCommandTimer();
+    }
+
+    /**
+     * Checks to see if a schema exists.
+     *
+     * @param string $schemaName Schema Name
+     * @return boolean
+     */
+    public function hasSchema($schemaName)
+    {
+        $sql = sprintf(
+            "SELECT count(*)
+             FROM pg_namespace
+             WHERE nspname = '%s'",
+            $schemaName
+        );
+        $result = $this->fetchRow($sql);
+        return $result['count'] > 0;
+    }
+
+    /**
+     * Drops the specified schema table.
+     *
+     * @param string $schemaName Schema name
+     * @return void
+     */
+    public function dropSchema($schemaName)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('dropSchema', array($schemaName));
+        $sql = sprintf("DROP SCHEMA IF EXISTS %s CASCADE;", $this->quoteSchemaName($schemaName));
+        $this->execute($sql);
+        $this->endCommandTimer();
+    }
+
+    /**
+     * Drops all schemas.
+     *
+     * @return void
+     */
+    public function dropAllSchemas()
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('dropAllSchemas');
+        foreach ($this->getAllSchemas() as $schema) {
+            $this->dropSchema($schema);
+        }
+        $this->endCommandTimer();
+    }
+
+    /**
+     * Returns schemas.
+     *
+     * @return array
+     */
+    public function getAllSchemas()
+    {
+        $sql = "SELECT schema_name
+                FROM information_schema.schemata
+                WHERE schema_name <> 'information_schema' AND schema_name !~ '^pg_'";
+        $items = $this->fetchAll($sql);
+        $schemaNames = array();
+        foreach ($items as $item) {
+            $schemaNames[] = $item['schema_name'];
+        }
+        return $schemaNames;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getColumnTypes()
+    {
+        return array_merge(parent::getColumnTypes(), array('json', 'jsonb'));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isValidColumnType(Column $column)
+    {
+        // If not a standard column type, maybe it is array type?
+        return (parent::isValidColumnType($column) || $this->isArrayType($column->getType()));
+    }
+
+    /**
+     * Check if the given column is an array of a valid type.
+     *
+     * @param  string $columnType
+     * @return bool
+     */
+    protected function isArrayType($columnType)
+    {
+        if (!preg_match('/^([a-z]+)(?:\[\]){1,}$/', $columnType, $matches)) {
+            return false;
+        }
+
+        $baseType = $matches[1];
+        return in_array($baseType, $this->getColumnTypes());
+    }
+
+    /**
+     * Gets the schema name.
+     *
+     * @return string
+     */
+    private function getSchemaName()
+    {
+        $options = $this->getOptions();
+        return empty($options['schema']) ? 'public' : $options['schema'];
+    }
+
+    /**
+     * Cast a value to a boolean appropriate for the adapter.
+     *
+     * @param mixed $value The value to be cast
+     *
+     * @return mixed
+     */
+    public function castToBool($value)
+    {
+        return (bool) $value ? 'TRUE' : 'FALSE';
+    }
+}
diff --git a/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/ProxyAdapter.php b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/ProxyAdapter.php
new file mode 100755
index 0000000..1236031
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/ProxyAdapter.php
@@ -0,0 +1,325 @@
+<?php
+/**
+ * Phinx
+ *
+ * (The MIT license)
+ * Copyright (c) 2015 Rob Morgan
+ *
+ * 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.
+ *
+ * @package    Phinx
+ * @subpackage Phinx\Db\Adapter
+ */
+namespace Phinx\Db\Adapter;
+
+use Phinx\Db\Table;
+use Phinx\Db\Table\Column;
+use Phinx\Db\Table\Index;
+use Phinx\Db\Table\ForeignKey;
+use Phinx\Migration\IrreversibleMigrationException;
+
+/**
+ * Phinx Proxy Adapter.
+ *
+ * Used for recording migration commands to automatically reverse them.
+ *
+ * @author Rob Morgan <robbym@gmail.com>
+ */
+class ProxyAdapter extends AdapterWrapper
+{
+    /**
+     * @var array
+     */
+    protected $commands;
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getAdapterType()
+    {
+        return 'ProxyAdapter';
+    }
+    /**
+     * {@inheritdoc}
+     */
+    public function createTable(Table $table)
+    {
+        $this->recordCommand('createTable', array($table->getName()));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function renameTable($tableName, $newTableName)
+    {
+        $this->recordCommand('renameTable', array($tableName, $newTableName));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropTable($tableName)
+    {
+        $this->recordCommand('dropTable', array($tableName));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addColumn(Table $table, Column $column)
+    {
+        $this->recordCommand('addColumn', array($table, $column));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function renameColumn($tableName, $columnName, $newColumnName)
+    {
+        $this->recordCommand('renameColumn', array($tableName, $columnName, $newColumnName));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function changeColumn($tableName, $columnName, Column $newColumn)
+    {
+        $this->recordCommand('changeColumn', array($tableName, $columnName, $newColumn));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropColumn($tableName, $columnName)
+    {
+        $this->recordCommand('dropColumn', array($tableName, $columnName));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addIndex(Table $table, Index $index)
+    {
+        $this->recordCommand('addIndex', array($table, $index));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropIndex($tableName, $columns, $options = array())
+    {
+        $this->recordCommand('dropIndex', array($tableName, $columns, $options));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropIndexByName($tableName, $indexName)
+    {
+        $this->recordCommand('dropIndexByName', array($tableName, $indexName));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addForeignKey(Table $table, ForeignKey $foreignKey)
+    {
+        $this->recordCommand('addForeignKey', array($table, $foreignKey));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropForeignKey($tableName, $columns, $constraint = null)
+    {
+        $this->recordCommand('dropForeignKey', array($columns, $constraint));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createDatabase($name, $options = array())
+    {
+        $this->recordCommand('createDatabase', array($name, $options));
+    }
+
+    /**
+     * Record a command for execution later.
+     *
+     * @param string $name Command Name
+     * @param array $arguments Command Arguments
+     * @return void
+     */
+    public function recordCommand($name, $arguments)
+    {
+        $this->commands[] = array(
+            'name'      => $name,
+            'arguments' => $arguments
+        );
+    }
+
+    /**
+     * Sets an array of recorded commands.
+     *
+     * @param array $commands Commands
+     * @return ProxyAdapter
+     */
+    public function setCommands($commands)
+    {
+        $this->commands = $commands;
+        return $this;
+    }
+
+    /**
+     * Gets an array of the recorded commands.
+     *
+     * @return array
+     */
+    public function getCommands()
+    {
+        return $this->commands;
+    }
+
+    /**
+     * Gets an array of the recorded commands in reverse.
+     *
+     * @throws IrreversibleMigrationException if a command cannot be reversed.
+     * @return array
+     */
+    public function getInvertedCommands()
+    {
+        if (null === $this->getCommands()) {
+            return array();
+        }
+
+        $invCommands = array();
+        $supportedCommands = array(
+            'createTable', 'renameTable', 'addColumn',
+            'renameColumn', 'addIndex', 'addForeignKey'
+        );
+        foreach (array_reverse($this->getCommands()) as $command) {
+            if (!in_array($command['name'], $supportedCommands)) {
+                throw new IrreversibleMigrationException(sprintf(
+                    'Cannot reverse a "%s" command',
+                    $command['name']
+                ));
+            }
+            $invertMethod = 'invert' . ucfirst($command['name']);
+            $invertedCommand = $this->$invertMethod($command['arguments']);
+            $invCommands[] = array(
+                'name'      => $invertedCommand['name'],
+                'arguments' => $invertedCommand['arguments']
+            );
+        }
+
+        return $invCommands;
+    }
+
+    /**
+     * Execute the recorded commands.
+     *
+     * @return void
+     */
+    public function executeCommands()
+    {
+        $commands = $this->getCommands();
+        foreach ($commands as $command) {
+            call_user_func_array(array($this->getAdapter(), $command['name']), $command['arguments']);
+        }
+    }
+
+    /**
+     * Execute the recorded commands in reverse.
+     *
+     * @return void
+     */
+    public function executeInvertedCommands()
+    {
+        $commands = $this->getInvertedCommands();
+        foreach ($commands as $command) {
+            call_user_func_array(array($this->getAdapter(), $command['name']), $command['arguments']);
+        }
+    }
+
+    /**
+     * Returns the reverse of a createTable command.
+     *
+     * @param array $args Method Arguments
+     * @return array
+     */
+    public function invertCreateTable($args)
+    {
+        return array('name' => 'dropTable', 'arguments' => array($args[0]));
+    }
+
+    /**
+     * Returns the reverse of a renameTable command.
+     *
+     * @param array $args Method Arguments
+     * @return array
+     */
+    public function invertRenameTable($args)
+    {
+        return array('name' => 'renameTable', 'arguments' => array($args[1], $args[0]));
+    }
+
+    /**
+     * Returns the reverse of a addColumn command.
+     *
+     * @param array $args Method Arguments
+     * @return array
+     */
+    public function invertAddColumn($args)
+    {
+        return array('name' => 'dropColumn', 'arguments' => array($args[0]->getName(), $args[1]->getName()));
+    }
+
+    /**
+     * Returns the reverse of a renameColumn command.
+     *
+     * @param array $args Method Arguments
+     * @return array
+     */
+    public function invertRenameColumn($args)
+    {
+        return array('name' => 'renameColumn', 'arguments' => array($args[0], $args[2], $args[1]));
+    }
+
+    /**
+     * Returns the reverse of a addIndex command.
+     *
+     * @param array $args Method Arguments
+     * @return array
+     */
+    public function invertAddIndex($args)
+    {
+        return array('name' => 'dropIndex', 'arguments' => array($args[0]->getName(), $args[1]->getColumns()));
+    }
+
+    /**
+     * Returns the reverse of a addForeignKey command.
+     *
+     * @param array $args Method Arguments
+     * @return array
+     */
+    public function invertAddForeignKey($args)
+    {
+        return array('name' => 'dropForeignKey', 'arguments' => array($args[0]->getName(), $args[1]->getColumns()));
+    }
+}
diff --git a/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/SQLiteAdapter.php b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/SQLiteAdapter.php
new file mode 100755
index 0000000..4ff7b74
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/SQLiteAdapter.php
@@ -0,0 +1,1136 @@
+<?php
+/**
+ * Phinx
+ *
+ * (The MIT license)
+ * Copyright (c) 2015 Rob Morgan
+ *
+ * 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.
+ *
+ * @package    Phinx
+ * @subpackage Phinx\Db\Adapter
+ */
+namespace Phinx\Db\Adapter;
+
+use Phinx\Db\Table;
+use Phinx\Db\Table\Column;
+use Phinx\Db\Table\Index;
+use Phinx\Db\Table\ForeignKey;
+
+/**
+ * Phinx SQLite Adapter.
+ *
+ * @author Rob Morgan <robbym@gmail.com>
+ * @author Richard McIntyre <richard.mackstars@gmail.com>
+ */
+class SQLiteAdapter extends PdoAdapter implements AdapterInterface
+{
+    protected $definitionsWithLimits = array(
+        'CHARACTER',
+        'VARCHAR',
+        'VARYING CHARACTER',
+        'NCHAR',
+        'NATIVE CHARACTER',
+        'NVARCHAR'
+    );
+
+    /**
+     * {@inheritdoc}
+     */
+    public function connect()
+    {
+        if (null === $this->connection) {
+            if (!class_exists('PDO') || !in_array('sqlite', \PDO::getAvailableDrivers(), true)) {
+                // @codeCoverageIgnoreStart
+                throw new \RuntimeException('You need to enable the PDO_SQLITE extension for Phinx to run properly.');
+                // @codeCoverageIgnoreEnd
+            }
+
+            $db = null;
+            $options = $this->getOptions();
+
+            // if port is specified use it, otherwise use the MySQL default
+            if (isset($options['memory'])) {
+                $dsn = 'sqlite::memory:';
+            } else {
+                $dsn = 'sqlite:' . $options['name'];
+                if (file_exists($options['name'] . '.sqlite3')) {
+                    $dsn = 'sqlite:' . $options['name'] . '.sqlite3';
+                }
+            }
+
+            try {
+                $db = new \PDO($dsn);
+            } catch (\PDOException $exception) {
+                throw new \InvalidArgumentException(sprintf(
+                    'There was a problem connecting to the database: %s',
+                    $exception->getMessage()
+                ));
+            }
+
+            $this->setConnection($db);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function disconnect()
+    {
+        $this->connection = null;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasTransactions()
+    {
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function beginTransaction()
+    {
+        $this->execute('BEGIN TRANSACTION');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function commitTransaction()
+    {
+        $this->execute('COMMIT');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function rollbackTransaction()
+    {
+        $this->execute('ROLLBACK');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function quoteTableName($tableName)
+    {
+        return str_replace('.', '`.`', $this->quoteColumnName($tableName));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function quoteColumnName($columnName)
+    {
+        return '`' . str_replace('`', '``', $columnName) . '`';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasTable($tableName)
+    {
+        $tables = array();
+        $rows = $this->fetchAll(sprintf('SELECT name FROM sqlite_master WHERE type=\'table\' AND name=\'%s\'', $tableName));
+        foreach ($rows as $row) {
+            $tables[] = strtolower($row[0]);
+        }
+
+        return in_array(strtolower($tableName), $tables);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createTable(Table $table)
+    {
+        $this->startCommandTimer();
+
+        // Add the default primary key
+        $columns = $table->getPendingColumns();
+        $options = $table->getOptions();
+        if (!isset($options['id']) || (isset($options['id']) && $options['id'] === true)) {
+            $column = new Column();
+            $column->setName('id')
+                   ->setType('integer')
+                   ->setIdentity(true);
+
+            array_unshift($columns, $column);
+
+        } elseif (isset($options['id']) && is_string($options['id'])) {
+            // Handle id => "field_name" to support AUTO_INCREMENT
+            $column = new Column();
+            $column->setName($options['id'])
+                   ->setType('integer')
+                   ->setIdentity(true);
+
+            array_unshift($columns, $column);
+        }
+
+        $sql = 'CREATE TABLE ';
+        $sql .= $this->quoteTableName($table->getName()) . ' (';
+        foreach ($columns as $column) {
+            $sql .= $this->quoteColumnName($column->getName()) . ' ' . $this->getColumnSqlDefinition($column) . ', ';
+        }
+
+        // set the primary key(s)
+        if (isset($options['primary_key'])) {
+            $sql = rtrim($sql);
+            $sql .= ' PRIMARY KEY (';
+            if (is_string($options['primary_key'])) {       // handle primary_key => 'id'
+                $sql .= $this->quoteColumnName($options['primary_key']);
+            } elseif (is_array($options['primary_key'])) { // handle primary_key => array('tag_id', 'resource_id')
+                // PHP 5.4 will allow access of $this, so we can call quoteColumnName() directly in the anonymous function,
+                // but for now just hard-code the adapter quotes
+                $sql .= implode(
+                    ',',
+                    array_map(
+                        function ($v) {
+                            return '`' . $v . '`';
+                        },
+                        $options['primary_key']
+                    )
+                );
+            }
+            $sql .= ')';
+        } else {
+            $sql = substr(rtrim($sql), 0, -1);              // no primary keys
+        }
+
+        // set the foreign keys
+        $foreignKeys = $table->getForeignKeys();
+        if (!empty($foreignKeys)) {
+            foreach ($foreignKeys as $foreignKey) {
+                $sql .= ', ' . $this->getForeignKeySqlDefinition($foreignKey);
+            }
+        }
+
+        $sql = rtrim($sql) . ');';
+        // execute the sql
+        $this->writeCommand('createTable', array($table->getName()));
+        $this->execute($sql);
+        $this->endCommandTimer();
+
+        foreach ($table->getIndexes() as $index) {
+            $this->addIndex($table, $index);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function renameTable($tableName, $newTableName)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('renameTable', array($tableName, $newTableName));
+        $this->execute(sprintf('ALTER TABLE %s RENAME TO %s', $this->quoteTableName($tableName), $this->quoteTableName($newTableName)));
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropTable($tableName)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('dropTable', array($tableName));
+        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tableName)));
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getColumns($tableName)
+    {
+        $columns = array();
+        $rows = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($tableName)));
+
+        foreach ($rows as $columnInfo) {
+            $column = new Column();
+            $type = strtolower($columnInfo['type']);
+            $column->setName($columnInfo['name'])
+                   ->setNull($columnInfo['notnull'] !== '1')
+                   ->setDefault($columnInfo['dflt_value']);
+
+            $phinxType = $this->getPhinxType($type);
+            $column->setType($phinxType['name'])
+                   ->setLimit($phinxType['limit']);
+
+            if ($columnInfo['pk'] == 1) {
+                $column->setIdentity(true);
+            }
+
+            $columns[] = $column;
+        }
+
+        return $columns;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasColumn($tableName, $columnName)
+    {
+        $rows = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($tableName)));
+        foreach ($rows as $column) {
+            if (strcasecmp($column['name'], $columnName) === 0) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addColumn(Table $table, Column $column)
+    {
+        $this->startCommandTimer();
+
+        $sql = sprintf(
+            'ALTER TABLE %s ADD COLUMN %s %s',
+            $this->quoteTableName($table->getName()),
+            $this->quoteColumnName($column->getName()),
+            $this->getColumnSqlDefinition($column)
+        );
+
+        $this->writeCommand('addColumn', array($table->getName(), $column->getName(), $column->getType()));
+        $this->execute($sql);
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function renameColumn($tableName, $columnName, $newColumnName)
+    {
+        $tmpTableName = 'tmp_' . $tableName;
+
+        $rows = $this->fetchAll('select * from sqlite_master where `type` = \'table\'');
+
+        $sql = '';
+        foreach ($rows as $table) {
+            if ($table['tbl_name'] === $tableName) {
+                $sql = $table['sql'];
+            }
+        }
+
+        $columns = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($tableName)));
+        $selectColumns = array();
+        $writeColumns = array();
+        foreach ($columns as $column) {
+            $selectName = $column['name'];
+            $writeName = ($selectName == $columnName) ? $newColumnName : $selectName;
+            $selectColumns[] = $this->quoteColumnName($selectName);
+            $writeColumns[] = $this->quoteColumnName($writeName);
+        }
+
+        if (!in_array($this->quoteColumnName($columnName), $selectColumns)) {
+            throw new \InvalidArgumentException(sprintf(
+                'The specified column doesn\'t exist: ' . $columnName
+            ));
+        }
+
+        $this->execute(sprintf('ALTER TABLE %s RENAME TO %s', $tableName, $tmpTableName));
+
+        $sql = str_replace(
+            $this->quoteColumnName($columnName),
+            $this->quoteColumnName($newColumnName),
+            $sql
+        );
+        $this->execute($sql);
+
+        $sql = sprintf(
+            'INSERT INTO %s(%s) SELECT %s FROM %s',
+            $tableName,
+            implode(', ', $writeColumns),
+            implode(', ', $selectColumns),
+            $tmpTableName
+        );
+
+        $this->execute($sql);
+
+        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tmpTableName)));
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function changeColumn($tableName, $columnName, Column $newColumn)
+    {
+
+        // TODO: DRY this up....
+
+        $this->startCommandTimer();
+        $this->writeCommand('changeColumn', array($tableName, $columnName, $newColumn->getType()));
+
+        $tmpTableName = 'tmp_' . $tableName;
+
+        $rows = $this->fetchAll('select * from sqlite_master where `type` = \'table\'');
+
+        $sql = '';
+        foreach ($rows as $table) {
+            if ($table['tbl_name'] === $tableName) {
+                $sql = $table['sql'];
+            }
+        }
+
+        $columns = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($tableName)));
+        $selectColumns = array();
+        $writeColumns = array();
+        foreach ($columns as $column) {
+            $selectName = $column['name'];
+            $writeName = ($selectName === $columnName) ? $newColumn->getName() : $selectName;
+            $selectColumns[] = $this->quoteColumnName($selectName);
+            $writeColumns[] = $this->quoteColumnName($writeName);
+        }
+
+        if (!in_array($this->quoteColumnName($columnName), $selectColumns)) {
+            throw new \InvalidArgumentException(sprintf(
+                'The specified column doesn\'t exist: ' . $columnName
+            ));
+        }
+
+        $this->execute(sprintf('ALTER TABLE %s RENAME TO %s', $tableName, $tmpTableName));
+
+        $sql = preg_replace(
+            sprintf("/%s[^,]+([,)])/", $this->quoteColumnName($columnName)),
+            sprintf('%s %s$1', $this->quoteColumnName($newColumn->getName()), $this->getColumnSqlDefinition($newColumn)),
+            $sql,
+            1
+        );
+
+        $this->execute($sql);
+
+        $sql = sprintf(
+            'INSERT INTO %s(%s) SELECT %s FROM %s',
+            $tableName,
+            implode(', ', $writeColumns),
+            implode(', ', $selectColumns),
+            $tmpTableName
+        );
+
+        $this->execute($sql);
+        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tmpTableName)));
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropColumn($tableName, $columnName)
+    {
+        // TODO: DRY this up....
+
+        $this->startCommandTimer();
+        $this->writeCommand('dropColumn', array($tableName, $columnName));
+
+        $tmpTableName = 'tmp_' . $tableName;
+
+        $rows = $this->fetchAll('select * from sqlite_master where `type` = \'table\'');
+
+        $sql = '';
+        foreach ($rows as $table) {
+            if ($table['tbl_name'] === $tableName) {
+                $sql = $table['sql'];
+            }
+        }
+
+        $rows = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($tableName)));
+        $columns = array();
+        $columnType = null;
+        foreach ($rows as $row) {
+            if ($row['name'] !== $columnName) {
+                $columns[] = $row['name'];
+            } else {
+                $found = true;
+                $columnType = $row['type'];
+            }
+        }
+
+        if (!isset($found)) {
+            throw new \InvalidArgumentException(sprintf(
+                'The specified column doesn\'t exist: ' . $columnName
+            ));
+        }
+
+        $this->execute(sprintf('ALTER TABLE %s RENAME TO %s', $tableName, $tmpTableName));
+
+        $sql = preg_replace(
+            sprintf("/%s\s%s[^,)]*(,\s|\))/", preg_quote($this->quoteColumnName($columnName)), preg_quote($columnType)),
+            "",
+            $sql
+        );
+
+        if (substr($sql, -2) === ', ') {
+            $sql = substr($sql, 0, -2) . ')';
+        }
+
+        $this->execute($sql);
+
+        $sql = sprintf(
+            'INSERT INTO %s(%s) SELECT %s FROM %s',
+            $tableName,
+            implode(', ', $columns),
+            implode(', ', $columns),
+            $tmpTableName
+        );
+
+        $this->execute($sql);
+        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tmpTableName)));
+        $this->endCommandTimer();
+    }
+
+    /**
+     * Get an array of indexes from a particular table.
+     *
+     * @param string $tableName Table Name
+     * @return array
+     */
+    protected function getIndexes($tableName)
+    {
+        $indexes = array();
+        $rows = $this->fetchAll(sprintf('pragma index_list(%s)', $tableName));
+
+        foreach ($rows as $row) {
+            $indexData = $this->fetchAll(sprintf('pragma index_info(%s)', $row['name']));
+            if (!isset($indexes[$tableName])) {
+                $indexes[$tableName] = array('index' => $row['name'], 'columns' => array());
+            }
+            foreach ($indexData as $indexItem) {
+                $indexes[$tableName]['columns'][] = strtolower($indexItem['name']);
+            }
+        }
+        return $indexes;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasIndex($tableName, $columns)
+    {
+        if (is_string($columns)) {
+            $columns = array($columns); // str to array
+        }
+
+        $columns = array_map('strtolower', $columns);
+        $indexes = $this->getIndexes($tableName);
+
+        foreach ($indexes as $index) {
+            $a = array_diff($columns, $index['columns']);
+            if (empty($a)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasIndexByName($tableName, $indexName)
+    {
+        $indexes = $this->getIndexes($tableName);
+
+        foreach ($indexes as $index) {
+            if ($indexName === $index['index']) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addIndex(Table $table, Index $index)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('addIndex', array($table->getName(), $index->getColumns()));
+        $indexColumnArray = array();
+        foreach ($index->getColumns() as $column) {
+            $indexColumnArray []= sprintf('`%s` ASC', $column);
+        }
+        $indexColumns = implode(',', $indexColumnArray);
+        $this->execute(
+            sprintf(
+                'CREATE %s ON %s (%s)',
+                $this->getIndexSqlDefinition($table, $index),
+                $this->quoteTableName($table->getName()),
+                $indexColumns
+            )
+        );
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropIndex($tableName, $columns)
+    {
+        $this->startCommandTimer();
+        if (is_string($columns)) {
+            $columns = array($columns); // str to array
+        }
+
+        $this->writeCommand('dropIndex', array($tableName, $columns));
+        $indexes = $this->getIndexes($tableName);
+        $columns = array_map('strtolower', $columns);
+
+        foreach ($indexes as $index) {
+            $a = array_diff($columns, $index['columns']);
+            if (empty($a)) {
+                $this->execute(
+                    sprintf(
+                        'DROP INDEX %s',
+                        $this->quoteColumnName($index['index'])
+                    )
+                );
+                $this->endCommandTimer();
+                return;
+            }
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropIndexByName($tableName, $indexName)
+    {
+        $this->startCommandTimer();
+
+        $this->writeCommand('dropIndexByName', array($tableName, $indexName));
+        $indexes = $this->getIndexes($tableName);
+
+        foreach ($indexes as $index) {
+            if ($indexName === $index['index']) {
+                $this->execute(
+                    sprintf(
+                        'DROP INDEX %s',
+                        $this->quoteColumnName($indexName)
+                    )
+                );
+                $this->endCommandTimer();
+                return;
+            }
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasForeignKey($tableName, $columns, $constraint = null)
+    {
+        if (is_string($columns)) {
+            $columns = array($columns); // str to array
+        }
+        $foreignKeys = $this->getForeignKeys($tableName);
+
+        $a = array_diff($columns, $foreignKeys);
+        if (empty($a)) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Get an array of foreign keys from a particular table.
+     *
+     * @param string $tableName Table Name
+     * @return array
+     */
+    protected function getForeignKeys($tableName)
+    {
+        $foreignKeys = array();
+        $rows = $this->fetchAll(
+            "SELECT sql, tbl_name
+              FROM (
+                    SELECT sql sql, type type, tbl_name tbl_name, name name
+                      FROM sqlite_master
+                     UNION ALL
+                    SELECT sql, type, tbl_name, name
+                      FROM sqlite_temp_master
+                   )
+             WHERE type != 'meta'
+               AND sql NOTNULL
+               AND name NOT LIKE 'sqlite_%'
+             ORDER BY substr(type, 2, 1), name"
+        );
+
+        foreach ($rows as $row) {
+            if ($row['tbl_name'] === $tableName) {
+
+                if (strpos($row['sql'], 'REFERENCES') !== false) {
+                    preg_match_all("/\(`([^`]*)`\) REFERENCES/", $row['sql'], $matches);
+                    foreach ($matches[1] as $match) {
+                        $foreignKeys[] = $match;
+                    }
+                }
+            }
+        }
+        return $foreignKeys;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addForeignKey(Table $table, ForeignKey $foreignKey)
+    {
+        // TODO: DRY this up....
+        $this->startCommandTimer();
+        $this->writeCommand('addForeignKey', array($table->getName(), $foreignKey->getColumns()));
+        $this->execute('pragma foreign_keys = ON');
+
+        $tmpTableName = 'tmp_' . $table->getName();
+        $rows = $this->fetchAll('select * from sqlite_master where `type` = \'table\'');
+
+        $sql = '';
+        foreach ($rows as $row) {
+            if ($row['tbl_name'] === $table->getName()) {
+                $sql = $row['sql'];
+            }
+        }
+
+        $rows = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($table->getName())));
+        $columns = array();
+        foreach ($rows as $column) {
+            $columns[] = $this->quoteColumnName($column['name']);
+        }
+
+        $this->execute(sprintf('ALTER TABLE %s RENAME TO %s', $this->quoteTableName($table->getName()), $tmpTableName));
+
+        $sql = substr($sql, 0, -1) . ',' . $this->getForeignKeySqlDefinition($foreignKey) . ')';
+        $this->execute($sql);
+
+        $sql = sprintf(
+            'INSERT INTO %s(%s) SELECT %s FROM %s',
+            $this->quoteTableName($table->getName()),
+            implode(', ', $columns),
+            implode(', ', $columns),
+            $this->quoteTableName($tmpTableName)
+        );
+
+        $this->execute($sql);
+        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tmpTableName)));
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropForeignKey($tableName, $columns, $constraint = null)
+    {
+        // TODO: DRY this up....
+
+        $this->startCommandTimer();
+        if (is_string($columns)) {
+            $columns = array($columns); // str to array
+        }
+
+        $this->writeCommand('dropForeignKey', array($tableName, $columns));
+
+        $tmpTableName = 'tmp_' . $tableName;
+
+        $rows = $this->fetchAll('select * from sqlite_master where `type` = \'table\'');
+
+        $sql = '';
+        foreach ($rows as $table) {
+            if ($table['tbl_name'] === $tableName) {
+                $sql = $table['sql'];
+            }
+        }
+
+        $rows = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($tableName)));
+        $replaceColumns = array();
+        foreach ($rows as $row) {
+            if (!in_array($row['name'], $columns)) {
+                $replaceColumns[] = $row['name'];
+            } else {
+                $found = true;
+            }
+        }
+
+        if (!isset($found)) {
+            throw new \InvalidArgumentException(sprintf(
+                'The specified column doesn\'t exist: '
+            ));
+        }
+
+        $this->execute(sprintf('ALTER TABLE %s RENAME TO %s', $this->quoteTableName($tableName), $tmpTableName));
+
+        foreach ($columns as $columnName) {
+            $search = sprintf(
+                "/,[^,]*\(%s(?:,`?(.*)`?)?\) REFERENCES[^,]*\([^\)]*\)[^,)]*/",
+                $this->quoteColumnName($columnName)
+            );
+            $sql = preg_replace($search, '', $sql, 1);
+        }
+
+        $this->execute($sql);
+
+        $sql = sprintf(
+            'INSERT INTO %s(%s) SELECT %s FROM %s',
+            $tableName,
+            implode(', ', $columns),
+            implode(', ', $columns),
+            $tmpTableName
+        );
+
+        $this->execute($sql);
+        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tmpTableName)));
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function insert(Table $table, $row)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('insert', array($table->getName()));
+
+        $sql = sprintf(
+            "INSERT INTO %s ",
+            $this->quoteTableName($table->getName())
+        );
+
+        $columns = array_keys($row);
+        $sql .= "(". implode(', ', array_map(array($this, 'quoteColumnName'), $columns)) . ")";
+        $sql .= " VALUES ";
+
+        $sql .= "(" . implode(', ', array_map(function ($value) {
+                if (is_numeric($value)) {
+                    return $value;
+                }
+                return "'{$value}'";
+            }, $row)) . ")";
+
+        $this->execute($sql);
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSqlType($type, $limit = null)
+    {
+        switch ($type) {
+            case static::PHINX_TYPE_STRING:
+                return array('name' => 'varchar', 'limit' => 255);
+                break;
+            case static::PHINX_TYPE_CHAR:
+                return array('name' => 'char', 'limit' => 255);
+                break;
+            case static::PHINX_TYPE_TEXT:
+                return array('name' => 'text');
+                break;
+            case static::PHINX_TYPE_INTEGER:
+                return array('name' => 'integer');
+                break;
+            case static::PHINX_TYPE_BIG_INTEGER:
+                return array('name' => 'bigint');
+                break;
+            case static::PHINX_TYPE_FLOAT:
+                return array('name' => 'float');
+                break;
+            case static::PHINX_TYPE_DECIMAL:
+                return array('name' => 'decimal');
+                break;
+            case static::PHINX_TYPE_DATETIME:
+                return array('name' => 'datetime');
+                break;
+            case static::PHINX_TYPE_TIMESTAMP:
+                return array('name' => 'datetime');
+                break;
+            case static::PHINX_TYPE_TIME:
+                return array('name' => 'time');
+                break;
+            case static::PHINX_TYPE_DATE:
+                return array('name' => 'date');
+                break;
+            case static::PHINX_TYPE_BLOB:
+            case static::PHINX_TYPE_BINARY:
+                return array('name' => 'blob');
+                break;
+            case static::PHINX_TYPE_BOOLEAN:
+                return array('name' => 'boolean');
+                break;
+            case static::PHINX_TYPE_UUID:
+                return array('name' => 'char', 'limit' => 36);
+            case static::PHINX_TYPE_ENUM:
+                return array('name' => 'enum');
+            // Geospatial database types
+            // No specific data types exist in SQLite, instead all geospatial
+            // functionality is handled in the client. See also: SpatiaLite.
+            case static::PHINX_TYPE_GEOMETRY:
+            case static::PHINX_TYPE_POLYGON:
+                return array('name' => 'text');
+                return;
+            case static::PHINX_TYPE_LINESTRING:
+                return array('name' => 'varchar', 'limit' => 255);
+                break;
+            case static::PHINX_TYPE_POINT:
+                return array('name' => 'float');
+            default:
+                throw new \RuntimeException('The type: "' . $type . '" is not supported.');
+        }
+    }
+
+    /**
+     * Returns Phinx type by SQL type
+     *
+     * @param string $sqlTypeDef SQL type
+     * @returns string Phinx type
+     */
+    public function getPhinxType($sqlTypeDef)
+    {
+        if (!preg_match('/^([\w]+)(\(([\d]+)*(,([\d]+))*\))*$/', $sqlTypeDef, $matches)) {
+            throw new \RuntimeException('Column type ' . $sqlTypeDef . ' is not supported');
+        } else {
+            $limit = null;
+            $precision = null;
+            $type = $matches[1];
+            if (count($matches) > 2) {
+                $limit = $matches[3] ? $matches[3] : null;
+            }
+            if (count($matches) > 4) {
+                $precision = $matches[5];
+            }
+            switch ($matches[1]) {
+                case 'varchar':
+                    $type = static::PHINX_TYPE_STRING;
+                    if ($limit === 255) {
+                        $limit = null;
+                    }
+                    break;
+                case 'char':
+                    $type = static::PHINX_TYPE_CHAR;
+                    if ($limit === 255) {
+                        $limit = null;
+                    }
+                    if ($limit === 36) {
+                        $type = static::PHINX_TYPE_UUID;
+                    }
+                    break;
+                case 'int':
+                    $type = static::PHINX_TYPE_INTEGER;
+                    if ($limit === 11) {
+                        $limit = null;
+                    }
+                    break;
+                case 'bigint':
+                    if ($limit === 11) {
+                        $limit = null;
+                    }
+                    $type = static::PHINX_TYPE_BIG_INTEGER;
+                    break;
+                case 'blob':
+                    $type = static::PHINX_TYPE_BINARY;
+                    break;
+            }
+            if ($type === 'tinyint') {
+                if ($matches[3] === 1) {
+                    $type = static::PHINX_TYPE_BOOLEAN;
+                    $limit = null;
+                }
+            }
+
+            $this->getSqlType($type);
+
+            return array(
+                'name' => $type,
+                'limit' => $limit,
+                'precision' => $precision
+            );
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createDatabase($name, $options = array())
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('createDatabase', array($name));
+        touch($name . '.sqlite3');
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasDatabase($name)
+    {
+        return is_file($name . '.sqlite3');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropDatabase($name)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('dropDatabase', array($name));
+        if (file_exists($name . '.sqlite3')) {
+            unlink($name . '.sqlite3');
+        }
+        $this->endCommandTimer();
+    }
+
+    /**
+     * Get the definition for a `DEFAULT` statement.
+     *
+     * @param  mixed $default
+     * @return string
+     */
+    protected function getDefaultValueDefinition($default)
+    {
+        if (is_string($default) && 'CURRENT_TIMESTAMP' !== $default) {
+            $default = $this->getConnection()->quote($default);
+        } elseif (is_bool($default)) {
+            $default = $this->castToBool($default);
+        }
+        return isset($default) ? ' DEFAULT ' . $default : '';
+    }
+
+    /**
+     * Gets the SQLite Column Definition for a Column object.
+     *
+     * @param Column $column Column
+     * @return string
+     */
+    protected function getColumnSqlDefinition(Column $column)
+    {
+        $sqlType = $this->getSqlType($column->getType());
+        $def = '';
+        $def .= strtoupper($sqlType['name']);
+        if ($column->getPrecision() && $column->getScale()) {
+            $def .= '(' . $column->getPrecision() . ',' . $column->getScale() . ')';
+        }
+        $limitable = in_array(strtoupper($sqlType['name']), $this->definitionsWithLimits);
+        if (($column->getLimit() || isset($sqlType['limit'])) && $limitable) {
+            $def .= '(' . ($column->getLimit() ? $column->getLimit() : $sqlType['limit']) . ')';
+        }
+        if (($values = $column->getValues()) && is_array($values)) {
+            $def .= " CHECK({$column->getName()} IN ('" . implode("', '", $values) . "'))";
+        }
+
+        $default = $column->getDefault();
+
+        $def .= ($column->isNull() || is_null($default)) ? ' NULL' : ' NOT NULL';
+        $def .= $this->getDefaultValueDefinition($default);
+        $def .= ($column->isIdentity()) ? ' PRIMARY KEY AUTOINCREMENT' : '';
+
+        if ($column->getUpdate()) {
+            $def .= ' ON UPDATE ' . $column->getUpdate();
+        }
+
+        $def .= $this->getCommentDefinition($column);
+
+        return $def;
+    }
+
+    /**
+     * Gets the comment Definition for a Column object.
+     *
+     * @param Column $column Column
+     * @return string
+     */
+    protected function getCommentDefinition(Column $column)
+    {
+        if ($column->getComment()) {
+            return ' /* ' . $column->getComment() . ' */ ';
+        }
+        return '';
+    }
+
+    /**
+     * Gets the SQLite Index Definition for an Index object.
+     *
+     * @param Index $index Index
+     * @return string
+     */
+    protected function getIndexSqlDefinition(Table $table, Index $index)
+    {
+        if ($index->getType() === Index::UNIQUE) {
+            $def = 'UNIQUE INDEX';
+        } else {
+            $def = 'INDEX';
+        }
+        if (is_string($index->getName())) {
+            $indexName = $index->getName();
+        } else {
+            $indexName = $table->getName() . '_';
+            foreach ($index->getColumns() as $column) {
+                $indexName .= $column . '_';
+            }
+            $indexName .= 'index';
+        }
+        $def .= ' `' . $indexName . '`';
+        return $def;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getColumnTypes()
+    {
+        return array_merge(parent::getColumnTypes(), array('enum'));
+    }
+
+    /**
+     * Gets the SQLite Foreign Key Definition for an ForeignKey object.
+     *
+     * @param ForeignKey $foreignKey
+     * @return string
+     */
+    protected function getForeignKeySqlDefinition(ForeignKey $foreignKey)
+    {
+        $def = '';
+        if ($foreignKey->getConstraint()) {
+            $def .= ' CONSTRAINT ' . $this->quoteColumnName($foreignKey->getConstraint());
+        } else {
+            $columnNames = array();
+            foreach ($foreignKey->getColumns() as $column) {
+                $columnNames[] = $this->quoteColumnName($column);
+            }
+            $def .= ' FOREIGN KEY (' . implode(',', $columnNames) . ')';
+            $refColumnNames = array();
+            foreach ($foreignKey->getReferencedColumns() as $column) {
+                $refColumnNames[] = $this->quoteColumnName($column);
+            }
+            $def .= ' REFERENCES ' . $this->quoteTableName($foreignKey->getReferencedTable()->getName()) . ' (' . implode(',', $refColumnNames) . ')';
+            if ($foreignKey->getOnDelete()) {
+                $def .= ' ON DELETE ' . $foreignKey->getOnDelete();
+            }
+            if ($foreignKey->getOnUpdate()) {
+                $def .= ' ON UPDATE ' . $foreignKey->getOnUpdate();
+            }
+        }
+        return $def;
+    }
+}
diff --git a/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/SqlServerAdapter.php b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/SqlServerAdapter.php
new file mode 100755
index 0000000..99f8a82
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/SqlServerAdapter.php
@@ -0,0 +1,1165 @@
+<?php
+/**
+ * Phinx
+ *
+ * (The MIT license)
+ * Copyright (c) 2015 Rob Morgan
+ *
+ * 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.
+ *
+ * @package    Phinx
+ * @subpackage Phinx\Db\Adapter
+ */
+namespace Phinx\Db\Adapter;
+
+use Phinx\Db\Table;
+use Phinx\Db\Table\Column;
+use Phinx\Db\Table\Index;
+use Phinx\Db\Table\ForeignKey;
+
+/**
+ * Phinx SqlServer Adapter.
+ *
+ * @author Rob Morgan <robbym@gmail.com>
+ */
+class SqlServerAdapter extends PdoAdapter implements AdapterInterface
+{
+    protected $schema = 'dbo';
+
+    protected $signedColumnTypes = array('integer' => true, 'biginteger' => true, 'float' => true, 'decimal' => true);
+
+    /**
+     * {@inheritdoc}
+     */
+    public function connect()
+    {
+        if (null === $this->connection) {
+            if (!class_exists('PDO') || !in_array('sqlsrv', \PDO::getAvailableDrivers(), true)) {
+                // try our connection via freetds (Mac/Linux)
+                return $this->connectDblib();
+            }
+
+            $db = null;
+            $options = $this->getOptions();
+
+            // if port is specified use it, otherwise use the SqlServer default
+            if (empty($options['port'])) {
+                $dsn = 'sqlsrv:server=' . $options['host'] . ';database=' . $options['name'];
+            } else {
+                $dsn = 'sqlsrv:server=' . $options['host'] . ',' . $options['port'] . ';database=' . $options['name'];
+            }
+            $dsn .= ';MultipleActiveResultSets=false';
+
+            $driverOptions = array(\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION);
+
+            // charset support
+            if (isset($options['charset'])) {
+                $driverOptions[\PDO::SQLSRV_ATTR_ENCODING] = $options['charset'];
+            }
+
+            // support arbitrary \PDO::SQLSRV_ATTR_* driver options and pass them to PDO
+            // http://php.net/manual/en/ref.pdo-sqlsrv.php#pdo-sqlsrv.constants
+            foreach ($options as $key => $option) {
+                if (strpos($key, 'sqlsrv_attr_') === 0) {
+                    $driverOptions[constant('\PDO::' . strtoupper($key))] = $option;
+                }
+            }
+
+            try {
+                $db = new \PDO($dsn, $options['user'], $options['pass'], $driverOptions);
+            } catch (\PDOException $exception) {
+                throw new \InvalidArgumentException(sprintf(
+                    'There was a problem connecting to the database: %s',
+                    $exception->getMessage()
+                ));
+            }
+
+            $this->setConnection($db);
+        }
+    }
+
+    /**
+     * Connect to MSSQL using dblib/freetds.
+     *
+     * The "sqlsrv" driver is not available on Unix machines.
+     *
+     * @throws \InvalidArgumentException
+     */
+    protected function connectDblib()
+    {
+        if (!class_exists('PDO') || !in_array('dblib', \PDO::getAvailableDrivers(), true)) {
+            // @codeCoverageIgnoreStart
+            throw new \RuntimeException('You need to enable the PDO_Dblib extension for Phinx to run properly.');
+            // @codeCoverageIgnoreEnd
+        }
+
+        $options = $this->getOptions();
+
+        // if port is specified use it, otherwise use the SqlServer default
+        if (empty($options['port'])) {
+            $dsn = 'dblib:host=' . $options['host'] . ';dbname=' . $options['name'];
+        } else {
+            $dsn = 'dblib:host=' . $options['host'] . ':' . $options['port'] . ';dbname=' . $options['name'];
+        }
+
+        $driverOptions = array(\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION);
+
+        try {
+            $db = new \PDO($dsn, $options['user'], $options['pass'], $driverOptions);
+        } catch (\PDOException $exception) {
+            throw new \InvalidArgumentException(sprintf(
+                'There was a problem connecting to the database: %s',
+                $exception->getMessage()
+            ));
+        }
+
+        $this->setConnection($db);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function disconnect()
+    {
+        $this->connection = null;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasTransactions()
+    {
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function beginTransaction()
+    {
+        $this->execute('BEGIN TRANSACTION');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function commitTransaction()
+    {
+        $this->execute('COMMIT TRANSACTION');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function rollbackTransaction()
+    {
+        $this->execute('ROLLBACK TRANSACTION');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function quoteTableName($tableName)
+    {
+        return str_replace('.', '].[', $this->quoteColumnName($tableName));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function quoteColumnName($columnName)
+    {
+        return '[' . str_replace(']', '\]', $columnName) . ']';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasTable($tableName)
+    {
+        $result = $this->fetchRow(sprintf('SELECT count(*) as [count] FROM information_schema.tables WHERE table_name = \'%s\';', $tableName));
+        return $result['count'] > 0;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createTable(Table $table)
+    {
+        $this->startCommandTimer();
+
+        $options = $table->getOptions();
+
+        // Add the default primary key
+        $columns = $table->getPendingColumns();
+        if (!isset($options['id']) || (isset($options['id']) && $options['id'] === true)) {
+            $column = new Column();
+            $column->setName('id')
+                   ->setType('integer')
+                   ->setIdentity(true);
+
+            array_unshift($columns, $column);
+            $options['primary_key'] = 'id';
+
+        } elseif (isset($options['id']) && is_string($options['id'])) {
+            // Handle id => "field_name" to support AUTO_INCREMENT
+            $column = new Column();
+            $column->setName($options['id'])
+                   ->setType('integer')
+                   ->setIdentity(true);
+
+            array_unshift($columns, $column);
+            $options['primary_key'] = $options['id'];
+        }
+
+        $sql = 'CREATE TABLE ';
+        $sql .= $this->quoteTableName($table->getName()) . ' (';
+        $sqlBuffer = array();
+        $columnsWithComments = array();
+        foreach ($columns as $column) {
+            $sqlBuffer[] = $this->quoteColumnName($column->getName()) . ' ' . $this->getColumnSqlDefinition($column);
+
+            // set column comments, if needed
+            if ($column->getComment()) {
+                $columnsWithComments[] = $column;
+            }
+        }
+
+        // set the primary key(s)
+        if (isset($options['primary_key'])) {
+            $pkSql = sprintf('CONSTRAINT PK_%s PRIMARY KEY (', $table->getName());
+            if (is_string($options['primary_key'])) { // handle primary_key => 'id'
+                $pkSql .= $this->quoteColumnName($options['primary_key']);
+            } elseif (is_array($options['primary_key'])) { // handle primary_key => array('tag_id', 'resource_id')
+                // PHP 5.4 will allow access of $this, so we can call quoteColumnName() directly in the anonymous function,
+                // but for now just hard-code the adapter quotes
+                $pkSql .= implode(
+                    ',',
+                    array_map(
+                        function ($v) {
+                            return '[' . $v . ']';
+                        },
+                        $options['primary_key']
+                    )
+                );
+            }
+            $pkSql .= ')';
+            $sqlBuffer[] = $pkSql;
+        }
+
+        // set the foreign keys
+        $foreignKeys = $table->getForeignKeys();
+        if (!empty($foreignKeys)) {
+            foreach ($foreignKeys as $foreignKey) {
+                $sqlBuffer[] = $this->getForeignKeySqlDefinition($foreignKey, $table->getName());
+            }
+        }
+
+        $sql .= implode(', ', $sqlBuffer);
+        $sql .= ');';
+
+        // process column comments
+        if (!empty($columnsWithComments)) {
+            foreach ($columnsWithComments as $column) {
+                $sql .= $this->getColumnCommentSqlDefinition($column, $table->getName());
+            }
+        }
+
+        // set the indexes
+        $indexes = $table->getIndexes();
+        if (!empty($indexes)) {
+            foreach ($indexes as $index) {
+                $sql .= $this->getIndexSqlDefinition($index, $table->getName());
+            }
+        }
+
+        // execute the sql
+        $this->writeCommand('createTable', array($table->getName()));
+        $this->execute($sql);
+        $this->endCommandTimer();
+    }
+
+    /**
+     * Gets the SqlServer Column Comment Defininition for a column object.
+     *
+     * @param Column $column    Column
+     * @param string $tableName Table name
+     *
+     * @return string
+     */
+    protected function getColumnCommentSqlDefinition(Column $column, $tableName)
+    {
+        // passing 'null' is to remove column comment
+        $currentComment = $this->getColumnComment($tableName, $column->getName());
+
+        $comment = (strcasecmp($column->getComment(), 'NULL') !== 0) ? $this->getConnection()->quote($column->getComment()) : '\'\'';
+        $command = $currentComment === false ? 'sp_addextendedproperty' : 'sp_updateextendedproperty';
+        return sprintf(
+            "EXECUTE %s N'MS_Description', N%s, N'SCHEMA', N'%s', N'TABLE', N'%s', N'COLUMN', N'%s';",
+            $command,
+            $comment,
+            $this->schema,
+            $tableName,
+            $column->getName()
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function renameTable($tableName, $newTableName)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('renameTable', array($tableName, $newTableName));
+        $this->execute(sprintf('EXEC sp_rename \'%s\', \'%s\'', $tableName, $newTableName));
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropTable($tableName)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('dropTable', array($tableName));
+        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tableName)));
+        $this->endCommandTimer();
+    }
+
+    public function getColumnComment($tableName, $columnName)
+    {
+        $sql = sprintf("SELECT cast(extended_properties.[value] as nvarchar(4000)) comment
+  FROM sys.schemas
+ INNER JOIN sys.tables
+    ON schemas.schema_id = tables.schema_id
+ INNER JOIN sys.columns
+    ON tables.object_id = columns.object_id
+ INNER JOIN sys.extended_properties
+    ON tables.object_id = extended_properties.major_id
+   AND columns.column_id = extended_properties.minor_id
+   AND extended_properties.name = 'MS_Description'
+   WHERE schemas.[name] = '%s' AND tables.[name] = '%s' AND columns.[name] = '%s'", $this->schema, $tableName, $columnName);
+        $row = $this->fetchRow($sql);
+
+        if ($row) {
+            return $row['comment'];
+        }
+
+        return false;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getColumns($tableName)
+    {
+        $columns = array();
+        $sql = sprintf(
+            "SELECT DISTINCT TABLE_SCHEMA AS [schema], TABLE_NAME as [table_name], COLUMN_NAME AS [name], DATA_TYPE AS [type],
+            IS_NULLABLE AS [null], COLUMN_DEFAULT AS [default],
+            CHARACTER_MAXIMUM_LENGTH AS [char_length],
+            NUMERIC_PRECISION AS [precision],
+            NUMERIC_SCALE AS [scale], ORDINAL_POSITION AS [ordinal_position],
+            COLUMNPROPERTY(object_id(TABLE_NAME), COLUMN_NAME, 'IsIdentity') as [identity]
+        FROM INFORMATION_SCHEMA.COLUMNS
+        WHERE TABLE_NAME = '%s'
+        ORDER BY ordinal_position",
+            $tableName
+        );
+        $rows = $this->fetchAll($sql);
+        foreach ($rows as $columnInfo) {
+            $column = new Column();
+            $column->setName($columnInfo['name'])
+                   ->setType($this->getPhinxType($columnInfo['type']))
+                   ->setNull($columnInfo['null'] !== 'NO')
+                   ->setDefault($this->parseDefault($columnInfo['default']))
+                   ->setIdentity($columnInfo['identity'] === '1')
+                   ->setComment($this->getColumnComment($columnInfo['table_name'], $columnInfo['name']));
+
+            if (!empty($columnInfo['char_length'])) {
+                $column->setLimit($columnInfo['char_length']);
+            }
+
+            $columns[$columnInfo['name']] = $column;
+        }
+
+        return $columns;
+    }
+
+    protected function parseDefault($default)
+    {
+        $default = preg_replace(array("/\('(.*)'\)/", "/\(\((.*)\)\)/", "/\((.*)\)/"), '$1', $default);
+
+        if (strtoupper($default) === 'NULL') {
+            $default = null;
+        } elseif (is_numeric($default)) {
+            $default = (int) $default;
+        }
+
+        return $default;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasColumn($tableName, $columnName, $options = array())
+    {
+        $sql = sprintf(
+            "SELECT count(*) as [count]
+             FROM information_schema.columns
+             WHERE table_name = '%s' AND column_name = '%s'",
+            $tableName,
+            $columnName
+        );
+        $result = $this->fetchRow($sql);
+
+        return $result['count'] > 0;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addColumn(Table $table, Column $column)
+    {
+        $this->startCommandTimer();
+        $sql = sprintf(
+            'ALTER TABLE %s ADD %s %s',
+            $this->quoteTableName($table->getName()),
+            $this->quoteColumnName($column->getName()),
+            $this->getColumnSqlDefinition($column)
+        );
+
+        $this->writeCommand('addColumn', array($table->getName(), $column->getName(), $column->getType()));
+        $this->execute($sql);
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function renameColumn($tableName, $columnName, $newColumnName)
+    {
+        $this->startCommandTimer();
+
+        if (!$this->hasColumn($tableName, $columnName)) {
+            throw new \InvalidArgumentException("The specified column does not exist: $columnName");
+        }
+        $this->writeCommand('renameColumn', array($tableName, $columnName, $newColumnName));
+        $this->renameDefault($tableName, $columnName, $newColumnName);
+        $this->execute(
+             sprintf(
+                 "EXECUTE sp_rename N'%s.%s', N'%s', 'COLUMN' ",
+                 $tableName,
+                 $columnName,
+                 $newColumnName
+             )
+        );
+        $this->endCommandTimer();
+    }
+
+    protected function renameDefault($tableName, $columnName, $newColumnName)
+    {
+        $oldConstraintName = "DF_{$tableName}_{$columnName}";
+        $newConstraintName = "DF_{$tableName}_{$newColumnName}";
+        $sql = <<<SQL
+IF (OBJECT_ID('$oldConstraintName', 'D') IS NOT NULL)
+BEGIN
+     EXECUTE sp_rename N'%s', N'%s', N'OBJECT'
+END
+SQL;
+        $this->execute(sprintf(
+            $sql,
+            $oldConstraintName,
+            $newConstraintName
+        ));
+    }
+
+    public function changeDefault($tableName, Column $newColumn)
+    {
+        $constraintName = "DF_{$tableName}_{$newColumn->getName()}";
+        $default = $newColumn->getDefault();
+
+        if ($default === null) {
+            $default = 'DEFAULT NULL';
+        } else {
+            $default = $this->getDefaultValueDefinition($default);
+        }
+
+        if (empty($default)) {
+            return;
+        }
+
+        $this->execute(sprintf(
+            'ALTER TABLE %s ADD CONSTRAINT %s %s FOR %s',
+            $this->quoteTableName($tableName),
+            $constraintName,
+            $default,
+            $this->quoteColumnName($newColumn->getName())
+        ));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function changeColumn($tableName, $columnName, Column $newColumn)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('changeColumn', array($tableName, $columnName, $newColumn->getType()));
+        $columns = $this->getColumns($tableName);
+        $changeDefault = $newColumn->getDefault() !== $columns[$columnName]->getDefault() || $newColumn->getType() !== $columns[$columnName]->getType();
+        if ($columnName !== $newColumn->getName()) {
+            $this->renameColumn($tableName, $columnName, $newColumn->getName());
+        }
+
+        if ($changeDefault) {
+            $this->dropDefaultConstraint($tableName, $newColumn->getName());
+        }
+
+        $this->execute(
+            sprintf(
+                'ALTER TABLE %s ALTER COLUMN %s %s',
+                $this->quoteTableName($tableName),
+                $this->quoteColumnName($newColumn->getName()),
+                $this->getColumnSqlDefinition($newColumn, false)
+            )
+        );
+        // change column comment if needed
+        if ($newColumn->getComment()) {
+            $sql = $this->getColumnCommentSqlDefinition($newColumn, $tableName);
+            $this->execute($sql);
+        }
+
+        if ($changeDefault) {
+            $this->changeDefault($tableName, $newColumn);
+        }
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropColumn($tableName, $columnName)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('dropColumn', array($tableName, $columnName));
+        $this->dropDefaultConstraint($tableName, $columnName);
+
+        $this->execute(
+            sprintf(
+                'ALTER TABLE %s DROP COLUMN %s',
+                $this->quoteTableName($tableName),
+                $this->quoteColumnName($columnName)
+            )
+        );
+        $this->endCommandTimer();
+    }
+
+    protected function dropDefaultConstraint($tableName, $columnName)
+    {
+        $defaultConstraint = $this->getDefaultConstraint($tableName, $columnName);
+
+        if (!$defaultConstraint) {
+            return;
+        }
+
+        $this->dropForeignKey($tableName, $columnName, $defaultConstraint);
+    }
+
+    protected function getDefaultConstraint($tableName, $columnName)
+    {
+        $sql = "SELECT
+    default_constraints.name
+FROM
+    sys.all_columns
+
+        INNER JOIN
+    sys.tables
+        ON all_columns.object_id = tables.object_id
+
+        INNER JOIN
+    sys.schemas
+        ON tables.schema_id = schemas.schema_id
+
+        INNER JOIN
+    sys.default_constraints
+        ON all_columns.default_object_id = default_constraints.object_id
+
+WHERE
+        schemas.name = 'dbo'
+    AND tables.name = '{$tableName}'
+    AND all_columns.name = '{$columnName}'";
+
+        $rows = $this->fetchAll($sql);
+        return empty($rows) ? false : $rows[0]['name'];
+    }
+
+    protected function getIndexColums($tableId, $indexId)
+    {
+        $sql = "SELECT AC.[name] AS [column_name]
+FROM sys.[index_columns] IC
+  INNER JOIN sys.[all_columns] AC ON IC.[column_id] = AC.[column_id]
+WHERE AC.[object_id] = {$tableId} AND IC.[index_id] = {$indexId}  AND IC.[object_id] = {$tableId}
+ORDER BY IC.[key_ordinal];";
+
+        $rows = $this->fetchAll($sql);
+        $columns = array();
+        foreach($rows as $row) {
+            $columns[] = strtolower($row['column_name']);
+        }
+        return $columns;
+    }
+
+    /**
+     * Get an array of indexes from a particular table.
+     *
+     * @param string $tableName Table Name
+     * @return array
+     */
+    public function getIndexes($tableName)
+    {
+        $indexes = array();
+        $sql = "SELECT I.[name] AS [index_name], I.[index_id] as [index_id], T.[object_id] as [table_id]
+FROM sys.[tables] AS T
+  INNER JOIN sys.[indexes] I ON T.[object_id] = I.[object_id]
+WHERE T.[is_ms_shipped] = 0 AND I.[type_desc] <> 'HEAP'  AND T.[name] = '{$tableName}'
+ORDER BY T.[name], I.[index_id];";
+
+        $rows = $this->fetchAll($sql);
+        foreach ($rows as $row) {
+            $columns = $this->getIndexColums($row['table_id'], $row['index_id']);
+            $indexes[$row['index_name']] = array('columns' => $columns);
+        }
+
+        return $indexes;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasIndex($tableName, $columns)
+    {
+        if (is_string($columns)) {
+            $columns = array($columns); // str to array
+        }
+
+        $columns = array_map('strtolower', $columns);
+        $indexes = $this->getIndexes($tableName);
+
+        foreach ($indexes as $index) {
+            $a = array_diff($columns, $index['columns']);
+
+            if (empty($a)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasIndexByName($tableName, $indexName)
+    {
+        $indexes = $this->getIndexes($tableName);
+
+        foreach ($indexes as $name => $index) {
+            if ($name === $indexName) {
+                 return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addIndex(Table $table, Index $index)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('addIndex', array($table->getName(), $index->getColumns()));
+        $sql = $this->getIndexSqlDefinition($index, $table->getName());
+        $this->execute($sql);
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropIndex($tableName, $columns)
+    {
+        $this->startCommandTimer();
+        if (is_string($columns)) {
+            $columns = array($columns); // str to array
+        }
+
+        $this->writeCommand('dropIndex', array($tableName, $columns));
+        $indexes = $this->getIndexes($tableName);
+        $columns = array_map('strtolower', $columns);
+
+        foreach ($indexes as $indexName => $index) {
+            $a = array_diff($columns, $index['columns']);
+            if (empty($a)) {
+                $this->execute(
+                    sprintf(
+                        'DROP INDEX %s ON %s',
+                        $this->quoteColumnName($indexName),
+                        $this->quoteTableName($tableName)
+                    )
+                );
+                $this->endCommandTimer();
+                return;
+            }
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropIndexByName($tableName, $indexName)
+    {
+        $this->startCommandTimer();
+
+        $this->writeCommand('dropIndexByName', array($tableName, $indexName));
+        $indexes = $this->getIndexes($tableName);
+
+        foreach ($indexes as $name => $index) {
+            if ($name === $indexName) {
+                $this->execute(
+                    sprintf(
+                        'DROP INDEX %s ON %s',
+                        $this->quoteColumnName($indexName),
+                        $this->quoteTableName($tableName)
+                    )
+                );
+                $this->endCommandTimer();
+                return;
+            }
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasForeignKey($tableName, $columns, $constraint = null)
+    {
+        if (is_string($columns)) {
+            $columns = array($columns); // str to array
+        }
+        $foreignKeys = $this->getForeignKeys($tableName);
+        if ($constraint) {
+            if (isset($foreignKeys[$constraint])) {
+                return !empty($foreignKeys[$constraint]);
+            }
+            return false;
+        } else {
+            foreach ($foreignKeys as $key) {
+                $a = array_diff($columns, $key['columns']);
+                if (empty($a)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Get an array of foreign keys from a particular table.
+     *
+     * @param string $tableName Table Name
+     * @return array
+     */
+    protected function getForeignKeys($tableName)
+    {
+        $foreignKeys = array();
+        $rows = $this->fetchAll(sprintf(
+            "SELECT
+                    tc.constraint_name,
+                    tc.table_name, kcu.column_name,
+                    ccu.table_name AS referenced_table_name,
+                    ccu.column_name AS referenced_column_name
+                FROM
+                    information_schema.table_constraints AS tc
+                    JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name
+                    JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
+                WHERE constraint_type = 'FOREIGN KEY' AND tc.table_name = '%s'
+                ORDER BY kcu.ordinal_position",
+            $tableName
+        ));
+        foreach ($rows as $row) {
+            $foreignKeys[$row['constraint_name']]['table'] = $row['table_name'];
+            $foreignKeys[$row['constraint_name']]['columns'][] = $row['column_name'];
+            $foreignKeys[$row['constraint_name']]['referenced_table'] = $row['referenced_table_name'];
+            $foreignKeys[$row['constraint_name']]['referenced_columns'][] = $row['referenced_column_name'];
+        }
+
+        return $foreignKeys;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addForeignKey(Table $table, ForeignKey $foreignKey)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('addForeignKey', array($table->getName(), $foreignKey->getColumns()));
+        $this->execute(
+            sprintf(
+                'ALTER TABLE %s ADD %s',
+                $this->quoteTableName($table->getName()),
+                $this->getForeignKeySqlDefinition($foreignKey, $table->getName())
+            )
+        );
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropForeignKey($tableName, $columns, $constraint = null)
+    {
+        $this->startCommandTimer();
+        if (is_string($columns)) {
+            $columns = array($columns); // str to array
+        }
+
+        $this->writeCommand('dropForeignKey', array($tableName, $columns));
+
+        if ($constraint) {
+            $this->execute(
+                sprintf(
+                    'ALTER TABLE %s DROP CONSTRAINT %s',
+                    $this->quoteTableName($tableName),
+                    $constraint
+                )
+            );
+            $this->endCommandTimer();
+            return;
+        } else {
+            foreach ($columns as $column) {
+                $rows = $this->fetchAll(sprintf(
+                    "SELECT
+                    tc.constraint_name,
+                    tc.table_name, kcu.column_name,
+                    ccu.table_name AS referenced_table_name,
+                    ccu.column_name AS referenced_column_name
+                FROM
+                    information_schema.table_constraints AS tc
+                    JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name
+                    JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
+                WHERE constraint_type = 'FOREIGN KEY' AND tc.table_name = '%s' and ccu.column_name='%s'
+                ORDER BY kcu.ordinal_position",
+                    $tableName,
+                    $column
+                ));
+                foreach ($rows as $row) {
+                    $this->dropForeignKey($tableName, $columns, $row['constraint_name']);
+                }
+            }
+        }
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSqlType($type, $limit = null)
+    {
+        switch ($type) {
+            case static::PHINX_TYPE_STRING:
+                return array('name' => 'nvarchar', 'limit' => 255);
+                break;
+            case static::PHINX_TYPE_CHAR:
+                return array('name' => 'nchar', 'limit' => 255);
+                break;
+            case static::PHINX_TYPE_TEXT:
+                return array('name' => 'ntext');
+                break;
+            case static::PHINX_TYPE_INTEGER:
+                return array('name' => 'int');
+                break;
+            case static::PHINX_TYPE_BIG_INTEGER:
+                return array('name' => 'bigint');
+                break;
+            case static::PHINX_TYPE_FLOAT:
+                return array('name' => 'float');
+                break;
+            case static::PHINX_TYPE_DECIMAL:
+                return array('name' => 'decimal');
+                break;
+            case static::PHINX_TYPE_DATETIME:
+            case static::PHINX_TYPE_TIMESTAMP:
+                return array('name' => 'datetime');
+                break;
+            case static::PHINX_TYPE_TIME:
+                return array('name' => 'time');
+                break;
+            case static::PHINX_TYPE_DATE:
+                return array('name' => 'date');
+                break;
+            case static::PHINX_TYPE_BLOB:
+            case static::PHINX_TYPE_BINARY:
+                return array('name' => 'varbinary');
+                break;
+            case static::PHINX_TYPE_BOOLEAN:
+                return array('name' => 'bit');
+                break;
+            case static::PHINX_TYPE_UUID:
+                return array('name' => 'uniqueidentifier');
+            case static::PHINX_TYPE_FILESTREAM:
+                return array('name' => 'varbinary', 'limit' => 'max');
+            // Geospatial database types
+            case static::PHINX_TYPE_GEOMETRY:
+            case static::PHINX_TYPE_POINT:
+            case static::PHINX_TYPE_LINESTRING:
+            case static::PHINX_TYPE_POLYGON:
+                // SQL Server stores all spatial data using a single data type.
+                // Specific types (point, polygon, etc) are set at insert time.
+                return array('name' => 'geography');
+                break;
+            default:
+                throw new \RuntimeException('The type: "' . $type . '" is not supported.');
+        }
+    }
+
+    /**
+     * Returns Phinx type by SQL type
+     *
+     * @param $sqlTypeDef
+     * @throws \RuntimeException
+     * @internal param string $sqlType SQL type
+     * @returns string Phinx type
+     */
+    public function getPhinxType($sqlType)
+    {
+        switch ($sqlType) {
+            case 'nvarchar':
+            case 'varchar':
+                return static::PHINX_TYPE_STRING;
+            case 'char':
+            case 'nchar':
+                return static::PHINX_TYPE_CHAR;
+            case 'text':
+            case 'ntext':
+                return static::PHINX_TYPE_TEXT;
+            case 'int':
+            case 'integer':
+                return static::PHINX_TYPE_INTEGER;
+            case 'decimal':
+            case 'numeric':
+            case 'money':
+                return static::PHINX_TYPE_DECIMAL;
+            case 'bigint':
+                return static::PHINX_TYPE_BIG_INTEGER;
+            case 'real':
+            case 'float':
+                return static::PHINX_TYPE_FLOAT;
+            case 'binary':
+            case 'image':
+            case 'varbinary':
+                return static::PHINX_TYPE_BINARY;
+                break;
+            case 'time':
+                return static::PHINX_TYPE_TIME;
+            case 'date':
+                return static::PHINX_TYPE_DATE;
+            case 'datetime':
+            case 'timestamp':
+                return static::PHINX_TYPE_DATETIME;
+            case 'bit':
+                return static::PHINX_TYPE_BOOLEAN;
+            case 'uniqueidentifier':
+                return static::PHINX_TYPE_UUID;
+            case 'filestream':
+                return static::PHINX_TYPE_FILESTREAM;
+            default:
+                throw new \RuntimeException('The SqlServer type: "' . $sqlType . '" is not supported');
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createDatabase($name, $options = array())
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('createDatabase', array($name));
+
+        if (isset($options['collation'])) {
+            $this->execute(sprintf('CREATE DATABASE [%s] COLLATE [%s]', $name, $options['collation']));
+        } else {
+            $this->execute(sprintf('CREATE DATABASE [%s]', $name));
+        }
+        $this->execute(sprintf('USE [%s]', $name));
+        $this->endCommandTimer();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasDatabase($name)
+    {
+        $result = $this->fetchRow(
+            sprintf(
+                'SELECT count(*) as [count] FROM master.dbo.sysdatabases WHERE [name] = \'%s\'',
+                $name
+            )
+        );
+
+        return $result['count'] > 0;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropDatabase($name)
+    {
+        $this->startCommandTimer();
+        $this->writeCommand('dropDatabase', array($name));
+        $sql = <<<SQL
+USE master;
+IF EXISTS(select * from sys.databases where name=N'$name')
+ALTER DATABASE [$name] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
+DROP DATABASE [$name];
+SQL;
+        $this->execute($sql);
+        $this->endCommandTimer();
+    }
+
+    /**
+     * Get the defintion for a `DEFAULT` statement.
+     *
+     * @param  mixed $default
+     * @return string
+     */
+    protected function getDefaultValueDefinition($default)
+    {
+        if (is_string($default) && 'CURRENT_TIMESTAMP' !== $default) {
+            $default = $this->getConnection()->quote($default);
+        } elseif (is_bool($default)) {
+            $default = $this->castToBool($default);
+        }
+        return isset($default) ? ' DEFAULT ' . $default : '';
+    }
+
+    /**
+     * Gets the SqlServer Column Definition for a Column object.
+     *
+     * @param Column $column Column
+     * @return string
+     */
+    protected function getColumnSqlDefinition(Column $column, $create = true)
+    {
+        $buffer = array();
+
+        $sqlType = $this->getSqlType($column->getType());
+        $buffer[] = strtoupper($sqlType['name']);
+        // integers cant have limits in SQlServer
+        $noLimits = array(
+            'bigint',
+            'int',
+            'tinyint'
+        );
+        if (!in_array($sqlType['name'], $noLimits) && ($column->getLimit() || isset($sqlType['limit']))) {
+            $buffer[] = sprintf('(%s)', $column->getLimit() ? $column->getLimit() : $sqlType['limit']);
+        }
+        if ($column->getPrecision() && $column->getScale()) {
+            $buffer[] = '(' . $column->getPrecision() . ',' . $column->getScale() . ')';
+        }
+
+        $properties = $column->getProperties();
+        $buffer[] = $column->getType() === 'filestream' ? 'FILESTREAM' : '';
+        $buffer[] = isset($properties['rowguidcol']) ? 'ROWGUIDCOL' : '';
+
+        $buffer[] = $column->isNull() ? 'NULL' : 'NOT NULL';
+
+        if ($create === true) {
+            if ($column->getDefault() === null && $column->isNull()) {
+                $buffer[] = ' DEFAULT NULL';
+            } else {
+                $buffer[] = $this->getDefaultValueDefinition($column->getDefault());
+            }
+        }
+
+        if ($column->isIdentity()) {
+            $buffer[] = 'IDENTITY(1, 1)';
+        }
+
+        return implode(' ', $buffer);
+    }
+
+    /**
+     * Gets the SqlServer Index Definition for an Index object.
+     *
+     * @param Index $index Index
+     * @return string
+     */
+    protected function getIndexSqlDefinition(Index $index, $tableName)
+    {
+        if (is_string($index->getName())) {
+            $indexName = $index->getName();
+        } else {
+            $columnNames = $index->getColumns();
+            if (is_string($columnNames)) {
+                $columnNames = array($columnNames);
+            }
+            $indexName = sprintf('%s_%s', $tableName, implode('_', $columnNames));
+        }
+        $def = sprintf(
+            "CREATE %s INDEX %s ON %s (%s);",
+            ($index->getType() === Index::UNIQUE ? 'UNIQUE' : ''),
+            $indexName,
+            $this->quoteTableName($tableName),
+            '[' . implode('],[', $index->getColumns()) . ']'
+        );
+
+        return $def;
+    }
+
+    /**
+     * Gets the SqlServer Foreign Key Definition for an ForeignKey object.
+     *
+     * @param ForeignKey $foreignKey
+     * @return string
+     */
+    protected function getForeignKeySqlDefinition(ForeignKey $foreignKey, $tableName)
+    {
+        $def = ' CONSTRAINT "';
+        $def .= $tableName . '_' . implode('_', $foreignKey->getColumns());
+        $def .= '" FOREIGN KEY ("' . implode('", "', $foreignKey->getColumns()) . '")';
+        $def .= " REFERENCES {$this->quoteTableName($foreignKey->getReferencedTable()->getName())} (\"" . implode('", "', $foreignKey->getReferencedColumns()) . '")';
+        if ($foreignKey->getOnDelete()) {
+            $def .= " ON DELETE {$foreignKey->getOnDelete()}";
+        }
+        if ($foreignKey->getOnUpdate()) {
+            $def .= " ON UPDATE {$foreignKey->getOnUpdate()}";
+        }
+
+        return $def;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getColumnTypes()
+    {
+        return array_merge(parent::getColumnTypes(), array('filestream'));
+    }
+}
diff --git a/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/TablePrefixAdapter.php b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/TablePrefixAdapter.php
new file mode 100755
index 0000000..66a850c
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/TablePrefixAdapter.php
@@ -0,0 +1,272 @@
+<?php
+/**
+ * Phinx
+ *
+ * (The MIT license)
+ * Copyright (c) 2015 Rob Morgan
+ *
+ * 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.
+ *
+ * @package    Phinx
+ * @subpackage Phinx\Db\Adapter
+ */
+namespace Phinx\Db\Adapter;
+
+use Phinx\Db\Table;
+use Phinx\Db\Table\Column;
+use Phinx\Db\Table\Index;
+use Phinx\Db\Table\ForeignKey;
+
+/**
+ * Table prefix/suffix adapter.
+ *
+ * Used for inserting a prefix or suffix into table names.
+ *
+ * @author Samuel Fisher <sam@sfisher.co>
+ */
+class TablePrefixAdapter extends AdapterWrapper
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getAdapterType()
+    {
+        return 'TablePrefixAdapter';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasTable($tableName)
+    {
+        $adapterTableName = $this->getAdapterTableName($tableName);
+        return parent::hasTable($adapterTableName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createTable(Table $table)
+    {
+        $adapterTable = clone $table;
+        $adapterTableName = $this->getAdapterTableName($table->getName());
+        $adapterTable->setName($adapterTableName);
+
+        foreach ($adapterTable->getForeignKeys() as $fk) {
+            $adapterReferenceTable = $fk->getReferencedTable();
+            $adapterReferenceTableName = $this->getAdapterTableName($adapterReferenceTable->getName());
+            $adapterReferenceTable->setName($adapterReferenceTableName);
+        }
+
+        return parent::createTable($adapterTable);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function renameTable($tableName, $newTableName)
+    {
+        $adapterTableName = $this->getAdapterTableName($tableName);
+        $adapterNewTableName = $this->getAdapterTableName($newTableName);
+        return parent::renameTable($adapterTableName, $adapterNewTableName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropTable($tableName)
+    {
+        $adapterTableName = $this->getAdapterTableName($tableName);
+        return parent::dropTable($adapterTableName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getColumns($tableName)
+    {
+        $adapterTableName = $this->getAdapterTableName($tableName);
+        return parent::getColumns($adapterTableName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasColumn($tableName, $columnName)
+    {
+        $adapterTableName = $this->getAdapterTableName($tableName);
+        return parent::hasColumn($adapterTableName, $columnName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addColumn(Table $table, Column $column)
+    {
+        $adapterTable = clone $table;
+        $adapterTableName = $this->getAdapterTableName($table->getName());
+        $adapterTable->setName($adapterTableName);
+        return parent::addColumn($adapterTable, $column);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function renameColumn($tableName, $columnName, $newColumnName)
+    {
+        $adapterTableName = $this->getAdapterTableName($tableName);
+        return parent::renameColumn($adapterTableName, $columnName, $newColumnName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function changeColumn($tableName, $columnName, Column $newColumn)
+    {
+        $adapterTableName = $this->getAdapterTableName($tableName);
+        return parent::changeColumn($adapterTableName, $columnName, $newColumn);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropColumn($tableName, $columnName)
+    {
+        $adapterTableName = $this->getAdapterTableName($tableName);
+        return parent::dropColumn($adapterTableName, $columnName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasIndex($tableName, $columns)
+    {
+        $adapterTableName = $this->getAdapterTableName($tableName);
+        return parent::hasIndex($adapterTableName, $columns);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasIndexByName($tableName, $indexName)
+    {
+        $adapterTableName = $this->getAdapterTableName($tableName);
+        return parent::hasIndexByName($adapterTableName, $indexName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addIndex(Table $table, Index $index)
+    {
+        $adapterTable = clone $table;
+        $adapterTableName = $this->getAdapterTableName($table->getName());
+        $adapterTable->setName($adapterTableName);
+        return parent::addIndex($adapterTable, $index);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropIndex($tableName, $columns, $options = array())
+    {
+        $adapterTableName = $this->getAdapterTableName($tableName);
+        return parent::dropIndex($adapterTableName, $columns, $options);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropIndexByName($tableName, $indexName)
+    {
+        $adapterTableName = $this->getAdapterTableName($tableName);
+        return parent::dropIndexByName($adapterTableName, $indexName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasForeignKey($tableName, $columns, $constraint = null)
+    {
+        $adapterTableName = $this->getAdapterTableName($tableName);
+        return parent::hasForeignKey($adapterTableName, $columns, $constraint);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addForeignKey(Table $table, ForeignKey $foreignKey)
+    {
+        $adapterTable = clone $table;
+        $adapterTableName = $this->getAdapterTableName($table->getName());
+        $adapterTable->setName($adapterTableName);
+        return parent::addForeignKey($adapterTable, $foreignKey);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropForeignKey($tableName, $columns, $constraint = null)
+    {
+        $adapterTableName = $this->getAdapterTableName($tableName);
+        return parent::dropForeignKey($adapterTableName, $columns, $constraint);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function insert(Table $table, $row)
+    {
+        $adapterTable = clone $table;
+        $adapterTableName = $this->getAdapterTableName($table->getName());
+        $adapterTable->setName($adapterTableName);
+        return parent::insert($adapterTable, $row);
+    }
+    
+    /**
+     * Gets the table prefix.
+     *
+     * @return string
+     */
+    public function getPrefix()
+    {
+        return (string) $this->getOption('table_prefix');
+    }
+
+    /**
+     * Gets the table suffix.
+     *
+     * @return string
+     */
+    public function getSuffix()
+    {
+        return (string) $this->getOption('table_suffix');
+    }
+
+    /**
+     * Applies the prefix and suffix to the table name.
+     *
+     * @param string $tableName
+     * @return string
+     */
+    public function getAdapterTableName($tableName)
+    {
+        return $this->getPrefix() . $tableName . $this->getSuffix();
+    }
+}
diff --git a/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/WrapperInterface.php b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/WrapperInterface.php
new file mode 100755
index 0000000..b8181d4
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Adapter/WrapperInterface.php
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Phinx
+ *
+ * (The MIT license)
+ * Copyright (c) 2015 Rob Morgan
+ *
+ * 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.
+ *
+ * @package    Phinx
+ * @subpackage Phinx\Db\Adapter
+ */
+namespace Phinx\Db\Adapter;
+
+/**
+ * Wrapper Interface.
+ *
+ * @author Woody Gilk <woody.gilk@gmail.com>
+ */
+interface WrapperInterface
+{
+    /**
+     * Class constructor, must always wrap another adapter.
+     *
+     * @param  AdapterInterface $adapter
+     */
+    public function __construct(AdapterInterface $adapter);
+
+    /**
+     * Sets the database adapter to proxy commands to.
+     *
+     * @param  AdapterInterface $adapter
+     * @return AdapterInterface
+     */
+    public function setAdapter(AdapterInterface $adapter);
+
+    /**
+     * Gets the database adapter.
+     *
+     * @throws \RuntimeException if the adapter has not been set
+     * @return AdapterInterface
+     */
+    public function getAdapter();
+}
diff --git a/vendor/topthink/think-migration/phinx/src/Phinx/Db/Table.php b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Table.php
new file mode 100755
index 0000000..b0e7202
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Table.php
@@ -0,0 +1,674 @@
+<?php
+/**
+ * Phinx
+ *
+ * (The MIT license)
+ * Copyright (c) 2015 Rob Morgan
+ *
+ * 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.
+ *
+ * @package    Phinx
+ * @subpackage Phinx\Db
+ */
+namespace Phinx\Db;
+
+use Phinx\Db\Table\Column;
+use Phinx\Db\Table\Index;
+use Phinx\Db\Table\ForeignKey;
+use Phinx\Db\Adapter\AdapterInterface;
+
+/**
+ *
+ * This object is based loosely on: http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html.
+ */
+class Table
+{
+    /**
+     * @var string
+     */
+    protected $name;
+
+    /**
+     * @var array
+     */
+    protected $options = array();
+
+    /**
+     * @var AdapterInterface
+     */
+    protected $adapter;
+
+    /**
+     * @var array
+     */
+    protected $columns = array();
+
+    /**
+     * @var array
+     */
+    protected $indexes = array();
+
+    /**
+     * @var ForeignKey[]
+     */
+    protected $foreignKeys = array();
+
+    /**
+     * @var array
+     */
+    protected $data = array();
+
+    /**
+     * Class Constuctor.
+     *
+     * @param string $name Table Name
+     * @param array $options Options
+     * @param AdapterInterface $adapter Database Adapter
+     */
+    public function __construct($name, $options = array(), AdapterInterface $adapter = null)
+    {
+        $this->setName($name);
+        $this->setOptions($options);
+
+        if (null !== $adapter) {
+            $this->setAdapter($adapter);
+        }
+    }
+
+    /**
+     * Sets the table name.
+     *
+     * @param string $name Table Name
+     * @return Table
+     */
+    public function setName($name)
+    {
+        $this->name = $name;
+        return $this;
+    }
+
+    /**
+     * Gets the table name.
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * Sets the table options.
+     *
+     * @param array $options
+     * @return Table
+     */
+    public function setOptions($options)
+    {
+        $this->options = $options;
+        return $this;
+    }
+
+    /**
+     * Gets the table options.
+     *
+     * @return array
+     */
+    public function getOptions()
+    {
+        return $this->options;
+    }
+
+    /**
+     * Sets the database adapter.
+     *
+     * @param AdapterInterface $adapter Database Adapter
+     * @return Table
+     */
+    public function setAdapter(AdapterInterface $adapter)
+    {
+        $this->adapter = $adapter;
+        return $this;
+    }
+
+    /**
+     * Gets the database adapter.
+     *
+     * @return AdapterInterface
+     */
+    public function getAdapter()
+    {
+        return $this->adapter;
+    }
+
+    /**
+     * Does the table exist?
+     *
+     * @return boolean
+     */
+    public function exists()
+    {
+        return $this->getAdapter()->hasTable($this->getName());
+    }
+
+    /**
+     * Drops the database table.
+     *
+     * @return void
+     */
+    public function drop()
+    {
+        $this->getAdapter()->dropTable($this->getName());
+    }
+
+    /**
+     * Renames the database table.
+     *
+     * @param string $newTableName New Table Name
+     * @return Table
+     */
+    public function rename($newTableName)
+    {
+        $this->getAdapter()->renameTable($this->getName(), $newTableName);
+        $this->setName($newTableName);
+        return $this;
+    }
+
+    /**
+     * Sets an array of columns waiting to be committed.
+     * Use setPendingColumns
+     *
+     * @deprecated
+     * @param array $columns Columns
+     * @return Table
+     */
+    public function setColumns($columns)
+    {
+        $this->setPendingColumns($columns);
+    }
+
+    /**
+     * Gets an array of the table columns.
+     *
+     * @return Column[]
+     */
+    public function getColumns()
+    {
+        return $this->getAdapter()->getColumns($this->getName());
+    }
+
+    /**
+     * Sets an array of columns waiting to be committed.
+     *
+     * @param array $columns Columns
+     * @return Table
+     */
+    public function setPendingColumns($columns)
+    {
+        $this->columns = $columns;
+        return $this;
+    }
+
+    /**
+     * Gets an array of columns waiting to be committed.
+     *
+     * @return Column[]
+     */
+    public function getPendingColumns()
+    {
+        return $this->columns;
+    }
+
+    /**
+     * Sets an array of columns waiting to be indexed.
+     *
+     * @param array $indexes Indexes
+     * @return Table
+     */
+    public function setIndexes($indexes)
+    {
+        $this->indexes = $indexes;
+        return $this;
+    }
+
+    /**
+     * Gets an array of indexes waiting to be committed.
+     *
+     * @return array
+     */
+    public function getIndexes()
+    {
+        return $this->indexes;
+    }
+
+    /**
+     * Sets an array of foreign keys waiting to be commited.
+     *
+     * @param ForeignKey[] $foreignKeys foreign keys
+     * @return Table
+     */
+    public function setForeignKeys($foreignKeys)
+    {
+        $this->foreignKeys = $foreignKeys;
+        return $this;
+    }
+
+    /**
+     * Gets an array of foreign keys waiting to be commited.
+     *
+     * @return array|ForeignKey[]
+     */
+    public function getForeignKeys()
+    {
+        return $this->foreignKeys;
+    }
+
+    /**
+     * Sets an array of data to be inserted.
+     *
+     * @param array $data Data
+     * @return Table
+     */
+    public function setData($data)
+    {
+        $this->data = $data;
+        return $this;
+    }
+
+    /**
+     * Gets the data waiting to be inserted.
+     *
+     * @return array
+     */
+    public function getData()
+    {
+        return $this->data;
+    }
+
+    /**
+     * Resets all of the pending table changes.
+     *
+     * @return void
+     */
+    public function reset()
+    {
+        $this->setPendingColumns(array());
+        $this->setIndexes(array());
+        $this->setForeignKeys(array());
+        $this->setData(array());
+    }
+
+    /**
+     * Add a table column.
+     *
+     * Type can be: string, text, integer, float, decimal, datetime, timestamp,
+     * time, date, binary, boolean.
+     *
+     * Valid options can be: limit, default, null, precision or scale.
+     *
+     * @param string|Column $columnName Column Name
+     * @param string $type Column Type
+     * @param array $options Column Options
+     * @throws \RuntimeException
+     * @throws \InvalidArgumentException
+     * @return Table
+     */
+    public function addColumn($columnName, $type = null, $options = array())
+    {
+        // we need an adapter set to add a column
+        if (null === $this->getAdapter()) {
+            throw new \RuntimeException('An adapter must be specified to add a column.');
+        }
+
+        // create a new column object if only strings were supplied
+        if (!$columnName instanceof Column) {
+            $column = new Column();
+            $column->setName($columnName);
+            $column->setType($type);
+            $column->setOptions($options); // map options to column methods
+        } else {
+            $column = $columnName;
+        }
+
+        // Delegate to Adapters to check column type
+        if (!$this->getAdapter()->isValidColumnType($column)) {
+            throw new \InvalidArgumentException(sprintf(
+                'An invalid column type "%s" was specified for column "%s".',
+                $column->getType(),
+                $column->getName()
+            ));
+        }
+
+        $this->columns[] = $column;
+        return $this;
+    }
+
+    /**
+     * Remove a table column.
+     *
+     * @param string $columnName Column Name
+     * @return Table
+     */
+    public function removeColumn($columnName)
+    {
+        $this->getAdapter()->dropColumn($this->getName(), $columnName);
+        return $this;
+    }
+
+    /**
+     * Rename a table column.
+     *
+     * @param string $oldName Old Column Name
+     * @param string $newName New Column Name
+     * @return Table
+     */
+    public function renameColumn($oldName, $newName)
+    {
+        $this->getAdapter()->renameColumn($this->getName(), $oldName, $newName);
+        return $this;
+    }
+
+    /**
+     * Change a table column type.
+     *
+     * @param string        $columnName    Column Name
+     * @param string|Column $newColumnType New Column Type
+     * @param array         $options       Options
+     * @return Table
+     */
+    public function changeColumn($columnName, $newColumnType, $options = array())
+    {
+        // create a column object if one wasn't supplied
+        if (!$newColumnType instanceof Column) {
+            $newColumn = new Column();
+            $newColumn->setType($newColumnType);
+            $newColumn->setOptions($options);
+        } else {
+            $newColumn = $newColumnType;
+        }
+
+        // if the name was omitted use the existing column name
+        if (null === $newColumn->getName() || strlen($newColumn->getName()) === 0) {
+            $newColumn->setName($columnName);
+        }
+
+        $this->getAdapter()->changeColumn($this->getName(), $columnName, $newColumn);
+        return $this;
+    }
+
+    /**
+     * Checks to see if a column exists.
+     *
+     * @param string $columnName Column Name
+     * @param array $options Options
+     * @return boolean
+     */
+    public function hasColumn($columnName, $options = array())
+    {
+        return $this->getAdapter()->hasColumn($this->getName(), $columnName, $options);
+    }
+
+    /**
+     * Add an index to a database table.
+     *
+     * In $options you can specific unique = true/false or name (index name).
+     *
+     * @param string|array|Index $columns Table Column(s)
+     * @param array $options Index Options
+     * @return Table
+     */
+    public function addIndex($columns, $options = array())
+    {
+        // create a new index object if strings or an array of strings were supplied
+        if (!$columns instanceof Index) {
+            $index = new Index();
+            if (is_string($columns)) {
+                $columns = array($columns); // str to array
+            }
+            $index->setColumns($columns);
+            $index->setOptions($options);
+        } else {
+            $index = $columns;
+        }
+
+        $this->indexes[] = $index;
+        return $this;
+    }
+
+    /**
+     * Removes the given index from a table.
+     *
+     * @param array $columns Columns
+     * @param array $options Options
+     * @return Table
+     */
+    public function removeIndex($columns, $options = array())
+    {
+        $this->getAdapter()->dropIndex($this->getName(), $columns, $options);
+        return $this;
+    }
+
+    /**
+     * Removes the given index identified by its name from a table.
+     *
+     * @param string $name Index name
+     * @return Table
+     */
+    public function removeIndexByName($name)
+    {
+        $this->getAdapter()->dropIndexByName($this->getName(), $name);
+        return $this;
+    }
+
+    /**
+     * Checks to see if an index exists.
+     *
+     * @param string|array $columns Columns
+     * @param array        $options Options
+     * @return boolean
+     */
+    public function hasIndex($columns, $options = array())
+    {
+        return $this->getAdapter()->hasIndex($this->getName(), $columns, $options);
+    }
+
+    /**
+     * Add a foreign key to a database table.
+     *
+     * In $options you can specify on_delete|on_delete = cascade|no_action ..,
+     * on_update, constraint = constraint name.
+     *
+     * @param string|array $columns Columns
+     * @param string|Table $referencedTable   Referenced Table
+     * @param string|array $referencedColumns Referenced Columns
+     * @param array $options Options
+     * @return Table
+     */
+    public function addForeignKey($columns, $referencedTable, $referencedColumns = array('id'), $options = array())
+    {
+        if (is_string($referencedColumns)) {
+            $referencedColumns = array($referencedColumns); // str to array
+        }
+        $fk = new ForeignKey();
+        if ($referencedTable instanceof Table) {
+            $fk->setReferencedTable($referencedTable);
+        } else {
+            $fk->setReferencedTable(new Table($referencedTable, array(), $this->adapter));
+        }
+        $fk->setColumns($columns)
+           ->setReferencedColumns($referencedColumns)
+           ->setOptions($options);
+        $this->foreignKeys[] = $fk;
+
+        return $this;
+    }
+
+    /**
+     * Removes the given foreign key from the table.
+     *
+     * @param string|array $columns    Column(s)
+     * @param null|string  $constraint Constraint names
+     * @return Table
+     */
+    public function dropForeignKey($columns, $constraint = null)
+    {
+        if (is_string($columns)) {
+            $columns = array($columns);
+        }
+        if ($constraint) {
+            $this->getAdapter()->dropForeignKey($this->getName(), array(), $constraint);
+        } else {
+            $this->getAdapter()->dropForeignKey($this->getName(), $columns);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Checks to see if a foreign key exists.
+     *
+     * @param  string|array $columns    Column(s)
+     * @param  null|string  $constraint Constraint names
+     * @return boolean
+     */
+    public function hasForeignKey($columns, $constraint = null)
+    {
+        return $this->getAdapter()->hasForeignKey($this->getName(), $columns, $constraint);
+    }
+
+    /**
+     * Add timestamp columns created_at and updated_at to the table.
+     *
+     * @param string $createdAtColumnName
+     * @param string $updatedAtColumnName
+     *
+     * @return Table
+     */
+    public function addTimestamps($createdAtColumnName = 'created_at', $updatedAtColumnName = 'updated_at')
+    {
+        $createdAtColumnName = is_null($createdAtColumnName) ? 'created_at' : $createdAtColumnName;
+        $updatedAtColumnName = is_null($updatedAtColumnName) ? 'updated_at' : $updatedAtColumnName;
+        $this->addColumn($createdAtColumnName, 'timestamp', array(
+                'default' => 'CURRENT_TIMESTAMP',
+                'update' => ''
+            ))
+             ->addColumn($updatedAtColumnName, 'timestamp', array(
+                'null'    => true,
+                'default' => null
+             ));
+
+        return $this;
+    }
+
+    /**
+     * Insert data into the table.
+     *
+     * @param $data array of data in the form:
+     *              array(
+     *                  array("col1" => "value1", "col2" => "anotherValue1"),
+     *                  array("col2" => "value2", "col2" => "anotherValue2"),
+     *              )
+     *              or array("col1" => "value1", "col2" => "anotherValue1")
+     *
+     * @return Table
+     */
+    public function insert($data)
+    {
+        // handle array of array situations
+        if (isset($data[0]) && is_array($data[0])) {
+            foreach ($data as $row) {
+                $this->data[] = $row;
+            }
+            return $this;
+        }
+        $this->data[] = $data;
+        return $this;
+    }
+
+    /**
+     * Creates a table from the object instance.
+     *
+     * @return void
+     */
+    public function create()
+    {
+        $this->getAdapter()->createTable($this);
+        $this->saveData();
+        $this->reset(); // reset pending changes
+    }
+
+    /**
+     * Updates a table from the object instance.
+     *
+     * @throws \RuntimeException
+     * @return void
+     */
+    public function update()
+    {
+        if (!$this->exists()) {
+            throw new \RuntimeException('Cannot update a table that doesn\'t exist!');
+        }
+
+        // update table
+        foreach ($this->getPendingColumns() as $column) {
+            $this->getAdapter()->addColumn($this, $column);
+        }
+
+        foreach ($this->getIndexes() as $index) {
+            $this->getAdapter()->addIndex($this, $index);
+        }
+
+        foreach ($this->getForeignKeys() as $foreignKey) {
+            $this->getAdapter()->addForeignKey($this, $foreignKey);
+        }
+
+        $this->saveData();
+        $this->reset(); // reset pending changes
+    }
+
+    /**
+     * Commit the pending data waiting for insertion.
+     *
+     * @return void
+     */
+    public function saveData()
+    {
+        foreach ($this->getData() as $row) {
+            $this->getAdapter()->insert($this, $row);
+        }
+    }
+
+    /**
+     * Commits the table changes.
+     *
+     * If the table doesn't exist it is created otherwise it is updated.
+     *
+     * @return void
+     */
+    public function save()
+    {
+        if ($this->exists()) {
+            $this->update(); // update the table
+        } else {
+            $this->create(); // create the table
+        }
+
+        $this->reset(); // reset pending changes
+    }
+}
diff --git a/vendor/topthink/think-migration/phinx/src/Phinx/Db/Table/Column.php b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Table/Column.php
new file mode 100755
index 0000000..874341e
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Table/Column.php
@@ -0,0 +1,550 @@
+<?php
+/**
+ * Phinx
+ *
+ * (The MIT license)
+ * Copyright (c) 2015 Rob Morgan
+ *
+ * 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.
+ *
+ * @package    Phinx
+ * @subpackage Phinx\Db
+ */
+namespace Phinx\Db\Table;
+
+/**
+ *
+ * This object is based loosely on: http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html.
+ */
+class Column
+{
+    /**
+     * @var string
+     */
+    protected $name;
+
+    /**
+     * @var string
+     */
+    protected $type;
+
+    /**
+     * @var integer
+     */
+    protected $limit = null;
+
+    /**
+     * @var boolean
+     */
+    protected $null = false;
+
+    /**
+     * @var mixed
+     */
+    protected $default = null;
+
+    /**
+     * @var boolean
+     */
+    protected $identity = false;
+
+    /**
+     * @var integer
+     */
+    protected $precision;
+
+    /**
+     * @var integer
+     */
+    protected $scale;
+
+    /**
+     * @var string
+     */
+    protected $after;
+
+    /**
+     * @var string
+     */
+    protected $update;
+
+    /**
+     * @var string
+     */
+    protected $comment;
+
+    /**
+     * @var boolean
+     */
+    protected $signed = true;
+
+    /**
+     * @var boolean
+     */
+    protected $timezone = false;
+
+    /**
+     * @var array
+     */
+    protected $properties = array();
+
+    /**
+     * @var array
+     */
+    protected $values;
+
+    /**
+     * Sets the column name.
+     *
+     * @param string $name
+     * @return $this
+     */
+    public function setName($name)
+    {
+        $this->name = $name;
+        return $this;
+    }
+
+    /**
+     * Gets the column name.
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * Sets the column type.
+     *
+     * @param string $type
+     * @return $this
+     */
+    public function setType($type)
+    {
+        $this->type = $type;
+        return $this;
+    }
+
+    /**
+     * Gets the column type.
+     *
+     * @return string
+     */
+    public function getType()
+    {
+        return $this->type;
+    }
+
+    /**
+     * Sets the column limit.
+     *
+     * @param integer $limit
+     * @return $this
+     */
+    public function setLimit($limit)
+    {
+        $this->limit = $limit;
+        return $this;
+    }
+
+    /**
+     * Gets the column limit.
+     *
+     * @return integer
+     */
+    public function getLimit()
+    {
+        return $this->limit;
+    }
+
+    /**
+     * Sets whether the column allows nulls.
+     *
+     * @param boolean $null
+     * @return $this
+     */
+    public function setNull($null)
+    {
+        $this->null = (bool) $null;
+        return $this;
+    }
+
+    /**
+     * Gets whether the column allows nulls.
+     *
+     * @return boolean
+     */
+    public function getNull()
+    {
+        return $this->null;
+    }
+
+    /**
+     * Does the column allow nulls?
+     *
+     * @return boolean
+     */
+    public function isNull()
+    {
+        return $this->getNull();
+    }
+
+    /**
+     * Sets the default column value.
+     *
+     * @param mixed $default
+     * @return $this
+     */
+    public function setDefault($default)
+    {
+        $this->default = $default;
+        return $this;
+    }
+
+    /**
+     * Gets the default column value.
+     *
+     * @return mixed
+     */
+    public function getDefault()
+    {
+        return $this->default;
+    }
+
+    /**
+     * Sets whether or not the column is an identity column.
+     *
+     * @param boolean $identity
+     * @return $this
+     */
+    public function setIdentity($identity)
+    {
+        $this->identity = $identity;
+        return $this;
+    }
+
+    /**
+     * Gets whether or not the column is an identity column.
+     *
+     * @return boolean
+     */
+    public function getIdentity()
+    {
+        return $this->identity;
+    }
+
+    /**
+     * Is the column an identity column?
+     *
+     * @return boolean
+     */
+    public function isIdentity()
+    {
+        return $this->getIdentity();
+    }
+
+    /**
+     * Sets the name of the column to add this column after.
+     *
+     * @param string $after After
+     * @return $this
+     */
+    public function setAfter($after)
+    {
+        $this->after = $after;
+        return $this;
+    }
+
+    /**
+     * Returns the name of the column to add this column after.
+     *
+     * @return string
+     */
+    public function getAfter()
+    {
+        return $this->after;
+    }
+
+    /**
+     * Sets the 'ON UPDATE' mysql column function.
+     *
+     * @param  string $update On Update function
+     * @return $this
+     */
+    public function setUpdate($update)
+    {
+        $this->update = $update;
+        return $this;
+    }
+
+    /**
+     * Returns the value of the ON UPDATE column function.
+     *
+     * @return string
+     */
+    public function getUpdate()
+    {
+        return $this->update;
+    }
+
+    /**
+     * Sets the column precision for decimal.
+     *
+     * @param integer $precision
+     * @return $this
+     */
+    public function setPrecision($precision)
+    {
+        $this->precision = $precision;
+        return $this;
+    }
+
+    /**
+     * Gets the column precision for decimal.
+     *
+     * @return integer
+     */
+    public function getPrecision()
+    {
+        return $this->precision;
+    }
+
+    /**
+     * Sets the column scale for decimal.
+     *
+     * @param integer $scale
+     * @return $this
+     */
+    public function setScale($scale)
+    {
+        $this->scale = $scale;
+        return $this;
+    }
+
+    /**
+     * Gets the column scale for decimal.
+     *
+     * @return integer
+     */
+    public function getScale()
+    {
+        return $this->scale;
+    }
+
+    /**
+     * Sets the column comment.
+     *
+     * @param string $comment
+     * @return $this
+     */
+    public function setComment($comment)
+    {
+        $this->comment = $comment;
+        return $this;
+    }
+
+    /**
+     * Gets the column comment.
+     *
+     * @return string
+     */
+    public function getComment()
+    {
+        return $this->comment;
+    }
+
+    /**
+     * Sets whether field should be signed.
+     *
+     * @param bool $signed
+     * @return $this
+     */
+    public function setSigned($signed)
+    {
+        $this->signed = (bool) $signed;
+        return $this;
+    }
+
+    /**
+     * Gets whether field should be signed.
+     *
+     * @return string
+     */
+    public function getSigned()
+    {
+        return $this->signed;
+    }
+
+    /**
+     * Should the column be signed?
+     *
+     * @return boolean
+     */
+    public function isSigned()
+    {
+        return $this->getSigned();
+    }
+
+    /**
+     * Sets whether the field should have a timezone identifier.
+     * Used for date/time columns only!
+     *
+     * @param bool $timezone
+     * @return $this
+     */
+    public function setTimezone($timezone)
+    {
+        $this->timezone = (bool) $timezone;
+        return $this;
+    }
+
+    /**
+     * Gets whether field has a timezone identifier.
+     *
+     * @return boolean
+     */
+    public function getTimezone()
+    {
+        return $this->timezone;
+    }
+
+    /**
+     * Should the column have a timezone?
+     *
+     * @return boolean
+     */
+    public function isTimezone()
+    {
+        return $this->getTimezone();
+    }
+
+    /**
+     * Sets field properties.
+     *
+     * @param array $properties
+     *
+     * @return $this
+     */
+    public function setProperties($properties)
+    {
+        $this->properties = $properties;
+        return $this;
+    }
+
+    /**
+     * Gets field properties
+     *
+     * @return array
+     */
+    public function getProperties()
+    {
+        return $this->properties;
+    }
+
+    /**
+     * Sets field values.
+     *
+     * @param mixed (array|string) $values
+     *
+     * @return $this
+     */
+    public function setValues($values)
+    {
+        if (!is_array($values)) {
+            $values = preg_split('/,\s*/', $values);
+        }
+        $this->values = $values;
+        return $this;
+    }
+
+    /**
+     * Gets field values
+     *
+     * @return string
+     */
+    public function getValues()
+    {
+        return $this->values;
+    }
+
+    /**
+     * Gets all allowed options. Each option must have a corresponding `setFoo` method.
+     *
+     * @return array
+     */
+    protected function getValidOptions()
+    {
+        return array(
+            'limit',
+            'default',
+            'null',
+            'identity',
+            'precision',
+            'scale',
+            'after',
+            'update',
+            'comment',
+            'signed',
+            'timezone',
+            'properties',
+            'values',
+        );
+    }
+
+    /**
+     * Gets all aliased options. Each alias must reference a valid option.
+     *
+     * @return array
+     */
+    protected function getAliasedOptions()
+    {
+        return array(
+            'length' => 'limit',
+        );
+    }
+
+    /**
+     * Utility method that maps an array of column options to this objects methods.
+     *
+     * @param array $options Options
+     * @return $this
+     */
+    public function setOptions($options)
+    {
+        $validOptions = $this->getValidOptions();
+        $aliasOptions = $this->getAliasedOptions();
+
+        foreach ($options as $option => $value) {
+            if (isset($aliasOptions[$option])) {
+                // proxy alias -> option
+                $option = $aliasOptions[$option];
+            }
+
+            if (!in_array($option, $validOptions, true)) {
+                throw new \RuntimeException(sprintf('"%s" is not a valid column option.', $option));
+            }
+
+            $method = 'set' . ucfirst($option);
+            $this->$method($value);
+        }
+        return $this;
+    }
+}
diff --git a/vendor/topthink/think-migration/phinx/src/Phinx/Db/Table/ForeignKey.php b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Table/ForeignKey.php
new file mode 100755
index 0000000..3033503
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Table/ForeignKey.php
@@ -0,0 +1,252 @@
+<?php
+/**
+ * Phinx
+ *
+ * (The MIT license)
+ * Copyright (c) 2015 Rob Morgan
+ *
+ * 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.
+ *
+ * @package    Phinx
+ * @subpackage Phinx\Db
+ * @author     Leonid Kuzmin <lndkuzmin@gmail.com>
+ */
+namespace Phinx\Db\Table;
+
+use Phinx\Db\Table;
+
+class ForeignKey
+{
+    const CASCADE = 'CASCADE';
+    const RESTRICT = 'RESTRICT';
+    const SET_NULL = 'SET NULL';
+    const NO_ACTION = 'NO ACTION';
+
+    /**
+     * @var array
+     */
+    protected $columns = array();
+
+    /**
+     * @var Table
+     */
+    protected $referencedTable;
+
+    /**
+     * @var array
+     */
+    protected $referencedColumns = array();
+
+    /**
+     * @var string
+     */
+    protected $onDelete;
+
+    /**
+     * @var string
+     */
+    protected $onUpdate;
+
+    /**
+     * @var string|boolean
+     */
+    protected $constraint;
+
+    /**
+     * Sets the foreign key columns.
+     *
+     * @param array|string $columns
+     * @return ForeignKey
+     */
+    public function setColumns($columns)
+    {
+        if (is_string($columns)) {
+            $columns = array($columns);
+        }
+        $this->columns = $columns;
+        return $this;
+    }
+
+    /**
+     * Gets the foreign key columns.
+     *
+     * @return array
+     */
+    public function getColumns()
+    {
+        return $this->columns;
+    }
+
+    /**
+     * Sets the foreign key referenced table.
+     *
+     * @param Table $table
+     * @return ForeignKey
+     */
+    public function setReferencedTable(Table $table)
+    {
+        $this->referencedTable = $table;
+        return $this;
+    }
+
+    /**
+     * Gets the foreign key referenced table.
+     *
+     * @return Table
+     */
+    public function getReferencedTable()
+    {
+        return $this->referencedTable;
+    }
+
+    /**
+     * Sets the foreign key referenced columns.
+     *
+     * @param array $referencedColumns
+     * @return ForeignKey
+     */
+    public function setReferencedColumns(array $referencedColumns)
+    {
+        $this->referencedColumns = $referencedColumns;
+        return $this;
+    }
+
+    /**
+     * Gets the foreign key referenced columns.
+     *
+     * @return array
+     */
+    public function getReferencedColumns()
+    {
+        return $this->referencedColumns;
+    }
+
+    /**
+     * Sets ON DELETE action for the foreign key.
+     *
+     * @param string $onDelete
+     * @return ForeignKey
+     */
+    public function setOnDelete($onDelete)
+    {
+        $this->onDelete = $this->normalizeAction($onDelete);
+        return $this;
+    }
+
+    /**
+     * Gets ON DELETE action for the foreign key.
+     *
+     * @return string
+     */
+    public function getOnDelete()
+    {
+        return $this->onDelete;
+    }
+
+    /**
+     * Gets ON UPDATE action for the foreign key.
+     *
+     * @return string
+     */
+    public function getOnUpdate()
+    {
+        return $this->onUpdate;
+    }
+
+    /**
+     * Sets ON UPDATE action for the foreign key.
+     *
+     * @param string $onUpdate
+     * @return ForeignKey
+     */
+    public function setOnUpdate($onUpdate)
+    {
+        $this->onUpdate = $this->normalizeAction($onUpdate);
+        return $this;
+    }
+
+    /**
+     * Sets constraint for the foreign key.
+     *
+     * @param string $constraint
+     * @return ForeignKey
+     */
+    public function setConstraint($constraint)
+    {
+        $this->constraint = $constraint;
+        return $this;
+    }
+
+    /**
+     * Gets constraint name for the foreign key.
+     *
+     * @return string
+     */
+    public function getConstraint()
+    {
+        return $this->constraint;
+    }
+
+    /**
+     * Utility method that maps an array of index options to this objects methods.
+     *
+     * @param array $options Options
+     * @throws \RuntimeException
+     * @throws \InvalidArgumentException
+     * @return ForeignKey
+     */
+    public function setOptions($options)
+    {
+        // Valid Options
+        $validOptions = array('delete', 'update', 'constraint');
+        foreach ($options as $option => $value) {
+            if (!in_array($option, $validOptions, true)) {
+                throw new \RuntimeException(sprintf('"%s" is not a valid foreign key option.', $option));
+            }
+
+            // handle $options['delete'] as $options['update']
+            if ('delete' === $option) {
+                $this->setOnDelete($value);
+            } elseif ('update' === $option) {
+                $this->setOnUpdate($value);
+            } else {
+                $method = 'set' . ucfirst($option);
+                $this->$method($value);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * From passed value checks if it's correct and fixes if needed
+     *
+     * @param string $action
+     * @throws \InvalidArgumentException
+     * @return string
+     */
+    protected function normalizeAction($action)
+    {
+        $constantName = 'static::' . str_replace(' ', '_', strtoupper(trim($action)));
+        if (!defined($constantName)) {
+            throw new \InvalidArgumentException('Unknown action passed: ' . $action);
+        }
+        return constant($constantName);
+    }
+}
diff --git a/vendor/topthink/think-migration/phinx/src/Phinx/Db/Table/Index.php b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Table/Index.php
new file mode 100755
index 0000000..82404cb
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/src/Phinx/Db/Table/Index.php
@@ -0,0 +1,185 @@
+<?php
+/**
+ * Phinx
+ *
+ * (The MIT license)
+ * Copyright (c) 2015 Rob Morgan
+ *
+ * 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.
+ *
+ * @package    Phinx
+ * @subpackage Phinx\Db
+ */
+namespace Phinx\Db\Table;
+
+class Index
+{
+    /**
+     * @var string
+     */
+    const UNIQUE = 'unique';
+
+    /**
+     * @var string
+     */
+    const INDEX = 'index';
+
+    /**
+     * @var string
+     */
+    const FULLTEXT = 'fulltext';
+
+    /**
+     * @var array
+     */
+    protected $columns;
+
+    /**
+     * @var string
+     */
+    protected $type = self::INDEX;
+
+    /**
+     * @var string
+     */
+    protected $name = null;
+
+    /**
+     * @var integer
+     */
+    protected $limit = null;
+
+    /**
+     * Sets the index columns.
+     *
+     * @param array $columns
+     * @return Index
+     */
+    public function setColumns($columns)
+    {
+        $this->columns = $columns;
+        return $this;
+    }
+
+    /**
+     * Gets the index columns.
+     *
+     * @return array
+     */
+    public function getColumns()
+    {
+        return $this->columns;
+    }
+
+    /**
+     * Sets the index type.
+     *
+     * @param string $type
+     * @return Index
+     */
+    public function setType($type)
+    {
+        $this->type = $type;
+        return $this;
+    }
+
+    /**
+     * Gets the index type.
+     *
+     * @return string
+     */
+    public function getType()
+    {
+        return $this->type;
+    }
+
+    /**
+     * Sets the index name.
+     *
+     * @param string $name
+     * @return Index
+     */
+    public function setName($name)
+    {
+        $this->name = $name;
+        return $this;
+    }
+
+    /**
+     * Gets the index name.
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * Sets the index limit.
+     *
+     * @param integer $limit
+     * @return Index
+     */
+    public function setLimit($limit)
+    {
+        $this->limit = $limit;
+        return $this;
+    }
+
+    /**
+     * Gets the index limit.
+     *
+     * @return integer
+     */
+    public function getLimit()
+    {
+        return $this->limit;
+    }
+
+    /**
+     * Utility method that maps an array of index options to this objects methods.
+     *
+     * @param array $options Options
+     * @throws \RuntimeException
+     * @return Index
+     */
+    public function setOptions($options)
+    {
+        // Valid Options
+        $validOptions = array('type', 'unique', 'name', 'limit');
+        foreach ($options as $option => $value) {
+            if (!in_array($option, $validOptions, true)) {
+                throw new \RuntimeException(sprintf('"%s" is not a valid index option.', $option));
+            }
+
+            // handle $options['unique']
+            if (strcasecmp($option, self::UNIQUE) === 0) {
+                if ((bool) $value) {
+                    $this->setType(self::UNIQUE);
+                }
+                continue;
+            }
+
+            $method = 'set' . ucfirst($option);
+            $this->$method($value);
+        }
+        return $this;
+    }
+}
diff --git a/vendor/topthink/think-migration/phinx/src/Phinx/Migration/AbstractMigration.php b/vendor/topthink/think-migration/phinx/src/Phinx/Migration/AbstractMigration.php
new file mode 100755
index 0000000..1598cfb
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/src/Phinx/Migration/AbstractMigration.php
@@ -0,0 +1,273 @@
+<?php
+/**
+ * Phinx
+ *
+ * (The MIT license)
+ * Copyright (c) 2015 Rob Morgan
+ *
+ * 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.
+ *
+ * @package    Phinx
+ * @subpackage Phinx\Migration
+ */
+namespace Phinx\Migration;
+
+use Phinx\Db\Table;
+use Phinx\Db\Adapter\AdapterInterface;
+use think\console\Input as InputInterface;
+use think\console\Output as OutputInterface;
+
+/**
+ * Abstract Migration Class.
+ *
+ * It is expected that the migrations you write extend from this class.
+ *
+ * This abstract class proxies the various database methods to your specified
+ * adapter.
+ *
+ * @author Rob Morgan <robbym@gmail.com>
+ */
+abstract class AbstractMigration implements MigrationInterface
+{
+    /**
+     * @var float
+     */
+    protected $version;
+
+    /**
+     * @var AdapterInterface
+     */
+    protected $adapter;
+
+    /**
+     * @var OutputInterface
+     */
+    protected $output;
+
+    /**
+     * @var InputInterface
+     */
+    protected $input;
+
+    /**
+     * Class Constructor.
+     *
+     * @param int $version Migration Version
+     * @param InputInterface|null $input
+     * @param OutputInterface|null $output
+     */
+    final public function __construct($version, InputInterface $input = null, OutputInterface $output = null)
+    {
+        $this->version = $version;
+        if (!is_null($input)){
+            $this->setInput($input);
+        }
+        if (!is_null($output)){
+            $this->setOutput($output);
+        }
+
+        $this->init();
+    }
+
+    /**
+     * Initialize method.
+     *
+     * @return void
+     */
+    protected function init()
+    {
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function up()
+    {
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function down()
+    {
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setAdapter(AdapterInterface $adapter)
+    {
+        $this->adapter = $adapter;
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getAdapter()
+    {
+        return $this->adapter;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setInput(InputInterface $input)
+    {
+        $this->input = $input;
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getInput()
+    {
+        return $this->input;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setOutput(OutputInterface $output)
+    {
+        $this->output = $output;
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getOutput()
+    {
+        return $this->output;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getName()
+    {
+        return get_class($this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setVersion($version)
+    {
+        $this->version = $version;
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getVersion()
+    {
+        return $this->version;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function execute($sql)
+    {
+        return $this->getAdapter()->execute($sql);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function query($sql)
+    {
+        return $this->getAdapter()->query($sql);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function fetchRow($sql)
+    {
+        return $this->getAdapter()->fetchRow($sql);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function fetchAll($sql)
+    {
+        return $this->getAdapter()->fetchAll($sql);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function insert($table, $data)
+    {
+        // convert to table object
+        if (is_string($table)) {
+            $table = new Table($table, array(), $this->getAdapter());
+        }
+        return $table->insert($data)->save();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createDatabase($name, $options)
+    {
+        $this->getAdapter()->createDatabase($name, $options);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dropDatabase($name)
+    {
+        $this->getAdapter()->dropDatabase($name);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasTable($tableName)
+    {
+        return $this->getAdapter()->hasTable($tableName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function table($tableName, $options = array())
+    {
+        return new Table($tableName, $options, $this->getAdapter());
+    }
+
+    /**
+     * A short-hand method to drop the given database table.
+     *
+     * @param string $tableName Table Name
+     * @return void
+     */
+    public function dropTable($tableName)
+    {
+        $this->table($tableName)->drop();
+    }
+}
diff --git a/vendor/topthink/think-migration/phinx/src/Phinx/Migration/AbstractTemplateCreation.php b/vendor/topthink/think-migration/phinx/src/Phinx/Migration/AbstractTemplateCreation.php
new file mode 100755
index 0000000..589a5f7
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/src/Phinx/Migration/AbstractTemplateCreation.php
@@ -0,0 +1,97 @@
+<?php
+/**
+ * Phinx
+ *
+ * (The MIT license)
+ * Copyright (c) 2015 Rob Morgan
+ *
+ * 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.
+ *
+ * @package    Phinx
+ * @subpackage Phinx\Migration
+ */
+namespace Phinx\Migration;
+
+use think\console\Input as InputInterface;
+use think\console\Output as OutputInterface;
+
+abstract class AbstractTemplateCreation implements CreationInterface
+{
+    /**
+     * @var InputInterface
+     */
+    protected $input;
+
+    /**
+     * @var OutputInterface
+     */
+    protected $output;
+
+    /**
+     * Class Constructor.
+     *
+     * @param InputInterface|null  $input
+     * @param OutputInterface|null $output
+     */
+    public function __construct(InputInterface $input = null, OutputInterface $output = null)
+    {
+        if (!is_null($input)) {
+            $this->setInput($input);
+        }
+        if (!is_null($output)) {
+            $this->setOutput($output);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getInput()
+    {
+        return $this->input;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setInput(InputInterface $input)
+    {
+        $this->input = $input;
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getOutput()
+    {
+        return $this->output;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setOutput(OutputInterface $output)
+    {
+        $this->output = $output;
+
+        return $this;
+    }
+}
diff --git a/vendor/topthink/think-migration/phinx/src/Phinx/Migration/CreationInterface.php b/vendor/topthink/think-migration/phinx/src/Phinx/Migration/CreationInterface.php
new file mode 100755
index 0000000..b60c889
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/src/Phinx/Migration/CreationInterface.php
@@ -0,0 +1,94 @@
+<?php
+/**
+ * Phinx
+ *
+ * (The MIT license)
+ * Copyright (c) 2015 Rob Morgan
+ *
+ * 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.
+ *
+ * @package    Phinx
+ * @subpackage Phinx\Migration
+ */
+namespace Phinx\Migration;
+
+use think\console\Input as InputInterface;
+use think\console\Output as OutputInterface;
+
+/**
+ * Migration interface
+ *
+ * @author Richard Quadling <RQuadling@GMail.com>
+ */
+interface CreationInterface
+{
+    /**
+     * CreationInterface constructor.
+     *
+     * @param InputInterface|null  $input
+     * @param OutputInterface|null $output
+     */
+    public function __construct(InputInterface $input = null, OutputInterface $output = null);
+
+    /**
+     * @param InputInterface $input
+     *
+     * @return CreationInterface
+     */
+    public function setInput(InputInterface $input);
+
+    /**
+     * @param OutputInterface $output
+     *
+     * @return CreationInterface
+     */
+    public function setOutput(OutputInterface $output);
+
+    /**
+     * @return InputInterface
+     */
+    public function getInput();
+
+    /**
+     * @return OutputInterface
+     */
+    public function getOutput();
+
+    /**
+     * Get the migration template.
+     *
+     * This will be the content that Phinx will amend to generate the migration file.
+     *
+     * @return string The content of the template for Phinx to amend.
+     */
+    public function getMigrationTemplate();
+
+    /**
+     * Post Migration Creation.
+     *
+     * Once the migration file has been created, this method will be called, allowing any additional
+     * processing, specific to the template to be performed.
+     *
+     * @param string $migrationFilename The name of the newly created migration.
+     * @param string $className         The class name.
+     * @param string $baseClassName     The name of the base class.
+     * @return void
+     */
+    public function postMigrationCreation($migrationFilename, $className, $baseClassName);
+}
diff --git a/vendor/topthink/think-migration/phinx/src/Phinx/Migration/IrreversibleMigrationException.php b/vendor/topthink/think-migration/phinx/src/Phinx/Migration/IrreversibleMigrationException.php
new file mode 100755
index 0000000..9c2a8d2
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/src/Phinx/Migration/IrreversibleMigrationException.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Phinx
+ *
+ * (The MIT license)
+ * Copyright (c) 2015 Rob Morgan
+ *
+ * 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.
+ *
+ * @package    Phinx
+ * @subpackage Phinx\Migration
+ */
+namespace Phinx\Migration;
+
+/**
+ * Exception class thrown when migrations cannot be reversed using the 'change'
+ * feature.
+ *
+ * @author Rob Morgan <robbym@gmail.com>
+ */
+class IrreversibleMigrationException extends \Exception
+{
+}
diff --git a/vendor/topthink/think-migration/phinx/src/Phinx/Migration/Migration.template.php.dist b/vendor/topthink/think-migration/phinx/src/Phinx/Migration/Migration.template.php.dist
new file mode 100755
index 0000000..80ac0c1
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/src/Phinx/Migration/Migration.template.php.dist
@@ -0,0 +1,32 @@
+<?php
+
+use $useClassName;
+
+class $className extends $baseClassName
+{
+    /**
+     * Change Method.
+     *
+     * Write your reversible migrations using this method.
+     *
+     * More information on writing migrations is available here:
+     * http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
+     *
+     * The following commands can be used in this method and Phinx will
+     * automatically reverse them when rolling back:
+     *
+     *    createTable
+     *    renameTable
+     *    addColumn
+     *    renameColumn
+     *    addIndex
+     *    addForeignKey
+     *
+     * Remember to call "create()" or "update()" and NOT "save()" when working
+     * with the Table class.
+     */
+    public function change()
+    {
+
+    }
+}
diff --git a/vendor/topthink/think-migration/phinx/src/Phinx/Migration/MigrationInterface.php b/vendor/topthink/think-migration/phinx/src/Phinx/Migration/MigrationInterface.php
new file mode 100755
index 0000000..927be3a
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/src/Phinx/Migration/MigrationInterface.php
@@ -0,0 +1,215 @@
+<?php
+/**
+ * Phinx
+ *
+ * (The MIT license)
+ * Copyright (c) 2015 Rob Morgan
+ *
+ * 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.
+ *
+ * @package    Phinx
+ * @subpackage Phinx\Migration
+ */
+namespace Phinx\Migration;
+
+use Phinx\Db\Adapter\AdapterInterface;
+use Phinx\Db\Table;
+use think\console\Input as InputInterface;
+use think\console\Output as OutputInterface;
+
+/**
+ * Migration interface
+ *
+ * @author Rob Morgan <robbym@gmail.com>
+ */
+interface MigrationInterface
+{
+    /**
+     * @var string
+     */
+    const CHANGE = 'change';
+
+    /**
+     * @var string
+     */
+    const UP = 'up';
+
+    /**
+     * @var string
+     */
+    const DOWN = 'down';
+
+    /**
+     * Migrate Up
+     *
+     * @return void
+     */
+    public function up();
+
+    /**
+     * Migrate Down
+     *
+     * @return void
+     */
+    public function down();
+
+    /**
+     * Sets the database adapter.
+     *
+     * @param AdapterInterface $adapter Database Adapter
+     * @return MigrationInterface
+     */
+    public function setAdapter(AdapterInterface $adapter);
+
+    /**
+     * Gets the database adapter.
+     *
+     * @return AdapterInterface
+     */
+    public function getAdapter();
+
+    /**
+     * Sets the input object to be used in migration object
+     *
+     * @param InputInterface $input
+     * @return MigrationInterface
+     */
+    public function setInput(InputInterface $input);
+
+    /**
+     * Gets the input object to be used in migration object
+     *
+     * @return InputInterface
+     */
+    public function getInput();
+
+    /**
+     * Sets the output object to be used in migration object
+     *
+     * @param OutputInterface $output
+     * @return MigrationInterface
+     */
+    public function setOutput(OutputInterface $output);
+
+    /**
+     * Gets the output object to be used in migration object
+     *
+     * @return OutputInterface
+     */
+    public function getOutput();
+
+    /**
+     * Gets the name.
+     *
+     * @return string
+     */
+    public function getName();
+
+    /**
+     * Sets the migration version number.
+     *
+     * @param float $version Version
+     * @return MigrationInterface
+     */
+    public function setVersion($version);
+
+    /**
+     * Gets the migration version number.
+     *
+     * @return float
+     */
+    public function getVersion();
+
+    /**
+     * Executes a SQL statement and returns the number of affected rows.
+     *
+     * @param string $sql SQL
+     * @return int
+     */
+    public function execute($sql);
+
+    /**
+     * Executes a SQL statement and returns the result as an array.
+     *
+     * @param string $sql SQL
+     * @return array
+     */
+    public function query($sql);
+
+    /**
+     * Executes a query and returns only one row as an array.
+     *
+     * @param string $sql SQL
+     * @return array
+     */
+    public function fetchRow($sql);
+
+    /**
+     * Executes a query and returns an array of rows.
+     *
+     * @param string $sql SQL
+     * @return array
+     */
+    public function fetchAll($sql);
+
+    /**
+     * Insert data into a table.
+     *
+     * @param string $tableName
+     * @param array $data
+     * @return void
+     */
+    public function insert($tableName, $data);
+
+    /**
+     * Create a new database.
+     *
+     * @param string $name Database Name
+     * @param array $options Options
+     * @return void
+     */
+    public function createDatabase($name, $options);
+
+    /**
+     * Drop a database.
+     *
+     * @param string $name Database Name
+     * @return void
+     */
+    public function dropDatabase($name);
+
+    /**
+     * Checks to see if a table exists.
+     *
+     * @param string $tableName Table Name
+     * @return boolean
+     */
+    public function hasTable($tableName);
+
+    /**
+     * Returns an instance of the <code>\Table</code> class.
+     *
+     * You can use this class to create and manipulate tables.
+     *
+     * @param string $tableName Table Name
+     * @param array $options Options
+     * @return Table
+     */
+    public function table($tableName, $options);
+}
diff --git a/vendor/topthink/think-migration/phinx/src/Phinx/Seed/AbstractSeed.php b/vendor/topthink/think-migration/phinx/src/Phinx/Seed/AbstractSeed.php
new file mode 100755
index 0000000..4d631f5
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/src/Phinx/Seed/AbstractSeed.php
@@ -0,0 +1,215 @@
+<?php
+/**
+ * Phinx
+ *
+ * (The MIT license)
+ * Copyright (c) 2015 Rob Morgan
+ *
+ * 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.
+ *
+ * @package    Phinx
+ * @subpackage Phinx\Seed
+ */
+namespace Phinx\Seed;
+
+use Phinx\Db\Table;
+use Phinx\Db\Adapter\AdapterInterface;
+use think\console\Input as InputInterface;
+use think\console\Output as OutputInterface;
+
+/**
+ * Abstract Seed Class.
+ *
+ * It is expected that the seeds you write extend from this class.
+ *
+ * This abstract class proxies the various database methods to your specified
+ * adapter.
+ *
+ * @author Rob Morgan <robbym@gmail.com>
+ */
+abstract class AbstractSeed implements SeedInterface
+{
+    /**
+     * @var AdapterInterface
+     */
+    protected $adapter;
+
+    /**
+     * @var InputInterface
+     */
+    protected $input;
+
+    /**
+     * @var OutputInterface
+     */
+    protected $output;
+
+    /**
+     * Class Constructor.
+     *
+     * @param InputInterface $input
+     * @param OutputInterface $output
+     */
+    final public function __construct(InputInterface $input = null, OutputInterface $output = null)
+    {
+        if (!is_null($input)){
+            $this->setInput($input);
+        }
+        if (!is_null($output)){
+            $this->setOutput($output);
+        }
+        
+        $this->init();
+    }
+
+    /**
+     * Initialize method.
+     *
+     * @return void
+     */
+    protected function init()
+    {
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function run()
+    {
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setAdapter(AdapterInterface $adapter)
+    {
+        $this->adapter = $adapter;
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getAdapter()
+    {
+        return $this->adapter;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setInput(InputInterface $input)
+    {
+        $this->input = $input;
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getInput()
+    {
+        return $this->input;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setOutput(OutputInterface $output)
+    {
+        $this->output = $output;
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getOutput()
+    {
+        return $this->output;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getName()
+    {
+        return get_class($this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function execute($sql)
+    {
+        return $this->getAdapter()->execute($sql);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function query($sql)
+    {
+        return $this->getAdapter()->query($sql);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function fetchRow($sql)
+    {
+        return $this->getAdapter()->fetchRow($sql);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function fetchAll($sql)
+    {
+        return $this->getAdapter()->fetchAll($sql);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function insert($table, $data)
+    {
+        // convert to table object
+        if (is_string($table)) {
+            $table = new Table($table, array(), $this->getAdapter());
+        }
+        return $table->insert($data)->save();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasTable($tableName)
+    {
+        return $this->getAdapter()->hasTable($tableName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function table($tableName, $options = array())
+    {
+        return new Table($tableName, $options, $this->getAdapter());
+    }
+}
diff --git a/vendor/topthink/think-migration/phinx/src/Phinx/Seed/Seed.template.php.dist b/vendor/topthink/think-migration/phinx/src/Phinx/Seed/Seed.template.php.dist
new file mode 100755
index 0000000..698ae26
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/src/Phinx/Seed/Seed.template.php.dist
@@ -0,0 +1,19 @@
+<?php
+
+use $useClassName;
+
+class $className extends $baseClassName
+{
+    /**
+     * Run Method.
+     *
+     * Write your database seeder using this method.
+     *
+     * More information on writing seeders is available here:
+     * http://docs.phinx.org/en/latest/seeding.html
+     */
+    public function run()
+    {
+
+    }
+}
diff --git a/vendor/topthink/think-migration/phinx/src/Phinx/Seed/SeedInterface.php b/vendor/topthink/think-migration/phinx/src/Phinx/Seed/SeedInterface.php
new file mode 100755
index 0000000..86be8e0
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/src/Phinx/Seed/SeedInterface.php
@@ -0,0 +1,166 @@
+<?php
+/**
+ * Phinx
+ *
+ * (The MIT license)
+ * Copyright (c) 2015 Rob Morgan
+ *
+ * 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.
+ *
+ * @package    Phinx
+ * @subpackage Phinx\Seed
+ */
+namespace Phinx\Seed;
+
+use Phinx\Db\Adapter\AdapterInterface;
+use Phinx\Db\Table;
+use think\console\Input as InputInterface;
+use think\console\Output as OutputInterface;
+
+/**
+ * Seed interface
+ *
+ * @author Rob Morgan <robbym@gmail.com>
+ */
+interface SeedInterface
+{
+    /**
+     * @var string
+     */
+    const RUN = 'run';
+
+    /**
+     * Run the seeder.
+     *
+     * @return void
+     */
+    public function run();
+
+    /**
+     * Sets the database adapter.
+     *
+     * @param AdapterInterface $adapter Database Adapter
+     * @return MigrationInterface
+     */
+    public function setAdapter(AdapterInterface $adapter);
+
+    /**
+     * Gets the database adapter.
+     *
+     * @return AdapterInterface
+     */
+    public function getAdapter();
+
+    /**
+     * Sets the input object to be used in migration object
+     *
+     * @param InputInterface $input
+     * @return MigrationInterface
+     */
+    public function setInput(InputInterface $input);
+
+    /**
+     * Gets the input object to be used in migration object
+     *
+     * @return InputInterface
+     */
+    public function getInput();
+
+    /**
+     * Sets the output object to be used in migration object
+     *
+     * @param OutputInterface $output
+     * @return MigrationInterface
+     */
+    public function setOutput(OutputInterface $output);
+
+    /**
+     * Gets the output object to be used in migration object
+     *
+     * @return OutputInterface
+     */
+    public function getOutput();
+
+    /**
+     * Gets the name.
+     *
+     * @return string
+     */
+    public function getName();
+
+    /**
+     * Executes a SQL statement and returns the number of affected rows.
+     *
+     * @param string $sql SQL
+     * @return int
+     */
+    public function execute($sql);
+
+    /**
+     * Executes a SQL statement and returns the result as an array.
+     *
+     * @param string $sql SQL
+     * @return array
+     */
+    public function query($sql);
+
+    /**
+     * Executes a query and returns only one row as an array.
+     *
+     * @param string $sql SQL
+     * @return array
+     */
+    public function fetchRow($sql);
+
+    /**
+     * Executes a query and returns an array of rows.
+     *
+     * @param string $sql SQL
+     * @return array
+     */
+    public function fetchAll($sql);
+
+    /**
+     * Insert data into a table.
+     *
+     * @param string $tableName
+     * @param array $data
+     * @return void
+     */
+    public function insert($tableName, $data);
+
+    /**
+     * Checks to see if a table exists.
+     *
+     * @param string $tableName Table Name
+     * @return boolean
+     */
+    public function hasTable($tableName);
+
+    /**
+     * Returns an instance of the <code>\Table</code> class.
+     *
+     * You can use this class to create and manipulate tables.
+     *
+     * @param string $tableName Table Name
+     * @param array $options Options
+     * @return Table
+     */
+    public function table($tableName, $options);
+}
diff --git a/vendor/topthink/think-migration/phinx/src/Phinx/Util/Util.php b/vendor/topthink/think-migration/phinx/src/Phinx/Util/Util.php
new file mode 100755
index 0000000..9b8edf2
--- /dev/null
+++ b/vendor/topthink/think-migration/phinx/src/Phinx/Util/Util.php
@@ -0,0 +1,190 @@
+<?php
+/**
+ * Phinx
+ *
+ * (The MIT license)
+ * Copyright (c) 2015 Rob Morgan
+ *
+ * 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.
+ *
+ * @package    Phinx
+ * @subpackage Phinx\Util
+ */
+namespace Phinx\Util;
+
+class Util
+{
+    /**
+     * @var string
+     */
+    const DATE_FORMAT = 'YmdHis';
+
+    /**
+     * @var string
+     */
+    const MIGRATION_FILE_NAME_PATTERN = '/^\d+_([\w_]+).php$/i';
+
+    /**
+     * @var string
+     */
+    const SEED_FILE_NAME_PATTERN = '/^([A-Z][a-z0-9]+).php$/i';
+
+    /**
+     * Gets the current timestamp string, in UTC.
+     *
+     * @return string
+     */
+    public static function getCurrentTimestamp()
+    {
+        $dt = new \DateTime('now', new \DateTimeZone('UTC'));
+        return $dt->format(static::DATE_FORMAT);
+    }
+
+    /**
+     * Gets an array of all the existing migration class names.
+     *
+     * @return string
+     */
+    public static function getExistingMigrationClassNames($path)
+    {
+        $classNames = array();
+
+        if (!is_dir($path)) {
+            return $classNames;
+        }
+
+        // filter the files to only get the ones that match our naming scheme
+        $phpFiles = glob($path . DIRECTORY_SEPARATOR . '*.php');
+
+        foreach ($phpFiles as $filePath) {
+            if (preg_match('/([0-9]+)_([_a-z0-9]*).php/', basename($filePath))) {
+                $classNames[] = static::mapFileNameToClassName(basename($filePath));
+            }
+        }
+
+        return $classNames;
+    }
+
+    /**
+     * Get the version from the beginning of a file name.
+     *
+     * @param string $fileName File Name
+     * @return string
+     */
+    public static function getVersionFromFileName($fileName)
+    {
+        $matches = array();
+        preg_match('/^[0-9]+/', basename($fileName), $matches);
+        return $matches[0];
+    }
+
+    /**
+     * Turn migration names like 'CreateUserTable' into file names like
+     * '12345678901234_create_user_table.php' or 'LimitResourceNamesTo30Chars' into
+     * '12345678901234_limit_resource_names_to_30_chars.php'.
+     *
+     * @param string $className Class Name
+     * @return string
+     */
+    public static function mapClassNameToFileName($className)
+    {
+        $arr = preg_split('/(?=[A-Z])/', $className);
+        unset($arr[0]); // remove the first element ('')
+        $fileName = static::getCurrentTimestamp() . '_' . strtolower(implode('_', $arr)) . '.php';
+        return $fileName;
+    }
+
+    /**
+     * Turn file names like '12345678901234_create_user_table.php' into class
+     * names like 'CreateUserTable'.
+     *
+     * @param string $fileName File Name
+     * @return string
+     */
+    public static function mapFileNameToClassName($fileName)
+    {
+        $matches = array();
+        if (preg_match(static::MIGRATION_FILE_NAME_PATTERN, $fileName, $matches)) {
+            $fileName = $matches[1];
+        }
+
+        return str_replace(' ', '', ucwords(str_replace('_', ' ', $fileName)));
+    }
+
+    /**
+     * Check if a migration class name is unique regardless of the
+     * timestamp.
+     *
+     * This method takes a class name and a path to a migrations directory.
+     *
+     * Migration class names must be in CamelCase format.
+     * e.g: CreateUserTable or AddIndexToPostsTable.
+     *
+     * Single words are not allowed on their own.
+     *
+     * @param string $className Class Name
+     * @param string $path Path
+     * @return boolean
+     */
+    public static function isUniqueMigrationClassName($className, $path)
+    {
+        $existingClassNames = static::getExistingMigrationClassNames($path);
+        return !(in_array($className, $existingClassNames));
+    }
+
+    /**
+     * Check if a migration/seed class name is valid.
+     *
+     * Migration & Seed class names must be in CamelCase format.
+     * e.g: CreateUserTable, AddIndexToPostsTable or UserSeeder.
+     *
+     * Single words are not allowed on their own.
+     *
+     * @param string $className Class Name
+     * @return boolean
+     */
+    public static function isValidPhinxClassName($className)
+    {
+        return (bool) preg_match('/^([A-Z][a-z0-9]+)+$/', $className);
+    }
+
+    /**
+     * Check if a migration file name is valid.
+     *
+     * @param string $fileName File Name
+     * @return boolean
+     */
+    public static function isValidMigrationFileName($fileName)
+    {
+        $matches = array();
+        return preg_match(static::MIGRATION_FILE_NAME_PATTERN, $fileName, $matches);
+    }
+
+    /**
+     * Check if a seed file name is valid.
+     *
+     * @param string $fileName File Name
+     * @return boolean
+     */
+    public static function isValidSeedFileName($fileName)
+    {
+        $matches = array();
+        return preg_match(static::SEED_FILE_NAME_PATTERN, $fileName, $matches);
+    }
+}
diff --git a/vendor/topthink/think-migration/src/Command.php b/vendor/topthink/think-migration/src/Command.php
new file mode 100755
index 0000000..b4db21f
--- /dev/null
+++ b/vendor/topthink/think-migration/src/Command.php
@@ -0,0 +1,89 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+namespace think\migration;
+
+use InvalidArgumentException;
+use Phinx\Db\Adapter\AdapterFactory;
+
+abstract class Command extends \think\console\Command
+{
+
+    public function getAdapter()
+    {
+        if (isset($this->adapter)) {
+            return $this->adapter;
+        }
+
+        $options = $this->getDbConfig();
+
+        $adapter = AdapterFactory::instance()->getAdapter($options['adapter'], $options);
+
+        if ($adapter->hasOption('table_prefix') || $adapter->hasOption('table_suffix')) {
+            $adapter = AdapterFactory::instance()->getWrapper('prefix', $adapter);
+        }
+
+        $this->adapter = $adapter;
+
+        return $adapter;
+    }
+
+    /**
+     * 获取数据库配置
+     * @return array
+     */
+    protected function getDbConfig(): array
+    {
+        $default = $this->app->config->get('database.default');
+
+        $config = $this->app->config->get("database.connections.{$default}");
+
+        if (0 == $config['deploy']) {
+            $dbConfig = [
+                'adapter'      => $config['type'],
+                'host'         => $config['hostname'],
+                'name'         => $config['database'],
+                'user'         => $config['username'],
+                'pass'         => $config['password'],
+                'port'         => $config['hostport'],
+                'charset'      => $config['charset'],
+                'table_prefix' => $config['prefix'],
+            ];
+        } else {
+            $dbConfig = [
+                'adapter'      => explode(',', $config['type'])[0],
+                'host'         => explode(',', $config['hostname'])[0],
+                'name'         => explode(',', $config['database'])[0],
+                'user'         => explode(',', $config['username'])[0],
+                'pass'         => explode(',', $config['password'])[0],
+                'port'         => explode(',', $config['hostport'])[0],
+                'charset'      => explode(',', $config['charset'])[0],
+                'table_prefix' => explode(',', $config['prefix'])[0],
+            ];
+        }
+
+        $table = $this->app->config->get('database.migration_table', 'migrations');
+
+        $dbConfig['default_migration_table'] = $dbConfig['table_prefix'] . $table;
+
+        return $dbConfig;
+    }
+
+    protected function verifyMigrationDirectory(string $path)
+    {
+        if (!is_dir($path)) {
+            throw new InvalidArgumentException(sprintf('Migration directory "%s" does not exist', $path));
+        }
+
+        if (!is_writable($path)) {
+            throw new InvalidArgumentException(sprintf('Migration directory "%s" is not writable', $path));
+        }
+    }
+}
diff --git a/vendor/topthink/think-migration/src/Creator.php b/vendor/topthink/think-migration/src/Creator.php
new file mode 100755
index 0000000..f4bc804
--- /dev/null
+++ b/vendor/topthink/think-migration/src/Creator.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace think\migration;
+
+use InvalidArgumentException;
+use Phinx\Util\Util;
+use RuntimeException;
+use think\App;
+
+class Creator
+{
+
+    protected $app;
+
+    public function __construct(App $app)
+    {
+        $this->app = $app;
+    }
+
+    public function create(string $className)
+    {
+        $path = $this->ensureDirectory();
+
+        if (!Util::isValidPhinxClassName($className)) {
+            throw new InvalidArgumentException(sprintf('The migration class name "%s" is invalid. Please use CamelCase format.', $className));
+        }
+
+        if (!Util::isUniqueMigrationClassName($className, $path)) {
+            throw new InvalidArgumentException(sprintf('The migration class name "%s" already exists', $className));
+        }
+
+        // Compute the file path
+        $fileName = Util::mapClassNameToFileName($className);
+        $filePath = $path . DIRECTORY_SEPARATOR . $fileName;
+
+        if (is_file($filePath)) {
+            throw new InvalidArgumentException(sprintf('The file "%s" already exists', $filePath));
+        }
+
+        // Verify that the template creation class (or the aliased class) exists and that it implements the required interface.
+        $aliasedClassName = null;
+
+        // Load the alternative template if it is defined.
+        $contents = file_get_contents($this->getTemplate());
+
+        // inject the class names appropriate to this migration
+        $contents = strtr($contents, [
+            'MigratorClass' => $className,
+        ]);
+
+        if (false === file_put_contents($filePath, $contents)) {
+            throw new RuntimeException(sprintf('The file "%s" could not be written to', $path));
+        }
+
+        return $filePath;
+    }
+
+    protected function ensureDirectory()
+    {
+        $path = $this->app->getRootPath() . 'database' . DIRECTORY_SEPARATOR . 'migrations';
+
+        if (!is_dir($path) && !mkdir($path, 0755, true)) {
+            throw new InvalidArgumentException(sprintf('directory "%s" does not exist', $path));
+        }
+
+        if (!is_writable($path)) {
+            throw new InvalidArgumentException(sprintf('directory "%s" is not writable', $path));
+        }
+
+        return $path;
+    }
+
+    protected function getTemplate()
+    {
+        return __DIR__ . '/command/stubs/migrate.stub';
+    }
+}
diff --git a/vendor/topthink/think-migration/src/Factory.php b/vendor/topthink/think-migration/src/Factory.php
new file mode 100755
index 0000000..976a999
--- /dev/null
+++ b/vendor/topthink/think-migration/src/Factory.php
@@ -0,0 +1,313 @@
+<?php
+
+namespace think\migration;
+
+use ArrayAccess;
+use Faker\Generator as Faker;
+
+class Factory implements ArrayAccess
+{
+
+    /**
+     * The model definitions in the container.
+     *
+     * @var array
+     */
+    protected $definitions = [];
+
+    /**
+     * The registered model states.
+     *
+     * @var array
+     */
+    protected $states = [];
+
+    /**
+     * The registered after making callbacks.
+     *
+     * @var array
+     */
+    protected $afterMaking = [];
+
+    /**
+     * The registered after creating callbacks.
+     *
+     * @var array
+     */
+    protected $afterCreating = [];
+
+    /**
+     * The Faker instance for the builder.
+     *
+     * @var Faker
+     */
+    protected $faker;
+
+    /**
+     * Create a new factory instance.
+     *
+     * @param Faker $faker
+     * @return void
+     */
+    public function __construct(Faker $faker)
+    {
+        $this->faker = $faker;
+    }
+
+    /**
+     * Define a class with a given short-name.
+     *
+     * @param string   $class
+     * @param string   $name
+     * @param callable $attributes
+     * @return $this
+     */
+    public function defineAs(string $class, string $name, callable $attributes)
+    {
+        return $this->define($class, $attributes, $name);
+    }
+
+    /**
+     * Define a class with a given set of attributes.
+     *
+     * @param string   $class
+     * @param callable $attributes
+     * @param string   $name
+     * @return $this
+     */
+    public function define(string $class, callable $attributes, string $name = 'default')
+    {
+        $this->definitions[$class][$name] = $attributes;
+
+        return $this;
+    }
+
+    /**
+     * Define a state with a given set of attributes.
+     *
+     * @param string         $class
+     * @param string         $state
+     * @param callable|array $attributes
+     * @return $this
+     */
+    public function state(string $class, string $state, $attributes)
+    {
+        $this->states[$class][$state] = $attributes;
+
+        return $this;
+    }
+
+    /**
+     * Define a callback to run after making a model.
+     *
+     * @param string   $class
+     * @param callable $callback
+     * @param string   $name
+     * @return $this
+     */
+    public function afterMaking(string $class, callable $callback, string $name = 'default')
+    {
+        $this->afterMaking[$class][$name][] = $callback;
+
+        return $this;
+    }
+
+    /**
+     * Define a callback to run after making a model with given state.
+     *
+     * @param string   $class
+     * @param string   $state
+     * @param callable $callback
+     * @return $this
+     */
+    public function afterMakingState(string $class, string $state, callable $callback)
+    {
+        return $this->afterMaking($class, $callback, $state);
+    }
+
+    /**
+     * Define a callback to run after creating a model.
+     *
+     * @param string   $class
+     * @param callable $callback
+     * @param string   $name
+     * @return $this
+     */
+    public function afterCreating(string $class, callable $callback, string $name = 'default')
+    {
+        $this->afterCreating[$class][$name][] = $callback;
+
+        return $this;
+    }
+
+    /**
+     * Define a callback to run after creating a model with given state.
+     *
+     * @param string   $class
+     * @param string   $state
+     * @param callable $callback
+     * @return $this
+     */
+    public function afterCreatingState(string $class, string $state, callable $callback)
+    {
+        return $this->afterCreating($class, $callback, $state);
+    }
+
+    /**
+     * Create an instance of the given model and persist it to the database.
+     *
+     * @param string $class
+     * @param array  $attributes
+     * @return mixed
+     */
+    public function create(string $class, array $attributes = [])
+    {
+        return $this->of($class)->create($attributes);
+    }
+
+    /**
+     * Create an instance of the given model and type and persist it to the database.
+     *
+     * @param string $class
+     * @param string $name
+     * @param array  $attributes
+     * @return mixed
+     */
+    public function createAs(string $class, string $name, array $attributes = [])
+    {
+        return $this->of($class, $name)->create($attributes);
+    }
+
+    /**
+     * Create an instance of the given model.
+     *
+     * @param string $class
+     * @param array  $attributes
+     * @return mixed
+     */
+    public function make(string $class, array $attributes = [])
+    {
+        return $this->of($class)->make($attributes);
+    }
+
+    /**
+     * Create an instance of the given model and type.
+     *
+     * @param string $class
+     * @param string $name
+     * @param array  $attributes
+     * @return mixed
+     */
+    public function makeAs(string $class, string $name, array $attributes = [])
+    {
+        return $this->of($class, $name)->make($attributes);
+    }
+
+    /**
+     * Get the raw attribute array for a given named model.
+     *
+     * @param string $class
+     * @param string $name
+     * @param array  $attributes
+     * @return array
+     */
+    public function rawOf(string $class, string $name, array $attributes = [])
+    {
+        return $this->raw($class, $attributes, $name);
+    }
+
+    /**
+     * Get the raw attribute array for a given model.
+     *
+     * @param string $class
+     * @param array  $attributes
+     * @param string $name
+     * @return array
+     */
+    public function raw(string $class, array $attributes = [], string $name = 'default')
+    {
+        return array_merge(
+            call_user_func($this->definitions[$class][$name], $this->faker), $attributes
+        );
+    }
+
+    /**
+     * Create a builder for the given model.
+     *
+     * @param string $class
+     * @param string $name
+     * @return FactoryBuilder
+     */
+    public function of(string $class, string $name = 'default')
+    {
+        return new FactoryBuilder(
+            $class, $name, $this->definitions, $this->states,
+            $this->afterMaking, $this->afterCreating, $this->faker
+        );
+    }
+
+    /**
+     * Load factories from path.
+     *
+     * @param string $path
+     * @return $this
+     */
+    public function load(string $path)
+    {
+        $factory = $this;
+
+        if (is_dir($path)) {
+            foreach (glob($path . '*.php') as $file) {
+                require $file;
+            }
+        }
+
+        return $factory;
+    }
+
+    /**
+     * Determine if the given offset exists.
+     *
+     * @param string $offset
+     * @return bool
+     */
+    public function offsetExists($offset)
+    {
+        return isset($this->definitions[$offset]);
+    }
+
+    /**
+     * Get the value of the given offset.
+     *
+     * @param string $offset
+     * @return mixed
+     */
+    public function offsetGet($offset)
+    {
+        return $this->make($offset);
+    }
+
+    /**
+     * Set the given offset to the given value.
+     *
+     * @param string   $offset
+     * @param callable $value
+     * @return void
+     */
+    public function offsetSet($offset, $value)
+    {
+        $this->define($offset, $value);
+    }
+
+    /**
+     * Unset the value at the given offset.
+     *
+     * @param string $offset
+     * @return void
+     */
+    public function offsetUnset($offset)
+    {
+        unset($this->definitions[$offset]);
+    }
+
+}
diff --git a/vendor/topthink/think-migration/src/FactoryBuilder.php b/vendor/topthink/think-migration/src/FactoryBuilder.php
new file mode 100755
index 0000000..a72b07e
--- /dev/null
+++ b/vendor/topthink/think-migration/src/FactoryBuilder.php
@@ -0,0 +1,437 @@
+<?php
+
+namespace think\migration;
+
+use Faker\Generator as Faker;
+use InvalidArgumentException;
+use think\Collection;
+use think\Model;
+
+class FactoryBuilder
+{
+
+    /**
+     * The model definitions in the container.
+     *
+     * @var array
+     */
+    protected $definitions;
+
+    /**
+     * The model being built.
+     *
+     * @var string
+     */
+    protected $class;
+
+    /**
+     * The name of the model being built.
+     *
+     * @var string
+     */
+    protected $name = 'default';
+
+    /**
+     * The database connection on which the model instance should be persisted.
+     *
+     * @var string
+     */
+    protected $connection;
+
+    /**
+     * The model states.
+     *
+     * @var array
+     */
+    protected $states;
+
+    /**
+     * The model after making callbacks.
+     *
+     * @var array
+     */
+    protected $afterMaking = [];
+
+    /**
+     * The model after creating callbacks.
+     *
+     * @var array
+     */
+    protected $afterCreating = [];
+
+    /**
+     * The states to apply.
+     *
+     * @var array
+     */
+    protected $activeStates = [];
+
+    /**
+     * The Faker instance for the builder.
+     *
+     * @var Faker
+     */
+    protected $faker;
+
+    /**
+     * The number of models to build.
+     *
+     * @var int|null
+     */
+    protected $amount = null;
+
+    /**
+     * Create an new builder instance.
+     *
+     * @param string $class
+     * @param string $name
+     * @param array $definitions
+     * @param array $states
+     * @param array $afterMaking
+     * @param array $afterCreating
+     * @param Faker $faker
+     * @return void
+     */
+    public function __construct($class, $name, array $definitions, array $states,
+                                array $afterMaking, array $afterCreating, Faker $faker)
+    {
+        $this->name          = $name;
+        $this->class         = $class;
+        $this->faker         = $faker;
+        $this->states        = $states;
+        $this->definitions   = $definitions;
+        $this->afterMaking   = $afterMaking;
+        $this->afterCreating = $afterCreating;
+    }
+
+    /**
+     * Set the amount of models you wish to create / make.
+     *
+     * @param int $amount
+     * @return $this
+     */
+    public function times($amount)
+    {
+        $this->amount = $amount;
+
+        return $this;
+    }
+
+    /**
+     * Set the state to be applied to the model.
+     *
+     * @param string $state
+     * @return $this
+     */
+    public function state($state)
+    {
+        return $this->states([$state]);
+    }
+
+    /**
+     * Set the states to be applied to the model.
+     *
+     * @param array|mixed $states
+     * @return $this
+     */
+    public function states($states)
+    {
+        $this->activeStates = is_array($states) ? $states : func_get_args();
+
+        return $this;
+    }
+
+    /**
+     * Set the database connection on which the model instance should be persisted.
+     *
+     * @param string $name
+     * @return $this
+     */
+    public function connection($name)
+    {
+        $this->connection = $name;
+
+        return $this;
+    }
+
+    /**
+     * Create a model and persist it in the database if requested.
+     *
+     * @param array $attributes
+     * @return \Closure
+     */
+    public function lazy(array $attributes = [])
+    {
+        return function () use ($attributes) {
+            return $this->create($attributes);
+        };
+    }
+
+    /**
+     * Create a collection of models and persist them to the database.
+     *
+     * @param array $attributes
+     * @return mixed
+     */
+    public function create(array $attributes = [])
+    {
+        $results = $this->make($attributes);
+
+        if ($results instanceof Model) {
+            $this->store(new Collection([$results]));
+
+            $this->callAfterCreating(new Collection([$results]));
+        } else {
+            $this->store($results);
+
+            $this->callAfterCreating($results);
+        }
+
+        return $results;
+    }
+
+    /**
+     * Set the connection name on the results and store them.
+     *
+     * @param Collection $results
+     * @return void
+     */
+    protected function store($results)
+    {
+        $results->each(function (Model $model) {
+            $model->save();
+        });
+    }
+
+    /**
+     * Create a collection of models.
+     *
+     * @param array $attributes
+     * @return mixed
+     */
+    public function make(array $attributes = [])
+    {
+        if ($this->amount === null) {
+            return tap($this->makeInstance($attributes), function ($instance) {
+                $this->callAfterMaking(new Collection([$instance]));
+            });
+        }
+
+        if ($this->amount < 1) {
+            return (new $this->class)->toCollection();
+        }
+
+        $instances = (new $this->class)->toCollection(array_map(function () use ($attributes) {
+            return $this->makeInstance($attributes);
+        }, range(1, $this->amount)));
+
+        $this->callAfterMaking($instances);
+
+        return $instances;
+    }
+
+    /**
+     * Create an array of raw attribute arrays.
+     *
+     * @param array $attributes
+     * @return mixed
+     */
+    public function raw(array $attributes = [])
+    {
+        if ($this->amount === null) {
+            return $this->getRawAttributes($attributes);
+        }
+
+        if ($this->amount < 1) {
+            return [];
+        }
+
+        return array_map(function () use ($attributes) {
+            return $this->getRawAttributes($attributes);
+        }, range(1, $this->amount));
+    }
+
+    /**
+     * Get a raw attributes array for the model.
+     *
+     * @param array $attributes
+     * @return mixed
+     *
+     * @throws \InvalidArgumentException
+     */
+    protected function getRawAttributes(array $attributes = [])
+    {
+        if (!isset($this->definitions[$this->class][$this->name])) {
+            throw new InvalidArgumentException("Unable to locate factory with name [{$this->name}] [{$this->class}].");
+        }
+
+        $definition = call_user_func(
+            $this->definitions[$this->class][$this->name],
+            $this->faker, $attributes
+        );
+
+        return $this->expandAttributes(
+            array_merge($this->applyStates($definition, $attributes), $attributes)
+        );
+    }
+
+    /**
+     * Make an instance of the model with the given attributes.
+     *
+     * @param array $attributes
+     * @return Model
+     */
+    protected function makeInstance(array $attributes = [])
+    {
+        /** @var Model $model */
+        $model = new $this->class;
+
+        $model->setAttrs($this->getRawAttributes($attributes));
+
+        return $model;
+    }
+
+    /**
+     * Apply the active states to the model definition array.
+     *
+     * @param array $definition
+     * @param array $attributes
+     * @return array
+     */
+    protected function applyStates(array $definition, array $attributes = [])
+    {
+        foreach ($this->activeStates as $state) {
+            if (!isset($this->states[$this->class][$state])) {
+                if ($this->stateHasAfterCallback($state)) {
+                    continue;
+                }
+
+                throw new InvalidArgumentException("Unable to locate [{$state}] state for [{$this->class}].");
+            }
+
+            $definition = array_merge(
+                $definition,
+                $this->stateAttributes($state, $attributes)
+            );
+        }
+
+        return $definition;
+    }
+
+    /**
+     * Get the state attributes.
+     *
+     * @param string $state
+     * @param array $attributes
+     * @return array
+     */
+    protected function stateAttributes($state, array $attributes)
+    {
+        $stateAttributes = $this->states[$this->class][$state];
+
+        if (!is_callable($stateAttributes)) {
+            return $stateAttributes;
+        }
+
+        return call_user_func(
+            $stateAttributes,
+            $this->faker, $attributes
+        );
+    }
+
+    /**
+     * Expand all attributes to their underlying values.
+     *
+     * @param array $attributes
+     * @return array
+     */
+    protected function expandAttributes(array $attributes)
+    {
+        foreach ($attributes as &$attribute) {
+            if (is_callable($attribute) && !is_string($attribute) && !is_array($attribute)) {
+                $attribute = $attribute($attributes);
+            }
+
+            if ($attribute instanceof static) {
+                $attribute = $attribute->create()->getKey();
+            }
+
+            if ($attribute instanceof Model) {
+                $attribute = $attribute->getKey();
+            }
+        }
+
+        return $attributes;
+    }
+
+    /**
+     * Run after making callbacks on a collection of models.
+     *
+     * @param Collection $models
+     * @return void
+     */
+    public function callAfterMaking($models)
+    {
+        $this->callAfter($this->afterMaking, $models);
+    }
+
+    /**
+     * Run after creating callbacks on a collection of models.
+     *
+     * @param Collection $models
+     * @return void
+     */
+    public function callAfterCreating($models)
+    {
+        $this->callAfter($this->afterCreating, $models);
+    }
+
+    /**
+     * Call after callbacks for each model and state.
+     *
+     * @param array $afterCallbacks
+     * @param Collection $models
+     * @return void
+     */
+    protected function callAfter(array $afterCallbacks, $models)
+    {
+        $states = array_merge([$this->name], $this->activeStates);
+
+        $models->each(function ($model) use ($states, $afterCallbacks) {
+            foreach ($states as $state) {
+                $this->callAfterCallbacks($afterCallbacks, $model, $state);
+            }
+        });
+    }
+
+    /**
+     * Call after callbacks for each model and state.
+     *
+     * @param array $afterCallbacks
+     * @param Model $model
+     * @param string $state
+     * @return void
+     */
+    protected function callAfterCallbacks(array $afterCallbacks, $model, $state)
+    {
+        if (!isset($afterCallbacks[$this->class][$state])) {
+            return;
+        }
+
+        foreach ($afterCallbacks[$this->class][$state] as $callback) {
+            $callback($model, $this->faker);
+        }
+    }
+
+    /**
+     * Determine if the given state has an "after" callback.
+     *
+     * @param string $state
+     * @return bool
+     */
+    protected function stateHasAfterCallback($state)
+    {
+        return isset($this->afterMaking[$this->class][$state]) ||
+            isset($this->afterCreating[$this->class][$state]);
+    }
+}
diff --git a/vendor/topthink/think-migration/src/Migrator.php b/vendor/topthink/think-migration/src/Migrator.php
new file mode 100755
index 0000000..1965953
--- /dev/null
+++ b/vendor/topthink/think-migration/src/Migrator.php
@@ -0,0 +1,27 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+namespace think\migration;
+
+use Phinx\Migration\AbstractMigration;
+use think\migration\db\Table;
+
+class Migrator extends AbstractMigration
+{
+    /**
+     * @param string $tableName
+     * @param array  $options
+     * @return Table
+     */
+    public function table($tableName, $options = [])
+    {
+        return new Table($tableName, $options, $this->getAdapter());
+    }
+}
diff --git a/vendor/topthink/think-migration/src/Seeder.php b/vendor/topthink/think-migration/src/Seeder.php
new file mode 100755
index 0000000..4b5f7f2
--- /dev/null
+++ b/vendor/topthink/think-migration/src/Seeder.php
@@ -0,0 +1,24 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+namespace think\migration;
+
+use Phinx\Seed\AbstractSeed;
+
+class Seeder extends AbstractSeed
+{
+    /**
+     * @return Factory
+     */
+    public function factory()
+    {
+        return app(Factory::class);
+    }
+}
diff --git a/vendor/topthink/think-migration/src/Service.php b/vendor/topthink/think-migration/src/Service.php
new file mode 100755
index 0000000..614e9e0
--- /dev/null
+++ b/vendor/topthink/think-migration/src/Service.php
@@ -0,0 +1,51 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\migration;
+
+use Faker\Factory as FakerFactory;
+use Faker\Generator as FakerGenerator;
+use think\migration\command\factory\Create as FactoryCreate;
+use think\migration\command\migrate\Breakpoint as MigrateBreakpoint;
+use think\migration\command\migrate\Create as MigrateCreate;
+use think\migration\command\migrate\Rollback as MigrateRollback;
+use think\migration\command\migrate\Run as MigrateRun;
+use think\migration\command\migrate\Status as MigrateStatus;
+use think\migration\command\seed\Create as SeedCreate;
+use think\migration\command\seed\Run as SeedRun;
+
+class Service extends \think\Service
+{
+
+    public function boot()
+    {
+        $this->app->bind(FakerGenerator::class, function () {
+            return FakerFactory::create($this->app->config->get('app.faker_locale', 'zh_CN'));
+        });
+
+        $this->app->bind(Factory::class, function () {
+            return (new Factory($this->app->make(FakerGenerator::class)))->load($this->app->getRootPath() . 'database/factories/');
+        });
+
+        $this->app->bind('migration.creator', Creator::class);
+
+        $this->commands([
+            MigrateCreate::class,
+            MigrateRun::class,
+            MigrateRollback::class,
+            MigrateBreakpoint::class,
+            MigrateStatus::class,
+            SeedCreate::class,
+            SeedRun::class,
+            FactoryCreate::class,
+        ]);
+    }
+}
diff --git a/vendor/topthink/think-migration/src/command/Migrate.php b/vendor/topthink/think-migration/src/command/Migrate.php
new file mode 100755
index 0000000..1d97973
--- /dev/null
+++ b/vendor/topthink/think-migration/src/command/Migrate.php
@@ -0,0 +1,146 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\migration\command;
+
+use Phinx\Db\Adapter\AdapterFactory;
+use Phinx\Db\Adapter\ProxyAdapter;
+use Phinx\Migration\AbstractMigration;
+use Phinx\Migration\MigrationInterface;
+use Phinx\Util\Util;
+use think\migration\Command;
+use think\migration\Migrator;
+
+abstract class Migrate extends Command
+{
+    /**
+     * @var array
+     */
+    protected $migrations;
+
+    protected function getPath()
+    {
+        return $this->app->getRootPath() . 'database' . DIRECTORY_SEPARATOR . 'migrations';
+    }
+
+    protected function executeMigration(MigrationInterface $migration, $direction = MigrationInterface::UP)
+    {
+        $this->output->writeln('');
+        $this->output->writeln(' ==' . ' <info>' . $migration->getVersion() . ' ' . $migration->getName() . ':</info>' . ' <comment>' . (MigrationInterface::UP === $direction ? 'migrating' : 'reverting') . '</comment>');
+
+        // Execute the migration and log the time elapsed.
+        $start = microtime(true);
+
+        $startTime = time();
+        $direction = (MigrationInterface::UP === $direction) ? MigrationInterface::UP : MigrationInterface::DOWN;
+        $migration->setAdapter($this->getAdapter());
+
+        // begin the transaction if the adapter supports it
+        if ($this->getAdapter()->hasTransactions()) {
+            $this->getAdapter()->beginTransaction();
+        }
+
+        // Run the migration
+        if (method_exists($migration, MigrationInterface::CHANGE)) {
+            if (MigrationInterface::DOWN === $direction) {
+                // Create an instance of the ProxyAdapter so we can record all
+                // of the migration commands for reverse playback
+                /** @var ProxyAdapter $proxyAdapter */
+                $proxyAdapter = AdapterFactory::instance()->getWrapper('proxy', $this->getAdapter());
+                $migration->setAdapter($proxyAdapter);
+                /** @noinspection PhpUndefinedMethodInspection */
+                $migration->change();
+                $proxyAdapter->executeInvertedCommands();
+                $migration->setAdapter($this->getAdapter());
+            } else {
+                /** @noinspection PhpUndefinedMethodInspection */
+                $migration->change();
+            }
+        } else {
+            $migration->{$direction}();
+        }
+
+        // commit the transaction if the adapter supports it
+        if ($this->getAdapter()->hasTransactions()) {
+            $this->getAdapter()->commitTransaction();
+        }
+
+        // Record it in the database
+        $this->getAdapter()
+            ->migrated($migration, $direction, date('Y-m-d H:i:s', $startTime), date('Y-m-d H:i:s', time()));
+
+        $end = microtime(true);
+
+        $this->output->writeln(' ==' . ' <info>' . $migration->getVersion() . ' ' . $migration->getName() . ':</info>' . ' <comment>' . (MigrationInterface::UP === $direction ? 'migrated' : 'reverted') . ' ' . sprintf('%.4fs', $end - $start) . '</comment>');
+    }
+
+    protected function getVersionLog()
+    {
+        return $this->getAdapter()->getVersionLog();
+    }
+
+    protected function getVersions()
+    {
+        return $this->getAdapter()->getVersions();
+    }
+
+    protected function getMigrations()
+    {
+        if (null === $this->migrations) {
+            $phpFiles = glob($this->getPath() . DIRECTORY_SEPARATOR . '*.php', defined('GLOB_BRACE') ? GLOB_BRACE : 0);
+
+            // filter the files to only get the ones that match our naming scheme
+            $fileNames = [];
+            /** @var Migrator[] $versions */
+            $versions = [];
+
+            foreach ($phpFiles as $filePath) {
+                if (Util::isValidMigrationFileName(basename($filePath))) {
+                    $version = Util::getVersionFromFileName(basename($filePath));
+
+                    if (isset($versions[$version])) {
+                        throw new \InvalidArgumentException(sprintf('Duplicate migration - "%s" has the same version as "%s"', $filePath, $versions[$version]->getVersion()));
+                    }
+
+                    // convert the filename to a class name
+                    $class = Util::mapFileNameToClassName(basename($filePath));
+
+                    if (isset($fileNames[$class])) {
+                        throw new \InvalidArgumentException(sprintf('Migration "%s" has the same name as "%s"', basename($filePath), $fileNames[$class]));
+                    }
+
+                    $fileNames[$class] = basename($filePath);
+
+                    // load the migration file
+                    /** @noinspection PhpIncludeInspection */
+                    require_once $filePath;
+                    if (!class_exists($class)) {
+                        throw new \InvalidArgumentException(sprintf('Could not find class "%s" in file "%s"', $class, $filePath));
+                    }
+
+                    // instantiate it
+                    $migration = new $class($version, $this->input, $this->output);
+
+                    if (!($migration instanceof AbstractMigration)) {
+                        throw new \InvalidArgumentException(sprintf('The class "%s" in file "%s" must extend \Phinx\Migration\AbstractMigration', $class, $filePath));
+                    }
+
+                    $versions[$version] = $migration;
+                }
+            }
+
+            ksort($versions);
+            $this->migrations = $versions;
+        }
+
+        return $this->migrations;
+    }
+}
diff --git a/vendor/topthink/think-migration/src/command/Seed.php b/vendor/topthink/think-migration/src/command/Seed.php
new file mode 100755
index 0000000..e7a2c06
--- /dev/null
+++ b/vendor/topthink/think-migration/src/command/Seed.php
@@ -0,0 +1,73 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\migration\command;
+
+use InvalidArgumentException;
+use Phinx\Seed\AbstractSeed;
+use Phinx\Util\Util;
+use think\migration\Command;
+use think\migration\Seeder;
+
+abstract class Seed extends Command
+{
+
+    /**
+     * @var array
+     */
+    protected $seeds;
+
+    protected function getPath()
+    {
+        return $this->app->getRootPath() . 'database' . DIRECTORY_SEPARATOR . 'seeds';
+    }
+
+    public function getSeeds()
+    {
+        if (null === $this->seeds) {
+            $phpFiles = glob($this->getPath() . DIRECTORY_SEPARATOR . '*.php', defined('GLOB_BRACE') ? GLOB_BRACE : 0);
+
+            // filter the files to only get the ones that match our naming scheme
+            $fileNames = [];
+            /** @var Seeder[] $seeds */
+            $seeds = [];
+
+            foreach ($phpFiles as $filePath) {
+                if (Util::isValidSeedFileName(basename($filePath))) {
+                    // convert the filename to a class name
+                    $class             = pathinfo($filePath, PATHINFO_FILENAME);
+                    $fileNames[$class] = basename($filePath);
+
+                    // load the seed file
+                    /** @noinspection PhpIncludeInspection */
+                    require_once $filePath;
+                    if (!class_exists($class)) {
+                        throw new InvalidArgumentException(sprintf('Could not find class "%s" in file "%s"', $class, $filePath));
+                    }
+
+                    // instantiate it
+                    $seed = new $class($this->input, $this->output);
+
+                    if (!($seed instanceof AbstractSeed)) {
+                        throw new InvalidArgumentException(sprintf('The class "%s" in file "%s" must extend \Phinx\Seed\AbstractSeed', $class, $filePath));
+                    }
+
+                    $seeds[$class] = $seed;
+                }
+            }
+
+            ksort($seeds);
+            $this->seeds = $seeds;
+        }
+
+        return $this->seeds;
+    }
+}
diff --git a/vendor/topthink/think-migration/src/command/factory/Create.php b/vendor/topthink/think-migration/src/command/factory/Create.php
new file mode 100755
index 0000000..a7b4684
--- /dev/null
+++ b/vendor/topthink/think-migration/src/command/factory/Create.php
@@ -0,0 +1,82 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\migration\command\factory;
+
+use InvalidArgumentException;
+use Phinx\Util\Util;
+use RuntimeException;
+use think\console\Command;
+use think\console\input\Argument as InputArgument;
+
+class Create extends Command
+{
+    protected function configure()
+    {
+        $this->setName('factory:create')
+            ->setDescription('Create a new model factory')
+            ->addArgument('name', InputArgument::REQUIRED, 'What is the name of the model?');
+    }
+
+    public function handle()
+    {
+        $path = $this->getPath();
+
+        if (!file_exists($path)) {
+            mkdir($path, 0755, true);
+        }
+
+        if (!is_dir($path)) {
+            throw new InvalidArgumentException(sprintf('Factory directory "%s" does not exist', $path));
+        }
+
+        if (!is_writable($path)) {
+            throw new InvalidArgumentException(sprintf('Factory directory "%s" is not writable', $path));
+        }
+
+        $path      = realpath($path);
+        $className = $this->input->getArgument('name');
+
+        if (!Util::isValidPhinxClassName($className)) {
+            throw new InvalidArgumentException(sprintf('The migration class name "%s" is invalid. Please use CamelCase format.', $className));
+        }
+
+        $filePath = $path . DIRECTORY_SEPARATOR . $className . '.php';
+
+        if (is_file($filePath)) {
+            throw new InvalidArgumentException(sprintf('The file "%s" already exists', $filePath));
+        }
+
+        // Load the alternative template if it is defined.
+        $contents = file_get_contents($this->getTemplate());
+
+        // inject the class names appropriate to this migration
+        $contents = strtr($contents, [
+            '"ModelClass"' => "\\app\\model\\" . $className . '::class',
+        ]);
+
+        if (false === file_put_contents($filePath, $contents)) {
+            throw new RuntimeException(sprintf('The file "%s" could not be written to', $path));
+        }
+
+        $this->output->writeln('<info>created</info> .' . str_replace(getcwd(), '', $filePath));
+    }
+
+    protected function getTemplate()
+    {
+        return __DIR__ . '/../stubs/factory.stub';
+    }
+
+    protected function getPath()
+    {
+        return $this->app->getRootPath() . 'database' . DIRECTORY_SEPARATOR . 'factories';
+    }
+}
diff --git a/vendor/topthink/think-migration/src/command/migrate/Breakpoint.php b/vendor/topthink/think-migration/src/command/migrate/Breakpoint.php
new file mode 100755
index 0000000..cfb2d32
--- /dev/null
+++ b/vendor/topthink/think-migration/src/command/migrate/Breakpoint.php
@@ -0,0 +1,92 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\migration\command\migrate;
+
+use think\console\Input;
+use think\console\input\Option as InputOption;
+use think\console\Output;
+use think\migration\command\Migrate;
+
+class Breakpoint extends Migrate
+{
+    protected function configure()
+    {
+        $this->setName('migrate:breakpoint')
+             ->setDescription('Manage breakpoints')
+             ->addOption('--target', '-t', InputOption::VALUE_REQUIRED, 'The version number to set or clear a breakpoint against')
+             ->addOption('--remove-all', '-r', InputOption::VALUE_NONE, 'Remove all breakpoints')
+             ->setHelp(<<<EOT
+                 The <info>breakpoint</info> command allows you to set or clear a breakpoint against a specific target to inhibit rollbacks beyond a certain target.
+If no target is supplied then the most recent migration will be used.
+You cannot specify un-migrated targets
+
+<info>php think migrate:breakpoint</info>
+<info>php think migrate:breakpoint -t 20110103081132</info>
+<info>php think migrate:breakpoint -r</info>
+EOT
+             );
+    }
+
+    protected function execute(Input $input, Output $output)
+    {
+        $version   = $input->getOption('target');
+        $removeAll = $input->getOption('remove-all');
+
+        if ($version && $removeAll) {
+            throw new \InvalidArgumentException('Cannot toggle a breakpoint and remove all breakpoints at the same time.');
+        }
+
+        // Remove all breakpoints
+        if ($removeAll) {
+            $this->removeBreakpoints();
+        } else {
+            // Toggle the breakpoint.
+            $this->toggleBreakpoint($version);
+        }
+    }
+
+    protected function toggleBreakpoint($version)
+    {
+        $migrations = $this->getMigrations();
+        $versions   = $this->getVersionLog();
+
+        if (empty($versions) || empty($migrations)) {
+            return;
+        }
+
+        if (null === $version) {
+            $lastVersion = end($versions);
+            $version     = $lastVersion['version'];
+        }
+
+        if (0 != $version && !isset($migrations[$version])) {
+            $this->output->writeln(sprintf('<comment>warning</comment> %s is not a valid version', $version));
+            return;
+        }
+
+        $this->getAdapter()->toggleBreakpoint($migrations[$version]);
+
+        $versions = $this->getVersionLog();
+
+        $this->output->writeln(' Breakpoint ' . ($versions[$version]['breakpoint'] ? 'set' : 'cleared') . ' for <info>' . $version . '</info>' . ' <comment>' . $migrations[$version]->getName() . '</comment>');
+    }
+
+    /**
+     * Remove all breakpoints
+     *
+     * @return void
+     */
+    protected function removeBreakpoints()
+    {
+        $this->output->writeln(sprintf(' %d breakpoints cleared.', $this->getAdapter()->resetAllBreakpoints()));
+    }
+}
diff --git a/vendor/topthink/think-migration/src/command/migrate/Create.php b/vendor/topthink/think-migration/src/command/migrate/Create.php
new file mode 100755
index 0000000..8f96de7
--- /dev/null
+++ b/vendor/topthink/think-migration/src/command/migrate/Create.php
@@ -0,0 +1,55 @@
+<?php
+// +----------------------------------------------------------------------
+// | TopThink [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016 http://www.topthink.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Author: zhangyajun <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\migration\command\migrate;
+
+use InvalidArgumentException;
+use RuntimeException;
+use think\console\Command;
+use think\console\Input;
+use think\console\input\Argument as InputArgument;
+use think\console\Output;
+use think\migration\Creator;
+
+class Create extends Command
+{
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function configure()
+    {
+        $this->setName('migrate:create')
+            ->setDescription('Create a new migration')
+            ->addArgument('name', InputArgument::REQUIRED, 'What is the name of the migration?')
+            ->setHelp(sprintf('%sCreates a new database migration%s', PHP_EOL, PHP_EOL));
+    }
+
+    /**
+     * Create the new migration.
+     *
+     * @param Input  $input
+     * @param Output $output
+     * @return void
+     * @throws InvalidArgumentException
+     * @throws RuntimeException
+     */
+    protected function execute(Input $input, Output $output)
+    {
+        /** @var Creator $creator */
+        $creator = $this->app->get('migration.creator');
+
+        $className = $input->getArgument('name');
+
+        $path = $creator->create($className);
+
+        $output->writeln('<info>created</info> .' . str_replace(getcwd(), '', realpath($path)));
+    }
+
+}
diff --git a/vendor/topthink/think-migration/src/command/migrate/Rollback.php b/vendor/topthink/think-migration/src/command/migrate/Rollback.php
new file mode 100755
index 0000000..e668ac7
--- /dev/null
+++ b/vendor/topthink/think-migration/src/command/migrate/Rollback.php
@@ -0,0 +1,146 @@
+<?php
+// +----------------------------------------------------------------------
+// | TopThink [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016 http://www.topthink.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Author: zhangyajun <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\migration\command\migrate;
+
+use Phinx\Migration\MigrationInterface;
+use think\console\input\Option as InputOption;
+use think\console\Input;
+use think\console\Output;
+use think\migration\command\Migrate;
+
+class Rollback extends Migrate
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function configure()
+    {
+        $this->setName('migrate:rollback')
+             ->setDescription('Rollback the last or to a specific migration')
+             ->addOption('--target', '-t', InputOption::VALUE_REQUIRED, 'The version number to rollback to')
+             ->addOption('--date', '-d', InputOption::VALUE_REQUIRED, 'The date to rollback to')
+             ->addOption('--force', '-f', InputOption::VALUE_NONE, 'Force rollback to ignore breakpoints')
+             ->setHelp(<<<EOT
+The <info>migrate:rollback</info> command reverts the last migration, or optionally up to a specific version
+
+<info>php think migrate:rollback</info>
+<info>php think migrate:rollback -t 20111018185412</info>
+<info>php think migrate:rollback -d 20111018</info>
+<info>php think migrate:rollback -v</info>
+
+EOT
+             );
+    }
+
+    /**
+     * Rollback the migration.
+     *
+     * @param Input  $input
+     * @param Output $output
+     * @return void
+     */
+    protected function execute(Input $input, Output $output)
+    {
+        $version = $input->getOption('target');
+        $date    = $input->getOption('date');
+        $force   = !!$input->getOption('force');
+
+        // rollback the specified environment
+        $start = microtime(true);
+        if (null !== $date) {
+            $this->rollbackToDateTime(new \DateTime($date), $force);
+        } else {
+            $this->rollback($version, $force);
+        }
+        $end = microtime(true);
+
+        $output->writeln('');
+        $output->writeln('<comment>All Done. Took ' . sprintf('%.4fs', $end - $start) . '</comment>');
+    }
+
+    protected function rollback($version = null, $force = false)
+    {
+        $migrations = $this->getMigrations();
+        $versionLog = $this->getVersionLog();
+        $versions   = array_keys($versionLog);
+
+        ksort($migrations);
+        sort($versions);
+
+        // Check we have at least 1 migration to revert
+        if (empty($versions) || $version == end($versions)) {
+            $this->output->writeln('<error>No migrations to rollback</error>');
+            return;
+        }
+
+        // If no target version was supplied, revert the last migration
+        if (null === $version) {
+            // Get the migration before the last run migration
+            $prev    = count($versions) - 2;
+            $version = $prev < 0 ? 0 : $versions[$prev];
+        } else {
+            // Get the first migration number
+            $first = $versions[0];
+
+            // If the target version is before the first migration, revert all migrations
+            if ($version < $first) {
+                $version = 0;
+            }
+        }
+
+        // Check the target version exists
+        if (0 !== $version && !isset($migrations[$version])) {
+            $this->output->writeln("<error>Target version ($version) not found</error>");
+            return;
+        }
+
+        // Revert the migration(s)
+        krsort($migrations);
+        foreach ($migrations as $migration) {
+            if ($migration->getVersion() <= $version) {
+                break;
+            }
+
+            if (in_array($migration->getVersion(), $versions)) {
+                if (isset($versionLog[$migration->getVersion()]) && 0 != $versionLog[$migration->getVersion()]['breakpoint'] && !$force) {
+                    $this->output->writeln('<error>Breakpoint reached. Further rollbacks inhibited.</error>');
+                    break;
+                }
+                $this->executeMigration($migration, MigrationInterface::DOWN);
+            }
+        }
+    }
+
+    protected function rollbackToDateTime(\DateTime $dateTime, $force = false)
+    {
+        $versions   = $this->getVersions();
+        $dateString = $dateTime->format('YmdHis');
+        sort($versions);
+
+        $earlierVersion      = null;
+        $availableMigrations = array_filter($versions, function ($version) use ($dateString, &$earlierVersion) {
+            if ($version <= $dateString) {
+                $earlierVersion = $version;
+            }
+            return $version >= $dateString;
+        });
+
+        if (count($availableMigrations) > 0) {
+            if (is_null($earlierVersion)) {
+                $this->output->writeln('Rolling back all migrations');
+                $migration = 0;
+            } else {
+                $this->output->writeln('Rolling back to version ' . $earlierVersion);
+                $migration = $earlierVersion;
+            }
+            $this->rollback($migration, $force);
+        }
+    }
+}
diff --git a/vendor/topthink/think-migration/src/command/migrate/Run.php b/vendor/topthink/think-migration/src/command/migrate/Run.php
new file mode 100755
index 0000000..4714f02
--- /dev/null
+++ b/vendor/topthink/think-migration/src/command/migrate/Run.php
@@ -0,0 +1,140 @@
+<?php
+// +----------------------------------------------------------------------
+// | TopThink [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016 http://www.topthink.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Author: zhangyajun <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\migration\command\migrate;
+
+use Phinx\Migration\MigrationInterface;
+use think\console\Input;
+use think\console\input\Option as InputOption;
+use think\console\Output;
+use think\migration\command\Migrate;
+
+class Run extends Migrate
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function configure()
+    {
+        $this->setName('migrate:run')
+            ->setDescription('Migrate the database')
+            ->addOption('--target', '-t', InputOption::VALUE_REQUIRED, 'The version number to migrate to')
+            ->addOption('--date', '-d', InputOption::VALUE_REQUIRED, 'The date to migrate to')
+            ->setHelp(<<<EOT
+The <info>migrate:run</info> command runs all available migrations, optionally up to a specific version
+
+<info>php think migrate:run</info>
+<info>php think migrate:run -t 20110103081132</info>
+<info>php think migrate:run -d 20110103</info>
+<info>php think migrate:run -v</info>
+
+EOT
+            );
+    }
+
+    /**
+     * Migrate the database.
+     *
+     * @param Input  $input
+     * @param Output $output
+     */
+    protected function execute(Input $input, Output $output)
+    {
+        $version = $input->getOption('target');
+        $date    = $input->getOption('date');
+
+        // run the migrations
+        $start = microtime(true);
+        if (null !== $date) {
+            $this->migrateToDateTime(new \DateTime($date));
+        } else {
+            $this->migrate($version);
+        }
+        $end = microtime(true);
+
+        $output->writeln('');
+        $output->writeln('<comment>All Done. Took ' . sprintf('%.4fs', $end - $start) . '</comment>');
+    }
+
+    public function migrateToDateTime(\DateTime $dateTime)
+    {
+        $versions   = array_keys($this->getMigrations());
+        $dateString = $dateTime->format('YmdHis');
+
+        $outstandingMigrations = array_filter($versions, function ($version) use ($dateString) {
+            return $version <= $dateString;
+        });
+
+        if (count($outstandingMigrations) > 0) {
+            $migration = max($outstandingMigrations);
+            $this->output->writeln('Migrating to version ' . $migration);
+            $this->migrate($migration);
+        }
+    }
+
+    protected function migrate($version = null)
+    {
+        $migrations = $this->getMigrations();
+        $versions   = $this->getVersions();
+        $current    = $this->getCurrentVersion();
+
+        if (empty($versions) && empty($migrations)) {
+            return;
+        }
+
+        if (null === $version) {
+            $version = max(array_merge($versions, array_keys($migrations)));
+        } else {
+            if (0 != $version && !isset($migrations[$version])) {
+                $this->output->writeln(sprintf('<comment>warning</comment> %s is not a valid version', $version));
+                return;
+            }
+        }
+
+        // are we migrating up or down?
+        $direction = $version > $current ? MigrationInterface::UP : MigrationInterface::DOWN;
+
+        if ($direction === MigrationInterface::DOWN) {
+            // run downs first
+            krsort($migrations);
+            foreach ($migrations as $migration) {
+                if ($migration->getVersion() <= $version) {
+                    break;
+                }
+
+                if (in_array($migration->getVersion(), $versions)) {
+                    $this->executeMigration($migration, MigrationInterface::DOWN);
+                }
+            }
+        }
+
+        ksort($migrations);
+        foreach ($migrations as $migration) {
+            if ($migration->getVersion() > $version) {
+                break;
+            }
+
+            if (!in_array($migration->getVersion(), $versions)) {
+                $this->executeMigration($migration, MigrationInterface::UP);
+            }
+        }
+    }
+
+    protected function getCurrentVersion()
+    {
+        $versions = $this->getVersions();
+        $version  = 0;
+
+        if (!empty($versions)) {
+            $version = end($versions);
+        }
+
+        return $version;
+    }
+}
diff --git a/vendor/topthink/think-migration/src/command/migrate/Status.php b/vendor/topthink/think-migration/src/command/migrate/Status.php
new file mode 100755
index 0000000..ccf424a
--- /dev/null
+++ b/vendor/topthink/think-migration/src/command/migrate/Status.php
@@ -0,0 +1,126 @@
+<?php
+// +----------------------------------------------------------------------
+// | TopThink [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016 http://www.topthink.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Author: zhangyajun <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\migration\command\migrate;
+
+use think\console\input\Option as InputOption;
+use think\console\Input;
+use think\console\Output;
+use think\migration\command\Migrate;
+
+class Status extends Migrate
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function configure()
+    {
+        $this->setName('migrate:status')
+             ->setDescription('Show migration status')
+             ->addOption('--format', '-f', InputOption::VALUE_REQUIRED, 'The output format: text or json. Defaults to text.')
+             ->setHelp(<<<EOT
+The <info>migrate:status</info> command prints a list of all migrations, along with their current status
+
+<info>php think migrate:status</info>
+<info>php think migrate:status -f json</info>
+EOT
+             );
+    }
+
+    /**
+     * Show the migration status.
+     *
+     * @param Input  $input
+     * @param Output $output
+     * @return integer 0 if all migrations are up, or an error code
+     */
+    protected function execute(Input $input, Output $output)
+    {
+        $format = $input->getOption('format');
+
+        if (null !== $format) {
+            $output->writeln('<info>using format</info> ' . $format);
+        }
+
+        // print the status
+        return $this->printStatus($format);
+    }
+
+    protected function printStatus($format = null)
+    {
+        $output     = $this->output;
+        $migrations = [];
+        if (count($this->getMigrations())) {
+            // TODO - rewrite using Symfony Table Helper as we already have this library
+            // included and it will fix formatting issues (e.g drawing the lines)
+            $output->writeln('');
+            $output->writeln(' Status  Migration ID    Started              Finished             Migration Name ');
+            $output->writeln('----------------------------------------------------------------------------------');
+
+            $versions      = $this->getVersionLog();
+            $maxNameLength = $versions ? max(array_map(function ($version) {
+                return strlen($version['migration_name']);
+            }, $versions)) : 0;
+
+            foreach ($this->getMigrations() as $migration) {
+                $version = array_key_exists($migration->getVersion(), $versions) ? $versions[$migration->getVersion()] : false;
+                if ($version) {
+                    $status = '     <info>up</info> ';
+                } else {
+                    $status = '   <error>down</error> ';
+                    $version = [];
+                    $version['start_time'] = $version['end_time'] = $version['breakpoint'] = '';
+                }
+                $maxNameLength = max($maxNameLength, strlen($migration->getName()));
+
+                $output->writeln(sprintf('%s %14.0f  %19s  %19s  <comment>%s</comment>', $status, $migration->getVersion(), $version['start_time'], $version['end_time'], $migration->getName()));
+
+                if ($version && $version['breakpoint']) {
+                    $output->writeln('         <error>BREAKPOINT SET</error>');
+                }
+
+                $migrations[] = [
+                    'migration_status' => trim(strip_tags($status)),
+                    'migration_id'     => sprintf('%14.0f', $migration->getVersion()),
+                    'migration_name'   => $migration->getName()
+                ];
+                unset($versions[$migration->getVersion()]);
+            }
+
+            if (count($versions)) {
+                foreach ($versions as $missing => $version) {
+                    $output->writeln(sprintf('     <error>up</error>  %14.0f  %19s  %19s  <comment>%s</comment>  <error>** MISSING **</error>', $missing, $version['start_time'], $version['end_time'], str_pad($version['migration_name'], $maxNameLength, ' ')));
+
+                    if ($version && $version['breakpoint']) {
+                        $output->writeln('         <error>BREAKPOINT SET</error>');
+                    }
+                }
+            }
+        } else {
+            // there are no migrations
+            $output->writeln('');
+            $output->writeln('There are no available migrations. Try creating one using the <info>create</info> command.');
+        }
+
+        // write an empty line
+        $output->writeln('');
+        if ($format !== null) {
+            switch ($format) {
+                case 'json':
+                    $output->writeln(json_encode([
+                        'pending_count' => count($this->getMigrations()),
+                        'migrations'    => $migrations
+                    ]));
+                    break;
+                default:
+                    $output->writeln('<info>Unsupported format: ' . $format . '</info>');
+            }
+        }
+    }
+}
diff --git a/vendor/topthink/think-migration/src/command/seed/Create.php b/vendor/topthink/think-migration/src/command/seed/Create.php
new file mode 100755
index 0000000..6873980
--- /dev/null
+++ b/vendor/topthink/think-migration/src/command/seed/Create.php
@@ -0,0 +1,83 @@
+<?php
+// +----------------------------------------------------------------------
+// | TopThink [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016 http://www.topthink.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Author: zhangyajun <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\migration\command\seed;
+
+use Phinx\Util\Util;
+use think\console\Input;
+use think\console\input\Argument as InputArgument;
+use think\console\Output;
+use think\migration\command\Seed;
+
+class Create extends Seed
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function configure()
+    {
+        $this->setName('seed:create')
+            ->setDescription('Create a new database seeder')
+            ->addArgument('name', InputArgument::REQUIRED, 'What is the name of the seeder?')
+            ->setHelp(sprintf('%sCreates a new database seeder%s', PHP_EOL, PHP_EOL));
+    }
+
+    /**
+     * Create the new seeder.
+     *
+     * @param Input  $input
+     * @param Output $output
+     * @return void
+     * @throws \InvalidArgumentException
+     * @throws \RuntimeException
+     */
+    protected function execute(Input $input, Output $output)
+    {
+        $path = $this->getPath();
+
+        if (!file_exists($path)) {
+            mkdir($path, 0755, true);
+        }
+
+        $this->verifyMigrationDirectory($path);
+
+        $path = realpath($path);
+
+        $className = $input->getArgument('name');
+
+        if (!Util::isValidPhinxClassName($className)) {
+            throw new \InvalidArgumentException(sprintf('The seed class name "%s" is invalid. Please use CamelCase format', $className));
+        }
+
+        // Compute the file path
+        $filePath = $path . DIRECTORY_SEPARATOR . $className . '.php';
+
+        if (is_file($filePath)) {
+            throw new \InvalidArgumentException(sprintf('The file "%s" already exists', basename($filePath)));
+        }
+
+        // inject the class names appropriate to this seeder
+        $contents = file_get_contents($this->getTemplate());
+        $classes  = [
+            'SeederClass' => $className,
+        ];
+        $contents = strtr($contents, $classes);
+
+        if (false === file_put_contents($filePath, $contents)) {
+            throw new \RuntimeException(sprintf('The file "%s" could not be written to', $path));
+        }
+
+        $output->writeln('<info>created</info> .' . str_replace(getcwd(), '', $filePath));
+    }
+
+    protected function getTemplate()
+    {
+        return __DIR__ . '/../stubs/seed.stub';
+    }
+}
diff --git a/vendor/topthink/think-migration/src/command/seed/Run.php b/vendor/topthink/think-migration/src/command/seed/Run.php
new file mode 100755
index 0000000..a0ac8f9
--- /dev/null
+++ b/vendor/topthink/think-migration/src/command/seed/Run.php
@@ -0,0 +1,107 @@
+<?php
+// +----------------------------------------------------------------------
+// | TopThink [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016 http://www.topthink.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Author: zhangyajun <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\migration\command\seed;
+
+use Phinx\Seed\SeedInterface;
+use think\console\Input;
+use think\console\input\Option as InputOption;
+use think\console\Output;
+use think\migration\command\Seed;
+
+class Run extends Seed
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function configure()
+    {
+        $this->setName('seed:run')
+             ->setDescription('Run database seeders')
+             ->addOption('--seed', '-s', InputOption::VALUE_REQUIRED, 'What is the name of the seeder?')
+             ->setHelp(<<<EOT
+                The <info>seed:run</info> command runs all available or individual seeders
+
+<info>php think seed:run</info>
+<info>php think seed:run -s UserSeeder</info>
+<info>php think seed:run -v</info>
+
+EOT
+             );
+    }
+
+    /**
+     * Run database seeders.
+     *
+     * @param Input  $input
+     * @param Output $output
+     * @return void
+     */
+    protected function execute(Input $input, Output $output)
+    {
+        $seed = $input->getOption('seed');
+
+        // run the seed(ers)
+        $start = microtime(true);
+        $this->seed($seed);
+        $end = microtime(true);
+
+        $output->writeln('');
+        $output->writeln('<comment>All Done. Took ' . sprintf('%.4fs', $end - $start) . '</comment>');
+    }
+
+    public function seed($seed = null)
+    {
+        $seeds = $this->getSeeds();
+
+        if (null === $seed) {
+            // run all seeders
+            foreach ($seeds as $seeder) {
+                if (array_key_exists($seeder->getName(), $seeds)) {
+                    $this->executeSeed($seeder);
+                }
+            }
+        } else {
+            // run only one seeder
+            if (array_key_exists($seed, $seeds)) {
+                $this->executeSeed($seeds[$seed]);
+            } else {
+                throw new \InvalidArgumentException(sprintf('The seed class "%s" does not exist', $seed));
+            }
+        }
+    }
+
+    protected function executeSeed(SeedInterface $seed)
+    {
+        $this->output->writeln('');
+        $this->output->writeln(' ==' . ' <info>' . $seed->getName() . ':</info>' . ' <comment>seeding</comment>');
+
+        // Execute the seeder and log the time elapsed.
+        $start = microtime(true);
+        $seed->setAdapter($this->getAdapter());
+
+        // begin the transaction if the adapter supports it
+        if ($this->getAdapter()->hasTransactions()) {
+            $this->getAdapter()->beginTransaction();
+        }
+
+        // Run the seeder
+        if (method_exists($seed, SeedInterface::RUN)) {
+            $seed->run();
+        }
+
+        // commit the transaction if the adapter supports it
+        if ($this->getAdapter()->hasTransactions()) {
+            $this->getAdapter()->commitTransaction();
+        }
+        $end = microtime(true);
+
+        $this->output->writeln(' ==' . ' <info>' . $seed->getName() . ':</info>' . ' <comment>seeded' . ' ' . sprintf('%.4fs', $end - $start) . '</comment>');
+    }
+}
diff --git a/vendor/topthink/think-migration/src/command/stubs/factory.stub b/vendor/topthink/think-migration/src/command/stubs/factory.stub
new file mode 100755
index 0000000..eef91dc
--- /dev/null
+++ b/vendor/topthink/think-migration/src/command/stubs/factory.stub
@@ -0,0 +1,11 @@
+<?php
+
+use Faker\Generator as Faker;
+use think\migration\Factory;
+
+/** @var Factory $factory */
+$factory->define("ModelClass", function (Faker $faker) {
+    return [
+        //
+    ];
+});
diff --git a/vendor/topthink/think-migration/src/command/stubs/migrate.stub b/vendor/topthink/think-migration/src/command/stubs/migrate.stub
new file mode 100755
index 0000000..f11d29a
--- /dev/null
+++ b/vendor/topthink/think-migration/src/command/stubs/migrate.stub
@@ -0,0 +1,33 @@
+<?php
+
+use think\migration\Migrator;
+use think\migration\db\Column;
+
+class MigratorClass extends Migrator
+{
+    /**
+     * Change Method.
+     *
+     * Write your reversible migrations using this method.
+     *
+     * More information on writing migrations is available here:
+     * http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
+     *
+     * The following commands can be used in this method and Phinx will
+     * automatically reverse them when rolling back:
+     *
+     *    createTable
+     *    renameTable
+     *    addColumn
+     *    renameColumn
+     *    addIndex
+     *    addForeignKey
+     *
+     * Remember to call "create()" or "update()" and NOT "save()" when working
+     * with the Table class.
+     */
+    public function change()
+    {
+
+    }
+}
diff --git a/vendor/topthink/think-migration/src/command/stubs/seed.stub b/vendor/topthink/think-migration/src/command/stubs/seed.stub
new file mode 100755
index 0000000..8e1fa03
--- /dev/null
+++ b/vendor/topthink/think-migration/src/command/stubs/seed.stub
@@ -0,0 +1,19 @@
+<?php
+
+use think\migration\Seeder;
+
+class SeederClass extends Seeder
+{
+    /**
+     * Run Method.
+     *
+     * Write your database seeder using this method.
+     *
+     * More information on writing seeders is available here:
+     * http://docs.phinx.org/en/latest/seeding.html
+     */
+    public function run()
+    {
+
+    }
+}
\ No newline at end of file
diff --git a/vendor/topthink/think-migration/src/db/Column.php b/vendor/topthink/think-migration/src/db/Column.php
new file mode 100755
index 0000000..3b5cb04
--- /dev/null
+++ b/vendor/topthink/think-migration/src/db/Column.php
@@ -0,0 +1,171 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\migration\db;
+
+use Phinx\Db\Adapter\AdapterInterface;
+use Phinx\Db\Adapter\MysqlAdapter;
+
+class Column extends \Phinx\Db\Table\Column
+{
+    protected $unique = false;
+
+    public function setNullable()
+    {
+        return $this->setNull(true);
+    }
+
+    public function setUnsigned()
+    {
+        return $this->setSigned(false);
+    }
+
+    public function setUnique()
+    {
+        $this->unique = true;
+        return $this;
+    }
+
+    public function getUnique()
+    {
+        return $this->unique;
+    }
+
+    public function isUnique()
+    {
+        return $this->getUnique();
+    }
+
+    public static function make($name, $type, $options = [])
+    {
+        $column = new self();
+        $column->setName($name);
+        $column->setType($type);
+        $column->setOptions($options);
+        return $column;
+    }
+
+    public static function bigInteger($name)
+    {
+        return self::make($name, AdapterInterface::PHINX_TYPE_BIG_INTEGER);
+    }
+
+    public static function binary($name)
+    {
+        return self::make($name, AdapterInterface::PHINX_TYPE_BLOB);
+    }
+
+    public static function boolean($name)
+    {
+        return self::make($name, AdapterInterface::PHINX_TYPE_BOOLEAN);
+    }
+
+    public static function char($name, $length = 255)
+    {
+        return self::make($name, AdapterInterface::PHINX_TYPE_CHAR, compact('length'));
+    }
+
+    public static function date($name)
+    {
+        return self::make($name, AdapterInterface::PHINX_TYPE_DATE);
+    }
+
+    public static function dateTime($name)
+    {
+        return self::make($name, AdapterInterface::PHINX_TYPE_DATETIME);
+    }
+
+    public static function decimal($name, $precision = 8, $scale = 2)
+    {
+        return self::make($name, AdapterInterface::PHINX_TYPE_DECIMAL, compact('precision', 'scale'));
+    }
+
+    public static function enum($name, array $values)
+    {
+        return self::make($name, AdapterInterface::PHINX_TYPE_ENUM, compact('values'));
+    }
+
+    public static function float($name)
+    {
+        return self::make($name, AdapterInterface::PHINX_TYPE_FLOAT);
+    }
+
+    public static function integer($name)
+    {
+        return self::make($name, AdapterInterface::PHINX_TYPE_INTEGER);
+    }
+
+    public static function json($name)
+    {
+        return self::make($name, AdapterInterface::PHINX_TYPE_JSON);
+    }
+
+    public static function jsonb($name)
+    {
+        return self::make($name, AdapterInterface::PHINX_TYPE_JSONB);
+    }
+
+    public static function longText($name)
+    {
+        return self::make($name, AdapterInterface::PHINX_TYPE_TEXT, ['length' => MysqlAdapter::TEXT_LONG]);
+    }
+
+    public static function mediumInteger($name)
+    {
+        return self::make($name, AdapterInterface::PHINX_TYPE_INTEGER, ['length' => MysqlAdapter::INT_MEDIUM]);
+    }
+
+    public static function mediumText($name)
+    {
+        return self::make($name, AdapterInterface::PHINX_TYPE_TEXT, ['length' => MysqlAdapter::TEXT_MEDIUM]);
+    }
+
+    public static function smallInteger($name)
+    {
+        return self::make($name, AdapterInterface::PHINX_TYPE_INTEGER, ['length' => MysqlAdapter::INT_SMALL]);
+    }
+
+    public static function string($name, $length = 255)
+    {
+        return self::make($name, AdapterInterface::PHINX_TYPE_STRING, compact('length'));
+    }
+
+    public static function text($name)
+    {
+        return self::make($name, AdapterInterface::PHINX_TYPE_TEXT);
+    }
+
+    public static function time($name)
+    {
+        return self::make($name, AdapterInterface::PHINX_TYPE_TIME);
+    }
+
+    public static function tinyInteger($name)
+    {
+        return self::make($name, AdapterInterface::PHINX_TYPE_INTEGER, ['length' => MysqlAdapter::INT_TINY]);
+    }
+
+    public static function unsignedInteger($name)
+    {
+        return self::integer($name)->setUnSigned();
+    }
+
+    public static function timestamp($name)
+    {
+        return self::make($name, AdapterInterface::PHINX_TYPE_TIMESTAMP);
+    }
+
+    public static function uuid($name)
+    {
+        return self::make($name, AdapterInterface::PHINX_TYPE_UUID);
+    }
+
+}
diff --git a/vendor/topthink/think-migration/src/db/Table.php b/vendor/topthink/think-migration/src/db/Table.php
new file mode 100755
index 0000000..cf4daa9
--- /dev/null
+++ b/vendor/topthink/think-migration/src/db/Table.php
@@ -0,0 +1,135 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\migration\db;
+
+use Phinx\Db\Table\Index;
+
+class Table extends \Phinx\Db\Table
+{
+    /**
+     * 设置id
+     * @param $id
+     * @return $this
+     */
+    public function setId($id)
+    {
+        $this->options['id'] = $id;
+        return $this;
+    }
+
+    /**
+     * 设置主键
+     * @param $key
+     * @return $this
+     */
+    public function setPrimaryKey($key)
+    {
+        $this->options['primary_key'] = $key;
+        return $this;
+    }
+
+    /**
+     * 设置引擎
+     * @param $engine
+     * @return $this
+     */
+    public function setEngine($engine)
+    {
+        $this->options['engine'] = $engine;
+        return $this;
+    }
+
+    /**
+     * 设置表注释
+     * @param $comment
+     * @return $this
+     */
+    public function setComment($comment)
+    {
+        $this->options['comment'] = $comment;
+        return $this;
+    }
+
+    /**
+     * 设置排序比对方法
+     * @param $collation
+     * @return $this
+     */
+    public function setCollation($collation)
+    {
+        $this->options['collation'] = $collation;
+        return $this;
+    }
+
+    public function addSoftDelete()
+    {
+        $this->addColumn(Column::timestamp('delete_time')->setNullable());
+        return $this;
+    }
+
+    public function addMorphs($name, $indexName = null)
+    {
+        $this->addColumn(Column::unsignedInteger("{$name}_id"));
+        $this->addColumn(Column::string("{$name}_type"));
+        $this->addIndex(["{$name}_id", "{$name}_type"], ['name' => $indexName]);
+        return $this;
+    }
+
+    public function addNullableMorphs($name, $indexName = null)
+    {
+        $this->addColumn(Column::unsignedInteger("{$name}_id")->setNullable());
+        $this->addColumn(Column::string("{$name}_type")->setNullable());
+        $this->addIndex(["{$name}_id", "{$name}_type"], ['name' => $indexName]);
+        return $this;
+    }
+
+    /**
+     * @param string $createdAtColumnName
+     * @param string $updatedAtColumnName
+     * @return \Phinx\Db\Table|Table
+     */
+    public function addTimestamps($createdAtColumnName = 'create_time', $updatedAtColumnName = 'update_time')
+    {
+        return parent::addTimestamps($createdAtColumnName, $updatedAtColumnName);
+    }
+
+    /**
+     * @param \Phinx\Db\Table\Column|string $columnName
+     * @param null                          $type
+     * @param array                         $options
+     * @return \Phinx\Db\Table|Table
+     */
+    public function addColumn($columnName, $type = null, $options = [])
+    {
+        if ($columnName instanceof Column && $columnName->getUnique()) {
+            $index = new Index();
+            $index->setColumns([$columnName->getName()]);
+            $index->setType(Index::UNIQUE);
+            $this->addIndex($index);
+        }
+        return parent::addColumn($columnName, $type, $options);
+    }
+
+    /**
+     * @param string $columnName
+     * @param null   $newColumnType
+     * @param array  $options
+     * @return \Phinx\Db\Table|Table
+     */
+    public function changeColumn($columnName, $newColumnType = null, $options = [])
+    {
+        if ($columnName instanceof \Phinx\Db\Table\Column) {
+            return parent::changeColumn($columnName->getName(), $columnName, $options);
+        }
+        return parent::changeColumn($columnName, $newColumnType, $options);
+    }
+}
diff --git a/vendor/topthink/think-migration/src/helper.php b/vendor/topthink/think-migration/src/helper.php
new file mode 100755
index 0000000..6dc47ab
--- /dev/null
+++ b/vendor/topthink/think-migration/src/helper.php
@@ -0,0 +1,40 @@
+<?php
+
+use think\migration\Factory;
+use think\migration\FactoryBuilder;
+
+if (!function_exists('factory')) {
+    /**
+     * Create a model factory builder for a given class, name, and amount.
+     *
+     * @param mixed  class|class,name|class,amount|class,name,amount
+     * @return FactoryBuilder
+     */
+    function factory()
+    {
+        /** @var Factory $factory */
+        $factory = app(Factory::class);
+
+        $arguments = func_get_args();
+
+        if (isset($arguments[1]) && is_string($arguments[1])) {
+            return $factory->of($arguments[0], $arguments[1])->times($arguments[2] ?? null);
+        } elseif (isset($arguments[1])) {
+            return $factory->of($arguments[0])->times($arguments[1]);
+        }
+
+        return $factory->of($arguments[0]);
+    }
+}
+
+if (!function_exists('database_path')) {
+    /**
+     * 获取数据迁移脚本地址
+     * @param string $path
+     * @return string
+     */
+    function database_path($path = '')
+    {
+        return app()->getRootPath() . 'database' . DIRECTORY_SEPARATOR . $path;
+    }
+}
diff --git a/vendor/topthink/think-multi-app/LICENSE b/vendor/topthink/think-multi-app/LICENSE
new file mode 100755
index 0000000..261eeb9
--- /dev/null
+++ b/vendor/topthink/think-multi-app/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/vendor/topthink/think-multi-app/README.md b/vendor/topthink/think-multi-app/README.md
new file mode 100755
index 0000000..a746fa7
--- /dev/null
+++ b/vendor/topthink/think-multi-app/README.md
@@ -0,0 +1,14 @@
+# think-multi-app
+
+用于ThinkPHP6+的多应用支持
+
+## 安装
+
+~~~
+composer require topthink/think-multi-app
+~~~
+
+## 使用
+
+用法参考ThinkPHP6完全开发手册[多应用模式](https://www.kancloud.cn/manual/thinkphp6_0/1297876)章节。
+
diff --git a/vendor/topthink/think-multi-app/composer.json b/vendor/topthink/think-multi-app/composer.json
new file mode 100755
index 0000000..92d620e
--- /dev/null
+++ b/vendor/topthink/think-multi-app/composer.json
@@ -0,0 +1,28 @@
+{
+    "name": "topthink/think-multi-app",
+    "description": "thinkphp6 multi app support",
+    "license": "Apache-2.0",
+    "authors": [
+        {
+            "name": "liu21st",
+            "email": "liu21st@gmail.com"
+        }
+    ],
+    "require": {
+    	"php": ">=7.1.0",
+        "topthink/framework": "^6.0.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "think\\app\\": "src"
+        }
+    },
+    "extra": {
+        "think":{
+            "services":[
+                "think\\app\\Service"
+            ]
+        }
+    },    
+    "minimum-stability": "dev"
+}
diff --git a/vendor/topthink/think-multi-app/src/MultiApp.php b/vendor/topthink/think-multi-app/src/MultiApp.php
new file mode 100755
index 0000000..b0ac260
--- /dev/null
+++ b/vendor/topthink/think-multi-app/src/MultiApp.php
@@ -0,0 +1,245 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\app;
+
+use Closure;
+use think\App;
+use think\exception\HttpException;
+use think\Request;
+use think\Response;
+
+/**
+ * 多应用模式支持
+ */
+class MultiApp
+{
+
+    /** @var App */
+    protected $app;
+
+    /**
+     * 应用名称
+     * @var string
+     */
+    protected $name;
+
+    /**
+     * 应用名称
+     * @var string
+     */
+    protected $appName;
+
+    /**
+     * 应用路径
+     * @var string
+     */
+    protected $path;
+
+    public function __construct(App $app)
+    {
+        $this->app  = $app;
+        $this->name = $this->app->http->getName();
+        $this->path = $this->app->http->getPath();
+    }
+
+    /**
+     * 多应用解析
+     * @access public
+     * @param Request $request
+     * @param Closure $next
+     * @return Response
+     */
+    public function handle($request, Closure $next)
+    {
+        if (!$this->parseMultiApp()) {
+            return $next($request);
+        }
+
+        return $this->app->middleware->pipeline('app')
+            ->send($request)
+            ->then(function ($request) use ($next) {
+                return $next($request);
+            });
+    }
+
+    /**
+     * 获取路由目录
+     * @access protected
+     * @return string
+     */
+    protected function getRoutePath(): string
+    {
+        return $this->app->getAppPath() . 'route' . DIRECTORY_SEPARATOR;
+    }
+
+    /**
+     * 解析多应用
+     * @return bool
+     */
+    protected function parseMultiApp(): bool
+    {
+        $scriptName = $this->getScriptName();
+        $defaultApp = $this->app->config->get('app.default_app') ?: 'index';
+
+        if ($this->name || ($scriptName && !in_array($scriptName, ['index', 'router', 'think']))) {
+            $appName = $this->name ?: $scriptName;
+            $this->app->http->setBind();
+        } else {
+            // 自动多应用识别
+            $this->app->http->setBind(false);
+            $appName       = null;
+            $this->appName = '';
+
+            $bind = $this->app->config->get('app.domain_bind', []);
+
+            if (!empty($bind)) {
+                // 获取当前子域名
+                $subDomain = $this->app->request->subDomain();
+                $domain    = $this->app->request->host(true);
+
+                if (isset($bind[$domain])) {
+                    $appName = $bind[$domain];
+                    $this->app->http->setBind();
+                } elseif (isset($bind[$subDomain])) {
+                    $appName = $bind[$subDomain];
+                    $this->app->http->setBind();
+                } elseif (isset($bind['*'])) {
+                    $appName = $bind['*'];
+                    $this->app->http->setBind();
+                }
+            }
+
+            if (!$this->app->http->isBind()) {
+                $path = $this->app->request->pathinfo();
+                $map  = $this->app->config->get('app.app_map', []);
+                $deny = $this->app->config->get('app.deny_app_list', []);
+                $name = current(explode('/', $path));
+
+                if (strpos($name, '.')) {
+                    $name = strstr($name, '.', true);
+                }
+
+                if (isset($map[$name])) {
+                    if ($map[$name] instanceof Closure) {
+                        $result  = call_user_func_array($map[$name], [$this->app]);
+                        $appName = $result ?: $name;
+                    } else {
+                        $appName = $map[$name];
+                    }
+                } elseif ($name && (false !== array_search($name, $map) || in_array($name, $deny))) {
+                    throw new HttpException(404, 'app not exists:' . $name);
+                } elseif ($name && isset($map['*'])) {
+                    $appName = $map['*'];
+                } else {
+                    $appName = $name ?: $defaultApp;
+                    $appPath = $this->path ?: $this->app->getBasePath() . $appName . DIRECTORY_SEPARATOR;
+
+                    if (!is_dir($appPath)) {
+                        $express = $this->app->config->get('app.app_express', false);
+                        if ($express) {
+                            $this->setApp($defaultApp);
+                            return true;
+                        } else {
+                            return false;
+                        }
+                    }
+                }
+
+                if ($name) {
+                    $this->app->request->setRoot('/' . $name);
+                    $this->app->request->setPathinfo(strpos($path, '/') ? ltrim(strstr($path, '/'), '/') : '');
+                }
+            }
+        }
+
+        $this->setApp($appName ?: $defaultApp);
+        return true;
+    }
+
+    /**
+     * 获取当前运行入口名称
+     * @access protected
+     * @codeCoverageIgnore
+     * @return string
+     */
+    protected function getScriptName(): string
+    {
+        if (isset($_SERVER['SCRIPT_FILENAME'])) {
+            $file = $_SERVER['SCRIPT_FILENAME'];
+        } elseif (isset($_SERVER['argv'][0])) {
+            $file = realpath($_SERVER['argv'][0]);
+        }
+
+        return isset($file) ? pathinfo($file, PATHINFO_FILENAME) : '';
+    }
+
+    /**
+     * 设置应用
+     * @param string $appName
+     */
+    protected function setApp(string $appName): void
+    {
+        $this->appName = $appName;
+        $this->app->http->name($appName);
+
+        $appPath = $this->path ?: $this->app->getBasePath() . $appName . DIRECTORY_SEPARATOR;
+
+        $this->app->setAppPath($appPath);
+        // 设置应用命名空间
+        $this->app->setNamespace($this->app->config->get('app.app_namespace') ?: 'app\\' . $appName);
+
+        if (is_dir($appPath)) {
+            $this->app->setRuntimePath($this->app->getRuntimePath() . $appName . DIRECTORY_SEPARATOR);
+            $this->app->http->setRoutePath($this->getRoutePath());
+
+            //加载应用
+            $this->loadApp($appName, $appPath);
+        }
+    }
+
+    /**
+     * 加载应用文件
+     * @param string $appName 应用名
+     * @return void
+     */
+    protected function loadApp(string $appName, string $appPath): void
+    {
+        if (is_file($appPath . 'common.php')) {
+            include_once $appPath . 'common.php';
+        }
+
+        $files = [];
+
+        $files = array_merge($files, glob($appPath . 'config' . DIRECTORY_SEPARATOR . '*' . $this->app->getConfigExt()));
+
+        foreach ($files as $file) {
+            $this->app->config->load($file, pathinfo($file, PATHINFO_FILENAME));
+        }
+
+        if (is_file($appPath . 'event.php')) {
+            $this->app->loadEvent(include $appPath . 'event.php');
+        }
+
+        if (is_file($appPath . 'middleware.php')) {
+            $this->app->middleware->import(include $appPath . 'middleware.php', 'app');
+        }
+
+        if (is_file($appPath . 'provider.php')) {
+            $this->app->bind(include $appPath . 'provider.php');
+        }
+
+        // 加载应用默认语言包
+        $this->app->loadLangPack($this->app->lang->defaultLangSet());
+    }
+
+}
diff --git a/vendor/topthink/think-multi-app/src/Service.php b/vendor/topthink/think-multi-app/src/Service.php
new file mode 100755
index 0000000..cdc90b4
--- /dev/null
+++ b/vendor/topthink/think-multi-app/src/Service.php
@@ -0,0 +1,32 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+namespace think\app;
+
+use think\Service as BaseService;
+
+class Service extends BaseService
+{
+    public function boot()
+    {
+        $this->app->event->listen('HttpRun', function () {
+            $this->app->middleware->add(MultiApp::class);
+        });
+
+        $this->commands([
+            'build' => command\Build::class,
+            'clear' => command\Clear::class,
+        ]);
+
+        $this->app->bind([
+            'think\route\Url' => Url::class,
+        ]);
+    }
+}
diff --git a/vendor/topthink/think-multi-app/src/Url.php b/vendor/topthink/think-multi-app/src/Url.php
new file mode 100755
index 0000000..7bd6057
--- /dev/null
+++ b/vendor/topthink/think-multi-app/src/Url.php
@@ -0,0 +1,232 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\app;
+
+use think\App;
+use think\Route;
+use think\route\Url as UrlBuild;
+
+/**
+ * 路由地址生成
+ */
+class Url extends UrlBuild
+{
+    /**
+     * 直接解析URL地址
+     * @access protected
+     * @param  string      $url URL
+     * @param  string|bool $domain Domain
+     * @return string
+     */
+    protected function parseUrl(string $url, &$domain): string
+    {
+        $request = $this->app->request;
+
+        if (0 === strpos($url, '/')) {
+            // 直接作为路由地址解析
+            $url = substr($url, 1);
+        } elseif (false !== strpos($url, '\\')) {
+            // 解析到类
+            $url = ltrim(str_replace('\\', '/', $url), '/');
+        } elseif (0 === strpos($url, '@')) {
+            // 解析到控制器
+            $url = substr($url, 1);
+        } elseif ('' === $url) {
+            $url = $this->getAppName() . '/' . $request->controller() . '/' . $request->action();
+        } else {
+            // 解析到 应用/控制器/操作
+            $controller = $request->controller();
+            $app        = $this->getAppName();
+            $path       = explode('/', $url);
+            $action     = array_pop($path);
+            $controller = empty($path) ? $controller : array_pop($path);
+            $app        = empty($path) ? $app : array_pop($path);
+            $url        = $controller . '/' . $action;
+            $bind       = $this->app->config->get('app.domain_bind', []);
+
+            if ($key = array_search($this->app->http->getName(), $bind)) {
+                isset($bind[$_SERVER['SERVER_NAME']]) && $domain = $_SERVER['SERVER_NAME'];
+
+                $domain = is_bool($domain) ? $key : $domain;
+            } else {
+                $url = $app . '/' . $url;
+            }
+        }
+
+        return $url;
+    }
+
+    public function build()
+    {
+        // 解析URL
+        $url     = $this->url;
+        $suffix  = $this->suffix;
+        $domain  = $this->domain;
+        $request = $this->app->request;
+        $vars    = $this->vars;
+
+        if (0 === strpos($url, '[') && $pos = strpos($url, ']')) {
+            // [name] 表示使用路由命名标识生成URL
+            $name = substr($url, 1, $pos - 1);
+            $url  = 'name' . substr($url, $pos + 1);
+        }
+
+        if (false === strpos($url, '://') && 0 !== strpos($url, '/')) {
+            $info = parse_url($url);
+            $url  = !empty($info['path']) ? $info['path'] : '';
+
+            if (isset($info['fragment'])) {
+                // 解析锚点
+                $anchor = $info['fragment'];
+
+                if (false !== strpos($anchor, '?')) {
+                    // 解析参数
+                    list($anchor, $info['query']) = explode('?', $anchor, 2);
+                }
+
+                if (false !== strpos($anchor, '@')) {
+                    // 解析域名
+                    list($anchor, $domain) = explode('@', $anchor, 2);
+                }
+            } elseif (strpos($url, '@') && false === strpos($url, '\\')) {
+                // 解析域名
+                list($url, $domain) = explode('@', $url, 2);
+            }
+        }
+
+        if ($url) {
+            $checkName   = isset($name) ? $name : $url . (isset($info['query']) ? '?' . $info['query'] : '');
+            $checkDomain = $domain && is_string($domain) ? $domain : null;
+
+            $rule = $this->route->getName($checkName, $checkDomain);
+
+            if (empty($rule) && isset($info['query'])) {
+                $rule = $this->route->getName($url, $checkDomain);
+                // 解析地址里面参数 合并到vars
+                parse_str($info['query'], $params);
+                $vars = array_merge($params, $vars);
+                unset($info['query']);
+            }
+        }
+
+        if (!empty($rule) && $match = $this->getRuleUrl($rule, $vars, $domain)) {
+            // 匹配路由命名标识
+            $url = $match[0];
+
+            if ($domain && !empty($match[1])) {
+                $domain = $match[1];
+            }
+
+            if (!is_null($match[2])) {
+                $suffix = $match[2];
+            }
+
+            if (!$this->app->http->isBind()) {
+                $app = $this->getAppName();
+                $url = $app . '/' . $url;
+            }
+        } elseif (!empty($rule) && isset($name)) {
+            throw new \InvalidArgumentException('route name not exists:' . $name);
+        } else {
+            // 检测URL绑定
+            $bind = $this->route->getDomainBind($domain && is_string($domain) ? $domain : null);
+
+            if ($bind && 0 === strpos($url, $bind)) {
+                $url = substr($url, strlen($bind) + 1);
+            } else {
+                $binds = $this->route->getBind();
+
+                foreach ($binds as $key => $val) {
+                    if (is_string($val) && 0 === strpos($url, $val) && substr_count($val, '/') > 1) {
+                        $url    = substr($url, strlen($val) + 1);
+                        $domain = $key;
+                        break;
+                    }
+                }
+            }
+
+            // 路由标识不存在 直接解析
+            $url = $this->parseUrl($url, $domain);
+
+            if (isset($info['query'])) {
+                // 解析地址里面参数 合并到vars
+                parse_str($info['query'], $params);
+                $vars = array_merge($params, $vars);
+            }
+        }
+
+        // 还原URL分隔符
+        $depr = $this->route->config('pathinfo_depr');
+        $url  = str_replace('/', $depr, $url);
+
+        $file = $request->baseFile();
+        if ($file && 0 !== strpos($request->url(), $file)) {
+            $file = str_replace('\\', '/', dirname($file));
+        }
+
+        $url = rtrim($file, '/') . '/' . ltrim($url, '/');
+
+        // URL后缀
+        if ('/' == substr($url, -1) || '' == $url) {
+            $suffix = '';
+        } else {
+            $suffix = $this->parseSuffix($suffix);
+        }
+
+        // 锚点
+        $anchor = !empty($anchor) ? '#' . $anchor : '';
+
+        // 参数组装
+        if (!empty($vars)) {
+            // 添加参数
+            if ($this->route->config('url_common_param')) {
+                $vars = http_build_query($vars);
+                $url .= $suffix . '?' . $vars . $anchor;
+            } else {
+                foreach ($vars as $var => $val) {
+                    $val = (string) $val;
+                    if ('' !== $val) {
+                        $url .= $depr . $var . $depr . urlencode($val);
+                    }
+                }
+
+                $url .= $suffix . $anchor;
+            }
+        } else {
+            $url .= $suffix . $anchor;
+        }
+
+        // 检测域名
+        $domain = $this->parseDomain($url, $domain);
+
+        // URL组装
+        return $domain . rtrim($this->root, '/') . '/' . ltrim($url, '/');
+    }
+
+    /**
+     * 获取URL的应用名
+     * @access protected
+     * @return string
+     */
+    protected function getAppName()
+    {
+        $app = $this->app->http->getName();
+        $map = $this->app->config->get('app.app_map', []);
+
+        if ($key = array_search($app, $map)) {
+            $app = $key;
+        }
+
+        return $app;
+    }
+}
diff --git a/vendor/topthink/think-multi-app/src/command/Build.php b/vendor/topthink/think-multi-app/src/command/Build.php
new file mode 100755
index 0000000..65b2f87
--- /dev/null
+++ b/vendor/topthink/think-multi-app/src/command/Build.php
@@ -0,0 +1,180 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\app\command;
+
+use think\console\Command;
+use think\console\Input;
+use think\console\input\Argument;
+use think\console\Output;
+
+class Build extends Command
+{
+    /**
+     * 应用基础目录
+     * @var string
+     */
+    protected $basePath;
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function configure()
+    {
+        $this->setName('build')
+            ->addArgument('app', Argument::OPTIONAL, 'app name .')
+            ->setDescription('Build App Dirs');
+    }
+
+    protected function execute(Input $input, Output $output)
+    {
+        $this->basePath = $this->app->getBasePath();
+        $app            = $input->getArgument('app') ?: '';
+
+        if (is_file($this->basePath . 'build.php')) {
+            $list = include $this->basePath . 'build.php';
+        } else {
+            $list = [
+                '__dir__' => ['controller', 'model', 'view'],
+            ];
+        }
+
+        $this->buildApp($app, $list);
+        $output->writeln("<info>Successed</info>");
+
+    }
+
+    /**
+     * 创建应用
+     * @access protected
+     * @param  string $app  应用名
+     * @param  array  $list 目录结构
+     * @return void
+     */
+    protected function buildApp(string $app, array $list = []): void
+    {
+        if (!is_dir($this->basePath . $app)) {
+            // 创建应用目录
+            mkdir($this->basePath . $app);
+        }
+
+        $appPath   = $this->basePath . ($app ? $app . DIRECTORY_SEPARATOR : '');
+        $namespace = 'app' . ($app ? '\\' . $app : '');
+
+        // 创建配置文件和公共文件
+        $this->buildCommon($app);
+        // 创建模块的默认页面
+        $this->buildHello($app, $namespace);
+
+        foreach ($list as $path => $file) {
+            if ('__dir__' == $path) {
+                // 生成子目录
+                foreach ($file as $dir) {
+                    $this->checkDirBuild($appPath . $dir);
+                }
+            } elseif ('__file__' == $path) {
+                // 生成(空白)文件
+                foreach ($file as $name) {
+                    if (!is_file($appPath . $name)) {
+                        file_put_contents($appPath . $name, 'php' == pathinfo($name, PATHINFO_EXTENSION) ? '<?php' . PHP_EOL : '');
+                    }
+                }
+            } else {
+                // 生成相关MVC文件
+                foreach ($file as $val) {
+                    $val      = trim($val);
+                    $filename = $appPath . $path . DIRECTORY_SEPARATOR . $val . '.php';
+                    $space    = $namespace . '\\' . $path;
+                    $class    = $val;
+                    switch ($path) {
+                        case 'controller': // 控制器
+                            if ($this->app->config->get('route.controller_suffix')) {
+                                $filename = $appPath . $path . DIRECTORY_SEPARATOR . $val . 'Controller.php';
+                                $class    = $val . 'Controller';
+                            }
+                            $content = "<?php" . PHP_EOL . "namespace {$space};" . PHP_EOL . PHP_EOL . "class {$class}" . PHP_EOL . "{" . PHP_EOL . PHP_EOL . "}";
+                            break;
+                        case 'model': // 模型
+                            $content = "<?php" . PHP_EOL . "namespace {$space};" . PHP_EOL . PHP_EOL . "use think\Model;" . PHP_EOL . PHP_EOL . "class {$class} extends Model" . PHP_EOL . "{" . PHP_EOL . PHP_EOL . "}";
+                            break;
+                        case 'view': // 视图
+                            $filename = $appPath . $path . DIRECTORY_SEPARATOR . $val . '.html';
+                            $this->checkDirBuild(dirname($filename));
+                            $content = '';
+                            break;
+                        default:
+                            // 其他文件
+                            $content = "<?php" . PHP_EOL . "namespace {$space};" . PHP_EOL . PHP_EOL . "class {$class}" . PHP_EOL . "{" . PHP_EOL . PHP_EOL . "}";
+                    }
+
+                    if (!is_file($filename)) {
+                        file_put_contents($filename, $content);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * 创建应用的欢迎页面
+     * @access protected
+     * @param  string $app 目录
+     * @param  string $namespace 类库命名空间
+     * @return void
+     */
+    protected function buildHello(string $app, string $namespace): void
+    {
+        $suffix   = $this->app->config->get('route.controller_suffix') ? 'Controller' : '';
+        $filename = $this->basePath . ($app ? $app . DIRECTORY_SEPARATOR : '') . 'controller' . DIRECTORY_SEPARATOR . 'Index' . $suffix . '.php';
+
+        if (!is_file($filename)) {
+            $content = file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'controller.stub');
+            $content = str_replace(['{%name%}', '{%app%}', '{%layer%}', '{%suffix%}'], [$app, $namespace, 'controller', $suffix], $content);
+            $this->checkDirBuild(dirname($filename));
+
+            file_put_contents($filename, $content);
+        }
+    }
+
+    /**
+     * 创建应用的公共文件
+     * @access protected
+     * @param  string $app 目录
+     * @return void
+     */
+    protected function buildCommon(string $app): void
+    {
+        $appPath = $this->basePath . ($app ? $app . DIRECTORY_SEPARATOR : '');
+
+        if (!is_file($appPath . 'common.php')) {
+            file_put_contents($appPath . 'common.php', "<?php" . PHP_EOL . "// 这是系统自动生成的公共文件" . PHP_EOL);
+        }
+
+        foreach (['event', 'middleware', 'common'] as $name) {
+            if (!is_file($appPath . $name . '.php')) {
+                file_put_contents($appPath . $name . '.php', "<?php" . PHP_EOL . "// 这是系统自动生成的{$name}定义文件" . PHP_EOL . "return [" . PHP_EOL . PHP_EOL . "];" . PHP_EOL);
+            }
+        }
+    }
+
+    /**
+     * 创建目录
+     * @access protected
+     * @param  string $dirname 目录名称
+     * @return void
+     */
+    protected function checkDirBuild(string $dirname): void
+    {
+        if (!is_dir($dirname)) {
+            mkdir($dirname, 0755, true);
+        }
+    }
+}
diff --git a/vendor/topthink/think-multi-app/src/command/Clear.php b/vendor/topthink/think-multi-app/src/command/Clear.php
new file mode 100755
index 0000000..f5283a3
--- /dev/null
+++ b/vendor/topthink/think-multi-app/src/command/Clear.php
@@ -0,0 +1,66 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+namespace think\app\command;
+
+use think\console\Command;
+use think\console\Input;
+use think\console\input\Argument;
+use think\console\input\Option;
+use think\console\Output;
+
+class Clear extends Command
+{
+    protected function configure()
+    {
+        // 指令配置
+        $this->setName('clear')
+            ->addArgument('app', Argument::OPTIONAL, 'app name .')
+            ->addOption('cache', 'c', Option::VALUE_NONE, 'clear cache file')
+            ->addOption('log', 'l', Option::VALUE_NONE, 'clear log file')
+            ->addOption('dir', 'r', Option::VALUE_NONE, 'clear empty dir')
+            ->setDescription('Clear runtime file');
+    }
+
+    protected function execute(Input $input, Output $output)
+    {
+        $app         = $input->getArgument('app') ?: '';
+        $runtimePath = $this->app->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR . ($app ? $app . DIRECTORY_SEPARATOR : '');
+
+        if ($input->getOption('cache')) {
+            $path = $runtimePath . 'cache';
+        } elseif ($input->getOption('log')) {
+            $path = $runtimePath . 'log';
+        } else {
+            $path = $runtimePath;
+        }
+
+        $rmdir = $input->getOption('dir') ? true : false;
+        $this->clear(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR, $rmdir);
+
+        $output->writeln("<info>Clear Successed</info>");
+    }
+
+    protected function clear(string $path, bool $rmdir): void
+    {
+        $files = is_dir($path) ? scandir($path) : [];
+
+        foreach ($files as $file) {
+            if ('.' != $file && '..' != $file && is_dir($path . $file)) {
+                array_map('unlink', glob($path . $file . DIRECTORY_SEPARATOR . '*.*'));
+                if ($rmdir) {
+                    rmdir($path . $file);
+                }
+            } elseif ('.gitignore' != $file && is_file($path . $file)) {
+                unlink($path . $file);
+            }
+        }
+    }
+}
diff --git a/vendor/topthink/think-multi-app/src/command/stubs/controller.stub b/vendor/topthink/think-multi-app/src/command/stubs/controller.stub
new file mode 100755
index 0000000..263c4c6
--- /dev/null
+++ b/vendor/topthink/think-multi-app/src/command/stubs/controller.stub
@@ -0,0 +1,12 @@
+<?php
+declare (strict_types = 1);
+
+namespace {%app%}\{%layer%};
+
+class Index{%suffix%}
+{
+    public function index()
+    {
+        return '您好!这是一个[{%name%}]示例应用';
+    }
+}
diff --git a/vendor/topthink/think-orm/.gitignore b/vendor/topthink/think-orm/.gitignore
new file mode 100755
index 0000000..82cfc4e
--- /dev/null
+++ b/vendor/topthink/think-orm/.gitignore
@@ -0,0 +1,3 @@
+.idea
+composer.lock
+vendor
diff --git a/vendor/topthink/think-orm/LICENSE b/vendor/topthink/think-orm/LICENSE
new file mode 100755
index 0000000..8dada3e
--- /dev/null
+++ b/vendor/topthink/think-orm/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/vendor/topthink/think-orm/README.md b/vendor/topthink/think-orm/README.md
new file mode 100755
index 0000000..c65afe9
--- /dev/null
+++ b/vendor/topthink/think-orm/README.md
@@ -0,0 +1,27 @@
+# ThinkORM
+
+基于PHP7.1+ 和PDO实现的ORM,支持多数据库,2.0版本主要特性包括:
+
+* 基于PDO和PHP强类型实现
+* 支持原生查询和查询构造器
+* 自动参数绑定和预查询
+* 简洁易用的查询功能
+* 强大灵活的模型用法
+* 支持预载入关联查询和延迟关联查询
+* 支持多数据库及动态切换
+* 支持`MongoDb`
+* 支持分布式及事务
+* 支持断点重连
+* 支持`JSON`查询
+* 支持数据库日志
+* 支持`PSR-16`缓存及`PSR-3`日志规范
+
+
+## 安装
+~~~
+composer require topthink/think-orm
+~~~
+
+## 文档
+
+详细参考 [ThinkORM开发指南](https://www.kancloud.cn/manual/think-orm/content)
diff --git a/vendor/topthink/think-orm/composer.json b/vendor/topthink/think-orm/composer.json
new file mode 100755
index 0000000..1a5b9e4
--- /dev/null
+++ b/vendor/topthink/think-orm/composer.json
@@ -0,0 +1,28 @@
+{
+    "name": "topthink/think-orm",
+    "description": "think orm",
+    "keywords": [
+        "orm",
+        "database"
+    ],      
+    "license": "Apache-2.0",
+    "authors": [
+        {
+            "name": "liu21st",
+            "email": "liu21st@gmail.com"
+        }
+    ],
+    "require": {
+        "php": ">=7.1.0",
+        "ext-json": "*",
+        "psr/simple-cache": "^1.0",
+        "psr/log": "~1.0",
+        "topthink/think-helper":"^3.1"
+    },
+    "autoload": {
+        "psr-4": {
+            "think\\": "src"
+        },
+        "files": []
+    }
+}
diff --git a/vendor/topthink/think-orm/src/DbManager.php b/vendor/topthink/think-orm/src/DbManager.php
new file mode 100755
index 0000000..414706a
--- /dev/null
+++ b/vendor/topthink/think-orm/src/DbManager.php
@@ -0,0 +1,396 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+use InvalidArgumentException;
+use Psr\Log\LoggerInterface;
+use Psr\SimpleCache\CacheInterface;
+use think\db\BaseQuery;
+use think\db\ConnectionInterface;
+use think\db\Query;
+use think\db\Raw;
+
+/**
+ * Class DbManager
+ * @package think
+ * @mixin BaseQuery
+ * @mixin Query
+ */
+class DbManager
+{
+    /**
+     * 数据库连接实例
+     * @var array
+     */
+    protected $instance = [];
+
+    /**
+     * 数据库配置
+     * @var array
+     */
+    protected $config = [];
+
+    /**
+     * Event对象或者数组
+     * @var array|object
+     */
+    protected $event;
+
+    /**
+     * SQL监听
+     * @var array
+     */
+    protected $listen = [];
+
+    /**
+     * SQL日志
+     * @var array
+     */
+    protected $dbLog = [];
+
+    /**
+     * 查询次数
+     * @var int
+     */
+    protected $queryTimes = 0;
+
+    /**
+     * 查询缓存对象
+     * @var CacheInterface
+     */
+    protected $cache;
+
+    /**
+     * 查询日志对象
+     * @var LoggerInterface
+     */
+    protected $log;
+
+    /**
+     * 架构函数
+     * @access public
+     */
+    public function __construct()
+    {
+        $this->modelMaker();
+    }
+
+    /**
+     * 注入模型对象
+     * @access public
+     * @return void
+     */
+    protected function modelMaker()
+    {
+        $this->triggerSql();
+
+        Model::setDb($this);
+
+        if (is_object($this->event)) {
+            Model::setEvent($this->event);
+        }
+
+        Model::maker(function (Model $model) {
+            $isAutoWriteTimestamp = $model->getAutoWriteTimestamp();
+
+            if (is_null($isAutoWriteTimestamp)) {
+                // 自动写入时间戳
+                $model->isAutoWriteTimestamp($this->getConfig('auto_timestamp', true));
+            }
+
+            $dateFormat = $model->getDateFormat();
+
+            if (is_null($dateFormat)) {
+                // 设置时间戳格式
+                $model->setDateFormat($this->getConfig('datetime_format', 'Y-m-d H:i:s'));
+            }
+        });
+    }
+
+    /**
+     * 监听SQL
+     * @access protected
+     * @return void
+     */
+    protected function triggerSql(): void
+    {
+        // 监听SQL
+        $this->listen(function ($sql, $time, $master) {
+            if (0 === strpos($sql, 'CONNECT:')) {
+                $this->log($sql);
+                return;
+            }
+
+            // 记录SQL
+            if (is_bool($master)) {
+                // 分布式记录当前操作的主从
+                $master = $master ? 'master|' : 'slave|';
+            } else {
+                $master = '';
+            }
+
+            $this->log($sql . ' [ ' . $master . 'RunTime:' . $time . 's ]');
+        });
+    }
+
+    /**
+     * 初始化配置参数
+     * @access public
+     * @param array $config 连接配置
+     * @return void
+     */
+    public function setConfig($config): void
+    {
+        $this->config = $config;
+    }
+
+    /**
+     * 设置缓存对象
+     * @access public
+     * @param CacheInterface $cache 缓存对象
+     * @return void
+     */
+    public function setCache(CacheInterface $cache): void
+    {
+        $this->cache = $cache;
+    }
+
+    /**
+     * 设置日志对象
+     * @access public
+     * @param LoggerInterface $log 日志对象
+     * @return void
+     */
+    public function setLog(LoggerInterface $log): void
+    {
+        $this->log = $log;
+    }
+
+    /**
+     * 记录SQL日志
+     * @access protected
+     * @param string $log  SQL日志信息
+     * @param string $type 日志类型
+     * @return void
+     */
+    public function log(string $log, string $type = 'sql')
+    {
+        if ($this->log) {
+            $this->log->log($type, $log);
+        } else {
+            $this->dbLog[$type][] = $log;
+        }
+    }
+
+    /**
+     * 获得查询日志(没有设置日志对象使用)
+     * @access public
+     * @param bool $clear 是否清空
+     * @return array
+     */
+    public function getDbLog(bool $clear = false): array
+    {
+        $logs = $this->dbLog;
+        if ($clear) {
+            $this->dbLog = [];
+        }
+
+        return $logs;
+    }
+
+    /**
+     * 获取配置参数
+     * @access public
+     * @param string $name    配置参数
+     * @param mixed  $default 默认值
+     * @return mixed
+     */
+    public function getConfig(string $name = '', $default = null)
+    {
+        if ('' === $name) {
+            return $this->config;
+        }
+
+        return $this->config[$name] ?? $default;
+    }
+
+    /**
+     * 创建/切换数据库连接查询
+     * @access public
+     * @param string|null $name  连接配置标识
+     * @param bool        $force 强制重新连接
+     * @return ConnectionInterface
+     */
+    public function connect(string $name = null, bool $force = false)
+    {
+        return $this->instance($name, $force);
+    }
+
+    /**
+     * 创建数据库连接实例
+     * @access protected
+     * @param string|null $name  连接标识
+     * @param bool        $force 强制重新连接
+     * @return ConnectionInterface
+     */
+    protected function instance(string $name = null, bool $force = false): ConnectionInterface
+    {
+        if (empty($name)) {
+            $name = $this->getConfig('default', 'mysql');
+        }
+
+        if ($force || !isset($this->instance[$name])) {
+            $this->instance[$name] = $this->createConnection($name);
+        }
+
+        return $this->instance[$name];
+    }
+
+    /**
+     * 获取连接配置
+     * @param string $name
+     * @return array
+     */
+    protected function getConnectionConfig(string $name): array
+    {
+        $connections = $this->getConfig('connections');
+        if (!isset($connections[$name])) {
+            throw new InvalidArgumentException('Undefined db config:' . $name);
+        }
+
+        return $connections[$name];
+    }
+
+    /**
+     * 创建连接
+     * @param $name
+     * @return ConnectionInterface
+     */
+    protected function createConnection(string $name): ConnectionInterface
+    {
+        $config = $this->getConnectionConfig($name);
+
+        $type = !empty($config['type']) ? $config['type'] : 'mysql';
+
+        if (false !== strpos($type, '\\')) {
+            $class = $type;
+        } else {
+            $class = '\\think\\db\\connector\\' . ucfirst($type);
+        }
+
+        /** @var ConnectionInterface $connection */
+        $connection = new $class($config);
+        $connection->setDb($this);
+
+        if ($this->cache) {
+            $connection->setCache($this->cache);
+        }
+
+        return $connection;
+    }
+
+    /**
+     * 使用表达式设置数据
+     * @access public
+     * @param string $value 表达式
+     * @return Raw
+     */
+    public function raw(string $value): Raw
+    {
+        return new Raw($value);
+    }
+
+    /**
+     * 更新查询次数
+     * @access public
+     * @return void
+     */
+    public function updateQueryTimes(): void
+    {
+        $this->queryTimes++;
+    }
+
+    /**
+     * 重置查询次数
+     * @access public
+     * @return void
+     */
+    public function clearQueryTimes(): void
+    {
+        $this->queryTimes = 0;
+    }
+
+    /**
+     * 获得查询次数
+     * @access public
+     * @return integer
+     */
+    public function getQueryTimes(): int
+    {
+        return $this->queryTimes;
+    }
+
+    /**
+     * 监听SQL执行
+     * @access public
+     * @param callable $callback 回调方法
+     * @return void
+     */
+    public function listen(callable $callback): void
+    {
+        $this->listen[] = $callback;
+    }
+
+    /**
+     * 获取监听SQL执行
+     * @access public
+     * @return array
+     */
+    public function getListen(): array
+    {
+        return $this->listen;
+    }
+
+    /**
+     * 注册回调方法
+     * @access public
+     * @param string   $event    事件名
+     * @param callable $callback 回调方法
+     * @return void
+     */
+    public function event(string $event, callable $callback): void
+    {
+        $this->event[$event][] = $callback;
+    }
+
+    /**
+     * 触发事件
+     * @access public
+     * @param string $event  事件名
+     * @param mixed  $params 传入参数
+     * @return mixed
+     */
+    public function trigger(string $event, $params = null)
+    {
+        if (isset($this->event[$event])) {
+            foreach ($this->event[$event] as $callback) {
+                call_user_func_array($callback, [$this]);
+            }
+        }
+    }
+
+    public function __call($method, $args)
+    {
+        return call_user_func_array([$this->connect(), $method], $args);
+    }
+}
diff --git a/vendor/topthink/think-orm/src/Model.php b/vendor/topthink/think-orm/src/Model.php
new file mode 100755
index 0000000..fc5dd90
--- /dev/null
+++ b/vendor/topthink/think-orm/src/Model.php
@@ -0,0 +1,1060 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+use ArrayAccess;
+use Closure;
+use JsonSerializable;
+use think\contract\Arrayable;
+use think\contract\Jsonable;
+use think\db\BaseQuery as Query;
+
+/**
+ * Class Model
+ * @package think
+ * @mixin Query
+ * @method void onAfterRead(Model $model) static after_read事件定义
+ * @method mixed onBeforeInsert(Model $model) static before_insert事件定义
+ * @method void onAfterInsert(Model $model) static after_insert事件定义
+ * @method mixed onBeforeUpdate(Model $model) static before_update事件定义
+ * @method void onAfterUpdate(Model $model) static after_update事件定义
+ * @method mixed onBeforeWrite(Model $model) static before_write事件定义
+ * @method void onAfterWrite(Model $model) static after_write事件定义
+ * @method mixed onBeforeDelete(Model $model) static before_write事件定义
+ * @method void onAfterDelete(Model $model) static after_delete事件定义
+ * @method void onBeforeRestore(Model $model) static before_restore事件定义
+ * @method void onAfterRestore(Model $model) static after_restore事件定义
+ */
+abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonable
+{
+    use model\concern\Attribute;
+    use model\concern\RelationShip;
+    use model\concern\ModelEvent;
+    use model\concern\TimeStamp;
+    use model\concern\Conversion;
+
+    /**
+     * 数据是否存在
+     * @var bool
+     */
+    private $exists = false;
+
+    /**
+     * 是否强制更新所有数据
+     * @var bool
+     */
+    private $force = false;
+
+    /**
+     * 是否Replace
+     * @var bool
+     */
+    private $replace = false;
+
+    /**
+     * 数据表后缀
+     * @var string
+     */
+    protected $suffix;
+
+    /**
+     * 更新条件
+     * @var array
+     */
+    private $updateWhere;
+
+    /**
+     * 数据库配置
+     * @var string
+     */
+    protected $connection;
+
+    /**
+     * 模型名称
+     * @var string
+     */
+    protected $name;
+
+    /**
+     * 主键值
+     * @var string
+     */
+    protected $key;
+
+    /**
+     * 数据表名称
+     * @var string
+     */
+    protected $table;
+
+    /**
+     * 初始化过的模型.
+     * @var array
+     */
+    protected static $initialized = [];
+
+    /**
+     * 软删除字段默认值
+     * @var mixed
+     */
+    protected $defaultSoftDelete;
+
+    /**
+     * 全局查询范围
+     * @var array
+     */
+    protected $globalScope = [];
+
+    /**
+     * 延迟保存信息
+     * @var bool
+     */
+    private $lazySave = false;
+
+    /**
+     * Db对象
+     * @var DbManager
+     */
+    protected static $db;
+
+    /**
+     * 容器对象的依赖注入方法
+     * @var callable
+     */
+    protected static $invoker;
+
+    /**
+     * 服务注入
+     * @var Closure[]
+     */
+    protected static $maker = [];
+
+    /**
+     * 方法注入
+     * @var Closure[][]
+     */
+    protected static $macro = [];
+
+    /**
+     * 设置服务注入
+     * @access public
+     * @param Closure $maker
+     * @return void
+     */
+    public static function maker(Closure $maker)
+    {
+        static::$maker[] = $maker;
+    }
+
+    /**
+     * 设置方法注入
+     * @access public
+     * @param string $method
+     * @param Closure $closure
+     * @return void
+     */
+    public static function macro(string $method, Closure $closure)
+    {
+        if (!isset(static::$macro[static::class])) {
+            static::$macro[static::class] = [];
+        }
+        static::$macro[static::class][$method] = $closure;
+    }
+
+    /**
+     * 设置Db对象
+     * @access public
+     * @param DbManager $db Db对象
+     * @return void
+     */
+    public static function setDb(DbManager $db)
+    {
+        self::$db = $db;
+    }
+
+    /**
+     * 设置容器对象的依赖注入方法
+     * @access public
+     * @param callable $callable 依赖注入方法
+     * @return void
+     */
+    public static function setInvoker(callable $callable): void
+    {
+        self::$invoker = $callable;
+    }
+
+    /**
+     * 调用反射执行模型方法 支持参数绑定
+     * @access public
+     * @param mixed $method
+     * @param array $vars 参数
+     * @return mixed
+     */
+    public function invoke($method, array $vars = [])
+    {
+        if (self::$invoker) {
+            $call = self::$invoker;
+            return $call($method instanceof Closure ? $method : Closure::fromCallable([$this, $method]), $vars);
+        }
+
+        return call_user_func_array($method instanceof Closure ? $method : [$this, $method], $vars);
+    }
+
+    /**
+     * 架构函数
+     * @access public
+     * @param array $data 数据
+     */
+    public function __construct(array $data = [])
+    {
+        $this->data = $data;
+
+        if (!empty($this->data)) {
+            // 废弃字段
+            foreach ((array) $this->disuse as $key) {
+                if (array_key_exists($key, $this->data)) {
+                    unset($this->data[$key]);
+                }
+            }
+        }
+
+        // 记录原始数据
+        $this->origin = $this->data;
+
+        if (empty($this->name)) {
+            // 当前模型名
+            $name       = str_replace('\\', '/', static::class);
+            $this->name = basename($name);
+        }
+
+        if (!empty(static::$maker)) {
+            foreach (static::$maker as $maker) {
+                call_user_func($maker, $this);
+            }
+        }
+
+        // 执行初始化操作
+        $this->initialize();
+    }
+
+    /**
+     * 获取当前模型名称
+     * @access public
+     * @return string
+     */
+    public function getName(): string
+    {
+        return $this->name;
+    }
+
+    /**
+     * 创建新的模型实例
+     * @access public
+     * @param array $data  数据
+     * @param mixed $where 更新条件
+     * @return Model
+     */
+    public function newInstance(array $data = [], $where = null): Model
+    {
+        $model = new static($data);
+
+        if ($this->connection) {
+            $model->setConnection($this->connection);
+        }
+
+        if ($this->suffix) {
+            $model->setSuffix($this->suffix);
+        }
+
+        if (empty($data)) {
+            return $model;
+        }
+
+        $model->exists(true);
+
+        $model->setUpdateWhere($where);
+
+        $model->trigger('AfterRead');
+
+        return $model;
+    }
+
+    /**
+     * 设置模型的更新条件
+     * @access protected
+     * @param mixed $where 更新条件
+     * @return void
+     */
+    protected function setUpdateWhere($where): void
+    {
+        $this->updateWhere = $where;
+    }
+
+    /**
+     * 设置当前模型的数据库连接
+     * @access public
+     * @param string $connection 数据表连接标识
+     * @return $this
+     */
+    public function setConnection(string $connection)
+    {
+        $this->connection = $connection;
+        return $this;
+    }
+
+    /**
+     * 获取当前模型的数据库连接标识
+     * @access public
+     * @return string
+     */
+    public function getConnection(): string
+    {
+        return $this->connection ?: '';
+    }
+
+    /**
+     * 设置当前模型数据表的后缀
+     * @access public
+     * @param string $suffix 数据表后缀
+     * @return $this
+     */
+    public function setSuffix(string $suffix)
+    {
+        $this->suffix = $suffix;
+        return $this;
+    }
+
+    /**
+     * 获取当前模型的数据表后缀
+     * @access public
+     * @return string
+     */
+    public function getSuffix(): string
+    {
+        return $this->suffix ?: '';
+    }
+
+    /**
+     * 获取当前模型的数据库查询对象
+     * @access public
+     * @param array $scope 设置不使用的全局查询范围
+     * @return Query
+     */
+    public function db($scope = []): Query
+    {
+        /** @var Query $query */
+        $query = self::$db->connect($this->connection)
+            ->name($this->name . $this->suffix)
+            ->pk($this->pk);
+
+        if (!empty($this->table)) {
+            $query->table($this->table . $this->suffix);
+        }
+
+        $query->model($this)
+            ->json($this->json, $this->jsonAssoc)
+            ->setFieldType(array_merge($this->schema, $this->jsonType));
+
+        // 软删除
+        if (property_exists($this, 'withTrashed') && !$this->withTrashed) {
+            $this->withNoTrashed($query);
+        }
+
+        // 全局作用域
+        if (is_array($scope)) {
+            $globalScope = array_diff($this->globalScope, $scope);
+            $query->scope($globalScope);
+        }
+
+        // 返回当前模型的数据库查询对象
+        return $query;
+    }
+
+    /**
+     *  初始化模型
+     * @access private
+     * @return void
+     */
+    private function initialize(): void
+    {
+        if (!isset(static::$initialized[static::class])) {
+            static::$initialized[static::class] = true;
+            static::init();
+        }
+    }
+
+    /**
+     * 初始化处理
+     * @access protected
+     * @return void
+     */
+    protected static function init()
+    {
+    }
+
+    protected function checkData(): void
+    {
+    }
+
+    protected function checkResult($result): void
+    {
+    }
+
+    /**
+     * 更新是否强制写入数据 而不做比较(亦可用于软删除的强制删除)
+     * @access public
+     * @param bool $force
+     * @return $this
+     */
+    public function force(bool $force = true)
+    {
+        $this->force = $force;
+        return $this;
+    }
+
+    /**
+     * 判断force
+     * @access public
+     * @return bool
+     */
+    public function isForce(): bool
+    {
+        return $this->force;
+    }
+
+    /**
+     * 新增数据是否使用Replace
+     * @access public
+     * @param bool $replace
+     * @return $this
+     */
+    public function replace(bool $replace = true)
+    {
+        $this->replace = $replace;
+        return $this;
+    }
+
+    /**
+     * 刷新模型数据
+     * @access public
+     * @param bool $relation 是否刷新关联数据
+     * @return $this
+     */
+    public function refresh(bool $relation = false)
+    {
+        if ($this->exists) {
+            $this->data   = $this->db()->find($this->getKey())->getData();
+            $this->origin = $this->data;
+
+            if ($relation) {
+                $this->relation = [];
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * 设置数据是否存在
+     * @access public
+     * @param bool $exists
+     * @return $this
+     */
+    public function exists(bool $exists = true)
+    {
+        $this->exists = $exists;
+        return $this;
+    }
+
+    /**
+     * 判断数据是否存在数据库
+     * @access public
+     * @return bool
+     */
+    public function isExists(): bool
+    {
+        return $this->exists;
+    }
+
+    /**
+     * 判断模型是否为空
+     * @access public
+     * @return bool
+     */
+    public function isEmpty(): bool
+    {
+        return empty($this->data);
+    }
+
+    /**
+     * 延迟保存当前数据对象
+     * @access public
+     * @param array|bool $data 数据
+     * @return void
+     */
+    public function lazySave($data = []): void
+    {
+        if (false === $data) {
+            $this->lazySave = false;
+        } else {
+            if (is_array($data)) {
+                $this->setAttrs($data);
+            }
+
+            $this->lazySave = true;
+        }
+    }
+
+    /**
+     * 保存当前数据对象
+     * @access public
+     * @param array  $data     数据
+     * @param string $sequence 自增序列名
+     * @return bool
+     */
+    public function save(array $data = [], string $sequence = null): bool
+    {
+        // 数据对象赋值
+        $this->setAttrs($data);
+
+        if ($this->isEmpty() || false === $this->trigger('BeforeWrite')) {
+            return false;
+        }
+
+        $result = $this->exists ? $this->updateData() : $this->insertData($sequence);
+
+        if (false === $result) {
+            return false;
+        }
+
+        // 写入回调
+        $this->trigger('AfterWrite');
+
+        // 重新记录原始数据
+        $this->origin   = $this->data;
+        $this->set      = [];
+        $this->lazySave = false;
+
+        return true;
+    }
+
+    /**
+     * 检查数据是否允许写入
+     * @access protected
+     * @return array
+     */
+    protected function checkAllowFields(): array
+    {
+        // 检测字段
+        if (empty($this->field)) {
+            if (!empty($this->schema)) {
+                $this->field = array_keys(array_merge($this->schema, $this->jsonType));
+            } else {
+                $query = $this->db();
+                $table = $this->table ? $this->table . $this->suffix : $query->getTable();
+
+                $this->field = $query->getConnection()->getTableFields($table);
+            }
+
+            return $this->field;
+        }
+
+        $field = $this->field;
+
+        if ($this->autoWriteTimestamp) {
+            array_push($field, $this->createTime, $this->updateTime);
+        }
+
+        if (!empty($this->disuse)) {
+            // 废弃字段
+            $field = array_diff($field, $this->disuse);
+        }
+
+        return $field;
+    }
+
+    /**
+     * 保存写入数据
+     * @access protected
+     * @return bool
+     */
+    protected function updateData(): bool
+    {
+        // 事件回调
+        if (false === $this->trigger('BeforeUpdate')) {
+            return false;
+        }
+
+        $this->checkData();
+
+        // 获取有更新的数据
+        $data = $this->getChangedData();
+
+        if (empty($data)) {
+            // 关联更新
+            if (!empty($this->relationWrite)) {
+                $this->autoRelationUpdate();
+            }
+
+            return true;
+        }
+
+        if ($this->autoWriteTimestamp && $this->updateTime && !isset($data[$this->updateTime])) {
+            // 自动写入更新时间
+            $data[$this->updateTime]       = $this->autoWriteTimestamp($this->updateTime);
+            $this->data[$this->updateTime] = $data[$this->updateTime];
+        }
+
+        // 检查允许字段
+        $allowFields = $this->checkAllowFields();
+
+        foreach ($this->relationWrite as $name => $val) {
+            if (!is_array($val)) {
+                continue;
+            }
+
+            foreach ($val as $key) {
+                if (isset($data[$key])) {
+                    unset($data[$key]);
+                }
+            }
+        }
+
+        // 模型更新
+        $db = $this->db();
+
+        $db->transaction(function () use ($data, $allowFields, $db) {
+            $this->key = null;
+            $where     = $this->getWhere();
+
+            $result = $db->where($where)
+                ->strict(false)
+                ->cache(true)
+                ->setOption('key', $this->key)
+                ->field($allowFields)
+                ->update($data);
+
+            $this->checkResult($result);
+
+            // 关联更新
+            if (!empty($this->relationWrite)) {
+                $this->autoRelationUpdate();
+            }
+        });
+
+        // 更新回调
+        $this->trigger('AfterUpdate');
+
+        return true;
+    }
+
+    /**
+     * 新增写入数据
+     * @access protected
+     * @param string $sequence 自增名
+     * @return bool
+     */
+    protected function insertData(string $sequence = null): bool
+    {
+        // 时间戳自动写入
+        if ($this->autoWriteTimestamp) {
+            if ($this->createTime && !isset($this->data[$this->createTime])) {
+                $this->data[$this->createTime] = $this->autoWriteTimestamp($this->createTime);
+            }
+
+            if ($this->updateTime && !isset($this->data[$this->updateTime])) {
+                $this->data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime);
+            }
+        }
+
+        if (false === $this->trigger('BeforeInsert')) {
+            return false;
+        }
+
+        $this->checkData();
+
+        // 检查允许字段
+        $allowFields = $this->checkAllowFields();
+
+        $db = $this->db();
+
+        $db->transaction(function () use ($sequence, $allowFields, $db) {
+            $result = $db->strict(false)
+                ->field($allowFields)
+                ->replace($this->replace)
+                ->sequence($sequence)
+                ->insert($this->data, true);
+
+            // 获取自动增长主键
+            if ($result) {
+                $pk = $this->getPk();
+
+                if (is_string($pk) && (!isset($this->data[$pk]) || '' == $this->data[$pk])) {
+                    $this->data[$pk] = $result;
+                }
+            }
+
+            // 关联写入
+            if (!empty($this->relationWrite)) {
+                $this->autoRelationInsert();
+            }
+        });
+
+        // 标记数据已经存在
+        $this->exists = true;
+
+        // 新增回调
+        $this->trigger('AfterInsert');
+
+        return true;
+    }
+
+    /**
+     * 获取当前的更新条件
+     * @access public
+     * @return mixed
+     */
+    public function getWhere()
+    {
+        $pk = $this->getPk();
+
+        if (is_string($pk) && isset($this->origin[$pk])) {
+            $where     = [[$pk, '=', $this->origin[$pk]]];
+            $this->key = $this->origin[$pk];
+        } elseif (is_array($pk)) {
+            foreach ($pk as $field) {
+                if (isset($this->origin[$field])) {
+                    $where[] = [$field, '=', $this->origin[$field]];
+                }
+            }
+        }
+
+        if (empty($where)) {
+            $where = empty($this->updateWhere) ? null : $this->updateWhere;
+        }
+
+        return $where;
+    }
+
+    /**
+     * 保存多个数据到当前数据对象
+     * @access public
+     * @param iterable $dataSet 数据
+     * @param boolean  $replace 是否自动识别更新和写入
+     * @return Collection
+     * @throws \Exception
+     */
+    public function saveAll(iterable $dataSet, bool $replace = true): Collection
+    {
+        $db = $this->db();
+
+        $result = $db->transaction(function () use ($replace, $dataSet) {
+
+            $pk = $this->getPk();
+
+            if (is_string($pk) && $replace) {
+                $auto = true;
+            }
+
+            $result = [];
+
+            $suffix = $this->getSuffix();
+
+            foreach ($dataSet as $key => $data) {
+                if ($this->exists || (!empty($auto) && isset($data[$pk]))) {
+                    $result[$key] = static::update($data, [], [], $suffix);
+                } else {
+                    $result[$key] = static::create($data, $this->field, $this->replace, $suffix);
+                }
+            }
+
+            return $result;
+        });
+
+        return $this->toCollection($result);
+    }
+
+    /**
+     * 删除当前的记录
+     * @access public
+     * @return bool
+     */
+    public function delete(): bool
+    {
+        if (!$this->exists || $this->isEmpty() || false === $this->trigger('BeforeDelete')) {
+            return false;
+        }
+
+        // 读取更新条件
+        $where = $this->getWhere();
+
+        $db = $this->db();
+
+        $db->transaction(function () use ($where, $db) {
+            // 删除当前模型数据
+            $db->where($where)->delete();
+
+            // 关联删除
+            if (!empty($this->relationWrite)) {
+                $this->autoRelationDelete();
+            }
+        });
+
+        $this->trigger('AfterDelete');
+
+        $this->exists   = false;
+        $this->lazySave = false;
+
+        return true;
+    }
+
+    /**
+     * 写入数据
+     * @access public
+     * @param array  $data       数据数组
+     * @param array  $allowField 允许字段
+     * @param bool   $replace    使用Replace
+     * @param string $suffix     数据表后缀
+     * @return static
+     */
+    public static function create(array $data, array $allowField = [], bool $replace = false, string $suffix = ''): Model
+    {
+        $model = new static();
+
+        if (!empty($allowField)) {
+            $model->allowField($allowField);
+        }
+
+        if (!empty($suffix)) {
+            $model->setSuffix($suffix);
+        }
+
+        $model->replace($replace)->save($data);
+
+        return $model;
+    }
+
+    /**
+     * 更新数据
+     * @access public
+     * @param array  $data       数据数组
+     * @param mixed  $where      更新条件
+     * @param array  $allowField 允许字段
+     * @param string $suffix     数据表后缀
+     * @return static
+     */
+    public static function update(array $data, $where = [], array $allowField = [], string $suffix = '')
+    {
+        $model = new static();
+
+        if (!empty($allowField)) {
+            $model->allowField($allowField);
+        }
+
+        if (!empty($where)) {
+            $model->setUpdateWhere($where);
+        }
+
+        if (!empty($suffix)) {
+            $model->setSuffix($suffix);
+        }
+
+        $model->exists(true)->save($data);
+
+        return $model;
+    }
+
+    /**
+     * 删除记录
+     * @access public
+     * @param mixed $data  主键列表 支持闭包查询条件
+     * @param bool  $force 是否强制删除
+     * @return bool
+     */
+    public static function destroy($data, bool $force = false): bool
+    {
+        if (empty($data) && 0 !== $data) {
+            return false;
+        }
+
+        $model = new static();
+
+        $query = $model->db();
+
+        if (is_array($data) && key($data) !== 0) {
+            $query->where($data);
+            $data = null;
+        } elseif ($data instanceof \Closure) {
+            $data($query);
+            $data = null;
+        }
+
+        $resultSet = $query->select($data);
+
+        foreach ($resultSet as $result) {
+            $result->force($force)->delete();
+        }
+
+        return true;
+    }
+
+    /**
+     * 解序列化后处理
+     */
+    public function __wakeup()
+    {
+        $this->initialize();
+    }
+
+    /**
+     * 修改器 设置数据对象的值
+     * @access public
+     * @param string $name  名称
+     * @param mixed  $value 值
+     * @return void
+     */
+    public function __set(string $name, $value): void
+    {
+        $this->setAttr($name, $value);
+    }
+
+    /**
+     * 获取器 获取数据对象的值
+     * @access public
+     * @param string $name 名称
+     * @return mixed
+     */
+    public function __get(string $name)
+    {
+        return $this->getAttr($name);
+    }
+
+    /**
+     * 检测数据对象的值
+     * @access public
+     * @param string $name 名称
+     * @return bool
+     */
+    public function __isset(string $name): bool
+    {
+        return !is_null($this->getAttr($name));
+    }
+
+    /**
+     * 销毁数据对象的值
+     * @access public
+     * @param string $name 名称
+     * @return void
+     */
+    public function __unset(string $name): void
+    {
+        unset($this->data[$name], $this->relation[$name]);
+    }
+
+    // ArrayAccess
+    public function offsetSet($name, $value)
+    {
+        $this->setAttr($name, $value);
+    }
+
+    public function offsetExists($name): bool
+    {
+        return $this->__isset($name);
+    }
+
+    public function offsetUnset($name)
+    {
+        $this->__unset($name);
+    }
+
+    public function offsetGet($name)
+    {
+        return $this->getAttr($name);
+    }
+
+    /**
+     * 设置不使用的全局查询范围
+     * @access public
+     * @param array $scope 不启用的全局查询范围
+     * @return Query
+     */
+    public static function withoutGlobalScope(array $scope = null)
+    {
+        $model = new static();
+
+        return $model->db($scope);
+    }
+
+    /**
+     * 切换后缀进行查询
+     * @access public
+     * @param string $suffix 切换的表后缀
+     * @return Model
+     */
+    public static function suffix(string $suffix)
+    {
+        $model = new static();
+        $model->setSuffix($suffix);
+
+        return $model;
+    }
+
+    /**
+     * 切换数据库连接进行查询
+     * @access public
+     * @param string $connection 数据库连接标识
+     * @return Model
+     */
+    public static function connect(string $connection)
+    {
+        $model = new static();
+        $model->setConnection($connection);
+
+        return $model;
+    }
+
+    public function __call($method, $args)
+    {
+        if (isset(static::$macro[static::class][$method])) {
+            return call_user_func_array(static::$macro[static::class][$method]->bindTo($this, static::class), $args);
+        }
+
+        if ('withattr' == strtolower($method)) {
+            return call_user_func_array([$this, 'withAttribute'], $args);
+        }
+
+        return call_user_func_array([$this->db(), $method], $args);
+    }
+
+    public static function __callStatic($method, $args)
+    {
+        if (isset(static::$macro[static::class][$method])) {
+            return call_user_func_array(static::$macro[static::class][$method]->bindTo(null, static::class), $args);
+        }
+
+        $model = new static();
+
+        return call_user_func_array([$model->db(), $method], $args);
+    }
+
+    /**
+     * 析构方法
+     * @access public
+     */
+    public function __destruct()
+    {
+        if ($this->lazySave) {
+            $this->save();
+        }
+    }
+}
diff --git a/vendor/topthink/think-orm/src/Paginator.php b/vendor/topthink/think-orm/src/Paginator.php
new file mode 100755
index 0000000..f5d8006
--- /dev/null
+++ b/vendor/topthink/think-orm/src/Paginator.php
@@ -0,0 +1,518 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: zhangyajun <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+use ArrayAccess;
+use ArrayIterator;
+use Closure;
+use Countable;
+use DomainException;
+use IteratorAggregate;
+use JsonSerializable;
+use think\paginator\driver\Bootstrap;
+use Traversable;
+
+/**
+ * 分页基础类
+ * @mixin Collection
+ */
+abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
+{
+    /**
+     * 是否简洁模式
+     * @var bool
+     */
+    protected $simple = false;
+
+    /**
+     * 数据集
+     * @var Collection
+     */
+    protected $items;
+
+    /**
+     * 当前页
+     * @var int
+     */
+    protected $currentPage;
+
+    /**
+     * 最后一页
+     * @var int
+     */
+    protected $lastPage;
+
+    /**
+     * 数据总数
+     * @var integer|null
+     */
+    protected $total;
+
+    /**
+     * 每页数量
+     * @var int
+     */
+    protected $listRows;
+
+    /**
+     * 是否有下一页
+     * @var bool
+     */
+    protected $hasMore;
+
+    /**
+     * 分页配置
+     * @var array
+     */
+    protected $options = [
+        'var_page' => 'page',
+        'path'     => '/',
+        'query'    => [],
+        'fragment' => '',
+    ];
+
+    /**
+     * 获取当前页码
+     * @var Closure
+     */
+    protected static $currentPageResolver;
+
+    /**
+     * 获取当前路径
+     * @var Closure
+     */
+    protected static $currentPathResolver;
+
+    /**
+     * @var Closure
+     */
+    protected static $maker;
+
+    public function __construct($items, int $listRows, int $currentPage = 1, int $total = null, bool $simple = false, array $options = [])
+    {
+        $this->options = array_merge($this->options, $options);
+
+        $this->options['path'] = '/' != $this->options['path'] ? rtrim($this->options['path'], '/') : $this->options['path'];
+
+        $this->simple   = $simple;
+        $this->listRows = $listRows;
+
+        if (!$items instanceof Collection) {
+            $items = Collection::make($items);
+        }
+
+        if ($simple) {
+            $this->currentPage = $this->setCurrentPage($currentPage);
+            $this->hasMore     = count($items) > ($this->listRows);
+            $items             = $items->slice(0, $this->listRows);
+        } else {
+            $this->total       = $total;
+            $this->lastPage    = (int) ceil($total / $listRows);
+            $this->currentPage = $this->setCurrentPage($currentPage);
+            $this->hasMore     = $this->currentPage < $this->lastPage;
+        }
+        $this->items = $items;
+    }
+
+    /**
+     * @access public
+     * @param mixed $items
+     * @param int   $listRows
+     * @param int   $currentPage
+     * @param int   $total
+     * @param bool  $simple
+     * @param array $options
+     * @return Paginator
+     */
+    public static function make($items, int $listRows, int $currentPage = 1, int $total = null, bool $simple = false, array $options = [])
+    {
+        if (isset(static::$maker)) {
+            return call_user_func(static::$maker, $items, $listRows, $currentPage, $total, $simple, $options);
+        }
+
+        return new Bootstrap($items, $listRows, $currentPage, $total, $simple, $options);
+    }
+
+    public static function maker(Closure $resolver)
+    {
+        static::$maker = $resolver;
+    }
+
+    protected function setCurrentPage(int $currentPage): int
+    {
+        if (!$this->simple && $currentPage > $this->lastPage) {
+            return $this->lastPage > 0 ? $this->lastPage : 1;
+        }
+
+        return $currentPage;
+    }
+
+    /**
+     * 获取页码对应的链接
+     *
+     * @access protected
+     * @param int $page
+     * @return string
+     */
+    protected function url(int $page): string
+    {
+        if ($page <= 0) {
+            $page = 1;
+        }
+
+        if (strpos($this->options['path'], '[PAGE]') === false) {
+            $parameters = [$this->options['var_page'] => $page];
+            $path       = $this->options['path'];
+        } else {
+            $parameters = [];
+            $path       = str_replace('[PAGE]', $page, $this->options['path']);
+        }
+
+        if (count($this->options['query']) > 0) {
+            $parameters = array_merge($this->options['query'], $parameters);
+        }
+
+        $url = $path;
+        if (!empty($parameters)) {
+            $url .= '?' . http_build_query($parameters, '', '&');
+        }
+
+        return $url . $this->buildFragment();
+    }
+
+    /**
+     * 自动获取当前页码
+     * @access public
+     * @param string $varPage
+     * @param int    $default
+     * @return int
+     */
+    public static function getCurrentPage(string $varPage = 'page', int $default = 1): int
+    {
+        if (isset(static::$currentPageResolver)) {
+            return call_user_func(static::$currentPageResolver, $varPage);
+        }
+
+        return $default;
+    }
+
+    /**
+     * 设置获取当前页码闭包
+     * @param Closure $resolver
+     */
+    public static function currentPageResolver(Closure $resolver)
+    {
+        static::$currentPageResolver = $resolver;
+    }
+
+    /**
+     * 自动获取当前的path
+     * @access public
+     * @param string $default
+     * @return string
+     */
+    public static function getCurrentPath($default = '/'): string
+    {
+        if (isset(static::$currentPathResolver)) {
+            return call_user_func(static::$currentPathResolver);
+        }
+
+        return $default;
+    }
+
+    /**
+     * 设置获取当前路径闭包
+     * @param Closure $resolver
+     */
+    public static function currentPathResolver(Closure $resolver)
+    {
+        static::$currentPathResolver = $resolver;
+    }
+
+    /**
+     * 获取数据总条数
+     * @return int
+     */
+    public function total(): int
+    {
+        if ($this->simple) {
+            throw new DomainException('not support total');
+        }
+
+        return $this->total;
+    }
+
+    /**
+     * 获取每页数量
+     * @return int
+     */
+    public function listRows(): int
+    {
+        return $this->listRows;
+    }
+
+    /**
+     * 获取当前页页码
+     * @return int
+     */
+    public function currentPage(): int
+    {
+        return $this->currentPage;
+    }
+
+    /**
+     * 获取最后一页页码
+     * @return int
+     */
+    public function lastPage(): int
+    {
+        if ($this->simple) {
+            throw new DomainException('not support last');
+        }
+
+        return $this->lastPage;
+    }
+
+    /**
+     * 数据是否足够分页
+     * @access public
+     * @return bool
+     */
+    public function hasPages(): bool
+    {
+        return !(1 == $this->currentPage && !$this->hasMore);
+    }
+
+    /**
+     * 创建一组分页链接
+     *
+     * @access public
+     * @param int $start
+     * @param int $end
+     * @return array
+     */
+    public function getUrlRange(int $start, int $end): array
+    {
+        $urls = [];
+
+        for ($page = $start; $page <= $end; $page++) {
+            $urls[$page] = $this->url($page);
+        }
+
+        return $urls;
+    }
+
+    /**
+     * 设置URL锚点
+     *
+     * @access public
+     * @param string|null $fragment
+     * @return $this
+     */
+    public function fragment(string $fragment = null)
+    {
+        $this->options['fragment'] = $fragment;
+
+        return $this;
+    }
+
+    /**
+     * 添加URL参数
+     *
+     * @access public
+     * @param array $append
+     * @return $this
+     */
+    public function appends(array $append)
+    {
+        foreach ($append as $k => $v) {
+            if ($k !== $this->options['var_page']) {
+                $this->options['query'][$k] = $v;
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * 构造锚点字符串
+     *
+     * @access public
+     * @return string
+     */
+    protected function buildFragment(): string
+    {
+        return $this->options['fragment'] ? '#' . $this->options['fragment'] : '';
+    }
+
+    /**
+     * 渲染分页html
+     * @access public
+     * @return mixed
+     */
+    abstract public function render();
+
+    public function items()
+    {
+        return $this->items->all();
+    }
+
+    /**
+     * 获取数据集
+     *
+     * @return Collection|\think\model\Collection
+     */
+    public function getCollection()
+    {
+        return $this->items;
+    }
+
+    public function isEmpty(): bool
+    {
+        return $this->items->isEmpty();
+    }
+
+    /**
+     * 给每个元素执行个回调
+     *
+     * @access public
+     * @param callable $callback
+     * @return $this
+     */
+    public function each(callable $callback)
+    {
+        foreach ($this->items as $key => $item) {
+            $result = $callback($item, $key);
+
+            if (false === $result) {
+                break;
+            } elseif (!is_object($item)) {
+                $this->items[$key] = $result;
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Retrieve an external iterator
+     * @access public
+     * @return Traversable An instance of an object implementing <b>Iterator</b> or
+     * <b>Traversable</b>
+     */
+    public function getIterator()
+    {
+        return new ArrayIterator($this->items->all());
+    }
+
+    /**
+     * Whether a offset exists
+     * @access public
+     * @param mixed $offset
+     * @return bool
+     */
+    public function offsetExists($offset)
+    {
+        return $this->items->offsetExists($offset);
+    }
+
+    /**
+     * Offset to retrieve
+     * @access public
+     * @param mixed $offset
+     * @return mixed
+     */
+    public function offsetGet($offset)
+    {
+        return $this->items->offsetGet($offset);
+    }
+
+    /**
+     * Offset to set
+     * @access public
+     * @param mixed $offset
+     * @param mixed $value
+     */
+    public function offsetSet($offset, $value)
+    {
+        $this->items->offsetSet($offset, $value);
+    }
+
+    /**
+     * Offset to unset
+     * @access public
+     * @param mixed $offset
+     * @return void
+     * @since  5.0.0
+     */
+    public function offsetUnset($offset)
+    {
+        $this->items->offsetUnset($offset);
+    }
+
+    /**
+     * 统计数据集条数
+     * @return int
+     */
+    public function count(): int
+    {
+        return $this->items->count();
+    }
+
+    public function __toString()
+    {
+        return (string) $this->render();
+    }
+
+    /**
+     * 转换为数组
+     * @return array
+     */
+    public function toArray(): array
+    {
+        try {
+            $total = $this->total();
+        } catch (DomainException $e) {
+            $total = null;
+        }
+
+        return [
+            'total'        => $total,
+            'per_page'     => $this->listRows(),
+            'current_page' => $this->currentPage(),
+            'last_page'    => $this->lastPage,
+            'data'         => $this->items->toArray(),
+        ];
+    }
+
+    /**
+     * Specify data which should be serialized to JSON
+     */
+    public function jsonSerialize()
+    {
+        return $this->toArray();
+    }
+
+    public function __call($name, $arguments)
+    {
+        $result = call_user_func_array([$this->items, $name], $arguments);
+
+        if ($result instanceof Collection) {
+            $this->items = $result;
+            return $this;
+        }
+
+        return $result;
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/db/BaseQuery.php b/vendor/topthink/think-orm/src/db/BaseQuery.php
new file mode 100755
index 0000000..65b2d2d
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/BaseQuery.php
@@ -0,0 +1,1282 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db;
+
+use think\Collection;
+use think\db\exception\DataNotFoundException;
+use think\db\exception\DbException as Exception;
+use think\db\exception\ModelNotFoundException;
+use think\helper\Str;
+use think\Model;
+use think\Paginator;
+
+/**
+ * 数据查询基础类
+ */
+abstract class BaseQuery
+{
+    use concern\TimeFieldQuery;
+    use concern\AggregateQuery;
+    use concern\ModelRelationQuery;
+    use concern\ResultOperation;
+    use concern\Transaction;
+    use concern\WhereQuery;
+
+    /**
+     * 当前数据库连接对象
+     * @var Connection
+     */
+    protected $connection;
+
+    /**
+     * 当前数据表名称(不含前缀)
+     * @var string
+     */
+    protected $name = '';
+
+    /**
+     * 当前数据表主键
+     * @var string|array
+     */
+    protected $pk;
+
+    /**
+     * 当前数据表自增主键
+     * @var string
+     */
+    protected $autoinc;
+
+    /**
+     * 当前数据表前缀
+     * @var string
+     */
+    protected $prefix = '';
+
+    /**
+     * 当前查询参数
+     * @var array
+     */
+    protected $options = [];
+
+    /**
+     * 架构函数
+     * @access public
+     * @param ConnectionInterface $connection 数据库连接对象
+     */
+    public function __construct(ConnectionInterface $connection)
+    {
+        $this->connection = $connection;
+
+        $this->prefix = $this->connection->getConfig('prefix');
+    }
+
+    /**
+     * 利用__call方法实现一些特殊的Model方法
+     * @access public
+     * @param string $method 方法名称
+     * @param array  $args   调用参数
+     * @return mixed
+     * @throws Exception
+     */
+    public function __call(string $method, array $args)
+    {
+        if (strtolower(substr($method, 0, 5)) == 'getby') {
+            // 根据某个字段获取记录
+            $field = Str::snake(substr($method, 5));
+            return $this->where($field, '=', $args[0])->find();
+        } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') {
+            // 根据某个字段获取记录的某个值
+            $name = Str::snake(substr($method, 10));
+            return $this->where($name, '=', $args[0])->value($args[1]);
+        } elseif (strtolower(substr($method, 0, 7)) == 'whereor') {
+            $name = Str::snake(substr($method, 7));
+            array_unshift($args, $name);
+            return call_user_func_array([$this, 'whereOr'], $args);
+        } elseif (strtolower(substr($method, 0, 5)) == 'where') {
+            $name = Str::snake(substr($method, 5));
+            array_unshift($args, $name);
+            return call_user_func_array([$this, 'where'], $args);
+        } elseif ($this->model && method_exists($this->model, 'scope' . $method)) {
+            // 动态调用命名范围
+            $method = 'scope' . $method;
+            array_unshift($args, $this);
+
+            call_user_func_array([$this->model, $method], $args);
+            return $this;
+        } else {
+            throw new Exception('method not exist:' . static::class . '->' . $method);
+        }
+    }
+
+    /**
+     * 创建一个新的查询对象
+     * @access public
+     * @return BaseQuery
+     */
+    public function newQuery(): BaseQuery
+    {
+        $query = new static($this->connection);
+
+        if ($this->model) {
+            $query->model($this->model);
+        }
+
+        if (isset($this->options['table'])) {
+            $query->table($this->options['table']);
+        } else {
+            $query->name($this->name);
+        }
+
+        if (isset($this->options['json'])) {
+            $query->json($this->options['json'], $this->options['json_assoc']);
+        }
+
+        if (isset($this->options['field_type'])) {
+            $query->setFieldType($this->options['field_type']);
+        }
+
+        return $query;
+    }
+
+    /**
+     * 获取当前的数据库Connection对象
+     * @access public
+     * @return ConnectionInterface
+     */
+    public function getConnection()
+    {
+        return $this->connection;
+    }
+
+    /**
+     * 指定当前数据表名(不含前缀)
+     * @access public
+     * @param string $name 不含前缀的数据表名字
+     * @return $this
+     */
+    public function name(string $name)
+    {
+        $this->name = $name;
+        return $this;
+    }
+
+    /**
+     * 获取当前的数据表名称
+     * @access public
+     * @return string
+     */
+    public function getName(): string
+    {
+        return $this->name ?: $this->model->getName();
+    }
+
+    /**
+     * 获取数据库的配置参数
+     * @access public
+     * @param string $name 参数名称
+     * @return mixed
+     */
+    public function getConfig(string $name = '')
+    {
+        return $this->connection->getConfig($name);
+    }
+
+    /**
+     * 得到当前或者指定名称的数据表
+     * @access public
+     * @param string $name 不含前缀的数据表名字
+     * @return mixed
+     */
+    public function getTable(string $name = '')
+    {
+        if (empty($name) && isset($this->options['table'])) {
+            return $this->options['table'];
+        }
+
+        $name = $name ?: $this->name;
+
+        return $this->prefix . Str::snake($name);
+    }
+
+    /**
+     * 设置字段类型信息
+     * @access public
+     * @param array $type 字段类型信息
+     * @return $this
+     */
+    public function setFieldType(array $type)
+    {
+        $this->options['field_type'] = $type;
+        return $this;
+    }
+
+    /**
+     * 获取最近一次查询的sql语句
+     * @access public
+     * @return string
+     */
+    public function getLastSql(): string
+    {
+        return $this->connection->getLastSql();
+    }
+
+    /**
+     * 获取返回或者影响的记录数
+     * @access public
+     * @return integer
+     */
+    public function getNumRows(): int
+    {
+        return $this->connection->getNumRows();
+    }
+
+    /**
+     * 获取最近插入的ID
+     * @access public
+     * @param string $sequence 自增序列名
+     * @return mixed
+     */
+    public function getLastInsID(string $sequence = null)
+    {
+        return $this->connection->getLastInsID($this, $sequence);
+    }
+
+    /**
+     * 得到某个字段的值
+     * @access public
+     * @param string $field   字段名
+     * @param mixed  $default 默认值
+     * @return mixed
+     */
+    public function value(string $field, $default = null)
+    {
+        return $this->connection->value($this, $field, $default);
+    }
+
+    /**
+     * 得到某个列的数组
+     * @access public
+     * @param string $field 字段名 多个字段用逗号分隔
+     * @param string $key   索引
+     * @return array
+     */
+    public function column(string $field, string $key = ''): array
+    {
+        return $this->connection->column($this, $field, $key);
+    }
+
+    /**
+     * 查询SQL组装 union
+     * @access public
+     * @param mixed   $union UNION
+     * @param boolean $all   是否适用UNION ALL
+     * @return $this
+     */
+    public function union($union, bool $all = false)
+    {
+        $this->options['union']['type'] = $all ? 'UNION ALL' : 'UNION';
+
+        if (is_array($union)) {
+            $this->options['union'] = array_merge($this->options['union'], $union);
+        } else {
+            $this->options['union'][] = $union;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 查询SQL组装 union all
+     * @access public
+     * @param mixed $union UNION数据
+     * @return $this
+     */
+    public function unionAll($union)
+    {
+        return $this->union($union, true);
+    }
+
+    /**
+     * 指定查询字段
+     * @access public
+     * @param mixed $field 字段信息
+     * @return $this
+     */
+    public function field($field)
+    {
+        if (empty($field)) {
+            return $this;
+        } elseif ($field instanceof Raw) {
+            $this->options['field'][] = $field;
+            return $this;
+        }
+
+        if (is_string($field)) {
+            if (preg_match('/[\<\'\"\(]/', $field)) {
+                return $this->fieldRaw($field);
+            }
+
+            $field = array_map('trim', explode(',', $field));
+        }
+
+        if (true === $field) {
+            // 获取全部字段
+            $fields = $this->getTableFields();
+            $field  = $fields ?: ['*'];
+        }
+
+        if (isset($this->options['field'])) {
+            $field = array_merge((array) $this->options['field'], $field);
+        }
+
+        $this->options['field'] = array_unique($field);
+
+        return $this;
+    }
+
+    /**
+     * 指定要排除的查询字段
+     * @access public
+     * @param array|string $field 要排除的字段
+     * @return $this
+     */
+    public function withoutField($field)
+    {
+        if (empty($field)) {
+            return $this;
+        }
+
+        if (is_string($field)) {
+            $field = array_map('trim', explode(',', $field));
+        }
+
+        // 字段排除
+        $fields = $this->getTableFields();
+        $field  = $fields ? array_diff($fields, $field) : $field;
+
+        if (isset($this->options['field'])) {
+            $field = array_merge((array) $this->options['field'], $field);
+        }
+
+        $this->options['field'] = array_unique($field);
+
+        return $this;
+    }
+
+    /**
+     * 指定其它数据表的查询字段
+     * @access public
+     * @param mixed   $field     字段信息
+     * @param string  $tableName 数据表名
+     * @param string  $prefix    字段前缀
+     * @param string  $alias     别名前缀
+     * @return $this
+     */
+    public function tableField($field, string $tableName, string $prefix = '', string $alias = '')
+    {
+        if (empty($field)) {
+            return $this;
+        }
+
+        if (is_string($field)) {
+            $field = array_map('trim', explode(',', $field));
+        }
+
+        if (true === $field) {
+            // 获取全部字段
+            $fields = $this->getTableFields($tableName);
+            $field  = $fields ?: ['*'];
+        }
+
+        // 添加统一的前缀
+        $prefix = $prefix ?: $tableName;
+        foreach ($field as $key => &$val) {
+            if (is_numeric($key) && $alias) {
+                $field[$prefix . '.' . $val] = $alias . $val;
+                unset($field[$key]);
+            } elseif (is_numeric($key)) {
+                $val = $prefix . '.' . $val;
+            }
+        }
+
+        if (isset($this->options['field'])) {
+            $field = array_merge((array) $this->options['field'], $field);
+        }
+
+        $this->options['field'] = array_unique($field);
+
+        return $this;
+    }
+
+    /**
+     * 设置数据
+     * @access public
+     * @param array $data 数据
+     * @return $this
+     */
+    public function data(array $data)
+    {
+        $this->options['data'] = $data;
+
+        return $this;
+    }
+
+    /**
+     * 去除查询参数
+     * @access public
+     * @param string $option 参数名 留空去除所有参数
+     * @return $this
+     */
+    public function removeOption(string $option = '')
+    {
+        if ('' === $option) {
+            $this->options = [];
+            $this->bind    = [];
+        } elseif (isset($this->options[$option])) {
+            unset($this->options[$option]);
+        }
+
+        return $this;
+    }
+
+    /**
+     * 指定查询数量
+     * @access public
+     * @param int $offset 起始位置
+     * @param int $length 查询数量
+     * @return $this
+     */
+    public function limit(int $offset, int $length = null)
+    {
+        $this->options['limit'] = $offset . ($length ? ',' . $length : '');
+
+        return $this;
+    }
+
+    /**
+     * 指定分页
+     * @access public
+     * @param int $page     页数
+     * @param int $listRows 每页数量
+     * @return $this
+     */
+    public function page(int $page, int $listRows = null)
+    {
+        $this->options['page'] = [$page, $listRows];
+
+        return $this;
+    }
+
+    /**
+     * 指定当前操作的数据表
+     * @access public
+     * @param mixed $table 表名
+     * @return $this
+     */
+    public function table($table)
+    {
+        if (is_string($table)) {
+            if (strpos($table, ')')) {
+                // 子查询
+            } elseif (false === strpos($table, ',')) {
+                if (strpos($table, ' ')) {
+                    [$item, $alias] = explode(' ', $table);
+                    $table          = [];
+                    $this->alias([$item => $alias]);
+                    $table[$item] = $alias;
+                }
+            } else {
+                $tables = explode(',', $table);
+                $table  = [];
+
+                foreach ($tables as $item) {
+                    $item = trim($item);
+                    if (strpos($item, ' ')) {
+                        [$item, $alias] = explode(' ', $item);
+                        $this->alias([$item => $alias]);
+                        $table[$item] = $alias;
+                    } else {
+                        $table[] = $item;
+                    }
+                }
+            }
+        } elseif (is_array($table)) {
+            $tables = $table;
+            $table  = [];
+
+            foreach ($tables as $key => $val) {
+                if (is_numeric($key)) {
+                    $table[] = $val;
+                } else {
+                    $this->alias([$key => $val]);
+                    $table[$key] = $val;
+                }
+            }
+        }
+
+        $this->options['table'] = $table;
+
+        return $this;
+    }
+
+    /**
+     * 指定排序 order('id','desc') 或者 order(['id'=>'desc','create_time'=>'desc'])
+     * @access public
+     * @param string|array|Raw $field 排序字段
+     * @param string           $order 排序
+     * @return $this
+     */
+    public function order($field, string $order = '')
+    {
+        if (empty($field)) {
+            return $this;
+        } elseif ($field instanceof Raw) {
+            $this->options['order'][] = $field;
+            return $this;
+        }
+
+        if (is_string($field)) {
+            if (!empty($this->options['via'])) {
+                $field = $this->options['via'] . '.' . $field;
+            }
+            if (strpos($field, ',')) {
+                $field = array_map('trim', explode(',', $field));
+            } else {
+                $field = empty($order) ? $field : [$field => $order];
+            }
+        } elseif (!empty($this->options['via'])) {
+            foreach ($field as $key => $val) {
+                if (is_numeric($key)) {
+                    $field[$key] = $this->options['via'] . '.' . $val;
+                } else {
+                    $field[$this->options['via'] . '.' . $key] = $val;
+                    unset($field[$key]);
+                }
+            }
+        }
+
+        if (!isset($this->options['order'])) {
+            $this->options['order'] = [];
+        }
+
+        if (is_array($field)) {
+            $this->options['order'] = array_merge($this->options['order'], $field);
+        } else {
+            $this->options['order'][] = $field;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 分页查询
+     * @access public
+     * @param int|array $listRows 每页数量 数组表示配置参数
+     * @param int|bool  $simple   是否简洁模式或者总记录数
+     * @return Paginator
+     * @throws Exception
+     */
+    public function paginate($listRows = null, $simple = false): Paginator
+    {
+        if (is_int($simple)) {
+            $total  = $simple;
+            $simple = false;
+        }
+
+        $defaultConfig = [
+            'query'     => [], //url额外参数
+            'fragment'  => '', //url锚点
+            'var_page'  => 'page', //分页变量
+            'list_rows' => 15, //每页数量
+        ];
+
+        if (is_array($listRows)) {
+            $config   = array_merge($defaultConfig, $listRows);
+            $listRows = intval($config['list_rows']);
+        } else {
+            $config   = $defaultConfig;
+            $listRows = intval($listRows ?: $config['list_rows']);
+        }
+
+        $page = isset($config['page']) ? (int) $config['page'] : Paginator::getCurrentPage($config['var_page']);
+
+        $page = $page < 1 ? 1 : $page;
+
+        $config['path'] = $config['path'] ?? Paginator::getCurrentPath();
+
+        if (!isset($total) && !$simple) {
+            $options = $this->getOptions();
+
+            unset($this->options['order'], $this->options['limit'], $this->options['page'], $this->options['field']);
+
+            $bind    = $this->bind;
+            $total   = $this->count();
+            $results = $this->options($options)->bind($bind)->page($page, $listRows)->select();
+        } elseif ($simple) {
+            $results = $this->limit(($page - 1) * $listRows, $listRows + 1)->select();
+            $total   = null;
+        } else {
+            $results = $this->page($page, $listRows)->select();
+        }
+
+        $this->removeOption('limit');
+        $this->removeOption('page');
+
+        return Paginator::make($results, $listRows, $page, $total, $simple, $config);
+    }
+
+    /**
+     * 根据数字类型字段进行分页查询(大数据)
+     * @access public
+     * @param int|array $listRows 每页数量或者分页配置
+     * @param string    $key      分页索引键
+     * @param string    $sort     索引键排序 asc|desc
+     * @return Paginator
+     * @throws Exception
+     */
+    public function paginateX($listRows = null, string $key = null, string $sort = null): Paginator
+    {
+        $defaultConfig = [
+            'query'     => [], //url额外参数
+            'fragment'  => '', //url锚点
+            'var_page'  => 'page', //分页变量
+            'list_rows' => 15, //每页数量
+        ];
+
+        $config   = is_array($listRows) ? array_merge($defaultConfig, $listRows) : $defaultConfig;
+        $listRows = is_int($listRows) ? $listRows : (int) $config['list_rows'];
+        $page     = isset($config['page']) ? (int) $config['page'] : Paginator::getCurrentPage($config['var_page']);
+        $page     = $page < 1 ? 1 : $page;
+
+        $config['path'] = $config['path'] ?? Paginator::getCurrentPath();
+
+        $key     = $key ?: $this->getPk();
+        $options = $this->getOptions();
+
+        if (is_null($sort)) {
+            $order = $options['order'] ?? '';
+            if (!empty($order)) {
+                $sort = $order[$key] ?? 'desc';
+            } else {
+                $this->order($key, 'desc');
+                $sort = 'desc';
+            }
+        } else {
+            $this->order($key, $sort);
+        }
+
+        $newOption = $options;
+        unset($newOption['field'], $newOption['page']);
+
+        $data = $this->newQuery()
+            ->options($newOption)
+            ->field($key)
+            ->where(true)
+            ->order($key, $sort)
+            ->limit(1)
+            ->find();
+
+        $result = $data[$key];
+
+        if (is_numeric($result)) {
+            $lastId = 'asc' == $sort ? ($result - 1) + ($page - 1) * $listRows : ($result + 1) - ($page - 1) * $listRows;
+        } else {
+            throw new Exception('not support type');
+        }
+
+        $results = $this->when($lastId, function ($query) use ($key, $sort, $lastId) {
+            $query->where($key, 'asc' == $sort ? '>' : '<', $lastId);
+        })
+            ->limit($listRows)
+            ->select();
+
+        $this->options($options);
+
+        return Paginator::make($results, $listRows, $page, null, true, $config);
+    }
+
+    /**
+     * 根据最后ID查询更多N个数据
+     * @access public
+     * @param int        $limit  LIMIT
+     * @param int|string $lastId LastId
+     * @param string     $key    分页索引键 默认为主键
+     * @param string     $sort   索引键排序 asc|desc
+     * @return array
+     * @throws Exception
+     */
+    public function more(int $limit, $lastId = null, string $key = null, string $sort = null): array
+    {
+        $key = $key ?: $this->getPk();
+
+        if (is_null($sort)) {
+            $order = $this->getOptions('order');
+            if (!empty($order)) {
+                $sort = $order[$key] ?? 'desc';
+            } else {
+                $this->order($key, 'desc');
+                $sort = 'desc';
+            }
+        } else {
+            $this->order($key, $sort);
+        }
+
+        $result = $this->when($lastId, function ($query) use ($key, $sort, $lastId) {
+            $query->where($key, 'asc' == $sort ? '>' : '<', $lastId);
+        })->limit($limit)->select();
+
+        $last = $result->last();
+
+        $result->first();
+
+        return [
+            'data'   => $result,
+            'lastId' => $last[$key],
+        ];
+    }
+
+    /**
+     * 查询缓存
+     * @access public
+     * @param mixed             $key    缓存key
+     * @param integer|\DateTime $expire 缓存有效期
+     * @param string|array      $tag    缓存标签
+     * @return $this
+     */
+    public function cache($key = true, $expire = null, $tag = null)
+    {
+        if (false === $key || !$this->getConnection()->getCache()) {
+            return $this;
+        }
+
+        if ($key instanceof \DateTimeInterface || $key instanceof \DateInterval || (is_int($key) && is_null($expire))) {
+            $expire = $key;
+            $key    = true;
+        }
+
+        $this->options['cache'] = [$key, $expire, $tag];
+
+        return $this;
+    }
+
+    /**
+     * 指定查询lock
+     * @access public
+     * @param bool|string $lock 是否lock
+     * @return $this
+     */
+    public function lock($lock = false)
+    {
+        $this->options['lock'] = $lock;
+
+        if ($lock) {
+            $this->options['master'] = true;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 指定数据表别名
+     * @access public
+     * @param array|string $alias 数据表别名
+     * @return $this
+     */
+    public function alias($alias)
+    {
+        if (is_array($alias)) {
+            $this->options['alias'] = $alias;
+        } else {
+            $table = $this->getTable();
+
+            $this->options['alias'][$table] = $alias;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 设置从主服务器读取数据
+     * @access public
+     * @param bool $readMaster 是否从主服务器读取
+     * @return $this
+     */
+    public function master(bool $readMaster = true)
+    {
+        $this->options['master'] = $readMaster;
+        return $this;
+    }
+
+    /**
+     * 设置是否严格检查字段名
+     * @access public
+     * @param bool $strict 是否严格检查字段
+     * @return $this
+     */
+    public function strict(bool $strict = true)
+    {
+        $this->options['strict'] = $strict;
+        return $this;
+    }
+
+    /**
+     * 设置自增序列名
+     * @access public
+     * @param string $sequence 自增序列名
+     * @return $this
+     */
+    public function sequence(string $sequence = null)
+    {
+        $this->options['sequence'] = $sequence;
+        return $this;
+    }
+
+    /**
+     * 设置JSON字段信息
+     * @access public
+     * @param array $json  JSON字段
+     * @param bool  $assoc 是否取出数组
+     * @return $this
+     */
+    public function json(array $json = [], bool $assoc = false)
+    {
+        $this->options['json']       = $json;
+        $this->options['json_assoc'] = $assoc;
+        return $this;
+    }
+
+    /**
+     * 指定数据表主键
+     * @access public
+     * @param string|array $pk 主键
+     * @return $this
+     */
+    public function pk($pk)
+    {
+        $this->pk = $pk;
+        return $this;
+    }
+
+    /**
+     * 查询参数批量赋值
+     * @access protected
+     * @param array $options 表达式参数
+     * @return $this
+     */
+    protected function options(array $options)
+    {
+        $this->options = $options;
+        return $this;
+    }
+
+    /**
+     * 获取当前的查询参数
+     * @access public
+     * @param string $name 参数名
+     * @return mixed
+     */
+    public function getOptions(string $name = '')
+    {
+        if ('' === $name) {
+            return $this->options;
+        }
+
+        return $this->options[$name] ?? null;
+    }
+
+    /**
+     * 设置当前的查询参数
+     * @access public
+     * @param string $option 参数名
+     * @param mixed  $value  参数值
+     * @return $this
+     */
+    public function setOption(string $option, $value)
+    {
+        $this->options[$option] = $value;
+        return $this;
+    }
+
+    /**
+     * 设置当前字段添加的表别名
+     * @access public
+     * @param string $via 临时表别名
+     * @return $this
+     */
+    public function via(string $via = '')
+    {
+        $this->options['via'] = $via;
+
+        return $this;
+    }
+
+    /**
+     * 保存记录 自动判断insert或者update
+     * @access public
+     * @param array $data        数据
+     * @param bool  $forceInsert 是否强制insert
+     * @return integer
+     */
+    public function save(array $data = [], bool $forceInsert = false)
+    {
+        if ($forceInsert) {
+            return $this->insert($data);
+        }
+
+        $this->options['data'] = array_merge($this->options['data'] ?? [], $data);
+
+        if (!empty($this->options['where'])) {
+            $isUpdate = true;
+        } else {
+            $isUpdate = $this->parseUpdateData($this->options['data']);
+        }
+
+        return $isUpdate ? $this->update() : $this->insert();
+    }
+
+    /**
+     * 插入记录
+     * @access public
+     * @param array   $data         数据
+     * @param boolean $getLastInsID 返回自增主键
+     * @return integer|string
+     */
+    public function insert(array $data = [], bool $getLastInsID = false)
+    {
+        if (!empty($data)) {
+            $this->options['data'] = $data;
+        }
+
+        return $this->connection->insert($this, $getLastInsID);
+    }
+
+    /**
+     * 插入记录并获取自增ID
+     * @access public
+     * @param array $data 数据
+     * @return integer|string
+     */
+    public function insertGetId(array $data)
+    {
+        return $this->insert($data, true);
+    }
+
+    /**
+     * 批量插入记录
+     * @access public
+     * @param array   $dataSet 数据集
+     * @param integer $limit   每次写入数据限制
+     * @return integer
+     */
+    public function insertAll(array $dataSet = [], int $limit = 0): int
+    {
+        if (empty($dataSet)) {
+            $dataSet = $this->options['data'] ?? [];
+        }
+
+        if (empty($limit) && !empty($this->options['limit']) && is_numeric($this->options['limit'])) {
+            $limit = (int) $this->options['limit'];
+        }
+
+        return $this->connection->insertAll($this, $dataSet, $limit);
+    }
+
+    /**
+     * 通过Select方式插入记录
+     * @access public
+     * @param array  $fields 要插入的数据表字段名
+     * @param string $table  要插入的数据表名
+     * @return integer
+     */
+    public function selectInsert(array $fields, string $table): int
+    {
+        return $this->connection->selectInsert($this, $fields, $table);
+    }
+
+    /**
+     * 更新记录
+     * @access public
+     * @param mixed $data 数据
+     * @return integer
+     * @throws Exception
+     */
+    public function update(array $data = []): int
+    {
+        if (!empty($data)) {
+            $this->options['data'] = array_merge($this->options['data'] ?? [], $data);
+        }
+
+        if (empty($this->options['where'])) {
+            $this->parseUpdateData($this->options['data']);
+        }
+
+        if (empty($this->options['where']) && $this->model) {
+            $this->where($this->model->getWhere());
+        }
+
+        if (empty($this->options['where'])) {
+            // 如果没有任何更新条件则不执行
+            throw new Exception('miss update condition');
+        }
+
+        return $this->connection->update($this);
+    }
+
+    /**
+     * 删除记录
+     * @access public
+     * @param mixed $data 表达式 true 表示强制删除
+     * @return int
+     * @throws Exception
+     */
+    public function delete($data = null): int
+    {
+        if (!is_null($data) && true !== $data) {
+            // AR模式分析主键条件
+            $this->parsePkWhere($data);
+        }
+
+        if (empty($this->options['where']) && $this->model) {
+            $this->where($this->model->getWhere());
+        }
+
+        if (true !== $data && empty($this->options['where'])) {
+            // 如果条件为空 不进行删除操作 除非设置 1=1
+            throw new Exception('delete without condition');
+        }
+
+        if (!empty($this->options['soft_delete'])) {
+            // 软删除
+            list($field, $condition) = $this->options['soft_delete'];
+            if ($condition) {
+                unset($this->options['soft_delete']);
+                $this->options['data'] = [$field => $condition];
+
+                return $this->connection->update($this);
+            }
+        }
+
+        $this->options['data'] = $data;
+
+        return $this->connection->delete($this);
+    }
+
+    /**
+     * 查找记录
+     * @access public
+     * @param mixed $data 数据
+     * @return Collection
+     * @throws Exception
+     * @throws ModelNotFoundException
+     * @throws DataNotFoundException
+     */
+    public function select($data = null): Collection
+    {
+        if (!is_null($data)) {
+            // 主键条件分析
+            $this->parsePkWhere($data);
+        }
+
+        $resultSet = $this->connection->select($this);
+
+        // 返回结果处理
+        if (!empty($this->options['fail']) && count($resultSet) == 0) {
+            $this->throwNotFound();
+        }
+
+        // 数据列表读取后的处理
+        if (!empty($this->model)) {
+            // 生成模型对象
+            $resultSet = $this->resultSetToModelCollection($resultSet);
+        } else {
+            $this->resultSet($resultSet);
+        }
+
+        return $resultSet;
+    }
+
+    /**
+     * 查找单条记录
+     * @access public
+     * @param mixed $data 查询数据
+     * @return array|Model|null
+     * @throws Exception
+     * @throws ModelNotFoundException
+     * @throws DataNotFoundException
+     */
+    public function find($data = null)
+    {
+        if (!is_null($data)) {
+            // AR模式分析主键条件
+            $this->parsePkWhere($data);
+        }
+
+        if (empty($this->options['where']) && empty($this->options['order'])) {
+            $result = [];
+        } else {
+            $result = $this->connection->find($this);
+        }
+
+        // 数据处理
+        if (empty($result)) {
+            return $this->resultToEmpty();
+        }
+
+        if (!empty($this->model)) {
+            // 返回模型对象
+            $this->resultToModel($result, $this->options);
+        } else {
+            $this->result($result);
+        }
+
+        return $result;
+    }
+
+    /**
+     * 分析表达式(可用于查询或者写入操作)
+     * @access public
+     * @return array
+     */
+    public function parseOptions(): array
+    {
+        $options = $this->getOptions();
+
+        // 获取数据表
+        if (empty($options['table'])) {
+            $options['table'] = $this->getTable();
+        }
+
+        if (!isset($options['where'])) {
+            $options['where'] = [];
+        } elseif (isset($options['view'])) {
+            // 视图查询条件处理
+            $this->parseView($options);
+        }
+
+        if (!isset($options['field'])) {
+            $options['field'] = '*';
+        }
+
+        foreach (['data', 'order', 'join', 'union'] as $name) {
+            if (!isset($options[$name])) {
+                $options[$name] = [];
+            }
+        }
+
+        if (!isset($options['strict'])) {
+            $options['strict'] = $this->connection->getConfig('fields_strict');
+        }
+
+        foreach (['master', 'lock', 'fetch_sql', 'array', 'distinct', 'procedure'] as $name) {
+            if (!isset($options[$name])) {
+                $options[$name] = false;
+            }
+        }
+
+        foreach (['group', 'having', 'limit', 'force', 'comment', 'partition', 'duplicate', 'extra'] as $name) {
+            if (!isset($options[$name])) {
+                $options[$name] = '';
+            }
+        }
+
+        if (isset($options['page'])) {
+            // 根据页数计算limit
+            [$page, $listRows] = $options['page'];
+            $page              = $page > 0 ? $page : 1;
+            $listRows          = $listRows ?: (is_numeric($options['limit']) ? $options['limit'] : 20);
+            $offset            = $listRows * ($page - 1);
+            $options['limit']  = $offset . ',' . $listRows;
+        }
+
+        $this->options = $options;
+
+        return $options;
+    }
+
+    /**
+     * 分析数据是否存在更新条件
+     * @access public
+     * @param array $data 数据
+     * @return bool
+     * @throws Exception
+     */
+    public function parseUpdateData(&$data): bool
+    {
+        $pk       = $this->getPk();
+        $isUpdate = false;
+        // 如果存在主键数据 则自动作为更新条件
+        if (is_string($pk) && isset($data[$pk])) {
+            $this->where($pk, '=', $data[$pk]);
+            $this->options['key'] = $data[$pk];
+            unset($data[$pk]);
+            $isUpdate = true;
+        } elseif (is_array($pk)) {
+            foreach ($pk as $field) {
+                if (isset($data[$field])) {
+                    $this->where($field, '=', $data[$field]);
+                    $isUpdate = true;
+                } else {
+                    // 如果缺少复合主键数据则不执行
+                    throw new Exception('miss complex primary data');
+                }
+                unset($data[$field]);
+            }
+        }
+
+        return $isUpdate;
+    }
+
+    /**
+     * 把主键值转换为查询条件 支持复合主键
+     * @access public
+     * @param array|string $data 主键数据
+     * @return void
+     * @throws Exception
+     */
+    public function parsePkWhere($data): void
+    {
+        $pk = $this->getPk();
+
+        if (is_string($pk)) {
+            // 获取数据表
+            if (empty($this->options['table'])) {
+                $this->options['table'] = $this->getTable();
+            }
+
+            $table = is_array($this->options['table']) ? key($this->options['table']) : $this->options['table'];
+
+            if (!empty($this->options['alias'][$table])) {
+                $alias = $this->options['alias'][$table];
+            }
+
+            $key = isset($alias) ? $alias . '.' . $pk : $pk;
+            // 根据主键查询
+            if (is_array($data)) {
+                $this->where($key, 'in', $data);
+            } else {
+                $this->where($key, '=', $data);
+                $this->options['key'] = $data;
+            }
+        }
+    }
+
+    /**
+     * 获取模型的更新条件
+     * @access protected
+     * @param array $options 查询参数
+     */
+    protected function getModelUpdateCondition(array $options)
+    {
+        return $options['where']['AND'] ?? null;
+    }
+}
diff --git a/vendor/topthink/think-orm/src/db/Builder.php b/vendor/topthink/think-orm/src/db/Builder.php
new file mode 100755
index 0000000..c807f82
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/Builder.php
@@ -0,0 +1,1303 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db;
+
+use Closure;
+use PDO;
+use think\db\exception\DbException as Exception;
+
+/**
+ * Db Builder
+ */
+abstract class Builder
+{
+    /**
+     * Connection对象
+     * @var ConnectionInterface
+     */
+    protected $connection;
+
+    /**
+     * 查询表达式映射
+     * @var array
+     */
+    protected $exp = ['NOTLIKE' => 'NOT LIKE', 'NOTIN' => 'NOT IN', 'NOTBETWEEN' => 'NOT BETWEEN', 'NOTEXISTS' => 'NOT EXISTS', 'NOTNULL' => 'NOT NULL', 'NOTBETWEEN TIME' => 'NOT BETWEEN TIME'];
+
+    /**
+     * 查询表达式解析
+     * @var array
+     */
+    protected $parser = [
+        'parseCompare'     => ['=', '<>', '>', '>=', '<', '<='],
+        'parseLike'        => ['LIKE', 'NOT LIKE'],
+        'parseBetween'     => ['NOT BETWEEN', 'BETWEEN'],
+        'parseIn'          => ['NOT IN', 'IN'],
+        'parseExp'         => ['EXP'],
+        'parseNull'        => ['NOT NULL', 'NULL'],
+        'parseBetweenTime' => ['BETWEEN TIME', 'NOT BETWEEN TIME'],
+        'parseTime'        => ['< TIME', '> TIME', '<= TIME', '>= TIME'],
+        'parseExists'      => ['NOT EXISTS', 'EXISTS'],
+        'parseColumn'      => ['COLUMN'],
+    ];
+
+    /**
+     * SELECT SQL表达式
+     * @var string
+     */
+    protected $selectSql = 'SELECT%DISTINCT%%EXTRA% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%UNION%%ORDER%%LIMIT% %LOCK%%COMMENT%';
+
+    /**
+     * INSERT SQL表达式
+     * @var string
+     */
+    protected $insertSql = '%INSERT%%EXTRA% INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%';
+
+    /**
+     * INSERT ALL SQL表达式
+     * @var string
+     */
+    protected $insertAllSql = '%INSERT%%EXTRA% INTO %TABLE% (%FIELD%) %DATA% %COMMENT%';
+
+    /**
+     * UPDATE SQL表达式
+     * @var string
+     */
+    protected $updateSql = 'UPDATE%EXTRA% %TABLE% SET %SET%%JOIN%%WHERE%%ORDER%%LIMIT% %LOCK%%COMMENT%';
+
+    /**
+     * DELETE SQL表达式
+     * @var string
+     */
+    protected $deleteSql = 'DELETE%EXTRA% FROM %TABLE%%USING%%JOIN%%WHERE%%ORDER%%LIMIT% %LOCK%%COMMENT%';
+
+    /**
+     * 架构函数
+     * @access public
+     * @param  ConnectionInterface $connection 数据库连接对象实例
+     */
+    public function __construct(ConnectionInterface $connection)
+    {
+        $this->connection = $connection;
+    }
+
+    /**
+     * 获取当前的连接对象实例
+     * @access public
+     * @return ConnectionInterface
+     */
+    public function getConnection(): ConnectionInterface
+    {
+        return $this->connection;
+    }
+
+    /**
+     * 注册查询表达式解析
+     * @access public
+     * @param  string $name   解析方法
+     * @param  array  $parser 匹配表达式数据
+     * @return $this
+     */
+    public function bindParser(string $name, array $parser)
+    {
+        $this->parser[$name] = $parser;
+        return $this;
+    }
+
+    /**
+     * 数据分析
+     * @access protected
+     * @param  Query $query     查询对象
+     * @param  array $data      数据
+     * @param  array $fields    字段信息
+     * @param  array $bind      参数绑定
+     * @return array
+     */
+    protected function parseData(Query $query, array $data = [], array $fields = [], array $bind = []): array
+    {
+        if (empty($data)) {
+            return [];
+        }
+
+        $options = $query->getOptions();
+
+        // 获取绑定信息
+        if (empty($bind)) {
+            $bind = $query->getFieldsBindType();
+        }
+
+        if (empty($fields)) {
+            if ('*' == $options['field']) {
+                $fields = array_keys($bind);
+            } else {
+                $fields = $options['field'];
+            }
+        }
+
+        $result = [];
+
+        foreach ($data as $key => $val) {
+            $item = $this->parseKey($query, $key, true);
+
+            if ($val instanceof Raw) {
+                $result[$item] = $this->parseRaw($query, $val);
+                continue;
+            } elseif (!is_scalar($val) && (in_array($key, (array) $query->getOptions('json')) || 'json' == $query->getFieldType($key))) {
+                $val = json_encode($val);
+            }
+
+            if (false !== strpos($key, '->')) {
+                [$key, $name]  = explode('->', $key, 2);
+                $item          = $this->parseKey($query, $key);
+                $result[$item] = 'json_set(' . $item . ', \'$.' . $name . '\', ' . $this->parseDataBind($query, $key . '->' . $name, $val, $bind) . ')';
+            } elseif (false === strpos($key, '.') && !in_array($key, $fields, true)) {
+                if ($options['strict']) {
+                    throw new Exception('fields not exists:[' . $key . ']');
+                }
+            } elseif (is_null($val)) {
+                $result[$item] = 'NULL';
+            } elseif (is_array($val) && !empty($val) && is_string($val[0])) {
+                switch (strtoupper($val[0])) {
+                    case 'INC':
+                        $result[$item] = $item . ' + ' . floatval($val[1]);
+                        break;
+                    case 'DEC':
+                        $result[$item] = $item . ' - ' . floatval($val[1]);
+                        break;
+                }
+            } elseif (is_scalar($val)) {
+                // 过滤非标量数据
+                $result[$item] = $this->parseDataBind($query, $key, $val, $bind);
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * 数据绑定处理
+     * @access protected
+     * @param  Query  $query     查询对象
+     * @param  string $key       字段名
+     * @param  mixed  $data      数据
+     * @param  array  $bind      绑定数据
+     * @return string
+     */
+    protected function parseDataBind(Query $query, string $key, $data, array $bind = []): string
+    {
+        if ($data instanceof Raw) {
+            return $this->parseRaw($query, $data);
+        }
+
+        $name = $query->bindValue($data, $bind[$key] ?? PDO::PARAM_STR);
+
+        return ':' . $name;
+    }
+
+    /**
+     * 字段名分析
+     * @access public
+     * @param  Query  $query    查询对象
+     * @param  mixed  $key      字段名
+     * @param  bool   $strict   严格检测
+     * @return string
+     */
+    public function parseKey(Query $query, $key, bool $strict = false): string
+    {
+        return $key;
+    }
+
+    /**
+     * 查询额外参数分析
+     * @access protected
+     * @param  Query  $query    查询对象
+     * @param  string $extra    额外参数
+     * @return string
+     */
+    protected function parseExtra(Query $query, string $extra): string
+    {
+        return preg_match('/^[\w]+$/i', $extra) ? ' ' . strtoupper($extra) : '';
+    }
+
+    /**
+     * field分析
+     * @access protected
+     * @param  Query     $query     查询对象
+     * @param  mixed     $fields    字段名
+     * @return string
+     */
+    protected function parseField(Query $query, $fields): string
+    {
+        if (is_array($fields)) {
+            // 支持 'field1'=>'field2' 这样的字段别名定义
+            $array = [];
+
+            foreach ($fields as $key => $field) {
+                if ($field instanceof Raw) {
+                    $array[] = $this->parseRaw($query, $field);
+                } elseif (!is_numeric($key)) {
+                    $array[] = $this->parseKey($query, $key) . ' AS ' . $this->parseKey($query, $field, true);
+                } else {
+                    $array[] = $this->parseKey($query, $field);
+                }
+            }
+
+            $fieldsStr = implode(',', $array);
+        } else {
+            $fieldsStr = '*';
+        }
+
+        return $fieldsStr;
+    }
+
+    /**
+     * table分析
+     * @access protected
+     * @param  Query     $query     查询对象
+     * @param  mixed     $tables    表名
+     * @return string
+     */
+    protected function parseTable(Query $query, $tables): string
+    {
+        $item    = [];
+        $options = $query->getOptions();
+
+        foreach ((array) $tables as $key => $table) {
+            if ($table instanceof Raw) {
+                $item[] = $this->parseRaw($query, $table);
+            } elseif (!is_numeric($key)) {
+                $item[] = $this->parseKey($query, $key) . ' ' . $this->parseKey($query, $table);
+            } elseif (isset($options['alias'][$table])) {
+                $item[] = $this->parseKey($query, $table) . ' ' . $this->parseKey($query, $options['alias'][$table]);
+            } else {
+                $item[] = $this->parseKey($query, $table);
+            }
+        }
+
+        return implode(',', $item);
+    }
+
+    /**
+     * where分析
+     * @access protected
+     * @param  Query $query   查询对象
+     * @param  mixed $where   查询条件
+     * @return string
+     */
+    protected function parseWhere(Query $query, array $where): string
+    {
+        $options  = $query->getOptions();
+        $whereStr = $this->buildWhere($query, $where);
+
+        if (!empty($options['soft_delete'])) {
+            // 附加软删除条件
+            [$field, $condition] = $options['soft_delete'];
+
+            $binds    = $query->getFieldsBindType();
+            $whereStr = $whereStr ? '( ' . $whereStr . ' ) AND ' : '';
+            $whereStr = $whereStr . $this->parseWhereItem($query, $field, $condition, $binds);
+        }
+
+        return empty($whereStr) ? '' : ' WHERE ' . $whereStr;
+    }
+
+    /**
+     * 生成查询条件SQL
+     * @access public
+     * @param  Query     $query     查询对象
+     * @param  mixed     $where     查询条件
+     * @return string
+     */
+    public function buildWhere(Query $query, array $where): string
+    {
+        if (empty($where)) {
+            $where = [];
+        }
+
+        $whereStr = '';
+
+        $binds = $query->getFieldsBindType();
+
+        foreach ($where as $logic => $val) {
+            $str = $this->parseWhereLogic($query, $logic, $val, $binds);
+
+            $whereStr .= empty($whereStr) ? substr(implode(' ', $str), strlen($logic) + 1) : implode(' ', $str);
+        }
+
+        return $whereStr;
+    }
+
+    /**
+     * 不同字段使用相同查询条件(AND)
+     * @access protected
+     * @param  Query  $query 查询对象
+     * @param  string $logic Logic
+     * @param  array  $val   查询条件
+     * @param  array  $binds 参数绑定
+     * @return array
+     */
+    protected function parseWhereLogic(Query $query, string $logic, array $val, array $binds = []): array
+    {
+        $where = [];
+        foreach ($val as $value) {
+            if ($value instanceof Raw) {
+                $where[] = ' ' . $logic . ' ( ' . $this->parseRaw($query, $value) . ' )';
+                continue;
+            }
+
+            if (is_array($value)) {
+                if (key($value) !== 0) {
+                    throw new Exception('where express error:' . var_export($value, true));
+                }
+                $field = array_shift($value);
+            } elseif (true === $value) {
+                $where[] = ' ' . $logic . ' 1 ';
+                continue;
+            } elseif (!($value instanceof Closure)) {
+                throw new Exception('where express error:' . var_export($value, true));
+            }
+
+            if ($value instanceof Closure) {
+                // 使用闭包查询
+                $whereClosureStr = $this->parseClosureWhere($query, $value, $logic);
+                if ($whereClosureStr) {
+                    $where[] = $whereClosureStr;
+                }
+            } elseif (is_array($field)) {
+                $where[] = $this->parseMultiWhereField($query, $value, $field, $logic, $binds);
+            } elseif ($field instanceof Raw) {
+                $where[] = ' ' . $logic . ' ' . $this->parseWhereItem($query, $field, $value, $binds);
+            } elseif (strpos($field, '|')) {
+                $where[] = $this->parseFieldsOr($query, $value, $field, $logic, $binds);
+            } elseif (strpos($field, '&')) {
+                $where[] = $this->parseFieldsAnd($query, $value, $field, $logic, $binds);
+            } else {
+                // 对字段使用表达式查询
+                $field   = is_string($field) ? $field : '';
+                $where[] = ' ' . $logic . ' ' . $this->parseWhereItem($query, $field, $value, $binds);
+            }
+        }
+
+        return $where;
+    }
+
+    /**
+     * 不同字段使用相同查询条件(AND)
+     * @access protected
+     * @param  Query  $query 查询对象
+     * @param  mixed  $value 查询条件
+     * @param  string $field 查询字段
+     * @param  string $logic Logic
+     * @param  array  $binds 参数绑定
+     * @return string
+     */
+    protected function parseFieldsAnd(Query $query, $value, string $field, string $logic, array $binds): string
+    {
+        $item = [];
+
+        foreach (explode('&', $field) as $k) {
+            $item[] = $this->parseWhereItem($query, $k, $value, $binds);
+        }
+
+        return ' ' . $logic . ' ( ' . implode(' AND ', $item) . ' )';
+    }
+
+    /**
+     * 不同字段使用相同查询条件(OR)
+     * @access protected
+     * @param  Query  $query 查询对象
+     * @param  mixed  $value 查询条件
+     * @param  string $field 查询字段
+     * @param  string $logic Logic
+     * @param  array  $binds 参数绑定
+     * @return string
+     */
+    protected function parseFieldsOr(Query $query, $value, string $field, string $logic, array $binds): string
+    {
+        $item = [];
+
+        foreach (explode('|', $field) as $k) {
+            $item[] = $this->parseWhereItem($query, $k, $value, $binds);
+        }
+
+        return ' ' . $logic . ' ( ' . implode(' OR ', $item) . ' )';
+    }
+
+    /**
+     * 闭包查询
+     * @access protected
+     * @param  Query   $query 查询对象
+     * @param  Closure $value 查询条件
+     * @param  string  $logic Logic
+     * @return string
+     */
+    protected function parseClosureWhere(Query $query, Closure $value, string $logic): string
+    {
+        $newQuery = $query->newQuery();
+        $value($newQuery);
+        $whereClosure = $this->buildWhere($newQuery, $newQuery->getOptions('where') ?: []);
+
+        if (!empty($whereClosure)) {
+            $query->bind($newQuery->getBind(false));
+            $where = ' ' . $logic . ' ( ' . $whereClosure . ' )';
+        }
+
+        return $where ?? '';
+    }
+
+    /**
+     * 复合条件查询
+     * @access protected
+     * @param  Query  $query 查询对象
+     * @param  mixed  $value 查询条件
+     * @param  mixed  $field 查询字段
+     * @param  string $logic Logic
+     * @param  array  $binds 参数绑定
+     * @return string
+     */
+    protected function parseMultiWhereField(Query $query, $value, $field, string $logic, array $binds): string
+    {
+        array_unshift($value, $field);
+
+        $where = [];
+        foreach ($value as $item) {
+            $where[] = $this->parseWhereItem($query, array_shift($item), $item, $binds);
+        }
+
+        return ' ' . $logic . ' ( ' . implode(' AND ', $where) . ' )';
+    }
+
+    /**
+     * where子单元分析
+     * @access protected
+     * @param  Query $query 查询对象
+     * @param  mixed $field 查询字段
+     * @param  array $val   查询条件
+     * @param  array $binds 参数绑定
+     * @return string
+     */
+    protected function parseWhereItem(Query $query, $field, array $val, array $binds = []): string
+    {
+        // 字段分析
+        $key = $field ? $this->parseKey($query, $field, true) : '';
+
+        list($exp, $value) = $val;
+
+        // 检测操作符
+        if (!is_string($exp)) {
+            throw new Exception('where express error:' . var_export($exp, true));
+        }
+
+        $exp = strtoupper($exp);
+        if (isset($this->exp[$exp])) {
+            $exp = $this->exp[$exp];
+        }
+
+        if (is_string($field) && 'LIKE' != $exp) {
+            $bindType = $binds[$field] ?? PDO::PARAM_STR;
+        } else {
+            $bindType = PDO::PARAM_STR;
+        }
+
+        if ($value instanceof Raw) {
+
+        } elseif (is_object($value) && method_exists($value, '__toString')) {
+            // 对象数据写入
+            $value = $value->__toString();
+        }
+
+        if (is_scalar($value) && !in_array($exp, ['EXP', 'NOT NULL', 'NULL', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN']) && strpos($exp, 'TIME') === false) {
+            if (is_string($value) && 0 === strpos($value, ':') && $query->isBind(substr($value, 1))) {
+            } else {
+                $name  = $query->bindValue($value, $bindType);
+                $value = ':' . $name;
+            }
+        }
+
+        // 解析查询表达式
+        foreach ($this->parser as $fun => $parse) {
+            if (in_array($exp, $parse)) {
+                return $this->$fun($query, $key, $exp, $value, $field, $bindType, $val[2] ?? 'AND');
+            }
+        }
+
+        throw new Exception('where express error:' . $exp);
+    }
+
+    /**
+     * 模糊查询
+     * @access protected
+     * @param  Query   $query   查询对象
+     * @param  string  $key
+     * @param  string  $exp
+     * @param  array   $value
+     * @param  string  $field
+     * @param  integer $bindType
+     * @param  string  $logic
+     * @return string
+     */
+    protected function parseLike(Query $query, string $key, string $exp, $value, $field, int $bindType, string $logic): string
+    {
+        // 模糊匹配
+        if (is_array($value)) {
+            $array = [];
+            foreach ($value as $item) {
+                $name    = $query->bindValue($item, PDO::PARAM_STR);
+                $array[] = $key . ' ' . $exp . ' :' . $name;
+            }
+
+            $whereStr = '(' . implode(' ' . strtoupper($logic) . ' ', $array) . ')';
+        } else {
+            $whereStr = $key . ' ' . $exp . ' ' . $value;
+        }
+
+        return $whereStr;
+    }
+
+    /**
+     * 表达式查询
+     * @access protected
+     * @param  Query   $query   查询对象
+     * @param  string  $key
+     * @param  string  $exp
+     * @param  array   $value
+     * @param  string  $field
+     * @param  integer $bindType
+     * @return string
+     */
+    protected function parseExp(Query $query, string $key, string $exp, Raw $value, string $field, int $bindType): string
+    {
+        // 表达式查询
+        return '( ' . $key . ' ' . $this->parseRaw($query, $value) . ' )';
+    }
+
+    /**
+     * 表达式查询
+     * @access protected
+     * @param  Query   $query   查询对象
+     * @param  string  $key
+     * @param  string  $exp
+     * @param  array   $value
+     * @param  string  $field
+     * @param  integer $bindType
+     * @return string
+     */
+    protected function parseColumn(Query $query, string $key, $exp, array $value, string $field, int $bindType): string
+    {
+        // 字段比较查询
+        [$op, $field] = $value;
+
+        if (!in_array(trim($op), ['=', '<>', '>', '>=', '<', '<='])) {
+            throw new Exception('where express error:' . var_export($value, true));
+        }
+
+        return '( ' . $key . ' ' . $op . ' ' . $this->parseKey($query, $field, true) . ' )';
+    }
+
+    /**
+     * Null查询
+     * @access protected
+     * @param  Query   $query   查询对象
+     * @param  string  $key
+     * @param  string  $exp
+     * @param  mixed   $value
+     * @param  string  $field
+     * @param  integer $bindType
+     * @return string
+     */
+    protected function parseNull(Query $query, string $key, string $exp, $value, $field, int $bindType): string
+    {
+        // NULL 查询
+        return $key . ' IS ' . $exp;
+    }
+
+    /**
+     * 范围查询
+     * @access protected
+     * @param  Query   $query   查询对象
+     * @param  string  $key
+     * @param  string  $exp
+     * @param  mixed   $value
+     * @param  string  $field
+     * @param  integer $bindType
+     * @return string
+     */
+    protected function parseBetween(Query $query, string $key, string $exp, $value, $field, int $bindType): string
+    {
+        // BETWEEN 查询
+        $data = is_array($value) ? $value : explode(',', $value);
+
+        $min = $query->bindValue($data[0], $bindType);
+        $max = $query->bindValue($data[1], $bindType);
+
+        return $key . ' ' . $exp . ' :' . $min . ' AND :' . $max . ' ';
+    }
+
+    /**
+     * Exists查询
+     * @access protected
+     * @param  Query   $query   查询对象
+     * @param  string  $key
+     * @param  string  $exp
+     * @param  mixed   $value
+     * @param  string  $field
+     * @param  integer $bindType
+     * @return string
+     */
+    protected function parseExists(Query $query, string $key, string $exp, $value, string $field, int $bindType): string
+    {
+        // EXISTS 查询
+        if ($value instanceof Closure) {
+            $value = $this->parseClosure($query, $value, false);
+        } elseif ($value instanceof Raw) {
+            $value = $this->parseRaw($query, $value);
+        } else {
+            throw new Exception('where express error:' . $value);
+        }
+
+        return $exp . ' ( ' . $value . ' )';
+    }
+
+    /**
+     * 时间比较查询
+     * @access protected
+     * @param  Query   $query  查询对象
+     * @param  string  $key
+     * @param  string  $exp
+     * @param  mixed   $value
+     * @param  string  $field
+     * @param  integer $bindType
+     * @return string
+     */
+    protected function parseTime(Query $query, string $key, string $exp, $value, $field, int $bindType): string
+    {
+        return $key . ' ' . substr($exp, 0, 2) . ' ' . $this->parseDateTime($query, $value, $field, $bindType);
+    }
+
+    /**
+     * 大小比较查询
+     * @access protected
+     * @param  Query   $query   查询对象
+     * @param  string  $key
+     * @param  string  $exp
+     * @param  mixed   $value
+     * @param  string  $field
+     * @param  integer $bindType
+     * @return string
+     */
+    protected function parseCompare(Query $query, string $key, string $exp, $value, $field, int $bindType): string
+    {
+        if (is_array($value)) {
+            throw new Exception('where express error:' . $exp . var_export($value, true));
+        }
+
+        // 比较运算
+        if ($value instanceof Closure) {
+            $value = $this->parseClosure($query, $value);
+        }
+
+        if ('=' == $exp && is_null($value)) {
+            return $key . ' IS NULL';
+        }
+
+        return $key . ' ' . $exp . ' ' . $value;
+    }
+
+    /**
+     * 时间范围查询
+     * @access protected
+     * @param  Query   $query     查询对象
+     * @param  string  $key
+     * @param  string  $exp
+     * @param  mixed   $value
+     * @param  string  $field
+     * @param  integer $bindType
+     * @return string
+     */
+    protected function parseBetweenTime(Query $query, string $key, string $exp, $value, $field, int $bindType): string
+    {
+        if (is_string($value)) {
+            $value = explode(',', $value);
+        }
+
+        return $key . ' ' . substr($exp, 0, -4)
+        . $this->parseDateTime($query, $value[0], $field, $bindType)
+        . ' AND '
+        . $this->parseDateTime($query, $value[1], $field, $bindType);
+
+    }
+
+    /**
+     * IN查询
+     * @access protected
+     * @param  Query   $query   查询对象
+     * @param  string  $key
+     * @param  string  $exp
+     * @param  mixed   $value
+     * @param  string  $field
+     * @param  integer $bindType
+     * @return string
+     */
+    protected function parseIn(Query $query, string $key, string $exp, $value, $field, int $bindType): string
+    {
+        // IN 查询
+        if ($value instanceof Closure) {
+            $value = $this->parseClosure($query, $value, false);
+        } elseif ($value instanceof Raw) {
+            $value = $this->parseRaw($query, $value);
+        } else {
+            $value = array_unique(is_array($value) ? $value : explode(',', $value));
+            $array = [];
+
+            foreach ($value as $v) {
+                $name    = $query->bindValue($v, $bindType);
+                $array[] = ':' . $name;
+            }
+
+            if (count($array) == 1) {
+                return $key . ('IN' == $exp ? ' = ' : ' <> ') . $array[0];
+            } else {
+                $zone  = implode(',', $array);
+                $value = empty($zone) ? "''" : $zone;
+            }
+        }
+
+        return $key . ' ' . $exp . ' (' . $value . ')';
+    }
+
+    /**
+     * 闭包子查询
+     * @access protected
+     * @param  Query    $query 查询对象
+     * @param  \Closure $call
+     * @param  bool     $show
+     * @return string
+     */
+    protected function parseClosure(Query $query, Closure $call, bool $show = true): string
+    {
+        $newQuery = $query->newQuery()->removeOption();
+        $call($newQuery);
+
+        return $newQuery->buildSql($show);
+    }
+
+    /**
+     * 日期时间条件解析
+     * @access protected
+     * @param  Query   $query 查询对象
+     * @param  mixed   $value
+     * @param  string  $key
+     * @param  integer $bindType
+     * @return string
+     */
+    protected function parseDateTime(Query $query, $value, string $key, int $bindType): string
+    {
+        $options = $query->getOptions();
+
+        // 获取时间字段类型
+        if (strpos($key, '.')) {
+            [$table, $key] = explode('.', $key);
+
+            if (isset($options['alias']) && $pos = array_search($table, $options['alias'])) {
+                $table = $pos;
+            }
+        } else {
+            $table = $options['table'];
+        }
+
+        $type = $query->getFieldType($key);
+
+        if ($type) {
+            if (is_string($value)) {
+                $value = strtotime($value) ?: $value;
+            }
+
+            if (is_int($value)) {
+                if (preg_match('/(datetime|timestamp)/is', $type)) {
+                    // 日期及时间戳类型
+                    $value = date('Y-m-d H:i:s', $value);
+                } elseif (preg_match('/(date)/is', $type)) {
+                    // 日期及时间戳类型
+                    $value = date('Y-m-d', $value);
+                }
+            }
+        }
+
+        $name = $query->bindValue($value, $bindType);
+
+        return ':' . $name;
+    }
+
+    /**
+     * limit分析
+     * @access protected
+     * @param  Query $query 查询对象
+     * @param  mixed $limit
+     * @return string
+     */
+    protected function parseLimit(Query $query, string $limit): string
+    {
+        return (!empty($limit) && false === strpos($limit, '(')) ? ' LIMIT ' . $limit . ' ' : '';
+    }
+
+    /**
+     * join分析
+     * @access protected
+     * @param  Query $query 查询对象
+     * @param  array $join
+     * @return string
+     */
+    protected function parseJoin(Query $query, array $join): string
+    {
+        $joinStr = '';
+
+        foreach ($join as $item) {
+            [$table, $type, $on] = $item;
+
+            if (strpos($on, '=')) {
+                [$val1, $val2] = explode('=', $on, 2);
+
+                $condition = $this->parseKey($query, $val1) . '=' . $this->parseKey($query, $val2);
+            } else {
+                $condition = $on;
+            }
+
+            $table = $this->parseTable($query, $table);
+
+            $joinStr .= ' ' . $type . ' JOIN ' . $table . ' ON ' . $condition;
+        }
+
+        return $joinStr;
+    }
+
+    /**
+     * order分析
+     * @access protected
+     * @param  Query $query 查询对象
+     * @param  array $order
+     * @return string
+     */
+    protected function parseOrder(Query $query, array $order): string
+    {
+        $array = [];
+        foreach ($order as $key => $val) {
+            if ($val instanceof Raw) {
+                $array[] = $this->parseRaw($query, $val);
+            } elseif (is_array($val) && preg_match('/^[\w\.]+$/', $key)) {
+                $array[] = $this->parseOrderField($query, $key, $val);
+            } elseif ('[rand]' == $val) {
+                $array[] = $this->parseRand($query);
+            } elseif (is_string($val)) {
+                if (is_numeric($key)) {
+                    list($key, $sort) = explode(' ', strpos($val, ' ') ? $val : $val . ' ');
+                } else {
+                    $sort = $val;
+                }
+
+                if (preg_match('/^[\w\.]+$/', $key)) {
+                    $sort    = strtoupper($sort);
+                    $sort    = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : '';
+                    $array[] = $this->parseKey($query, $key, true) . $sort;
+                } else {
+                    throw new Exception('order express error:' . $key);
+                }
+            }
+        }
+
+        return empty($array) ? '' : ' ORDER BY ' . implode(',', $array);
+    }
+
+    /**
+     * 分析Raw对象
+     * @access protected
+     * @param  Query $query 查询对象
+     * @param  Raw   $raw   Raw对象
+     * @return string
+     */
+    protected function parseRaw(Query $query, Raw $raw): string
+    {
+        $sql  = $raw->getValue();
+        $bind = $raw->getBind();
+
+        if ($bind) {
+            $query->bindParams($sql, $bind);
+        }
+
+        return $sql;
+    }
+
+    /**
+     * 随机排序
+     * @access protected
+     * @param  Query $query 查询对象
+     * @return string
+     */
+    protected function parseRand(Query $query): string
+    {
+        return '';
+    }
+
+    /**
+     * orderField分析
+     * @access protected
+     * @param  Query  $query 查询对象
+     * @param  string $key
+     * @param  array  $val
+     * @return string
+     */
+    protected function parseOrderField(Query $query, string $key, array $val): string
+    {
+        if (isset($val['sort'])) {
+            $sort = $val['sort'];
+            unset($val['sort']);
+        } else {
+            $sort = '';
+        }
+
+        $sort = strtoupper($sort);
+        $sort = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : '';
+        $bind = $query->getFieldsBindType();
+
+        foreach ($val as $item) {
+            $val[] = $this->parseDataBind($query, $key, $item, $bind);
+        }
+
+        return 'field(' . $this->parseKey($query, $key, true) . ',' . implode(',', $val) . ')' . $sort;
+    }
+
+    /**
+     * group分析
+     * @access protected
+     * @param  Query $query 查询对象
+     * @param  mixed $group
+     * @return string
+     */
+    protected function parseGroup(Query $query, $group): string
+    {
+        if (empty($group)) {
+            return '';
+        }
+
+        if (is_string($group)) {
+            $group = explode(',', $group);
+        }
+
+        $val = [];
+        foreach ($group as $key) {
+            $val[] = $this->parseKey($query, $key);
+        }
+
+        return ' GROUP BY ' . implode(',', $val);
+    }
+
+    /**
+     * having分析
+     * @access protected
+     * @param  Query  $query  查询对象
+     * @param  string $having
+     * @return string
+     */
+    protected function parseHaving(Query $query, string $having): string
+    {
+        return !empty($having) ? ' HAVING ' . $having : '';
+    }
+
+    /**
+     * comment分析
+     * @access protected
+     * @param  Query  $query  查询对象
+     * @param  string $comment
+     * @return string
+     */
+    protected function parseComment(Query $query, string $comment): string
+    {
+        if (false !== strpos($comment, '*/')) {
+            $comment = strstr($comment, '*/', true);
+        }
+
+        return !empty($comment) ? ' /* ' . $comment . ' */' : '';
+    }
+
+    /**
+     * distinct分析
+     * @access protected
+     * @param  Query $query  查询对象
+     * @param  mixed $distinct
+     * @return string
+     */
+    protected function parseDistinct(Query $query, bool $distinct): string
+    {
+        return !empty($distinct) ? ' DISTINCT ' : '';
+    }
+
+    /**
+     * union分析
+     * @access protected
+     * @param  Query $query 查询对象
+     * @param  array $union
+     * @return string
+     */
+    protected function parseUnion(Query $query, array $union): string
+    {
+        if (empty($union)) {
+            return '';
+        }
+
+        $type = $union['type'];
+        unset($union['type']);
+
+        foreach ($union as $u) {
+            if ($u instanceof Closure) {
+                $sql[] = $type . ' ' . $this->parseClosure($query, $u);
+            } elseif (is_string($u)) {
+                $sql[] = $type . ' ( ' . $u . ' )';
+            }
+        }
+
+        return ' ' . implode(' ', $sql);
+    }
+
+    /**
+     * index分析,可在操作链中指定需要强制使用的索引
+     * @access protected
+     * @param  Query $query 查询对象
+     * @param  mixed $index
+     * @return string
+     */
+    protected function parseForce(Query $query, $index): string
+    {
+        if (empty($index)) {
+            return '';
+        }
+
+        if (is_array($index)) {
+            $index = join(',', $index);
+        }
+
+        return sprintf(" FORCE INDEX ( %s ) ", $index);
+    }
+
+    /**
+     * 设置锁机制
+     * @access protected
+     * @param  Query       $query 查询对象
+     * @param  bool|string $lock
+     * @return string
+     */
+    protected function parseLock(Query $query, $lock = false): string
+    {
+        if (is_bool($lock)) {
+            return $lock ? ' FOR UPDATE ' : '';
+        }
+
+        if (is_string($lock) && !empty($lock)) {
+            return ' ' . trim($lock) . ' ';
+        } else {
+            return '';
+        }
+    }
+
+    /**
+     * 生成查询SQL
+     * @access public
+     * @param  Query $query 查询对象
+     * @param  bool  $one   是否仅获取一个记录
+     * @return string
+     */
+    public function select(Query $query, bool $one = false): string
+    {
+        $options = $query->getOptions();
+
+        return str_replace(
+            ['%TABLE%', '%DISTINCT%', '%EXTRA%', '%FIELD%', '%JOIN%', '%WHERE%', '%GROUP%', '%HAVING%', '%ORDER%', '%LIMIT%', '%UNION%', '%LOCK%', '%COMMENT%', '%FORCE%'],
+            [
+                $this->parseTable($query, $options['table']),
+                $this->parseDistinct($query, $options['distinct']),
+                $this->parseExtra($query, $options['extra']),
+                $this->parseField($query, $options['field']),
+                $this->parseJoin($query, $options['join']),
+                $this->parseWhere($query, $options['where']),
+                $this->parseGroup($query, $options['group']),
+                $this->parseHaving($query, $options['having']),
+                $this->parseOrder($query, $options['order']),
+                $this->parseLimit($query, $one ? '1' : $options['limit']),
+                $this->parseUnion($query, $options['union']),
+                $this->parseLock($query, $options['lock']),
+                $this->parseComment($query, $options['comment']),
+                $this->parseForce($query, $options['force']),
+            ],
+            $this->selectSql);
+    }
+
+    /**
+     * 生成Insert SQL
+     * @access public
+     * @param  Query $query 查询对象
+     * @return string
+     */
+    public function insert(Query $query): string
+    {
+        $options = $query->getOptions();
+
+        // 分析并处理数据
+        $data = $this->parseData($query, $options['data']);
+        if (empty($data)) {
+            return '';
+        }
+
+        $fields = array_keys($data);
+        $values = array_values($data);
+
+        return str_replace(
+            ['%INSERT%', '%TABLE%', '%EXTRA%', '%FIELD%', '%DATA%', '%COMMENT%'],
+            [
+                !empty($options['replace']) ? 'REPLACE' : 'INSERT',
+                $this->parseTable($query, $options['table']),
+                $this->parseExtra($query, $options['extra']),
+                implode(' , ', $fields),
+                implode(' , ', $values),
+                $this->parseComment($query, $options['comment']),
+            ],
+            $this->insertSql);
+    }
+
+    /**
+     * 生成insertall SQL
+     * @access public
+     * @param  Query $query   查询对象
+     * @param  array $dataSet 数据集
+     * @return string
+     */
+    public function insertAll(Query $query, array $dataSet): string
+    {
+        $options = $query->getOptions();
+
+        // 获取绑定信息
+        $bind = $query->getFieldsBindType();
+
+        // 获取合法的字段
+        if ('*' == $options['field']) {
+            $allowFields = array_keys($bind);
+        } else {
+            $allowFields = $options['field'];
+        }
+
+        $fields = [];
+        $values = [];
+
+        foreach ($dataSet as $k => $data) {
+            $data = $this->parseData($query, $data, $allowFields, $bind);
+
+            $values[] = 'SELECT ' . implode(',', array_values($data));
+
+            if (!isset($insertFields)) {
+                $insertFields = array_keys($data);
+            }
+        }
+
+        foreach ($insertFields as $field) {
+            $fields[] = $this->parseKey($query, $field);
+        }
+
+        return str_replace(
+            ['%INSERT%', '%TABLE%', '%EXTRA%', '%FIELD%', '%DATA%', '%COMMENT%'],
+            [
+                !empty($options['replace']) ? 'REPLACE' : 'INSERT',
+                $this->parseTable($query, $options['table']),
+                $this->parseExtra($query, $options['extra']),
+                implode(' , ', $fields),
+                implode(' UNION ALL ', $values),
+                $this->parseComment($query, $options['comment']),
+            ],
+            $this->insertAllSql);
+    }
+
+    /**
+     * 生成slect insert SQL
+     * @access public
+     * @param  Query  $query  查询对象
+     * @param  array  $fields 数据
+     * @param  string $table  数据表
+     * @return string
+     */
+    public function selectInsert(Query $query, array $fields, string $table): string
+    {
+        foreach ($fields as &$field) {
+            $field = $this->parseKey($query, $field, true);
+        }
+
+        return 'INSERT INTO ' . $this->parseTable($query, $table) . ' (' . implode(',', $fields) . ') ' . $this->select($query);
+    }
+
+    /**
+     * 生成update SQL
+     * @access public
+     * @param  Query $query 查询对象
+     * @return string
+     */
+    public function update(Query $query): string
+    {
+        $options = $query->getOptions();
+
+        $data = $this->parseData($query, $options['data']);
+
+        if (empty($data)) {
+            return '';
+        }
+
+        $set = [];
+        foreach ($data as $key => $val) {
+            $set[] = $key . ' = ' . $val;
+        }
+
+        return str_replace(
+            ['%TABLE%', '%EXTRA%', '%SET%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'],
+            [
+                $this->parseTable($query, $options['table']),
+                $this->parseExtra($query, $options['extra']),
+                implode(' , ', $set),
+                $this->parseJoin($query, $options['join']),
+                $this->parseWhere($query, $options['where']),
+                $this->parseOrder($query, $options['order']),
+                $this->parseLimit($query, $options['limit']),
+                $this->parseLock($query, $options['lock']),
+                $this->parseComment($query, $options['comment']),
+            ],
+            $this->updateSql);
+    }
+
+    /**
+     * 生成delete SQL
+     * @access public
+     * @param  Query $query 查询对象
+     * @return string
+     */
+    public function delete(Query $query): string
+    {
+        $options = $query->getOptions();
+
+        return str_replace(
+            ['%TABLE%', '%EXTRA%', '%USING%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'],
+            [
+                $this->parseTable($query, $options['table']),
+                $this->parseExtra($query, $options['extra']),
+                !empty($options['using']) ? ' USING ' . $this->parseTable($query, $options['using']) . ' ' : '',
+                $this->parseJoin($query, $options['join']),
+                $this->parseWhere($query, $options['where']),
+                $this->parseOrder($query, $options['order']),
+                $this->parseLimit($query, $options['limit']),
+                $this->parseLock($query, $options['lock']),
+                $this->parseComment($query, $options['comment']),
+            ],
+            $this->deleteSql);
+    }
+}
diff --git a/vendor/topthink/think-orm/src/db/CacheItem.php b/vendor/topthink/think-orm/src/db/CacheItem.php
new file mode 100755
index 0000000..839f384
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/CacheItem.php
@@ -0,0 +1,209 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db;
+
+use DateInterval;
+use DateTime;
+use DateTimeInterface;
+use think\db\exception\InvalidArgumentException;
+
+/**
+ * CacheItem实现类
+ */
+class CacheItem
+{
+    /**
+     * 缓存Key
+     * @var string
+     */
+    protected $key;
+
+    /**
+     * 缓存内容
+     * @var mixed
+     */
+    protected $value;
+
+    /**
+     * 过期时间
+     * @var int|DateTimeInterface
+     */
+    protected $expire;
+
+    /**
+     * 缓存tag
+     * @var string
+     */
+    protected $tag;
+
+    /**
+     * 缓存是否命中
+     * @var bool
+     */
+    protected $isHit = false;
+
+    public function __construct(string $key = null)
+    {
+        $this->key = $key;
+    }
+
+    /**
+     * 为此缓存项设置「键」
+     * @access public
+     * @param  string $key
+     * @return $this
+     */
+    public function setKey(string $key)
+    {
+        $this->key = $key;
+        return $this;
+    }
+
+    /**
+     * 返回当前缓存项的「键」
+     * @access public
+     * @return string
+     */
+    public function getKey()
+    {
+        return $this->key;
+    }
+
+    /**
+     * 返回当前缓存项的有效期
+     * @access public
+     * @return DateTimeInterface|int|null
+     */
+    public function getExpire()
+    {
+        if ($this->expire instanceof DateTimeInterface) {
+            return $this->expire;
+        }
+
+        return $this->expire ? $this->expire - time() : null;
+    }
+
+    /**
+     * 获取缓存Tag
+     * @access public
+     * @return string|array
+     */
+    public function getTag()
+    {
+        return $this->tag;
+    }
+
+    /**
+     * 凭借此缓存项的「键」从缓存系统里面取出缓存项
+     * @access public
+     * @return mixed
+     */
+    public function get()
+    {
+        return $this->value;
+    }
+
+    /**
+     * 确认缓存项的检查是否命中
+     * @access public
+     * @return bool
+     */
+    public function isHit(): bool
+    {
+        return $this->isHit;
+    }
+
+    /**
+     * 为此缓存项设置「值」
+     * @access public
+     * @param  mixed $value
+     * @return $this
+     */
+    public function set($value)
+    {
+        $this->value = $value;
+        $this->isHit = true;
+        return $this;
+    }
+
+    /**
+     * 为此缓存项设置所属标签
+     * @access public
+     * @param  string|array $tag
+     * @return $this
+     */
+    public function tag($tag = null)
+    {
+        $this->tag = $tag;
+        return $this;
+    }
+
+    /**
+     * 设置缓存项的有效期
+     * @access public
+     * @param  mixed $expire
+     * @return $this
+     */
+    public function expire($expire)
+    {
+        if (is_null($expire)) {
+            $this->expire = null;
+        } elseif (is_numeric($expire) || $expire instanceof DateInterval) {
+            $this->expiresAfter($expire);
+        } elseif ($expire instanceof DateTimeInterface) {
+            $this->expire = $expire;
+        } else {
+            throw new InvalidArgumentException('not support datetime');
+        }
+
+        return $this;
+    }
+
+    /**
+     * 设置缓存项的准确过期时间点
+     * @access public
+     * @param  DateTimeInterface $expiration
+     * @return $this
+     */
+    public function expiresAt($expiration)
+    {
+        if ($expiration instanceof DateTimeInterface) {
+            $this->expire = $expiration;
+        } else {
+            throw new InvalidArgumentException('not support datetime');
+        }
+
+        return $this;
+    }
+
+    /**
+     * 设置缓存项的过期时间
+     * @access public
+     * @param int|DateInterval $timeInterval
+     * @return $this
+     * @throws InvalidArgumentException
+     */
+    public function expiresAfter($timeInterval)
+    {
+        if ($timeInterval instanceof DateInterval) {
+            $this->expire = (int) DateTime::createFromFormat('U', (string) time())->add($timeInterval)->format('U');
+        } elseif (is_numeric($timeInterval)) {
+            $this->expire = $timeInterval + time();
+        } else {
+            throw new InvalidArgumentException('not support datetime');
+        }
+
+        return $this;
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/db/Connection.php b/vendor/topthink/think-orm/src/db/Connection.php
new file mode 100755
index 0000000..cb6a912
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/Connection.php
@@ -0,0 +1,332 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db;
+
+use Psr\SimpleCache\CacheInterface;
+use think\DbManager;
+
+/**
+ * 数据库连接基础类
+ */
+abstract class Connection implements ConnectionInterface
+{
+
+    /**
+     * 当前SQL指令
+     * @var string
+     */
+    protected $queryStr = '';
+
+    /**
+     * 返回或者影响记录数
+     * @var int
+     */
+    protected $numRows = 0;
+
+    /**
+     * 事务指令数
+     * @var int
+     */
+    protected $transTimes = 0;
+
+    /**
+     * 错误信息
+     * @var string
+     */
+    protected $error = '';
+
+    /**
+     * 数据库连接ID 支持多个连接
+     * @var array
+     */
+    protected $links = [];
+
+    /**
+     * 当前连接ID
+     * @var object
+     */
+    protected $linkID;
+
+    /**
+     * 当前读连接ID
+     * @var object
+     */
+    protected $linkRead;
+
+    /**
+     * 当前写连接ID
+     * @var object
+     */
+    protected $linkWrite;
+
+    /**
+     * 数据表信息
+     * @var array
+     */
+    protected $info = [];
+
+    /**
+     * 查询开始时间
+     * @var float
+     */
+    protected $queryStartTime;
+
+    /**
+     * Builder对象
+     * @var Builder
+     */
+    protected $builder;
+
+    /**
+     * Db对象
+     * @var DbManager
+     */
+    protected $db;
+
+    /**
+     * 是否读取主库
+     * @var bool
+     */
+    protected $readMaster = false;
+
+    /**
+     * 数据库连接参数配置
+     * @var array
+     */
+    protected $config = [];
+
+    /**
+     * 缓存对象
+     * @var CacheInterface
+     */
+    protected $cache;
+
+    /**
+     * 架构函数 读取数据库配置信息
+     * @access public
+     * @param array $config 数据库配置数组
+     */
+    public function __construct(array $config = [])
+    {
+        if (!empty($config)) {
+            $this->config = array_merge($this->config, $config);
+        }
+
+        // 创建Builder对象
+        $class = $this->getBuilderClass();
+
+        $this->builder = new $class($this);
+    }
+
+    /**
+     * 获取当前的builder实例对象
+     * @access public
+     * @return Builder
+     */
+    public function getBuilder()
+    {
+        return $this->builder;
+    }
+
+
+    /**
+     * 创建查询对象
+     */
+    public function newQuery()
+    {
+        $class = $this->getQueryClass();
+
+        /** @var BaseQuery $query */
+        $query = new $class($this);
+
+        $timeRule = $this->db->getConfig('time_query_rule');
+        if (!empty($timeRule)) {
+            $query->timeRule($timeRule);
+        }
+
+        return $query;
+    }
+
+    /**
+     * 指定表名开始查询
+     * @param $table
+     * @return BaseQuery
+     */
+    public function table($table)
+    {
+        return $this->newQuery()->table($table);
+    }
+
+    /**
+     * 指定表名开始查询(不带前缀)
+     * @param $name
+     * @return BaseQuery
+     */
+    public function name($name)
+    {
+        return $this->newQuery()->name($name);
+    }
+
+    /**
+     * 设置当前的数据库Db对象
+     * @access public
+     * @param DbManager $db
+     * @return void
+     */
+    public function setDb(DbManager $db)
+    {
+        $this->db = $db;
+    }
+
+    /**
+     * 设置当前的缓存对象
+     * @access public
+     * @param CacheInterface $cache
+     * @return void
+     */
+    public function setCache(CacheInterface $cache)
+    {
+        $this->cache = $cache;
+    }
+
+    /**
+     * 获取当前的缓存对象
+     * @access public
+     * @return CacheInterface|null
+     */
+    public function getCache()
+    {
+        return $this->cache;
+    }
+
+    /**
+     * 获取数据库的配置参数
+     * @access public
+     * @param string $config 配置名称
+     * @return mixed
+     */
+    public function getConfig(string $config = '')
+    {
+        if ('' === $config) {
+            return $this->config;
+        }
+
+        return $this->config[$config] ?? null;
+    }
+
+    /**
+     * 数据库SQL监控
+     * @access protected
+     * @param string $sql    执行的SQL语句 留空自动获取
+     * @param bool   $master  主从标记
+     * @return void
+     */
+    protected function trigger(string $sql = '', bool $master = false): void
+    {
+        $listen = $this->db->getListen();
+
+        if (!empty($listen)) {
+            $runtime = number_format((microtime(true) - $this->queryStartTime), 6);
+            $sql     = $sql ?: $this->getLastsql();
+
+            if (empty($this->config['deploy'])) {
+                $master = null;
+            }
+
+            foreach ($listen as $callback) {
+                if (is_callable($callback)) {
+                    $callback($sql, $runtime, $master);
+                }
+            }
+        }
+    }
+
+    /**
+     * 缓存数据
+     * @access protected
+     * @param CacheItem $cacheItem 缓存Item
+     */
+    protected function cacheData(CacheItem $cacheItem)
+    {
+        if ($cacheItem->getTag() && method_exists($this->cache, 'tag')) {
+            $this->cache->tag($cacheItem->getTag())->set($cacheItem->getKey(), $cacheItem->get(), $cacheItem->getExpire());
+        } else {
+            $this->cache->set($cacheItem->getKey(), $cacheItem->get(), $cacheItem->getExpire());
+        }
+    }
+
+    /**
+     * 分析缓存Key
+     * @access protected
+     * @param BaseQuery $query 查询对象
+     * @param string    $method 查询方法
+     * @return string
+     */
+    protected function getCacheKey(BaseQuery $query, string $method = ''): string
+    {
+        if (!empty($query->getOptions('key')) && empty($method)) {
+            $key = 'think:' . $this->getConfig('database') . '.' . $query->getTable() . '|' . $query->getOptions('key');
+        } else {
+            $key = $query->getQueryGuid();
+        }
+
+        return $key;
+    }
+
+    /**
+     * 分析缓存
+     * @access protected
+     * @param BaseQuery $query 查询对象
+     * @param array     $cache 缓存信息
+     * @param string    $method 查询方法
+     * @return CacheItem
+     */
+    protected function parseCache(BaseQuery $query, array $cache, string $method = ''): CacheItem
+    {
+        [$key, $expire, $tag] = $cache;
+
+        if ($key instanceof CacheItem) {
+            $cacheItem = $key;
+        } else {
+            if (true === $key) {
+                $key = $this->getCacheKey($query, $method);
+            }
+
+            $cacheItem = new CacheItem($key);
+            $cacheItem->expire($expire);
+            $cacheItem->tag($tag);
+        }
+
+        return $cacheItem;
+    }
+
+    /**
+     * 获取返回或者影响的记录数
+     * @access public
+     * @return integer
+     */
+    public function getNumRows(): int
+    {
+        return $this->numRows;
+    }
+
+    /**
+     * 析构方法
+     * @access public
+     */
+    public function __destruct()
+    {
+        // 关闭连接
+        $this->close();
+    }
+}
diff --git a/vendor/topthink/think-orm/src/db/ConnectionInterface.php b/vendor/topthink/think-orm/src/db/ConnectionInterface.php
new file mode 100755
index 0000000..207a424
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/ConnectionInterface.php
@@ -0,0 +1,190 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db;
+
+use Psr\SimpleCache\CacheInterface;
+use think\DbManager;
+
+/**
+ * Connection interface
+ */
+interface ConnectionInterface
+{
+    /**
+     * 获取当前连接器类对应的Query类
+     * @access public
+     * @return string
+     */
+    public function getQueryClass(): string;
+
+    /**
+     * 指定表名开始查询
+     * @param $table
+     * @return BaseQuery
+     */
+    public function table($table);
+
+    /**
+     * 指定表名开始查询(不带前缀)
+     * @param $name
+     * @return BaseQuery
+     */
+    public function name($name);
+
+    /**
+     * 连接数据库方法
+     * @access public
+     * @param array   $config  接参数
+     * @param integer $linkNum 连接序号
+     * @return mixed
+     */
+    public function connect(array $config = [], $linkNum = 0);
+
+    /**
+     * 设置当前的数据库Db对象
+     * @access public
+     * @param DbManager $db
+     * @return void
+     */
+    public function setDb(DbManager $db);
+
+    /**
+     * 设置当前的缓存对象
+     * @access public
+     * @param CacheInterface $cache
+     * @return void
+     */
+    public function setCache(CacheInterface $cache);
+
+    /**
+     * 获取数据库的配置参数
+     * @access public
+     * @param string $config 配置名称
+     * @return mixed
+     */
+    public function getConfig(string $config = '');
+
+    /**
+     * 关闭数据库(或者重新连接)
+     * @access public
+     * @return $this
+     */
+    public function close();
+
+    /**
+     * 查找单条记录
+     * @access public
+     * @param BaseQuery $query 查询对象
+     * @return array
+     */
+    public function find(BaseQuery $query): array;
+
+    /**
+     * 查找记录
+     * @access public
+     * @param BaseQuery $query 查询对象
+     * @return array
+     */
+    public function select(BaseQuery $query): array;
+
+    /**
+     * 插入记录
+     * @access public
+     * @param BaseQuery   $query        查询对象
+     * @param boolean $getLastInsID 返回自增主键
+     * @return mixed
+     */
+    public function insert(BaseQuery $query, bool $getLastInsID = false);
+
+    /**
+     * 批量插入记录
+     * @access public
+     * @param BaseQuery   $query   查询对象
+     * @param mixed   $dataSet 数据集
+     * @return integer
+     */
+    public function insertAll(BaseQuery $query, array $dataSet = []): int;
+
+    /**
+     * 更新记录
+     * @access public
+     * @param BaseQuery $query 查询对象
+     * @return integer
+     */
+    public function update(BaseQuery $query): int;
+
+    /**
+     * 删除记录
+     * @access public
+     * @param BaseQuery $query 查询对象
+     * @return int
+     */
+    public function delete(BaseQuery $query): int;
+
+    /**
+     * 得到某个字段的值
+     * @access public
+     * @param BaseQuery  $query   查询对象
+     * @param string $field   字段名
+     * @param mixed  $default 默认值
+     * @return mixed
+     */
+    public function value(BaseQuery $query, string $field, $default = null);
+
+    /**
+     * 得到某个列的数组
+     * @access public
+     * @param BaseQuery  $query  查询对象
+     * @param string $column 字段名 多个字段用逗号分隔
+     * @param string $key    索引
+     * @return array
+     */
+    public function column(BaseQuery $query, string $column, string $key = ''): array;
+
+    /**
+     * 执行数据库事务
+     * @access public
+     * @param callable $callback 数据操作方法回调
+     * @return mixed
+     */
+    public function transaction(callable $callback);
+
+    /**
+     * 启动事务
+     * @access public
+     * @return void
+     */
+    public function startTrans();
+
+    /**
+     * 用于非自动提交状态下面的查询提交
+     * @access public
+     * @return void
+     */
+    public function commit();
+
+    /**
+     * 事务回滚
+     * @access public
+     * @return void
+     */
+    public function rollback();
+
+    /**
+     * 获取最近一次查询的sql语句
+     * @access public
+     * @return string
+     */
+    public function getLastSql(): string;
+
+}
diff --git a/vendor/topthink/think-orm/src/db/Fetch.php b/vendor/topthink/think-orm/src/db/Fetch.php
new file mode 100755
index 0000000..16caed2
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/Fetch.php
@@ -0,0 +1,494 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db;
+
+use think\db\exception\DbException as Exception;
+use think\helper\Str;
+
+/**
+ * SQL获取类
+ */
+class Fetch
+{
+    /**
+     * 查询对象
+     * @var Query
+     */
+    protected $query;
+
+    /**
+     * Connection对象
+     * @var Connection
+     */
+    protected $connection;
+
+    /**
+     * Builder对象
+     * @var Builder
+     */
+    protected $builder;
+
+    /**
+     * 创建一个查询SQL获取对象
+     *
+     * @param  Query    $query      查询对象
+     */
+    public function __construct(Query $query)
+    {
+        $this->query      = $query;
+        $this->connection = $query->getConnection();
+        $this->builder    = $this->connection->getBuilder();
+    }
+
+    /**
+     * 聚合查询
+     * @access protected
+     * @param  string $aggregate    聚合方法
+     * @param  string $field        字段名
+     * @return string
+     */
+    protected function aggregate(string $aggregate, string $field): string
+    {
+        $this->query->parseOptions();
+
+        $field = $aggregate . '(' . $this->builder->parseKey($this->query, $field) . ') AS think_' . strtolower($aggregate);
+
+        return $this->value($field, 0, false);
+    }
+
+    /**
+     * 得到某个字段的值
+     * @access public
+     * @param string $field 字段名
+     * @param mixed $default 默认值
+     * @param bool $one
+     * @return string
+     */
+    public function value(string $field, $default = null, bool $one = true): string
+    {
+        $options = $this->query->parseOptions();
+
+        if (isset($options['field'])) {
+            $this->query->removeOption('field');
+        }
+
+        $this->query->setOption('field', (array) $field);
+
+        // 生成查询SQL
+        $sql = $this->builder->select($this->query, $one);
+
+        if (isset($options['field'])) {
+            $this->query->setOption('field', $options['field']);
+        } else {
+            $this->query->removeOption('field');
+        }
+
+        return $this->fetch($sql);
+    }
+
+    /**
+     * 得到某个列的数组
+     * @access public
+     * @param  string $field 字段名 多个字段用逗号分隔
+     * @param  string $key   索引
+     * @return string
+     */
+    public function column(string $field, string $key = ''): string
+    {
+        $options = $this->query->parseOptions();
+
+        if (isset($options['field'])) {
+            $this->query->removeOption('field');
+        }
+
+        if ($key && '*' != $field) {
+            $field = $key . ',' . $field;
+        }
+
+        $field = array_map('trim', explode(',', $field));
+
+        $this->query->setOption('field', $field);
+
+        // 生成查询SQL
+        $sql = $this->builder->select($this->query);
+
+        if (isset($options['field'])) {
+            $this->query->setOption('field', $options['field']);
+        } else {
+            $this->query->removeOption('field');
+        }
+
+        return $this->fetch($sql);
+    }
+
+    /**
+     * 插入记录
+     * @access public
+     * @param  array $data 数据
+     * @return string
+     */
+    public function insert(array $data = []): string
+    {
+        $options = $this->query->parseOptions();
+
+        if (!empty($data)) {
+            $this->query->setOption('data', $data);
+        }
+
+        $sql = $this->builder->insert($this->query);
+
+        return $this->fetch($sql);
+    }
+
+    /**
+     * 插入记录并获取自增ID
+     * @access public
+     * @param  array $data 数据
+     * @return string
+     */
+    public function insertGetId(array $data = []): string
+    {
+        return $this->insert($data);
+    }
+
+    /**
+     * 保存数据 自动判断insert或者update
+     * @access public
+     * @param  array $data        数据
+     * @param  bool  $forceInsert 是否强制insert
+     * @return string
+     */
+    public function save(array $data = [], bool $forceInsert = false): string
+    {
+        if ($forceInsert) {
+            return $this->insert($data);
+        }
+
+        $data = array_merge($this->query->getOptions('data') ?: [], $data);
+
+        $this->query->setOption('data', $data);
+
+        if ($this->query->getOptions('where')) {
+            $isUpdate = true;
+        } else {
+            $isUpdate = $this->query->parseUpdateData($data);
+        }
+
+        return $isUpdate ? $this->update() : $this->insert();
+    }
+
+    /**
+     * 批量插入记录
+     * @access public
+     * @param  array     $dataSet 数据集
+     * @param  integer   $limit   每次写入数据限制
+     * @return string
+     */
+    public function insertAll(array $dataSet = [], int $limit = null): string
+    {
+        $options = $this->query->parseOptions();
+
+        if (empty($dataSet)) {
+            $dataSet = $options['data'];
+        }
+
+        if (empty($limit) && !empty($options['limit'])) {
+            $limit = $options['limit'];
+        }
+
+        if ($limit) {
+            $array    = array_chunk($dataSet, $limit, true);
+            $fetchSql = [];
+            foreach ($array as $item) {
+                $sql  = $this->builder->insertAll($this->query, $item);
+                $bind = $this->query->getBind();
+
+                $fetchSql[] = $this->connection->getRealSql($sql, $bind);
+            }
+
+            return implode(';', $fetchSql);
+        }
+
+        $sql = $this->builder->insertAll($this->query, $dataSet);
+
+        return $this->fetch($sql);
+    }
+
+    /**
+     * 通过Select方式插入记录
+     * @access public
+     * @param  array    $fields 要插入的数据表字段名
+     * @param  string   $table  要插入的数据表名
+     * @return string
+     */
+    public function selectInsert(array $fields, string $table): string
+    {
+        $this->query->parseOptions();
+
+        $sql = $this->builder->selectInsert($this->query, $fields, $table);
+
+        return $this->fetch($sql);
+    }
+
+    /**
+     * 更新记录
+     * @access public
+     * @param  mixed $data 数据
+     * @return string
+     */
+    public function update(array $data = []): string
+    {
+        $options = $this->query->parseOptions();
+
+        $data = !empty($data) ? $data : $options['data'];
+
+        $pk = $this->query->getPk();
+
+        if (empty($options['where'])) {
+            // 如果存在主键数据 则自动作为更新条件
+            if (is_string($pk) && isset($data[$pk])) {
+                $this->query->where($pk, '=', $data[$pk]);
+                unset($data[$pk]);
+            } elseif (is_array($pk)) {
+                // 增加复合主键支持
+                foreach ($pk as $field) {
+                    if (isset($data[$field])) {
+                        $this->query->where($field, '=', $data[$field]);
+                    } else {
+                        // 如果缺少复合主键数据则不执行
+                        throw new Exception('miss complex primary data');
+                    }
+                    unset($data[$field]);
+                }
+            }
+
+            if (empty($this->query->getOptions('where'))) {
+                // 如果没有任何更新条件则不执行
+                throw new Exception('miss update condition');
+            }
+        }
+
+        // 更新数据
+        $this->query->setOption('data', $data);
+
+        // 生成UPDATE SQL语句
+        $sql = $this->builder->update($this->query);
+
+        return $this->fetch($sql);
+    }
+
+    /**
+     * 删除记录
+     * @access public
+     * @param  mixed $data 表达式 true 表示强制删除
+     * @return string
+     */
+    public function delete($data = null): string
+    {
+        $options = $this->query->parseOptions();
+
+        if (!is_null($data) && true !== $data) {
+            // AR模式分析主键条件
+            $this->query->parsePkWhere($data);
+        }
+
+        if (!empty($options['soft_delete'])) {
+            // 软删除
+            [$field, $condition] = $options['soft_delete'];
+            if ($condition) {
+                $this->query->setOption('soft_delete', null);
+                $this->query->setOption('data', [$field => $condition]);
+                // 生成删除SQL语句
+                $sql = $this->builder->delete($this->query);
+                return $this->fetch($sql);
+            }
+        }
+
+        // 生成删除SQL语句
+        $sql = $this->builder->delete($this->query);
+
+        return $this->fetch($sql);
+    }
+
+    /**
+     * 查找记录 返回SQL
+     * @access public
+     * @param  mixed $data
+     * @return string
+     */
+    public function select($data = null): string
+    {
+        $this->query->parseOptions();
+
+        if (!is_null($data)) {
+            // 主键条件分析
+            $this->query->parsePkWhere($data);
+        }
+
+        // 生成查询SQL
+        $sql = $this->builder->select($this->query);
+
+        return $this->fetch($sql);
+    }
+
+    /**
+     * 查找单条记录 返回SQL语句
+     * @access public
+     * @param  mixed $data
+     * @return string
+     */
+    public function find($data = null): string
+    {
+        $this->query->parseOptions();
+
+        if (!is_null($data)) {
+            // AR模式分析主键条件
+            $this->query->parsePkWhere($data);
+        }
+
+        // 生成查询SQL
+        $sql = $this->builder->select($this->query, true);
+
+        // 获取实际执行的SQL语句
+        return $this->fetch($sql);
+    }
+
+    /**
+     * 查找多条记录 如果不存在则抛出异常
+     * @access public
+     * @param  mixed $data
+     * @return string
+     */
+    public function selectOrFail($data = null): string
+    {
+        return $this->select($data);
+    }
+
+    /**
+     * 查找单条记录 如果不存在则抛出异常
+     * @access public
+     * @param  mixed $data
+     * @return string
+     */
+    public function findOrFail($data = null): string
+    {
+        return $this->find($data);
+    }
+
+    /**
+     * 查找单条记录 不存在返回空数据(或者空模型)
+     * @access public
+     * @param  mixed $data 数据
+     * @return string
+     */
+    public function findOrEmpty($data = null)
+    {
+        return $this->find($data);
+    }
+
+    /**
+     * 获取实际的SQL语句
+     * @access public
+     * @param  string $sql
+     * @return string
+     */
+    public function fetch(string $sql): string
+    {
+        $bind = $this->query->getBind();
+
+        return $this->connection->getRealSql($sql, $bind);
+    }
+
+    /**
+     * COUNT查询
+     * @access public
+     * @param  string $field 字段名
+     * @return string
+     */
+    public function count(string $field = '*'): string
+    {
+        $options = $this->query->parseOptions();
+
+        if (!empty($options['group'])) {
+            // 支持GROUP
+            $bind   = $this->query->getBind();
+            $subSql = $this->query->options($options)->field('count(' . $field . ') AS think_count')->bind($bind)->buildSql();
+
+            $query = $this->query->newQuery()->table([$subSql => '_group_count_']);
+
+            return $query->fetchsql()->aggregate('COUNT', '*');
+        } else {
+            return $this->aggregate('COUNT', $field);
+        }
+    }
+
+    /**
+     * SUM查询
+     * @access public
+     * @param  string $field 字段名
+     * @return string
+     */
+    public function sum(string $field): string
+    {
+        return $this->aggregate('SUM', $field);
+    }
+
+    /**
+     * MIN查询
+     * @access public
+     * @param  string $field    字段名
+     * @return string
+     */
+    public function min(string $field): string
+    {
+        return $this->aggregate('MIN', $field);
+    }
+
+    /**
+     * MAX查询
+     * @access public
+     * @param  string $field    字段名
+     * @return string
+     */
+    public function max(string $field): string
+    {
+        return $this->aggregate('MAX', $field);
+    }
+
+    /**
+     * AVG查询
+     * @access public
+     * @param  string $field 字段名
+     * @return string
+     */
+    public function avg(string $field): string
+    {
+        return $this->aggregate('AVG', $field);
+    }
+
+    public function __call($method, $args)
+    {
+        if (strtolower(substr($method, 0, 5)) == 'getby') {
+            // 根据某个字段获取记录
+            $field = Str::snake(substr($method, 5));
+            return $this->where($field, '=', $args[0])->find();
+        } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') {
+            // 根据某个字段获取记录的某个值
+            $name = Str::snake(substr($method, 10));
+            return $this->where($name, '=', $args[0])->value($args[1]);
+        }
+
+        $result = call_user_func_array([$this->query, $method], $args);
+        return $result === $this->query ? $this : $result;
+    }
+}
diff --git a/vendor/topthink/think-orm/src/db/Mongo.php b/vendor/topthink/think-orm/src/db/Mongo.php
new file mode 100755
index 0000000..5e8a09a
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/Mongo.php
@@ -0,0 +1,712 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+namespace think\db;
+
+use MongoDB\Driver\Command;
+use MongoDB\Driver\Cursor;
+use MongoDB\Driver\Exception\AuthenticationException;
+use MongoDB\Driver\Exception\ConnectionException;
+use MongoDB\Driver\Exception\InvalidArgumentException;
+use MongoDB\Driver\Exception\RuntimeException;
+use MongoDB\Driver\ReadPreference;
+use MongoDB\Driver\WriteConcern;
+use think\db\exception\DbException as Exception;
+use think\Paginator;
+
+class Mongo extends BaseQuery
+{
+    /**
+     * 当前数据库连接对象
+     * @var \think\db\connector\Mongo
+     */
+    protected $connection;
+
+    /**
+     * 执行指令 返回数据集
+     * @access public
+     * @param  Command        $command 指令
+     * @param  string         $dbName
+     * @param  ReadPreference $readPreference readPreference
+     * @param  string|array   $typeMap 指定返回的typeMap
+     * @return mixed
+     * @throws AuthenticationException
+     * @throws InvalidArgumentException
+     * @throws ConnectionException
+     * @throws RuntimeException
+     */
+    public function command(Command $command, string $dbName = '', ReadPreference $readPreference = null, $typeMap = null)
+    {
+        return $this->connection->command($command, $dbName, $readPreference, $typeMap);
+    }
+
+    /**
+     * 执行command
+     * @access public
+     * @param  string|array|object $command 指令
+     * @param  mixed               $extra 额外参数
+     * @param  string              $db 数据库名
+     * @return array
+     */
+    public function cmd($command, $extra = null, string $db = ''): array
+    {
+        $this->parseOptions();
+        return $this->connection->cmd($this, $command, $extra, $db);
+    }
+
+    /**
+     * 指定distinct查询
+     * @access public
+     * @param  string $field 字段名
+     * @return array
+     */
+    public function getDistinct(string $field)
+    {
+        $result = $this->cmd('distinct', $field);
+        return $result[0]['values'];
+    }
+
+    /**
+     * 获取数据库的所有collection
+     * @access public
+     * @param  string  $db 数据库名称 留空为当前数据库
+     * @throws Exception
+     */
+    public function listCollections(string $db = '')
+    {
+        $cursor = $this->cmd('listCollections', null, $db);
+        $result = [];
+        foreach ($cursor as $collection) {
+            $result[] = $collection['name'];
+        }
+
+        return $result;
+    }
+
+    /**
+     * COUNT查询
+     * @access public
+     * @param  string $field 字段名
+     * @return integer
+     */
+    public function count(string $field = null): int
+    {
+        $result = $this->cmd('count');
+
+        return $result[0]['n'];
+    }
+
+    /**
+     * 聚合查询
+     * @access public
+     * @param  string $aggregate 聚合指令
+     * @param  string $field     字段名
+     * @param  bool   $force   强制转为数字类型
+     * @return mixed
+     */
+    public function aggregate(string $aggregate, $field, bool $force = false)
+    {
+        $result = $this->cmd('aggregate', [strtolower($aggregate), $field]);
+        $value  = $result[0]['aggregate'] ?? 0;
+
+        if ($force) {
+            $value += 0;
+        }
+
+        return $value;
+    }
+
+    /**
+     * 多聚合操作
+     *
+     * @param  array $aggregate 聚合指令, 可以聚合多个参数, 如 ['sum' => 'field1', 'avg' => 'field2']
+     * @param  array $groupBy 类似mysql里面的group字段, 可以传入多个字段, 如 ['field_a', 'field_b', 'field_c']
+     * @return array 查询结果
+     */
+    public function multiAggregate(array $aggregate, array $groupBy): array
+    {
+        $result = $this->cmd('multiAggregate', [$aggregate, $groupBy]);
+
+        foreach ($result as &$row) {
+            if (isset($row['_id']) && !empty($row['_id'])) {
+                foreach ($row['_id'] as $k => $v) {
+                    $row[$k] = $v;
+                }
+                unset($row['_id']);
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * 字段值增长
+     * @access public
+     * @param string  $field 字段名
+     * @param float   $step  增长值
+     * @return $this
+     */
+    public function inc(string $field, float $step = 1)
+    {
+        $this->options['data'][$field] = ['$inc', $step];
+
+        return $this;
+    }
+
+    /**
+     * 字段值减少
+     * @access public
+     * @param  string  $field 字段名
+     * @param  float   $step  减少值
+     * @return $this
+     */
+    public function dec(string $field, float $step = 1)
+    {
+        return $this->inc($field, -1 * $step);
+    }
+
+    /**
+     * 指定当前操作的Collection
+     * @access public
+     * @param string $table 表名
+     * @return $this
+     */
+    public function table($table)
+    {
+        $this->options['table'] = $table;
+
+        return $this;
+    }
+
+    /**
+     * table方法的别名
+     * @access public
+     * @param  string $collection
+     * @return $this
+     */
+    public function collection(string $collection)
+    {
+        return $this->table($collection);
+    }
+
+    /**
+     * 设置typeMap
+     * @access public
+     * @param string|array $typeMap
+     * @return $this
+     */
+    public function typeMap($typeMap)
+    {
+        $this->options['typeMap'] = $typeMap;
+        return $this;
+    }
+
+    /**
+     * awaitData
+     * @access public
+     * @param bool $awaitData
+     * @return $this
+     */
+    public function awaitData(bool $awaitData)
+    {
+        $this->options['awaitData'] = $awaitData;
+        return $this;
+    }
+
+    /**
+     * batchSize
+     * @access public
+     * @param integer $batchSize
+     * @return $this
+     */
+    public function batchSize(int $batchSize)
+    {
+        $this->options['batchSize'] = $batchSize;
+        return $this;
+    }
+
+    /**
+     * exhaust
+     * @access public
+     * @param bool $exhaust
+     * @return $this
+     */
+    public function exhaust(bool $exhaust)
+    {
+        $this->options['exhaust'] = $exhaust;
+        return $this;
+    }
+
+    /**
+     * 设置modifiers
+     * @access public
+     * @param array $modifiers
+     * @return $this
+     */
+    public function modifiers(array $modifiers)
+    {
+        $this->options['modifiers'] = $modifiers;
+        return $this;
+    }
+
+    /**
+     * 设置noCursorTimeout
+     * @access public
+     * @param bool $noCursorTimeout
+     * @return $this
+     */
+    public function noCursorTimeout(bool $noCursorTimeout)
+    {
+        $this->options['noCursorTimeout'] = $noCursorTimeout;
+        return $this;
+    }
+
+    /**
+     * 设置oplogReplay
+     * @access public
+     * @param bool $oplogReplay
+     * @return $this
+     */
+    public function oplogReplay(bool $oplogReplay)
+    {
+        $this->options['oplogReplay'] = $oplogReplay;
+        return $this;
+    }
+
+    /**
+     * 设置partial
+     * @access public
+     * @param bool $partial
+     * @return $this
+     */
+    public function partial(bool $partial)
+    {
+        $this->options['partial'] = $partial;
+        return $this;
+    }
+
+    /**
+     * maxTimeMS
+     * @access public
+     * @param string $maxTimeMS
+     * @return $this
+     */
+    public function maxTimeMS(string $maxTimeMS)
+    {
+        $this->options['maxTimeMS'] = $maxTimeMS;
+        return $this;
+    }
+
+    /**
+     * collation
+     * @access public
+     * @param array $collation
+     * @return $this
+     */
+    public function collation(array $collation)
+    {
+        $this->options['collation'] = $collation;
+        return $this;
+    }
+
+    /**
+     * 设置是否REPLACE
+     * @access public
+     * @param bool $replace 是否使用REPLACE写入数据
+     * @return $this
+     */
+    public function replace(bool $replace = true)
+    {
+        return $this;
+    }
+
+    /**
+     * 设置返回字段
+     * @access public
+     * @param  mixed $field 字段信息
+     * @return $this
+     */
+    public function field($field)
+    {
+        if (empty($field) || '*' == $field) {
+            return $this;
+        }
+
+        if (is_string($field)) {
+            $field = array_map('trim', explode(',', $field));
+        }
+
+        $projection = [];
+        foreach ($field as $key => $val) {
+            if (is_numeric($key)) {
+                $projection[$val] = 1;
+            } else {
+                $projection[$key] = $val;
+            }
+        }
+
+        $this->options['projection'] = $projection;
+
+        return $this;
+    }
+
+    /**
+     * 指定要排除的查询字段
+     * @access public
+     * @param array|string $field 要排除的字段
+     * @return $this
+     */
+    public function withoutField($field)
+    {
+        if (empty($field) || '*' == $field) {
+            return $this;
+        }
+
+        if (is_string($field)) {
+            $field = array_map('trim', explode(',', $field));
+        }
+
+        $projection = [];
+        foreach ($field as $key => $val) {
+            if (is_numeric($key)) {
+                $projection[$val] = 0;
+            } else {
+                $projection[$key] = $val;
+            }
+        }
+
+        $this->options['projection'] = $projection;
+        return $this;
+    }
+
+    /**
+     * 设置skip
+     * @access public
+     * @param integer $skip
+     * @return $this
+     */
+    public function skip(int $skip)
+    {
+        $this->options['skip'] = $skip;
+        return $this;
+    }
+
+    /**
+     * 设置slaveOk
+     * @access public
+     * @param bool $slaveOk
+     * @return $this
+     */
+    public function slaveOk(bool $slaveOk)
+    {
+        $this->options['slaveOk'] = $slaveOk;
+        return $this;
+    }
+
+    /**
+     * 指定查询数量
+     * @access public
+     * @param int $offset 起始位置
+     * @param int $length 查询数量
+     * @return $this
+     */
+    public function limit(int $offset, int $length = null)
+    {
+        if (is_null($length)) {
+            $length = $offset;
+            $offset = 0;
+        }
+
+        $this->options['skip']  = $offset;
+        $this->options['limit'] = $length;
+
+        return $this;
+    }
+
+    /**
+     * 设置sort
+     * @access public
+     * @param  array|string $field
+     * @param  string       $order
+     * @return $this
+     */
+    public function order($field, string $order = '')
+    {
+        if (is_array($field)) {
+            $this->options['sort'] = $field;
+        } else {
+            $this->options['sort'][$field] = 'asc' == strtolower($order) ? 1 : -1;
+        }
+        return $this;
+    }
+
+    /**
+     * 设置tailable
+     * @access public
+     * @param  bool $tailable
+     * @return $this
+     */
+    public function tailable(bool $tailable)
+    {
+        $this->options['tailable'] = $tailable;
+        return $this;
+    }
+
+    /**
+     * 设置writeConcern对象
+     * @access public
+     * @param  WriteConcern $writeConcern
+     * @return $this
+     */
+    public function writeConcern(WriteConcern $writeConcern)
+    {
+        $this->options['writeConcern'] = $writeConcern;
+        return $this;
+    }
+
+    /**
+     * 获取当前数据表的主键
+     * @access public
+     * @return string|array
+     */
+    public function getPk()
+    {
+        return $this->pk ?: $this->connection->getConfig('pk');
+    }
+
+    /**
+     * 执行查询但只返回Cursor对象
+     * @access public
+     * @return Cursor
+     */
+    public function getCursor(): Cursor
+    {
+        $this->parseOptions();
+
+        return $this->connection->getCursor($this);
+    }
+
+    /**
+     * 获取当前的查询标识
+     * @access public
+     * @param mixed $data 要序列化的数据
+     * @return string
+     */
+    public function getQueryGuid($data = null): string
+    {
+        return md5($this->getConfig('database') . serialize(var_export($data ?: $this->options, true)));
+    }
+
+    /**
+     * 分页查询
+     * @access public
+     * @param int|array $listRows 每页数量 数组表示配置参数
+     * @param int|bool  $simple   是否简洁模式或者总记录数
+     * @return Paginator
+     * @throws Exception
+     */
+    public function paginate($listRows = null, $simple = false): Paginator
+    {
+        if (is_int($simple)) {
+            $total  = $simple;
+            $simple = false;
+        }
+
+        $defaultConfig = [
+            'query'     => [], //url额外参数
+            'fragment'  => '', //url锚点
+            'var_page'  => 'page', //分页变量
+            'list_rows' => 15, //每页数量
+        ];
+
+        if (is_array($listRows)) {
+            $config   = array_merge($defaultConfig, $listRows);
+            $listRows = intval($config['list_rows']);
+        } else {
+            $config   = $defaultConfig;
+            $listRows = intval($listRows ?: $config['list_rows']);
+        }
+
+        $page = isset($config['page']) ? (int) $config['page'] : Paginator::getCurrentPage($config['var_page']);
+
+        $page = $page < 1 ? 1 : $page;
+
+        $config['path'] = $config['path'] ?? Paginator::getCurrentPath();
+
+        if (!isset($total) && !$simple) {
+            $options = $this->getOptions();
+
+            unset($this->options['order'], $this->options['limit'], $this->options['page'], $this->options['field']);
+
+            $total   = $this->count();
+            $results = $this->options($options)->page($page, $listRows)->select();
+        } elseif ($simple) {
+            $results = $this->limit(($page - 1) * $listRows, $listRows + 1)->select();
+            $total   = null;
+        } else {
+            $results = $this->page($page, $listRows)->select();
+        }
+
+        $this->removeOption('limit');
+        $this->removeOption('page');
+
+        return Paginator::make($results, $listRows, $page, $total, $simple, $config);
+    }
+
+    /**
+     * 分批数据返回处理
+     * @access public
+     * @param integer      $count    每次处理的数据数量
+     * @param callable     $callback 处理回调方法
+     * @param string|array $column   分批处理的字段名
+     * @param string       $order    字段排序
+     * @return bool
+     * @throws Exception
+     */
+    public function chunk(int $count, callable $callback, $column = null, string $order = 'asc'): bool
+    {
+        $options = $this->getOptions();
+        $column  = $column ?: $this->getPk();
+
+        if (isset($options['order'])) {
+            unset($options['order']);
+        }
+
+        if (is_array($column)) {
+            $times = 1;
+            $query = $this->options($options)->page($times, $count);
+        } else {
+            $query = $this->options($options)->limit($count);
+
+            if (strpos($column, '.')) {
+                [$alias, $key] = explode('.', $column);
+            } else {
+                $key = $column;
+            }
+        }
+
+        $resultSet = $query->order($column, $order)->select();
+
+        while (count($resultSet) > 0) {
+            if (false === call_user_func($callback, $resultSet)) {
+                return false;
+            }
+
+            if (isset($times)) {
+                $times++;
+                $query = $this->options($options)->page($times, $count);
+            } else {
+                $end    = $resultSet->pop();
+                $lastId = is_array($end) ? $end[$key] : $end->getData($key);
+
+                $query = $this->options($options)
+                    ->limit($count)
+                    ->where($column, 'asc' == strtolower($order) ? '>' : '<', $lastId);
+            }
+
+            $resultSet = $query->order($column, $order)->select();
+        }
+
+        return true;
+    }
+
+    /**
+     * 分析表达式(可用于查询或者写入操作)
+     * @access public
+     * @return array
+     */
+    public function parseOptions(): array
+    {
+        $options = $this->options;
+
+        // 获取数据表
+        if (empty($options['table'])) {
+            $options['table'] = $this->getTable();
+        }
+
+        foreach (['where', 'data'] as $name) {
+            if (!isset($options[$name])) {
+                $options[$name] = [];
+            }
+        }
+
+        $modifiers = empty($options['modifiers']) ? [] : $options['modifiers'];
+        if (isset($options['comment'])) {
+            $modifiers['$comment'] = $options['comment'];
+        }
+
+        if (isset($options['maxTimeMS'])) {
+            $modifiers['$maxTimeMS'] = $options['maxTimeMS'];
+        }
+
+        if (!empty($modifiers)) {
+            $options['modifiers'] = $modifiers;
+        }
+
+        if (!isset($options['projection'])) {
+            $options['projection'] = [];
+        }
+
+        if (!isset($options['typeMap'])) {
+            $options['typeMap'] = $this->getConfig('type_map');
+        }
+
+        if (!isset($options['limit'])) {
+            $options['limit'] = 0;
+        }
+
+        foreach (['master', 'fetch_sql', 'fetch_cursor'] as $name) {
+            if (!isset($options[$name])) {
+                $options[$name] = false;
+            }
+        }
+
+        if (isset($options['page'])) {
+            // 根据页数计算limit
+            [$page, $listRows] = $options['page'];
+
+            $page             = $page > 0 ? $page : 1;
+            $listRows         = $listRows > 0 ? $listRows : (is_numeric($options['limit']) ? $options['limit'] : 20);
+            $offset           = $listRows * ($page - 1);
+            $options['skip']  = intval($offset);
+            $options['limit'] = intval($listRows);
+        }
+
+        $this->options = $options;
+
+        return $options;
+    }
+
+    /**
+     * 获取字段类型信息
+     * @access public
+     * @return array
+     */
+    public function getFieldsType(): array
+    {
+        if (!empty($this->options['field_type'])) {
+            return $this->options['field_type'];
+        }
+
+        return [];
+    }
+
+    /**
+     * 获取字段类型信息
+     * @access public
+     * @param string $field 字段名
+     * @return string|null
+     */
+    public function getFieldType(string $field)
+    {
+        $fieldType = $this->getFieldsType();
+
+        return $fieldType[$field] ?? null;
+    }
+}
diff --git a/vendor/topthink/think-orm/src/db/PDOConnection.php b/vendor/topthink/think-orm/src/db/PDOConnection.php
new file mode 100755
index 0000000..540f135
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/PDOConnection.php
@@ -0,0 +1,1746 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db;
+
+use Closure;
+use PDO;
+use PDOStatement;
+use think\db\exception\BindParamException;
+use think\db\exception\DbException;
+use think\db\exception\PDOException;
+
+/**
+ * 数据库连接基础类
+ */
+abstract class PDOConnection extends Connection
+{
+    const PARAM_FLOAT = 21;
+
+    /**
+     * 数据库连接参数配置
+     * @var array
+     */
+    protected $config = [
+        // 数据库类型
+        'type'            => '',
+        // 服务器地址
+        'hostname'        => '',
+        // 数据库名
+        'database'        => '',
+        // 用户名
+        'username'        => '',
+        // 密码
+        'password'        => '',
+        // 端口
+        'hostport'        => '',
+        // 连接dsn
+        'dsn'             => '',
+        // 数据库连接参数
+        'params'          => [],
+        // 数据库编码默认采用utf8
+        'charset'         => 'utf8',
+        // 数据库表前缀
+        'prefix'          => '',
+        // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
+        'deploy'          => 0,
+        // 数据库读写是否分离 主从式有效
+        'rw_separate'     => false,
+        // 读写分离后 主服务器数量
+        'master_num'      => 1,
+        // 指定从服务器序号
+        'slave_no'        => '',
+        // 模型写入后自动读取主服务器
+        'read_master'     => false,
+        // 是否严格检查字段是否存在
+        'fields_strict'   => true,
+        // 开启字段缓存
+        'fields_cache'    => false,
+        // 监听SQL
+        'trigger_sql'     => true,
+        // Builder类
+        'builder'         => '',
+        // Query类
+        'query'           => '',
+        // 是否需要断线重连
+        'break_reconnect' => false,
+        // 断线标识字符串
+        'break_match_str' => [],
+    ];
+
+    /**
+     * PDO操作实例
+     * @var PDOStatement
+     */
+    protected $PDOStatement;
+
+    /**
+     * 当前SQL指令
+     * @var string
+     */
+    protected $queryStr = '';
+
+    /**
+     * 事务指令数
+     * @var int
+     */
+    protected $transTimes = 0;
+
+    /**
+     * 重连次数
+     * @var int
+     */
+    protected $reConnectTimes = 0;
+
+    /**
+     * 查询结果类型
+     * @var int
+     */
+    protected $fetchType = PDO::FETCH_ASSOC;
+
+    /**
+     * 字段属性大小写
+     * @var int
+     */
+    protected $attrCase = PDO::CASE_LOWER;
+
+    /**
+     * 数据表信息
+     * @var array
+     */
+    protected $info = [];
+
+    /**
+     * 查询开始时间
+     * @var float
+     */
+    protected $queryStartTime;
+
+    /**
+     * PDO连接参数
+     * @var array
+     */
+    protected $params = [
+        PDO::ATTR_CASE              => PDO::CASE_NATURAL,
+        PDO::ATTR_ERRMODE           => PDO::ERRMODE_EXCEPTION,
+        PDO::ATTR_ORACLE_NULLS      => PDO::NULL_NATURAL,
+        PDO::ATTR_STRINGIFY_FETCHES => false,
+        PDO::ATTR_EMULATE_PREPARES  => false,
+    ];
+
+    /**
+     * 参数绑定类型映射
+     * @var array
+     */
+    protected $bindType = [
+        'string'    => PDO::PARAM_STR,
+        'str'       => PDO::PARAM_STR,
+        'integer'   => PDO::PARAM_INT,
+        'int'       => PDO::PARAM_INT,
+        'boolean'   => PDO::PARAM_BOOL,
+        'bool'      => PDO::PARAM_BOOL,
+        'float'     => self::PARAM_FLOAT,
+        'datetime'  => PDO::PARAM_STR,
+        'timestamp' => PDO::PARAM_STR,
+    ];
+
+    /**
+     * 服务器断线标识字符
+     * @var array
+     */
+    protected $breakMatchStr = [
+        'server has gone away',
+        'no connection to the server',
+        'Lost connection',
+        'is dead or not enabled',
+        'Error while sending',
+        'decryption failed or bad record mac',
+        'server closed the connection unexpectedly',
+        'SSL connection has been closed unexpectedly',
+        'Error writing data to the connection',
+        'Resource deadlock avoided',
+        'failed with errno',
+    ];
+
+    /**
+     * 绑定参数
+     * @var array
+     */
+    protected $bind = [];
+
+    /**
+     * 获取当前连接器类对应的Query类
+     * @access public
+     * @return string
+     */
+    public function getQueryClass(): string
+    {
+        return $this->getConfig('query') ?: Query::class;
+    }
+
+    /**
+     * 获取当前连接器类对应的Builder类
+     * @access public
+     * @return string
+     */
+    public function getBuilderClass(): string
+    {
+        return $this->getConfig('builder') ?: '\\think\\db\\builder\\' . ucfirst($this->getConfig('type'));
+    }
+
+    /**
+     * 解析pdo连接的dsn信息
+     * @access protected
+     * @param array $config 连接信息
+     * @return string
+     */
+    abstract protected function parseDsn(array $config);
+
+    /**
+     * 取得数据表的字段信息
+     * @access public
+     * @param string $tableName 数据表名称
+     * @return array
+     */
+    abstract public function getFields(string $tableName);
+
+    /**
+     * 取得数据库的表信息
+     * @access public
+     * @param string $dbName 数据库名称
+     * @return array
+     */
+    abstract public function getTables(string $dbName);
+
+    /**
+     * 对返数据表字段信息进行大小写转换出来
+     * @access public
+     * @param array $info 字段信息
+     * @return array
+     */
+    public function fieldCase(array $info): array
+    {
+        // 字段大小写转换
+        switch ($this->attrCase) {
+            case PDO::CASE_LOWER:
+                $info = array_change_key_case($info);
+                break;
+            case PDO::CASE_UPPER:
+                $info = array_change_key_case($info, CASE_UPPER);
+                break;
+            case PDO::CASE_NATURAL:
+            default:
+                // 不做转换
+        }
+
+        return $info;
+    }
+
+    /**
+     * 获取字段类型
+     * @access protected
+     * @param string $type 字段类型
+     * @return string
+     */
+    protected function getFieldType(string $type): string
+    {
+        if (0 === strpos($type, 'set') || 0 === strpos($type, 'enum')) {
+            $result = 'string';
+        } elseif (preg_match('/(double|float|decimal|real|numeric)/is', $type)) {
+            $result = 'float';
+        } elseif (preg_match('/(int|serial|bit)/is', $type)) {
+            $result = 'int';
+        } elseif (preg_match('/bool/is', $type)) {
+            $result = 'bool';
+        } elseif (0 === strpos($type, 'timestamp')) {
+            $result = 'timestamp';
+        } elseif (0 === strpos($type, 'datetime')) {
+            $result = 'datetime';
+        } elseif (0 === strpos($type, 'date')) {
+            $result = 'date';
+        } else {
+            $result = 'string';
+        }
+
+        return $result;
+    }
+
+    /**
+     * 获取字段绑定类型
+     * @access public
+     * @param string $type 字段类型
+     * @return integer
+     */
+    public function getFieldBindType(string $type): int
+    {
+        if (in_array($type, ['integer', 'string', 'float', 'boolean', 'bool', 'int', 'str'])) {
+            $bind = $this->bindType[$type];
+        } elseif (0 === strpos($type, 'set') || 0 === strpos($type, 'enum')) {
+            $bind = PDO::PARAM_STR;
+        } elseif (preg_match('/(double|float|decimal|real|numeric)/is', $type)) {
+            $bind = self::PARAM_FLOAT;
+        } elseif (preg_match('/(int|serial|bit)/is', $type)) {
+            $bind = PDO::PARAM_INT;
+        } elseif (preg_match('/bool/is', $type)) {
+            $bind = PDO::PARAM_BOOL;
+        } else {
+            $bind = PDO::PARAM_STR;
+        }
+
+        return $bind;
+    }
+
+    /**
+     * 获取数据表信息缓存key
+     * @access protected
+     * @param string $schema 数据表名称
+     * @return string
+     */
+    protected function getSchemaCacheKey(string $schema): string
+    {
+        return $this->getConfig('hostname') . ':' . $this->getConfig('hostport') . '@' . $schema;
+    }
+
+    /**
+     * @param string $tableName 数据表名称
+     * @param bool $force 强制从数据库获取
+     * @return array
+     */
+    public function getSchemaInfo(string $tableName, $force = false)
+    {
+        if (!strpos($tableName, '.')) {
+            $schema = $this->getConfig('database') . '.' . $tableName;
+        } else {
+            $schema = $tableName;
+        }
+
+        if (!isset($this->info[$schema]) || $force) {
+            // 读取字段缓存
+            $cacheKey   = $this->getSchemaCacheKey($schema);
+            $cacheField = $this->config['fields_cache'] && !empty($this->cache);
+
+            if ($cacheField && !$force) {
+                $info = $this->cache->get($cacheKey);
+            }
+
+            if (empty($info)) {
+                $info = $this->getTableFieldsInfo($tableName);
+                if ($cacheField) {
+                    $this->cache->set($cacheKey, $info);
+                }
+            }
+
+            $pk      = $info['_pk'] ?? null;
+            $autoinc = $info['_autoinc'] ?? null;
+            unset($info['_pk'], $info['_autoinc']);
+
+            $bind = [];
+            foreach ($info as $name => $val) {
+                $bind[$name] = $this->getFieldBindType($val);
+            }
+
+            $this->info[$schema] = [
+                'fields'  => array_keys($info),
+                'type'    => $info,
+                'bind'    => $bind,
+                'pk'      => $pk,
+                'autoinc' => $autoinc,
+            ];
+        }
+
+        return $this->info[$schema];
+    }
+
+    /**
+     * 获取数据表信息
+     * @access public
+     * @param mixed  $tableName 数据表名 留空自动获取
+     * @param string $fetch     获取信息类型 包括 fields type bind pk
+     * @return mixed
+     */
+    public function getTableInfo($tableName, string $fetch = '')
+    {
+        if (is_array($tableName)) {
+            $tableName = key($tableName) ?: current($tableName);
+        }
+
+        if (strpos($tableName, ',') || strpos($tableName, ')')) {
+            // 多表不获取字段信息
+            return [];
+        }
+
+        [$tableName] = explode(' ', $tableName);
+
+        $info = $this->getSchemaInfo($tableName);
+
+        return $fetch ? $info[$fetch] : $info;
+    }
+
+    /**
+     * 获取数据表的字段信息
+     * @access public
+     * @param string $tableName 数据表名
+     * @return array
+     */
+    public function getTableFieldsInfo(string $tableName): array
+    {
+        $fields = $this->getFields($tableName);
+        $info   = [];
+
+        foreach ($fields as $key => $val) {
+            // 记录字段类型
+            $info[$key] = $this->getFieldType($val['type']);
+
+            if (!empty($val['primary'])) {
+                $pk[] = $key;
+            }
+
+            if (!empty($val['autoinc'])) {
+                $autoinc = $key;
+            }
+        }
+
+        if (isset($pk)) {
+            // 设置主键
+            $pk          = count($pk) > 1 ? $pk : $pk[0];
+            $info['_pk'] = $pk;
+        }
+
+        if (isset($autoinc)) {
+            $info['_autoinc'] = $autoinc;
+        }
+
+        return $info;
+    }
+
+    /**
+     * 获取数据表的主键
+     * @access public
+     * @param mixed $tableName 数据表名
+     * @return string|array
+     */
+    public function getPk($tableName)
+    {
+        return $this->getTableInfo($tableName, 'pk');
+    }
+
+    /**
+     * 获取数据表的自增主键
+     * @access public
+     * @param mixed $tableName 数据表名
+     * @return string
+     */
+    public function getAutoInc($tableName)
+    {
+        return $this->getTableInfo($tableName, 'autoinc');
+    }
+
+    /**
+     * 获取数据表字段信息
+     * @access public
+     * @param mixed $tableName 数据表名
+     * @return array
+     */
+    public function getTableFields($tableName): array
+    {
+        return $this->getTableInfo($tableName, 'fields');
+    }
+
+    /**
+     * 获取数据表字段类型
+     * @access public
+     * @param mixed  $tableName 数据表名
+     * @param string $field     字段名
+     * @return array|string
+     */
+    public function getFieldsType($tableName, string $field = null)
+    {
+        $result = $this->getTableInfo($tableName, 'type');
+
+        if ($field && isset($result[$field])) {
+            return $result[$field];
+        }
+
+        return $result;
+    }
+
+    /**
+     * 获取数据表绑定信息
+     * @access public
+     * @param mixed $tableName 数据表名
+     * @return array
+     */
+    public function getFieldsBind($tableName): array
+    {
+        return $this->getTableInfo($tableName, 'bind');
+    }
+
+    /**
+     * 连接数据库方法
+     * @access public
+     * @param array      $config         连接参数
+     * @param integer    $linkNum        连接序号
+     * @param array|bool $autoConnection 是否自动连接主数据库(用于分布式)
+     * @return PDO
+     * @throws PDOException
+     */
+    public function connect(array $config = [], $linkNum = 0, $autoConnection = false): PDO
+    {
+        if (isset($this->links[$linkNum])) {
+            return $this->links[$linkNum];
+        }
+
+        if (empty($config)) {
+            $config = $this->config;
+        } else {
+            $config = array_merge($this->config, $config);
+        }
+
+        // 连接参数
+        if (isset($config['params']) && is_array($config['params'])) {
+            $params = $config['params'] + $this->params;
+        } else {
+            $params = $this->params;
+        }
+
+        // 记录当前字段属性大小写设置
+        $this->attrCase = $params[PDO::ATTR_CASE];
+
+        if (!empty($config['break_match_str'])) {
+            $this->breakMatchStr = array_merge($this->breakMatchStr, (array) $config['break_match_str']);
+        }
+
+        try {
+            if (empty($config['dsn'])) {
+                $config['dsn'] = $this->parseDsn($config);
+            }
+
+            $startTime = microtime(true);
+
+            $this->links[$linkNum] = $this->createPdo($config['dsn'], $config['username'], $config['password'], $params);
+
+            // SQL监控
+            if (!empty($config['trigger_sql'])) {
+                $this->trigger('CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn']);
+            }
+
+            return $this->links[$linkNum];
+        } catch (\PDOException $e) {
+            if ($autoConnection) {
+                $this->db->log($e->getMessage(), 'error');
+                return $this->connect($autoConnection, $linkNum);
+            } else {
+                throw $e;
+            }
+        }
+    }
+
+    /**
+     * 视图查询
+     * @access public
+     * @param array $args
+     * @return BaseQuery
+     */
+    public function view(...$args)
+    {
+        return $this->newQuery()->view(...$args);
+    }
+
+    /**
+     * 创建PDO实例
+     * @param $dsn
+     * @param $username
+     * @param $password
+     * @param $params
+     * @return PDO
+     */
+    protected function createPdo($dsn, $username, $password, $params)
+    {
+        return new PDO($dsn, $username, $password, $params);
+    }
+
+    /**
+     * 释放查询结果
+     * @access public
+     */
+    public function free(): void
+    {
+        $this->PDOStatement = null;
+    }
+
+    /**
+     * 获取PDO对象
+     * @access public
+     * @return \PDO|false
+     */
+    public function getPdo()
+    {
+        if (!$this->linkID) {
+            return false;
+        }
+
+        return $this->linkID;
+    }
+
+    /**
+     * 执行查询 使用生成器返回数据
+     * @access public
+     * @param BaseQuery    $query     查询对象
+     * @param string       $sql       sql指令
+     * @param array        $bind      参数绑定
+     * @param \think\Model $model     模型对象实例
+     * @param array        $condition 查询条件
+     * @return \Generator
+     */
+    public function getCursor(BaseQuery $query, string $sql, array $bind = [], $model = null, $condition = null)
+    {
+        $this->queryPDOStatement($query, $sql, $bind);
+
+        // 返回结果集
+        while ($result = $this->PDOStatement->fetch($this->fetchType)) {
+            if ($model) {
+                yield $model->newInstance($result, $condition);
+            } else {
+                yield $result;
+            }
+        }
+    }
+
+    /**
+     * 执行查询 返回数据集
+     * @access public
+     * @param string $sql  sql指令
+     * @param array  $bind 参数绑定
+     * @param bool   $master 主库读取
+     * @return array
+     * @throws BindParamException
+     * @throws \PDOException
+     */
+    public function query(string $sql, array $bind = [], bool $master = false): array
+    {
+        return $this->pdoQuery($this->newQuery(), $sql, $bind, $master);
+    }
+
+    /**
+     * 执行语句
+     * @access public
+     * @param string $sql  sql指令
+     * @param array  $bind 参数绑定
+     * @return int
+     * @throws BindParamException
+     * @throws \PDOException
+     */
+    public function execute(string $sql, array $bind = []): int
+    {
+        return $this->pdoExecute($this->newQuery(), $sql, $bind, true);
+    }
+
+    /**
+     * 执行查询 返回数据集
+     * @access protected
+     * @param BaseQuery $query 查询对象
+     * @param mixed     $sql   sql指令
+     * @param array     $bind  参数绑定
+     * @param bool      $master 主库读取
+     * @return array
+     * @throws BindParamException
+     * @throws \PDOException
+     * @throws \Exception
+     * @throws \Throwable
+     */
+    protected function pdoQuery(BaseQuery $query, $sql, array $bind = [], bool $master = null): array
+    {
+        // 分析查询表达式
+        $query->parseOptions();
+
+        if ($query->getOptions('cache')) {
+            // 检查查询缓存
+            $cacheItem = $this->parseCache($query, $query->getOptions('cache'));
+            $key       = $cacheItem->getKey();
+
+            $data = $this->cache->get($key);
+
+            if (null !== $data) {
+                return $data;
+            }
+        }
+
+        if ($sql instanceof Closure) {
+            $sql  = $sql($query);
+            $bind = $query->getBind();
+        }
+
+        if (!isset($master)) {
+            $master = $query->getOptions('master') ? true : false;
+        }
+
+        $procedure = $query->getOptions('procedure') ? true : in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']);
+
+        $this->getPDOStatement($sql, $bind, $master, $procedure);
+
+        $resultSet = $this->getResult($procedure);
+
+        if (isset($cacheItem) && $resultSet) {
+            // 缓存数据集
+            $cacheItem->set($resultSet);
+            $this->cacheData($cacheItem);
+        }
+
+        return $resultSet;
+    }
+
+    /**
+     * 执行查询但只返回PDOStatement对象
+     * @access public
+     * @param BaseQuery $query 查询对象
+     * @return \PDOStatement
+     */
+    public function pdo(BaseQuery $query): PDOStatement
+    {
+        $bind = $query->getBind();
+        // 生成查询SQL
+        $sql = $this->builder->select($query);
+
+        return $this->queryPDOStatement($query, $sql, $bind);
+    }
+
+    /**
+     * 执行查询但只返回PDOStatement对象
+     * @access public
+     * @param string $sql       sql指令
+     * @param array  $bind      参数绑定
+     * @param bool   $master    是否在主服务器读操作
+     * @param bool   $procedure 是否为存储过程调用
+     * @return PDOStatement
+     * @throws BindParamException
+     * @throws \PDOException
+     * @throws \Exception
+     * @throws \Throwable
+     */
+    public function getPDOStatement(string $sql, array $bind = [], bool $master = false, bool $procedure = false): PDOStatement
+    {
+        $this->initConnect($this->readMaster ?: $master);
+
+        // 记录SQL语句
+        $this->queryStr = $sql;
+
+        $this->bind = $bind;
+
+        $this->db->updateQueryTimes();
+
+        try {
+            $this->queryStartTime = microtime(true);
+
+            // 预处理
+            $this->PDOStatement = $this->linkID->prepare($sql);
+
+            // 参数绑定
+            if ($procedure) {
+                $this->bindParam($bind);
+            } else {
+                $this->bindValue($bind);
+            }
+
+            // 执行查询
+            $this->PDOStatement->execute();
+
+            // SQL监控
+            if (!empty($this->config['trigger_sql'])) {
+                $this->trigger('', $master);
+            }
+
+            $this->reConnectTimes = 0;
+
+            return $this->PDOStatement;
+        } catch (\Throwable | \Exception $e) {
+            if ($this->reConnectTimes < 4 && $this->isBreak($e)) {
+                ++$this->reConnectTimes;
+                return $this->close()->getPDOStatement($sql, $bind, $master, $procedure);
+            }
+
+            if ($e instanceof \PDOException) {
+                throw new PDOException($e, $this->config, $this->getLastsql());
+            } else {
+                throw $e;
+            }
+        }
+    }
+
+    /**
+     * 执行语句
+     * @access protected
+     * @param BaseQuery $query  查询对象
+     * @param string    $sql    sql指令
+     * @param array     $bind   参数绑定
+     * @param bool      $origin 是否原生查询
+     * @return int
+     * @throws BindParamException
+     * @throws \PDOException
+     * @throws \Exception
+     * @throws \Throwable
+     */
+    protected function pdoExecute(BaseQuery $query, string $sql, array $bind = [], bool $origin = false): int
+    {
+        if ($origin) {
+            $query->parseOptions();
+        }
+
+        $this->queryPDOStatement($query->master(true), $sql, $bind);
+
+        if (!$origin && !empty($this->config['deploy']) && !empty($this->config['read_master'])) {
+            $this->readMaster = true;
+        }
+
+        $this->numRows = $this->PDOStatement->rowCount();
+
+        if ($query->getOptions('cache')) {
+            // 清理缓存数据
+            $cacheItem = $this->parseCache($query, $query->getOptions('cache'));
+            $key       = $cacheItem->getKey();
+            $tag       = $cacheItem->getTag();
+
+            if (isset($key) && $this->cache->has($key)) {
+                $this->cache->delete($key);
+            } elseif (!empty($tag) && method_exists($this->cache, 'tag')) {
+                $this->cache->tag($tag)->clear();
+            }
+        }
+
+        return $this->numRows;
+    }
+
+    protected function queryPDOStatement(BaseQuery $query, string $sql, array $bind = []): PDOStatement
+    {
+        $options   = $query->getOptions();
+        $master    = !empty($options['master']) ? true : false;
+        $procedure = !empty($options['procedure']) ? true : in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']);
+
+        return $this->getPDOStatement($sql, $bind, $master, $procedure);
+    }
+
+    /**
+     * 查找单条记录
+     * @access public
+     * @param BaseQuery $query 查询对象
+     * @return array
+     * @throws DbException
+     */
+    public function find(BaseQuery $query): array
+    {
+        // 事件回调
+        $result = $this->db->trigger('before_find', $query);
+
+        if (!$result) {
+            // 执行查询
+            $resultSet = $this->pdoQuery($query, function ($query) {
+                return $this->builder->select($query, true);
+            });
+
+            $result = $resultSet[0] ?? [];
+        }
+
+        return $result;
+    }
+
+    /**
+     * 使用游标查询记录
+     * @access public
+     * @param BaseQuery $query 查询对象
+     * @return \Generator
+     */
+    public function cursor(BaseQuery $query)
+    {
+        // 分析查询表达式
+        $options = $query->parseOptions();
+
+        // 生成查询SQL
+        $sql = $this->builder->select($query);
+
+        $condition = $options['where']['AND'] ?? null;
+
+        // 执行查询操作
+        return $this->getCursor($query, $sql, $query->getBind(), $query->getModel(), $condition);
+    }
+
+    /**
+     * 查找记录
+     * @access public
+     * @param BaseQuery $query 查询对象
+     * @return array
+     * @throws DbException
+     */
+    public function select(BaseQuery $query): array
+    {
+        $resultSet = $this->db->trigger('before_select', $query);
+
+        if (!$resultSet) {
+            // 执行查询操作
+            $resultSet = $this->pdoQuery($query, function ($query) {
+                return $this->builder->select($query);
+            });
+        }
+
+        return $resultSet;
+    }
+
+    /**
+     * 插入记录
+     * @access public
+     * @param BaseQuery $query        查询对象
+     * @param boolean   $getLastInsID 返回自增主键
+     * @return mixed
+     */
+    public function insert(BaseQuery $query, bool $getLastInsID = false)
+    {
+        // 分析查询表达式
+        $options = $query->parseOptions();
+
+        // 生成SQL语句
+        $sql = $this->builder->insert($query);
+
+        // 执行操作
+        $result = '' == $sql ? 0 : $this->pdoExecute($query, $sql, $query->getBind());
+
+        if ($result) {
+            $sequence  = $options['sequence'] ?? null;
+            $lastInsId = $this->getLastInsID($query, $sequence);
+
+            $data = $options['data'];
+
+            if ($lastInsId) {
+                $pk = $query->getAutoInc();
+                if ($pk) {
+                    $data[$pk] = $lastInsId;
+                }
+            }
+
+            $query->setOption('data', $data);
+
+            $this->db->trigger('after_insert', $query);
+
+            if ($getLastInsID && $lastInsId) {
+                return $lastInsId;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * 批量插入记录
+     * @access public
+     * @param BaseQuery $query   查询对象
+     * @param mixed     $dataSet 数据集
+     * @param integer   $limit   每次写入数据限制
+     * @return integer
+     * @throws \Exception
+     * @throws \Throwable
+     */
+    public function insertAll(BaseQuery $query, array $dataSet = [], int $limit = 0): int
+    {
+        if (!is_array(reset($dataSet))) {
+            return 0;
+        }
+
+        $options = $query->parseOptions();
+        $replace = !empty($options['replace']);
+
+        if (0 === $limit && count($dataSet) >= 5000) {
+            $limit = 1000;
+        }
+
+        if ($limit) {
+            // 分批写入 自动启动事务支持
+            $this->startTrans();
+
+            try {
+                $array = array_chunk($dataSet, $limit, true);
+                $count = 0;
+
+                foreach ($array as $item) {
+                    $sql = $this->builder->insertAll($query, $item, $replace);
+                    $count += $this->pdoExecute($query, $sql, $query->getBind());
+                }
+
+                // 提交事务
+                $this->commit();
+            } catch (\Exception | \Throwable $e) {
+                $this->rollback();
+                throw $e;
+            }
+
+            return $count;
+        }
+
+        $sql = $this->builder->insertAll($query, $dataSet, $replace);
+
+        return $this->pdoExecute($query, $sql, $query->getBind());
+    }
+
+    /**
+     * 通过Select方式插入记录
+     * @access public
+     * @param BaseQuery $query  查询对象
+     * @param array     $fields 要插入的数据表字段名
+     * @param string    $table  要插入的数据表名
+     * @return integer
+     * @throws PDOException
+     */
+    public function selectInsert(BaseQuery $query, array $fields, string $table): int
+    {
+        // 分析查询表达式
+        $query->parseOptions();
+
+        $sql = $this->builder->selectInsert($query, $fields, $table);
+
+        return $this->pdoExecute($query, $sql, $query->getBind());
+    }
+
+    /**
+     * 更新记录
+     * @access public
+     * @param BaseQuery $query 查询对象
+     * @return integer
+     * @throws PDOException
+     */
+    public function update(BaseQuery $query): int
+    {
+        $query->parseOptions();
+
+        // 生成UPDATE SQL语句
+        $sql = $this->builder->update($query);
+
+        // 执行操作
+        $result = '' == $sql ? 0 : $this->pdoExecute($query, $sql, $query->getBind());
+
+        if ($result) {
+            $this->db->trigger('after_update', $query);
+        }
+
+        return $result;
+    }
+
+    /**
+     * 删除记录
+     * @access public
+     * @param BaseQuery $query 查询对象
+     * @return int
+     * @throws PDOException
+     */
+    public function delete(BaseQuery $query): int
+    {
+        // 分析查询表达式
+        $query->parseOptions();
+
+        // 生成删除SQL语句
+        $sql = $this->builder->delete($query);
+
+        // 执行操作
+        $result = $this->pdoExecute($query, $sql, $query->getBind());
+
+        if ($result) {
+            $this->db->trigger('after_delete', $query);
+        }
+
+        return $result;
+    }
+
+    /**
+     * 得到某个字段的值
+     * @access public
+     * @param BaseQuery $query   查询对象
+     * @param string    $field   字段名
+     * @param mixed     $default 默认值
+     * @param bool      $one     返回一个值
+     * @return mixed
+     */
+    public function value(BaseQuery $query, string $field, $default = null, bool $one = true)
+    {
+        $options = $query->parseOptions();
+
+        if (isset($options['field'])) {
+            $query->removeOption('field');
+        }
+
+        if (isset($options['group'])) {
+            $query->group('');
+        }
+
+        $query->setOption('field', (array) $field);
+
+        if (!empty($options['cache'])) {
+            $cacheItem = $this->parseCache($query, $options['cache'], 'value');
+            $key       = $cacheItem->getKey();
+
+            if ($this->cache->has($key)) {
+                return $this->cache->get($key);
+            }
+        }
+
+        // 生成查询SQL
+        $sql = $this->builder->select($query, $one);
+
+        if (isset($options['field'])) {
+            $query->setOption('field', $options['field']);
+        } else {
+            $query->removeOption('field');
+        }
+
+        if (isset($options['group'])) {
+            $query->setOption('group', $options['group']);
+        }
+
+        // 执行查询操作
+        $pdo = $this->getPDOStatement($sql, $query->getBind(), $options['master']);
+
+        $result = $pdo->fetchColumn();
+
+        if (isset($cacheItem)) {
+            // 缓存数据
+            $cacheItem->set($result);
+            $this->cacheData($cacheItem);
+        }
+
+        return false !== $result ? $result : $default;
+    }
+
+    /**
+     * 得到某个字段的值
+     * @access public
+     * @param BaseQuery $query     查询对象
+     * @param string    $aggregate 聚合方法
+     * @param mixed     $field     字段名
+     * @param bool      $force     强制转为数字类型
+     * @return mixed
+     */
+    public function aggregate(BaseQuery $query, string $aggregate, $field, bool $force = false)
+    {
+        if (is_string($field) && 0 === stripos($field, 'DISTINCT ')) {
+            [$distinct, $field] = explode(' ', $field);
+        }
+
+        $field = $aggregate . '(' . (!empty($distinct) ? 'DISTINCT ' : '') . $this->builder->parseKey($query, $field, true) . ') AS think_' . strtolower($aggregate);
+
+        $result = $this->value($query, $field, 0, false);
+
+        return $force ? (float) $result : $result;
+    }
+
+    /**
+     * 得到某个列的数组
+     * @access public
+     * @param BaseQuery $query  查询对象
+     * @param string    $column 字段名 多个字段用逗号分隔
+     * @param string    $key    索引
+     * @return array
+     */
+    public function column(BaseQuery $query, string $column, string $key = ''): array
+    {
+        $options = $query->parseOptions();
+
+        if (isset($options['field'])) {
+            $query->removeOption('field');
+        }
+
+        if ($key && '*' != $column) {
+            $field = $key . ',' . $column;
+        } else {
+            $field = $column;
+        }
+
+        $field = array_map('trim', explode(',', $field));
+
+        $query->setOption('field', $field);
+
+        if (!empty($options['cache'])) {
+            // 判断查询缓存
+            $cacheItem = $this->parseCache($query, $options['cache'], 'column');
+            $name      = $cacheItem->getKey();
+
+            if ($this->cache->has($name)) {
+                return $this->cache->get($name);
+            }
+        }
+
+        // 生成查询SQL
+        $sql = $this->builder->select($query);
+
+        if (isset($options['field'])) {
+            $query->setOption('field', $options['field']);
+        } else {
+            $query->removeOption('field');
+        }
+
+        // 执行查询操作
+        $pdo = $this->getPDOStatement($sql, $query->getBind(), $options['master']);
+
+        $resultSet = $pdo->fetchAll(PDO::FETCH_ASSOC);
+
+        if (empty($resultSet)) {
+            $result = [];
+        } elseif (('*' == $column || strpos($column, ',')) && $key) {
+            $result = array_column($resultSet, null, $key);
+        } else {
+            if (empty($key)) {
+                $key = null;
+            }
+
+            if (strpos($column, ',')) {
+                $column = null;
+            } elseif (strpos($column, ' ')) {
+                $column = substr(strrchr(trim($column), ' '), 1);
+            } elseif (strpos($column, '.')) {
+                [$alias, $column] = explode('.', $column);
+            }
+
+            if (is_string($key) && strpos($key, '.')) {
+                [$alias, $key] = explode('.', $key);
+            }
+
+            $result = array_column($resultSet, $column, $key);
+        }
+
+        if (isset($cacheItem)) {
+            // 缓存数据
+            $cacheItem->set($result);
+            $this->cacheData($cacheItem);
+        }
+
+        return $result;
+    }
+
+    /**
+     * 根据参数绑定组装最终的SQL语句 便于调试
+     * @access public
+     * @param string $sql  带参数绑定的sql语句
+     * @param array  $bind 参数绑定列表
+     * @return string
+     */
+    public function getRealSql(string $sql, array $bind = []): string
+    {
+        foreach ($bind as $key => $val) {
+            $value = is_array($val) ? $val[0] : $val;
+            $type  = is_array($val) ? $val[1] : PDO::PARAM_STR;
+
+            if ((self::PARAM_FLOAT == $type || PDO::PARAM_STR == $type) && is_string($value)) {
+                $value = '\'' . addslashes($value) . '\'';
+            } elseif (PDO::PARAM_INT == $type && '' === $value) {
+                $value = 0;
+            }
+
+            // 判断占位符
+            $sql = is_numeric($key) ?
+            substr_replace($sql, $value, strpos($sql, '?'), 1) :
+            substr_replace($sql, $value, strpos($sql, ':' . $key), strlen(':' . $key));
+        }
+
+        return rtrim($sql);
+    }
+
+    /**
+     * 参数绑定
+     * 支持 ['name'=>'value','id'=>123] 对应命名占位符
+     * 或者 ['value',123] 对应问号占位符
+     * @access public
+     * @param array $bind 要绑定的参数列表
+     * @return void
+     * @throws BindParamException
+     */
+    protected function bindValue(array $bind = []): void
+    {
+        foreach ($bind as $key => $val) {
+            // 占位符
+            $param = is_numeric($key) ? $key + 1 : ':' . $key;
+
+            if (is_array($val)) {
+                if (PDO::PARAM_INT == $val[1] && '' === $val[0]) {
+                    $val[0] = 0;
+                } elseif (self::PARAM_FLOAT == $val[1]) {
+                    $val[0] = is_string($val[0]) ? (float) $val[0] : $val[0];
+                    $val[1] = PDO::PARAM_STR;
+                }
+
+                $result = $this->PDOStatement->bindValue($param, $val[0], $val[1]);
+            } else {
+                $result = $this->PDOStatement->bindValue($param, $val);
+            }
+
+            if (!$result) {
+                throw new BindParamException(
+                    "Error occurred  when binding parameters '{$param}'",
+                    $this->config,
+                    $this->getLastsql(),
+                    $bind
+                );
+            }
+        }
+    }
+
+    /**
+     * 存储过程的输入输出参数绑定
+     * @access public
+     * @param array $bind 要绑定的参数列表
+     * @return void
+     * @throws BindParamException
+     */
+    protected function bindParam(array $bind): void
+    {
+        foreach ($bind as $key => $val) {
+            $param = is_numeric($key) ? $key + 1 : ':' . $key;
+
+            if (is_array($val)) {
+                array_unshift($val, $param);
+                $result = call_user_func_array([$this->PDOStatement, 'bindParam'], $val);
+            } else {
+                $result = $this->PDOStatement->bindValue($param, $val);
+            }
+
+            if (!$result) {
+                $param = array_shift($val);
+
+                throw new BindParamException(
+                    "Error occurred  when binding parameters '{$param}'",
+                    $this->config,
+                    $this->getLastsql(),
+                    $bind
+                );
+            }
+        }
+    }
+
+    /**
+     * 获得数据集数组
+     * @access protected
+     * @param bool $procedure 是否存储过程
+     * @return array
+     */
+    protected function getResult(bool $procedure = false): array
+    {
+        if ($procedure) {
+            // 存储过程返回结果
+            return $this->procedure();
+        }
+
+        $result = $this->PDOStatement->fetchAll($this->fetchType);
+
+        $this->numRows = count($result);
+
+        return $result;
+    }
+
+    /**
+     * 获得存储过程数据集
+     * @access protected
+     * @return array
+     */
+    protected function procedure(): array
+    {
+        $item = [];
+
+        do {
+            $result = $this->getResult();
+            if (!empty($result)) {
+                $item[] = $result;
+            }
+        } while ($this->PDOStatement->nextRowset());
+
+        $this->numRows = count($item);
+
+        return $item;
+    }
+
+    /**
+     * 执行数据库事务
+     * @access public
+     * @param  callable $callback 数据操作方法回调
+     * @return mixed
+     * @throws PDOException
+     * @throws \Exception
+     * @throws \Throwable
+     */
+    public function transaction(callable $callback)
+    {
+        $this->startTrans();
+
+        try {
+            $result = null;
+            if (is_callable($callback)) {
+                $result = $callback($this);
+            }
+
+            $this->commit();
+            return $result;
+        } catch (\Exception | \Throwable $e) {
+            $this->rollback();
+            throw $e;
+        }
+    }
+
+    /**
+     * 启动事务
+     * @access public
+     * @return void
+     * @throws \PDOException
+     * @throws \Exception
+     */
+    public function startTrans(): void
+    {
+        $this->initConnect(true);
+
+        ++$this->transTimes;
+
+        try {
+            if (1 == $this->transTimes) {
+                $this->linkID->beginTransaction();
+            } elseif ($this->transTimes > 1 && $this->supportSavepoint()) {
+                $this->linkID->exec(
+                    $this->parseSavepoint('trans' . $this->transTimes)
+                );
+            }
+            $this->reConnectTimes = 0;
+        } catch (\Exception $e) {
+            if ($this->reConnectTimes < 4 && $this->isBreak($e)) {
+                --$this->transTimes;
+                ++$this->reConnectTimes;
+                $this->close()->startTrans();
+            }
+            throw $e;
+        }
+    }
+
+    /**
+     * 用于非自动提交状态下面的查询提交
+     * @access public
+     * @return void
+     * @throws PDOException
+     */
+    public function commit(): void
+    {
+        $this->initConnect(true);
+
+        if (1 == $this->transTimes) {
+            $this->linkID->commit();
+        }
+
+        --$this->transTimes;
+    }
+
+    /**
+     * 事务回滚
+     * @access public
+     * @return void
+     * @throws PDOException
+     */
+    public function rollback(): void
+    {
+        $this->initConnect(true);
+
+        if (1 == $this->transTimes) {
+            $this->linkID->rollBack();
+        } elseif ($this->transTimes > 1 && $this->supportSavepoint()) {
+            $this->linkID->exec(
+                $this->parseSavepointRollBack('trans' . $this->transTimes)
+            );
+        }
+
+        $this->transTimes = max(0, $this->transTimes - 1);
+    }
+
+    /**
+     * 是否支持事务嵌套
+     * @return bool
+     */
+    protected function supportSavepoint(): bool
+    {
+        return false;
+    }
+
+    /**
+     * 生成定义保存点的SQL
+     * @access protected
+     * @param string $name 标识
+     * @return string
+     */
+    protected function parseSavepoint(string $name): string
+    {
+        return 'SAVEPOINT ' . $name;
+    }
+
+    /**
+     * 生成回滚到保存点的SQL
+     * @access protected
+     * @param string $name 标识
+     * @return string
+     */
+    protected function parseSavepointRollBack(string $name): string
+    {
+        return 'ROLLBACK TO SAVEPOINT ' . $name;
+    }
+
+    /**
+     * 批处理执行SQL语句
+     * 批处理的指令都认为是execute操作
+     * @access public
+     * @param BaseQuery $query    查询对象
+     * @param array     $sqlArray SQL批处理指令
+     * @param array     $bind     参数绑定
+     * @return bool
+     */
+    public function batchQuery(BaseQuery $query, array $sqlArray = [], array $bind = []): bool
+    {
+        // 自动启动事务支持
+        $this->startTrans();
+
+        try {
+            foreach ($sqlArray as $sql) {
+                $this->pdoExecute($query, $sql, $bind);
+            }
+            // 提交事务
+            $this->commit();
+        } catch (\Exception $e) {
+            $this->rollback();
+            throw $e;
+        }
+
+        return true;
+    }
+
+    /**
+     * 关闭数据库(或者重新连接)
+     * @access public
+     * @return $this
+     */
+    public function close()
+    {
+        $this->linkID    = null;
+        $this->linkWrite = null;
+        $this->linkRead  = null;
+        $this->links     = [];
+
+        $this->free();
+
+        return $this;
+    }
+
+    /**
+     * 是否断线
+     * @access protected
+     * @param \PDOException|\Exception $e 异常对象
+     * @return bool
+     */
+    protected function isBreak($e): bool
+    {
+        if (!$this->config['break_reconnect']) {
+            return false;
+        }
+
+        $error = $e->getMessage();
+
+        foreach ($this->breakMatchStr as $msg) {
+            if (false !== stripos($error, $msg)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * 获取最近一次查询的sql语句
+     * @access public
+     * @return string
+     */
+    public function getLastSql(): string
+    {
+        return $this->getRealSql($this->queryStr, $this->bind);
+    }
+
+    /**
+     * 获取最近插入的ID
+     * @access public
+     * @param BaseQuery $query    查询对象
+     * @param string    $sequence 自增序列名
+     * @return mixed
+     */
+    public function getLastInsID(BaseQuery $query, string $sequence = null)
+    {
+        try {
+            $insertId = $this->linkID->lastInsertId($sequence);
+        } catch (\Exception $e) {
+            $insertId = '';
+        }
+
+        return $this->autoInsIDType($query, $insertId);
+    }
+
+    /**
+     * 获取最近插入的ID
+     * @access public
+     * @param BaseQuery $query    查询对象
+     * @param string    $insertId 自增ID
+     * @return mixed
+     */
+    protected function autoInsIDType(BaseQuery $query, string $insertId)
+    {
+        $pk = $query->getAutoInc();
+
+        if ($pk) {
+            $type = $this->getFieldBindType($pk);
+
+            if (PDO::PARAM_INT == $type) {
+                $insertId = (int) $insertId;
+            } elseif (self::PARAM_FLOAT == $type) {
+                $insertId = (float) $insertId;
+            }
+        }
+
+        return $insertId;
+    }
+
+    /**
+     * 获取最近的错误信息
+     * @access public
+     * @return string
+     */
+    public function getError(): string
+    {
+        if ($this->PDOStatement) {
+            $error = $this->PDOStatement->errorInfo();
+            $error = $error[1] . ':' . $error[2];
+        } else {
+            $error = '';
+        }
+
+        if ('' != $this->queryStr) {
+            $error .= "\n [ SQL语句 ] : " . $this->getLastsql();
+        }
+
+        return $error;
+    }
+
+    /**
+     * 初始化数据库连接
+     * @access protected
+     * @param boolean $master 是否主服务器
+     * @return void
+     */
+    protected function initConnect(bool $master = true): void
+    {
+        if (!empty($this->config['deploy'])) {
+            // 采用分布式数据库
+            if ($master || $this->transTimes) {
+                if (!$this->linkWrite) {
+                    $this->linkWrite = $this->multiConnect(true);
+                }
+
+                $this->linkID = $this->linkWrite;
+            } else {
+                if (!$this->linkRead) {
+                    $this->linkRead = $this->multiConnect(false);
+                }
+
+                $this->linkID = $this->linkRead;
+            }
+        } elseif (!$this->linkID) {
+            // 默认单数据库
+            $this->linkID = $this->connect();
+        }
+    }
+
+    /**
+     * 连接分布式服务器
+     * @access protected
+     * @param boolean $master 主服务器
+     * @return PDO
+     */
+    protected function multiConnect(bool $master = false): PDO
+    {
+        $config = [];
+
+        // 分布式数据库配置解析
+        foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) {
+            $config[$name] = is_string($this->config[$name]) ? explode(',', $this->config[$name]) : $this->config[$name];
+        }
+
+        // 主服务器序号
+        $m = floor(mt_rand(0, $this->config['master_num'] - 1));
+
+        if ($this->config['rw_separate']) {
+            // 主从式采用读写分离
+            if ($master) // 主服务器写入
+            {
+                $r = $m;
+            } elseif (is_numeric($this->config['slave_no'])) {
+                // 指定服务器读
+                $r = $this->config['slave_no'];
+            } else {
+                // 读操作连接从服务器 每次随机连接的数据库
+                $r = floor(mt_rand($this->config['master_num'], count($config['hostname']) - 1));
+            }
+        } else {
+            // 读写操作不区分服务器 每次随机连接的数据库
+            $r = floor(mt_rand(0, count($config['hostname']) - 1));
+        }
+        $dbMaster = false;
+
+        if ($m != $r) {
+            $dbMaster = [];
+            foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) {
+                $dbMaster[$name] = $config[$name][$m] ?? $config[$name][0];
+            }
+        }
+
+        $dbConfig = [];
+
+        foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) {
+            $dbConfig[$name] = $config[$name][$r] ?? $config[$name][0];
+        }
+
+        return $this->connect($dbConfig, $r, $r == $m ? false : $dbMaster);
+    }
+
+    /**
+     * 启动XA事务
+     * @access public
+     * @param  string $xid XA事务id
+     * @return void
+     */
+    public function startTransXa(string $xid)
+    {}
+
+    /**
+     * 预编译XA事务
+     * @access public
+     * @param  string $xid XA事务id
+     * @return void
+     */
+    public function prepareXa(string $xid)
+    {}
+
+    /**
+     * 提交XA事务
+     * @access public
+     * @param  string $xid XA事务id
+     * @return void
+     */
+    public function commitXa(string $xid)
+    {}
+
+    /**
+     * 回滚XA事务
+     * @access public
+     * @param  string $xid XA事务id
+     * @return void
+     */
+    public function rollbackXa(string $xid)
+    {}
+}
diff --git a/vendor/topthink/think-orm/src/db/Query.php b/vendor/topthink/think-orm/src/db/Query.php
new file mode 100755
index 0000000..aa96123
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/Query.php
@@ -0,0 +1,451 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db;
+
+use PDOStatement;
+use think\helper\Str;
+
+/**
+ * PDO数据查询类
+ */
+class Query extends BaseQuery
+{
+    use concern\JoinAndViewQuery;
+    use concern\ParamsBind;
+    use concern\TableFieldInfo;
+
+    /**
+     * 表达式方式指定Field排序
+     * @access public
+     * @param string $field 排序字段
+     * @param array  $bind  参数绑定
+     * @return $this
+     */
+    public function orderRaw(string $field, array $bind = [])
+    {
+        $this->options['order'][] = new Raw($field, $bind);
+
+        return $this;
+    }
+
+    /**
+     * 表达式方式指定查询字段
+     * @access public
+     * @param string $field 字段名
+     * @return $this
+     */
+    public function fieldRaw(string $field)
+    {
+        $this->options['field'][] = new Raw($field);
+
+        return $this;
+    }
+
+    /**
+     * 指定Field排序 orderField('id',[1,2,3],'desc')
+     * @access public
+     * @param string $field  排序字段
+     * @param array  $values 排序值
+     * @param string $order  排序 desc/asc
+     * @return $this
+     */
+    public function orderField(string $field, array $values, string $order = '')
+    {
+        if (!empty($values)) {
+            $values['sort'] = $order;
+
+            $this->options['order'][$field] = $values;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 随机排序
+     * @access public
+     * @return $this
+     */
+    public function orderRand()
+    {
+        $this->options['order'][] = '[rand]';
+        return $this;
+    }
+
+    /**
+     * 使用表达式设置数据
+     * @access public
+     * @param string $field 字段名
+     * @param string $value 字段值
+     * @return $this
+     */
+    public function exp(string $field, string $value)
+    {
+        $this->options['data'][$field] = new Raw($value);
+        return $this;
+    }
+
+    /**
+     * 表达式方式指定当前操作的数据表
+     * @access public
+     * @param mixed $table 表名
+     * @return $this
+     */
+    public function tableRaw(string $table)
+    {
+        $this->options['table'] = new Raw($table);
+
+        return $this;
+    }
+
+    /**
+     * 获取执行的SQL语句而不进行实际的查询
+     * @access public
+     * @param bool $fetch 是否返回sql
+     * @return $this|Fetch
+     */
+    public function fetchSql(bool $fetch = true)
+    {
+        $this->options['fetch_sql'] = $fetch;
+
+        if ($fetch) {
+            return new Fetch($this);
+        }
+
+        return $this;
+    }
+
+    /**
+     * 批处理执行SQL语句
+     * 批处理的指令都认为是execute操作
+     * @access public
+     * @param array $sql SQL批处理指令
+     * @return bool
+     */
+    public function batchQuery(array $sql = []): bool
+    {
+        return $this->connection->batchQuery($this, $sql);
+    }
+
+    /**
+     * USING支持 用于多表删除
+     * @access public
+     * @param mixed $using USING
+     * @return $this
+     */
+    public function using($using)
+    {
+        $this->options['using'] = $using;
+        return $this;
+    }
+
+    /**
+     * 存储过程调用
+     * @access public
+     * @param bool $procedure 是否为存储过程查询
+     * @return $this
+     */
+    public function procedure(bool $procedure = true)
+    {
+        $this->options['procedure'] = $procedure;
+        return $this;
+    }
+
+    /**
+     * 指定group查询
+     * @access public
+     * @param string|array $group GROUP
+     * @return $this
+     */
+    public function group($group)
+    {
+        $this->options['group'] = $group;
+        return $this;
+    }
+
+    /**
+     * 指定having查询
+     * @access public
+     * @param string $having having
+     * @return $this
+     */
+    public function having(string $having)
+    {
+        $this->options['having'] = $having;
+        return $this;
+    }
+
+    /**
+     * 指定distinct查询
+     * @access public
+     * @param bool $distinct 是否唯一
+     * @return $this
+     */
+    public function distinct(bool $distinct = true)
+    {
+        $this->options['distinct'] = $distinct;
+        return $this;
+    }
+
+    /**
+     * 指定强制索引
+     * @access public
+     * @param string $force 索引名称
+     * @return $this
+     */
+    public function force(string $force)
+    {
+        $this->options['force'] = $force;
+        return $this;
+    }
+
+    /**
+     * 查询注释
+     * @access public
+     * @param string $comment 注释
+     * @return $this
+     */
+    public function comment(string $comment)
+    {
+        $this->options['comment'] = $comment;
+        return $this;
+    }
+
+    /**
+     * 设置是否REPLACE
+     * @access public
+     * @param bool $replace 是否使用REPLACE写入数据
+     * @return $this
+     */
+    public function replace(bool $replace = true)
+    {
+        $this->options['replace'] = $replace;
+        return $this;
+    }
+
+    /**
+     * 设置当前查询所在的分区
+     * @access public
+     * @param string|array $partition 分区名称
+     * @return $this
+     */
+    public function partition($partition)
+    {
+        $this->options['partition'] = $partition;
+        return $this;
+    }
+
+    /**
+     * 设置DUPLICATE
+     * @access public
+     * @param array|string|Raw $duplicate DUPLICATE信息
+     * @return $this
+     */
+    public function duplicate($duplicate)
+    {
+        $this->options['duplicate'] = $duplicate;
+        return $this;
+    }
+
+    /**
+     * 设置查询的额外参数
+     * @access public
+     * @param string $extra 额外信息
+     * @return $this
+     */
+    public function extra(string $extra)
+    {
+        $this->options['extra'] = $extra;
+        return $this;
+    }
+
+    /**
+     * 创建子查询SQL
+     * @access public
+     * @param bool $sub 是否添加括号
+     * @return string
+     * @throws Exception
+     */
+    public function buildSql(bool $sub = true): string
+    {
+        return $sub ? '( ' . $this->fetchSql()->select() . ' )' : $this->fetchSql()->select();
+    }
+
+    /**
+     * 获取当前数据表的主键
+     * @access public
+     * @return string|array
+     */
+    public function getPk()
+    {
+        if (empty($this->pk)) {
+            $this->pk = $this->connection->getPk($this->getTable());
+        }
+
+        return $this->pk;
+    }
+
+    /**
+     * 指定数据表自增主键
+     * @access public
+     * @param string $autoinc 自增键
+     * @return $this
+     */
+    public function autoinc(string $autoinc)
+    {
+        $this->autoinc = $autoinc;
+        return $this;
+    }
+
+    /**
+     * 获取当前数据表的自增主键
+     * @access public
+     * @return string|null
+     */
+    public function getAutoInc()
+    {
+        $tableName = $this->getTable();
+
+        if (empty($this->autoinc) && $tableName) {
+            $this->autoinc = $this->connection->getAutoInc($tableName);
+        }
+
+        return $this->autoinc;
+    }
+
+    /**
+     * 字段值增长
+     * @access public
+     * @param string  $field    字段名
+     * @param float   $step     增长值
+     * @return $this
+     */
+    public function inc(string $field, float $step = 1)
+    {
+        $this->options['data'][$field] = ['INC', $step];
+
+        return $this;
+    }
+
+    /**
+     * 字段值减少
+     * @access public
+     * @param string  $field    字段名
+     * @param float   $step     增长值
+     * @return $this
+     */
+    public function dec(string $field, float $step = 1)
+    {
+        $this->options['data'][$field] = ['DEC', $step];
+        return $this;
+    }
+
+    /**
+     * 获取当前的查询标识
+     * @access public
+     * @param mixed $data 要序列化的数据
+     * @return string
+     */
+    public function getQueryGuid($data = null): string
+    {
+        return md5($this->getConfig('database') . serialize(var_export($data ?: $this->options, true)) . serialize($this->getBind(false)));
+    }
+
+    /**
+     * 执行查询但只返回PDOStatement对象
+     * @access public
+     * @return PDOStatement
+     */
+    public function getPdo(): PDOStatement
+    {
+        return $this->connection->pdo($this);
+    }
+
+    /**
+     * 使用游标查找记录
+     * @access public
+     * @param mixed $data 数据
+     * @return \Generator
+     */
+    public function cursor($data = null)
+    {
+        if (!is_null($data)) {
+            // 主键条件分析
+            $this->parsePkWhere($data);
+        }
+
+        $this->options['data'] = $data;
+
+        $connection = clone $this->connection;
+
+        return $connection->cursor($this);
+    }
+
+    /**
+     * 分批数据返回处理
+     * @access public
+     * @param integer      $count    每次处理的数据数量
+     * @param callable     $callback 处理回调方法
+     * @param string|array $column   分批处理的字段名
+     * @param string       $order    字段排序
+     * @return bool
+     * @throws Exception
+     */
+    public function chunk(int $count, callable $callback, $column = null, string $order = 'asc'): bool
+    {
+        $options = $this->getOptions();
+        $column  = $column ?: $this->getPk();
+
+        if (isset($options['order'])) {
+            unset($options['order']);
+        }
+
+        $bind = $this->bind;
+
+        if (is_array($column)) {
+            $times = 1;
+            $query = $this->options($options)->page($times, $count);
+        } else {
+            $query = $this->options($options)->limit($count);
+
+            if (strpos($column, '.')) {
+                [$alias, $key] = explode('.', $column);
+            } else {
+                $key = $column;
+            }
+        }
+
+        $resultSet = $query->order($column, $order)->select();
+
+        while (count($resultSet) > 0) {
+            if (false === call_user_func($callback, $resultSet)) {
+                return false;
+            }
+
+            if (isset($times)) {
+                $times++;
+                $query = $this->options($options)->page($times, $count);
+            } else {
+                $end    = $resultSet->pop();
+                $lastId = is_array($end) ? $end[$key] : $end->getData($key);
+
+                $query = $this->options($options)
+                    ->limit($count)
+                    ->where($column, 'asc' == strtolower($order) ? '>' : '<', $lastId);
+            }
+
+            $resultSet = $query->bind($bind)->order($column, $order)->select();
+        }
+
+        return true;
+    }
+}
diff --git a/vendor/topthink/think-orm/src/db/Raw.php b/vendor/topthink/think-orm/src/db/Raw.php
new file mode 100755
index 0000000..833fbf0
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/Raw.php
@@ -0,0 +1,71 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db;
+
+/**
+ * SQL Raw
+ */
+class Raw
+{
+    /**
+     * 查询表达式
+     *
+     * @var string
+     */
+    protected $value;
+
+    /**
+     * 参数绑定
+     *
+     * @var array
+     */
+    protected $bind = [];
+
+    /**
+     * 创建一个查询表达式
+     *
+     * @param  string  $value
+     * @param  array   $bind
+     * @return void
+     */
+    public function __construct(string $value, array $bind = [])
+    {
+        $this->value = $value;
+        $this->bind  = $bind;
+    }
+
+    /**
+     * 获取表达式
+     *
+     * @return string
+     */
+    public function getValue(): string
+    {
+        return $this->value;
+    }
+
+    /**
+     * 获取参数绑定
+     *
+     * @return string
+     */
+    public function getBind(): array
+    {
+        return $this->bind;
+    }
+
+    public function __toString()
+    {
+        return (string) $this->value;
+    }
+}
diff --git a/vendor/topthink/think-orm/src/db/Where.php b/vendor/topthink/think-orm/src/db/Where.php
new file mode 100755
index 0000000..0880460
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/Where.php
@@ -0,0 +1,182 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db;
+
+use ArrayAccess;
+
+/**
+ * 数组查询对象
+ */
+class Where implements ArrayAccess
+{
+    /**
+     * 查询表达式
+     * @var array
+     */
+    protected $where = [];
+
+    /**
+     * 是否需要把查询条件两边增加括号
+     * @var bool
+     */
+    protected $enclose = false;
+
+    /**
+     * 创建一个查询表达式
+     *
+     * @param  array    $where      查询条件数组
+     * @param  bool     $enclose    是否增加括号
+     */
+    public function __construct(array $where = [], bool $enclose = false)
+    {
+        $this->where   = $where;
+        $this->enclose = $enclose;
+    }
+
+    /**
+     * 设置是否添加括号
+     * @access public
+     * @param  bool $enclose
+     * @return $this
+     */
+    public function enclose(bool $enclose = true)
+    {
+        $this->enclose = $enclose;
+        return $this;
+    }
+
+    /**
+     * 解析为Query对象可识别的查询条件数组
+     * @access public
+     * @return array
+     */
+    public function parse(): array
+    {
+        $where = [];
+
+        foreach ($this->where as $key => $val) {
+            if ($val instanceof Raw) {
+                $where[] = [$key, 'exp', $val];
+            } elseif (is_null($val)) {
+                $where[] = [$key, 'NULL', ''];
+            } elseif (is_array($val)) {
+                $where[] = $this->parseItem($key, $val);
+            } else {
+                $where[] = [$key, '=', $val];
+            }
+        }
+
+        return $this->enclose ? [$where] : $where;
+    }
+
+    /**
+     * 分析查询表达式
+     * @access protected
+     * @param  string   $field     查询字段
+     * @param  array    $where     查询条件
+     * @return array
+     */
+    protected function parseItem(string $field, array $where = []): array
+    {
+        $op        = $where[0];
+        $condition = $where[1] ?? null;
+
+        if (is_array($op)) {
+            // 同一字段多条件查询
+            array_unshift($where, $field);
+        } elseif (is_null($condition)) {
+            if (is_string($op) && in_array(strtoupper($op), ['NULL', 'NOTNULL', 'NOT NULL'], true)) {
+                // null查询
+                $where = [$field, $op, ''];
+            } elseif (is_null($op) || '=' == $op) {
+                $where = [$field, 'NULL', ''];
+            } elseif ('<>' == $op) {
+                $where = [$field, 'NOTNULL', ''];
+            } else {
+                // 字段相等查询
+                $where = [$field, '=', $op];
+            }
+        } else {
+            $where = [$field, $op, $condition];
+        }
+
+        return $where;
+    }
+
+    /**
+     * 修改器 设置数据对象的值
+     * @access public
+     * @param  string $name  名称
+     * @param  mixed  $value 值
+     * @return void
+     */
+    public function __set($name, $value)
+    {
+        $this->where[$name] = $value;
+    }
+
+    /**
+     * 获取器 获取数据对象的值
+     * @access public
+     * @param  string $name 名称
+     * @return mixed
+     */
+    public function __get($name)
+    {
+        return $this->where[$name] ?? null;
+    }
+
+    /**
+     * 检测数据对象的值
+     * @access public
+     * @param  string $name 名称
+     * @return bool
+     */
+    public function __isset($name)
+    {
+        return isset($this->where[$name]);
+    }
+
+    /**
+     * 销毁数据对象的值
+     * @access public
+     * @param  string $name 名称
+     * @return void
+     */
+    public function __unset($name)
+    {
+        unset($this->where[$name]);
+    }
+
+    // ArrayAccess
+    public function offsetSet($name, $value)
+    {
+        $this->__set($name, $value);
+    }
+
+    public function offsetExists($name)
+    {
+        return $this->__isset($name);
+    }
+
+    public function offsetUnset($name)
+    {
+        $this->__unset($name);
+    }
+
+    public function offsetGet($name)
+    {
+        return $this->__get($name);
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/db/builder/Mongo.php b/vendor/topthink/think-orm/src/db/builder/Mongo.php
new file mode 100755
index 0000000..823156b
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/builder/Mongo.php
@@ -0,0 +1,675 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+namespace think\db\builder;
+
+use MongoDB\BSON\Javascript;
+use MongoDB\BSON\ObjectID;
+use MongoDB\BSON\Regex;
+use MongoDB\Driver\BulkWrite;
+use MongoDB\Driver\Command;
+use MongoDB\Driver\Exception\InvalidArgumentException;
+use MongoDB\Driver\Query as MongoQuery;
+use think\db\connector\Mongo as Connection;
+use think\db\exception\DbException as Exception;
+use think\db\Mongo as Query;
+
+class Mongo
+{
+    // connection对象实例
+    protected $connection;
+    // 最后插入ID
+    protected $insertId = [];
+    // 查询表达式
+    protected $exp = ['<>' => 'ne', '=' => 'eq', '>' => 'gt', '>=' => 'gte', '<' => 'lt', '<=' => 'lte', 'in' => 'in', 'not in' => 'nin', 'nin' => 'nin', 'mod' => 'mod', 'exists' => 'exists', 'null' => 'null', 'notnull' => 'not null', 'not null' => 'not null', 'regex' => 'regex', 'type' => 'type', 'all' => 'all', '> time' => '> time', '< time' => '< time', 'between' => 'between', 'not between' => 'not between', 'between time' => 'between time', 'not between time' => 'not between time', 'notbetween time' => 'not between time', 'like' => 'like', 'near' => 'near', 'size' => 'size'];
+
+    /**
+     * 架构函数
+     * @access public
+     * @param Connection $connection 数据库连接对象实例
+     */
+    public function __construct(Connection $connection)
+    {
+        $this->connection = $connection;
+    }
+
+    /**
+     * 获取当前的连接对象实例
+     * @access public
+     * @return Connection
+     */
+    public function getConnection(): Connection
+    {
+        return $this->connection;
+    }
+
+    /**
+     * key分析
+     * @access protected
+     * @param string $key
+     * @return string
+     */
+    protected function parseKey(Query $query, string $key): string
+    {
+        if (0 === strpos($key, '__TABLE__.')) {
+            [$collection, $key] = explode('.', $key, 2);
+        }
+
+        if ('id' == $key && $this->connection->getConfig('pk_convert_id')) {
+            $key = '_id';
+        }
+
+        return trim($key);
+    }
+
+    /**
+     * value分析
+     * @access protected
+     * @param Query     $query 查询对象
+     * @param mixed     $value
+     * @param string    $field
+     * @return string
+     */
+    protected function parseValue(Query $query, $value, $field = '')
+    {
+        if ('_id' == $field && 'ObjectID' == $this->connection->getConfig('pk_type') && is_string($value)) {
+            try {
+                return new ObjectID($value);
+            } catch (InvalidArgumentException $e) {
+                return new ObjectID();
+            }
+        }
+
+        return $value;
+    }
+
+    /**
+     * insert数据分析
+     * @access protected
+     * @param Query $query 查询对象
+     * @param array $data 数据
+     * @return array
+     */
+    protected function parseData(Query $query, array $data): array
+    {
+        if (empty($data)) {
+            return [];
+        }
+
+        $result = [];
+
+        foreach ($data as $key => $val) {
+            $item = $this->parseKey($query, $key);
+
+            if (is_object($val)) {
+                $result[$item] = $val;
+            } elseif (isset($val[0]) && 'exp' == $val[0]) {
+                $result[$item] = $val[1];
+            } elseif (is_null($val)) {
+                $result[$item] = 'NULL';
+            } else {
+                $result[$item] = $this->parseValue($query, $val, $key);
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Set数据分析
+     * @access protected
+     * @param Query $query 查询对象
+     * @param array $data 数据
+     * @return array
+     */
+    protected function parseSet(Query $query, array $data): array
+    {
+        if (empty($data)) {
+            return [];
+        }
+
+        $result = [];
+
+        foreach ($data as $key => $val) {
+            $item = $this->parseKey($query, $key);
+
+            if (is_array($val) && isset($val[0]) && is_string($val[0]) && 0 === strpos($val[0], '$')) {
+                $result[$val[0]][$item] = $this->parseValue($query, $val[1], $key);
+            } else {
+                $result['$set'][$item] = $this->parseValue($query, $val, $key);
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * 生成查询过滤条件
+     * @access public
+     * @param Query $query 查询对象
+     * @param mixed $where
+     * @return array
+     */
+    public function parseWhere(Query $query, array $where): array
+    {
+        if (empty($where)) {
+            $where = [];
+        }
+
+        $filter = [];
+        foreach ($where as $logic => $val) {
+            $logic = '$' . strtolower($logic);
+            foreach ($val as $field => $value) {
+                if (is_array($value)) {
+                    if (key($value) !== 0) {
+                        throw new Exception('where express error:' . var_export($value, true));
+                    }
+                    $field = array_shift($value);
+                } elseif (!($value instanceof \Closure)) {
+                    throw new Exception('where express error:' . var_export($value, true));
+                }
+
+                if ($value instanceof \Closure) {
+                    // 使用闭包查询
+                    $query = new Query($this->connection);
+                    call_user_func_array($value, [ & $query]);
+                    $filter[$logic][] = $this->parseWhere($query, $query->getOptions('where'));
+                } else {
+                    if (strpos($field, '|')) {
+                        // 不同字段使用相同查询条件(OR)
+                        $array = explode('|', $field);
+                        foreach ($array as $k) {
+                            $filter['$or'][] = $this->parseWhereItem($query, $k, $value);
+                        }
+                    } elseif (strpos($field, '&')) {
+                        // 不同字段使用相同查询条件(AND)
+                        $array = explode('&', $field);
+                        foreach ($array as $k) {
+                            $filter['$and'][] = $this->parseWhereItem($query, $k, $value);
+                        }
+                    } else {
+                        // 对字段使用表达式查询
+                        $field            = is_string($field) ? $field : '';
+                        $filter[$logic][] = $this->parseWhereItem($query, $field, $value);
+                    }
+                }
+            }
+        }
+
+        $options = $query->getOptions();
+        if (!empty($options['soft_delete'])) {
+            // 附加软删除条件
+            [$field, $condition] = $options['soft_delete'];
+            $filter['$and'][]    = $this->parseWhereItem($query, $field, $condition);
+        }
+
+        return $filter;
+    }
+
+    // where子单元分析
+    protected function parseWhereItem(Query $query, $field, $val): array
+    {
+        $key = $field ? $this->parseKey($query, $field) : '';
+        // 查询规则和条件
+        if (!is_array($val)) {
+            $val = ['=', $val];
+        }
+        [$exp, $value] = $val;
+
+        // 对一个字段使用多个查询条件
+        if (is_array($exp)) {
+            $data = [];
+            foreach ($val as $value) {
+                $exp   = $value[0];
+                $value = $value[1];
+                if (!in_array($exp, $this->exp)) {
+                    $exp = strtolower($exp);
+                    if (isset($this->exp[$exp])) {
+                        $exp = $this->exp[$exp];
+                    }
+                }
+                $k        = '$' . $exp;
+                $data[$k] = $value;
+            }
+            $result[$key] = $data;
+            return $result;
+        } elseif (!in_array($exp, $this->exp)) {
+            $exp = strtolower($exp);
+            if (isset($this->exp[$exp])) {
+                $exp = $this->exp[$exp];
+            } else {
+                throw new Exception('where express error:' . $exp);
+            }
+        }
+
+        $result = [];
+        if ('=' == $exp) {
+            // 普通查询
+            $result[$key] = $this->parseValue($query, $value, $key);
+        } elseif (in_array($exp, ['neq', 'ne', 'gt', 'egt', 'gte', 'lt', 'lte', 'elt', 'mod'])) {
+            // 比较运算
+            $k            = '$' . $exp;
+            $result[$key] = [$k => $this->parseValue($query, $value, $key)];
+        } elseif ('null' == $exp) {
+            // NULL 查询
+            $result[$key] = null;
+        } elseif ('not null' == $exp) {
+            $result[$key] = ['$ne' => null];
+        } elseif ('all' == $exp) {
+            // 满足所有指定条件
+            $result[$key] = ['$all', $this->parseValue($query, $value, $key)];
+        } elseif ('between' == $exp) {
+            // 区间查询
+            $value        = is_array($value) ? $value : explode(',', $value);
+            $result[$key] = ['$gte' => $this->parseValue($query, $value[0], $key), '$lte' => $this->parseValue($query, $value[1], $key)];
+        } elseif ('not between' == $exp) {
+            // 范围查询
+            $value        = is_array($value) ? $value : explode(',', $value);
+            $result[$key] = ['$lt' => $this->parseValue($query, $value[0], $key), '$gt' => $this->parseValue($query, $value[1], $key)];
+        } elseif ('exists' == $exp) {
+            // 字段是否存在
+            $result[$key] = ['$exists' => (bool) $value];
+        } elseif ('type' == $exp) {
+            // 类型查询
+            $result[$key] = ['$type' => intval($value)];
+        } elseif ('exp' == $exp) {
+            // 表达式查询
+            $result['$where'] = $value instanceof Javascript ? $value : new Javascript($value);
+        } elseif ('like' == $exp) {
+            // 模糊查询 采用正则方式
+            $result[$key] = $value instanceof Regex ? $value : new Regex($value, 'i');
+        } elseif (in_array($exp, ['nin', 'in'])) {
+            // IN 查询
+            $value = is_array($value) ? $value : explode(',', $value);
+            foreach ($value as $k => $val) {
+                $value[$k] = $this->parseValue($query, $val, $key);
+            }
+            $result[$key] = ['$' . $exp => $value];
+        } elseif ('regex' == $exp) {
+            $result[$key] = $value instanceof Regex ? $value : new Regex($value, 'i');
+        } elseif ('< time' == $exp) {
+            $result[$key] = ['$lt' => $this->parseDateTime($query, $value, $field)];
+        } elseif ('> time' == $exp) {
+            $result[$key] = ['$gt' => $this->parseDateTime($query, $value, $field)];
+        } elseif ('between time' == $exp) {
+            // 区间查询
+            $value        = is_array($value) ? $value : explode(',', $value);
+            $result[$key] = ['$gte' => $this->parseDateTime($query, $value[0], $field), '$lte' => $this->parseDateTime($query, $value[1], $field)];
+        } elseif ('not between time' == $exp) {
+            // 范围查询
+            $value        = is_array($value) ? $value : explode(',', $value);
+            $result[$key] = ['$lt' => $this->parseDateTime($query, $value[0], $field), '$gt' => $this->parseDateTime($query, $value[1], $field)];
+        } elseif ('near' == $exp) {
+            // 经纬度查询
+            $result[$key] = ['$near' => $this->parseValue($query, $value, $key)];
+        } elseif ('size' == $exp) {
+            // 元素长度查询
+            $result[$key] = ['$size' => intval($value)];
+        } else {
+            // 普通查询
+            $result[$key] = $this->parseValue($query, $value, $key);
+        }
+
+        return $result;
+    }
+
+    /**
+     * 日期时间条件解析
+     * @access protected
+     * @param Query     $query 查询对象
+     * @param string    $value
+     * @param string    $key
+     * @return string
+     */
+    protected function parseDateTime(Query $query, $value, $key)
+    {
+        // 获取时间字段类型
+        $type = $query->getFieldType($key);
+
+        if ($type) {
+            if (is_string($value)) {
+                $value = strtotime($value) ?: $value;
+            }
+
+            if (is_int($value)) {
+                if (preg_match('/(datetime|timestamp)/is', $type)) {
+                    // 日期及时间戳类型
+                    $value = date('Y-m-d H:i:s', $value);
+                } elseif (preg_match('/(date)/is', $type)) {
+                    // 日期及时间戳类型
+                    $value = date('Y-m-d', $value);
+                }
+            }
+        }
+
+        return $value;
+    }
+
+    /**
+     * 获取最后写入的ID 如果是insertAll方法的话 返回所有写入的ID
+     * @access public
+     * @return mixed
+     */
+    public function getLastInsID()
+    {
+        return $this->insertId;
+    }
+
+    /**
+     * 生成insert BulkWrite对象
+     * @access public
+     * @param Query     $query 查询对象
+     * @return BulkWrite
+     */
+    public function insert(Query $query): BulkWrite
+    {
+        // 分析并处理数据
+        $options = $query->getOptions();
+
+        $data = $this->parseData($query, $options['data']);
+
+        $bulk = new BulkWrite;
+
+        if ($insertId = $bulk->insert($data)) {
+            $this->insertId = $insertId;
+        }
+
+        $this->log('insert', $data, $options);
+
+        return $bulk;
+    }
+
+    /**
+     * 生成insertall BulkWrite对象
+     * @access public
+     * @param Query     $query 查询对象
+     * @param array     $dataSet 数据集
+     * @return BulkWrite
+     */
+    public function insertAll(Query $query, array $dataSet): BulkWrite
+    {
+        $bulk    = new BulkWrite;
+        $options = $query->getOptions();
+
+        $this->insertId = [];
+        foreach ($dataSet as $data) {
+            // 分析并处理数据
+            $data = $this->parseData($query, $data);
+            if ($insertId = $bulk->insert($data)) {
+                $this->insertId[] = $insertId;
+            }
+        }
+
+        $this->log('insert', $dataSet, $options);
+
+        return $bulk;
+    }
+
+    /**
+     * 生成update BulkWrite对象
+     * @access public
+     * @param Query     $query 查询对象
+     * @return BulkWrite
+     */
+    public function update(Query $query): BulkWrite
+    {
+        $options = $query->getOptions();
+
+        $data  = $this->parseSet($query, $options['data']);
+        $where = $this->parseWhere($query, $options['where']);
+
+        if (1 == $options['limit']) {
+            $updateOptions = ['multi' => false];
+        } else {
+            $updateOptions = ['multi' => true];
+        }
+
+        $bulk = new BulkWrite;
+
+        $bulk->update($where, $data, $updateOptions);
+
+        $this->log('update', $data, $where);
+
+        return $bulk;
+    }
+
+    /**
+     * 生成delete BulkWrite对象
+     * @access public
+     * @param Query     $query 查询对象
+     * @return BulkWrite
+     */
+    public function delete(Query $query): BulkWrite
+    {
+        $options = $query->getOptions();
+        $where   = $this->parseWhere($query, $options['where']);
+
+        $bulk = new BulkWrite;
+
+        if (1 == $options['limit']) {
+            $deleteOptions = ['limit' => 1];
+        } else {
+            $deleteOptions = ['limit' => 0];
+        }
+
+        $bulk->delete($where, $deleteOptions);
+
+        $this->log('remove', $where, $deleteOptions);
+
+        return $bulk;
+    }
+
+    /**
+     * 生成Mongo查询对象
+     * @access public
+     * @param  Query $query 查询对象
+     * @param  bool  $one   是否仅获取一个记录
+     * @return MongoQuery
+     */
+    public function select(Query $query, bool $one = false): MongoQuery
+    {
+        $options = $query->getOptions();
+
+        $where = $this->parseWhere($query, $options['where']);
+
+        if ($one) {
+            $options['limit'] = 1;
+        }
+
+        $query = new MongoQuery($where, $options);
+
+        $this->log('find', $where, $options);
+
+        return $query;
+    }
+
+    /**
+     * 生成Count命令
+     * @access public
+     * @param Query     $query 查询对象
+     * @return Command
+     */
+    public function count(Query $query): Command
+    {
+        $options = $query->getOptions();
+
+        $cmd['count'] = $options['table'];
+        $cmd['query'] = (object) $this->parseWhere($query, $options['where']);
+
+        foreach (['hint', 'limit', 'maxTimeMS', 'skip'] as $option) {
+            if (isset($options[$option])) {
+                $cmd[$option] = $options[$option];
+            }
+        }
+
+        $command = new Command($cmd);
+        $this->log('cmd', 'count', $cmd);
+
+        return $command;
+    }
+
+    /**
+     * 聚合查询命令
+     * @access public
+     * @param Query     $query  查询对象
+     * @param array     $extra  指令和字段
+     * @return Command
+     */
+    public function aggregate(Query $query, array $extra): Command
+    {
+        $options       = $query->getOptions();
+        [$fun, $field] = $extra;
+
+        if ('id' == $field && $this->connection->getConfig('pk_convert_id')) {
+            $field = '_id';
+        }
+
+        $group = isset($options['group']) ? '$' . $options['group'] : null;
+
+        $pipeline = [
+            ['$match' => (object) $this->parseWhere($query, $options['where'])],
+            ['$group' => ['_id' => $group, 'aggregate' => ['$' . $fun => '$' . $field]]],
+        ];
+
+        $cmd = [
+            'aggregate'    => $options['table'],
+            'allowDiskUse' => true,
+            'pipeline'     => $pipeline,
+            'cursor'       => new \stdClass,
+        ];
+
+        foreach (['explain', 'collation', 'bypassDocumentValidation', 'readConcern'] as $option) {
+            if (isset($options[$option])) {
+                $cmd[$option] = $options[$option];
+            }
+        }
+
+        $command = new Command($cmd);
+
+        $this->log('aggregate', $cmd);
+
+        return $command;
+    }
+
+    /**
+     * 多聚合查询命令, 可以对多个字段进行 group by 操作
+     *
+     * @param Query     $query  查询对象
+     * @param array     $extra 指令和字段
+     * @return Command
+     */
+    public function multiAggregate(Query $query, $extra): Command
+    {
+        $options = $query->getOptions();
+
+        [$aggregate, $groupBy] = $extra;
+
+        $groups = ['_id' => []];
+
+        foreach ($groupBy as $field) {
+            $groups['_id'][$field] = '$' . $field;
+        }
+
+        foreach ($aggregate as $fun => $field) {
+            $groups[$field . '_' . $fun] = ['$' . $fun => '$' . $field];
+        }
+
+        $pipeline = [
+            ['$match' => (object) $this->parseWhere($query, $options['where'])],
+            ['$group' => $groups],
+        ];
+
+        $cmd = [
+            'aggregate'    => $options['table'],
+            'allowDiskUse' => true,
+            'pipeline'     => $pipeline,
+            'cursor'       => new \stdClass,
+        ];
+
+        foreach (['explain', 'collation', 'bypassDocumentValidation', 'readConcern'] as $option) {
+            if (isset($options[$option])) {
+                $cmd[$option] = $options[$option];
+            }
+        }
+
+        $command = new Command($cmd);
+        $this->log('group', $cmd);
+
+        return $command;
+    }
+
+    /**
+     * 生成distinct命令
+     * @access public
+     * @param Query     $query 查询对象
+     * @param string    $field 字段名
+     * @return Command
+     */
+    public function distinct(Query $query, $field): Command
+    {
+        $options = $query->getOptions();
+
+        $cmd = [
+            'distinct' => $options['table'],
+            'key'      => $field,
+        ];
+
+        if (!empty($options['where'])) {
+            $cmd['query'] = (object) $this->parseWhere($query, $options['where']);
+        }
+
+        if (isset($options['maxTimeMS'])) {
+            $cmd['maxTimeMS'] = $options['maxTimeMS'];
+        }
+
+        $command = new Command($cmd);
+
+        $this->log('cmd', 'distinct', $cmd);
+
+        return $command;
+    }
+
+    /**
+     * 查询所有的collection
+     * @access public
+     * @return Command
+     */
+    public function listcollections(): Command
+    {
+        $cmd     = ['listCollections' => 1];
+        $command = new Command($cmd);
+
+        $this->log('cmd', 'listCollections', $cmd);
+
+        return $command;
+    }
+
+    /**
+     * 查询数据表的状态信息
+     * @access public
+     * @param Query     $query 查询对象
+     * @return Command
+     */
+    public function collStats(Query $query): Command
+    {
+        $options = $query->getOptions();
+
+        $cmd     = ['collStats' => $options['table']];
+        $command = new Command($cmd);
+
+        $this->log('cmd', 'collStats', $cmd);
+
+        return $command;
+    }
+
+    protected function log($type, $data, $options = [])
+    {
+        $this->connection->mongoLog($type, $data, $options);
+    }
+}
diff --git a/vendor/topthink/think-orm/src/db/builder/Mysql.php b/vendor/topthink/think-orm/src/db/builder/Mysql.php
new file mode 100755
index 0000000..df6bffd
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/builder/Mysql.php
@@ -0,0 +1,421 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db\builder;
+
+use think\db\Builder;
+use think\db\exception\DbException as Exception;
+use think\db\Query;
+use think\db\Raw;
+
+/**
+ * mysql数据库驱动
+ */
+class Mysql extends Builder
+{
+    /**
+     * 查询表达式解析
+     * @var array
+     */
+    protected $parser = [
+        'parseCompare'     => ['=', '<>', '>', '>=', '<', '<='],
+        'parseLike'        => ['LIKE', 'NOT LIKE'],
+        'parseBetween'     => ['NOT BETWEEN', 'BETWEEN'],
+        'parseIn'          => ['NOT IN', 'IN'],
+        'parseExp'         => ['EXP'],
+        'parseRegexp'      => ['REGEXP', 'NOT REGEXP'],
+        'parseNull'        => ['NOT NULL', 'NULL'],
+        'parseBetweenTime' => ['BETWEEN TIME', 'NOT BETWEEN TIME'],
+        'parseTime'        => ['< TIME', '> TIME', '<= TIME', '>= TIME'],
+        'parseExists'      => ['NOT EXISTS', 'EXISTS'],
+        'parseColumn'      => ['COLUMN'],
+        'parseFindInSet'   => ['FIND IN SET'],
+    ];
+
+    /**
+     * SELECT SQL表达式
+     * @var string
+     */
+    protected $selectSql = 'SELECT%DISTINCT%%EXTRA% %FIELD% FROM %TABLE%%PARTITION%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%UNION%%ORDER%%LIMIT% %LOCK%%COMMENT%';
+
+    /**
+     * INSERT SQL表达式
+     * @var string
+     */
+    protected $insertSql = '%INSERT%%EXTRA% INTO %TABLE%%PARTITION% SET %SET% %DUPLICATE%%COMMENT%';
+
+    /**
+     * INSERT ALL SQL表达式
+     * @var string
+     */
+    protected $insertAllSql = '%INSERT%%EXTRA% INTO %TABLE%%PARTITION% (%FIELD%) VALUES %DATA% %DUPLICATE%%COMMENT%';
+
+    /**
+     * UPDATE SQL表达式
+     * @var string
+     */
+    protected $updateSql = 'UPDATE%EXTRA% %TABLE%%PARTITION% %JOIN% SET %SET% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';
+
+    /**
+     * DELETE SQL表达式
+     * @var string
+     */
+    protected $deleteSql = 'DELETE%EXTRA% FROM %TABLE%%PARTITION%%USING%%JOIN%%WHERE%%ORDER%%LIMIT% %LOCK%%COMMENT%';
+
+    /**
+     * 生成查询SQL
+     * @access public
+     * @param  Query  $query  查询对象
+     * @param  bool   $one    是否仅获取一个记录
+     * @return string
+     */
+    public function select(Query $query, bool $one = false): string
+    {
+        $options = $query->getOptions();
+
+        return str_replace(
+            ['%TABLE%', '%PARTITION%', '%DISTINCT%', '%EXTRA%', '%FIELD%', '%JOIN%', '%WHERE%', '%GROUP%', '%HAVING%', '%ORDER%', '%LIMIT%', '%UNION%', '%LOCK%', '%COMMENT%', '%FORCE%'],
+            [
+                $this->parseTable($query, $options['table']),
+                $this->parsePartition($query, $options['partition']),
+                $this->parseDistinct($query, $options['distinct']),
+                $this->parseExtra($query, $options['extra']),
+                $this->parseField($query, $options['field']),
+                $this->parseJoin($query, $options['join']),
+                $this->parseWhere($query, $options['where']),
+                $this->parseGroup($query, $options['group']),
+                $this->parseHaving($query, $options['having']),
+                $this->parseOrder($query, $options['order']),
+                $this->parseLimit($query, $one ? '1' : $options['limit']),
+                $this->parseUnion($query, $options['union']),
+                $this->parseLock($query, $options['lock']),
+                $this->parseComment($query, $options['comment']),
+                $this->parseForce($query, $options['force']),
+            ],
+            $this->selectSql);
+    }
+
+    /**
+     * 生成Insert SQL
+     * @access public
+     * @param  Query $query 查询对象
+     * @return string
+     */
+    public function insert(Query $query): string
+    {
+        $options = $query->getOptions();
+
+        // 分析并处理数据
+        $data = $this->parseData($query, $options['data']);
+        if (empty($data)) {
+            return '';
+        }
+
+        $set = [];
+        foreach ($data as $key => $val) {
+            $set[] = $key . ' = ' . $val;
+        }
+
+        return str_replace(
+            ['%INSERT%', '%EXTRA%', '%TABLE%', '%PARTITION%', '%SET%', '%DUPLICATE%', '%COMMENT%'],
+            [
+                !empty($options['replace']) ? 'REPLACE' : 'INSERT',
+                $this->parseExtra($query, $options['extra']),
+                $this->parseTable($query, $options['table']),
+                $this->parsePartition($query, $options['partition']),
+                implode(' , ', $set),
+                $this->parseDuplicate($query, $options['duplicate']),
+                $this->parseComment($query, $options['comment']),
+            ],
+            $this->insertSql);
+    }
+
+    /**
+     * 生成insertall SQL
+     * @access public
+     * @param  Query     $query   查询对象
+     * @param  array     $dataSet 数据集
+     * @param  bool      $replace 是否replace
+     * @return string
+     */
+    public function insertAll(Query $query, array $dataSet, bool $replace = false): string
+    {
+        $options = $query->getOptions();
+
+        // 获取绑定信息
+        $bind = $query->getFieldsBindType();
+
+        // 获取合法的字段
+        if ('*' == $options['field']) {
+            $allowFields = array_keys($bind);
+        } else {
+            $allowFields = $options['field'];
+        }
+
+        $fields = [];
+        $values = [];
+
+        foreach ($dataSet as $data) {
+            $data = $this->parseData($query, $data, $allowFields, $bind);
+
+            $values[] = '( ' . implode(',', array_values($data)) . ' )';
+
+            if (!isset($insertFields)) {
+                $insertFields = array_keys($data);
+            }
+        }
+
+        foreach ($insertFields as $field) {
+            $fields[] = $this->parseKey($query, $field);
+        }
+
+        return str_replace(
+            ['%INSERT%', '%EXTRA%', '%TABLE%', '%PARTITION%', '%FIELD%', '%DATA%', '%DUPLICATE%', '%COMMENT%'],
+            [
+                $replace ? 'REPLACE' : 'INSERT',
+                $this->parseExtra($query, $options['extra']),
+                $this->parseTable($query, $options['table']),
+                $this->parsePartition($query, $options['partition']),
+                implode(' , ', $fields),
+                implode(' , ', $values),
+                $this->parseDuplicate($query, $options['duplicate']),
+                $this->parseComment($query, $options['comment']),
+            ],
+            $this->insertAllSql);
+    }
+
+    /**
+     * 生成update SQL
+     * @access public
+     * @param  Query     $query  查询对象
+     * @return string
+     */
+    public function update(Query $query): string
+    {
+        $options = $query->getOptions();
+
+        $data = $this->parseData($query, $options['data']);
+
+        if (empty($data)) {
+            return '';
+        }
+        $set = [];
+        foreach ($data as $key => $val) {
+            $set[] = $key . ' = ' . $val;
+        }
+
+        return str_replace(
+            ['%TABLE%', '%PARTITION%', '%EXTRA%', '%SET%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'],
+            [
+                $this->parseTable($query, $options['table']),
+                $this->parsePartition($query, $options['partition']),
+                $this->parseExtra($query, $options['extra']),
+                implode(' , ', $set),
+                $this->parseJoin($query, $options['join']),
+                $this->parseWhere($query, $options['where']),
+                $this->parseOrder($query, $options['order']),
+                $this->parseLimit($query, $options['limit']),
+                $this->parseLock($query, $options['lock']),
+                $this->parseComment($query, $options['comment']),
+            ],
+            $this->updateSql);
+    }
+
+    /**
+     * 生成delete SQL
+     * @access public
+     * @param  Query  $query  查询对象
+     * @return string
+     */
+    public function delete(Query $query): string
+    {
+        $options = $query->getOptions();
+
+        return str_replace(
+            ['%TABLE%', '%PARTITION%', '%EXTRA%', '%USING%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'],
+            [
+                $this->parseTable($query, $options['table']),
+                $this->parsePartition($query, $options['partition']),
+                $this->parseExtra($query, $options['extra']),
+                !empty($options['using']) ? ' USING ' . $this->parseTable($query, $options['using']) . ' ' : '',
+                $this->parseJoin($query, $options['join']),
+                $this->parseWhere($query, $options['where']),
+                $this->parseOrder($query, $options['order']),
+                $this->parseLimit($query, $options['limit']),
+                $this->parseLock($query, $options['lock']),
+                $this->parseComment($query, $options['comment']),
+            ],
+            $this->deleteSql);
+    }
+
+    /**
+     * 正则查询
+     * @access protected
+     * @param  Query        $query        查询对象
+     * @param  string       $key
+     * @param  string       $exp
+     * @param  mixed        $value
+     * @param  string       $field
+     * @return string
+     */
+    protected function parseRegexp(Query $query, string $key, string $exp, $value, string $field): string
+    {
+        if ($value instanceof Raw) {
+            $value = $this->parseRaw($query, $value);
+        }
+
+        return $key . ' ' . $exp . ' ' . $value;
+    }
+
+    /**
+     * FIND_IN_SET 查询
+     * @access protected
+     * @param  Query        $query        查询对象
+     * @param  string       $key
+     * @param  string       $exp
+     * @param  mixed        $value
+     * @param  string       $field
+     * @return string
+     */
+    protected function parseFindInSet(Query $query, string $key, string $exp, $value, string $field): string
+    {
+        if ($value instanceof Raw) {
+            $value = $this->parseRaw($query, $value);
+        }
+
+        return 'FIND_IN_SET(' . $value . ', ' . $key . ')';
+    }
+
+    /**
+     * 字段和表名处理
+     * @access public
+     * @param  Query     $query 查询对象
+     * @param  mixed     $key   字段名
+     * @param  bool      $strict   严格检测
+     * @return string
+     */
+    public function parseKey(Query $query, $key, bool $strict = false): string
+    {
+        if (is_int($key)) {
+            return (string) $key;
+        } elseif ($key instanceof Raw) {
+            return $this->parseRaw($query, $key);
+        }
+
+        $key = trim($key);
+
+        if (strpos($key, '->') && false === strpos($key, '(')) {
+            // JSON字段支持
+            [$field, $name] = explode('->', $key, 2);
+            return 'json_extract(' . $this->parseKey($query, $field) . ', \'$' . (strpos($name, '[') === 0 ? '' : '.') . str_replace('->', '.', $name) . '\')';
+        } elseif (strpos($key, '.') && !preg_match('/[,\'\"\(\)`\s]/', $key)) {
+            [$table, $key] = explode('.', $key, 2);
+
+            $alias = $query->getOptions('alias');
+
+            if ('__TABLE__' == $table) {
+                $table = $query->getOptions('table');
+                $table = is_array($table) ? array_shift($table) : $table;
+            }
+
+            if (isset($alias[$table])) {
+                $table = $alias[$table];
+            }
+        }
+
+        if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) {
+            throw new Exception('not support data:' . $key);
+        }
+
+        if ('*' != $key && !preg_match('/[,\'\"\*\(\)`.\s]/', $key)) {
+            $key = '`' . $key . '`';
+        }
+
+        if (isset($table)) {
+            if (strpos($table, '.')) {
+                $table = str_replace('.', '`.`', $table);
+            }
+
+            $key = '`' . $table . '`.' . $key;
+        }
+
+        return $key;
+    }
+
+    /**
+     * 随机排序
+     * @access protected
+     * @param  Query     $query        查询对象
+     * @return string
+     */
+    protected function parseRand(Query $query): string
+    {
+        return 'rand()';
+    }
+
+    /**
+     * Partition 分析
+     * @access protected
+     * @param  Query        $query    查询对象
+     * @param  string|array $partition  分区
+     * @return string
+     */
+    protected function parsePartition(Query $query, $partition): string
+    {
+        if ('' == $partition) {
+            return '';
+        }
+
+        if (is_string($partition)) {
+            $partition = explode(',', $partition);
+        }
+
+        return ' PARTITION (' . implode(' , ', $partition) . ') ';
+    }
+
+    /**
+     * ON DUPLICATE KEY UPDATE 分析
+     * @access protected
+     * @param  Query  $query    查询对象
+     * @param  mixed  $duplicate
+     * @return string
+     */
+    protected function parseDuplicate(Query $query, $duplicate): string
+    {
+        if ('' == $duplicate) {
+            return '';
+        }
+
+        if ($duplicate instanceof Raw) {
+            return ' ON DUPLICATE KEY UPDATE ' . $this->parseRaw($query, $duplicate) . ' ';
+        }
+
+        if (is_string($duplicate)) {
+            $duplicate = explode(',', $duplicate);
+        }
+
+        $updates = [];
+        foreach ($duplicate as $key => $val) {
+            if (is_numeric($key)) {
+                $val       = $this->parseKey($query, $val);
+                $updates[] = $val . ' = VALUES(' . $val . ')';
+            } elseif ($val instanceof Raw) {
+                $updates[] = $this->parseKey($query, $key) . " = " . $this->parseRaw($query, $val);
+            } else {
+                $name      = $query->bindValue($val, $query->getConnection()->getFieldBindType($key));
+                $updates[] = $this->parseKey($query, $key) . " = :" . $name;
+            }
+        }
+
+        return ' ON DUPLICATE KEY UPDATE ' . implode(' , ', $updates) . ' ';
+    }
+}
diff --git a/vendor/topthink/think-orm/src/db/builder/Oracle.php b/vendor/topthink/think-orm/src/db/builder/Oracle.php
new file mode 100755
index 0000000..77ad3c8
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/builder/Oracle.php
@@ -0,0 +1,95 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db\builder;
+
+use think\db\Builder;
+use think\db\Query;
+
+/**
+ * Oracle数据库驱动
+ */
+class Oracle extends Builder
+{
+    protected $selectSql = 'SELECT * FROM (SELECT thinkphp.*, rownum AS numrow FROM (SELECT  %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%) thinkphp ) %LIMIT%%COMMENT%';
+
+    /**
+     * limit分析
+     * @access protected
+     * @param  Query $query 查询对象
+     * @param  mixed $limit
+     * @return string
+     */
+    protected function parseLimit(Query $query, string $limit): string
+    {
+        $limitStr = '';
+
+        if (!empty($limit)) {
+            $limit = explode(',', $limit);
+
+            if (count($limit) > 1) {
+                $limitStr = "(numrow>" . $limit[0] . ") AND (numrow<=" . ($limit[0] + $limit[1]) . ")";
+            } else {
+                $limitStr = "(numrow>0 AND numrow<=" . $limit[0] . ")";
+            }
+
+        }
+
+        return $limitStr ? ' WHERE ' . $limitStr : '';
+    }
+
+    /**
+     * 设置锁机制
+     * @access protected
+     * @param  Query      $query 查询对象
+     * @param  bool|false $lock
+     * @return string
+     */
+    protected function parseLock(Query $query, $lock = false): string
+    {
+        if (!$lock) {
+            return '';
+        }
+
+        return ' FOR UPDATE NOWAIT ';
+    }
+
+    /**
+     * 字段和表名处理
+     * @access public
+     * @param  Query  $query  查询对象
+     * @param  string $key
+     * @param  string $strict
+     * @return string
+     */
+    public function parseKey(Query $query, $key, bool $strict = false): string
+    {
+        $key = trim($key);
+
+        if (strpos($key, '->') && false === strpos($key, '(')) {
+            // JSON字段支持
+            [$field, $name] = explode($key, '->');
+            $key            = $field . '."' . $name . '"';
+        }
+
+        return $key;
+    }
+
+    /**
+     * 随机排序
+     * @access protected
+     * @param  Query $query 查询对象
+     * @return string
+     */
+    protected function parseRand(Query $query): string
+    {
+        return 'DBMS_RANDOM.value';
+    }
+}
diff --git a/vendor/topthink/think-orm/src/db/builder/Pgsql.php b/vendor/topthink/think-orm/src/db/builder/Pgsql.php
new file mode 100755
index 0000000..4eace0a
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/builder/Pgsql.php
@@ -0,0 +1,118 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db\builder;
+
+use think\db\Builder;
+use think\db\Query;
+use think\db\Raw;
+
+/**
+ * Pgsql数据库驱动
+ */
+class Pgsql extends Builder
+{
+    /**
+     * INSERT SQL表达式
+     * @var string
+     */
+    protected $insertSql = 'INSERT INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%';
+
+    /**
+     * INSERT ALL SQL表达式
+     * @var string
+     */
+    protected $insertAllSql = 'INSERT INTO %TABLE% (%FIELD%) %DATA% %COMMENT%';
+
+    /**
+     * limit分析
+     * @access protected
+     * @param  Query     $query        查询对象
+     * @param  mixed     $limit
+     * @return string
+     */
+    public function parseLimit(Query $query, string $limit): string
+    {
+        $limitStr = '';
+
+        if (!empty($limit)) {
+            $limit = explode(',', $limit);
+            if (count($limit) > 1) {
+                $limitStr .= ' LIMIT ' . $limit[1] . ' OFFSET ' . $limit[0] . ' ';
+            } else {
+                $limitStr .= ' LIMIT ' . $limit[0] . ' ';
+            }
+        }
+
+        return $limitStr;
+    }
+
+    /**
+     * 字段和表名处理
+     * @access public
+     * @param  Query     $query     查询对象
+     * @param  mixed     $key       字段名
+     * @param  bool      $strict   严格检测
+     * @return string
+     */
+    public function parseKey(Query $query, $key, bool $strict = false): string
+    {
+        if (is_int($key)) {
+            return (string) $key;
+        } elseif ($key instanceof Raw) {
+            return $this->parseRaw($query, $key);
+        }
+
+        $key = trim($key);
+
+        if (strpos($key, '->') && false === strpos($key, '(')) {
+            // JSON字段支持
+            [$field, $name] = explode('->', $key);
+            $key            = '"' . $field . '"' . '->>\'' . $name . '\'';
+        } elseif (strpos($key, '.')) {
+            [$table, $key] = explode('.', $key, 2);
+
+            $alias = $query->getOptions('alias');
+
+            if ('__TABLE__' == $table) {
+                $table = $query->getOptions('table');
+                $table = is_array($table) ? array_shift($table) : $table;
+            }
+
+            if (isset($alias[$table])) {
+                $table = $alias[$table];
+            }
+
+            if ('*' != $key && !preg_match('/[,\"\*\(\).\s]/', $key)) {
+                $key = '"' . $key . '"';
+            }
+        }
+
+        if (isset($table)) {
+            $key = $table . '.' . $key;
+        }
+
+        return $key;
+    }
+
+    /**
+     * 随机排序
+     * @access protected
+     * @param  Query     $query        查询对象
+     * @return string
+     */
+    protected function parseRand(Query $query): string
+    {
+        return 'RANDOM()';
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/db/builder/Sqlite.php b/vendor/topthink/think-orm/src/db/builder/Sqlite.php
new file mode 100755
index 0000000..40cab7f
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/builder/Sqlite.php
@@ -0,0 +1,97 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db\builder;
+
+use think\db\Builder;
+use think\db\Query;
+use think\db\Raw;
+
+/**
+ * Sqlite数据库驱动
+ */
+class Sqlite extends Builder
+{
+    /**
+     * limit
+     * @access public
+     * @param  Query     $query        查询对象
+     * @param  mixed     $limit
+     * @return string
+     */
+    public function parseLimit(Query $query, string $limit): string
+    {
+        $limitStr = '';
+
+        if (!empty($limit)) {
+            $limit = explode(',', $limit);
+            if (count($limit) > 1) {
+                $limitStr .= ' LIMIT ' . $limit[1] . ' OFFSET ' . $limit[0] . ' ';
+            } else {
+                $limitStr .= ' LIMIT ' . $limit[0] . ' ';
+            }
+        }
+
+        return $limitStr;
+    }
+
+    /**
+     * 随机排序
+     * @access protected
+     * @param  Query     $query        查询对象
+     * @return string
+     */
+    protected function parseRand(Query $query): string
+    {
+        return 'RANDOM()';
+    }
+
+    /**
+     * 字段和表名处理
+     * @access public
+     * @param  Query     $query     查询对象
+     * @param  mixed     $key       字段名
+     * @param  bool      $strict   严格检测
+     * @return string
+     */
+    public function parseKey(Query $query, $key, bool $strict = false): string
+    {
+        if (is_int($key)) {
+            return (string) $key;
+        } elseif ($key instanceof Raw) {
+            return $this->parseRaw($query, $key);
+        }
+
+        $key = trim($key);
+
+        if (strpos($key, '.')) {
+            [$table, $key] = explode('.', $key, 2);
+
+            $alias = $query->getOptions('alias');
+
+            if ('__TABLE__' == $table) {
+                $table = $query->getOptions('table');
+                $table = is_array($table) ? array_shift($table) : $table;
+            }
+
+            if (isset($alias[$table])) {
+                $table = $alias[$table];
+            }
+        }
+
+        if (isset($table)) {
+            $key = $table . '.' . $key;
+        }
+
+        return $key;
+    }
+}
diff --git a/vendor/topthink/think-orm/src/db/builder/Sqlsrv.php b/vendor/topthink/think-orm/src/db/builder/Sqlsrv.php
new file mode 100755
index 0000000..779b5e3
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/builder/Sqlsrv.php
@@ -0,0 +1,184 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2012 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\db\builder;
+
+use think\db\Builder;
+use think\db\exception\DbException as Exception;
+use think\db\Query;
+use think\db\Raw;
+
+/**
+ * Sqlsrv数据库驱动
+ */
+class Sqlsrv extends Builder
+{
+    /**
+     * SELECT SQL表达式
+     * @var string
+     */
+    protected $selectSql = 'SELECT T1.* FROM (SELECT thinkphp.*, ROW_NUMBER() OVER (%ORDER%) AS ROW_NUMBER FROM (SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%) AS thinkphp) AS T1 %LIMIT%%COMMENT%';
+    /**
+     * SELECT INSERT SQL表达式
+     * @var string
+     */
+    protected $selectInsertSql = 'SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%';
+
+    /**
+     * UPDATE SQL表达式
+     * @var string
+     */
+    protected $updateSql = 'UPDATE %TABLE% SET %SET% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%';
+
+    /**
+     * DELETE SQL表达式
+     * @var string
+     */
+    protected $deleteSql = 'DELETE FROM %TABLE% %USING% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%';
+
+    /**
+     * INSERT SQL表达式
+     * @var string
+     */
+    protected $insertSql = 'INSERT INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%';
+
+    /**
+     * INSERT ALL SQL表达式
+     * @var string
+     */
+    protected $insertAllSql = 'INSERT INTO %TABLE% (%FIELD%) %DATA% %COMMENT%';
+
+    /**
+     * order分析
+     * @access protected
+     * @param  Query     $query        查询对象
+     * @param  mixed     $order
+     * @return string
+     */
+    protected function parseOrder(Query $query, array $order): string
+    {
+        if (empty($order)) {
+            return ' ORDER BY rand()';
+        }
+
+        $array = [];
+
+        foreach ($order as $key => $val) {
+            if ($val instanceof Raw) {
+                $array[] = $this->parseRaw($query, $val);
+            } elseif ('[rand]' == $val) {
+                $array[] = $this->parseRand($query);
+            } else {
+                if (is_numeric($key)) {
+                    [$key, $sort] = explode(' ', strpos($val, ' ') ? $val : $val . ' ');
+                } else {
+                    $sort = $val;
+                }
+
+                $sort    = in_array(strtolower($sort), ['asc', 'desc'], true) ? ' ' . $sort : '';
+                $array[] = $this->parseKey($query, $key, true) . $sort;
+            }
+        }
+
+        return ' ORDER BY ' . implode(',', $array);
+    }
+
+    /**
+     * 随机排序
+     * @access protected
+     * @param  Query     $query        查询对象
+     * @return string
+     */
+    protected function parseRand(Query $query): string
+    {
+        return 'rand()';
+    }
+
+    /**
+     * 字段和表名处理
+     * @access public
+     * @param  Query     $query     查询对象
+     * @param  mixed     $key       字段名
+     * @param  bool      $strict   严格检测
+     * @return string
+     */
+    public function parseKey(Query $query, $key, bool $strict = false): string
+    {
+        if (is_int($key)) {
+            return (string) $key;
+        } elseif ($key instanceof Raw) {
+            return $this->parseRaw($query, $key);
+        }
+
+        $key = trim($key);
+
+        if (strpos($key, '.') && !preg_match('/[,\'\"\(\)\[\s]/', $key)) {
+            [$table, $key] = explode('.', $key, 2);
+
+            $alias = $query->getOptions('alias');
+
+            if ('__TABLE__' == $table) {
+                $table = $query->getOptions('table');
+                $table = is_array($table) ? array_shift($table) : $table;
+            }
+
+            if (isset($alias[$table])) {
+                $table = $alias[$table];
+            }
+        }
+
+        if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) {
+            throw new Exception('not support data:' . $key);
+        }
+
+        if ('*' != $key && !preg_match('/[,\'\"\*\(\)\[.\s]/', $key)) {
+            $key = '[' . $key . ']';
+        }
+
+        if (isset($table)) {
+            $key = '[' . $table . '].' . $key;
+        }
+
+        return $key;
+    }
+
+    /**
+     * limit
+     * @access protected
+     * @param  Query     $query        查询对象
+     * @param  mixed     $limit
+     * @return string
+     */
+    protected function parseLimit(Query $query, string $limit): string
+    {
+        if (empty($limit)) {
+            return '';
+        }
+
+        $limit = explode(',', $limit);
+
+        if (count($limit) > 1) {
+            $limitStr = '(T1.ROW_NUMBER BETWEEN ' . $limit[0] . ' + 1 AND ' . $limit[0] . ' + ' . $limit[1] . ')';
+        } else {
+            $limitStr = '(T1.ROW_NUMBER BETWEEN 1 AND ' . $limit[0] . ")";
+        }
+
+        return 'WHERE ' . $limitStr;
+    }
+
+    public function selectInsert(Query $query, array $fields, string $table): string
+    {
+        $this->selectSql = $this->selectInsertSql;
+
+        return parent::selectInsert($query, $fields, $table);
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php b/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php
new file mode 100755
index 0000000..dabfb92
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php
@@ -0,0 +1,107 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db\concern;
+
+use think\db\Raw;
+
+/**
+ * 聚合查询
+ */
+trait AggregateQuery
+{
+    /**
+     * 聚合查询
+     * @access protected
+     * @param string     $aggregate 聚合方法
+     * @param string|Raw $field     字段名
+     * @param bool       $force     强制转为数字类型
+     * @return mixed
+     */
+    protected function aggregate(string $aggregate, $field, bool $force = false)
+    {
+        return $this->connection->aggregate($this, $aggregate, $field, $force);
+    }
+
+    /**
+     * COUNT查询
+     * @access public
+     * @param string|Raw $field 字段名
+     * @return int
+     */
+    public function count(string $field = '*'): int
+    {
+        if (!empty($this->options['group'])) {
+            // 支持GROUP
+            $options = $this->getOptions();
+            $subSql  = $this->options($options)
+                ->field('count(' . $field . ') AS think_count')
+                ->bind($this->bind)
+                ->buildSql();
+
+            $query = $this->newQuery()->table([$subSql => '_group_count_']);
+
+            $count = $query->aggregate('COUNT', '*');
+        } else {
+            $count = $this->aggregate('COUNT', $field);
+        }
+
+        return (int) $count;
+    }
+
+    /**
+     * SUM查询
+     * @access public
+     * @param string|Raw $field 字段名
+     * @return float
+     */
+    public function sum($field): float
+    {
+        return $this->aggregate('SUM', $field, true);
+    }
+
+    /**
+     * MIN查询
+     * @access public
+     * @param string|Raw $field 字段名
+     * @param bool       $force 强制转为数字类型
+     * @return mixed
+     */
+    public function min($field, bool $force = true)
+    {
+        return $this->aggregate('MIN', $field, $force);
+    }
+
+    /**
+     * MAX查询
+     * @access public
+     * @param string|Raw $field 字段名
+     * @param bool       $force 强制转为数字类型
+     * @return mixed
+     */
+    public function max($field, bool $force = true)
+    {
+        return $this->aggregate('MAX', $field, $force);
+    }
+
+    /**
+     * AVG查询
+     * @access public
+     * @param string|Raw $field 字段名
+     * @return float
+     */
+    public function avg($field): float
+    {
+        return $this->aggregate('AVG', $field, true);
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php b/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php
new file mode 100755
index 0000000..c33d1ed
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php
@@ -0,0 +1,229 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db\concern;
+
+use think\db\Raw;
+use think\helper\Str;
+
+/**
+ * JOIN和VIEW查询
+ */
+trait JoinAndViewQuery
+{
+
+    /**
+     * 查询SQL组装 join
+     * @access public
+     * @param mixed  $join      关联的表名
+     * @param mixed  $condition 条件
+     * @param string $type      JOIN类型
+     * @param array  $bind      参数绑定
+     * @return $this
+     */
+    public function join($join, string $condition = null, string $type = 'INNER', array $bind = [])
+    {
+        $table = $this->getJoinTable($join);
+
+        if (!empty($bind) && $condition) {
+            $this->bindParams($condition, $bind);
+        }
+
+        $this->options['join'][] = [$table, strtoupper($type), $condition];
+
+        return $this;
+    }
+
+    /**
+     * LEFT JOIN
+     * @access public
+     * @param mixed $join      关联的表名
+     * @param mixed $condition 条件
+     * @param array $bind      参数绑定
+     * @return $this
+     */
+    public function leftJoin($join, string $condition = null, array $bind = [])
+    {
+        return $this->join($join, $condition, 'LEFT', $bind);
+    }
+
+    /**
+     * RIGHT JOIN
+     * @access public
+     * @param mixed $join      关联的表名
+     * @param mixed $condition 条件
+     * @param array $bind      参数绑定
+     * @return $this
+     */
+    public function rightJoin($join, string $condition = null, array $bind = [])
+    {
+        return $this->join($join, $condition, 'RIGHT', $bind);
+    }
+
+    /**
+     * FULL JOIN
+     * @access public
+     * @param mixed $join      关联的表名
+     * @param mixed $condition 条件
+     * @param array $bind      参数绑定
+     * @return $this
+     */
+    public function fullJoin($join, string $condition = null, array $bind = [])
+    {
+        return $this->join($join, $condition, 'FULL');
+    }
+
+    /**
+     * 获取Join表名及别名 支持
+     * ['prefix_table或者子查询'=>'alias'] 'table alias'
+     * @access protected
+     * @param array|string|Raw $join  JION表名
+     * @param string           $alias 别名
+     * @return string|array
+     */
+    protected function getJoinTable($join, &$alias = null)
+    {
+        if (is_array($join)) {
+            $table = $join;
+            $alias = array_shift($join);
+            return $table;
+        } elseif ($join instanceof Raw) {
+            return $join;
+        }
+
+        $join = trim($join);
+
+        if (false !== strpos($join, '(')) {
+            // 使用子查询
+            $table = $join;
+        } else {
+            // 使用别名
+            if (strpos($join, ' ')) {
+                // 使用别名
+                [$table, $alias] = explode(' ', $join);
+            } else {
+                $table = $join;
+                if (false === strpos($join, '.')) {
+                    $alias = $join;
+                }
+            }
+
+            if ($this->prefix && false === strpos($table, '.') && 0 !== strpos($table, $this->prefix)) {
+                $table = $this->getTable($table);
+            }
+        }
+
+        if (!empty($alias) && $table != $alias) {
+            $table = [$table => $alias];
+        }
+
+        return $table;
+    }
+
+    /**
+     * 指定JOIN查询字段
+     * @access public
+     * @param string|array $join  数据表
+     * @param string|array $field 查询字段
+     * @param string       $on    JOIN条件
+     * @param string       $type  JOIN类型
+     * @param array        $bind  参数绑定
+     * @return $this
+     */
+    public function view($join, $field = true, $on = null, string $type = 'INNER', array $bind = [])
+    {
+        $this->options['view'] = true;
+
+        $fields = [];
+        $table  = $this->getJoinTable($join, $alias);
+
+        if (true === $field) {
+            $fields = $alias . '.*';
+        } else {
+            if (is_string($field)) {
+                $field = explode(',', $field);
+            }
+
+            foreach ($field as $key => $val) {
+                if (is_numeric($key)) {
+                    $fields[] = $alias . '.' . $val;
+
+                    $this->options['map'][$val] = $alias . '.' . $val;
+                } else {
+                    if (preg_match('/[,=\.\'\"\(\s]/', $key)) {
+                        $name = $key;
+                    } else {
+                        $name = $alias . '.' . $key;
+                    }
+
+                    $fields[] = $name . ' AS ' . $val;
+
+                    $this->options['map'][$val] = $name;
+                }
+            }
+        }
+
+        $this->field($fields);
+
+        if ($on) {
+            $this->join($table, $on, $type, $bind);
+        } else {
+            $this->table($table);
+        }
+
+        return $this;
+    }
+
+    /**
+     * 视图查询处理
+     * @access protected
+     * @param array $options 查询参数
+     * @return void
+     */
+    protected function parseView(array &$options): void
+    {
+        foreach (['AND', 'OR'] as $logic) {
+            if (isset($options['where'][$logic])) {
+                foreach ($options['where'][$logic] as $key => $val) {
+                    if (array_key_exists($key, $options['map'])) {
+                        array_shift($val);
+                        array_unshift($val, $options['map'][$key]);
+                        $options['where'][$logic][$options['map'][$key]] = $val;
+                        unset($options['where'][$logic][$key]);
+                    }
+                }
+            }
+        }
+
+        if (isset($options['order'])) {
+            // 视图查询排序处理
+            foreach ($options['order'] as $key => $val) {
+                if (is_numeric($key) && is_string($val)) {
+                    if (strpos($val, ' ')) {
+                        [$field, $sort] = explode(' ', $val);
+                        if (array_key_exists($field, $options['map'])) {
+                            $options['order'][$options['map'][$field]] = $sort;
+                            unset($options['order'][$key]);
+                        }
+                    } elseif (array_key_exists($val, $options['map'])) {
+                        $options['order'][$options['map'][$val]] = 'asc';
+                        unset($options['order'][$key]);
+                    }
+                } elseif (array_key_exists($key, $options['map'])) {
+                    $options['order'][$options['map'][$key]] = $val;
+                    unset($options['order'][$key]);
+                }
+            }
+        }
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php b/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php
new file mode 100755
index 0000000..ffb72de
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php
@@ -0,0 +1,524 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db\concern;
+
+use Closure;
+use think\helper\Str;
+use think\Model;
+use think\model\Collection as ModelCollection;
+
+/**
+ * 模型及关联查询
+ */
+trait ModelRelationQuery
+{
+
+    /**
+     * 当前模型对象
+     * @var Model
+     */
+    protected $model;
+
+    /**
+     * 指定模型
+     * @access public
+     * @param Model $model 模型对象实例
+     * @return $this
+     */
+    public function model(Model $model)
+    {
+        $this->model = $model;
+        return $this;
+    }
+
+    /**
+     * 获取当前的模型对象
+     * @access public
+     * @return Model|null
+     */
+    public function getModel()
+    {
+        return $this->model;
+    }
+
+    /**
+     * 设置需要隐藏的输出属性
+     * @access public
+     * @param array $hidden 需要隐藏的字段名
+     * @return $this
+     */
+    public function hidden(array $hidden)
+    {
+        $this->options['hidden'] = $hidden;
+        return $this;
+    }
+
+    /**
+     * 设置需要输出的属性
+     * @access public
+     * @param array $visible 需要输出的属性
+     * @return $this
+     */
+    public function visible(array $visible)
+    {
+        $this->options['visible'] = $visible;
+        return $this;
+    }
+
+    /**
+     * 设置需要追加输出的属性
+     * @access public
+     * @param array $append 需要追加的属性
+     * @return $this
+     */
+    public function append(array $append)
+    {
+        $this->options['append'] = $append;
+        return $this;
+    }
+
+    /**
+     * 添加查询范围
+     * @access public
+     * @param array|string|Closure $scope 查询范围定义
+     * @param array                $args  参数
+     * @return $this
+     */
+    public function scope($scope, ...$args)
+    {
+        // 查询范围的第一个参数始终是当前查询对象
+        array_unshift($args, $this);
+
+        if ($scope instanceof Closure) {
+            call_user_func_array($scope, $args);
+            return $this;
+        }
+
+        if (is_string($scope)) {
+            $scope = explode(',', $scope);
+        }
+
+        if ($this->model) {
+            // 检查模型类的查询范围方法
+            foreach ($scope as $name) {
+                $method = 'scope' . trim($name);
+
+                if (method_exists($this->model, $method)) {
+                    call_user_func_array([$this->model, $method], $args);
+                }
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * 设置关联查询
+     * @access public
+     * @param array $relation 关联名称
+     * @return $this
+     */
+    public function relation(array $relation)
+    {
+        if (!empty($relation)) {
+            $this->options['relation'] = $relation;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 使用搜索器条件搜索字段
+     * @access public
+     * @param string|array  $fields 搜索字段
+     * @param mixed         $data   搜索数据
+     * @param string        $prefix 字段前缀标识
+     * @return $this
+     */
+    public function withSearch($fields, $data = [], string $prefix = '')
+    {
+        if (is_string($fields)) {
+            $fields = explode(',', $fields);
+        }
+
+        $likeFields = $this->getConfig('match_like_fields') ?: [];
+
+        foreach ($fields as $key => $field) {
+            if ($field instanceof Closure) {
+                $field($this, $data[$key] ?? null, $data, $prefix);
+            } elseif ($this->model) {
+                // 检测搜索器
+                $fieldName = is_numeric($key) ? $field : $key;
+                $method    = 'search' . Str::studly($fieldName) . 'Attr';
+
+                if (method_exists($this->model, $method)) {
+                    $this->model->$method($this, $data[$field] ?? null, $data, $prefix);
+                } elseif (isset($data[$field])) {
+                    $this->where($fieldName, in_array($fieldName, $likeFields) ? 'like' : '=', in_array($fieldName, $likeFields) ? '%' . $data[$field] . '%' : $data[$field]);
+                }
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * 设置数据字段获取器
+     * @access public
+     * @param string|array $name     字段名
+     * @param callable     $callback 闭包获取器
+     * @return $this
+     */
+    public function withAttr($name, callable $callback = null)
+    {
+        if (is_array($name)) {
+            $this->options['with_attr'] = $name;
+        } else {
+            $this->options['with_attr'][$name] = $callback;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 关联预载入 In方式
+     * @access public
+     * @param array|string $with 关联方法名称
+     * @return $this
+     */
+    public function with($with)
+    {
+        if (!empty($with)) {
+            $this->options['with'] = (array) $with;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 关联预载入 JOIN方式
+     * @access protected
+     * @param array|string $with     关联方法名
+     * @param string       $joinType JOIN方式
+     * @return $this
+     */
+    public function withJoin($with, string $joinType = '')
+    {
+        if (empty($with)) {
+            return $this;
+        }
+
+        $with  = (array) $with;
+        $first = true;
+
+        foreach ($with as $key => $relation) {
+            $closure = null;
+            $field   = true;
+
+            if ($relation instanceof Closure) {
+                // 支持闭包查询过滤关联条件
+                $closure  = $relation;
+                $relation = $key;
+            } elseif (is_array($relation)) {
+                $field    = $relation;
+                $relation = $key;
+            } elseif (is_string($relation) && strpos($relation, '.')) {
+                $relation = strstr($relation, '.', true);
+            }
+
+            $result = $this->model->eagerly($this, $relation, $field, $joinType, $closure, $first);
+
+            if (!$result) {
+                unset($with[$key]);
+            } else {
+                $first = false;
+            }
+        }
+
+        $this->via();
+
+        $this->options['with_join'] = $with;
+
+        return $this;
+    }
+
+    /**
+     * 关联统计
+     * @access protected
+     * @param array|string $relations 关联方法名
+     * @param string       $aggregate 聚合查询方法
+     * @param string       $field     字段
+     * @param bool         $subQuery  是否使用子查询
+     * @return $this
+     */
+    protected function withAggregate($relations, string $aggregate = 'count', $field = '*', bool $subQuery = true)
+    {
+        if (!$subQuery) {
+            $this->options['with_count'][] = [$relations, $aggregate, $field];
+        } else {
+            if (!isset($this->options['field'])) {
+                $this->field('*');
+            }
+
+            $this->model->relationCount($this, (array) $relations, $aggregate, $field, true);
+        }
+
+        return $this;
+    }
+
+    /**
+     * 关联缓存
+     * @access public
+     * @param string|array|bool $relation 关联方法名
+     * @param mixed             $key    缓存key
+     * @param integer|\DateTime $expire 缓存有效期
+     * @param string            $tag    缓存标签
+     * @return $this
+     */
+    public function withCache($relation = true, $key = true, $expire = null, string $tag = null)
+    {
+        if (false === $relation || false === $key || !$this->getConnection()->getCache()) {
+            return $this;
+        }
+
+        if ($key instanceof \DateTimeInterface || $key instanceof \DateInterval || (is_int($key) && is_null($expire))) {
+            $expire = $key;
+            $key    = true;
+        }
+
+        if (true === $relation || is_numeric($relation)) {
+            $this->options['with_cache'] = $relation;
+            return $this;
+        }
+
+        $relations = (array) $relation;
+        foreach ($relations as $name => $relation) {
+            if (!is_numeric($name)) {
+                $this->options['with_cache'][$name] = is_array($relation) ? $relation : [$key, $relation, $tag];
+            } else {
+                $this->options['with_cache'][$relation] = [$key, $expire, $tag];
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * 关联统计
+     * @access public
+     * @param string|array $relation 关联方法名
+     * @param bool         $subQuery 是否使用子查询
+     * @return $this
+     */
+    public function withCount($relation, bool $subQuery = true)
+    {
+        return $this->withAggregate($relation, 'count', '*', $subQuery);
+    }
+
+    /**
+     * 关联统计Sum
+     * @access public
+     * @param string|array $relation 关联方法名
+     * @param string       $field    字段
+     * @param bool         $subQuery 是否使用子查询
+     * @return $this
+     */
+    public function withSum($relation, string $field, bool $subQuery = true)
+    {
+        return $this->withAggregate($relation, 'sum', $field, $subQuery);
+    }
+
+    /**
+     * 关联统计Max
+     * @access public
+     * @param string|array $relation 关联方法名
+     * @param string       $field    字段
+     * @param bool         $subQuery 是否使用子查询
+     * @return $this
+     */
+    public function withMax($relation, string $field, bool $subQuery = true)
+    {
+        return $this->withAggregate($relation, 'max', $field, $subQuery);
+    }
+
+    /**
+     * 关联统计Min
+     * @access public
+     * @param string|array $relation 关联方法名
+     * @param string       $field    字段
+     * @param bool         $subQuery 是否使用子查询
+     * @return $this
+     */
+    public function withMin($relation, string $field, bool $subQuery = true)
+    {
+        return $this->withAggregate($relation, 'min', $field, $subQuery);
+    }
+
+    /**
+     * 关联统计Avg
+     * @access public
+     * @param string|array $relation 关联方法名
+     * @param string       $field    字段
+     * @param bool         $subQuery 是否使用子查询
+     * @return $this
+     */
+    public function withAvg($relation, string $field, bool $subQuery = true)
+    {
+        return $this->withAggregate($relation, 'avg', $field, $subQuery);
+    }
+
+    /**
+     * 根据关联条件查询当前模型
+     * @access public
+     * @param  string  $relation 关联方法名
+     * @param  mixed   $operator 比较操作符
+     * @param  integer $count    个数
+     * @param  string  $id       关联表的统计字段
+     * @param  string  $joinType JOIN类型
+     * @return $this
+     */
+    public function has(string $relation, string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '')
+    {
+        return $this->model->has($relation, $operator, $count, $id, $joinType, $this);
+    }
+
+    /**
+     * 根据关联条件查询当前模型
+     * @access public
+     * @param  string $relation 关联方法名
+     * @param  mixed  $where    查询条件(数组或者闭包)
+     * @param  mixed  $fields   字段
+     * @param  string $joinType JOIN类型
+     * @return $this
+     */
+    public function hasWhere(string $relation, $where = [], string $fields = '*', string $joinType = '')
+    {
+        return $this->model->hasWhere($relation, $where, $fields, $joinType, $this);
+    }
+
+    /**
+     * 查询数据转换为模型数据集对象
+     * @access protected
+     * @param array $resultSet 数据集
+     * @return ModelCollection
+     */
+    protected function resultSetToModelCollection(array $resultSet): ModelCollection
+    {
+        if (empty($resultSet)) {
+            return $this->model->toCollection();
+        }
+
+        // 检查动态获取器
+        if (!empty($this->options['with_attr'])) {
+            foreach ($this->options['with_attr'] as $name => $val) {
+                if (strpos($name, '.')) {
+                    [$relation, $field] = explode('.', $name);
+
+                    $withRelationAttr[$relation][$field] = $val;
+                    unset($this->options['with_attr'][$name]);
+                }
+            }
+        }
+
+        $withRelationAttr = $withRelationAttr ?? [];
+
+        foreach ($resultSet as $key => &$result) {
+            // 数据转换为模型对象
+            $this->resultToModel($result, $this->options, true, $withRelationAttr);
+        }
+
+        if (!empty($this->options['with'])) {
+            // 预载入
+            $result->eagerlyResultSet($resultSet, $this->options['with'], $withRelationAttr, false, $this->options['with_cache'] ?? false);
+        }
+
+        if (!empty($this->options['with_join'])) {
+            // 预载入
+            $result->eagerlyResultSet($resultSet, $this->options['with_join'], $withRelationAttr, true, $this->options['with_cache'] ?? false);
+        }
+
+        // 模型数据集转换
+        return $this->model->toCollection($resultSet);
+    }
+
+    /**
+     * 查询数据转换为模型对象
+     * @access protected
+     * @param array $result           查询数据
+     * @param array $options          查询参数
+     * @param bool  $resultSet        是否为数据集查询
+     * @param array $withRelationAttr 关联字段获取器
+     * @return void
+     */
+    protected function resultToModel(array &$result, array $options = [], bool $resultSet = false, array $withRelationAttr = []): void
+    {
+        // 动态获取器
+        if (!empty($options['with_attr']) && empty($withRelationAttr)) {
+            foreach ($options['with_attr'] as $name => $val) {
+                if (strpos($name, '.')) {
+                    [$relation, $field] = explode('.', $name);
+
+                    $withRelationAttr[$relation][$field] = $val;
+                    unset($options['with_attr'][$name]);
+                }
+            }
+        }
+
+        // JSON 数据处理
+        if (!empty($options['json'])) {
+            $this->jsonResult($result, $options['json'], $options['json_assoc'], $withRelationAttr);
+        }
+
+        $result = $this->model
+            ->newInstance($result, $resultSet ? null : $this->getModelUpdateCondition($options));
+
+        // 动态获取器
+        if (!empty($options['with_attr'])) {
+            $result->withAttribute($options['with_attr']);
+        }
+
+        // 输出属性控制
+        if (!empty($options['visible'])) {
+            $result->visible($options['visible']);
+        } elseif (!empty($options['hidden'])) {
+            $result->hidden($options['hidden']);
+        }
+
+        if (!empty($options['append'])) {
+            $result->append($options['append']);
+        }
+
+        // 关联查询
+        if (!empty($options['relation'])) {
+            $result->relationQuery($options['relation'], $withRelationAttr);
+        }
+
+        // 预载入查询
+        if (!$resultSet && !empty($options['with'])) {
+            $result->eagerlyResult($result, $options['with'], $withRelationAttr, false, $options['with_cache'] ?? false);
+        }
+
+        // JOIN预载入查询
+        if (!$resultSet && !empty($options['with_join'])) {
+            $result->eagerlyResult($result, $options['with_join'], $withRelationAttr, true, $options['with_cache'] ?? false);
+        }
+
+        // 关联统计
+        if (!empty($options['with_count'])) {
+            foreach ($options['with_count'] as $val) {
+                $result->relationCount($this, (array) $val[0], $val[1], $val[2], false);
+            }
+        }
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/db/concern/ParamsBind.php b/vendor/topthink/think-orm/src/db/concern/ParamsBind.php
new file mode 100755
index 0000000..296e221
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/concern/ParamsBind.php
@@ -0,0 +1,106 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db\concern;
+
+use PDO;
+
+/**
+ * 参数绑定支持
+ */
+trait ParamsBind
+{
+    /**
+     * 当前参数绑定
+     * @var array
+     */
+    protected $bind = [];
+
+    /**
+     * 批量参数绑定
+     * @access public
+     * @param array $value 绑定变量值
+     * @return $this
+     */
+    public function bind(array $value)
+    {
+        $this->bind = array_merge($this->bind, $value);
+        return $this;
+    }
+
+    /**
+     * 单个参数绑定
+     * @access public
+     * @param mixed   $value 绑定变量值
+     * @param integer $type  绑定类型
+     * @param string  $name  绑定标识
+     * @return string
+     */
+    public function bindValue($value, int $type = null, string $name = null)
+    {
+        $name = $name ?: 'ThinkBind_' . (count($this->bind) + 1) . '_' . mt_rand() . '_';
+
+        $this->bind[$name] = [$value, $type ?: PDO::PARAM_STR];
+        return $name;
+    }
+
+    /**
+     * 检测参数是否已经绑定
+     * @access public
+     * @param string $key 参数名
+     * @return bool
+     */
+    public function isBind($key)
+    {
+        return isset($this->bind[$key]);
+    }
+
+    /**
+     * 参数绑定
+     * @access public
+     * @param string $sql  绑定的sql表达式
+     * @param array  $bind 参数绑定
+     * @return void
+     */
+    public function bindParams(string &$sql, array $bind = []): void
+    {
+        foreach ($bind as $key => $value) {
+            if (is_array($value)) {
+                $name = $this->bindValue($value[0], $value[1], $value[2] ?? null);
+            } else {
+                $name = $this->bindValue($value);
+            }
+
+            if (is_numeric($key)) {
+                $sql = substr_replace($sql, ':' . $name, strpos($sql, '?'), 1);
+            } else {
+                $sql = str_replace(':' . $key, ':' . $name, $sql);
+            }
+        }
+    }
+
+    /**
+     * 获取绑定的参数 并清空
+     * @access public
+     * @param bool $clear 是否清空绑定数据
+     * @return array
+     */
+    public function getBind(bool $clear = true): array
+    {
+        $bind = $this->bind;
+        if ($clear) {
+            $this->bind = [];
+        }
+
+        return $bind;
+    }
+}
diff --git a/vendor/topthink/think-orm/src/db/concern/ResultOperation.php b/vendor/topthink/think-orm/src/db/concern/ResultOperation.php
new file mode 100755
index 0000000..d93c409
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/concern/ResultOperation.php
@@ -0,0 +1,247 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db\concern;
+
+use Closure;
+use think\Collection;
+use think\db\exception\DataNotFoundException;
+use think\db\exception\DbException;
+use think\db\exception\ModelNotFoundException;
+use think\db\Query;
+use think\helper\Str;
+use think\Model;
+
+/**
+ * 查询数据处理
+ */
+trait ResultOperation
+{
+    /**
+     * 是否允许返回空数据(或空模型)
+     * @access public
+     * @param bool $allowEmpty 是否允许为空
+     * @return $this
+     */
+    public function allowEmpty(bool $allowEmpty = true)
+    {
+        $this->options['allow_empty'] = $allowEmpty;
+        return $this;
+    }
+
+    /**
+     * 设置查询数据不存在是否抛出异常
+     * @access public
+     * @param bool $fail 数据不存在是否抛出异常
+     * @return $this
+     */
+    public function failException(bool $fail = true)
+    {
+        $this->options['fail'] = $fail;
+        return $this;
+    }
+
+    /**
+     * 处理数据
+     * @access protected
+     * @param array $result 查询数据
+     * @return void
+     */
+    protected function result(array &$result): void
+    {
+        if (!empty($this->options['json'])) {
+            $this->jsonResult($result, $this->options['json'], true);
+        }
+
+        if (!empty($this->options['with_attr'])) {
+            $this->getResultAttr($result, $this->options['with_attr']);
+        }
+
+        $this->filterResult($result);
+    }
+
+    /**
+     * 处理数据集
+     * @access public
+     * @param array $resultSet 数据集
+     * @return void
+     */
+    protected function resultSet(array &$resultSet): void
+    {
+        if (!empty($this->options['json'])) {
+            foreach ($resultSet as &$result) {
+                $this->jsonResult($result, $this->options['json'], true);
+            }
+        }
+
+        if (!empty($this->options['with_attr'])) {
+            foreach ($resultSet as &$result) {
+                $this->getResultAttr($result, $this->options['with_attr']);
+            }
+        }
+
+        if (!empty($this->options['visible']) || !empty($this->options['hidden'])) {
+            foreach ($resultSet as &$result) {
+                $this->filterResult($result);
+            }
+        }
+
+        // 返回Collection对象
+        $resultSet = new Collection($resultSet);
+    }
+
+    /**
+     * 处理数据的可见和隐藏
+     * @access protected
+     * @param array $result 查询数据
+     * @return void
+     */
+    protected function filterResult(&$result): void
+    {
+        $array = [];
+        if (!empty($this->options['visible'])) {
+            foreach ($this->options['visible'] as $key) {
+                $array[] = $key;
+            }
+            $result = array_intersect_key($result, array_flip($array));
+        } elseif (!empty($this->options['hidden'])) {
+            foreach ($this->options['hidden'] as $key) {
+                $array[] = $key;
+            }
+            $result = array_diff_key($result, array_flip($array));
+        }
+    }
+
+    /**
+     * 使用获取器处理数据
+     * @access protected
+     * @param array $result   查询数据
+     * @param array $withAttr 字段获取器
+     * @return void
+     */
+    protected function getResultAttr(array &$result, array $withAttr = []): void
+    {
+        foreach ($withAttr as $name => $closure) {
+            $name = Str::snake($name);
+
+            if (strpos($name, '.')) {
+                // 支持JSON字段 获取器定义
+                [$key, $field] = explode('.', $name);
+
+                if (isset($result[$key])) {
+                    $result[$key][$field] = $closure($result[$key][$field] ?? null, $result[$key]);
+                }
+            } else {
+                $result[$name] = $closure($result[$name] ?? null, $result);
+            }
+        }
+    }
+
+    /**
+     * 处理空数据
+     * @access protected
+     * @return array|Model|null
+     * @throws DbException
+     * @throws ModelNotFoundException
+     * @throws DataNotFoundException
+     */
+    protected function resultToEmpty()
+    {
+        if (!empty($this->options['fail'])) {
+            $this->throwNotFound();
+        } elseif (!empty($this->options['allow_empty'])) {
+            return !empty($this->model) ? $this->model->newInstance() : [];
+        }
+    }
+
+    /**
+     * 查找单条记录 不存在返回空数据(或者空模型)
+     * @access public
+     * @param mixed $data 数据
+     * @return array|Model
+     */
+    public function findOrEmpty($data = null)
+    {
+        return $this->allowEmpty(true)->find($data);
+    }
+
+    /**
+     * JSON字段数据转换
+     * @access protected
+     * @param array $result           查询数据
+     * @param array $json             JSON字段
+     * @param bool  $assoc            是否转换为数组
+     * @param array $withRelationAttr 关联获取器
+     * @return void
+     */
+    protected function jsonResult(array &$result, array $json = [], bool $assoc = false, array $withRelationAttr = []): void
+    {
+        foreach ($json as $name) {
+            if (!isset($result[$name])) {
+                continue;
+            }
+
+            $result[$name] = json_decode($result[$name], true);
+
+            if (isset($withRelationAttr[$name])) {
+                foreach ($withRelationAttr[$name] as $key => $closure) {
+                    $result[$name][$key] = $closure($result[$name][$key] ?? null, $result[$name]);
+                }
+            }
+
+            if (!$assoc) {
+                $result[$name] = (object) $result[$name];
+            }
+        }
+    }
+
+    /**
+     * 查询失败 抛出异常
+     * @access protected
+     * @return void
+     * @throws ModelNotFoundException
+     * @throws DataNotFoundException
+     */
+    protected function throwNotFound(): void
+    {
+        if (!empty($this->model)) {
+            $class = get_class($this->model);
+            throw new ModelNotFoundException('model data Not Found:' . $class, $class, $this->options);
+        }
+
+        $table = $this->getTable();
+        throw new DataNotFoundException('table data not Found:' . $table, $table, $this->options);
+    }
+
+    /**
+     * 查找多条记录 如果不存在则抛出异常
+     * @access public
+     * @param array|string|Query|Closure $data 数据
+     * @return array|Model
+     */
+    public function selectOrFail($data = null)
+    {
+        return $this->failException(true)->select($data);
+    }
+
+    /**
+     * 查找单条记录 如果不存在则抛出异常
+     * @access public
+     * @param array|string|Query|Closure $data 数据
+     * @return array|Model
+     */
+    public function findOrFail($data = null)
+    {
+        return $this->failException(true)->find($data);
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php b/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php
new file mode 100755
index 0000000..9070bef
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php
@@ -0,0 +1,99 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db\concern;
+
+/**
+ * 数据字段信息
+ */
+trait TableFieldInfo
+{
+
+    /**
+     * 获取数据表字段信息
+     * @access public
+     * @param string $tableName 数据表名
+     * @return array
+     */
+    public function getTableFields($tableName = ''): array
+    {
+        if ('' == $tableName) {
+            $tableName = $this->getTable();
+        }
+
+        return $this->connection->getTableFields($tableName);
+    }
+
+    /**
+     * 获取详细字段类型信息
+     * @access public
+     * @param string $tableName 数据表名称
+     * @return array
+     */
+    public function getFields(string $tableName = ''): array
+    {
+        return $this->connection->getFields($tableName ?: $this->getTable());
+    }
+
+    /**
+     * 获取字段类型信息
+     * @access public
+     * @return array
+     */
+    public function getFieldsType(): array
+    {
+        if (!empty($this->options['field_type'])) {
+            return $this->options['field_type'];
+        }
+
+        return $this->connection->getFieldsType($this->getTable());
+    }
+
+    /**
+     * 获取字段类型信息
+     * @access public
+     * @param string $field 字段名
+     * @return string|null
+     */
+    public function getFieldType(string $field)
+    {
+        $fieldType = $this->getFieldsType();
+
+        return $fieldType[$field] ?? null;
+    }
+
+    /**
+     * 获取字段类型信息
+     * @access public
+     * @return array
+     */
+    public function getFieldsBindType(): array
+    {
+        $fieldType = $this->getFieldsType();
+
+        return array_map([$this->connection, 'getFieldBindType'], $fieldType);
+    }
+
+    /**
+     * 获取字段类型信息
+     * @access public
+     * @param string $field 字段名
+     * @return int
+     */
+    public function getFieldBindType(string $field): int
+    {
+        $fieldType = $this->getFieldType($field);
+
+        return $this->connection->getFieldBindType($fieldType ?: '');
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php b/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php
new file mode 100755
index 0000000..1267e54
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php
@@ -0,0 +1,214 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db\concern;
+
+/**
+ * 时间查询支持
+ */
+trait TimeFieldQuery
+{
+    /**
+     * 日期查询表达式
+     * @var array
+     */
+    protected $timeRule = [
+        'today'      => ['today', 'tomorrow -1second'],
+        'yesterday'  => ['yesterday', 'today -1second'],
+        'week'       => ['this week 00:00:00', 'next week 00:00:00 -1second'],
+        'last week'  => ['last week 00:00:00', 'this week 00:00:00 -1second'],
+        'month'      => ['first Day of this month 00:00:00', 'first Day of next month 00:00:00 -1second'],
+        'last month' => ['first Day of last month 00:00:00', 'first Day of this month 00:00:00 -1second'],
+        'year'       => ['this year 1/1', 'next year 1/1 -1second'],
+        'last year'  => ['last year 1/1', 'this year 1/1 -1second'],
+    ];
+
+    /**
+     * 添加日期或者时间查询规则
+     * @access public
+     * @param array $rule 时间表达式
+     * @return $this
+     */
+    public function timeRule(array $rule)
+    {
+        $this->timeRule = array_merge($this->timeRule, $rule);
+        return $this;
+    }
+
+    /**
+     * 查询日期或者时间
+     * @access public
+     * @param string       $field 日期字段名
+     * @param string       $op    比较运算符或者表达式
+     * @param string|array $range 比较范围
+     * @param string       $logic AND OR
+     * @return $this
+     */
+    public function whereTime(string $field, string $op, $range = null, string $logic = 'AND')
+    {
+        if (is_null($range)) {
+            if (isset($this->timeRule[$op])) {
+                $range = $this->timeRule[$op];
+            } else {
+                $range = $op;
+            }
+            $op = is_array($range) ? 'between' : '>=';
+        }
+
+        return $this->parseWhereExp($logic, $field, strtolower($op) . ' time', $range, [], true);
+    }
+
+    /**
+     * 查询某个时间间隔数据
+     * @access public
+     * @param string $field    日期字段名
+     * @param string $start    开始时间
+     * @param string $interval 时间间隔单位 day/month/year/week/hour/minute/second
+     * @param int    $step     间隔
+     * @param string $logic    AND OR
+     * @return $this
+     */
+    public function whereTimeInterval(string $field, string $start, string $interval = 'day', int $step = 1, string $logic = 'AND')
+    {
+        $startTime = strtotime($start);
+        $endTime   = strtotime(($step > 0 ? '+' : '-') . abs($step) . ' ' . $interval . (abs($step) > 1 ? 's' : ''), $startTime);
+
+        return $this->whereTime($field, 'between', $step > 0 ? [$startTime, $endTime - 1] : [$endTime, $startTime - 1], $logic);
+    }
+
+    /**
+     * 查询月数据 whereMonth('time_field', '2018-1')
+     * @access public
+     * @param string $field 日期字段名
+     * @param string $month 月份信息
+     * @param int    $step  间隔
+     * @param string $logic AND OR
+     * @return $this
+     */
+    public function whereMonth(string $field, string $month = 'this month', int $step = 1, string $logic = 'AND')
+    {
+        if (in_array($month, ['this month', 'last month'])) {
+            $month = date('Y-m', strtotime($month));
+        }
+
+        return $this->whereTimeInterval($field, $month, 'month', $step, $logic);
+    }
+
+    /**
+     * 查询周数据 whereWeek('time_field', '2018-1-1') 从2018-1-1开始的一周数据
+     * @access public
+     * @param string $field 日期字段名
+     * @param string $week  周信息
+     * @param int    $step  间隔
+     * @param string $logic AND OR
+     * @return $this
+     */
+    public function whereWeek(string $field, string $week = 'this week', int $step = 1, string $logic = 'AND')
+    {
+        if (in_array($week, ['this week', 'last week'])) {
+            $week = date('Y-m-d', strtotime($week));
+        }
+
+        return $this->whereTimeInterval($field, $week, 'week', $step, $logic);
+    }
+
+    /**
+     * 查询年数据 whereYear('time_field', '2018')
+     * @access public
+     * @param string $field 日期字段名
+     * @param string $year  年份信息
+     * @param int    $step     间隔
+     * @param string $logic AND OR
+     * @return $this
+     */
+    public function whereYear(string $field, string $year = 'this year', int $step = 1, string $logic = 'AND')
+    {
+        if (in_array($year, ['this year', 'last year'])) {
+            $year = date('Y', strtotime($year));
+        }
+
+        return $this->whereTimeInterval($field, $year . '-1-1', 'year', $step, $logic);
+    }
+
+    /**
+     * 查询日数据 whereDay('time_field', '2018-1-1')
+     * @access public
+     * @param string $field 日期字段名
+     * @param string $day   日期信息
+     * @param int    $step     间隔
+     * @param string $logic AND OR
+     * @return $this
+     */
+    public function whereDay(string $field, string $day = 'today', int $step = 1, string $logic = 'AND')
+    {
+        if (in_array($day, ['today', 'yesterday'])) {
+            $day = date('Y-m-d', strtotime($day));
+        }
+
+        return $this->whereTimeInterval($field, $day, 'day', $step, $logic);
+    }
+
+    /**
+     * 查询日期或者时间范围 whereBetweenTime('time_field', '2018-1-1','2018-1-15')
+     * @access public
+     * @param string     $field     日期字段名
+     * @param string|int $startTime 开始时间
+     * @param string|int $endTime   结束时间
+     * @param string     $logic     AND OR
+     * @return $this
+     */
+    public function whereBetweenTime(string $field, $startTime, $endTime, string $logic = 'AND')
+    {
+        return $this->whereTime($field, 'between', [$startTime, $endTime], $logic);
+    }
+
+    /**
+     * 查询日期或者时间范围 whereNotBetweenTime('time_field', '2018-1-1','2018-1-15')
+     * @access public
+     * @param string     $field     日期字段名
+     * @param string|int $startTime 开始时间
+     * @param string|int $endTime   结束时间
+     * @return $this
+     */
+    public function whereNotBetweenTime(string $field, $startTime, $endTime)
+    {
+        return $this->whereTime($field, '<', $startTime)
+            ->whereTime($field, '>', $endTime);
+    }
+
+    /**
+     * 查询当前时间在两个时间字段范围 whereBetweenTimeField('start_time', 'end_time')
+     * @access public
+     * @param string $startField 开始时间字段
+     * @param string $endField   结束时间字段
+     * @return $this
+     */
+    public function whereBetweenTimeField(string $startField, string $endField)
+    {
+        return $this->whereTime($startField, '<=', time())
+            ->whereTime($endField, '>=', time());
+    }
+
+    /**
+     * 查询当前时间不在两个时间字段范围 whereNotBetweenTimeField('start_time', 'end_time')
+     * @access public
+     * @param string $startField 开始时间字段
+     * @param string $endField   结束时间字段
+     * @return $this
+     */
+    public function whereNotBetweenTimeField(string $startField, string $endField)
+    {
+        return $this->whereTime($startField, '>', time())
+            ->whereTime($endField, '<', time(), 'OR');
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/db/concern/Transaction.php b/vendor/topthink/think-orm/src/db/concern/Transaction.php
new file mode 100755
index 0000000..f804ae2
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/concern/Transaction.php
@@ -0,0 +1,117 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db\concern;
+
+use think\db\BaseQuery;
+
+/**
+ * 事务支持
+ */
+trait Transaction
+{
+
+    /**
+     * 执行数据库Xa事务
+     * @access public
+     * @param  callable $callback 数据操作方法回调
+     * @param  array    $dbs      多个查询对象或者连接对象
+     * @return mixed
+     * @throws PDOException
+     * @throws \Exception
+     * @throws \Throwable
+     */
+    public function transactionXa($callback, array $dbs = [])
+    {
+        $xid = uniqid('xa');
+
+        if (empty($dbs)) {
+            $dbs[] = $this->getConnection();
+        }
+
+        foreach ($dbs as $key => $db) {
+            if ($db instanceof BaseQuery) {
+                $db = $db->getConnection();
+
+                $dbs[$key] = $db;
+            }
+
+            $db->startTransXa($xid);
+        }
+
+        try {
+            $result = null;
+            if (is_callable($callback)) {
+                $result = call_user_func_array($callback, [$this]);
+            }
+
+            foreach ($dbs as $db) {
+                $db->prepareXa($xid);
+            }
+
+            foreach ($dbs as $db) {
+                $db->commitXa($xid);
+            }
+
+            return $result;
+        } catch (\Exception | \Throwable $e) {
+            foreach ($dbs as $db) {
+                $db->rollbackXa($xid);
+            }
+            throw $e;
+        }
+    }
+
+    /**
+     * 执行数据库事务
+     * @access public
+     * @param callable $callback 数据操作方法回调
+     * @return mixed
+     */
+    public function transaction(callable $callback)
+    {
+        return $this->connection->transaction($callback);
+    }
+
+    /**
+     * 启动事务
+     * @access public
+     * @return void
+     */
+    public function startTrans(): void
+    {
+        $this->connection->startTrans();
+    }
+
+    /**
+     * 用于非自动提交状态下面的查询提交
+     * @access public
+     * @return void
+     * @throws PDOException
+     */
+    public function commit(): void
+    {
+        $this->connection->commit();
+    }
+
+    /**
+     * 事务回滚
+     * @access public
+     * @return void
+     * @throws PDOException
+     */
+    public function rollback(): void
+    {
+        $this->connection->rollback();
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/db/concern/WhereQuery.php b/vendor/topthink/think-orm/src/db/concern/WhereQuery.php
new file mode 100755
index 0000000..62dc983
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/concern/WhereQuery.php
@@ -0,0 +1,532 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db\concern;
+
+use Closure;
+use think\db\BaseQuery;
+use think\db\Raw;
+
+trait WhereQuery
+{
+    /**
+     * 指定AND查询条件
+     * @access public
+     * @param mixed $field     查询字段
+     * @param mixed $op        查询表达式
+     * @param mixed $condition 查询条件
+     * @return $this
+     */
+    public function where($field, $op = null, $condition = null)
+    {
+        if ($field instanceof $this) {
+            $this->parseQueryWhere($field);
+            return $this;
+        } elseif (true === $field || 1 === $field) {
+            $this->options['where']['AND'][] = true;
+            return $this;
+        }
+
+        $param = func_get_args();
+        array_shift($param);
+        return $this->parseWhereExp('AND', $field, $op, $condition, $param);
+    }
+
+    /**
+     * 解析Query对象查询条件
+     * @access public
+     * @param BaseQuery $query 查询对象
+     * @return void
+     */
+    protected function parseQueryWhere(BaseQuery $query): void
+    {
+        $this->options['where'] = $query->getOptions('where');
+
+        if ($query->getOptions('via')) {
+            $via = $query->getOptions('via');
+            foreach ($this->options['where'] as $logic => &$where) {
+                foreach ($where as $key => &$val) {
+                    if (is_array($val) && !strpos($val[0], '.')) {
+                        $val[0] = $via . '.' . $val[0];
+                    }
+                }
+            }
+        }
+
+        $this->bind($query->getBind(false));
+    }
+
+    /**
+     * 指定OR查询条件
+     * @access public
+     * @param mixed $field     查询字段
+     * @param mixed $op        查询表达式
+     * @param mixed $condition 查询条件
+     * @return $this
+     */
+    public function whereOr($field, $op = null, $condition = null)
+    {
+        $param = func_get_args();
+        array_shift($param);
+        return $this->parseWhereExp('OR', $field, $op, $condition, $param);
+    }
+
+    /**
+     * 指定XOR查询条件
+     * @access public
+     * @param mixed $field     查询字段
+     * @param mixed $op        查询表达式
+     * @param mixed $condition 查询条件
+     * @return $this
+     */
+    public function whereXor($field, $op = null, $condition = null)
+    {
+        $param = func_get_args();
+        array_shift($param);
+        return $this->parseWhereExp('XOR', $field, $op, $condition, $param);
+    }
+
+    /**
+     * 指定Null查询条件
+     * @access public
+     * @param mixed  $field 查询字段
+     * @param string $logic 查询逻辑 and or xor
+     * @return $this
+     */
+    public function whereNull(string $field, string $logic = 'AND')
+    {
+        return $this->parseWhereExp($logic, $field, 'NULL', null, [], true);
+    }
+
+    /**
+     * 指定NotNull查询条件
+     * @access public
+     * @param mixed  $field 查询字段
+     * @param string $logic 查询逻辑 and or xor
+     * @return $this
+     */
+    public function whereNotNull(string $field, string $logic = 'AND')
+    {
+        return $this->parseWhereExp($logic, $field, 'NOTNULL', null, [], true);
+    }
+
+    /**
+     * 指定Exists查询条件
+     * @access public
+     * @param mixed  $condition 查询条件
+     * @param string $logic     查询逻辑 and or xor
+     * @return $this
+     */
+    public function whereExists($condition, string $logic = 'AND')
+    {
+        if (is_string($condition)) {
+            $condition = new Raw($condition);
+        }
+
+        $this->options['where'][strtoupper($logic)][] = ['', 'EXISTS', $condition];
+        return $this;
+    }
+
+    /**
+     * 指定NotExists查询条件
+     * @access public
+     * @param mixed  $condition 查询条件
+     * @param string $logic     查询逻辑 and or xor
+     * @return $this
+     */
+    public function whereNotExists($condition, string $logic = 'AND')
+    {
+        if (is_string($condition)) {
+            $condition = new Raw($condition);
+        }
+
+        $this->options['where'][strtoupper($logic)][] = ['', 'NOT EXISTS', $condition];
+        return $this;
+    }
+
+    /**
+     * 指定In查询条件
+     * @access public
+     * @param mixed  $field     查询字段
+     * @param mixed  $condition 查询条件
+     * @param string $logic     查询逻辑 and or xor
+     * @return $this
+     */
+    public function whereIn(string $field, $condition, string $logic = 'AND')
+    {
+        return $this->parseWhereExp($logic, $field, 'IN', $condition, [], true);
+    }
+
+    /**
+     * 指定NotIn查询条件
+     * @access public
+     * @param mixed  $field     查询字段
+     * @param mixed  $condition 查询条件
+     * @param string $logic     查询逻辑 and or xor
+     * @return $this
+     */
+    public function whereNotIn(string $field, $condition, string $logic = 'AND')
+    {
+        return $this->parseWhereExp($logic, $field, 'NOT IN', $condition, [], true);
+    }
+
+    /**
+     * 指定Like查询条件
+     * @access public
+     * @param mixed  $field     查询字段
+     * @param mixed  $condition 查询条件
+     * @param string $logic     查询逻辑 and or xor
+     * @return $this
+     */
+    public function whereLike(string $field, $condition, string $logic = 'AND')
+    {
+        return $this->parseWhereExp($logic, $field, 'LIKE', $condition, [], true);
+    }
+
+    /**
+     * 指定NotLike查询条件
+     * @access public
+     * @param mixed  $field     查询字段
+     * @param mixed  $condition 查询条件
+     * @param string $logic     查询逻辑 and or xor
+     * @return $this
+     */
+    public function whereNotLike(string $field, $condition, string $logic = 'AND')
+    {
+        return $this->parseWhereExp($logic, $field, 'NOT LIKE', $condition, [], true);
+    }
+
+    /**
+     * 指定Between查询条件
+     * @access public
+     * @param mixed  $field     查询字段
+     * @param mixed  $condition 查询条件
+     * @param string $logic     查询逻辑 and or xor
+     * @return $this
+     */
+    public function whereBetween(string $field, $condition, string $logic = 'AND')
+    {
+        return $this->parseWhereExp($logic, $field, 'BETWEEN', $condition, [], true);
+    }
+
+    /**
+     * 指定NotBetween查询条件
+     * @access public
+     * @param mixed  $field     查询字段
+     * @param mixed  $condition 查询条件
+     * @param string $logic     查询逻辑 and or xor
+     * @return $this
+     */
+    public function whereNotBetween(string $field, $condition, string $logic = 'AND')
+    {
+        return $this->parseWhereExp($logic, $field, 'NOT BETWEEN', $condition, [], true);
+    }
+
+    /**
+     * 指定FIND_IN_SET查询条件
+     * @access public
+     * @param mixed  $field     查询字段
+     * @param mixed  $condition 查询条件
+     * @param string $logic     查询逻辑 and or xor
+     * @return $this
+     */
+    public function whereFindInSet(string $field, $condition, string $logic = 'AND')
+    {
+        return $this->parseWhereExp($logic, $field, 'FIND IN SET', $condition, [], true);
+    }
+
+    /**
+     * 比较两个字段
+     * @access public
+     * @param string $field1   查询字段
+     * @param string $operator 比较操作符
+     * @param string $field2   比较字段
+     * @param string $logic    查询逻辑 and or xor
+     * @return $this
+     */
+    public function whereColumn(string $field1, string $operator, string $field2 = null, string $logic = 'AND')
+    {
+        if (is_null($field2)) {
+            $field2   = $operator;
+            $operator = '=';
+        }
+
+        return $this->parseWhereExp($logic, $field1, 'COLUMN', [$operator, $field2], [], true);
+    }
+
+    /**
+     * 设置软删除字段及条件
+     * @access public
+     * @param string $field     查询字段
+     * @param mixed  $condition 查询条件
+     * @return $this
+     */
+    public function useSoftDelete(string $field, $condition = null)
+    {
+        if ($field) {
+            $this->options['soft_delete'] = [$field, $condition];
+        }
+
+        return $this;
+    }
+
+    /**
+     * 指定Exp查询条件
+     * @access public
+     * @param mixed  $field 查询字段
+     * @param string $where 查询条件
+     * @param array  $bind  参数绑定
+     * @param string $logic 查询逻辑 and or xor
+     * @return $this
+     */
+    public function whereExp(string $field, string $where, array $bind = [], string $logic = 'AND')
+    {
+        $this->options['where'][$logic][] = [$field, 'EXP', new Raw($where, $bind)];
+
+        return $this;
+    }
+
+    /**
+     * 指定字段Raw查询
+     * @access public
+     * @param string $field     查询字段表达式
+     * @param mixed  $op        查询表达式
+     * @param string $condition 查询条件
+     * @param string $logic     查询逻辑 and or xor
+     * @return $this
+     */
+    public function whereFieldRaw(string $field, $op, $condition = null, string $logic = 'AND')
+    {
+        if (is_null($condition)) {
+            $condition = $op;
+            $op        = '=';
+        }
+
+        $this->options['where'][$logic][] = [new Raw($field), $op, $condition];
+        return $this;
+    }
+
+    /**
+     * 指定表达式查询条件
+     * @access public
+     * @param string $where 查询条件
+     * @param array  $bind  参数绑定
+     * @param string $logic 查询逻辑 and or xor
+     * @return $this
+     */
+    public function whereRaw(string $where, array $bind = [], string $logic = 'AND')
+    {
+        $this->options['where'][$logic][] = new Raw($where, $bind);
+
+        return $this;
+    }
+
+    /**
+     * 指定表达式查询条件 OR
+     * @access public
+     * @param string $where 查询条件
+     * @param array  $bind  参数绑定
+     * @return $this
+     */
+    public function whereOrRaw(string $where, array $bind = [])
+    {
+        return $this->whereRaw($where, $bind, 'OR');
+    }
+
+    /**
+     * 分析查询表达式
+     * @access protected
+     * @param string $logic     查询逻辑 and or xor
+     * @param mixed  $field     查询字段
+     * @param mixed  $op        查询表达式
+     * @param mixed  $condition 查询条件
+     * @param array  $param     查询参数
+     * @param bool   $strict    严格模式
+     * @return $this
+     */
+    protected function parseWhereExp(string $logic, $field, $op, $condition, array $param = [], bool $strict = false)
+    {
+        $logic = strtoupper($logic);
+
+        if (is_string($field) && !empty($this->options['via']) && false === strpos($field, '.')) {
+            $field = $this->options['via'] . '.' . $field;
+        }
+
+        if ($field instanceof Raw) {
+            return $this->whereRaw($field, is_array($op) ? $op : [], $logic);
+        } elseif ($strict) {
+            // 使用严格模式查询
+            if ('=' == $op) {
+                $where = $this->whereEq($field, $condition);
+            } else {
+                $where = [$field, $op, $condition, $logic];
+            }
+        } elseif (is_array($field)) {
+            // 解析数组批量查询
+            return $this->parseArrayWhereItems($field, $logic);
+        } elseif ($field instanceof Closure) {
+            $where = $field;
+        } elseif (is_string($field)) {
+            if (preg_match('/[,=\<\'\"\(\s]/', $field)) {
+                return $this->whereRaw($field, is_array($op) ? $op : [], $logic);
+            } elseif (is_string($op) && strtolower($op) == 'exp') {
+                $bind = isset($param[2]) && is_array($param[2]) ? $param[2] : [];
+                return $this->whereExp($field, $condition, $bind, $logic);
+            }
+
+            $where = $this->parseWhereItem($logic, $field, $op, $condition, $param);
+        }
+
+        if (!empty($where)) {
+            $this->options['where'][$logic][] = $where;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 分析查询表达式
+     * @access protected
+     * @param string $logic     查询逻辑 and or xor
+     * @param mixed  $field     查询字段
+     * @param mixed  $op        查询表达式
+     * @param mixed  $condition 查询条件
+     * @param array  $param     查询参数
+     * @return array
+     */
+    protected function parseWhereItem(string $logic, $field, $op, $condition, array $param = []): array
+    {
+        if (is_array($op)) {
+            // 同一字段多条件查询
+            array_unshift($param, $field);
+            $where = $param;
+        } elseif ($field && is_null($condition)) {
+            if (is_string($op) && in_array(strtoupper($op), ['NULL', 'NOTNULL', 'NOT NULL'], true)) {
+                // null查询
+                $where = [$field, $op, ''];
+            } elseif ('=' === $op || is_null($op)) {
+                $where = [$field, 'NULL', ''];
+            } elseif ('<>' === $op) {
+                $where = [$field, 'NOTNULL', ''];
+            } else {
+                // 字段相等查询
+                $where = $this->whereEq($field, $op);
+            }
+        } elseif (is_string($op) && in_array(strtoupper($op), ['EXISTS', 'NOT EXISTS', 'NOTEXISTS'], true)) {
+            $where = [$field, $op, is_string($condition) ? new Raw($condition) : $condition];
+        } else {
+            $where = $field ? [$field, $op, $condition, $param[2] ?? null] : [];
+        }
+
+        return $where;
+    }
+
+    /**
+     * 相等查询的主键处理
+     * @access protected
+     * @param string $field 字段名
+     * @param mixed  $value 字段值
+     * @return array
+     */
+    protected function whereEq(string $field, $value): array
+    {
+        if ($this->getPk() == $field) {
+            $this->options['key'] = $value;
+        }
+
+        return [$field, '=', $value];
+    }
+
+    /**
+     * 数组批量查询
+     * @access protected
+     * @param array  $field 批量查询
+     * @param string $logic 查询逻辑 and or xor
+     * @return $this
+     */
+    protected function parseArrayWhereItems(array $field, string $logic)
+    {
+        if (key($field) !== 0) {
+            $where = [];
+            foreach ($field as $key => $val) {
+                if ($val instanceof Raw) {
+                    $where[] = [$key, 'exp', $val];
+                } else {
+                    $where[] = is_null($val) ? [$key, 'NULL', ''] : [$key, is_array($val) ? 'IN' : '=', $val];
+                }
+            }
+        } else {
+            // 数组批量查询
+            $where = $field;
+        }
+
+        if (!empty($where)) {
+            $this->options['where'][$logic] = isset($this->options['where'][$logic]) ?
+            array_merge($this->options['where'][$logic], $where) : $where;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 去除某个查询条件
+     * @access public
+     * @param string $field 查询字段
+     * @param string $logic 查询逻辑 and or xor
+     * @return $this
+     */
+    public function removeWhereField(string $field, string $logic = 'AND')
+    {
+        $logic = strtoupper($logic);
+
+        if (isset($this->options['where'][$logic])) {
+            foreach ($this->options['where'][$logic] as $key => $val) {
+                if (is_array($val) && $val[0] == $field) {
+                    unset($this->options['where'][$logic][$key]);
+                }
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * 条件查询
+     * @access public
+     * @param mixed         $condition 满足条件(支持闭包)
+     * @param Closure|array $query     满足条件后执行的查询表达式(闭包或数组)
+     * @param Closure|array $otherwise 不满足条件后执行
+     * @return $this
+     */
+    public function when($condition, $query, $otherwise = null)
+    {
+        if ($condition instanceof Closure) {
+            $condition = $condition($this);
+        }
+
+        if ($condition) {
+            if ($query instanceof Closure) {
+                $query($this, $condition);
+            } elseif (is_array($query)) {
+                $this->where($query);
+            }
+        } elseif ($otherwise) {
+            if ($otherwise instanceof Closure) {
+                $otherwise($this, $condition);
+            } elseif (is_array($otherwise)) {
+                $this->where($otherwise);
+            }
+        }
+
+        return $this;
+    }
+}
diff --git a/vendor/topthink/think-orm/src/db/connector/Mongo.php b/vendor/topthink/think-orm/src/db/connector/Mongo.php
new file mode 100755
index 0000000..ec39939
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/connector/Mongo.php
@@ -0,0 +1,1167 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db\connector;
+
+use Closure;
+use MongoDB\BSON\ObjectID;
+use MongoDB\Driver\BulkWrite;
+use MongoDB\Driver\Command;
+use MongoDB\Driver\Cursor;
+use MongoDB\Driver\Exception\AuthenticationException;
+use MongoDB\Driver\Exception\BulkWriteException;
+use MongoDB\Driver\Exception\ConnectionException;
+use MongoDB\Driver\Exception\InvalidArgumentException;
+use MongoDB\Driver\Exception\RuntimeException;
+use MongoDB\Driver\Manager;
+use MongoDB\Driver\Query as MongoQuery;
+use MongoDB\Driver\ReadPreference;
+use MongoDB\Driver\WriteConcern;
+use think\db\BaseQuery;
+use think\db\builder\Mongo as Builder;
+use think\db\Connection;
+use think\db\exception\DbException as Exception;
+use think\db\Mongo as Query;
+
+/**
+ * Mongo数据库驱动
+ */
+class Mongo extends Connection
+{
+
+    // 查询数据类型
+    protected $dbName  = '';
+    protected $typeMap = 'array';
+    protected $mongo; // MongoDb Object
+    protected $cursor; // MongoCursor Object
+    protected $session_uuid; // sessions会话列表当前会话数组key 随机生成
+    protected $sessions = []; // 会话列表
+
+    /** @var Builder */
+    protected $builder;
+
+    // 数据库连接参数配置
+    protected $config = [
+        // 数据库类型
+        'type'            => '',
+        // 服务器地址
+        'hostname'        => '',
+        // 数据库名
+        'database'        => '',
+        // 是否是复制集
+        'is_replica_set'  => false,
+        // 用户名
+        'username'        => '',
+        // 密码
+        'password'        => '',
+        // 端口
+        'hostport'        => '',
+        // 连接dsn
+        'dsn'             => '',
+        // 数据库连接参数
+        'params'          => [],
+        // 数据库编码默认采用utf8
+        'charset'         => 'utf8',
+        // 主键名
+        'pk'              => '_id',
+        // 主键类型
+        'pk_type'         => 'ObjectID',
+        // 数据库表前缀
+        'prefix'          => '',
+        // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
+        'deploy'          => 0,
+        // 数据库读写是否分离 主从式有效
+        'rw_separate'     => false,
+        // 读写分离后 主服务器数量
+        'master_num'      => 1,
+        // 指定从服务器序号
+        'slave_no'        => '',
+        // 是否严格检查字段是否存在
+        'fields_strict'   => true,
+        // 开启字段缓存
+        'fields_cache'    => false,
+        // 监听SQL
+        'trigger_sql'     => true,
+        // 自动写入时间戳字段
+        'auto_timestamp'  => false,
+        // 时间字段取出后的默认时间格式
+        'datetime_format' => 'Y-m-d H:i:s',
+        // 是否_id转换为id
+        'pk_convert_id'   => false,
+        // typeMap
+        'type_map'        => ['root' => 'array', 'document' => 'array'],
+    ];
+
+    /**
+     * 获取当前连接器类对应的Query类
+     * @access public
+     * @return string
+     */
+    public function getQueryClass(): string
+    {
+        return Query::class;
+    }
+
+    /**
+     * 获取当前的builder实例对象
+     * @access public
+     * @return Builder
+     */
+    public function getBuilder()
+    {
+        return $this->builder;
+    }
+
+    /**
+     * 获取当前连接器类对应的Builder类
+     * @access public
+     * @return string
+     */
+    public function getBuilderClass(): string
+    {
+        return Builder::class;
+    }
+
+    /**
+     * 连接数据库方法
+     * @access public
+     * @param  array   $config 连接参数
+     * @param  integer $linkNum 连接序号
+     * @return Manager
+     * @throws InvalidArgumentException
+     * @throws RuntimeException
+     */
+    public function connect(array $config = [], $linkNum = 0)
+    {
+        if (!isset($this->links[$linkNum])) {
+            if (empty($config)) {
+                $config = $this->config;
+            } else {
+                $config = array_merge($this->config, $config);
+            }
+
+            $this->dbName  = $config['database'];
+            $this->typeMap = $config['type_map'];
+
+            if ($config['pk_convert_id'] && '_id' == $config['pk']) {
+                $this->config['pk'] = 'id';
+            }
+
+            if (empty($config['dsn'])) {
+                $config['dsn'] = 'mongodb://' . ($config['username'] ? "{$config['username']}" : '') . ($config['password'] ? ":{$config['password']}@" : '') . $config['hostname'] . ($config['hostport'] ? ":{$config['hostport']}" : '');
+            }
+
+            $startTime = microtime(true);
+
+            $this->links[$linkNum] = new Manager($config['dsn'], $config['params']);
+
+            if (!empty($config['trigger_sql'])) {
+                // 记录数据库连接信息
+                $this->trigger('CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn']);
+            }
+
+        }
+
+        return $this->links[$linkNum];
+    }
+
+    /**
+     * 获取Mongo Manager对象
+     * @access public
+     * @return Manager|null
+     */
+    public function getMongo()
+    {
+        return $this->mongo ?: null;
+    }
+
+    /**
+     * 设置/获取当前操作的database
+     * @access public
+     * @param string $db db
+     * @return string
+     */
+    public function db(string $db = null)
+    {
+        if (is_null($db)) {
+            return $this->dbName;
+        } else {
+            $this->dbName = $db;
+        }
+    }
+
+    /**
+     * 执行查询但只返回Cursor对象
+     * @access public
+     * @param Query $query 查询对象
+     * @return Cursor
+     */
+    public function cursor($query)
+    {
+        // 分析查询表达式
+        $options = $query->parseOptions();
+
+        // 生成MongoQuery对象
+        $mongoQuery = $this->builder->select($query);
+
+        $master = $query->getOptions('master') ? true : false;
+
+        // 执行查询操作
+        return $this->getCursor($query, $mongoQuery, $master);
+    }
+
+    /**
+     * 执行查询并返回Cursor对象
+     * @access public
+     * @param BaseQuery          $query 查询对象
+     * @param MongoQuery|Closure $mongoQuery Mongo查询对象
+     * @param bool               $master 是否主库操作
+     * @return Cursor
+     * @throws AuthenticationException
+     * @throws InvalidArgumentException
+     * @throws ConnectionException
+     * @throws RuntimeException
+     */
+    public function getCursor(BaseQuery $query, $mongoQuery, bool $master = false): Cursor
+    {
+        $this->initConnect($master);
+        $this->db->updateQueryTimes();
+
+        $options   = $query->getOptions();
+        $namespace = $options['table'];
+
+        if (false === strpos($namespace, '.')) {
+            $namespace = $this->dbName . '.' . $namespace;
+        }
+
+        if (!empty($this->queryStr)) {
+            // 记录执行指令
+            $this->queryStr = 'db' . strstr($namespace, '.') . '.' . $this->queryStr;
+        }
+
+        if ($mongoQuery instanceof Closure) {
+            $mongoQuery = $mongoQuery($query);
+        }
+
+        $readPreference       = $options['readPreference'] ?? null;
+        $this->queryStartTime = microtime(true);
+
+        if ($session = $this->getSession()) {
+            $this->cursor = $this->mongo->executeQuery($namespace, $query, [
+                'readPreference' => is_null($readPreference) ? new ReadPreference(ReadPreference::RP_PRIMARY) : $readPreference,
+                'session'        => $session,
+            ]);
+        } else {
+            $this->cursor = $this->mongo->executeQuery($namespace, $mongoQuery, $readPreference);
+        }
+
+        // SQL监控
+        if (!empty($this->config['trigger_sql'])) {
+            $this->trigger('', $master);
+        }
+
+        return $this->cursor;
+    }
+
+    /**
+     * 执行查询 返回数据集
+     * @access public
+     * @param  MongoQuery $query 查询对象
+     * @return mixed
+     * @throws AuthenticationException
+     * @throws InvalidArgumentException
+     * @throws ConnectionException
+     * @throws RuntimeException
+     */
+    public function query(MongoQuery $query)
+    {
+        return $this->mongoQuery($this->newQuery(), $query);
+    }
+
+    /**
+     * 执行语句
+     * @access public
+     * @param  BulkWrite $bulk
+     * @return int
+     * @throws AuthenticationException
+     * @throws InvalidArgumentException
+     * @throws ConnectionException
+     * @throws RuntimeException
+     * @throws BulkWriteException
+     */
+    public function execute(BulkWrite $bulk)
+    {
+        return $this->mongoExecute($this->newQuery(), $bulk);
+    }
+
+    /**
+     * 执行查询
+     * @access protected
+     * @param BaseQuery          $query 查询对象
+     * @param MongoQuery|Closure $mongoQuery Mongo查询对象
+     * @return array
+     * @throws AuthenticationException
+     * @throws InvalidArgumentException
+     * @throws ConnectionException
+     * @throws RuntimeException
+     */
+    protected function mongoQuery(BaseQuery $query, $mongoQuery): array
+    {
+        $options = $query->parseOptions();
+
+        if ($query->getOptions('cache')) {
+            // 检查查询缓存
+            $cacheItem = $this->parseCache($query, $query->getOptions('cache'));
+            $key       = $cacheItem->getKey();
+
+            if ($this->cache->has($key)) {
+                return $this->cache->get($key);
+            }
+        }
+
+        if ($mongoQuery instanceof Closure) {
+            $mongoQuery = $mongoQuery($query);
+        }
+
+        $master = $query->getOptions('master') ? true : false;
+        $this->getCursor($query, $mongoQuery, $master);
+
+        $resultSet = $this->getResult($options['typeMap']);
+
+        if (isset($cacheItem) && $resultSet) {
+            // 缓存数据集
+            $cacheItem->set($resultSet);
+            $this->cacheData($cacheItem);
+        }
+
+        return $resultSet;
+    }
+
+    /**
+     * 执行写操作
+     * @access protected
+     * @param BaseQuery $query
+     * @param BulkWrite $bulk
+     *
+     * @return WriteResult
+     * @throws AuthenticationException
+     * @throws InvalidArgumentException
+     * @throws ConnectionException
+     * @throws RuntimeException
+     * @throws BulkWriteException
+     */
+    protected function mongoExecute(BaseQuery $query, BulkWrite $bulk)
+    {
+        $this->initConnect(true);
+        $this->db->updateQueryTimes();
+
+        $options = $query->getOptions();
+
+        $namespace = $options['table'];
+        if (false === strpos($namespace, '.')) {
+            $namespace = $this->dbName . '.' . $namespace;
+        }
+
+        if (!empty($this->queryStr)) {
+            // 记录执行指令
+            $this->queryStr = 'db' . strstr($namespace, '.') . '.' . $this->queryStr;
+        }
+
+        $writeConcern         = $options['writeConcern'] ?? null;
+        $this->queryStartTime = microtime(true);
+
+        if ($session = $this->getSession()) {
+            $writeResult = $this->mongo->executeBulkWrite($namespace, $bulk, [
+                'session'      => $session,
+                'writeConcern' => is_null($writeConcern) ? new WriteConcern(1) : $writeConcern,
+            ]);
+        } else {
+            $writeResult = $this->mongo->executeBulkWrite($namespace, $bulk, $writeConcern);
+        }
+
+        // SQL监控
+        if (!empty($this->config['trigger_sql'])) {
+            $this->trigger();
+        }
+
+        $this->numRows = $writeResult->getMatchedCount();
+
+        if ($query->getOptions('cache')) {
+            // 清理缓存数据
+            $cacheItem = $this->parseCache($query, $query->getOptions('cache'));
+            $key       = $cacheItem->getKey();
+            $tag       = $cacheItem->getTag();
+
+            if (isset($key) && $this->cache->has($key)) {
+                $this->cache->delete($key);
+            } elseif (!empty($tag) && method_exists($this->cache, 'tag')) {
+                $this->cache->tag($tag)->clear();
+            }
+        }
+
+        return $writeResult;
+    }
+
+    /**
+     * 执行指令
+     * @access public
+     * @param  Command        $command 指令
+     * @param  string         $dbName 当前数据库名
+     * @param  ReadPreference $readPreference readPreference
+     * @param  string|array   $typeMap 指定返回的typeMap
+     * @param  bool           $master 是否主库操作
+     * @return array
+     * @throws AuthenticationException
+     * @throws InvalidArgumentException
+     * @throws ConnectionException
+     * @throws RuntimeException
+     */
+    public function command(Command $command, string $dbName = '', ReadPreference $readPreference = null, $typeMap = null, bool $master = false): array
+    {
+        $this->initConnect($master);
+        $this->db->updateQueryTimes();
+
+        $this->queryStartTime = microtime(true);
+
+        $dbName = $dbName ?: $this->dbName;
+
+        if (!empty($this->queryStr)) {
+            $this->queryStr = 'db.' . $this->queryStr;
+        }
+
+        if ($session = $this->getSession()) {
+            $this->cursor = $this->mongo->executeCommand($dbName, $command, [
+                'readPreference' => is_null($readPreference) ? new ReadPreference(ReadPreference::RP_PRIMARY) : $readPreference,
+                'session'        => $session,
+            ]);
+        } else {
+            $this->cursor = $this->mongo->executeCommand($dbName, $command, $readPreference);
+        }
+
+        // SQL监控
+        if (!empty($this->config['trigger_sql'])) {
+            $this->trigger('', $master);
+        }
+
+        return $this->getResult($typeMap);
+    }
+
+    /**
+     * 获得数据集
+     * @access protected
+     * @param  string|array $typeMap 指定返回的typeMap
+     * @return mixed
+     */
+    protected function getResult($typeMap = null): array
+    {
+        // 设置结果数据类型
+        if (is_null($typeMap)) {
+            $typeMap = $this->typeMap;
+        }
+
+        $typeMap = is_string($typeMap) ? ['root' => $typeMap] : $typeMap;
+
+        $this->cursor->setTypeMap($typeMap);
+
+        // 获取数据集
+        $result = $this->cursor->toArray();
+
+        if ($this->getConfig('pk_convert_id')) {
+            // 转换ObjectID 字段
+            foreach ($result as &$data) {
+                $this->convertObjectID($data);
+            }
+        }
+
+        $this->numRows = count($result);
+
+        return $result;
+    }
+
+    /**
+     * ObjectID处理
+     * @access protected
+     * @param  array $data 数据
+     * @return void
+     */
+    protected function convertObjectID(array &$data): void
+    {
+        if (isset($data['_id']) && is_object($data['_id'])) {
+            $data['id'] = $data['_id']->__toString();
+            unset($data['_id']);
+        }
+    }
+
+    /**
+     * 数据库日志记录(仅供参考)
+     * @access public
+     * @param  string $type 类型
+     * @param  mixed  $data 数据
+     * @param  array  $options 参数
+     * @return void
+     */
+    public function mongoLog(string $type, $data, array $options = [])
+    {
+        if (!$this->config['trigger_sql']) {
+            return;
+        }
+
+        if (is_array($data)) {
+            array_walk_recursive($data, function (&$value) {
+                if ($value instanceof ObjectID) {
+                    $value = $value->__toString();
+                }
+            });
+        }
+
+        switch (strtolower($type)) {
+            case 'aggregate':
+                $this->queryStr = 'runCommand(' . ($data ? json_encode($data) : '') . ');';
+                break;
+            case 'find':
+                $this->queryStr = $type . '(' . ($data ? json_encode($data) : '') . ')';
+
+                if (isset($options['sort'])) {
+                    $this->queryStr .= '.sort(' . json_encode($options['sort']) . ')';
+                }
+
+                if (isset($options['skip'])) {
+                    $this->queryStr .= '.skip(' . $options['skip'] . ')';
+                }
+
+                if (isset($options['limit'])) {
+                    $this->queryStr .= '.limit(' . $options['limit'] . ')';
+                }
+
+                $this->queryStr .= ';';
+                break;
+            case 'insert':
+            case 'remove':
+                $this->queryStr = $type . '(' . ($data ? json_encode($data) : '') . ');';
+                break;
+            case 'update':
+                $this->queryStr = $type . '(' . json_encode($options) . ',' . json_encode($data) . ');';
+                break;
+            case 'cmd':
+                $this->queryStr = $data . '(' . json_encode($options) . ');';
+                break;
+        }
+
+        $this->options = $options;
+    }
+
+    /**
+     * 获取最近执行的指令
+     * @access public
+     * @return string
+     */
+    public function getLastSql(): string
+    {
+        return $this->queryStr;
+    }
+
+    /**
+     * 关闭数据库
+     * @access public
+     */
+    public function close()
+    {
+        $this->mongo     = null;
+        $this->cursor    = null;
+        $this->linkRead  = null;
+        $this->linkWrite = null;
+        $this->links     = [];
+    }
+
+    /**
+     * 初始化数据库连接
+     * @access protected
+     * @param boolean $master 是否主服务器
+     * @return void
+     */
+    protected function initConnect(bool $master = true): void
+    {
+        if (!empty($this->config['deploy'])) {
+            // 采用分布式数据库
+            if ($master) {
+                if (!$this->linkWrite) {
+                    $this->linkWrite = $this->multiConnect(true);
+                }
+
+                $this->mongo = $this->linkWrite;
+            } else {
+                if (!$this->linkRead) {
+                    $this->linkRead = $this->multiConnect(false);
+                }
+
+                $this->mongo = $this->linkRead;
+            }
+        } elseif (!$this->mongo) {
+            // 默认单数据库
+            $this->mongo = $this->connect();
+        }
+    }
+
+    /**
+     * 连接分布式服务器
+     * @access protected
+     * @param  boolean $master 主服务器
+     * @return Manager
+     */
+    protected function multiConnect(bool $master = false): Manager
+    {
+        $config = [];
+        // 分布式数据库配置解析
+        foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn'] as $name) {
+            $config[$name] = is_string($this->config[$name]) ? explode(',', $this->config[$name]) : $this->config[$name];
+        }
+
+        // 主服务器序号
+        $m = floor(mt_rand(0, $this->config['master_num'] - 1));
+
+        if ($this->config['rw_separate']) {
+            // 主从式采用读写分离
+            if ($master) // 主服务器写入
+            {
+                if ($this->config['is_replica_set']) {
+                    return $this->replicaSetConnect();
+                } else {
+                    $r = $m;
+                }
+            } elseif (is_numeric($this->config['slave_no'])) {
+                // 指定服务器读
+                $r = $this->config['slave_no'];
+            } else {
+                // 读操作连接从服务器 每次随机连接的数据库
+                $r = floor(mt_rand($this->config['master_num'], count($config['hostname']) - 1));
+            }
+        } else {
+            // 读写操作不区分服务器 每次随机连接的数据库
+            $r = floor(mt_rand(0, count($config['hostname']) - 1));
+        }
+
+        $dbConfig = [];
+
+        foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn'] as $name) {
+            $dbConfig[$name] = $config[$name][$r] ?? $config[$name][0];
+        }
+
+        return $this->connect($dbConfig, $r);
+    }
+
+    /**
+     * 创建基于复制集的连接
+     * @return Manager
+     */
+    public function replicaSetConnect(): Manager
+    {
+        $this->dbName  = $this->config['database'];
+        $this->typeMap = $this->config['type_map'];
+
+        $startTime = microtime(true);
+
+        $this->config['params']['replicaSet'] = $this->config['database'];
+
+        $manager = new Manager($this->buildUrl(), $this->config['params']);
+
+        // 记录数据库连接信息
+        if (!empty($config['trigger_sql'])) {
+            $this->trigger('CONNECT:ReplicaSet[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $this->config['dsn']);
+        }
+
+        return $manager;
+    }
+
+    /**
+     * 根据配置信息 生成适用于连接复制集的 URL
+     * @return string
+     */
+    private function buildUrl(): string
+    {
+        $url = 'mongodb://' . ($this->config['username'] ? "{$this->config['username']}" : '') . ($this->config['password'] ? ":{$this->config['password']}@" : '');
+
+        $hostList = is_string($this->config['hostname']) ? explode(',', $this->config['hostname']) : $this->config['hostname'];
+        $portList = is_string($this->config['hostport']) ? explode(',', $this->config['hostport']) : $this->config['hostport'];
+
+        for ($i = 0; $i < count($hostList); $i++) {
+            $url = $url . $hostList[$i] . ':' . $portList[0] . ',';
+        }
+
+        return rtrim($url, ",") . '/';
+    }
+
+    /**
+     * 插入记录
+     * @access public
+     * @param  BaseQuery $query 查询对象
+     * @param  boolean   $getLastInsID 返回自增主键
+     * @return mixed
+     * @throws AuthenticationException
+     * @throws InvalidArgumentException
+     * @throws ConnectionException
+     * @throws RuntimeException
+     * @throws BulkWriteException
+     */
+    public function insert(BaseQuery $query, bool $getLastInsID = false)
+    {
+        // 分析查询表达式
+        $options = $query->parseOptions();
+
+        if (empty($options['data'])) {
+            throw new Exception('miss data to insert');
+        }
+
+        // 生成bulk对象
+        $bulk = $this->builder->insert($query);
+
+        $writeResult = $this->mongoExecute($query, $bulk);
+        $result      = $writeResult->getInsertedCount();
+
+        if ($result) {
+            $data      = $options['data'];
+            $lastInsId = $this->getLastInsID($query);
+
+            if ($lastInsId) {
+                $pk        = $query->getPk();
+                $data[$pk] = $lastInsId;
+            }
+
+            $query->setOption('data', $data);
+
+            $this->db->trigger('after_insert', $query);
+
+            if ($getLastInsID) {
+                return $lastInsId;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * 获取最近插入的ID
+     * @access public
+     * @param BaseQuery $query 查询对象
+     * @return mixed
+     */
+    public function getLastInsID(BaseQuery $query)
+    {
+        $id = $this->builder->getLastInsID();
+
+        if (is_array($id)) {
+            array_walk($id, function (&$item, $key) {
+                if ($item instanceof ObjectID) {
+                    $item = $item->__toString();
+                }
+            });
+        } elseif ($id instanceof ObjectID) {
+            $id = $id->__toString();
+        }
+
+        return $id;
+    }
+
+    /**
+     * 批量插入记录
+     * @access public
+     * @param  BaseQuery $query 查询对象
+     * @param  array     $dataSet 数据集
+     * @return integer
+     * @throws AuthenticationException
+     * @throws InvalidArgumentException
+     * @throws ConnectionException
+     * @throws RuntimeException
+     * @throws BulkWriteException
+     */
+    public function insertAll(BaseQuery $query, array $dataSet = []): int
+    {
+        // 分析查询表达式
+        $query->parseOptions();
+
+        if (!is_array(reset($dataSet))) {
+            return 0;
+        }
+
+        // 生成bulkWrite对象
+        $bulk = $this->builder->insertAll($query, $dataSet);
+
+        $writeResult = $this->mongoExecute($query, $bulk);
+
+        return $writeResult->getInsertedCount();
+    }
+
+    /**
+     * 更新记录
+     * @access public
+     * @param  BaseQuery $query 查询对象
+     * @return int
+     * @throws Exception
+     * @throws AuthenticationException
+     * @throws InvalidArgumentException
+     * @throws ConnectionException
+     * @throws RuntimeException
+     * @throws BulkWriteException
+     */
+    public function update(BaseQuery $query): int
+    {
+        $query->parseOptions();
+
+        // 生成bulkWrite对象
+        $bulk = $this->builder->update($query);
+
+        $writeResult = $this->mongoExecute($query, $bulk);
+
+        $result = $writeResult->getModifiedCount();
+
+        if ($result) {
+            $this->db->trigger('after_update', $query);
+        }
+
+        return $result;
+    }
+
+    /**
+     * 删除记录
+     * @access public
+     * @param  BaseQuery $query 查询对象
+     * @return int
+     * @throws Exception
+     * @throws AuthenticationException
+     * @throws InvalidArgumentException
+     * @throws ConnectionException
+     * @throws RuntimeException
+     * @throws BulkWriteException
+     */
+    public function delete(BaseQuery $query): int
+    {
+        // 分析查询表达式
+        $query->parseOptions();
+
+        // 生成bulkWrite对象
+        $bulk = $this->builder->delete($query);
+
+        // 执行操作
+        $writeResult = $this->mongoExecute($query, $bulk);
+
+        $result = $writeResult->getDeletedCount();
+
+        if ($result) {
+            $this->db->trigger('after_delete', $query);
+        }
+
+        return $result;
+    }
+
+    /**
+     * 查找记录
+     * @access public
+     * @param  BaseQuery $query 查询对象
+     * @return array
+     * @throws ModelNotFoundException
+     * @throws DataNotFoundException
+     * @throws AuthenticationException
+     * @throws InvalidArgumentException
+     * @throws ConnectionException
+     * @throws RuntimeException
+     */
+    public function select(BaseQuery $query): array
+    {
+        $resultSet = $this->db->trigger('before_select', $query);
+
+        if (!$resultSet) {
+            $resultSet = $this->mongoQuery($query, function ($query) {
+                return $this->builder->select($query);
+            });
+        }
+
+        return $resultSet;
+    }
+
+    /**
+     * 查找单条记录
+     * @access public
+     * @param  BaseQuery $query 查询对象
+     * @return array
+     * @throws ModelNotFoundException
+     * @throws DataNotFoundException
+     * @throws AuthenticationException
+     * @throws InvalidArgumentException
+     * @throws ConnectionException
+     * @throws RuntimeException
+     */
+    public function find(BaseQuery $query): array
+    {
+        // 事件回调
+        $result = $this->db->trigger('before_find', $query);
+
+        if (!$result) {
+            // 执行查询
+            $resultSet = $this->mongoQuery($query, function ($query) {
+                return $this->builder->select($query, true);
+            });
+
+            $result = $resultSet[0] ?? [];
+        }
+
+        return $result;
+    }
+
+    /**
+     * 得到某个字段的值
+     * @access public
+     * @param  string $field 字段名
+     * @param  mixed  $default 默认值
+     * @return mixed
+     */
+    public function value(BaseQuery $query, string $field, $default = null)
+    {
+        $options = $query->parseOptions();
+
+        if (isset($options['projection'])) {
+            $query->removeOption('projection');
+        }
+
+        $query->setOption('projection', (array) $field);
+
+        if (!empty($options['cache'])) {
+            $cacheItem = $this->parseCache($query, $options['cache']);
+            $key       = $cacheItem->getKey();
+
+            if ($this->cache->has($key)) {
+                return $this->cache->get($key);
+            }
+        }
+
+        $mongoQuery = $this->builder->select($query, true);
+
+        if (isset($options['projection'])) {
+            $query->setOption('projection', $options['projection']);
+        } else {
+            $query->removeOption('projection');
+        }
+
+        // 执行查询操作
+        $resultSet = $this->mongoQuery($query, $mongoQuery);
+
+        if (!empty($resultSet)) {
+            $data   = array_shift($resultSet);
+            $result = $data[$field];
+        } else {
+            $result = false;
+        }
+
+        if (isset($cacheItem) && false !== $result) {
+            // 缓存数据
+            $cacheItem->set($result);
+            $this->cacheData($cacheItem);
+        }
+
+        return false !== $result ? $result : $default;
+    }
+
+    /**
+     * 得到某个列的数组
+     * @access public
+     * @param  string $field 字段名 多个字段用逗号分隔
+     * @param  string $key 索引
+     * @return array
+     */
+    public function column(BaseQuery $query, string $field, string $key = ''): array
+    {
+        $options = $query->parseOptions();
+
+        if (isset($options['projection'])) {
+            $query->removeOption('projection');
+        }
+
+        if ($key && '*' != $field) {
+            $projection = $key . ',' . $field;
+        } else {
+            $projection = $field;
+        }
+
+        $query->field($projection);
+
+        if (!empty($options['cache'])) {
+            // 判断查询缓存
+            $cacheItem = $this->parseCache($query, $options['cache']);
+            $key       = $cacheItem->getKey();
+
+            if ($this->cache->has($key)) {
+                return $this->cache->get($key);
+            }
+        }
+
+        $mongoQuery = $this->builder->select($query);
+
+        if (isset($options['projection'])) {
+            $query->setOption('projection', $options['projection']);
+        } else {
+            $query->removeOption('projection');
+        }
+
+        // 执行查询操作
+        $resultSet = $this->mongoQuery($query, $mongoQuery);
+
+        if (('*' == $field || strpos($field, ',')) && $key) {
+            $result = array_column($resultSet, null, $key);
+        } elseif (!empty($resultSet)) {
+            $result = array_column($resultSet, $field, $key);
+        } else {
+            $result = [];
+        }
+
+        if (isset($cacheItem)) {
+            // 缓存数据
+            $cacheItem->set($result);
+            $this->cacheData($cacheItem);
+        }
+
+        return $result;
+    }
+
+    /**
+     * 执行command
+     * @access public
+     * @param  BaseQuery           $query      查询对象
+     * @param  string|array|object $command 指令
+     * @param  mixed               $extra 额外参数
+     * @param  string              $db 数据库名
+     * @return array
+     */
+    public function cmd(BaseQuery $query, $command, $extra = null, string $db = ''): array
+    {
+        if (is_array($command) || is_object($command)) {
+
+            $this->mongoLog('cmd', 'cmd', $command);
+
+            // 直接创建Command对象
+            $command = new Command($command);
+        } else {
+            // 调用Builder封装的Command对象
+            $command = $this->builder->$command($query, $extra);
+        }
+
+        return $this->command($command, $db);
+    }
+
+    /**
+     * 获取数据库字段
+     * @access public
+     * @param mixed $tableName 数据表名
+     * @return array
+     */
+    public function getTableFields($tableName): array
+    {
+        return [];
+    }
+
+    /**
+     * 执行数据库事务
+     * @access public
+     * @param  callable $callback 数据操作方法回调
+     * @return mixed
+     * @throws PDOException
+     * @throws \Exception
+     * @throws \Throwable
+     */
+    public function transaction(callable $callback)
+    {
+        $this->startTrans();
+        try {
+            $result = null;
+            if (is_callable($callback)) {
+                $result = call_user_func_array($callback, [$this]);
+            }
+            $this->commit();
+            return $result;
+        } catch (\Exception $e) {
+            $this->rollback();
+            throw $e;
+        } catch (\Throwable $e) {
+            $this->rollback();
+            throw $e;
+        }
+    }
+
+    /**
+     * 启动事务
+     * @access public
+     * @return void
+     * @throws \PDOException
+     * @throws \Exception
+     */
+    public function startTrans()
+    {
+        $this->initConnect(true);
+        $this->session_uuid                  = uniqid();
+        $this->sessions[$this->session_uuid] = $this->getMongo()->startSession();
+
+        $this->sessions[$this->session_uuid]->startTransaction([]);
+    }
+
+    /**
+     * 用于非自动提交状态下面的查询提交
+     * @access public
+     * @return void
+     * @throws PDOException
+     */
+    public function commit()
+    {
+        if ($session = $this->getSession()) {
+            $session->commitTransaction();
+            $this->setLastSession();
+        }
+    }
+
+    /**
+     * 事务回滚
+     * @access public
+     * @return void
+     * @throws PDOException
+     */
+    public function rollback()
+    {
+        if ($session = $this->getSession()) {
+            $session->abortTransaction();
+            $this->setLastSession();
+        }
+    }
+
+    /**
+     * 结束当前会话,设置上一个会话为当前会话
+     * @author klinson <klinson@163.com>
+     */
+    protected function setLastSession()
+    {
+        if ($session = $this->getSession()) {
+            $session->endSession();
+            unset($this->sessions[$this->session_uuid]);
+            if (empty($this->sessions)) {
+                $this->session_uuid = null;
+            } else {
+                end($this->sessions);
+                $this->session_uuid = key($this->sessions);
+            }
+        }
+    }
+
+    /**
+     * 获取当前会话
+     * @return \MongoDB\Driver\Session|null
+     * @author klinson <klinson@163.com>
+     */
+    public function getSession()
+    {
+        return ($this->session_uuid && isset($this->sessions[$this->session_uuid]))
+        ? $this->sessions[$this->session_uuid]
+        : null;
+    }
+}
diff --git a/vendor/topthink/think-orm/src/db/connector/Mysql.php b/vendor/topthink/think-orm/src/db/connector/Mysql.php
new file mode 100755
index 0000000..483b447
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/connector/Mysql.php
@@ -0,0 +1,162 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db\connector;
+
+use PDO;
+use think\db\PDOConnection;
+
+/**
+ * mysql数据库驱动
+ */
+class Mysql extends PDOConnection
+{
+
+    /**
+     * 解析pdo连接的dsn信息
+     * @access protected
+     * @param  array $config 连接信息
+     * @return string
+     */
+    protected function parseDsn(array $config): string
+    {
+        if (!empty($config['socket'])) {
+            $dsn = 'mysql:unix_socket=' . $config['socket'];
+        } elseif (!empty($config['hostport'])) {
+            $dsn = 'mysql:host=' . $config['hostname'] . ';port=' . $config['hostport'];
+        } else {
+            $dsn = 'mysql:host=' . $config['hostname'];
+        }
+        $dsn .= ';dbname=' . $config['database'];
+
+        if (!empty($config['charset'])) {
+            $dsn .= ';charset=' . $config['charset'];
+        }
+
+        return $dsn;
+    }
+
+    /**
+     * 取得数据表的字段信息
+     * @access public
+     * @param  string $tableName
+     * @return array
+     */
+    public function getFields(string $tableName): array
+    {
+        [$tableName] = explode(' ', $tableName);
+
+        if (false === strpos($tableName, '`')) {
+            if (strpos($tableName, '.')) {
+                $tableName = str_replace('.', '`.`', $tableName);
+            }
+            $tableName = '`' . $tableName . '`';
+        }
+
+        $sql    = 'SHOW FULL COLUMNS FROM ' . $tableName;
+        $pdo    = $this->getPDOStatement($sql);
+        $result = $pdo->fetchAll(PDO::FETCH_ASSOC);
+        $info   = [];
+
+        if (!empty($result)) {
+            foreach ($result as $key => $val) {
+                $val = array_change_key_case($val);
+
+                $info[$val['field']] = [
+                    'name'    => $val['field'],
+                    'type'    => $val['type'],
+                    'notnull' => 'NO' == $val['null'],
+                    'default' => $val['default'],
+                    'primary' => strtolower($val['key']) == 'pri',
+                    'autoinc' => strtolower($val['extra']) == 'auto_increment',
+                    'comment' => $val['comment'],
+                ];
+            }
+        }
+
+        return $this->fieldCase($info);
+    }
+
+    /**
+     * 取得数据库的表信息
+     * @access public
+     * @param  string $dbName
+     * @return array
+     */
+    public function getTables(string $dbName = ''): array
+    {
+        $sql    = !empty($dbName) ? 'SHOW TABLES FROM ' . $dbName : 'SHOW TABLES ';
+        $pdo    = $this->getPDOStatement($sql);
+        $result = $pdo->fetchAll(PDO::FETCH_ASSOC);
+        $info   = [];
+
+        foreach ($result as $key => $val) {
+            $info[$key] = current($val);
+        }
+
+        return $info;
+    }
+
+    protected function supportSavepoint(): bool
+    {
+        return true;
+    }
+
+    /**
+     * 启动XA事务
+     * @access public
+     * @param  string $xid XA事务id
+     * @return void
+     */
+    public function startTransXa(string $xid)
+    {
+        $this->initConnect(true);
+        $this->linkID->exec("XA START '$xid'");
+    }
+
+    /**
+     * 预编译XA事务
+     * @access public
+     * @param  string $xid XA事务id
+     * @return void
+     */
+    public function prepareXa(string $xid)
+    {
+        $this->initConnect(true);
+        $this->linkID->exec("XA END '$xid'");
+        $this->linkID->exec("XA PREPARE '$xid'");
+    }
+
+    /**
+     * 提交XA事务
+     * @access public
+     * @param  string $xid XA事务id
+     * @return void
+     */
+    public function commitXa(string $xid)
+    {
+        $this->initConnect(true);
+        $this->linkID->exec("XA COMMIT '$xid'");
+    }
+
+    /**
+     * 回滚XA事务
+     * @access public
+     * @param  string $xid XA事务id
+     * @return void
+     */
+    public function rollbackXa(string $xid)
+    {
+        $this->initConnect(true);
+        $this->linkID->exec("XA ROLLBACK '$xid'");
+    }
+}
diff --git a/vendor/topthink/think-orm/src/db/connector/Oracle.php b/vendor/topthink/think-orm/src/db/connector/Oracle.php
new file mode 100755
index 0000000..236d8bf
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/connector/Oracle.php
@@ -0,0 +1,117 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\db\connector;
+
+use PDO;
+use think\db\BaseQuery;
+use think\db\PDOConnection;
+
+/**
+ * Oracle数据库驱动
+ */
+class Oracle extends PDOConnection
+{
+    /**
+     * 解析pdo连接的dsn信息
+     * @access protected
+     * @param array $config 连接信息
+     * @return string
+     */
+    protected function parseDsn(array $config): string
+    {
+        $dsn = 'oci:dbname=';
+
+        if (!empty($config['hostname'])) {
+            //  Oracle Instant Client
+            $dsn .= '//' . $config['hostname'] . ($config['hostport'] ? ':' . $config['hostport'] : '') . '/';
+        }
+
+        $dsn .= $config['database'];
+
+        if (!empty($config['charset'])) {
+            $dsn .= ';charset=' . $config['charset'];
+        }
+
+        return $dsn;
+    }
+
+    /**
+     * 取得数据表的字段信息
+     * @access public
+     * @param string $tableName
+     * @return array
+     */
+    public function getFields(string $tableName): array
+    {
+        [$tableName] = explode(' ', $tableName);
+        $sql         = "select a.column_name,data_type,DECODE (nullable, 'Y', 0, 1) notnull,data_default, DECODE (A .column_name,b.column_name,1,0) pk from all_tab_columns a,(select column_name from all_constraints c, all_cons_columns col where c.constraint_name = col.constraint_name and c.constraint_type = 'P' and c.table_name = '" . strtoupper($tableName) . "' ) b where table_name = '" . strtoupper($tableName) . "' and a.column_name = b.column_name (+)";
+
+        $pdo    = $this->getPDOStatement($sql);
+        $result = $pdo->fetchAll(PDO::FETCH_ASSOC);
+        $info   = [];
+
+        if ($result) {
+            foreach ($result as $key => $val) {
+                $val = array_change_key_case($val);
+
+                $info[$val['column_name']] = [
+                    'name'    => $val['column_name'],
+                    'type'    => $val['data_type'],
+                    'notnull' => $val['notnull'],
+                    'default' => $val['data_default'],
+                    'primary' => $val['pk'],
+                    'autoinc' => $val['pk'],
+                ];
+            }
+        }
+
+        return $this->fieldCase($info);
+    }
+
+    /**
+     * 取得数据库的表信息(暂时实现取得用户表信息)
+     * @access   public
+     * @param string $dbName
+     * @return array
+     */
+    public function getTables(string $dbName = ''): array
+    {
+        $sql    = 'select table_name from all_tables';
+        $pdo    = $this->getPDOStatement($sql);
+        $result = $pdo->fetchAll(PDO::FETCH_ASSOC);
+        $info   = [];
+
+        foreach ($result as $key => $val) {
+            $info[$key] = current($val);
+        }
+
+        return $info;
+    }
+
+    /**
+     * 获取最近插入的ID
+     * @access public
+     * @param BaseQuery $query    查询对象
+     * @param string    $sequence 自增序列名
+     * @return mixed
+     */
+    public function getLastInsID(BaseQuery $query, string $sequence = null)
+    {
+        $pdo    = $this->linkID->query("select {$sequence}.currval as id from dual");
+        $result = $pdo->fetchColumn();
+
+        return $result;
+    }
+
+    protected function supportSavepoint(): bool
+    {
+        return true;
+    }
+}
diff --git a/vendor/topthink/think-orm/src/db/connector/Pgsql.php b/vendor/topthink/think-orm/src/db/connector/Pgsql.php
new file mode 100755
index 0000000..fec8f84
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/connector/Pgsql.php
@@ -0,0 +1,108 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\db\connector;
+
+use PDO;
+use think\db\PDOConnection;
+
+/**
+ * Pgsql数据库驱动
+ */
+class Pgsql extends PDOConnection
+{
+
+    /**
+     * 默认PDO连接参数
+     * @var array
+     */
+    protected $params = [
+        PDO::ATTR_CASE              => PDO::CASE_NATURAL,
+        PDO::ATTR_ERRMODE           => PDO::ERRMODE_EXCEPTION,
+        PDO::ATTR_ORACLE_NULLS      => PDO::NULL_NATURAL,
+        PDO::ATTR_STRINGIFY_FETCHES => false,
+    ];
+
+    /**
+     * 解析pdo连接的dsn信息
+     * @access protected
+     * @param  array $config 连接信息
+     * @return string
+     */
+    protected function parseDsn(array $config): string
+    {
+        $dsn = 'pgsql:dbname=' . $config['database'] . ';host=' . $config['hostname'];
+
+        if (!empty($config['hostport'])) {
+            $dsn .= ';port=' . $config['hostport'];
+        }
+
+        return $dsn;
+    }
+
+    /**
+     * 取得数据表的字段信息
+     * @access public
+     * @param  string $tableName
+     * @return array
+     */
+    public function getFields(string $tableName): array
+    {
+        [$tableName] = explode(' ', $tableName);
+        $sql         = 'select fields_name as "field",fields_type as "type",fields_not_null as "null",fields_key_name as "key",fields_default as "default",fields_default as "extra" from table_msg(\'' . $tableName . '\');';
+
+        $pdo    = $this->getPDOStatement($sql);
+        $result = $pdo->fetchAll(PDO::FETCH_ASSOC);
+        $info   = [];
+
+        if (!empty($result)) {
+            foreach ($result as $key => $val) {
+                $val = array_change_key_case($val);
+
+                $info[$val['field']] = [
+                    'name'    => $val['field'],
+                    'type'    => $val['type'],
+                    'notnull' => (bool) ('' !== $val['null']),
+                    'default' => $val['default'],
+                    'primary' => !empty($val['key']),
+                    'autoinc' => (0 === strpos($val['extra'], 'nextval(')),
+                ];
+            }
+        }
+
+        return $this->fieldCase($info);
+    }
+
+    /**
+     * 取得数据库的表信息
+     * @access public
+     * @param  string $dbName
+     * @return array
+     */
+    public function getTables(string $dbName = ''): array
+    {
+        $sql    = "select tablename as Tables_in_test from pg_tables where  schemaname ='public'";
+        $pdo    = $this->getPDOStatement($sql);
+        $result = $pdo->fetchAll(PDO::FETCH_ASSOC);
+        $info   = [];
+
+        foreach ($result as $key => $val) {
+            $info[$key] = current($val);
+        }
+
+        return $info;
+    }
+
+    protected function supportSavepoint(): bool
+    {
+        return true;
+    }
+}
diff --git a/vendor/topthink/think-orm/src/db/connector/Sqlite.php b/vendor/topthink/think-orm/src/db/connector/Sqlite.php
new file mode 100755
index 0000000..c664f20
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/connector/Sqlite.php
@@ -0,0 +1,96 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\db\connector;
+
+use PDO;
+use think\db\PDOConnection;
+
+/**
+ * Sqlite数据库驱动
+ */
+class Sqlite extends PDOConnection
+{
+
+    /**
+     * 解析pdo连接的dsn信息
+     * @access protected
+     * @param  array $config 连接信息
+     * @return string
+     */
+    protected function parseDsn(array $config): string
+    {
+        $dsn = 'sqlite:' . $config['database'];
+
+        return $dsn;
+    }
+
+    /**
+     * 取得数据表的字段信息
+     * @access public
+     * @param  string $tableName
+     * @return array
+     */
+    public function getFields(string $tableName): array
+    {
+        [$tableName] = explode(' ', $tableName);
+        $sql         = 'PRAGMA table_info( ' . $tableName . ' )';
+
+        $pdo    = $this->getPDOStatement($sql);
+        $result = $pdo->fetchAll(PDO::FETCH_ASSOC);
+        $info   = [];
+
+        if (!empty($result)) {
+            foreach ($result as $key => $val) {
+                $val = array_change_key_case($val);
+
+                $info[$val['name']] = [
+                    'name'    => $val['name'],
+                    'type'    => $val['type'],
+                    'notnull' => 1 === $val['notnull'],
+                    'default' => $val['dflt_value'],
+                    'primary' => '1' == $val['pk'],
+                    'autoinc' => '1' == $val['pk'],
+                ];
+            }
+        }
+
+        return $this->fieldCase($info);
+    }
+
+    /**
+     * 取得数据库的表信息
+     * @access public
+     * @param  string $dbName
+     * @return array
+     */
+    public function getTables(string $dbName = ''): array
+    {
+        $sql = "SELECT name FROM sqlite_master WHERE type='table' "
+            . "UNION ALL SELECT name FROM sqlite_temp_master "
+            . "WHERE type='table' ORDER BY name";
+
+        $pdo    = $this->getPDOStatement($sql);
+        $result = $pdo->fetchAll(PDO::FETCH_ASSOC);
+        $info   = [];
+
+        foreach ($result as $key => $val) {
+            $info[$key] = current($val);
+        }
+
+        return $info;
+    }
+
+    protected function supportSavepoint(): bool
+    {
+        return true;
+    }
+}
diff --git a/vendor/topthink/think-orm/src/db/connector/Sqlsrv.php b/vendor/topthink/think-orm/src/db/connector/Sqlsrv.php
new file mode 100755
index 0000000..63b2df0
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/connector/Sqlsrv.php
@@ -0,0 +1,122 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2012 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\db\connector;
+
+use PDO;
+use think\db\PDOConnection;
+
+/**
+ * Sqlsrv数据库驱动
+ */
+class Sqlsrv extends PDOConnection
+{
+    /**
+     * 默认PDO连接参数
+     * @var array
+     */
+    protected $params = [
+        PDO::ATTR_CASE              => PDO::CASE_NATURAL,
+        PDO::ATTR_ERRMODE           => PDO::ERRMODE_EXCEPTION,
+        PDO::ATTR_ORACLE_NULLS      => PDO::NULL_NATURAL,
+        PDO::ATTR_STRINGIFY_FETCHES => false,
+    ];
+
+    /**
+     * 解析pdo连接的dsn信息
+     * @access protected
+     * @param  array $config 连接信息
+     * @return string
+     */
+    protected function parseDsn(array $config): string
+    {
+        $dsn = 'sqlsrv:Database=' . $config['database'] . ';Server=' . $config['hostname'];
+
+        if (!empty($config['hostport'])) {
+            $dsn .= ',' . $config['hostport'];
+        }
+
+        return $dsn;
+    }
+
+    /**
+     * 取得数据表的字段信息
+     * @access public
+     * @param  string $tableName
+     * @return array
+     */
+    public function getFields(string $tableName): array
+    {
+        [$tableName] = explode(' ', $tableName);
+
+        $sql = "SELECT   column_name,   data_type,   column_default,   is_nullable
+        FROM    information_schema.tables AS t
+        JOIN    information_schema.columns AS c
+        ON  t.table_catalog = c.table_catalog
+        AND t.table_schema  = c.table_schema
+        AND t.table_name    = c.table_name
+        WHERE   t.table_name = '$tableName'";
+
+        $pdo    = $this->getPDOStatement($sql);
+        $result = $pdo->fetchAll(PDO::FETCH_ASSOC);
+        $info   = [];
+
+        if (!empty($result)) {
+            foreach ($result as $key => $val) {
+                $val = array_change_key_case($val);
+
+                $info[$val['column_name']] = [
+                    'name'    => $val['column_name'],
+                    'type'    => $val['data_type'],
+                    'notnull' => (bool) ('' === $val['is_nullable']), // not null is empty, null is yes
+                    'default' => $val['column_default'],
+                    'primary' => false,
+                    'autoinc' => false,
+                ];
+            }
+        }
+
+        $sql    = "SELECT column_name FROM information_schema.key_column_usage WHERE table_name='$tableName'";
+        $pdo    = $this->linkID->query($sql);
+        $result = $pdo->fetch(PDO::FETCH_ASSOC);
+
+        if ($result) {
+            $info[$result['column_name']]['primary'] = true;
+        }
+
+        return $this->fieldCase($info);
+    }
+
+    /**
+     * 取得数据表的字段信息
+     * @access public
+     * @param  string $dbName
+     * @return array
+     */
+    public function getTables(string $dbName = ''): array
+    {
+        $sql = "SELECT TABLE_NAME
+            FROM INFORMATION_SCHEMA.TABLES
+            WHERE TABLE_TYPE = 'BASE TABLE'
+            ";
+
+        $pdo    = $this->getPDOStatement($sql);
+        $result = $pdo->fetchAll(PDO::FETCH_ASSOC);
+        $info   = [];
+
+        foreach ($result as $key => $val) {
+            $info[$key] = current($val);
+        }
+
+        return $info;
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/db/connector/pgsql.sql b/vendor/topthink/think-orm/src/db/connector/pgsql.sql
new file mode 100755
index 0000000..e1a09a3
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/connector/pgsql.sql
@@ -0,0 +1,117 @@
+CREATE OR REPLACE FUNCTION pgsql_type(a_type varchar) RETURNS varchar AS
+$BODY$
+DECLARE
+     v_type varchar;
+BEGIN
+     IF a_type='int8' THEN
+          v_type:='bigint';
+     ELSIF a_type='int4' THEN
+          v_type:='integer';
+     ELSIF a_type='int2' THEN
+          v_type:='smallint';
+     ELSIF a_type='bpchar' THEN
+          v_type:='char';
+     ELSE
+          v_type:=a_type;
+     END IF;
+     RETURN v_type;
+END;
+$BODY$
+LANGUAGE PLPGSQL;
+
+CREATE TYPE "public"."tablestruct" AS (
+  "fields_key_name" varchar(100),
+  "fields_name" VARCHAR(200),
+  "fields_type" VARCHAR(20),
+  "fields_length" BIGINT,
+  "fields_not_null" VARCHAR(10),
+  "fields_default" VARCHAR(500),
+  "fields_comment" VARCHAR(1000)
+);
+
+CREATE OR REPLACE FUNCTION "public"."table_msg" (a_schema_name varchar, a_table_name varchar) RETURNS SETOF "public"."tablestruct" AS
+$body$
+DECLARE
+     v_ret tablestruct;
+     v_oid oid;
+     v_sql varchar;
+     v_rec RECORD;
+     v_key varchar;
+BEGIN
+     SELECT
+           pg_class.oid  INTO v_oid
+     FROM
+           pg_class
+           INNER JOIN pg_namespace ON (pg_class.relnamespace = pg_namespace.oid AND lower(pg_namespace.nspname) = a_schema_name)
+     WHERE
+           pg_class.relname=a_table_name;
+     IF NOT FOUND THEN
+         RETURN;
+     END IF;
+
+     v_sql='
+     SELECT
+           pg_attribute.attname AS fields_name,
+           pg_attribute.attnum AS fields_index,
+           pgsql_type(pg_type.typname::varchar) AS fields_type,
+           pg_attribute.atttypmod-4 as fields_length,
+           CASE WHEN pg_attribute.attnotnull  THEN ''not null''
+           ELSE ''''
+           END AS fields_not_null,
+           pg_attrdef.adsrc AS fields_default,
+           pg_description.description AS fields_comment
+     FROM
+           pg_attribute
+           INNER JOIN pg_class  ON pg_attribute.attrelid = pg_class.oid
+           INNER JOIN pg_type   ON pg_attribute.atttypid = pg_type.oid
+           LEFT OUTER JOIN pg_attrdef ON pg_attrdef.adrelid = pg_class.oid AND pg_attrdef.adnum = pg_attribute.attnum
+           LEFT OUTER JOIN pg_description ON pg_description.objoid = pg_class.oid AND pg_description.objsubid = pg_attribute.attnum
+     WHERE
+           pg_attribute.attnum > 0
+           AND attisdropped <> ''t''
+           AND pg_class.oid = ' || v_oid || '
+     ORDER BY pg_attribute.attnum' ;
+
+     FOR v_rec IN EXECUTE v_sql LOOP
+         v_ret.fields_name=v_rec.fields_name;
+         v_ret.fields_type=v_rec.fields_type;
+         IF v_rec.fields_length > 0 THEN
+            v_ret.fields_length:=v_rec.fields_length;
+         ELSE
+            v_ret.fields_length:=NULL;
+         END IF;
+         v_ret.fields_not_null=v_rec.fields_not_null;
+         v_ret.fields_default=v_rec.fields_default;
+         v_ret.fields_comment=v_rec.fields_comment;
+         SELECT constraint_name INTO v_key FROM information_schema.key_column_usage WHERE table_schema=a_schema_name AND table_name=a_table_name AND column_name=v_rec.fields_name;
+         IF FOUND THEN
+            v_ret.fields_key_name=v_key;
+         ELSE
+            v_ret.fields_key_name='';
+         END IF;
+         RETURN NEXT v_ret;
+     END LOOP;
+     RETURN ;
+END;
+$body$
+LANGUAGE 'plpgsql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER;
+
+COMMENT ON FUNCTION "public"."table_msg"(a_schema_name varchar, a_table_name varchar)
+IS '获得表信息';
+
+---重载一个函数
+CREATE OR REPLACE FUNCTION "public"."table_msg" (a_table_name varchar) RETURNS SETOF "public"."tablestruct" AS
+$body$
+DECLARE
+    v_ret tablestruct;
+BEGIN
+    FOR v_ret IN SELECT * FROM table_msg('public',a_table_name) LOOP
+        RETURN NEXT v_ret;
+    END LOOP;
+    RETURN;
+END;
+$body$
+LANGUAGE 'plpgsql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER;
+
+COMMENT ON FUNCTION "public"."table_msg"(a_table_name varchar)
+IS '获得表信息';
\ No newline at end of file
diff --git a/vendor/topthink/think-orm/src/db/exception/BindParamException.php b/vendor/topthink/think-orm/src/db/exception/BindParamException.php
new file mode 100755
index 0000000..08bb388
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/exception/BindParamException.php
@@ -0,0 +1,35 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db\exception;
+
+/**
+ * PDO参数绑定异常
+ */
+class BindParamException extends DbException
+{
+
+    /**
+     * BindParamException constructor.
+     * @access public
+     * @param  string $message
+     * @param  array  $config
+     * @param  string $sql
+     * @param  array    $bind
+     * @param  int    $code
+     */
+    public function __construct(string $message, array $config, string $sql, array $bind, int $code = 10502)
+    {
+        $this->setData('Bind Param', $bind);
+        parent::__construct($message, $config, $sql, $code);
+    }
+}
diff --git a/vendor/topthink/think-orm/src/db/exception/DataNotFoundException.php b/vendor/topthink/think-orm/src/db/exception/DataNotFoundException.php
new file mode 100755
index 0000000..d10dd43
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/exception/DataNotFoundException.php
@@ -0,0 +1,43 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db\exception;
+
+class DataNotFoundException extends DbException
+{
+    protected $table;
+
+    /**
+     * DbException constructor.
+     * @access public
+     * @param  string $message
+     * @param  string $table
+     * @param  array $config
+     */
+    public function __construct(string $message, string $table = '', array $config = [])
+    {
+        $this->message = $message;
+        $this->table   = $table;
+
+        $this->setData('Database Config', $config);
+    }
+
+    /**
+     * 获取数据表名
+     * @access public
+     * @return string
+     */
+    public function getTable()
+    {
+        return $this->table;
+    }
+}
diff --git a/vendor/topthink/think-orm/src/db/exception/DbException.php b/vendor/topthink/think-orm/src/db/exception/DbException.php
new file mode 100755
index 0000000..d5bfc3f
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/exception/DbException.php
@@ -0,0 +1,81 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db\exception;
+
+use Exception;
+
+/**
+ * Database相关异常处理类
+ */
+class DbException extends Exception
+{
+    /**
+     * DbException constructor.
+     * @access public
+     * @param  string    $message
+     * @param  array     $config
+     * @param  string    $sql
+     * @param  int       $code
+     */
+    public function __construct(string $message, array $config = [], string $sql = '', int $code = 10500)
+    {
+        $this->message = $message;
+        $this->code    = $code;
+
+        $this->setData('Database Status', [
+            'Error Code'    => $code,
+            'Error Message' => $message,
+            'Error SQL'     => $sql,
+        ]);
+
+        unset($config['username'], $config['password']);
+        $this->setData('Database Config', $config);
+    }
+
+    /**
+     * 保存异常页面显示的额外Debug数据
+     * @var array
+     */
+    protected $data = [];
+
+    /**
+     * 设置异常额外的Debug数据
+     * 数据将会显示为下面的格式
+     *
+     * Exception Data
+     * --------------------------------------------------
+     * Label 1
+     *   key1      value1
+     *   key2      value2
+     * Label 2
+     *   key1      value1
+     *   key2      value2
+     *
+     * @param string $label 数据分类,用于异常页面显示
+     * @param array  $data  需要显示的数据,必须为关联数组
+     */
+    final protected function setData($label, array $data)
+    {
+        $this->data[$label] = $data;
+    }
+
+    /**
+     * 获取异常额外Debug数据
+     * 主要用于输出到异常页面便于调试
+     * @return array 由setData设置的Debug数据
+     */
+    final public function getData()
+    {
+        return $this->data;
+    }
+}
diff --git a/vendor/topthink/think-orm/src/db/exception/InvalidArgumentException.php b/vendor/topthink/think-orm/src/db/exception/InvalidArgumentException.php
new file mode 100755
index 0000000..047e45e
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/exception/InvalidArgumentException.php
@@ -0,0 +1,21 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+namespace think\db\exception;
+
+use Psr\SimpleCache\InvalidArgumentException as SimpleCacheInvalidArgumentInterface;
+
+/**
+ * 非法数据异常
+ */
+class InvalidArgumentException extends \InvalidArgumentException implements SimpleCacheInvalidArgumentInterface
+{
+}
diff --git a/vendor/topthink/think-orm/src/db/exception/ModelEventException.php b/vendor/topthink/think-orm/src/db/exception/ModelEventException.php
new file mode 100755
index 0000000..767bc1a
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/exception/ModelEventException.php
@@ -0,0 +1,19 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\db\exception;
+
+/**
+ * 模型事件异常
+ */
+class ModelEventException extends DbException
+{
+}
diff --git a/vendor/topthink/think-orm/src/db/exception/ModelNotFoundException.php b/vendor/topthink/think-orm/src/db/exception/ModelNotFoundException.php
new file mode 100755
index 0000000..84a1525
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/exception/ModelNotFoundException.php
@@ -0,0 +1,44 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db\exception;
+
+class ModelNotFoundException extends DbException
+{
+    protected $model;
+
+    /**
+     * 构造方法
+     * @access public
+     * @param  string $message
+     * @param  string $model
+     * @param  array  $config
+     */
+    public function __construct(string $message, string $model = '', array $config = [])
+    {
+        $this->message = $message;
+        $this->model   = $model;
+
+        $this->setData('Database Config', $config);
+    }
+
+    /**
+     * 获取模型类名
+     * @access public
+     * @return string
+     */
+    public function getModel()
+    {
+        return $this->model;
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/db/exception/PDOException.php b/vendor/topthink/think-orm/src/db/exception/PDOException.php
new file mode 100755
index 0000000..41c0d73
--- /dev/null
+++ b/vendor/topthink/think-orm/src/db/exception/PDOException.php
@@ -0,0 +1,41 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\db\exception;
+
+/**
+ * PDO异常处理类
+ * 重新封装了系统的\PDOException类
+ */
+class PDOException extends DbException
+{
+    /**
+     * PDOException constructor.
+     * @access public
+     * @param  \PDOException $exception
+     * @param  array         $config
+     * @param  string        $sql
+     * @param  int           $code
+     */
+    public function __construct(\PDOException $exception, array $config = [], string $sql = '', int $code = 10501)
+    {
+        $error = $exception->errorInfo;
+
+        $this->setData('PDO Error Info', [
+            'SQLSTATE'             => $error[0],
+            'Driver Error Code'    => isset($error[1]) ? $error[1] : 0,
+            'Driver Error Message' => isset($error[2]) ? $error[2] : '',
+        ]);
+
+        parent::__construct($exception->getMessage(), $config, $sql, $code);
+    }
+}
diff --git a/vendor/topthink/think-orm/src/facade/Db.php b/vendor/topthink/think-orm/src/facade/Db.php
new file mode 100755
index 0000000..174a4f0
--- /dev/null
+++ b/vendor/topthink/think-orm/src/facade/Db.php
@@ -0,0 +1,86 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\facade;
+
+if (class_exists('think\Facade')) {
+    class Facade extends \think\Facade
+    {}
+} else {
+    class Facade
+    {
+        /**
+         * 始终创建新的对象实例
+         * @var bool
+         */
+        protected static $alwaysNewInstance;
+
+        protected static $instance;
+
+        /**
+         * 获取当前Facade对应类名
+         * @access protected
+         * @return string
+         */
+        protected static function getFacadeClass()
+        {}
+
+        /**
+         * 创建Facade实例
+         * @static
+         * @access protected
+         * @param  bool $newInstance 是否每次创建新的实例
+         * @return object
+         */
+        protected static function createFacade(bool $newInstance = false)
+        {
+            $class = static::getFacadeClass() ?: 'think\DbManager';
+
+            if (static::$alwaysNewInstance) {
+                $newInstance = true;
+            }
+
+            if ($newInstance) {
+                return new $class();
+            }
+
+            if (!self::$instance) {
+                self::$instance = new $class();
+            }
+
+            return self::$instance;
+
+        }
+
+        // 调用实际类的方法
+        public static function __callStatic($method, $params)
+        {
+            return call_user_func_array([static::createFacade(), $method], $params);
+        }
+    }
+}
+
+/**
+ * @see \think\DbManager
+ * @mixin \think\DbManager
+ */
+class Db extends Facade
+{
+    /**
+     * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+     * @access protected
+     * @return string
+     */
+    protected static function getFacadeClass()
+    {
+        return 'think\DbManager';
+    }
+}
diff --git a/vendor/topthink/think-orm/src/model/Collection.php b/vendor/topthink/think-orm/src/model/Collection.php
new file mode 100755
index 0000000..fd54272
--- /dev/null
+++ b/vendor/topthink/think-orm/src/model/Collection.php
@@ -0,0 +1,250 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: zhangyajun <448901948@qq.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\model;
+
+use think\Collection as BaseCollection;
+use think\Model;
+use think\Paginator;
+
+/**
+ * 模型数据集类
+ */
+class Collection extends BaseCollection
+{
+    /**
+     * 延迟预载入关联查询
+     * @access public
+     * @param  array|string $relation 关联
+     * @param  mixed        $cache    关联缓存
+     * @return $this
+     */
+    public function load($relation, $cache = false)
+    {
+        if (!$this->isEmpty()) {
+            $item = current($this->items);
+            $item->eagerlyResultSet($this->items, (array) $relation, [], false, $cache);
+        }
+
+        return $this;
+    }
+
+    /**
+     * 删除数据集的数据
+     * @access public
+     * @return bool
+     */
+    public function delete(): bool
+    {
+        $this->each(function (Model $model) {
+            $model->delete();
+        });
+
+        return true;
+    }
+
+    /**
+     * 更新数据
+     * @access public
+     * @param array $data       数据数组
+     * @param array $allowField 允许字段
+     * @return bool
+     */
+    public function update(array $data, array $allowField = []): bool
+    {
+        $this->each(function (Model $model) use ($data, $allowField) {
+            if (!empty($allowField)) {
+                $model->allowField($allowField);
+            }
+
+            $model->save($data);
+        });
+
+        return true;
+    }
+
+    /**
+     * 设置需要隐藏的输出属性
+     * @access public
+     * @param  array $hidden 属性列表
+     * @return $this
+     */
+    public function hidden(array $hidden)
+    {
+        $this->each(function (Model $model) use ($hidden) {
+            $model->hidden($hidden);
+        });
+
+        return $this;
+    }
+
+    /**
+     * 设置需要输出的属性
+     * @access public
+     * @param  array $visible
+     * @return $this
+     */
+    public function visible(array $visible)
+    {
+        $this->each(function (Model $model) use ($visible) {
+            $model->visible($visible);
+        });
+
+        return $this;
+    }
+
+    /**
+     * 设置需要追加的输出属性
+     * @access public
+     * @param  array $append 属性列表
+     * @return $this
+     */
+    public function append(array $append)
+    {
+        $this->each(function (Model $model) use ($append) {
+            $model->append($append);
+        });
+
+        return $this;
+    }
+
+    /**
+     * 设置父模型
+     * @access public
+     * @param  Model $parent 父模型
+     * @return $this
+     */
+    public function setParent(Model $parent)
+    {
+        $this->each(function (Model $model) use ($parent) {
+            $model->setParent($parent);
+        });
+
+        return $this;
+    }
+
+    /**
+     * 设置数据字段获取器
+     * @access public
+     * @param  string|array $name       字段名
+     * @param  callable     $callback   闭包获取器
+     * @return $this
+     */
+    public function withAttr($name, $callback = null)
+    {
+        $this->each(function (Model $model) use ($name, $callback) {
+            $model->withAttribute($name, $callback);
+        });
+
+        return $this;
+    }
+
+    /**
+     * 绑定(一对一)关联属性到当前模型
+     * @access protected
+     * @param  string $relation 关联名称
+     * @param  array  $attrs    绑定属性
+     * @return $this
+     * @throws Exception
+     */
+    public function bindAttr(string $relation, array $attrs = [])
+    {
+        $this->each(function (Model $model) use ($relation, $attrs) {
+            $model->bindAttr($relation, $attrs);
+        });
+
+        return $this;
+    }
+
+    /**
+     * 按指定键整理数据
+     *
+     * @access public
+     * @param  mixed  $items    数据
+     * @param  string $indexKey 键名
+     * @return array
+     */
+    public function dictionary($items = null, string &$indexKey = null)
+    {
+        if ($items instanceof self || $items instanceof Paginator) {
+            $items = $items->all();
+        }
+
+        $items = is_null($items) ? $this->items : $items;
+
+        if ($items && empty($indexKey)) {
+            $indexKey = $items[0]->getPk();
+        }
+
+        if (isset($indexKey) && is_string($indexKey)) {
+            return array_column($items, null, $indexKey);
+        }
+
+        return $items;
+    }
+
+    /**
+     * 比较数据集,返回差集
+     *
+     * @access public
+     * @param  mixed  $items    数据
+     * @param  string $indexKey 指定比较的键名
+     * @return static
+     */
+    public function diff($items, string $indexKey = null)
+    {
+        if ($this->isEmpty()) {
+            return new static($items);
+        }
+
+        $diff       = [];
+        $dictionary = $this->dictionary($items, $indexKey);
+
+        if (is_string($indexKey)) {
+            foreach ($this->items as $item) {
+                if (!isset($dictionary[$item[$indexKey]])) {
+                    $diff[] = $item;
+                }
+            }
+        }
+
+        return new static($diff);
+    }
+
+    /**
+     * 比较数据集,返回交集
+     *
+     * @access public
+     * @param  mixed  $items    数据
+     * @param  string $indexKey 指定比较的键名
+     * @return static
+     */
+    public function intersect($items, string $indexKey = null)
+    {
+        if ($this->isEmpty()) {
+            return new static([]);
+        }
+
+        $intersect  = [];
+        $dictionary = $this->dictionary($items, $indexKey);
+
+        if (is_string($indexKey)) {
+            foreach ($this->items as $item) {
+                if (isset($dictionary[$item[$indexKey]])) {
+                    $intersect[] = $item;
+                }
+            }
+        }
+
+        return new static($intersect);
+    }
+}
diff --git a/vendor/topthink/think-orm/src/model/Pivot.php b/vendor/topthink/think-orm/src/model/Pivot.php
new file mode 100755
index 0000000..893c01b
--- /dev/null
+++ b/vendor/topthink/think-orm/src/model/Pivot.php
@@ -0,0 +1,53 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\model;
+
+use think\Model;
+
+/**
+ * 多对多中间表模型类
+ */
+class Pivot extends Model
+{
+
+    /**
+     * 父模型
+     * @var Model
+     */
+    public $parent;
+
+    /**
+     * 是否时间自动写入
+     * @var bool
+     */
+    protected $autoWriteTimestamp = false;
+
+    /**
+     * 架构函数
+     * @access public
+     * @param  array  $data 数据
+     * @param  Model  $parent 上级模型
+     * @param  string $table 中间数据表名
+     */
+    public function __construct(array $data = [], Model $parent = null, string $table = '')
+    {
+        $this->parent = $parent;
+
+        if (is_null($this->name)) {
+            $this->name = $table;
+        }
+
+        parent::__construct($data);
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/model/Relation.php b/vendor/topthink/think-orm/src/model/Relation.php
new file mode 100755
index 0000000..d9d02e3
--- /dev/null
+++ b/vendor/topthink/think-orm/src/model/Relation.php
@@ -0,0 +1,258 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\model;
+
+use Closure;
+use ReflectionFunction;
+use think\db\BaseQuery as Query;
+use think\db\exception\DbException as Exception;
+use think\Model;
+
+/**
+ * 模型关联基础类
+ * @package think\model
+ * @mixin Query
+ */
+abstract class Relation
+{
+    /**
+     * 父模型对象
+     * @var Model
+     */
+    protected $parent;
+
+    /**
+     * 当前关联的模型类名
+     * @var string
+     */
+    protected $model;
+
+    /**
+     * 关联模型查询对象
+     * @var Query
+     */
+    protected $query;
+
+    /**
+     * 关联表外键
+     * @var string
+     */
+    protected $foreignKey;
+
+    /**
+     * 关联表主键
+     * @var string
+     */
+    protected $localKey;
+
+    /**
+     * 是否执行关联基础查询
+     * @var bool
+     */
+    protected $baseQuery;
+
+    /**
+     * 是否为自关联
+     * @var bool
+     */
+    protected $selfRelation = false;
+
+    /**
+     * 关联数据数量限制
+     * @var int
+     */
+    protected $withLimit;
+
+    /**
+     * 关联数据字段限制
+     * @var array
+     */
+    protected $withField;
+
+    /**
+     * 获取关联的所属模型
+     * @access public
+     * @return Model
+     */
+    public function getParent(): Model
+    {
+        return $this->parent;
+    }
+
+    /**
+     * 获取当前的关联模型类的Query实例
+     * @access public
+     * @return Query
+     */
+    public function getQuery()
+    {
+        return $this->query;
+    }
+
+    /**
+     * 获取当前的关联模型类的实例
+     * @access public
+     * @return Model
+     */
+    public function getModel(): Model
+    {
+        return $this->query->getModel();
+    }
+
+    /**
+     * 当前关联是否为自关联
+     * @access public
+     * @return bool
+     */
+    public function isSelfRelation(): bool
+    {
+        return $this->selfRelation;
+    }
+
+    /**
+     * 封装关联数据集
+     * @access public
+     * @param  array $resultSet 数据集
+     * @param  Model $parent 父模型
+     * @return mixed
+     */
+    protected function resultSetBuild(array $resultSet, Model $parent = null)
+    {
+        return (new $this->model)->toCollection($resultSet)->setParent($parent);
+    }
+
+    protected function getQueryFields(string $model)
+    {
+        $fields = $this->query->getOptions('field');
+        return $this->getRelationQueryFields($fields, $model);
+    }
+
+    protected function getRelationQueryFields($fields, string $model)
+    {
+        if (empty($fields) || '*' == $fields) {
+            return $model . '.*';
+        }
+
+        if (is_string($fields)) {
+            $fields = explode(',', $fields);
+        }
+
+        foreach ($fields as &$field) {
+            if (false === strpos($field, '.')) {
+                $field = $model . '.' . $field;
+            }
+        }
+
+        return $fields;
+    }
+
+    protected function getQueryWhere(array &$where, string $relation): void
+    {
+        foreach ($where as $key => &$val) {
+            if (is_string($key)) {
+                $where[] = [false === strpos($key, '.') ? $relation . '.' . $key : $key, '=', $val];
+                unset($where[$key]);
+            } elseif (isset($val[0]) && false === strpos($val[0], '.')) {
+                $val[0] = $relation . '.' . $val[0];
+            }
+        }
+    }
+
+    /**
+     * 更新数据
+     * @access public
+     * @param  array $data 更新数据
+     * @return integer
+     */
+    public function update(array $data = []): int
+    {
+        return $this->query->update($data);
+    }
+
+    /**
+     * 删除记录
+     * @access public
+     * @param  mixed $data 表达式 true 表示强制删除
+     * @return int
+     * @throws Exception
+     * @throws PDOException
+     */
+    public function delete($data = null): int
+    {
+        return $this->query->delete($data);
+    }
+
+    /**
+     * 限制关联数据的数量
+     * @access public
+     * @param  int $limit 关联数量限制
+     * @return $this
+     */
+    public function withLimit(int $limit)
+    {
+        $this->withLimit = $limit;
+        return $this;
+    }
+
+    /**
+     * 限制关联数据的字段
+     * @access public
+     * @param  array $field 关联字段限制
+     * @return $this
+     */
+    public function withField(array $field)
+    {
+        $this->withField = $field;
+        return $this;
+    }
+
+    /**
+     * 判断闭包的参数类型
+     * @access protected
+     * @return mixed
+     */
+    protected function getClosureType(Closure $closure)
+    {
+        $reflect = new ReflectionFunction($closure);
+        $params  = $reflect->getParameters();
+
+        if (!empty($params)) {
+            $type = $params[0]->getType();
+            return is_null($type) || Relation::class == $type->getName() ? $this : $this->query;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 执行基础查询(仅执行一次)
+     * @access protected
+     * @return void
+     */
+    protected function baseQuery(): void
+    {}
+
+    public function __call($method, $args)
+    {
+        if ($this->query) {
+            // 执行基础查询
+            $this->baseQuery();
+
+            $result = call_user_func_array([$this->query, $method], $args);
+
+            return $result === $this->query ? $this : $result;
+        }
+
+        throw new Exception('method not exists:' . __CLASS__ . '->' . $method);
+    }
+}
diff --git a/vendor/topthink/think-orm/src/model/concern/Attribute.php b/vendor/topthink/think-orm/src/model/concern/Attribute.php
new file mode 100755
index 0000000..9e4367b
--- /dev/null
+++ b/vendor/topthink/think-orm/src/model/concern/Attribute.php
@@ -0,0 +1,651 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\model\concern;
+
+use InvalidArgumentException;
+use think\db\Raw;
+use think\helper\Str;
+use think\model\Relation;
+
+/**
+ * 模型数据处理
+ */
+trait Attribute
+{
+    /**
+     * 数据表主键 复合主键使用数组定义
+     * @var string|array
+     */
+    protected $pk = 'id';
+
+    /**
+     * 数据表字段信息 留空则自动获取
+     * @var array
+     */
+    protected $schema = [];
+
+    /**
+     * 当前允许写入的字段
+     * @var array
+     */
+    protected $field = [];
+
+    /**
+     * 字段自动类型转换
+     * @var array
+     */
+    protected $type = [];
+
+    /**
+     * 数据表废弃字段
+     * @var array
+     */
+    protected $disuse = [];
+
+    /**
+     * 数据表只读字段
+     * @var array
+     */
+    protected $readonly = [];
+
+    /**
+     * 当前模型数据
+     * @var array
+     */
+    private $data = [];
+
+    /**
+     * 原始数据
+     * @var array
+     */
+    private $origin = [];
+
+    /**
+     * JSON数据表字段
+     * @var array
+     */
+    protected $json = [];
+
+    /**
+     * JSON数据表字段类型
+     * @var array
+     */
+    protected $jsonType = [];
+
+    /**
+     * JSON数据取出是否需要转换为数组
+     * @var bool
+     */
+    protected $jsonAssoc = false;
+
+    /**
+     * 是否严格字段大小写
+     * @var bool
+     */
+    protected $strict = true;
+
+    /**
+     * 修改器执行记录
+     * @var array
+     */
+    private $set = [];
+
+    /**
+     * 动态获取器
+     * @var array
+     */
+    private $withAttr = [];
+
+    /**
+     * 获取模型对象的主键
+     * @access public
+     * @return string|array
+     */
+    public function getPk()
+    {
+        return $this->pk;
+    }
+
+    /**
+     * 判断一个字段名是否为主键字段
+     * @access public
+     * @param  string $key 名称
+     * @return bool
+     */
+    protected function isPk(string $key): bool
+    {
+        $pk = $this->getPk();
+
+        if (is_string($pk) && $pk == $key) {
+            return true;
+        } elseif (is_array($pk) && in_array($key, $pk)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * 获取模型对象的主键值
+     * @access public
+     * @return mixed
+     */
+    public function getKey()
+    {
+        $pk = $this->getPk();
+
+        if (is_string($pk) && array_key_exists($pk, $this->data)) {
+            return $this->data[$pk];
+        }
+
+        return;
+    }
+
+    /**
+     * 设置允许写入的字段
+     * @access public
+     * @param  array $field 允许写入的字段
+     * @return $this
+     */
+    public function allowField(array $field)
+    {
+        $this->field = $field;
+
+        return $this;
+    }
+
+    /**
+     * 设置只读字段
+     * @access public
+     * @param  array $field 只读字段
+     * @return $this
+     */
+    public function readOnly(array $field)
+    {
+        $this->readonly = $field;
+
+        return $this;
+    }
+
+    /**
+     * 获取实际的字段名
+     * @access protected
+     * @param  string $name 字段名
+     * @return string
+     */
+    protected function getRealFieldName(string $name): string
+    {
+        return $this->strict ? $name : Str::snake($name);
+    }
+
+    /**
+     * 设置数据对象值
+     * @access public
+     * @param  array    $data  数据
+     * @param  bool     $set   是否调用修改器
+     * @param  array    $allow 允许的字段名
+     * @return $this
+     */
+    public function data(array $data, bool $set = false, array $allow = [])
+    {
+        // 清空数据
+        $this->data = [];
+
+        // 废弃字段
+        foreach ($this->disuse as $key) {
+            if (array_key_exists($key, $data)) {
+                unset($data[$key]);
+            }
+        }
+
+        if (!empty($allow)) {
+            $result = [];
+            foreach ($allow as $name) {
+                if (isset($data[$name])) {
+                    $result[$name] = $data[$name];
+                }
+            }
+            $data = $result;
+        }
+
+        if ($set) {
+            // 数据对象赋值
+            $this->setAttrs($data);
+        } else {
+            $this->data = $data;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 批量追加数据对象值
+     * @access public
+     * @param  array $data  数据
+     * @param  bool  $set   是否需要进行数据处理
+     * @return $this
+     */
+    public function appendData(array $data, bool $set = false)
+    {
+        if ($set) {
+            $this->setAttrs($data);
+        } else {
+            $this->data = array_merge($this->data, $data);
+        }
+
+        return $this;
+    }
+
+    /**
+     * 获取对象原始数据 如果不存在指定字段返回null
+     * @access public
+     * @param  string $name 字段名 留空获取全部
+     * @return mixed
+     */
+    public function getOrigin(string $name = null)
+    {
+        if (is_null($name)) {
+            return $this->origin;
+        }
+
+        return array_key_exists($name, $this->origin) ? $this->origin[$name] : null;
+    }
+
+    /**
+     * 获取对象原始数据 如果不存在指定字段返回false
+     * @access public
+     * @param  string $name 字段名 留空获取全部
+     * @return mixed
+     * @throws InvalidArgumentException
+     */
+    public function getData(string $name = null)
+    {
+        if (is_null($name)) {
+            return $this->data;
+        }
+
+        $fieldName = $this->getRealFieldName($name);
+
+        if (array_key_exists($fieldName, $this->data)) {
+            return $this->data[$fieldName];
+        } elseif (array_key_exists($fieldName, $this->relation)) {
+            return $this->relation[$fieldName];
+        }
+
+        throw new InvalidArgumentException('property not exists:' . static::class . '->' . $name);
+    }
+
+    /**
+     * 获取变化的数据 并排除只读数据
+     * @access public
+     * @return array
+     */
+    public function getChangedData(): array
+    {
+        $data = $this->force ? $this->data : array_udiff_assoc($this->data, $this->origin, function ($a, $b) {
+            if ((empty($a) || empty($b)) && $a !== $b) {
+                return 1;
+            }
+
+            return is_object($a) || $a != $b ? 1 : 0;
+        });
+
+        // 只读字段不允许更新
+        foreach ($this->readonly as $key => $field) {
+            if (isset($data[$field])) {
+                unset($data[$field]);
+            }
+        }
+
+        return $data;
+    }
+
+    /**
+     * 直接设置数据对象值
+     * @access public
+     * @param  string $name  属性名
+     * @param  mixed  $value 值
+     * @return void
+     */
+    public function set(string $name, $value): void
+    {
+        $name = $this->getRealFieldName($name);
+
+        $this->data[$name] = $value;
+    }
+
+    /**
+     * 通过修改器 批量设置数据对象值
+     * @access public
+     * @param  array $data  数据
+     * @return void
+     */
+    public function setAttrs(array $data): void
+    {
+        // 进行数据处理
+        foreach ($data as $key => $value) {
+            $this->setAttr($key, $value, $data);
+        }
+    }
+
+    /**
+     * 通过修改器 设置数据对象值
+     * @access public
+     * @param  string $name  属性名
+     * @param  mixed  $value 属性值
+     * @param  array  $data  数据
+     * @return void
+     */
+    public function setAttr(string $name, $value, array $data = []): void
+    {
+        $name = $this->getRealFieldName($name);
+
+        if (isset($this->set[$name])) {
+            return;
+        }
+
+        if (is_null($value) && $this->autoWriteTimestamp && in_array($name, [$this->createTime, $this->updateTime])) {
+            // 自动写入的时间戳字段
+            $value = $this->autoWriteTimestamp();
+        } else {
+            // 检测修改器
+            $method = 'set' . Str::studly($name) . 'Attr';
+
+            if (method_exists($this, $method)) {
+                $array = $this->data;
+
+                $value = $this->$method($value, array_merge($this->data, $data));
+
+                $this->set[$name] = true;
+                if (is_null($value) && $array !== $this->data) {
+                    return;
+                }
+            } elseif (isset($this->type[$name])) {
+                // 类型转换
+                $value = $this->writeTransform($value, $this->type[$name]);
+            }
+        }
+
+        // 设置数据对象属性
+        $this->data[$name] = $value;
+    }
+
+    /**
+     * 数据写入 类型转换
+     * @access protected
+     * @param  mixed        $value 值
+     * @param  string|array $type  要转换的类型
+     * @return mixed
+     */
+    protected function writeTransform($value, $type)
+    {
+        if (is_null($value)) {
+            return;
+        }
+
+        if ($value instanceof Raw) {
+            return $value;
+        }
+
+        if (is_array($type)) {
+            [$type, $param] = $type;
+        } elseif (strpos($type, ':')) {
+            [$type, $param] = explode(':', $type, 2);
+        }
+
+        switch ($type) {
+            case 'integer':
+                $value = (int) $value;
+                break;
+            case 'float':
+                if (empty($param)) {
+                    $value = (float) $value;
+                } else {
+                    $value = (float) number_format($value, (int) $param, '.', '');
+                }
+                break;
+            case 'boolean':
+                $value = (bool) $value;
+                break;
+            case 'timestamp':
+                if (!is_numeric($value)) {
+                    $value = strtotime($value);
+                }
+                break;
+            case 'datetime':
+                $value = is_numeric($value) ? $value : strtotime($value);
+                $value = $this->formatDateTime('Y-m-d H:i:s.u', $value, true);
+                break;
+            case 'object':
+                if (is_object($value)) {
+                    $value = json_encode($value, JSON_FORCE_OBJECT);
+                }
+                break;
+            case 'array':
+                $value = (array) $value;
+            case 'json':
+                $option = !empty($param) ? (int) $param : JSON_UNESCAPED_UNICODE;
+                $value  = json_encode($value, $option);
+                break;
+            case 'serialize':
+                $value = serialize($value);
+                break;
+            default:
+                if (is_object($value) && false !== strpos($type, '\\') && method_exists($value, '__toString')) {
+                    // 对象类型
+                    $value = $value->__toString();
+                }
+        }
+
+        return $value;
+    }
+
+    /**
+     * 获取器 获取数据对象的值
+     * @access public
+     * @param  string $name 名称
+     * @return mixed
+     * @throws InvalidArgumentException
+     */
+    public function getAttr(string $name)
+    {
+        try {
+            $relation = false;
+            $value    = $this->getData($name);
+        } catch (InvalidArgumentException $e) {
+            $relation = $this->isRelationAttr($name);
+            $value    = null;
+        }
+
+        return $this->getValue($name, $value, $relation);
+    }
+
+    /**
+     * 获取经过获取器处理后的数据对象的值
+     * @access protected
+     * @param  string      $name 字段名称
+     * @param  mixed       $value 字段值
+     * @param  bool|string $relation 是否为关联属性或者关联名
+     * @return mixed
+     * @throws InvalidArgumentException
+     */
+    protected function getValue(string $name, $value, $relation = false)
+    {
+        // 检测属性获取器
+        $fieldName = $this->getRealFieldName($name);
+        $method    = 'get' . Str::studly($name) . 'Attr';
+
+        if (isset($this->withAttr[$fieldName])) {
+            if ($relation) {
+                $value = $this->getRelationValue($relation);
+            }
+
+            if (in_array($fieldName, $this->json) && is_array($this->withAttr[$fieldName])) {
+                $value = $this->getJsonValue($fieldName, $value);
+            } else {
+                $closure = $this->withAttr[$fieldName];
+                $value   = $closure($value, $this->data);
+            }
+        } elseif (method_exists($this, $method)) {
+            if ($relation) {
+                $value = $this->getRelationValue($relation);
+            }
+
+            $value = $this->$method($value, $this->data);
+        } elseif (isset($this->type[$fieldName])) {
+            // 类型转换
+            $value = $this->readTransform($value, $this->type[$fieldName]);
+        } elseif ($this->autoWriteTimestamp && in_array($fieldName, [$this->createTime, $this->updateTime])) {
+            $value = $this->getTimestampValue($value);
+        } elseif ($relation) {
+            $value = $this->getRelationValue($relation);
+            // 保存关联对象值
+            $this->relation[$name] = $value;
+        }
+
+        return $value;
+    }
+
+    /**
+     * 获取JSON字段属性值
+     * @access protected
+     * @param  string $name  属性名
+     * @param  mixed  $value JSON数据
+     * @return mixed
+     */
+    protected function getJsonValue($name, $value)
+    {
+        foreach ($this->withAttr[$name] as $key => $closure) {
+            if ($this->jsonAssoc) {
+                $value[$key] = $closure($value[$key], $value);
+            } else {
+                $value->$key = $closure($value->$key, $value);
+            }
+        }
+
+        return $value;
+    }
+
+    /**
+     * 获取关联属性值
+     * @access protected
+     * @param  string $relation 关联名
+     * @return mixed
+     */
+    protected function getRelationValue(string $relation)
+    {
+        $modelRelation = $this->$relation();
+
+        return $modelRelation instanceof Relation ? $this->getRelationData($modelRelation) : null;
+    }
+
+    /**
+     * 数据读取 类型转换
+     * @access protected
+     * @param  mixed        $value 值
+     * @param  string|array $type  要转换的类型
+     * @return mixed
+     */
+    protected function readTransform($value, $type)
+    {
+        if (is_null($value)) {
+            return;
+        }
+
+        if (is_array($type)) {
+            [$type, $param] = $type;
+        } elseif (strpos($type, ':')) {
+            [$type, $param] = explode(':', $type, 2);
+        }
+
+        switch ($type) {
+            case 'integer':
+                $value = (int) $value;
+                break;
+            case 'float':
+                if (empty($param)) {
+                    $value = (float) $value;
+                } else {
+                    $value = (float) number_format($value, (int) $param, '.', '');
+                }
+                break;
+            case 'boolean':
+                $value = (bool) $value;
+                break;
+            case 'timestamp':
+                if (!is_null($value)) {
+                    $format = !empty($param) ? $param : $this->dateFormat;
+                    $value  = $this->formatDateTime($format, $value, true);
+                }
+                break;
+            case 'datetime':
+                if (!is_null($value)) {
+                    $format = !empty($param) ? $param : $this->dateFormat;
+                    $value  = $this->formatDateTime($format, $value);
+                }
+                break;
+            case 'json':
+                $value = json_decode($value, true);
+                break;
+            case 'array':
+                $value = empty($value) ? [] : json_decode($value, true);
+                break;
+            case 'object':
+                $value = empty($value) ? new \stdClass() : json_decode($value);
+                break;
+            case 'serialize':
+                try {
+                    $value = unserialize($value);
+                } catch (\Exception $e) {
+                    $value = null;
+                }
+                break;
+            default:
+                if (false !== strpos($type, '\\')) {
+                    // 对象类型
+                    $value = new $type($value);
+                }
+        }
+
+        return $value;
+    }
+
+    /**
+     * 设置数据字段获取器
+     * @access public
+     * @param  string|array $name       字段名
+     * @param  callable     $callback   闭包获取器
+     * @return $this
+     */
+    public function withAttribute($name, callable $callback = null)
+    {
+        if (is_array($name)) {
+            foreach ($name as $key => $val) {
+                $this->withAttribute($key, $val);
+            }
+        } else {
+            $name = $this->getRealFieldName($name);
+
+            if (strpos($name, '.')) {
+                [$name, $key] = explode('.', $name);
+
+                $this->withAttr[$name][$key] = $callback;
+            } else {
+                $this->withAttr[$name] = $callback;
+            }
+        }
+
+        return $this;
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/model/concern/Conversion.php b/vendor/topthink/think-orm/src/model/concern/Conversion.php
new file mode 100755
index 0000000..e12d0af
--- /dev/null
+++ b/vendor/topthink/think-orm/src/model/concern/Conversion.php
@@ -0,0 +1,278 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\model\concern;
+
+use think\Collection;
+use think\db\exception\DbException as Exception;
+use think\helper\Str;
+use think\Model;
+use think\model\Collection as ModelCollection;
+use think\model\relation\OneToOne;
+
+/**
+ * 模型数据转换处理
+ */
+trait Conversion
+{
+    /**
+     * 数据输出显示的属性
+     * @var array
+     */
+    protected $visible = [];
+
+    /**
+     * 数据输出隐藏的属性
+     * @var array
+     */
+    protected $hidden = [];
+
+    /**
+     * 数据输出需要追加的属性
+     * @var array
+     */
+    protected $append = [];
+
+    /**
+     * 数据集对象名
+     * @var string
+     */
+    protected $resultSetType;
+
+    /**
+     * 设置需要附加的输出属性
+     * @access public
+     * @param  array $append   属性列表
+     * @return $this
+     */
+    public function append(array $append = [])
+    {
+        $this->append = $append;
+
+        return $this;
+    }
+
+    /**
+     * 设置附加关联对象的属性
+     * @access public
+     * @param  string       $attr    关联属性
+     * @param  string|array $append  追加属性名
+     * @return $this
+     * @throws Exception
+     */
+    public function appendRelationAttr(string $attr, array $append)
+    {
+        $relation = Str::camel($attr);
+
+        if (isset($this->relation[$relation])) {
+            $model = $this->relation[$relation];
+        } else {
+            $model = $this->getRelationData($this->$relation());
+        }
+
+        if ($model instanceof Model) {
+            foreach ($append as $key => $attr) {
+                $key = is_numeric($key) ? $attr : $key;
+                if (isset($this->data[$key])) {
+                    throw new Exception('bind attr has exists:' . $key);
+                }
+
+                $this->data[$key] = $model->$attr;
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * 设置需要隐藏的输出属性
+     * @access public
+     * @param  array $hidden   属性列表
+     * @return $this
+     */
+    public function hidden(array $hidden = [])
+    {
+        $this->hidden = $hidden;
+
+        return $this;
+    }
+
+    /**
+     * 设置需要输出的属性
+     * @access public
+     * @param  array $visible
+     * @return $this
+     */
+    public function visible(array $visible = [])
+    {
+        $this->visible = $visible;
+
+        return $this;
+    }
+
+    /**
+     * 转换当前模型对象为数组
+     * @access public
+     * @return array
+     */
+    public function toArray(): array
+    {
+        $item       = [];
+        $hasVisible = false;
+
+        foreach ($this->visible as $key => $val) {
+            if (is_string($val)) {
+                if (strpos($val, '.')) {
+                    [$relation, $name]          = explode('.', $val);
+                    $this->visible[$relation][] = $name;
+                } else {
+                    $this->visible[$val] = true;
+                    $hasVisible          = true;
+                }
+                unset($this->visible[$key]);
+            }
+        }
+
+        foreach ($this->hidden as $key => $val) {
+            if (is_string($val)) {
+                if (strpos($val, '.')) {
+                    [$relation, $name]         = explode('.', $val);
+                    $this->hidden[$relation][] = $name;
+                } else {
+                    $this->hidden[$val] = true;
+                }
+                unset($this->hidden[$key]);
+            }
+        }
+
+        // 合并关联数据
+        $data = array_merge($this->data, $this->relation);
+
+        foreach ($data as $key => $val) {
+            if ($val instanceof Model || $val instanceof ModelCollection) {
+                // 关联模型对象
+                if (isset($this->visible[$key]) && is_array($this->visible[$key])) {
+                    $val->visible($this->visible[$key]);
+                } elseif (isset($this->hidden[$key]) && is_array($this->hidden[$key])) {
+                    $val->hidden($this->hidden[$key]);
+                }
+                // 关联模型对象
+                if (!isset($this->hidden[$key]) || true !== $this->hidden[$key]) {
+                    $item[$key] = $val->toArray();
+                }
+            } elseif (isset($this->visible[$key])) {
+                $item[$key] = $this->getAttr($key);
+            } elseif (!isset($this->hidden[$key]) && !$hasVisible) {
+                $item[$key] = $this->getAttr($key);
+            }
+        }
+
+        // 追加属性(必须定义获取器)
+        foreach ($this->append as $key => $name) {
+            $this->appendAttrToArray($item, $key, $name);
+        }
+
+        return $item;
+    }
+
+    protected function appendAttrToArray(array &$item, $key, $name)
+    {
+        if (is_array($name)) {
+            // 追加关联对象属性
+            $relation   = $this->getRelation($key, true);
+            $item[$key] = $relation ? $relation->append($name)
+                ->toArray() : [];
+        } elseif (strpos($name, '.')) {
+            [$key, $attr] = explode('.', $name);
+            // 追加关联对象属性
+            $relation   = $this->getRelation($key, true);
+            $item[$key] = $relation ? $relation->append([$attr])
+                ->toArray() : [];
+        } else {
+            $value       = $this->getAttr($name);
+            $item[$name] = $value;
+
+            $this->getBindAttr($name, $value, $item);
+        }
+    }
+
+    protected function getBindAttr(string $name, $value, array &$item = [])
+    {
+        $relation = $this->isRelationAttr($name);
+        if (!$relation) {
+            return false;
+        }
+
+        $modelRelation = $this->$relation();
+
+        if ($modelRelation instanceof OneToOne) {
+            $bindAttr = $modelRelation->getBindAttr();
+
+            if (!empty($bindAttr)) {
+                unset($item[$name]);
+            }
+
+            foreach ($bindAttr as $key => $attr) {
+                $key = is_numeric($key) ? $attr : $key;
+
+                if (isset($item[$key])) {
+                    throw new Exception('bind attr has exists:' . $key);
+                }
+
+                $item[$key] = $value ? $value->getAttr($attr) : null;
+            }
+        }
+    }
+
+    /**
+     * 转换当前模型对象为JSON字符串
+     * @access public
+     * @param  integer $options json参数
+     * @return string
+     */
+    public function toJson(int $options = JSON_UNESCAPED_UNICODE): string
+    {
+        return json_encode($this->toArray(), $options);
+    }
+
+    public function __toString()
+    {
+        return $this->toJson();
+    }
+
+    // JsonSerializable
+    public function jsonSerialize()
+    {
+        return $this->toArray();
+    }
+
+    /**
+     * 转换数据集为数据集对象
+     * @access public
+     * @param  array|Collection $collection 数据集
+     * @param  string           $resultSetType 数据集类
+     * @return Collection
+     */
+    public function toCollection(iterable $collection = [], string $resultSetType = null): Collection
+    {
+        $resultSetType = $resultSetType ?: $this->resultSetType;
+
+        if ($resultSetType && false !== strpos($resultSetType, '\\')) {
+            $collection = new $resultSetType($collection);
+        } else {
+            $collection = new ModelCollection($collection);
+        }
+
+        return $collection;
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/model/concern/ModelEvent.php b/vendor/topthink/think-orm/src/model/concern/ModelEvent.php
new file mode 100755
index 0000000..f560379
--- /dev/null
+++ b/vendor/topthink/think-orm/src/model/concern/ModelEvent.php
@@ -0,0 +1,88 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\model\concern;
+
+use think\db\exception\ModelEventException;
+use think\helper\Str;
+
+/**
+ * 模型事件处理
+ */
+trait ModelEvent
+{
+
+    /**
+     * Event对象
+     * @var object
+     */
+    protected static $event;
+
+    /**
+     * 是否需要事件响应
+     * @var bool
+     */
+    protected $withEvent = true;
+
+    /**
+     * 设置Event对象
+     * @access public
+     * @param object $event Event对象
+     * @return void
+     */
+    public static function setEvent($event)
+    {
+        self::$event = $event;
+    }
+
+    /**
+     * 当前操作的事件响应
+     * @access protected
+     * @param  bool $event  是否需要事件响应
+     * @return $this
+     */
+    public function withEvent(bool $event)
+    {
+        $this->withEvent = $event;
+        return $this;
+    }
+
+    /**
+     * 触发事件
+     * @access protected
+     * @param  string $event 事件名
+     * @return bool
+     */
+    protected function trigger(string $event): bool
+    {
+        if (!$this->withEvent) {
+            return true;
+        }
+
+        $call = 'on' . Str::studly($event);
+
+        try {
+            if (method_exists(static::class, $call)) {
+                $result = call_user_func([static::class, $call], $this);
+            } elseif (is_object(self::$event) && method_exists(self::$event, 'trigger')) {
+                $result = self::$event->trigger(static::class . '.' . $event, $this);
+                $result = empty($result) ? true : end($result);
+            } else {
+                $result = true;
+            }
+
+            return false === $result ? false : true;
+        } catch (ModelEventException $e) {
+            return false;
+        }
+    }
+}
diff --git a/vendor/topthink/think-orm/src/model/concern/OptimLock.php b/vendor/topthink/think-orm/src/model/concern/OptimLock.php
new file mode 100755
index 0000000..5e61318
--- /dev/null
+++ b/vendor/topthink/think-orm/src/model/concern/OptimLock.php
@@ -0,0 +1,85 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\model\concern;
+
+use think\db\exception\DbException as Exception;
+
+/**
+ * 乐观锁
+ */
+trait OptimLock
+{
+    protected function getOptimLockField()
+    {
+        return property_exists($this, 'optimLock') && isset($this->optimLock) ? $this->optimLock : 'lock_version';
+    }
+
+    /**
+     * 数据检查
+     * @access protected
+     * @return void
+     */
+    protected function checkData(): void
+    {
+        $this->isExists() ? $this->updateLockVersion() : $this->recordLockVersion();
+    }
+
+    /**
+     * 记录乐观锁
+     * @access protected
+     * @return void
+     */
+    protected function recordLockVersion(): void
+    {
+        $optimLock = $this->getOptimLockField();
+
+        if ($optimLock) {
+            $this->set($optimLock, 0);
+        }
+    }
+
+    /**
+     * 更新乐观锁
+     * @access protected
+     * @return void
+     */
+    protected function updateLockVersion(): void
+    {
+        $optimLock = $this->getOptimLockField();
+
+        if ($optimLock && $lockVer = $this->getOrigin($optimLock)) {
+            // 更新乐观锁
+            $this->set($optimLock, $lockVer + 1);
+        }
+    }
+
+    public function getWhere()
+    {
+        $where     = parent::getWhere();
+        $optimLock = $this->getOptimLockField();
+
+        if ($optimLock && $lockVer = $this->getOrigin($optimLock)) {
+            $where[] = [$optimLock, '=', $lockVer];
+        }
+
+        return $where;
+    }
+
+    protected function checkResult($result): void
+    {
+        if (!$result) {
+            throw new Exception('record has update');
+        }
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/model/concern/RelationShip.php b/vendor/topthink/think-orm/src/model/concern/RelationShip.php
new file mode 100755
index 0000000..f3da1c4
--- /dev/null
+++ b/vendor/topthink/think-orm/src/model/concern/RelationShip.php
@@ -0,0 +1,841 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\model\concern;
+
+use Closure;
+use think\Collection;
+use think\db\BaseQuery as Query;
+use think\db\exception\DbException as Exception;
+use think\helper\Str;
+use think\Model;
+use think\model\Relation;
+use think\model\relation\BelongsTo;
+use think\model\relation\BelongsToMany;
+use think\model\relation\HasMany;
+use think\model\relation\HasManyThrough;
+use think\model\relation\HasOne;
+use think\model\relation\HasOneThrough;
+use think\model\relation\MorphMany;
+use think\model\relation\MorphOne;
+use think\model\relation\MorphTo;
+use think\model\relation\MorphToMany;
+use think\model\relation\OneToOne;
+
+/**
+ * 模型关联处理
+ */
+trait RelationShip
+{
+    /**
+     * 父关联模型对象
+     * @var object
+     */
+    private $parent;
+
+    /**
+     * 模型关联数据
+     * @var array
+     */
+    private $relation = [];
+
+    /**
+     * 关联写入定义信息
+     * @var array
+     */
+    private $together = [];
+
+    /**
+     * 关联自动写入信息
+     * @var array
+     */
+    protected $relationWrite = [];
+
+    /**
+     * 设置父关联对象
+     * @access public
+     * @param  Model $model  模型对象
+     * @return $this
+     */
+    public function setParent(Model $model)
+    {
+        $this->parent = $model;
+
+        return $this;
+    }
+
+    /**
+     * 获取父关联对象
+     * @access public
+     * @return Model
+     */
+    public function getParent(): Model
+    {
+        return $this->parent;
+    }
+
+    /**
+     * 获取当前模型的关联模型数据
+     * @access public
+     * @param  string $name 关联方法名
+     * @param  bool   $auto 不存在是否自动获取
+     * @return mixed
+     */
+    public function getRelation(string $name = null, bool $auto = false)
+    {
+        if (is_null($name)) {
+            return $this->relation;
+        }
+
+        if (array_key_exists($name, $this->relation)) {
+            return $this->relation[$name];
+        } elseif ($auto) {
+            $relation = Str::camel($name);
+            return $this->getRelationValue($relation);
+        }
+    }
+
+    /**
+     * 设置关联数据对象值
+     * @access public
+     * @param  string $name  属性名
+     * @param  mixed  $value 属性值
+     * @param  array  $data  数据
+     * @return $this
+     */
+    public function setRelation(string $name, $value, array $data = [])
+    {
+        // 检测修改器
+        $method = 'set' . Str::studly($name) . 'Attr';
+
+        if (method_exists($this, $method)) {
+            $value = $this->$method($value, array_merge($this->data, $data));
+        }
+
+        $this->relation[$this->getRealFieldName($name)] = $value;
+
+        return $this;
+    }
+
+    /**
+     * 查询当前模型的关联数据
+     * @access public
+     * @param  array $relations 关联名
+     * @param  array $withRelationAttr   关联获取器
+     * @return void
+     */
+    public function relationQuery(array $relations, array $withRelationAttr = []): void
+    {
+        foreach ($relations as $key => $relation) {
+            $subRelation = '';
+            $closure     = null;
+
+            if ($relation instanceof Closure) {
+                // 支持闭包查询过滤关联条件
+                $closure  = $relation;
+                $relation = $key;
+            }
+
+            if (is_array($relation)) {
+                $subRelation = $relation;
+                $relation    = $key;
+            } elseif (strpos($relation, '.')) {
+                [$relation, $subRelation] = explode('.', $relation, 2);
+            }
+
+            $method       = Str::camel($relation);
+            $relationName = Str::snake($relation);
+
+            $relationResult = $this->$method();
+
+            if (isset($withRelationAttr[$relationName])) {
+                $relationResult->withAttr($withRelationAttr[$relationName]);
+            }
+
+            $this->relation[$relation] = $relationResult->getRelation($subRelation, $closure);
+        }
+    }
+
+    /**
+     * 关联数据写入
+     * @access public
+     * @param  array $relation 关联
+     * @return $this
+     */
+    public function together(array $relation)
+    {
+        $this->together = $relation;
+
+        $this->checkAutoRelationWrite();
+
+        return $this;
+    }
+
+    /**
+     * 根据关联条件查询当前模型
+     * @access public
+     * @param  string  $relation 关联方法名
+     * @param  mixed   $operator 比较操作符
+     * @param  integer $count    个数
+     * @param  string  $id       关联表的统计字段
+     * @param  string  $joinType JOIN类型
+     * @param  Query   $query    Query对象
+     * @return Query
+     */
+    public static function has(string $relation, string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '', Query $query = null): Query
+    {
+        return (new static())
+            ->$relation()
+            ->has($operator, $count, $id, $joinType, $query);
+    }
+
+    /**
+     * 根据关联条件查询当前模型
+     * @access public
+     * @param  string $relation 关联方法名
+     * @param  mixed  $where    查询条件(数组或者闭包)
+     * @param  mixed  $fields   字段
+     * @param  string $joinType JOIN类型
+     * @param  Query  $query    Query对象
+     * @return Query
+     */
+    public static function hasWhere(string $relation, $where = [], string $fields = '*', string $joinType = '', Query $query = null): Query
+    {
+        return (new static())
+            ->$relation()
+            ->hasWhere($where, $fields, $joinType, $query);
+    }
+
+    /**
+     * 预载入关联查询 JOIN方式
+     * @access public
+     * @param  Query   $query    Query对象
+     * @param  string  $relation 关联方法名
+     * @param  mixed   $field    字段
+     * @param  string  $joinType JOIN类型
+     * @param  Closure $closure  闭包
+     * @param  bool    $first
+     * @return bool
+     */
+    public function eagerly(Query $query, string $relation, $field, string $joinType = '', Closure $closure = null, bool $first = false): bool
+    {
+        $relation = Str::camel($relation);
+        $class    = $this->$relation();
+
+        if ($class instanceof OneToOne) {
+            $class->eagerly($query, $relation, $field, $joinType, $closure, $first);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * 预载入关联查询 返回数据集
+     * @access public
+     * @param  array  $resultSet 数据集
+     * @param  string $relation  关联名
+     * @param  array  $withRelationAttr 关联获取器
+     * @param  bool   $join      是否为JOIN方式
+     * @param  mixed  $cache     关联缓存
+     * @return void
+     */
+    public function eagerlyResultSet(array &$resultSet, array $relations, array $withRelationAttr = [], bool $join = false, $cache = false): void
+    {
+        foreach ($relations as $key => $relation) {
+            $subRelation = [];
+            $closure     = null;
+
+            if ($relation instanceof Closure) {
+                $closure  = $relation;
+                $relation = $key;
+            }
+
+            if (is_array($relation)) {
+                $subRelation = $relation;
+                $relation    = $key;
+            } elseif (strpos($relation, '.')) {
+                [$relation, $subRelation] = explode('.', $relation, 2);
+
+                $subRelation = [$subRelation];
+            }
+
+            $relationName = $relation;
+            $relation     = Str::camel($relation);
+
+            $relationResult = $this->$relation();
+
+            if (isset($withRelationAttr[$relationName])) {
+                $relationResult->withAttr($withRelationAttr[$relationName]);
+            }
+
+            if (is_scalar($cache)) {
+                $relationCache = [$cache];
+            } else {
+                $relationCache = $cache[$relationName] ?? $cache;
+            }
+
+            $relationResult->eagerlyResultSet($resultSet, $relationName, $subRelation, $closure, $relationCache, $join);
+        }
+    }
+
+    /**
+     * 预载入关联查询 返回模型对象
+     * @access public
+     * @param  Model $result    数据对象
+     * @param  array $relations 关联
+     * @param  array $withRelationAttr 关联获取器
+     * @param  bool  $join      是否为JOIN方式
+     * @param  mixed $cache     关联缓存
+     * @return void
+     */
+    public function eagerlyResult(Model $result, array $relations, array $withRelationAttr = [], bool $join = false, $cache = false): void
+    {
+        foreach ($relations as $key => $relation) {
+            $subRelation = [];
+            $closure     = null;
+
+            if ($relation instanceof Closure) {
+                $closure  = $relation;
+                $relation = $key;
+            }
+
+            if (is_array($relation)) {
+                $subRelation = $relation;
+                $relation    = $key;
+            } elseif (strpos($relation, '.')) {
+                [$relation, $subRelation] = explode('.', $relation, 2);
+
+                $subRelation = [$subRelation];
+            }
+
+            $relationName = $relation;
+            $relation     = Str::camel($relation);
+
+            $relationResult = $this->$relation();
+
+            if (isset($withRelationAttr[$relationName])) {
+                $relationResult->withAttr($withRelationAttr[$relationName]);
+            }
+
+            if (is_scalar($cache)) {
+                $relationCache = [$cache];
+            } else {
+                $relationCache = $cache[$relationName] ?? [];
+            }
+
+            $relationResult->eagerlyResult($result, $relationName, $subRelation, $closure, $relationCache, $join);
+        }
+    }
+
+    /**
+     * 绑定(一对一)关联属性到当前模型
+     * @access protected
+     * @param  string   $relation    关联名称
+     * @param  array    $attrs       绑定属性
+     * @return $this
+     * @throws Exception
+     */
+    public function bindAttr(string $relation, array $attrs = [])
+    {
+        $relation = $this->getRelation($relation);
+
+        foreach ($attrs as $key => $attr) {
+            $key   = is_numeric($key) ? $attr : $key;
+            $value = $this->getOrigin($key);
+
+            if (!is_null($value)) {
+                throw new Exception('bind attr has exists:' . $key);
+            }
+
+            $this->set($key, $relation ? $relation->$attr : null);
+        }
+
+        return $this;
+    }
+
+    /**
+     * 关联统计
+     * @access public
+     * @param  Query  $query       查询对象
+     * @param  array  $relations   关联名
+     * @param  string $aggregate   聚合查询方法
+     * @param  string $field       字段
+     * @param  bool   $useSubQuery 子查询
+     * @return void
+     */
+    public function relationCount(Query $query, array $relations, string $aggregate = 'sum', string $field = '*', bool $useSubQuery = true): void
+    {
+        foreach ($relations as $key => $relation) {
+            $closure = $name = null;
+
+            if ($relation instanceof Closure) {
+                $closure  = $relation;
+                $relation = $key;
+            } elseif (is_string($key)) {
+                $name     = $relation;
+                $relation = $key;
+            }
+
+            $relation = Str::camel($relation);
+
+            if ($useSubQuery) {
+                $count = $this->$relation()->getRelationCountQuery($closure, $aggregate, $field, $name);
+            } else {
+                $count = $this->$relation()->relationCount($this, $closure, $aggregate, $field, $name);
+            }
+
+            if (empty($name)) {
+                $name = Str::snake($relation) . '_' . $aggregate;
+            }
+
+            if ($useSubQuery) {
+                $query->field(['(' . $count . ')' => $name]);
+            } else {
+                $this->setAttr($name, $count);
+            }
+        }
+    }
+
+    /**
+     * HAS ONE 关联定义
+     * @access public
+     * @param  string $model      模型名
+     * @param  string $foreignKey 关联外键
+     * @param  string $localKey   当前主键
+     * @return HasOne
+     */
+    public function hasOne(string $model, string $foreignKey = '', string $localKey = ''): HasOne
+    {
+        // 记录当前关联信息
+        $model      = $this->parseModel($model);
+        $localKey   = $localKey ?: $this->getPk();
+        $foreignKey = $foreignKey ?: $this->getForeignKey($this->name);
+
+        return new HasOne($this, $model, $foreignKey, $localKey);
+    }
+
+    /**
+     * BELONGS TO 关联定义
+     * @access public
+     * @param  string $model      模型名
+     * @param  string $foreignKey 关联外键
+     * @param  string $localKey   关联主键
+     * @return BelongsTo
+     */
+    public function belongsTo(string $model, string $foreignKey = '', string $localKey = ''): BelongsTo
+    {
+        // 记录当前关联信息
+        $model      = $this->parseModel($model);
+        $foreignKey = $foreignKey ?: $this->getForeignKey((new $model)->getName());
+        $localKey   = $localKey ?: (new $model)->getPk();
+        $trace      = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
+        $relation   = Str::snake($trace[1]['function']);
+
+        return new BelongsTo($this, $model, $foreignKey, $localKey, $relation);
+    }
+
+    /**
+     * HAS MANY 关联定义
+     * @access public
+     * @param  string $model      模型名
+     * @param  string $foreignKey 关联外键
+     * @param  string $localKey   当前主键
+     * @return HasMany
+     */
+    public function hasMany(string $model, string $foreignKey = '', string $localKey = ''): HasMany
+    {
+        // 记录当前关联信息
+        $model      = $this->parseModel($model);
+        $localKey   = $localKey ?: $this->getPk();
+        $foreignKey = $foreignKey ?: $this->getForeignKey($this->name);
+
+        return new HasMany($this, $model, $foreignKey, $localKey);
+    }
+
+    /**
+     * HAS MANY 远程关联定义
+     * @access public
+     * @param  string $model      模型名
+     * @param  string $through    中间模型名
+     * @param  string $foreignKey 关联外键
+     * @param  string $throughKey 关联外键
+     * @param  string $localKey   当前主键
+     * @param  string $throughPk  中间表主键
+     * @return HasManyThrough
+     */
+    public function hasManyThrough(string $model, string $through, string $foreignKey = '', string $throughKey = '', string $localKey = '', string $throughPk = ''): HasManyThrough
+    {
+        // 记录当前关联信息
+        $model      = $this->parseModel($model);
+        $through    = $this->parseModel($through);
+        $localKey   = $localKey ?: $this->getPk();
+        $foreignKey = $foreignKey ?: $this->getForeignKey($this->name);
+        $throughKey = $throughKey ?: $this->getForeignKey((new $through)->getName());
+        $throughPk  = $throughPk ?: (new $through)->getPk();
+
+        return new HasManyThrough($this, $model, $through, $foreignKey, $throughKey, $localKey, $throughPk);
+    }
+
+    /**
+     * HAS ONE 远程关联定义
+     * @access public
+     * @param  string $model      模型名
+     * @param  string $through    中间模型名
+     * @param  string $foreignKey 关联外键
+     * @param  string $throughKey 关联外键
+     * @param  string $localKey   当前主键
+     * @param  string $throughPk  中间表主键
+     * @return HasOneThrough
+     */
+    public function hasOneThrough(string $model, string $through, string $foreignKey = '', string $throughKey = '', string $localKey = '', string $throughPk = ''): HasOneThrough
+    {
+        // 记录当前关联信息
+        $model      = $this->parseModel($model);
+        $through    = $this->parseModel($through);
+        $localKey   = $localKey ?: $this->getPk();
+        $foreignKey = $foreignKey ?: $this->getForeignKey($this->name);
+        $throughKey = $throughKey ?: $this->getForeignKey((new $through)->getName());
+        $throughPk  = $throughPk ?: (new $through)->getPk();
+
+        return new HasOneThrough($this, $model, $through, $foreignKey, $throughKey, $localKey, $throughPk);
+    }
+
+    /**
+     * BELONGS TO MANY 关联定义
+     * @access public
+     * @param  string $model      模型名
+     * @param  string $middle     中间表/模型名
+     * @param  string $foreignKey 关联外键
+     * @param  string $localKey   当前模型关联键
+     * @return BelongsToMany
+     */
+    public function belongsToMany(string $model, string $middle = '', string $foreignKey = '', string $localKey = ''): BelongsToMany
+    {
+        // 记录当前关联信息
+        $model      = $this->parseModel($model);
+        $name       = Str::snake(class_basename($model));
+        $middle     = $middle ?: Str::snake($this->name) . '_' . $name;
+        $foreignKey = $foreignKey ?: $name . '_id';
+        $localKey   = $localKey ?: $this->getForeignKey($this->name);
+
+        return new BelongsToMany($this, $model, $middle, $foreignKey, $localKey);
+    }
+
+    /**
+     * MORPH  One 关联定义
+     * @access public
+     * @param  string       $model 模型名
+     * @param  string|array $morph 多态字段信息
+     * @param  string       $type  多态类型
+     * @return MorphOne
+     */
+    public function morphOne(string $model, $morph = null, string $type = ''): MorphOne
+    {
+        // 记录当前关联信息
+        $model = $this->parseModel($model);
+
+        if (is_null($morph)) {
+            $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
+            $morph = Str::snake($trace[1]['function']);
+        }
+
+        if (is_array($morph)) {
+            [$morphType, $foreignKey] = $morph;
+        } else {
+            $morphType  = $morph . '_type';
+            $foreignKey = $morph . '_id';
+        }
+
+        $type = $type ?: get_class($this);
+
+        return new MorphOne($this, $model, $foreignKey, $morphType, $type);
+    }
+
+    /**
+     * MORPH  MANY 关联定义
+     * @access public
+     * @param  string       $model 模型名
+     * @param  string|array $morph 多态字段信息
+     * @param  string       $type  多态类型
+     * @return MorphMany
+     */
+    public function morphMany(string $model, $morph = null, string $type = ''): MorphMany
+    {
+        // 记录当前关联信息
+        $model = $this->parseModel($model);
+
+        if (is_null($morph)) {
+            $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
+            $morph = Str::snake($trace[1]['function']);
+        }
+
+        $type = $type ?: get_class($this);
+
+        if (is_array($morph)) {
+            [$morphType, $foreignKey] = $morph;
+        } else {
+            $morphType  = $morph . '_type';
+            $foreignKey = $morph . '_id';
+        }
+
+        return new MorphMany($this, $model, $foreignKey, $morphType, $type);
+    }
+
+    /**
+     * MORPH TO 关联定义
+     * @access public
+     * @param  string|array $morph 多态字段信息
+     * @param  array        $alias 多态别名定义
+     * @return MorphTo
+     */
+    public function morphTo($morph = null, array $alias = []): MorphTo
+    {
+        $trace    = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
+        $relation = Str::snake($trace[1]['function']);
+
+        if (is_null($morph)) {
+            $morph = $relation;
+        }
+
+        // 记录当前关联信息
+        if (is_array($morph)) {
+            [$morphType, $foreignKey] = $morph;
+        } else {
+            $morphType  = $morph . '_type';
+            $foreignKey = $morph . '_id';
+        }
+
+        return new MorphTo($this, $morphType, $foreignKey, $alias, $relation);
+    }
+
+    /**
+     * MORPH TO MANY关联定义
+     * @access public
+     * @param  string       $model 模型名
+     * @param  string       $middle 中间表名/模型名
+     * @param  string|array $morph 多态字段信息
+     * @param  string       $localKey   当前模型关联键
+     * @return MorphToMany
+     */
+    public function morphToMany(string $model, string $middle, $morph = null, string $localKey = null): MorphToMany
+    {
+        if (is_null($morph)) {
+            $morph = $middle;
+        }
+
+        // 记录当前关联信息
+        if (is_array($morph)) {
+            [$morphType, $morphKey] = $morph;
+        } else {
+            $morphType = $morph . '_type';
+            $morphKey  = $morph . '_id';
+        }
+
+        $model    = $this->parseModel($model);
+        $name     = Str::snake(class_basename($model));
+        $localKey = $localKey ?: $this->getForeignKey($name);
+
+        return new MorphToMany($this, $model, $middle, $morphType, $morphKey, $localKey);
+    }
+
+    /**
+     * MORPH BY MANY关联定义
+     * @access public
+     * @param  string       $model 模型名
+     * @param  string       $middle 中间表名/模型名
+     * @param  string|array $morph 多态字段信息
+     * @param  string       $foreignKey 关联外键
+     * @return MorphToMany
+     */
+    public function morphByMany(string $model, string $middle, $morph = null, string $foreignKey = null): MorphToMany
+    {
+        if (is_null($morph)) {
+            $morph = $middle;
+        }
+
+        // 记录当前关联信息
+        if (is_array($morph)) {
+            [$morphType, $morphKey] = $morph;
+        } else {
+            $morphType = $morph . '_type';
+            $morphKey  = $morph . '_id';
+        }
+
+        $model      = $this->parseModel($model);
+        $foreignKey = $foreignKey ?: $this->getForeignKey($this->name);
+
+        return new MorphToMany($this, $model, $middle, $morphType, $morphKey, $foreignKey, true);
+    }
+
+    /**
+     * 解析模型的完整命名空间
+     * @access protected
+     * @param  string $model 模型名(或者完整类名)
+     * @return string
+     */
+    protected function parseModel(string $model): string
+    {
+        if (false === strpos($model, '\\')) {
+            $path = explode('\\', static::class);
+            array_pop($path);
+            array_push($path, Str::studly($model));
+            $model = implode('\\', $path);
+        }
+
+        return $model;
+    }
+
+    /**
+     * 获取模型的默认外键名
+     * @access protected
+     * @param  string $name 模型名
+     * @return string
+     */
+    protected function getForeignKey(string $name): string
+    {
+        if (strpos($name, '\\')) {
+            $name = class_basename($name);
+        }
+
+        return Str::snake($name) . '_id';
+    }
+
+    /**
+     * 检查属性是否为关联属性 如果是则返回关联方法名
+     * @access protected
+     * @param  string $attr 关联属性名
+     * @return string|false
+     */
+    protected function isRelationAttr(string $attr)
+    {
+        $relation = Str::camel($attr);
+
+        if ((method_exists($this, $relation) && !method_exists('think\Model', $relation)) || isset(static::$macro[static::class][$relation])) {
+            return $relation;
+        }
+
+        return false;
+    }
+
+    /**
+     * 智能获取关联模型数据
+     * @access protected
+     * @param  Relation $modelRelation 模型关联对象
+     * @return mixed
+     */
+    protected function getRelationData(Relation $modelRelation)
+    {
+        if ($this->parent && !$modelRelation->isSelfRelation()
+            && get_class($this->parent) == get_class($modelRelation->getModel())) {
+            return $this->parent;
+        }
+
+        // 获取关联数据
+        return $modelRelation->getRelation();
+    }
+
+    /**
+     * 关联数据自动写入检查
+     * @access protected
+     * @return void
+     */
+    protected function checkAutoRelationWrite(): void
+    {
+        foreach ($this->together as $key => $name) {
+            if (is_array($name)) {
+                if (key($name) === 0) {
+                    $this->relationWrite[$key] = [];
+                    // 绑定关联属性
+                    foreach ($name as $val) {
+                        if (isset($this->data[$val])) {
+                            $this->relationWrite[$key][$val] = $this->data[$val];
+                        }
+                    }
+                } else {
+                    // 直接传入关联数据
+                    $this->relationWrite[$key] = $name;
+                }
+            } elseif (isset($this->relation[$name])) {
+                $this->relationWrite[$name] = $this->relation[$name];
+            } elseif (isset($this->data[$name])) {
+                $this->relationWrite[$name] = $this->data[$name];
+                unset($this->data[$name]);
+            }
+        }
+    }
+
+    /**
+     * 自动关联数据更新(针对一对一关联)
+     * @access protected
+     * @return void
+     */
+    protected function autoRelationUpdate(): void
+    {
+        foreach ($this->relationWrite as $name => $val) {
+            if ($val instanceof Model) {
+                $val->exists(true)->save();
+            } else {
+                $model = $this->getRelation($name, true);
+
+                if ($model instanceof Model) {
+                    $model->exists(true)->save($val);
+                }
+            }
+        }
+    }
+
+    /**
+     * 自动关联数据写入(针对一对一关联)
+     * @access protected
+     * @return void
+     */
+    protected function autoRelationInsert(): void
+    {
+        foreach ($this->relationWrite as $name => $val) {
+            $method = Str::camel($name);
+            $this->$method()->save($val);
+        }
+    }
+
+    /**
+     * 自动关联数据删除(支持一对一及一对多关联)
+     * @access protected
+     * @return void
+     */
+    protected function autoRelationDelete(): void
+    {
+        foreach ($this->relationWrite as $key => $name) {
+            $name   = is_numeric($key) ? $name : $key;
+            $result = $this->getRelation($name, true);
+
+            if ($result instanceof Model) {
+                $result->delete();
+            } elseif ($result instanceof Collection) {
+                foreach ($result as $model) {
+                    $model->delete();
+                }
+            }
+        }
+    }
+
+    /**
+     * 移除当前模型的关联属性
+     * @access public
+     * @return $this
+     */
+    public function removeRelation()
+    {
+        $this->relation = [];
+        return $this;
+    }
+}
diff --git a/vendor/topthink/think-orm/src/model/concern/SoftDelete.php b/vendor/topthink/think-orm/src/model/concern/SoftDelete.php
new file mode 100755
index 0000000..ce5d392
--- /dev/null
+++ b/vendor/topthink/think-orm/src/model/concern/SoftDelete.php
@@ -0,0 +1,248 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\model\concern;
+
+use think\db\BaseQuery as Query;
+use think\Model;
+
+/**
+ * 数据软删除
+ * @mixin Model
+ */
+trait SoftDelete
+{
+    /**
+     * 是否包含软删除数据
+     * @var bool
+     */
+    protected $withTrashed = false;
+
+    /**
+     * 判断当前实例是否被软删除
+     * @access public
+     * @return bool
+     */
+    public function trashed(): bool
+    {
+        $field = $this->getDeleteTimeField();
+
+        if ($field && !empty($this->getOrigin($field))) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * 查询软删除数据
+     * @access public
+     * @return Query
+     */
+    public static function withTrashed(): Query
+    {
+        $model = new static();
+
+        return $model->withTrashedData(true)->db();
+    }
+
+    /**
+     * 是否包含软删除数据
+     * @access protected
+     * @param  bool $withTrashed 是否包含软删除数据
+     * @return $this
+     */
+    protected function withTrashedData(bool $withTrashed)
+    {
+        $this->withTrashed = $withTrashed;
+        return $this;
+    }
+
+    /**
+     * 只查询软删除数据
+     * @access public
+     * @return Query
+     */
+    public static function onlyTrashed(): Query
+    {
+        $model = new static();
+        $field = $model->getDeleteTimeField(true);
+
+        if ($field) {
+            return $model
+                ->db()
+                ->useSoftDelete($field, $model->getWithTrashedExp());
+        }
+
+        return $model->db();
+    }
+
+    /**
+     * 获取软删除数据的查询条件
+     * @access protected
+     * @return array
+     */
+    protected function getWithTrashedExp(): array
+    {
+        return is_null($this->defaultSoftDelete) ? ['notnull', ''] : ['<>', $this->defaultSoftDelete];
+    }
+
+    /**
+     * 删除当前的记录
+     * @access public
+     * @return bool
+     */
+    public function delete(): bool
+    {
+        if (!$this->isExists() || $this->isEmpty() || false === $this->trigger('BeforeDelete')) {
+            return false;
+        }
+
+        $name = $this->getDeleteTimeField();
+
+        if ($name && !$this->isForce()) {
+            // 软删除
+            $this->set($name, $this->autoWriteTimestamp($name));
+
+            $result = $this->exists()->withEvent(false)->save();
+
+            $this->withEvent(true);
+        } else {
+            // 读取更新条件
+            $where = $this->getWhere();
+
+            // 删除当前模型数据
+            $result = $this->db()
+                ->where($where)
+                ->removeOption('soft_delete')
+                ->delete();
+
+            $this->lazySave(false);
+        }
+
+        // 关联删除
+        if (!empty($this->relationWrite)) {
+            $this->autoRelationDelete();
+        }
+
+        $this->trigger('AfterDelete');
+
+        $this->exists(false);
+
+        return true;
+    }
+
+    /**
+     * 删除记录
+     * @access public
+     * @param  mixed $data 主键列表 支持闭包查询条件
+     * @param  bool  $force 是否强制删除
+     * @return bool
+     */
+    public static function destroy($data, bool $force = false): bool
+    {
+        // 包含软删除数据
+        $query = (new static())->withTrashedData(true)->db(false);
+
+        if (is_array($data) && key($data) !== 0) {
+            $query->where($data);
+            $data = null;
+        } elseif ($data instanceof \Closure) {
+            call_user_func_array($data, [ & $query]);
+            $data = null;
+        } elseif (is_null($data)) {
+            return false;
+        }
+
+        $resultSet = $query->select($data);
+
+        foreach ($resultSet as $result) {
+            $result->force($force)->delete();
+        }
+
+        return true;
+    }
+
+    /**
+     * 恢复被软删除的记录
+     * @access public
+     * @param  array $where 更新条件
+     * @return bool
+     */
+    public function restore($where = []): bool
+    {
+        $name = $this->getDeleteTimeField();
+
+        if (!$name || false === $this->trigger('BeforeRestore')) {
+            return false;
+        }
+
+        if (empty($where)) {
+            $pk = $this->getPk();
+            if (is_string($pk)) {
+                $where[] = [$pk, '=', $this->getData($pk)];
+            }
+        }
+
+        // 恢复删除
+        $this->db(false)
+            ->where($where)
+            ->useSoftDelete($name, $this->getWithTrashedExp())
+            ->update([$name => $this->defaultSoftDelete]);
+
+        $this->trigger('AfterRestore');
+
+        return true;
+    }
+
+    /**
+     * 获取软删除字段
+     * @access protected
+     * @param  bool  $read 是否查询操作 写操作的时候会自动去掉表别名
+     * @return string|false
+     */
+    protected function getDeleteTimeField(bool $read = false)
+    {
+        $field = property_exists($this, 'deleteTime') && isset($this->deleteTime) ? $this->deleteTime : 'delete_time';
+
+        if (false === $field) {
+            return false;
+        }
+
+        if (false === strpos($field, '.')) {
+            $field = '__TABLE__.' . $field;
+        }
+
+        if (!$read && strpos($field, '.')) {
+            $array = explode('.', $field);
+            $field = array_pop($array);
+        }
+
+        return $field;
+    }
+
+    /**
+     * 查询的时候默认排除软删除数据
+     * @access protected
+     * @param  Query  $query
+     * @return void
+     */
+    protected function withNoTrashed(Query $query): void
+    {
+        $field = $this->getDeleteTimeField(true);
+
+        if ($field) {
+            $condition = is_null($this->defaultSoftDelete) ? ['null', ''] : ['=', $this->defaultSoftDelete];
+            $query->useSoftDelete($field, $condition);
+        }
+    }
+}
diff --git a/vendor/topthink/think-orm/src/model/concern/TimeStamp.php b/vendor/topthink/think-orm/src/model/concern/TimeStamp.php
new file mode 100755
index 0000000..e207961
--- /dev/null
+++ b/vendor/topthink/think-orm/src/model/concern/TimeStamp.php
@@ -0,0 +1,208 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\model\concern;
+
+use DateTime;
+
+/**
+ * 自动时间戳
+ */
+trait TimeStamp
+{
+    /**
+     * 是否需要自动写入时间戳 如果设置为字符串 则表示时间字段的类型
+     * @var bool|string
+     */
+    protected $autoWriteTimestamp;
+
+    /**
+     * 创建时间字段 false表示关闭
+     * @var false|string
+     */
+    protected $createTime = 'create_time';
+
+    /**
+     * 更新时间字段 false表示关闭
+     * @var false|string
+     */
+    protected $updateTime = 'update_time';
+
+    /**
+     * 时间字段显示格式
+     * @var string
+     */
+    protected $dateFormat;
+
+    /**
+     * 是否需要自动写入时间字段
+     * @access public
+     * @param  bool|string $auto
+     * @return $this
+     */
+    public function isAutoWriteTimestamp($auto)
+    {
+        $this->autoWriteTimestamp = $this->checkTimeFieldType($auto);
+
+        return $this;
+    }
+
+    /**
+     * 检测时间字段的实际类型
+     * @access public
+     * @param  bool|string $type
+     * @return mixed
+     */
+    protected function checkTimeFieldType($type)
+    {
+        if (true === $type) {
+            if (isset($this->type[$this->createTime])) {
+                $type = $this->type[$this->createTime];
+            } elseif (isset($this->schema[$this->createTime]) && in_array($this->schema[$this->createTime], ['datetime', 'date', 'timestamp', 'int'])) {
+                $type = $this->schema[$this->createTime];
+            } else {
+                $type = $this->getFieldType($this->createTime);
+            }
+        }
+
+        return $type;
+    }
+
+    /**
+     * 获取自动写入时间字段
+     * @access public
+     * @return bool|string
+     */
+    public function getAutoWriteTimestamp()
+    {
+        return $this->autoWriteTimestamp;
+    }
+
+    /**
+     * 设置时间字段格式化
+     * @access public
+     * @param  string|false $format
+     * @return $this
+     */
+    public function setDateFormat($format)
+    {
+        $this->dateFormat = $format;
+
+        return $this;
+    }
+
+    /**
+     * 获取自动写入时间字段
+     * @access public
+     * @return string|false
+     */
+    public function getDateFormat()
+    {
+        return $this->dateFormat;
+    }
+
+    /**
+     * 自动写入时间戳
+     * @access protected
+     * @return mixed
+     */
+    protected function autoWriteTimestamp()
+    {
+        // 检测时间字段类型
+        $type = $this->checkTimeFieldType($this->autoWriteTimestamp);
+
+        return is_string($type) ? $this->getTimeTypeValue($type) : time();
+    }
+
+    /**
+     * 获取指定类型的时间字段值
+     * @access protected
+     * @param  string $type 时间字段类型
+     * @return mixed
+     */
+    protected function getTimeTypeValue(string $type)
+    {
+        $value = time();
+
+        switch ($type) {
+            case 'datetime':
+            case 'date':
+            case 'timestamp':
+                $value = $this->formatDateTime('Y-m-d H:i:s.u');
+                break;
+            default:
+                if (false !== strpos($type, '\\')) {
+                    // 对象数据写入
+                    $obj = new $type();
+                    if (method_exists($obj, '__toString')) {
+                        // 对象数据写入
+                        $value = $obj->__toString();
+                    }
+                }
+        }
+
+        return $value;
+    }
+
+    /**
+     * 时间日期字段格式化处理
+     * @access protected
+     * @param  mixed $format    日期格式
+     * @param  mixed $time      时间日期表达式
+     * @param  bool  $timestamp 时间表达式是否为时间戳
+     * @return mixed
+     */
+    protected function formatDateTime($format, $time = 'now', bool $timestamp = false)
+    {
+        if (empty($time)) {
+            return;
+        }
+
+        if (false === $format) {
+            return $time;
+        } elseif (false !== strpos($format, '\\')) {
+            return new $format($time);
+        }
+
+        if ($time instanceof DateTime) {
+            $dateTime = $time;
+        } elseif ($timestamp) {
+            $dateTime = new DateTime();
+            $dateTime->setTimestamp((int) $time);
+        } else {
+            $dateTime = new DateTime($time);
+        }
+
+        return $dateTime->format($format);
+    }
+
+    /**
+     * 获取时间字段值
+     * @access protected
+     * @param  mixed   $value
+     * @return mixed
+     */
+    protected function getTimestampValue($value)
+    {
+        $type = $this->checkTimeFieldType($this->autoWriteTimestamp);
+
+        if (is_string($type) && in_array(strtolower($type), [
+            'datetime', 'date', 'timestamp',
+        ])) {
+            $value = $this->formatDateTime($this->dateFormat, $value);
+        } else {
+            $value = $this->formatDateTime($this->dateFormat, $value, true);
+        }
+
+        return $value;
+    }
+}
diff --git a/vendor/topthink/think-orm/src/model/relation/BelongsTo.php b/vendor/topthink/think-orm/src/model/relation/BelongsTo.php
new file mode 100755
index 0000000..789c944
--- /dev/null
+++ b/vendor/topthink/think-orm/src/model/relation/BelongsTo.php
@@ -0,0 +1,331 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\model\relation;
+
+use Closure;
+use think\db\BaseQuery as Query;
+use think\helper\Str;
+use think\Model;
+
+/**
+ * BelongsTo关联类
+ */
+class BelongsTo extends OneToOne
+{
+    /**
+     * 架构函数
+     * @access public
+     * @param  Model  $parent 上级模型对象
+     * @param  string $model 模型名
+     * @param  string $foreignKey 关联外键
+     * @param  string $localKey 关联主键
+     * @param  string $relation  关联名
+     */
+    public function __construct(Model $parent, string $model, string $foreignKey, string $localKey, string $relation = null)
+    {
+        $this->parent     = $parent;
+        $this->model      = $model;
+        $this->foreignKey = $foreignKey;
+        $this->localKey   = $localKey;
+        $this->query      = (new $model)->db();
+        $this->relation   = $relation;
+
+        if (get_class($parent) == $model) {
+            $this->selfRelation = true;
+        }
+    }
+
+    /**
+     * 延迟获取关联数据
+     * @access public
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包查询条件
+     * @return Model
+     */
+    public function getRelation(array $subRelation = [], Closure $closure = null)
+    {
+        if ($closure) {
+            $closure($this->getClosureType($closure));
+        }
+
+        $foreignKey = $this->foreignKey;
+
+        $relationModel = $this->query
+            ->removeWhereField($this->localKey)
+            ->where($this->localKey, $this->parent->$foreignKey)
+            ->relation($subRelation)
+            ->find();
+
+        if ($relationModel) {
+            if (!empty($this->bindAttr)) {
+                // 绑定关联属性
+                $this->bindAttr($this->parent, $relationModel);
+            }
+
+            $relationModel->setParent(clone $this->parent);
+        }
+
+        return $relationModel;
+    }
+
+    /**
+     * 创建关联统计子查询
+     * @access public
+     * @param  Closure $closure 闭包
+     * @param  string  $aggregate 聚合查询方法
+     * @param  string  $field 字段
+     * @param  string  $name 聚合字段别名
+     * @return string
+     */
+    public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', &$name = ''): string
+    {
+        if ($closure) {
+            $closure($this->getClosureType($closure), $name);
+        }
+
+        return $this->query
+            ->whereExp($this->localKey, '=' . $this->parent->getTable() . '.' . $this->foreignKey)
+            ->fetchSql()
+            ->$aggregate($field);
+    }
+
+    /**
+     * 关联统计
+     * @access public
+     * @param  Model   $result  数据对象
+     * @param  Closure $closure 闭包
+     * @param  string  $aggregate 聚合查询方法
+     * @param  string  $field 字段
+     * @param  string  $name 统计字段别名
+     * @return integer
+     */
+    public function relationCount(Model $result, Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null)
+    {
+        $foreignKey = $this->foreignKey;
+
+        if (!isset($result->$foreignKey)) {
+            return 0;
+        }
+
+        if ($closure) {
+            $closure($this->getClosureType($closure), $name);
+        }
+
+        return $this->query
+            ->where($this->localKey, '=', $result->$foreignKey)
+            ->$aggregate($field);
+    }
+
+    /**
+     * 根据关联条件查询当前模型
+     * @access public
+     * @param  string  $operator 比较操作符
+     * @param  integer $count    个数
+     * @param  string  $id       关联表的统计字段
+     * @param  string  $joinType JOIN类型
+     * @param  Query   $query    Query对象
+     * @return Query
+     */
+    public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '', Query $query = null): Query
+    {
+        $table      = $this->query->getTable();
+        $model      = class_basename($this->parent);
+        $relation   = class_basename($this->model);
+        $localKey   = $this->localKey;
+        $foreignKey = $this->foreignKey;
+        $softDelete = $this->query->getOptions('soft_delete');
+        $query      = $query ?: $this->parent->db()->alias($model);
+
+        return $query->whereExists(function ($query) use ($table, $model, $relation, $localKey, $foreignKey, $softDelete) {
+            $query->table([$table => $relation])
+                ->field($relation . '.' . $localKey)
+                ->whereExp($model . '.' . $foreignKey, '=' . $relation . '.' . $localKey)
+                ->when($softDelete, function ($query) use ($softDelete, $relation) {
+                    $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
+                });
+        });
+    }
+
+    /**
+     * 根据关联条件查询当前模型
+     * @access public
+     * @param  mixed  $where  查询条件(数组或者闭包)
+     * @param  mixed  $fields 字段
+     * @param  string $joinType JOIN类型
+     * @param  Query  $query    Query对象
+     * @return Query
+     */
+    public function hasWhere($where = [], $fields = null, string $joinType = '', Query $query = null): Query
+    {
+        $table    = $this->query->getTable();
+        $model    = class_basename($this->parent);
+        $relation = class_basename($this->model);
+
+        if (is_array($where)) {
+            $this->getQueryWhere($where, $relation);
+        } elseif ($where instanceof Query) {
+            $where->via($relation);
+        } elseif ($where instanceof Closure) {
+            $where($this->query->via($relation));
+            $where = $this->query;
+        }
+
+        $fields     = $this->getRelationQueryFields($fields, $model);
+        $softDelete = $this->query->getOptions('soft_delete');
+        $query      = $query ?: $this->parent->db()->alias($model);
+
+        return $query->field($fields)
+            ->join([$table => $relation], $model . '.' . $this->foreignKey . '=' . $relation . '.' . $this->localKey, $joinType ?: $this->joinType)
+            ->when($softDelete, function ($query) use ($softDelete, $relation) {
+                $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
+            })
+            ->where($where);
+    }
+
+    /**
+     * 预载入关联查询(数据集)
+     * @access protected
+     * @param  array   $resultSet 数据集
+     * @param  string  $relation 当前关联名
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure 闭包
+     * @param  array   $cache       关联缓存
+     * @return void
+     */
+    protected function eagerlySet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void
+    {
+        $localKey   = $this->localKey;
+        $foreignKey = $this->foreignKey;
+
+        $range = [];
+        foreach ($resultSet as $result) {
+            // 获取关联外键列表
+            if (isset($result->$foreignKey)) {
+                $range[] = $result->$foreignKey;
+            }
+        }
+
+        if (!empty($range)) {
+            $this->query->removeWhereField($localKey);
+
+            $data = $this->eagerlyWhere([
+                [$localKey, 'in', $range],
+            ], $localKey, $subRelation, $closure, $cache);
+
+            // 关联数据封装
+            foreach ($resultSet as $result) {
+                // 关联模型
+                if (!isset($data[$result->$foreignKey])) {
+                    $relationModel = null;
+                } else {
+                    $relationModel = $data[$result->$foreignKey];
+                    $relationModel->setParent(clone $result);
+                    $relationModel->exists(true);
+                }
+
+                if (!empty($this->bindAttr)) {
+                    // 绑定关联属性
+                    $this->bindAttr($result, $relationModel);
+                } else {
+                    // 设置关联属性
+                    $result->setRelation($relation, $relationModel);
+                }
+            }
+        }
+    }
+
+    /**
+     * 预载入关联查询(数据)
+     * @access protected
+     * @param  Model   $result 数据对象
+     * @param  string  $relation 当前关联名
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure 闭包
+     * @param  array   $cache       关联缓存
+     * @return void
+     */
+    protected function eagerlyOne(Model $result, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void
+    {
+        $localKey   = $this->localKey;
+        $foreignKey = $this->foreignKey;
+
+        $this->query->removeWhereField($localKey);
+
+        $data = $this->eagerlyWhere([
+            [$localKey, '=', $result->$foreignKey],
+        ], $localKey, $subRelation, $closure, $cache);
+
+        // 关联模型
+        if (!isset($data[$result->$foreignKey])) {
+            $relationModel = null;
+        } else {
+            $relationModel = $data[$result->$foreignKey];
+            $relationModel->setParent(clone $result);
+            $relationModel->exists(true);
+        }
+
+        if (!empty($this->bindAttr)) {
+            // 绑定关联属性
+            $this->bindAttr($result, $relationModel);
+        } else {
+            // 设置关联属性
+            $result->setRelation($relation, $relationModel);
+        }
+    }
+
+    /**
+     * 添加关联数据
+     * @access public
+     * @param  Model $model关联模型对象
+     * @return Model
+     */
+    public function associate(Model $model): Model
+    {
+        $this->parent->setAttr($this->foreignKey, $model->getKey());
+        $this->parent->save();
+
+        return $this->parent->setRelation($this->relation, $model);
+    }
+
+    /**
+     * 注销关联数据
+     * @access public
+     * @return Model
+     */
+    public function dissociate(): Model
+    {
+        $foreignKey = $this->foreignKey;
+
+        $this->parent->setAttr($foreignKey, null);
+        $this->parent->save();
+
+        return $this->parent->setRelation($this->relation, null);
+    }
+
+    /**
+     * 执行基础查询(仅执行一次)
+     * @access protected
+     * @return void
+     */
+    protected function baseQuery(): void
+    {
+        if (empty($this->baseQuery)) {
+            if (isset($this->parent->{$this->foreignKey})) {
+                // 关联查询带入关联条件
+                $this->query->where($this->localKey, '=', $this->parent->{$this->foreignKey});
+            }
+
+            $this->baseQuery = true;
+        }
+    }
+}
diff --git a/vendor/topthink/think-orm/src/model/relation/BelongsToMany.php b/vendor/topthink/think-orm/src/model/relation/BelongsToMany.php
new file mode 100755
index 0000000..a3c8aa4
--- /dev/null
+++ b/vendor/topthink/think-orm/src/model/relation/BelongsToMany.php
@@ -0,0 +1,677 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\model\relation;
+
+use Closure;
+use think\Collection;
+use think\db\BaseQuery as Query;
+use think\db\exception\DbException as Exception;
+use think\db\Raw;
+use think\Model;
+use think\model\Pivot;
+use think\model\Relation;
+use think\Paginator;
+
+/**
+ * 多对多关联类
+ */
+class BelongsToMany extends Relation
+{
+    /**
+     * 中间表表名
+     * @var string
+     */
+    protected $middle;
+
+    /**
+     * 中间表模型名称
+     * @var string
+     */
+    protected $pivotName;
+
+    /**
+     * 中间表模型对象
+     * @var Pivot
+     */
+    protected $pivot;
+
+    /**
+     * 中间表数据名称
+     * @var string
+     */
+    protected $pivotDataName = 'pivot';
+
+    /**
+     * 架构函数
+     * @access public
+     * @param  Model  $parent     上级模型对象
+     * @param  string $model      模型名
+     * @param  string $middle     中间表/模型名
+     * @param  string $foreignKey 关联模型外键
+     * @param  string $localKey   当前模型关联键
+     */
+    public function __construct(Model $parent, string $model, string $middle, string $foreignKey, string $localKey)
+    {
+        $this->parent     = $parent;
+        $this->model      = $model;
+        $this->foreignKey = $foreignKey;
+        $this->localKey   = $localKey;
+
+        if (false !== strpos($middle, '\\')) {
+            $this->pivotName = $middle;
+            $this->middle    = class_basename($middle);
+        } else {
+            $this->middle = $middle;
+        }
+
+        $this->query = (new $model)->db();
+        $this->pivot = $this->newPivot();
+    }
+
+    /**
+     * 设置中间表模型
+     * @access public
+     * @param  $pivot
+     * @return $this
+     */
+    public function pivot(string $pivot)
+    {
+        $this->pivotName = $pivot;
+        return $this;
+    }
+
+    /**
+     * 设置中间表数据名称
+     * @access public
+     * @param  string $name
+     * @return $this
+     */
+    public function name(string $name)
+    {
+        $this->pivotDataName = $name;
+        return $this;
+    }
+
+    /**
+     * 实例化中间表模型
+     * @access public
+     * @param  $data
+     * @return Pivot
+     * @throws Exception
+     */
+    protected function newPivot(array $data = []): Pivot
+    {
+        $class = $this->pivotName ?: Pivot::class;
+        $pivot = new $class($data, $this->parent, $this->middle);
+
+        if ($pivot instanceof Pivot) {
+            return $pivot;
+        } else {
+            throw new Exception('pivot model must extends: \think\model\Pivot');
+        }
+    }
+
+    /**
+     * 合成中间表模型
+     * @access protected
+     * @param  array|Collection|Paginator $models
+     */
+    protected function hydratePivot(iterable $models)
+    {
+        foreach ($models as $model) {
+            $pivot = [];
+
+            foreach ($model->getData() as $key => $val) {
+                if (strpos($key, '__')) {
+                    [$name, $attr] = explode('__', $key, 2);
+
+                    if ('pivot' == $name) {
+                        $pivot[$attr] = $val;
+                        unset($model->$key);
+                    }
+                }
+            }
+
+            $model->setRelation($this->pivotDataName, $this->newPivot($pivot));
+        }
+    }
+
+    /**
+     * 延迟获取关联数据
+     * @access public
+     * @param  array    $subRelation 子关联名
+     * @param  Closure  $closure     闭包查询条件
+     * @return Collection
+     */
+    public function getRelation(array $subRelation = [], Closure $closure = null): Collection
+    {
+        if ($closure) {
+            $closure($this->getClosureType($closure));
+        }
+
+        $result = $this->relation($subRelation)
+            ->select()
+            ->setParent(clone $this->parent);
+
+        $this->hydratePivot($result);
+
+        return $result;
+    }
+
+    /**
+     * 重载select方法
+     * @access public
+     * @param  mixed $data
+     * @return Collection
+     */
+    public function select($data = null): Collection
+    {
+        $this->baseQuery();
+        $result = $this->query->select($data);
+        $this->hydratePivot($result);
+
+        return $result;
+    }
+
+    /**
+     * 重载paginate方法
+     * @access public
+     * @param  int|array $listRows
+     * @param  int|bool  $simple
+     * @return Paginator
+     */
+    public function paginate($listRows = null, $simple = false): Paginator
+    {
+        $this->baseQuery();
+        $result = $this->query->paginate($listRows, $simple);
+        $this->hydratePivot($result);
+
+        return $result;
+    }
+
+    /**
+     * 重载find方法
+     * @access public
+     * @param  mixed $data
+     * @return Model
+     */
+    public function find($data = null)
+    {
+        $this->baseQuery();
+        $result = $this->query->find($data);
+
+        if ($result && !$result->isEmpty()) {
+            $this->hydratePivot([$result]);
+        }
+
+        return $result;
+    }
+
+    /**
+     * 根据关联条件查询当前模型
+     * @access public
+     * @param  string  $operator 比较操作符
+     * @param  integer $count    个数
+     * @param  string  $id       关联表的统计字段
+     * @param  string  $joinType JOIN类型
+     * @param  Query   $query    Query对象
+     * @return Model
+     */
+    public function has(string $operator = '>=', $count = 1, $id = '*', string $joinType = 'INNER', Query $query = null)
+    {
+        return $this->parent;
+    }
+
+    /**
+     * 根据关联条件查询当前模型
+     * @access public
+     * @param  mixed  $where 查询条件(数组或者闭包)
+     * @param  mixed  $fields 字段
+     * @param  string $joinType JOIN类型
+     * @param  Query  $query    Query对象
+     * @return Query
+     * @throws Exception
+     */
+    public function hasWhere($where = [], $fields = null, string $joinType = '', Query $query = null)
+    {
+        throw new Exception('relation not support: hasWhere');
+    }
+
+    /**
+     * 设置中间表的查询条件
+     * @access public
+     * @param  string $field
+     * @param  string $op
+     * @param  mixed  $condition
+     * @return $this
+     */
+    public function wherePivot($field, $op = null, $condition = null)
+    {
+        $this->query->where('pivot.' . $field, $op, $condition);
+        return $this;
+    }
+
+    /**
+     * 预载入关联查询(数据集)
+     * @access public
+     * @param  array   $resultSet   数据集
+     * @param  string  $relation    当前关联名
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包
+     * @param  array   $cache       关联缓存
+     * @return void
+     */
+    public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null, array $cache = []): void
+    {
+        $localKey = $this->localKey;
+        $pk       = $resultSet[0]->getPk();
+        $range    = [];
+
+        foreach ($resultSet as $result) {
+            // 获取关联外键列表
+            if (isset($result->$pk)) {
+                $range[] = $result->$pk;
+            }
+        }
+
+        if (!empty($range)) {
+            // 查询关联数据
+            $data = $this->eagerlyManyToMany([
+                ['pivot.' . $localKey, 'in', $range],
+            ], $subRelation, $closure, $cache);
+
+            // 关联数据封装
+            foreach ($resultSet as $result) {
+                if (!isset($data[$result->$pk])) {
+                    $data[$result->$pk] = [];
+                }
+
+                $result->setRelation($relation, $this->resultSetBuild($data[$result->$pk], clone $this->parent));
+            }
+        }
+    }
+
+    /**
+     * 预载入关联查询(单个数据)
+     * @access public
+     * @param  Model   $result      数据对象
+     * @param  string  $relation    当前关联名
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包
+     * @param  array   $cache       关联缓存
+     * @return void
+     */
+    public function eagerlyResult(Model $result, string $relation, array $subRelation, Closure $closure = null, array $cache = []): void
+    {
+        $pk = $result->getPk();
+
+        if (isset($result->$pk)) {
+            $pk = $result->$pk;
+            // 查询管理数据
+            $data = $this->eagerlyManyToMany([
+                ['pivot.' . $this->localKey, '=', $pk],
+            ], $subRelation, $closure, $cache);
+
+            // 关联数据封装
+            if (!isset($data[$pk])) {
+                $data[$pk] = [];
+            }
+
+            $result->setRelation($relation, $this->resultSetBuild($data[$pk], clone $this->parent));
+        }
+    }
+
+    /**
+     * 关联统计
+     * @access public
+     * @param  Model   $result  数据对象
+     * @param  Closure $closure 闭包
+     * @param  string  $aggregate 聚合查询方法
+     * @param  string  $field 字段
+     * @param  string  $name 统计字段别名
+     * @return integer
+     */
+    public function relationCount(Model $result, Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): float
+    {
+        $pk = $result->getPk();
+
+        if (!isset($result->$pk)) {
+            return 0;
+        }
+
+        $pk = $result->$pk;
+
+        if ($closure) {
+            $closure($this->getClosureType($closure), $name);
+        }
+
+        return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [
+            ['pivot.' . $this->localKey, '=', $pk],
+        ])->$aggregate($field);
+    }
+
+    /**
+     * 获取关联统计子查询
+     * @access public
+     * @param  Closure $closure 闭包
+     * @param  string  $aggregate 聚合查询方法
+     * @param  string  $field 字段
+     * @param  string  $name 统计字段别名
+     * @return string
+     */
+    public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string
+    {
+        if ($closure) {
+            $closure($this->getClosureType($closure), $name);
+        }
+
+        return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [
+            [
+                'pivot.' . $this->localKey, 'exp', new Raw('=' . $this->parent->db(false)->getTable() . '.' . $this->parent->getPk()),
+            ],
+        ])->fetchSql()->$aggregate($field);
+    }
+
+    /**
+     * 多对多 关联模型预查询
+     * @access protected
+     * @param  array   $where       关联预查询条件
+     * @param  array   $subRelation 子关联
+     * @param  Closure $closure     闭包
+     * @param  array   $cache       关联缓存
+     * @return array
+     */
+    protected function eagerlyManyToMany(array $where, array $subRelation = [], Closure $closure = null, array $cache = []): array
+    {
+        if ($closure) {
+            $closure($this->getClosureType($closure));
+        }
+
+        // 预载入关联查询 支持嵌套预载入
+        $list = $this->belongsToManyQuery($this->foreignKey, $this->localKey, $where)
+            ->with($subRelation)
+            ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null)
+            ->select();
+
+        // 组装模型数据
+        $data = [];
+        foreach ($list as $set) {
+            $pivot = [];
+            foreach ($set->getData() as $key => $val) {
+                if (strpos($key, '__')) {
+                    [$name, $attr] = explode('__', $key, 2);
+                    if ('pivot' == $name) {
+                        $pivot[$attr] = $val;
+                        unset($set->$key);
+                    }
+                }
+            }
+            $key = $pivot[$this->localKey];
+
+            if ($this->withLimit && isset($data[$key]) && count($data[$key]) >= $this->withLimit) {
+                continue;
+            }
+
+            $set->setRelation($this->pivotDataName, $this->newPivot($pivot));
+
+            $data[$key][] = $set;
+        }
+
+        return $data;
+    }
+
+    /**
+     * BELONGS TO MANY 关联查询
+     * @access protected
+     * @param  string $foreignKey 关联模型关联键
+     * @param  string $localKey   当前模型关联键
+     * @param  array  $condition  关联查询条件
+     * @return Query
+     */
+    protected function belongsToManyQuery(string $foreignKey, string $localKey, array $condition = []): Query
+    {
+        // 关联查询封装
+        $tableName = $this->query->getTable();
+        $table     = $this->pivot->db()->getTable();
+        $fields    = $this->getQueryFields($tableName);
+
+        if ($this->withLimit) {
+            $this->query->limit($this->withLimit);
+        }
+
+        $this->query
+            ->field($fields)
+            ->tableField(true, $table, 'pivot', 'pivot__')
+            ->join([$table => 'pivot'], 'pivot.' . $foreignKey . '=' . $tableName . '.' . $this->query->getPk())
+            ->where($condition);
+
+        return $this->query;
+    }
+
+    /**
+     * 保存(新增)当前关联数据对象
+     * @access public
+     * @param  mixed $data  数据 可以使用数组 关联模型对象 和 关联对象的主键
+     * @param  array $pivot 中间表额外数据
+     * @return array|Pivot
+     */
+    public function save($data, array $pivot = [])
+    {
+        // 保存关联表/中间表数据
+        return $this->attach($data, $pivot);
+    }
+
+    /**
+     * 批量保存当前关联数据对象
+     * @access public
+     * @param  iterable $dataSet   数据集
+     * @param  array    $pivot     中间表额外数据
+     * @param  bool     $samePivot 额外数据是否相同
+     * @return array|false
+     */
+    public function saveAll(iterable $dataSet, array $pivot = [], bool $samePivot = false)
+    {
+        $result = [];
+
+        foreach ($dataSet as $key => $data) {
+            if (!$samePivot) {
+                $pivotData = $pivot[$key] ?? [];
+            } else {
+                $pivotData = $pivot;
+            }
+
+            $result[] = $this->attach($data, $pivotData);
+        }
+
+        return empty($result) ? false : $result;
+    }
+
+    /**
+     * 附加关联的一个中间表数据
+     * @access public
+     * @param  mixed $data  数据 可以使用数组、关联模型对象 或者 关联对象的主键
+     * @param  array $pivot 中间表额外数据
+     * @return array|Pivot
+     * @throws Exception
+     */
+    public function attach($data, array $pivot = [])
+    {
+        if (is_array($data)) {
+            if (key($data) === 0) {
+                $id = $data;
+            } else {
+                // 保存关联表数据
+                $model = new $this->model;
+                $id    = $model->insertGetId($data);
+            }
+        } elseif (is_numeric($data) || is_string($data)) {
+            // 根据关联表主键直接写入中间表
+            $id = $data;
+        } elseif ($data instanceof Model) {
+            // 根据关联表主键直接写入中间表
+            $id = $data->getKey();
+        }
+
+        if (!empty($id)) {
+            // 保存中间表数据
+            $pivot[$this->localKey] = $this->parent->getKey();
+
+            $ids = (array) $id;
+            foreach ($ids as $id) {
+                $pivot[$this->foreignKey] = $id;
+                $this->pivot->replace()
+                    ->exists(false)
+                    ->data([])
+                    ->save($pivot);
+                $result[] = $this->newPivot($pivot);
+            }
+
+            if (count($result) == 1) {
+                // 返回中间表模型对象
+                $result = $result[0];
+            }
+
+            return $result;
+        } else {
+            throw new Exception('miss relation data');
+        }
+    }
+
+    /**
+     * 判断是否存在关联数据
+     * @access public
+     * @param  mixed $data 数据 可以使用关联模型对象 或者 关联对象的主键
+     * @return Pivot|false
+     */
+    public function attached($data)
+    {
+        if ($data instanceof Model) {
+            $id = $data->getKey();
+        } else {
+            $id = $data;
+        }
+
+        $pivot = $this->pivot
+            ->where($this->localKey, $this->parent->getKey())
+            ->where($this->foreignKey, $id)
+            ->find();
+
+        return $pivot ?: false;
+    }
+
+    /**
+     * 解除关联的一个中间表数据
+     * @access public
+     * @param  integer|array $data        数据 可以使用关联对象的主键
+     * @param  bool          $relationDel 是否同时删除关联表数据
+     * @return integer
+     */
+    public function detach($data = null, bool $relationDel = false): int
+    {
+        if (is_array($data)) {
+            $id = $data;
+        } elseif (is_numeric($data) || is_string($data)) {
+            // 根据关联表主键直接写入中间表
+            $id = $data;
+        } elseif ($data instanceof Model) {
+            // 根据关联表主键直接写入中间表
+            $id = $data->getKey();
+        }
+
+        // 删除中间表数据
+        $pivot   = [];
+        $pivot[] = [$this->localKey, '=', $this->parent->getKey()];
+
+        if (isset($id)) {
+            $pivot[] = [$this->foreignKey, is_array($id) ? 'in' : '=', $id];
+        }
+
+        $result = $this->pivot->where($pivot)->delete();
+
+        // 删除关联表数据
+        if (isset($id) && $relationDel) {
+            $model = $this->model;
+            $model::destroy($id);
+        }
+
+        return $result;
+    }
+
+    /**
+     * 数据同步
+     * @access public
+     * @param  array $ids
+     * @param  bool  $detaching
+     * @return array
+     */
+    public function sync(array $ids, bool $detaching = true): array
+    {
+        $changes = [
+            'attached' => [],
+            'detached' => [],
+            'updated'  => [],
+        ];
+
+        $current = $this->pivot
+            ->where($this->localKey, $this->parent->getKey())
+            ->column($this->foreignKey);
+
+        $records = [];
+
+        foreach ($ids as $key => $value) {
+            if (!is_array($value)) {
+                $records[$value] = [];
+            } else {
+                $records[$key] = $value;
+            }
+        }
+
+        $detach = array_diff($current, array_keys($records));
+
+        if ($detaching && count($detach) > 0) {
+            $this->detach($detach);
+            $changes['detached'] = $detach;
+        }
+
+        foreach ($records as $id => $attributes) {
+            if (!in_array($id, $current)) {
+                $this->attach($id, $attributes);
+                $changes['attached'][] = $id;
+            } elseif (count($attributes) > 0 && $this->attach($id, $attributes)) {
+                $changes['updated'][] = $id;
+            }
+        }
+
+        return $changes;
+    }
+
+    /**
+     * 执行基础查询(仅执行一次)
+     * @access protected
+     * @return void
+     */
+    protected function baseQuery(): void
+    {
+        if (empty($this->baseQuery)) {
+            $foreignKey = $this->foreignKey;
+            $localKey   = $this->localKey;
+
+            // 关联查询
+            $condition = ['pivot.' . $localKey, '=', $this->parent->getKey()];
+
+            $this->belongsToManyQuery($foreignKey, $localKey, [$condition]);
+
+            $this->baseQuery = true;
+        }
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/model/relation/HasMany.php b/vendor/topthink/think-orm/src/model/relation/HasMany.php
new file mode 100755
index 0000000..a67d41b
--- /dev/null
+++ b/vendor/topthink/think-orm/src/model/relation/HasMany.php
@@ -0,0 +1,367 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\model\relation;
+
+use Closure;
+use think\Collection;
+use think\db\BaseQuery as Query;
+use think\helper\Str;
+use think\Model;
+use think\model\Relation;
+
+/**
+ * 一对多关联类
+ */
+class HasMany extends Relation
+{
+    /**
+     * 架构函数
+     * @access public
+     * @param  Model  $parent     上级模型对象
+     * @param  string $model      模型名
+     * @param  string $foreignKey 关联外键
+     * @param  string $localKey   当前模型主键
+     */
+    public function __construct(Model $parent, string $model, string $foreignKey, string $localKey)
+    {
+        $this->parent     = $parent;
+        $this->model      = $model;
+        $this->foreignKey = $foreignKey;
+        $this->localKey   = $localKey;
+        $this->query      = (new $model)->db();
+
+        if (get_class($parent) == $model) {
+            $this->selfRelation = true;
+        }
+    }
+
+    /**
+     * 延迟获取关联数据
+     * @access public
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包查询条件
+     * @return Collection
+     */
+    public function getRelation(array $subRelation = [], Closure $closure = null): Collection
+    {
+        if ($closure) {
+            $closure($this->getClosureType($closure));
+        }
+
+        if ($this->withLimit) {
+            $this->query->limit($this->withLimit);
+        }
+
+        return $this->query
+            ->where($this->foreignKey, $this->parent->{$this->localKey})
+            ->relation($subRelation)
+            ->select()
+            ->setParent(clone $this->parent);
+    }
+
+    /**
+     * 预载入关联查询
+     * @access public
+     * @param  array   $resultSet   数据集
+     * @param  string  $relation    当前关联名
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包
+     * @param  array   $cache       关联缓存
+     * @return void
+     */
+    public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null, array $cache = []): void
+    {
+        $localKey = $this->localKey;
+        $range    = [];
+
+        foreach ($resultSet as $result) {
+            // 获取关联外键列表
+            if (isset($result->$localKey)) {
+                $range[] = $result->$localKey;
+            }
+        }
+
+        if (!empty($range)) {
+            $data = $this->eagerlyOneToMany([
+                [$this->foreignKey, 'in', $range],
+            ], $subRelation, $closure, $cache);
+
+            // 关联数据封装
+            foreach ($resultSet as $result) {
+                $pk = $result->$localKey;
+                if (!isset($data[$pk])) {
+                    $data[$pk] = [];
+                }
+
+                $result->setRelation($relation, $this->resultSetBuild($data[$pk], clone $this->parent));
+            }
+        }
+    }
+
+    /**
+     * 预载入关联查询
+     * @access public
+     * @param  Model   $result      数据对象
+     * @param  string  $relation    当前关联名
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包
+     * @param  array   $cache       关联缓存
+     * @return void
+     */
+    public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void
+    {
+        $localKey = $this->localKey;
+
+        if (isset($result->$localKey)) {
+            $pk   = $result->$localKey;
+            $data = $this->eagerlyOneToMany([
+                [$this->foreignKey, '=', $pk],
+            ], $subRelation, $closure, $cache);
+
+            // 关联数据封装
+            if (!isset($data[$pk])) {
+                $data[$pk] = [];
+            }
+
+            $result->setRelation($relation, $this->resultSetBuild($data[$pk], clone $this->parent));
+        }
+    }
+
+    /**
+     * 关联统计
+     * @access public
+     * @param  Model   $result  数据对象
+     * @param  Closure $closure 闭包
+     * @param  string  $aggregate 聚合查询方法
+     * @param  string  $field 字段
+     * @param  string  $name 统计字段别名
+     * @return integer
+     */
+    public function relationCount(Model $result, Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null)
+    {
+        $localKey = $this->localKey;
+
+        if (!isset($result->$localKey)) {
+            return 0;
+        }
+
+        if ($closure) {
+            $closure($this->getClosureType($closure), $name);
+        }
+
+        return $this->query
+            ->where($this->foreignKey, '=', $result->$localKey)
+            ->$aggregate($field);
+    }
+
+    /**
+     * 创建关联统计子查询
+     * @access public
+     * @param  Closure $closure 闭包
+     * @param  string  $aggregate 聚合查询方法
+     * @param  string  $field 字段
+     * @param  string  $name 统计字段别名
+     * @return string
+     */
+    public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string
+    {
+        if ($closure) {
+            $closure($this->getClosureType($closure), $name);
+        }
+
+        return $this->query->alias($aggregate . '_table')
+            ->whereExp($aggregate . '_table.' . $this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->localKey)
+            ->fetchSql()
+            ->$aggregate($field);
+    }
+
+    /**
+     * 一对多 关联模型预查询
+     * @access public
+     * @param  array   $where       关联预查询条件
+     * @param  array   $subRelation 子关联
+     * @param  Closure $closure
+     * @param  array   $cache       关联缓存
+     * @return array
+     */
+    protected function eagerlyOneToMany(array $where, array $subRelation = [], Closure $closure = null, array $cache = []): array
+    {
+        $foreignKey = $this->foreignKey;
+
+        $this->query->removeWhereField($this->foreignKey);
+
+        // 预载入关联查询 支持嵌套预载入
+        if ($closure) {
+            $this->baseQuery = true;
+            $closure($this->getClosureType($closure));
+        }
+
+        $list = $this->query
+            ->where($where)
+            ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null)
+            ->with($subRelation)
+            ->select();
+
+        // 组装模型数据
+        $data = [];
+
+        foreach ($list as $set) {
+            $key = $set->$foreignKey;
+
+            if ($this->withLimit && isset($data[$key]) && count($data[$key]) >= $this->withLimit) {
+                continue;
+            }
+
+            $data[$key][] = $set;
+        }
+
+        return $data;
+    }
+
+    /**
+     * 保存(新增)当前关联数据对象
+     * @access public
+     * @param  mixed   $data 数据 可以使用数组 关联模型对象
+     * @param  boolean $replace 是否自动识别更新和写入
+     * @return Model|false
+     */
+    public function save($data, bool $replace = true)
+    {
+        $model = $this->make();
+
+        return $model->replace($replace)->save($data) ? $model : false;
+    }
+
+    /**
+     * 创建关联对象实例
+     * @param array|Model $data
+     * @return Model
+     */
+    public function make($data = []): Model
+    {
+        if ($data instanceof Model) {
+            $data = $data->getData();
+        }
+
+        // 保存关联表数据
+        $data[$this->foreignKey] = $this->parent->{$this->localKey};
+
+        return new $this->model($data);
+    }
+
+    /**
+     * 批量保存当前关联数据对象
+     * @access public
+     * @param  iterable $dataSet 数据集
+     * @param  boolean  $replace 是否自动识别更新和写入
+     * @return array|false
+     */
+    public function saveAll(iterable $dataSet, bool $replace = true)
+    {
+        $result = [];
+
+        foreach ($dataSet as $key => $data) {
+            $result[] = $this->save($data, $replace);
+        }
+
+        return empty($result) ? false : $result;
+    }
+
+    /**
+     * 根据关联条件查询当前模型
+     * @access public
+     * @param  string  $operator 比较操作符
+     * @param  integer $count    个数
+     * @param  string  $id       关联表的统计字段
+     * @param  string  $joinType JOIN类型
+     * @param  Query   $query    Query对象
+     * @return Query
+     */
+    public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = 'INNER', Query $query = null): Query
+    {
+        $table = $this->query->getTable();
+
+        $model    = class_basename($this->parent);
+        $relation = class_basename($this->model);
+
+        if ('*' != $id) {
+            $id = $relation . '.' . (new $this->model)->getPk();
+        }
+
+        $softDelete = $this->query->getOptions('soft_delete');
+        $query      = $query ?: $this->parent->db()->alias($model);
+
+        return $query->field($model . '.*')
+            ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $joinType)
+            ->when($softDelete, function ($query) use ($softDelete, $relation) {
+                $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
+            })
+            ->group($relation . '.' . $this->foreignKey)
+            ->having('count(' . $id . ')' . $operator . $count);
+    }
+
+    /**
+     * 根据关联条件查询当前模型
+     * @access public
+     * @param  mixed  $where 查询条件(数组或者闭包)
+     * @param  mixed  $fields 字段
+     * @param  string $joinType JOIN类型
+     * @param  Query  $query    Query对象
+     * @return Query
+     */
+    public function hasWhere($where = [], $fields = null, string $joinType = '', Query $query = null): Query
+    {
+        $table    = $this->query->getTable();
+        $model    = class_basename($this->parent);
+        $relation = class_basename($this->model);
+
+        if (is_array($where)) {
+            $this->getQueryWhere($where, $relation);
+        } elseif ($where instanceof Query) {
+            $where->via($relation);
+        } elseif ($where instanceof Closure) {
+            $where($this->query->via($relation));
+            $where = $this->query;
+        }
+
+        $fields     = $this->getRelationQueryFields($fields, $model);
+        $softDelete = $this->query->getOptions('soft_delete');
+        $query      = $query ?: $this->parent->db()->alias($model);
+
+        return $query->group($model . '.' . $this->localKey)
+            ->field($fields)
+            ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $joinType)
+            ->when($softDelete, function ($query) use ($softDelete, $relation) {
+                $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
+            })
+            ->where($where);
+    }
+
+    /**
+     * 执行基础查询(仅执行一次)
+     * @access protected
+     * @return void
+     */
+    protected function baseQuery(): void
+    {
+        if (empty($this->baseQuery)) {
+            if (isset($this->parent->{$this->localKey})) {
+                // 关联查询带入关联条件
+                $this->query->where($this->foreignKey, '=', $this->parent->{$this->localKey});
+            }
+
+            $this->baseQuery = true;
+        }
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/model/relation/HasManyThrough.php b/vendor/topthink/think-orm/src/model/relation/HasManyThrough.php
new file mode 100755
index 0000000..30d5ca4
--- /dev/null
+++ b/vendor/topthink/think-orm/src/model/relation/HasManyThrough.php
@@ -0,0 +1,382 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\model\relation;
+
+use Closure;
+use think\Collection;
+use think\db\BaseQuery as Query;
+use think\helper\Str;
+use think\Model;
+use think\model\Relation;
+
+/**
+ * 远程一对多关联类
+ */
+class HasManyThrough extends Relation
+{
+    /**
+     * 中间关联表外键
+     * @var string
+     */
+    protected $throughKey;
+
+    /**
+     * 中间主键
+     * @var string
+     */
+    protected $throughPk;
+
+    /**
+     * 中间表查询对象
+     * @var Query
+     */
+    protected $through;
+
+    /**
+     * 架构函数
+     * @access public
+     * @param  Model  $parent     上级模型对象
+     * @param  string $model      关联模型名
+     * @param  string $through    中间模型名
+     * @param  string $foreignKey 关联外键
+     * @param  string $throughKey 中间关联外键
+     * @param  string $localKey   当前模型主键
+     * @param  string $throughPk  中间模型主键
+     */
+    public function __construct(Model $parent, string $model, string $through, string $foreignKey, string $throughKey, string $localKey, string $throughPk)
+    {
+        $this->parent     = $parent;
+        $this->model      = $model;
+        $this->through    = (new $through)->db();
+        $this->foreignKey = $foreignKey;
+        $this->throughKey = $throughKey;
+        $this->localKey   = $localKey;
+        $this->throughPk  = $throughPk;
+        $this->query      = (new $model)->db();
+    }
+
+    /**
+     * 延迟获取关联数据
+     * @access public
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包查询条件
+     * @return Collection
+     */
+    public function getRelation(array $subRelation = [], Closure $closure = null)
+    {
+        if ($closure) {
+            $closure($this->getClosureType($closure));
+        }
+
+        $this->baseQuery();
+
+        if ($this->withLimit) {
+            $this->query->limit($this->withLimit);
+        }
+
+        return $this->query->relation($subRelation)
+            ->select()
+            ->setParent(clone $this->parent);
+    }
+
+    /**
+     * 根据关联条件查询当前模型
+     * @access public
+     * @param  string  $operator 比较操作符
+     * @param  integer $count    个数
+     * @param  string  $id       关联表的统计字段
+     * @param  string  $joinType JOIN类型
+     * @param  Query   $query    Query对象
+     * @return Query
+     */
+    public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '', Query $query = null): Query
+    {
+        $model         = Str::snake(class_basename($this->parent));
+        $throughTable  = $this->through->getTable();
+        $pk            = $this->throughPk;
+        $throughKey    = $this->throughKey;
+        $relation      = new $this->model;
+        $relationTable = $relation->getTable();
+        $softDelete    = $this->query->getOptions('soft_delete');
+
+        if ('*' != $id) {
+            $id = $relationTable . '.' . $relation->getPk();
+        }
+        $query = $query ?: $this->parent->db()->alias($model);
+
+        return $query->field($model . '.*')
+            ->join($throughTable, $throughTable . '.' . $this->foreignKey . '=' . $model . '.' . $this->localKey)
+            ->join($relationTable, $relationTable . '.' . $throughKey . '=' . $throughTable . '.' . $this->throughPk)
+            ->when($softDelete, function ($query) use ($softDelete, $relationTable) {
+                $query->where($relationTable . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
+            })
+            ->group($relationTable . '.' . $this->throughKey)
+            ->having('count(' . $id . ')' . $operator . $count);
+    }
+
+    /**
+     * 根据关联条件查询当前模型
+     * @access public
+     * @param  mixed  $where 查询条件(数组或者闭包)
+     * @param  mixed  $fields 字段
+     * @param  string $joinType JOIN类型
+     * @param  Query  $query    Query对象
+     * @return Query
+     */
+    public function hasWhere($where = [], $fields = null, $joinType = '', Query $query = null): Query
+    {
+        $model        = Str::snake(class_basename($this->parent));
+        $throughTable = $this->through->getTable();
+        $pk           = $this->throughPk;
+        $throughKey   = $this->throughKey;
+        $modelTable   = (new $this->model)->getTable();
+
+        if (is_array($where)) {
+            $this->getQueryWhere($where, $modelTable);
+        } elseif ($where instanceof Query) {
+            $where->via($modelTable);
+        } elseif ($where instanceof Closure) {
+            $where($this->query->via($modelTable));
+            $where = $this->query;
+        }
+
+        $fields     = $this->getRelationQueryFields($fields, $model);
+        $softDelete = $this->query->getOptions('soft_delete');
+        $query      = $query ?: $this->parent->db()->alias($model);
+
+        return $query->join($throughTable, $throughTable . '.' . $this->foreignKey . '=' . $model . '.' . $this->localKey)
+            ->join($modelTable, $modelTable . '.' . $throughKey . '=' . $throughTable . '.' . $this->throughPk, $joinType)
+            ->when($softDelete, function ($query) use ($softDelete, $modelTable) {
+                $query->where($modelTable . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
+            })
+            ->group($modelTable . '.' . $this->throughKey)
+            ->where($where)
+            ->field($fields);
+    }
+
+    /**
+     * 预载入关联查询(数据集)
+     * @access protected
+     * @param  array   $resultSet   数据集
+     * @param  string  $relation    当前关联名
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包
+     * @param  array   $cache       关联缓存
+     * @return void
+     */
+    public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void
+    {
+        $localKey   = $this->localKey;
+        $foreignKey = $this->foreignKey;
+
+        $range = [];
+        foreach ($resultSet as $result) {
+            // 获取关联外键列表
+            if (isset($result->$localKey)) {
+                $range[] = $result->$localKey;
+            }
+        }
+
+        if (!empty($range)) {
+            $this->query->removeWhereField($foreignKey);
+
+            $data = $this->eagerlyWhere([
+                [$this->foreignKey, 'in', $range],
+            ], $foreignKey, $subRelation, $closure, $cache);
+
+            // 关联数据封装
+            foreach ($resultSet as $result) {
+                $pk = $result->$localKey;
+                if (!isset($data[$pk])) {
+                    $data[$pk] = [];
+                }
+
+                // 设置关联属性
+                $result->setRelation($relation, $this->resultSetBuild($data[$pk], clone $this->parent));
+            }
+        }
+    }
+
+    /**
+     * 预载入关联查询(数据)
+     * @access protected
+     * @param  Model   $result      数据对象
+     * @param  string  $relation    当前关联名
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包
+     * @param  array   $cache       关联缓存
+     * @return void
+     */
+    public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void
+    {
+        $localKey   = $this->localKey;
+        $foreignKey = $this->foreignKey;
+        $pk         = $result->$localKey;
+
+        $this->query->removeWhereField($foreignKey);
+
+        $data = $this->eagerlyWhere([
+            [$foreignKey, '=', $pk],
+        ], $foreignKey, $subRelation, $closure, $cache);
+
+        // 关联数据封装
+        if (!isset($data[$pk])) {
+            $data[$pk] = [];
+        }
+
+        $result->setRelation($relation, $this->resultSetBuild($data[$pk], clone $this->parent));
+    }
+
+    /**
+     * 关联模型预查询
+     * @access public
+     * @param  array   $where       关联预查询条件
+     * @param  string  $key         关联键名
+     * @param  array   $subRelation 子关联
+     * @param  Closure $closure
+     * @param  array   $cache       关联缓存
+     * @return array
+     */
+    protected function eagerlyWhere(array $where, string $key, array $subRelation = [], Closure $closure = null, array $cache = []): array
+    {
+        // 预载入关联查询 支持嵌套预载入
+        $throughList = $this->through->where($where)->select();
+        $keys        = $throughList->column($this->throughPk, $this->throughPk);
+
+        if ($closure) {
+            $this->baseQuery = true;
+            $closure($this->getClosureType($closure));
+        }
+
+        $list = $this->query
+            ->where($this->throughKey, 'in', $keys)
+            ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null)
+            ->select();
+
+        // 组装模型数据
+        $data = [];
+        $keys = $throughList->column($this->foreignKey, $this->throughPk);
+
+        foreach ($list as $set) {
+            $key = $keys[$set->{$this->throughKey}];
+
+            if ($this->withLimit && isset($data[$key]) && count($data[$key]) >= $this->withLimit) {
+                continue;
+            }
+
+            $data[$key][] = $set;
+        }
+
+        return $data;
+    }
+
+    /**
+     * 关联统计
+     * @access public
+     * @param  Model   $result  数据对象
+     * @param  Closure $closure 闭包
+     * @param  string  $aggregate 聚合查询方法
+     * @param  string  $field 字段
+     * @param  string  $name 统计字段别名
+     * @return mixed
+     */
+    public function relationCount(Model $result, Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null)
+    {
+        $localKey = $this->localKey;
+
+        if (!isset($result->$localKey)) {
+            return 0;
+        }
+
+        if ($closure) {
+            $closure($this->getClosureType($closure), $name);
+        }
+
+        $alias        = Str::snake(class_basename($this->model));
+        $throughTable = $this->through->getTable();
+        $pk           = $this->throughPk;
+        $throughKey   = $this->throughKey;
+        $modelTable   = $this->parent->getTable();
+
+        if (false === strpos($field, '.')) {
+            $field = $alias . '.' . $field;
+        }
+
+        return $this->query
+            ->alias($alias)
+            ->join($throughTable, $throughTable . '.' . $pk . '=' . $alias . '.' . $throughKey)
+            ->join($modelTable, $modelTable . '.' . $this->localKey . '=' . $throughTable . '.' . $this->foreignKey)
+            ->where($throughTable . '.' . $this->foreignKey, $result->$localKey)
+            ->$aggregate($field);
+    }
+
+    /**
+     * 创建关联统计子查询
+     * @access public
+     * @param  Closure $closure 闭包
+     * @param  string  $aggregate 聚合查询方法
+     * @param  string  $field 字段
+     * @param  string  $name 统计字段别名
+     * @return string
+     */
+    public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string
+    {
+        if ($closure) {
+            $closure($this->getClosureType($closure), $name);
+        }
+
+        $alias        = Str::snake(class_basename($this->model));
+        $throughTable = $this->through->getTable();
+        $pk           = $this->throughPk;
+        $throughKey   = $this->throughKey;
+        $modelTable   = $this->parent->getTable();
+
+        if (false === strpos($field, '.')) {
+            $field = $alias . '.' . $field;
+        }
+
+        return $this->query
+            ->alias($alias)
+            ->join($throughTable, $throughTable . '.' . $pk . '=' . $alias . '.' . $throughKey)
+            ->join($modelTable, $modelTable . '.' . $this->localKey . '=' . $throughTable . '.' . $this->foreignKey)
+            ->whereExp($throughTable . '.' . $this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->localKey)
+            ->fetchSql()
+            ->$aggregate($field);
+    }
+
+    /**
+     * 执行基础查询(仅执行一次)
+     * @access protected
+     * @return void
+     */
+    protected function baseQuery(): void
+    {
+        if (empty($this->baseQuery) && $this->parent->getData()) {
+            $alias        = Str::snake(class_basename($this->model));
+            $throughTable = $this->through->getTable();
+            $pk           = $this->throughPk;
+            $throughKey   = $this->throughKey;
+            $modelTable   = $this->parent->getTable();
+            $fields       = $this->getQueryFields($alias);
+
+            $this->query
+                ->field($fields)
+                ->alias($alias)
+                ->join($throughTable, $throughTable . '.' . $pk . '=' . $alias . '.' . $throughKey)
+                ->join($modelTable, $modelTable . '.' . $this->localKey . '=' . $throughTable . '.' . $this->foreignKey)
+                ->where($throughTable . '.' . $this->foreignKey, $this->parent->{$this->localKey});
+
+            $this->baseQuery = true;
+        }
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/model/relation/HasOne.php b/vendor/topthink/think-orm/src/model/relation/HasOne.php
new file mode 100755
index 0000000..d0dc21d
--- /dev/null
+++ b/vendor/topthink/think-orm/src/model/relation/HasOne.php
@@ -0,0 +1,300 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\model\relation;
+
+use Closure;
+use think\db\BaseQuery as Query;
+use think\helper\Str;
+use think\Model;
+
+/**
+ * HasOne 关联类
+ */
+class HasOne extends OneToOne
+{
+    /**
+     * 架构函数
+     * @access public
+     * @param  Model  $parent     上级模型对象
+     * @param  string $model      模型名
+     * @param  string $foreignKey 关联外键
+     * @param  string $localKey   当前模型主键
+     */
+    public function __construct(Model $parent, string $model, string $foreignKey, string $localKey)
+    {
+        $this->parent     = $parent;
+        $this->model      = $model;
+        $this->foreignKey = $foreignKey;
+        $this->localKey   = $localKey;
+        $this->query      = (new $model)->db();
+
+        if (get_class($parent) == $model) {
+            $this->selfRelation = true;
+        }
+    }
+
+    /**
+     * 延迟获取关联数据
+     * @access public
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包查询条件
+     * @return Model
+     */
+    public function getRelation(array $subRelation = [], Closure $closure = null)
+    {
+        $localKey = $this->localKey;
+
+        if ($closure) {
+            $closure($this->getClosureType($closure));
+        }
+
+        // 判断关联类型执行查询
+        $relationModel = $this->query
+            ->removeWhereField($this->foreignKey)
+            ->where($this->foreignKey, $this->parent->$localKey)
+            ->relation($subRelation)
+            ->find();
+
+        if ($relationModel) {
+            if (!empty($this->bindAttr)) {
+                // 绑定关联属性
+                $this->bindAttr($this->parent, $relationModel);
+            }
+
+            $relationModel->setParent(clone $this->parent);
+        }
+
+        return $relationModel;
+    }
+
+    /**
+     * 创建关联统计子查询
+     * @access public
+     * @param  Closure $closure 闭包
+     * @param  string  $aggregate 聚合查询方法
+     * @param  string  $field 字段
+     * @param  string  $name 统计字段别名
+     * @return string
+     */
+    public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string
+    {
+        if ($closure) {
+            $closure($this->getClosureType($closure), $name);
+        }
+
+        return $this->query
+            ->whereExp($this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->localKey)
+            ->fetchSql()
+            ->$aggregate($field);
+    }
+
+    /**
+     * 关联统计
+     * @access public
+     * @param  Model   $result  数据对象
+     * @param  Closure $closure 闭包
+     * @param  string  $aggregate 聚合查询方法
+     * @param  string  $field 字段
+     * @param  string  $name 统计字段别名
+     * @return integer
+     */
+    public function relationCount(Model $result, Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null)
+    {
+        $localKey = $this->localKey;
+
+        if (!isset($result->$localKey)) {
+            return 0;
+        }
+
+        if ($closure) {
+            $closure($this->getClosureType($closure), $name);
+        }
+
+        return $this->query
+            ->where($this->foreignKey, '=', $result->$localKey)
+            ->$aggregate($field);
+    }
+
+    /**
+     * 根据关联条件查询当前模型
+     * @access public
+     * @param  string  $operator 比较操作符
+     * @param  integer $count    个数
+     * @param  string  $id       关联表的统计字段
+     * @param  string  $joinType JOIN类型
+     * @param  Query   $query    Query对象
+     * @return Query
+     */
+    public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '', Query $query = null): Query
+    {
+        $table      = $this->query->getTable();
+        $model      = class_basename($this->parent);
+        $relation   = class_basename($this->model);
+        $localKey   = $this->localKey;
+        $foreignKey = $this->foreignKey;
+        $softDelete = $this->query->getOptions('soft_delete');
+        $query      = $query ?: $this->parent->db()->alias($model);
+
+        return $query->whereExists(function ($query) use ($table, $model, $relation, $localKey, $foreignKey, $softDelete) {
+            $query->table([$table => $relation])
+                ->field($relation . '.' . $foreignKey)
+                ->whereExp($model . '.' . $localKey, '=' . $relation . '.' . $foreignKey)
+                ->when($softDelete, function ($query) use ($softDelete, $relation) {
+                    $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
+                });
+        });
+    }
+
+    /**
+     * 根据关联条件查询当前模型
+     * @access public
+     * @param  mixed  $where 查询条件(数组或者闭包)
+     * @param  mixed  $fields   字段
+     * @param  string $joinType JOIN类型
+     * @param  Query  $query    Query对象
+     * @return Query
+     */
+    public function hasWhere($where = [], $fields = null, string $joinType = '', Query $query = null): Query
+    {
+        $table    = $this->query->getTable();
+        $model    = class_basename($this->parent);
+        $relation = class_basename($this->model);
+
+        if (is_array($where)) {
+            $this->getQueryWhere($where, $relation);
+        } elseif ($where instanceof Query) {
+            $where->via($relation);
+        } elseif ($where instanceof Closure) {
+            $where($this->query->via($relation));
+            $where = $this->query;
+        }
+
+        $fields     = $this->getRelationQueryFields($fields, $model);
+        $softDelete = $this->query->getOptions('soft_delete');
+        $query      = $query ?: $this->parent->db()->alias($model);
+
+        return $query->field($fields)
+            ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $joinType ?: $this->joinType)
+            ->when($softDelete, function ($query) use ($softDelete, $relation) {
+                $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
+            })
+            ->where($where);
+    }
+
+    /**
+     * 预载入关联查询(数据集)
+     * @access protected
+     * @param  array   $resultSet   数据集
+     * @param  string  $relation    当前关联名
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包
+     * @param  array   $cache       关联缓存
+     * @return void
+     */
+    protected function eagerlySet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void
+    {
+        $localKey   = $this->localKey;
+        $foreignKey = $this->foreignKey;
+
+        $range = [];
+        foreach ($resultSet as $result) {
+            // 获取关联外键列表
+            if (isset($result->$localKey)) {
+                $range[] = $result->$localKey;
+            }
+        }
+
+        if (!empty($range)) {
+            $this->query->removeWhereField($foreignKey);
+
+            $data = $this->eagerlyWhere([
+                [$foreignKey, 'in', $range],
+            ], $foreignKey, $subRelation, $closure, $cache);
+
+            // 关联数据封装
+            foreach ($resultSet as $result) {
+                // 关联模型
+                if (!isset($data[$result->$localKey])) {
+                    $relationModel = null;
+                } else {
+                    $relationModel = $data[$result->$localKey];
+                    $relationModel->setParent(clone $result);
+                    $relationModel->exists(true);
+                }
+
+                if (!empty($this->bindAttr)) {
+                    // 绑定关联属性
+                    $this->bindAttr($result, $relationModel);
+                } else {
+                    // 设置关联属性
+                    $result->setRelation($relation, $relationModel);
+                }
+            }
+        }
+    }
+
+    /**
+     * 预载入关联查询(数据)
+     * @access protected
+     * @param  Model   $result      数据对象
+     * @param  string  $relation    当前关联名
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包
+     * @param  array   $cache       关联缓存
+     * @return void
+     */
+    protected function eagerlyOne(Model $result, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void
+    {
+        $localKey   = $this->localKey;
+        $foreignKey = $this->foreignKey;
+
+        $this->query->removeWhereField($foreignKey);
+
+        $data = $this->eagerlyWhere([
+            [$foreignKey, '=', $result->$localKey],
+        ], $foreignKey, $subRelation, $closure, $cache);
+
+        // 关联模型
+        if (!isset($data[$result->$localKey])) {
+            $relationModel = null;
+        } else {
+            $relationModel = $data[$result->$localKey];
+            $relationModel->setParent(clone $result);
+            $relationModel->exists(true);
+        }
+
+        if (!empty($this->bindAttr)) {
+            // 绑定关联属性
+            $this->bindAttr($result, $relationModel);
+        } else {
+            $result->setRelation($relation, $relationModel);
+        }
+    }
+
+    /**
+     * 执行基础查询(仅执行一次)
+     * @access protected
+     * @return void
+     */
+    protected function baseQuery(): void
+    {
+        if (empty($this->baseQuery)) {
+            if (isset($this->parent->{$this->localKey})) {
+                // 关联查询带入关联条件
+                $this->query->where($this->foreignKey, '=', $this->parent->{$this->localKey});
+            }
+
+            $this->baseQuery = true;
+        }
+    }
+}
diff --git a/vendor/topthink/think-orm/src/model/relation/HasOneThrough.php b/vendor/topthink/think-orm/src/model/relation/HasOneThrough.php
new file mode 100755
index 0000000..8ec42df
--- /dev/null
+++ b/vendor/topthink/think-orm/src/model/relation/HasOneThrough.php
@@ -0,0 +1,163 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\model\relation;
+
+use Closure;
+use think\helper\Str;
+use think\Model;
+
+/**
+ * 远程一对一关联类
+ */
+class HasOneThrough extends HasManyThrough
+{
+
+    /**
+     * 延迟获取关联数据
+     * @access public
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包查询条件
+     * @return Model
+     */
+    public function getRelation(array $subRelation = [], Closure $closure = null)
+    {
+        if ($closure) {
+            $closure($this->getClosureType($closure));
+        }
+
+        $this->baseQuery();
+
+        $relationModel = $this->query->relation($subRelation)->find();
+
+        if ($relationModel) {
+            $relationModel->setParent(clone $this->parent);
+        }
+
+        return $relationModel;
+    }
+
+    /**
+     * 预载入关联查询(数据集)
+     * @access protected
+     * @param  array   $resultSet   数据集
+     * @param  string  $relation    当前关联名
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包
+     * @param  array   $cache       关联缓存
+     * @return void
+     */
+    public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void
+    {
+        $localKey   = $this->localKey;
+        $foreignKey = $this->foreignKey;
+
+        $range = [];
+        foreach ($resultSet as $result) {
+            // 获取关联外键列表
+            if (isset($result->$localKey)) {
+                $range[] = $result->$localKey;
+            }
+        }
+
+        if (!empty($range)) {
+            $this->query->removeWhereField($foreignKey);
+
+            $data = $this->eagerlyWhere([
+                [$this->foreignKey, 'in', $range],
+            ], $foreignKey, $subRelation, $closure, $cache);
+
+            // 关联数据封装
+            foreach ($resultSet as $result) {
+                // 关联模型
+                if (!isset($data[$result->$localKey])) {
+                    $relationModel = null;
+                } else {
+                    $relationModel = $data[$result->$localKey];
+                    $relationModel->setParent(clone $result);
+                    $relationModel->exists(true);
+                }
+
+                // 设置关联属性
+                $result->setRelation($relation, $relationModel);
+            }
+        }
+    }
+
+    /**
+     * 预载入关联查询(数据)
+     * @access protected
+     * @param  Model   $result      数据对象
+     * @param  string  $relation    当前关联名
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包
+     * @param  array   $cache       关联缓存
+     * @return void
+     */
+    public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void
+    {
+        $localKey   = $this->localKey;
+        $foreignKey = $this->foreignKey;
+
+        $this->query->removeWhereField($foreignKey);
+
+        $data = $this->eagerlyWhere([
+            [$foreignKey, '=', $result->$localKey],
+        ], $foreignKey, $subRelation, $closure, $cache);
+
+        // 关联模型
+        if (!isset($data[$result->$localKey])) {
+            $relationModel = null;
+        } else {
+            $relationModel = $data[$result->$localKey];
+            $relationModel->setParent(clone $result);
+            $relationModel->exists(true);
+        }
+
+        $result->setRelation($relation, $relationModel);
+    }
+
+    /**
+     * 关联模型预查询
+     * @access public
+     * @param  array   $where       关联预查询条件
+     * @param  string  $key         关联键名
+     * @param  array   $subRelation 子关联
+     * @param  Closure $closure
+     * @param  array   $cache       关联缓存
+     * @return array
+     */
+    protected function eagerlyWhere(array $where, string $key, array $subRelation = [], Closure $closure = null, array $cache = []): array
+    {
+        // 预载入关联查询 支持嵌套预载入
+        $keys = $this->through->where($where)->column($this->throughPk, $this->foreignKey);
+
+        if ($closure) {
+            $closure($this->getClosureType($closure));
+        }
+
+        $list = $this->query
+            ->where($this->throughKey, 'in', $keys)
+            ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null)
+            ->select();
+
+        // 组装模型数据
+        $data = [];
+        $keys = array_flip($keys);
+
+        foreach ($list as $set) {
+            $data[$keys[$set->{$this->throughKey}]] = $set;
+        }
+
+        return $data;
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/model/relation/MorphMany.php b/vendor/topthink/think-orm/src/model/relation/MorphMany.php
new file mode 100755
index 0000000..82910cb
--- /dev/null
+++ b/vendor/topthink/think-orm/src/model/relation/MorphMany.php
@@ -0,0 +1,353 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\model\relation;
+
+use Closure;
+use think\Collection;
+use think\db\BaseQuery as Query;
+use think\db\exception\DbException as Exception;
+use think\helper\Str;
+use think\Model;
+use think\model\Relation;
+
+/**
+ * 多态一对多关联
+ */
+class MorphMany extends Relation
+{
+
+    /**
+     * 多态关联外键
+     * @var string
+     */
+    protected $morphKey;
+    /**
+     * 多态字段名
+     * @var string
+     */
+    protected $morphType;
+
+    /**
+     * 多态类型
+     * @var string
+     */
+    protected $type;
+
+    /**
+     * 架构函数
+     * @access public
+     * @param  Model  $parent    上级模型对象
+     * @param  string $model     模型名
+     * @param  string $morphKey  关联外键
+     * @param  string $morphType 多态字段名
+     * @param  string $type      多态类型
+     */
+    public function __construct(Model $parent, string $model, string $morphKey, string $morphType, string $type)
+    {
+        $this->parent    = $parent;
+        $this->model     = $model;
+        $this->type      = $type;
+        $this->morphKey  = $morphKey;
+        $this->morphType = $morphType;
+        $this->query     = (new $model)->db();
+    }
+
+    /**
+     * 延迟获取关联数据
+     * @access public
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包查询条件
+     * @return Collection
+     */
+    public function getRelation(array $subRelation = [], Closure $closure = null): Collection
+    {
+        if ($closure) {
+            $closure($this->getClosureType($closure));
+        }
+
+        $this->baseQuery();
+
+        if ($this->withLimit) {
+            $this->query->limit($this->withLimit);
+        }
+
+        return $this->query->relation($subRelation)
+            ->select()
+            ->setParent(clone $this->parent);
+    }
+
+    /**
+     * 根据关联条件查询当前模型
+     * @access public
+     * @param  string  $operator 比较操作符
+     * @param  integer $count    个数
+     * @param  string  $id       关联表的统计字段
+     * @param  string  $joinType JOIN类型
+     * @param  Query   $query    Query对象
+     * @return Query
+     */
+    public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '', Query $query = null)
+    {
+        throw new Exception('relation not support: has');
+    }
+
+    /**
+     * 根据关联条件查询当前模型
+     * @access public
+     * @param  mixed  $where 查询条件(数组或者闭包)
+     * @param  mixed  $fields 字段
+     * @param  string $joinType JOIN类型
+     * @param  Query  $query    Query对象
+     * @return Query
+     */
+    public function hasWhere($where = [], $fields = null, string $joinType = '', Query $query = null)
+    {
+        throw new Exception('relation not support: hasWhere');
+    }
+
+    /**
+     * 预载入关联查询
+     * @access public
+     * @param  array   $resultSet   数据集
+     * @param  string  $relation    当前关联名
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包
+     * @param  array   $cache       关联缓存
+     * @return void
+     */
+    public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null, array $cache = []): void
+    {
+        $morphType = $this->morphType;
+        $morphKey  = $this->morphKey;
+        $type      = $this->type;
+        $range     = [];
+
+        foreach ($resultSet as $result) {
+            $pk = $result->getPk();
+            // 获取关联外键列表
+            if (isset($result->$pk)) {
+                $range[] = $result->$pk;
+            }
+        }
+
+        if (!empty($range)) {
+            $where = [
+                [$morphKey, 'in', $range],
+                [$morphType, '=', $type],
+            ];
+            $data = $this->eagerlyMorphToMany($where, $subRelation, $closure, $cache);
+
+            // 关联数据封装
+            foreach ($resultSet as $result) {
+                if (!isset($data[$result->$pk])) {
+                    $data[$result->$pk] = [];
+                }
+
+                $result->setRelation($relation, $this->resultSetBuild($data[$result->$pk], clone $this->parent));
+            }
+        }
+    }
+
+    /**
+     * 预载入关联查询
+     * @access public
+     * @param  Model   $result      数据对象
+     * @param  string  $relation    当前关联名
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包
+     * @param  array   $cache       关联缓存
+     * @return void
+     */
+    public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void
+    {
+        $pk = $result->getPk();
+
+        if (isset($result->$pk)) {
+            $key  = $result->$pk;
+            $data = $this->eagerlyMorphToMany([
+                [$this->morphKey, '=', $key],
+                [$this->morphType, '=', $this->type],
+            ], $subRelation, $closure, $cache);
+
+            if (!isset($data[$key])) {
+                $data[$key] = [];
+            }
+
+            $result->setRelation($relation, $this->resultSetBuild($data[$key], clone $this->parent));
+        }
+    }
+
+    /**
+     * 关联统计
+     * @access public
+     * @param  Model   $result  数据对象
+     * @param  Closure $closure 闭包
+     * @param  string  $aggregate 聚合查询方法
+     * @param  string  $field 字段
+     * @param  string  $name 统计字段别名
+     * @return mixed
+     */
+    public function relationCount(Model $result, Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null)
+    {
+        $pk = $result->getPk();
+
+        if (!isset($result->$pk)) {
+            return 0;
+        }
+
+        if ($closure) {
+            $closure($this->getClosureType($closure), $name);
+        }
+
+        return $this->query
+            ->where([
+                [$this->morphKey, '=', $result->$pk],
+                [$this->morphType, '=', $this->type],
+            ])
+            ->$aggregate($field);
+    }
+
+    /**
+     * 获取关联统计子查询
+     * @access public
+     * @param  Closure $closure 闭包
+     * @param  string  $aggregate 聚合查询方法
+     * @param  string  $field 字段
+     * @param  string  $name 统计字段别名
+     * @return string
+     */
+    public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string
+    {
+        if ($closure) {
+            $closure($this->getClosureType($closure), $name);
+        }
+
+        return $this->query
+            ->whereExp($this->morphKey, '=' . $this->parent->getTable() . '.' . $this->parent->getPk())
+            ->where($this->morphType, '=', $this->type)
+            ->fetchSql()
+            ->$aggregate($field);
+    }
+
+    /**
+     * 多态一对多 关联模型预查询
+     * @access protected
+     * @param  array   $where       关联预查询条件
+     * @param  array   $subRelation 子关联
+     * @param  Closure $closure     闭包
+     * @param  array   $cache       关联缓存
+     * @return array
+     */
+    protected function eagerlyMorphToMany(array $where, array $subRelation = [], Closure $closure = null, array $cache = []): array
+    {
+        // 预载入关联查询 支持嵌套预载入
+        $this->query->removeOption('where');
+
+        if ($closure) {
+            $this->baseQuery = true;
+            $closure($this->getClosureType($closure));
+        }
+
+        $list = $this->query
+            ->where($where)
+            ->with($subRelation)
+            ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null)
+            ->select();
+        $morphKey = $this->morphKey;
+
+        // 组装模型数据
+        $data = [];
+        foreach ($list as $set) {
+            $key = $set->$morphKey;
+
+            if ($this->withLimit && isset($data[$key]) && count($data[$key]) >= $this->withLimit) {
+                continue;
+            }
+
+            $data[$key][] = $set;
+        }
+
+        return $data;
+    }
+
+    /**
+     * 保存(新增)当前关联数据对象
+     * @access public
+     * @param  mixed $data 数据 可以使用数组 关联模型对象
+     * @param  bool  $replace 是否自动识别更新和写入
+     * @return Model|false
+     */
+    public function save($data, bool $replace = true)
+    {
+        $model = $this->make();
+
+        return $model->replace($replace)->save($data) ? $model : false;
+    }
+
+    /**
+     * 创建关联对象实例
+     * @param array|Model $data
+     * @return Model
+     */
+    public function make($data = []): Model
+    {
+        if ($data instanceof Model) {
+            $data = $data->getData();
+        }
+
+        // 保存关联表数据
+        $pk = $this->parent->getPk();
+
+        $data[$this->morphKey]  = $this->parent->$pk;
+        $data[$this->morphType] = $this->type;
+
+        return new $this->model($data);
+    }
+
+    /**
+     * 批量保存当前关联数据对象
+     * @access public
+     * @param  iterable $dataSet 数据集
+     * @param  boolean  $replace 是否自动识别更新和写入
+     * @return array|false
+     */
+    public function saveAll(iterable $dataSet, bool $replace = true)
+    {
+        $result = [];
+
+        foreach ($dataSet as $key => $data) {
+            $result[] = $this->save($data, $replace);
+        }
+
+        return empty($result) ? false : $result;
+    }
+
+    /**
+     * 执行基础查询(仅执行一次)
+     * @access protected
+     * @return void
+     */
+    protected function baseQuery(): void
+    {
+        if (empty($this->baseQuery) && $this->parent->getData()) {
+            $pk = $this->parent->getPk();
+
+            $this->query->where([
+                [$this->morphKey, '=', $this->parent->$pk],
+                [$this->morphType, '=', $this->type],
+            ]);
+
+            $this->baseQuery = true;
+        }
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/model/relation/MorphOne.php b/vendor/topthink/think-orm/src/model/relation/MorphOne.php
new file mode 100755
index 0000000..6789c76
--- /dev/null
+++ b/vendor/topthink/think-orm/src/model/relation/MorphOne.php
@@ -0,0 +1,280 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\model\relation;
+
+use Closure;
+use think\db\BaseQuery as Query;
+use think\db\exception\DbException as Exception;
+use think\helper\Str;
+use think\Model;
+use think\model\Relation;
+
+/**
+ * 多态一对一关联类
+ */
+class MorphOne extends Relation
+{
+    /**
+     * 多态关联外键
+     * @var string
+     */
+    protected $morphKey;
+
+    /**
+     * 多态字段
+     * @var string
+     */
+    protected $morphType;
+
+    /**
+     * 多态类型
+     * @var string
+     */
+    protected $type;
+
+    /**
+     * 构造函数
+     * @access public
+     * @param  Model  $parent    上级模型对象
+     * @param  string $model     模型名
+     * @param  string $morphKey  关联外键
+     * @param  string $morphType 多态字段名
+     * @param  string $type      多态类型
+     */
+    public function __construct(Model $parent, string $model, string $morphKey, string $morphType, string $type)
+    {
+        $this->parent    = $parent;
+        $this->model     = $model;
+        $this->type      = $type;
+        $this->morphKey  = $morphKey;
+        $this->morphType = $morphType;
+        $this->query     = (new $model)->db();
+    }
+
+    /**
+     * 延迟获取关联数据
+     * @access public
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包查询条件
+     * @return Model
+     */
+    public function getRelation(array $subRelation = [], Closure $closure = null)
+    {
+        if ($closure) {
+            $closure($this->getClosureType($closure));
+        }
+
+        $this->baseQuery();
+
+        $relationModel = $this->query->relation($subRelation)->find();
+
+        if ($relationModel) {
+            $relationModel->setParent(clone $this->parent);
+        }
+
+        return $relationModel;
+    }
+
+    /**
+     * 根据关联条件查询当前模型
+     * @access public
+     * @param  string  $operator 比较操作符
+     * @param  integer $count    个数
+     * @param  string  $id       关联表的统计字段
+     * @param  string  $joinType JOIN类型
+     * @param  Query   $query    Query对象
+     * @return Query
+     */
+    public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '', Query $query = null)
+    {
+        return $this->parent;
+    }
+
+    /**
+     * 根据关联条件查询当前模型
+     * @access public
+     * @param  mixed  $where 查询条件(数组或者闭包)
+     * @param  mixed  $fields 字段
+     * @param  string $joinType JOIN类型
+     * @param  Query  $query    Query对象
+     * @return Query
+     */
+    public function hasWhere($where = [], $fields = null, string $joinType = '', Query $query = null)
+    {
+        throw new Exception('relation not support: hasWhere');
+    }
+
+    /**
+     * 预载入关联查询
+     * @access public
+     * @param  array   $resultSet   数据集
+     * @param  string  $relation    当前关联名
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包
+     * @param  array   $cache       关联缓存
+     * @return void
+     */
+    public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null, array $cache = []): void
+    {
+        $morphType = $this->morphType;
+        $morphKey  = $this->morphKey;
+        $type      = $this->type;
+        $range     = [];
+
+        foreach ($resultSet as $result) {
+            $pk = $result->getPk();
+            // 获取关联外键列表
+            if (isset($result->$pk)) {
+                $range[] = $result->$pk;
+            }
+        }
+
+        if (!empty($range)) {
+            $data = $this->eagerlyMorphToOne([
+                [$morphKey, 'in', $range],
+                [$morphType, '=', $type],
+            ], $subRelation, $closure, $cache);
+
+            // 关联数据封装
+            foreach ($resultSet as $result) {
+                if (!isset($data[$result->$pk])) {
+                    $relationModel = null;
+                } else {
+                    $relationModel = $data[$result->$pk];
+                    $relationModel->setParent(clone $result);
+                    $relationModel->exists(true);
+                }
+
+                $result->setRelation($relation, $relationModel);
+            }
+        }
+    }
+
+    /**
+     * 预载入关联查询
+     * @access public
+     * @param  Model   $result      数据对象
+     * @param  string  $relation    当前关联名
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包
+     * @param  array   $cache       关联缓存
+     * @return void
+     */
+    public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void
+    {
+        $pk = $result->getPk();
+
+        if (isset($result->$pk)) {
+            $pk   = $result->$pk;
+            $data = $this->eagerlyMorphToOne([
+                [$this->morphKey, '=', $pk],
+                [$this->morphType, '=', $this->type],
+            ], $subRelation, $closure, $cache);
+
+            if (isset($data[$pk])) {
+                $relationModel = $data[$pk];
+                $relationModel->setParent(clone $result);
+                $relationModel->exists(true);
+            } else {
+                $relationModel = null;
+            }
+
+            $result->setRelation($relation, $relationModel);
+        }
+    }
+
+    /**
+     * 多态一对一 关联模型预查询
+     * @access protected
+     * @param  array   $where       关联预查询条件
+     * @param  array   $subRelation 子关联
+     * @param  Closure $closure     闭包
+     * @param  array   $cache       关联缓存
+     * @return array
+     */
+    protected function eagerlyMorphToOne(array $where, array $subRelation = [], $closure = null, array $cache = []): array
+    {
+        // 预载入关联查询 支持嵌套预载入
+        if ($closure) {
+            $this->baseQuery = true;
+            $closure($this->getClosureType($closure));
+        }
+
+        $list = $this->query
+            ->where($where)
+            ->with($subRelation)
+            ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null)
+            ->select();
+        $morphKey = $this->morphKey;
+
+        // 组装模型数据
+        $data = [];
+
+        foreach ($list as $set) {
+            $data[$set->$morphKey] = $set;
+        }
+
+        return $data;
+    }
+
+    /**
+     * 保存(新增)当前关联数据对象
+     * @access public
+     * @param  mixed   $data 数据 可以使用数组 关联模型对象
+     * @param  boolean $replace 是否自动识别更新和写入
+     * @return Model|false
+     */
+    public function save($data, bool $replace = true)
+    {
+        $model = $this->make();
+        return $model->replace($replace)->save($data) ? $model : false;
+    }
+
+    /**
+     * 创建关联对象实例
+     * @param array|Model $data
+     * @return Model
+     */
+    public function make($data = []): Model
+    {
+        if ($data instanceof Model) {
+            $data = $data->getData();
+        }
+
+        // 保存关联表数据
+        $pk = $this->parent->getPk();
+
+        $data[$this->morphKey]  = $this->parent->$pk;
+        $data[$this->morphType] = $this->type;
+
+        return new $this->model($data);
+    }
+
+    /**
+     * 执行基础查询(进执行一次)
+     * @access protected
+     * @return void
+     */
+    protected function baseQuery(): void
+    {
+        if (empty($this->baseQuery) && $this->parent->getData()) {
+            $pk = $this->parent->getPk();
+
+            $this->query->where([
+                [$this->morphKey, '=', $this->parent->$pk],
+                [$this->morphType, '=', $this->type],
+            ]);
+            $this->baseQuery = true;
+        }
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/model/relation/MorphTo.php b/vendor/topthink/think-orm/src/model/relation/MorphTo.php
new file mode 100755
index 0000000..986380e
--- /dev/null
+++ b/vendor/topthink/think-orm/src/model/relation/MorphTo.php
@@ -0,0 +1,332 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\model\relation;
+
+use Closure;
+use think\db\exception\DbException as Exception;
+use think\helper\Str;
+use think\Model;
+use think\model\Relation;
+
+/**
+ * 多态关联类
+ */
+class MorphTo extends Relation
+{
+    /**
+     * 多态关联外键
+     * @var string
+     */
+    protected $morphKey;
+
+    /**
+     * 多态字段
+     * @var string
+     */
+    protected $morphType;
+
+    /**
+     * 多态别名
+     * @var array
+     */
+    protected $alias = [];
+
+    /**
+     * 关联名
+     * @var string
+     */
+    protected $relation;
+
+    /**
+     * 架构函数
+     * @access public
+     * @param  Model  $parent    上级模型对象
+     * @param  string $morphType 多态字段名
+     * @param  string $morphKey  外键名
+     * @param  array  $alias     多态别名定义
+     * @param  string $relation  关联名
+     */
+    public function __construct(Model $parent, string $morphType, string $morphKey, array $alias = [], string $relation = null)
+    {
+        $this->parent    = $parent;
+        $this->morphType = $morphType;
+        $this->morphKey  = $morphKey;
+        $this->alias     = $alias;
+        $this->relation  = $relation;
+    }
+
+    /**
+     * 获取当前的关联模型类的实例
+     * @access public
+     * @return Model
+     */
+    public function getModel(): Model
+    {
+        $morphType = $this->morphType;
+        $model     = $this->parseModel($this->parent->$morphType);
+
+        return (new $model);
+    }
+
+    /**
+     * 延迟获取关联数据
+     * @access public
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包查询条件
+     * @return Model
+     */
+    public function getRelation(array $subRelation = [], Closure $closure = null)
+    {
+        $morphKey  = $this->morphKey;
+        $morphType = $this->morphType;
+
+        // 多态模型
+        $model = $this->parseModel($this->parent->$morphType);
+
+        // 主键数据
+        $pk = $this->parent->$morphKey;
+
+        $relationModel = (new $model)->relation($subRelation)->find($pk);
+
+        if ($relationModel) {
+            $relationModel->setParent(clone $this->parent);
+        }
+
+        return $relationModel;
+    }
+
+    /**
+     * 根据关联条件查询当前模型
+     * @access public
+     * @param  string  $operator 比较操作符
+     * @param  integer $count    个数
+     * @param  string  $id       关联表的统计字段
+     * @param  string  $joinType JOIN类型
+     * @param  Query   $query    Query对象
+     * @return Query
+     */
+    public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '', Query $query = null)
+    {
+        return $this->parent;
+    }
+
+    /**
+     * 根据关联条件查询当前模型
+     * @access public
+     * @param  mixed  $where 查询条件(数组或者闭包)
+     * @param  mixed  $fields 字段
+     * @param  string $joinType JOIN类型
+     * @param  Query  $query    Query对象
+     * @return Query
+     */
+    public function hasWhere($where = [], $fields = null, string $joinType = '', Query $query = null)
+    {
+        throw new Exception('relation not support: hasWhere');
+    }
+
+    /**
+     * 解析模型的完整命名空间
+     * @access protected
+     * @param  string $model 模型名(或者完整类名)
+     * @return string
+     */
+    protected function parseModel(string $model): string
+    {
+        if (isset($this->alias[$model])) {
+            $model = $this->alias[$model];
+        }
+
+        if (false === strpos($model, '\\')) {
+            $path = explode('\\', get_class($this->parent));
+            array_pop($path);
+            array_push($path, Str::studly($model));
+            $model = implode('\\', $path);
+        }
+
+        return $model;
+    }
+
+    /**
+     * 设置多态别名
+     * @access public
+     * @param  array $alias 别名定义
+     * @return $this
+     */
+    public function setAlias(array $alias)
+    {
+        $this->alias = $alias;
+
+        return $this;
+    }
+
+    /**
+     * 移除关联查询参数
+     * @access public
+     * @return $this
+     */
+    public function removeOption()
+    {
+        return $this;
+    }
+
+    /**
+     * 预载入关联查询
+     * @access public
+     * @param  array   $resultSet   数据集
+     * @param  string  $relation    当前关联名
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包
+     * @param  array   $cache       关联缓存
+     * @return void
+     * @throws Exception
+     */
+    public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null, array $cache = []): void
+    {
+        $morphKey  = $this->morphKey;
+        $morphType = $this->morphType;
+        $range     = [];
+
+        foreach ($resultSet as $result) {
+            // 获取关联外键列表
+            if (!empty($result->$morphKey)) {
+                $range[$result->$morphType][] = $result->$morphKey;
+            }
+        }
+
+        if (!empty($range)) {
+
+            foreach ($range as $key => $val) {
+                // 多态类型映射
+                $model = $this->parseModel($key);
+                $obj   = new $model;
+                $pk    = $obj->getPk();
+                $list  = $obj->with($subRelation)
+                    ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null)
+                    ->select($val);
+                $data = [];
+
+                foreach ($list as $k => $vo) {
+                    $data[$vo->$pk] = $vo;
+                }
+
+                foreach ($resultSet as $result) {
+                    if ($key == $result->$morphType) {
+                        // 关联模型
+                        if (!isset($data[$result->$morphKey])) {
+                            $relationModel = null;
+                        } else {
+                            $relationModel = $data[$result->$morphKey];
+                            $relationModel->setParent(clone $result);
+                            $relationModel->exists(true);
+                        }
+
+                        $result->setRelation($relation, $relationModel);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * 预载入关联查询
+     * @access public
+     * @param  Model   $result      数据对象
+     * @param  string  $relation    当前关联名
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包
+     * @param  array   $cache       关联缓存
+     * @return void
+     */
+    public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void
+    {
+        // 多态类型映射
+        $model = $this->parseModel($result->{$this->morphType});
+
+        $this->eagerlyMorphToOne($model, $relation, $result, $subRelation, $cache);
+    }
+
+    /**
+     * 关联统计
+     * @access public
+     * @param  Model   $result  数据对象
+     * @param  Closure $closure 闭包
+     * @param  string  $aggregate 聚合查询方法
+     * @param  string  $field 字段
+     * @return integer
+     */
+    public function relationCount(Model $result, Closure $closure = null, string $aggregate = 'count', string $field = '*')
+    {}
+
+    /**
+     * 多态MorphTo 关联模型预查询
+     * @access protected
+     * @param  string $model       关联模型对象
+     * @param  string $relation    关联名
+     * @param  Model  $result
+     * @param  array  $subRelation 子关联
+     * @param  array  $cache       关联缓存
+     * @return void
+     */
+    protected function eagerlyMorphToOne(string $model, string $relation, Model $result, array $subRelation = [], array $cache = []): void
+    {
+        // 预载入关联查询 支持嵌套预载入
+        $pk   = $this->parent->{$this->morphKey};
+        $data = (new $model)->with($subRelation)
+            ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null)
+            ->find($pk);
+
+        if ($data) {
+            $data->setParent(clone $result);
+            $data->exists(true);
+        }
+
+        $result->setRelation($relation, $data ?: null);
+    }
+
+    /**
+     * 添加关联数据
+     * @access public
+     * @param  Model  $model  关联模型对象
+     * @param  string $type   多态类型
+     * @return Model
+     */
+    public function associate(Model $model, string $type = ''): Model
+    {
+        $morphKey  = $this->morphKey;
+        $morphType = $this->morphType;
+        $pk        = $model->getPk();
+
+        $this->parent->setAttr($morphKey, $model->$pk);
+        $this->parent->setAttr($morphType, $type ?: get_class($model));
+        $this->parent->save();
+
+        return $this->parent->setRelation($this->relation, $model);
+    }
+
+    /**
+     * 注销关联数据
+     * @access public
+     * @return Model
+     */
+    public function dissociate(): Model
+    {
+        $morphKey  = $this->morphKey;
+        $morphType = $this->morphType;
+
+        $this->parent->setAttr($morphKey, null);
+        $this->parent->setAttr($morphType, null);
+        $this->parent->save();
+
+        return $this->parent->setRelation($this->relation, null);
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/model/relation/MorphToMany.php b/vendor/topthink/think-orm/src/model/relation/MorphToMany.php
new file mode 100755
index 0000000..88bbf9a
--- /dev/null
+++ b/vendor/topthink/think-orm/src/model/relation/MorphToMany.php
@@ -0,0 +1,458 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\model\relation;
+
+use Closure;
+use Exception;
+use think\db\BaseQuery as Query;
+use think\db\Raw;
+use think\Model;
+use think\model\Pivot;
+
+/**
+ * 多态多对多关联
+ */
+class MorphToMany extends BelongsToMany
+{
+
+    /**
+     * 多态字段名
+     * @var string
+     */
+    protected $morphType;
+
+    /**
+     * 多态模型名
+     * @var string
+     */
+    protected $morphClass;
+
+    /**
+     * 是否反向关联
+     * @var bool
+     */
+    protected $inverse;
+
+    /**
+     * 架构函数
+     * @access public
+     * @param  Model  $parent    上级模型对象
+     * @param  string $model     模型名
+     * @param  string $middle    中间表名/模型名
+     * @param  string $morphKey  关联外键
+     * @param  string $morphType 多态字段名
+     * @param  string $localKey  当前模型关联键
+     * @param  bool   $inverse   反向关联
+     */
+    public function __construct(Model $parent, string $model, string $middle, string $morphType, string $morphKey, string $localKey, bool $inverse = false)
+    {
+        $this->morphType  = $morphType;
+        $this->inverse    = $inverse;
+        $this->morphClass = $inverse ? $model : get_class($parent);
+
+        $foreignKey = $inverse ? $morphKey : $localKey;
+        $localKey   = $inverse ? $localKey : $morphKey;
+
+        parent::__construct($parent, $model, $middle, $foreignKey, $localKey);
+    }
+
+    /**
+     * 预载入关联查询(数据集)
+     * @access public
+     * @param  array   $resultSet   数据集
+     * @param  string  $relation    当前关联名
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包
+     * @param  array   $cache       关联缓存
+     * @return void
+     */
+    public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null, array $cache = []): void
+    {
+        $pk    = $resultSet[0]->getPk();
+        $range = [];
+
+        foreach ($resultSet as $result) {
+            // 获取关联外键列表
+            if (isset($result->$pk)) {
+                $range[] = $result->$pk;
+            }
+        }
+
+        if (!empty($range)) {
+            // 查询关联数据
+            $data = $this->eagerlyManyToMany([
+                ['pivot.' . $this->localKey, 'in', $range],
+                ['pivot.' . $this->morphType, '=', $this->morphClass],
+            ], $subRelation, $closure, $cache);
+
+            // 关联数据封装
+            foreach ($resultSet as $result) {
+                if (!isset($data[$result->$pk])) {
+                    $data[$result->$pk] = [];
+                }
+
+                $result->setRelation($relation, $this->resultSetBuild($data[$result->$pk], clone $this->parent));
+            }
+        }
+    }
+
+    /**
+     * 预载入关联查询(单个数据)
+     * @access public
+     * @param  Model   $result      数据对象
+     * @param  string  $relation    当前关联名
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包
+     * @param  array   $cache       关联缓存
+     * @return void
+     */
+    public function eagerlyResult(Model $result, string $relation, array $subRelation, Closure $closure = null, array $cache = []): void
+    {
+        $pk = $result->getPk();
+
+        if (isset($result->$pk)) {
+            $pk = $result->$pk;
+            // 查询管理数据
+            $data = $this->eagerlyManyToMany([
+                ['pivot.' . $this->localKey, '=', $pk],
+                ['pivot.' . $this->morphType, '=', $this->morphClass],
+            ], $subRelation, $closure, $cache);
+
+            // 关联数据封装
+            if (!isset($data[$pk])) {
+                $data[$pk] = [];
+            }
+
+            $result->setRelation($relation, $this->resultSetBuild($data[$pk], clone $this->parent));
+        }
+    }
+
+    /**
+     * 关联统计
+     * @access public
+     * @param  Model   $result  数据对象
+     * @param  Closure $closure 闭包
+     * @param  string  $aggregate 聚合查询方法
+     * @param  string  $field 字段
+     * @param  string  $name 统计字段别名
+     * @return integer
+     */
+    public function relationCount(Model $result, Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): float
+    {
+        $pk = $result->getPk();
+
+        if (!isset($result->$pk)) {
+            return 0;
+        }
+
+        $pk = $result->$pk;
+
+        if ($closure) {
+            $closure($this->getClosureType($closure), $name);
+        }
+
+        return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [
+            ['pivot.' . $this->localKey, '=', $pk],
+            ['pivot.' . $this->morphType, '=', $this->morphClass],
+        ])->$aggregate($field);
+    }
+
+    /**
+     * 获取关联统计子查询
+     * @access public
+     * @param  Closure $closure 闭包
+     * @param  string  $aggregate 聚合查询方法
+     * @param  string  $field 字段
+     * @param  string  $name 统计字段别名
+     * @return string
+     */
+    public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string
+    {
+        if ($closure) {
+            $closure($this->getClosureType($closure), $name);
+        }
+
+        return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [
+            ['pivot.' . $this->localKey, 'exp', new Raw('=' . $this->parent->db(false)->getTable() . '.' . $this->parent->getPk())],
+            ['pivot.' . $this->morphType, '=', $this->morphClass],
+        ])->fetchSql()->$aggregate($field);
+    }
+
+    /**
+     * BELONGS TO MANY 关联查询
+     * @access protected
+     * @param  string $foreignKey 关联模型关联键
+     * @param  string $localKey   当前模型关联键
+     * @param  array  $condition  关联查询条件
+     * @return Query
+     */
+    protected function belongsToManyQuery(string $foreignKey, string $localKey, array $condition = []): Query
+    {
+        // 关联查询封装
+        $tableName = $this->query->getTable();
+        $table     = $this->pivot->db()->getTable();
+        $fields    = $this->getQueryFields($tableName);
+
+        if ($this->withLimit) {
+            $this->query->limit($this->withLimit);
+        }
+
+        $query = $this->query
+            ->field($fields)
+            ->tableField(true, $table, 'pivot', 'pivot__');
+
+        if (empty($this->baseQuery)) {
+            $relationFk = $this->query->getPk();
+            $query->join([$table => 'pivot'], 'pivot.' . $foreignKey . '=' . $tableName . '.' . $relationFk)
+                ->where($condition);
+        }
+
+        return $query;
+    }
+
+    /**
+     * 多对多 关联模型预查询
+     * @access protected
+     * @param  array   $where       关联预查询条件
+     * @param  array   $subRelation 子关联
+     * @param  Closure $closure     闭包
+     * @param  array   $cache       关联缓存
+     * @return array
+     */
+    protected function eagerlyManyToMany(array $where, array $subRelation = [], Closure $closure = null, array $cache = []): array
+    {
+        if ($closure) {
+            $closure($this->getClosureType($closure));
+        }
+
+        // 预载入关联查询 支持嵌套预载入
+        $list = $this->belongsToManyQuery($this->foreignKey, $this->localKey, $where)
+            ->with($subRelation)
+            ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null)
+            ->select();
+
+        // 组装模型数据
+        $data = [];
+        foreach ($list as $set) {
+            $pivot = [];
+            foreach ($set->getData() as $key => $val) {
+                if (strpos($key, '__')) {
+                    [$name, $attr] = explode('__', $key, 2);
+                    if ('pivot' == $name) {
+                        $pivot[$attr] = $val;
+                        unset($set->$key);
+                    }
+                }
+            }
+
+            $key = $pivot[$this->localKey];
+
+            if ($this->withLimit && isset($data[$key]) && count($data[$key]) >= $this->withLimit) {
+                continue;
+            }
+
+            $set->setRelation($this->pivotDataName, $this->newPivot($pivot));
+
+            $data[$key][] = $set;
+        }
+
+        return $data;
+    }
+
+    /**
+     * 附加关联的一个中间表数据
+     * @access public
+     * @param  mixed $data  数据 可以使用数组、关联模型对象 或者 关联对象的主键
+     * @param  array $pivot 中间表额外数据
+     * @return array|Pivot
+     */
+    public function attach($data, array $pivot = [])
+    {
+        if (is_array($data)) {
+            if (key($data) === 0) {
+                $id = $data;
+            } else {
+                // 保存关联表数据
+                $model = new $this->model;
+                $id    = $model->insertGetId($data);
+            }
+        } else if (is_numeric($data) || is_string($data)) {
+            // 根据关联表主键直接写入中间表
+            $id = $data;
+        } else if ($data instanceof Model) {
+            // 根据关联表主键直接写入中间表
+            $id = $data->getKey();
+        }
+
+        if (!empty($id)) {
+            // 保存中间表数据
+            $pivot[$this->localKey]  = $this->parent->getKey();
+            $pivot[$this->morphType] = $this->morphClass;
+            $ids                     = (array) $id;
+
+            $result = [];
+
+            foreach ($ids as $id) {
+                $pivot[$this->foreignKey] = $id;
+
+                $this->pivot->replace()
+                    ->exists(false)
+                    ->data([])
+                    ->save($pivot);
+                $result[] = $this->newPivot($pivot);
+            }
+
+            if (count($result) == 1) {
+                // 返回中间表模型对象
+                $result = $result[0];
+            }
+
+            return $result;
+        } else {
+            throw new Exception('miss relation data');
+        }
+    }
+
+    /**
+     * 判断是否存在关联数据
+     * @access public
+     * @param  mixed $data 数据 可以使用关联模型对象 或者 关联对象的主键
+     * @return Pivot|false
+     */
+    public function attached($data)
+    {
+        if ($data instanceof Model) {
+            $id = $data->getKey();
+        } else {
+            $id = $data;
+        }
+
+        $pivot = $this->pivot
+            ->where($this->localKey, $this->parent->getKey())
+            ->where($this->morphType, $this->morphClass)
+            ->where($this->foreignKey, $id)
+            ->find();
+
+        return $pivot ?: false;
+    }
+
+    /**
+     * 解除关联的一个中间表数据
+     * @access public
+     * @param  integer|array $data        数据 可以使用关联对象的主键
+     * @param  bool          $relationDel 是否同时删除关联表数据
+     * @return integer
+     */
+    public function detach($data = null, bool $relationDel = false): int
+    {
+        if (is_array($data)) {
+            $id = $data;
+        } else if (is_numeric($data) || is_string($data)) {
+            // 根据关联表主键直接写入中间表
+            $id = $data;
+        } else if ($data instanceof Model) {
+            // 根据关联表主键直接写入中间表
+            $id = $data->getKey();
+        }
+
+        // 删除中间表数据
+        $pivot = [
+            [$this->localKey, '=', $this->parent->getKey()],
+            [$this->morphType, '=', $this->morphClass],
+        ];
+
+        if (isset($id)) {
+            $pivot[] = [$this->foreignKey, is_array($id) ? 'in' : '=', $id];
+        }
+
+        $result = $this->pivot->where($pivot)->delete();
+
+        // 删除关联表数据
+        if (isset($id) && $relationDel) {
+            $model = $this->model;
+            $model::destroy($id);
+        }
+
+        return $result;
+    }
+
+    /**
+     * 数据同步
+     * @access public
+     * @param  array $ids
+     * @param  bool  $detaching
+     * @return array
+     */
+    public function sync(array $ids, bool $detaching = true): array
+    {
+        $changes = [
+            'attached' => [],
+            'detached' => [],
+            'updated'  => [],
+        ];
+
+        $current = $this->pivot
+            ->where($this->localKey, $this->parent->getKey())
+            ->where($this->morphType, $this->morphClass)
+            ->column($this->foreignKey);
+
+        $records = [];
+
+        foreach ($ids as $key => $value) {
+            if (!is_array($value)) {
+                $records[$value] = [];
+            } else {
+                $records[$key] = $value;
+            }
+        }
+
+        $detach = array_diff($current, array_keys($records));
+
+        if ($detaching && count($detach) > 0) {
+            $this->detach($detach);
+            $changes['detached'] = $detach;
+        }
+
+        foreach ($records as $id => $attributes) {
+            if (!in_array($id, $current)) {
+                $this->attach($id, $attributes);
+                $changes['attached'][] = $id;
+            } else if (count($attributes) > 0 && $this->attach($id, $attributes)) {
+                $changes['updated'][] = $id;
+            }
+        }
+
+        return $changes;
+    }
+
+    /**
+     * 执行基础查询(仅执行一次)
+     * @access protected
+     * @return void
+     */
+    protected function baseQuery(): void
+    {
+        if (empty($this->baseQuery)) {
+            $foreignKey = $this->foreignKey;
+            $localKey   = $this->localKey;
+
+            // 关联查询
+            $this->belongsToManyQuery($foreignKey, $localKey, [
+                ['pivot.' . $localKey, '=', $this->parent->getKey()],
+                ['pivot.' . $this->morphType, '=', $this->morphClass],
+            ]);
+
+            $this->baseQuery = true;
+        }
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/model/relation/OneToOne.php b/vendor/topthink/think-orm/src/model/relation/OneToOne.php
new file mode 100755
index 0000000..efb2359
--- /dev/null
+++ b/vendor/topthink/think-orm/src/model/relation/OneToOne.php
@@ -0,0 +1,332 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\model\relation;
+
+use Closure;
+use think\db\BaseQuery as Query;
+use think\db\exception\DbException as Exception;
+use think\helper\Str;
+use think\Model;
+use think\model\Relation;
+
+/**
+ * 一对一关联基础类
+ * @package think\model\relation
+ */
+abstract class OneToOne extends Relation
+{
+    /**
+     * JOIN类型
+     * @var string
+     */
+    protected $joinType = 'INNER';
+
+    /**
+     * 绑定的关联属性
+     * @var array
+     */
+    protected $bindAttr = [];
+
+    /**
+     * 关联名
+     * @var string
+     */
+    protected $relation;
+
+    /**
+     * 设置join类型
+     * @access public
+     * @param  string $type JOIN类型
+     * @return $this
+     */
+    public function joinType(string $type)
+    {
+        $this->joinType = $type;
+        return $this;
+    }
+
+    /**
+     * 预载入关联查询(JOIN方式)
+     * @access public
+     * @param  Query   $query       查询对象
+     * @param  string  $relation    关联名
+     * @param  mixed   $field       关联字段
+     * @param  string  $joinType    JOIN方式
+     * @param  Closure $closure     闭包条件
+     * @param  bool    $first
+     * @return void
+     */
+    public function eagerly(Query $query, string $relation, $field = true, string $joinType = '', Closure $closure = null, bool $first = false): void
+    {
+        $name = Str::snake(class_basename($this->parent));
+
+        if ($first) {
+            $table = $query->getTable();
+            $query->table([$table => $name]);
+
+            if ($query->getOptions('field')) {
+                $masterField = $query->getOptions('field');
+                $query->removeOption('field');
+            } else {
+                $masterField = true;
+            }
+
+            $query->tableField($masterField, $table, $name);
+        }
+
+        // 预载入封装
+        $joinTable = $this->query->getTable();
+        $joinAlias = $relation;
+        $joinType  = $joinType ?: $this->joinType;
+
+        $query->via($joinAlias);
+
+        if ($this instanceof BelongsTo) {
+            $joinOn = $name . '.' . $this->foreignKey . '=' . $joinAlias . '.' . $this->localKey;
+        } else {
+            $joinOn = $name . '.' . $this->localKey . '=' . $joinAlias . '.' . $this->foreignKey;
+        }
+
+        if ($closure) {
+            // 执行闭包查询
+            $closure($this->getClosureType($closure));
+
+            // 使用withField指定获取关联的字段
+            if ($this->withField) {
+                $field = $this->withField;
+            }
+        }
+
+        $query->join([$joinTable => $joinAlias], $joinOn, $joinType)
+            ->tableField($field, $joinTable, $joinAlias, $relation . '__');
+    }
+
+    /**
+     *  预载入关联查询(数据集)
+     * @access protected
+     * @param  array   $resultSet
+     * @param  string  $relation
+     * @param  array   $subRelation
+     * @param  Closure $closure
+     * @return mixed
+     */
+    abstract protected function eagerlySet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null);
+
+    /**
+     * 预载入关联查询(数据)
+     * @access protected
+     * @param  Model   $result
+     * @param  string  $relation
+     * @param  array   $subRelation
+     * @param  Closure $closure
+     * @return mixed
+     */
+    abstract protected function eagerlyOne(Model $result, string $relation, array $subRelation = [], Closure $closure = null);
+
+    /**
+     * 预载入关联查询(数据集)
+     * @access public
+     * @param  array   $resultSet   数据集
+     * @param  string  $relation    当前关联名
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包
+     * @param  array   $cache       关联缓存
+     * @param  bool    $join        是否为JOIN方式
+     * @return void
+     */
+    public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null, array $cache = [], bool $join = false): void
+    {
+        if ($join) {
+            // 模型JOIN关联组装
+            foreach ($resultSet as $result) {
+                $this->match($this->model, $relation, $result);
+            }
+        } else {
+            // IN查询
+            $this->eagerlySet($resultSet, $relation, $subRelation, $closure, $cache);
+        }
+    }
+
+    /**
+     * 预载入关联查询(数据)
+     * @access public
+     * @param  Model   $result      数据对象
+     * @param  string  $relation    当前关联名
+     * @param  array   $subRelation 子关联名
+     * @param  Closure $closure     闭包
+     * @param  array   $cache       关联缓存
+     * @param  bool    $join        是否为JOIN方式
+     * @return void
+     */
+    public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null, array $cache = [], bool $join = false): void
+    {
+        if ($join) {
+            // 模型JOIN关联组装
+            $this->match($this->model, $relation, $result);
+        } else {
+            // IN查询
+            $this->eagerlyOne($result, $relation, $subRelation, $closure, $cache);
+        }
+    }
+
+    /**
+     * 保存(新增)当前关联数据对象
+     * @access public
+     * @param  mixed   $data 数据 可以使用数组 关联模型对象
+     * @param  boolean $replace 是否自动识别更新和写入
+     * @return Model|false
+     */
+    public function save($data, bool $replace = true)
+    {
+        if ($data instanceof Model) {
+            $data = $data->getData();
+        }
+
+        $model = new $this->model;
+        // 保存关联表数据
+        $data[$this->foreignKey] = $this->parent->{$this->localKey};
+
+        return $model->replace($replace)->save($data) ? $model : false;
+    }
+
+    /**
+     * 绑定关联表的属性到父模型属性
+     * @access public
+     * @param  array $attr 要绑定的属性列表
+     * @return $this
+     */
+    public function bind(array $attr)
+    {
+        $this->bindAttr = $attr;
+
+        return $this;
+    }
+
+    /**
+     * 获取绑定属性
+     * @access public
+     * @return array
+     */
+    public function getBindAttr(): array
+    {
+        return $this->bindAttr;
+    }
+
+    /**
+     * 一对一 关联模型预查询拼装
+     * @access public
+     * @param  string $model    模型名称
+     * @param  string $relation 关联名
+     * @param  Model  $result   模型对象实例
+     * @return void
+     */
+    protected function match(string $model, string $relation, Model $result): void
+    {
+        // 重新组装模型数据
+        foreach ($result->getData() as $key => $val) {
+            if (strpos($key, '__')) {
+                [$name, $attr] = explode('__', $key, 2);
+                if ($name == $relation) {
+                    $list[$name][$attr] = $val;
+                    unset($result->$key);
+                }
+            }
+        }
+
+        if (isset($list[$relation])) {
+            $array = array_unique($list[$relation]);
+
+            if (count($array) == 1 && null === current($array)) {
+                $relationModel = null;
+            } else {
+                $relationModel = new $model($list[$relation]);
+                $relationModel->setParent(clone $result);
+                $relationModel->exists(true);
+            }
+
+            if (!empty($this->bindAttr)) {
+                $this->bindAttr($result, $relationModel);
+            }
+        } else {
+            $relationModel = null;
+        }
+
+        $result->setRelation($relation, $relationModel);
+    }
+
+    /**
+     * 绑定关联属性到父模型
+     * @access protected
+     * @param  Model $result 父模型对象
+     * @param  Model $model  关联模型对象
+     * @return void
+     * @throws Exception
+     */
+    protected function bindAttr(Model $result, Model $model = null): void
+    {
+        foreach ($this->bindAttr as $key => $attr) {
+            $key   = is_numeric($key) ? $attr : $key;
+            $value = $result->getOrigin($key);
+
+            if (!is_null($value)) {
+                throw new Exception('bind attr has exists:' . $key);
+            }
+
+            $result->setAttr($key, $model ? $model->$attr : null);
+        }
+    }
+
+    /**
+     * 一对一 关联模型预查询(IN方式)
+     * @access public
+     * @param  array   $where       关联预查询条件
+     * @param  string  $key         关联键名
+     * @param  array   $subRelation 子关联
+     * @param  Closure $closure
+     * @param  array   $cache       关联缓存
+     * @return array
+     */
+    protected function eagerlyWhere(array $where, string $key, array $subRelation = [], Closure $closure = null, array $cache = [])
+    {
+        // 预载入关联查询 支持嵌套预载入
+        if ($closure) {
+            $this->baseQuery = true;
+            $closure($this->getClosureType($closure));
+        }
+
+        if ($this->withField) {
+            $this->query->field($this->withField);
+        }
+
+        if ($this->query->getOptions('order')) {
+            $this->query->group($key);
+        }
+
+        $list = $this->query
+            ->where($where)
+            ->with($subRelation)
+            ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null)
+            ->select();
+
+        // 组装模型数据
+        $data = [];
+
+        foreach ($list as $set) {
+            if (!isset($data[$set->$key])) {
+                $data[$set->$key] = $set;
+            }
+        }
+
+        return $data;
+    }
+
+}
diff --git a/vendor/topthink/think-orm/src/paginator/driver/Bootstrap.php b/vendor/topthink/think-orm/src/paginator/driver/Bootstrap.php
new file mode 100755
index 0000000..6d55c39
--- /dev/null
+++ b/vendor/topthink/think-orm/src/paginator/driver/Bootstrap.php
@@ -0,0 +1,209 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: zhangyajun <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\paginator\driver;
+
+use think\Paginator;
+
+/**
+ * Bootstrap 分页驱动
+ */
+class Bootstrap extends Paginator
+{
+
+    /**
+     * 上一页按钮
+     * @param string $text
+     * @return string
+     */
+    protected function getPreviousButton(string $text = "&laquo;"): string
+    {
+
+        if ($this->currentPage() <= 1) {
+            return $this->getDisabledTextWrapper($text);
+        }
+
+        $url = $this->url(
+            $this->currentPage() - 1
+        );
+
+        return $this->getPageLinkWrapper($url, $text);
+    }
+
+    /**
+     * 下一页按钮
+     * @param string $text
+     * @return string
+     */
+    protected function getNextButton(string $text = '&raquo;'): string
+    {
+        if (!$this->hasMore) {
+            return $this->getDisabledTextWrapper($text);
+        }
+
+        $url = $this->url($this->currentPage() + 1);
+
+        return $this->getPageLinkWrapper($url, $text);
+    }
+
+    /**
+     * 页码按钮
+     * @return string
+     */
+    protected function getLinks(): string
+    {
+        if ($this->simple) {
+            return '';
+        }
+
+        $block = [
+            'first'  => null,
+            'slider' => null,
+            'last'   => null,
+        ];
+
+        $side   = 3;
+        $window = $side * 2;
+
+        if ($this->lastPage < $window + 6) {
+            $block['first'] = $this->getUrlRange(1, $this->lastPage);
+        } elseif ($this->currentPage <= $window) {
+            $block['first'] = $this->getUrlRange(1, $window + 2);
+            $block['last']  = $this->getUrlRange($this->lastPage - 1, $this->lastPage);
+        } elseif ($this->currentPage > ($this->lastPage - $window)) {
+            $block['first'] = $this->getUrlRange(1, 2);
+            $block['last']  = $this->getUrlRange($this->lastPage - ($window + 2), $this->lastPage);
+        } else {
+            $block['first']  = $this->getUrlRange(1, 2);
+            $block['slider'] = $this->getUrlRange($this->currentPage - $side, $this->currentPage + $side);
+            $block['last']   = $this->getUrlRange($this->lastPage - 1, $this->lastPage);
+        }
+
+        $html = '';
+
+        if (is_array($block['first'])) {
+            $html .= $this->getUrlLinks($block['first']);
+        }
+
+        if (is_array($block['slider'])) {
+            $html .= $this->getDots();
+            $html .= $this->getUrlLinks($block['slider']);
+        }
+
+        if (is_array($block['last'])) {
+            $html .= $this->getDots();
+            $html .= $this->getUrlLinks($block['last']);
+        }
+
+        return $html;
+    }
+
+    /**
+     * 渲染分页html
+     * @return mixed
+     */
+    public function render()
+    {
+        if ($this->hasPages()) {
+            if ($this->simple) {
+                return sprintf(
+                    '<ul class="pager">%s %s</ul>',
+                    $this->getPreviousButton(),
+                    $this->getNextButton()
+                );
+            } else {
+                return sprintf(
+                    '<ul class="pagination">%s %s %s</ul>',
+                    $this->getPreviousButton(),
+                    $this->getLinks(),
+                    $this->getNextButton()
+                );
+            }
+        }
+    }
+
+    /**
+     * 生成一个可点击的按钮
+     *
+     * @param  string $url
+     * @param  string $page
+     * @return string
+     */
+    protected function getAvailablePageWrapper(string $url, string $page): string
+    {
+        return '<li><a href="' . htmlentities($url) . '">' . $page . '</a></li>';
+    }
+
+    /**
+     * 生成一个禁用的按钮
+     *
+     * @param  string $text
+     * @return string
+     */
+    protected function getDisabledTextWrapper(string $text): string
+    {
+        return '<li class="disabled"><span>' . $text . '</span></li>';
+    }
+
+    /**
+     * 生成一个激活的按钮
+     *
+     * @param  string $text
+     * @return string
+     */
+    protected function getActivePageWrapper(string $text): string
+    {
+        return '<li class="active"><span>' . $text . '</span></li>';
+    }
+
+    /**
+     * 生成省略号按钮
+     *
+     * @return string
+     */
+    protected function getDots(): string
+    {
+        return $this->getDisabledTextWrapper('...');
+    }
+
+    /**
+     * 批量生成页码按钮.
+     *
+     * @param  array $urls
+     * @return string
+     */
+    protected function getUrlLinks(array $urls): string
+    {
+        $html = '';
+
+        foreach ($urls as $page => $url) {
+            $html .= $this->getPageLinkWrapper($url, $page);
+        }
+
+        return $html;
+    }
+
+    /**
+     * 生成普通页码按钮
+     *
+     * @param  string $url
+     * @param  string    $page
+     * @return string
+     */
+    protected function getPageLinkWrapper(string $url, string $page): string
+    {
+        if ($this->currentPage() == $page) {
+            return $this->getActivePageWrapper($page);
+        }
+
+        return $this->getAvailablePageWrapper($url, $page);
+    }
+}
diff --git a/vendor/topthink/think-queue/.gitignore b/vendor/topthink/think-queue/.gitignore
new file mode 100755
index 0000000..4aec782
--- /dev/null
+++ b/vendor/topthink/think-queue/.gitignore
@@ -0,0 +1,4 @@
+/vendor/
+/.idea/
+/composer.lock
+/thinkphp/
diff --git a/vendor/topthink/think-queue/LICENSE b/vendor/topthink/think-queue/LICENSE
new file mode 100755
index 0000000..8dada3e
--- /dev/null
+++ b/vendor/topthink/think-queue/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/vendor/topthink/think-queue/README.md b/vendor/topthink/think-queue/README.md
new file mode 100755
index 0000000..08266d8
--- /dev/null
+++ b/vendor/topthink/think-queue/README.md
@@ -0,0 +1,118 @@
+# think-queue for ThinkPHP6
+
+## 安装
+
+> composer require topthink/think-queue
+
+## 配置
+
+> 配置文件位于 `config/queue.php`
+
+### 公共配置
+
+```
+[
+    'default'=>'sync' //驱动类型,可选择 sync(默认):同步执行,database:数据库驱动,redis:Redis驱动,topthink:Topthink驱动
+                   //或其他自定义的完整的类名
+]
+```
+
+### 驱动配置
+> 各个驱动的具体可用配置项在`think\queue\connector`目录下各个驱动类里的`options`属性中,写在上面的`queue`配置里即可覆盖
+
+
+## 创建任务类
+> 单模块项目推荐使用 `app\job` 作为任务类的命名空间
+> 多模块项目可用使用 `app\module\job` 作为任务类的命名空间
+> 也可以放在任意可以自动加载到的地方
+
+任务类不需继承任何类,如果这个类只有一个任务,那么就只需要提供一个`fire`方法就可以了,如果有多个小任务,就写多个方法,下面发布任务的时候会有区别  
+每个方法会传入两个参数 `think\queue\Job $job`(当前的任务对象) 和 `$data`(发布任务时自定义的数据)
+
+还有个可选的任务失败执行的方法 `failed` 传入的参数为`$data`(发布任务时自定义的数据)
+
+### 下面写两个例子
+
+```
+namespace app\job;
+
+use think\queue\Job;
+
+class Job1{
+    
+    public function fire(Job $job, $data){
+    
+            //....这里执行具体的任务 
+            
+             if ($job->attempts() > 3) {
+                  //通过这个方法可以检查这个任务已经重试了几次了
+             }
+            
+            
+            //如果任务执行成功后 记得删除任务,不然这个任务会重复执行,直到达到最大重试次数后失败后,执行failed方法
+            $job->delete();
+            
+            // 也可以重新发布这个任务
+            $job->release($delay); //$delay为延迟时间
+          
+    }
+    
+    public function failed($data){
+    
+        // ...任务达到最大重试次数后,失败了
+    }
+
+}
+
+```
+
+```
+
+namespace app\lib\job;
+
+use think\queue\Job;
+
+class Job2{
+    
+    public function task1(Job $job, $data){
+    
+          
+    }
+    
+    public function task2(Job $job, $data){
+    
+          
+    }
+    
+    public function failed($data){
+    
+          
+    }
+
+}
+
+```
+
+
+## 发布任务
+> `think\Queue::push($job, $data = '', $queue = null)` 和 `think\Queue::later($delay, $job, $data = '', $queue = null)` 两个方法,前者是立即执行,后者是在`$delay`秒后执行
+
+`$job` 是任务名  
+单模块的,且命名空间是`app\job`的,比如上面的例子一,写`Job1`类名即可  
+多模块的,且命名空间是`app\module\job`的,写`model/Job1`即可  
+其他的需要些完整的类名,比如上面的例子二,需要写完整的类名`app\lib\job\Job2`  
+如果一个任务类里有多个小任务的话,如上面的例子二,需要用@+方法名`app\lib\job\Job2@task1`、`app\lib\job\Job2@task2`
+
+`$data` 是你要传到任务里的参数
+
+`$queue` 队列名,指定这个任务是在哪个队列上执行,同下面监控队列的时候指定的队列名,可不填
+
+## 监听任务并执行
+
+> php think queue:listen
+
+> php think queue:work
+
+两种,具体的可选参数可以输入命令加 --help 查看
+
+>可配合supervisor使用,保证进程常驻
diff --git a/vendor/topthink/think-queue/composer.json b/vendor/topthink/think-queue/composer.json
new file mode 100755
index 0000000..c128c25
--- /dev/null
+++ b/vendor/topthink/think-queue/composer.json
@@ -0,0 +1,47 @@
+{
+    "name": "topthink/think-queue",
+    "description": "The ThinkPHP5 Queue Package",
+    "authors": [
+        {
+            "name": "yunwuxin",
+            "email": "448901948@qq.com"
+        }
+    ],
+    "license": "Apache-2.0",
+    "autoload": {
+        "psr-4": {
+            "think\\": "src"
+        },
+        "files": [
+            "src/common.php"
+        ]
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "think\\test\\queue\\": "tests"
+        }
+    },
+    "minimum-stability": "dev",
+    "require": {
+        "topthink/think-helper": "^3.0.4",
+        "topthink/framework": "^6.0.0",
+        "topthink/think-factory": "^1.0.0",
+        "symfony/process": "^4.2",
+        "nesbot/carbon": "^2.16"
+    },
+    "extra": {
+        "think": {
+            "services": [
+                "think\\queue\\Service"
+            ],
+            "config": {
+                "queue": "src/config.php"
+            }
+        }
+    },
+    "require-dev": {
+        "phpunit/phpunit": "^6.2",
+        "mockery/mockery": "^1.2",
+        "topthink/think-migration": "^3.0.0"
+    }
+}
diff --git a/vendor/topthink/think-queue/phpunit.xml.dist b/vendor/topthink/think-queue/phpunit.xml.dist
new file mode 100755
index 0000000..0234fbc
--- /dev/null
+++ b/vendor/topthink/think-queue/phpunit.xml.dist
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit backupGlobals="false"
+         backupStaticAttributes="false"
+         beStrictAboutTestsThatDoNotTestAnything="false"
+         bootstrap="tests/bootstrap.php"
+         colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         processIsolation="false"
+         stopOnError="false"
+         stopOnFailure="false"
+         verbose="true"
+>
+    <testsuites>
+        <testsuite name="Think Queue Test Suite">
+            <directory suffix="Test.php">./tests</directory>
+        </testsuite>
+    </testsuites>
+    <filter>
+        <whitelist processUncoveredFilesFromWhitelist="true">
+            <directory suffix=".php">./src</directory>
+            <exclude>
+                <file>./src/queue/Service.php</file>
+                <file>./src/common.php</file>
+                <file>./src/config.php</file>
+            </exclude>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/vendor/topthink/think-queue/src/Queue.php b/vendor/topthink/think-queue/src/Queue.php
new file mode 100755
index 0000000..175d89f
--- /dev/null
+++ b/vendor/topthink/think-queue/src/Queue.php
@@ -0,0 +1,77 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+use InvalidArgumentException;
+use think\cache\driver\Redis;
+use think\helper\Str;
+use think\queue\Connector;
+use think\queue\connector\Database;
+
+/**
+ * Class Queue
+ * @package think\queue
+ *
+ * @method Connector driver($driver = null)
+ * @mixin Database
+ * @mixin Redis
+ */
+class Queue extends Factory
+{
+    protected $namespace = '\\think\\queue\\connector\\';
+
+    /**
+     * Get the queue connector configuration.
+     *
+     * @param string $name
+     * @return array
+     */
+    protected function getConfig($name)
+    {
+        return $this->app->config->get("queue.connections.{$name}", ['driver' => 'sync']);
+    }
+
+    protected function createDriver($name)
+    {
+        $driver = $this->getConfig($name)['driver'];
+
+        $class = false !== strpos($driver, '\\') ? $driver : $this->namespace . Str::studly($driver);
+
+        /** @var Connector $driver */
+        if (class_exists($class)) {
+            $driver = $this->app->invokeClass($class, [$this->getConfig($driver)]);
+
+            return $driver->setApp($this->app)
+                ->setConnection($name);
+        }
+
+        throw new InvalidArgumentException("Driver [$driver] not supported.");
+    }
+
+    /**
+     * @param null|string $name
+     * @return Connector
+     */
+    public function connection($name = null)
+    {
+        return $this->driver($name);
+    }
+
+    /**
+     * 默认驱动
+     * @return string
+     */
+    public function getDefaultDriver()
+    {
+        return $this->app->config->get('queue.default');
+    }
+}
diff --git a/vendor/topthink/think-queue/src/common.php b/vendor/topthink/think-queue/src/common.php
new file mode 100755
index 0000000..207b0b8
--- /dev/null
+++ b/vendor/topthink/think-queue/src/common.php
@@ -0,0 +1,31 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+use think\facade\Queue;
+
+if (!function_exists('queue')) {
+
+    /**
+     * 添加到队列
+     * @param        $job
+     * @param string $data
+     * @param int    $delay
+     * @param null   $queue
+     */
+    function queue($job, $data = '', $delay = 0, $queue = null)
+    {
+        if ($delay > 0) {
+            Queue::later($delay, $job, $data, $queue);
+        } else {
+            Queue::push($job, $data, $queue);
+        }
+    }
+}
diff --git a/vendor/topthink/think-queue/src/config.php b/vendor/topthink/think-queue/src/config.php
new file mode 100755
index 0000000..e2f25c8
--- /dev/null
+++ b/vendor/topthink/think-queue/src/config.php
@@ -0,0 +1,38 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+return [
+    'default'     => 'sync',
+    'connections' => [
+        'sync'     => [
+            'driver' => 'sync',
+        ],
+        'database' => [
+            'driver' => 'database',
+            'queue'  => 'default',
+            'table'  => 'jobs',
+        ],
+        'redis'    => [
+            'driver'     => 'redis',
+            'queue'      => 'default',
+            'host'       => '127.0.0.1',
+            'port'       => 6379,
+            'password'   => '',
+            'select'     => 0,
+            'timeout'    => 0,
+            'persistent' => false,
+        ],
+    ],
+    'failed'      => [
+        'type'  => 'none',
+        'table' => 'failed_jobs',
+    ],
+];
diff --git a/vendor/topthink/think-queue/src/facade/Queue.php b/vendor/topthink/think-queue/src/facade/Queue.php
new file mode 100755
index 0000000..23c2c30
--- /dev/null
+++ b/vendor/topthink/think-queue/src/facade/Queue.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace think\facade;
+
+use think\Facade;
+
+/**
+ * Class Queue
+ * @package think\facade
+ * @mixin \think\Queue
+ */
+class Queue extends Facade
+{
+    protected static function getFacadeClass()
+    {
+        return 'queue';
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/CallQueuedHandler.php b/vendor/topthink/think-queue/src/queue/CallQueuedHandler.php
new file mode 100755
index 0000000..0f1a627
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/CallQueuedHandler.php
@@ -0,0 +1,36 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\queue;
+
+class CallQueuedHandler
+{
+
+    public function call(Job $job, array $data)
+    {
+        $command = unserialize($data['command']);
+
+        call_user_func([$command, 'handle']);
+
+        if (!$job->isDeletedOrReleased()) {
+            $job->delete();
+        }
+    }
+
+    public function failed(array $data)
+    {
+        $command = unserialize($data['command']);
+
+        if (method_exists($command, 'failed')) {
+            $command->failed();
+        }
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/Connector.php b/vendor/topthink/think-queue/src/queue/Connector.php
new file mode 100755
index 0000000..618500e
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/Connector.php
@@ -0,0 +1,163 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\queue;
+
+use DateTimeInterface;
+use InvalidArgumentException;
+use think\App;
+
+abstract class Connector
+{
+    /** @var App */
+    protected $app;
+
+    /**
+     * The connector name for the queue.
+     *
+     * @var string
+     */
+    protected $connection;
+
+    protected $options = [];
+
+    abstract public function size($queue);
+
+    abstract public function push($job, $data = '', $queue = null);
+
+    public function pushOn($queue, $job, $data = '')
+    {
+        return $this->push($job, $data, $queue);
+    }
+
+    abstract public function pushRaw($payload, $queue = null, array $options = []);
+
+    abstract public function later($delay, $job, $data = '', $queue = null);
+
+    public function laterOn($queue, $delay, $job, $data = '')
+    {
+        return $this->later($delay, $job, $data, $queue);
+    }
+
+    public function bulk($jobs, $data = '', $queue = null)
+    {
+        foreach ((array) $jobs as $job) {
+            $this->push($job, $data, $queue);
+        }
+    }
+
+    abstract public function pop($queue = null);
+
+    protected function createPayload($job, $data = '')
+    {
+        $payload = $this->createPayloadArray($job, $data);
+
+        $payload = json_encode($payload);
+
+        if (JSON_ERROR_NONE !== json_last_error()) {
+            throw new InvalidArgumentException('Unable to create payload: ' . json_last_error_msg());
+        }
+
+        return $payload;
+    }
+
+    protected function createPayloadArray($job, $data = '')
+    {
+        return is_object($job)
+            ? $this->createObjectPayload($job)
+            : $this->createPlainPayload($job, $data);
+    }
+
+    protected function createPlainPayload($job, $data)
+    {
+        return [
+            'job'      => $job,
+            'maxTries' => null,
+            'timeout'  => null,
+            'data'     => $data,
+        ];
+    }
+
+    protected function createObjectPayload($job)
+    {
+        $payload = [
+            'job'       => 'think\queue\CallQueuedHandler@call',
+            'maxTries'  => $job->tries ?? null,
+            'timeout'   => $job->timeout ?? null,
+            'timeoutAt' => $this->getJobExpiration($job),
+            'data'      => [
+                'commandName' => $job,
+                'command'     => $job,
+            ],
+        ];
+
+        return array_merge($payload, [
+            'data' => [
+                'commandName' => get_class($job),
+                'command'     => serialize(clone $job),
+            ],
+        ]);
+    }
+
+    public function getJobExpiration($job)
+    {
+        if (!method_exists($job, 'retryUntil') && !isset($job->timeoutAt)) {
+            return;
+        }
+
+        $expiration = $job->timeoutAt ?? $job->retryUntil();
+
+        return $expiration instanceof DateTimeInterface
+            ? $expiration->getTimestamp() : $expiration;
+    }
+
+    protected function setMeta($payload, $key, $value)
+    {
+        $payload       = json_decode($payload, true);
+        $payload[$key] = $value;
+        $payload       = json_encode($payload);
+
+        if (JSON_ERROR_NONE !== json_last_error()) {
+            throw new InvalidArgumentException('Unable to create payload: ' . json_last_error_msg());
+        }
+
+        return $payload;
+    }
+
+    public function setApp(App $app)
+    {
+        $this->app = $app;
+        return $this;
+    }
+
+    /**
+     * Get the connector name for the queue.
+     *
+     * @return string
+     */
+    public function getConnection()
+    {
+        return $this->connection;
+    }
+
+    /**
+     * Set the connector name for the queue.
+     *
+     * @param string $name
+     * @return $this
+     */
+    public function setConnection($name)
+    {
+        $this->connection = $name;
+
+        return $this;
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/FailedJob.php b/vendor/topthink/think-queue/src/queue/FailedJob.php
new file mode 100755
index 0000000..e03f816
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/FailedJob.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace think\queue;
+
+abstract class FailedJob
+{
+    /**
+     * Log a failed job into storage.
+     *
+     * @param string     $connection
+     * @param string     $queue
+     * @param string     $payload
+     * @param \Exception $exception
+     * @return int|null
+     */
+    abstract public function log($connection, $queue, $payload, $exception);
+
+    /**
+     * Get a list of all of the failed jobs.
+     *
+     * @return array
+     */
+    abstract public function all();
+
+    /**
+     * Get a single failed job.
+     *
+     * @param mixed $id
+     * @return object|null
+     */
+    abstract public function find($id);
+
+    /**
+     * Delete a single failed job from storage.
+     *
+     * @param mixed $id
+     * @return bool
+     */
+    abstract public function forget($id);
+
+    /**
+     * Flush all of the failed jobs from storage.
+     *
+     * @return void
+     */
+    abstract public function flush();
+}
diff --git a/vendor/topthink/think-queue/src/queue/InteractsWithTime.php b/vendor/topthink/think-queue/src/queue/InteractsWithTime.php
new file mode 100755
index 0000000..04602c9
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/InteractsWithTime.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace think\queue;
+
+use Carbon\Carbon;
+use DateInterval;
+use DateTimeInterface;
+
+trait InteractsWithTime
+{
+    /**
+     * Get the number of seconds until the given DateTime.
+     *
+     * @param DateTimeInterface|DateInterval|int $delay
+     * @return int
+     */
+    protected function secondsUntil($delay)
+    {
+        $delay = $this->parseDateInterval($delay);
+
+        return $delay instanceof DateTimeInterface
+            ? max(0, $delay->getTimestamp() - $this->currentTime())
+            : (int) $delay;
+    }
+
+    /**
+     * Get the "available at" UNIX timestamp.
+     *
+     * @param DateTimeInterface|DateInterval|int $delay
+     * @return int
+     */
+    protected function availableAt($delay = 0)
+    {
+        $delay = $this->parseDateInterval($delay);
+
+        return $delay instanceof DateTimeInterface
+            ? $delay->getTimestamp()
+            : Carbon::now()->addRealSeconds($delay)->getTimestamp();
+    }
+
+    /**
+     * If the given value is an interval, convert it to a DateTime instance.
+     *
+     * @param DateTimeInterface|DateInterval|int $delay
+     * @return DateTimeInterface|int
+     */
+    protected function parseDateInterval($delay)
+    {
+        if ($delay instanceof DateInterval) {
+            $delay = Carbon::now()->add($delay);
+        }
+
+        return $delay;
+    }
+
+    /**
+     * Get the current system time as a UNIX timestamp.
+     *
+     * @return int
+     */
+    protected function currentTime()
+    {
+        return Carbon::now()->getTimestamp();
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/Job.php b/vendor/topthink/think-queue/src/queue/Job.php
new file mode 100755
index 0000000..27357f3
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/Job.php
@@ -0,0 +1,282 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\queue;
+
+use Exception;
+use think\App;
+
+abstract class Job
+{
+
+    /**
+     * The job handler instance.
+     * @var mixed
+     */
+    protected $instance;
+
+    /**
+     * @var App
+     */
+    protected $app;
+
+    /**
+     * The name of the queue the job belongs to.
+     * @var string
+     */
+    protected $queue;
+
+    /**
+     * The name of the connection the job belongs to.
+     */
+    protected $connection;
+
+    /**
+     * Indicates if the job has been deleted.
+     * @var bool
+     */
+    protected $deleted = false;
+
+    /**
+     * Indicates if the job has been released.
+     * @var bool
+     */
+    protected $released = false;
+
+    /**
+     * Indicates if the job has failed.
+     *
+     * @var bool
+     */
+    protected $failed = false;
+
+    /**
+     * Get the decoded body of the job.
+     *
+     * @return array
+     */
+    public function payload()
+    {
+        return json_decode($this->getRawBody(), true);
+    }
+
+    /**
+     * Fire the job.
+     * @return void
+     */
+    public function fire()
+    {
+        $payload = $this->payload();
+
+        list($class, $method) = $this->parseJob($payload['job']);
+
+        $this->instance = $this->resolve($class);
+        if ($this->instance) {
+            $this->instance->{$method}($this, $payload['data']);
+        }
+    }
+
+    /**
+     * Delete the job from the queue.
+     * @return void
+     */
+    public function delete()
+    {
+        $this->deleted = true;
+    }
+
+    /**
+     * Determine if the job has been deleted.
+     * @return bool
+     */
+    public function isDeleted()
+    {
+        return $this->deleted;
+    }
+
+    /**
+     * Release the job back into the queue.
+     * @param int $delay
+     * @return void
+     */
+    public function release($delay = 0)
+    {
+        $this->released = true;
+    }
+
+    /**
+     * Determine if the job was released back into the queue.
+     * @return bool
+     */
+    public function isReleased()
+    {
+        return $this->released;
+    }
+
+    /**
+     * Determine if the job has been deleted or released.
+     * @return bool
+     */
+    public function isDeletedOrReleased()
+    {
+        return $this->isDeleted() || $this->isReleased();
+    }
+
+    /**
+     * Get the job identifier.
+     *
+     * @return string
+     */
+    abstract public function getJobId();
+
+    /**
+     * Get the number of times the job has been attempted.
+     * @return int
+     */
+    abstract public function attempts();
+
+    /**
+     * Get the raw body string for the job.
+     * @return string
+     */
+    abstract public function getRawBody();
+
+    /**
+     * Parse the job declaration into class and method.
+     * @param string $job
+     * @return array
+     */
+    protected function parseJob($job)
+    {
+        $segments = explode('@', $job);
+
+        return count($segments) > 1 ? $segments : [$segments[0], 'fire'];
+    }
+
+    /**
+     * Resolve the given job handler.
+     * @param string $name
+     * @return mixed
+     */
+    protected function resolve($name)
+    {
+        if (strpos($name, '\\') === false) {
+
+            if (strpos($name, '/') === false) {
+                $app = '';
+            } else {
+                list($app, $name) = explode('/', $name, 2);
+            }
+
+            $name = ($this->app->config->get('app.app_namespace') ?: 'app\\') . ($app ? strtolower($app) . '\\' : '') . 'job\\' . $name;
+        }
+
+        return $this->app->make($name);
+    }
+
+    /**
+     * Determine if the job has been marked as a failure.
+     *
+     * @return bool
+     */
+    public function hasFailed()
+    {
+        return $this->failed;
+    }
+
+    /**
+     * Mark the job as "failed".
+     *
+     * @return void
+     */
+    public function markAsFailed()
+    {
+        $this->failed = true;
+    }
+
+    /**
+     * Process an exception that caused the job to fail.
+     *
+     * @param Exception $e
+     * @return void
+     */
+    public function failed($e)
+    {
+        $this->markAsFailed();
+
+        $payload = $this->payload();
+
+        list($class, $method) = $this->parseJob($payload['job']);
+
+        if (method_exists($this->instance = $this->resolve($class), 'failed')) {
+            $this->instance->failed($payload['data'], $e);
+        }
+    }
+
+    /**
+     * Get the number of times to attempt a job.
+     *
+     * @return int|null
+     */
+    public function maxTries()
+    {
+        return $this->payload()['maxTries'] ?? null;
+    }
+
+    /**
+     * Get the number of seconds the job can run.
+     *
+     * @return int|null
+     */
+    public function timeout()
+    {
+        return $this->payload()['timeout'] ?? null;
+    }
+
+    /**
+     * Get the timestamp indicating when the job should timeout.
+     *
+     * @return int|null
+     */
+    public function timeoutAt()
+    {
+        return $this->payload()['timeoutAt'] ?? null;
+    }
+
+    /**
+     * Get the name of the queued job class.
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->payload()['job'];
+    }
+
+    /**
+     * Get the name of the connection the job belongs to.
+     *
+     * @return string
+     */
+    public function getConnection()
+    {
+        return $this->connection;
+    }
+
+    /**
+     * Get the name of the queue the job belongs to.
+     * @return string
+     */
+    public function getQueue()
+    {
+        return $this->queue;
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/Listener.php b/vendor/topthink/think-queue/src/queue/Listener.php
new file mode 100755
index 0000000..632510d
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/Listener.php
@@ -0,0 +1,162 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\queue;
+
+use Closure;
+use Symfony\Component\Process\PhpExecutableFinder;
+use Symfony\Component\Process\Process;
+use think\App;
+
+class Listener
+{
+
+    /**
+     * @var string
+     */
+    protected $commandPath;
+
+    /**
+     * @var string
+     */
+    protected $workerCommand;
+
+    /**
+     * @var \Closure|null
+     */
+    protected $outputHandler;
+
+    /**
+     * @param string $commandPath
+     */
+    public function __construct($commandPath)
+    {
+        $this->commandPath = $commandPath;
+    }
+
+    public static function __make(App $app)
+    {
+        return new self($app->getRootPath());
+    }
+
+    /**
+     * Get the PHP binary.
+     *
+     * @return string
+     */
+    protected function phpBinary()
+    {
+        return (new PhpExecutableFinder)->find(false);
+    }
+
+    /**
+     * @param string $connection
+     * @param string $queue
+     * @param int    $delay
+     * @param int    $sleep
+     * @param int    $maxTries
+     * @param int    $memory
+     * @param int    $timeout
+     * @return void
+     */
+    public function listen($connection, $queue, $delay = 0, $sleep = 3, $maxTries = 0, $memory = 128, $timeout = 60)
+    {
+        $process = $this->makeProcess($connection, $queue, $delay, $sleep, $maxTries, $memory, $timeout);
+
+        while (true) {
+            $this->runProcess($process, $memory);
+        }
+    }
+
+    /**
+     * @param string $connection
+     * @param string $queue
+     * @param int    $delay
+     * @param int    $sleep
+     * @param int    $maxTries
+     * @param int    $memory
+     * @param int    $timeout
+     * @return Process
+     */
+    public function makeProcess($connection, $queue, $delay, $sleep, $maxTries, $memory, $timeout)
+    {
+        $command = array_filter([
+            $this->phpBinary(),
+            'think',
+            'queue:work',
+            $connection,
+            '--once',
+            "--queue={$queue}",
+            "--delay={$delay}",
+            "--memory={$memory}",
+            "--sleep={$sleep}",
+            "--tries={$maxTries}",
+        ], function ($value) {
+            return !is_null($value);
+        });
+
+        return new Process($command, $this->commandPath, null, null, $timeout);
+    }
+
+    /**
+     * @param Process $process
+     * @param int     $memory
+     */
+    public function runProcess(Process $process, $memory)
+    {
+        $process->run(function ($type, $line) {
+            $this->handleWorkerOutput($type, $line);
+        });
+
+        if ($this->memoryExceeded($memory)) {
+            $this->stop();
+        }
+    }
+
+    /**
+     * @param int    $type
+     * @param string $line
+     * @return void
+     */
+    protected function handleWorkerOutput($type, $line)
+    {
+        if (isset($this->outputHandler)) {
+            call_user_func($this->outputHandler, $type, $line);
+        }
+    }
+
+    /**
+     * @param int $memoryLimit
+     * @return bool
+     */
+    public function memoryExceeded($memoryLimit)
+    {
+        return (memory_get_usage() / 1024 / 1024) >= $memoryLimit;
+    }
+
+    /**
+     * @return void
+     */
+    public function stop()
+    {
+        die;
+    }
+
+    /**
+     * @param \Closure $outputHandler
+     * @return void
+     */
+    public function setOutputHandler(Closure $outputHandler)
+    {
+        $this->outputHandler = $outputHandler;
+    }
+
+}
diff --git a/vendor/topthink/think-queue/src/queue/Queueable.php b/vendor/topthink/think-queue/src/queue/Queueable.php
new file mode 100755
index 0000000..2a6ec4e
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/Queueable.php
@@ -0,0 +1,46 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\queue;
+
+trait Queueable
+{
+
+    /** @var string 队列名称 */
+    public $queue;
+
+    /** @var integer 延迟时间 */
+    public $delay;
+
+    /**
+     * 设置队列名
+     * @param $queue
+     * @return $this
+     */
+    public function queue($queue)
+    {
+        $this->queue = $queue;
+
+        return $this;
+    }
+
+    /**
+     * 设置延迟时间
+     * @param $delay
+     * @return $this
+     */
+    public function delay($delay)
+    {
+        $this->delay = $delay;
+
+        return $this;
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/Service.php b/vendor/topthink/think-queue/src/queue/Service.php
new file mode 100755
index 0000000..a6b3f4c
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/Service.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace think\queue;
+
+use think\helper\Arr;
+use think\helper\Str;
+use think\Queue;
+use think\queue\command\FailedTable;
+use think\queue\command\FlushFailed;
+use think\queue\command\ForgetFailed;
+use think\queue\command\Listen;
+use think\queue\command\ListFailed;
+use think\queue\command\Restart;
+use think\queue\command\Retry;
+use think\queue\command\Table;
+use think\queue\command\Work;
+
+class Service extends \think\Service
+{
+    public function register()
+    {
+        $this->app->bind('queue', Queue::class);
+        $this->app->bind('queue.failer', function () {
+
+            $config = $this->app->config->get('queue.failed', []);
+
+            $type = Arr::pull($config, 'type', 'none');
+
+            $class = false !== strpos($type, '\\') ? $type : '\\think\\queue\\failed\\' . Str::studly($type);
+
+            return $this->app->invokeClass($class, [$config]);
+        });
+    }
+
+    public function boot()
+    {
+        $this->commands([
+            FailedJob::class,
+            Table::class,
+            FlushFailed::class,
+            ForgetFailed::class,
+            ListFailed::class,
+            Retry::class,
+            Work::class,
+            Restart::class,
+            Listen::class,
+            FailedTable::class,
+        ]);
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/ShouldQueue.php b/vendor/topthink/think-queue/src/queue/ShouldQueue.php
new file mode 100755
index 0000000..cb49c12
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/ShouldQueue.php
@@ -0,0 +1,17 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\queue;
+
+interface ShouldQueue
+{
+
+}
diff --git a/vendor/topthink/think-queue/src/queue/Worker.php b/vendor/topthink/think-queue/src/queue/Worker.php
new file mode 100755
index 0000000..f98079f
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/Worker.php
@@ -0,0 +1,414 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\queue;
+
+use Carbon\Carbon;
+use Exception;
+use RuntimeException;
+use think\Cache;
+use think\Event;
+use think\exception\Handle;
+use think\Queue;
+use think\queue\event\JobExceptionOccurred;
+use think\queue\event\JobFailed;
+use think\queue\event\JobProcessed;
+use think\queue\event\JobProcessing;
+use think\queue\event\WorkerStopping;
+use think\queue\exception\MaxAttemptsExceededException;
+use Throwable;
+
+class Worker
+{
+    /** @var Event */
+    protected $event;
+    /** @var Handle */
+    protected $handle;
+    /** @var Queue */
+    protected $queue;
+
+    /** @var Cache */
+    protected $cache;
+
+    /**
+     * Indicates if the worker should exit.
+     *
+     * @var bool
+     */
+    public $shouldQuit = false;
+
+    /**
+     * Indicates if the worker is paused.
+     *
+     * @var bool
+     */
+    public $paused = false;
+
+    public function __construct(Queue $queue, Event $event, Handle $handle, Cache $cache = null)
+    {
+        $this->queue  = $queue;
+        $this->event  = $event;
+        $this->handle = $handle;
+        $this->cache  = $cache;
+    }
+
+    /**
+     * @param string $connection
+     * @param string $queue
+     * @param int    $delay
+     * @param int    $sleep
+     * @param int    $maxTries
+     * @param int    $memory
+     * @param int    $timeout
+     */
+    public function daemon($connection, $queue, $delay = 0, $sleep = 3, $maxTries = 0, $memory = 128, $timeout = 60)
+    {
+        if ($this->supportsAsyncSignals()) {
+            $this->listenForSignals();
+        }
+
+        $lastRestart = $this->getTimestampOfLastQueueRestart();
+
+        while (true) {
+
+            $job = $this->getNextJob(
+                $this->queue->connection($connection), $queue
+            );
+
+            if ($this->supportsAsyncSignals()) {
+                $this->registerTimeoutHandler($job, $timeout);
+            }
+
+            if ($job) {
+                $this->runJob($job, $connection, $maxTries, $delay);
+            } else {
+                $this->sleep($sleep);
+            }
+
+            $this->stopIfNecessary($job, $lastRestart, $memory);
+        }
+    }
+
+    protected function stopIfNecessary($job, $lastRestart, $memory)
+    {
+        if ($this->shouldQuit || $this->queueShouldRestart($lastRestart)) {
+            $this->stop();
+        } elseif ($this->memoryExceeded($memory)) {
+            $this->stop(12);
+        }
+    }
+
+    /**
+     * Determine if the queue worker should restart.
+     *
+     * @param int|null $lastRestart
+     * @return bool
+     */
+    protected function queueShouldRestart($lastRestart)
+    {
+        return $this->getTimestampOfLastQueueRestart() != $lastRestart;
+    }
+
+    /**
+     * Determine if the memory limit has been exceeded.
+     *
+     * @param int $memoryLimit
+     * @return bool
+     */
+    public function memoryExceeded($memoryLimit)
+    {
+        return (memory_get_usage(true) / 1024 / 1024) >= $memoryLimit;
+    }
+
+    /**
+     * 获取队列重启时间
+     * @return mixed
+     */
+    protected function getTimestampOfLastQueueRestart()
+    {
+        if ($this->cache) {
+            return $this->cache->get('think:queue:restart');
+        }
+    }
+
+    /**
+     * Register the worker timeout handler.
+     *
+     * @param Job|null $job
+     * @param int      $timeout
+     * @return void
+     */
+    protected function registerTimeoutHandler($job, $timeout)
+    {
+        pcntl_signal(SIGALRM, function () {
+            $this->kill(1);
+        });
+
+        pcntl_alarm(
+            max($this->timeoutForJob($job, $timeout), 0)
+        );
+    }
+
+    /**
+     * Stop listening and bail out of the script.
+     *
+     * @param int $status
+     * @return void
+     */
+    public function stop($status = 0)
+    {
+        $this->event->trigger(new WorkerStopping($status));
+
+        exit($status);
+    }
+
+    /**
+     * Kill the process.
+     *
+     * @param int $status
+     * @return void
+     */
+    public function kill($status = 0)
+    {
+        $this->event->trigger(new WorkerStopping($status));
+
+        if (extension_loaded('posix')) {
+            posix_kill(getmypid(), SIGKILL);
+        }
+
+        exit($status);
+    }
+
+    /**
+     * Get the appropriate timeout for the given job.
+     *
+     * @param Job|null $job
+     * @param int      $timeout
+     * @return int
+     */
+    protected function timeoutForJob($job, $timeout)
+    {
+        return $job && !is_null($job->timeout()) ? $job->timeout() : $timeout;
+    }
+
+    /**
+     * Determine if "async" signals are supported.
+     *
+     * @return bool
+     */
+    protected function supportsAsyncSignals()
+    {
+        return extension_loaded('pcntl');
+    }
+
+    /**
+     * Enable async signals for the process.
+     *
+     * @return void
+     */
+    protected function listenForSignals()
+    {
+        pcntl_async_signals(true);
+
+        pcntl_signal(SIGTERM, function () {
+            $this->shouldQuit = true;
+        });
+
+        pcntl_signal(SIGUSR2, function () {
+            $this->paused = true;
+        });
+
+        pcntl_signal(SIGCONT, function () {
+            $this->paused = false;
+        });
+    }
+
+    /**
+     * 执行下个任务
+     * @param string $connection
+     * @param string $queue
+     * @param int    $delay
+     * @param int    $sleep
+     * @param int    $maxTries
+     * @return void
+     * @throws Exception
+     */
+    public function runNextJob($connection, $queue, $delay = 0, $sleep = 3, $maxTries = 0)
+    {
+
+        $job = $this->getNextJob($this->queue->connection($connection), $queue);
+
+        if ($job) {
+            $this->runJob($job, $connection, $maxTries, $delay);
+        } else {
+            $this->sleep($sleep);
+        }
+    }
+
+    /**
+     * 执行任务
+     * @param Job    $job
+     * @param string $connection
+     * @param int    $maxTries
+     * @param int    $delay
+     * @return void
+     */
+    protected function runJob($job, $connection, $maxTries, $delay)
+    {
+        try {
+            $this->process($connection, $job, $maxTries, $delay);
+        } catch (Exception | Throwable $e) {
+            $this->handle->report($e);
+        }
+    }
+
+    /**
+     * 获取下个任务
+     * @param Connector $connector
+     * @param string    $queue
+     * @return Job
+     */
+    protected function getNextJob($connector, $queue)
+    {
+        try {
+            foreach (explode(',', $queue) as $queue) {
+                if (!is_null($job = $connector->pop($queue))) {
+                    return $job;
+                }
+            }
+        } catch (Exception | Throwable $e) {
+            $this->handle->report($e);
+            $this->sleep(1);
+        }
+    }
+
+    /**
+     * Process a given job from the queue.
+     * @param string $connection
+     * @param Job    $job
+     * @param int    $maxTries
+     * @param int    $delay
+     * @return void
+     * @throws Exception
+     */
+    public function process($connection, $job, $maxTries = 0, $delay = 0)
+    {
+        try {
+            $this->event->trigger(new JobProcessing($connection, $job));
+
+            $this->markJobAsFailedIfAlreadyExceedsMaxAttempts(
+                $connection, $job, (int) $maxTries
+            );
+
+            $job->fire();
+
+            $this->event->trigger(new JobProcessed($connection, $job));
+        } catch (Exception | Throwable $e) {
+            try {
+                if (!$job->hasFailed()) {
+                    $this->markJobAsFailedIfWillExceedMaxAttempts($connection, $job, (int) $maxTries, $e);
+                }
+
+                $this->event->trigger(new JobExceptionOccurred($connection, $job, $e));
+            } finally {
+                if (!$job->isDeleted() && !$job->isReleased() && !$job->hasFailed()) {
+                    $job->release($delay);
+                }
+            }
+
+            throw $e;
+        }
+    }
+
+    /**
+     * @param string $connection
+     * @param Job    $job
+     * @param int    $maxTries
+     */
+    protected function markJobAsFailedIfAlreadyExceedsMaxAttempts($connection, $job, $maxTries)
+    {
+        $maxTries = !is_null($job->maxTries()) ? $job->maxTries() : $maxTries;
+
+        $timeoutAt = $job->timeoutAt();
+
+        if ($timeoutAt && Carbon::now()->getTimestamp() <= $timeoutAt) {
+            return;
+        }
+
+        if (!$timeoutAt && (0 === $maxTries || $job->attempts() <= $maxTries)) {
+            return;
+        }
+
+        $this->failJob($connection, $job, $e = new MaxAttemptsExceededException(
+            $job->getName() . ' has been attempted too many times or run too long. The job may have previously timed out.'
+        ));
+
+        throw $e;
+    }
+
+    /**
+     * @param string    $connection
+     * @param Job       $job
+     * @param int       $maxTries
+     * @param Exception $e
+     */
+    protected function markJobAsFailedIfWillExceedMaxAttempts($connection, $job, $maxTries, $e)
+    {
+        $maxTries = !is_null($job->maxTries()) ? $job->maxTries() : $maxTries;
+
+        if ($job->timeoutAt() && $job->timeoutAt() <= Carbon::now()->getTimestamp()) {
+            $this->failJob($connection, $job, $e);
+        }
+
+        if ($maxTries > 0 && $job->attempts() >= $maxTries) {
+            $this->failJob($connection, $job, $e);
+        }
+    }
+
+    /**
+     * @param string    $connection
+     * @param Job       $job
+     * @param Exception $e
+     */
+    protected function failJob($connection, $job, $e)
+    {
+        $job->markAsFailed();
+
+        if ($job->isDeleted()) {
+            return;
+        }
+
+        try {
+            $job->delete();
+
+            $job->failed($e);
+        } finally {
+            $this->event->trigger(new JobFailed(
+                $connection, $job, $e ?: new RuntimeException('ManuallyFailed')
+            ));
+        }
+    }
+
+    /**
+     * Sleep the script for a given number of seconds.
+     * @param int $seconds
+     * @return void
+     */
+    public function sleep($seconds)
+    {
+        if ($seconds < 1) {
+            usleep($seconds * 1000000);
+        } else {
+            sleep($seconds);
+        }
+    }
+
+}
diff --git a/vendor/topthink/think-queue/src/queue/command/FailedTable.php b/vendor/topthink/think-queue/src/queue/command/FailedTable.php
new file mode 100755
index 0000000..620af93
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/command/FailedTable.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace think\queue\command;
+
+use think\console\Command;
+use think\helper\Str;
+use think\migration\Creator;
+
+class FailedTable extends Command
+{
+    protected function configure()
+    {
+        $this->setName('queue:failed-table')
+            ->setDescription('Create a migration for the failed queue jobs database table');
+    }
+
+    public function handle()
+    {
+        if (!$this->app->has('migration.creator')) {
+            $this->output->error('Install think-migration first please');
+            return;
+        }
+
+        $table = $this->app->config->get('queue.failed.table');
+
+        $className = Str::studly("create_{$table}_table");
+
+        /** @var Creator $creator */
+        $creator = $this->app->get('migration.creator');
+
+        $path = $creator->create($className);
+
+        // Load the alternative template if it is defined.
+        $contents = file_get_contents(__DIR__ . '/stubs/failed_jobs.stub');
+
+        // inject the class names appropriate to this migration
+        $contents = strtr($contents, [
+            'CreateFailedJobsTable' => $className,
+            '{{table}}'             => $table,
+        ]);
+
+        file_put_contents($path, $contents);
+
+        $this->output->info('Migration created successfully!');
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/command/FlushFailed.php b/vendor/topthink/think-queue/src/queue/command/FlushFailed.php
new file mode 100755
index 0000000..69ea8b4
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/command/FlushFailed.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace think\queue\command;
+
+use think\console\Command;
+
+class FlushFailed extends Command
+{
+    protected function configure()
+    {
+        $this->setName('queue:flush')
+            ->setDescription('Flush all of the failed queue jobs');
+    }
+
+    public function handle()
+    {
+        $this->app->get('queue.failer')->flush();
+
+        $this->output->info('All failed jobs deleted successfully!');
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/command/ForgetFailed.php b/vendor/topthink/think-queue/src/queue/command/ForgetFailed.php
new file mode 100755
index 0000000..37d175d
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/command/ForgetFailed.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace think\queue\command;
+
+use think\console\Command;
+use think\console\input\Argument;
+
+class ForgetFailed extends Command
+{
+    protected function configure()
+    {
+        $this->setName('queue:forget')
+            ->addArgument('id', Argument::REQUIRED, 'The ID of the failed job')
+            ->setDescription('Delete a failed queue job');
+    }
+
+    public function handle()
+    {
+        if ($this->app['queue.failer']->forget($this->input->getArgument('id'))) {
+            $this->output->info('Failed job deleted successfully!');
+        } else {
+            $this->output->error('No failed job matches the given ID.');
+        }
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/command/ListFailed.php b/vendor/topthink/think-queue/src/queue/command/ListFailed.php
new file mode 100755
index 0000000..0604d72
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/command/ListFailed.php
@@ -0,0 +1,110 @@
+<?php
+
+namespace think\queue\command;
+
+use think\console\Command;
+use think\console\Table;
+use think\helper\Arr;
+
+class ListFailed extends Command
+{
+    /**
+     * The table headers for the command.
+     *
+     * @var array
+     */
+    protected $headers = ['ID', 'Connection', 'Queue', 'Class', 'Failed At'];
+
+    protected function configure()
+    {
+        $this->setName('queue:failed')
+            ->setDescription('List all of the failed queue jobs');
+    }
+
+    public function handle()
+    {
+        if (count($jobs = $this->getFailedJobs()) === 0) {
+            $this->output->info('No failed jobs!');
+            return;
+        }
+        $this->displayFailedJobs($jobs);
+    }
+
+    /**
+     * Display the failed jobs in the console.
+     *
+     * @param array $jobs
+     * @return void
+     */
+    protected function displayFailedJobs(array $jobs)
+    {
+        $table = new Table();
+        $table->setHeader($this->headers);
+        $table->setRows($jobs);
+
+        $this->table($table);
+    }
+
+    /**
+     * Compile the failed jobs into a displayable format.
+     *
+     * @return array
+     */
+    protected function getFailedJobs()
+    {
+        $failed = $this->app['queue.failer']->all();
+
+        return collect($failed)->map(function ($failed) {
+            return $this->parseFailedJob((array) $failed);
+        })->filter()->all();
+    }
+
+    /**
+     * Parse the failed job row.
+     *
+     * @param array $failed
+     * @return array
+     */
+    protected function parseFailedJob(array $failed)
+    {
+        $row = array_values(Arr::except($failed, ['payload', 'exception']));
+
+        array_splice($row, 3, 0, $this->extractJobName($failed['payload']));
+
+        return $row;
+    }
+
+    /**
+     * Extract the failed job name from payload.
+     *
+     * @param string $payload
+     * @return string|null
+     */
+    private function extractJobName($payload)
+    {
+        $payload = json_decode($payload, true);
+
+        if ($payload && (!isset($payload['data']['command']))) {
+            return $payload['job'] ?? null;
+        } elseif ($payload && isset($payload['data']['command'])) {
+            return $this->matchJobName($payload);
+        }
+    }
+
+    /**
+     * Match the job name from the payload.
+     *
+     * @param array $payload
+     * @return string
+     */
+    protected function matchJobName($payload)
+    {
+        preg_match('/"([^"]+)"/', $payload['data']['command'], $matches);
+
+        if (isset($matches[1])) {
+            return $matches[1];
+        }
+
+        return $payload['job'] ?? null;
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/command/Listen.php b/vendor/topthink/think-queue/src/queue/command/Listen.php
new file mode 100755
index 0000000..8f8e19d
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/command/Listen.php
@@ -0,0 +1,63 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\queue\command;
+
+use think\console\Command;
+use think\console\Input;
+use think\console\input\Argument;
+use think\console\input\Option;
+use think\console\Output;
+use think\queue\Listener;
+
+class Listen extends Command
+{
+    /** @var  Listener */
+    protected $listener;
+
+    public function __construct(Listener $listener)
+    {
+        parent::__construct();
+        $this->listener = $listener;
+        $this->listener->setOutputHandler(function ($type, $line) {
+            $this->output->write($line);
+        });
+    }
+
+    protected function configure()
+    {
+        $this->setName('queue:listen')
+            ->addArgument('connection', Argument::OPTIONAL, 'The name of the queue connection to work', null)
+            ->addOption('queue', null, Option::VALUE_OPTIONAL, 'The queue to listen on', null)
+            ->addOption('delay', null, Option::VALUE_OPTIONAL, 'Amount of time to delay failed jobs', 0)
+            ->addOption('memory', null, Option::VALUE_OPTIONAL, 'The memory limit in megabytes', 128)
+            ->addOption('timeout', null, Option::VALUE_OPTIONAL, 'Seconds a job may run before timing out', 60)
+            ->addOption('sleep', null, Option::VALUE_OPTIONAL, 'Seconds to wait before checking queue for jobs', 3)
+            ->addOption('tries', null, Option::VALUE_OPTIONAL, 'Number of times to attempt a job before logging it failed', 0)
+            ->setDescription('Listen to a given queue');
+    }
+
+    public function execute(Input $input, Output $output)
+    {
+        $connection = $input->getArgument('connection') ?: $this->app->config->get('queue.default');
+
+        $queue   = $input->getOption('queue') ?: $this->app->config->get("queue.connections.{$connection}.queue", 'default');
+        $delay   = $input->getOption('delay');
+        $memory  = $input->getOption('memory');
+        $timeout = $input->getOption('timeout');
+        $sleep   = $input->getOption('sleep');
+        $tries   = $input->getOption('tries');
+        
+        $this->app->event->trigger('QueueStartListener', [$output]);
+
+        $this->listener->listen($connection, $queue, $delay, $sleep, $tries, $memory, $timeout);
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/command/Restart.php b/vendor/topthink/think-queue/src/queue/command/Restart.php
new file mode 100755
index 0000000..f0d3b4d
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/command/Restart.php
@@ -0,0 +1,33 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\queue\command;
+
+use think\Cache;
+use think\console\Command;
+use think\queue\InteractsWithTime;
+
+class Restart extends Command
+{
+    use InteractsWithTime;
+
+    protected function configure()
+    {
+        $this->setName('queue:restart')
+            ->setDescription('Restart queue worker daemons after their current job');
+    }
+
+    public function handle(Cache $cache)
+    {
+        $cache->set('think:queue:restart', $this->currentTime());
+        $this->output->info("Broadcasting queue restart signal.");
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/command/Retry.php b/vendor/topthink/think-queue/src/queue/command/Retry.php
new file mode 100755
index 0000000..7551a37
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/command/Retry.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace think\queue\command;
+
+use stdClass;
+use think\console\Command;
+use think\console\input\Argument;
+use think\helper\Arr;
+
+class Retry extends Command
+{
+    protected function configure()
+    {
+        $this->setName('queue:retry')
+            ->addArgument('id', Argument::IS_ARRAY | Argument::REQUIRED, 'The ID of the failed job or "all" to retry all jobs')
+            ->setDescription('Retry a failed queue job');
+    }
+
+    public function handle()
+    {
+        foreach ($this->getJobIds() as $id) {
+            $job = $this->app['queue.failer']->find($id);
+
+            if (is_null($job)) {
+                $this->output->error("Unable to find failed job with ID [{$id}].");
+            } else {
+                $this->retryJob($job);
+
+                $this->output->info("The failed job [{$id}] has been pushed back onto the queue!");
+
+                $this->app['queue.failer']->forget($id);
+            }
+        }
+    }
+
+    /**
+     * Retry the queue job.
+     *
+     * @param stdClass $job
+     * @return void
+     */
+    protected function retryJob($job)
+    {
+        $this->app['queue']->connection($job['connection'])->pushRaw(
+            $this->resetAttempts($job['payload']), $job['queue']
+        );
+    }
+
+    /**
+     * Reset the payload attempts.
+     *
+     * Applicable to Redis jobs which store attempts in their payload.
+     *
+     * @param string $payload
+     * @return string
+     */
+    protected function resetAttempts($payload)
+    {
+        $payload = json_decode($payload, true);
+
+        if (isset($payload['attempts'])) {
+            $payload['attempts'] = 0;
+        }
+
+        return json_encode($payload);
+    }
+
+    /**
+     * Get the job IDs to be retried.
+     *
+     * @return array
+     */
+    protected function getJobIds()
+    {
+        $ids = (array) $this->input->getArgument('id');
+
+        if (count($ids) === 1 && $ids[0] === 'all') {
+            $ids = Arr::pluck($this->app['queue.failer']->all(), 'id');
+        }
+
+        return $ids;
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/command/Table.php b/vendor/topthink/think-queue/src/queue/command/Table.php
new file mode 100755
index 0000000..9354dc7
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/command/Table.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace think\queue\command;
+
+use think\console\Command;
+use think\helper\Str;
+use think\migration\Creator;
+
+class Table extends Command
+{
+    protected function configure()
+    {
+        $this->setName('queue:table')
+            ->setDescription('Create a migration for the queue jobs database table');
+    }
+
+    public function handle()
+    {
+        if (!$this->app->has('migration.creator')) {
+            $this->output->error('Install think-migration first please');
+            return;
+        }
+
+        $table = $this->app->config->get('queue.connections.database.table');
+
+        $className = Str::studly("create_{$table}_table");
+
+        /** @var Creator $creator */
+        $creator = $this->app->get('migration.creator');
+
+        $path = $creator->create($className);
+
+        // Load the alternative template if it is defined.
+        $contents = file_get_contents(__DIR__ . '/stubs/jobs.stub');
+
+        // inject the class names appropriate to this migration
+        $contents = strtr($contents, [
+            'CreateJobsTable' => $className,
+            '{{table}}'       => $table,
+        ]);
+
+        file_put_contents($path, $contents);
+
+        $this->output->info('Migration created successfully!');
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/command/Work.php b/vendor/topthink/think-queue/src/queue/command/Work.php
new file mode 100755
index 0000000..5ead2d4
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/command/Work.php
@@ -0,0 +1,151 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+namespace think\queue\command;
+
+use think\console\Command;
+use think\console\Input;
+use think\console\input\Argument;
+use think\console\input\Option;
+use think\console\Output;
+use think\queue\event\JobFailed;
+use think\queue\event\JobProcessed;
+use think\queue\event\JobProcessing;
+use think\queue\Job;
+use think\queue\Worker;
+
+class Work extends Command
+{
+
+    /**
+     * The queue worker instance.
+     * @var Worker
+     */
+    protected $worker;
+
+    public function __construct(Worker $worker)
+    {
+        parent::__construct();
+        $this->worker = $worker;
+    }
+
+    protected function configure()
+    {
+        $this->setName('queue:work')
+            ->addArgument('connection', Argument::OPTIONAL, 'The name of the queue connection to work', null)
+            ->addOption('queue', null, Option::VALUE_OPTIONAL, 'The queue to listen on')
+            ->addOption('once', null, Option::VALUE_NONE, 'Only process the next job on the queue')
+            ->addOption('delay', null, Option::VALUE_OPTIONAL, 'Amount of time to delay failed jobs', 0)
+            ->addOption('force', null, Option::VALUE_NONE, 'Force the worker to run even in maintenance mode')
+            ->addOption('memory', null, Option::VALUE_OPTIONAL, 'The memory limit in megabytes', 128)
+            ->addOption('timeout', null, Option::VALUE_OPTIONAL, 'The number of seconds a child process can run', 60)
+            ->addOption('sleep', null, Option::VALUE_OPTIONAL, 'Number of seconds to sleep when no job is available', 3)
+            ->addOption('tries', null, Option::VALUE_OPTIONAL, 'Number of times to attempt a job before logging it failed', 0)
+            ->setDescription('Process the next job on a queue');
+    }
+
+    /**
+     * Execute the console command.
+     * @param Input  $input
+     * @param Output $output
+     * @return int|null|void
+     */
+    public function execute(Input $input, Output $output)
+    {
+        $connection = $input->getArgument('connection') ?: $this->app->config->get('queue.default');
+
+        $queue = $input->getOption('queue') ?: $this->app->config->get("queue.connections.{$connection}.queue", 'default');
+        $delay = $input->getOption('delay');
+        $sleep = $input->getOption('sleep');
+        $tries = $input->getOption('tries');
+
+        $this->listenForEvents();
+
+        if ($input->getOption('once')) {
+            $this->worker->runNextJob($connection, $queue, $delay, $sleep, $tries);
+        } else {
+            $memory  = $input->getOption('memory');
+            $timeout = $input->getOption('timeout');
+            $this->worker->daemon($connection, $queue, $delay, $sleep, $tries, $memory, $timeout);
+        }
+    }
+
+    /**
+     * 注册事件
+     */
+    protected function listenForEvents()
+    {
+        $this->app->event->listen(JobProcessing::class, function (JobProcessing $event) {
+            $this->writeOutput($event->job, 'starting');
+        });
+
+        $this->app->event->listen(JobProcessed::class, function (JobProcessed $event) {
+            $this->writeOutput($event->job, 'success');
+        });
+
+        $this->app->event->listen(JobFailed::class, function (JobFailed $event) {
+            $this->writeOutput($event->job, 'failed');
+
+            $this->logFailedJob($event);
+        });
+    }
+
+    /**
+     * Write the status output for the queue worker.
+     *
+     * @param Job $job
+     * @param     $status
+     */
+    protected function writeOutput(Job $job, $status)
+    {
+        switch ($status) {
+            case 'starting':
+                $this->writeStatus($job, 'Processing', 'comment');
+                break;
+            case 'success':
+                $this->writeStatus($job, 'Processed', 'info');
+                break;
+            case 'failed':
+                $this->writeStatus($job, 'Failed', 'error');
+                break;
+        }
+    }
+
+    /**
+     * Format the status output for the queue worker.
+     *
+     * @param Job    $job
+     * @param string $status
+     * @param string $type
+     * @return void
+     */
+    protected function writeStatus(Job $job, $status, $type)
+    {
+        $this->output->writeln(sprintf(
+            "<{$type}>[%s][%s] %s</{$type}> %s",
+            date('Y-m-d H:i:s'),
+            $job->getJobId(),
+            str_pad("{$status}:", 11), $job->getName()
+        ));
+    }
+
+    /**
+     * 记录失败任务
+     * @param JobFailed $event
+     */
+    protected function logFailedJob(JobFailed $event)
+    {
+        $this->app['queue.failer']->log(
+            $event->connection, $event->job->getQueue(),
+            $event->job->getRawBody(), $event->exception
+        );
+    }
+
+}
diff --git a/vendor/topthink/think-queue/src/queue/command/stubs/failed_jobs.stub b/vendor/topthink/think-queue/src/queue/command/stubs/failed_jobs.stub
new file mode 100755
index 0000000..d44ebcd
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/command/stubs/failed_jobs.stub
@@ -0,0 +1,18 @@
+<?php
+
+use think\migration\db\Column;
+use think\migration\Migrator;
+
+class CreateFailedJobsTable extends Migrator
+{
+    public function change()
+    {
+        $this->table('{{table}}')
+            ->addColumn(Column::text('connection'))
+            ->addColumn(Column::text('queue'))
+            ->addColumn(Column::longText('payload'))
+            ->addColumn(Column::longText('exception'))
+            ->addColumn(Column::timestamp('failed_at')->setDefault('CURRENT_TIMESTAMP'))
+            ->create();
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/command/stubs/jobs.stub b/vendor/topthink/think-queue/src/queue/command/stubs/jobs.stub
new file mode 100755
index 0000000..937d549
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/command/stubs/jobs.stub
@@ -0,0 +1,20 @@
+<?php
+
+use think\migration\db\Column;
+use think\migration\Migrator;
+
+class CreateJobsTable extends Migrator
+{
+    public function change()
+    {
+        $this->table('{{table}}')
+            ->addColumn(Column::string('queue'))
+            ->addColumn(Column::longText('payload'))
+            ->addColumn(Column::tinyInteger('attempts')->setUnsigned())
+            ->addColumn(Column::unsignedInteger('reserved_at')->setNullable())
+            ->addColumn(Column::unsignedInteger('available_at'))
+            ->addColumn(Column::unsignedInteger('created_at'))
+            ->addIndex('queue')
+            ->create();
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/connector/Database.php b/vendor/topthink/think-queue/src/queue/connector/Database.php
new file mode 100755
index 0000000..ba4f1da
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/connector/Database.php
@@ -0,0 +1,220 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\queue\connector;
+
+use Carbon\Carbon;
+use stdClass;
+use think\Db;
+use think\db\Query;
+use think\queue\Connector;
+use think\queue\InteractsWithTime;
+use think\queue\job\Database as DatabaseJob;
+
+class Database extends Connector
+{
+
+    use InteractsWithTime;
+
+    protected $db;
+
+    /**
+     * The database table that holds the jobs.
+     *
+     * @var string
+     */
+    protected $table;
+
+    /**
+     * The name of the default queue.
+     *
+     * @var string
+     */
+    protected $default;
+
+    /**
+     * The expiration time of a job.
+     *
+     * @var int|null
+     */
+    protected $retryAfter = 60;
+
+    public function __construct(Db $db, $table, $default = 'default', $retryAfter = 60)
+    {
+        $this->db         = $db;
+        $this->table      = $table;
+        $this->default    = $default;
+        $this->retryAfter = $retryAfter;
+    }
+
+    public static function __make(Db $db, $config)
+    {
+        return new self($db, $config['table'], $config['queue'], $config['retry_after'] ?? 60);
+    }
+
+    public function size($queue = null)
+    {
+        $this->db->name($this->table)
+            ->where('queue', $this->getQueue($queue))
+            ->count();
+    }
+
+    public function push($job, $data = '', $queue = null)
+    {
+        return $this->pushToDatabase($queue, $this->createPayload($job, $data));
+    }
+
+    public function pushRaw($payload, $queue = null, array $options = [])
+    {
+        return $this->pushToDatabase($queue, $payload);
+    }
+
+    public function later($delay, $job, $data = '', $queue = null)
+    {
+        return $this->pushToDatabase($queue, $this->createPayload($job, $data), $delay);
+    }
+
+    public function bulk($jobs, $data = '', $queue = null)
+    {
+        $queue = $this->getQueue($queue);
+
+        $availableAt = $this->availableAt();
+
+        return $this->db->name($this->table)->insertAll(collect((array) $jobs)->map(
+            function ($job) use ($queue, $data, $availableAt) {
+                return [
+                    'queue'        => $queue,
+                    'attempts'     => 0,
+                    'reserved_at'  => null,
+                    'available_at' => $availableAt,
+                    'created_at'   => $this->currentTime(),
+                    'payload'      => $this->createPayload($job, $data),
+                ];
+            }
+        )->all());
+    }
+
+    /**
+     * 重新发布任务
+     *
+     * @param string   $queue
+     * @param StdClass $job
+     * @param int      $delay
+     * @return mixed
+     */
+    public function release($queue, $job, $delay)
+    {
+        return $this->pushToDatabase($queue, $job->payload, $delay, $job->attempts);
+    }
+
+    /**
+     * Push a raw payload to the database with a given delay.
+     *
+     * @param \DateTime|int $delay
+     * @param string|null   $queue
+     * @param string        $payload
+     * @param int           $attempts
+     * @return mixed
+     */
+    protected function pushToDatabase($queue, $payload, $delay = 0, $attempts = 0)
+    {
+        return $this->db->name($this->table)->insertGetId([
+            'queue'        => $this->getQueue($queue),
+            'attempts'     => $attempts,
+            'reserved_at'  => null,
+            'available_at' => $this->availableAt($delay),
+            'created_at'   => $this->currentTime(),
+            'payload'      => $payload,
+        ]);
+    }
+
+    public function pop($queue = null)
+    {
+        $queue = $this->getQueue($queue);
+
+        return $this->db->transaction(function () use ($queue) {
+
+            if ($job = $this->getNextAvailableJob($queue)) {
+
+                $job = $this->markJobAsReserved($job);
+
+                return new DatabaseJob($this->app, $this, $job, $this->connection, $queue);
+            }
+        });
+    }
+
+    /**
+     * 获取下个有效任务
+     *
+     * @param string|null $queue
+     * @return StdClass|null
+     */
+    protected function getNextAvailableJob($queue)
+    {
+
+        $job = $this->db->name($this->table)
+            ->lock(true)
+            ->where('queue', $this->getQueue($queue))
+            ->where(function (Query $query) {
+                $query->where(function (Query $query) {
+                    $query->whereNull('reserved_at')
+                        ->where('available_at', '<=', $this->currentTime());
+                });
+
+                //超时任务重试
+                $expiration = Carbon::now()->subSeconds($this->retryAfter)->getTimestamp();
+
+                $query->whereOr(function (Query $query) use ($expiration) {
+                    $query->where('reserved_at', '<=', $expiration);
+                });
+            })
+            ->order('id', 'asc')
+            ->find();
+
+        return $job ? (object) $job : null;
+    }
+
+    /**
+     * 标记任务正在执行.
+     *
+     * @param stdClass $job
+     * @return stdClass
+     */
+    protected function markJobAsReserved($job)
+    {
+        $this->db->name($this->table)->where('id', $job->id)->update([
+            'reserved_at' => $job->reserved_at = $this->currentTime(),
+            'attempts'    => ++$job->attempts,
+        ]);
+
+        return $job;
+    }
+
+    /**
+     * 删除任务
+     *
+     * @param string $id
+     * @return void
+     */
+    public function deleteReserved($id)
+    {
+        $this->db->transaction(function () use ($id) {
+            if ($this->db->name($this->table)->lock(true)->find($id)) {
+                $this->db->name($this->table)->where('id', $id)->delete();
+            }
+        });
+    }
+
+    protected function getQueue($queue)
+    {
+        return $queue ?: $this->default;
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/connector/Redis.php b/vendor/topthink/think-queue/src/queue/connector/Redis.php
new file mode 100755
index 0000000..063565a
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/connector/Redis.php
@@ -0,0 +1,298 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\queue\connector;
+
+use Closure;
+use Exception;
+use think\helper\Str;
+use think\queue\Connector;
+use think\queue\InteractsWithTime;
+use think\queue\job\Redis as RedisJob;
+
+class Redis extends Connector
+{
+    use InteractsWithTime;
+
+    /** @var  \Redis */
+    protected $redis;
+
+    /**
+     * The name of the default queue.
+     *
+     * @var string
+     */
+    protected $default;
+
+    /**
+     * The expiration time of a job.
+     *
+     * @var int|null
+     */
+    protected $retryAfter = 60;
+
+    /**
+     * The maximum number of seconds to block for a job.
+     *
+     * @var int|null
+     */
+    protected $blockFor = null;
+
+    public function __construct(\Redis $redis, $default = 'default', $retryAfter = 60, $blockFor = null)
+    {
+        $this->redis      = $redis;
+        $this->default    = $default;
+        $this->retryAfter = $retryAfter;
+        $this->blockFor   = $blockFor;
+    }
+
+    public static function __make($config)
+    {
+        if (!extension_loaded('redis')) {
+            throw new Exception('redis扩展未安装');
+        }
+
+        $func = $config['persistent'] ? 'pconnect' : 'connect';
+
+        $redis = new \Redis;
+        $redis->$func($config['host'], $config['port'], $config['timeout']);
+
+        if ('' != $config['password']) {
+            $redis->auth($config['password']);
+        }
+
+        if (0 != $config['select']) {
+            $redis->select($config['select']);
+        }
+
+        return new self($redis, $config['queue'], $config['retry_after'] ?? 60, $config['block_for'] ?? null);
+    }
+
+    public function size($queue)
+    {
+        $queue = $this->getQueue($queue);
+
+        return $this->redis->lLen($queue) + $this->redis->zCard("{$queue}:delayed") + $this->redis->zCard("{$queue}:reserved");
+    }
+
+    public function push($job, $data = '', $queue = null)
+    {
+        return $this->pushRaw($this->createPayload($job, $data), $queue);
+    }
+
+    public function pushRaw($payload, $queue = null, array $options = [])
+    {
+        $this->redis->rPush($this->getQueue($queue), $payload);
+
+        return json_decode($payload, true)['id'] ?? null;
+    }
+
+    public function later($delay, $job, $data = '', $queue = null)
+    {
+        return $this->laterRaw($delay, $this->createPayload($job, $data), $queue);
+    }
+
+    protected function laterRaw($delay, $payload, $queue = null)
+    {
+        $this->redis->zadd(
+            $this->getQueue($queue) . ':delayed', $this->availableAt($delay), $payload
+        );
+
+        return json_decode($payload, true)['id'] ?? null;
+    }
+
+    public function pop($queue = null)
+    {
+        $this->migrate($prefixed = $this->getQueue($queue));
+
+        if (empty($nextJob = $this->retrieveNextJob($prefixed))) {
+            return;
+        }
+
+        [$job, $reserved] = $nextJob;
+
+        if ($reserved) {
+            return new RedisJob($this->app, $this, $job, $reserved, $this->connection, $queue);
+        }
+    }
+
+    /**
+     * Migrate any delayed or expired jobs onto the primary queue.
+     *
+     * @param string $queue
+     * @return void
+     */
+    protected function migrate($queue)
+    {
+        $this->migrateExpiredJobs($queue . ':delayed', $queue);
+
+        if (!is_null($this->retryAfter)) {
+            $this->migrateExpiredJobs($queue . ':reserved', $queue);
+        }
+    }
+
+    /**
+     * 移动延迟任务
+     *
+     * @param string $from
+     * @param string $to
+     * @param bool   $attempt
+     */
+    public function migrateExpiredJobs($from, $to, $attempt = true)
+    {
+        $this->redis->watch($from);
+
+        $jobs = $this->redis->zRangeByScore($from, '-inf', $this->currentTime());
+
+        if (!empty($jobs)) {
+            $this->transaction(function () use ($from, $to, $jobs, $attempt) {
+
+                $this->redis->zRemRangeByRank($from, 0, count($jobs) - 1);
+
+                for ($i = 0; $i < count($jobs); $i += 100) {
+
+                    $values = array_slice($jobs, $i, 100);
+
+                    $this->redis->rPush($to, ...$values);
+                }
+            });
+        }
+
+        $this->redis->unwatch();
+    }
+
+    /**
+     * Retrieve the next job from the queue.
+     *
+     * @param string $queue
+     * @return array
+     */
+    protected function retrieveNextJob($queue)
+    {
+        if (!is_null($this->blockFor)) {
+            return $this->blockingPop($queue);
+        }
+
+        $job      = $this->redis->lpop($queue);
+        $reserved = false;
+
+        if ($job) {
+            $reserved = json_decode($job, true);
+            $reserved['attempts']++;
+            $reserved = json_encode($reserved);
+            $this->redis->zAdd($queue . ':reserved', $this->availableAt($this->retryAfter), $reserved);
+        }
+
+        return [$job, $reserved];
+    }
+
+    /**
+     * Retrieve the next job by blocking-pop.
+     *
+     * @param string $queue
+     * @return array
+     */
+    protected function blockingPop($queue)
+    {
+        $rawBody = $this->redis->blpop($queue, $this->blockFor);
+
+        if (!empty($rawBody)) {
+            $payload = json_decode($rawBody[1], true);
+
+            $payload['attempts']++;
+
+            $reserved = json_encode($payload);
+
+            $this->redis->zadd($queue . ':reserved', $this->availableAt($this->retryAfter), $reserved);
+
+            return [$rawBody[1], $reserved];
+        }
+
+        return [null, null];
+    }
+
+    /**
+     * 删除任务
+     *
+     * @param string   $queue
+     * @param RedisJob $job
+     * @return void
+     */
+    public function deleteReserved($queue, $job)
+    {
+        $this->redis->zRem($this->getQueue($queue) . ':reserved', $job->getReservedJob());
+    }
+
+    /**
+     * Delete a reserved job from the reserved queue and release it.
+     *
+     * @param string   $queue
+     * @param RedisJob $job
+     * @param int      $delay
+     * @return void
+     */
+    public function deleteAndRelease($queue, $job, $delay)
+    {
+        $queue = $this->getQueue($queue);
+
+        $reserved = $job->getReservedJob();
+
+        $this->redis->zRem($queue . ':reserved', $reserved);
+
+        $this->redis->zAdd($queue . ':delayed', $this->availableAt($delay), $reserved);
+    }
+
+    /**
+     * redis事务
+     * @param Closure $closure
+     */
+    protected function transaction(Closure $closure)
+    {
+        $this->redis->multi();
+        try {
+            call_user_func($closure);
+            if (!$this->redis->exec()) {
+                $this->redis->discard();
+            }
+        } catch (Exception $e) {
+            $this->redis->discard();
+        }
+    }
+
+    protected function createPayloadArray($job, $data = '')
+    {
+        return array_merge(parent::createPayloadArray($job, $data), [
+            'id'       => $this->getRandomId(),
+            'attempts' => 0,
+        ]);
+    }
+
+    /**
+     * 随机id
+     *
+     * @return string
+     */
+    protected function getRandomId()
+    {
+        return Str::random(32);
+    }
+
+    /**
+     * 获取队列名
+     *
+     * @param string|null $queue
+     * @return string
+     */
+    protected function getQueue($queue)
+    {
+        return 'queues:' . ($queue ?: $this->default);
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/connector/Sync.php b/vendor/topthink/think-queue/src/queue/connector/Sync.php
new file mode 100755
index 0000000..3f4d420
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/connector/Sync.php
@@ -0,0 +1,74 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\queue\connector;
+
+use Exception;
+use think\queue\Connector;
+use think\queue\event\JobFailed;
+use think\queue\event\JobProcessed;
+use think\queue\event\JobProcessing;
+use think\queue\job\Sync as SyncJob;
+use Throwable;
+
+class Sync extends Connector
+{
+
+    public function size($queue = null)
+    {
+        return 0;
+    }
+
+    public function push($job, $data = '', $queue = null)
+    {
+        $queueJob = $this->resolveJob($this->createPayload($job, $data), $queue);
+
+        try {
+            $this->triggerEvent(new JobProcessing($this->connection, $job));
+
+            $queueJob->fire();
+
+            $this->triggerEvent(new JobProcessed($this->connection, $job));
+        } catch (Exception | Throwable $e) {
+
+            $this->triggerEvent(new JobFailed($this->connection, $job, $e));
+
+            throw $e;
+        }
+
+        return 0;
+    }
+
+    protected function triggerEvent($event)
+    {
+        $this->app->event->trigger($event);
+    }
+
+    public function pop($queue = null)
+    {
+
+    }
+
+    protected function resolveJob($payload, $queue)
+    {
+        return new SyncJob($this->app, $payload, $this->connection, $queue);
+    }
+
+    public function pushRaw($payload, $queue = null, array $options = [])
+    {
+
+    }
+
+    public function later($delay, $job, $data = '', $queue = null)
+    {
+        return $this->push($job, $data, $queue);
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/event/JobExceptionOccurred.php b/vendor/topthink/think-queue/src/queue/event/JobExceptionOccurred.php
new file mode 100755
index 0000000..2e7d1eb
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/event/JobExceptionOccurred.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace think\queue\event;
+
+use Exception;
+use think\queue\Job;
+
+class JobExceptionOccurred
+{
+    /**
+     * The connection name.
+     *
+     * @var string
+     */
+    public $connectionName;
+
+    /**
+     * The job instance.
+     *
+     * @var Job
+     */
+    public $job;
+
+    /**
+     * The exception instance.
+     *
+     * @var Exception
+     */
+    public $exception;
+
+    /**
+     * Create a new event instance.
+     *
+     * @param string    $connectionName
+     * @param Job       $job
+     * @param Exception $exception
+     * @return void
+     */
+    public function __construct($connectionName, $job, $exception)
+    {
+        $this->job            = $job;
+        $this->exception      = $exception;
+        $this->connectionName = $connectionName;
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/event/JobFailed.php b/vendor/topthink/think-queue/src/queue/event/JobFailed.php
new file mode 100755
index 0000000..9e3706a
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/event/JobFailed.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace think\queue\event;
+
+use think\queue\Job;
+
+class JobFailed
+{
+    /** @var string */
+    public $connection;
+
+    /** @var Job */
+    public $job;
+
+    /** @var \Exception */
+    public $exception;
+
+    public function __construct($connection, $job, $exception)
+    {
+        $this->connection = $connection;
+        $this->job        = $job;
+        $this->exception  = $exception;
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/event/JobProcessed.php b/vendor/topthink/think-queue/src/queue/event/JobProcessed.php
new file mode 100755
index 0000000..9ae9fca
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/event/JobProcessed.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace think\queue\event;
+
+use think\queue\Job;
+
+class JobProcessed
+{
+    /** @var string */
+    public $connection;
+
+    /** @var Job */
+    public $job;
+
+    public function __construct($connection, $job)
+    {
+        $this->connection = $connection;
+        $this->job        = $job;
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/event/JobProcessing.php b/vendor/topthink/think-queue/src/queue/event/JobProcessing.php
new file mode 100755
index 0000000..72ff3c5
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/event/JobProcessing.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace think\queue\event;
+
+use think\queue\Job;
+
+class JobProcessing
+{
+    /** @var string */
+    public $connection;
+
+    /** @var Job */
+    public $job;
+
+    public function __construct($connection, $job)
+    {
+        $this->connection = $connection;
+        $this->job        = $job;
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/event/WorkerStopping.php b/vendor/topthink/think-queue/src/queue/event/WorkerStopping.php
new file mode 100755
index 0000000..5f1fc75
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/event/WorkerStopping.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace think\queue\event;
+
+class WorkerStopping
+{
+    /**
+     * The exit status.
+     *
+     * @var int
+     */
+    public $status;
+
+    /**
+     * Create a new event instance.
+     *
+     * @param int $status
+     * @return void
+     */
+    public function __construct($status = 0)
+    {
+        $this->status = $status;
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/exception/MaxAttemptsExceededException.php b/vendor/topthink/think-queue/src/queue/exception/MaxAttemptsExceededException.php
new file mode 100755
index 0000000..fb8440b
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/exception/MaxAttemptsExceededException.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace think\queue\exception;
+
+use RuntimeException;
+
+class MaxAttemptsExceededException extends RuntimeException
+{
+
+}
diff --git a/vendor/topthink/think-queue/src/queue/failed/Database.php b/vendor/topthink/think-queue/src/queue/failed/Database.php
new file mode 100755
index 0000000..3f15af9
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/failed/Database.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace think\queue\failed;
+
+use Carbon\Carbon;
+use think\Db;
+use think\queue\FailedJob;
+
+class Database extends FailedJob
+{
+
+    /** @var Db */
+    protected $db;
+
+    /**
+     * The database table.
+     *
+     * @var string
+     */
+    protected $table;
+
+    public function __construct(Db $db, $table)
+    {
+        $this->db    = $db;
+        $this->table = $table;
+    }
+
+    public static function __make(Db $db, $config)
+    {
+        return new self($db, $config['table']);
+    }
+
+    /**
+     * Log a failed job into storage.
+     *
+     * @param string     $connection
+     * @param string     $queue
+     * @param string     $payload
+     * @param \Exception $exception
+     * @return int|null
+     */
+    public function log($connection, $queue, $payload, $exception)
+    {
+        $failed_at = Carbon::now();
+
+        $exception = (string) $exception;
+
+        return $this->getTable()->insertGetId(compact(
+            'connection', 'queue', 'payload', 'exception', 'failed_at'
+        ));
+    }
+
+    /**
+     * Get a list of all of the failed jobs.
+     *
+     * @return array
+     */
+    public function all()
+    {
+        return collect($this->getTable()->order('id', 'desc')->select())->all();
+    }
+
+    /**
+     * Get a single failed job.
+     *
+     * @param mixed $id
+     * @return object|null
+     */
+    public function find($id)
+    {
+        return $this->getTable()->find($id);
+    }
+
+    /**
+     * Delete a single failed job from storage.
+     *
+     * @param mixed $id
+     * @return bool
+     */
+    public function forget($id)
+    {
+        return $this->getTable()->where('id', $id)->delete() > 0;
+    }
+
+    /**
+     * Flush all of the failed jobs from storage.
+     *
+     * @return void
+     */
+    public function flush()
+    {
+        $this->getTable()->delete(true);
+    }
+
+    protected function getTable()
+    {
+        return $this->db->name($this->table);
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/failed/None.php b/vendor/topthink/think-queue/src/queue/failed/None.php
new file mode 100755
index 0000000..205dd2f
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/failed/None.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace think\queue\failed;
+
+use think\queue\FailedJob;
+
+class None extends FailedJob
+{
+
+    /**
+     * Log a failed job into storage.
+     *
+     * @param string     $connection
+     * @param string     $queue
+     * @param string     $payload
+     * @param \Exception $exception
+     */
+    public function log($connection, $queue, $payload, $exception)
+    {
+
+    }
+
+    /**
+     * Get a list of all of the failed jobs.
+     *
+     * @return array
+     */
+    public function all()
+    {
+        return [];
+    }
+
+    /**
+     * Get a single failed job.
+     *
+     * @param mixed $id
+     */
+    public function find($id)
+    {
+
+    }
+
+    /**
+     * Delete a single failed job from storage.
+     *
+     * @param mixed $id
+     * @return bool
+     */
+    public function forget($id)
+    {
+        return true;
+    }
+
+    /**
+     * Flush all of the failed jobs from storage.
+     *
+     * @return void
+     */
+    public function flush()
+    {
+
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/job/Database.php b/vendor/topthink/think-queue/src/queue/job/Database.php
new file mode 100755
index 0000000..bef756a
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/job/Database.php
@@ -0,0 +1,91 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+namespace think\queue\job;
+
+use think\App;
+use think\queue\connector\Database as DatabaseQueue;
+use think\queue\Job;
+
+class Database extends Job
+{
+    /**
+     * The database queue instance.
+     * @var DatabaseQueue
+     */
+    protected $database;
+
+    /**
+     * The database job payload.
+     * @var Object
+     */
+    protected $job;
+
+    public function __construct(App $app, DatabaseQueue $database, $job, $connection, $queue)
+    {
+        $this->app        = $app;
+        $this->job        = $job;
+        $this->queue      = $queue;
+        $this->database   = $database;
+        $this->connection = $connection;
+    }
+
+    /**
+     * 删除任务
+     * @return void
+     */
+    public function delete()
+    {
+        parent::delete();
+        $this->database->deleteReserved($this->job->id);
+    }
+
+    /**
+     * 重新发布任务
+     * @param int $delay
+     * @return void
+     */
+    public function release($delay = 0)
+    {
+        parent::release($delay);
+
+        $this->delete();
+
+        $this->database->release($this->queue, $this->job, $delay);
+    }
+
+    /**
+     * 获取当前任务尝试次数
+     * @return int
+     */
+    public function attempts()
+    {
+        return (int) $this->job->attempts;
+    }
+
+    /**
+     * Get the raw body string for the job.
+     * @return string
+     */
+    public function getRawBody()
+    {
+        return $this->job->payload;
+    }
+
+    /**
+     * Get the job identifier.
+     *
+     * @return string
+     */
+    public function getJobId()
+    {
+        return $this->job->id;
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/job/Redis.php b/vendor/topthink/think-queue/src/queue/job/Redis.php
new file mode 100755
index 0000000..79b6e40
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/job/Redis.php
@@ -0,0 +1,121 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\queue\job;
+
+use think\App;
+use think\queue\connector\Redis as RedisQueue;
+use think\queue\Job;
+
+class Redis extends Job
+{
+
+    /**
+     * The redis queue instance.
+     * @var RedisQueue
+     */
+    protected $redis;
+
+    /**
+     * The database job payload.
+     * @var Object
+     */
+    protected $job;
+
+    /**
+     * The JSON decoded version of "$job".
+     *
+     * @var array
+     */
+    protected $decoded;
+
+    /**
+     * The Redis job payload inside the reserved queue.
+     *
+     * @var string
+     */
+    protected $reserved;
+
+    public function __construct(App $app, RedisQueue $redis, $job, $reserved, $connection, $queue)
+    {
+        $this->app        = $app;
+        $this->job        = $job;
+        $this->queue      = $queue;
+        $this->connection = $connection;
+        $this->redis      = $redis;
+        $this->reserved   = $reserved;
+
+        $this->decoded = $this->payload();
+    }
+
+    /**
+     * Get the number of times the job has been attempted.
+     * @return int
+     */
+    public function attempts()
+    {
+        return ($this->decoded['attempts'] ?? null) + 1;
+    }
+
+    /**
+     * Get the raw body string for the job.
+     * @return string
+     */
+    public function getRawBody()
+    {
+        return $this->job;
+    }
+
+    /**
+     * 删除任务
+     *
+     * @return void
+     */
+    public function delete()
+    {
+        parent::delete();
+
+        $this->redis->deleteReserved($this->queue, $this);
+    }
+
+    /**
+     * 重新发布任务
+     *
+     * @param int $delay
+     * @return void
+     */
+    public function release($delay = 0)
+    {
+        parent::release($delay);
+
+        $this->redis->deleteAndRelease($this->queue, $this, $delay);
+    }
+
+    /**
+     * Get the job identifier.
+     *
+     * @return string
+     */
+    public function getJobId()
+    {
+        return $this->decoded['id'] ?? null;
+    }
+
+    /**
+     * Get the underlying reserved Redis job.
+     *
+     * @return string
+     */
+    public function getReservedJob()
+    {
+        return $this->reserved;
+    }
+}
diff --git a/vendor/topthink/think-queue/src/queue/job/Sync.php b/vendor/topthink/think-queue/src/queue/job/Sync.php
new file mode 100755
index 0000000..c2299d8
--- /dev/null
+++ b/vendor/topthink/think-queue/src/queue/job/Sync.php
@@ -0,0 +1,66 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\queue\job;
+
+use think\App;
+use think\queue\Job;
+
+class Sync extends Job
+{
+    /**
+     * The queue message data.
+     *
+     * @var string
+     */
+    protected $payload;
+
+    public function __construct(App $app, $payload, $connection, $queue)
+    {
+        $this->app        = $app;
+        $this->connection = $connection;
+        $this->queue      = $queue;
+        $this->payload    = $payload;
+    }
+
+    /**
+     * Get the number of times the job has been attempted.
+     * @return int
+     */
+    public function attempts()
+    {
+        return 1;
+    }
+
+    /**
+     * Get the raw body string for the job.
+     * @return string
+     */
+    public function getRawBody()
+    {
+        return $this->payload;
+    }
+
+    /**
+     * Get the job identifier.
+     *
+     * @return string
+     */
+    public function getJobId()
+    {
+        return '';
+    }
+
+    public function getQueue()
+    {
+        return 'sync';
+    }
+}
diff --git a/vendor/topthink/think-queue/tests/DatabaseConnectorTest.php b/vendor/topthink/think-queue/tests/DatabaseConnectorTest.php
new file mode 100755
index 0000000..3856886
--- /dev/null
+++ b/vendor/topthink/think-queue/tests/DatabaseConnectorTest.php
@@ -0,0 +1,123 @@
+<?php
+
+namespace think\test\queue;
+
+use Carbon\Carbon;
+use Mockery as m;
+use Mockery\MockInterface;
+use ReflectionClass;
+use stdClass;
+use think\Db;
+use think\queue\Connector;
+use think\queue\connector\Database;
+
+class DatabaseConnectorTest extends TestCase
+{
+    /** @var Database|MockInterface */
+    protected $connector;
+
+    /** @var Db|MockInterface */
+    protected $db;
+
+    protected function setUp()
+    {
+        parent::setUp();
+        $this->db        = m::mock(Db::class);
+        $this->connector = new Database($this->db, 'table', 'default');
+    }
+
+    public function testPushProperlyPushesJobOntoDatabase()
+    {
+        $this->db->shouldReceive('name')->with('table')->andReturn($query = m::mock(stdClass::class));
+
+        $query->shouldReceive('insertGetId')->once()->andReturnUsing(function ($array) {
+            $this->assertEquals('default', $array['queue']);
+            $this->assertEquals(json_encode(['job' => 'foo', 'maxTries' => null, 'timeout' => null, 'data' => ['data']]), $array['payload']);
+            $this->assertEquals(0, $array['attempts']);
+            $this->assertNull($array['reserved_at']);
+            $this->assertInternalType('int', $array['available_at']);
+        });
+        $this->connector->push('foo', ['data']);
+    }
+
+    public function testDelayedPushProperlyPushesJobOntoDatabase()
+    {
+        $this->db->shouldReceive('name')->with('table')->andReturn($query = m::mock(stdClass::class));
+
+        $query->shouldReceive('insertGetId')->once()->andReturnUsing(function ($array) {
+            $this->assertEquals('default', $array['queue']);
+            $this->assertEquals(json_encode(['job' => 'foo', 'maxTries' => null, 'timeout' => null, 'data' => ['data']]), $array['payload']);
+            $this->assertEquals(0, $array['attempts']);
+            $this->assertNull($array['reserved_at']);
+            $this->assertInternalType('int', $array['available_at']);
+        });
+
+        $this->connector->later(10, 'foo', ['data']);
+    }
+
+    public function testFailureToCreatePayloadFromObject()
+    {
+        $this->expectException('InvalidArgumentException');
+
+        $job          = new stdClass;
+        $job->invalid = "\xc3\x28";
+
+        $queue = $this->getMockForAbstractClass(Connector::class);
+        $class = new ReflectionClass(Connector::class);
+
+        $createPayload = $class->getMethod('createPayload');
+        $createPayload->setAccessible(true);
+        $createPayload->invokeArgs($queue, [
+            $job,
+            'queue-name',
+        ]);
+    }
+
+    public function testFailureToCreatePayloadFromArray()
+    {
+        $this->expectException('InvalidArgumentException');
+
+        $queue = $this->getMockForAbstractClass(Connector::class);
+        $class = new ReflectionClass(Connector::class);
+
+        $createPayload = $class->getMethod('createPayload');
+        $createPayload->setAccessible(true);
+        $createPayload->invokeArgs($queue, [
+            ["\xc3\x28"],
+            'queue-name',
+        ]);
+    }
+
+    public function testBulkBatchPushesOntoDatabase()
+    {
+
+        $this->db->shouldReceive('name')->with('table')->andReturn($query = m::mock(stdClass::class));
+
+        Carbon::setTestNow(
+            $now = Carbon::now()->addSeconds()
+        );
+
+        $query->shouldReceive('insertAll')->once()->andReturnUsing(function ($records) use ($now) {
+            $this->assertEquals([
+                [
+                    'queue'        => 'queue',
+                    'payload'      => json_encode(['job' => 'foo', 'maxTries' => null, 'timeout' => null, 'data' => ['data']]),
+                    'attempts'     => 0,
+                    'reserved_at'  => null,
+                    'available_at' => $now->getTimestamp(),
+                    'created_at'   => $now->getTimestamp(),
+                ], [
+                    'queue'        => 'queue',
+                    'payload'      => json_encode(['job' => 'bar', 'maxTries' => null, 'timeout' => null, 'data' => ['data']]),
+                    'attempts'     => 0,
+                    'reserved_at'  => null,
+                    'available_at' => $now->getTimestamp(),
+                    'created_at'   => $now->getTimestamp(),
+                ],
+            ], $records);
+        });
+
+        $this->connector->bulk(['foo', 'bar'], ['data'], 'queue');
+    }
+
+}
diff --git a/vendor/topthink/think-queue/tests/ListenerTest.php b/vendor/topthink/think-queue/tests/ListenerTest.php
new file mode 100755
index 0000000..883b033
--- /dev/null
+++ b/vendor/topthink/think-queue/tests/ListenerTest.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace think\test\queue;
+
+use Mockery as m;
+use Mockery\MockInterface;
+use Symfony\Component\Process\Process;
+use think\queue\Listener;
+
+class ListenerTest extends TestCase
+{
+    /** @var Process|MockInterface */
+    protected $process;
+    /** @var Listener|MockInterface */
+    protected $listener;
+
+    public function testRunProcessCallsProcess()
+    {
+        /** @var Process|MockInterface $process */
+        $process = m::mock(Process::class)->makePartial();
+        $process->shouldReceive('run')->once();
+        /** @var Listener|MockInterface $listener */
+        $listener = m::mock(Listener::class)->makePartial();
+        $listener->shouldReceive('memoryExceeded')->once()->with(1)->andReturn(false);
+
+        $listener->runProcess($process, 1);
+    }
+
+    public function testListenerStopsWhenMemoryIsExceeded()
+    {
+        /** @var Process|MockInterface $process */
+        $process = m::mock(Process::class)->makePartial();
+        $process->shouldReceive('run')->once();
+        /** @var Listener|MockInterface $listener */
+        $listener = m::mock(Listener::class)->makePartial();
+        $listener->shouldReceive('memoryExceeded')->once()->with(1)->andReturn(true);
+        $listener->shouldReceive('stop')->once();
+
+        $listener->runProcess($process, 1);
+    }
+
+    public function testMakeProcessCorrectlyFormatsCommandLine()
+    {
+        $listener = new Listener(__DIR__);
+
+        $process = $listener->makeProcess('connection', 'queue', 1, 3, 0, 2, 3);
+        $escape  = '\\' === DIRECTORY_SEPARATOR ? '"' : '\'';
+
+        $this->assertInstanceOf(Process::class, $process);
+        $this->assertEquals(__DIR__, $process->getWorkingDirectory());
+        $this->assertEquals(3, $process->getTimeout());
+        $this->assertEquals($escape . PHP_BINARY . $escape . " {$escape}think{$escape} {$escape}queue:work{$escape} {$escape}connection{$escape} {$escape}--once{$escape} {$escape}--queue=queue{$escape} {$escape}--delay=1{$escape} {$escape}--memory=2{$escape} {$escape}--sleep=3{$escape} {$escape}--tries=0{$escape}", $process->getCommandLine());
+    }
+
+    public function testMakeProcessCorrectlyFormatsCommandLineWithAnEnvironmentSpecified()
+    {
+        $listener = new Listener(__DIR__);
+
+        $process = $listener->makeProcess('connection', 'queue', 1, 3, 0, 2, 3);
+        $escape  = '\\' === DIRECTORY_SEPARATOR ? '"' : '\'';
+
+        $this->assertInstanceOf(Process::class, $process);
+        $this->assertEquals(__DIR__, $process->getWorkingDirectory());
+        $this->assertEquals(3, $process->getTimeout());
+        $this->assertEquals($escape . PHP_BINARY . $escape . " {$escape}think{$escape} {$escape}queue:work{$escape} {$escape}connection{$escape} {$escape}--once{$escape} {$escape}--queue=queue{$escape} {$escape}--delay=1{$escape} {$escape}--memory=2{$escape} {$escape}--sleep=3{$escape} {$escape}--tries=0{$escape}", $process->getCommandLine());
+    }
+
+    public function testMakeProcessCorrectlyFormatsCommandLineWhenTheConnectionIsNotSpecified()
+    {
+        $listener = new Listener(__DIR__);
+
+        $process = $listener->makeProcess(null, 'queue', 1, 3, 0, 2, 3);
+        $escape  = '\\' === DIRECTORY_SEPARATOR ? '"' : '\'';
+
+        $this->assertInstanceOf(Process::class, $process);
+        $this->assertEquals(__DIR__, $process->getWorkingDirectory());
+        $this->assertEquals(3, $process->getTimeout());
+        $this->assertEquals($escape . PHP_BINARY . $escape . " {$escape}think{$escape} {$escape}queue:work{$escape} {$escape}--once{$escape} {$escape}--queue=queue{$escape} {$escape}--delay=1{$escape} {$escape}--memory=2{$escape} {$escape}--sleep=3{$escape} {$escape}--tries=0{$escape}", $process->getCommandLine());
+    }
+}
diff --git a/vendor/topthink/think-queue/tests/QueueTest.php b/vendor/topthink/think-queue/tests/QueueTest.php
new file mode 100755
index 0000000..a87c80d
--- /dev/null
+++ b/vendor/topthink/think-queue/tests/QueueTest.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace think\test\queue;
+
+use InvalidArgumentException;
+use Mockery as m;
+use think\Config;
+use think\Queue;
+use think\queue\connector\Sync;
+
+class QueueTest extends TestCase
+{
+    /** @var Queue */
+    protected $queue;
+
+    protected function setUp()
+    {
+        parent::setUp();
+        $this->queue = new Queue($this->app);
+    }
+
+    public function testDefaultConnectionCanBeResolved()
+    {
+        $sync = new Sync();
+
+        $this->app->shouldReceive('invokeClass')->once()->with('\think\queue\connector\Sync', [['driver' => 'sync']])->andReturn($sync);
+
+        $config = m::mock(Config::class);
+
+        $config->shouldReceive('get')->twice()->with('queue.connectors.sync', ['driver' => 'sync'])->andReturn(['driver' => 'sync']);
+        $config->shouldReceive('get')->once()->with('queue.default', 'sync')->andReturn('sync');
+
+        $this->app->shouldReceive('get')->times(3)->with('config')->andReturn($config);
+
+        $this->assertSame($sync, $this->queue->driver('sync'));
+        $this->assertSame($sync, $this->queue->driver());
+    }
+
+    public function testNotSupportDriver()
+    {
+        $config = m::mock(Config::class);
+
+        $config->shouldReceive('get')->once()->with('queue.connectors.hello', ['driver' => 'sync'])->andReturn(['driver' => 'hello']);
+        $this->app->shouldReceive('get')->once()->with('config')->andReturn($config);
+
+        $this->expectException(InvalidArgumentException::class);
+        $this->queue->driver('hello');
+    }
+}
diff --git a/vendor/topthink/think-queue/tests/TestCase.php b/vendor/topthink/think-queue/tests/TestCase.php
new file mode 100755
index 0000000..5a9279d
--- /dev/null
+++ b/vendor/topthink/think-queue/tests/TestCase.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace think\test\queue;
+
+use Mockery as m;
+use Mockery\MockInterface;
+use think\App;
+
+abstract class TestCase extends \PHPUnit\Framework\TestCase
+{
+    /** @var App|MockInterface */
+    protected $app;
+
+    public function tearDown()
+    {
+        m::close();
+    }
+
+    protected function setUp()
+    {
+        $this->app = m::mock(App::class)->makePartial();
+    }
+}
diff --git a/vendor/topthink/think-queue/tests/WorkerTest.php b/vendor/topthink/think-queue/tests/WorkerTest.php
new file mode 100755
index 0000000..87eeacc
--- /dev/null
+++ b/vendor/topthink/think-queue/tests/WorkerTest.php
@@ -0,0 +1,420 @@
+<?php
+
+namespace think\test\queue;
+
+use Carbon\Carbon;
+use Mockery as m;
+use Mockery\MockInterface;
+use RuntimeException;
+use think\Cache;
+use think\Event;
+use think\exception\Handle;
+use think\Queue;
+use think\queue\connector\Sync;
+use think\queue\event\JobExceptionOccurred;
+use think\queue\event\JobFailed;
+use think\queue\event\JobProcessed;
+use think\queue\event\JobProcessing;
+use think\queue\exception\MaxAttemptsExceededException;
+
+class WorkerTest extends TestCase
+{
+    /** @var Handle|MockInterface */
+    protected $handle;
+
+    /** @var Event|MockInterface */
+    protected $event;
+
+    /** @var Cache|MockInterface */
+    protected $cache;
+
+    /** @var Queue|MockInterface */
+    protected $queue;
+
+    protected function setUp()
+    {
+        parent::setUp();
+        $this->queue  = m::mock(Queue::class);
+        $this->handle = m::spy(Handle::class);
+        $this->event  = m::spy(Event::class);
+        $this->cache  = m::spy(Cache::class);
+    }
+
+    public function testJobCanBeFired()
+    {
+
+        $worker = $this->getWorker(['default' => [$job = new WorkerFakeJob]]);
+
+        $this->event->shouldReceive('trigger')->with(m::type(JobProcessing::class))->once();
+        $this->event->shouldReceive('trigger')->with(m::type(JobProcessed::class))->once();
+
+        $worker->runNextJob('sync', 'default');
+    }
+
+    public function testWorkerCanWorkUntilQueueIsEmpty()
+    {
+        $worker = $this->getWorker(['default' => [
+            $firstJob = new WorkerFakeJob,
+            $secondJob = new WorkerFakeJob,
+        ]]);
+
+        $this->expectException(LoopBreakerException::class);
+
+        $worker->daemon('sync', 'default');
+
+        $this->assertTrue($firstJob->fired);
+
+        $this->assertTrue($secondJob->fired);
+
+        $this->assertSame(0, $worker->stoppedWithStatus);
+
+        $this->event->shouldHaveReceived('trigger')->with(m::type(JobProcessing::class))->twice();
+
+        $this->event->shouldHaveReceived('trigger')->with(m::type(JobProcessed::class))->twice();
+    }
+
+    public function testJobCanBeFiredBasedOnPriority()
+    {
+        $worker = $this->getWorker([
+            'high' => [
+                $highJob = new WorkerFakeJob,
+                $secondHighJob = new WorkerFakeJob,
+            ],
+            'low'  => [$lowJob = new WorkerFakeJob],
+        ]);
+
+        $worker->runNextJob('sync', 'high,low');
+
+        $this->assertTrue($highJob->fired);
+        $this->assertFalse($secondHighJob->fired);
+        $this->assertFalse($lowJob->fired);
+
+        $worker->runNextJob('sync', 'high,low');
+        $this->assertTrue($secondHighJob->fired);
+        $this->assertFalse($lowJob->fired);
+
+        $worker->runNextJob('sync', 'high,low');
+        $this->assertTrue($lowJob->fired);
+    }
+
+    public function testExceptionIsReportedIfConnectionThrowsExceptionOnJobPop()
+    {
+        $e = new RuntimeException();
+
+        $sync = m::mock(Sync::class);
+
+        $sync->shouldReceive('pop')->andReturnUsing(function () use ($e) {
+            throw $e;
+        });
+
+        $this->queue->shouldReceive('driver')->with('sync')->andReturn($sync);
+
+        $worker = new Worker($this->queue, $this->event, $this->handle);
+
+        $worker->runNextJob('sync', 'default');
+
+        $this->handle->shouldHaveReceived('report')->with($e);
+    }
+
+    public function testWorkerSleepsWhenQueueIsEmpty()
+    {
+        $worker = $this->getWorker(['default' => []]);
+        $worker->runNextJob('sync', 'default', 0, 5);
+        $this->assertEquals(5, $worker->sleptFor);
+    }
+
+    public function testJobIsReleasedOnException()
+    {
+        $e = new RuntimeException;
+
+        $job = new WorkerFakeJob(function () use ($e) {
+            throw $e;
+        });
+
+        $worker = $this->getWorker(['default' => [$job]]);
+        $worker->runNextJob('sync', 'default', 10);
+
+        $this->assertEquals(10, $job->releaseAfter);
+        $this->assertFalse($job->deleted);
+        $this->handle->shouldHaveReceived('report')->with($e);
+        $this->event->shouldHaveReceived('trigger')->with(m::type(JobExceptionOccurred::class))->once();
+        $this->event->shouldNotHaveReceived('trigger', [m::type(JobProcessed::class)]);
+    }
+
+    public function testJobIsNotReleasedIfItHasExceededMaxAttempts()
+    {
+        $e = new RuntimeException;
+
+        $job           = new WorkerFakeJob(function ($job) use ($e) {
+            // In normal use this would be incremented by being popped off the queue
+            $job->attempts++;
+
+            throw $e;
+        });
+        $job->attempts = 1;
+
+        $worker = $this->getWorker(['default' => [$job]]);
+        $worker->runNextJob('sync', 'default', 0, 3, 1);
+
+        $this->assertNull($job->releaseAfter);
+        $this->assertTrue($job->deleted);
+        $this->assertEquals($e, $job->failedWith);
+        $this->handle->shouldHaveReceived('report')->with($e);
+        $this->event->shouldHaveReceived('trigger')->with(m::type(JobExceptionOccurred::class))->once();
+        $this->event->shouldHaveReceived('trigger')->with(m::type(JobFailed::class))->once();
+        $this->event->shouldNotHaveReceived('trigger', [m::type(JobProcessed::class)]);
+    }
+
+    public function testJobIsNotReleasedIfItHasExpired()
+    {
+        $e = new RuntimeException;
+
+        $job = new WorkerFakeJob(function ($job) use ($e) {
+            // In normal use this would be incremented by being popped off the queue
+            $job->attempts++;
+
+            throw $e;
+        });
+
+        $job->timeoutAt = Carbon::now()->addSeconds(1)->getTimestamp();
+
+        $job->attempts = 0;
+
+        Carbon::setTestNow(
+            Carbon::now()->addSeconds(1)
+        );
+
+        $worker = $this->getWorker(['default' => [$job]]);
+        $worker->runNextJob('sync', 'default');
+
+        $this->assertNull($job->releaseAfter);
+        $this->assertTrue($job->deleted);
+        $this->assertEquals($e, $job->failedWith);
+        $this->handle->shouldHaveReceived('report')->with($e);
+        $this->event->shouldHaveReceived('trigger')->with(m::type(JobExceptionOccurred::class))->once();
+        $this->event->shouldHaveReceived('trigger')->with(m::type(JobFailed::class))->once();
+        $this->event->shouldNotHaveReceived('trigger', [m::type(JobProcessed::class)]);
+    }
+
+    public function testJobIsFailedIfItHasAlreadyExceededMaxAttempts()
+    {
+        $job = new WorkerFakeJob(function ($job) {
+            $job->attempts++;
+        });
+
+        $job->attempts = 2;
+
+        $worker = $this->getWorker(['default' => [$job]]);
+        $worker->runNextJob('sync', 'default', 0, 3, 1);
+
+        $this->assertNull($job->releaseAfter);
+        $this->assertTrue($job->deleted);
+        $this->assertInstanceOf(MaxAttemptsExceededException::class, $job->failedWith);
+        $this->handle->shouldHaveReceived('report')->with(m::type(MaxAttemptsExceededException::class));
+        $this->event->shouldHaveReceived('trigger')->with(m::type(JobExceptionOccurred::class))->once();
+        $this->event->shouldHaveReceived('trigger')->with(m::type(JobFailed::class))->once();
+        $this->event->shouldNotHaveReceived('trigger', [m::type(JobProcessed::class)]);
+    }
+
+    public function testJobIsFailedIfItHasAlreadyExpired()
+    {
+        $job = new WorkerFakeJob(function ($job) {
+            $job->attempts++;
+        });
+
+        $job->timeoutAt = Carbon::now()->addSeconds(2)->getTimestamp();
+
+        $job->attempts = 1;
+
+        Carbon::setTestNow(
+            Carbon::now()->addSeconds(3)
+        );
+
+        $worker = $this->getWorker(['default' => [$job]]);
+        $worker->runNextJob('sync', 'default');
+
+        $this->assertNull($job->releaseAfter);
+        $this->assertTrue($job->deleted);
+        $this->assertInstanceOf(MaxAttemptsExceededException::class, $job->failedWith);
+        $this->handle->shouldHaveReceived('report')->with(m::type(MaxAttemptsExceededException::class));
+        $this->event->shouldHaveReceived('trigger')->with(m::type(JobExceptionOccurred::class))->once();
+        $this->event->shouldHaveReceived('trigger')->with(m::type(JobFailed::class))->once();
+        $this->event->shouldNotHaveReceived('trigger', [m::type(JobProcessed::class)]);
+    }
+
+    public function testJobBasedMaxRetries()
+    {
+        $job = new WorkerFakeJob(function ($job) {
+            $job->attempts++;
+        });
+
+        $job->attempts = 2;
+
+        $job->maxTries = 10;
+
+        $worker = $this->getWorker(['default' => [$job]]);
+        $worker->runNextJob('sync', 'default', 0, 3, 1);
+
+        $this->assertFalse($job->deleted);
+        $this->assertNull($job->failedWith);
+    }
+
+    protected function getWorker($jobs)
+    {
+        $sync = m::mock(Sync::class);
+
+        $sync->shouldReceive('pop')->andReturnUsing(function ($queue) use (&$jobs) {
+            return array_shift($jobs[$queue]);
+        });
+
+        $this->queue->shouldReceive('driver')->with('sync')->andReturn($sync);
+
+        return new Worker($this->queue, $this->event, $this->handle, $this->cache);
+    }
+}
+
+class WorkerFakeConnector
+{
+    public $jobs = [];
+
+    public function __construct($jobs)
+    {
+        $this->jobs = $jobs;
+    }
+
+    public function pop($queue)
+    {
+        return array_shift($this->jobs[$queue]);
+    }
+}
+
+class Worker extends \think\queue\Worker
+{
+    public $sleptFor;
+
+    public $stoppedWithStatus;
+
+    public function sleep($seconds)
+    {
+        $this->sleptFor = $seconds;
+    }
+
+    public function stop($status = 0)
+    {
+        $this->stoppedWithStatus = $status;
+
+        throw new LoopBreakerException;
+    }
+
+    protected function stopIfNecessary($job, $lastRestart, $memory)
+    {
+        if (is_null($job)) {
+            $this->stop();
+        } else {
+            parent::stopIfNecessary($job, $lastRestart, $memory);
+        }
+    }
+}
+
+class WorkerFakeJob
+{
+
+    public $fired    = false;
+    public $callback;
+    public $deleted  = false;
+    public $releaseAfter;
+    public $released = false;
+    public $maxTries;
+    public $timeoutAt;
+    public $attempts = 0;
+    public $failedWith;
+    public $failed   = false;
+    public $connectionName;
+
+    public function __construct($callback = null)
+    {
+        $this->callback = $callback ?: function () {
+            //
+        };
+    }
+
+    public function fire()
+    {
+        $this->fired = true;
+        $this->callback->__invoke($this);
+    }
+
+    public function payload()
+    {
+        return [];
+    }
+
+    public function maxTries()
+    {
+        return $this->maxTries;
+    }
+
+    public function timeoutAt()
+    {
+        return $this->timeoutAt;
+    }
+
+    public function delete()
+    {
+        $this->deleted = true;
+    }
+
+    public function isDeleted()
+    {
+        return $this->deleted;
+    }
+
+    public function release($delay)
+    {
+        $this->released = true;
+
+        $this->releaseAfter = $delay;
+    }
+
+    public function isReleased()
+    {
+        return $this->released;
+    }
+
+    public function attempts()
+    {
+        return $this->attempts;
+    }
+
+    public function markAsFailed()
+    {
+        $this->failed = true;
+    }
+
+    public function failed($e)
+    {
+        $this->markAsFailed();
+
+        $this->failedWith = $e;
+    }
+
+    public function hasFailed()
+    {
+        return $this->failed;
+    }
+
+    public function timeout()
+    {
+        return time() + 60;
+    }
+
+    public function getName()
+    {
+        return 'WorkerFakeJob';
+    }
+}
+
+class LoopBreakerException extends RuntimeException
+{
+    //
+}
diff --git a/vendor/topthink/think-queue/tests/bootstrap.php b/vendor/topthink/think-queue/tests/bootstrap.php
new file mode 100755
index 0000000..401013c
--- /dev/null
+++ b/vendor/topthink/think-queue/tests/bootstrap.php
@@ -0,0 +1,3 @@
+<?php
+
+include __DIR__.'/../vendor/autoload.php';
diff --git a/vendor/topthink/think-template/.gitignore b/vendor/topthink/think-template/.gitignore
new file mode 100755
index 0000000..485dee6
--- /dev/null
+++ b/vendor/topthink/think-template/.gitignore
@@ -0,0 +1 @@
+.idea
diff --git a/vendor/topthink/think-template/LICENSE b/vendor/topthink/think-template/LICENSE
new file mode 100755
index 0000000..8dada3e
--- /dev/null
+++ b/vendor/topthink/think-template/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/vendor/topthink/think-template/README.md b/vendor/topthink/think-template/README.md
new file mode 100755
index 0000000..1d19064
--- /dev/null
+++ b/vendor/topthink/think-template/README.md
@@ -0,0 +1,70 @@
+# ThinkTemplate
+
+基于XML和标签库的编译型模板引擎
+
+## 主要特性
+
+- 支持XML标签库和普通标签的混合定义;
+- 支持直接使用PHP代码书写;
+- 支持文件包含;
+- 支持多级标签嵌套;
+- 支持布局模板功能;
+- 一次编译多次运行,编译和运行效率非常高;
+- 模板文件和布局模板更新,自动更新模板缓存;
+- 系统变量无需赋值直接输出;
+- 支持多维数组的快速输出;
+- 支持模板变量的默认值;
+- 支持页面代码去除Html空白;
+- 支持变量组合调节器和格式化功能;
+- 允许定义模板禁用函数和禁用PHP语法;
+- 通过标签库方式扩展;
+
+## 安装
+
+~~~php
+composer require topthink/think-template
+~~~
+
+## 用法示例
+
+
+~~~php
+<?php
+namespace think;
+
+require __DIR__.'/vendor/autoload.php';
+
+// 设置模板引擎参数
+$config = [
+	'view_path'	=>	'./template/',
+	'cache_path'	=>	'./runtime/',
+	'view_suffix'   =>	'html',
+];
+
+$template = new Template($config);
+// 模板变量赋值
+$template->assign(['name' => 'think']);
+// 读取模板文件渲染输出
+$template->fetch('index');
+// 完整模板文件渲染
+$template->fetch('./template/test.php');
+// 渲染内容输出
+$template->display($content);
+~~~
+
+支持静态调用
+
+~~~
+use think\facade\Template;
+
+Template::config([
+	'view_path'	=>	'./template/',
+	'cache_path'	=>	'./runtime/',
+	'view_suffix'   =>	'html',
+]);
+Template::assign(['name' => 'think']);
+Template::fetch('index',['name' => 'think']);
+Template::display($content,['name' => 'think']);
+~~~
+
+详细用法参考[开发手册](https://www.kancloud.cn/manual/think-template/content)
\ No newline at end of file
diff --git a/vendor/topthink/think-template/composer.json b/vendor/topthink/think-template/composer.json
new file mode 100755
index 0000000..f4e1205
--- /dev/null
+++ b/vendor/topthink/think-template/composer.json
@@ -0,0 +1,20 @@
+{
+    "name": "topthink/think-template",
+    "description": "the php template engine",
+    "license": "Apache-2.0",
+    "authors": [
+        {
+            "name": "liu21st",
+            "email": "liu21st@gmail.com"
+        }
+    ],
+    "require": {
+	   "php": ">=7.1.0",
+       "psr/simple-cache": "^1.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "think\\": "src"
+        }
+    }
+}
\ No newline at end of file
diff --git a/vendor/topthink/think-template/src/Template.php b/vendor/topthink/think-template/src/Template.php
new file mode 100755
index 0000000..84d35a5
--- /dev/null
+++ b/vendor/topthink/think-template/src/Template.php
@@ -0,0 +1,1320 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think;
+
+use Exception;
+use Psr\SimpleCache\CacheInterface;
+
+/**
+ * ThinkPHP分离出来的模板引擎
+ * 支持XML标签和普通标签的模板解析
+ * 编译型模板引擎 支持动态缓存
+ */
+class Template
+{
+    /**
+     * 模板变量
+     * @var array
+     */
+    protected $data = [];
+
+    /**
+     * 模板配置参数
+     * @var array
+     */
+    protected $config = [
+        'view_path'          => '', // 模板路径
+        'view_suffix'        => 'html', // 默认模板文件后缀
+        'view_depr'          => DIRECTORY_SEPARATOR,
+        'cache_path'         => '',
+        'cache_suffix'       => 'php', // 默认模板缓存后缀
+        'tpl_deny_func_list' => 'echo,exit', // 模板引擎禁用函数
+        'tpl_deny_php'       => false, // 默认模板引擎是否禁用PHP原生代码
+        'tpl_begin'          => '{', // 模板引擎普通标签开始标记
+        'tpl_end'            => '}', // 模板引擎普通标签结束标记
+        'strip_space'        => false, // 是否去除模板文件里面的html空格与换行
+        'tpl_cache'          => true, // 是否开启模板编译缓存,设为false则每次都会重新编译
+        'compile_type'       => 'file', // 模板编译类型
+        'cache_prefix'       => '', // 模板缓存前缀标识,可以动态改变
+        'cache_time'         => 0, // 模板缓存有效期 0 为永久,(以数字为值,单位:秒)
+        'layout_on'          => false, // 布局模板开关
+        'layout_name'        => 'layout', // 布局模板入口文件
+        'layout_item'        => '{__CONTENT__}', // 布局模板的内容替换标识
+        'taglib_begin'       => '{', // 标签库标签开始标记
+        'taglib_end'         => '}', // 标签库标签结束标记
+        'taglib_load'        => true, // 是否使用内置标签库之外的其它标签库,默认自动检测
+        'taglib_build_in'    => 'cx', // 内置标签库名称(标签使用不必指定标签库名称),以逗号分隔 注意解析顺序
+        'taglib_pre_load'    => '', // 需要额外加载的标签库(须指定标签库名称),多个以逗号分隔
+        'display_cache'      => false, // 模板渲染缓存
+        'cache_id'           => '', // 模板缓存ID
+        'tpl_replace_string' => [],
+        'tpl_var_identify'   => 'array', // .语法变量识别,array|object|'', 为空时自动识别
+        'default_filter'     => 'htmlentities', // 默认过滤方法 用于普通标签输出
+    ];
+
+    /**
+     * 保留内容信息
+     * @var array
+     */
+    private $literal = [];
+
+    /**
+     * 扩展解析规则
+     * @var array
+     */
+    private $extend = [];
+
+    /**
+     * 模板包含信息
+     * @var array
+     */
+    private $includeFile = [];
+
+    /**
+     * 模板存储对象
+     * @var object
+     */
+    protected $storage;
+
+    /**
+     * 查询缓存对象
+     * @var CacheInterface
+     */
+    protected $cache;
+
+    /**
+     * 架构函数
+     * @access public
+     * @param  array $config
+     */
+    public function __construct(array $config = [])
+    {
+        $this->config = array_merge($this->config, $config);
+
+        $this->config['taglib_begin_origin'] = $this->config['taglib_begin'];
+        $this->config['taglib_end_origin']   = $this->config['taglib_end'];
+
+        $this->config['taglib_begin'] = preg_quote($this->config['taglib_begin'], '/');
+        $this->config['taglib_end']   = preg_quote($this->config['taglib_end'], '/');
+        $this->config['tpl_begin']    = preg_quote($this->config['tpl_begin'], '/');
+        $this->config['tpl_end']      = preg_quote($this->config['tpl_end'], '/');
+
+        // 初始化模板编译存储器
+        $type  = $this->config['compile_type'] ? $this->config['compile_type'] : 'File';
+        $class = false !== strpos($type, '\\') ? $type : '\\think\\template\\driver\\' . ucwords($type);
+
+        $this->storage = new $class();
+    }
+
+    /**
+     * 模板变量赋值
+     * @access public
+     * @param  array $vars 模板变量
+     * @return $this
+     */
+    public function assign(array $vars = [])
+    {
+        $this->data = array_merge($this->data, $vars);
+        return $this;
+    }
+
+    /**
+     * 模板引擎参数赋值
+     * @access public
+     * @param  string $name
+     * @param  mixed  $value
+     */
+    public function __set($name, $value)
+    {
+        $this->config[$name] = $value;
+    }
+
+    /**
+     * 设置缓存对象
+     * @access public
+     * @param  CacheInterface $cache 缓存对象
+     * @return void
+     */
+    public function setCache(CacheInterface $cache): void
+    {
+        $this->cache = $cache;
+    }
+
+    /**
+     * 模板引擎配置
+     * @access public
+     * @param  array $config
+     * @return $this
+     */
+    public function config(array $config)
+    {
+        $this->config = array_merge($this->config, $config);
+        return $this;
+    }
+
+    /**
+     * 获取模板引擎配置项
+     * @access public
+     * @param  string $name
+     * @return mixed
+     */
+    public function getConfig(string $name)
+    {
+        return $this->config[$name] ?? null;
+    }
+
+    /**
+     * 模板变量获取
+     * @access public
+     * @param  string $name 变量名
+     * @return mixed
+     */
+    public function get(string $name = '')
+    {
+        if ('' == $name) {
+            return $this->data;
+        }
+
+        $data = $this->data;
+
+        foreach (explode('.', $name) as $key => $val) {
+            if (isset($data[$val])) {
+                $data = $data[$val];
+            } else {
+                $data = null;
+                break;
+            }
+        }
+
+        return $data;
+    }
+
+    /**
+     * 扩展模板解析规则
+     * @access public
+     * @param  string   $rule 解析规则
+     * @param  callable $callback 解析规则
+     * @return void
+     */
+    public function extend(string $rule, callable $callback = null): void
+    {
+        $this->extend[$rule] = $callback;
+    }
+
+    /**
+     * 渲染模板文件
+     * @access public
+     * @param  string $template 模板文件
+     * @param  array  $vars 模板变量
+     * @return void
+     */
+    public function fetch(string $template, array $vars = []): void
+    {
+        if ($vars) {
+            $this->data = array_merge($this->data, $vars);
+        }
+
+        if (!empty($this->config['cache_id']) && $this->config['display_cache'] && $this->cache) {
+            // 读取渲染缓存
+            if ($this->cache->has($this->config['cache_id'])) {
+                echo $this->cache->get($this->config['cache_id']);
+                return;
+            }
+        }
+
+        $template = $this->parseTemplateFile($template);
+
+        if ($template) {
+            $cacheFile = $this->config['cache_path'] . $this->config['cache_prefix'] . md5($this->config['layout_on'] . $this->config['layout_name'] . $template) . '.' . ltrim($this->config['cache_suffix'], '.');
+
+            if (!$this->checkCache($cacheFile)) {
+                // 缓存无效 重新模板编译
+                $content = file_get_contents($template);
+                $this->compiler($content, $cacheFile);
+            }
+
+            // 页面缓存
+            ob_start();
+            ob_implicit_flush(0);
+
+            // 读取编译存储
+            $this->storage->read($cacheFile, $this->data);
+
+            // 获取并清空缓存
+            $content = ob_get_clean();
+
+            if (!empty($this->config['cache_id']) && $this->config['display_cache'] && $this->cache) {
+                // 缓存页面输出
+                $this->cache->set($this->config['cache_id'], $content, $this->config['cache_time']);
+            }
+
+            echo $content;
+        }
+    }
+
+    /**
+     * 检查编译缓存是否存在
+     * @access public
+     * @param  string $cacheId 缓存的id
+     * @return boolean
+     */
+    public function isCache(string $cacheId): bool
+    {
+        if ($cacheId && $this->cache && $this->config['display_cache']) {
+            // 缓存页面输出
+            return $this->cache->has($cacheId);
+        }
+
+        return false;
+    }
+
+    /**
+     * 渲染模板内容
+     * @access public
+     * @param  string $content 模板内容
+     * @param  array  $vars 模板变量
+     * @return void
+     */
+    public function display(string $content, array $vars = []): void
+    {
+        if ($vars) {
+            $this->data = array_merge($this->data, $vars);
+        }
+
+        $cacheFile = $this->config['cache_path'] . $this->config['cache_prefix'] . md5($content) . '.' . ltrim($this->config['cache_suffix'], '.');
+
+        if (!$this->checkCache($cacheFile)) {
+            // 缓存无效 模板编译
+            $this->compiler($content, $cacheFile);
+        }
+
+        // 读取编译存储
+        $this->storage->read($cacheFile, $this->data);
+    }
+
+    /**
+     * 设置布局
+     * @access public
+     * @param  mixed  $name 布局模板名称 false 则关闭布局
+     * @param  string $replace 布局模板内容替换标识
+     * @return $this
+     */
+    public function layout($name, string $replace = '')
+    {
+        if (false === $name) {
+            // 关闭布局
+            $this->config['layout_on'] = false;
+        } else {
+            // 开启布局
+            $this->config['layout_on'] = true;
+
+            // 名称必须为字符串
+            if (is_string($name)) {
+                $this->config['layout_name'] = $name;
+            }
+
+            if (!empty($replace)) {
+                $this->config['layout_item'] = $replace;
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * 检查编译缓存是否有效
+     * 如果无效则需要重新编译
+     * @access private
+     * @param  string $cacheFile 缓存文件名
+     * @return bool
+     */
+    private function checkCache(string $cacheFile): bool
+    {
+        if (!$this->config['tpl_cache'] || !is_file($cacheFile) || !$handle = @fopen($cacheFile, "r")) {
+            return false;
+        }
+
+        // 读取第一行
+        preg_match('/\/\*(.+?)\*\//', fgets($handle), $matches);
+
+        if (!isset($matches[1])) {
+            return false;
+        }
+
+        $includeFile = unserialize($matches[1]);
+
+        if (!is_array($includeFile)) {
+            return false;
+        }
+
+        // 检查模板文件是否有更新
+        foreach ($includeFile as $path => $time) {
+            if (is_file($path) && filemtime($path) > $time) {
+                // 模板文件如果有更新则缓存需要更新
+                return false;
+            }
+        }
+
+        // 检查编译存储是否有效
+        return $this->storage->check($cacheFile, $this->config['cache_time']);
+    }
+
+    /**
+     * 编译模板文件内容
+     * @access private
+     * @param  string $content 模板内容
+     * @param  string $cacheFile 缓存文件名
+     * @return void
+     */
+    private function compiler(string &$content, string $cacheFile): void
+    {
+        // 判断是否启用布局
+        if ($this->config['layout_on']) {
+            if (false !== strpos($content, '{__NOLAYOUT__}')) {
+                // 可以单独定义不使用布局
+                $content = str_replace('{__NOLAYOUT__}', '', $content);
+            } else {
+                // 读取布局模板
+                $layoutFile = $this->parseTemplateFile($this->config['layout_name']);
+
+                if ($layoutFile) {
+                    // 替换布局的主体内容
+                    $content = str_replace($this->config['layout_item'], $content, file_get_contents($layoutFile));
+                }
+            }
+        } else {
+            $content = str_replace('{__NOLAYOUT__}', '', $content);
+        }
+
+        // 模板解析
+        $this->parse($content);
+
+        if ($this->config['strip_space']) {
+            /* 去除html空格与换行 */
+            $find    = ['~>\s+<~', '~>(\s+\n|\r)~'];
+            $replace = ['><', '>'];
+            $content = preg_replace($find, $replace, $content);
+        }
+
+        // 优化生成的php代码
+        $content = preg_replace('/\?>\s*<\?php\s(?!echo\b|\bend)/s', '', $content);
+
+        // 模板过滤输出
+        $replace = $this->config['tpl_replace_string'];
+        $content = str_replace(array_keys($replace), array_values($replace), $content);
+
+        // 添加安全代码及模板引用记录
+        $content = '<?php /*' . serialize($this->includeFile) . '*/ ?>' . "\n" . $content;
+        // 编译存储
+        $this->storage->write($cacheFile, $content);
+
+        $this->includeFile = [];
+    }
+
+    /**
+     * 模板解析入口
+     * 支持普通标签和TagLib解析 支持自定义标签库
+     * @access public
+     * @param  string $content 要解析的模板内容
+     * @return void
+     */
+    public function parse(string &$content): void
+    {
+        // 内容为空不解析
+        if (empty($content)) {
+            return;
+        }
+
+        // 替换literal标签内容
+        $this->parseLiteral($content);
+
+        // 解析继承
+        $this->parseExtend($content);
+
+        // 解析布局
+        $this->parseLayout($content);
+
+        // 检查include语法
+        $this->parseInclude($content);
+
+        // 替换包含文件中literal标签内容
+        $this->parseLiteral($content);
+
+        // 检查PHP语法
+        $this->parsePhp($content);
+
+        // 获取需要引入的标签库列表
+        // 标签库只需要定义一次,允许引入多个一次
+        // 一般放在文件的最前面
+        // 格式:<taglib name="html,mytag..." />
+        // 当TAGLIB_LOAD配置为true时才会进行检测
+        if ($this->config['taglib_load']) {
+            $tagLibs = $this->getIncludeTagLib($content);
+
+            if (!empty($tagLibs)) {
+                // 对导入的TagLib进行解析
+                foreach ($tagLibs as $tagLibName) {
+                    $this->parseTagLib($tagLibName, $content);
+                }
+            }
+        }
+
+        // 预先加载的标签库 无需在每个模板中使用taglib标签加载 但必须使用标签库XML前缀
+        if ($this->config['taglib_pre_load']) {
+            $tagLibs = explode(',', $this->config['taglib_pre_load']);
+
+            foreach ($tagLibs as $tag) {
+                $this->parseTagLib($tag, $content);
+            }
+        }
+
+        // 内置标签库 无需使用taglib标签导入就可以使用 并且不需使用标签库XML前缀
+        $tagLibs = explode(',', $this->config['taglib_build_in']);
+
+        foreach ($tagLibs as $tag) {
+            $this->parseTagLib($tag, $content, true);
+        }
+
+        // 解析普通模板标签 {$tagName}
+        $this->parseTag($content);
+
+        // 还原被替换的Literal标签
+        $this->parseLiteral($content, true);
+    }
+
+    /**
+     * 检查PHP语法
+     * @access private
+     * @param  string $content 要解析的模板内容
+     * @return void
+     * @throws Exception
+     */
+    private function parsePhp(string &$content): void
+    {
+        // 短标签的情况要将<?标签用echo方式输出 否则无法正常输出xml标识
+        $content = preg_replace('/(<\?(?!php|=|$))/i', '<?php echo \'\\1\'; ?>' . "\n", $content);
+
+        // PHP语法检查
+        if ($this->config['tpl_deny_php'] && false !== strpos($content, '<?php')) {
+            throw new Exception('not allow php tag');
+        }
+    }
+
+    /**
+     * 解析模板中的布局标签
+     * @access private
+     * @param  string $content 要解析的模板内容
+     * @return void
+     */
+    private function parseLayout(string &$content): void
+    {
+        // 读取模板中的布局标签
+        if (preg_match($this->getRegex('layout'), $content, $matches)) {
+            // 替换Layout标签
+            $content = str_replace($matches[0], '', $content);
+            // 解析Layout标签
+            $array = $this->parseAttr($matches[0]);
+
+            if (!$this->config['layout_on'] || $this->config['layout_name'] != $array['name']) {
+                // 读取布局模板
+                $layoutFile = $this->parseTemplateFile($array['name']);
+
+                if ($layoutFile) {
+                    $replace = isset($array['replace']) ? $array['replace'] : $this->config['layout_item'];
+                    // 替换布局的主体内容
+                    $content = str_replace($replace, $content, file_get_contents($layoutFile));
+                }
+            }
+        } else {
+            $content = str_replace('{__NOLAYOUT__}', '', $content);
+        }
+    }
+
+    /**
+     * 解析模板中的include标签
+     * @access private
+     * @param  string $content 要解析的模板内容
+     * @return void
+     */
+    private function parseInclude(string &$content): void
+    {
+        $regex = $this->getRegex('include');
+        $func  = function ($template) use (&$func, &$regex, &$content) {
+            if (preg_match_all($regex, $template, $matches, PREG_SET_ORDER)) {
+                foreach ($matches as $match) {
+                    $array = $this->parseAttr($match[0]);
+                    $file  = $array['file'];
+                    unset($array['file']);
+
+                    // 分析模板文件名并读取内容
+                    $parseStr = $this->parseTemplateName($file);
+
+                    foreach ($array as $k => $v) {
+                        // 以$开头字符串转换成模板变量
+                        if (0 === strpos($v, '$')) {
+                            $v = $this->get(substr($v, 1));
+                        }
+
+                        $parseStr = str_replace('[' . $k . ']', $v, $parseStr);
+                    }
+
+                    $content = str_replace($match[0], $parseStr, $content);
+                    // 再次对包含文件进行模板分析
+                    $func($parseStr);
+                }
+                unset($matches);
+            }
+        };
+
+        // 替换模板中的include标签
+        $func($content);
+    }
+
+    /**
+     * 解析模板中的extend标签
+     * @access private
+     * @param  string $content 要解析的模板内容
+     * @return void
+     */
+    private function parseExtend(string &$content): void
+    {
+        $regex  = $this->getRegex('extend');
+        $array  = $blocks  = $baseBlocks  = [];
+        $extend = '';
+
+        $func = function ($template) use (&$func, &$regex, &$array, &$extend, &$blocks, &$baseBlocks) {
+            if (preg_match($regex, $template, $matches)) {
+                if (!isset($array[$matches['name']])) {
+                    $array[$matches['name']] = 1;
+                    // 读取继承模板
+                    $extend = $this->parseTemplateName($matches['name']);
+
+                    // 递归检查继承
+                    $func($extend);
+
+                    // 取得block标签内容
+                    $blocks = array_merge($blocks, $this->parseBlock($template));
+
+                    return;
+                }
+            } else {
+                // 取得顶层模板block标签内容
+                $baseBlocks = $this->parseBlock($template, true);
+
+                if (empty($extend)) {
+                    // 无extend标签但有block标签的情况
+                    $extend = $template;
+                }
+            }
+        };
+
+        $func($content);
+
+        if (!empty($extend)) {
+            if ($baseBlocks) {
+                $children = [];
+                foreach ($baseBlocks as $name => $val) {
+                    $replace = $val['content'];
+
+                    if (!empty($children[$name])) {
+                        // 如果包含有子block标签
+                        foreach ($children[$name] as $key) {
+                            $replace = str_replace($baseBlocks[$key]['begin'] . $baseBlocks[$key]['content'] . $baseBlocks[$key]['end'], $blocks[$key]['content'], $replace);
+                        }
+                    }
+
+                    if (isset($blocks[$name])) {
+                        // 带有{__block__}表示与所继承模板的相应标签合并,而不是覆盖
+                        $replace = str_replace(['{__BLOCK__}', '{__block__}'], $replace, $blocks[$name]['content']);
+
+                        if (!empty($val['parent'])) {
+                            // 如果不是最顶层的block标签
+                            $parent = $val['parent'];
+
+                            if (isset($blocks[$parent])) {
+                                $blocks[$parent]['content'] = str_replace($blocks[$name]['begin'] . $blocks[$name]['content'] . $blocks[$name]['end'], $replace, $blocks[$parent]['content']);
+                            }
+
+                            $blocks[$name]['content'] = $replace;
+                            $children[$parent][]      = $name;
+
+                            continue;
+                        }
+                    } elseif (!empty($val['parent'])) {
+                        // 如果子标签没有被继承则用原值
+                        $children[$val['parent']][] = $name;
+                        $blocks[$name]              = $val;
+                    }
+
+                    if (!$val['parent']) {
+                        // 替换模板中的顶级block标签
+                        $extend = str_replace($val['begin'] . $val['content'] . $val['end'], $replace, $extend);
+                    }
+                }
+            }
+
+            $content = $extend;
+            unset($blocks, $baseBlocks);
+        }
+    }
+
+    /**
+     * 替换页面中的literal标签
+     * @access private
+     * @param  string   $content 模板内容
+     * @param  boolean  $restore 是否为还原
+     * @return void
+     */
+    private function parseLiteral(string &$content, bool $restore = false): void
+    {
+        $regex = $this->getRegex($restore ? 'restoreliteral' : 'literal');
+
+        if (preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) {
+            if (!$restore) {
+                $count = count($this->literal);
+
+                // 替换literal标签
+                foreach ($matches as $match) {
+                    $this->literal[] = substr($match[0], strlen($match[1]), -strlen($match[2]));
+                    $content         = str_replace($match[0], "<!--###literal{$count}###-->", $content);
+                    $count++;
+                }
+            } else {
+                // 还原literal标签
+                foreach ($matches as $match) {
+                    $content = str_replace($match[0], $this->literal[$match[1]], $content);
+                }
+
+                // 清空literal记录
+                $this->literal = [];
+            }
+
+            unset($matches);
+        }
+    }
+
+    /**
+     * 获取模板中的block标签
+     * @access private
+     * @param  string   $content 模板内容
+     * @param  boolean  $sort 是否排序
+     * @return array
+     */
+    private function parseBlock(string &$content, bool $sort = false): array
+    {
+        $regex  = $this->getRegex('block');
+        $result = [];
+
+        if (preg_match_all($regex, $content, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) {
+            $right = $keys = [];
+
+            foreach ($matches as $match) {
+                if (empty($match['name'][0])) {
+                    if (count($right) > 0) {
+                        $tag    = array_pop($right);
+                        $start  = $tag['offset'] + strlen($tag['tag']);
+                        $length = $match[0][1] - $start;
+
+                        $result[$tag['name']] = [
+                            'begin'   => $tag['tag'],
+                            'content' => substr($content, $start, $length),
+                            'end'     => $match[0][0],
+                            'parent'  => count($right) ? end($right)['name'] : '',
+                        ];
+
+                        $keys[$tag['name']] = $match[0][1];
+                    }
+                } else {
+                    // 标签头压入栈
+                    $right[] = [
+                        'name'   => $match[2][0],
+                        'offset' => $match[0][1],
+                        'tag'    => $match[0][0],
+                    ];
+                }
+            }
+
+            unset($right, $matches);
+
+            if ($sort) {
+                // 按block标签结束符在模板中的位置排序
+                array_multisort($keys, $result);
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * 搜索模板页面中包含的TagLib库
+     * 并返回列表
+     * @access private
+     * @param  string $content 模板内容
+     * @return array|null
+     */
+    private function getIncludeTagLib(string &$content)
+    {
+        // 搜索是否有TagLib标签
+        if (preg_match($this->getRegex('taglib'), $content, $matches)) {
+            // 替换TagLib标签
+            $content = str_replace($matches[0], '', $content);
+
+            return explode(',', $matches['name']);
+        }
+    }
+
+    /**
+     * TagLib库解析
+     * @access public
+     * @param  string   $tagLib 要解析的标签库
+     * @param  string   $content 要解析的模板内容
+     * @param  boolean  $hide 是否隐藏标签库前缀
+     * @return void
+     */
+    public function parseTagLib(string $tagLib, string &$content, bool $hide = false): void
+    {
+        if (false !== strpos($tagLib, '\\')) {
+            // 支持指定标签库的命名空间
+            $className = $tagLib;
+            $tagLib    = substr($tagLib, strrpos($tagLib, '\\') + 1);
+        } else {
+            $className = '\\think\\template\\taglib\\' . ucwords($tagLib);
+        }
+
+        $tLib = new $className($this);
+
+        $tLib->parseTag($content, $hide ? '' : $tagLib);
+    }
+
+    /**
+     * 分析标签属性
+     * @access public
+     * @param  string   $str 属性字符串
+     * @param  string   $name 不为空时返回指定的属性名
+     * @return array
+     */
+    public function parseAttr(string $str, string $name = null): array
+    {
+        $regex = '/\s+(?>(?P<name>[\w-]+)\s*)=(?>\s*)([\"\'])(?P<value>(?:(?!\\2).)*)\\2/is';
+        $array = [];
+
+        if (preg_match_all($regex, $str, $matches, PREG_SET_ORDER)) {
+            foreach ($matches as $match) {
+                $array[$match['name']] = $match['value'];
+            }
+            unset($matches);
+        }
+
+        if (!empty($name) && isset($array[$name])) {
+            return $array[$name];
+        }
+
+        return $array;
+    }
+
+    /**
+     * 模板标签解析
+     * 格式: {TagName:args [|content] }
+     * @access private
+     * @param  string $content 要解析的模板内容
+     * @return void
+     */
+    private function parseTag(string &$content): void
+    {
+        $regex = $this->getRegex('tag');
+
+        if (preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) {
+            foreach ($matches as $match) {
+                $str  = stripslashes($match[1]);
+                $flag = substr($str, 0, 1);
+
+                switch ($flag) {
+                    case '$':
+                        // 解析模板变量 格式 {$varName}
+                        // 是否带有?号
+                        if (false !== $pos = strpos($str, '?')) {
+                            $array = preg_split('/([!=]={1,2}|(?<!-)[><]={0,1})/', substr($str, 0, $pos), 2, PREG_SPLIT_DELIM_CAPTURE);
+                            $name  = $array[0];
+
+                            $this->parseVar($name);
+                            //$this->parseVarFunction($name);
+
+                            $str = trim(substr($str, $pos + 1));
+                            $this->parseVar($str);
+                            $first = substr($str, 0, 1);
+
+                            if (strpos($name, ')')) {
+                                // $name为对象或是自动识别,或者含有函数
+                                if (isset($array[1])) {
+                                    $this->parseVar($array[2]);
+                                    $name .= $array[1] . $array[2];
+                                }
+
+                                switch ($first) {
+                                    case '?':
+                                        $this->parseVarFunction($name);
+                                        $str = '<?php echo (' . $name . ') ? ' . $name . ' : ' . substr($str, 1) . '; ?>';
+                                        break;
+                                    case '=':
+                                        $str = '<?php if(' . $name . ') echo ' . substr($str, 1) . '; ?>';
+                                        break;
+                                    default:
+                                        $str = '<?php echo ' . $name . '?' . $str . '; ?>';
+                                }
+                            } else {
+                                if (isset($array[1])) {
+                                    $express = true;
+                                    $this->parseVar($array[2]);
+                                    $express = $name . $array[1] . $array[2];
+                                } else {
+                                    $express = false;
+                                }
+
+                                if (in_array($first, ['?', '=', ':'])) {
+                                    $str = trim(substr($str, 1));
+                                    if ('$' == substr($str, 0, 1)) {
+                                        $str = $this->parseVarFunction($str);
+                                    }
+                                }
+
+                                // $name为数组
+                                switch ($first) {
+                                    case '?':
+                                        // {$varname??'xxx'} $varname有定义则输出$varname,否则输出xxx
+                                        $str = '<?php echo ' . ($express ?: 'isset(' . $name . ')') . ' ? ' . $this->parseVarFunction($name) . ' : ' . $str . '; ?>';
+                                        break;
+                                    case '=':
+                                        // {$varname?='xxx'} $varname为真时才输出xxx
+                                        $str = '<?php if(' . ($express ?: '!empty(' . $name . ')') . ') echo ' . $str . '; ?>';
+                                        break;
+                                    case ':':
+                                        // {$varname?:'xxx'} $varname为真时输出$varname,否则输出xxx
+                                        $str = '<?php echo ' . ($express ?: '!empty(' . $name . ')') . ' ? ' . $this->parseVarFunction($name) . ' : ' . $str . '; ?>';
+                                        break;
+                                    default:
+                                        if (strpos($str, ':')) {
+                                            // {$varname ? 'a' : 'b'} $varname为真时输出a,否则输出b
+                                            $array = explode(':', $str, 2);
+
+                                            $array[0] = '$' == substr(trim($array[0]), 0, 1) ? $this->parseVarFunction($array[0]) : $array[0];
+                                            $array[1] = '$' == substr(trim($array[1]), 0, 1) ? $this->parseVarFunction($array[1]) : $array[1];
+
+                                            $str = implode(' : ', $array);
+                                        }
+                                        $str = '<?php echo ' . ($express ?: '!empty(' . $name . ')') . ' ? ' . $str . '; ?>';
+                                }
+                            }
+                        } else {
+                            $this->parseVar($str);
+                            $this->parseVarFunction($str);
+                            $str = '<?php echo ' . $str . '; ?>';
+                        }
+                        break;
+                    case ':':
+                        // 输出某个函数的结果
+                        $str = substr($str, 1);
+                        $this->parseVar($str);
+                        $str = '<?php echo ' . $str . '; ?>';
+                        break;
+                    case '~':
+                        // 执行某个函数
+                        $str = substr($str, 1);
+                        $this->parseVar($str);
+                        $str = '<?php ' . $str . '; ?>';
+                        break;
+                    case '-':
+                    case '+':
+                        // 输出计算
+                        $this->parseVar($str);
+                        $str = '<?php echo ' . $str . '; ?>';
+                        break;
+                    case '/':
+                        // 注释标签
+                        $flag2 = substr($str, 1, 1);
+                        if ('/' == $flag2 || ('*' == $flag2 && substr(rtrim($str), -2) == '*/')) {
+                            $str = '';
+                        }
+                        break;
+                    default:
+                        // 未识别的标签直接返回
+                        $str = $this->config['tpl_begin'] . $str . $this->config['tpl_end'];
+                        break;
+                }
+
+                $content = str_replace($match[0], $str, $content);
+            }
+
+            unset($matches);
+        }
+    }
+
+    /**
+     * 模板变量解析,支持使用函数
+     * 格式: {$varname|function1|function2=arg1,arg2}
+     * @access public
+     * @param  string $varStr 变量数据
+     * @return void
+     */
+    public function parseVar(string &$varStr): void
+    {
+        $varStr = trim($varStr);
+
+        if (preg_match_all('/\$[a-zA-Z_](?>\w*)(?:[:\.][0-9a-zA-Z_](?>\w*))+/', $varStr, $matches, PREG_OFFSET_CAPTURE)) {
+            static $_varParseList = [];
+
+            while ($matches[0]) {
+                $match = array_pop($matches[0]);
+
+                //如果已经解析过该变量字串,则直接返回变量值
+                if (isset($_varParseList[$match[0]])) {
+                    $parseStr = $_varParseList[$match[0]];
+                } else {
+                    if (strpos($match[0], '.')) {
+                        $vars  = explode('.', $match[0]);
+                        $first = array_shift($vars);
+
+                        if (isset($this->extend[$first])) {
+                            $callback = $this->extend[$first];
+                            $parseStr = $callback($vars);
+                        } elseif ('$Request' == $first) {
+                            // 输出请求变量
+                            $parseStr = $this->parseRequestVar($vars);
+                        } elseif ('$Think' == $first) {
+                            // 所有以Think.打头的以特殊变量对待 无需模板赋值就可以输出
+                            $parseStr = $this->parseThinkVar($vars);
+                        } else {
+                            switch ($this->config['tpl_var_identify']) {
+                                case 'array': // 识别为数组
+                                    $parseStr = $first . '[\'' . implode('\'][\'', $vars) . '\']';
+                                    break;
+                                case 'obj': // 识别为对象
+                                    $parseStr = $first . '->' . implode('->', $vars);
+                                    break;
+                                default: // 自动判断数组或对象
+                                    $parseStr = '(is_array(' . $first . ')?' . $first . '[\'' . implode('\'][\'', $vars) . '\']:' . $first . '->' . implode('->', $vars) . ')';
+                            }
+                        }
+                    } else {
+                        $parseStr = str_replace(':', '->', $match[0]);
+                    }
+
+                    $_varParseList[$match[0]] = $parseStr;
+                }
+
+                $varStr = substr_replace($varStr, $parseStr, $match[1], strlen($match[0]));
+            }
+            unset($matches);
+        }
+    }
+
+    /**
+     * 对模板中使用了函数的变量进行解析
+     * 格式 {$varname|function1|function2=arg1,arg2}
+     * @access public
+     * @param  string $varStr     变量字符串
+     * @param  bool   $autoescape 自动转义
+     * @return string
+     */
+    public function parseVarFunction(string &$varStr, bool $autoescape = true): string
+    {
+        if (!$autoescape && false === strpos($varStr, '|')) {
+            return $varStr;
+        } elseif ($autoescape && !preg_match('/\|(\s)?raw(\||\s)?/i', $varStr)) {
+            $varStr .= '|' . $this->config['default_filter'];
+        }
+
+        static $_varFunctionList = [];
+
+        $_key = md5($varStr);
+
+        //如果已经解析过该变量字串,则直接返回变量值
+        if (isset($_varFunctionList[$_key])) {
+            $varStr = $_varFunctionList[$_key];
+        } else {
+            $varArray = explode('|', $varStr);
+
+            // 取得变量名称
+            $name = trim(array_shift($varArray));
+
+            // 对变量使用函数
+            $length = count($varArray);
+
+            // 取得模板禁止使用函数列表
+            $template_deny_funs = explode(',', $this->config['tpl_deny_func_list']);
+
+            for ($i = 0; $i < $length; $i++) {
+                $args = explode('=', $varArray[$i], 2);
+
+                // 模板函数过滤
+                $fun = trim($args[0]);
+                if (in_array($fun, $template_deny_funs)) {
+                    continue;
+                }
+
+                switch (strtolower($fun)) {
+                    case 'raw':
+                        break;
+                    case 'date':
+                        $name = 'date(' . $args[1] . ',!is_numeric(' . $name . ')? strtotime(' . $name . ') : ' . $name . ')';
+                        break;
+                    case 'first':
+                        $name = 'current(' . $name . ')';
+                        break;
+                    case 'last':
+                        $name = 'end(' . $name . ')';
+                        break;
+                    case 'upper':
+                        $name = 'strtoupper(' . $name . ')';
+                        break;
+                    case 'lower':
+                        $name = 'strtolower(' . $name . ')';
+                        break;
+                    case 'format':
+                        $name = 'sprintf(' . $args[1] . ',' . $name . ')';
+                        break;
+                    case 'default': // 特殊模板函数
+                        if (false === strpos($name, '(')) {
+                            $name = '(isset(' . $name . ') && (' . $name . ' !== \'\')?' . $name . ':' . $args[1] . ')';
+                        } else {
+                            $name = '(' . $name . ' ?: ' . $args[1] . ')';
+                        }
+                        break;
+                    default: // 通用模板函数
+                        if (isset($args[1])) {
+                            if (strstr($args[1], '###')) {
+                                $args[1] = str_replace('###', $name, $args[1]);
+                                $name    = "$fun($args[1])";
+                            } else {
+                                $name = "$fun($name,$args[1])";
+                            }
+                        } else {
+                            if (!empty($args[0])) {
+                                $name = "$fun($name)";
+                            }
+                        }
+                }
+            }
+
+            $_varFunctionList[$_key] = $name;
+            $varStr                  = $name;
+        }
+        return $varStr;
+    }
+
+    /**
+     * 请求变量解析
+     * 格式 以 $Request. 打头的变量属于请求变量
+     * @access public
+     * @param  array $vars 变量数组
+     * @return string
+     */
+    public function parseRequestVar(array $vars): string
+    {
+        $type  = strtoupper(trim(array_shift($vars)));
+        $param = implode('.', $vars);
+
+        switch ($type) {
+            case 'SERVER':
+                $parseStr = '$_SERVER[\'' . $param . '\']';
+                break;
+            case 'GET':
+                $parseStr = '$_GET[\'' . $param . '\']';
+                break;
+            case 'POST':
+                $parseStr = '$_POST[\'' . $param . '\']';
+                break;
+            case 'COOKIE':
+                $parseStr = '$_COOKIE[\'' . $param . '\']';
+                break;
+            case 'SESSION':
+                $parseStr = '$_SESSION[\'' . $param . '\']';
+                break;
+            case 'ENV':
+                $parseStr = '$_ENV[\'' . $param . '\']';
+                break;
+            case 'REQUEST':
+                $parseStr = '$_REQUEST[\'' . $param . '\']';
+                break;
+            default:
+                $parseStr = '\'\'';
+        }
+
+        return $parseStr;
+    }
+
+    /**
+     * 特殊模板变量解析
+     * 格式 以 $Think. 打头的变量属于特殊模板变量
+     * @access public
+     * @param  array $vars 变量数组
+     * @return string
+     */
+    public function parseThinkVar(array $vars): string
+    {
+        $type  = strtoupper(trim(array_shift($vars)));
+        $param = implode('.', $vars);
+
+        switch ($type) {
+            case 'CONST':
+                $parseStr = strtoupper($param);
+                break;
+            case 'NOW':
+                $parseStr = "date('Y-m-d g:i a',time())";
+                break;
+            case 'LDELIM':
+                $parseStr = '\'' . ltrim($this->config['tpl_begin'], '\\') . '\'';
+                break;
+            case 'RDELIM':
+                $parseStr = '\'' . ltrim($this->config['tpl_end'], '\\') . '\'';
+                break;
+            default:
+                $parseStr = defined($type) ? $type : '\'\'';
+        }
+
+        return $parseStr;
+    }
+
+    /**
+     * 分析加载的模板文件并读取内容 支持多个模板文件读取
+     * @access private
+     * @param  string $templateName 模板文件名
+     * @return string
+     */
+    private function parseTemplateName(string $templateName): string
+    {
+        $array    = explode(',', $templateName);
+        $parseStr = '';
+
+        foreach ($array as $templateName) {
+            if (empty($templateName)) {
+                continue;
+            }
+
+            if (0 === strpos($templateName, '$')) {
+                //支持加载变量文件名
+                $templateName = $this->get(substr($templateName, 1));
+            }
+
+            $template = $this->parseTemplateFile($templateName);
+
+            if ($template) {
+                // 获取模板文件内容
+                $parseStr .= file_get_contents($template);
+            }
+        }
+
+        return $parseStr;
+    }
+
+    /**
+     * 解析模板文件名
+     * @access private
+     * @param  string $template 文件名
+     * @return string
+     */
+    private function parseTemplateFile(string $template): string
+    {
+        if ('' == pathinfo($template, PATHINFO_EXTENSION)) {
+
+            if (0 !== strpos($template, '/')) {
+                $template = str_replace(['/', ':'], $this->config['view_depr'], $template);
+            } else {
+                $template = str_replace(['/', ':'], $this->config['view_depr'], substr($template, 1));
+            }
+
+            $template = $this->config['view_path'] . $template . '.' . ltrim($this->config['view_suffix'], '.');
+        }
+
+        if (is_file($template)) {
+            // 记录模板文件的更新时间
+            $this->includeFile[$template] = filemtime($template);
+
+            return $template;
+        }
+
+        throw new Exception('template not exists:' . $template);
+    }
+
+    /**
+     * 按标签生成正则
+     * @access private
+     * @param  string $tagName 标签名
+     * @return string
+     */
+    private function getRegex(string $tagName): string
+    {
+        $regex = '';
+        if ('tag' == $tagName) {
+            $begin = $this->config['tpl_begin'];
+            $end   = $this->config['tpl_end'];
+
+            if (strlen(ltrim($begin, '\\')) == 1 && strlen(ltrim($end, '\\')) == 1) {
+                $regex = $begin . '((?:[\$]{1,2}[a-wA-w_]|[\:\~][\$a-wA-w_]|[+]{2}[\$][a-wA-w_]|[-]{2}[\$][a-wA-w_]|\/[\*\/])(?>[^' . $end . ']*))' . $end;
+            } else {
+                $regex = $begin . '((?:[\$]{1,2}[a-wA-w_]|[\:\~][\$a-wA-w_]|[+]{2}[\$][a-wA-w_]|[-]{2}[\$][a-wA-w_]|\/[\*\/])(?>(?:(?!' . $end . ').)*))' . $end;
+            }
+        } else {
+            $begin  = $this->config['taglib_begin'];
+            $end    = $this->config['taglib_end'];
+            $single = strlen(ltrim($begin, '\\')) == 1 && strlen(ltrim($end, '\\')) == 1 ? true : false;
+
+            switch ($tagName) {
+                case 'block':
+                    if ($single) {
+                        $regex = $begin . '(?:' . $tagName . '\b\s+(?>(?:(?!name=).)*)\bname=([\'\"])(?P<name>[\$\w\-\/\.]+)\\1(?>[^' . $end . ']*)|\/' . $tagName . ')' . $end;
+                    } else {
+                        $regex = $begin . '(?:' . $tagName . '\b\s+(?>(?:(?!name=).)*)\bname=([\'\"])(?P<name>[\$\w\-\/\.]+)\\1(?>(?:(?!' . $end . ').)*)|\/' . $tagName . ')' . $end;
+                    }
+                    break;
+                case 'literal':
+                    if ($single) {
+                        $regex = '(' . $begin . $tagName . '\b(?>[^' . $end . ']*)' . $end . ')';
+                        $regex .= '(?:(?>[^' . $begin . ']*)(?>(?!' . $begin . '(?>' . $tagName . '\b[^' . $end . ']*|\/' . $tagName . ')' . $end . ')' . $begin . '[^' . $begin . ']*)*)';
+                        $regex .= '(' . $begin . '\/' . $tagName . $end . ')';
+                    } else {
+                        $regex = '(' . $begin . $tagName . '\b(?>(?:(?!' . $end . ').)*)' . $end . ')';
+                        $regex .= '(?:(?>(?:(?!' . $begin . ').)*)(?>(?!' . $begin . '(?>' . $tagName . '\b(?>(?:(?!' . $end . ').)*)|\/' . $tagName . ')' . $end . ')' . $begin . '(?>(?:(?!' . $begin . ').)*))*)';
+                        $regex .= '(' . $begin . '\/' . $tagName . $end . ')';
+                    }
+                    break;
+                case 'restoreliteral':
+                    $regex = '<!--###literal(\d+)###-->';
+                    break;
+                case 'include':
+                    $name = 'file';
+                case 'taglib':
+                case 'layout':
+                case 'extend':
+                    if (empty($name)) {
+                        $name = 'name';
+                    }
+                    if ($single) {
+                        $regex = $begin . $tagName . '\b\s+(?>(?:(?!' . $name . '=).)*)\b' . $name . '=([\'\"])(?P<name>[\$\w\-\/\.\:@,\\\\]+)\\1(?>[^' . $end . ']*)' . $end;
+                    } else {
+                        $regex = $begin . $tagName . '\b\s+(?>(?:(?!' . $name . '=).)*)\b' . $name . '=([\'\"])(?P<name>[\$\w\-\/\.\:@,\\\\]+)\\1(?>(?:(?!' . $end . ').)*)' . $end;
+                    }
+                    break;
+            }
+        }
+
+        return '/' . $regex . '/is';
+    }
+
+    public function __debugInfo()
+    {
+        $data = get_object_vars($this);
+        unset($data['storage']);
+
+        return $data;
+    }
+}
diff --git a/vendor/topthink/think-template/src/facade/Template.php b/vendor/topthink/think-template/src/facade/Template.php
new file mode 100755
index 0000000..665a180
--- /dev/null
+++ b/vendor/topthink/think-template/src/facade/Template.php
@@ -0,0 +1,83 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\facade;
+
+if (class_exists('think\Facade')) {
+    class Facade extends \think\Facade
+    {}
+} else {
+    class Facade
+    {
+        /**
+         * 始终创建新的对象实例
+         * @var bool
+         */
+        protected static $alwaysNewInstance;
+
+        protected static $instance;
+
+        /**
+         * 获取当前Facade对应类名
+         * @access protected
+         * @return string
+         */
+        protected static function getFacadeClass()
+        {}
+
+        /**
+         * 创建Facade实例
+         * @static
+         * @access protected
+         * @return object
+         */
+        protected static function createFacade()
+        {
+            $class = static::getFacadeClass() ?: 'think\Template';
+
+            if (static::$alwaysNewInstance) {
+                return new $class();
+            }
+
+            if (!self::$instance) {
+                self::$instance = new $class();
+            }
+
+            return self::$instance;
+
+        }
+
+        // 调用实际类的方法
+        public static function __callStatic($method, $params)
+        {
+            return call_user_func_array([static::createFacade(), $method], $params);
+        }
+    }
+}
+
+/**
+ * @see \think\Template
+ * @mixin \think\Template
+ */
+class Template extends Facade
+{
+    protected static $alwaysNewInstance = true;
+
+    /**
+     * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+     * @access protected
+     * @return string
+     */
+    protected static function getFacadeClass()
+    {
+        return 'think\Template';
+    }
+}
diff --git a/vendor/topthink/think-template/src/template/TagLib.php b/vendor/topthink/think-template/src/template/TagLib.php
new file mode 100755
index 0000000..f6c8fbb
--- /dev/null
+++ b/vendor/topthink/think-template/src/template/TagLib.php
@@ -0,0 +1,349 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\template;
+
+use Exception;
+use think\Template;
+
+/**
+ * ThinkPHP标签库TagLib解析基类
+ * @category   Think
+ * @package  Think
+ * @subpackage  Template
+ * @author    liu21st <liu21st@gmail.com>
+ */
+class TagLib
+{
+
+    /**
+     * 标签库定义XML文件
+     * @var string
+     * @access protected
+     */
+    protected $xml  = '';
+    protected $tags = []; // 标签定义
+    /**
+     * 标签库名称
+     * @var string
+     * @access protected
+     */
+    protected $tagLib = '';
+
+    /**
+     * 标签库标签列表
+     * @var array
+     * @access protected
+     */
+    protected $tagList = [];
+
+    /**
+     * 标签库分析数组
+     * @var array
+     * @access protected
+     */
+    protected $parse = [];
+
+    /**
+     * 标签库是否有效
+     * @var bool
+     * @access protected
+     */
+    protected $valid = false;
+
+    /**
+     * 当前模板对象
+     * @var object
+     * @access protected
+     */
+    protected $tpl;
+
+    protected $comparison = [' nheq ' => ' !== ', ' heq ' => ' === ', ' neq ' => ' != ', ' eq ' => ' == ', ' egt ' => ' >= ', ' gt ' => ' > ', ' elt ' => ' <= ', ' lt ' => ' < '];
+
+    /**
+     * 架构函数
+     * @access public
+     * @param  Template $template 模板引擎对象
+     */
+    public function __construct(Template $template)
+    {
+        $this->tpl = $template;
+    }
+
+    /**
+     * 按签标库替换页面中的标签
+     * @access public
+     * @param  string $content 模板内容
+     * @param  string $lib 标签库名
+     * @return void
+     */
+    public function parseTag(string &$content, string $lib = ''): void
+    {
+        $tags = [];
+        $lib  = $lib ? strtolower($lib) . ':' : '';
+
+        foreach ($this->tags as $name => $val) {
+            $close                      = !isset($val['close']) || $val['close'] ? 1 : 0;
+            $tags[$close][$lib . $name] = $name;
+            if (isset($val['alias'])) {
+                // 别名设置
+                $array = (array) $val['alias'];
+                foreach (explode(',', $array[0]) as $v) {
+                    $tags[$close][$lib . $v] = $name;
+                }
+            }
+        }
+
+        // 闭合标签
+        if (!empty($tags[1])) {
+            $nodes = [];
+            $regex = $this->getRegex(array_keys($tags[1]), 1);
+            if (preg_match_all($regex, $content, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) {
+                $right = [];
+                foreach ($matches as $match) {
+                    if ('' == $match[1][0]) {
+                        $name = strtolower($match[2][0]);
+                        // 如果有没闭合的标签头则取出最后一个
+                        if (!empty($right[$name])) {
+                            // $match[0][1]为标签结束符在模板中的位置
+                            $nodes[$match[0][1]] = [
+                                'name'  => $name,
+                                'begin' => array_pop($right[$name]), // 标签开始符
+                                'end'   => $match[0], // 标签结束符
+                            ];
+                        }
+                    } else {
+                        // 标签头压入栈
+                        $right[strtolower($match[1][0])][] = $match[0];
+                    }
+                }
+                unset($right, $matches);
+                // 按标签在模板中的位置从后向前排序
+                krsort($nodes);
+            }
+
+            $break = '<!--###break###--!>';
+            if ($nodes) {
+                $beginArray = [];
+                // 标签替换 从后向前
+                foreach ($nodes as $pos => $node) {
+                    // 对应的标签名
+                    $name  = $tags[1][$node['name']];
+                    $alias = $lib . $name != $node['name'] ? ($lib ? strstr($node['name'], $lib) : $node['name']) : '';
+
+                    // 解析标签属性
+                    $attrs  = $this->parseAttr($node['begin'][0], $name, $alias);
+                    $method = 'tag' . $name;
+
+                    // 读取标签库中对应的标签内容 replace[0]用来替换标签头,replace[1]用来替换标签尾
+                    $replace = explode($break, $this->$method($attrs, $break));
+
+                    if (count($replace) > 1) {
+                        while ($beginArray) {
+                            $begin = end($beginArray);
+                            // 判断当前标签尾的位置是否在栈中最后一个标签头的后面,是则为子标签
+                            if ($node['end'][1] > $begin['pos']) {
+                                break;
+                            } else {
+                                // 不为子标签时,取出栈中最后一个标签头
+                                $begin = array_pop($beginArray);
+                                // 替换标签头部
+                                $content = substr_replace($content, $begin['str'], $begin['pos'], $begin['len']);
+                            }
+                        }
+                        // 替换标签尾部
+                        $content = substr_replace($content, $replace[1], $node['end'][1], strlen($node['end'][0]));
+                        // 把标签头压入栈
+                        $beginArray[] = ['pos' => $node['begin'][1], 'len' => strlen($node['begin'][0]), 'str' => $replace[0]];
+                    }
+                }
+
+                while ($beginArray) {
+                    $begin = array_pop($beginArray);
+                    // 替换标签头部
+                    $content = substr_replace($content, $begin['str'], $begin['pos'], $begin['len']);
+                }
+            }
+        }
+        // 自闭合标签
+        if (!empty($tags[0])) {
+            $regex   = $this->getRegex(array_keys($tags[0]), 0);
+            $content = preg_replace_callback($regex, function ($matches) use (&$tags, &$lib) {
+                // 对应的标签名
+                $name  = $tags[0][strtolower($matches[1])];
+                $alias = $lib . $name != $matches[1] ? ($lib ? strstr($matches[1], $lib) : $matches[1]) : '';
+                // 解析标签属性
+                $attrs  = $this->parseAttr($matches[0], $name, $alias);
+                $method = 'tag' . $name;
+                return $this->$method($attrs, '');
+            }, $content);
+        }
+    }
+
+    /**
+     * 按标签生成正则
+     * @access public
+     * @param  array|string     $tags 标签名
+     * @param  boolean          $close 是否为闭合标签
+     * @return string
+     */
+    public function getRegex($tags, bool $close): string
+    {
+        $begin   = $this->tpl->getConfig('taglib_begin');
+        $end     = $this->tpl->getConfig('taglib_end');
+        $single  = strlen(ltrim($begin, '\\')) == 1 && strlen(ltrim($end, '\\')) == 1 ? true : false;
+        $tagName = is_array($tags) ? implode('|', $tags) : $tags;
+
+        if ($single) {
+            if ($close) {
+                // 如果是闭合标签
+                $regex = $begin . '(?:(' . $tagName . ')\b(?>[^' . $end . ']*)|\/(' . $tagName . '))' . $end;
+            } else {
+                $regex = $begin . '(' . $tagName . ')\b(?>[^' . $end . ']*)' . $end;
+            }
+        } else {
+            if ($close) {
+                // 如果是闭合标签
+                $regex = $begin . '(?:(' . $tagName . ')\b(?>(?:(?!' . $end . ').)*)|\/(' . $tagName . '))' . $end;
+            } else {
+                $regex = $begin . '(' . $tagName . ')\b(?>(?:(?!' . $end . ').)*)' . $end;
+            }
+        }
+
+        return '/' . $regex . '/is';
+    }
+
+    /**
+     * 分析标签属性 正则方式
+     * @access public
+     * @param  string $str 标签属性字符串
+     * @param  string $name 标签名
+     * @param  string $alias 别名
+     * @return array
+     */
+    public function parseAttr(string $str, string $name, string $alias = ''): array
+    {
+        $regex  = '/\s+(?>(?P<name>[\w-]+)\s*)=(?>\s*)([\"\'])(?P<value>(?:(?!\\2).)*)\\2/is';
+        $result = [];
+
+        if (preg_match_all($regex, $str, $matches)) {
+            foreach ($matches['name'] as $key => $val) {
+                $result[$val] = $matches['value'][$key];
+            }
+
+            if (!isset($this->tags[$name])) {
+                // 检测是否存在别名定义
+                foreach ($this->tags as $key => $val) {
+                    if (isset($val['alias'])) {
+                        $array = (array) $val['alias'];
+                        if (in_array($name, explode(',', $array[0]))) {
+                            $tag           = $val;
+                            $type          = !empty($array[1]) ? $array[1] : 'type';
+                            $result[$type] = $name;
+                            break;
+                        }
+                    }
+                }
+            } else {
+                $tag = $this->tags[$name];
+                // 设置了标签别名
+                if (!empty($alias) && isset($tag['alias'])) {
+                    $type          = !empty($tag['alias'][1]) ? $tag['alias'][1] : 'type';
+                    $result[$type] = $alias;
+                }
+            }
+
+            if (!empty($tag['must'])) {
+                $must = explode(',', $tag['must']);
+                foreach ($must as $name) {
+                    if (!isset($result[$name])) {
+                        throw new Exception('tag attr must:' . $name);
+                    }
+                }
+            }
+        } else {
+            // 允许直接使用表达式的标签
+            if (!empty($this->tags[$name]['expression'])) {
+                static $_taglibs;
+                if (!isset($_taglibs[$name])) {
+                    $_taglibs[$name][0] = strlen($this->tpl->getConfig('taglib_begin_origin') . $name);
+                    $_taglibs[$name][1] = strlen($this->tpl->getConfig('taglib_end_origin'));
+                }
+                $result['expression'] = substr($str, $_taglibs[$name][0], -$_taglibs[$name][1]);
+                // 清除自闭合标签尾部/
+                $result['expression'] = rtrim($result['expression'], '/');
+                $result['expression'] = trim($result['expression']);
+            } elseif (empty($this->tags[$name]) || !empty($this->tags[$name]['attr'])) {
+                throw new Exception('tag error:' . $name);
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * 解析条件表达式
+     * @access public
+     * @param  string $condition 表达式标签内容
+     * @return string
+     */
+    public function parseCondition(string $condition): string
+    {
+        if (strpos($condition, ':')) {
+            $condition = ' ' . substr(strstr($condition, ':'), 1);
+        }
+
+        $condition = str_ireplace(array_keys($this->comparison), array_values($this->comparison), $condition);
+        $this->tpl->parseVar($condition);
+
+        return $condition;
+    }
+
+    /**
+     * 自动识别构建变量
+     * @access public
+     * @param  string    $name       变量描述
+     * @return string
+     */
+    public function autoBuildVar(string &$name): string
+    {
+        $flag = substr($name, 0, 1);
+
+        if (':' == $flag) {
+            // 以:开头为函数调用,解析前去掉:
+            $name = substr($name, 1);
+        } elseif ('$' != $flag && preg_match('/[a-zA-Z_]/', $flag)) {
+            // XXX: 这句的写法可能还需要改进
+            // 常量不需要解析
+            if (defined($name)) {
+                return $name;
+            }
+
+            // 不以$开头并且也不是常量,自动补上$前缀
+            $name = '$' . $name;
+        }
+
+        $this->tpl->parseVar($name);
+        $this->tpl->parseVarFunction($name, false);
+
+        return $name;
+    }
+
+    /**
+     * 获取标签列表
+     * @access public
+     * @return array
+     */
+    public function getTags(): array
+    {
+        return $this->tags;
+    }
+}
diff --git a/vendor/topthink/think-template/src/template/driver/File.php b/vendor/topthink/think-template/src/template/driver/File.php
new file mode 100755
index 0000000..510d10a
--- /dev/null
+++ b/vendor/topthink/think-template/src/template/driver/File.php
@@ -0,0 +1,83 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\template\driver;
+
+use Exception;
+
+class File
+{
+    protected $cacheFile;
+
+    /**
+     * 写入编译缓存
+     * @access public
+     * @param  string $cacheFile 缓存的文件名
+     * @param  string $content 缓存的内容
+     * @return void
+     */
+    public function write(string $cacheFile, string $content): void
+    {
+        // 检测模板目录
+        $dir = dirname($cacheFile);
+
+        if (!is_dir($dir)) {
+            mkdir($dir, 0755, true);
+        }
+
+        // 生成模板缓存文件
+        if (false === file_put_contents($cacheFile, $content)) {
+            throw new Exception('cache write error:' . $cacheFile, 11602);
+        }
+    }
+
+    /**
+     * 读取编译编译
+     * @access public
+     * @param  string  $cacheFile 缓存的文件名
+     * @param  array   $vars 变量数组
+     * @return void
+     */
+    public function read(string $cacheFile, array $vars = []): void
+    {
+        $this->cacheFile = $cacheFile;
+
+        if (!empty($vars) && is_array($vars)) {
+            // 模板阵列变量分解成为独立变量
+            extract($vars, EXTR_OVERWRITE);
+        }
+
+        //载入模版缓存文件
+        include $this->cacheFile;
+    }
+
+    /**
+     * 检查编译缓存是否有效
+     * @access public
+     * @param  string  $cacheFile 缓存的文件名
+     * @param  int     $cacheTime 缓存时间
+     * @return bool
+     */
+    public function check(string $cacheFile, int $cacheTime): bool
+    {
+        // 缓存文件不存在, 直接返回false
+        if (!file_exists($cacheFile)) {
+            return false;
+        }
+
+        if (0 != $cacheTime && time() > filemtime($cacheFile) + $cacheTime) {
+            // 缓存是否在有效期
+            return false;
+        }
+
+        return true;
+    }
+}
diff --git a/vendor/topthink/think-template/src/template/exception/TemplateNotFoundException.php b/vendor/topthink/think-template/src/template/exception/TemplateNotFoundException.php
new file mode 100755
index 0000000..dd88b32
--- /dev/null
+++ b/vendor/topthink/think-template/src/template/exception/TemplateNotFoundException.php
@@ -0,0 +1,33 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\template\exception;
+
+class TemplateNotFoundException extends \RuntimeException
+{
+    protected $template;
+
+    public function __construct(string $message, string $template = '')
+    {
+        $this->message  = $message;
+        $this->template = $template;
+    }
+
+    /**
+     * 获取模板文件
+     * @access public
+     * @return string
+     */
+    public function getTemplate(): string
+    {
+        return $this->template;
+    }
+}
diff --git a/vendor/topthink/think-template/src/template/taglib/Cx.php b/vendor/topthink/think-template/src/template/taglib/Cx.php
new file mode 100755
index 0000000..bccafc1
--- /dev/null
+++ b/vendor/topthink/think-template/src/template/taglib/Cx.php
@@ -0,0 +1,715 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\template\taglib;
+
+use think\template\TagLib;
+
+/**
+ * CX标签库解析类
+ * @category   Think
+ * @package  Think
+ * @subpackage  Driver.Taglib
+ * @author    liu21st <liu21st@gmail.com>
+ */
+class Cx extends Taglib
+{
+
+    // 标签定义
+    protected $tags = [
+        // 标签定义: attr 属性列表 close 是否闭合(0 或者1 默认1) alias 标签别名 level 嵌套层次
+        'php'        => ['attr' => ''],
+        'volist'     => ['attr' => 'name,id,offset,length,key,mod', 'alias' => 'iterate'],
+        'foreach'    => ['attr' => 'name,id,item,key,offset,length,mod', 'expression' => true],
+        'if'         => ['attr' => 'condition', 'expression' => true],
+        'elseif'     => ['attr' => 'condition', 'close' => 0, 'expression' => true],
+        'else'       => ['attr' => '', 'close' => 0],
+        'switch'     => ['attr' => 'name', 'expression' => true],
+        'case'       => ['attr' => 'value,break', 'expression' => true],
+        'default'    => ['attr' => '', 'close' => 0],
+        'compare'    => ['attr' => 'name,value,type', 'alias' => ['eq,equal,notequal,neq,gt,lt,egt,elt,heq,nheq', 'type']],
+        'range'      => ['attr' => 'name,value,type', 'alias' => ['in,notin,between,notbetween', 'type']],
+        'empty'      => ['attr' => 'name'],
+        'notempty'   => ['attr' => 'name'],
+        'present'    => ['attr' => 'name'],
+        'notpresent' => ['attr' => 'name'],
+        'defined'    => ['attr' => 'name'],
+        'notdefined' => ['attr' => 'name'],
+        'load'       => ['attr' => 'file,href,type,value,basepath', 'close' => 0, 'alias' => ['import,css,js', 'type']],
+        'assign'     => ['attr' => 'name,value', 'close' => 0],
+        'define'     => ['attr' => 'name,value', 'close' => 0],
+        'for'        => ['attr' => 'start,end,name,comparison,step'],
+        'url'        => ['attr' => 'link,vars,suffix,domain', 'close' => 0, 'expression' => true],
+        'function'   => ['attr' => 'name,vars,use,call'],
+    ];
+
+    /**
+     * php标签解析
+     * 格式:
+     * {php}echo $name{/php}
+     * @access public
+     * @param  array $tag 标签属性
+     * @param  string $content 标签内容
+     * @return string
+     */
+    public function tagPhp(array $tag, string $content): string
+    {
+        $parseStr = '<?php ' . $content . ' ?>';
+        return $parseStr;
+    }
+
+    /**
+     * volist标签解析 循环输出数据集
+     * 格式:
+     * {volist name="userList" id="user" empty=""}
+     * {user.username}
+     * {user.email}
+     * {/volist}
+     * @access public
+     * @param  array $tag 标签属性
+     * @param  string $content 标签内容
+     * @return string
+     */
+    public function tagVolist(array $tag, string $content): string
+    {
+        $name   = $tag['name'];
+        $id     = $tag['id'];
+        $empty  = isset($tag['empty']) ? $tag['empty'] : '';
+        $key    = !empty($tag['key']) ? $tag['key'] : 'i';
+        $mod    = isset($tag['mod']) ? $tag['mod'] : '2';
+        $offset = !empty($tag['offset']) && is_numeric($tag['offset']) ? intval($tag['offset']) : 0;
+        $length = !empty($tag['length']) && is_numeric($tag['length']) ? intval($tag['length']) : 'null';
+        // 允许使用函数设定数据集 <volist name=":fun('arg')" id="vo">{$vo.name}</volist>
+        $parseStr = '<?php ';
+        $flag     = substr($name, 0, 1);
+
+        if (':' == $flag) {
+            $name = $this->autoBuildVar($name);
+            $parseStr .= '$_result=' . $name . ';';
+            $name = '$_result';
+        } else {
+            $name = $this->autoBuildVar($name);
+        }
+
+        $parseStr .= 'if(is_array(' . $name . ') || ' . $name . ' instanceof \think\Collection || ' . $name . ' instanceof \think\Paginator): $' . $key . ' = 0;';
+
+        // 设置了输出数组长度
+        if (0 != $offset || 'null' != $length) {
+            $parseStr .= '$__LIST__ = is_array(' . $name . ') ? array_slice(' . $name . ',' . $offset . ',' . $length . ', true) : ' . $name . '->slice(' . $offset . ',' . $length . ', true); ';
+        } else {
+            $parseStr .= ' $__LIST__ = ' . $name . ';';
+        }
+
+        $parseStr .= 'if( count($__LIST__)==0 ) : echo "' . $empty . '" ;';
+        $parseStr .= 'else: ';
+        $parseStr .= 'foreach($__LIST__ as $key=>$' . $id . '): ';
+        $parseStr .= '$mod = ($' . $key . ' % ' . $mod . ' );';
+        $parseStr .= '++$' . $key . ';?>';
+        $parseStr .= $content;
+        $parseStr .= '<?php endforeach; endif; else: echo "' . $empty . '" ;endif; ?>';
+
+        return $parseStr;
+    }
+
+    /**
+     * foreach标签解析 循环输出数据集
+     * 格式:
+     * {foreach name="userList" id="user" key="key" index="i" mod="2" offset="3" length="5" empty=""}
+     * {user.username}
+     * {/foreach}
+     * @access public
+     * @param  array $tag 标签属性
+     * @param  string $content 标签内容
+     * @return string
+     */
+    public function tagForeach(array $tag, string $content): string
+    {
+        // 直接使用表达式
+        if (!empty($tag['expression'])) {
+            $expression = ltrim(rtrim($tag['expression'], ')'), '(');
+            $expression = $this->autoBuildVar($expression);
+            $parseStr   = '<?php foreach(' . $expression . '): ?>';
+            $parseStr .= $content;
+            $parseStr .= '<?php endforeach; ?>';
+            return $parseStr;
+        }
+
+        $name   = $tag['name'];
+        $key    = !empty($tag['key']) ? $tag['key'] : 'key';
+        $item   = !empty($tag['id']) ? $tag['id'] : $tag['item'];
+        $empty  = isset($tag['empty']) ? $tag['empty'] : '';
+        $offset = !empty($tag['offset']) && is_numeric($tag['offset']) ? intval($tag['offset']) : 0;
+        $length = !empty($tag['length']) && is_numeric($tag['length']) ? intval($tag['length']) : 'null';
+
+        $parseStr = '<?php ';
+
+        // 支持用函数传数组
+        if (':' == substr($name, 0, 1)) {
+            $var  = '$_' . uniqid();
+            $name = $this->autoBuildVar($name);
+            $parseStr .= $var . '=' . $name . '; ';
+            $name = $var;
+        } else {
+            $name = $this->autoBuildVar($name);
+        }
+
+        $parseStr .= 'if(is_array(' . $name . ') || ' . $name . ' instanceof \think\Collection || ' . $name . ' instanceof \think\Paginator): ';
+
+        // 设置了输出数组长度
+        if (0 != $offset || 'null' != $length) {
+            if (!isset($var)) {
+                $var = '$_' . uniqid();
+            }
+            $parseStr .= $var . ' = is_array(' . $name . ') ? array_slice(' . $name . ',' . $offset . ',' . $length . ', true) : ' . $name . '->slice(' . $offset . ',' . $length . ', true); ';
+        } else {
+            $var = &$name;
+        }
+
+        $parseStr .= 'if( count(' . $var . ')==0 ) : echo "' . $empty . '" ;';
+        $parseStr .= 'else: ';
+
+        // 设置了索引项
+        if (isset($tag['index'])) {
+            $index = $tag['index'];
+            $parseStr .= '$' . $index . '=0; ';
+        }
+
+        $parseStr .= 'foreach(' . $var . ' as $' . $key . '=>$' . $item . '): ';
+
+        // 设置了索引项
+        if (isset($tag['index'])) {
+            $index = $tag['index'];
+            if (isset($tag['mod'])) {
+                $mod = (int) $tag['mod'];
+                $parseStr .= '$mod = ($' . $index . ' % ' . $mod . '); ';
+            }
+            $parseStr .= '++$' . $index . '; ';
+        }
+
+        $parseStr .= '?>';
+        // 循环体中的内容
+        $parseStr .= $content;
+        $parseStr .= '<?php endforeach; endif; else: echo "' . $empty . '" ;endif; ?>';
+
+        return $parseStr;
+    }
+
+    /**
+     * if标签解析
+     * 格式:
+     * {if condition=" $a eq 1"}
+     * {elseif condition="$a eq 2" /}
+     * {else /}
+     * {/if}
+     * 表达式支持 eq neq gt egt lt elt == > >= < <= or and || &&
+     * @access public
+     * @param  array $tag 标签属性
+     * @param  string $content 标签内容
+     * @return string
+     */
+    public function tagIf(array $tag, string $content): string
+    {
+        $condition = !empty($tag['expression']) ? $tag['expression'] : $tag['condition'];
+        $condition = $this->parseCondition($condition);
+        $parseStr  = '<?php if(' . $condition . '): ?>' . $content . '<?php endif; ?>';
+
+        return $parseStr;
+    }
+
+    /**
+     * elseif标签解析
+     * 格式:见if标签
+     * @access public
+     * @param  array $tag 标签属性
+     * @param  string $content 标签内容
+     * @return string
+     */
+    public function tagElseif(array $tag, string $content): string
+    {
+        $condition = !empty($tag['expression']) ? $tag['expression'] : $tag['condition'];
+        $condition = $this->parseCondition($condition);
+        $parseStr  = '<?php elseif(' . $condition . '): ?>';
+
+        return $parseStr;
+    }
+
+    /**
+     * else标签解析
+     * 格式:见if标签
+     * @access public
+     * @param  array $tag 标签属性
+     * @return string
+     */
+    public function tagElse(array $tag): string
+    {
+        $parseStr = '<?php else: ?>';
+
+        return $parseStr;
+    }
+
+    /**
+     * switch标签解析
+     * 格式:
+     * {switch name="a.name"}
+     * {case value="1" break="false"}1{/case}
+     * {case value="2" }2{/case}
+     * {default /}other
+     * {/switch}
+     * @access public
+     * @param  array $tag 标签属性
+     * @param  string $content 标签内容
+     * @return string
+     */
+    public function tagSwitch(array $tag, string $content): string
+    {
+        $name     = !empty($tag['expression']) ? $tag['expression'] : $tag['name'];
+        $name     = $this->autoBuildVar($name);
+        $parseStr = '<?php switch(' . $name . '): ?>' . $content . '<?php endswitch; ?>';
+
+        return $parseStr;
+    }
+
+    /**
+     * case标签解析 需要配合switch才有效
+     * @access public
+     * @param  array $tag 标签属性
+     * @param  string $content 标签内容
+     * @return string
+     */
+    public function tagCase(array $tag, string $content): string
+    {
+        $value = isset($tag['expression']) ? $tag['expression'] : $tag['value'];
+        $flag  = substr($value, 0, 1);
+
+        if ('$' == $flag || ':' == $flag) {
+            $value = $this->autoBuildVar($value);
+            $value = 'case ' . $value . ':';
+        } elseif (strpos($value, '|')) {
+            $values = explode('|', $value);
+            $value  = '';
+            foreach ($values as $val) {
+                $value .= 'case "' . addslashes($val) . '":';
+            }
+        } else {
+            $value = 'case "' . $value . '":';
+        }
+
+        $parseStr = '<?php ' . $value . ' ?>' . $content;
+        $isBreak  = isset($tag['break']) ? $tag['break'] : '';
+
+        if ('' == $isBreak || $isBreak) {
+            $parseStr .= '<?php break; ?>';
+        }
+
+        return $parseStr;
+    }
+
+    /**
+     * default标签解析 需要配合switch才有效
+     * 使用: {default /}ddfdf
+     * @access public
+     * @param  array $tag 标签属性
+     * @return string
+     */
+    public function tagDefault(array $tag): string
+    {
+        $parseStr = '<?php default: ?>';
+
+        return $parseStr;
+    }
+
+    /**
+     * compare标签解析
+     * 用于值的比较 支持 eq neq gt lt egt elt heq nheq 默认是eq
+     * 格式: {compare name="" type="eq" value="" }content{/compare}
+     * @access public
+     * @param  array $tag 标签属性
+     * @param  string $content 标签内容
+     * @return string
+     */
+    public function tagCompare(array $tag, string $content): string
+    {
+        $name  = $tag['name'];
+        $value = $tag['value'];
+        $type  = isset($tag['type']) ? $tag['type'] : 'eq'; // 比较类型
+        $name  = $this->autoBuildVar($name);
+        $flag  = substr($value, 0, 1);
+
+        if ('$' == $flag || ':' == $flag) {
+            $value = $this->autoBuildVar($value);
+        } else {
+            $value = '\'' . $value . '\'';
+        }
+
+        switch ($type) {
+            case 'equal':
+                $type = 'eq';
+                break;
+            case 'notequal':
+                $type = 'neq';
+                break;
+        }
+        $type     = $this->parseCondition(' ' . $type . ' ');
+        $parseStr = '<?php if(' . $name . ' ' . $type . ' ' . $value . '): ?>' . $content . '<?php endif; ?>';
+
+        return $parseStr;
+    }
+
+    /**
+     * range标签解析
+     * 如果某个变量存在于某个范围 则输出内容 type= in 表示在范围内 否则表示在范围外
+     * 格式: {range name="var|function"  value="val" type='in|notin' }content{/range}
+     * example: {range name="a"  value="1,2,3" type='in' }content{/range}
+     * @access public
+     * @param  array $tag 标签属性
+     * @param  string $content 标签内容
+     * @return string
+     */
+    public function tagRange(array $tag, string $content): string
+    {
+        $name  = $tag['name'];
+        $value = $tag['value'];
+        $type  = isset($tag['type']) ? $tag['type'] : 'in'; // 比较类型
+
+        $name = $this->autoBuildVar($name);
+        $flag = substr($value, 0, 1);
+
+        if ('$' == $flag || ':' == $flag) {
+            $value = $this->autoBuildVar($value);
+            $str   = 'is_array(' . $value . ')?' . $value . ':explode(\',\',' . $value . ')';
+        } else {
+            $value = '"' . $value . '"';
+            $str   = 'explode(\',\',' . $value . ')';
+        }
+
+        if ('between' == $type) {
+            $parseStr = '<?php $_RANGE_VAR_=' . $str . ';if(' . $name . '>= $_RANGE_VAR_[0] && ' . $name . '<= $_RANGE_VAR_[1]):?>' . $content . '<?php endif; ?>';
+        } elseif ('notbetween' == $type) {
+            $parseStr = '<?php $_RANGE_VAR_=' . $str . ';if(' . $name . '<$_RANGE_VAR_[0] || ' . $name . '>$_RANGE_VAR_[1]):?>' . $content . '<?php endif; ?>';
+        } else {
+            $fun      = ('in' == $type) ? 'in_array' : '!in_array';
+            $parseStr = '<?php if(' . $fun . '((' . $name . '), ' . $str . ')): ?>' . $content . '<?php endif; ?>';
+        }
+
+        return $parseStr;
+    }
+
+    /**
+     * present标签解析
+     * 如果某个变量已经设置 则输出内容
+     * 格式: {present name="" }content{/present}
+     * @access public
+     * @param  array $tag 标签属性
+     * @param  string $content 标签内容
+     * @return string
+     */
+    public function tagPresent(array $tag, string $content): string
+    {
+        $name     = $tag['name'];
+        $name     = $this->autoBuildVar($name);
+        $parseStr = '<?php if(isset(' . $name . ')): ?>' . $content . '<?php endif; ?>';
+
+        return $parseStr;
+    }
+
+    /**
+     * notpresent标签解析
+     * 如果某个变量没有设置,则输出内容
+     * 格式: {notpresent name="" }content{/notpresent}
+     * @access public
+     * @param  array $tag 标签属性
+     * @param  string $content 标签内容
+     * @return string
+     */
+    public function tagNotpresent(array $tag, string $content): string
+    {
+        $name     = $tag['name'];
+        $name     = $this->autoBuildVar($name);
+        $parseStr = '<?php if(!isset(' . $name . ')): ?>' . $content . '<?php endif; ?>';
+
+        return $parseStr;
+    }
+
+    /**
+     * empty标签解析
+     * 如果某个变量为empty 则输出内容
+     * 格式: {empty name="" }content{/empty}
+     * @access public
+     * @param  array $tag 标签属性
+     * @param  string $content 标签内容
+     * @return string
+     */
+    public function tagEmpty(array $tag, string $content): string
+    {
+        $name     = $tag['name'];
+        $name     = $this->autoBuildVar($name);
+        $parseStr = '<?php if(empty(' . $name . ') || ((' . $name . ' instanceof \think\Collection || ' . $name . ' instanceof \think\Paginator ) && ' . $name . '->isEmpty())): ?>' . $content . '<?php endif; ?>';
+
+        return $parseStr;
+    }
+
+    /**
+     * notempty标签解析
+     * 如果某个变量不为empty 则输出内容
+     * 格式: {notempty name="" }content{/notempty}
+     * @access public
+     * @param  array $tag 标签属性
+     * @param  string $content 标签内容
+     * @return string
+     */
+    public function tagNotempty(array $tag, string $content): string
+    {
+        $name     = $tag['name'];
+        $name     = $this->autoBuildVar($name);
+        $parseStr = '<?php if(!(empty(' . $name . ') || ((' . $name . ' instanceof \think\Collection || ' . $name . ' instanceof \think\Paginator ) && ' . $name . '->isEmpty()))): ?>' . $content . '<?php endif; ?>';
+
+        return $parseStr;
+    }
+
+    /**
+     * 判断是否已经定义了该常量
+     * {defined name='TXT'}已定义{/defined}
+     * @access public
+     * @param  array $tag
+     * @param  string $content
+     * @return string
+     */
+    public function tagDefined(array $tag, string $content): string
+    {
+        $name     = $tag['name'];
+        $parseStr = '<?php if(defined("' . $name . '")): ?>' . $content . '<?php endif; ?>';
+
+        return $parseStr;
+    }
+
+    /**
+     * 判断是否没有定义了该常量
+     * {notdefined name='TXT'}已定义{/notdefined}
+     * @access public
+     * @param  array $tag
+     * @param  string $content
+     * @return string
+     */
+    public function tagNotdefined(array $tag, string $content): string
+    {
+        $name     = $tag['name'];
+        $parseStr = '<?php if(!defined("' . $name . '")): ?>' . $content . '<?php endif; ?>';
+
+        return $parseStr;
+    }
+
+    /**
+     * load 标签解析 {load file="/static/js/base.js" /}
+     * 格式:{load file="/static/css/base.css" /}
+     * @access public
+     * @param  array $tag 标签属性
+     * @param  string $content 标签内容
+     * @return string
+     */
+    public function tagLoad(array $tag, string $content): string
+    {
+        $file = isset($tag['file']) ? $tag['file'] : $tag['href'];
+        $type = isset($tag['type']) ? strtolower($tag['type']) : '';
+
+        $parseStr = '';
+        $endStr   = '';
+
+        // 判断是否存在加载条件 允许使用函数判断(默认为isset)
+        if (isset($tag['value'])) {
+            $name = $tag['value'];
+            $name = $this->autoBuildVar($name);
+            $name = 'isset(' . $name . ')';
+            $parseStr .= '<?php if(' . $name . '): ?>';
+            $endStr = '<?php endif; ?>';
+        }
+
+        // 文件方式导入
+        $array = explode(',', $file);
+
+        foreach ($array as $val) {
+            $type = strtolower(substr(strrchr($val, '.'), 1));
+            switch ($type) {
+                case 'js':
+                    $parseStr .= '<script type="text/javascript" src="' . $val . '"></script>';
+                    break;
+                case 'css':
+                    $parseStr .= '<link rel="stylesheet" type="text/css" href="' . $val . '" />';
+                    break;
+                case 'php':
+                    $parseStr .= '<?php include "' . $val . '"; ?>';
+                    break;
+            }
+        }
+
+        return $parseStr . $endStr;
+    }
+
+    /**
+     * assign标签解析
+     * 在模板中给某个变量赋值 支持变量赋值
+     * 格式: {assign name="" value="" /}
+     * @access public
+     * @param  array $tag 标签属性
+     * @param  string $content 标签内容
+     * @return string
+     */
+    public function tagAssign(array $tag, string $content): string
+    {
+        $name = $this->autoBuildVar($tag['name']);
+        $flag = substr($tag['value'], 0, 1);
+
+        if ('$' == $flag || ':' == $flag) {
+            $value = $this->autoBuildVar($tag['value']);
+        } else {
+            $value = '\'' . $tag['value'] . '\'';
+        }
+
+        $parseStr = '<?php ' . $name . ' = ' . $value . '; ?>';
+
+        return $parseStr;
+    }
+
+    /**
+     * define标签解析
+     * 在模板中定义常量 支持变量赋值
+     * 格式: {define name="" value="" /}
+     * @access public
+     * @param  array $tag 标签属性
+     * @param  string $content 标签内容
+     * @return string
+     */
+    public function tagDefine(array $tag, string $content): string
+    {
+        $name = '\'' . $tag['name'] . '\'';
+        $flag = substr($tag['value'], 0, 1);
+
+        if ('$' == $flag || ':' == $flag) {
+            $value = $this->autoBuildVar($tag['value']);
+        } else {
+            $value = '\'' . $tag['value'] . '\'';
+        }
+
+        $parseStr = '<?php define(' . $name . ', ' . $value . '); ?>';
+
+        return $parseStr;
+    }
+
+    /**
+     * for标签解析
+     * 格式:
+     * {for start="" end="" comparison="" step="" name=""}
+     * content
+     * {/for}
+     * @access public
+     * @param  array $tag 标签属性
+     * @param  string $content 标签内容
+     * @return string
+     */
+    public function tagFor(array $tag, string $content): string
+    {
+        //设置默认值
+        $start      = 0;
+        $end        = 0;
+        $step       = 1;
+        $comparison = 'lt';
+        $name       = 'i';
+        $rand       = rand(); //添加随机数,防止嵌套变量冲突
+
+        //获取属性
+        foreach ($tag as $key => $value) {
+            $value = trim($value);
+            $flag  = substr($value, 0, 1);
+            if ('$' == $flag || ':' == $flag) {
+                $value = $this->autoBuildVar($value);
+            }
+
+            switch ($key) {
+                case 'start':
+                    $start = $value;
+                    break;
+                case 'end':
+                    $end = $value;
+                    break;
+                case 'step':
+                    $step = $value;
+                    break;
+                case 'comparison':
+                    $comparison = $value;
+                    break;
+                case 'name':
+                    $name = $value;
+                    break;
+            }
+        }
+
+        $parseStr = '<?php $__FOR_START_' . $rand . '__=' . $start . ';$__FOR_END_' . $rand . '__=' . $end . ';';
+        $parseStr .= 'for($' . $name . '=$__FOR_START_' . $rand . '__;' . $this->parseCondition('$' . $name . ' ' . $comparison . ' $__FOR_END_' . $rand . '__') . ';$' . $name . '+=' . $step . '){ ?>';
+        $parseStr .= $content;
+        $parseStr .= '<?php } ?>';
+
+        return $parseStr;
+    }
+
+    /**
+     * url函数的tag标签
+     * 格式:{url link="模块/控制器/方法" vars="参数" suffix="true或者false 是否带有后缀" domain="true或者false 是否携带域名" /}
+     * @access public
+     * @param  array $tag 标签属性
+     * @param  string $content 标签内容
+     * @return string
+     */
+    public function tagUrl(array $tag, string $content): string
+    {
+        $url    = isset($tag['link']) ? $tag['link'] : '';
+        $vars   = isset($tag['vars']) ? $tag['vars'] : '';
+        $suffix = isset($tag['suffix']) ? $tag['suffix'] : 'true';
+        $domain = isset($tag['domain']) ? $tag['domain'] : 'false';
+
+        return '<?php echo url("' . $url . '","' . $vars . '",' . $suffix . ',' . $domain . ');?>';
+    }
+
+    /**
+     * function标签解析 匿名函数,可实现递归
+     * 使用:
+     * {function name="func" vars="$data" call="$list" use="&$a,&$b"}
+     *      {if is_array($data)}
+     *          {foreach $data as $val}
+     *              {~func($val) /}
+     *          {/foreach}
+     *      {else /}
+     *          {$data}
+     *      {/if}
+     * {/function}
+     * @access public
+     * @param  array $tag 标签属性
+     * @param  string $content 标签内容
+     * @return string
+     */
+    public function tagFunction(array $tag, string $content): string
+    {
+        $name = !empty($tag['name']) ? $tag['name'] : 'func';
+        $vars = !empty($tag['vars']) ? $tag['vars'] : '';
+        $call = !empty($tag['call']) ? $tag['call'] : '';
+        $use  = ['&$' . $name];
+
+        if (!empty($tag['use'])) {
+            foreach (explode(',', $tag['use']) as $val) {
+                $use[] = '&' . ltrim(trim($val), '&');
+            }
+        }
+
+        $parseStr = '<?php $' . $name . '=function(' . $vars . ') use(' . implode(',', $use) . ') {';
+        $parseStr .= ' ?>' . $content . '<?php }; ';
+        $parseStr .= $call ? '$' . $name . '(' . $call . '); ?>' : '?>';
+
+        return $parseStr;
+    }
+}
diff --git a/vendor/topthink/think-view/.gitignore b/vendor/topthink/think-view/.gitignore
new file mode 100755
index 0000000..485dee6
--- /dev/null
+++ b/vendor/topthink/think-view/.gitignore
@@ -0,0 +1 @@
+.idea
diff --git a/vendor/topthink/think-view/LICENSE b/vendor/topthink/think-view/LICENSE
new file mode 100755
index 0000000..8dada3e
--- /dev/null
+++ b/vendor/topthink/think-view/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/vendor/topthink/think-view/README.md b/vendor/topthink/think-view/README.md
new file mode 100755
index 0000000..4e52def
--- /dev/null
+++ b/vendor/topthink/think-view/README.md
@@ -0,0 +1,36 @@
+# think-view
+
+ThinkPHP6.0 Think-Template模板引擎驱动
+
+
+## 安装
+
+~~~php
+composer require topthink/think-view
+~~~
+
+## 用法示例
+
+本扩展不能单独使用,依赖ThinkPHP6.0+
+
+首先配置config目录下的template.php配置文件,然后可以按照下面的用法使用。
+
+~~~php
+
+use think\facade\View;
+
+// 模板变量赋值和渲染输出
+View::assign(['name' => 'think'])
+	// 输出过滤
+	->filter(function($content){
+		return str_replace('search', 'replace', $content);
+	})
+	// 读取模板文件渲染输出
+	->fetch('index');
+
+
+// 或者使用助手函数
+view('index', ['name' => 'think']);
+~~~
+
+具体的模板引擎配置请参考think-template库。
\ No newline at end of file
diff --git a/vendor/topthink/think-view/composer.json b/vendor/topthink/think-view/composer.json
new file mode 100755
index 0000000..f4e6431
--- /dev/null
+++ b/vendor/topthink/think-view/composer.json
@@ -0,0 +1,20 @@
+{
+    "name": "topthink/think-view",
+    "description": "thinkphp template driver",
+    "license": "Apache-2.0",
+    "authors": [
+        {
+            "name": "liu21st",
+            "email": "liu21st@gmail.com"
+        }
+    ],
+    "require": {
+    	"php": ">=7.1.0",
+        "topthink/think-template": "^2.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "think\\view\\driver\\": "src"
+        }
+    }  
+}
diff --git a/vendor/topthink/think-view/src/Think.php b/vendor/topthink/think-view/src/Think.php
new file mode 100755
index 0000000..02be10f
--- /dev/null
+++ b/vendor/topthink/think-view/src/Think.php
@@ -0,0 +1,263 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace think\view\driver;
+
+use think\App;
+use think\helper\Str;
+use think\Template;
+use think\template\exception\TemplateNotFoundException;
+
+class Think
+{
+    // 模板引擎实例
+    private $template;
+    private $app;
+
+    // 模板引擎参数
+    protected $config = [
+        // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 3 保持操作方法
+        'auto_rule'     => 1,
+        // 视图目录名
+        'view_dir_name' => 'view',
+        // 模板起始路径
+        'view_path'     => '',
+        // 模板文件后缀
+        'view_suffix'   => 'html',
+        // 模板文件名分隔符
+        'view_depr'     => DIRECTORY_SEPARATOR,
+        // 是否开启模板编译缓存,设为false则每次都会重新编译
+        'tpl_cache'     => true,
+    ];
+
+    public function __construct(App $app, array $config = [])
+    {
+        $this->app = $app;
+
+        $this->config = array_merge($this->config, (array) $config);
+
+        if (empty($this->config['cache_path'])) {
+            $this->config['cache_path'] = $app->getRuntimePath() . 'temp' . DIRECTORY_SEPARATOR;
+        }
+
+        $this->template = new Template($this->config);
+        $this->template->setCache($app->cache);
+        $this->template->extend('$Think', function (array $vars) {
+            $type  = strtoupper(trim(array_shift($vars)));
+            $param = implode('.', $vars);
+
+            switch ($type) {
+                case 'CONST':
+                    $parseStr = strtoupper($param);
+                    break;
+                case 'CONFIG':
+                    $parseStr = 'config(\'' . $param . '\')';
+                    break;
+                case 'LANG':
+                    $parseStr = 'lang(\'' . $param . '\')';
+                    break;
+                case 'NOW':
+                    $parseStr = "date('Y-m-d g:i a',time())";
+                    break;
+                case 'LDELIM':
+                    $parseStr = '\'' . ltrim($this->getConfig('tpl_begin'), '\\') . '\'';
+                    break;
+                case 'RDELIM':
+                    $parseStr = '\'' . ltrim($this->getConfig('tpl_end'), '\\') . '\'';
+                    break;
+                default:
+                    $parseStr = defined($type) ? $type : '\'\'';
+            }
+
+            return $parseStr;
+        });
+
+        $this->template->extend('$Request', function (array $vars) {
+            // 获取Request请求对象参数
+            $method = array_shift($vars);
+            if (!empty($vars)) {
+                $params = implode('.', $vars);
+                if ('true' != $params) {
+                    $params = '\'' . $params . '\'';
+                }
+            } else {
+                $params = '';
+            }
+
+            return 'app(\'request\')->' . $method . '(' . $params . ')';
+        });
+    }
+
+    /**
+     * 检测是否存在模板文件
+     * @access public
+     * @param  string $template 模板文件或者模板规则
+     * @return bool
+     */
+    public function exists(string $template): bool
+    {
+        if ('' == pathinfo($template, PATHINFO_EXTENSION)) {
+            // 获取模板文件名
+            $template = $this->parseTemplate($template);
+        }
+
+        return is_file($template);
+    }
+
+    /**
+     * 渲染模板文件
+     * @access public
+     * @param  string $template 模板文件
+     * @param  array  $data 模板变量
+     * @return void
+     */
+    public function fetch(string $template, array $data = []): void
+    {
+        if (empty($this->config['view_path'])) {
+            $view = $this->config['view_dir_name'];
+
+            if (is_dir($this->app->getAppPath() . $view)) {
+                $path = $this->app->getAppPath() . $view . DIRECTORY_SEPARATOR;
+            } else {
+                $appName = $this->app->http->getName();
+                $path    = $this->app->getRootPath() . $view . DIRECTORY_SEPARATOR . ($appName ? $appName . DIRECTORY_SEPARATOR : '');
+            }
+
+            $this->config['view_path'] = $path;
+            $this->template->view_path = $path;
+        }
+
+        if ('' == pathinfo($template, PATHINFO_EXTENSION)) {
+            // 获取模板文件名
+            $template = $this->parseTemplate($template);
+        }
+
+        // 模板不存在 抛出异常
+        if (!is_file($template)) {
+            throw new TemplateNotFoundException('template not exists:' . $template, $template);
+        }
+
+        // 记录视图信息
+        $this->app['log']
+            ->record('[ VIEW ] ' . $template . ' [ ' . var_export(array_keys($data), true) . ' ]');
+
+        $this->template->fetch($template, $data);
+    }
+
+    /**
+     * 渲染模板内容
+     * @access public
+     * @param  string $template 模板内容
+     * @param  array  $data 模板变量
+     * @return void
+     */
+    public function display(string $template, array $data = []): void
+    {
+        $this->template->display($template, $data);
+    }
+
+    /**
+     * 自动定位模板文件
+     * @access private
+     * @param  string $template 模板文件规则
+     * @return string
+     */
+    private function parseTemplate(string $template): string
+    {
+        // 分析模板文件规则
+        $request = $this->app['request'];
+
+        // 获取视图根目录
+        if (strpos($template, '@')) {
+            // 跨模块调用
+            list($app, $template) = explode('@', $template);
+        }
+
+        if (isset($app)) {
+            $view     = $this->config['view_dir_name'];
+            $viewPath = $this->app->getBasePath() . $app . DIRECTORY_SEPARATOR . $view . DIRECTORY_SEPARATOR;
+
+            if (is_dir($viewPath)) {
+                $path = $viewPath;
+            } else {
+                $path = $this->app->getRootPath() . $view . DIRECTORY_SEPARATOR . $app . DIRECTORY_SEPARATOR;
+            }
+
+            $this->template->view_path = $path;
+        } else {
+            $path = $this->config['view_path'];
+        }
+
+        $depr = $this->config['view_depr'];
+
+        if (0 !== strpos($template, '/')) {
+            $template   = str_replace(['/', ':'], $depr, $template);
+            $controller = $request->controller();
+
+            if (strpos($controller, '.')) {
+                $pos        = strrpos($controller, '.');
+                $controller = substr($controller, 0, $pos) . '.' . Str::snake(substr($controller, $pos + 1));
+            } else {
+                $controller = Str::snake($controller);
+            }
+
+            if ($controller) {
+                if ('' == $template) {
+                    // 如果模板文件名为空 按照默认模板渲染规则定位
+                    if (2 == $this->config['auto_rule']) {
+                        $template = $request->action(true);
+                    } elseif (3 == $this->config['auto_rule']) {
+                        $template = $request->action();
+                    } else {
+                        $template = Str::snake($request->action());
+                    }
+
+                    $template = str_replace('.', DIRECTORY_SEPARATOR, $controller) . $depr . $template;
+                } elseif (false === strpos($template, $depr)) {
+                    $template = str_replace('.', DIRECTORY_SEPARATOR, $controller) . $depr . $template;
+                }
+            }
+        } else {
+            $template = str_replace(['/', ':'], $depr, substr($template, 1));
+        }
+
+        return $path . ltrim($template, '/') . '.' . ltrim($this->config['view_suffix'], '.');
+    }
+
+    /**
+     * 配置模板引擎
+     * @access private
+     * @param  array  $config 参数
+     * @return void
+     */
+    public function config(array $config): void
+    {
+        $this->template->config($config);
+        $this->config = array_merge($this->config, $config);
+    }
+
+    /**
+     * 获取模板引擎配置
+     * @access public
+     * @param  string  $name 参数名
+     * @return void
+     */
+    public function getConfig(string $name)
+    {
+        return $this->template->getConfig($name);
+    }
+
+    public function __call($method, $params)
+    {
+        return call_user_func_array([$this->template, $method], $params);
+    }
+}
diff --git a/vendor/volcengine/volc-sdk-php/.gitignore b/vendor/volcengine/volc-sdk-php/.gitignore
new file mode 100755
index 0000000..c88f952
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/.gitignore
@@ -0,0 +1,9 @@
+vendor
+composer.lock
+.idea/**
+phpunit*
+*.phar*
+*.bat*
+*.DS_Store*
+.envrc
+composer-setup.php
diff --git a/vendor/volcengine/volc-sdk-php/Changelog b/vendor/volcengine/volc-sdk-php/Changelog
new file mode 100755
index 0000000..b3420ce
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/Changelog
@@ -0,0 +1,211 @@
+Change log
+
+2022-11-10 Bumped to version v1.0.54
+- Added live service
+- Updated apis for security
+
+2022-10-27 Bumped to version v1.0.53
+- Updated apis for vod
+
+2022-10-13 Bumped to version v1.0.52
+- Updated apis for vod
+
+2022-09-15 Bumped to version v1.0.51
+- Updated apis for vod
+
+2022-09-09 Bumped to version v1.0.50
+- Added apis for billing
+
+2022-09-06 Bumped to version v1.0.49
+- Updated apis for security
+
+2022-08-25 Bumped to version v1.0.48
+- Added apis for live
+- Fixed apis for imagex
+
+2022-08-11 Bumped to version v1.0.47
+- Updated apis for vod
+
+2022-08-04 Bumped to version v1.0.46
+- Added apis for sts
+- Updated demo for rtc
+
+2022-07-28 Bumped to version v1.0.45
+- Upgraded apis for vod\rtc
+
+2022-07-25 Bumped to version v1.0.44
+- Added apis for security
+
+2022-07-07 Bumped to version v1.0.43
+- Added apis for live\rtc
+
+2022-06-23 Bumped to version v1.0.42
+- Added apis for vod
+
+2022-06-17 Bumped to version v1.0.41
+- Updated apis for vod
+- Updated demo for rtc
+
+2022-06-09 Bumped to version v1.0.40
+- Added service secertNumber
+- Fixed apis for cdn
+
+2022-05-26 Bumped to version v1.0.39
+- Added rtc service
+- Added apis for ocr
+
+2022-05-20 Bumped to version v1.0.38
+- Added notify service
+
+2022-05-12 Bumped to version v1.0.37
+- Added apis for vod\live\FaceSwap
+
+2022-04-14 Bumped to version v1.0.36 
+- Added apis for vod\cdn\imagex\security
+
+2022-04-07 Bumped to version v1.0.35
+- Added imp service
+
+2022-03-31 Bumped to version v1.0.34
+- Upgraded api for vod
+
+2022-03-18 Bumped to version v1.0.33
+- Add some apis of space\callback\cdn for vod
+
+2022-03-03 Bumped to version v1.0.32
+- BREAKING CHANGE: only accept json body as the in parameter.
+- Implement related example for apis
+- Add new apis
+- Add Video-Classification For VOD
+
+2022-02-15 Bumped to version v1.0.31
+- Fixed protobuf dependency
+
+2022-02-15 Bumped to version v1.0.30
+- Fixed sign only method 
+- Fixed guzzle version to *
+- Added mobile_status api
+
+2022-02-10 Bumped to version v1.0.29
+- 号码验证2期需求
+
+2022-01-21 Bumped to version v1.0.28
+- 点播火山引擎转码处理新增 EnableLowPriority 字段
+
+2022-01-19 Bumped to version v1.0.27
+- Fixed signing url method
+- Removed multi catch for 7.0
+
+2022-01-14 Bumped to version v1.0.26
+- Downgrade PHP to 7.0
+
+2021-12-30 Bumped to version v1.0.25
+- Refactored signing method
+
+2021-12-28 Bumped to version v1.0.24
+- Added live php sdk
+
+2021-12-16 Bumped to version v1.0.23
+- element verify
+- DescribeCdnUpperIp and describeCdnDataDetail apis
+- Fix foreach warning
+
+
+2021-11-15 Bumped to version v1.0.22
+- Updated example and remove unused formal param
+- Increased default timeouts
+
+2021-10-27 Bumped to version v1.0.21
+- Fixed Vod large file
+- Fixed HDR support for vod api
+
+2021-09-29 Bumped to version v1.0.20
+- Minor refactor
+
+2021-09-28 Bumped to version v1.0.19
+- SubmitRefreshTask
+- SubmitPreloadTask
+- DescribeContentTasks
+- DescribeContentQuota
+- DescribeCdnData
+- DescribeCdnOriginData
+- DescribeCdnRegionAndIsp
+- DescribeCdnDomainTopData
+- DescribeCdnAccountingData
+- DescribeCdnAccessLog
+- StartCdnDomain
+- StopCdnDomain
+- DeleteCdnDomain
+- ListCdnDomains
+
+2021-09-16 Bumped to version v1.0.18
+- Added sms v2 sdk
+- Added private drm for vod
+
+2021-08-05 Bumped to version v1.0.17
+- Added sms sdk
+- Added ocr sdk
+
+2021-07-22 Bumped to version v1.0.16
+- Fixed cr32
+
+2021-07-15 Bumped to version v1.0.15
+- Upgraded signer method to latest
+
+2021-07-01 Bumped to version v1.0.14
+- Added subtitle sdk
+
+2021-06-02 Bumped to version v1.0.13
+- Added async risk result
+- Updated media list
+- Added product game protect
+- Added Vod support material ext
+
+2021-05-20 Bumped to version v1.0.12
+- Add hls drm utils for vod
+- Support material upload
+- Add retry after http error
+- Add imagex image fetch API
+
+2021-04-20 Bumped to version v1.0.11
+- Added pcdn and barrageMask support.
+- Added store key res.
+
+2021-03-24 Bumped to version v1.0.10
+- Added sms sdk.
+
+2021-03-24 Bumped to version v1.0.9
+- Added async and risk result.
+- Added delete media support.
+
+2021-03-02 Bumped to version v1.0.8
+- Visual: add new apis: FaceSwap, HumanSegment, GeneralSegment, EnhancePhoto, ConvertPhoto
+
+2021-02-22 Bumped to version v1.0.7
+- Visual:add JPCartoon api.
+
+2021-02-18 Bumped to version v1.0.6
+- Upgrade adblock sdk.
+- Vod: bug fix.
+
+2021-02-04 Bumped to version v1.0.5
+- VEdit: support more cloud-edit sdk.
+
+2021-01-28 Bumped to version v1.0.4
+- Fixed sts2_ak.
+- Fixed visual null pointer.
+- more vod sdk support.
+
+2021-01-19 Bumped to version v1.0.3
+- Add adblock sdk.
+- Vod:enable large-file slice upload function.
+
+2020-12-30 Bumped to version v1.0.2
+- ImageX:add delete api.
+
+2020-12-22 Bumped to version v1.0.1
+- Add vod sdk.
+- Add ImageX sdk.
+
+2020-12-11 Bumped to version v1.0.0
+- Add iam sdk.
diff --git a/vendor/volcengine/volc-sdk-php/README.md b/vendor/volcengine/volc-sdk-php/README.md
new file mode 100755
index 0000000..844c732
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/README.md
@@ -0,0 +1,49 @@
+<h1 align="center"><img src="https://iam.volccdn.com/obj/volcengine-public/pic/volcengine-icon.png"></h1>
+<h1 align="center">火山引擎SDK for PHP</h1> 
+欢迎使用火山引擎SDK for PHP,本文档为您介绍如何获取及调用SDK。
+
+## 前置准备
+### 服务开通
+请确保您已开通了您需要访问的服务。您可前往[火山引擎控制台](https://console.volcengine.com/ ),在左侧菜单中选择或在顶部搜索栏中搜索您需要使用的服务,进入服务控制台内完成开通流程。
+### 获取安全凭证
+Access Key(访问密钥)是访问火山引擎服务的安全凭证,包含Access Key ID(简称为AK)和Secret Access Key(简称为SK)两部分。您可登录[火山引擎控制台](https://console.volcengine.com/ ),前往“[访问控制](https://console.volcengine.com/iam )”的“[访问密钥](https://console.volcengine.com/iam/keymanage/ )”中创建及管理您的Access Key。更多信息可参考[访问密钥帮助文档](https://www.volcengine.com/docs/6291/65568 )。
+###环境检查
+PHP版本需要不低于7.1。
+## 获取与安装
+推荐使用composer安装火山引擎SDK for PHP:
+```shell
+$ composer require volcengine/volc-sdk-php
+```
+## 相关配置
+### 安全凭证配置
+火山引擎SDK for PHP支持以下几种方式进行凭证管理:
+
+*注意:代码中Your AK及Your SK需要分别替换为您的AK及SK。*
+
+**方式一**:在Client中显式指定AK/SK **(推荐)**
+```php
+$client = Iam::getInstance();
+$client->setAccessKey(Your AK);
+$client->setSecretKey(Your SK);
+```
+
+**方式二**:从环境变量加载AK/SK
+  ```bash
+  VOLC_ACCESSKEY="Your AK"  
+  VOLC_SECRETKEY="Your SK"
+  ```
+**方式三**:从HOME文件加载AK/SK
+
+在本地的~/.volc/config中添加如下内容:
+  ```json
+    {
+      "ak": "Your AK",
+      "sk": "Your SK"
+    }
+  ```
+
+##其它资源
+示例参见[examples](./examples)
+
+
+
diff --git a/vendor/volcengine/volc-sdk-php/VERSION b/vendor/volcengine/volc-sdk-php/VERSION
new file mode 100755
index 0000000..77bf58c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/VERSION
@@ -0,0 +1 @@
+v1.0.54
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/composer.json b/vendor/volcengine/volc-sdk-php/composer.json
new file mode 100755
index 0000000..a61aabd
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/composer.json
@@ -0,0 +1,25 @@
+{
+  "name": "volcengine/volc-sdk-php",
+  "type": "library",
+  "license": "MIT",
+  "authors": [
+    {
+      "name": "bytedance"
+    }
+  ],
+  "require": {
+    "php": ">=7.0",
+    "guzzlehttp/guzzle": "*",
+    "ext-json": "*",
+    "google/protobuf": "*",
+    "google/gax": "*",
+    "ext-openssl": "*"
+  },
+  "autoload": {
+    "psr-4": {
+      "Volc\\": "src/",
+      "Test\\": "tests/"
+    }
+  },
+  "description": ""
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/.gitignore b/vendor/volcengine/volc-sdk-php/examples/.gitignore
new file mode 100755
index 0000000..06e7e9f
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/.gitignore
@@ -0,0 +1 @@
+LocalTest.php
diff --git a/vendor/volcengine/volc-sdk-php/examples/AdBlocker/DemoAdBlock.php b/vendor/volcengine/volc-sdk-php/examples/AdBlocker/DemoAdBlock.php
new file mode 100755
index 0000000..c4f7e57
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/AdBlocker/DemoAdBlock.php
@@ -0,0 +1,12 @@
+<?php
+require('../../vendor/autoload.php');
+
+use Volc\Service\AdBlocker;
+$client = AdBlocker::getInstance();
+
+// call below method if you dont set ak and sk in ~/.volc/config
+$client->setAccessKey("ak");
+$client->setSecretKey("sk");
+
+$response = $client->adBlock(3332, "chat", "{\"uid\":123411, \"operate_time\":1609818934, \"chat_text\":\"a😊\"}");
+echo $response;
diff --git a/vendor/volcengine/volc-sdk-php/examples/Billing/DemoListBill.php b/vendor/volcengine/volc-sdk-php/examples/Billing/DemoListBill.php
new file mode 100755
index 0000000..10d65ba
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Billing/DemoListBill.php
@@ -0,0 +1,24 @@
+<?php
+require('../../vendor/autoload.php');
+
+use Volc\Service\Billing;
+
+$client = Billing::getInstance();
+
+// call below method if you dont set ak and sk in ~/.volc/config
+$ak = "<Your AK>";
+$sk = "<Your SK>";
+$client->setAccessKey($ak);
+$client->setSecretKey($sk);
+
+$response = $client->listBill(['query' => [
+    'BillPeriod' => '2022-01',
+    'Limit' => 10,
+    'Offset' => 0,
+    'Product' => '',
+    'BillingMode' => '',
+    'BillCategoryParent' => '',
+    'PayStatus' => '',
+    'IgnoreZero' => '0',
+    'NeedRecordNum' => '0']]);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/Billing/DemoListBillDetail.php b/vendor/volcengine/volc-sdk-php/examples/Billing/DemoListBillDetail.php
new file mode 100755
index 0000000..fc658a5
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Billing/DemoListBillDetail.php
@@ -0,0 +1,26 @@
+<?php
+require('../../vendor/autoload.php');
+
+use Volc\Service\Billing;
+
+$client = Billing::getInstance();
+
+// call below method if you dont set ak and sk in ~/.volc/config
+$ak = "<Your AK>";
+$sk = "<Your SK>";
+$client->setAccessKey($ak);
+$client->setSecretKey($sk);
+
+$response = $client->listBillDetail(['query' => [
+    'BillPeriod' => '2022-01',
+    'Limit' => 10,
+    'Offset' => 0,
+    'GroupTerm' => 0,
+    'GroupPeriod' => 2,
+    'Product' => '',
+    'BillingMode' => '',
+    'BillCategory' => '',
+    'InstanceNo' => '',
+    'IgnoreZero' => '0',
+    'NeedRecordNum' => '0']]);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/Billing/DemoListBillOverviewByProd.php b/vendor/volcengine/volc-sdk-php/examples/Billing/DemoListBillOverviewByProd.php
new file mode 100755
index 0000000..42bdd63
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Billing/DemoListBillOverviewByProd.php
@@ -0,0 +1,23 @@
+<?php
+require('../../vendor/autoload.php');
+
+use Volc\Service\Billing;
+
+$client = Billing::getInstance();
+
+// call below method if you dont set ak and sk in ~/.volc/config
+$ak = "<Your AK>";
+$sk = "<Your SK>";
+$client->setAccessKey($ak);
+$client->setSecretKey($sk);
+
+$response = $client->listBillOverviewByProd(['query' => [
+    'BillPeriod' => '2022-01',
+    'Limit' => 10,
+    'Offset' => 0,
+    'Product' => '',
+    'BillingMode' => '',
+    'BillCategoryParent' => '',
+    'IgnoreZero' => '0',
+    'NeedRecordNum' => '0']]);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/BusinessSecurity/DemoRiskDetect.php b/vendor/volcengine/volc-sdk-php/examples/BusinessSecurity/DemoRiskDetect.php
new file mode 100755
index 0000000..760c260
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/BusinessSecurity/DemoRiskDetect.php
@@ -0,0 +1,36 @@
+<?php
+require('../../vendor/autoload.php');
+
+use Volc\Service\BusinessSecurity;
+$client = BusinessSecurity::getInstance();
+
+// call below method if you dont set ak and sk in ~/.volc/config
+$client->setAccessKey("ak");
+$client->setSecretKey("sk");
+
+$response = $client->RiskDetect(1, "chat", "{\"uid\":123411, \"operate_time\":1609818934, \"chat_text\":\"a😊\"}");
+echo $response;
+
+$response = $client->ElementVerify(5461, "idcard_two_element_verify", "{\"operate_time\":1609818934, \"idcard_no\": \"\", \"idcard_name\":\"\"}");
+echo $response;
+
+$response = $client->MobileStatus(5461, "mobile_empty_status", "{\"operate_time\":1609818934, \"mobile\": \"\"}");
+echo $response;
+
+$response = $client->ImageContentRisk(5461, "image_content_risk", "{\"operate_time\": 1617960951, \"ip\": \"127.0.0.9\", \"did\":1357924680, \"data_id\": \"wangyifan\", \"account_id\": \"212910\", \"type\":1,\"url\":\"待检测图片\", \"label\": \"301,302,305,351,303,310\"}");
+echo $response;
+
+$response = $client->AsyncImageRisk(5461, "image_content_risk", "{\"operate_time\": 1617960951, \"ip\": \"127.0.0.9\", \"did\":1357924680, \"data_id\": \"w\", \"account_id\": \"212910\", \"type\":1,\"url\":\"待检测图片\", \"label\": \"301,302,305,351,303,310\"}");
+echo $response;
+
+$response = $client->GetImageResult(5461, "image_content_risk", "w");
+echo $response;
+
+$response = $client->TextRisk(5461, "text_risk",  "{\"operate_time\": 1652154752, \"text\": \"待检测文本\", \"did\":1357924680, \"account_id\": \"123\"}");
+echo $response;
+
+$response = $client->GetAudioResult(334361, "audio_risk",  "70");
+echo $response;
+
+$response = $client->TextSliceRisk(415493, "text_risk",  "{}");
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoAddCdnDomain.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoAddCdnDomain.php
new file mode 100755
index 0000000..d2ed7b4
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoAddCdnDomain.php
@@ -0,0 +1,37 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'Domain' => $config->operateDomain,
+    'ServiceType' => 'web',
+    'Origin' => [
+        [
+            'OriginAction' => [
+                'OriginLines' => [
+                    [
+                        'OriginType' => 'primary',
+                        'InstanceType' => 'ip',
+                        'Address' => '1.1.1.1',
+                        'HttpPort' => '80',
+                        'HttpsPort' => '443',
+                        'Weight' => '100'
+                    ]
+                ]
+            ]
+        ]
+    ],
+    'OriginProtocol' => 'HTTP'
+];
+
+$response = $client->addCdnDomain($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoAddResourceTags.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoAddResourceTags.php
new file mode 100755
index 0000000..ca52a68
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoAddResourceTags.php
@@ -0,0 +1,22 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'Resources' => [$config->exampleDomain],
+    'ResourceTags' => [
+        ['Key' => 'userKey', 'Value' => 'userValue']
+    ]
+];
+
+$response = $client->addResourceTags($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDeleteCdnDomain.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDeleteCdnDomain.php
new file mode 100755
index 0000000..1bb3609
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDeleteCdnDomain.php
@@ -0,0 +1,19 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'Domain' => $config->operateDomain
+];
+
+$response = $client->deleteCdnDomain($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDeleteResourceTags.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDeleteResourceTags.php
new file mode 100755
index 0000000..e59f2d6
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDeleteResourceTags.php
@@ -0,0 +1,22 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'Resources' => [$config->exampleDomain],
+    'ResourceTags' => [
+        ['Key' => 'userKey', 'Value' => 'userValue']
+    ]
+];
+
+$response = $client->deleteResourceTags($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeAccountingData.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeAccountingData.php
new file mode 100755
index 0000000..1fc94df
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeAccountingData.php
@@ -0,0 +1,22 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'StartTime' => $config->startTime,
+    'EndTime' => $config->endTime,
+    'Domain' => $config->exampleDomain,
+    "Metric" => "flux",
+];
+
+$response = $client->describeAccountingData($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnAccessLog.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnAccessLog.php
new file mode 100755
index 0000000..c1c863a
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnAccessLog.php
@@ -0,0 +1,23 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'StartTime' => $config->startTime,
+    'EndTime' => $config->endTime,
+    'Domain' => $config->exampleDomain,
+    'PageSize' => 100,
+    'PageNum' => 1
+];
+
+$response = $client->describeCdnAccessLog($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnConfig.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnConfig.php
new file mode 100755
index 0000000..3668ea8
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnConfig.php
@@ -0,0 +1,19 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'Domain' => $config->exampleDomain,
+];
+
+$response = $client->describeCdnConfig($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnData.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnData.php
new file mode 100755
index 0000000..414f864
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnData.php
@@ -0,0 +1,27 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'StartTime' => $config->startTime,
+    'EndTime' => $config->endTime,
+    'Metric' => 'pv',
+    'Domain' => $config->exampleDomain,
+    'Interval' => '5min',
+    'Isp' => 'CT',
+    'Region' => 'BJ',
+    'Protocol' => 'http',
+    'IpVersion' => 'ipv4'
+];
+
+$response = $client->describeCdnData($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnDataDetail.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnDataDetail.php
new file mode 100755
index 0000000..4f6cfa1
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnDataDetail.php
@@ -0,0 +1,27 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+
+$client = Cdn::getInstance();
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'StartTime' => $config->startTime,
+    'EndTime' => $config->endTime,
+    'Domain' => $config->exampleDomain,
+    'Metric' => "bandwidth",
+    'Interval' => '5min',
+    'Protocol' => 'http',
+    'IpVersion' => 'ipv4'
+];
+
+$response = $client->describeCdnDataDetail($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnDomainTopData.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnDomainTopData.php
new file mode 100755
index 0000000..56b52fa
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnDomainTopData.php
@@ -0,0 +1,23 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'StartTime' => $config->startTime,
+    'EndTime' => $config->endTime,
+    'Metric' => 'pv',
+    'Domain' => $config->exampleDomain,
+    'Item' => 'region'
+];
+
+$response = $client->describeCdnDomainTopData($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnOriginData.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnOriginData.php
new file mode 100755
index 0000000..c9a5b06
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnOriginData.php
@@ -0,0 +1,23 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'StartTime' => $config->startTime,
+    'EndTime' => $config->endTime,
+    'Metric' => 'pv',
+    'Domain' => $config->exampleDomain,
+    'Interval' => '5min',
+];
+
+$response = $client->describeCdnOriginData($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnRegionAndIsp.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnRegionAndIsp.php
new file mode 100755
index 0000000..69febb0
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnRegionAndIsp.php
@@ -0,0 +1,19 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+
+$body = [
+    'Domain' => $config->exampleDomain,
+];
+$response = $client->describeCdnRegionAndIsp($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnService.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnService.php
new file mode 100755
index 0000000..cf6ba8c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnService.php
@@ -0,0 +1,15 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$response = $client->describeCdnService();
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnUpperIp.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnUpperIp.php
new file mode 100755
index 0000000..cd9e0c0
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeCdnUpperIp.php
@@ -0,0 +1,19 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'Domain' => $config->exampleDomain,
+];
+
+$response = $client->describeCdnUpperIp(['json' => $body]);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeContentBlockTasks.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeContentBlockTasks.php
new file mode 100755
index 0000000..25bf862
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeContentBlockTasks.php
@@ -0,0 +1,21 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'TaskType' => 'block_url',
+    'StartTime' => $config->startTime,
+    'EndTime' => $config->endTime,
+];
+
+$response = $client->describeContentBlockTasks($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeContentQuota.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeContentQuota.php
new file mode 100755
index 0000000..b8f1eed
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeContentQuota.php
@@ -0,0 +1,15 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$response = $client->describeContentQuota();
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeContentTasks.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeContentTasks.php
new file mode 100755
index 0000000..c1e53ec
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeContentTasks.php
@@ -0,0 +1,20 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    "PageNum" => 1,
+    'PageSize' => 10,
+    'TaskType' => 'refresh_file'
+];
+$response = $client->describeContentTasks($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeEdgeNrtDataSummary.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeEdgeNrtDataSummary.php
new file mode 100755
index 0000000..6e490d6
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeEdgeNrtDataSummary.php
@@ -0,0 +1,29 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'StartTime' => $config->startTime,
+    'EndTime' => $config->endTime,
+    'Metric' => 'pv',
+    'Domain' => $config->exampleDomain,
+    'Interval' => '5min',
+    'Area' => 'China',
+    "Isp" => 'CT',
+    'Region' => 'BJ',
+    'Protocol' => 'http',
+    'IpVersion' => 'ipv4',
+    'Aggregate' => 'aggregate'
+];
+
+$response = $client->describeEdgeNrtDataSummary($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeEdgeStatisticalData.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeEdgeStatisticalData.php
new file mode 100755
index 0000000..5571a04
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeEdgeStatisticalData.php
@@ -0,0 +1,26 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'StartTime' => $config->startTime,
+    'EndTime' => $config->endTime,
+    'Metric' => 'clientIp',
+    'Domain' => $config->exampleDomain,
+    'Interval' => 'hour',
+    'Area' => 'China',
+    'Region' => 'BJ',
+    'IpVersion' => 'ipv4'
+];
+
+$response = $client->describeEdgeStatisticalData($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeEdgeTopNrtData.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeEdgeTopNrtData.php
new file mode 100755
index 0000000..a1b550d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeEdgeTopNrtData.php
@@ -0,0 +1,25 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'StartTime' => $config->startTime,
+    'EndTime' => $config->endTime,
+    'Metric' => 'pv',
+    'Item' => 'isp',
+    'Domain' => $config->exampleDomain,
+    'Interval' => '5min',
+    'Area' => 'China',
+];
+
+$response = $client->describeEdgeTopNrtData($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeEdgeTopStatisticalData.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeEdgeTopStatisticalData.php
new file mode 100755
index 0000000..e02d117
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeEdgeTopStatisticalData.php
@@ -0,0 +1,24 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'StartTime' => $config->startTime,
+    'EndTime' => $config->endTime,
+    'Metric' => 'pv',
+    'Item' => 'url',
+    'Domain' => $config->exampleDomain,
+    'Area' => 'China',
+];
+
+$response = $client->describeEdgeTopStatisticalData($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeEdgeTopStatusCode.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeEdgeTopStatusCode.php
new file mode 100755
index 0000000..7ace23c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeEdgeTopStatusCode.php
@@ -0,0 +1,20 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'Metric' => 'status_5xx',
+    'Item' => 'domain',
+];
+
+$response = $client->describeEdgeTopStatusCode($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeIPInfo.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeIPInfo.php
new file mode 100755
index 0000000..0c3ed31
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeIPInfo.php
@@ -0,0 +1,19 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'IP' => '1.1.1.1'
+];
+
+$response = $client->describeIPInfo($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeOriginNrtDataSummary.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeOriginNrtDataSummary.php
new file mode 100755
index 0000000..4888c8d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeOriginNrtDataSummary.php
@@ -0,0 +1,24 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'StartTime' => $config->startTime,
+    'EndTime' => $config->endTime,
+    'Metric' => 'pv',
+    'Domain' => $config->exampleDomain,
+    'Interval' => '5min',
+    'Aggregate' => 'aggregate'
+];
+
+$response = $client->describeOriginNrtDataSummary($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeOriginTopNrtData.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeOriginTopNrtData.php
new file mode 100755
index 0000000..8adac2f
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeOriginTopNrtData.php
@@ -0,0 +1,22 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'StartTime' => $config->startTime,
+    'EndTime' => $config->endTime,
+    'Metric' => 'flux',
+    'Item' => 'domain',
+];
+
+$response = $client->describeOriginTopNrtData($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeOriginTopStatusCode.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeOriginTopStatusCode.php
new file mode 100755
index 0000000..b78f43b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoDescribeOriginTopStatusCode.php
@@ -0,0 +1,20 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'Metric' => 'status_5xx',
+    'Item' => 'domain',
+];
+
+$response = $client->describeOriginTopStatusCode($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoListCdnDomains.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoListCdnDomains.php
new file mode 100755
index 0000000..fb4cad4
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoListCdnDomains.php
@@ -0,0 +1,22 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'Domain' => $config->exampleDomain,
+    'ServiceType' => 'web',
+    'PageNum' => 1,
+    'PageSize' => 10
+];
+
+$response = $client->listCdnDomains($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoListResourceTags.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoListResourceTags.php
new file mode 100755
index 0000000..c11d1df
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoListResourceTags.php
@@ -0,0 +1,15 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$response = $client->listResourceTags();
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoStartCdnDomain.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoStartCdnDomain.php
new file mode 100755
index 0000000..0dabe92
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoStartCdnDomain.php
@@ -0,0 +1,19 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'Domain' => $config->exampleDomain,
+];
+
+$response = $client->startCdnDomain($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoStopCdnDomain.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoStopCdnDomain.php
new file mode 100755
index 0000000..2f6ac6b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoStopCdnDomain.php
@@ -0,0 +1,19 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'Domain' => $config->operateDomain,
+];
+
+$response = $client->stopCdnDomain($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoSubmitBlockTask.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoSubmitBlockTask.php
new file mode 100755
index 0000000..3e68765
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoSubmitBlockTask.php
@@ -0,0 +1,20 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+
+$body = [
+    'Urls' => sprintf('http://%s/1.txt\nhttp://%s/2.jpg', $config->exampleDomain, $config->exampleDomain),
+];
+
+$response = $client->submitBlockTask($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoSubmitPreloadTask.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoSubmitPreloadTask.php
new file mode 100755
index 0000000..89fa416
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoSubmitPreloadTask.php
@@ -0,0 +1,19 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'Urls' => sprintf('http://%s/1.txt\nhttp://%s/2.jpg', $config->exampleDomain, $config->exampleDomain),
+];
+
+$response = $client->submitPreloadTask($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoSubmitRefreshTask.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoSubmitRefreshTask.php
new file mode 100755
index 0000000..5e2ad52
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoSubmitRefreshTask.php
@@ -0,0 +1,20 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'Type' => 'file',
+    'Urls' => sprintf('http://%s/1.txt\nhttp://%s/2.jpg', $config->exampleDomain, $config->exampleDomain),
+];
+
+$response = $client->submitRefreshTask($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoSubmitUnblockTask.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoSubmitUnblockTask.php
new file mode 100755
index 0000000..47a725d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoSubmitUnblockTask.php
@@ -0,0 +1,19 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'Urls' => sprintf('http://%s/1.txt\nhttp://%s/2.jpg', $config->exampleDomain, $config->exampleDomain),
+];
+
+$response = $client->submitUnblockTask($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoUpdateCdnConfig.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoUpdateCdnConfig.php
new file mode 100755
index 0000000..cbd1569
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoUpdateCdnConfig.php
@@ -0,0 +1,36 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+$body = [
+    'Domain' => $config->exampleDomain,
+    'Origin' => [
+        [
+            'OriginAction' => [
+                'OriginLines' => [
+                    [
+                        'OriginType' => 'primary',
+                        'InstanceType' => 'ip',
+                        'Address' => '1.1.1.1',
+                        'HttpPort' => '80',
+                        'HttpsPort' => '443',
+                        'Weight' => '100'
+                    ]
+                ]
+            ]
+        ]
+    ],
+    'OriginProtocol' => 'HTTP'
+];
+
+$response = $client->updateCdnConfig($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoUpdateResourceTags.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoUpdateResourceTags.php
new file mode 100755
index 0000000..c14d399
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/DemoUpdateResourceTags.php
@@ -0,0 +1,23 @@
+<?php
+
+use Volc\Service\Cdn;
+
+require('../../vendor/autoload.php');
+require_once "init.php";
+
+$client = Cdn::getInstance();
+
+$config = new Config();
+$client->setAccessKey($config->ak);
+$client->setSecretKey($config->sk);
+
+
+$body = [
+    'Resources' => [$config->exampleDomain],
+    'ResourceTags' => [
+        ['Key' => 'userKey', 'Value' => 'userValue']
+    ]
+];
+
+$response = $client->updateResourceTags($body);
+var_dump($response);
diff --git a/vendor/volcengine/volc-sdk-php/examples/Cdn/init.php b/vendor/volcengine/volc-sdk-php/examples/Cdn/init.php
new file mode 100755
index 0000000..4e05258
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Cdn/init.php
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * @property string $ak
+ * @property string $sk
+ * @property string $operateDomain
+ * @property int $startTime
+ * @property int $endTime
+ * @property string $exampleDomain
+ */
+class Config {
+
+    public function __construct() {
+        $this->ak = "ak";
+        $this->sk = "sk";
+        $this->operateDomain = 'operate.com';
+        $this->exampleDomain = 'example.com';
+        $this->startTime = time();
+        $this->endTime = time() + 60*10;
+    }
+}
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/DemoIam.php b/vendor/volcengine/volc-sdk-php/examples/DemoIam.php
new file mode 100755
index 0000000..ed274e2
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/DemoIam.php
@@ -0,0 +1,21 @@
+<?php
+require('../vendor/autoload.php');
+use Volc\Service\Iam;
+
+$client = Iam::getInstance();
+// call below method if you dont set ak and sk in ~/.volc/config
+
+$client->setAccessKey($ak);
+$client->setSecretKey($sk);
+
+echo "\nDemo 1\n";
+$response = $client->listUsers([]);
+echo $response;
+
+echo "\nDemo 2\n";
+$response = $client->listUsers(['query'=>['Limit'=>10, 'Offset'=>0]]);
+echo $response;
+
+echo "\nDemo 3\n";
+$response = $client->listUsers(['v4_credentials'=>['ak'=>"$ak", 'sk'=>$sk], 'query'=>['Limit'=>10, 'Offset'=>0]]);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/DemoSign.php b/vendor/volcengine/volc-sdk-php/examples/DemoSign.php
new file mode 100755
index 0000000..e07c336
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/DemoSign.php
@@ -0,0 +1,23 @@
+<?php
+require('../vendor/autoload.php');
+
+$sign = new \Volc\Base\SignatureV4();
+$credentials = [];
+$credentials['region'] = 'cn-north-1';
+$credentials['service'] = 'iam';
+$credentials['ak'] = $ak;
+$credentials['sk'] = $sk;
+
+$req = new \Volc\Base\Model\SignParam();
+$req->setDate(new DateTime("20211110T201554Z"));
+$req->setHeaderList(["Accept"=>["application/json"], "Host"=>["open.volcengineapi.com"]]);
+$req->setHost("open.volcengineapi.com");
+$req->setPath("/");
+$req->setIsSignUrl(false);
+$req->setMethod("GET");
+$req->setQueryList(["Action"=>["ListUsers"], "Version"=>["2018-01-01"], "Limit"=>["10"], "Offset"=>["0"]]);
+$s = Utils::streamFor("");
+$req->setPyloadHash(Utils::hash($s,"sha256"));
+
+$resp = $sign->signOnly($req, $credentials);
+echo $resp;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/GameProtect/DemoRiskResult.php b/vendor/volcengine/volc-sdk-php/examples/GameProtect/DemoRiskResult.php
new file mode 100755
index 0000000..a51ee92
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/GameProtect/DemoRiskResult.php
@@ -0,0 +1,14 @@
+<?php
+
+require('../../vendor/autoload.php');
+
+use Volc\Service\GameProduct;
+
+$client = GameProduct::getInstance();
+
+// call below method if you dont set ak and sk in ~/.volc/config
+$client->setAccessKey("ak");
+$client->setSecretKey("sk");
+
+$response = $client->riskResult(218745, 1618502400, 1618545491, 10, 1);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/ImageX/DemoDeleteImages.php b/vendor/volcengine/volc-sdk-php/examples/ImageX/DemoDeleteImages.php
new file mode 100755
index 0000000..0a41d20
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/ImageX/DemoDeleteImages.php
@@ -0,0 +1,16 @@
+<?php
+require('../../vendor/autoload.php');
+
+use Volc\Service\ImageX;
+
+$client = ImageX::getInstance();
+
+// call below method if you dont set ak and sk in ~/.volc/config
+$client->setAccessKey("ak");
+$client->setSecretKey("sk");
+
+$serviceID = "imagex service id";
+$uris = ["image uri 1", "image uri 2"];
+
+$response = $client->deleteImages($serviceID, $uris);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/ImageX/DemoFetchUrl.php b/vendor/volcengine/volc-sdk-php/examples/ImageX/DemoFetchUrl.php
new file mode 100755
index 0000000..0ee7bbb
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/ImageX/DemoFetchUrl.php
@@ -0,0 +1,20 @@
+<?php
+require('../../vendor/autoload.php');
+
+use Volc\Service\ImageX;
+
+$client = ImageX::getInstance();
+
+// call below method if you dont set ak and sk in ~/.volc/config
+$client->setAccessKey("ak");
+$client->setSecretKey("sk");
+
+$config = [
+    "json" => [
+        'Url' => 'image url',
+        'ServiceId' => 'imagex service id',
+    ],
+];
+
+$response = $client->requestImageX("FetchImageUrl", $config);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/ImageX/DemoGetImageOCR.php b/vendor/volcengine/volc-sdk-php/examples/ImageX/DemoGetImageOCR.php
new file mode 100755
index 0000000..385d3a0
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/ImageX/DemoGetImageOCR.php
@@ -0,0 +1,17 @@
+<?php
+require('../../vendor/autoload.php');
+use Volc\Service\ImageX;
+
+$client = ImageX::getInstance();
+
+// call below method if you dont set ak and sk in ~/.volc/config
+$client->setAccessKey("xx");
+$client->setSecretKey("xx");
+
+$params = array();
+$params["ServiceId"] = "xx";
+$params["StoreUri"] = "xx";
+$params["Scene"] = "license";
+
+$response = $client->getImageOCR($params);
+print_r($response);
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/ImageX/DemoGetImageSegment.php b/vendor/volcengine/volc-sdk-php/examples/ImageX/DemoGetImageSegment.php
new file mode 100755
index 0000000..566c0f4
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/ImageX/DemoGetImageSegment.php
@@ -0,0 +1,26 @@
+<?php
+require('../../vendor/autoload.php');
+use Volc\Service\ImageX;
+
+$client = ImageX::getInstance();
+
+// call below method if you dont set ak and sk in ~/.volc/config
+$client->setAccessKey("xx");
+$client->setSecretKey("xx");
+
+$Contour = array(
+    'Color' => 'color',
+    'Size' => 0,
+);
+$params = array(
+    "Contour" => $Contour,
+);
+$params["ServiceId"] = "xx";
+$params["Class"] = "class";
+$params["Refine"] = true;
+$params["StoreUri"] = "xx";
+$params["OutFormat"] = "out format";
+$params["TransBg"] = true;
+
+$response = $client->getSegmentImage($params);
+print_r($response);
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/ImageX/DemoGetUploadToken.php b/vendor/volcengine/volc-sdk-php/examples/ImageX/DemoGetUploadToken.php
new file mode 100755
index 0000000..7e28391
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/ImageX/DemoGetUploadToken.php
@@ -0,0 +1,20 @@
+<?php
+require('../../vendor/autoload.php');
+
+use Volc\Service\ImageX;
+
+$xclient = ImageX::getInstance();
+
+// call below method if you dont set ak and sk in ~/.volc/config
+$xclient->setAccessKey("ak");
+$xclient->setSecretKey("sk");
+
+$serviceIDList = [];
+$response = $xclient->getUploadAuth($serviceIDList, 3600);
+echo json_encode($response);
+
+echo "\n=======================\n";
+
+$query = ['query' => ['ServiceId' => "imagex service id"]];
+$response = $xclient->getUploadAuthToken($query);
+echo $response;
diff --git a/vendor/volcengine/volc-sdk-php/examples/ImageX/DemoRequest.php b/vendor/volcengine/volc-sdk-php/examples/ImageX/DemoRequest.php
new file mode 100755
index 0000000..b0d4f39
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/ImageX/DemoRequest.php
@@ -0,0 +1,19 @@
+<?php
+require('../../vendor/autoload.php');
+
+use Volc\Service\ImageX;
+
+$client = ImageX::getInstance();
+
+// call below method if you dont set ak and sk in ~/.volc/config
+$client->setAccessKey("ak");
+$client->setSecretKey("sk");
+
+$config = [
+    "query" => [],  // api query param
+    "json" => [], // api json param
+];
+
+// ImageX api doc: https://www.volcengine.cn/docs/508/14106
+$response = $client->requestImageX("Action", $config);
+echo $response;
diff --git a/vendor/volcengine/volc-sdk-php/examples/ImageX/DemoUpdateImageUrls.php b/vendor/volcengine/volc-sdk-php/examples/ImageX/DemoUpdateImageUrls.php
new file mode 100755
index 0000000..1606d9c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/ImageX/DemoUpdateImageUrls.php
@@ -0,0 +1,16 @@
+<?php
+require('../../vendor/autoload.php');
+
+use Volc\Service\ImageX;
+
+$client = ImageX::getInstance();
+
+// call below method if you dont set ak and sk in ~/.volc/config
+$client->setAccessKey("ak");
+$client->setSecretKey("sk");
+
+$serviceID = "imagex service id";
+$urls = ["image url 1"];
+
+$resp = $client->updateImageUrls($serviceID, $urls);
+var_dump($resp);
diff --git a/vendor/volcengine/volc-sdk-php/examples/ImageX/DemoUpload.php b/vendor/volcengine/volc-sdk-php/examples/ImageX/DemoUpload.php
new file mode 100755
index 0000000..7cf2213
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/ImageX/DemoUpload.php
@@ -0,0 +1,17 @@
+<?php
+require('../../vendor/autoload.php');
+
+use Volc\Service\ImageX;
+
+$client = ImageX::getInstance();
+
+// call below method if you dont set ak and sk in ~/.volc/config
+$client->setAccessKey("ak");
+$client->setSecretKey("sk");
+
+$params = array();
+$params["ServiceId"] = "imagex service id";
+$filePaths = array("image path 1");
+
+$response = $client->uploadImages($params, $filePaths);
+echo $response;
diff --git a/vendor/volcengine/volc-sdk-php/examples/Imp/job/DemoKillJob.php b/vendor/volcengine/volc-sdk-php/examples/Imp/job/DemoKillJob.php
new file mode 100755
index 0000000..80953d2
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Imp/job/DemoKillJob.php
@@ -0,0 +1,31 @@
+<?php
+
+require('../../../vendor/autoload.php');
+
+use Volc\Service\Imp\Models\Request\ImpKillJobRequest;
+use Volc\Service\Imp\Models\Response\ImpKillJobResponse;
+use Volc\Service\Imp\Imp;
+
+// call below method if you don't set ak and sk in $HOME/.vcloud/config
+$client = Imp::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+$request = new ImpKillJobRequest();
+
+$request->setJobId("your job id");
+
+$response = new ImpKillJobResponse();
+try {
+    $response = $client->KillJob($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response->getResponseMetadata()->getError() != null) {
+    print_r($response->getResponseMetadata()->getError());
+}
+
+echo $response->serializeToJsonString();
+echo "\n";
diff --git a/vendor/volcengine/volc-sdk-php/examples/Imp/job/DemoRetrieveJob.php b/vendor/volcengine/volc-sdk-php/examples/Imp/job/DemoRetrieveJob.php
new file mode 100755
index 0000000..6e4d93f
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Imp/job/DemoRetrieveJob.php
@@ -0,0 +1,32 @@
+<?php
+
+require('../../../vendor/autoload.php');
+
+use Volc\Service\Imp\Models\Request\ImpRetrieveJobRequest;
+use Volc\Service\Imp\Models\Response\ImpRetrieveJobResponse;
+use Volc\Service\Imp\Imp;
+
+// call below method if you don't set ak and sk in $HOME/.vcloud/config
+$client = Imp::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+$request = new ImpRetrieveJobRequest();
+
+$jobIds = ["your job id 1", "your job id 2"];
+$request->setJobIds($jobIds);
+
+$response = new ImpRetrieveJobResponse();
+try {
+    $response = $client->RetrieveJob($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response->getResponseMetadata()->getError() != null) {
+    print_r($response->getResponseMetadata()->getError());
+}
+
+echo $response->serializeToJsonString();
+echo "\n";
diff --git a/vendor/volcengine/volc-sdk-php/examples/Imp/job/DemoSubmitJob.php b/vendor/volcengine/volc-sdk-php/examples/Imp/job/DemoSubmitJob.php
new file mode 100755
index 0000000..1f2c292
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Imp/job/DemoSubmitJob.php
@@ -0,0 +1,41 @@
+<?php
+
+require('../../../vendor/autoload.php');
+
+use Volc\Service\Imp\Models\Request\ImpSubmitJobRequest;
+use Volc\Service\Imp\Models\Response\ImpSubmitJobResponse;
+use Volc\Service\Imp\Models\Business\InputPath;
+use Volc\Service\Imp\Imp;
+
+// call below method if you don't set ak and sk in $HOME/.vcloud/config
+$client = Imp::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+$request = new ImpSubmitJobRequest();
+
+
+$inputPath = new InputPath();
+$inputPath->setType("VOD");
+$inputPath->setVodSpaceName("your space name");
+$inputPath->setFileId("your file id");
+
+$request->setTemplateId("your template id");
+$request->setCallbackArgs("your callback args");
+$request->setEnableLowPriority("false"); // true 开启 false 不开启 闲时转码
+$request->setInputPath($inputPath);
+
+$response = new ImpSubmitJobResponse();
+try {
+    $response = $client->SubmitJob($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response->getResponseMetadata()->getError() != null) {
+    print_r($response->getResponseMetadata()->getError());
+}
+
+echo $response->serializeToJsonString();
+echo "\n";
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoAuth.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoAuth.php
new file mode 100755
index 0000000..821ceef
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoAuth.php
@@ -0,0 +1,66 @@
+<?php
+
+include 'DemoBase.php';
+global $client;
+
+$body = [
+    'Domain' => 'domain',
+    'SceneType' => 'push',
+    "AuthDetailList" => [
+        [
+            "EncryptionAlgorithm" => "md5",
+            "SecretKey" => "xx",
+        ],
+    ],
+];
+
+$response = $client->updateAuthKey(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    'Domain' => 'domain',
+    'SceneType' => 'push'
+];
+
+$response = $client->describeAuth(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    'Domain' => 'domain',
+    'App' => 'app',
+    'RefererInfoList' => [
+        [
+            'Key' => 'asd',
+            'Type' => 'xx',
+            'Value' => 'sad',
+            'Priority' => 0,
+        ],
+    ],
+];
+
+$response = $client->updateReferer(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    'Domain' => 'domain',
+    'App' => 'app',
+];
+
+$response = $client->describeReferer(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    'Vhost' => 'vv',
+    'App' => 'app',
+];
+
+$response = $client->deleteReferer(['json' => $body]);
+echo $response;
+echo '<br>';
+
+
+
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoBase.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoBase.php
new file mode 100755
index 0000000..e9c2fd8
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoBase.php
@@ -0,0 +1,12 @@
+<?php
+
+use Volc\Service\Live;
+
+require('../../vendor/autoload.php');
+
+$client = Live::getInstance();
+
+$ak = '';
+$sk = '';
+$client->setAccessKey($ak);
+$client->setSecretKey($sk);
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoCallback.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoCallback.php
new file mode 100755
index 0000000..0c433d4
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoCallback.php
@@ -0,0 +1,33 @@
+<?php
+
+include 'DemoBase.php';
+global $client;
+
+$body = [
+    'MessageType' => 'record',
+    'Vhost' => 'vhost',
+    'CallbackDetailList' => [],
+];
+
+$response = $client->updateCallback(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    'MessageType' => 'record',
+    'Domain' => 'domain',
+    'App' => 'app',
+];
+
+$response = $client->describeCallback(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    'MessageType' => 'record',
+    'Vhost' => 'vhost1',
+];
+
+$response = $client->deleteCallback(['json' => $body]);
+echo $response;
+echo '<br>';
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoCert.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoCert.php
new file mode 100755
index 0000000..ef7f10b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoCert.php
@@ -0,0 +1,58 @@
+<?php
+
+include 'DemoBase.php';
+global $client;
+
+$body = [
+    'Available' => true,
+    'Expiring' => true,
+    'Domain' => 'dd',
+];
+
+$response = $client->listCert(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    'UseWay' => 'sign',
+    'CertName' => 'asd',
+];
+
+$response = $client->createCert(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    'UseWay' => 'sign',
+    'ChainID' => 'xxx',
+];
+
+$response = $client->updateCert(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    'ChainID' => 'xxx',
+];
+
+$response = $client->deleteCert(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    'Domain' => 'do',
+    'CertDomain'=>'',
+    'ChainID'=> 'x',
+];
+$response = $client->bindCert(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    'Domain' => 'do'
+];
+$response = $client->unbindCert(['json' => $body]);
+echo $response;
+echo '<br>';
+
+
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoCreatePullToPushTask.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoCreatePullToPushTask.php
new file mode 100755
index 0000000..43f6880
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoCreatePullToPushTask.php
@@ -0,0 +1,35 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: createPullToPushTask
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Live\Live::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Live\Models\Request\CreatePullToPushTaskRequest();
+$request->setTitle("your Title");
+$request->setStartTime(0);
+$request->setEndTime(0);
+$request->setCallbackURL("your CallbackURL");
+$request->setType(0);
+$request->setCycleMode(0);
+$request->setDstAddr("your DstAddr");
+$request->setSrcAddr("your SrcAddr");
+$request->setSrcAddrS([new Volc\Service\Live\Models\Business\SrcAddrS()]);
+
+
+$response = new Volc\Service\Live\Models\Response\CreatePullToPushTaskResponse();
+try {
+    $response = $client->createPullToPushTask($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoCreateVQScoreTask.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoCreateVQScoreTask.php
new file mode 100755
index 0000000..62e7c6d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoCreateVQScoreTask.php
@@ -0,0 +1,31 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: createVQScoreTask
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Live\Live::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Live\Models\Request\CreateVQScoreTaskRequest();
+$request->setMainAddr("your MainAddr");
+$request->setContrastAddr("your ContrastAddr");
+$request->setFrameInterval(0);
+$request->setDuration(0);
+$request->setAlgorithm("your Algorithm");
+
+
+$response = new Volc\Service\Live\Models\Response\CreateVQScoreTaskResponse();
+try {
+    $response = $client->createVQScoreTask($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoDeletePullToPushTask.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoDeletePullToPushTask.php
new file mode 100755
index 0000000..5957caa
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoDeletePullToPushTask.php
@@ -0,0 +1,27 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: deletePullToPushTask
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Live\Live::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Live\Models\Request\DeletePullToPushTaskRequest();
+$request->setTaskId("your TaskId");
+
+
+$response = new Volc\Service\Live\Models\Response\DeletePullToPushTaskResponse();
+try {
+    $response = $client->deletePullToPushTask($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoDeleteRelaySourceV2.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoDeleteRelaySourceV2.php
new file mode 100755
index 0000000..a0121a3
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoDeleteRelaySourceV2.php
@@ -0,0 +1,28 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: deleteRelaySourceV2
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Live\Live::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Live\Models\Request\DeleteRelaySourceRequest();
+$request->setVhost("your Vhost");
+$request->setApp("your App");
+
+
+$response = new Volc\Service\Live\Models\Response\DeleteRelaySourceResponse();
+try {
+    $response = $client->deleteRelaySourceV2($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeCDNSnapshotHistory.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeCDNSnapshotHistory.php
new file mode 100755
index 0000000..b5504eb
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeCDNSnapshotHistory.php
@@ -0,0 +1,34 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: DescribeCDNSnapshotHistory
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Live\Live::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Live\Models\Request\DescribeCDNSnapshotHistoryRequest();
+$request->setVhost("your Vhost");
+$request->setApp("your App");
+$request->setStream("your Stream");
+$request->setDateFrom("your DateFrom");
+$request->setDateTo("your DateTo");
+$request->setPageNum(0);
+$request->setPageSize(0);
+$request->setType("your Type");
+
+
+$response = new Volc\Service\Live\Models\Response\DescribeCDNSnapshotHistoryResponse();
+try {
+    $response = $client->DescribeCDNSnapshotHistory($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeDenyConfig.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeDenyConfig.php
new file mode 100755
index 0000000..28c86da
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeDenyConfig.php
@@ -0,0 +1,29 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: describeDenyConfig
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Live\Live::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Live\Models\Request\DescribeDenyConfigRequest();
+$request->setVhost("your Vhost");
+$request->setDomain("your Domain");
+$request->setApp("your App");
+
+
+$response = new Volc\Service\Live\Models\Response\DescribeDenyConfigResponse();
+try {
+    $response = $client->describeDenyConfig($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeForbiddenStreamInfoByPage.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeForbiddenStreamInfoByPage.php
new file mode 100755
index 0000000..2ec8763
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeForbiddenStreamInfoByPage.php
@@ -0,0 +1,32 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: DescribeForbiddenStreamInfoByPage
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Live\Live::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Live\Models\Request\DescribeForbiddenStreamInfoByPageRequest();
+$request->setPageNum(0);
+$request->setPageSize(0);
+$request->setVhost("your Vhost");
+$request->setApp("your App");
+$request->setStream("your Stream");
+$request->setSort("your Sort");
+
+
+$response = new Volc\Service\Live\Models\Response\DescribeForbiddenStreamInfoByPageResponse();
+try {
+    $response = $client->DescribeForbiddenStreamInfoByPage($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeInfo.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeInfo.php
new file mode 100755
index 0000000..336c4a6
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeInfo.php
@@ -0,0 +1,198 @@
+<?php
+
+include 'DemoBase.php';
+global $client;
+
+$body = [
+    "DomainList" => ["example.com", "example2.com"],
+    "StartTime" => "2022-04-13T00:00:00Z",
+    "EndTime" => "2022-04-14T00:00:00Z",
+    "Aggregation" => 300,
+];
+
+$response = $client->describeLiveBandwidthData(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    "DomainList" => ["example.com", "example2.com"],
+    "StartTime" => "2022-04-13T00:00:00Z",
+    "EndTime" => "2022-04-14T00:00:00Z",
+    "Aggregation" => 300,
+];
+
+$response = $client->describeLiveTrafficData(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    "StartTime" => "2022-04-13T00:00:00Z",
+    "EndTime" => "2022-04-14T00:00:00Z",
+    "Aggregation" => 300,
+];
+
+$response = $client->describeLiveP95PeakBandwidthData(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    "DomainList" => ["example.com", "example2.com"],
+    "StartTime" => "2022-04-13T00:00:00Z",
+    "EndTime" => "2022-04-13T00:10:00Z",
+    "Aggregation" => 300,
+];
+
+$response = $client->describeRecordData(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    "DomainList" => ["example.com", "example2.com"],
+    "StartTime" => "2022-03-07T00:00:00+08:00",
+    "EndTime" => "2022-03-08T00:00:00+08:00",
+    "Aggregation" => 86400,
+];
+
+$response = $client->describeTranscodeData(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    "DomainList" => ["example.com", "example2.com"],
+    "StartTime" => "2022-03-07T00:00:00+08:00",
+    "EndTime" => "2022-03-08T00:00:00+08:00",
+    "Aggregation" => 86400,
+];
+
+$response = $client->describeSnapshotData(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    "DomainList" => "example.com,example2.com",
+    "StartTime" => "2022-04-21T14:00:00+08:00",
+    "EndTime" => "2022-04-22T14:00:00+08:00",
+];
+
+$response = $client->describeLiveDomainLog(['query' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    "Domain" => "example.com",
+    "App" => "example_app",
+    "Stream" => "example_stream",
+    "StartTime" => "2022-04-21T00:00:00Z",
+    "EndTime" => "2022-04-23T23:59:59Z",
+    "Aggregation" => 30,
+];
+
+$response = $client->describePushStreamMetrics(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    "Domain" => "example.com",
+    "App" => "example",
+    "Stream" => "example080601",
+    "ProtocolList" => ["RTMP", "HTTP-FLV"],
+    "StartTime" => "2022-04-21T00:00:00Z",
+    "EndTime" => "2022-04-21T10:59:59Z",
+    "Aggregation" => 60,
+];
+
+$response = $client->describeLiveStreamSessions(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    "DomainList" => ["example.com", "example2.com"],
+    "StartTime" => "2021-08-16T00:00:00Z",
+    "EndTime" => "2021-08-16T00:01:59Z",
+    "Aggregation" => 60
+];
+
+$response = $client->describePlayResponseStatusStat(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    "DomainList" => ["example.com"],
+    "ProtocolList" => ["HTTP-FLV", "RTMP"],
+    "ISPList" => ["telecom"],
+    "IPList" => ["123.123.123.000"],
+    "RegionList" => [
+        [
+            "Area" => "CN",
+            "Country" => "CN",
+            "Province" => "beijing",
+        ],
+    ],
+    "StartTime" => "2022-04-13T00:00:00+08:00",
+    "EndTime" => "2022-04-13T12:00:00+08:00",
+    "Aggregation" => 300,
+    "ShowDetail" => True,
+];
+
+$response = $client->describeLiveMetricTrafficData(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    "DomainList" => ["example.com"],
+    "ProtocolList" => ["HTTP-FLV", "RTMP"],
+    "ISPList" => ["telecom"],
+    "IPList" => ["123.123.123.000"],
+    "RegionList" => [
+        [
+            "Area" => "CN",
+            "Country" => "CN",
+            "Province" => "beijing",
+        ]
+    ],
+    "StartTime" => "2021-04-13T00:00:00+08:00",
+    "EndTime" => "2021-04-14T00:00:00+08:00",
+    "Aggregation" => 300,
+    "ShowDetail" => True,
+];
+
+$response = $client->describeLiveMetricBandwidthData(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    "Domain" => "example.com",
+    "StartTime" => "2022-04-13T00:00:00+08:00",
+    "EndTime" => "2022-04-14T00:00:00+08:00",
+];
+
+$response = $client->describePlayStreamList(['query' => $body]);
+echo $response;
+echo '<br>';
+
+
+$body = [
+    "DomainList" => ["example.com"],
+    "DstAddrTypeList" => ["live","Third"],
+    "StartTime" => "2021-04-13T00:00:00+08:00",
+    "EndTime" => "2021-04-14T00:00:00+08:00",
+    "Aggregation" => 300,
+    "ShowDetail" => True,
+];
+
+$response = $client->describePullToPushBandwidthData(['json' => $body]);
+echo $response;
+echo '<br>';
+
+
+$body = [
+    "DomainList" => ["example.com", "example2.com"],
+    "DetailField" => ["Domain"],
+    "StartTime" => "2022-07-13T00:00:00+08:00",
+    "EndTime" => "2022-07-14T00:00:00+08:00",
+    "Aggregation" => 86400,
+];
+
+$response = $client->describeLiveAuditData(['json' => $body]);
+echo $response;
+echo '<br>';
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeLiveStreamInfoByPage.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeLiveStreamInfoByPage.php
new file mode 100755
index 0000000..f12c728
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeLiveStreamInfoByPage.php
@@ -0,0 +1,34 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: DescribeLiveStreamInfoByPage
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Live\Live::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Live\Models\Request\DescribeLiveStreamInfoByPageRequest();
+$request->setPageNum(0);
+$request->setPageSize(0);
+$request->setVhost("your Vhost");
+$request->setDomain("your Domain");
+$request->setApp("your App");
+$request->setStream("your Stream");
+$request->setStreamType("your StreamType");
+$request->setInfoType("your InfoType");
+
+
+$response = new Volc\Service\Live\Models\Response\DescribeLiveStreamInfoByPageResponse();
+try {
+    $response = $client->DescribeLiveStreamInfoByPage($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeLiveStreamState.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeLiveStreamState.php
new file mode 100755
index 0000000..12ee8e2
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeLiveStreamState.php
@@ -0,0 +1,30 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: DescribeLiveStreamState
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Live\Live::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Live\Models\Request\DescribeLiveStreamStateRequest();
+$request->setVhost("your Vhost");
+$request->setDomain("your Domain");
+$request->setApp("your App");
+$request->setStream("your Stream");
+
+
+$response = new Volc\Service\Live\Models\Response\DescribeLiveStreamStateResponse();
+try {
+    $response = $client->DescribeLiveStreamState($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeRecordTaskFileHistory.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeRecordTaskFileHistory.php
new file mode 100755
index 0000000..138b9f3
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeRecordTaskFileHistory.php
@@ -0,0 +1,34 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: DescribeRecordTaskFileHistory
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Live\Live::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Live\Models\Request\DescribeRecordTaskFileHistoryRequest();
+$request->setVhost("your Vhost");
+$request->setApp("your App");
+$request->setStream("your Stream");
+$request->setDateFrom("your DateFrom");
+$request->setDateTo("your DateTo");
+$request->setPageNum(0);
+$request->setPageSize(0);
+$request->setType("your Type");
+
+
+$response = new Volc\Service\Live\Models\Response\DescribeRecordTaskFileHistoryResponse();
+try {
+    $response = $client->DescribeRecordTaskFileHistory($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeRelaySourceV2.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeRelaySourceV2.php
new file mode 100755
index 0000000..ba3a26c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeRelaySourceV2.php
@@ -0,0 +1,27 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: describeRelaySourceV2
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Live\Live::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Live\Models\Request\DescribeRelaySourceRequest();
+$request->setVhost("your Vhost");
+
+
+$response = new Volc\Service\Live\Models\Response\DescribeRelaySourceResponse();
+try {
+    $response = $client->describeRelaySourceV2($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeVQScoreTask.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeVQScoreTask.php
new file mode 100755
index 0000000..78a1a09
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoDescribeVQScoreTask.php
@@ -0,0 +1,27 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: describeVQScoreTask
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Live\Live::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Live\Models\Request\DescribeVQScoreTaskRequest();
+$request->setID("your ID");
+
+
+$response = new Volc\Service\Live\Models\Response\DescribeVQScoreTaskResponse();
+try {
+    $response = $client->describeVQScoreTask($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoDomain.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoDomain.php
new file mode 100755
index 0000000..4ec8868
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoDomain.php
@@ -0,0 +1,61 @@
+<?php
+
+include 'DemoBase.php';
+global $client;
+
+$body = [
+    'Domain' => 'domain',
+    'Type' => 'push',
+];
+
+$response = $client->createDomain(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    'Domain' => 'domain',
+];
+
+$response = $client->deletedomain(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    'PageNum' => 1,
+    'PageSize' => 1,
+];
+
+$response = $client->listDomainDetail(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    'DomainList' => ['domain1', 'domain2'],
+];
+$response = $client->describeDomain(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$response = $client->enableDomain(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$response = $client->disableDomain(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    'PullDomain' => 'pullDomain',
+    'PushDomain' => 'pushDomain',
+];
+$response = $client->managerPullPushDomainBind(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    'PullDomain' => 'pullDomain'
+];
+$response = $client->managerPullPushDomainBind(['json' => $body]);
+echo $response;
+echo '<br>';
+
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoGeneratePlayURL.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoGeneratePlayURL.php
new file mode 100755
index 0000000..19d8c7e
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoGeneratePlayURL.php
@@ -0,0 +1,34 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: generatePlayURL
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Live\Live::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Live\Models\Request\GeneratePlayURLRequest();
+$request->setVhost("your Vhost");
+$request->setDomain("your Domain");
+$request->setApp("your App");
+$request->setStream("your Stream");
+$request->setSuffix("your Suffix");
+$request->setType("your Type");
+$request->setValidDuration(0);
+$request->setExpiredTime("your ExpiredTime");
+
+
+$response = new Volc\Service\Live\Models\Response\GeneratePlayURLResponse();
+try {
+    $response = $client->generatePlayURL($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoGeneratePushURL.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoGeneratePushURL.php
new file mode 100755
index 0000000..4910a8e
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoGeneratePushURL.php
@@ -0,0 +1,32 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: generatePushURL
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Live\Live::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Live\Models\Request\GeneratePushURLRequest();
+$request->setVhost("your Vhost");
+$request->setDomain("your Domain");
+$request->setApp("your App");
+$request->setStream("your Stream");
+$request->setValidDuration(0);
+$request->setExpiredTime("your ExpiredTime");
+
+
+$response = new Volc\Service\Live\Models\Response\GeneratePushURLResponse();
+try {
+    $response = $client->generatePushURL($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoKillStream.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoKillStream.php
new file mode 100755
index 0000000..bbcbaf1
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoKillStream.php
@@ -0,0 +1,29 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: KillStream
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Live\Live::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Live\Models\Request\KillStreamRequest();
+$request->setVhost("your Vhost");
+$request->setApp("your App");
+$request->setStream("your Stream");
+
+
+$response = new Volc\Service\Live\Models\Response\KillStreamResponse();
+try {
+    $response = $client->KillStream($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoListPullToPushTask.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoListPullToPushTask.php
new file mode 100755
index 0000000..6a450fe
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoListPullToPushTask.php
@@ -0,0 +1,29 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: listPullToPushTask
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Live\Live::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Live\Models\Request\ListPullToPushTaskRequest();
+$request->setPage(0);
+$request->setSize(0);
+$request->setTitle("your Title");
+
+
+$response = new Volc\Service\Live\Models\Response\ListPullToPushTaskResponse();
+try {
+    $response = $client->listPullToPushTask($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoListVQScoreTask.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoListVQScoreTask.php
new file mode 100755
index 0000000..1bd7a96
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoListVQScoreTask.php
@@ -0,0 +1,31 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: listVQScoreTask
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Live\Live::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Live\Models\Request\ListVQScoreTaskRequest();
+$request->setStartTime("your StartTime");
+$request->setEndTime("your EndTime");
+$request->setPageNum(0);
+$request->setPageSize(0);
+$request->setStatus(0);
+
+
+$response = new Volc\Service\Live\Models\Response\ListVQScoreTaskResponse();
+try {
+    $response = $client->listVQScoreTask($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoRecordPreset.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoRecordPreset.php
new file mode 100755
index 0000000..8c54e8f
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoRecordPreset.php
@@ -0,0 +1,59 @@
+<?php
+
+include 'DemoBase.php';
+global $client;
+
+$body = [
+    'Vhost' => 'vhost',
+    'App' => 'app',
+    'Bucket' => 'bb',
+    'RecordTob' => [
+        [
+            'Format' => 'hls',
+            'Duration' => 100,
+            'Splice' => -1,
+            'RecordObject' => '/xx/xx',
+        ],
+    ]
+];
+
+$response = $client->createRecordPreset(['json' => $body]);
+echo $response;
+echo '<br>';
+
+
+$body = [
+    "Preset" => "preset",
+    'Vhost' => 'vhost',
+    'App' => 'app',
+    'Bucket' => 'bb2',
+    'RecordTob' => [
+        [
+            'Format' => 'hls',
+            'Duration' => 100,
+            'Splice' => -1,
+            'RecordObject' => '/xx/xx',
+        ],
+    ]
+];
+$response = $client->updateRecordPreset(['json' => $body]);
+echo $response;
+echo '<br>';
+
+
+$body = [
+    'Vhost' => 'vhost',
+    'App' => 'app',
+    'Preset' => 'xx',
+];
+$response = $client->deleteRecordPreset(['json' => $body]);
+echo $response;
+echo '<br>';
+
+
+$body = [
+    'Vhost' => 'vhost',
+];
+$response = $client->listVhostRecordPreset(['json' => $body]);
+echo $response;
+echo '<br>';
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoRestartPullToPushTask.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoRestartPullToPushTask.php
new file mode 100755
index 0000000..156f2c3
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoRestartPullToPushTask.php
@@ -0,0 +1,27 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: restartPullToPushTask
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Live\Live::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Live\Models\Request\RestartPullToPushTaskRequest();
+$request->setTaskId("your TaskId");
+
+
+$response = new Volc\Service\Live\Models\Response\RestartPullToPushTaskResponse();
+try {
+    $response = $client->restartPullToPushTask($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoSnapshotAuditPreset.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoSnapshotAuditPreset.php
new file mode 100755
index 0000000..715ab54
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoSnapshotAuditPreset.php
@@ -0,0 +1,51 @@
+<?php
+
+include 'DemoBase.php';
+global $client;
+
+$body = [
+    'Vhost' => 'vhost',
+    'App' => 'app',
+    'Bucket' => 'bb',
+    'Interval' => 2.3,
+    'StorageStrategy' => 1,
+    'Label' => ['301'],
+    'CallbackDetailList' => [
+        [
+            'URL' => 'http://xx',
+            'CallbackType' => 'http'
+        ]
+    ],
+    'StorageDir' => '/xx'
+];
+
+$response = $client->createSnapshotAuditPreset(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    'PresetName' => 'presetName',
+    'Vhost' => 'vhost',
+    'App' => 'app',
+    'Bucket' => 'bb2'
+];
+$response = $client->updateSnapshotAuditPreset(['json' => $body]);
+echo $response;
+echo '<br>';
+
+
+$body = [
+    'Vhost' => 'vhost',
+    'App' => 'app',
+    'PresetName' => 'presetName',
+];
+$response = $client->deleteSnapshotAuditPreset(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    'Vhost' => 'vhost',
+];
+$response = $client->listVhostSnapshotAuditPreset(['json' => $body]);
+echo $response;
+echo '<br>';
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoSnapshotPreset.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoSnapshotPreset.php
new file mode 100755
index 0000000..ea86c07
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoSnapshotPreset.php
@@ -0,0 +1,47 @@
+<?php
+
+include 'DemoBase.php';
+global $client;
+
+$body = [
+    'Vhost' => 'vhost',
+    'App' => 'app',
+    'Bucket' => 'bb',
+    'SnapshotFormat' => 'jpeg',
+    'SnapshotObject' => 'xx/xx',
+    'StorageDir' => '/xx'
+];
+
+$response = $client->createSnapshotPreset(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    'Preset' => 'preset',
+    'Vhost' => 'vhost',
+    'App' => 'app',
+    'Bucket' => 'bb2',
+    'SnapshotFormat' => 'jpeg',
+    'SnapshotObject' => 'xx/xx',
+    'StorageDir' => '/xx'
+];
+$response = $client->updateSnapshotPreset(['json' => $body]);
+echo $response;
+echo '<br>';
+
+
+$body = [
+    'Vhost' => 'vhost',
+    'App' => 'app',
+    'Preset' => 'xx',
+];
+$response = $client->deleteSnapshotPreset(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    'Vhost' => 'vhost',
+];
+$response = $client->listVhostSnapshotPreset(['json' => $body]);
+echo $response;
+echo '<br>';
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoStopPullToPushTask.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoStopPullToPushTask.php
new file mode 100755
index 0000000..0976823
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoStopPullToPushTask.php
@@ -0,0 +1,27 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: stopPullToPushTask
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Live\Live::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Live\Models\Request\StopPullToPushTaskRequest();
+$request->setTaskId("your TaskId");
+
+
+$response = new Volc\Service\Live\Models\Response\StopPullToPushTaskResponse();
+try {
+    $response = $client->stopPullToPushTask($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoStream.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoStream.php
new file mode 100755
index 0000000..f46c5d3
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoStream.php
@@ -0,0 +1,25 @@
+<?php
+
+include 'DemoBase.php';
+global $client;
+
+$body = [
+    'Domain' => 'domain1',
+    'App' => 'app',
+    'Stream' => 'stream',
+    'EndTime' => '2022-02-12T08:12:48.000Z',
+];
+
+$response = $client->forbidStream(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    'Domain' => 'domain1',
+    'App' => 'app',
+    'Stream' => 'stream',
+];
+
+$response = $client->resumeStream(['json' => $body]);
+echo $response;
+echo '<br>';
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoTransPreset.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoTransPreset.php
new file mode 100755
index 0000000..55a87cb
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoTransPreset.php
@@ -0,0 +1,54 @@
+<?php
+
+include 'DemoBase.php';
+global $client;
+
+$body = [
+    'PresetList' => []
+];
+
+$response = $client->listCommonTransPresetDetail(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    'Vhost' => 'vhost',
+    'App' => 'app',
+    'Status' => 0,
+    'SuffixName' => 'xx',
+    'Preset' => 'test',
+    'FPS' => 30,
+];
+
+$response = $client->createTranscodePreset(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    'Vhost' => 'vhost',
+    'App' => 'app',
+    'Status' => 0,
+    'SuffixName' => 'xx2',
+    'Preset' => 'test',
+    'FPS' => 60,
+];
+$response = $client->updateTranscodePreset(['json' => $body]);
+echo $response;
+echo '<br>';
+
+
+$body = [
+    'Vhost' => 'vhost',
+    'App' => 'app',
+    'Preset' => 'test',
+];
+$response = $client->deleteTranscodePreset(['json' => $body]);
+echo $response;
+echo '<br>';
+
+$body = [
+    'Vhost' => 'vhost',
+];
+$response = $client->listVhostTransCodePreset(['json' => $body]);
+echo $response;
+echo '<br>';
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoUpdateDenyConfig.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoUpdateDenyConfig.php
new file mode 100755
index 0000000..a82bc40
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoUpdateDenyConfig.php
@@ -0,0 +1,30 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: updateDenyConfig
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Live\Live::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Live\Models\Request\UpdateDenyConfigRequest();
+$request->setVhost("your Vhost");
+$request->setDomain("your Domain");
+$request->setApp("your App");
+$request->setDenyConfigList([new Volc\Service\Live\Models\Business\DenyConfigList()]);
+
+
+$response = new Volc\Service\Live\Models\Response\UpdateDenyConfigResponse();
+try {
+    $response = $client->updateDenyConfig($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoUpdatePullToPushTask.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoUpdatePullToPushTask.php
new file mode 100755
index 0000000..e195e12
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoUpdatePullToPushTask.php
@@ -0,0 +1,36 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: updatePullToPushTask
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Live\Live::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Live\Models\Request\UpdatePullToPushTaskRequest();
+$request->setTitle("your Title");
+$request->setTaskId("your TaskId");
+$request->setStartTime(0);
+$request->setEndTime(0);
+$request->setCallbackURL("your CallbackURL");
+$request->setType(0);
+$request->setCycleMode(0);
+$request->setDstAddr("your DstAddr");
+$request->setSrcAddr("your SrcAddr");
+$request->setSrcAddrS([new Volc\Service\Live\Models\Business\SrcAddrS()]);
+
+
+$response = new Volc\Service\Live\Models\Response\UpdatePullToPushTaskResponse();
+try {
+    $response = $client->updatePullToPushTask($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DemoUpdateRelaySourceV2.php b/vendor/volcengine/volc-sdk-php/examples/Live/DemoUpdateRelaySourceV2.php
new file mode 100755
index 0000000..d45fccc
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DemoUpdateRelaySourceV2.php
@@ -0,0 +1,31 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: updateRelaySourceV2
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Live\Live::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Live\Models\Request\UpdateRelaySourceRequest();
+$request->setVhost("your Vhost");
+$request->setApp("your App");
+$request->setRelaySourceDomainList([new Volc\Service\Live\Models\Business\RelaySourceDomainList()]);
+$request->setRelaySourceParams([new Volc\Service\Live\Models\Business\RelaySourceParams()]);
+$request->setRelaySourceProtocol("your RelaySourceProtocol");
+
+
+$response = new Volc\Service\Live\Models\Response\UpdateRelaySourceResponse();
+try {
+    $response = $client->updateRelaySourceV2($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Live/DescribeClosedStreamInfoByPage.php b/vendor/volcengine/volc-sdk-php/examples/Live/DescribeClosedStreamInfoByPage.php
new file mode 100755
index 0000000..effdb87
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Live/DescribeClosedStreamInfoByPage.php
@@ -0,0 +1,36 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: DescribeClosedStreamInfoByPage
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Live\Live::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Live\Models\Request\DescribeClosedStreamInfoByPageRequest();
+$request->setPageNum(0);
+$request->setPageSize(0);
+$request->setVhost("your Vhost");
+$request->setDomain("your Domain");
+$request->setApp("your App");
+$request->setStream("your Stream");
+$request->setSort("your Sort");
+$request->setEndTimeFrom("your EndTimeFrom");
+$request->setEndTimeTo("your EndTimeTo");
+$request->setSourceType("your SourceType");
+
+
+$response = new Volc\Service\Live\Models\Response\DescribeClosedStreamInfoByPageResponse();
+try {
+    $response = $client->DescribeClosedStreamInfoByPage($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Rtc/rtc-openapi-demo/README.md b/vendor/volcengine/volc-sdk-php/examples/Rtc/rtc-openapi-demo/README.md
new file mode 100755
index 0000000..ff3702b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Rtc/rtc-openapi-demo/README.md
@@ -0,0 +1,34 @@
+# 说明
+```
+演示了OpenAPI鉴权SDK的使用方式
+使用时请参考rtc-openapi-demo的用法或把它拷贝到自己的项目中追加所需的API
+```
+
+# 运行
+## 安装composer
+
+```angular2html
+参考文档:https://getcomposer.org/doc/00-intro.md
+```
+
+## 引入volc签名包
+
+更新本地composer.josn,引入volc签名包
+```go
+{
+    ...
+    
+    "require": {
+        "volcengine/volc-sdk-php": "*"
+    }
+    
+    ...
+}
+```
+
+
+## 获取依赖
+
+```angular2html
+$ composer update
+```
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/Rtc/rtc-openapi-demo/Rtc/DemoRequest.php b/vendor/volcengine/volc-sdk-php/examples/Rtc/rtc-openapi-demo/Rtc/DemoRequest.php
new file mode 100755
index 0000000..e5eb127
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Rtc/rtc-openapi-demo/Rtc/DemoRequest.php
@@ -0,0 +1,59 @@
+<?php
+
+//调用示例
+
+namespace YourProject\Rtc;
+
+require('../vendor/autoload.php');
+
+$client = Rtc::getInstance();
+
+// ak/sk 获取方式参考:https://www.volcengine.com/docs/6348/69828
+$ak = '';
+$sk = '';
+$appId = '';
+
+$client->setAccessKey($ak);
+$client->setSecretKey($sk);
+
+// 参数规范参考: https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html#query
+// startRecord - POST 请求
+$roomId = 'Your_RoomId';
+$body = [
+    'AppId' => $appId,
+    'RoomId' => '1234',
+    'TaskId' => $appId.'_'.$roomId,
+    'BusinessId' => 'Your_BusinessId',
+    'Encode' => [
+        "VideoWidth" => 1920,
+        "VideoHeight" => 1080,
+        "VideoFps" => 15,
+        "VideoBitrate"=> 4000,
+    ],
+    'FileFormatConfig' => [
+        'FileFormat' => ['MP4'],
+    ],
+    'StorageConfig' => [
+        'TosConfig'=> [
+            'AccountId' => 'Your_Volc_AccountId',
+            'Bucket' => 'Your_Bucket',
+        ],
+    ],
+];
+
+$response = $client->startRecord(['json' => $body]);
+echo "startRecord result:\n";
+echo $response;
+echo "\n";
+
+// getRecordTask - GET 请求
+$body = [
+    'AppId' => $appId,
+    'RoomId' => $roomId,
+    'TaskId' => $appId.'_'.$roomId,
+];
+
+$response = $client->getRecordTask(['query' => $body]);
+echo "getRecordTask result:\n";
+echo $response;
+echo "\n";
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/Rtc/rtc-openapi-demo/Rtc/Rtc.php b/vendor/volcengine/volc-sdk-php/examples/Rtc/rtc-openapi-demo/Rtc/Rtc.php
new file mode 100755
index 0000000..7e6f74a
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Rtc/rtc-openapi-demo/Rtc/Rtc.php
@@ -0,0 +1,83 @@
+<?php
+
+// RTC API 管理类
+// 1. $apiList 新增API配置
+// 2. 添加对应action的方法及版本号
+namespace YourProject\Rtc;
+
+use Volc\Base\V4Curl;
+
+class Rtc extends V4Curl
+{
+    public function __construct()
+    {
+        $this->region = func_get_arg(0);
+        parent::__construct($this->region);
+    }
+
+    protected function getConfig(string $region = 'cn-north-1')
+    {
+        //公共配置
+        return [
+            'host' => "https://rtc.volcengineapi.com",
+            'config' => [
+                'timeout' => 10.0,
+                'headers' => [
+                    'Accept' => 'application/json'
+                ],
+                'v4_credentials' => [
+                    'region' => $region,
+                    'service' => 'rtc',
+                ],
+            ],
+        ];
+    }
+
+    // ===API 配置===
+    // 格式固定,变量是 method、Action、Version
+    // 最外层 key 和 Action 保持一致
+    protected $apiList = [
+        'StartRecord' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'StartRecord',
+                    'Version' => '2022-06-01',
+                ]
+            ]
+        ],
+        'GetRecordTask' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetRecordTask',
+                    'Version' => '2022-06-01',
+                ]
+            ]
+        ]
+    ];
+
+    public function requestWithRetry(string $api, array $configs): string
+    {
+        try {
+            $response = $this->request($api, $configs);
+            return (string)$response->getBody();
+        } catch (\Exception $e) {
+            $response = $this->request($api, $configs);
+            return (string)$response->getBody();
+        }
+    }
+
+    // ===API 方法===
+    public function startRecord(array $query = []): string
+    {
+        return $this->requestWithRetry("StartRecord", $query);
+    }
+
+    public function getRecordTask(array $query = []): string
+    {
+        return $this->requestWithRetry("GetRecordTask", $query);
+    }
+}
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/Rtc/rtc-openapi-demo/VERSION b/vendor/volcengine/volc-sdk-php/examples/Rtc/rtc-openapi-demo/VERSION
new file mode 100755
index 0000000..833f82e
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Rtc/rtc-openapi-demo/VERSION
@@ -0,0 +1 @@
+v1.0.38
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/Rtc/rtc-openapi-demo/composer.json b/vendor/volcengine/volc-sdk-php/examples/Rtc/rtc-openapi-demo/composer.json
new file mode 100755
index 0000000..f230919
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Rtc/rtc-openapi-demo/composer.json
@@ -0,0 +1,19 @@
+{
+  "name": "yourproject/yourproject",
+  "type": "library",
+  "license": "MIT",
+  "authors": [
+    {
+      "name": "bytedance"
+    }
+  ],
+  "require": {
+    "volcengine/volc-sdk-php": "*"
+  },
+  "description": "",
+  "autoload": {
+    "psr-4": {
+      "YourProject\\": "./"
+    }
+  }
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoBindAXB.php b/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoBindAXB.php
new file mode 100755
index 0000000..d971aaa
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoBindAXB.php
@@ -0,0 +1,22 @@
+<?php
+
+use Volc\Service\SecretNumber;
+
+require('../../vendor/autoload.php');
+require('../../src/Service/SecretNumber.php');
+
+$client = SecretNumber::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+$body = [
+    'NumberPoolNo' => 'NP161156328504091435',
+    'PhoneNoA' => '13700000001',
+    'PhoneNoB' => '13700000002',
+    'PhoneNoX' => '13700000003',
+    'ExpireTime' => '1632920195',
+    'UserData' => 'this is my user data',
+];
+
+$response = $client->BindAXB($body);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoBindAXN.php b/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoBindAXN.php
new file mode 100755
index 0000000..e584a90
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoBindAXN.php
@@ -0,0 +1,21 @@
+<?php
+use Volc\Service\SecretNumber;
+
+require('../../vendor/autoload.php');
+require('../../src/Service/SecretNumber.php');
+
+$client = SecretNumber::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+$body = [
+    'NumberPoolNo' => 'NP162981168404095092',
+    'PhoneNoA' => '13700000001',
+    'PhoneNoB' => '13700000002',
+    'PhoneNoX' => '13700000005',
+    'ExpireTime' => '1632920195',
+    'UserData' => 'this is my user data',
+];
+
+$response = $client->BindAXN($body);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoClick2Call.php b/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoClick2Call.php
new file mode 100755
index 0000000..fdd8615
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoClick2Call.php
@@ -0,0 +1,19 @@
+<?php
+use Volc\Service\SecretNumber;
+
+require('../../vendor/autoload.php');
+require('../../src/Service/SecretNumber.php');
+
+$client = SecretNumber::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+$body = [
+    'Caller' => "137XXXX8257",
+    'Callee' => "158XXXX9130",
+    'CallerNumberPoolNo' => 'NP163517154204092175',
+    'CalleeNumberPoolNo' => 'NP163517154204092175',
+];
+
+$response = $client->Click2Call($body);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoClick2CallLite.php b/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoClick2CallLite.php
new file mode 100755
index 0000000..df2f01d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoClick2CallLite.php
@@ -0,0 +1,18 @@
+<?php
+use Volc\Service\SecretNumber;
+
+require('../../vendor/autoload.php');
+require('../../src/Service/SecretNumber.php');
+
+$client = SecretNumber::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+$body = [
+    'Caller' => '137XXXX8257',
+    'Callee' => '158XXXX9130',
+    'NumberPoolNo' => 'NPXXXXX810901043',
+];
+
+$response = $client->Click2CallLite($body);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoQuerySubscription.php b/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoQuerySubscription.php
new file mode 100755
index 0000000..fb98cf6
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoQuerySubscription.php
@@ -0,0 +1,17 @@
+<?php
+use Volc\Service\SecretNumber;
+
+require('../../vendor/autoload.php');
+require('../../src/Service/SecretNumber.php');
+
+$client = SecretNumber::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+$body = [
+    'NumberPoolNo' => 'NP161156328504091435',
+    'SubId' => 'S16329001153159fa121d9',
+];
+
+$response = $client->QuerySubscription($body);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoQuerySubscriptionForList.php b/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoQuerySubscriptionForList.php
new file mode 100755
index 0000000..8006a31
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoQuerySubscriptionForList.php
@@ -0,0 +1,20 @@
+<?php
+use Volc\Service\SecretNumber;
+
+require('../../vendor/autoload.php');
+require('../../src/Service/SecretNumber.php');
+
+$client = SecretNumber::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+$body = [
+    'NumberPoolNo' => 'NP161156328504091435',
+    'PhoneNoX' => '13700000003',
+    'Status' => 1,
+    'Offset' => 0,
+    'Limit' => 20,
+];
+
+$response = $client->QuerySubscriptionForList($body);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoSelectNumberAndBindAXB.php b/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoSelectNumberAndBindAXB.php
new file mode 100755
index 0000000..e63540f
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoSelectNumberAndBindAXB.php
@@ -0,0 +1,19 @@
+<?php
+use Volc\Service\SecretNumber;
+
+require('../../vendor/autoload.php');
+require('../../src/Service/SecretNumber.php');
+
+$client = SecretNumber::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+$body = [
+    'NumberPoolNo' => 'NP161156328504091435',
+    'PhoneNoA' => '13700000001',
+    'PhoneNoB' => '13700000002',
+    'ExpireTime' => '1632920195',
+];
+
+$response = $client->SelectNumberAndBindAXB($body);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoUnBindAXB.php b/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoUnBindAXB.php
new file mode 100755
index 0000000..e4a34c1
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoUnBindAXB.php
@@ -0,0 +1,17 @@
+<?php
+use Volc\Service\SecretNumber;
+
+require('../../vendor/autoload.php');
+require('../../src/Service/SecretNumber.php');
+
+$client = SecretNumber::getInstance();
+$client->setAccessKey("");
+$client->setSecretKey("");
+
+$body = [
+    'NumberPoolNo' => 'NP161156328504091435',
+    'SubId' => 'S16328999093159b70bc71',
+];
+
+$response = $client->UnbindAXB($body);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoUnbindAXN.php b/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoUnbindAXN.php
new file mode 100755
index 0000000..47147c8
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoUnbindAXN.php
@@ -0,0 +1,17 @@
+<?php
+use Volc\Service\SecretNumber;
+
+require('../../vendor/autoload.php');
+require('../../src/Service/SecretNumber.php');
+
+$client = SecretNumber::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+$body = [
+    'NumberPoolNo' => 'NP162981168404095092',
+    'SubId' => 'S16329006138991e7e1003',
+];
+
+$response = $client->UnbindAXN($body);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoUpdateAXB.php b/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoUpdateAXB.php
new file mode 100755
index 0000000..b789016
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoUpdateAXB.php
@@ -0,0 +1,19 @@
+<?php
+use Volc\Service\SecretNumber;
+
+require('../../vendor/autoload.php');
+require('../../src/Service/SecretNumber.php');
+
+$client = SecretNumber::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+$body = [
+    'NumberPoolNo' => 'NP161156328504091435',
+    'SubId' => 'S1632900399315954ffbfd',
+    'UpdateType' => 'updateExpireTime',
+    'ExpireTime' => '1632924444',
+];
+
+$response = $client->UpdateAXB($body);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoUpdateAXN.php b/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoUpdateAXN.php
new file mode 100755
index 0000000..849c473
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoUpdateAXN.php
@@ -0,0 +1,19 @@
+<?php
+use Volc\Service\SecretNumber;
+
+require('../../vendor/autoload.php');
+require('../../src/Service/SecretNumber.php');
+
+$client = SecretNumber::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+$body = [
+    'NumberPoolNo' => 'NP162981168404095092',
+    'SubId' => 'S16329006138991e7e1003',
+    'UpdateType' => 'updatePhoneNoB',
+    'PhoneNoB' => '13700000004',
+];
+
+$response = $client->UpdateAXN($body);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoUpgradeAXToAXB.php b/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoUpgradeAXToAXB.php
new file mode 100755
index 0000000..9098ee3
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/SecretNumber/DemoUpgradeAXToAXB.php
@@ -0,0 +1,19 @@
+<?php
+use Volc\Service\SecretNumber;
+
+require('../../vendor/autoload.php');
+require('../../src/Service/SecretNumber.php');
+
+$client = SecretNumber::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+$body = [
+    'NumberPoolNo' => 'NP161156328504091435',
+    'SubId' => 'S163290034831599ce523b',
+    'PhoneNoB' => '13700000002',
+    'UserData' => 'this is my user data',
+];
+
+$response = $client->UpgradeAXToAXB($body);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/Sms/DemoSendSms.php b/vendor/volcengine/volc-sdk-php/examples/Sms/DemoSendSms.php
new file mode 100755
index 0000000..67030d4
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Sms/DemoSendSms.php
@@ -0,0 +1,26 @@
+<?php
+require('../../vendor/autoload.php');
+use Volc\Service\Sms;
+use Volc\Models\Vod\Request\VodApplyUploadInfoRequest;
+use Volc\Models\Vod\Response\VodApplyUploadInfoResponse;
+
+$client = Sms::getInstance('your region');// 不传默认cn-north-1,可选ap-singapore-1 新加坡
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+// template
+$template = [
+    'code' => 1111,
+];
+
+$body = [
+    'SmsAccount' => "your sms account",
+    'Sign' => "sign",
+    "TemplateID"=>    "ST_xxx",
+    "TemplateParam"=> json_encode($template),
+    "PhoneNumbers"=>  "phone numbers",
+    "Tag"=>           "tag",
+];
+
+$response = $client->sendSms(['json' => $body]);
+echo $response;
diff --git a/vendor/volcengine/volc-sdk-php/examples/Sts/DemoAssumeRole.php b/vendor/volcengine/volc-sdk-php/examples/Sts/DemoAssumeRole.php
new file mode 100755
index 0000000..0e0e25a
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Sts/DemoAssumeRole.php
@@ -0,0 +1,19 @@
+<?php
+require('../../vendor/autoload.php');
+
+use Volc\Service\Sts;
+
+$client = Sts::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+$query = [
+    "query" => [
+        "DurationSeconds" => "900",
+        "RoleSessionName" => "just_for_test",
+        "RoleTrn" => "trn:iam::yourAccountID:role/yourRole",
+    ]
+];
+
+$response = $client->assumeRole($query);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/VEdit/DemoSubmitDirectEditTask.php b/vendor/volcengine/volc-sdk-php/examples/VEdit/DemoSubmitDirectEditTask.php
new file mode 100755
index 0000000..5a66673
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/VEdit/DemoSubmitDirectEditTask.php
@@ -0,0 +1,45 @@
+<?php
+require('../../vendor/autoload.php');
+
+use Volc\Service\VEdit;
+
+$client = VEdit::getInstance();
+// $client->setAccessKey($ak);
+// $client->setSecretKey($sk);
+
+
+// async
+// your custom editParam
+// below just an example, not complete
+$editParam = [
+    'Upload' => [
+        'Uploader' => 'your uploader',
+        'VideoName' => 'your videoName',
+    ],
+    'Output' => [
+        'Fps' => 25,
+        'Height' => 720,
+        'Width' => 1280,
+        'Quality' => 'medium',
+    ],
+    'Segments' => [[
+        'BackGround' => '0xFFFFFFFF',
+        'Duration' => 3,
+        'Elements' => [],
+        'Volume' => 1,
+    ]],
+    'GlobalElements' => []
+];
+
+$body = [
+    'EditParam' => $editParam,
+    'CallbackArgs' => 'test args',
+    'CallbackUri' => 'test callbackUri'
+];
+
+$response = $client->submitDirectEditTaskAsync(['json' => $body]);
+echo $response;
+
+// get results
+$response = $client->getDirectEditResult(['json' => ['ReqIds' => ['123']]]);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/VEdit/DemoSubmitTemplateTask.php b/vendor/volcengine/volc-sdk-php/examples/VEdit/DemoSubmitTemplateTask.php
new file mode 100755
index 0000000..edc323c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/VEdit/DemoSubmitTemplateTask.php
@@ -0,0 +1,38 @@
+<?php
+require('../../vendor/autoload.php');
+
+use Volc\Service\VEdit;
+
+$client = VEdit::getInstance();
+// $client->setAccessKey($ak);
+// $client->setSecretKey($sk);
+
+
+// async
+// below just an example, not complete
+$param = [
+    [
+        'Name' => 'img1',
+        'Type' => 'image',
+        'Position' => 's0e0',
+        'Source' => 'your source 1'
+    ],
+    [
+        'Name' => 'img2',
+        'Type' => 'image',
+        'Position' => 's1e0',
+        'Source' => 'your source 2'
+    ]
+];
+
+$body = [
+    'TemplateId' => 'templateId',
+    'Space' => 'your space',
+    'VideoName' => ['your title'],
+    'Params' => [$param],
+    'CallbackArgs' => 'your callback args',
+    'CallbackUri' => 'your callback uri'
+];
+
+$response = $client->submitTemplateTaskAsync(['json' => $body]);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/Visual/DemoConvertPhoto.php b/vendor/volcengine/volc-sdk-php/examples/Visual/DemoConvertPhoto.php
new file mode 100755
index 0000000..6bcf42c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Visual/DemoConvertPhoto.php
@@ -0,0 +1,13 @@
+<?php
+require('../../vendor/autoload.php');
+use Volc\Service\Visual;
+
+$client = Visual::getInstance();
+// call below method if you dont set ak and sk in ~/.volc/config
+
+$client->setAccessKey($ak);
+$client->setSecretKey($sk);
+
+echo "\nDemo ConvertPhoto\n";
+$response = $client->ConvertPhoto(['form_params' => ['image_base64' => '']]);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/Visual/DemoEnhancePhoto.php b/vendor/volcengine/volc-sdk-php/examples/Visual/DemoEnhancePhoto.php
new file mode 100755
index 0000000..aafba6a
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Visual/DemoEnhancePhoto.php
@@ -0,0 +1,13 @@
+<?php
+require('../../vendor/autoload.php');
+use Volc\Service\Visual;
+
+$client = Visual::getInstance();
+// call below method if you dont set ak and sk in ~/.volc/config
+
+$client->setAccessKey($ak);
+$client->setSecretKey($sk);
+
+echo "\nDemo EnhancePhoto\n";
+$response = $client->EnhancePhoto(['form_params' => ['image_base64' => '']]);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/Visual/DemoFaceSwap.php b/vendor/volcengine/volc-sdk-php/examples/Visual/DemoFaceSwap.php
new file mode 100755
index 0000000..9f782d5
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Visual/DemoFaceSwap.php
@@ -0,0 +1,14 @@
+<?php
+require('../../vendor/autoload.php');
+use Volc\Service\Visual;
+
+$client = Visual::getInstance();
+// call below method if you dont set ak and sk in ~/.volc/config
+
+$client->setAccessKey($ak);
+$client->setSecretKey($sk);
+
+echo "\nDemo FaceSwap\n";
+$response = $client->FaceSwap(['form_params' => [
+    'image_base64' => '', 'template_base64' => '', 'action_id' => 'faceswap']]);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/Visual/DemoGeneralSegment.php b/vendor/volcengine/volc-sdk-php/examples/Visual/DemoGeneralSegment.php
new file mode 100755
index 0000000..a4bf54d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Visual/DemoGeneralSegment.php
@@ -0,0 +1,13 @@
+<?php
+require('../../vendor/autoload.php');
+use Volc\Service\Visual;
+
+$client = Visual::getInstance();
+// call below method if you dont set ak and sk in ~/.volc/config
+
+$client->setAccessKey($ak);
+$client->setSecretKey($sk);
+
+echo "\nDemo GeneralSegment\n";
+$response = $client->GeneralSegment(['form_params' => ['image_base64' => '']]);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/Visual/DemoHumanSegment.php b/vendor/volcengine/volc-sdk-php/examples/Visual/DemoHumanSegment.php
new file mode 100755
index 0000000..bdf08ea
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Visual/DemoHumanSegment.php
@@ -0,0 +1,13 @@
+<?php
+require('../../vendor/autoload.php');
+use Volc\Service\Visual;
+
+$client = Visual::getInstance();
+// call below method if you dont set ak and sk in ~/.volc/config
+
+$client->setAccessKey($ak);
+$client->setSecretKey($sk);
+
+echo "\nDemo HumanSegment\n";
+$response = $client->HumanSegment(['form_params' => ['image_base64' => '']]);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/Visual/DemoJPCartoon.php b/vendor/volcengine/volc-sdk-php/examples/Visual/DemoJPCartoon.php
new file mode 100755
index 0000000..c2563dd
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Visual/DemoJPCartoon.php
@@ -0,0 +1,14 @@
+<?php
+require('../../vendor/autoload.php');
+use Volc\Service\Visual;
+
+$client = Visual::getInstance();
+// call below method if you dont set ak and sk in ~/.volc/config
+
+$client->setAccessKey($ak);
+$client->setSecretKey($sk);
+
+echo "\nDemo JPCartoon\n";
+$response = $client->JPCartoon(['form_params' => ['image_base64' => '']]);
+echo $response;
+
diff --git a/vendor/volcengine/volc-sdk-php/examples/Visual/DemoOCR.php b/vendor/volcengine/volc-sdk-php/examples/Visual/DemoOCR.php
new file mode 100755
index 0000000..d9cf04c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Visual/DemoOCR.php
@@ -0,0 +1,21 @@
+<?php
+require('../../vendor/autoload.php');
+use Volc\Service\Visual;
+
+$client = Visual::getInstance();
+// call below method if you dont set ak and sk in ~/.volc/config
+
+$client->setAccessKey($ak);
+$client->setSecretKey($sk);
+
+echo "\nDemo OCRNormal\n";
+$response = $client->OCRNormal(['form_params' => ['image_base64' => '']]);
+echo $response;
+
+// echo "\nDemo ID Card\n";
+// $response = $client->OCRNormal(['form_params' => ['image_base64' => '']]);
+// echo $response;
+
+// echo "\nDemo Bank Card\n";
+// $response = $client->OCRNormal(['form_params' => ['image_base64' => '']]);
+// echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoAddCallbackSubscription.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoAddCallbackSubscription.php
new file mode 100755
index 0000000..454cbdd
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoAddCallbackSubscription.php
@@ -0,0 +1,29 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: addCallbackSubscription
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodAddCallbackSubscriptionRequest();
+$request->setSpaceName("your SpaceName");
+$request->setUrl("your Url");
+$request->setContentType("your ContentType");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodAddCallbackSubscriptionResponse();
+try {
+    $response = $client->addCallbackSubscription($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoApplyUploadInfo.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoApplyUploadInfo.php
new file mode 100755
index 0000000..cf45915
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoApplyUploadInfo.php
@@ -0,0 +1,31 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: applyUploadInfo
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodApplyUploadInfoRequest();
+$request->setSpaceName("your SpaceName");
+$request->setSessionKey("your SessionKey");
+$request->setFileSize(0);
+$request->setFileType("your FileType");
+$request->setFileName("your FileName");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodApplyUploadInfoResponse();
+try {
+    $response = $client->applyUploadInfo($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoCommitUploadInfo.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoCommitUploadInfo.php
new file mode 100755
index 0000000..58adcf1
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoCommitUploadInfo.php
@@ -0,0 +1,30 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: commitUploadInfo
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodCommitUploadInfoRequest();
+$request->setSpaceName("your SpaceName");
+$request->setSessionKey("your SessionKey");
+$request->setCallbackArgs("your CallbackArgs");
+$request->setFunctions("your Functions");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodCommitUploadInfoResponse();
+try {
+    $response = $client->commitUploadInfo($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoCreateCdnPreloadTask.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoCreateCdnPreloadTask.php
new file mode 100755
index 0000000..2931708
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoCreateCdnPreloadTask.php
@@ -0,0 +1,28 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: createCdnPreloadTask
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodCreateCdnPreloadTaskRequest();
+$request->setSpaceName("your SpaceName");
+$request->setUrls("your Urls");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodCreateCdnPreloadTaskResponse();
+try {
+    $response = $client->createCdnPreloadTask($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoCreateCdnRefreshTask.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoCreateCdnRefreshTask.php
new file mode 100755
index 0000000..3998ddf
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoCreateCdnRefreshTask.php
@@ -0,0 +1,29 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: createCdnRefreshTask
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodCreateCdnRefreshTaskRequest();
+$request->setSpaceName("your SpaceName");
+$request->setUrls("your Urls");
+$request->setType("your Type");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodCreateCdnRefreshTaskResponse();
+try {
+    $response = $client->createCdnRefreshTask($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoCreateSpace.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoCreateSpace.php
new file mode 100755
index 0000000..1b03aef
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoCreateSpace.php
@@ -0,0 +1,31 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: createSpace
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodCreateSpaceRequest();
+$request->setSpaceName("your SpaceName");
+$request->setProjectName("your ProjectName");
+$request->setDescription("your Description");
+$request->setRegion("your Region");
+$request->setUserName("your UserName");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodCreateSpaceResponse();
+try {
+    $response = $client->createSpace($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoCreateVideoClassification.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoCreateVideoClassification.php
new file mode 100755
index 0000000..b6482ad
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoCreateVideoClassification.php
@@ -0,0 +1,30 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: createVideoClassification
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodCreateVideoClassificationRequest();
+$request->setSpaceName("your SpaceName");
+$request->setLevel(0);
+$request->setParentId(0);
+$request->setClassification("your Classification");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodCreateVideoClassificationResponse();
+try {
+    $response = $client->createVideoClassification($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoDeleteMedia.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoDeleteMedia.php
new file mode 100755
index 0000000..9419b10
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoDeleteMedia.php
@@ -0,0 +1,28 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: deleteMedia
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodDeleteMediaRequest();
+$request->setVids("your Vids");
+$request->setCallbackArgs("your CallbackArgs");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodDeleteMediaResponse();
+try {
+    $response = $client->deleteMedia($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoDeleteTranscodes.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoDeleteTranscodes.php
new file mode 100755
index 0000000..a71b1d9
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoDeleteTranscodes.php
@@ -0,0 +1,29 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: deleteTranscodes
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodDeleteTranscodesRequest();
+$request->setVid("your Vid");
+$request->setFileIds("your FileIds");
+$request->setCallbackArgs("your CallbackArgs");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodDeleteTranscodesResponse();
+try {
+    $response = $client->deleteTranscodes($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoDeleteVideoClassification.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoDeleteVideoClassification.php
new file mode 100755
index 0000000..d2bb539
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoDeleteVideoClassification.php
@@ -0,0 +1,28 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: deleteVideoClassification
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodDeleteVideoClassificationRequest();
+$request->setSpaceName("your SpaceName");
+$request->setClassificationId(0);
+
+
+$response = new Volc\Service\Vod\Models\Response\VodDeleteVideoClassificationResponse();
+try {
+    $response = $client->deleteVideoClassification($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoDescribeIpInfo.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoDescribeIpInfo.php
new file mode 100755
index 0000000..f0aa1b1
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoDescribeIpInfo.php
@@ -0,0 +1,27 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: describeIpInfo
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodDescribeIPInfoRequest();
+$request->setIps("your Ips");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodDescribeIPInfoResponse();
+try {
+    $response = $client->describeIpInfo($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoDescribeVodDomainBandwidthData.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoDescribeVodDomainBandwidthData.php
new file mode 100755
index 0000000..baf9da7
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoDescribeVodDomainBandwidthData.php
@@ -0,0 +1,32 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: describeVodDomainBandwidthData
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodDescribeVodDomainBandwidthDataRequest();
+$request->setDomainList("your DomainList");
+$request->setStartTime("your StartTime");
+$request->setEndTime("your EndTime");
+$request->setAggregation(0);
+$request->setBandwidthType("your BandwidthType");
+$request->setArea("your Area");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodDescribeVodDomainBandwidthDataResponse();
+try {
+    $response = $client->describeVodDomainBandwidthData($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoDescribeVodDomainTrafficData.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoDescribeVodDomainTrafficData.php
new file mode 100755
index 0000000..64dcc32
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoDescribeVodDomainTrafficData.php
@@ -0,0 +1,31 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: describeVodDomainTrafficData
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodDescribeVodDomainTrafficDataRequest();
+$request->setDomainList("your DomainList");
+$request->setStartTime("your StartTime");
+$request->setEndTime("your EndTime");
+$request->setAggregation(0);
+$request->setTrafficType("your TrafficType");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodDescribeVodDomainTrafficDataResponse();
+try {
+    $response = $client->describeVodDomainTrafficData($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoDescribeVodSpaceStorageData.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoDescribeVodSpaceStorageData.php
new file mode 100755
index 0000000..340233b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoDescribeVodSpaceStorageData.php
@@ -0,0 +1,31 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: describeVodSpaceStorageData
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodDescribeVodSpaceStorageDataRequest();
+$request->setSpaceList("your SpaceList");
+$request->setStartTime("your StartTime");
+$request->setEndTime("your EndTime");
+$request->setAggregation(0);
+$request->setType("your Type");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodDescribeVodSpaceStorageDataResponse();
+try {
+    $response = $client->describeVodSpaceStorageData($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoDrm.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoDrm.php
new file mode 100755
index 0000000..8621242
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoDrm.php
@@ -0,0 +1,50 @@
+<?php
+require('../../vendor/autoload.php');
+
+use Volc\Service\Vod\Models\Request\VodGetPrivateDrmPlayAuthRequest;
+use Volc\Service\Vod\Models\Response\VodGetPrivateDrmPlayAuthResponse;
+use Volc\Service\Vod\Vod;
+
+
+$client = Vod::getInstance();
+// $client->setAccessKey("");
+// $client->setSecretKey("");
+
+$vid = "your vid";
+$playAuthId = "your play auth id";
+$expire = 6000000; // 请求的签名有效期
+$req = new VodGetPrivateDrmPlayAuthRequest();
+$req->setVid($vid);
+$req->setDrmType("your drm type");
+$req->setPlayAuthIds($playAuthId);
+$req->setUnionInfo("your unionInfo");
+
+echo "\n获取HlsDrmAuthToken\n";
+try {
+    $response1 = $client->getSHA1HlsDrmAuthToken($expire);
+    echo $response1;
+} catch (Exception | Throwable $e) {
+    echo $e, "\n";
+}
+
+echo "\n获取PrivateDrmAuthToken\n";
+try {
+
+    $response2 = $client->getPrivateDrmPlayAuthToken($req, $expire);
+    echo $response2;
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+
+echo "\n获取PrivateDrmAuth\n";
+try {
+    $response3 = $client->getPrivateDrmPlayAuth($req);
+    echo $response3->serializeToJsonString();
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetAllPlayInfo.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetAllPlayInfo.php
new file mode 100755
index 0000000..86a1105
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetAllPlayInfo.php
@@ -0,0 +1,44 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: getAllPlayInfo
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodGetAllPlayInfoRequest();
+$request->setVids("your Vids");
+$request->setFormats("your Formats");
+$request->setCodecs("your Codecs");
+$request->setDefinitions("your Definitions");
+$request->setFileTypes("your FileTypes");
+$request->setLogoTypes("your LogoTypes");
+$request->setNeedEncryptStream("your NeedEncryptStream");
+$request->setSsl("your Ssl");
+$request->setNeedThumbs("your NeedThumbs");
+$request->setNeedBarrageMask("your NeedBarrageMask");
+$request->setCdnType("your CdnType");
+$request->setUnionInfo("your UnionInfo");
+$request->setPlayScene("your PlayScene");
+$request->setDrmExpireTimestamp("your DrmExpireTimestamp");
+$request->setHDRType("your HDRType");
+$request->setKeyFrameAlignmentVersion("your KeyFrameAlignmentVersion");
+$request->setUserAction("your UserAction");
+$request->setQuality("your Quality");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodGetAllPlayInfoResponse();
+try {
+    $response = $client->getAllPlayInfo($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetAppInfo.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetAppInfo.php
new file mode 100755
index 0000000..1c7c162
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetAppInfo.php
@@ -0,0 +1,27 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: getAppInfo
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodGetAppInfoRequest();
+$request->setAppId(0);
+
+
+$response = new Volc\Service\Vod\Models\Response\VodGetAppInfoResponse();
+try {
+    $response = $client->getAppInfo($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetAudioEventDetectionForAudit.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetAudioEventDetectionForAudit.php
new file mode 100755
index 0000000..c61e931
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetAudioEventDetectionForAudit.php
@@ -0,0 +1,28 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: getAudioEventDetectionForAudit
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodGetAudioEventDetectionForAuditRequest();
+$request->setVid("your Vid");
+$request->setStrategy("your Strategy");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodGetAudioEventDetectionForAuditResponse();
+try {
+    $response = $client->getAudioEventDetectionForAudit($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetAudioInfoForAudit.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetAudioInfoForAudit.php
new file mode 100755
index 0000000..2587bd0
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetAudioInfoForAudit.php
@@ -0,0 +1,28 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: getAudioInfoForAudit
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodGetAudioInfoForAuditRequest();
+$request->setVid("your Vid");
+$request->setStrategy("your Strategy");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodGetAudioInfoForAuditResponse();
+try {
+    $response = $client->getAudioInfoForAudit($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetAuditFramesForAudit.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetAuditFramesForAudit.php
new file mode 100755
index 0000000..5235ae9
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetAuditFramesForAudit.php
@@ -0,0 +1,30 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: getAuditFramesForAudit
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodGetAuditFramesForAuditRequest();
+$request->setVid("your Vid");
+$request->setStrategy("your Strategy");
+$request->setMinNumberOfFrames("your MinNumberOfFrames");
+$request->setMaxNumberOfFrames("your MaxNumberOfFrames");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodGetAuditFramesForAuditResponse();
+try {
+    $response = $client->getAuditFramesForAudit($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetAutomaticSpeechRecognitionForAudit.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetAutomaticSpeechRecognitionForAudit.php
new file mode 100755
index 0000000..7a1a2cd
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetAutomaticSpeechRecognitionForAudit.php
@@ -0,0 +1,28 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: getAutomaticSpeechRecognitionForAudit
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodGetAutomaticSpeechRecognitionForAuditRequest();
+$request->setVid("your Vid");
+$request->setStrategy("your Strategy");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodGetAutomaticSpeechRecognitionForAuditResponse();
+try {
+    $response = $client->getAutomaticSpeechRecognitionForAudit($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetBetterFramesForAudit.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetBetterFramesForAudit.php
new file mode 100755
index 0000000..23c0434
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetBetterFramesForAudit.php
@@ -0,0 +1,29 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: getBetterFramesForAudit
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodGetBetterFramesForAuditRequest();
+$request->setVid("your Vid");
+$request->setStrategy("your Strategy");
+$request->setCoverRate("your CoverRate");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodGetBetterFramesForAuditResponse();
+try {
+    $response = $client->getBetterFramesForAudit($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetDirectEditProgress.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetDirectEditProgress.php
new file mode 100755
index 0000000..5e3a86f
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetDirectEditProgress.php
@@ -0,0 +1,26 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: getDirectEditProgress
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+$request = new Volc\Service\Vod\Models\Request\VodGetDirectEditProgressRequest();
+$request->setReqId("your reqId");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodGetDirectEditProgressResponse();
+try {
+    $response = $client->getDirectEditProgress($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetDirectEditResult.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetDirectEditResult.php
new file mode 100755
index 0000000..eeb6628
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetDirectEditResult.php
@@ -0,0 +1,29 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: getDirectEditResult
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+$request = new Volc\Service\Vod\Models\Request\VodGetDirectEditResultRequest();
+$request->setReqIds(["your reqId1","your reqId2"]);
+
+
+$response = new Volc\Service\Vod\Models\Response\VodGetDirectEditResultResponse();
+try {
+
+    $response = $client->getDirectEditResult($request);
+
+
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetHlsDecryptionKey.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetHlsDecryptionKey.php
new file mode 100755
index 0000000..599e77f
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetHlsDecryptionKey.php
@@ -0,0 +1,29 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: getHlsDecryptionKey
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodGetHlsDecryptionKeyRequest();
+$request->setDrmAuthToken("your DrmAuthToken");
+$request->setAk("your Ak");
+$request->setSource("your Source");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodGetHlsDecryptionKeyResponse();
+try {
+    $response = $client->getHlsDecryptionKey($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetMLFramesForAudit.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetMLFramesForAudit.php
new file mode 100755
index 0000000..7fd841e
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetMLFramesForAudit.php
@@ -0,0 +1,38 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: getMLFramesForAudit
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodGetMLFramesForAuditRequest();
+$request->setVid("your Vid");
+$request->setStrategy("your Strategy");
+$request->setFrameOpt("your FrameOpt");
+$request->setFrameFps("your FrameFps");
+$request->setNumberOfFrames("your NumberOfFrames");
+$request->setCutTimeMills("your CutTimeMills");
+$request->setNeedFirstFrame("your NeedFirstFrame");
+$request->setNeedLastFrame("your NeedLastFrame");
+$request->setStartTimeMill("your StartTimeMill");
+$request->setEndTimeMill("your EndTimeMill");
+$request->setMinNumberOfFrames("your MinNumberOfFrames");
+$request->setMaxNumberOfFrames("your MaxNumberOfFrames");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodGetMLFramesForAuditResponse();
+try {
+    $response = $client->getMLFramesForAudit($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetMediaInfos.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetMediaInfos.php
new file mode 100755
index 0000000..a9ee5e4
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetMediaInfos.php
@@ -0,0 +1,27 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: getMediaInfos
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodGetMediaInfosRequest();
+$request->setVids("your Vids");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodGetMediaInfosResponse();
+try {
+    $response = $client->getMediaInfos($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetMediaList.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetMediaList.php
new file mode 100755
index 0000000..90fafe6
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetMediaList.php
@@ -0,0 +1,36 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: getMediaList
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodGetMediaListRequest();
+$request->setSpaceName("your SpaceName");
+$request->setVid("your Vid");
+$request->setStatus("your Status");
+$request->setOrder("your Order");
+$request->setTags("your Tags");
+$request->setStartTime("your StartTime");
+$request->setEndTime("your EndTime");
+$request->setOffset("your Offset");
+$request->setPageSize("your PageSize");
+$request->setClassificationIds("your ClassificationIds");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodGetMediaListResponse();
+try {
+    $response = $client->getMediaList($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetPlayInfo.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetPlayInfo.php
new file mode 100755
index 0000000..3f5c253
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetPlayInfo.php
@@ -0,0 +1,42 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: getPlayInfo
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodGetPlayInfoRequest();
+$request->setVid("your Vid");
+$request->setFormat("your Format");
+$request->setCodec("your Codec");
+$request->setDefinition("your Definition");
+$request->setFileType("your FileType");
+$request->setLogoType("your LogoType");
+$request->setBase64("your Base64");
+$request->setSsl("your Ssl");
+$request->setNeedThumbs("your NeedThumbs");
+$request->setNeedBarrageMask("your NeedBarrageMask");
+$request->setCdnType("your CdnType");
+$request->setUnionInfo("your UnionInfo");
+$request->setHDRDefinition("your HDRDefinition");
+$request->setPlayScene("your PlayScene");
+$request->setDrmExpireTimestamp("your DrmExpireTimestamp");
+$request->setQuality("your Quality");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodGetPlayInfoResponse();
+try {
+    $response = $client->getPlayInfo($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetPlayInfoWithLiveTimeShiftScene.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetPlayInfoWithLiveTimeShiftScene.php
new file mode 100755
index 0000000..340687d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetPlayInfoWithLiveTimeShiftScene.php
@@ -0,0 +1,31 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: getPlayInfoWithLiveTimeShiftScene
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodGetPlayInfoWithLiveTimeShiftSceneRequest();
+$request->setStoreUris("your StoreUris");
+$request->setSpaceName("your SpaceName");
+$request->setSsl("your Ssl");
+$request->setExpireTimestamp("your ExpireTimestamp");
+$request->setNeedComposeBucketName("your NeedComposeBucketName");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodGetPlayInfoWithLiveTimeShiftSceneResponse();
+try {
+    $response = $client->getPlayInfoWithLiveTimeShiftScene($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetPrivateDrmPlayAuth.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetPrivateDrmPlayAuth.php
new file mode 100755
index 0000000..206743d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetPrivateDrmPlayAuth.php
@@ -0,0 +1,30 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: getPrivateDrmPlayAuth
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodGetPrivateDrmPlayAuthRequest();
+$request->setDrmType("your DrmType");
+$request->setVid("your Vid");
+$request->setPlayAuthIds("your PlayAuthIds");
+$request->setUnionInfo("your UnionInfo");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodGetPrivateDrmPlayAuthResponse();
+try {
+    $response = $client->getPrivateDrmPlayAuth($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetRecommendedPoster.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetRecommendedPoster.php
new file mode 100755
index 0000000..a24cfbb
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetRecommendedPoster.php
@@ -0,0 +1,27 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: getRecommendedPoster
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodGetRecommendedPosterRequest();
+$request->setVids("your Vids");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodGetRecommendedPosterResponse();
+try {
+    $response = $client->getRecommendedPoster($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetSmartStrategyLitePlayInfo.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetSmartStrategyLitePlayInfo.php
new file mode 100755
index 0000000..4dc01ef
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetSmartStrategyLitePlayInfo.php
@@ -0,0 +1,37 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: getSmartStrategyLitePlayInfo
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodGetSmartStrategyLitePlayInfoRequest();
+$request->setPlayUrl("your PlayUrl");
+$request->setFormat("your Format");
+$request->setCodec("your Codec");
+$request->setDefinition("your Definition");
+$request->setFileType("your FileType");
+$request->setLogoType("your LogoType");
+$request->setSsl("your Ssl");
+$request->setNeedThumbs("your NeedThumbs");
+$request->setNeedBarrageMask("your NeedBarrageMask");
+$request->setUnionInfo("your UnionInfo");
+$request->setHDRDefinition("your HDRDefinition");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodGetSmartStrategyLitePlayInfoResponse();
+try {
+    $response = $client->getSmartStrategyLitePlayInfo($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetSpaceDetail.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetSpaceDetail.php
new file mode 100755
index 0000000..f70f12d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetSpaceDetail.php
@@ -0,0 +1,27 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: getSpaceDetail
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodGetSpaceDetailRequest();
+$request->setSpaceName("your SpaceName");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodGetSpaceDetailResponse();
+try {
+    $response = $client->getSpaceDetail($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetSubtitleAuthToken.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetSubtitleAuthToken.php
new file mode 100755
index 0000000..5ace75e
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetSubtitleAuthToken.php
@@ -0,0 +1,26 @@
+<?php
+require('../../vendor/autoload.php');
+
+use Volc\Service\Vod\Models\Request\VodGetSubtitleInfoListRequest;
+use Volc\Service\Vod\Vod;
+
+$client = Vod::getInstance();
+// call below method if you dont set ak and sk in ~/.vcloud/config
+// $client->setAccessKey("");
+// $client->setSecretKey("");
+
+$vid = "vid";
+
+echo "\n获取字幕token\n";
+
+$req = new VodGetSubtitleInfoListRequest();
+
+$req->setVid($vid);
+
+try {
+    $token = $client->getSubtitleAuthToken($req, 60);
+} catch (Throwable $e) {
+    print($e);
+}
+
+echo $token;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetSubtitleInfoList.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetSubtitleInfoList.php
new file mode 100755
index 0000000..2118c1c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetSubtitleInfoList.php
@@ -0,0 +1,38 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: getSubtitleInfoList
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodGetSubtitleInfoListRequest();
+$request->setVid("your Vid");
+$request->setFileIds("your FileIds");
+$request->setLanguages("your Languages");
+$request->setFormats("your Formats");
+$request->setLanguageIds("your LanguageIds");
+$request->setSubtitleIds("your SubtitleIds");
+$request->setStatus("your Status");
+$request->setTitle("your Title");
+$request->setTag("your Tag");
+$request->setOffset("your Offset");
+$request->setPageSize("your PageSize");
+$request->setSsl("your Ssl");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodGetSubtitleInfoListResponse();
+try {
+    $response = $client->getSubtitleInfoList($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetWorkflowExecution b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetWorkflowExecution
new file mode 100755
index 0000000..df9b6be
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoGetWorkflowExecution
@@ -0,0 +1,26 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: getWorkflowExecution
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodGetWorkflowExecutionStatusRequest();
+$request->setRunId("your RunId");
+
+$response = new Volc\Service\Vod\Models\Response\VodGetWorkflowExecutionStatusResponse();
+try {
+    $response = $client->GetWorkflowExecution($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListCdnAccessLog.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListCdnAccessLog.php
new file mode 100755
index 0000000..97f4401
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListCdnAccessLog.php
@@ -0,0 +1,30 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: listCdnAccessLog
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodListCdnAccessLogRequest();
+$request->setDomains("your Domains");
+$request->setStartTimestamp(0);
+$request->setEndTimestamp(0);
+$request->setSpaceName("your SpaceName");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodListCdnAccessLogResponse();
+try {
+    $response = $client->listCdnAccessLog($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListCdnPvData.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListCdnPvData.php
new file mode 100755
index 0000000..100d56f
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListCdnPvData.php
@@ -0,0 +1,31 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: listCdnPvData
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodListCdnPvDataRequest();
+$request->setDomains("your Domains");
+$request->setInterval("your Interval");
+$request->setStartTimestamp(0);
+$request->setEndTimestamp(0);
+$request->setDataType("your DataType");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodCdnStatisticsCommonResponse();
+try {
+    $response = $client->listCdnPvData($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListCdnStatusData.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListCdnStatusData.php
new file mode 100755
index 0000000..2821b18
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListCdnStatusData.php
@@ -0,0 +1,32 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: listCdnStatusData
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodListCdnStatusDataRequest();
+$request->setDomains("your Domains");
+$request->setInterval("your Interval");
+$request->setStartTimestamp(0);
+$request->setEndTimestamp(0);
+$request->setDataType("your DataType");
+$request->setMetric("your Metric");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodCdnStatisticsCommonResponse();
+try {
+    $response = $client->listCdnStatusData($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListCdnTasks.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListCdnTasks.php
new file mode 100755
index 0000000..c07942d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListCdnTasks.php
@@ -0,0 +1,35 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: listCdnTasks
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodListCdnTasksRequest();
+$request->setSpaceName("your SpaceName");
+$request->setTaskId("your TaskId");
+$request->setDomainName("your DomainName");
+$request->setTaskType("your TaskType");
+$request->setStatus("your Status");
+$request->setStartTimestamp(0);
+$request->setEndTimestamp(0);
+$request->setPageNum(0);
+$request->setPageSize(0);
+
+
+$response = new Volc\Service\Vod\Models\Response\VodListCdnTasksResponse();
+try {
+    $response = $client->listCdnTasks($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListCdnTopAccessUrl.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListCdnTopAccessUrl.php
new file mode 100755
index 0000000..470fa00
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListCdnTopAccessUrl.php
@@ -0,0 +1,30 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: listCdnTopAccessUrl
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodListCdnTopAccessUrlRequest();
+$request->setDomains("your Domains");
+$request->setStartTimestamp(0);
+$request->setEndTimestamp(0);
+$request->setSortType("your SortType");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodListCdnTopAccessUrlResponse();
+try {
+    $response = $client->listCdnTopAccessUrl($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListCdnUsageData.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListCdnUsageData.php
new file mode 100755
index 0000000..c0e1b7d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListCdnUsageData.php
@@ -0,0 +1,32 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: listCdnUsageData
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodListCdnUsageDataRequest();
+$request->setDomains("your Domains");
+$request->setInterval("your Interval");
+$request->setStartTimestamp(0);
+$request->setEndTimestamp(0);
+$request->setDataType("your DataType");
+$request->setMetric("your Metric");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodCdnStatisticsCommonResponse();
+try {
+    $response = $client->listCdnUsageData($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListDomain.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListDomain.php
new file mode 100755
index 0000000..f6ac20e
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListDomain.php
@@ -0,0 +1,27 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: listDomain
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodListDomainRequest();
+$request->setSpaceName("your SpaceName");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodListDomainResponse();
+try {
+    $response = $client->listDomain($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListSnapshots.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListSnapshots.php
new file mode 100755
index 0000000..2623eb7
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListSnapshots.php
@@ -0,0 +1,27 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: listSnapshots
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodListSnapshotsRequest();
+$request->setVid("your Vid");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodListSnapshotsResponse();
+try {
+    $response = $client->listSnapshots($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListSpace.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListSpace.php
new file mode 100755
index 0000000..3f7c126
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListSpace.php
@@ -0,0 +1,28 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: listSpace
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodListSpaceRequest();
+$request->setOffset(0);
+$request->setLimit(0);
+
+
+$response = new Volc\Service\Vod\Models\Response\VodListSpaceResponse();
+try {
+    $response = $client->listSpace($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListVideoClassifications.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListVideoClassifications.php
new file mode 100755
index 0000000..c5cf81b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoListVideoClassifications.php
@@ -0,0 +1,28 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: listVideoClassifications
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodListVideoClassificationsRequest();
+$request->setSpaceName("your SpaceName");
+$request->setClassificationId(0);
+
+
+$response = new Volc\Service\Vod\Models\Response\VodListVideoClassificationsResponse();
+try {
+    $response = $client->listVideoClassifications($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoPlay.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoPlay.php
new file mode 100755
index 0000000..d4f326b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoPlay.php
@@ -0,0 +1,47 @@
+<?php
+require('../../vendor/autoload.php');
+
+use Volc\Service\Vod\Models\Request\VodGetPlayInfoRequest;
+use Volc\Service\Vod\Models\Response\VodGetPlayInfoResponse;
+use Volc\Service\Vod\Vod;
+
+
+$client = Vod::getInstance();
+// $client->setAccessKey("");
+// $client->setSecretKey("");
+
+$vid = "your vid";
+$expire = 6000000; // 请求的签名有效期
+
+
+echo "\nstaging 获取播放地址\n";
+$req = new VodGetPlayInfoRequest();
+$req->setVid($vid);
+$req->setSsl('1');
+$req->setFormat('hls');
+$req->setDefinition('480p');
+$req->setFileType('evideo');
+$response = new VodGetPlayInfoResponse();
+try {
+    $response = $client->getPlayInfo($req);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response->getResponseMetadata()->getError() != null) {
+    print_r($response->getResponseMetadata()->getError());
+}
+echo $response->getResult()->getPlayInfoList()[0]->getMainPlayUrl();
+
+echo "\n获取PlayAuthToken\n";
+$req2 = new VodGetPlayInfoRequest();
+$req2->setVid($vid);
+try {
+    $response2 = $client->getPlayAuthToken($req2, $expire);
+    echo $response2;
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoPublish.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoPublish.php
new file mode 100755
index 0000000..b641596
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoPublish.php
@@ -0,0 +1,32 @@
+<?php
+require('../../vendor/autoload.php');
+
+use Volc\Service\Vod\Models\Request\VodUpdateMediaPublishStatusRequest;
+use Volc\Service\Vod\Models\Response\VodUpdateMediaPublishStatusResponse;
+use Volc\Service\Vod\Vod;
+
+$client = Vod::getInstance();
+// call below method if you dont set ak and sk in ~/.vcloud/config
+// $client->setAccessKey("");
+// $client->setSecretKey("");
+
+$vid = "vid";
+$status = "Published";
+
+echo "\n修改发布状态\n";
+
+$req = new VodUpdateMediaPublishStatusRequest();
+$req->setVid($vid);
+$req->setStatus($status);
+$response = new VodUpdateMediaPublishStatusResponse();
+try {
+    $response = $client->updateMediaPublishStatus($req);
+} catch (Throwable $e) {
+    print($e);
+}
+
+if ($response->getResponseMetadata()->getError() != null) {
+    print_r($response->getResponseMetadata()->getError());
+}
+
+echo $response->serializeToJsonString();
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoQueryUploadTaskInfo.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoQueryUploadTaskInfo.php
new file mode 100755
index 0000000..b346b5e
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoQueryUploadTaskInfo.php
@@ -0,0 +1,27 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: queryUploadTaskInfo
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodQueryUploadTaskInfoRequest();
+$request->setJobIds("your JobIds");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodQueryUploadTaskInfoResponse();
+try {
+    $response = $client->queryUploadTaskInfo($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoRetrieveTranscodeResult.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoRetrieveTranscodeResult.php
new file mode 100755
index 0000000..4274547
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoRetrieveTranscodeResult.php
@@ -0,0 +1,28 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: retrieveTranscodeResult
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodRetrieveTranscodeResultRequest();
+$request->setVid("your Vid");
+$request->setResultType("your ResultType");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodRetrieveTranscodeResultResponse();
+try {
+    $response = $client->retrieveTranscodeResult($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoSetCallbackEvent.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoSetCallbackEvent.php
new file mode 100755
index 0000000..c609bc2
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoSetCallbackEvent.php
@@ -0,0 +1,30 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: setCallbackEvent
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodSetCallbackEventRequest();
+$request->setSpaceName("your SpaceName");
+$request->setEvents("your Events");
+$request->setAuthEnabled("your AuthEnabled");
+$request->setPrivateKey("your PrivateKey");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodSetCallbackEventResponse();
+try {
+    $response = $client->setCallbackEvent($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoStartWorkflow.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoStartWorkflow.php
new file mode 100755
index 0000000..bcc0574
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoStartWorkflow.php
@@ -0,0 +1,31 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: startWorkflow
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodStartWorkflowRequest();
+$request->setVid("your Vid");
+$request->setTemplateId("your TemplateId");
+$request->setInput(new Volc\Service\Vod\Models\Business\WorkflowParams());$request->setPriority(0);
+$request->setCallbackArgs("your CallbackArgs");
+$request->setEnableLowPriority(false);
+
+
+$response = new Volc\Service\Vod\Models\Response\VodStartWorkflowResponse();
+try {
+    $response = $client->startWorkflow($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoSubmitDirectEditTaskAsync.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoSubmitDirectEditTaskAsync.php
new file mode 100755
index 0000000..13aac4f
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoSubmitDirectEditTaskAsync.php
@@ -0,0 +1,33 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: submitDirectEditTaskAsync
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodSubmitDirectEditTaskAsyncRequest();
+$request->setUploader("your uploader");
+$request->setApplication("VideoTrackToB");
+
+$request->setEditParam("your editParam");
+$request->setPriority(0);
+$request->setCallbackUri("your CallbackUri");
+$request->setCallbackArgs("your CallbackArgs");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodSubmitDirectEditTaskAsyncResponse();
+try {
+    $response = $client->submitDirectEditTaskAsync($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateMediaInfo.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateMediaInfo.php
new file mode 100755
index 0000000..16fc5d3
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateMediaInfo.php
@@ -0,0 +1,32 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: updateMediaInfo
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodUpdateMediaInfoRequest();
+$request->setVid("your Vid");
+$request->setPosterUriUnwrapped("your PosterUri");
+$request->setTitleUnwrapped("your Title");
+$request->setDescriptionUnwrapped("your Description");
+$request->setTagsUnwrapped("your Tags");
+$request->setClassificationIdUnwrapped(0);
+
+
+$response = new Volc\Service\Vod\Models\Response\VodUpdateMediaInfoResponse();
+try {
+    $response = $client->updateMediaInfo($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateMediaPublishStatus.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateMediaPublishStatus.php
new file mode 100755
index 0000000..89b7095
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateMediaPublishStatus.php
@@ -0,0 +1,28 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: updateMediaPublishStatus
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodUpdateMediaPublishStatusRequest();
+$request->setVid("your Vid");
+$request->setStatus("your Status");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodUpdateMediaPublishStatusResponse();
+try {
+    $response = $client->updateMediaPublishStatus($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateSpace.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateSpace.php
new file mode 100755
index 0000000..2cf4df2
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateSpace.php
@@ -0,0 +1,30 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: updateSpace
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodUpdateSpaceRequest();
+$request->setSpaceName("your SpaceName");
+$request->setSourceProjectName("your SourceProjectName");
+$request->setTargetProjectName("your TargetProjectName");
+$request->setDescription("your Description");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodUpdateSpaceResponse();
+try {
+    $response = $client->updateSpace($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateSpaceUploadConfig.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateSpaceUploadConfig.php
new file mode 100755
index 0000000..03d70b1
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateSpaceUploadConfig.php
@@ -0,0 +1,29 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: updateSpaceUploadConfig
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodUpdateSpaceUploadConfigRequest();
+$request->setSpaceName("your SpaceName");
+$request->setConfigKey("your ConfigKey");
+$request->setConfigValue("your ConfigValue");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodUpdateSpaceUploadConfigResponse();
+try {
+    $response = $client->updateSpaceUploadConfig($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateSubtitleInfo.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateSubtitleInfo.php
new file mode 100755
index 0000000..3118873
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateSubtitleInfo.php
@@ -0,0 +1,32 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: updateSubtitleInfo
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodUpdateSubtitleInfoRequest();
+$request->setVid("your Vid");
+$request->setFileId("your FileId");
+$request->setLanguage("your Language");
+$request->setFormat("your Format");
+$request->setTitleUnwrapped("your Title");
+$request->setTagUnwrapped("your Tag");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodUpdateSubtitleInfoResponse();
+try {
+    $response = $client->updateSubtitleInfo($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateSubtitleStatus.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateSubtitleStatus.php
new file mode 100755
index 0000000..a6a9360
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateSubtitleStatus.php
@@ -0,0 +1,31 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: updateSubtitleStatus
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodUpdateSubtitleStatusRequest();
+$request->setVid("your Vid");
+$request->setFileIds("your FileIds");
+$request->setLanguages("your Languages");
+$request->setFormats("your Formats");
+$request->setStatus("your Status");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodUpdateSubtitleStatusResponse();
+try {
+    $response = $client->updateSubtitleStatus($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateVideoClassification.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateVideoClassification.php
new file mode 100755
index 0000000..f5a952e
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUpdateVideoClassification.php
@@ -0,0 +1,29 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: updateVideoClassification
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodUpdateVideoClassificationRequest();
+$request->setSpaceName("your SpaceName");
+$request->setClassificationId(0);
+$request->setClassification("your Classification");
+
+
+$response = new Volc\Service\Vod\Models\Response\VodUpdateVideoClassificationResponse();
+try {
+    $response = $client->updateVideoClassification($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUploadMaterial.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUploadMaterial.php
new file mode 100755
index 0000000..aaeb64a
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUploadMaterial.php
@@ -0,0 +1,148 @@
+<?php
+require('../../vendor/autoload.php');
+
+use Volc\Service\Vod\Models\Request\VodUploadMaterialRequest;
+use Volc\Service\Vod\Models\Response\VodCommitUploadInfoResponse;
+use Volc\Service\Vod\Upload\Functions;
+use Volc\Service\Vod\Upload\VodUpload;
+use Volc\Service\Vod\Upload\OptionInfo;
+
+$client = VodUpload::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$space = 'your space';
+$filePath = "file path";
+
+//音视频素材上传
+$functions = new Functions();
+$functions->addGetMetaFunc();
+$functions->addSnapshotTimeFunc(2.1);
+
+$optionInfo = new OptionInfo();
+$optionInfo->Title = '素材测试视频';
+$optionInfo->Tags = 'test';
+$optionInfo->Description = '素材测试,视频文件';
+$optionInfo->Category = 'video';
+$optionInfo->RecordType = 2;
+$optionInfo->Format = 'mp4';
+$functions->addOptionInfoFunc($optionInfo);
+
+$functions = $functions->getFunctionsString();
+
+$request = new VodUploadMaterialRequest();
+$request->setSpaceName($space);
+$request->setFilePath($filePath);
+$request->setFunctions($functions);
+$request->setFileType("media");
+
+$request->setCallbackArgs("my callback");
+
+$response = new VodCommitUploadInfoResponse();
+try {
+    $response = $client->uploadMaterial($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError(), "\n";
+}
+echo $response->serializeToJsonString();
+echo "\n";
+
+if ($response->getResult() != null) {
+    echo $response->getResult()->getData()->getMid(), "\n";
+    echo $response->getResult()->getData()->getSourceInfo()->getWidth(), "\n";
+    echo $response->getResult()->getData()->getSourceInfo()->getHeight(), "\n";
+}
+
+////图片素材上传
+//$functions = new Functions();
+//$functions->addGetMetaFunc();
+//
+//$optionInfo = new OptionInfo();
+//$optionInfo->Title = '素材测试图片';
+//$optionInfo->Tags = 'test';
+//$optionInfo->Description = '素材测试,图片文件';
+//$optionInfo->Category = 'image';
+//$optionInfo->RecordType = 2;
+//$optionInfo->Format = 'jpg';
+//$functions->addOptionInfoFunc($optionInfo);
+//
+//$functions = $functions->getFunctionsString();
+//
+//$request = new VodUploadMaterialRequest();
+//$request->setSpaceName($space);
+//$request->setFilePath($filePath);
+//$request->setFunctions($functions);
+//$request->setFileType("image");
+//$request->setFileName("hello/vod/image");
+//
+//$request->setCallbackArgs("my callback");
+//
+//$response = new VodCommitUploadInfoResponse();
+//try {
+//    $response = $client->uploadMaterial($request);
+//} catch (Exception $e) {
+//    echo $e, "\n";
+//} catch (Throwable $e) {
+//    echo $e, "\n";
+//}
+//if ($response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+//    echo $response->getResponseMetadata()->getError(), "\n";
+//}
+//echo $response->serializeToJsonString();
+//echo "\n";
+//
+//if ($response->getResult() != null) {
+//    echo $response->getResult()->getData()->getMid(), "\n";
+//    echo $response->getResult()->getData()->getSourceInfo()->getWidth(), "\n";
+//    echo $response->getResult()->getData()->getSourceInfo()->getHeight(), "\n";
+//}
+
+////字幕素材上传
+//$functions = new Functions();
+//$functions->addGetMetaFunc();
+//
+//$optionInfo = new OptionInfo();
+//$optionInfo->Title = '素材测试字幕';
+//$optionInfo->Tags = 'test';
+//$optionInfo->Description = '素材测试,字幕文件';
+//$optionInfo->Category = 'subtitle';
+//$optionInfo->RecordType = 2;
+//$optionInfo->Format = 'vtt';
+//$functions->addOptionInfoFunc($optionInfo);
+//
+//$functions = $functions->getFunctionsString();
+//
+//$request = new VodUploadMaterialRequest();
+//$request->setSpaceName($space);
+//$request->setFilePath($filePath);
+//$request->setFunctions($functions);
+//$request->setFileType("object");
+//$request->setFileName("hello/vod/object");
+//
+//$request->setCallbackArgs("my callback");
+//
+//$response = new VodCommitUploadInfoResponse();
+//try {
+//    $response = $client->uploadMaterial($request);
+//} catch (Exception $e) {
+//    echo $e, "\n";
+//} catch (Throwable $e) {
+//    echo $e, "\n";
+//}
+//if ($response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+//    echo $response->getResponseMetadata()->getError(), "\n";
+//}
+//echo $response->serializeToJsonString();
+//echo "\n";
+//
+//if ($response->getResult() != null) {
+//    echo $response->getResult()->getData()->getMid(), "\n";
+//    echo $response->getResult()->getData()->getSourceInfo()->getSize(), "\n";
+//}
+
+
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUploadMedia.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUploadMedia.php
new file mode 100755
index 0000000..ffd6803
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUploadMedia.php
@@ -0,0 +1,49 @@
+<?php
+require('../../vendor/autoload.php');
+
+use Volc\Service\Vod\Models\Request\VodUploadMediaRequest;
+use Volc\Service\Vod\Models\Response\VodCommitUploadInfoResponse;
+use Volc\Service\Vod\Upload\Functions;
+use Volc\Service\Vod\Upload\VodUpload;
+
+$client = VodUpload::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$space = 'your space';
+$filePath = "file path";
+
+$functions = new Functions();
+$functions->addGetMetaFunc();
+$functions->addSnapshotTimeFunc(2.1);
+$functions = $functions->getFunctionsString();
+
+$request = new VodUploadMediaRequest();
+$request->setSpaceName($space);
+$request->setFilePath($filePath);
+$request->setFunctions($functions);
+$request->setCallbackArgs("my callback");
+$request->setFileName("hello/vod");
+
+$response = new VodCommitUploadInfoResponse();
+try {
+    $response = $client->uploadMedia($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+}
+echo $response->serializeToJsonString();
+echo "\n";
+
+if ($response->getResult() != null) {
+    echo $response->getResult()->getData()->getVid(), "\n";
+    echo $response->getResult()->getData()->getPosterUri(), "\n";
+    echo $response->getResult()->getData()->getSourceInfo()->getWidth(), "\n";
+    echo $response->getResult()->getData()->getSourceInfo()->getHeight(), "\n";
+}
+
+
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUploadMediaByUrl.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUploadMediaByUrl.php
new file mode 100755
index 0000000..fccfd6a
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUploadMediaByUrl.php
@@ -0,0 +1,27 @@
+// Code generated by protoc-gen-volcengine-sdk
+// source: uploadMediaByUrl
+// DO NOT EDIT!
+<?php
+require('../../vendor/autoload.php');
+
+$client = Volc\Service\Vod\Vod::getInstance();
+$client->setAccessKey('your ak');
+$client->setSecretKey('your sk');
+
+$request = new Volc\Service\Vod\Models\Request\VodUrlUploadRequest();
+$request->setSpaceName("your SpaceName");
+$request->setURLSets([new Volc\Service\Vod\Models\Business\VodUrlUploadURLSet()]);
+
+$response = new Volc\Service\Vod\Models\Response\VodUrlUploadResponse();
+try {
+    $response = $client->uploadMediaByUrl($request);
+} catch (Exception $e) {
+    echo $e, "\n";
+} catch (Throwable $e) {
+    echo $e, "\n";
+}
+if ($response != null && $response->getResponseMetadata() != null && $response->getResponseMetadata()->getError() != null) {
+    echo $response->getResponseMetadata()->getError()->serializeToJsonString(), "\n";
+} else {
+    echo $response->serializeToJsonString(), "\n";
+}
diff --git a/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUploadSts2.php b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUploadSts2.php
new file mode 100755
index 0000000..db6d35d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/Vod/DemoUploadSts2.php
@@ -0,0 +1,21 @@
+<?php
+require('../../vendor/autoload.php');
+
+use Volc\Service\Vod\Vod;
+
+$client = Vod::getInstance();
+// call below method if you dont set ak and sk in ~/.vcloud/config
+// $client->setAccessKey($ak);
+// $client->setSecretKey($sk);
+
+$expire = 60; // 请求的签名有效期
+
+echo "\nSTS2鉴权签名\n";
+$space = "";
+$response = $client->getUploadVideoAuthWithExpiredTime($expire);
+echo json_encode($response);
+
+echo "\nSTS2鉴权签名,过期时间默认1小时\n";
+$vid = "";
+$response = $client->getUploadVideoAuth();
+echo json_encode($response);
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/BatchAppend.php b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/BatchAppend.php
new file mode 100755
index 0000000..a321184
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/BatchAppend.php
@@ -0,0 +1,24 @@
+<?php
+
+use Volc\Service\Notify;
+
+require('../../../vendor/autoload.php');
+require('../../src/Service/Notify.php');
+
+
+$client = Notify::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+$body = [
+    'TaskOpenId' => '106d2984fbf0480480cbc8b98d609592',
+    'PhoneList' => [
+        [
+            'Phone' => 'your phone',
+        ]
+    ],
+];
+
+$response = $client->BatchAppend($body);
+echo $response;
+
diff --git a/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/CommitResourceUpload.php b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/CommitResourceUpload.php
new file mode 100755
index 0000000..bb1c742
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/CommitResourceUpload.php
@@ -0,0 +1,19 @@
+<?php
+
+
+use Volc\Service\Notify;
+
+require('../../../vendor/autoload.php');
+require('../../src/Service/Notify.php');
+
+$client = Notify::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+
+$body = [
+    'FileName'=>'mytest.mp3',
+];
+
+$response = $client->CommitResourceUpload($body);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/CreateTask.php b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/CreateTask.php
new file mode 100755
index 0000000..b1239f9
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/CreateTask.php
@@ -0,0 +1,30 @@
+<?php
+
+use Volc\Service\Notify;
+
+require('../../../vendor/autoload.php');
+require('../../src/Service/Notify.php');
+
+$client = Notify::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+$body = [
+    'Name' => '你好2222',
+    'Resource' => '9b39e17fb12444c78f20d6551469a6f0',
+    'Type' => 0,
+    'NumberPoolNo' => 'NP162213338604093530',
+    'Concurrency' => 2,
+    'PhoneList' => [
+        [
+            'Phone' => 'your phone',
+        ]
+    ],
+    'StartTime' => '2022-05-18 00:00:00',
+    'EndTime' => '2022-05-15 18:00:00',
+    'SelectNumberRule' => 5,
+];
+
+$response = $client->CreateTask($body);
+echo $response;
+
diff --git a/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/CreateTtsResource.php b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/CreateTtsResource.php
new file mode 100755
index 0000000..786a5f6
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/CreateTtsResource.php
@@ -0,0 +1,23 @@
+<?php
+
+
+
+use Volc\Service\Notify;
+
+require('../../../vendor/autoload.php');
+require('../../src/Service/Notify.php');
+
+$client = Notify::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+
+$body = [
+    'Name'=>'测试',
+    'TtsTemplateContent'=>'测试',
+    'Remark'=>"测试",
+    'TtsOption'=>"{\"loop\":0,\"loop_interval\":0,\"speed\":10,\"volume\":10,\"pitch\":10,\"voice_type\":\"BV001_streaming\",\"lang\":\"ch\",\"voice\":\"other\"}",
+];
+
+$response = $client->CreateTtsResource($body);
+echo $response;
diff --git a/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/DeleteResource.php b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/DeleteResource.php
new file mode 100755
index 0000000..45d09a8
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/DeleteResource.php
@@ -0,0 +1,12 @@
+<?php
+use Volc\Service\Notify;
+
+require('../../../vendor/autoload.php');
+require('../../src/Service/Notify.php');
+
+$client = Notify::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+$response = $client->DeleteResourceByKey("1e81711901d844cfa1fe89d341558a39");
+echo $response;
diff --git a/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/FetchResource.php b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/FetchResource.php
new file mode 100755
index 0000000..4a96523
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/FetchResource.php
@@ -0,0 +1,20 @@
+<?php
+
+
+use Volc\Service\Notify;
+
+require('../../../vendor/autoload.php');
+require('../../src/Service/Notify.php');
+
+$client = Notify::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+
+$body = [
+    'Url'=>'公网可以访问的url,需要申请正向代理',
+    'Name'=>'测试',
+];
+
+$response = $client->FetchResource($body);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/GetResourceUploadUrl.php b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/GetResourceUploadUrl.php
new file mode 100755
index 0000000..f7016cc
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/GetResourceUploadUrl.php
@@ -0,0 +1,20 @@
+<?php
+
+
+
+use Volc\Service\Notify;
+
+require('../../../vendor/autoload.php');
+require('../../src/Service/Notify.php');
+
+$client = Notify::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+
+$body = [
+    'FileName'=>'mytest.mp3'
+];
+
+$response = $client->GetResourceUploadUrl($body);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/PauseTask.php b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/PauseTask.php
new file mode 100755
index 0000000..9340bf2
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/PauseTask.php
@@ -0,0 +1,14 @@
+<?php
+
+
+use Volc\Service\Notify;
+
+require('../../../vendor/autoload.php');
+require('../../src/Service/Notify.php');
+
+$client = Notify::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+$response = $client->PauseTask("0742e8123ada40cb9efa6b55e8fced7a");
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/QueryResource.php b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/QueryResource.php
new file mode 100755
index 0000000..f1d6a66
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/QueryResource.php
@@ -0,0 +1,18 @@
+<?php
+
+use Volc\Service\Notify;
+
+require('../../../vendor/autoload.php');
+require('../../src/Service/Notify.php');
+
+$client = Notify::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+
+$body = [
+   'Type' => 0,
+];
+
+$response = $client->QueryResource($body);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/ResumeTask.php b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/ResumeTask.php
new file mode 100755
index 0000000..c6fa7e5
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/ResumeTask.php
@@ -0,0 +1,13 @@
+<?php
+
+use Volc\Service\Notify;
+
+require('../../../vendor/autoload.php');
+require('../../src/Service/Notify.php');
+
+$client = Notify::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+$response = $client->ResumeTask("0742e8123ada40cb9efa6b55e8fced7a");
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/SingleBatchAppend.php b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/SingleBatchAppend.php
new file mode 100755
index 0000000..7e7a9d0
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/SingleBatchAppend.php
@@ -0,0 +1,28 @@
+<?php
+
+use Volc\Service\Notify;
+
+require('../../../vendor/autoload.php');
+require('../../src/Service/Notify.php');
+
+$client = Notify::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+
+$body = [
+    'List' => [
+        [
+            'Phone'=>'your phone',
+            'Resource' => '9b39e17fb12444c78f20d6551469a6f0',
+            'NumberPoolNo'=>'NP162213338604093530',
+            'NumberType'=>0,
+            'TriggerTime'=>'2022-05-18 19:18:00',
+            'Type'=>0,
+            'SingleOpenId'=>'9b39e17fb12444c78f20d6551469a6e3',
+        ],
+    ],
+];
+
+$response = $client->SingleBatchAppend($body);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/SingleCancel.php b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/SingleCancel.php
new file mode 100755
index 0000000..107f34f
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/SingleCancel.php
@@ -0,0 +1,16 @@
+<?php
+
+
+use Volc\Service\Notify;
+
+require('../../../vendor/autoload.php');
+require('../../src/Service/Notify.php');
+
+$client = Notify::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+
+
+$response = $client->SingleCancel('9b39e17fb12444c78f20d6551469a6e3');
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/SingleInfo.php b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/SingleInfo.php
new file mode 100755
index 0000000..214529e
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/SingleInfo.php
@@ -0,0 +1,16 @@
+<?php
+
+
+use Volc\Service\Notify;
+
+require('../../../vendor/autoload.php');
+require('../../src/Service/Notify.php');
+
+$client = Notify::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+
+
+$response = $client->SingleInfo('9b39e17fb12444c78f20d6551469a6e3');
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/StopTask.php b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/StopTask.php
new file mode 100755
index 0000000..ece9088
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/StopTask.php
@@ -0,0 +1,14 @@
+<?php
+
+
+use Volc\Service\Notify;
+
+require('../../../vendor/autoload.php');
+require('../../src/Service/Notify.php');
+
+$client = Notify::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+$response = $client->StopTask("0742e8123ada40cb9efa6b55e8fced7a");
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/UpdateTask.php b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/UpdateTask.php
new file mode 100755
index 0000000..3b2a074
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/examples/VoiceNotify/UpdateTask.php
@@ -0,0 +1,21 @@
+<?php
+use Volc\Service\Notify;
+
+require('../../../vendor/autoload.php');
+require('../../src/Service/Notify.php');
+
+$client = Notify::getInstance();
+$client->setAccessKey("your ak");
+$client->setSecretKey("your sk");
+
+
+$body = [
+    'TaskOpenId' => '0742e8123ada40cb9efa6b55e8fced7a',
+    'Concurrency'=> 3,
+    'StartTime' => '2022-05-17 00:00:00',
+    'EndTime' => '2022-05-17 18:30:00',
+    'Recall' => 1,
+];
+
+$response = $client->UpdateTask($body);
+echo $response;
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/src/Base/Model/SignParam.php b/vendor/volcengine/volc-sdk-php/src/Base/Model/SignParam.php
new file mode 100755
index 0000000..950b80a
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Base/Model/SignParam.php
@@ -0,0 +1,150 @@
+<?php
+
+
+namespace Volc\Base\Model;
+
+class SignParam
+{
+    private $isSignUrl = false;
+    private $pyloadHash = '';
+    private $method = '';
+    private $date;
+    private $path = '';
+    private $host = '';
+    private $queryList = [];
+    private $headerList = [];
+
+    /**
+     * @return bool
+     */
+    public function isSignUrl(): bool
+    {
+        return $this->isSignUrl;
+    }
+
+    /**
+     * @param bool $isSignUrl
+     */
+    public function setIsSignUrl(bool $isSignUrl): void
+    {
+        $this->isSignUrl = $isSignUrl;
+    }
+
+    /**
+     * @return string
+     */
+    public function getPyloadHash(): string
+    {
+        return $this->pyloadHash;
+    }
+
+    /**
+     * @param string $pyloadHash
+     */
+    public function setPyloadHash(string $pyloadHash): void
+    {
+        $this->pyloadHash = $pyloadHash;
+    }
+
+
+
+    /**
+     * @return string
+     */
+    public function getMethod(): string
+    {
+        return $this->method;
+    }
+
+    /**
+     * @param string $method
+     */
+    public function setMethod(string $method): void
+    {
+        $this->method = $method;
+    }
+
+    /**
+     * @return \DateTime
+     */
+    public function getDate(): \DateTime
+    {
+        return $this->date;
+    }
+
+    /**
+     * @param \DateTime $date
+     */
+    public function setDate(\DateTime $date): void
+    {
+        $this->date = $date;
+    }
+
+    /**
+     * @return string
+     */
+    public function getPath(): string
+    {
+        return $this->path;
+    }
+
+    /**
+     * @param string $path
+     */
+    public function setPath(string $path): void
+    {
+        $this->path = $path;
+    }
+
+    /**
+     * @return string
+     */
+    public function getHost(): string
+    {
+        return $this->host;
+    }
+
+    /**
+     * @param string $host
+     */
+    public function setHost(string $host): void
+    {
+        $this->host = $host;
+    }
+
+    /**
+     * @return array
+     */
+    public function getQueryList(): array
+    {
+        return $this->queryList;
+    }
+
+    /**
+     * @param array $queryList
+     */
+    public function setQueryList(array $queryList): void
+    {
+        $this->queryList = $queryList;
+    }
+
+    /**
+     * @return array
+     */
+    public function getHeaderList(): array
+    {
+        return $this->headerList;
+    }
+
+    /**
+     * @param array $headerList
+     */
+    public function setHeaderList(array $headerList): void
+    {
+        $this->headerList = $headerList;
+    }
+
+
+
+
+}
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/src/Base/Model/SignResult.php b/vendor/volcengine/volc-sdk-php/src/Base/Model/SignResult.php
new file mode 100755
index 0000000..ff5b4e0
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Base/Model/SignResult.php
@@ -0,0 +1,136 @@
+<?php
+
+
+namespace Volc\Base\Model;
+
+
+class SignResult
+{
+    private $xDate= '';
+    private $xCredential= '';
+    private $xAlgorithm= '';
+    private $xSignedHeaders= '';
+    private $xSignedQueries= '';
+    private $xSignature= '';
+
+    private $authorization= '';
+
+    /**
+     * @return string
+     */
+    public function getXDate(): string
+    {
+        return $this->xDate;
+    }
+
+    /**
+     * @param string $xDate
+     */
+    public function setXDate(string $xDate): void
+    {
+        $this->xDate = $xDate;
+    }
+
+    /**
+     * @return string
+     */
+    public function getXCredential(): string
+    {
+        return $this->xCredential;
+    }
+
+    /**
+     * @param string $xCredential
+     */
+    public function setXCredential(string $xCredential): void
+    {
+        $this->xCredential = $xCredential;
+    }
+
+    /**
+     * @return string
+     */
+    public function getXAlgorithm(): string
+    {
+        return $this->xAlgorithm;
+    }
+
+    /**
+     * @param string $xAlgorithm
+     */
+    public function setXAlgorithm(string $xAlgorithm): void
+    {
+        $this->xAlgorithm = $xAlgorithm;
+    }
+
+    /**
+     * @return string
+     */
+    public function getXSignedHeaders(): string
+    {
+        return $this->xSignedHeaders;
+    }
+
+    /**
+     * @param string $xSignedHeaders
+     */
+    public function setXSignedHeaders(string $xSignedHeaders): void
+    {
+        $this->xSignedHeaders = $xSignedHeaders;
+    }
+
+    /**
+     * @return string
+     */
+    public function getXSignedQueries(): string
+    {
+        return $this->xSignedQueries;
+    }
+
+    /**
+     * @param string $xSignedQueries
+     */
+    public function setXSignedQueries(string $xSignedQueries): void
+    {
+        $this->xSignedQueries = $xSignedQueries;
+    }
+
+    /**
+     * @return string
+     */
+    public function getXSignature(): string
+    {
+        return $this->xSignature;
+    }
+
+    /**
+     * @param string $xSignature
+     */
+    public function setXSignature(string $xSignature): void
+    {
+        $this->xSignature = $xSignature;
+    }
+
+    /**
+     * @return string
+     */
+    public function getAuthorization(): string
+    {
+        return $this->authorization;
+    }
+
+    /**
+     * @param string $authorization
+     */
+    public function setAuthorization(string $authorization): void
+    {
+        $this->authorization = $authorization;
+    }
+
+    public function __toString()
+    {
+        return (string)($this->xDate . $this->authorization . $this->xCredential . $this->xSignature);
+    }
+
+
+}
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/src/Base/SignatureTrait.php b/vendor/volcengine/volc-sdk-php/src/Base/SignatureTrait.php
new file mode 100755
index 0000000..5bc21da
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Base/SignatureTrait.php
@@ -0,0 +1,49 @@
+<?php
+namespace Volc\Base;
+
+/**
+ * Provides signature calculation for SignatureV4.
+ */
+trait SignatureTrait
+{
+    /** @var array Cache of previously signed values */
+    private $cache = [];
+
+    /** @var int Size of the hash cache */
+    private $cacheSize = 0;
+
+    private function createScope($shortDate, $region, $service)
+    {
+        return "$shortDate/$region/$service/request";
+    }
+
+    private function getSigningKey($shortDate, $region, $service, $secretKey)
+    {
+        $k = $shortDate . '_' . $region . '_' . $service . '_' . $secretKey;
+
+        if (!isset($this->cache[$k])) {
+            // Clear the cache when it reaches 50 entries
+            if (++$this->cacheSize > 50) {
+                $this->cache = [];
+                $this->cacheSize = 0;
+            }
+
+            $dateKey = hash_hmac(
+                'sha256',
+                $shortDate,
+                "{$secretKey}",
+                true
+            );
+            $regionKey = hash_hmac('sha256', $region, $dateKey, true);
+            $serviceKey = hash_hmac('sha256', $service, $regionKey, true);
+            $this->cache[$k] = hash_hmac(
+                'sha256',
+                'request',
+                $serviceKey,
+                true
+            );
+        }
+
+        return $this->cache[$k];
+    }
+}
diff --git a/vendor/volcengine/volc-sdk-php/src/Base/SignatureV4.php b/vendor/volcengine/volc-sdk-php/src/Base/SignatureV4.php
new file mode 100755
index 0000000..2a2de72
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Base/SignatureV4.php
@@ -0,0 +1,316 @@
+<?php
+
+namespace Volc\Base;
+
+use GuzzleHttp\Psr7;
+use GuzzleHttp\Psr7\Query;
+use GuzzleHttp\Psr7\Utils;
+use Psr\Http\Message\RequestInterface;
+use Volc\Base\Model\SignParam;
+use Volc\Base\Model\SignResult;
+
+class SignatureV4
+{
+    use SignatureTrait;
+
+    const ISO8601_BASIC = 'Ymd\THis\Z';
+
+    public function signRequestToUrl(RequestInterface $request, $credentials)
+    {
+        $ldt = gmdate(self::ISO8601_BASIC);
+        $sdt = substr($ldt, 0, 8);
+        $ak = $credentials['ak'];
+        $cs = $this->createScope($sdt, $credentials['region'], $credentials['service']);
+
+        $parsed = $this->parseRequest($request);
+        $parsed['query']['X-Date'] = $ldt;
+        $parsed['query']['X-NotSignBody'] = "";
+        $parsed['query']['X-Algorithm'] = "HMAC-SHA256";
+        $parsed['query']['X-Credential'] = "{$ak}/${cs}";
+        $parsed['query']['X-SignedHeaders'] = '';
+
+        $signedQueries = array_keys($parsed['query']);
+        sort($signedQueries);
+        $parsed['query']['X-SignedQueries'] = implode(';', $signedQueries);
+
+        $cs = $this->createScope($sdt, $credentials['region'], $credentials['service']);
+        $payload = $this->getPayload($request);
+        $context = $this->createContext($parsed, $payload);
+        $toSign = $this->createStringToSign($ldt, $cs, $context['creq']);
+        $signingKey = $this->getSigningKey(
+            $sdt,
+            $credentials['region'],
+            $credentials['service'],
+            $credentials['sk']
+        );
+        $signature = hash_hmac('sha256', $toSign, $signingKey);
+
+        $parsed['query']['X-Signature'] = $signature;
+
+        return $this->buildRequestString($parsed);
+    }
+
+    public function signRequest(
+        RequestInterface $request,
+        $credentials
+    ) {
+        $ldt = gmdate(self::ISO8601_BASIC);
+        $sdt = substr($ldt, 0, 8);
+        $parsed = $this->parseRequest($request);
+        $parsed['headers']['X-Date'] = [$ldt];
+
+        $cs = $this->createScope($sdt, $credentials['region'], $credentials['service']);
+        $payload = $this->getPayload($request);
+        $context = $this->createContext($parsed, $payload);
+        $toSign = $this->createStringToSign($ldt, $cs, $context['creq']);
+        $signingKey = $this->getSigningKey(
+            $sdt,
+            $credentials['region'],
+            $credentials['service'],
+            $credentials['sk']
+        );
+        $signature = hash_hmac('sha256', $toSign, $signingKey);
+
+        $ak = $credentials['ak'];
+        $parsed['headers']['Authorization'] = [
+            "HMAC-SHA256 "
+            . "Credential={$ak}/{$cs}, "
+            . "SignedHeaders={$context['headers']}, Signature={$signature}"
+        ];
+
+        return $this->buildRequest($parsed);
+    }
+
+    public function signOnly(SignParam $param, $credentials): SignResult
+    {
+        $ldt = gmdate(self::ISO8601_BASIC, $param->getDate()->getTimestamp());
+        $sdt = substr($ldt, 0, 8);
+        $cs = $this->createScope($sdt, $credentials['region'], $credentials['service']);
+        $credential = "{$credentials['ak']}/{$cs}";
+        $parsed['method'] = $param->getMethod();
+        $parsed['path'] = $param->getPath();
+        $parsed['query'] = $param->getQueryList();
+        $parsed['headers'] = $param->getHeaderList();
+        $parsed['headers']['X-Date'] = [$ldt];
+
+        $isSignUrl = $param->isSignUrl();
+        if ($isSignUrl) {
+            $parsed['query']['X-NotSignBody'] = "";
+            $parsed['query']['X-Algorithm'] = "HMAC-SHA256";
+            $parsed['query']['X-Credential'] = $credential;
+            $parsed['query']['X-SignedHeaders'] = '';
+            $signedQueries = array_keys($parsed['query']);
+            sort($signedQueries);
+            $parsed['query']['X-SignedQueries'] = implode(';', $signedQueries);
+        }
+
+        $context = $this->createContext($parsed, $param->getPyloadHash());
+        $toSign = $this->createStringToSign($ldt, $cs, $context['creq']);
+        $signingKey = $this->getSigningKey(
+            $sdt,
+            $credentials['region'],
+            $credentials['service'],
+            $credentials['sk']
+        );
+        $signature = hash_hmac('sha256', $toSign, $signingKey);
+        $result = new SignResult();
+        $result->setXAlgorithm("HMAC-SHA256");
+        $result->setXCredential($credential);
+        $result->setXDate($ldt);
+        $result->setXSignature($signature);
+        if (isset($context['headers']) and $context['headers'] != null) {
+            $result->setXSignedHeaders($context['headers']);
+        }
+        if (isset($parsed['query']['X-SignedQueries']) and $parsed['query']['X-SignedQueries'] != null) {
+            $result->setXSignedQueries($parsed['query']['X-SignedQueries']);
+        }
+        $result->setAuthorization(
+            "HMAC-SHA256 "
+            . "Credential={$credential}, "
+            . "SignedHeaders={$context['headers']}, Signature={$signature}");
+        return $result;
+    }
+
+    protected function getPayload(RequestInterface $request)
+    {
+        // Calculate the request signature payload
+        if ($request->hasHeader('X-Content-Sha256')) {
+            // Handle streaming operations (e.g. Glacier.UploadArchive)
+            return $request->getHeaderLine('X-Content-Sha256');
+        }
+
+        if (!$request->getBody()->isSeekable()) {
+            throw new CouldNotCreateChecksumException('sha256');
+        }
+
+        try {
+            return Utils::hash($request->getBody(), 'sha256');
+        } catch (\Exception $e) {
+            throw new CouldNotCreateChecksumException('sha256', $e);
+        }
+    }
+
+    protected function createCanonicalizedPath($path)
+    {
+        $doubleEncoded = rawurlencode(ltrim($path, '/'));
+
+        return '/' . str_replace('%2F', '/', $doubleEncoded);
+    }
+
+    private function createStringToSign($longDate, $credentialScope, $creq)
+    {
+        $hash = hash('sha256', $creq);
+
+        return "HMAC-SHA256\n{$longDate}\n{$credentialScope}\n{$hash}";
+    }
+
+    /**
+     * @param array $parsedRequest
+     * @param string $payload Hash of the request payload
+     * @return array Returns an array of context information
+     */
+    private function createContext(array $parsedRequest, $payload)
+    {
+        // The following headers are not signed because signing these headers
+        // would potentially cause a signature mismatch when sending a request
+        // through a proxy or if modified at the HTTP client level.
+        static $blacklist = [
+            'cache-control' => true,
+            'content-type' => true,
+            'content-length' => true,
+            'expect' => true,
+            'max-forwards' => true,
+            'pragma' => true,
+            'range' => true,
+            'te' => true,
+            'if-match' => true,
+            'if-none-match' => true,
+            'if-modified-since' => true,
+            'if-unmodified-since' => true,
+            'if-range' => true,
+            'accept' => true,
+            'authorization' => true,
+            'proxy-authorization' => true,
+            'from' => true,
+            'referer' => true,
+            'user-agent' => true
+        ];
+
+        // Normalize the path as required by SigV4
+        $canon = $parsedRequest['method'] . "\n"
+            . $this->createCanonicalizedPath($parsedRequest['path']) . "\n"
+            . $this->getCanonicalizedQuery($parsedRequest['query']) . "\n";
+
+        $signedHeadersString = '';
+        $canonHeaders = [];
+        // Case-insensitively aggregate all of the headers.
+        if (!isset($parsedRequest['query']['X-SignedQueries'])) {
+            $aggregate = [];
+            foreach ($parsedRequest['headers'] as $key => $values) {
+                $key = strtolower($key);
+                if (!isset($blacklist[$key])) {
+                    foreach ($values as $v) {
+                        $aggregate[$key][] = $v;
+                    }
+                }
+            }
+
+            ksort($aggregate);
+            foreach ($aggregate as $k => $v) {
+                if (count($v) > 0) {
+                    sort($v);
+                }
+                $canonHeaders[] = $k . ':' . preg_replace('/\s+/', ' ', implode(',', $v));
+            }
+
+            $signedHeadersString = implode(';', array_keys($aggregate));
+        }
+        $canon .= implode("\n", $canonHeaders) . "\n\n"
+            . $signedHeadersString . "\n"
+            . $payload;
+
+        return ['creq' => $canon, 'headers' => $signedHeadersString];
+    }
+
+    private function getCanonicalizedQuery(array $query)
+    {
+        unset($query['X-Signature']);
+
+        if (!$query) {
+            return '';
+        }
+
+        $qs = '';
+        if (isset($query['X-SignedQueries'])) {
+            foreach (explode(';', $query['X-SignedQueries']) as $k) {
+                $v = $query[$k];
+                if (!is_array($v)) {
+                    $qs .= rawurlencode($k) . '=' . rawurlencode($v) . '&';
+                } else {
+                    sort($v);
+                    foreach ($v as $value) {
+                        $qs .= rawurlencode($k) . '=' . rawurlencode($value) . '&';
+                    }
+                }
+            }
+        } else {
+            ksort($query);
+            foreach ($query as $k => $v) {
+                if (!is_array($v)) {
+                    $qs .= rawurlencode($k) . '=' . rawurlencode($v) . '&';
+                } else {
+                    sort($v);
+                    foreach ($v as $value) {
+                        $qs .= rawurlencode($k) . '=' . rawurlencode($value) . '&';
+                    }
+                }
+            }
+        }
+
+        return substr($qs, 0, -1);
+    }
+
+    private function parseRequest(RequestInterface $request)
+    {
+        // Clean up any previously set headers.
+        /** @var RequestInterface $request */
+        $request = $request
+            ->withoutHeader('X-Date')
+            ->withoutHeader('Date')
+            ->withoutHeader('Authorization');
+        $uri = $request->getUri();
+
+        return [
+            'method' => $request->getMethod(),
+            'path' => $uri->getPath(),
+            'query' => Query::parse($uri->getQuery()),
+            'uri' => $uri,
+            'headers' => $request->getHeaders(),
+            'body' => $request->getBody(),
+            'version' => $request->getProtocolVersion()
+        ];
+    }
+
+    private function buildRequest(array $req)
+    {
+        if ($req['query']) {
+            $req['uri'] = $req['uri']->withQuery(Query::build($req['query']));
+        }
+
+        return new Psr7\Request(
+            $req['method'],
+            $req['uri'],
+            $req['headers'],
+            $req['body'],
+            $req['version']
+        );
+    }
+
+    private function buildRequestString(array $req)
+    {
+        if ($req['query']) {
+            $req['uri'] = $req['uri']->withQuery(Query::build($req['query']));
+        }
+        return (string)$req['uri'];
+    }
+}
diff --git a/vendor/volcengine/volc-sdk-php/src/Base/Singleton.php b/vendor/volcengine/volc-sdk-php/src/Base/Singleton.php
new file mode 100755
index 0000000..65d3981
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Base/Singleton.php
@@ -0,0 +1,30 @@
+<?php
+/*
+ * creator: maigohuang
+ * */
+
+namespace Volc\Base;
+
+use Exception;
+
+class Singleton
+{
+    private static $instances = array();
+    public function __construct()
+    { }
+    public function __clone()
+    { }
+    public function __wakeup()
+    {
+        throw new Exception('Cannot unserialize');
+    }
+
+    public static function getInstance(string $region = 'cn-north-1')
+    {
+        $cls = get_called_class();
+        if (!isset(self::$instances[$cls])) {
+            self::$instances[$cls] = new static($region);
+        }
+        return self::$instances[$cls];
+    }
+}
diff --git a/vendor/volcengine/volc-sdk-php/src/Base/V4Curl.php b/vendor/volcengine/volc-sdk-php/src/Base/V4Curl.php
new file mode 100755
index 0000000..c5ce05d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Base/V4Curl.php
@@ -0,0 +1,330 @@
+<?php
+
+/**
+ * creator: maigohuang
+ */
+
+namespace Volc\Base;
+
+use Exception;
+use GuzzleHttp\Client;
+use GuzzleHttp\Psr7\Uri;
+use GuzzleHttp\Psr7\Request;
+use GuzzleHttp\HandlerStack;
+use Psr\Http\Message\RequestInterface;
+use GuzzleHttp\Exception\ClientException;
+use Throwable;
+
+abstract class V4Curl extends Singleton
+{
+    protected $client = null;
+    protected $stack = null;
+    protected $region = '';
+    protected $ak = '';
+    protected $sk = '';
+
+
+    public function __construct()
+    {
+        $this->region = func_get_arg(0);
+        $this->stack = HandlerStack::create();
+        $this->stack->push($this->replaceUri());
+        $this->stack->push($this->v4Sign());
+
+        $config = $this->getConfig($this->region);
+        $this->client = new Client([
+            'handler' => $this->stack,
+            'base_uri' => $config['host'],
+        ]);
+
+        $this->version = trim(file_get_contents(__DIR__ . '/../../VERSION'));
+
+    }
+
+    public function setAccessKey($ak)
+    {
+        if ($ak != "") {
+            $this->ak = $ak;
+        }
+    }
+
+    public function setHost($host)
+    {
+        if ($host == "") {
+            return;
+        }
+        if (substr($host, 0, 8) != "https://" && substr($host, 0, 7) != "http://") {
+            $host = "https://" . $host;
+        }
+        $this->client = new Client([
+            'handler' => $this->stack,
+            'base_uri' => $host,
+        ]);
+    }
+
+    public function setSecretKey($sk)
+    {
+        if ($sk != "") {
+            $this->sk = $sk;
+        }
+    }
+
+    protected function v4Sign()
+    {
+        return function (callable $handler) {
+            return function (RequestInterface $request, array $options) use ($handler) {
+                $v4 = new SignatureV4();
+                $credentials = $this->prepareCredentials($options['v4_credentials']);
+                $request = $v4->signRequest($request, $credentials);
+                return $handler($request, $options);
+            };
+        };
+    }
+
+    abstract protected function getConfig(string $region);
+
+    private function prepareCredentials(array $credentials)
+    {
+        if (!isset($credentials['ak']) || !isset($credentials['sk'])) {
+            if ($this->ak != "" && $this->sk != "") {
+                $credentials['ak'] = $this->ak;
+                $credentials['sk'] = $this->sk;
+            } elseif (getenv("VOLC_ACCESSKEY") != "" && getenv("VOLC_SECRETKEY") != "") {
+                $credentials['ak'] = getenv("VOLC_ACCESSKEY");
+                $credentials['sk'] = getenv("VOLC_SECRETKEY");
+            } else {
+                $json = json_decode(file_get_contents(getenv('HOME') . '/.volc/config'), true);
+                if (is_array($json) && isset($json['ak']) && isset($json['sk'])) {
+                    $credentials = array_merge($credentials, $json);
+                }
+            }
+        }
+        return $credentials;
+    }
+
+    public function getRequestUrl($api, array $config = [])
+    {
+        $config_api = isset($this->apiList[$api]) ? $this->apiList[$api] : false;
+
+        $defaultConfig = $this->getConfig($this->region);
+        $config = $this->configMerge($defaultConfig['config'], $config_api['config'], $config);
+        $info = array_merge($defaultConfig, $config_api);
+
+        $method = $info['method'];
+        $request = new Request($method, $info['host'] . $info['url'] . '?' . http_build_query($config['query']));
+
+        $credentials = $this->prepareCredentials($config['v4_credentials']);
+        $v4 = new SignatureV4();
+
+        return $v4->signRequestToUrl($request, $credentials);
+    }
+
+
+    public function request($api, array $config = [])
+    {
+        $config_api = isset($this->apiList[$api]) ? $this->apiList[$api] : false;
+
+        $defaultConfig = $this->getConfig($this->region);
+        $config = $this->configMerge($defaultConfig['config'], $config_api['config'], $config);
+        $config['headers']['User-Agent'] = 'volc-sdk-php/' . $this->version;
+        $info = array_merge($defaultConfig, $config_api);
+        $info['config'] = $config;
+
+        $method = $info['method'];
+        try {
+            $response = $this->client->request($method, $info['url'], $info['config']);
+            return $response;
+        } catch (ClientException $exception) {
+            return $exception->getResponse();
+        }
+    }
+
+    public function signSts2(array $policy, int $expire)
+    {
+        // 获取长期的aksk信息
+        $credentials = $this->prepareCredentials([]);
+        $now = time();
+
+        $sts = [
+            "AccessKeyID" => $this->generateAccessKeyId("AKTP"),
+            "SecretAccessKey" => $this->generateSecretKey(),
+            "ExpiredTime" => date('Y-m-d\TH:i:sP', $now + $expire),
+            "CurrentTime" => date('Y-m-d\TH:i:sP', $now),
+        ];
+
+        $innerToken = $this->createInnerToken($credentials, $sts, $policy, $now + $expire);
+        $sts["SessionToken"] = "STS2" . base64_encode(json_encode($innerToken));
+
+        return $sts;
+    }
+
+    private function createInnerToken(array $credentials, array $sts, array $policy, int $expire)
+    {
+        $inner = [
+            "LTAccessKeyId" => $credentials["ak"],
+            "AccessKeyId" => $sts["AccessKeyID"],
+            "ExpiredTime" => $expire,
+        ];
+
+        $key = md5($credentials["sk"], true);
+        $inner["SignedSecretAccessKey"] = $this->aesEncrypt($sts["SecretAccessKey"], $key);
+
+        if (sizeof($policy) > 0) {
+            $inner["PolicyString"] = json_encode($policy);
+        } else {
+            $inner["PolicyString"] = "";
+        }
+
+        $signStr = sprintf("%s|%s|%d|%s|%s",
+            $inner["LTAccessKeyId"],
+            $inner["AccessKeyId"],
+            $inner["ExpiredTime"],
+            $inner["SignedSecretAccessKey"],
+            $inner["PolicyString"]);
+
+        $inner["Signature"] = hash_hmac('sha256', $signStr, $key);
+
+        return $inner;
+    }
+
+    public function newAllowStatement(array $actions, array $resources)
+    {
+        return [
+            "Effect" => "Allow",
+            "Action" => $actions,
+            "Resource" => $resources,
+        ];
+    }
+
+    public function newDenyStatement(array $actions, array $resources)
+    {
+        return [
+            "Effect" => "Deny",
+            "Action" => $actions,
+            "Resource" => $resources,
+        ];
+    }
+
+    protected function configMerge($c1, $c2, $c3)
+    {
+        $result = $c1;
+        foreach ($c2 as $k => $v) {
+            if (isset($result[$k]) && is_array($result[$k]) && is_array($v)) {
+                $result[$k] = array_merge($result[$k], $v);
+            } else {
+                $result[$k] = $v;
+            }
+        }
+
+        foreach ($c3 as $k => $v) {
+            if (isset($result[$k]) && is_array($result[$k]) && is_array($v)) {
+                $result[$k] = array_merge($result[$k], $v);
+            } else {
+                $result[$k] = $v;
+            }
+        }
+        return $result;
+    }
+
+    protected function replaceUri()
+    {
+        return function (callable $handler) {
+            return function (RequestInterface $request, array $options) use ($handler) {
+                if (isset($options['replace'])) {
+                    $replace = $options['replace'];
+                    $uri = (string)$request->getUri();
+
+                    $func = function ($matches) use ($replace) {
+                        $key = substr($matches[0], 1, -1);
+                        return $replace[$key];
+                    };
+                    $uri = preg_replace_callback('/\{.*?\}/', $func, $uri);
+
+                    $func2 = function ($matches) use ($replace) {
+                        $key = substr($matches[0], 3, -3);
+                        return $replace[$key];
+                    };
+                    $uri = preg_replace_callback('/%7B.*?%7D/', $func2, $uri);
+                    $request = $request->withUri(new Uri($uri));
+                }
+                return $handler($request, $options);
+            };
+        };
+    }
+
+    protected function generateAccessKeyId($prefix)
+    {
+        // 随机128bit,转化成16进制后再base64编码
+        $accessKeyId = $prefix . base64_encode(bin2hex(random_bytes(16)));
+
+        $accessKeyId = str_replace("=", "", $accessKeyId);
+        $accessKeyId = str_replace("/", "", $accessKeyId);
+        $accessKeyId = str_replace("+", "", $accessKeyId);
+        $accessKeyId = str_replace("-", "", $accessKeyId);
+        return $accessKeyId;
+    }
+
+    protected function generateSecretKey()
+    {
+        return base64_encode(bin2hex(random_bytes(30)));
+    }
+
+    protected function aesEncrypt($src, $pwd)
+    {
+        return base64_encode(openssl_encrypt($src, "AES-128-CBC", $pwd, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $pwd));
+    }
+
+    /**
+     * @throws Exception
+     * @throws Throwable
+     */
+    protected function createHlsDrmAuthToken(string $authAlgorithm, int $expireSeconds)
+    {
+        if ($expireSeconds == 0) {
+            throw new Exception("invalid exception");
+        }
+        $credentials = $this->prepareCredentials($this->getConfig($this->region));
+        try {
+            $token = $this->createAuth($authAlgorithm, "2.0", $credentials['ak'], $credentials['sk'], $this->region, $expireSeconds);
+            $query = array(
+                "DrmAuthToken" => $token,
+                "X-Expires" => strval($expireSeconds),
+            );
+            return parse_url($this->getRequestUrl("GetHlsDecryptionKey", ['query' => $query]))['query'];
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+    }
+
+
+    /**
+     * @throws Exception
+     */
+    private function createAuth(string $dsa, string $version, string $accessKey, string $secretKey, $region, int $expireSeconds): string
+    {
+        if ($accessKey == "") {
+            throw new Exception("invalid accessKey");
+        }
+        if ($secretKey == "") {
+            throw new Exception("invalid secretKey");
+        }
+        $timestamp = time() + $expireSeconds;
+        $deadline = gmdate("Ymd\THis\Z", $timestamp);
+        $kDate = hash_hmac('sha256', $deadline, $secretKey, true);
+        $kRegion = hash_hmac('sha256', $region, $kDate, true);
+        $kService = hash_hmac('sha256', "vod", $kRegion, true);
+        $kCredentials = hash_hmac('sha256', "request", $kService, true);
+        $dateKey = bin2hex($kCredentials);
+        $data = $dsa . "&" . $version . "&" . $timestamp;
+        switch ($dsa) {
+            case "HMAC-SHA1":
+                $sign = base64_encode(hash_hmac('sha1', $data, $dateKey, true));
+                break;
+            default:
+                throw new Exception("invalid dsa");
+        }
+        return $dsa . ":" . $version . ":" . $timestamp . ":" . $accessKey . ":" . $sign;
+    }
+}
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/AdBlocker.php b/vendor/volcengine/volc-sdk-php/src/Service/AdBlocker.php
new file mode 100755
index 0000000..b62f74a
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/AdBlocker.php
@@ -0,0 +1,68 @@
+<?php
+namespace Volc\Service;
+use Volc\Base\V4Curl;
+
+class AdBlocker extends V4Curl
+{
+    protected function getConfig(string $region)
+    {
+        switch ($region) {
+            case 'cn-north-1':
+                $config = [
+                    'host' => 'https://riskcontrol.volcengineapi.com',
+                    'config' => [
+                        'timeout' => 5.0,
+                        'headers' => [
+                            'Accept' => 'application/json'
+                        ],
+                        'v4_credentials' => [
+                            'region' => 'cn-north-1',
+                            'service' => 'AdBlocker',
+                        ],
+                    ],
+                ];
+                break;
+            default:
+                throw new \Exception(sprintf("AdBlocker not support region, %s", $region));
+        }
+        return $config;
+    }
+
+    protected function requestWithRetry(string $api, array $configs): string
+    {
+        try {
+            $response = $this->request($api, $configs);
+            return (string)$response->getBody();
+        }
+        catch (\Exception $e)
+        {
+            $response = $this->request($api, $configs);
+            return (string)$response->getBody();
+        }
+    }
+
+    protected $apiList = [
+        'AdBlock' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'AdBlock',
+                    'Version' => '2021-01-06',
+                ],
+            ]
+        ],
+    ];
+
+    public function adBlock(int $appId, string $service, string $parameters)
+    {
+        $commitBody = array();
+        $commitBody["AppId"] = $appId;
+        $commitBody["Service"] = $service;
+        $commitBody["Parameters"] = $parameters;
+        $commitReq = [
+            "json" => $commitBody
+        ];
+        return $this->requestWithRetry("AdBlock", $commitReq);
+    }
+}
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Base/Models/Base/ResponseError.php b/vendor/volcengine/volc-sdk-php/src/Service/Base/Models/Base/ResponseError.php
new file mode 100755
index 0000000..4794469
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Base/Models/Base/ResponseError.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: base/base.proto
+
+namespace Volc\Service\Base\Models\Base;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Base.Models.Base.ResponseError</code>
+ */
+class ResponseError extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 错误码
+     *
+     * Generated from protobuf field <code>string Code = 1;</code>
+     */
+    protected $Code = '';
+    /**
+     * 详细错误信息
+     *
+     * Generated from protobuf field <code>string Message = 2;</code>
+     */
+    protected $Message = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Code
+     *           错误码
+     *     @type string $Message
+     *           详细错误信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Base\Models\GPBMetadata\Base::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 错误码
+     *
+     * Generated from protobuf field <code>string Code = 1;</code>
+     * @return string
+     */
+    public function getCode()
+    {
+        return $this->Code;
+    }
+
+    /**
+     * 错误码
+     *
+     * Generated from protobuf field <code>string Code = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCode($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Code = $var;
+
+        return $this;
+    }
+
+    /**
+     * 详细错误信息
+     *
+     * Generated from protobuf field <code>string Message = 2;</code>
+     * @return string
+     */
+    public function getMessage()
+    {
+        return $this->Message;
+    }
+
+    /**
+     * 详细错误信息
+     *
+     * Generated from protobuf field <code>string Message = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setMessage($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Message = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Base/Models/Base/ResponseMetadata.php b/vendor/volcengine/volc-sdk-php/src/Service/Base/Models/Base/ResponseMetadata.php
new file mode 100755
index 0000000..2cacbee
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Base/Models/Base/ResponseMetadata.php
@@ -0,0 +1,245 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: base/base.proto
+
+namespace Volc\Service\Base\Models\Base;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Base.Models.Base.ResponseMetadata</code>
+ */
+class ResponseMetadata extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 请求ID
+     *
+     * Generated from protobuf field <code>string RequestId = 1;</code>
+     */
+    protected $RequestId = '';
+    /**
+     * 请求接口
+     *
+     * Generated from protobuf field <code>string Action = 2;</code>
+     */
+    protected $Action = '';
+    /**
+     * 版本
+     *
+     * Generated from protobuf field <code>string Version = 3;</code>
+     */
+    protected $Version = '';
+    /**
+     * 服务
+     *
+     * Generated from protobuf field <code>string Service = 4;</code>
+     */
+    protected $Service = '';
+    /**
+     * 区域
+     *
+     * Generated from protobuf field <code>string Region = 5;</code>
+     */
+    protected $Region = '';
+    /**
+     * 异常信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseError Error = 6;</code>
+     */
+    protected $Error = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $RequestId
+     *           请求ID
+     *     @type string $Action
+     *           请求接口
+     *     @type string $Version
+     *           版本
+     *     @type string $Service
+     *           服务
+     *     @type string $Region
+     *           区域
+     *     @type \Volc\Service\Base\Models\Base\ResponseError $Error
+     *           异常信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Base\Models\GPBMetadata\Base::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 请求ID
+     *
+     * Generated from protobuf field <code>string RequestId = 1;</code>
+     * @return string
+     */
+    public function getRequestId()
+    {
+        return $this->RequestId;
+    }
+
+    /**
+     * 请求ID
+     *
+     * Generated from protobuf field <code>string RequestId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setRequestId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->RequestId = $var;
+
+        return $this;
+    }
+
+    /**
+     * 请求接口
+     *
+     * Generated from protobuf field <code>string Action = 2;</code>
+     * @return string
+     */
+    public function getAction()
+    {
+        return $this->Action;
+    }
+
+    /**
+     * 请求接口
+     *
+     * Generated from protobuf field <code>string Action = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setAction($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Action = $var;
+
+        return $this;
+    }
+
+    /**
+     * 版本
+     *
+     * Generated from protobuf field <code>string Version = 3;</code>
+     * @return string
+     */
+    public function getVersion()
+    {
+        return $this->Version;
+    }
+
+    /**
+     * 版本
+     *
+     * Generated from protobuf field <code>string Version = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVersion($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Version = $var;
+
+        return $this;
+    }
+
+    /**
+     * 服务
+     *
+     * Generated from protobuf field <code>string Service = 4;</code>
+     * @return string
+     */
+    public function getService()
+    {
+        return $this->Service;
+    }
+
+    /**
+     * 服务
+     *
+     * Generated from protobuf field <code>string Service = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setService($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Service = $var;
+
+        return $this;
+    }
+
+    /**
+     * 区域
+     *
+     * Generated from protobuf field <code>string Region = 5;</code>
+     * @return string
+     */
+    public function getRegion()
+    {
+        return $this->Region;
+    }
+
+    /**
+     * 区域
+     *
+     * Generated from protobuf field <code>string Region = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setRegion($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Region = $var;
+
+        return $this;
+    }
+
+    /**
+     * 异常信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseError Error = 6;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseError|null
+     */
+    public function getError()
+    {
+        return $this->Error;
+    }
+
+    public function hasError()
+    {
+        return isset($this->Error);
+    }
+
+    public function clearError()
+    {
+        unset($this->Error);
+    }
+
+    /**
+     * 异常信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseError Error = 6;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseError $var
+     * @return $this
+     */
+    public function setError($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseError::class);
+        $this->Error = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Base/Models/GPBMetadata/Base.php b/vendor/volcengine/volc-sdk-php/src/Service/Base/Models/GPBMetadata/Base.php
new file mode 100755
index 0000000..2912e36
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Base/Models/GPBMetadata/Base.php
Binary files differ
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Billing.php b/vendor/volcengine/volc-sdk-php/src/Service/Billing.php
new file mode 100755
index 0000000..1603660
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Billing.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace Volc\Service;
+
+use Volc\Base\V4Curl;
+
+class Billing extends V4Curl
+{
+    protected function getConfig(string $region = '')
+    {
+        return [
+            'host' => 'https://billing.volcengineapi.com',
+            'config' => [
+                'timeout' => 5.0,
+                'headers' => [
+                    'Accept' => 'application/json'
+                ],
+                'v4_credentials' => [
+                    'region' => 'cn-north-1',
+                    'service' => 'billing',
+                ],
+            ],
+        ];
+    }
+
+    public function listBill(array $query = [])
+    {        
+        $response = $this->request('ListBill', $query);
+        return $response->getBody();
+    }
+
+    public function listBillDetail(array $query = [])
+    {
+        $response = $this->request('ListBillDetail', $query);
+        return $response->getBody();
+    }
+
+    public function listBillOverviewByProd(array $query = [])
+    {
+        $response = $this->request('ListBillOverviewByProd', $query);
+        return $response->getBody();
+    }
+
+    protected $apiList = [
+        'ListBill' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListBill',
+                    'Version' => '2022-01-01',
+                ],
+            ],
+        ],
+        'ListBillDetail' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListBillDetail',
+                    'Version' => '2022-01-01',
+                ],
+            ],
+        ],
+        'ListBillOverviewByProd' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListBillOverviewByProd',
+                    'Version' => '2022-01-01',
+                ],
+            ],
+        ],
+    ];
+}
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/BusinessSecurity.php b/vendor/volcengine/volc-sdk-php/src/Service/BusinessSecurity.php
new file mode 100755
index 0000000..5d459ff
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/BusinessSecurity.php
@@ -0,0 +1,489 @@
+<?php
+
+
+namespace Volc\Service;
+use Volc\Base\V4Curl;
+
+class BusinessSecurity extends V4Curl
+{
+    protected function getConfig(string $region)
+    {
+        switch ($region) {
+            case 'cn-north-1':
+                $config = [
+                    'host' => 'https://open.volcengineapi.com',
+                    'config' => [
+                        'timeout' => 5.0,
+                        'headers' => [
+                            'Accept' => 'application/json'
+                        ],
+                        'v4_credentials' => [
+                            'region' => 'cn-north-1',
+                            'service' => 'BusinessSecurity',
+                        ],
+                    ],
+                ];
+                break;
+            default:
+                throw new \Exception(sprintf("AdBlocker not support region, %s", $region));
+        }
+        return $config;
+    }
+
+    protected $apiList = [
+        'RiskDetection' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'RiskDetection',
+                    'Version' => '2021-02-02',
+                ],
+            ]
+        ],
+        'AsyncRiskDetection' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'AsyncRiskDetection',
+                    'Version' => '2021-02-25',
+                ],
+            ]
+        ],
+        'RiskResult' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'RiskResult',
+                    'Version' => '2021-03-10',
+                ],
+            ]
+        ],
+        'ElementVerify' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'ElementVerify',
+                    'Version' => '2021-11-23',
+                ],
+            ]
+        ],
+        'ElementVerifyV2' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'ElementVerify',
+                    'Version' => '2022-04-13',
+                ],
+            ]
+        ],
+        'MobileStatus' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'MobileStatus',
+                    'Version' => '2020-12-25',
+                ],
+            ]
+        ],
+        'MobileStatusV2' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'MobileStatus',
+                    'Version' => '2022-04-13',
+                ],
+            ]
+        ],
+        'ImageContentRisk' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'ImageContentRisk',
+                    'Version' => '2021-11-29',
+                ],
+            ]
+        ],
+        'AsyncImageRisk' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'AsyncImageRisk',
+                    'Version' => '2021-11-29',
+                ],
+            ]
+        ],
+        'GetImageResult' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetImageResult',
+                    'Version' => '2021-11-29',
+                ],
+            ]
+        ],
+        'TextRisk' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'TextRisk',
+                    'Version' => '2022-01-26',
+                ],
+            ]
+        ],
+        'AsyncAudioRisk' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'AsyncAudioRisk',
+                    'Version' => '2022-04-01',
+                ],
+            ]
+        ],
+        'AudioResult' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetAudioResult',
+                    'Version' => '2022-04-01',
+                ],
+            ]
+        ],
+        'AsyncVideoRisk' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'AsyncVideoRisk',
+                    'Version' => '2021-11-29',
+                ],
+            ]
+        ],
+        'VideoResult' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'VideoResult',
+                    'Version' => '2022-08-26',
+                ],
+            ]
+        ],
+        'AsyncLiveAudioRisk' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'AsyncLiveAudioRisk',
+                    'Version' => '2022-04-25',
+                ],
+            ]
+        ],
+        'AudioLiveResult' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetAudioLiveResult',
+                    'Version' => '2022-04-25',
+                ],
+            ]
+        ],
+        'AsyncLiveVideoRisk' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'AsyncLiveVideoRisk',
+                    'Version' => '2022-04-25',
+                ],
+            ]
+        ],
+        'GetVideoLiveResult' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetVideoLiveResult',
+                    'Version' => '2022-04-25',
+                ],
+            ]
+        ],
+        'TextSliceRisk' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'TextSliceRisk',
+                    'Version' => '2022-11-07',
+                ],
+            ]
+        ],
+    ];
+
+    protected function requestWithRetry(string $api, array $configs): string
+    {
+        try {
+            $response = $this->request($api, $configs);
+            return (string)$response->getBody();
+        } catch (\Exception $e) {
+            $response = $this->request($api, $configs);
+            return (string)$response->getBody();
+        }
+    }
+
+    public function RiskDetect(int $appId, string $service, string $parameters): string
+    {
+        $commitBody = array();
+        $commitBody["AppId"] = $appId;
+        $commitBody["Service"] = $service;
+        $commitBody["Parameters"] = $parameters;
+        $commitReq = [
+            "json" => $commitBody
+        ];
+        return $this->requestWithRetry("RiskDetection", $commitReq);
+    }
+
+    public function AsyncRiskDetect(int $appId, string  $service, string $parameters): string
+    {
+        $commitBody = array();
+        $commitBody["AppId"] = $appId;
+        $commitBody["Service"] = $service;
+        $commitBody["Parameters"] = $parameters;
+        $commitReq = [
+            "json" => $commitBody
+        ];
+        return $this->requestWithRetry("AsyncRiskDetection", $commitReq);
+    }
+
+    public function RiskResult(int $appId, string $service, int $startTime, int $endTime, int $pageSize, int $pageNum): string
+    {
+        $commitBody = array();
+        $commitBody["AppId"] = $appId;
+        $commitBody["Service"] = $service;
+        $commitBody["StartTime"] = $startTime;
+        $commitBody["EndTime"] = $endTime;
+        $commitBody["PageSize"] = $pageSize;
+        $commitBody["PageNum"] = $pageNum;
+        $commitReq = [
+            "query" => $commitBody
+        ];
+        return $this->requestWithRetry("RiskResult", $commitReq);
+    }
+
+    public function ElementVerify(int $appId, string $service, string $parameters): string
+    {
+        $commitBody = array();
+        $commitBody["AppId"] = $appId;
+        $commitBody["Service"] = $service;
+        $commitBody["Parameters"] = $parameters;
+        $commitReq = [
+            "json" => $commitBody
+        ];
+        return $this->requestWithRetry("ElementVerify", $commitReq);
+    }
+
+    public function ElementVerifyV2(int $appId, string $service, string $parameters): string
+    {
+        $commitBody = array();
+        $commitBody["AppId"] = $appId;
+        $commitBody["Service"] = $service;
+        $commitBody["Parameters"] = $parameters;
+        $commitReq = [
+            "json" => $commitBody
+        ];
+        return $this->requestWithRetry("ElementVerifyV2", $commitReq);
+    }
+
+    public function MobileStatus(int $appId, string $service, string $parameters): string
+    {
+        $commitBody = array();
+        $commitBody["AppId"] = $appId;
+        $commitBody["Service"] = $service;
+        $commitBody["Parameters"] = $parameters;
+        $commitReq = [
+            "json" => $commitBody
+        ];
+        return $this->requestWithRetry("MobileStatus", $commitReq);
+    }
+
+    public function MobileStatusV2(int $appId, string $service, string $parameters): string
+    {
+        $commitBody = array();
+        $commitBody["AppId"] = $appId;
+        $commitBody["Service"] = $service;
+        $commitBody["Parameters"] = $parameters;
+        $commitReq = [
+            "json" => $commitBody
+        ];
+        return $this->requestWithRetry("MobileStatusV2", $commitReq);
+    }
+
+    public function ImageContentRisk(int $appId, string $service, string $parameters): string
+    {
+        $commitBody = array();
+        $commitBody["AppId"] = $appId;
+        $commitBody["Service"] = $service;
+        $commitBody["Parameters"] = $parameters;
+        $commitReq = [
+            "json" => $commitBody
+        ];
+        return $this->requestWithRetry("ImageContentRisk", $commitReq);
+    }
+
+    public function AsyncImageRisk(int $appId, string  $service, string $parameters): string
+    {
+        $commitBody = array();
+        $commitBody["AppId"] = $appId;
+        $commitBody["Service"] = $service;
+        $commitBody["Parameters"] = $parameters;
+        $commitReq = [
+            "json" => $commitBody
+        ];
+        return $this->requestWithRetry("AsyncImageRisk", $commitReq);
+    }
+
+    public function GetImageResult(int $appId, string $service, string  $dataId): string
+    {
+        $commitBody = array();
+        $commitBody["AppId"] = $appId;
+        $commitBody["Service"] = $service;
+        $commitBody["DataId"] = $dataId;
+        $commitReq = [
+            "query" => $commitBody
+        ];
+        return $this->requestWithRetry("GetImageResult", $commitReq);
+    }
+
+    public function TextRisk(int $appId, string $service, string $parameters): string
+    {
+        $commitBody = array();
+        $commitBody["AppId"] = $appId;
+        $commitBody["Service"] = $service;
+        $commitBody["Parameters"] = $parameters;
+        $commitReq = [
+            "json" => $commitBody
+        ];
+        return $this->requestWithRetry("TextRisk", $commitReq);
+    }
+
+    public function AsyncAudioRisk(int $appId, string $service, string $parameters): string
+    {
+        $commitBody = array();
+        $commitBody["AppId"] = $appId;
+        $commitBody["Service"] = $service;
+        $commitBody["Parameters"] = $parameters;
+        $commitReq = [
+            "json" => $commitBody
+        ];
+        return $this->requestWithRetry("AsyncAudioRisk", $commitReq);
+    }
+
+    public function AsyncLiveAudioRisk(int $appId, string $service, string $parameters): string
+    {
+        $commitBody = array();
+        $commitBody["AppId"] = $appId;
+        $commitBody["Service"] = $service;
+        $commitBody["Parameters"] = $parameters;
+        $commitReq = [
+            "json" => $commitBody
+        ];
+        return $this->requestWithRetry("AsyncLiveAudioRisk", $commitReq);
+    }
+
+    public function GetAudioResult(int $appId, string $service, string $dataId): string
+    {
+        $commitBody = array();
+        $commitBody["AppId"] = $appId;
+        $commitBody["Service"] = $service;
+        $commitBody["DataId"] = $dataId;
+        $commitReq = [
+            "query" => $commitBody
+        ];
+        return $this->requestWithRetry("AudioResult", $commitReq);
+    }
+
+    public function GetAudioLiveResult(int $appId, string $service, string  $dataId): string
+    {
+        $commitBody = array();
+        $commitBody["AppId"] = $appId;
+        $commitBody["Service"] = $service;
+        $commitBody["DataId"] = $dataId;
+        $commitReq = [
+            "query" => $commitBody
+        ];
+        return $this->requestWithRetry("GetAudioLiveResult", $commitReq);
+    }
+
+    public function AsyncVideoRisk(int $appId, string $service, string $parameters): string
+    {
+        $commitBody = array();
+        $commitBody["AppId"] = $appId;
+        $commitBody["Service"] = $service;
+        $commitBody["Parameters"] = $parameters;
+        $commitReq = [
+            "json" => $commitBody
+        ];
+        return $this->requestWithRetry("AsyncVideoRisk", $commitReq);
+    }
+
+    public function AsyncLiveVideoRisk(int $appId, string $service, string $parameters): string
+    {
+        $commitBody = array();
+        $commitBody["AppId"] = $appId;
+        $commitBody["Service"] = $service;
+        $commitBody["Parameters"] = $parameters;
+        $commitReq = [
+            "json" => $commitBody
+        ];
+        return $this->requestWithRetry("AsyncLiveVideoRisk", $commitReq);
+    }
+
+    public function GetVideoResult(int $appId, string $service, string  $dataId): string
+    {
+        $commitBody = array();
+        $commitBody["AppId"] = $appId;
+        $commitBody["Service"] = $service;
+        $commitBody["DataId"] = $dataId;
+        $commitReq = [
+            "query" => $commitBody
+        ];
+        return $this->requestWithRetry("GetVideoResult", $commitReq);
+    }
+
+    public function GetVideoLiveResult(int $appId, string $service, string  $dataId): string
+    {
+        $commitBody = array();
+        $commitBody["AppId"] = $appId;
+        $commitBody["Service"] = $service;
+        $commitBody["DataId"] = $dataId;
+        $commitReq = [
+            "query" => $commitBody
+        ];
+        return $this->requestWithRetry("GetVideoLiveResult", $commitReq);
+    }
+
+    public function TextSliceRisk(int $appId, string $service, string $parameters): string
+    {
+        $commitBody = array();
+        $commitBody["AppId"] = $appId;
+        $commitBody["Service"] = $service;
+        $commitBody["Parameters"] = $parameters;
+        $commitReq = [
+            "json" => $commitBody
+        ];
+        return $this->requestWithRetry("TextSliceRisk", $commitReq);
+    }
+}
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Cdn.php b/vendor/volcengine/volc-sdk-php/src/Service/Cdn.php
new file mode 100755
index 0000000..27cb9d1
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Cdn.php
@@ -0,0 +1,733 @@
+<?php
+
+namespace Volc\Service;
+
+use Volc\Base\V4Curl;
+
+class Cdn extends V4Curl
+{
+
+    protected $apiList = [
+#添加加速域名: https://www.volcengine.com/docs/6454/97340
+        'AddCdnDomain' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'AddCdnDomain',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#上线加速域名: https://www.volcengine.com/docs/6454/74667
+        'StartCdnDomain' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'StartCdnDomain',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#下线加速域名: https://www.volcengine.com/docs/6454/75129
+        'StopCdnDomain' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'StopCdnDomain',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#删除加速域名: https://www.volcengine.com/docs/6454/75130
+        'DeleteCdnDomain' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DeleteCdnDomain',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#获取域名列表: https://www.volcengine.com/docs/6454/75269
+        'ListCdnDomains' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListCdnDomains',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#获取域名配置详情: https://www.volcengine.com/docs/6454/80320
+        'DescribeCdnConfig' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeCdnConfig',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#修改加速域名配置: https://www.volcengine.com/docs/6454/97266
+        'UpdateCdnConfig' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'UpdateCdnConfig',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#获取访问统计的细分数据: https://www.volcengine.com/docs/6454/70442
+        'DescribeCdnData' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeCdnData',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#获取访问统计的汇总数据: https://www.volcengine.com/docs/6454/96132
+        'DescribeEdgeNrtDataSummary' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeEdgeNrtDataSummary',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#获取回源统计的细分数据: https://www.volcengine.com/docs/6454/70443
+        'DescribeCdnOriginData' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeCdnOriginData',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#获取回源统计的汇总数据: https://www.volcengine.com/docs/6454/96133
+        'DescribeOriginNrtDataSummary' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeOriginNrtDataSummary',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#获取省份运营商的细分数据: https://www.volcengine.com/docs/6454/75159
+        'DescribeCdnDataDetail' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeCdnDataDetail',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#获取独立访客的细分数据: https://www.volcengine.com/docs/6454/79321
+        'DescribeEdgeStatisticalData' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeEdgeStatisticalData',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#获取访问统计的排行数据: https://www.volcengine.com/docs/6454/96145
+        'DescribeEdgeTopNrtData' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeEdgeTopNrtData',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#获取回源数据的统计排序: https://www.volcengine.com/docs/6454/104892
+        'DescribeOriginTopNrtData' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeOriginTopNrtData',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#获取访问状态码的统计排序: https://www.volcengine.com/docs/6454/104888
+        'DescribeEdgeTopStatusCode' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeEdgeTopStatusCode',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#获取回源状态码的统计排序: https://www.volcengine.com/docs/6454/104891
+        'DescribeOriginTopStatusCode' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeOriginTopStatusCode',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#获取热点及访客排行数据: https://www.volcengine.com/docs/6454/79322
+        'DescribeEdgeTopStatisticalData' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeEdgeTopStatisticalData',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#获取区域和 ISP 列表: https://www.volcengine.com/docs/6454/70445
+        'DescribeCdnRegionAndIsp' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeCdnRegionAndIsp',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#(Deprecated)查询域名排行数据: https://www.volcengine.com/docs/6454/70447?type=preview
+        'DescribeCdnDomainTopData' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeCdnDomainTopData',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#查询账号计费方式: https://www.volcengine.com/docs/6454/78999
+        'DescribeCdnService' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeCdnService',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#获取计费指标的细分数据: https://www.volcengine.com/docs/6454/96167
+        'DescribeAccountingData' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeAccountingData',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#提交刷新任务: https://www.volcengine.com/docs/6454/70438
+        'SubmitRefreshTask' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'SubmitRefreshTask',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#提交预热任务: https://www.volcengine.com/docs/6454/70436
+        'SubmitPreloadTask' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'SubmitPreloadTask',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#获取刷新预热任务信息: https://www.volcengine.com/docs/6454/70437
+        'DescribeContentTasks' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeContentTasks',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#获取刷新预热配额信息: https://www.volcengine.com/docs/6454/70439
+        'DescribeContentQuota' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeContentQuota',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#提交封禁任务: https://www.volcengine.com/docs/6454/79890
+        'SubmitBlockTask' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'SubmitBlockTask',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#提交解封任务: https://www.volcengine.com/docs/6454/79893
+        'SubmitUnblockTask' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'SubmitUnblockTask',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#获取封禁解封任务信息: https://www.volcengine.com/docs/6454/79906
+        'DescribeContentBlockTasks' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeContentBlockTasks',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#获取访问日志下载链接: https://www.volcengine.com/docs/6454/70446
+        'DescribeCdnAccessLog' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeCdnAccessLog',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#获取 IP 归属信息: https://www.volcengine.com/docs/6454/75233
+        'DescribeIPInfo' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeIPInfo',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#获取回源节点 IP 列表: https://www.volcengine.com/docs/6454/75273
+        'DescribeCdnUpperIp' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeCdnUpperIp',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#添加资源标签: https://www.volcengine.com/docs/6454/80308
+        'AddResourceTags' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'AddResourceTags',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#更新资源标签: https://www.volcengine.com/docs/6454/80313
+        'UpdateResourceTags' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'UpdateResourceTags',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#查询标签清单: https://www.volcengine.com/docs/6454/80315
+        'ListResourceTags' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListResourceTags',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+#删除资源标签: https://www.volcengine.com/docs/6454/80316
+        'DeleteResourceTags' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DeleteResourceTags',
+                    'Version' => '2021-03-01',
+                ],
+            ]
+        ],
+    ];
+
+    public function __construct()
+    {
+        $this->region = func_get_arg(0);
+        parent::__construct($this->region);
+    }
+
+    protected function getConfig(string $region = 'cn-north-1')
+    {
+        return [
+            'host' => "https://cdn.volcengineapi.com",
+            'config' => [
+                'timeout' => 60 * 5,
+                'headers' => [
+                    'Accept' => 'application/json',
+                    'Content-Type' => 'application/json'
+                ],
+                'v4_credentials' => [
+                    'region' => $region,
+                    'service' => 'CDN',
+                ],
+            ],
+        ];
+    }
+
+    public function requestWithRetry(string $api, array $configs): string
+    {
+        try {
+            $response = $this->request($api, $configs);
+            return $response->getBody()->getContents();
+        } catch (\Exception $e) {
+            $response = $this->request($api, $configs);
+            return $response->getBody()->getContents();
+        }
+    }
+
+    public function addCdnDomain(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("AddCdnDomain", ['json' => $data]);
+    }
+
+    public function startCdnDomain(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("StartCdnDomain", ['json' => $data]);
+    }
+
+    public function stopCdnDomain(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("StopCdnDomain", ['json' => $data]);
+    }
+
+    public function deleteCdnDomain(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("DeleteCdnDomain", ['json' => $data]);
+    }
+
+    public function listCdnDomains(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("ListCdnDomains", ['json' => $data]);
+    }
+
+    public function describeCdnConfig(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("DescribeCdnConfig", ['json' => $data]);
+    }
+
+    public function updateCdnConfig(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("UpdateCdnConfig", ['json' => $data]);
+    }
+
+    public function describeCdnData(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("DescribeCdnData", ['json' => $data]);
+    }
+
+    public function describeEdgeNrtDataSummary(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("DescribeEdgeNrtDataSummary", ['json' => $data]);
+    }
+
+    public function describeCdnOriginData(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("DescribeCdnOriginData", ['json' => $data]);
+    }
+
+    public function describeOriginNrtDataSummary(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("DescribeOriginNrtDataSummary", ['json' => $data]);
+    }
+
+    public function describeCdnDataDetail(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("DescribeCdnDataDetail", ['json' => $data]);
+    }
+
+    public function describeEdgeStatisticalData(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("DescribeEdgeStatisticalData", ['json' => $data]);
+    }
+
+    public function describeEdgeTopNrtData(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("DescribeEdgeTopNrtData", ['json' => $data]);
+    }
+
+    public function describeOriginTopNrtData(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("DescribeOriginTopNrtData", ['json' => $data]);
+    }
+
+    public function describeEdgeTopStatusCode(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("DescribeEdgeTopStatusCode", ['json' => $data]);
+    }
+
+    public function describeOriginTopStatusCode(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("DescribeOriginTopStatusCode", ['json' => $data]);
+    }
+
+    public function describeEdgeTopStatisticalData(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("DescribeEdgeTopStatisticalData", ['json' => $data]);
+    }
+
+    public function describeCdnRegionAndIsp(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("DescribeCdnRegionAndIsp", ['json' => $data]);
+    }
+
+    public function describeCdnDomainTopData(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("DescribeCdnDomainTopData", ['json' => $data]);
+    }
+
+    public function describeCdnService(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("DescribeCdnService", ['json' => $data]);
+    }
+
+    public function describeAccountingData(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("DescribeAccountingData", ['json' => $data]);
+    }
+
+    public function submitRefreshTask(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("SubmitRefreshTask", ['json' => $data]);
+    }
+
+    public function submitPreloadTask(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("SubmitPreloadTask", ['json' => $data]);
+    }
+
+    public function describeContentTasks(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("DescribeContentTasks", ['json' => $data]);
+    }
+
+    public function describeContentQuota(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("DescribeContentQuota", ['json' => $data]);
+    }
+
+    public function submitBlockTask(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("SubmitBlockTask", ['json' => $data]);
+    }
+
+    public function submitUnblockTask(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("SubmitUnblockTask", ['json' => $data]);
+    }
+
+    public function describeContentBlockTasks(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("DescribeContentBlockTasks", ['json' => $data]);
+    }
+
+    public function describeCdnAccessLog(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("DescribeCdnAccessLog", ['json' => $data]);
+    }
+
+    public function describeIPInfo(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("DescribeIPInfo", ['json' => $data]);
+    }
+
+    public function describeCdnUpperIp(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("DescribeCdnUpperIp", ['json' => $data]);
+    }
+
+    public function addResourceTags(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("AddResourceTags", ['json' => $data]);
+    }
+
+    public function updateResourceTags(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("UpdateResourceTags", ['json' => $data]);
+    }
+
+    public function listResourceTags(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("ListResourceTags", ['json' => $data]);
+    }
+
+    public function deleteResourceTags(array $data = []): string
+    {
+        if (empty($data)) {
+            $data = new \ArrayObject();
+        }
+        return $this->requestWithRetry("DeleteResourceTags", ['json' => $data]);
+    }
+}
+
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/GameProduct.php b/vendor/volcengine/volc-sdk-php/src/Service/GameProduct.php
new file mode 100755
index 0000000..75b0698
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/GameProduct.php
@@ -0,0 +1,73 @@
+<?php
+
+
+namespace Volc\Service;
+use Volc\Base\V4Curl;
+
+class GameProduct extends V4Curl
+{
+    protected function getConfig(string $region)
+    {
+        switch ($region) {
+            case 'cn-north-1':
+                $config = [
+                    'host' => 'https://open.volcengineapi.com',
+                    'config' => [
+                        'timeout' => 5.0,
+                        'headers' => [
+                            'Accept' => 'application/json'
+                        ],
+                        'v4_credentials' => [
+                            'region' => 'cn-north-1',
+                            'service' => 'game_protect',
+                        ],
+                    ],
+                ];
+                break;
+            default:
+                throw new \Exception(sprintf("AdBlocker not support region, %s", $region));
+        }
+        return $config;
+    }
+
+    protected function requestWithRetry(string $api, array $configs): string
+    {
+        try {
+            $response = $this->request($api, $configs);
+            return (string)$response->getBody();
+        }
+        catch (\Exception $e)
+        {
+            $response = $this->request($api, $configs);
+            return (string)$response->getBody();
+        }
+    }
+
+    protected $apiList = [
+        'RiskResult' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'RiskResult',
+                    'Version' => '2021-04-25',
+                ],
+            ]
+        ],
+    ];
+
+    public function riskResult(int $appId, int $startTime, int $endTime, int $pageSize, int $pageNum): string
+    {
+        $commitBody = array();
+        $commitBody["AppId"] = $appId;
+        $commitBody["Service"] = "anti_plugin";
+        $commitBody["StartTime"] = $startTime;
+        $commitBody["EndTime"] = $endTime;
+        $commitBody["PageSize"] = $pageSize;
+        $commitBody["PageNum"] = $pageNum;
+        $commitReq = [
+            "query" => $commitBody
+        ];
+        return $this->requestWithRetry("RiskResult", $commitReq);
+    }
+}
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Iam.php b/vendor/volcengine/volc-sdk-php/src/Service/Iam.php
new file mode 100755
index 0000000..ff4ef17
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Iam.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace Volc\Service;
+
+use Volc\Base\V4Curl;
+
+class Iam extends V4Curl
+{
+    protected function getConfig(string $region = '')
+    {
+        return [
+            'host' => 'https://iam.volcengineapi.com',
+            'config' => [
+                'timeout' => 5.0,
+                'headers' => [
+                    'Accept' => 'application/json'
+                ],
+                'v4_credentials' => [
+                    'region' => 'cn-north-1',
+                    'service' => 'iam',
+                ],
+            ],
+        ];
+    }
+
+    public function listUsers(array $query = [])
+    {        
+        $response = $this->request('ListUsers', $query);
+        return $response->getBody();
+    }
+
+    protected $apiList = [
+        'CreateUser' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'CreateUser',
+                    'Version' => '2018-01-01',
+                ],
+            ],
+        ],
+        'ListUsers' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListUsers',
+                    'Version' => '2018-01-01',
+                ],
+            ],
+        ],
+    ];
+}
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/ImageX.php b/vendor/volcengine/volc-sdk-php/src/Service/ImageX.php
new file mode 100755
index 0000000..be2b00a
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/ImageX.php
@@ -0,0 +1,433 @@
+<?php
+
+namespace Volc\Service;
+
+use Volc\Base\V4Curl;
+use GuzzleHttp\Client;
+
+const ResourceServiceIdTRN = "trn:ImageX:*:*:ServiceId/%s";
+const ResourceStoreKeyTRN = "trn:ImageX:*:*:StoreKeys/%s";
+
+class ImageX extends V4Curl
+{
+    protected function getConfig(string $region)
+    {
+        switch ($region) {
+            case 'cn-north-1':
+                $config = [
+                    'host' => 'https://imagex.volcengineapi.com',
+                    'config' => [
+                        'timeout' => 5.0,
+                        'headers' => [
+                            'Accept' => 'application/json'
+                        ],
+                        'v4_credentials' => [
+                            'region' => 'cn-north-1',
+                            'service' => 'ImageX',
+                        ],
+                    ],
+                ];
+                break;
+            case 'ap-singapore-1':
+                $config = [
+                    'host' => 'https://imagex-ap-singapore-1.volcengineapi.com',
+                    'config' => [
+                        'timeout' => 5.0,
+                        'headers' => [
+                            'Accept' => 'application/json'
+                        ],
+                        'v4_credentials' => [
+                            'region' => 'ap-singapore-1',
+                            'service' => 'ImageX',
+                        ],
+                    ],
+                ];
+                break;
+            case 'us-east-1':
+                $config = [
+                    'host' => 'https://imagex-us-east-1.volcengineapi.com',
+                    'config' => [
+                        'timeout' => 5.0,
+                        'headers' => [
+                            'Accept' => 'application/json'
+                        ],
+                        'v4_credentials' => [
+                            'region' => 'us-east-1',
+                            'service' => 'ImageX',
+                        ],
+                    ],
+                ];
+                break;
+            default:
+                throw new \Exception(sprintf("ImageX not support region, %s", $region));
+        }
+        return $config;
+    }
+
+    protected $apiList = [
+        // 模板管理
+        'CreateImageTemplate' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'CreateImageTemplate',
+                    'Version' => '2018-08-01',
+                ],
+            ]
+        ],
+        'DeleteImageTemplate' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DeleteImageTemplate',
+                    'Version' => '2018-08-01',
+                ],
+            ]
+        ],
+        'PreviewImageTemplate' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'PreviewImageTemplate',
+                    'Version' => '2018-08-01',
+                ],
+            ]
+        ],
+        'GetImageTemplate' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetImageTemplate',
+                    'Version' => '2018-08-01',
+                ],
+            ]
+        ],
+        'GetAllImageTemplates' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetAllImageTemplates',
+                    'Version' => '2018-08-01',
+                ],
+            ]
+        ],
+        // 资源管理
+        'ApplyImageUpload' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'ApplyImageUpload',
+                    'Version' => '2018-08-01',
+                ],
+            ]
+        ],
+        'CommitImageUpload' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'CommitImageUpload',
+                    'Version' => '2018-08-01',
+                ],
+            ]
+        ],
+        'UpdateImageUploadFiles' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'UpdateImageUploadFiles',
+                    'Version' => '2018-08-01',
+                ],
+            ]
+        ],
+        'DeleteImageUploadFiles' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DeleteImageUploadFiles',
+                    'Version' => '2018-08-01',
+                ],
+            ]
+        ],
+        'GetImageUploadFile' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetImageUploadFile',
+                    'Version' => '2018-08-01',
+                ],
+            ]
+        ],
+        'GetImageUploadFiles' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetImageUploadFiles',
+                    'Version' => '2018-08-01',
+                ],
+            ]
+        ],
+        'PreviewImageUploadFile' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'PreviewImageUploadFile',
+                    'Version' => '2018-08-01',
+                ],
+            ]
+        ],
+        'GetImageUpdateFiles' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetImageUpdateFiles',
+                    'Version' => '2018-08-01',
+                ],
+            ]
+        ],
+        'FetchImageUrl' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'FetchImageUrl',
+                    'Version' => '2018-08-01',
+                ],
+            ]
+        ],
+        'GetImageOCR' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetImageOCR',
+                    'Version'=> '2018-08-01',
+                ]
+            ]
+        ],
+        'GetSegmentImage' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetSegmentImage',
+                    'Version'=> '2018-08-01',
+                ]
+            ]
+        ]
+    ];
+
+    public function applyUploadImage(array $query)
+    {
+        $response = $this->request('ApplyImageUpload', $query);
+        return (string) $response->getBody();
+    }
+
+    public function commitUploadImage(array $query)
+    {
+        $response = $this->request('CommitImageUpload', $query);
+        return (string) $response->getBody();
+    }
+
+    public function updateImageUrls($serviceID, $urls, $action = 0)
+    {
+        if ($action < 0 || $action > 2)
+        {
+            throw new \Exception(sprintf("update action should be [0,2], %d", $action));
+        }
+        $config = [
+            "query" => ["ServiceId" => $serviceID],
+            "json" => [
+                "Action" => $action,
+                "ImageUrls" => $urls,
+            ],
+        ];
+        $response = $this->request('UpdateImageUploadFiles', $config);
+        return (string)$response->getBody();
+    }
+
+    public function upload(string $uploadHost, $storeInfo, string $filePath)
+    {
+        if (!file_exists($filePath)) {
+            return -1;
+        }
+        $content = file_get_contents($filePath);
+        $crc32 = sprintf("%08x", crc32($content));
+
+        $body = fopen($filePath, "r");
+        $tosClient = new Client([
+            'base_uri' => "https://" . $uploadHost,
+            'timeout' => 5.0,
+        ]);
+
+        $response = $tosClient->request('PUT', $storeInfo["StoreUri"], ["body" => $body, "headers" => ['Authorization' => $storeInfo["Auth"], 'Content-CRC32' => $crc32]]);
+        $uploadResponse = json_decode((string) $response->getBody(), true);
+        if (!isset($uploadResponse["success"]) || $uploadResponse["success"] != 0) {
+            return -2;
+        }
+        return 0;
+    }
+
+    public function uploadImages(array $params = [], array $filePaths = [])
+    {
+        if (!isset($params["ServiceId"])) {
+            return "uploadImages: no ServiceId found";
+        }
+        $params["UploadNum"] = count($filePaths);
+
+        $applyParams = array();
+        $applyParams["Action"] = "ApplyImageUpload";
+        $applyParams["Version"] = "2018-08-01";
+        $applyParams["ServiceId"] = $params["ServiceId"];
+        if (isset($params["SessionKey"])) {
+            $applyParams["SessionKey"] = $params["SessionKey"];
+        }
+        $applyParams["UploadNum"] = $params["UploadNum"];
+        if (isset($params["StoreKeys"]) && count($params["StoreKeys"]) != $params["UploadNum"]) {
+            return "uploadImages: no StoreKeys found or StoreKeys size is unmatch";
+        }
+        $applyParams["StoreKeys"] = array();
+        $queryStr = http_build_query($applyParams);
+        if (isset($params["StoreKeys"]) && is_array($params["StoreKeys"])) {
+            foreach ($params["StoreKeys"] as $key => $value) {
+                $queryStr = $queryStr . "&StoreKeys=" . urlencode($value);
+            }
+        }
+
+        $response = $this->applyUploadImage(['query' => $queryStr]);
+        $applyResponse = json_decode($response, true);
+        if (isset($applyResponse["ResponseMetadata"]["Error"])) {
+            return sprintf("uploadImages: request id %s error %s", $applyResponse["ResponseMetadata"]["RequestId"], $applyResponse["ResponseMetadata"]["Error"]["Message"]);
+        }
+
+        $uploadAddr = $applyResponse['Result']['UploadAddress'];
+        if (count($uploadAddr['UploadHosts']) == 0) {
+            return "uploadImages: no upload host found";
+        }
+        $uploadHost = $uploadAddr['UploadHosts'][0];
+        if (count($uploadAddr['StoreInfos']) != $params["UploadNum"]) {
+            return "uploadImages: store infos num != upload num";
+        }
+
+        for ($i = 0; $i < count($filePaths); ++$i) {
+            $respCode = $this->upload($uploadHost, $uploadAddr['StoreInfos'][$i], $filePaths[$i]);
+            if ($respCode != 0) {
+                return "upload " . $filePaths[$i] . " error";
+            }
+        }
+
+        $commitParams = array();
+        $commitParams["ServiceId"] = $params["ServiceId"];
+        $commitBody = array();
+        $commitBody["SessionKey"] = $uploadAddr['SessionKey'];
+        if (isset($params["OptionInfos"])) {
+            $commitBody["OptionInfos"] = $params["OptionInfos"];
+        }
+        if (isset($params["Functions"])) {
+            $commitBody["Functions"] = $params["Functions"];
+        }
+        $commitReq = [
+            "query" => $commitParams,
+            "json" => $commitBody,
+        ];
+
+        $response = $this->commitUploadImage($commitReq);
+        return (string) $response;
+    }
+
+    public function getUploadAuthToken($query)
+    {
+        $token = [
+            "Version" => 'v1',
+        ];
+
+        $url = $this->getRequestUrl("ApplyImageUpload", $query);
+        $m = parse_url($url);
+        $token["ApplyUploadToken"] = $m["query"];
+
+        $url = $this->getRequestUrl("CommitImageUpload", $query);
+        $m = parse_url($url);
+        $token["CommitUploadToken"] = $m["query"];
+
+        return base64_encode(json_encode($token));
+    }
+
+    public function getUploadAuth(array $serviceIDList, int $expire = 3600, string $keyPtn = '')
+    {
+        $applyRes = [];
+        $commitRes = [];
+        if (sizeof($serviceIDList) == 0)
+        {
+            $applyRes[] = sprintf(ResourceServiceIdTRN, "*");
+            $commitRes[] = sprintf(ResourceServiceIdTRN, "*");
+        }
+        else
+        {
+            foreach ($serviceIDList as $serviceID)
+            {
+                $applyRes[] = sprintf(ResourceServiceIdTRN, $serviceID);
+                $commitRes[] = sprintf(ResourceServiceIdTRN, $serviceID);
+            }
+        }
+        $applyRes[] = sprintf(ResourceStoreKeyTRN, $keyPtn);
+
+        $policy = [
+            'Statement' => [
+                $this->newAllowStatement(['ImageX:ApplyImageUpload'], $applyRes),
+                $this->newAllowStatement(['ImageX:CommitImageUpload'], $commitRes),
+            ],
+        ];
+
+        return $this->signSts2($policy, $expire);
+    }
+
+    public function deleteImages(string $serviceID, array $uris = [])
+    {
+        $response = $this->request('DeleteImageUploadFiles', ['query' => ['ServiceId' => $serviceID], 'json' => ['StoreUris' => $uris]]);
+        return (string)$response->getBody();
+    }
+
+    public function requestImageX(string $action, array $config = [])
+    {
+        $response = $this->request($action, $config);
+        return (string)$response->getBody();
+    }
+
+    public function getImageOCR(array $params = [])
+    {
+        $params["Action"] = "GetImageOCR";
+        $params["Version"] = "2018-08-01";
+        $queryStr = http_build_query($params);
+        $response = $this->request('GetImageOCR', ['query' => $queryStr]);
+        $ocrResponse = json_decode((string) $response->getBody(), true);
+        if (isset($ocrResponse["ResponseMetadata"]["Error"])) {
+            return sprintf("getImageOCR: request id %s error %s", $ocrResponse["ResponseMetadata"]["RequestId"], $ocrResponse["ResponseMetadata"]["Error"]["Message"]);
+        }
+        return $ocrResponse["Result"];
+    }
+
+    public function getSegmentImage(array $params = [])
+    {
+        $params["Action"] = "GetSegmentImage";
+        $params["Version"] = "2018-08-01";
+        $queryStr = http_build_query($params);
+        $response = $this->request('GetSegmentImage', ['query' => $queryStr, 'json' => $params]);
+        $segmentResponse = json_decode((string) $response->getBody(), true);
+        if (isset($segmentResponse["ResponseMetadata"]["Error"])) {
+            return sprintf("getSegmentImage: request id %s error %s", $segmentResponse["ResponseMetadata"]["RequestId"], $segmentResponse["ResponseMetadata"]["Error"]["Message"]);
+        }
+        return $segmentResponse["Result"];
+    }
+}
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Imp/Imp.php b/vendor/volcengine/volc-sdk-php/src/Service/Imp/Imp.php
new file mode 100755
index 0000000..fb48925
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Imp/Imp.php
@@ -0,0 +1,146 @@
+<?php
+# Code generated by protoc-gen-volcengine-sdk
+# source: Imp
+# DO NOT EDIT!
+
+namespace Volc\Service\Imp;
+
+use Exception;
+use Throwable;
+use Volc\Base\V4Curl;
+use Volc\Service\Imp\Models\Request\ImpSubmitJobRequest;
+use Volc\Service\Imp\Models\Response\ImpSubmitJobResponse;
+use Volc\Service\Imp\Models\Request\ImpKillJobRequest;
+use Volc\Service\Imp\Models\Response\ImpKillJobResponse;
+use Volc\Service\Imp\Models\Request\ImpRetrieveJobRequest;
+use Volc\Service\Imp\Models\Response\ImpRetrieveJobResponse;
+use Volc\Service\Imp\ImpOption;
+
+/**
+ * Generated from protobuf service <code>imp/service/service_imp.proto</code>
+ */
+class Imp extends V4Curl
+{
+    protected $apiList;
+
+    public function __construct()
+    {
+        $this->region = func_get_arg(0);
+        $this->apiList = ImpOption::$apiList;
+        parent::__construct($this->region);
+    }
+
+    /**
+     * @throws Exception
+     */
+    protected function getConfig(string $region): array
+    {
+        try {
+            return ImpOption::getConfig($region);
+        } catch (Exception $e) {
+            print_r($e->getMessage());
+            print_r($e->getTraceAsString());
+            throw $e;
+        }
+    }
+
+	/**
+     * SubmitJob.
+     *
+     * @param $req ImpSubmitJobRequest
+     * @return ImpSubmitJobResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function SubmitJob (ImpSubmitJobRequest $req): ImpSubmitJobResponse
+	{
+		try {
+			$query = ImpUtils::formatRequestParam($req);
+			$response = $this->request('SubmitJob', ['query' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new ImpSubmitJobResponse();
+		try {
+            $respData = ImpUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * KillJob.
+     *
+     * @param $req ImpKillJobRequest
+     * @return ImpKillJobResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function KillJob (ImpKillJobRequest $req): ImpKillJobResponse
+	{
+		try {
+			$query = ImpUtils::formatRequestParam($req);
+			$response = $this->request('KillJob', ['query' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new ImpKillJobResponse();
+		try {
+            $respData = ImpUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * RetrieveJob.
+     *
+     * @param $req ImpRetrieveJobRequest
+     * @return ImpRetrieveJobResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function RetrieveJob (ImpRetrieveJobRequest $req): ImpRetrieveJobResponse
+	{
+		try {
+			$query = ImpUtils::formatRequestParam($req);
+            $response = $this->request('RetrieveJob', ['query' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new ImpRetrieveJobResponse();
+		try {
+            $respData = ImpUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+}  // end of service
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Imp/ImpOption.php b/vendor/volcengine/volc-sdk-php/src/Service/Imp/ImpOption.php
new file mode 100755
index 0000000..fdaaa02
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Imp/ImpOption.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace Volc\Service\Imp;
+
+use Exception;
+
+class ImpOption
+{
+    public static $apiList = [
+        'SubmitJob' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'SubmitJob',
+                    'Version' => '2021-06-11',
+                ],
+            ]
+        ],
+
+        'KillJob' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'KillJob',
+                    'Version' => '2021-06-11',
+                ],
+            ]
+        ],
+
+        'RetrieveJob' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'RetrieveJob',
+                    'Version' => '2021-06-11',
+                ],
+            ]
+        ]
+    ];
+
+    /**
+     * @throws Exception
+     */
+    public static function getConfig(string $region = '')
+    {
+        switch ($region) {
+            case 'cn-north-1':
+                $config = [
+                    'host' => 'http://open.volcengineapi.com',
+                    'config' => [
+                        'timeout' => 5.0,
+                        'headers' => [
+                            'Accept' => 'application/json',
+                        ],
+                        'v4_credentials' => [
+                            'region' => 'cn-north-1',
+                            'service' => 'imp',
+                        ],
+                    ],
+                ];
+                break;
+            default:
+                throw new Exception("Cant find the region, please check it carefully");
+        }
+        return $config;
+    }
+}
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Imp/ImpUtils.php b/vendor/volcengine/volc-sdk-php/src/Service/Imp/ImpUtils.php
new file mode 100755
index 0000000..5e520e7
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Imp/ImpUtils.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace Volc\Service\Imp;
+
+use Exception;
+use Throwable;
+
+class ImpUtils {
+
+    public static function formatRequestParam($req): array
+    {
+        try {
+            $jsonData = $req->serializeToJsonString();
+            $query = json_decode($jsonData, true);
+            foreach ($query as $key => $value) {
+                switch (gettype($query[$key])) {
+                    case 'integer':
+                    case 'string':
+                    case 'double':
+                    case 'NULL':
+                    case 'boolean':
+                        break;
+                    default:
+                        $d = json_encode($value);
+                        $query[$key] = $d;
+                }
+            }
+        } catch (Exception $e) {
+            echo $e, "\n";
+            throw $e;
+        } catch (Throwable $t) {
+            echo $t, "\n";
+            throw $t;
+        }
+        return $query;
+    }
+
+    public static function parseResponseData($response, $respData)
+    {
+        try {
+            $respData->mergeFromJsonString($response->getBody(), true);
+        } catch (Exception $e) {
+            echo $e, "\n";
+            if ($respData == null || $respData->getResponseMetadata() == null) {
+                throw new Exception($response->getReasonPhrase());
+            }
+        } catch (Throwable $t) {
+            echo $t, "\n";
+            if ($respData == null || $respData->getResponseMetadata() == null) {
+                throw new Exception($response->getReasonPhrase());
+            }
+        }
+        return $respData;
+    }
+
+}
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Business/InputPath.php b/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Business/InputPath.php
new file mode 100755
index 0000000..68c08ff
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Business/InputPath.php
@@ -0,0 +1,146 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: imp/business/imp_common.proto
+
+namespace Volc\Service\Imp\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Imp.Models.Business.InputPath</code>
+ */
+class InputPath extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 可选:VOD TOS
+     *
+     * Generated from protobuf field <code>string Type = 1;</code>
+     */
+    protected $Type = '';
+    /**
+     * Generated from protobuf field <code>string TosBucket = 2;</code>
+     */
+    protected $TosBucket = '';
+    /**
+     * Generated from protobuf field <code>string VodSpaceName = 3;</code>
+     */
+    protected $VodSpaceName = '';
+    /**
+     * Generated from protobuf field <code>string FileId = 4;</code>
+     */
+    protected $FileId = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Type
+     *           可选:VOD TOS
+     *     @type string $TosBucket
+     *     @type string $VodSpaceName
+     *     @type string $FileId
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Imp\Models\GPBMetadata\ImpCommon::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 可选:VOD TOS
+     *
+     * Generated from protobuf field <code>string Type = 1;</code>
+     * @return string
+     */
+    public function getType()
+    {
+        return $this->Type;
+    }
+
+    /**
+     * 可选:VOD TOS
+     *
+     * Generated from protobuf field <code>string Type = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Type = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string TosBucket = 2;</code>
+     * @return string
+     */
+    public function getTosBucket()
+    {
+        return $this->TosBucket;
+    }
+
+    /**
+     * Generated from protobuf field <code>string TosBucket = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTosBucket($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TosBucket = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string VodSpaceName = 3;</code>
+     * @return string
+     */
+    public function getVodSpaceName()
+    {
+        return $this->VodSpaceName;
+    }
+
+    /**
+     * Generated from protobuf field <code>string VodSpaceName = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVodSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->VodSpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string FileId = 4;</code>
+     * @return string
+     */
+    public function getFileId()
+    {
+        return $this->FileId;
+    }
+
+    /**
+     * Generated from protobuf field <code>string FileId = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileId = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Business/JobExecution.php b/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Business/JobExecution.php
new file mode 100755
index 0000000..b95206d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Business/JobExecution.php
@@ -0,0 +1,323 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: imp/business/imp_common.proto
+
+namespace Volc\Service\Imp\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Imp.Models.Business.JobExecution</code>
+ */
+class JobExecution extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 工作流执行id
+     *
+     * Generated from protobuf field <code>string JobId = 1;</code>
+     */
+    protected $JobId = '';
+    /**
+     * 输入文件info
+     *
+     * Generated from protobuf field <code>.Volcengine.Imp.Models.Business.InputPath InputPath = 2;</code>
+     */
+    protected $InputPath = null;
+    /**
+     * 任务节点info
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Imp.Models.Business.JobOutput Output = 3;</code>
+     */
+    private $Output;
+    /**
+     * 工作流状态
+     *
+     * Generated from protobuf field <code>string Status = 4;</code>
+     */
+    protected $Status = '';
+    /**
+     * Generated from protobuf field <code>string CreateAt = 5;</code>
+     */
+    protected $CreateAt = '';
+    /**
+     * Generated from protobuf field <code>string FinishedAt = 6;</code>
+     */
+    protected $FinishedAt = '';
+    /**
+     * 工作流模板id
+     *
+     * Generated from protobuf field <code>string TemplateId = 7;</code>
+     */
+    protected $TemplateId = '';
+    /**
+     * 是否开启闲时任务
+     *
+     * Generated from protobuf field <code>string EnableLowPriority = 8;</code>
+     */
+    protected $EnableLowPriority = '';
+    /**
+     * 任务来源
+     *
+     * Generated from protobuf field <code>string JobSource = 9;</code>
+     */
+    protected $JobSource = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $JobId
+     *           工作流执行id
+     *     @type \Volc\Service\Imp\Models\Business\InputPath $InputPath
+     *           输入文件info
+     *     @type \Volc\Service\Imp\Models\Business\JobOutput[]|\Google\Protobuf\Internal\RepeatedField $Output
+     *           任务节点info
+     *     @type string $Status
+     *           工作流状态
+     *     @type string $CreateAt
+     *     @type string $FinishedAt
+     *     @type string $TemplateId
+     *           工作流模板id
+     *     @type string $EnableLowPriority
+     *           是否开启闲时任务
+     *     @type string $JobSource
+     *           任务来源
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Imp\Models\GPBMetadata\ImpCommon::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 工作流执行id
+     *
+     * Generated from protobuf field <code>string JobId = 1;</code>
+     * @return string
+     */
+    public function getJobId()
+    {
+        return $this->JobId;
+    }
+
+    /**
+     * 工作流执行id
+     *
+     * Generated from protobuf field <code>string JobId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setJobId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->JobId = $var;
+
+        return $this;
+    }
+
+    /**
+     * 输入文件info
+     *
+     * Generated from protobuf field <code>.Volcengine.Imp.Models.Business.InputPath InputPath = 2;</code>
+     * @return \Volc\Service\Imp\Models\Business\InputPath
+     */
+    public function getInputPath()
+    {
+        return $this->InputPath;
+    }
+
+    /**
+     * 输入文件info
+     *
+     * Generated from protobuf field <code>.Volcengine.Imp.Models.Business.InputPath InputPath = 2;</code>
+     * @param \Volc\Service\Imp\Models\Business\InputPath $var
+     * @return $this
+     */
+    public function setInputPath($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Imp\Models\Business\InputPath::class);
+        $this->InputPath = $var;
+
+        return $this;
+    }
+
+    /**
+     * 任务节点info
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Imp.Models.Business.JobOutput Output = 3;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getOutput()
+    {
+        return $this->Output;
+    }
+
+    /**
+     * 任务节点info
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Imp.Models.Business.JobOutput Output = 3;</code>
+     * @param \Volc\Service\Imp\Models\Business\JobOutput[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setOutput($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Imp\Models\Business\JobOutput::class);
+        $this->Output = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 工作流状态
+     *
+     * Generated from protobuf field <code>string Status = 4;</code>
+     * @return string
+     */
+    public function getStatus()
+    {
+        return $this->Status;
+    }
+
+    /**
+     * 工作流状态
+     *
+     * Generated from protobuf field <code>string Status = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStatus($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Status = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string CreateAt = 5;</code>
+     * @return string
+     */
+    public function getCreateAt()
+    {
+        return $this->CreateAt;
+    }
+
+    /**
+     * Generated from protobuf field <code>string CreateAt = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCreateAt($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CreateAt = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string FinishedAt = 6;</code>
+     * @return string
+     */
+    public function getFinishedAt()
+    {
+        return $this->FinishedAt;
+    }
+
+    /**
+     * Generated from protobuf field <code>string FinishedAt = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFinishedAt($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FinishedAt = $var;
+
+        return $this;
+    }
+
+    /**
+     * 工作流模板id
+     *
+     * Generated from protobuf field <code>string TemplateId = 7;</code>
+     * @return string
+     */
+    public function getTemplateId()
+    {
+        return $this->TemplateId;
+    }
+
+    /**
+     * 工作流模板id
+     *
+     * Generated from protobuf field <code>string TemplateId = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTemplateId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TemplateId = $var;
+
+        return $this;
+    }
+
+    /**
+     * 是否开启闲时任务
+     *
+     * Generated from protobuf field <code>string EnableLowPriority = 8;</code>
+     * @return string
+     */
+    public function getEnableLowPriority()
+    {
+        return $this->EnableLowPriority;
+    }
+
+    /**
+     * 是否开启闲时任务
+     *
+     * Generated from protobuf field <code>string EnableLowPriority = 8;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setEnableLowPriority($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->EnableLowPriority = $var;
+
+        return $this;
+    }
+
+    /**
+     * 任务来源
+     *
+     * Generated from protobuf field <code>string JobSource = 9;</code>
+     * @return string
+     */
+    public function getJobSource()
+    {
+        return $this->JobSource;
+    }
+
+    /**
+     * 任务来源
+     *
+     * Generated from protobuf field <code>string JobSource = 9;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setJobSource($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->JobSource = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Business/JobOutput.php b/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Business/JobOutput.php
new file mode 100755
index 0000000..5566698
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Business/JobOutput.php
@@ -0,0 +1,329 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: imp/business/imp_common.proto
+
+namespace Volc\Service\Imp\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Imp.Models.Business.JobOutput</code>
+ */
+class JobOutput extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 任务模板id
+     *
+     * Generated from protobuf field <code>string TemplateId = 1;</code>
+     */
+    protected $TemplateId = '';
+    /**
+     * 任务输出info
+     *
+     * Generated from protobuf field <code>string Properties = 2;</code>
+     */
+    protected $Properties = '';
+    /**
+     * Generated from protobuf field <code>string Code = 3;</code>
+     */
+    protected $Code = '';
+    /**
+     * Generated from protobuf field <code>string FileMessageId = 4;</code>
+     */
+    protected $FileMessageId = '';
+    /**
+     * 任务类型
+     *
+     * Generated from protobuf field <code>string TaskType = 5;</code>
+     */
+    protected $TaskType = '';
+    /**
+     * 任务状态
+     *
+     * Generated from protobuf field <code>string Status = 6;</code>
+     */
+    protected $Status = '';
+    /**
+     * Generated from protobuf field <code>string ActivityId = 7;</code>
+     */
+    protected $ActivityId = '';
+    /**
+     * Generated from protobuf field <code>string StartTime = 8;</code>
+     */
+    protected $StartTime = '';
+    /**
+     * Generated from protobuf field <code>string EndTime = 9;</code>
+     */
+    protected $EndTime = '';
+    /**
+     * Generated from protobuf field <code>string TemplateName = 10;</code>
+     */
+    protected $TemplateName = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $TemplateId
+     *           任务模板id
+     *     @type string $Properties
+     *           任务输出info
+     *     @type string $Code
+     *     @type string $FileMessageId
+     *     @type string $TaskType
+     *           任务类型
+     *     @type string $Status
+     *           任务状态
+     *     @type string $ActivityId
+     *     @type string $StartTime
+     *     @type string $EndTime
+     *     @type string $TemplateName
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Imp\Models\GPBMetadata\ImpCommon::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 任务模板id
+     *
+     * Generated from protobuf field <code>string TemplateId = 1;</code>
+     * @return string
+     */
+    public function getTemplateId()
+    {
+        return $this->TemplateId;
+    }
+
+    /**
+     * 任务模板id
+     *
+     * Generated from protobuf field <code>string TemplateId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTemplateId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TemplateId = $var;
+
+        return $this;
+    }
+
+    /**
+     * 任务输出info
+     *
+     * Generated from protobuf field <code>string Properties = 2;</code>
+     * @return string
+     */
+    public function getProperties()
+    {
+        return $this->Properties;
+    }
+
+    /**
+     * 任务输出info
+     *
+     * Generated from protobuf field <code>string Properties = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setProperties($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Properties = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string Code = 3;</code>
+     * @return string
+     */
+    public function getCode()
+    {
+        return $this->Code;
+    }
+
+    /**
+     * Generated from protobuf field <code>string Code = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCode($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Code = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string FileMessageId = 4;</code>
+     * @return string
+     */
+    public function getFileMessageId()
+    {
+        return $this->FileMessageId;
+    }
+
+    /**
+     * Generated from protobuf field <code>string FileMessageId = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileMessageId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileMessageId = $var;
+
+        return $this;
+    }
+
+    /**
+     * 任务类型
+     *
+     * Generated from protobuf field <code>string TaskType = 5;</code>
+     * @return string
+     */
+    public function getTaskType()
+    {
+        return $this->TaskType;
+    }
+
+    /**
+     * 任务类型
+     *
+     * Generated from protobuf field <code>string TaskType = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTaskType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TaskType = $var;
+
+        return $this;
+    }
+
+    /**
+     * 任务状态
+     *
+     * Generated from protobuf field <code>string Status = 6;</code>
+     * @return string
+     */
+    public function getStatus()
+    {
+        return $this->Status;
+    }
+
+    /**
+     * 任务状态
+     *
+     * Generated from protobuf field <code>string Status = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStatus($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Status = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string ActivityId = 7;</code>
+     * @return string
+     */
+    public function getActivityId()
+    {
+        return $this->ActivityId;
+    }
+
+    /**
+     * Generated from protobuf field <code>string ActivityId = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setActivityId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->ActivityId = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string StartTime = 8;</code>
+     * @return string
+     */
+    public function getStartTime()
+    {
+        return $this->StartTime;
+    }
+
+    /**
+     * Generated from protobuf field <code>string StartTime = 8;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStartTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StartTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string EndTime = 9;</code>
+     * @return string
+     */
+    public function getEndTime()
+    {
+        return $this->EndTime;
+    }
+
+    /**
+     * Generated from protobuf field <code>string EndTime = 9;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setEndTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->EndTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string TemplateName = 10;</code>
+     * @return string
+     */
+    public function getTemplateName()
+    {
+        return $this->TemplateName;
+    }
+
+    /**
+     * Generated from protobuf field <code>string TemplateName = 10;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTemplateName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TemplateName = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/GPBMetadata/ImpCommon.php b/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/GPBMetadata/ImpCommon.php
new file mode 100755
index 0000000..58b3653
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/GPBMetadata/ImpCommon.php
@@ -0,0 +1,24 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: imp/business/imp_common.proto
+
+namespace Volc\Service\Imp\Models\GPBMetadata;
+
+class ImpCommon
+{
+    public static $is_initialized = false;
+
+    public static function initOnce() {
+        $pool = \Google\Protobuf\Internal\DescriptorPool::getGeneratedPool();
+
+        if (static::$is_initialized == true) {
+          return;
+        }
+        $pool->internalAddGeneratedFile(hex2bin(
+            "0ac6060a1d696d702f627573696e6573732f696d705f636f6d6d6f6e2e70726f746f121e566f6c63656e67696e652e496d702e4d6f64656c732e427573696e65737322520a09496e70757450617468120c0a045479706518012001280912110a09546f734275636b657418022001280912140a0c566f6453706163654e616d65180320012809120e0a0646696c65496418042001280922c8010a094a6f624f757470757412120a0a54656d706c617465496418012001280912120a0a50726f70657274696573180220012809120c0a04436f646518032001280912150a0d46696c654d657373616765496418042001280912100a085461736b54797065180520012809120e0a0653746174757318062001280912120a0a4163746976697479496418072001280912110a09537461727454696d65180820012809120f0a07456e6454696d6518092001280912140a0c54656d706c6174654e616d65180a20012809228e020a0c4a6f62457865637574696f6e120d0a054a6f624964180120012809123c0a09496e7075745061746818022001280b32292e566f6c63656e67696e652e496d702e4d6f64656c732e427573696e6573732e496e7075745061746812390a064f757470757418032003280b32292e566f6c63656e67696e652e496d702e4d6f64656c732e427573696e6573732e4a6f624f7574707574120e0a0653746174757318042001280912100a08437265617465417418052001280912120a0a46696e6973686564417418062001280912120a0a54656d706c617465496418072001280912190a11456e61626c654c6f775072696f7269747918082001280912110a094a6f62536f7572636518092001280942cc010a29636f6d2e766f6c63656e67696e652e736572766963652e696d702e6d6f64656c2e627573696e657373420b496d70576f726b666c6f7750015a416769746875622e636f6d2f766f6c63656e67696e652f766f6c632d73646b2d676f6c616e672f736572766963652f696d702f6d6f64656c732f627573696e657373a00101d80101ca0220566f6c635c536572766963655c496d705c4d6f64656c735c427573696e657373e20223566f6c635c536572766963655c496d705c4d6f64656c735c4750424d65746164617461620670726f746f33"
+        ), true);
+
+        static::$is_initialized = true;
+    }
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/GPBMetadata/RequestImp.php b/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/GPBMetadata/RequestImp.php
new file mode 100755
index 0000000..0e81c4d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/GPBMetadata/RequestImp.php
@@ -0,0 +1,25 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: imp/request/request_imp.proto
+
+namespace Volc\Service\Imp\Models\GPBMetadata;
+
+class RequestImp
+{
+    public static $is_initialized = false;
+
+    public static function initOnce() {
+        $pool = \Google\Protobuf\Internal\DescriptorPool::getGeneratedPool();
+
+        if (static::$is_initialized == true) {
+          return;
+        }
+        \Volc\Service\Imp\Models\GPBMetadata\ImpCommon::initOnce();
+        $pool->internalAddGeneratedFile(hex2bin(
+            "0af9030a1d696d702f726571756573742f726571756573745f696d702e70726f746f121d566f6c63656e67696e652e496d702e4d6f64656c732e526571756573742298010a13496d705375626d69744a6f6252657175657374123c0a09496e7075745061746818012001280b32292e566f6c63656e67696e652e496d702e4d6f64656c732e427573696e6573732e496e7075745061746812120a0a54656d706c617465496418022001280912140a0c43616c6c6261636b4172677318032001280912190a11456e61626c654c6f775072696f7269747918042001280922220a11496d704b696c6c4a6f6252657175657374120d0a054a6f62496418012001280922270a15496d7052657472696576654a6f6252657175657374120e0a064a6f6249647318012003280942c8010a28636f6d2e766f6c63656e67696e652e736572766963652e696d702e6d6f64656c2e72657175657374420a496d705265717565737450015a406769746875622e636f6d2f766f6c63656e67696e652f766f6c632d73646b2d676f6c616e672f736572766963652f696d702f6d6f64656c732f72657175657374a00101d80101ca021f566f6c635c536572766963655c496d705c4d6f64656c735c52657175657374e20223566f6c635c536572766963655c496d705c4d6f64656c735c4750424d65746164617461620670726f746f33"
+        ), true);
+
+        static::$is_initialized = true;
+    }
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/GPBMetadata/ResponseImp.php b/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/GPBMetadata/ResponseImp.php
new file mode 100755
index 0000000..ae81136
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/GPBMetadata/ResponseImp.php
@@ -0,0 +1,26 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: imp/response/response_imp.proto
+
+namespace Volc\Service\Imp\Models\GPBMetadata;
+
+class ResponseImp
+{
+    public static $is_initialized = false;
+
+    public static function initOnce() {
+        $pool = \Google\Protobuf\Internal\DescriptorPool::getGeneratedPool();
+
+        if (static::$is_initialized == true) {
+          return;
+        }
+        \Volc\Service\Base\Models\GPBMetadata\Base::initOnce();
+        \Volc\Service\Imp\Models\GPBMetadata\ImpCommon::initOnce();
+        $pool->internalAddGeneratedFile(hex2bin(
+            "0a9c060a1f696d702f726573706f6e73652f726573706f6e73655f696d702e70726f746f121e566f6c63656e67696e652e496d702e4d6f64656c732e526573706f6e73651a1d696d702f627573696e6573732f696d705f636f6d6d6f6e2e70726f746f226f0a14496d705375626d69744a6f62526573706f6e736512470a10526573706f6e73654d6574616461746118012001280b322d2e566f6c63656e67696e652e426173652e4d6f64656c732e426173652e526573706f6e73654d65746164617461120e0a06526573756c74180220012809225d0a12496d704b696c6c4a6f62526573706f6e736512470a10526573706f6e73654d6574616461746118012001280b322d2e566f6c63656e67696e652e426173652e4d6f64656c732e426173652e526573706f6e73654d657461646174612292020a16496d7052657472696576654a6f62526573706f6e736512470a10526573706f6e73654d6574616461746118012001280b322d2e566f6c63656e67696e652e426173652e4d6f64656c732e426173652e526573706f6e73654d6574616461746112520a06526573756c7418022003280b32422e566f6c63656e67696e652e496d702e4d6f64656c732e526573706f6e73652e496d7052657472696576654a6f62526573706f6e73652e526573756c74456e7472791a5b0a0b526573756c74456e747279120b0a036b6579180120012809123b0a0576616c756518022001280b322c2e566f6c63656e67696e652e496d702e4d6f64656c732e427573696e6573732e4a6f62457865637574696f6e3a02380142cc010a29636f6d2e766f6c63656e67696e652e736572766963652e696d702e6d6f64656c2e726573706f6e7365420b496d70526573706f6e736550015a416769746875622e636f6d2f766f6c63656e67696e652f766f6c632d73646b2d676f6c616e672f736572766963652f696d702f6d6f64656c732f726573706f6e7365a00101d80101ca0220566f6c635c536572766963655c496d705c4d6f64656c735c526573706f6e7365e20223566f6c635c536572766963655c496d705c4d6f64656c735c4750424d65746164617461620670726f746f33"
+        ), true);
+
+        static::$is_initialized = true;
+    }
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Request/ImpKillJobRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Request/ImpKillJobRequest.php
new file mode 100755
index 0000000..d128a51
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Request/ImpKillJobRequest.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: imp/request/request_imp.proto
+
+namespace Volc\Service\Imp\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Imp.Models.Request.ImpKillJobRequest</code>
+ */
+class ImpKillJobRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 工作流执行id
+     *
+     * Generated from protobuf field <code>string JobId = 1;</code>
+     */
+    protected $JobId = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $JobId
+     *           工作流执行id
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Imp\Models\GPBMetadata\RequestImp::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 工作流执行id
+     *
+     * Generated from protobuf field <code>string JobId = 1;</code>
+     * @return string
+     */
+    public function getJobId()
+    {
+        return $this->JobId;
+    }
+
+    /**
+     * 工作流执行id
+     *
+     * Generated from protobuf field <code>string JobId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setJobId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->JobId = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Request/ImpRetrieveJobRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Request/ImpRetrieveJobRequest.php
new file mode 100755
index 0000000..c242aae
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Request/ImpRetrieveJobRequest.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: imp/request/request_imp.proto
+
+namespace Volc\Service\Imp\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Imp.Models.Request.ImpRetrieveJobRequest</code>
+ */
+class ImpRetrieveJobRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 想获取的工作流执行记录列表
+     *
+     * Generated from protobuf field <code>repeated string JobIds = 1;</code>
+     */
+    private $JobIds;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $JobIds
+     *           想获取的工作流执行记录列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Imp\Models\GPBMetadata\RequestImp::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 想获取的工作流执行记录列表
+     *
+     * Generated from protobuf field <code>repeated string JobIds = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getJobIds()
+    {
+        return $this->JobIds;
+    }
+
+    /**
+     * 想获取的工作流执行记录列表
+     *
+     * Generated from protobuf field <code>repeated string JobIds = 1;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setJobIds($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->JobIds = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Request/ImpSubmitJobRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Request/ImpSubmitJobRequest.php
new file mode 100755
index 0000000..7ec5089
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Request/ImpSubmitJobRequest.php
@@ -0,0 +1,167 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: imp/request/request_imp.proto
+
+namespace Volc\Service\Imp\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Imp.Models.Request.ImpSubmitJobRequest</code>
+ */
+class ImpSubmitJobRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 输入文件info
+     *
+     * Generated from protobuf field <code>.Volcengine.Imp.Models.Business.InputPath InputPath = 1;</code>
+     */
+    protected $InputPath = null;
+    /**
+     * 工作流模板id
+     *
+     * Generated from protobuf field <code>string TemplateId = 2;</code>
+     */
+    protected $TemplateId = '';
+    /**
+     * 回调参数
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 3;</code>
+     */
+    protected $CallbackArgs = '';
+    /**
+     * 是否开启闲时任务, "true"/"false"
+     *
+     * Generated from protobuf field <code>string EnableLowPriority = 4;</code>
+     */
+    protected $EnableLowPriority = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Imp\Models\Business\InputPath $InputPath
+     *           输入文件info
+     *     @type string $TemplateId
+     *           工作流模板id
+     *     @type string $CallbackArgs
+     *           回调参数
+     *     @type string $EnableLowPriority
+     *           是否开启闲时任务, "true"/"false"
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Imp\Models\GPBMetadata\RequestImp::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 输入文件info
+     *
+     * Generated from protobuf field <code>.Volcengine.Imp.Models.Business.InputPath InputPath = 1;</code>
+     * @return \Volc\Service\Imp\Models\Business\InputPath
+     */
+    public function getInputPath()
+    {
+        return $this->InputPath;
+    }
+
+    /**
+     * 输入文件info
+     *
+     * Generated from protobuf field <code>.Volcengine.Imp.Models.Business.InputPath InputPath = 1;</code>
+     * @param \Volc\Service\Imp\Models\Business\InputPath $var
+     * @return $this
+     */
+    public function setInputPath($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Imp\Models\Business\InputPath::class);
+        $this->InputPath = $var;
+
+        return $this;
+    }
+
+    /**
+     * 工作流模板id
+     *
+     * Generated from protobuf field <code>string TemplateId = 2;</code>
+     * @return string
+     */
+    public function getTemplateId()
+    {
+        return $this->TemplateId;
+    }
+
+    /**
+     * 工作流模板id
+     *
+     * Generated from protobuf field <code>string TemplateId = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTemplateId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TemplateId = $var;
+
+        return $this;
+    }
+
+    /**
+     * 回调参数
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 3;</code>
+     * @return string
+     */
+    public function getCallbackArgs()
+    {
+        return $this->CallbackArgs;
+    }
+
+    /**
+     * 回调参数
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCallbackArgs($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CallbackArgs = $var;
+
+        return $this;
+    }
+
+    /**
+     * 是否开启闲时任务, "true"/"false"
+     *
+     * Generated from protobuf field <code>string EnableLowPriority = 4;</code>
+     * @return string
+     */
+    public function getEnableLowPriority()
+    {
+        return $this->EnableLowPriority;
+    }
+
+    /**
+     * 是否开启闲时任务, "true"/"false"
+     *
+     * Generated from protobuf field <code>string EnableLowPriority = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setEnableLowPriority($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->EnableLowPriority = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Response/ImpKillJobResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Response/ImpKillJobResponse.php
new file mode 100755
index 0000000..87e6171
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Response/ImpKillJobResponse.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: imp/response/response_imp.proto
+
+namespace Volc\Service\Imp\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Imp.Models.Response.ImpKillJobResponse</code>
+ */
+class ImpKillJobResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Imp\Models\GPBMetadata\ResponseImp::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Response/ImpRetrieveJobResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Response/ImpRetrieveJobResponse.php
new file mode 100755
index 0000000..fcaa30d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Response/ImpRetrieveJobResponse.php
@@ -0,0 +1,92 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: imp/response/response_imp.proto
+
+namespace Volc\Service\Imp\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Imp.Models.Response.ImpRetrieveJobResponse</code>
+ */
+class ImpRetrieveJobResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     * Generated from protobuf field <code>map<string, .Volcengine.Imp.Models.Business.JobExecution> Result = 2;</code>
+     */
+    private $Result;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type array|\Google\Protobuf\Internal\MapField $Result
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Imp\Models\GPBMetadata\ResponseImp::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>map<string, .Volcengine.Imp.Models.Business.JobExecution> Result = 2;</code>
+     * @return \Google\Protobuf\Internal\MapField
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    /**
+     * Generated from protobuf field <code>map<string, .Volcengine.Imp.Models.Business.JobExecution> Result = 2;</code>
+     * @param array|\Google\Protobuf\Internal\MapField $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        $arr = GPBUtil::checkMapField($var, \Google\Protobuf\Internal\GPBType::STRING, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Imp\Models\Business\JobExecution::class);
+        $this->Result = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Response/ImpSubmitJobResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Response/ImpSubmitJobResponse.php
new file mode 100755
index 0000000..1725b54
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Imp/Models/Response/ImpSubmitJobResponse.php
@@ -0,0 +1,92 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: imp/response/response_imp.proto
+
+namespace Volc\Service\Imp\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Imp.Models.Response.ImpSubmitJobResponse</code>
+ */
+class ImpSubmitJobResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     * Generated from protobuf field <code>string Result = 2;</code>
+     */
+    protected $Result = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type string $Result
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Imp\Models\GPBMetadata\ResponseImp::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string Result = 2;</code>
+     * @return string
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    /**
+     * Generated from protobuf field <code>string Result = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live.php b/vendor/volcengine/volc-sdk-php/src/Service/Live.php
new file mode 100755
index 0000000..f0d2a8b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live.php
@@ -0,0 +1,871 @@
+<?php
+
+namespace Volc\Service;
+
+use Volc\Base\V4Curl;
+
+class Live extends V4Curl
+{
+    protected $apiList = [
+        'ListCommonTransPresetDetail' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListCommonTransPresetDetail',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'UpdateCallback' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'UpdateCallback',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribeCallback' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeCallback',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DeleteCallback' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DeleteCallback',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'CreateDomain' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'CreateDomain',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DeleteDomain' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DeleteDomain',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'ListDomainDetail' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListDomainDetail',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribeDomain' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeDomain',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'EnableDomain' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'EnableDomain',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DisableDomain' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DisableDomain',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'ManagerPullPushDomainBind' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'ManagerPullPushDomainBind',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'UpdateAuthKey' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'UpdateAuthKey',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribeAuth' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeAuth',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'ForbidStream' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'ForbidStream',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'ResumeStream' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'ResumeStream',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'ListCert' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListCert',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'CreateCert' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'CreateCert',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'UpdateCert' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'UpdateCert',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'BindCert' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'BindCert',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'UnbindCert' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'UnbindCert',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DeleteCert' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DeleteCert',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'UpdateReferer' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'UpdateReferer',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DeleteReferer' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DeleteReferer',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribeReferer' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeReferer',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'CreateRecordPreset' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'CreateRecordPreset',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'UpdateRecordPreset' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'UpdateRecordPreset',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DeleteRecordPreset' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DeleteRecordPreset',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'ListVhostRecordPreset' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListVhostRecordPreset',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'CreateTranscodePreset' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'CreateTranscodePreset',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'UpdateTranscodePreset' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'UpdateTranscodePreset',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DeleteTranscodePreset' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DeleteTranscodePreset',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'ListVhostTransCodePreset' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListVhostTransCodePreset',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'CreateSnapshotPreset' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'CreateSnapshotPreset',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'UpdateSnapshotPreset' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'UpdateSnapshotPreset',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DeleteSnapshotPreset' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DeleteSnapshotPreset',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'ListVhostSnapshotPreset' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListVhostSnapshotPreset',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribeLiveBandwidthData' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeLiveBandwidthData',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribeLiveTrafficData' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeLiveTrafficData',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribeLiveP95PeakBandwidthData' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeLiveP95PeakBandwidthData',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribeRecordData' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeRecordData',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribeTranscodeData' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeTranscodeData',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribeSnapshotData' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeSnapshotData',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribeLiveDomainLog' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeLiveDomainLog',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribePushStreamMetrics' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribePushStreamMetrics',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribeLiveStreamSessions' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeLiveStreamSessions',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribePlayResponseStatusStat' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribePlayResponseStatusStat',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribeLiveMetricTrafficData' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeLiveMetricTrafficData',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribeLiveMetricBandwidthData' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeLiveMetricBandwidthData',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribePlayStreamList' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribePlayStreamList',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribePullToPushBandwidthData' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribePullToPushBandwidthData',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'CreateSnapshotAuditPreset' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'CreateSnapshotAuditPreset',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'UpdateSnapshotAuditPreset' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'UpdateSnapshotAuditPreset',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DeleteSnapshotAuditPreset' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DeleteSnapshotAuditPreset',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'ListVhostSnapshotAuditPreset' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListVhostSnapshotAuditPreset',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribeLiveAuditData' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeLiveAuditData',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+    ];
+
+    public function __construct()
+    {
+        $this->region = func_get_arg(0);
+        parent::__construct($this->region);
+    }
+
+    protected function getConfig(string $region = 'cn-north-1'): array
+    {
+        return [
+            'host' => "https://live.volcengineapi.com",
+            'config' => [
+                'timeout' => 30.0,
+                'headers' => [
+                    'Accept' => 'application/json'
+                ],
+                'v4_credentials' => [
+                    'region' => $region,
+                    'service' => 'live',
+                ],
+            ],
+        ];
+    }
+
+    public function requestWithRetry(string $api, array $configs): string
+    {
+        try {
+            $response = $this->request($api, $configs);
+            return (string)$response->getBody();
+        } catch (\Exception $e) {
+            $response = $this->request($api, $configs);
+            return (string)$response->getBody();
+        }
+    }
+
+    public function listCommonTransPresetDetail(array $query = []): string
+    {
+        return $this->requestWithRetry("ListCommonTransPresetDetail", $query);
+    }
+
+    public function updateCallback(array $query = []): string
+    {
+        return $this->requestWithRetry("UpdateCallback", $query);
+    }
+
+    public function describeCallback(array $query = []): string
+    {
+        return $this->requestWithRetry("DescribeCallback", $query);
+    }
+
+    public function deleteCallback(array $query = []): string
+    {
+        return $this->requestWithRetry("DeleteCallback", $query);
+    }
+
+    public function createDomain(array $query = []): string
+    {
+        return $this->requestWithRetry("CreateDomain", $query);
+    }
+
+    public function deleteDomain(array $query = []): string
+    {
+        return $this->requestWithRetry("DeleteDomain", $query);
+    }
+
+    public function listDomainDetail(array $query = []): string
+    {
+        return $this->requestWithRetry("ListDomainDetail", $query);
+    }
+
+    public function describeDomain(array $query = []): string
+    {
+        return $this->requestWithRetry("DescribeDomain", $query);
+    }
+
+    public function enableDomain(array $query = []): string
+    {
+        return $this->requestWithRetry("EnableDomain", $query);
+    }
+
+    public function disableDomain(array $query = []): string
+    {
+        return $this->requestWithRetry("DisableDomain", $query);
+    }
+
+    public function managerPullPushDomainBind(array $query = []): string
+    {
+        return $this->requestWithRetry("ManagerPullPushDomainBind", $query);
+    }
+
+    public function updateAuthKey(array $query = []): string
+    {
+        return $this->requestWithRetry("UpdateAuthKey", $query);
+    }
+
+    public function describeAuth(array $query = []): string
+    {
+        return $this->requestWithRetry("DescribeAuth", $query);
+    }
+
+    public function forbidStream(array $query = []): string
+    {
+        return $this->requestWithRetry("ForbidStream", $query);
+    }
+
+    public function resumeStream(array $query = []): string
+    {
+        return $this->requestWithRetry("ResumeStream", $query);
+    }
+
+    public function listCert(array $query = []): string
+    {
+        return $this->requestWithRetry("ListCert", $query);
+    }
+
+    public function createCert(array $query = []): string
+    {
+        return $this->requestWithRetry("CreateCert", $query);
+    }
+
+
+    public function updateCert(array $query = []): string
+    {
+        return $this->requestWithRetry("UpdateCert", $query);
+    }
+
+    public function bindCert(array $query = []): string
+    {
+        return $this->requestWithRetry("BindCert", $query);
+    }
+
+    public function unbindCert(array $query = []): string
+    {
+        return $this->requestWithRetry("UnbindCert", $query);
+    }
+
+    public function deleteCert(array $query = []): string
+    {
+        return $this->requestWithRetry("DeleteCert", $query);
+    }
+
+    public function updateReferer(array $query = []): string
+    {
+        return $this->requestWithRetry("UpdateReferer", $query);
+    }
+
+    public function deleteReferer(array $query = []): string
+    {
+        return $this->requestWithRetry("DeleteReferer", $query);
+    }
+
+    public function describeReferer(array $query = []): string
+    {
+        return $this->requestWithRetry("DescribeReferer", $query);
+    }
+
+    public function createRecordPreset(array $query = []): string
+    {
+        return $this->requestWithRetry("CreateRecordPreset", $query);
+    }
+
+    public function updateRecordPreset(array $query = []): string
+    {
+        return $this->requestWithRetry("UpdateRecordPreset", $query);
+    }
+
+    public function deleteRecordPreset(array $query = []): string
+    {
+        return $this->requestWithRetry("DeleteRecordPreset", $query);
+    }
+
+    public function listVhostRecordPreset(array $query = []): string
+    {
+        return $this->requestWithRetry("ListVhostRecordPreset", $query);
+    }
+
+    public function createTranscodePreset(array $query = []): string
+    {
+        return $this->requestWithRetry("CreateTranscodePreset", $query);
+    }
+
+    public function updateTranscodePreset(array $query = []): string
+    {
+        return $this->requestWithRetry("UpdateTranscodePreset", $query);
+    }
+
+    public function deleteTranscodePreset(array $query = []): string
+    {
+        return $this->requestWithRetry("DeleteTranscodePreset", $query);
+    }
+
+    public function listVhostTransCodePreset(array $query = []): string
+    {
+        return $this->requestWithRetry("ListVhostTransCodePreset", $query);
+    }
+
+    public function createSnapshotPreset(array $query = []): string
+    {
+        return $this->requestWithRetry("CreateSnapshotPreset", $query);
+    }
+
+    public function updateSnapshotPreset(array $query = []): string
+    {
+        return $this->requestWithRetry("UpdateSnapshotPreset", $query);
+    }
+
+    public function deleteSnapshotPreset(array $query = []): string
+    {
+        return $this->requestWithRetry("DeleteSnapshotPreset", $query);
+    }
+
+    public function listVhostSnapshotPreset(array $query = []): string
+    {
+        return $this->requestWithRetry("ListVhostSnapshotPreset", $query);
+    }
+
+    public function describeLiveBandwidthData(array $query = []): string
+    {
+        return $this->requestWithRetry("DescribeLiveBandwidthData", $query);
+    }
+
+    public function describeLiveTrafficData(array $query = []): string
+    {
+        return $this->requestWithRetry("DescribeLiveTrafficData", $query);
+    }
+
+    public function describeLiveP95PeakBandwidthData(array $query = []): string
+    {
+        return $this->requestWithRetry("DescribeLiveP95PeakBandwidthData", $query);
+    }
+
+    public function describeRecordData(array $query = []): string
+    {
+        return $this->requestWithRetry("DescribeRecordData", $query);
+    }
+
+    public function describeTranscodeData(array $query = []): string
+    {
+        return $this->requestWithRetry("DescribeTranscodeData", $query);
+    }
+
+    public function describeSnapshotData(array $query = []): string
+    {
+        return $this->requestWithRetry("DescribeSnapshotData", $query);
+    }
+
+    public function describeLiveDomainLog(array $query = []): string
+    {
+        return $this->requestWithRetry("DescribeLiveDomainLog", $query);
+    }
+
+    public function describePushStreamMetrics(array $query = []): string
+    {
+        return $this->requestWithRetry("DescribePushStreamMetrics", $query);
+    }
+
+    public function describeLiveStreamSessions(array $query = []): string
+    {
+        return $this->requestWithRetry("DescribeLiveStreamSessions", $query);
+    }
+
+    public function describePlayResponseStatusStat(array $query = []): string
+    {
+        return $this->requestWithRetry("DescribePlayResponseStatusStat", $query);
+    }
+
+    public function describeLiveMetricTrafficData(array $query = []): string
+    {
+        return $this->requestWithRetry("DescribeLiveMetricTrafficData", $query);
+    }
+
+    public function describeLiveMetricBandwidthData(array $query = []): string
+    {
+        return $this->requestWithRetry("DescribeLiveMetricBandwidthData", $query);
+    }
+
+    public function describePlayStreamList(array $query = []): string
+    {
+        return $this->requestWithRetry("DescribePlayStreamList", $query);
+    }
+
+    public function describePullToPushBandwidthData(array $query = []): string
+    {
+        return $this->requestWithRetry("DescribePullToPushBandwidthData", $query);
+    }
+
+    public function createSnapshotAuditPreset(array $query = []): string
+    {
+        return $this->requestWithRetry("CreateSnapshotAuditPreset", $query);
+    }
+
+    public function updateSnapshotAuditPreset(array $query = []): string
+    {
+        return $this->requestWithRetry("UpdateSnapshotAuditPreset", $query);
+    }
+
+    public function deleteSnapshotAuditPreset(array $query = []): string
+    {
+        return $this->requestWithRetry("DeleteSnapshotAuditPreset", $query);
+    }
+
+    public function listVhostSnapshotAuditPreset(array $query = []): string
+    {
+        return $this->requestWithRetry("ListVhostSnapshotAuditPreset", $query);
+    }
+
+    public function describeLiveAuditData(array $query = []): string
+    {
+        return $this->requestWithRetry("DescribeLiveAuditData", $query);
+    }
+}
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Live.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Live.php
new file mode 100755
index 0000000..ba605d4
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Live.php
@@ -0,0 +1,1232 @@
+<?php
+# Code generated by protoc-gen-volcengine-sdk
+# source: Live
+# DO NOT EDIT!
+
+namespace Volc\Service\Live;
+use Volc\Base\V4Curl;
+use Volc\Service\Live\Models\Request\CreateDomainRequest;
+use Volc\Service\Live\Models\Request\CreateVQScoreTaskRequest;
+use Volc\Service\Live\Models\Request\DescribeClosedStreamInfoByPageRequest;
+use Volc\Service\Live\Models\Request\DescribeLiveStreamStateRequest;
+use Volc\Service\Live\Models\Response\CreateDomainResponse;
+use Volc\Service\Live\Models\Request\ListDomainDetailRequest;
+use Volc\Service\Live\Models\Response\DescribeCDNSnapshotHistoryResponse;
+use Volc\Service\Live\Models\Response\DescribeClosedStreamInfoByPageResponse;
+use Volc\Service\Live\Models\Response\DescribeLiveStreamStateResponse;
+use Volc\Service\Live\Models\Response\ListDomainDetailResponse;
+use Volc\Service\Live\Models\Request\DisableDomainRequest;
+use Volc\Service\Live\Models\Response\DisableDomainResponse;
+use Volc\Service\Live\Models\Request\EnableDomainRequest;
+use Volc\Service\Live\Models\Response\EnableDomainResponse;
+use Volc\Service\Live\Models\Request\DeleteDomainRequest;
+use Volc\Service\Live\Models\Response\DeleteDomainResponse;
+use Volc\Service\Live\Models\Request\DescribeDomainRequest;
+use Volc\Service\Live\Models\Response\DescribeDomainResponse;
+use Volc\Service\Live\Models\Request\ManagerPullPushDomainBindRequest;
+use Volc\Service\Live\Models\Response\ManagerPullPushDomainBindResponse;
+use Volc\Service\Live\Models\Request\DescribeCDNSnapshotHistoryRequest;
+use Volc\Service\Live\Models\Response\DescribeLiveStreamInfoByPageResponse;
+use Volc\Service\Live\Models\Request\DescribeRecordTaskFileHistoryRequest;
+use Volc\Service\Live\Models\Response\DescribeRecordTaskFileHistoryResponse;
+use Volc\Service\Live\Models\Request\DescribeLiveStreamInfoByPageRequest;
+use Volc\Service\Live\Models\Request\KillStreamRequest;
+use Volc\Service\Live\Models\Response\KillStreamResponse;
+use Volc\Service\Live\Models\Request\ForbidStreamRequest;
+use Volc\Service\Live\Models\Response\ForbidStreamResponse;
+use Volc\Service\Live\Models\Response\UpdateRelaySourceResponse;
+use Volc\Service\Live\Models\Request\DescribeForbiddenStreamInfoByPageRequest;
+use Volc\Service\Live\Models\Response\DescribeForbiddenStreamInfoByPageResponse;
+use Volc\Service\Live\Models\Request\ResumeStreamRequest;
+use Volc\Service\Live\Models\Response\ResumeStreamResponse;
+use Volc\Service\Live\Models\Request\UpdateRelaySourceRequest;
+use Volc\Service\Live\Models\Request\DeleteRelaySourceRequest;
+use Volc\Service\Live\Models\Response\DeleteRelaySourceResponse;
+use Volc\Service\Live\Models\Request\DescribeRelaySourceRequest;
+use Volc\Service\Live\Models\Response\DescribeRelaySourceResponse;
+use Volc\Service\Live\Models\Response\CreateVQScoreTaskResponse;
+use Volc\Service\Live\Models\Request\DescribeVQScoreTaskRequest;
+use Volc\Service\Live\Models\Response\DescribeVQScoreTaskResponse;
+use Volc\Service\Live\Models\Request\ListVQScoreTaskRequest;
+use Volc\Service\Live\Models\Response\ListVQScoreTaskResponse;
+use Volc\Service\Live\Models\Request\GeneratePlayURLRequest;
+use Volc\Service\Live\Models\Response\GeneratePlayURLResponse;
+use Volc\Service\Live\Models\Request\GeneratePushURLRequest;
+use Volc\Service\Live\Models\Response\GeneratePushURLResponse;
+use Volc\Service\Live\Models\Request\CreatePullToPushTaskRequest;
+use Volc\Service\Live\Models\Response\CreatePullToPushTaskResponse;
+use Volc\Service\Live\Models\Request\ListPullToPushTaskRequest;
+use Volc\Service\Live\Models\Response\ListPullToPushTaskResponse;
+use Volc\Service\Live\Models\Request\UpdatePullToPushTaskRequest;
+use Volc\Service\Live\Models\Response\UpdatePullToPushTaskResponse;
+use Volc\Service\Live\Models\Request\StopPullToPushTaskRequest;
+use Volc\Service\Live\Models\Response\StopPullToPushTaskResponse;
+use Volc\Service\Live\Models\Request\RestartPullToPushTaskRequest;
+use Volc\Service\Live\Models\Response\RestartPullToPushTaskResponse;
+use Volc\Service\Live\Models\Request\DeletePullToPushTaskRequest;
+use Volc\Service\Live\Models\Response\DeletePullToPushTaskResponse;
+use Volc\Service\Live\Models\Request\UpdateDenyConfigRequest;
+use Volc\Service\Live\Models\Response\UpdateDenyConfigResponse;
+use Volc\Service\Live\Models\Request\DescribeDenyConfigRequest;
+use Volc\Service\Live\Models\Response\DescribeDenyConfigResponse;
+use Volc\Service\Live\Models\Request\DeleteDenyConfigRequest;
+use Volc\Service\Live\Models\Response\DeleteDenyConfigResponse;
+use Volc\Service\Vod\VodOption;
+
+/**
+ * Generated from protobuf service <code>live/service/service_live.proto</code>
+ */
+class Live extends V4Curl
+{
+
+    protected $apiList;
+
+    public function __construct()
+    {
+        $this->region = func_get_arg(0);
+        $this->apiList = LiveOption::$apiList;
+        parent::__construct($this->region);
+    }
+
+
+    protected function getConfig(string $region)
+    {
+        try {
+            return LiveOption::getConfig($region);
+        } catch (Exception $e) {
+            print_r($e->getMessage());
+            print_r($e->getTraceAsString());
+            throw $e;
+        }
+    }
+
+	/**
+     * CreateDomain.
+     *
+     * @param $req CreateDomainRequest
+     * @return CreateDomainResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function createDomain (CreateDomainRequest $req): CreateDomainResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParam($req);
+			$response = $this->request('CreateDomain', ['json' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new CreateDomainResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * ListDomainDetail.
+     *
+     * @param $req ListDomainDetailRequest
+     * @return ListDomainDetailResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function listDomainDetail (ListDomainDetailRequest $req): ListDomainDetailResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParam($req);
+			$response = $this->request('ListDomainDetail', ['json' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new ListDomainDetailResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * DisableDomain.
+     *
+     * @param $req DisableDomainRequest
+     * @return DisableDomainResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function disableDomain (DisableDomainRequest $req): DisableDomainResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParam($req);
+			$response = $this->request('DisableDomain', ['json' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new DisableDomainResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * EnableDomain.
+     *
+     * @param $req EnableDomainRequest
+     * @return EnableDomainResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function enableDomain (EnableDomainRequest $req): EnableDomainResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParam($req);
+			$response = $this->request('EnableDomain', ['json' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new EnableDomainResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * DeleteDomain.
+     *
+     * @param $req DeleteDomainRequest
+     * @return DeleteDomainResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function deleteDomain (DeleteDomainRequest $req): DeleteDomainResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParam($req);
+			$response = $this->request('DeleteDomain', ['json' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new DeleteDomainResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * DescribeDomain.
+     *
+     * @param $req DescribeDomainRequest
+     * @return DescribeDomainResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function describeDomain (DescribeDomainRequest $req): DescribeDomainResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParam($req);
+			$response = $this->request('DescribeDomain', ['json' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new DescribeDomainResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * ManagerPullPushDomainBind.
+     *
+     * @param $req ManagerPullPushDomainBindRequest
+     * @return ManagerPullPushDomainBindResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function managerPullPushDomainBind (ManagerPullPushDomainBindRequest $req): ManagerPullPushDomainBindResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParam($req);
+			$response = $this->request('ManagerPullPushDomainBind', ['json' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new ManagerPullPushDomainBindResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * DescribeCDNSnapshotHistory.
+     *
+     * @param $req DescribeCDNSnapshotHistoryRequest
+     * @return DescribeCDNSnapshotHistoryResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function DescribeCDNSnapshotHistory (DescribeCDNSnapshotHistoryRequest $req): DescribeCDNSnapshotHistoryResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParamV2($req);
+			$response = $this->request('DescribeCDNSnapshotHistory', ['json' => $query]);
+
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new DescribeCDNSnapshotHistoryResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * DescribeRecordTaskFileHistory.
+     *
+     * @param $req DescribeRecordTaskFileHistoryRequest
+     * @return DescribeRecordTaskFileHistoryResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function DescribeRecordTaskFileHistory (DescribeRecordTaskFileHistoryRequest $req): DescribeRecordTaskFileHistoryResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParamV2($req);
+			$response = $this->request('DescribeRecordTaskFileHistory', ['json' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new DescribeRecordTaskFileHistoryResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * DescribeLiveStreamInfoByPage.
+     *
+     * @param $req DescribeLiveStreamInfoByPageRequest
+     * @return DescribeLiveStreamInfoByPageResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function DescribeLiveStreamInfoByPage (DescribeLiveStreamInfoByPageRequest $req): DescribeLiveStreamInfoByPageResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParam($req);
+			$response = $this->request('DescribeLiveStreamInfoByPage', ['query' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new DescribeLiveStreamInfoByPageResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * KillStream.
+     *
+     * @param $req KillStreamRequest
+     * @return KillStreamResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function KillStream (KillStreamRequest $req): KillStreamResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParam($req);
+			$response = $this->request('KillStream', ['json' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new KillStreamResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * ForbidStream.
+     *
+     * @param $req ForbidStreamRequest
+     * @return ForbidStreamResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function ForbidStream (ForbidStreamRequest $req): ForbidStreamResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParam($req);
+			$response = $this->request('ForbidStream', ['json' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new ForbidStreamResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * DescribeClosedStreamInfoByPage.
+     *
+     * @param $req DescribeLiveStreamInfoByPageRequest
+     * @return UpdateRelaySourceResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function DescribeClosedStreamInfoByPage (DescribeClosedStreamInfoByPageRequest $req): DescribeClosedStreamInfoByPageResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParam($req);
+			$response = $this->request('DescribeClosedStreamInfoByPage', ['query' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new DescribeClosedStreamInfoByPageResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * DescribeLiveStreamState.
+     *
+     * @param $req DescribeLiveStreamInfoByPageRequest
+     * @return UpdateRelaySourceResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function DescribeLiveStreamState (DescribeLiveStreamStateRequest $req): DescribeLiveStreamStateResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParam($req);
+			$response = $this->request('DescribeLiveStreamState', ['query' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new DescribeLiveStreamStateResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * DescribeForbiddenStreamInfoByPage.
+     *
+     * @param $req DescribeForbiddenStreamInfoByPageRequest
+     * @return DescribeForbiddenStreamInfoByPageResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function DescribeForbiddenStreamInfoByPage (DescribeForbiddenStreamInfoByPageRequest $req): DescribeForbiddenStreamInfoByPageResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParam($req);
+			$response = $this->request('DescribeForbiddenStreamInfoByPage', ['query' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new DescribeForbiddenStreamInfoByPageResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * ResumeStream.
+     *
+     * @param $req ResumeStreamRequest
+     * @return ResumeStreamResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function ResumeStream (ResumeStreamRequest $req): ResumeStreamResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParam($req);
+			$response = $this->request('ResumeStream', ['json' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new ResumeStreamResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * UpdateRelaySourceV2.
+     *
+     * @param $req UpdateRelaySourceRequest
+     * @return UpdateRelaySourceResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function updateRelaySourceV2 (UpdateRelaySourceRequest $req): UpdateRelaySourceResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParamV2($req);
+            foreach ($query as $key => $value) {
+                if ($key == "RelaySourceDomainList"){
+                    $RelaySourceDomainList = $req->RelaySourceDomainList;
+                    $json_encode = json_encode($RelaySourceDomainList);
+                    $json_decode = json_decode($json_encode);
+                    $query[$key] = $json_decode;
+                }
+                if ($key == "RelaySourceParams"){
+                    $RelaySourceParams = $req->RelaySourceParams;
+                    $json_encode = json_encode($RelaySourceParams);
+                    $json_decode = json_decode($json_encode);
+                    $query[$key] = $json_decode;
+                }
+            }
+			$response = $this->request('UpdateRelaySourceV2', ['json' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new UpdateRelaySourceResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * DeleteRelaySourceV2.
+     *
+     * @param $req DeleteRelaySourceRequest
+     * @return DeleteRelaySourceResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function deleteRelaySourceV2 (DeleteRelaySourceRequest $req): DeleteRelaySourceResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParam($req);
+			$response = $this->request('DeleteRelaySourceV2', ['json' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new DeleteRelaySourceResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * DescribeRelaySourceV2.
+     *
+     * @param $req DescribeRelaySourceRequest
+     * @return DescribeRelaySourceResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function describeRelaySourceV2 (DescribeRelaySourceRequest $req): DescribeRelaySourceResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParam($req);
+			$response = $this->request('DescribeRelaySourceV2', ['json' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new DescribeRelaySourceResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+
+    /**
+     * CreateVQScoreTask.
+     *
+     * @param $req CreateVQScoreTaskRequest
+     * @return CreateVQScoreTaskResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function createVQScoreTask (CreateVQScoreTaskRequest $req): CreateVQScoreTaskResponse
+    {
+        try {
+            $query = LiveUtils::formatRequestParamV2($req);
+            $response = $this->request('CreateVQScoreTask', ['json' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new CreateVQScoreTaskResponse();
+        try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+
+    /**
+     * DescribeVQScoreTask.
+     *
+     * @param $req DescribeVQScoreTaskRequest
+     * @return DescribeVQScoreTaskResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function describeVQScoreTask (DescribeVQScoreTaskRequest $req): DescribeVQScoreTaskResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParam($req);
+			$response = $this->request('DescribeVQScoreTask', ['json' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new DescribeVQScoreTaskResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * ListVQScoreTask.
+     *
+     * @param $req ListVQScoreTaskRequest
+     * @return ListVQScoreTaskResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function listVQScoreTask (ListVQScoreTaskRequest $req): ListVQScoreTaskResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParamV2($req);
+			$response = $this->request('ListVQScoreTask', ['json' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new ListVQScoreTaskResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * GeneratePlayURL.
+     *
+     * @param $req GeneratePlayURLRequest
+     * @return GeneratePlayURLResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function generatePlayURL (GeneratePlayURLRequest $req): GeneratePlayURLResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParam($req);
+			$response = $this->request('GeneratePlayURL', ['json' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new GeneratePlayURLResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * GeneratePushURL.
+     *
+     * @param $req GeneratePushURLRequest
+     * @return GeneratePushURLResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function generatePushURL (GeneratePushURLRequest $req): GeneratePushURLResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParam($req);
+			$response = $this->request('GeneratePushURL', ['json' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new GeneratePushURLResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * CreatePullToPushTask.
+     *
+     * @param $req CreatePullToPushTaskRequest
+     * @return CreatePullToPushTaskResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function createPullToPushTask (CreatePullToPushTaskRequest $req): CreatePullToPushTaskResponse
+	{
+		try {
+            $query = LiveUtils::formatRequestParamV2($req);
+            foreach ($query as $key => $value) {
+                if ($key == "SrcAddrS"){
+                    $SrcAddrS = $req->SrcAddrS;
+                    $json_encode = json_encode($SrcAddrS);
+                    $json_decode = json_decode($json_encode);
+                    $query[$key] = $json_decode;
+                }
+            }
+			$response = $this->request('CreatePullToPushTask', ['json' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new CreatePullToPushTaskResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * ListPullToPushTask.
+     *
+     * @param $req ListPullToPushTaskRequest
+     * @return ListPullToPushTaskResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function listPullToPushTask (ListPullToPushTaskRequest $req): ListPullToPushTaskResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParam($req);
+			$response = $this->request('ListPullToPushTask', ['json' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new ListPullToPushTaskResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * UpdatePullToPushTask.
+     *
+     * @param $req UpdatePullToPushTaskRequest
+     * @return UpdatePullToPushTaskResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function updatePullToPushTask (UpdatePullToPushTaskRequest $req): UpdatePullToPushTaskResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParamV2($req);
+            foreach ($query as $key => $value) {
+                if ($key == "SrcAddrS"){
+                    $SrcAddrS = $req->SrcAddrS;
+                    $json_encode = json_encode($SrcAddrS);
+                    $json_decode = json_decode($json_encode);
+                    $query[$key] = $json_decode;
+                }
+            }
+			$response = $this->request('UpdatePullToPushTask', ['json' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new UpdatePullToPushTaskResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * StopPullToPushTask.
+     *
+     * @param $req StopPullToPushTaskRequest
+     * @return StopPullToPushTaskResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function stopPullToPushTask (StopPullToPushTaskRequest $req): StopPullToPushTaskResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParam($req);
+			$response = $this->request('StopPullToPushTask', ['json' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new StopPullToPushTaskResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * RestartPullToPushTask.
+     *
+     * @param $req RestartPullToPushTaskRequest
+     * @return RestartPullToPushTaskResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function restartPullToPushTask (RestartPullToPushTaskRequest $req): RestartPullToPushTaskResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParam($req);
+			$response = $this->request('RestartPullToPushTask', ['json' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new RestartPullToPushTaskResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * DeletePullToPushTask.
+     *
+     * @param $req DeletePullToPushTaskRequest
+     * @return DeletePullToPushTaskResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function deletePullToPushTask (DeletePullToPushTaskRequest $req): DeletePullToPushTaskResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParam($req);
+			$response = $this->request('DeletePullToPushTask', ['json' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new DeletePullToPushTaskResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * UpdateDenyConfig.
+     *
+     * @param $req UpdateDenyConfigRequest
+     * @return UpdateDenyConfigResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function updateDenyConfig (UpdateDenyConfigRequest $req): UpdateDenyConfigResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParamV2($req);
+            foreach ($query as $key => $value) {
+                if ($key == "DenyConfigList"){
+                    $denyConfigList = $req->DenyConfigList;
+                    $json_encode = json_encode($denyConfigList);
+                    $json_decode = json_decode($json_encode);
+                    $query[$key] = $json_decode;
+                }
+            }
+            $response = $this->request('UpdateDenyConfig', ['json' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new UpdateDenyConfigResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * DescribeDenyConfig.
+     *
+     * @param $req DescribeDenyConfigRequest
+     * @return DescribeDenyConfigResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function describeDenyConfig (DescribeDenyConfigRequest $req): DescribeDenyConfigResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParam($req);
+			$response = $this->request('DescribeDenyConfig', ['json' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new DescribeDenyConfigResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+	
+	/**
+     * DeleteDenyConfig.
+     *
+     * @param $req DeleteDenyConfigRequest
+     * @return DeleteDenyConfigResponse
+     * @throws Exception the exception
+	 * @throws Throwable the exception
+     */
+	public function deleteDenyConfig (DeleteDenyConfigRequest $req): DeleteDenyConfigResponse
+	{
+		try {
+			$query = LiveUtils::formatRequestParam($req);
+			$response = $this->request('DeleteDenyConfig', ['json' => $query]);
+		} catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }			
+		if ($response->getStatusCode() != 200) {
+			echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+		}
+		$respData = new DeleteDenyConfigResponse();
+		try {
+            $respData = LiveUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+	}
+
+}  // end of service
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/LiveOption.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/LiveOption.php
new file mode 100755
index 0000000..8057d9c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/LiveOption.php
@@ -0,0 +1,272 @@
+<?php
+
+namespace Volc\Service\Live;
+
+
+use Exception;
+
+
+class LiveOption
+{
+    public static $apiList = [
+        // **********************************************************************
+        // 直播
+        // **********************************************************************
+        'DescribeCDNSnapshotHistory' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeCDNSnapshotHistory',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribeRecordTaskFileHistory' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeRecordTaskFileHistory',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribeLiveStreamInfoByPage' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeLiveStreamInfoByPage',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'KillStream' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'KillStream',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribeClosedStreamInfoByPage' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeClosedStreamInfoByPage',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribeLiveStreamState' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeLiveStreamState',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribeForbiddenStreamInfoByPage' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeForbiddenStreamInfoByPage',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'UpdateRelaySourceV2' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'UpdateRelaySourceV2',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DeleteRelaySourceV2' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DeleteRelaySourceV2',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribeRelaySourceV2' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeRelaySourceV2',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'CreateVQScoreTask' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'CreateVQScoreTask',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribeVQScoreTask' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeVQScoreTask',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'ListVQScoreTask' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListVQScoreTask',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'GeneratePlayURL' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'GeneratePlayURL',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'GeneratePushURL' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'GeneratePushURL',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'CreatePullToPushTask' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'CreatePullToPushTask',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'ListPullToPushTask' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListPullToPushTask',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'UpdatePullToPushTask' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'UpdatePullToPushTask',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'StopPullToPushTask' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'StopPullToPushTask',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'RestartPullToPushTask' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'RestartPullToPushTask',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DeletePullToPushTask' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DeletePullToPushTask',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ], 'UpdateDenyConfig' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'UpdateDenyConfig',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribeDenyConfig' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeDenyConfig',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+    ];
+
+    /**
+     * @throws Exception
+     */
+    public static function getConfig(string $region = '')
+    {
+        switch ($region) {
+            case 'cn-north-1':
+                $config = [
+                    'host' => 'https://live.volcengineapi.com',
+                    'config' => [
+                        'timeout' => 5.0,
+                        'headers' => [
+                            'Accept' => 'application/json',
+                        ],
+                        'v4_credentials' => [
+                            'region' => 'cn-north-1',
+                            'service' => 'live',
+                        ],
+                    ],
+                ];
+                break;
+            default:
+                throw new Exception("Cant find the region, please check it carefully");
+        }
+        return $config;
+    }
+}
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/LiveUtils.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/LiveUtils.php
new file mode 100755
index 0000000..13f4484
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/LiveUtils.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace Volc\Service\Live;
+
+use Exception;
+use Throwable;
+use Volc\Service\Live\Models\Request\UpdateDenyConfigRequest;
+
+class LiveUtils {
+
+    public static function formatRequestParamV2($req): array
+    {
+        try {
+            $json_encode = json_encode($req);
+
+            $query = json_decode($json_encode, true);
+            foreach ($query as $key => $value) {
+                switch (gettype($value)) {
+                    case 'integer':
+                    case 'string':
+                    case 'double':
+                    case 'NULL':
+                    case 'boolean':
+                        break;
+                    default:
+                        $d = json_encode($value);
+                        $query[$key] = $d;
+                }
+            }
+        } catch (Exception $e) {
+            echo $e, "\n";
+            throw $e;
+        } catch (Throwable $t) {
+            echo $t, "\n";
+            throw $t;
+        }
+        return $query;
+    }
+
+    public static function formatRequestParam($req): array
+    {
+        try {
+            $jsonData = $req->serializeToJsonString();
+            $query = json_decode($jsonData, true);
+            foreach ($query as $key => $value) {
+                switch (gettype($query[$key])) {
+                    case 'integer':
+                    case 'string':
+                    case 'double':
+                    case 'NULL':
+                    case 'boolean':
+                        break;
+                    default:
+                        $d = json_encode($value);
+                        $query[$key] = $d;
+                }
+            }
+        } catch (Exception $e) {
+            echo $e, "\n";
+            throw $e;
+        } catch (Throwable $t) {
+            echo $t, "\n";
+            throw $t;
+        }
+        return $query;
+    }
+
+    public static function parseResponseData($response, $respData)
+    {
+        try {
+            $respData->mergeFromJsonString($response->getBody(), true);
+        } catch (Exception $e) {
+            echo $e, "\n";
+            if ($respData == null || $respData->getResponseMetadata() == null) {
+                throw new Exception($response->getReasonPhrase());
+            }
+        } catch (Throwable $t) {
+            echo $t, "\n";
+            if ($respData == null || $respData->getResponseMetadata() == null) {
+                throw new Exception($response->getReasonPhrase());
+            }
+        }
+        return $respData;
+    }
+
+}
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/AddrScoreInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/AddrScoreInfo.php
new file mode 100755
index 0000000..432211b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/AddrScoreInfo.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/VQScore.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.AddrScoreInfo</code>
+ */
+class AddrScoreInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *拉流地址类型,1:主拉流地址,2:对比拉流地址
+     *
+     * Generated from protobuf field <code>int64 AddrType = 1;</code>
+     */
+    protected $AddrType = 0;
+    /**
+     * 测评得分列表。
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.ScoreInfo ScoreInfoList = 2;</code>
+     */
+    private $ScoreInfoList;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int|string $AddrType
+     *          拉流地址类型,1:主拉流地址,2:对比拉流地址
+     *     @type \Volc\Service\Live\Models\Business\ScoreInfo[]|\Google\Protobuf\Internal\RepeatedField $ScoreInfoList
+     *           测评得分列表。
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\VQScore::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *拉流地址类型,1:主拉流地址,2:对比拉流地址
+     *
+     * Generated from protobuf field <code>int64 AddrType = 1;</code>
+     * @return int|string
+     */
+    public function getAddrType()
+    {
+        return $this->AddrType;
+    }
+
+    /**
+     *拉流地址类型,1:主拉流地址,2:对比拉流地址
+     *
+     * Generated from protobuf field <code>int64 AddrType = 1;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setAddrType($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->AddrType = $var;
+
+        return $this;
+    }
+
+    /**
+     * 测评得分列表。
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.ScoreInfo ScoreInfoList = 2;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getScoreInfoList()
+    {
+        return $this->ScoreInfoList;
+    }
+
+    /**
+     * 测评得分列表。
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.ScoreInfo ScoreInfoList = 2;</code>
+     * @param \Volc\Service\Live\Models\Business\ScoreInfo[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setScoreInfoList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Live\Models\Business\ScoreInfo::class);
+        $this->ScoreInfoList = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/CDNSnapshotHistory.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/CDNSnapshotHistory.php
new file mode 100755
index 0000000..4bd4f17
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/CDNSnapshotHistory.php
@@ -0,0 +1,371 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/snapshot_manage.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.CDNSnapshotHistory</code>
+ */
+class CDNSnapshotHistory extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Vhost表示视频直播服务的配置空间
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     */
+    protected $Vhost = '';
+    /**
+     * 应用名称
+     *
+     * Generated from protobuf field <code>string App = 2;</code>
+     */
+    protected $App = '';
+    /**
+     * 流名称
+     *
+     * Generated from protobuf field <code>string Stream = 3;</code>
+     */
+    protected $Stream = '';
+    /**
+     * 截图文件保存的路径
+     *
+     * Generated from protobuf field <code>string Path = 4;</code>
+     */
+    protected $Path = '';
+    /**
+     *文件大小
+     *
+     * Generated from protobuf field <code>string FileName = 5;</code>
+     */
+    protected $FileName = '';
+    /**
+     * 直播流开始时间
+     *
+     * Generated from protobuf field <code>string TimeStamp = 6;</code>
+     */
+    protected $TimeStamp = '';
+    /**
+     * 视频宽度
+     *
+     * Generated from protobuf field <code>int64 Width = 7;</code>
+     */
+    protected $Width = 0;
+    /**
+     * 视频高度
+     *
+     * Generated from protobuf field <code>int64 Height = 8;</code>
+     */
+    protected $Height = 0;
+    /**
+     * 直播流开始时间
+     *
+     * Generated from protobuf field <code>string ServiceID = 9;</code>
+     */
+    protected $ServiceID = '';
+    /**
+     * 直播流开始时间
+     *
+     * Generated from protobuf field <code>string URI = 10;</code>
+     */
+    protected $URI = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vhost
+     *           Vhost表示视频直播服务的配置空间
+     *     @type string $App
+     *           应用名称
+     *     @type string $Stream
+     *           流名称
+     *     @type string $Path
+     *           截图文件保存的路径
+     *     @type string $FileName
+     *          文件大小
+     *     @type string $TimeStamp
+     *           直播流开始时间
+     *     @type int|string $Width
+     *           视频宽度
+     *     @type int|string $Height
+     *           视频高度
+     *     @type string $ServiceID
+     *           直播流开始时间
+     *     @type string $URI
+     *           直播流开始时间
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\SnapshotManage::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Vhost表示视频直播服务的配置空间
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @return string
+     */
+    public function getVhost()
+    {
+        return $this->Vhost;
+    }
+
+    /**
+     * Vhost表示视频直播服务的配置空间
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVhost($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vhost = $var;
+
+        return $this;
+    }
+
+    /**
+     * 应用名称
+     *
+     * Generated from protobuf field <code>string App = 2;</code>
+     * @return string
+     */
+    public function getApp()
+    {
+        return $this->App;
+    }
+
+    /**
+     * 应用名称
+     *
+     * Generated from protobuf field <code>string App = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setApp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->App = $var;
+
+        return $this;
+    }
+
+    /**
+     * 流名称
+     *
+     * Generated from protobuf field <code>string Stream = 3;</code>
+     * @return string
+     */
+    public function getStream()
+    {
+        return $this->Stream;
+    }
+
+    /**
+     * 流名称
+     *
+     * Generated from protobuf field <code>string Stream = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStream($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Stream = $var;
+
+        return $this;
+    }
+
+    /**
+     * 截图文件保存的路径
+     *
+     * Generated from protobuf field <code>string Path = 4;</code>
+     * @return string
+     */
+    public function getPath()
+    {
+        return $this->Path;
+    }
+
+    /**
+     * 截图文件保存的路径
+     *
+     * Generated from protobuf field <code>string Path = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setPath($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Path = $var;
+
+        return $this;
+    }
+
+    /**
+     *文件大小
+     *
+     * Generated from protobuf field <code>string FileName = 5;</code>
+     * @return string
+     */
+    public function getFileName()
+    {
+        return $this->FileName;
+    }
+
+    /**
+     *文件大小
+     *
+     * Generated from protobuf field <code>string FileName = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 直播流开始时间
+     *
+     * Generated from protobuf field <code>string TimeStamp = 6;</code>
+     * @return string
+     */
+    public function getTimeStamp()
+    {
+        return $this->TimeStamp;
+    }
+
+    /**
+     * 直播流开始时间
+     *
+     * Generated from protobuf field <code>string TimeStamp = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTimeStamp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TimeStamp = $var;
+
+        return $this;
+    }
+
+    /**
+     * 视频宽度
+     *
+     * Generated from protobuf field <code>int64 Width = 7;</code>
+     * @return int|string
+     */
+    public function getWidth()
+    {
+        return $this->Width;
+    }
+
+    /**
+     * 视频宽度
+     *
+     * Generated from protobuf field <code>int64 Width = 7;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setWidth($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->Width = $var;
+
+        return $this;
+    }
+
+    /**
+     * 视频高度
+     *
+     * Generated from protobuf field <code>int64 Height = 8;</code>
+     * @return int|string
+     */
+    public function getHeight()
+    {
+        return $this->Height;
+    }
+
+    /**
+     * 视频高度
+     *
+     * Generated from protobuf field <code>int64 Height = 8;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setHeight($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->Height = $var;
+
+        return $this;
+    }
+
+    /**
+     * 直播流开始时间
+     *
+     * Generated from protobuf field <code>string ServiceID = 9;</code>
+     * @return string
+     */
+    public function getServiceID()
+    {
+        return $this->ServiceID;
+    }
+
+    /**
+     * 直播流开始时间
+     *
+     * Generated from protobuf field <code>string ServiceID = 9;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setServiceID($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->ServiceID = $var;
+
+        return $this;
+    }
+
+    /**
+     * 直播流开始时间
+     *
+     * Generated from protobuf field <code>string URI = 10;</code>
+     * @return string
+     */
+    public function getURI()
+    {
+        return $this->URI;
+    }
+
+    /**
+     * 直播流开始时间
+     *
+     * Generated from protobuf field <code>string URI = 10;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setURI($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->URI = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/CDNSnapshotHistoryInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/CDNSnapshotHistoryInfo.php
new file mode 100755
index 0000000..14b3132
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/CDNSnapshotHistoryInfo.php
@@ -0,0 +1,109 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/snapshot_manage.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.CDNSnapshotHistoryInfo</code>
+ */
+class CDNSnapshotHistoryInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 截图历史列表记录
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.CDNSnapshotHistory Data = 1;</code>
+     */
+    private $Data;
+    /**
+     * 分页信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.Pagination Pagination = 2;</code>
+     */
+    protected $Pagination = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Live\Models\Business\CDNSnapshotHistory[]|\Google\Protobuf\Internal\RepeatedField $Data
+     *           截图历史列表记录
+     *     @type \Volc\Service\Live\Models\Business\Pagination $Pagination
+     *           分页信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\SnapshotManage::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 截图历史列表记录
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.CDNSnapshotHistory Data = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getData()
+    {
+        return $this->Data;
+    }
+
+    /**
+     * 截图历史列表记录
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.CDNSnapshotHistory Data = 1;</code>
+     * @param \Volc\Service\Live\Models\Business\CDNSnapshotHistory[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setData($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Live\Models\Business\CDNSnapshotHistory::class);
+        $this->Data = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 分页信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.Pagination Pagination = 2;</code>
+     * @return \Volc\Service\Live\Models\Business\Pagination|null
+     */
+    public function getPagination()
+    {
+        return $this->Pagination;
+    }
+
+    public function hasPagination()
+    {
+        return isset($this->Pagination);
+    }
+
+    public function clearPagination()
+    {
+        unset($this->Pagination);
+    }
+
+    /**
+     * 分页信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.Pagination Pagination = 2;</code>
+     * @param \Volc\Service\Live\Models\Business\Pagination $var
+     * @return $this
+     */
+    public function setPagination($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Live\Models\Business\Pagination::class);
+        $this->Pagination = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/ClosedStream.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/ClosedStream.php
new file mode 100755
index 0000000..4d5e008
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/ClosedStream.php
@@ -0,0 +1,277 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/stream_manage.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.ClosedStream</code>
+ */
+class ClosedStream extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Vhost表示视频直播服务的配置空间
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     */
+    protected $Vhost = '';
+    /**
+     * 域名
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     */
+    protected $Domain = '';
+    /**
+     * 应用名称
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     */
+    protected $App = '';
+    /**
+     * 流名称
+     *
+     * Generated from protobuf field <code>string Stream = 4;</code>
+     */
+    protected $Stream = '';
+    /**
+     * 直播流开始时间
+     *
+     * Generated from protobuf field <code>string StartTime = 5;</code>
+     */
+    protected $StartTime = '';
+    /**
+     * 直播流结束时间
+     *
+     * Generated from protobuf field <code>string EndTime = 6;</code>
+     */
+    protected $EndTime = '';
+    /**
+     * 表示推流方式,缺省情况查询全部推流方式。支持如下取值。
+     *<li>push:直推流;
+     *<li>relay:回源流。
+     *
+     * Generated from protobuf field <code>string SourceType = 7;</code>
+     */
+    protected $SourceType = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vhost
+     *           Vhost表示视频直播服务的配置空间
+     *     @type string $Domain
+     *           域名
+     *     @type string $App
+     *           应用名称
+     *     @type string $Stream
+     *           流名称
+     *     @type string $StartTime
+     *           直播流开始时间
+     *     @type string $EndTime
+     *           直播流结束时间
+     *     @type string $SourceType
+     *           表示推流方式,缺省情况查询全部推流方式。支持如下取值。
+     *          <li>push:直推流;
+     *          <li>relay:回源流。
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\StreamManage::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Vhost表示视频直播服务的配置空间
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @return string
+     */
+    public function getVhost()
+    {
+        return $this->Vhost;
+    }
+
+    /**
+     * Vhost表示视频直播服务的配置空间
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVhost($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vhost = $var;
+
+        return $this;
+    }
+
+    /**
+     * 域名
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     * @return string
+     */
+    public function getDomain()
+    {
+        return $this->Domain;
+    }
+
+    /**
+     * 域名
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomain($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Domain = $var;
+
+        return $this;
+    }
+
+    /**
+     * 应用名称
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     * @return string
+     */
+    public function getApp()
+    {
+        return $this->App;
+    }
+
+    /**
+     * 应用名称
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setApp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->App = $var;
+
+        return $this;
+    }
+
+    /**
+     * 流名称
+     *
+     * Generated from protobuf field <code>string Stream = 4;</code>
+     * @return string
+     */
+    public function getStream()
+    {
+        return $this->Stream;
+    }
+
+    /**
+     * 流名称
+     *
+     * Generated from protobuf field <code>string Stream = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStream($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Stream = $var;
+
+        return $this;
+    }
+
+    /**
+     * 直播流开始时间
+     *
+     * Generated from protobuf field <code>string StartTime = 5;</code>
+     * @return string
+     */
+    public function getStartTime()
+    {
+        return $this->StartTime;
+    }
+
+    /**
+     * 直播流开始时间
+     *
+     * Generated from protobuf field <code>string StartTime = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStartTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StartTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * 直播流结束时间
+     *
+     * Generated from protobuf field <code>string EndTime = 6;</code>
+     * @return string
+     */
+    public function getEndTime()
+    {
+        return $this->EndTime;
+    }
+
+    /**
+     * 直播流结束时间
+     *
+     * Generated from protobuf field <code>string EndTime = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setEndTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->EndTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * 表示推流方式,缺省情况查询全部推流方式。支持如下取值。
+     *<li>push:直推流;
+     *<li>relay:回源流。
+     *
+     * Generated from protobuf field <code>string SourceType = 7;</code>
+     * @return string
+     */
+    public function getSourceType()
+    {
+        return $this->SourceType;
+    }
+
+    /**
+     * 表示推流方式,缺省情况查询全部推流方式。支持如下取值。
+     *<li>push:直推流;
+     *<li>relay:回源流。
+     *
+     * Generated from protobuf field <code>string SourceType = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSourceType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SourceType = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/ClosedStreamInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/ClosedStreamInfo.php
new file mode 100755
index 0000000..23796d9
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/ClosedStreamInfo.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/stream_manage.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.ClosedStreamInfo</code>
+ */
+class ClosedStreamInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 历史流的信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.ClosedStream StreamInfoList = 1;</code>
+     */
+    private $StreamInfoList;
+    /**
+     * 流数量
+     *
+     * Generated from protobuf field <code>int64 RoughCount = 2;</code>
+     */
+    protected $RoughCount = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Live\Models\Business\ClosedStream[]|\Google\Protobuf\Internal\RepeatedField $StreamInfoList
+     *           历史流的信息列表
+     *     @type int|string $RoughCount
+     *           流数量
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\StreamManage::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 历史流的信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.ClosedStream StreamInfoList = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getStreamInfoList()
+    {
+        return $this->StreamInfoList;
+    }
+
+    /**
+     * 历史流的信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.ClosedStream StreamInfoList = 1;</code>
+     * @param \Volc\Service\Live\Models\Business\ClosedStream[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setStreamInfoList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Live\Models\Business\ClosedStream::class);
+        $this->StreamInfoList = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 流数量
+     *
+     * Generated from protobuf field <code>int64 RoughCount = 2;</code>
+     * @return int|string
+     */
+    public function getRoughCount()
+    {
+        return $this->RoughCount;
+    }
+
+    /**
+     * 流数量
+     *
+     * Generated from protobuf field <code>int64 RoughCount = 2;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setRoughCount($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->RoughCount = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/CreatePullToPushTaskResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/CreatePullToPushTaskResult.php
new file mode 100755
index 0000000..dbbb4bc
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/CreatePullToPushTaskResult.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/pull_to_push.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.CreatePullToPushTaskResult</code>
+ */
+class CreatePullToPushTaskResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *生成拉流转推任务ID
+     *
+     * Generated from protobuf field <code>string TaskId = 1;</code>
+     */
+    protected $TaskId = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $TaskId
+     *          生成拉流转推任务ID
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\PullToPush::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *生成拉流转推任务ID
+     *
+     * Generated from protobuf field <code>string TaskId = 1;</code>
+     * @return string
+     */
+    public function getTaskId()
+    {
+        return $this->TaskId;
+    }
+
+    /**
+     *生成拉流转推任务ID
+     *
+     * Generated from protobuf field <code>string TaskId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTaskId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TaskId = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/DenyConfigDetail.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/DenyConfigDetail.php
new file mode 100755
index 0000000..2694ce2
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/DenyConfigDetail.php
@@ -0,0 +1,337 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/deny_config.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.DenyConfigDetail</code>
+ */
+class DenyConfigDetail extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *协议类型,比如tcp,kcp,quic
+     *
+     * Generated from protobuf field <code>repeated string ProType = 1;</code>
+     */
+    public $ProType;
+    /**
+     *格式类型,比如http,rtmp
+     *
+     * Generated from protobuf field <code>repeated string FmtType = 2;</code>
+     */
+    public $FmtType;
+    /**
+     *大洲
+     *
+     * Generated from protobuf field <code>string Continent = 3;</code>
+     */
+    public $Continent = '';
+    /**
+     *国家码
+     *
+     * Generated from protobuf field <code>string Country = 4;</code>
+     */
+    public $Country = '';
+    /**
+     *区域
+     *
+     * Generated from protobuf field <code>string Region = 5;</code>
+     */
+    public $Region = '';
+    /**
+     *城市
+     *
+     * Generated from protobuf field <code>string City = 6;</code>
+     */
+    public $City = '';
+    /**
+     *运营商
+     *
+     * Generated from protobuf field <code>string ISP = 7;</code>
+     */
+    public $ISP = '';
+    /**
+     *黑名单
+     *
+     * Generated from protobuf field <code>repeated string DenyList = 8;</code>
+     */
+    public $DenyList;
+    /**
+     *白名单
+     *
+     * Generated from protobuf field <code>repeated string AllowList = 9;</code>
+     */
+    public $AllowList;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $ProType
+     *          协议类型,比如tcp,kcp,quic
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $FmtType
+     *          格式类型,比如http,rtmp
+     *     @type string $Continent
+     *          大洲
+     *     @type string $Country
+     *          国家码
+     *     @type string $Region
+     *          区域
+     *     @type string $City
+     *          城市
+     *     @type string $ISP
+     *          运营商
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $DenyList
+     *          黑名单
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $AllowList
+     *          白名单
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\DenyConfig::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *协议类型,比如tcp,kcp,quic
+     *
+     * Generated from protobuf field <code>repeated string ProType = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getProType()
+    {
+        return $this->ProType;
+    }
+
+    /**
+     *协议类型,比如tcp,kcp,quic
+     *
+     * Generated from protobuf field <code>repeated string ProType = 1;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setProType($var)
+    {
+//        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->ProType = $var;
+
+        return $this;
+    }
+
+    /**
+     *格式类型,比如http,rtmp
+     *
+     * Generated from protobuf field <code>repeated string FmtType = 2;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getFmtType()
+    {
+        return $this->FmtType;
+    }
+
+    /**
+     *格式类型,比如http,rtmp
+     *
+     * Generated from protobuf field <code>repeated string FmtType = 2;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setFmtType($var)
+    {
+//        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->FmtType = $var;
+
+        return $this;
+    }
+
+    /**
+     *大洲
+     *
+     * Generated from protobuf field <code>string Continent = 3;</code>
+     * @return string
+     */
+    public function getContinent()
+    {
+        return $this->Continent;
+    }
+
+    /**
+     *大洲
+     *
+     * Generated from protobuf field <code>string Continent = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setContinent($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Continent = $var;
+
+        return $this;
+    }
+
+    /**
+     *国家码
+     *
+     * Generated from protobuf field <code>string Country = 4;</code>
+     * @return string
+     */
+    public function getCountry()
+    {
+        return $this->Country;
+    }
+
+    /**
+     *国家码
+     *
+     * Generated from protobuf field <code>string Country = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCountry($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Country = $var;
+
+        return $this;
+    }
+
+    /**
+     *区域
+     *
+     * Generated from protobuf field <code>string Region = 5;</code>
+     * @return string
+     */
+    public function getRegion()
+    {
+        return $this->Region;
+    }
+
+    /**
+     *区域
+     *
+     * Generated from protobuf field <code>string Region = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setRegion($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Region = $var;
+
+        return $this;
+    }
+
+    /**
+     *城市
+     *
+     * Generated from protobuf field <code>string City = 6;</code>
+     * @return string
+     */
+    public function getCity()
+    {
+        return $this->City;
+    }
+
+    /**
+     *城市
+     *
+     * Generated from protobuf field <code>string City = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCity($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->City = $var;
+
+        return $this;
+    }
+
+    /**
+     *运营商
+     *
+     * Generated from protobuf field <code>string ISP = 7;</code>
+     * @return string
+     */
+    public function getISP()
+    {
+        return $this->ISP;
+    }
+
+    /**
+     *运营商
+     *
+     * Generated from protobuf field <code>string ISP = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setISP($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->ISP = $var;
+
+        return $this;
+    }
+
+    /**
+     *黑名单
+     *
+     * Generated from protobuf field <code>repeated string DenyList = 8;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getDenyList()
+    {
+        return $this->DenyList;
+    }
+
+    /**
+     *黑名单
+     *
+     * Generated from protobuf field <code>repeated string DenyList = 8;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setDenyList($var)
+    {
+//        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->DenyList = $var;
+
+        return $this;
+    }
+
+    /**
+     *白名单
+     *
+     * Generated from protobuf field <code>repeated string AllowList = 9;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getAllowList()
+    {
+        return $this->AllowList;
+    }
+
+    /**
+     *白名单
+     *
+     * Generated from protobuf field <code>repeated string AllowList = 9;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setAllowList($var)
+    {
+//        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->AllowList = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/DescribeDenyConfigResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/DescribeDenyConfigResult.php
new file mode 100755
index 0000000..ab55d8c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/DescribeDenyConfigResult.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/deny_config.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.DescribeDenyConfigResult</code>
+ */
+class DescribeDenyConfigResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *配置列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.VhostWithDenyConfig DenyList = 1;</code>
+     */
+    private $DenyList;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Live\Models\Business\VhostWithDenyConfig[]|\Google\Protobuf\Internal\RepeatedField $DenyList
+     *          配置列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\DenyConfig::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *配置列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.VhostWithDenyConfig DenyList = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getDenyList()
+    {
+        return $this->DenyList;
+    }
+
+    /**
+     *配置列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.VhostWithDenyConfig DenyList = 1;</code>
+     * @param \Volc\Service\Live\Models\Business\VhostWithDenyConfig[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setDenyList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Live\Models\Business\VhostWithDenyConfig::class);
+        $this->DenyList = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/DomainList.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/DomainList.php
new file mode 100755
index 0000000..437253b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/DomainList.php
@@ -0,0 +1,567 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/domain.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.DomainList</code>
+ */
+class DomainList extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *对应的 Vhost。
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     */
+    protected $Vhost = '';
+    /**
+     * Domain 的名称。
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     */
+    protected $Domain = '';
+    /**
+     * 表示 Domain状态。状态说明如下所示。
+     * <li> 0:正常;
+     * <li> 1:审核中;
+     * <li> 2:禁用,禁止使用,此时domain不生效;
+     * <li> 3:删除;
+     * <li> 4:审核被驳回。审核不通过,需要重新创建并审核。
+     * <li> 5:欠费关停。
+     *
+     * Generated from protobuf field <code>int64 Status = 3;</code>
+     */
+    protected $Status = 0;
+    /**
+     * 域名类型,包含两种类型。
+     * <li> push:推流域名;
+     * <li> pull-flv:拉流域名,包含RTMP、FLV、HLS 格式。
+     *
+     * Generated from protobuf field <code>string Type = 4;</code>
+     */
+    protected $Type = '';
+    /**
+     * 区域,包含两种类型。
+     * <li> cn:中国大陆;
+     * <li> oversea:海外。
+     *
+     * Generated from protobuf field <code>string Region = 5;</code>
+     */
+    protected $Region = '';
+    /**
+     * Cname 信息。
+     *
+     * Generated from protobuf field <code>string CName = 6;</code>
+     */
+    protected $CName = '';
+    /**
+     * Cname 状态。
+     * <li> 0:未配置 CNAME;
+     * <li> 1:已配置 CNAME。
+     *
+     * Generated from protobuf field <code>int64 CnameCheck = 7;</code>
+     */
+    protected $CnameCheck = 0;
+    /**
+     * 域名是否可用的状态。
+     * <li> 0:正常,域名为可用状态;
+     * <li> 1:配置中,域名为可用状态;
+     * <li> 2:不可用,域名为其他的不可用状态。
+     *
+     * Generated from protobuf field <code>int64 DomainCheck = 8;</code>
+     */
+    protected $DomainCheck = 0;
+    /**
+     * IPC 备案校验是否通过,是否过期信息。
+     *
+     * Generated from protobuf field <code>int64 ICPCheck = 9;</code>
+     */
+    protected $ICPCheck = 0;
+    /**
+     *创建时间。
+     *
+     * Generated from protobuf field <code>string CreateTime = 10;</code>
+     */
+    protected $CreateTime = '';
+    /**
+     *所绑定证书支持的泛域名。
+     *
+     * Generated from protobuf field <code>string CertDomain = 11;</code>
+     */
+    protected $CertDomain = '';
+    /**
+     *绑定的证书信息。
+     *
+     * Generated from protobuf field <code>string ChainID = 12;</code>
+     */
+    protected $ChainID = '';
+    /**
+     *绑定的证书名称。
+     *
+     * Generated from protobuf field <code>string CertName = 13;</code>
+     */
+    protected $CertName = '';
+    /**
+     *绑定的推流域名。
+     *
+     * Generated from protobuf field <code>string PushDomain = 14;</code>
+     */
+    protected $PushDomain = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vhost
+     *          对应的 Vhost。
+     *     @type string $Domain
+     *           Domain 的名称。
+     *     @type int|string $Status
+     *           表示 Domain状态。状态说明如下所示。
+     *           <li> 0:正常;
+     *           <li> 1:审核中;
+     *           <li> 2:禁用,禁止使用,此时domain不生效;
+     *           <li> 3:删除;
+     *           <li> 4:审核被驳回。审核不通过,需要重新创建并审核。
+     *           <li> 5:欠费关停。
+     *     @type string $Type
+     *           域名类型,包含两种类型。
+     *           <li> push:推流域名;
+     *           <li> pull-flv:拉流域名,包含RTMP、FLV、HLS 格式。
+     *     @type string $Region
+     *           区域,包含两种类型。
+     *           <li> cn:中国大陆;
+     *           <li> oversea:海外。
+     *     @type string $CName
+     *           Cname 信息。
+     *     @type int|string $CnameCheck
+     *           Cname 状态。
+     *           <li> 0:未配置 CNAME;
+     *           <li> 1:已配置 CNAME。
+     *     @type int|string $DomainCheck
+     *           域名是否可用的状态。
+     *           <li> 0:正常,域名为可用状态;
+     *           <li> 1:配置中,域名为可用状态;
+     *           <li> 2:不可用,域名为其他的不可用状态。
+     *     @type int|string $ICPCheck
+     *           IPC 备案校验是否通过,是否过期信息。
+     *     @type string $CreateTime
+     *          创建时间。
+     *     @type string $CertDomain
+     *          所绑定证书支持的泛域名。
+     *     @type string $ChainID
+     *          绑定的证书信息。
+     *     @type string $CertName
+     *          绑定的证书名称。
+     *     @type string $PushDomain
+     *          绑定的推流域名。
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\Domain::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *对应的 Vhost。
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @return string
+     */
+    public function getVhost()
+    {
+        return $this->Vhost;
+    }
+
+    /**
+     *对应的 Vhost。
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVhost($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vhost = $var;
+
+        return $this;
+    }
+
+    /**
+     * Domain 的名称。
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     * @return string
+     */
+    public function getDomain()
+    {
+        return $this->Domain;
+    }
+
+    /**
+     * Domain 的名称。
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomain($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Domain = $var;
+
+        return $this;
+    }
+
+    /**
+     * 表示 Domain状态。状态说明如下所示。
+     * <li> 0:正常;
+     * <li> 1:审核中;
+     * <li> 2:禁用,禁止使用,此时domain不生效;
+     * <li> 3:删除;
+     * <li> 4:审核被驳回。审核不通过,需要重新创建并审核。
+     * <li> 5:欠费关停。
+     *
+     * Generated from protobuf field <code>int64 Status = 3;</code>
+     * @return int|string
+     */
+    public function getStatus()
+    {
+        return $this->Status;
+    }
+
+    /**
+     * 表示 Domain状态。状态说明如下所示。
+     * <li> 0:正常;
+     * <li> 1:审核中;
+     * <li> 2:禁用,禁止使用,此时domain不生效;
+     * <li> 3:删除;
+     * <li> 4:审核被驳回。审核不通过,需要重新创建并审核。
+     * <li> 5:欠费关停。
+     *
+     * Generated from protobuf field <code>int64 Status = 3;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setStatus($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->Status = $var;
+
+        return $this;
+    }
+
+    /**
+     * 域名类型,包含两种类型。
+     * <li> push:推流域名;
+     * <li> pull-flv:拉流域名,包含RTMP、FLV、HLS 格式。
+     *
+     * Generated from protobuf field <code>string Type = 4;</code>
+     * @return string
+     */
+    public function getType()
+    {
+        return $this->Type;
+    }
+
+    /**
+     * 域名类型,包含两种类型。
+     * <li> push:推流域名;
+     * <li> pull-flv:拉流域名,包含RTMP、FLV、HLS 格式。
+     *
+     * Generated from protobuf field <code>string Type = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Type = $var;
+
+        return $this;
+    }
+
+    /**
+     * 区域,包含两种类型。
+     * <li> cn:中国大陆;
+     * <li> oversea:海外。
+     *
+     * Generated from protobuf field <code>string Region = 5;</code>
+     * @return string
+     */
+    public function getRegion()
+    {
+        return $this->Region;
+    }
+
+    /**
+     * 区域,包含两种类型。
+     * <li> cn:中国大陆;
+     * <li> oversea:海外。
+     *
+     * Generated from protobuf field <code>string Region = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setRegion($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Region = $var;
+
+        return $this;
+    }
+
+    /**
+     * Cname 信息。
+     *
+     * Generated from protobuf field <code>string CName = 6;</code>
+     * @return string
+     */
+    public function getCName()
+    {
+        return $this->CName;
+    }
+
+    /**
+     * Cname 信息。
+     *
+     * Generated from protobuf field <code>string CName = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CName = $var;
+
+        return $this;
+    }
+
+    /**
+     * Cname 状态。
+     * <li> 0:未配置 CNAME;
+     * <li> 1:已配置 CNAME。
+     *
+     * Generated from protobuf field <code>int64 CnameCheck = 7;</code>
+     * @return int|string
+     */
+    public function getCnameCheck()
+    {
+        return $this->CnameCheck;
+    }
+
+    /**
+     * Cname 状态。
+     * <li> 0:未配置 CNAME;
+     * <li> 1:已配置 CNAME。
+     *
+     * Generated from protobuf field <code>int64 CnameCheck = 7;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setCnameCheck($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->CnameCheck = $var;
+
+        return $this;
+    }
+
+    /**
+     * 域名是否可用的状态。
+     * <li> 0:正常,域名为可用状态;
+     * <li> 1:配置中,域名为可用状态;
+     * <li> 2:不可用,域名为其他的不可用状态。
+     *
+     * Generated from protobuf field <code>int64 DomainCheck = 8;</code>
+     * @return int|string
+     */
+    public function getDomainCheck()
+    {
+        return $this->DomainCheck;
+    }
+
+    /**
+     * 域名是否可用的状态。
+     * <li> 0:正常,域名为可用状态;
+     * <li> 1:配置中,域名为可用状态;
+     * <li> 2:不可用,域名为其他的不可用状态。
+     *
+     * Generated from protobuf field <code>int64 DomainCheck = 8;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setDomainCheck($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->DomainCheck = $var;
+
+        return $this;
+    }
+
+    /**
+     * IPC 备案校验是否通过,是否过期信息。
+     *
+     * Generated from protobuf field <code>int64 ICPCheck = 9;</code>
+     * @return int|string
+     */
+    public function getICPCheck()
+    {
+        return $this->ICPCheck;
+    }
+
+    /**
+     * IPC 备案校验是否通过,是否过期信息。
+     *
+     * Generated from protobuf field <code>int64 ICPCheck = 9;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setICPCheck($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->ICPCheck = $var;
+
+        return $this;
+    }
+
+    /**
+     *创建时间。
+     *
+     * Generated from protobuf field <code>string CreateTime = 10;</code>
+     * @return string
+     */
+    public function getCreateTime()
+    {
+        return $this->CreateTime;
+    }
+
+    /**
+     *创建时间。
+     *
+     * Generated from protobuf field <code>string CreateTime = 10;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCreateTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CreateTime = $var;
+
+        return $this;
+    }
+
+    /**
+     *所绑定证书支持的泛域名。
+     *
+     * Generated from protobuf field <code>string CertDomain = 11;</code>
+     * @return string
+     */
+    public function getCertDomain()
+    {
+        return $this->CertDomain;
+    }
+
+    /**
+     *所绑定证书支持的泛域名。
+     *
+     * Generated from protobuf field <code>string CertDomain = 11;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCertDomain($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CertDomain = $var;
+
+        return $this;
+    }
+
+    /**
+     *绑定的证书信息。
+     *
+     * Generated from protobuf field <code>string ChainID = 12;</code>
+     * @return string
+     */
+    public function getChainID()
+    {
+        return $this->ChainID;
+    }
+
+    /**
+     *绑定的证书信息。
+     *
+     * Generated from protobuf field <code>string ChainID = 12;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setChainID($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->ChainID = $var;
+
+        return $this;
+    }
+
+    /**
+     *绑定的证书名称。
+     *
+     * Generated from protobuf field <code>string CertName = 13;</code>
+     * @return string
+     */
+    public function getCertName()
+    {
+        return $this->CertName;
+    }
+
+    /**
+     *绑定的证书名称。
+     *
+     * Generated from protobuf field <code>string CertName = 13;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCertName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CertName = $var;
+
+        return $this;
+    }
+
+    /**
+     *绑定的推流域名。
+     *
+     * Generated from protobuf field <code>string PushDomain = 14;</code>
+     * @return string
+     */
+    public function getPushDomain()
+    {
+        return $this->PushDomain;
+    }
+
+    /**
+     *绑定的推流域名。
+     *
+     * Generated from protobuf field <code>string PushDomain = 14;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setPushDomain($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->PushDomain = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/DomainListInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/DomainListInfo.php
new file mode 100755
index 0000000..bfc4401
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/DomainListInfo.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/domain.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.DomainListInfo</code>
+ */
+class DomainListInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *域名详细信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.DomainList DomainList = 1;</code>
+     */
+    private $DomainList;
+    /**
+     *总记录数。
+     *
+     * Generated from protobuf field <code>int64 Total = 2;</code>
+     */
+    protected $Total = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Live\Models\Business\DomainList[]|\Google\Protobuf\Internal\RepeatedField $DomainList
+     *          域名详细信息列表
+     *     @type int|string $Total
+     *          总记录数。
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\Domain::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *域名详细信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.DomainList DomainList = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getDomainList()
+    {
+        return $this->DomainList;
+    }
+
+    /**
+     *域名详细信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.DomainList DomainList = 1;</code>
+     * @param \Volc\Service\Live\Models\Business\DomainList[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setDomainList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Live\Models\Business\DomainList::class);
+        $this->DomainList = $arr;
+
+        return $this;
+    }
+
+    /**
+     *总记录数。
+     *
+     * Generated from protobuf field <code>int64 Total = 2;</code>
+     * @return int|string
+     */
+    public function getTotal()
+    {
+        return $this->Total;
+    }
+
+    /**
+     *总记录数。
+     *
+     * Generated from protobuf field <code>int64 Total = 2;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setTotal($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->Total = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/ForbiddenStreamInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/ForbiddenStreamInfo.php
new file mode 100755
index 0000000..3c74e93
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/ForbiddenStreamInfo.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/stream_manage.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.ForbiddenStreamInfo</code>
+ */
+class ForbiddenStreamInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 禁推流的信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.ForbiddenStreamInfoList StreamInfoList = 1;</code>
+     */
+    private $StreamInfoList;
+    /**
+     * 流数量
+     *
+     * Generated from protobuf field <code>int64 RoughCount = 2;</code>
+     */
+    protected $RoughCount = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Live\Models\Business\ForbiddenStreamInfoList[]|\Google\Protobuf\Internal\RepeatedField $StreamInfoList
+     *           禁推流的信息列表
+     *     @type int|string $RoughCount
+     *           流数量
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\StreamManage::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 禁推流的信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.ForbiddenStreamInfoList StreamInfoList = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getStreamInfoList()
+    {
+        return $this->StreamInfoList;
+    }
+
+    /**
+     * 禁推流的信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.ForbiddenStreamInfoList StreamInfoList = 1;</code>
+     * @param \Volc\Service\Live\Models\Business\ForbiddenStreamInfoList[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setStreamInfoList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Live\Models\Business\ForbiddenStreamInfoList::class);
+        $this->StreamInfoList = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 流数量
+     *
+     * Generated from protobuf field <code>int64 RoughCount = 2;</code>
+     * @return int|string
+     */
+    public function getRoughCount()
+    {
+        return $this->RoughCount;
+    }
+
+    /**
+     * 流数量
+     *
+     * Generated from protobuf field <code>int64 RoughCount = 2;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setRoughCount($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->RoughCount = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/ForbiddenStreamInfoList.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/ForbiddenStreamInfoList.php
new file mode 100755
index 0000000..d5b40d1
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/ForbiddenStreamInfoList.php
@@ -0,0 +1,277 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/stream_manage.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.ForbiddenStreamInfoList</code>
+ */
+class ForbiddenStreamInfoList extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Vhost表示视频直播服务的配置空间
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     */
+    protected $Vhost = '';
+    /**
+     * 域名
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     */
+    protected $Domain = '';
+    /**
+     * 应用名称
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     */
+    protected $App = '';
+    /**
+     * 流名称
+     *
+     * Generated from protobuf field <code>string Stream = 4;</code>
+     */
+    protected $Stream = '';
+    /**
+     * 直播流开始时间
+     *
+     * Generated from protobuf field <code>string CreateTime = 5;</code>
+     */
+    protected $CreateTime = '';
+    /**
+     * 直播流结束时间
+     *
+     * Generated from protobuf field <code>string EndTime = 6;</code>
+     */
+    protected $EndTime = '';
+    /**
+     * 是否禁用。
+     *<li>true:禁用;
+     *<li>false:启用。
+     *
+     * Generated from protobuf field <code>string type = 7;</code>
+     */
+    protected $type = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vhost
+     *           Vhost表示视频直播服务的配置空间
+     *     @type string $Domain
+     *           域名
+     *     @type string $App
+     *           应用名称
+     *     @type string $Stream
+     *           流名称
+     *     @type string $CreateTime
+     *           直播流开始时间
+     *     @type string $EndTime
+     *           直播流结束时间
+     *     @type string $type
+     *           是否禁用。
+     *          <li>true:禁用;
+     *          <li>false:启用。
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\StreamManage::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Vhost表示视频直播服务的配置空间
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @return string
+     */
+    public function getVhost()
+    {
+        return $this->Vhost;
+    }
+
+    /**
+     * Vhost表示视频直播服务的配置空间
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVhost($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vhost = $var;
+
+        return $this;
+    }
+
+    /**
+     * 域名
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     * @return string
+     */
+    public function getDomain()
+    {
+        return $this->Domain;
+    }
+
+    /**
+     * 域名
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomain($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Domain = $var;
+
+        return $this;
+    }
+
+    /**
+     * 应用名称
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     * @return string
+     */
+    public function getApp()
+    {
+        return $this->App;
+    }
+
+    /**
+     * 应用名称
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setApp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->App = $var;
+
+        return $this;
+    }
+
+    /**
+     * 流名称
+     *
+     * Generated from protobuf field <code>string Stream = 4;</code>
+     * @return string
+     */
+    public function getStream()
+    {
+        return $this->Stream;
+    }
+
+    /**
+     * 流名称
+     *
+     * Generated from protobuf field <code>string Stream = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStream($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Stream = $var;
+
+        return $this;
+    }
+
+    /**
+     * 直播流开始时间
+     *
+     * Generated from protobuf field <code>string CreateTime = 5;</code>
+     * @return string
+     */
+    public function getCreateTime()
+    {
+        return $this->CreateTime;
+    }
+
+    /**
+     * 直播流开始时间
+     *
+     * Generated from protobuf field <code>string CreateTime = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCreateTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CreateTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * 直播流结束时间
+     *
+     * Generated from protobuf field <code>string EndTime = 6;</code>
+     * @return string
+     */
+    public function getEndTime()
+    {
+        return $this->EndTime;
+    }
+
+    /**
+     * 直播流结束时间
+     *
+     * Generated from protobuf field <code>string EndTime = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setEndTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->EndTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * 是否禁用。
+     *<li>true:禁用;
+     *<li>false:启用。
+     *
+     * Generated from protobuf field <code>string type = 7;</code>
+     * @return string
+     */
+    public function getType()
+    {
+        return $this->type;
+    }
+
+    /**
+     * 是否禁用。
+     *<li>true:禁用;
+     *<li>false:启用。
+     *
+     * Generated from protobuf field <code>string type = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->type = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/GeneratePlayURLResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/GeneratePlayURLResult.php
new file mode 100755
index 0000000..c273dc6
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/GeneratePlayURLResult.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/addr.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.GeneratePlayURLResult</code>
+ */
+class GeneratePlayURLResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *播放地址详情列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.PlayURL URLList = 1;</code>
+     */
+    private $URLList;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Live\Models\Business\PlayURL[]|\Google\Protobuf\Internal\RepeatedField $URLList
+     *          播放地址详情列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\Addr::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *播放地址详情列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.PlayURL URLList = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getURLList()
+    {
+        return $this->URLList;
+    }
+
+    /**
+     *播放地址详情列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.PlayURL URLList = 1;</code>
+     * @param \Volc\Service\Live\Models\Business\PlayURL[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setURLList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Live\Models\Business\PlayURL::class);
+        $this->URLList = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/GeneratePushURLResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/GeneratePushURLResult.php
new file mode 100755
index 0000000..f289353
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/GeneratePushURLResult.php
@@ -0,0 +1,201 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/addr.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.GeneratePushURLResult</code>
+ */
+class GeneratePushURLResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * rtmp 推流地址列表
+     *
+     * Generated from protobuf field <code>repeated string PushURLList = 1;</code>
+     */
+    private $PushURLList;
+    /**
+     * rtmp 推流地址列表详情,会针对OBS推流方式对地址进行分离
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.PushURLItem PushURLListDetail = 2;</code>
+     */
+    private $PushURLListDetail;
+    /**
+     * TS over SRT地址
+     *
+     * Generated from protobuf field <code>repeated string TsOverSrtURLList = 3;</code>
+     */
+    private $TsOverSrtURLList;
+    /**
+     * RTMP over SRT地址
+     *
+     * Generated from protobuf field <code>repeated string RtmpOverSrtURLList = 4;</code>
+     */
+    private $RtmpOverSrtURLList;
+    /**
+     * RTM推流地址
+     *
+     * Generated from protobuf field <code>repeated string RtmURLList = 5;</code>
+     */
+    private $RtmURLList;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $PushURLList
+     *           rtmp 推流地址列表
+     *     @type \Volc\Service\Live\Models\Business\PushURLItem[]|\Google\Protobuf\Internal\RepeatedField $PushURLListDetail
+     *           rtmp 推流地址列表详情,会针对OBS推流方式对地址进行分离
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $TsOverSrtURLList
+     *           TS over SRT地址
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $RtmpOverSrtURLList
+     *           RTMP over SRT地址
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $RtmURLList
+     *           RTM推流地址
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\Addr::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * rtmp 推流地址列表
+     *
+     * Generated from protobuf field <code>repeated string PushURLList = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getPushURLList()
+    {
+        return $this->PushURLList;
+    }
+
+    /**
+     * rtmp 推流地址列表
+     *
+     * Generated from protobuf field <code>repeated string PushURLList = 1;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setPushURLList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->PushURLList = $arr;
+
+        return $this;
+    }
+
+    /**
+     * rtmp 推流地址列表详情,会针对OBS推流方式对地址进行分离
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.PushURLItem PushURLListDetail = 2;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getPushURLListDetail()
+    {
+        return $this->PushURLListDetail;
+    }
+
+    /**
+     * rtmp 推流地址列表详情,会针对OBS推流方式对地址进行分离
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.PushURLItem PushURLListDetail = 2;</code>
+     * @param \Volc\Service\Live\Models\Business\PushURLItem[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setPushURLListDetail($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Live\Models\Business\PushURLItem::class);
+        $this->PushURLListDetail = $arr;
+
+        return $this;
+    }
+
+    /**
+     * TS over SRT地址
+     *
+     * Generated from protobuf field <code>repeated string TsOverSrtURLList = 3;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getTsOverSrtURLList()
+    {
+        return $this->TsOverSrtURLList;
+    }
+
+    /**
+     * TS over SRT地址
+     *
+     * Generated from protobuf field <code>repeated string TsOverSrtURLList = 3;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setTsOverSrtURLList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->TsOverSrtURLList = $arr;
+
+        return $this;
+    }
+
+    /**
+     * RTMP over SRT地址
+     *
+     * Generated from protobuf field <code>repeated string RtmpOverSrtURLList = 4;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getRtmpOverSrtURLList()
+    {
+        return $this->RtmpOverSrtURLList;
+    }
+
+    /**
+     * RTMP over SRT地址
+     *
+     * Generated from protobuf field <code>repeated string RtmpOverSrtURLList = 4;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setRtmpOverSrtURLList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->RtmpOverSrtURLList = $arr;
+
+        return $this;
+    }
+
+    /**
+     * RTM推流地址
+     *
+     * Generated from protobuf field <code>repeated string RtmURLList = 5;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getRtmURLList()
+    {
+        return $this->RtmURLList;
+    }
+
+    /**
+     * RTM推流地址
+     *
+     * Generated from protobuf field <code>repeated string RtmURLList = 5;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setRtmURLList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->RtmURLList = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/ListPullToPushTaskResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/ListPullToPushTaskResult.php
new file mode 100755
index 0000000..ef6f9f0
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/ListPullToPushTaskResult.php
@@ -0,0 +1,109 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/pull_to_push.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.ListPullToPushTaskResult</code>
+ */
+class ListPullToPushTaskResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *任务详情列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.TaskInfoItem List = 1;</code>
+     */
+    private $List;
+    /**
+     *页码信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.Pagination Pagination = 2;</code>
+     */
+    protected $Pagination = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Live\Models\Business\TaskInfoItem[]|\Google\Protobuf\Internal\RepeatedField $List
+     *          任务详情列表
+     *     @type \Volc\Service\Live\Models\Business\Pagination $Pagination
+     *          页码信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\PullToPush::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *任务详情列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.TaskInfoItem List = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getList()
+    {
+        return $this->List;
+    }
+
+    /**
+     *任务详情列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.TaskInfoItem List = 1;</code>
+     * @param \Volc\Service\Live\Models\Business\TaskInfoItem[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Live\Models\Business\TaskInfoItem::class);
+        $this->List = $arr;
+
+        return $this;
+    }
+
+    /**
+     *页码信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.Pagination Pagination = 2;</code>
+     * @return \Volc\Service\Live\Models\Business\Pagination|null
+     */
+    public function getPagination()
+    {
+        return $this->Pagination;
+    }
+
+    public function hasPagination()
+    {
+        return isset($this->Pagination);
+    }
+
+    public function clearPagination()
+    {
+        unset($this->Pagination);
+    }
+
+    /**
+     *页码信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.Pagination Pagination = 2;</code>
+     * @param \Volc\Service\Live\Models\Business\Pagination $var
+     * @return $this
+     */
+    public function setPagination($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Live\Models\Business\Pagination::class);
+        $this->Pagination = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/LiveStreamInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/LiveStreamInfo.php
new file mode 100755
index 0000000..ddb9ae1
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/LiveStreamInfo.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/stream_manage.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.LiveStreamInfo</code>
+ */
+class LiveStreamInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 流信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.StreamInfoList StreamInfoList = 1;</code>
+     */
+    private $StreamInfoList;
+    /**
+     * 流数量
+     *
+     * Generated from protobuf field <code>int64 RoughCount = 2;</code>
+     */
+    protected $RoughCount = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Live\Models\Business\StreamInfoList[]|\Google\Protobuf\Internal\RepeatedField $StreamInfoList
+     *           流信息列表
+     *     @type int|string $RoughCount
+     *           流数量
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\StreamManage::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 流信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.StreamInfoList StreamInfoList = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getStreamInfoList()
+    {
+        return $this->StreamInfoList;
+    }
+
+    /**
+     * 流信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.StreamInfoList StreamInfoList = 1;</code>
+     * @param \Volc\Service\Live\Models\Business\StreamInfoList[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setStreamInfoList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Live\Models\Business\StreamInfoList::class);
+        $this->StreamInfoList = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 流数量
+     *
+     * Generated from protobuf field <code>int64 RoughCount = 2;</code>
+     * @return int|string
+     */
+    public function getRoughCount()
+    {
+        return $this->RoughCount;
+    }
+
+    /**
+     * 流数量
+     *
+     * Generated from protobuf field <code>int64 RoughCount = 2;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setRoughCount($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->RoughCount = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/Pagination.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/Pagination.php
new file mode 100755
index 0000000..00a5243
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/Pagination.php
@@ -0,0 +1,167 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/snapshot_manage.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.Pagination</code>
+ */
+class Pagination extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 当前页
+     *
+     * Generated from protobuf field <code>int64 PageCur = 1;</code>
+     */
+    protected $PageCur = 0;
+    /**
+     * 当前页的大小
+     *
+     * Generated from protobuf field <code>int64 PageSize = 2;</code>
+     */
+    protected $PageSize = 0;
+    /**
+     * 当前页的数据量
+     *
+     * Generated from protobuf field <code>int64 PageTotal = 3;</code>
+     */
+    protected $PageTotal = 0;
+    /**
+     * 数据总量
+     *
+     * Generated from protobuf field <code>int64 TotalCount = 4;</code>
+     */
+    protected $TotalCount = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int|string $PageCur
+     *           当前页
+     *     @type int|string $PageSize
+     *           当前页的大小
+     *     @type int|string $PageTotal
+     *           当前页的数据量
+     *     @type int|string $TotalCount
+     *           数据总量
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\SnapshotManage::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 当前页
+     *
+     * Generated from protobuf field <code>int64 PageCur = 1;</code>
+     * @return int|string
+     */
+    public function getPageCur()
+    {
+        return $this->PageCur;
+    }
+
+    /**
+     * 当前页
+     *
+     * Generated from protobuf field <code>int64 PageCur = 1;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setPageCur($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->PageCur = $var;
+
+        return $this;
+    }
+
+    /**
+     * 当前页的大小
+     *
+     * Generated from protobuf field <code>int64 PageSize = 2;</code>
+     * @return int|string
+     */
+    public function getPageSize()
+    {
+        return $this->PageSize;
+    }
+
+    /**
+     * 当前页的大小
+     *
+     * Generated from protobuf field <code>int64 PageSize = 2;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setPageSize($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->PageSize = $var;
+
+        return $this;
+    }
+
+    /**
+     * 当前页的数据量
+     *
+     * Generated from protobuf field <code>int64 PageTotal = 3;</code>
+     * @return int|string
+     */
+    public function getPageTotal()
+    {
+        return $this->PageTotal;
+    }
+
+    /**
+     * 当前页的数据量
+     *
+     * Generated from protobuf field <code>int64 PageTotal = 3;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setPageTotal($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->PageTotal = $var;
+
+        return $this;
+    }
+
+    /**
+     * 数据总量
+     *
+     * Generated from protobuf field <code>int64 TotalCount = 4;</code>
+     * @return int|string
+     */
+    public function getTotalCount()
+    {
+        return $this->TotalCount;
+    }
+
+    /**
+     * 数据总量
+     *
+     * Generated from protobuf field <code>int64 TotalCount = 4;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setTotalCount($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->TotalCount = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/PlayURL.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/PlayURL.php
new file mode 100755
index 0000000..c47a357
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/PlayURL.php
@@ -0,0 +1,167 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/addr.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.PlayURL</code>
+ */
+class PlayURL extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *播放地址
+     *
+     * Generated from protobuf field <code>string URL = 1;</code>
+     */
+    protected $URL = '';
+    /**
+     * CDN类型:fcdn、hw、ws、ali
+     *
+     * Generated from protobuf field <code>string CDN = 2;</code>
+     */
+    protected $CDN = '';
+    /**
+     *协议类型:hls、rtmp、flv、udp
+     *
+     * Generated from protobuf field <code>string Protocol = 3;</code>
+     */
+    protected $Protocol = '';
+    /**
+     *地址类型,pull、push、3rd_play(relay_sink)、3rd_play(relay_source)
+     *
+     * Generated from protobuf field <code>string Type = 4;</code>
+     */
+    protected $Type = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $URL
+     *          播放地址
+     *     @type string $CDN
+     *           CDN类型:fcdn、hw、ws、ali
+     *     @type string $Protocol
+     *          协议类型:hls、rtmp、flv、udp
+     *     @type string $Type
+     *          地址类型,pull、push、3rd_play(relay_sink)、3rd_play(relay_source)
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\Addr::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *播放地址
+     *
+     * Generated from protobuf field <code>string URL = 1;</code>
+     * @return string
+     */
+    public function getURL()
+    {
+        return $this->URL;
+    }
+
+    /**
+     *播放地址
+     *
+     * Generated from protobuf field <code>string URL = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setURL($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->URL = $var;
+
+        return $this;
+    }
+
+    /**
+     * CDN类型:fcdn、hw、ws、ali
+     *
+     * Generated from protobuf field <code>string CDN = 2;</code>
+     * @return string
+     */
+    public function getCDN()
+    {
+        return $this->CDN;
+    }
+
+    /**
+     * CDN类型:fcdn、hw、ws、ali
+     *
+     * Generated from protobuf field <code>string CDN = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCDN($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CDN = $var;
+
+        return $this;
+    }
+
+    /**
+     *协议类型:hls、rtmp、flv、udp
+     *
+     * Generated from protobuf field <code>string Protocol = 3;</code>
+     * @return string
+     */
+    public function getProtocol()
+    {
+        return $this->Protocol;
+    }
+
+    /**
+     *协议类型:hls、rtmp、flv、udp
+     *
+     * Generated from protobuf field <code>string Protocol = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setProtocol($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Protocol = $var;
+
+        return $this;
+    }
+
+    /**
+     *地址类型,pull、push、3rd_play(relay_sink)、3rd_play(relay_source)
+     *
+     * Generated from protobuf field <code>string Type = 4;</code>
+     * @return string
+     */
+    public function getType()
+    {
+        return $this->Type;
+    }
+
+    /**
+     *地址类型,pull、push、3rd_play(relay_sink)、3rd_play(relay_source)
+     *
+     * Generated from protobuf field <code>string Type = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Type = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/PushURLItem.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/PushURLItem.php
new file mode 100755
index 0000000..2359588
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/PushURLItem.php
@@ -0,0 +1,133 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/addr.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.PushURLItem</code>
+ */
+class PushURLItem extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *推流地址
+     *
+     * Generated from protobuf field <code>string URL = 1;</code>
+     */
+    protected $URL = '';
+    /**
+     * OBS推流服务器地址
+     *
+     * Generated from protobuf field <code>string DomainApp = 2;</code>
+     */
+    protected $DomainApp = '';
+    /**
+     * OBS串流密钥
+     *
+     * Generated from protobuf field <code>string StreamSign = 3;</code>
+     */
+    protected $StreamSign = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $URL
+     *          推流地址
+     *     @type string $DomainApp
+     *           OBS推流服务器地址
+     *     @type string $StreamSign
+     *           OBS串流密钥
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\Addr::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *推流地址
+     *
+     * Generated from protobuf field <code>string URL = 1;</code>
+     * @return string
+     */
+    public function getURL()
+    {
+        return $this->URL;
+    }
+
+    /**
+     *推流地址
+     *
+     * Generated from protobuf field <code>string URL = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setURL($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->URL = $var;
+
+        return $this;
+    }
+
+    /**
+     * OBS推流服务器地址
+     *
+     * Generated from protobuf field <code>string DomainApp = 2;</code>
+     * @return string
+     */
+    public function getDomainApp()
+    {
+        return $this->DomainApp;
+    }
+
+    /**
+     * OBS推流服务器地址
+     *
+     * Generated from protobuf field <code>string DomainApp = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomainApp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->DomainApp = $var;
+
+        return $this;
+    }
+
+    /**
+     * OBS串流密钥
+     *
+     * Generated from protobuf field <code>string StreamSign = 3;</code>
+     * @return string
+     */
+    public function getStreamSign()
+    {
+        return $this->StreamSign;
+    }
+
+    /**
+     * OBS串流密钥
+     *
+     * Generated from protobuf field <code>string StreamSign = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStreamSign($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StreamSign = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/RecordHistoryInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/RecordHistoryInfo.php
new file mode 100755
index 0000000..ac18e63
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/RecordHistoryInfo.php
@@ -0,0 +1,109 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/record_manage.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.RecordHistoryInfo</code>
+ */
+class RecordHistoryInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 截图历史列表记录
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.RecordTaskFile Data = 1;</code>
+     */
+    private $Data;
+    /**
+     * 分页信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.Pagination Pagination = 2;</code>
+     */
+    protected $Pagination = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Live\Models\Business\RecordTaskFile[]|\Google\Protobuf\Internal\RepeatedField $Data
+     *           截图历史列表记录
+     *     @type \Volc\Service\Live\Models\Business\Pagination $Pagination
+     *           分页信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RecordManage::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 截图历史列表记录
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.RecordTaskFile Data = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getData()
+    {
+        return $this->Data;
+    }
+
+    /**
+     * 截图历史列表记录
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.RecordTaskFile Data = 1;</code>
+     * @param \Volc\Service\Live\Models\Business\RecordTaskFile[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setData($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Live\Models\Business\RecordTaskFile::class);
+        $this->Data = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 分页信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.Pagination Pagination = 2;</code>
+     * @return \Volc\Service\Live\Models\Business\Pagination|null
+     */
+    public function getPagination()
+    {
+        return $this->Pagination;
+    }
+
+    public function hasPagination()
+    {
+        return isset($this->Pagination);
+    }
+
+    public function clearPagination()
+    {
+        unset($this->Pagination);
+    }
+
+    /**
+     * 分页信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.Pagination Pagination = 2;</code>
+     * @param \Volc\Service\Live\Models\Business\Pagination $var
+     * @return $this
+     */
+    public function setPagination($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Live\Models\Business\Pagination::class);
+        $this->Pagination = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/RecordTaskFile.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/RecordTaskFile.php
new file mode 100755
index 0000000..7e18ece
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/RecordTaskFile.php
@@ -0,0 +1,398 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/record_manage.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.RecordTaskFile</code>
+ */
+class RecordTaskFile extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     */
+    protected $Vid = '';
+    /**
+     * 域名空间名称。由 1 到 60 位数字、字母、下划线及"-"和"."组成
+     *
+     * Generated from protobuf field <code>string Vhost = 2;</code>
+     */
+    protected $Vhost = '';
+    /**
+     * 应用名称
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     */
+    protected $App = '';
+    /**
+     * 流名称
+     *
+     * Generated from protobuf field <code>string Stream = 4;</code>
+     */
+    protected $Stream = '';
+    /**
+     * 对象存储 bucket 名称
+     *
+     * Generated from protobuf field <code>string Bucket = 5;</code>
+     */
+    protected $Bucket = '';
+    /**
+     * ToS 中的保存路径
+     *
+     * Generated from protobuf field <code>string Path = 6;</code>
+     */
+    protected $Path = '';
+    /**
+     *录制时长
+     *
+     * Generated from protobuf field <code>string Duration = 7;</code>
+     */
+    protected $Duration = '';
+    /**
+     * 开始推流时间
+     *
+     * Generated from protobuf field <code>string StartTime = 8;</code>
+     */
+    protected $StartTime = '';
+    /**
+     * 录制文件存储格式
+     *
+     * Generated from protobuf field <code>string Format = 9;</code>
+     */
+    protected $Format = '';
+    /**
+     * 结束推流时间
+     *
+     * Generated from protobuf field <code>string EndTime = 10;</code>
+     */
+    protected $EndTime = '';
+    /**
+     * 录制文件的文件名
+     *
+     * Generated from protobuf field <code>string FileName = 11;</code>
+     */
+    protected $FileName = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vid
+     *     @type string $Vhost
+     *           域名空间名称。由 1 到 60 位数字、字母、下划线及"-"和"."组成
+     *     @type string $App
+     *           应用名称
+     *     @type string $Stream
+     *           流名称
+     *     @type string $Bucket
+     *           对象存储 bucket 名称
+     *     @type string $Path
+     *           ToS 中的保存路径
+     *     @type string $Duration
+     *          录制时长
+     *     @type string $StartTime
+     *           开始推流时间
+     *     @type string $Format
+     *           录制文件存储格式
+     *     @type string $EndTime
+     *           结束推流时间
+     *     @type string $FileName
+     *           录制文件的文件名
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RecordManage::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     * 域名空间名称。由 1 到 60 位数字、字母、下划线及"-"和"."组成
+     *
+     * Generated from protobuf field <code>string Vhost = 2;</code>
+     * @return string
+     */
+    public function getVhost()
+    {
+        return $this->Vhost;
+    }
+
+    /**
+     * 域名空间名称。由 1 到 60 位数字、字母、下划线及"-"和"."组成
+     *
+     * Generated from protobuf field <code>string Vhost = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVhost($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vhost = $var;
+
+        return $this;
+    }
+
+    /**
+     * 应用名称
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     * @return string
+     */
+    public function getApp()
+    {
+        return $this->App;
+    }
+
+    /**
+     * 应用名称
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setApp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->App = $var;
+
+        return $this;
+    }
+
+    /**
+     * 流名称
+     *
+     * Generated from protobuf field <code>string Stream = 4;</code>
+     * @return string
+     */
+    public function getStream()
+    {
+        return $this->Stream;
+    }
+
+    /**
+     * 流名称
+     *
+     * Generated from protobuf field <code>string Stream = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStream($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Stream = $var;
+
+        return $this;
+    }
+
+    /**
+     * 对象存储 bucket 名称
+     *
+     * Generated from protobuf field <code>string Bucket = 5;</code>
+     * @return string
+     */
+    public function getBucket()
+    {
+        return $this->Bucket;
+    }
+
+    /**
+     * 对象存储 bucket 名称
+     *
+     * Generated from protobuf field <code>string Bucket = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setBucket($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Bucket = $var;
+
+        return $this;
+    }
+
+    /**
+     * ToS 中的保存路径
+     *
+     * Generated from protobuf field <code>string Path = 6;</code>
+     * @return string
+     */
+    public function getPath()
+    {
+        return $this->Path;
+    }
+
+    /**
+     * ToS 中的保存路径
+     *
+     * Generated from protobuf field <code>string Path = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setPath($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Path = $var;
+
+        return $this;
+    }
+
+    /**
+     *录制时长
+     *
+     * Generated from protobuf field <code>string Duration = 7;</code>
+     * @return string
+     */
+    public function getDuration()
+    {
+        return $this->Duration;
+    }
+
+    /**
+     *录制时长
+     *
+     * Generated from protobuf field <code>string Duration = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDuration($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Duration = $var;
+
+        return $this;
+    }
+
+    /**
+     * 开始推流时间
+     *
+     * Generated from protobuf field <code>string StartTime = 8;</code>
+     * @return string
+     */
+    public function getStartTime()
+    {
+        return $this->StartTime;
+    }
+
+    /**
+     * 开始推流时间
+     *
+     * Generated from protobuf field <code>string StartTime = 8;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStartTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StartTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * 录制文件存储格式
+     *
+     * Generated from protobuf field <code>string Format = 9;</code>
+     * @return string
+     */
+    public function getFormat()
+    {
+        return $this->Format;
+    }
+
+    /**
+     * 录制文件存储格式
+     *
+     * Generated from protobuf field <code>string Format = 9;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFormat($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Format = $var;
+
+        return $this;
+    }
+
+    /**
+     * 结束推流时间
+     *
+     * Generated from protobuf field <code>string EndTime = 10;</code>
+     * @return string
+     */
+    public function getEndTime()
+    {
+        return $this->EndTime;
+    }
+
+    /**
+     * 结束推流时间
+     *
+     * Generated from protobuf field <code>string EndTime = 10;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setEndTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->EndTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * 录制文件的文件名
+     *
+     * Generated from protobuf field <code>string FileName = 11;</code>
+     * @return string
+     */
+    public function getFileName()
+    {
+        return $this->FileName;
+    }
+
+    /**
+     * 录制文件的文件名
+     *
+     * Generated from protobuf field <code>string FileName = 11;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileName = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/RelaySourceConfig.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/RelaySourceConfig.php
new file mode 100755
index 0000000..7414fa7
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/RelaySourceConfig.php
@@ -0,0 +1,133 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/relay_source.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.RelaySourceConfig</code>
+ */
+class RelaySourceConfig extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 对应的 Vhost。
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     */
+    protected $Vhost = '';
+    /**
+     * 应用名称。
+     *
+     * Generated from protobuf field <code>string App = 2;</code>
+     */
+    protected $App = '';
+    /**
+     * 分组列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.RelaySourceGroupItemV2 GroupList = 3;</code>
+     */
+    private $GroupList;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vhost
+     *           对应的 Vhost。
+     *     @type string $App
+     *           应用名称。
+     *     @type \Volc\Service\Live\Models\Business\RelaySourceGroupItemV2[]|\Google\Protobuf\Internal\RepeatedField $GroupList
+     *           分组列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RelaySource::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 对应的 Vhost。
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @return string
+     */
+    public function getVhost()
+    {
+        return $this->Vhost;
+    }
+
+    /**
+     * 对应的 Vhost。
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVhost($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vhost = $var;
+
+        return $this;
+    }
+
+    /**
+     * 应用名称。
+     *
+     * Generated from protobuf field <code>string App = 2;</code>
+     * @return string
+     */
+    public function getApp()
+    {
+        return $this->App;
+    }
+
+    /**
+     * 应用名称。
+     *
+     * Generated from protobuf field <code>string App = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setApp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->App = $var;
+
+        return $this;
+    }
+
+    /**
+     * 分组列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.RelaySourceGroupItemV2 GroupList = 3;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getGroupList()
+    {
+        return $this->GroupList;
+    }
+
+    /**
+     * 分组列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.RelaySourceGroupItemV2 GroupList = 3;</code>
+     * @param \Volc\Service\Live\Models\Business\RelaySourceGroupItemV2[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setGroupList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Live\Models\Business\RelaySourceGroupItemV2::class);
+        $this->GroupList = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/RelaySourceConfigList.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/RelaySourceConfigList.php
new file mode 100755
index 0000000..a3ecbbc
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/RelaySourceConfigList.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/relay_source.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.RelaySourceConfigList</code>
+ */
+class RelaySourceConfigList extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 配置列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.RelaySourceConfig RelaySourceConfigList = 1;</code>
+     */
+    private $RelaySourceConfigList;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Live\Models\Business\RelaySourceConfig[]|\Google\Protobuf\Internal\RepeatedField $RelaySourceConfigList
+     *           配置列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RelaySource::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 配置列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.RelaySourceConfig RelaySourceConfigList = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getRelaySourceConfigList()
+    {
+        return $this->RelaySourceConfigList;
+    }
+
+    /**
+     * 配置列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.RelaySourceConfig RelaySourceConfigList = 1;</code>
+     * @param \Volc\Service\Live\Models\Business\RelaySourceConfig[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setRelaySourceConfigList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Live\Models\Business\RelaySourceConfig::class);
+        $this->RelaySourceConfigList = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/RelaySourceGroupItemV2.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/RelaySourceGroupItemV2.php
new file mode 100755
index 0000000..232e275
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/RelaySourceGroupItemV2.php
@@ -0,0 +1,133 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/relay_source.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.RelaySourceGroupItemV2</code>
+ */
+class RelaySourceGroupItemV2 extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 回源地址数组
+     *
+     * Generated from protobuf field <code>repeated string RelaySourceDomainList = 1;</code>
+     */
+    private $RelaySourceDomainList;
+    /**
+     * 回源参数。
+     *
+     * Generated from protobuf field <code>map<string, string> RelaySourceParams = 2;</code>
+     */
+    private $RelaySourceParams;
+    /**
+     * 回源协议,目前只支持以下两种:<li>rmtp <li>flv。
+     *
+     * Generated from protobuf field <code>string RelaySourceProtocol = 3;</code>
+     */
+    protected $RelaySourceProtocol = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $RelaySourceDomainList
+     *           回源地址数组
+     *     @type array|\Google\Protobuf\Internal\MapField $RelaySourceParams
+     *           回源参数。
+     *     @type string $RelaySourceProtocol
+     *           回源协议,目前只支持以下两种:<li>rmtp <li>flv。
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RelaySource::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 回源地址数组
+     *
+     * Generated from protobuf field <code>repeated string RelaySourceDomainList = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getRelaySourceDomainList()
+    {
+        return $this->RelaySourceDomainList;
+    }
+
+    /**
+     * 回源地址数组
+     *
+     * Generated from protobuf field <code>repeated string RelaySourceDomainList = 1;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setRelaySourceDomainList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->RelaySourceDomainList = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 回源参数。
+     *
+     * Generated from protobuf field <code>map<string, string> RelaySourceParams = 2;</code>
+     * @return \Google\Protobuf\Internal\MapField
+     */
+    public function getRelaySourceParams()
+    {
+        return $this->RelaySourceParams;
+    }
+
+    /**
+     * 回源参数。
+     *
+     * Generated from protobuf field <code>map<string, string> RelaySourceParams = 2;</code>
+     * @param array|\Google\Protobuf\Internal\MapField $var
+     * @return $this
+     */
+    public function setRelaySourceParams($var)
+    {
+        $arr = GPBUtil::checkMapField($var, \Google\Protobuf\Internal\GPBType::STRING, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->RelaySourceParams = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 回源协议,目前只支持以下两种:<li>rmtp <li>flv。
+     *
+     * Generated from protobuf field <code>string RelaySourceProtocol = 3;</code>
+     * @return string
+     */
+    public function getRelaySourceProtocol()
+    {
+        return $this->RelaySourceProtocol;
+    }
+
+    /**
+     * 回源协议,目前只支持以下两种:<li>rmtp <li>flv。
+     *
+     * Generated from protobuf field <code>string RelaySourceProtocol = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setRelaySourceProtocol($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->RelaySourceProtocol = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/ScoreInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/ScoreInfo.php
new file mode 100755
index 0000000..e9de954
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/ScoreInfo.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/VQScore.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.ScoreInfo</code>
+ */
+class ScoreInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *测评取值时间点
+     *
+     * Generated from protobuf field <code>string PointTime = 1;</code>
+     */
+    protected $PointTime = '';
+    /**
+     *测评得分
+     *
+     * Generated from protobuf field <code>float Score = 2;</code>
+     */
+    protected $Score = 0.0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $PointTime
+     *          测评取值时间点
+     *     @type float $Score
+     *          测评得分
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\VQScore::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *测评取值时间点
+     *
+     * Generated from protobuf field <code>string PointTime = 1;</code>
+     * @return string
+     */
+    public function getPointTime()
+    {
+        return $this->PointTime;
+    }
+
+    /**
+     *测评取值时间点
+     *
+     * Generated from protobuf field <code>string PointTime = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setPointTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->PointTime = $var;
+
+        return $this;
+    }
+
+    /**
+     *测评得分
+     *
+     * Generated from protobuf field <code>float Score = 2;</code>
+     * @return float
+     */
+    public function getScore()
+    {
+        return $this->Score;
+    }
+
+    /**
+     *测评得分
+     *
+     * Generated from protobuf field <code>float Score = 2;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setScore($var)
+    {
+        GPBUtil::checkFloat($var);
+        $this->Score = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/StreamInfoList.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/StreamInfoList.php
new file mode 100755
index 0000000..b8dd5c9
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/StreamInfoList.php
@@ -0,0 +1,405 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/stream_manage.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.StreamInfoList</code>
+ */
+class StreamInfoList extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *流ID
+     *
+     * Generated from protobuf field <code>int64 ID = 1;</code>
+     */
+    protected $ID = 0;
+    /**
+     * 域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 2;</code>
+     */
+    protected $Vhost = '';
+    /**
+     * 域名
+     *
+     * Generated from protobuf field <code>string Domain = 3;</code>
+     */
+    protected $Domain = '';
+    /**
+     * 应用名称
+     *
+     * Generated from protobuf field <code>string App = 4;</code>
+     */
+    protected $App = '';
+    /**
+     * 流名称
+     *
+     * Generated from protobuf field <code>string Stream = 5;</code>
+     */
+    protected $Stream = '';
+    /**
+     * 开始推流时间
+     *
+     * Generated from protobuf field <code>string SessionStartTime = 6;</code>
+     */
+    protected $SessionStartTime = '';
+    /**
+     * 在线人数
+     *
+     * Generated from protobuf field <code>int64 OnlineUser = 7;</code>
+     */
+    protected $OnlineUser = 0;
+    /**
+     * 带宽
+     *
+     * Generated from protobuf field <code>int64 BandWidth = 8;</code>
+     */
+    protected $BandWidth = 0;
+    /**
+     * 码率
+     *
+     * Generated from protobuf field <code>int64 Bitrate = 9;</code>
+     */
+    protected $Bitrate = 0;
+    /**
+     * 帧率
+     *
+     * Generated from protobuf field <code>int64 Framerate = 10;</code>
+     */
+    protected $Framerate = 0;
+    /**
+     * 预览地址
+     *
+     * Generated from protobuf field <code>string PreviewURL = 11;</code>
+     */
+    protected $PreviewURL = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int|string $ID
+     *          流ID
+     *     @type string $Vhost
+     *           域名空间名称
+     *     @type string $Domain
+     *           域名
+     *     @type string $App
+     *           应用名称
+     *     @type string $Stream
+     *           流名称
+     *     @type string $SessionStartTime
+     *           开始推流时间
+     *     @type int|string $OnlineUser
+     *           在线人数
+     *     @type int|string $BandWidth
+     *           带宽
+     *     @type int|string $Bitrate
+     *           码率
+     *     @type int|string $Framerate
+     *           帧率
+     *     @type string $PreviewURL
+     *           预览地址
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\StreamManage::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *流ID
+     *
+     * Generated from protobuf field <code>int64 ID = 1;</code>
+     * @return int|string
+     */
+    public function getID()
+    {
+        return $this->ID;
+    }
+
+    /**
+     *流ID
+     *
+     * Generated from protobuf field <code>int64 ID = 1;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setID($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->ID = $var;
+
+        return $this;
+    }
+
+    /**
+     * 域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 2;</code>
+     * @return string
+     */
+    public function getVhost()
+    {
+        return $this->Vhost;
+    }
+
+    /**
+     * 域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVhost($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vhost = $var;
+
+        return $this;
+    }
+
+    /**
+     * 域名
+     *
+     * Generated from protobuf field <code>string Domain = 3;</code>
+     * @return string
+     */
+    public function getDomain()
+    {
+        return $this->Domain;
+    }
+
+    /**
+     * 域名
+     *
+     * Generated from protobuf field <code>string Domain = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomain($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Domain = $var;
+
+        return $this;
+    }
+
+    /**
+     * 应用名称
+     *
+     * Generated from protobuf field <code>string App = 4;</code>
+     * @return string
+     */
+    public function getApp()
+    {
+        return $this->App;
+    }
+
+    /**
+     * 应用名称
+     *
+     * Generated from protobuf field <code>string App = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setApp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->App = $var;
+
+        return $this;
+    }
+
+    /**
+     * 流名称
+     *
+     * Generated from protobuf field <code>string Stream = 5;</code>
+     * @return string
+     */
+    public function getStream()
+    {
+        return $this->Stream;
+    }
+
+    /**
+     * 流名称
+     *
+     * Generated from protobuf field <code>string Stream = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStream($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Stream = $var;
+
+        return $this;
+    }
+
+    /**
+     * 开始推流时间
+     *
+     * Generated from protobuf field <code>string SessionStartTime = 6;</code>
+     * @return string
+     */
+    public function getSessionStartTime()
+    {
+        return $this->SessionStartTime;
+    }
+
+    /**
+     * 开始推流时间
+     *
+     * Generated from protobuf field <code>string SessionStartTime = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSessionStartTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SessionStartTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * 在线人数
+     *
+     * Generated from protobuf field <code>int64 OnlineUser = 7;</code>
+     * @return int|string
+     */
+    public function getOnlineUser()
+    {
+        return $this->OnlineUser;
+    }
+
+    /**
+     * 在线人数
+     *
+     * Generated from protobuf field <code>int64 OnlineUser = 7;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setOnlineUser($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->OnlineUser = $var;
+
+        return $this;
+    }
+
+    /**
+     * 带宽
+     *
+     * Generated from protobuf field <code>int64 BandWidth = 8;</code>
+     * @return int|string
+     */
+    public function getBandWidth()
+    {
+        return $this->BandWidth;
+    }
+
+    /**
+     * 带宽
+     *
+     * Generated from protobuf field <code>int64 BandWidth = 8;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setBandWidth($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->BandWidth = $var;
+
+        return $this;
+    }
+
+    /**
+     * 码率
+     *
+     * Generated from protobuf field <code>int64 Bitrate = 9;</code>
+     * @return int|string
+     */
+    public function getBitrate()
+    {
+        return $this->Bitrate;
+    }
+
+    /**
+     * 码率
+     *
+     * Generated from protobuf field <code>int64 Bitrate = 9;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setBitrate($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->Bitrate = $var;
+
+        return $this;
+    }
+
+    /**
+     * 帧率
+     *
+     * Generated from protobuf field <code>int64 Framerate = 10;</code>
+     * @return int|string
+     */
+    public function getFramerate()
+    {
+        return $this->Framerate;
+    }
+
+    /**
+     * 帧率
+     *
+     * Generated from protobuf field <code>int64 Framerate = 10;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setFramerate($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->Framerate = $var;
+
+        return $this;
+    }
+
+    /**
+     * 预览地址
+     *
+     * Generated from protobuf field <code>string PreviewURL = 11;</code>
+     * @return string
+     */
+    public function getPreviewURL()
+    {
+        return $this->PreviewURL;
+    }
+
+    /**
+     * 预览地址
+     *
+     * Generated from protobuf field <code>string PreviewURL = 11;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setPreviewURL($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->PreviewURL = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/StreamStateInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/StreamStateInfo.php
new file mode 100755
index 0000000..f28b9b0
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/StreamStateInfo.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/stream_manage.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.StreamStateInfo</code>
+ */
+class StreamStateInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 直播流状态。
+     *<li>online:在线流;
+     *<li>offline:历史流;
+     *<li>forbidden:禁推流。
+     *
+     * Generated from protobuf field <code>string stream_state = 1;</code>
+     */
+    protected $stream_state = '';
+    /**
+     * 直播流类型。
+     *<li>push:直推直拉;
+     *<li>pull:回源拉流。
+     *
+     * Generated from protobuf field <code>string type = 2;</code>
+     */
+    protected $type = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $stream_state
+     *           直播流状态。
+     *          <li>online:在线流;
+     *          <li>offline:历史流;
+     *          <li>forbidden:禁推流。
+     *     @type string $type
+     *           直播流类型。
+     *          <li>push:直推直拉;
+     *          <li>pull:回源拉流。
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\StreamManage::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 直播流状态。
+     *<li>online:在线流;
+     *<li>offline:历史流;
+     *<li>forbidden:禁推流。
+     *
+     * Generated from protobuf field <code>string stream_state = 1;</code>
+     * @return string
+     */
+    public function getStreamState()
+    {
+        return $this->stream_state;
+    }
+
+    /**
+     * 直播流状态。
+     *<li>online:在线流;
+     *<li>offline:历史流;
+     *<li>forbidden:禁推流。
+     *
+     * Generated from protobuf field <code>string stream_state = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStreamState($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->stream_state = $var;
+
+        return $this;
+    }
+
+    /**
+     * 直播流类型。
+     *<li>push:直推直拉;
+     *<li>pull:回源拉流。
+     *
+     * Generated from protobuf field <code>string type = 2;</code>
+     * @return string
+     */
+    public function getType()
+    {
+        return $this->type;
+    }
+
+    /**
+     * 直播流类型。
+     *<li>push:直推直拉;
+     *<li>pull:回源拉流。
+     *
+     * Generated from protobuf field <code>string type = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->type = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/TaskInfoItem.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/TaskInfoItem.php
new file mode 100755
index 0000000..e406843
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/TaskInfoItem.php
@@ -0,0 +1,439 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/pull_to_push.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.TaskInfoItem</code>
+ */
+class TaskInfoItem extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *任务标题
+     *
+     * Generated from protobuf field <code>string Title = 1;</code>
+     */
+    protected $Title = '';
+    /**
+     *任务ID
+     *
+     * Generated from protobuf field <code>string TaskId = 2;</code>
+     */
+    protected $TaskId = '';
+    /**
+     *任务开始时间,UTC时间
+     *
+     * Generated from protobuf field <code>string StartTime = 3;</code>
+     */
+    protected $StartTime = '';
+    /**
+     *任务结束时间,UTC时间
+     *
+     * Generated from protobuf field <code>string EndTime = 4;</code>
+     */
+    protected $EndTime = '';
+    /**
+     *任务回调地址
+     *
+     * Generated from protobuf field <code>string CallbackURL = 5;</code>
+     */
+    protected $CallbackURL = '';
+    /**
+     *拉流转推类型,0:直播,1:点播
+     *
+     * Generated from protobuf field <code>int32 Type = 6;</code>
+     */
+    protected $Type = 0;
+    /**
+     *点播时,拉流地址的循环模式,Type=1时必选,-1:顺序循环
+     *
+     * Generated from protobuf field <code>int32 CycleMode = 7;</code>
+     */
+    protected $CycleMode = 0;
+    /**
+     *目标推流地址
+     *
+     * Generated from protobuf field <code>string DstAddr = 8;</code>
+     */
+    protected $DstAddr = '';
+    /**
+     *直播拉流地址
+     *
+     * Generated from protobuf field <code>string SrcAddr = 9;</code>
+     */
+    protected $SrcAddr = '';
+    /**
+     *点播拉流地址列表
+     *
+     * Generated from protobuf field <code>repeated string SrcAddrS = 10;</code>
+     */
+    private $SrcAddrS;
+    /**
+     *任务状态:停用、未开始、生效中、已结束
+     *
+     * Generated from protobuf field <code>string Status = 11;</code>
+     */
+    protected $Status = '';
+    /**
+     *任务状态:0:停用,1:未开始,2:生效中,3:已结束
+     *
+     * Generated from protobuf field <code>int32 TaskStatus = 12;</code>
+     */
+    protected $TaskStatus = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Title
+     *          任务标题
+     *     @type string $TaskId
+     *          任务ID
+     *     @type string $StartTime
+     *          任务开始时间,UTC时间
+     *     @type string $EndTime
+     *          任务结束时间,UTC时间
+     *     @type string $CallbackURL
+     *          任务回调地址
+     *     @type int $Type
+     *          拉流转推类型,0:直播,1:点播
+     *     @type int $CycleMode
+     *          点播时,拉流地址的循环模式,Type=1时必选,-1:顺序循环
+     *     @type string $DstAddr
+     *          目标推流地址
+     *     @type string $SrcAddr
+     *          直播拉流地址
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $SrcAddrS
+     *          点播拉流地址列表
+     *     @type string $Status
+     *          任务状态:停用、未开始、生效中、已结束
+     *     @type int $TaskStatus
+     *          任务状态:0:停用,1:未开始,2:生效中,3:已结束
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\PullToPush::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *任务标题
+     *
+     * Generated from protobuf field <code>string Title = 1;</code>
+     * @return string
+     */
+    public function getTitle()
+    {
+        return $this->Title;
+    }
+
+    /**
+     *任务标题
+     *
+     * Generated from protobuf field <code>string Title = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTitle($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Title = $var;
+
+        return $this;
+    }
+
+    /**
+     *任务ID
+     *
+     * Generated from protobuf field <code>string TaskId = 2;</code>
+     * @return string
+     */
+    public function getTaskId()
+    {
+        return $this->TaskId;
+    }
+
+    /**
+     *任务ID
+     *
+     * Generated from protobuf field <code>string TaskId = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTaskId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TaskId = $var;
+
+        return $this;
+    }
+
+    /**
+     *任务开始时间,UTC时间
+     *
+     * Generated from protobuf field <code>string StartTime = 3;</code>
+     * @return string
+     */
+    public function getStartTime()
+    {
+        return $this->StartTime;
+    }
+
+    /**
+     *任务开始时间,UTC时间
+     *
+     * Generated from protobuf field <code>string StartTime = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStartTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StartTime = $var;
+
+        return $this;
+    }
+
+    /**
+     *任务结束时间,UTC时间
+     *
+     * Generated from protobuf field <code>string EndTime = 4;</code>
+     * @return string
+     */
+    public function getEndTime()
+    {
+        return $this->EndTime;
+    }
+
+    /**
+     *任务结束时间,UTC时间
+     *
+     * Generated from protobuf field <code>string EndTime = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setEndTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->EndTime = $var;
+
+        return $this;
+    }
+
+    /**
+     *任务回调地址
+     *
+     * Generated from protobuf field <code>string CallbackURL = 5;</code>
+     * @return string
+     */
+    public function getCallbackURL()
+    {
+        return $this->CallbackURL;
+    }
+
+    /**
+     *任务回调地址
+     *
+     * Generated from protobuf field <code>string CallbackURL = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCallbackURL($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CallbackURL = $var;
+
+        return $this;
+    }
+
+    /**
+     *拉流转推类型,0:直播,1:点播
+     *
+     * Generated from protobuf field <code>int32 Type = 6;</code>
+     * @return int
+     */
+    public function getType()
+    {
+        return $this->Type;
+    }
+
+    /**
+     *拉流转推类型,0:直播,1:点播
+     *
+     * Generated from protobuf field <code>int32 Type = 6;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setType($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Type = $var;
+
+        return $this;
+    }
+
+    /**
+     *点播时,拉流地址的循环模式,Type=1时必选,-1:顺序循环
+     *
+     * Generated from protobuf field <code>int32 CycleMode = 7;</code>
+     * @return int
+     */
+    public function getCycleMode()
+    {
+        return $this->CycleMode;
+    }
+
+    /**
+     *点播时,拉流地址的循环模式,Type=1时必选,-1:顺序循环
+     *
+     * Generated from protobuf field <code>int32 CycleMode = 7;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setCycleMode($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->CycleMode = $var;
+
+        return $this;
+    }
+
+    /**
+     *目标推流地址
+     *
+     * Generated from protobuf field <code>string DstAddr = 8;</code>
+     * @return string
+     */
+    public function getDstAddr()
+    {
+        return $this->DstAddr;
+    }
+
+    /**
+     *目标推流地址
+     *
+     * Generated from protobuf field <code>string DstAddr = 8;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDstAddr($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->DstAddr = $var;
+
+        return $this;
+    }
+
+    /**
+     *直播拉流地址
+     *
+     * Generated from protobuf field <code>string SrcAddr = 9;</code>
+     * @return string
+     */
+    public function getSrcAddr()
+    {
+        return $this->SrcAddr;
+    }
+
+    /**
+     *直播拉流地址
+     *
+     * Generated from protobuf field <code>string SrcAddr = 9;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSrcAddr($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SrcAddr = $var;
+
+        return $this;
+    }
+
+    /**
+     *点播拉流地址列表
+     *
+     * Generated from protobuf field <code>repeated string SrcAddrS = 10;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getSrcAddrS()
+    {
+        return $this->SrcAddrS;
+    }
+
+    /**
+     *点播拉流地址列表
+     *
+     * Generated from protobuf field <code>repeated string SrcAddrS = 10;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setSrcAddrS($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->SrcAddrS = $arr;
+
+        return $this;
+    }
+
+    /**
+     *任务状态:停用、未开始、生效中、已结束
+     *
+     * Generated from protobuf field <code>string Status = 11;</code>
+     * @return string
+     */
+    public function getStatus()
+    {
+        return $this->Status;
+    }
+
+    /**
+     *任务状态:停用、未开始、生效中、已结束
+     *
+     * Generated from protobuf field <code>string Status = 11;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStatus($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Status = $var;
+
+        return $this;
+    }
+
+    /**
+     *任务状态:0:停用,1:未开始,2:生效中,3:已结束
+     *
+     * Generated from protobuf field <code>int32 TaskStatus = 12;</code>
+     * @return int
+     */
+    public function getTaskStatus()
+    {
+        return $this->TaskStatus;
+    }
+
+    /**
+     *任务状态:0:停用,1:未开始,2:生效中,3:已结束
+     *
+     * Generated from protobuf field <code>int32 TaskStatus = 12;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setTaskStatus($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->TaskStatus = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/VQScoreID.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/VQScoreID.php
new file mode 100755
index 0000000..e60056c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/VQScoreID.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/VQScore.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.VQScoreID</code>
+ */
+class VQScoreID extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *测试任务ID。
+     *
+     * Generated from protobuf field <code>string ID = 1;</code>
+     */
+    protected $ID = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $ID
+     *          测试任务ID。
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\VQScore::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *测试任务ID。
+     *
+     * Generated from protobuf field <code>string ID = 1;</code>
+     * @return string
+     */
+    public function getID()
+    {
+        return $this->ID;
+    }
+
+    /**
+     *测试任务ID。
+     *
+     * Generated from protobuf field <code>string ID = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setID($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->ID = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/VQScoreInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/VQScoreInfo.php
new file mode 100755
index 0000000..9da9344
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/VQScoreInfo.php
@@ -0,0 +1,337 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/VQScore.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.VQScoreInfo</code>
+ */
+class VQScoreInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *主拉流地址
+     *
+     * Generated from protobuf field <code>string MainAddr = 1;</code>
+     */
+    protected $MainAddr = '';
+    /**
+     *对比拉流地址
+     *
+     * Generated from protobuf field <code>string ContrastAddr = 2;</code>
+     */
+    protected $ContrastAddr = '';
+    /**
+     *测评运行时间
+     *
+     * Generated from protobuf field <code>int64 Duration = 3;</code>
+     */
+    protected $Duration = 0;
+    /**
+     *计算取值点数
+     *
+     * Generated from protobuf field <code>int64 TotalPointNum = 4;</code>
+     */
+    protected $TotalPointNum = 0;
+    /**
+     *计算主拉流地址平均得分
+     *
+     * Generated from protobuf field <code>float MainAverageScore = 5;</code>
+     */
+    protected $MainAverageScore = 0.0;
+    /**
+     *计算对比拉流地址的画质平均得分
+     *
+     * Generated from protobuf field <code>float ContrastAverageScore = 6;</code>
+     */
+    protected $ContrastAverageScore = 0.0;
+    /**
+     *主评分与对比评分的差值,最大-最小
+     *
+     * Generated from protobuf field <code>float Difference = 7;</code>
+     */
+    protected $Difference = 0.0;
+    /**
+     *主评分与对比评分的差值百分比,(最大-最小)/最小
+     *
+     * Generated from protobuf field <code>float DifferencePer = 8;</code>
+     */
+    protected $DifferencePer = 0.0;
+    /**
+     *拉流地址测评得分详细信息
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.AddrScoreInfo AddrScoreList = 9;</code>
+     */
+    private $AddrScoreList;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $MainAddr
+     *          主拉流地址
+     *     @type string $ContrastAddr
+     *          对比拉流地址
+     *     @type int|string $Duration
+     *          测评运行时间
+     *     @type int|string $TotalPointNum
+     *          计算取值点数
+     *     @type float $MainAverageScore
+     *          计算主拉流地址平均得分
+     *     @type float $ContrastAverageScore
+     *          计算对比拉流地址的画质平均得分
+     *     @type float $Difference
+     *          主评分与对比评分的差值,最大-最小
+     *     @type float $DifferencePer
+     *          主评分与对比评分的差值百分比,(最大-最小)/最小
+     *     @type \Volc\Service\Live\Models\Business\AddrScoreInfo[]|\Google\Protobuf\Internal\RepeatedField $AddrScoreList
+     *          拉流地址测评得分详细信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\VQScore::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *主拉流地址
+     *
+     * Generated from protobuf field <code>string MainAddr = 1;</code>
+     * @return string
+     */
+    public function getMainAddr()
+    {
+        return $this->MainAddr;
+    }
+
+    /**
+     *主拉流地址
+     *
+     * Generated from protobuf field <code>string MainAddr = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setMainAddr($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->MainAddr = $var;
+
+        return $this;
+    }
+
+    /**
+     *对比拉流地址
+     *
+     * Generated from protobuf field <code>string ContrastAddr = 2;</code>
+     * @return string
+     */
+    public function getContrastAddr()
+    {
+        return $this->ContrastAddr;
+    }
+
+    /**
+     *对比拉流地址
+     *
+     * Generated from protobuf field <code>string ContrastAddr = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setContrastAddr($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->ContrastAddr = $var;
+
+        return $this;
+    }
+
+    /**
+     *测评运行时间
+     *
+     * Generated from protobuf field <code>int64 Duration = 3;</code>
+     * @return int|string
+     */
+    public function getDuration()
+    {
+        return $this->Duration;
+    }
+
+    /**
+     *测评运行时间
+     *
+     * Generated from protobuf field <code>int64 Duration = 3;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setDuration($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->Duration = $var;
+
+        return $this;
+    }
+
+    /**
+     *计算取值点数
+     *
+     * Generated from protobuf field <code>int64 TotalPointNum = 4;</code>
+     * @return int|string
+     */
+    public function getTotalPointNum()
+    {
+        return $this->TotalPointNum;
+    }
+
+    /**
+     *计算取值点数
+     *
+     * Generated from protobuf field <code>int64 TotalPointNum = 4;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setTotalPointNum($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->TotalPointNum = $var;
+
+        return $this;
+    }
+
+    /**
+     *计算主拉流地址平均得分
+     *
+     * Generated from protobuf field <code>float MainAverageScore = 5;</code>
+     * @return float
+     */
+    public function getMainAverageScore()
+    {
+        return $this->MainAverageScore;
+    }
+
+    /**
+     *计算主拉流地址平均得分
+     *
+     * Generated from protobuf field <code>float MainAverageScore = 5;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setMainAverageScore($var)
+    {
+        GPBUtil::checkFloat($var);
+        $this->MainAverageScore = $var;
+
+        return $this;
+    }
+
+    /**
+     *计算对比拉流地址的画质平均得分
+     *
+     * Generated from protobuf field <code>float ContrastAverageScore = 6;</code>
+     * @return float
+     */
+    public function getContrastAverageScore()
+    {
+        return $this->ContrastAverageScore;
+    }
+
+    /**
+     *计算对比拉流地址的画质平均得分
+     *
+     * Generated from protobuf field <code>float ContrastAverageScore = 6;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setContrastAverageScore($var)
+    {
+        GPBUtil::checkFloat($var);
+        $this->ContrastAverageScore = $var;
+
+        return $this;
+    }
+
+    /**
+     *主评分与对比评分的差值,最大-最小
+     *
+     * Generated from protobuf field <code>float Difference = 7;</code>
+     * @return float
+     */
+    public function getDifference()
+    {
+        return $this->Difference;
+    }
+
+    /**
+     *主评分与对比评分的差值,最大-最小
+     *
+     * Generated from protobuf field <code>float Difference = 7;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setDifference($var)
+    {
+        GPBUtil::checkFloat($var);
+        $this->Difference = $var;
+
+        return $this;
+    }
+
+    /**
+     *主评分与对比评分的差值百分比,(最大-最小)/最小
+     *
+     * Generated from protobuf field <code>float DifferencePer = 8;</code>
+     * @return float
+     */
+    public function getDifferencePer()
+    {
+        return $this->DifferencePer;
+    }
+
+    /**
+     *主评分与对比评分的差值百分比,(最大-最小)/最小
+     *
+     * Generated from protobuf field <code>float DifferencePer = 8;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setDifferencePer($var)
+    {
+        GPBUtil::checkFloat($var);
+        $this->DifferencePer = $var;
+
+        return $this;
+    }
+
+    /**
+     *拉流地址测评得分详细信息
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.AddrScoreInfo AddrScoreList = 9;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getAddrScoreList()
+    {
+        return $this->AddrScoreList;
+    }
+
+    /**
+     *拉流地址测评得分详细信息
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.AddrScoreInfo AddrScoreList = 9;</code>
+     * @param \Volc\Service\Live\Models\Business\AddrScoreInfo[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setAddrScoreList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Live\Models\Business\AddrScoreInfo::class);
+        $this->AddrScoreList = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/VQScoreTaskInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/VQScoreTaskInfo.php
new file mode 100755
index 0000000..9094cf3
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/VQScoreTaskInfo.php
@@ -0,0 +1,167 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/VQScore.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.VQScoreTaskInfo</code>
+ */
+class VQScoreTaskInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *测评任务ID
+     *
+     * Generated from protobuf field <code>string ID = 1;</code>
+     */
+    protected $ID = '';
+    /**
+     *测评运行时间
+     *
+     * Generated from protobuf field <code>int64 Duration = 2;</code>
+     */
+    protected $Duration = 0;
+    /**
+     *测评状态,1:测试中,2:成功,3:失败
+     *
+     * Generated from protobuf field <code>int64 Status = 3;</code>
+     */
+    protected $Status = 0;
+    /**
+     *账号
+     *
+     * Generated from protobuf field <code>string AccountID = 4;</code>
+     */
+    protected $AccountID = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $ID
+     *          测评任务ID
+     *     @type int|string $Duration
+     *          测评运行时间
+     *     @type int|string $Status
+     *          测评状态,1:测试中,2:成功,3:失败
+     *     @type string $AccountID
+     *          账号
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\VQScore::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *测评任务ID
+     *
+     * Generated from protobuf field <code>string ID = 1;</code>
+     * @return string
+     */
+    public function getID()
+    {
+        return $this->ID;
+    }
+
+    /**
+     *测评任务ID
+     *
+     * Generated from protobuf field <code>string ID = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setID($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->ID = $var;
+
+        return $this;
+    }
+
+    /**
+     *测评运行时间
+     *
+     * Generated from protobuf field <code>int64 Duration = 2;</code>
+     * @return int|string
+     */
+    public function getDuration()
+    {
+        return $this->Duration;
+    }
+
+    /**
+     *测评运行时间
+     *
+     * Generated from protobuf field <code>int64 Duration = 2;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setDuration($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->Duration = $var;
+
+        return $this;
+    }
+
+    /**
+     *测评状态,1:测试中,2:成功,3:失败
+     *
+     * Generated from protobuf field <code>int64 Status = 3;</code>
+     * @return int|string
+     */
+    public function getStatus()
+    {
+        return $this->Status;
+    }
+
+    /**
+     *测评状态,1:测试中,2:成功,3:失败
+     *
+     * Generated from protobuf field <code>int64 Status = 3;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setStatus($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->Status = $var;
+
+        return $this;
+    }
+
+    /**
+     *账号
+     *
+     * Generated from protobuf field <code>string AccountID = 4;</code>
+     * @return string
+     */
+    public function getAccountID()
+    {
+        return $this->AccountID;
+    }
+
+    /**
+     *账号
+     *
+     * Generated from protobuf field <code>string AccountID = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setAccountID($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->AccountID = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/VQScoreTaskListInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/VQScoreTaskListInfo.php
new file mode 100755
index 0000000..e5d35eb
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/VQScoreTaskListInfo.php
@@ -0,0 +1,167 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/VQScore.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.VQScoreTaskListInfo</code>
+ */
+class VQScoreTaskListInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *查询开始时间,UTC时间格式
+     *
+     * Generated from protobuf field <code>string StartTime = 2;</code>
+     */
+    protected $StartTime = '';
+    /**
+     *查询结束时间,UTC时间格式
+     *
+     * Generated from protobuf field <code>string EndTime = 3;</code>
+     */
+    protected $EndTime = '';
+    /**
+     *条目总数
+     *
+     * Generated from protobuf field <code>int64 Total = 4;</code>
+     */
+    protected $Total = 0;
+    /**
+     *测评任务列表明细
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.VQScoreTaskInfo TaskList = 5;</code>
+     */
+    private $TaskList;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $StartTime
+     *          查询开始时间,UTC时间格式
+     *     @type string $EndTime
+     *          查询结束时间,UTC时间格式
+     *     @type int|string $Total
+     *          条目总数
+     *     @type \Volc\Service\Live\Models\Business\VQScoreTaskInfo[]|\Google\Protobuf\Internal\RepeatedField $TaskList
+     *          测评任务列表明细
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\VQScore::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *查询开始时间,UTC时间格式
+     *
+     * Generated from protobuf field <code>string StartTime = 2;</code>
+     * @return string
+     */
+    public function getStartTime()
+    {
+        return $this->StartTime;
+    }
+
+    /**
+     *查询开始时间,UTC时间格式
+     *
+     * Generated from protobuf field <code>string StartTime = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStartTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StartTime = $var;
+
+        return $this;
+    }
+
+    /**
+     *查询结束时间,UTC时间格式
+     *
+     * Generated from protobuf field <code>string EndTime = 3;</code>
+     * @return string
+     */
+    public function getEndTime()
+    {
+        return $this->EndTime;
+    }
+
+    /**
+     *查询结束时间,UTC时间格式
+     *
+     * Generated from protobuf field <code>string EndTime = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setEndTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->EndTime = $var;
+
+        return $this;
+    }
+
+    /**
+     *条目总数
+     *
+     * Generated from protobuf field <code>int64 Total = 4;</code>
+     * @return int|string
+     */
+    public function getTotal()
+    {
+        return $this->Total;
+    }
+
+    /**
+     *条目总数
+     *
+     * Generated from protobuf field <code>int64 Total = 4;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setTotal($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->Total = $var;
+
+        return $this;
+    }
+
+    /**
+     *测评任务列表明细
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.VQScoreTaskInfo TaskList = 5;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getTaskList()
+    {
+        return $this->TaskList;
+    }
+
+    /**
+     *测评任务列表明细
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.VQScoreTaskInfo TaskList = 5;</code>
+     * @param \Volc\Service\Live\Models\Business\VQScoreTaskInfo[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setTaskList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Live\Models\Business\VQScoreTaskInfo::class);
+        $this->TaskList = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/VhostWithDenyConfig.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/VhostWithDenyConfig.php
new file mode 100755
index 0000000..9ec2fef
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Business/VhostWithDenyConfig.php
@@ -0,0 +1,167 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/business/deny_config.proto
+
+namespace Volc\Service\Live\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Business.VhostWithDenyConfig</code>
+ */
+class VhostWithDenyConfig extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     */
+    protected $Vhost = '';
+    /**
+     *推拉流域名
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     */
+    protected $Domain = '';
+    /**
+     * App的名称,由 1 到 30 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     */
+    protected $App = '';
+    /**
+     *配置详情列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.DenyConfigDetail DenyConfig = 4;</code>
+     */
+    private $DenyConfig;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vhost
+     *          域名空间名称
+     *     @type string $Domain
+     *          推拉流域名
+     *     @type string $App
+     *           App的名称,由 1 到 30 位数字、字母、下划线及"-"和"."组成。
+     *     @type \Volc\Service\Live\Models\Business\DenyConfigDetail[]|\Google\Protobuf\Internal\RepeatedField $DenyConfig
+     *          配置详情列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\DenyConfig::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @return string
+     */
+    public function getVhost()
+    {
+        return $this->Vhost;
+    }
+
+    /**
+     *域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVhost($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vhost = $var;
+
+        return $this;
+    }
+
+    /**
+     *推拉流域名
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     * @return string
+     */
+    public function getDomain()
+    {
+        return $this->Domain;
+    }
+
+    /**
+     *推拉流域名
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomain($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Domain = $var;
+
+        return $this;
+    }
+
+    /**
+     * App的名称,由 1 到 30 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     * @return string
+     */
+    public function getApp()
+    {
+        return $this->App;
+    }
+
+    /**
+     * App的名称,由 1 到 30 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setApp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->App = $var;
+
+        return $this;
+    }
+
+    /**
+     *配置详情列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.DenyConfigDetail DenyConfig = 4;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getDenyConfig()
+    {
+        return $this->DenyConfig;
+    }
+
+    /**
+     *配置详情列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.DenyConfigDetail DenyConfig = 4;</code>
+     * @param \Volc\Service\Live\Models\Business\DenyConfigDetail[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setDenyConfig($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Live\Models\Business\DenyConfigDetail::class);
+        $this->DenyConfig = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/Addr.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/Addr.php
new file mode 100755
index 0000000..f628050
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/Addr.php
Binary files differ
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/DenyConfig.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/DenyConfig.php
new file mode 100755
index 0000000..21f637a
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/DenyConfig.php
Binary files differ
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/Domain.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/Domain.php
new file mode 100755
index 0000000..d81b1d8
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/Domain.php
Binary files differ
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/PullToPush.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/PullToPush.php
new file mode 100755
index 0000000..a1fd95a
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/PullToPush.php
Binary files differ
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/RecordManage.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/RecordManage.php
new file mode 100755
index 0000000..2fbf3b7
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/RecordManage.php
Binary files differ
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/RelaySource.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/RelaySource.php
new file mode 100755
index 0000000..c0b18e0
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/RelaySource.php
Binary files differ
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/RequestLive.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/RequestLive.php
new file mode 100755
index 0000000..33da2c1
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/RequestLive.php
@@ -0,0 +1,212 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\GPBMetadata;
+
+class RequestLive
+{
+    public static $is_initialized = false;
+
+    public static function initOnce() {
+        $pool = \Google\Protobuf\Internal\DescriptorPool::getGeneratedPool();
+
+        if (static::$is_initialized == true) {
+          return;
+        }
+        \Volc\Service\Live\Models\GPBMetadata\DenyConfig::initOnce();
+        $pool->internalAddGeneratedFile(
+            '
+�
+live/request/request_live.protoVolcengine.Live.Models.Request"C
+CreateDomainRequest
+Domain (	
+Type (	
+Region (	"�
+ListDomainDetailRequest
+PageNum (
+PageSize (
+	VhostList (	
+DomainStatusList (	
+DomainTypeList (	
+DomainRegionList (	
+DomainNameList (	"&
+DisableDomainRequest
+Domain (	"%
+EnableDomainRequest
+Domain (	"%
+DeleteDomainRequest
+Domain (	"+
+DescribeDomainRequest
+
+DomainList (	"J
+ ManagerPullPushDomainBindRequest
+
+PullDomain (	
+
+PushDomain (	"�
+$DescribeRecordTaskFileHistoryRequest
+Vhost (	
+App (	
+Stream (	
+DateFrom (	
+DateTo (	
+PageNum (
+PageSize (
+Type (	"�
+!DescribeCDNSnapshotHistoryRequest
+Vhost (	
+App (	
+Stream (	
+DateFrom (	
+DateTo (	
+PageNum (
+PageSize (
+Type (	"�
+#DescribeLiveStreamInfoByPageRequest
+PageNum (
+PageSize (
+Vhost (	
+Domain (	
+App (	
+Stream (	
+
+StreamType (	
+InfoType (	"�
+%DescribeClosedStreamInfoByPageRequest
+PageNum (
+PageSize (
+Vhost (	
+Domain (	
+App (	
+Stream (	
+Sort (	
+EndTimeFrom (	
+	EndTimeTo	 (	
+
+SourceType
+ (	"�
+(DescribeForbiddenStreamInfoByPageRequest
+PageNum (
+PageSize (
+Vhost (	
+App (	
+Stream (	
+Sort (	"\\
+DescribeLiveStreamStateRequest
+Vhost (	
+Domain (	
+App (	
+Stream (	"Q
+ResumeStreamRequest
+Vhost (	
+Domain (	
+App (	
+Stream (	"?
+KillStreamRequest
+Vhost (	
+App (	
+Stream (	"b
+ForbidStreamRequest
+Vhost (	
+Domain (	
+App (	
+Stream (	
+EndTime (	"�
+UpdateRelaySourceRequest
+Vhost (	
+App (	
+RelaySourceDomainList (	j
+RelaySourceParams (2O.Volcengine.Live.Models.Request.UpdateRelaySourceRequest.RelaySourceParamsEntry
+RelaySourceProtocol (	8
+RelaySourceParamsEntry
+key (	
+value (	:8"6
+DeleteRelaySourceRequest
+Vhost (	
+App (	"+
+DescribeRelaySourceRequest
+Vhost (	"~
+CreateVQScoreTaskRequest
+MainAddr (	
+ContrastAddr (	
+
FrameInterval (
+Duration (
+	Algorithm (	"(
+DescribeVQScoreTaskRequest
+
+ID (	"o
+ListVQScoreTaskRequest
+	StartTime (	
+EndTime (	
+PageNum (
+PageSize (
+Status ("�
+GeneratePlayURLRequest
+Vhost (	
+Domain (	
+App (	
+Stream (	
+Suffix (	
+Type (	
+
ValidDuration (
+ExpiredTime (	"�
+GeneratePushURLRequest
+Vhost (	
+Domain (	
+App (	
+Stream (	
+
ValidDuration (
+ExpiredTime (	"�
+CreatePullToPushTaskRequest
+Title (	
+	StartTime (
+EndTime (
+CallbackURL (	
+Type (
+	CycleMode (
+DstAddr (	
+SrcAddr (	
+SrcAddrS	 (	"F
+ListPullToPushTaskRequest
+Page (
+Size (
+Title (	"�
+UpdatePullToPushTaskRequest
+Title (	
+TaskId (	
+	StartTime (
+EndTime (
+CallbackURL (	
+Type (
+	CycleMode (
+DstAddr (	
+SrcAddr	 (	
+SrcAddrS
+ (	".
+RestartPullToPushTaskRequest
+TaskId (	"+
+StopPullToPushTaskRequest
+TaskId (	"-
+DeletePullToPushTaskRequest
+TaskId (	"�
+UpdateDenyConfigRequest
+Vhost (	
+Domain (	
+App (	I
+DenyConfigList (21.Volcengine.Live.Models.Business.DenyConfigDetail"G
+DescribeDenyConfigRequest
+Vhost (	
+Domain (	
+App (	"E
+DeleteDenyConfigRequest
+Vhost (	
+Domain (	
+App (	B�
+)com.volcengine.service.live.model.requestBLiveRequestPZAgithub.com/volcengine/volc-sdk-golang/service/live/models/request��� Volc\\Service\\Live\\Models\\Request�$Volc\\Service\\Live\\Models\\GPBMetadatabproto3'
+        , true);
+
+        static::$is_initialized = true;
+    }
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/ResponseLive.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/ResponseLive.php
new file mode 100755
index 0000000..86d6d8a
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/ResponseLive.php
@@ -0,0 +1,120 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\GPBMetadata;
+
+class ResponseLive
+{
+    public static $is_initialized = false;
+
+    public static function initOnce() {
+        $pool = \Google\Protobuf\Internal\DescriptorPool::getGeneratedPool();
+
+        if (static::$is_initialized == true) {
+          return;
+        }
+        \Volc\Service\Base\Models\GPBMetadata\Base::initOnce();
+        \Volc\Service\Live\Models\GPBMetadata\Domain::initOnce();
+        \Volc\Service\Live\Models\GPBMetadata\VQScore::initOnce();
+        \Volc\Service\Live\Models\GPBMetadata\Addr::initOnce();
+        \Volc\Service\Live\Models\GPBMetadata\PullToPush::initOnce();
+        \Volc\Service\Live\Models\GPBMetadata\DenyConfig::initOnce();
+        \Volc\Service\Live\Models\GPBMetadata\RelaySource::initOnce();
+        \Volc\Service\Live\Models\GPBMetadata\StreamManage::initOnce();
+        \Volc\Service\Live\Models\GPBMetadata\SnapshotManage::initOnce();
+        \Volc\Service\Live\Models\GPBMetadata\RecordManage::initOnce();
+        $pool->internalAddGeneratedFile(
+            '
+�(
+!live/response/response_live.protoVolcengine.Live.Models.Responselive/business/domain.protolive/business/VQScore.protolive/business/addr.proto live/business/pull_to_push.protolive/business/deny_config.proto live/business/relay_source.proto!live/business/stream_manage.proto#live/business/snapshot_manage.proto!live/business/record_manage.proto"_
+CreateDomainResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata"�
+ListDomainDetailResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata?
+Result (2/.Volcengine.Live.Models.Business.DomainListInfo"`
+DisableDomainResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata"_
+EnableDomainResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata"_
+DeleteDomainResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata"�
+DescribeDomainResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata?
+Result (2/.Volcengine.Live.Models.Business.DomainListInfo"l
+!ManagerPullPushDomainBindResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata"�
+$DescribeLiveStreamInfoByPageResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata?
+Result (2/.Volcengine.Live.Models.Business.LiveStreamInfo"�
+&DescribeClosedStreamInfoByPageResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataA
+Result (21.Volcengine.Live.Models.Business.ClosedStreamInfo"�
+DescribeLiveStreamStateResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata@
+Result (20.Volcengine.Live.Models.Business.StreamStateInfo"]
+KillStreamResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata"_
+ForbidStreamResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata"�
+)DescribeForbiddenStreamInfoByPageResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataD
+Result (24.Volcengine.Live.Models.Business.ForbiddenStreamInfo"_
+ResumeStreamResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata"�
+"DescribeCDNSnapshotHistoryResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataG
+Result (27.Volcengine.Live.Models.Business.CDNSnapshotHistoryInfo"�
+%DescribeRecordTaskFileHistoryResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataB
+Result (22.Volcengine.Live.Models.Business.RecordHistoryInfo"d
+UpdateRelaySourceResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata"d
+DeleteRelaySourceResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata"�
+DescribeRelaySourceResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataF
+Result (26.Volcengine.Live.Models.Business.RelaySourceConfigList"�
+CreateVQScoreTaskResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata:
+Result (2*.Volcengine.Live.Models.Business.VQScoreID"�
+DescribeVQScoreTaskResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata<
+Result (2,.Volcengine.Live.Models.Business.VQScoreInfo"�
+ListVQScoreTaskResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataD
+Result (24.Volcengine.Live.Models.Business.VQScoreTaskListInfo"�
+GeneratePlayURLResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataF
+Result (26.Volcengine.Live.Models.Business.GeneratePlayURLResult"�
+GeneratePushURLResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataF
+Result (26.Volcengine.Live.Models.Business.GeneratePushURLResult"�
+CreatePullToPushTaskResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataK
+Result (2;.Volcengine.Live.Models.Business.CreatePullToPushTaskResult"�
+ListPullToPushTaskResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataI
+Result (29.Volcengine.Live.Models.Business.ListPullToPushTaskResult"g
+UpdatePullToPushTaskResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata"h
+RestartPullToPushTaskResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata"e
+StopPullToPushTaskResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata"g
+DeletePullToPushTaskResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata"c
+UpdateDenyConfigResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata"�
+DescribeDenyConfigResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataI
+Result (29.Volcengine.Live.Models.Business.DescribeDenyConfigResult"c
+DeleteDenyConfigResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataB�
+*com.volcengine.service.live.model.responseBLiveResponsePZBgithub.com/volcengine/volc-sdk-golang/service/live/models/response���!Volc\\Service\\Live\\Models\\Response�$Volc\\Service\\Live\\Models\\GPBMetadatabproto3'
+        , true);
+
+        static::$is_initialized = true;
+    }
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/SnapshotManage.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/SnapshotManage.php
new file mode 100755
index 0000000..64d8d9d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/SnapshotManage.php
Binary files differ
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/StreamManage.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/StreamManage.php
new file mode 100755
index 0000000..58e7f75
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/StreamManage.php
Binary files differ
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/VQScore.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/VQScore.php
new file mode 100755
index 0000000..071be11
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/GPBMetadata/VQScore.php
Binary files differ
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/CreateDomainRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/CreateDomainRequest.php
new file mode 100755
index 0000000..136dd73
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/CreateDomainRequest.php
@@ -0,0 +1,133 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.CreateDomainRequest</code>
+ */
+class CreateDomainRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 域名。一次只能提交一个域名。域名请在工信部完成备案。
+     *
+     * Generated from protobuf field <code>string Domain = 1;</code>
+     */
+    protected $Domain = '';
+    /**
+     * 域名类型,包含两种类型。<li>push:推流域名;<li>pull-flv:拉流域名,包含RTMP、FLV、HLS格式。
+     *
+     * Generated from protobuf field <code>string Type = 2;</code>
+     */
+    protected $Type = '';
+    /**
+     * 区域,包含两种类型。<li>cn:中国大陆;<li>oversea:海外。
+     *
+     * Generated from protobuf field <code>string Region = 3;</code>
+     */
+    protected $Region = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Domain
+     *           域名。一次只能提交一个域名。域名请在工信部完成备案。
+     *     @type string $Type
+     *           域名类型,包含两种类型。<li>push:推流域名;<li>pull-flv:拉流域名,包含RTMP、FLV、HLS格式。
+     *     @type string $Region
+     *           区域,包含两种类型。<li>cn:中国大陆;<li>oversea:海外。
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 域名。一次只能提交一个域名。域名请在工信部完成备案。
+     *
+     * Generated from protobuf field <code>string Domain = 1;</code>
+     * @return string
+     */
+    public function getDomain()
+    {
+        return $this->Domain;
+    }
+
+    /**
+     * 域名。一次只能提交一个域名。域名请在工信部完成备案。
+     *
+     * Generated from protobuf field <code>string Domain = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomain($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Domain = $var;
+
+        return $this;
+    }
+
+    /**
+     * 域名类型,包含两种类型。<li>push:推流域名;<li>pull-flv:拉流域名,包含RTMP、FLV、HLS格式。
+     *
+     * Generated from protobuf field <code>string Type = 2;</code>
+     * @return string
+     */
+    public function getType()
+    {
+        return $this->Type;
+    }
+
+    /**
+     * 域名类型,包含两种类型。<li>push:推流域名;<li>pull-flv:拉流域名,包含RTMP、FLV、HLS格式。
+     *
+     * Generated from protobuf field <code>string Type = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Type = $var;
+
+        return $this;
+    }
+
+    /**
+     * 区域,包含两种类型。<li>cn:中国大陆;<li>oversea:海外。
+     *
+     * Generated from protobuf field <code>string Region = 3;</code>
+     * @return string
+     */
+    public function getRegion()
+    {
+        return $this->Region;
+    }
+
+    /**
+     * 区域,包含两种类型。<li>cn:中国大陆;<li>oversea:海外。
+     *
+     * Generated from protobuf field <code>string Region = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setRegion($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Region = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/CreatePullToPushTaskRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/CreatePullToPushTaskRequest.php
new file mode 100755
index 0000000..31f518d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/CreatePullToPushTaskRequest.php
@@ -0,0 +1,337 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.CreatePullToPushTaskRequest</code>
+ */
+class CreatePullToPushTaskRequest extends \Google\Protobuf\Internal\Message
+{
+
+    /**
+     *标题
+     *
+     * Generated from protobuf field <code>string Title = 1;</code>
+     */
+    public $Title = '';
+    /**
+     *开始时间,unix时间戳,单位s
+     *
+     * Generated from protobuf field <code>int64 StartTime = 2;</code>
+     */
+    public  $StartTime =0;
+    /**
+     *结束时间,unix时间戳,单位s,和开始时间的跨度最大7天
+     *
+     * Generated from protobuf field <code>int64 EndTime = 3;</code>
+     */
+    public  $EndTime =0;
+    /**
+     *回调地址,小于2000字符
+     *
+     * Generated from protobuf field <code>string CallbackURL = 4;</code>
+     */
+    public $CallbackURL = '';
+    /**
+     *拉流转推地址类型,0:直播,1:点播
+     *
+     * Generated from protobuf field <code>int64 Type = 5;</code>
+     */
+    public  $Type = 0;
+    /**
+     *点播时,拉流地址的循环模式,Type=1时必选,-1:顺序循环
+     *
+     * Generated from protobuf field <code>int32 CycleMode = 6;</code>
+     */
+    public  $CycleMode = 0;
+    /**
+     *推流目标地址
+     *
+     * Generated from protobuf field <code>string DstAddr = 7;</code>
+     */
+    public $DstAddr = '';
+    /**
+     *直播拉流地址,Type=0时必填
+     *
+     * Generated from protobuf field <code>string SrcAddr = 8;</code>
+     */
+    public $SrcAddr = '';
+    /**
+     *点播拉流地址,Type=1时必填
+     *
+     * Generated from protobuf field <code>repeated string SrcAddrS = 9;</code>
+     */
+    public  $SrcAddrS;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Title
+     *          标题
+     *     @type int|string $StartTime
+     *          开始时间,unix时间戳,单位s
+     *     @type int|string $EndTime
+     *          结束时间,unix时间戳,单位s,和开始时间的跨度最大7天
+     *     @type string $CallbackURL
+     *          回调地址,小于2000字符
+     *     @type int|string $Type
+     *          拉流转推地址类型,0:直播,1:点播
+     *     @type int $CycleMode
+     *          点播时,拉流地址的循环模式,Type=1时必选,-1:顺序循环
+     *     @type string $DstAddr
+     *          推流目标地址
+     *     @type string $SrcAddr
+     *          直播拉流地址,Type=0时必填
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $SrcAddrS
+     *          点播拉流地址,Type=1时必填
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *标题
+     *
+     * Generated from protobuf field <code>string Title = 1;</code>
+     * @return string
+     */
+    public function getTitle()
+    {
+        return $this->Title;
+    }
+
+    /**
+     *标题
+     *
+     * Generated from protobuf field <code>string Title = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTitle($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Title = $var;
+
+        return $this;
+    }
+
+    /**
+     *开始时间,unix时间戳,单位s
+     *
+     * Generated from protobuf field <code>int64 StartTime = 2;</code>
+     * @return int|string
+     */
+    public function getStartTime()
+    {
+        return $this->StartTime;
+    }
+
+    /**
+     *开始时间,unix时间戳,单位s
+     *
+     * Generated from protobuf field <code>int64 StartTime = 2;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setStartTime($var)
+    {
+        GPBUtil::checkFloat($var);
+        $this->StartTime = $var;
+
+        return $this;
+    }
+
+    /**
+     *结束时间,unix时间戳,单位s,和开始时间的跨度最大7天
+     *
+     * Generated from protobuf field <code>int64 EndTime = 3;</code>
+     * @return int|string
+     */
+    public function getEndTime()
+    {
+        return $this->EndTime;
+    }
+
+    /**
+     *结束时间,unix时间戳,单位s,和开始时间的跨度最大7天
+     *
+     * Generated from protobuf field <code>int64 EndTime = 3;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setEndTime($var)
+    {
+        GPBUtil::checkFloat($var);
+        $this->EndTime = $var;
+
+        return $this;
+    }
+
+    /**
+     *回调地址,小于2000字符
+     *
+     * Generated from protobuf field <code>string CallbackURL = 4;</code>
+     * @return string
+     */
+    public function getCallbackURL()
+    {
+        return $this->CallbackURL;
+    }
+
+    /**
+     *回调地址,小于2000字符
+     *
+     * Generated from protobuf field <code>string CallbackURL = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCallbackURL($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CallbackURL = $var;
+
+        return $this;
+    }
+
+    /**
+     *拉流转推地址类型,0:直播,1:点播
+     *
+     * Generated from protobuf field <code>int64 Type = 5;</code>
+     * @return int|string
+     */
+    public function getType()
+    {
+        return $this->Type;
+    }
+
+    /**
+     *拉流转推地址类型,0:直播,1:点播
+     *
+     * Generated from protobuf field <code>int64 Type = 5;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setType($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->Type = $var;
+        return $this;
+    }
+
+    /**
+     *点播时,拉流地址的循环模式,Type=1时必选,-1:顺序循环
+     *
+     * Generated from protobuf field <code>int32 CycleMode = 6;</code>
+     * @return int
+     */
+    public function getCycleMode()
+    {
+        return $this->CycleMode;
+    }
+
+    /**
+     *点播时,拉流地址的循环模式,Type=1时必选,-1:顺序循环
+     *
+     * Generated from protobuf field <code>int32 CycleMode = 6;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setCycleMode($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->CycleMode = $var;
+
+        return $this;
+    }
+
+    /**
+     *推流目标地址
+     *
+     * Generated from protobuf field <code>string DstAddr = 7;</code>
+     * @return string
+     */
+    public function getDstAddr()
+    {
+        return $this->DstAddr;
+    }
+
+    /**
+     *推流目标地址
+     *
+     * Generated from protobuf field <code>string DstAddr = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDstAddr($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->DstAddr = $var;
+
+        return $this;
+    }
+
+    /**
+     *直播拉流地址,Type=0时必填
+     *
+     * Generated from protobuf field <code>string SrcAddr = 8;</code>
+     * @return string
+     */
+    public function getSrcAddr()
+    {
+        return $this->SrcAddr;
+    }
+
+    /**
+     *直播拉流地址,Type=0时必填
+     *
+     * Generated from protobuf field <code>string SrcAddr = 8;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSrcAddr($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SrcAddr = $var;
+
+        return $this;
+    }
+
+    /**
+     *点播拉流地址,Type=1时必填
+     *
+     * Generated from protobuf field <code>repeated string SrcAddrS = 9;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getSrcAddrS()
+    {
+        return $this->SrcAddrS;
+    }
+
+    /**
+     *点播拉流地址,Type=1时必填
+     *
+     * Generated from protobuf field <code>repeated string SrcAddrS = 9;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setSrcAddrS($var)
+    {
+//        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->SrcAddrS = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/CreateVQScoreTaskRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/CreateVQScoreTaskRequest.php
new file mode 100755
index 0000000..2b9f7dc
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/CreateVQScoreTaskRequest.php
@@ -0,0 +1,213 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.CreateVQScoreTaskRequest</code>
+ */
+class CreateVQScoreTaskRequest  extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 主拉流地址。 支持输入FCDN拉流地址和第三方CDN拉流地址。
+     *
+     * Generated from protobuf field <code>string MainAddr = 1;</code>
+     */
+    public $MainAddr = '';
+    /**
+     * 对比拉流地址。
+     *
+     * Generated from protobuf field <code>string ContrastAddr = 2;</code>
+     */
+    public $ContrastAddr = '';
+    /**
+     * 抽帧间隔,目前只能密集抽帧模板控制,以模板为主。
+     *
+     * Generated from protobuf field <code>int32 FrameInterval = 3;</code>
+     */
+    public $FrameInterval = 0;
+    /**
+     * 测评运行时间
+     *- 支持输入s整数
+     *- 最大支持7*24小时的测评任务
+     *- 最小支持1min的测评任务
+     *
+     * Generated from protobuf field <code>int32 Duration = 4;</code>
+     */
+    public $Duration = 0;
+    /**
+     * 测评算法,支持vqscore分数计算
+     *
+     * Generated from protobuf field <code>string Algorithm = 5;</code>
+     */
+    public $Algorithm = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $MainAddr
+     *           主拉流地址。 支持输入FCDN拉流地址和第三方CDN拉流地址。
+     *     @type string $ContrastAddr
+     *           对比拉流地址。
+     *     @type int $FrameInterval
+     *           抽帧间隔,目前只能密集抽帧模板控制,以模板为主。
+     *     @type int $Duration
+     *           测评运行时间
+     *          - 支持输入s整数
+     *          - 最大支持7*24小时的测评任务
+     *          - 最小支持1min的测评任务
+     *     @type string $Algorithm
+     *           测评算法,支持vqscore分数计算
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 主拉流地址。 支持输入FCDN拉流地址和第三方CDN拉流地址。
+     *
+     * Generated from protobuf field <code>string MainAddr = 1;</code>
+     * @return string
+     */
+    public function getMainAddr()
+    {
+        return $this->MainAddr;
+    }
+
+    /**
+     * 主拉流地址。 支持输入FCDN拉流地址和第三方CDN拉流地址。
+     *
+     * Generated from protobuf field <code>string MainAddr = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setMainAddr($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->MainAddr = $var;
+
+        return $this;
+    }
+
+    /**
+     * 对比拉流地址。
+     *
+     * Generated from protobuf field <code>string ContrastAddr = 2;</code>
+     * @return string
+     */
+    public function getContrastAddr()
+    {
+        return $this->ContrastAddr;
+    }
+
+    /**
+     * 对比拉流地址。
+     *
+     * Generated from protobuf field <code>string ContrastAddr = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setContrastAddr($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->ContrastAddr = $var;
+
+        return $this;
+    }
+
+    /**
+     * 抽帧间隔,目前只能密集抽帧模板控制,以模板为主。
+     *
+     * Generated from protobuf field <code>int32 FrameInterval = 3;</code>
+     * @return int
+     */
+    public function getFrameInterval()
+    {
+        return $this->FrameInterval;
+    }
+
+    /**
+     * 抽帧间隔,目前只能密集抽帧模板控制,以模板为主。
+     *
+     * Generated from protobuf field <code>int32 FrameInterval = 3;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setFrameInterval(int $var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->FrameInterval = $var;
+
+        return $this;
+    }
+
+    /**
+     * 测评运行时间7
+     *- 支持输入s整数
+     *- 最大支持7*24小时的测评任务
+     *- 最小支持1min的测评任务
+     *
+     * Generated from protobuf field <code>int32 Duration = 4;</code>
+     * @return int
+     */
+    public function getDuration()
+    {
+        return $this->Duration;
+    }
+
+    /**
+     * 测评运行时间
+     *- 支持输入s整数
+     *- 最大支持7*24小时的测评任务
+     *- 最小支持1min的测评任务
+     *
+     * Generated from protobuf field <code>int32 Duration = 4;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setDuration(int $var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Duration = $var;
+
+        return $this;
+    }
+
+    /**
+     * 测评算法,支持vqscore分数计算
+     *
+     * Generated from protobuf field <code>string Algorithm = 5;</code>
+     * @return string
+     */
+    public function getAlgorithm()
+    {
+        return $this->Algorithm;
+    }
+
+    /**
+     * 测评算法,支持vqscore分数计算
+     *
+     * Generated from protobuf field <code>string Algorithm = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setAlgorithm($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Algorithm = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DeleteDenyConfigRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DeleteDenyConfigRequest.php
new file mode 100755
index 0000000..ed97bdd
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DeleteDenyConfigRequest.php
@@ -0,0 +1,137 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.DeleteDenyConfigRequest</code>
+ */
+class DeleteDenyConfigRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     */
+    protected $Vhost = '';
+    /**
+     *推拉流域名,domain和app二选一填
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     */
+    protected $Domain = '';
+    /**
+     * App的名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。domain和app二选一填。
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     */
+    protected $App = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vhost
+     *          域名空间名称
+     *     @type string $Domain
+     *          推拉流域名,domain和app二选一填
+     *     @type string $App
+     *           App的名称,由 1 到 30
+     *           位数字、字母、下划线及"-"和"."组成。domain和app二选一填。
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @return string
+     */
+    public function getVhost()
+    {
+        return $this->Vhost;
+    }
+
+    /**
+     *域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVhost($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vhost = $var;
+
+        return $this;
+    }
+
+    /**
+     *推拉流域名,domain和app二选一填
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     * @return string
+     */
+    public function getDomain()
+    {
+        return $this->Domain;
+    }
+
+    /**
+     *推拉流域名,domain和app二选一填
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomain($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Domain = $var;
+
+        return $this;
+    }
+
+    /**
+     * App的名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。domain和app二选一填。
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     * @return string
+     */
+    public function getApp()
+    {
+        return $this->App;
+    }
+
+    /**
+     * App的名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。domain和app二选一填。
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setApp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->App = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DeleteDomainRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DeleteDomainRequest.php
new file mode 100755
index 0000000..03d746b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DeleteDomainRequest.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.DeleteDomainRequest</code>
+ */
+class DeleteDomainRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *待删除域名。
+     *
+     * Generated from protobuf field <code>string Domain = 1;</code>
+     */
+    protected $Domain = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Domain
+     *          待删除域名。
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *待删除域名。
+     *
+     * Generated from protobuf field <code>string Domain = 1;</code>
+     * @return string
+     */
+    public function getDomain()
+    {
+        return $this->Domain;
+    }
+
+    /**
+     *待删除域名。
+     *
+     * Generated from protobuf field <code>string Domain = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomain($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Domain = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DeletePullToPushTaskRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DeletePullToPushTaskRequest.php
new file mode 100755
index 0000000..043358d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DeletePullToPushTaskRequest.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.DeletePullToPushTaskRequest</code>
+ */
+class DeletePullToPushTaskRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *任务ID
+     *
+     * Generated from protobuf field <code>string TaskId = 1;</code>
+     */
+    protected $TaskId = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $TaskId
+     *          任务ID
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *任务ID
+     *
+     * Generated from protobuf field <code>string TaskId = 1;</code>
+     * @return string
+     */
+    public function getTaskId()
+    {
+        return $this->TaskId;
+    }
+
+    /**
+     *任务ID
+     *
+     * Generated from protobuf field <code>string TaskId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTaskId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TaskId = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DeleteRelaySourceRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DeleteRelaySourceRequest.php
new file mode 100755
index 0000000..28b919f
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DeleteRelaySourceRequest.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.DeleteRelaySourceRequest</code>
+ */
+class DeleteRelaySourceRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 域名空间名称。
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     */
+    protected $Vhost = '';
+    /**
+     * 应用名称,由 1 到 30 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 2;</code>
+     */
+    protected $App = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vhost
+     *           域名空间名称。
+     *     @type string $App
+     *           应用名称,由 1 到 30 位数字、字母、下划线及"-"和"."组成。
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 域名空间名称。
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @return string
+     */
+    public function getVhost()
+    {
+        return $this->Vhost;
+    }
+
+    /**
+     * 域名空间名称。
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVhost($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vhost = $var;
+
+        return $this;
+    }
+
+    /**
+     * 应用名称,由 1 到 30 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 2;</code>
+     * @return string
+     */
+    public function getApp()
+    {
+        return $this->App;
+    }
+
+    /**
+     * 应用名称,由 1 到 30 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setApp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->App = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeCDNSnapshotHistoryRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeCDNSnapshotHistoryRequest.php
new file mode 100755
index 0000000..15b0b5b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeCDNSnapshotHistoryRequest.php
@@ -0,0 +1,323 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.DescribeCDNSnapshotHistoryRequest</code>
+ */
+class DescribeCDNSnapshotHistoryRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *域名空间名称。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     */
+    public $Vhost = '';
+    /**
+     * 应用名称,默认查询所有应用名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 2;</code>
+     */
+    public $App = '';
+    /**
+     * 流名称,缺省情况下,查询所有流名称,由 1 到 100
+     * 位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定 App 的值
+     *
+     * Generated from protobuf field <code>string Stream = 3;</code>
+     */
+    public $Stream = '';
+    /**
+     *查询时间范围的开始时间
+     *
+     * Generated from protobuf field <code>string DateFrom = 4;</code>
+     */
+    public $DateFrom = '';
+    /**
+     *查询时间范围的结束时间
+     *
+     * Generated from protobuf field <code>string DateTo = 5;</code>
+     */
+    public $DateTo = '';
+    /**
+     *当前页码,取值范围为 。
+     *
+     * Generated from protobuf field <code>int32 PageNum = 6;</code>
+     */
+    public $PageNum = 0;
+    /**
+     *分页大小,取值范围为 。
+     *
+     * Generated from protobuf field <code>int32 PageSize = 7;</code>
+     */
+    public $PageSize = 0;
+    /**
+     * 截图文件保存位置。默认取值为 ToS。
+     *<li>ToS
+     *<li>ImageX
+     *
+     * Generated from protobuf field <code>string Type = 8;</code>
+     */
+    public $Type = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vhost
+     *          域名空间名称。
+     *          参数 `Domain` 和 `Vhost`传且仅传一个。
+     *     @type string $App
+     *           应用名称,默认查询所有应用名称,由 1 到 30
+     *           位数字、字母、下划线及"-"和"."组成。
+     *     @type string $Stream
+     *           流名称,缺省情况下,查询所有流名称,由 1 到 100
+     *           位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定 App 的值
+     *     @type string $DateFrom
+     *          查询时间范围的开始时间
+     *     @type string $DateTo
+     *          查询时间范围的结束时间
+     *     @type int $PageNum
+     *          当前页码,取值范围为 。
+     *     @type int $PageSize
+     *          分页大小,取值范围为 。
+     *     @type string $Type
+     *           截图文件保存位置。默认取值为 ToS。
+     *          <li>ToS
+     *          <li>ImageX
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *域名空间名称。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @return string
+     */
+    public function getVhost()
+    {
+        return $this->Vhost;
+    }
+
+    /**
+     *域名空间名称。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVhost($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vhost = $var;
+
+        return $this;
+    }
+
+    /**
+     * 应用名称,默认查询所有应用名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 2;</code>
+     * @return string
+     */
+    public function getApp()
+    {
+        return $this->App;
+    }
+
+    /**
+     * 应用名称,默认查询所有应用名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setApp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->App = $var;
+
+        return $this;
+    }
+
+    /**
+     * 流名称,缺省情况下,查询所有流名称,由 1 到 100
+     * 位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定 App 的值
+     *
+     * Generated from protobuf field <code>string Stream = 3;</code>
+     * @return string
+     */
+    public function getStream()
+    {
+        return $this->Stream;
+    }
+
+    /**
+     * 流名称,缺省情况下,查询所有流名称,由 1 到 100
+     * 位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定 App 的值
+     *
+     * Generated from protobuf field <code>string Stream = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStream($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Stream = $var;
+
+        return $this;
+    }
+
+    /**
+     *查询时间范围的开始时间
+     *
+     * Generated from protobuf field <code>string DateFrom = 4;</code>
+     * @return string
+     */
+    public function getDateFrom()
+    {
+        return $this->DateFrom;
+    }
+
+    /**
+     *查询时间范围的开始时间
+     *
+     * Generated from protobuf field <code>string DateFrom = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDateFrom($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->DateFrom = $var;
+
+        return $this;
+    }
+
+    /**
+     *查询时间范围的结束时间
+     *
+     * Generated from protobuf field <code>string DateTo = 5;</code>
+     * @return string
+     */
+    public function getDateTo()
+    {
+        return $this->DateTo;
+    }
+
+    /**
+     *查询时间范围的结束时间
+     *
+     * Generated from protobuf field <code>string DateTo = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDateTo($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->DateTo = $var;
+
+        return $this;
+    }
+
+    /**
+     *当前页码,取值范围为 。
+     *
+     * Generated from protobuf field <code>int32 PageNum = 6;</code>
+     * @return int
+     */
+    public function getPageNum()
+    {
+        return $this->PageNum;
+    }
+
+    /**
+     *当前页码,取值范围为 。
+     *
+     * Generated from protobuf field <code>int32 PageNum = 6;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setPageNum($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->PageNum = $var;
+
+        return $this;
+    }
+
+    /**
+     *分页大小,取值范围为 。
+     *
+     * Generated from protobuf field <code>int32 PageSize = 7;</code>
+     * @return int
+     */
+    public function getPageSize()
+    {
+        return $this->PageSize;
+    }
+
+    /**
+     *分页大小,取值范围为 。
+     *
+     * Generated from protobuf field <code>int32 PageSize = 7;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setPageSize($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->PageSize = $var;
+
+        return $this;
+    }
+
+    /**
+     * 截图文件保存位置。默认取值为 ToS。
+     *<li>ToS
+     *<li>ImageX
+     *
+     * Generated from protobuf field <code>string Type = 8;</code>
+     * @return string
+     */
+    public function getType()
+    {
+        return $this->Type;
+    }
+
+    /**
+     * 截图文件保存位置。默认取值为 ToS。
+     *<li>ToS
+     *<li>ImageX
+     *
+     * Generated from protobuf field <code>string Type = 8;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Type = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeClosedStreamInfoByPageRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeClosedStreamInfoByPageRequest.php
new file mode 100755
index 0000000..6e7b9be
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeClosedStreamInfoByPageRequest.php
@@ -0,0 +1,411 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.DescribeClosedStreamInfoByPageRequest</code>
+ */
+class DescribeClosedStreamInfoByPageRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *当前页码,取值范围 ≥1。
+     *
+     * Generated from protobuf field <code>int64 PageNum = 1;</code>
+     */
+    protected $PageNum = 0;
+    /**
+     *分页大小,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageSize = 2;</code>
+     */
+    protected $PageSize = 0;
+    /**
+     *域名空间名称。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Vhost = 3;</code>
+     */
+    protected $Vhost = '';
+    /**
+     *推流域名。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Domain = 4;</code>
+     */
+    protected $Domain = '';
+    /**
+     * 应用名称,默认查询所有应用名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 5;</code>
+     */
+    protected $App = '';
+    /**
+     * 流名称,缺省情况下,查询所有流名称,由 1 到 100
+     * 位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定 App 的值
+     *
+     * Generated from protobuf field <code>string Stream = 6;</code>
+     */
+    protected $Stream = '';
+    /**
+     * 排列方式,根据结束时间排序。支持两种形式。缺省情况下为 desc。
+     *<li>asc:按从小到大升序排列;
+     *<li>desc:按从大到小降序排列。
+     *
+     * Generated from protobuf field <code>string Sort = 7;</code>
+     */
+    protected $Sort = '';
+    /**
+     * 查询的起始时间,以 RFC3339 格式表示的 UTC
+     * 时间戳,例如,2021-10-02T19:39:26Z。筛选结束推流时间符合查询条件的历史流
+     *
+     * Generated from protobuf field <code>string EndTimeFrom = 8;</code>
+     */
+    protected $EndTimeFrom = '';
+    /**
+     * 查询的结束时间,以 RFC3339 格式表示的 UTC
+     * 时间戳,例如,2021-10-02T19:39:26Z。筛选结束推流时间符合查询条件的历史流
+     *
+     * Generated from protobuf field <code>string EndTimeTo = 9;</code>
+     */
+    protected $EndTimeTo = '';
+    /**
+     * 表示推流方式,缺省情况查询全部推流方式。支持如下取值。
+     *<li>push:直推流;
+     *<li>relay:回源流。
+     *
+     * Generated from protobuf field <code>string SourceType = 10;</code>
+     */
+    protected $SourceType = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int|string $PageNum
+     *          当前页码,取值范围 ≥1。
+     *     @type int|string $PageSize
+     *          分页大小,取值范围为 。
+     *     @type string $Vhost
+     *          域名空间名称。
+     *          参数 `Domain` 和 `Vhost`传且仅传一个。
+     *     @type string $Domain
+     *          推流域名。
+     *          参数 `Domain` 和 `Vhost`传且仅传一个。
+     *     @type string $App
+     *           应用名称,默认查询所有应用名称,由 1 到 30
+     *           位数字、字母、下划线及"-"和"."组成。
+     *     @type string $Stream
+     *           流名称,缺省情况下,查询所有流名称,由 1 到 100
+     *           位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定 App 的值
+     *     @type string $Sort
+     *           排列方式,根据结束时间排序。支持两种形式。缺省情况下为 desc。
+     *          <li>asc:按从小到大升序排列;
+     *          <li>desc:按从大到小降序排列。
+     *     @type string $EndTimeFrom
+     *           查询的起始时间,以 RFC3339 格式表示的 UTC
+     *           时间戳,例如,2021-10-02T19:39:26Z。筛选结束推流时间符合查询条件的历史流
+     *     @type string $EndTimeTo
+     *           查询的结束时间,以 RFC3339 格式表示的 UTC
+     *           时间戳,例如,2021-10-02T19:39:26Z。筛选结束推流时间符合查询条件的历史流
+     *     @type string $SourceType
+     *           表示推流方式,缺省情况查询全部推流方式。支持如下取值。
+     *          <li>push:直推流;
+     *          <li>relay:回源流。
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *当前页码,取值范围 ≥1。
+     *
+     * Generated from protobuf field <code>int64 PageNum = 1;</code>
+     * @return int|string
+     */
+    public function getPageNum()
+    {
+        return $this->PageNum;
+    }
+
+    /**
+     *当前页码,取值范围 ≥1。
+     *
+     * Generated from protobuf field <code>int64 PageNum = 1;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setPageNum($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->PageNum = $var;
+
+        return $this;
+    }
+
+    /**
+     *分页大小,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageSize = 2;</code>
+     * @return int|string
+     */
+    public function getPageSize()
+    {
+        return $this->PageSize;
+    }
+
+    /**
+     *分页大小,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageSize = 2;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setPageSize($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->PageSize = $var;
+
+        return $this;
+    }
+
+    /**
+     *域名空间名称。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Vhost = 3;</code>
+     * @return string
+     */
+    public function getVhost()
+    {
+        return $this->Vhost;
+    }
+
+    /**
+     *域名空间名称。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Vhost = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVhost($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vhost = $var;
+
+        return $this;
+    }
+
+    /**
+     *推流域名。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Domain = 4;</code>
+     * @return string
+     */
+    public function getDomain()
+    {
+        return $this->Domain;
+    }
+
+    /**
+     *推流域名。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Domain = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomain($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Domain = $var;
+
+        return $this;
+    }
+
+    /**
+     * 应用名称,默认查询所有应用名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 5;</code>
+     * @return string
+     */
+    public function getApp()
+    {
+        return $this->App;
+    }
+
+    /**
+     * 应用名称,默认查询所有应用名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setApp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->App = $var;
+
+        return $this;
+    }
+
+    /**
+     * 流名称,缺省情况下,查询所有流名称,由 1 到 100
+     * 位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定 App 的值
+     *
+     * Generated from protobuf field <code>string Stream = 6;</code>
+     * @return string
+     */
+    public function getStream()
+    {
+        return $this->Stream;
+    }
+
+    /**
+     * 流名称,缺省情况下,查询所有流名称,由 1 到 100
+     * 位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定 App 的值
+     *
+     * Generated from protobuf field <code>string Stream = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStream($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Stream = $var;
+
+        return $this;
+    }
+
+    /**
+     * 排列方式,根据结束时间排序。支持两种形式。缺省情况下为 desc。
+     *<li>asc:按从小到大升序排列;
+     *<li>desc:按从大到小降序排列。
+     *
+     * Generated from protobuf field <code>string Sort = 7;</code>
+     * @return string
+     */
+    public function getSort()
+    {
+        return $this->Sort;
+    }
+
+    /**
+     * 排列方式,根据结束时间排序。支持两种形式。缺省情况下为 desc。
+     *<li>asc:按从小到大升序排列;
+     *<li>desc:按从大到小降序排列。
+     *
+     * Generated from protobuf field <code>string Sort = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSort($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Sort = $var;
+
+        return $this;
+    }
+
+    /**
+     * 查询的起始时间,以 RFC3339 格式表示的 UTC
+     * 时间戳,例如,2021-10-02T19:39:26Z。筛选结束推流时间符合查询条件的历史流
+     *
+     * Generated from protobuf field <code>string EndTimeFrom = 8;</code>
+     * @return string
+     */
+    public function getEndTimeFrom()
+    {
+        return $this->EndTimeFrom;
+    }
+
+    /**
+     * 查询的起始时间,以 RFC3339 格式表示的 UTC
+     * 时间戳,例如,2021-10-02T19:39:26Z。筛选结束推流时间符合查询条件的历史流
+     *
+     * Generated from protobuf field <code>string EndTimeFrom = 8;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setEndTimeFrom($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->EndTimeFrom = $var;
+
+        return $this;
+    }
+
+    /**
+     * 查询的结束时间,以 RFC3339 格式表示的 UTC
+     * 时间戳,例如,2021-10-02T19:39:26Z。筛选结束推流时间符合查询条件的历史流
+     *
+     * Generated from protobuf field <code>string EndTimeTo = 9;</code>
+     * @return string
+     */
+    public function getEndTimeTo()
+    {
+        return $this->EndTimeTo;
+    }
+
+    /**
+     * 查询的结束时间,以 RFC3339 格式表示的 UTC
+     * 时间戳,例如,2021-10-02T19:39:26Z。筛选结束推流时间符合查询条件的历史流
+     *
+     * Generated from protobuf field <code>string EndTimeTo = 9;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setEndTimeTo($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->EndTimeTo = $var;
+
+        return $this;
+    }
+
+    /**
+     * 表示推流方式,缺省情况查询全部推流方式。支持如下取值。
+     *<li>push:直推流;
+     *<li>relay:回源流。
+     *
+     * Generated from protobuf field <code>string SourceType = 10;</code>
+     * @return string
+     */
+    public function getSourceType()
+    {
+        return $this->SourceType;
+    }
+
+    /**
+     * 表示推流方式,缺省情况查询全部推流方式。支持如下取值。
+     *<li>push:直推流;
+     *<li>relay:回源流。
+     *
+     * Generated from protobuf field <code>string SourceType = 10;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSourceType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SourceType = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeDenyConfigRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeDenyConfigRequest.php
new file mode 100755
index 0000000..ec0f7f7
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeDenyConfigRequest.php
@@ -0,0 +1,137 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.DescribeDenyConfigRequest</code>
+ */
+class DescribeDenyConfigRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     */
+    protected $Vhost = '';
+    /**
+     *推拉流域名,domain和app二选一填
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     */
+    protected $Domain = '';
+    /**
+     * App的名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。domain和app二选一填。
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     */
+    protected $App = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vhost
+     *          域名空间名称
+     *     @type string $Domain
+     *          推拉流域名,domain和app二选一填
+     *     @type string $App
+     *           App的名称,由 1 到 30
+     *           位数字、字母、下划线及"-"和"."组成。domain和app二选一填。
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @return string
+     */
+    public function getVhost()
+    {
+        return $this->Vhost;
+    }
+
+    /**
+     *域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVhost($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vhost = $var;
+
+        return $this;
+    }
+
+    /**
+     *推拉流域名,domain和app二选一填
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     * @return string
+     */
+    public function getDomain()
+    {
+        return $this->Domain;
+    }
+
+    /**
+     *推拉流域名,domain和app二选一填
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomain($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Domain = $var;
+
+        return $this;
+    }
+
+    /**
+     * App的名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。domain和app二选一填。
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     * @return string
+     */
+    public function getApp()
+    {
+        return $this->App;
+    }
+
+    /**
+     * App的名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。domain和app二选一填。
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setApp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->App = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeDomainRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeDomainRequest.php
new file mode 100755
index 0000000..6d43dad
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeDomainRequest.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.DescribeDomainRequest</code>
+ */
+class DescribeDomainRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *域名列表。
+     *
+     * Generated from protobuf field <code>repeated string DomainList = 1;</code>
+     */
+    private $DomainList;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $DomainList
+     *          域名列表。
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *域名列表。
+     *
+     * Generated from protobuf field <code>repeated string DomainList = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getDomainList()
+    {
+        return $this->DomainList;
+    }
+
+    /**
+     *域名列表。
+     *
+     * Generated from protobuf field <code>repeated string DomainList = 1;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setDomainList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->DomainList = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeForbiddenStreamInfoByPageRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeForbiddenStreamInfoByPageRequest.php
new file mode 100755
index 0000000..840d3aa
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeForbiddenStreamInfoByPageRequest.php
@@ -0,0 +1,255 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.DescribeForbiddenStreamInfoByPageRequest</code>
+ */
+class DescribeForbiddenStreamInfoByPageRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *当前页码,取值范围 ≥1。
+     *
+     * Generated from protobuf field <code>int64 PageNum = 1;</code>
+     */
+    protected $PageNum = 0;
+    /**
+     *分页大小,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageSize = 2;</code>
+     */
+    protected $PageSize = 0;
+    /**
+     *域名空间名称。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Vhost = 3;</code>
+     */
+    protected $Vhost = '';
+    /**
+     * 应用名称,默认查询所有应用名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 4;</code>
+     */
+    protected $App = '';
+    /**
+     * 流名称,缺省情况下,查询所有流名称,由 1 到 100
+     * 位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定 App 的值
+     *
+     * Generated from protobuf field <code>string Stream = 5;</code>
+     */
+    protected $Stream = '';
+    /**
+     * 排列方式,根据结束时间排序。支持两种形式。缺省情况下为 desc。
+     *<li>asc:按从小到大升序排列;
+     *<li>desc:按从大到小降序排列。
+     *
+     * Generated from protobuf field <code>string Sort = 6;</code>
+     */
+    protected $Sort = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int|string $PageNum
+     *          当前页码,取值范围 ≥1。
+     *     @type int|string $PageSize
+     *          分页大小,取值范围为 。
+     *     @type string $Vhost
+     *          域名空间名称。
+     *          参数 `Domain` 和 `Vhost`传且仅传一个。
+     *     @type string $App
+     *           应用名称,默认查询所有应用名称,由 1 到 30
+     *           位数字、字母、下划线及"-"和"."组成。
+     *     @type string $Stream
+     *           流名称,缺省情况下,查询所有流名称,由 1 到 100
+     *           位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定 App 的值
+     *     @type string $Sort
+     *           排列方式,根据结束时间排序。支持两种形式。缺省情况下为 desc。
+     *          <li>asc:按从小到大升序排列;
+     *          <li>desc:按从大到小降序排列。
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *当前页码,取值范围 ≥1。
+     *
+     * Generated from protobuf field <code>int64 PageNum = 1;</code>
+     * @return int|string
+     */
+    public function getPageNum()
+    {
+        return $this->PageNum;
+    }
+
+    /**
+     *当前页码,取值范围 ≥1。
+     *
+     * Generated from protobuf field <code>int64 PageNum = 1;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setPageNum($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->PageNum = $var;
+
+        return $this;
+    }
+
+    /**
+     *分页大小,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageSize = 2;</code>
+     * @return int|string
+     */
+    public function getPageSize()
+    {
+        return $this->PageSize;
+    }
+
+    /**
+     *分页大小,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageSize = 2;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setPageSize($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->PageSize = $var;
+
+        return $this;
+    }
+
+    /**
+     *域名空间名称。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Vhost = 3;</code>
+     * @return string
+     */
+    public function getVhost()
+    {
+        return $this->Vhost;
+    }
+
+    /**
+     *域名空间名称。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Vhost = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVhost($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vhost = $var;
+
+        return $this;
+    }
+
+    /**
+     * 应用名称,默认查询所有应用名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 4;</code>
+     * @return string
+     */
+    public function getApp()
+    {
+        return $this->App;
+    }
+
+    /**
+     * 应用名称,默认查询所有应用名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setApp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->App = $var;
+
+        return $this;
+    }
+
+    /**
+     * 流名称,缺省情况下,查询所有流名称,由 1 到 100
+     * 位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定 App 的值
+     *
+     * Generated from protobuf field <code>string Stream = 5;</code>
+     * @return string
+     */
+    public function getStream()
+    {
+        return $this->Stream;
+    }
+
+    /**
+     * 流名称,缺省情况下,查询所有流名称,由 1 到 100
+     * 位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定 App 的值
+     *
+     * Generated from protobuf field <code>string Stream = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStream($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Stream = $var;
+
+        return $this;
+    }
+
+    /**
+     * 排列方式,根据结束时间排序。支持两种形式。缺省情况下为 desc。
+     *<li>asc:按从小到大升序排列;
+     *<li>desc:按从大到小降序排列。
+     *
+     * Generated from protobuf field <code>string Sort = 6;</code>
+     * @return string
+     */
+    public function getSort()
+    {
+        return $this->Sort;
+    }
+
+    /**
+     * 排列方式,根据结束时间排序。支持两种形式。缺省情况下为 desc。
+     *<li>asc:按从小到大升序排列;
+     *<li>desc:按从大到小降序排列。
+     *
+     * Generated from protobuf field <code>string Sort = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSort($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Sort = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeLiveStreamInfoByPageRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeLiveStreamInfoByPageRequest.php
new file mode 100755
index 0000000..30e2bd0
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeLiveStreamInfoByPageRequest.php
@@ -0,0 +1,347 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.DescribeLiveStreamInfoByPageRequest</code>
+ */
+class DescribeLiveStreamInfoByPageRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *当前页码,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageNum = 1;</code>
+     */
+    protected $PageNum = 0;
+    /**
+     *分页大小,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageSize = 2;</code>
+     */
+    protected $PageSize = 0;
+    /**
+     *域名空间名称。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Vhost = 3;</code>
+     */
+    protected $Vhost = '';
+    /**
+     *推流域名。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Domain = 4;</code>
+     */
+    protected $Domain = '';
+    /**
+     * 应用名称,默认查询所有应用名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 5;</code>
+     */
+    protected $App = '';
+    /**
+     * 流名称,缺省情况下,查询所有流名称,由 1 到 100
+     * 位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定 App 的值
+     *
+     * Generated from protobuf field <code>string Stream = 6;</code>
+     */
+    protected $Stream = '';
+    /**
+     * 流类型,缺省情况下表示全选。支持如下取值。
+     *<li>Origin:原始流;
+     *<li>trans:转码流。
+     *
+     * Generated from protobuf field <code>string StreamType = 7;</code>
+     */
+    protected $StreamType = '';
+    /**
+     * 想要查询的目标信息,使用英文逗号作为分隔符“,”,例如,bitrate,framerate。缺省情况下表示
+     *bitrate,framerate。支持如下取值。 <li>all:所有信息;
+     *<li>onlineuser:在线人数;
+     *<li>bandwidth:带宽信息;
+     *<li>bitrate:码率信息;
+     *<li>framerate:帧率信息;
+     *
+     * Generated from protobuf field <code>string InfoType = 8;</code>
+     */
+    protected $InfoType = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int|string $PageNum
+     *          当前页码,取值范围为 。
+     *     @type int|string $PageSize
+     *          分页大小,取值范围为 。
+     *     @type string $Vhost
+     *          域名空间名称。
+     *          参数 `Domain` 和 `Vhost`传且仅传一个。
+     *     @type string $Domain
+     *          推流域名。
+     *          参数 `Domain` 和 `Vhost`传且仅传一个。
+     *     @type string $App
+     *           应用名称,默认查询所有应用名称,由 1 到 30
+     *           位数字、字母、下划线及"-"和"."组成。
+     *     @type string $Stream
+     *           流名称,缺省情况下,查询所有流名称,由 1 到 100
+     *           位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定 App 的值
+     *     @type string $StreamType
+     *           流类型,缺省情况下表示全选。支持如下取值。
+     *          <li>Origin:原始流;
+     *          <li>trans:转码流。
+     *     @type string $InfoType
+     *           想要查询的目标信息,使用英文逗号作为分隔符“,”,例如,bitrate,framerate。缺省情况下表示
+     *          bitrate,framerate。支持如下取值。 <li>all:所有信息;
+     *          <li>onlineuser:在线人数;
+     *          <li>bandwidth:带宽信息;
+     *          <li>bitrate:码率信息;
+     *          <li>framerate:帧率信息;
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *当前页码,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageNum = 1;</code>
+     * @return int|string
+     */
+    public function getPageNum()
+    {
+        return $this->PageNum;
+    }
+
+    /**
+     *当前页码,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageNum = 1;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setPageNum($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->PageNum = $var;
+
+        return $this;
+    }
+
+    /**
+     *分页大小,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageSize = 2;</code>
+     * @return int|string
+     */
+    public function getPageSize()
+    {
+        return $this->PageSize;
+    }
+
+    /**
+     *分页大小,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageSize = 2;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setPageSize($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->PageSize = $var;
+
+        return $this;
+    }
+
+    /**
+     *域名空间名称。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Vhost = 3;</code>
+     * @return string
+     */
+    public function getVhost()
+    {
+        return $this->Vhost;
+    }
+
+    /**
+     *域名空间名称。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Vhost = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVhost($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vhost = $var;
+
+        return $this;
+    }
+
+    /**
+     *推流域名。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Domain = 4;</code>
+     * @return string
+     */
+    public function getDomain()
+    {
+        return $this->Domain;
+    }
+
+    /**
+     *推流域名。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Domain = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomain($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Domain = $var;
+
+        return $this;
+    }
+
+    /**
+     * 应用名称,默认查询所有应用名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 5;</code>
+     * @return string
+     */
+    public function getApp()
+    {
+        return $this->App;
+    }
+
+    /**
+     * 应用名称,默认查询所有应用名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setApp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->App = $var;
+
+        return $this;
+    }
+
+    /**
+     * 流名称,缺省情况下,查询所有流名称,由 1 到 100
+     * 位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定 App 的值
+     *
+     * Generated from protobuf field <code>string Stream = 6;</code>
+     * @return string
+     */
+    public function getStream()
+    {
+        return $this->Stream;
+    }
+
+    /**
+     * 流名称,缺省情况下,查询所有流名称,由 1 到 100
+     * 位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定 App 的值
+     *
+     * Generated from protobuf field <code>string Stream = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStream($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Stream = $var;
+
+        return $this;
+    }
+
+    /**
+     * 流类型,缺省情况下表示全选。支持如下取值。
+     *<li>Origin:原始流;
+     *<li>trans:转码流。
+     *
+     * Generated from protobuf field <code>string StreamType = 7;</code>
+     * @return string
+     */
+    public function getStreamType()
+    {
+        return $this->StreamType;
+    }
+
+    /**
+     * 流类型,缺省情况下表示全选。支持如下取值。
+     *<li>Origin:原始流;
+     *<li>trans:转码流。
+     *
+     * Generated from protobuf field <code>string StreamType = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStreamType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StreamType = $var;
+
+        return $this;
+    }
+
+    /**
+     * 想要查询的目标信息,使用英文逗号作为分隔符“,”,例如,bitrate,framerate。缺省情况下表示
+     *bitrate,framerate。支持如下取值。 <li>all:所有信息;
+     *<li>onlineuser:在线人数;
+     *<li>bandwidth:带宽信息;
+     *<li>bitrate:码率信息;
+     *<li>framerate:帧率信息;
+     *
+     * Generated from protobuf field <code>string InfoType = 8;</code>
+     * @return string
+     */
+    public function getInfoType()
+    {
+        return $this->InfoType;
+    }
+
+    /**
+     * 想要查询的目标信息,使用英文逗号作为分隔符“,”,例如,bitrate,framerate。缺省情况下表示
+     *bitrate,framerate。支持如下取值。 <li>all:所有信息;
+     *<li>onlineuser:在线人数;
+     *<li>bandwidth:带宽信息;
+     *<li>bitrate:码率信息;
+     *<li>framerate:帧率信息;
+     *
+     * Generated from protobuf field <code>string InfoType = 8;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setInfoType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->InfoType = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeLiveStreamStateRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeLiveStreamStateRequest.php
new file mode 100755
index 0000000..4e0f6dd
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeLiveStreamStateRequest.php
@@ -0,0 +1,175 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.DescribeLiveStreamStateRequest</code>
+ */
+class DescribeLiveStreamStateRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     */
+    protected $Vhost = '';
+    /**
+     *推流域名。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     */
+    protected $Domain = '';
+    /**
+     *应用名称,由 1 到 30 位数字、字母、下划线及"-"和"."组成
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     */
+    protected $App = '';
+    /**
+     *流名称。由 1 到 100 位数字、字母、下划线及"-"和"."组成,如果指定
+     * Stream,必须同时指定 App 的值
+     *
+     * Generated from protobuf field <code>string Stream = 4;</code>
+     */
+    protected $Stream = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vhost
+     *           域名空间名称
+     *     @type string $Domain
+     *          推流域名。
+     *          参数 `Domain` 和 `Vhost`传且仅传一个。
+     *     @type string $App
+     *          应用名称,由 1 到 30 位数字、字母、下划线及"-"和"."组成
+     *     @type string $Stream
+     *          流名称。由 1 到 100 位数字、字母、下划线及"-"和"."组成,如果指定
+     *           Stream,必须同时指定 App 的值
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @return string
+     */
+    public function getVhost()
+    {
+        return $this->Vhost;
+    }
+
+    /**
+     * 域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVhost($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vhost = $var;
+
+        return $this;
+    }
+
+    /**
+     *推流域名。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     * @return string
+     */
+    public function getDomain()
+    {
+        return $this->Domain;
+    }
+
+    /**
+     *推流域名。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomain($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Domain = $var;
+
+        return $this;
+    }
+
+    /**
+     *应用名称,由 1 到 30 位数字、字母、下划线及"-"和"."组成
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     * @return string
+     */
+    public function getApp()
+    {
+        return $this->App;
+    }
+
+    /**
+     *应用名称,由 1 到 30 位数字、字母、下划线及"-"和"."组成
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setApp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->App = $var;
+
+        return $this;
+    }
+
+    /**
+     *流名称。由 1 到 100 位数字、字母、下划线及"-"和"."组成,如果指定
+     * Stream,必须同时指定 App 的值
+     *
+     * Generated from protobuf field <code>string Stream = 4;</code>
+     * @return string
+     */
+    public function getStream()
+    {
+        return $this->Stream;
+    }
+
+    /**
+     *流名称。由 1 到 100 位数字、字母、下划线及"-"和"."组成,如果指定
+     * Stream,必须同时指定 App 的值
+     *
+     * Generated from protobuf field <code>string Stream = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStream($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Stream = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeRecordTaskFileHistoryRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeRecordTaskFileHistoryRequest.php
new file mode 100755
index 0000000..111b20e
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeRecordTaskFileHistoryRequest.php
@@ -0,0 +1,327 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.DescribeRecordTaskFileHistoryRequest</code>
+ */
+class DescribeRecordTaskFileHistoryRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *域名空间名称。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     */
+    public $Vhost = '';
+    /**
+     * 应用名称,默认查询所有应用名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 2;</code>
+     */
+    public $App = '';
+    /**
+     * 流名称,缺省情况下,查询所有流名称,由 1 到 100
+     * 位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定 App 的值
+     *
+     * Generated from protobuf field <code>string Stream = 3;</code>
+     */
+    public $Stream = '';
+    /**
+     *查询时间范围的开始时间
+     *
+     * Generated from protobuf field <code>string DateFrom = 4;</code>
+     */
+    public $DateFrom = '';
+    /**
+     *查询时间范围的结束时间
+     *
+     * Generated from protobuf field <code>string DateTo = 5;</code>
+     */
+    public $DateTo = '';
+    /**
+     *当前页码,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageNum = 6;</code>
+     */
+    public float $PageNum = 0;
+    /**
+     *分页大小,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageSize = 7;</code>
+     */
+    public float $PageSize = 0;
+    /**
+     * 录制文件保存位置。默认取值为 ToS。
+     *<li>ToS
+     *<li>ImageX
+     *
+     * Generated from protobuf field <code>string Type = 8;</code>
+     */
+    public $Type = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vhost
+     *          域名空间名称。
+     *          参数 `Domain` 和 `Vhost`传且仅传一个。
+     *     @type string $App
+     *           应用名称,默认查询所有应用名称,由 1 到 30
+     *           位数字、字母、下划线及"-"和"."组成。
+     *     @type string $Stream
+     *           流名称,缺省情况下,查询所有流名称,由 1 到 100
+     *           位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定 App 的值
+     *     @type string $DateFrom
+     *          查询时间范围的开始时间
+     *     @type string $DateTo
+     *          查询时间范围的结束时间
+     *     @type int|float $PageNum
+     *          当前页码,取值范围为 。
+     *     @type int|float $PageSize
+     *          分页大小,取值范围为 。
+     *     @type string $Type
+     *           录制文件保存位置。默认取值为 ToS。
+     *          <li>ToS
+     *          <li>ImageX
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *域名空间名称。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @return string
+     */
+    public function getVhost()
+    {
+        return $this->Vhost;
+    }
+
+    /**
+     *域名空间名称。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVhost($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vhost = $var;
+
+        return $this;
+    }
+
+    /**
+     * 应用名称,默认查询所有应用名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 2;</code>
+     * @return string
+     */
+    public function getApp()
+    {
+        return $this->App;
+    }
+
+    /**
+     * 应用名称,默认查询所有应用名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setApp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->App = $var;
+
+        return $this;
+    }
+
+    /**
+     * 流名称,缺省情况下,查询所有流名称,由 1 到 100
+     * 位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定 App 的值
+     *
+     * Generated from protobuf field <code>string Stream = 3;</code>
+     * @return string
+     */
+    public function getStream()
+    {
+        return $this->Stream;
+    }
+
+    /**
+     * 流名称,缺省情况下,查询所有流名称,由 1 到 100
+     * 位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定 App 的值
+     *
+     * Generated from protobuf field <code>string Stream = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStream($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Stream = $var;
+
+        return $this;
+    }
+
+    /**
+     *查询时间范围的开始时间
+     *
+     * Generated from protobuf field <code>string DateFrom = 4;</code>
+     * @return string
+     */
+    public function getDateFrom()
+    {
+        return $this->DateFrom;
+    }
+
+    /**
+     *查询时间范围的开始时间
+     *
+     * Generated from protobuf field <code>string DateFrom = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDateFrom($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->DateFrom = $var;
+
+        return $this;
+    }
+
+    /**
+     *查询时间范围的结束时间
+     *
+     * Generated from protobuf field <code>string DateTo = 5;</code>
+     * @return string
+     */
+    public function getDateTo()
+    {
+        return $this->DateTo;
+    }
+
+    /**
+     *查询时间范围的结束时间
+     *
+     * Generated from protobuf field <code>string DateTo = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDateTo($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->DateTo = $var;
+
+        return $this;
+    }
+
+    /**
+     *当前页码,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageNum = 6;</code>
+     * @return int|string
+     */
+    public function getPageNum()
+    {
+        return $this->PageNum;
+    }
+
+    /**
+     *当前页码,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageNum = 6;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setPageNum($var)
+    {
+        GPBUtil::checkFloat($var);
+        $this->PageNum = $var;
+        var_dump( "$this->PageNum");
+        var_dump( $this->PageNum);
+
+        return $this;
+    }
+
+    /**
+     *分页大小,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageSize = 7;</code>
+     * @return int|string
+     */
+    public function getPageSize()
+    {
+        return $this->float;
+    }
+
+    /**
+     *分页大小,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageSize = 7;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setPageSize($var)
+    {
+        GPBUtil::checkFloat($var);
+        $this->PageSize = $var;
+        var_dump( "$this->PageSize");
+        var_dump( $this->PageSize);
+
+        return $this;
+    }
+
+    /**
+     * 录制文件保存位置。默认取值为 ToS。
+     *<li>ToS
+     *<li>ImageX
+     *
+     * Generated from protobuf field <code>string Type = 8;</code>
+     * @return string
+     */
+    public function getType()
+    {
+        return $this->Type;
+    }
+
+    /**
+     * 录制文件保存位置。默认取值为 ToS。
+     *<li>ToS
+     *<li>ImageX
+     *
+     * Generated from protobuf field <code>string Type = 8;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Type = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeRelaySourceRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeRelaySourceRequest.php
new file mode 100755
index 0000000..100bf20
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeRelaySourceRequest.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.DescribeRelaySourceRequest</code>
+ */
+class DescribeRelaySourceRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 域名空间名称。
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     */
+    protected $Vhost = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vhost
+     *           域名空间名称。
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 域名空间名称。
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @return string
+     */
+    public function getVhost()
+    {
+        return $this->Vhost;
+    }
+
+    /**
+     * 域名空间名称。
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVhost($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vhost = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeVQScoreTaskRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeVQScoreTaskRequest.php
new file mode 100755
index 0000000..4d5a90e
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DescribeVQScoreTaskRequest.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.DescribeVQScoreTaskRequest</code>
+ */
+class DescribeVQScoreTaskRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 测评任务ID
+     *
+     * Generated from protobuf field <code>string ID = 1;</code>
+     */
+    protected $ID = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $ID
+     *           测评任务ID
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 测评任务ID
+     *
+     * Generated from protobuf field <code>string ID = 1;</code>
+     * @return string
+     */
+    public function getID()
+    {
+        return $this->ID;
+    }
+
+    /**
+     * 测评任务ID
+     *
+     * Generated from protobuf field <code>string ID = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setID($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->ID = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DisableDomainRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DisableDomainRequest.php
new file mode 100755
index 0000000..cf8ec13
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/DisableDomainRequest.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.DisableDomainRequest</code>
+ */
+class DisableDomainRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *待禁用域名。
+     *
+     * Generated from protobuf field <code>string Domain = 1;</code>
+     */
+    protected $Domain = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Domain
+     *          待禁用域名。
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *待禁用域名。
+     *
+     * Generated from protobuf field <code>string Domain = 1;</code>
+     * @return string
+     */
+    public function getDomain()
+    {
+        return $this->Domain;
+    }
+
+    /**
+     *待禁用域名。
+     *
+     * Generated from protobuf field <code>string Domain = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomain($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Domain = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/EnableDomainRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/EnableDomainRequest.php
new file mode 100755
index 0000000..3e742c5
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/EnableDomainRequest.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.EnableDomainRequest</code>
+ */
+class EnableDomainRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *待启用域名。
+     *
+     * Generated from protobuf field <code>string Domain = 1;</code>
+     */
+    protected $Domain = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Domain
+     *          待启用域名。
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *待启用域名。
+     *
+     * Generated from protobuf field <code>string Domain = 1;</code>
+     * @return string
+     */
+    public function getDomain()
+    {
+        return $this->Domain;
+    }
+
+    /**
+     *待启用域名。
+     *
+     * Generated from protobuf field <code>string Domain = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomain($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Domain = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/ForbidStreamRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/ForbidStreamRequest.php
new file mode 100755
index 0000000..d1aa630
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/ForbidStreamRequest.php
@@ -0,0 +1,217 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.ForbidStreamRequest</code>
+ */
+class ForbidStreamRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     */
+    protected $Vhost = '';
+    /**
+     *推流域名。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     */
+    protected $Domain = '';
+    /**
+     * 应用名称,默认查询所有应用名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     */
+    protected $App = '';
+    /**
+     * 流名称,缺省情况下,查询所有流名称,由 1 到
+     * 100位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定
+     * App的值
+     *
+     * Generated from protobuf field <code>string Stream = 4;</code>
+     */
+    protected $Stream = '';
+    /**
+     * 禁推的结束时间,禁推有效期最长为 90 天,默认为当前时间加 90 天
+     *
+     * Generated from protobuf field <code>string EndTime = 5;</code>
+     */
+    protected $EndTime = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vhost
+     *           域名空间名称
+     *     @type string $Domain
+     *          推流域名。
+     *          参数 `Domain` 和 `Vhost`传且仅传一个。
+     *     @type string $App
+     *           应用名称,默认查询所有应用名称,由 1 到 30
+     *           位数字、字母、下划线及"-"和"."组成。
+     *     @type string $Stream
+     *           流名称,缺省情况下,查询所有流名称,由 1 到
+     *           100位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定
+     *           App的值
+     *     @type string $EndTime
+     *           禁推的结束时间,禁推有效期最长为 90 天,默认为当前时间加 90 天
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @return string
+     */
+    public function getVhost()
+    {
+        return $this->Vhost;
+    }
+
+    /**
+     * 域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVhost($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vhost = $var;
+
+        return $this;
+    }
+
+    /**
+     *推流域名。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     * @return string
+     */
+    public function getDomain()
+    {
+        return $this->Domain;
+    }
+
+    /**
+     *推流域名。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomain($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Domain = $var;
+
+        return $this;
+    }
+
+    /**
+     * 应用名称,默认查询所有应用名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     * @return string
+     */
+    public function getApp()
+    {
+        return $this->App;
+    }
+
+    /**
+     * 应用名称,默认查询所有应用名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setApp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->App = $var;
+
+        return $this;
+    }
+
+    /**
+     * 流名称,缺省情况下,查询所有流名称,由 1 到
+     * 100位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定
+     * App的值
+     *
+     * Generated from protobuf field <code>string Stream = 4;</code>
+     * @return string
+     */
+    public function getStream()
+    {
+        return $this->Stream;
+    }
+
+    /**
+     * 流名称,缺省情况下,查询所有流名称,由 1 到
+     * 100位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定
+     * App的值
+     *
+     * Generated from protobuf field <code>string Stream = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStream($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Stream = $var;
+
+        return $this;
+    }
+
+    /**
+     * 禁推的结束时间,禁推有效期最长为 90 天,默认为当前时间加 90 天
+     *
+     * Generated from protobuf field <code>string EndTime = 5;</code>
+     * @return string
+     */
+    public function getEndTime()
+    {
+        return $this->EndTime;
+    }
+
+    /**
+     * 禁推的结束时间,禁推有效期最长为 90 天,默认为当前时间加 90 天
+     *
+     * Generated from protobuf field <code>string EndTime = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setEndTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->EndTime = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/GeneratePlayURLRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/GeneratePlayURLRequest.php
new file mode 100755
index 0000000..ffa043f
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/GeneratePlayURLRequest.php
@@ -0,0 +1,303 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.GeneratePlayURLRequest</code>
+ */
+class GeneratePlayURLRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     */
+    protected $Vhost = '';
+    /**
+     *拉流域名名称,需要生成拉流地址的域名
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     */
+    protected $Domain = '';
+    /**
+     * App名称
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     */
+    protected $App = '';
+    /**
+     * Stream名称
+     *
+     * Generated from protobuf field <code>string Stream = 4;</code>
+     */
+    protected $Stream = '';
+    /**
+     *转码流后缀,不填源流
+     *
+     * Generated from protobuf field <code>string Suffix = 5;</code>
+     */
+    protected $Suffix = '';
+    /**
+     * cdn类型,fcdn/3rd(第二方cdn)二选一填,不填默认fcdn
+     *
+     * Generated from protobuf field <code>string Type = 6;</code>
+     */
+    protected $Type = '';
+    /**
+     *生成的地址鉴权的有效时间,单位s,不填默认使用鉴权配置的过期时间,没有鉴权时间则默认7天过期
+     *
+     * Generated from protobuf field <code>int64 ValidDuration = 7;</code>
+     */
+    protected $ValidDuration = 0;
+    /**
+     *绝对有效时间,UTC格式,优先级比ValidDuration低
+     *
+     * Generated from protobuf field <code>string ExpiredTime = 8;</code>
+     */
+    protected $ExpiredTime = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vhost
+     *          域名空间名称
+     *     @type string $Domain
+     *          拉流域名名称,需要生成拉流地址的域名
+     *     @type string $App
+     *           App名称
+     *     @type string $Stream
+     *           Stream名称
+     *     @type string $Suffix
+     *          转码流后缀,不填源流
+     *     @type string $Type
+     *           cdn类型,fcdn/3rd(第二方cdn)二选一填,不填默认fcdn
+     *     @type int|string $ValidDuration
+     *          生成的地址鉴权的有效时间,单位s,不填默认使用鉴权配置的过期时间,没有鉴权时间则默认7天过期
+     *     @type string $ExpiredTime
+     *          绝对有效时间,UTC格式,优先级比ValidDuration低
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @return string
+     */
+    public function getVhost()
+    {
+        return $this->Vhost;
+    }
+
+    /**
+     *域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVhost($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vhost = $var;
+
+        return $this;
+    }
+
+    /**
+     *拉流域名名称,需要生成拉流地址的域名
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     * @return string
+     */
+    public function getDomain()
+    {
+        return $this->Domain;
+    }
+
+    /**
+     *拉流域名名称,需要生成拉流地址的域名
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomain($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Domain = $var;
+
+        return $this;
+    }
+
+    /**
+     * App名称
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     * @return string
+     */
+    public function getApp()
+    {
+        return $this->App;
+    }
+
+    /**
+     * App名称
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setApp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->App = $var;
+
+        return $this;
+    }
+
+    /**
+     * Stream名称
+     *
+     * Generated from protobuf field <code>string Stream = 4;</code>
+     * @return string
+     */
+    public function getStream()
+    {
+        return $this->Stream;
+    }
+
+    /**
+     * Stream名称
+     *
+     * Generated from protobuf field <code>string Stream = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStream($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Stream = $var;
+
+        return $this;
+    }
+
+    /**
+     *转码流后缀,不填源流
+     *
+     * Generated from protobuf field <code>string Suffix = 5;</code>
+     * @return string
+     */
+    public function getSuffix()
+    {
+        return $this->Suffix;
+    }
+
+    /**
+     *转码流后缀,不填源流
+     *
+     * Generated from protobuf field <code>string Suffix = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSuffix($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Suffix = $var;
+
+        return $this;
+    }
+
+    /**
+     * cdn类型,fcdn/3rd(第二方cdn)二选一填,不填默认fcdn
+     *
+     * Generated from protobuf field <code>string Type = 6;</code>
+     * @return string
+     */
+    public function getType()
+    {
+        return $this->Type;
+    }
+
+    /**
+     * cdn类型,fcdn/3rd(第二方cdn)二选一填,不填默认fcdn
+     *
+     * Generated from protobuf field <code>string Type = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Type = $var;
+
+        return $this;
+    }
+
+    /**
+     *生成的地址鉴权的有效时间,单位s,不填默认使用鉴权配置的过期时间,没有鉴权时间则默认7天过期
+     *
+     * Generated from protobuf field <code>int64 ValidDuration = 7;</code>
+     * @return int|string
+     */
+    public function getValidDuration()
+    {
+        return $this->ValidDuration;
+    }
+
+    /**
+     *生成的地址鉴权的有效时间,单位s,不填默认使用鉴权配置的过期时间,没有鉴权时间则默认7天过期
+     *
+     * Generated from protobuf field <code>int64 ValidDuration = 7;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setValidDuration($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->ValidDuration = $var;
+
+        return $this;
+    }
+
+    /**
+     *绝对有效时间,UTC格式,优先级比ValidDuration低
+     *
+     * Generated from protobuf field <code>string ExpiredTime = 8;</code>
+     * @return string
+     */
+    public function getExpiredTime()
+    {
+        return $this->ExpiredTime;
+    }
+
+    /**
+     *绝对有效时间,UTC格式,优先级比ValidDuration低
+     *
+     * Generated from protobuf field <code>string ExpiredTime = 8;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setExpiredTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->ExpiredTime = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/GeneratePushURLRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/GeneratePushURLRequest.php
new file mode 100755
index 0000000..f8508d5
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/GeneratePushURLRequest.php
@@ -0,0 +1,235 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.GeneratePushURLRequest</code>
+ */
+class GeneratePushURLRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     */
+    protected $Vhost = '';
+    /**
+     *推流域名名称,需要推流地址的域名,不填返回Vhost下所有推流域名生成的地址
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     */
+    protected $Domain = '';
+    /**
+     * App名称
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     */
+    protected $App = '';
+    /**
+     * App名称
+     *
+     * Generated from protobuf field <code>string Stream = 4;</code>
+     */
+    protected $Stream = '';
+    /**
+     *生成的地址鉴权的有效时间,单位s,不填默认使用鉴权配置的过期时间,没有鉴权时间则默认7天过期
+     *
+     * Generated from protobuf field <code>int64 ValidDuration = 5;</code>
+     */
+    protected $ValidDuration = 0;
+    /**
+     *绝对有效时间,UTC格式,优先级比ValidDuration低
+     *
+     * Generated from protobuf field <code>string ExpiredTime = 6;</code>
+     */
+    protected $ExpiredTime = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vhost
+     *          域名空间名称
+     *     @type string $Domain
+     *          推流域名名称,需要推流地址的域名,不填返回Vhost下所有推流域名生成的地址
+     *     @type string $App
+     *           App名称
+     *     @type string $Stream
+     *           App名称
+     *     @type int|string $ValidDuration
+     *          生成的地址鉴权的有效时间,单位s,不填默认使用鉴权配置的过期时间,没有鉴权时间则默认7天过期
+     *     @type string $ExpiredTime
+     *          绝对有效时间,UTC格式,优先级比ValidDuration低
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @return string
+     */
+    public function getVhost()
+    {
+        return $this->Vhost;
+    }
+
+    /**
+     *域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVhost($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vhost = $var;
+
+        return $this;
+    }
+
+    /**
+     *推流域名名称,需要推流地址的域名,不填返回Vhost下所有推流域名生成的地址
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     * @return string
+     */
+    public function getDomain()
+    {
+        return $this->Domain;
+    }
+
+    /**
+     *推流域名名称,需要推流地址的域名,不填返回Vhost下所有推流域名生成的地址
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomain($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Domain = $var;
+
+        return $this;
+    }
+
+    /**
+     * App名称
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     * @return string
+     */
+    public function getApp()
+    {
+        return $this->App;
+    }
+
+    /**
+     * App名称
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setApp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->App = $var;
+
+        return $this;
+    }
+
+    /**
+     * App名称
+     *
+     * Generated from protobuf field <code>string Stream = 4;</code>
+     * @return string
+     */
+    public function getStream()
+    {
+        return $this->Stream;
+    }
+
+    /**
+     * App名称
+     *
+     * Generated from protobuf field <code>string Stream = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStream($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Stream = $var;
+
+        return $this;
+    }
+
+    /**
+     *生成的地址鉴权的有效时间,单位s,不填默认使用鉴权配置的过期时间,没有鉴权时间则默认7天过期
+     *
+     * Generated from protobuf field <code>int64 ValidDuration = 5;</code>
+     * @return int|string
+     */
+    public function getValidDuration()
+    {
+        return $this->ValidDuration;
+    }
+
+    /**
+     *生成的地址鉴权的有效时间,单位s,不填默认使用鉴权配置的过期时间,没有鉴权时间则默认7天过期
+     *
+     * Generated from protobuf field <code>int64 ValidDuration = 5;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setValidDuration($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->ValidDuration = $var;
+
+        return $this;
+    }
+
+    /**
+     *绝对有效时间,UTC格式,优先级比ValidDuration低
+     *
+     * Generated from protobuf field <code>string ExpiredTime = 6;</code>
+     * @return string
+     */
+    public function getExpiredTime()
+    {
+        return $this->ExpiredTime;
+    }
+
+    /**
+     *绝对有效时间,UTC格式,优先级比ValidDuration低
+     *
+     * Generated from protobuf field <code>string ExpiredTime = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setExpiredTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->ExpiredTime = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/KillStreamRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/KillStreamRequest.php
new file mode 100755
index 0000000..002536a
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/KillStreamRequest.php
@@ -0,0 +1,145 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.KillStreamRequest</code>
+ */
+class KillStreamRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     */
+    protected $Vhost = '';
+    /**
+     * 应用名称,默认查询所有应用名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 2;</code>
+     */
+    protected $App = '';
+    /**
+     * 流名称,缺省情况下,查询所有流名称,由 1 到
+     * 100位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定 App
+     * 的值
+     *
+     * Generated from protobuf field <code>string Stream = 3;</code>
+     */
+    protected $Stream = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vhost
+     *           域名空间名称
+     *     @type string $App
+     *           应用名称,默认查询所有应用名称,由 1 到 30
+     *           位数字、字母、下划线及"-"和"."组成。
+     *     @type string $Stream
+     *           流名称,缺省情况下,查询所有流名称,由 1 到
+     *           100位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定 App
+     *           的值
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @return string
+     */
+    public function getVhost()
+    {
+        return $this->Vhost;
+    }
+
+    /**
+     * 域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVhost($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vhost = $var;
+
+        return $this;
+    }
+
+    /**
+     * 应用名称,默认查询所有应用名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 2;</code>
+     * @return string
+     */
+    public function getApp()
+    {
+        return $this->App;
+    }
+
+    /**
+     * 应用名称,默认查询所有应用名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setApp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->App = $var;
+
+        return $this;
+    }
+
+    /**
+     * 流名称,缺省情况下,查询所有流名称,由 1 到
+     * 100位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定 App
+     * 的值
+     *
+     * Generated from protobuf field <code>string Stream = 3;</code>
+     * @return string
+     */
+    public function getStream()
+    {
+        return $this->Stream;
+    }
+
+    /**
+     * 流名称,缺省情况下,查询所有流名称,由 1 到
+     * 100位数字、字母、下划线及"-"和"."组成,如果指定 Stream,必须同时指定 App
+     * 的值
+     *
+     * Generated from protobuf field <code>string Stream = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStream($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Stream = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/ListDomainDetailRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/ListDomainDetailRequest.php
new file mode 100755
index 0000000..58210cd
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/ListDomainDetailRequest.php
@@ -0,0 +1,309 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.ListDomainDetailRequest</code>
+ */
+class ListDomainDetailRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *当前页码,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageNum = 1;</code>
+     */
+    protected $PageNum = 0;
+    /**
+     *分页大小,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageSize = 2;</code>
+     */
+    protected $PageSize = 0;
+    /**
+     *配置空间列表,缺省情况下表示过滤全部。
+     *
+     * Generated from protobuf field <code>repeated string VhostList = 3;</code>
+     */
+    private $VhostList;
+    /**
+     *域名状态列表。缺省情况下表示过滤全部。状态说明如下所示。
+     * <li>0:正常;
+     * <li>1:审核中;
+     * <li>2:禁用,禁止使用,此时domain不生效;
+     * <li>3:删除;
+     * <li>4:审核被驳回。审核不通过,需要重新创建并审核;
+     * <li>5:欠费关停。
+     *
+     * Generated from protobuf field <code>repeated string DomainStatusList = 4;</code>
+     */
+    private $DomainStatusList;
+    /**
+     *域名类型列表。缺省情况下表示过滤全部。域名类型包括:
+     * <li>push:推流域名;
+     * <li>pull-flv:拉流域名。
+     *
+     * Generated from protobuf field <code>repeated string DomainTypeList = 5;</code>
+     */
+    private $DomainTypeList;
+    /**
+     *域名区域列表。缺省情况下表示过滤全部,区域包括:
+     * <li>cn:中国大陆;
+     * <li>oversea:海外。
+     *
+     * Generated from protobuf field <code>repeated string DomainRegionList = 6;</code>
+     */
+    private $DomainRegionList;
+    /**
+     *域名名称列表,缺省情况下表示过滤全部。
+     *
+     * Generated from protobuf field <code>repeated string DomainNameList = 7;</code>
+     */
+    private $DomainNameList;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int|string $PageNum
+     *          当前页码,取值范围为 。
+     *     @type int|string $PageSize
+     *          分页大小,取值范围为 。
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $VhostList
+     *          配置空间列表,缺省情况下表示过滤全部。
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $DomainStatusList
+     *          域名状态列表。缺省情况下表示过滤全部。状态说明如下所示。
+     *           <li>0:正常;
+     *           <li>1:审核中;
+     *           <li>2:禁用,禁止使用,此时domain不生效;
+     *           <li>3:删除;
+     *           <li>4:审核被驳回。审核不通过,需要重新创建并审核;
+     *           <li>5:欠费关停。
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $DomainTypeList
+     *          域名类型列表。缺省情况下表示过滤全部。域名类型包括:
+     *           <li>push:推流域名;
+     *           <li>pull-flv:拉流域名。
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $DomainRegionList
+     *          域名区域列表。缺省情况下表示过滤全部,区域包括:
+     *           <li>cn:中国大陆;
+     *           <li>oversea:海外。
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $DomainNameList
+     *          域名名称列表,缺省情况下表示过滤全部。
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *当前页码,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageNum = 1;</code>
+     * @return int|string
+     */
+    public function getPageNum()
+    {
+        return $this->PageNum;
+    }
+
+    /**
+     *当前页码,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageNum = 1;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setPageNum($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->PageNum = $var;
+
+        return $this;
+    }
+
+    /**
+     *分页大小,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageSize = 2;</code>
+     * @return int|string
+     */
+    public function getPageSize()
+    {
+        return $this->PageSize;
+    }
+
+    /**
+     *分页大小,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageSize = 2;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setPageSize($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->PageSize = $var;
+
+        return $this;
+    }
+
+    /**
+     *配置空间列表,缺省情况下表示过滤全部。
+     *
+     * Generated from protobuf field <code>repeated string VhostList = 3;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getVhostList()
+    {
+        return $this->VhostList;
+    }
+
+    /**
+     *配置空间列表,缺省情况下表示过滤全部。
+     *
+     * Generated from protobuf field <code>repeated string VhostList = 3;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setVhostList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->VhostList = $arr;
+
+        return $this;
+    }
+
+    /**
+     *域名状态列表。缺省情况下表示过滤全部。状态说明如下所示。
+     * <li>0:正常;
+     * <li>1:审核中;
+     * <li>2:禁用,禁止使用,此时domain不生效;
+     * <li>3:删除;
+     * <li>4:审核被驳回。审核不通过,需要重新创建并审核;
+     * <li>5:欠费关停。
+     *
+     * Generated from protobuf field <code>repeated string DomainStatusList = 4;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getDomainStatusList()
+    {
+        return $this->DomainStatusList;
+    }
+
+    /**
+     *域名状态列表。缺省情况下表示过滤全部。状态说明如下所示。
+     * <li>0:正常;
+     * <li>1:审核中;
+     * <li>2:禁用,禁止使用,此时domain不生效;
+     * <li>3:删除;
+     * <li>4:审核被驳回。审核不通过,需要重新创建并审核;
+     * <li>5:欠费关停。
+     *
+     * Generated from protobuf field <code>repeated string DomainStatusList = 4;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setDomainStatusList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->DomainStatusList = $arr;
+
+        return $this;
+    }
+
+    /**
+     *域名类型列表。缺省情况下表示过滤全部。域名类型包括:
+     * <li>push:推流域名;
+     * <li>pull-flv:拉流域名。
+     *
+     * Generated from protobuf field <code>repeated string DomainTypeList = 5;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getDomainTypeList()
+    {
+        return $this->DomainTypeList;
+    }
+
+    /**
+     *域名类型列表。缺省情况下表示过滤全部。域名类型包括:
+     * <li>push:推流域名;
+     * <li>pull-flv:拉流域名。
+     *
+     * Generated from protobuf field <code>repeated string DomainTypeList = 5;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setDomainTypeList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->DomainTypeList = $arr;
+
+        return $this;
+    }
+
+    /**
+     *域名区域列表。缺省情况下表示过滤全部,区域包括:
+     * <li>cn:中国大陆;
+     * <li>oversea:海外。
+     *
+     * Generated from protobuf field <code>repeated string DomainRegionList = 6;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getDomainRegionList()
+    {
+        return $this->DomainRegionList;
+    }
+
+    /**
+     *域名区域列表。缺省情况下表示过滤全部,区域包括:
+     * <li>cn:中国大陆;
+     * <li>oversea:海外。
+     *
+     * Generated from protobuf field <code>repeated string DomainRegionList = 6;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setDomainRegionList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->DomainRegionList = $arr;
+
+        return $this;
+    }
+
+    /**
+     *域名名称列表,缺省情况下表示过滤全部。
+     *
+     * Generated from protobuf field <code>repeated string DomainNameList = 7;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getDomainNameList()
+    {
+        return $this->DomainNameList;
+    }
+
+    /**
+     *域名名称列表,缺省情况下表示过滤全部。
+     *
+     * Generated from protobuf field <code>repeated string DomainNameList = 7;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setDomainNameList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->DomainNameList = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/ListPullToPushTaskRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/ListPullToPushTaskRequest.php
new file mode 100755
index 0000000..c920136
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/ListPullToPushTaskRequest.php
@@ -0,0 +1,133 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.ListPullToPushTaskRequest</code>
+ */
+class ListPullToPushTaskRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *页码,不填默认1
+     *
+     * Generated from protobuf field <code>int32 Page = 1;</code>
+     */
+    protected $Page = 0;
+    /**
+     *每页数量,不填默认20
+     *
+     * Generated from protobuf field <code>int32 Size = 2;</code>
+     */
+    protected $Size = 0;
+    /**
+     *任务标题
+     *
+     * Generated from protobuf field <code>string Title = 3;</code>
+     */
+    protected $Title = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int $Page
+     *          页码,不填默认1
+     *     @type int $Size
+     *          每页数量,不填默认20
+     *     @type string $Title
+     *          任务标题
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *页码,不填默认1
+     *
+     * Generated from protobuf field <code>int32 Page = 1;</code>
+     * @return int
+     */
+    public function getPage()
+    {
+        return $this->Page;
+    }
+
+    /**
+     *页码,不填默认1
+     *
+     * Generated from protobuf field <code>int32 Page = 1;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setPage($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Page = $var;
+
+        return $this;
+    }
+
+    /**
+     *每页数量,不填默认20
+     *
+     * Generated from protobuf field <code>int32 Size = 2;</code>
+     * @return int
+     */
+    public function getSize()
+    {
+        return $this->Size;
+    }
+
+    /**
+     *每页数量,不填默认20
+     *
+     * Generated from protobuf field <code>int32 Size = 2;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setSize($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Size = $var;
+
+        return $this;
+    }
+
+    /**
+     *任务标题
+     *
+     * Generated from protobuf field <code>string Title = 3;</code>
+     * @return string
+     */
+    public function getTitle()
+    {
+        return $this->Title;
+    }
+
+    /**
+     *任务标题
+     *
+     * Generated from protobuf field <code>string Title = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTitle($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Title = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/ListVQScoreTaskRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/ListVQScoreTaskRequest.php
new file mode 100755
index 0000000..c41ecaf
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/ListVQScoreTaskRequest.php
@@ -0,0 +1,201 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.ListVQScoreTaskRequest</code>
+ */
+class ListVQScoreTaskRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *查询开始时间,UTC时间格式,支持查询最近30天内的数据
+     *
+     * Generated from protobuf field <code>string StartTime = 1;</code>
+     */
+    public $StartTime = '';
+    /**
+     *查询结束时间,UTC时间格式,支持查询最近30天内的数据
+     *
+     * Generated from protobuf field <code>string EndTime = 2;</code>
+     */
+    public $EndTime = '';
+    /**
+     *当前页码,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageNum = 3;</code>
+     */
+    public $PageNum = 0;
+    /**
+     *分页大小,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageSize = 4;</code>
+     */
+    public $PageSize = 0;
+    /**
+     *测评状态,0:全部,1:测试中,2:成功,3:失败
+     *
+     * Generated from protobuf field <code>int64 Status = 5;</code>
+     */
+    public $Status = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $StartTime
+     *          查询开始时间,UTC时间格式,支持查询最近30天内的数据
+     *     @type string $EndTime
+     *          查询结束时间,UTC时间格式,支持查询最近30天内的数据
+     *     @type int|string $PageNum
+     *          当前页码,取值范围为 。
+     *     @type int|string $PageSize
+     *          分页大小,取值范围为 。
+     *     @type int|string $Status
+     *          测评状态,0:全部,1:测试中,2:成功,3:失败
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *查询开始时间,UTC时间格式,支持查询最近30天内的数据
+     *
+     * Generated from protobuf field <code>string StartTime = 1;</code>
+     * @return string
+     */
+    public function getStartTime()
+    {
+        return $this->StartTime;
+    }
+
+    /**
+     *查询开始时间,UTC时间格式,支持查询最近30天内的数据
+     *
+     * Generated from protobuf field <code>string StartTime = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStartTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StartTime = $var;
+
+        return $this;
+    }
+
+    /**
+     *查询结束时间,UTC时间格式,支持查询最近30天内的数据
+     *
+     * Generated from protobuf field <code>string EndTime = 2;</code>
+     * @return string
+     */
+    public function getEndTime()
+    {
+        return $this->EndTime;
+    }
+
+    /**
+     *查询结束时间,UTC时间格式,支持查询最近30天内的数据
+     *
+     * Generated from protobuf field <code>string EndTime = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setEndTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->EndTime = $var;
+
+        return $this;
+    }
+
+    /**
+     *当前页码,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageNum = 3;</code>
+     * @return int|string
+     */
+    public function getPageNum()
+    {
+        return $this->PageNum;
+    }
+
+    /**
+     *当前页码,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageNum = 3;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setPageNum($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->PageNum = $var;
+
+        return $this;
+    }
+
+    /**
+     *分页大小,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageSize = 4;</code>
+     * @return int|string
+     */
+    public function getPageSize()
+    {
+        return $this->PageSize;
+    }
+
+    /**
+     *分页大小,取值范围为 。
+     *
+     * Generated from protobuf field <code>int64 PageSize = 4;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setPageSize($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->PageSize = $var;
+
+        return $this;
+    }
+
+    /**
+     *测评状态,0:全部,1:测试中,2:成功,3:失败
+     *
+     * Generated from protobuf field <code>int64 Status = 5;</code>
+     * @return int|string
+     */
+    public function getStatus()
+    {
+        return $this->Status;
+    }
+
+    /**
+     *测评状态,0:全部,1:测试中,2:成功,3:失败
+     *
+     * Generated from protobuf field <code>int64 Status = 5;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setStatus($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->Status = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/ManagerPullPushDomainBindRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/ManagerPullPushDomainBindRequest.php
new file mode 100755
index 0000000..d3bbb96
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/ManagerPullPushDomainBindRequest.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.ManagerPullPushDomainBindRequest</code>
+ */
+class ManagerPullPushDomainBindRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *需要绑定/解绑的拉流域名。
+     *
+     * Generated from protobuf field <code>string PullDomain = 1;</code>
+     */
+    protected $PullDomain = '';
+    /**
+     *需要绑定的推流域名,在绑定推拉流域名的场景下,该参数为必填项。缺省情况下表示解绑。
+     *
+     * Generated from protobuf field <code>string PushDomain = 2;</code>
+     */
+    protected $PushDomain = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $PullDomain
+     *          需要绑定/解绑的拉流域名。
+     *     @type string $PushDomain
+     *          需要绑定的推流域名,在绑定推拉流域名的场景下,该参数为必填项。缺省情况下表示解绑。
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *需要绑定/解绑的拉流域名。
+     *
+     * Generated from protobuf field <code>string PullDomain = 1;</code>
+     * @return string
+     */
+    public function getPullDomain()
+    {
+        return $this->PullDomain;
+    }
+
+    /**
+     *需要绑定/解绑的拉流域名。
+     *
+     * Generated from protobuf field <code>string PullDomain = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setPullDomain($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->PullDomain = $var;
+
+        return $this;
+    }
+
+    /**
+     *需要绑定的推流域名,在绑定推拉流域名的场景下,该参数为必填项。缺省情况下表示解绑。
+     *
+     * Generated from protobuf field <code>string PushDomain = 2;</code>
+     * @return string
+     */
+    public function getPushDomain()
+    {
+        return $this->PushDomain;
+    }
+
+    /**
+     *需要绑定的推流域名,在绑定推拉流域名的场景下,该参数为必填项。缺省情况下表示解绑。
+     *
+     * Generated from protobuf field <code>string PushDomain = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setPushDomain($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->PushDomain = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/RestartPullToPushTaskRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/RestartPullToPushTaskRequest.php
new file mode 100755
index 0000000..748bfa4
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/RestartPullToPushTaskRequest.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.RestartPullToPushTaskRequest</code>
+ */
+class RestartPullToPushTaskRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *任务ID
+     *
+     * Generated from protobuf field <code>string TaskId = 1;</code>
+     */
+    protected $TaskId = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $TaskId
+     *          任务ID
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *任务ID
+     *
+     * Generated from protobuf field <code>string TaskId = 1;</code>
+     * @return string
+     */
+    public function getTaskId()
+    {
+        return $this->TaskId;
+    }
+
+    /**
+     *任务ID
+     *
+     * Generated from protobuf field <code>string TaskId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTaskId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TaskId = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/ResumeStreamRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/ResumeStreamRequest.php
new file mode 100755
index 0000000..04e3c9a
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/ResumeStreamRequest.php
@@ -0,0 +1,179 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.ResumeStreamRequest</code>
+ */
+class ResumeStreamRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Vhost 的名称。
+     * 参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     */
+    protected $Vhost = '';
+    /**
+     *推流域名。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     */
+    protected $Domain = '';
+    /**
+     *应用名称,由 1 到 30 位数字、字母、下划线及"-"和"."组成
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     */
+    protected $App = '';
+    /**
+     *流名称。由 1 到 100 位数字、字母、下划线及"-"和"."组成,如果指定
+     * Stream,必须同时指定 App 的值
+     *
+     * Generated from protobuf field <code>string Stream = 4;</code>
+     */
+    protected $Stream = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vhost
+     *           Vhost 的名称。
+     *           参数 `Domain` 和 `Vhost`传且仅传一个。
+     *     @type string $Domain
+     *          推流域名。
+     *          参数 `Domain` 和 `Vhost`传且仅传一个。
+     *     @type string $App
+     *          应用名称,由 1 到 30 位数字、字母、下划线及"-"和"."组成
+     *     @type string $Stream
+     *          流名称。由 1 到 100 位数字、字母、下划线及"-"和"."组成,如果指定
+     *           Stream,必须同时指定 App 的值
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Vhost 的名称。
+     * 参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @return string
+     */
+    public function getVhost()
+    {
+        return $this->Vhost;
+    }
+
+    /**
+     * Vhost 的名称。
+     * 参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVhost($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vhost = $var;
+
+        return $this;
+    }
+
+    /**
+     *推流域名。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     * @return string
+     */
+    public function getDomain()
+    {
+        return $this->Domain;
+    }
+
+    /**
+     *推流域名。
+     *参数 `Domain` 和 `Vhost`传且仅传一个。
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomain($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Domain = $var;
+
+        return $this;
+    }
+
+    /**
+     *应用名称,由 1 到 30 位数字、字母、下划线及"-"和"."组成
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     * @return string
+     */
+    public function getApp()
+    {
+        return $this->App;
+    }
+
+    /**
+     *应用名称,由 1 到 30 位数字、字母、下划线及"-"和"."组成
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setApp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->App = $var;
+
+        return $this;
+    }
+
+    /**
+     *流名称。由 1 到 100 位数字、字母、下划线及"-"和"."组成,如果指定
+     * Stream,必须同时指定 App 的值
+     *
+     * Generated from protobuf field <code>string Stream = 4;</code>
+     * @return string
+     */
+    public function getStream()
+    {
+        return $this->Stream;
+    }
+
+    /**
+     *流名称。由 1 到 100 位数字、字母、下划线及"-"和"."组成,如果指定
+     * Stream,必须同时指定 App 的值
+     *
+     * Generated from protobuf field <code>string Stream = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStream($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Stream = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/StopPullToPushTaskRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/StopPullToPushTaskRequest.php
new file mode 100755
index 0000000..580ab4b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/StopPullToPushTaskRequest.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.StopPullToPushTaskRequest</code>
+ */
+class StopPullToPushTaskRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *任务ID
+     *
+     * Generated from protobuf field <code>string TaskId = 1;</code>
+     */
+    protected $TaskId = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $TaskId
+     *          任务ID
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *任务ID
+     *
+     * Generated from protobuf field <code>string TaskId = 1;</code>
+     * @return string
+     */
+    public function getTaskId()
+    {
+        return $this->TaskId;
+    }
+
+    /**
+     *任务ID
+     *
+     * Generated from protobuf field <code>string TaskId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTaskId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TaskId = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/UpdateDenyConfigRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/UpdateDenyConfigRequest.php
new file mode 100755
index 0000000..451b238
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/UpdateDenyConfigRequest.php
@@ -0,0 +1,170 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.UpdateDenyConfigRequest</code>
+ */
+class UpdateDenyConfigRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     */
+    public $Vhost = '';
+    /**
+     *推拉流域名,domain和app二选一填
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     */
+    public $Domain = '';
+    /**
+     * App的名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。domain和app二选一填。
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     */
+    public $App = '';
+    /**
+     *黑白名称配置列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.DenyConfigDetail DenyConfigList = 4;</code>
+     */
+    public $DenyConfigList;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vhost
+     *          域名空间名称
+     *     @type string $Domain
+     *          推拉流域名,domain和app二选一填
+     *     @type string $App
+     *           App的名称,由 1 到 30
+     *           位数字、字母、下划线及"-"和"."组成。domain和app二选一填。
+     *     @type \Volc\Service\Live\Models\Business\DenyConfigDetail[]|\Google\Protobuf\Internal\RepeatedField $DenyConfigList
+     *          黑白名称配置列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @return string
+     */
+    public function getVhost()
+    {
+        return $this->Vhost;
+    }
+
+    /**
+     *域名空间名称
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVhost($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vhost = $var;
+
+        return $this;
+    }
+
+    /**
+     *推拉流域名,domain和app二选一填
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     * @return string
+     */
+    public function getDomain()
+    {
+        return $this->Domain;
+    }
+
+    /**
+     *推拉流域名,domain和app二选一填
+     *
+     * Generated from protobuf field <code>string Domain = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomain($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Domain = $var;
+
+        return $this;
+    }
+
+    /**
+     * App的名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。domain和app二选一填。
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     * @return string
+     */
+    public function getApp()
+    {
+        return $this->App;
+    }
+
+    /**
+     * App的名称,由 1 到 30
+     * 位数字、字母、下划线及"-"和"."组成。domain和app二选一填。
+     *
+     * Generated from protobuf field <code>string App = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setApp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->App = $var;
+
+        return $this;
+    }
+
+    /**
+     *黑白名称配置列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.DenyConfigDetail DenyConfigList = 4;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getDenyConfigList()
+    {
+        return $this->DenyConfigList;
+    }
+
+    /**
+     *黑白名称配置列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Live.Models.Business.DenyConfigDetail DenyConfigList = 4;</code>
+     * @param \Volc\Service\Live\Models\Business\DenyConfigDetail[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setDenyConfigList($var)
+    {
+//        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Live\Models\Business\DenyConfigDetail::class);
+        $this->DenyConfigList = $var;
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/UpdatePullToPushTaskRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/UpdatePullToPushTaskRequest.php
new file mode 100755
index 0000000..dcf4b4d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/UpdatePullToPushTaskRequest.php
@@ -0,0 +1,379 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.UpdatePullToPushTaskRequest</code>
+ */
+class UpdatePullToPushTaskRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *任务标题
+     *
+     * Generated from protobuf field <code>string Title = 1;</code>
+     */
+    public $Title = '';
+    /**
+     *任务ID
+     *
+     * Generated from protobuf field <code>string TaskId = 2;</code>
+     */
+    public $TaskId = '';
+    /**
+     *开始时间,unix时间戳,单位s
+     *
+     * Generated from protobuf field <code>int64 StartTime = 3;</code>
+     */
+    public $StartTime = 0;
+    /**
+     *结束时间,unix时间戳,单位s,与开始时间跨度不超过7天
+     *
+     * Generated from protobuf field <code>int64 EndTime = 4;</code>
+     */
+    public $EndTime = 0;
+    /**
+     *回调地址,长度小于2000
+     *
+     * Generated from protobuf field <code>string CallbackURL = 5;</code>
+     */
+    public $CallbackURL = '';
+    /**
+     *拉流转推地址类型,0:直播,1:点播
+     *
+     * Generated from protobuf field <code>int32 Type = 6;</code>
+     */
+    public $Type = 0;
+    /**
+     *点播时,拉流地址的循环模式,Type=1时必选,-1:顺序循环
+     *
+     * Generated from protobuf field <code>int32 CycleMode = 7;</code>
+     */
+    public $CycleMode = 0;
+    /**
+     *推流目标地址
+     *
+     * Generated from protobuf field <code>string DstAddr = 8;</code>
+     */
+    public $DstAddr = '';
+    /**
+     *直播拉流地址,Type=0时必填
+     *
+     * Generated from protobuf field <code>string SrcAddr = 9;</code>
+     */
+    public $SrcAddr = '';
+    /**
+     *点播拉流地址,Type=1时必填
+     *
+     * Generated from protobuf field <code>repeated string SrcAddrS = 10;</code>
+     */
+    public $SrcAddrS;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Title
+     *          任务标题
+     *     @type string $TaskId
+     *          任务ID
+     *     @type int|string $StartTime
+     *          开始时间,unix时间戳,单位s
+     *     @type int|string $EndTime
+     *          结束时间,unix时间戳,单位s,与开始时间跨度不超过7天
+     *     @type string $CallbackURL
+     *          回调地址,长度小于2000
+     *     @type int $Type
+     *          拉流转推地址类型,0:直播,1:点播
+     *     @type int $CycleMode
+     *          点播时,拉流地址的循环模式,Type=1时必选,-1:顺序循环
+     *     @type string $DstAddr
+     *          推流目标地址
+     *     @type string $SrcAddr
+     *          直播拉流地址,Type=0时必填
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $SrcAddrS
+     *          点播拉流地址,Type=1时必填
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *任务标题
+     *
+     * Generated from protobuf field <code>string Title = 1;</code>
+     * @return string
+     */
+    public function getTitle()
+    {
+        return $this->Title;
+    }
+
+    /**
+     *任务标题
+     *
+     * Generated from protobuf field <code>string Title = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTitle($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Title = $var;
+
+        var_dump( "#####################");
+        var_dump( $var);
+        var_dump( "#####################");
+        return $this;
+    }
+
+    /**
+     *任务ID
+     *
+     * Generated from protobuf field <code>string TaskId = 2;</code>
+     * @return string
+     */
+    public function getTaskId()
+    {
+        return $this->TaskId;
+    }
+
+    /**
+     *任务ID
+     *
+     * Generated from protobuf field <code>string TaskId = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTaskId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TaskId = $var;
+
+        return $this;
+    }
+
+    /**
+     *开始时间,unix时间戳,单位s
+     *
+     * Generated from protobuf field <code>int64 StartTime = 3;</code>
+     * @return int|string
+     */
+    public function getStartTime()
+    {
+        return $this->StartTime;
+    }
+
+    /**
+     *开始时间,unix时间戳,单位s
+     *
+     * Generated from protobuf field <code>int64 StartTime = 3;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setStartTime($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->StartTime = $var;
+        var_dump( "#####################");
+        var_dump( $this->StartTime);
+        var_dump( "#####################");
+        return $this;
+    }
+
+    /**
+     *结束时间,unix时间戳,单位s,与开始时间跨度不超过7天
+     *
+     * Generated from protobuf field <code>int64 EndTime = 4;</code>
+     * @return int|string
+     */
+    public function getEndTime()
+    {
+        return $this->EndTime;
+    }
+
+    /**
+     *结束时间,unix时间戳,单位s,与开始时间跨度不超过7天
+     *
+     * Generated from protobuf field <code>int64 EndTime = 4;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setEndTime($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->EndTime = $var;
+
+        return $this;
+    }
+
+    /**
+     *回调地址,长度小于2000
+     *
+     * Generated from protobuf field <code>string CallbackURL = 5;</code>
+     * @return string
+     */
+    public function getCallbackURL()
+    {
+        return $this->CallbackURL;
+    }
+
+    /**
+     *回调地址,长度小于2000
+     *
+     * Generated from protobuf field <code>string CallbackURL = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCallbackURL($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CallbackURL = $var;
+
+        return $this;
+    }
+
+    /**
+     *拉流转推地址类型,0:直播,1:点播
+     *
+     * Generated from protobuf field <code>int32 Type = 6;</code>
+     * @return int
+     */
+    public function getType()
+    {
+        return $this->Type;
+    }
+
+    /**
+     *拉流转推地址类型,0:直播,1:点播
+     *
+     * Generated from protobuf field <code>int32 Type = 6;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setType($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->Type = $var;
+        var_dump( "#####################");
+        var_dump( $var);
+        var_dump( "#####################");
+
+        return $this;
+    }
+
+    /**
+     *点播时,拉流地址的循环模式,Type=1时必选,-1:顺序循环
+     *
+     * Generated from protobuf field <code>int32 CycleMode = 7;</code>
+     * @return int
+     */
+    public function getCycleMode()
+    {
+        return $this->CycleMode;
+    }
+
+    /**
+     *点播时,拉流地址的循环模式,Type=1时必选,-1:顺序循环
+     *
+     * Generated from protobuf field <code>int32 CycleMode = 7;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setCycleMode($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->CycleMode = $var;
+
+        return $this;
+    }
+
+    /**
+     *推流目标地址
+     *
+     * Generated from protobuf field <code>string DstAddr = 8;</code>
+     * @return string
+     */
+    public function getDstAddr()
+    {
+        return $this->DstAddr;
+    }
+
+    /**
+     *推流目标地址
+     *
+     * Generated from protobuf field <code>string DstAddr = 8;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDstAddr($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->DstAddr = $var;
+
+        return $this;
+    }
+
+    /**
+     *直播拉流地址,Type=0时必填
+     *
+     * Generated from protobuf field <code>string SrcAddr = 9;</code>
+     * @return string
+     */
+    public function getSrcAddr()
+    {
+        return $this->SrcAddr;
+    }
+
+    /**
+     *直播拉流地址,Type=0时必填
+     *
+     * Generated from protobuf field <code>string SrcAddr = 9;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSrcAddr($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SrcAddr = $var;
+
+        return $this;
+    }
+
+    /**
+     *点播拉流地址,Type=1时必填
+     *
+     * Generated from protobuf field <code>repeated string SrcAddrS = 10;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getSrcAddrS()
+    {
+        return $this->SrcAddrS;
+    }
+
+    /**
+     *点播拉流地址,Type=1时必填
+     *
+     * Generated from protobuf field <code>repeated string SrcAddrS = 10;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setSrcAddrS($var)
+    {
+//        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->SrcAddrS = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/UpdateRelaySourceRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/UpdateRelaySourceRequest.php
new file mode 100755
index 0000000..6388626
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Request/UpdateRelaySourceRequest.php
@@ -0,0 +1,201 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/request/request_live.proto
+
+namespace Volc\Service\Live\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Request.UpdateRelaySourceRequest</code>
+ */
+class UpdateRelaySourceRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 域名空间名称。
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     */
+    public $Vhost = '';
+    /**
+     * 应用名称,由 1 到 30 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 2;</code>
+     */
+    public $App = '';
+    /**
+     * 回源地址数组。
+     *
+     * Generated from protobuf field <code>repeated string RelaySourceDomainList = 3;</code>
+     */
+    public $RelaySourceDomainList;
+    /**
+     * 回源参数。
+     *
+     * Generated from protobuf field <code>map<string, string> RelaySourceParams = 4;</code>
+     */
+    public $RelaySourceParams;
+    /**
+     * 回源协议,目前只支持以下两种:<li>rmtp <li>flv。
+     *
+     * Generated from protobuf field <code>string RelaySourceProtocol = 5;</code>
+     */
+    public $RelaySourceProtocol = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vhost
+     *           域名空间名称。
+     *     @type string $App
+     *           应用名称,由 1 到 30 位数字、字母、下划线及"-"和"."组成。
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $RelaySourceDomainList
+     *           回源地址数组。
+     *     @type array|\Google\Protobuf\Internal\MapField $RelaySourceParams
+     *           回源参数。
+     *     @type string $RelaySourceProtocol
+     *           回源协议,目前只支持以下两种:<li>rmtp <li>flv。
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\RequestLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 域名空间名称。
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @return string
+     */
+    public function getVhost()
+    {
+        return $this->Vhost;
+    }
+
+    /**
+     * 域名空间名称。
+     *
+     * Generated from protobuf field <code>string Vhost = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVhost($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vhost = $var;
+
+        return $this;
+    }
+
+    /**
+     * 应用名称,由 1 到 30 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 2;</code>
+     * @return string
+     */
+    public function getApp()
+    {
+        return $this->App;
+    }
+
+    /**
+     * 应用名称,由 1 到 30 位数字、字母、下划线及"-"和"."组成。
+     *
+     * Generated from protobuf field <code>string App = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setApp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->App = $var;
+
+        return $this;
+    }
+
+    /**
+     * 回源地址数组。
+     *
+     * Generated from protobuf field <code>repeated string RelaySourceDomainList = 3;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getRelaySourceDomainList()
+    {
+        return $this->RelaySourceDomainList;
+    }
+
+    /**
+     * 回源地址数组。
+     *
+     * Generated from protobuf field <code>repeated string RelaySourceDomainList = 3;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setRelaySourceDomainList($var)
+    {
+//        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->RelaySourceDomainList = $var;
+
+        return $this;
+    }
+
+    /**
+     * 回源参数。
+     *
+     * Generated from protobuf field <code>map<string, string> RelaySourceParams = 4;</code>
+     * @return \Google\Protobuf\Internal\MapField
+     */
+    public function getRelaySourceParams()
+    {
+        return $this->RelaySourceParams;
+    }
+
+    /**
+     * 回源参数。
+     *
+     * Generated from protobuf field <code>map<string, string> RelaySourceParams = 4;</code>
+     * @param array|\Google\Protobuf\Internal\MapField $var
+     * @return $this
+     */
+    public function setRelaySourceParams($var)
+    {
+//        $arr = GPBUtil::checkMapField($var, \Google\Protobuf\Internal\GPBType::STRING, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->RelaySourceParams = $var;
+
+        return $this;
+    }
+
+    /**
+     * 回源协议,目前只支持以下两种:<li>rmtp <li>flv。
+     *
+     * Generated from protobuf field <code>string RelaySourceProtocol = 5;</code>
+     * @return string
+     */
+    public function getRelaySourceProtocol()
+    {
+        return $this->RelaySourceProtocol;
+    }
+
+    /**
+     * 回源协议,目前只支持以下两种:<li>rmtp <li>flv。
+     *
+     * Generated from protobuf field <code>string RelaySourceProtocol = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setRelaySourceProtocol($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->RelaySourceProtocol = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/CreateDomainResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/CreateDomainResponse.php
new file mode 100755
index 0000000..c32c6c6
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/CreateDomainResponse.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.CreateDomainResponse</code>
+ */
+class CreateDomainResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/CreatePullToPushTaskResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/CreatePullToPushTaskResponse.php
new file mode 100755
index 0000000..ddc9fa9
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/CreatePullToPushTaskResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.CreatePullToPushTaskResponse</code>
+ */
+class CreatePullToPushTaskResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.CreatePullToPushTaskResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Live\Models\Business\CreatePullToPushTaskResult $Result
+     *          返回结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.CreatePullToPushTaskResult Result = 2;</code>
+     * @return \Volc\Service\Live\Models\Business\CreatePullToPushTaskResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.CreatePullToPushTaskResult Result = 2;</code>
+     * @param \Volc\Service\Live\Models\Business\CreatePullToPushTaskResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Live\Models\Business\CreatePullToPushTaskResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/CreateVQScoreTaskResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/CreateVQScoreTaskResponse.php
new file mode 100755
index 0000000..3b56198
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/CreateVQScoreTaskResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.CreateVQScoreTaskResponse</code>
+ */
+class CreateVQScoreTaskResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.VQScoreID Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Live\Models\Business\VQScoreID $Result
+     *          返回结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.VQScoreID Result = 2;</code>
+     * @return \Volc\Service\Live\Models\Business\VQScoreID|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.VQScoreID Result = 2;</code>
+     * @param \Volc\Service\Live\Models\Business\VQScoreID $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Live\Models\Business\VQScoreID::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DeleteDenyConfigResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DeleteDenyConfigResponse.php
new file mode 100755
index 0000000..b4e5532
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DeleteDenyConfigResponse.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.DeleteDenyConfigResponse</code>
+ */
+class DeleteDenyConfigResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DeleteDomainResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DeleteDomainResponse.php
new file mode 100755
index 0000000..9eb471f
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DeleteDomainResponse.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.DeleteDomainResponse</code>
+ */
+class DeleteDomainResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DeletePullToPushTaskResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DeletePullToPushTaskResponse.php
new file mode 100755
index 0000000..667dd9c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DeletePullToPushTaskResponse.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.DeletePullToPushTaskResponse</code>
+ */
+class DeletePullToPushTaskResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DeleteRelaySourceResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DeleteRelaySourceResponse.php
new file mode 100755
index 0000000..009fb4b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DeleteRelaySourceResponse.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.DeleteRelaySourceResponse</code>
+ */
+class DeleteRelaySourceResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeCDNSnapshotHistoryResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeCDNSnapshotHistoryResponse.php
new file mode 100755
index 0000000..3b0b8b2
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeCDNSnapshotHistoryResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.DescribeCDNSnapshotHistoryResponse</code>
+ */
+class DescribeCDNSnapshotHistoryResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.CDNSnapshotHistoryInfo Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Live\Models\Business\CDNSnapshotHistoryInfo $Result
+     *          返回结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.CDNSnapshotHistoryInfo Result = 2;</code>
+     * @return \Volc\Service\Live\Models\Business\CDNSnapshotHistoryInfo|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.CDNSnapshotHistoryInfo Result = 2;</code>
+     * @param \Volc\Service\Live\Models\Business\CDNSnapshotHistoryInfo $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Live\Models\Business\CDNSnapshotHistoryInfo::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeClosedStreamInfoByPageResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeClosedStreamInfoByPageResponse.php
new file mode 100755
index 0000000..658d55f
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeClosedStreamInfoByPageResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.DescribeClosedStreamInfoByPageResponse</code>
+ */
+class DescribeClosedStreamInfoByPageResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.ClosedStreamInfo Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Live\Models\Business\ClosedStreamInfo $Result
+     *          返回结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.ClosedStreamInfo Result = 2;</code>
+     * @return \Volc\Service\Live\Models\Business\ClosedStreamInfo|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.ClosedStreamInfo Result = 2;</code>
+     * @param \Volc\Service\Live\Models\Business\ClosedStreamInfo $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Live\Models\Business\ClosedStreamInfo::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeDenyConfigResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeDenyConfigResponse.php
new file mode 100755
index 0000000..a42c8d7
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeDenyConfigResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.DescribeDenyConfigResponse</code>
+ */
+class DescribeDenyConfigResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.DescribeDenyConfigResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Live\Models\Business\DescribeDenyConfigResult $Result
+     *          返回结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.DescribeDenyConfigResult Result = 2;</code>
+     * @return \Volc\Service\Live\Models\Business\DescribeDenyConfigResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.DescribeDenyConfigResult Result = 2;</code>
+     * @param \Volc\Service\Live\Models\Business\DescribeDenyConfigResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Live\Models\Business\DescribeDenyConfigResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeDomainResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeDomainResponse.php
new file mode 100755
index 0000000..2917175
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeDomainResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.DescribeDomainResponse</code>
+ */
+class DescribeDomainResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.DomainListInfo Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Live\Models\Business\DomainListInfo $Result
+     *          返回结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.DomainListInfo Result = 2;</code>
+     * @return \Volc\Service\Live\Models\Business\DomainListInfo|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.DomainListInfo Result = 2;</code>
+     * @param \Volc\Service\Live\Models\Business\DomainListInfo $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Live\Models\Business\DomainListInfo::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeForbiddenStreamInfoByPageResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeForbiddenStreamInfoByPageResponse.php
new file mode 100755
index 0000000..74fcf19
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeForbiddenStreamInfoByPageResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.DescribeForbiddenStreamInfoByPageResponse</code>
+ */
+class DescribeForbiddenStreamInfoByPageResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.ForbiddenStreamInfo Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Live\Models\Business\ForbiddenStreamInfo $Result
+     *          返回结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.ForbiddenStreamInfo Result = 2;</code>
+     * @return \Volc\Service\Live\Models\Business\ForbiddenStreamInfo|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.ForbiddenStreamInfo Result = 2;</code>
+     * @param \Volc\Service\Live\Models\Business\ForbiddenStreamInfo $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Live\Models\Business\ForbiddenStreamInfo::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeLiveStreamInfoByPageResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeLiveStreamInfoByPageResponse.php
new file mode 100755
index 0000000..7ce2872
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeLiveStreamInfoByPageResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.DescribeLiveStreamInfoByPageResponse</code>
+ */
+class DescribeLiveStreamInfoByPageResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.LiveStreamInfo Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Live\Models\Business\LiveStreamInfo $Result
+     *          返回结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.LiveStreamInfo Result = 2;</code>
+     * @return \Volc\Service\Live\Models\Business\LiveStreamInfo|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.LiveStreamInfo Result = 2;</code>
+     * @param \Volc\Service\Live\Models\Business\LiveStreamInfo $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Live\Models\Business\LiveStreamInfo::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeLiveStreamStateResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeLiveStreamStateResponse.php
new file mode 100755
index 0000000..8a4565f
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeLiveStreamStateResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.DescribeLiveStreamStateResponse</code>
+ */
+class DescribeLiveStreamStateResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.StreamStateInfo Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Live\Models\Business\StreamStateInfo $Result
+     *          返回结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.StreamStateInfo Result = 2;</code>
+     * @return \Volc\Service\Live\Models\Business\StreamStateInfo|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.StreamStateInfo Result = 2;</code>
+     * @param \Volc\Service\Live\Models\Business\StreamStateInfo $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Live\Models\Business\StreamStateInfo::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeRecordTaskFileHistoryResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeRecordTaskFileHistoryResponse.php
new file mode 100755
index 0000000..3beac2c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeRecordTaskFileHistoryResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.DescribeRecordTaskFileHistoryResponse</code>
+ */
+class DescribeRecordTaskFileHistoryResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.RecordHistoryInfo Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Live\Models\Business\RecordHistoryInfo $Result
+     *          返回结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.RecordHistoryInfo Result = 2;</code>
+     * @return \Volc\Service\Live\Models\Business\RecordHistoryInfo|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.RecordHistoryInfo Result = 2;</code>
+     * @param \Volc\Service\Live\Models\Business\RecordHistoryInfo $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Live\Models\Business\RecordHistoryInfo::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeRelaySourceResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeRelaySourceResponse.php
new file mode 100755
index 0000000..70ca2d4
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeRelaySourceResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.DescribeRelaySourceResponse</code>
+ */
+class DescribeRelaySourceResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.RelaySourceConfigList Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Live\Models\Business\RelaySourceConfigList $Result
+     *          返回结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.RelaySourceConfigList Result = 2;</code>
+     * @return \Volc\Service\Live\Models\Business\RelaySourceConfigList|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.RelaySourceConfigList Result = 2;</code>
+     * @param \Volc\Service\Live\Models\Business\RelaySourceConfigList $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Live\Models\Business\RelaySourceConfigList::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeVQScoreTaskResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeVQScoreTaskResponse.php
new file mode 100755
index 0000000..24ae07c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DescribeVQScoreTaskResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.DescribeVQScoreTaskResponse</code>
+ */
+class DescribeVQScoreTaskResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.VQScoreInfo Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Live\Models\Business\VQScoreInfo $Result
+     *          返回结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.VQScoreInfo Result = 2;</code>
+     * @return \Volc\Service\Live\Models\Business\VQScoreInfo|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.VQScoreInfo Result = 2;</code>
+     * @param \Volc\Service\Live\Models\Business\VQScoreInfo $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Live\Models\Business\VQScoreInfo::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DisableDomainResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DisableDomainResponse.php
new file mode 100755
index 0000000..82577fe
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/DisableDomainResponse.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.DisableDomainResponse</code>
+ */
+class DisableDomainResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/EnableDomainResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/EnableDomainResponse.php
new file mode 100755
index 0000000..3fcd576
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/EnableDomainResponse.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.EnableDomainResponse</code>
+ */
+class EnableDomainResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/ForbidStreamResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/ForbidStreamResponse.php
new file mode 100755
index 0000000..92b402d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/ForbidStreamResponse.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.ForbidStreamResponse</code>
+ */
+class ForbidStreamResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/GeneratePlayURLResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/GeneratePlayURLResponse.php
new file mode 100755
index 0000000..9c1321e
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/GeneratePlayURLResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.GeneratePlayURLResponse</code>
+ */
+class GeneratePlayURLResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.GeneratePlayURLResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Live\Models\Business\GeneratePlayURLResult $Result
+     *          返回结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.GeneratePlayURLResult Result = 2;</code>
+     * @return \Volc\Service\Live\Models\Business\GeneratePlayURLResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.GeneratePlayURLResult Result = 2;</code>
+     * @param \Volc\Service\Live\Models\Business\GeneratePlayURLResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Live\Models\Business\GeneratePlayURLResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/GeneratePushURLResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/GeneratePushURLResponse.php
new file mode 100755
index 0000000..ee467ce
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/GeneratePushURLResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.GeneratePushURLResponse</code>
+ */
+class GeneratePushURLResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.GeneratePushURLResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Live\Models\Business\GeneratePushURLResult $Result
+     *          返回结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.GeneratePushURLResult Result = 2;</code>
+     * @return \Volc\Service\Live\Models\Business\GeneratePushURLResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.GeneratePushURLResult Result = 2;</code>
+     * @param \Volc\Service\Live\Models\Business\GeneratePushURLResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Live\Models\Business\GeneratePushURLResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/KillStreamResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/KillStreamResponse.php
new file mode 100755
index 0000000..ebec258
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/KillStreamResponse.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.KillStreamResponse</code>
+ */
+class KillStreamResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/ListDomainDetailResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/ListDomainDetailResponse.php
new file mode 100755
index 0000000..3e88dec
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/ListDomainDetailResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.ListDomainDetailResponse</code>
+ */
+class ListDomainDetailResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.DomainListInfo Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Live\Models\Business\DomainListInfo $Result
+     *          返回结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.DomainListInfo Result = 2;</code>
+     * @return \Volc\Service\Live\Models\Business\DomainListInfo|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.DomainListInfo Result = 2;</code>
+     * @param \Volc\Service\Live\Models\Business\DomainListInfo $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Live\Models\Business\DomainListInfo::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/ListPullToPushTaskResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/ListPullToPushTaskResponse.php
new file mode 100755
index 0000000..bd7c0ab
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/ListPullToPushTaskResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.ListPullToPushTaskResponse</code>
+ */
+class ListPullToPushTaskResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.ListPullToPushTaskResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Live\Models\Business\ListPullToPushTaskResult $Result
+     *          返回结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.ListPullToPushTaskResult Result = 2;</code>
+     * @return \Volc\Service\Live\Models\Business\ListPullToPushTaskResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.ListPullToPushTaskResult Result = 2;</code>
+     * @param \Volc\Service\Live\Models\Business\ListPullToPushTaskResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Live\Models\Business\ListPullToPushTaskResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/ListVQScoreTaskResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/ListVQScoreTaskResponse.php
new file mode 100755
index 0000000..8eb5cc1
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/ListVQScoreTaskResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.ListVQScoreTaskResponse</code>
+ */
+class ListVQScoreTaskResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.VQScoreTaskListInfo Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Live\Models\Business\VQScoreTaskListInfo $Result
+     *          返回结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.VQScoreTaskListInfo Result = 2;</code>
+     * @return \Volc\Service\Live\Models\Business\VQScoreTaskListInfo|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *返回结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Live.Models.Business.VQScoreTaskListInfo Result = 2;</code>
+     * @param \Volc\Service\Live\Models\Business\VQScoreTaskListInfo $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Live\Models\Business\VQScoreTaskListInfo::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/ManagerPullPushDomainBindResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/ManagerPullPushDomainBindResponse.php
new file mode 100755
index 0000000..5c79437
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/ManagerPullPushDomainBindResponse.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.ManagerPullPushDomainBindResponse</code>
+ */
+class ManagerPullPushDomainBindResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/RestartPullToPushTaskResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/RestartPullToPushTaskResponse.php
new file mode 100755
index 0000000..c3c59bd
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/RestartPullToPushTaskResponse.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.RestartPullToPushTaskResponse</code>
+ */
+class RestartPullToPushTaskResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/ResumeStreamResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/ResumeStreamResponse.php
new file mode 100755
index 0000000..9762dac
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/ResumeStreamResponse.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.ResumeStreamResponse</code>
+ */
+class ResumeStreamResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/StopPullToPushTaskResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/StopPullToPushTaskResponse.php
new file mode 100755
index 0000000..62a8159
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/StopPullToPushTaskResponse.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.StopPullToPushTaskResponse</code>
+ */
+class StopPullToPushTaskResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/UpdateDenyConfigResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/UpdateDenyConfigResponse.php
new file mode 100755
index 0000000..a7835f3
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/UpdateDenyConfigResponse.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.UpdateDenyConfigResponse</code>
+ */
+class UpdateDenyConfigResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/UpdatePullToPushTaskResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/UpdatePullToPushTaskResponse.php
new file mode 100755
index 0000000..31acc30
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/UpdatePullToPushTaskResponse.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.UpdatePullToPushTaskResponse</code>
+ */
+class UpdatePullToPushTaskResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/UpdateRelaySourceResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/UpdateRelaySourceResponse.php
new file mode 100755
index 0000000..a09db9b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Live/Models/Response/UpdateRelaySourceResponse.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: live/response/response_live.proto
+
+namespace Volc\Service\Live\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Live.Models.Response.UpdateRelaySourceResponse</code>
+ */
+class UpdateRelaySourceResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Live\Models\GPBMetadata\ResponseLive::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Notify.php b/vendor/volcengine/volc-sdk-php/src/Service/Notify.php
new file mode 100755
index 0000000..58d0cd9
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Notify.php
@@ -0,0 +1,324 @@
+<?php
+
+namespace Volc\Service;
+
+use ArrayObject;
+use Exception;
+use Volc\Base\V4Curl;
+
+class Notify extends V4Curl
+{
+    /**
+     * @throws Exception
+     */
+    protected function getConfig(string $region): array
+    {
+        switch ($region){
+            case 'cn-north-1':
+                $config = [
+                    'host' => 'https://cloud-vms.volcengineapi.com',
+                    'config' => [
+                        'timeout' => 10.0,
+                        'headers' => [
+                            'Accept' => 'application/json'
+                        ],
+                        'v4_credentials' => [
+                            'region' => 'cn-north-1',
+                            'service' => 'volc_voice_notify',
+                        ],
+                    ],
+                ];
+                break;
+            default:
+                throw new Exception(sprintf("This region not support now, %s", $region));
+        }
+        return $config;
+    }
+
+    protected $apiList = [
+        'CreateTask' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'CreateTask',
+                    'Version' => '2021-01-01',
+                ],
+            ]
+        ],
+
+        'BatchAppend' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'BatchAppend',
+                    'Version' => '2021-01-01',
+                ],
+            ]
+        ],
+
+        'PauseTask' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'PauseTask',
+                    'Version' => '2021-01-01',
+                ],
+            ]
+        ],
+
+        'ResumeTask' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'ResumeTask',
+                    'Version' => '2021-01-01',
+                ],
+            ]
+        ],
+
+        'StopTask' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'StopTask',
+                    'Version' => '2021-01-01',
+                ],
+            ]
+        ],
+
+        'UpdateTask' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'UpdateTask',
+                    'Version' => '2021-01-01',
+                ],
+            ]
+        ],
+
+        'SingleBatchAppend' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'SingleBatchAppend',
+                    'Version' => '2021-01-01',
+                ],
+            ]
+        ],
+
+
+        'SingleInfo' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'SingleInfo',
+                    'Version' => '2021-01-01',
+                ],
+            ]
+        ],
+
+
+        'SingleCancel' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'SingleCancel',
+                    'Version' => '2021-01-01',
+                ],
+            ]
+        ],
+
+        'FetchResource' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'FetchResource',
+                    'Version' => '2021-01-01',
+                ],
+            ]
+        ],
+
+        'OpenCreateTts' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'OpenCreateTts',
+                    'Version' => '2021-01-01',
+                ],
+            ]
+        ],
+
+        'OpenDeleteResource' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'OpenDeleteResource',
+                    'Version' => '2021-01-01',
+                ],
+            ]
+        ],
+
+        'GetResourceUploadUrl' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetResourceUploadUrl',
+                    'Version' => '2021-01-01',
+                ],
+            ]
+        ],
+
+        'CommitResourceUpload' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'CommitResourceUpload',
+                    'Version' => '2021-01-01',
+                ],
+            ]
+        ],
+
+        'QueryOpenGetResource' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'QueryOpenGetResource',
+                    'Version' => '2021-01-01',
+                ],
+            ]
+        ],
+    ];
+
+    protected function requestWithRetry(string $api, array $configs): string
+    {
+        try {
+            $response = $this->request($api, $configs);
+            return (string)$response->getBody();
+        }
+        catch (Exception $e)
+        {
+            $response = $this->request($api, $configs);
+            return (string)$response->getBody();
+        }
+    }
+
+
+    public function CreateTask(array $data = []): string
+    {
+        if (empty($data)){
+            $data = new ArrayObject();
+        }
+        return $this->requestWithRetry("CreateTask", ['json' => $data]);
+    }
+
+
+    public function BatchAppend(array $data = []): string
+    {
+        if (empty($data)){
+            $data = new ArrayObject();
+        }
+        return $this->requestWithRetry("BatchAppend", ['json' => $data]);
+    }
+
+
+    public function  PauseTask(string $taskId): string
+    {
+        return $this->requestWithRetry("PauseTask", ['query' => ['TaskOpenId' => $taskId]]);
+    }
+
+    public function ResumeTask(string $taskId): string
+    {
+        return $this->requestWithRetry("ResumeTask", ['query' => ['TaskOpenId' => $taskId]]);
+    }
+
+    public function StopTask(string $taskId) : string
+    {
+        return $this->requestWithRetry("StopTask", ['query' => ['TaskOpenId' => $taskId]]);
+    }
+
+    public function UpdateTask(array $data = []): string
+    {
+        if (empty($data)){
+            $data = new ArrayObject();
+        }
+        return $this->requestWithRetry("UpdateTask", ['json' => $data]);
+    }
+
+    public function SingleBatchAppend(array $data = []): string
+    {
+        if (empty($data)){
+            $data = new ArrayObject();
+        }
+        return $this->requestWithRetry("SingleBatchAppend", ['json' => $data]);
+    }
+
+    public function SingleInfo(string $singleOpenId): string
+    {
+        return $this->requestWithRetry("SingleInfo", ['query' => ["SingleOpenId" => $singleOpenId]]);
+    }
+
+    public function SingleCancel(string $singleOpenId): string
+    {
+        return $this->requestWithRetry("SingleCancel", ['query' => ["SingleOpenId" => $singleOpenId]]);
+    }
+
+    public function FetchResource(array $data = []): string
+    {
+        if (empty($data)){
+            $data = new ArrayObject();
+        }
+        return $this->requestWithRetry("FetchResource", ['json' => $data]);
+    }
+
+    public function  CreateTtsResource(array $data = []): string
+    {
+        if (empty($data)){
+            $data = new ArrayObject();
+        }
+        return $this->requestWithRetry("OpenCreateTts", ['json' => $data]);
+    }
+
+    public function DeleteResourceByKey(string $resourceKey): string
+    {
+        return $this->requestWithRetry("OpenDeleteResource", ['query' => ['ResourceKey' => $resourceKey], 'json' => []]);
+    }
+
+    public function GetResourceUploadUrl(array $data = []): string
+    {
+        if (empty($data)){
+            $data = new ArrayObject();
+        }
+        return $this->requestWithRetry("GetResourceUploadUrl", ['json' => $data]);
+    }
+
+    public function CommitResourceUpload(array $data = []): string
+    {
+        if (empty($data)){
+            $data = new ArrayObject();
+        }
+        return $this->requestWithRetry("CommitResourceUpload", ['json' => $data]);
+    }
+
+    public function QueryResource(array $data = []): string
+    {
+        if (empty($data)){
+            $data = new ArrayObject();
+        }
+        return $this->requestWithRetry("QueryOpenGetResource", ['json' => $data]);
+    }
+}
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Rtc.php b/vendor/volcengine/volc-sdk-php/src/Service/Rtc.php
new file mode 100755
index 0000000..5ef21a7
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Rtc.php
@@ -0,0 +1,78 @@
+<?php
+
+
+namespace Volc\Service;
+
+use Volc\Base\V4Curl;
+
+class Rtc extends V4Curl
+{
+    public function __construct()
+    {
+        $this->region = func_get_arg(0);
+        parent::__construct($this->region);
+    }
+
+    protected function getConfig(string $region = 'cn-north-1')
+    {
+        //公共配置
+        return [
+            'host' => "https://rtc.volcengineapi.com",
+            'config' => [
+                'timeout' => 10.0,
+                'headers' => [
+                    'Accept' => 'application/json'
+                ],
+                'v4_credentials' => [
+                    'region' => $region,
+                    'service' => 'rtc',
+                ],
+            ],
+        ];
+    }
+
+    //OpenAPI 配置: 主要是method、Action、version
+    protected $apiList = [
+        'ListIndicators' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListIndicators',
+                    'Version' => '2020-12-01',
+                ]
+            ]
+        ],
+        'ListRoomInformation' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListRoomInformation',
+                    'Version' => '2020-12-01',
+                ]
+            ]
+        ]
+    ];
+
+    public function requestWithRetry(string $api, array $configs): string
+    {
+        try {
+            $response = $this->request($api, $configs);
+            return (string)$response->getBody();
+        } catch (\Exception $e) {
+            $response = $this->request($api, $configs);
+            return (string)$response->getBody();
+        }
+    }
+
+    public function listIndicators(array $query = []): string
+    {
+        return $this->requestWithRetry("ListIndicators", $query);
+    }
+
+    public function listRoomInformation(array $query = []): string
+    {
+        return $this->requestWithRetry("ListRoomInformation", $query);
+    }
+}
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/SecretNumber.php b/vendor/volcengine/volc-sdk-php/src/Service/SecretNumber.php
new file mode 100755
index 0000000..7b395a4
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/SecretNumber.php
@@ -0,0 +1,282 @@
+<?php
+
+namespace Volc\Service;
+
+use ArrayObject;
+use Exception;
+use Volc\Base\V4Curl;
+
+class SecretNumber extends V4Curl
+{
+
+    /**
+     * @throws Exception
+     */
+    protected function getConfig(string $region): array
+    {
+        // TODO: Implement getConfig() method.
+        switch ($region){
+            case 'cn-north-1':
+                $config = [
+                    'host' => 'https://cloud-vms.volcengineapi.com',
+                    'config' => [
+                        'timeout' => 10.0,
+                        'headers' => [
+                            'Accept' => 'application/json'
+                        ],
+                        'v4_credentials' => [
+                            'region' => 'cn-north-1',
+                            'service' => 'volc_secret_number',
+                        ],
+                    ],
+                ];
+                break;
+            default:
+                throw new Exception(sprintf("This region not support now, %s", $region));
+        }
+        return $config;
+    }
+
+    protected $apiList = [
+        'BindAXB' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'BindAXB',
+                    'Version' => '2020-09-01',
+                ],
+            ]
+        ],
+
+        'SelectNumberAndBindAXB' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'SelectNumberAndBindAXB',
+                    'Version' => '2020-09-01',
+                ],
+            ]
+        ],
+
+        'UnbindAXB' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'UnbindAXB',
+                    'Version' => '2020-09-01',
+                ],
+            ]
+        ],
+
+        'QuerySubscription' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'QuerySubscription',
+                    'Version' => '2020-09-01',
+                ],
+            ]
+        ],
+
+        'QuerySubscriptionForList' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'QuerySubscriptionForList',
+                    'Version' => '2020-09-01',
+                ],
+            ]
+        ],
+
+        'UpgradeAXToAXB' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'UpgradeAXToAXB',
+                    'Version' => '2020-09-01',
+                ],
+            ]
+        ],
+
+        'UpdateAXB' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'UpdateAXB',
+                    'Version' => '2020-09-01',
+                ],
+            ]
+        ],
+
+        'BindAXN' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'BindAXN',
+                    'Version' => '2020-09-01',
+                ],
+            ]
+        ],
+
+        'UpdateAXN' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'UpdateAXN',
+                    'Version' => '2020-09-01',
+                ],
+            ]
+        ],
+
+        'UnbindAXN' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'UnbindAXN',
+                    'Version' => '2020-09-01',
+                ],
+            ]
+        ],
+
+        'Click2Call' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'Click2Call',
+                    'Version' => '2021-09-01',
+                ],
+            ]
+        ],
+
+        'Click2CallLite' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'Click2CallLite',
+                    'Version' => '2021-09-01',
+                ],
+            ]
+        ],
+    ];
+
+    protected function requestWithRetry(string $api, array $configs): string
+    {
+        try {
+            $response = $this->request($api, $configs);
+            return (string)$response->getBody();
+        }
+        catch (Exception $e)
+        {
+            $response = $this->request($api, $configs);
+            return (string)$response->getBody();
+        }
+    }
+
+    public function BindAXB(array $data = []): string
+    {
+        if (empty($data)){
+            $data = new ArrayObject();
+        }
+        return $this->requestWithRetry("BindAXB", ['form_params' => $data]);
+    }
+
+    public function SelectNumberAndBindAXB(array $data = []): string
+    {
+        if (empty($data)){
+            $data = new ArrayObject();
+        }
+        return $this->requestWithRetry("SelectNumberAndBindAXB", ['form_params' => $data]);
+    }
+
+    public function UnbindAXB(array $data = []): string
+    {
+        if (empty($data)){
+            $data = new ArrayObject();
+        }
+        return $this->requestWithRetry("UnbindAXB", ['form_params' => $data]);
+    }
+
+    public function QuerySubscription(array $data = []): string
+    {
+        if (empty($data)){
+            $data = new ArrayObject();
+        }
+        return $this->requestWithRetry("QuerySubscription", ['form_params' => $data]);
+    }
+
+    public function QuerySubscriptionForList(array $data = []): string
+    {
+        if (empty($data)){
+            $data = new ArrayObject();
+        }
+        return $this->requestWithRetry("QuerySubscriptionForList", ['form_params' => $data]);
+    }
+
+    public function UpgradeAXToAXB(array $data = []): string
+    {
+        if (empty($data)){
+            $data = new ArrayObject();
+        }
+        return $this->requestWithRetry("UpgradeAXToAXB", ['form_params' => $data]);
+    }
+
+    public function UpdateAXB(array $data = []): string
+    {
+        if (empty($data)){
+            $data = new ArrayObject();
+        }
+        return $this->requestWithRetry("UpdateAXB", ['form_params' => $data]);
+    }
+
+    public function BindAXN(array $data = []): string
+    {
+        if (empty($data)){
+            $data = new ArrayObject();
+        }
+        return $this->requestWithRetry("BindAXN", ['form_params' => $data]);
+    }
+
+    public function UpdateAXN(array $data = []): string
+    {
+        if (empty($data)){
+            $data = new ArrayObject();
+        }
+        return $this->requestWithRetry("UpdateAXN", ['form_params' => $data]);
+    }
+
+    public function UnbindAXN(array $data = []): string
+    {
+        if (empty($data)){
+            $data = new ArrayObject();
+        }
+        return $this->requestWithRetry("UnbindAXN", ['form_params' => $data]);
+    }
+
+    public function Click2Call(array $data = []): string
+    {
+        if (empty($data)){
+            $data = new ArrayObject();
+        }
+        return $this->requestWithRetry("Click2Call", ['form_params' => $data]);
+    }
+
+    public function Click2CallLite(array $data = []): string
+    {
+        if (empty($data)){
+            $data = new ArrayObject();
+        }
+        return $this->requestWithRetry("Click2CallLite", ['form_params' => $data]);
+    }
+}
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Sms.php b/vendor/volcengine/volc-sdk-php/src/Service/Sms.php
new file mode 100755
index 0000000..ae450aa
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Sms.php
@@ -0,0 +1,60 @@
+<?php
+
+
+namespace Volc\Service;
+
+use Volc\Base\V4Curl;
+
+class Sms extends V4Curl
+{
+
+    protected function getConfig(string $region = '')
+    {
+
+        switch ($region){
+            case 'ap-singapore-1':
+                $host = 'https://sms.byteplusapi.com';
+                $region = 'ap-singapore-1';
+                break;
+            default:
+                $host = 'https://sms.volcengineapi.com';
+                $region = 'cn-north-1';
+        }
+        return [
+            'host' => $host,
+            'config' => [
+                'timeout' => 10.0,
+                'headers' => [
+                    'Accept' => 'application/json'
+                ],
+                'v4_credentials' => [
+                    'region' => $region,
+                    'service' => 'volcSMS',
+                ],
+            ],
+        ];
+    }
+
+    protected $apiList = [
+        'SendSms' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'SendSms',
+                    'Version' => '2020-01-01',
+                ],
+            ]
+        ],
+    ];
+
+    public function sendSms(array $query = [])
+    {
+        $response = $this->request('SendSms', $query);
+        if ($response->getStatusCode() >= 500) {
+            $response = $this->request('SendSms', $query);
+        }
+        return $response->getBody();
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Sts.php b/vendor/volcengine/volc-sdk-php/src/Service/Sts.php
new file mode 100755
index 0000000..d7ffd7f
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Sts.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Volc\Service;
+
+use Volc\Base\V4Curl;
+
+class Sts extends V4Curl
+{
+    protected function getConfig(string $region = '')
+    {
+        return [
+            'host' => 'https://sts.volcengineapi.com',
+            'config' => [
+                'timeout' => 5.0,
+                'headers' => [
+                    'Accept' => 'application/json'
+                ],
+                'v4_credentials' => [
+                    'region' => 'cn-north-1',
+                    'service' => 'sts',
+                ],
+            ],
+        ];
+    }
+
+    protected $apiList = [
+        'AssumeRole' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'AssumeRole',
+                    'Version' => '2018-01-01',
+                ],
+            ],
+        ],
+    ];
+
+    public function assumeRole(array $query = [])
+    {
+        $response = $this->request('AssumeRole', $query);
+        return $response->getBody();
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/VEdit.php b/vendor/volcengine/volc-sdk-php/src/Service/VEdit.php
new file mode 100755
index 0000000..de9736b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/VEdit.php
@@ -0,0 +1,84 @@
+<?php
+
+namespace Volc\Service;
+
+use Volc\Base\V4Curl;
+
+class VEdit extends V4Curl
+{
+    protected function getConfig(string $region)
+    {
+        switch ($region) {
+            case 'cn-north-1':
+                $config = [
+                    'host' => 'https://vedit.volcengineapi.com',
+                    'config' => [
+                        'timeout' => 5.0,
+                        'headers' => [
+                            'Accept' => 'application/json'
+                        ],
+                        'v4_credentials' => [
+                            'region' => 'cn-north-1',
+                            'service' => 'edit',
+                        ],
+                    ],
+                ];
+                break;
+            default:
+                throw new \Exception(sprintf("edit not support region, %s", $region));
+        }
+        return $config;
+    }
+
+    protected $apiList = [
+        'SubmitDirectEditTaskAsync' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'SubmitDirectEditTaskAsync',
+                    'Version' => '2018-01-01',
+                ],
+            ]
+        ],
+        'SubmitTemplateTaskAsync' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'SubmitTemplateTaskAsync',
+                    'Version' => '2018-01-01',
+                ],
+            ]
+        ],
+        'GetDirectEditResult' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetDirectEditResult',
+                    'Version' => '2018-01-01',
+                ],
+            ]
+        ],
+    ];
+
+
+    public function submitTemplateTaskAsync(array $query = [])
+    {
+        $response = $this->request('SubmitTemplateTaskAsync', $query);
+        return $response->getBody();
+    }
+
+    public function submitDirectEditTaskAsync(array $query = [])
+    {
+        $response = $this->request('SubmitDirectEditTaskAsync', $query);
+        return $response->getBody();
+    }
+
+    public function getDirectEditResult(array $query = [])
+    {
+        $response = $this->request('GetDirectEditResult', $query);
+        return $response->getBody();
+    }
+}
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Visual.php b/vendor/volcengine/volc-sdk-php/src/Service/Visual.php
new file mode 100755
index 0000000..2a2e2b9
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Visual.php
@@ -0,0 +1,173 @@
+<?php
+
+namespace Volc\Service;
+
+use Volc\Base\V4Curl;
+
+class Visual extends V4Curl
+{
+    protected function getConfig(string $region = '')
+    {
+        return [
+            'host' => 'https://visual.volcengineapi.com',
+            'config' => [
+                'timeout' => 10.0,
+                'headers' => [
+                    'Accept' => 'application/json'
+                ],
+                'v4_credentials' => [
+                    'region' => 'cn-north-1',
+                    'service' => 'cv',
+                ],
+            ],
+        ];
+    }
+
+    public function JPCartoon(array $query = [])
+    {
+        $response = $this->request('JPCartoon', $query);
+        return $response->getBody();
+    }
+
+    public function FaceSwap(array $query = [])
+    {
+        $response = $this->request('FaceSwap', $query);
+        return $response->getBody();
+    }
+
+    public function HumanSegment(array $query = [])
+    {
+        $response = $this->request('HumanSegment', $query);
+        return $response->getBody();
+    }
+
+    public function GeneralSegment(array $query = [])
+    {
+        $response = $this->request('GeneralSegment', $query);
+        return $response->getBody();
+    }
+
+    public function EnhancePhoto(array $query = [])
+    {
+        $response = $this->request('EnhancePhoto', $query);
+        return $response->getBody();
+    }
+
+    public function ConvertPhoto(array $query = [])
+    {
+        $response = $this->request('ConvertPhoto', $query);
+        return $response->getBody();
+    }
+
+    public function OCRNormal(array $query = [])
+    {
+        $response = $this->request('OCRNormal', $query);
+        return $response->getBody();
+    }
+
+    public function IDCard(array $query = [])
+    {
+        $response = $this->request('IDCard', $query);
+        return $response->getBody();
+    }
+
+    public function BankCard(array $query = [])
+    {
+        $response = $this->request('BankCard', $query);
+        return $response->getBody();
+    }
+
+    protected $apiList = [
+        'JPCartoon' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'JPCartoon',
+                    'Version' => '2020-08-26',
+                ],
+            ],
+        ],
+        'FaceSwap' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'FaceSwap',
+                    'Version' => '2020-08-26',
+                ],
+            ],
+        ],
+        'HumanSegment' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'HumanSegment',
+                    'Version' => '2020-08-26',
+                ],
+            ],
+        ],
+        'GeneralSegment' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'GeneralSegment',
+                    'Version' => '2020-08-26',
+                ],
+            ],
+        ],
+        'EnhancePhoto' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'EnhancePhoto',
+                    'Version' => '2020-08-26',
+                ],
+            ],
+        ],
+        'ConvertPhoto' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'ConvertPhoto',
+                    'Version' => '2020-08-26',
+                ],
+            ],
+        ],
+        'OCRNormal' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'OCRNormal',
+                    'Version' => '2020-08-26',
+                ],
+            ],
+        ],
+        'IDCard' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'IDCard',
+                    'Version' => '2020-08-26',
+                ],
+            ],
+        ],
+        'BankCard' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'BankCard',
+                    'Version' => '2020-08-26',
+                ],
+            ],
+        ],
+
+    ];
+}
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/CategoryTagInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/CategoryTagInfo.php
new file mode 100755
index 0000000..3c65c0b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/CategoryTagInfo.php
@@ -0,0 +1,201 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_workflow.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.CategoryTagInfo</code>
+ */
+class CategoryTagInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 标签id
+     *
+     * Generated from protobuf field <code>int64 TagId = 1;</code>
+     */
+    protected $TagId = 0;
+    /**
+     * 置信度
+     *
+     * Generated from protobuf field <code>double Prob = 2;</code>
+     */
+    protected $Prob = 0.0;
+    /**
+     * 标签名称
+     *
+     * Generated from protobuf field <code>string TagName = 3;</code>
+     */
+    protected $TagName = '';
+    /**
+     * 等级
+     *
+     * Generated from protobuf field <code>int64 Level = 4;</code>
+     */
+    protected $Level = 0;
+    /**
+     * 父节点标签信息
+     *
+     * Generated from protobuf field <code>map<string, string> ParentInfo = 5;</code>
+     */
+    private $ParentInfo;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int|string $TagId
+     *           标签id
+     *     @type float $Prob
+     *           置信度
+     *     @type string $TagName
+     *           标签名称
+     *     @type int|string $Level
+     *           等级
+     *     @type array|\Google\Protobuf\Internal\MapField $ParentInfo
+     *           父节点标签信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodWorkflow::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 标签id
+     *
+     * Generated from protobuf field <code>int64 TagId = 1;</code>
+     * @return int|string
+     */
+    public function getTagId()
+    {
+        return $this->TagId;
+    }
+
+    /**
+     * 标签id
+     *
+     * Generated from protobuf field <code>int64 TagId = 1;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setTagId($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->TagId = $var;
+
+        return $this;
+    }
+
+    /**
+     * 置信度
+     *
+     * Generated from protobuf field <code>double Prob = 2;</code>
+     * @return float
+     */
+    public function getProb()
+    {
+        return $this->Prob;
+    }
+
+    /**
+     * 置信度
+     *
+     * Generated from protobuf field <code>double Prob = 2;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setProb($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->Prob = $var;
+
+        return $this;
+    }
+
+    /**
+     * 标签名称
+     *
+     * Generated from protobuf field <code>string TagName = 3;</code>
+     * @return string
+     */
+    public function getTagName()
+    {
+        return $this->TagName;
+    }
+
+    /**
+     * 标签名称
+     *
+     * Generated from protobuf field <code>string TagName = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTagName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TagName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 等级
+     *
+     * Generated from protobuf field <code>int64 Level = 4;</code>
+     * @return int|string
+     */
+    public function getLevel()
+    {
+        return $this->Level;
+    }
+
+    /**
+     * 等级
+     *
+     * Generated from protobuf field <code>int64 Level = 4;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setLevel($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->Level = $var;
+
+        return $this;
+    }
+
+    /**
+     * 父节点标签信息
+     *
+     * Generated from protobuf field <code>map<string, string> ParentInfo = 5;</code>
+     * @return \Google\Protobuf\Internal\MapField
+     */
+    public function getParentInfo()
+    {
+        return $this->ParentInfo;
+    }
+
+    /**
+     * 父节点标签信息
+     *
+     * Generated from protobuf field <code>map<string, string> ParentInfo = 5;</code>
+     * @param array|\Google\Protobuf\Internal\MapField $var
+     * @return $this
+     */
+    public function setParentInfo($var)
+    {
+        $arr = GPBUtil::checkMapField($var, \Google\Protobuf\Internal\GPBType::STRING, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->ParentInfo = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/Clip.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/Clip.php
new file mode 100755
index 0000000..bf96e65
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/Clip.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_workflow.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.Clip</code>
+ */
+class Clip extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 开始时间 ms
+     *
+     * Generated from protobuf field <code>int32 StartTime = 1;</code>
+     */
+    protected $StartTime = 0;
+    /**
+     * 结束时间 ms
+     *
+     * Generated from protobuf field <code>int32 EndTime = 2;</code>
+     */
+    protected $EndTime = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int $StartTime
+     *           开始时间 ms
+     *     @type int $EndTime
+     *           结束时间 ms
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodWorkflow::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 开始时间 ms
+     *
+     * Generated from protobuf field <code>int32 StartTime = 1;</code>
+     * @return int
+     */
+    public function getStartTime()
+    {
+        return $this->StartTime;
+    }
+
+    /**
+     * 开始时间 ms
+     *
+     * Generated from protobuf field <code>int32 StartTime = 1;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setStartTime($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->StartTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * 结束时间 ms
+     *
+     * Generated from protobuf field <code>int32 EndTime = 2;</code>
+     * @return int
+     */
+    public function getEndTime()
+    {
+        return $this->EndTime;
+    }
+
+    /**
+     * 结束时间 ms
+     *
+     * Generated from protobuf field <code>int32 EndTime = 2;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setEndTime($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->EndTime = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/DeLogoInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/DeLogoInfo.php
new file mode 100755
index 0000000..bfa7330
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/DeLogoInfo.php
@@ -0,0 +1,235 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_workflow.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.DeLogoInfo</code>
+ */
+class DeLogoInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Anchor基准宽度
+     *
+     * Generated from protobuf field <code>int64 AnchorWidth = 1;</code>
+     */
+    protected $AnchorWidth = 0;
+    /**
+     * Anchor基准宽度
+     *
+     * Generated from protobuf field <code>int64 AnchorHeight = 2;</code>
+     */
+    protected $AnchorHeight = 0;
+    /**
+     * 水印左上角X坐标
+     *
+     * Generated from protobuf field <code>int64 PosX = 3;</code>
+     */
+    protected $PosX = 0;
+    /**
+     * 水印左上角Y坐标
+     *
+     * Generated from protobuf field <code>int64 PosY = 4;</code>
+     */
+    protected $PosY = 0;
+    /**
+     * 水印宽度
+     *
+     * Generated from protobuf field <code>int64 SizeX = 5;</code>
+     */
+    protected $SizeX = 0;
+    /**
+     * 水印高度
+     *
+     * Generated from protobuf field <code>int64 SizeY = 6;</code>
+     */
+    protected $SizeY = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int|string $AnchorWidth
+     *           Anchor基准宽度
+     *     @type int|string $AnchorHeight
+     *           Anchor基准宽度
+     *     @type int|string $PosX
+     *           水印左上角X坐标
+     *     @type int|string $PosY
+     *           水印左上角Y坐标
+     *     @type int|string $SizeX
+     *           水印宽度
+     *     @type int|string $SizeY
+     *           水印高度
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodWorkflow::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Anchor基准宽度
+     *
+     * Generated from protobuf field <code>int64 AnchorWidth = 1;</code>
+     * @return int|string
+     */
+    public function getAnchorWidth()
+    {
+        return $this->AnchorWidth;
+    }
+
+    /**
+     * Anchor基准宽度
+     *
+     * Generated from protobuf field <code>int64 AnchorWidth = 1;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setAnchorWidth($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->AnchorWidth = $var;
+
+        return $this;
+    }
+
+    /**
+     * Anchor基准宽度
+     *
+     * Generated from protobuf field <code>int64 AnchorHeight = 2;</code>
+     * @return int|string
+     */
+    public function getAnchorHeight()
+    {
+        return $this->AnchorHeight;
+    }
+
+    /**
+     * Anchor基准宽度
+     *
+     * Generated from protobuf field <code>int64 AnchorHeight = 2;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setAnchorHeight($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->AnchorHeight = $var;
+
+        return $this;
+    }
+
+    /**
+     * 水印左上角X坐标
+     *
+     * Generated from protobuf field <code>int64 PosX = 3;</code>
+     * @return int|string
+     */
+    public function getPosX()
+    {
+        return $this->PosX;
+    }
+
+    /**
+     * 水印左上角X坐标
+     *
+     * Generated from protobuf field <code>int64 PosX = 3;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setPosX($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->PosX = $var;
+
+        return $this;
+    }
+
+    /**
+     * 水印左上角Y坐标
+     *
+     * Generated from protobuf field <code>int64 PosY = 4;</code>
+     * @return int|string
+     */
+    public function getPosY()
+    {
+        return $this->PosY;
+    }
+
+    /**
+     * 水印左上角Y坐标
+     *
+     * Generated from protobuf field <code>int64 PosY = 4;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setPosY($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->PosY = $var;
+
+        return $this;
+    }
+
+    /**
+     * 水印宽度
+     *
+     * Generated from protobuf field <code>int64 SizeX = 5;</code>
+     * @return int|string
+     */
+    public function getSizeX()
+    {
+        return $this->SizeX;
+    }
+
+    /**
+     * 水印宽度
+     *
+     * Generated from protobuf field <code>int64 SizeX = 5;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setSizeX($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->SizeX = $var;
+
+        return $this;
+    }
+
+    /**
+     * 水印高度
+     *
+     * Generated from protobuf field <code>int64 SizeY = 6;</code>
+     * @return int|string
+     */
+    public function getSizeY()
+    {
+        return $this->SizeY;
+    }
+
+    /**
+     * 水印高度
+     *
+     * Generated from protobuf field <code>int64 SizeY = 6;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setSizeY($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->SizeY = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/ExecutionStage.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/ExecutionStage.php
new file mode 100755
index 0000000..76b4a99
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/ExecutionStage.php
@@ -0,0 +1,187 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_workflow.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.ExecutionStage</code>
+ */
+class ExecutionStage extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 阶段名称
+     *
+     * Generated from protobuf field <code>string DisplayName = 1;</code>
+     */
+    protected $DisplayName = '';
+    /**
+     * 阶段详情
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.StageDetail StageDetail = 2;</code>
+     */
+    private $StageDetail;
+    /**
+     * 阶段开始时间
+     *
+     * Generated from protobuf field <code>.google.protobuf.Timestamp StartTime = 3;</code>
+     */
+    protected $StartTime = null;
+    /**
+     * 阶段完成时间
+     *
+     * Generated from protobuf field <code>.google.protobuf.Timestamp EndTime = 4;</code>
+     */
+    protected $EndTime = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $DisplayName
+     *           阶段名称
+     *     @type \Volc\Service\Vod\Models\Business\StageDetail[]|\Google\Protobuf\Internal\RepeatedField $StageDetail
+     *           阶段详情
+     *     @type \Google\Protobuf\Timestamp $StartTime
+     *           阶段开始时间
+     *     @type \Google\Protobuf\Timestamp $EndTime
+     *           阶段完成时间
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodWorkflow::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 阶段名称
+     *
+     * Generated from protobuf field <code>string DisplayName = 1;</code>
+     * @return string
+     */
+    public function getDisplayName()
+    {
+        return $this->DisplayName;
+    }
+
+    /**
+     * 阶段名称
+     *
+     * Generated from protobuf field <code>string DisplayName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDisplayName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->DisplayName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 阶段详情
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.StageDetail StageDetail = 2;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getStageDetail()
+    {
+        return $this->StageDetail;
+    }
+
+    /**
+     * 阶段详情
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.StageDetail StageDetail = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\StageDetail[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setStageDetail($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\StageDetail::class);
+        $this->StageDetail = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 阶段开始时间
+     *
+     * Generated from protobuf field <code>.google.protobuf.Timestamp StartTime = 3;</code>
+     * @return \Google\Protobuf\Timestamp|null
+     */
+    public function getStartTime()
+    {
+        return $this->StartTime;
+    }
+
+    public function hasStartTime()
+    {
+        return isset($this->StartTime);
+    }
+
+    public function clearStartTime()
+    {
+        unset($this->StartTime);
+    }
+
+    /**
+     * 阶段开始时间
+     *
+     * Generated from protobuf field <code>.google.protobuf.Timestamp StartTime = 3;</code>
+     * @param \Google\Protobuf\Timestamp $var
+     * @return $this
+     */
+    public function setStartTime($var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\Timestamp::class);
+        $this->StartTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * 阶段完成时间
+     *
+     * Generated from protobuf field <code>.google.protobuf.Timestamp EndTime = 4;</code>
+     * @return \Google\Protobuf\Timestamp|null
+     */
+    public function getEndTime()
+    {
+        return $this->EndTime;
+    }
+
+    public function hasEndTime()
+    {
+        return isset($this->EndTime);
+    }
+
+    public function clearEndTime()
+    {
+        unset($this->EndTime);
+    }
+
+    /**
+     * 阶段完成时间
+     *
+     * Generated from protobuf field <code>.google.protobuf.Timestamp EndTime = 4;</code>
+     * @param \Google\Protobuf\Timestamp $var
+     * @return $this
+     */
+    public function setEndTime($var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\Timestamp::class);
+        $this->EndTime = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/GetDirectEditProgress.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/GetDirectEditProgress.php
new file mode 100755
index 0000000..215577b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/GetDirectEditProgress.php
@@ -0,0 +1,58 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_edit.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.GetDirectEditProgress</code>
+ */
+class GetDirectEditProgress extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>int32 Result = 1;</code>
+     */
+    protected $Result = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int $Result
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodEdit::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 Result = 1;</code>
+     * @return int
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 Result = 1;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/GetDirectEditResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/GetDirectEditResult.php
new file mode 100755
index 0000000..d169ecc
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/GetDirectEditResult.php
@@ -0,0 +1,269 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_edit.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.GetDirectEditResult</code>
+ */
+class GetDirectEditResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 视频编辑执行Id
+     *
+     * Generated from protobuf field <code>string ReqId = 1;</code>
+     */
+    protected $ReqId = '';
+    /**
+     * 视频编辑参数
+     *
+     * Generated from protobuf field <code>string EditParam = 2;</code>
+     */
+    protected $EditParam = '';
+    /**
+     * 优先级
+     *
+     * Generated from protobuf field <code>int32 Priority = 3;</code>
+     */
+    protected $Priority = 0;
+    /**
+     * 回调地址
+     *
+     * Generated from protobuf field <code>string CallbackUri = 4;</code>
+     */
+    protected $CallbackUri = '';
+    /**
+     * 回调参数
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 5;</code>
+     */
+    protected $CallbackArgs = '';
+    /**
+     * 编辑任务状态
+     *
+     * Generated from protobuf field <code>string Status = 6;</code>
+     */
+    protected $Status = '';
+    /**
+     * 产物vid
+     *
+     * Generated from protobuf field <code>string OutputVid = 7;</code>
+     */
+    protected $OutputVid = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $ReqId
+     *           视频编辑执行Id
+     *     @type string $EditParam
+     *           视频编辑参数
+     *     @type int $Priority
+     *           优先级
+     *     @type string $CallbackUri
+     *           回调地址
+     *     @type string $CallbackArgs
+     *           回调参数
+     *     @type string $Status
+     *           编辑任务状态
+     *     @type string $OutputVid
+     *           产物vid
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodEdit::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 视频编辑执行Id
+     *
+     * Generated from protobuf field <code>string ReqId = 1;</code>
+     * @return string
+     */
+    public function getReqId()
+    {
+        return $this->ReqId;
+    }
+
+    /**
+     * 视频编辑执行Id
+     *
+     * Generated from protobuf field <code>string ReqId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setReqId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->ReqId = $var;
+
+        return $this;
+    }
+
+    /**
+     * 视频编辑参数
+     *
+     * Generated from protobuf field <code>string EditParam = 2;</code>
+     * @return string
+     */
+    public function getEditParam()
+    {
+        return $this->EditParam;
+    }
+
+    /**
+     * 视频编辑参数
+     *
+     * Generated from protobuf field <code>string EditParam = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setEditParam($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->EditParam = $var;
+
+        return $this;
+    }
+
+    /**
+     * 优先级
+     *
+     * Generated from protobuf field <code>int32 Priority = 3;</code>
+     * @return int
+     */
+    public function getPriority()
+    {
+        return $this->Priority;
+    }
+
+    /**
+     * 优先级
+     *
+     * Generated from protobuf field <code>int32 Priority = 3;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setPriority($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Priority = $var;
+
+        return $this;
+    }
+
+    /**
+     * 回调地址
+     *
+     * Generated from protobuf field <code>string CallbackUri = 4;</code>
+     * @return string
+     */
+    public function getCallbackUri()
+    {
+        return $this->CallbackUri;
+    }
+
+    /**
+     * 回调地址
+     *
+     * Generated from protobuf field <code>string CallbackUri = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCallbackUri($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CallbackUri = $var;
+
+        return $this;
+    }
+
+    /**
+     * 回调参数
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 5;</code>
+     * @return string
+     */
+    public function getCallbackArgs()
+    {
+        return $this->CallbackArgs;
+    }
+
+    /**
+     * 回调参数
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCallbackArgs($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CallbackArgs = $var;
+
+        return $this;
+    }
+
+    /**
+     * 编辑任务状态
+     *
+     * Generated from protobuf field <code>string Status = 6;</code>
+     * @return string
+     */
+    public function getStatus()
+    {
+        return $this->Status;
+    }
+
+    /**
+     * 编辑任务状态
+     *
+     * Generated from protobuf field <code>string Status = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStatus($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Status = $var;
+
+        return $this;
+    }
+
+    /**
+     * 产物vid
+     *
+     * Generated from protobuf field <code>string OutputVid = 7;</code>
+     * @return string
+     */
+    public function getOutputVid()
+    {
+        return $this->OutputVid;
+    }
+
+    /**
+     * 产物vid
+     *
+     * Generated from protobuf field <code>string OutputVid = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setOutputVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->OutputVid = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/Inspection.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/Inspection.php
new file mode 100755
index 0000000..91d6ab5
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/Inspection.php
@@ -0,0 +1,109 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_workflow.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.Inspection</code>
+ */
+class Inspection extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 视频检测质量
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.Quality Quality = 1;</code>
+     */
+    protected $Quality = null;
+    /**
+     * 水印检测结果
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.DeLogoInfo DeLogo = 2;</code>
+     */
+    private $DeLogo;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\Quality $Quality
+     *           视频检测质量
+     *     @type \Volc\Service\Vod\Models\Business\DeLogoInfo[]|\Google\Protobuf\Internal\RepeatedField $DeLogo
+     *           水印检测结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodWorkflow::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 视频检测质量
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.Quality Quality = 1;</code>
+     * @return \Volc\Service\Vod\Models\Business\Quality|null
+     */
+    public function getQuality()
+    {
+        return $this->Quality;
+    }
+
+    public function hasQuality()
+    {
+        return isset($this->Quality);
+    }
+
+    public function clearQuality()
+    {
+        unset($this->Quality);
+    }
+
+    /**
+     * 视频检测质量
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.Quality Quality = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\Quality $var
+     * @return $this
+     */
+    public function setQuality($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\Quality::class);
+        $this->Quality = $var;
+
+        return $this;
+    }
+
+    /**
+     * 水印检测结果
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.DeLogoInfo DeLogo = 2;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getDeLogo()
+    {
+        return $this->DeLogo;
+    }
+
+    /**
+     * 水印检测结果
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.DeLogoInfo DeLogo = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\DeLogoInfo[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setDeLogo($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\DeLogoInfo::class);
+        $this->DeLogo = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/LogoOverride.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/LogoOverride.php
new file mode 100755
index 0000000..5a73c0c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/LogoOverride.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_workflow.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.LogoOverride</code>
+ */
+class LogoOverride extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 被覆盖的水印模板Id, 支持ALL
+     *
+     * Generated from protobuf field <code>string TemplateId = 1;</code>
+     */
+    protected $TemplateId = '';
+    /**
+     * 自定义水印变量
+     *
+     * Generated from protobuf field <code>map<string, string> Vars = 2;</code>
+     */
+    private $Vars;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $TemplateId
+     *           被覆盖的水印模板Id, 支持ALL
+     *     @type array|\Google\Protobuf\Internal\MapField $Vars
+     *           自定义水印变量
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodWorkflow::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 被覆盖的水印模板Id, 支持ALL
+     *
+     * Generated from protobuf field <code>string TemplateId = 1;</code>
+     * @return string
+     */
+    public function getTemplateId()
+    {
+        return $this->TemplateId;
+    }
+
+    /**
+     * 被覆盖的水印模板Id, 支持ALL
+     *
+     * Generated from protobuf field <code>string TemplateId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTemplateId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TemplateId = $var;
+
+        return $this;
+    }
+
+    /**
+     * 自定义水印变量
+     *
+     * Generated from protobuf field <code>map<string, string> Vars = 2;</code>
+     * @return \Google\Protobuf\Internal\MapField
+     */
+    public function getVars()
+    {
+        return $this->Vars;
+    }
+
+    /**
+     * 自定义水印变量
+     *
+     * Generated from protobuf field <code>map<string, string> Vars = 2;</code>
+     * @param array|\Google\Protobuf\Internal\MapField $var
+     * @return $this
+     */
+    public function setVars($var)
+    {
+        $arr = GPBUtil::checkMapField($var, \Google\Protobuf\Internal\GPBType::STRING, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->Vars = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/OverrideParams.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/OverrideParams.php
new file mode 100755
index 0000000..e33c27c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/OverrideParams.php
@@ -0,0 +1,167 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_workflow.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.OverrideParams</code>
+ */
+class OverrideParams extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 水印覆盖参数
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.LogoOverride Logo = 1;</code>
+     */
+    private $Logo;
+    /**
+     * 视频转码覆盖参数
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.TranscodeVideoOverride TranscodeVideo = 2;</code>
+     */
+    private $TranscodeVideo;
+    /**
+     * 音频转码覆盖参数
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.TranscodeAudioOverride TranscodeAudio = 3;</code>
+     */
+    private $TranscodeAudio;
+    /**
+     * 截图覆盖参数
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.SnapshotOverride Snapshot = 4;</code>
+     */
+    private $Snapshot;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\LogoOverride[]|\Google\Protobuf\Internal\RepeatedField $Logo
+     *           水印覆盖参数
+     *     @type \Volc\Service\Vod\Models\Business\TranscodeVideoOverride[]|\Google\Protobuf\Internal\RepeatedField $TranscodeVideo
+     *           视频转码覆盖参数
+     *     @type \Volc\Service\Vod\Models\Business\TranscodeAudioOverride[]|\Google\Protobuf\Internal\RepeatedField $TranscodeAudio
+     *           音频转码覆盖参数
+     *     @type \Volc\Service\Vod\Models\Business\SnapshotOverride[]|\Google\Protobuf\Internal\RepeatedField $Snapshot
+     *           截图覆盖参数
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodWorkflow::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 水印覆盖参数
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.LogoOverride Logo = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getLogo()
+    {
+        return $this->Logo;
+    }
+
+    /**
+     * 水印覆盖参数
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.LogoOverride Logo = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\LogoOverride[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setLogo($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\LogoOverride::class);
+        $this->Logo = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 视频转码覆盖参数
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.TranscodeVideoOverride TranscodeVideo = 2;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getTranscodeVideo()
+    {
+        return $this->TranscodeVideo;
+    }
+
+    /**
+     * 视频转码覆盖参数
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.TranscodeVideoOverride TranscodeVideo = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\TranscodeVideoOverride[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setTranscodeVideo($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\TranscodeVideoOverride::class);
+        $this->TranscodeVideo = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 音频转码覆盖参数
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.TranscodeAudioOverride TranscodeAudio = 3;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getTranscodeAudio()
+    {
+        return $this->TranscodeAudio;
+    }
+
+    /**
+     * 音频转码覆盖参数
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.TranscodeAudioOverride TranscodeAudio = 3;</code>
+     * @param \Volc\Service\Vod\Models\Business\TranscodeAudioOverride[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setTranscodeAudio($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\TranscodeAudioOverride::class);
+        $this->TranscodeAudio = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 截图覆盖参数
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.SnapshotOverride Snapshot = 4;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getSnapshot()
+    {
+        return $this->Snapshot;
+    }
+
+    /**
+     * 截图覆盖参数
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.SnapshotOverride Snapshot = 4;</code>
+     * @param \Volc\Service\Vod\Models\Business\SnapshotOverride[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setSnapshot($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\SnapshotOverride::class);
+        $this->Snapshot = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/Quality.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/Quality.php
new file mode 100755
index 0000000..26a8311
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/Quality.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_workflow.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.Quality</code>
+ */
+class Quality extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 片源检测参数
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VisualQuality Visual = 1;</code>
+     */
+    protected $Visual = null;
+    /**
+     * 音量
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VolumeInfo VolumeInfo = 2;</code>
+     */
+    protected $VolumeInfo = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\VisualQuality $Visual
+     *           片源检测参数
+     *     @type \Volc\Service\Vod\Models\Business\VolumeInfo $VolumeInfo
+     *           音量
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodWorkflow::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 片源检测参数
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VisualQuality Visual = 1;</code>
+     * @return \Volc\Service\Vod\Models\Business\VisualQuality|null
+     */
+    public function getVisual()
+    {
+        return $this->Visual;
+    }
+
+    public function hasVisual()
+    {
+        return isset($this->Visual);
+    }
+
+    public function clearVisual()
+    {
+        unset($this->Visual);
+    }
+
+    /**
+     * 片源检测参数
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VisualQuality Visual = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\VisualQuality $var
+     * @return $this
+     */
+    public function setVisual($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VisualQuality::class);
+        $this->Visual = $var;
+
+        return $this;
+    }
+
+    /**
+     * 音量
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VolumeInfo VolumeInfo = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VolumeInfo|null
+     */
+    public function getVolumeInfo()
+    {
+        return $this->VolumeInfo;
+    }
+
+    public function hasVolumeInfo()
+    {
+        return isset($this->VolumeInfo);
+    }
+
+    public function clearVolumeInfo()
+    {
+        unset($this->VolumeInfo);
+    }
+
+    /**
+     * 音量
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VolumeInfo VolumeInfo = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VolumeInfo $var
+     * @return $this
+     */
+    public function setVolumeInfo($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VolumeInfo::class);
+        $this->VolumeInfo = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/SnapshotOverride.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/SnapshotOverride.php
new file mode 100755
index 0000000..6bfc050
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/SnapshotOverride.php
@@ -0,0 +1,133 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_workflow.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.SnapshotOverride</code>
+ */
+class SnapshotOverride extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 被覆盖的截图模板Id, 支持ALL
+     *
+     * Generated from protobuf field <code>repeated string TemplateId = 1;</code>
+     */
+    private $TemplateId;
+    /**
+     * 截图时间, 单位ms, AIDynpost和Sprite类型不支持
+     *
+     * Generated from protobuf field <code>int32 OffsetTime = 2;</code>
+     */
+    protected $OffsetTime = 0;
+    /**
+     * 多Dynpost类型截取时间,单位ms
+     *
+     * Generated from protobuf field <code>repeated int32 OffsetTimeList = 3;</code>
+     */
+    private $OffsetTimeList;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $TemplateId
+     *           被覆盖的截图模板Id, 支持ALL
+     *     @type int $OffsetTime
+     *           截图时间, 单位ms, AIDynpost和Sprite类型不支持
+     *     @type int[]|\Google\Protobuf\Internal\RepeatedField $OffsetTimeList
+     *           多Dynpost类型截取时间,单位ms
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodWorkflow::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 被覆盖的截图模板Id, 支持ALL
+     *
+     * Generated from protobuf field <code>repeated string TemplateId = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getTemplateId()
+    {
+        return $this->TemplateId;
+    }
+
+    /**
+     * 被覆盖的截图模板Id, 支持ALL
+     *
+     * Generated from protobuf field <code>repeated string TemplateId = 1;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setTemplateId($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->TemplateId = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 截图时间, 单位ms, AIDynpost和Sprite类型不支持
+     *
+     * Generated from protobuf field <code>int32 OffsetTime = 2;</code>
+     * @return int
+     */
+    public function getOffsetTime()
+    {
+        return $this->OffsetTime;
+    }
+
+    /**
+     * 截图时间, 单位ms, AIDynpost和Sprite类型不支持
+     *
+     * Generated from protobuf field <code>int32 OffsetTime = 2;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setOffsetTime($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->OffsetTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * 多Dynpost类型截取时间,单位ms
+     *
+     * Generated from protobuf field <code>repeated int32 OffsetTimeList = 3;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getOffsetTimeList()
+    {
+        return $this->OffsetTimeList;
+    }
+
+    /**
+     * 多Dynpost类型截取时间,单位ms
+     *
+     * Generated from protobuf field <code>repeated int32 OffsetTimeList = 3;</code>
+     * @param int[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setOffsetTimeList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::INT32);
+        $this->OffsetTimeList = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/StageDetail.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/StageDetail.php
new file mode 100755
index 0000000..b720b35
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/StageDetail.php
@@ -0,0 +1,315 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_workflow.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.StageDetail</code>
+ */
+class StageDetail extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>string Id = 1;</code>
+     */
+    protected $Id = '';
+    /**
+     * Generated from protobuf field <code>string DisplayName = 2;</code>
+     */
+    protected $DisplayName = '';
+    /**
+     * Generated from protobuf field <code>string Type = 3;</code>
+     */
+    protected $Type = '';
+    /**
+     * Generated from protobuf field <code>string TemplateId = 4;</code>
+     */
+    protected $TemplateId = '';
+    /**
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.StageStatus Status = 5;</code>
+     */
+    protected $Status = 0;
+    /**
+     * 错误码
+     *
+     * Generated from protobuf field <code>int64 ErrorCode = 6;</code>
+     */
+    protected $ErrorCode = 0;
+    /**
+     * Generated from protobuf field <code>string Message = 7;</code>
+     */
+    protected $Message = '';
+    /**
+     * 阶段开始时间
+     *
+     * Generated from protobuf field <code>.google.protobuf.Timestamp StartTime = 8;</code>
+     */
+    protected $StartTime = null;
+    /**
+     * 阶段结束时间
+     *
+     * Generated from protobuf field <code>.google.protobuf.Timestamp EndTime = 9;</code>
+     */
+    protected $EndTime = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Id
+     *     @type string $DisplayName
+     *     @type string $Type
+     *     @type string $TemplateId
+     *     @type int $Status
+     *     @type int|string $ErrorCode
+     *           错误码
+     *     @type string $Message
+     *     @type \Google\Protobuf\Timestamp $StartTime
+     *           阶段开始时间
+     *     @type \Google\Protobuf\Timestamp $EndTime
+     *           阶段结束时间
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodWorkflow::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>string Id = 1;</code>
+     * @return string
+     */
+    public function getId()
+    {
+        return $this->Id;
+    }
+
+    /**
+     * Generated from protobuf field <code>string Id = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Id = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string DisplayName = 2;</code>
+     * @return string
+     */
+    public function getDisplayName()
+    {
+        return $this->DisplayName;
+    }
+
+    /**
+     * Generated from protobuf field <code>string DisplayName = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDisplayName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->DisplayName = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string Type = 3;</code>
+     * @return string
+     */
+    public function getType()
+    {
+        return $this->Type;
+    }
+
+    /**
+     * Generated from protobuf field <code>string Type = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Type = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string TemplateId = 4;</code>
+     * @return string
+     */
+    public function getTemplateId()
+    {
+        return $this->TemplateId;
+    }
+
+    /**
+     * Generated from protobuf field <code>string TemplateId = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTemplateId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TemplateId = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.StageStatus Status = 5;</code>
+     * @return int
+     */
+    public function getStatus()
+    {
+        return $this->Status;
+    }
+
+    /**
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.StageStatus Status = 5;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setStatus($var)
+    {
+        GPBUtil::checkEnum($var, \Volc\Service\Vod\Models\Business\StageStatus::class);
+        $this->Status = $var;
+
+        return $this;
+    }
+
+    /**
+     * 错误码
+     *
+     * Generated from protobuf field <code>int64 ErrorCode = 6;</code>
+     * @return int|string
+     */
+    public function getErrorCode()
+    {
+        return $this->ErrorCode;
+    }
+
+    /**
+     * 错误码
+     *
+     * Generated from protobuf field <code>int64 ErrorCode = 6;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setErrorCode($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->ErrorCode = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string Message = 7;</code>
+     * @return string
+     */
+    public function getMessage()
+    {
+        return $this->Message;
+    }
+
+    /**
+     * Generated from protobuf field <code>string Message = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setMessage($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Message = $var;
+
+        return $this;
+    }
+
+    /**
+     * 阶段开始时间
+     *
+     * Generated from protobuf field <code>.google.protobuf.Timestamp StartTime = 8;</code>
+     * @return \Google\Protobuf\Timestamp|null
+     */
+    public function getStartTime()
+    {
+        return $this->StartTime;
+    }
+
+    public function hasStartTime()
+    {
+        return isset($this->StartTime);
+    }
+
+    public function clearStartTime()
+    {
+        unset($this->StartTime);
+    }
+
+    /**
+     * 阶段开始时间
+     *
+     * Generated from protobuf field <code>.google.protobuf.Timestamp StartTime = 8;</code>
+     * @param \Google\Protobuf\Timestamp $var
+     * @return $this
+     */
+    public function setStartTime($var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\Timestamp::class);
+        $this->StartTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * 阶段结束时间
+     *
+     * Generated from protobuf field <code>.google.protobuf.Timestamp EndTime = 9;</code>
+     * @return \Google\Protobuf\Timestamp|null
+     */
+    public function getEndTime()
+    {
+        return $this->EndTime;
+    }
+
+    public function hasEndTime()
+    {
+        return isset($this->EndTime);
+    }
+
+    public function clearEndTime()
+    {
+        unset($this->EndTime);
+    }
+
+    /**
+     * 阶段结束时间
+     *
+     * Generated from protobuf field <code>.google.protobuf.Timestamp EndTime = 9;</code>
+     * @param \Google\Protobuf\Timestamp $var
+     * @return $this
+     */
+    public function setEndTime($var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\Timestamp::class);
+        $this->EndTime = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/StageStatus.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/StageStatus.php
new file mode 100755
index 0000000..97f729b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/StageStatus.php
@@ -0,0 +1,94 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_workflow.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use UnexpectedValueException;
+
+/**
+ * Protobuf type <code>Volcengine.Vod.Models.Business.StageStatus</code>
+ */
+class StageStatus
+{
+    /**
+     * 未知
+     *
+     * Generated from protobuf enum <code>Unknown = 0;</code>
+     */
+    const Unknown = 0;
+    /**
+     * 等待调度
+     *
+     * Generated from protobuf enum <code>Scheduled = 1;</code>
+     */
+    const Scheduled = 1;
+    /**
+     * 执行中
+     *
+     * Generated from protobuf enum <code>Running = 2;</code>
+     */
+    const Running = 2;
+    /**
+     * 取消
+     *
+     * Generated from protobuf enum <code>Canceled = 3;</code>
+     */
+    const Canceled = 3;
+    /**
+     * 超时
+     *
+     * Generated from protobuf enum <code>TimedOut = 4;</code>
+     */
+    const TimedOut = 4;
+    /**
+     * 跳过
+     *
+     * Generated from protobuf enum <code>Skipped = 5;</code>
+     */
+    const Skipped = 5;
+    /**
+     * 完成
+     *
+     * Generated from protobuf enum <code>Completed = 6;</code>
+     */
+    const Completed = 6;
+    /**
+     * 失败
+     *
+     * Generated from protobuf enum <code>Failed = 7;</code>
+     */
+    const Failed = 7;
+
+    private static $valueToName = [
+        self::Unknown => 'Unknown',
+        self::Scheduled => 'Scheduled',
+        self::Running => 'Running',
+        self::Canceled => 'Canceled',
+        self::TimedOut => 'TimedOut',
+        self::Skipped => 'Skipped',
+        self::Completed => 'Completed',
+        self::Failed => 'Failed',
+    ];
+
+    public static function name($value)
+    {
+        if (!isset(self::$valueToName[$value])) {
+            throw new UnexpectedValueException(sprintf(
+                    'Enum %s has no name defined for value %s', __CLASS__, $value));
+        }
+        return self::$valueToName[$value];
+    }
+
+
+    public static function value($name)
+    {
+        $const = __CLASS__ . '::' . strtoupper($name);
+        if (!defined($const)) {
+            throw new UnexpectedValueException(sprintf(
+                    'Enum %s has no value defined for name %s', __CLASS__, $name));
+        }
+        return constant($const);
+    }
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/SubmitDirectEditTaskAsyncResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/SubmitDirectEditTaskAsyncResult.php
new file mode 100755
index 0000000..c52bbfe
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/SubmitDirectEditTaskAsyncResult.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_edit.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.SubmitDirectEditTaskAsyncResult</code>
+ */
+class SubmitDirectEditTaskAsyncResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 视频编辑执行Id
+     *
+     * Generated from protobuf field <code>string ReqId = 1;</code>
+     */
+    protected $ReqId = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $ReqId
+     *           视频编辑执行Id
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodEdit::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 视频编辑执行Id
+     *
+     * Generated from protobuf field <code>string ReqId = 1;</code>
+     * @return string
+     */
+    public function getReqId()
+    {
+        return $this->ReqId;
+    }
+
+    /**
+     * 视频编辑执行Id
+     *
+     * Generated from protobuf field <code>string ReqId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setReqId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->ReqId = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/TranscodeAudioOverride.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/TranscodeAudioOverride.php
new file mode 100755
index 0000000..fd5e234
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/TranscodeAudioOverride.php
@@ -0,0 +1,136 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_workflow.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.TranscodeAudioOverride</code>
+ */
+class TranscodeAudioOverride extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 被覆盖的音频模板Id, 支持ALL
+     *
+     * Generated from protobuf field <code>repeated string TemplateId = 1;</code>
+     */
+    private $TemplateId;
+    /**
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.Clip Clip = 2;</code>
+     */
+    protected $Clip = null;
+    /**
+     * FileName
+     *
+     * Generated from protobuf field <code>string FileName = 3;</code>
+     */
+    protected $FileName = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $TemplateId
+     *           被覆盖的音频模板Id, 支持ALL
+     *     @type \Volc\Service\Vod\Models\Business\Clip $Clip
+     *     @type string $FileName
+     *           FileName
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodWorkflow::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 被覆盖的音频模板Id, 支持ALL
+     *
+     * Generated from protobuf field <code>repeated string TemplateId = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getTemplateId()
+    {
+        return $this->TemplateId;
+    }
+
+    /**
+     * 被覆盖的音频模板Id, 支持ALL
+     *
+     * Generated from protobuf field <code>repeated string TemplateId = 1;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setTemplateId($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->TemplateId = $arr;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.Clip Clip = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\Clip|null
+     */
+    public function getClip()
+    {
+        return $this->Clip;
+    }
+
+    public function hasClip()
+    {
+        return isset($this->Clip);
+    }
+
+    public function clearClip()
+    {
+        unset($this->Clip);
+    }
+
+    /**
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.Clip Clip = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\Clip $var
+     * @return $this
+     */
+    public function setClip($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\Clip::class);
+        $this->Clip = $var;
+
+        return $this;
+    }
+
+    /**
+     * FileName
+     *
+     * Generated from protobuf field <code>string FileName = 3;</code>
+     * @return string
+     */
+    public function getFileName()
+    {
+        return $this->FileName;
+    }
+
+    /**
+     * FileName
+     *
+     * Generated from protobuf field <code>string FileName = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileName = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/TranscodeResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/TranscodeResult.php
new file mode 100755
index 0000000..8bfaade
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/TranscodeResult.php
@@ -0,0 +1,143 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_workflow.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.TranscodeResult</code>
+ */
+class TranscodeResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *视频id,为请求值
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     */
+    protected $Vid = '';
+    /**
+     * 片源检测结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.Inspection Inspection = 2;</code>
+     */
+    protected $Inspection = null;
+    /**
+     * 视频分类结果
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.CategoryTagInfo CategoryTags = 3;</code>
+     */
+    private $CategoryTags;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vid
+     *          视频id,为请求值
+     *     @type \Volc\Service\Vod\Models\Business\Inspection $Inspection
+     *           片源检测结果
+     *     @type \Volc\Service\Vod\Models\Business\CategoryTagInfo[]|\Google\Protobuf\Internal\RepeatedField $CategoryTags
+     *           视频分类结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodWorkflow::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *视频id,为请求值
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     *视频id,为请求值
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     * 片源检测结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.Inspection Inspection = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\Inspection|null
+     */
+    public function getInspection()
+    {
+        return $this->Inspection;
+    }
+
+    public function hasInspection()
+    {
+        return isset($this->Inspection);
+    }
+
+    public function clearInspection()
+    {
+        unset($this->Inspection);
+    }
+
+    /**
+     * 片源检测结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.Inspection Inspection = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\Inspection $var
+     * @return $this
+     */
+    public function setInspection($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\Inspection::class);
+        $this->Inspection = $var;
+
+        return $this;
+    }
+
+    /**
+     * 视频分类结果
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.CategoryTagInfo CategoryTags = 3;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getCategoryTags()
+    {
+        return $this->CategoryTags;
+    }
+
+    /**
+     * 视频分类结果
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.CategoryTagInfo CategoryTags = 3;</code>
+     * @param \Volc\Service\Vod\Models\Business\CategoryTagInfo[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setCategoryTags($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\CategoryTagInfo::class);
+        $this->CategoryTags = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/TranscodeVideoOverride.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/TranscodeVideoOverride.php
new file mode 100755
index 0000000..bcd2b6a
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/TranscodeVideoOverride.php
@@ -0,0 +1,177 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_workflow.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.TranscodeVideoOverride</code>
+ */
+class TranscodeVideoOverride extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 被覆盖的视频模板Id, 支持ALL
+     *
+     * Generated from protobuf field <code>repeated string TemplateId = 1;</code>
+     */
+    private $TemplateId;
+    /**
+     * 裁剪参数
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.Clip Clip = 2;</code>
+     */
+    protected $Clip = null;
+    /**
+     * 模板组下标,仅对模板组生效
+     *
+     * Generated from protobuf field <code>repeated int32 OutputIndex = 3;</code>
+     */
+    private $OutputIndex;
+    /**
+     * FileName
+     *
+     * Generated from protobuf field <code>string FileName = 4;</code>
+     */
+    protected $FileName = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $TemplateId
+     *           被覆盖的视频模板Id, 支持ALL
+     *     @type \Volc\Service\Vod\Models\Business\Clip $Clip
+     *           裁剪参数
+     *     @type int[]|\Google\Protobuf\Internal\RepeatedField $OutputIndex
+     *           模板组下标,仅对模板组生效
+     *     @type string $FileName
+     *           FileName
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodWorkflow::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 被覆盖的视频模板Id, 支持ALL
+     *
+     * Generated from protobuf field <code>repeated string TemplateId = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getTemplateId()
+    {
+        return $this->TemplateId;
+    }
+
+    /**
+     * 被覆盖的视频模板Id, 支持ALL
+     *
+     * Generated from protobuf field <code>repeated string TemplateId = 1;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setTemplateId($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->TemplateId = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 裁剪参数
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.Clip Clip = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\Clip|null
+     */
+    public function getClip()
+    {
+        return $this->Clip;
+    }
+
+    public function hasClip()
+    {
+        return isset($this->Clip);
+    }
+
+    public function clearClip()
+    {
+        unset($this->Clip);
+    }
+
+    /**
+     * 裁剪参数
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.Clip Clip = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\Clip $var
+     * @return $this
+     */
+    public function setClip($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\Clip::class);
+        $this->Clip = $var;
+
+        return $this;
+    }
+
+    /**
+     * 模板组下标,仅对模板组生效
+     *
+     * Generated from protobuf field <code>repeated int32 OutputIndex = 3;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getOutputIndex()
+    {
+        return $this->OutputIndex;
+    }
+
+    /**
+     * 模板组下标,仅对模板组生效
+     *
+     * Generated from protobuf field <code>repeated int32 OutputIndex = 3;</code>
+     * @param int[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setOutputIndex($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::INT32);
+        $this->OutputIndex = $arr;
+
+        return $this;
+    }
+
+    /**
+     * FileName
+     *
+     * Generated from protobuf field <code>string FileName = 4;</code>
+     * @return string
+     */
+    public function getFileName()
+    {
+        return $this->FileName;
+    }
+
+    /**
+     * FileName
+     *
+     * Generated from protobuf field <code>string FileName = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileName = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/ValuePair.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/ValuePair.php
new file mode 100755
index 0000000..9b001d1
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/ValuePair.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_upload.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.ValuePair</code>
+ */
+class ValuePair extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Url对应的JobId 
+     *
+     * Generated from protobuf field <code>string JobId = 1;</code>
+     */
+    protected $JobId = '';
+    /**
+     * 用户上传的Url 
+     *
+     * Generated from protobuf field <code>string SourceUrl = 2;</code>
+     */
+    protected $SourceUrl = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $JobId
+     *           Url对应的JobId 
+     *     @type string $SourceUrl
+     *           用户上传的Url 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodUpload::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Url对应的JobId 
+     *
+     * Generated from protobuf field <code>string JobId = 1;</code>
+     * @return string
+     */
+    public function getJobId()
+    {
+        return $this->JobId;
+    }
+
+    /**
+     * Url对应的JobId 
+     *
+     * Generated from protobuf field <code>string JobId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setJobId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->JobId = $var;
+
+        return $this;
+    }
+
+    /**
+     * 用户上传的Url 
+     *
+     * Generated from protobuf field <code>string SourceUrl = 2;</code>
+     * @return string
+     */
+    public function getSourceUrl()
+    {
+        return $this->SourceUrl;
+    }
+
+    /**
+     * 用户上传的Url 
+     *
+     * Generated from protobuf field <code>string SourceUrl = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSourceUrl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SourceUrl = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VisualQuality.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VisualQuality.php
new file mode 100755
index 0000000..8dd78cf
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VisualQuality.php
@@ -0,0 +1,235 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_workflow.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VisualQuality</code>
+ */
+class VisualQuality extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 视频质量评分
+     *
+     * Generated from protobuf field <code>double VQScore = 1;</code>
+     */
+    protected $VQScore = 0.0;
+    /**
+     * 对比度
+     *
+     * Generated from protobuf field <code>double Contrast = 2;</code>
+     */
+    protected $Contrast = 0.0;
+    /**
+     * 色彩
+     *
+     * Generated from protobuf field <code>double Colorfulness = 3;</code>
+     */
+    protected $Colorfulness = 0.0;
+    /**
+     * 亮度
+     *
+     * Generated from protobuf field <code>double Brightness = 4;</code>
+     */
+    protected $Brightness = 0.0;
+    /**
+     * 纹理
+     *
+     * Generated from protobuf field <code>double Texture = 5;</code>
+     */
+    protected $Texture = 0.0;
+    /**
+     * 噪声
+     *
+     * Generated from protobuf field <code>double Noise = 6;</code>
+     */
+    protected $Noise = 0.0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type float $VQScore
+     *           视频质量评分
+     *     @type float $Contrast
+     *           对比度
+     *     @type float $Colorfulness
+     *           色彩
+     *     @type float $Brightness
+     *           亮度
+     *     @type float $Texture
+     *           纹理
+     *     @type float $Noise
+     *           噪声
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodWorkflow::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 视频质量评分
+     *
+     * Generated from protobuf field <code>double VQScore = 1;</code>
+     * @return float
+     */
+    public function getVQScore()
+    {
+        return $this->VQScore;
+    }
+
+    /**
+     * 视频质量评分
+     *
+     * Generated from protobuf field <code>double VQScore = 1;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setVQScore($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->VQScore = $var;
+
+        return $this;
+    }
+
+    /**
+     * 对比度
+     *
+     * Generated from protobuf field <code>double Contrast = 2;</code>
+     * @return float
+     */
+    public function getContrast()
+    {
+        return $this->Contrast;
+    }
+
+    /**
+     * 对比度
+     *
+     * Generated from protobuf field <code>double Contrast = 2;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setContrast($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->Contrast = $var;
+
+        return $this;
+    }
+
+    /**
+     * 色彩
+     *
+     * Generated from protobuf field <code>double Colorfulness = 3;</code>
+     * @return float
+     */
+    public function getColorfulness()
+    {
+        return $this->Colorfulness;
+    }
+
+    /**
+     * 色彩
+     *
+     * Generated from protobuf field <code>double Colorfulness = 3;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setColorfulness($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->Colorfulness = $var;
+
+        return $this;
+    }
+
+    /**
+     * 亮度
+     *
+     * Generated from protobuf field <code>double Brightness = 4;</code>
+     * @return float
+     */
+    public function getBrightness()
+    {
+        return $this->Brightness;
+    }
+
+    /**
+     * 亮度
+     *
+     * Generated from protobuf field <code>double Brightness = 4;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setBrightness($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->Brightness = $var;
+
+        return $this;
+    }
+
+    /**
+     * 纹理
+     *
+     * Generated from protobuf field <code>double Texture = 5;</code>
+     * @return float
+     */
+    public function getTexture()
+    {
+        return $this->Texture;
+    }
+
+    /**
+     * 纹理
+     *
+     * Generated from protobuf field <code>double Texture = 5;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setTexture($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->Texture = $var;
+
+        return $this;
+    }
+
+    /**
+     * 噪声
+     *
+     * Generated from protobuf field <code>double Noise = 6;</code>
+     * @return float
+     */
+    public function getNoise()
+    {
+        return $this->Noise;
+    }
+
+    /**
+     * 噪声
+     *
+     * Generated from protobuf field <code>double Noise = 6;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setNoise($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->Noise = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAEDEventItemForAudit.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAEDEventItemForAudit.php
new file mode 100755
index 0000000..350a03a
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAEDEventItemForAudit.php
@@ -0,0 +1,133 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodAEDEventItemForAudit</code>
+ */
+class VodAEDEventItemForAudit extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *事件名,仅当识别成功时填写
+     *
+     * Generated from protobuf field <code>string Event = 1;</code>
+     */
+    protected $Event = '';
+    /**
+     *整条音频对应的事件概率,仅当识别成功时填写
+     *
+     * Generated from protobuf field <code>double UttProb = 2;</code>
+     */
+    protected $UttProb = 0.0;
+    /**
+     *事件识别区间,仅当识别成功时填写
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodAEDTimeRangeForAudit TimeRanges = 3;</code>
+     */
+    private $TimeRanges;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Event
+     *          事件名,仅当识别成功时填写
+     *     @type float $UttProb
+     *          整条音频对应的事件概率,仅当识别成功时填写
+     *     @type \Volc\Service\Vod\Models\Business\VodAEDTimeRangeForAudit[]|\Google\Protobuf\Internal\RepeatedField $TimeRanges
+     *          事件识别区间,仅当识别成功时填写
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *事件名,仅当识别成功时填写
+     *
+     * Generated from protobuf field <code>string Event = 1;</code>
+     * @return string
+     */
+    public function getEvent()
+    {
+        return $this->Event;
+    }
+
+    /**
+     *事件名,仅当识别成功时填写
+     *
+     * Generated from protobuf field <code>string Event = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setEvent($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Event = $var;
+
+        return $this;
+    }
+
+    /**
+     *整条音频对应的事件概率,仅当识别成功时填写
+     *
+     * Generated from protobuf field <code>double UttProb = 2;</code>
+     * @return float
+     */
+    public function getUttProb()
+    {
+        return $this->UttProb;
+    }
+
+    /**
+     *整条音频对应的事件概率,仅当识别成功时填写
+     *
+     * Generated from protobuf field <code>double UttProb = 2;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setUttProb($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->UttProb = $var;
+
+        return $this;
+    }
+
+    /**
+     *事件识别区间,仅当识别成功时填写
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodAEDTimeRangeForAudit TimeRanges = 3;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getTimeRanges()
+    {
+        return $this->TimeRanges;
+    }
+
+    /**
+     *事件识别区间,仅当识别成功时填写
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodAEDTimeRangeForAudit TimeRanges = 3;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodAEDTimeRangeForAudit[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setTimeRanges($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodAEDTimeRangeForAudit::class);
+        $this->TimeRanges = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAEDInfoForAudit.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAEDInfoForAudit.php
new file mode 100755
index 0000000..0f36ba9
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAEDInfoForAudit.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodAEDInfoForAudit</code>
+ */
+class VodAEDInfoForAudit extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 识别出来的音频事件信息
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodAEDEventItemForAudit EventItems = 1;</code>
+     */
+    private $EventItems;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\VodAEDEventItemForAudit[]|\Google\Protobuf\Internal\RepeatedField $EventItems
+     *           识别出来的音频事件信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 识别出来的音频事件信息
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodAEDEventItemForAudit EventItems = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getEventItems()
+    {
+        return $this->EventItems;
+    }
+
+    /**
+     * 识别出来的音频事件信息
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodAEDEventItemForAudit EventItems = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodAEDEventItemForAudit[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setEventItems($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodAEDEventItemForAudit::class);
+        $this->EventItems = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAEDTimeRangeForAudit.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAEDTimeRangeForAudit.php
new file mode 100755
index 0000000..0a58be8
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAEDTimeRangeForAudit.php
@@ -0,0 +1,133 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodAEDTimeRangeForAudit</code>
+ */
+class VodAEDTimeRangeForAudit extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *事件识别区间的起点(ms)
+     *
+     * Generated from protobuf field <code>double StartTimeMill = 1;</code>
+     */
+    protected $StartTimeMill = 0.0;
+    /**
+     *事件识别区间的终点(ms)
+     *
+     * Generated from protobuf field <code>double EndTimeMill = 2;</code>
+     */
+    protected $EndTimeMill = 0.0;
+    /**
+     *事件区间对应的事件概率
+     *
+     * Generated from protobuf field <code>double Prob = 3;</code>
+     */
+    protected $Prob = 0.0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type float $StartTimeMill
+     *          事件识别区间的起点(ms)
+     *     @type float $EndTimeMill
+     *          事件识别区间的终点(ms)
+     *     @type float $Prob
+     *          事件区间对应的事件概率
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *事件识别区间的起点(ms)
+     *
+     * Generated from protobuf field <code>double StartTimeMill = 1;</code>
+     * @return float
+     */
+    public function getStartTimeMill()
+    {
+        return $this->StartTimeMill;
+    }
+
+    /**
+     *事件识别区间的起点(ms)
+     *
+     * Generated from protobuf field <code>double StartTimeMill = 1;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setStartTimeMill($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->StartTimeMill = $var;
+
+        return $this;
+    }
+
+    /**
+     *事件识别区间的终点(ms)
+     *
+     * Generated from protobuf field <code>double EndTimeMill = 2;</code>
+     * @return float
+     */
+    public function getEndTimeMill()
+    {
+        return $this->EndTimeMill;
+    }
+
+    /**
+     *事件识别区间的终点(ms)
+     *
+     * Generated from protobuf field <code>double EndTimeMill = 2;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setEndTimeMill($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->EndTimeMill = $var;
+
+        return $this;
+    }
+
+    /**
+     *事件区间对应的事件概率
+     *
+     * Generated from protobuf field <code>double Prob = 3;</code>
+     * @return float
+     */
+    public function getProb()
+    {
+        return $this->Prob;
+    }
+
+    /**
+     *事件区间对应的事件概率
+     *
+     * Generated from protobuf field <code>double Prob = 3;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setProb($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->Prob = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodASRInfoForAudit.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodASRInfoForAudit.php
new file mode 100755
index 0000000..4f897b3
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodASRInfoForAudit.php
@@ -0,0 +1,235 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodASRInfoForAudit</code>
+ */
+class VodASRInfoForAudit extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 识别的语音文本
+     *
+     * Generated from protobuf field <code>string Text = 1;</code>
+     */
+    protected $Text = '';
+    /**
+     * 识别结果语音分句信息
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodASRUtteranceForAudit Utterances = 3;</code>
+     */
+    private $Utterances;
+    /**
+     * 整个音频对应的语言
+     *
+     * Generated from protobuf field <code>string Language = 4;</code>
+     */
+    protected $Language = '';
+    /**
+     * 语言的详情,描述每个语言的概率
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodASRLanguageDetailForAudit LanguageDetails = 5;</code>
+     */
+    private $LanguageDetails;
+    /**
+     *语速,即一秒钟几个字
+     *
+     * Generated from protobuf field <code>double SpeechRate = 6;</code>
+     */
+    protected $SpeechRate = 0.0;
+    /**
+     *音量
+     *
+     * Generated from protobuf field <code>double Volume = 7;</code>
+     */
+    protected $Volume = 0.0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Text
+     *           识别的语音文本
+     *     @type \Volc\Service\Vod\Models\Business\VodASRUtteranceForAudit[]|\Google\Protobuf\Internal\RepeatedField $Utterances
+     *           识别结果语音分句信息
+     *     @type string $Language
+     *           整个音频对应的语言
+     *     @type \Volc\Service\Vod\Models\Business\VodASRLanguageDetailForAudit[]|\Google\Protobuf\Internal\RepeatedField $LanguageDetails
+     *           语言的详情,描述每个语言的概率
+     *     @type float $SpeechRate
+     *          语速,即一秒钟几个字
+     *     @type float $Volume
+     *          音量
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 识别的语音文本
+     *
+     * Generated from protobuf field <code>string Text = 1;</code>
+     * @return string
+     */
+    public function getText()
+    {
+        return $this->Text;
+    }
+
+    /**
+     * 识别的语音文本
+     *
+     * Generated from protobuf field <code>string Text = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setText($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Text = $var;
+
+        return $this;
+    }
+
+    /**
+     * 识别结果语音分句信息
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodASRUtteranceForAudit Utterances = 3;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getUtterances()
+    {
+        return $this->Utterances;
+    }
+
+    /**
+     * 识别结果语音分句信息
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodASRUtteranceForAudit Utterances = 3;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodASRUtteranceForAudit[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setUtterances($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodASRUtteranceForAudit::class);
+        $this->Utterances = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 整个音频对应的语言
+     *
+     * Generated from protobuf field <code>string Language = 4;</code>
+     * @return string
+     */
+    public function getLanguage()
+    {
+        return $this->Language;
+    }
+
+    /**
+     * 整个音频对应的语言
+     *
+     * Generated from protobuf field <code>string Language = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setLanguage($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Language = $var;
+
+        return $this;
+    }
+
+    /**
+     * 语言的详情,描述每个语言的概率
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodASRLanguageDetailForAudit LanguageDetails = 5;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getLanguageDetails()
+    {
+        return $this->LanguageDetails;
+    }
+
+    /**
+     * 语言的详情,描述每个语言的概率
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodASRLanguageDetailForAudit LanguageDetails = 5;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodASRLanguageDetailForAudit[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setLanguageDetails($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodASRLanguageDetailForAudit::class);
+        $this->LanguageDetails = $arr;
+
+        return $this;
+    }
+
+    /**
+     *语速,即一秒钟几个字
+     *
+     * Generated from protobuf field <code>double SpeechRate = 6;</code>
+     * @return float
+     */
+    public function getSpeechRate()
+    {
+        return $this->SpeechRate;
+    }
+
+    /**
+     *语速,即一秒钟几个字
+     *
+     * Generated from protobuf field <code>double SpeechRate = 6;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setSpeechRate($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->SpeechRate = $var;
+
+        return $this;
+    }
+
+    /**
+     *音量
+     *
+     * Generated from protobuf field <code>double Volume = 7;</code>
+     * @return float
+     */
+    public function getVolume()
+    {
+        return $this->Volume;
+    }
+
+    /**
+     *音量
+     *
+     * Generated from protobuf field <code>double Volume = 7;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setVolume($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->Volume = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodASRLanguageDetailForAudit.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodASRLanguageDetailForAudit.php
new file mode 100755
index 0000000..be0672a
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodASRLanguageDetailForAudit.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodASRLanguageDetailForAudit</code>
+ */
+class VodASRLanguageDetailForAudit extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 识别为此语言的概率
+     *
+     * Generated from protobuf field <code>double Prob = 1;</code>
+     */
+    protected $Prob = 0.0;
+    /**
+     *语言类型,形式形如`<mand>`=普通话,`<sc>`=四川话,`<cant>`=粤语
+     *
+     * Generated from protobuf field <code>string Language = 2;</code>
+     */
+    protected $Language = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type float $Prob
+     *           识别为此语言的概率
+     *     @type string $Language
+     *          语言类型,形式形如`<mand>`=普通话,`<sc>`=四川话,`<cant>`=粤语
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 识别为此语言的概率
+     *
+     * Generated from protobuf field <code>double Prob = 1;</code>
+     * @return float
+     */
+    public function getProb()
+    {
+        return $this->Prob;
+    }
+
+    /**
+     * 识别为此语言的概率
+     *
+     * Generated from protobuf field <code>double Prob = 1;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setProb($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->Prob = $var;
+
+        return $this;
+    }
+
+    /**
+     *语言类型,形式形如`<mand>`=普通话,`<sc>`=四川话,`<cant>`=粤语
+     *
+     * Generated from protobuf field <code>string Language = 2;</code>
+     * @return string
+     */
+    public function getLanguage()
+    {
+        return $this->Language;
+    }
+
+    /**
+     *语言类型,形式形如`<mand>`=普通话,`<sc>`=四川话,`<cant>`=粤语
+     *
+     * Generated from protobuf field <code>string Language = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setLanguage($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Language = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodASRUtteranceForAudit.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodASRUtteranceForAudit.php
new file mode 100755
index 0000000..2a4f8e8
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodASRUtteranceForAudit.php
@@ -0,0 +1,235 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodASRUtteranceForAudit</code>
+ */
+class VodASRUtteranceForAudit extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 分段文本
+     *
+     * Generated from protobuf field <code>string Text = 1;</code>
+     */
+    protected $Text = '';
+    /**
+     * 起始时间(毫秒)
+     *
+     * Generated from protobuf field <code>double StartTimeMill = 2;</code>
+     */
+    protected $StartTimeMill = 0.0;
+    /**
+     * 结束时间(毫秒)
+     *
+     * Generated from protobuf field <code>double EndTimeMill = 3;</code>
+     */
+    protected $EndTimeMill = 0.0;
+    /**
+     * 分词
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodASRUtteranceWordForAudit Words = 4;</code>
+     */
+    private $Words;
+    /**
+     * 分句的被识别成的语言
+     *
+     * Generated from protobuf field <code>string Language = 5;</code>
+     */
+    protected $Language = '';
+    /**
+     * 语速,即一秒钟几个字
+     *
+     * Generated from protobuf field <code>double SpeechRate = 6;</code>
+     */
+    protected $SpeechRate = 0.0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Text
+     *           分段文本
+     *     @type float $StartTimeMill
+     *           起始时间(毫秒)
+     *     @type float $EndTimeMill
+     *           结束时间(毫秒)
+     *     @type \Volc\Service\Vod\Models\Business\VodASRUtteranceWordForAudit[]|\Google\Protobuf\Internal\RepeatedField $Words
+     *           分词
+     *     @type string $Language
+     *           分句的被识别成的语言
+     *     @type float $SpeechRate
+     *           语速,即一秒钟几个字
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 分段文本
+     *
+     * Generated from protobuf field <code>string Text = 1;</code>
+     * @return string
+     */
+    public function getText()
+    {
+        return $this->Text;
+    }
+
+    /**
+     * 分段文本
+     *
+     * Generated from protobuf field <code>string Text = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setText($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Text = $var;
+
+        return $this;
+    }
+
+    /**
+     * 起始时间(毫秒)
+     *
+     * Generated from protobuf field <code>double StartTimeMill = 2;</code>
+     * @return float
+     */
+    public function getStartTimeMill()
+    {
+        return $this->StartTimeMill;
+    }
+
+    /**
+     * 起始时间(毫秒)
+     *
+     * Generated from protobuf field <code>double StartTimeMill = 2;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setStartTimeMill($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->StartTimeMill = $var;
+
+        return $this;
+    }
+
+    /**
+     * 结束时间(毫秒)
+     *
+     * Generated from protobuf field <code>double EndTimeMill = 3;</code>
+     * @return float
+     */
+    public function getEndTimeMill()
+    {
+        return $this->EndTimeMill;
+    }
+
+    /**
+     * 结束时间(毫秒)
+     *
+     * Generated from protobuf field <code>double EndTimeMill = 3;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setEndTimeMill($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->EndTimeMill = $var;
+
+        return $this;
+    }
+
+    /**
+     * 分词
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodASRUtteranceWordForAudit Words = 4;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getWords()
+    {
+        return $this->Words;
+    }
+
+    /**
+     * 分词
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodASRUtteranceWordForAudit Words = 4;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodASRUtteranceWordForAudit[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setWords($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodASRUtteranceWordForAudit::class);
+        $this->Words = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 分句的被识别成的语言
+     *
+     * Generated from protobuf field <code>string Language = 5;</code>
+     * @return string
+     */
+    public function getLanguage()
+    {
+        return $this->Language;
+    }
+
+    /**
+     * 分句的被识别成的语言
+     *
+     * Generated from protobuf field <code>string Language = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setLanguage($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Language = $var;
+
+        return $this;
+    }
+
+    /**
+     * 语速,即一秒钟几个字
+     *
+     * Generated from protobuf field <code>double SpeechRate = 6;</code>
+     * @return float
+     */
+    public function getSpeechRate()
+    {
+        return $this->SpeechRate;
+    }
+
+    /**
+     * 语速,即一秒钟几个字
+     *
+     * Generated from protobuf field <code>double SpeechRate = 6;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setSpeechRate($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->SpeechRate = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodASRUtteranceWordForAudit.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodASRUtteranceWordForAudit.php
new file mode 100755
index 0000000..dd14fab
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodASRUtteranceWordForAudit.php
@@ -0,0 +1,167 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodASRUtteranceWordForAudit</code>
+ */
+class VodASRUtteranceWordForAudit extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 分段文本
+     *
+     * Generated from protobuf field <code>string Text = 1;</code>
+     */
+    protected $Text = '';
+    /**
+     * 起始时间(毫秒)
+     *
+     * Generated from protobuf field <code>double StartTimeMill = 2;</code>
+     */
+    protected $StartTimeMill = 0.0;
+    /**
+     * 结束时间(毫秒)
+     *
+     * Generated from protobuf field <code>double EndTimeMill = 3;</code>
+     */
+    protected $EndTimeMill = 0.0;
+    /**
+     * 空白时长(毫秒)
+     *
+     * Generated from protobuf field <code>double BlankDuration = 4;</code>
+     */
+    protected $BlankDuration = 0.0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Text
+     *           分段文本
+     *     @type float $StartTimeMill
+     *           起始时间(毫秒)
+     *     @type float $EndTimeMill
+     *           结束时间(毫秒)
+     *     @type float $BlankDuration
+     *           空白时长(毫秒)
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 分段文本
+     *
+     * Generated from protobuf field <code>string Text = 1;</code>
+     * @return string
+     */
+    public function getText()
+    {
+        return $this->Text;
+    }
+
+    /**
+     * 分段文本
+     *
+     * Generated from protobuf field <code>string Text = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setText($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Text = $var;
+
+        return $this;
+    }
+
+    /**
+     * 起始时间(毫秒)
+     *
+     * Generated from protobuf field <code>double StartTimeMill = 2;</code>
+     * @return float
+     */
+    public function getStartTimeMill()
+    {
+        return $this->StartTimeMill;
+    }
+
+    /**
+     * 起始时间(毫秒)
+     *
+     * Generated from protobuf field <code>double StartTimeMill = 2;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setStartTimeMill($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->StartTimeMill = $var;
+
+        return $this;
+    }
+
+    /**
+     * 结束时间(毫秒)
+     *
+     * Generated from protobuf field <code>double EndTimeMill = 3;</code>
+     * @return float
+     */
+    public function getEndTimeMill()
+    {
+        return $this->EndTimeMill;
+    }
+
+    /**
+     * 结束时间(毫秒)
+     *
+     * Generated from protobuf field <code>double EndTimeMill = 3;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setEndTimeMill($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->EndTimeMill = $var;
+
+        return $this;
+    }
+
+    /**
+     * 空白时长(毫秒)
+     *
+     * Generated from protobuf field <code>double BlankDuration = 4;</code>
+     * @return float
+     */
+    public function getBlankDuration()
+    {
+        return $this->BlankDuration;
+    }
+
+    /**
+     * 空白时长(毫秒)
+     *
+     * Generated from protobuf field <code>double BlankDuration = 4;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setBlankDuration($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->BlankDuration = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAdaptiveInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAdaptiveInfo.php
new file mode 100755
index 0000000..a279893
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAdaptiveInfo.php
@@ -0,0 +1,133 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_common.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodAdaptiveInfo</code>
+ */
+class VodAdaptiveInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * mpd主链接
+     *
+     * Generated from protobuf field <code>string MainPlayUrl = 1;</code>
+     */
+    protected $MainPlayUrl = '';
+    /**
+     * mpd备用链接
+     *
+     * Generated from protobuf field <code>string BackupPlayUrl = 2;</code>
+     */
+    protected $BackupPlayUrl = '';
+    /**
+     * 动态类型segment_base-mpd,segment_template-dash
+     *
+     * Generated from protobuf field <code>string AdaptiveType = 3;</code>
+     */
+    protected $AdaptiveType = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $MainPlayUrl
+     *           mpd主链接
+     *     @type string $BackupPlayUrl
+     *           mpd备用链接
+     *     @type string $AdaptiveType
+     *           动态类型segment_base-mpd,segment_template-dash
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCommon::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * mpd主链接
+     *
+     * Generated from protobuf field <code>string MainPlayUrl = 1;</code>
+     * @return string
+     */
+    public function getMainPlayUrl()
+    {
+        return $this->MainPlayUrl;
+    }
+
+    /**
+     * mpd主链接
+     *
+     * Generated from protobuf field <code>string MainPlayUrl = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setMainPlayUrl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->MainPlayUrl = $var;
+
+        return $this;
+    }
+
+    /**
+     * mpd备用链接
+     *
+     * Generated from protobuf field <code>string BackupPlayUrl = 2;</code>
+     * @return string
+     */
+    public function getBackupPlayUrl()
+    {
+        return $this->BackupPlayUrl;
+    }
+
+    /**
+     * mpd备用链接
+     *
+     * Generated from protobuf field <code>string BackupPlayUrl = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setBackupPlayUrl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->BackupPlayUrl = $var;
+
+        return $this;
+    }
+
+    /**
+     * 动态类型segment_base-mpd,segment_template-dash
+     *
+     * Generated from protobuf field <code>string AdaptiveType = 3;</code>
+     * @return string
+     */
+    public function getAdaptiveType()
+    {
+        return $this->AdaptiveType;
+    }
+
+    /**
+     * 动态类型segment_base-mpd,segment_template-dash
+     *
+     * Generated from protobuf field <code>string AdaptiveType = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setAdaptiveType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->AdaptiveType = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAllPlayInfoModel.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAllPlayInfoModel.php
new file mode 100755
index 0000000..3031d69
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAllPlayInfoModel.php
@@ -0,0 +1,415 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_common.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodAllPlayInfoModel</code>
+ */
+class VodAllPlayInfoModel extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 唯一ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     */
+    protected $Vid = '';
+    /**
+     * 状态
+     *
+     * Generated from protobuf field <code>int32 Status = 2;</code>
+     */
+    protected $Status = 0;
+    /**
+     * 封面地址
+     *
+     * Generated from protobuf field <code>string PosterUrl = 3;</code>
+     */
+    protected $PosterUrl = '';
+    /**
+     * 播放列表数量
+     *
+     * Generated from protobuf field <code>int32 TotalCount = 4;</code>
+     */
+    protected $TotalCount = 0;
+    /**
+     * 是否关键针对齐
+     *
+     * Generated from protobuf field <code>bool EnableAdaptive = 5;</code>
+     */
+    protected $EnableAdaptive = false;
+    /**
+     * 转码流
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodPlayInfo VodTranscodePlayInfo = 6;</code>
+     */
+    private $VodTranscodePlayInfo;
+    /**
+     * 原片流
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodPlayInfo VodSourcePlayInfo = 7;</code>
+     */
+    protected $VodSourcePlayInfo = null;
+    /**
+     * 数据版本
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodPlayInfoModelVersion Version = 8;</code>
+     */
+    protected $Version = 0;
+    /**
+     * 雪碧图列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodThumbInfo ThumbInfoList = 9;</code>
+     */
+    private $ThumbInfoList;
+    /**
+     * 弹幕蒙版url
+     *
+     * Generated from protobuf field <code>string BarrageMaskUrl = 10;</code>
+     */
+    protected $BarrageMaskUrl = '';
+    /**
+     * 字幕信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSubtitleInfo SubtitleInfoList = 11;</code>
+     */
+    private $SubtitleInfoList;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vid
+     *           唯一ID
+     *     @type int $Status
+     *           状态
+     *     @type string $PosterUrl
+     *           封面地址
+     *     @type int $TotalCount
+     *           播放列表数量
+     *     @type bool $EnableAdaptive
+     *           是否关键针对齐
+     *     @type \Volc\Service\Vod\Models\Business\VodPlayInfo[]|\Google\Protobuf\Internal\RepeatedField $VodTranscodePlayInfo
+     *           转码流
+     *     @type \Volc\Service\Vod\Models\Business\VodPlayInfo $VodSourcePlayInfo
+     *           原片流
+     *     @type int $Version
+     *           数据版本
+     *     @type \Volc\Service\Vod\Models\Business\VodThumbInfo[]|\Google\Protobuf\Internal\RepeatedField $ThumbInfoList
+     *           雪碧图列表
+     *     @type string $BarrageMaskUrl
+     *           弹幕蒙版url
+     *     @type \Volc\Service\Vod\Models\Business\VodSubtitleInfo[]|\Google\Protobuf\Internal\RepeatedField $SubtitleInfoList
+     *           字幕信息列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCommon::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 唯一ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     * 唯一ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     * 状态
+     *
+     * Generated from protobuf field <code>int32 Status = 2;</code>
+     * @return int
+     */
+    public function getStatus()
+    {
+        return $this->Status;
+    }
+
+    /**
+     * 状态
+     *
+     * Generated from protobuf field <code>int32 Status = 2;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setStatus($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Status = $var;
+
+        return $this;
+    }
+
+    /**
+     * 封面地址
+     *
+     * Generated from protobuf field <code>string PosterUrl = 3;</code>
+     * @return string
+     */
+    public function getPosterUrl()
+    {
+        return $this->PosterUrl;
+    }
+
+    /**
+     * 封面地址
+     *
+     * Generated from protobuf field <code>string PosterUrl = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setPosterUrl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->PosterUrl = $var;
+
+        return $this;
+    }
+
+    /**
+     * 播放列表数量
+     *
+     * Generated from protobuf field <code>int32 TotalCount = 4;</code>
+     * @return int
+     */
+    public function getTotalCount()
+    {
+        return $this->TotalCount;
+    }
+
+    /**
+     * 播放列表数量
+     *
+     * Generated from protobuf field <code>int32 TotalCount = 4;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setTotalCount($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->TotalCount = $var;
+
+        return $this;
+    }
+
+    /**
+     * 是否关键针对齐
+     *
+     * Generated from protobuf field <code>bool EnableAdaptive = 5;</code>
+     * @return bool
+     */
+    public function getEnableAdaptive()
+    {
+        return $this->EnableAdaptive;
+    }
+
+    /**
+     * 是否关键针对齐
+     *
+     * Generated from protobuf field <code>bool EnableAdaptive = 5;</code>
+     * @param bool $var
+     * @return $this
+     */
+    public function setEnableAdaptive($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->EnableAdaptive = $var;
+
+        return $this;
+    }
+
+    /**
+     * 转码流
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodPlayInfo VodTranscodePlayInfo = 6;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getVodTranscodePlayInfo()
+    {
+        return $this->VodTranscodePlayInfo;
+    }
+
+    /**
+     * 转码流
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodPlayInfo VodTranscodePlayInfo = 6;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodPlayInfo[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setVodTranscodePlayInfo($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodPlayInfo::class);
+        $this->VodTranscodePlayInfo = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 原片流
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodPlayInfo VodSourcePlayInfo = 7;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodPlayInfo|null
+     */
+    public function getVodSourcePlayInfo()
+    {
+        return $this->VodSourcePlayInfo;
+    }
+
+    public function hasVodSourcePlayInfo()
+    {
+        return isset($this->VodSourcePlayInfo);
+    }
+
+    public function clearVodSourcePlayInfo()
+    {
+        unset($this->VodSourcePlayInfo);
+    }
+
+    /**
+     * 原片流
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodPlayInfo VodSourcePlayInfo = 7;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodPlayInfo $var
+     * @return $this
+     */
+    public function setVodSourcePlayInfo($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodPlayInfo::class);
+        $this->VodSourcePlayInfo = $var;
+
+        return $this;
+    }
+
+    /**
+     * 数据版本
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodPlayInfoModelVersion Version = 8;</code>
+     * @return int
+     */
+    public function getVersion()
+    {
+        return $this->Version;
+    }
+
+    /**
+     * 数据版本
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodPlayInfoModelVersion Version = 8;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setVersion($var)
+    {
+        GPBUtil::checkEnum($var, \Volc\Service\Vod\Models\Business\VodPlayInfoModelVersion::class);
+        $this->Version = $var;
+
+        return $this;
+    }
+
+    /**
+     * 雪碧图列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodThumbInfo ThumbInfoList = 9;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getThumbInfoList()
+    {
+        return $this->ThumbInfoList;
+    }
+
+    /**
+     * 雪碧图列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodThumbInfo ThumbInfoList = 9;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodThumbInfo[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setThumbInfoList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodThumbInfo::class);
+        $this->ThumbInfoList = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 弹幕蒙版url
+     *
+     * Generated from protobuf field <code>string BarrageMaskUrl = 10;</code>
+     * @return string
+     */
+    public function getBarrageMaskUrl()
+    {
+        return $this->BarrageMaskUrl;
+    }
+
+    /**
+     * 弹幕蒙版url
+     *
+     * Generated from protobuf field <code>string BarrageMaskUrl = 10;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setBarrageMaskUrl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->BarrageMaskUrl = $var;
+
+        return $this;
+    }
+
+    /**
+     * 字幕信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSubtitleInfo SubtitleInfoList = 11;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getSubtitleInfoList()
+    {
+        return $this->SubtitleInfoList;
+    }
+
+    /**
+     * 字幕信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSubtitleInfo SubtitleInfoList = 11;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodSubtitleInfo[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setSubtitleInfoList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodSubtitleInfo::class);
+        $this->SubtitleInfoList = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAllPlayInfoResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAllPlayInfoResult.php
new file mode 100755
index 0000000..5df7fdf
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAllPlayInfoResult.php
@@ -0,0 +1,126 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_common.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodAllPlayInfoResult</code>
+ */
+class VodAllPlayInfoResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodAllPlayInfoModel VodAllPlayInfoModelList = 1;</code>
+     */
+    private $VodAllPlayInfoModelList;
+    /**
+     * 查到多少个vid
+     *
+     * Generated from protobuf field <code>int32 TotalCount = 2;</code>
+     */
+    protected $TotalCount = 0;
+    /**
+     * 没有查询到任何信息的vid
+     *
+     * Generated from protobuf field <code>repeated string NotFoundVids = 3;</code>
+     */
+    private $NotFoundVids;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\VodAllPlayInfoModel[]|\Google\Protobuf\Internal\RepeatedField $VodAllPlayInfoModelList
+     *     @type int $TotalCount
+     *           查到多少个vid
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $NotFoundVids
+     *           没有查询到任何信息的vid
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCommon::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodAllPlayInfoModel VodAllPlayInfoModelList = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getVodAllPlayInfoModelList()
+    {
+        return $this->VodAllPlayInfoModelList;
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodAllPlayInfoModel VodAllPlayInfoModelList = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodAllPlayInfoModel[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setVodAllPlayInfoModelList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodAllPlayInfoModel::class);
+        $this->VodAllPlayInfoModelList = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 查到多少个vid
+     *
+     * Generated from protobuf field <code>int32 TotalCount = 2;</code>
+     * @return int
+     */
+    public function getTotalCount()
+    {
+        return $this->TotalCount;
+    }
+
+    /**
+     * 查到多少个vid
+     *
+     * Generated from protobuf field <code>int32 TotalCount = 2;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setTotalCount($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->TotalCount = $var;
+
+        return $this;
+    }
+
+    /**
+     * 没有查询到任何信息的vid
+     *
+     * Generated from protobuf field <code>repeated string NotFoundVids = 3;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getNotFoundVids()
+    {
+        return $this->NotFoundVids;
+    }
+
+    /**
+     * 没有查询到任何信息的vid
+     *
+     * Generated from protobuf field <code>repeated string NotFoundVids = 3;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setNotFoundVids($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->NotFoundVids = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodApplyUploadInfoData.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodApplyUploadInfoData.php
new file mode 100755
index 0000000..0efe8fb
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodApplyUploadInfoData.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_upload.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodApplyUploadInfoData</code>
+ */
+class VodApplyUploadInfoData extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 1001 阶段返回信息 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodUploadAddress UploadAddress = 1;</code>
+     */
+    protected $UploadAddress = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\VodUploadAddress $UploadAddress
+     *           1001 阶段返回信息 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodUpload::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 1001 阶段返回信息 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodUploadAddress UploadAddress = 1;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodUploadAddress|null
+     */
+    public function getUploadAddress()
+    {
+        return $this->UploadAddress;
+    }
+
+    public function hasUploadAddress()
+    {
+        return isset($this->UploadAddress);
+    }
+
+    public function clearUploadAddress()
+    {
+        unset($this->UploadAddress);
+    }
+
+    /**
+     * 1001 阶段返回信息 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodUploadAddress UploadAddress = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodUploadAddress $var
+     * @return $this
+     */
+    public function setUploadAddress($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodUploadAddress::class);
+        $this->UploadAddress = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodApplyUploadInfoResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodApplyUploadInfoResult.php
new file mode 100755
index 0000000..581b637
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodApplyUploadInfoResult.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_upload.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodApplyUploadInfoResult</code>
+ */
+class VodApplyUploadInfoResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 1001 阶段返回信息 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodApplyUploadInfoData Data = 1;</code>
+     */
+    protected $Data = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\VodApplyUploadInfoData $Data
+     *           1001 阶段返回信息 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodUpload::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 1001 阶段返回信息 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodApplyUploadInfoData Data = 1;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodApplyUploadInfoData|null
+     */
+    public function getData()
+    {
+        return $this->Data;
+    }
+
+    public function hasData()
+    {
+        return isset($this->Data);
+    }
+
+    public function clearData()
+    {
+        unset($this->Data);
+    }
+
+    /**
+     * 1001 阶段返回信息 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodApplyUploadInfoData Data = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodApplyUploadInfoData $var
+     * @return $this
+     */
+    public function setData($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodApplyUploadInfoData::class);
+        $this->Data = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAudioInfoForAudit.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAudioInfoForAudit.php
new file mode 100755
index 0000000..73cdc62
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAudioInfoForAudit.php
@@ -0,0 +1,133 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodAudioInfoForAudit</code>
+ */
+class VodAudioInfoForAudit extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *审核音频存储的URI
+     *
+     * Generated from protobuf field <code>string StoreUri = 1;</code>
+     */
+    protected $StoreUri = '';
+    /**
+     *音频时长(ms)
+     *
+     * Generated from protobuf field <code>double Duration = 2;</code>
+     */
+    protected $Duration = 0.0;
+    /**
+     *格式:wav
+     *
+     * Generated from protobuf field <code>string Format = 3;</code>
+     */
+    protected $Format = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $StoreUri
+     *          审核音频存储的URI
+     *     @type float $Duration
+     *          音频时长(ms)
+     *     @type string $Format
+     *          格式:wav
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *审核音频存储的URI
+     *
+     * Generated from protobuf field <code>string StoreUri = 1;</code>
+     * @return string
+     */
+    public function getStoreUri()
+    {
+        return $this->StoreUri;
+    }
+
+    /**
+     *审核音频存储的URI
+     *
+     * Generated from protobuf field <code>string StoreUri = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStoreUri($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StoreUri = $var;
+
+        return $this;
+    }
+
+    /**
+     *音频时长(ms)
+     *
+     * Generated from protobuf field <code>double Duration = 2;</code>
+     * @return float
+     */
+    public function getDuration()
+    {
+        return $this->Duration;
+    }
+
+    /**
+     *音频时长(ms)
+     *
+     * Generated from protobuf field <code>double Duration = 2;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setDuration($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->Duration = $var;
+
+        return $this;
+    }
+
+    /**
+     *格式:wav
+     *
+     * Generated from protobuf field <code>string Format = 3;</code>
+     * @return string
+     */
+    public function getFormat()
+    {
+        return $this->Format;
+    }
+
+    /**
+     *格式:wav
+     *
+     * Generated from protobuf field <code>string Format = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFormat($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Format = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAudioStreamMeta.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAudioStreamMeta.php
new file mode 100755
index 0000000..a5d5c76
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodAudioStreamMeta.php
@@ -0,0 +1,201 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_common.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodAudioStreamMeta</code>
+ */
+class VodAudioStreamMeta extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *音频编码格式
+     *
+     * Generated from protobuf field <code>string Codec = 1;</code>
+     */
+    protected $Codec = '';
+    /**
+     *音频时长
+     *
+     * Generated from protobuf field <code>float Duration = 2;</code>
+     */
+    protected $Duration = 0.0;
+    /**
+     *音频采样率
+     *
+     * Generated from protobuf field <code>int32 SampleRate = 3;</code>
+     */
+    protected $SampleRate = 0;
+    /**
+     *音频码率(Kbps)
+     *
+     * Generated from protobuf field <code>int32 Bitrate = 4;</code>
+     */
+    protected $Bitrate = 0;
+    /**
+     *音频质量
+     *
+     * Generated from protobuf field <code>string Quality = 5;</code>
+     */
+    protected $Quality = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Codec
+     *          音频编码格式
+     *     @type float $Duration
+     *          音频时长
+     *     @type int $SampleRate
+     *          音频采样率
+     *     @type int $Bitrate
+     *          音频码率(Kbps)
+     *     @type string $Quality
+     *          音频质量
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCommon::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *音频编码格式
+     *
+     * Generated from protobuf field <code>string Codec = 1;</code>
+     * @return string
+     */
+    public function getCodec()
+    {
+        return $this->Codec;
+    }
+
+    /**
+     *音频编码格式
+     *
+     * Generated from protobuf field <code>string Codec = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCodec($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Codec = $var;
+
+        return $this;
+    }
+
+    /**
+     *音频时长
+     *
+     * Generated from protobuf field <code>float Duration = 2;</code>
+     * @return float
+     */
+    public function getDuration()
+    {
+        return $this->Duration;
+    }
+
+    /**
+     *音频时长
+     *
+     * Generated from protobuf field <code>float Duration = 2;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setDuration($var)
+    {
+        GPBUtil::checkFloat($var);
+        $this->Duration = $var;
+
+        return $this;
+    }
+
+    /**
+     *音频采样率
+     *
+     * Generated from protobuf field <code>int32 SampleRate = 3;</code>
+     * @return int
+     */
+    public function getSampleRate()
+    {
+        return $this->SampleRate;
+    }
+
+    /**
+     *音频采样率
+     *
+     * Generated from protobuf field <code>int32 SampleRate = 3;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setSampleRate($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->SampleRate = $var;
+
+        return $this;
+    }
+
+    /**
+     *音频码率(Kbps)
+     *
+     * Generated from protobuf field <code>int32 Bitrate = 4;</code>
+     * @return int
+     */
+    public function getBitrate()
+    {
+        return $this->Bitrate;
+    }
+
+    /**
+     *音频码率(Kbps)
+     *
+     * Generated from protobuf field <code>int32 Bitrate = 4;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setBitrate($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Bitrate = $var;
+
+        return $this;
+    }
+
+    /**
+     *音频质量
+     *
+     * Generated from protobuf field <code>string Quality = 5;</code>
+     * @return string
+     */
+    public function getQuality()
+    {
+        return $this->Quality;
+    }
+
+    /**
+     *音频质量
+     *
+     * Generated from protobuf field <code>string Quality = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setQuality($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Quality = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodBandwidthData.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodBandwidthData.php
new file mode 100755
index 0000000..0576661
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodBandwidthData.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_cdn.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodBandwidthData</code>
+ */
+class VodBandwidthData extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 每个时间粒度的时间
+     *
+     * Generated from protobuf field <code>string Time = 1;</code>
+     */
+    protected $Time = '';
+    /**
+     * 带宽,单位为 Mbps
+     *
+     * Generated from protobuf field <code>double Bandwidth = 2;</code>
+     */
+    protected $Bandwidth = 0.0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Time
+     *           每个时间粒度的时间
+     *     @type float $Bandwidth
+     *           带宽,单位为 Mbps
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCdn::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 每个时间粒度的时间
+     *
+     * Generated from protobuf field <code>string Time = 1;</code>
+     * @return string
+     */
+    public function getTime()
+    {
+        return $this->Time;
+    }
+
+    /**
+     * 每个时间粒度的时间
+     *
+     * Generated from protobuf field <code>string Time = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Time = $var;
+
+        return $this;
+    }
+
+    /**
+     * 带宽,单位为 Mbps
+     *
+     * Generated from protobuf field <code>double Bandwidth = 2;</code>
+     * @return float
+     */
+    public function getBandwidth()
+    {
+        return $this->Bandwidth;
+    }
+
+    /**
+     * 带宽,单位为 Mbps
+     *
+     * Generated from protobuf field <code>double Bandwidth = 2;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setBandwidth($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->Bandwidth = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodBetterFrameDataForAudit.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodBetterFrameDataForAudit.php
new file mode 100755
index 0000000..2942817
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodBetterFrameDataForAudit.php
@@ -0,0 +1,269 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodBetterFrameDataForAudit</code>
+ */
+class VodBetterFrameDataForAudit extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Frame URI
+     *
+     * Generated from protobuf field <code>string StoreUri = 1;</code>
+     */
+    protected $StoreUri = '';
+    /**
+     * 帧号
+     *
+     * Generated from protobuf field <code>int32 FrameNumber = 2;</code>
+     */
+    protected $FrameNumber = 0;
+    /**
+     * 帧的时间戳,单位:ms
+     *
+     * Generated from protobuf field <code>double CutTimeMill = 3;</code>
+     */
+    protected $CutTimeMill = 0.0;
+    /**
+     *截图的宽度
+     *
+     * Generated from protobuf field <code>int32 Width = 4;</code>
+     */
+    protected $Width = 0;
+    /**
+     *截图的高度
+     *
+     * Generated from protobuf field <code>int32 Height = 5;</code>
+     */
+    protected $Height = 0;
+    /**
+     * Cover Rate
+     *
+     * Generated from protobuf field <code>double CoverRate = 6;</code>
+     */
+    protected $CoverRate = 0.0;
+    /**
+     * Hash Code
+     *
+     * Generated from protobuf field <code>repeated double LBPHashCode = 7;</code>
+     */
+    private $LBPHashCode;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $StoreUri
+     *           Frame URI
+     *     @type int $FrameNumber
+     *           帧号
+     *     @type float $CutTimeMill
+     *           帧的时间戳,单位:ms
+     *     @type int $Width
+     *          截图的宽度
+     *     @type int $Height
+     *          截图的高度
+     *     @type float $CoverRate
+     *           Cover Rate
+     *     @type float[]|\Google\Protobuf\Internal\RepeatedField $LBPHashCode
+     *           Hash Code
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Frame URI
+     *
+     * Generated from protobuf field <code>string StoreUri = 1;</code>
+     * @return string
+     */
+    public function getStoreUri()
+    {
+        return $this->StoreUri;
+    }
+
+    /**
+     * Frame URI
+     *
+     * Generated from protobuf field <code>string StoreUri = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStoreUri($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StoreUri = $var;
+
+        return $this;
+    }
+
+    /**
+     * 帧号
+     *
+     * Generated from protobuf field <code>int32 FrameNumber = 2;</code>
+     * @return int
+     */
+    public function getFrameNumber()
+    {
+        return $this->FrameNumber;
+    }
+
+    /**
+     * 帧号
+     *
+     * Generated from protobuf field <code>int32 FrameNumber = 2;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setFrameNumber($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->FrameNumber = $var;
+
+        return $this;
+    }
+
+    /**
+     * 帧的时间戳,单位:ms
+     *
+     * Generated from protobuf field <code>double CutTimeMill = 3;</code>
+     * @return float
+     */
+    public function getCutTimeMill()
+    {
+        return $this->CutTimeMill;
+    }
+
+    /**
+     * 帧的时间戳,单位:ms
+     *
+     * Generated from protobuf field <code>double CutTimeMill = 3;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setCutTimeMill($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->CutTimeMill = $var;
+
+        return $this;
+    }
+
+    /**
+     *截图的宽度
+     *
+     * Generated from protobuf field <code>int32 Width = 4;</code>
+     * @return int
+     */
+    public function getWidth()
+    {
+        return $this->Width;
+    }
+
+    /**
+     *截图的宽度
+     *
+     * Generated from protobuf field <code>int32 Width = 4;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setWidth($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Width = $var;
+
+        return $this;
+    }
+
+    /**
+     *截图的高度
+     *
+     * Generated from protobuf field <code>int32 Height = 5;</code>
+     * @return int
+     */
+    public function getHeight()
+    {
+        return $this->Height;
+    }
+
+    /**
+     *截图的高度
+     *
+     * Generated from protobuf field <code>int32 Height = 5;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setHeight($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Height = $var;
+
+        return $this;
+    }
+
+    /**
+     * Cover Rate
+     *
+     * Generated from protobuf field <code>double CoverRate = 6;</code>
+     * @return float
+     */
+    public function getCoverRate()
+    {
+        return $this->CoverRate;
+    }
+
+    /**
+     * Cover Rate
+     *
+     * Generated from protobuf field <code>double CoverRate = 6;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setCoverRate($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->CoverRate = $var;
+
+        return $this;
+    }
+
+    /**
+     * Hash Code
+     *
+     * Generated from protobuf field <code>repeated double LBPHashCode = 7;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getLBPHashCode()
+    {
+        return $this->LBPHashCode;
+    }
+
+    /**
+     * Hash Code
+     *
+     * Generated from protobuf field <code>repeated double LBPHashCode = 7;</code>
+     * @param float[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setLBPHashCode($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::DOUBLE);
+        $this->LBPHashCode = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCallbackAuthType.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCallbackAuthType.php
new file mode 100755
index 0000000..f439d9b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCallbackAuthType.php
@@ -0,0 +1,52 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_callback.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use UnexpectedValueException;
+
+/**
+ * Protobuf type <code>Volcengine.Vod.Models.Business.VodCallbackAuthType</code>
+ */
+class VodCallbackAuthType
+{
+    /**
+     * 回调鉴权关闭
+     *
+     * Generated from protobuf enum <code>VodCallbackAuthTypeDisabled = 0;</code>
+     */
+    const VodCallbackAuthTypeDisabled = 0;
+    /**
+     * 回调鉴权开启
+     *
+     * Generated from protobuf enum <code>VodCallbackAuthTypeEnabled = 1;</code>
+     */
+    const VodCallbackAuthTypeEnabled = 1;
+
+    private static $valueToName = [
+        self::VodCallbackAuthTypeDisabled => 'VodCallbackAuthTypeDisabled',
+        self::VodCallbackAuthTypeEnabled => 'VodCallbackAuthTypeEnabled',
+    ];
+
+    public static function name($value)
+    {
+        if (!isset(self::$valueToName[$value])) {
+            throw new UnexpectedValueException(sprintf(
+                    'Enum %s has no name defined for value %s', __CLASS__, $value));
+        }
+        return self::$valueToName[$value];
+    }
+
+
+    public static function value($name)
+    {
+        $const = __CLASS__ . '::' . strtoupper($name);
+        if (!defined($const)) {
+            throw new UnexpectedValueException(sprintf(
+                    'Enum %s has no value defined for name %s', __CLASS__, $name));
+        }
+        return constant($const);
+    }
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnAccessLogElement.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnAccessLogElement.php
new file mode 100755
index 0000000..288dcc6
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnAccessLogElement.php
@@ -0,0 +1,201 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_cdn.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodCdnAccessLogElement</code>
+ */
+class VodCdnAccessLogElement extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 下载地址
+     *
+     * Generated from protobuf field <code>string DownloadUrl = 1;</code>
+     */
+    protected $DownloadUrl = '';
+    /**
+     * 文件大小,单位:bytes
+     *
+     * Generated from protobuf field <code>int64 FileSize = 2;</code>
+     */
+    protected $FileSize = 0;
+    /**
+     * 日志文件名
+     *
+     * Generated from protobuf field <code>string FileName = 3;</code>
+     */
+    protected $FileName = '';
+    /**
+     * 日志包起始时间,单位:Unix秒级时间戳
+     *
+     * Generated from protobuf field <code>int32 StartTimestamp = 4;</code>
+     */
+    protected $StartTimestamp = 0;
+    /**
+     * 日志包结束时间,单位:Unix秒级时间戳
+     *
+     * Generated from protobuf field <code>int32 EndTimestamp = 5;</code>
+     */
+    protected $EndTimestamp = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $DownloadUrl
+     *           下载地址
+     *     @type int|string $FileSize
+     *           文件大小,单位:bytes
+     *     @type string $FileName
+     *           日志文件名
+     *     @type int $StartTimestamp
+     *           日志包起始时间,单位:Unix秒级时间戳
+     *     @type int $EndTimestamp
+     *           日志包结束时间,单位:Unix秒级时间戳
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCdn::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 下载地址
+     *
+     * Generated from protobuf field <code>string DownloadUrl = 1;</code>
+     * @return string
+     */
+    public function getDownloadUrl()
+    {
+        return $this->DownloadUrl;
+    }
+
+    /**
+     * 下载地址
+     *
+     * Generated from protobuf field <code>string DownloadUrl = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDownloadUrl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->DownloadUrl = $var;
+
+        return $this;
+    }
+
+    /**
+     * 文件大小,单位:bytes
+     *
+     * Generated from protobuf field <code>int64 FileSize = 2;</code>
+     * @return int|string
+     */
+    public function getFileSize()
+    {
+        return $this->FileSize;
+    }
+
+    /**
+     * 文件大小,单位:bytes
+     *
+     * Generated from protobuf field <code>int64 FileSize = 2;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setFileSize($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->FileSize = $var;
+
+        return $this;
+    }
+
+    /**
+     * 日志文件名
+     *
+     * Generated from protobuf field <code>string FileName = 3;</code>
+     * @return string
+     */
+    public function getFileName()
+    {
+        return $this->FileName;
+    }
+
+    /**
+     * 日志文件名
+     *
+     * Generated from protobuf field <code>string FileName = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 日志包起始时间,单位:Unix秒级时间戳
+     *
+     * Generated from protobuf field <code>int32 StartTimestamp = 4;</code>
+     * @return int
+     */
+    public function getStartTimestamp()
+    {
+        return $this->StartTimestamp;
+    }
+
+    /**
+     * 日志包起始时间,单位:Unix秒级时间戳
+     *
+     * Generated from protobuf field <code>int32 StartTimestamp = 4;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setStartTimestamp($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->StartTimestamp = $var;
+
+        return $this;
+    }
+
+    /**
+     * 日志包结束时间,单位:Unix秒级时间戳
+     *
+     * Generated from protobuf field <code>int32 EndTimestamp = 5;</code>
+     * @return int
+     */
+    public function getEndTimestamp()
+    {
+        return $this->EndTimestamp;
+    }
+
+    /**
+     * 日志包结束时间,单位:Unix秒级时间戳
+     *
+     * Generated from protobuf field <code>int32 EndTimestamp = 5;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setEndTimestamp($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->EndTimestamp = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnAccessLogInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnAccessLogInfo.php
new file mode 100755
index 0000000..df068e6
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnAccessLogInfo.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_cdn.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodCdnAccessLogInfo</code>
+ */
+class VodCdnAccessLogInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 域名
+     *
+     * Generated from protobuf field <code>string Domain = 1;</code>
+     */
+    protected $Domain = '';
+    /**
+     * 日志列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodCdnAccessLogElement LogList = 2;</code>
+     */
+    private $LogList;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Domain
+     *           域名
+     *     @type \Volc\Service\Vod\Models\Business\VodCdnAccessLogElement[]|\Google\Protobuf\Internal\RepeatedField $LogList
+     *           日志列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCdn::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 域名
+     *
+     * Generated from protobuf field <code>string Domain = 1;</code>
+     * @return string
+     */
+    public function getDomain()
+    {
+        return $this->Domain;
+    }
+
+    /**
+     * 域名
+     *
+     * Generated from protobuf field <code>string Domain = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomain($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Domain = $var;
+
+        return $this;
+    }
+
+    /**
+     * 日志列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodCdnAccessLogElement LogList = 2;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getLogList()
+    {
+        return $this->LogList;
+    }
+
+    /**
+     * 日志列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodCdnAccessLogElement LogList = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodCdnAccessLogElement[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setLogList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodCdnAccessLogElement::class);
+        $this->LogList = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnIpInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnIpInfo.php
new file mode 100755
index 0000000..a4c2b5a
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnIpInfo.php
@@ -0,0 +1,167 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_cdn.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodCdnIpInfo</code>
+ */
+class VodCdnIpInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * ip地址
+     *
+     * Generated from protobuf field <code>string Ip = 1;</code>
+     */
+    protected $Ip = '';
+    /**
+     *是否是火山cdn的ip
+     *
+     * Generated from protobuf field <code>bool CdnIp = 2;</code>
+     */
+    protected $CdnIp = false;
+    /**
+     * IP所属地区,非火山cdn返回"-"
+     *
+     * Generated from protobuf field <code>string Location = 3;</code>
+     */
+    protected $Location = '';
+    /**
+     * IP所属运营商,非火山cdn返回"-"
+     *
+     * Generated from protobuf field <code>string Isp = 4;</code>
+     */
+    protected $Isp = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Ip
+     *           ip地址
+     *     @type bool $CdnIp
+     *          是否是火山cdn的ip
+     *     @type string $Location
+     *           IP所属地区,非火山cdn返回"-"
+     *     @type string $Isp
+     *           IP所属运营商,非火山cdn返回"-"
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCdn::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * ip地址
+     *
+     * Generated from protobuf field <code>string Ip = 1;</code>
+     * @return string
+     */
+    public function getIp()
+    {
+        return $this->Ip;
+    }
+
+    /**
+     * ip地址
+     *
+     * Generated from protobuf field <code>string Ip = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setIp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Ip = $var;
+
+        return $this;
+    }
+
+    /**
+     *是否是火山cdn的ip
+     *
+     * Generated from protobuf field <code>bool CdnIp = 2;</code>
+     * @return bool
+     */
+    public function getCdnIp()
+    {
+        return $this->CdnIp;
+    }
+
+    /**
+     *是否是火山cdn的ip
+     *
+     * Generated from protobuf field <code>bool CdnIp = 2;</code>
+     * @param bool $var
+     * @return $this
+     */
+    public function setCdnIp($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->CdnIp = $var;
+
+        return $this;
+    }
+
+    /**
+     * IP所属地区,非火山cdn返回"-"
+     *
+     * Generated from protobuf field <code>string Location = 3;</code>
+     * @return string
+     */
+    public function getLocation()
+    {
+        return $this->Location;
+    }
+
+    /**
+     * IP所属地区,非火山cdn返回"-"
+     *
+     * Generated from protobuf field <code>string Location = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setLocation($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Location = $var;
+
+        return $this;
+    }
+
+    /**
+     * IP所属运营商,非火山cdn返回"-"
+     *
+     * Generated from protobuf field <code>string Isp = 4;</code>
+     * @return string
+     */
+    public function getIsp()
+    {
+        return $this->Isp;
+    }
+
+    /**
+     * IP所属运营商,非火山cdn返回"-"
+     *
+     * Generated from protobuf field <code>string Isp = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setIsp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Isp = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnStatisticsCommonResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnStatisticsCommonResult.php
new file mode 100755
index 0000000..7a93757
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnStatisticsCommonResult.php
@@ -0,0 +1,85 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_cdn.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodCdnStatisticsCommonResult</code>
+ */
+class VodCdnStatisticsCommonResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodCdnStatisticsData Datas = 1;</code>
+     */
+    private $Datas;
+    /**
+     * Generated from protobuf field <code>repeated string NoPermissionDomains = 2;</code>
+     */
+    private $NoPermissionDomains;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\VodCdnStatisticsData[]|\Google\Protobuf\Internal\RepeatedField $Datas
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $NoPermissionDomains
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCdn::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodCdnStatisticsData Datas = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getDatas()
+    {
+        return $this->Datas;
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodCdnStatisticsData Datas = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodCdnStatisticsData[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setDatas($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodCdnStatisticsData::class);
+        $this->Datas = $arr;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated string NoPermissionDomains = 2;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getNoPermissionDomains()
+    {
+        return $this->NoPermissionDomains;
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated string NoPermissionDomains = 2;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setNoPermissionDomains($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->NoPermissionDomains = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnStatisticsData.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnStatisticsData.php
new file mode 100755
index 0000000..963359e
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnStatisticsData.php
@@ -0,0 +1,139 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_cdn.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodCdnStatisticsData</code>
+ */
+class VodCdnStatisticsData extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>string Name = 1;</code>
+     */
+    protected $Name = '';
+    /**
+     * Generated from protobuf field <code>string Metric = 2;</code>
+     */
+    protected $Metric = '';
+    /**
+     * Generated from protobuf field <code>string DataType = 3;</code>
+     */
+    protected $DataType = '';
+    /**
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodPoint Points = 4;</code>
+     */
+    private $Points;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Name
+     *     @type string $Metric
+     *     @type string $DataType
+     *     @type \Volc\Service\Vod\Models\Business\VodPoint[]|\Google\Protobuf\Internal\RepeatedField $Points
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCdn::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>string Name = 1;</code>
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->Name;
+    }
+
+    /**
+     * Generated from protobuf field <code>string Name = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Name = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string Metric = 2;</code>
+     * @return string
+     */
+    public function getMetric()
+    {
+        return $this->Metric;
+    }
+
+    /**
+     * Generated from protobuf field <code>string Metric = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setMetric($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Metric = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string DataType = 3;</code>
+     * @return string
+     */
+    public function getDataType()
+    {
+        return $this->DataType;
+    }
+
+    /**
+     * Generated from protobuf field <code>string DataType = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDataType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->DataType = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodPoint Points = 4;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getPoints()
+    {
+        return $this->Points;
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodPoint Points = 4;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodPoint[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setPoints($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodPoint::class);
+        $this->Points = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnTaskResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnTaskResult.php
new file mode 100755
index 0000000..a27c9f4
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnTaskResult.php
@@ -0,0 +1,167 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_cdn.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodCdnTaskResult</code>
+ */
+class VodCdnTaskResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *结果总数
+     *
+     * Generated from protobuf field <code>int32 TotalCount = 1;</code>
+     */
+    protected $TotalCount = 0;
+    /**
+     *页码
+     *
+     * Generated from protobuf field <code>int32 PageNum = 2;</code>
+     */
+    protected $PageNum = 0;
+    /**
+     *页面大小
+     *
+     * Generated from protobuf field <code>int32 PageSize = 3;</code>
+     */
+    protected $PageSize = 0;
+    /**
+     *任务列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodContentInfo ContentInfos = 4;</code>
+     */
+    private $ContentInfos;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int $TotalCount
+     *          结果总数
+     *     @type int $PageNum
+     *          页码
+     *     @type int $PageSize
+     *          页面大小
+     *     @type \Volc\Service\Vod\Models\Business\VodContentInfo[]|\Google\Protobuf\Internal\RepeatedField $ContentInfos
+     *          任务列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCdn::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *结果总数
+     *
+     * Generated from protobuf field <code>int32 TotalCount = 1;</code>
+     * @return int
+     */
+    public function getTotalCount()
+    {
+        return $this->TotalCount;
+    }
+
+    /**
+     *结果总数
+     *
+     * Generated from protobuf field <code>int32 TotalCount = 1;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setTotalCount($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->TotalCount = $var;
+
+        return $this;
+    }
+
+    /**
+     *页码
+     *
+     * Generated from protobuf field <code>int32 PageNum = 2;</code>
+     * @return int
+     */
+    public function getPageNum()
+    {
+        return $this->PageNum;
+    }
+
+    /**
+     *页码
+     *
+     * Generated from protobuf field <code>int32 PageNum = 2;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setPageNum($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->PageNum = $var;
+
+        return $this;
+    }
+
+    /**
+     *页面大小
+     *
+     * Generated from protobuf field <code>int32 PageSize = 3;</code>
+     * @return int
+     */
+    public function getPageSize()
+    {
+        return $this->PageSize;
+    }
+
+    /**
+     *页面大小
+     *
+     * Generated from protobuf field <code>int32 PageSize = 3;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setPageSize($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->PageSize = $var;
+
+        return $this;
+    }
+
+    /**
+     *任务列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodContentInfo ContentInfos = 4;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getContentInfos()
+    {
+        return $this->ContentInfos;
+    }
+
+    /**
+     *任务列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodContentInfo ContentInfos = 4;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodContentInfo[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setContentInfos($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodContentInfo::class);
+        $this->ContentInfos = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnTopAccessUrlElement.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnTopAccessUrlElement.php
new file mode 100755
index 0000000..1ee3094
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCdnTopAccessUrlElement.php
@@ -0,0 +1,133 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_cdn.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodCdnTopAccessUrlElement</code>
+ */
+class VodCdnTopAccessUrlElement extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * URL
+     *
+     * Generated from protobuf field <code>string Url = 1;</code>
+     */
+    protected $Url = '';
+    /**
+     * 请求次数计数
+     *
+     * Generated from protobuf field <code>int64 Pv = 2;</code>
+     */
+    protected $Pv = 0;
+    /**
+     * 流量计数,单位:Byte
+     *
+     * Generated from protobuf field <code>int64 Flux = 3;</code>
+     */
+    protected $Flux = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Url
+     *           URL
+     *     @type int|string $Pv
+     *           请求次数计数
+     *     @type int|string $Flux
+     *           流量计数,单位:Byte
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCdn::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * URL
+     *
+     * Generated from protobuf field <code>string Url = 1;</code>
+     * @return string
+     */
+    public function getUrl()
+    {
+        return $this->Url;
+    }
+
+    /**
+     * URL
+     *
+     * Generated from protobuf field <code>string Url = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setUrl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Url = $var;
+
+        return $this;
+    }
+
+    /**
+     * 请求次数计数
+     *
+     * Generated from protobuf field <code>int64 Pv = 2;</code>
+     * @return int|string
+     */
+    public function getPv()
+    {
+        return $this->Pv;
+    }
+
+    /**
+     * 请求次数计数
+     *
+     * Generated from protobuf field <code>int64 Pv = 2;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setPv($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->Pv = $var;
+
+        return $this;
+    }
+
+    /**
+     * 流量计数,单位:Byte
+     *
+     * Generated from protobuf field <code>int64 Flux = 3;</code>
+     * @return int|string
+     */
+    public function getFlux()
+    {
+        return $this->Flux;
+    }
+
+    /**
+     * 流量计数,单位:Byte
+     *
+     * Generated from protobuf field <code>int64 Flux = 3;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setFlux($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->Flux = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodClassification.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodClassification.php
new file mode 100755
index 0000000..944247e
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodClassification.php
@@ -0,0 +1,257 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodClassification</code>
+ */
+class VodClassification extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     * Generated from protobuf field <code>int64 ClassificationId = 2;</code>
+     */
+    protected $ClassificationId = 0;
+    /**
+     * Generated from protobuf field <code>int32 Level = 3;</code>
+     */
+    protected $Level = 0;
+    /**
+     * Generated from protobuf field <code>string Classification = 4;</code>
+     */
+    protected $Classification = '';
+    /**
+     * Generated from protobuf field <code>int64 ParentClassificationId = 5;</code>
+     */
+    protected $ParentClassificationId = 0;
+    /**
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodClassification SubClassification = 6;</code>
+     */
+    protected $SubClassification = null;
+    /**
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodClassification SubClassificationTrees = 7;</code>
+     */
+    private $SubClassificationTrees;
+    /**
+     * Generated from protobuf field <code>string CreatedAt = 8;</code>
+     */
+    protected $CreatedAt = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *     @type int|string $ClassificationId
+     *     @type int $Level
+     *     @type string $Classification
+     *     @type int|string $ParentClassificationId
+     *     @type \Volc\Service\Vod\Models\Business\VodClassification $SubClassification
+     *     @type \Volc\Service\Vod\Models\Business\VodClassification[]|\Google\Protobuf\Internal\RepeatedField $SubClassificationTrees
+     *     @type string $CreatedAt
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>int64 ClassificationId = 2;</code>
+     * @return int|string
+     */
+    public function getClassificationId()
+    {
+        return $this->ClassificationId;
+    }
+
+    /**
+     * Generated from protobuf field <code>int64 ClassificationId = 2;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setClassificationId($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->ClassificationId = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 Level = 3;</code>
+     * @return int
+     */
+    public function getLevel()
+    {
+        return $this->Level;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 Level = 3;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setLevel($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Level = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string Classification = 4;</code>
+     * @return string
+     */
+    public function getClassification()
+    {
+        return $this->Classification;
+    }
+
+    /**
+     * Generated from protobuf field <code>string Classification = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setClassification($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Classification = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>int64 ParentClassificationId = 5;</code>
+     * @return int|string
+     */
+    public function getParentClassificationId()
+    {
+        return $this->ParentClassificationId;
+    }
+
+    /**
+     * Generated from protobuf field <code>int64 ParentClassificationId = 5;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setParentClassificationId($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->ParentClassificationId = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodClassification SubClassification = 6;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodClassification|null
+     */
+    public function getSubClassification()
+    {
+        return $this->SubClassification;
+    }
+
+    public function hasSubClassification()
+    {
+        return isset($this->SubClassification);
+    }
+
+    public function clearSubClassification()
+    {
+        unset($this->SubClassification);
+    }
+
+    /**
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodClassification SubClassification = 6;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodClassification $var
+     * @return $this
+     */
+    public function setSubClassification($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodClassification::class);
+        $this->SubClassification = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodClassification SubClassificationTrees = 7;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getSubClassificationTrees()
+    {
+        return $this->SubClassificationTrees;
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodClassification SubClassificationTrees = 7;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodClassification[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setSubClassificationTrees($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodClassification::class);
+        $this->SubClassificationTrees = $arr;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string CreatedAt = 8;</code>
+     * @return string
+     */
+    public function getCreatedAt()
+    {
+        return $this->CreatedAt;
+    }
+
+    /**
+     * Generated from protobuf field <code>string CreatedAt = 8;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCreatedAt($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CreatedAt = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCommitData.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCommitData.php
new file mode 100755
index 0000000..72ba338
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCommitData.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_upload.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodCommitData</code>
+ */
+class VodCommitData extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 视频信息上报结果 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCommitUploadInfoResponseData Data = 1;</code>
+     */
+    protected $Data = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\VodCommitUploadInfoResponseData $Data
+     *           视频信息上报结果 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodUpload::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 视频信息上报结果 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCommitUploadInfoResponseData Data = 1;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodCommitUploadInfoResponseData|null
+     */
+    public function getData()
+    {
+        return $this->Data;
+    }
+
+    public function hasData()
+    {
+        return isset($this->Data);
+    }
+
+    public function clearData()
+    {
+        unset($this->Data);
+    }
+
+    /**
+     * 视频信息上报结果 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCommitUploadInfoResponseData Data = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodCommitUploadInfoResponseData $var
+     * @return $this
+     */
+    public function setData($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodCommitUploadInfoResponseData::class);
+        $this->Data = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCommitUploadInfoData.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCommitUploadInfoData.php
new file mode 100755
index 0000000..12bd303
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCommitUploadInfoData.php
@@ -0,0 +1,177 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_upload.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodCommitUploadInfoData</code>
+ */
+class VodCommitUploadInfoData extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 视频 id 
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     */
+    protected $Vid = '';
+    /**
+     * 视频对应的封面图 URI id 
+     *
+     * Generated from protobuf field <code>string PosterUri = 2;</code>
+     */
+    protected $PosterUri = '';
+    /**
+     * 视频元信息 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodSourceInfo SourceInfo = 3;</code>
+     */
+    protected $SourceInfo = null;
+    /**
+     * 素材 id 
+     *
+     * Generated from protobuf field <code>string Mid = 4;</code>
+     */
+    protected $Mid = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vid
+     *           视频 id 
+     *     @type string $PosterUri
+     *           视频对应的封面图 URI id 
+     *     @type \Volc\Service\Vod\Models\Business\VodSourceInfo $SourceInfo
+     *           视频元信息 
+     *     @type string $Mid
+     *           素材 id 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodUpload::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 视频 id 
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     * 视频 id 
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     * 视频对应的封面图 URI id 
+     *
+     * Generated from protobuf field <code>string PosterUri = 2;</code>
+     * @return string
+     */
+    public function getPosterUri()
+    {
+        return $this->PosterUri;
+    }
+
+    /**
+     * 视频对应的封面图 URI id 
+     *
+     * Generated from protobuf field <code>string PosterUri = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setPosterUri($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->PosterUri = $var;
+
+        return $this;
+    }
+
+    /**
+     * 视频元信息 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodSourceInfo SourceInfo = 3;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodSourceInfo|null
+     */
+    public function getSourceInfo()
+    {
+        return $this->SourceInfo;
+    }
+
+    public function hasSourceInfo()
+    {
+        return isset($this->SourceInfo);
+    }
+
+    public function clearSourceInfo()
+    {
+        unset($this->SourceInfo);
+    }
+
+    /**
+     * 视频元信息 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodSourceInfo SourceInfo = 3;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodSourceInfo $var
+     * @return $this
+     */
+    public function setSourceInfo($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodSourceInfo::class);
+        $this->SourceInfo = $var;
+
+        return $this;
+    }
+
+    /**
+     * 素材 id 
+     *
+     * Generated from protobuf field <code>string Mid = 4;</code>
+     * @return string
+     */
+    public function getMid()
+    {
+        return $this->Mid;
+    }
+
+    /**
+     * 素材 id 
+     *
+     * Generated from protobuf field <code>string Mid = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setMid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Mid = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCommitUploadInfoResponseData.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCommitUploadInfoResponseData.php
new file mode 100755
index 0000000..85a1037
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCommitUploadInfoResponseData.php
@@ -0,0 +1,211 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_upload.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodCommitUploadInfoResponseData</code>
+ */
+class VodCommitUploadInfoResponseData extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 视频的 id 
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     */
+    protected $Vid = '';
+    /**
+     * 视频的元信息 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodSourceInfo SourceInfo = 2;</code>
+     */
+    protected $SourceInfo = null;
+    /**
+     * 视频的封面图 URI 
+     *
+     * Generated from protobuf field <code>string PosterUri = 3;</code>
+     */
+    protected $PosterUri = '';
+    /**
+     * 视频透传业务的参数 
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 4;</code>
+     */
+    protected $CallbackArgs = '';
+    /**
+     * 素材 id 
+     *
+     * Generated from protobuf field <code>string Mid = 5;</code>
+     */
+    protected $Mid = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vid
+     *           视频的 id 
+     *     @type \Volc\Service\Vod\Models\Business\VodSourceInfo $SourceInfo
+     *           视频的元信息 
+     *     @type string $PosterUri
+     *           视频的封面图 URI 
+     *     @type string $CallbackArgs
+     *           视频透传业务的参数 
+     *     @type string $Mid
+     *           素材 id 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodUpload::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 视频的 id 
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     * 视频的 id 
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     * 视频的元信息 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodSourceInfo SourceInfo = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodSourceInfo|null
+     */
+    public function getSourceInfo()
+    {
+        return $this->SourceInfo;
+    }
+
+    public function hasSourceInfo()
+    {
+        return isset($this->SourceInfo);
+    }
+
+    public function clearSourceInfo()
+    {
+        unset($this->SourceInfo);
+    }
+
+    /**
+     * 视频的元信息 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodSourceInfo SourceInfo = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodSourceInfo $var
+     * @return $this
+     */
+    public function setSourceInfo($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodSourceInfo::class);
+        $this->SourceInfo = $var;
+
+        return $this;
+    }
+
+    /**
+     * 视频的封面图 URI 
+     *
+     * Generated from protobuf field <code>string PosterUri = 3;</code>
+     * @return string
+     */
+    public function getPosterUri()
+    {
+        return $this->PosterUri;
+    }
+
+    /**
+     * 视频的封面图 URI 
+     *
+     * Generated from protobuf field <code>string PosterUri = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setPosterUri($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->PosterUri = $var;
+
+        return $this;
+    }
+
+    /**
+     * 视频透传业务的参数 
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 4;</code>
+     * @return string
+     */
+    public function getCallbackArgs()
+    {
+        return $this->CallbackArgs;
+    }
+
+    /**
+     * 视频透传业务的参数 
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCallbackArgs($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CallbackArgs = $var;
+
+        return $this;
+    }
+
+    /**
+     * 素材 id 
+     *
+     * Generated from protobuf field <code>string Mid = 5;</code>
+     * @return string
+     */
+    public function getMid()
+    {
+        return $this->Mid;
+    }
+
+    /**
+     * 素材 id 
+     *
+     * Generated from protobuf field <code>string Mid = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setMid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Mid = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCommitUploadInfoResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCommitUploadInfoResult.php
new file mode 100755
index 0000000..76fcd6d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCommitUploadInfoResult.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_upload.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodCommitUploadInfoResult</code>
+ */
+class VodCommitUploadInfoResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 1005 阶段返回信息 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCommitUploadInfoData Data = 1;</code>
+     */
+    protected $Data = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\VodCommitUploadInfoData $Data
+     *           1005 阶段返回信息 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodUpload::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 1005 阶段返回信息 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCommitUploadInfoData Data = 1;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodCommitUploadInfoData|null
+     */
+    public function getData()
+    {
+        return $this->Data;
+    }
+
+    public function hasData()
+    {
+        return isset($this->Data);
+    }
+
+    public function clearData()
+    {
+        unset($this->Data);
+    }
+
+    /**
+     * 1005 阶段返回信息 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCommitUploadInfoData Data = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodCommitUploadInfoData $var
+     * @return $this
+     */
+    public function setData($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodCommitUploadInfoData::class);
+        $this->Data = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCommonConfigInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCommonConfigInfo.php
new file mode 100755
index 0000000..52297d2
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCommonConfigInfo.php
@@ -0,0 +1,133 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_common.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodCommonConfigInfo</code>
+ */
+class VodCommonConfigInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 配置模块类型
+     *
+     * Generated from protobuf field <code>string Module = 1;</code>
+     */
+    protected $Module = '';
+    /**
+     * 配置Key
+     *
+     * Generated from protobuf field <code>string Key = 2;</code>
+     */
+    protected $Key = '';
+    /**
+     * 配置Value
+     *
+     * Generated from protobuf field <code>string Value = 3;</code>
+     */
+    protected $Value = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Module
+     *           配置模块类型
+     *     @type string $Key
+     *           配置Key
+     *     @type string $Value
+     *           配置Value
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCommon::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 配置模块类型
+     *
+     * Generated from protobuf field <code>string Module = 1;</code>
+     * @return string
+     */
+    public function getModule()
+    {
+        return $this->Module;
+    }
+
+    /**
+     * 配置模块类型
+     *
+     * Generated from protobuf field <code>string Module = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setModule($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Module = $var;
+
+        return $this;
+    }
+
+    /**
+     * 配置Key
+     *
+     * Generated from protobuf field <code>string Key = 2;</code>
+     * @return string
+     */
+    public function getKey()
+    {
+        return $this->Key;
+    }
+
+    /**
+     * 配置Key
+     *
+     * Generated from protobuf field <code>string Key = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setKey($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Key = $var;
+
+        return $this;
+    }
+
+    /**
+     * 配置Value
+     *
+     * Generated from protobuf field <code>string Value = 3;</code>
+     * @return string
+     */
+    public function getValue()
+    {
+        return $this->Value;
+    }
+
+    /**
+     * 配置Value
+     *
+     * Generated from protobuf field <code>string Value = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setValue($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Value = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodContentInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodContentInfo.php
new file mode 100755
index 0000000..55e2af2
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodContentInfo.php
@@ -0,0 +1,235 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_cdn.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodContentInfo</code>
+ */
+class VodContentInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *记录ID,可为空
+     *
+     * Generated from protobuf field <code>string ItemId = 1;</code>
+     */
+    protected $ItemId = '';
+    /**
+     *任务URL
+     *
+     * Generated from protobuf field <code>string Url = 2;</code>
+     */
+    protected $Url = '';
+    /**
+     *任务状态
+     *
+     * Generated from protobuf field <code>string Status = 3;</code>
+     */
+    protected $Status = '';
+    /**
+     *任务类型
+     *
+     * Generated from protobuf field <code>string TaskType = 4;</code>
+     */
+    protected $TaskType = '';
+    /**
+     *任务创建时间,单位:Unix秒级时间戳
+     *
+     * Generated from protobuf field <code>int32 CreateTimestamp = 5;</code>
+     */
+    protected $CreateTimestamp = 0;
+    /**
+     *任务ID
+     *
+     * Generated from protobuf field <code>string TaskId = 6;</code>
+     */
+    protected $TaskId = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $ItemId
+     *          记录ID,可为空
+     *     @type string $Url
+     *          任务URL
+     *     @type string $Status
+     *          任务状态
+     *     @type string $TaskType
+     *          任务类型
+     *     @type int $CreateTimestamp
+     *          任务创建时间,单位:Unix秒级时间戳
+     *     @type string $TaskId
+     *          任务ID
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCdn::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *记录ID,可为空
+     *
+     * Generated from protobuf field <code>string ItemId = 1;</code>
+     * @return string
+     */
+    public function getItemId()
+    {
+        return $this->ItemId;
+    }
+
+    /**
+     *记录ID,可为空
+     *
+     * Generated from protobuf field <code>string ItemId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setItemId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->ItemId = $var;
+
+        return $this;
+    }
+
+    /**
+     *任务URL
+     *
+     * Generated from protobuf field <code>string Url = 2;</code>
+     * @return string
+     */
+    public function getUrl()
+    {
+        return $this->Url;
+    }
+
+    /**
+     *任务URL
+     *
+     * Generated from protobuf field <code>string Url = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setUrl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Url = $var;
+
+        return $this;
+    }
+
+    /**
+     *任务状态
+     *
+     * Generated from protobuf field <code>string Status = 3;</code>
+     * @return string
+     */
+    public function getStatus()
+    {
+        return $this->Status;
+    }
+
+    /**
+     *任务状态
+     *
+     * Generated from protobuf field <code>string Status = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStatus($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Status = $var;
+
+        return $this;
+    }
+
+    /**
+     *任务类型
+     *
+     * Generated from protobuf field <code>string TaskType = 4;</code>
+     * @return string
+     */
+    public function getTaskType()
+    {
+        return $this->TaskType;
+    }
+
+    /**
+     *任务类型
+     *
+     * Generated from protobuf field <code>string TaskType = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTaskType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TaskType = $var;
+
+        return $this;
+    }
+
+    /**
+     *任务创建时间,单位:Unix秒级时间戳
+     *
+     * Generated from protobuf field <code>int32 CreateTimestamp = 5;</code>
+     * @return int
+     */
+    public function getCreateTimestamp()
+    {
+        return $this->CreateTimestamp;
+    }
+
+    /**
+     *任务创建时间,单位:Unix秒级时间戳
+     *
+     * Generated from protobuf field <code>int32 CreateTimestamp = 5;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setCreateTimestamp($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->CreateTimestamp = $var;
+
+        return $this;
+    }
+
+    /**
+     *任务ID
+     *
+     * Generated from protobuf field <code>string TaskId = 6;</code>
+     * @return string
+     */
+    public function getTaskId()
+    {
+        return $this->TaskId;
+    }
+
+    /**
+     *任务ID
+     *
+     * Generated from protobuf field <code>string TaskId = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTaskId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TaskId = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCreateCdnTaskResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCreateCdnTaskResult.php
new file mode 100755
index 0000000..c4c23a0
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCreateCdnTaskResult.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_cdn.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodCreateCdnTaskResult</code>
+ */
+class VodCreateCdnTaskResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 任务ID
+     *
+     * Generated from protobuf field <code>string TaskId = 1;</code>
+     */
+    protected $TaskId = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $TaskId
+     *           任务ID
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCdn::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 任务ID
+     *
+     * Generated from protobuf field <code>string TaskId = 1;</code>
+     * @return string
+     */
+    public function getTaskId()
+    {
+        return $this->TaskId;
+    }
+
+    /**
+     * 任务ID
+     *
+     * Generated from protobuf field <code>string TaskId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTaskId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TaskId = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCreateVideoClassificationData.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCreateVideoClassificationData.php
new file mode 100755
index 0000000..3a32849
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodCreateVideoClassificationData.php
@@ -0,0 +1,58 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodCreateVideoClassificationData</code>
+ */
+class VodCreateVideoClassificationData extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>int64 ClassificationId = 1;</code>
+     */
+    protected $ClassificationId = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int|string $ClassificationId
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>int64 ClassificationId = 1;</code>
+     * @return int|string
+     */
+    public function getClassificationId()
+    {
+        return $this->ClassificationId;
+    }
+
+    /**
+     * Generated from protobuf field <code>int64 ClassificationId = 1;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setClassificationId($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->ClassificationId = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDeleteMediaData.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDeleteMediaData.php
new file mode 100755
index 0000000..9a73568
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDeleteMediaData.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodDeleteMediaData</code>
+ */
+class VodDeleteMediaData extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *不存在的视频VID列表
+     *
+     * Generated from protobuf field <code>repeated string NotExistVids = 1;</code>
+     */
+    private $NotExistVids;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $NotExistVids
+     *          不存在的视频VID列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *不存在的视频VID列表
+     *
+     * Generated from protobuf field <code>repeated string NotExistVids = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getNotExistVids()
+    {
+        return $this->NotExistVids;
+    }
+
+    /**
+     *不存在的视频VID列表
+     *
+     * Generated from protobuf field <code>repeated string NotExistVids = 1;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setNotExistVids($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->NotExistVids = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDeleteTranscodesData.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDeleteTranscodesData.php
new file mode 100755
index 0000000..70d711b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDeleteTranscodesData.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodDeleteTranscodesData</code>
+ */
+class VodDeleteTranscodesData extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *不存在的转码FileID列表
+     *
+     * Generated from protobuf field <code>repeated string NotExistFileIds = 1;</code>
+     */
+    private $NotExistFileIds;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $NotExistFileIds
+     *          不存在的转码FileID列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *不存在的转码FileID列表
+     *
+     * Generated from protobuf field <code>repeated string NotExistFileIds = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getNotExistFileIds()
+    {
+        return $this->NotExistFileIds;
+    }
+
+    /**
+     *不存在的转码FileID列表
+     *
+     * Generated from protobuf field <code>repeated string NotExistFileIds = 1;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setNotExistFileIds($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->NotExistFileIds = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDescribeIpInfoResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDescribeIpInfoResult.php
new file mode 100755
index 0000000..83f89a7
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDescribeIpInfoResult.php
@@ -0,0 +1,58 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_cdn.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodDescribeIpInfoResult</code>
+ */
+class VodDescribeIpInfoResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodCdnIpInfo Infos = 1;</code>
+     */
+    private $Infos;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\VodCdnIpInfo[]|\Google\Protobuf\Internal\RepeatedField $Infos
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCdn::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodCdnIpInfo Infos = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getInfos()
+    {
+        return $this->Infos;
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodCdnIpInfo Infos = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodCdnIpInfo[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setInfos($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodCdnIpInfo::class);
+        $this->Infos = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDescribeVodDomainBandwidthDataResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDescribeVodDomainBandwidthDataResult.php
new file mode 100755
index 0000000..ffcba09
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDescribeVodDomainBandwidthDataResult.php
@@ -0,0 +1,303 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_cdn.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodDescribeVodDomainBandwidthDataResult</code>
+ */
+class VodDescribeVodDomainBandwidthDataResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 域名列表
+     *
+     * Generated from protobuf field <code>repeated string DomainList = 1;</code>
+     */
+    private $DomainList;
+    /**
+     * 查询的起始时间
+     *
+     * Generated from protobuf field <code>string StartTime = 2;</code>
+     */
+    protected $StartTime = '';
+    /**
+     * 查询的结束时间
+     *
+     * Generated from protobuf field <code>string EndTime = 3;</code>
+     */
+    protected $EndTime = '';
+    /**
+     * 聚合的时间粒度
+     *
+     * Generated from protobuf field <code>int32 Aggregation = 4;</code>
+     */
+    protected $Aggregation = 0;
+    /**
+     * 默认是CDN,支持CDN、PCDN、ALL
+     *
+     * Generated from protobuf field <code>string BandwidthType = 5;</code>
+     */
+    protected $BandwidthType = '';
+    /**
+     * 带宽峰值,单位为Mbps
+     *
+     * Generated from protobuf field <code>double PeakBandwidth = 6;</code>
+     */
+    protected $PeakBandwidth = 0.0;
+    /**
+     * 带宽峰值出现的时间
+     *
+     * Generated from protobuf field <code>string PeakBandwidthTime = 7;</code>
+     */
+    protected $PeakBandwidthTime = '';
+    /**
+     * 所有时间粒度数据
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodBandwidthData BandwidthDataList = 8;</code>
+     */
+    private $BandwidthDataList;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $DomainList
+     *           域名列表
+     *     @type string $StartTime
+     *           查询的起始时间
+     *     @type string $EndTime
+     *           查询的结束时间
+     *     @type int $Aggregation
+     *           聚合的时间粒度
+     *     @type string $BandwidthType
+     *           默认是CDN,支持CDN、PCDN、ALL
+     *     @type float $PeakBandwidth
+     *           带宽峰值,单位为Mbps
+     *     @type string $PeakBandwidthTime
+     *           带宽峰值出现的时间
+     *     @type \Volc\Service\Vod\Models\Business\VodBandwidthData[]|\Google\Protobuf\Internal\RepeatedField $BandwidthDataList
+     *           所有时间粒度数据
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCdn::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 域名列表
+     *
+     * Generated from protobuf field <code>repeated string DomainList = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getDomainList()
+    {
+        return $this->DomainList;
+    }
+
+    /**
+     * 域名列表
+     *
+     * Generated from protobuf field <code>repeated string DomainList = 1;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setDomainList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->DomainList = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 查询的起始时间
+     *
+     * Generated from protobuf field <code>string StartTime = 2;</code>
+     * @return string
+     */
+    public function getStartTime()
+    {
+        return $this->StartTime;
+    }
+
+    /**
+     * 查询的起始时间
+     *
+     * Generated from protobuf field <code>string StartTime = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStartTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StartTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * 查询的结束时间
+     *
+     * Generated from protobuf field <code>string EndTime = 3;</code>
+     * @return string
+     */
+    public function getEndTime()
+    {
+        return $this->EndTime;
+    }
+
+    /**
+     * 查询的结束时间
+     *
+     * Generated from protobuf field <code>string EndTime = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setEndTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->EndTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * 聚合的时间粒度
+     *
+     * Generated from protobuf field <code>int32 Aggregation = 4;</code>
+     * @return int
+     */
+    public function getAggregation()
+    {
+        return $this->Aggregation;
+    }
+
+    /**
+     * 聚合的时间粒度
+     *
+     * Generated from protobuf field <code>int32 Aggregation = 4;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setAggregation($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Aggregation = $var;
+
+        return $this;
+    }
+
+    /**
+     * 默认是CDN,支持CDN、PCDN、ALL
+     *
+     * Generated from protobuf field <code>string BandwidthType = 5;</code>
+     * @return string
+     */
+    public function getBandwidthType()
+    {
+        return $this->BandwidthType;
+    }
+
+    /**
+     * 默认是CDN,支持CDN、PCDN、ALL
+     *
+     * Generated from protobuf field <code>string BandwidthType = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setBandwidthType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->BandwidthType = $var;
+
+        return $this;
+    }
+
+    /**
+     * 带宽峰值,单位为Mbps
+     *
+     * Generated from protobuf field <code>double PeakBandwidth = 6;</code>
+     * @return float
+     */
+    public function getPeakBandwidth()
+    {
+        return $this->PeakBandwidth;
+    }
+
+    /**
+     * 带宽峰值,单位为Mbps
+     *
+     * Generated from protobuf field <code>double PeakBandwidth = 6;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setPeakBandwidth($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->PeakBandwidth = $var;
+
+        return $this;
+    }
+
+    /**
+     * 带宽峰值出现的时间
+     *
+     * Generated from protobuf field <code>string PeakBandwidthTime = 7;</code>
+     * @return string
+     */
+    public function getPeakBandwidthTime()
+    {
+        return $this->PeakBandwidthTime;
+    }
+
+    /**
+     * 带宽峰值出现的时间
+     *
+     * Generated from protobuf field <code>string PeakBandwidthTime = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setPeakBandwidthTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->PeakBandwidthTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * 所有时间粒度数据
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodBandwidthData BandwidthDataList = 8;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getBandwidthDataList()
+    {
+        return $this->BandwidthDataList;
+    }
+
+    /**
+     * 所有时间粒度数据
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodBandwidthData BandwidthDataList = 8;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodBandwidthData[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setBandwidthDataList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodBandwidthData::class);
+        $this->BandwidthDataList = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDescribeVodDomainTrafficDataResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDescribeVodDomainTrafficDataResult.php
new file mode 100755
index 0000000..d64eebd
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDescribeVodDomainTrafficDataResult.php
@@ -0,0 +1,220 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_cdn.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodDescribeVodDomainTrafficDataResult</code>
+ */
+class VodDescribeVodDomainTrafficDataResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>repeated string DomainList = 1;</code>
+     */
+    private $DomainList;
+    /**
+     * Generated from protobuf field <code>string StartTime = 2;</code>
+     */
+    protected $StartTime = '';
+    /**
+     * Generated from protobuf field <code>string EndTime = 3;</code>
+     */
+    protected $EndTime = '';
+    /**
+     * Generated from protobuf field <code>int32 Aggregation = 4;</code>
+     */
+    protected $Aggregation = 0;
+    /**
+     * Generated from protobuf field <code>string TrafficType = 5;</code>
+     */
+    protected $TrafficType = '';
+    /**
+     * Generated from protobuf field <code>double TotalTraffic = 6;</code>
+     */
+    protected $TotalTraffic = 0.0;
+    /**
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodTrafficData TrafficDataList = 7;</code>
+     */
+    private $TrafficDataList;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $DomainList
+     *     @type string $StartTime
+     *     @type string $EndTime
+     *     @type int $Aggregation
+     *     @type string $TrafficType
+     *     @type float $TotalTraffic
+     *     @type \Volc\Service\Vod\Models\Business\VodTrafficData[]|\Google\Protobuf\Internal\RepeatedField $TrafficDataList
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCdn::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated string DomainList = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getDomainList()
+    {
+        return $this->DomainList;
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated string DomainList = 1;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setDomainList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->DomainList = $arr;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string StartTime = 2;</code>
+     * @return string
+     */
+    public function getStartTime()
+    {
+        return $this->StartTime;
+    }
+
+    /**
+     * Generated from protobuf field <code>string StartTime = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStartTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StartTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string EndTime = 3;</code>
+     * @return string
+     */
+    public function getEndTime()
+    {
+        return $this->EndTime;
+    }
+
+    /**
+     * Generated from protobuf field <code>string EndTime = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setEndTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->EndTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 Aggregation = 4;</code>
+     * @return int
+     */
+    public function getAggregation()
+    {
+        return $this->Aggregation;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 Aggregation = 4;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setAggregation($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Aggregation = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string TrafficType = 5;</code>
+     * @return string
+     */
+    public function getTrafficType()
+    {
+        return $this->TrafficType;
+    }
+
+    /**
+     * Generated from protobuf field <code>string TrafficType = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTrafficType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TrafficType = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>double TotalTraffic = 6;</code>
+     * @return float
+     */
+    public function getTotalTraffic()
+    {
+        return $this->TotalTraffic;
+    }
+
+    /**
+     * Generated from protobuf field <code>double TotalTraffic = 6;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setTotalTraffic($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->TotalTraffic = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodTrafficData TrafficDataList = 7;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getTrafficDataList()
+    {
+        return $this->TrafficDataList;
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodTrafficData TrafficDataList = 7;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodTrafficData[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setTrafficDataList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodTrafficData::class);
+        $this->TrafficDataList = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDescribeVodSpaceStorageDataResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDescribeVodSpaceStorageDataResult.php
new file mode 100755
index 0000000..162fe5e
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDescribeVodSpaceStorageDataResult.php
@@ -0,0 +1,269 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_space.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodDescribeVodSpaceStorageDataResult</code>
+ */
+class VodDescribeVodSpaceStorageDataResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Space Name list
+     *
+     * Generated from protobuf field <code>repeated string SpaceList = 1;</code>
+     */
+    private $SpaceList;
+    /**
+     * start time of query
+     *
+     * Generated from protobuf field <code>string StartTime = 2;</code>
+     */
+    protected $StartTime = '';
+    /**
+     * End time of query
+     *
+     * Generated from protobuf field <code>string EndTime = 3;</code>
+     */
+    protected $EndTime = '';
+    /**
+     * Time granularity of aggregation
+     *
+     * Generated from protobuf field <code>int32 Aggregation = 4;</code>
+     */
+    protected $Aggregation = 0;
+    /**
+     * hot, lowfreq, punish
+     *
+     * Generated from protobuf field <code>string Type = 5;</code>
+     */
+    protected $Type = '';
+    /**
+     * Byte of latest storage
+     *
+     * Generated from protobuf field <code>int64 LatestStorageData = 6;</code>
+     */
+    protected $LatestStorageData = 0;
+    /**
+     * list of Storage Data
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodStorageData StorageDataList = 7;</code>
+     */
+    private $StorageDataList;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $SpaceList
+     *           Space Name list
+     *     @type string $StartTime
+     *           start time of query
+     *     @type string $EndTime
+     *           End time of query
+     *     @type int $Aggregation
+     *           Time granularity of aggregation
+     *     @type string $Type
+     *           hot, lowfreq, punish
+     *     @type int|string $LatestStorageData
+     *           Byte of latest storage
+     *     @type \Volc\Service\Vod\Models\Business\VodStorageData[]|\Google\Protobuf\Internal\RepeatedField $StorageDataList
+     *           list of Storage Data
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodSpace::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Space Name list
+     *
+     * Generated from protobuf field <code>repeated string SpaceList = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getSpaceList()
+    {
+        return $this->SpaceList;
+    }
+
+    /**
+     * Space Name list
+     *
+     * Generated from protobuf field <code>repeated string SpaceList = 1;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setSpaceList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->SpaceList = $arr;
+
+        return $this;
+    }
+
+    /**
+     * start time of query
+     *
+     * Generated from protobuf field <code>string StartTime = 2;</code>
+     * @return string
+     */
+    public function getStartTime()
+    {
+        return $this->StartTime;
+    }
+
+    /**
+     * start time of query
+     *
+     * Generated from protobuf field <code>string StartTime = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStartTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StartTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * End time of query
+     *
+     * Generated from protobuf field <code>string EndTime = 3;</code>
+     * @return string
+     */
+    public function getEndTime()
+    {
+        return $this->EndTime;
+    }
+
+    /**
+     * End time of query
+     *
+     * Generated from protobuf field <code>string EndTime = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setEndTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->EndTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * Time granularity of aggregation
+     *
+     * Generated from protobuf field <code>int32 Aggregation = 4;</code>
+     * @return int
+     */
+    public function getAggregation()
+    {
+        return $this->Aggregation;
+    }
+
+    /**
+     * Time granularity of aggregation
+     *
+     * Generated from protobuf field <code>int32 Aggregation = 4;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setAggregation($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Aggregation = $var;
+
+        return $this;
+    }
+
+    /**
+     * hot, lowfreq, punish
+     *
+     * Generated from protobuf field <code>string Type = 5;</code>
+     * @return string
+     */
+    public function getType()
+    {
+        return $this->Type;
+    }
+
+    /**
+     * hot, lowfreq, punish
+     *
+     * Generated from protobuf field <code>string Type = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Type = $var;
+
+        return $this;
+    }
+
+    /**
+     * Byte of latest storage
+     *
+     * Generated from protobuf field <code>int64 LatestStorageData = 6;</code>
+     * @return int|string
+     */
+    public function getLatestStorageData()
+    {
+        return $this->LatestStorageData;
+    }
+
+    /**
+     * Byte of latest storage
+     *
+     * Generated from protobuf field <code>int64 LatestStorageData = 6;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setLatestStorageData($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->LatestStorageData = $var;
+
+        return $this;
+    }
+
+    /**
+     * list of Storage Data
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodStorageData StorageDataList = 7;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getStorageDataList()
+    {
+        return $this->StorageDataList;
+    }
+
+    /**
+     * list of Storage Data
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodStorageData StorageDataList = 7;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodStorageData[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setStorageDataList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodStorageData::class);
+        $this->StorageDataList = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDomainCertificateInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDomainCertificateInfo.php
new file mode 100755
index 0000000..cc03b25
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDomainCertificateInfo.php
@@ -0,0 +1,235 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_cdn.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodDomainCertificateInfo</code>
+ */
+class VodDomainCertificateInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *证书ID
+     *
+     * Generated from protobuf field <code>string CertificateId = 1;</code>
+     */
+    protected $CertificateId = '';
+    /**
+     *证书名
+     *
+     * Generated from protobuf field <code>string CertificateName = 2;</code>
+     */
+    protected $CertificateName = '';
+    /**
+     *证书内容
+     *
+     * Generated from protobuf field <code>string CertificatePub = 3;</code>
+     */
+    protected $CertificatePub = '';
+    /**
+     *私钥
+     *
+     * Generated from protobuf field <code>string CertificatePri = 4;</code>
+     */
+    protected $CertificatePri = '';
+    /**
+     * Http开启状态
+     *
+     * Generated from protobuf field <code>string HttpsStatus = 5;</code>
+     */
+    protected $HttpsStatus = '';
+    /**
+     * 证书过期时间,格式为"yyyy-MM-dd HH:mm:ss"
+     *
+     * Generated from protobuf field <code>string ExpiredAt = 6;</code>
+     */
+    protected $ExpiredAt = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $CertificateId
+     *          证书ID
+     *     @type string $CertificateName
+     *          证书名
+     *     @type string $CertificatePub
+     *          证书内容
+     *     @type string $CertificatePri
+     *          私钥
+     *     @type string $HttpsStatus
+     *           Http开启状态
+     *     @type string $ExpiredAt
+     *           证书过期时间,格式为"yyyy-MM-dd HH:mm:ss"
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCdn::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *证书ID
+     *
+     * Generated from protobuf field <code>string CertificateId = 1;</code>
+     * @return string
+     */
+    public function getCertificateId()
+    {
+        return $this->CertificateId;
+    }
+
+    /**
+     *证书ID
+     *
+     * Generated from protobuf field <code>string CertificateId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCertificateId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CertificateId = $var;
+
+        return $this;
+    }
+
+    /**
+     *证书名
+     *
+     * Generated from protobuf field <code>string CertificateName = 2;</code>
+     * @return string
+     */
+    public function getCertificateName()
+    {
+        return $this->CertificateName;
+    }
+
+    /**
+     *证书名
+     *
+     * Generated from protobuf field <code>string CertificateName = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCertificateName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CertificateName = $var;
+
+        return $this;
+    }
+
+    /**
+     *证书内容
+     *
+     * Generated from protobuf field <code>string CertificatePub = 3;</code>
+     * @return string
+     */
+    public function getCertificatePub()
+    {
+        return $this->CertificatePub;
+    }
+
+    /**
+     *证书内容
+     *
+     * Generated from protobuf field <code>string CertificatePub = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCertificatePub($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CertificatePub = $var;
+
+        return $this;
+    }
+
+    /**
+     *私钥
+     *
+     * Generated from protobuf field <code>string CertificatePri = 4;</code>
+     * @return string
+     */
+    public function getCertificatePri()
+    {
+        return $this->CertificatePri;
+    }
+
+    /**
+     *私钥
+     *
+     * Generated from protobuf field <code>string CertificatePri = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCertificatePri($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CertificatePri = $var;
+
+        return $this;
+    }
+
+    /**
+     * Http开启状态
+     *
+     * Generated from protobuf field <code>string HttpsStatus = 5;</code>
+     * @return string
+     */
+    public function getHttpsStatus()
+    {
+        return $this->HttpsStatus;
+    }
+
+    /**
+     * Http开启状态
+     *
+     * Generated from protobuf field <code>string HttpsStatus = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setHttpsStatus($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->HttpsStatus = $var;
+
+        return $this;
+    }
+
+    /**
+     * 证书过期时间,格式为"yyyy-MM-dd HH:mm:ss"
+     *
+     * Generated from protobuf field <code>string ExpiredAt = 6;</code>
+     * @return string
+     */
+    public function getExpiredAt()
+    {
+        return $this->ExpiredAt;
+    }
+
+    /**
+     * 证书过期时间,格式为"yyyy-MM-dd HH:mm:ss"
+     *
+     * Generated from protobuf field <code>string ExpiredAt = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setExpiredAt($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->ExpiredAt = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDomainConfigInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDomainConfigInfo.php
new file mode 100755
index 0000000..9a6e9d7
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDomainConfigInfo.php
@@ -0,0 +1,153 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_cdn.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodDomainConfigInfo</code>
+ */
+class VodDomainConfigInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 空间名称
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     * 播放分发加速配置
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDomainInstanceInfos PlayInstanceInfo = 2;</code>
+     */
+    protected $PlayInstanceInfo = null;
+    /**
+     * 图片分发加速配置
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDomainInstanceInfos ImageInstanceInfo = 3;</code>
+     */
+    protected $ImageInstanceInfo = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *           空间名称
+     *     @type \Volc\Service\Vod\Models\Business\VodDomainInstanceInfos $PlayInstanceInfo
+     *           播放分发加速配置
+     *     @type \Volc\Service\Vod\Models\Business\VodDomainInstanceInfos $ImageInstanceInfo
+     *           图片分发加速配置
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCdn::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 空间名称
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * 空间名称
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 播放分发加速配置
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDomainInstanceInfos PlayInstanceInfo = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodDomainInstanceInfos|null
+     */
+    public function getPlayInstanceInfo()
+    {
+        return $this->PlayInstanceInfo;
+    }
+
+    public function hasPlayInstanceInfo()
+    {
+        return isset($this->PlayInstanceInfo);
+    }
+
+    public function clearPlayInstanceInfo()
+    {
+        unset($this->PlayInstanceInfo);
+    }
+
+    /**
+     * 播放分发加速配置
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDomainInstanceInfos PlayInstanceInfo = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodDomainInstanceInfos $var
+     * @return $this
+     */
+    public function setPlayInstanceInfo($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodDomainInstanceInfos::class);
+        $this->PlayInstanceInfo = $var;
+
+        return $this;
+    }
+
+    /**
+     * 图片分发加速配置
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDomainInstanceInfos ImageInstanceInfo = 3;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodDomainInstanceInfos|null
+     */
+    public function getImageInstanceInfo()
+    {
+        return $this->ImageInstanceInfo;
+    }
+
+    public function hasImageInstanceInfo()
+    {
+        return isset($this->ImageInstanceInfo);
+    }
+
+    public function clearImageInstanceInfo()
+    {
+        unset($this->ImageInstanceInfo);
+    }
+
+    /**
+     * 图片分发加速配置
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDomainInstanceInfos ImageInstanceInfo = 3;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodDomainInstanceInfos $var
+     * @return $this
+     */
+    public function setImageInstanceInfo($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodDomainInstanceInfos::class);
+        $this->ImageInstanceInfo = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDomainInstanceInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDomainInstanceInfo.php
new file mode 100755
index 0000000..d000af4
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDomainInstanceInfo.php
@@ -0,0 +1,167 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_cdn.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodDomainInstanceInfo</code>
+ */
+class VodDomainInstanceInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *实例ID
+     *
+     * Generated from protobuf field <code>string InstanceId = 1;</code>
+     */
+    protected $InstanceId = '';
+    /**
+     *域名列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodDomainoInfo Domains = 2;</code>
+     */
+    private $Domains;
+    /**
+     *是否可自助配置
+     *
+     * Generated from protobuf field <code>bool CanSelfEditing = 3;</code>
+     */
+    protected $CanSelfEditing = false;
+    /**
+     *实例配置状态
+     *
+     * Generated from protobuf field <code>string ConfigStatus = 4;</code>
+     */
+    protected $ConfigStatus = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $InstanceId
+     *          实例ID
+     *     @type \Volc\Service\Vod\Models\Business\VodDomainoInfo[]|\Google\Protobuf\Internal\RepeatedField $Domains
+     *          域名列表
+     *     @type bool $CanSelfEditing
+     *          是否可自助配置
+     *     @type string $ConfigStatus
+     *          实例配置状态
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCdn::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *实例ID
+     *
+     * Generated from protobuf field <code>string InstanceId = 1;</code>
+     * @return string
+     */
+    public function getInstanceId()
+    {
+        return $this->InstanceId;
+    }
+
+    /**
+     *实例ID
+     *
+     * Generated from protobuf field <code>string InstanceId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setInstanceId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->InstanceId = $var;
+
+        return $this;
+    }
+
+    /**
+     *域名列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodDomainoInfo Domains = 2;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getDomains()
+    {
+        return $this->Domains;
+    }
+
+    /**
+     *域名列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodDomainoInfo Domains = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodDomainoInfo[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setDomains($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodDomainoInfo::class);
+        $this->Domains = $arr;
+
+        return $this;
+    }
+
+    /**
+     *是否可自助配置
+     *
+     * Generated from protobuf field <code>bool CanSelfEditing = 3;</code>
+     * @return bool
+     */
+    public function getCanSelfEditing()
+    {
+        return $this->CanSelfEditing;
+    }
+
+    /**
+     *是否可自助配置
+     *
+     * Generated from protobuf field <code>bool CanSelfEditing = 3;</code>
+     * @param bool $var
+     * @return $this
+     */
+    public function setCanSelfEditing($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->CanSelfEditing = $var;
+
+        return $this;
+    }
+
+    /**
+     *实例配置状态
+     *
+     * Generated from protobuf field <code>string ConfigStatus = 4;</code>
+     * @return string
+     */
+    public function getConfigStatus()
+    {
+        return $this->ConfigStatus;
+    }
+
+    /**
+     *实例配置状态
+     *
+     * Generated from protobuf field <code>string ConfigStatus = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setConfigStatus($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->ConfigStatus = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDomainInstanceInfos.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDomainInstanceInfos.php
new file mode 100755
index 0000000..5dc5ace
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDomainInstanceInfos.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_cdn.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodDomainInstanceInfos</code>
+ */
+class VodDomainInstanceInfos extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 点播源站实例列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodDomainInstanceInfo ByteInstances = 1;</code>
+     */
+    private $ByteInstances;
+    /**
+     * 第三方源站实力列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodDomainInstanceInfo OtherInstances = 2;</code>
+     */
+    private $OtherInstances;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\VodDomainInstanceInfo[]|\Google\Protobuf\Internal\RepeatedField $ByteInstances
+     *           点播源站实例列表
+     *     @type \Volc\Service\Vod\Models\Business\VodDomainInstanceInfo[]|\Google\Protobuf\Internal\RepeatedField $OtherInstances
+     *           第三方源站实力列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCdn::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 点播源站实例列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodDomainInstanceInfo ByteInstances = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getByteInstances()
+    {
+        return $this->ByteInstances;
+    }
+
+    /**
+     * 点播源站实例列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodDomainInstanceInfo ByteInstances = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodDomainInstanceInfo[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setByteInstances($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodDomainInstanceInfo::class);
+        $this->ByteInstances = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 第三方源站实力列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodDomainInstanceInfo OtherInstances = 2;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getOtherInstances()
+    {
+        return $this->OtherInstances;
+    }
+
+    /**
+     * 第三方源站实力列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodDomainInstanceInfo OtherInstances = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodDomainInstanceInfo[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setOtherInstances($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodDomainInstanceInfo::class);
+        $this->OtherInstances = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDomainoInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDomainoInfo.php
new file mode 100755
index 0000000..4cd2a0f
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodDomainoInfo.php
@@ -0,0 +1,347 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_cdn.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodDomainoInfo</code>
+ */
+class VodDomainoInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *域名
+     *
+     * Generated from protobuf field <code>string Domain = 1;</code>
+     */
+    protected $Domain = '';
+    /**
+     * cname域名
+     *
+     * Generated from protobuf field <code>string Cname = 2;</code>
+     */
+    protected $Cname = '';
+    /**
+     *配置状态
+     *
+     * Generated from protobuf field <code>string ConfigStatus = 3;</code>
+     */
+    protected $ConfigStatus = '';
+    /**
+     *解析状态
+     *
+     * Generated from protobuf field <code>string CnameStatus = 4;</code>
+     */
+    protected $CnameStatus = '';
+    /**
+     *运行状态
+     *
+     * Generated from protobuf field <code>string Status = 5;</code>
+     */
+    protected $Status = '';
+    /**
+     *证书
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDomainCertificateInfo Certificate = 6;</code>
+     */
+    protected $Certificate = null;
+    /**
+     *创建时间
+     *
+     * Generated from protobuf field <code>string CreateTime = 7;</code>
+     */
+    protected $CreateTime = '';
+    /**
+     *更新时间
+     *
+     * Generated from protobuf field <code>string UpdateTime = 8;</code>
+     */
+    protected $UpdateTime = '';
+    /**
+     *加速区域,cn:国内,overseas:海外,global:全球
+     *
+     * Generated from protobuf field <code>string Region = 9;</code>
+     */
+    protected $Region = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Domain
+     *          域名
+     *     @type string $Cname
+     *           cname域名
+     *     @type string $ConfigStatus
+     *          配置状态
+     *     @type string $CnameStatus
+     *          解析状态
+     *     @type string $Status
+     *          运行状态
+     *     @type \Volc\Service\Vod\Models\Business\VodDomainCertificateInfo $Certificate
+     *          证书
+     *     @type string $CreateTime
+     *          创建时间
+     *     @type string $UpdateTime
+     *          更新时间
+     *     @type string $Region
+     *          加速区域,cn:国内,overseas:海外,global:全球
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCdn::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *域名
+     *
+     * Generated from protobuf field <code>string Domain = 1;</code>
+     * @return string
+     */
+    public function getDomain()
+    {
+        return $this->Domain;
+    }
+
+    /**
+     *域名
+     *
+     * Generated from protobuf field <code>string Domain = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomain($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Domain = $var;
+
+        return $this;
+    }
+
+    /**
+     * cname域名
+     *
+     * Generated from protobuf field <code>string Cname = 2;</code>
+     * @return string
+     */
+    public function getCname()
+    {
+        return $this->Cname;
+    }
+
+    /**
+     * cname域名
+     *
+     * Generated from protobuf field <code>string Cname = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCname($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Cname = $var;
+
+        return $this;
+    }
+
+    /**
+     *配置状态
+     *
+     * Generated from protobuf field <code>string ConfigStatus = 3;</code>
+     * @return string
+     */
+    public function getConfigStatus()
+    {
+        return $this->ConfigStatus;
+    }
+
+    /**
+     *配置状态
+     *
+     * Generated from protobuf field <code>string ConfigStatus = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setConfigStatus($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->ConfigStatus = $var;
+
+        return $this;
+    }
+
+    /**
+     *解析状态
+     *
+     * Generated from protobuf field <code>string CnameStatus = 4;</code>
+     * @return string
+     */
+    public function getCnameStatus()
+    {
+        return $this->CnameStatus;
+    }
+
+    /**
+     *解析状态
+     *
+     * Generated from protobuf field <code>string CnameStatus = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCnameStatus($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CnameStatus = $var;
+
+        return $this;
+    }
+
+    /**
+     *运行状态
+     *
+     * Generated from protobuf field <code>string Status = 5;</code>
+     * @return string
+     */
+    public function getStatus()
+    {
+        return $this->Status;
+    }
+
+    /**
+     *运行状态
+     *
+     * Generated from protobuf field <code>string Status = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStatus($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Status = $var;
+
+        return $this;
+    }
+
+    /**
+     *证书
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDomainCertificateInfo Certificate = 6;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodDomainCertificateInfo|null
+     */
+    public function getCertificate()
+    {
+        return $this->Certificate;
+    }
+
+    public function hasCertificate()
+    {
+        return isset($this->Certificate);
+    }
+
+    public function clearCertificate()
+    {
+        unset($this->Certificate);
+    }
+
+    /**
+     *证书
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDomainCertificateInfo Certificate = 6;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodDomainCertificateInfo $var
+     * @return $this
+     */
+    public function setCertificate($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodDomainCertificateInfo::class);
+        $this->Certificate = $var;
+
+        return $this;
+    }
+
+    /**
+     *创建时间
+     *
+     * Generated from protobuf field <code>string CreateTime = 7;</code>
+     * @return string
+     */
+    public function getCreateTime()
+    {
+        return $this->CreateTime;
+    }
+
+    /**
+     *创建时间
+     *
+     * Generated from protobuf field <code>string CreateTime = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCreateTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CreateTime = $var;
+
+        return $this;
+    }
+
+    /**
+     *更新时间
+     *
+     * Generated from protobuf field <code>string UpdateTime = 8;</code>
+     * @return string
+     */
+    public function getUpdateTime()
+    {
+        return $this->UpdateTime;
+    }
+
+    /**
+     *更新时间
+     *
+     * Generated from protobuf field <code>string UpdateTime = 8;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setUpdateTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->UpdateTime = $var;
+
+        return $this;
+    }
+
+    /**
+     *加速区域,cn:国内,overseas:海外,global:全球
+     *
+     * Generated from protobuf field <code>string Region = 9;</code>
+     * @return string
+     */
+    public function getRegion()
+    {
+        return $this->Region;
+    }
+
+    /**
+     *加速区域,cn:国内,overseas:海外,global:全球
+     *
+     * Generated from protobuf field <code>string Region = 9;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setRegion($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Region = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodFileSubtitleInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodFileSubtitleInfo.php
new file mode 100755
index 0000000..7fc762a
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodFileSubtitleInfo.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodFileSubtitleInfo</code>
+ */
+class VodFileSubtitleInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *文件ID
+     *
+     * Generated from protobuf field <code>string FileId = 1;</code>
+     */
+    protected $FileId = '';
+    /**
+     *字幕列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSubtitleInfo SubtitleInfoList = 2;</code>
+     */
+    private $SubtitleInfoList;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $FileId
+     *          文件ID
+     *     @type \Volc\Service\Vod\Models\Business\VodSubtitleInfo[]|\Google\Protobuf\Internal\RepeatedField $SubtitleInfoList
+     *          字幕列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *文件ID
+     *
+     * Generated from protobuf field <code>string FileId = 1;</code>
+     * @return string
+     */
+    public function getFileId()
+    {
+        return $this->FileId;
+    }
+
+    /**
+     *文件ID
+     *
+     * Generated from protobuf field <code>string FileId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileId = $var;
+
+        return $this;
+    }
+
+    /**
+     *字幕列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSubtitleInfo SubtitleInfoList = 2;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getSubtitleInfoList()
+    {
+        return $this->SubtitleInfoList;
+    }
+
+    /**
+     *字幕列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSubtitleInfo SubtitleInfoList = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodSubtitleInfo[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setSubtitleInfoList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodSubtitleInfo::class);
+        $this->SubtitleInfoList = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodFrameDataForAudit.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodFrameDataForAudit.php
new file mode 100755
index 0000000..255a784
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodFrameDataForAudit.php
@@ -0,0 +1,201 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodFrameDataForAudit</code>
+ */
+class VodFrameDataForAudit extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * URI of frame store
+     *
+     * Generated from protobuf field <code>string StoreUri = 1;</code>
+     */
+    protected $StoreUri = '';
+    /**
+     *帧号
+     *
+     * Generated from protobuf field <code>int32 FrameNumber = 2;</code>
+     */
+    protected $FrameNumber = 0;
+    /**
+     *帧对应时间(ms)
+     *
+     * Generated from protobuf field <code>double CutTimeMill = 3;</code>
+     */
+    protected $CutTimeMill = 0.0;
+    /**
+     *图片宽度
+     *
+     * Generated from protobuf field <code>int32 Width = 4;</code>
+     */
+    protected $Width = 0;
+    /**
+     *图片高度
+     *
+     * Generated from protobuf field <code>int32 Height = 5;</code>
+     */
+    protected $Height = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $StoreUri
+     *           URI of frame store
+     *     @type int $FrameNumber
+     *          帧号
+     *     @type float $CutTimeMill
+     *          帧对应时间(ms)
+     *     @type int $Width
+     *          图片宽度
+     *     @type int $Height
+     *          图片高度
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * URI of frame store
+     *
+     * Generated from protobuf field <code>string StoreUri = 1;</code>
+     * @return string
+     */
+    public function getStoreUri()
+    {
+        return $this->StoreUri;
+    }
+
+    /**
+     * URI of frame store
+     *
+     * Generated from protobuf field <code>string StoreUri = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStoreUri($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StoreUri = $var;
+
+        return $this;
+    }
+
+    /**
+     *帧号
+     *
+     * Generated from protobuf field <code>int32 FrameNumber = 2;</code>
+     * @return int
+     */
+    public function getFrameNumber()
+    {
+        return $this->FrameNumber;
+    }
+
+    /**
+     *帧号
+     *
+     * Generated from protobuf field <code>int32 FrameNumber = 2;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setFrameNumber($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->FrameNumber = $var;
+
+        return $this;
+    }
+
+    /**
+     *帧对应时间(ms)
+     *
+     * Generated from protobuf field <code>double CutTimeMill = 3;</code>
+     * @return float
+     */
+    public function getCutTimeMill()
+    {
+        return $this->CutTimeMill;
+    }
+
+    /**
+     *帧对应时间(ms)
+     *
+     * Generated from protobuf field <code>double CutTimeMill = 3;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setCutTimeMill($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->CutTimeMill = $var;
+
+        return $this;
+    }
+
+    /**
+     *图片宽度
+     *
+     * Generated from protobuf field <code>int32 Width = 4;</code>
+     * @return int
+     */
+    public function getWidth()
+    {
+        return $this->Width;
+    }
+
+    /**
+     *图片宽度
+     *
+     * Generated from protobuf field <code>int32 Width = 4;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setWidth($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Width = $var;
+
+        return $this;
+    }
+
+    /**
+     *图片高度
+     *
+     * Generated from protobuf field <code>int32 Height = 5;</code>
+     * @return int
+     */
+    public function getHeight()
+    {
+        return $this->Height;
+    }
+
+    /**
+     *图片高度
+     *
+     * Generated from protobuf field <code>int32 Height = 5;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setHeight($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Height = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodFrameExtractingOptionForAudit.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodFrameExtractingOptionForAudit.php
new file mode 100755
index 0000000..db79c23
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodFrameExtractingOptionForAudit.php
@@ -0,0 +1,82 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use UnexpectedValueException;
+
+/**
+ * 模型帧 抽帧选项
+ *
+ * Protobuf type <code>Volcengine.Vod.Models.Business.VodFrameExtractingOptionForAudit</code>
+ */
+class VodFrameExtractingOptionForAudit
+{
+    /**
+     * 未定义的
+     *
+     * Generated from protobuf enum <code>UndefinedVodFrameExtractingOptionForAudit = 0;</code>
+     */
+    const UndefinedVodFrameExtractingOptionForAudit = 0;
+    /**
+     * 根据帧率取帧
+     *
+     * Generated from protobuf enum <code>FpsVodFrameExtractingOptionForAudit = 1;</code>
+     */
+    const FpsVodFrameExtractingOptionForAudit = 1;
+    /**
+     * 根据帧数取帧
+     *
+     * Generated from protobuf enum <code>NumberOfFramesVodFrameExtractingOptionForAudit = 2;</code>
+     */
+    const NumberOfFramesVodFrameExtractingOptionForAudit = 2;
+    /**
+     * 根据时间戳取帧
+     *
+     * Generated from protobuf enum <code>CutTimesVodFrameExtractingOptionForAudit = 3;</code>
+     */
+    const CutTimesVodFrameExtractingOptionForAudit = 3;
+    /**
+     * 根据Fps抽帧,并限制最大或最小帧数
+     *
+     * Generated from protobuf enum <code>FpsLimitNumberOfFramesVodFrameExtractingOptionForAudit = 4;</code>
+     */
+    const FpsLimitNumberOfFramesVodFrameExtractingOptionForAudit = 4;
+    /**
+     * 只取首帧或尾帧
+     *
+     * Generated from protobuf enum <code>OnlyFirstLastFrameVodFrameExtractingOptionForAudit = 5;</code>
+     */
+    const OnlyFirstLastFrameVodFrameExtractingOptionForAudit = 5;
+
+    private static $valueToName = [
+        self::UndefinedVodFrameExtractingOptionForAudit => 'UndefinedVodFrameExtractingOptionForAudit',
+        self::FpsVodFrameExtractingOptionForAudit => 'FpsVodFrameExtractingOptionForAudit',
+        self::NumberOfFramesVodFrameExtractingOptionForAudit => 'NumberOfFramesVodFrameExtractingOptionForAudit',
+        self::CutTimesVodFrameExtractingOptionForAudit => 'CutTimesVodFrameExtractingOptionForAudit',
+        self::FpsLimitNumberOfFramesVodFrameExtractingOptionForAudit => 'FpsLimitNumberOfFramesVodFrameExtractingOptionForAudit',
+        self::OnlyFirstLastFrameVodFrameExtractingOptionForAudit => 'OnlyFirstLastFrameVodFrameExtractingOptionForAudit',
+    ];
+
+    public static function name($value)
+    {
+        if (!isset(self::$valueToName[$value])) {
+            throw new UnexpectedValueException(sprintf(
+                    'Enum %s has no name defined for value %s', __CLASS__, $value));
+        }
+        return self::$valueToName[$value];
+    }
+
+
+    public static function value($name)
+    {
+        $const = __CLASS__ . '::' . strtoupper($name);
+        if (!defined($const)) {
+            throw new UnexpectedValueException(sprintf(
+                    'Enum %s has no value defined for name %s', __CLASS__, $name));
+        }
+        return constant($const);
+    }
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetAppInfoResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetAppInfoResult.php
new file mode 100755
index 0000000..37e06a2
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetAppInfoResult.php
@@ -0,0 +1,167 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_apps_manage.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodGetAppInfoResult</code>
+ */
+class VodGetAppInfoResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * appcloud应用的Id
+     *
+     * Generated from protobuf field <code>uint64 AppId = 1;</code>
+     */
+    protected $AppId = 0;
+    /**
+     *扫码唤起sdk的关键
+     *
+     * Generated from protobuf field <code>string Scheme = 2;</code>
+     */
+    protected $Scheme = '';
+    /**
+     * App英文名
+     *
+     * Generated from protobuf field <code>string AppEnName = 3;</code>
+     */
+    protected $AppEnName = '';
+    /**
+     * App中文名
+     *
+     * Generated from protobuf field <code>string AppCnName = 4;</code>
+     */
+    protected $AppCnName = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int|string $AppId
+     *           appcloud应用的Id
+     *     @type string $Scheme
+     *          扫码唤起sdk的关键
+     *     @type string $AppEnName
+     *           App英文名
+     *     @type string $AppCnName
+     *           App中文名
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodAppsManage::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * appcloud应用的Id
+     *
+     * Generated from protobuf field <code>uint64 AppId = 1;</code>
+     * @return int|string
+     */
+    public function getAppId()
+    {
+        return $this->AppId;
+    }
+
+    /**
+     * appcloud应用的Id
+     *
+     * Generated from protobuf field <code>uint64 AppId = 1;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setAppId($var)
+    {
+        GPBUtil::checkUint64($var);
+        $this->AppId = $var;
+
+        return $this;
+    }
+
+    /**
+     *扫码唤起sdk的关键
+     *
+     * Generated from protobuf field <code>string Scheme = 2;</code>
+     * @return string
+     */
+    public function getScheme()
+    {
+        return $this->Scheme;
+    }
+
+    /**
+     *扫码唤起sdk的关键
+     *
+     * Generated from protobuf field <code>string Scheme = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setScheme($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Scheme = $var;
+
+        return $this;
+    }
+
+    /**
+     * App英文名
+     *
+     * Generated from protobuf field <code>string AppEnName = 3;</code>
+     * @return string
+     */
+    public function getAppEnName()
+    {
+        return $this->AppEnName;
+    }
+
+    /**
+     * App英文名
+     *
+     * Generated from protobuf field <code>string AppEnName = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setAppEnName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->AppEnName = $var;
+
+        return $this;
+    }
+
+    /**
+     * App中文名
+     *
+     * Generated from protobuf field <code>string AppCnName = 4;</code>
+     * @return string
+     */
+    public function getAppCnName()
+    {
+        return $this->AppCnName;
+    }
+
+    /**
+     * App中文名
+     *
+     * Generated from protobuf field <code>string AppCnName = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setAppCnName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->AppCnName = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetAudioEventDetectionForAuditResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetAudioEventDetectionForAuditResult.php
new file mode 100755
index 0000000..efb5b8b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetAudioEventDetectionForAuditResult.php
@@ -0,0 +1,68 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodGetAudioEventDetectionForAuditResult</code>
+ */
+class VodGetAudioEventDetectionForAuditResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodAEDInfoForAudit Info = 1;</code>
+     */
+    protected $Info = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\VodAEDInfoForAudit $Info
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodAEDInfoForAudit Info = 1;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodAEDInfoForAudit|null
+     */
+    public function getInfo()
+    {
+        return $this->Info;
+    }
+
+    public function hasInfo()
+    {
+        return isset($this->Info);
+    }
+
+    public function clearInfo()
+    {
+        unset($this->Info);
+    }
+
+    /**
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodAEDInfoForAudit Info = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodAEDInfoForAudit $var
+     * @return $this
+     */
+    public function setInfo($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodAEDInfoForAudit::class);
+        $this->Info = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetAudioInfoForAuditResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetAudioInfoForAuditResult.php
new file mode 100755
index 0000000..63c4660
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetAudioInfoForAuditResult.php
@@ -0,0 +1,68 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodGetAudioInfoForAuditResult</code>
+ */
+class VodGetAudioInfoForAuditResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodAudioInfoForAudit AudioInfo = 1;</code>
+     */
+    protected $AudioInfo = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\VodAudioInfoForAudit $AudioInfo
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodAudioInfoForAudit AudioInfo = 1;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodAudioInfoForAudit|null
+     */
+    public function getAudioInfo()
+    {
+        return $this->AudioInfo;
+    }
+
+    public function hasAudioInfo()
+    {
+        return isset($this->AudioInfo);
+    }
+
+    public function clearAudioInfo()
+    {
+        unset($this->AudioInfo);
+    }
+
+    /**
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodAudioInfoForAudit AudioInfo = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodAudioInfoForAudit $var
+     * @return $this
+     */
+    public function setAudioInfo($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodAudioInfoForAudit::class);
+        $this->AudioInfo = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetAutomaticSpeechRecognitionForAuditResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetAutomaticSpeechRecognitionForAuditResult.php
new file mode 100755
index 0000000..496a4c2
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetAutomaticSpeechRecognitionForAuditResult.php
@@ -0,0 +1,68 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodGetAutomaticSpeechRecognitionForAuditResult</code>
+ */
+class VodGetAutomaticSpeechRecognitionForAuditResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodASRInfoForAudit Info = 1;</code>
+     */
+    protected $Info = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\VodASRInfoForAudit $Info
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodASRInfoForAudit Info = 1;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodASRInfoForAudit|null
+     */
+    public function getInfo()
+    {
+        return $this->Info;
+    }
+
+    public function hasInfo()
+    {
+        return isset($this->Info);
+    }
+
+    public function clearInfo()
+    {
+        unset($this->Info);
+    }
+
+    /**
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodASRInfoForAudit Info = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodASRInfoForAudit $var
+     * @return $this
+     */
+    public function setInfo($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodASRInfoForAudit::class);
+        $this->Info = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetBetterFramesForAuditResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetBetterFramesForAuditResult.php
new file mode 100755
index 0000000..f1136bb
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetBetterFramesForAuditResult.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodGetBetterFramesForAuditResult</code>
+ */
+class VodGetBetterFramesForAuditResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * frame data
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodBetterFrameDataForAudit Frames = 1;</code>
+     */
+    private $Frames;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\VodBetterFrameDataForAudit[]|\Google\Protobuf\Internal\RepeatedField $Frames
+     *           frame data
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * frame data
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodBetterFrameDataForAudit Frames = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getFrames()
+    {
+        return $this->Frames;
+    }
+
+    /**
+     * frame data
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodBetterFrameDataForAudit Frames = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodBetterFrameDataForAudit[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setFrames($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodBetterFrameDataForAudit::class);
+        $this->Frames = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetFramesForAuditResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetFramesForAuditResult.php
new file mode 100755
index 0000000..c98fc87
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetFramesForAuditResult.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodGetFramesForAuditResult</code>
+ */
+class VodGetFramesForAuditResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 帧列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodFrameDataForAudit Frames = 1;</code>
+     */
+    private $Frames;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\VodFrameDataForAudit[]|\Google\Protobuf\Internal\RepeatedField $Frames
+     *           帧列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 帧列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodFrameDataForAudit Frames = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getFrames()
+    {
+        return $this->Frames;
+    }
+
+    /**
+     * 帧列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodFrameDataForAudit Frames = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodFrameDataForAudit[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setFrames($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodFrameDataForAudit::class);
+        $this->Frames = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetHlsDecryptionKeyResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetHlsDecryptionKeyResult.php
new file mode 100755
index 0000000..faca87d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetHlsDecryptionKeyResult.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_play.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodGetHlsDecryptionKeyResult</code>
+ */
+class VodGetHlsDecryptionKeyResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Drm密钥
+     *
+     * Generated from protobuf field <code>string SecretKey = 1;</code>
+     */
+    protected $SecretKey = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SecretKey
+     *           Drm密钥
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodPlay::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Drm密钥
+     *
+     * Generated from protobuf field <code>string SecretKey = 1;</code>
+     * @return string
+     */
+    public function getSecretKey()
+    {
+        return $this->SecretKey;
+    }
+
+    /**
+     * Drm密钥
+     *
+     * Generated from protobuf field <code>string SecretKey = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSecretKey($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SecretKey = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetMediaInfosData.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetMediaInfosData.php
new file mode 100755
index 0000000..070b22a
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetMediaInfosData.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodGetMediaInfosData</code>
+ */
+class VodGetMediaInfosData extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *视频信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodMediaInfo MediaInfoList = 1;</code>
+     */
+    private $MediaInfoList;
+    /**
+     *不存在的视频VID列表
+     *
+     * Generated from protobuf field <code>repeated string NotExistVids = 2;</code>
+     */
+    private $NotExistVids;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\VodMediaInfo[]|\Google\Protobuf\Internal\RepeatedField $MediaInfoList
+     *          视频信息列表
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $NotExistVids
+     *          不存在的视频VID列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *视频信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodMediaInfo MediaInfoList = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getMediaInfoList()
+    {
+        return $this->MediaInfoList;
+    }
+
+    /**
+     *视频信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodMediaInfo MediaInfoList = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodMediaInfo[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setMediaInfoList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodMediaInfo::class);
+        $this->MediaInfoList = $arr;
+
+        return $this;
+    }
+
+    /**
+     *不存在的视频VID列表
+     *
+     * Generated from protobuf field <code>repeated string NotExistVids = 2;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getNotExistVids()
+    {
+        return $this->NotExistVids;
+    }
+
+    /**
+     *不存在的视频VID列表
+     *
+     * Generated from protobuf field <code>repeated string NotExistVids = 2;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setNotExistVids($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->NotExistVids = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetMediaListData.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetMediaListData.php
new file mode 100755
index 0000000..18c10f7
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetMediaListData.php
@@ -0,0 +1,201 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodGetMediaListData</code>
+ */
+class VodGetMediaListData extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *空间名
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     *视频信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodMediaInfo MediaInfoList = 2;</code>
+     */
+    private $MediaInfoList;
+    /**
+     *符合条件的结果总数
+     *
+     * Generated from protobuf field <code>int32 TotalCount = 3;</code>
+     */
+    protected $TotalCount = 0;
+    /**
+     *数据偏移量
+     *
+     * Generated from protobuf field <code>int32 Offset = 4;</code>
+     */
+    protected $Offset = 0;
+    /**
+     *分页大小
+     *
+     * Generated from protobuf field <code>int32 PageSize = 5;</code>
+     */
+    protected $PageSize = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *          空间名
+     *     @type \Volc\Service\Vod\Models\Business\VodMediaInfo[]|\Google\Protobuf\Internal\RepeatedField $MediaInfoList
+     *          视频信息列表
+     *     @type int $TotalCount
+     *          符合条件的结果总数
+     *     @type int $Offset
+     *          数据偏移量
+     *     @type int $PageSize
+     *          分页大小
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *空间名
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     *空间名
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodMediaInfo MediaInfoList = 2;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getMediaInfoList()
+    {
+        return $this->MediaInfoList;
+    }
+
+    /**
+     *视频信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodMediaInfo MediaInfoList = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodMediaInfo[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setMediaInfoList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodMediaInfo::class);
+        $this->MediaInfoList = $arr;
+
+        return $this;
+    }
+
+    /**
+     *符合条件的结果总数
+     *
+     * Generated from protobuf field <code>int32 TotalCount = 3;</code>
+     * @return int
+     */
+    public function getTotalCount()
+    {
+        return $this->TotalCount;
+    }
+
+    /**
+     *符合条件的结果总数
+     *
+     * Generated from protobuf field <code>int32 TotalCount = 3;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setTotalCount($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->TotalCount = $var;
+
+        return $this;
+    }
+
+    /**
+     *数据偏移量
+     *
+     * Generated from protobuf field <code>int32 Offset = 4;</code>
+     * @return int
+     */
+    public function getOffset()
+    {
+        return $this->Offset;
+    }
+
+    /**
+     *数据偏移量
+     *
+     * Generated from protobuf field <code>int32 Offset = 4;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setOffset($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Offset = $var;
+
+        return $this;
+    }
+
+    /**
+     *分页大小
+     *
+     * Generated from protobuf field <code>int32 PageSize = 5;</code>
+     * @return int
+     */
+    public function getPageSize()
+    {
+        return $this->PageSize;
+    }
+
+    /**
+     *分页大小
+     *
+     * Generated from protobuf field <code>int32 PageSize = 5;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setPageSize($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->PageSize = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetOriginalPlayInfoResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetOriginalPlayInfoResult.php
new file mode 100755
index 0000000..450379b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetOriginalPlayInfoResult.php
@@ -0,0 +1,405 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_play.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodGetOriginalPlayInfoResult</code>
+ */
+class VodGetOriginalPlayInfoResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *返回的媒体类型(video/audio)
+     *
+     * Generated from protobuf field <code>string FileType = 1;</code>
+     */
+    protected $FileType = '';
+    /**
+     *视频时长(单位:s)
+     *
+     * Generated from protobuf field <code>float Duration = 2;</code>
+     */
+    protected $Duration = 0.0;
+    /**
+     *视频文件大小
+     *
+     * Generated from protobuf field <code>double Size = 3;</code>
+     */
+    protected $Size = 0.0;
+    /**
+     *视频高度
+     *
+     * Generated from protobuf field <code>int32 Height = 4;</code>
+     */
+    protected $Height = 0;
+    /**
+     *视频宽度
+     *
+     * Generated from protobuf field <code>int32 Width = 5;</code>
+     */
+    protected $Width = 0;
+    /**
+     *视频格式
+     *
+     * Generated from protobuf field <code>string Format = 6;</code>
+     */
+    protected $Format = '';
+    /**
+     *编码类型
+     *
+     * Generated from protobuf field <code>string Codec = 7;</code>
+     */
+    protected $Codec = '';
+    /**
+     *码率(Kbps)
+     *
+     * Generated from protobuf field <code>int32 Bitrate = 8;</code>
+     */
+    protected $Bitrate = 0;
+    /**
+     * hash值
+     *
+     * Generated from protobuf field <code>string Md5 = 9;</code>
+     */
+    protected $Md5 = '';
+    /**
+     *主播放地址
+     *
+     * Generated from protobuf field <code>string MainPlayUrl = 10;</code>
+     */
+    protected $MainPlayUrl = '';
+    /**
+     *备用播放地址
+     *
+     * Generated from protobuf field <code>string BackupPlayUrl = 11;</code>
+     */
+    protected $BackupPlayUrl = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $FileType
+     *          返回的媒体类型(video/audio)
+     *     @type float $Duration
+     *          视频时长(单位:s)
+     *     @type float $Size
+     *          视频文件大小
+     *     @type int $Height
+     *          视频高度
+     *     @type int $Width
+     *          视频宽度
+     *     @type string $Format
+     *          视频格式
+     *     @type string $Codec
+     *          编码类型
+     *     @type int $Bitrate
+     *          码率(Kbps)
+     *     @type string $Md5
+     *           hash值
+     *     @type string $MainPlayUrl
+     *          主播放地址
+     *     @type string $BackupPlayUrl
+     *          备用播放地址
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodPlay::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *返回的媒体类型(video/audio)
+     *
+     * Generated from protobuf field <code>string FileType = 1;</code>
+     * @return string
+     */
+    public function getFileType()
+    {
+        return $this->FileType;
+    }
+
+    /**
+     *返回的媒体类型(video/audio)
+     *
+     * Generated from protobuf field <code>string FileType = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileType = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频时长(单位:s)
+     *
+     * Generated from protobuf field <code>float Duration = 2;</code>
+     * @return float
+     */
+    public function getDuration()
+    {
+        return $this->Duration;
+    }
+
+    /**
+     *视频时长(单位:s)
+     *
+     * Generated from protobuf field <code>float Duration = 2;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setDuration($var)
+    {
+        GPBUtil::checkFloat($var);
+        $this->Duration = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频文件大小
+     *
+     * Generated from protobuf field <code>double Size = 3;</code>
+     * @return float
+     */
+    public function getSize()
+    {
+        return $this->Size;
+    }
+
+    /**
+     *视频文件大小
+     *
+     * Generated from protobuf field <code>double Size = 3;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setSize($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->Size = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频高度
+     *
+     * Generated from protobuf field <code>int32 Height = 4;</code>
+     * @return int
+     */
+    public function getHeight()
+    {
+        return $this->Height;
+    }
+
+    /**
+     *视频高度
+     *
+     * Generated from protobuf field <code>int32 Height = 4;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setHeight($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Height = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频宽度
+     *
+     * Generated from protobuf field <code>int32 Width = 5;</code>
+     * @return int
+     */
+    public function getWidth()
+    {
+        return $this->Width;
+    }
+
+    /**
+     *视频宽度
+     *
+     * Generated from protobuf field <code>int32 Width = 5;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setWidth($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Width = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频格式
+     *
+     * Generated from protobuf field <code>string Format = 6;</code>
+     * @return string
+     */
+    public function getFormat()
+    {
+        return $this->Format;
+    }
+
+    /**
+     *视频格式
+     *
+     * Generated from protobuf field <code>string Format = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFormat($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Format = $var;
+
+        return $this;
+    }
+
+    /**
+     *编码类型
+     *
+     * Generated from protobuf field <code>string Codec = 7;</code>
+     * @return string
+     */
+    public function getCodec()
+    {
+        return $this->Codec;
+    }
+
+    /**
+     *编码类型
+     *
+     * Generated from protobuf field <code>string Codec = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCodec($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Codec = $var;
+
+        return $this;
+    }
+
+    /**
+     *码率(Kbps)
+     *
+     * Generated from protobuf field <code>int32 Bitrate = 8;</code>
+     * @return int
+     */
+    public function getBitrate()
+    {
+        return $this->Bitrate;
+    }
+
+    /**
+     *码率(Kbps)
+     *
+     * Generated from protobuf field <code>int32 Bitrate = 8;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setBitrate($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Bitrate = $var;
+
+        return $this;
+    }
+
+    /**
+     * hash值
+     *
+     * Generated from protobuf field <code>string Md5 = 9;</code>
+     * @return string
+     */
+    public function getMd5()
+    {
+        return $this->Md5;
+    }
+
+    /**
+     * hash值
+     *
+     * Generated from protobuf field <code>string Md5 = 9;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setMd5($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Md5 = $var;
+
+        return $this;
+    }
+
+    /**
+     *主播放地址
+     *
+     * Generated from protobuf field <code>string MainPlayUrl = 10;</code>
+     * @return string
+     */
+    public function getMainPlayUrl()
+    {
+        return $this->MainPlayUrl;
+    }
+
+    /**
+     *主播放地址
+     *
+     * Generated from protobuf field <code>string MainPlayUrl = 10;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setMainPlayUrl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->MainPlayUrl = $var;
+
+        return $this;
+    }
+
+    /**
+     *备用播放地址
+     *
+     * Generated from protobuf field <code>string BackupPlayUrl = 11;</code>
+     * @return string
+     */
+    public function getBackupPlayUrl()
+    {
+        return $this->BackupPlayUrl;
+    }
+
+    /**
+     *备用播放地址
+     *
+     * Generated from protobuf field <code>string BackupPlayUrl = 11;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setBackupPlayUrl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->BackupPlayUrl = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetPlayInfoWithLiveTimeShiftSceneResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetPlayInfoWithLiveTimeShiftSceneResult.php
new file mode 100755
index 0000000..6fabd6c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetPlayInfoWithLiveTimeShiftSceneResult.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_play.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodGetPlayInfoWithLiveTimeShiftSceneResult</code>
+ */
+class VodGetPlayInfoWithLiveTimeShiftSceneResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 播放信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodPlayInfoWithLiveTimeShiftScene PlayInfoList = 1;</code>
+     */
+    private $PlayInfoList;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\VodPlayInfoWithLiveTimeShiftScene[]|\Google\Protobuf\Internal\RepeatedField $PlayInfoList
+     *           播放信息列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodPlay::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 播放信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodPlayInfoWithLiveTimeShiftScene PlayInfoList = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getPlayInfoList()
+    {
+        return $this->PlayInfoList;
+    }
+
+    /**
+     * 播放信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodPlayInfoWithLiveTimeShiftScene PlayInfoList = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodPlayInfoWithLiveTimeShiftScene[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setPlayInfoList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodPlayInfoWithLiveTimeShiftScene::class);
+        $this->PlayInfoList = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetPrivateDrmPlayAuthResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetPrivateDrmPlayAuthResult.php
new file mode 100755
index 0000000..92fa61b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetPrivateDrmPlayAuthResult.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_play.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodGetPrivateDrmPlayAuthResult</code>
+ */
+class VodGetPrivateDrmPlayAuthResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 播放许可信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodPrivateDrmPlayAuthInfo PlayAuthInfoList = 1;</code>
+     */
+    private $PlayAuthInfoList;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\VodPrivateDrmPlayAuthInfo[]|\Google\Protobuf\Internal\RepeatedField $PlayAuthInfoList
+     *           播放许可信息列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodPlay::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 播放许可信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodPrivateDrmPlayAuthInfo PlayAuthInfoList = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getPlayAuthInfoList()
+    {
+        return $this->PlayAuthInfoList;
+    }
+
+    /**
+     * 播放许可信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodPrivateDrmPlayAuthInfo PlayAuthInfoList = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodPrivateDrmPlayAuthInfo[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setPlayAuthInfoList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodPrivateDrmPlayAuthInfo::class);
+        $this->PlayAuthInfoList = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetRecPosterData.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetRecPosterData.php
new file mode 100755
index 0000000..12a9309
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetRecPosterData.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodGetRecPosterData</code>
+ */
+class VodGetRecPosterData extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *封面图信息
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodStoreUriGroup StoreUriGroups = 1;</code>
+     */
+    private $StoreUriGroups;
+    /**
+     *不存在的视频VID列表
+     *
+     * Generated from protobuf field <code>repeated string NotExistVids = 2;</code>
+     */
+    private $NotExistVids;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\VodStoreUriGroup[]|\Google\Protobuf\Internal\RepeatedField $StoreUriGroups
+     *          封面图信息
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $NotExistVids
+     *          不存在的视频VID列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *封面图信息
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodStoreUriGroup StoreUriGroups = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getStoreUriGroups()
+    {
+        return $this->StoreUriGroups;
+    }
+
+    /**
+     *封面图信息
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodStoreUriGroup StoreUriGroups = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodStoreUriGroup[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setStoreUriGroups($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodStoreUriGroup::class);
+        $this->StoreUriGroups = $arr;
+
+        return $this;
+    }
+
+    /**
+     *不存在的视频VID列表
+     *
+     * Generated from protobuf field <code>repeated string NotExistVids = 2;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getNotExistVids()
+    {
+        return $this->NotExistVids;
+    }
+
+    /**
+     *不存在的视频VID列表
+     *
+     * Generated from protobuf field <code>repeated string NotExistVids = 2;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setNotExistVids($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->NotExistVids = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetSmartStrategyLitePlayInfoResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetSmartStrategyLitePlayInfoResult.php
new file mode 100755
index 0000000..15334e9
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetSmartStrategyLitePlayInfoResult.php
@@ -0,0 +1,177 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_smart_strategy.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodGetSmartStrategyLitePlayInfoResult</code>
+ */
+class VodGetSmartStrategyLitePlayInfoResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 当前返回的播放流类型
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodSmartStrategyResponseStreamType StreamType = 1;</code>
+     */
+    protected $StreamType = 0;
+    /**
+     * 该视频在点播中的元信息状态
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodSmartStrategyDataStoreStatus DataStoreStatus = 2;</code>
+     */
+    protected $DataStoreStatus = 0;
+    /**
+     * 匹配的VideoModel
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodPlayInfoModel PlayInfoModel = 3;</code>
+     */
+    protected $PlayInfoModel = null;
+    /**
+     * 原始URL
+     *
+     * Generated from protobuf field <code>string OriginalPlayUrl = 4;</code>
+     */
+    protected $OriginalPlayUrl = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int $StreamType
+     *           当前返回的播放流类型
+     *     @type int $DataStoreStatus
+     *           该视频在点播中的元信息状态
+     *     @type \Volc\Service\Vod\Models\Business\VodPlayInfoModel $PlayInfoModel
+     *           匹配的VideoModel
+     *     @type string $OriginalPlayUrl
+     *           原始URL
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodSmartStrategy::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 当前返回的播放流类型
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodSmartStrategyResponseStreamType StreamType = 1;</code>
+     * @return int
+     */
+    public function getStreamType()
+    {
+        return $this->StreamType;
+    }
+
+    /**
+     * 当前返回的播放流类型
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodSmartStrategyResponseStreamType StreamType = 1;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setStreamType($var)
+    {
+        GPBUtil::checkEnum($var, \Volc\Service\Vod\Models\Business\VodSmartStrategyResponseStreamType::class);
+        $this->StreamType = $var;
+
+        return $this;
+    }
+
+    /**
+     * 该视频在点播中的元信息状态
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodSmartStrategyDataStoreStatus DataStoreStatus = 2;</code>
+     * @return int
+     */
+    public function getDataStoreStatus()
+    {
+        return $this->DataStoreStatus;
+    }
+
+    /**
+     * 该视频在点播中的元信息状态
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodSmartStrategyDataStoreStatus DataStoreStatus = 2;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setDataStoreStatus($var)
+    {
+        GPBUtil::checkEnum($var, \Volc\Service\Vod\Models\Business\VodSmartStrategyDataStoreStatus::class);
+        $this->DataStoreStatus = $var;
+
+        return $this;
+    }
+
+    /**
+     * 匹配的VideoModel
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodPlayInfoModel PlayInfoModel = 3;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodPlayInfoModel|null
+     */
+    public function getPlayInfoModel()
+    {
+        return $this->PlayInfoModel;
+    }
+
+    public function hasPlayInfoModel()
+    {
+        return isset($this->PlayInfoModel);
+    }
+
+    public function clearPlayInfoModel()
+    {
+        unset($this->PlayInfoModel);
+    }
+
+    /**
+     * 匹配的VideoModel
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodPlayInfoModel PlayInfoModel = 3;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodPlayInfoModel $var
+     * @return $this
+     */
+    public function setPlayInfoModel($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodPlayInfoModel::class);
+        $this->PlayInfoModel = $var;
+
+        return $this;
+    }
+
+    /**
+     * 原始URL
+     *
+     * Generated from protobuf field <code>string OriginalPlayUrl = 4;</code>
+     * @return string
+     */
+    public function getOriginalPlayUrl()
+    {
+        return $this->OriginalPlayUrl;
+    }
+
+    /**
+     * 原始URL
+     *
+     * Generated from protobuf field <code>string OriginalPlayUrl = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setOriginalPlayUrl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->OriginalPlayUrl = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetSubtitleInfoListData.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetSubtitleInfoListData.php
new file mode 100755
index 0000000..6b84c33
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetSubtitleInfoListData.php
@@ -0,0 +1,235 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodGetSubtitleInfoListData</code>
+ */
+class VodGetSubtitleInfoListData extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     */
+    protected $Vid = '';
+    /**
+     *字幕列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodFileSubtitleInfo FileSubtitleInfoList = 2;</code>
+     */
+    private $FileSubtitleInfoList;
+    /**
+     *不存在的转码FileID列表
+     *
+     * Generated from protobuf field <code>repeated string NotExistFileIds = 3;</code>
+     */
+    private $NotExistFileIds;
+    /**
+     *符合条件的结果总数
+     *
+     * Generated from protobuf field <code>int32 TotalCount = 4;</code>
+     */
+    protected $TotalCount = 0;
+    /**
+     *数据偏移量
+     *
+     * Generated from protobuf field <code>int32 Offset = 5;</code>
+     */
+    protected $Offset = 0;
+    /**
+     *分页大小
+     *
+     * Generated from protobuf field <code>int32 PageSize = 6;</code>
+     */
+    protected $PageSize = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vid
+     *          视频ID
+     *     @type \Volc\Service\Vod\Models\Business\VodFileSubtitleInfo[]|\Google\Protobuf\Internal\RepeatedField $FileSubtitleInfoList
+     *          字幕列表
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $NotExistFileIds
+     *          不存在的转码FileID列表
+     *     @type int $TotalCount
+     *          符合条件的结果总数
+     *     @type int $Offset
+     *          数据偏移量
+     *     @type int $PageSize
+     *          分页大小
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     *字幕列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodFileSubtitleInfo FileSubtitleInfoList = 2;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getFileSubtitleInfoList()
+    {
+        return $this->FileSubtitleInfoList;
+    }
+
+    /**
+     *字幕列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodFileSubtitleInfo FileSubtitleInfoList = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodFileSubtitleInfo[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setFileSubtitleInfoList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodFileSubtitleInfo::class);
+        $this->FileSubtitleInfoList = $arr;
+
+        return $this;
+    }
+
+    /**
+     *不存在的转码FileID列表
+     *
+     * Generated from protobuf field <code>repeated string NotExistFileIds = 3;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getNotExistFileIds()
+    {
+        return $this->NotExistFileIds;
+    }
+
+    /**
+     *不存在的转码FileID列表
+     *
+     * Generated from protobuf field <code>repeated string NotExistFileIds = 3;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setNotExistFileIds($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->NotExistFileIds = $arr;
+
+        return $this;
+    }
+
+    /**
+     *符合条件的结果总数
+     *
+     * Generated from protobuf field <code>int32 TotalCount = 4;</code>
+     * @return int
+     */
+    public function getTotalCount()
+    {
+        return $this->TotalCount;
+    }
+
+    /**
+     *符合条件的结果总数
+     *
+     * Generated from protobuf field <code>int32 TotalCount = 4;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setTotalCount($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->TotalCount = $var;
+
+        return $this;
+    }
+
+    /**
+     *数据偏移量
+     *
+     * Generated from protobuf field <code>int32 Offset = 5;</code>
+     * @return int
+     */
+    public function getOffset()
+    {
+        return $this->Offset;
+    }
+
+    /**
+     *数据偏移量
+     *
+     * Generated from protobuf field <code>int32 Offset = 5;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setOffset($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Offset = $var;
+
+        return $this;
+    }
+
+    /**
+     *分页大小
+     *
+     * Generated from protobuf field <code>int32 PageSize = 6;</code>
+     * @return int
+     */
+    public function getPageSize()
+    {
+        return $this->PageSize;
+    }
+
+    /**
+     *分页大小
+     *
+     * Generated from protobuf field <code>int32 PageSize = 6;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setPageSize($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->PageSize = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetWorkflowExecutionDetailResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetWorkflowExecutionDetailResult.php
new file mode 100755
index 0000000..2e13c8c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodGetWorkflowExecutionDetailResult.php
@@ -0,0 +1,392 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_workflow.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodGetWorkflowExecutionDetailResult</code>
+ */
+class VodGetWorkflowExecutionDetailResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>string RunId = 1;</code>
+     */
+    protected $RunId = '';
+    /**
+     * Generated from protobuf field <code>string Vid = 2;</code>
+     */
+    protected $Vid = '';
+    /**
+     * Generated from protobuf field <code>string TemplateId = 3;</code>
+     */
+    protected $TemplateId = '';
+    /**
+     * Generated from protobuf field <code>string SpaceName = 4;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     * Generated from protobuf field <code>string Status = 6;</code>
+     */
+    protected $Status = '';
+    /**
+     * Generated from protobuf field <code>string TaskListId = 7;</code>
+     */
+    protected $TaskListId = '';
+    /**
+     * Generated from protobuf field <code>bool EnableLowPriority = 8;</code>
+     */
+    protected $EnableLowPriority = false;
+    /**
+     * Generated from protobuf field <code>string JobSource = 9;</code>
+     */
+    protected $JobSource = '';
+    /**
+     * 执行阶段
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.ExecutionStage Stages = 10;</code>
+     */
+    private $Stages;
+    /**
+     * Generated from protobuf field <code>.google.protobuf.Timestamp CreateTime = 11;</code>
+     */
+    protected $CreateTime = null;
+    /**
+     * Generated from protobuf field <code>.google.protobuf.Timestamp StartTime = 12;</code>
+     */
+    protected $StartTime = null;
+    /**
+     * Generated from protobuf field <code>.google.protobuf.Timestamp EndTime = 13;</code>
+     */
+    protected $EndTime = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $RunId
+     *     @type string $Vid
+     *     @type string $TemplateId
+     *     @type string $SpaceName
+     *     @type string $Status
+     *     @type string $TaskListId
+     *     @type bool $EnableLowPriority
+     *     @type string $JobSource
+     *     @type \Volc\Service\Vod\Models\Business\ExecutionStage[]|\Google\Protobuf\Internal\RepeatedField $Stages
+     *           执行阶段
+     *     @type \Google\Protobuf\Timestamp $CreateTime
+     *     @type \Google\Protobuf\Timestamp $StartTime
+     *     @type \Google\Protobuf\Timestamp $EndTime
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodWorkflow::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>string RunId = 1;</code>
+     * @return string
+     */
+    public function getRunId()
+    {
+        return $this->RunId;
+    }
+
+    /**
+     * Generated from protobuf field <code>string RunId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setRunId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->RunId = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string Vid = 2;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     * Generated from protobuf field <code>string Vid = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string TemplateId = 3;</code>
+     * @return string
+     */
+    public function getTemplateId()
+    {
+        return $this->TemplateId;
+    }
+
+    /**
+     * Generated from protobuf field <code>string TemplateId = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTemplateId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TemplateId = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string SpaceName = 4;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * Generated from protobuf field <code>string SpaceName = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string Status = 6;</code>
+     * @return string
+     */
+    public function getStatus()
+    {
+        return $this->Status;
+    }
+
+    /**
+     * Generated from protobuf field <code>string Status = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStatus($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Status = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string TaskListId = 7;</code>
+     * @return string
+     */
+    public function getTaskListId()
+    {
+        return $this->TaskListId;
+    }
+
+    /**
+     * Generated from protobuf field <code>string TaskListId = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTaskListId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TaskListId = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>bool EnableLowPriority = 8;</code>
+     * @return bool
+     */
+    public function getEnableLowPriority()
+    {
+        return $this->EnableLowPriority;
+    }
+
+    /**
+     * Generated from protobuf field <code>bool EnableLowPriority = 8;</code>
+     * @param bool $var
+     * @return $this
+     */
+    public function setEnableLowPriority($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->EnableLowPriority = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string JobSource = 9;</code>
+     * @return string
+     */
+    public function getJobSource()
+    {
+        return $this->JobSource;
+    }
+
+    /**
+     * Generated from protobuf field <code>string JobSource = 9;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setJobSource($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->JobSource = $var;
+
+        return $this;
+    }
+
+    /**
+     * 执行阶段
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.ExecutionStage Stages = 10;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getStages()
+    {
+        return $this->Stages;
+    }
+
+    /**
+     * 执行阶段
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.ExecutionStage Stages = 10;</code>
+     * @param \Volc\Service\Vod\Models\Business\ExecutionStage[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setStages($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\ExecutionStage::class);
+        $this->Stages = $arr;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>.google.protobuf.Timestamp CreateTime = 11;</code>
+     * @return \Google\Protobuf\Timestamp|null
+     */
+    public function getCreateTime()
+    {
+        return $this->CreateTime;
+    }
+
+    public function hasCreateTime()
+    {
+        return isset($this->CreateTime);
+    }
+
+    public function clearCreateTime()
+    {
+        unset($this->CreateTime);
+    }
+
+    /**
+     * Generated from protobuf field <code>.google.protobuf.Timestamp CreateTime = 11;</code>
+     * @param \Google\Protobuf\Timestamp $var
+     * @return $this
+     */
+    public function setCreateTime($var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\Timestamp::class);
+        $this->CreateTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>.google.protobuf.Timestamp StartTime = 12;</code>
+     * @return \Google\Protobuf\Timestamp|null
+     */
+    public function getStartTime()
+    {
+        return $this->StartTime;
+    }
+
+    public function hasStartTime()
+    {
+        return isset($this->StartTime);
+    }
+
+    public function clearStartTime()
+    {
+        unset($this->StartTime);
+    }
+
+    /**
+     * Generated from protobuf field <code>.google.protobuf.Timestamp StartTime = 12;</code>
+     * @param \Google\Protobuf\Timestamp $var
+     * @return $this
+     */
+    public function setStartTime($var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\Timestamp::class);
+        $this->StartTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>.google.protobuf.Timestamp EndTime = 13;</code>
+     * @return \Google\Protobuf\Timestamp|null
+     */
+    public function getEndTime()
+    {
+        return $this->EndTime;
+    }
+
+    public function hasEndTime()
+    {
+        return isset($this->EndTime);
+    }
+
+    public function clearEndTime()
+    {
+        unset($this->EndTime);
+    }
+
+    /**
+     * Generated from protobuf field <code>.google.protobuf.Timestamp EndTime = 13;</code>
+     * @param \Google\Protobuf\Timestamp $var
+     * @return $this
+     */
+    public function setEndTime($var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\Timestamp::class);
+        $this->EndTime = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodHeaderPair.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodHeaderPair.php
new file mode 100755
index 0000000..ebc783d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodHeaderPair.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_upload.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodHeaderPair</code>
+ */
+class VodHeaderPair extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 1001 阶段返回信息,1002-1004 上传时需要带的 header 中的 key 
+     *
+     * Generated from protobuf field <code>string Key = 1;</code>
+     */
+    protected $Key = '';
+    /**
+     * 1001 阶段返回信息,1002-1004 上传时需要带的 header 中的 value 
+     *
+     * Generated from protobuf field <code>string Value = 2;</code>
+     */
+    protected $Value = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Key
+     *           1001 阶段返回信息,1002-1004 上传时需要带的 header 中的 key 
+     *     @type string $Value
+     *           1001 阶段返回信息,1002-1004 上传时需要带的 header 中的 value 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodUpload::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 1001 阶段返回信息,1002-1004 上传时需要带的 header 中的 key 
+     *
+     * Generated from protobuf field <code>string Key = 1;</code>
+     * @return string
+     */
+    public function getKey()
+    {
+        return $this->Key;
+    }
+
+    /**
+     * 1001 阶段返回信息,1002-1004 上传时需要带的 header 中的 key 
+     *
+     * Generated from protobuf field <code>string Key = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setKey($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Key = $var;
+
+        return $this;
+    }
+
+    /**
+     * 1001 阶段返回信息,1002-1004 上传时需要带的 header 中的 value 
+     *
+     * Generated from protobuf field <code>string Value = 2;</code>
+     * @return string
+     */
+    public function getValue()
+    {
+        return $this->Value;
+    }
+
+    /**
+     * 1001 阶段返回信息,1002-1004 上传时需要带的 header 中的 value 
+     *
+     * Generated from protobuf field <code>string Value = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setValue($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Value = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodListCdnAccessLogResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodListCdnAccessLogResult.php
new file mode 100755
index 0000000..bac4bd8
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodListCdnAccessLogResult.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_cdn.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodListCdnAccessLogResult</code>
+ */
+class VodListCdnAccessLogResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 日志列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodCdnAccessLogInfo Logs = 1;</code>
+     */
+    private $Logs;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\VodCdnAccessLogInfo[]|\Google\Protobuf\Internal\RepeatedField $Logs
+     *           日志列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCdn::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 日志列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodCdnAccessLogInfo Logs = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getLogs()
+    {
+        return $this->Logs;
+    }
+
+    /**
+     * 日志列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodCdnAccessLogInfo Logs = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodCdnAccessLogInfo[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setLogs($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodCdnAccessLogInfo::class);
+        $this->Logs = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodListCdnTopAccessUrlResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodListCdnTopAccessUrlResult.php
new file mode 100755
index 0000000..359a55f
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodListCdnTopAccessUrlResult.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_cdn.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodListCdnTopAccessUrlResult</code>
+ */
+class VodListCdnTopAccessUrlResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * TopURL查询得到的数据明细以及全部域名的汇总值
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodCdnTopAccessUrlElement UrlInfos = 1;</code>
+     */
+    private $UrlInfos;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\VodCdnTopAccessUrlElement[]|\Google\Protobuf\Internal\RepeatedField $UrlInfos
+     *           TopURL查询得到的数据明细以及全部域名的汇总值
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCdn::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * TopURL查询得到的数据明细以及全部域名的汇总值
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodCdnTopAccessUrlElement UrlInfos = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getUrlInfos()
+    {
+        return $this->UrlInfos;
+    }
+
+    /**
+     * TopURL查询得到的数据明细以及全部域名的汇总值
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodCdnTopAccessUrlElement UrlInfos = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodCdnTopAccessUrlElement[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setUrlInfos($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodCdnTopAccessUrlElement::class);
+        $this->UrlInfos = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodListWorkflowExecutionResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodListWorkflowExecutionResult.php
new file mode 100755
index 0000000..4ed5a74
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodListWorkflowExecutionResult.php
@@ -0,0 +1,167 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_workflow.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodListWorkflowExecutionResult</code>
+ */
+class VodListWorkflowExecutionResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 工作流记录
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.WorkflowExecution Data = 1;</code>
+     */
+    private $Data;
+    /**
+     * 符合条件的结果总数
+     *
+     * Generated from protobuf field <code>int32 TotalCount = 2;</code>
+     */
+    protected $TotalCount = 0;
+    /**
+     * 分页大小
+     *
+     * Generated from protobuf field <code>int32 PageSize = 3;</code>
+     */
+    protected $PageSize = 0;
+    /**
+     * 数据偏移量
+     *
+     * Generated from protobuf field <code>int32 Offset = 4;</code>
+     */
+    protected $Offset = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\WorkflowExecution[]|\Google\Protobuf\Internal\RepeatedField $Data
+     *           工作流记录
+     *     @type int $TotalCount
+     *           符合条件的结果总数
+     *     @type int $PageSize
+     *           分页大小
+     *     @type int $Offset
+     *           数据偏移量
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodWorkflow::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 工作流记录
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.WorkflowExecution Data = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getData()
+    {
+        return $this->Data;
+    }
+
+    /**
+     * 工作流记录
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.WorkflowExecution Data = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\WorkflowExecution[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setData($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\WorkflowExecution::class);
+        $this->Data = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 符合条件的结果总数
+     *
+     * Generated from protobuf field <code>int32 TotalCount = 2;</code>
+     * @return int
+     */
+    public function getTotalCount()
+    {
+        return $this->TotalCount;
+    }
+
+    /**
+     * 符合条件的结果总数
+     *
+     * Generated from protobuf field <code>int32 TotalCount = 2;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setTotalCount($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->TotalCount = $var;
+
+        return $this;
+    }
+
+    /**
+     * 分页大小
+     *
+     * Generated from protobuf field <code>int32 PageSize = 3;</code>
+     * @return int
+     */
+    public function getPageSize()
+    {
+        return $this->PageSize;
+    }
+
+    /**
+     * 分页大小
+     *
+     * Generated from protobuf field <code>int32 PageSize = 3;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setPageSize($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->PageSize = $var;
+
+        return $this;
+    }
+
+    /**
+     * 数据偏移量
+     *
+     * Generated from protobuf field <code>int32 Offset = 4;</code>
+     * @return int
+     */
+    public function getOffset()
+    {
+        return $this->Offset;
+    }
+
+    /**
+     * 数据偏移量
+     *
+     * Generated from protobuf field <code>int32 Offset = 4;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setOffset($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Offset = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodMediaBasicInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodMediaBasicInfo.php
new file mode 100755
index 0000000..9b83b05
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodMediaBasicInfo.php
@@ -0,0 +1,347 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodMediaBasicInfo</code>
+ */
+class VodMediaBasicInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *空间名
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 2;</code>
+     */
+    protected $Vid = '';
+    /**
+     *视频名称
+     *
+     * Generated from protobuf field <code>string Title = 3;</code>
+     */
+    protected $Title = '';
+    /**
+     *视频描述
+     *
+     * Generated from protobuf field <code>string Description = 4;</code>
+     */
+    protected $Description = '';
+    /**
+     *封面图对象地址
+     *
+     * Generated from protobuf field <code>string PosterUri = 5;</code>
+     */
+    protected $PosterUri = '';
+    /**
+     *发布状态
+     *
+     * Generated from protobuf field <code>string PublishStatus = 6;</code>
+     */
+    protected $PublishStatus = '';
+    /**
+     *标签列表
+     *
+     * Generated from protobuf field <code>repeated string Tags = 7;</code>
+     */
+    private $Tags;
+    /**
+     *创建时间
+     *
+     * Generated from protobuf field <code>string CreateTime = 8;</code>
+     */
+    protected $CreateTime = '';
+    /**
+     *分类串
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodClassification Classification = 9;</code>
+     */
+    protected $Classification = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *          空间名
+     *     @type string $Vid
+     *          视频ID
+     *     @type string $Title
+     *          视频名称
+     *     @type string $Description
+     *          视频描述
+     *     @type string $PosterUri
+     *          封面图对象地址
+     *     @type string $PublishStatus
+     *          发布状态
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $Tags
+     *          标签列表
+     *     @type string $CreateTime
+     *          创建时间
+     *     @type \Volc\Service\Vod\Models\Business\VodClassification $Classification
+     *          分类串
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *空间名
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     *空间名
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 2;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频名称
+     *
+     * Generated from protobuf field <code>string Title = 3;</code>
+     * @return string
+     */
+    public function getTitle()
+    {
+        return $this->Title;
+    }
+
+    /**
+     *视频名称
+     *
+     * Generated from protobuf field <code>string Title = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTitle($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Title = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频描述
+     *
+     * Generated from protobuf field <code>string Description = 4;</code>
+     * @return string
+     */
+    public function getDescription()
+    {
+        return $this->Description;
+    }
+
+    /**
+     *视频描述
+     *
+     * Generated from protobuf field <code>string Description = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDescription($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Description = $var;
+
+        return $this;
+    }
+
+    /**
+     *封面图对象地址
+     *
+     * Generated from protobuf field <code>string PosterUri = 5;</code>
+     * @return string
+     */
+    public function getPosterUri()
+    {
+        return $this->PosterUri;
+    }
+
+    /**
+     *封面图对象地址
+     *
+     * Generated from protobuf field <code>string PosterUri = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setPosterUri($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->PosterUri = $var;
+
+        return $this;
+    }
+
+    /**
+     *发布状态
+     *
+     * Generated from protobuf field <code>string PublishStatus = 6;</code>
+     * @return string
+     */
+    public function getPublishStatus()
+    {
+        return $this->PublishStatus;
+    }
+
+    /**
+     *发布状态
+     *
+     * Generated from protobuf field <code>string PublishStatus = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setPublishStatus($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->PublishStatus = $var;
+
+        return $this;
+    }
+
+    /**
+     *标签列表
+     *
+     * Generated from protobuf field <code>repeated string Tags = 7;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getTags()
+    {
+        return $this->Tags;
+    }
+
+    /**
+     *标签列表
+     *
+     * Generated from protobuf field <code>repeated string Tags = 7;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setTags($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->Tags = $arr;
+
+        return $this;
+    }
+
+    /**
+     *创建时间
+     *
+     * Generated from protobuf field <code>string CreateTime = 8;</code>
+     * @return string
+     */
+    public function getCreateTime()
+    {
+        return $this->CreateTime;
+    }
+
+    /**
+     *创建时间
+     *
+     * Generated from protobuf field <code>string CreateTime = 8;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCreateTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CreateTime = $var;
+
+        return $this;
+    }
+
+    /**
+     *分类串
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodClassification Classification = 9;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodClassification|null
+     */
+    public function getClassification()
+    {
+        return $this->Classification;
+    }
+
+    public function hasClassification()
+    {
+        return isset($this->Classification);
+    }
+
+    public function clearClassification()
+    {
+        unset($this->Classification);
+    }
+
+    /**
+     *分类串
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodClassification Classification = 9;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodClassification $var
+     * @return $this
+     */
+    public function setClassification($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodClassification::class);
+        $this->Classification = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodMediaInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodMediaInfo.php
new file mode 100755
index 0000000..e35d681
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodMediaInfo.php
@@ -0,0 +1,153 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodMediaInfo</code>
+ */
+class VodMediaInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *视频基础信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodMediaBasicInfo BasicInfo = 1;</code>
+     */
+    protected $BasicInfo = null;
+    /**
+     *原视频信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodSourceInfo SourceInfo = 2;</code>
+     */
+    protected $SourceInfo = null;
+    /**
+     *转码视频信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodTranscodeInfo TranscodeInfos = 3;</code>
+     */
+    private $TranscodeInfos;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\VodMediaBasicInfo $BasicInfo
+     *          视频基础信息
+     *     @type \Volc\Service\Vod\Models\Business\VodSourceInfo $SourceInfo
+     *          原视频信息
+     *     @type \Volc\Service\Vod\Models\Business\VodTranscodeInfo[]|\Google\Protobuf\Internal\RepeatedField $TranscodeInfos
+     *          转码视频信息列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *视频基础信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodMediaBasicInfo BasicInfo = 1;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodMediaBasicInfo|null
+     */
+    public function getBasicInfo()
+    {
+        return $this->BasicInfo;
+    }
+
+    public function hasBasicInfo()
+    {
+        return isset($this->BasicInfo);
+    }
+
+    public function clearBasicInfo()
+    {
+        unset($this->BasicInfo);
+    }
+
+    /**
+     *视频基础信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodMediaBasicInfo BasicInfo = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodMediaBasicInfo $var
+     * @return $this
+     */
+    public function setBasicInfo($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodMediaBasicInfo::class);
+        $this->BasicInfo = $var;
+
+        return $this;
+    }
+
+    /**
+     *原视频信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodSourceInfo SourceInfo = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodSourceInfo|null
+     */
+    public function getSourceInfo()
+    {
+        return $this->SourceInfo;
+    }
+
+    public function hasSourceInfo()
+    {
+        return isset($this->SourceInfo);
+    }
+
+    public function clearSourceInfo()
+    {
+        unset($this->SourceInfo);
+    }
+
+    /**
+     *原视频信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodSourceInfo SourceInfo = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodSourceInfo $var
+     * @return $this
+     */
+    public function setSourceInfo($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodSourceInfo::class);
+        $this->SourceInfo = $var;
+
+        return $this;
+    }
+
+    /**
+     *转码视频信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodTranscodeInfo TranscodeInfos = 3;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getTranscodeInfos()
+    {
+        return $this->TranscodeInfos;
+    }
+
+    /**
+     *转码视频信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodTranscodeInfo TranscodeInfos = 3;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodTranscodeInfo[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setTranscodeInfos($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodTranscodeInfo::class);
+        $this->TranscodeInfos = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodPlayInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodPlayInfo.php
new file mode 100755
index 0000000..0e67ad0
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodPlayInfo.php
@@ -0,0 +1,823 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_common.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodPlayInfo</code>
+ */
+class VodPlayInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *文件ID
+     *
+     * Generated from protobuf field <code>string FileId = 1;</code>
+     */
+    protected $FileId = '';
+    /**
+     * hash值
+     *
+     * Generated from protobuf field <code>string Md5 = 2;</code>
+     */
+    protected $Md5 = '';
+    /**
+     * 文件类型 video/audio
+     *
+     * Generated from protobuf field <code>string FileType = 3;</code>
+     */
+    protected $FileType = '';
+    /**
+     *视频格式
+     *
+     * Generated from protobuf field <code>string Format = 4;</code>
+     */
+    protected $Format = '';
+    /**
+     *编码类型
+     *
+     * Generated from protobuf field <code>string Codec = 5;</code>
+     */
+    protected $Codec = '';
+    /**
+     *视频分辨率
+     *
+     * Generated from protobuf field <code>string Definition = 6;</code>
+     */
+    protected $Definition = '';
+    /**
+     *主播放地址
+     *
+     * Generated from protobuf field <code>string MainPlayUrl = 7;</code>
+     */
+    protected $MainPlayUrl = '';
+    /**
+     *备用播放地址
+     *
+     * Generated from protobuf field <code>string BackupPlayUrl = 8;</code>
+     */
+    protected $BackupPlayUrl = '';
+    /**
+     *码率(Kbps)
+     *
+     * Generated from protobuf field <code>int32 Bitrate = 9;</code>
+     */
+    protected $Bitrate = 0;
+    /**
+     *视频高度
+     *
+     * Generated from protobuf field <code>int32 Width = 10;</code>
+     */
+    protected $Width = 0;
+    /**
+     *视频宽度
+     *
+     * Generated from protobuf field <code>int32 Height = 11;</code>
+     */
+    protected $Height = 0;
+    /**
+     *文件大小
+     *
+     * Generated from protobuf field <code>double Size = 12;</code>
+     */
+    protected $Size = 0.0;
+    /**
+     *劫持校验信息
+     *
+     * Generated from protobuf field <code>string CheckInfo = 13;</code>
+     */
+    protected $CheckInfo = '';
+    /**
+     * dash segment_base 分片信息
+     *
+     * Generated from protobuf field <code>string IndexRange = 14;</code>
+     */
+    protected $IndexRange = '';
+    /**
+     * dash segment_base 分片信息
+     *
+     * Generated from protobuf field <code>string InitRange = 15;</code>
+     */
+    protected $InitRange = '';
+    /**
+     *加密过的秘钥
+     *
+     * Generated from protobuf field <code>string PlayAuth = 16;</code>
+     */
+    protected $PlayAuth = '';
+    /**
+     *密钥keyID
+     *
+     * Generated from protobuf field <code>string PlayAuthId = 17;</code>
+     */
+    protected $PlayAuthId = '';
+    /**
+     *水印类型
+     *
+     * Generated from protobuf field <code>string LogoType = 18;</code>
+     */
+    protected $LogoType = '';
+    /**
+     *音频质量
+     *
+     * Generated from protobuf field <code>string Quality = 19;</code>
+     */
+    protected $Quality = '';
+    /**
+     * 弹幕蒙版偏移量
+     *
+     * Generated from protobuf field <code>string BarrageMaskOffset = 20;</code>
+     */
+    protected $BarrageMaskOffset = '';
+    /**
+     *时长
+     *
+     * Generated from protobuf field <code>float Duration = 21;</code>
+     */
+    protected $Duration = 0.0;
+    /**
+     * 在转对齐,使用的帧对齐转码版本
+     *
+     * Generated from protobuf field <code>string KeyFrameAlignment = 22;</code>
+     */
+    protected $KeyFrameAlignment = '';
+    /**
+     *响度信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodVolumeInfo Volume = 23;</code>
+     */
+    protected $Volume = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $FileId
+     *          文件ID
+     *     @type string $Md5
+     *           hash值
+     *     @type string $FileType
+     *           文件类型 video/audio
+     *     @type string $Format
+     *          视频格式
+     *     @type string $Codec
+     *          编码类型
+     *     @type string $Definition
+     *          视频分辨率
+     *     @type string $MainPlayUrl
+     *          主播放地址
+     *     @type string $BackupPlayUrl
+     *          备用播放地址
+     *     @type int $Bitrate
+     *          码率(Kbps)
+     *     @type int $Width
+     *          视频高度
+     *     @type int $Height
+     *          视频宽度
+     *     @type float $Size
+     *          文件大小
+     *     @type string $CheckInfo
+     *          劫持校验信息
+     *     @type string $IndexRange
+     *           dash segment_base 分片信息
+     *     @type string $InitRange
+     *           dash segment_base 分片信息
+     *     @type string $PlayAuth
+     *          加密过的秘钥
+     *     @type string $PlayAuthId
+     *          密钥keyID
+     *     @type string $LogoType
+     *          水印类型
+     *     @type string $Quality
+     *          音频质量
+     *     @type string $BarrageMaskOffset
+     *           弹幕蒙版偏移量
+     *     @type float $Duration
+     *          时长
+     *     @type string $KeyFrameAlignment
+     *           在转对齐,使用的帧对齐转码版本
+     *     @type \Volc\Service\Vod\Models\Business\VodVolumeInfo $Volume
+     *          响度信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCommon::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *文件ID
+     *
+     * Generated from protobuf field <code>string FileId = 1;</code>
+     * @return string
+     */
+    public function getFileId()
+    {
+        return $this->FileId;
+    }
+
+    /**
+     *文件ID
+     *
+     * Generated from protobuf field <code>string FileId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileId = $var;
+
+        return $this;
+    }
+
+    /**
+     * hash值
+     *
+     * Generated from protobuf field <code>string Md5 = 2;</code>
+     * @return string
+     */
+    public function getMd5()
+    {
+        return $this->Md5;
+    }
+
+    /**
+     * hash值
+     *
+     * Generated from protobuf field <code>string Md5 = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setMd5($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Md5 = $var;
+
+        return $this;
+    }
+
+    /**
+     * 文件类型 video/audio
+     *
+     * Generated from protobuf field <code>string FileType = 3;</code>
+     * @return string
+     */
+    public function getFileType()
+    {
+        return $this->FileType;
+    }
+
+    /**
+     * 文件类型 video/audio
+     *
+     * Generated from protobuf field <code>string FileType = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileType = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频格式
+     *
+     * Generated from protobuf field <code>string Format = 4;</code>
+     * @return string
+     */
+    public function getFormat()
+    {
+        return $this->Format;
+    }
+
+    /**
+     *视频格式
+     *
+     * Generated from protobuf field <code>string Format = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFormat($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Format = $var;
+
+        return $this;
+    }
+
+    /**
+     *编码类型
+     *
+     * Generated from protobuf field <code>string Codec = 5;</code>
+     * @return string
+     */
+    public function getCodec()
+    {
+        return $this->Codec;
+    }
+
+    /**
+     *编码类型
+     *
+     * Generated from protobuf field <code>string Codec = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCodec($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Codec = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频分辨率
+     *
+     * Generated from protobuf field <code>string Definition = 6;</code>
+     * @return string
+     */
+    public function getDefinition()
+    {
+        return $this->Definition;
+    }
+
+    /**
+     *视频分辨率
+     *
+     * Generated from protobuf field <code>string Definition = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDefinition($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Definition = $var;
+
+        return $this;
+    }
+
+    /**
+     *主播放地址
+     *
+     * Generated from protobuf field <code>string MainPlayUrl = 7;</code>
+     * @return string
+     */
+    public function getMainPlayUrl()
+    {
+        return $this->MainPlayUrl;
+    }
+
+    /**
+     *主播放地址
+     *
+     * Generated from protobuf field <code>string MainPlayUrl = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setMainPlayUrl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->MainPlayUrl = $var;
+
+        return $this;
+    }
+
+    /**
+     *备用播放地址
+     *
+     * Generated from protobuf field <code>string BackupPlayUrl = 8;</code>
+     * @return string
+     */
+    public function getBackupPlayUrl()
+    {
+        return $this->BackupPlayUrl;
+    }
+
+    /**
+     *备用播放地址
+     *
+     * Generated from protobuf field <code>string BackupPlayUrl = 8;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setBackupPlayUrl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->BackupPlayUrl = $var;
+
+        return $this;
+    }
+
+    /**
+     *码率(Kbps)
+     *
+     * Generated from protobuf field <code>int32 Bitrate = 9;</code>
+     * @return int
+     */
+    public function getBitrate()
+    {
+        return $this->Bitrate;
+    }
+
+    /**
+     *码率(Kbps)
+     *
+     * Generated from protobuf field <code>int32 Bitrate = 9;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setBitrate($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Bitrate = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频高度
+     *
+     * Generated from protobuf field <code>int32 Width = 10;</code>
+     * @return int
+     */
+    public function getWidth()
+    {
+        return $this->Width;
+    }
+
+    /**
+     *视频高度
+     *
+     * Generated from protobuf field <code>int32 Width = 10;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setWidth($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Width = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频宽度
+     *
+     * Generated from protobuf field <code>int32 Height = 11;</code>
+     * @return int
+     */
+    public function getHeight()
+    {
+        return $this->Height;
+    }
+
+    /**
+     *视频宽度
+     *
+     * Generated from protobuf field <code>int32 Height = 11;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setHeight($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Height = $var;
+
+        return $this;
+    }
+
+    /**
+     *文件大小
+     *
+     * Generated from protobuf field <code>double Size = 12;</code>
+     * @return float
+     */
+    public function getSize()
+    {
+        return $this->Size;
+    }
+
+    /**
+     *文件大小
+     *
+     * Generated from protobuf field <code>double Size = 12;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setSize($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->Size = $var;
+
+        return $this;
+    }
+
+    /**
+     *劫持校验信息
+     *
+     * Generated from protobuf field <code>string CheckInfo = 13;</code>
+     * @return string
+     */
+    public function getCheckInfo()
+    {
+        return $this->CheckInfo;
+    }
+
+    /**
+     *劫持校验信息
+     *
+     * Generated from protobuf field <code>string CheckInfo = 13;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCheckInfo($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CheckInfo = $var;
+
+        return $this;
+    }
+
+    /**
+     * dash segment_base 分片信息
+     *
+     * Generated from protobuf field <code>string IndexRange = 14;</code>
+     * @return string
+     */
+    public function getIndexRange()
+    {
+        return $this->IndexRange;
+    }
+
+    /**
+     * dash segment_base 分片信息
+     *
+     * Generated from protobuf field <code>string IndexRange = 14;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setIndexRange($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->IndexRange = $var;
+
+        return $this;
+    }
+
+    /**
+     * dash segment_base 分片信息
+     *
+     * Generated from protobuf field <code>string InitRange = 15;</code>
+     * @return string
+     */
+    public function getInitRange()
+    {
+        return $this->InitRange;
+    }
+
+    /**
+     * dash segment_base 分片信息
+     *
+     * Generated from protobuf field <code>string InitRange = 15;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setInitRange($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->InitRange = $var;
+
+        return $this;
+    }
+
+    /**
+     *加密过的秘钥
+     *
+     * Generated from protobuf field <code>string PlayAuth = 16;</code>
+     * @return string
+     */
+    public function getPlayAuth()
+    {
+        return $this->PlayAuth;
+    }
+
+    /**
+     *加密过的秘钥
+     *
+     * Generated from protobuf field <code>string PlayAuth = 16;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setPlayAuth($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->PlayAuth = $var;
+
+        return $this;
+    }
+
+    /**
+     *密钥keyID
+     *
+     * Generated from protobuf field <code>string PlayAuthId = 17;</code>
+     * @return string
+     */
+    public function getPlayAuthId()
+    {
+        return $this->PlayAuthId;
+    }
+
+    /**
+     *密钥keyID
+     *
+     * Generated from protobuf field <code>string PlayAuthId = 17;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setPlayAuthId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->PlayAuthId = $var;
+
+        return $this;
+    }
+
+    /**
+     *水印类型
+     *
+     * Generated from protobuf field <code>string LogoType = 18;</code>
+     * @return string
+     */
+    public function getLogoType()
+    {
+        return $this->LogoType;
+    }
+
+    /**
+     *水印类型
+     *
+     * Generated from protobuf field <code>string LogoType = 18;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setLogoType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->LogoType = $var;
+
+        return $this;
+    }
+
+    /**
+     *音频质量
+     *
+     * Generated from protobuf field <code>string Quality = 19;</code>
+     * @return string
+     */
+    public function getQuality()
+    {
+        return $this->Quality;
+    }
+
+    /**
+     *音频质量
+     *
+     * Generated from protobuf field <code>string Quality = 19;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setQuality($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Quality = $var;
+
+        return $this;
+    }
+
+    /**
+     * 弹幕蒙版偏移量
+     *
+     * Generated from protobuf field <code>string BarrageMaskOffset = 20;</code>
+     * @return string
+     */
+    public function getBarrageMaskOffset()
+    {
+        return $this->BarrageMaskOffset;
+    }
+
+    /**
+     * 弹幕蒙版偏移量
+     *
+     * Generated from protobuf field <code>string BarrageMaskOffset = 20;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setBarrageMaskOffset($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->BarrageMaskOffset = $var;
+
+        return $this;
+    }
+
+    /**
+     *时长
+     *
+     * Generated from protobuf field <code>float Duration = 21;</code>
+     * @return float
+     */
+    public function getDuration()
+    {
+        return $this->Duration;
+    }
+
+    /**
+     *时长
+     *
+     * Generated from protobuf field <code>float Duration = 21;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setDuration($var)
+    {
+        GPBUtil::checkFloat($var);
+        $this->Duration = $var;
+
+        return $this;
+    }
+
+    /**
+     * 在转对齐,使用的帧对齐转码版本
+     *
+     * Generated from protobuf field <code>string KeyFrameAlignment = 22;</code>
+     * @return string
+     */
+    public function getKeyFrameAlignment()
+    {
+        return $this->KeyFrameAlignment;
+    }
+
+    /**
+     * 在转对齐,使用的帧对齐转码版本
+     *
+     * Generated from protobuf field <code>string KeyFrameAlignment = 22;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setKeyFrameAlignment($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->KeyFrameAlignment = $var;
+
+        return $this;
+    }
+
+    /**
+     *响度信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodVolumeInfo Volume = 23;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodVolumeInfo|null
+     */
+    public function getVolume()
+    {
+        return $this->Volume;
+    }
+
+    public function hasVolume()
+    {
+        return isset($this->Volume);
+    }
+
+    public function clearVolume()
+    {
+        unset($this->Volume);
+    }
+
+    /**
+     *响度信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodVolumeInfo Volume = 23;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodVolumeInfo $var
+     * @return $this
+     */
+    public function setVolume($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodVolumeInfo::class);
+        $this->Volume = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodPlayInfoModel.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodPlayInfoModel.php
new file mode 100755
index 0000000..cda0ef8
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodPlayInfoModel.php
@@ -0,0 +1,483 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_common.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodPlayInfoModel</code>
+ */
+class VodPlayInfoModel extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 数据版本
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodPlayInfoModelVersion Version = 10;</code>
+     */
+    protected $Version = 0;
+    /**
+     * 唯一ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     */
+    protected $Vid = '';
+    /**
+     * 状态
+     *
+     * Generated from protobuf field <code>int32 Status = 2;</code>
+     */
+    protected $Status = 0;
+    /**
+     * 封面地址
+     *
+     * Generated from protobuf field <code>string PosterUrl = 3;</code>
+     */
+    protected $PosterUrl = '';
+    /**
+     * 播放时长(单位:s)
+     *
+     * Generated from protobuf field <code>float Duration = 4;</code>
+     */
+    protected $Duration = 0.0;
+    /**
+     * 媒体类型
+     *
+     * Generated from protobuf field <code>string FileType = 5;</code>
+     */
+    protected $FileType = '';
+    /**
+     * 是否关键针对齐
+     *
+     * Generated from protobuf field <code>bool EnableAdaptive = 6;</code>
+     */
+    protected $EnableAdaptive = false;
+    /**
+     * 播放列表数量
+     *
+     * Generated from protobuf field <code>int32 TotalCount = 7;</code>
+     */
+    protected $TotalCount = 0;
+    /**
+     * dash播放信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodAdaptiveInfo AdaptiveInfo = 8;</code>
+     */
+    protected $AdaptiveInfo = null;
+    /**
+     * 播放列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodPlayInfo PlayInfoList = 9;</code>
+     */
+    private $PlayInfoList;
+    /**
+     * 雪碧图列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodThumbInfo ThumbInfoList = 11;</code>
+     */
+    private $ThumbInfoList;
+    /**
+     * 弹幕蒙版url
+     *
+     * Generated from protobuf field <code>string BarrageMaskUrl = 12;</code>
+     */
+    protected $BarrageMaskUrl = '';
+    /**
+     * 字幕信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSubtitleInfo SubtitleInfoList = 13;</code>
+     */
+    private $SubtitleInfoList;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int $Version
+     *           数据版本
+     *     @type string $Vid
+     *           唯一ID
+     *     @type int $Status
+     *           状态
+     *     @type string $PosterUrl
+     *           封面地址
+     *     @type float $Duration
+     *           播放时长(单位:s)
+     *     @type string $FileType
+     *           媒体类型
+     *     @type bool $EnableAdaptive
+     *           是否关键针对齐
+     *     @type int $TotalCount
+     *           播放列表数量
+     *     @type \Volc\Service\Vod\Models\Business\VodAdaptiveInfo $AdaptiveInfo
+     *           dash播放信息
+     *     @type \Volc\Service\Vod\Models\Business\VodPlayInfo[]|\Google\Protobuf\Internal\RepeatedField $PlayInfoList
+     *           播放列表
+     *     @type \Volc\Service\Vod\Models\Business\VodThumbInfo[]|\Google\Protobuf\Internal\RepeatedField $ThumbInfoList
+     *           雪碧图列表
+     *     @type string $BarrageMaskUrl
+     *           弹幕蒙版url
+     *     @type \Volc\Service\Vod\Models\Business\VodSubtitleInfo[]|\Google\Protobuf\Internal\RepeatedField $SubtitleInfoList
+     *           字幕信息列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCommon::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 数据版本
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodPlayInfoModelVersion Version = 10;</code>
+     * @return int
+     */
+    public function getVersion()
+    {
+        return $this->Version;
+    }
+
+    /**
+     * 数据版本
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodPlayInfoModelVersion Version = 10;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setVersion($var)
+    {
+        GPBUtil::checkEnum($var, \Volc\Service\Vod\Models\Business\VodPlayInfoModelVersion::class);
+        $this->Version = $var;
+
+        return $this;
+    }
+
+    /**
+     * 唯一ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     * 唯一ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     * 状态
+     *
+     * Generated from protobuf field <code>int32 Status = 2;</code>
+     * @return int
+     */
+    public function getStatus()
+    {
+        return $this->Status;
+    }
+
+    /**
+     * 状态
+     *
+     * Generated from protobuf field <code>int32 Status = 2;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setStatus($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Status = $var;
+
+        return $this;
+    }
+
+    /**
+     * 封面地址
+     *
+     * Generated from protobuf field <code>string PosterUrl = 3;</code>
+     * @return string
+     */
+    public function getPosterUrl()
+    {
+        return $this->PosterUrl;
+    }
+
+    /**
+     * 封面地址
+     *
+     * Generated from protobuf field <code>string PosterUrl = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setPosterUrl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->PosterUrl = $var;
+
+        return $this;
+    }
+
+    /**
+     * 播放时长(单位:s)
+     *
+     * Generated from protobuf field <code>float Duration = 4;</code>
+     * @return float
+     */
+    public function getDuration()
+    {
+        return $this->Duration;
+    }
+
+    /**
+     * 播放时长(单位:s)
+     *
+     * Generated from protobuf field <code>float Duration = 4;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setDuration($var)
+    {
+        GPBUtil::checkFloat($var);
+        $this->Duration = $var;
+
+        return $this;
+    }
+
+    /**
+     * 媒体类型
+     *
+     * Generated from protobuf field <code>string FileType = 5;</code>
+     * @return string
+     */
+    public function getFileType()
+    {
+        return $this->FileType;
+    }
+
+    /**
+     * 媒体类型
+     *
+     * Generated from protobuf field <code>string FileType = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileType = $var;
+
+        return $this;
+    }
+
+    /**
+     * 是否关键针对齐
+     *
+     * Generated from protobuf field <code>bool EnableAdaptive = 6;</code>
+     * @return bool
+     */
+    public function getEnableAdaptive()
+    {
+        return $this->EnableAdaptive;
+    }
+
+    /**
+     * 是否关键针对齐
+     *
+     * Generated from protobuf field <code>bool EnableAdaptive = 6;</code>
+     * @param bool $var
+     * @return $this
+     */
+    public function setEnableAdaptive($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->EnableAdaptive = $var;
+
+        return $this;
+    }
+
+    /**
+     * 播放列表数量
+     *
+     * Generated from protobuf field <code>int32 TotalCount = 7;</code>
+     * @return int
+     */
+    public function getTotalCount()
+    {
+        return $this->TotalCount;
+    }
+
+    /**
+     * 播放列表数量
+     *
+     * Generated from protobuf field <code>int32 TotalCount = 7;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setTotalCount($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->TotalCount = $var;
+
+        return $this;
+    }
+
+    /**
+     * dash播放信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodAdaptiveInfo AdaptiveInfo = 8;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodAdaptiveInfo|null
+     */
+    public function getAdaptiveInfo()
+    {
+        return $this->AdaptiveInfo;
+    }
+
+    public function hasAdaptiveInfo()
+    {
+        return isset($this->AdaptiveInfo);
+    }
+
+    public function clearAdaptiveInfo()
+    {
+        unset($this->AdaptiveInfo);
+    }
+
+    /**
+     * dash播放信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodAdaptiveInfo AdaptiveInfo = 8;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodAdaptiveInfo $var
+     * @return $this
+     */
+    public function setAdaptiveInfo($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodAdaptiveInfo::class);
+        $this->AdaptiveInfo = $var;
+
+        return $this;
+    }
+
+    /**
+     * 播放列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodPlayInfo PlayInfoList = 9;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getPlayInfoList()
+    {
+        return $this->PlayInfoList;
+    }
+
+    /**
+     * 播放列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodPlayInfo PlayInfoList = 9;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodPlayInfo[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setPlayInfoList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodPlayInfo::class);
+        $this->PlayInfoList = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 雪碧图列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodThumbInfo ThumbInfoList = 11;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getThumbInfoList()
+    {
+        return $this->ThumbInfoList;
+    }
+
+    /**
+     * 雪碧图列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodThumbInfo ThumbInfoList = 11;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodThumbInfo[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setThumbInfoList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodThumbInfo::class);
+        $this->ThumbInfoList = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 弹幕蒙版url
+     *
+     * Generated from protobuf field <code>string BarrageMaskUrl = 12;</code>
+     * @return string
+     */
+    public function getBarrageMaskUrl()
+    {
+        return $this->BarrageMaskUrl;
+    }
+
+    /**
+     * 弹幕蒙版url
+     *
+     * Generated from protobuf field <code>string BarrageMaskUrl = 12;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setBarrageMaskUrl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->BarrageMaskUrl = $var;
+
+        return $this;
+    }
+
+    /**
+     * 字幕信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSubtitleInfo SubtitleInfoList = 13;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getSubtitleInfoList()
+    {
+        return $this->SubtitleInfoList;
+    }
+
+    /**
+     * 字幕信息列表
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSubtitleInfo SubtitleInfoList = 13;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodSubtitleInfo[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setSubtitleInfoList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodSubtitleInfo::class);
+        $this->SubtitleInfoList = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodPlayInfoModelVersion.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodPlayInfoModelVersion.php
new file mode 100755
index 0000000..3d35ba7
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodPlayInfoModelVersion.php
@@ -0,0 +1,67 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_common.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use UnexpectedValueException;
+
+/**
+ * Protobuf type <code>Volcengine.Vod.Models.Business.VodPlayInfoModelVersion</code>
+ */
+class VodPlayInfoModelVersion
+{
+    /**
+     * 未定义的数据版本
+     *
+     * Generated from protobuf enum <code>UndefinedVodPlayInfoModelVersion = 0;</code>
+     */
+    const UndefinedVodPlayInfoModelVersion = 0;
+    /**
+     * Generated from protobuf enum <code>InternalV1VodPlayInfoModelVersion = 1;</code>
+     */
+    const InternalV1VodPlayInfoModelVersion = 1;
+    /**
+     * Generated from protobuf enum <code>InternalV2VodPlayInfoModelVersion = 2;</code>
+     */
+    const InternalV2VodPlayInfoModelVersion = 2;
+    /**
+     * Generated from protobuf enum <code>InternalV3VodPlayInfoModelVersion = 3;</code>
+     */
+    const InternalV3VodPlayInfoModelVersion = 3;
+    /**
+     * ToB第一版
+     *
+     * Generated from protobuf enum <code>ToBV1VodPlayInfoModelVersion = 4;</code>
+     */
+    const ToBV1VodPlayInfoModelVersion = 4;
+
+    private static $valueToName = [
+        self::UndefinedVodPlayInfoModelVersion => 'UndefinedVodPlayInfoModelVersion',
+        self::InternalV1VodPlayInfoModelVersion => 'InternalV1VodPlayInfoModelVersion',
+        self::InternalV2VodPlayInfoModelVersion => 'InternalV2VodPlayInfoModelVersion',
+        self::InternalV3VodPlayInfoModelVersion => 'InternalV3VodPlayInfoModelVersion',
+        self::ToBV1VodPlayInfoModelVersion => 'ToBV1VodPlayInfoModelVersion',
+    ];
+
+    public static function name($value)
+    {
+        if (!isset(self::$valueToName[$value])) {
+            throw new UnexpectedValueException(sprintf(
+                    'Enum %s has no name defined for value %s', __CLASS__, $value));
+        }
+        return self::$valueToName[$value];
+    }
+
+
+    public static function value($name)
+    {
+        $const = __CLASS__ . '::' . strtoupper($name);
+        if (!defined($const)) {
+            throw new UnexpectedValueException(sprintf(
+                    'Enum %s has no value defined for name %s', __CLASS__, $name));
+        }
+        return constant($const);
+    }
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodPlayInfoWithLiveTimeShiftScene.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodPlayInfoWithLiveTimeShiftScene.php
new file mode 100755
index 0000000..552b0c7
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodPlayInfoWithLiveTimeShiftScene.php
@@ -0,0 +1,167 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_play.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodPlayInfoWithLiveTimeShiftScene</code>
+ */
+class VodPlayInfoWithLiveTimeShiftScene extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * URI
+     *
+     * Generated from protobuf field <code>string StoreUri = 1;</code>
+     */
+    protected $StoreUri = '';
+    /**
+     * 主播放URL
+     *
+     * Generated from protobuf field <code>string MainPlayUrl = 2;</code>
+     */
+    protected $MainPlayUrl = '';
+    /**
+     * 备播放URL,可能不存在
+     *
+     * Generated from protobuf field <code>string BackupPlayUrl = 3;</code>
+     */
+    protected $BackupPlayUrl = '';
+    /**
+     * 过期DDL,Unix时间戳,单位:秒
+     *
+     * Generated from protobuf field <code>double UrlExpire = 4;</code>
+     */
+    protected $UrlExpire = 0.0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $StoreUri
+     *           URI
+     *     @type string $MainPlayUrl
+     *           主播放URL
+     *     @type string $BackupPlayUrl
+     *           备播放URL,可能不存在
+     *     @type float $UrlExpire
+     *           过期DDL,Unix时间戳,单位:秒
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodPlay::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * URI
+     *
+     * Generated from protobuf field <code>string StoreUri = 1;</code>
+     * @return string
+     */
+    public function getStoreUri()
+    {
+        return $this->StoreUri;
+    }
+
+    /**
+     * URI
+     *
+     * Generated from protobuf field <code>string StoreUri = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStoreUri($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StoreUri = $var;
+
+        return $this;
+    }
+
+    /**
+     * 主播放URL
+     *
+     * Generated from protobuf field <code>string MainPlayUrl = 2;</code>
+     * @return string
+     */
+    public function getMainPlayUrl()
+    {
+        return $this->MainPlayUrl;
+    }
+
+    /**
+     * 主播放URL
+     *
+     * Generated from protobuf field <code>string MainPlayUrl = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setMainPlayUrl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->MainPlayUrl = $var;
+
+        return $this;
+    }
+
+    /**
+     * 备播放URL,可能不存在
+     *
+     * Generated from protobuf field <code>string BackupPlayUrl = 3;</code>
+     * @return string
+     */
+    public function getBackupPlayUrl()
+    {
+        return $this->BackupPlayUrl;
+    }
+
+    /**
+     * 备播放URL,可能不存在
+     *
+     * Generated from protobuf field <code>string BackupPlayUrl = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setBackupPlayUrl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->BackupPlayUrl = $var;
+
+        return $this;
+    }
+
+    /**
+     * 过期DDL,Unix时间戳,单位:秒
+     *
+     * Generated from protobuf field <code>double UrlExpire = 4;</code>
+     * @return float
+     */
+    public function getUrlExpire()
+    {
+        return $this->UrlExpire;
+    }
+
+    /**
+     * 过期DDL,Unix时间戳,单位:秒
+     *
+     * Generated from protobuf field <code>double UrlExpire = 4;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setUrlExpire($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->UrlExpire = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodPoint.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodPoint.php
new file mode 100755
index 0000000..d505ee4
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodPoint.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_common.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodPoint</code>
+ */
+class VodPoint extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 时间戳
+     *
+     * Generated from protobuf field <code>double Timestamp = 1;</code>
+     */
+    protected $Timestamp = 0.0;
+    /**
+     * 数值
+     *
+     * Generated from protobuf field <code>double Value = 2;</code>
+     */
+    protected $Value = 0.0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type float $Timestamp
+     *           时间戳
+     *     @type float $Value
+     *           数值
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCommon::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 时间戳
+     *
+     * Generated from protobuf field <code>double Timestamp = 1;</code>
+     * @return float
+     */
+    public function getTimestamp()
+    {
+        return $this->Timestamp;
+    }
+
+    /**
+     * 时间戳
+     *
+     * Generated from protobuf field <code>double Timestamp = 1;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setTimestamp($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->Timestamp = $var;
+
+        return $this;
+    }
+
+    /**
+     * 数值
+     *
+     * Generated from protobuf field <code>double Value = 2;</code>
+     * @return float
+     */
+    public function getValue()
+    {
+        return $this->Value;
+    }
+
+    /**
+     * 数值
+     *
+     * Generated from protobuf field <code>double Value = 2;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setValue($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->Value = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodPrivateDrmPlayAuthInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodPrivateDrmPlayAuthInfo.php
new file mode 100755
index 0000000..7ae90b5
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodPrivateDrmPlayAuthInfo.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_play.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodPrivateDrmPlayAuthInfo</code>
+ */
+class VodPrivateDrmPlayAuthInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 播放许可id
+     *
+     * Generated from protobuf field <code>string PlayAuthId = 1;</code>
+     */
+    protected $PlayAuthId = '';
+    /**
+     * 播放许可内容
+     *
+     * Generated from protobuf field <code>string PlayAuthContent = 2;</code>
+     */
+    protected $PlayAuthContent = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $PlayAuthId
+     *           播放许可id
+     *     @type string $PlayAuthContent
+     *           播放许可内容
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodPlay::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 播放许可id
+     *
+     * Generated from protobuf field <code>string PlayAuthId = 1;</code>
+     * @return string
+     */
+    public function getPlayAuthId()
+    {
+        return $this->PlayAuthId;
+    }
+
+    /**
+     * 播放许可id
+     *
+     * Generated from protobuf field <code>string PlayAuthId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setPlayAuthId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->PlayAuthId = $var;
+
+        return $this;
+    }
+
+    /**
+     * 播放许可内容
+     *
+     * Generated from protobuf field <code>string PlayAuthContent = 2;</code>
+     * @return string
+     */
+    public function getPlayAuthContent()
+    {
+        return $this->PlayAuthContent;
+    }
+
+    /**
+     * 播放许可内容
+     *
+     * Generated from protobuf field <code>string PlayAuthContent = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setPlayAuthContent($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->PlayAuthContent = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodQueryData.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodQueryData.php
new file mode 100755
index 0000000..21bcbc5
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodQueryData.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_upload.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodQueryData</code>
+ */
+class VodQueryData extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * URL批量上传状态查询结果 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodQueryUploadResult Data = 1;</code>
+     */
+    protected $Data = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\VodQueryUploadResult $Data
+     *           URL批量上传状态查询结果 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodUpload::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * URL批量上传状态查询结果 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodQueryUploadResult Data = 1;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodQueryUploadResult|null
+     */
+    public function getData()
+    {
+        return $this->Data;
+    }
+
+    public function hasData()
+    {
+        return isset($this->Data);
+    }
+
+    public function clearData()
+    {
+        unset($this->Data);
+    }
+
+    /**
+     * URL批量上传状态查询结果 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodQueryUploadResult Data = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodQueryUploadResult $var
+     * @return $this
+     */
+    public function setData($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodQueryUploadResult::class);
+        $this->Data = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodQueryUploadResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodQueryUploadResult.php
new file mode 100755
index 0000000..5b0ae55
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodQueryUploadResult.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_upload.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodQueryUploadResult</code>
+ */
+class VodQueryUploadResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * URL批量上传状态查询返回的 JobId 对应的信息 
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodURLSet MediaInfoList = 1;</code>
+     */
+    private $MediaInfoList;
+    /**
+     * URL批量上传状态查询不存在的 JobId 
+     *
+     * Generated from protobuf field <code>repeated string NotExistJobIds = 2;</code>
+     */
+    private $NotExistJobIds;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\VodURLSet[]|\Google\Protobuf\Internal\RepeatedField $MediaInfoList
+     *           URL批量上传状态查询返回的 JobId 对应的信息 
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $NotExistJobIds
+     *           URL批量上传状态查询不存在的 JobId 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodUpload::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * URL批量上传状态查询返回的 JobId 对应的信息 
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodURLSet MediaInfoList = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getMediaInfoList()
+    {
+        return $this->MediaInfoList;
+    }
+
+    /**
+     * URL批量上传状态查询返回的 JobId 对应的信息 
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodURLSet MediaInfoList = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodURLSet[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setMediaInfoList($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodURLSet::class);
+        $this->MediaInfoList = $arr;
+
+        return $this;
+    }
+
+    /**
+     * URL批量上传状态查询不存在的 JobId 
+     *
+     * Generated from protobuf field <code>repeated string NotExistJobIds = 2;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getNotExistJobIds()
+    {
+        return $this->NotExistJobIds;
+    }
+
+    /**
+     * URL批量上传状态查询不存在的 JobId 
+     *
+     * Generated from protobuf field <code>repeated string NotExistJobIds = 2;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setNotExistJobIds($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->NotExistJobIds = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSamplePosterSnapshot.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSamplePosterSnapshot.php
new file mode 100755
index 0000000..1a96170
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSamplePosterSnapshot.php
@@ -0,0 +1,303 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodSamplePosterSnapshot</code>
+ */
+class VodSamplePosterSnapshot extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *多张雪碧图地址,item为string
+     *
+     * Generated from protobuf field <code>repeated string StoreUris = 1;</code>
+     */
+    private $StoreUris;
+    /**
+     *小图数量
+     *
+     * Generated from protobuf field <code>int32 ImgNum = 2;</code>
+     */
+    protected $ImgNum = 0;
+    /**
+     *小图宽
+     *
+     * Generated from protobuf field <code>int32 CellWidth = 3;</code>
+     */
+    protected $CellWidth = 0;
+    /**
+     *小图高
+     *
+     * Generated from protobuf field <code>int32 CellHeight = 4;</code>
+     */
+    protected $CellHeight = 0;
+    /**
+     *截图间隔
+     *
+     * Generated from protobuf field <code>float Interval = 5;</code>
+     */
+    protected $Interval = 0.0;
+    /**
+     *格式,目前默认 'jpg'
+     *
+     * Generated from protobuf field <code>string Format = 6;</code>
+     */
+    protected $Format = '';
+    /**
+     *视频时长
+     *
+     * Generated from protobuf field <code>float Duration = 7;</code>
+     */
+    protected $Duration = 0.0;
+    /**
+     *图片地址列表
+     *
+     * Generated from protobuf field <code>repeated string Urls = 8;</code>
+     */
+    private $Urls;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $StoreUris
+     *          多张雪碧图地址,item为string
+     *     @type int $ImgNum
+     *          小图数量
+     *     @type int $CellWidth
+     *          小图宽
+     *     @type int $CellHeight
+     *          小图高
+     *     @type float $Interval
+     *          截图间隔
+     *     @type string $Format
+     *          格式,目前默认 'jpg'
+     *     @type float $Duration
+     *          视频时长
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $Urls
+     *          图片地址列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *多张雪碧图地址,item为string
+     *
+     * Generated from protobuf field <code>repeated string StoreUris = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getStoreUris()
+    {
+        return $this->StoreUris;
+    }
+
+    /**
+     *多张雪碧图地址,item为string
+     *
+     * Generated from protobuf field <code>repeated string StoreUris = 1;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setStoreUris($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->StoreUris = $arr;
+
+        return $this;
+    }
+
+    /**
+     *小图数量
+     *
+     * Generated from protobuf field <code>int32 ImgNum = 2;</code>
+     * @return int
+     */
+    public function getImgNum()
+    {
+        return $this->ImgNum;
+    }
+
+    /**
+     *小图数量
+     *
+     * Generated from protobuf field <code>int32 ImgNum = 2;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setImgNum($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->ImgNum = $var;
+
+        return $this;
+    }
+
+    /**
+     *小图宽
+     *
+     * Generated from protobuf field <code>int32 CellWidth = 3;</code>
+     * @return int
+     */
+    public function getCellWidth()
+    {
+        return $this->CellWidth;
+    }
+
+    /**
+     *小图宽
+     *
+     * Generated from protobuf field <code>int32 CellWidth = 3;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setCellWidth($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->CellWidth = $var;
+
+        return $this;
+    }
+
+    /**
+     *小图高
+     *
+     * Generated from protobuf field <code>int32 CellHeight = 4;</code>
+     * @return int
+     */
+    public function getCellHeight()
+    {
+        return $this->CellHeight;
+    }
+
+    /**
+     *小图高
+     *
+     * Generated from protobuf field <code>int32 CellHeight = 4;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setCellHeight($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->CellHeight = $var;
+
+        return $this;
+    }
+
+    /**
+     *截图间隔
+     *
+     * Generated from protobuf field <code>float Interval = 5;</code>
+     * @return float
+     */
+    public function getInterval()
+    {
+        return $this->Interval;
+    }
+
+    /**
+     *截图间隔
+     *
+     * Generated from protobuf field <code>float Interval = 5;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setInterval($var)
+    {
+        GPBUtil::checkFloat($var);
+        $this->Interval = $var;
+
+        return $this;
+    }
+
+    /**
+     *格式,目前默认 'jpg'
+     *
+     * Generated from protobuf field <code>string Format = 6;</code>
+     * @return string
+     */
+    public function getFormat()
+    {
+        return $this->Format;
+    }
+
+    /**
+     *格式,目前默认 'jpg'
+     *
+     * Generated from protobuf field <code>string Format = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFormat($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Format = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频时长
+     *
+     * Generated from protobuf field <code>float Duration = 7;</code>
+     * @return float
+     */
+    public function getDuration()
+    {
+        return $this->Duration;
+    }
+
+    /**
+     *视频时长
+     *
+     * Generated from protobuf field <code>float Duration = 7;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setDuration($var)
+    {
+        GPBUtil::checkFloat($var);
+        $this->Duration = $var;
+
+        return $this;
+    }
+
+    /**
+     *图片地址列表
+     *
+     * Generated from protobuf field <code>repeated string Urls = 8;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getUrls()
+    {
+        return $this->Urls;
+    }
+
+    /**
+     *图片地址列表
+     *
+     * Generated from protobuf field <code>repeated string Urls = 8;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setUrls($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->Urls = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSmartStrategyDataStoreStatus.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSmartStrategyDataStoreStatus.php
new file mode 100755
index 0000000..30772d1
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSmartStrategyDataStoreStatus.php
@@ -0,0 +1,68 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_smart_strategy.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use UnexpectedValueException;
+
+/**
+ * 数据存储状态
+ *
+ * Protobuf type <code>Volcengine.Vod.Models.Business.VodSmartStrategyDataStoreStatus</code>
+ */
+class VodSmartStrategyDataStoreStatus
+{
+    /**
+     * 未定义的
+     *
+     * Generated from protobuf enum <code>UndefinedVodSmartStrategyDataStoreStatus = 0;</code>
+     */
+    const UndefinedVodSmartStrategyDataStoreStatus = 0;
+    /**
+     * VDA中不存在
+     *
+     * Generated from protobuf enum <code>NotExistVodSmartStrategyDataStoreStatus = 1;</code>
+     */
+    const NotExistVodSmartStrategyDataStoreStatus = 1;
+    /**
+     * VDA中存在源片
+     *
+     * Generated from protobuf enum <code>HasOriginalStreamVodSmartStrategyDataStoreStatus = 2;</code>
+     */
+    const HasOriginalStreamVodSmartStrategyDataStoreStatus = 2;
+    /**
+     * VDA中存在转码流
+     *
+     * Generated from protobuf enum <code>HasEncodeStreamVodSmartStrategyDataStoreStatus = 3;</code>
+     */
+    const HasEncodeStreamVodSmartStrategyDataStoreStatus = 3;
+
+    private static $valueToName = [
+        self::UndefinedVodSmartStrategyDataStoreStatus => 'UndefinedVodSmartStrategyDataStoreStatus',
+        self::NotExistVodSmartStrategyDataStoreStatus => 'NotExistVodSmartStrategyDataStoreStatus',
+        self::HasOriginalStreamVodSmartStrategyDataStoreStatus => 'HasOriginalStreamVodSmartStrategyDataStoreStatus',
+        self::HasEncodeStreamVodSmartStrategyDataStoreStatus => 'HasEncodeStreamVodSmartStrategyDataStoreStatus',
+    ];
+
+    public static function name($value)
+    {
+        if (!isset(self::$valueToName[$value])) {
+            throw new UnexpectedValueException(sprintf(
+                    'Enum %s has no name defined for value %s', __CLASS__, $value));
+        }
+        return self::$valueToName[$value];
+    }
+
+
+    public static function value($name)
+    {
+        $const = __CLASS__ . '::' . strtoupper($name);
+        if (!defined($const)) {
+            throw new UnexpectedValueException(sprintf(
+                    'Enum %s has no value defined for name %s', __CLASS__, $name));
+        }
+        return constant($const);
+    }
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSmartStrategyResponseStreamType.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSmartStrategyResponseStreamType.php
new file mode 100755
index 0000000..1eeef3d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSmartStrategyResponseStreamType.php
@@ -0,0 +1,68 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_smart_strategy.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use UnexpectedValueException;
+
+/**
+ * 响应数据流类型
+ *
+ * Protobuf type <code>Volcengine.Vod.Models.Business.VodSmartStrategyResponseStreamType</code>
+ */
+class VodSmartStrategyResponseStreamType
+{
+    /**
+     * 未定义的(保留置空)
+     *
+     * Generated from protobuf enum <code>UndefinedVodSmartStrategyResponseStreamType = 0;</code>
+     */
+    const UndefinedVodSmartStrategyResponseStreamType = 0;
+    /**
+     * 返回原片流
+     *
+     * Generated from protobuf enum <code>OriginalStreamVodSmartStrategyResponseStreamType = 1;</code>
+     */
+    const OriginalStreamVodSmartStrategyResponseStreamType = 1;
+    /**
+     * 返回匹配的策略转码流
+     *
+     * Generated from protobuf enum <code>StrategyEncodeStreamVodSmartStrategyResponseStreamType = 2;</code>
+     */
+    const StrategyEncodeStreamVodSmartStrategyResponseStreamType = 2;
+    /**
+     * 返回降级的转码流
+     *
+     * Generated from protobuf enum <code>FallbackEncodeStreamVodSmartStrategyResponseStreamType = 3;</code>
+     */
+    const FallbackEncodeStreamVodSmartStrategyResponseStreamType = 3;
+
+    private static $valueToName = [
+        self::UndefinedVodSmartStrategyResponseStreamType => 'UndefinedVodSmartStrategyResponseStreamType',
+        self::OriginalStreamVodSmartStrategyResponseStreamType => 'OriginalStreamVodSmartStrategyResponseStreamType',
+        self::StrategyEncodeStreamVodSmartStrategyResponseStreamType => 'StrategyEncodeStreamVodSmartStrategyResponseStreamType',
+        self::FallbackEncodeStreamVodSmartStrategyResponseStreamType => 'FallbackEncodeStreamVodSmartStrategyResponseStreamType',
+    ];
+
+    public static function name($value)
+    {
+        if (!isset(self::$valueToName[$value])) {
+            throw new UnexpectedValueException(sprintf(
+                    'Enum %s has no name defined for value %s', __CLASS__, $value));
+        }
+        return self::$valueToName[$value];
+    }
+
+
+    public static function value($name)
+    {
+        $const = __CLASS__ . '::' . strtoupper($name);
+        if (!defined($const)) {
+            throw new UnexpectedValueException(sprintf(
+                    'Enum %s has no value defined for name %s', __CLASS__, $name));
+        }
+        return constant($const);
+    }
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSnapshot.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSnapshot.php
new file mode 100755
index 0000000..030a8e9
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSnapshot.php
@@ -0,0 +1,201 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodSnapshot</code>
+ */
+class VodSnapshot extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *格式
+     *
+     * Generated from protobuf field <code>string Format = 1;</code>
+     */
+    protected $Format = '';
+    /**
+     *高
+     *
+     * Generated from protobuf field <code>int32 Height = 2;</code>
+     */
+    protected $Height = 0;
+    /**
+     *宽
+     *
+     * Generated from protobuf field <code>int32 Width = 3;</code>
+     */
+    protected $Width = 0;
+    /**
+     * Uri
+     *
+     * Generated from protobuf field <code>string StoreUri = 4;</code>
+     */
+    protected $StoreUri = '';
+    /**
+     *图片地址
+     *
+     * Generated from protobuf field <code>string Url = 5;</code>
+     */
+    protected $Url = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Format
+     *          格式
+     *     @type int $Height
+     *          高
+     *     @type int $Width
+     *          宽
+     *     @type string $StoreUri
+     *           Uri
+     *     @type string $Url
+     *          图片地址
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *格式
+     *
+     * Generated from protobuf field <code>string Format = 1;</code>
+     * @return string
+     */
+    public function getFormat()
+    {
+        return $this->Format;
+    }
+
+    /**
+     *格式
+     *
+     * Generated from protobuf field <code>string Format = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFormat($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Format = $var;
+
+        return $this;
+    }
+
+    /**
+     *高
+     *
+     * Generated from protobuf field <code>int32 Height = 2;</code>
+     * @return int
+     */
+    public function getHeight()
+    {
+        return $this->Height;
+    }
+
+    /**
+     *高
+     *
+     * Generated from protobuf field <code>int32 Height = 2;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setHeight($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Height = $var;
+
+        return $this;
+    }
+
+    /**
+     *宽
+     *
+     * Generated from protobuf field <code>int32 Width = 3;</code>
+     * @return int
+     */
+    public function getWidth()
+    {
+        return $this->Width;
+    }
+
+    /**
+     *宽
+     *
+     * Generated from protobuf field <code>int32 Width = 3;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setWidth($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Width = $var;
+
+        return $this;
+    }
+
+    /**
+     * Uri
+     *
+     * Generated from protobuf field <code>string StoreUri = 4;</code>
+     * @return string
+     */
+    public function getStoreUri()
+    {
+        return $this->StoreUri;
+    }
+
+    /**
+     * Uri
+     *
+     * Generated from protobuf field <code>string StoreUri = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStoreUri($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StoreUri = $var;
+
+        return $this;
+    }
+
+    /**
+     *图片地址
+     *
+     * Generated from protobuf field <code>string Url = 5;</code>
+     * @return string
+     */
+    public function getUrl()
+    {
+        return $this->Url;
+    }
+
+    /**
+     *图片地址
+     *
+     * Generated from protobuf field <code>string Url = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setUrl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Url = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSnapshotData.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSnapshotData.php
new file mode 100755
index 0000000..c9dcfb6
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSnapshotData.php
@@ -0,0 +1,303 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodSnapshotData</code>
+ */
+class VodSnapshotData extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *空间名
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 2;</code>
+     */
+    protected $Vid = '';
+    /**
+     *单张静图
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSnapshot PosterSnapshots = 3;</code>
+     */
+    private $PosterSnapshots;
+    /**
+     *普通动图
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSnapshot DynpostSnapshots = 4;</code>
+     */
+    private $DynpostSnapshots;
+    /**
+     *循环动图
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSnapshot AnimatedPosterSnapshots = 5;</code>
+     */
+    private $AnimatedPosterSnapshots;
+    /**
+     * Ai动图
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSnapshot AiDynpostSnapshots = 6;</code>
+     */
+    private $AiDynpostSnapshots;
+    /**
+     *雪碧图
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSpriteSnapshot SpriteSnapshots = 7;</code>
+     */
+    private $SpriteSnapshots;
+    /**
+     *视频采样截图
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSamplePosterSnapshot VSamplePosterSnapshots = 8;</code>
+     */
+    private $VSamplePosterSnapshots;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *          空间名
+     *     @type string $Vid
+     *          视频ID
+     *     @type \Volc\Service\Vod\Models\Business\VodSnapshot[]|\Google\Protobuf\Internal\RepeatedField $PosterSnapshots
+     *          单张静图
+     *     @type \Volc\Service\Vod\Models\Business\VodSnapshot[]|\Google\Protobuf\Internal\RepeatedField $DynpostSnapshots
+     *          普通动图
+     *     @type \Volc\Service\Vod\Models\Business\VodSnapshot[]|\Google\Protobuf\Internal\RepeatedField $AnimatedPosterSnapshots
+     *          循环动图
+     *     @type \Volc\Service\Vod\Models\Business\VodSnapshot[]|\Google\Protobuf\Internal\RepeatedField $AiDynpostSnapshots
+     *           Ai动图
+     *     @type \Volc\Service\Vod\Models\Business\VodSpriteSnapshot[]|\Google\Protobuf\Internal\RepeatedField $SpriteSnapshots
+     *          雪碧图
+     *     @type \Volc\Service\Vod\Models\Business\VodSamplePosterSnapshot[]|\Google\Protobuf\Internal\RepeatedField $VSamplePosterSnapshots
+     *          视频采样截图
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *空间名
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     *空间名
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 2;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     *单张静图
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSnapshot PosterSnapshots = 3;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getPosterSnapshots()
+    {
+        return $this->PosterSnapshots;
+    }
+
+    /**
+     *单张静图
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSnapshot PosterSnapshots = 3;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodSnapshot[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setPosterSnapshots($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodSnapshot::class);
+        $this->PosterSnapshots = $arr;
+
+        return $this;
+    }
+
+    /**
+     *普通动图
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSnapshot DynpostSnapshots = 4;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getDynpostSnapshots()
+    {
+        return $this->DynpostSnapshots;
+    }
+
+    /**
+     *普通动图
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSnapshot DynpostSnapshots = 4;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodSnapshot[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setDynpostSnapshots($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodSnapshot::class);
+        $this->DynpostSnapshots = $arr;
+
+        return $this;
+    }
+
+    /**
+     *循环动图
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSnapshot AnimatedPosterSnapshots = 5;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getAnimatedPosterSnapshots()
+    {
+        return $this->AnimatedPosterSnapshots;
+    }
+
+    /**
+     *循环动图
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSnapshot AnimatedPosterSnapshots = 5;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodSnapshot[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setAnimatedPosterSnapshots($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodSnapshot::class);
+        $this->AnimatedPosterSnapshots = $arr;
+
+        return $this;
+    }
+
+    /**
+     * Ai动图
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSnapshot AiDynpostSnapshots = 6;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getAiDynpostSnapshots()
+    {
+        return $this->AiDynpostSnapshots;
+    }
+
+    /**
+     * Ai动图
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSnapshot AiDynpostSnapshots = 6;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodSnapshot[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setAiDynpostSnapshots($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodSnapshot::class);
+        $this->AiDynpostSnapshots = $arr;
+
+        return $this;
+    }
+
+    /**
+     *雪碧图
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSpriteSnapshot SpriteSnapshots = 7;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getSpriteSnapshots()
+    {
+        return $this->SpriteSnapshots;
+    }
+
+    /**
+     *雪碧图
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSpriteSnapshot SpriteSnapshots = 7;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodSpriteSnapshot[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setSpriteSnapshots($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodSpriteSnapshot::class);
+        $this->SpriteSnapshots = $arr;
+
+        return $this;
+    }
+
+    /**
+     *视频采样截图
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSamplePosterSnapshot VSamplePosterSnapshots = 8;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getVSamplePosterSnapshots()
+    {
+        return $this->VSamplePosterSnapshots;
+    }
+
+    /**
+     *视频采样截图
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSamplePosterSnapshot VSamplePosterSnapshots = 8;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodSamplePosterSnapshot[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setVSamplePosterSnapshots($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodSamplePosterSnapshot::class);
+        $this->VSamplePosterSnapshots = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSourceInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSourceInfo.php
new file mode 100755
index 0000000..640fc1d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSourceInfo.php
@@ -0,0 +1,541 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_common.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodSourceInfo</code>
+ */
+class VodSourceInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *文件ID
+     *
+     * Generated from protobuf field <code>string FileId = 1;</code>
+     */
+    protected $FileId = '';
+    /**
+     * hash值
+     *
+     * Generated from protobuf field <code>string Md5 = 2;</code>
+     */
+    protected $Md5 = '';
+    /**
+     *文件类型 video/audio
+     *
+     * Generated from protobuf field <code>string FileType = 3;</code>
+     */
+    protected $FileType = '';
+    /**
+     *编码格式
+     *
+     * Generated from protobuf field <code>string Codec = 4;</code>
+     */
+    protected $Codec = '';
+    /**
+     *视频高度
+     *
+     * Generated from protobuf field <code>int32 Height = 5;</code>
+     */
+    protected $Height = 0;
+    /**
+     *视频宽度
+     *
+     * Generated from protobuf field <code>int32 Width = 6;</code>
+     */
+    protected $Width = 0;
+    /**
+     *文件格式
+     *
+     * Generated from protobuf field <code>string Format = 7;</code>
+     */
+    protected $Format = '';
+    /**
+     *时长
+     *
+     * Generated from protobuf field <code>float Duration = 8;</code>
+     */
+    protected $Duration = 0.0;
+    /**
+     *文件大小
+     *
+     * Generated from protobuf field <code>double Size = 9;</code>
+     */
+    protected $Size = 0.0;
+    /**
+     *对象地址
+     *
+     * Generated from protobuf field <code>string StoreUri = 10;</code>
+     */
+    protected $StoreUri = '';
+    /**
+     *视频分辨率
+     *
+     * Generated from protobuf field <code>string Definition = 11;</code>
+     */
+    protected $Definition = '';
+    /**
+     *码率(Kbps)
+     *
+     * Generated from protobuf field <code>int32 Bitrate = 12;</code>
+     */
+    protected $Bitrate = 0;
+    /**
+     *帧率
+     *
+     * Generated from protobuf field <code>float Fps = 13;</code>
+     */
+    protected $Fps = 0.0;
+    /**
+     *创建时间
+     *
+     * Generated from protobuf field <code>string CreateTime = 14;</code>
+     */
+    protected $CreateTime = '';
+    /**
+     *音频质量
+     *
+     * Generated from protobuf field <code>string Quality = 15;</code>
+     */
+    protected $Quality = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $FileId
+     *          文件ID
+     *     @type string $Md5
+     *           hash值
+     *     @type string $FileType
+     *          文件类型 video/audio
+     *     @type string $Codec
+     *          编码格式
+     *     @type int $Height
+     *          视频高度
+     *     @type int $Width
+     *          视频宽度
+     *     @type string $Format
+     *          文件格式
+     *     @type float $Duration
+     *          时长
+     *     @type float $Size
+     *          文件大小
+     *     @type string $StoreUri
+     *          对象地址
+     *     @type string $Definition
+     *          视频分辨率
+     *     @type int $Bitrate
+     *          码率(Kbps)
+     *     @type float $Fps
+     *          帧率
+     *     @type string $CreateTime
+     *          创建时间
+     *     @type string $Quality
+     *          音频质量
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCommon::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *文件ID
+     *
+     * Generated from protobuf field <code>string FileId = 1;</code>
+     * @return string
+     */
+    public function getFileId()
+    {
+        return $this->FileId;
+    }
+
+    /**
+     *文件ID
+     *
+     * Generated from protobuf field <code>string FileId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileId = $var;
+
+        return $this;
+    }
+
+    /**
+     * hash值
+     *
+     * Generated from protobuf field <code>string Md5 = 2;</code>
+     * @return string
+     */
+    public function getMd5()
+    {
+        return $this->Md5;
+    }
+
+    /**
+     * hash值
+     *
+     * Generated from protobuf field <code>string Md5 = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setMd5($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Md5 = $var;
+
+        return $this;
+    }
+
+    /**
+     *文件类型 video/audio
+     *
+     * Generated from protobuf field <code>string FileType = 3;</code>
+     * @return string
+     */
+    public function getFileType()
+    {
+        return $this->FileType;
+    }
+
+    /**
+     *文件类型 video/audio
+     *
+     * Generated from protobuf field <code>string FileType = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileType = $var;
+
+        return $this;
+    }
+
+    /**
+     *编码格式
+     *
+     * Generated from protobuf field <code>string Codec = 4;</code>
+     * @return string
+     */
+    public function getCodec()
+    {
+        return $this->Codec;
+    }
+
+    /**
+     *编码格式
+     *
+     * Generated from protobuf field <code>string Codec = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCodec($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Codec = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频高度
+     *
+     * Generated from protobuf field <code>int32 Height = 5;</code>
+     * @return int
+     */
+    public function getHeight()
+    {
+        return $this->Height;
+    }
+
+    /**
+     *视频高度
+     *
+     * Generated from protobuf field <code>int32 Height = 5;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setHeight($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Height = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频宽度
+     *
+     * Generated from protobuf field <code>int32 Width = 6;</code>
+     * @return int
+     */
+    public function getWidth()
+    {
+        return $this->Width;
+    }
+
+    /**
+     *视频宽度
+     *
+     * Generated from protobuf field <code>int32 Width = 6;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setWidth($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Width = $var;
+
+        return $this;
+    }
+
+    /**
+     *文件格式
+     *
+     * Generated from protobuf field <code>string Format = 7;</code>
+     * @return string
+     */
+    public function getFormat()
+    {
+        return $this->Format;
+    }
+
+    /**
+     *文件格式
+     *
+     * Generated from protobuf field <code>string Format = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFormat($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Format = $var;
+
+        return $this;
+    }
+
+    /**
+     *时长
+     *
+     * Generated from protobuf field <code>float Duration = 8;</code>
+     * @return float
+     */
+    public function getDuration()
+    {
+        return $this->Duration;
+    }
+
+    /**
+     *时长
+     *
+     * Generated from protobuf field <code>float Duration = 8;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setDuration($var)
+    {
+        GPBUtil::checkFloat($var);
+        $this->Duration = $var;
+
+        return $this;
+    }
+
+    /**
+     *文件大小
+     *
+     * Generated from protobuf field <code>double Size = 9;</code>
+     * @return float
+     */
+    public function getSize()
+    {
+        return $this->Size;
+    }
+
+    /**
+     *文件大小
+     *
+     * Generated from protobuf field <code>double Size = 9;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setSize($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->Size = $var;
+
+        return $this;
+    }
+
+    /**
+     *对象地址
+     *
+     * Generated from protobuf field <code>string StoreUri = 10;</code>
+     * @return string
+     */
+    public function getStoreUri()
+    {
+        return $this->StoreUri;
+    }
+
+    /**
+     *对象地址
+     *
+     * Generated from protobuf field <code>string StoreUri = 10;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStoreUri($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StoreUri = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频分辨率
+     *
+     * Generated from protobuf field <code>string Definition = 11;</code>
+     * @return string
+     */
+    public function getDefinition()
+    {
+        return $this->Definition;
+    }
+
+    /**
+     *视频分辨率
+     *
+     * Generated from protobuf field <code>string Definition = 11;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDefinition($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Definition = $var;
+
+        return $this;
+    }
+
+    /**
+     *码率(Kbps)
+     *
+     * Generated from protobuf field <code>int32 Bitrate = 12;</code>
+     * @return int
+     */
+    public function getBitrate()
+    {
+        return $this->Bitrate;
+    }
+
+    /**
+     *码率(Kbps)
+     *
+     * Generated from protobuf field <code>int32 Bitrate = 12;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setBitrate($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Bitrate = $var;
+
+        return $this;
+    }
+
+    /**
+     *帧率
+     *
+     * Generated from protobuf field <code>float Fps = 13;</code>
+     * @return float
+     */
+    public function getFps()
+    {
+        return $this->Fps;
+    }
+
+    /**
+     *帧率
+     *
+     * Generated from protobuf field <code>float Fps = 13;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setFps($var)
+    {
+        GPBUtil::checkFloat($var);
+        $this->Fps = $var;
+
+        return $this;
+    }
+
+    /**
+     *创建时间
+     *
+     * Generated from protobuf field <code>string CreateTime = 14;</code>
+     * @return string
+     */
+    public function getCreateTime()
+    {
+        return $this->CreateTime;
+    }
+
+    /**
+     *创建时间
+     *
+     * Generated from protobuf field <code>string CreateTime = 14;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCreateTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CreateTime = $var;
+
+        return $this;
+    }
+
+    /**
+     *音频质量
+     *
+     * Generated from protobuf field <code>string Quality = 15;</code>
+     * @return string
+     */
+    public function getQuality()
+    {
+        return $this->Quality;
+    }
+
+    /**
+     *音频质量
+     *
+     * Generated from protobuf field <code>string Quality = 15;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setQuality($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Quality = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSpaceInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSpaceInfo.php
new file mode 100755
index 0000000..060f0ea
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSpaceInfo.php
@@ -0,0 +1,371 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_space.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodSpaceInfo</code>
+ */
+class VodSpaceInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 空间名
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     * 所属区域
+     *
+     * Generated from protobuf field <code>string Region = 3;</code>
+     */
+    protected $Region = '';
+    /**
+     * IAM项目名称
+     *
+     * Generated from protobuf field <code>string ProjectName = 4;</code>
+     */
+    protected $ProjectName = '';
+    /**
+     * 绑定的TOS Bucket名称
+     *
+     * Generated from protobuf field <code>string BucketName = 5;</code>
+     */
+    protected $BucketName = '';
+    /**
+     * 绑定的TosBucket状态
+     *
+     * Generated from protobuf field <code>string BucketStatus = 6;</code>
+     */
+    protected $BucketStatus = '';
+    /**
+     * 描述
+     *
+     * Generated from protobuf field <code>string Description = 7;</code>
+     */
+    protected $Description = '';
+    /**
+     * 创建人
+     *
+     * Generated from protobuf field <code>string UserName = 8;</code>
+     */
+    protected $UserName = '';
+    /**
+     * 创建时间
+     *
+     * Generated from protobuf field <code>string CreatedAt = 9;</code>
+     */
+    protected $CreatedAt = '';
+    /**
+     * 类型
+     *
+     * Generated from protobuf field <code>string Type = 10;</code>
+     */
+    protected $Type = '';
+    /**
+     * 媒资同步类型
+     *
+     * Generated from protobuf field <code>string MediaSyncLevel = 11;</code>
+     */
+    protected $MediaSyncLevel = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *           空间名
+     *     @type string $Region
+     *           所属区域
+     *     @type string $ProjectName
+     *           IAM项目名称
+     *     @type string $BucketName
+     *           绑定的TOS Bucket名称
+     *     @type string $BucketStatus
+     *           绑定的TosBucket状态
+     *     @type string $Description
+     *           描述
+     *     @type string $UserName
+     *           创建人
+     *     @type string $CreatedAt
+     *           创建时间
+     *     @type string $Type
+     *           类型
+     *     @type string $MediaSyncLevel
+     *           媒资同步类型
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodSpace::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 空间名
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * 空间名
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 所属区域
+     *
+     * Generated from protobuf field <code>string Region = 3;</code>
+     * @return string
+     */
+    public function getRegion()
+    {
+        return $this->Region;
+    }
+
+    /**
+     * 所属区域
+     *
+     * Generated from protobuf field <code>string Region = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setRegion($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Region = $var;
+
+        return $this;
+    }
+
+    /**
+     * IAM项目名称
+     *
+     * Generated from protobuf field <code>string ProjectName = 4;</code>
+     * @return string
+     */
+    public function getProjectName()
+    {
+        return $this->ProjectName;
+    }
+
+    /**
+     * IAM项目名称
+     *
+     * Generated from protobuf field <code>string ProjectName = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setProjectName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->ProjectName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 绑定的TOS Bucket名称
+     *
+     * Generated from protobuf field <code>string BucketName = 5;</code>
+     * @return string
+     */
+    public function getBucketName()
+    {
+        return $this->BucketName;
+    }
+
+    /**
+     * 绑定的TOS Bucket名称
+     *
+     * Generated from protobuf field <code>string BucketName = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setBucketName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->BucketName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 绑定的TosBucket状态
+     *
+     * Generated from protobuf field <code>string BucketStatus = 6;</code>
+     * @return string
+     */
+    public function getBucketStatus()
+    {
+        return $this->BucketStatus;
+    }
+
+    /**
+     * 绑定的TosBucket状态
+     *
+     * Generated from protobuf field <code>string BucketStatus = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setBucketStatus($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->BucketStatus = $var;
+
+        return $this;
+    }
+
+    /**
+     * 描述
+     *
+     * Generated from protobuf field <code>string Description = 7;</code>
+     * @return string
+     */
+    public function getDescription()
+    {
+        return $this->Description;
+    }
+
+    /**
+     * 描述
+     *
+     * Generated from protobuf field <code>string Description = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDescription($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Description = $var;
+
+        return $this;
+    }
+
+    /**
+     * 创建人
+     *
+     * Generated from protobuf field <code>string UserName = 8;</code>
+     * @return string
+     */
+    public function getUserName()
+    {
+        return $this->UserName;
+    }
+
+    /**
+     * 创建人
+     *
+     * Generated from protobuf field <code>string UserName = 8;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setUserName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->UserName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 创建时间
+     *
+     * Generated from protobuf field <code>string CreatedAt = 9;</code>
+     * @return string
+     */
+    public function getCreatedAt()
+    {
+        return $this->CreatedAt;
+    }
+
+    /**
+     * 创建时间
+     *
+     * Generated from protobuf field <code>string CreatedAt = 9;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCreatedAt($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CreatedAt = $var;
+
+        return $this;
+    }
+
+    /**
+     * 类型
+     *
+     * Generated from protobuf field <code>string Type = 10;</code>
+     * @return string
+     */
+    public function getType()
+    {
+        return $this->Type;
+    }
+
+    /**
+     * 类型
+     *
+     * Generated from protobuf field <code>string Type = 10;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Type = $var;
+
+        return $this;
+    }
+
+    /**
+     * 媒资同步类型
+     *
+     * Generated from protobuf field <code>string MediaSyncLevel = 11;</code>
+     * @return string
+     */
+    public function getMediaSyncLevel()
+    {
+        return $this->MediaSyncLevel;
+    }
+
+    /**
+     * 媒资同步类型
+     *
+     * Generated from protobuf field <code>string MediaSyncLevel = 11;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setMediaSyncLevel($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->MediaSyncLevel = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSpaceUploadConfigKey.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSpaceUploadConfigKey.php
new file mode 100755
index 0000000..e9d36a8
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSpaceUploadConfigKey.php
@@ -0,0 +1,59 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_space.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use UnexpectedValueException;
+
+/**
+ * Protobuf type <code>Volcengine.Vod.Models.Business.VodSpaceUploadConfigKey</code>
+ */
+class VodSpaceUploadConfigKey
+{
+    /**
+     * 未定义的key
+     *
+     * Generated from protobuf enum <code>UndefinedVodSpaceUploadConfigKey = 0;</code>
+     */
+    const UndefinedVodSpaceUploadConfigKey = 0;
+    /**
+     * 自定义文件路径key
+     *
+     * Generated from protobuf enum <code>CustomUploadFilePathVodSpaceUploadConfigKey = 1;</code>
+     */
+    const CustomUploadFilePathVodSpaceUploadConfigKey = 1;
+    /**
+     * 上传完成后自动发布key
+     *
+     * Generated from protobuf enum <code>AutoPublishAfterUploadVodSpaceUploadConfigKey = 2;</code>
+     */
+    const AutoPublishAfterUploadVodSpaceUploadConfigKey = 2;
+
+    private static $valueToName = [
+        self::UndefinedVodSpaceUploadConfigKey => 'UndefinedVodSpaceUploadConfigKey',
+        self::CustomUploadFilePathVodSpaceUploadConfigKey => 'CustomUploadFilePathVodSpaceUploadConfigKey',
+        self::AutoPublishAfterUploadVodSpaceUploadConfigKey => 'AutoPublishAfterUploadVodSpaceUploadConfigKey',
+    ];
+
+    public static function name($value)
+    {
+        if (!isset(self::$valueToName[$value])) {
+            throw new UnexpectedValueException(sprintf(
+                    'Enum %s has no name defined for value %s', __CLASS__, $value));
+        }
+        return self::$valueToName[$value];
+    }
+
+
+    public static function value($name)
+    {
+        $const = __CLASS__ . '::' . strtoupper($name);
+        if (!defined($const)) {
+            throw new UnexpectedValueException(sprintf(
+                    'Enum %s has no value defined for name %s', __CLASS__, $name));
+        }
+        return constant($const);
+    }
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSpriteSnapshot.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSpriteSnapshot.php
new file mode 100755
index 0000000..3d345ea
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSpriteSnapshot.php
@@ -0,0 +1,337 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodSpriteSnapshot</code>
+ */
+class VodSpriteSnapshot extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *格式
+     *
+     * Generated from protobuf field <code>string Format = 1;</code>
+     */
+    protected $Format = '';
+    /**
+     *多图中横向数量
+     *
+     * Generated from protobuf field <code>int32 ImgXLen = 2;</code>
+     */
+    protected $ImgXLen = 0;
+    /**
+     *多图中纵向数量
+     *
+     * Generated from protobuf field <code>int32 ImgYLen = 3;</code>
+     */
+    protected $ImgYLen = 0;
+    /**
+     *小图宽
+     *
+     * Generated from protobuf field <code>int32 CellWidth = 4;</code>
+     */
+    protected $CellWidth = 0;
+    /**
+     *小图高
+     *
+     * Generated from protobuf field <code>int32 CellHeight = 5;</code>
+     */
+    protected $CellHeight = 0;
+    /**
+     *截图间隔
+     *
+     * Generated from protobuf field <code>float Interval = 6;</code>
+     */
+    protected $Interval = 0.0;
+    /**
+     *截图总数限制
+     *
+     * Generated from protobuf field <code>int32 CaptureNum = 7;</code>
+     */
+    protected $CaptureNum = 0;
+    /**
+     * Uri列表
+     *
+     * Generated from protobuf field <code>repeated string StoreUris = 8;</code>
+     */
+    private $StoreUris;
+    /**
+     *图片地址列表
+     *
+     * Generated from protobuf field <code>repeated string Urls = 9;</code>
+     */
+    private $Urls;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Format
+     *          格式
+     *     @type int $ImgXLen
+     *          多图中横向数量
+     *     @type int $ImgYLen
+     *          多图中纵向数量
+     *     @type int $CellWidth
+     *          小图宽
+     *     @type int $CellHeight
+     *          小图高
+     *     @type float $Interval
+     *          截图间隔
+     *     @type int $CaptureNum
+     *          截图总数限制
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $StoreUris
+     *           Uri列表
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $Urls
+     *          图片地址列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *格式
+     *
+     * Generated from protobuf field <code>string Format = 1;</code>
+     * @return string
+     */
+    public function getFormat()
+    {
+        return $this->Format;
+    }
+
+    /**
+     *格式
+     *
+     * Generated from protobuf field <code>string Format = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFormat($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Format = $var;
+
+        return $this;
+    }
+
+    /**
+     *多图中横向数量
+     *
+     * Generated from protobuf field <code>int32 ImgXLen = 2;</code>
+     * @return int
+     */
+    public function getImgXLen()
+    {
+        return $this->ImgXLen;
+    }
+
+    /**
+     *多图中横向数量
+     *
+     * Generated from protobuf field <code>int32 ImgXLen = 2;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setImgXLen($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->ImgXLen = $var;
+
+        return $this;
+    }
+
+    /**
+     *多图中纵向数量
+     *
+     * Generated from protobuf field <code>int32 ImgYLen = 3;</code>
+     * @return int
+     */
+    public function getImgYLen()
+    {
+        return $this->ImgYLen;
+    }
+
+    /**
+     *多图中纵向数量
+     *
+     * Generated from protobuf field <code>int32 ImgYLen = 3;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setImgYLen($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->ImgYLen = $var;
+
+        return $this;
+    }
+
+    /**
+     *小图宽
+     *
+     * Generated from protobuf field <code>int32 CellWidth = 4;</code>
+     * @return int
+     */
+    public function getCellWidth()
+    {
+        return $this->CellWidth;
+    }
+
+    /**
+     *小图宽
+     *
+     * Generated from protobuf field <code>int32 CellWidth = 4;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setCellWidth($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->CellWidth = $var;
+
+        return $this;
+    }
+
+    /**
+     *小图高
+     *
+     * Generated from protobuf field <code>int32 CellHeight = 5;</code>
+     * @return int
+     */
+    public function getCellHeight()
+    {
+        return $this->CellHeight;
+    }
+
+    /**
+     *小图高
+     *
+     * Generated from protobuf field <code>int32 CellHeight = 5;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setCellHeight($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->CellHeight = $var;
+
+        return $this;
+    }
+
+    /**
+     *截图间隔
+     *
+     * Generated from protobuf field <code>float Interval = 6;</code>
+     * @return float
+     */
+    public function getInterval()
+    {
+        return $this->Interval;
+    }
+
+    /**
+     *截图间隔
+     *
+     * Generated from protobuf field <code>float Interval = 6;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setInterval($var)
+    {
+        GPBUtil::checkFloat($var);
+        $this->Interval = $var;
+
+        return $this;
+    }
+
+    /**
+     *截图总数限制
+     *
+     * Generated from protobuf field <code>int32 CaptureNum = 7;</code>
+     * @return int
+     */
+    public function getCaptureNum()
+    {
+        return $this->CaptureNum;
+    }
+
+    /**
+     *截图总数限制
+     *
+     * Generated from protobuf field <code>int32 CaptureNum = 7;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setCaptureNum($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->CaptureNum = $var;
+
+        return $this;
+    }
+
+    /**
+     * Uri列表
+     *
+     * Generated from protobuf field <code>repeated string StoreUris = 8;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getStoreUris()
+    {
+        return $this->StoreUris;
+    }
+
+    /**
+     * Uri列表
+     *
+     * Generated from protobuf field <code>repeated string StoreUris = 8;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setStoreUris($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->StoreUris = $arr;
+
+        return $this;
+    }
+
+    /**
+     *图片地址列表
+     *
+     * Generated from protobuf field <code>repeated string Urls = 9;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getUrls()
+    {
+        return $this->Urls;
+    }
+
+    /**
+     *图片地址列表
+     *
+     * Generated from protobuf field <code>repeated string Urls = 9;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setUrls($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->Urls = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodStartWorkflowResult.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodStartWorkflowResult.php
new file mode 100755
index 0000000..6c6002f
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodStartWorkflowResult.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_workflow.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodStartWorkflowResult</code>
+ */
+class VodStartWorkflowResult extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 工作流执行Id
+     *
+     * Generated from protobuf field <code>string RunId = 1;</code>
+     */
+    protected $RunId = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $RunId
+     *           工作流执行Id
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodWorkflow::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 工作流执行Id
+     *
+     * Generated from protobuf field <code>string RunId = 1;</code>
+     * @return string
+     */
+    public function getRunId()
+    {
+        return $this->RunId;
+    }
+
+    /**
+     * 工作流执行Id
+     *
+     * Generated from protobuf field <code>string RunId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setRunId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->RunId = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodStorageData.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodStorageData.php
new file mode 100755
index 0000000..d900a34
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodStorageData.php
@@ -0,0 +1,85 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_space.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodStorageData</code>
+ */
+class VodStorageData extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>string Time = 1;</code>
+     */
+    protected $Time = '';
+    /**
+     * Generated from protobuf field <code>int64 Storage = 2;</code>
+     */
+    protected $Storage = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Time
+     *     @type int|string $Storage
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodSpace::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>string Time = 1;</code>
+     * @return string
+     */
+    public function getTime()
+    {
+        return $this->Time;
+    }
+
+    /**
+     * Generated from protobuf field <code>string Time = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Time = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>int64 Storage = 2;</code>
+     * @return int|string
+     */
+    public function getStorage()
+    {
+        return $this->Storage;
+    }
+
+    /**
+     * Generated from protobuf field <code>int64 Storage = 2;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setStorage($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->Storage = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodStoreInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodStoreInfo.php
new file mode 100755
index 0000000..9ce6434
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodStoreInfo.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_upload.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodStoreInfo</code>
+ */
+class VodStoreInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 1001 阶段返回存储的 uri 
+     *
+     * Generated from protobuf field <code>string StoreUri = 1;</code>
+     */
+    protected $StoreUri = '';
+    /**
+     * 1001 阶段返回上传使用的签名信息 
+     *
+     * Generated from protobuf field <code>string Auth = 2;</code>
+     */
+    protected $Auth = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $StoreUri
+     *           1001 阶段返回存储的 uri 
+     *     @type string $Auth
+     *           1001 阶段返回上传使用的签名信息 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodUpload::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 1001 阶段返回存储的 uri 
+     *
+     * Generated from protobuf field <code>string StoreUri = 1;</code>
+     * @return string
+     */
+    public function getStoreUri()
+    {
+        return $this->StoreUri;
+    }
+
+    /**
+     * 1001 阶段返回存储的 uri 
+     *
+     * Generated from protobuf field <code>string StoreUri = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStoreUri($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StoreUri = $var;
+
+        return $this;
+    }
+
+    /**
+     * 1001 阶段返回上传使用的签名信息 
+     *
+     * Generated from protobuf field <code>string Auth = 2;</code>
+     * @return string
+     */
+    public function getAuth()
+    {
+        return $this->Auth;
+    }
+
+    /**
+     * 1001 阶段返回上传使用的签名信息 
+     *
+     * Generated from protobuf field <code>string Auth = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setAuth($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Auth = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodStoreUriGroup.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodStoreUriGroup.php
new file mode 100755
index 0000000..9ad899c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodStoreUriGroup.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodStoreUriGroup</code>
+ */
+class VodStoreUriGroup extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     */
+    protected $Vid = '';
+    /**
+     *封面图对象地址列表
+     *
+     * Generated from protobuf field <code>repeated string StoreUris = 2;</code>
+     */
+    private $StoreUris;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vid
+     *          视频ID
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $StoreUris
+     *          封面图对象地址列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     *封面图对象地址列表
+     *
+     * Generated from protobuf field <code>repeated string StoreUris = 2;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getStoreUris()
+    {
+        return $this->StoreUris;
+    }
+
+    /**
+     *封面图对象地址列表
+     *
+     * Generated from protobuf field <code>repeated string StoreUris = 2;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setStoreUris($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->StoreUris = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSubtitleInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSubtitleInfo.php
new file mode 100755
index 0000000..08ebc73
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodSubtitleInfo.php
@@ -0,0 +1,507 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_common.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodSubtitleInfo</code>
+ */
+class VodSubtitleInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     */
+    protected $Vid = '';
+    /**
+     *文件ID
+     *
+     * Generated from protobuf field <code>string FileId = 2;</code>
+     */
+    protected $FileId = '';
+    /**
+     *字幕语言
+     *
+     * Generated from protobuf field <code>string Language = 3;</code>
+     */
+    protected $Language = '';
+    /**
+     *字幕语言ID
+     *
+     * Generated from protobuf field <code>int32 LanguageId = 4;</code>
+     */
+    protected $LanguageId = 0;
+    /**
+     *字幕格式
+     *
+     * Generated from protobuf field <code>string Format = 5;</code>
+     */
+    protected $Format = '';
+    /**
+     *字幕ID
+     *
+     * Generated from protobuf field <code>string SubtitleId = 6;</code>
+     */
+    protected $SubtitleId = '';
+    /**
+     *字幕标题
+     *
+     * Generated from protobuf field <code>string Title = 7;</code>
+     */
+    protected $Title = '';
+    /**
+     *字幕标签
+     *
+     * Generated from protobuf field <code>string Tag = 8;</code>
+     */
+    protected $Tag = '';
+    /**
+     *字幕状态
+     *
+     * Generated from protobuf field <code>string Status = 9;</code>
+     */
+    protected $Status = '';
+    /**
+     *字幕来源
+     *
+     * Generated from protobuf field <code>string Source = 10;</code>
+     */
+    protected $Source = '';
+    /**
+     *字幕uri
+     *
+     * Generated from protobuf field <code>string StoreUri = 11;</code>
+     */
+    protected $StoreUri = '';
+    /**
+     *字幕url
+     *
+     * Generated from protobuf field <code>string SubtitleUrl = 12;</code>
+     */
+    protected $SubtitleUrl = '';
+    /**
+     *创建时间
+     *
+     * Generated from protobuf field <code>string CreateTime = 13;</code>
+     */
+    protected $CreateTime = '';
+    /**
+     *字幕版本
+     *
+     * Generated from protobuf field <code>string Version = 14;</code>
+     */
+    protected $Version = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vid
+     *          视频ID
+     *     @type string $FileId
+     *          文件ID
+     *     @type string $Language
+     *          字幕语言
+     *     @type int $LanguageId
+     *          字幕语言ID
+     *     @type string $Format
+     *          字幕格式
+     *     @type string $SubtitleId
+     *          字幕ID
+     *     @type string $Title
+     *          字幕标题
+     *     @type string $Tag
+     *          字幕标签
+     *     @type string $Status
+     *          字幕状态
+     *     @type string $Source
+     *          字幕来源
+     *     @type string $StoreUri
+     *          字幕uri
+     *     @type string $SubtitleUrl
+     *          字幕url
+     *     @type string $CreateTime
+     *          创建时间
+     *     @type string $Version
+     *          字幕版本
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCommon::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     *文件ID
+     *
+     * Generated from protobuf field <code>string FileId = 2;</code>
+     * @return string
+     */
+    public function getFileId()
+    {
+        return $this->FileId;
+    }
+
+    /**
+     *文件ID
+     *
+     * Generated from protobuf field <code>string FileId = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileId = $var;
+
+        return $this;
+    }
+
+    /**
+     *字幕语言
+     *
+     * Generated from protobuf field <code>string Language = 3;</code>
+     * @return string
+     */
+    public function getLanguage()
+    {
+        return $this->Language;
+    }
+
+    /**
+     *字幕语言
+     *
+     * Generated from protobuf field <code>string Language = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setLanguage($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Language = $var;
+
+        return $this;
+    }
+
+    /**
+     *字幕语言ID
+     *
+     * Generated from protobuf field <code>int32 LanguageId = 4;</code>
+     * @return int
+     */
+    public function getLanguageId()
+    {
+        return $this->LanguageId;
+    }
+
+    /**
+     *字幕语言ID
+     *
+     * Generated from protobuf field <code>int32 LanguageId = 4;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setLanguageId($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->LanguageId = $var;
+
+        return $this;
+    }
+
+    /**
+     *字幕格式
+     *
+     * Generated from protobuf field <code>string Format = 5;</code>
+     * @return string
+     */
+    public function getFormat()
+    {
+        return $this->Format;
+    }
+
+    /**
+     *字幕格式
+     *
+     * Generated from protobuf field <code>string Format = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFormat($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Format = $var;
+
+        return $this;
+    }
+
+    /**
+     *字幕ID
+     *
+     * Generated from protobuf field <code>string SubtitleId = 6;</code>
+     * @return string
+     */
+    public function getSubtitleId()
+    {
+        return $this->SubtitleId;
+    }
+
+    /**
+     *字幕ID
+     *
+     * Generated from protobuf field <code>string SubtitleId = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSubtitleId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SubtitleId = $var;
+
+        return $this;
+    }
+
+    /**
+     *字幕标题
+     *
+     * Generated from protobuf field <code>string Title = 7;</code>
+     * @return string
+     */
+    public function getTitle()
+    {
+        return $this->Title;
+    }
+
+    /**
+     *字幕标题
+     *
+     * Generated from protobuf field <code>string Title = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTitle($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Title = $var;
+
+        return $this;
+    }
+
+    /**
+     *字幕标签
+     *
+     * Generated from protobuf field <code>string Tag = 8;</code>
+     * @return string
+     */
+    public function getTag()
+    {
+        return $this->Tag;
+    }
+
+    /**
+     *字幕标签
+     *
+     * Generated from protobuf field <code>string Tag = 8;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTag($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Tag = $var;
+
+        return $this;
+    }
+
+    /**
+     *字幕状态
+     *
+     * Generated from protobuf field <code>string Status = 9;</code>
+     * @return string
+     */
+    public function getStatus()
+    {
+        return $this->Status;
+    }
+
+    /**
+     *字幕状态
+     *
+     * Generated from protobuf field <code>string Status = 9;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStatus($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Status = $var;
+
+        return $this;
+    }
+
+    /**
+     *字幕来源
+     *
+     * Generated from protobuf field <code>string Source = 10;</code>
+     * @return string
+     */
+    public function getSource()
+    {
+        return $this->Source;
+    }
+
+    /**
+     *字幕来源
+     *
+     * Generated from protobuf field <code>string Source = 10;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSource($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Source = $var;
+
+        return $this;
+    }
+
+    /**
+     *字幕uri
+     *
+     * Generated from protobuf field <code>string StoreUri = 11;</code>
+     * @return string
+     */
+    public function getStoreUri()
+    {
+        return $this->StoreUri;
+    }
+
+    /**
+     *字幕uri
+     *
+     * Generated from protobuf field <code>string StoreUri = 11;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStoreUri($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StoreUri = $var;
+
+        return $this;
+    }
+
+    /**
+     *字幕url
+     *
+     * Generated from protobuf field <code>string SubtitleUrl = 12;</code>
+     * @return string
+     */
+    public function getSubtitleUrl()
+    {
+        return $this->SubtitleUrl;
+    }
+
+    /**
+     *字幕url
+     *
+     * Generated from protobuf field <code>string SubtitleUrl = 12;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSubtitleUrl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SubtitleUrl = $var;
+
+        return $this;
+    }
+
+    /**
+     *创建时间
+     *
+     * Generated from protobuf field <code>string CreateTime = 13;</code>
+     * @return string
+     */
+    public function getCreateTime()
+    {
+        return $this->CreateTime;
+    }
+
+    /**
+     *创建时间
+     *
+     * Generated from protobuf field <code>string CreateTime = 13;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCreateTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CreateTime = $var;
+
+        return $this;
+    }
+
+    /**
+     *字幕版本
+     *
+     * Generated from protobuf field <code>string Version = 14;</code>
+     * @return string
+     */
+    public function getVersion()
+    {
+        return $this->Version;
+    }
+
+    /**
+     *字幕版本
+     *
+     * Generated from protobuf field <code>string Version = 14;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVersion($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Version = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodThumbInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodThumbInfo.php
new file mode 100755
index 0000000..08935d1
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodThumbInfo.php
@@ -0,0 +1,303 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_common.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodThumbInfo</code>
+ */
+class VodThumbInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 包含的截图总数
+     *
+     * Generated from protobuf field <code>int32 CaptureNum = 1;</code>
+     */
+    protected $CaptureNum = 0;
+    /**
+     * 雪碧图URL
+     *
+     * Generated from protobuf field <code>repeated string StoreUrls = 2;</code>
+     */
+    private $StoreUrls;
+    /**
+     * 小图宽
+     *
+     * Generated from protobuf field <code>int32 CellWidth = 3;</code>
+     */
+    protected $CellWidth = 0;
+    /**
+     * 小图高
+     *
+     * Generated from protobuf field <code>int32 CellHeight = 4;</code>
+     */
+    protected $CellHeight = 0;
+    /**
+     * 雪碧图每行包含的小图数量
+     *
+     * Generated from protobuf field <code>int32 ImgXLen = 5;</code>
+     */
+    protected $ImgXLen = 0;
+    /**
+     * 雪碧图每列包含的小图数量
+     *
+     * Generated from protobuf field <code>int32 ImgYLen = 6;</code>
+     */
+    protected $ImgYLen = 0;
+    /**
+     * 显示图片时间周期
+     *
+     * Generated from protobuf field <code>double Interval = 7;</code>
+     */
+    protected $Interval = 0.0;
+    /**
+     * 图片格式:例:jpg
+     *
+     * Generated from protobuf field <code>string Format = 8;</code>
+     */
+    protected $Format = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int $CaptureNum
+     *           包含的截图总数
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $StoreUrls
+     *           雪碧图URL
+     *     @type int $CellWidth
+     *           小图宽
+     *     @type int $CellHeight
+     *           小图高
+     *     @type int $ImgXLen
+     *           雪碧图每行包含的小图数量
+     *     @type int $ImgYLen
+     *           雪碧图每列包含的小图数量
+     *     @type float $Interval
+     *           显示图片时间周期
+     *     @type string $Format
+     *           图片格式:例:jpg
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCommon::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 包含的截图总数
+     *
+     * Generated from protobuf field <code>int32 CaptureNum = 1;</code>
+     * @return int
+     */
+    public function getCaptureNum()
+    {
+        return $this->CaptureNum;
+    }
+
+    /**
+     * 包含的截图总数
+     *
+     * Generated from protobuf field <code>int32 CaptureNum = 1;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setCaptureNum($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->CaptureNum = $var;
+
+        return $this;
+    }
+
+    /**
+     * 雪碧图URL
+     *
+     * Generated from protobuf field <code>repeated string StoreUrls = 2;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getStoreUrls()
+    {
+        return $this->StoreUrls;
+    }
+
+    /**
+     * 雪碧图URL
+     *
+     * Generated from protobuf field <code>repeated string StoreUrls = 2;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setStoreUrls($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->StoreUrls = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 小图宽
+     *
+     * Generated from protobuf field <code>int32 CellWidth = 3;</code>
+     * @return int
+     */
+    public function getCellWidth()
+    {
+        return $this->CellWidth;
+    }
+
+    /**
+     * 小图宽
+     *
+     * Generated from protobuf field <code>int32 CellWidth = 3;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setCellWidth($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->CellWidth = $var;
+
+        return $this;
+    }
+
+    /**
+     * 小图高
+     *
+     * Generated from protobuf field <code>int32 CellHeight = 4;</code>
+     * @return int
+     */
+    public function getCellHeight()
+    {
+        return $this->CellHeight;
+    }
+
+    /**
+     * 小图高
+     *
+     * Generated from protobuf field <code>int32 CellHeight = 4;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setCellHeight($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->CellHeight = $var;
+
+        return $this;
+    }
+
+    /**
+     * 雪碧图每行包含的小图数量
+     *
+     * Generated from protobuf field <code>int32 ImgXLen = 5;</code>
+     * @return int
+     */
+    public function getImgXLen()
+    {
+        return $this->ImgXLen;
+    }
+
+    /**
+     * 雪碧图每行包含的小图数量
+     *
+     * Generated from protobuf field <code>int32 ImgXLen = 5;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setImgXLen($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->ImgXLen = $var;
+
+        return $this;
+    }
+
+    /**
+     * 雪碧图每列包含的小图数量
+     *
+     * Generated from protobuf field <code>int32 ImgYLen = 6;</code>
+     * @return int
+     */
+    public function getImgYLen()
+    {
+        return $this->ImgYLen;
+    }
+
+    /**
+     * 雪碧图每列包含的小图数量
+     *
+     * Generated from protobuf field <code>int32 ImgYLen = 6;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setImgYLen($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->ImgYLen = $var;
+
+        return $this;
+    }
+
+    /**
+     * 显示图片时间周期
+     *
+     * Generated from protobuf field <code>double Interval = 7;</code>
+     * @return float
+     */
+    public function getInterval()
+    {
+        return $this->Interval;
+    }
+
+    /**
+     * 显示图片时间周期
+     *
+     * Generated from protobuf field <code>double Interval = 7;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setInterval($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->Interval = $var;
+
+        return $this;
+    }
+
+    /**
+     * 图片格式:例:jpg
+     *
+     * Generated from protobuf field <code>string Format = 8;</code>
+     * @return string
+     */
+    public function getFormat()
+    {
+        return $this->Format;
+    }
+
+    /**
+     * 图片格式:例:jpg
+     *
+     * Generated from protobuf field <code>string Format = 8;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFormat($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Format = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodTrafficData.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodTrafficData.php
new file mode 100755
index 0000000..3e9675f
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodTrafficData.php
@@ -0,0 +1,92 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_cdn.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodTrafficData</code>
+ */
+class VodTrafficData extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>string Time = 1;</code>
+     */
+    protected $Time = '';
+    /**
+     * GB
+     *
+     * Generated from protobuf field <code>double Traffic = 2;</code>
+     */
+    protected $Traffic = 0.0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Time
+     *     @type float $Traffic
+     *           GB
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCdn::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>string Time = 1;</code>
+     * @return string
+     */
+    public function getTime()
+    {
+        return $this->Time;
+    }
+
+    /**
+     * Generated from protobuf field <code>string Time = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Time = $var;
+
+        return $this;
+    }
+
+    /**
+     * GB
+     *
+     * Generated from protobuf field <code>double Traffic = 2;</code>
+     * @return float
+     */
+    public function getTraffic()
+    {
+        return $this->Traffic;
+    }
+
+    /**
+     * GB
+     *
+     * Generated from protobuf field <code>double Traffic = 2;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setTraffic($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->Traffic = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodTranscodeInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodTranscodeInfo.php
new file mode 100755
index 0000000..dd50225
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodTranscodeInfo.php
@@ -0,0 +1,459 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_common.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodTranscodeInfo</code>
+ */
+class VodTranscodeInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *文件ID
+     *
+     * Generated from protobuf field <code>string FileId = 1;</code>
+     */
+    protected $FileId = '';
+    /**
+     * hash值
+     *
+     * Generated from protobuf field <code>string Md5 = 2;</code>
+     */
+    protected $Md5 = '';
+    /**
+     *文件类型 video/audio
+     *
+     * Generated from protobuf field <code>string FileType = 3;</code>
+     */
+    protected $FileType = '';
+    /**
+     * logo类型
+     *
+     * Generated from protobuf field <code>string LogoType = 4;</code>
+     */
+    protected $LogoType = '';
+    /**
+     *是否加密
+     *
+     * Generated from protobuf field <code>bool Encrypt = 5;</code>
+     */
+    protected $Encrypt = false;
+    /**
+     *文件格式
+     *
+     * Generated from protobuf field <code>string Format = 6;</code>
+     */
+    protected $Format = '';
+    /**
+     *时长
+     *
+     * Generated from protobuf field <code>float Duration = 7;</code>
+     */
+    protected $Duration = 0.0;
+    /**
+     *文件大小
+     *
+     * Generated from protobuf field <code>double Size = 8;</code>
+     */
+    protected $Size = 0.0;
+    /**
+     *对象地址
+     *
+     * Generated from protobuf field <code>string StoreUri = 9;</code>
+     */
+    protected $StoreUri = '';
+    /**
+     *视频流信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodVideoStreamMeta VideoStreamMeta = 10;</code>
+     */
+    protected $VideoStreamMeta = null;
+    /**
+     *音频流信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodAudioStreamMeta AudioStreamMeta = 11;</code>
+     */
+    protected $AudioStreamMeta = null;
+    /**
+     *创建时间
+     *
+     * Generated from protobuf field <code>string CreateTime = 12;</code>
+     */
+    protected $CreateTime = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $FileId
+     *          文件ID
+     *     @type string $Md5
+     *           hash值
+     *     @type string $FileType
+     *          文件类型 video/audio
+     *     @type string $LogoType
+     *           logo类型
+     *     @type bool $Encrypt
+     *          是否加密
+     *     @type string $Format
+     *          文件格式
+     *     @type float $Duration
+     *          时长
+     *     @type float $Size
+     *          文件大小
+     *     @type string $StoreUri
+     *          对象地址
+     *     @type \Volc\Service\Vod\Models\Business\VodVideoStreamMeta $VideoStreamMeta
+     *          视频流信息
+     *     @type \Volc\Service\Vod\Models\Business\VodAudioStreamMeta $AudioStreamMeta
+     *          音频流信息
+     *     @type string $CreateTime
+     *          创建时间
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCommon::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *文件ID
+     *
+     * Generated from protobuf field <code>string FileId = 1;</code>
+     * @return string
+     */
+    public function getFileId()
+    {
+        return $this->FileId;
+    }
+
+    /**
+     *文件ID
+     *
+     * Generated from protobuf field <code>string FileId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileId = $var;
+
+        return $this;
+    }
+
+    /**
+     * hash值
+     *
+     * Generated from protobuf field <code>string Md5 = 2;</code>
+     * @return string
+     */
+    public function getMd5()
+    {
+        return $this->Md5;
+    }
+
+    /**
+     * hash值
+     *
+     * Generated from protobuf field <code>string Md5 = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setMd5($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Md5 = $var;
+
+        return $this;
+    }
+
+    /**
+     *文件类型 video/audio
+     *
+     * Generated from protobuf field <code>string FileType = 3;</code>
+     * @return string
+     */
+    public function getFileType()
+    {
+        return $this->FileType;
+    }
+
+    /**
+     *文件类型 video/audio
+     *
+     * Generated from protobuf field <code>string FileType = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileType = $var;
+
+        return $this;
+    }
+
+    /**
+     * logo类型
+     *
+     * Generated from protobuf field <code>string LogoType = 4;</code>
+     * @return string
+     */
+    public function getLogoType()
+    {
+        return $this->LogoType;
+    }
+
+    /**
+     * logo类型
+     *
+     * Generated from protobuf field <code>string LogoType = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setLogoType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->LogoType = $var;
+
+        return $this;
+    }
+
+    /**
+     *是否加密
+     *
+     * Generated from protobuf field <code>bool Encrypt = 5;</code>
+     * @return bool
+     */
+    public function getEncrypt()
+    {
+        return $this->Encrypt;
+    }
+
+    /**
+     *是否加密
+     *
+     * Generated from protobuf field <code>bool Encrypt = 5;</code>
+     * @param bool $var
+     * @return $this
+     */
+    public function setEncrypt($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->Encrypt = $var;
+
+        return $this;
+    }
+
+    /**
+     *文件格式
+     *
+     * Generated from protobuf field <code>string Format = 6;</code>
+     * @return string
+     */
+    public function getFormat()
+    {
+        return $this->Format;
+    }
+
+    /**
+     *文件格式
+     *
+     * Generated from protobuf field <code>string Format = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFormat($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Format = $var;
+
+        return $this;
+    }
+
+    /**
+     *时长
+     *
+     * Generated from protobuf field <code>float Duration = 7;</code>
+     * @return float
+     */
+    public function getDuration()
+    {
+        return $this->Duration;
+    }
+
+    /**
+     *时长
+     *
+     * Generated from protobuf field <code>float Duration = 7;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setDuration($var)
+    {
+        GPBUtil::checkFloat($var);
+        $this->Duration = $var;
+
+        return $this;
+    }
+
+    /**
+     *文件大小
+     *
+     * Generated from protobuf field <code>double Size = 8;</code>
+     * @return float
+     */
+    public function getSize()
+    {
+        return $this->Size;
+    }
+
+    /**
+     *文件大小
+     *
+     * Generated from protobuf field <code>double Size = 8;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setSize($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->Size = $var;
+
+        return $this;
+    }
+
+    /**
+     *对象地址
+     *
+     * Generated from protobuf field <code>string StoreUri = 9;</code>
+     * @return string
+     */
+    public function getStoreUri()
+    {
+        return $this->StoreUri;
+    }
+
+    /**
+     *对象地址
+     *
+     * Generated from protobuf field <code>string StoreUri = 9;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStoreUri($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StoreUri = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频流信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodVideoStreamMeta VideoStreamMeta = 10;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodVideoStreamMeta|null
+     */
+    public function getVideoStreamMeta()
+    {
+        return $this->VideoStreamMeta;
+    }
+
+    public function hasVideoStreamMeta()
+    {
+        return isset($this->VideoStreamMeta);
+    }
+
+    public function clearVideoStreamMeta()
+    {
+        unset($this->VideoStreamMeta);
+    }
+
+    /**
+     *视频流信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodVideoStreamMeta VideoStreamMeta = 10;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodVideoStreamMeta $var
+     * @return $this
+     */
+    public function setVideoStreamMeta($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodVideoStreamMeta::class);
+        $this->VideoStreamMeta = $var;
+
+        return $this;
+    }
+
+    /**
+     *音频流信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodAudioStreamMeta AudioStreamMeta = 11;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodAudioStreamMeta|null
+     */
+    public function getAudioStreamMeta()
+    {
+        return $this->AudioStreamMeta;
+    }
+
+    public function hasAudioStreamMeta()
+    {
+        return isset($this->AudioStreamMeta);
+    }
+
+    public function clearAudioStreamMeta()
+    {
+        unset($this->AudioStreamMeta);
+    }
+
+    /**
+     *音频流信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodAudioStreamMeta AudioStreamMeta = 11;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodAudioStreamMeta $var
+     * @return $this
+     */
+    public function setAudioStreamMeta($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodAudioStreamMeta::class);
+        $this->AudioStreamMeta = $var;
+
+        return $this;
+    }
+
+    /**
+     *创建时间
+     *
+     * Generated from protobuf field <code>string CreateTime = 12;</code>
+     * @return string
+     */
+    public function getCreateTime()
+    {
+        return $this->CreateTime;
+    }
+
+    /**
+     *创建时间
+     *
+     * Generated from protobuf field <code>string CreateTime = 12;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCreateTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CreateTime = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodURLSet.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodURLSet.php
new file mode 100755
index 0000000..2f85de0
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodURLSet.php
@@ -0,0 +1,313 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_upload.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodURLSet</code>
+ */
+class VodURLSet extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 请求 Id,用于日志查询 
+     *
+     * Generated from protobuf field <code>string RequestId = 1;</code>
+     */
+    protected $RequestId = '';
+    /**
+     * JobId,用于查询 URL 上传状态 
+     *
+     * Generated from protobuf field <code>string JobId = 2;</code>
+     */
+    protected $JobId = '';
+    /**
+     * 用户上传的Url 
+     *
+     * Generated from protobuf field <code>string SourceUrl = 3;</code>
+     */
+    protected $SourceUrl = '';
+    /**
+     * Url 的上传状态,init,process,success,fail 
+     *
+     * Generated from protobuf field <code>string State = 4;</code>
+     */
+    protected $State = '';
+    /**
+     * 视频 id 
+     *
+     * Generated from protobuf field <code>string Vid = 5;</code>
+     */
+    protected $Vid = '';
+    /**
+     * 上传的空间 
+     *
+     * Generated from protobuf field <code>string SpaceName = 6;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     * 用户账号的 Id 
+     *
+     * Generated from protobuf field <code>string AccountId = 7;</code>
+     */
+    protected $AccountId = '';
+    /**
+     * 视频的元信息 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodSourceInfo SourceInfo = 8;</code>
+     */
+    protected $SourceInfo = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $RequestId
+     *           请求 Id,用于日志查询 
+     *     @type string $JobId
+     *           JobId,用于查询 URL 上传状态 
+     *     @type string $SourceUrl
+     *           用户上传的Url 
+     *     @type string $State
+     *           Url 的上传状态,init,process,success,fail 
+     *     @type string $Vid
+     *           视频 id 
+     *     @type string $SpaceName
+     *           上传的空间 
+     *     @type string $AccountId
+     *           用户账号的 Id 
+     *     @type \Volc\Service\Vod\Models\Business\VodSourceInfo $SourceInfo
+     *           视频的元信息 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodUpload::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 请求 Id,用于日志查询 
+     *
+     * Generated from protobuf field <code>string RequestId = 1;</code>
+     * @return string
+     */
+    public function getRequestId()
+    {
+        return $this->RequestId;
+    }
+
+    /**
+     * 请求 Id,用于日志查询 
+     *
+     * Generated from protobuf field <code>string RequestId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setRequestId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->RequestId = $var;
+
+        return $this;
+    }
+
+    /**
+     * JobId,用于查询 URL 上传状态 
+     *
+     * Generated from protobuf field <code>string JobId = 2;</code>
+     * @return string
+     */
+    public function getJobId()
+    {
+        return $this->JobId;
+    }
+
+    /**
+     * JobId,用于查询 URL 上传状态 
+     *
+     * Generated from protobuf field <code>string JobId = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setJobId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->JobId = $var;
+
+        return $this;
+    }
+
+    /**
+     * 用户上传的Url 
+     *
+     * Generated from protobuf field <code>string SourceUrl = 3;</code>
+     * @return string
+     */
+    public function getSourceUrl()
+    {
+        return $this->SourceUrl;
+    }
+
+    /**
+     * 用户上传的Url 
+     *
+     * Generated from protobuf field <code>string SourceUrl = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSourceUrl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SourceUrl = $var;
+
+        return $this;
+    }
+
+    /**
+     * Url 的上传状态,init,process,success,fail 
+     *
+     * Generated from protobuf field <code>string State = 4;</code>
+     * @return string
+     */
+    public function getState()
+    {
+        return $this->State;
+    }
+
+    /**
+     * Url 的上传状态,init,process,success,fail 
+     *
+     * Generated from protobuf field <code>string State = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setState($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->State = $var;
+
+        return $this;
+    }
+
+    /**
+     * 视频 id 
+     *
+     * Generated from protobuf field <code>string Vid = 5;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     * 视频 id 
+     *
+     * Generated from protobuf field <code>string Vid = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     * 上传的空间 
+     *
+     * Generated from protobuf field <code>string SpaceName = 6;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * 上传的空间 
+     *
+     * Generated from protobuf field <code>string SpaceName = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 用户账号的 Id 
+     *
+     * Generated from protobuf field <code>string AccountId = 7;</code>
+     * @return string
+     */
+    public function getAccountId()
+    {
+        return $this->AccountId;
+    }
+
+    /**
+     * 用户账号的 Id 
+     *
+     * Generated from protobuf field <code>string AccountId = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setAccountId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->AccountId = $var;
+
+        return $this;
+    }
+
+    /**
+     * 视频的元信息 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodSourceInfo SourceInfo = 8;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodSourceInfo|null
+     */
+    public function getSourceInfo()
+    {
+        return $this->SourceInfo;
+    }
+
+    public function hasSourceInfo()
+    {
+        return isset($this->SourceInfo);
+    }
+
+    public function clearSourceInfo()
+    {
+        unset($this->SourceInfo);
+    }
+
+    /**
+     * 视频的元信息 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodSourceInfo SourceInfo = 8;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodSourceInfo $var
+     * @return $this
+     */
+    public function setSourceInfo($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodSourceInfo::class);
+        $this->SourceInfo = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodUpdateSubtitleStatusData.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodUpdateSubtitleStatusData.php
new file mode 100755
index 0000000..112a1f6
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodUpdateSubtitleStatusData.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodUpdateSubtitleStatusData</code>
+ */
+class VodUpdateSubtitleStatusData extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *不存在的转码FileID列表
+     *
+     * Generated from protobuf field <code>repeated string NotExistFileIds = 1;</code>
+     */
+    private $NotExistFileIds;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $NotExistFileIds
+     *          不存在的转码FileID列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *不存在的转码FileID列表
+     *
+     * Generated from protobuf field <code>repeated string NotExistFileIds = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getNotExistFileIds()
+    {
+        return $this->NotExistFileIds;
+    }
+
+    /**
+     *不存在的转码FileID列表
+     *
+     * Generated from protobuf field <code>repeated string NotExistFileIds = 1;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setNotExistFileIds($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->NotExistFileIds = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodUploadAddress.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodUploadAddress.php
new file mode 100755
index 0000000..1ee1598
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodUploadAddress.php
@@ -0,0 +1,167 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_upload.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodUploadAddress</code>
+ */
+class VodUploadAddress extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 1001 阶段返回指定存储相关信息 
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodStoreInfo StoreInfos = 1;</code>
+     */
+    private $StoreInfos;
+    /**
+     * 1001 阶段返回指定的上传域名 
+     *
+     * Generated from protobuf field <code>repeated string UploadHosts = 2;</code>
+     */
+    private $UploadHosts;
+    /**
+     * 1001 阶段返回指定上传带的头信息 
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodHeaderPair UploadHeader = 3;</code>
+     */
+    private $UploadHeader;
+    /**
+     * 1001 阶段返回的 Session 信息 
+     *
+     * Generated from protobuf field <code>string SessionKey = 4;</code>
+     */
+    protected $SessionKey = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\VodStoreInfo[]|\Google\Protobuf\Internal\RepeatedField $StoreInfos
+     *           1001 阶段返回指定存储相关信息 
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $UploadHosts
+     *           1001 阶段返回指定的上传域名 
+     *     @type \Volc\Service\Vod\Models\Business\VodHeaderPair[]|\Google\Protobuf\Internal\RepeatedField $UploadHeader
+     *           1001 阶段返回指定上传带的头信息 
+     *     @type string $SessionKey
+     *           1001 阶段返回的 Session 信息 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodUpload::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 1001 阶段返回指定存储相关信息 
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodStoreInfo StoreInfos = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getStoreInfos()
+    {
+        return $this->StoreInfos;
+    }
+
+    /**
+     * 1001 阶段返回指定存储相关信息 
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodStoreInfo StoreInfos = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodStoreInfo[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setStoreInfos($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodStoreInfo::class);
+        $this->StoreInfos = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 1001 阶段返回指定的上传域名 
+     *
+     * Generated from protobuf field <code>repeated string UploadHosts = 2;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getUploadHosts()
+    {
+        return $this->UploadHosts;
+    }
+
+    /**
+     * 1001 阶段返回指定的上传域名 
+     *
+     * Generated from protobuf field <code>repeated string UploadHosts = 2;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setUploadHosts($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->UploadHosts = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 1001 阶段返回指定上传带的头信息 
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodHeaderPair UploadHeader = 3;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getUploadHeader()
+    {
+        return $this->UploadHeader;
+    }
+
+    /**
+     * 1001 阶段返回指定上传带的头信息 
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodHeaderPair UploadHeader = 3;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodHeaderPair[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setUploadHeader($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodHeaderPair::class);
+        $this->UploadHeader = $arr;
+
+        return $this;
+    }
+
+    /**
+     * 1001 阶段返回的 Session 信息 
+     *
+     * Generated from protobuf field <code>string SessionKey = 4;</code>
+     * @return string
+     */
+    public function getSessionKey()
+    {
+        return $this->SessionKey;
+    }
+
+    /**
+     * 1001 阶段返回的 Session 信息 
+     *
+     * Generated from protobuf field <code>string SessionKey = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSessionKey($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SessionKey = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodUrlResponseData.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodUrlResponseData.php
new file mode 100755
index 0000000..03d768e
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodUrlResponseData.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_upload.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodUrlResponseData</code>
+ */
+class VodUrlResponseData extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 视频的URL的键值对,包含 JobId 和对应的 SourceUrl 
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.ValuePair Data = 1;</code>
+     */
+    private $Data;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\ValuePair[]|\Google\Protobuf\Internal\RepeatedField $Data
+     *           视频的URL的键值对,包含 JobId 和对应的 SourceUrl 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodUpload::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 视频的URL的键值对,包含 JobId 和对应的 SourceUrl 
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.ValuePair Data = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getData()
+    {
+        return $this->Data;
+    }
+
+    /**
+     * 视频的URL的键值对,包含 JobId 和对应的 SourceUrl 
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.ValuePair Data = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\ValuePair[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setData($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\ValuePair::class);
+        $this->Data = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodUrlUploadURLSet.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodUrlUploadURLSet.php
new file mode 100755
index 0000000..345ed71
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodUrlUploadURLSet.php
@@ -0,0 +1,371 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_upload.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodUrlUploadURLSet</code>
+ */
+class VodUrlUploadURLSet extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 视频的URL 
+     *
+     * Generated from protobuf field <code>string SourceUrl = 1;</code>
+     */
+    protected $SourceUrl = '';
+    /**
+     * 透传的业务信息 
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 2;</code>
+     */
+    protected $CallbackArgs = '';
+    /**
+     * 视频的MD5 
+     *
+     * Generated from protobuf field <code>string Md5 = 3;</code>
+     */
+    protected $Md5 = '';
+    /**
+     * 模板Id 
+     *
+     * Generated from protobuf field <code>string TemplateId = 4;</code>
+     */
+    protected $TemplateId = '';
+    /**
+     * 标题 
+     *
+     * Generated from protobuf field <code>string Title = 5;</code>
+     */
+    protected $Title = '';
+    /**
+     * 描述信息 
+     *
+     * Generated from protobuf field <code>string Description = 6;</code>
+     */
+    protected $Description = '';
+    /**
+     * 标签 
+     *
+     * Generated from protobuf field <code>string Tags = 7;</code>
+     */
+    protected $Tags = '';
+    /**
+     * 分类 
+     *
+     * Generated from protobuf field <code>string Category = 8;</code>
+     */
+    protected $Category = '';
+    /**
+     * 设置文件名 
+     *
+     * Generated from protobuf field <code>string FileName = 9;</code>
+     */
+    protected $FileName = '';
+    /**
+     * 设置分类Id 
+     *
+     * Generated from protobuf field <code>int64 ClassificationId = 10;</code>
+     */
+    protected $ClassificationId = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SourceUrl
+     *           视频的URL 
+     *     @type string $CallbackArgs
+     *           透传的业务信息 
+     *     @type string $Md5
+     *           视频的MD5 
+     *     @type string $TemplateId
+     *           模板Id 
+     *     @type string $Title
+     *           标题 
+     *     @type string $Description
+     *           描述信息 
+     *     @type string $Tags
+     *           标签 
+     *     @type string $Category
+     *           分类 
+     *     @type string $FileName
+     *           设置文件名 
+     *     @type int|string $ClassificationId
+     *           设置分类Id 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodUpload::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 视频的URL 
+     *
+     * Generated from protobuf field <code>string SourceUrl = 1;</code>
+     * @return string
+     */
+    public function getSourceUrl()
+    {
+        return $this->SourceUrl;
+    }
+
+    /**
+     * 视频的URL 
+     *
+     * Generated from protobuf field <code>string SourceUrl = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSourceUrl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SourceUrl = $var;
+
+        return $this;
+    }
+
+    /**
+     * 透传的业务信息 
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 2;</code>
+     * @return string
+     */
+    public function getCallbackArgs()
+    {
+        return $this->CallbackArgs;
+    }
+
+    /**
+     * 透传的业务信息 
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCallbackArgs($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CallbackArgs = $var;
+
+        return $this;
+    }
+
+    /**
+     * 视频的MD5 
+     *
+     * Generated from protobuf field <code>string Md5 = 3;</code>
+     * @return string
+     */
+    public function getMd5()
+    {
+        return $this->Md5;
+    }
+
+    /**
+     * 视频的MD5 
+     *
+     * Generated from protobuf field <code>string Md5 = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setMd5($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Md5 = $var;
+
+        return $this;
+    }
+
+    /**
+     * 模板Id 
+     *
+     * Generated from protobuf field <code>string TemplateId = 4;</code>
+     * @return string
+     */
+    public function getTemplateId()
+    {
+        return $this->TemplateId;
+    }
+
+    /**
+     * 模板Id 
+     *
+     * Generated from protobuf field <code>string TemplateId = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTemplateId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TemplateId = $var;
+
+        return $this;
+    }
+
+    /**
+     * 标题 
+     *
+     * Generated from protobuf field <code>string Title = 5;</code>
+     * @return string
+     */
+    public function getTitle()
+    {
+        return $this->Title;
+    }
+
+    /**
+     * 标题 
+     *
+     * Generated from protobuf field <code>string Title = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTitle($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Title = $var;
+
+        return $this;
+    }
+
+    /**
+     * 描述信息 
+     *
+     * Generated from protobuf field <code>string Description = 6;</code>
+     * @return string
+     */
+    public function getDescription()
+    {
+        return $this->Description;
+    }
+
+    /**
+     * 描述信息 
+     *
+     * Generated from protobuf field <code>string Description = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDescription($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Description = $var;
+
+        return $this;
+    }
+
+    /**
+     * 标签 
+     *
+     * Generated from protobuf field <code>string Tags = 7;</code>
+     * @return string
+     */
+    public function getTags()
+    {
+        return $this->Tags;
+    }
+
+    /**
+     * 标签 
+     *
+     * Generated from protobuf field <code>string Tags = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTags($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Tags = $var;
+
+        return $this;
+    }
+
+    /**
+     * 分类 
+     *
+     * Generated from protobuf field <code>string Category = 8;</code>
+     * @return string
+     */
+    public function getCategory()
+    {
+        return $this->Category;
+    }
+
+    /**
+     * 分类 
+     *
+     * Generated from protobuf field <code>string Category = 8;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCategory($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Category = $var;
+
+        return $this;
+    }
+
+    /**
+     * 设置文件名 
+     *
+     * Generated from protobuf field <code>string FileName = 9;</code>
+     * @return string
+     */
+    public function getFileName()
+    {
+        return $this->FileName;
+    }
+
+    /**
+     * 设置文件名 
+     *
+     * Generated from protobuf field <code>string FileName = 9;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 设置分类Id 
+     *
+     * Generated from protobuf field <code>int64 ClassificationId = 10;</code>
+     * @return int|string
+     */
+    public function getClassificationId()
+    {
+        return $this->ClassificationId;
+    }
+
+    /**
+     * 设置分类Id 
+     *
+     * Generated from protobuf field <code>int64 ClassificationId = 10;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setClassificationId($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->ClassificationId = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodVideoClassificationsData.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodVideoClassificationsData.php
new file mode 100755
index 0000000..34bc5e5
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodVideoClassificationsData.php
@@ -0,0 +1,58 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_media.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodVideoClassificationsData</code>
+ */
+class VodVideoClassificationsData extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodClassification ClassificationTrees = 1;</code>
+     */
+    private $ClassificationTrees;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\VodClassification[]|\Google\Protobuf\Internal\RepeatedField $ClassificationTrees
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodClassification ClassificationTrees = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getClassificationTrees()
+    {
+        return $this->ClassificationTrees;
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodClassification ClassificationTrees = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodClassification[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setClassificationTrees($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodClassification::class);
+        $this->ClassificationTrees = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodVideoStreamMeta.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodVideoStreamMeta.php
new file mode 100755
index 0000000..c2f96d5
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodVideoStreamMeta.php
@@ -0,0 +1,269 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_common.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodVideoStreamMeta</code>
+ */
+class VodVideoStreamMeta extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *视频编码格式
+     *
+     * Generated from protobuf field <code>string Codec = 1;</code>
+     */
+    protected $Codec = '';
+    /**
+     *视频高度
+     *
+     * Generated from protobuf field <code>int32 Height = 2;</code>
+     */
+    protected $Height = 0;
+    /**
+     *视频宽度
+     *
+     * Generated from protobuf field <code>int32 Width = 3;</code>
+     */
+    protected $Width = 0;
+    /**
+     *视频时长
+     *
+     * Generated from protobuf field <code>float Duration = 4;</code>
+     */
+    protected $Duration = 0.0;
+    /**
+     *视频清晰度
+     *
+     * Generated from protobuf field <code>string Definition = 5;</code>
+     */
+    protected $Definition = '';
+    /**
+     *视频码率(Kbps)
+     *
+     * Generated from protobuf field <code>int32 Bitrate = 6;</code>
+     */
+    protected $Bitrate = 0;
+    /**
+     *帧率
+     *
+     * Generated from protobuf field <code>float Fps = 7;</code>
+     */
+    protected $Fps = 0.0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Codec
+     *          视频编码格式
+     *     @type int $Height
+     *          视频高度
+     *     @type int $Width
+     *          视频宽度
+     *     @type float $Duration
+     *          视频时长
+     *     @type string $Definition
+     *          视频清晰度
+     *     @type int $Bitrate
+     *          视频码率(Kbps)
+     *     @type float $Fps
+     *          帧率
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCommon::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *视频编码格式
+     *
+     * Generated from protobuf field <code>string Codec = 1;</code>
+     * @return string
+     */
+    public function getCodec()
+    {
+        return $this->Codec;
+    }
+
+    /**
+     *视频编码格式
+     *
+     * Generated from protobuf field <code>string Codec = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCodec($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Codec = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频高度
+     *
+     * Generated from protobuf field <code>int32 Height = 2;</code>
+     * @return int
+     */
+    public function getHeight()
+    {
+        return $this->Height;
+    }
+
+    /**
+     *视频高度
+     *
+     * Generated from protobuf field <code>int32 Height = 2;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setHeight($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Height = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频宽度
+     *
+     * Generated from protobuf field <code>int32 Width = 3;</code>
+     * @return int
+     */
+    public function getWidth()
+    {
+        return $this->Width;
+    }
+
+    /**
+     *视频宽度
+     *
+     * Generated from protobuf field <code>int32 Width = 3;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setWidth($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Width = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频时长
+     *
+     * Generated from protobuf field <code>float Duration = 4;</code>
+     * @return float
+     */
+    public function getDuration()
+    {
+        return $this->Duration;
+    }
+
+    /**
+     *视频时长
+     *
+     * Generated from protobuf field <code>float Duration = 4;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setDuration($var)
+    {
+        GPBUtil::checkFloat($var);
+        $this->Duration = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频清晰度
+     *
+     * Generated from protobuf field <code>string Definition = 5;</code>
+     * @return string
+     */
+    public function getDefinition()
+    {
+        return $this->Definition;
+    }
+
+    /**
+     *视频清晰度
+     *
+     * Generated from protobuf field <code>string Definition = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDefinition($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Definition = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频码率(Kbps)
+     *
+     * Generated from protobuf field <code>int32 Bitrate = 6;</code>
+     * @return int
+     */
+    public function getBitrate()
+    {
+        return $this->Bitrate;
+    }
+
+    /**
+     *视频码率(Kbps)
+     *
+     * Generated from protobuf field <code>int32 Bitrate = 6;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setBitrate($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Bitrate = $var;
+
+        return $this;
+    }
+
+    /**
+     *帧率
+     *
+     * Generated from protobuf field <code>float Fps = 7;</code>
+     * @return float
+     */
+    public function getFps()
+    {
+        return $this->Fps;
+    }
+
+    /**
+     *帧率
+     *
+     * Generated from protobuf field <code>float Fps = 7;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setFps($var)
+    {
+        GPBUtil::checkFloat($var);
+        $this->Fps = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodVolumeInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodVolumeInfo.php
new file mode 100755
index 0000000..3efed9c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VodVolumeInfo.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_common.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VodVolumeInfo</code>
+ */
+class VodVolumeInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响度
+     *
+     * Generated from protobuf field <code>double Loudness = 1;</code>
+     */
+    protected $Loudness = 0.0;
+    /**
+     *峰值
+     *
+     * Generated from protobuf field <code>double Peak = 2;</code>
+     */
+    protected $Peak = 0.0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type float $Loudness
+     *          响度
+     *     @type float $Peak
+     *          峰值
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodCommon::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响度
+     *
+     * Generated from protobuf field <code>double Loudness = 1;</code>
+     * @return float
+     */
+    public function getLoudness()
+    {
+        return $this->Loudness;
+    }
+
+    /**
+     *响度
+     *
+     * Generated from protobuf field <code>double Loudness = 1;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setLoudness($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->Loudness = $var;
+
+        return $this;
+    }
+
+    /**
+     *峰值
+     *
+     * Generated from protobuf field <code>double Peak = 2;</code>
+     * @return float
+     */
+    public function getPeak()
+    {
+        return $this->Peak;
+    }
+
+    /**
+     *峰值
+     *
+     * Generated from protobuf field <code>double Peak = 2;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setPeak($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->Peak = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VolumeInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VolumeInfo.php
new file mode 100755
index 0000000..8a8aeb1
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/VolumeInfo.php
@@ -0,0 +1,167 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_workflow.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.VolumeInfo</code>
+ */
+class VolumeInfo extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响度, 单位:LUFS
+     *
+     * Generated from protobuf field <code>double Loudness = 1;</code>
+     */
+    protected $Loudness = 0.0;
+    /**
+     * 峰值,范围:(0, 1.0], 峰值归一化
+     *
+     * Generated from protobuf field <code>double Peak = 2;</code>
+     */
+    protected $Peak = 0.0;
+    /**
+     * 均值,单位:dB
+     *
+     * Generated from protobuf field <code>double MeanVolume = 3;</code>
+     */
+    protected $MeanVolume = 0.0;
+    /**
+     * 最大值,单位:dB
+     *
+     * Generated from protobuf field <code>double MaxVolume = 4;</code>
+     */
+    protected $MaxVolume = 0.0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type float $Loudness
+     *           响度, 单位:LUFS
+     *     @type float $Peak
+     *           峰值,范围:(0, 1.0], 峰值归一化
+     *     @type float $MeanVolume
+     *           均值,单位:dB
+     *     @type float $MaxVolume
+     *           最大值,单位:dB
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodWorkflow::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响度, 单位:LUFS
+     *
+     * Generated from protobuf field <code>double Loudness = 1;</code>
+     * @return float
+     */
+    public function getLoudness()
+    {
+        return $this->Loudness;
+    }
+
+    /**
+     * 响度, 单位:LUFS
+     *
+     * Generated from protobuf field <code>double Loudness = 1;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setLoudness($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->Loudness = $var;
+
+        return $this;
+    }
+
+    /**
+     * 峰值,范围:(0, 1.0], 峰值归一化
+     *
+     * Generated from protobuf field <code>double Peak = 2;</code>
+     * @return float
+     */
+    public function getPeak()
+    {
+        return $this->Peak;
+    }
+
+    /**
+     * 峰值,范围:(0, 1.0], 峰值归一化
+     *
+     * Generated from protobuf field <code>double Peak = 2;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setPeak($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->Peak = $var;
+
+        return $this;
+    }
+
+    /**
+     * 均值,单位:dB
+     *
+     * Generated from protobuf field <code>double MeanVolume = 3;</code>
+     * @return float
+     */
+    public function getMeanVolume()
+    {
+        return $this->MeanVolume;
+    }
+
+    /**
+     * 均值,单位:dB
+     *
+     * Generated from protobuf field <code>double MeanVolume = 3;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setMeanVolume($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->MeanVolume = $var;
+
+        return $this;
+    }
+
+    /**
+     * 最大值,单位:dB
+     *
+     * Generated from protobuf field <code>double MaxVolume = 4;</code>
+     * @return float
+     */
+    public function getMaxVolume()
+    {
+        return $this->MaxVolume;
+    }
+
+    /**
+     * 最大值,单位:dB
+     *
+     * Generated from protobuf field <code>double MaxVolume = 4;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setMaxVolume($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->MaxVolume = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/WorkflowExecution.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/WorkflowExecution.php
new file mode 100755
index 0000000..9770deb
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/WorkflowExecution.php
@@ -0,0 +1,581 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_workflow.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.WorkflowExecution</code>
+ */
+class WorkflowExecution extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *工作流执行ID
+     *
+     * Generated from protobuf field <code>string RunId = 1;</code>
+     */
+    protected $RunId = '';
+    /**
+     * 视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 2;</code>
+     */
+    protected $Vid = '';
+    /**
+     *模板ID,即工作流ID
+     *
+     * Generated from protobuf field <code>string TemplateId = 3;</code>
+     */
+    protected $TemplateId = '';
+    /**
+     * 工作流名称
+     *
+     * Generated from protobuf field <code>string TemplateName = 4;</code>
+     */
+    protected $TemplateName = '';
+    /**
+     * 空间名
+     *
+     * Generated from protobuf field <code>string SpaceName = 5;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     *执行状态
+     *
+     * Generated from protobuf field <code>string Status = 6;</code>
+     */
+    protected $Status = '';
+    /**
+     *任务队列ID
+     *
+     * Generated from protobuf field <code>string TaskListId = 7;</code>
+     */
+    protected $TaskListId = '';
+    /**
+     *闲时转码
+     *
+     * Generated from protobuf field <code>bool EnableLowPriority = 8;</code>
+     */
+    protected $EnableLowPriority = false;
+    /**
+     *任务来源
+     *
+     * Generated from protobuf field <code>string JobSource = 9;</code>
+     */
+    protected $JobSource = '';
+    /**
+     *任务创建时间,即触发任务的时间,格式:rfc3339。如果不存在则为空
+     *
+     * Generated from protobuf field <code>.google.protobuf.Timestamp CreateTime = 10;</code>
+     */
+    protected $CreateTime = null;
+    /**
+     *任务开始执行时间,即任务被调度到的时间戳,可用于计算执行时间,格式:rfc3339。如果不存在则为空
+     *
+     * Generated from protobuf field <code>.google.protobuf.Timestamp StartTime = 11;</code>
+     */
+    protected $StartTime = null;
+    /**
+     *任务结束时间,可用于排序,格式:格式:rfc3339。如果不存在则为空
+     *
+     * Generated from protobuf field <code>.google.protobuf.Timestamp EndTime = 12;</code>
+     */
+    protected $EndTime = null;
+    /**
+     *工作流输入参数
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.WorkflowParams Input = 13;</code>
+     */
+    protected $Input = null;
+    /**
+     *转码记录优先级
+     *
+     * Generated from protobuf field <code>int32 Priority = 14;</code>
+     */
+    protected $Priority = 0;
+    /**
+     *回调透传参数
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 15;</code>
+     */
+    protected $CallbackArgs = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $RunId
+     *          工作流执行ID
+     *     @type string $Vid
+     *           视频ID
+     *     @type string $TemplateId
+     *          模板ID,即工作流ID
+     *     @type string $TemplateName
+     *           工作流名称
+     *     @type string $SpaceName
+     *           空间名
+     *     @type string $Status
+     *          执行状态
+     *     @type string $TaskListId
+     *          任务队列ID
+     *     @type bool $EnableLowPriority
+     *          闲时转码
+     *     @type string $JobSource
+     *          任务来源
+     *     @type \Google\Protobuf\Timestamp $CreateTime
+     *          任务创建时间,即触发任务的时间,格式:rfc3339。如果不存在则为空
+     *     @type \Google\Protobuf\Timestamp $StartTime
+     *          任务开始执行时间,即任务被调度到的时间戳,可用于计算执行时间,格式:rfc3339。如果不存在则为空
+     *     @type \Google\Protobuf\Timestamp $EndTime
+     *          任务结束时间,可用于排序,格式:格式:rfc3339。如果不存在则为空
+     *     @type \Volc\Service\Vod\Models\Business\WorkflowParams $Input
+     *          工作流输入参数
+     *     @type int $Priority
+     *          转码记录优先级
+     *     @type string $CallbackArgs
+     *          回调透传参数
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodWorkflow::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *工作流执行ID
+     *
+     * Generated from protobuf field <code>string RunId = 1;</code>
+     * @return string
+     */
+    public function getRunId()
+    {
+        return $this->RunId;
+    }
+
+    /**
+     *工作流执行ID
+     *
+     * Generated from protobuf field <code>string RunId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setRunId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->RunId = $var;
+
+        return $this;
+    }
+
+    /**
+     * 视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 2;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     * 视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     *模板ID,即工作流ID
+     *
+     * Generated from protobuf field <code>string TemplateId = 3;</code>
+     * @return string
+     */
+    public function getTemplateId()
+    {
+        return $this->TemplateId;
+    }
+
+    /**
+     *模板ID,即工作流ID
+     *
+     * Generated from protobuf field <code>string TemplateId = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTemplateId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TemplateId = $var;
+
+        return $this;
+    }
+
+    /**
+     * 工作流名称
+     *
+     * Generated from protobuf field <code>string TemplateName = 4;</code>
+     * @return string
+     */
+    public function getTemplateName()
+    {
+        return $this->TemplateName;
+    }
+
+    /**
+     * 工作流名称
+     *
+     * Generated from protobuf field <code>string TemplateName = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTemplateName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TemplateName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 空间名
+     *
+     * Generated from protobuf field <code>string SpaceName = 5;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * 空间名
+     *
+     * Generated from protobuf field <code>string SpaceName = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     *执行状态
+     *
+     * Generated from protobuf field <code>string Status = 6;</code>
+     * @return string
+     */
+    public function getStatus()
+    {
+        return $this->Status;
+    }
+
+    /**
+     *执行状态
+     *
+     * Generated from protobuf field <code>string Status = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStatus($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Status = $var;
+
+        return $this;
+    }
+
+    /**
+     *任务队列ID
+     *
+     * Generated from protobuf field <code>string TaskListId = 7;</code>
+     * @return string
+     */
+    public function getTaskListId()
+    {
+        return $this->TaskListId;
+    }
+
+    /**
+     *任务队列ID
+     *
+     * Generated from protobuf field <code>string TaskListId = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTaskListId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TaskListId = $var;
+
+        return $this;
+    }
+
+    /**
+     *闲时转码
+     *
+     * Generated from protobuf field <code>bool EnableLowPriority = 8;</code>
+     * @return bool
+     */
+    public function getEnableLowPriority()
+    {
+        return $this->EnableLowPriority;
+    }
+
+    /**
+     *闲时转码
+     *
+     * Generated from protobuf field <code>bool EnableLowPriority = 8;</code>
+     * @param bool $var
+     * @return $this
+     */
+    public function setEnableLowPriority($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->EnableLowPriority = $var;
+
+        return $this;
+    }
+
+    /**
+     *任务来源
+     *
+     * Generated from protobuf field <code>string JobSource = 9;</code>
+     * @return string
+     */
+    public function getJobSource()
+    {
+        return $this->JobSource;
+    }
+
+    /**
+     *任务来源
+     *
+     * Generated from protobuf field <code>string JobSource = 9;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setJobSource($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->JobSource = $var;
+
+        return $this;
+    }
+
+    /**
+     *任务创建时间,即触发任务的时间,格式:rfc3339。如果不存在则为空
+     *
+     * Generated from protobuf field <code>.google.protobuf.Timestamp CreateTime = 10;</code>
+     * @return \Google\Protobuf\Timestamp|null
+     */
+    public function getCreateTime()
+    {
+        return $this->CreateTime;
+    }
+
+    public function hasCreateTime()
+    {
+        return isset($this->CreateTime);
+    }
+
+    public function clearCreateTime()
+    {
+        unset($this->CreateTime);
+    }
+
+    /**
+     *任务创建时间,即触发任务的时间,格式:rfc3339。如果不存在则为空
+     *
+     * Generated from protobuf field <code>.google.protobuf.Timestamp CreateTime = 10;</code>
+     * @param \Google\Protobuf\Timestamp $var
+     * @return $this
+     */
+    public function setCreateTime($var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\Timestamp::class);
+        $this->CreateTime = $var;
+
+        return $this;
+    }
+
+    /**
+     *任务开始执行时间,即任务被调度到的时间戳,可用于计算执行时间,格式:rfc3339。如果不存在则为空
+     *
+     * Generated from protobuf field <code>.google.protobuf.Timestamp StartTime = 11;</code>
+     * @return \Google\Protobuf\Timestamp|null
+     */
+    public function getStartTime()
+    {
+        return $this->StartTime;
+    }
+
+    public function hasStartTime()
+    {
+        return isset($this->StartTime);
+    }
+
+    public function clearStartTime()
+    {
+        unset($this->StartTime);
+    }
+
+    /**
+     *任务开始执行时间,即任务被调度到的时间戳,可用于计算执行时间,格式:rfc3339。如果不存在则为空
+     *
+     * Generated from protobuf field <code>.google.protobuf.Timestamp StartTime = 11;</code>
+     * @param \Google\Protobuf\Timestamp $var
+     * @return $this
+     */
+    public function setStartTime($var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\Timestamp::class);
+        $this->StartTime = $var;
+
+        return $this;
+    }
+
+    /**
+     *任务结束时间,可用于排序,格式:格式:rfc3339。如果不存在则为空
+     *
+     * Generated from protobuf field <code>.google.protobuf.Timestamp EndTime = 12;</code>
+     * @return \Google\Protobuf\Timestamp|null
+     */
+    public function getEndTime()
+    {
+        return $this->EndTime;
+    }
+
+    public function hasEndTime()
+    {
+        return isset($this->EndTime);
+    }
+
+    public function clearEndTime()
+    {
+        unset($this->EndTime);
+    }
+
+    /**
+     *任务结束时间,可用于排序,格式:格式:rfc3339。如果不存在则为空
+     *
+     * Generated from protobuf field <code>.google.protobuf.Timestamp EndTime = 12;</code>
+     * @param \Google\Protobuf\Timestamp $var
+     * @return $this
+     */
+    public function setEndTime($var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\Timestamp::class);
+        $this->EndTime = $var;
+
+        return $this;
+    }
+
+    /**
+     *工作流输入参数
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.WorkflowParams Input = 13;</code>
+     * @return \Volc\Service\Vod\Models\Business\WorkflowParams|null
+     */
+    public function getInput()
+    {
+        return $this->Input;
+    }
+
+    public function hasInput()
+    {
+        return isset($this->Input);
+    }
+
+    public function clearInput()
+    {
+        unset($this->Input);
+    }
+
+    /**
+     *工作流输入参数
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.WorkflowParams Input = 13;</code>
+     * @param \Volc\Service\Vod\Models\Business\WorkflowParams $var
+     * @return $this
+     */
+    public function setInput($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\WorkflowParams::class);
+        $this->Input = $var;
+
+        return $this;
+    }
+
+    /**
+     *转码记录优先级
+     *
+     * Generated from protobuf field <code>int32 Priority = 14;</code>
+     * @return int
+     */
+    public function getPriority()
+    {
+        return $this->Priority;
+    }
+
+    /**
+     *转码记录优先级
+     *
+     * Generated from protobuf field <code>int32 Priority = 14;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setPriority($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Priority = $var;
+
+        return $this;
+    }
+
+    /**
+     *回调透传参数
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 15;</code>
+     * @return string
+     */
+    public function getCallbackArgs()
+    {
+        return $this->CallbackArgs;
+    }
+
+    /**
+     *回调透传参数
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 15;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCallbackArgs($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CallbackArgs = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/WorkflowParams.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/WorkflowParams.php
new file mode 100755
index 0000000..3e936e8
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Business/WorkflowParams.php
@@ -0,0 +1,109 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_workflow.proto
+
+namespace Volc\Service\Vod\Models\Business;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Business.WorkflowParams</code>
+ */
+class WorkflowParams extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 覆盖参数
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.OverrideParams OverrideParams = 1;</code>
+     */
+    protected $OverrideParams = null;
+    /**
+     * 条件变量
+     *
+     * Generated from protobuf field <code>map<string, bool> Condition = 2;</code>
+     */
+    private $Condition;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Vod\Models\Business\OverrideParams $OverrideParams
+     *           覆盖参数
+     *     @type array|\Google\Protobuf\Internal\MapField $Condition
+     *           条件变量
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\VodWorkflow::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 覆盖参数
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.OverrideParams OverrideParams = 1;</code>
+     * @return \Volc\Service\Vod\Models\Business\OverrideParams|null
+     */
+    public function getOverrideParams()
+    {
+        return $this->OverrideParams;
+    }
+
+    public function hasOverrideParams()
+    {
+        return isset($this->OverrideParams);
+    }
+
+    public function clearOverrideParams()
+    {
+        unset($this->OverrideParams);
+    }
+
+    /**
+     * 覆盖参数
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.OverrideParams OverrideParams = 1;</code>
+     * @param \Volc\Service\Vod\Models\Business\OverrideParams $var
+     * @return $this
+     */
+    public function setOverrideParams($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\OverrideParams::class);
+        $this->OverrideParams = $var;
+
+        return $this;
+    }
+
+    /**
+     * 条件变量
+     *
+     * Generated from protobuf field <code>map<string, bool> Condition = 2;</code>
+     * @return \Google\Protobuf\Internal\MapField
+     */
+    public function getCondition()
+    {
+        return $this->Condition;
+    }
+
+    /**
+     * 条件变量
+     *
+     * Generated from protobuf field <code>map<string, bool> Condition = 2;</code>
+     * @param array|\Google\Protobuf\Internal\MapField $var
+     * @return $this
+     */
+    public function setCondition($var)
+    {
+        $arr = GPBUtil::checkMapField($var, \Google\Protobuf\Internal\GPBType::STRING, \Google\Protobuf\Internal\GPBType::BOOL);
+        $this->Condition = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/RequestVod.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/RequestVod.php
new file mode 100755
index 0000000..7e52677
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/RequestVod.php
@@ -0,0 +1,398 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\GPBMetadata;
+
+class RequestVod
+{
+    public static $is_initialized = false;
+
+    public static function initOnce() {
+        $pool = \Google\Protobuf\Internal\DescriptorPool::getGeneratedPool();
+
+        if (static::$is_initialized == true) {
+          return;
+        }
+        \GPBMetadata\Google\Protobuf\Wrappers::initOnce();
+        \Volc\Service\Vod\Models\GPBMetadata\VodWorkflow::initOnce();
+        \Volc\Service\Vod\Models\GPBMetadata\VodUpload::initOnce();
+        $pool->internalAddGeneratedFile(
+            '
+�=
+vod/request/request_vod.protoVolcengine.Vod.Models.Requestvod/business/vod_workflow.protovod/business/vod_upload.proto"�
+VodGetAllPlayInfoRequest
+Vids (	
+Formats (	
+Codecs (	
+Definitions (	
+	FileTypes (	
+	LogoTypes (	
+NeedEncryptStream (	
+Ssl (	
+
+NeedThumbs	 (	
+NeedBarrageMask
+ (	
+CdnType (	
+	UnionInfo (	
+	PlayScene
 (	
+DrmExpireTimestamp (	
+HDRType (	 
+KeyFrameAlignmentVersion (	
+
+UserAction (	
+Quality (	"�
+VodGetPlayInfoRequest
+Vid (	
+Format (	
+Codec (	
+
+Definition (	
+FileType (	
+LogoType (	
+Base64 (	
+Ssl (	
+
+NeedThumbs	 (	
+NeedBarrageMask
+ (	
+CdnType (	
+	UnionInfo (	
+
HDRDefinition
 (	
+	PlayScene (	
+DrmExpireTimestamp (	
+Quality (	"g
+VodGetPrivateDrmPlayAuthRequest
+DrmType (	
+Vid (	
+PlayAuthIds (	
+	UnionInfo (	"Q
+VodGetHlsDecryptionKeyRequest
+DrmAuthToken (	
+
+Ak (	
+Source (	"�
++VodGetPlayInfoWithLiveTimeShiftSceneRequest
+	StoreUris (	
+	SpaceName (	
+Ssl (	
+ExpireTimestamp (	
+NeedComposeBucketName (	"m
+VodUrlUploadRequest
+	SpaceName (	C
+URLSets (22.Volcengine.Vod.Models.Business.VodUrlUploadURLSet"/
+VodQueryUploadTaskInfoRequest
+JobIds (	"x
+VodApplyUploadInfoRequest
+	SpaceName (	
+
+SessionKey (	
+FileSize (
+FileType (	
+FileName (	"w
+VodUploadMediaRequest
+	SpaceName (	
+FilePath (	
+CallbackArgs (	
+	Functions (	
+FileName (	"�
+VodUploadMaterialRequest
+	SpaceName (	
+FilePath (	
+CallbackArgs (	
+	Functions (	
+FileType (	
+FileName (	"l
+VodCommitUploadInfoRequest
+	SpaceName (	
+
+SessionKey (	
+CallbackArgs (	
+	Functions (	"=
+VodUrlUploadJsonRequest
+	SpaceName (	
+URLSets (	".
+VodGetRecommendedPosterRequest
+Vids (	"A
+"VodUpdateMediaPublishStatusRequest
+Vid (	
+Status (	"�
+VodUpdateMediaInfoRequest
+Vid (	/
+	PosterUri (2.google.protobuf.StringValue+
+Title (2.google.protobuf.StringValue1
+Description (2.google.protobuf.StringValue*
+Tags (2.google.protobuf.StringValue5
+ClassificationId (2.google.protobuf.Int64Value"\'
+VodGetMediaInfosRequest
+Vids (	";
+VodDeleteMediaRequest
+Vids (	
+CallbackArgs (	"P
+VodDeleteTranscodesRequest
+Vid (	
+FileIds (	
+CallbackArgs (	"�
+VodGetMediaListRequest
+	SpaceName (	
+Vid (	
+Status (	
+Order (	
+Tags (	
+	StartTime (	
+EndTime (	
+Offset (	
+PageSize	 (	
+ClassificationIds
+ (	"�
+VodGetSubtitleInfoListRequest
+Vid (	
+FileIds (	
+	Languages (	
+Formats (	
+LanguageIds (	
+SubtitleIds (	
+Status (	
+Title (	
+Tag	 (	
+Offset
+ (	
+PageSize (	
+Ssl (	"r
+VodUpdateSubtitleStatusRequest
+Vid (	
+FileIds (	
+	Languages (	
+Formats (	
+Status (	"�
+VodUpdateSubtitleInfoRequest
+Vid (	
+FileId (	
+Language (	
+Format (	+
+Title (2.google.protobuf.StringValue)
+Tag (2.google.protobuf.StringValue"w
+ VodGetAuditFramesForAuditRequest
+Vid (	
+Strategy (	
+MinNumberOfFrames (	
+MaxNumberOfFrames (	"�
+VodGetMLFramesForAuditRequest
+Vid (	
+Strategy (	
+FrameOpt (	
+FrameFps (	
+NumberOfFrames (	
+CutTimeMills (	
+NeedFirstFrame (	
+
NeedLastFrame (	
+
StartTimeMill	 (	
+EndTimeMill
+ (	
+MinNumberOfFrames (	
+MaxNumberOfFrames (	"U
+!VodGetBetterFramesForAuditRequest
+Vid (	
+Strategy (	
+	CoverRate (	"?
+VodGetAudioInfoForAuditRequest
+Vid (	
+Strategy (	"P
+/VodGetAutomaticSpeechRecognitionForAuditRequest
+Vid (	
+Strategy (	"I
+(VodGetAudioEventDetectionForAuditRequest
+Vid (	
+Strategy (	"q
+#VodCreateVideoClassificationRequest
+	SpaceName (	
+Level (
+ParentId (
+Classification (	"j
+#VodUpdateVideoClassificationRequest
+	SpaceName (	
+ClassificationId (
+Classification (	"R
+#VodDeleteVideoClassificationRequest
+	SpaceName (	
+ClassificationId ("Q
+"VodListVideoClassificationsRequest
+	SpaceName (	
+ClassificationId ("&
+VodListSnapshotsRequest
+Vid (	"�
+VodStartWorkflowRequest
+Vid (	
+
+TemplateId (	=
+Input (2..Volcengine.Vod.Models.Business.WorkflowParams
+Priority (
+CallbackArgs (	
+EnableLowPriority ("D
+!VodRetrieveTranscodeResultRequest
+Vid (	
+
+ResultType (	"�
+VodListWorkflowExecutionRequest
+RunId (	
+Vid (	
+	SpaceName (	
+
+TemplateId (	
+
+TaskListId (	
+EnableLowPriority (	
+	JobSource (	
+Status (	
+	StartTime	 (	
+EndTime
+ (	
+PageSize (	
+Offset (	
+
+OrderByKey
 (	
+Order (	"5
+$VodGetWorkflowExecutionDetailRequest
+RunId (	"5
+$VodGetWorkflowExecutionStatusRequest
+RunId (	"�
+#VodSubmitDirectEditTaskAsyncRequest
+Uploader (	
+Application (	
+	EditParam (
+Priority (
+CallbackUri (	
+CallbackArgs (	"/
+VodGetDirectEditResultRequest
+ReqIds (	"0
+VodGetDirectEditProgressRequest
+ReqId (	"v
+VodCreateSpaceRequest
+	SpaceName (	
+ProjectName (	
+Description (	
+Region (	
+UserName (	"-
+VodGetSpaceDetailRequest
+	SpaceName (	"4
+VodListSpaceRequest
+Offset (
+Limit ("u
+VodUpdateSpaceRequest
+	SpaceName (	
+SourceProjectName (	
+TargetProjectName (	
+Description (	"^
+!VodUpdateSpaceUploadConfigRequest
+	SpaceName (	
+	ConfigKey (	
+ConfigValue (	"�
+%VodDescribeVodSpaceStorageDataRequest
+	SpaceList (	
+	StartTime (	
+EndTime (	
+Aggregation (
+Type (	")
+VodListDomainRequest
+	SpaceName (	"O
+VodCreateCdnRefreshTaskRequest
+	SpaceName (	
+Urls (	
+Type (	"A
+VodCreateCdnPreloadTaskRequest
+	SpaceName (	
+Urls (	"�
+VodListCdnTasksRequest
+	SpaceName (	
+TaskId (	
+
+DomainName (	
+TaskType (	
+Status (	
+StartTimestamp (
+EndTimestamp (
+PageNum (
+PageSize	 ("n
+VodListCdnAccessLogRequest
+Domains (	
+StartTimestamp (
+EndTimestamp (
+	SpaceName (	"p
+VodListCdnTopAccessUrlRequest
+Domains (	
+StartTimestamp (
+EndTimestamp (
+SortType (	"�
+(VodDescribeVodDomainBandwidthDataRequest
+
+DomainList (	
+	StartTime (	
+EndTime (	
+Aggregation (
+
BandwidthType (	
+Area (	"�
+VodListCdnUsageDataRequest
+Domains (	
+Interval (	
+StartTimestamp (
+EndTimestamp (
+DataType (	
+Metric (	"�
+VodListCdnStatusDataRequest
+Domains (	
+Interval (	
+StartTimestamp (
+EndTimestamp (
+DataType (	
+Metric (	"\'
+VodDescribeIPInfoRequest
+Ips (	"|
+VodListCdnPvDataRequest
+Domains (	
+Interval (	
+StartTimestamp (
+EndTimestamp (
+DataType (	"�
+&VodDescribeVodDomainTrafficDataRequest
+
+DomainList (	
+	StartTime (	
+EndTime (	
+Aggregation (
+TrafficType (	"X
+!VodAddCallbackSubscriptionRequest
+	SpaceName (	
+Url (	
+ContentType (	"h
+VodSetCallbackEventRequest
+	SpaceName (	
+Events (	
+AuthEnabled (	
+
+PrivateKey (	"�
+&VodGetSmartStrategyLitePlayInfoRequest
+PlayUrl (	
+Format (	
+Codec (	
+
+Definition (	
+FileType (	
+LogoType (	
+Ssl (	
+
+NeedThumbs (	
+NeedBarrageMask	 (	
+	UnionInfo
+ (	
+
HDRDefinition (	"%
+VodGetAppInfoRequest
+AppId (B�
+(com.volcengine.service.vod.model.requestB
+VodRequestPZ@github.com/volcengine/volc-sdk-golang/service/vod/models/request���Volc\\Service\\Vod\\Models\\Request�#Volc\\Service\\Vod\\Models\\GPBMetadatabproto3'
+        , true);
+
+        static::$is_initialized = true;
+    }
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/ResponseVod.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/ResponseVod.php
new file mode 100755
index 0000000..b3a48f2
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/ResponseVod.php
@@ -0,0 +1,208 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\GPBMetadata;
+
+class ResponseVod
+{
+    public static $is_initialized = false;
+
+    public static function initOnce() {
+        $pool = \Google\Protobuf\Internal\DescriptorPool::getGeneratedPool();
+
+        if (static::$is_initialized == true) {
+          return;
+        }
+        \Volc\Service\Base\Models\GPBMetadata\Base::initOnce();
+        \Volc\Service\Vod\Models\GPBMetadata\VodPlay::initOnce();
+        \Volc\Service\Vod\Models\GPBMetadata\VodMedia::initOnce();
+        \Volc\Service\Vod\Models\GPBMetadata\VodUpload::initOnce();
+        \Volc\Service\Vod\Models\GPBMetadata\VodWorkflow::initOnce();
+        \Volc\Service\Vod\Models\GPBMetadata\VodEdit::initOnce();
+        \Volc\Service\Vod\Models\GPBMetadata\VodSpace::initOnce();
+        \Volc\Service\Vod\Models\GPBMetadata\VodCdn::initOnce();
+        \Volc\Service\Vod\Models\GPBMetadata\VodCommon::initOnce();
+        \Volc\Service\Vod\Models\GPBMetadata\VodSmartStrategy::initOnce();
+        \Volc\Service\Vod\Models\GPBMetadata\VodAppsManage::initOnce();
+        $pool->internalAddGeneratedFile(
+            '
+�T
+vod/response/response_vod.protoVolcengine.Vod.Models.Responsevod/business/vod_play.protovod/business/vod_media.protovod/business/vod_upload.protovod/business/vod_workflow.protovod/business/vod_edit.protovod/business/vod_space.protovod/business/vod_cdn.protovod/business/vod_common.proto%vod/business/vod_smart_strategy.proto"vod/business/vod_apps_manage.proto"�
+VodGetAllPlayInfoResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataD
+Result (24.Volcengine.Vod.Models.Business.VodAllPlayInfoResult"�
+VodGetPlayInfoResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata@
+Result (20.Volcengine.Vod.Models.Business.VodPlayInfoModel"�
+VodGetOriginalPlayInfoResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataL
+Result (2<.Volcengine.Vod.Models.Business.VodGetOriginalPlayInfoResult"�
+ VodGetPrivateDrmPlayAuthResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataN
+Result (2>.Volcengine.Vod.Models.Business.VodGetPrivateDrmPlayAuthResult"�
+VodGetHlsDecryptionKeyResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataL
+Result (2<.Volcengine.Vod.Models.Business.VodGetHlsDecryptionKeyResult"�
+,VodGetPlayInfoWithLiveTimeShiftSceneResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataZ
+Result (2J.Volcengine.Vod.Models.Business.VodGetPlayInfoWithLiveTimeShiftSceneResult"�
+VodUploadMediaResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata=
+Result (2-.Volcengine.Vod.Models.Business.VodCommitData"�
+VodQueryUploadTaskInfoResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata<
+Result (2,.Volcengine.Vod.Models.Business.VodQueryData"�
+VodUrlUploadResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataB
+Result (22.Volcengine.Vod.Models.Business.VodUrlResponseData"�
+VodApplyUploadInfoResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataH
+Result (28.Volcengine.Vod.Models.Business.VodApplyUploadInfoResult"�
+VodCommitUploadInfoResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataI
+Result (29.Volcengine.Vod.Models.Business.VodCommitUploadInfoResult"�
+VodGetMediaInfosResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataD
+Result (24.Volcengine.Vod.Models.Business.VodGetMediaInfosData"e
+VodUpdateMediaInfoResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata"�
+VodGetRecommendedPosterResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataC
+Result (23.Volcengine.Vod.Models.Business.VodGetRecPosterData"n
+#VodUpdateMediaPublishStatusResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata"�
+VodDeleteMediaResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataB
+Result (22.Volcengine.Vod.Models.Business.VodDeleteMediaData"�
+VodDeleteTranscodesResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataG
+Result (27.Volcengine.Vod.Models.Business.VodDeleteTranscodesData"�
+VodGetMediaListResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataC
+Result (23.Volcengine.Vod.Models.Business.VodGetMediaListData"�
+VodGetSubtitleInfoListResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataJ
+Result (2:.Volcengine.Vod.Models.Business.VodGetSubtitleInfoListData"�
+VodUpdateSubtitleStatusResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataK
+Result (2;.Volcengine.Vod.Models.Business.VodUpdateSubtitleStatusData"h
+VodUpdateSubtitleInfoResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata"�
+!VodGetAuditFramesForAuditResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataJ
+Result (2:.Volcengine.Vod.Models.Business.VodGetFramesForAuditResult"�
+VodGetMLFramesForAuditResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataJ
+Result (2:.Volcengine.Vod.Models.Business.VodGetFramesForAuditResult"�
+"VodGetBetterFramesForAuditResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataP
+Result (2@.Volcengine.Vod.Models.Business.VodGetBetterFramesForAuditResult"�
+VodGetAudioInfoForAuditResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataM
+Result (2=.Volcengine.Vod.Models.Business.VodGetAudioInfoForAuditResult"�
+0VodGetAutomaticSpeechRecognitionForAuditResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata^
+Result (2N.Volcengine.Vod.Models.Business.VodGetAutomaticSpeechRecognitionForAuditResult"�
+)VodGetAudioEventDetectionForAuditResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataW
+Result (2G.Volcengine.Vod.Models.Business.VodGetAudioEventDetectionForAuditResult"�
+$VodCreateVideoClassificationResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataP
+Result (2@.Volcengine.Vod.Models.Business.VodCreateVideoClassificationData"o
+$VodUpdateVideoClassificationResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata"o
+$VodDeleteVideoClassificationResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata"�
+#VodListVideoClassificationsResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataK
+Result (2;.Volcengine.Vod.Models.Business.VodVideoClassificationsData"�
+VodListSnapshotsResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata?
+Result (2/.Volcengine.Vod.Models.Business.VodSnapshotData"�
+VodStartWorkflowResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataF
+Result (26.Volcengine.Vod.Models.Business.VodStartWorkflowResult"�
+"VodRetrieveTranscodeResultResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata?
+Result (2/.Volcengine.Vod.Models.Business.TranscodeResult"�
+ VodListWorkflowExecutionResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataN
+Result (2>.Volcengine.Vod.Models.Business.VodListWorkflowExecutionResult"�
+%VodGetWorkflowExecutionDetailResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataS
+Result (2C.Volcengine.Vod.Models.Business.VodGetWorkflowExecutionDetailResult"�
+%VodGetWorkflowExecutionStatusResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataA
+Result (21.Volcengine.Vod.Models.Business.WorkflowExecution"�
+$VodSubmitDirectEditTaskAsyncResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataO
+Result (2?.Volcengine.Vod.Models.Business.SubmitDirectEditTaskAsyncResult"�
+ VodGetDirectEditProgressResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataE
+Result (25.Volcengine.Vod.Models.Business.GetDirectEditProgress"�
+VodGetDirectEditResultResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataC
+Result (23.Volcengine.Vod.Models.Business.GetDirectEditResult"a
+VodCreateSpaceResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata"�
+VodListSpaceResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata<
+Result (2,.Volcengine.Vod.Models.Business.VodSpaceInfo"�
+VodGetSpaceDetailResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata<
+Result (2,.Volcengine.Vod.Models.Business.VodSpaceInfo"a
+VodUpdateSpaceResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata"m
+"VodUpdateSpaceUploadConfigResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata"�
+&VodDescribeVodSpaceStorageDataResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataT
+Result (2D.Volcengine.Vod.Models.Business.VodDescribeVodSpaceStorageDataResult"�
+VodListDomainResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataC
+Result (23.Volcengine.Vod.Models.Business.VodDomainConfigInfo"�
+VodCreateCdnRefreshTaskResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataF
+Result (26.Volcengine.Vod.Models.Business.VodCreateCdnTaskResult"�
+VodCreateCdnPreloadTaskResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataF
+Result (26.Volcengine.Vod.Models.Business.VodCreateCdnTaskResult"�
+VodListCdnTasksResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata@
+Result (20.Volcengine.Vod.Models.Business.VodCdnTaskResult"�
+VodListCdnAccessLogResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataI
+Result (29.Volcengine.Vod.Models.Business.VodListCdnAccessLogResult"�
+VodListCdnTopAccessUrlResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataL
+Result (2<.Volcengine.Vod.Models.Business.VodListCdnTopAccessUrlResult"�
+)VodDescribeVodDomainBandwidthDataResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataW
+Result (2G.Volcengine.Vod.Models.Business.VodDescribeVodDomainBandwidthDataResult"�
+VodCdnStatisticsCommonResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataL
+Result (2<.Volcengine.Vod.Models.Business.VodCdnStatisticsCommonResult"�
+VodDescribeIPInfoResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata<
+Result (2,.Volcengine.Vod.Models.Business.VodCdnIpInfo"�
+\'VodDescribeVodDomainTrafficDataResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataU
+Result (2E.Volcengine.Vod.Models.Business.VodDescribeVodDomainTrafficDataResult"m
+"VodAddCallbackSubscriptionResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata"f
+VodSetCallbackEventResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadata"�
+\'VodGetSmartStrategyLitePlayInfoResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataU
+Result (2E.Volcengine.Vod.Models.Business.VodGetSmartStrategyLitePlayInfoResult"�
+VodGetAppInfoResponseG
+ResponseMetadata (2-.Volcengine.Base.Models.Base.ResponseMetadataC
+Result (23.Volcengine.Vod.Models.Business.VodGetAppInfoResultB�
+)com.volcengine.service.vod.model.responseBVodResponsePZAgithub.com/volcengine/volc-sdk-golang/service/vod/models/response��� Volc\\Service\\Vod\\Models\\Response�#Volc\\Service\\Vod\\Models\\GPBMetadatabproto3'
+        , true);
+
+        static::$is_initialized = true;
+    }
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodAppsManage.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodAppsManage.php
new file mode 100755
index 0000000..99ed95b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodAppsManage.php
Binary files differ
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodCallback.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodCallback.php
new file mode 100755
index 0000000..6b94e9c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodCallback.php
Binary files differ
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodCdn.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodCdn.php
new file mode 100755
index 0000000..6f87d8b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodCdn.php
Binary files differ
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodCommon.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodCommon.php
new file mode 100755
index 0000000..2a8ca56
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodCommon.php
Binary files differ
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodEdit.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodEdit.php
new file mode 100755
index 0000000..7783f63
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodEdit.php
@@ -0,0 +1,39 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/business/vod_edit.proto
+
+namespace Volc\Service\Vod\Models\GPBMetadata;
+
+class VodEdit
+{
+    public static $is_initialized = false;
+
+    public static function initOnce() {
+        $pool = \Google\Protobuf\Internal\DescriptorPool::getGeneratedPool();
+
+        if (static::$is_initialized == true) {
+          return;
+        }
+        $pool->internalAddGeneratedFile(
+            '
+�
+vod/business/vod_edit.protoVolcengine.Vod.Models.Business"0
+SubmitDirectEditTaskAsyncResult
+ReqId (	"\'
+GetDirectEditProgress
+Result ("�
+GetDirectEditResult
+ReqId (	
+	EditParam (	
+Priority (
+CallbackUri (	
+CallbackArgs (	
+Status (	
+	OutputVid (	B�
+)com.volcengine.service.vod.model.businessBVodEditPZAgithub.com/volcengine/volc-sdk-golang/service/vod/models/business��� Volc\\Service\\Vod\\Models\\Business�#Volc\\Service\\Vod\\Models\\GPBMetadatabproto3'
+        , true);
+
+        static::$is_initialized = true;
+    }
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodMedia.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodMedia.php
new file mode 100755
index 0000000..08e894c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodMedia.php
Binary files differ
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodPlay.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodPlay.php
new file mode 100755
index 0000000..b025996
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodPlay.php
Binary files differ
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodSmartStrategy.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodSmartStrategy.php
new file mode 100755
index 0000000..a63dd29
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodSmartStrategy.php
Binary files differ
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodSpace.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodSpace.php
new file mode 100755
index 0000000..7311cd8
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodSpace.php
Binary files differ
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodUpload.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodUpload.php
new file mode 100755
index 0000000..d01238b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodUpload.php
Binary files differ
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodWorkflow.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodWorkflow.php
new file mode 100755
index 0000000..5b70d5e
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/GPBMetadata/VodWorkflow.php
Binary files differ
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodAddCallbackSubscriptionRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodAddCallbackSubscriptionRequest.php
new file mode 100755
index 0000000..f7f0572
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodAddCallbackSubscriptionRequest.php
@@ -0,0 +1,145 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * **********************************************************************
+ * 回调管理
+ * **********************************************************************
+ *
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodAddCallbackSubscriptionRequest</code>
+ */
+class VodAddCallbackSubscriptionRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     * 订阅URL 
+     *
+     * Generated from protobuf field <code>string Url = 2;</code>
+     */
+    protected $Url = '';
+    /**
+     * 添加订阅地址的内容类型。默认值为“application/octet-stream”。支持的取值为
+     * "application/json",
+     * "application/octet-stream"
+     *
+     * Generated from protobuf field <code>string ContentType = 3;</code>
+     */
+    protected $ContentType = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *           空间名 
+     *     @type string $Url
+     *           订阅URL 
+     *     @type string $ContentType
+     *           添加订阅地址的内容类型。默认值为“application/octet-stream”。支持的取值为
+     *           "application/json",
+     *           "application/octet-stream"
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 订阅URL 
+     *
+     * Generated from protobuf field <code>string Url = 2;</code>
+     * @return string
+     */
+    public function getUrl()
+    {
+        return $this->Url;
+    }
+
+    /**
+     * 订阅URL 
+     *
+     * Generated from protobuf field <code>string Url = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setUrl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Url = $var;
+
+        return $this;
+    }
+
+    /**
+     * 添加订阅地址的内容类型。默认值为“application/octet-stream”。支持的取值为
+     * "application/json",
+     * "application/octet-stream"
+     *
+     * Generated from protobuf field <code>string ContentType = 3;</code>
+     * @return string
+     */
+    public function getContentType()
+    {
+        return $this->ContentType;
+    }
+
+    /**
+     * 添加订阅地址的内容类型。默认值为“application/octet-stream”。支持的取值为
+     * "application/json",
+     * "application/octet-stream"
+     *
+     * Generated from protobuf field <code>string ContentType = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setContentType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->ContentType = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodApplyUploadInfoRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodApplyUploadInfoRequest.php
new file mode 100755
index 0000000..6eb08b7
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodApplyUploadInfoRequest.php
@@ -0,0 +1,201 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodApplyUploadInfoRequest</code>
+ */
+class VodApplyUploadInfoRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 上传的空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     * 上传重试时可选择带上的 Session 信息,作为下发参数的辅助信息 
+     *
+     * Generated from protobuf field <code>string SessionKey = 2;</code>
+     */
+    protected $SessionKey = '';
+    /**
+     * 上传的文件大小,单位为字节,非必填参数 
+     *
+     * Generated from protobuf field <code>double FileSize = 3;</code>
+     */
+    protected $FileSize = 0.0;
+    /**
+     * 上传的类型,可不填,默认为 media 
+     *
+     * Generated from protobuf field <code>string FileType = 4;</code>
+     */
+    protected $FileType = '';
+    /**
+     * 上传的文件在存储中的名字,即 bucket/key 中的 key 
+     *
+     * Generated from protobuf field <code>string FileName = 5;</code>
+     */
+    protected $FileName = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *           上传的空间名 
+     *     @type string $SessionKey
+     *           上传重试时可选择带上的 Session 信息,作为下发参数的辅助信息 
+     *     @type float $FileSize
+     *           上传的文件大小,单位为字节,非必填参数 
+     *     @type string $FileType
+     *           上传的类型,可不填,默认为 media 
+     *     @type string $FileName
+     *           上传的文件在存储中的名字,即 bucket/key 中的 key 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 上传的空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * 上传的空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 上传重试时可选择带上的 Session 信息,作为下发参数的辅助信息 
+     *
+     * Generated from protobuf field <code>string SessionKey = 2;</code>
+     * @return string
+     */
+    public function getSessionKey()
+    {
+        return $this->SessionKey;
+    }
+
+    /**
+     * 上传重试时可选择带上的 Session 信息,作为下发参数的辅助信息 
+     *
+     * Generated from protobuf field <code>string SessionKey = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSessionKey($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SessionKey = $var;
+
+        return $this;
+    }
+
+    /**
+     * 上传的文件大小,单位为字节,非必填参数 
+     *
+     * Generated from protobuf field <code>double FileSize = 3;</code>
+     * @return float
+     */
+    public function getFileSize()
+    {
+        return $this->FileSize;
+    }
+
+    /**
+     * 上传的文件大小,单位为字节,非必填参数 
+     *
+     * Generated from protobuf field <code>double FileSize = 3;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setFileSize($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->FileSize = $var;
+
+        return $this;
+    }
+
+    /**
+     * 上传的类型,可不填,默认为 media 
+     *
+     * Generated from protobuf field <code>string FileType = 4;</code>
+     * @return string
+     */
+    public function getFileType()
+    {
+        return $this->FileType;
+    }
+
+    /**
+     * 上传的类型,可不填,默认为 media 
+     *
+     * Generated from protobuf field <code>string FileType = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileType = $var;
+
+        return $this;
+    }
+
+    /**
+     * 上传的文件在存储中的名字,即 bucket/key 中的 key 
+     *
+     * Generated from protobuf field <code>string FileName = 5;</code>
+     * @return string
+     */
+    public function getFileName()
+    {
+        return $this->FileName;
+    }
+
+    /**
+     * 上传的文件在存储中的名字,即 bucket/key 中的 key 
+     *
+     * Generated from protobuf field <code>string FileName = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileName = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodCommitUploadInfoRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodCommitUploadInfoRequest.php
new file mode 100755
index 0000000..124abd8
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodCommitUploadInfoRequest.php
@@ -0,0 +1,167 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodCommitUploadInfoRequest</code>
+ */
+class VodCommitUploadInfoRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 上传的空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     * 上传 1005 阶段需要带上 1001 的 Session 
+     *
+     * Generated from protobuf field <code>string SessionKey = 2;</code>
+     */
+    protected $SessionKey = '';
+    /**
+     * 业务希望上传透传的信息,会在上传成功时返回给用户 
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 3;</code>
+     */
+    protected $CallbackArgs = '';
+    /**
+     * 上传的功能函数 
+     *
+     * Generated from protobuf field <code>string Functions = 4;</code>
+     */
+    protected $Functions = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *           上传的空间名 
+     *     @type string $SessionKey
+     *           上传 1005 阶段需要带上 1001 的 Session 
+     *     @type string $CallbackArgs
+     *           业务希望上传透传的信息,会在上传成功时返回给用户 
+     *     @type string $Functions
+     *           上传的功能函数 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 上传的空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * 上传的空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 上传 1005 阶段需要带上 1001 的 Session 
+     *
+     * Generated from protobuf field <code>string SessionKey = 2;</code>
+     * @return string
+     */
+    public function getSessionKey()
+    {
+        return $this->SessionKey;
+    }
+
+    /**
+     * 上传 1005 阶段需要带上 1001 的 Session 
+     *
+     * Generated from protobuf field <code>string SessionKey = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSessionKey($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SessionKey = $var;
+
+        return $this;
+    }
+
+    /**
+     * 业务希望上传透传的信息,会在上传成功时返回给用户 
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 3;</code>
+     * @return string
+     */
+    public function getCallbackArgs()
+    {
+        return $this->CallbackArgs;
+    }
+
+    /**
+     * 业务希望上传透传的信息,会在上传成功时返回给用户 
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCallbackArgs($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CallbackArgs = $var;
+
+        return $this;
+    }
+
+    /**
+     * 上传的功能函数 
+     *
+     * Generated from protobuf field <code>string Functions = 4;</code>
+     * @return string
+     */
+    public function getFunctions()
+    {
+        return $this->Functions;
+    }
+
+    /**
+     * 上传的功能函数 
+     *
+     * Generated from protobuf field <code>string Functions = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFunctions($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Functions = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodCreateCdnPreloadTaskRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodCreateCdnPreloadTaskRequest.php
new file mode 100755
index 0000000..9d354f6
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodCreateCdnPreloadTaskRequest.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodCreateCdnPreloadTaskRequest</code>
+ */
+class VodCreateCdnPreloadTaskRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     * 预热Url 
+     *
+     * Generated from protobuf field <code>string Urls = 2;</code>
+     */
+    protected $Urls = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *           空间名 
+     *     @type string $Urls
+     *           预热Url 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 预热Url 
+     *
+     * Generated from protobuf field <code>string Urls = 2;</code>
+     * @return string
+     */
+    public function getUrls()
+    {
+        return $this->Urls;
+    }
+
+    /**
+     * 预热Url 
+     *
+     * Generated from protobuf field <code>string Urls = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setUrls($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Urls = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodCreateCdnRefreshTaskRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodCreateCdnRefreshTaskRequest.php
new file mode 100755
index 0000000..7da7248
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodCreateCdnRefreshTaskRequest.php
@@ -0,0 +1,133 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodCreateCdnRefreshTaskRequest</code>
+ */
+class VodCreateCdnRefreshTaskRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     * 刷新Url或目录 
+     *
+     * Generated from protobuf field <code>string Urls = 2;</code>
+     */
+    protected $Urls = '';
+    /**
+     * 刷新任务类型 
+     *
+     * Generated from protobuf field <code>string Type = 3;</code>
+     */
+    protected $Type = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *           空间名 
+     *     @type string $Urls
+     *           刷新Url或目录 
+     *     @type string $Type
+     *           刷新任务类型 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 刷新Url或目录 
+     *
+     * Generated from protobuf field <code>string Urls = 2;</code>
+     * @return string
+     */
+    public function getUrls()
+    {
+        return $this->Urls;
+    }
+
+    /**
+     * 刷新Url或目录 
+     *
+     * Generated from protobuf field <code>string Urls = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setUrls($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Urls = $var;
+
+        return $this;
+    }
+
+    /**
+     * 刷新任务类型 
+     *
+     * Generated from protobuf field <code>string Type = 3;</code>
+     * @return string
+     */
+    public function getType()
+    {
+        return $this->Type;
+    }
+
+    /**
+     * 刷新任务类型 
+     *
+     * Generated from protobuf field <code>string Type = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Type = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodCreateSpaceRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodCreateSpaceRequest.php
new file mode 100755
index 0000000..6d8d79b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodCreateSpaceRequest.php
@@ -0,0 +1,205 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * **********************************************************************
+ * 空间管理
+ * **********************************************************************
+ *
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodCreateSpaceRequest</code>
+ */
+class VodCreateSpaceRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     * 项目名 
+     *
+     * Generated from protobuf field <code>string ProjectName = 2;</code>
+     */
+    protected $ProjectName = '';
+    /**
+     * 描述 
+     *
+     * Generated from protobuf field <code>string Description = 3;</code>
+     */
+    protected $Description = '';
+    /**
+     * 空间区域 
+     *
+     * Generated from protobuf field <code>string Region = 4;</code>
+     */
+    protected $Region = '';
+    /**
+     * 创建用户 
+     *
+     * Generated from protobuf field <code>string UserName = 5;</code>
+     */
+    protected $UserName = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *           空间名 
+     *     @type string $ProjectName
+     *           项目名 
+     *     @type string $Description
+     *           描述 
+     *     @type string $Region
+     *           空间区域 
+     *     @type string $UserName
+     *           创建用户 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 项目名 
+     *
+     * Generated from protobuf field <code>string ProjectName = 2;</code>
+     * @return string
+     */
+    public function getProjectName()
+    {
+        return $this->ProjectName;
+    }
+
+    /**
+     * 项目名 
+     *
+     * Generated from protobuf field <code>string ProjectName = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setProjectName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->ProjectName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 描述 
+     *
+     * Generated from protobuf field <code>string Description = 3;</code>
+     * @return string
+     */
+    public function getDescription()
+    {
+        return $this->Description;
+    }
+
+    /**
+     * 描述 
+     *
+     * Generated from protobuf field <code>string Description = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDescription($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Description = $var;
+
+        return $this;
+    }
+
+    /**
+     * 空间区域 
+     *
+     * Generated from protobuf field <code>string Region = 4;</code>
+     * @return string
+     */
+    public function getRegion()
+    {
+        return $this->Region;
+    }
+
+    /**
+     * 空间区域 
+     *
+     * Generated from protobuf field <code>string Region = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setRegion($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Region = $var;
+
+        return $this;
+    }
+
+    /**
+     * 创建用户 
+     *
+     * Generated from protobuf field <code>string UserName = 5;</code>
+     * @return string
+     */
+    public function getUserName()
+    {
+        return $this->UserName;
+    }
+
+    /**
+     * 创建用户 
+     *
+     * Generated from protobuf field <code>string UserName = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setUserName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->UserName = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodCreateVideoClassificationRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodCreateVideoClassificationRequest.php
new file mode 100755
index 0000000..8969216
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodCreateVideoClassificationRequest.php
@@ -0,0 +1,139 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodCreateVideoClassificationRequest</code>
+ */
+class VodCreateVideoClassificationRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     * Generated from protobuf field <code>int32 Level = 2;</code>
+     */
+    protected $Level = 0;
+    /**
+     * Generated from protobuf field <code>int64 ParentId = 3;</code>
+     */
+    protected $ParentId = 0;
+    /**
+     * Generated from protobuf field <code>string Classification = 4;</code>
+     */
+    protected $Classification = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *     @type int $Level
+     *     @type int|string $ParentId
+     *     @type string $Classification
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 Level = 2;</code>
+     * @return int
+     */
+    public function getLevel()
+    {
+        return $this->Level;
+    }
+
+    /**
+     * Generated from protobuf field <code>int32 Level = 2;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setLevel($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Level = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>int64 ParentId = 3;</code>
+     * @return int|string
+     */
+    public function getParentId()
+    {
+        return $this->ParentId;
+    }
+
+    /**
+     * Generated from protobuf field <code>int64 ParentId = 3;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setParentId($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->ParentId = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string Classification = 4;</code>
+     * @return string
+     */
+    public function getClassification()
+    {
+        return $this->Classification;
+    }
+
+    /**
+     * Generated from protobuf field <code>string Classification = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setClassification($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Classification = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDeleteMediaRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDeleteMediaRequest.php
new file mode 100755
index 0000000..2400b97
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDeleteMediaRequest.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodDeleteMediaRequest</code>
+ */
+class VodDeleteMediaRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *视频ID列表
+     *
+     * Generated from protobuf field <code>string Vids = 1;</code>
+     */
+    protected $Vids = '';
+    /**
+     *回调回传参数
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 2;</code>
+     */
+    protected $CallbackArgs = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vids
+     *          视频ID列表
+     *     @type string $CallbackArgs
+     *          回调回传参数
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *视频ID列表
+     *
+     * Generated from protobuf field <code>string Vids = 1;</code>
+     * @return string
+     */
+    public function getVids()
+    {
+        return $this->Vids;
+    }
+
+    /**
+     *视频ID列表
+     *
+     * Generated from protobuf field <code>string Vids = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVids($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vids = $var;
+
+        return $this;
+    }
+
+    /**
+     *回调回传参数
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 2;</code>
+     * @return string
+     */
+    public function getCallbackArgs()
+    {
+        return $this->CallbackArgs;
+    }
+
+    /**
+     *回调回传参数
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCallbackArgs($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CallbackArgs = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDeleteTranscodesRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDeleteTranscodesRequest.php
new file mode 100755
index 0000000..fa5fcd9
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDeleteTranscodesRequest.php
@@ -0,0 +1,133 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodDeleteTranscodesRequest</code>
+ */
+class VodDeleteTranscodesRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     */
+    protected $Vid = '';
+    /**
+     *转码视频ID列表
+     *
+     * Generated from protobuf field <code>string FileIds = 2;</code>
+     */
+    protected $FileIds = '';
+    /**
+     *回调回传参数
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 3;</code>
+     */
+    protected $CallbackArgs = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vid
+     *          视频ID
+     *     @type string $FileIds
+     *          转码视频ID列表
+     *     @type string $CallbackArgs
+     *          回调回传参数
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     *转码视频ID列表
+     *
+     * Generated from protobuf field <code>string FileIds = 2;</code>
+     * @return string
+     */
+    public function getFileIds()
+    {
+        return $this->FileIds;
+    }
+
+    /**
+     *转码视频ID列表
+     *
+     * Generated from protobuf field <code>string FileIds = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileIds($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileIds = $var;
+
+        return $this;
+    }
+
+    /**
+     *回调回传参数
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 3;</code>
+     * @return string
+     */
+    public function getCallbackArgs()
+    {
+        return $this->CallbackArgs;
+    }
+
+    /**
+     *回调回传参数
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCallbackArgs($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CallbackArgs = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDeleteVideoClassificationRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDeleteVideoClassificationRequest.php
new file mode 100755
index 0000000..bf30e2b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDeleteVideoClassificationRequest.php
@@ -0,0 +1,85 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodDeleteVideoClassificationRequest</code>
+ */
+class VodDeleteVideoClassificationRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     * Generated from protobuf field <code>int64 ClassificationId = 2;</code>
+     */
+    protected $ClassificationId = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *     @type int|string $ClassificationId
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>int64 ClassificationId = 2;</code>
+     * @return int|string
+     */
+    public function getClassificationId()
+    {
+        return $this->ClassificationId;
+    }
+
+    /**
+     * Generated from protobuf field <code>int64 ClassificationId = 2;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setClassificationId($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->ClassificationId = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDescribeIPInfoRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDescribeIPInfoRequest.php
new file mode 100755
index 0000000..d88ee67
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDescribeIPInfoRequest.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodDescribeIPInfoRequest</code>
+ */
+class VodDescribeIPInfoRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * ip,多个用逗号隔开
+     *
+     * Generated from protobuf field <code>string Ips = 1;</code>
+     */
+    protected $Ips = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Ips
+     *           ip,多个用逗号隔开
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * ip,多个用逗号隔开
+     *
+     * Generated from protobuf field <code>string Ips = 1;</code>
+     * @return string
+     */
+    public function getIps()
+    {
+        return $this->Ips;
+    }
+
+    /**
+     * ip,多个用逗号隔开
+     *
+     * Generated from protobuf field <code>string Ips = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setIps($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Ips = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDescribeVodDomainBandwidthDataRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDescribeVodDomainBandwidthDataRequest.php
new file mode 100755
index 0000000..f7df684
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDescribeVodDomainBandwidthDataRequest.php
@@ -0,0 +1,235 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodDescribeVodDomainBandwidthDataRequest</code>
+ */
+class VodDescribeVodDomainBandwidthDataRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 域名列表,多个域名用逗号分割 
+     *
+     * Generated from protobuf field <code>string DomainList = 1;</code>
+     */
+    protected $DomainList = '';
+    /**
+     * 查询的起始时间, rfc3339 
+     *
+     * Generated from protobuf field <code>string StartTime = 2;</code>
+     */
+    protected $StartTime = '';
+    /**
+     * 查询的结束时间, rfc3339;查询时间跨度不超过93天 
+     *
+     * Generated from protobuf field <code>string EndTime = 3;</code>
+     */
+    protected $EndTime = '';
+    /**
+     * 聚合的时间粒度,单位秒,默认300,支持300,1800,3600和86400 
+     *
+     * Generated from protobuf field <code>int32 Aggregation = 4;</code>
+     */
+    protected $Aggregation = 0;
+    /**
+     * 默认是CDN,支持CDN、PCDN、ALL 
+     *
+     * Generated from protobuf field <code>string BandwidthType = 5;</code>
+     */
+    protected $BandwidthType = '';
+    /**
+     * 加速区域 
+     *
+     * Generated from protobuf field <code>string Area = 6;</code>
+     */
+    protected $Area = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $DomainList
+     *           域名列表,多个域名用逗号分割 
+     *     @type string $StartTime
+     *           查询的起始时间, rfc3339 
+     *     @type string $EndTime
+     *           查询的结束时间, rfc3339;查询时间跨度不超过93天 
+     *     @type int $Aggregation
+     *           聚合的时间粒度,单位秒,默认300,支持300,1800,3600和86400 
+     *     @type string $BandwidthType
+     *           默认是CDN,支持CDN、PCDN、ALL 
+     *     @type string $Area
+     *           加速区域 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 域名列表,多个域名用逗号分割 
+     *
+     * Generated from protobuf field <code>string DomainList = 1;</code>
+     * @return string
+     */
+    public function getDomainList()
+    {
+        return $this->DomainList;
+    }
+
+    /**
+     * 域名列表,多个域名用逗号分割 
+     *
+     * Generated from protobuf field <code>string DomainList = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomainList($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->DomainList = $var;
+
+        return $this;
+    }
+
+    /**
+     * 查询的起始时间, rfc3339 
+     *
+     * Generated from protobuf field <code>string StartTime = 2;</code>
+     * @return string
+     */
+    public function getStartTime()
+    {
+        return $this->StartTime;
+    }
+
+    /**
+     * 查询的起始时间, rfc3339 
+     *
+     * Generated from protobuf field <code>string StartTime = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStartTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StartTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * 查询的结束时间, rfc3339;查询时间跨度不超过93天 
+     *
+     * Generated from protobuf field <code>string EndTime = 3;</code>
+     * @return string
+     */
+    public function getEndTime()
+    {
+        return $this->EndTime;
+    }
+
+    /**
+     * 查询的结束时间, rfc3339;查询时间跨度不超过93天 
+     *
+     * Generated from protobuf field <code>string EndTime = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setEndTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->EndTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * 聚合的时间粒度,单位秒,默认300,支持300,1800,3600和86400 
+     *
+     * Generated from protobuf field <code>int32 Aggregation = 4;</code>
+     * @return int
+     */
+    public function getAggregation()
+    {
+        return $this->Aggregation;
+    }
+
+    /**
+     * 聚合的时间粒度,单位秒,默认300,支持300,1800,3600和86400 
+     *
+     * Generated from protobuf field <code>int32 Aggregation = 4;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setAggregation($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Aggregation = $var;
+
+        return $this;
+    }
+
+    /**
+     * 默认是CDN,支持CDN、PCDN、ALL 
+     *
+     * Generated from protobuf field <code>string BandwidthType = 5;</code>
+     * @return string
+     */
+    public function getBandwidthType()
+    {
+        return $this->BandwidthType;
+    }
+
+    /**
+     * 默认是CDN,支持CDN、PCDN、ALL 
+     *
+     * Generated from protobuf field <code>string BandwidthType = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setBandwidthType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->BandwidthType = $var;
+
+        return $this;
+    }
+
+    /**
+     * 加速区域 
+     *
+     * Generated from protobuf field <code>string Area = 6;</code>
+     * @return string
+     */
+    public function getArea()
+    {
+        return $this->Area;
+    }
+
+    /**
+     * 加速区域 
+     *
+     * Generated from protobuf field <code>string Area = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setArea($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Area = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDescribeVodDomainTrafficDataRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDescribeVodDomainTrafficDataRequest.php
new file mode 100755
index 0000000..17c5c29
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDescribeVodDomainTrafficDataRequest.php
@@ -0,0 +1,201 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodDescribeVodDomainTrafficDataRequest</code>
+ */
+class VodDescribeVodDomainTrafficDataRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * list of domains to query
+     *
+     * Generated from protobuf field <code>string DomainList = 1;</code>
+     */
+    protected $DomainList = '';
+    /**
+     * rfc339
+     *
+     * Generated from protobuf field <code>string StartTime = 2;</code>
+     */
+    protected $StartTime = '';
+    /**
+     * rfc339
+     *
+     * Generated from protobuf field <code>string EndTime = 3;</code>
+     */
+    protected $EndTime = '';
+    /**
+     * default 300, supports 1800, 3600 and 86400
+     *
+     * Generated from protobuf field <code>int32 Aggregation = 4;</code>
+     */
+    protected $Aggregation = 0;
+    /**
+     * PCDN, CDN, or ALL
+     *
+     * Generated from protobuf field <code>string TrafficType = 5;</code>
+     */
+    protected $TrafficType = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $DomainList
+     *           list of domains to query
+     *     @type string $StartTime
+     *           rfc339
+     *     @type string $EndTime
+     *           rfc339
+     *     @type int $Aggregation
+     *           default 300, supports 1800, 3600 and 86400
+     *     @type string $TrafficType
+     *           PCDN, CDN, or ALL
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * list of domains to query
+     *
+     * Generated from protobuf field <code>string DomainList = 1;</code>
+     * @return string
+     */
+    public function getDomainList()
+    {
+        return $this->DomainList;
+    }
+
+    /**
+     * list of domains to query
+     *
+     * Generated from protobuf field <code>string DomainList = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomainList($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->DomainList = $var;
+
+        return $this;
+    }
+
+    /**
+     * rfc339
+     *
+     * Generated from protobuf field <code>string StartTime = 2;</code>
+     * @return string
+     */
+    public function getStartTime()
+    {
+        return $this->StartTime;
+    }
+
+    /**
+     * rfc339
+     *
+     * Generated from protobuf field <code>string StartTime = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStartTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StartTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * rfc339
+     *
+     * Generated from protobuf field <code>string EndTime = 3;</code>
+     * @return string
+     */
+    public function getEndTime()
+    {
+        return $this->EndTime;
+    }
+
+    /**
+     * rfc339
+     *
+     * Generated from protobuf field <code>string EndTime = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setEndTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->EndTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * default 300, supports 1800, 3600 and 86400
+     *
+     * Generated from protobuf field <code>int32 Aggregation = 4;</code>
+     * @return int
+     */
+    public function getAggregation()
+    {
+        return $this->Aggregation;
+    }
+
+    /**
+     * default 300, supports 1800, 3600 and 86400
+     *
+     * Generated from protobuf field <code>int32 Aggregation = 4;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setAggregation($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Aggregation = $var;
+
+        return $this;
+    }
+
+    /**
+     * PCDN, CDN, or ALL
+     *
+     * Generated from protobuf field <code>string TrafficType = 5;</code>
+     * @return string
+     */
+    public function getTrafficType()
+    {
+        return $this->TrafficType;
+    }
+
+    /**
+     * PCDN, CDN, or ALL
+     *
+     * Generated from protobuf field <code>string TrafficType = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTrafficType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TrafficType = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDescribeVodSpaceStorageDataRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDescribeVodSpaceStorageDataRequest.php
new file mode 100755
index 0000000..441b4a3
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodDescribeVodSpaceStorageDataRequest.php
@@ -0,0 +1,201 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodDescribeVodSpaceStorageDataRequest</code>
+ */
+class VodDescribeVodSpaceStorageDataRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * SpaceList, multiple space names are divided by
+     *
+     * Generated from protobuf field <code>string SpaceList = 1;</code>
+     */
+    protected $SpaceList = '';
+    /**
+     * commas
+     *
+     * Generated from protobuf field <code>string StartTime = 2;</code>
+     */
+    protected $StartTime = '';
+    /**
+     * end time of query rfc3339
+     *
+     * Generated from protobuf field <code>string EndTime = 3;</code>
+     */
+    protected $EndTime = '';
+    /**
+     * default and only supports 86400
+     *
+     * Generated from protobuf field <code>int32 Aggregation = 4;</code>
+     */
+    protected $Aggregation = 0;
+    /**
+     * hot default, lowfreq, punish
+     *
+     * Generated from protobuf field <code>string Type = 5;</code>
+     */
+    protected $Type = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceList
+     *           SpaceList, multiple space names are divided by
+     *     @type string $StartTime
+     *           commas
+     *     @type string $EndTime
+     *           end time of query rfc3339
+     *     @type int $Aggregation
+     *           default and only supports 86400
+     *     @type string $Type
+     *           hot default, lowfreq, punish
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * SpaceList, multiple space names are divided by
+     *
+     * Generated from protobuf field <code>string SpaceList = 1;</code>
+     * @return string
+     */
+    public function getSpaceList()
+    {
+        return $this->SpaceList;
+    }
+
+    /**
+     * SpaceList, multiple space names are divided by
+     *
+     * Generated from protobuf field <code>string SpaceList = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceList($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceList = $var;
+
+        return $this;
+    }
+
+    /**
+     * commas
+     *
+     * Generated from protobuf field <code>string StartTime = 2;</code>
+     * @return string
+     */
+    public function getStartTime()
+    {
+        return $this->StartTime;
+    }
+
+    /**
+     * commas
+     *
+     * Generated from protobuf field <code>string StartTime = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStartTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StartTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * end time of query rfc3339
+     *
+     * Generated from protobuf field <code>string EndTime = 3;</code>
+     * @return string
+     */
+    public function getEndTime()
+    {
+        return $this->EndTime;
+    }
+
+    /**
+     * end time of query rfc3339
+     *
+     * Generated from protobuf field <code>string EndTime = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setEndTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->EndTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * default and only supports 86400
+     *
+     * Generated from protobuf field <code>int32 Aggregation = 4;</code>
+     * @return int
+     */
+    public function getAggregation()
+    {
+        return $this->Aggregation;
+    }
+
+    /**
+     * default and only supports 86400
+     *
+     * Generated from protobuf field <code>int32 Aggregation = 4;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setAggregation($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Aggregation = $var;
+
+        return $this;
+    }
+
+    /**
+     * hot default, lowfreq, punish
+     *
+     * Generated from protobuf field <code>string Type = 5;</code>
+     * @return string
+     */
+    public function getType()
+    {
+        return $this->Type;
+    }
+
+    /**
+     * hot default, lowfreq, punish
+     *
+     * Generated from protobuf field <code>string Type = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Type = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetAllPlayInfoRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetAllPlayInfoRequest.php
new file mode 100755
index 0000000..1f467d7
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetAllPlayInfoRequest.php
@@ -0,0 +1,667 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * **********************************************************************
+ * 播放
+ * **********************************************************************
+ *
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodGetAllPlayInfoRequest</code>
+ */
+class VodGetAllPlayInfoRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 视频ID 
+     *
+     * Generated from protobuf field <code>string Vids = 1;</code>
+     */
+    protected $Vids = '';
+    /**
+     * 封装格式,支持 mp4、dash、hls、mp3、m4a、ogg。多个Format
+     * 组成的字符串,中间用","隔开。默认获取全部
+     *
+     * Generated from protobuf field <code>string Formats = 2;</code>
+     */
+    protected $Formats = '';
+    /**
+     * 编码格式,支持 mp3、aac、opus、H264、H265、H266。多个Codec
+     * 组成的字符串,中间用","隔开。默认获取全部
+     *
+     * Generated from protobuf field <code>string Codecs = 3;</code>
+     */
+    protected $Codecs = '';
+    /**
+     * 视频流清晰度,默认返回全部,支持:240p,360p,480p,540p,720p,1080p。多个
+     * Codec 组成的字符串,中间用","隔开。默认获取全部
+     *
+     * Generated from protobuf field <code>string Definitions = 4;</code>
+     */
+    protected $Definitions = '';
+    /**
+     * 流文件类型。支持:video-视频流, audio-音频流。
+     * 多个 FileType 组成的字符串,中间用","隔开。默认获取全部类型
+     *
+     * Generated from protobuf field <code>string FileTypes = 5;</code>
+     */
+    protected $FileTypes = '';
+    /**
+     * 水印贴片标签 
+     *
+     * Generated from protobuf field <code>string LogoTypes = 6;</code>
+     */
+    protected $LogoTypes = '';
+    /**
+     * 是否加密, 支持: "encrypt"-加密流, "normal"-非加密流, 默认返回全部 
+     *
+     * Generated from protobuf field <code>string NeedEncryptStream = 7;</code>
+     */
+    protected $NeedEncryptStream = '';
+    /**
+     * 返回https播放地址,默认否, 1-是;0-否 
+     *
+     * Generated from protobuf field <code>string Ssl = 8;</code>
+     */
+    protected $Ssl = '';
+    /**
+     * 是否需要雪碧图(缩略图),默认否,1-是;0-否 
+     *
+     * Generated from protobuf field <code>string NeedThumbs = 9;</code>
+     */
+    protected $NeedThumbs = '';
+    /**
+     * 是否需要蒙版弹幕,默认否,1-是;0-否 
+     *
+     * Generated from protobuf field <code>string NeedBarrageMask = 10;</code>
+     */
+    protected $NeedBarrageMask = '';
+    /**
+     * 指定CDN类型, 默认不传为普通CDN, 若需使用请联系技术支持 
+     *
+     * Generated from protobuf field <code>string CdnType = 11;</code>
+     */
+    protected $CdnType = '';
+    /**
+     * 唯一性标识信息, 若需使用请联系技术支持 
+     *
+     * Generated from protobuf field <code>string UnionInfo = 12;</code>
+     */
+    protected $UnionInfo = '';
+    /**
+     * 播放场景,指定获取对应场景的音视频流。当前支持:preview-试看 
+     *
+     * Generated from protobuf field <code>string PlayScene = 13;</code>
+     */
+    protected $PlayScene = '';
+    /**
+     * DRM过期时间戳, 若需使用请联系技术支持 
+     *
+     * Generated from protobuf field <code>string DrmExpireTimestamp = 14;</code>
+     */
+    protected $DrmExpireTimestamp = '';
+    /**
+     * 默认获取全部。支持: "SDR", "PQHDR", "SLGHDR", 默认返回全部 
+     *
+     * Generated from protobuf field <code>string HDRType = 15;</code>
+     */
+    protected $HDRType = '';
+    /**
+     * 使用的帧对齐转码版本。支持: "1"-返回版本一, 默认不指定版本,全部返回 
+     *
+     * Generated from protobuf field <code>string KeyFrameAlignmentVersion = 16;</code>
+     */
+    protected $KeyFrameAlignmentVersion = '';
+    /**
+     * 发布状态, 支持: "Available"-发布, "NoAction"-未发布, 默认返回全部 
+     *
+     * Generated from protobuf field <code>string UserAction = 17;</code>
+     */
+    protected $UserAction = '';
+    /**
+     * 音频音质。当 FileType 为
+     * audio时起作用,表示音频音质参数。支持:medium、higher、highest.默认返回所有音频流
+     *
+     * Generated from protobuf field <code>string Quality = 18;</code>
+     */
+    protected $Quality = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vids
+     *           视频ID 
+     *     @type string $Formats
+     *           封装格式,支持 mp4、dash、hls、mp3、m4a、ogg。多个Format
+     *           组成的字符串,中间用","隔开。默认获取全部
+     *     @type string $Codecs
+     *           编码格式,支持 mp3、aac、opus、H264、H265、H266。多个Codec
+     *           组成的字符串,中间用","隔开。默认获取全部
+     *     @type string $Definitions
+     *           视频流清晰度,默认返回全部,支持:240p,360p,480p,540p,720p,1080p。多个
+     *           Codec 组成的字符串,中间用","隔开。默认获取全部
+     *     @type string $FileTypes
+     *           流文件类型。支持:video-视频流, audio-音频流。
+     *           多个 FileType 组成的字符串,中间用","隔开。默认获取全部类型
+     *     @type string $LogoTypes
+     *           水印贴片标签 
+     *     @type string $NeedEncryptStream
+     *           是否加密, 支持: "encrypt"-加密流, "normal"-非加密流, 默认返回全部 
+     *     @type string $Ssl
+     *           返回https播放地址,默认否, 1-是;0-否 
+     *     @type string $NeedThumbs
+     *           是否需要雪碧图(缩略图),默认否,1-是;0-否 
+     *     @type string $NeedBarrageMask
+     *           是否需要蒙版弹幕,默认否,1-是;0-否 
+     *     @type string $CdnType
+     *           指定CDN类型, 默认不传为普通CDN, 若需使用请联系技术支持 
+     *     @type string $UnionInfo
+     *           唯一性标识信息, 若需使用请联系技术支持 
+     *     @type string $PlayScene
+     *           播放场景,指定获取对应场景的音视频流。当前支持:preview-试看 
+     *     @type string $DrmExpireTimestamp
+     *           DRM过期时间戳, 若需使用请联系技术支持 
+     *     @type string $HDRType
+     *           默认获取全部。支持: "SDR", "PQHDR", "SLGHDR", 默认返回全部 
+     *     @type string $KeyFrameAlignmentVersion
+     *           使用的帧对齐转码版本。支持: "1"-返回版本一, 默认不指定版本,全部返回 
+     *     @type string $UserAction
+     *           发布状态, 支持: "Available"-发布, "NoAction"-未发布, 默认返回全部 
+     *     @type string $Quality
+     *           音频音质。当 FileType 为
+     *           audio时起作用,表示音频音质参数。支持:medium、higher、highest.默认返回所有音频流
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 视频ID 
+     *
+     * Generated from protobuf field <code>string Vids = 1;</code>
+     * @return string
+     */
+    public function getVids()
+    {
+        return $this->Vids;
+    }
+
+    /**
+     * 视频ID 
+     *
+     * Generated from protobuf field <code>string Vids = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVids($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vids = $var;
+
+        return $this;
+    }
+
+    /**
+     * 封装格式,支持 mp4、dash、hls、mp3、m4a、ogg。多个Format
+     * 组成的字符串,中间用","隔开。默认获取全部
+     *
+     * Generated from protobuf field <code>string Formats = 2;</code>
+     * @return string
+     */
+    public function getFormats()
+    {
+        return $this->Formats;
+    }
+
+    /**
+     * 封装格式,支持 mp4、dash、hls、mp3、m4a、ogg。多个Format
+     * 组成的字符串,中间用","隔开。默认获取全部
+     *
+     * Generated from protobuf field <code>string Formats = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFormats($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Formats = $var;
+
+        return $this;
+    }
+
+    /**
+     * 编码格式,支持 mp3、aac、opus、H264、H265、H266。多个Codec
+     * 组成的字符串,中间用","隔开。默认获取全部
+     *
+     * Generated from protobuf field <code>string Codecs = 3;</code>
+     * @return string
+     */
+    public function getCodecs()
+    {
+        return $this->Codecs;
+    }
+
+    /**
+     * 编码格式,支持 mp3、aac、opus、H264、H265、H266。多个Codec
+     * 组成的字符串,中间用","隔开。默认获取全部
+     *
+     * Generated from protobuf field <code>string Codecs = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCodecs($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Codecs = $var;
+
+        return $this;
+    }
+
+    /**
+     * 视频流清晰度,默认返回全部,支持:240p,360p,480p,540p,720p,1080p。多个
+     * Codec 组成的字符串,中间用","隔开。默认获取全部
+     *
+     * Generated from protobuf field <code>string Definitions = 4;</code>
+     * @return string
+     */
+    public function getDefinitions()
+    {
+        return $this->Definitions;
+    }
+
+    /**
+     * 视频流清晰度,默认返回全部,支持:240p,360p,480p,540p,720p,1080p。多个
+     * Codec 组成的字符串,中间用","隔开。默认获取全部
+     *
+     * Generated from protobuf field <code>string Definitions = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDefinitions($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Definitions = $var;
+
+        return $this;
+    }
+
+    /**
+     * 流文件类型。支持:video-视频流, audio-音频流。
+     * 多个 FileType 组成的字符串,中间用","隔开。默认获取全部类型
+     *
+     * Generated from protobuf field <code>string FileTypes = 5;</code>
+     * @return string
+     */
+    public function getFileTypes()
+    {
+        return $this->FileTypes;
+    }
+
+    /**
+     * 流文件类型。支持:video-视频流, audio-音频流。
+     * 多个 FileType 组成的字符串,中间用","隔开。默认获取全部类型
+     *
+     * Generated from protobuf field <code>string FileTypes = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileTypes($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileTypes = $var;
+
+        return $this;
+    }
+
+    /**
+     * 水印贴片标签 
+     *
+     * Generated from protobuf field <code>string LogoTypes = 6;</code>
+     * @return string
+     */
+    public function getLogoTypes()
+    {
+        return $this->LogoTypes;
+    }
+
+    /**
+     * 水印贴片标签 
+     *
+     * Generated from protobuf field <code>string LogoTypes = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setLogoTypes($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->LogoTypes = $var;
+
+        return $this;
+    }
+
+    /**
+     * 是否加密, 支持: "encrypt"-加密流, "normal"-非加密流, 默认返回全部 
+     *
+     * Generated from protobuf field <code>string NeedEncryptStream = 7;</code>
+     * @return string
+     */
+    public function getNeedEncryptStream()
+    {
+        return $this->NeedEncryptStream;
+    }
+
+    /**
+     * 是否加密, 支持: "encrypt"-加密流, "normal"-非加密流, 默认返回全部 
+     *
+     * Generated from protobuf field <code>string NeedEncryptStream = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setNeedEncryptStream($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->NeedEncryptStream = $var;
+
+        return $this;
+    }
+
+    /**
+     * 返回https播放地址,默认否, 1-是;0-否 
+     *
+     * Generated from protobuf field <code>string Ssl = 8;</code>
+     * @return string
+     */
+    public function getSsl()
+    {
+        return $this->Ssl;
+    }
+
+    /**
+     * 返回https播放地址,默认否, 1-是;0-否 
+     *
+     * Generated from protobuf field <code>string Ssl = 8;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSsl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Ssl = $var;
+
+        return $this;
+    }
+
+    /**
+     * 是否需要雪碧图(缩略图),默认否,1-是;0-否 
+     *
+     * Generated from protobuf field <code>string NeedThumbs = 9;</code>
+     * @return string
+     */
+    public function getNeedThumbs()
+    {
+        return $this->NeedThumbs;
+    }
+
+    /**
+     * 是否需要雪碧图(缩略图),默认否,1-是;0-否 
+     *
+     * Generated from protobuf field <code>string NeedThumbs = 9;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setNeedThumbs($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->NeedThumbs = $var;
+
+        return $this;
+    }
+
+    /**
+     * 是否需要蒙版弹幕,默认否,1-是;0-否 
+     *
+     * Generated from protobuf field <code>string NeedBarrageMask = 10;</code>
+     * @return string
+     */
+    public function getNeedBarrageMask()
+    {
+        return $this->NeedBarrageMask;
+    }
+
+    /**
+     * 是否需要蒙版弹幕,默认否,1-是;0-否 
+     *
+     * Generated from protobuf field <code>string NeedBarrageMask = 10;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setNeedBarrageMask($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->NeedBarrageMask = $var;
+
+        return $this;
+    }
+
+    /**
+     * 指定CDN类型, 默认不传为普通CDN, 若需使用请联系技术支持 
+     *
+     * Generated from protobuf field <code>string CdnType = 11;</code>
+     * @return string
+     */
+    public function getCdnType()
+    {
+        return $this->CdnType;
+    }
+
+    /**
+     * 指定CDN类型, 默认不传为普通CDN, 若需使用请联系技术支持 
+     *
+     * Generated from protobuf field <code>string CdnType = 11;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCdnType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CdnType = $var;
+
+        return $this;
+    }
+
+    /**
+     * 唯一性标识信息, 若需使用请联系技术支持 
+     *
+     * Generated from protobuf field <code>string UnionInfo = 12;</code>
+     * @return string
+     */
+    public function getUnionInfo()
+    {
+        return $this->UnionInfo;
+    }
+
+    /**
+     * 唯一性标识信息, 若需使用请联系技术支持 
+     *
+     * Generated from protobuf field <code>string UnionInfo = 12;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setUnionInfo($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->UnionInfo = $var;
+
+        return $this;
+    }
+
+    /**
+     * 播放场景,指定获取对应场景的音视频流。当前支持:preview-试看 
+     *
+     * Generated from protobuf field <code>string PlayScene = 13;</code>
+     * @return string
+     */
+    public function getPlayScene()
+    {
+        return $this->PlayScene;
+    }
+
+    /**
+     * 播放场景,指定获取对应场景的音视频流。当前支持:preview-试看 
+     *
+     * Generated from protobuf field <code>string PlayScene = 13;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setPlayScene($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->PlayScene = $var;
+
+        return $this;
+    }
+
+    /**
+     * DRM过期时间戳, 若需使用请联系技术支持 
+     *
+     * Generated from protobuf field <code>string DrmExpireTimestamp = 14;</code>
+     * @return string
+     */
+    public function getDrmExpireTimestamp()
+    {
+        return $this->DrmExpireTimestamp;
+    }
+
+    /**
+     * DRM过期时间戳, 若需使用请联系技术支持 
+     *
+     * Generated from protobuf field <code>string DrmExpireTimestamp = 14;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDrmExpireTimestamp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->DrmExpireTimestamp = $var;
+
+        return $this;
+    }
+
+    /**
+     * 默认获取全部。支持: "SDR", "PQHDR", "SLGHDR", 默认返回全部 
+     *
+     * Generated from protobuf field <code>string HDRType = 15;</code>
+     * @return string
+     */
+    public function getHDRType()
+    {
+        return $this->HDRType;
+    }
+
+    /**
+     * 默认获取全部。支持: "SDR", "PQHDR", "SLGHDR", 默认返回全部 
+     *
+     * Generated from protobuf field <code>string HDRType = 15;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setHDRType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->HDRType = $var;
+
+        return $this;
+    }
+
+    /**
+     * 使用的帧对齐转码版本。支持: "1"-返回版本一, 默认不指定版本,全部返回 
+     *
+     * Generated from protobuf field <code>string KeyFrameAlignmentVersion = 16;</code>
+     * @return string
+     */
+    public function getKeyFrameAlignmentVersion()
+    {
+        return $this->KeyFrameAlignmentVersion;
+    }
+
+    /**
+     * 使用的帧对齐转码版本。支持: "1"-返回版本一, 默认不指定版本,全部返回 
+     *
+     * Generated from protobuf field <code>string KeyFrameAlignmentVersion = 16;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setKeyFrameAlignmentVersion($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->KeyFrameAlignmentVersion = $var;
+
+        return $this;
+    }
+
+    /**
+     * 发布状态, 支持: "Available"-发布, "NoAction"-未发布, 默认返回全部 
+     *
+     * Generated from protobuf field <code>string UserAction = 17;</code>
+     * @return string
+     */
+    public function getUserAction()
+    {
+        return $this->UserAction;
+    }
+
+    /**
+     * 发布状态, 支持: "Available"-发布, "NoAction"-未发布, 默认返回全部 
+     *
+     * Generated from protobuf field <code>string UserAction = 17;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setUserAction($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->UserAction = $var;
+
+        return $this;
+    }
+
+    /**
+     * 音频音质。当 FileType 为
+     * audio时起作用,表示音频音质参数。支持:medium、higher、highest.默认返回所有音频流
+     *
+     * Generated from protobuf field <code>string Quality = 18;</code>
+     * @return string
+     */
+    public function getQuality()
+    {
+        return $this->Quality;
+    }
+
+    /**
+     * 音频音质。当 FileType 为
+     * audio时起作用,表示音频音质参数。支持:medium、higher、highest.默认返回所有音频流
+     *
+     * Generated from protobuf field <code>string Quality = 18;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setQuality($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Quality = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetAppInfoRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetAppInfoRequest.php
new file mode 100755
index 0000000..832a930
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetAppInfoRequest.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodGetAppInfoRequest</code>
+ */
+class VodGetAppInfoRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * appCloud应用ID 
+     *
+     * Generated from protobuf field <code>uint64 AppId = 1;</code>
+     */
+    protected $AppId = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type int|string $AppId
+     *           appCloud应用ID 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * appCloud应用ID 
+     *
+     * Generated from protobuf field <code>uint64 AppId = 1;</code>
+     * @return int|string
+     */
+    public function getAppId()
+    {
+        return $this->AppId;
+    }
+
+    /**
+     * appCloud应用ID 
+     *
+     * Generated from protobuf field <code>uint64 AppId = 1;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setAppId($var)
+    {
+        GPBUtil::checkUint64($var);
+        $this->AppId = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetAudioEventDetectionForAuditRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetAudioEventDetectionForAuditRequest.php
new file mode 100755
index 0000000..14b9763
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetAudioEventDetectionForAuditRequest.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodGetAudioEventDetectionForAuditRequest</code>
+ */
+class VodGetAudioEventDetectionForAuditRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Vid
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     */
+    protected $Vid = '';
+    /**
+     * 语音转文字模板中的Strategy
+     *
+     * Generated from protobuf field <code>string Strategy = 2;</code>
+     */
+    protected $Strategy = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vid
+     *           Vid
+     *     @type string $Strategy
+     *           语音转文字模板中的Strategy
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Vid
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     * Vid
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     * 语音转文字模板中的Strategy
+     *
+     * Generated from protobuf field <code>string Strategy = 2;</code>
+     * @return string
+     */
+    public function getStrategy()
+    {
+        return $this->Strategy;
+    }
+
+    /**
+     * 语音转文字模板中的Strategy
+     *
+     * Generated from protobuf field <code>string Strategy = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStrategy($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Strategy = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetAudioInfoForAuditRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetAudioInfoForAuditRequest.php
new file mode 100755
index 0000000..632a988
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetAudioInfoForAuditRequest.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodGetAudioInfoForAuditRequest</code>
+ */
+class VodGetAudioInfoForAuditRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * vid
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     */
+    protected $Vid = '';
+    /**
+     *音频审核帧模板中的Strategy,抽帧策略
+     *
+     * Generated from protobuf field <code>string Strategy = 2;</code>
+     */
+    protected $Strategy = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vid
+     *           vid
+     *     @type string $Strategy
+     *          音频审核帧模板中的Strategy,抽帧策略
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * vid
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     * vid
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     *音频审核帧模板中的Strategy,抽帧策略
+     *
+     * Generated from protobuf field <code>string Strategy = 2;</code>
+     * @return string
+     */
+    public function getStrategy()
+    {
+        return $this->Strategy;
+    }
+
+    /**
+     *音频审核帧模板中的Strategy,抽帧策略
+     *
+     * Generated from protobuf field <code>string Strategy = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStrategy($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Strategy = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetAuditFramesForAuditRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetAuditFramesForAuditRequest.php
new file mode 100755
index 0000000..558455a
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetAuditFramesForAuditRequest.php
@@ -0,0 +1,167 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodGetAuditFramesForAuditRequest</code>
+ */
+class VodGetAuditFramesForAuditRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     */
+    protected $Vid = '';
+    /**
+     *审核帧模板信息中的Strategy,抽帧策略
+     *
+     * Generated from protobuf field <code>string Strategy = 2;</code>
+     */
+    protected $Strategy = '';
+    /**
+     *最小帧数,未设置时以服务配置默认值为准
+     *
+     * Generated from protobuf field <code>string MinNumberOfFrames = 3;</code>
+     */
+    protected $MinNumberOfFrames = '';
+    /**
+     *最大帧数,未设置时以服务配置默认值为准
+     *
+     * Generated from protobuf field <code>string MaxNumberOfFrames = 4;</code>
+     */
+    protected $MaxNumberOfFrames = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vid
+     *          视频ID
+     *     @type string $Strategy
+     *          审核帧模板信息中的Strategy,抽帧策略
+     *     @type string $MinNumberOfFrames
+     *          最小帧数,未设置时以服务配置默认值为准
+     *     @type string $MaxNumberOfFrames
+     *          最大帧数,未设置时以服务配置默认值为准
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     *审核帧模板信息中的Strategy,抽帧策略
+     *
+     * Generated from protobuf field <code>string Strategy = 2;</code>
+     * @return string
+     */
+    public function getStrategy()
+    {
+        return $this->Strategy;
+    }
+
+    /**
+     *审核帧模板信息中的Strategy,抽帧策略
+     *
+     * Generated from protobuf field <code>string Strategy = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStrategy($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Strategy = $var;
+
+        return $this;
+    }
+
+    /**
+     *最小帧数,未设置时以服务配置默认值为准
+     *
+     * Generated from protobuf field <code>string MinNumberOfFrames = 3;</code>
+     * @return string
+     */
+    public function getMinNumberOfFrames()
+    {
+        return $this->MinNumberOfFrames;
+    }
+
+    /**
+     *最小帧数,未设置时以服务配置默认值为准
+     *
+     * Generated from protobuf field <code>string MinNumberOfFrames = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setMinNumberOfFrames($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->MinNumberOfFrames = $var;
+
+        return $this;
+    }
+
+    /**
+     *最大帧数,未设置时以服务配置默认值为准
+     *
+     * Generated from protobuf field <code>string MaxNumberOfFrames = 4;</code>
+     * @return string
+     */
+    public function getMaxNumberOfFrames()
+    {
+        return $this->MaxNumberOfFrames;
+    }
+
+    /**
+     *最大帧数,未设置时以服务配置默认值为准
+     *
+     * Generated from protobuf field <code>string MaxNumberOfFrames = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setMaxNumberOfFrames($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->MaxNumberOfFrames = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetAutomaticSpeechRecognitionForAuditRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetAutomaticSpeechRecognitionForAuditRequest.php
new file mode 100755
index 0000000..1861284
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetAutomaticSpeechRecognitionForAuditRequest.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodGetAutomaticSpeechRecognitionForAuditRequest</code>
+ */
+class VodGetAutomaticSpeechRecognitionForAuditRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Vid
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     */
+    protected $Vid = '';
+    /**
+     * 语音转文字模板中的Strategy
+     *
+     * Generated from protobuf field <code>string Strategy = 2;</code>
+     */
+    protected $Strategy = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vid
+     *           Vid
+     *     @type string $Strategy
+     *           语音转文字模板中的Strategy
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Vid
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     * Vid
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     * 语音转文字模板中的Strategy
+     *
+     * Generated from protobuf field <code>string Strategy = 2;</code>
+     * @return string
+     */
+    public function getStrategy()
+    {
+        return $this->Strategy;
+    }
+
+    /**
+     * 语音转文字模板中的Strategy
+     *
+     * Generated from protobuf field <code>string Strategy = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStrategy($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Strategy = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetBetterFramesForAuditRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetBetterFramesForAuditRequest.php
new file mode 100755
index 0000000..4986814
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetBetterFramesForAuditRequest.php
@@ -0,0 +1,133 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodGetBetterFramesForAuditRequest</code>
+ */
+class VodGetBetterFramesForAuditRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Vid
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     */
+    protected $Vid = '';
+    /**
+     *视频审核帧模板中的Strategy,抽帧策略,默认为default
+     *
+     * Generated from protobuf field <code>string Strategy = 2;</code>
+     */
+    protected $Strategy = '';
+    /**
+     * 优选帧的覆盖率得分,取值为从0至1的小数,0代表取全部优选帧
+     *
+     * Generated from protobuf field <code>string CoverRate = 3;</code>
+     */
+    protected $CoverRate = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vid
+     *           Vid
+     *     @type string $Strategy
+     *          视频审核帧模板中的Strategy,抽帧策略,默认为default
+     *     @type string $CoverRate
+     *           优选帧的覆盖率得分,取值为从0至1的小数,0代表取全部优选帧
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Vid
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     * Vid
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频审核帧模板中的Strategy,抽帧策略,默认为default
+     *
+     * Generated from protobuf field <code>string Strategy = 2;</code>
+     * @return string
+     */
+    public function getStrategy()
+    {
+        return $this->Strategy;
+    }
+
+    /**
+     *视频审核帧模板中的Strategy,抽帧策略,默认为default
+     *
+     * Generated from protobuf field <code>string Strategy = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStrategy($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Strategy = $var;
+
+        return $this;
+    }
+
+    /**
+     * 优选帧的覆盖率得分,取值为从0至1的小数,0代表取全部优选帧
+     *
+     * Generated from protobuf field <code>string CoverRate = 3;</code>
+     * @return string
+     */
+    public function getCoverRate()
+    {
+        return $this->CoverRate;
+    }
+
+    /**
+     * 优选帧的覆盖率得分,取值为从0至1的小数,0代表取全部优选帧
+     *
+     * Generated from protobuf field <code>string CoverRate = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCoverRate($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CoverRate = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetDirectEditProgressRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetDirectEditProgressRequest.php
new file mode 100755
index 0000000..6706194
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetDirectEditProgressRequest.php
@@ -0,0 +1,58 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodGetDirectEditProgressRequest</code>
+ */
+class VodGetDirectEditProgressRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>string ReqId = 1;</code>
+     */
+    protected $ReqId = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $ReqId
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>string ReqId = 1;</code>
+     * @return string
+     */
+    public function getReqId()
+    {
+        return $this->ReqId;
+    }
+
+    /**
+     * Generated from protobuf field <code>string ReqId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setReqId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->ReqId = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetDirectEditResultRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetDirectEditResultRequest.php
new file mode 100755
index 0000000..2453623
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetDirectEditResultRequest.php
@@ -0,0 +1,58 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodGetDirectEditResultRequest</code>
+ */
+class VodGetDirectEditResultRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>repeated string ReqIds = 1;</code>
+     */
+    private $ReqIds;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string[]|\Google\Protobuf\Internal\RepeatedField $ReqIds
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated string ReqIds = 1;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getReqIds()
+    {
+        return $this->ReqIds;
+    }
+
+    /**
+     * Generated from protobuf field <code>repeated string ReqIds = 1;</code>
+     * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setReqIds($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+        $this->ReqIds = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetHlsDecryptionKeyRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetHlsDecryptionKeyRequest.php
new file mode 100755
index 0000000..ccefbe3
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetHlsDecryptionKeyRequest.php
@@ -0,0 +1,133 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodGetHlsDecryptionKeyRequest</code>
+ */
+class VodGetHlsDecryptionKeyRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Drm鉴权Token 
+     *
+     * Generated from protobuf field <code>string DrmAuthToken = 1;</code>
+     */
+    protected $DrmAuthToken = '';
+    /**
+     * 加密hls的访问Key,从m3u8文件中取到 
+     *
+     * Generated from protobuf field <code>string Ak = 2;</code>
+     */
+    protected $Ak = '';
+    /**
+     * Drm鉴权数据源,从m3u8文件中取到 
+     *
+     * Generated from protobuf field <code>string Source = 3;</code>
+     */
+    protected $Source = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $DrmAuthToken
+     *           Drm鉴权Token 
+     *     @type string $Ak
+     *           加密hls的访问Key,从m3u8文件中取到 
+     *     @type string $Source
+     *           Drm鉴权数据源,从m3u8文件中取到 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Drm鉴权Token 
+     *
+     * Generated from protobuf field <code>string DrmAuthToken = 1;</code>
+     * @return string
+     */
+    public function getDrmAuthToken()
+    {
+        return $this->DrmAuthToken;
+    }
+
+    /**
+     * Drm鉴权Token 
+     *
+     * Generated from protobuf field <code>string DrmAuthToken = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDrmAuthToken($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->DrmAuthToken = $var;
+
+        return $this;
+    }
+
+    /**
+     * 加密hls的访问Key,从m3u8文件中取到 
+     *
+     * Generated from protobuf field <code>string Ak = 2;</code>
+     * @return string
+     */
+    public function getAk()
+    {
+        return $this->Ak;
+    }
+
+    /**
+     * 加密hls的访问Key,从m3u8文件中取到 
+     *
+     * Generated from protobuf field <code>string Ak = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setAk($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Ak = $var;
+
+        return $this;
+    }
+
+    /**
+     * Drm鉴权数据源,从m3u8文件中取到 
+     *
+     * Generated from protobuf field <code>string Source = 3;</code>
+     * @return string
+     */
+    public function getSource()
+    {
+        return $this->Source;
+    }
+
+    /**
+     * Drm鉴权数据源,从m3u8文件中取到 
+     *
+     * Generated from protobuf field <code>string Source = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSource($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Source = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetMLFramesForAuditRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetMLFramesForAuditRequest.php
new file mode 100755
index 0000000..e1a5ecf
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetMLFramesForAuditRequest.php
@@ -0,0 +1,439 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodGetMLFramesForAuditRequest</code>
+ */
+class VodGetMLFramesForAuditRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     */
+    protected $Vid = '';
+    /**
+     *审核帧模板信息中的Strategy,抽帧策略
+     *
+     * Generated from protobuf field <code>string Strategy = 2;</code>
+     */
+    protected $Strategy = '';
+    /**
+     *抽帧方式,参考VodFrameExtractingOptionForAudit
+     *
+     * Generated from protobuf field <code>string FrameOpt = 3;</code>
+     */
+    protected $FrameOpt = '';
+    /**
+     *抽帧方式选择Fps或FpsLimitNumberOfFrames时生效,表示抽帧的帧率,生效时需要保证大于0
+     *
+     * Generated from protobuf field <code>string FrameFps = 4;</code>
+     */
+    protected $FrameFps = '';
+    /**
+     *抽帧方式选择NumberOfFrames时生效,表示需要多少帧,生效时需要保证大于0
+     *
+     * Generated from protobuf field <code>string NumberOfFrames = 5;</code>
+     */
+    protected $NumberOfFrames = '';
+    /**
+     *抽帧方式选择CutTimes时生效,逗号分割,表示需要获取的帧的时间戳(ms)列表,生效时需要保证数组不为空
+     *
+     * Generated from protobuf field <code>string CutTimeMills = 6;</code>
+     */
+    protected $CutTimeMills = '';
+    /**
+     *需要首帧, 默认0,支持设置: 0-否,1-是
+     *
+     * Generated from protobuf field <code>string NeedFirstFrame = 7;</code>
+     */
+    protected $NeedFirstFrame = '';
+    /**
+     *需要尾帧, 默认0,支持设置: 0-否,1-是
+     *
+     * Generated from protobuf field <code>string NeedLastFrame = 8;</code>
+     */
+    protected $NeedLastFrame = '';
+    /**
+     *从视频的第几秒开始抽帧,单位:ms
+     *
+     * Generated from protobuf field <code>string StartTimeMill = 9;</code>
+     */
+    protected $StartTimeMill = '';
+    /**
+     *从视频的第几秒结束抽帧,单位:ms
+     *
+     * Generated from protobuf field <code>string EndTimeMill = 10;</code>
+     */
+    protected $EndTimeMill = '';
+    /**
+     *抽帧方式选择FpsLimitNumberOfFrames时生效,表示限制的最小帧数,为0表示不限制
+     *
+     * Generated from protobuf field <code>string MinNumberOfFrames = 11;</code>
+     */
+    protected $MinNumberOfFrames = '';
+    /**
+     *抽帧方式选择FpsLimitNumberOfFrames时生效,表示限制的最大帧数,为0表示不限制
+     *
+     * Generated from protobuf field <code>string MaxNumberOfFrames = 12;</code>
+     */
+    protected $MaxNumberOfFrames = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vid
+     *          视频ID
+     *     @type string $Strategy
+     *          审核帧模板信息中的Strategy,抽帧策略
+     *     @type string $FrameOpt
+     *          抽帧方式,参考VodFrameExtractingOptionForAudit
+     *     @type string $FrameFps
+     *          抽帧方式选择Fps或FpsLimitNumberOfFrames时生效,表示抽帧的帧率,生效时需要保证大于0
+     *     @type string $NumberOfFrames
+     *          抽帧方式选择NumberOfFrames时生效,表示需要多少帧,生效时需要保证大于0
+     *     @type string $CutTimeMills
+     *          抽帧方式选择CutTimes时生效,逗号分割,表示需要获取的帧的时间戳(ms)列表,生效时需要保证数组不为空
+     *     @type string $NeedFirstFrame
+     *          需要首帧, 默认0,支持设置: 0-否,1-是
+     *     @type string $NeedLastFrame
+     *          需要尾帧, 默认0,支持设置: 0-否,1-是
+     *     @type string $StartTimeMill
+     *          从视频的第几秒开始抽帧,单位:ms
+     *     @type string $EndTimeMill
+     *          从视频的第几秒结束抽帧,单位:ms
+     *     @type string $MinNumberOfFrames
+     *          抽帧方式选择FpsLimitNumberOfFrames时生效,表示限制的最小帧数,为0表示不限制
+     *     @type string $MaxNumberOfFrames
+     *          抽帧方式选择FpsLimitNumberOfFrames时生效,表示限制的最大帧数,为0表示不限制
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     *审核帧模板信息中的Strategy,抽帧策略
+     *
+     * Generated from protobuf field <code>string Strategy = 2;</code>
+     * @return string
+     */
+    public function getStrategy()
+    {
+        return $this->Strategy;
+    }
+
+    /**
+     *审核帧模板信息中的Strategy,抽帧策略
+     *
+     * Generated from protobuf field <code>string Strategy = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStrategy($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Strategy = $var;
+
+        return $this;
+    }
+
+    /**
+     *抽帧方式,参考VodFrameExtractingOptionForAudit
+     *
+     * Generated from protobuf field <code>string FrameOpt = 3;</code>
+     * @return string
+     */
+    public function getFrameOpt()
+    {
+        return $this->FrameOpt;
+    }
+
+    /**
+     *抽帧方式,参考VodFrameExtractingOptionForAudit
+     *
+     * Generated from protobuf field <code>string FrameOpt = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFrameOpt($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FrameOpt = $var;
+
+        return $this;
+    }
+
+    /**
+     *抽帧方式选择Fps或FpsLimitNumberOfFrames时生效,表示抽帧的帧率,生效时需要保证大于0
+     *
+     * Generated from protobuf field <code>string FrameFps = 4;</code>
+     * @return string
+     */
+    public function getFrameFps()
+    {
+        return $this->FrameFps;
+    }
+
+    /**
+     *抽帧方式选择Fps或FpsLimitNumberOfFrames时生效,表示抽帧的帧率,生效时需要保证大于0
+     *
+     * Generated from protobuf field <code>string FrameFps = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFrameFps($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FrameFps = $var;
+
+        return $this;
+    }
+
+    /**
+     *抽帧方式选择NumberOfFrames时生效,表示需要多少帧,生效时需要保证大于0
+     *
+     * Generated from protobuf field <code>string NumberOfFrames = 5;</code>
+     * @return string
+     */
+    public function getNumberOfFrames()
+    {
+        return $this->NumberOfFrames;
+    }
+
+    /**
+     *抽帧方式选择NumberOfFrames时生效,表示需要多少帧,生效时需要保证大于0
+     *
+     * Generated from protobuf field <code>string NumberOfFrames = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setNumberOfFrames($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->NumberOfFrames = $var;
+
+        return $this;
+    }
+
+    /**
+     *抽帧方式选择CutTimes时生效,逗号分割,表示需要获取的帧的时间戳(ms)列表,生效时需要保证数组不为空
+     *
+     * Generated from protobuf field <code>string CutTimeMills = 6;</code>
+     * @return string
+     */
+    public function getCutTimeMills()
+    {
+        return $this->CutTimeMills;
+    }
+
+    /**
+     *抽帧方式选择CutTimes时生效,逗号分割,表示需要获取的帧的时间戳(ms)列表,生效时需要保证数组不为空
+     *
+     * Generated from protobuf field <code>string CutTimeMills = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCutTimeMills($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CutTimeMills = $var;
+
+        return $this;
+    }
+
+    /**
+     *需要首帧, 默认0,支持设置: 0-否,1-是
+     *
+     * Generated from protobuf field <code>string NeedFirstFrame = 7;</code>
+     * @return string
+     */
+    public function getNeedFirstFrame()
+    {
+        return $this->NeedFirstFrame;
+    }
+
+    /**
+     *需要首帧, 默认0,支持设置: 0-否,1-是
+     *
+     * Generated from protobuf field <code>string NeedFirstFrame = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setNeedFirstFrame($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->NeedFirstFrame = $var;
+
+        return $this;
+    }
+
+    /**
+     *需要尾帧, 默认0,支持设置: 0-否,1-是
+     *
+     * Generated from protobuf field <code>string NeedLastFrame = 8;</code>
+     * @return string
+     */
+    public function getNeedLastFrame()
+    {
+        return $this->NeedLastFrame;
+    }
+
+    /**
+     *需要尾帧, 默认0,支持设置: 0-否,1-是
+     *
+     * Generated from protobuf field <code>string NeedLastFrame = 8;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setNeedLastFrame($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->NeedLastFrame = $var;
+
+        return $this;
+    }
+
+    /**
+     *从视频的第几秒开始抽帧,单位:ms
+     *
+     * Generated from protobuf field <code>string StartTimeMill = 9;</code>
+     * @return string
+     */
+    public function getStartTimeMill()
+    {
+        return $this->StartTimeMill;
+    }
+
+    /**
+     *从视频的第几秒开始抽帧,单位:ms
+     *
+     * Generated from protobuf field <code>string StartTimeMill = 9;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStartTimeMill($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StartTimeMill = $var;
+
+        return $this;
+    }
+
+    /**
+     *从视频的第几秒结束抽帧,单位:ms
+     *
+     * Generated from protobuf field <code>string EndTimeMill = 10;</code>
+     * @return string
+     */
+    public function getEndTimeMill()
+    {
+        return $this->EndTimeMill;
+    }
+
+    /**
+     *从视频的第几秒结束抽帧,单位:ms
+     *
+     * Generated from protobuf field <code>string EndTimeMill = 10;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setEndTimeMill($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->EndTimeMill = $var;
+
+        return $this;
+    }
+
+    /**
+     *抽帧方式选择FpsLimitNumberOfFrames时生效,表示限制的最小帧数,为0表示不限制
+     *
+     * Generated from protobuf field <code>string MinNumberOfFrames = 11;</code>
+     * @return string
+     */
+    public function getMinNumberOfFrames()
+    {
+        return $this->MinNumberOfFrames;
+    }
+
+    /**
+     *抽帧方式选择FpsLimitNumberOfFrames时生效,表示限制的最小帧数,为0表示不限制
+     *
+     * Generated from protobuf field <code>string MinNumberOfFrames = 11;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setMinNumberOfFrames($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->MinNumberOfFrames = $var;
+
+        return $this;
+    }
+
+    /**
+     *抽帧方式选择FpsLimitNumberOfFrames时生效,表示限制的最大帧数,为0表示不限制
+     *
+     * Generated from protobuf field <code>string MaxNumberOfFrames = 12;</code>
+     * @return string
+     */
+    public function getMaxNumberOfFrames()
+    {
+        return $this->MaxNumberOfFrames;
+    }
+
+    /**
+     *抽帧方式选择FpsLimitNumberOfFrames时生效,表示限制的最大帧数,为0表示不限制
+     *
+     * Generated from protobuf field <code>string MaxNumberOfFrames = 12;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setMaxNumberOfFrames($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->MaxNumberOfFrames = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetMediaInfosRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetMediaInfosRequest.php
new file mode 100755
index 0000000..a07d33c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetMediaInfosRequest.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodGetMediaInfosRequest</code>
+ */
+class VodGetMediaInfosRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *视频ID列表
+     *
+     * Generated from protobuf field <code>string Vids = 1;</code>
+     */
+    protected $Vids = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vids
+     *          视频ID列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *视频ID列表
+     *
+     * Generated from protobuf field <code>string Vids = 1;</code>
+     * @return string
+     */
+    public function getVids()
+    {
+        return $this->Vids;
+    }
+
+    /**
+     *视频ID列表
+     *
+     * Generated from protobuf field <code>string Vids = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVids($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vids = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetMediaListRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetMediaListRequest.php
new file mode 100755
index 0000000..89db7f8
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetMediaListRequest.php
@@ -0,0 +1,371 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodGetMediaListRequest</code>
+ */
+class VodGetMediaListRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *空间名
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 2;</code>
+     */
+    protected $Vid = '';
+    /**
+     *视频状态
+     *
+     * Generated from protobuf field <code>string Status = 3;</code>
+     */
+    protected $Status = '';
+    /**
+     *排序规则
+     *
+     * Generated from protobuf field <code>string Order = 4;</code>
+     */
+    protected $Order = '';
+    /**
+     *标签,多个用","隔开
+     *
+     * Generated from protobuf field <code>string Tags = 5;</code>
+     */
+    protected $Tags = '';
+    /**
+     *查询时间范围下限
+     *
+     * Generated from protobuf field <code>string StartTime = 6;</code>
+     */
+    protected $StartTime = '';
+    /**
+     *查询时间范围上限
+     *
+     * Generated from protobuf field <code>string EndTime = 7;</code>
+     */
+    protected $EndTime = '';
+    /**
+     *数据偏移量
+     *
+     * Generated from protobuf field <code>string Offset = 8;</code>
+     */
+    protected $Offset = '';
+    /**
+     *分页大小
+     *
+     * Generated from protobuf field <code>string PageSize = 9;</code>
+     */
+    protected $PageSize = '';
+    /**
+     *分类id列表
+     *
+     * Generated from protobuf field <code>string ClassificationIds = 10;</code>
+     */
+    protected $ClassificationIds = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *          空间名
+     *     @type string $Vid
+     *          视频ID
+     *     @type string $Status
+     *          视频状态
+     *     @type string $Order
+     *          排序规则
+     *     @type string $Tags
+     *          标签,多个用","隔开
+     *     @type string $StartTime
+     *          查询时间范围下限
+     *     @type string $EndTime
+     *          查询时间范围上限
+     *     @type string $Offset
+     *          数据偏移量
+     *     @type string $PageSize
+     *          分页大小
+     *     @type string $ClassificationIds
+     *          分类id列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *空间名
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     *空间名
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 2;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频状态
+     *
+     * Generated from protobuf field <code>string Status = 3;</code>
+     * @return string
+     */
+    public function getStatus()
+    {
+        return $this->Status;
+    }
+
+    /**
+     *视频状态
+     *
+     * Generated from protobuf field <code>string Status = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStatus($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Status = $var;
+
+        return $this;
+    }
+
+    /**
+     *排序规则
+     *
+     * Generated from protobuf field <code>string Order = 4;</code>
+     * @return string
+     */
+    public function getOrder()
+    {
+        return $this->Order;
+    }
+
+    /**
+     *排序规则
+     *
+     * Generated from protobuf field <code>string Order = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setOrder($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Order = $var;
+
+        return $this;
+    }
+
+    /**
+     *标签,多个用","隔开
+     *
+     * Generated from protobuf field <code>string Tags = 5;</code>
+     * @return string
+     */
+    public function getTags()
+    {
+        return $this->Tags;
+    }
+
+    /**
+     *标签,多个用","隔开
+     *
+     * Generated from protobuf field <code>string Tags = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTags($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Tags = $var;
+
+        return $this;
+    }
+
+    /**
+     *查询时间范围下限
+     *
+     * Generated from protobuf field <code>string StartTime = 6;</code>
+     * @return string
+     */
+    public function getStartTime()
+    {
+        return $this->StartTime;
+    }
+
+    /**
+     *查询时间范围下限
+     *
+     * Generated from protobuf field <code>string StartTime = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStartTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StartTime = $var;
+
+        return $this;
+    }
+
+    /**
+     *查询时间范围上限
+     *
+     * Generated from protobuf field <code>string EndTime = 7;</code>
+     * @return string
+     */
+    public function getEndTime()
+    {
+        return $this->EndTime;
+    }
+
+    /**
+     *查询时间范围上限
+     *
+     * Generated from protobuf field <code>string EndTime = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setEndTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->EndTime = $var;
+
+        return $this;
+    }
+
+    /**
+     *数据偏移量
+     *
+     * Generated from protobuf field <code>string Offset = 8;</code>
+     * @return string
+     */
+    public function getOffset()
+    {
+        return $this->Offset;
+    }
+
+    /**
+     *数据偏移量
+     *
+     * Generated from protobuf field <code>string Offset = 8;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setOffset($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Offset = $var;
+
+        return $this;
+    }
+
+    /**
+     *分页大小
+     *
+     * Generated from protobuf field <code>string PageSize = 9;</code>
+     * @return string
+     */
+    public function getPageSize()
+    {
+        return $this->PageSize;
+    }
+
+    /**
+     *分页大小
+     *
+     * Generated from protobuf field <code>string PageSize = 9;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setPageSize($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->PageSize = $var;
+
+        return $this;
+    }
+
+    /**
+     *分类id列表
+     *
+     * Generated from protobuf field <code>string ClassificationIds = 10;</code>
+     * @return string
+     */
+    public function getClassificationIds()
+    {
+        return $this->ClassificationIds;
+    }
+
+    /**
+     *分类id列表
+     *
+     * Generated from protobuf field <code>string ClassificationIds = 10;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setClassificationIds($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->ClassificationIds = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetPlayInfoRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetPlayInfoRequest.php
new file mode 100755
index 0000000..beeb93b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetPlayInfoRequest.php
@@ -0,0 +1,587 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodGetPlayInfoRequest</code>
+ */
+class VodGetPlayInfoRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 视频ID 
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     */
+    protected $Vid = '';
+    /**
+     * 封装格式,支持 mp4、dash、hls、mp3、m4a、ogg, 默认mp4 
+     *
+     * Generated from protobuf field <code>string Format = 2;</code>
+     */
+    protected $Format = '';
+    /**
+     * 编码格式,支持 mp3、aac、opus、H264、H265、H266, 视频默认H264,音频默认aac
+     *
+     * Generated from protobuf field <code>string Codec = 3;</code>
+     */
+    protected $Codec = '';
+    /**
+     * 视频流清晰度,支持:240p,360p,480p,540p,720p,1080p,2k,4k.默认返回全部
+     *
+     * Generated from protobuf field <code>string Definition = 4;</code>
+     */
+    protected $Definition = '';
+    /**
+     * 流文件类型,支持:
+     * 加密视频流evideo,加密音频流传eaudio,非加密视频流video,普通音频音频流audio.默认video
+     *
+     * Generated from protobuf field <code>string FileType = 5;</code>
+     */
+    protected $FileType = '';
+    /**
+     * 水印贴片标签 
+     *
+     * Generated from protobuf field <code>string LogoType = 6;</code>
+     */
+    protected $LogoType = '';
+    /**
+     * 播放地址是否base64编码,默认否,支持设置: 0-否,1-是 
+     *
+     * Generated from protobuf field <code>string Base64 = 7;</code>
+     */
+    protected $Base64 = '';
+    /**
+     * 返回https播放地址,默认否, 1-是;0-否 
+     *
+     * Generated from protobuf field <code>string Ssl = 8;</code>
+     */
+    protected $Ssl = '';
+    /**
+     * 是否需要雪碧图(缩略图),默认否,1-是;0-否 
+     *
+     * Generated from protobuf field <code>string NeedThumbs = 9;</code>
+     */
+    protected $NeedThumbs = '';
+    /**
+     * 是否需要蒙版弹幕,默认否,1-是;0-否 
+     *
+     * Generated from protobuf field <code>string NeedBarrageMask = 10;</code>
+     */
+    protected $NeedBarrageMask = '';
+    /**
+     * 指定CDN类型, 默认不传为普通CDN, 若需使用请联系技术支持 
+     *
+     * Generated from protobuf field <code>string CdnType = 11;</code>
+     */
+    protected $CdnType = '';
+    /**
+     * 唯一性标识信息, 若需使用请联系技术支持 
+     *
+     * Generated from protobuf field <code>string UnionInfo = 12;</code>
+     */
+    protected $UnionInfo = '';
+    /**
+     * HDR清晰度,默认不查询,支持:all, 240p, 360p, 420p, 480p, 540p, 720p,
+     * 1080p, 2k, 4k 
+     *
+     * Generated from protobuf field <code>string HDRDefinition = 13;</code>
+     */
+    protected $HDRDefinition = '';
+    /**
+     * 播放场景,指定获取对应场景的音视频流。当前支持:preview-试看 
+     *
+     * Generated from protobuf field <code>string PlayScene = 14;</code>
+     */
+    protected $PlayScene = '';
+    /**
+     * DRM过期时间戳, 若需使用请联系技术支持 
+     *
+     * Generated from protobuf field <code>string DrmExpireTimestamp = 15;</code>
+     */
+    protected $DrmExpireTimestamp = '';
+    /**
+     * 音频音质。当FileType为audio和eaudio时起作用,表示音频音质参数.支持: medium,
+     * higher, highest.默认返回所有音频流
+     *
+     * Generated from protobuf field <code>string Quality = 16;</code>
+     */
+    protected $Quality = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vid
+     *           视频ID 
+     *     @type string $Format
+     *           封装格式,支持 mp4、dash、hls、mp3、m4a、ogg, 默认mp4 
+     *     @type string $Codec
+     *           编码格式,支持 mp3、aac、opus、H264、H265、H266, 视频默认H264,音频默认aac
+     *     @type string $Definition
+     *           视频流清晰度,支持:240p,360p,480p,540p,720p,1080p,2k,4k.默认返回全部
+     *     @type string $FileType
+     *           流文件类型,支持:
+     *           加密视频流evideo,加密音频流传eaudio,非加密视频流video,普通音频音频流audio.默认video
+     *     @type string $LogoType
+     *           水印贴片标签 
+     *     @type string $Base64
+     *           播放地址是否base64编码,默认否,支持设置: 0-否,1-是 
+     *     @type string $Ssl
+     *           返回https播放地址,默认否, 1-是;0-否 
+     *     @type string $NeedThumbs
+     *           是否需要雪碧图(缩略图),默认否,1-是;0-否 
+     *     @type string $NeedBarrageMask
+     *           是否需要蒙版弹幕,默认否,1-是;0-否 
+     *     @type string $CdnType
+     *           指定CDN类型, 默认不传为普通CDN, 若需使用请联系技术支持 
+     *     @type string $UnionInfo
+     *           唯一性标识信息, 若需使用请联系技术支持 
+     *     @type string $HDRDefinition
+     *           HDR清晰度,默认不查询,支持:all, 240p, 360p, 420p, 480p, 540p, 720p,
+     *           1080p, 2k, 4k 
+     *     @type string $PlayScene
+     *           播放场景,指定获取对应场景的音视频流。当前支持:preview-试看 
+     *     @type string $DrmExpireTimestamp
+     *           DRM过期时间戳, 若需使用请联系技术支持 
+     *     @type string $Quality
+     *           音频音质。当FileType为audio和eaudio时起作用,表示音频音质参数.支持: medium,
+     *           higher, highest.默认返回所有音频流
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 视频ID 
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     * 视频ID 
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     * 封装格式,支持 mp4、dash、hls、mp3、m4a、ogg, 默认mp4 
+     *
+     * Generated from protobuf field <code>string Format = 2;</code>
+     * @return string
+     */
+    public function getFormat()
+    {
+        return $this->Format;
+    }
+
+    /**
+     * 封装格式,支持 mp4、dash、hls、mp3、m4a、ogg, 默认mp4 
+     *
+     * Generated from protobuf field <code>string Format = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFormat($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Format = $var;
+
+        return $this;
+    }
+
+    /**
+     * 编码格式,支持 mp3、aac、opus、H264、H265、H266, 视频默认H264,音频默认aac
+     *
+     * Generated from protobuf field <code>string Codec = 3;</code>
+     * @return string
+     */
+    public function getCodec()
+    {
+        return $this->Codec;
+    }
+
+    /**
+     * 编码格式,支持 mp3、aac、opus、H264、H265、H266, 视频默认H264,音频默认aac
+     *
+     * Generated from protobuf field <code>string Codec = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCodec($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Codec = $var;
+
+        return $this;
+    }
+
+    /**
+     * 视频流清晰度,支持:240p,360p,480p,540p,720p,1080p,2k,4k.默认返回全部
+     *
+     * Generated from protobuf field <code>string Definition = 4;</code>
+     * @return string
+     */
+    public function getDefinition()
+    {
+        return $this->Definition;
+    }
+
+    /**
+     * 视频流清晰度,支持:240p,360p,480p,540p,720p,1080p,2k,4k.默认返回全部
+     *
+     * Generated from protobuf field <code>string Definition = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDefinition($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Definition = $var;
+
+        return $this;
+    }
+
+    /**
+     * 流文件类型,支持:
+     * 加密视频流evideo,加密音频流传eaudio,非加密视频流video,普通音频音频流audio.默认video
+     *
+     * Generated from protobuf field <code>string FileType = 5;</code>
+     * @return string
+     */
+    public function getFileType()
+    {
+        return $this->FileType;
+    }
+
+    /**
+     * 流文件类型,支持:
+     * 加密视频流evideo,加密音频流传eaudio,非加密视频流video,普通音频音频流audio.默认video
+     *
+     * Generated from protobuf field <code>string FileType = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileType = $var;
+
+        return $this;
+    }
+
+    /**
+     * 水印贴片标签 
+     *
+     * Generated from protobuf field <code>string LogoType = 6;</code>
+     * @return string
+     */
+    public function getLogoType()
+    {
+        return $this->LogoType;
+    }
+
+    /**
+     * 水印贴片标签 
+     *
+     * Generated from protobuf field <code>string LogoType = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setLogoType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->LogoType = $var;
+
+        return $this;
+    }
+
+    /**
+     * 播放地址是否base64编码,默认否,支持设置: 0-否,1-是 
+     *
+     * Generated from protobuf field <code>string Base64 = 7;</code>
+     * @return string
+     */
+    public function getBase64()
+    {
+        return $this->Base64;
+    }
+
+    /**
+     * 播放地址是否base64编码,默认否,支持设置: 0-否,1-是 
+     *
+     * Generated from protobuf field <code>string Base64 = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setBase64($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Base64 = $var;
+
+        return $this;
+    }
+
+    /**
+     * 返回https播放地址,默认否, 1-是;0-否 
+     *
+     * Generated from protobuf field <code>string Ssl = 8;</code>
+     * @return string
+     */
+    public function getSsl()
+    {
+        return $this->Ssl;
+    }
+
+    /**
+     * 返回https播放地址,默认否, 1-是;0-否 
+     *
+     * Generated from protobuf field <code>string Ssl = 8;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSsl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Ssl = $var;
+
+        return $this;
+    }
+
+    /**
+     * 是否需要雪碧图(缩略图),默认否,1-是;0-否 
+     *
+     * Generated from protobuf field <code>string NeedThumbs = 9;</code>
+     * @return string
+     */
+    public function getNeedThumbs()
+    {
+        return $this->NeedThumbs;
+    }
+
+    /**
+     * 是否需要雪碧图(缩略图),默认否,1-是;0-否 
+     *
+     * Generated from protobuf field <code>string NeedThumbs = 9;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setNeedThumbs($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->NeedThumbs = $var;
+
+        return $this;
+    }
+
+    /**
+     * 是否需要蒙版弹幕,默认否,1-是;0-否 
+     *
+     * Generated from protobuf field <code>string NeedBarrageMask = 10;</code>
+     * @return string
+     */
+    public function getNeedBarrageMask()
+    {
+        return $this->NeedBarrageMask;
+    }
+
+    /**
+     * 是否需要蒙版弹幕,默认否,1-是;0-否 
+     *
+     * Generated from protobuf field <code>string NeedBarrageMask = 10;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setNeedBarrageMask($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->NeedBarrageMask = $var;
+
+        return $this;
+    }
+
+    /**
+     * 指定CDN类型, 默认不传为普通CDN, 若需使用请联系技术支持 
+     *
+     * Generated from protobuf field <code>string CdnType = 11;</code>
+     * @return string
+     */
+    public function getCdnType()
+    {
+        return $this->CdnType;
+    }
+
+    /**
+     * 指定CDN类型, 默认不传为普通CDN, 若需使用请联系技术支持 
+     *
+     * Generated from protobuf field <code>string CdnType = 11;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCdnType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CdnType = $var;
+
+        return $this;
+    }
+
+    /**
+     * 唯一性标识信息, 若需使用请联系技术支持 
+     *
+     * Generated from protobuf field <code>string UnionInfo = 12;</code>
+     * @return string
+     */
+    public function getUnionInfo()
+    {
+        return $this->UnionInfo;
+    }
+
+    /**
+     * 唯一性标识信息, 若需使用请联系技术支持 
+     *
+     * Generated from protobuf field <code>string UnionInfo = 12;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setUnionInfo($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->UnionInfo = $var;
+
+        return $this;
+    }
+
+    /**
+     * HDR清晰度,默认不查询,支持:all, 240p, 360p, 420p, 480p, 540p, 720p,
+     * 1080p, 2k, 4k 
+     *
+     * Generated from protobuf field <code>string HDRDefinition = 13;</code>
+     * @return string
+     */
+    public function getHDRDefinition()
+    {
+        return $this->HDRDefinition;
+    }
+
+    /**
+     * HDR清晰度,默认不查询,支持:all, 240p, 360p, 420p, 480p, 540p, 720p,
+     * 1080p, 2k, 4k 
+     *
+     * Generated from protobuf field <code>string HDRDefinition = 13;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setHDRDefinition($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->HDRDefinition = $var;
+
+        return $this;
+    }
+
+    /**
+     * 播放场景,指定获取对应场景的音视频流。当前支持:preview-试看 
+     *
+     * Generated from protobuf field <code>string PlayScene = 14;</code>
+     * @return string
+     */
+    public function getPlayScene()
+    {
+        return $this->PlayScene;
+    }
+
+    /**
+     * 播放场景,指定获取对应场景的音视频流。当前支持:preview-试看 
+     *
+     * Generated from protobuf field <code>string PlayScene = 14;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setPlayScene($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->PlayScene = $var;
+
+        return $this;
+    }
+
+    /**
+     * DRM过期时间戳, 若需使用请联系技术支持 
+     *
+     * Generated from protobuf field <code>string DrmExpireTimestamp = 15;</code>
+     * @return string
+     */
+    public function getDrmExpireTimestamp()
+    {
+        return $this->DrmExpireTimestamp;
+    }
+
+    /**
+     * DRM过期时间戳, 若需使用请联系技术支持 
+     *
+     * Generated from protobuf field <code>string DrmExpireTimestamp = 15;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDrmExpireTimestamp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->DrmExpireTimestamp = $var;
+
+        return $this;
+    }
+
+    /**
+     * 音频音质。当FileType为audio和eaudio时起作用,表示音频音质参数.支持: medium,
+     * higher, highest.默认返回所有音频流
+     *
+     * Generated from protobuf field <code>string Quality = 16;</code>
+     * @return string
+     */
+    public function getQuality()
+    {
+        return $this->Quality;
+    }
+
+    /**
+     * 音频音质。当FileType为audio和eaudio时起作用,表示音频音质参数.支持: medium,
+     * higher, highest.默认返回所有音频流
+     *
+     * Generated from protobuf field <code>string Quality = 16;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setQuality($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Quality = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetPlayInfoWithLiveTimeShiftSceneRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetPlayInfoWithLiveTimeShiftSceneRequest.php
new file mode 100755
index 0000000..4fb24ed
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetPlayInfoWithLiveTimeShiftSceneRequest.php
@@ -0,0 +1,201 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodGetPlayInfoWithLiveTimeShiftSceneRequest</code>
+ */
+class VodGetPlayInfoWithLiveTimeShiftSceneRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 播放URI列表,多个uri之间使用“,”分隔 
+     *
+     * Generated from protobuf field <code>string StoreUris = 1;</code>
+     */
+    protected $StoreUris = '';
+    /**
+     * 空间名称 
+     *
+     * Generated from protobuf field <code>string SpaceName = 2;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     * 返回https播放地址,默认否, 1-是;0-否 
+     *
+     * Generated from protobuf field <code>string Ssl = 3;</code>
+     */
+    protected $Ssl = '';
+    /**
+     * 过期时间戳:毫秒。若不传,则为当前时间+1小时 
+     *
+     * Generated from protobuf field <code>string ExpireTimestamp = 4;</code>
+     */
+    protected $ExpireTimestamp = '';
+    /**
+     * 生成地址时填充BucketName,默认否, 1-是;0-否 
+     *
+     * Generated from protobuf field <code>string NeedComposeBucketName = 5;</code>
+     */
+    protected $NeedComposeBucketName = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $StoreUris
+     *           播放URI列表,多个uri之间使用“,”分隔 
+     *     @type string $SpaceName
+     *           空间名称 
+     *     @type string $Ssl
+     *           返回https播放地址,默认否, 1-是;0-否 
+     *     @type string $ExpireTimestamp
+     *           过期时间戳:毫秒。若不传,则为当前时间+1小时 
+     *     @type string $NeedComposeBucketName
+     *           生成地址时填充BucketName,默认否, 1-是;0-否 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 播放URI列表,多个uri之间使用“,”分隔 
+     *
+     * Generated from protobuf field <code>string StoreUris = 1;</code>
+     * @return string
+     */
+    public function getStoreUris()
+    {
+        return $this->StoreUris;
+    }
+
+    /**
+     * 播放URI列表,多个uri之间使用“,”分隔 
+     *
+     * Generated from protobuf field <code>string StoreUris = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStoreUris($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StoreUris = $var;
+
+        return $this;
+    }
+
+    /**
+     * 空间名称 
+     *
+     * Generated from protobuf field <code>string SpaceName = 2;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * 空间名称 
+     *
+     * Generated from protobuf field <code>string SpaceName = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 返回https播放地址,默认否, 1-是;0-否 
+     *
+     * Generated from protobuf field <code>string Ssl = 3;</code>
+     * @return string
+     */
+    public function getSsl()
+    {
+        return $this->Ssl;
+    }
+
+    /**
+     * 返回https播放地址,默认否, 1-是;0-否 
+     *
+     * Generated from protobuf field <code>string Ssl = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSsl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Ssl = $var;
+
+        return $this;
+    }
+
+    /**
+     * 过期时间戳:毫秒。若不传,则为当前时间+1小时 
+     *
+     * Generated from protobuf field <code>string ExpireTimestamp = 4;</code>
+     * @return string
+     */
+    public function getExpireTimestamp()
+    {
+        return $this->ExpireTimestamp;
+    }
+
+    /**
+     * 过期时间戳:毫秒。若不传,则为当前时间+1小时 
+     *
+     * Generated from protobuf field <code>string ExpireTimestamp = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setExpireTimestamp($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->ExpireTimestamp = $var;
+
+        return $this;
+    }
+
+    /**
+     * 生成地址时填充BucketName,默认否, 1-是;0-否 
+     *
+     * Generated from protobuf field <code>string NeedComposeBucketName = 5;</code>
+     * @return string
+     */
+    public function getNeedComposeBucketName()
+    {
+        return $this->NeedComposeBucketName;
+    }
+
+    /**
+     * 生成地址时填充BucketName,默认否, 1-是;0-否 
+     *
+     * Generated from protobuf field <code>string NeedComposeBucketName = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setNeedComposeBucketName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->NeedComposeBucketName = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetPrivateDrmPlayAuthRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetPrivateDrmPlayAuthRequest.php
new file mode 100755
index 0000000..8e62210
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetPrivateDrmPlayAuthRequest.php
@@ -0,0 +1,167 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodGetPrivateDrmPlayAuthRequest</code>
+ */
+class VodGetPrivateDrmPlayAuthRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * drm类型,支持web,app。默认web 
+     *
+     * Generated from protobuf field <code>string DrmType = 1;</code>
+     */
+    protected $DrmType = '';
+    /**
+     * 视频id 
+     *
+     * Generated from protobuf field <code>string Vid = 2;</code>
+     */
+    protected $Vid = '';
+    /**
+     * 播放许可id列表,以逗号分割 
+     *
+     * Generated from protobuf field <code>string PlayAuthIds = 3;</code>
+     */
+    protected $PlayAuthIds = '';
+    /**
+     * 唯一性标识信息, 若需使用请联系技术支持 
+     *
+     * Generated from protobuf field <code>string UnionInfo = 4;</code>
+     */
+    protected $UnionInfo = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $DrmType
+     *           drm类型,支持web,app。默认web 
+     *     @type string $Vid
+     *           视频id 
+     *     @type string $PlayAuthIds
+     *           播放许可id列表,以逗号分割 
+     *     @type string $UnionInfo
+     *           唯一性标识信息, 若需使用请联系技术支持 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * drm类型,支持web,app。默认web 
+     *
+     * Generated from protobuf field <code>string DrmType = 1;</code>
+     * @return string
+     */
+    public function getDrmType()
+    {
+        return $this->DrmType;
+    }
+
+    /**
+     * drm类型,支持web,app。默认web 
+     *
+     * Generated from protobuf field <code>string DrmType = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDrmType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->DrmType = $var;
+
+        return $this;
+    }
+
+    /**
+     * 视频id 
+     *
+     * Generated from protobuf field <code>string Vid = 2;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     * 视频id 
+     *
+     * Generated from protobuf field <code>string Vid = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     * 播放许可id列表,以逗号分割 
+     *
+     * Generated from protobuf field <code>string PlayAuthIds = 3;</code>
+     * @return string
+     */
+    public function getPlayAuthIds()
+    {
+        return $this->PlayAuthIds;
+    }
+
+    /**
+     * 播放许可id列表,以逗号分割 
+     *
+     * Generated from protobuf field <code>string PlayAuthIds = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setPlayAuthIds($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->PlayAuthIds = $var;
+
+        return $this;
+    }
+
+    /**
+     * 唯一性标识信息, 若需使用请联系技术支持 
+     *
+     * Generated from protobuf field <code>string UnionInfo = 4;</code>
+     * @return string
+     */
+    public function getUnionInfo()
+    {
+        return $this->UnionInfo;
+    }
+
+    /**
+     * 唯一性标识信息, 若需使用请联系技术支持 
+     *
+     * Generated from protobuf field <code>string UnionInfo = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setUnionInfo($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->UnionInfo = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetRecommendedPosterRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetRecommendedPosterRequest.php
new file mode 100755
index 0000000..a74ed0e
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetRecommendedPosterRequest.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodGetRecommendedPosterRequest</code>
+ */
+class VodGetRecommendedPosterRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *视频ID列表
+     *
+     * Generated from protobuf field <code>string Vids = 1;</code>
+     */
+    protected $Vids = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vids
+     *          视频ID列表
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *视频ID列表
+     *
+     * Generated from protobuf field <code>string Vids = 1;</code>
+     * @return string
+     */
+    public function getVids()
+    {
+        return $this->Vids;
+    }
+
+    /**
+     *视频ID列表
+     *
+     * Generated from protobuf field <code>string Vids = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVids($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vids = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetSmartStrategyLitePlayInfoRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetSmartStrategyLitePlayInfoRequest.php
new file mode 100755
index 0000000..4cc10d9
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetSmartStrategyLitePlayInfoRequest.php
@@ -0,0 +1,417 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * **********************************************************************
+ * 智能策略-轻量版
+ * **********************************************************************
+ *
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodGetSmartStrategyLitePlayInfoRequest</code>
+ */
+class VodGetSmartStrategyLitePlayInfoRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 播放url 
+     *
+     * Generated from protobuf field <code>string PlayUrl = 1;</code>
+     */
+    protected $PlayUrl = '';
+    /**
+     * 封装格式,支持mp4,dash,hls,默认mp4Format 
+     *
+     * Generated from protobuf field <code>string Format = 2;</code>
+     */
+    protected $Format = '';
+    /**
+     * 编码类型,默认h264,可选值为h264,bytevc1等Codec 
+     *
+     * Generated from protobuf field <code>string Codec = 3;</code>
+     */
+    protected $Codec = '';
+    /**
+     * 视频流清晰度,默认返回全部,支持:240p,360p,480p,540p,720p,1080p,
+     * 2k, 4k
+     *
+     * Generated from protobuf field <code>string Definition = 4;</code>
+     */
+    protected $Definition = '';
+    /**
+     * 流文件类型,默认video,支持:加密视频流evideo,加密音频流传eaudio,非加密视频流video,普通音频音频流audio
+     *
+     * Generated from protobuf field <code>string FileType = 5;</code>
+     */
+    protected $FileType = '';
+    /**
+     * 水印贴片标签 
+     *
+     * Generated from protobuf field <code>string LogoType = 6;</code>
+     */
+    protected $LogoType = '';
+    /**
+     * 返回https播放地址,默认否, 1-是;0-否 
+     *
+     * Generated from protobuf field <code>string Ssl = 7;</code>
+     */
+    protected $Ssl = '';
+    /**
+     * 是否需要雪碧图(缩略图),默认否,1-是;0-否 
+     *
+     * Generated from protobuf field <code>string NeedThumbs = 8;</code>
+     */
+    protected $NeedThumbs = '';
+    /**
+     * 是否需要蒙版弹幕,默认否,1-是;0-否 
+     *
+     * Generated from protobuf field <code>string NeedBarrageMask = 9;</code>
+     */
+    protected $NeedBarrageMask = '';
+    /**
+     * 唯一性标识信息 
+     *
+     * Generated from protobuf field <code>string UnionInfo = 10;</code>
+     */
+    protected $UnionInfo = '';
+    /**
+     * HDR清晰度,默认不查询,支持:all,
+     * 240p,360p, 420p,480p,540p,720p,1080p, 2k, 4k
+     *
+     * Generated from protobuf field <code>string HDRDefinition = 11;</code>
+     */
+    protected $HDRDefinition = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $PlayUrl
+     *           播放url 
+     *     @type string $Format
+     *           封装格式,支持mp4,dash,hls,默认mp4Format 
+     *     @type string $Codec
+     *           编码类型,默认h264,可选值为h264,bytevc1等Codec 
+     *     @type string $Definition
+     *           视频流清晰度,默认返回全部,支持:240p,360p,480p,540p,720p,1080p,
+     *           2k, 4k
+     *     @type string $FileType
+     *           流文件类型,默认video,支持:加密视频流evideo,加密音频流传eaudio,非加密视频流video,普通音频音频流audio
+     *     @type string $LogoType
+     *           水印贴片标签 
+     *     @type string $Ssl
+     *           返回https播放地址,默认否, 1-是;0-否 
+     *     @type string $NeedThumbs
+     *           是否需要雪碧图(缩略图),默认否,1-是;0-否 
+     *     @type string $NeedBarrageMask
+     *           是否需要蒙版弹幕,默认否,1-是;0-否 
+     *     @type string $UnionInfo
+     *           唯一性标识信息 
+     *     @type string $HDRDefinition
+     *           HDR清晰度,默认不查询,支持:all,
+     *           240p,360p, 420p,480p,540p,720p,1080p, 2k, 4k
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 播放url 
+     *
+     * Generated from protobuf field <code>string PlayUrl = 1;</code>
+     * @return string
+     */
+    public function getPlayUrl()
+    {
+        return $this->PlayUrl;
+    }
+
+    /**
+     * 播放url 
+     *
+     * Generated from protobuf field <code>string PlayUrl = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setPlayUrl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->PlayUrl = $var;
+
+        return $this;
+    }
+
+    /**
+     * 封装格式,支持mp4,dash,hls,默认mp4Format 
+     *
+     * Generated from protobuf field <code>string Format = 2;</code>
+     * @return string
+     */
+    public function getFormat()
+    {
+        return $this->Format;
+    }
+
+    /**
+     * 封装格式,支持mp4,dash,hls,默认mp4Format 
+     *
+     * Generated from protobuf field <code>string Format = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFormat($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Format = $var;
+
+        return $this;
+    }
+
+    /**
+     * 编码类型,默认h264,可选值为h264,bytevc1等Codec 
+     *
+     * Generated from protobuf field <code>string Codec = 3;</code>
+     * @return string
+     */
+    public function getCodec()
+    {
+        return $this->Codec;
+    }
+
+    /**
+     * 编码类型,默认h264,可选值为h264,bytevc1等Codec 
+     *
+     * Generated from protobuf field <code>string Codec = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCodec($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Codec = $var;
+
+        return $this;
+    }
+
+    /**
+     * 视频流清晰度,默认返回全部,支持:240p,360p,480p,540p,720p,1080p,
+     * 2k, 4k
+     *
+     * Generated from protobuf field <code>string Definition = 4;</code>
+     * @return string
+     */
+    public function getDefinition()
+    {
+        return $this->Definition;
+    }
+
+    /**
+     * 视频流清晰度,默认返回全部,支持:240p,360p,480p,540p,720p,1080p,
+     * 2k, 4k
+     *
+     * Generated from protobuf field <code>string Definition = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDefinition($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Definition = $var;
+
+        return $this;
+    }
+
+    /**
+     * 流文件类型,默认video,支持:加密视频流evideo,加密音频流传eaudio,非加密视频流video,普通音频音频流audio
+     *
+     * Generated from protobuf field <code>string FileType = 5;</code>
+     * @return string
+     */
+    public function getFileType()
+    {
+        return $this->FileType;
+    }
+
+    /**
+     * 流文件类型,默认video,支持:加密视频流evideo,加密音频流传eaudio,非加密视频流video,普通音频音频流audio
+     *
+     * Generated from protobuf field <code>string FileType = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileType = $var;
+
+        return $this;
+    }
+
+    /**
+     * 水印贴片标签 
+     *
+     * Generated from protobuf field <code>string LogoType = 6;</code>
+     * @return string
+     */
+    public function getLogoType()
+    {
+        return $this->LogoType;
+    }
+
+    /**
+     * 水印贴片标签 
+     *
+     * Generated from protobuf field <code>string LogoType = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setLogoType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->LogoType = $var;
+
+        return $this;
+    }
+
+    /**
+     * 返回https播放地址,默认否, 1-是;0-否 
+     *
+     * Generated from protobuf field <code>string Ssl = 7;</code>
+     * @return string
+     */
+    public function getSsl()
+    {
+        return $this->Ssl;
+    }
+
+    /**
+     * 返回https播放地址,默认否, 1-是;0-否 
+     *
+     * Generated from protobuf field <code>string Ssl = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSsl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Ssl = $var;
+
+        return $this;
+    }
+
+    /**
+     * 是否需要雪碧图(缩略图),默认否,1-是;0-否 
+     *
+     * Generated from protobuf field <code>string NeedThumbs = 8;</code>
+     * @return string
+     */
+    public function getNeedThumbs()
+    {
+        return $this->NeedThumbs;
+    }
+
+    /**
+     * 是否需要雪碧图(缩略图),默认否,1-是;0-否 
+     *
+     * Generated from protobuf field <code>string NeedThumbs = 8;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setNeedThumbs($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->NeedThumbs = $var;
+
+        return $this;
+    }
+
+    /**
+     * 是否需要蒙版弹幕,默认否,1-是;0-否 
+     *
+     * Generated from protobuf field <code>string NeedBarrageMask = 9;</code>
+     * @return string
+     */
+    public function getNeedBarrageMask()
+    {
+        return $this->NeedBarrageMask;
+    }
+
+    /**
+     * 是否需要蒙版弹幕,默认否,1-是;0-否 
+     *
+     * Generated from protobuf field <code>string NeedBarrageMask = 9;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setNeedBarrageMask($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->NeedBarrageMask = $var;
+
+        return $this;
+    }
+
+    /**
+     * 唯一性标识信息 
+     *
+     * Generated from protobuf field <code>string UnionInfo = 10;</code>
+     * @return string
+     */
+    public function getUnionInfo()
+    {
+        return $this->UnionInfo;
+    }
+
+    /**
+     * 唯一性标识信息 
+     *
+     * Generated from protobuf field <code>string UnionInfo = 10;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setUnionInfo($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->UnionInfo = $var;
+
+        return $this;
+    }
+
+    /**
+     * HDR清晰度,默认不查询,支持:all,
+     * 240p,360p, 420p,480p,540p,720p,1080p, 2k, 4k
+     *
+     * Generated from protobuf field <code>string HDRDefinition = 11;</code>
+     * @return string
+     */
+    public function getHDRDefinition()
+    {
+        return $this->HDRDefinition;
+    }
+
+    /**
+     * HDR清晰度,默认不查询,支持:all,
+     * 240p,360p, 420p,480p,540p,720p,1080p, 2k, 4k
+     *
+     * Generated from protobuf field <code>string HDRDefinition = 11;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setHDRDefinition($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->HDRDefinition = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetSpaceDetailRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetSpaceDetailRequest.php
new file mode 100755
index 0000000..5987041
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetSpaceDetailRequest.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodGetSpaceDetailRequest</code>
+ */
+class VodGetSpaceDetailRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *           空间名 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetSubtitleInfoListRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetSubtitleInfoListRequest.php
new file mode 100755
index 0000000..9356aef
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetSubtitleInfoListRequest.php
@@ -0,0 +1,439 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodGetSubtitleInfoListRequest</code>
+ */
+class VodGetSubtitleInfoListRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     */
+    protected $Vid = '';
+    /**
+     *转码视频id列表,多个用","隔开
+     *
+     * Generated from protobuf field <code>string FileIds = 2;</code>
+     */
+    protected $FileIds = '';
+    /**
+     *字幕语言列表,多个用","隔开
+     *
+     * Generated from protobuf field <code>string Languages = 3;</code>
+     */
+    protected $Languages = '';
+    /**
+     *字幕格式列表,多个用","隔开
+     *
+     * Generated from protobuf field <code>string Formats = 4;</code>
+     */
+    protected $Formats = '';
+    /**
+     *字幕语言Id列表,多个用","隔开
+     *
+     * Generated from protobuf field <code>string LanguageIds = 5;</code>
+     */
+    protected $LanguageIds = '';
+    /**
+     *字幕Id列表,多个用","隔开
+     *
+     * Generated from protobuf field <code>string SubtitleIds = 6;</code>
+     */
+    protected $SubtitleIds = '';
+    /**
+     *状态,多个用","隔开
+     *
+     * Generated from protobuf field <code>string Status = 7;</code>
+     */
+    protected $Status = '';
+    /**
+     *标题
+     *
+     * Generated from protobuf field <code>string Title = 8;</code>
+     */
+    protected $Title = '';
+    /**
+     *标签
+     *
+     * Generated from protobuf field <code>string Tag = 9;</code>
+     */
+    protected $Tag = '';
+    /**
+     * file的偏移量
+     *
+     * Generated from protobuf field <code>string Offset = 10;</code>
+     */
+    protected $Offset = '';
+    /**
+     *分页大小(结果一fileId分页)
+     *
+     * Generated from protobuf field <code>string PageSize = 11;</code>
+     */
+    protected $PageSize = '';
+    /**
+     *返回https播放地址,默认否, 1-是;0-否
+     *
+     * Generated from protobuf field <code>string Ssl = 12;</code>
+     */
+    protected $Ssl = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vid
+     *          视频ID
+     *     @type string $FileIds
+     *          转码视频id列表,多个用","隔开
+     *     @type string $Languages
+     *          字幕语言列表,多个用","隔开
+     *     @type string $Formats
+     *          字幕格式列表,多个用","隔开
+     *     @type string $LanguageIds
+     *          字幕语言Id列表,多个用","隔开
+     *     @type string $SubtitleIds
+     *          字幕Id列表,多个用","隔开
+     *     @type string $Status
+     *          状态,多个用","隔开
+     *     @type string $Title
+     *          标题
+     *     @type string $Tag
+     *          标签
+     *     @type string $Offset
+     *           file的偏移量
+     *     @type string $PageSize
+     *          分页大小(结果一fileId分页)
+     *     @type string $Ssl
+     *          返回https播放地址,默认否, 1-是;0-否
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     *转码视频id列表,多个用","隔开
+     *
+     * Generated from protobuf field <code>string FileIds = 2;</code>
+     * @return string
+     */
+    public function getFileIds()
+    {
+        return $this->FileIds;
+    }
+
+    /**
+     *转码视频id列表,多个用","隔开
+     *
+     * Generated from protobuf field <code>string FileIds = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileIds($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileIds = $var;
+
+        return $this;
+    }
+
+    /**
+     *字幕语言列表,多个用","隔开
+     *
+     * Generated from protobuf field <code>string Languages = 3;</code>
+     * @return string
+     */
+    public function getLanguages()
+    {
+        return $this->Languages;
+    }
+
+    /**
+     *字幕语言列表,多个用","隔开
+     *
+     * Generated from protobuf field <code>string Languages = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setLanguages($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Languages = $var;
+
+        return $this;
+    }
+
+    /**
+     *字幕格式列表,多个用","隔开
+     *
+     * Generated from protobuf field <code>string Formats = 4;</code>
+     * @return string
+     */
+    public function getFormats()
+    {
+        return $this->Formats;
+    }
+
+    /**
+     *字幕格式列表,多个用","隔开
+     *
+     * Generated from protobuf field <code>string Formats = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFormats($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Formats = $var;
+
+        return $this;
+    }
+
+    /**
+     *字幕语言Id列表,多个用","隔开
+     *
+     * Generated from protobuf field <code>string LanguageIds = 5;</code>
+     * @return string
+     */
+    public function getLanguageIds()
+    {
+        return $this->LanguageIds;
+    }
+
+    /**
+     *字幕语言Id列表,多个用","隔开
+     *
+     * Generated from protobuf field <code>string LanguageIds = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setLanguageIds($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->LanguageIds = $var;
+
+        return $this;
+    }
+
+    /**
+     *字幕Id列表,多个用","隔开
+     *
+     * Generated from protobuf field <code>string SubtitleIds = 6;</code>
+     * @return string
+     */
+    public function getSubtitleIds()
+    {
+        return $this->SubtitleIds;
+    }
+
+    /**
+     *字幕Id列表,多个用","隔开
+     *
+     * Generated from protobuf field <code>string SubtitleIds = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSubtitleIds($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SubtitleIds = $var;
+
+        return $this;
+    }
+
+    /**
+     *状态,多个用","隔开
+     *
+     * Generated from protobuf field <code>string Status = 7;</code>
+     * @return string
+     */
+    public function getStatus()
+    {
+        return $this->Status;
+    }
+
+    /**
+     *状态,多个用","隔开
+     *
+     * Generated from protobuf field <code>string Status = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStatus($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Status = $var;
+
+        return $this;
+    }
+
+    /**
+     *标题
+     *
+     * Generated from protobuf field <code>string Title = 8;</code>
+     * @return string
+     */
+    public function getTitle()
+    {
+        return $this->Title;
+    }
+
+    /**
+     *标题
+     *
+     * Generated from protobuf field <code>string Title = 8;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTitle($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Title = $var;
+
+        return $this;
+    }
+
+    /**
+     *标签
+     *
+     * Generated from protobuf field <code>string Tag = 9;</code>
+     * @return string
+     */
+    public function getTag()
+    {
+        return $this->Tag;
+    }
+
+    /**
+     *标签
+     *
+     * Generated from protobuf field <code>string Tag = 9;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTag($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Tag = $var;
+
+        return $this;
+    }
+
+    /**
+     * file的偏移量
+     *
+     * Generated from protobuf field <code>string Offset = 10;</code>
+     * @return string
+     */
+    public function getOffset()
+    {
+        return $this->Offset;
+    }
+
+    /**
+     * file的偏移量
+     *
+     * Generated from protobuf field <code>string Offset = 10;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setOffset($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Offset = $var;
+
+        return $this;
+    }
+
+    /**
+     *分页大小(结果一fileId分页)
+     *
+     * Generated from protobuf field <code>string PageSize = 11;</code>
+     * @return string
+     */
+    public function getPageSize()
+    {
+        return $this->PageSize;
+    }
+
+    /**
+     *分页大小(结果一fileId分页)
+     *
+     * Generated from protobuf field <code>string PageSize = 11;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setPageSize($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->PageSize = $var;
+
+        return $this;
+    }
+
+    /**
+     *返回https播放地址,默认否, 1-是;0-否
+     *
+     * Generated from protobuf field <code>string Ssl = 12;</code>
+     * @return string
+     */
+    public function getSsl()
+    {
+        return $this->Ssl;
+    }
+
+    /**
+     *返回https播放地址,默认否, 1-是;0-否
+     *
+     * Generated from protobuf field <code>string Ssl = 12;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSsl($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Ssl = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetWorkflowExecutionDetailRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetWorkflowExecutionDetailRequest.php
new file mode 100755
index 0000000..eff80b7
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetWorkflowExecutionDetailRequest.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodGetWorkflowExecutionDetailRequest</code>
+ */
+class VodGetWorkflowExecutionDetailRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 工作流执行Id
+     *
+     * Generated from protobuf field <code>string RunId = 1;</code>
+     */
+    protected $RunId = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $RunId
+     *           工作流执行Id
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 工作流执行Id
+     *
+     * Generated from protobuf field <code>string RunId = 1;</code>
+     * @return string
+     */
+    public function getRunId()
+    {
+        return $this->RunId;
+    }
+
+    /**
+     * 工作流执行Id
+     *
+     * Generated from protobuf field <code>string RunId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setRunId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->RunId = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetWorkflowExecutionStatusRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetWorkflowExecutionStatusRequest.php
new file mode 100755
index 0000000..c0bccb0
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodGetWorkflowExecutionStatusRequest.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodGetWorkflowExecutionStatusRequest</code>
+ */
+class VodGetWorkflowExecutionStatusRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 工作流执行Id
+     *
+     * Generated from protobuf field <code>string RunId = 1;</code>
+     */
+    protected $RunId = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $RunId
+     *           工作流执行Id
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 工作流执行Id
+     *
+     * Generated from protobuf field <code>string RunId = 1;</code>
+     * @return string
+     */
+    public function getRunId()
+    {
+        return $this->RunId;
+    }
+
+    /**
+     * 工作流执行Id
+     *
+     * Generated from protobuf field <code>string RunId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setRunId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->RunId = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListCdnAccessLogRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListCdnAccessLogRequest.php
new file mode 100755
index 0000000..7381282
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListCdnAccessLogRequest.php
@@ -0,0 +1,167 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodListCdnAccessLogRequest</code>
+ */
+class VodListCdnAccessLogRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 域名(当前仅支持设置一个域名) 
+     *
+     * Generated from protobuf field <code>string Domains = 1;</code>
+     */
+    protected $Domains = '';
+    /**
+     * 查询起始时间戳,单位:Unix秒级时间戳 
+     *
+     * Generated from protobuf field <code>int32 StartTimestamp = 2;</code>
+     */
+    protected $StartTimestamp = 0;
+    /**
+     * 查询结束时间戳,单位:Unix秒级时间戳 
+     *
+     * Generated from protobuf field <code>int32 EndTimestamp = 3;</code>
+     */
+    protected $EndTimestamp = 0;
+    /**
+     * 空间名称 
+     *
+     * Generated from protobuf field <code>string SpaceName = 4;</code>
+     */
+    protected $SpaceName = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Domains
+     *           域名(当前仅支持设置一个域名) 
+     *     @type int $StartTimestamp
+     *           查询起始时间戳,单位:Unix秒级时间戳 
+     *     @type int $EndTimestamp
+     *           查询结束时间戳,单位:Unix秒级时间戳 
+     *     @type string $SpaceName
+     *           空间名称 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 域名(当前仅支持设置一个域名) 
+     *
+     * Generated from protobuf field <code>string Domains = 1;</code>
+     * @return string
+     */
+    public function getDomains()
+    {
+        return $this->Domains;
+    }
+
+    /**
+     * 域名(当前仅支持设置一个域名) 
+     *
+     * Generated from protobuf field <code>string Domains = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomains($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Domains = $var;
+
+        return $this;
+    }
+
+    /**
+     * 查询起始时间戳,单位:Unix秒级时间戳 
+     *
+     * Generated from protobuf field <code>int32 StartTimestamp = 2;</code>
+     * @return int
+     */
+    public function getStartTimestamp()
+    {
+        return $this->StartTimestamp;
+    }
+
+    /**
+     * 查询起始时间戳,单位:Unix秒级时间戳 
+     *
+     * Generated from protobuf field <code>int32 StartTimestamp = 2;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setStartTimestamp($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->StartTimestamp = $var;
+
+        return $this;
+    }
+
+    /**
+     * 查询结束时间戳,单位:Unix秒级时间戳 
+     *
+     * Generated from protobuf field <code>int32 EndTimestamp = 3;</code>
+     * @return int
+     */
+    public function getEndTimestamp()
+    {
+        return $this->EndTimestamp;
+    }
+
+    /**
+     * 查询结束时间戳,单位:Unix秒级时间戳 
+     *
+     * Generated from protobuf field <code>int32 EndTimestamp = 3;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setEndTimestamp($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->EndTimestamp = $var;
+
+        return $this;
+    }
+
+    /**
+     * 空间名称 
+     *
+     * Generated from protobuf field <code>string SpaceName = 4;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * 空间名称 
+     *
+     * Generated from protobuf field <code>string SpaceName = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListCdnPvDataRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListCdnPvDataRequest.php
new file mode 100755
index 0000000..d07d168
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListCdnPvDataRequest.php
@@ -0,0 +1,201 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodListCdnPvDataRequest</code>
+ */
+class VodListCdnPvDataRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 域名,多个用逗号隔开 
+     *
+     * Generated from protobuf field <code>string Domains = 1;</code>
+     */
+    protected $Domains = '';
+    /**
+     * 聚合度,可选:auto,5min,hour,day 
+     *
+     * Generated from protobuf field <code>string Interval = 2;</code>
+     */
+    protected $Interval = '';
+    /**
+     * 查询范围起始时间 
+     *
+     * Generated from protobuf field <code>int64 StartTimestamp = 3;</code>
+     */
+    protected $StartTimestamp = 0;
+    /**
+     * 查询范围截止时间 
+     *
+     * Generated from protobuf field <code>int64 EndTimestamp = 4;</code>
+     */
+    protected $EndTimestamp = 0;
+    /**
+     * 统计数据类别, 可选edge-边缘数据,origin-回源数据 
+     *
+     * Generated from protobuf field <code>string DataType = 5;</code>
+     */
+    protected $DataType = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Domains
+     *           域名,多个用逗号隔开 
+     *     @type string $Interval
+     *           聚合度,可选:auto,5min,hour,day 
+     *     @type int|string $StartTimestamp
+     *           查询范围起始时间 
+     *     @type int|string $EndTimestamp
+     *           查询范围截止时间 
+     *     @type string $DataType
+     *           统计数据类别, 可选edge-边缘数据,origin-回源数据 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 域名,多个用逗号隔开 
+     *
+     * Generated from protobuf field <code>string Domains = 1;</code>
+     * @return string
+     */
+    public function getDomains()
+    {
+        return $this->Domains;
+    }
+
+    /**
+     * 域名,多个用逗号隔开 
+     *
+     * Generated from protobuf field <code>string Domains = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomains($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Domains = $var;
+
+        return $this;
+    }
+
+    /**
+     * 聚合度,可选:auto,5min,hour,day 
+     *
+     * Generated from protobuf field <code>string Interval = 2;</code>
+     * @return string
+     */
+    public function getInterval()
+    {
+        return $this->Interval;
+    }
+
+    /**
+     * 聚合度,可选:auto,5min,hour,day 
+     *
+     * Generated from protobuf field <code>string Interval = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setInterval($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Interval = $var;
+
+        return $this;
+    }
+
+    /**
+     * 查询范围起始时间 
+     *
+     * Generated from protobuf field <code>int64 StartTimestamp = 3;</code>
+     * @return int|string
+     */
+    public function getStartTimestamp()
+    {
+        return $this->StartTimestamp;
+    }
+
+    /**
+     * 查询范围起始时间 
+     *
+     * Generated from protobuf field <code>int64 StartTimestamp = 3;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setStartTimestamp($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->StartTimestamp = $var;
+
+        return $this;
+    }
+
+    /**
+     * 查询范围截止时间 
+     *
+     * Generated from protobuf field <code>int64 EndTimestamp = 4;</code>
+     * @return int|string
+     */
+    public function getEndTimestamp()
+    {
+        return $this->EndTimestamp;
+    }
+
+    /**
+     * 查询范围截止时间 
+     *
+     * Generated from protobuf field <code>int64 EndTimestamp = 4;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setEndTimestamp($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->EndTimestamp = $var;
+
+        return $this;
+    }
+
+    /**
+     * 统计数据类别, 可选edge-边缘数据,origin-回源数据 
+     *
+     * Generated from protobuf field <code>string DataType = 5;</code>
+     * @return string
+     */
+    public function getDataType()
+    {
+        return $this->DataType;
+    }
+
+    /**
+     * 统计数据类别, 可选edge-边缘数据,origin-回源数据 
+     *
+     * Generated from protobuf field <code>string DataType = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDataType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->DataType = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListCdnStatusDataRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListCdnStatusDataRequest.php
new file mode 100755
index 0000000..a454a32
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListCdnStatusDataRequest.php
@@ -0,0 +1,235 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodListCdnStatusDataRequest</code>
+ */
+class VodListCdnStatusDataRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 域名,多个用逗号隔开 
+     *
+     * Generated from protobuf field <code>string Domains = 1;</code>
+     */
+    protected $Domains = '';
+    /**
+     * 聚合度 
+     *
+     * Generated from protobuf field <code>string Interval = 2;</code>
+     */
+    protected $Interval = '';
+    /**
+     * 查询范围起始时间 
+     *
+     * Generated from protobuf field <code>int64 StartTimestamp = 3;</code>
+     */
+    protected $StartTimestamp = 0;
+    /**
+     * 查询范围截止时间 
+     *
+     * Generated from protobuf field <code>int64 EndTimestamp = 4;</code>
+     */
+    protected $EndTimestamp = 0;
+    /**
+     * 统计数据类别 
+     *
+     * Generated from protobuf field <code>string DataType = 5;</code>
+     */
+    protected $DataType = '';
+    /**
+     * 指标类别 
+     *
+     * Generated from protobuf field <code>string Metric = 6;</code>
+     */
+    protected $Metric = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Domains
+     *           域名,多个用逗号隔开 
+     *     @type string $Interval
+     *           聚合度 
+     *     @type int|string $StartTimestamp
+     *           查询范围起始时间 
+     *     @type int|string $EndTimestamp
+     *           查询范围截止时间 
+     *     @type string $DataType
+     *           统计数据类别 
+     *     @type string $Metric
+     *           指标类别 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 域名,多个用逗号隔开 
+     *
+     * Generated from protobuf field <code>string Domains = 1;</code>
+     * @return string
+     */
+    public function getDomains()
+    {
+        return $this->Domains;
+    }
+
+    /**
+     * 域名,多个用逗号隔开 
+     *
+     * Generated from protobuf field <code>string Domains = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomains($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Domains = $var;
+
+        return $this;
+    }
+
+    /**
+     * 聚合度 
+     *
+     * Generated from protobuf field <code>string Interval = 2;</code>
+     * @return string
+     */
+    public function getInterval()
+    {
+        return $this->Interval;
+    }
+
+    /**
+     * 聚合度 
+     *
+     * Generated from protobuf field <code>string Interval = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setInterval($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Interval = $var;
+
+        return $this;
+    }
+
+    /**
+     * 查询范围起始时间 
+     *
+     * Generated from protobuf field <code>int64 StartTimestamp = 3;</code>
+     * @return int|string
+     */
+    public function getStartTimestamp()
+    {
+        return $this->StartTimestamp;
+    }
+
+    /**
+     * 查询范围起始时间 
+     *
+     * Generated from protobuf field <code>int64 StartTimestamp = 3;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setStartTimestamp($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->StartTimestamp = $var;
+
+        return $this;
+    }
+
+    /**
+     * 查询范围截止时间 
+     *
+     * Generated from protobuf field <code>int64 EndTimestamp = 4;</code>
+     * @return int|string
+     */
+    public function getEndTimestamp()
+    {
+        return $this->EndTimestamp;
+    }
+
+    /**
+     * 查询范围截止时间 
+     *
+     * Generated from protobuf field <code>int64 EndTimestamp = 4;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setEndTimestamp($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->EndTimestamp = $var;
+
+        return $this;
+    }
+
+    /**
+     * 统计数据类别 
+     *
+     * Generated from protobuf field <code>string DataType = 5;</code>
+     * @return string
+     */
+    public function getDataType()
+    {
+        return $this->DataType;
+    }
+
+    /**
+     * 统计数据类别 
+     *
+     * Generated from protobuf field <code>string DataType = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDataType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->DataType = $var;
+
+        return $this;
+    }
+
+    /**
+     * 指标类别 
+     *
+     * Generated from protobuf field <code>string Metric = 6;</code>
+     * @return string
+     */
+    public function getMetric()
+    {
+        return $this->Metric;
+    }
+
+    /**
+     * 指标类别 
+     *
+     * Generated from protobuf field <code>string Metric = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setMetric($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Metric = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListCdnTasksRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListCdnTasksRequest.php
new file mode 100755
index 0000000..0f14fa9
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListCdnTasksRequest.php
@@ -0,0 +1,337 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodListCdnTasksRequest</code>
+ */
+class VodListCdnTasksRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     * 任务ID 
+     *
+     * Generated from protobuf field <code>string TaskId = 2;</code>
+     */
+    protected $TaskId = '';
+    /**
+     * 域名 
+     *
+     * Generated from protobuf field <code>string DomainName = 3;</code>
+     */
+    protected $DomainName = '';
+    /**
+     * 任务类型 
+     *
+     * Generated from protobuf field <code>string TaskType = 4;</code>
+     */
+    protected $TaskType = '';
+    /**
+     * 任务状态 
+     *
+     * Generated from protobuf field <code>string Status = 5;</code>
+     */
+    protected $Status = '';
+    /**
+     * 起始时间戳,单位:Unix秒级时间戳 
+     *
+     * Generated from protobuf field <code>int32 StartTimestamp = 6;</code>
+     */
+    protected $StartTimestamp = 0;
+    /**
+     * 截止时间戳,单位:Unix秒级时间戳 
+     *
+     * Generated from protobuf field <code>int32 EndTimestamp = 7;</code>
+     */
+    protected $EndTimestamp = 0;
+    /**
+     * 页码 
+     *
+     * Generated from protobuf field <code>int32 PageNum = 8;</code>
+     */
+    protected $PageNum = 0;
+    /**
+     * 页面大小 
+     *
+     * Generated from protobuf field <code>int32 PageSize = 9;</code>
+     */
+    protected $PageSize = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *           空间名 
+     *     @type string $TaskId
+     *           任务ID 
+     *     @type string $DomainName
+     *           域名 
+     *     @type string $TaskType
+     *           任务类型 
+     *     @type string $Status
+     *           任务状态 
+     *     @type int $StartTimestamp
+     *           起始时间戳,单位:Unix秒级时间戳 
+     *     @type int $EndTimestamp
+     *           截止时间戳,单位:Unix秒级时间戳 
+     *     @type int $PageNum
+     *           页码 
+     *     @type int $PageSize
+     *           页面大小 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 任务ID 
+     *
+     * Generated from protobuf field <code>string TaskId = 2;</code>
+     * @return string
+     */
+    public function getTaskId()
+    {
+        return $this->TaskId;
+    }
+
+    /**
+     * 任务ID 
+     *
+     * Generated from protobuf field <code>string TaskId = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTaskId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TaskId = $var;
+
+        return $this;
+    }
+
+    /**
+     * 域名 
+     *
+     * Generated from protobuf field <code>string DomainName = 3;</code>
+     * @return string
+     */
+    public function getDomainName()
+    {
+        return $this->DomainName;
+    }
+
+    /**
+     * 域名 
+     *
+     * Generated from protobuf field <code>string DomainName = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomainName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->DomainName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 任务类型 
+     *
+     * Generated from protobuf field <code>string TaskType = 4;</code>
+     * @return string
+     */
+    public function getTaskType()
+    {
+        return $this->TaskType;
+    }
+
+    /**
+     * 任务类型 
+     *
+     * Generated from protobuf field <code>string TaskType = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTaskType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TaskType = $var;
+
+        return $this;
+    }
+
+    /**
+     * 任务状态 
+     *
+     * Generated from protobuf field <code>string Status = 5;</code>
+     * @return string
+     */
+    public function getStatus()
+    {
+        return $this->Status;
+    }
+
+    /**
+     * 任务状态 
+     *
+     * Generated from protobuf field <code>string Status = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStatus($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Status = $var;
+
+        return $this;
+    }
+
+    /**
+     * 起始时间戳,单位:Unix秒级时间戳 
+     *
+     * Generated from protobuf field <code>int32 StartTimestamp = 6;</code>
+     * @return int
+     */
+    public function getStartTimestamp()
+    {
+        return $this->StartTimestamp;
+    }
+
+    /**
+     * 起始时间戳,单位:Unix秒级时间戳 
+     *
+     * Generated from protobuf field <code>int32 StartTimestamp = 6;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setStartTimestamp($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->StartTimestamp = $var;
+
+        return $this;
+    }
+
+    /**
+     * 截止时间戳,单位:Unix秒级时间戳 
+     *
+     * Generated from protobuf field <code>int32 EndTimestamp = 7;</code>
+     * @return int
+     */
+    public function getEndTimestamp()
+    {
+        return $this->EndTimestamp;
+    }
+
+    /**
+     * 截止时间戳,单位:Unix秒级时间戳 
+     *
+     * Generated from protobuf field <code>int32 EndTimestamp = 7;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setEndTimestamp($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->EndTimestamp = $var;
+
+        return $this;
+    }
+
+    /**
+     * 页码 
+     *
+     * Generated from protobuf field <code>int32 PageNum = 8;</code>
+     * @return int
+     */
+    public function getPageNum()
+    {
+        return $this->PageNum;
+    }
+
+    /**
+     * 页码 
+     *
+     * Generated from protobuf field <code>int32 PageNum = 8;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setPageNum($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->PageNum = $var;
+
+        return $this;
+    }
+
+    /**
+     * 页面大小 
+     *
+     * Generated from protobuf field <code>int32 PageSize = 9;</code>
+     * @return int
+     */
+    public function getPageSize()
+    {
+        return $this->PageSize;
+    }
+
+    /**
+     * 页面大小 
+     *
+     * Generated from protobuf field <code>int32 PageSize = 9;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setPageSize($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->PageSize = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListCdnTopAccessUrlRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListCdnTopAccessUrlRequest.php
new file mode 100755
index 0000000..377a146
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListCdnTopAccessUrlRequest.php
@@ -0,0 +1,167 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodListCdnTopAccessUrlRequest</code>
+ */
+class VodListCdnTopAccessUrlRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 域名(当前仅支持一个域名) 
+     *
+     * Generated from protobuf field <code>string Domains = 1;</code>
+     */
+    protected $Domains = '';
+    /**
+     * 查询起始时间戳,单位:Unix秒级时间戳 
+     *
+     * Generated from protobuf field <code>int32 StartTimestamp = 2;</code>
+     */
+    protected $StartTimestamp = 0;
+    /**
+     * 查询结束时间戳,单位:Unix秒级时间戳 
+     *
+     * Generated from protobuf field <code>int32 EndTimestamp = 3;</code>
+     */
+    protected $EndTimestamp = 0;
+    /**
+     * 排序方式,默认值为pv。取值:flux - 流量, pv -访问量 
+     *
+     * Generated from protobuf field <code>string SortType = 4;</code>
+     */
+    protected $SortType = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Domains
+     *           域名(当前仅支持一个域名) 
+     *     @type int $StartTimestamp
+     *           查询起始时间戳,单位:Unix秒级时间戳 
+     *     @type int $EndTimestamp
+     *           查询结束时间戳,单位:Unix秒级时间戳 
+     *     @type string $SortType
+     *           排序方式,默认值为pv。取值:flux - 流量, pv -访问量 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 域名(当前仅支持一个域名) 
+     *
+     * Generated from protobuf field <code>string Domains = 1;</code>
+     * @return string
+     */
+    public function getDomains()
+    {
+        return $this->Domains;
+    }
+
+    /**
+     * 域名(当前仅支持一个域名) 
+     *
+     * Generated from protobuf field <code>string Domains = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomains($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Domains = $var;
+
+        return $this;
+    }
+
+    /**
+     * 查询起始时间戳,单位:Unix秒级时间戳 
+     *
+     * Generated from protobuf field <code>int32 StartTimestamp = 2;</code>
+     * @return int
+     */
+    public function getStartTimestamp()
+    {
+        return $this->StartTimestamp;
+    }
+
+    /**
+     * 查询起始时间戳,单位:Unix秒级时间戳 
+     *
+     * Generated from protobuf field <code>int32 StartTimestamp = 2;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setStartTimestamp($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->StartTimestamp = $var;
+
+        return $this;
+    }
+
+    /**
+     * 查询结束时间戳,单位:Unix秒级时间戳 
+     *
+     * Generated from protobuf field <code>int32 EndTimestamp = 3;</code>
+     * @return int
+     */
+    public function getEndTimestamp()
+    {
+        return $this->EndTimestamp;
+    }
+
+    /**
+     * 查询结束时间戳,单位:Unix秒级时间戳 
+     *
+     * Generated from protobuf field <code>int32 EndTimestamp = 3;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setEndTimestamp($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->EndTimestamp = $var;
+
+        return $this;
+    }
+
+    /**
+     * 排序方式,默认值为pv。取值:flux - 流量, pv -访问量 
+     *
+     * Generated from protobuf field <code>string SortType = 4;</code>
+     * @return string
+     */
+    public function getSortType()
+    {
+        return $this->SortType;
+    }
+
+    /**
+     * 排序方式,默认值为pv。取值:flux - 流量, pv -访问量 
+     *
+     * Generated from protobuf field <code>string SortType = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSortType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SortType = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListCdnUsageDataRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListCdnUsageDataRequest.php
new file mode 100755
index 0000000..b692997
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListCdnUsageDataRequest.php
@@ -0,0 +1,235 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodListCdnUsageDataRequest</code>
+ */
+class VodListCdnUsageDataRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 域名,多个用逗号隔开 
+     *
+     * Generated from protobuf field <code>string Domains = 1;</code>
+     */
+    protected $Domains = '';
+    /**
+     * 聚合度 
+     *
+     * Generated from protobuf field <code>string Interval = 2;</code>
+     */
+    protected $Interval = '';
+    /**
+     * 查询范围起始时间 
+     *
+     * Generated from protobuf field <code>int64 StartTimestamp = 3;</code>
+     */
+    protected $StartTimestamp = 0;
+    /**
+     * 查询范围截止时间 
+     *
+     * Generated from protobuf field <code>int64 EndTimestamp = 4;</code>
+     */
+    protected $EndTimestamp = 0;
+    /**
+     * 统计数据类别 
+     *
+     * Generated from protobuf field <code>string DataType = 5;</code>
+     */
+    protected $DataType = '';
+    /**
+     * 指标类别 
+     *
+     * Generated from protobuf field <code>string Metric = 6;</code>
+     */
+    protected $Metric = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Domains
+     *           域名,多个用逗号隔开 
+     *     @type string $Interval
+     *           聚合度 
+     *     @type int|string $StartTimestamp
+     *           查询范围起始时间 
+     *     @type int|string $EndTimestamp
+     *           查询范围截止时间 
+     *     @type string $DataType
+     *           统计数据类别 
+     *     @type string $Metric
+     *           指标类别 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 域名,多个用逗号隔开 
+     *
+     * Generated from protobuf field <code>string Domains = 1;</code>
+     * @return string
+     */
+    public function getDomains()
+    {
+        return $this->Domains;
+    }
+
+    /**
+     * 域名,多个用逗号隔开 
+     *
+     * Generated from protobuf field <code>string Domains = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDomains($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Domains = $var;
+
+        return $this;
+    }
+
+    /**
+     * 聚合度 
+     *
+     * Generated from protobuf field <code>string Interval = 2;</code>
+     * @return string
+     */
+    public function getInterval()
+    {
+        return $this->Interval;
+    }
+
+    /**
+     * 聚合度 
+     *
+     * Generated from protobuf field <code>string Interval = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setInterval($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Interval = $var;
+
+        return $this;
+    }
+
+    /**
+     * 查询范围起始时间 
+     *
+     * Generated from protobuf field <code>int64 StartTimestamp = 3;</code>
+     * @return int|string
+     */
+    public function getStartTimestamp()
+    {
+        return $this->StartTimestamp;
+    }
+
+    /**
+     * 查询范围起始时间 
+     *
+     * Generated from protobuf field <code>int64 StartTimestamp = 3;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setStartTimestamp($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->StartTimestamp = $var;
+
+        return $this;
+    }
+
+    /**
+     * 查询范围截止时间 
+     *
+     * Generated from protobuf field <code>int64 EndTimestamp = 4;</code>
+     * @return int|string
+     */
+    public function getEndTimestamp()
+    {
+        return $this->EndTimestamp;
+    }
+
+    /**
+     * 查询范围截止时间 
+     *
+     * Generated from protobuf field <code>int64 EndTimestamp = 4;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setEndTimestamp($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->EndTimestamp = $var;
+
+        return $this;
+    }
+
+    /**
+     * 统计数据类别 
+     *
+     * Generated from protobuf field <code>string DataType = 5;</code>
+     * @return string
+     */
+    public function getDataType()
+    {
+        return $this->DataType;
+    }
+
+    /**
+     * 统计数据类别 
+     *
+     * Generated from protobuf field <code>string DataType = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDataType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->DataType = $var;
+
+        return $this;
+    }
+
+    /**
+     * 指标类别 
+     *
+     * Generated from protobuf field <code>string Metric = 6;</code>
+     * @return string
+     */
+    public function getMetric()
+    {
+        return $this->Metric;
+    }
+
+    /**
+     * 指标类别 
+     *
+     * Generated from protobuf field <code>string Metric = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setMetric($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Metric = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListDomainRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListDomainRequest.php
new file mode 100755
index 0000000..510bcd4
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListDomainRequest.php
@@ -0,0 +1,69 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * **********************************************************************
+ * 分发加速管理
+ * **********************************************************************
+ *
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodListDomainRequest</code>
+ */
+class VodListDomainRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *           空间名 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListSnapshotsRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListSnapshotsRequest.php
new file mode 100755
index 0000000..738a368
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListSnapshotsRequest.php
@@ -0,0 +1,58 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodListSnapshotsRequest</code>
+ */
+class VodListSnapshotsRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     */
+    protected $Vid = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vid
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListSpaceRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListSpaceRequest.php
new file mode 100755
index 0000000..248eac1
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListSpaceRequest.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodListSpaceRequest</code>
+ */
+class VodListSpaceRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 查询游标 
+     *
+     * Generated from protobuf field <code>double Offset = 1;</code>
+     */
+    protected $Offset = 0.0;
+    /**
+     * 查询数量 
+     *
+     * Generated from protobuf field <code>double Limit = 2;</code>
+     */
+    protected $Limit = 0.0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type float $Offset
+     *           查询游标 
+     *     @type float $Limit
+     *           查询数量 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 查询游标 
+     *
+     * Generated from protobuf field <code>double Offset = 1;</code>
+     * @return float
+     */
+    public function getOffset()
+    {
+        return $this->Offset;
+    }
+
+    /**
+     * 查询游标 
+     *
+     * Generated from protobuf field <code>double Offset = 1;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setOffset($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->Offset = $var;
+
+        return $this;
+    }
+
+    /**
+     * 查询数量 
+     *
+     * Generated from protobuf field <code>double Limit = 2;</code>
+     * @return float
+     */
+    public function getLimit()
+    {
+        return $this->Limit;
+    }
+
+    /**
+     * 查询数量 
+     *
+     * Generated from protobuf field <code>double Limit = 2;</code>
+     * @param float $var
+     * @return $this
+     */
+    public function setLimit($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->Limit = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListVideoClassificationsRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListVideoClassificationsRequest.php
new file mode 100755
index 0000000..9550de5
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListVideoClassificationsRequest.php
@@ -0,0 +1,85 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodListVideoClassificationsRequest</code>
+ */
+class VodListVideoClassificationsRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     * Generated from protobuf field <code>int64 ClassificationId = 2;</code>
+     */
+    protected $ClassificationId = 0;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *     @type int|string $ClassificationId
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>int64 ClassificationId = 2;</code>
+     * @return int|string
+     */
+    public function getClassificationId()
+    {
+        return $this->ClassificationId;
+    }
+
+    /**
+     * Generated from protobuf field <code>int64 ClassificationId = 2;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setClassificationId($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->ClassificationId = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListWorkflowExecutionRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListWorkflowExecutionRequest.php
new file mode 100755
index 0000000..3c29d7b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodListWorkflowExecutionRequest.php
@@ -0,0 +1,507 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodListWorkflowExecutionRequest</code>
+ */
+class VodListWorkflowExecutionRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 工作流执行Id
+     *
+     * Generated from protobuf field <code>string RunId = 1;</code>
+     */
+    protected $RunId = '';
+    /**
+     * 视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 2;</code>
+     */
+    protected $Vid = '';
+    /**
+     * 空间名
+     *
+     * Generated from protobuf field <code>string SpaceName = 3;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     *模板ID,即工作流ID
+     *
+     * Generated from protobuf field <code>string TemplateId = 4;</code>
+     */
+    protected $TemplateId = '';
+    /**
+     *任务队列ID
+     *
+     * Generated from protobuf field <code>string TaskListId = 5;</code>
+     */
+    protected $TaskListId = '';
+    /**
+     *闲时转码
+     *
+     * Generated from protobuf field <code>string EnableLowPriority = 6;</code>
+     */
+    protected $EnableLowPriority = '';
+    /**
+     *任务来源,逗号分隔,例如:API,TranscodeStrategy
+     *
+     * Generated from protobuf field <code>string JobSource = 7;</code>
+     */
+    protected $JobSource = '';
+    /**
+     *执行状态,逗号分隔,例如:0,1001
+     *
+     * Generated from protobuf field <code>string Status = 8;</code>
+     */
+    protected $Status = '';
+    /**
+     *创建时间的开始时间格式:RFC3339,例如:2019-01-23T12:35:12Z,2019-01-23T12:40:12Z
+     *
+     * Generated from protobuf field <code>string StartTime = 9;</code>
+     */
+    protected $StartTime = '';
+    /**
+     *创建时间的结束时间,格式按照 rfc3339标准表示,格式为
+     *
+     * Generated from protobuf field <code>string EndTime = 10;</code>
+     */
+    protected $EndTime = '';
+    /**
+     * yyyy-MM-ddTHH:mm:ssZ
+     *
+     * Generated from protobuf field <code>string PageSize = 11;</code>
+     */
+    protected $PageSize = '';
+    /**
+     *用于分页
+     *
+     * Generated from protobuf field <code>string Offset = 12;</code>
+     */
+    protected $Offset = '';
+    /**
+     *结果列表排序依赖的键,支持按时间字段created_at或updated_at排序,默认按created_at排序
+     *
+     * Generated from protobuf field <code>string OrderByKey = 13;</code>
+     */
+    protected $OrderByKey = '';
+    /**
+     *排序的升降属性,默认desc
+     *
+     * Generated from protobuf field <code>string Order = 14;</code>
+     */
+    protected $Order = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $RunId
+     *           工作流执行Id
+     *     @type string $Vid
+     *           视频ID
+     *     @type string $SpaceName
+     *           空间名
+     *     @type string $TemplateId
+     *          模板ID,即工作流ID
+     *     @type string $TaskListId
+     *          任务队列ID
+     *     @type string $EnableLowPriority
+     *          闲时转码
+     *     @type string $JobSource
+     *          任务来源,逗号分隔,例如:API,TranscodeStrategy
+     *     @type string $Status
+     *          执行状态,逗号分隔,例如:0,1001
+     *     @type string $StartTime
+     *          创建时间的开始时间格式:RFC3339,例如:2019-01-23T12:35:12Z,2019-01-23T12:40:12Z
+     *     @type string $EndTime
+     *          创建时间的结束时间,格式按照 rfc3339标准表示,格式为
+     *     @type string $PageSize
+     *           yyyy-MM-ddTHH:mm:ssZ
+     *     @type string $Offset
+     *          用于分页
+     *     @type string $OrderByKey
+     *          结果列表排序依赖的键,支持按时间字段created_at或updated_at排序,默认按created_at排序
+     *     @type string $Order
+     *          排序的升降属性,默认desc
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 工作流执行Id
+     *
+     * Generated from protobuf field <code>string RunId = 1;</code>
+     * @return string
+     */
+    public function getRunId()
+    {
+        return $this->RunId;
+    }
+
+    /**
+     * 工作流执行Id
+     *
+     * Generated from protobuf field <code>string RunId = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setRunId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->RunId = $var;
+
+        return $this;
+    }
+
+    /**
+     * 视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 2;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     * 视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     * 空间名
+     *
+     * Generated from protobuf field <code>string SpaceName = 3;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * 空间名
+     *
+     * Generated from protobuf field <code>string SpaceName = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     *模板ID,即工作流ID
+     *
+     * Generated from protobuf field <code>string TemplateId = 4;</code>
+     * @return string
+     */
+    public function getTemplateId()
+    {
+        return $this->TemplateId;
+    }
+
+    /**
+     *模板ID,即工作流ID
+     *
+     * Generated from protobuf field <code>string TemplateId = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTemplateId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TemplateId = $var;
+
+        return $this;
+    }
+
+    /**
+     *任务队列ID
+     *
+     * Generated from protobuf field <code>string TaskListId = 5;</code>
+     * @return string
+     */
+    public function getTaskListId()
+    {
+        return $this->TaskListId;
+    }
+
+    /**
+     *任务队列ID
+     *
+     * Generated from protobuf field <code>string TaskListId = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTaskListId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TaskListId = $var;
+
+        return $this;
+    }
+
+    /**
+     *闲时转码
+     *
+     * Generated from protobuf field <code>string EnableLowPriority = 6;</code>
+     * @return string
+     */
+    public function getEnableLowPriority()
+    {
+        return $this->EnableLowPriority;
+    }
+
+    /**
+     *闲时转码
+     *
+     * Generated from protobuf field <code>string EnableLowPriority = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setEnableLowPriority($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->EnableLowPriority = $var;
+
+        return $this;
+    }
+
+    /**
+     *任务来源,逗号分隔,例如:API,TranscodeStrategy
+     *
+     * Generated from protobuf field <code>string JobSource = 7;</code>
+     * @return string
+     */
+    public function getJobSource()
+    {
+        return $this->JobSource;
+    }
+
+    /**
+     *任务来源,逗号分隔,例如:API,TranscodeStrategy
+     *
+     * Generated from protobuf field <code>string JobSource = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setJobSource($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->JobSource = $var;
+
+        return $this;
+    }
+
+    /**
+     *执行状态,逗号分隔,例如:0,1001
+     *
+     * Generated from protobuf field <code>string Status = 8;</code>
+     * @return string
+     */
+    public function getStatus()
+    {
+        return $this->Status;
+    }
+
+    /**
+     *执行状态,逗号分隔,例如:0,1001
+     *
+     * Generated from protobuf field <code>string Status = 8;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStatus($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Status = $var;
+
+        return $this;
+    }
+
+    /**
+     *创建时间的开始时间格式:RFC3339,例如:2019-01-23T12:35:12Z,2019-01-23T12:40:12Z
+     *
+     * Generated from protobuf field <code>string StartTime = 9;</code>
+     * @return string
+     */
+    public function getStartTime()
+    {
+        return $this->StartTime;
+    }
+
+    /**
+     *创建时间的开始时间格式:RFC3339,例如:2019-01-23T12:35:12Z,2019-01-23T12:40:12Z
+     *
+     * Generated from protobuf field <code>string StartTime = 9;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStartTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->StartTime = $var;
+
+        return $this;
+    }
+
+    /**
+     *创建时间的结束时间,格式按照 rfc3339标准表示,格式为
+     *
+     * Generated from protobuf field <code>string EndTime = 10;</code>
+     * @return string
+     */
+    public function getEndTime()
+    {
+        return $this->EndTime;
+    }
+
+    /**
+     *创建时间的结束时间,格式按照 rfc3339标准表示,格式为
+     *
+     * Generated from protobuf field <code>string EndTime = 10;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setEndTime($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->EndTime = $var;
+
+        return $this;
+    }
+
+    /**
+     * yyyy-MM-ddTHH:mm:ssZ
+     *
+     * Generated from protobuf field <code>string PageSize = 11;</code>
+     * @return string
+     */
+    public function getPageSize()
+    {
+        return $this->PageSize;
+    }
+
+    /**
+     * yyyy-MM-ddTHH:mm:ssZ
+     *
+     * Generated from protobuf field <code>string PageSize = 11;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setPageSize($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->PageSize = $var;
+
+        return $this;
+    }
+
+    /**
+     *用于分页
+     *
+     * Generated from protobuf field <code>string Offset = 12;</code>
+     * @return string
+     */
+    public function getOffset()
+    {
+        return $this->Offset;
+    }
+
+    /**
+     *用于分页
+     *
+     * Generated from protobuf field <code>string Offset = 12;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setOffset($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Offset = $var;
+
+        return $this;
+    }
+
+    /**
+     *结果列表排序依赖的键,支持按时间字段created_at或updated_at排序,默认按created_at排序
+     *
+     * Generated from protobuf field <code>string OrderByKey = 13;</code>
+     * @return string
+     */
+    public function getOrderByKey()
+    {
+        return $this->OrderByKey;
+    }
+
+    /**
+     *结果列表排序依赖的键,支持按时间字段created_at或updated_at排序,默认按created_at排序
+     *
+     * Generated from protobuf field <code>string OrderByKey = 13;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setOrderByKey($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->OrderByKey = $var;
+
+        return $this;
+    }
+
+    /**
+     *排序的升降属性,默认desc
+     *
+     * Generated from protobuf field <code>string Order = 14;</code>
+     * @return string
+     */
+    public function getOrder()
+    {
+        return $this->Order;
+    }
+
+    /**
+     *排序的升降属性,默认desc
+     *
+     * Generated from protobuf field <code>string Order = 14;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setOrder($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Order = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodQueryUploadTaskInfoRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodQueryUploadTaskInfoRequest.php
new file mode 100755
index 0000000..9cb86ad
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodQueryUploadTaskInfoRequest.php
@@ -0,0 +1,65 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodQueryUploadTaskInfoRequest</code>
+ */
+class VodQueryUploadTaskInfoRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 任务ID列表,通过任务ID可以查询任务的状态 
+     *
+     * Generated from protobuf field <code>string JobIds = 1;</code>
+     */
+    protected $JobIds = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $JobIds
+     *           任务ID列表,通过任务ID可以查询任务的状态 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 任务ID列表,通过任务ID可以查询任务的状态 
+     *
+     * Generated from protobuf field <code>string JobIds = 1;</code>
+     * @return string
+     */
+    public function getJobIds()
+    {
+        return $this->JobIds;
+    }
+
+    /**
+     * 任务ID列表,通过任务ID可以查询任务的状态 
+     *
+     * Generated from protobuf field <code>string JobIds = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setJobIds($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->JobIds = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodRetrieveTranscodeResultRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodRetrieveTranscodeResultRequest.php
new file mode 100755
index 0000000..3315190
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodRetrieveTranscodeResultRequest.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodRetrieveTranscodeResultRequest</code>
+ */
+class VodRetrieveTranscodeResultRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 视频Id
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     */
+    protected $Vid = '';
+    /**
+     * 预期获取的结果类型,可选:inspection、category_tags,支持多选,用,分隔,例如inspection,category_tags
+     *
+     * Generated from protobuf field <code>string ResultType = 2;</code>
+     */
+    protected $ResultType = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vid
+     *           视频Id
+     *     @type string $ResultType
+     *           预期获取的结果类型,可选:inspection、category_tags,支持多选,用,分隔,例如inspection,category_tags
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 视频Id
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     * 视频Id
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     * 预期获取的结果类型,可选:inspection、category_tags,支持多选,用,分隔,例如inspection,category_tags
+     *
+     * Generated from protobuf field <code>string ResultType = 2;</code>
+     * @return string
+     */
+    public function getResultType()
+    {
+        return $this->ResultType;
+    }
+
+    /**
+     * 预期获取的结果类型,可选:inspection、category_tags,支持多选,用,分隔,例如inspection,category_tags
+     *
+     * Generated from protobuf field <code>string ResultType = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setResultType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->ResultType = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodSetCallbackEventRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodSetCallbackEventRequest.php
new file mode 100755
index 0000000..8fea620
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodSetCallbackEventRequest.php
@@ -0,0 +1,167 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodSetCallbackEventRequest</code>
+ */
+class VodSetCallbackEventRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     * 事件列表 
+     *
+     * Generated from protobuf field <code>string Events = 2;</code>
+     */
+    protected $Events = '';
+    /**
+     * 是否开启回调鉴权,值为VodCallbackAuthType中的枚举数字 
+     *
+     * Generated from protobuf field <code>string AuthEnabled = 3;</code>
+     */
+    protected $AuthEnabled = '';
+    /**
+     * 回调鉴权key 
+     *
+     * Generated from protobuf field <code>string PrivateKey = 4;</code>
+     */
+    protected $PrivateKey = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *           空间名 
+     *     @type string $Events
+     *           事件列表 
+     *     @type string $AuthEnabled
+     *           是否开启回调鉴权,值为VodCallbackAuthType中的枚举数字 
+     *     @type string $PrivateKey
+     *           回调鉴权key 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 事件列表 
+     *
+     * Generated from protobuf field <code>string Events = 2;</code>
+     * @return string
+     */
+    public function getEvents()
+    {
+        return $this->Events;
+    }
+
+    /**
+     * 事件列表 
+     *
+     * Generated from protobuf field <code>string Events = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setEvents($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Events = $var;
+
+        return $this;
+    }
+
+    /**
+     * 是否开启回调鉴权,值为VodCallbackAuthType中的枚举数字 
+     *
+     * Generated from protobuf field <code>string AuthEnabled = 3;</code>
+     * @return string
+     */
+    public function getAuthEnabled()
+    {
+        return $this->AuthEnabled;
+    }
+
+    /**
+     * 是否开启回调鉴权,值为VodCallbackAuthType中的枚举数字 
+     *
+     * Generated from protobuf field <code>string AuthEnabled = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setAuthEnabled($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->AuthEnabled = $var;
+
+        return $this;
+    }
+
+    /**
+     * 回调鉴权key 
+     *
+     * Generated from protobuf field <code>string PrivateKey = 4;</code>
+     * @return string
+     */
+    public function getPrivateKey()
+    {
+        return $this->PrivateKey;
+    }
+
+    /**
+     * 回调鉴权key 
+     *
+     * Generated from protobuf field <code>string PrivateKey = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setPrivateKey($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->PrivateKey = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodStartWorkflowRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodStartWorkflowRequest.php
new file mode 100755
index 0000000..66b5a0f
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodStartWorkflowRequest.php
@@ -0,0 +1,249 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * **********************************************************************
+ * 转码
+ * **********************************************************************
+ *
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodStartWorkflowRequest</code>
+ */
+class VodStartWorkflowRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 视频Id
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     */
+    protected $Vid = '';
+    /**
+     * 工作流模板Id
+     *
+     * Generated from protobuf field <code>string TemplateId = 2;</code>
+     */
+    protected $TemplateId = '';
+    /**
+     * 动态参数
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.WorkflowParams Input = 3;</code>
+     */
+    protected $Input = null;
+    /**
+     * 任务优先级
+     *
+     * Generated from protobuf field <code>int32 Priority = 4;</code>
+     */
+    protected $Priority = 0;
+    /**
+     * 回调参数
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 5;</code>
+     */
+    protected $CallbackArgs = '';
+    /**
+     * 是否使能闲时转码
+     *
+     * Generated from protobuf field <code>bool EnableLowPriority = 6;</code>
+     */
+    protected $EnableLowPriority = false;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vid
+     *           视频Id
+     *     @type string $TemplateId
+     *           工作流模板Id
+     *     @type \Volc\Service\Vod\Models\Business\WorkflowParams $Input
+     *           动态参数
+     *     @type int $Priority
+     *           任务优先级
+     *     @type string $CallbackArgs
+     *           回调参数
+     *     @type bool $EnableLowPriority
+     *           是否使能闲时转码
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 视频Id
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     * 视频Id
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     * 工作流模板Id
+     *
+     * Generated from protobuf field <code>string TemplateId = 2;</code>
+     * @return string
+     */
+    public function getTemplateId()
+    {
+        return $this->TemplateId;
+    }
+
+    /**
+     * 工作流模板Id
+     *
+     * Generated from protobuf field <code>string TemplateId = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTemplateId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TemplateId = $var;
+
+        return $this;
+    }
+
+    /**
+     * 动态参数
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.WorkflowParams Input = 3;</code>
+     * @return \Volc\Service\Vod\Models\Business\WorkflowParams|null
+     */
+    public function getInput()
+    {
+        return $this->Input;
+    }
+
+    public function hasInput()
+    {
+        return isset($this->Input);
+    }
+
+    public function clearInput()
+    {
+        unset($this->Input);
+    }
+
+    /**
+     * 动态参数
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.WorkflowParams Input = 3;</code>
+     * @param \Volc\Service\Vod\Models\Business\WorkflowParams $var
+     * @return $this
+     */
+    public function setInput($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\WorkflowParams::class);
+        $this->Input = $var;
+
+        return $this;
+    }
+
+    /**
+     * 任务优先级
+     *
+     * Generated from protobuf field <code>int32 Priority = 4;</code>
+     * @return int
+     */
+    public function getPriority()
+    {
+        return $this->Priority;
+    }
+
+    /**
+     * 任务优先级
+     *
+     * Generated from protobuf field <code>int32 Priority = 4;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setPriority($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Priority = $var;
+
+        return $this;
+    }
+
+    /**
+     * 回调参数
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 5;</code>
+     * @return string
+     */
+    public function getCallbackArgs()
+    {
+        return $this->CallbackArgs;
+    }
+
+    /**
+     * 回调参数
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCallbackArgs($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CallbackArgs = $var;
+
+        return $this;
+    }
+
+    /**
+     * 是否使能闲时转码
+     *
+     * Generated from protobuf field <code>bool EnableLowPriority = 6;</code>
+     * @return bool
+     */
+    public function getEnableLowPriority()
+    {
+        return $this->EnableLowPriority;
+    }
+
+    /**
+     * 是否使能闲时转码
+     *
+     * Generated from protobuf field <code>bool EnableLowPriority = 6;</code>
+     * @param bool $var
+     * @return $this
+     */
+    public function setEnableLowPriority($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->EnableLowPriority = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodSubmitDirectEditTaskAsyncRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodSubmitDirectEditTaskAsyncRequest.php
new file mode 100755
index 0000000..ff1ed60
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodSubmitDirectEditTaskAsyncRequest.php
@@ -0,0 +1,239 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * **********************************************************************
+ * 视频编辑
+ * **********************************************************************
+ *
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodSubmitDirectEditTaskAsyncRequest</code>
+ */
+class VodSubmitDirectEditTaskAsyncRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 产物上传空间
+     *
+     * Generated from protobuf field <code>string Uploader = 1;</code>
+     */
+    protected $Uploader = '';
+    /**
+     * Application名称
+     *
+     * Generated from protobuf field <code>string Application = 2;</code>
+     */
+    protected $Application = '';
+    /**
+     * 编辑参数
+     *
+     * Generated from protobuf field <code>bytes EditParam = 4;</code>
+     */
+    protected $EditParam = '';
+    /**
+     * 优先级
+     *
+     * Generated from protobuf field <code>int32 Priority = 5;</code>
+     */
+    protected $Priority = 0;
+    /**
+     * 回调地址
+     *
+     * Generated from protobuf field <code>string CallbackUri = 6;</code>
+     */
+    protected $CallbackUri = '';
+    /**
+     * 回调参数
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 7;</code>
+     */
+    protected $CallbackArgs = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Uploader
+     *           产物上传空间
+     *     @type string $Application
+     *           Application名称
+     *     @type string $EditParam
+     *           编辑参数
+     *     @type int $Priority
+     *           优先级
+     *     @type string $CallbackUri
+     *           回调地址
+     *     @type string $CallbackArgs
+     *           回调参数
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 产物上传空间
+     *
+     * Generated from protobuf field <code>string Uploader = 1;</code>
+     * @return string
+     */
+    public function getUploader()
+    {
+        return $this->Uploader;
+    }
+
+    /**
+     * 产物上传空间
+     *
+     * Generated from protobuf field <code>string Uploader = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setUploader($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Uploader = $var;
+
+        return $this;
+    }
+
+    /**
+     * Application名称
+     *
+     * Generated from protobuf field <code>string Application = 2;</code>
+     * @return string
+     */
+    public function getApplication()
+    {
+        return $this->Application;
+    }
+
+    /**
+     * Application名称
+     *
+     * Generated from protobuf field <code>string Application = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setApplication($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Application = $var;
+
+        return $this;
+    }
+
+    /**
+     * 编辑参数
+     *
+     * Generated from protobuf field <code>bytes EditParam = 4;</code>
+     * @return string
+     */
+    public function getEditParam()
+    {
+        return $this->EditParam;
+    }
+
+    /**
+     * 编辑参数
+     *
+     * Generated from protobuf field <code>bytes EditParam = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setEditParam($var)
+    {
+        GPBUtil::checkString($var, False);
+        $this->EditParam = $var;
+
+        return $this;
+    }
+
+    /**
+     * 优先级
+     *
+     * Generated from protobuf field <code>int32 Priority = 5;</code>
+     * @return int
+     */
+    public function getPriority()
+    {
+        return $this->Priority;
+    }
+
+    /**
+     * 优先级
+     *
+     * Generated from protobuf field <code>int32 Priority = 5;</code>
+     * @param int $var
+     * @return $this
+     */
+    public function setPriority($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->Priority = $var;
+
+        return $this;
+    }
+
+    /**
+     * 回调地址
+     *
+     * Generated from protobuf field <code>string CallbackUri = 6;</code>
+     * @return string
+     */
+    public function getCallbackUri()
+    {
+        return $this->CallbackUri;
+    }
+
+    /**
+     * 回调地址
+     *
+     * Generated from protobuf field <code>string CallbackUri = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCallbackUri($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CallbackUri = $var;
+
+        return $this;
+    }
+
+    /**
+     * 回调参数
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 7;</code>
+     * @return string
+     */
+    public function getCallbackArgs()
+    {
+        return $this->CallbackArgs;
+    }
+
+    /**
+     * 回调参数
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 7;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCallbackArgs($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CallbackArgs = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateMediaInfoRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateMediaInfoRequest.php
new file mode 100755
index 0000000..4537fdc
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateMediaInfoRequest.php
@@ -0,0 +1,420 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodUpdateMediaInfoRequest</code>
+ */
+class VodUpdateMediaInfoRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     */
+    protected $Vid = '';
+    /**
+     *视频封面Uri
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue PosterUri = 2;</code>
+     */
+    protected $PosterUri = null;
+    /**
+     *视频名称
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue Title = 3;</code>
+     */
+    protected $Title = null;
+    /**
+     *视频描述
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue Description = 4;</code>
+     */
+    protected $Description = null;
+    /**
+     *视频标签
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue Tags = 5;</code>
+     */
+    protected $Tags = null;
+    /**
+     *视频分类Id
+     *
+     * Generated from protobuf field <code>.google.protobuf.Int64Value ClassificationId = 6;</code>
+     */
+    protected $ClassificationId = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vid
+     *          视频ID
+     *     @type \Google\Protobuf\StringValue $PosterUri
+     *          视频封面Uri
+     *     @type \Google\Protobuf\StringValue $Title
+     *          视频名称
+     *     @type \Google\Protobuf\StringValue $Description
+     *          视频描述
+     *     @type \Google\Protobuf\StringValue $Tags
+     *          视频标签
+     *     @type \Google\Protobuf\Int64Value $ClassificationId
+     *          视频分类Id
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频封面Uri
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue PosterUri = 2;</code>
+     * @return \Google\Protobuf\StringValue|null
+     */
+    public function getPosterUri()
+    {
+        return $this->PosterUri;
+    }
+
+    public function hasPosterUri()
+    {
+        return isset($this->PosterUri);
+    }
+
+    public function clearPosterUri()
+    {
+        unset($this->PosterUri);
+    }
+
+    /**
+     * Returns the unboxed value from <code>getPosterUri()</code>
+
+     *视频封面Uri
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue PosterUri = 2;</code>
+     * @return string|null
+     */
+    public function getPosterUriUnwrapped()
+    {
+        return $this->readWrapperValue("PosterUri");
+    }
+
+    /**
+     *视频封面Uri
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue PosterUri = 2;</code>
+     * @param \Google\Protobuf\StringValue $var
+     * @return $this
+     */
+    public function setPosterUri($var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\StringValue::class);
+        $this->PosterUri = $var;
+
+        return $this;
+    }
+
+    /**
+     * Sets the field by wrapping a primitive type in a Google\Protobuf\StringValue object.
+
+     *视频封面Uri
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue PosterUri = 2;</code>
+     * @param string|null $var
+     * @return $this
+     */
+    public function setPosterUriUnwrapped($var)
+    {
+        $this->writeWrapperValue("PosterUri", $var);
+        return $this;}
+
+    /**
+     *视频名称
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue Title = 3;</code>
+     * @return \Google\Protobuf\StringValue|null
+     */
+    public function getTitle()
+    {
+        return $this->Title;
+    }
+
+    public function hasTitle()
+    {
+        return isset($this->Title);
+    }
+
+    public function clearTitle()
+    {
+        unset($this->Title);
+    }
+
+    /**
+     * Returns the unboxed value from <code>getTitle()</code>
+
+     *视频名称
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue Title = 3;</code>
+     * @return string|null
+     */
+    public function getTitleUnwrapped()
+    {
+        return $this->readWrapperValue("Title");
+    }
+
+    /**
+     *视频名称
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue Title = 3;</code>
+     * @param \Google\Protobuf\StringValue $var
+     * @return $this
+     */
+    public function setTitle($var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\StringValue::class);
+        $this->Title = $var;
+
+        return $this;
+    }
+
+    /**
+     * Sets the field by wrapping a primitive type in a Google\Protobuf\StringValue object.
+
+     *视频名称
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue Title = 3;</code>
+     * @param string|null $var
+     * @return $this
+     */
+    public function setTitleUnwrapped($var)
+    {
+        $this->writeWrapperValue("Title", $var);
+        return $this;}
+
+    /**
+     *视频描述
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue Description = 4;</code>
+     * @return \Google\Protobuf\StringValue|null
+     */
+    public function getDescription()
+    {
+        return $this->Description;
+    }
+
+    public function hasDescription()
+    {
+        return isset($this->Description);
+    }
+
+    public function clearDescription()
+    {
+        unset($this->Description);
+    }
+
+    /**
+     * Returns the unboxed value from <code>getDescription()</code>
+
+     *视频描述
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue Description = 4;</code>
+     * @return string|null
+     */
+    public function getDescriptionUnwrapped()
+    {
+        return $this->readWrapperValue("Description");
+    }
+
+    /**
+     *视频描述
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue Description = 4;</code>
+     * @param \Google\Protobuf\StringValue $var
+     * @return $this
+     */
+    public function setDescription($var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\StringValue::class);
+        $this->Description = $var;
+
+        return $this;
+    }
+
+    /**
+     * Sets the field by wrapping a primitive type in a Google\Protobuf\StringValue object.
+
+     *视频描述
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue Description = 4;</code>
+     * @param string|null $var
+     * @return $this
+     */
+    public function setDescriptionUnwrapped($var)
+    {
+        $this->writeWrapperValue("Description", $var);
+        return $this;}
+
+    /**
+     *视频标签
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue Tags = 5;</code>
+     * @return \Google\Protobuf\StringValue|null
+     */
+    public function getTags()
+    {
+        return $this->Tags;
+    }
+
+    public function hasTags()
+    {
+        return isset($this->Tags);
+    }
+
+    public function clearTags()
+    {
+        unset($this->Tags);
+    }
+
+    /**
+     * Returns the unboxed value from <code>getTags()</code>
+
+     *视频标签
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue Tags = 5;</code>
+     * @return string|null
+     */
+    public function getTagsUnwrapped()
+    {
+        return $this->readWrapperValue("Tags");
+    }
+
+    /**
+     *视频标签
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue Tags = 5;</code>
+     * @param \Google\Protobuf\StringValue $var
+     * @return $this
+     */
+    public function setTags($var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\StringValue::class);
+        $this->Tags = $var;
+
+        return $this;
+    }
+
+    /**
+     * Sets the field by wrapping a primitive type in a Google\Protobuf\StringValue object.
+
+     *视频标签
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue Tags = 5;</code>
+     * @param string|null $var
+     * @return $this
+     */
+    public function setTagsUnwrapped($var)
+    {
+        $this->writeWrapperValue("Tags", $var);
+        return $this;}
+
+    /**
+     *视频分类Id
+     *
+     * Generated from protobuf field <code>.google.protobuf.Int64Value ClassificationId = 6;</code>
+     * @return \Google\Protobuf\Int64Value|null
+     */
+    public function getClassificationId()
+    {
+        return $this->ClassificationId;
+    }
+
+    public function hasClassificationId()
+    {
+        return isset($this->ClassificationId);
+    }
+
+    public function clearClassificationId()
+    {
+        unset($this->ClassificationId);
+    }
+
+    /**
+     * Returns the unboxed value from <code>getClassificationId()</code>
+
+     *视频分类Id
+     *
+     * Generated from protobuf field <code>.google.protobuf.Int64Value ClassificationId = 6;</code>
+     * @return int|string|null
+     */
+    public function getClassificationIdUnwrapped()
+    {
+        return $this->readWrapperValue("ClassificationId");
+    }
+
+    /**
+     *视频分类Id
+     *
+     * Generated from protobuf field <code>.google.protobuf.Int64Value ClassificationId = 6;</code>
+     * @param \Google\Protobuf\Int64Value $var
+     * @return $this
+     */
+    public function setClassificationId($var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\Int64Value::class);
+        $this->ClassificationId = $var;
+
+        return $this;
+    }
+
+    /**
+     * Sets the field by wrapping a primitive type in a Google\Protobuf\Int64Value object.
+
+     *视频分类Id
+     *
+     * Generated from protobuf field <code>.google.protobuf.Int64Value ClassificationId = 6;</code>
+     * @param int|string|null $var
+     * @return $this
+     */
+    public function setClassificationIdUnwrapped($var)
+    {
+        $this->writeWrapperValue("ClassificationId", $var);
+        return $this;}
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateMediaPublishStatusRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateMediaPublishStatusRequest.php
new file mode 100755
index 0000000..2a9bbfc
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateMediaPublishStatusRequest.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodUpdateMediaPublishStatusRequest</code>
+ */
+class VodUpdateMediaPublishStatusRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     */
+    protected $Vid = '';
+    /**
+     *视频发布状态
+     *
+     * Generated from protobuf field <code>string Status = 2;</code>
+     */
+    protected $Status = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vid
+     *          视频ID
+     *     @type string $Status
+     *          视频发布状态
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     *视频发布状态
+     *
+     * Generated from protobuf field <code>string Status = 2;</code>
+     * @return string
+     */
+    public function getStatus()
+    {
+        return $this->Status;
+    }
+
+    /**
+     *视频发布状态
+     *
+     * Generated from protobuf field <code>string Status = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStatus($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Status = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateSpaceRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateSpaceRequest.php
new file mode 100755
index 0000000..11f8d4e
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateSpaceRequest.php
@@ -0,0 +1,167 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodUpdateSpaceRequest</code>
+ */
+class VodUpdateSpaceRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     * 原项目 
+     *
+     * Generated from protobuf field <code>string SourceProjectName = 2;</code>
+     */
+    protected $SourceProjectName = '';
+    /**
+     * 变更目标项目 
+     *
+     * Generated from protobuf field <code>string TargetProjectName = 3;</code>
+     */
+    protected $TargetProjectName = '';
+    /**
+     * 描述 
+     *
+     * Generated from protobuf field <code>string Description = 4;</code>
+     */
+    protected $Description = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *           空间名 
+     *     @type string $SourceProjectName
+     *           原项目 
+     *     @type string $TargetProjectName
+     *           变更目标项目 
+     *     @type string $Description
+     *           描述 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 原项目 
+     *
+     * Generated from protobuf field <code>string SourceProjectName = 2;</code>
+     * @return string
+     */
+    public function getSourceProjectName()
+    {
+        return $this->SourceProjectName;
+    }
+
+    /**
+     * 原项目 
+     *
+     * Generated from protobuf field <code>string SourceProjectName = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSourceProjectName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SourceProjectName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 变更目标项目 
+     *
+     * Generated from protobuf field <code>string TargetProjectName = 3;</code>
+     * @return string
+     */
+    public function getTargetProjectName()
+    {
+        return $this->TargetProjectName;
+    }
+
+    /**
+     * 变更目标项目 
+     *
+     * Generated from protobuf field <code>string TargetProjectName = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setTargetProjectName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->TargetProjectName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 描述 
+     *
+     * Generated from protobuf field <code>string Description = 4;</code>
+     * @return string
+     */
+    public function getDescription()
+    {
+        return $this->Description;
+    }
+
+    /**
+     * 描述 
+     *
+     * Generated from protobuf field <code>string Description = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setDescription($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Description = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateSpaceUploadConfigRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateSpaceUploadConfigRequest.php
new file mode 100755
index 0000000..896c885
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateSpaceUploadConfigRequest.php
@@ -0,0 +1,141 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodUpdateSpaceUploadConfigRequest</code>
+ */
+class VodUpdateSpaceUploadConfigRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     * 需要变更的设置key,值为VodSpaceUploadConfigKey中的枚举数字 
+     *
+     * Generated from protobuf field <code>string ConfigKey = 2;</code>
+     */
+    protected $ConfigKey = '';
+    /**
+     * 需要变更的设置value,取值与ConfigKey相关。
+     * ConfigKey传入1时,ConfigValue可选值为”true“或”false“;
+     * ConfigKey传入2时,ConfigValue可选值为”Available“或”Unavailable“
+     *
+     * Generated from protobuf field <code>string ConfigValue = 3;</code>
+     */
+    protected $ConfigValue = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *           空间名 
+     *     @type string $ConfigKey
+     *           需要变更的设置key,值为VodSpaceUploadConfigKey中的枚举数字 
+     *     @type string $ConfigValue
+     *           需要变更的设置value,取值与ConfigKey相关。
+     *           ConfigKey传入1时,ConfigValue可选值为”true“或”false“;
+     *           ConfigKey传入2时,ConfigValue可选值为”Available“或”Unavailable“
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * 空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 需要变更的设置key,值为VodSpaceUploadConfigKey中的枚举数字 
+     *
+     * Generated from protobuf field <code>string ConfigKey = 2;</code>
+     * @return string
+     */
+    public function getConfigKey()
+    {
+        return $this->ConfigKey;
+    }
+
+    /**
+     * 需要变更的设置key,值为VodSpaceUploadConfigKey中的枚举数字 
+     *
+     * Generated from protobuf field <code>string ConfigKey = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setConfigKey($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->ConfigKey = $var;
+
+        return $this;
+    }
+
+    /**
+     * 需要变更的设置value,取值与ConfigKey相关。
+     * ConfigKey传入1时,ConfigValue可选值为”true“或”false“;
+     * ConfigKey传入2时,ConfigValue可选值为”Available“或”Unavailable“
+     *
+     * Generated from protobuf field <code>string ConfigValue = 3;</code>
+     * @return string
+     */
+    public function getConfigValue()
+    {
+        return $this->ConfigValue;
+    }
+
+    /**
+     * 需要变更的设置value,取值与ConfigKey相关。
+     * ConfigKey传入1时,ConfigValue可选值为”true“或”false“;
+     * ConfigKey传入2时,ConfigValue可选值为”Available“或”Unavailable“
+     *
+     * Generated from protobuf field <code>string ConfigValue = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setConfigValue($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->ConfigValue = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateSubtitleInfoRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateSubtitleInfoRequest.php
new file mode 100755
index 0000000..91a71d5
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateSubtitleInfoRequest.php
@@ -0,0 +1,309 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodUpdateSubtitleInfoRequest</code>
+ */
+class VodUpdateSubtitleInfoRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     */
+    protected $Vid = '';
+    /**
+     *转码视频id列表
+     *
+     * Generated from protobuf field <code>string FileId = 2;</code>
+     */
+    protected $FileId = '';
+    /**
+     *字幕语言列表
+     *
+     * Generated from protobuf field <code>string Language = 3;</code>
+     */
+    protected $Language = '';
+    /**
+     *字幕格式列表
+     *
+     * Generated from protobuf field <code>string Format = 4;</code>
+     */
+    protected $Format = '';
+    /**
+     *标题
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue Title = 6;</code>
+     */
+    protected $Title = null;
+    /**
+     *标签
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue Tag = 7;</code>
+     */
+    protected $Tag = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vid
+     *          视频ID
+     *     @type string $FileId
+     *          转码视频id列表
+     *     @type string $Language
+     *          字幕语言列表
+     *     @type string $Format
+     *          字幕格式列表
+     *     @type \Google\Protobuf\StringValue $Title
+     *          标题
+     *     @type \Google\Protobuf\StringValue $Tag
+     *          标签
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     *转码视频id列表
+     *
+     * Generated from protobuf field <code>string FileId = 2;</code>
+     * @return string
+     */
+    public function getFileId()
+    {
+        return $this->FileId;
+    }
+
+    /**
+     *转码视频id列表
+     *
+     * Generated from protobuf field <code>string FileId = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileId($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileId = $var;
+
+        return $this;
+    }
+
+    /**
+     *字幕语言列表
+     *
+     * Generated from protobuf field <code>string Language = 3;</code>
+     * @return string
+     */
+    public function getLanguage()
+    {
+        return $this->Language;
+    }
+
+    /**
+     *字幕语言列表
+     *
+     * Generated from protobuf field <code>string Language = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setLanguage($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Language = $var;
+
+        return $this;
+    }
+
+    /**
+     *字幕格式列表
+     *
+     * Generated from protobuf field <code>string Format = 4;</code>
+     * @return string
+     */
+    public function getFormat()
+    {
+        return $this->Format;
+    }
+
+    /**
+     *字幕格式列表
+     *
+     * Generated from protobuf field <code>string Format = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFormat($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Format = $var;
+
+        return $this;
+    }
+
+    /**
+     *标题
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue Title = 6;</code>
+     * @return \Google\Protobuf\StringValue|null
+     */
+    public function getTitle()
+    {
+        return $this->Title;
+    }
+
+    public function hasTitle()
+    {
+        return isset($this->Title);
+    }
+
+    public function clearTitle()
+    {
+        unset($this->Title);
+    }
+
+    /**
+     * Returns the unboxed value from <code>getTitle()</code>
+
+     *标题
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue Title = 6;</code>
+     * @return string|null
+     */
+    public function getTitleUnwrapped()
+    {
+        return $this->readWrapperValue("Title");
+    }
+
+    /**
+     *标题
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue Title = 6;</code>
+     * @param \Google\Protobuf\StringValue $var
+     * @return $this
+     */
+    public function setTitle($var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\StringValue::class);
+        $this->Title = $var;
+
+        return $this;
+    }
+
+    /**
+     * Sets the field by wrapping a primitive type in a Google\Protobuf\StringValue object.
+
+     *标题
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue Title = 6;</code>
+     * @param string|null $var
+     * @return $this
+     */
+    public function setTitleUnwrapped($var)
+    {
+        $this->writeWrapperValue("Title", $var);
+        return $this;}
+
+    /**
+     *标签
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue Tag = 7;</code>
+     * @return \Google\Protobuf\StringValue|null
+     */
+    public function getTag()
+    {
+        return $this->Tag;
+    }
+
+    public function hasTag()
+    {
+        return isset($this->Tag);
+    }
+
+    public function clearTag()
+    {
+        unset($this->Tag);
+    }
+
+    /**
+     * Returns the unboxed value from <code>getTag()</code>
+
+     *标签
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue Tag = 7;</code>
+     * @return string|null
+     */
+    public function getTagUnwrapped()
+    {
+        return $this->readWrapperValue("Tag");
+    }
+
+    /**
+     *标签
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue Tag = 7;</code>
+     * @param \Google\Protobuf\StringValue $var
+     * @return $this
+     */
+    public function setTag($var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\StringValue::class);
+        $this->Tag = $var;
+
+        return $this;
+    }
+
+    /**
+     * Sets the field by wrapping a primitive type in a Google\Protobuf\StringValue object.
+
+     *标签
+     *
+     * Generated from protobuf field <code>.google.protobuf.StringValue Tag = 7;</code>
+     * @param string|null $var
+     * @return $this
+     */
+    public function setTagUnwrapped($var)
+    {
+        $this->writeWrapperValue("Tag", $var);
+        return $this;}
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateSubtitleStatusRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateSubtitleStatusRequest.php
new file mode 100755
index 0000000..13b9c01
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateSubtitleStatusRequest.php
@@ -0,0 +1,201 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodUpdateSubtitleStatusRequest</code>
+ */
+class VodUpdateSubtitleStatusRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     */
+    protected $Vid = '';
+    /**
+     *转码视频id列表,多个用","隔开
+     *
+     * Generated from protobuf field <code>string FileIds = 2;</code>
+     */
+    protected $FileIds = '';
+    /**
+     *字幕语言列表,多个用","隔开
+     *
+     * Generated from protobuf field <code>string Languages = 3;</code>
+     */
+    protected $Languages = '';
+    /**
+     *字幕格式列表,多个用","隔开
+     *
+     * Generated from protobuf field <code>string Formats = 4;</code>
+     */
+    protected $Formats = '';
+    /**
+     *状态
+     *
+     * Generated from protobuf field <code>string Status = 5;</code>
+     */
+    protected $Status = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $Vid
+     *          视频ID
+     *     @type string $FileIds
+     *          转码视频id列表,多个用","隔开
+     *     @type string $Languages
+     *          字幕语言列表,多个用","隔开
+     *     @type string $Formats
+     *          字幕格式列表,多个用","隔开
+     *     @type string $Status
+     *          状态
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @return string
+     */
+    public function getVid()
+    {
+        return $this->Vid;
+    }
+
+    /**
+     *视频ID
+     *
+     * Generated from protobuf field <code>string Vid = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setVid($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Vid = $var;
+
+        return $this;
+    }
+
+    /**
+     *转码视频id列表,多个用","隔开
+     *
+     * Generated from protobuf field <code>string FileIds = 2;</code>
+     * @return string
+     */
+    public function getFileIds()
+    {
+        return $this->FileIds;
+    }
+
+    /**
+     *转码视频id列表,多个用","隔开
+     *
+     * Generated from protobuf field <code>string FileIds = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileIds($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileIds = $var;
+
+        return $this;
+    }
+
+    /**
+     *字幕语言列表,多个用","隔开
+     *
+     * Generated from protobuf field <code>string Languages = 3;</code>
+     * @return string
+     */
+    public function getLanguages()
+    {
+        return $this->Languages;
+    }
+
+    /**
+     *字幕语言列表,多个用","隔开
+     *
+     * Generated from protobuf field <code>string Languages = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setLanguages($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Languages = $var;
+
+        return $this;
+    }
+
+    /**
+     *字幕格式列表,多个用","隔开
+     *
+     * Generated from protobuf field <code>string Formats = 4;</code>
+     * @return string
+     */
+    public function getFormats()
+    {
+        return $this->Formats;
+    }
+
+    /**
+     *字幕格式列表,多个用","隔开
+     *
+     * Generated from protobuf field <code>string Formats = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFormats($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Formats = $var;
+
+        return $this;
+    }
+
+    /**
+     *状态
+     *
+     * Generated from protobuf field <code>string Status = 5;</code>
+     * @return string
+     */
+    public function getStatus()
+    {
+        return $this->Status;
+    }
+
+    /**
+     *状态
+     *
+     * Generated from protobuf field <code>string Status = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setStatus($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Status = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateVideoClassificationRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateVideoClassificationRequest.php
new file mode 100755
index 0000000..3a4afe4
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUpdateVideoClassificationRequest.php
@@ -0,0 +1,112 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodUpdateVideoClassificationRequest</code>
+ */
+class VodUpdateVideoClassificationRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     * Generated from protobuf field <code>int64 ClassificationId = 2;</code>
+     */
+    protected $ClassificationId = 0;
+    /**
+     * Generated from protobuf field <code>string Classification = 3;</code>
+     */
+    protected $Classification = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *     @type int|string $ClassificationId
+     *     @type string $Classification
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>int64 ClassificationId = 2;</code>
+     * @return int|string
+     */
+    public function getClassificationId()
+    {
+        return $this->ClassificationId;
+    }
+
+    /**
+     * Generated from protobuf field <code>int64 ClassificationId = 2;</code>
+     * @param int|string $var
+     * @return $this
+     */
+    public function setClassificationId($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->ClassificationId = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>string Classification = 3;</code>
+     * @return string
+     */
+    public function getClassification()
+    {
+        return $this->Classification;
+    }
+
+    /**
+     * Generated from protobuf field <code>string Classification = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setClassification($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Classification = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUploadMaterialRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUploadMaterialRequest.php
new file mode 100755
index 0000000..f1e5fc1
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUploadMaterialRequest.php
@@ -0,0 +1,235 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodUploadMaterialRequest</code>
+ */
+class VodUploadMaterialRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 上传的空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     * 上传文件的路径 
+     *
+     * Generated from protobuf field <code>string FilePath = 2;</code>
+     */
+    protected $FilePath = '';
+    /**
+     * 业务希望上传透传的信息,会在上传成功时返回给用户 
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 3;</code>
+     */
+    protected $CallbackArgs = '';
+    /**
+     * 上传的功能函数 
+     *
+     * Generated from protobuf field <code>string Functions = 4;</code>
+     */
+    protected $Functions = '';
+    /**
+     * 上传的类型,media、image、object 默认为 media 
+     *
+     * Generated from protobuf field <code>string FileType = 5;</code>
+     */
+    protected $FileType = '';
+    /**
+     * 上传的文件在存储中的名字,即 bucket/key 中的 key 
+     *
+     * Generated from protobuf field <code>string FileName = 6;</code>
+     */
+    protected $FileName = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *           上传的空间名 
+     *     @type string $FilePath
+     *           上传文件的路径 
+     *     @type string $CallbackArgs
+     *           业务希望上传透传的信息,会在上传成功时返回给用户 
+     *     @type string $Functions
+     *           上传的功能函数 
+     *     @type string $FileType
+     *           上传的类型,media、image、object 默认为 media 
+     *     @type string $FileName
+     *           上传的文件在存储中的名字,即 bucket/key 中的 key 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 上传的空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * 上传的空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 上传文件的路径 
+     *
+     * Generated from protobuf field <code>string FilePath = 2;</code>
+     * @return string
+     */
+    public function getFilePath()
+    {
+        return $this->FilePath;
+    }
+
+    /**
+     * 上传文件的路径 
+     *
+     * Generated from protobuf field <code>string FilePath = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFilePath($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FilePath = $var;
+
+        return $this;
+    }
+
+    /**
+     * 业务希望上传透传的信息,会在上传成功时返回给用户 
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 3;</code>
+     * @return string
+     */
+    public function getCallbackArgs()
+    {
+        return $this->CallbackArgs;
+    }
+
+    /**
+     * 业务希望上传透传的信息,会在上传成功时返回给用户 
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCallbackArgs($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CallbackArgs = $var;
+
+        return $this;
+    }
+
+    /**
+     * 上传的功能函数 
+     *
+     * Generated from protobuf field <code>string Functions = 4;</code>
+     * @return string
+     */
+    public function getFunctions()
+    {
+        return $this->Functions;
+    }
+
+    /**
+     * 上传的功能函数 
+     *
+     * Generated from protobuf field <code>string Functions = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFunctions($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Functions = $var;
+
+        return $this;
+    }
+
+    /**
+     * 上传的类型,media、image、object 默认为 media 
+     *
+     * Generated from protobuf field <code>string FileType = 5;</code>
+     * @return string
+     */
+    public function getFileType()
+    {
+        return $this->FileType;
+    }
+
+    /**
+     * 上传的类型,media、image、object 默认为 media 
+     *
+     * Generated from protobuf field <code>string FileType = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileType = $var;
+
+        return $this;
+    }
+
+    /**
+     * 上传的文件在存储中的名字,即 bucket/key 中的 key 
+     *
+     * Generated from protobuf field <code>string FileName = 6;</code>
+     * @return string
+     */
+    public function getFileName()
+    {
+        return $this->FileName;
+    }
+
+    /**
+     * 上传的文件在存储中的名字,即 bucket/key 中的 key 
+     *
+     * Generated from protobuf field <code>string FileName = 6;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileName = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUploadMediaRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUploadMediaRequest.php
new file mode 100755
index 0000000..a5a8c2b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUploadMediaRequest.php
@@ -0,0 +1,201 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodUploadMediaRequest</code>
+ */
+class VodUploadMediaRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 上传的空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     * 上传文件的路径 
+     *
+     * Generated from protobuf field <code>string FilePath = 2;</code>
+     */
+    protected $FilePath = '';
+    /**
+     * 业务希望上传透传的信息,会在上传成功时返回给用户 
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 3;</code>
+     */
+    protected $CallbackArgs = '';
+    /**
+     * 上传的功能函数 
+     *
+     * Generated from protobuf field <code>string Functions = 4;</code>
+     */
+    protected $Functions = '';
+    /**
+     * 上传的文件在存储中的名字,即 bucket/key 中的 key 
+     *
+     * Generated from protobuf field <code>string FileName = 5;</code>
+     */
+    protected $FileName = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *           上传的空间名 
+     *     @type string $FilePath
+     *           上传文件的路径 
+     *     @type string $CallbackArgs
+     *           业务希望上传透传的信息,会在上传成功时返回给用户 
+     *     @type string $Functions
+     *           上传的功能函数 
+     *     @type string $FileName
+     *           上传的文件在存储中的名字,即 bucket/key 中的 key 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 上传的空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * 上传的空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 上传文件的路径 
+     *
+     * Generated from protobuf field <code>string FilePath = 2;</code>
+     * @return string
+     */
+    public function getFilePath()
+    {
+        return $this->FilePath;
+    }
+
+    /**
+     * 上传文件的路径 
+     *
+     * Generated from protobuf field <code>string FilePath = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFilePath($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FilePath = $var;
+
+        return $this;
+    }
+
+    /**
+     * 业务希望上传透传的信息,会在上传成功时返回给用户 
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 3;</code>
+     * @return string
+     */
+    public function getCallbackArgs()
+    {
+        return $this->CallbackArgs;
+    }
+
+    /**
+     * 业务希望上传透传的信息,会在上传成功时返回给用户 
+     *
+     * Generated from protobuf field <code>string CallbackArgs = 3;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setCallbackArgs($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->CallbackArgs = $var;
+
+        return $this;
+    }
+
+    /**
+     * 上传的功能函数 
+     *
+     * Generated from protobuf field <code>string Functions = 4;</code>
+     * @return string
+     */
+    public function getFunctions()
+    {
+        return $this->Functions;
+    }
+
+    /**
+     * 上传的功能函数 
+     *
+     * Generated from protobuf field <code>string Functions = 4;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFunctions($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->Functions = $var;
+
+        return $this;
+    }
+
+    /**
+     * 上传的文件在存储中的名字,即 bucket/key 中的 key 
+     *
+     * Generated from protobuf field <code>string FileName = 5;</code>
+     * @return string
+     */
+    public function getFileName()
+    {
+        return $this->FileName;
+    }
+
+    /**
+     * 上传的文件在存储中的名字,即 bucket/key 中的 key 
+     *
+     * Generated from protobuf field <code>string FileName = 5;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setFileName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->FileName = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUrlUploadJsonRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUrlUploadJsonRequest.php
new file mode 100755
index 0000000..7a93fd5
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUrlUploadJsonRequest.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodUrlUploadJsonRequest</code>
+ */
+class VodUrlUploadJsonRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 上传的空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     * 上传URL设置参数集合,每个URL入参的设置均在此设置 
+     *
+     * Generated from protobuf field <code>string URLSets = 2;</code>
+     */
+    protected $URLSets = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *           上传的空间名 
+     *     @type string $URLSets
+     *           上传URL设置参数集合,每个URL入参的设置均在此设置 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 上传的空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * 上传的空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 上传URL设置参数集合,每个URL入参的设置均在此设置 
+     *
+     * Generated from protobuf field <code>string URLSets = 2;</code>
+     * @return string
+     */
+    public function getURLSets()
+    {
+        return $this->URLSets;
+    }
+
+    /**
+     * 上传URL设置参数集合,每个URL入参的设置均在此设置 
+     *
+     * Generated from protobuf field <code>string URLSets = 2;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setURLSets($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->URLSets = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUrlUploadRequest.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUrlUploadRequest.php
new file mode 100755
index 0000000..8e3f07a
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Request/VodUrlUploadRequest.php
@@ -0,0 +1,99 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/request/request_vod.proto
+
+namespace Volc\Service\Vod\Models\Request;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Request.VodUrlUploadRequest</code>
+ */
+class VodUrlUploadRequest extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 上传的空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     */
+    protected $SpaceName = '';
+    /**
+     * 上传URL设置参数集合,每个URL入参的设置均在此设置 
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodUrlUploadURLSet URLSets = 2;</code>
+     */
+    private $URLSets;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type string $SpaceName
+     *           上传的空间名 
+     *     @type \Volc\Service\Vod\Models\Business\VodUrlUploadURLSet[]|\Google\Protobuf\Internal\RepeatedField $URLSets
+     *           上传URL设置参数集合,每个URL入参的设置均在此设置 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\RequestVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 上传的空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @return string
+     */
+    public function getSpaceName()
+    {
+        return $this->SpaceName;
+    }
+
+    /**
+     * 上传的空间名 
+     *
+     * Generated from protobuf field <code>string SpaceName = 1;</code>
+     * @param string $var
+     * @return $this
+     */
+    public function setSpaceName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->SpaceName = $var;
+
+        return $this;
+    }
+
+    /**
+     * 上传URL设置参数集合,每个URL入参的设置均在此设置 
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodUrlUploadURLSet URLSets = 2;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getURLSets()
+    {
+        return $this->URLSets;
+    }
+
+    /**
+     * 上传URL设置参数集合,每个URL入参的设置均在此设置 
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodUrlUploadURLSet URLSets = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodUrlUploadURLSet[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setURLSets($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodUrlUploadURLSet::class);
+        $this->URLSets = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodAddCallbackSubscriptionResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodAddCallbackSubscriptionResponse.php
new file mode 100755
index 0000000..6fae8af
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodAddCallbackSubscriptionResponse.php
@@ -0,0 +1,79 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * **********************************************************************
+ * 回调管理
+ * **********************************************************************
+ *
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodAddCallbackSubscriptionResponse</code>
+ */
+class VodAddCallbackSubscriptionResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodApplyUploadInfoResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodApplyUploadInfoResponse.php
new file mode 100755
index 0000000..f8dafc8
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodApplyUploadInfoResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodApplyUploadInfoResponse</code>
+ */
+class VodApplyUploadInfoResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 通用返回结构 
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     * 视频上传 1001 阶段返回结果 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodApplyUploadInfoResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           通用返回结构 
+     *     @type \Volc\Service\Vod\Models\Business\VodApplyUploadInfoResult $Result
+     *           视频上传 1001 阶段返回结果 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 通用返回结构 
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 通用返回结构 
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     * 视频上传 1001 阶段返回结果 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodApplyUploadInfoResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodApplyUploadInfoResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     * 视频上传 1001 阶段返回结果 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodApplyUploadInfoResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodApplyUploadInfoResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodApplyUploadInfoResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodCdnStatisticsCommonResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodCdnStatisticsCommonResponse.php
new file mode 100755
index 0000000..0e75a86
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodCdnStatisticsCommonResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodCdnStatisticsCommonResponse</code>
+ */
+class VodCdnStatisticsCommonResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCdnStatisticsCommonResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodCdnStatisticsCommonResult $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCdnStatisticsCommonResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodCdnStatisticsCommonResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCdnStatisticsCommonResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodCdnStatisticsCommonResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodCdnStatisticsCommonResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodCommitUploadInfoResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodCommitUploadInfoResponse.php
new file mode 100755
index 0000000..394d8c1
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodCommitUploadInfoResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodCommitUploadInfoResponse</code>
+ */
+class VodCommitUploadInfoResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 通用返回结构 
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     * 视频上传 1005 阶段返回结果 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCommitUploadInfoResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           通用返回结构 
+     *     @type \Volc\Service\Vod\Models\Business\VodCommitUploadInfoResult $Result
+     *           视频上传 1005 阶段返回结果 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 通用返回结构 
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 通用返回结构 
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     * 视频上传 1005 阶段返回结果 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCommitUploadInfoResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodCommitUploadInfoResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     * 视频上传 1005 阶段返回结果 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCommitUploadInfoResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodCommitUploadInfoResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodCommitUploadInfoResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodCreateCdnPreloadTaskResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodCreateCdnPreloadTaskResponse.php
new file mode 100755
index 0000000..b1d3c56
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodCreateCdnPreloadTaskResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodCreateCdnPreloadTaskResponse</code>
+ */
+class VodCreateCdnPreloadTaskResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCreateCdnTaskResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodCreateCdnTaskResult $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCreateCdnTaskResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodCreateCdnTaskResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCreateCdnTaskResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodCreateCdnTaskResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodCreateCdnTaskResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodCreateCdnRefreshTaskResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodCreateCdnRefreshTaskResponse.php
new file mode 100755
index 0000000..a8f699a
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodCreateCdnRefreshTaskResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodCreateCdnRefreshTaskResponse</code>
+ */
+class VodCreateCdnRefreshTaskResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCreateCdnTaskResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodCreateCdnTaskResult $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCreateCdnTaskResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodCreateCdnTaskResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCreateCdnTaskResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodCreateCdnTaskResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodCreateCdnTaskResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodCreateSpaceResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodCreateSpaceResponse.php
new file mode 100755
index 0000000..951fc9d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodCreateSpaceResponse.php
@@ -0,0 +1,79 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * **********************************************************************
+ * 空间管理
+ * **********************************************************************
+ *
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodCreateSpaceResponse</code>
+ */
+class VodCreateSpaceResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodCreateVideoClassificationResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodCreateVideoClassificationResponse.php
new file mode 100755
index 0000000..606fa70
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodCreateVideoClassificationResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodCreateVideoClassificationResponse</code>
+ */
+class VodCreateVideoClassificationResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCreateVideoClassificationData Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodCreateVideoClassificationData $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCreateVideoClassificationData Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodCreateVideoClassificationData|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCreateVideoClassificationData Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodCreateVideoClassificationData $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodCreateVideoClassificationData::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDeleteMediaResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDeleteMediaResponse.php
new file mode 100755
index 0000000..9a0d1b8
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDeleteMediaResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodDeleteMediaResponse</code>
+ */
+class VodDeleteMediaResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDeleteMediaData Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodDeleteMediaData $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDeleteMediaData Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodDeleteMediaData|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDeleteMediaData Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodDeleteMediaData $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodDeleteMediaData::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDeleteTranscodesResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDeleteTranscodesResponse.php
new file mode 100755
index 0000000..73c50ff
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDeleteTranscodesResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodDeleteTranscodesResponse</code>
+ */
+class VodDeleteTranscodesResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDeleteTranscodesData Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodDeleteTranscodesData $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDeleteTranscodesData Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodDeleteTranscodesData|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDeleteTranscodesData Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodDeleteTranscodesData $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodDeleteTranscodesData::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDeleteVideoClassificationResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDeleteVideoClassificationResponse.php
new file mode 100755
index 0000000..5e9f71b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDeleteVideoClassificationResponse.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodDeleteVideoClassificationResponse</code>
+ */
+class VodDeleteVideoClassificationResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDescribeIPInfoResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDescribeIPInfoResponse.php
new file mode 100755
index 0000000..27482c9
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDescribeIPInfoResponse.php
@@ -0,0 +1,109 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodDescribeIPInfoResponse</code>
+ */
+class VodDescribeIPInfoResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodCdnIpInfo Result = 2;</code>
+     */
+    private $Result;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodCdnIpInfo[]|\Google\Protobuf\Internal\RepeatedField $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodCdnIpInfo Result = 2;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodCdnIpInfo Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodCdnIpInfo[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodCdnIpInfo::class);
+        $this->Result = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDescribeVodDomainBandwidthDataResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDescribeVodDomainBandwidthDataResponse.php
new file mode 100755
index 0000000..8feabbe
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDescribeVodDomainBandwidthDataResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodDescribeVodDomainBandwidthDataResponse</code>
+ */
+class VodDescribeVodDomainBandwidthDataResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDescribeVodDomainBandwidthDataResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodDescribeVodDomainBandwidthDataResult $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDescribeVodDomainBandwidthDataResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodDescribeVodDomainBandwidthDataResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDescribeVodDomainBandwidthDataResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodDescribeVodDomainBandwidthDataResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodDescribeVodDomainBandwidthDataResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDescribeVodDomainTrafficDataResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDescribeVodDomainTrafficDataResponse.php
new file mode 100755
index 0000000..a358341
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDescribeVodDomainTrafficDataResponse.php
@@ -0,0 +1,105 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodDescribeVodDomainTrafficDataResponse</code>
+ */
+class VodDescribeVodDomainTrafficDataResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDescribeVodDomainTrafficDataResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *     @type \Volc\Service\Vod\Models\Business\VodDescribeVodDomainTrafficDataResult $Result
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDescribeVodDomainTrafficDataResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodDescribeVodDomainTrafficDataResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDescribeVodDomainTrafficDataResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodDescribeVodDomainTrafficDataResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodDescribeVodDomainTrafficDataResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDescribeVodSpaceStorageDataResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDescribeVodSpaceStorageDataResponse.php
new file mode 100755
index 0000000..805d5d7
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodDescribeVodSpaceStorageDataResponse.php
@@ -0,0 +1,105 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodDescribeVodSpaceStorageDataResponse</code>
+ */
+class VodDescribeVodSpaceStorageDataResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDescribeVodSpaceStorageDataResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *     @type \Volc\Service\Vod\Models\Business\VodDescribeVodSpaceStorageDataResult $Result
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDescribeVodSpaceStorageDataResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodDescribeVodSpaceStorageDataResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDescribeVodSpaceStorageDataResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodDescribeVodSpaceStorageDataResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodDescribeVodSpaceStorageDataResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetAllPlayInfoResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetAllPlayInfoResponse.php
new file mode 100755
index 0000000..fc0cd00
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetAllPlayInfoResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodGetAllPlayInfoResponse</code>
+ */
+class VodGetAllPlayInfoResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodAllPlayInfoResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodAllPlayInfoResult $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodAllPlayInfoResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodAllPlayInfoResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodAllPlayInfoResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodAllPlayInfoResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodAllPlayInfoResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetAppInfoResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetAppInfoResponse.php
new file mode 100755
index 0000000..cfb02e5
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetAppInfoResponse.php
@@ -0,0 +1,123 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * **********************************************************************
+ * 应用管理
+ * **********************************************************************
+ *
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodGetAppInfoResponse</code>
+ */
+class VodGetAppInfoResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetAppInfoResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodGetAppInfoResult $Result
+     *           响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetAppInfoResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodGetAppInfoResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetAppInfoResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodGetAppInfoResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodGetAppInfoResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetAudioEventDetectionForAuditResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetAudioEventDetectionForAuditResponse.php
new file mode 100755
index 0000000..9374f30
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetAudioEventDetectionForAuditResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodGetAudioEventDetectionForAuditResponse</code>
+ */
+class VodGetAudioEventDetectionForAuditResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetAudioEventDetectionForAuditResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodGetAudioEventDetectionForAuditResult $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetAudioEventDetectionForAuditResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodGetAudioEventDetectionForAuditResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetAudioEventDetectionForAuditResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodGetAudioEventDetectionForAuditResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodGetAudioEventDetectionForAuditResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetAudioInfoForAuditResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetAudioInfoForAuditResponse.php
new file mode 100755
index 0000000..90a99c1
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetAudioInfoForAuditResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodGetAudioInfoForAuditResponse</code>
+ */
+class VodGetAudioInfoForAuditResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetAudioInfoForAuditResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodGetAudioInfoForAuditResult $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetAudioInfoForAuditResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodGetAudioInfoForAuditResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetAudioInfoForAuditResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodGetAudioInfoForAuditResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodGetAudioInfoForAuditResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetAuditFramesForAuditResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetAuditFramesForAuditResponse.php
new file mode 100755
index 0000000..428e634
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetAuditFramesForAuditResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodGetAuditFramesForAuditResponse</code>
+ */
+class VodGetAuditFramesForAuditResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetFramesForAuditResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodGetFramesForAuditResult $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetFramesForAuditResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodGetFramesForAuditResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetFramesForAuditResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodGetFramesForAuditResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodGetFramesForAuditResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetAutomaticSpeechRecognitionForAuditResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetAutomaticSpeechRecognitionForAuditResponse.php
new file mode 100755
index 0000000..b6706f5
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetAutomaticSpeechRecognitionForAuditResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodGetAutomaticSpeechRecognitionForAuditResponse</code>
+ */
+class VodGetAutomaticSpeechRecognitionForAuditResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetAutomaticSpeechRecognitionForAuditResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodGetAutomaticSpeechRecognitionForAuditResult $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetAutomaticSpeechRecognitionForAuditResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodGetAutomaticSpeechRecognitionForAuditResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetAutomaticSpeechRecognitionForAuditResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodGetAutomaticSpeechRecognitionForAuditResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodGetAutomaticSpeechRecognitionForAuditResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetBetterFramesForAuditResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetBetterFramesForAuditResponse.php
new file mode 100755
index 0000000..fff4dac
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetBetterFramesForAuditResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodGetBetterFramesForAuditResponse</code>
+ */
+class VodGetBetterFramesForAuditResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetBetterFramesForAuditResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodGetBetterFramesForAuditResult $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetBetterFramesForAuditResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodGetBetterFramesForAuditResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetBetterFramesForAuditResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodGetBetterFramesForAuditResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodGetBetterFramesForAuditResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetDirectEditProgressResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetDirectEditProgressResponse.php
new file mode 100755
index 0000000..d45537e
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetDirectEditProgressResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodGetDirectEditProgressResponse</code>
+ */
+class VodGetDirectEditProgressResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.GetDirectEditProgress Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\GetDirectEditProgress $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.GetDirectEditProgress Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\GetDirectEditProgress|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.GetDirectEditProgress Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\GetDirectEditProgress $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\GetDirectEditProgress::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetDirectEditResultResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetDirectEditResultResponse.php
new file mode 100755
index 0000000..50413f2
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetDirectEditResultResponse.php
@@ -0,0 +1,109 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodGetDirectEditResultResponse</code>
+ */
+class VodGetDirectEditResultResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.GetDirectEditResult Result = 2;</code>
+     */
+    private $Result;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\GetDirectEditResult[]|\Google\Protobuf\Internal\RepeatedField $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.GetDirectEditResult Result = 2;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.GetDirectEditResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\GetDirectEditResult[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\GetDirectEditResult::class);
+        $this->Result = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetHlsDecryptionKeyResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetHlsDecryptionKeyResponse.php
new file mode 100755
index 0000000..c211066
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetHlsDecryptionKeyResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodGetHlsDecryptionKeyResponse</code>
+ */
+class VodGetHlsDecryptionKeyResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetHlsDecryptionKeyResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodGetHlsDecryptionKeyResult $Result
+     *           响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetHlsDecryptionKeyResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodGetHlsDecryptionKeyResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetHlsDecryptionKeyResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodGetHlsDecryptionKeyResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodGetHlsDecryptionKeyResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetMLFramesForAuditResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetMLFramesForAuditResponse.php
new file mode 100755
index 0000000..9c1146a
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetMLFramesForAuditResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodGetMLFramesForAuditResponse</code>
+ */
+class VodGetMLFramesForAuditResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetFramesForAuditResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodGetFramesForAuditResult $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetFramesForAuditResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodGetFramesForAuditResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetFramesForAuditResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodGetFramesForAuditResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodGetFramesForAuditResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetMediaInfosResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetMediaInfosResponse.php
new file mode 100755
index 0000000..181c9ba
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetMediaInfosResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodGetMediaInfosResponse</code>
+ */
+class VodGetMediaInfosResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetMediaInfosData Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodGetMediaInfosData $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetMediaInfosData Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodGetMediaInfosData|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetMediaInfosData Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodGetMediaInfosData $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodGetMediaInfosData::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetMediaListResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetMediaListResponse.php
new file mode 100755
index 0000000..7a0c1a0
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetMediaListResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodGetMediaListResponse</code>
+ */
+class VodGetMediaListResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetMediaListData Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodGetMediaListData $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetMediaListData Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodGetMediaListData|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetMediaListData Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodGetMediaListData $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodGetMediaListData::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetOriginalPlayInfoResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetOriginalPlayInfoResponse.php
new file mode 100755
index 0000000..6a08525
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetOriginalPlayInfoResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodGetOriginalPlayInfoResponse</code>
+ */
+class VodGetOriginalPlayInfoResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetOriginalPlayInfoResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodGetOriginalPlayInfoResult $Result
+     *           响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetOriginalPlayInfoResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodGetOriginalPlayInfoResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetOriginalPlayInfoResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodGetOriginalPlayInfoResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodGetOriginalPlayInfoResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetPlayInfoResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetPlayInfoResponse.php
new file mode 100755
index 0000000..938a141
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetPlayInfoResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodGetPlayInfoResponse</code>
+ */
+class VodGetPlayInfoResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodPlayInfoModel Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodPlayInfoModel $Result
+     *           响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodPlayInfoModel Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodPlayInfoModel|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodPlayInfoModel Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodPlayInfoModel $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodPlayInfoModel::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetPlayInfoWithLiveTimeShiftSceneResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetPlayInfoWithLiveTimeShiftSceneResponse.php
new file mode 100755
index 0000000..9e109dc
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetPlayInfoWithLiveTimeShiftSceneResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodGetPlayInfoWithLiveTimeShiftSceneResponse</code>
+ */
+class VodGetPlayInfoWithLiveTimeShiftSceneResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetPlayInfoWithLiveTimeShiftSceneResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodGetPlayInfoWithLiveTimeShiftSceneResult $Result
+     *           响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetPlayInfoWithLiveTimeShiftSceneResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodGetPlayInfoWithLiveTimeShiftSceneResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetPlayInfoWithLiveTimeShiftSceneResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodGetPlayInfoWithLiveTimeShiftSceneResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodGetPlayInfoWithLiveTimeShiftSceneResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetPrivateDrmPlayAuthResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetPrivateDrmPlayAuthResponse.php
new file mode 100755
index 0000000..a264427
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetPrivateDrmPlayAuthResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodGetPrivateDrmPlayAuthResponse</code>
+ */
+class VodGetPrivateDrmPlayAuthResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetPrivateDrmPlayAuthResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodGetPrivateDrmPlayAuthResult $Result
+     *           响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetPrivateDrmPlayAuthResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodGetPrivateDrmPlayAuthResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetPrivateDrmPlayAuthResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodGetPrivateDrmPlayAuthResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodGetPrivateDrmPlayAuthResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetRecommendedPosterResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetRecommendedPosterResponse.php
new file mode 100755
index 0000000..d12b872
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetRecommendedPosterResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodGetRecommendedPosterResponse</code>
+ */
+class VodGetRecommendedPosterResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetRecPosterData Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodGetRecPosterData $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetRecPosterData Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodGetRecPosterData|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetRecPosterData Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodGetRecPosterData $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodGetRecPosterData::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetSmartStrategyLitePlayInfoResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetSmartStrategyLitePlayInfoResponse.php
new file mode 100755
index 0000000..176f293
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetSmartStrategyLitePlayInfoResponse.php
@@ -0,0 +1,123 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * **********************************************************************
+ * 智能策略-轻量版
+ * **********************************************************************
+ *
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodGetSmartStrategyLitePlayInfoResponse</code>
+ */
+class VodGetSmartStrategyLitePlayInfoResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetSmartStrategyLitePlayInfoResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodGetSmartStrategyLitePlayInfoResult $Result
+     *           响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetSmartStrategyLitePlayInfoResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodGetSmartStrategyLitePlayInfoResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetSmartStrategyLitePlayInfoResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodGetSmartStrategyLitePlayInfoResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodGetSmartStrategyLitePlayInfoResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetSpaceDetailResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetSpaceDetailResponse.php
new file mode 100755
index 0000000..7c702ea
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetSpaceDetailResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodGetSpaceDetailResponse</code>
+ */
+class VodGetSpaceDetailResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodSpaceInfo Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodSpaceInfo $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodSpaceInfo Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodSpaceInfo|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodSpaceInfo Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodSpaceInfo $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodSpaceInfo::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetSubtitleInfoListResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetSubtitleInfoListResponse.php
new file mode 100755
index 0000000..c45584b
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetSubtitleInfoListResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodGetSubtitleInfoListResponse</code>
+ */
+class VodGetSubtitleInfoListResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetSubtitleInfoListData Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodGetSubtitleInfoListData $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetSubtitleInfoListData Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodGetSubtitleInfoListData|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetSubtitleInfoListData Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodGetSubtitleInfoListData $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodGetSubtitleInfoListData::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetWorkflowExecutionDetailResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetWorkflowExecutionDetailResponse.php
new file mode 100755
index 0000000..6936aae
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetWorkflowExecutionDetailResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodGetWorkflowExecutionDetailResponse</code>
+ */
+class VodGetWorkflowExecutionDetailResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetWorkflowExecutionDetailResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodGetWorkflowExecutionDetailResult $Result
+     *           响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetWorkflowExecutionDetailResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodGetWorkflowExecutionDetailResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodGetWorkflowExecutionDetailResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodGetWorkflowExecutionDetailResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodGetWorkflowExecutionDetailResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetWorkflowExecutionStatusResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetWorkflowExecutionStatusResponse.php
new file mode 100755
index 0000000..94106f1
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodGetWorkflowExecutionStatusResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodGetWorkflowExecutionStatusResponse</code>
+ */
+class VodGetWorkflowExecutionStatusResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.WorkflowExecution Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\WorkflowExecution $Result
+     *           响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.WorkflowExecution Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\WorkflowExecution|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.WorkflowExecution Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\WorkflowExecution $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\WorkflowExecution::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListCdnAccessLogResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListCdnAccessLogResponse.php
new file mode 100755
index 0000000..5d1001d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListCdnAccessLogResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodListCdnAccessLogResponse</code>
+ */
+class VodListCdnAccessLogResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodListCdnAccessLogResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodListCdnAccessLogResult $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodListCdnAccessLogResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodListCdnAccessLogResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodListCdnAccessLogResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodListCdnAccessLogResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodListCdnAccessLogResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListCdnTasksResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListCdnTasksResponse.php
new file mode 100755
index 0000000..a351752
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListCdnTasksResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodListCdnTasksResponse</code>
+ */
+class VodListCdnTasksResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCdnTaskResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodCdnTaskResult $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCdnTaskResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodCdnTaskResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCdnTaskResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodCdnTaskResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodCdnTaskResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListCdnTopAccessUrlResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListCdnTopAccessUrlResponse.php
new file mode 100755
index 0000000..4c901a1
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListCdnTopAccessUrlResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodListCdnTopAccessUrlResponse</code>
+ */
+class VodListCdnTopAccessUrlResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodListCdnTopAccessUrlResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodListCdnTopAccessUrlResult $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodListCdnTopAccessUrlResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodListCdnTopAccessUrlResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodListCdnTopAccessUrlResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodListCdnTopAccessUrlResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodListCdnTopAccessUrlResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListDomainResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListDomainResponse.php
new file mode 100755
index 0000000..f62a6cf
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListDomainResponse.php
@@ -0,0 +1,123 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * **********************************************************************
+ * 分发加速管理
+ * **********************************************************************
+ *
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodListDomainResponse</code>
+ */
+class VodListDomainResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDomainConfigInfo Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodDomainConfigInfo $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDomainConfigInfo Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodDomainConfigInfo|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodDomainConfigInfo Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodDomainConfigInfo $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodDomainConfigInfo::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListSnapshotsResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListSnapshotsResponse.php
new file mode 100755
index 0000000..afd53b9
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListSnapshotsResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodListSnapshotsResponse</code>
+ */
+class VodListSnapshotsResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodSnapshotData Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodSnapshotData $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodSnapshotData Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodSnapshotData|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodSnapshotData Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodSnapshotData $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodSnapshotData::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListSpaceResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListSpaceResponse.php
new file mode 100755
index 0000000..b031777
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListSpaceResponse.php
@@ -0,0 +1,109 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodListSpaceResponse</code>
+ */
+class VodListSpaceResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSpaceInfo Result = 2;</code>
+     */
+    private $Result;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodSpaceInfo[]|\Google\Protobuf\Internal\RepeatedField $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSpaceInfo Result = 2;</code>
+     * @return \Google\Protobuf\Internal\RepeatedField
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>repeated .Volcengine.Vod.Models.Business.VodSpaceInfo Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodSpaceInfo[]|\Google\Protobuf\Internal\RepeatedField $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Volc\Service\Vod\Models\Business\VodSpaceInfo::class);
+        $this->Result = $arr;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListVideoClassificationsResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListVideoClassificationsResponse.php
new file mode 100755
index 0000000..d3b136a
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListVideoClassificationsResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodListVideoClassificationsResponse</code>
+ */
+class VodListVideoClassificationsResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodVideoClassificationsData Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodVideoClassificationsData $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodVideoClassificationsData Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodVideoClassificationsData|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodVideoClassificationsData Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodVideoClassificationsData $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodVideoClassificationsData::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListWorkflowExecutionResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListWorkflowExecutionResponse.php
new file mode 100755
index 0000000..8436554
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodListWorkflowExecutionResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodListWorkflowExecutionResponse</code>
+ */
+class VodListWorkflowExecutionResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodListWorkflowExecutionResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodListWorkflowExecutionResult $Result
+     *           响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodListWorkflowExecutionResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodListWorkflowExecutionResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     * 响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodListWorkflowExecutionResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodListWorkflowExecutionResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodListWorkflowExecutionResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodQueryUploadTaskInfoResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodQueryUploadTaskInfoResponse.php
new file mode 100755
index 0000000..5c7d065
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodQueryUploadTaskInfoResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodQueryUploadTaskInfoResponse</code>
+ */
+class VodQueryUploadTaskInfoResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 通用返回结构 
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     * URL批量上传状态查询结果 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodQueryData Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           通用返回结构 
+     *     @type \Volc\Service\Vod\Models\Business\VodQueryData $Result
+     *           URL批量上传状态查询结果 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 通用返回结构 
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 通用返回结构 
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     * URL批量上传状态查询结果 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodQueryData Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodQueryData|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     * URL批量上传状态查询结果 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodQueryData Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodQueryData $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodQueryData::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodRetrieveTranscodeResultResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodRetrieveTranscodeResultResponse.php
new file mode 100755
index 0000000..ee9eadb
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodRetrieveTranscodeResultResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodRetrieveTranscodeResultResponse</code>
+ */
+class VodRetrieveTranscodeResultResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.TranscodeResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\TranscodeResult $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.TranscodeResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\TranscodeResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.TranscodeResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\TranscodeResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\TranscodeResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodSetCallbackEventResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodSetCallbackEventResponse.php
new file mode 100755
index 0000000..d592487
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodSetCallbackEventResponse.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodSetCallbackEventResponse</code>
+ */
+class VodSetCallbackEventResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodStartWorkflowResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodStartWorkflowResponse.php
new file mode 100755
index 0000000..ca2861c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodStartWorkflowResponse.php
@@ -0,0 +1,123 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * **********************************************************************
+ * 转码
+ * **********************************************************************
+ *
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodStartWorkflowResponse</code>
+ */
+class VodStartWorkflowResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodStartWorkflowResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodStartWorkflowResult $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodStartWorkflowResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodStartWorkflowResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodStartWorkflowResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodStartWorkflowResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodStartWorkflowResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodSubmitDirectEditTaskAsyncResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodSubmitDirectEditTaskAsyncResponse.php
new file mode 100755
index 0000000..ff233ab
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodSubmitDirectEditTaskAsyncResponse.php
@@ -0,0 +1,123 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * **********************************************************************
+ * 视频编辑
+ * **********************************************************************
+ *
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodSubmitDirectEditTaskAsyncResponse</code>
+ */
+class VodSubmitDirectEditTaskAsyncResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.SubmitDirectEditTaskAsyncResult Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\SubmitDirectEditTaskAsyncResult $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.SubmitDirectEditTaskAsyncResult Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\SubmitDirectEditTaskAsyncResult|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.SubmitDirectEditTaskAsyncResult Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\SubmitDirectEditTaskAsyncResult $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\SubmitDirectEditTaskAsyncResult::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateMediaInfoResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateMediaInfoResponse.php
new file mode 100755
index 0000000..a43d469
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateMediaInfoResponse.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodUpdateMediaInfoResponse</code>
+ */
+class VodUpdateMediaInfoResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateMediaPublishStatusResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateMediaPublishStatusResponse.php
new file mode 100755
index 0000000..f2b5b84
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateMediaPublishStatusResponse.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodUpdateMediaPublishStatusResponse</code>
+ */
+class VodUpdateMediaPublishStatusResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateSpaceResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateSpaceResponse.php
new file mode 100755
index 0000000..50ec4db
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateSpaceResponse.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodUpdateSpaceResponse</code>
+ */
+class VodUpdateSpaceResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateSpaceUploadConfigResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateSpaceUploadConfigResponse.php
new file mode 100755
index 0000000..1930c75
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateSpaceUploadConfigResponse.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodUpdateSpaceUploadConfigResponse</code>
+ */
+class VodUpdateSpaceUploadConfigResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateSubtitleInfoResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateSubtitleInfoResponse.php
new file mode 100755
index 0000000..b179d30
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateSubtitleInfoResponse.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodUpdateSubtitleInfoResponse</code>
+ */
+class VodUpdateSubtitleInfoResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateSubtitleStatusResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateSubtitleStatusResponse.php
new file mode 100755
index 0000000..7791a4f
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateSubtitleStatusResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodUpdateSubtitleStatusResponse</code>
+ */
+class VodUpdateSubtitleStatusResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodUpdateSubtitleStatusData Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     *     @type \Volc\Service\Vod\Models\Business\VodUpdateSubtitleStatusData $Result
+     *          响应结果
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodUpdateSubtitleStatusData Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodUpdateSubtitleStatusData|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     *响应结果
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodUpdateSubtitleStatusData Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodUpdateSubtitleStatusData $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodUpdateSubtitleStatusData::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateVideoClassificationResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateVideoClassificationResponse.php
new file mode 100755
index 0000000..4d3ab8d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUpdateVideoClassificationResponse.php
@@ -0,0 +1,75 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodUpdateVideoClassificationResponse</code>
+ */
+class VodUpdateVideoClassificationResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *          响应通用信息
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     *响应通用信息
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUploadMediaResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUploadMediaResponse.php
new file mode 100755
index 0000000..8bc8ae3
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUploadMediaResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodUploadMediaResponse</code>
+ */
+class VodUploadMediaResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 通用返回结构 
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     * 视频上传结果 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCommitData Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           通用返回结构 
+     *     @type \Volc\Service\Vod\Models\Business\VodCommitData $Result
+     *           视频上传结果 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 通用返回结构 
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 通用返回结构 
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     * 视频上传结果 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCommitData Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodCommitData|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     * 视频上传结果 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodCommitData Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodCommitData $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodCommitData::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUrlUploadResponse.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUrlUploadResponse.php
new file mode 100755
index 0000000..608b3c3
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Models/Response/VodUrlUploadResponse.php
@@ -0,0 +1,119 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: vod/response/response_vod.proto
+
+namespace Volc\Service\Vod\Models\Response;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * Generated from protobuf message <code>Volcengine.Vod.Models.Response.VodUrlUploadResponse</code>
+ */
+class VodUrlUploadResponse extends \Google\Protobuf\Internal\Message
+{
+    /**
+     * 通用返回结构 
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     */
+    protected $ResponseMetadata = null;
+    /**
+     * URL批量上传结果 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodUrlResponseData Result = 2;</code>
+     */
+    protected $Result = null;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data {
+     *     Optional. Data for populating the Message object.
+     *
+     *     @type \Volc\Service\Base\Models\Base\ResponseMetadata $ResponseMetadata
+     *           通用返回结构 
+     *     @type \Volc\Service\Vod\Models\Business\VodUrlResponseData $Result
+     *           URL批量上传结果 
+     * }
+     */
+    public function __construct($data = NULL) {
+        \Volc\Service\Vod\Models\GPBMetadata\ResponseVod::initOnce();
+        parent::__construct($data);
+    }
+
+    /**
+     * 通用返回结构 
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @return \Volc\Service\Base\Models\Base\ResponseMetadata|null
+     */
+    public function getResponseMetadata()
+    {
+        return $this->ResponseMetadata;
+    }
+
+    public function hasResponseMetadata()
+    {
+        return isset($this->ResponseMetadata);
+    }
+
+    public function clearResponseMetadata()
+    {
+        unset($this->ResponseMetadata);
+    }
+
+    /**
+     * 通用返回结构 
+     *
+     * Generated from protobuf field <code>.Volcengine.Base.Models.Base.ResponseMetadata ResponseMetadata = 1;</code>
+     * @param \Volc\Service\Base\Models\Base\ResponseMetadata $var
+     * @return $this
+     */
+    public function setResponseMetadata($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Base\Models\Base\ResponseMetadata::class);
+        $this->ResponseMetadata = $var;
+
+        return $this;
+    }
+
+    /**
+     * URL批量上传结果 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodUrlResponseData Result = 2;</code>
+     * @return \Volc\Service\Vod\Models\Business\VodUrlResponseData|null
+     */
+    public function getResult()
+    {
+        return $this->Result;
+    }
+
+    public function hasResult()
+    {
+        return isset($this->Result);
+    }
+
+    public function clearResult()
+    {
+        unset($this->Result);
+    }
+
+    /**
+     * URL批量上传结果 
+     *
+     * Generated from protobuf field <code>.Volcengine.Vod.Models.Business.VodUrlResponseData Result = 2;</code>
+     * @param \Volc\Service\Vod\Models\Business\VodUrlResponseData $var
+     * @return $this
+     */
+    public function setResult($var)
+    {
+        GPBUtil::checkMessage($var, \Volc\Service\Vod\Models\Business\VodUrlResponseData::class);
+        $this->Result = $var;
+
+        return $this;
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Upload/FunctionInner.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Upload/FunctionInner.php
new file mode 100755
index 0000000..1289adb
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Upload/FunctionInner.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace Volc\Service\Vod\Upload;
+
+class FunctionInner
+{
+    public $Name = "";
+    public $Input;
+
+    /**
+     * FunctionInner constructor.
+     * @param string $Name
+     * @param $Input
+     */
+    public function __construct(string $Name, $Input)
+    {
+        $this->Name = $Name;
+        $this->Input = $Input;
+    }
+
+
+    public function __toString()
+    {
+        return json_encode($this);
+    }
+
+
+}
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Upload/Functions.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Upload/Functions.php
new file mode 100755
index 0000000..e8b755d
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Upload/Functions.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Volc\Service\Vod\Upload;
+
+class Functions
+{
+    private $funcs = [];
+
+//[{"Name": "GetMeta"},{"Name":"Snapshot","Input":{"SnapshotTime": 2.0}}]
+    public function addGetMetaFunc()
+    {
+        $this->funcs[] = new FunctionInner("GetMeta", "");
+    }
+
+    public function addSnapshotTimeFunc(float $snapshotTime)
+    {
+        $this->funcs[] = new FunctionInner("Snapshot", new SnapshotTimeInput($snapshotTime));
+    }
+
+    public function addStartWorkflowFunc(string $templateId)
+    {
+        $this->funcs[] = new FunctionInner("StartWorkflow", new WorkflowInput($templateId));
+    }
+
+    public function addOptionInfoFunc(OptionInfo $optionInfo)
+    {
+        $this->funcs[] = new FunctionInner("AddOptionInfo", $optionInfo);
+    }
+
+    public function getFunctionsString(): string
+    {
+        return json_encode($this->funcs);
+    }
+
+}
+
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Upload/OptionInfo.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Upload/OptionInfo.php
new file mode 100755
index 0000000..832a4d0
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Upload/OptionInfo.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace Volc\Service\Vod\Upload;
+
+class OptionInfo
+{
+    public $Title;
+    public $Tags;
+    public $Description;
+    public $Category;
+    public $RecordType;
+    public $Format;
+    public $ClassificationId;
+}
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Upload/SnapshotTimeInput.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Upload/SnapshotTimeInput.php
new file mode 100755
index 0000000..af5f928
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Upload/SnapshotTimeInput.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Volc\Service\Vod\Upload;
+
+class SnapshotTimeInput
+{
+
+    public $SnapshotTime;
+
+    /**
+     * SnapshotTimeInput constructor.
+     * @param $SnapshotTime
+     */
+    public function __construct($SnapshotTime)
+    {
+        $this->SnapshotTime = $SnapshotTime;
+    }
+}
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Upload/VodUpload.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Upload/VodUpload.php
new file mode 100755
index 0000000..9c9102c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Upload/VodUpload.php
@@ -0,0 +1,266 @@
+<?php
+
+
+namespace Volc\Service\Vod\Upload;
+
+
+use Exception;
+use GuzzleHttp\Client;
+use GuzzleHttp\Exception\RequestException;
+use GuzzleHttp\Handler\CurlHandler;
+use GuzzleHttp\HandlerStack;
+use GuzzleHttp\Middleware;
+use GuzzleHttp\Psr7\Request;
+use GuzzleHttp\Psr7\Response;
+use GuzzleHttp\RetryMiddleware;
+use Throwable;
+use Volc\Service\Vod\Models\Business\VodStoreInfo;
+use Volc\Service\Vod\Models\Request\VodApplyUploadInfoRequest;
+use Volc\Service\Vod\Models\Request\VodCommitUploadInfoRequest;
+use Volc\Service\Vod\Models\Request\VodUploadMaterialRequest;
+use Volc\Service\Vod\Models\Request\VodUploadMediaRequest;
+use Volc\Service\Vod\Models\Response\VodCommitUploadInfoResponse;
+use Volc\Service\Vod\Vod;
+
+const MinChunkSize = 1024 * 1024 * 20;
+const LargeFileSize = 1024 * 1024 * 1024;
+
+class VodUpload extends Vod
+{
+    /**
+     * @param VodUploadMediaRequest $vodUploadMediaRequest
+     * @return VodCommitUploadInfoResponse
+     * @throws Exception
+     * @throws Throwable
+     */
+    public function uploadMedia(VodUploadMediaRequest $vodUploadMediaRequest): VodCommitUploadInfoResponse
+    {
+        $applyRequest = new VodApplyUploadInfoRequest();
+        $applyRequest->setSpaceName($vodUploadMediaRequest->getSpaceName());
+        $applyRequest->setFileName($vodUploadMediaRequest->getFileName());
+        $resp = $this->upload($applyRequest, $vodUploadMediaRequest->getFilePath());
+        if ($resp[0] != 0) {
+            throw new Exception($resp[1]);
+        }
+        $request = new VodCommitUploadInfoRequest();
+        $request->setSpaceName($vodUploadMediaRequest->getSpaceName());
+        $request->setSessionKey($resp[2]);
+        $request->setCallbackArgs($vodUploadMediaRequest->getCallbackArgs());
+        $request->setFunctions($vodUploadMediaRequest->getFunctions());
+        try {
+            return $this->commitUploadInfo($request);
+        } catch (Throwable $e) {
+            throw $e;
+        }
+    }
+
+    public function uploadMaterial(VodUploadMaterialRequest $vodUploadMaterialRequest): VodCommitUploadInfoResponse
+    {
+        $applyRequest = new VodApplyUploadInfoRequest();
+        $applyRequest->setSpaceName($vodUploadMaterialRequest->getSpaceName());
+        $applyRequest->setFileType($vodUploadMaterialRequest->getFileType());
+        $applyRequest->setFileName($vodUploadMaterialRequest->getFileName());
+        $resp = $this->upload($applyRequest, $vodUploadMaterialRequest->getFilePath());
+        if ($resp[0] != 0) {
+            throw new Exception($resp[1]);
+        }
+        $request = new VodCommitUploadInfoRequest();
+        $request->setSpaceName($vodUploadMaterialRequest->getSpaceName());
+        $request->setSessionKey($resp[2]);
+        $request->setCallbackArgs($vodUploadMaterialRequest->getCallbackArgs());
+        $request->setFunctions($vodUploadMaterialRequest->getFunctions());
+        try {
+            return $this->commitUploadInfo($request);
+        } catch (Throwable $e) {
+            throw $e;
+        }
+    }
+
+
+    public function upload(VodApplyUploadInfoRequest $applyRequest, string $filePath): array
+    {
+        if (!file_exists($filePath)) {
+            return array(-1, "file not exists", "", "");
+        }
+        try {
+            $response = $this->applyUploadInfo($applyRequest);
+            if ($response->getResponseMetadata()->getError() != null) {
+                return array(-1, $response->getResponseMetadata()->serializeToJsonString(), "", "");
+            }
+
+            $uploadAddress = $response->getResult()->getData()->getUploadAddress();
+
+            $uploadHost = ($uploadAddress->getUploadHosts())[0];
+            $oid = ($uploadAddress->getStoreInfos())[0]->getStoreUri();
+            $session = $uploadAddress->getSessionKey();
+
+            $storeInfo = new VodStoreInfo();
+            $storeInfo->mergeFrom(($uploadAddress->getStoreInfos())[0]);
+            $respCode = $this->uploadFile($uploadHost, $storeInfo, $filePath);
+            if ($respCode != 0) {
+                return array(-1, "upload " . $filePath . " error", "", "");
+            }
+
+            return array(0, "", $session, $oid);
+        } catch (Throwable $e) {
+            return array(-1, $e->getMessage(), "", "");
+        }
+    }
+
+    public function uploadFile(string $uploadHost, VodStoreInfo $storeInfo, string $filePath): int
+    {
+        if (!file_exists($filePath)) {
+            return -1;
+        }
+
+        $oid = $storeInfo->getStoreUri();
+        $auth = $storeInfo->getAuth();
+
+        $handlerStack = HandlerStack::create(new CurlHandler());
+        $handlerStack->push(Middleware::retry($this->retryDecider(), $this->retryDelay()));
+
+        $timeout = 30.0;
+        $fileSize = filesize($filePath);
+
+        if ($fileSize > LargeFileSize) {
+            $timeout = 600.0;
+        }
+        $client = new Client([
+            'base_uri' => "http://" . $uploadHost,
+            'timeout' => $timeout,
+            'handler' => $handlerStack,
+        ]);
+
+        if ($fileSize < MinChunkSize) {
+            return $this->directUpload($oid, $auth, $filePath, $client);
+        } elseif ($fileSize > LargeFileSize) {
+            return $this->chunkUpload($oid, $auth, $filePath, true, $client);
+        } else {
+            return $this->chunkUpload($oid, $auth, $filePath, false, $client);
+        }
+    }
+
+    private function retryDecider(): \Closure
+    {
+        return function ($retries,
+                         Request $request,
+                         Response $response = null,
+                         RequestException $exception = null) {
+            if ($retries >= 3) {
+                return false;
+            }
+            return false;
+        };
+    }
+
+    private function retryDelay(): \Closure
+    {
+        return function ($num) {
+            return RetryMiddleware::exponentialDelay($num);
+        };
+    }
+
+    private function directUpload(string $oid, string $auth, string $filePath, Client $client): int
+    {
+        $content = file_get_contents($filePath);
+        $crc32 = sprintf("%08x", crc32($content));
+        $response = $client->put($oid, ["body" => $content, "headers" => ['Authorization' => $auth, 'Content-CRC32' => $crc32]]);
+        $directUploadResponse = json_decode((string)$response->getBody(), true);
+        if (!isset($directUploadResponse["success"]) || $directUploadResponse["success"] != 0) {
+            return -2;
+        }
+        return 0;
+    }
+
+    private function chunkUpload(string $oid, string $auth, string $filePath, bool $isLargeFile, Client $client): int
+    {
+        $uploadID = $this->initUploadPart($oid, $auth, $isLargeFile, $client);
+        if ($uploadID == "") {
+            return -1;
+        }
+        $fileSize = filesize($filePath);
+        $num = floor($fileSize / MinChunkSize);
+        $lastNum = $num - 1;
+        $fp = fopen($filePath, 'r');
+        $checkSum = [];
+        for ($i = 0; $i < $lastNum; $i++) {
+            $data = stream_get_contents($fp, MinChunkSize, $i * MinChunkSize);
+            $partNumber = $i;
+            if ($isLargeFile) {
+                $partNumber = $partNumber + 1;
+            }
+            $crc32 = $this->uploadPart($oid, $auth, $uploadID, $partNumber, $data, $isLargeFile, $client);
+            if ($crc32 == "") {
+                return -1;
+            }
+            $checkSum[] = $crc32;
+        }
+        $maxLength = $fileSize - $lastNum * MinChunkSize;
+        $data = stream_get_contents($fp, $maxLength, $lastNum * MinChunkSize);
+        if ($isLargeFile) {
+            $lastNum = $lastNum + 1;
+        }
+        $crc32 = $this->uploadPart($oid, $auth, $uploadID, $lastNum, $data, $isLargeFile, $client);
+        if ($crc32 == "") {
+            return -1;
+        }
+        $checkSum[] = $crc32;
+        return $this->uploadMergePart($oid, $auth, $uploadID, $checkSum, $isLargeFile, $client);
+    }
+
+    private function initUploadPart(string $oid, string $auth, bool $isLargeFile, Client $client)
+    {
+        $headers = ['Authorization' => $auth];
+        if ($isLargeFile) {
+            $headers[] = ['X-Storage-Mode' => 'gateway'];
+        }
+        $response = $client->put($oid . '?uploads', ['headers' => $headers]);
+        $initUploadResponse = json_decode((string)$response->getBody(), true);
+        if (!isset($initUploadResponse["success"]) || $initUploadResponse["success"] != 0) {
+            return "";
+        }
+        return $initUploadResponse['payload']['uploadID'];
+    }
+
+    private function uploadPart(string $oid, string $auth, string $uploadID, int $partNumber, $data, bool $isLargeFile, Client $client): string
+    {
+        $uri = sprintf("%s?partNumber=%d&uploadID=%s", $oid, $partNumber, $uploadID);
+        $crc32 = sprintf("%08x", crc32($data));
+        $headers = ['Authorization' => $auth, 'Content-CRC32' => $crc32];
+        if ($isLargeFile) {
+            $headers[] = ['X-Storage-Mode' => 'gateway'];
+        }
+        $response = $client->put($uri, ['headers' => $headers, 'body' => $data]);
+        $uploadPartResponse = json_decode((string)$response->getBody(), true);
+        if (!isset($uploadPartResponse["success"]) || $uploadPartResponse["success"] != 0) {
+            return "";
+        }
+        return $crc32;
+    }
+
+    private function uploadMergePart(string $oid, string $auth, string $uploadID, array $checkSum, bool $isLargeFile, Client $client): int
+    {
+        $uri = sprintf("%s?uploadID=%s", $oid, $uploadID);
+        $m = [];
+        for ($i = 0; $i < count($checkSum); $i++) {
+            $no = $i;
+            if ($isLargeFile) {
+                $no = $i + 1;
+            }
+            $m[] = sprintf("%d:%s", $no, $checkSum[$i]);
+        }
+        $body = implode(",", $m);
+        $headers = ['Authorization' => $auth];
+        if ($isLargeFile) {
+            $headers[] = ['X-Storage-Mode' => 'gateway'];
+        }
+        $response = $client->put($uri, ['headers' => $headers, 'body' => $body]);
+        $uploadPartResponse = json_decode((string)$response->getBody(), true);
+        if (!isset($uploadPartResponse["success"]) || $uploadPartResponse["success"] != 0) {
+            return -1;
+        }
+        return 0;
+    }
+
+
+}
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Upload/WorkflowInput.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Upload/WorkflowInput.php
new file mode 100755
index 0000000..2eb86cc
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Upload/WorkflowInput.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Volc\Service\Vod\Upload;
+
+class WorkflowInput
+{
+    public $TemplateId;
+
+    /**
+     * WorkflowInput constructor.
+     * @param $TemplateId
+     */
+    public function __construct($TemplateId)
+    {
+        $this->TemplateId = $TemplateId;
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/Vod.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Vod.php
new file mode 100755
index 0000000..59d9581
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/Vod.php
@@ -0,0 +1,2208 @@
+<?php
+# Code generated by protoc-gen-volcengine-sdk
+# source: Vod
+# DO NOT EDIT!
+
+namespace Volc\Service\Vod;
+
+use Exception;
+use Throwable;
+use Volc\Base\V4Curl;
+use Volc\Service\Vod\Models\Request\VodGetAllPlayInfoRequest;
+use Volc\Service\Vod\Models\Response\VodGetAllPlayInfoResponse;
+use Volc\Service\Vod\Models\Request\VodGetPlayInfoRequest;
+use Volc\Service\Vod\Models\Response\VodGetPlayInfoResponse;
+use Volc\Service\Vod\Models\Request\VodGetPrivateDrmPlayAuthRequest;
+use Volc\Service\Vod\Models\Response\VodGetPrivateDrmPlayAuthResponse;
+use Volc\Service\Vod\Models\Request\VodGetHlsDecryptionKeyRequest;
+use Volc\Service\Vod\Models\Response\VodGetHlsDecryptionKeyResponse;
+use Volc\Service\Vod\Models\Request\VodGetPlayInfoWithLiveTimeShiftSceneRequest;
+use Volc\Service\Vod\Models\Response\VodGetPlayInfoWithLiveTimeShiftSceneResponse;
+use Volc\Service\Vod\Models\Request\VodUrlUploadRequest;
+use Volc\Service\Vod\Models\Response\VodUrlUploadResponse;
+use Volc\Service\Vod\Models\Request\VodQueryUploadTaskInfoRequest;
+use Volc\Service\Vod\Models\Response\VodQueryUploadTaskInfoResponse;
+use Volc\Service\Vod\Models\Request\VodApplyUploadInfoRequest;
+use Volc\Service\Vod\Models\Response\VodApplyUploadInfoResponse;
+use Volc\Service\Vod\Models\Request\VodCommitUploadInfoRequest;
+use Volc\Service\Vod\Models\Response\VodCommitUploadInfoResponse;
+use Volc\Service\Vod\Models\Request\VodUpdateMediaInfoRequest;
+use Volc\Service\Vod\Models\Response\VodUpdateMediaInfoResponse;
+use Volc\Service\Vod\Models\Request\VodUpdateMediaPublishStatusRequest;
+use Volc\Service\Vod\Models\Response\VodUpdateMediaPublishStatusResponse;
+use Volc\Service\Vod\Models\Request\VodGetMediaInfosRequest;
+use Volc\Service\Vod\Models\Response\VodGetMediaInfosResponse;
+use Volc\Service\Vod\Models\Request\VodGetRecommendedPosterRequest;
+use Volc\Service\Vod\Models\Response\VodGetRecommendedPosterResponse;
+use Volc\Service\Vod\Models\Request\VodDeleteMediaRequest;
+use Volc\Service\Vod\Models\Response\VodDeleteMediaResponse;
+use Volc\Service\Vod\Models\Request\VodDeleteTranscodesRequest;
+use Volc\Service\Vod\Models\Response\VodDeleteTranscodesResponse;
+use Volc\Service\Vod\Models\Request\VodGetMediaListRequest;
+use Volc\Service\Vod\Models\Response\VodGetMediaListResponse;
+use Volc\Service\Vod\Models\Request\VodGetSubtitleInfoListRequest;
+use Volc\Service\Vod\Models\Response\VodGetSubtitleInfoListResponse;
+use Volc\Service\Vod\Models\Request\VodUpdateSubtitleStatusRequest;
+use Volc\Service\Vod\Models\Response\VodUpdateSubtitleStatusResponse;
+use Volc\Service\Vod\Models\Request\VodUpdateSubtitleInfoRequest;
+use Volc\Service\Vod\Models\Response\VodUpdateSubtitleInfoResponse;
+use Volc\Service\Vod\Models\Request\VodGetAuditFramesForAuditRequest;
+use Volc\Service\Vod\Models\Response\VodGetAuditFramesForAuditResponse;
+use Volc\Service\Vod\Models\Request\VodGetMLFramesForAuditRequest;
+use Volc\Service\Vod\Models\Response\VodGetMLFramesForAuditResponse;
+use Volc\Service\Vod\Models\Request\VodGetBetterFramesForAuditRequest;
+use Volc\Service\Vod\Models\Response\VodGetBetterFramesForAuditResponse;
+use Volc\Service\Vod\Models\Request\VodGetAudioInfoForAuditRequest;
+use Volc\Service\Vod\Models\Response\VodGetAudioInfoForAuditResponse;
+use Volc\Service\Vod\Models\Request\VodGetAutomaticSpeechRecognitionForAuditRequest;
+use Volc\Service\Vod\Models\Response\VodGetAutomaticSpeechRecognitionForAuditResponse;
+use Volc\Service\Vod\Models\Request\VodGetAudioEventDetectionForAuditRequest;
+use Volc\Service\Vod\Models\Response\VodGetAudioEventDetectionForAuditResponse;
+use Volc\Service\Vod\Models\Request\VodCreateVideoClassificationRequest;
+use Volc\Service\Vod\Models\Response\VodCreateVideoClassificationResponse;
+use Volc\Service\Vod\Models\Request\VodUpdateVideoClassificationRequest;
+use Volc\Service\Vod\Models\Response\VodUpdateVideoClassificationResponse;
+use Volc\Service\Vod\Models\Request\VodDeleteVideoClassificationRequest;
+use Volc\Service\Vod\Models\Response\VodDeleteVideoClassificationResponse;
+use Volc\Service\Vod\Models\Request\VodListVideoClassificationsRequest;
+use Volc\Service\Vod\Models\Response\VodListVideoClassificationsResponse;
+use Volc\Service\Vod\Models\Request\VodListSnapshotsRequest;
+use Volc\Service\Vod\Models\Response\VodListSnapshotsResponse;
+use Volc\Service\Vod\Models\Request\VodStartWorkflowRequest;
+use Volc\Service\Vod\Models\Response\VodStartWorkflowResponse;
+use Volc\Service\Vod\Models\Request\VodRetrieveTranscodeResultRequest;
+use Volc\Service\Vod\Models\Response\VodRetrieveTranscodeResultResponse;
+use Volc\Service\Vod\Models\Request\VodGetWorkflowExecutionStatusRequest;
+use Volc\Service\Vod\Models\Response\VodGetWorkflowExecutionStatusResponse;
+use Volc\Service\Vod\Models\Request\VodSubmitDirectEditTaskAsyncRequest;
+use Volc\Service\Vod\Models\Response\VodSubmitDirectEditTaskAsyncResponse;
+use Volc\Service\Vod\Models\Request\VodGetDirectEditResultRequest;
+use Volc\Service\Vod\Models\Response\VodGetDirectEditResultResponse;
+use Volc\Service\Vod\Models\Request\VodGetDirectEditProgressRequest;
+use Volc\Service\Vod\Models\Response\VodGetDirectEditProgressResponse;
+use Volc\Service\Vod\Models\Request\VodCreateSpaceRequest;
+use Volc\Service\Vod\Models\Response\VodCreateSpaceResponse;
+use Volc\Service\Vod\Models\Request\VodListSpaceRequest;
+use Volc\Service\Vod\Models\Response\VodListSpaceResponse;
+use Volc\Service\Vod\Models\Request\VodGetSpaceDetailRequest;
+use Volc\Service\Vod\Models\Response\VodGetSpaceDetailResponse;
+use Volc\Service\Vod\Models\Request\VodUpdateSpaceRequest;
+use Volc\Service\Vod\Models\Response\VodUpdateSpaceResponse;
+use Volc\Service\Vod\Models\Request\VodUpdateSpaceUploadConfigRequest;
+use Volc\Service\Vod\Models\Response\VodUpdateSpaceUploadConfigResponse;
+use Volc\Service\Vod\Models\Request\VodDescribeVodSpaceStorageDataRequest;
+use Volc\Service\Vod\Models\Response\VodDescribeVodSpaceStorageDataResponse;
+use Volc\Service\Vod\Models\Request\VodListDomainRequest;
+use Volc\Service\Vod\Models\Response\VodListDomainResponse;
+use Volc\Service\Vod\Models\Request\VodCreateCdnRefreshTaskRequest;
+use Volc\Service\Vod\Models\Response\VodCreateCdnRefreshTaskResponse;
+use Volc\Service\Vod\Models\Request\VodCreateCdnPreloadTaskRequest;
+use Volc\Service\Vod\Models\Response\VodCreateCdnPreloadTaskResponse;
+use Volc\Service\Vod\Models\Request\VodListCdnTasksRequest;
+use Volc\Service\Vod\Models\Response\VodListCdnTasksResponse;
+use Volc\Service\Vod\Models\Request\VodListCdnAccessLogRequest;
+use Volc\Service\Vod\Models\Response\VodListCdnAccessLogResponse;
+use Volc\Service\Vod\Models\Request\VodListCdnTopAccessUrlRequest;
+use Volc\Service\Vod\Models\Response\VodListCdnTopAccessUrlResponse;
+use Volc\Service\Vod\Models\Request\VodDescribeVodDomainBandwidthDataRequest;
+use Volc\Service\Vod\Models\Response\VodDescribeVodDomainBandwidthDataResponse;
+use Volc\Service\Vod\Models\Request\VodListCdnUsageDataRequest;
+use Volc\Service\Vod\Models\Response\VodCdnStatisticsCommonResponse;
+use Volc\Service\Vod\Models\Request\VodListCdnStatusDataRequest;
+use Volc\Service\Vod\Models\Request\VodDescribeIPInfoRequest;
+use Volc\Service\Vod\Models\Response\VodDescribeIPInfoResponse;
+use Volc\Service\Vod\Models\Request\VodDescribeVodDomainTrafficDataRequest;
+use Volc\Service\Vod\Models\Response\VodDescribeVodDomainTrafficDataResponse;
+use Volc\Service\Vod\Models\Request\VodListCdnPvDataRequest;
+use Volc\Service\Vod\Models\Request\VodAddCallbackSubscriptionRequest;
+use Volc\Service\Vod\Models\Response\VodAddCallbackSubscriptionResponse;
+use Volc\Service\Vod\Models\Request\VodSetCallbackEventRequest;
+use Volc\Service\Vod\Models\Response\VodSetCallbackEventResponse;
+use Volc\Service\Vod\Models\Request\VodGetSmartStrategyLitePlayInfoRequest;
+use Volc\Service\Vod\Models\Response\VodGetSmartStrategyLitePlayInfoResponse;
+use Volc\Service\Vod\Models\Request\VodGetAppInfoRequest;
+use Volc\Service\Vod\Models\Response\VodGetAppInfoResponse;
+
+/**
+ * Generated from protobuf service <code>vod/service/service_vod.proto</code>
+ */
+class Vod extends V4Curl
+{
+
+    protected $apiList;
+
+    public function __construct()
+    {
+        $this->region = func_get_arg(0);
+        $this->apiList = VodOption::$apiList;
+        parent::__construct($this->region);
+    }
+
+    /**
+     * @throws Exception
+     */
+    protected function getConfig(string $region): array
+    {
+        try {
+            return VodOption::getConfig($region);
+        } catch (Exception $e) {
+            print_r($e->getMessage());
+            print_r($e->getTraceAsString());
+            throw $e;
+        }
+    }
+
+    /**
+     * @throws Throwable
+     */
+    public function getPlayAuthToken(VodGetPlayInfoRequest $req, int $expireSeconds): string
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($expireSeconds > 0) {
+            $query["X-Expires"] = $expireSeconds;
+        }
+        $token = ["TokenVersion" => "V2"];
+        $token["GetPlayInfoToken"] = parse_url($this->getRequestUrl("GetPlayInfo", ['query' => $query]))['query'];
+        return base64_encode(json_encode($token));
+    }
+
+    /**
+     * @throws Throwable
+     */
+    public function getSubtitleAuthToken(VodGetSubtitleInfoListRequest $req, int $expireSeconds): string
+    {
+        try {
+            if ($req->getVid() == "") {
+                throw new Exception("传入的Vid为空");
+            }
+            $query = array("Vid" => $req->getVid());
+            $query["Status"] = "Published";
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($expireSeconds > 0) {
+            $query["X-Expires"] = $expireSeconds;
+        }
+        $token["GetSubtitleAuthToken"] = parse_url($this->getRequestUrl("GetSubtitleInfoList", ['query' => $query]))['query'];
+        return base64_encode(json_encode($token));
+    }
+
+    public function getUploadVideoAuth(): array
+    {
+        return $this->getUploadVideoAuthWithExpiredTime(60 * 60);
+    }
+
+    public function getUploadVideoAuthWithExpiredTime(int $expire): array
+    {
+        $actions = [ActionApplyUpload, ActionCommitUpload];
+        $resources = [];
+        $statement = $this->newAllowStatement($actions, $resources);
+        $policy = [
+            Statement => [$statement],
+        ];
+        return $this->signSts2($policy, $expire);
+    }
+
+    /**
+     * @throws Throwable
+     */
+    public function getSHA1HlsDrmAuthToken(int $expireSeconds): string
+    {
+        return $this->createHlsDrmAuthToken("HMAC-SHA1", $expireSeconds);
+    }
+
+    /**
+     * @throws Throwable
+     */
+    public function getPrivateDrmPlayAuthToken(VodGetPrivateDrmPlayAuthRequest $req, int $expireSeconds): string
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($expireSeconds > 0) {
+            $query["X-Expires"] = $expireSeconds;
+        }
+        return parse_url($this->getRequestUrl("GetPrivateDrmPlayAuth", ['query' => $query]))['query'];
+    }
+
+    /**
+     * GetAllPlayInfo.
+     *
+     * @param $req VodGetAllPlayInfoRequest
+     * @return VodGetAllPlayInfoResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function getAllPlayInfo(VodGetAllPlayInfoRequest $req): VodGetAllPlayInfoResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('GetAllPlayInfo', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodGetAllPlayInfoResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * GetPlayInfo.
+     *
+     * @param $req VodGetPlayInfoRequest
+     * @return VodGetPlayInfoResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function getPlayInfo(VodGetPlayInfoRequest $req): VodGetPlayInfoResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('GetPlayInfo', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodGetPlayInfoResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * GetPrivateDrmPlayAuth.
+     *
+     * @param $req VodGetPrivateDrmPlayAuthRequest
+     * @return VodGetPrivateDrmPlayAuthResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function getPrivateDrmPlayAuth(VodGetPrivateDrmPlayAuthRequest $req): VodGetPrivateDrmPlayAuthResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('GetPrivateDrmPlayAuth', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodGetPrivateDrmPlayAuthResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * GetHlsDecryptionKey.
+     *
+     * @param $req VodGetHlsDecryptionKeyRequest
+     * @return VodGetHlsDecryptionKeyResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function getHlsDecryptionKey(VodGetHlsDecryptionKeyRequest $req): VodGetHlsDecryptionKeyResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('GetHlsDecryptionKey', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodGetHlsDecryptionKeyResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * GetPlayInfoWithLiveTimeShiftScene.
+     *
+     * @param $req VodGetPlayInfoWithLiveTimeShiftSceneRequest
+     * @return VodGetPlayInfoWithLiveTimeShiftSceneResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function getPlayInfoWithLiveTimeShiftScene(VodGetPlayInfoWithLiveTimeShiftSceneRequest $req): VodGetPlayInfoWithLiveTimeShiftSceneResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('GetPlayInfoWithLiveTimeShiftScene', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodGetPlayInfoWithLiveTimeShiftSceneResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * UploadMediaByUrl.
+     *
+     * @param $req VodUrlUploadRequest
+     * @return VodUrlUploadResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function uploadMediaByUrl(VodUrlUploadRequest $req): VodUrlUploadResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('UploadMediaByUrl', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodUrlUploadResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * QueryUploadTaskInfo.
+     *
+     * @param $req VodQueryUploadTaskInfoRequest
+     * @return VodQueryUploadTaskInfoResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function queryUploadTaskInfo(VodQueryUploadTaskInfoRequest $req): VodQueryUploadTaskInfoResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('QueryUploadTaskInfo', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodQueryUploadTaskInfoResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * ApplyUploadInfo.
+     *
+     * @param $req VodApplyUploadInfoRequest
+     * @return VodApplyUploadInfoResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function applyUploadInfo(VodApplyUploadInfoRequest $req): VodApplyUploadInfoResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('ApplyUploadInfo', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodApplyUploadInfoResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * CommitUploadInfo.
+     *
+     * @param $req VodCommitUploadInfoRequest
+     * @return VodCommitUploadInfoResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function commitUploadInfo(VodCommitUploadInfoRequest $req): VodCommitUploadInfoResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('CommitUploadInfo', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodCommitUploadInfoResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * UpdateMediaInfo.
+     *
+     * @param $req VodUpdateMediaInfoRequest
+     * @return VodUpdateMediaInfoResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function updateMediaInfo(VodUpdateMediaInfoRequest $req): VodUpdateMediaInfoResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('UpdateMediaInfo', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodUpdateMediaInfoResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * UpdateMediaPublishStatus.
+     *
+     * @param $req VodUpdateMediaPublishStatusRequest
+     * @return VodUpdateMediaPublishStatusResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function updateMediaPublishStatus(VodUpdateMediaPublishStatusRequest $req): VodUpdateMediaPublishStatusResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('UpdateMediaPublishStatus', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodUpdateMediaPublishStatusResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * GetMediaInfos.
+     *
+     * @param $req VodGetMediaInfosRequest
+     * @return VodGetMediaInfosResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function getMediaInfos(VodGetMediaInfosRequest $req): VodGetMediaInfosResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('GetMediaInfos', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodGetMediaInfosResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * GetRecommendedPoster.
+     *
+     * @param $req VodGetRecommendedPosterRequest
+     * @return VodGetRecommendedPosterResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function getRecommendedPoster(VodGetRecommendedPosterRequest $req): VodGetRecommendedPosterResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('GetRecommendedPoster', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodGetRecommendedPosterResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * DeleteMedia.
+     *
+     * @param $req VodDeleteMediaRequest
+     * @return VodDeleteMediaResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function deleteMedia(VodDeleteMediaRequest $req): VodDeleteMediaResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('DeleteMedia', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodDeleteMediaResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * DeleteTranscodes.
+     *
+     * @param $req VodDeleteTranscodesRequest
+     * @return VodDeleteTranscodesResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function deleteTranscodes(VodDeleteTranscodesRequest $req): VodDeleteTranscodesResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('DeleteTranscodes', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodDeleteTranscodesResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * GetMediaList.
+     *
+     * @param $req VodGetMediaListRequest
+     * @return VodGetMediaListResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function getMediaList(VodGetMediaListRequest $req): VodGetMediaListResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('GetMediaList', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodGetMediaListResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * GetSubtitleInfoList.
+     *
+     * @param $req VodGetSubtitleInfoListRequest
+     * @return VodGetSubtitleInfoListResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function getSubtitleInfoList(VodGetSubtitleInfoListRequest $req): VodGetSubtitleInfoListResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('GetSubtitleInfoList', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodGetSubtitleInfoListResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * UpdateSubtitleStatus.
+     *
+     * @param $req VodUpdateSubtitleStatusRequest
+     * @return VodUpdateSubtitleStatusResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function updateSubtitleStatus(VodUpdateSubtitleStatusRequest $req): VodUpdateSubtitleStatusResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('UpdateSubtitleStatus', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodUpdateSubtitleStatusResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * UpdateSubtitleInfo.
+     *
+     * @param $req VodUpdateSubtitleInfoRequest
+     * @return VodUpdateSubtitleInfoResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function updateSubtitleInfo(VodUpdateSubtitleInfoRequest $req): VodUpdateSubtitleInfoResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('UpdateSubtitleInfo', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodUpdateSubtitleInfoResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * GetAuditFramesForAudit.
+     *
+     * @param $req VodGetAuditFramesForAuditRequest
+     * @return VodGetAuditFramesForAuditResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function getAuditFramesForAudit(VodGetAuditFramesForAuditRequest $req): VodGetAuditFramesForAuditResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('GetAuditFramesForAudit', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodGetAuditFramesForAuditResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * GetMLFramesForAudit.
+     *
+     * @param $req VodGetMLFramesForAuditRequest
+     * @return VodGetMLFramesForAuditResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function getMLFramesForAudit(VodGetMLFramesForAuditRequest $req): VodGetMLFramesForAuditResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('GetMLFramesForAudit', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodGetMLFramesForAuditResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * GetBetterFramesForAudit.
+     *
+     * @param $req VodGetBetterFramesForAuditRequest
+     * @return VodGetBetterFramesForAuditResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function getBetterFramesForAudit(VodGetBetterFramesForAuditRequest $req): VodGetBetterFramesForAuditResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('GetBetterFramesForAudit', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodGetBetterFramesForAuditResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * GetAudioInfoForAudit.
+     *
+     * @param $req VodGetAudioInfoForAuditRequest
+     * @return VodGetAudioInfoForAuditResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function getAudioInfoForAudit(VodGetAudioInfoForAuditRequest $req): VodGetAudioInfoForAuditResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('GetAudioInfoForAudit', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodGetAudioInfoForAuditResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * GetAutomaticSpeechRecognitionForAudit.
+     *
+     * @param $req VodGetAutomaticSpeechRecognitionForAuditRequest
+     * @return VodGetAutomaticSpeechRecognitionForAuditResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function getAutomaticSpeechRecognitionForAudit(VodGetAutomaticSpeechRecognitionForAuditRequest $req): VodGetAutomaticSpeechRecognitionForAuditResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('GetAutomaticSpeechRecognitionForAudit', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodGetAutomaticSpeechRecognitionForAuditResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * GetAudioEventDetectionForAudit.
+     *
+     * @param $req VodGetAudioEventDetectionForAuditRequest
+     * @return VodGetAudioEventDetectionForAuditResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function getAudioEventDetectionForAudit(VodGetAudioEventDetectionForAuditRequest $req): VodGetAudioEventDetectionForAuditResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('GetAudioEventDetectionForAudit', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodGetAudioEventDetectionForAuditResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * CreateVideoClassification.
+     *
+     * @param $req VodCreateVideoClassificationRequest
+     * @return VodCreateVideoClassificationResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function createVideoClassification(VodCreateVideoClassificationRequest $req): VodCreateVideoClassificationResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('CreateVideoClassification', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodCreateVideoClassificationResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * UpdateVideoClassification.
+     *
+     * @param $req VodUpdateVideoClassificationRequest
+     * @return VodUpdateVideoClassificationResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function updateVideoClassification(VodUpdateVideoClassificationRequest $req): VodUpdateVideoClassificationResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('UpdateVideoClassification', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodUpdateVideoClassificationResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * DeleteVideoClassification.
+     *
+     * @param $req VodDeleteVideoClassificationRequest
+     * @return VodDeleteVideoClassificationResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function deleteVideoClassification(VodDeleteVideoClassificationRequest $req): VodDeleteVideoClassificationResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('DeleteVideoClassification', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodDeleteVideoClassificationResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * ListVideoClassifications.
+     *
+     * @param $req VodListVideoClassificationsRequest
+     * @return VodListVideoClassificationsResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function listVideoClassifications(VodListVideoClassificationsRequest $req): VodListVideoClassificationsResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('ListVideoClassifications', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodListVideoClassificationsResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * ListSnapshots.
+     *
+     * @param $req VodListSnapshotsRequest
+     * @return VodListSnapshotsResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function listSnapshots(VodListSnapshotsRequest $req): VodListSnapshotsResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('ListSnapshots', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodListSnapshotsResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * StartWorkflow.
+     *
+     * @param $req VodStartWorkflowRequest
+     * @return VodStartWorkflowResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function startWorkflow(VodStartWorkflowRequest $req): VodStartWorkflowResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('StartWorkflow', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodStartWorkflowResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * RetrieveTranscodeResult.
+     *
+     * @param $req VodRetrieveTranscodeResultRequest
+     * @return VodRetrieveTranscodeResultResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function retrieveTranscodeResult(VodRetrieveTranscodeResultRequest $req): VodRetrieveTranscodeResultResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('RetrieveTranscodeResult', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodRetrieveTranscodeResultResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * GetWorkflowExecution.
+     *
+     * @param $req VodGetWorkflowExecutionStatusRequest
+     * @return VodGetWorkflowExecutionStatusResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function GetWorkflowExecution(VodGetWorkflowExecutionStatusRequest $req): VodGetWorkflowExecutionStatusResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('GetWorkflowExecution', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodGetWorkflowExecutionStatusResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * SubmitDirectEditTaskAsync.
+     *
+     * @param $req VodSubmitDirectEditTaskAsyncRequest
+     * @return VodSubmitDirectEditTaskAsyncResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function submitDirectEditTaskAsync(VodSubmitDirectEditTaskAsyncRequest $req): VodSubmitDirectEditTaskAsyncResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $query["EditParam"] = json_decode($req->getEditParam());
+            $response = $this->request('SubmitDirectEditTaskAsync', ['json' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodSubmitDirectEditTaskAsyncResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * GetDirectEditResult.
+     *
+     * @param $req VodGetDirectEditResultRequest
+     * @return VodGetDirectEditResultResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function getDirectEditResult(VodGetDirectEditResultRequest $req): VodGetDirectEditResultResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $query["ReqIds"] = json_decode($query["ReqIds"]);
+            $response = $this->request('GetDirectEditResult', ['json' => $query]);
+            $json_decode = json_decode($response->getBody());
+            $results = null;
+            if (!empty($json_decode->Result)) {
+                $results = $json_decode->Result;
+                foreach ($results as $key => $result) {
+                    $result->EditParam = json_encode($result->EditParam);
+                }
+            }
+
+            $resp = new class($json_decode->ResponseMetadata, $results) {
+                public function __construct($ResponseMetadata, $Result)
+                {
+                    $this->ResponseMetadata = $ResponseMetadata;
+                    $this->Result = $Result;
+                }
+
+                public $ResponseMetadata = null;
+                public $Result = null;
+
+                public function getBody()
+                {
+                    return json_encode($this);
+                }
+            };
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodGetDirectEditResultResponse();
+        try {
+            $respData = VodUtils::parseResponseData($resp, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * GetDirectEditProgress.
+     *
+     * @param $req VodGetDirectEditProgressRequest
+     * @return VodGetDirectEditProgressResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function getDirectEditProgress(VodGetDirectEditProgressRequest $req): VodGetDirectEditProgressResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('GetDirectEditProgress', ['query' => $query]);
+            $json_decode = json_decode($response->getBody());
+            $res = null;
+            if (!empty($json_decode->Result)) {
+                $res = $json_decode->Result;
+            }
+            $resp = new class($json_decode->ResponseMetadata, $res) {
+                public function __construct($ResponseMetadata, $Result)
+                {
+                    $this->ResponseMetadata = $ResponseMetadata;
+                    $this->Result = new class($Result) {
+                        public $Result;
+
+                        public function __construct($Result)
+                        {
+                            $this->Result = $Result;
+                        }
+                    };
+                }
+
+                public $ResponseMetadata = null;
+                public $Result = null;
+
+                public function getBody()
+                {
+                    return json_encode($this);
+                }
+            };
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodGetDirectEditProgressResponse();
+        try {
+            $respData = VodUtils::parseResponseData($resp, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+
+    /**
+     * CreateSpace.
+     *
+     * @param $req VodCreateSpaceRequest
+     * @return VodCreateSpaceResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function createSpace(VodCreateSpaceRequest $req): VodCreateSpaceResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('CreateSpace', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodCreateSpaceResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * ListSpace.
+     *
+     * @param $req VodListSpaceRequest
+     * @return VodListSpaceResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function listSpace(VodListSpaceRequest $req): VodListSpaceResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('ListSpace', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodListSpaceResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * GetSpaceDetail.
+     *
+     * @param $req VodGetSpaceDetailRequest
+     * @return VodGetSpaceDetailResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function getSpaceDetail(VodGetSpaceDetailRequest $req): VodGetSpaceDetailResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('GetSpaceDetail', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodGetSpaceDetailResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * UpdateSpace.
+     *
+     * @param $req VodUpdateSpaceRequest
+     * @return VodUpdateSpaceResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function updateSpace(VodUpdateSpaceRequest $req): VodUpdateSpaceResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('UpdateSpace', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodUpdateSpaceResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * UpdateSpaceUploadConfig.
+     *
+     * @param $req VodUpdateSpaceUploadConfigRequest
+     * @return VodUpdateSpaceUploadConfigResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function updateSpaceUploadConfig(VodUpdateSpaceUploadConfigRequest $req): VodUpdateSpaceUploadConfigResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('UpdateSpaceUploadConfig', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodUpdateSpaceUploadConfigResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * DescribeVodSpaceStorageData.
+     *
+     * @param $req VodDescribeVodSpaceStorageDataRequest
+     * @return VodDescribeVodSpaceStorageDataResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function describeVodSpaceStorageData(VodDescribeVodSpaceStorageDataRequest $req): VodDescribeVodSpaceStorageDataResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('DescribeVodSpaceStorageData', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodDescribeVodSpaceStorageDataResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * ListDomain.
+     *
+     * @param $req VodListDomainRequest
+     * @return VodListDomainResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function listDomain(VodListDomainRequest $req): VodListDomainResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('ListDomain', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodListDomainResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * CreateCdnRefreshTask.
+     *
+     * @param $req VodCreateCdnRefreshTaskRequest
+     * @return VodCreateCdnRefreshTaskResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function createCdnRefreshTask(VodCreateCdnRefreshTaskRequest $req): VodCreateCdnRefreshTaskResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('CreateCdnRefreshTask', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodCreateCdnRefreshTaskResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * CreateCdnPreloadTask.
+     *
+     * @param $req VodCreateCdnPreloadTaskRequest
+     * @return VodCreateCdnPreloadTaskResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function createCdnPreloadTask(VodCreateCdnPreloadTaskRequest $req): VodCreateCdnPreloadTaskResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('CreateCdnPreloadTask', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodCreateCdnPreloadTaskResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * ListCdnTasks.
+     *
+     * @param $req VodListCdnTasksRequest
+     * @return VodListCdnTasksResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function listCdnTasks(VodListCdnTasksRequest $req): VodListCdnTasksResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('ListCdnTasks', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodListCdnTasksResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * ListCdnAccessLog.
+     *
+     * @param $req VodListCdnAccessLogRequest
+     * @return VodListCdnAccessLogResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function listCdnAccessLog(VodListCdnAccessLogRequest $req): VodListCdnAccessLogResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('ListCdnAccessLog', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodListCdnAccessLogResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * ListCdnTopAccessUrl.
+     *
+     * @param $req VodListCdnTopAccessUrlRequest
+     * @return VodListCdnTopAccessUrlResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function listCdnTopAccessUrl(VodListCdnTopAccessUrlRequest $req): VodListCdnTopAccessUrlResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('ListCdnTopAccessUrl', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodListCdnTopAccessUrlResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * DescribeVodDomainBandwidthData.
+     *
+     * @param $req VodDescribeVodDomainBandwidthDataRequest
+     * @return VodDescribeVodDomainBandwidthDataResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function describeVodDomainBandwidthData(VodDescribeVodDomainBandwidthDataRequest $req): VodDescribeVodDomainBandwidthDataResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('DescribeVodDomainBandwidthData', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodDescribeVodDomainBandwidthDataResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * ListCdnUsageData.
+     *
+     * @param $req VodListCdnUsageDataRequest
+     * @return VodCdnStatisticsCommonResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function listCdnUsageData(VodListCdnUsageDataRequest $req): VodCdnStatisticsCommonResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('ListCdnUsageData', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodCdnStatisticsCommonResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * ListCdnStatusData.
+     *
+     * @param $req VodListCdnStatusDataRequest
+     * @return VodCdnStatisticsCommonResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function listCdnStatusData(VodListCdnStatusDataRequest $req): VodCdnStatisticsCommonResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('ListCdnStatusData', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodCdnStatisticsCommonResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * DescribeIpInfo.
+     *
+     * @param $req VodDescribeIPInfoRequest
+     * @return VodDescribeIPInfoResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function describeIpInfo(VodDescribeIPInfoRequest $req): VodDescribeIPInfoResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('DescribeIpInfo', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodDescribeIPInfoResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * DescribeVodDomainTrafficData.
+     *
+     * @param $req VodDescribeVodDomainTrafficDataRequest
+     * @return VodDescribeVodDomainTrafficDataResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function describeVodDomainTrafficData(VodDescribeVodDomainTrafficDataRequest $req): VodDescribeVodDomainTrafficDataResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('DescribeVodDomainTrafficData', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodDescribeVodDomainTrafficDataResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * ListCdnPvData.
+     *
+     * @param $req VodListCdnPvDataRequest
+     * @return VodCdnStatisticsCommonResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function listCdnPvData(VodListCdnPvDataRequest $req): VodCdnStatisticsCommonResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('ListCdnPvData', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodCdnStatisticsCommonResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * AddCallbackSubscription.
+     *
+     * @param $req VodAddCallbackSubscriptionRequest
+     * @return VodAddCallbackSubscriptionResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function addCallbackSubscription(VodAddCallbackSubscriptionRequest $req): VodAddCallbackSubscriptionResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('AddCallbackSubscription', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodAddCallbackSubscriptionResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * SetCallbackEvent.
+     *
+     * @param $req VodSetCallbackEventRequest
+     * @return VodSetCallbackEventResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function setCallbackEvent(VodSetCallbackEventRequest $req): VodSetCallbackEventResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('SetCallbackEvent', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodSetCallbackEventResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * GetSmartStrategyLitePlayInfo.
+     *
+     * @param $req VodGetSmartStrategyLitePlayInfoRequest
+     * @return VodGetSmartStrategyLitePlayInfoResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function getSmartStrategyLitePlayInfo(VodGetSmartStrategyLitePlayInfoRequest $req): VodGetSmartStrategyLitePlayInfoResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('GetSmartStrategyLitePlayInfo', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodGetSmartStrategyLitePlayInfoResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+    /**
+     * GetAppInfo.
+     *
+     * @param $req VodGetAppInfoRequest
+     * @return VodGetAppInfoResponse
+     * @throws Exception the exception
+     * @throws Throwable the exception
+     */
+    public function getAppInfo(VodGetAppInfoRequest $req): VodGetAppInfoResponse
+    {
+        try {
+            $query = VodUtils::formatRequestParam($req);
+            $response = $this->request('GetAppInfo', ['query' => $query]);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        if ($response->getStatusCode() != 200) {
+            echo $response->getStatusCode(), "\n";
+            echo $response->getBody()->getContents(), "\n";
+        }
+        $respData = new VodGetAppInfoResponse();
+        try {
+            $respData = VodUtils::parseResponseData($response, $respData);
+        } catch (Exception $e) {
+            throw $e;
+        } catch (Throwable $t) {
+            throw $t;
+        }
+        return $respData;
+    }
+
+}  // end of service
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/VodOption.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/VodOption.php
new file mode 100755
index 0000000..d52ad37
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/VodOption.php
@@ -0,0 +1,630 @@
+<?php
+
+namespace Volc\Service\Vod;
+
+
+use Exception;
+
+const ActionApplyUpload = "vod:ApplyUploadInfo";
+const ActionCommitUpload = "vod:CommitUploadInfo";
+const Statement = "Statement";
+
+
+class VodOption
+{
+    public static $apiList = [
+        // **********************************************************************
+        // 播放
+        // **********************************************************************
+        'GetPlayInfo' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetPlayInfo',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'GetAllPlayInfo' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetAllPlayInfo',
+                    'Version' => '2022-01-01',
+                ],
+            ]
+        ],
+        'GetPrivateDrmPlayAuth' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetPrivateDrmPlayAuth',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'GetHlsDecryptionKey' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetHlsDecryptionKey',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'GetPlayInfoWithLiveTimeShiftScene' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetPlayInfoWithLiveTimeShiftScene',
+                    'Version' => '2021-11-01',
+                ],
+            ]
+        ],
+        // **********************************************************************
+        // 上传
+        // **********************************************************************
+        'UploadMediaByUrl' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'UploadMediaByUrl',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'QueryUploadTaskInfo' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'QueryUploadTaskInfo',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'ApplyUploadInfo' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'ApplyUploadInfo',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'CommitUploadInfo' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'timeout' => 8.0,
+                'query' => [
+                    'Action' => 'CommitUploadInfo',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        // **********************************************************************
+        // 媒资
+        // **********************************************************************
+        'UpdateMediaInfo' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'UpdateMediaInfo',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'UpdateMediaPublishStatus' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'UpdateMediaPublishStatus',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'GetMediaInfos' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetMediaInfos',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'GetRecommendedPoster' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetRecommendedPoster',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DeleteMedia' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'DeleteMedia',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DeleteTranscodes' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'DeleteTranscodes',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'GetMediaList' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetMediaList',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'GetSubtitleInfoList' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetSubtitleInfoList',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'UpdateSubtitleStatus' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'UpdateSubtitleStatus',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'UpdateSubtitleInfo' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'UpdateSubtitleInfo',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'GetAuditFramesForAudit' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetAuditFramesForAudit',
+                    'Version' => '2021-11-01',
+                ],
+            ]
+        ],
+        'GetMLFramesForAudit' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetMLFramesForAudit',
+                    'Version' => '2021-11-01',
+                ],
+            ]
+        ],
+        'GetBetterFramesForAudit' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetBetterFramesForAudit',
+                    'Version' => '2021-11-01',
+                ],
+            ]
+        ],
+        'GetAudioInfoForAudit' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetAudioInfoForAudit',
+                    'Version' => '2021-11-01',
+                ],
+            ]
+        ],
+        'GetAutomaticSpeechRecognitionForAudit' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetAutomaticSpeechRecognitionForAudit',
+                    'Version' => '2021-11-01',
+                ],
+            ]
+        ],
+        'GetAudioEventDetectionForAudit' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetAudioEventDetectionForAudit',
+                    'Version' => '2021-11-01',
+                ],
+            ]
+        ],
+        'CreateVideoClassification' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'CreateVideoClassification',
+                    'Version' => '2021-01-01',
+                ],
+            ]
+        ],
+        'UpdateVideoClassification' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'UpdateVideoClassification',
+                    'Version' => '2021-01-01',
+                ],
+            ]
+        ],
+        'DeleteVideoClassification' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'DeleteVideoClassification',
+                    'Version' => '2021-01-01',
+                ],
+            ]
+        ],
+        'ListVideoClassifications' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListVideoClassifications',
+                    'Version' => '2021-01-01',
+                ],
+            ]
+        ],
+        'ListSnapshots' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListSnapshots',
+                    'Version' => '2021-01-01',
+                ],
+            ]
+        ],
+        // **********************************************************************
+        // 转码
+        // **********************************************************************
+        'StartWorkflow' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'StartWorkflow',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'RetrieveTranscodeResult' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'RetrieveTranscodeResult',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'GetWorkflowExecution' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetWorkflowExecution',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        // **********************************************************************
+        // 视频编辑
+        // **********************************************************************
+        'SubmitDirectEditTaskAsync' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'SubmitDirectEditTaskAsync',
+                    'Version' => '2018-01-01',
+                ],
+            ]
+        ],
+        'GetDirectEditResult' => [
+            'url' => '/',
+            'method' => 'post',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetDirectEditResult',
+                    'Version' => '2018-01-01',
+                ],
+            ]
+        ],
+        'GetDirectEditProgress' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetDirectEditProgress',
+                    'Version' => '2018-01-01',
+                ],
+            ]
+        ],
+        // **********************************************************************
+        // 空间管理
+        // **********************************************************************
+        'CreateSpace' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'CreateSpace',
+                    'Version' => '2021-01-01',
+                ],
+            ]
+        ],
+        'ListSpace' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListSpace',
+                    'Version' => '2021-01-01',
+                ],
+            ]
+        ],
+        'GetSpaceDetail' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'GetSpaceDetail',
+                    'Version' => '2022-01-01',
+                ],
+            ]
+        ],
+        'UpdateSpace' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'UpdateSpace',
+                    'Version' => '2021-01-01',
+                ],
+            ]
+        ],
+        'UpdateSpaceUploadConfig' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'UpdateSpaceUploadConfig',
+                    'Version' => '2022-01-01',
+                ],
+            ]
+        ],
+        'DescribeVodSpaceStorageData' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeVodSpaceStorageData',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        // **********************************************************************
+        // 分发加速管理
+        // **********************************************************************
+        'ListDomain' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListDomain',
+                    'Version' => '2021-01-01',
+                ],
+            ]
+        ],
+        'CreateCdnRefreshTask' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'CreateCdnRefreshTask',
+                    'Version' => '2021-01-01',
+                ],
+            ]
+        ],
+        'CreateCdnPreloadTask' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'CreateCdnPreloadTask',
+                    'Version' => '2021-01-01',
+                ],
+            ]
+        ],
+        'ListCdnTasks' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListCdnTasks',
+                    'Version' => '2022-01-01',
+                ],
+            ]
+        ],
+        'ListCdnAccessLog' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListCdnAccessLog',
+                    'Version' => '2022-01-01',
+                ],
+            ]
+        ],
+        'ListCdnTopAccessUrl' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListCdnTopAccessUrl',
+                    'Version' => '2022-01-01',
+                ],
+            ]
+        ],
+        'DescribeVodDomainBandwidthData' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeVodDomainBandwidthData',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'DescribeVodDomainTrafficData' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeVodDomainTrafficData',
+                    'Version' => '2020-08-01',
+                ],
+            ]
+        ],
+        'ListCdnUsageData' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListCdnUsageData',
+                    'Version' => '2022-01-01',
+                ],
+            ]
+        ],
+        'ListCdnStatusData' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListCdnStatusData',
+                    'Version' => '2022-01-01',
+                ],
+            ]
+        ],
+        'DescribeIpInfo' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'DescribeIpInfo',
+                    'Version' => '2022-01-01',
+                ],
+            ]
+        ],
+        'ListCdnPvData' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'ListCdnPvData',
+                    'Version' => '2022-01-01',
+                ],
+            ]
+        ],
+
+        // **********************************************************************
+        // 回调管理
+        // **********************************************************************
+        'AddCallbackSubscription' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'AddCallbackSubscription',
+                    'Version' => '2021-12-01',
+                ],
+            ]
+        ],
+        'SetCallbackEvent' => [
+            'url' => '/',
+            'method' => 'get',
+            'config' => [
+                'query' => [
+                    'Action' => 'SetCallbackEvent',
+                    'Version' => '2022-01-01',
+                ],
+            ]
+        ],
+    ];
+
+    /**
+     * @throws Exception
+     */
+    public static function getConfig(string $region = '')
+    {
+        switch ($region) {
+            case 'cn-north-1':
+                $config = [
+                    'host' => 'https://vod.volcengineapi.com',
+                    'config' => [
+                        'timeout' => 5.0,
+                        'headers' => [
+                            'Accept' => 'application/json',
+                        ],
+                        'v4_credentials' => [
+                            'region' => 'cn-north-1',
+                            'service' => 'vod',
+                        ],
+                    ],
+                ];
+                break;
+            default:
+                throw new Exception("Cant find the region, please check it carefully");
+        }
+        return $config;
+    }
+}
\ No newline at end of file
diff --git a/vendor/volcengine/volc-sdk-php/src/Service/Vod/VodUtils.php b/vendor/volcengine/volc-sdk-php/src/Service/Vod/VodUtils.php
new file mode 100755
index 0000000..538e88c
--- /dev/null
+++ b/vendor/volcengine/volc-sdk-php/src/Service/Vod/VodUtils.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace Volc\Service\Vod;
+
+use Exception;
+use Throwable;
+
+class VodUtils {
+
+    public static function formatRequestParam($req): array
+    {
+        try {
+            $jsonData = $req->serializeToJsonString();
+            $query = json_decode($jsonData, true);
+            foreach ($query as $key => $value) {
+                switch (gettype($query[$key])) {
+                    case 'integer':
+                    case 'string':
+                    case 'double':
+                    case 'NULL':
+                    case 'boolean':
+                        break;
+                    default:
+                        $d = json_encode($value);
+                        $query[$key] = $d;
+                }
+            }
+        } catch (Exception $e) {
+            echo $e, "\n";
+            throw $e;
+        } catch (Throwable $t) {
+            echo $t, "\n";
+            throw $t;
+        }
+        return $query;
+    }
+
+    public static function parseResponseData($response, $respData)
+    {
+        try {
+            $respData->mergeFromJsonString($response->getBody(), true);
+        } catch (Exception $e) {
+            echo $e, "\n";
+            if ($respData == null || $respData->getResponseMetadata() == null) {
+                throw new Exception($response->getReasonPhrase());
+            }
+        } catch (Throwable $t) {
+            echo $t, "\n";
+            if ($respData == null || $respData->getResponseMetadata() == null) {
+                throw new Exception($response->getReasonPhrase());
+            }
+        }
+        return $respData;
+    }
+
+}
diff --git a/vendor/workerman/channel/README.md b/vendor/workerman/channel/README.md
new file mode 100755
index 0000000..ba3b760
--- /dev/null
+++ b/vendor/workerman/channel/README.md
@@ -0,0 +1,49 @@
+# Channel
+基于订阅的多进程通讯组件,用于workerman进程间通讯或者服务器集群通讯,类似redis订阅发布机制。基于workerman开发。
+
+# 手册地址
+[Channel手册](http://doc3.workerman.net/component/channel.html)
+
+# 服务端
+```php
+use Workerman\Worker;
+
+$channel_server = new Channel\Server('0.0.0.0', 2206);
+
+if(!defined('GLOBAL_START'))
+{
+    Worker::runAll();
+}
+```
+
+# 客户端
+```php
+use Workerman\Worker;
+
+$worker = new Worker(....);
+$worker->onWorkerStart = function()
+{
+    // Channel客户端连接到Channel服务端
+    Channel\Client::connect('<Channel服务端ip>', 2206);
+    // 要订阅的事件名称(名称可以为任意的数字和字符串组合)
+    $event_name = 'event_xxxx';
+    // 订阅某个自定义事件并注册回调,收到事件后会自动触发此回调
+    Channel\Client::on($event_name, function($event_data){
+        var_dump($event_data);
+    });
+};
+$worker->onMessage = function($connection, $data)
+{
+    // 要发布的事件名称
+    $event_name = 'event_xxxx';
+    // 事件数据(数据格式可以为数字、字符串、数组),会传递给客户端回调函数作为参数
+    $event_data = array('some data.', 'some data..');
+    // 发布某个自定义事件,订阅这个事件的客户端会收到事件数据,并触发客户端对应的事件回调
+    Channel\Client::publish($event_name, $event_data);
+};
+
+if(!defined('GLOBAL_START'))
+{
+    Worker::runAll();
+}
+````
diff --git a/vendor/workerman/channel/composer.json b/vendor/workerman/channel/composer.json
new file mode 100755
index 0000000..31279fe
--- /dev/null
+++ b/vendor/workerman/channel/composer.json
@@ -0,0 +1,12 @@
+{
+    "name"  : "workerman/channel",
+    "type"  : "library",
+    "homepage": "http://www.workerman.net",
+    "license" : "MIT",
+    "require": {
+        "workerman/workerman" : ">=3.3.0"
+    },
+    "autoload": {
+        "psr-4": {"Channel\\": "./src"}
+    }
+}
diff --git a/vendor/workerman/channel/src/Client.php b/vendor/workerman/channel/src/Client.php
new file mode 100755
index 0000000..215929d
--- /dev/null
+++ b/vendor/workerman/channel/src/Client.php
@@ -0,0 +1,275 @@
+<?php
+namespace Channel;
+use Workerman\Lib\Timer;
+use Workerman\Connection\AsyncTcpConnection;
+
+/**
+ * Channel/Client
+ * @version 1.0.5
+ */
+class Client
+{
+    /**
+     * onMessage.
+     * @var callback
+     */
+    public static $onMessage = null;
+
+    /**
+     * onConnect
+     * @var callback
+     */
+    public static $onConnect = null;
+
+    /**
+     * onClose
+     * @var callback
+     */
+    public static $onClose = null;
+
+    /**
+     * Connction to channel server.
+     * @var TcpConnection
+     */
+    protected static $_remoteConnection = null;
+
+    /**
+     * Channel server ip.
+     * @var string
+     */
+    protected static $_remoteIp = null;
+
+    /**
+     * Channel server port.
+     * @var int
+     */
+    protected static $_remotePort = null;
+
+    /**
+     * Reconnect timer.
+     * @var Timer
+     */
+    protected static $_reconnectTimer = null;
+
+    /**
+     * Ping timer.
+     * @var Timer
+     */
+    protected static $_pingTimer = null;
+
+    /**
+     * All event callback.
+     * @var array
+     */
+    protected static $_events = array();
+
+    /**
+     * @var bool
+     */
+    protected static $_isWorkermanEnv = true;
+
+    /**
+     * Ping interval.
+     * @var int
+     */
+    public static $pingInterval = 25;
+
+    /**
+     * Connect to channel server
+     * @param string $ip
+     * @param int $port
+     * @return void
+     */
+    public static function connect($ip = '127.0.0.1', $port = 2206)
+    {
+        if(!self::$_remoteConnection)
+        {
+            self::$_remoteIp = $ip;
+            self::$_remotePort = $port;
+            if (PHP_SAPI !== 'cli' || !class_exists('Workerman\Worker', false)) {
+                self::$_isWorkermanEnv = false;
+            }
+            // For workerman environment.
+            if (self::$_isWorkermanEnv) {
+                self::$_remoteConnection = new AsyncTcpConnection('frame://' . self::$_remoteIp . ':' . self::$_remotePort);
+                self::$_remoteConnection->onClose = 'Channel\Client::onRemoteClose';
+                self::$_remoteConnection->onConnect = 'Channel\Client::onRemoteConnect';
+                self::$_remoteConnection->onMessage = 'Channel\Client::onRemoteMessage';
+                self::$_remoteConnection->connect();
+
+                if (empty(self::$_pingTimer)) {
+                    self::$_pingTimer = Timer::add(self::$pingInterval, 'Channel\Client::ping');
+                }
+                // Not workerman environment.
+            } else {
+                self::$_remoteConnection = stream_socket_client('tcp://'.self::$_remoteIp.':'.self::$_remotePort, $code, $message, 5);
+                if (!self::$_remoteConnection) {
+                    throw new \Exception($message);
+                }
+            }
+        }
+    }
+
+    /**
+     * onRemoteMessage.
+     * @param TcpConnection $connection
+     * @param string $data
+     * @throws \Exception
+     */
+    public static function onRemoteMessage($connection, $data)
+    {
+        $data = unserialize($data);
+        $event = $data['channel'];
+        $event_data = $data['data'];
+        if(!empty(self::$_events[$event]))
+        {
+            call_user_func(self::$_events[$event], $event_data);
+        }
+        elseif(!empty(Client::$onMessage))
+        {
+            call_user_func(Client::$onMessage, $event, $event_data);
+        }
+        else
+        {
+            throw new \Exception("event:$event have not callback");
+        }
+    }
+
+    /**
+     * Ping.
+     * @return void
+     */
+    public static function ping()
+    {
+        if(self::$_remoteConnection)
+        {
+            self::$_remoteConnection->send('');
+        }
+    }
+
+    /**
+     * onRemoteClose.
+     * @return void
+     */
+    public static function onRemoteClose()
+    {
+        echo "Waring channel connection closed and try to reconnect\n";
+        self::$_remoteConnection = null;
+        self::clearTimer();
+        self::$_reconnectTimer = Timer::add(1, 'Channel\Client::connect', array(self::$_remoteIp, self::$_remotePort));
+        if (self::$onClose) {
+            call_user_func(Client::$onClose);
+        }
+    }
+
+    /**
+     * onRemoteConnect.
+     * @return void
+     */
+    public static function onRemoteConnect()
+    {
+        $all_event_names = array_keys(self::$_events);
+        if($all_event_names)
+        {
+            self::subscribe($all_event_names);
+        }
+        self::clearTimer();
+
+        if (self::$onConnect) {
+            call_user_func(Client::$onConnect);
+        }
+    }
+
+    /**
+     * clearTimer.
+     * @return void
+     */
+    public static function clearTimer()
+    {
+        if (!self::$_isWorkermanEnv) {
+            throw new \Exception('Channel\\Client not support clearTimer method when it is not in the workerman environment.');
+        }
+        if(self::$_reconnectTimer)
+        {
+            Timer::del(self::$_reconnectTimer);
+            self::$_reconnectTimer = null;
+        }
+    }
+
+    /**
+     * On.
+     * @param string $event
+     * @param callback $callback
+     * @throws \Exception
+     */
+    public static function on($event, $callback)
+    {
+        if (!self::$_isWorkermanEnv) {
+            throw new \Exception('Channel\\Client not support on method when it is not in the workerman environment.');
+        }
+        if(!is_callable($callback))
+        {
+            throw new \Exception('callback is not callable');
+        }
+        self::$_events[$event] = $callback;
+        self::subscribe(array($event));
+    }
+
+    /**
+     * Subscribe.
+     * @param string $events
+     * @return void
+     */
+    public static function subscribe($events)
+    {
+        if (!self::$_isWorkermanEnv) {
+            throw new \Exception('Channel\\Client not support subscribe method when it is not in the workerman environment.');
+        }
+        self::connect(self::$_remoteIp, self::$_remotePort);
+        $events = (array)$events;
+        foreach($events as $event)
+        {
+            if(!isset(self::$_events[$event]))
+            {
+                self::$_events[$event] = null;
+            }
+        }
+        self::$_remoteConnection->send(serialize(array('type' => 'subscribe', 'channels'=>(array)$events)));
+    }
+
+    /**
+     * Unsubscribe.
+     * @param string $events
+     * @return void
+     */
+    public static function unsubscribe($events)
+    {
+        if (!self::$_isWorkermanEnv) {
+            throw new \Exception('Channel\\Client not support unsubscribe method when it is not in the workerman environment.');
+        }
+        self::connect(self::$_remoteIp, self::$_remotePort);
+        $events = (array)$events;
+        foreach($events as $event)
+        {
+            unset(self::$_events[$event]);
+        }
+        self::$_remoteConnection->send(serialize(array('type' => 'unsubscribe', 'channels'=>$events)));
+    }
+
+    /**
+     * Publish.
+     * @param string $events
+     * @param mixed $data
+     */
+    public static function publish($events, $data)
+    {
+        self::connect(self::$_remoteIp, self::$_remotePort);
+        if (self::$_isWorkermanEnv) {
+            self::$_remoteConnection->send(serialize(array('type' => 'publish', 'channels' => (array)$events, 'data' => $data)));
+        } else {
+            $body = serialize(array('type' => 'publish', 'channels'=>(array)$events, 'data' => $data));
+            $buffer = pack('N', 4+strlen($body)) . $body;
+            fwrite(self::$_remoteConnection, $buffer);
+        }
+    }
+}
diff --git a/vendor/workerman/channel/src/Server.php b/vendor/workerman/channel/src/Server.php
new file mode 100755
index 0000000..3ad2ee5
--- /dev/null
+++ b/vendor/workerman/channel/src/Server.php
@@ -0,0 +1,108 @@
+<?php
+namespace Channel;
+use Workerman\Worker;
+/**
+ * Channel server.
+ */
+class Server
+{
+    /**
+     * Worker instance.
+     * @var Worker
+     */
+    protected $_worker = null;
+
+    /**
+     * Construct.
+     * @param string $ip
+     * @param int $port
+     */
+    public function __construct($ip = '0.0.0.0', $port = 2206)
+    {
+        $worker = new Worker("frame://$ip:$port");
+        $worker->count = 1;
+        $worker->name = 'ChannelServer';
+        $worker->channels = array();
+        $worker->onMessage = array($this, 'onMessage') ;
+        $worker->onClose = array($this, 'onClose'); 
+        $this->_worker = $worker;
+    }
+
+    /**
+     * onClose
+     * @return void
+     */
+    public function onClose($connection)
+    {
+        if(empty($connection->channels))
+        {
+            return;
+        }
+        foreach($connection->channels as $channel)
+        {
+            unset($this->_worker->channels[$channel][$connection->id]);
+            if(empty($this->_worker->channels[$channel]))
+            {
+                unset($this->_worker->channels[$channel]);
+            }
+        }
+    }
+
+    /**
+     * onMessage.
+     * @param TcpConnection $connection
+     * @param string $data
+     */
+    public function onMessage($connection, $data)
+    {
+        if(!$data)
+        {
+            return;
+        }
+        $worker = $this->_worker;
+        $data = unserialize($data);
+        $type = $data['type'];
+        $channels = $data['channels'];
+        switch($type)
+        {
+            case 'subscribe':
+                foreach($channels as $channel)
+                {
+                    $connection->channels[$channel] = $channel;
+                    $worker->channels[$channel][$connection->id] = $connection;
+                }
+                break;
+            case 'unsubscribe':
+                foreach($channels as $channel)
+                {
+                    if(isset($connection->channels[$channel]))
+                    {
+                        unset($connection->channels[$channel]);
+                    }
+                    if(isset($worker->channels[$channel][$connection->id]))
+                    {
+                        unset($worker->channels[$channel][$connection->id]);
+                        if(empty($worker->channels[$channel]))
+                        {
+                            unset($worker->channels[$channel]);
+                        }
+                    }
+                }
+                break;
+            case 'publish':
+                foreach($channels as $channel)
+                {
+                    if(empty($worker->channels[$channel]))
+                    {
+                        continue;
+                    }
+                    $buffer = serialize(array('channel'=>$channel, 'data' => $data['data']))."\n";
+                    foreach($worker->channels[$channel] as $connection)
+                    {
+                        $connection->send($buffer);
+                    }
+                }
+                break;
+        }
+    }
+}
diff --git a/vendor/workerman/channel/test/server.php b/vendor/workerman/channel/test/server.php
new file mode 100755
index 0000000..451750c
--- /dev/null
+++ b/vendor/workerman/channel/test/server.php
@@ -0,0 +1,22 @@
+<?php
+use Workerman\Worker;
+use Workerman\Lib\Timer;
+
+// composer autoload
+include __DIR__ . '/../vendor/autoload.php';
+
+$channel_server = new Channel\Server();
+
+$worker = new Worker();
+$worker->onWorkerStart = function()
+{
+    Channel\Client::on('test event', function($event_data){
+        echo 'test event triggered event_data :';
+        var_dump($event_data);
+    });
+    Timer::add(5, function(){
+        Channel\Client::publish('test event', 'some data');
+    });
+};
+
+Worker::runAll();
diff --git a/vendor/workerman/crontab/README.md b/vendor/workerman/crontab/README.md
new file mode 100755
index 0000000..0117eb9
--- /dev/null
+++ b/vendor/workerman/crontab/README.md
@@ -0,0 +1,29 @@
+# Crontab
+A crontab with precision in seconds written in PHP based on [workerman](https://github.com/walkor/workerman).
+
+# Install
+```
+composer require workerman/crontab
+```
+
+# Usage
+start.php
+```php
+<?php
+use Workerman\Worker;
+require __DIR__ . '/../vendor/autoload.php';
+
+use Workerman\Crontab\Crontab;
+$worker = new Worker();
+
+$worker->onWorkerStart = function () {
+    // Execute the function in the first second of every minute.
+    new Crontab('1 * * * * *', function(){
+        echo date('Y-m-d H:i:s')."\n";
+    });
+};
+
+Worker::runAll();
+```
+
+Run with commands `php start.php start` or php `start.php start -d`
diff --git a/vendor/workerman/crontab/composer.json b/vendor/workerman/crontab/composer.json
new file mode 100755
index 0000000..e4c487d
--- /dev/null
+++ b/vendor/workerman/crontab/composer.json
@@ -0,0 +1,34 @@
+{
+    "name": "workerman/crontab",
+    "type": "library",
+    "keywords": [
+        "crontab"
+    ],
+    "homepage": "http://www.workerman.net",
+    "license": "MIT",
+    "description": "A crontab written in PHP based on workerman",
+    "authors": [
+        {
+            "name": "walkor",
+            "email": "walkor@workerman.net",
+            "homepage": "http://www.workerman.net",
+            "role": "Developer"
+        }
+    ],
+    "support": {
+        "email": "walkor@workerman.net",
+        "issues": "https://github.com/walkor/workerman/issues",
+        "forum": "http://wenda.workerman.net/",
+        "wiki": "http://doc.workerman.net/",
+        "source": "https://github.com/walkor/crontab"
+    },
+    "require": {
+        "php": ">=7.0",
+        "workerman/workerman": ">=3.5.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "Workerman\\Crontab\\": "./src"
+        }
+    }
+}
diff --git a/vendor/workerman/crontab/example/test.php b/vendor/workerman/crontab/example/test.php
new file mode 100755
index 0000000..e6a87ed
--- /dev/null
+++ b/vendor/workerman/crontab/example/test.php
@@ -0,0 +1,16 @@
+<?php
+use Workerman\Worker;
+require __DIR__ . '/../vendor/autoload.php';
+
+use Workerman\Crontab\Crontab;
+
+$worker = new Worker();
+
+$worker->onWorkerStart = function () {
+    // Execute the function in the first second of every minute.
+    new Crontab('1 * * * * *', function(){
+        echo date('Y-m-d H:i:s')."\n";
+    });
+};
+
+Worker::runAll();
diff --git a/vendor/workerman/crontab/src/Crontab.php b/vendor/workerman/crontab/src/Crontab.php
new file mode 100755
index 0000000..e79853f
--- /dev/null
+++ b/vendor/workerman/crontab/src/Crontab.php
@@ -0,0 +1,177 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Crontab;
+use Workerman\Lib\Timer;
+
+/**
+ * Class Crontab
+ * @package Workerman\Crontab
+ */
+class Crontab
+{
+    /**
+     * @var string
+     */
+    protected $_rule;
+
+    /**
+     * @var callable
+     */
+    protected $_callback;
+
+    /**
+     * @var string
+     */
+    protected $_name;
+
+    /**
+     * @var int
+     */
+    protected $_id;
+
+    /**
+     * @var array
+     */
+    protected static $_instances = [];
+
+    /**
+     * Crontab constructor.
+     * @param $rule
+     * @param $callback
+     * @param null $name
+     */
+    public function __construct($rule, $callback, $name = null)
+    {
+        $this->_rule = $rule;
+        $this->_callback = $callback;
+        $this->_name = $name;
+        $this->_id = static::createId();
+        static::$_instances[$this->_id] = $this;
+        static::tryInit();
+    }
+
+    /**
+     * @return string
+     */
+    public function getRule()
+    {
+        return $this->_rule;
+    }
+
+    /**
+     * @return callable
+     */
+    public function getCallback()
+    {
+        return $this->_callback;
+    }
+
+    /**
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->_name;
+    }
+
+    /**
+     * @return int
+     */
+    public function getId()
+    {
+        return $this->_id;
+    }
+
+    /**
+     * @return bool
+     */
+    public function destroy()
+    {
+        return static::remove($this->_id);
+    }
+
+    /**
+     * @return array
+     */
+    public static function getAll()
+    {
+        return static::$_instances;
+    }
+
+    /**
+     * @param $id
+     * @return bool
+     */
+    public static function remove($id)
+    {
+        if ($id instanceof Crontab) {
+            $id = $id->getId();
+        }
+        if (!isset(static::$_instances[$id])) {
+            return false;
+        }
+        unset(static::$_instances[$id]);
+        return true;
+    }
+
+    /**
+     * @return int
+     */
+    protected static function createId()
+    {
+        static $id = 0;
+        return ++$id;
+    }
+
+    /**
+     * tryInit
+     */
+    protected static function tryInit()
+    {
+        static $inited = false;
+        if ($inited) {
+            return;
+        }
+        $inited = true;
+        $parser = new Parser();
+        $callback = function () use ($parser, &$callback) {
+            foreach (static::$_instances as $crontab) {
+                $rule = $crontab->getRule();
+                $cb = $crontab->getCallback();
+                if (!$cb || !$rule) {
+                    continue;
+                }
+                $times = $parser->parse($rule);
+                $now = time();
+                foreach ($times as $time) {
+                    $t = $time-$now;
+                    if ($t <= 0) {
+                        $t = 0.000001;
+                    }
+                    Timer::add($t, $cb, null, false);
+                }
+            }
+            Timer::add(60 - time()%60, $callback, null, false);
+        };
+
+        $next_time = time()%60;
+        if ($next_time == 0) {
+            $next_time = 0.00001;
+        } else {
+            $next_time = 60 - $next_time;
+        }
+        Timer::add($next_time, $callback, null, false);
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/workerman/crontab/src/Parser.php b/vendor/workerman/crontab/src/Parser.php
new file mode 100755
index 0000000..e9a9a86
--- /dev/null
+++ b/vendor/workerman/crontab/src/Parser.php
@@ -0,0 +1,171 @@
+<?php
+/**
+ * @author:  Jan Konieczny <jkonieczny@gmail.com>, group@hyperf.io
+ * @license: http://www.gnu.org/licenses/
+ * @license: https://github.com/hyperf/hyperf/blob/master/LICENSE
+ *
+ *  This is a simple script to parse crontab syntax to get the execution time
+ *
+ *  Eg.:   $timestamp = Crontab::parse('12 * * * 1-5');
+ *
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Provides basic cron syntax parsing functionality
+ *
+ * @author:  Jan Konieczny <jkonieczny@gmail.com>, group@hyperf.io
+ * @license: http://www.gnu.org/licenses/
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Workerman\Crontab;
+
+/**
+ * Class Parser
+ * @package Workerman\Crontab
+ */
+class Parser
+{
+    /**
+     *  Finds next execution time(stamp) parsin crontab syntax.
+     *
+     * @param string $crontab_string :
+     *   0    1    2    3    4    5
+     *   *    *    *    *    *    *
+     *   -    -    -    -    -    -
+     *   |    |    |    |    |    |
+     *   |    |    |    |    |    +----- day of week (0 - 6) (Sunday=0)
+     *   |    |    |    |    +----- month (1 - 12)
+     *   |    |    |    +------- day of month (1 - 31)
+     *   |    |    +--------- hour (0 - 23)
+     *   |    +----------- min (0 - 59)
+     *   +------------- sec (0-59)
+     *
+     * @param null|int $start_time
+     * @throws \InvalidArgumentException
+     * @return int[]
+     */
+    public function parse($crontab_string, $start_time = null)
+    {
+        if (! $this->isValid($crontab_string)) {
+            throw new \InvalidArgumentException('Invalid cron string: ' . $crontab_string);
+        }
+        $start_time = $start_time ? $start_time : time();
+        $date = $this->parseDate($crontab_string);
+        if (in_array((int) date('i', $start_time), $date['minutes'])
+            && in_array((int) date('G', $start_time), $date['hours'])
+            && in_array((int) date('j', $start_time), $date['day'])
+            && in_array((int) date('w', $start_time), $date['week'])
+            && in_array((int) date('n', $start_time), $date['month'])
+        ) {
+            $result = [];
+            foreach ($date['second'] as $second) {
+                $result[] = $start_time + $second;
+            }
+            return $result;
+        }
+        return [];
+    }
+
+    public function isValid(string $crontab_string): bool
+    {
+        if (! preg_match('/^((\*(\/[0-9]+)?)|[0-9\-\,\/]+)\s+((\*(\/[0-9]+)?)|[0-9\-\,\/]+)\s+((\*(\/[0-9]+)?)|[0-9\-\,\/]+)\s+((\*(\/[0-9]+)?)|[0-9\-\,\/]+)\s+((\*(\/[0-9]+)?)|[0-9\-\,\/]+)\s+((\*(\/[0-9]+)?)|[0-9\-\,\/]+)$/i', trim($crontab_string))) {
+            if (! preg_match('/^((\*(\/[0-9]+)?)|[0-9\-\,\/]+)\s+((\*(\/[0-9]+)?)|[0-9\-\,\/]+)\s+((\*(\/[0-9]+)?)|[0-9\-\,\/]+)\s+((\*(\/[0-9]+)?)|[0-9\-\,\/]+)\s+((\*(\/[0-9]+)?)|[0-9\-\,\/]+)$/i', trim($crontab_string))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Parse each segment of crontab string.
+     */
+    protected function parseSegment(string $string, int $min, int $max, int $start = null)
+    {
+        if ($start === null || $start < $min) {
+            $start = $min;
+        }
+        $result = [];
+        if ($string === '*') {
+            for ($i = $start; $i <= $max; ++$i) {
+                $result[] = $i;
+            }
+        } elseif (strpos($string, ',') !== false) {
+            $exploded = explode(',', $string);
+            foreach ($exploded as $value) {
+                if (strpos($value, '/') !== false || strpos($string, '-') !== false) {
+                    $result = array_merge($result, $this->parseSegment($value, $min, $max, $start));
+                    continue;
+                }
+                if (trim($value) === '' || ! $this->between((int) $value, (int) ($min > $start ? $min : $start), (int) $max)) {
+                    continue;
+                }
+                $result[] = (int) $value;
+            }
+        } elseif (strpos($string, '/') !== false) {
+            $exploded = explode('/', $string);
+            if (strpos($exploded[0], '-') !== false) {
+                [$nMin, $nMax] = explode('-', $exploded[0]);
+                $nMin > $min && $min = (int) $nMin;
+                $nMax < $max && $max = (int) $nMax;
+            }
+            $start < $min && $start = $min;
+            for ($i = $start; $i <= $max;) {
+                $result[] = $i;
+                $i += $exploded[1];
+            }
+        } elseif (strpos($string, '-') !== false) {
+            $result = array_merge($result, $this->parseSegment($string . '/1', $min, $max, $start));
+        } elseif ($this->between((int) $string, $min > $start ? $min : $start, $max)) {
+            $result[] = (int) $string;
+        }
+        return $result;
+    }
+
+    /**
+     * Determire if the $value is between in $min and $max ?
+     */
+    private function between(int $value, int $min, int $max): bool
+    {
+        return $value >= $min && $value <= $max;
+    }
+
+
+    private function parseDate(string $crontab_string): array
+    {
+        $cron = preg_split('/[\\s]+/i', trim($crontab_string));
+        if (count($cron) == 6) {
+            $date = [
+                'second'  => $this->parseSegment($cron[0], 0, 59),
+                'minutes' => $this->parseSegment($cron[1], 0, 59),
+                'hours'   => $this->parseSegment($cron[2], 0, 23),
+                'day'     => $this->parseSegment($cron[3], 1, 31),
+                'month'   => $this->parseSegment($cron[4], 1, 12),
+                'week'    => $this->parseSegment($cron[5], 0, 6),
+            ];
+        } else {
+            $date = [
+                'second'  => [1 => 0],
+                'minutes' => $this->parseSegment($cron[0], 0, 59),
+                'hours'   => $this->parseSegment($cron[1], 0, 23),
+                'day'     => $this->parseSegment($cron[2], 1, 31),
+                'month'   => $this->parseSegment($cron[3], 1, 12),
+                'week'    => $this->parseSegment($cron[4], 0, 6),
+            ];
+        }
+        return $date;
+    }
+}
diff --git a/vendor/workerman/workerman.log b/vendor/workerman/workerman.log
new file mode 100755
index 0000000..30e001f
--- /dev/null
+++ b/vendor/workerman/workerman.log
@@ -0,0 +1,740 @@
+2023-06-19 14:11:49 pid:14415 Workerman[think] start in DEBUG mode
+2023-06-19 14:11:54 pid:14415 Workerman[think] stopping ...
+2023-06-19 14:11:54 pid:14415 Workerman[think] has been stopped
+2023-06-19 14:12:09 pid:14428 Workerman[think] start in DEBUG mode
+2023-06-19 14:12:46 pid:14489 Workerman[think] start in DEBUG mode
+2023-06-19 14:13:31 pid:14489 Workerman[think] stopping ...
+2023-06-19 14:13:31 pid:14489 Workerman[think] has been stopped
+2023-06-19 14:13:33 pid:14507 Workerman[think] start in DEBUG mode
+2023-06-19 14:13:57 pid:14507 Workerman[think] stopping ...
+2023-06-19 14:13:57 pid:14507 Workerman[think] has been stopped
+2023-06-19 14:14:07 pid:14516 Workerman[think] start in DEBUG mode
+2023-08-03 16:11:14 pid:17718 Workerman[think] start in DEBUG mode
+2023-08-03 16:11:28 pid:17721 Workerman[think] start in DEBUG mode
+2023-08-03 16:14:48 pid:17721 Workerman[think] stopping ...
+2023-08-03 16:14:48 pid:17721 Workerman[think] has been stopped
+2023-08-03 16:14:49 pid:17743 Workerman[think] start in DEBUG mode
+2023-08-03 16:15:37 pid:17743 Workerman[think] stopping ...
+2023-08-03 16:15:37 pid:17743 Workerman[think] has been stopped
+2023-08-03 16:15:38 pid:17757 Workerman[think] start in DEBUG mode
+2023-08-03 16:16:03 pid:17757 Workerman[think] stopping ...
+2023-08-03 16:16:03 pid:17757 Workerman[think] has been stopped
+2023-08-03 16:16:04 pid:17764 Workerman[think] start in DEBUG mode
+2023-08-03 16:17:19 pid:17764 Workerman[think] stopping ...
+2023-08-03 16:17:19 pid:17764 Workerman[think] has been stopped
+2023-08-03 16:17:20 pid:17781 Workerman[think] start in DEBUG mode
+2023-08-03 16:21:00 pid:17781 Workerman[think] stopping ...
+2023-08-03 16:21:00 pid:17781 Workerman[think] has been stopped
+2023-08-03 16:21:02 pid:17815 Workerman[think] start in DEBUG mode
+2023-08-03 16:23:09 pid:17815 Workerman[think] stopping ...
+2023-08-03 16:23:09 pid:17815 Workerman[think] has been stopped
+2023-08-03 16:23:10 pid:17851 Workerman[think] start in DEBUG mode
+2023-08-03 16:24:12 pid:17851 Workerman[think] stopping ...
+2023-08-03 16:24:12 pid:17851 Workerman[think] has been stopped
+2023-08-03 16:24:13 pid:17865 Workerman[think] start in DEBUG mode
+2023-08-03 16:24:54 pid:17865 Workerman[think] stopping ...
+2023-08-03 16:24:54 pid:17865 Workerman[think] has been stopped
+2023-08-03 16:24:55 pid:17872 Workerman[think] start in DEBUG mode
+2023-08-03 16:37:59 pid:17872 Workerman[think] stopping ...
+2023-08-03 16:37:59 pid:17872 Workerman[think] has been stopped
+2023-08-03 16:38:00 pid:17958 Workerman[think] start in DEBUG mode
+2023-08-03 16:39:57 pid:17958 Workerman[think] stopping ...
+2023-08-03 16:39:57 pid:17958 Workerman[think] has been stopped
+2023-08-03 16:39:58 pid:17981 Workerman[think] start in DEBUG mode
+2023-08-03 16:47:25 pid:17981 Workerman[think] stopping ...
+2023-08-03 16:47:25 pid:17981 Workerman[think] has been stopped
+2023-08-03 16:47:25 pid:18055 Workerman[think] start in DEBUG mode
+2023-08-03 16:48:01 pid:18055 Workerman[think] stopping ...
+2023-08-03 16:48:01 pid:18055 Workerman[think] has been stopped
+2023-08-03 16:48:01 pid:18066 Workerman[think] start in DEBUG mode
+2023-08-03 16:49:07 pid:18066 Workerman[think] stopping ...
+2023-08-03 16:49:07 pid:18066 Workerman[think] has been stopped
+2023-08-03 16:49:08 pid:18078 Workerman[think] start in DEBUG mode
+2023-08-03 16:49:40 pid:18078 Workerman[think] stopping ...
+2023-08-03 16:49:40 pid:18078 Workerman[think] has been stopped
+2023-08-03 16:49:40 pid:18091 Workerman[think] start in DEBUG mode
+2023-08-03 16:50:52 pid:18091 Workerman[think] stopping ...
+2023-08-03 16:50:52 pid:18091 Workerman[think] has been stopped
+2023-08-03 16:50:54 pid:18132 Workerman[think] start in DEBUG mode
+2023-08-03 16:51:19 pid:18132 Workerman[think] stopping ...
+2023-08-03 16:51:19 pid:18132 Workerman[think] has been stopped
+2023-08-03 16:51:20 pid:18150 Workerman[think] start in DEBUG mode
+2023-08-03 17:00:32 pid:18150 Workerman[think] stopping ...
+2023-08-03 17:00:32 pid:18150 Workerman[think] has been stopped
+2023-08-03 17:00:33 pid:18236 Workerman[think] start in DEBUG mode
+2023-08-03 17:14:51 pid:18236 Workerman[think] stopping ...
+2023-08-03 17:14:51 pid:18236 Workerman[think] has been stopped
+2023-08-03 17:14:52 pid:18323 Workerman[think] start in DEBUG mode
+2023-08-03 17:16:11 pid:18323 Workerman[think] stopping ...
+2023-08-03 17:16:11 pid:18323 Workerman[think] has been stopped
+2023-08-03 17:16:12 pid:18343 Workerman[think] start in DEBUG mode
+2023-08-07 14:42:11 pid:6396 Workerman[think] start in DEBUG mode
+2023-08-07 15:00:01 pid:6396 Workerman[think] stopping ...
+2023-08-07 15:00:01 pid:6396 Workerman[think] has been stopped
+2023-10-27 14:28:44 pid:44901 Workerman[think] start in DEBUG mode
+2023-10-27 14:46:15 pid:44901 Workerman[think] stopping ...
+2023-10-27 14:46:15 pid:44901 Workerman[think] has been stopped
+2023-10-27 14:46:16 pid:46108 Workerman[think] start in DEBUG mode
+2023-10-27 16:22:27 pid:46108 Workerman[think] stopping ...
+2023-10-27 16:22:27 pid:46108 Workerman[think] has been stopped
+2023-10-27 16:22:29 pid:46958 Workerman[think] start in DEBUG mode
+2023-10-27 16:35:55 pid:46958 Workerman[think] stopping ...
+2023-10-27 16:35:55 pid:46958 Workerman[think] has been stopped
+2023-10-27 16:35:56 pid:47043 Workerman[think] start in DEBUG mode
+2023-10-27 16:36:19 pid:47043 Workerman[think] stopping ...
+2023-10-27 16:36:19 pid:47043 Workerman[think] has been stopped
+2023-10-27 16:36:24 pid:47047 Workerman[think] start in DEBUG mode
+2023-10-27 16:47:38 pid:47047 Workerman[think] stopping ...
+2023-10-27 16:47:38 pid:47047 Workerman[think] has been stopped
+2023-10-27 16:47:44 pid:47150 Workerman[think] start in DEBUG mode
+2023-10-27 17:00:35 pid:47150 Workerman[think] stopping ...
+2023-10-27 17:00:35 pid:47150 Workerman[think] has been stopped
+2023-10-27 17:00:36 pid:47206 Workerman[think] start in DEBUG mode
+2023-10-27 17:07:13 pid:47206 Workerman[think] stopping ...
+2023-10-27 17:07:13 pid:47206 Workerman[think] has been stopped
+2023-10-27 17:07:14 pid:47263 Workerman[think] start in DEBUG mode
+2023-10-27 17:26:54 pid:47263 Workerman[think] stopping ...
+2023-10-27 17:26:54 pid:47263 Workerman[think] has been stopped
+2024-04-29 09:24:59 pid:53118 Workerman[think] start in DEBUG mode
+2024-04-29 09:25:34 pid:53118 Workerman[think] stopping ...
+2024-04-29 09:25:34 pid:53118 Workerman[think] has been stopped
+2024-04-29 09:25:35 pid:53171 Workerman[think] start in DEBUG mode
+2024-04-29 09:45:22 pid:53171 Workerman[think] stopping ...
+2024-04-29 09:45:22 pid:53171 Workerman[think] has been stopped
+2024-04-29 09:45:23 pid:54404 Workerman[think] start in DEBUG mode
+2024-04-29 09:47:39 pid:54404 Workerman[think] stopping ...
+2024-04-29 09:47:39 pid:54404 Workerman[think] has been stopped
+2024-06-05 14:06:02 pid:89212 Workerman[think] start in DEBUG mode
+2024-06-05 14:11:47 pid:89212 Workerman[think] stopping ...
+2024-06-05 14:11:47 pid:89212 Workerman[think] has been stopped
+2024-06-05 16:33:01 pid:1578 Workerman[think] start in DEBUG mode
+2024-06-05 16:35:59 pid:1578 Workerman[think] stopping ...
+2024-06-05 16:35:59 pid:1578 Workerman[think] has been stopped
+2024-06-05 16:36:00 pid:1774 Workerman[think] start in DEBUG mode
+2024-06-05 16:37:39 pid:1774 Workerman[think] stopping ...
+2024-06-05 16:37:39 pid:1774 Workerman[think] has been stopped
+2024-06-05 16:37:40 pid:1889 Workerman[think] start in DEBUG mode
+2024-06-05 16:38:23 pid:1889 Workerman[think] stopping ...
+2024-06-05 16:38:23 pid:1889 Workerman[think] has been stopped
+2024-06-05 17:01:00 pid:3449 Workerman[think] start in DEBUG mode
+2024-06-05 17:04:48 pid:3449 Workerman[think] stopping ...
+2024-06-05 17:04:48 pid:3449 Workerman[think] has been stopped
+2024-06-05 17:04:49 pid:3682 Workerman[think] start in DEBUG mode
+2024-06-05 17:30:33 pid:3682 Workerman[think] stopping ...
+2024-06-05 17:30:33 pid:3682 Workerman[think] has been stopped
+2024-06-05 17:31:02 pid:6571 Workerman[think] start in DEBUG mode
+2024-06-05 17:32:07 pid:6571 Workerman[think] stopping ...
+2024-06-05 17:32:07 pid:6571 Workerman[think] has been stopped
+2024-07-30 16:00:07 pid:67066 Workerman[think] start in DEBUG mode
+2024-07-30 16:02:37 pid:67066 Workerman[think] stopping ...
+2024-07-30 16:02:37 pid:67066 Workerman[think] has been stopped
+2024-07-30 16:02:39 pid:67235 Workerman[think] start in DEBUG mode
+2024-07-30 16:30:52 pid:67235 Workerman[think] stopping ...
+2024-07-30 16:30:52 pid:67235 Workerman[think] has been stopped
+2024-07-30 16:31:20 pid:69024 Workerman[think] start in DEBUG mode
+2024-07-30 16:32:27 pid:69025 Worker[69025] process terminated
+2024-07-30 16:33:00 pid:69105 Worker[69105] process terminated
+2024-07-30 16:33:00 pid:69146 Worker[69146] process terminated
+2024-07-30 16:33:00 pid:69153 Worker[69153] process terminated
+2024-07-30 16:33:00 pid:69160 Worker[69160] process terminated
+2024-07-30 16:33:00 pid:69167 Worker[69167] process terminated
+2024-07-30 16:33:00 pid:69174 Worker[69174] process terminated
+2024-07-30 16:33:00 pid:69181 Worker[69181] process terminated
+2024-07-30 16:33:00 pid:69188 Worker[69188] process terminated
+2024-07-30 16:33:00 pid:69195 Worker[69195] process terminated
+2024-07-30 16:33:00 pid:69202 Worker[69202] process terminated
+2024-07-30 16:33:00 pid:69209 Worker[69209] process terminated
+2024-07-30 16:33:00 pid:69216 Worker[69216] process terminated
+2024-07-30 16:33:00 pid:69223 Worker[69223] process terminated
+2024-07-30 16:33:00 pid:69230 Worker[69230] process terminated
+2024-07-30 16:33:00 pid:69237 Worker[69237] process terminated
+2024-07-30 16:33:00 pid:69244 Worker[69244] process terminated
+2024-07-30 16:33:00 pid:69251 Worker[69251] process terminated
+2024-07-30 16:33:00 pid:69258 Worker[69258] process terminated
+2024-07-30 16:33:00 pid:69265 Worker[69265] process terminated
+2024-07-30 16:33:00 pid:69272 Worker[69272] process terminated
+2024-07-30 16:33:00 pid:69279 Worker[69279] process terminated
+2024-07-30 16:33:00 pid:69286 Worker[69286] process terminated
+2024-07-30 16:33:01 pid:69293 Worker[69293] process terminated
+2024-07-30 16:33:22 pid:69024 Workerman[think] stopping ...
+2024-07-30 16:33:22 pid:69024 Workerman[think] has been stopped
+2024-07-30 16:33:23 pid:69325 Workerman[think] start in DEBUG mode
+2024-07-30 16:34:00 pid:69326 Worker[69326] process terminated
+2024-07-30 16:34:00 pid:69372 Worker[69372] process terminated
+2024-07-30 16:34:00 pid:69379 Worker[69379] process terminated
+2024-07-30 16:34:00 pid:69386 Worker[69386] process terminated
+2024-07-30 16:34:00 pid:69393 Worker[69393] process terminated
+2024-07-30 16:34:00 pid:69400 Worker[69400] process terminated
+2024-07-30 16:34:00 pid:69407 Worker[69407] process terminated
+2024-07-30 16:34:01 pid:69414 Worker[69414] process terminated
+2024-07-30 16:34:57 pid:69325 Workerman[think] stopping ...
+2024-07-30 16:34:57 pid:69325 Workerman[think] has been stopped
+2024-07-30 16:34:59 pid:69488 Workerman[think] start in DEBUG mode
+2024-07-30 16:35:41 pid:69488 Workerman[think] stopping ...
+2024-07-30 16:35:41 pid:69488 Workerman[think] has been stopped
+2024-07-30 16:36:42 pid:69594 Workerman[think] start in DEBUG mode
+2024-07-30 16:39:48 pid:69594 Workerman[think] stopping ...
+2024-07-30 16:39:48 pid:69594 Workerman[think] has been stopped
+2024-07-30 16:39:50 pid:69802 Workerman[think] start in DEBUG mode
+2024-07-30 16:40:06 pid:69802 Workerman[think] stopping ...
+2024-07-30 16:40:06 pid:69802 Workerman[think] has been stopped
+2024-07-30 16:41:51 pid:69931 Workerman[think] start in DEBUG mode
+2024-07-30 16:42:03 pid:69931 Workerman[think] stopping ...
+2024-07-30 16:42:03 pid:69931 Workerman[think] has been stopped
+2024-07-30 16:42:53 pid:70002 Workerman[think] start in DEBUG mode
+2024-07-30 16:43:04 pid:70002 Workerman[think] stopping ...
+2024-07-30 16:43:04 pid:70002 Workerman[think] has been stopped
+2024-07-30 16:44:00 pid:70090 Workerman[think] start in DEBUG mode
+2024-07-30 16:49:14 pid:70090 Workerman[think] stopping ...
+2024-07-30 16:49:14 pid:70090 Workerman[think] has been stopped
+2024-07-30 16:49:29 pid:70467 Workerman[think] start in DEBUG mode
+2024-07-30 16:53:07 pid:70467 Workerman[think] stopping ...
+2024-07-30 16:53:07 pid:70467 Workerman[think] has been stopped
+2024-07-30 16:53:52 pid:70752 Workerman[think] start in DEBUG mode
+2024-07-30 16:58:22 pid:70752 Workerman[think] stopping ...
+2024-07-30 16:58:22 pid:70752 Workerman[think] has been stopped
+2024-07-30 17:51:16 pid:3746 Workerman[think] start in DEBUG mode
+2024-07-30 17:59:14 pid:3746 Workerman[think] stopping ...
+2024-07-30 17:59:14 pid:3746 Workerman[think] has been stopped
+2024-09-24 14:34:52 pid:71338 Workerman[think] start in DEBUG mode
+2024-09-24 14:35:00 pid:71339 Worker[71339] process terminated
+2024-09-24 14:35:00 pid:71338 worker[none:71339] exit with status 256
+2024-09-24 14:35:00 pid:71357 Worker[71357] process terminated
+2024-09-24 14:35:00 pid:71338 worker[none:71357] exit with status 256
+2024-09-24 14:35:00 pid:71364 Worker[71364] process terminated
+2024-09-24 14:35:00 pid:71338 worker[none:71364] exit with status 256
+2024-09-24 14:35:00 pid:71371 Worker[71371] process terminated
+2024-09-24 14:35:00 pid:71338 worker[none:71371] exit with status 256
+2024-09-24 14:35:00 pid:71378 Worker[71378] process terminated
+2024-09-24 14:35:00 pid:71338 worker[none:71378] exit with status 256
+2024-09-24 14:35:00 pid:71385 Worker[71385] process terminated
+2024-09-24 14:35:00 pid:71338 worker[none:71385] exit with status 256
+2024-09-24 14:35:00 pid:71392 Worker[71392] process terminated
+2024-09-24 14:35:00 pid:71338 worker[none:71392] exit with status 256
+2024-09-24 14:35:00 pid:71399 Worker[71399] process terminated
+2024-09-24 14:35:00 pid:71338 worker[none:71399] exit with status 256
+2024-09-24 14:35:00 pid:71406 Worker[71406] process terminated
+2024-09-24 14:35:00 pid:71338 worker[none:71406] exit with status 256
+2024-09-24 14:35:00 pid:71413 Worker[71413] process terminated
+2024-09-24 14:35:00 pid:71338 worker[none:71413] exit with status 256
+2024-09-24 14:35:00 pid:71420 Worker[71420] process terminated
+2024-09-24 14:35:00 pid:71338 worker[none:71420] exit with status 256
+2024-09-24 14:35:00 pid:71427 Worker[71427] process terminated
+2024-09-24 14:35:00 pid:71338 worker[none:71427] exit with status 256
+2024-09-24 14:35:00 pid:71434 Worker[71434] process terminated
+2024-09-24 14:35:00 pid:71338 worker[none:71434] exit with status 256
+2024-09-24 14:35:00 pid:71442 Worker[71442] process terminated
+2024-09-24 14:35:00 pid:71338 worker[none:71442] exit with status 256
+2024-09-24 14:35:00 pid:71449 Worker[71449] process terminated
+2024-09-24 14:35:00 pid:71338 worker[none:71449] exit with status 256
+2024-09-24 14:35:00 pid:71456 Worker[71456] process terminated
+2024-09-24 14:35:00 pid:71338 worker[none:71456] exit with status 256
+2024-09-24 14:35:00 pid:71463 Worker[71463] process terminated
+2024-09-24 14:35:00 pid:71338 worker[none:71463] exit with status 256
+2024-09-24 14:35:00 pid:71470 Worker[71470] process terminated
+2024-09-24 14:35:00 pid:71338 worker[none:71470] exit with status 256
+2024-09-24 14:35:00 pid:71477 Worker[71477] process terminated
+2024-09-24 14:35:00 pid:71338 worker[none:71477] exit with status 256
+2024-09-24 14:35:00 pid:71484 Worker[71484] process terminated
+2024-09-24 14:35:00 pid:71338 worker[none:71484] exit with status 256
+2024-09-24 14:35:00 pid:71491 Worker[71491] process terminated
+2024-09-24 14:35:01 pid:71338 worker[none:71491] exit with status 256
+2024-09-24 14:35:07 pid:71338 Workerman[think] stopping ...
+2024-09-24 14:35:07 pid:71338 Workerman[think] has been stopped
+2024-09-24 14:35:10 pid:71509 Workerman[think] start in DEBUG mode
+2024-09-24 14:36:00 pid:71510 Worker[71510] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71510] exit with status 256
+2024-09-24 14:36:00 pid:71570 Worker[71570] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71570] exit with status 256
+2024-09-24 14:36:00 pid:71577 Worker[71577] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71577] exit with status 256
+2024-09-24 14:36:00 pid:71584 Worker[71584] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71584] exit with status 256
+2024-09-24 14:36:00 pid:71591 Worker[71591] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71591] exit with status 256
+2024-09-24 14:36:00 pid:71598 Worker[71598] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71598] exit with status 256
+2024-09-24 14:36:00 pid:71605 Worker[71605] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71605] exit with status 256
+2024-09-24 14:36:00 pid:71612 Worker[71612] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71612] exit with status 256
+2024-09-24 14:36:00 pid:71619 Worker[71619] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71619] exit with status 256
+2024-09-24 14:36:00 pid:71626 Worker[71626] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71626] exit with status 256
+2024-09-24 14:36:00 pid:71633 Worker[71633] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71633] exit with status 256
+2024-09-24 14:36:00 pid:71640 Worker[71640] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71640] exit with status 256
+2024-09-24 14:36:00 pid:71647 Worker[71647] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71647] exit with status 256
+2024-09-24 14:36:00 pid:71654 Worker[71654] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71654] exit with status 256
+2024-09-24 14:36:00 pid:71661 Worker[71661] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71661] exit with status 256
+2024-09-24 14:36:00 pid:71668 Worker[71668] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71668] exit with status 256
+2024-09-24 14:36:00 pid:71675 Worker[71675] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71675] exit with status 256
+2024-09-24 14:36:00 pid:71682 Worker[71682] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71682] exit with status 256
+2024-09-24 14:36:00 pid:71689 Worker[71689] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71689] exit with status 256
+2024-09-24 14:36:00 pid:71696 Worker[71696] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71696] exit with status 256
+2024-09-24 14:36:00 pid:71703 Worker[71703] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71703] exit with status 256
+2024-09-24 14:36:00 pid:71710 Worker[71710] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71710] exit with status 256
+2024-09-24 14:36:00 pid:71717 Worker[71717] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71717] exit with status 256
+2024-09-24 14:36:00 pid:71724 Worker[71724] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71724] exit with status 256
+2024-09-24 14:36:00 pid:71731 Worker[71731] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71731] exit with status 256
+2024-09-24 14:36:00 pid:71738 Worker[71738] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71738] exit with status 256
+2024-09-24 14:36:00 pid:71745 Worker[71745] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71745] exit with status 256
+2024-09-24 14:36:00 pid:71752 Worker[71752] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71752] exit with status 256
+2024-09-24 14:36:00 pid:71759 Worker[71759] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71759] exit with status 256
+2024-09-24 14:36:00 pid:71766 Worker[71766] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71766] exit with status 256
+2024-09-24 14:36:00 pid:71773 Worker[71773] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71773] exit with status 256
+2024-09-24 14:36:00 pid:71781 Worker[71781] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71781] exit with status 256
+2024-09-24 14:36:00 pid:71788 Worker[71788] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71788] exit with status 256
+2024-09-24 14:36:00 pid:71795 Worker[71795] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71795] exit with status 256
+2024-09-24 14:36:00 pid:71802 Worker[71802] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71802] exit with status 256
+2024-09-24 14:36:00 pid:71809 Worker[71809] process terminated
+2024-09-24 14:36:00 pid:71509 worker[none:71809] exit with status 256
+2024-09-24 14:36:00 pid:71816 Worker[71816] process terminated
+2024-09-24 14:36:01 pid:71509 worker[none:71816] exit with status 256
+2024-09-24 14:36:04 pid:71509 Workerman[think] stopping ...
+2024-09-24 14:36:04 pid:71509 Workerman[think] has been stopped
+2024-09-24 14:36:11 pid:71851 Workerman[think] start in DEBUG mode
+2024-09-24 14:36:16 pid:71851 Workerman[think] stopping ...
+2024-09-24 14:36:16 pid:71851 Workerman[think] has been stopped
+2024-10-15 17:21:45 pid:72311 Workerman[think] start in DEBUG mode
+2024-10-15 17:22:00 pid:72312 Worker[72312] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72312] exit with status 256
+2024-10-15 17:22:00 pid:72336 Worker[72336] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72336] exit with status 256
+2024-10-15 17:22:00 pid:72343 Worker[72343] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72343] exit with status 256
+2024-10-15 17:22:00 pid:72350 Worker[72350] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72350] exit with status 256
+2024-10-15 17:22:00 pid:72357 Worker[72357] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72357] exit with status 256
+2024-10-15 17:22:00 pid:72364 Worker[72364] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72364] exit with status 256
+2024-10-15 17:22:00 pid:72371 Worker[72371] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72371] exit with status 256
+2024-10-15 17:22:00 pid:72378 Worker[72378] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72378] exit with status 256
+2024-10-15 17:22:00 pid:72385 Worker[72385] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72385] exit with status 256
+2024-10-15 17:22:00 pid:72392 Worker[72392] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72392] exit with status 256
+2024-10-15 17:22:00 pid:72400 Worker[72400] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72400] exit with status 256
+2024-10-15 17:22:00 pid:72407 Worker[72407] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72407] exit with status 256
+2024-10-15 17:22:00 pid:72414 Worker[72414] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72414] exit with status 256
+2024-10-15 17:22:00 pid:72421 Worker[72421] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72421] exit with status 256
+2024-10-15 17:22:00 pid:72428 Worker[72428] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72428] exit with status 256
+2024-10-15 17:22:00 pid:72435 Worker[72435] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72435] exit with status 256
+2024-10-15 17:22:00 pid:72442 Worker[72442] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72442] exit with status 256
+2024-10-15 17:22:00 pid:72449 Worker[72449] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72449] exit with status 256
+2024-10-15 17:22:00 pid:72456 Worker[72456] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72456] exit with status 256
+2024-10-15 17:22:00 pid:72463 Worker[72463] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72463] exit with status 256
+2024-10-15 17:22:00 pid:72470 Worker[72470] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72470] exit with status 256
+2024-10-15 17:22:00 pid:72477 Worker[72477] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72477] exit with status 256
+2024-10-15 17:22:00 pid:72484 Worker[72484] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72484] exit with status 256
+2024-10-15 17:22:00 pid:72491 Worker[72491] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72491] exit with status 256
+2024-10-15 17:22:00 pid:72498 Worker[72498] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72498] exit with status 256
+2024-10-15 17:22:00 pid:72505 Worker[72505] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72505] exit with status 256
+2024-10-15 17:22:00 pid:72512 Worker[72512] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72512] exit with status 256
+2024-10-15 17:22:00 pid:72519 Worker[72519] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72519] exit with status 256
+2024-10-15 17:22:00 pid:72526 Worker[72526] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72526] exit with status 256
+2024-10-15 17:22:00 pid:72533 Worker[72533] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72533] exit with status 256
+2024-10-15 17:22:00 pid:72540 Worker[72540] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72540] exit with status 256
+2024-10-15 17:22:00 pid:72547 Worker[72547] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72547] exit with status 256
+2024-10-15 17:22:00 pid:72554 Worker[72554] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72554] exit with status 256
+2024-10-15 17:22:00 pid:72561 Worker[72561] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72561] exit with status 256
+2024-10-15 17:22:00 pid:72568 Worker[72568] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72568] exit with status 256
+2024-10-15 17:22:00 pid:72575 Worker[72575] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72575] exit with status 256
+2024-10-15 17:22:00 pid:72582 Worker[72582] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72582] exit with status 256
+2024-10-15 17:22:00 pid:72589 Worker[72589] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72589] exit with status 256
+2024-10-15 17:22:00 pid:72596 Worker[72596] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72596] exit with status 256
+2024-10-15 17:22:00 pid:72603 Worker[72603] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72603] exit with status 256
+2024-10-15 17:22:00 pid:72610 Worker[72610] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72610] exit with status 256
+2024-10-15 17:22:00 pid:72617 Worker[72617] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72617] exit with status 256
+2024-10-15 17:22:00 pid:72624 Worker[72624] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72624] exit with status 256
+2024-10-15 17:22:00 pid:72631 Worker[72631] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72631] exit with status 256
+2024-10-15 17:22:00 pid:72638 Worker[72638] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72638] exit with status 256
+2024-10-15 17:22:00 pid:72645 Worker[72645] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72645] exit with status 256
+2024-10-15 17:22:00 pid:72652 Worker[72652] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72652] exit with status 256
+2024-10-15 17:22:00 pid:72659 Worker[72659] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72659] exit with status 256
+2024-10-15 17:22:00 pid:72666 Worker[72666] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72666] exit with status 256
+2024-10-15 17:22:00 pid:72673 Worker[72673] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72673] exit with status 256
+2024-10-15 17:22:00 pid:72680 Worker[72680] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72680] exit with status 256
+2024-10-15 17:22:00 pid:72687 Worker[72687] process terminated
+2024-10-15 17:22:00 pid:72311 worker[none:72687] exit with status 256
+2024-10-15 17:22:01 pid:72694 Worker[72694] process terminated
+2024-10-15 17:22:01 pid:72311 worker[none:72694] exit with status 256
+2024-10-15 17:22:05 pid:72311 Workerman[think] stopping ...
+2024-10-15 17:22:05 pid:72311 Workerman[think] has been stopped
+2024-10-28 09:22:12 pid:4060 Workerman[think] start in DEBUG mode
+2024-10-28 09:22:31 pid:4060 Workerman[think] stopping ...
+2024-10-28 09:22:31 pid:4060 Workerman[think] has been stopped
+2024-10-28 09:22:32 pid:4098 Workerman[think] start in DEBUG mode
+2024-10-28 09:23:00 pid:4099 Worker[4099] process terminated
+2024-10-28 09:23:00 pid:4098 worker[none:4099] exit with status 256
+2024-10-28 09:23:00 pid:4135 Worker[4135] process terminated
+2024-10-28 09:23:00 pid:4098 worker[none:4135] exit with status 256
+2024-10-28 09:23:00 pid:4142 Worker[4142] process terminated
+2024-10-28 09:23:00 pid:4098 worker[none:4142] exit with status 256
+2024-10-28 09:23:00 pid:4149 Worker[4149] process terminated
+2024-10-28 09:23:00 pid:4098 worker[none:4149] exit with status 256
+2024-10-28 09:23:00 pid:4156 Worker[4156] process terminated
+2024-10-28 09:23:00 pid:4098 worker[none:4156] exit with status 256
+2024-10-28 09:23:01 pid:4163 Worker[4163] process terminated
+2024-10-28 09:23:01 pid:4098 worker[none:4163] exit with status 256
+2024-10-28 09:23:04 pid:4098 Workerman[think] stopping ...
+2024-10-28 09:23:04 pid:4098 Workerman[think] has been stopped
+2024-10-28 09:23:06 pid:4181 Workerman[think] start in DEBUG mode
+2024-10-28 09:24:00 pid:4182 Worker[4182] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4182] exit with status 256
+2024-10-28 09:24:00 pid:4249 Worker[4249] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4249] exit with status 256
+2024-10-28 09:24:00 pid:4256 Worker[4256] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4256] exit with status 256
+2024-10-28 09:24:00 pid:4263 Worker[4263] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4263] exit with status 256
+2024-10-28 09:24:00 pid:4270 Worker[4270] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4270] exit with status 256
+2024-10-28 09:24:00 pid:4277 Worker[4277] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4277] exit with status 256
+2024-10-28 09:24:00 pid:4284 Worker[4284] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4284] exit with status 256
+2024-10-28 09:24:00 pid:4291 Worker[4291] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4291] exit with status 256
+2024-10-28 09:24:00 pid:4298 Worker[4298] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4298] exit with status 256
+2024-10-28 09:24:00 pid:4305 Worker[4305] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4305] exit with status 256
+2024-10-28 09:24:00 pid:4312 Worker[4312] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4312] exit with status 256
+2024-10-28 09:24:00 pid:4319 Worker[4319] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4319] exit with status 256
+2024-10-28 09:24:00 pid:4326 Worker[4326] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4326] exit with status 256
+2024-10-28 09:24:00 pid:4333 Worker[4333] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4333] exit with status 256
+2024-10-28 09:24:00 pid:4340 Worker[4340] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4340] exit with status 256
+2024-10-28 09:24:00 pid:4348 Worker[4348] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4348] exit with status 256
+2024-10-28 09:24:00 pid:4355 Worker[4355] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4355] exit with status 256
+2024-10-28 09:24:00 pid:4362 Worker[4362] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4362] exit with status 256
+2024-10-28 09:24:00 pid:4369 Worker[4369] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4369] exit with status 256
+2024-10-28 09:24:00 pid:4376 Worker[4376] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4376] exit with status 256
+2024-10-28 09:24:00 pid:4383 Worker[4383] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4383] exit with status 256
+2024-10-28 09:24:00 pid:4390 Worker[4390] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4390] exit with status 256
+2024-10-28 09:24:00 pid:4397 Worker[4397] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4397] exit with status 256
+2024-10-28 09:24:00 pid:4404 Worker[4404] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4404] exit with status 256
+2024-10-28 09:24:00 pid:4411 Worker[4411] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4411] exit with status 256
+2024-10-28 09:24:00 pid:4418 Worker[4418] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4418] exit with status 256
+2024-10-28 09:24:00 pid:4425 Worker[4425] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4425] exit with status 256
+2024-10-28 09:24:00 pid:4432 Worker[4432] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4432] exit with status 256
+2024-10-28 09:24:00 pid:4439 Worker[4439] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4439] exit with status 256
+2024-10-28 09:24:00 pid:4446 Worker[4446] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4446] exit with status 256
+2024-10-28 09:24:00 pid:4453 Worker[4453] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4453] exit with status 256
+2024-10-28 09:24:00 pid:4460 Worker[4460] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4460] exit with status 256
+2024-10-28 09:24:00 pid:4467 Worker[4467] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4467] exit with status 256
+2024-10-28 09:24:00 pid:4474 Worker[4474] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4474] exit with status 256
+2024-10-28 09:24:00 pid:4481 Worker[4481] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4481] exit with status 256
+2024-10-28 09:24:00 pid:4488 Worker[4488] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4488] exit with status 256
+2024-10-28 09:24:00 pid:4495 Worker[4495] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4495] exit with status 256
+2024-10-28 09:24:00 pid:4502 Worker[4502] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4502] exit with status 256
+2024-10-28 09:24:00 pid:4509 Worker[4509] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4509] exit with status 256
+2024-10-28 09:24:00 pid:4516 Worker[4516] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4516] exit with status 256
+2024-10-28 09:24:00 pid:4523 Worker[4523] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4523] exit with status 256
+2024-10-28 09:24:00 pid:4530 Worker[4530] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4530] exit with status 256
+2024-10-28 09:24:00 pid:4537 Worker[4537] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4537] exit with status 256
+2024-10-28 09:24:00 pid:4544 Worker[4544] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4544] exit with status 256
+2024-10-28 09:24:00 pid:4551 Worker[4551] process terminated
+2024-10-28 09:24:00 pid:4181 worker[none:4551] exit with status 256
+2024-10-28 09:24:01 pid:4558 Worker[4558] process terminated
+2024-10-28 09:24:01 pid:4181 worker[none:4558] exit with status 256
+2024-10-28 09:24:10 pid:4181 Workerman[think] stopping ...
+2024-10-28 09:24:10 pid:4181 Workerman[think] has been stopped
+2024-10-28 09:24:16 pid:4581 Workerman[think] start in DEBUG mode
+2024-10-28 09:25:00 pid:4582 Worker[4582] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4582] exit with status 256
+2024-10-28 09:25:00 pid:4631 Worker[4631] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4631] exit with status 256
+2024-10-28 09:25:00 pid:4638 Worker[4638] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4638] exit with status 256
+2024-10-28 09:25:00 pid:4645 Worker[4645] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4645] exit with status 256
+2024-10-28 09:25:00 pid:4652 Worker[4652] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4652] exit with status 256
+2024-10-28 09:25:00 pid:4659 Worker[4659] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4659] exit with status 256
+2024-10-28 09:25:00 pid:4666 Worker[4666] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4666] exit with status 256
+2024-10-28 09:25:00 pid:4673 Worker[4673] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4673] exit with status 256
+2024-10-28 09:25:00 pid:4680 Worker[4680] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4680] exit with status 256
+2024-10-28 09:25:00 pid:4687 Worker[4687] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4687] exit with status 256
+2024-10-28 09:25:00 pid:4694 Worker[4694] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4694] exit with status 256
+2024-10-28 09:25:00 pid:4701 Worker[4701] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4701] exit with status 256
+2024-10-28 09:25:00 pid:4708 Worker[4708] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4708] exit with status 256
+2024-10-28 09:25:00 pid:4715 Worker[4715] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4715] exit with status 256
+2024-10-28 09:25:00 pid:4722 Worker[4722] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4722] exit with status 256
+2024-10-28 09:25:00 pid:4729 Worker[4729] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4729] exit with status 256
+2024-10-28 09:25:00 pid:4736 Worker[4736] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4736] exit with status 256
+2024-10-28 09:25:00 pid:4743 Worker[4743] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4743] exit with status 256
+2024-10-28 09:25:00 pid:4750 Worker[4750] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4750] exit with status 256
+2024-10-28 09:25:00 pid:4757 Worker[4757] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4757] exit with status 256
+2024-10-28 09:25:00 pid:4764 Worker[4764] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4764] exit with status 256
+2024-10-28 09:25:00 pid:4771 Worker[4771] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4771] exit with status 256
+2024-10-28 09:25:00 pid:4779 Worker[4779] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4779] exit with status 256
+2024-10-28 09:25:00 pid:4786 Worker[4786] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4786] exit with status 256
+2024-10-28 09:25:00 pid:4793 Worker[4793] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4793] exit with status 256
+2024-10-28 09:25:00 pid:4800 Worker[4800] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4800] exit with status 256
+2024-10-28 09:25:00 pid:4807 Worker[4807] process terminated
+2024-10-28 09:25:00 pid:4581 worker[none:4807] exit with status 256
+2024-10-28 09:25:01 pid:4814 Worker[4814] process terminated
+2024-10-28 09:25:01 pid:4581 worker[none:4814] exit with status 256
+2024-10-28 09:26:00 pid:4821 Worker[4821] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:4821] exit with status 256
+2024-10-28 09:26:00 pid:4896 Worker[4896] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:4896] exit with status 256
+2024-10-28 09:26:00 pid:4903 Worker[4903] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:4903] exit with status 256
+2024-10-28 09:26:00 pid:4910 Worker[4910] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:4910] exit with status 256
+2024-10-28 09:26:00 pid:4917 Worker[4917] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:4917] exit with status 256
+2024-10-28 09:26:00 pid:4924 Worker[4924] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:4924] exit with status 256
+2024-10-28 09:26:00 pid:4931 Worker[4931] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:4931] exit with status 256
+2024-10-28 09:26:00 pid:4938 Worker[4938] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:4938] exit with status 256
+2024-10-28 09:26:00 pid:4945 Worker[4945] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:4945] exit with status 256
+2024-10-28 09:26:00 pid:4952 Worker[4952] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:4952] exit with status 256
+2024-10-28 09:26:00 pid:4959 Worker[4959] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:4959] exit with status 256
+2024-10-28 09:26:00 pid:4966 Worker[4966] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:4966] exit with status 256
+2024-10-28 09:26:00 pid:4974 Worker[4974] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:4974] exit with status 256
+2024-10-28 09:26:00 pid:4981 Worker[4981] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:4981] exit with status 256
+2024-10-28 09:26:00 pid:4988 Worker[4988] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:4988] exit with status 256
+2024-10-28 09:26:00 pid:4995 Worker[4995] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:4995] exit with status 256
+2024-10-28 09:26:00 pid:5002 Worker[5002] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5002] exit with status 256
+2024-10-28 09:26:00 pid:5009 Worker[5009] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5009] exit with status 256
+2024-10-28 09:26:00 pid:5016 Worker[5016] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5016] exit with status 256
+2024-10-28 09:26:00 pid:5023 Worker[5023] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5023] exit with status 256
+2024-10-28 09:26:00 pid:5030 Worker[5030] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5030] exit with status 256
+2024-10-28 09:26:00 pid:5037 Worker[5037] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5037] exit with status 256
+2024-10-28 09:26:00 pid:5044 Worker[5044] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5044] exit with status 256
+2024-10-28 09:26:00 pid:5051 Worker[5051] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5051] exit with status 256
+2024-10-28 09:26:00 pid:5058 Worker[5058] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5058] exit with status 256
+2024-10-28 09:26:00 pid:5065 Worker[5065] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5065] exit with status 256
+2024-10-28 09:26:00 pid:5072 Worker[5072] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5072] exit with status 256
+2024-10-28 09:26:00 pid:5079 Worker[5079] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5079] exit with status 256
+2024-10-28 09:26:00 pid:5086 Worker[5086] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5086] exit with status 256
+2024-10-28 09:26:00 pid:5093 Worker[5093] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5093] exit with status 256
+2024-10-28 09:26:00 pid:5100 Worker[5100] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5100] exit with status 256
+2024-10-28 09:26:00 pid:5107 Worker[5107] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5107] exit with status 256
+2024-10-28 09:26:00 pid:5114 Worker[5114] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5114] exit with status 256
+2024-10-28 09:26:00 pid:5121 Worker[5121] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5121] exit with status 256
+2024-10-28 09:26:00 pid:5128 Worker[5128] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5128] exit with status 256
+2024-10-28 09:26:00 pid:5135 Worker[5135] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5135] exit with status 256
+2024-10-28 09:26:00 pid:5142 Worker[5142] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5142] exit with status 256
+2024-10-28 09:26:00 pid:5149 Worker[5149] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5149] exit with status 256
+2024-10-28 09:26:00 pid:5156 Worker[5156] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5156] exit with status 256
+2024-10-28 09:26:00 pid:5163 Worker[5163] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5163] exit with status 256
+2024-10-28 09:26:00 pid:5170 Worker[5170] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5170] exit with status 256
+2024-10-28 09:26:00 pid:5177 Worker[5177] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5177] exit with status 256
+2024-10-28 09:26:00 pid:5184 Worker[5184] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5184] exit with status 256
+2024-10-28 09:26:00 pid:5191 Worker[5191] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5191] exit with status 256
+2024-10-28 09:26:00 pid:5198 Worker[5198] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5198] exit with status 256
+2024-10-28 09:26:00 pid:5205 Worker[5205] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5205] exit with status 256
+2024-10-28 09:26:00 pid:5212 Worker[5212] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5212] exit with status 256
+2024-10-28 09:26:00 pid:5219 Worker[5219] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5219] exit with status 256
+2024-10-28 09:26:00 pid:5226 Worker[5226] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5226] exit with status 256
+2024-10-28 09:26:00 pid:5233 Worker[5233] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5233] exit with status 256
+2024-10-28 09:26:00 pid:5240 Worker[5240] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5240] exit with status 256
+2024-10-28 09:26:00 pid:5247 Worker[5247] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5247] exit with status 256
+2024-10-28 09:26:00 pid:5254 Worker[5254] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5254] exit with status 256
+2024-10-28 09:26:00 pid:5261 Worker[5261] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5261] exit with status 256
+2024-10-28 09:26:00 pid:5268 Worker[5268] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5268] exit with status 256
+2024-10-28 09:26:00 pid:5275 Worker[5275] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5275] exit with status 256
+2024-10-28 09:26:00 pid:5282 Worker[5282] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5282] exit with status 256
+2024-10-28 09:26:00 pid:5289 Worker[5289] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5289] exit with status 256
+2024-10-28 09:26:00 pid:5296 Worker[5296] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5296] exit with status 256
+2024-10-28 09:26:00 pid:5303 Worker[5303] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5303] exit with status 256
+2024-10-28 09:26:00 pid:5310 Worker[5310] process terminated
+2024-10-28 09:26:00 pid:4581 worker[none:5310] exit with status 256
+2024-10-28 09:26:01 pid:5317 Worker[5317] process terminated
+2024-10-28 09:26:01 pid:4581 worker[none:5317] exit with status 256
+2024-10-28 09:26:15 pid:4581 Workerman[think] stopping ...
+2024-10-28 09:26:15 pid:4581 Workerman[think] has been stopped
+2024-10-28 09:29:37 pid:5561 Workerman[think] start in DEBUG mode
+2024-10-28 09:29:41 pid:5561 Workerman[think] stopping ...
+2024-10-28 09:29:41 pid:5561 Workerman[think] has been stopped
+2024-12-13 17:26:44 pid:83277 Workerman[think] start in DEBUG mode
+2024-12-13 17:29:17 pid:83277 Workerman[think] stopping ...
+2024-12-13 17:29:17 pid:83277 Workerman[think] has been stopped
+2024-12-13 17:29:52 pid:83522 Workerman[think] start in DEBUG mode
+2024-12-13 17:30:54 pid:83522 Workerman[think] stopping ...
+2024-12-13 17:30:54 pid:83522 Workerman[think] has been stopped
+2024-12-13 17:37:07 pid:84000 Workerman[think] start in DEBUG mode
+2024-12-13 17:39:13 pid:84000 Workerman[think] stopping ...
+2024-12-13 17:39:13 pid:84000 Workerman[think] has been stopped
diff --git a/vendor/workerman/workerman/.gitignore b/vendor/workerman/workerman/.gitignore
new file mode 100755
index 0000000..f3f9e18
--- /dev/null
+++ b/vendor/workerman/workerman/.gitignore
@@ -0,0 +1,6 @@
+logs
+.buildpath
+.project
+.settings
+.idea
+.DS_Store
diff --git a/vendor/workerman/workerman/Autoloader.php b/vendor/workerman/workerman/Autoloader.php
new file mode 100755
index 0000000..45773c9
--- /dev/null
+++ b/vendor/workerman/workerman/Autoloader.php
@@ -0,0 +1,69 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman;
+
+/**
+ * Autoload.
+ */
+class Autoloader
+{
+    /**
+     * Autoload root path.
+     *
+     * @var string
+     */
+    protected static $_autoloadRootPath = '';
+
+    /**
+     * Set autoload root path.
+     *
+     * @param string $root_path
+     * @return void
+     */
+    public static function setRootPath($root_path)
+    {
+        self::$_autoloadRootPath = $root_path;
+    }
+
+    /**
+     * Load files by namespace.
+     *
+     * @param string $name
+     * @return boolean
+     */
+    public static function loadByNamespace($name)
+    {
+        $class_path = str_replace('\\', DIRECTORY_SEPARATOR, $name);
+        if (strpos($name, 'Workerman\\') === 0) {
+            $class_file = __DIR__ . substr($class_path, strlen('Workerman')) . '.php';
+        } else {
+            if (self::$_autoloadRootPath) {
+                $class_file = self::$_autoloadRootPath . DIRECTORY_SEPARATOR . $class_path . '.php';
+            }
+            if (empty($class_file) || !is_file($class_file)) {
+                $class_file = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . "$class_path.php";
+            }
+        }
+
+        if (is_file($class_file)) {
+            require_once($class_file);
+            if (class_exists($name, false)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
+
+spl_autoload_register('\Workerman\Autoloader::loadByNamespace');
\ No newline at end of file
diff --git a/vendor/workerman/workerman/Connection/AsyncTcpConnection.php b/vendor/workerman/workerman/Connection/AsyncTcpConnection.php
new file mode 100755
index 0000000..8800d19
--- /dev/null
+++ b/vendor/workerman/workerman/Connection/AsyncTcpConnection.php
@@ -0,0 +1,369 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Connection;
+
+use Workerman\Events\EventInterface;
+use Workerman\Lib\Timer;
+use Workerman\Worker;
+use Exception;
+
+/**
+ * AsyncTcpConnection.
+ */
+class AsyncTcpConnection extends TcpConnection
+{
+    /**
+     * Emitted when socket connection is successfully established.
+     *
+     * @var callback
+     */
+    public $onConnect = null;
+
+    /**
+     * Transport layer protocol.
+     *
+     * @var string
+     */
+    public $transport = 'tcp';
+
+    /**
+     * Status.
+     *
+     * @var int
+     */
+    protected $_status = self::STATUS_INITIAL;
+
+    /**
+     * Remote host.
+     *
+     * @var string
+     */
+    protected $_remoteHost = '';
+
+    /**
+     * Remote port.
+     *
+     * @var int
+     */
+    protected $_remotePort = 80;
+
+    /**
+     * Connect start time.
+     *
+     * @var string
+     */
+    protected $_connectStartTime = 0;
+
+    /**
+     * Remote URI.
+     *
+     * @var string
+     */
+    protected $_remoteURI = '';
+
+    /**
+     * Context option.
+     *
+     * @var array
+     */
+    protected $_contextOption = null;
+
+    /**
+     * Reconnect timer.
+     *
+     * @var int
+     */
+    protected $_reconnectTimer = null;
+
+
+    /**
+     * PHP built-in protocols.
+     *
+     * @var array
+     */
+    protected static $_builtinTransports = array(
+        'tcp'   => 'tcp',
+        'udp'   => 'udp',
+        'unix'  => 'unix',
+        'ssl'   => 'ssl',
+        'sslv2' => 'sslv2',
+        'sslv3' => 'sslv3',
+        'tls'   => 'tls'
+    );
+
+    /**
+     * Construct.
+     *
+     * @param string $remote_address
+     * @param array $context_option
+     * @throws Exception
+     */
+    public function __construct($remote_address, $context_option = null)
+    {
+        $address_info = parse_url($remote_address);
+        if (!$address_info) {
+            list($scheme, $this->_remoteAddress) = explode(':', $remote_address, 2);
+            if (!$this->_remoteAddress) {
+                Worker::safeEcho(new \Exception('bad remote_address'));
+            }
+        } else {
+            if (!isset($address_info['port'])) {
+                $address_info['port'] = 80;
+            }
+            if (!isset($address_info['path'])) {
+                $address_info['path'] = '/';
+            }
+            if (!isset($address_info['query'])) {
+                $address_info['query'] = '';
+            } else {
+                $address_info['query'] = '?' . $address_info['query'];
+            }
+            $this->_remoteAddress = "{$address_info['host']}:{$address_info['port']}";
+            $this->_remoteHost    = $address_info['host'];
+            $this->_remotePort    = $address_info['port'];
+            $this->_remoteURI     = "{$address_info['path']}{$address_info['query']}";
+            $scheme               = isset($address_info['scheme']) ? $address_info['scheme'] : 'tcp';
+        }
+
+        $this->id = $this->_id = self::$_idRecorder++;
+        if(PHP_INT_MAX === self::$_idRecorder){
+            self::$_idRecorder = 0;
+        }
+        // Check application layer protocol class.
+        if (!isset(self::$_builtinTransports[$scheme])) {
+            $scheme         = ucfirst($scheme);
+            $this->protocol = '\\Protocols\\' . $scheme;
+            if (!class_exists($this->protocol)) {
+                $this->protocol = "\\Workerman\\Protocols\\$scheme";
+                if (!class_exists($this->protocol)) {
+                    throw new Exception("class \\Protocols\\$scheme not exist");
+                }
+            }
+        } else {
+            $this->transport = self::$_builtinTransports[$scheme];
+        }
+
+        // For statistics.
+        self::$statistics['connection_count']++;
+        $this->maxSendBufferSize         = self::$defaultMaxSendBufferSize;
+        $this->_contextOption            = $context_option;
+        static::$connections[$this->_id] = $this;
+    }
+
+    /**
+     * Do connect.
+     *
+     * @return void
+     */
+    public function connect()
+    {
+        if ($this->_status !== self::STATUS_INITIAL && $this->_status !== self::STATUS_CLOSING &&
+            $this->_status !== self::STATUS_CLOSED) {
+            return;
+        }
+        $this->_status           = self::STATUS_CONNECTING;
+        $this->_connectStartTime = microtime(true);
+        if ($this->transport !== 'unix') {
+            // Open socket connection asynchronously.
+            if ($this->_contextOption) {
+                $context = stream_context_create($this->_contextOption);
+                $this->_socket = stream_socket_client("tcp://{$this->_remoteHost}:{$this->_remotePort}",
+                    $errno, $errstr, 0, STREAM_CLIENT_ASYNC_CONNECT, $context);
+            } else {
+                $this->_socket = stream_socket_client("tcp://{$this->_remoteHost}:{$this->_remotePort}",
+                    $errno, $errstr, 0, STREAM_CLIENT_ASYNC_CONNECT);
+            }
+        } else {
+            $this->_socket = stream_socket_client("{$this->transport}://{$this->_remoteAddress}", $errno, $errstr, 0,
+                STREAM_CLIENT_ASYNC_CONNECT);
+        }
+        // If failed attempt to emit onError callback.
+        if (!$this->_socket || !is_resource($this->_socket)) {
+            $this->emitError(WORKERMAN_CONNECT_FAIL, $errstr);
+            if ($this->_status === self::STATUS_CLOSING) {
+                $this->destroy();
+            }
+            if ($this->_status === self::STATUS_CLOSED) {
+                $this->onConnect = null;
+            }
+            return;
+        }
+        // Add socket to global event loop waiting connection is successfully established or faild.
+        Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'checkConnection'));
+        // For windows.
+        if(DIRECTORY_SEPARATOR === '\\') {
+            Worker::$globalEvent->add($this->_socket, EventInterface::EV_EXCEPT, array($this, 'checkConnection'));
+        }
+    }
+
+    /**
+     * Reconnect.
+     *
+     * @param int $after
+     * @return void
+     */
+    public function reconnect($after = 0)
+    {
+        $this->_status                   = self::STATUS_INITIAL;
+        static::$connections[$this->_id] = $this;
+        if ($this->_reconnectTimer) {
+            Timer::del($this->_reconnectTimer);
+        }
+        if ($after > 0) {
+            $this->_reconnectTimer = Timer::add($after, array($this, 'connect'), null, false);
+            return;
+        }
+        $this->connect();
+    }
+
+    /**
+     * CancelReconnect.
+     */
+    public function cancelReconnect()
+    {
+        if ($this->_reconnectTimer) {
+            Timer::del($this->_reconnectTimer);
+        }
+    }
+
+    /**
+     * Get remote address.
+     *
+     * @return string
+     */
+    public function getRemoteHost()
+    {
+        return $this->_remoteHost;
+    }
+
+    /**
+     * Get remote URI.
+     *
+     * @return string
+     */
+    public function getRemoteURI()
+    {
+        return $this->_remoteURI;
+    }
+
+    /**
+     * Try to emit onError callback.
+     *
+     * @param int    $code
+     * @param string $msg
+     * @return void
+     */
+    protected function emitError($code, $msg)
+    {
+        $this->_status = self::STATUS_CLOSING;
+        if ($this->onError) {
+            try {
+                call_user_func($this->onError, $this, $code, $msg);
+            } catch (\Exception $e) {
+                Worker::log($e);
+                exit(250);
+            } catch (\Error $e) {
+                Worker::log($e);
+                exit(250);
+            }
+        }
+    }
+
+    /**
+     * Check connection is successfully established or faild.
+     *
+     * @param resource $socket
+     * @return void
+     */
+    public function checkConnection()
+    {
+        // Remove EV_EXPECT for windows.
+        if(DIRECTORY_SEPARATOR === '\\') {
+            Worker::$globalEvent->del($this->_socket, EventInterface::EV_EXCEPT);
+        }
+
+        // Remove write listener.
+        Worker::$globalEvent->del($this->_socket, EventInterface::EV_WRITE);
+
+        if ($this->_status != self::STATUS_CONNECTING) {
+            return;
+        }
+
+        // Check socket state.
+        if ($address = stream_socket_get_name($this->_socket, true)) {
+            // Nonblocking.
+            stream_set_blocking($this->_socket, 0);
+            // Compatible with hhvm
+            if (function_exists('stream_set_read_buffer')) {
+                stream_set_read_buffer($this->_socket, 0);
+            }
+            // Try to open keepalive for tcp and disable Nagle algorithm.
+            if (function_exists('socket_import_stream') && $this->transport === 'tcp') {
+                $raw_socket = socket_import_stream($this->_socket);
+                socket_set_option($raw_socket, SOL_SOCKET, SO_KEEPALIVE, 1);
+                socket_set_option($raw_socket, SOL_TCP, TCP_NODELAY, 1);
+            }
+
+            // SSL handshake.
+            if ($this->transport === 'ssl') {
+                $this->_sslHandshakeCompleted = $this->doSslHandshake($this->_socket);
+            } else {
+                // There are some data waiting to send.
+                if ($this->_sendBuffer) {
+                    Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'baseWrite'));
+                }
+            }
+
+            // Register a listener waiting read event.
+            Worker::$globalEvent->add($this->_socket, EventInterface::EV_READ, array($this, 'baseRead'));
+
+            $this->_status                = self::STATUS_ESTABLISHED;
+            $this->_remoteAddress         = $address;
+
+            // Try to emit onConnect callback.
+            if ($this->onConnect) {
+                try {
+                    call_user_func($this->onConnect, $this);
+                } catch (\Exception $e) {
+                    Worker::log($e);
+                    exit(250);
+                } catch (\Error $e) {
+                    Worker::log($e);
+                    exit(250);
+                }
+            }
+            // Try to emit protocol::onConnect
+            if (method_exists($this->protocol, 'onConnect')) {
+                try {
+                    call_user_func(array($this->protocol, 'onConnect'), $this);
+                } catch (\Exception $e) {
+                    Worker::log($e);
+                    exit(250);
+                } catch (\Error $e) {
+                    Worker::log($e);
+                    exit(250);
+                }
+            }
+        } else {
+            // Connection failed.
+            $this->emitError(WORKERMAN_CONNECT_FAIL, 'connect ' . $this->_remoteAddress . ' fail after ' . round(microtime(true) - $this->_connectStartTime, 4) . ' seconds');
+            if ($this->_status === self::STATUS_CLOSING) {
+                $this->destroy();
+            }
+            if ($this->_status === self::STATUS_CLOSED) {
+                $this->onConnect = null;
+            }
+        }
+    }
+}
diff --git a/vendor/workerman/workerman/Connection/AsyncUdpConnection.php b/vendor/workerman/workerman/Connection/AsyncUdpConnection.php
new file mode 100755
index 0000000..fdae348
--- /dev/null
+++ b/vendor/workerman/workerman/Connection/AsyncUdpConnection.php
@@ -0,0 +1,209 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Connection;
+
+use Workerman\Events\EventInterface;
+use Workerman\Worker;
+use Exception;
+
+/**
+ * AsyncTcpConnection.
+ */
+class AsyncUdpConnection extends UdpConnection
+{
+    /**
+     * Emitted when socket connection is successfully established.
+     *
+     * @var callback
+     */
+    public $onConnect = null;
+
+    /**
+     * Emitted when socket connection closed.
+     *
+     * @var callback
+     */
+    public $onClose = null;
+
+    /**
+     * Connected or not.
+     *
+     * @var bool
+     */
+    protected $connected = false;
+
+    /**
+     * Context option.
+     *
+     * @var array
+     */
+    protected $_contextOption = null;
+
+    /**
+     * Construct.
+     *
+     * @param string $remote_address
+     * @throws Exception
+     */
+    public function __construct($remote_address, $context_option = null)
+    {
+        // Get the application layer communication protocol and listening address.
+        list($scheme, $address) = explode(':', $remote_address, 2);
+        // Check application layer protocol class.
+        if ($scheme !== 'udp') {
+            $scheme         = ucfirst($scheme);
+            $this->protocol = '\\Protocols\\' . $scheme;
+            if (!class_exists($this->protocol)) {
+                $this->protocol = "\\Workerman\\Protocols\\$scheme";
+                if (!class_exists($this->protocol)) {
+                    throw new Exception("class \\Protocols\\$scheme not exist");
+                }
+            }
+        }
+        
+        $this->_remoteAddress = substr($address, 2);
+        $this->_contextOption = $context_option;
+    }
+    
+    /**
+     * For udp package.
+     *
+     * @param resource $socket
+     * @return bool
+     */
+    public function baseRead($socket)
+    {
+        $recv_buffer = stream_socket_recvfrom($socket, Worker::MAX_UDP_PACKAGE_SIZE, 0, $remote_address);
+        if (false === $recv_buffer || empty($remote_address)) {
+            return false;
+        }
+        
+        if ($this->onMessage) {
+            if ($this->protocol) {
+                $parser      = $this->protocol;
+                $recv_buffer = $parser::decode($recv_buffer, $this);
+            }
+            ConnectionInterface::$statistics['total_request']++;
+            try {
+                call_user_func($this->onMessage, $this, $recv_buffer);
+            } catch (\Exception $e) {
+                Worker::log($e);
+                exit(250);
+            } catch (\Error $e) {
+                Worker::log($e);
+                exit(250);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Sends data on the connection.
+     *
+     * @param string $send_buffer
+     * @param bool   $raw
+     * @return void|boolean
+     */
+    public function send($send_buffer, $raw = false)
+    {
+        if (false === $raw && $this->protocol) {
+            $parser      = $this->protocol;
+            $send_buffer = $parser::encode($send_buffer, $this);
+            if ($send_buffer === '') {
+                return null;
+            }
+        }
+        if ($this->connected === false) {
+            $this->connect();
+        }
+        return strlen($send_buffer) === stream_socket_sendto($this->_socket, $send_buffer, 0);
+    }
+    
+    
+    /**
+     * Close connection.
+     *
+     * @param mixed $data
+     * @param bool $raw
+     *
+     * @return bool
+     */
+    public function close($data = null, $raw = false)
+    {
+        if ($data !== null) {
+            $this->send($data, $raw);
+        }
+        Worker::$globalEvent->del($this->_socket, EventInterface::EV_READ);
+        fclose($this->_socket);
+        $this->connected = false;
+        // Try to emit onClose callback.
+        if ($this->onClose) {
+            try {
+                call_user_func($this->onClose, $this);
+            } catch (\Exception $e) {
+                Worker::log($e);
+                exit(250);
+            } catch (\Error $e) {
+                Worker::log($e);
+                exit(250);
+            }
+        }
+        $this->onConnect = $this->onMessage = $this->onClose = null;
+        return true;
+    }
+
+    /**
+     * Connect.
+     *
+     * @return void
+     */
+    public function connect()
+    {
+        if ($this->connected === true) {
+            return;
+        }
+        if ($this->_contextOption) {
+            $context = stream_context_create($this->_contextOption);
+            $this->_socket = stream_socket_client("udp://{$this->_remoteAddress}", $errno, $errmsg,
+                30, STREAM_CLIENT_CONNECT, $context);
+        } else {
+            $this->_socket = stream_socket_client("udp://{$this->_remoteAddress}", $errno, $errmsg);
+        }
+
+        if (!$this->_socket) {
+            Worker::safeEcho(new \Exception($errmsg));
+            return;
+        }
+        
+        stream_set_blocking($this->_socket, false);
+        
+        if ($this->onMessage) {
+            Worker::$globalEvent->add($this->_socket, EventInterface::EV_READ, array($this, 'baseRead'));
+        }
+        $this->connected = true;
+        // Try to emit onConnect callback.
+        if ($this->onConnect) {
+            try {
+                call_user_func($this->onConnect, $this);
+            } catch (\Exception $e) {
+                Worker::log($e);
+                exit(250);
+            } catch (\Error $e) {
+                Worker::log($e);
+                exit(250);
+            }
+        }
+    }
+
+}
diff --git a/vendor/workerman/workerman/Connection/ConnectionInterface.php b/vendor/workerman/workerman/Connection/ConnectionInterface.php
new file mode 100755
index 0000000..5ddd2f8
--- /dev/null
+++ b/vendor/workerman/workerman/Connection/ConnectionInterface.php
@@ -0,0 +1,125 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Connection;
+
+/**
+ * ConnectionInterface.
+ */
+abstract class  ConnectionInterface
+{
+    /**
+     * Statistics for status command.
+     *
+     * @var array
+     */
+    public static $statistics = array(
+        'connection_count' => 0,
+        'total_request'    => 0,
+        'throw_exception'  => 0,
+        'send_fail'        => 0,
+    );
+
+    /**
+     * Emitted when data is received.
+     *
+     * @var callback
+     */
+    public $onMessage = null;
+
+    /**
+     * Emitted when the other end of the socket sends a FIN packet.
+     *
+     * @var callback
+     */
+    public $onClose = null;
+
+    /**
+     * Emitted when an error occurs with connection.
+     *
+     * @var callback
+     */
+    public $onError = null;
+
+    /**
+     * Sends data on the connection.
+     *
+     * @param string $send_buffer
+     * @return void|boolean
+     */
+    abstract public function send($send_buffer);
+
+    /**
+     * Get remote IP.
+     *
+     * @return string
+     */
+    abstract public function getRemoteIp();
+
+    /**
+     * Get remote port.
+     *
+     * @return int
+     */
+    abstract public function getRemotePort();
+
+    /**
+     * Get remote address.
+     *
+     * @return string
+     */
+    abstract public function getRemoteAddress();
+
+    /**
+     * Get local IP.
+     *
+     * @return string
+     */
+    abstract public function getLocalIp();
+
+    /**
+     * Get local port.
+     *
+     * @return int
+     */
+    abstract public function getLocalPort();
+
+    /**
+     * Get local address.
+     *
+     * @return string
+     */
+    abstract public function getLocalAddress();
+
+    /**
+     * Is ipv4.
+     *
+     * @return bool
+     */
+    abstract public function isIPv4();
+
+    /**
+     * Is ipv6.
+     *
+     * @return bool
+     */
+    abstract public function isIPv6();
+
+    /**
+     * Close connection.
+     *
+     * @param $data
+     * @return void
+     */
+    abstract public function close($data = null);
+}
diff --git a/vendor/workerman/workerman/Connection/TcpConnection.php b/vendor/workerman/workerman/Connection/TcpConnection.php
new file mode 100755
index 0000000..c6f5637
--- /dev/null
+++ b/vendor/workerman/workerman/Connection/TcpConnection.php
@@ -0,0 +1,1001 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Connection;
+
+use Workerman\Events\EventInterface;
+use Workerman\Worker;
+use Exception;
+
+/**
+ * TcpConnection.
+ */
+class TcpConnection extends ConnectionInterface
+{
+    /**
+     * Read buffer size.
+     *
+     * @var int
+     */
+    const READ_BUFFER_SIZE = 65535;
+
+    /**
+     * Status initial.
+     *
+     * @var int
+     */
+    const STATUS_INITIAL = 0;
+
+    /**
+     * Status connecting.
+     *
+     * @var int
+     */
+    const STATUS_CONNECTING = 1;
+
+    /**
+     * Status connection established.
+     *
+     * @var int
+     */
+    const STATUS_ESTABLISHED = 2;
+
+    /**
+     * Status closing.
+     *
+     * @var int
+     */
+    const STATUS_CLOSING = 4;
+
+    /**
+     * Status closed.
+     *
+     * @var int
+     */
+    const STATUS_CLOSED = 8;
+
+    /**
+     * Emitted when data is received.
+     *
+     * @var callback
+     */
+    public $onMessage = null;
+
+    /**
+     * Emitted when the other end of the socket sends a FIN packet.
+     *
+     * @var callback
+     */
+    public $onClose = null;
+
+    /**
+     * Emitted when an error occurs with connection.
+     *
+     * @var callback
+     */
+    public $onError = null;
+
+    /**
+     * Emitted when the send buffer becomes full.
+     *
+     * @var callback
+     */
+    public $onBufferFull = null;
+
+    /**
+     * Emitted when the send buffer becomes empty.
+     *
+     * @var callback
+     */
+    public $onBufferDrain = null;
+
+    /**
+     * Application layer protocol.
+     * The format is like this Workerman\\Protocols\\Http.
+     *
+     * @var \Workerman\Protocols\ProtocolInterface
+     */
+    public $protocol = null;
+
+    /**
+     * Transport (tcp/udp/unix/ssl).
+     *
+     * @var string
+     */
+    public $transport = 'tcp';
+
+    /**
+     * Which worker belong to.
+     *
+     * @var Worker
+     */
+    public $worker = null;
+
+    /**
+     * Bytes read.
+     *
+     * @var int
+     */
+    public $bytesRead = 0;
+
+    /**
+     * Bytes written.
+     *
+     * @var int
+     */
+    public $bytesWritten = 0;
+
+    /**
+     * Connection->id.
+     *
+     * @var int
+     */
+    public $id = 0;
+
+    /**
+     * A copy of $worker->id which used to clean up the connection in worker->connections
+     *
+     * @var int
+     */
+    protected $_id = 0;
+
+    /**
+     * Sets the maximum send buffer size for the current connection.
+     * OnBufferFull callback will be emited When the send buffer is full.
+     *
+     * @var int
+     */
+    public $maxSendBufferSize = 1048576;
+
+    /**
+     * Default send buffer size.
+     *
+     * @var int
+     */
+    public static $defaultMaxSendBufferSize = 1048576;
+
+    /**
+     * Sets the maximum acceptable packet size for the current connection.
+     *
+     * @var int
+     */
+    public $maxPackageSize = 1048576;
+    
+    /**
+     * Default maximum acceptable packet size.
+     *
+     * @var int
+     */
+    public static $defaultMaxPackageSize = 10485760;
+
+    /**
+     * Id recorder.
+     *
+     * @var int
+     */
+    protected static $_idRecorder = 1;
+
+    /**
+     * Socket
+     *
+     * @var resource
+     */
+    protected $_socket = null;
+
+    /**
+     * Send buffer.
+     *
+     * @var string
+     */
+    protected $_sendBuffer = '';
+
+    /**
+     * Receive buffer.
+     *
+     * @var string
+     */
+    protected $_recvBuffer = '';
+
+    /**
+     * Current package length.
+     *
+     * @var int
+     */
+    protected $_currentPackageLength = 0;
+
+    /**
+     * Connection status.
+     *
+     * @var int
+     */
+    protected $_status = self::STATUS_ESTABLISHED;
+
+    /**
+     * Remote address.
+     *
+     * @var string
+     */
+    protected $_remoteAddress = '';
+
+    /**
+     * Is paused.
+     *
+     * @var bool
+     */
+    protected $_isPaused = false;
+
+    /**
+     * SSL handshake completed or not.
+     *
+     * @var bool
+     */
+    protected $_sslHandshakeCompleted = false;
+
+    /**
+     * All connection instances.
+     *
+     * @var array
+     */
+    public static $connections = array();
+
+    /**
+     * Status to string.
+     *
+     * @var array
+     */
+    public static $_statusToString = array(
+        self::STATUS_INITIAL     => 'INITIAL',
+        self::STATUS_CONNECTING  => 'CONNECTING',
+        self::STATUS_ESTABLISHED => 'ESTABLISHED',
+        self::STATUS_CLOSING     => 'CLOSING',
+        self::STATUS_CLOSED      => 'CLOSED',
+    );
+
+
+    /**
+     * Adding support of custom functions within protocols
+     *
+     * @param string $name
+     * @param array  $arguments
+     * @return void
+     */
+    public function __call($name, $arguments) {
+        // Try to emit custom function within protocol
+        if (method_exists($this->protocol, $name)) {
+            try {
+                return call_user_func(array($this->protocol, $name), $this, $arguments);
+            } catch (\Exception $e) {
+                Worker::log($e);
+                exit(250);
+            } catch (\Error $e) {
+                Worker::log($e);
+                exit(250);
+            }
+        }
+    }
+
+    /**
+     * Construct.
+     *
+     * @param resource $socket
+     * @param string   $remote_address
+     */
+    public function __construct($socket, $remote_address = '')
+    {
+        self::$statistics['connection_count']++;
+        $this->id = $this->_id = self::$_idRecorder++;
+        if(self::$_idRecorder === PHP_INT_MAX){
+            self::$_idRecorder = 0;
+        }
+        $this->_socket = $socket;
+        stream_set_blocking($this->_socket, 0);
+        // Compatible with hhvm
+        if (function_exists('stream_set_read_buffer')) {
+            stream_set_read_buffer($this->_socket, 0);
+        }
+        Worker::$globalEvent->add($this->_socket, EventInterface::EV_READ, array($this, 'baseRead'));
+        $this->maxSendBufferSize        = self::$defaultMaxSendBufferSize;
+        $this->maxPackageSize           = self::$defaultMaxPackageSize;
+        $this->_remoteAddress           = $remote_address;
+        static::$connections[$this->id] = $this;
+    }
+
+    /**
+     * Get status.
+     *
+     * @param bool $raw_output
+     *
+     * @return int
+     */
+    public function getStatus($raw_output = true)
+    {
+        if ($raw_output) {
+            return $this->_status;
+        }
+        return self::$_statusToString[$this->_status];
+    }
+
+    /**
+     * Sends data on the connection.
+     *
+     * @param string $send_buffer
+     * @param bool  $raw
+     * @return bool|null
+     */
+    public function send($send_buffer, $raw = false)
+    {
+        if ($this->_status === self::STATUS_CLOSING || $this->_status === self::STATUS_CLOSED) {
+            return false;
+        }
+
+        // Try to call protocol::encode($send_buffer) before sending.
+        if (false === $raw && $this->protocol !== null) {
+            $parser      = $this->protocol;
+            $send_buffer = $parser::encode($send_buffer, $this);
+            if ($send_buffer === '') {
+                return null;
+            }
+        }
+
+        if ($this->_status !== self::STATUS_ESTABLISHED ||
+            ($this->transport === 'ssl' && $this->_sslHandshakeCompleted !== true)
+        ) {
+            if ($this->_sendBuffer) {
+                if ($this->bufferIsFull()) {
+                    self::$statistics['send_fail']++;
+                    return false;
+                }
+            }
+            $this->_sendBuffer .= $send_buffer;
+            $this->checkBufferWillFull();
+            return null;
+        }
+
+        // Attempt to send data directly.
+        if ($this->_sendBuffer === '') {
+            if ($this->transport === 'ssl') {
+                Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'baseWrite'));
+                $this->_sendBuffer = $send_buffer;
+                $this->checkBufferWillFull();
+                return null;
+            }
+            set_error_handler(function(){});
+            $len = fwrite($this->_socket, $send_buffer);
+            restore_error_handler();
+            // send successful.
+            if ($len === strlen($send_buffer)) {
+                $this->bytesWritten += $len;
+                return true;
+            }
+            // Send only part of the data.
+            if ($len > 0) {
+                $this->_sendBuffer = substr($send_buffer, $len);
+                $this->bytesWritten += $len;
+            } else {
+                // Connection closed?
+                if (!is_resource($this->_socket) || feof($this->_socket)) {
+                    self::$statistics['send_fail']++;
+                    if ($this->onError) {
+                        try {
+                            call_user_func($this->onError, $this, WORKERMAN_SEND_FAIL, 'client closed');
+                        } catch (\Exception $e) {
+                            Worker::log($e);
+                            exit(250);
+                        } catch (\Error $e) {
+                            Worker::log($e);
+                            exit(250);
+                        }
+                    }
+                    $this->destroy();
+                    return false;
+                }
+                $this->_sendBuffer = $send_buffer;
+            }
+            Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'baseWrite'));
+            // Check if the send buffer will be full.
+            $this->checkBufferWillFull();
+            return null;
+        } else {
+            if ($this->bufferIsFull()) {
+                self::$statistics['send_fail']++;
+                return false;
+            }
+
+            $this->_sendBuffer .= $send_buffer;
+            // Check if the send buffer is full.
+            $this->checkBufferWillFull();
+        }
+    }
+
+    /**
+     * Get remote IP.
+     *
+     * @return string
+     */
+    public function getRemoteIp()
+    {
+        $pos = strrpos($this->_remoteAddress, ':');
+        if ($pos) {
+            return substr($this->_remoteAddress, 0, $pos);
+        }
+        return '';
+    }
+
+    /**
+     * Get remote port.
+     *
+     * @return int
+     */
+    public function getRemotePort()
+    {
+        if ($this->_remoteAddress) {
+            return (int)substr(strrchr($this->_remoteAddress, ':'), 1);
+        }
+        return 0;
+    }
+
+    /**
+     * Get remote address.
+     *
+     * @return string
+     */
+    public function getRemoteAddress()
+    {
+        return $this->_remoteAddress;
+    }
+
+    /**
+     * Get local IP.
+     *
+     * @return string
+     */
+    public function getLocalIp()
+    {
+        $address = $this->getLocalAddress();
+        $pos = strrpos($address, ':');
+        if (!$pos) {
+            return '';
+        }
+        return substr($address, 0, $pos);
+    }
+
+    /**
+     * Get local port.
+     *
+     * @return int
+     */
+    public function getLocalPort()
+    {
+        $address = $this->getLocalAddress();
+        $pos = strrpos($address, ':');
+        if (!$pos) {
+            return 0;
+        }
+        return (int)substr(strrchr($address, ':'), 1);
+    }
+
+    /**
+     * Get local address.
+     *
+     * @return string
+     */
+    public function getLocalAddress()
+    {
+        return (string)@stream_socket_get_name($this->_socket, false);
+    }
+
+    /**
+     * Get send buffer queue size.
+     *
+     * @return integer
+     */
+    public function getSendBufferQueueSize()
+    {
+        return strlen($this->_sendBuffer);
+    }
+
+    /**
+     * Get recv buffer queue size.
+     *
+     * @return integer
+     */
+    public function getRecvBufferQueueSize()
+    {
+        return strlen($this->_recvBuffer);
+    }
+
+    /**
+     * Is ipv4.
+     *
+     * return bool.
+     */
+    public function isIpV4()
+    {
+        if ($this->transport === 'unix') {
+            return false;
+        }
+        return strpos($this->getRemoteIp(), ':') === false;
+    }
+
+    /**
+     * Is ipv6.
+     *
+     * return bool.
+     */
+    public function isIpV6()
+    {
+        if ($this->transport === 'unix') {
+            return false;
+        }
+        return strpos($this->getRemoteIp(), ':') !== false;
+    }
+
+    /**
+     * Pauses the reading of data. That is onMessage will not be emitted. Useful to throttle back an upload.
+     *
+     * @return void
+     */
+    public function pauseRecv()
+    {
+        Worker::$globalEvent->del($this->_socket, EventInterface::EV_READ);
+        $this->_isPaused = true;
+    }
+
+    /**
+     * Resumes reading after a call to pauseRecv.
+     *
+     * @return void
+     */
+    public function resumeRecv()
+    {
+        if ($this->_isPaused === true) {
+            Worker::$globalEvent->add($this->_socket, EventInterface::EV_READ, array($this, 'baseRead'));
+            $this->_isPaused = false;
+            $this->baseRead($this->_socket, false);
+        }
+    }
+
+
+
+    /**
+     * Base read handler.
+     *
+     * @param resource $socket
+     * @param bool $check_eof
+     * @return void
+     */
+    public function baseRead($socket, $check_eof = true)
+    {
+        // SSL handshake.
+        if ($this->transport === 'ssl' && $this->_sslHandshakeCompleted !== true) {
+            if ($this->doSslHandshake($socket)) {
+                $this->_sslHandshakeCompleted = true;
+                if ($this->_sendBuffer) {
+                    Worker::$globalEvent->add($socket, EventInterface::EV_WRITE, array($this, 'baseWrite'));
+                }
+            } else {
+                return;
+            }
+        }
+
+        set_error_handler(function(){});
+        $buffer = fread($socket, self::READ_BUFFER_SIZE);
+        restore_error_handler();
+
+        // Check connection closed.
+        if ($buffer === '' || $buffer === false) {
+            if ($check_eof && (feof($socket) || !is_resource($socket) || $buffer === false)) {
+                $this->destroy();
+                return;
+            }
+        } else {
+            $this->bytesRead += strlen($buffer);
+            $this->_recvBuffer .= $buffer;
+        }
+
+        // If the application layer protocol has been set up.
+        if ($this->protocol !== null) {
+            $parser = $this->protocol;
+            while ($this->_recvBuffer !== '' && !$this->_isPaused) {
+                // The current packet length is known.
+                if ($this->_currentPackageLength) {
+                    // Data is not enough for a package.
+                    if ($this->_currentPackageLength > strlen($this->_recvBuffer)) {
+                        break;
+                    }
+                } else {
+                    // Get current package length.
+                    set_error_handler(function($code, $msg, $file, $line){
+                        Worker::safeEcho("$msg in file $file on line $line\n");
+                    });
+                    $this->_currentPackageLength = $parser::input($this->_recvBuffer, $this);
+                    restore_error_handler();
+                    // The packet length is unknown.
+                    if ($this->_currentPackageLength === 0) {
+                        break;
+                    } elseif ($this->_currentPackageLength > 0 && $this->_currentPackageLength <= $this->maxPackageSize) {
+                        // Data is not enough for a package.
+                        if ($this->_currentPackageLength > strlen($this->_recvBuffer)) {
+                            break;
+                        }
+                    } // Wrong package.
+                    else {
+                        Worker::safeEcho('error package. package_length=' . var_export($this->_currentPackageLength, true));
+                        $this->destroy();
+                        return;
+                    }
+                }
+
+                // The data is enough for a packet.
+                self::$statistics['total_request']++;
+                // The current packet length is equal to the length of the buffer.
+                if (strlen($this->_recvBuffer) === $this->_currentPackageLength) {
+                    $one_request_buffer = $this->_recvBuffer;
+                    $this->_recvBuffer  = '';
+                } else {
+                    // Get a full package from the buffer.
+                    $one_request_buffer = substr($this->_recvBuffer, 0, $this->_currentPackageLength);
+                    // Remove the current package from the receive buffer.
+                    $this->_recvBuffer = substr($this->_recvBuffer, $this->_currentPackageLength);
+                }
+                // Reset the current packet length to 0.
+                $this->_currentPackageLength = 0;
+                if (!$this->onMessage) {
+                    continue;
+                }
+                try {
+                    // Decode request buffer before Emitting onMessage callback.
+                    call_user_func($this->onMessage, $this, $parser::decode($one_request_buffer, $this));
+                } catch (\Exception $e) {
+                    Worker::log($e);
+                    exit(250);
+                } catch (\Error $e) {
+                    Worker::log($e);
+                    exit(250);
+                }
+            }
+            return;
+        }
+
+        if ($this->_recvBuffer === '' || $this->_isPaused) {
+            return;
+        }
+
+        // Applications protocol is not set.
+        self::$statistics['total_request']++;
+        if (!$this->onMessage) {
+            $this->_recvBuffer = '';
+            return;
+        }
+        try {
+            call_user_func($this->onMessage, $this, $this->_recvBuffer);
+        } catch (\Exception $e) {
+            Worker::log($e);
+            exit(250);
+        } catch (\Error $e) {
+            Worker::log($e);
+            exit(250);
+        }
+        // Clean receive buffer.
+        $this->_recvBuffer = '';
+    }
+
+    /**
+     * Base write handler.
+     *
+     * @return void|bool
+     */
+    public function baseWrite()
+    {
+        set_error_handler(function(){});
+        if ($this->transport === 'ssl') {
+            $len = fwrite($this->_socket, $this->_sendBuffer, 8192);
+        } else {
+            $len = fwrite($this->_socket, $this->_sendBuffer);
+        }
+        restore_error_handler();
+        if ($len === strlen($this->_sendBuffer)) {
+            $this->bytesWritten += $len;
+            Worker::$globalEvent->del($this->_socket, EventInterface::EV_WRITE);
+            $this->_sendBuffer = '';
+            // Try to emit onBufferDrain callback when the send buffer becomes empty.
+            if ($this->onBufferDrain) {
+                try {
+                    call_user_func($this->onBufferDrain, $this);
+                } catch (\Exception $e) {
+                    Worker::log($e);
+                    exit(250);
+                } catch (\Error $e) {
+                    Worker::log($e);
+                    exit(250);
+                }
+            }
+            if ($this->_status === self::STATUS_CLOSING) {
+                $this->destroy();
+            }
+            return true;
+        }
+        if ($len > 0) {
+            $this->bytesWritten += $len;
+            $this->_sendBuffer = substr($this->_sendBuffer, $len);
+        } else {
+            self::$statistics['send_fail']++;
+            $this->destroy();
+        }
+    }
+
+    /**
+     * SSL handshake.
+     *
+     * @param $socket
+     * @return bool
+     */
+    public function doSslHandshake($socket){
+        if (feof($socket)) {
+            $this->destroy();
+            return false;
+        }
+        $async = $this instanceof AsyncTcpConnection;
+        
+        /**
+          *  We disabled ssl3 because https://blog.qualys.com/ssllabs/2014/10/15/ssl-3-is-dead-killed-by-the-poodle-attack.
+          *  You can enable ssl3 by the codes below.
+          */
+        /*if($async){
+            $type = STREAM_CRYPTO_METHOD_SSLv2_CLIENT | STREAM_CRYPTO_METHOD_SSLv23_CLIENT | STREAM_CRYPTO_METHOD_SSLv3_CLIENT;
+        }else{
+            $type = STREAM_CRYPTO_METHOD_SSLv2_SERVER | STREAM_CRYPTO_METHOD_SSLv23_SERVER | STREAM_CRYPTO_METHOD_SSLv3_SERVER;
+        }*/
+        
+        if($async){
+            $type = STREAM_CRYPTO_METHOD_SSLv2_CLIENT | STREAM_CRYPTO_METHOD_SSLv23_CLIENT;
+        }else{
+            $type = STREAM_CRYPTO_METHOD_SSLv2_SERVER | STREAM_CRYPTO_METHOD_SSLv23_SERVER;
+        }
+        
+        // Hidden error.
+        set_error_handler(function($errno, $errstr, $file){
+            if (!Worker::$daemonize) {
+                Worker::safeEcho("SSL handshake error: $errstr \n");
+            }
+        });
+        $ret     = stream_socket_enable_crypto($socket, true, $type);
+        restore_error_handler();
+        // Negotiation has failed.
+        if (false === $ret) {
+            $this->destroy();
+            return false;
+        } elseif (0 === $ret) {
+            // There isn't enough data and should try again.
+            return false;
+        }
+        if (isset($this->onSslHandshake)) {
+            try {
+                call_user_func($this->onSslHandshake, $this);
+            } catch (\Exception $e) {
+                Worker::log($e);
+                exit(250);
+            } catch (\Error $e) {
+                Worker::log($e);
+                exit(250);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * This method pulls all the data out of a readable stream, and writes it to the supplied destination.
+     *
+     * @param TcpConnection $dest
+     * @return void
+     */
+    public function pipe($dest)
+    {
+        $source              = $this;
+        $this->onMessage     = function ($source, $data) use ($dest) {
+            $dest->send($data);
+        };
+        $this->onClose       = function ($source) use ($dest) {
+            $dest->destroy();
+        };
+        $dest->onBufferFull  = function ($dest) use ($source) {
+            $source->pauseRecv();
+        };
+        $dest->onBufferDrain = function ($dest) use ($source) {
+            $source->resumeRecv();
+        };
+    }
+
+    /**
+     * Remove $length of data from receive buffer.
+     *
+     * @param int $length
+     * @return void
+     */
+    public function consumeRecvBuffer($length)
+    {
+        $this->_recvBuffer = substr($this->_recvBuffer, $length);
+    }
+
+    /**
+     * Close connection.
+     *
+     * @param mixed $data
+     * @param bool $raw
+     * @return void
+     */
+    public function close($data = null, $raw = false)
+    {
+        if ($this->_status === self::STATUS_CLOSING || $this->_status === self::STATUS_CLOSED) {
+            return;
+        } else {
+            if ($data !== null) {
+                $this->send($data, $raw);
+            }
+            $this->_status = self::STATUS_CLOSING;
+        }
+        if ($this->_sendBuffer === '') {
+            $this->destroy();
+        } else {
+            $this->pauseRecv();
+        }
+    }
+
+    /**
+     * Get the real socket.
+     *
+     * @return resource
+     */
+    public function getSocket()
+    {
+        return $this->_socket;
+    }
+
+    /**
+     * Check whether the send buffer will be full.
+     *
+     * @return void
+     */
+    protected function checkBufferWillFull()
+    {
+        if ($this->maxSendBufferSize <= strlen($this->_sendBuffer)) {
+            if ($this->onBufferFull) {
+                try {
+                    call_user_func($this->onBufferFull, $this);
+                } catch (\Exception $e) {
+                    Worker::log($e);
+                    exit(250);
+                } catch (\Error $e) {
+                    Worker::log($e);
+                    exit(250);
+                }
+            }
+        }
+    }
+
+    /**
+     * Whether send buffer is full.
+     *
+     * @return bool
+     */
+    protected function bufferIsFull()
+    {
+        // Buffer has been marked as full but still has data to send then the packet is discarded.
+        if ($this->maxSendBufferSize <= strlen($this->_sendBuffer)) {
+            if ($this->onError) {
+                try {
+                    call_user_func($this->onError, $this, WORKERMAN_SEND_FAIL, 'send buffer full and drop package');
+                } catch (\Exception $e) {
+                    Worker::log($e);
+                    exit(250);
+                } catch (\Error $e) {
+                    Worker::log($e);
+                    exit(250);
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+    
+    /**
+     * Whether send buffer is Empty.
+     *
+     * @return bool
+     */
+    public function bufferIsEmpty()
+    {
+    	return empty($this->_sendBuffer);
+    }
+
+    /**
+     * Destroy connection.
+     *
+     * @return void
+     */
+    public function destroy()
+    {
+        // Avoid repeated calls.
+        if ($this->_status === self::STATUS_CLOSED) {
+            return;
+        }
+        // Remove event listener.
+        Worker::$globalEvent->del($this->_socket, EventInterface::EV_READ);
+        Worker::$globalEvent->del($this->_socket, EventInterface::EV_WRITE);
+
+        // Close socket.
+        set_error_handler(function(){});
+        fclose($this->_socket);
+        restore_error_handler();
+
+        $this->_status = self::STATUS_CLOSED;
+        // Try to emit onClose callback.
+        if ($this->onClose) {
+            try {
+                call_user_func($this->onClose, $this);
+            } catch (\Exception $e) {
+                Worker::log($e);
+                exit(250);
+            } catch (\Error $e) {
+                Worker::log($e);
+                exit(250);
+            }
+        }
+        // Try to emit protocol::onClose
+        if ($this->protocol && method_exists($this->protocol, 'onClose')) {
+            try {
+                call_user_func(array($this->protocol, 'onClose'), $this);
+            } catch (\Exception $e) {
+                Worker::log($e);
+                exit(250);
+            } catch (\Error $e) {
+                Worker::log($e);
+                exit(250);
+            }
+        }
+        $this->_sendBuffer = $this->_recvBuffer = '';
+        if ($this->_status === self::STATUS_CLOSED) {
+            // Cleaning up the callback to avoid memory leaks.
+            $this->onMessage = $this->onClose = $this->onError = $this->onBufferFull = $this->onBufferDrain = null;
+            // Remove from worker->connections.
+            if ($this->worker) {
+                unset($this->worker->connections[$this->_id]);
+            }
+            unset(static::$connections[$this->_id]);
+        }
+    }
+
+    /**
+     * Destruct.
+     *
+     * @return void
+     */
+    public function __destruct()
+    {
+        static $mod;
+        self::$statistics['connection_count']--;
+        if (Worker::getGracefulStop()) {
+            if (!isset($mod)) {
+                $mod = ceil((self::$statistics['connection_count'] + 1) / 3);
+            }
+
+            if (0 === self::$statistics['connection_count'] % $mod) {
+                Worker::log('worker[' . posix_getpid() . '] remains ' . self::$statistics['connection_count'] . ' connection(s)');
+            }
+
+            if(0 === self::$statistics['connection_count']) {
+                Worker::stopAll();
+            }
+        }
+    }
+}
diff --git a/vendor/workerman/workerman/Connection/UdpConnection.php b/vendor/workerman/workerman/Connection/UdpConnection.php
new file mode 100755
index 0000000..2e7aeee
--- /dev/null
+++ b/vendor/workerman/workerman/Connection/UdpConnection.php
@@ -0,0 +1,191 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Connection;
+
+/**
+ * UdpConnection.
+ */
+class UdpConnection extends ConnectionInterface
+{
+    /**
+     * Application layer protocol.
+     * The format is like this Workerman\\Protocols\\Http.
+     *
+     * @var \Workerman\Protocols\ProtocolInterface
+     */
+    public $protocol = null;
+
+    /**
+     * Udp socket.
+     *
+     * @var resource
+     */
+    protected $_socket = null;
+
+    /**
+     * Remote address.
+     *
+     * @var string
+     */
+    protected $_remoteAddress = '';
+
+    /**
+     * Construct.
+     *
+     * @param resource $socket
+     * @param string   $remote_address
+     */
+    public function __construct($socket, $remote_address)
+    {
+        $this->_socket        = $socket;
+        $this->_remoteAddress = $remote_address;
+    }
+
+    /**
+     * Sends data on the connection.
+     *
+     * @param string $send_buffer
+     * @param bool   $raw
+     * @return void|boolean
+     */
+    public function send($send_buffer, $raw = false)
+    {
+        if (false === $raw && $this->protocol) {
+            $parser      = $this->protocol;
+            $send_buffer = $parser::encode($send_buffer, $this);
+            if ($send_buffer === '') {
+                return null;
+            }
+        }
+        return strlen($send_buffer) === stream_socket_sendto($this->_socket, $send_buffer, 0, $this->_remoteAddress);
+    }
+
+    /**
+     * Get remote IP.
+     *
+     * @return string
+     */
+    public function getRemoteIp()
+    {
+        $pos = strrpos($this->_remoteAddress, ':');
+        if ($pos) {
+            return trim(substr($this->_remoteAddress, 0, $pos), '[]');
+        }
+        return '';
+    }
+
+    /**
+     * Get remote port.
+     *
+     * @return int
+     */
+    public function getRemotePort()
+    {
+        if ($this->_remoteAddress) {
+            return (int)substr(strrchr($this->_remoteAddress, ':'), 1);
+        }
+        return 0;
+    }
+
+    /**
+     * Get remote address.
+     *
+     * @return string
+     */
+    public function getRemoteAddress()
+    {
+        return $this->_remoteAddress;
+    }
+
+    /**
+     * Get local IP.
+     *
+     * @return string
+     */
+    public function getLocalIp()
+    {
+        $address = $this->getLocalAddress();
+        $pos = strrpos($address, ':');
+        if (!$pos) {
+            return '';
+        }
+        return substr($address, 0, $pos);
+    }
+
+    /**
+     * Get local port.
+     *
+     * @return int
+     */
+    public function getLocalPort()
+    {
+        $address = $this->getLocalAddress();
+        $pos = strrpos($address, ':');
+        if (!$pos) {
+            return 0;
+        }
+        return (int)substr(strrchr($address, ':'), 1);
+    }
+
+    /**
+     * Get local address.
+     *
+     * @return string
+     */
+    public function getLocalAddress()
+    {
+        return (string)@stream_socket_get_name($this->_socket, false);
+    }
+
+    /**
+     * Is ipv4.
+     *
+     * return bool.
+     */
+    public function isIpV4()
+    {
+        if ($this->transport === 'unix') {
+            return false;
+        }
+        return strpos($this->getRemoteIp(), ':') === false;
+    }
+
+    /**
+     * Is ipv6.
+     *
+     * return bool.
+     */
+    public function isIpV6()
+    {
+        if ($this->transport === 'unix') {
+            return false;
+        }
+        return strpos($this->getRemoteIp(), ':') !== false;
+    }
+
+    /**
+     * Close connection.
+     *
+     * @param mixed $data
+     * @param bool  $raw
+     * @return bool
+     */
+    public function close($data = null, $raw = false)
+    {
+        if ($data !== null) {
+            $this->send($data, $raw);
+        }
+        return true;
+    }
+}
diff --git a/vendor/workerman/workerman/Events/Ev.php b/vendor/workerman/workerman/Events/Ev.php
new file mode 100755
index 0000000..1e6471d
--- /dev/null
+++ b/vendor/workerman/workerman/Events/Ev.php
@@ -0,0 +1,194 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author  有个鬼<42765633@qq.com>
+ * @link    http://www.workerman.net/
+ * @license http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Events;
+
+use Workerman\Worker;
+
+/**
+ * ev eventloop
+ */
+class Ev implements EventInterface
+{
+    /**
+     * All listeners for read/write event.
+     *
+     * @var array
+     */
+    protected $_allEvents = array();
+
+    /**
+     * Event listeners of signal.
+     *
+     * @var array
+     */
+    protected $_eventSignal = array();
+
+    /**
+     * All timer event listeners.
+     * [func, args, event, flag, time_interval]
+     *
+     * @var array
+     */
+    protected $_eventTimer = array();
+
+    /**
+     * Timer id.
+     *
+     * @var int
+     */
+    protected static $_timerId = 1;
+
+    /**
+     * Add a timer.
+     * {@inheritdoc}
+     */
+    public function add($fd, $flag, $func, $args = null)
+    {
+        $callback = function ($event, $socket) use ($fd, $func) {
+            try {
+                call_user_func($func, $fd);
+            } catch (\Exception $e) {
+                Worker::log($e);
+                exit(250);
+            } catch (\Error $e) {
+                Worker::log($e);
+                exit(250);
+            }
+        };
+        switch ($flag) {
+            case self::EV_SIGNAL:
+                $event                   = new \EvSignal($fd, $callback);
+                $this->_eventSignal[$fd] = $event;
+                return true;
+            case self::EV_TIMER:
+            case self::EV_TIMER_ONCE:
+                $repeat                             = $flag == self::EV_TIMER_ONCE ? 0 : $fd;
+                $param                              = array($func, (array)$args, $flag, $fd, self::$_timerId);
+                $event                              = new \EvTimer($fd, $repeat, array($this, 'timerCallback'), $param);
+                $this->_eventTimer[self::$_timerId] = $event;
+                return self::$_timerId++;
+            default :
+                $fd_key                           = (int)$fd;
+                $real_flag                        = $flag === self::EV_READ ? \Ev::READ : \Ev::WRITE;
+                $event                            = new \EvIo($fd, $real_flag, $callback);
+                $this->_allEvents[$fd_key][$flag] = $event;
+                return true;
+        }
+
+    }
+
+    /**
+     * Remove a timer.
+     * {@inheritdoc}
+     */
+    public function del($fd, $flag)
+    {
+        switch ($flag) {
+            case self::EV_READ:
+            case self::EV_WRITE:
+                $fd_key = (int)$fd;
+                if (isset($this->_allEvents[$fd_key][$flag])) {
+                    $this->_allEvents[$fd_key][$flag]->stop();
+                    unset($this->_allEvents[$fd_key][$flag]);
+                }
+                if (empty($this->_allEvents[$fd_key])) {
+                    unset($this->_allEvents[$fd_key]);
+                }
+                break;
+            case  self::EV_SIGNAL:
+                $fd_key = (int)$fd;
+                if (isset($this->_eventSignal[$fd_key])) {
+                    $this->_eventSignal[$fd_key]->stop();
+                    unset($this->_eventSignal[$fd_key]);
+                }
+                break;
+            case self::EV_TIMER:
+            case self::EV_TIMER_ONCE:
+                if (isset($this->_eventTimer[$fd])) {
+                    $this->_eventTimer[$fd]->stop();
+                    unset($this->_eventTimer[$fd]);
+                }
+                break;
+        }
+        return true;
+    }
+
+    /**
+     * Timer callback.
+     *
+     * @param \EvWatcher $event
+     */
+    public function timerCallback($event)
+    {
+        $param    = $event->data;
+        $timer_id = $param[4];
+        if ($param[2] === self::EV_TIMER_ONCE) {
+            $this->_eventTimer[$timer_id]->stop();
+            unset($this->_eventTimer[$timer_id]);
+        }
+        try {
+            call_user_func_array($param[0], $param[1]);
+        } catch (\Exception $e) {
+            Worker::log($e);
+            exit(250);
+        } catch (\Error $e) {
+            Worker::log($e);
+            exit(250);
+        }
+    }
+
+    /**
+     * Remove all timers.
+     *
+     * @return void
+     */
+    public function clearAllTimer()
+    {
+        foreach ($this->_eventTimer as $event) {
+            $event->stop();
+        }
+        $this->_eventTimer = array();
+    }
+
+    /**
+     * Main loop.
+     *
+     * @see EventInterface::loop()
+     */
+    public function loop()
+    {
+        \Ev::run();
+    }
+
+    /**
+     * Destroy loop.
+     *
+     * @return void
+     */
+    public function destroy()
+    {
+        foreach ($this->_allEvents as $event) {
+            $event->stop();
+        }
+    }
+
+    /**
+     * Get timer count.
+     *
+     * @return integer
+     */
+    public function getTimerCount()
+    {
+        return count($this->_eventTimer);
+    }
+}
diff --git a/vendor/workerman/workerman/Events/Event.php b/vendor/workerman/workerman/Events/Event.php
new file mode 100755
index 0000000..b4d371a
--- /dev/null
+++ b/vendor/workerman/workerman/Events/Event.php
@@ -0,0 +1,219 @@
+<?php 
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    有个鬼<42765633@qq.com>
+ * @copyright 有个鬼<42765633@qq.com>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Events;
+
+use Workerman\Worker;
+
+/**
+ * libevent eventloop
+ */
+class Event implements EventInterface
+{
+    /**
+     * Event base.
+     * @var object
+     */
+    protected $_eventBase = null;
+    
+    /**
+     * All listeners for read/write event.
+     * @var array
+     */
+    protected $_allEvents = array();
+    
+    /**
+     * Event listeners of signal.
+     * @var array
+     */
+    protected $_eventSignal = array();
+    
+    /**
+     * All timer event listeners.
+     * [func, args, event, flag, time_interval]
+     * @var array
+     */
+    protected $_eventTimer = array();
+
+    /**
+     * Timer id.
+     * @var int
+     */
+    protected static $_timerId = 1;
+    
+    /**
+     * construct
+     * @return void
+     */
+    public function __construct()
+    {
+        if (class_exists('\\\\EventBase', false)) {
+            $class_name = '\\\\EventBase';
+        } else {
+            $class_name = '\EventBase';
+        }
+        $this->_eventBase = new $class_name();
+    }
+   
+    /**
+     * @see EventInterface::add()
+     */
+    public function add($fd, $flag, $func, $args=array())
+    {
+        if (class_exists('\\\\Event', false)) {
+            $class_name = '\\\\Event';
+        } else {
+            $class_name = '\Event';
+        }
+        switch ($flag) {
+            case self::EV_SIGNAL:
+
+                $fd_key = (int)$fd;
+                $event = $class_name::signal($this->_eventBase, $fd, $func);
+                if (!$event||!$event->add()) {
+                    return false;
+                }
+                $this->_eventSignal[$fd_key] = $event;
+                return true;
+
+            case self::EV_TIMER:
+            case self::EV_TIMER_ONCE:
+
+                $param = array($func, (array)$args, $flag, $fd, self::$_timerId);
+                $event = new $class_name($this->_eventBase, -1, $class_name::TIMEOUT|$class_name::PERSIST, array($this, "timerCallback"), $param);
+                if (!$event||!$event->addTimer($fd)) {
+                    return false;
+                }
+                $this->_eventTimer[self::$_timerId] = $event;
+                return self::$_timerId++;
+                
+            default :
+                $fd_key = (int)$fd;
+                $real_flag = $flag === self::EV_READ ? $class_name::READ | $class_name::PERSIST : $class_name::WRITE | $class_name::PERSIST;
+                $event = new $class_name($this->_eventBase, $fd, $real_flag, $func, $fd);
+                if (!$event||!$event->add()) {
+                    return false;
+                }
+                $this->_allEvents[$fd_key][$flag] = $event;
+                return true;
+        }
+    }
+    
+    /**
+     * @see Events\EventInterface::del()
+     */
+    public function del($fd, $flag)
+    {
+        switch ($flag) {
+
+            case self::EV_READ:
+            case self::EV_WRITE:
+
+                $fd_key = (int)$fd;
+                if (isset($this->_allEvents[$fd_key][$flag])) {
+                    $this->_allEvents[$fd_key][$flag]->del();
+                    unset($this->_allEvents[$fd_key][$flag]);
+                }
+                if (empty($this->_allEvents[$fd_key])) {
+                    unset($this->_allEvents[$fd_key]);
+                }
+                break;
+
+            case  self::EV_SIGNAL:
+                $fd_key = (int)$fd;
+                if (isset($this->_eventSignal[$fd_key])) {
+                    $this->_eventSignal[$fd_key]->del();
+                    unset($this->_eventSignal[$fd_key]);
+                }
+                break;
+
+            case self::EV_TIMER:
+            case self::EV_TIMER_ONCE:
+                if (isset($this->_eventTimer[$fd])) {
+                    $this->_eventTimer[$fd]->del();
+                    unset($this->_eventTimer[$fd]);
+                }
+                break;
+        }
+        return true;
+    }
+    
+    /**
+     * Timer callback.
+     * @param null $fd
+     * @param int $what
+     * @param int $timer_id
+     */
+    public function timerCallback($fd, $what, $param)
+    {
+        $timer_id = $param[4];
+        
+        if ($param[2] === self::EV_TIMER_ONCE) {
+            $this->_eventTimer[$timer_id]->del();
+            unset($this->_eventTimer[$timer_id]);
+        }
+
+        try {
+            call_user_func_array($param[0], $param[1]);
+        } catch (\Exception $e) {
+            Worker::log($e);
+            exit(250);
+        } catch (\Error $e) {
+            Worker::log($e);
+            exit(250);
+        }
+    }
+    
+    /**
+     * @see Events\EventInterface::clearAllTimer() 
+     * @return void
+     */
+    public function clearAllTimer()
+    {
+        foreach ($this->_eventTimer as $event) {
+            $event->del();
+        }
+        $this->_eventTimer = array();
+    }
+     
+
+    /**
+     * @see EventInterface::loop()
+     */
+    public function loop()
+    {
+        $this->_eventBase->loop();
+    }
+
+    /**
+     * Destroy loop.
+     *
+     * @return void
+     */
+    public function destroy()
+    {
+        foreach ($this->_eventSignal as $event) {
+            $event->del();
+        }
+    }
+
+    /**
+     * Get timer count.
+     *
+     * @return integer
+     */
+    public function getTimerCount()
+    {
+        return count($this->_eventTimer);
+    }
+}
diff --git a/vendor/workerman/workerman/Events/EventInterface.php b/vendor/workerman/workerman/Events/EventInterface.php
new file mode 100755
index 0000000..88f38f2
--- /dev/null
+++ b/vendor/workerman/workerman/Events/EventInterface.php
@@ -0,0 +1,107 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Events;
+
+interface EventInterface
+{
+    /**
+     * Read event.
+     *
+     * @var int
+     */
+    const EV_READ = 1;
+
+    /**
+     * Write event.
+     *
+     * @var int
+     */
+    const EV_WRITE = 2;
+
+    /**
+     * Except event
+     *
+     * @var int
+     */
+    const EV_EXCEPT = 3;
+
+    /**
+     * Signal event.
+     *
+     * @var int
+     */
+    const EV_SIGNAL = 4;
+
+    /**
+     * Timer event.
+     *
+     * @var int
+     */
+    const EV_TIMER = 8;
+
+    /**
+     * Timer once event.
+     *
+     * @var int
+     */
+    const EV_TIMER_ONCE = 16;
+
+    /**
+     * Add event listener to event loop.
+     *
+     * @param mixed    $fd
+     * @param int      $flag
+     * @param callable $func
+     * @param mixed    $args
+     * @return bool
+     */
+    public function add($fd, $flag, $func, $args = null);
+
+    /**
+     * Remove event listener from event loop.
+     *
+     * @param mixed $fd
+     * @param int   $flag
+     * @return bool
+     */
+    public function del($fd, $flag);
+
+    /**
+     * Remove all timers.
+     *
+     * @return void
+     */
+    public function clearAllTimer();
+
+    /**
+     * Main loop.
+     *
+     * @return void
+     */
+    public function loop();
+
+    /**
+     * Destroy loop.
+     *
+     * @return mixed
+     */
+    public function destroy();
+
+    /**
+     * Get Timer count.
+     *
+     * @return mixed
+     */
+    public function getTimerCount();
+}
diff --git a/vendor/workerman/workerman/Events/Libevent.php b/vendor/workerman/workerman/Events/Libevent.php
new file mode 100755
index 0000000..0b3f7c0
--- /dev/null
+++ b/vendor/workerman/workerman/Events/Libevent.php
@@ -0,0 +1,227 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Events;
+
+use Workerman\Worker;
+
+/**
+ * libevent eventloop
+ */
+class Libevent implements EventInterface
+{
+    /**
+     * Event base.
+     *
+     * @var resource
+     */
+    protected $_eventBase = null;
+
+    /**
+     * All listeners for read/write event.
+     *
+     * @var array
+     */
+    protected $_allEvents = array();
+
+    /**
+     * Event listeners of signal.
+     *
+     * @var array
+     */
+    protected $_eventSignal = array();
+
+    /**
+     * All timer event listeners.
+     * [func, args, event, flag, time_interval]
+     *
+     * @var array
+     */
+    protected $_eventTimer = array();
+
+    /**
+     * construct
+     */
+    public function __construct()
+    {
+        $this->_eventBase = event_base_new();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function add($fd, $flag, $func, $args = array())
+    {
+        switch ($flag) {
+            case self::EV_SIGNAL:
+                $fd_key                      = (int)$fd;
+                $real_flag                   = EV_SIGNAL | EV_PERSIST;
+                $this->_eventSignal[$fd_key] = event_new();
+                if (!event_set($this->_eventSignal[$fd_key], $fd, $real_flag, $func, null)) {
+                    return false;
+                }
+                if (!event_base_set($this->_eventSignal[$fd_key], $this->_eventBase)) {
+                    return false;
+                }
+                if (!event_add($this->_eventSignal[$fd_key])) {
+                    return false;
+                }
+                return true;
+            case self::EV_TIMER:
+            case self::EV_TIMER_ONCE:
+                $event    = event_new();
+                $timer_id = (int)$event;
+                if (!event_set($event, 0, EV_TIMEOUT, array($this, 'timerCallback'), $timer_id)) {
+                    return false;
+                }
+
+                if (!event_base_set($event, $this->_eventBase)) {
+                    return false;
+                }
+
+                $time_interval = $fd * 1000000;
+                if (!event_add($event, $time_interval)) {
+                    return false;
+                }
+                $this->_eventTimer[$timer_id] = array($func, (array)$args, $event, $flag, $time_interval);
+                return $timer_id;
+
+            default :
+                $fd_key    = (int)$fd;
+                $real_flag = $flag === self::EV_READ ? EV_READ | EV_PERSIST : EV_WRITE | EV_PERSIST;
+
+                $event = event_new();
+
+                if (!event_set($event, $fd, $real_flag, $func, null)) {
+                    return false;
+                }
+
+                if (!event_base_set($event, $this->_eventBase)) {
+                    return false;
+                }
+
+                if (!event_add($event)) {
+                    return false;
+                }
+
+                $this->_allEvents[$fd_key][$flag] = $event;
+
+                return true;
+        }
+
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function del($fd, $flag)
+    {
+        switch ($flag) {
+            case self::EV_READ:
+            case self::EV_WRITE:
+                $fd_key = (int)$fd;
+                if (isset($this->_allEvents[$fd_key][$flag])) {
+                    event_del($this->_allEvents[$fd_key][$flag]);
+                    unset($this->_allEvents[$fd_key][$flag]);
+                }
+                if (empty($this->_allEvents[$fd_key])) {
+                    unset($this->_allEvents[$fd_key]);
+                }
+                break;
+            case  self::EV_SIGNAL:
+                $fd_key = (int)$fd;
+                if (isset($this->_eventSignal[$fd_key])) {
+                    event_del($this->_eventSignal[$fd_key]);
+                    unset($this->_eventSignal[$fd_key]);
+                }
+                break;
+            case self::EV_TIMER:
+            case self::EV_TIMER_ONCE:
+                // 这里 fd 为timerid 
+                if (isset($this->_eventTimer[$fd])) {
+                    event_del($this->_eventTimer[$fd][2]);
+                    unset($this->_eventTimer[$fd]);
+                }
+                break;
+        }
+        return true;
+    }
+
+    /**
+     * Timer callback.
+     *
+     * @param mixed $_null1
+     * @param int   $_null2
+     * @param mixed $timer_id
+     */
+    protected function timerCallback($_null1, $_null2, $timer_id)
+    {
+        if ($this->_eventTimer[$timer_id][3] === self::EV_TIMER) {
+            event_add($this->_eventTimer[$timer_id][2], $this->_eventTimer[$timer_id][4]);
+        }
+        try {
+            call_user_func_array($this->_eventTimer[$timer_id][0], $this->_eventTimer[$timer_id][1]);
+        } catch (\Exception $e) {
+            Worker::log($e);
+            exit(250);
+        } catch (\Error $e) {
+            Worker::log($e);
+            exit(250);
+        }
+        if (isset($this->_eventTimer[$timer_id]) && $this->_eventTimer[$timer_id][3] === self::EV_TIMER_ONCE) {
+            $this->del($timer_id, self::EV_TIMER_ONCE);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function clearAllTimer()
+    {
+        foreach ($this->_eventTimer as $task_data) {
+            event_del($task_data[2]);
+        }
+        $this->_eventTimer = array();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function loop()
+    {
+        event_base_loop($this->_eventBase);
+    }
+
+    /**
+     * Destroy loop.
+     *
+     * @return void
+     */
+    public function destroy()
+    {
+        foreach ($this->_eventSignal as $event) {
+            event_del($event);
+        }
+    }
+
+    /**
+     * Get timer count.
+     *
+     * @return integer
+     */
+    public function getTimerCount()
+    {
+        return count($this->_eventTimer);
+    }
+}
+
diff --git a/vendor/workerman/workerman/Events/React/Base.php b/vendor/workerman/workerman/Events/React/Base.php
new file mode 100755
index 0000000..d558b88
--- /dev/null
+++ b/vendor/workerman/workerman/Events/React/Base.php
@@ -0,0 +1,262 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Events\React;
+use Workerman\Events\EventInterface;
+use React\EventLoop\TimerInterface;
+
+/**
+ * Class StreamSelectLoop
+ * @package Workerman\Events\React
+ */
+class Base implements \React\EventLoop\LoopInterface
+{
+    /**
+     * @var array
+     */
+    protected $_timerIdMap = array();
+
+    /**
+     * @var int
+     */
+    protected $_timerIdIndex = 0;
+
+    /**
+     * @var array
+     */
+    protected $_signalHandlerMap = array();
+
+    /**
+     * @var \React\EventLoop\LoopInterface
+     */
+    protected $_eventLoop = null;
+
+    /**
+     * Base constructor.
+     */
+    public function __construct()
+    {
+        $this->_eventLoop = new \React\EventLoop\StreamSelectLoop();
+    }
+
+    /**
+     * Add event listener to event loop.
+     *
+     * @param $fd
+     * @param $flag
+     * @param $func
+     * @param array $args
+     * @return bool
+     */
+    public function add($fd, $flag, $func, $args = array())
+    {
+        $args = (array)$args;
+        switch ($flag) {
+            case EventInterface::EV_READ:
+                return $this->addReadStream($fd, $func);
+            case EventInterface::EV_WRITE:
+                return $this->addWriteStream($fd, $func);
+            case EventInterface::EV_SIGNAL:
+                if (isset($this->_signalHandlerMap[$fd])) {
+                    $this->removeSignal($fd, $this->_signalHandlerMap[$fd]);
+                }
+                $this->_signalHandlerMap[$fd] = $func;
+                return $this->addSignal($fd, $func);
+            case EventInterface::EV_TIMER:
+                $timer_obj = $this->addPeriodicTimer($fd, function() use ($func, $args) {
+                    call_user_func_array($func, $args);
+                });
+                $this->_timerIdMap[++$this->_timerIdIndex] = $timer_obj;
+                return $this->_timerIdIndex;
+            case EventInterface::EV_TIMER_ONCE:
+                $index = ++$this->_timerIdIndex;
+                $timer_obj = $this->addTimer($fd, function() use ($func, $args, $index) {
+                    $this->del($index,EventInterface::EV_TIMER_ONCE);
+                    call_user_func_array($func, $args);
+                });
+                $this->_timerIdMap[$index] = $timer_obj;
+                return $this->_timerIdIndex;
+        }
+        return false;
+    }
+
+    /**
+     * Remove event listener from event loop.
+     *
+     * @param mixed $fd
+     * @param int   $flag
+     * @return bool
+     */
+    public function del($fd, $flag)
+    {
+        switch ($flag) {
+            case EventInterface::EV_READ:
+                return $this->removeReadStream($fd);
+            case EventInterface::EV_WRITE:
+                return $this->removeWriteStream($fd);
+            case EventInterface::EV_SIGNAL:
+                if (!isset($this->_eventLoop[$fd])) {
+                    return false;
+                }
+                $func = $this->_eventLoop[$fd];
+                unset($this->_eventLoop[$fd]);
+                return $this->removeSignal($fd, $func);
+
+            case EventInterface::EV_TIMER:
+            case EventInterface::EV_TIMER_ONCE:
+                if (isset($this->_timerIdMap[$fd])){
+                    $timer_obj = $this->_timerIdMap[$fd];
+                    unset($this->_timerIdMap[$fd]);
+                    $this->cancelTimer($timer_obj);
+                    return true;
+                }
+        }
+        return false;
+    }
+
+
+    /**
+     * Main loop.
+     *
+     * @return void
+     */
+    public function loop()
+    {
+        $this->run();
+    }
+
+
+    /**
+     * Destroy loop.
+     *
+     * @return void
+     */
+    public function destroy()
+    {
+
+    }
+
+    /**
+     * Get timer count.
+     *
+     * @return integer
+     */
+    public function getTimerCount()
+    {
+        return count($this->_timerIdMap);
+    }
+
+    /**
+     * @param resource $stream
+     * @param callable $listener
+     */
+    public function addReadStream($stream, $listener)
+    {
+        return $this->_eventLoop->addReadStream($stream, $listener);
+    }
+
+    /**
+     * @param resource $stream
+     * @param callable $listener
+     */
+    public function addWriteStream($stream, $listener)
+    {
+        return $this->_eventLoop->addWriteStream($stream, $listener);
+    }
+
+    /**
+     * @param resource $stream
+     */
+    public function removeReadStream($stream)
+    {
+        return $this->_eventLoop->removeReadStream($stream);
+    }
+
+    /**
+     * @param resource $stream
+     */
+    public function removeWriteStream($stream)
+    {
+        return $this->_eventLoop->removeWriteStream($stream);
+    }
+
+    /**
+     * @param float|int $interval
+     * @param callable $callback
+     * @return \React\EventLoop\Timer\Timer|TimerInterface
+     */
+    public function addTimer($interval, $callback)
+    {
+        return $this->_eventLoop->addTimer($interval, $callback);
+    }
+
+    /**
+     * @param float|int $interval
+     * @param callable $callback
+     * @return \React\EventLoop\Timer\Timer|TimerInterface
+     */
+    public function addPeriodicTimer($interval, $callback)
+    {
+        return $this->_eventLoop->addPeriodicTimer($interval, $callback);
+    }
+
+    /**
+     * @param TimerInterface $timer
+     */
+    public function cancelTimer(TimerInterface $timer)
+    {
+        return $this->_eventLoop->cancelTimer($timer);
+    }
+
+    /**
+     * @param callable $listener
+     */
+    public function futureTick($listener)
+    {
+        return $this->_eventLoop->futureTick($listener);
+    }
+
+    /**
+     * @param int $signal
+     * @param callable $listener
+     */
+    public function addSignal($signal, $listener)
+    {
+        return $this->_eventLoop->addSignal($signal, $listener);
+    }
+
+    /**
+     * @param int $signal
+     * @param callable $listener
+     */
+    public function removeSignal($signal, $listener)
+    {
+        return $this->_eventLoop->removeSignal($signal, $listener);
+    }
+
+    /**
+     * Run.
+     */
+    public function run()
+    {
+        return $this->_eventLoop->run();
+    }
+
+    /**
+     * Stop.
+     */
+    public function stop()
+    {
+        return $this->_eventLoop->stop();
+    }
+}
diff --git a/vendor/workerman/workerman/Events/React/ExtEventLoop.php b/vendor/workerman/workerman/Events/React/ExtEventLoop.php
new file mode 100755
index 0000000..3dab25b
--- /dev/null
+++ b/vendor/workerman/workerman/Events/React/ExtEventLoop.php
@@ -0,0 +1,27 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Events\React;
+
+/**
+ * Class ExtEventLoop
+ * @package Workerman\Events\React
+ */
+class ExtEventLoop extends Base
+{
+
+    public function __construct()
+    {
+        $this->_eventLoop = new \React\EventLoop\ExtEventLoop();
+    }
+}
diff --git a/vendor/workerman/workerman/Events/React/ExtLibEventLoop.php b/vendor/workerman/workerman/Events/React/ExtLibEventLoop.php
new file mode 100755
index 0000000..eb02b35
--- /dev/null
+++ b/vendor/workerman/workerman/Events/React/ExtLibEventLoop.php
@@ -0,0 +1,27 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Events\React;
+use Workerman\Events\EventInterface;
+
+/**
+ * Class ExtLibEventLoop
+ * @package Workerman\Events\React
+ */
+class ExtLibEventLoop extends Base
+{
+    public function __construct()
+    {
+        $this->_eventLoop = new \React\EventLoop\ExtLibeventLoop();
+    }
+}
diff --git a/vendor/workerman/workerman/Events/React/StreamSelectLoop.php b/vendor/workerman/workerman/Events/React/StreamSelectLoop.php
new file mode 100755
index 0000000..7f5f94b
--- /dev/null
+++ b/vendor/workerman/workerman/Events/React/StreamSelectLoop.php
@@ -0,0 +1,26 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Events\React;
+
+/**
+ * Class StreamSelectLoop
+ * @package Workerman\Events\React
+ */
+class StreamSelectLoop extends Base
+{
+    public function __construct()
+    {
+        $this->_eventLoop = new \React\EventLoop\StreamSelectLoop();
+    }
+}
diff --git a/vendor/workerman/workerman/Events/Select.php b/vendor/workerman/workerman/Events/Select.php
new file mode 100755
index 0000000..801586b
--- /dev/null
+++ b/vendor/workerman/workerman/Events/Select.php
@@ -0,0 +1,340 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Events;
+
+/**
+ * select eventloop
+ */
+class Select implements EventInterface
+{
+    /**
+     * All listeners for read/write event.
+     *
+     * @var array
+     */
+    public $_allEvents = array();
+
+    /**
+     * Event listeners of signal.
+     *
+     * @var array
+     */
+    public $_signalEvents = array();
+
+    /**
+     * Fds waiting for read event.
+     *
+     * @var array
+     */
+    protected $_readFds = array();
+
+    /**
+     * Fds waiting for write event.
+     *
+     * @var array
+     */
+    protected $_writeFds = array();
+
+    /**
+     * Fds waiting for except event.
+     *
+     * @var array
+     */
+    protected $_exceptFds = array();
+
+    /**
+     * Timer scheduler.
+     * {['data':timer_id, 'priority':run_timestamp], ..}
+     *
+     * @var \SplPriorityQueue
+     */
+    protected $_scheduler = null;
+
+    /**
+     * All timer event listeners.
+     * [[func, args, flag, timer_interval], ..]
+     *
+     * @var array
+     */
+    protected $_eventTimer = array();
+
+    /**
+     * Timer id.
+     *
+     * @var int
+     */
+    protected $_timerId = 1;
+
+    /**
+     * Select timeout.
+     *
+     * @var int
+     */
+    protected $_selectTimeout = 100000000;
+
+    /**
+     * Paired socket channels
+     *
+     * @var array
+     */
+    protected $channel = array();
+
+    /**
+     * Construct.
+     */
+    public function __construct()
+    {
+        // Create a pipeline and put into the collection of the read to read the descriptor to avoid empty polling.
+        $this->channel = stream_socket_pair(DIRECTORY_SEPARATOR === '/' ? STREAM_PF_UNIX : STREAM_PF_INET,
+            STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
+        if($this->channel) {
+            stream_set_blocking($this->channel[0], 0);
+            $this->_readFds[0] = $this->channel[0];
+        }
+        // Init SplPriorityQueue.
+        $this->_scheduler = new \SplPriorityQueue();
+        $this->_scheduler->setExtractFlags(\SplPriorityQueue::EXTR_BOTH);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function add($fd, $flag, $func, $args = array())
+    {
+        switch ($flag) {
+            case self::EV_READ:
+            case self::EV_WRITE:
+                $count = $flag === self::EV_READ ? count($this->_readFds) : count($this->_writeFds);
+                if ($count >= 1024) {
+                    echo "Warning: system call select exceeded the maximum number of connections 1024, please install event/libevent extension for more connections.\n";
+                } else if (DIRECTORY_SEPARATOR !== '/' && $count >= 256) {
+                    echo "Warning: system call select exceeded the maximum number of connections 256.\n";
+                }
+                $fd_key                           = (int)$fd;
+                $this->_allEvents[$fd_key][$flag] = array($func, $fd);
+                if ($flag === self::EV_READ) {
+                    $this->_readFds[$fd_key] = $fd;
+                } else {
+                    $this->_writeFds[$fd_key] = $fd;
+                }
+                break;
+            case self::EV_EXCEPT:
+                $fd_key = (int)$fd;
+                $this->_allEvents[$fd_key][$flag] = array($func, $fd);
+                $this->_exceptFds[$fd_key] = $fd;
+                break;
+            case self::EV_SIGNAL:
+                // Windows not support signal.
+                if(DIRECTORY_SEPARATOR !== '/') {
+                    return false;
+                }
+                $fd_key                              = (int)$fd;
+                $this->_signalEvents[$fd_key][$flag] = array($func, $fd);
+                pcntl_signal($fd, array($this, 'signalHandler'));
+                break;
+            case self::EV_TIMER:
+            case self::EV_TIMER_ONCE:
+                $timer_id = $this->_timerId++;
+                $run_time = microtime(true) + $fd;
+                $this->_scheduler->insert($timer_id, -$run_time);
+                $this->_eventTimer[$timer_id] = array($func, (array)$args, $flag, $fd);
+                $select_timeout = ($run_time - microtime(true)) * 1000000;
+                if( $this->_selectTimeout > $select_timeout ){ 
+                    $this->_selectTimeout = $select_timeout;   
+                }  
+                return $timer_id;
+        }
+
+        return true;
+    }
+
+    /**
+     * Signal handler.
+     *
+     * @param int $signal
+     */
+    public function signalHandler($signal)
+    {
+        call_user_func_array($this->_signalEvents[$signal][self::EV_SIGNAL][0], array($signal));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function del($fd, $flag)
+    {
+        $fd_key = (int)$fd;
+        switch ($flag) {
+            case self::EV_READ:
+                unset($this->_allEvents[$fd_key][$flag], $this->_readFds[$fd_key]);
+                if (empty($this->_allEvents[$fd_key])) {
+                    unset($this->_allEvents[$fd_key]);
+                }
+                return true;
+            case self::EV_WRITE:
+                unset($this->_allEvents[$fd_key][$flag], $this->_writeFds[$fd_key]);
+                if (empty($this->_allEvents[$fd_key])) {
+                    unset($this->_allEvents[$fd_key]);
+                }
+                return true;
+            case self::EV_EXCEPT:
+                unset($this->_allEvents[$fd_key][$flag], $this->_exceptFds[$fd_key]);
+                if(empty($this->_allEvents[$fd_key]))
+                {
+                    unset($this->_allEvents[$fd_key]);
+                }
+                return true;
+            case self::EV_SIGNAL:
+                if(DIRECTORY_SEPARATOR !== '/') {
+                    return false;
+                }
+                unset($this->_signalEvents[$fd_key]);
+                pcntl_signal($fd, SIG_IGN);
+                break;
+            case self::EV_TIMER:
+            case self::EV_TIMER_ONCE;
+                unset($this->_eventTimer[$fd_key]);
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Tick for timer.
+     *
+     * @return void
+     */
+    protected function tick()
+    {
+        while (!$this->_scheduler->isEmpty()) {
+            $scheduler_data       = $this->_scheduler->top();
+            $timer_id             = $scheduler_data['data'];
+            $next_run_time        = -$scheduler_data['priority'];
+            $time_now             = microtime(true);
+            $this->_selectTimeout = ($next_run_time - $time_now) * 1000000;
+            if ($this->_selectTimeout <= 0) {
+                $this->_scheduler->extract();
+
+                if (!isset($this->_eventTimer[$timer_id])) {
+                    continue;
+                }
+
+                // [func, args, flag, timer_interval]
+                $task_data = $this->_eventTimer[$timer_id];
+                if ($task_data[2] === self::EV_TIMER) {
+                    $next_run_time = $time_now + $task_data[3];
+                    $this->_scheduler->insert($timer_id, -$next_run_time);
+                }
+                call_user_func_array($task_data[0], $task_data[1]);
+                if (isset($this->_eventTimer[$timer_id]) && $task_data[2] === self::EV_TIMER_ONCE) {
+                    $this->del($timer_id, self::EV_TIMER_ONCE);
+                }
+                continue;
+            }
+            return;
+        }
+        $this->_selectTimeout = 100000000;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function clearAllTimer()
+    {
+        $this->_scheduler = new \SplPriorityQueue();
+        $this->_scheduler->setExtractFlags(\SplPriorityQueue::EXTR_BOTH);
+        $this->_eventTimer = array();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function loop()
+    {
+        while (1) {
+            if(DIRECTORY_SEPARATOR === '/') {
+                // Calls signal handlers for pending signals
+                pcntl_signal_dispatch();
+            }
+
+            $read  = $this->_readFds;
+            $write = $this->_writeFds;
+            $except = $this->_exceptFds;
+
+            // Waiting read/write/signal/timeout events.
+            set_error_handler(function(){});
+            $ret = stream_select($read, $write, $except, 0, $this->_selectTimeout);
+            restore_error_handler();
+
+
+            if (!$this->_scheduler->isEmpty()) {
+                $this->tick();
+            }
+
+            if (!$ret) {
+                continue;
+            }
+
+            if ($read) {
+                foreach ($read as $fd) {
+                    $fd_key = (int)$fd;
+                    if (isset($this->_allEvents[$fd_key][self::EV_READ])) {
+                        call_user_func_array($this->_allEvents[$fd_key][self::EV_READ][0],
+                            array($this->_allEvents[$fd_key][self::EV_READ][1]));
+                    }
+                }
+            }
+
+            if ($write) {
+                foreach ($write as $fd) {
+                    $fd_key = (int)$fd;
+                    if (isset($this->_allEvents[$fd_key][self::EV_WRITE])) {
+                        call_user_func_array($this->_allEvents[$fd_key][self::EV_WRITE][0],
+                            array($this->_allEvents[$fd_key][self::EV_WRITE][1]));
+                    }
+                }
+            }
+
+            if($except) {
+                foreach($except as $fd) {
+                    $fd_key = (int) $fd;
+                    if(isset($this->_allEvents[$fd_key][self::EV_EXCEPT])) {
+                        call_user_func_array($this->_allEvents[$fd_key][self::EV_EXCEPT][0],
+                            array($this->_allEvents[$fd_key][self::EV_EXCEPT][1]));
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Destroy loop.
+     *
+     * @return void
+     */
+    public function destroy()
+    {
+
+    }
+
+    /**
+     * Get timer count.
+     *
+     * @return integer
+     */
+    public function getTimerCount()
+    {
+        return count($this->_eventTimer);
+    }
+}
diff --git a/vendor/workerman/workerman/Events/Swoole.php b/vendor/workerman/workerman/Events/Swoole.php
new file mode 100755
index 0000000..40ef499
--- /dev/null
+++ b/vendor/workerman/workerman/Events/Swoole.php
@@ -0,0 +1,221 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    Ares<aresrr#qq.com>
+ * @link      http://www.workerman.net/
+ * @link      https://github.com/ares333/Workerman
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Events;
+
+use Swoole\Event;
+use Swoole\Timer;
+
+class Swoole implements EventInterface
+{
+
+    protected $_timer = array();
+
+    protected $_timerOnceMap = array();
+
+    protected $mapId = 0;
+
+    protected $_fd = array();
+
+    // milisecond
+    public static $signalDispatchInterval = 200;
+
+    protected $_hasSignal = false;
+
+    /**
+     *
+     * {@inheritdoc}
+     *
+     * @see \Workerman\Events\EventInterface::add()
+     */
+    public function add($fd, $flag, $func, $args = null)
+    {
+        if (! isset($args)) {
+            $args = array();
+        }
+        switch ($flag) {
+            case self::EV_SIGNAL:
+                $res = pcntl_signal($fd, $func, false);
+                if (! $this->_hasSignal && $res) {
+                    Timer::tick(static::$signalDispatchInterval,
+                        function () {
+                            pcntl_signal_dispatch();
+                        });
+                    $this->_hasSignal = true;
+                }
+                return $res;
+            case self::EV_TIMER:
+            case self::EV_TIMER_ONCE:
+                $method = self::EV_TIMER == $flag ? 'tick' : 'after';
+                if ($this->mapId > PHP_INT_MAX) {
+                    $this->mapId = 0;
+                }
+                $mapId = $this->mapId++;
+                $timer_id = Timer::$method($fd * 1000,
+                    function ($timer_id = null) use ($func, $args, $mapId) {
+                        call_user_func_array($func, $args);
+                        // EV_TIMER_ONCE
+                        if (! isset($timer_id)) {
+                            // may be deleted in $func
+                            if (array_key_exists($mapId, $this->_timerOnceMap)) {
+                                $timer_id = $this->_timerOnceMap[$mapId];
+                                unset($this->_timer[$timer_id],
+                                    $this->_timerOnceMap[$mapId]);
+                            }
+                        }
+                    });
+                if ($flag == self::EV_TIMER_ONCE) {
+                    $this->_timerOnceMap[$mapId] = $timer_id;
+                    $this->_timer[$timer_id] = $mapId;
+                } else {
+                    $this->_timer[$timer_id] = null;
+                }
+                return $timer_id;
+            case self::EV_READ:
+            case self::EV_WRITE:
+                $fd_key = (int) $fd;
+                if (! isset($this->_fd[$fd_key])) {
+                    if ($flag == self::EV_READ) {
+                        $res = Event::add($fd, $func, null, SWOOLE_EVENT_READ);
+                        $fd_type = SWOOLE_EVENT_READ;
+                    } else {
+                        $res = Event::add($fd, null, $func, SWOOLE_EVENT_WRITE);
+                        $fd_type = SWOOLE_EVENT_WRITE;
+                    }
+                    if ($res) {
+                        $this->_fd[$fd_key] = $fd_type;
+                    }
+                } else {
+                    $fd_val = $this->_fd[$fd_key];
+                    $res = true;
+                    if ($flag == self::EV_READ) {
+                        if (($fd_val & SWOOLE_EVENT_READ) != SWOOLE_EVENT_READ) {
+                            $res = Event::set($fd, $func, null,
+                                SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE);
+                            $this->_fd[$fd_key] |= SWOOLE_EVENT_READ;
+                        }
+                    } else {
+                        if (($fd_val & SWOOLE_EVENT_WRITE) != SWOOLE_EVENT_WRITE) {
+                            $res = Event::set($fd, null, $func,
+                                SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE);
+                            $this->_fd[$fd_key] |= SWOOLE_EVENT_WRITE;
+                        }
+                    }
+                }
+                return $res;
+        }
+    }
+
+    /**
+     *
+     * {@inheritdoc}
+     *
+     * @see \Workerman\Events\EventInterface::del()
+     */
+    public function del($fd, $flag)
+    {
+        switch ($flag) {
+            case self::EV_SIGNAL:
+                return pcntl_signal($fd, SIG_IGN, false);
+            case self::EV_TIMER:
+            case self::EV_TIMER_ONCE:
+                // already remove in EV_TIMER_ONCE callback.
+                if (! array_key_exists($fd, $this->_timer)) {
+                    return true;
+                }
+                $res = Timer::clear($fd);
+                if ($res) {
+                    $mapId = $this->_timer[$fd];
+                    if (isset($mapId)) {
+                        unset($this->_timerOnceMap[$mapId]);
+                    }
+                    unset($this->_timer[$fd]);
+                }
+                return $res;
+            case self::EV_READ:
+            case self::EV_WRITE:
+                $fd_key = (int) $fd;
+                if (isset($this->_fd[$fd_key])) {
+                    $fd_val = $this->_fd[$fd_key];
+                    if ($flag == self::EV_READ) {
+                        $flag_remove = ~ SWOOLE_EVENT_READ;
+                    } else {
+                        $flag_remove = ~ SWOOLE_EVENT_WRITE;
+                    }
+                    $fd_val &= $flag_remove;
+                    if (0 === $fd_val) {
+                        $res = Event::del($fd);
+                        if ($res) {
+                            unset($this->_fd[$fd_key]);
+                        }
+                    } else {
+                        $res = Event::set($fd, null, null, $fd_val);
+                        if ($res) {
+                            $this->_fd[$fd_key] = $fd_val;
+                        }
+                    }
+                } else {
+                    $res = true;
+                }
+                return $res;
+        }
+    }
+
+    /**
+     *
+     * {@inheritdoc}
+     *
+     * @see \Workerman\Events\EventInterface::clearAllTimer()
+     */
+    public function clearAllTimer()
+    {
+        foreach (array_keys($this->_timer) as $v) {
+            Timer::clear($v);
+        }
+        $this->_timer = array();
+        $this->_timerOnceMap = array();
+    }
+
+    /**
+     *
+     * {@inheritdoc}
+     *
+     * @see \Workerman\Events\EventInterface::loop()
+     */
+    public function loop()
+    {
+        Event::wait();
+    }
+
+    /**
+     *
+     * {@inheritdoc}
+     *
+     * @see \Workerman\Events\EventInterface::destroy()
+     */
+    public function destroy()
+    {
+        //Event::exit();
+    }
+
+    /**
+     *
+     * {@inheritdoc}
+     *
+     * @see \Workerman\Events\EventInterface::getTimerCount()
+     */
+    public function getTimerCount()
+    {
+        return count($this->_timer);
+    }
+}
diff --git a/vendor/workerman/workerman/Lib/Constants.php b/vendor/workerman/workerman/Lib/Constants.php
new file mode 100755
index 0000000..1b3561b
--- /dev/null
+++ b/vendor/workerman/workerman/Lib/Constants.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+// Display errors.
+ini_set('display_errors', 'on');
+// Reporting all.
+error_reporting(E_ALL);
+
+// Reset opcache.
+if (function_exists('opcache_reset')) {
+    opcache_reset();
+}
+
+// For onError callback.
+define('WORKERMAN_CONNECT_FAIL', 1);
+// For onError callback.
+define('WORKERMAN_SEND_FAIL', 2);
+
+// Define OS Type
+define('OS_TYPE_LINUX', 'linux');
+define('OS_TYPE_WINDOWS', 'windows');
+
+// Compatible with php7
+if(!class_exists('Error'))
+{
+    class Error extends Exception
+    {
+    }
+}
diff --git a/vendor/workerman/workerman/Lib/Timer.php b/vendor/workerman/workerman/Lib/Timer.php
new file mode 100755
index 0000000..2dc2302
--- /dev/null
+++ b/vendor/workerman/workerman/Lib/Timer.php
@@ -0,0 +1,179 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Lib;
+
+use Workerman\Events\EventInterface;
+use Workerman\Worker;
+use Exception;
+
+/**
+ * Timer.
+ *
+ * example:
+ * Workerman\Lib\Timer::add($time_interval, callback, array($arg1, $arg2..));
+ */
+class Timer
+{
+    /**
+     * Tasks that based on ALARM signal.
+     * [
+     *   run_time => [[$func, $args, $persistent, time_interval],[$func, $args, $persistent, time_interval],..]],
+     *   run_time => [[$func, $args, $persistent, time_interval],[$func, $args, $persistent, time_interval],..]],
+     *   ..
+     * ]
+     *
+     * @var array
+     */
+    protected static $_tasks = array();
+
+    /**
+     * event
+     *
+     * @var \Workerman\Events\EventInterface
+     */
+    protected static $_event = null;
+
+    /**
+     * Init.
+     *
+     * @param \Workerman\Events\EventInterface $event
+     * @return void
+     */
+    public static function init($event = null)
+    {
+        if ($event) {
+            self::$_event = $event;
+        } else {
+            if (function_exists('pcntl_signal')) {
+                pcntl_signal(SIGALRM, array('\Workerman\Lib\Timer', 'signalHandle'), false);
+            }
+        }
+    }
+
+    /**
+     * ALARM signal handler.
+     *
+     * @return void
+     */
+    public static function signalHandle()
+    {
+        if (!self::$_event) {
+            pcntl_alarm(1);
+            self::tick();
+        }
+    }
+
+    /**
+     * Add a timer.
+     *
+     * @param float    $time_interval
+     * @param callable $func
+     * @param mixed    $args
+     * @param bool     $persistent
+     * @return int/false
+     */
+    public static function add($time_interval, $func, $args = array(), $persistent = true)
+    {
+        if ($time_interval <= 0) {
+            Worker::safeEcho(new Exception("bad time_interval"));
+            return false;
+        }
+
+        if (self::$_event) {
+            return self::$_event->add($time_interval,
+                $persistent ? EventInterface::EV_TIMER : EventInterface::EV_TIMER_ONCE, $func, $args);
+        }
+
+        if (!is_callable($func)) {
+            Worker::safeEcho(new Exception("not callable"));
+            return false;
+        }
+
+        if (empty(self::$_tasks)) {
+            pcntl_alarm(1);
+        }
+
+        $time_now = time();
+        $run_time = $time_now + $time_interval;
+        if (!isset(self::$_tasks[$run_time])) {
+            self::$_tasks[$run_time] = array();
+        }
+        self::$_tasks[$run_time][] = array($func, (array)$args, $persistent, $time_interval);
+        return 1;
+    }
+
+
+    /**
+     * Tick.
+     *
+     * @return void
+     */
+    public static function tick()
+    {
+        if (empty(self::$_tasks)) {
+            pcntl_alarm(0);
+            return;
+        }
+
+        $time_now = time();
+        foreach (self::$_tasks as $run_time => $task_data) {
+            if ($time_now >= $run_time) {
+                foreach ($task_data as $index => $one_task) {
+                    $task_func     = $one_task[0];
+                    $task_args     = $one_task[1];
+                    $persistent    = $one_task[2];
+                    $time_interval = $one_task[3];
+                    try {
+                        call_user_func_array($task_func, $task_args);
+                    } catch (\Exception $e) {
+                        Worker::safeEcho($e);
+                    }
+                    if ($persistent) {
+                        self::add($time_interval, $task_func, $task_args);
+                    }
+                }
+                unset(self::$_tasks[$run_time]);
+            }
+        }
+    }
+
+    /**
+     * Remove a timer.
+     *
+     * @param mixed $timer_id
+     * @return bool
+     */
+    public static function del($timer_id)
+    {
+        if (self::$_event) {
+            return self::$_event->del($timer_id, EventInterface::EV_TIMER);
+        }
+
+        return false;
+    }
+
+    /**
+     * Remove all timers.
+     *
+     * @return void
+     */
+    public static function delAll()
+    {
+        self::$_tasks = array();
+        pcntl_alarm(0);
+        if (self::$_event) {
+            self::$_event->clearAllTimer();
+        }
+    }
+}
diff --git a/vendor/workerman/workerman/MIT-LICENSE.txt b/vendor/workerman/workerman/MIT-LICENSE.txt
new file mode 100755
index 0000000..fd6b1c8
--- /dev/null
+++ b/vendor/workerman/workerman/MIT-LICENSE.txt
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2009-2015 walkor<walkor@workerman.net> and contributors (see https://github.com/walkor/workerman/contributors)
+
+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.
diff --git a/vendor/workerman/workerman/Protocols/Frame.php b/vendor/workerman/workerman/Protocols/Frame.php
new file mode 100755
index 0000000..4a6b13e
--- /dev/null
+++ b/vendor/workerman/workerman/Protocols/Frame.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Protocols;
+
+use Workerman\Connection\TcpConnection;
+
+/**
+ * Frame Protocol.
+ */
+class Frame
+{
+    /**
+     * Check the integrity of the package.
+     *
+     * @param string        $buffer
+     * @param TcpConnection $connection
+     * @return int
+     */
+    public static function input($buffer, TcpConnection $connection)
+    {
+        if (strlen($buffer) < 4) {
+            return 0;
+        }
+        $unpack_data = unpack('Ntotal_length', $buffer);
+        return $unpack_data['total_length'];
+    }
+
+    /**
+     * Decode.
+     *
+     * @param string $buffer
+     * @return string
+     */
+    public static function decode($buffer)
+    {
+        return substr($buffer, 4);
+    }
+
+    /**
+     * Encode.
+     *
+     * @param string $buffer
+     * @return string
+     */
+    public static function encode($buffer)
+    {
+        $total_length = 4 + strlen($buffer);
+        return pack('N', $total_length) . $buffer;
+    }
+}
diff --git a/vendor/workerman/workerman/Protocols/Http.php b/vendor/workerman/workerman/Protocols/Http.php
new file mode 100755
index 0000000..12f7a2f
--- /dev/null
+++ b/vendor/workerman/workerman/Protocols/Http.php
@@ -0,0 +1,701 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Protocols;
+
+use Workerman\Connection\TcpConnection;
+use Workerman\Worker;
+
+/**
+ * http protocol
+ */
+class Http
+{
+    /**
+      * The supported HTTP methods
+      * @var array
+      */
+    public static $methods = array('GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS');
+
+    /**
+     * Check the integrity of the package.
+     *
+     * @param string        $recv_buffer
+     * @param TcpConnection $connection
+     * @return int
+     */
+    public static function input($recv_buffer, TcpConnection $connection)
+    {
+        if (!strpos($recv_buffer, "\r\n\r\n")) {
+            // Judge whether the package length exceeds the limit.
+            if (strlen($recv_buffer) >= $connection->maxPackageSize) {
+                $connection->close();
+                return 0;
+            }
+            return 0;
+        }
+
+        list($header,) = explode("\r\n\r\n", $recv_buffer, 2);
+        $method = substr($header, 0, strpos($header, ' '));
+
+        if(in_array($method, static::$methods)) {
+            return static::getRequestSize($header, $method);
+        }else{
+            $connection->send("HTTP/1.1 400 Bad Request\r\n\r\n", true);
+            return 0;
+        }
+    }
+
+    /**
+      * Get whole size of the request
+      * includes the request headers and request body.
+      * @param string $header The request headers
+      * @param string $method The request method
+      * @return integer
+      */
+    protected static function getRequestSize($header, $method)
+    {
+        if($method === 'GET' || $method === 'OPTIONS' || $method === 'HEAD') {
+            return strlen($header) + 4;
+        }
+        $match = array();
+        if (preg_match("/\r\nContent-Length: ?(\d+)/i", $header, $match)) {
+            $content_length = isset($match[1]) ? $match[1] : 0;
+            return $content_length + strlen($header) + 4;
+        }
+        return $method === 'DELETE' ? strlen($header) + 4 : 0;
+    }
+
+    /**
+     * Parse $_POST、$_GET、$_COOKIE.
+     *
+     * @param string        $recv_buffer
+     * @param TcpConnection $connection
+     * @return array
+     */
+    public static function decode($recv_buffer, TcpConnection $connection)
+    {
+        // Init.
+        $_POST                         = $_GET = $_COOKIE = $_REQUEST = $_SESSION = $_FILES = array();
+        $GLOBALS['HTTP_RAW_POST_DATA'] = '';
+        // Clear cache.
+        HttpCache::$header   = array('Connection' => 'Connection: keep-alive');
+        HttpCache::$instance = new HttpCache();
+        // $_SERVER
+        $_SERVER = array(
+            'QUERY_STRING'         => '',
+            'REQUEST_METHOD'       => '',
+            'REQUEST_URI'          => '',
+            'SERVER_PROTOCOL'      => '',
+            'SERVER_SOFTWARE'      => 'workerman/'.Worker::VERSION,
+            'SERVER_NAME'          => '',
+            'HTTP_HOST'            => '',
+            'HTTP_USER_AGENT'      => '',
+            'HTTP_ACCEPT'          => '',
+            'HTTP_ACCEPT_LANGUAGE' => '',
+            'HTTP_ACCEPT_ENCODING' => '',
+            'HTTP_COOKIE'          => '',
+            'HTTP_CONNECTION'      => '',
+            'CONTENT_TYPE'         => '',
+            'REMOTE_ADDR'          => '',
+            'REMOTE_PORT'          => '0',
+            'REQUEST_TIME'         => time()
+        );
+
+        // Parse headers.
+        list($http_header, $http_body) = explode("\r\n\r\n", $recv_buffer, 2);
+        $header_data = explode("\r\n", $http_header);
+
+        list($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_SERVER['SERVER_PROTOCOL']) = explode(' ',
+            $header_data[0]);
+
+        $http_post_boundary = '';
+        unset($header_data[0]);
+        foreach ($header_data as $content) {
+            // \r\n\r\n
+            if (empty($content)) {
+                continue;
+            }
+            list($key, $value)       = explode(':', $content, 2);
+            $key                     = str_replace('-', '_', strtoupper($key));
+            $value                   = trim($value);
+            $_SERVER['HTTP_' . $key] = $value;
+            switch ($key) {
+                // HTTP_HOST
+                case 'HOST':
+                    $tmp                    = explode(':', $value);
+                    $_SERVER['SERVER_NAME'] = $tmp[0];
+                    if (isset($tmp[1])) {
+                        $_SERVER['SERVER_PORT'] = $tmp[1];
+                    }
+                    break;
+                // cookie
+                case 'COOKIE':
+                    parse_str(str_replace('; ', '&', $_SERVER['HTTP_COOKIE']), $_COOKIE);
+                    break;
+                // content-type
+                case 'CONTENT_TYPE':
+                    if (!preg_match('/boundary="?(\S+)"?/', $value, $match)) {
+                        if ($pos = strpos($value, ';')) {
+                            $_SERVER['CONTENT_TYPE'] = substr($value, 0, $pos);
+                        } else {
+                            $_SERVER['CONTENT_TYPE'] = $value;
+                        }
+                    } else {
+                        $_SERVER['CONTENT_TYPE'] = 'multipart/form-data';
+                        $http_post_boundary      = '--' . $match[1];
+                    }
+                    break;
+                case 'CONTENT_LENGTH':
+                    $_SERVER['CONTENT_LENGTH'] = $value;
+                    break;
+                case 'UPGRADE':
+					if($value=='websocket'){
+						$connection->protocol = "\\Workerman\\Protocols\\Websocket";
+						return \Workerman\Protocols\Websocket::input($recv_buffer,$connection);
+					}
+                    break;
+            }
+        }
+		if(isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE){
+			HttpCache::$gzip = true;
+		}
+        // Parse $_POST.
+        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+            if (isset($_SERVER['CONTENT_TYPE'])) {
+                switch ($_SERVER['CONTENT_TYPE']) {
+                    case 'multipart/form-data':
+                        self::parseUploadFiles($http_body, $http_post_boundary);
+                        break;
+                    case 'application/json':
+                        $_POST = json_decode($http_body, true);
+                        break;
+                    case 'application/x-www-form-urlencoded':
+                        parse_str($http_body, $_POST);
+                        break;
+                }
+            }
+        }
+
+        // Parse other HTTP action parameters
+        if ($_SERVER['REQUEST_METHOD'] != 'GET' && $_SERVER['REQUEST_METHOD'] != "POST") {
+            $data = array();
+            if ($_SERVER['CONTENT_TYPE'] === "application/x-www-form-urlencoded") {
+                parse_str($http_body, $data);
+            } elseif ($_SERVER['CONTENT_TYPE'] === "application/json") {
+                $data = json_decode($http_body, true);
+            }
+            $_REQUEST = array_merge($_REQUEST, $data);
+        }
+
+        // HTTP_RAW_REQUEST_DATA HTTP_RAW_POST_DATA
+        $GLOBALS['HTTP_RAW_REQUEST_DATA'] = $GLOBALS['HTTP_RAW_POST_DATA'] = $http_body;
+
+        // QUERY_STRING
+        $_SERVER['QUERY_STRING'] = parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY);
+        if ($_SERVER['QUERY_STRING']) {
+            // $GET
+            parse_str($_SERVER['QUERY_STRING'], $_GET);
+        } else {
+            $_SERVER['QUERY_STRING'] = '';
+        }
+
+        if (is_array($_POST)) {
+            // REQUEST
+            $_REQUEST = array_merge($_GET, $_POST, $_REQUEST);
+        } else {
+            // REQUEST
+            $_REQUEST = array_merge($_GET, $_REQUEST);
+        }
+
+        // REMOTE_ADDR REMOTE_PORT
+        $_SERVER['REMOTE_ADDR'] = $connection->getRemoteIp();
+        $_SERVER['REMOTE_PORT'] = $connection->getRemotePort();
+
+        return array('get' => $_GET, 'post' => $_POST, 'cookie' => $_COOKIE, 'server' => $_SERVER, 'files' => $_FILES);
+    }
+
+    /**
+     * Http encode.
+     *
+     * @param string        $content
+     * @param TcpConnection $connection
+     * @return string
+     */
+    public static function encode($content, TcpConnection $connection)
+    {
+        // Default http-code.
+        if (!isset(HttpCache::$header['Http-Code'])) {
+            $header = "HTTP/1.1 200 OK\r\n";
+        } else {
+            $header = HttpCache::$header['Http-Code'] . "\r\n";
+            unset(HttpCache::$header['Http-Code']);
+        }
+
+        // Content-Type
+        if (!isset(HttpCache::$header['Content-Type'])) {
+            $header .= "Content-Type: text/html;charset=utf-8\r\n";
+        }
+
+        // other headers
+        foreach (HttpCache::$header as $key => $item) {
+            if ('Set-Cookie' === $key && is_array($item)) {
+                foreach ($item as $it) {
+                    $header .= $it . "\r\n";
+                }
+            } else {
+                $header .= $item . "\r\n";
+            }
+        }
+		if(HttpCache::$gzip && isset($connection->gzip) && $connection->gzip){
+			$header .= "Content-Encoding: gzip\r\n";
+			$content = gzencode($content,$connection->gzip);
+		}
+        // header
+        $header .= "Server: workerman/" . Worker::VERSION . "\r\nContent-Length: " . strlen($content) . "\r\n\r\n";
+
+        // save session
+        self::sessionWriteClose();
+
+        // the whole http package
+        return $header . $content;
+    }
+
+    /**
+     * 设置http头
+     *
+     * @return bool|void
+     */
+    public static function header($content, $replace = true, $http_response_code = 0)
+    {
+        if (PHP_SAPI != 'cli') {
+            return $http_response_code ? header($content, $replace, $http_response_code) : header($content, $replace);
+        }
+        if (strpos($content, 'HTTP') === 0) {
+            $key = 'Http-Code';
+        } else {
+            $key = strstr($content, ":", true);
+            if (empty($key)) {
+                return false;
+            }
+        }
+
+        if ('location' === strtolower($key) && !$http_response_code) {
+            return self::header($content, true, 302);
+        }
+
+        if (isset(HttpCache::$codes[$http_response_code])) {
+            HttpCache::$header['Http-Code'] = "HTTP/1.1 $http_response_code " . HttpCache::$codes[$http_response_code];
+            if ($key === 'Http-Code') {
+                return true;
+            }
+        }
+
+        if ($key === 'Set-Cookie') {
+            HttpCache::$header[$key][] = $content;
+        } else {
+            HttpCache::$header[$key] = $content;
+        }
+
+        return true;
+    }
+
+    /**
+     * Remove header.
+     *
+     * @param string $name
+     * @return void
+     */
+    public static function headerRemove($name)
+    {
+        if (PHP_SAPI != 'cli') {
+            header_remove($name);
+            return;
+        }
+        unset(HttpCache::$header[$name]);
+    }
+
+    /**
+     * Set cookie.
+     *
+     * @param string  $name
+     * @param string  $value
+     * @param integer $maxage
+     * @param string  $path
+     * @param string  $domain
+     * @param bool    $secure
+     * @param bool    $HTTPOnly
+     * @return bool|void
+     */
+    public static function setcookie(
+        $name,
+        $value = '',
+        $maxage = 0,
+        $path = '',
+        $domain = '',
+        $secure = false,
+        $HTTPOnly = false
+    ) {
+        if (PHP_SAPI != 'cli') {
+            return setcookie($name, $value, $maxage, $path, $domain, $secure, $HTTPOnly);
+        }
+        return self::header(
+            'Set-Cookie: ' . $name . '=' . rawurlencode($value)
+            . (empty($domain) ? '' : '; Domain=' . $domain)
+            . (empty($maxage) ? '' : '; Max-Age=' . $maxage)
+            . (empty($path) ? '' : '; Path=' . $path)
+            . (!$secure ? '' : '; Secure')
+            . (!$HTTPOnly ? '' : '; HttpOnly'), false);
+    }
+
+    /**
+     * sessionCreateId
+     *
+     * @return string
+     */
+    public static function sessionCreateId()
+    {
+        mt_srand();
+        return bin2hex(pack('d', microtime(true)) . pack('N',mt_rand(0, 2147483647)));
+    }
+
+    /**
+     * sessionId
+     *
+     * @param string  $id
+     *
+     * @return string|null
+     */
+    public static function sessionId($id = null)
+    {
+        if (PHP_SAPI != 'cli') {
+            return $id ? session_id($id) : session_id();
+        }
+        if (static::sessionStarted() && HttpCache::$instance->sessionFile) {
+            return str_replace('ses_', '', basename(HttpCache::$instance->sessionFile));
+        }
+        return '';
+    }
+
+    /**
+     * sessionName
+     *
+     * @param string  $name
+     *
+     * @return string
+     */
+    public static function sessionName($name = null)
+    {
+        if (PHP_SAPI != 'cli') {
+            return $name ? session_name($name) : session_name();
+        }
+        $session_name = HttpCache::$sessionName;
+        if ($name && ! static::sessionStarted()) {
+            HttpCache::$sessionName = $name;
+        }
+        return $session_name;
+    }
+
+    /**
+     * sessionSavePath
+     *
+     * @param string  $path
+     *
+     * @return void
+     */
+    public static function sessionSavePath($path = null)
+    {
+        if (PHP_SAPI != 'cli') {
+            return $path ? session_save_path($path) : session_save_path();
+        }
+        if ($path && is_dir($path) && is_writable($path) && !static::sessionStarted()) {
+            HttpCache::$sessionPath = $path;
+        }
+        return HttpCache::$sessionPath;
+    }
+
+    /**
+     * sessionStarted
+     *
+     * @return bool
+     */
+    public static function sessionStarted()
+    {
+        if (!HttpCache::$instance) return false;
+
+        return HttpCache::$instance->sessionStarted;
+    }
+
+    /**
+     * sessionStart
+     *
+     * @return bool
+     */
+    public static function sessionStart()
+    {
+        if (PHP_SAPI != 'cli') {
+            return session_start();
+        }
+
+        self::tryGcSessions();
+
+        if (HttpCache::$instance->sessionStarted) {
+            Worker::safeEcho("already sessionStarted\n");
+            return true;
+        }
+        HttpCache::$instance->sessionStarted = true;
+        // Generate a SID.
+        if (!isset($_COOKIE[HttpCache::$sessionName]) || !is_file(HttpCache::$sessionPath . '/ses_' . $_COOKIE[HttpCache::$sessionName])) {
+            // Create a unique session_id and the associated file name.
+            while (true) {
+                $session_id = static::sessionCreateId();
+                if (!is_file($file_name = HttpCache::$sessionPath . '/ses_' . $session_id)) break;
+            }
+            HttpCache::$instance->sessionFile = $file_name;
+            return self::setcookie(
+                HttpCache::$sessionName
+                , $session_id
+                , ini_get('session.cookie_lifetime')
+                , ini_get('session.cookie_path')
+                , ini_get('session.cookie_domain')
+                , ini_get('session.cookie_secure')
+                , ini_get('session.cookie_httponly')
+            );
+        }
+        if (!HttpCache::$instance->sessionFile) {
+            HttpCache::$instance->sessionFile = HttpCache::$sessionPath . '/ses_' . $_COOKIE[HttpCache::$sessionName];
+        }
+        // Read session from session file.
+        if (HttpCache::$instance->sessionFile) {
+            $raw = file_get_contents(HttpCache::$instance->sessionFile);
+            if ($raw) {
+                $_SESSION = unserialize($raw);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Save session.
+     *
+     * @return bool
+     */
+    public static function sessionWriteClose()
+    {
+        if (PHP_SAPI != 'cli') {
+            return session_write_close();
+        }
+        if (!empty(HttpCache::$instance->sessionStarted) && !empty($_SESSION)) {
+            $session_str = serialize($_SESSION);
+            if ($session_str && HttpCache::$instance->sessionFile) {
+                return file_put_contents(HttpCache::$instance->sessionFile, $session_str);
+            }
+        }
+        return empty($_SESSION);
+    }
+
+    /**
+     * End, like call exit in php-fpm.
+     *
+     * @param string $msg
+     * @throws \Exception
+     */
+    public static function end($msg = '')
+    {
+        if (PHP_SAPI != 'cli') {
+            exit($msg);
+        }
+        if ($msg) {
+            echo $msg;
+        }
+        throw new \Exception('jump_exit');
+    }
+
+    /**
+     * Get mime types.
+     *
+     * @return string
+     */
+    public static function getMimeTypesFile()
+    {
+        return __DIR__ . '/Http/mime.types';
+    }
+
+    /**
+     * Parse $_FILES.
+     *
+     * @param string $http_body
+     * @param string $http_post_boundary
+     * @return void
+     */
+    protected static function parseUploadFiles($http_body, $http_post_boundary)
+    {
+        $http_body           = substr($http_body, 0, strlen($http_body) - (strlen($http_post_boundary) + 4));
+        $boundary_data_array = explode($http_post_boundary . "\r\n", $http_body);
+        if ($boundary_data_array[0] === '') {
+            unset($boundary_data_array[0]);
+        }
+        $key = -1;
+        foreach ($boundary_data_array as $boundary_data_buffer) {
+            list($boundary_header_buffer, $boundary_value) = explode("\r\n\r\n", $boundary_data_buffer, 2);
+            // Remove \r\n from the end of buffer.
+            $boundary_value = substr($boundary_value, 0, -2);
+            $key ++;
+            foreach (explode("\r\n", $boundary_header_buffer) as $item) {
+                list($header_key, $header_value) = explode(": ", $item);
+                $header_key = strtolower($header_key);
+                switch ($header_key) {
+                    case "content-disposition":
+                        // Is file data.
+                        if (preg_match('/name="(.*?)"; filename="(.*?)"$/', $header_value, $match)) {
+                            // Parse $_FILES.
+                            $_FILES[$key] = array(
+                                'name' => $match[1],
+                                'file_name' => $match[2],
+                                'file_data' => $boundary_value,
+                                'file_size' => strlen($boundary_value),
+                            );
+                            break;
+                        } // Is post field.
+                        else {
+                            // Parse $_POST.
+                            if (preg_match('/name="(.*?)"$/', $header_value, $match)) {
+                                $_POST[$match[1]] = $boundary_value;
+                            }
+                        }
+                        break;
+                    case "content-type":
+                        // add file_type
+                        $_FILES[$key]['file_type'] = trim($header_value);
+                        break;
+                }
+            }
+        }
+    }
+
+    /**
+     * Try GC sessions.
+     *
+     * @return void
+     */
+    public static function tryGcSessions()
+    {
+        if (HttpCache::$sessionGcProbability <= 0 ||
+            HttpCache::$sessionGcDivisor     <= 0 ||
+            rand(1, HttpCache::$sessionGcDivisor) > HttpCache::$sessionGcProbability) {
+            return;
+        }
+
+        $time_now = time();
+        foreach(glob(HttpCache::$sessionPath.'/ses*') as $file) {
+            if(is_file($file) && $time_now - filemtime($file) > HttpCache::$sessionGcMaxLifeTime) {
+                unlink($file);
+            }
+        }
+    }
+}
+
+/**
+ * Http cache for the current http response.
+ */
+class HttpCache
+{
+    public static $codes = array(
+        100 => 'Continue',
+        101 => 'Switching Protocols',
+        200 => 'OK',
+        201 => 'Created',
+        202 => 'Accepted',
+        203 => 'Non-Authoritative Information',
+        204 => 'No Content',
+        205 => 'Reset Content',
+        206 => 'Partial Content',
+        300 => 'Multiple Choices',
+        301 => 'Moved Permanently',
+        302 => 'Found',
+        303 => 'See Other',
+        304 => 'Not Modified',
+        305 => 'Use Proxy',
+        306 => '(Unused)',
+        307 => 'Temporary Redirect',
+        400 => 'Bad Request',
+        401 => 'Unauthorized',
+        402 => 'Payment Required',
+        403 => 'Forbidden',
+        404 => 'Not Found',
+        405 => 'Method Not Allowed',
+        406 => 'Not Acceptable',
+        407 => 'Proxy Authentication Required',
+        408 => 'Request Timeout',
+        409 => 'Conflict',
+        410 => 'Gone',
+        411 => 'Length Required',
+        412 => 'Precondition Failed',
+        413 => 'Request Entity Too Large',
+        414 => 'Request-URI Too Long',
+        415 => 'Unsupported Media Type',
+        416 => 'Requested Range Not Satisfiable',
+        417 => 'Expectation Failed',
+        422 => 'Unprocessable Entity',
+        423 => 'Locked',
+        500 => 'Internal Server Error',
+        501 => 'Not Implemented',
+        502 => 'Bad Gateway',
+        503 => 'Service Unavailable',
+        504 => 'Gateway Timeout',
+        505 => 'HTTP Version Not Supported',
+    );
+
+    /**
+     * @var HttpCache
+     */
+    public static $instance             = null;
+    public static $header               = array();
+    public static $gzip                 = false;
+    public static $sessionPath          = '';
+    public static $sessionName          = '';
+    public static $sessionGcProbability = 1;
+    public static $sessionGcDivisor     = 1000;
+    public static $sessionGcMaxLifeTime = 1440;
+    public $sessionStarted = false;
+    public $sessionFile = '';
+
+    public static function init()
+    {
+        if (!self::$sessionName) {
+            self::$sessionName = ini_get('session.name');
+        }
+
+        if (!self::$sessionPath) {
+            self::$sessionPath = @session_save_path();
+        }
+
+        if (!self::$sessionPath || strpos(self::$sessionPath, 'tcp://') === 0) {
+            self::$sessionPath = sys_get_temp_dir();
+        }
+
+        if ($gc_probability = ini_get('session.gc_probability')) {
+            self::$sessionGcProbability = $gc_probability;
+        }
+
+        if ($gc_divisor = ini_get('session.gc_divisor')) {
+            self::$sessionGcDivisor = $gc_divisor;
+        }
+
+        if ($gc_max_life_time = ini_get('session.gc_maxlifetime')) {
+            self::$sessionGcMaxLifeTime = $gc_max_life_time;
+        }
+    }
+}
+
+HttpCache::init();
diff --git a/vendor/workerman/workerman/Protocols/Http/mime.types b/vendor/workerman/workerman/Protocols/Http/mime.types
new file mode 100755
index 0000000..8a218b2
--- /dev/null
+++ b/vendor/workerman/workerman/Protocols/Http/mime.types
@@ -0,0 +1,80 @@
+
+types {
+    text/html                             html htm shtml;
+    text/css                              css;
+    text/xml                              xml;
+    image/gif                             gif;
+    image/jpeg                            jpeg jpg;
+    application/x-javascript              js;
+    application/atom+xml                  atom;
+    application/rss+xml                   rss;
+
+    text/mathml                           mml;
+    text/plain                            txt;
+    text/vnd.sun.j2me.app-descriptor      jad;
+    text/vnd.wap.wml                      wml;
+    text/x-component                      htc;
+
+    image/png                             png;
+    image/tiff                            tif tiff;
+    image/vnd.wap.wbmp                    wbmp;
+    image/x-icon                          ico;
+    image/x-jng                           jng;
+    image/x-ms-bmp                        bmp;
+    image/svg+xml                         svg svgz;
+    image/webp                            webp;
+
+    application/java-archive              jar war ear;
+    application/mac-binhex40              hqx;
+    application/msword                    doc;
+    application/pdf                       pdf;
+    application/postscript                ps eps ai;
+    application/rtf                       rtf;
+    application/vnd.ms-excel              xls;
+    application/vnd.ms-powerpoint         ppt;
+    application/vnd.wap.wmlc              wmlc;
+    application/vnd.google-earth.kml+xml  kml;
+    application/vnd.google-earth.kmz      kmz;
+    application/x-7z-compressed           7z;
+    application/x-cocoa                   cco;
+    application/x-java-archive-diff       jardiff;
+    application/x-java-jnlp-file          jnlp;
+    application/x-makeself                run;
+    application/x-perl                    pl pm;
+    application/x-pilot                   prc pdb;
+    application/x-rar-compressed          rar;
+    application/x-redhat-package-manager  rpm;
+    application/x-sea                     sea;
+    application/x-shockwave-flash         swf;
+    application/x-stuffit                 sit;
+    application/x-tcl                     tcl tk;
+    application/x-x509-ca-cert            der pem crt;
+    application/x-xpinstall               xpi;
+    application/xhtml+xml                 xhtml;
+    application/zip                       zip;
+
+    application/octet-stream              bin exe dll;
+    application/octet-stream              deb;
+    application/octet-stream              dmg;
+    application/octet-stream              eot;
+    application/octet-stream              iso img;
+    application/octet-stream              msi msp msm;
+
+    audio/midi                            mid midi kar;
+    audio/mpeg                            mp3;
+    audio/ogg                             ogg;
+    audio/x-m4a                           m4a;
+    audio/x-realaudio                     ra;
+
+    video/3gpp                            3gpp 3gp;
+    video/mp4                             mp4;
+    video/mpeg                            mpeg mpg;
+    video/quicktime                       mov;
+    video/webm                            webm;
+    video/x-flv                           flv;
+    video/x-m4v                           m4v;
+    video/x-mng                           mng;
+    video/x-ms-asf                        asx asf;
+    video/x-ms-wmv                        wmv;
+    video/x-msvideo                       avi;
+}
diff --git a/vendor/workerman/workerman/Protocols/ProtocolInterface.php b/vendor/workerman/workerman/Protocols/ProtocolInterface.php
new file mode 100755
index 0000000..9afe984
--- /dev/null
+++ b/vendor/workerman/workerman/Protocols/ProtocolInterface.php
@@ -0,0 +1,52 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Protocols;
+
+use Workerman\Connection\ConnectionInterface;
+
+/**
+ * Protocol interface
+ */
+interface ProtocolInterface
+{
+    /**
+     * Check the integrity of the package.
+     * Please return the length of package.
+     * If length is unknow please return 0 that mean wating more data.
+     * If the package has something wrong please return false the connection will be closed.
+     *
+     * @param ConnectionInterface $connection
+     * @param string              $recv_buffer
+     * @return int|false
+     */
+    public static function input($recv_buffer, ConnectionInterface $connection);
+
+    /**
+     * Decode package and emit onMessage($message) callback, $message is the result that decode returned.
+     *
+     * @param ConnectionInterface $connection
+     * @param string              $recv_buffer
+     * @return mixed
+     */
+    public static function decode($recv_buffer, ConnectionInterface $connection);
+
+    /**
+     * Encode package brefore sending to client.
+     *
+     * @param ConnectionInterface $connection
+     * @param mixed               $data
+     * @return string
+     */
+    public static function encode($data, ConnectionInterface $connection);
+}
diff --git a/vendor/workerman/workerman/Protocols/Text.php b/vendor/workerman/workerman/Protocols/Text.php
new file mode 100755
index 0000000..84b937a
--- /dev/null
+++ b/vendor/workerman/workerman/Protocols/Text.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Protocols;
+
+use Workerman\Connection\TcpConnection;
+
+/**
+ * Text Protocol.
+ */
+class Text
+{
+    /**
+     * Check the integrity of the package.
+     *
+     * @param string        $buffer
+     * @param TcpConnection $connection
+     * @return int
+     */
+    public static function input($buffer, TcpConnection $connection)
+    {
+        // Judge whether the package length exceeds the limit.
+        if (strlen($buffer) >= $connection->maxPackageSize) {
+            $connection->close();
+            return 0;
+        }
+        //  Find the position of  "\n".
+        $pos = strpos($buffer, "\n");
+        // No "\n", packet length is unknown, continue to wait for the data so return 0.
+        if ($pos === false) {
+            return 0;
+        }
+        // Return the current package length.
+        return $pos + 1;
+    }
+
+    /**
+     * Encode.
+     *
+     * @param string $buffer
+     * @return string
+     */
+    public static function encode($buffer)
+    {
+        // Add "\n"
+        return $buffer . "\n";
+    }
+
+    /**
+     * Decode.
+     *
+     * @param string $buffer
+     * @return string
+     */
+    public static function decode($buffer)
+    {
+        // Remove "\n"
+        return rtrim($buffer, "\r\n");
+    }
+}
diff --git a/vendor/workerman/workerman/Protocols/Websocket.php b/vendor/workerman/workerman/Protocols/Websocket.php
new file mode 100755
index 0000000..146063f
--- /dev/null
+++ b/vendor/workerman/workerman/Protocols/Websocket.php
@@ -0,0 +1,504 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Protocols;
+
+use Workerman\Connection\ConnectionInterface;
+use Workerman\Connection\TcpConnection;
+use Workerman\Worker;
+
+/**
+ * WebSocket protocol.
+ */
+class Websocket implements \Workerman\Protocols\ProtocolInterface
+{
+    /**
+     * Websocket blob type.
+     *
+     * @var string
+     */
+    const BINARY_TYPE_BLOB = "\x81";
+
+    /**
+     * Websocket arraybuffer type.
+     *
+     * @var string
+     */
+    const BINARY_TYPE_ARRAYBUFFER = "\x82";
+
+    /**
+     * Check the integrity of the package.
+     *
+     * @param string              $buffer
+     * @param ConnectionInterface $connection
+     * @return int
+     */
+    public static function input($buffer, ConnectionInterface $connection)
+    {
+        // Receive length.
+        $recv_len = strlen($buffer);
+        // We need more data.
+        if ($recv_len < 6) {
+            return 0;
+        }
+
+        // Has not yet completed the handshake.
+        if (empty($connection->websocketHandshake)) {
+            return static::dealHandshake($buffer, $connection);
+        }
+
+        // Buffer websocket frame data.
+        if ($connection->websocketCurrentFrameLength) {
+            // We need more frame data.
+            if ($connection->websocketCurrentFrameLength > $recv_len) {
+                // Return 0, because it is not clear the full packet length, waiting for the frame of fin=1.
+                return 0;
+            }
+        } else {
+            $firstbyte    = ord($buffer[0]);
+            $secondbyte   = ord($buffer[1]);
+            $data_len     = $secondbyte & 127;
+            $is_fin_frame = $firstbyte >> 7;
+            $masked       = $secondbyte >> 7;
+
+            if (!$masked) {
+                Worker::safeEcho("frame not masked so close the connection\n");
+                $connection->close();
+                return 0;
+            }
+
+            $opcode       = $firstbyte & 0xf;
+            switch ($opcode) {
+                case 0x0:
+                    break;
+                // Blob type.
+                case 0x1:
+                    break;
+                // Arraybuffer type.
+                case 0x2:
+                    break;
+                // Close package.
+                case 0x8:
+                    // Try to emit onWebSocketClose callback.
+                    if (isset($connection->onWebSocketClose) || isset($connection->worker->onWebSocketClose)) {
+                        try {
+                            call_user_func(isset($connection->onWebSocketClose)?$connection->onWebSocketClose:$connection->worker->onWebSocketClose, $connection);
+                        } catch (\Exception $e) {
+                            Worker::log($e);
+                            exit(250);
+                        } catch (\Error $e) {
+                            Worker::log($e);
+                            exit(250);
+                        }
+                    } // Close connection.
+                    else {
+                        $connection->close("\x88\x02\x27\x10", true);
+                    }
+                    return 0;
+                // Ping package.
+                case 0x9:
+                    break;
+                // Pong package.
+                case 0xa:
+                    break;
+                // Wrong opcode. 
+                default :
+                    Worker::safeEcho("error opcode $opcode and close websocket connection. Buffer:" . bin2hex($buffer) . "\n");
+                    $connection->close();
+                    return 0;
+            }
+
+            // Calculate packet length.
+            $head_len = 6;
+            if ($data_len === 126) {
+                $head_len = 8;
+                if ($head_len > $recv_len) {
+                    return 0;
+                }
+                $pack     = unpack('nn/ntotal_len', $buffer);
+                $data_len = $pack['total_len'];
+            } else {
+                if ($data_len === 127) {
+                    $head_len = 14;
+                    if ($head_len > $recv_len) {
+                        return 0;
+                    }
+                    $arr      = unpack('n/N2c', $buffer);
+                    $data_len = $arr['c1']*4294967296 + $arr['c2'];
+                }
+            }
+            $current_frame_length = $head_len + $data_len;
+
+            $total_package_size = strlen($connection->websocketDataBuffer) + $current_frame_length;
+            if ($total_package_size > $connection->maxPackageSize) {
+                Worker::safeEcho("error package. package_length=$total_package_size\n");
+                $connection->close();
+                return 0;
+            }
+
+            if ($is_fin_frame) {
+                if ($opcode === 0x9) {
+                    if ($recv_len >= $current_frame_length) {
+                        $ping_data = static::decode(substr($buffer, 0, $current_frame_length), $connection);
+                        $connection->consumeRecvBuffer($current_frame_length);
+                        $tmp_connection_type = isset($connection->websocketType) ? $connection->websocketType : static::BINARY_TYPE_BLOB;
+                        $connection->websocketType = "\x8a";
+                        if (isset($connection->onWebSocketPing) || isset($connection->worker->onWebSocketPing)) {
+                            try {
+                                call_user_func(isset($connection->onWebSocketPing)?$connection->onWebSocketPing:$connection->worker->onWebSocketPing, $connection, $ping_data);
+                            } catch (\Exception $e) {
+                                Worker::log($e);
+                                exit(250);
+                            } catch (\Error $e) {
+                                Worker::log($e);
+                                exit(250);
+                            }
+                        } else {
+                            $connection->send($ping_data);
+                        }
+                        $connection->websocketType = $tmp_connection_type;
+                        if ($recv_len > $current_frame_length) {
+                            return static::input(substr($buffer, $current_frame_length), $connection);
+                        }
+                    }
+                    return 0;
+                } else if ($opcode === 0xa) {
+                    if ($recv_len >= $current_frame_length) {
+                        $pong_data = static::decode(substr($buffer, 0, $current_frame_length), $connection);
+                        $connection->consumeRecvBuffer($current_frame_length);
+                        $tmp_connection_type = isset($connection->websocketType) ? $connection->websocketType : static::BINARY_TYPE_BLOB;
+                        $connection->websocketType = "\x8a";
+                        // Try to emit onWebSocketPong callback.
+                        if (isset($connection->onWebSocketPong) || isset($connection->worker->onWebSocketPong)) {
+                            try {
+                                call_user_func(isset($connection->onWebSocketPong)?$connection->onWebSocketPong:$connection->worker->onWebSocketPong, $connection, $pong_data);
+                            } catch (\Exception $e) {
+                                Worker::log($e);
+                                exit(250);
+                            } catch (\Error $e) {
+                                Worker::log($e);
+                                exit(250);
+                            }
+                        }
+                        $connection->websocketType = $tmp_connection_type;
+                        if ($recv_len > $current_frame_length) {
+                            return static::input(substr($buffer, $current_frame_length), $connection);
+                        }
+                    }
+                    return 0;
+                }
+                return $current_frame_length;
+            } else {
+                $connection->websocketCurrentFrameLength = $current_frame_length;
+            }
+        }
+
+        // Received just a frame length data.
+        if ($connection->websocketCurrentFrameLength === $recv_len) {
+            static::decode($buffer, $connection);
+            $connection->consumeRecvBuffer($connection->websocketCurrentFrameLength);
+            $connection->websocketCurrentFrameLength = 0;
+            return 0;
+        } // The length of the received data is greater than the length of a frame.
+        elseif ($connection->websocketCurrentFrameLength < $recv_len) {
+            static::decode(substr($buffer, 0, $connection->websocketCurrentFrameLength), $connection);
+            $connection->consumeRecvBuffer($connection->websocketCurrentFrameLength);
+            $current_frame_length                    = $connection->websocketCurrentFrameLength;
+            $connection->websocketCurrentFrameLength = 0;
+            // Continue to read next frame.
+            return static::input(substr($buffer, $current_frame_length), $connection);
+        } // The length of the received data is less than the length of a frame.
+        else {
+            return 0;
+        }
+    }
+
+    /**
+     * Websocket encode.
+     *
+     * @param string              $buffer
+     * @param ConnectionInterface $connection
+     * @return string
+     */
+    public static function encode($buffer, ConnectionInterface $connection)
+    {
+        if (!is_scalar($buffer)) {
+            throw new \Exception("You can't send(" . gettype($buffer) . ") to client, you need to convert it to a string. ");
+        }
+        $len = strlen($buffer);
+        if (empty($connection->websocketType)) {
+            $connection->websocketType = static::BINARY_TYPE_BLOB;
+        }
+
+        $first_byte = $connection->websocketType;
+
+        if ($len <= 125) {
+            $encode_buffer = $first_byte . chr($len) . $buffer;
+        } else {
+            if ($len <= 65535) {
+                $encode_buffer = $first_byte . chr(126) . pack("n", $len) . $buffer;
+            } else {
+                $encode_buffer = $first_byte . chr(127) . pack("xxxxN", $len) . $buffer;
+            }
+        }
+
+        // Handshake not completed so temporary buffer websocket data waiting for send.
+        if (empty($connection->websocketHandshake)) {
+            if (empty($connection->tmpWebsocketData)) {
+                $connection->tmpWebsocketData = '';
+            }
+            // If buffer has already full then discard the current package.
+            if (strlen($connection->tmpWebsocketData) > $connection->maxSendBufferSize) {
+                if ($connection->onError) {
+                    try {
+                        call_user_func($connection->onError, $connection, WORKERMAN_SEND_FAIL, 'send buffer full and drop package');
+                    } catch (\Exception $e) {
+                        Worker::log($e);
+                        exit(250);
+                    } catch (\Error $e) {
+                        Worker::log($e);
+                        exit(250);
+                    }
+                }
+                return '';
+            }
+            $connection->tmpWebsocketData .= $encode_buffer;
+            // Check buffer is full.
+            if ($connection->maxSendBufferSize <= strlen($connection->tmpWebsocketData)) {
+                if ($connection->onBufferFull) {
+                    try {
+                        call_user_func($connection->onBufferFull, $connection);
+                    } catch (\Exception $e) {
+                        Worker::log($e);
+                        exit(250);
+                    } catch (\Error $e) {
+                        Worker::log($e);
+                        exit(250);
+                    }
+                }
+            }
+
+            // Return empty string.
+            return '';
+        }
+
+        return $encode_buffer;
+    }
+
+    /**
+     * Websocket decode.
+     *
+     * @param string              $buffer
+     * @param ConnectionInterface $connection
+     * @return string
+     */
+    public static function decode($buffer, ConnectionInterface $connection)
+    {
+        $masks = $data = $decoded = '';
+        $len = ord($buffer[1]) & 127;
+        if ($len === 126) {
+            $masks = substr($buffer, 4, 4);
+            $data  = substr($buffer, 8);
+        } else {
+            if ($len === 127) {
+                $masks = substr($buffer, 10, 4);
+                $data  = substr($buffer, 14);
+            } else {
+                $masks = substr($buffer, 2, 4);
+                $data  = substr($buffer, 6);
+            }
+        }
+        for ($index = 0; $index < strlen($data); $index++) {
+            $decoded .= $data[$index] ^ $masks[$index % 4];
+        }
+        if ($connection->websocketCurrentFrameLength) {
+            $connection->websocketDataBuffer .= $decoded;
+            return $connection->websocketDataBuffer;
+        } else {
+            if ($connection->websocketDataBuffer !== '') {
+                $decoded                         = $connection->websocketDataBuffer . $decoded;
+                $connection->websocketDataBuffer = '';
+            }
+            return $decoded;
+        }
+    }
+
+    /**
+     * Websocket handshake.
+     *
+     * @param string                              $buffer
+     * @param \Workerman\Connection\TcpConnection $connection
+     * @return int
+     */
+    protected static function dealHandshake($buffer, $connection)
+    {
+        // HTTP protocol.
+        if (0 === strpos($buffer, 'GET')) {
+            // Find \r\n\r\n.
+            $heder_end_pos = strpos($buffer, "\r\n\r\n");
+            if (!$heder_end_pos) {
+                return 0;
+            }
+            $header_length = $heder_end_pos + 4;
+
+            // Get Sec-WebSocket-Key.
+            $Sec_WebSocket_Key = '';
+            if (preg_match("/Sec-WebSocket-Key: *(.*?)\r\n/i", $buffer, $match)) {
+                $Sec_WebSocket_Key = $match[1];
+            } else {
+                $connection->send("HTTP/1.1 400 Bad Request\r\n\r\n<b>400 Bad Request</b><br>Sec-WebSocket-Key not found.<br>This is a WebSocket service and can not be accessed via HTTP.<br>See <a href=\"http://wiki.workerman.net/Error1\">http://wiki.workerman.net/Error1</a> for detail.",
+                    true);
+                $connection->close();
+                return 0;
+            }
+            // Calculation websocket key.
+            $new_key = base64_encode(sha1($Sec_WebSocket_Key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true));
+            // Handshake response data.
+            $handshake_message = "HTTP/1.1 101 Switching Protocols\r\n";
+            $handshake_message .= "Upgrade: websocket\r\n";
+            $handshake_message .= "Sec-WebSocket-Version: 13\r\n";
+            $handshake_message .= "Connection: Upgrade\r\n";
+            $handshake_message .= "Sec-WebSocket-Accept: " . $new_key . "\r\n";
+
+            // Websocket data buffer.
+            $connection->websocketDataBuffer = '';
+            // Current websocket frame length.
+            $connection->websocketCurrentFrameLength = 0;
+            // Current websocket frame data.
+            $connection->websocketCurrentFrameBuffer = '';
+            // Consume handshake data.
+            $connection->consumeRecvBuffer($header_length);
+
+            // blob or arraybuffer
+            if (empty($connection->websocketType)) {
+                $connection->websocketType = static::BINARY_TYPE_BLOB;
+            }
+
+            $has_server_header = false;
+
+            // Try to emit onWebSocketConnect callback.
+            if (isset($connection->onWebSocketConnect) || isset($connection->worker->onWebSocketConnect)) {
+                static::parseHttpHeader($buffer);
+                try {
+                    call_user_func(isset($connection->onWebSocketConnect)?$connection->onWebSocketConnect:$connection->worker->onWebSocketConnect, $connection, $buffer);
+                } catch (\Exception $e) {
+                    Worker::log($e);
+                    exit(250);
+                } catch (\Error $e) {
+                    Worker::log($e);
+                    exit(250);
+                }
+                if (!empty($_SESSION) && class_exists('\GatewayWorker\Lib\Context')) {
+                    $connection->session = \GatewayWorker\Lib\Context::sessionEncode($_SESSION);
+                }
+                $_GET = $_SERVER = $_SESSION = $_COOKIE = array();
+
+                if (isset($connection->headers)) {
+                    if (is_array($connection->headers))  {
+                        foreach ($connection->headers as $header) {
+                            if (strpos($header, 'Server:') === 0) {
+                                $has_server_header = true;
+                            }
+                            $handshake_message .= "$header\r\n";
+                        }
+                    } else {
+                        $handshake_message .= "$connection->headers\r\n";
+                    }
+                }
+            }
+            if (!$has_server_header) {
+                $handshake_message .= "Server: workerman/".Worker::VERSION."\r\n";
+            }
+            $handshake_message .= "\r\n";
+            // Send handshake response.
+            $connection->send($handshake_message, true);
+            // Mark handshake complete..
+            $connection->websocketHandshake = true;
+            // There are data waiting to be sent.
+            if (!empty($connection->tmpWebsocketData)) {
+                $connection->send($connection->tmpWebsocketData, true);
+                $connection->tmpWebsocketData = '';
+            }
+            if (strlen($buffer) > $header_length) {
+                return static::input(substr($buffer, $header_length), $connection);
+            }
+            return 0;
+        } // Is flash policy-file-request.
+        elseif (0 === strpos($buffer, '<polic')) {
+            $policy_xml = '<?xml version="1.0"?><cross-domain-policy><site-control permitted-cross-domain-policies="all"/><allow-access-from domain="*" to-ports="*"/></cross-domain-policy>' . "\0";
+            $connection->send($policy_xml, true);
+            $connection->consumeRecvBuffer(strlen($buffer));
+            return 0;
+        }
+        // Bad websocket handshake request.
+        $connection->send("HTTP/1.1 400 Bad Request\r\n\r\n<b>400 Bad Request</b><br>Invalid handshake data for websocket. <br> See <a href=\"http://wiki.workerman.net/Error1\">http://wiki.workerman.net/Error1</a> for detail.",
+            true);
+        $connection->close();
+        return 0;
+    }
+
+    /**
+     * Parse http header.
+     *
+     * @param string $buffer
+     * @return void
+     */
+    protected static function parseHttpHeader($buffer)
+    {
+        // Parse headers.
+        list($http_header, ) = explode("\r\n\r\n", $buffer, 2);
+        $header_data = explode("\r\n", $http_header);
+
+        if ($_SERVER) {
+            $_SERVER = array();
+        }
+
+        list($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_SERVER['SERVER_PROTOCOL']) = explode(' ',
+            $header_data[0]);
+
+        unset($header_data[0]);
+        foreach ($header_data as $content) {
+            // \r\n\r\n
+            if (empty($content)) {
+                continue;
+            }
+            list($key, $value)       = explode(':', $content, 2);
+            $key                     = str_replace('-', '_', strtoupper($key));
+            $value                   = trim($value);
+            $_SERVER['HTTP_' . $key] = $value;
+            switch ($key) {
+                // HTTP_HOST
+                case 'HOST':
+                    $tmp                    = explode(':', $value);
+                    $_SERVER['SERVER_NAME'] = $tmp[0];
+                    if (isset($tmp[1])) {
+                        $_SERVER['SERVER_PORT'] = $tmp[1];
+                    }
+                    break;
+                // cookie
+                case 'COOKIE':
+                    parse_str(str_replace('; ', '&', $_SERVER['HTTP_COOKIE']), $_COOKIE);
+                    break;
+            }
+        }
+
+        // QUERY_STRING
+        $_SERVER['QUERY_STRING'] = parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY);
+        if ($_SERVER['QUERY_STRING']) {
+            // $GET
+            parse_str($_SERVER['QUERY_STRING'], $_GET);
+        } else {
+            $_SERVER['QUERY_STRING'] = '';
+        }
+    }
+}
diff --git a/vendor/workerman/workerman/Protocols/Ws.php b/vendor/workerman/workerman/Protocols/Ws.php
new file mode 100755
index 0000000..917ca02
--- /dev/null
+++ b/vendor/workerman/workerman/Protocols/Ws.php
@@ -0,0 +1,472 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Protocols;
+
+use Workerman\Worker;
+use Workerman\Lib\Timer;
+use Workerman\Connection\TcpConnection;
+
+/**
+ * Websocket protocol for client.
+ */
+class Ws
+{
+    /**
+     * Websocket blob type.
+     *
+     * @var string
+     */
+    const BINARY_TYPE_BLOB = "\x81";
+
+    /**
+     * Websocket arraybuffer type.
+     *
+     * @var string
+     */
+    const BINARY_TYPE_ARRAYBUFFER = "\x82";
+
+    /**
+     * Check the integrity of the package.
+     *
+     * @param string              $buffer
+     * @param ConnectionInterface $connection
+     * @return int
+     */
+    public static function input($buffer, $connection)
+    {
+        if (empty($connection->handshakeStep)) {
+            Worker::safeEcho("recv data before handshake. Buffer:" . bin2hex($buffer) . "\n");
+            return false;
+        }
+        // Recv handshake response
+        if ($connection->handshakeStep === 1) {
+            return self::dealHandshake($buffer, $connection);
+        }
+        $recv_len = strlen($buffer);
+        if ($recv_len < 2) {
+            return 0;
+        }
+        // Buffer websocket frame data.
+        if ($connection->websocketCurrentFrameLength) {
+            // We need more frame data.
+            if ($connection->websocketCurrentFrameLength > $recv_len) {
+                // Return 0, because it is not clear the full packet length, waiting for the frame of fin=1.
+                return 0;
+            }
+        } else {
+
+            $firstbyte    = ord($buffer[0]);
+            $secondbyte   = ord($buffer[1]);
+            $data_len     = $secondbyte & 127;
+            $is_fin_frame = $firstbyte >> 7;
+            $masked       = $secondbyte >> 7;
+
+            if ($masked) {
+                Worker::safeEcho("frame masked so close the connection\n");
+                $connection->close();
+                return 0;
+            }
+
+            $opcode       = $firstbyte & 0xf;
+
+            switch ($opcode) {
+                case 0x0:
+                    break;
+                // Blob type.
+                case 0x1:
+                    break;
+                // Arraybuffer type.
+                case 0x2:
+                    break;
+                // Close package.
+                case 0x8:
+                    // Try to emit onWebSocketClose callback.
+                    if (isset($connection->onWebSocketClose)) {
+                        try {
+                            call_user_func($connection->onWebSocketClose, $connection);
+                        } catch (\Exception $e) {
+                            Worker::log($e);
+                            exit(250);
+                        } catch (\Error $e) {
+                            Worker::log($e);
+                            exit(250);
+                        }
+                    } // Close connection.
+                    else {
+                        $connection->close();
+                    }
+                    return 0;
+                // Ping package.
+                case 0x9:
+                    break;
+                // Pong package.
+                case 0xa:
+                    break;
+                // Wrong opcode.
+                default :
+                    Worker::safeEcho("error opcode $opcode and close websocket connection. Buffer:" . $buffer . "\n");
+                    $connection->close();
+                    return 0;
+            }
+            // Calculate packet length.
+            if ($data_len === 126) {
+                if (strlen($buffer) < 4) {
+                    return 0;
+                }
+                $pack = unpack('nn/ntotal_len', $buffer);
+                $current_frame_length = $pack['total_len'] + 4;
+            } else if ($data_len === 127) {
+                if (strlen($buffer) < 10) {
+                    return 0;
+                }
+                $arr = unpack('n/N2c', $buffer);
+                $current_frame_length = $arr['c1']*4294967296 + $arr['c2'] + 10;
+            } else {
+                $current_frame_length = $data_len + 2;
+            }
+
+            $total_package_size = strlen($connection->websocketDataBuffer) + $current_frame_length;
+            if ($total_package_size > $connection->maxPackageSize) {
+                Worker::safeEcho("error package. package_length=$total_package_size\n");
+                $connection->close();
+                return 0;
+            }
+
+            if ($is_fin_frame) {
+                if ($opcode === 0x9) {
+                    if ($recv_len >= $current_frame_length) {
+                        $ping_data = static::decode(substr($buffer, 0, $current_frame_length), $connection);
+                        $connection->consumeRecvBuffer($current_frame_length);
+                        $tmp_connection_type = isset($connection->websocketType) ? $connection->websocketType : static::BINARY_TYPE_BLOB;
+                        $connection->websocketType = "\x8a";
+                        if (isset($connection->onWebSocketPing)) {
+                            try {
+                                call_user_func($connection->onWebSocketPing, $connection, $ping_data);
+                            } catch (\Exception $e) {
+                                Worker::log($e);
+                                exit(250);
+                            } catch (\Error $e) {
+                                Worker::log($e);
+                                exit(250);
+                            }
+                        } else {
+                            $connection->send($ping_data);
+                        }
+                        $connection->websocketType = $tmp_connection_type;
+                        if ($recv_len > $current_frame_length) {
+                            return static::input(substr($buffer, $current_frame_length), $connection);
+                        }
+                    }
+                    return 0;
+
+                } else if ($opcode === 0xa) {
+                    if ($recv_len >= $current_frame_length) {
+                        $pong_data = static::decode(substr($buffer, 0, $current_frame_length), $connection);
+                        $connection->consumeRecvBuffer($current_frame_length);
+                        $tmp_connection_type = isset($connection->websocketType) ? $connection->websocketType : static::BINARY_TYPE_BLOB;
+                        $connection->websocketType = "\x8a";
+                        // Try to emit onWebSocketPong callback.
+                        if (isset($connection->onWebSocketPong)) {
+                            try {
+                                call_user_func($connection->onWebSocketPong, $connection, $pong_data);
+                            } catch (\Exception $e) {
+                                Worker::log($e);
+                                exit(250);
+                            } catch (\Error $e) {
+                                Worker::log($e);
+                                exit(250);
+                            }
+                        }
+                        $connection->websocketType = $tmp_connection_type;
+                        if ($recv_len > $current_frame_length) {
+                            return static::input(substr($buffer, $current_frame_length), $connection);
+                        }
+                    }
+                    return 0;
+                }
+                return $current_frame_length;
+            } else {
+                $connection->websocketCurrentFrameLength = $current_frame_length;
+            }
+        }
+        // Received just a frame length data.
+        if ($connection->websocketCurrentFrameLength === $recv_len) {
+            self::decode($buffer, $connection);
+            $connection->consumeRecvBuffer($connection->websocketCurrentFrameLength);
+            $connection->websocketCurrentFrameLength = 0;
+            return 0;
+        } // The length of the received data is greater than the length of a frame.
+        elseif ($connection->websocketCurrentFrameLength < $recv_len) {
+            self::decode(substr($buffer, 0, $connection->websocketCurrentFrameLength), $connection);
+            $connection->consumeRecvBuffer($connection->websocketCurrentFrameLength);
+            $current_frame_length                    = $connection->websocketCurrentFrameLength;
+            $connection->websocketCurrentFrameLength = 0;
+            // Continue to read next frame.
+            return self::input(substr($buffer, $current_frame_length), $connection);
+        } // The length of the received data is less than the length of a frame.
+        else {
+            return 0;
+        }
+    }
+
+    /**
+     * Websocket encode.
+     *
+     * @param string              $buffer
+     * @param ConnectionInterface $connection
+     * @return string
+     */
+    public static function encode($payload, $connection)
+    {
+        if (empty($connection->websocketType)) {
+            $connection->websocketType = self::BINARY_TYPE_BLOB;
+        }
+        $payload = (string)$payload;
+        if (empty($connection->handshakeStep)) {
+            self::sendHandshake($connection);
+        }
+        $mask = 1;
+        $mask_key = "\x00\x00\x00\x00";
+
+        $pack = '';
+        $length = $length_flag = strlen($payload);
+        if (65535 < $length) {
+            $pack   = pack('NN', ($length & 0xFFFFFFFF00000000) >> 32, $length & 0x00000000FFFFFFFF);
+            $length_flag = 127;
+        } else if (125 < $length) {
+            $pack   = pack('n*', $length);
+            $length_flag = 126;
+        }
+
+        $head = ($mask << 7) | $length_flag;
+        $head = $connection->websocketType . chr($head) . $pack;
+
+        $frame = $head . $mask_key;
+        // append payload to frame:
+        for ($i = 0; $i < $length; $i++) {
+            $frame .= $payload[$i] ^ $mask_key[$i % 4];
+        }
+        if ($connection->handshakeStep === 1) {
+            // If buffer has already full then discard the current package.
+            if (strlen($connection->tmpWebsocketData) > $connection->maxSendBufferSize) {
+                if ($connection->onError) {
+                    try {
+                        call_user_func($connection->onError, $connection, WORKERMAN_SEND_FAIL, 'send buffer full and drop package');
+                    } catch (\Exception $e) {
+                        Worker::log($e);
+                        exit(250);
+                    } catch (\Error $e) {
+                        Worker::log($e);
+                        exit(250);
+                    }
+                }
+                return '';
+            }
+            $connection->tmpWebsocketData = $connection->tmpWebsocketData . $frame;
+            // Check buffer is full.
+            if ($connection->maxSendBufferSize <= strlen($connection->tmpWebsocketData)) {
+                if ($connection->onBufferFull) {
+                    try {
+                        call_user_func($connection->onBufferFull, $connection);
+                    } catch (\Exception $e) {
+                        Worker::log($e);
+                        exit(250);
+                    } catch (\Error $e) {
+                        Worker::log($e);
+                        exit(250);
+                    }
+                }
+            }
+            return '';
+        }
+        return $frame;
+    }
+
+    /**
+     * Websocket decode.
+     *
+     * @param string              $buffer
+     * @param ConnectionInterface $connection
+     * @return string
+     */
+    public static function decode($bytes, $connection)
+    {
+        $data_length = ord($bytes[1]);
+
+        if ($data_length === 126) {
+            $decoded_data = substr($bytes, 4);
+        } else if ($data_length === 127) {
+            $decoded_data = substr($bytes, 10);
+        } else {
+            $decoded_data = substr($bytes, 2);
+        }
+        if ($connection->websocketCurrentFrameLength) {
+            $connection->websocketDataBuffer .= $decoded_data;
+            return $connection->websocketDataBuffer;
+        } else {
+            if ($connection->websocketDataBuffer !== '') {
+                $decoded_data                    = $connection->websocketDataBuffer . $decoded_data;
+                $connection->websocketDataBuffer = '';
+            }
+            return $decoded_data;
+        }
+    }
+
+    /**
+     * Send websocket handshake data.
+     *
+     * @return void
+     */
+    public static function onConnect($connection)
+    {
+        self::sendHandshake($connection);
+    }
+
+    /**
+     * Clean
+     *
+     * @param $connection
+     */
+    public static function onClose($connection)
+    {
+        $connection->handshakeStep               = null;
+        $connection->websocketCurrentFrameLength = 0;
+        $connection->tmpWebsocketData            = '';
+        $connection->websocketDataBuffer         = '';
+        if (!empty($connection->websocketPingTimer)) {
+            Timer::del($connection->websocketPingTimer);
+            $connection->websocketPingTimer = null;
+        }
+    }
+
+    /**
+     * Send websocket handshake.
+     *
+     * @param \Workerman\Connection\TcpConnection $connection
+     * @return void
+     */
+    public static function sendHandshake($connection)
+    {
+        if (!empty($connection->handshakeStep)) {
+            return;
+        }
+        // Get Host.
+        $port = $connection->getRemotePort();
+        $host = $port === 80 ? $connection->getRemoteHost() : $connection->getRemoteHost() . ':' . $port;
+        // Handshake header.
+        $connection->websocketSecKey = base64_encode(md5(mt_rand(), true));
+        $user_header = isset($connection->headers) ? $connection->headers :
+            (isset($connection->wsHttpHeader) ? $connection->wsHttpHeader : null);
+        $user_header_str = '';
+        if (!empty($user_header)) {
+            if (is_array($user_header)){
+                foreach($user_header as $k=>$v){
+                    $user_header_str .= "$k: $v\r\n";
+                }
+            } else {
+                $user_header_str .= $user_header;
+            }
+            $user_header_str = "\r\n".trim($user_header_str);
+        }
+        $header = 'GET ' . $connection->getRemoteURI() . " HTTP/1.1\r\n".
+        (!preg_match("/\nHost:/i", $user_header_str) ? "Host: $host\r\n" : '').
+        "Connection: Upgrade\r\n".
+        "Upgrade: websocket\r\n".
+        "Origin: ". (isset($connection->websocketOrigin) ? $connection->websocketOrigin : '*') ."\r\n".
+        (isset($connection->WSClientProtocol)?"Sec-WebSocket-Protocol: ".$connection->WSClientProtocol."\r\n":'').
+        "Sec-WebSocket-Version: 13\r\n".
+        "Sec-WebSocket-Key: " . $connection->websocketSecKey . $user_header_str . "\r\n\r\n";
+        $connection->send($header, true);
+        $connection->handshakeStep               = 1;
+        $connection->websocketCurrentFrameLength = 0;
+        $connection->websocketDataBuffer         = '';
+        $connection->tmpWebsocketData            = '';
+    }
+
+    /**
+     * Websocket handshake.
+     *
+     * @param string                              $buffer
+     * @param \Workerman\Connection\TcpConnection $connection
+     * @return int
+     */
+    public static function dealHandshake($buffer, $connection)
+    {
+        $pos = strpos($buffer, "\r\n\r\n");
+        if ($pos) {
+            //checking Sec-WebSocket-Accept
+            if (preg_match("/Sec-WebSocket-Accept: *(.*?)\r\n/i", $buffer, $match)) {
+                if ($match[1] !== base64_encode(sha1($connection->websocketSecKey . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true))) {
+                    Worker::safeEcho("Sec-WebSocket-Accept not match. Header:\n" . substr($buffer, 0, $pos) . "\n");
+                    $connection->close();
+                    return 0;
+                }
+            } else {
+                Worker::safeEcho("Sec-WebSocket-Accept not found. Header:\n" . substr($buffer, 0, $pos) . "\n");
+                $connection->close();
+                return 0;
+            }
+
+            // handshake complete
+
+            // Get WebSocket subprotocol (if specified by server)
+            if (preg_match("/Sec-WebSocket-Protocol: *(.*?)\r\n/i", $buffer, $match)) {
+                $connection->WSServerProtocol = trim($match[1]);
+            }
+
+            $connection->handshakeStep = 2;
+            $handshake_response_length = $pos + 4;
+            // Try to emit onWebSocketConnect callback.
+            if (isset($connection->onWebSocketConnect)) {
+                try {
+                    call_user_func($connection->onWebSocketConnect, $connection, substr($buffer, 0, $handshake_response_length));
+                } catch (\Exception $e) {
+                    Worker::log($e);
+                    exit(250);
+                } catch (\Error $e) {
+                    Worker::log($e);
+                    exit(250);
+                }
+            }
+            // Headbeat.
+            if (!empty($connection->websocketPingInterval)) {
+                $connection->websocketPingTimer = Timer::add($connection->websocketPingInterval, function() use ($connection){
+                    if (false === $connection->send(pack('H*', '898000000000'), true)) {
+                        Timer::del($connection->websocketPingTimer);
+                        $connection->websocketPingTimer = null;
+                    }
+                });
+            }
+
+            $connection->consumeRecvBuffer($handshake_response_length);
+            if (!empty($connection->tmpWebsocketData)) {
+                $connection->send($connection->tmpWebsocketData, true);
+                $connection->tmpWebsocketData = '';
+            }
+            if (strlen($buffer) > $handshake_response_length) {
+                return self::input(substr($buffer, $handshake_response_length), $connection);
+            }
+        }
+        return 0;
+    }
+
+    public static function WSSetProtocol($connection, $params) {
+	$connection->WSClientProtocol = $params[0];
+    }
+
+    public static function WSGetServerProtocol($connection) {
+	return (property_exists($connection, 'WSServerProtocol')?$connection->WSServerProtocol:null);
+    }
+
+}
diff --git a/vendor/workerman/workerman/README.md b/vendor/workerman/workerman/README.md
new file mode 100755
index 0000000..c0d4ddb
--- /dev/null
+++ b/vendor/workerman/workerman/README.md
@@ -0,0 +1,603 @@
+# Workerman
+[![Gitter](https://badges.gitter.im/walkor/Workerman.svg)](https://gitter.im/walkor/Workerman?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge)
+[![Latest Stable Version](https://poser.pugx.org/workerman/workerman/v/stable)](https://packagist.org/packages/workerman/workerman)
+[![Total Downloads](https://poser.pugx.org/workerman/workerman/downloads)](https://packagist.org/packages/workerman/workerman)
+[![Monthly Downloads](https://poser.pugx.org/workerman/workerman/d/monthly)](https://packagist.org/packages/workerman/workerman)
+[![Daily Downloads](https://poser.pugx.org/workerman/workerman/d/daily)](https://packagist.org/packages/workerman/workerman)
+[![License](https://poser.pugx.org/workerman/workerman/license)](https://packagist.org/packages/workerman/workerman)
+
+## What is it
+Workerman is an asynchronous event driven PHP framework with high performance for easily building fast, scalable network applications. Supports HTTP, Websocket, SSL and other custom protocols. Supports libevent, [HHVM](https://github.com/facebook/hhvm) , [ReactPHP](https://github.com/reactphp/react).
+
+## Requires
+PHP 5.3 or Higher  
+A POSIX compatible operating system (Linux, OSX, BSD)  
+POSIX and PCNTL extensions for PHP  
+
+## Installation
+
+```
+composer require workerman/workerman
+```
+
+## Basic Usage
+
+### A websocket server 
+```php
+<?php
+require_once __DIR__ . '/vendor/autoload.php';
+use Workerman\Worker;
+
+// Create a Websocket server
+$ws_worker = new Worker("websocket://0.0.0.0:2346");
+
+// 4 processes
+$ws_worker->count = 4;
+
+// Emitted when new connection come
+$ws_worker->onConnect = function($connection)
+{
+    echo "New connection\n";
+ };
+
+// Emitted when data received
+$ws_worker->onMessage = function($connection, $data)
+{
+    // Send hello $data
+    $connection->send('hello ' . $data);
+};
+
+// Emitted when connection closed
+$ws_worker->onClose = function($connection)
+{
+    echo "Connection closed\n";
+};
+
+// Run worker
+Worker::runAll();
+```
+
+### An http server
+```php
+require_once __DIR__ . '/vendor/autoload.php';
+use Workerman\Worker;
+
+// #### http worker ####
+$http_worker = new Worker("http://0.0.0.0:2345");
+
+// 4 processes
+$http_worker->count = 4;
+
+// Emitted when data received
+$http_worker->onMessage = function($connection, $data)
+{
+    // $_GET, $_POST, $_COOKIE, $_SESSION, $_SERVER, $_FILES are available
+    var_dump($_GET, $_POST, $_COOKIE, $_SESSION, $_SERVER, $_FILES);
+    // send data to client
+    $connection->send("hello world \n");
+};
+
+// run all workers
+Worker::runAll();
+```
+
+### A WebServer
+```php
+require_once __DIR__ . '/vendor/autoload.php';
+use Workerman\WebServer;
+use Workerman\Worker;
+
+// WebServer
+$web = new WebServer("http://0.0.0.0:80");
+
+// 4 processes
+$web->count = 4;
+
+// Set the root of domains
+$web->addRoot('www.your_domain.com', '/your/path/Web');
+$web->addRoot('www.another_domain.com', '/another/path/Web');
+// run all workers
+Worker::runAll();
+```
+
+### A tcp server
+```php
+require_once __DIR__ . '/vendor/autoload.php';
+use Workerman\Worker;
+
+// #### create socket and listen 1234 port ####
+$tcp_worker = new Worker("tcp://0.0.0.0:1234");
+
+// 4 processes
+$tcp_worker->count = 4;
+
+// Emitted when new connection come
+$tcp_worker->onConnect = function($connection)
+{
+    echo "New Connection\n";
+};
+
+// Emitted when data received
+$tcp_worker->onMessage = function($connection, $data)
+{
+    // send data to client
+    $connection->send("hello $data \n");
+};
+
+// Emitted when new connection come
+$tcp_worker->onClose = function($connection)
+{
+    echo "Connection closed\n";
+};
+
+Worker::runAll();
+```
+
+### Enable SSL
+```php
+<?php
+require_once __DIR__ . '/vendor/autoload.php';
+use Workerman\Worker;
+
+// SSL context.
+$context = array(
+    'ssl' => array(
+        'local_cert'  => '/your/path/of/server.pem',
+        'local_pk'    => '/your/path/of/server.key',
+        'verify_peer' => false,
+    )
+);
+
+// Create a Websocket server with ssl context.
+$ws_worker = new Worker("websocket://0.0.0.0:2346", $context);
+
+// Enable SSL. WebSocket+SSL means that Secure WebSocket (wss://). 
+// The similar approaches for Https etc.
+$ws_worker->transport = 'ssl';
+
+$ws_worker->onMessage = function($connection, $data)
+{
+    // Send hello $data
+    $connection->send('hello ' . $data);
+};
+
+Worker::runAll();
+```
+
+### Custom protocol
+Protocols/MyTextProtocol.php
+```php
+namespace Protocols;
+/**
+ * User defined protocol
+ * Format Text+"\n"
+ */
+class MyTextProtocol
+{
+    public static function input($recv_buffer)
+    {
+        // Find the position of the first occurrence of "\n"
+        $pos = strpos($recv_buffer, "\n");
+        // Not a complete package. Return 0 because the length of package can not be calculated
+        if($pos === false)
+        {
+            return 0;
+        }
+        // Return length of the package
+        return $pos+1;
+    }
+
+    public static function decode($recv_buffer)
+    {
+        return trim($recv_buffer);
+    }
+
+    public static function encode($data)
+    {
+        return $data."\n";
+    }
+}
+```
+
+```php
+require_once __DIR__ . '/vendor/autoload.php';
+use Workerman\Worker;
+
+// #### MyTextProtocol worker ####
+$text_worker = new Worker("MyTextProtocol://0.0.0.0:5678");
+
+$text_worker->onConnect = function($connection)
+{
+    echo "New connection\n";
+};
+
+$text_worker->onMessage =  function($connection, $data)
+{
+    // send data to client
+    $connection->send("hello world \n");
+};
+
+$text_worker->onClose = function($connection)
+{
+    echo "Connection closed\n";
+};
+
+// run all workers
+Worker::runAll();
+```
+
+### Timer
+```php
+require_once __DIR__ . '/vendor/autoload.php';
+use Workerman\Worker;
+use Workerman\Lib\Timer;
+
+$task = new Worker();
+$task->onWorkerStart = function($task)
+{
+    // 2.5 seconds
+    $time_interval = 2.5; 
+    $timer_id = Timer::add($time_interval, 
+        function()
+        {
+            echo "Timer run\n";
+        }
+    );
+};
+
+// run all workers
+Worker::runAll();
+```
+
+### AsyncTcpConnection (tcp/ws/text/frame etc...)
+```php
+require_once __DIR__ . '/vendor/autoload.php';
+use Workerman\Worker;
+use Workerman\Connection\AsyncTcpConnection;
+
+$worker = new Worker();
+$worker->onWorkerStart = function()
+{
+    // Websocket protocol for client.
+    $ws_connection = new AsyncTcpConnection("ws://echo.websocket.org:80");
+    $ws_connection->onConnect = function($connection){
+        $connection->send('hello');
+    };
+    $ws_connection->onMessage = function($connection, $data){
+        echo "recv: $data\n";
+    };
+    $ws_connection->onError = function($connection, $code, $msg){
+        echo "error: $msg\n";
+    };
+    $ws_connection->onClose = function($connection){
+        echo "connection closed\n";
+    };
+    $ws_connection->connect();
+};
+Worker::runAll();
+```
+
+### Async Mysql of ReactPHP
+```
+composer require react/mysql
+```
+
+```php
+<?php
+require_once __DIR__ . '/vendor/autoload.php';
+use Workerman\Worker;
+
+$worker = new Worker('tcp://0.0.0.0:6161');
+$worker->onWorkerStart = function() {
+    global $mysql;
+    $loop  = Worker::getEventLoop();
+    $mysql = new React\MySQL\Connection($loop, array(
+        'host'   => '127.0.0.1',
+        'dbname' => 'dbname',
+        'user'   => 'user',
+        'passwd' => 'passwd',
+    ));
+    $mysql->on('error', function($e){
+        echo $e;
+    });
+    $mysql->connect(function ($e) {
+        if($e) {
+            echo $e;
+        } else {
+            echo "connect success\n";
+        }
+    });
+};
+$worker->onMessage = function($connection, $data) {
+    global $mysql;
+    $mysql->query('show databases' /*trim($data)*/, function ($command, $mysql) use ($connection) {
+        if ($command->hasError()) {
+            $error = $command->getError();
+        } else {
+            $results = $command->resultRows;
+            $fields  = $command->resultFields;
+            $connection->send(json_encode($results));
+        }
+    });
+};
+Worker::runAll();
+```
+
+### Async Redis of ReactPHP
+```
+composer require clue/redis-react
+```
+
+```php
+<?php
+require_once __DIR__ . '/vendor/autoload.php';
+use Clue\React\Redis\Factory;
+use Clue\React\Redis\Client;
+use Workerman\Worker;
+
+$worker = new Worker('tcp://0.0.0.0:6161');
+
+$worker->onWorkerStart = function() {
+    global $factory;
+    $loop    = Worker::getEventLoop();
+    $factory = new Factory($loop);
+};
+
+$worker->onMessage = function($connection, $data) {
+    global $factory;
+    $factory->createClient('localhost:6379')->then(function (Client $client) use ($connection) {
+        $client->set('greeting', 'Hello world');
+        $client->append('greeting', '!');
+
+        $client->get('greeting')->then(function ($greeting) use ($connection){
+            // Hello world!
+            echo $greeting . PHP_EOL;
+            $connection->send($greeting);
+        });
+
+        $client->incr('invocation')->then(function ($n) use ($connection){
+            echo 'This is invocation #' . $n . PHP_EOL;
+            $connection->send($n);
+        });
+    });
+};
+
+Worker::runAll();
+```
+
+### Aysnc dns of ReactPHP
+```
+composer require react/dns
+```
+
+```php
+require_once __DIR__ . '/vendor/autoload.php';
+use Workerman\Worker;
+$worker = new Worker('tcp://0.0.0.0:6161');
+$worker->onWorkerStart = function() {
+    global   $dns;
+    // Get event-loop.
+    $loop    = Worker::getEventLoop();
+    $factory = new React\Dns\Resolver\Factory();
+    $dns     = $factory->create('8.8.8.8', $loop);
+};
+$worker->onMessage = function($connection, $host) {
+    global $dns;
+    $host = trim($host);
+    $dns->resolve($host)->then(function($ip) use($host, $connection) {
+        $connection->send("$host: $ip");
+    },function($e) use($host, $connection){
+        $connection->send("$host: {$e->getMessage()}");
+    });
+};
+
+Worker::runAll();
+```
+
+### Http client of ReactPHP
+```
+composer require react/http-client
+```
+
+```php
+<?php
+require_once __DIR__ . '/vendor/autoload.php';
+use Workerman\Worker;
+
+$worker = new Worker('tcp://0.0.0.0:6161');
+
+$worker->onMessage = function($connection, $host) {
+    $loop    = Worker::getEventLoop();
+    $client  = new \React\HttpClient\Client($loop);
+    $request = $client->request('GET', trim($host));
+    $request->on('error', function(Exception $e) use ($connection) {
+        $connection->send($e);
+    });
+    $request->on('response', function ($response) use ($connection) {
+        $response->on('data', function ($data) use ($connection) {
+            $connection->send($data);
+        });
+    });
+    $request->end();
+};
+
+Worker::runAll();
+```
+
+### ZMQ of ReactPHP
+```
+composer require react/zmq
+```
+
+```php
+<?php
+require_once __DIR__ . '/vendor/autoload.php';
+use Workerman\Worker;
+
+$worker = new Worker('text://0.0.0.0:6161');
+
+$worker->onWorkerStart = function() {
+    global   $pull;
+    $loop    = Worker::getEventLoop();
+    $context = new React\ZMQ\Context($loop);
+    $pull    = $context->getSocket(ZMQ::SOCKET_PULL);
+    $pull->bind('tcp://127.0.0.1:5555');
+
+    $pull->on('error', function ($e) {
+        var_dump($e->getMessage());
+    });
+
+    $pull->on('message', function ($msg) {
+        echo "Received: $msg\n";
+    });
+};
+
+Worker::runAll();
+```
+
+### STOMP of ReactPHP
+```
+composer require react/stomp
+```
+
+```php
+<?php
+require_once __DIR__ . '/vendor/autoload.php';
+use Workerman\Worker;
+
+$worker = new Worker('text://0.0.0.0:6161');
+
+$worker->onWorkerStart = function() {
+    global   $client;
+    $loop    = Worker::getEventLoop();
+    $factory = new React\Stomp\Factory($loop);
+    $client  = $factory->createClient(array('vhost' => '/', 'login' => 'guest', 'passcode' => 'guest'));
+
+    $client
+        ->connect()
+        ->then(function ($client) use ($loop) {
+            $client->subscribe('/topic/foo', function ($frame) {
+                echo "Message received: {$frame->body}\n";
+            });
+        });
+};
+
+Worker::runAll();
+```
+
+
+
+## Available commands
+```php start.php start  ```  
+```php start.php start -d  ```  
+![workerman start](http://www.workerman.net/img/workerman-start.png)  
+```php start.php status  ```  
+![workerman satus](http://www.workerman.net/img/workerman-status.png?a=123)  
+```php start.php connections```  
+```php start.php stop  ```  
+```php start.php restart  ```  
+```php start.php reload  ```  
+
+## Documentation
+
+中文主页:[http://www.workerman.net](http://www.workerman.net)
+
+中文文档: [http://doc.workerman.net](http://doc.workerman.net)
+
+Documentation:[https://github.com/walkor/workerman-manual](https://github.com/walkor/workerman-manual/blob/master/english/src/SUMMARY.md)
+
+# Benchmarks
+```
+CPU:      Intel(R) Core(TM) i3-3220 CPU @ 3.30GHz and 4 processors totally
+Memory:   8G
+OS:       Ubuntu 14.04 LTS
+Software: ab
+PHP:      5.5.9
+```
+
+**Codes**
+```php
+<?php
+use Workerman\Worker;
+$worker = new Worker('tcp://0.0.0.0:1234');
+$worker->count=3;
+$worker->onMessage = function($connection, $data)
+{
+    $connection->send("HTTP/1.1 200 OK\r\nConnection: keep-alive\r\nServer: workerman\r\nContent-Length: 5\r\n\r\nhello");
+};
+Worker::runAll();
+```
+**Result**
+
+```shell
+ab -n1000000 -c100 -k http://127.0.0.1:1234/
+This is ApacheBench, Version 2.3 <$Revision: 1528965 $>
+Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
+Licensed to The Apache Software Foundation, http://www.apache.org/
+
+Benchmarking 127.0.0.1 (be patient)
+Completed 100000 requests
+Completed 200000 requests
+Completed 300000 requests
+Completed 400000 requests
+Completed 500000 requests
+Completed 600000 requests
+Completed 700000 requests
+Completed 800000 requests
+Completed 900000 requests
+Completed 1000000 requests
+Finished 1000000 requests
+
+
+Server Software:        workerman/3.1.4
+Server Hostname:        127.0.0.1
+Server Port:            1234
+
+Document Path:          /
+Document Length:        5 bytes
+
+Concurrency Level:      100
+Time taken for tests:   7.240 seconds
+Complete requests:      1000000
+Failed requests:        0
+Keep-Alive requests:    1000000
+Total transferred:      73000000 bytes
+HTML transferred:       5000000 bytes
+Requests per second:    138124.14 [#/sec] (mean)
+Time per request:       0.724 [ms] (mean)
+Time per request:       0.007 [ms] (mean, across all concurrent requests)
+Transfer rate:          9846.74 [Kbytes/sec] received
+
+Connection Times (ms)
+              min  mean[+/-sd] median   max
+Connect:        0    0   0.0      0       5
+Processing:     0    1   0.2      1       9
+Waiting:        0    1   0.2      1       9
+Total:          0    1   0.2      1       9
+
+Percentage of the requests served within a certain time (ms)
+  50%      1
+  66%      1
+  75%      1
+  80%      1
+  90%      1
+  95%      1
+  98%      1
+  99%      1
+ 100%      9 (longest request)
+
+```
+
+
+## Other links with workerman
+
+[PHPSocket.IO](https://github.com/walkor/phpsocket.io)   
+[php-socks5](https://github.com/walkor/php-socks5)  
+[php-http-proxy](https://github.com/walkor/php-http-proxy)  
+
+## Donate
+<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=UQGGS9UB35WWG"><img src="http://donate.workerman.net/img/donate.png"></a>
+
+## LICENSE
+
+Workerman is released under the [MIT license](https://github.com/walkor/workerman/blob/master/MIT-LICENSE.txt).
diff --git a/vendor/workerman/workerman/WebServer.php b/vendor/workerman/workerman/WebServer.php
new file mode 100755
index 0000000..82ea776
--- /dev/null
+++ b/vendor/workerman/workerman/WebServer.php
@@ -0,0 +1,311 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman;
+
+use Workerman\Protocols\Http;
+use Workerman\Protocols\HttpCache;
+
+/**
+ *  WebServer.
+ */
+class WebServer extends Worker
+{
+    /**
+     * Virtual host to path mapping.
+     *
+     * @var array ['workerman.net'=>'/home', 'www.workerman.net'=>'home/www']
+     */
+    protected $serverRoot = array();
+
+    /**
+     * Mime mapping.
+     *
+     * @var array
+     */
+    protected static $mimeTypeMap = array();
+
+
+    /**
+     * Used to save user OnWorkerStart callback settings.
+     *
+     * @var callback
+     */
+    protected $_onWorkerStart = null;
+
+    /**
+     * Add virtual host.
+     *
+     * @param string $domain
+     * @param string $config
+     * @return void
+     */
+    public function addRoot($domain, $config)
+    {
+	if (is_string($config)) {
+            $config = array('root' => $config);
+	}
+        $this->serverRoot[$domain] = $config;
+    }
+
+    /**
+     * Construct.
+     *
+     * @param string $socket_name
+     * @param array  $context_option
+     */
+    public function __construct($socket_name, $context_option = array())
+    {
+        list(, $address) = explode(':', $socket_name, 2);
+        parent::__construct('http:' . $address, $context_option);
+        $this->name = 'WebServer';
+    }
+
+    /**
+     * Run webserver instance.
+     *
+     * @see Workerman.Worker::run()
+     */
+    public function run()
+    {
+        $this->_onWorkerStart = $this->onWorkerStart;
+        $this->onWorkerStart  = array($this, 'onWorkerStart');
+        $this->onMessage      = array($this, 'onMessage');
+        parent::run();
+    }
+
+    /**
+     * Emit when process start.
+     *
+     * @throws \Exception
+     */
+    public function onWorkerStart()
+    {
+        if (empty($this->serverRoot)) {
+            Worker::safeEcho(new \Exception('server root not set, please use WebServer::addRoot($domain, $root_path) to set server root path'));
+            exit(250);
+        }
+
+        // Init mimeMap.
+        $this->initMimeTypeMap();
+
+        // Try to emit onWorkerStart callback.
+        if ($this->_onWorkerStart) {
+            try {
+                call_user_func($this->_onWorkerStart, $this);
+            } catch (\Exception $e) {
+                self::log($e);
+                exit(250);
+            } catch (\Error $e) {
+                self::log($e);
+                exit(250);
+            }
+        }
+    }
+
+    /**
+     * Init mime map.
+     *
+     * @return void
+     */
+    public function initMimeTypeMap()
+    {
+        $mime_file = Http::getMimeTypesFile();
+        if (!is_file($mime_file)) {
+            $this->log("$mime_file mime.type file not fond");
+            return;
+        }
+        $items = file($mime_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
+        if (!is_array($items)) {
+            $this->log("get $mime_file mime.type content fail");
+            return;
+        }
+        foreach ($items as $content) {
+            if (preg_match("/\s*(\S+)\s+(\S.+)/", $content, $match)) {
+                $mime_type                      = $match[1];
+                $workerman_file_extension_var   = $match[2];
+                $workerman_file_extension_array = explode(' ', substr($workerman_file_extension_var, 0, -1));
+                foreach ($workerman_file_extension_array as $workerman_file_extension) {
+                    self::$mimeTypeMap[$workerman_file_extension] = $mime_type;
+                }
+            }
+        }
+    }
+
+    /**
+     * Emit when http message coming.
+     *
+     * @param Connection\TcpConnection $connection
+     * @return void
+     */
+    public function onMessage($connection)
+    {
+        // REQUEST_URI.
+        $workerman_url_info = parse_url('http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);
+        if (!$workerman_url_info) {
+            Http::header('HTTP/1.1 400 Bad Request');
+            $connection->close('<h1>400 Bad Request</h1>');
+            return;
+        }
+
+        $workerman_path = isset($workerman_url_info['path']) ? $workerman_url_info['path'] : '/';
+
+        $workerman_path_info      = pathinfo($workerman_path);
+        $workerman_file_extension = isset($workerman_path_info['extension']) ? $workerman_path_info['extension'] : '';
+        if ($workerman_file_extension === '') {
+            $workerman_path           = ($len = strlen($workerman_path)) && $workerman_path[$len - 1] === '/' ? $workerman_path . 'index.php' : $workerman_path . '/index.php';
+            $workerman_file_extension = 'php';
+        }
+
+        $workerman_siteConfig = isset($this->serverRoot[$_SERVER['SERVER_NAME']]) ? $this->serverRoot[$_SERVER['SERVER_NAME']] : current($this->serverRoot);
+		$workerman_root_dir = $workerman_siteConfig['root'];
+        $workerman_file = "$workerman_root_dir/$workerman_path";
+		if(isset($workerman_siteConfig['additionHeader'])){
+			Http::header($workerman_siteConfig['additionHeader']);
+		}
+        if ($workerman_file_extension === 'php' && !is_file($workerman_file)) {
+            $workerman_file = "$workerman_root_dir/index.php";
+            if (!is_file($workerman_file)) {
+                $workerman_file           = "$workerman_root_dir/index.html";
+                $workerman_file_extension = 'html';
+            }
+        }
+
+        // File exsits.
+        if (is_file($workerman_file)) {
+            // Security check.
+            if ((!($workerman_request_realpath = realpath($workerman_file)) || !($workerman_root_dir_realpath = realpath($workerman_root_dir))) || 0 !== strpos($workerman_request_realpath,
+                    $workerman_root_dir_realpath)
+            ) {
+                Http::header('HTTP/1.1 400 Bad Request');
+                $connection->close('<h1>400 Bad Request</h1>');
+                return;
+            }
+
+            $workerman_file = realpath($workerman_file);
+
+            // Request php file.
+            if ($workerman_file_extension === 'php') {
+                $workerman_cwd = getcwd();
+                chdir($workerman_root_dir);
+                ini_set('display_errors', 'off');
+                ob_start();
+                // Try to include php file.
+                try {
+                    // $_SERVER.
+                    $_SERVER['REMOTE_ADDR'] = $connection->getRemoteIp();
+                    $_SERVER['REMOTE_PORT'] = $connection->getRemotePort();
+                    include $workerman_file;
+                } catch (\Exception $e) {
+                    // Jump_exit?
+                    if ($e->getMessage() != 'jump_exit') {
+                        Worker::safeEcho($e);
+                    }
+                }
+                $content = ob_get_clean();
+                ini_set('display_errors', 'on');
+                if (strtolower($_SERVER['HTTP_CONNECTION']) === "keep-alive") {
+                    $connection->send($content);
+                } else {
+                    $connection->close($content);
+                }
+                chdir($workerman_cwd);
+                return;
+            }
+
+            // Send file to client.
+            return self::sendFile($connection, $workerman_file);
+        } else {
+            // 404
+            Http::header("HTTP/1.1 404 Not Found");
+			if(isset($workerman_siteConfig['custom404']) && file_exists($workerman_siteConfig['custom404'])){
+				$html404 = file_get_contents($workerman_siteConfig['custom404']);
+			}else{
+				$html404 = '<html><head><title>404 File not found</title></head><body><center><h3>404 Not Found</h3></center></body></html>';
+			}
+            $connection->close($html404);
+            return;
+        }
+    }
+
+    public static function sendFile($connection, $file_path)
+    {
+        // Check 304.
+        $info = stat($file_path);
+        $modified_time = $info ? date('D, d M Y H:i:s', $info['mtime']) . ' ' . date_default_timezone_get() : '';
+        if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $info) {
+            // Http 304.
+            if ($modified_time === $_SERVER['HTTP_IF_MODIFIED_SINCE']) {
+                // 304
+                Http::header('HTTP/1.1 304 Not Modified');
+                // Send nothing but http headers..
+                $connection->close('');
+                return;
+            }
+        }
+
+        // Http header.
+        if ($modified_time) {
+            $modified_time = "Last-Modified: $modified_time\r\n";
+        }
+        $file_size = filesize($file_path);
+        $file_info = pathinfo($file_path);
+        $extension = isset($file_info['extension']) ? $file_info['extension'] : '';
+        $file_name = isset($file_info['filename']) ? $file_info['filename'] : '';
+        $header = "HTTP/1.1 200 OK\r\n";
+        if (isset(self::$mimeTypeMap[$extension])) {
+            $header .= "Content-Type: " . self::$mimeTypeMap[$extension] . "\r\n";
+        } else {
+            $header .= "Content-Type: application/octet-stream\r\n";
+            $header .= "Content-Disposition: attachment; filename=\"$file_name\"\r\n";
+        }
+        $header .= "Connection: keep-alive\r\n";
+        $header .= $modified_time;
+        $header .= "Content-Length: $file_size\r\n\r\n";
+        $trunk_limit_size = 1024*1024;
+        if ($file_size < $trunk_limit_size) {
+            return $connection->send($header.file_get_contents($file_path), true);
+        }
+        $connection->send($header, true);
+
+        // Read file content from disk piece by piece and send to client.
+        $connection->fileHandler = fopen($file_path, 'r');
+        $do_write = function()use($connection)
+        {
+            // Send buffer not full.
+            while(empty($connection->bufferFull))
+            {
+                // Read from disk.
+                $buffer = fread($connection->fileHandler, 8192);
+                // Read eof.
+                if($buffer === '' || $buffer === false)
+                {
+                    return;
+                }
+                $connection->send($buffer, true);
+            }
+        };
+        // Send buffer full.
+        $connection->onBufferFull = function($connection)
+        {
+            $connection->bufferFull = true;
+        };
+        // Send buffer drain.
+        $connection->onBufferDrain = function($connection)use($do_write)
+        {
+            $connection->bufferFull = false;
+            $do_write();
+        };
+        $do_write();
+    }
+}
diff --git a/vendor/workerman/workerman/Worker.php b/vendor/workerman/workerman/Worker.php
new file mode 100755
index 0000000..8dbbb57
--- /dev/null
+++ b/vendor/workerman/workerman/Worker.php
@@ -0,0 +1,2490 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman;
+require_once __DIR__ . '/Lib/Constants.php';
+
+use Workerman\Events\EventInterface;
+use Workerman\Connection\ConnectionInterface;
+use Workerman\Connection\TcpConnection;
+use Workerman\Connection\UdpConnection;
+use Workerman\Lib\Timer;
+use Workerman\Events\Select;
+use Exception;
+
+/**
+ * Worker class
+ * A container for listening ports
+ */
+class Worker
+{
+    /**
+     * Version.
+     *
+     * @var string
+     */
+    const VERSION = '3.5.19';
+
+    /**
+     * Status starting.
+     *
+     * @var int
+     */
+    const STATUS_STARTING = 1;
+
+    /**
+     * Status running.
+     *
+     * @var int
+     */
+    const STATUS_RUNNING = 2;
+
+    /**
+     * Status shutdown.
+     *
+     * @var int
+     */
+    const STATUS_SHUTDOWN = 4;
+
+    /**
+     * Status reloading.
+     *
+     * @var int
+     */
+    const STATUS_RELOADING = 8;
+
+    /**
+     * After sending the restart command to the child process KILL_WORKER_TIMER_TIME seconds,
+     * if the process is still living then forced to kill.
+     *
+     * @var int
+     */
+    const KILL_WORKER_TIMER_TIME = 2;
+
+    /**
+     * Default backlog. Backlog is the maximum length of the queue of pending connections.
+     *
+     * @var int
+     */
+    const DEFAULT_BACKLOG = 102400;
+    /**
+     * Max udp package size.
+     *
+     * @var int
+     */
+    const MAX_UDP_PACKAGE_SIZE = 65535;
+
+    /**
+     * The safe distance for columns adjacent
+     *
+     * @var int
+     */
+    const UI_SAFE_LENGTH = 4;
+
+    /**
+     * Worker id.
+     *
+     * @var int
+     */
+    public $id = 0;
+
+    /**
+     * Name of the worker processes.
+     *
+     * @var string
+     */
+    public $name = 'none';
+
+    /**
+     * Number of worker processes.
+     *
+     * @var int
+     */
+    public $count = 1;
+
+    /**
+     * Unix user of processes, needs appropriate privileges (usually root).
+     *
+     * @var string
+     */
+    public $user = '';
+
+    /**
+     * Unix group of processes, needs appropriate privileges (usually root).
+     *
+     * @var string
+     */
+    public $group = '';
+
+    /**
+     * reloadable.
+     *
+     * @var bool
+     */
+    public $reloadable = true;
+
+    /**
+     * reuse port.
+     *
+     * @var bool
+     */
+    public $reusePort = false;
+
+    /**
+     * Emitted when worker processes start.
+     *
+     * @var callback
+     */
+    public $onWorkerStart = null;
+
+    /**
+     * Emitted when a socket connection is successfully established.
+     *
+     * @var callback
+     */
+    public $onConnect = null;
+
+    /**
+     * Emitted when data is received.
+     *
+     * @var callback
+     */
+    public $onMessage = null;
+
+    /**
+     * Emitted when the other end of the socket sends a FIN packet.
+     *
+     * @var callback
+     */
+    public $onClose = null;
+
+    /**
+     * Emitted when an error occurs with connection.
+     *
+     * @var callback
+     */
+    public $onError = null;
+
+    /**
+     * Emitted when the send buffer becomes full.
+     *
+     * @var callback
+     */
+    public $onBufferFull = null;
+
+    /**
+     * Emitted when the send buffer becomes empty.
+     *
+     * @var callback
+     */
+    public $onBufferDrain = null;
+
+    /**
+     * Emitted when worker processes stoped.
+     *
+     * @var callback
+     */
+    public $onWorkerStop = null;
+
+    /**
+     * Emitted when worker processes get reload signal.
+     *
+     * @var callback
+     */
+    public $onWorkerReload = null;
+
+    /**
+     * Transport layer protocol.
+     *
+     * @var string
+     */
+    public $transport = 'tcp';
+
+    /**
+     * Store all connections of clients.
+     *
+     * @var array
+     */
+    public $connections = array();
+
+    /**
+     * Application layer protocol.
+     *
+     * @var string
+     */
+    public $protocol = null;
+
+    /**
+     * Root path for autoload.
+     *
+     * @var string
+     */
+    protected $_autoloadRootPath = '';
+
+    /**
+     * Pause accept new connections or not.
+     *
+     * @var bool
+     */
+    protected $_pauseAccept = true;
+
+    /**
+     * Is worker stopping ?
+     * @var bool
+     */
+    public $stopping = false;
+
+    /**
+     * Daemonize.
+     *
+     * @var bool
+     */
+    public static $daemonize = false;
+
+    /**
+     * Stdout file.
+     *
+     * @var string
+     */
+    public static $stdoutFile = '/dev/null';
+
+    /**
+     * The file to store master process PID.
+     *
+     * @var string
+     */
+    public static $pidFile = '';
+
+    /**
+     * Log file.
+     *
+     * @var mixed
+     */
+    public static $logFile = '';
+
+    /**
+     * Global event loop.
+     *
+     * @var Events\EventInterface
+     */
+    public static $globalEvent = null;
+
+    /**
+     * Emitted when the master process get reload signal.
+     *
+     * @var callback
+     */
+    public static $onMasterReload = null;
+
+    /**
+     * Emitted when the master process terminated.
+     *
+     * @var callback
+     */
+    public static $onMasterStop = null;
+
+    /**
+     * EventLoopClass
+     *
+     * @var string
+     */
+    public static $eventLoopClass = '';
+
+    /**
+     * The PID of master process.
+     *
+     * @var int
+     */
+    protected static $_masterPid = 0;
+
+    /**
+     * Listening socket.
+     *
+     * @var resource
+     */
+    protected $_mainSocket = null;
+
+    /**
+     * Socket name. The format is like this http://0.0.0.0:80 .
+     *
+     * @var string
+     */
+    protected $_socketName = '';
+
+    /**
+     * Context of socket.
+     *
+     * @var resource
+     */
+    protected $_context = null;
+
+    /**
+     * All worker instances.
+     *
+     * @var Worker[]
+     */
+    protected static $_workers = array();
+
+    /**
+     * All worker processes pid.
+     * The format is like this [worker_id=>[pid=>pid, pid=>pid, ..], ..]
+     *
+     * @var array
+     */
+    protected static $_pidMap = array();
+
+    /**
+     * All worker processes waiting for restart.
+     * The format is like this [pid=>pid, pid=>pid].
+     *
+     * @var array
+     */
+    protected static $_pidsToRestart = array();
+
+    /**
+     * Mapping from PID to worker process ID.
+     * The format is like this [worker_id=>[0=>$pid, 1=>$pid, ..], ..].
+     *
+     * @var array
+     */
+    protected static $_idMap = array();
+
+    /**
+     * Current status.
+     *
+     * @var int
+     */
+    protected static $_status = self::STATUS_STARTING;
+
+    /**
+     * Maximum length of the worker names.
+     *
+     * @var int
+     */
+    protected static $_maxWorkerNameLength = 12;
+
+    /**
+     * Maximum length of the socket names.
+     *
+     * @var int
+     */
+    protected static $_maxSocketNameLength = 12;
+
+    /**
+     * Maximum length of the process user names.
+     *
+     * @var int
+     */
+    protected static $_maxUserNameLength = 12;
+
+    /**
+     * Maximum length of the Proto names.
+     *
+     * @var int
+     */
+    protected static $_maxProtoNameLength = 4;
+
+    /**
+     * Maximum length of the Processes names.
+     *
+     * @var int
+     */
+    protected static $_maxProcessesNameLength = 9;
+
+    /**
+     * Maximum length of the Status names.
+     *
+     * @var int
+     */
+    protected static $_maxStatusNameLength = 1;
+
+    /**
+     * The file to store status info of current worker process.
+     *
+     * @var string
+     */
+    protected static $_statisticsFile = '';
+
+    /**
+     * Start file.
+     *
+     * @var string
+     */
+    protected static $_startFile = '';
+
+    /**
+     * OS.
+     *
+     * @var string
+     */
+    protected static $_OS = OS_TYPE_LINUX;
+
+    /**
+     * Processes for windows.
+     *
+     * @var array
+     */
+    protected static $_processForWindows = array();
+
+    /**
+     * Status info of current worker process.
+     *
+     * @var array
+     */
+    protected static $_globalStatistics = array(
+        'start_timestamp'  => 0,
+        'worker_exit_info' => array()
+    );
+
+    /**
+     * Available event loops.
+     *
+     * @var array
+     */
+    protected static $_availableEventLoops = array(
+        'libevent' => '\Workerman\Events\Libevent',
+        'event'    => '\Workerman\Events\Event'
+        // Temporarily removed swoole because it is not stable enough  
+        //'swoole'   => '\Workerman\Events\Swoole'
+    );
+
+    /**
+     * PHP built-in protocols.
+     *
+     * @var array
+     */
+    protected static $_builtinTransports = array(
+        'tcp'   => 'tcp',
+        'udp'   => 'udp',
+        'unix'  => 'unix',
+        'ssl'   => 'tcp'
+    );
+
+    /**
+     * Graceful stop or not.
+     *
+     * @var string
+     */
+    protected static $_gracefulStop = false;
+
+    /**
+     * Standard output stream
+     * @var resource
+     */
+    protected static $_outputStream = null;
+
+    /**
+     * If $outputStream support decorated
+     * @var bool
+     */
+    protected static $_outputDecorated = null;
+
+    /**
+     * Run all worker instances.
+     *
+     * @return void
+     */
+    public static function runAll()
+    {
+        static::checkSapiEnv();
+        static::init();
+        static::lock();
+        static::parseCommand();
+        static::daemonize();
+        static::initWorkers();
+        static::installSignal();
+        static::saveMasterPid();
+        static::unlock();
+        static::displayUI();
+        static::forkWorkers();
+        static::resetStd();
+        static::monitorWorkers();
+    }
+
+    /**
+     * Check sapi.
+     *
+     * @return void
+     */
+    protected static function checkSapiEnv()
+    {
+        // Only for cli.
+        if (php_sapi_name() != "cli") {
+            exit("only run in command line mode \n");
+        }
+        if (DIRECTORY_SEPARATOR === '\\') {
+            self::$_OS = OS_TYPE_WINDOWS;
+        }
+    }
+
+    /**
+     * Init.
+     *
+     * @return void
+     */
+    protected static function init()
+    {
+        set_error_handler(function($code, $msg, $file, $line){
+            Worker::safeEcho("$msg in file $file on line $line\n");
+        });
+
+        // Start file.
+        $backtrace        = debug_backtrace();
+        static::$_startFile = $backtrace[count($backtrace) - 1]['file'];
+
+
+        $unique_prefix = str_replace('/', '_', static::$_startFile);
+
+        // Pid file.
+        if (empty(static::$pidFile)) {
+            static::$pidFile = __DIR__ . "/../$unique_prefix.pid";
+        }
+
+        // Log file.
+        if (empty(static::$logFile)) {
+            static::$logFile = __DIR__ . '/../workerman.log';
+        }
+        $log_file = (string)static::$logFile;
+        if (!is_file($log_file)) {
+            touch($log_file);
+            chmod($log_file, 0622);
+        }
+
+        // State.
+        static::$_status = static::STATUS_STARTING;
+
+        // For statistics.
+        static::$_globalStatistics['start_timestamp'] = time();
+        static::$_statisticsFile                      = sys_get_temp_dir() . "/$unique_prefix.status";
+
+        // Process title.
+        static::setProcessTitle('WorkerMan: master process  start_file=' . static::$_startFile);
+
+        // Init data for worker id.
+        static::initId();
+
+        // Timer init.
+        Timer::init();
+    }
+
+    /**
+     * Lock.
+     *
+     * @return void
+     */
+    protected static function lock()
+    {
+        $fd = fopen(static::$_startFile, 'r');
+        if (!$fd || !flock($fd, LOCK_EX)) {
+            static::log("Workerman[".static::$_startFile."] already running");
+            exit;
+        }
+    }
+
+    /**
+     * Unlock.
+     *
+     * @return void
+     */
+    protected static function unlock()
+    {
+        $fd = fopen(static::$_startFile, 'r');
+        $fd && flock($fd, LOCK_UN);
+    }
+
+    /**
+     * Init All worker instances.
+     *
+     * @return void
+     */
+    protected static function initWorkers()
+    {
+        if (static::$_OS !== OS_TYPE_LINUX) {
+            return;
+        }
+        foreach (static::$_workers as $worker) {
+            // Worker name.
+            if (empty($worker->name)) {
+                $worker->name = 'none';
+            }
+
+            // Get unix user of the worker process.
+            if (empty($worker->user)) {
+                $worker->user = static::getCurrentUser();
+            } else {
+                if (posix_getuid() !== 0 && $worker->user != static::getCurrentUser()) {
+                    static::log('Warning: You must have the root privileges to change uid and gid.');
+                }
+            }
+
+            // Socket name.
+            $worker->socket = $worker->getSocketName();
+
+            // Status name.
+            $worker->status = '<g> [OK] </g>';
+
+            // Get column mapping for UI
+            foreach(static::getUiColumns() as $column_name => $prop){
+                !isset($worker->{$prop}) && $worker->{$prop}= 'NNNN';
+                $prop_length = strlen($worker->{$prop});
+                $key = '_max' . ucfirst(strtolower($column_name)) . 'NameLength';
+                static::$$key = max(static::$$key, $prop_length);
+            }
+
+            // Listen.
+            if (!$worker->reusePort) {
+                $worker->listen();
+            }
+        }
+    }
+
+    /**
+     * Get all worker instances.
+     *
+     * @return array
+     */
+    public static function getAllWorkers()
+    {
+        return static::$_workers;
+    }
+
+    /**
+     * Get global event-loop instance.
+     *
+     * @return EventInterface
+     */
+    public static function getEventLoop()
+    {
+        return static::$globalEvent;
+    }
+    
+    /**
+     * Get main socket resource
+     * @return resource
+     */
+    public function getMainSocket(){
+        return $this->_mainSocket;
+    }
+
+    /**
+     * Init idMap.
+     * return void
+     */
+    protected static function initId()
+    {
+        foreach (static::$_workers as $worker_id => $worker) {
+            $new_id_map = array();
+            $worker->count = $worker->count <= 0 ? 1 : $worker->count;
+            for($key = 0; $key < $worker->count; $key++) {
+                $new_id_map[$key] = isset(static::$_idMap[$worker_id][$key]) ? static::$_idMap[$worker_id][$key] : 0;
+            }
+            static::$_idMap[$worker_id] = $new_id_map;
+        }
+    }
+
+    /**
+     * Get unix user of current porcess.
+     *
+     * @return string
+     */
+    protected static function getCurrentUser()
+    {
+        $user_info = posix_getpwuid(posix_getuid());
+        return $user_info['name'];
+    }
+
+    /**
+     * Display staring UI.
+     *
+     * @return void
+     */
+    protected static function displayUI()
+    {
+        global $argv;
+        if (in_array('-q', $argv)) {
+            return;
+        }
+        if (static::$_OS !== OS_TYPE_LINUX) {
+            static::safeEcho("----------------------- WORKERMAN -----------------------------\r\n");
+            static::safeEcho('Workerman version:'. static::VERSION. "          PHP version:". PHP_VERSION. "\r\n");
+            static::safeEcho("------------------------ WORKERS -------------------------------\r\n");
+            static::safeEcho("worker               listen                              processes status\r\n");
+            return;
+        }
+
+        //show version
+        $line_version = 'Workerman version:' . static::VERSION . str_pad('PHP version:', 22, ' ', STR_PAD_LEFT) . PHP_VERSION . PHP_EOL;
+        !defined('LINE_VERSIOIN_LENGTH') && define('LINE_VERSIOIN_LENGTH', strlen($line_version));
+        $total_length = static::getSingleLineTotalLength();
+        $line_one = '<n>' . str_pad('<w> WORKERMAN </w>', $total_length + strlen('<w></w>'), '-', STR_PAD_BOTH) . '</n>'. PHP_EOL;
+        $line_two = str_pad('<w> WORKERS </w>' , $total_length  + strlen('<w></w>'), '-', STR_PAD_BOTH) . PHP_EOL;
+        static::safeEcho($line_one . $line_version . $line_two);
+
+        //Show title
+        $title = '';
+        foreach(static::getUiColumns() as $column_name => $prop){
+            $key = '_max' . ucfirst(strtolower($column_name)) . 'NameLength';
+            //just keep compatible with listen name 
+            $column_name == 'socket' && $column_name = 'listen';
+            $title.= "<w>{$column_name}</w>"  .  str_pad('', static::$$key + static::UI_SAFE_LENGTH - strlen($column_name));
+        }
+        $title && static::safeEcho($title . PHP_EOL);
+
+        //Show content
+        foreach (static::$_workers as $worker) {
+            $content = '';
+            foreach(static::getUiColumns() as $column_name => $prop){
+                $key = '_max' . ucfirst(strtolower($column_name)) . 'NameLength';
+                preg_match_all("/(<n>|<\/n>|<w>|<\/w>|<g>|<\/g>)/is", $worker->{$prop}, $matches);
+                $place_holder_length = !empty($matches) ? strlen(implode('', $matches[0])) : 0;
+                $content .= str_pad($worker->{$prop}, static::$$key + static::UI_SAFE_LENGTH + $place_holder_length);
+            }
+            $content && static::safeEcho($content . PHP_EOL);
+        }
+
+        //Show last line
+        $line_last = str_pad('', static::getSingleLineTotalLength(), '-') . PHP_EOL;
+        !empty($content) && static::safeEcho($line_last);
+
+        if (static::$daemonize) {
+            static::safeEcho("Input \"php $argv[0] stop\" to stop. Start success.\n\n");
+        } else {
+            static::safeEcho("Press Ctrl+C to stop. Start success.\n");
+        }
+    }
+
+    /**
+     * Get UI columns to be shown in terminal
+     *
+     * 1. $column_map: array('ui_column_name' => 'clas_property_name')
+     * 2. Consider move into configuration in future
+     *
+     * @return array
+     */
+    public static function getUiColumns()
+    {
+        $column_map = array(
+            'proto'     =>  'transport',
+            'user'      =>  'user',
+            'worker'    =>  'name',
+            'socket'    =>  'socket',
+            'processes' =>  'count',
+            'status'    =>  'status',
+        );
+
+        return $column_map;
+    }
+
+    /**
+     * Get single line total length for ui
+     *
+     * @return int
+     */
+    public static function getSingleLineTotalLength()
+    {
+        $total_length = 0;
+
+        foreach(static::getUiColumns() as $column_name => $prop){
+            $key = '_max' . ucfirst(strtolower($column_name)) . 'NameLength';
+            $total_length += static::$$key + static::UI_SAFE_LENGTH;
+        }
+
+        //keep beauty when show less colums
+        !defined('LINE_VERSIOIN_LENGTH') && define('LINE_VERSIOIN_LENGTH', 0);
+        $total_length <= LINE_VERSIOIN_LENGTH && $total_length = LINE_VERSIOIN_LENGTH;
+
+        return $total_length;
+    }
+
+    /**
+     * Parse command.
+     *
+     * @return void
+     */
+    protected static function parseCommand()
+    {
+        if (static::$_OS !== OS_TYPE_LINUX) {
+            return;
+        }
+        global $argv;
+        // Check argv;
+        $start_file = $argv[0];
+        $available_commands = array(
+            'start',
+            'stop',
+            'restart',
+            'reload',
+            'status',
+            'connections',
+        );
+        $usage = "Usage: php yourfile <command> [mode]\nCommands: \nstart\t\tStart worker in DEBUG mode.\n\t\tUse mode -d to start in DAEMON mode.\nstop\t\tStop worker.\n\t\tUse mode -g to stop gracefully.\nrestart\t\tRestart workers.\n\t\tUse mode -d to start in DAEMON mode.\n\t\tUse mode -g to stop gracefully.\nreload\t\tReload codes.\n\t\tUse mode -g to reload gracefully.\nstatus\t\tGet worker status.\n\t\tUse mode -d to show live status.\nconnections\tGet worker connections.\n";
+        if (!isset($argv[1]) || !in_array($argv[1], $available_commands)) {
+            if (isset($argv[1])) {
+                static::safeEcho('Unknown command: ' . $argv[1] . "\n");
+            }
+            exit($usage);
+        }
+
+        // Get command.
+        $command  = trim($argv[1]);
+        $command2 = isset($argv[2]) ? $argv[2] : '';
+
+        // Start command.
+        $mode = '';
+        if ($command === 'start') {
+            if ($command2 === '-d' || static::$daemonize) {
+                $mode = 'in DAEMON mode';
+            } else {
+                $mode = 'in DEBUG mode';
+            }
+        }
+        static::log("Workerman[$start_file] $command $mode");
+
+        // Get master process PID.
+        $master_pid      = is_file(static::$pidFile) ? file_get_contents(static::$pidFile) : 0;
+        $master_is_alive = $master_pid && posix_kill($master_pid, 0) && posix_getpid() != $master_pid;
+        // Master is still alive?
+        if ($master_is_alive) {
+            if ($command === 'start') {
+                static::log("Workerman[$start_file] already running");
+                exit;
+            }
+        } elseif ($command !== 'start' && $command !== 'restart') {
+            static::log("Workerman[$start_file] not run");
+            exit;
+        }
+
+        // execute command.
+        switch ($command) {
+            case 'start':
+                if ($command2 === '-d') {
+                    static::$daemonize = true;
+                }
+                break;
+            case 'status':
+                while (1) {
+                    if (is_file(static::$_statisticsFile)) {
+                        @unlink(static::$_statisticsFile);
+                    }
+                    // Master process will send SIGUSR2 signal to all child processes.
+                    posix_kill($master_pid, SIGUSR2);
+                    // Sleep 1 second.
+                    sleep(1);
+                    // Clear terminal.
+                    if ($command2 === '-d') {
+                        static::safeEcho("\33[H\33[2J\33(B\33[m", true);
+                    }
+                    // Echo status data.
+                    static::safeEcho(static::formatStatusData());
+                    if ($command2 !== '-d') {
+                        exit(0);
+                    }
+                    static::safeEcho("\nPress Ctrl+C to quit.\n\n");
+                }
+                exit(0);
+            case 'connections':
+                if (is_file(static::$_statisticsFile) && is_writable(static::$_statisticsFile)) {
+                    unlink(static::$_statisticsFile);
+                }
+                // Master process will send SIGIO signal to all child processes.
+                posix_kill($master_pid, SIGIO);
+                // Waiting amoment.
+                usleep(500000);
+                // Display statisitcs data from a disk file.
+                if(is_readable(static::$_statisticsFile)) {
+                    readfile(static::$_statisticsFile);
+                }
+                exit(0);
+            case 'restart':
+            case 'stop':
+                if ($command2 === '-g') {
+                    static::$_gracefulStop = true;
+                    $sig = SIGTERM;
+                    static::log("Workerman[$start_file] is gracefully stopping ...");
+                } else {
+                    static::$_gracefulStop = false;
+                    $sig = SIGINT;
+                    static::log("Workerman[$start_file] is stopping ...");
+                }
+                // Send stop signal to master process.
+                $master_pid && posix_kill($master_pid, $sig);
+                // Timeout.
+                $timeout    = 5;
+                $start_time = time();
+                // Check master process is still alive?
+                while (1) {
+                    $master_is_alive = $master_pid && posix_kill($master_pid, 0);
+                    if ($master_is_alive) {
+                        // Timeout?
+                        if (!static::$_gracefulStop && time() - $start_time >= $timeout) {
+                            static::log("Workerman[$start_file] stop fail");
+                            exit;
+                        }
+                        // Waiting amoment.
+                        usleep(10000);
+                        continue;
+                    }
+                    // Stop success.
+                    static::log("Workerman[$start_file] stop success");
+                    if ($command === 'stop') {
+                        exit(0);
+                    }
+                    if ($command2 === '-d') {
+                        static::$daemonize = true;
+                    }
+                    break;
+                }
+                break;
+            case 'reload':
+                if($command2 === '-g'){
+                    $sig = SIGQUIT;
+                }else{
+                    $sig = SIGUSR1;
+                }
+                posix_kill($master_pid, $sig);
+                exit;
+            default :
+                if (isset($command)) {
+                    static::safeEcho('Unknown command: ' . $command . "\n");
+                }
+                exit($usage);
+        }
+    }
+
+    /**
+     * Format status data.
+     *
+     * @return string
+     */
+    protected static function formatStatusData()
+    {
+        static $total_request_cache = array();
+        if (!is_readable(static::$_statisticsFile)) {
+            return '';
+        }
+        $info = file(static::$_statisticsFile, FILE_IGNORE_NEW_LINES);
+        if (!$info) {
+            return '';
+        }
+        $status_str = '';
+        $current_total_request = array();
+        $worker_info = json_decode($info[0], true);
+        ksort($worker_info, SORT_NUMERIC);
+        unset($info[0]);
+        $data_waiting_sort = array();
+        $read_process_status = false;
+        $total_requests = 0;
+        $total_qps = 0;
+        $total_connections = 0;
+        $total_fails = 0;
+        $total_memory = 0;
+        $total_timers = 0;
+        $maxLen1 = static::$_maxSocketNameLength;
+        $maxLen2 = static::$_maxWorkerNameLength;
+        foreach($info as $key => $value) {
+            if (!$read_process_status) {
+                $status_str .= $value . "\n";
+                if (preg_match('/^pid.*?memory.*?listening/', $value)) {
+                    $read_process_status = true;
+                }
+                continue;
+            }
+            if(preg_match('/^[0-9]+/', $value, $pid_math)) {
+                $pid = $pid_math[0];
+                $data_waiting_sort[$pid] = $value;
+                if(preg_match('/^\S+?\s+?(\S+?)\s+?(\S+?)\s+?(\S+?)\s+?(\S+?)\s+?(\S+?)\s+?(\S+?)\s+?(\S+?)\s+?/', $value, $match)) {
+                    $total_memory += intval(str_ireplace('M','',$match[1]));
+                    $maxLen1 = max($maxLen1,strlen($match[2]));
+                    $maxLen2 = max($maxLen2,strlen($match[3]));
+                    $total_connections += intval($match[4]);
+                    $total_fails += intval($match[5]);
+                    $total_timers += intval($match[6]);
+                    $current_total_request[$pid] = $match[7];
+                    $total_requests += intval($match[7]);
+                }
+            }
+        }
+        foreach($worker_info as $pid => $info) {
+            if (!isset($data_waiting_sort[$pid])) {
+                $status_str .= "$pid\t" . str_pad('N/A', 7) . " "
+                    . str_pad($info['listen'], static::$_maxSocketNameLength) . " "
+                    . str_pad($info['name'], static::$_maxWorkerNameLength) . " "
+                    . str_pad('N/A', 11) . " " . str_pad('N/A', 9) . " "
+                    . str_pad('N/A', 7) . " " . str_pad('N/A', 13) . " N/A    [busy] \n";
+                continue;
+            }
+            //$qps = isset($total_request_cache[$pid]) ? $current_total_request[$pid]
+            if (!isset($total_request_cache[$pid]) || !isset($current_total_request[$pid])) {
+                $qps = 0;
+            } else {
+                $qps = $current_total_request[$pid] - $total_request_cache[$pid];
+                $total_qps += $qps;
+            }
+            $status_str .= $data_waiting_sort[$pid]. " " . str_pad($qps, 6) ." [idle]\n";
+        }
+        $total_request_cache = $current_total_request;
+        $status_str .= "----------------------------------------------PROCESS STATUS---------------------------------------------------\n";
+        $status_str .= "Summary\t" . str_pad($total_memory.'M', 7) . " "
+            . str_pad('-', $maxLen1) . " "
+            . str_pad('-', $maxLen2) . " "
+            . str_pad($total_connections, 11) . " " . str_pad($total_fails, 9) . " "
+            . str_pad($total_timers, 7) . " " . str_pad($total_requests, 13) . " "
+            . str_pad($total_qps,6)." [Summary] \n";
+        return $status_str;
+    }
+
+
+    /**
+     * Install signal handler.
+     *
+     * @return void
+     */
+    protected static function installSignal()
+    {
+        if (static::$_OS !== OS_TYPE_LINUX) {
+            return;
+        }
+        // stop
+        pcntl_signal(SIGINT, array('\Workerman\Worker', 'signalHandler'), false);
+        // graceful stop
+        pcntl_signal(SIGTERM, array('\Workerman\Worker', 'signalHandler'), false);
+        // reload
+        pcntl_signal(SIGUSR1, array('\Workerman\Worker', 'signalHandler'), false);
+        // graceful reload
+        pcntl_signal(SIGQUIT, array('\Workerman\Worker', 'signalHandler'), false);
+        // status
+        pcntl_signal(SIGUSR2, array('\Workerman\Worker', 'signalHandler'), false);
+        // connection status
+        pcntl_signal(SIGIO, array('\Workerman\Worker', 'signalHandler'), false);
+        // ignore
+        pcntl_signal(SIGPIPE, SIG_IGN, false);
+    }
+
+    /**
+     * Reinstall signal handler.
+     *
+     * @return void
+     */
+    protected static function reinstallSignal()
+    {
+        if (static::$_OS !== OS_TYPE_LINUX) {
+            return;
+        }
+        // uninstall stop signal handler
+        pcntl_signal(SIGINT, SIG_IGN, false);
+        // uninstall graceful stop signal handler
+        pcntl_signal(SIGTERM, SIG_IGN, false);
+        // uninstall reload signal handler
+        pcntl_signal(SIGUSR1, SIG_IGN, false);
+        // uninstall graceful reload signal handler
+        pcntl_signal(SIGQUIT, SIG_IGN, false);
+        // uninstall status signal handler
+        pcntl_signal(SIGUSR2, SIG_IGN, false);
+        // reinstall stop signal handler
+        static::$globalEvent->add(SIGINT, EventInterface::EV_SIGNAL, array('\Workerman\Worker', 'signalHandler'));
+        // reinstall graceful stop signal handler
+        static::$globalEvent->add(SIGTERM, EventInterface::EV_SIGNAL, array('\Workerman\Worker', 'signalHandler'));
+        // reinstall reload signal handler
+        static::$globalEvent->add(SIGUSR1, EventInterface::EV_SIGNAL, array('\Workerman\Worker', 'signalHandler'));
+        // reinstall graceful reload signal handler
+        static::$globalEvent->add(SIGQUIT, EventInterface::EV_SIGNAL, array('\Workerman\Worker', 'signalHandler'));
+        // reinstall status signal handler
+        static::$globalEvent->add(SIGUSR2, EventInterface::EV_SIGNAL, array('\Workerman\Worker', 'signalHandler'));
+        // reinstall connection status signal handler
+        static::$globalEvent->add(SIGIO, EventInterface::EV_SIGNAL, array('\Workerman\Worker', 'signalHandler'));
+    }
+
+    /**
+     * Signal handler.
+     *
+     * @param int $signal
+     */
+    public static function signalHandler($signal)
+    {
+        switch ($signal) {
+            // Stop.
+            case SIGINT:
+                static::$_gracefulStop = false;
+                static::stopAll();
+                break;
+            // Graceful stop.
+            case SIGTERM:
+                static::$_gracefulStop = true;
+                static::stopAll();
+                break;
+            // Reload.
+            case SIGQUIT:
+            case SIGUSR1:
+                if($signal === SIGQUIT){
+                    static::$_gracefulStop = true;
+                }else{
+                    static::$_gracefulStop = false;
+                }
+                static::$_pidsToRestart = static::getAllWorkerPids();
+                static::reload();
+                break;
+            // Show status.
+            case SIGUSR2:
+                static::writeStatisticsToStatusFile();
+                break;
+            // Show connection status.
+            case SIGIO:
+                static::writeConnectionsStatisticsToStatusFile();
+                break;
+        }
+    }
+
+    /**
+     * Run as deamon mode.
+     *
+     * @throws Exception
+     */
+    protected static function daemonize()
+    {
+        if (!static::$daemonize || static::$_OS !== OS_TYPE_LINUX) {
+            return;
+        }
+        umask(0);
+        $pid = pcntl_fork();
+        if (-1 === $pid) {
+            throw new Exception('fork fail');
+        } elseif ($pid > 0) {
+            exit(0);
+        }
+        if (-1 === posix_setsid()) {
+            throw new Exception("setsid fail");
+        }
+        // Fork again avoid SVR4 system regain the control of terminal.
+        $pid = pcntl_fork();
+        if (-1 === $pid) {
+            throw new Exception("fork fail");
+        } elseif (0 !== $pid) {
+            exit(0);
+        }
+    }
+
+    /**
+     * Redirect standard input and output.
+     *
+     * @throws Exception
+     */
+    public static function resetStd()
+    {
+        if (!static::$daemonize || static::$_OS !== OS_TYPE_LINUX) {
+            return;
+        }
+        global $STDOUT, $STDERR;
+        $handle = fopen(static::$stdoutFile, "a");
+        if ($handle) {
+            unset($handle);
+            set_error_handler(function(){});
+            fclose($STDOUT);
+            fclose($STDERR);
+            fclose(STDOUT);
+            fclose(STDERR);
+            $STDOUT = fopen(static::$stdoutFile, "a");
+            $STDERR = fopen(static::$stdoutFile, "a");
+            // change output stream
+            static::$_outputStream = null;
+            static::outputStream($STDOUT);
+            restore_error_handler();
+        } else {
+            throw new Exception('can not open stdoutFile ' . static::$stdoutFile);
+        }
+    }
+
+    /**
+     * Save pid.
+     *
+     * @throws Exception
+     */
+    protected static function saveMasterPid()
+    {
+        if (static::$_OS !== OS_TYPE_LINUX) {
+            return;
+        }
+
+        static::$_masterPid = posix_getpid();
+        if (false === file_put_contents(static::$pidFile, static::$_masterPid)) {
+            throw new Exception('can not save pid to ' . static::$pidFile);
+        }
+    }
+
+    /**
+     * Get event loop name.
+     *
+     * @return string
+     */
+    protected static function getEventLoopName()
+    {
+        if (static::$eventLoopClass) {
+            return static::$eventLoopClass;
+        }
+
+        if (!class_exists('\Swoole\Event', false)) {
+            unset(static::$_availableEventLoops['swoole']);
+        }
+        
+        $loop_name = '';
+        foreach (static::$_availableEventLoops as $name=>$class) {
+            if (extension_loaded($name)) {
+                $loop_name = $name;
+                break;
+            }
+        }
+
+        if ($loop_name) {
+            if (interface_exists('\React\EventLoop\LoopInterface')) {
+                switch ($loop_name) {
+                    case 'libevent':
+                        static::$eventLoopClass = '\Workerman\Events\React\ExtLibEventLoop';
+                        break;
+                    case 'event':
+                        static::$eventLoopClass = '\Workerman\Events\React\ExtEventLoop';
+                        break;
+                    default :
+                        static::$eventLoopClass = '\Workerman\Events\React\StreamSelectLoop';
+                        break;
+                }
+            } else {
+                static::$eventLoopClass = static::$_availableEventLoops[$loop_name];
+            }
+        } else {
+            static::$eventLoopClass = interface_exists('\React\EventLoop\LoopInterface')? '\Workerman\Events\React\StreamSelectLoop':'\Workerman\Events\Select';
+        }
+        return static::$eventLoopClass;
+    }
+
+    /**
+     * Get all pids of worker processes.
+     *
+     * @return array
+     */
+    protected static function getAllWorkerPids()
+    {
+        $pid_array = array();
+        foreach (static::$_pidMap as $worker_pid_array) {
+            foreach ($worker_pid_array as $worker_pid) {
+                $pid_array[$worker_pid] = $worker_pid;
+            }
+        }
+        return $pid_array;
+    }
+
+    /**
+     * Fork some worker processes.
+     *
+     * @return void
+     */
+    protected static function forkWorkers()
+    {
+        if (static::$_OS === OS_TYPE_LINUX) {
+            static::forkWorkersForLinux();
+        } else {
+            static::forkWorkersForWindows();
+        }
+    }
+
+    /**
+     * Fork some worker processes.
+     *
+     * @return void
+     */
+    protected static function forkWorkersForLinux()
+    {
+
+        foreach (static::$_workers as $worker) {
+            if (static::$_status === static::STATUS_STARTING) {
+                if (empty($worker->name)) {
+                    $worker->name = $worker->getSocketName();
+                }
+                $worker_name_length = strlen($worker->name);
+                if (static::$_maxWorkerNameLength < $worker_name_length) {
+                    static::$_maxWorkerNameLength = $worker_name_length;
+                }
+            }
+
+            while (count(static::$_pidMap[$worker->workerId]) < $worker->count) {
+                static::forkOneWorkerForLinux($worker);
+            }
+        }
+    }
+
+    /**
+     * Fork some worker processes.
+     *
+     * @return void
+     */
+    protected static function forkWorkersForWindows()
+    {
+        $files = static::getStartFilesForWindows();
+        global $argv;
+        if(in_array('-q', $argv) || count($files) === 1)
+        {
+            if(count(static::$_workers) > 1)
+            {
+                static::safeEcho("@@@ Error: multi workers init in one php file are not support @@@\r\n");
+                static::safeEcho("@@@ Please visit http://wiki.workerman.net/Multi_woker_for_win @@@\r\n");
+            }
+            elseif(count(static::$_workers) <= 0)
+            {
+                exit("@@@no worker inited@@@\r\n\r\n");
+            }
+
+            reset(static::$_workers);
+            /** @var Worker $worker */
+            $worker = current(static::$_workers);
+
+            // Display UI.
+            static::safeEcho(str_pad($worker->name, 21) . str_pad($worker->getSocketName(), 36) . str_pad($worker->count, 10) . "[ok]\n");
+            $worker->listen();
+            $worker->run();
+            exit("@@@child exit@@@\r\n");
+        }
+        else
+        {
+            static::$globalEvent = new \Workerman\Events\Select();
+            Timer::init(static::$globalEvent);
+            foreach($files as $start_file)
+            {
+                static::forkOneWorkerForWindows($start_file);
+            }
+        }
+    }
+
+    /**
+     * Get start files for windows.
+     *
+     * @return array
+     */
+    public static function getStartFilesForWindows() {
+        global $argv;
+        $files = array();
+        foreach($argv as $file)
+        {
+            if(is_file($file))
+            {
+                $files[$file] = $file;
+            }
+        }
+        return $files;
+    }
+
+    /**
+     * Fork one worker process.
+     *
+     * @param string $start_file
+     */
+    public static function forkOneWorkerForWindows($start_file)
+    {
+        $start_file = realpath($start_file);
+        $std_file = sys_get_temp_dir() . '/'.str_replace(array('/', "\\", ':'), '_', $start_file).'.out.txt';
+
+        $descriptorspec = array(
+            0 => array('pipe', 'a'), // stdin
+            1 => array('file', $std_file, 'w'), // stdout
+            2 => array('file', $std_file, 'w') // stderr
+        );
+
+
+        $pipes       = array();
+        $process     = proc_open("php \"$start_file\" -q", $descriptorspec, $pipes);
+        $std_handler = fopen($std_file, 'a+');
+        stream_set_blocking($std_handler, 0);
+
+        if (empty(static::$globalEvent)) {
+            static::$globalEvent = new Select();
+            Timer::init(static::$globalEvent);
+        }
+        $timer_id = Timer::add(0.1, function()use($std_handler)
+        {
+            Worker::safeEcho(fread($std_handler, 65535));
+        });
+
+        // 保存子进程句柄
+        static::$_processForWindows[$start_file] = array($process, $start_file, $timer_id);
+    }
+
+    /**
+     * check worker status for windows.
+     * @return void
+     */
+    public static function checkWorkerStatusForWindows()
+    {
+        foreach(static::$_processForWindows as $process_data)
+        {
+            $process = $process_data[0];
+            $start_file = $process_data[1];
+            $timer_id = $process_data[2];
+            $status = proc_get_status($process);
+            if(isset($status['running']))
+            {
+                if(!$status['running'])
+                {
+                    static::safeEcho("process $start_file terminated and try to restart\n");
+                    Timer::del($timer_id);
+                    proc_close($process);
+                    static::forkOneWorkerForWindows($start_file);
+                }
+            }
+            else
+            {
+                static::safeEcho("proc_get_status fail\n");
+            }
+        }
+    }
+
+
+    /**
+     * Fork one worker process.
+     *
+     * @param \Workerman\Worker $worker
+     * @throws Exception
+     */
+    protected static function forkOneWorkerForLinux($worker)
+    {
+        // Get available worker id.
+        $id = static::getId($worker->workerId, 0);
+        if ($id === false) {
+            return;
+        }
+        $pid = pcntl_fork();
+        // For master process.
+        if ($pid > 0) {
+            static::$_pidMap[$worker->workerId][$pid] = $pid;
+            static::$_idMap[$worker->workerId][$id]   = $pid;
+        } // For child processes.
+        elseif (0 === $pid) {
+            srand();
+            mt_srand();
+            if ($worker->reusePort) {
+                $worker->listen();
+            }
+            if (static::$_status === static::STATUS_STARTING) {
+                static::resetStd();
+            }
+            static::$_pidMap  = array();
+            // Remove other listener.
+            foreach(static::$_workers as $key => $one_worker) {
+                if ($one_worker->workerId !== $worker->workerId) {
+                    $one_worker->unlisten();
+                    unset(static::$_workers[$key]);
+                }
+            }
+            Timer::delAll();
+            static::setProcessTitle('WorkerMan: worker process  ' . $worker->name . ' ' . $worker->getSocketName());
+            $worker->setUserAndGroup();
+            $worker->id = $id;
+            $worker->run();
+            $err = new Exception('event-loop exited');
+            static::log($err);
+            exit(250);
+        } else {
+            throw new Exception("forkOneWorker fail");
+        }
+    }
+
+    /**
+     * Get worker id.
+     *
+     * @param int $worker_id
+     * @param int $pid
+     *
+     * @return integer
+     */
+    protected static function getId($worker_id, $pid)
+    {
+        return array_search($pid, static::$_idMap[$worker_id]);
+    }
+
+    /**
+     * Set unix user and group for current process.
+     *
+     * @return void
+     */
+    public function setUserAndGroup()
+    {
+        // Get uid.
+        $user_info = posix_getpwnam($this->user);
+        if (!$user_info) {
+            static::log("Warning: User {$this->user} not exsits");
+            return;
+        }
+        $uid = $user_info['uid'];
+        // Get gid.
+        if ($this->group) {
+            $group_info = posix_getgrnam($this->group);
+            if (!$group_info) {
+                static::log("Warning: Group {$this->group} not exsits");
+                return;
+            }
+            $gid = $group_info['gid'];
+        } else {
+            $gid = $user_info['gid'];
+        }
+
+        // Set uid and gid.
+        if ($uid != posix_getuid() || $gid != posix_getgid()) {
+            if (!posix_setgid($gid) || !posix_initgroups($user_info['name'], $gid) || !posix_setuid($uid)) {
+                static::log("Warning: change gid or uid fail.");
+            }
+        }
+    }
+
+    /**
+     * Set process name.
+     *
+     * @param string $title
+     * @return void
+     */
+    protected static function setProcessTitle($title)
+    {
+        set_error_handler(function(){});
+        // >=php 5.5
+        if (function_exists('cli_set_process_title')) {
+            cli_set_process_title($title);
+        } // Need proctitle when php<=5.5 .
+        elseif (extension_loaded('proctitle') && function_exists('setproctitle')) {
+            setproctitle($title);
+        }
+        restore_error_handler();
+    }
+
+    /**
+     * Monitor all child processes.
+     *
+     * @return void
+     */
+    protected static function monitorWorkers()
+    {
+        if (static::$_OS === OS_TYPE_LINUX) {
+            static::monitorWorkersForLinux();
+        } else {
+            static::monitorWorkersForWindows();
+        }
+    }
+
+    /**
+     * Monitor all child processes.
+     *
+     * @return void
+     */
+    protected static function monitorWorkersForLinux()
+    {
+        static::$_status = static::STATUS_RUNNING;
+        while (1) {
+            // Calls signal handlers for pending signals.
+            pcntl_signal_dispatch();
+            // Suspends execution of the current process until a child has exited, or until a signal is delivered
+            $status = 0;
+            $pid    = pcntl_wait($status, WUNTRACED);
+            // Calls signal handlers for pending signals again.
+            pcntl_signal_dispatch();
+            // If a child has already exited.
+            if ($pid > 0) {
+                // Find out witch worker process exited.
+                foreach (static::$_pidMap as $worker_id => $worker_pid_array) {
+                    if (isset($worker_pid_array[$pid])) {
+                        $worker = static::$_workers[$worker_id];
+                        // Exit status.
+                        if ($status !== 0) {
+                            static::log("worker[" . $worker->name . ":$pid] exit with status $status");
+                        }
+
+                        // For Statistics.
+                        if (!isset(static::$_globalStatistics['worker_exit_info'][$worker_id][$status])) {
+                            static::$_globalStatistics['worker_exit_info'][$worker_id][$status] = 0;
+                        }
+                        static::$_globalStatistics['worker_exit_info'][$worker_id][$status]++;
+
+                        // Clear process data.
+                        unset(static::$_pidMap[$worker_id][$pid]);
+
+                        // Mark id is available.
+                        $id                              = static::getId($worker_id, $pid);
+                        static::$_idMap[$worker_id][$id] = 0;
+
+                        break;
+                    }
+                }
+                // Is still running state then fork a new worker process.
+                if (static::$_status !== static::STATUS_SHUTDOWN) {
+                    static::forkWorkers();
+                    // If reloading continue.
+                    if (isset(static::$_pidsToRestart[$pid])) {
+                        unset(static::$_pidsToRestart[$pid]);
+                        static::reload();
+                    }
+                }
+            }
+
+            // If shutdown state and all child processes exited then master process exit.
+            if (static::$_status === static::STATUS_SHUTDOWN && !static::getAllWorkerPids()) {
+                static::exitAndClearAll();
+            }
+        }
+    }
+
+    /**
+     * Monitor all child processes.
+     *
+     * @return void
+     */
+    protected static function monitorWorkersForWindows()
+    {
+        Timer::add(1, "\\Workerman\\Worker::checkWorkerStatusForWindows");
+
+        static::$globalEvent->loop();
+    }
+
+    /**
+     * Exit current process.
+     *
+     * @return void
+     */
+    protected static function exitAndClearAll()
+    {
+        foreach (static::$_workers as $worker) {
+            $socket_name = $worker->getSocketName();
+            if ($worker->transport === 'unix' && $socket_name) {
+                list(, $address) = explode(':', $socket_name, 2);
+                @unlink($address);
+            }
+        }
+        @unlink(static::$pidFile);
+        static::log("Workerman[" . basename(static::$_startFile) . "] has been stopped");
+        if (static::$onMasterStop) {
+            call_user_func(static::$onMasterStop);
+        }
+        exit(0);
+    }
+
+    /**
+     * Execute reload.
+     *
+     * @return void
+     */
+    protected static function reload()
+    {
+        // For master process.
+        if (static::$_masterPid === posix_getpid()) {
+            // Set reloading state.
+            if (static::$_status !== static::STATUS_RELOADING && static::$_status !== static::STATUS_SHUTDOWN) {
+                static::log("Workerman[" . basename(static::$_startFile) . "] reloading");
+                static::$_status = static::STATUS_RELOADING;
+                // Try to emit onMasterReload callback.
+                if (static::$onMasterReload) {
+                    try {
+                        call_user_func(static::$onMasterReload);
+                    } catch (\Exception $e) {
+                        static::log($e);
+                        exit(250);
+                    } catch (\Error $e) {
+                        static::log($e);
+                        exit(250);
+                    }
+                    static::initId();
+                }
+            }
+
+            if (static::$_gracefulStop) {
+                $sig = SIGQUIT;
+            } else {
+                $sig = SIGUSR1;
+            }
+
+            // Send reload signal to all child processes.
+            $reloadable_pid_array = array();
+            foreach (static::$_pidMap as $worker_id => $worker_pid_array) {
+                $worker = static::$_workers[$worker_id];
+                if ($worker->reloadable) {
+                    foreach ($worker_pid_array as $pid) {
+                        $reloadable_pid_array[$pid] = $pid;
+                    }
+                } else {
+                    foreach ($worker_pid_array as $pid) {
+                        // Send reload signal to a worker process which reloadable is false.
+                        posix_kill($pid, $sig);
+                    }
+                }
+            }
+
+            // Get all pids that are waiting reload.
+            static::$_pidsToRestart = array_intersect(static::$_pidsToRestart, $reloadable_pid_array);
+
+            // Reload complete.
+            if (empty(static::$_pidsToRestart)) {
+                if (static::$_status !== static::STATUS_SHUTDOWN) {
+                    static::$_status = static::STATUS_RUNNING;
+                }
+                return;
+            }
+            // Continue reload.
+            $one_worker_pid = current(static::$_pidsToRestart);
+            // Send reload signal to a worker process.
+            posix_kill($one_worker_pid, $sig);
+            // If the process does not exit after static::KILL_WORKER_TIMER_TIME seconds try to kill it.
+            if(!static::$_gracefulStop){
+                Timer::add(static::KILL_WORKER_TIMER_TIME, 'posix_kill', array($one_worker_pid, SIGKILL), false);
+            }
+        } // For child processes.
+        else {
+            reset(static::$_workers);
+            $worker = current(static::$_workers);
+            // Try to emit onWorkerReload callback.
+            if ($worker->onWorkerReload) {
+                try {
+                    call_user_func($worker->onWorkerReload, $worker);
+                } catch (\Exception $e) {
+                    static::log($e);
+                    exit(250);
+                } catch (\Error $e) {
+                    static::log($e);
+                    exit(250);
+                }
+            }
+
+            if ($worker->reloadable) {
+                static::stopAll();
+            }
+        }
+    }
+
+    /**
+     * Stop.
+     *
+     * @return void
+     */
+    public static function stopAll()
+    {
+        static::$_status = static::STATUS_SHUTDOWN;
+        // For master process.
+        if (static::$_masterPid === posix_getpid()) {
+            static::log("Workerman[" . basename(static::$_startFile) . "] stopping ...");
+            $worker_pid_array = static::getAllWorkerPids();
+            // Send stop signal to all child processes.
+            if (static::$_gracefulStop) {
+                $sig = SIGTERM;
+            } else {
+                $sig = SIGINT;
+            }
+            foreach ($worker_pid_array as $worker_pid) {
+                posix_kill($worker_pid, $sig);
+                if(!static::$_gracefulStop){
+                    Timer::add(static::KILL_WORKER_TIMER_TIME, 'posix_kill', array($worker_pid, SIGKILL), false);
+                }
+            }
+            Timer::add(1, "\\Workerman\\Worker::checkIfChildRunning");
+            // Remove statistics file.
+            if (is_file(static::$_statisticsFile)) {
+                @unlink(static::$_statisticsFile);
+            }
+        } // For child processes.
+        else {
+            // Execute exit.
+            foreach (static::$_workers as $worker) {
+                if(!$worker->stopping){
+                    $worker->stop();
+                    $worker->stopping = true;
+                }
+            }
+            if (!static::$_gracefulStop || ConnectionInterface::$statistics['connection_count'] <= 0) {
+                static::$_workers = array();
+                if (static::$globalEvent) {
+                    static::$globalEvent->destroy();
+                }
+                exit(0);
+            }
+        }
+    }
+
+    /**
+     * check if child processes is really running
+     */
+    public static function checkIfChildRunning()
+    {
+        foreach (static::$_pidMap as $worker_id => $worker_pid_array) {
+            foreach ($worker_pid_array as $pid => $worker_pid) {
+                if (!posix_kill($pid, 0)) {
+                    unset(static::$_pidMap[$worker_id][$pid]);
+                }
+            }
+        }
+    }
+
+    /**
+     * Get process status.
+     *
+     * @return number
+     */
+    public static function getStatus()
+    {
+        return static::$_status;
+    }
+
+    /**
+     * If stop gracefully.
+     *
+     * @return boolean
+     */
+    public static function getGracefulStop()
+    {
+        return static::$_gracefulStop;
+    }
+
+    /**
+     * Write statistics data to disk.
+     *
+     * @return void
+     */
+    protected static function writeStatisticsToStatusFile()
+    {
+        // For master process.
+        if (static::$_masterPid === posix_getpid()) {
+            $all_worker_info = array();
+            foreach(static::$_pidMap as $worker_id => $pid_array) {
+                /** @var /Workerman/Worker $worker */
+                $worker = static::$_workers[$worker_id];
+                foreach($pid_array as $pid) {
+                    $all_worker_info[$pid] = array('name' => $worker->name, 'listen' => $worker->getSocketName());
+                }
+            }
+
+            file_put_contents(static::$_statisticsFile, json_encode($all_worker_info)."\n", FILE_APPEND);
+            $loadavg = function_exists('sys_getloadavg') ? array_map('round', sys_getloadavg(), array(2)) : array('-', '-', '-');
+            file_put_contents(static::$_statisticsFile,
+                "----------------------------------------------GLOBAL STATUS----------------------------------------------------\n", FILE_APPEND);
+            file_put_contents(static::$_statisticsFile,
+                'Workerman version:' . static::VERSION . "          PHP version:" . PHP_VERSION . "\n", FILE_APPEND);
+            file_put_contents(static::$_statisticsFile, 'start time:' . date('Y-m-d H:i:s',
+                    static::$_globalStatistics['start_timestamp']) . '   run ' . floor((time() - static::$_globalStatistics['start_timestamp']) / (24 * 60 * 60)) . ' days ' . floor(((time() - static::$_globalStatistics['start_timestamp']) % (24 * 60 * 60)) / (60 * 60)) . " hours   \n",
+                FILE_APPEND);
+            $load_str = 'load average: ' . implode(", ", $loadavg);
+            file_put_contents(static::$_statisticsFile,
+                str_pad($load_str, 33) . 'event-loop:' . static::getEventLoopName() . "\n", FILE_APPEND);
+            file_put_contents(static::$_statisticsFile,
+                count(static::$_pidMap) . ' workers       ' . count(static::getAllWorkerPids()) . " processes\n",
+                FILE_APPEND);
+            file_put_contents(static::$_statisticsFile,
+                str_pad('worker_name', static::$_maxWorkerNameLength) . " exit_status      exit_count\n", FILE_APPEND);
+            foreach (static::$_pidMap as $worker_id => $worker_pid_array) {
+                $worker = static::$_workers[$worker_id];
+                if (isset(static::$_globalStatistics['worker_exit_info'][$worker_id])) {
+                    foreach (static::$_globalStatistics['worker_exit_info'][$worker_id] as $worker_exit_status => $worker_exit_count) {
+                        file_put_contents(static::$_statisticsFile,
+                            str_pad($worker->name, static::$_maxWorkerNameLength) . " " . str_pad($worker_exit_status,
+                                16) . " $worker_exit_count\n", FILE_APPEND);
+                    }
+                } else {
+                    file_put_contents(static::$_statisticsFile,
+                        str_pad($worker->name, static::$_maxWorkerNameLength) . " " . str_pad(0, 16) . " 0\n",
+                        FILE_APPEND);
+                }
+            }
+            file_put_contents(static::$_statisticsFile,
+                "----------------------------------------------PROCESS STATUS---------------------------------------------------\n",
+                FILE_APPEND);
+            file_put_contents(static::$_statisticsFile,
+                "pid\tmemory  " . str_pad('listening', static::$_maxSocketNameLength) . " " . str_pad('worker_name',
+                    static::$_maxWorkerNameLength) . " connections " . str_pad('send_fail', 9) . " "
+                . str_pad('timers', 8) . str_pad('total_request', 13) ." qps    status\n", FILE_APPEND);
+
+            chmod(static::$_statisticsFile, 0722);
+
+            foreach (static::getAllWorkerPids() as $worker_pid) {
+                posix_kill($worker_pid, SIGUSR2);
+            }
+            return;
+        }
+
+        // For child processes.
+        reset(static::$_workers);
+        /** @var \Workerman\Worker $worker */
+        $worker            = current(static::$_workers);
+        $worker_status_str = posix_getpid() . "\t" . str_pad(round(memory_get_usage(true) / (1024 * 1024), 2) . "M", 7)
+            . " " . str_pad($worker->getSocketName(), static::$_maxSocketNameLength) . " "
+            . str_pad(($worker->name === $worker->getSocketName() ? 'none' : $worker->name), static::$_maxWorkerNameLength)
+            . " ";
+        $worker_status_str .= str_pad(ConnectionInterface::$statistics['connection_count'], 11)
+            . " " .  str_pad(ConnectionInterface::$statistics['send_fail'], 9)
+            . " " . str_pad(static::$globalEvent->getTimerCount(), 7)
+            . " " . str_pad(ConnectionInterface::$statistics['total_request'], 13) . "\n";
+        file_put_contents(static::$_statisticsFile, $worker_status_str, FILE_APPEND);
+    }
+
+    /**
+     * Write statistics data to disk.
+     *
+     * @return void
+     */
+    protected static function writeConnectionsStatisticsToStatusFile()
+    {
+        // For master process.
+        if (static::$_masterPid === posix_getpid()) {
+            file_put_contents(static::$_statisticsFile, "--------------------------------------------------------------------- WORKERMAN CONNECTION STATUS --------------------------------------------------------------------------------\n", FILE_APPEND);
+            file_put_contents(static::$_statisticsFile, "PID      Worker          CID       Trans   Protocol        ipv4   ipv6   Recv-Q       Send-Q       Bytes-R      Bytes-W       Status         Local Address          Foreign Address\n", FILE_APPEND);
+            chmod(static::$_statisticsFile, 0722);
+            foreach (static::getAllWorkerPids() as $worker_pid) {
+                posix_kill($worker_pid, SIGIO);
+            }
+            return;
+        }
+
+        // For child processes.
+        $bytes_format = function($bytes)
+        {
+            if($bytes > 1024*1024*1024*1024) {
+                return round($bytes/(1024*1024*1024*1024), 1)."TB";
+            }
+            if($bytes > 1024*1024*1024) {
+                return round($bytes/(1024*1024*1024), 1)."GB";
+            }
+            if($bytes > 1024*1024) {
+                return round($bytes/(1024*1024), 1)."MB";
+            }
+            if($bytes > 1024) {
+                return round($bytes/(1024), 1)."KB";
+            }
+            return $bytes."B";
+        };
+
+        $pid = posix_getpid();
+        $str = '';
+        reset(static::$_workers);
+        $current_worker = current(static::$_workers);
+        $default_worker_name = $current_worker->name;
+
+        /** @var \Workerman\Worker $worker */
+        foreach(TcpConnection::$connections as $connection) {
+            /** @var \Workerman\Connection\TcpConnection $connection */
+            $transport      = $connection->transport;
+            $ipv4           = $connection->isIpV4() ? ' 1' : ' 0';
+            $ipv6           = $connection->isIpV6() ? ' 1' : ' 0';
+            $recv_q         = $bytes_format($connection->getRecvBufferQueueSize());
+            $send_q         = $bytes_format($connection->getSendBufferQueueSize());
+            $local_address  = trim($connection->getLocalAddress());
+            $remote_address = trim($connection->getRemoteAddress());
+            $state          = $connection->getStatus(false);
+            $bytes_read     = $bytes_format($connection->bytesRead);
+            $bytes_written  = $bytes_format($connection->bytesWritten);
+            $id             = $connection->id;
+            $protocol       = $connection->protocol ? $connection->protocol : $connection->transport;
+            $pos            = strrpos($protocol, '\\');
+            if ($pos) {
+                $protocol = substr($protocol, $pos+1);
+            }
+            if (strlen($protocol) > 15) {
+                $protocol = substr($protocol, 0, 13) . '..';
+            }
+            $worker_name = isset($connection->worker) ? $connection->worker->name : $default_worker_name;
+            if (strlen($worker_name) > 14) {
+                $worker_name = substr($worker_name, 0, 12) . '..';
+            }
+            $str .= str_pad($pid, 9) . str_pad($worker_name, 16) .  str_pad($id, 10) . str_pad($transport, 8)
+                . str_pad($protocol, 16) . str_pad($ipv4, 7) . str_pad($ipv6, 7) . str_pad($recv_q, 13)
+                . str_pad($send_q, 13) . str_pad($bytes_read, 13) . str_pad($bytes_written, 13) . ' '
+                . str_pad($state, 14) . ' ' . str_pad($local_address, 22) . ' ' . str_pad($remote_address, 22) ."\n";
+        }
+        if ($str) {
+            file_put_contents(static::$_statisticsFile, $str, FILE_APPEND);
+        }
+    }
+
+    /**
+     * Check errors when current process exited.
+     *
+     * @return void
+     */
+    public static function checkErrors()
+    {
+        if (static::STATUS_SHUTDOWN != static::$_status) {
+            $error_msg = static::$_OS === OS_TYPE_LINUX ? 'Worker['. posix_getpid() .'] process terminated' : 'Worker process terminated';
+            $errors    = error_get_last();
+            if ($errors && ($errors['type'] === E_ERROR ||
+                    $errors['type'] === E_PARSE ||
+                    $errors['type'] === E_CORE_ERROR ||
+                    $errors['type'] === E_COMPILE_ERROR ||
+                    $errors['type'] === E_RECOVERABLE_ERROR)
+            ) {
+                $error_msg .= ' with ERROR: ' . static::getErrorType($errors['type']) . " \"{$errors['message']} in {$errors['file']} on line {$errors['line']}\"";
+            }
+            static::log($error_msg);
+        }
+    }
+
+    /**
+     * Get error message by error code.
+     *
+     * @param integer $type
+     * @return string
+     */
+    protected static function getErrorType($type)
+    {
+        switch ($type) {
+            case E_ERROR: // 1 //
+                return 'E_ERROR';
+            case E_WARNING: // 2 //
+                return 'E_WARNING';
+            case E_PARSE: // 4 //
+                return 'E_PARSE';
+            case E_NOTICE: // 8 //
+                return 'E_NOTICE';
+            case E_CORE_ERROR: // 16 //
+                return 'E_CORE_ERROR';
+            case E_CORE_WARNING: // 32 //
+                return 'E_CORE_WARNING';
+            case E_COMPILE_ERROR: // 64 //
+                return 'E_COMPILE_ERROR';
+            case E_COMPILE_WARNING: // 128 //
+                return 'E_COMPILE_WARNING';
+            case E_USER_ERROR: // 256 //
+                return 'E_USER_ERROR';
+            case E_USER_WARNING: // 512 //
+                return 'E_USER_WARNING';
+            case E_USER_NOTICE: // 1024 //
+                return 'E_USER_NOTICE';
+            case E_STRICT: // 2048 //
+                return 'E_STRICT';
+            case E_RECOVERABLE_ERROR: // 4096 //
+                return 'E_RECOVERABLE_ERROR';
+            case E_DEPRECATED: // 8192 //
+                return 'E_DEPRECATED';
+            case E_USER_DEPRECATED: // 16384 //
+                return 'E_USER_DEPRECATED';
+        }
+        return "";
+    }
+
+    /**
+     * Log.
+     *
+     * @param string $msg
+     * @return void
+     */
+    public static function log($msg)
+    {
+        $msg = $msg . "\n";
+        if (!static::$daemonize) {
+            static::safeEcho($msg);
+        }
+        file_put_contents((string)static::$logFile, date('Y-m-d H:i:s') . ' ' . 'pid:'
+            . (static::$_OS === OS_TYPE_LINUX ? posix_getpid() : 1) . ' ' . $msg, FILE_APPEND | LOCK_EX);
+    }
+
+    /**
+     * Safe Echo.
+     * @param $msg
+     * @param bool $decorated
+     * @return bool
+     */
+    public static function safeEcho($msg, $decorated = false)
+    {
+        $stream = static::outputStream();
+        if (!$stream) {
+            return false;
+        }
+        if (!$decorated) {
+            $line = $white = $green = $end = '';
+            if (static::$_outputDecorated) {
+                $line = "\033[1A\n\033[K";
+                $white = "\033[47;30m";
+                $green = "\033[32;40m";
+                $end = "\033[0m";
+            }
+            $msg = str_replace(array('<n>', '<w>', '<g>'), array($line, $white, $green), $msg);
+            $msg = str_replace(array('</n>', '</w>', '</g>'), $end, $msg);
+        } elseif (!static::$_outputDecorated) {
+            return false;
+        }
+        fwrite($stream, $msg);
+        fflush($stream);
+        return true;
+    }
+
+    /**
+     * @param null $stream
+     * @return bool|resource
+     */
+    private static function outputStream($stream = null)
+    {
+        if (!$stream) {
+            $stream = static::$_outputStream ? static::$_outputStream : STDOUT;
+        }
+        if (!$stream || !is_resource($stream) || 'stream' !== get_resource_type($stream)) {
+            return false;
+        }
+        $stat = fstat($stream);
+        if (($stat['mode'] & 0170000) === 0100000) {
+            // file
+            static::$_outputDecorated = false;
+        } else {
+            static::$_outputDecorated =
+                static::$_OS === OS_TYPE_LINUX &&
+                function_exists('posix_isatty') &&
+                posix_isatty($stream);
+        }
+        return static::$_outputStream = $stream;
+    }
+
+    /**
+     * Construct.
+     *
+     * @param string $socket_name
+     * @param array  $context_option
+     */
+    public function __construct($socket_name = '', $context_option = array())
+    {
+        // Save all worker instances.
+        $this->workerId                    = spl_object_hash($this);
+        static::$_workers[$this->workerId] = $this;
+        static::$_pidMap[$this->workerId]  = array();
+
+        // Get autoload root path.
+        $backtrace                = debug_backtrace();
+        $this->_autoloadRootPath = dirname($backtrace[0]['file']);
+
+        // Context for socket.
+        if ($socket_name) {
+            $this->_socketName = $socket_name;
+            if (!isset($context_option['socket']['backlog'])) {
+                $context_option['socket']['backlog'] = static::DEFAULT_BACKLOG;
+            }
+            $this->_context = stream_context_create($context_option);
+        }
+    }
+
+
+    /**
+     * Listen.
+     *
+     * @throws Exception
+     */
+    public function listen()
+    {
+        if (!$this->_socketName) {
+            return;
+        }
+
+        // Autoload.
+        Autoloader::setRootPath($this->_autoloadRootPath);
+
+        if (!$this->_mainSocket) {
+            // Get the application layer communication protocol and listening address.
+            list($scheme, $address) = explode(':', $this->_socketName, 2);
+            // Check application layer protocol class.
+            if (!isset(static::$_builtinTransports[$scheme])) {
+                $scheme         = ucfirst($scheme);
+                $this->protocol = substr($scheme,0,1)==='\\' ? $scheme : '\\Protocols\\' . $scheme;
+                if (!class_exists($this->protocol)) {
+                    $this->protocol = "\\Workerman\\Protocols\\$scheme";
+                    if (!class_exists($this->protocol)) {
+                        throw new Exception("class \\Protocols\\$scheme not exist");
+                    }
+                }
+
+                if (!isset(static::$_builtinTransports[$this->transport])) {
+                    throw new \Exception('Bad worker->transport ' . var_export($this->transport, true));
+                }
+            } else {
+                $this->transport = $scheme;
+            }
+
+            $local_socket = static::$_builtinTransports[$this->transport] . ":" . $address;
+
+            // Flag.
+            $flags = $this->transport === 'udp' ? STREAM_SERVER_BIND : STREAM_SERVER_BIND | STREAM_SERVER_LISTEN;
+            $errno = 0;
+            $errmsg = '';
+            // SO_REUSEPORT.
+            if ($this->reusePort) {
+                stream_context_set_option($this->_context, 'socket', 'so_reuseport', 1);
+            }
+
+            // Create an Internet or Unix domain server socket.
+            $this->_mainSocket = stream_socket_server($local_socket, $errno, $errmsg, $flags, $this->_context);
+            if (!$this->_mainSocket) {
+                throw new Exception($errmsg);
+            }
+
+            if ($this->transport === 'ssl') {
+                stream_socket_enable_crypto($this->_mainSocket, false);
+            } elseif ($this->transport === 'unix') {
+                $socketFile = substr($address, 2);
+                if ($this->user) {
+                    chown($socketFile, $this->user);
+                }
+                if ($this->group) {
+                    chgrp($socketFile, $this->group);
+                }
+            }
+
+            // Try to open keepalive for tcp and disable Nagle algorithm.
+            if (function_exists('socket_import_stream') && static::$_builtinTransports[$this->transport] === 'tcp') {
+                set_error_handler(function(){});
+                $socket = socket_import_stream($this->_mainSocket);
+                socket_set_option($socket, SOL_SOCKET, SO_KEEPALIVE, 1);
+                socket_set_option($socket, SOL_TCP, TCP_NODELAY, 1);
+                restore_error_handler();
+            }
+
+            // Non blocking.
+            stream_set_blocking($this->_mainSocket, 0);
+        }
+
+        $this->resumeAccept();
+    }
+
+    /**
+     * Unlisten.
+     *
+     * @return void
+     */
+    public function unlisten() {
+        $this->pauseAccept();
+        if ($this->_mainSocket) {
+            set_error_handler(function(){});
+            fclose($this->_mainSocket);
+            restore_error_handler();
+            $this->_mainSocket = null;
+        }
+    }
+
+    /**
+     * Pause accept new connections.
+     *
+     * @return void
+     */
+    public function pauseAccept()
+    {
+        if (static::$globalEvent && false === $this->_pauseAccept && $this->_mainSocket) {
+            static::$globalEvent->del($this->_mainSocket, EventInterface::EV_READ);
+            $this->_pauseAccept = true;
+        }
+    }
+
+    /**
+     * Resume accept new connections.
+     *
+     * @return void
+     */
+    public function resumeAccept()
+    {
+        // Register a listener to be notified when server socket is ready to read.
+        if (static::$globalEvent && true === $this->_pauseAccept && $this->_mainSocket) {
+            if ($this->transport !== 'udp') {
+                static::$globalEvent->add($this->_mainSocket, EventInterface::EV_READ, array($this, 'acceptConnection'));
+            } else {
+                static::$globalEvent->add($this->_mainSocket, EventInterface::EV_READ, array($this, 'acceptUdpConnection'));
+            }
+            $this->_pauseAccept = false;
+        }
+    }
+
+    /**
+     * Get socket name.
+     *
+     * @return string
+     */
+    public function getSocketName()
+    {
+        return $this->_socketName ? lcfirst($this->_socketName) : 'none';
+    }
+
+    /**
+     * Run worker instance.
+     *
+     * @return void
+     */
+    public function run()
+    {
+        //Update process state.
+        static::$_status = static::STATUS_RUNNING;
+
+        // Register shutdown function for checking errors.
+        register_shutdown_function(array("\\Workerman\\Worker", 'checkErrors'));
+
+        // Set autoload root path.
+        Autoloader::setRootPath($this->_autoloadRootPath);
+
+        // Create a global event loop.
+        if (!static::$globalEvent) {
+            $event_loop_class = static::getEventLoopName();
+            static::$globalEvent = new $event_loop_class;
+            $this->resumeAccept();
+        }
+
+        // Reinstall signal.
+        static::reinstallSignal();
+
+        // Init Timer.
+        Timer::init(static::$globalEvent);
+
+        // Set an empty onMessage callback.
+        if (empty($this->onMessage)) {
+            $this->onMessage = function () {};
+        }
+
+        restore_error_handler();
+        
+        // Try to emit onWorkerStart callback.
+        if ($this->onWorkerStart) {
+            try {
+                call_user_func($this->onWorkerStart, $this);
+            } catch (\Exception $e) {
+                static::log($e);
+                // Avoid rapid infinite loop exit.
+                sleep(1);
+                exit(250);
+            } catch (\Error $e) {
+                static::log($e);
+                // Avoid rapid infinite loop exit.
+                sleep(1);
+                exit(250);
+            }
+        }
+
+        // Main loop.
+        static::$globalEvent->loop();
+    }
+
+    /**
+     * Stop current worker instance.
+     *
+     * @return void
+     */
+    public function stop()
+    {
+        // Try to emit onWorkerStop callback.
+        if ($this->onWorkerStop) {
+            try {
+                call_user_func($this->onWorkerStop, $this);
+            } catch (\Exception $e) {
+                static::log($e);
+                exit(250);
+            } catch (\Error $e) {
+                static::log($e);
+                exit(250);
+            }
+        }
+        // Remove listener for server socket.
+        $this->unlisten();
+        // Close all connections for the worker.
+        if (!static::$_gracefulStop) {
+            foreach ($this->connections as $connection) {
+                $connection->close();
+            }
+        }
+        // Clear callback.
+        $this->onMessage = $this->onClose = $this->onError = $this->onBufferDrain = $this->onBufferFull = null;
+    }
+
+    /**
+     * Accept a connection.
+     *
+     * @param resource $socket
+     * @return void
+     */
+    public function acceptConnection($socket)
+    {
+        // Accept a connection on server socket.
+        set_error_handler(function(){});
+        $new_socket = stream_socket_accept($socket, 0, $remote_address);
+        restore_error_handler();
+
+        // Thundering herd.
+        if (!$new_socket) {
+            return;
+        }
+
+        // TcpConnection.
+        $connection                         = new TcpConnection($new_socket, $remote_address);
+        $this->connections[$connection->id] = $connection;
+        $connection->worker                 = $this;
+        $connection->protocol               = $this->protocol;
+        $connection->transport              = $this->transport;
+        $connection->onMessage              = $this->onMessage;
+        $connection->onClose                = $this->onClose;
+        $connection->onError                = $this->onError;
+        $connection->onBufferDrain          = $this->onBufferDrain;
+        $connection->onBufferFull           = $this->onBufferFull;
+
+        // Try to emit onConnect callback.
+        if ($this->onConnect) {
+            try {
+                call_user_func($this->onConnect, $connection);
+            } catch (\Exception $e) {
+                static::log($e);
+                exit(250);
+            } catch (\Error $e) {
+                static::log($e);
+                exit(250);
+            }
+        }
+    }
+
+    /**
+     * For udp package.
+     *
+     * @param resource $socket
+     * @return bool
+     */
+    public function acceptUdpConnection($socket)
+    {
+        set_error_handler(function(){});
+        $recv_buffer = stream_socket_recvfrom($socket, static::MAX_UDP_PACKAGE_SIZE, 0, $remote_address);
+        restore_error_handler();
+        if (false === $recv_buffer || empty($remote_address)) {
+            return false;
+        }
+        // UdpConnection.
+        $connection           = new UdpConnection($socket, $remote_address);
+        $connection->protocol = $this->protocol;
+        if ($this->onMessage) {
+            try {
+                if ($this->protocol !== null) {
+                    /** @var \Workerman\Protocols\ProtocolInterface $parser */
+                    $parser      = $this->protocol;
+                    if(method_exists($parser,'input')){
+                        while($recv_buffer !== ''){
+                            $len = $parser::input($recv_buffer, $connection);
+                            if($len == 0)
+                                return true;
+                            $package = substr($recv_buffer,0,$len);
+                            $recv_buffer = substr($recv_buffer,$len);
+                            $data = $parser::decode($package,$connection);
+                            if ($data === false)
+                                continue;
+                            call_user_func($this->onMessage, $connection, $data);
+                        }
+                    }else{
+                        $data = $parser::decode($recv_buffer, $connection);
+                        // Discard bad packets.
+                        if ($data === false)
+                            return true;
+                        call_user_func($this->onMessage, $connection, $data);
+                    }
+                }else{
+                    call_user_func($this->onMessage, $connection, $recv_buffer);
+                }
+                ConnectionInterface::$statistics['total_request']++;
+            } catch (\Exception $e) {
+                static::log($e);
+                exit(250);
+            } catch (\Error $e) {
+                static::log($e);
+                exit(250);
+            }
+        }
+        return true;
+    }
+}
diff --git a/vendor/workerman/workerman/composer.json b/vendor/workerman/workerman/composer.json
new file mode 100755
index 0000000..95c3097
--- /dev/null
+++ b/vendor/workerman/workerman/composer.json
@@ -0,0 +1,38 @@
+{
+    "name": "workerman/workerman",
+    "type": "library",
+    "keywords": [
+        "event-loop",
+        "asynchronous"
+    ],
+    "homepage": "http://www.workerman.net",
+    "license": "MIT",
+    "description": "An asynchronous event driven PHP framework for easily building fast, scalable network applications.",
+    "authors": [
+        {
+            "name": "walkor",
+            "email": "walkor@workerman.net",
+            "homepage": "http://www.workerman.net",
+            "role": "Developer"
+        }
+    ],
+    "support": {
+        "email": "walkor@workerman.net",
+        "issues": "https://github.com/walkor/workerman/issues",
+        "forum": "http://wenda.workerman.net/",
+        "wiki": "http://doc3.workerman.net/index.html",
+        "source": "https://github.com/walkor/workerman"
+    },
+    "require": {
+        "php": ">=5.3"
+    },
+    "suggest": {
+        "ext-event": "For better performance. "
+    },
+    "autoload": {
+        "psr-4": {
+            "Workerman\\": "./"
+        }
+    },
+    "minimum-stability": "dev"
+}
diff --git a/vendor/xaboy/form-builder/LICENSE b/vendor/xaboy/form-builder/LICENSE
new file mode 100755
index 0000000..bd7924d
--- /dev/null
+++ b/vendor/xaboy/form-builder/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 xaboy
+
+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.
diff --git a/vendor/xaboy/form-builder/README.md b/vendor/xaboy/form-builder/README.md
new file mode 100755
index 0000000..b6ffa96
--- /dev/null
+++ b/vendor/xaboy/form-builder/README.md
@@ -0,0 +1,105 @@
+<p align="center">
+    <a href="https://github.com/xaboy/form-builder">
+        <img width="200" src="https://camo.githubusercontent.com/39b61b302b187171ad49bc0a9305f9e79787e71a/687474703a2f2f66696c652e6c6f746b6b2e636f6d2f666f726d2d6275696c6465722e706e67">
+    </a>
+</p>
+<h1 align="center">form-builder</h1>
+<p align="center">
+    <img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="MIT" />
+  <a href="https://github.com/xaboy">
+    <img src="https://img.shields.io/badge/Author-xaboy-blue.svg" alt="xaboy" />
+  </a>
+  <a href="https://packagist.org/packages/xaboy/form-builder">
+    <img src="https://img.shields.io/packagist/v/xaboy/form-builder.svg" alt="version" />
+  </a>
+  <a href="https://packagist.org/packages/xaboy/form-builder">
+    <img src="https://img.shields.io/packagist/php-v/xaboy/form-builder.svg" alt="php version" />
+  </a>
+</p>
+
+<p align="center">
+PHP表单生成器,快速生成现代化的form表单。包含复选框、单选框、输入框、下拉选择框等元素以及省市区三级联动、时间选择、日期选择、颜色选择、树型、文件/图片上传等功能。
+</p>
+
+![demo1](https://raw.githubusercontent.com/xaboy/form-create/dev/images/demo-live3.gif)
+![demo2](https://github.com/xaboy/form-create/raw/dev/images/demo-group.gif?raw=true)
+
+## 文档
+
+[文档](http://php.form-create.com)
+
+## 环境需求
+
+>  - PHP >= 5.4
+
+## 支持 UI
+
+>  - IView
+>  - ElementUI
+
+## 功能介绍
+
+>  - 内置17种常用的表单组件
+>  - 支持表单验证
+>  - 支持生成任何 Vue 组件
+>  - 支持栅格布局
+>  - 支持注解
+>  - 可以配合 [form-create](https://github.com/xaboy/form-create) 生成更复杂的表单
+
+## 内置组件
+
+>  - hidden
+>  - input
+>  - inputNumber
+>  - checkbox
+>  - radio
+>  - switch
+>  - select
+>  - autoComplete
+>  - cascader
+>  - colorPicker
+>  - datePicker
+>  - timePicker
+>  - rate
+>  - slider
+>  - upload
+>  - tree
+>  - frame
+
+## 安装
+
+使用 [composer](http://getcomposer.org/):
+
+```shell
+$ composer require xaboy/form-builder:~2.0
+```
+
+## DEMO
+下载项目
+
+```shell
+git clone https://github.com/xaboy/form-builder.git
+```
+开启服务
+
+```shell
+cd form-builder
+php -S 127.0.0.1:8112
+```
+查看 Demo
+
+- elementUI : [127.0.0.1:8112/demo/elm.php](127.0.0.1:8112/demo/elm.php)
+- iview : [127.0.0.1:8112/demo/iview.php](127.0.0.1:8112/demo/iview.php)
+
+## 演示项目
+[开源的高品质微信商城](http://github.crmeb.net/u/xaboy)
+
+演示地址: [http://demo25.crmeb.net](http://demo25.crmeb.net) 账号:demo 密码:crmeb.com
+
+## 使用建议
+1. 建议将静态资源加载方式从 CDN 加载修改为自己本地资源或自己信任的 CDN
+2. 建议根据自己的业务逻辑重写默认的表单生成页 默认表单生成页
+
+
+## 组件生成效果
+![https://raw.githubusercontent.com/xaboy/form-builder/2.0/images/components.png](https://raw.githubusercontent.com/xaboy/form-builder/master/images/components.png)
diff --git a/vendor/xaboy/form-builder/composer.json b/vendor/xaboy/form-builder/composer.json
new file mode 100755
index 0000000..7958808
--- /dev/null
+++ b/vendor/xaboy/form-builder/composer.json
@@ -0,0 +1,36 @@
+{
+  "name": "xaboy/form-builder",
+  "description": "PHP表单生成器,快速生成现代化的form表单。包含复选框、单选框、输入框、下拉选择框等元素以及,省市区三级联动,时间选择,日期选择,颜色选择,文件/图片上传等功能。",
+  "license": "MIT",
+  "keywords": [
+    "form",
+    "form-create",
+    "form-generator",
+    "dynamic-form",
+    "element-ui",
+    "iview"
+  ],
+  "homepage": "https://github.com/xaboy/form-builder",
+  "authors": [
+    {
+      "name": "xaboy",
+      "email": "xaboy2005@qq.com"
+    }
+  ],
+  "autoload": {
+    "psr-4": {
+      "FormBuilder\\": "./src/"
+    }
+  },
+  "support": {
+    "issues": "https://github.com/xaboy/form-builder/issues",
+    "source": "https://github.com/xaboy/form-builder"
+  },
+  "require": {
+    "php": ">=5.4.0",
+    "ext-json": "*",
+    "symfony/http-foundation": "~2.6|~2.7|~2.8|~3.0|~4.0",
+    "doctrine/annotations": "1.2.7"
+  },
+  "minimum-stability": "stable"
+}
diff --git a/vendor/xaboy/form-builder/demo/demo.php b/vendor/xaboy/form-builder/demo/demo.php
new file mode 100755
index 0000000..5f631ec
--- /dev/null
+++ b/vendor/xaboy/form-builder/demo/demo.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace App;
+
+use  FormBuilder\Factory\Elm;
+use FormBuilder\Form\IviewForm;
+
+require '../vendor/autoload.php';
+
+
+$action = '/save.php';
+$method = 'POST';
+
+$input = Elm::input('goods_name', '商品名称')->required();
+$textarea = Elm::textarea('goods_info', '商品简介');
+$switch = Elm::switches('is_open', '是否开启')->activeText('开启')->inactiveText('关闭');
+
+//创建表单
+$form = (new IviewForm($action))->setMethod($method);
+
+//添加组件
+$form->setRule([$input, $textarea]);
+$form->append($switch);
+
+$form->formData([
+    'goods_name' => 'goods_name123',
+    'asdf' => 'asdfafd',
+    'is_open' => '0'
+])->setValue('goods_info', "asdf\r\nadfa");
+
+//生成表单页面
+$formHtml = $form->view();
+
+echo $formHtml;
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/demo/elm.php b/vendor/xaboy/form-builder/demo/elm.php
new file mode 100755
index 0000000..f7e1321
--- /dev/null
+++ b/vendor/xaboy/form-builder/demo/elm.php
@@ -0,0 +1,162 @@
+<?php
+
+namespace App;
+
+
+require '../vendor/autoload.php';
+
+use FormBuilder\Annotation\Col;
+use FormBuilder\Annotation\Emit;
+use FormBuilder\Annotation\Group;
+use FormBuilder\Annotation\Validate\Required;
+use FormBuilder\Annotation\Validate\Min;
+use FormBuilder\Annotation\Validate\Range;
+use FormBuilder\Factory\Elm;
+use FormBuilder\Handle\ElmFormHandle;
+use FormBuilder\UI\Elm\Components\Rate;
+use FormBuilder\UI\Iview\Components\DatePicker;
+
+
+class GoodsForm extends ElmFormHandle
+{
+    protected $action = 'save.php';
+    protected $title = '测试 Handle';
+    protected $fieldTitles = [
+        'start_time' => '开启时间',
+        'star' => '点赞'
+    ];
+
+    protected $scene = 'get';
+
+    protected function getScene()
+    {
+//        $this->except = ['goods_name'];
+    }
+
+    /**
+     * @Col(6)
+     * @return \FormBuilder\UI\Elm\Components\Input
+     */
+    public function goods_name_field()
+    {
+        return Elm::input('goods_name', '商品名称')->required();
+    }
+
+    /**
+     * @Required()
+     * @Group()
+     * @Col(8)
+     * @Range({10,1000},message = "最少输入10个字")
+     * @return \FormBuilder\UI\Elm\Components\Input
+     */
+    public function goods_info_field()
+    {
+        return Elm::textarea('goods_info', '商品简介');
+    }
+
+    /**
+     * @Group()
+     * @Col(8)
+     * @Emit({"change","click"})
+     * @return \FormBuilder\UI\Elm\Components\Switches
+     */
+    public function is_open_field()
+    {
+        return Elm::switches('is_open', '是否开启');
+    }
+
+    /**
+     * @Group(2)
+     * @Col(12)
+     * @return \FormBuilder\UI\Elm\Components\Frame
+     */
+    public function frame_field()
+    {
+        return Elm::frameFile('as', 'asd', 'afsdfasdf');
+    }
+
+    /**
+     * @Group(2)
+     * @Col(12)
+     * @return \FormBuilder\UI\Elm\Components\Upload
+     */
+    public function test_field()
+    {
+        return Elm::uploadFiles('aaa', 'aaa', 'bbb', [1])->required();
+    }
+
+    /**
+     * @return \FormBuilder\UI\Elm\Components\Hidden
+     */
+    public function id_field()
+    {
+        return Elm::hidden('1', '1');
+    }
+
+    /**
+     * @Required("请输入 testRow")
+     * @return array
+     */
+    public function row_field()
+    {
+//        return [
+//            'type' => 'row',
+//            'children' =>
+//                [
+        return [
+            'type' => 'input',
+            'field' => 'row',
+            'title' => 'test Row',
+            'value' => '123',
+            'col' => [
+                'span' => 12
+            ]
+        ];
+//        ,
+//                Elm::input('row2', 'row2', 'asdf')->col(12)
+//            ],
+//            'native' => true
+//        ];
+    }
+
+    /**
+     * 通过依赖注入方式生成组件
+     *
+     * @param DatePicker $date
+     * @return DatePicker
+     */
+    public function start_time_field(DatePicker $date)
+    {
+        return $date->required()->info('asdfasdfasdfsf');
+    }
+
+    public function starField(Rate $rate)
+    {
+        return $rate;
+    }
+
+    protected function getFormConfig()
+    {
+        $config = Elm::config();
+        $config->createResetBtn()->show(true);
+
+        return $config;
+    }
+
+    protected function getFormData()
+    {
+        return [
+            'goods_name' => 'goods_name123',
+            'asdf' => 'asdfafd',
+            'is_open' => '0',
+            'goods_info' => "asdf\r\nadfa",
+            'star' => 0,
+            'row' => 'adsfasdfasd'
+        ];
+    }
+}
+
+$formHtml = (new GoodsForm())->view();
+//$formHtml = (new GoodsForm())->form()->view();
+
+echo $formHtml;
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/demo/iview.php b/vendor/xaboy/form-builder/demo/iview.php
new file mode 100755
index 0000000..4b57ea1
--- /dev/null
+++ b/vendor/xaboy/form-builder/demo/iview.php
@@ -0,0 +1,140 @@
+<?php
+
+namespace App;
+
+
+require '../vendor/autoload.php';
+
+use FormBuilder\Annotation\Col;
+use FormBuilder\Annotation\Group;
+use  FormBuilder\Factory\Iview;
+use FormBuilder\Handle\ElmFormHandle;
+use FormBuilder\Handle\IviewFormHandle;
+use FormBuilder\FormHandle;
+use FormBuilder\UI\Elm\Components\Checkbox;
+use FormBuilder\UI\Elm\Components\Rate;
+use FormBuilder\UI\Iview\Components\DatePicker;
+
+
+class GoodsForm extends IviewFormHandle
+{
+    protected $action = 'save.php';
+    protected $title = '测试 Handle';
+    protected $fieldTitles = [
+        'start_time' => '开启时间',
+        'star' => '点赞'
+    ];
+
+    protected $scene = 'get';
+
+    protected function getScene()
+    {
+//        $this->except = ['goods_name'];
+    }
+
+    /**
+     * @Col(12)
+     * @return \FormBuilder\UI\Iview\Components\Input
+     */
+    public function goods_name_field()
+    {
+        return Iview::input('goods_name', '商品名称')->required();
+    }
+
+    /**
+     * @Group(className="test")
+     * @Col(12)
+     * @return \FormBuilder\UI\Iview\Components\Input
+     */
+    public function goods_info_field()
+    {
+        return Iview::textarea('goods_info', '商品简介');
+    }
+
+    /**
+     * @Group()
+     * @Col(12)
+     * @return \FormBuilder\UI\Iview\Components\Switches
+     */
+    public function is_open_field()
+    {
+        return Iview::switches('is_open', '是否开启');
+    }
+
+    public function id_field()
+    {
+        return Iview::hidden('1', '1');
+    }
+
+    public function frame_field()
+    {
+        return Iview::frame('as', 'asd', 'afsdfasdf');
+    }
+
+    public function test_field()
+    {
+        return Iview::dateTime('aaa', 'aaa')->required();
+    }
+
+    public function row_field()
+    {
+        return [
+            'type' => 'row',
+            'children' => [
+                [
+                    'type' => 'input',
+                    'field' => 'row',
+                    'title' => 'test Row',
+                    'value' => '123',
+                    'col' => [
+                        'span' => 12
+                    ]
+                ],
+                Iview::input('row2', 'row2', 'asdf')->col(12)
+            ],
+            'native' => true
+        ];
+    }
+
+    /**
+     * 通过依赖注入方式生成组件
+     *
+     * @param DatePicker $date
+     * @return DatePicker
+     */
+    public function start_time_field(DatePicker $date)
+    {
+        return $date->required()->info('asdfasdfasdfsf');
+    }
+
+    public function starField(Rate $rate)
+    {
+        return $rate;
+    }
+
+    protected function getFormConfig()
+    {
+        $config = Iview::config();
+        $config->createResetBtn()->show(true);
+
+        return $config;
+    }
+
+    protected function getFormData()
+    {
+        return [
+            'goods_name' => 'goods_name123',
+            'asdf' => 'asdfafd',
+            'is_open' => '0',
+            'goods_info' => "asdf\r\nadfa",
+            'start_time' => '1999-11-11',
+            'star' => 0,
+            'row' => 'adsfasdfasd'
+        ];
+    }
+}
+
+$formHtml = (new GoodsForm())->view();
+//$formHtml = (new GoodsForm())->form()->view();
+
+echo $formHtml;
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/images/components.png b/vendor/xaboy/form-builder/images/components.png
new file mode 100755
index 0000000..2429f4e
--- /dev/null
+++ b/vendor/xaboy/form-builder/images/components.png
Binary files differ
diff --git a/vendor/xaboy/form-builder/images/demo02.jpg b/vendor/xaboy/form-builder/images/demo02.jpg
new file mode 100755
index 0000000..21fdcf7
--- /dev/null
+++ b/vendor/xaboy/form-builder/images/demo02.jpg
Binary files differ
diff --git a/vendor/xaboy/form-builder/images/demo03.jpg b/vendor/xaboy/form-builder/images/demo03.jpg
new file mode 100755
index 0000000..655bb6e
--- /dev/null
+++ b/vendor/xaboy/form-builder/images/demo03.jpg
Binary files differ
diff --git a/vendor/xaboy/form-builder/images/demo04.jpg b/vendor/xaboy/form-builder/images/demo04.jpg
new file mode 100755
index 0000000..fef1f46
--- /dev/null
+++ b/vendor/xaboy/form-builder/images/demo04.jpg
Binary files differ
diff --git a/vendor/xaboy/form-builder/src/Annotation/AnnotationReader.php b/vendor/xaboy/form-builder/src/Annotation/AnnotationReader.php
new file mode 100755
index 0000000..f62681b
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Annotation/AnnotationReader.php
@@ -0,0 +1,122 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Annotation;
+
+
+use Doctrine\Common\Annotations\AnnotationRegistry;
+use Doctrine\Common\Annotations\AnnotationReader as Render;
+use FormBuilder\Contract\AnnotationInterface;
+use FormBuilder\FormHandle;
+use FormBuilder\Util;
+
+class AnnotationReader
+{
+    protected static $isInit = false;
+
+    /**
+     * @var Render
+     */
+    protected $annotationReader;
+
+    /**
+     * @var FormHandle
+     */
+    protected $handle;
+
+    public function __construct(FormHandle $handle)
+    {
+        if (!self::$isInit) {
+            AnnotationRegistry::registerLoader('class_exists');
+            self::$isInit = true;
+        }
+
+        $this->annotationReader = new Render();
+        $this->handle = $handle;
+    }
+
+    public function getRender()
+    {
+        return $this->annotationReader;
+    }
+
+    /**
+     * @return array
+     * @throws \ReflectionException
+     */
+    public function render()
+    {
+        $reflectionClass = new \ReflectionClass($this->handle);
+        $methods = $reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC);
+        $rule = [];
+        $except = $this->handle->getExcept();
+        foreach ($methods as $method) {
+            $field = preg_replace('/^(.+)(Field|_field)$/', '$1', $method->name);
+            $value = null;
+            if ($field != $method->name && !in_array($field, $except)) {
+                $params = $method->getParameters();
+                if (isset($params[0]) && ($dep = $params[0]->getClass())) {
+                    if (in_array('FormBuilder\\Contract\\FormComponentInterface', $dep->getInterfaceNames())) {
+                        $componentClass = $dep->getName();
+                        $value = $method->invokeArgs($this->handle, [new $componentClass($field, $this->handle->getFieldTitle($field))]);
+                    }
+                }
+                if (is_null($value)) $value = $method->invoke($this->handle);
+                if (!is_null($value) && (($isArray = is_array($value)) || Util::isComponent($value))) {
+                    $rule[] = compact('value', 'method', 'isArray');
+                }
+            }
+        }
+        return $this->parse($rule);
+    }
+
+    /**
+     * @param $rules
+     * @return array
+     */
+    protected function parse($rules)
+    {
+        $formRule = [];
+        $groupList = [];
+        foreach ($rules as $rule) {
+            $annotations = $this->annotationReader->getMethodAnnotations($rule['method']);
+            $value = $rule['value'];
+            $group = null;
+            foreach ($annotations as $annotation) {
+                if (!$annotation instanceof AnnotationInterface) continue;
+                if ($annotation instanceof Group) {
+                    $group = $annotation;
+                } else {
+                    $value = $rule['isArray'] ? $annotation->parseRule($value) : $annotation->parseComponent($value);
+                }
+            }
+
+            if (!is_null($group)) {
+                if (!isset($groupList[$group->id])) {
+                    $groupList[$group->id] = $group;
+                    $formRule[] = $group;
+                }
+                $groupList[$group->id]->appendChildren($value);
+            } else {
+                $formRule[] = $value;
+            }
+        }
+
+        foreach ($formRule as $k => $v) {
+            if ($v instanceof Group) {
+                $formRule[$k] = $v->parse($this->handle->ui());
+            }
+        }
+
+        return $formRule;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Annotation/ClassName.php b/vendor/xaboy/form-builder/src/Annotation/ClassName.php
new file mode 100755
index 0000000..573852e
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Annotation/ClassName.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Annotation;
+
+use FormBuilder\Contract\AnnotationInterface;
+
+/**
+ * @Annotation
+ */
+final class ClassName implements AnnotationInterface
+{
+
+    /**
+     * @var string
+     */
+    public $className = '';
+
+    public function parseRule($rule)
+    {
+        $rule['className'] = $this->className;
+        return $rule;
+    }
+
+    public function parseComponent($component)
+    {
+        $component->className($this->className);
+        return $component;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Annotation/Col.php b/vendor/xaboy/form-builder/src/Annotation/Col.php
new file mode 100755
index 0000000..22926fb
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Annotation/Col.php
@@ -0,0 +1,45 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Annotation;
+
+use FormBuilder\Contract\AnnotationInterface;
+
+/**
+ * @Annotation
+ */
+final class Col implements AnnotationInterface
+{
+    public $props = 24;
+
+    protected function getCol()
+    {
+        if (is_integer($this->props))
+            return ['span' => $this->props];
+        else
+            return $this->props;
+    }
+
+    public function parseRule($rule)
+    {
+        $rule['col'] = $this->getCol();
+
+        return $rule;
+    }
+
+    public function parseComponent($component)
+    {
+        $component->col($this->getCol());
+
+        return $component;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Annotation/Emit.php b/vendor/xaboy/form-builder/src/Annotation/Emit.php
new file mode 100755
index 0000000..1a26cd9
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Annotation/Emit.php
@@ -0,0 +1,47 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Annotation;
+
+use FormBuilder\Contract\AnnotationInterface;
+
+/**
+ * @Annotation
+ */
+final class Emit implements AnnotationInterface
+{
+    /**
+     * @var array
+     */
+    public $emit = [];
+
+    /**
+     * @var string
+     */
+    public $prefix = '';
+
+
+    public function parseRule($rule)
+    {
+        $rule['emit'] = $this->emit;
+        if ($this->prefix)
+            $rule['emitPrefix'] = $this->prefix;
+
+        return $rule;
+    }
+
+    public function parseComponent($component)
+    {
+        $component->emit($this->emit)->emitPrefix($this->prefix);
+        return $component;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Annotation/Group.php b/vendor/xaboy/form-builder/src/Annotation/Group.php
new file mode 100755
index 0000000..bc88c1a
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Annotation/Group.php
@@ -0,0 +1,86 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Annotation;
+
+use FormBuilder\Contract\AnnotationInterface;
+
+/**
+ * @Annotation
+ */
+final class Group implements AnnotationInterface
+{
+    /**
+     * @var int
+     */
+    public $id = 1;
+
+    /**
+     * @var string
+     */
+    public $tag;
+
+    /**
+     * @var string
+     */
+    public $className = '';
+
+    public $span = 24;
+
+    protected $children = [];
+
+    public function parseRule($rule)
+    {
+        return $rule;
+    }
+
+    public function parseComponent($component)
+    {
+        return $component;
+    }
+
+    public function appendChildren($rule)
+    {
+        $this->children[] = $rule;
+    }
+
+    /**
+     * @param string $UI
+     * @return array
+     */
+    public function parse($UI)
+    {
+        $col = 'i-col';
+        $row = 'row';
+        if ($UI == 'elm') {
+            $row = 'el-row';
+            $col = 'el-col';
+        }
+
+        return [
+            'type' => $col,
+            'props' => [
+                'span' => $this->span
+            ],
+            'children' => [
+                [
+                    'type' => is_null($this->tag) ? $row : $this->tag,
+                    'children' => $this->children,
+                    'attrs' => [
+                        'class' => $this->className
+                    ]
+                ]
+            ],
+            'native' => true
+        ];
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Annotation/Info.php b/vendor/xaboy/form-builder/src/Annotation/Info.php
new file mode 100755
index 0000000..7f7ab8f
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Annotation/Info.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Annotation;
+
+use FormBuilder\Contract\AnnotationInterface;
+
+/**
+ * @Annotation
+ */
+final class Info implements AnnotationInterface
+{
+    /**
+     * @var string
+     */
+    public $info = '';
+
+    public function parseRule($rule)
+    {
+        $rule['info'] = $this->info;
+
+        return $rule;
+    }
+
+    public function parseComponent($component)
+    {
+        $component->info($this->info);
+
+        return $component;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Annotation/Validate/Enum.php b/vendor/xaboy/form-builder/src/Annotation/Validate/Enum.php
new file mode 100755
index 0000000..0609f57
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Annotation/Validate/Enum.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Annotation\Validate;
+
+
+/**
+ * @Annotation
+ */
+final class Enum extends ValidateAnnotation
+{
+    /**
+     * @Required
+     * @var array
+     */
+    public $value = [];
+
+
+    public function parseRule($rule)
+    {
+        $rule = $this->tidyValidate($rule);
+        $rule['validate'][] = ['enum' => $this->value, 'type' => $this->type, 'trigger' => $this->trigger, 'message' => $this->message];
+        return $rule;
+    }
+
+    public function parseComponent($component)
+    {
+        $component->appendValidate($component->createValidate()->enum($this->value)->message($this->message));
+        return $component;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Annotation/Validate/Len.php b/vendor/xaboy/form-builder/src/Annotation/Validate/Len.php
new file mode 100755
index 0000000..6a627c7
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Annotation/Validate/Len.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Annotation\Validate;
+
+
+/**
+ * @Annotation
+ */
+final class Len extends ValidateAnnotation
+{
+    /**
+     * @Required
+     */
+    public $value;
+
+
+    public function parseRule($rule)
+    {
+        $rule = $this->tidyValidate($rule);
+        $rule['validate'][] = ['len' => $this->value, 'type' => $this->type, 'trigger' => $this->trigger, 'message' => $this->message];
+        return $rule;
+    }
+
+    public function parseComponent($component)
+    {
+        $component->appendValidate($component->createValidate()->len($this->value)->message($this->message));
+        return $component;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Annotation/Validate/Max.php b/vendor/xaboy/form-builder/src/Annotation/Validate/Max.php
new file mode 100755
index 0000000..aead994
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Annotation/Validate/Max.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Annotation\Validate;
+
+
+/**
+ * @Annotation
+ */
+final class Max extends ValidateAnnotation
+{
+    /**
+     * @Required
+     */
+    public $value;
+
+
+    public function parseRule($rule)
+    {
+        $rule = $this->tidyValidate($rule);
+        $rule['validate'][] = ['max' => $this->value, 'type' => $this->type, 'trigger' => $this->trigger, 'message' => $this->message];
+        return $rule;
+    }
+
+    public function parseComponent($component)
+    {
+        $component->appendValidate($component->createValidate()->max($this->value)->message($this->message));
+        return $component;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Annotation/Validate/Min.php b/vendor/xaboy/form-builder/src/Annotation/Validate/Min.php
new file mode 100755
index 0000000..5518d62
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Annotation/Validate/Min.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Annotation\Validate;
+
+
+/**
+ * @Annotation
+ */
+final class Min extends ValidateAnnotation
+{
+    /**
+     * @Required
+     */
+    public $value;
+
+
+    public function parseRule($rule)
+    {
+        $rule = $this->tidyValidate($rule);
+        $rule['validate'][] = ['min' => $this->value, 'type' => $this->type, 'trigger' => $this->trigger, 'message' => $this->message];
+        return $rule;
+    }
+
+    public function parseComponent($component)
+    {
+        $component->appendValidate($component->createValidate()->min($this->value)->message($this->message));
+        return $component;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Annotation/Validate/Pattern.php b/vendor/xaboy/form-builder/src/Annotation/Validate/Pattern.php
new file mode 100755
index 0000000..f5a1dc2
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Annotation/Validate/Pattern.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Annotation\Validate;
+
+
+/**
+ * @Annotation
+ */
+final class Pattern extends ValidateAnnotation
+{
+    /**
+     * @Required
+     * @var string
+     */
+    public $value = '';
+
+
+    public function parseRule($rule)
+    {
+        $rule = $this->tidyValidate($rule);
+        $rule['validate'][] = ['pattern' => $this->value, 'type' => $this->type, 'trigger' => $this->trigger, 'message' => $this->message];
+        return $rule;
+    }
+
+    public function parseComponent($component)
+    {
+        $component->appendValidate($component->createValidate()->pattern($this->value)->message($this->message));
+        return $component;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Annotation/Validate/Range.php b/vendor/xaboy/form-builder/src/Annotation/Validate/Range.php
new file mode 100755
index 0000000..beba0e4
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Annotation/Validate/Range.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Annotation\Validate;
+
+
+/**
+ * @Annotation
+ */
+final class Range extends ValidateAnnotation
+{
+    /**
+     * @Required
+     * @var array
+     */
+    public $value = [];
+
+
+    public function parseRule($rule)
+    {
+        $rule = $this->tidyValidate($rule);
+        $rule['validate'][] = ['range' => $this->value, 'type' => $this->type, 'trigger' => $this->trigger, 'message' => $this->message];
+        return $rule;
+    }
+
+    public function parseComponent($component)
+    {
+        $component->appendValidate($component->createValidate()->range($this->value[0], $this->value[1])->message($this->message));
+        return $component;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Annotation/Validate/Required.php b/vendor/xaboy/form-builder/src/Annotation/Validate/Required.php
new file mode 100755
index 0000000..e809e7e
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Annotation/Validate/Required.php
@@ -0,0 +1,38 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Annotation\Validate;
+
+use FormBuilder\Contract\AnnotationInterface;
+
+/**
+ * @Annotation
+ * Class RuleAnnotation
+ * @package FormBuilder
+ */
+final class Required extends ValidateAnnotation
+{
+
+    public function parseRule($rule)
+    {
+        $rule = $this->tidyValidate($rule);
+        $rule['required'] = true;
+        $rule['validate'][] = ['required' => true, 'type' => $this->type, 'trigger' => $this->trigger, 'message' => $this->message];
+        return $rule;
+    }
+
+    public function parseComponent($component)
+    {
+        $component->required($this->message);
+        return $component;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Annotation/Validate/ValidateAnnotation.php b/vendor/xaboy/form-builder/src/Annotation/Validate/ValidateAnnotation.php
new file mode 100755
index 0000000..56e44bf
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Annotation/Validate/ValidateAnnotation.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Annotation\Validate;
+
+
+use FormBuilder\Contract\AnnotationInterface;
+
+abstract class ValidateAnnotation implements AnnotationInterface
+{
+
+    /**
+     * @var string
+     */
+    public $message;
+
+    /**
+     * @var string
+     */
+    public $type = 'string';
+
+    /**
+     * @var string
+     */
+    public $trigger = 'change';
+
+
+    public function tidyValidate($rule)
+    {
+        if (!isset($rule['validate']) || !is_array($rule['validate'])) {
+            $rule['validate'] = [];
+        }
+        return $rule;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Contract/AnnotationInterface.php b/vendor/xaboy/form-builder/src/Contract/AnnotationInterface.php
new file mode 100755
index 0000000..d98be87
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Contract/AnnotationInterface.php
@@ -0,0 +1,29 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Contract;
+
+
+interface AnnotationInterface
+{
+    /**
+     * @param array $rule
+     * @return array
+     */
+    public function parseRule($rule);
+
+    /**
+     * @param CustomComponentInterface $component
+     * @return CustomComponentInterface
+     */
+    public function parseComponent($component);
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Contract/BootstrapInterface.php b/vendor/xaboy/form-builder/src/Contract/BootstrapInterface.php
new file mode 100755
index 0000000..b198747
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Contract/BootstrapInterface.php
@@ -0,0 +1,27 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Contract;
+
+
+use FormBuilder\Form;
+
+interface BootstrapInterface
+{
+    /**
+     * 初始化
+     *
+     * @param Form $form
+     * @return void
+     */
+    public function init(Form $form);
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Contract/ColComponentInterface.php b/vendor/xaboy/form-builder/src/Contract/ColComponentInterface.php
new file mode 100755
index 0000000..210ccf0
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Contract/ColComponentInterface.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Contract;
+
+
+interface ColComponentInterface
+{
+    public function getCol();
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Contract/ConfigInterface.php b/vendor/xaboy/form-builder/src/Contract/ConfigInterface.php
new file mode 100755
index 0000000..d2e5eae
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Contract/ConfigInterface.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Contract;
+
+
+interface ConfigInterface
+{
+    public function info($type);
+
+    public function formStyle($formStyle);
+
+    public function row($row);
+
+    public function submitBtn($submitBtn);
+
+    public function resetBtn($resetBtn);
+
+    public function injectEvent($bool);
+
+    /**
+     * @param string $componentName
+     * @param array $config
+     * @return $this
+     */
+    public function componentGlobalConfig($componentName, array $config);
+
+    /**
+     * @param array $config
+     * @return $this
+     */
+    public function componentGlobalCommonConfig(array $config);
+
+    public function getConfig();
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Contract/CustomComponentInterface.php b/vendor/xaboy/form-builder/src/Contract/CustomComponentInterface.php
new file mode 100755
index 0000000..34e124b
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Contract/CustomComponentInterface.php
@@ -0,0 +1,25 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Contract;
+
+
+interface CustomComponentInterface
+{
+
+    /**
+     * 获取组件的生成规则
+     *
+     * @return array
+     */
+    public function build();
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Contract/FormComponentInterface.php b/vendor/xaboy/form-builder/src/Contract/FormComponentInterface.php
new file mode 100755
index 0000000..38a8a4b
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Contract/FormComponentInterface.php
@@ -0,0 +1,20 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Contract;
+
+
+interface FormComponentInterface extends CustomComponentInterface
+{
+    public function __construct($field, $title);
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Contract/FormHandleInterface.php b/vendor/xaboy/form-builder/src/Contract/FormHandleInterface.php
new file mode 100755
index 0000000..e56fc19
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Contract/FormHandleInterface.php
@@ -0,0 +1,11 @@
+<?php
+
+
+namespace FormBuilder\Contract;
+
+
+interface FormHandleInterface
+{
+    public function form();
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Contract/FormOptionsComponentInterface.php b/vendor/xaboy/form-builder/src/Contract/FormOptionsComponentInterface.php
new file mode 100755
index 0000000..49e847a
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Contract/FormOptionsComponentInterface.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ * @package crmeb_merchant
+ *
+ * @author xaboy
+ * @day 2020-05-13
+ *
+ * Copyright (c) http://crmeb.net
+ */
+
+namespace FormBuilder\Contract;
+
+
+interface FormOptionsComponentInterface extends FormComponentInterface
+{
+    public function setOptions(array $options);
+
+    public function getOptions();
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Contract/OptionComponentInterface.php b/vendor/xaboy/form-builder/src/Contract/OptionComponentInterface.php
new file mode 100755
index 0000000..d1bbe9f
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Contract/OptionComponentInterface.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Contract;
+
+
+interface OptionComponentInterface
+{
+    public function getOption();
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Contract/StyleInterface.php b/vendor/xaboy/form-builder/src/Contract/StyleInterface.php
new file mode 100755
index 0000000..203bda1
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Contract/StyleInterface.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Contract;
+
+
+interface StyleInterface
+{
+    public function getStyle();
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Contract/ValidateInterface.php b/vendor/xaboy/form-builder/src/Contract/ValidateInterface.php
new file mode 100755
index 0000000..9f08c49
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Contract/ValidateInterface.php
@@ -0,0 +1,25 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Contract;
+
+
+interface ValidateInterface
+{
+
+    /**
+     * 获取验证规则
+     *
+     * @return array
+     */
+    public function getValidate();
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Driver/CustomComponent.php b/vendor/xaboy/form-builder/src/Driver/CustomComponent.php
new file mode 100755
index 0000000..8812287
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Driver/CustomComponent.php
@@ -0,0 +1,121 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Driver;
+
+
+use FormBuilder\Contract\CustomComponentInterface;
+use FormBuilder\Rule\BaseRule;
+use FormBuilder\Rule\CallPropsRule;
+use FormBuilder\Rule\ChildrenRule;
+use FormBuilder\Rule\ControlRule;
+use FormBuilder\Rule\EmitRule;
+use FormBuilder\Rule\PropsRule;
+use FormBuilder\Rule\ValidateRule;
+
+/**
+ * 自定义组件
+ * Class CustomComponent
+ */
+class CustomComponent implements CustomComponentInterface, \JsonSerializable, \ArrayAccess
+{
+    use BaseRule;
+    use ChildrenRule;
+    use EmitRule;
+    use PropsRule;
+    use ValidateRule;
+    use CallPropsRule;
+    use ControlRule;
+
+    protected static $propsRule = [];
+
+    protected $defaultProps = [];
+
+    protected $appendRule = [];
+
+    /**
+     * CustomComponent constructor.
+     * @param null|string $type
+     */
+    public function __construct($type = null)
+    {
+        $this->setRuleType(is_null($type) ? $this->getComponentName() : $type)->props($this->defaultProps);
+    }
+
+    public function __toString()
+    {
+        return $this->toJson();
+    }
+
+    public function __invoke()
+    {
+        return $this->build();
+    }
+
+    public function toJson()
+    {
+        return json_encode($this->build());
+    }
+
+    protected function getComponentName()
+    {
+        return lcfirst(basename(str_replace('\\', '/', get_class($this))));
+    }
+
+    public function appendRule($name, $value)
+    {
+        $this->appendRule[$name] = $name == 'props' ? (object)$value : $value;
+        return $this;
+    }
+
+    public function getRule()
+    {
+        return array_merge(
+            $this->parseBaseRule(),
+            $this->parseEmitRule(),
+            $this->parsePropsRule(),
+            $this->parseValidateRule(),
+            $this->parseChildrenRule(),
+            $this->parseControlRule()
+        );
+    }
+
+    public function build()
+    {
+        return $this->appendRule + $this->getRule();
+    }
+
+    public function jsonSerialize()
+    {
+        return $this->build();
+    }
+
+    public function offsetExists($offset)
+    {
+        return isset($this->props[$offset]);
+    }
+
+    public function offsetGet($offset)
+    {
+        return $this->props[$offset];
+    }
+
+    public function offsetSet($offset, $value)
+    {
+        $this->props[$offset] = $value;
+    }
+
+    public function offsetUnset($offset)
+    {
+        unset($this->props[$offset]);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Driver/FormComponent.php b/vendor/xaboy/form-builder/src/Driver/FormComponent.php
new file mode 100755
index 0000000..7f40cb0
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Driver/FormComponent.php
@@ -0,0 +1,69 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Driver;
+
+
+use FormBuilder\Contract\FormComponentInterface;
+use FormBuilder\Contract\ValidateInterface;
+
+abstract class FormComponent extends CustomComponent implements FormComponentInterface
+{
+    protected $defaultValue = '';
+
+    protected $selectComponent = false;
+
+    /**
+     * FormComponentDriver constructor.
+     *
+     * @param string $field 字段名
+     * @param string $title 字段昵称
+     * @param mixed $value 字段值
+     */
+    public function __construct($field, $title, $value = null)
+    {
+        parent::__construct();
+        $this->field($field)->title($title)->value(is_null($value) ? $this->defaultValue : $value);
+        if (isset(static::$propsRule['placeholder']))
+            $this->placeholder($this->getPlaceHolder());
+        $this->init();
+    }
+
+    protected function init()
+    {
+    }
+
+    /**
+     * @return ValidateInterface
+     */
+    abstract public function createValidate();
+
+
+    /**
+     * @param null|string $message
+     * @return $this
+     */
+    public function required($message = null)
+    {
+        if (is_null($message)) $message = $this->getPlaceHolder();
+        $this->appendValidate($this->createValidate()->message($message)->required());
+        return $this;
+    }
+
+    /**
+     * @return string
+     */
+    protected function getPlaceHolder()
+    {
+        return ($this->selectComponent ? '请选择' : '请输入') . $this->title;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Driver/FormOptionsComponent.php b/vendor/xaboy/form-builder/src/Driver/FormOptionsComponent.php
new file mode 100755
index 0000000..94bcd91
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Driver/FormOptionsComponent.php
@@ -0,0 +1,29 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Driver;
+
+
+use FormBuilder\Contract\FormOptionsComponentInterface;
+use FormBuilder\Rule\OptionsRule;
+
+abstract class FormOptionsComponent extends FormComponent implements FormOptionsComponentInterface
+{
+    use OptionsRule;
+
+    public function getRule()
+    {
+        $rule = parent::getRule();
+
+        return array_merge($rule, $this->parseOptionsRule());
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Factory/Base.php b/vendor/xaboy/form-builder/src/Factory/Base.php
new file mode 100755
index 0000000..5604f90
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Factory/Base.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Factory;
+
+
+use FormBuilder\Driver\CustomComponent;
+
+abstract class Base
+{
+    /**
+     * 创建自定义组件
+     *
+     * @param string $type
+     * @return CustomComponent
+     */
+    public static function createComponent($type)
+    {
+        return new CustomComponent($type);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Factory/Elm.php b/vendor/xaboy/form-builder/src/Factory/Elm.php
new file mode 100755
index 0000000..848c59a
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Factory/Elm.php
@@ -0,0 +1,154 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Factory;
+
+use FormBuilder\Exception\FormBuilderException;
+use FormBuilder\Form;
+use FormBuilder\UI\Elm\Components\Button;
+use FormBuilder\UI\Elm\Components\Option;
+use FormBuilder\UI\Elm\Components\Popover;
+use FormBuilder\UI\Elm\Components\Tooltip;
+use FormBuilder\UI\Elm\Config;
+use FormBuilder\UI\Elm\Traits\CascaderFactoryTrait;
+use FormBuilder\UI\Elm\Traits\CheckBoxFactoryTrait;
+use FormBuilder\UI\Elm\Traits\ColorPickerFactoryTrait;
+use FormBuilder\UI\Elm\Traits\DatePickerFactoryTrait;
+use FormBuilder\UI\Elm\Traits\FrameFactoryTrait;
+use FormBuilder\UI\Elm\Traits\FormStyleFactoryTrait;
+use FormBuilder\UI\Elm\Traits\GroupFactoryTrait;
+use FormBuilder\UI\Elm\Traits\HiddenFactoryTrait;
+use FormBuilder\UI\Elm\Traits\InputFactoryTrait;
+use FormBuilder\UI\Elm\Traits\InputNumberFactoryTrait;
+use FormBuilder\UI\Elm\Traits\RadioFactoryTrait;
+use FormBuilder\UI\Elm\Traits\RateFactoryTrait;
+use FormBuilder\UI\Elm\Traits\SelectFactoryTrait;
+use FormBuilder\UI\Elm\Traits\SliderFactoryTrait;
+use FormBuilder\UI\Elm\Traits\SwitchesFactoryTrait;
+use FormBuilder\UI\Elm\Traits\TimePickerFactoryTrait;
+use FormBuilder\UI\Elm\Traits\TreeFactoryTrait;
+use FormBuilder\UI\Elm\Traits\UploadFactoryTrait;
+use FormBuilder\UI\Elm\Traits\ValidateFactoryTrait;
+
+abstract class Elm extends Base
+{
+    use CascaderFactoryTrait;
+    use CheckBoxFactoryTrait;
+    use ColorPickerFactoryTrait;
+    use DatePickerFactoryTrait;
+    use FrameFactoryTrait;
+    use HiddenFactoryTrait;
+    use InputNumberFactoryTrait;
+    use InputFactoryTrait;
+    use RadioFactoryTrait;
+    use RateFactoryTrait;
+    use SliderFactoryTrait;
+    use SelectFactoryTrait;
+    use FormStyleFactoryTrait;
+    use SwitchesFactoryTrait;
+    use TimePickerFactoryTrait;
+    use TreeFactoryTrait;
+    use UploadFactoryTrait;
+    use ValidateFactoryTrait;
+    use GroupFactoryTrait;
+
+    /**
+     * 获取选择类组件 option 类
+     *
+     * @param string|number $value
+     * @param string $label
+     * @param bool $disabled
+     * @return Option
+     */
+    public static function option($value, $label = '', $disabled = false)
+    {
+        return new Option($value, $label, $disabled);
+    }
+
+    /**
+     * @param array $rule
+     * @return \FormBuilder\UI\Elm\Style\FormStyle
+     * @author xaboy
+     * @day 2020/5/22
+     */
+    public static function formStyle(array $rule = [])
+    {
+        return self::style($rule);
+    }
+
+    /**
+     * 全局配置
+     *
+     * @param array $config
+     * @return Config
+     */
+    public static function config(array $config = [])
+    {
+        return new Config($config);
+    }
+
+    /**
+     * 组件提示消息配置 Popover
+     *
+     * @return Popover
+     */
+    public static function popover()
+    {
+        return new Popover();
+    }
+
+    /**
+     * 组件提示消息配置 Tooltip
+     *
+     * @return Tooltip
+     */
+    public static function tooltip()
+    {
+        return new Tooltip();
+    }
+
+    /**
+     * 按钮组件
+     *
+     * @return Button
+     */
+    public static function button()
+    {
+        return new Button();
+    }
+
+
+    /**
+     * 创建表单
+     *
+     * @param string $action
+     * @param array $rule
+     * @param array $config
+     * @return Form
+     * @throws FormBuilderException
+     */
+    public static function createForm($action = '', $rule = [], $config = [])
+    {
+        return Form::elm($action, $rule, $config);
+    }
+
+    /**
+     * 组件分组
+     *
+     * @param array $children
+     * @return \FormBuilder\Driver\CustomComponent
+     */
+    public static function item($children = [])
+    {
+        return self::createComponent('el-row')->children($children);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Factory/Iview.php b/vendor/xaboy/form-builder/src/Factory/Iview.php
new file mode 100755
index 0000000..44114bb
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Factory/Iview.php
@@ -0,0 +1,169 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Factory;
+
+use FormBuilder\Exception\FormBuilderException;
+use FormBuilder\Form;
+use FormBuilder\UI\Iview\Components\Button;
+use FormBuilder\UI\Iview\Components\Option;
+use FormBuilder\UI\Iview\Components\Poptip;
+use FormBuilder\UI\Iview\Components\Tooltip;
+use FormBuilder\UI\Iview\Config;
+use FormBuilder\UI\Iview\Traits\CascaderFactoryTrait;
+use FormBuilder\UI\Iview\Traits\CheckBoxFactoryTrait;
+use FormBuilder\UI\Iview\Traits\ColorPickerFactoryTrait;
+use FormBuilder\UI\Iview\Traits\DatePickerFactoryTrait;
+use FormBuilder\UI\Iview\Traits\FrameFactoryTrait;
+use FormBuilder\UI\Iview\Traits\FormStyleFactoryTrait;
+use FormBuilder\UI\Iview\Traits\GroupFactoryTrait;
+use FormBuilder\UI\Iview\Traits\HiddenFactoryTrait;
+use FormBuilder\UI\Iview\Traits\InputFactoryTrait;
+use FormBuilder\UI\Iview\Traits\InputNumberFactoryTrait;
+use FormBuilder\UI\Iview\Traits\RadioFactoryTrait;
+use FormBuilder\UI\Iview\Traits\RateFactoryTrait;
+use FormBuilder\UI\Iview\Traits\SelectFactoryTrait;
+use FormBuilder\UI\Iview\Traits\SliderFactoryTrait;
+use FormBuilder\UI\Iview\Traits\SwitchesFactoryTrait;
+use FormBuilder\UI\Iview\Traits\TimePickerFactoryTrait;
+use FormBuilder\UI\Iview\Traits\TreeFactoryTrait;
+use FormBuilder\UI\Iview\Traits\UploadFactoryTrait;
+use FormBuilder\UI\Iview\Traits\ValidateFactoryTrait;
+
+abstract class Iview extends Base
+{
+    use CascaderFactoryTrait;
+    use CheckBoxFactoryTrait;
+    use ColorPickerFactoryTrait;
+    use DatePickerFactoryTrait;
+    use FrameFactoryTrait;
+    use HiddenFactoryTrait;
+    use InputNumberFactoryTrait;
+    use InputFactoryTrait;
+    use RadioFactoryTrait;
+    use RateFactoryTrait;
+    use SliderFactoryTrait;
+    use SelectFactoryTrait;
+    use FormStyleFactoryTrait;
+    use SwitchesFactoryTrait;
+    use TimePickerFactoryTrait;
+    use TreeFactoryTrait;
+    use UploadFactoryTrait;
+    use ValidateFactoryTrait;
+    use GroupFactoryTrait;
+
+    /**
+     * 获取选择类组件 option 类
+     *
+     * @param string|number $value
+     * @param string $label
+     * @param bool $disabled
+     * @return Option
+     */
+    public static function option($value, $label = '', $disabled = false)
+    {
+        return new Option($value, $label, $disabled);
+    }
+
+    /**
+     * @param array $rule
+     * @return \FormBuilder\UI\Iview\Style\FormStyle
+     * @author xaboy
+     * @day 2020/5/22
+     */
+    public static function formStyle(array $rule = [])
+    {
+        return self::style($rule);
+    }
+
+    /**
+     * 全局配置
+     *
+     * @param array $config
+     * @return Config
+     */
+    public static function config(array $config = [])
+    {
+        return new Config($config);
+    }
+
+    /**
+     * 组件提示消息配置 Poptip
+     *
+     * @return Poptip
+     */
+    public static function poptip()
+    {
+        return new Poptip();
+    }
+
+    /**
+     * 组件提示消息配置 Tooltip
+     *
+     * @return Tooltip
+     */
+    public static function tooltip()
+    {
+        return new Tooltip();
+    }
+
+    /**
+     * 按钮组件
+     *
+     * @return Button
+     */
+    public static function button()
+    {
+        return new Button();
+    }
+
+
+    /**
+     * 创建表单
+     *
+     * @param string $action
+     * @param array $rule
+     * @param array $config
+     * @return Form
+     * @throws FormBuilderException
+     */
+    public static function createForm($action = '', $rule = [], $config = [])
+    {
+        return Form::iview($action, $rule, $config);
+    }
+
+
+    /**
+     * 创建表单 v4版本
+     *
+     * @param string $action
+     * @param array $rule
+     * @param array $config
+     * @return Form
+     * @throws FormBuilderException
+     */
+    public static function createFormV4($action = '', $rule = [], $config = [])
+    {
+        return Form::iview4($action, $rule, $config);
+    }
+
+    /**
+     * 组件分组
+     *
+     * @param array $children
+     * @return \FormBuilder\Driver\CustomComponent
+     */
+    public static function item($children = [])
+    {
+        return self::createComponent('row')->children($children);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Form.php b/vendor/xaboy/form-builder/src/Form.php
new file mode 100755
index 0000000..d4e7c79
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Form.php
@@ -0,0 +1,535 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder;
+
+
+use FormBuilder\Contract\BootstrapInterface;
+use FormBuilder\Contract\ConfigInterface;
+use FormBuilder\Exception\FormBuilderException;
+use FormBuilder\UI\Iview\Bootstrap as IViewBootstrap;
+use FormBuilder\UI\Elm\Bootstrap as ElmBootstrap;
+
+class Form
+{
+    protected $headers = [];
+
+    protected $formContentType = 'application/x-www-form-urlencoded';
+
+    /**
+     * @var BootstrapInterface
+     */
+    protected $ui;
+
+    /**
+     * @var array|ConfigInterface
+     */
+    protected $config;
+
+    protected $action;
+
+    protected $method = 'POST';
+
+    protected $title = 'FormBuilder';
+
+    protected $rule;
+
+    protected $formData = [];
+
+    protected $dependScript = [
+        '<script src="https://unpkg.com/jquery@3.3.1/dist/jquery.min.js"></script>',
+        '<script src="https://unpkg.com/vue@2.5.13/dist/vue.min.js"></script>',
+        '<script src="https://unpkg.com/@form-create/data@1.0.0/dist/province_city.js"></script>',
+        '<script src="https://unpkg.com/@form-create/data@1.0.0/dist/province_city_area.js"></script>'
+    ];
+
+    protected $template;
+
+    /**
+     * Form constructor.
+     * @param BootstrapInterface $ui
+     * @param string $action
+     * @param array $rule
+     * @param array $config
+     * @throws FormBuilderException
+     */
+    protected function __construct(BootstrapInterface $ui, $action = '', $rule = [], $config = [])
+    {
+        $this->action = $action;
+        $this->rule = $rule;
+        $this->config = $config;
+        $this->ui = $ui;
+        $ui->init($this);
+        $this->checkFieldUnique();
+        $this->template = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'Template' . DIRECTORY_SEPARATOR . 'form.php';
+    }
+
+    /**
+     * @return string
+     */
+    public function getFormContentType()
+    {
+        return $this->formContentType;
+    }
+
+    /**
+     * @param string $name
+     * @param string $value
+     * @return $this
+     */
+    public function setHeader($name, $value)
+    {
+        $this->headers[$name] = (string)$value;
+
+        return $this;
+    }
+
+    /**
+     * @param array $headers
+     * @return $this
+     */
+    public function headers(array $headers)
+    {
+        $this->headers = $headers;
+
+        return $this;
+    }
+
+    /**
+     * @param array $formData
+     * @return $this
+     */
+    public function formData(array $formData)
+    {
+        $this->formData = $formData;
+        return $this;
+    }
+
+    /**
+     * @param $field
+     * @param $value
+     * @return $this
+     */
+    public function setValue($field, $value)
+    {
+        $this->formData[$field] = $value;
+        return $this;
+    }
+
+    /**
+     * @return false|string
+     */
+    public function parseHeaders()
+    {
+        return json_encode((object)$this->headers);
+    }
+
+    /**
+     * @param $formContentType
+     * @return $this
+     */
+    public function setFormContentType($formContentType)
+    {
+        $this->formContentType = (string)$formContentType;
+
+        return $this;
+    }
+
+    public function setDependScript(array $dependScript)
+    {
+        $this->dependScript = $dependScript;
+
+        return $this;
+    }
+
+    /**
+     * @param string $title
+     * @return $this
+     */
+    public function setTitle($title)
+    {
+        $this->title = (string)$title;
+
+        return $this;
+    }
+
+    /**
+     * @param string $method
+     * @return $this
+     */
+    public function setMethod($method)
+    {
+        $this->method = (string)$method;
+
+        return $this;
+    }
+
+    /**
+     * @return string|null
+     */
+    public function getTitle()
+    {
+        return $this->title;
+    }
+
+    /**
+     * @return string|null
+     */
+    public function getMethod()
+    {
+        return $this->method;
+    }
+
+    /**
+     * @param array $rule
+     * @return $this
+     * @throws FormBuilderException
+     */
+    public function setRule(array $rule)
+    {
+        $this->rule = $rule;
+        $this->checkFieldUnique();
+        return $this;
+    }
+
+    /**
+     * @param array|ConfigInterface $config
+     * @return $this
+     */
+    public function setConfig($config)
+    {
+        if (is_array($config) || $config instanceof ConfigInterface)
+            $this->config = $config;
+        return $this;
+    }
+
+    /**
+     * 追加组件
+     *
+     * @param $component
+     * @return $this
+     * @throws FormBuilderException
+     */
+    public function append($component)
+    {
+        $this->rule[] = $component;
+        $this->checkFieldUnique();
+        return $this;
+    }
+
+    /**
+     * 开头插入组件
+     *
+     * @param $component
+     * @return $this
+     * @throws FormBuilderException
+     */
+    public function prepend($component)
+    {
+        array_unshift($this->rule, $component);
+        $this->checkFieldUnique();
+        return $this;
+    }
+
+    /**
+     * @param $action
+     * @return $this
+     */
+    public function setAction($action)
+    {
+        $this->action = (string)$action;
+        return $this;
+    }
+
+    /**
+     * @return string
+     */
+    public function getAction()
+    {
+        return $this->action;
+    }
+
+    /**
+     * 提交按钮显示状态
+     *
+     * @param $isShow
+     * @return $this
+     */
+    public function showSubmitBtn($isShow)
+    {
+        if ($this->config instanceof ConfigInterface)
+            $this->config->submitBtn(!!$isShow);
+        else
+            $this->config['submitBtn'] = !!$isShow;
+        return $this;
+    }
+
+    /**
+     * 重置按钮显示状态
+     *
+     * @param $isShow
+     * @return $this
+     */
+    public function showResetBtn($isShow)
+    {
+        if ($this->config instanceof ConfigInterface)
+            $this->config->resetBtn(!!$isShow);
+        else
+            $this->config['resetBtn'] = !!$isShow;
+        return $this;
+    }
+
+    /**
+     * 设置组件全局配置
+     * @param string $componentName
+     * @param array $config
+     * @return $this
+     */
+    public function componentGlobalConfig($componentName, array $config)
+    {
+        if ($this->config instanceof ConfigInterface)
+            $this->config->componentGlobalConfig($componentName, $config);
+        else {
+            if (!isset($this->config['global'])) $this->config['global'] = [];
+            $this->config['global'][$componentName] = $config;
+        }
+        return $this;
+    }
+
+    protected function parseFormComponent($rule)
+    {
+        if (Util::isComponent($rule)) {
+            $rule = $rule->build();
+        } else {
+            if (isset($rule['children']) && is_array($rule['children'])) {
+                foreach ($rule['children'] as $i => $child) {
+                    $rule['children'][$i] = $this->parseFormComponent($child);
+                }
+            }
+            if (isset($rule['control'])) {
+                foreach ($rule['control'] as $i => $child) {
+                    foreach ($child['rule'] as $k => $rule) {
+                        $child['rule'][$k] = Util::isComponent($rule) ? $rule->build() : $rule;
+                    }
+                    $rule['control'][$i] = $child;
+                }
+            }
+        }
+        return $rule;
+    }
+
+    public function getDependScript()
+    {
+        return $this->dependScript;
+    }
+
+    /**
+     * @param array $formData
+     * @param array $rule
+     * @return array
+     */
+    protected function deepSetFormData($formData, $rule)
+    {
+        if (!count($formData)) return $rule;
+        foreach ($rule as $k => $item) {
+            if (is_array($item)) {
+                if (isset($item['field']) && isset($formData[$item['field']])) {
+                    $item['value'] = $formData[$item['field']];
+                }
+                if (isset($item['children']) && is_array($item['children']) && count($item['children'])) {
+                    $item['children'] = $this->deepSetFormData($formData, $item['children']);
+                }
+                if (isset($item['control']) && count($item['control'])) {
+                    foreach ($item['control'] as $_k => $_rule) {
+                        $item['control'][$_k]['rule'] = $this->deepSetFormData($formData, $_rule['rule']);
+                    }
+                }
+            }
+            $rule[$k] = $item;
+        }
+
+        return $rule;
+    }
+
+    /**
+     * 获取表单生成规则
+     *
+     * @return array
+     */
+    public function formRule()
+    {
+        $rules = [];
+        foreach ($this->rule as $rule) {
+            $rules[] = $this->parseFormComponent($rule);
+        }
+        return $this->deepSetFormData($this->formData, $rules);
+    }
+
+    /**
+     * @return false|string
+     */
+    public function parseFormRule()
+    {
+        return json_encode($this->formRule());
+    }
+
+    /**
+     * @return false|string
+     */
+    public function parseFormConfig()
+    {
+        return json_encode((object)$this->formConfig());
+    }
+
+    /**
+     * @return string
+     */
+    public function parseDependScript()
+    {
+        return implode("\r\n", $this->dependScript);
+    }
+
+    /**
+     * 获取表单配置
+     *
+     * @return array
+     */
+    public function formConfig()
+    {
+        $config = $this->config;
+        if ($config instanceof ConfigInterface) return $config->getConfig();
+        foreach ($config as $k => $v) {
+            $config[$k] = $this->parseFormComponent($v);
+        }
+        return $config;
+    }
+
+
+    /**
+     * 获取表单创建的 js 代码
+     *
+     * @return false|string
+     */
+    public function formScript()
+    {
+        return $this->template(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'Template' . DIRECTORY_SEPARATOR . 'createScript.min.php');
+    }
+
+    /**
+     * 获取表单视图
+     *
+     * @return string
+     */
+    public function view()
+    {
+        return $this->template($this->template);
+    }
+
+    /**
+     * 自定义表单页面
+     *
+     * @param $templateDir
+     * @return false|string
+     */
+    public function template($templateDir)
+    {
+        ob_start();
+        $form = $this;
+        require $templateDir;
+        $html = ob_get_clean();
+        return $html;
+    }
+
+    /**
+     * 设置模板
+     *
+     * @param string $templateDir
+     * @return $this
+     */
+    public function setTemplate($templateDir)
+    {
+        $this->template = $templateDir;
+        return $this;
+    }
+
+    /**
+     * 检查field 是否重复
+     *
+     * @param null $rules
+     * @param array $fields
+     * @return array
+     * @throws FormBuilderException
+     */
+    protected function checkFieldUnique($rules = null, $fields = [])
+    {
+        if (is_null($rules)) $rules = $this->rule;
+
+        foreach ($rules as $rule) {
+            $rule = $this->parseFormComponent($rule);
+            $field = isset($rule['field']) ? $rule['field'] : null;
+
+            if (isset($rule['children']) && count($rule['children']))
+                $fields = $this->checkFieldUnique($rule['children'], $fields);
+
+            if (is_null($field) || $field === '')
+                continue;
+            else if (isset($fields[$field]))
+                throw new FormBuilderException('组件的 field 不能重复');
+            else
+                $fields[$field] = true;
+        }
+
+        return $fields;
+    }
+
+    /**
+     * Iview 版表单生成器
+     *
+     * @param string $action
+     * @param array $rule
+     * @param array|ConfigInterface $config
+     * @return Form
+     * @throws FormBuilderException
+     */
+    public static function iview($action = '', $rule = [], $config = [])
+    {
+        return new self(new IViewBootstrap(), $action, $rule, $config);
+    }
+
+    /**
+     * Iview v4 版表单生成器
+     *
+     * @param string $action
+     * @param array $rule
+     * @param array|ConfigInterface $config
+     * @return Form
+     * @throws FormBuilderException
+     */
+    public static function iview4($action = '', $rule = [], $config = [])
+    {
+        return new self(new IViewBootstrap(4), $action, $rule, $config);
+    }
+
+    /**
+     * element-ui 版表单生成器
+     *
+     * @param string $action
+     * @param array $rule
+     * @param array|ConfigInterface $config
+     * @return Form
+     * @throws FormBuilderException
+     */
+    public static function elm($action = '', $rule = [], $config = [])
+    {
+        return new self(new ElmBootstrap(), $action, $rule, $config);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Form/ElmForm.php b/vendor/xaboy/form-builder/src/Form/ElmForm.php
new file mode 100755
index 0000000..455bddd
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Form/ElmForm.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Form;
+
+
+use FormBuilder\UI\Elm\Bootstrap;
+
+class ElmForm extends \FormBuilder\Form
+{
+    public function __construct($action = '', $rule = [], $config = [])
+    {
+        parent::__construct(new Bootstrap(), $action, $rule, $config);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Form/IviewForm.php b/vendor/xaboy/form-builder/src/Form/IviewForm.php
new file mode 100755
index 0000000..04ae124
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Form/IviewForm.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Form;
+
+
+use FormBuilder\UI\Iview\Bootstrap;
+
+class IviewForm extends \FormBuilder\Form
+{
+    public function __construct($action = '', $rule = [], $config = [])
+    {
+        parent::__construct(new Bootstrap(), $action, $rule, $config);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/FormComponentDriver.php b/vendor/xaboy/form-builder/src/FormComponentDriver.php
new file mode 100755
index 0000000..e0c9289
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/FormComponentDriver.php
@@ -0,0 +1,267 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder;
+
+use FormBuilder\components\Col;
+use FormBuilder\components\Validate;
+use FormBuilder\interfaces\FormComponentInterFace;
+use FormBuilder\traits\component\CallPropsTrait;
+
+/**
+ * Class FormComponentDriver
+ *
+ * @package FormBuilder
+ */
+abstract class FormComponentDriver implements FormComponentInterFace
+{
+    use CallPropsTrait;
+    /**
+     * 字段名
+     *
+     * @var String
+     */
+    protected $field;
+
+    /**
+     * 字段昵称
+     *
+     * @var String
+     */
+    protected $title;
+
+    /**
+     * 组件名称
+     *
+     * @var String
+     */
+    protected $name;
+
+    /**
+     * 组件的规则
+     *
+     * @var array
+     */
+    protected $props = [];
+
+    /**
+     * info
+     *
+     * @var String
+     */
+    public $info;
+
+    /**
+     * 字段的值
+     *
+     * @var
+     */
+    protected $value = '';
+
+    /**
+     * 栅格规则
+     *
+     * @var array
+     */
+    protected $col = [];
+
+    /**
+     * 字段验证规则
+     *
+     * @var array
+     */
+    protected $validate = [];
+
+
+    protected $validateHandler = null;
+
+    /**
+     * 组件属性设置规则
+     *
+     * @var array
+     */
+    protected static $propsRule = [];
+
+    /**
+     * FormComponentDriver constructor.
+     *
+     * @param String $field 字段名
+     * @param String $title 字段昵称
+     * @param String $value 字段值
+     */
+    public function __construct($field, $title, $value = null)
+    {
+        $this->field = (string)$field;
+        $this->title = (string)$title;
+        static::value($value);
+        static::init();
+    }
+
+    /**
+     * 组件初始化
+     */
+    protected function init()
+    {
+
+    }
+
+    /**
+     * @param $span
+     * @return $this
+     */
+    public function col($span)
+    {
+        if ($span instanceof Col)
+            $this->col = $span->build();
+        else if (is_numeric($span))
+            $this->col['span'] = $span;
+        return $this;
+    }
+
+
+    /**
+     * 批量设置组件的规则
+     *
+     * @param array $props
+     * @return $this
+     */
+    public function setProps(array $props = [])
+    {
+        foreach ($props as $k => $v) {
+            $this->{$k}($v);
+        }
+        return $this;
+    }
+
+    /**
+     * 获取组件的规则
+     *
+     * @param $name
+     * @return mixed|null
+     */
+    public function getProps($name)
+    {
+        return isset($this->props[$name]) ? $this->props[$name] : null;
+    }
+
+    /**
+     * 设置组件的值
+     *
+     * @param        $value
+     * @return $this
+     */
+    public function value($value)
+    {
+        if (is_null($value)) $value = '';
+        $this->value = (string)$value;
+        return $this;
+    }
+
+    /**
+     * 设置组件的info
+     *
+     * @param string $info
+     * @return $this
+     */
+    public function info($info)
+    {
+        $this->info = (string)$info;
+        return $this;
+    }
+
+    /**
+     * 获取组件的值
+     *
+     * @return string
+     */
+    public function getValue()
+    {
+        return $this->value;
+    }
+
+    /**
+     * 获取组件的字段名
+     *
+     * @return String
+     */
+    public function getField()
+    {
+        return $this->field;
+    }
+
+    /**
+     * 设置组件的昵称
+     *
+     * @return String
+     */
+    public function getTitle()
+    {
+        return $this->title;
+    }
+
+    /**
+     * @param string|null $message
+     * @return $this
+     */
+    public function required($message = null)
+    {
+        $this->validate()->required($message ?: $this->getPlaceHolder());
+        $this->props['required'] = true;
+        return $this;
+    }
+
+    /**
+     * @param string $pre
+     * @return string
+     */
+    protected function getPlaceHolder($pre = '请选择')
+    {
+        return $pre . $this->title;
+    }
+
+    /**
+     * 添加验证规则
+     *
+     * @param array $validate
+     * @return $this
+     */
+    public function validateAs(array $validate)
+    {
+        $this->validate = array_merge($this->validate, $validate);
+        return $this;
+    }
+
+    /**
+     * @return Validate
+     */
+    abstract protected function getValidateHandler();
+
+    /**
+     * @param bool $clear
+     * @return Validate
+     */
+    public function validate($clear = false)
+    {
+        if ($clear || is_null($this->validateHandler))
+            $this->validateHandler = $this->getValidateHandler();
+
+        return $this->validateHandler;
+    }
+
+    public function validateFn($callback, $clear = false)
+    {
+        if (is_callable($callback)) $callback($this->validate($clear));
+        return $this;
+    }
+
+    public function setValidate(Validate $validate)
+    {
+        $this->validateHandler = $validate;
+        return $this;
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/FormHandle.php b/vendor/xaboy/form-builder/src/FormHandle.php
new file mode 100755
index 0000000..8346202
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/FormHandle.php
@@ -0,0 +1,153 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder;
+
+
+use FormBuilder\Annotation\AnnotationReader;
+use FormBuilder\Contract\ConfigInterface;
+use FormBuilder\Contract\FormHandleInterface;
+
+/**
+ * 表单生成类
+ *
+ * Class FormHandle
+ * @package FormBuilder
+ */
+abstract class FormHandle implements FormHandleInterface
+{
+    protected $action = '';
+
+    protected $method = 'POST';
+
+    protected $title;
+
+    protected $formContentType;
+
+    protected $headers = [];
+
+    protected $fieldTitles = [];
+
+    protected $except = [];
+
+    protected $scene;
+
+    /**
+     * 表单 UI
+     *
+     * @return mixed
+     */
+    abstract public function ui();
+
+    final public function getExcept()
+    {
+        return $this->except;
+    }
+
+    /**
+     * 获取表单数据
+     * @return array
+     */
+    protected function getFormData()
+    {
+        return [];
+    }
+
+    public function scene($scene = null)
+    {
+        if (!is_null($scene)) $this->scene = $scene;
+        return $this->scene;
+    }
+
+    /**
+     * 获取表单配置
+     *
+     * @return mixed|array|ConfigInterface
+     */
+    protected function getFormConfig()
+    {
+        return;
+    }
+
+    public function getFieldTitle($field)
+    {
+        return isset($this->fieldTitles[$field]) ? $this->fieldTitles[$field] : null;
+    }
+
+    /**
+     * 获取表单组件
+     *
+     * @return array
+     * @throws \ReflectionException
+     */
+    protected function getFormRule()
+    {
+        $render = new AnnotationReader($this);
+        return $render->render();
+    }
+
+    /**
+     * 创建表单
+     *
+     * @return Form
+     * @throws \ReflectionException
+     */
+    protected function createForm()
+    {
+        $ui = lcfirst($this->ui());
+        return call_user_func_array(['FormBuilder\\Form', $ui], $this->getParams());
+    }
+
+    /**
+     * @return array
+     * @throws \ReflectionException
+     */
+    protected function getParams()
+    {
+        $params = [$this->action, $this->getFormRule()];
+        $config = $this->getFormConfig();
+        if (is_array($config) || $config instanceof ConfigInterface)
+            $params[] = $config;
+
+        return $params;
+    }
+
+    /**
+     * 获取表单
+     *
+     * @return Form
+     * @throws \ReflectionException
+     */
+    public function form()
+    {
+        if ($this->scene && method_exists($this, $this->scene . 'Scene'))
+            $this->{$this->scene . 'Scene'}();
+
+        $form = $this->createForm()->setMethod($this->method);
+        if (!is_null($this->title)) $form->setTitle($this->title)->headers($this->headers);
+        $formData = $this->getFormData();
+        if (is_array($formData)) $form->formData($formData);
+        if ($this->formContentType) $form->setFormContentType($this->formContentType);
+        $config = $this->getFormConfig();
+        if ($config) $form->setConfig($config);
+        return $form;
+    }
+
+    /**
+     * @return string
+     * @throws \ReflectionException
+     */
+    public function view()
+    {
+        return $this->form()->view();
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Handle/ElmFormHandle.php b/vendor/xaboy/form-builder/src/Handle/ElmFormHandle.php
new file mode 100755
index 0000000..b7aebf7
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Handle/ElmFormHandle.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Handle;
+
+
+use FormBuilder\FormHandle;
+
+/**
+ * ElementUI 表单生成类
+ * Class ElmFormHandle
+ * @package FormBuilder\Factory
+ */
+abstract class ElmFormHandle extends FormHandle
+{
+
+    public function ui()
+    {
+        return 'elm';
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Handle/IviewFormHandle.php b/vendor/xaboy/form-builder/src/Handle/IviewFormHandle.php
new file mode 100755
index 0000000..5d43d50
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Handle/IviewFormHandle.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Handle;
+
+
+use FormBuilder\FormHandle;
+
+/**
+ * Iview 表单生成类
+ *
+ * Class IviewFormHandle
+ * @package FormBuilder\Factory
+ */
+abstract class IviewFormHandle extends FormHandle
+{
+
+    protected $version = 3;
+
+    public function ui()
+    {
+        return $this->version == 4 ? 'iview4' : 'iview';
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Helper.php b/vendor/xaboy/form-builder/src/Helper.php
new file mode 100755
index 0000000..450ff2c
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Helper.php
@@ -0,0 +1,64 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder;
+
+
+use FormBuilder\exception\FormBuilderException;
+
+class Helper
+{
+    public static function toType($var, $type = 'string')
+    {
+        if ($type === 'string') {
+            return (string)$var;
+        } else if ($type === 'array') {
+            return is_array($var) ? $var : [$var];
+        } else if ($type === 'boolean') {
+            return (bool)$var;
+        } else if ($type === 'float') {
+            return (float)$var;
+        } else if ($type === 'int') {
+            return (int)$var;
+        } else if ($type === 'null') {
+            return null;
+        }
+        return $var;
+    }
+
+    public static function getVarType($var)
+    {
+        if (is_array($var)) return 'array';
+        if (is_bool($var)) return 'boolean';
+        if (is_float($var)) return 'float';
+        if (is_int($var)) return 'integer';
+        if (is_null($var)) return 'null';
+        if (is_numeric($var)) return 'numeric';
+        if (is_object($var)) return 'object';
+        if (is_resource($var)) return 'resource';
+        if (is_string($var)) return 'string';
+        return "unknown type";
+    }
+
+
+    public static function verifyType($var, $verify, $title = '')
+    {
+        if (!is_array($verify)) $verify = [$verify];
+        if (in_array('numeric', $verify)) {
+            $verify[] = 'float';
+            $verify[] = 'integer';
+        }
+        $type = self::getVarType($var);
+        if (!in_array($type, $verify))
+            throw new FormBuilderException($title . '类型需为' . implode(',', $verify));
+    }
+
+    public static function getDate($date)
+    {
+        return is_numeric($date) ? $date * 1000 : $date;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Json.php b/vendor/xaboy/form-builder/src/Json.php
new file mode 100755
index 0000000..a999015
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Json.php
@@ -0,0 +1,38 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder;
+
+
+class Json
+{
+    protected static function result($code, $msg = 'ok', $data = [])
+    {
+        return json_encode(compact('code', 'msg', 'data'));
+    }
+
+    public static function succ($msg, $data = [])
+    {
+        return static::result(200, $msg, $data);
+    }
+
+    public static function fail($msg, $data = [])
+    {
+        return static::result(400, $msg, $data);
+    }
+
+    public static function uploadSucc($filePath, $msg = '上传成功', $data = [])
+    {
+        $data['filePath'] = $filePath;
+        return static::succ($msg, $data);
+    }
+
+    public static function uploadFail($msg = '上传失败', $data = [])
+    {
+        return static::fail($msg, $data);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Response.php b/vendor/xaboy/form-builder/src/Response.php
new file mode 100755
index 0000000..b82acb0
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Response.php
@@ -0,0 +1,107 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder;
+
+use \Symfony\Component\HttpFoundation\Response as HttpResponse;
+
+abstract class Response
+{
+    /**
+     * @param int $code
+     * @param string $msg
+     * @param null|array $data
+     * @return HttpResponse
+     */
+    protected static function createResponse($code, $msg = 'ok', $data = null)
+    {
+        $res = compact('code', 'msg');
+        if (!is_null($data)) $res['data'] = $data;
+
+        return new HttpResponse(json_encode($res));
+    }
+
+    /**
+     * 请求成功
+     *
+     * @param string $msg
+     * @param null|array $data
+     * @return HttpResponse
+     */
+    public static function succ($msg = 'ok', $data = null)
+    {
+        return self::createResponse(200, $msg, $data);
+    }
+
+    /**
+     * 请求失败
+     *
+     * @param string $msg
+     * @param null|array $data
+     * @return HttpResponse
+     */
+    public static function fail($msg = 'fail', $data = null)
+    {
+        return self::createResponse(400, $msg, $data);
+    }
+
+    /**
+     * 请求成功
+     *
+     * @param string $msg
+     * @param null|array $data
+     * @return HttpResponse
+     */
+    public static function success($msg = 'ok', $data = null)
+    {
+        return self::succ($msg, $data);
+    }
+
+    /**
+     * 图片/文件上传成功
+     *
+     * @param string $filePath
+     * @param string $msg
+     * @param array $data
+     * @return HttpResponse
+     */
+    public static function uploadSucc($filePath, $msg = '上传成功', array $data = [])
+    {
+        $data['filePath'] = $filePath;
+        return self::succ($msg, $data);
+    }
+
+    /**
+     * 图片/文件上传失败
+     *
+     * @param string $msg
+     * @param null|array $data
+     * @return HttpResponse
+     */
+    public static function uploadFail($msg = '上传失败', $data = null)
+    {
+        return self::fail($msg, $data);
+    }
+
+    /**
+     * 图片/文件上传成功
+     *
+     * @param string $filePath
+     * @param string $msg
+     * @param array $data
+     * @return HttpResponse
+     */
+    public static function uploadSuccess($filePath, $msg = '上传成功', array $data = [])
+    {
+        return self::uploadSucc($filePath, $msg, $data);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Rule/BaseRule.php b/vendor/xaboy/form-builder/src/Rule/BaseRule.php
new file mode 100755
index 0000000..cd198cf
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Rule/BaseRule.php
@@ -0,0 +1,323 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Rule;
+
+
+use FormBuilder\Contract\ColComponentInterface;
+
+trait BaseRule
+{
+
+    /**
+     * 组件类型
+     *
+     * @var string
+     */
+    protected $type;
+
+    /**
+     * 组件字段名
+     *
+     * @var string
+     */
+    protected $field;
+
+    /**
+     * 字段昵称
+     *
+     * @var string
+     */
+    protected $title;
+
+    /**
+     * 组件名称
+     *
+     * @var string
+     */
+    protected $name;
+
+    /**
+     * 组件的提示信息
+     *
+     * @var string
+     */
+    protected $info;
+
+    /**
+     * 组件 class
+     *
+     * @var string
+     */
+    protected $className;
+
+    /**
+     * 是否原样生成组件,不嵌套的FormItem中
+     *
+     * @var bool
+     */
+    protected $native;
+
+    /**
+     * 事件注入时的自定义数据
+     *
+     * @var mixed
+     */
+    protected $inject;
+
+    /**
+     * 组件布局规则
+     *
+     * @var ColComponentInterface|array
+     */
+    protected $col;
+
+    /**
+     * 组件的值
+     *
+     * @var mixed
+     */
+    protected $value = '';
+
+    /**
+     * 组件显示状态
+     *
+     * @var bool
+     */
+    protected $hidden;
+
+    /**
+     * 组件显示状态
+     *
+     * @var bool
+     */
+    protected $visibility;
+
+    /**
+     * 组件显示状态
+     *
+     * @param bool $hidden
+     * @return $this
+     */
+    public function hiddenStatus($hidden = true)
+    {
+        $this->hidden = !!$hidden;
+        return $this;
+    }
+
+    /**
+     * 组件显示状态
+     *
+     * @param bool $visibility
+     * @return $this
+     */
+    public function visibilityStatus($visibility = true)
+    {
+        $this->visibility = !!$visibility;
+        return $this;
+    }
+
+    /**
+     * @param string $type
+     * @return $this
+     */
+    protected function setRuleType($type)
+    {
+        $this->type = (string)$type;
+        return $this;
+    }
+
+    /**
+     * @param string $field
+     * @return $this
+     */
+    public function field($field)
+    {
+        $this->field = (string)$field;
+        return $this;
+    }
+
+    /**
+     * @param string $title
+     * @return $this
+     */
+    public function title($title)
+    {
+        $this->title = (string)$title;
+        return $this;
+    }
+
+    /**
+     * @param string $name
+     * @return $this
+     */
+    public function name($name)
+    {
+        $this->name = (string)$name;
+        return $this;
+    }
+
+    /**
+     * @param string $className
+     * @return $this
+     */
+    public function className($className)
+    {
+        $this->className = (string)$className;
+        return $this;
+    }
+
+    /**
+     * @param bool $native
+     * @return $this
+     */
+    public function native($native)
+    {
+        $this->native = !!$native;
+        return $this;
+    }
+
+    /**
+     * @param mixed $inject
+     * @return $this
+     */
+    public function inject($inject)
+    {
+        $this->inject = $inject;
+        return $this;
+    }
+
+    /**
+     * @param string $info
+     * @return $this
+     */
+    public function info($info)
+    {
+        $this->info = $info;
+        return $this;
+    }
+
+    /**
+     * @param array|int|ColComponentInterface $col
+     * @return $this
+     */
+    public function col($col)
+    {
+        if (is_integer($col)) $col = ['span' => $col];
+        $this->col = $col;
+        return $this;
+    }
+
+    /**
+     * @param mixed $value
+     * @return $this
+     */
+    public function value($value)
+    {
+        if (is_null($value)) $value = '';
+        $this->value = $value;
+        return $this;
+    }
+
+    public function getHiddenStatus()
+    {
+        return $this->hidden;
+    }
+
+    public function getVisibilityStatus()
+    {
+        return $this->visibility;
+    }
+
+    public function getField()
+    {
+        return $this->field;
+    }
+
+    public function getTitle()
+    {
+        return $this->title;
+    }
+
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    public function getClassName()
+    {
+        return $this->className;
+    }
+
+    public function getInfo()
+    {
+        return $this->info;
+    }
+
+    public function getNative()
+    {
+        return $this->native;
+    }
+
+    public function getInject()
+    {
+        return $this->inject;
+    }
+
+    public function getCol()
+    {
+        return $this->col;
+    }
+
+    public function getValue()
+    {
+        return $this->value;
+    }
+
+    protected function parseCol($col)
+    {
+        return $col instanceof ColComponentInterface ? $col->getCol() : $col;
+    }
+
+    protected function parseBaseRule()
+    {
+        $rule = [
+            'type' => $this->type
+        ];
+
+        if (!is_null($this->field))
+            $rule['field'] = $this->field;
+        if (!is_null($this->value))
+            $rule['value'] = $this->value;
+        if (!is_null($this->title))
+            $rule['title'] = $this->title;
+        if (!is_null($this->className))
+            $rule['className'] = $this->className;
+        if (!is_null($this->name))
+            $rule['name'] = $this->name;
+        if (!is_null($this->native))
+            $rule['native'] = $this->native;
+        if (!is_null($this->info))
+            $rule['info'] = $this->info;
+        if (!is_null($this->inject))
+            $rule['inject'] = $this->inject;
+        if (!is_null($this->hidden))
+            $rule['hidden'] = $this->hidden;
+        if (!is_null($this->visibility))
+            $rule['visibility'] = $this->visibility;
+        if (!is_null($this->col))
+            $rule['col'] = $this->parseCol($this->col);
+
+        return $rule;
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Rule/CallPropsRule.php b/vendor/xaboy/form-builder/src/Rule/CallPropsRule.php
new file mode 100755
index 0000000..44ad4c4
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Rule/CallPropsRule.php
@@ -0,0 +1,45 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Rule;
+
+
+use FormBuilder\Exception\FormBuilderException;
+
+trait CallPropsRule
+{
+    /**
+     * 设置组件属性
+     *
+     * @param $name
+     * @param $arguments
+     * @return $this
+     * @throws FormBuilderException
+     */
+    public function __call($name, $arguments)
+    {
+        if (isset(static::$propsRule[$name])) {
+            if (!isset($arguments[0])) return isset($this->props[$name]) ? $this->props[$name] : null;
+            $value = $arguments[0];
+            if (is_array(static::$propsRule[$name])) {
+                settype($value, static::$propsRule[$name][0]);
+                $name = static::$propsRule[$name][1];
+            } else if (static::$propsRule[$name]) {
+                settype($value, static::$propsRule[$name]);
+            }
+            $this->props[$name] = $value;
+            return $this;
+        } else {
+            throw new FormBuilderException($name . '方法不存在');
+        }
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Rule/ChildrenRule.php b/vendor/xaboy/form-builder/src/Rule/ChildrenRule.php
new file mode 100755
index 0000000..a9a951f
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Rule/ChildrenRule.php
@@ -0,0 +1,97 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Rule;
+
+
+use FormBuilder\Util;
+
+trait ChildrenRule
+{
+    /**
+     * 组件的插槽名称,如果组件是其它组件的子组件,需为插槽指定名称
+     *
+     * @var
+     */
+    protected $slot;
+
+    /**
+     * 设置父级组件的插槽,默认为default.可配合 slot 配置项使用
+     *
+     * @var
+     */
+    protected $children = [];
+
+    /**
+     * @param $slot
+     * @return $this
+     */
+    public function slot($slot)
+    {
+        $this->slot = (string)$slot;
+        return $this;
+    }
+
+    /**
+     * @param array $children
+     * @return $this
+     */
+    public function children(array $children)
+    {
+        $this->children = $children;
+        return $this;
+    }
+
+    /**
+     * @param string|array|self $child
+     * @return $this
+     */
+    public function appendChild($child)
+    {
+        $this->children[] = $child;
+        return $this;
+    }
+
+    /**
+     * @param array $children
+     * @return $this
+     */
+    public function appendChildren($children)
+    {
+        $this->children = array_merge($this->children, $children);
+        return $this;
+    }
+
+    public function getSlot()
+    {
+        return $this->slot;
+    }
+
+    public function getChildren()
+    {
+        return $this->children;
+    }
+
+    /**
+     * @return array
+     */
+    public function parseChildrenRule()
+    {
+        if (!count($this->children)) return [];
+        $children = [];
+        foreach ($this->children as $child) {
+            $children[] = Util::isComponent($child) ? $child->build() : $child;
+        }
+
+        return compact('children');
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Rule/ControlRule.php b/vendor/xaboy/form-builder/src/Rule/ControlRule.php
new file mode 100755
index 0000000..2ce6778
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Rule/ControlRule.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Rule;
+
+
+use FormBuilder\Util;
+
+trait ControlRule
+{
+    /**
+     * 根据组件的值显示不同的组件
+     *
+     * @var
+     */
+    protected $control = [];
+
+
+    /**
+     * @param array $control
+     * @return $this
+     */
+    public function control(array $control)
+    {
+        $this->control = $control;
+        return $this;
+    }
+
+    /**
+     * @param string|int|float $value
+     * @param array $rule
+     * @return $this
+     */
+    public function appendControl($value, array $rule)
+    {
+        $this->control[] = compact('value', 'rule');
+        return $this;
+    }
+
+    /**
+     * @param array $controls
+     * @return $this
+     */
+    public function appendControls(array $controls)
+    {
+        $this->control = array_merge($this->control, $controls);
+        return $this;
+    }
+
+    public function getControl()
+    {
+        return $this->control;
+    }
+
+    /**
+     * @return array
+     */
+    public function parseControlRule()
+    {
+        if (!count($this->control)) return [];
+        $control = [];
+        foreach ($this->control as $child) {
+            foreach ($child['rule'] as $k => $rule) {
+                $child['rule'][$k] = Util::isComponent($rule) ? $rule->build() : $rule;
+            }
+            $control[] = $child;
+        }
+
+        return compact('control');
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Rule/EmitRule.php b/vendor/xaboy/form-builder/src/Rule/EmitRule.php
new file mode 100755
index 0000000..1562725
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Rule/EmitRule.php
@@ -0,0 +1,68 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Rule;
+
+
+trait EmitRule
+{
+    /**
+     * 组件模式下配置使用emit方式触发的事件名
+     * @var array
+     */
+    protected $emit = [];
+
+    /**
+     * 自定义组件emit事件的前缀
+     * @var
+     */
+    protected $emitPrefix;
+
+    public function emit(array $emits)
+    {
+        $this->emit = array_merge($this->emit, array_map('strval', $emits));
+        return $this;
+    }
+
+    public function appendEmit($emit)
+    {
+        $this->emit[] = (string)$emit;
+        return $this;
+    }
+
+    public function emitPrefix($prefix)
+    {
+        $this->emitPrefix = (string)$prefix;
+        return $prefix;
+    }
+
+    public function getEmit()
+    {
+        return $this->emit;
+    }
+
+    public function getEmitPrefix()
+    {
+        return $this->emitPrefix;
+    }
+
+    public function parseEmitRule()
+    {
+        $rule = [];
+        if (count($this->emit))
+            $rule['emit'] = $this->emit;
+        if (!is_null($this->emitPrefix))
+            $rule['emitPrefix'] = $this->emitPrefix;
+
+        return $rule;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Rule/OptionsRule.php b/vendor/xaboy/form-builder/src/Rule/OptionsRule.php
new file mode 100755
index 0000000..d35732e
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Rule/OptionsRule.php
@@ -0,0 +1,109 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Rule;
+
+
+use FormBuilder\Contract\OptionComponentInterface;
+
+trait OptionsRule
+{
+    /**
+     * @var array
+     */
+    protected $options = [];
+
+    /**
+     * 添加选项
+     *
+     * @param string $value
+     * @param string $label
+     * @param bool $disabled
+     * @return $this
+     */
+    public function appendOption($value, $label, $disabled = false)
+    {
+        $label = (string)$label;
+        $this->options[] = compact('value', 'label', 'disabled');
+        return $this;
+    }
+
+    /**
+     * 批量添加选项
+     *
+     * @param array $options
+     * @return $this
+     */
+    public function appendOptions(array $options)
+    {
+        $this->options = array_merge($this->options, $options);
+        return $this;
+    }
+
+    /**
+     * 批量设置的选项
+     *
+     * @param array $options
+     * @return $this
+     */
+    public function setOptions(array $options)
+    {
+        $this->options = $options;
+        return $this;
+    }
+
+    /**
+     * 批量设置选项 支持匿名函数
+     *
+     * @param array|callable $options
+     * @return $this
+     */
+    public function options($options)
+    {
+        return $this->setOptions(is_callable($options) ? $options($this) : $options);
+    }
+
+    /**
+     * @return array
+     */
+    public function getOptions()
+    {
+        return $this->options;
+    }
+
+    /**
+     * @param $option
+     * @return array
+     */
+    protected function parseOption($option)
+    {
+        return $option instanceof OptionComponentInterface ? $option->getOption() : $option;
+    }
+
+    /**
+     * @return array
+     */
+    protected function parseOptions()
+    {
+        $options = [];
+        foreach ($this->options as $option) {
+            $options[] = $this->parseOption($option);
+        }
+
+        return $options;
+    }
+
+    public function parseOptionsRule()
+    {
+        return ['options' => $this->parseOptions()];
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Rule/PropsRule.php b/vendor/xaboy/form-builder/src/Rule/PropsRule.php
new file mode 100755
index 0000000..51ce097
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Rule/PropsRule.php
@@ -0,0 +1,132 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Rule;
+
+
+trait PropsRule
+{
+    /**
+     * 组件的props
+     *
+     * @var array
+     */
+    protected $props = [];
+
+    /**
+     * 组件普通的 HTML 特性
+     *
+     * @var array
+     */
+    protected $attrs = [];
+
+    /**
+     * 组件的样式
+     * @var string|array
+     */
+    protected $style;
+
+    /**
+     * 组件的 DOM 属性
+     *
+     * @var array
+     */
+    protected $domProps = [];
+
+    /**
+     * 设置组件的 style 属性
+     * @param string|array $style
+     * @return $this
+     */
+    public function style($style)
+    {
+        $this->style = $style;
+        return $this;
+    }
+
+    /**
+     * @return array|string
+     */
+    public function getStyle()
+    {
+        return $this->style;
+    }
+
+    public function prop($name, $value)
+    {
+        $this->props[$name] = $value;
+        return $this;
+    }
+
+    public function props(array $props)
+    {
+        $this->props = array_merge($this->props, $props);
+        return $this;
+    }
+
+    public function attr($name, $value)
+    {
+        $this->attrs[$name] = $value;
+        return $this;
+    }
+
+    public function attrs(array $attrs)
+    {
+        $this->attrs = array_merge($this->attrs, $attrs);
+        return $this;
+    }
+
+    public function domProp($name, $value)
+    {
+        $this->domProps[$name] = $value;
+        return $this;
+    }
+
+    public function domProps(array $domProps)
+    {
+        $this->domProps = array_merge($this->domProps, $domProps);
+        return $this;
+    }
+
+    public function getProps()
+    {
+        return $this->props;
+    }
+
+    public function getProp($name)
+    {
+        return isset($this->props[$name]) ? $this->props[$name] : null;
+    }
+
+    public function getAttrs()
+    {
+        return $this->attrs;
+    }
+
+    public function getDomProps()
+    {
+        return $this->domProps;
+    }
+
+    public function parsePropsRule()
+    {
+        $rule = ['props' => (object)$this->props];
+        if (count($this->attrs))
+            $rule['attrs'] = $this->attrs;
+        if (count($this->domProps))
+            $rule['domProps'] = $this->domProps;
+        if (!is_null($this->style))
+            $rule['style'] = $this->style;
+
+        return $rule;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Rule/ValidateRule.php b/vendor/xaboy/form-builder/src/Rule/ValidateRule.php
new file mode 100755
index 0000000..d544899
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Rule/ValidateRule.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Rule;
+
+
+use FormBuilder\Contract\ValidateInterface;
+
+trait ValidateRule
+{
+    /**
+     * 组件验证规则
+     *
+     * @var array
+     */
+    protected $validate = [];
+
+    public function validate(array $validates)
+    {
+        $this->validate = $validates;
+        return $this;
+    }
+
+    /**
+     * @param array|ValidateInterface $validate
+     * @return $this
+     */
+    public function appendValidate($validate)
+    {
+        $this->validate[] = $validate;
+        return $this;
+    }
+
+    public function appendValidates(array $validates)
+    {
+        $this->validate = array_merge($this->validate, $validates);
+        return $this;
+    }
+
+    protected function getValidate()
+    {
+        return $this->validate;
+    }
+
+    protected function parseValidate()
+    {
+        $validate = [];
+        foreach ($this->validate as $value) {
+            $validate[] = $value instanceof ValidateInterface ? $value->getValidate() : $value;
+        }
+
+        return $validate;
+    }
+
+    public function parseValidateRule()
+    {
+        if (!count($this->validate))
+            return [];
+        return ['validate' => $this->parseValidate()];
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Template/createScript.min.php b/vendor/xaboy/form-builder/src/Template/createScript.min.php
new file mode 100755
index 0000000..aab39b0
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Template/createScript.min.php
@@ -0,0 +1 @@
+(function(){var e=function(l,k){return Object.prototype.toString.call(l)===("[object "+k+"]")};var g=function(k){if(!k){return[]}k.forEach(function(n){var m=n.type?(""+n.type).toLocaleLowerCase():"",l=e(k.children,"Array")?k.children:[];if((m==="cascader"||m==="tree")&&n.props){if(n.props.data&&e(n.props.data,"String")&&n.props.data.indexOf("js.")===0){n.props.data=window[n.props.data.substr(3)]}else{if(n.props.options&&e(n.props.options,"String")&&n.props.options.indexOf("js.")===0){n.props.options=window[n.props.options.substr(3)]}}}else{if(m==="group"){if(n.props.rules){g(n.props.rules)}if(n.props.rule){g([n.props.rule])}}else{if(n.control && n.control.length){n.control.forEach(function(o){g(o.rule)})}}}if(l.length){g(l)}});return k};var i=function(k,l,m,n){$.ajax({url:k,type:(""+l).toLocaleUpperCase(),dataType:"json",headers:c,contentType:j,data:m,success:function(o){n(1,o)},error:function(){n(0,{})}})};var h=g(<?=$form->parseFormRule()?>),c=<?=$form->parseHeaders()?>,d=<?=$form->parseFormConfig()?>,j="<?=$form->getFormContentType()?>",f="<?=$form->getAction()?>",a="<?=$form->getMethod()?>",b=new Vue();if(!b.$Message&&b.$message){Vue.prototype.$Message=b.$message}return function(l){if(!l){l={}}if(l.el){d.el=l.el}d.onSubmit=function(o,n){n.submitBtnProps({loading:true,disabled:true});i(f,a,o,function(p,q){if(l.callback){return l.callback(p,q,n)}n.submitBtnProps({loading:false,disabled:false});if(p&&q.code===200){b.$Message.success(q.msg||"表单提交成功")}else{b.$Message.error(q.msg||"表单提交失败")}})};if(!d.global){d.global={}}if(!d.global.upload){d.global.upload={}}if(!d.global.upload.props){d.global.upload.props={}}var k=d.global.upload.props;k.onExceededSize=function(n){b.$Message.error(n.name+"超出指定大小限制")};k.onFormatError=function(n){b.$Message.error(n.name+"格式验证失败")};k.onError=function(n,o){b.$Message.error(o.name+"上传失败,("+n+")")};k.onSuccess=function(o,n){if(o.code===200){n.url=o.data.filePath}else{b.$Message.error(o.msg)}};var m=function(){return formCreate.create(h,d)};return l.filter?l.filter(m):m()}})();
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Template/createScript.php b/vendor/xaboy/form-builder/src/Template/createScript.php
new file mode 100755
index 0000000..8a67b76
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Template/createScript.php
@@ -0,0 +1,94 @@
+(function () {
+
+    var isType = function (data, type) {
+        return Object.prototype.toString.call(data) === ('[object ' + type + ']');
+    };
+
+    var parseRule = function (rule) {
+        if (!rule) return [];
+        rule.forEach(function (c) {
+            var type = c.type ? ('' + c.type).toLocaleLowerCase() : '',
+                children = isType(rule.children, 'Array') ? rule.children : [];
+            if ((type === 'cascader' || type === 'tree') && c.props) {
+                if (c.props.data && isType(c.props.data, 'String') && c.props.data.indexOf('js.') === 0) {
+                    c.props.data = window[c.props.data.substr(3)];
+                } else if (c.props.options && isType(c.props.options, 'String') && c.props.options.indexOf('js.') === 0) {
+                    c.props.options = window[c.props.options.substr(3)];
+                }
+            } else if (type === 'group') {
+                if (c.props.rules) parseRule(c.props.rules);
+                if (c.props.rule) parseRule([c.props.rule]);
+            } else if (c.control && c.control.length) {
+                c.control.forEach(function(r) {
+                    parseRule(r.rule);
+                });
+            }
+            if (children.length) parseRule(children);
+        });
+
+        return rule;
+    };
+
+    var ajax = function (url, type, formData, callback){
+        $.ajax({
+            url: url,
+            type: ('' + type).toLocaleUpperCase(),
+            dataType: 'json',
+            headers: headers,
+            contentType: contentType,
+            data: formData,
+            success: function (res) {
+                callback(1, res);
+            },
+            error: function () {
+                callback(0, {});
+            }
+        });
+
+    }
+
+    var rule = parseRule(<?=$form->parseFormRule()?>), headers = <?=$form->parseHeaders()?>, config = <?=$form->parseFormConfig()?>, contentType = "<?=$form->getFormContentType()?>", action = "<?=$form->getAction()?>", method = "<?=$form->getMethod()?>", vm = new Vue();
+    if (!vm.$Message && vm.$message) {
+        Vue.prototype.$Message = vm.$message
+    }
+    return function (option) {
+        if (!option) option = {};
+        if (option.el) config.el = option.el;
+
+        config.onSubmit = function (formData, $f) {
+            $f.submitBtnProps({loading: true, disabled: true});
+            ajax(action, method, formData, function (status, res) {
+                if (option.callback) return option.callback(status, res, $f);
+
+                $f.submitBtnProps({loading: false, disabled: false});
+                if (status && res.code === 200) {
+                    vm.$Message.success(res.msg || '表单提交成功');
+                } else {
+                    vm.$Message.error(res.msg || '表单提交失败');
+                }
+            });
+        };
+        if(!config.global) config.global = {};
+        if(!config.global.upload) config.global.upload = {};
+        if(!config.global.upload.props) config.global.upload.props = {};
+        var uploadProps = config.global.upload.props;
+        uploadProps.onExceededSize = function (file) {
+            vm.$Message.error(file.name + '超出指定大小限制');
+        };
+        uploadProps.onFormatError = function (file) {
+            vm.$Message.error(file.name + '格式验证失败');
+        };
+        uploadProps.onError = function (error, file) {
+            vm.$Message.error(file.name + '上传失败,(' + error + ')');
+        };
+        uploadProps.onSuccess = function (res, file) {
+            if (res.code === 200) {
+                file.url = res.data.filePath;
+            } else {
+                vm.$Message.error(res.msg);
+            }
+        };
+        var _create = function(){return formCreate.create(rule, config);}
+        return option.filter ? option.filter(_create) : _create() ;
+    };
+})();
diff --git a/vendor/xaboy/form-builder/src/Template/form.php b/vendor/xaboy/form-builder/src/Template/form.php
new file mode 100755
index 0000000..97d16d4
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Template/form.php
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title><?= $form->getTitle() ?></title>
+    <?= $form->parseDependScript() ?>
+</head>
+<body>
+<script>
+    (function () {
+        var create = <?=$form->formScript()?>;
+        create();
+    })();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Bootstrap.php b/vendor/xaboy/form-builder/src/UI/Elm/Bootstrap.php
new file mode 100755
index 0000000..d5712c6
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Bootstrap.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm;
+
+use FormBuilder\Contract\BootstrapInterface;
+use FormBuilder\Form;
+
+class Bootstrap implements BootstrapInterface
+{
+
+    public function init(Form $form)
+    {
+        $dependScript = $form->getDependScript();
+
+        array_splice($dependScript, 2, 0, [
+            '<link href="https://unpkg.com/element-ui@2.12.0/lib/theme-chalk/index.css" rel="stylesheet">',
+            '<script src="https://unpkg.com/element-ui@2.12.0/lib/index.js"></script>',
+            '<script src="https://unpkg.com/@form-create/element-ui@1.0.15/dist/form-create.min.js"></script>',
+        ]);
+
+        $form->setDependScript($dependScript);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Components/Button.php b/vendor/xaboy/form-builder/src/UI/Elm/Components/Button.php
new file mode 100755
index 0000000..cc4f50c
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Components/Button.php
@@ -0,0 +1,51 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Components;
+
+
+use FormBuilder\Driver\CustomComponent;
+
+/**
+ * Class Button
+ *
+ * @method $this size(string $size) 尺寸, 可选值: medium / small / mini
+ * @method $this type(string $type) 类型, 可选值: primary / success / warning / danger / info / text
+ * @method $this plain(bool $plain) 是否朴素按钮, 默认值: false
+ * @method $this round(bool $round) 是否圆角按钮, 默认值: false
+ * @method $this circle(bool $circle) 是否圆形按钮, 默认值: false
+ * @method $this loading(bool $loading) 是否加载中状态, 默认值: false
+ * @method $this disabled(bool $disabled) 是否禁用状态, 默认值: false
+ * @method $this icon(string $icon) 图标类名
+ * @method $this autofocus(bool $autofocus) 是否默认聚焦, 默认值: false
+ * @method $this nativeType(string $nativeType) 原生 type 属性, 可选值: button / submit / reset, 默认值: button
+ *
+ * @method $this show(bool $show) 是否显示, 默认显示
+ * @method $this innerText(string $innerText) 按钮文字提示
+ */
+class Button extends CustomComponent
+{
+    protected static $propsRule = [
+        'size' => 'string',
+        'type' => 'string',
+        'plain' => 'bool',
+        'round' => 'bool',
+        'circle' => 'bool',
+        'loading' => 'bool',
+        'disabled' => 'bool',
+        'icon' => 'string',
+        'autofocus' => 'bool',
+        'nativeType' => 'string',
+        'innerText' => 'string',
+        'show' => 'bool'
+    ];
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Components/Cascader.php b/vendor/xaboy/form-builder/src/UI/Elm/Components/Cascader.php
new file mode 100755
index 0000000..53f9eac
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Components/Cascader.php
@@ -0,0 +1,126 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Components;
+
+
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Factory\Elm;
+
+/**
+ * 多级联动组件
+ * Class Cascader
+ *
+ * @method $this type(string $type) 数据类型, 支持 city_area(省市区三级联动), city (省市二级联动), other (自定义)
+ * @method $this props(array $props) 配置选项
+ * @method $this size(string $size) 尺寸, 可选值: medium / small / mini
+ * @method $this placeholder(string $placeholder) 输入框占位文本, 默认值: 请选择
+ * @method $this disabled(bool $disabled) 是否禁用, 默认值: false
+ * @method $this clearable(bool $clearable) 是否支持清空选项, 默认值: false
+ * @method $this showAllLevels(bool $showAllLevels) 输入框中是否显示选中值的完整路径, 默认值: true
+ * @method $this collapseTags(bool $collapseTags) 多选模式下是否折叠Tag, 默认值: false
+ * @method $this separator(string $separator) 选项分隔符, 默认值: 斜杠' / '
+ * @method $this filterable(bool $filterable) 是否可搜索选项
+ * @method $this debounce(float $debounce) 搜索关键词输入的去抖延迟,毫秒, 默认值: 300
+ * @method $this popperClass(string $popperClass) 自定义浮层类名
+ */
+class Cascader extends FormComponent
+{
+    /**
+     * 省市区三级联动数据
+     */
+    const TYPE_CITY_AREA = 'city_area';
+
+    /**
+     * 省市二级联动数据
+     */
+    const TYPE_CITY = 'city';
+
+    /**
+     * 自定义数据
+     */
+    const TYPE_OTHER = 'other';
+
+
+    protected $defaultValue = [];
+
+    protected $selectComponent = true;
+
+    protected $defaultProps = [
+        'type' => self::TYPE_OTHER,
+        'options' => []
+    ];
+
+    /**
+     * @var array
+     */
+    protected static $propsRule = [
+        'type' => 'string',
+        'props' => 'array',
+        'size' => 'string',
+        'placeholder' => 'string',
+        'disabled' => 'bool',
+        'clearable' => 'bool',
+        'showAllLevels' => 'bool',
+        'collapseTags' => 'bool',
+        'separator' => 'string',
+        'filterable' => 'bool',
+        'debounce' => 'float',
+        'popperClass' => 'string',
+    ];
+
+    /**
+     * @param array $value
+     * @return $this
+     */
+    public function value($value)
+    {
+        $this->value = (array)$value;
+        return $this;
+    }
+
+    /**
+     * 可选项的数据源
+     * 例如:{
+     *    "value":"北京市", "label":"北京市", "children":[{
+     *        "value":"东城区", "label":"东城区"
+     *    }]
+     *  }
+     *
+     * @param array|callable $data
+     * @return $this
+     */
+    public function options($data)
+    {
+        $is_callable = is_callable($data);
+        if (!is_array($data) && !$is_callable) return $this;
+
+        $this->props['options'] = $is_callable ? $data($this) : $data;
+        return $this;
+    }
+
+    /**
+     * @param $var
+     * @return $this
+     */
+    public function jsOptions($var)
+    {
+        $this->props['options'] = 'js.' . (string)$var;
+        return $this;
+    }
+
+    public function createValidate()
+    {
+        return Elm::validateArr();
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Components/Checkbox.php b/vendor/xaboy/form-builder/src/UI/Elm/Components/Checkbox.php
new file mode 100755
index 0000000..ed090b4
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Components/Checkbox.php
@@ -0,0 +1,79 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Components;
+
+
+use FormBuilder\Driver\FormOptionsComponent;
+use FormBuilder\Factory\Elm;
+
+/**
+ * 复选框组件
+ * Class Checkbox
+ *
+ * @method $this size(string $size) 多选框组尺寸,仅对按钮形式的 Checkbox 或带有边框的 Checkbox 有效, 可选值: medium / small / mini
+ * @method $this disabled(bool $disabled) 是否禁用, 默认值: false
+ * @method $this min(float $min) 可被勾选的 checkbox 的最小数量
+ * @method $this max(float $max) 可被勾选的 checkbox 的最大数量
+ * @method $this textColor(string $textColor) 按钮形式的 Checkbox 激活时的文本颜色, 默认值: #ffffff
+ * @method $this fill(string $fill) 按钮形式的 Checkbox 激活时的填充色和边框色, 默认值: #409EFF
+ * @method $this checked(bool $checked) 当前是否勾选(只有在checkbox-button时有效), 默认值: false
+ */
+class Checkbox extends FormOptionsComponent
+{
+    const TYPE_BUTTON = 'button';
+
+    const TYPE_GROUP = 'group';
+
+    protected $defaultValue = [];
+
+    protected $selectComponent = true;
+
+    protected static $propsRule = [
+        'size' => 'string',
+        'disabled' => 'bool',
+        'min' => 'float',
+        'max' => 'float',
+        'textColor' => 'string',
+        'fill' => 'string',
+        'checked' => 'bool',
+    ];
+
+    /**
+     * @param array $value
+     * @return $this
+     */
+    public function value($value)
+    {
+        $this->value = (array)$value;
+        return $this;
+    }
+
+    /**
+     * @param bool $bool
+     * @return $this
+     */
+    public function button($bool)
+    {
+        if ($bool)
+            $this->props['type'] = 'button';
+        else
+            unset($this->props['type']);
+
+        return $this;
+    }
+
+    public function createValidate()
+    {
+        return Elm::validateArr();
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Components/ColorPicker.php b/vendor/xaboy/form-builder/src/UI/Elm/Components/ColorPicker.php
new file mode 100755
index 0000000..57cf8f1
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Components/ColorPicker.php
@@ -0,0 +1,47 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Components;
+
+
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Factory\Elm;
+
+/**
+ * 颜色选择器组件
+ * Class ColorPicker
+ *
+ * @method $this disabled(bool $disabled) 是否禁用, 默认值: false
+ * @method $this size(string $size) 尺寸, 默认值: medium / small / mini
+ * @method $this showAlpha(bool $showAlpha) 是否支持透明度选择, 默认值: false
+ * @method $this colorFormat(string $colorFormat) 的格式, 可选值: hsl / hsv / hex / rgb, 默认值: hex(show-alpha 为 false)/ rgb(show-alpha 为 true)
+ * @method $this popperClass(string $popperClass) ColorPicker 下拉框的类名
+ * @method $this predefine(array $predefine) 预定义颜色
+ */
+class ColorPicker extends FormComponent
+{
+    protected $selectComponent = true;
+
+    protected static $propsRule = [
+        'disabled' => 'bool',
+        'size' => 'string',
+        'showAlpha' => 'bool',
+        'colorFormat' => 'string',
+        'popperClass' => 'string',
+        'predefine' => 'array',
+    ];
+
+    public function createValidate()
+    {
+        return Elm::validateStr();
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Components/DatePicker.php b/vendor/xaboy/form-builder/src/UI/Elm/Components/DatePicker.php
new file mode 100755
index 0000000..f6e47ae
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Components/DatePicker.php
@@ -0,0 +1,153 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Components;
+
+
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Factory\Elm;
+
+/**
+ * 日期选择器组件
+ * Class DatePicker
+ *
+ * @method $this readonly(bool $readonly) 完全只读, 默认值: false
+ * @method $this disabled(bool $disabled) 禁用, 默认值: false
+ * @method $this editable(bool $editable) 文本框可输入, 默认值: true
+ * @method $this clearable(bool $clearable) 是否显示清除按钮, 默认值: true
+ * @method $this size(string $size) 输入框尺寸, 可选值: large, small, mini
+ * @method $this placeholder(string $placeholder) 非范围选择时的占位内容
+ * @method $this startPlaceholder(string $startPlaceholder) 范围选择时开始日期的占位内容
+ * @method $this endPlaceholder(string $endPlaceholder) 范围选择时结束日期的占位内容
+ * @method $this type(string $type) 显示类型, 可选值: year/month/date/dates/ week/datetime/datetimerange/ daterange/monthrange, 默认值: date
+ * @method $this format(string $format) 显示在输入框中的格式, 可选值: 见日期格式, 默认值: yyyy-MM-dd
+ * @method $this align(string $align) 对齐方式, 可选值: left, center, right, 默认值: left
+ * @method $this popperClass(string $popperClass) DatePicker 下拉框的类名
+ * @method $this pickerOptions(array $pickerOptions) 当前时间日期选择器特有的选项参考下表, 默认值: {
+ * }
+ * @method $this rangeSeparator(string $rangeSeparator) 选择范围时的分隔符, 默认值: '-'
+ * @method $this defaultValue(string $defaultValue) 可选,选择器打开时默认显示的时间, 可选值: 可被new Date()解析
+ * @method $this defaultTime(array $defaultTime) 范围选择时选中日期所使用的当日内具体时刻, 可选值: 数组,长度为 2,每项值为字符串,形如12:00:00,第一项指定开始日期的时刻,第二项指定结束日期的时刻,不指定会使用时刻 00:00:00
+ * @method $this valueFormat(string $valueFormat) 可选,绑定值的格式。不指定则绑定值为 Date 对象, 可选值: 见日期格式
+ * @method $this name(string $name) 原生属性
+ * @method $this unlinkPanels(bool $unlinkPanels) 在范围选择器里取消两个日期面板之间的联动, 默认值: false
+ * @method $this prefixIcon(string $prefixIcon) 自定义头部图标的类名, 默认值: el-icon-date
+ * @method $this clearIcon(string $clearIcon) 自定义清空图标的类名, 默认值: el-icon-circle-close
+ * @method $this validateEvent(bool $validateEvent) 输入时是否触发表单的校验, 默认值: true
+ * @method $this timeArrowControl(bool $timeArrowControl) 是否使用箭头进行时间选择, 默认值: false
+ *
+ *
+ */
+class DatePicker extends FormComponent
+{
+    /**
+     * 日期选择
+     */
+    const TYPE_DATE = 'date';
+
+    const TYPE_DATES = 'dates';
+
+    const TYPE_WEEK = 'week';
+
+    /**
+     * 日期区间选择
+     */
+    const TYPE_DATE_RANGE = 'daterange';
+
+    const TYPE_MONTH_RANGE = 'monthrange';
+
+    /**
+     * 日期+时间选择
+     */
+    const TYPE_DATE_TIME = 'datetime';
+    /**
+     * 日期+时间区间选择
+     */
+    const TYPE_DATE_TIME_RANGE = 'datetimerange';
+    /**
+     * 年份选择
+     */
+    const TYPE_YEAR = 'year';
+    /**
+     * 月份选择
+     */
+    const TYPE_MONTH = 'month';
+
+
+    protected $selectComponent = true;
+
+    protected $defaultProps = [
+        'type' => self::TYPE_DATE,
+        'editable' => false
+    ];
+
+    protected static $propsRule = [
+        'readonly' => 'bool',
+        'disabled' => 'bool',
+        'editable' => 'bool',
+        'clearable' => 'bool',
+        'size' => 'string',
+        'placeholder' => 'string',
+        'startPlaceholder' => 'string',
+        'endPlaceholder' => 'string',
+        'timeArrowControl' => 'bool',
+        'type' => 'string',
+        'format' => 'string',
+        'align' => 'string',
+        'popperClass' => 'string',
+        'pickerOptions' => 'array',
+        'rangeSeparator' => 'string',
+        'defaultValue' => 'string',
+        'defaultTime' => 'array',
+        'valueFormat' => 'string',
+        'name' => 'string',
+        'unlinkPanels' => 'bool',
+        'prefixIcon' => 'string',
+        'clearIcon' => 'string',
+        'validateEvent' => 'bool',
+    ];
+
+    protected function isRange()
+    {
+        return in_array(strtolower($this->props['type']), ['datetimerange', 'daterange', 'monthrange']);
+    }
+
+    protected function isMultiple()
+    {
+        return isset($this->props['type']) && strtolower($this->props['type']) == 'dates';
+    }
+
+    public function createValidate()
+    {
+        if ($this->isRange() || $this->isMultiple())
+            return Elm::validateArr();
+        else
+            return Elm::validateDate();
+    }
+
+    public function required($message = null)
+    {
+        if (is_null($message)) $message = $this->getPlaceHolder();
+        $validate = $this->createValidate();
+
+        if ($this->isRange()) {
+            $dateRequired = Elm::validateDate()->message($message)->required();
+            $validate->fields([
+                '0' => $dateRequired,
+                '1' => $dateRequired
+            ]);
+        }
+
+        $this->appendValidate($validate->message($message)->required());
+        return $this;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Components/Frame.php b/vendor/xaboy/form-builder/src/UI/Elm/Components/Frame.php
new file mode 100755
index 0000000..d4f1141
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Components/Frame.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Components;
+
+
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Factory\Elm;
+
+/**
+ * 框架组件
+ * Class Frame
+ *
+ * @method $this type(string $type) frame类型, 有input, file, image, 默认为input
+ * @method $this src(string $src) iframe地址
+ * @method $this maxLength(int $length) value的最大数量, 默认无限制
+ * @method $this icon(string $icon) 打开弹出框的按钮图标
+ * @method $this height(string $height) 弹出框高度
+ * @method $this width(string $width) 弹出框宽度
+ * @method $this spin(bool $bool) 是否显示加载动画, 默认为 true
+ * @method $this frameTitle(string $title) 弹出框标题
+ * @method $this modal(array $modalProps) 弹出框props
+ * @method $this handleIcon(bool $bool) 操作按钮的图标, 设置为false将不显示, 设置为true为默认的预览图标, 类型为file时默认为false, image类型默认为true
+ * @method $this allowRemove(bool $bool) 是否可删除, 设置为false是不显示删除按钮
+ * @method $this disabled(bool $bool) 是否禁用
+ *
+ *
+ */
+class Frame extends FormComponent
+{
+    /**
+     * 图片类型
+     */
+    const TYPE_IMAGE = 'image';
+    /**
+     * 文件类型
+     */
+    const TYPE_FILE = 'file';
+    /**
+     * input 类型
+     */
+    const TYPE_INPUT = 'input';
+
+
+    protected $selectComponent = true;
+
+    protected $defaultProps = [
+        'type' => self::TYPE_INPUT,
+        'maxLength' => 0
+    ];
+
+    protected static $propsRule = [
+        'type' => 'string',
+        'src' => 'string',
+        'maxLength' => 'int',
+        'icon' => 'string',
+        'height' => 'string',
+        'width' => 'string',
+        'spin' => 'bool',
+        'modal' => 'array',
+        'frameTitle' => ['string', 'title'],
+        'handleIcon' => 'bool',
+        'allowRemove' => 'bool',
+    ];
+
+    public function createValidate()
+    {
+        return Elm::validateArr();
+    }
+
+    protected function init()
+    {
+        $this->frameTitle($this->getPlaceHolder());
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Components/Group.php b/vendor/xaboy/form-builder/src/UI/Elm/Components/Group.php
new file mode 100755
index 0000000..fcfdca0
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Components/Group.php
@@ -0,0 +1,77 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Components;
+
+
+use FormBuilder\Contract\ValidateInterface;
+use FormBuilder\Driver\CustomComponent;
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Factory\Elm;
+use FormBuilder\Util;
+
+/**
+ * 数组组件
+ *
+ * Class Group
+ * @method $this min(int $min) 最少几项
+ * @method $this max(int $max) 最多几项
+ * @method $this disabled(bool $bool) 是否禁用
+ */
+class Group extends FormComponent
+{
+    protected $defaultValue = [];
+
+    protected static $propsRule = [
+        'min' => 'string',
+        'max' => 'string',
+        'disabled' => 'int',
+    ];
+
+    /**
+     * @param array|CustomComponent $rule
+     * @return $this
+     */
+    public function rule($rule)
+    {
+        $this->props['rule'] = $this->tidyRule([$rule])[0];
+        return $this;
+    }
+
+    /**
+     * @param array $rules
+     * @return array
+     */
+    protected function tidyRule(array $rules)
+    {
+        foreach ($rules as $k => $rule) {
+            if (Util::isComponent($rule)) {
+                $rules[$k] = $rule->build();
+            }
+        }
+        return $rules;
+    }
+
+    public function rules(array $rules)
+    {
+        $this->props['rules'] = $this->tidyRule($rules);
+        return $this;
+    }
+
+    /**
+     * @return ValidateInterface
+     */
+    public function createValidate()
+    {
+        return Elm::validateArr();
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Components/Hidden.php b/vendor/xaboy/form-builder/src/UI/Elm/Components/Hidden.php
new file mode 100755
index 0000000..7a70ff3
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Components/Hidden.php
@@ -0,0 +1,57 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Components;
+
+
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Exception\FormBuilderException;
+
+/**
+ * hidden组件
+ * Class Hidden
+ *
+ */
+class Hidden extends FormComponent
+{
+    /**
+     * Hidden constructor.
+     *
+     * @param string $field
+     * @param string $value
+     */
+    public function __construct($field, $value)
+    {
+        parent::__construct($field, '', $value);
+    }
+
+    /**
+     * @return array
+     */
+    public function getRule()
+    {
+        return [
+            'type' => $this->type,
+            'field' => $this->field,
+            'value' => $this->value
+        ];
+    }
+
+    /**
+     * @return void
+     * @throws FormBuilderException
+     */
+    public function createValidate()
+    {
+        throw new FormBuilderException('hidden 组件不支持 createValidate 方法');
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Components/Input.php b/vendor/xaboy/form-builder/src/UI/Elm/Components/Input.php
new file mode 100755
index 0000000..ea5bc5c
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Components/Input.php
@@ -0,0 +1,111 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Components;
+
+
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Factory\Elm;
+
+/**
+ * Input组件,支持类型text、password、textarea、url、email、date
+ * Class Input
+ *
+ * @method $this type(string $type) 类型, 可选值: text,textarea 和其他 原生 input 的 type 值, 默认值: text
+ * @method $this maxlength(float $maxlength) 原生属性,最大输入长度
+ * @method $this minlength(float $minlength) 原生属性,最小输入长度
+ * @method $this showWordLimit(bool $showWordLimit) 是否显示输入字数统计,只在 type = "text" 或 type = "textarea" 时有效, 默认值: false
+ * @method $this placeholder(string $placeholder) 输入框占位文本
+ * @method $this clearable(bool $clearable) 是否可清空, 默认值: false
+ * @method $this showPassword(bool $showPassword) 是否显示切换密码图标, 默认值: false
+ * @method $this disabled(bool $disabled) 禁用, 默认值: false
+ * @method $this size(string $size) 输入框尺寸,只在 type!="textarea" 时有效, 可选值: medium / small / mini
+ * @method $this prefixIcon(string $prefixIcon) 输入框头部图标
+ * @method $this suffixIcon(string $suffixIcon) 输入框尾部图标
+ * @method $this rows(float $rows) 输入框行数,只对 type = "textarea" 有效, 默认值: 2
+ * @method $this autocomplete(string $autocomplete) 原生属性,自动补全, 可选值: on, off, 默认值: off
+ * @method $this name(string $name) 原生属性
+ * @method $this readonly(bool $readonly) 原生属性,是否只读, 默认值: false
+ * @method $this max(string $max) 原生属性,设置最大值
+ * @method $this min(string $min) 原生属性,设置最小值
+ * @method $this step(string $step) 原生属性,设置输入字段的合法数字间隔
+ * @method $this resize(string $resize) 控制是否能被用户缩放, 可选值: none, both, horizontal, vertical
+ * @method $this autofocus(bool $autofocus) 原生属性,自动获取焦点, 可选值: true, false, 默认值: false
+ * @method $this form(string $form) 原生属性
+ * @method $this label(string $label) 输入框关联的label文字
+ * @method $this tabindex(string $tabindex) 输入框的tabindex
+ * @method $this validateEvent(bool $validateEvent) 输入时是否触发表单的校验, 默认值: true
+ */
+class Input extends FormComponent
+{
+    const TYPE_TEXT = 'text';
+
+    const TYPE_PASSWORD = 'password';
+
+    const TYPE_TEXTAREA = 'textarea';
+
+    const TYPE_URL = 'url';
+
+    const TYPE_EMAIL = 'email';
+
+    const TYPE_DATE = 'date';
+
+
+    protected $defaultProps = [
+        'type' => self::TYPE_TEXT
+    ];
+
+    protected static $propsRule = [
+        'type' => 'string',
+        'maxlength' => 'float',
+        'minlength' => 'float',
+        'showWordLimit' => 'bool',
+        'placeholder' => 'string',
+        'clearable' => 'bool',
+        'showPassword' => 'bool',
+        'disabled' => 'bool',
+        'size' => 'string',
+        'prefixIcon' => 'string',
+        'suffixIcon' => 'string',
+        'rows' => 'float',
+        'autocomplete' => 'string',
+        'name' => 'string',
+        'readonly' => 'bool',
+        'max' => 'string',
+        'min' => 'string',
+        'step' => 'string',
+        'resize' => 'string',
+        'autofocus' => 'bool',
+        'form' => 'string',
+        'label' => 'string',
+        'tabindex' => 'string',
+        'validateEvent' => 'bool',
+    ];
+
+    /**
+     * 自适应内容高度,仅在 textarea 类型下有效
+     *
+     * @param Bool|Number $minRows
+     * @param null|Number $maxRows
+     * @return $this
+     */
+    public function autoSize($minRows = false, $maxRows = null)
+    {
+        $this->props['autosize'] = $maxRows === null ? boolval($minRows) : compact('minRows', 'maxRows');
+        return $this;
+    }
+
+    public function createValidate()
+    {
+        return Elm::validateStr();
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Components/InputNumber.php b/vendor/xaboy/form-builder/src/UI/Elm/Components/InputNumber.php
new file mode 100755
index 0000000..fd1f817
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Components/InputNumber.php
@@ -0,0 +1,57 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Components;
+
+
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Factory\Elm;
+
+/**
+ * 数字输入框组件
+ * Class InputNumber
+ *
+ * @method $this min(float $min) 设置计数器允许的最小值, 默认值: -Infinity
+ * @method $this max(float $max) 设置计数器允许的最大值, 默认值: Infinity
+ * @method $this step(float $step) 计数器步长, 默认值: 1
+ * @method $this stepStrictly(float $stepStrictly) 是否只能输入 step 的倍数, 默认值: false
+ * @method $this precision(float $precision) 数值精度
+ * @method $this size(string $size) 计数器尺寸, 可选值: large, small
+ * @method $this disabled(bool $disabled) 是否禁用计数器, 默认值: false
+ * @method $this controls(bool $controls) 是否使用控制按钮, 默认值: true
+ * @method $this controlsPosition(string $controlsPosition) 控制按钮位置, 可选值: right
+ * @method $this name(string $name) 原生属性
+ * @method $this label(string $label) 输入框关联的label文字
+ * @method $this placeholder(string $placeholder) 输入框默认 placeholder
+ */
+class InputNumber extends FormComponent
+{
+    protected static $propsRule = [
+        'min' => 'float',
+        'max' => 'float',
+        'step' => 'float',
+        'stepStrictly' => 'float',
+        'precision' => 'float',
+        'size' => 'string',
+        'disabled' => 'bool',
+        'controls' => 'bool',
+        'controlsPosition' => 'string',
+        'name' => 'string',
+        'label' => 'string',
+        'placeholder' => 'string',
+    ];
+
+    public function createValidate()
+    {
+        return Elm::validateNum();
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Components/Option.php b/vendor/xaboy/form-builder/src/UI/Elm/Components/Option.php
new file mode 100755
index 0000000..342ed21
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Components/Option.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Components;
+
+
+use FormBuilder\Contract\OptionComponentInterface;
+
+class Option implements OptionComponentInterface
+{
+    /**
+     * @var array
+     */
+    protected $rule;
+
+    /**
+     * Option constructor.
+     *
+     * @param string|number $value
+     * @param string $label
+     * @param bool $disabled
+     */
+    public function __construct($value, $label = '', $disabled = null)
+    {
+        $this->rule = compact('label', 'value');
+        if (!is_null($disabled))
+            $this->disabled($disabled);
+    }
+
+    /**
+     * @param bool $disabled
+     * @return $this
+     */
+    public function disabled($disabled = true)
+    {
+        $this->rule['disabled'] = !!$disabled;
+        return $this;
+    }
+
+    /**
+     * @return array
+     */
+    public function getOption()
+    {
+        return $this->rule;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Components/Popover.php b/vendor/xaboy/form-builder/src/UI/Elm/Components/Popover.php
new file mode 100755
index 0000000..89ccea9
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Components/Popover.php
@@ -0,0 +1,55 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Components;
+
+
+use FormBuilder\Driver\CustomComponent;
+
+/**
+ * Class Popover
+ *
+ * @method $this trigger(string $trigger) 触发方式, 可选值: click/focus/hover/manual, 默认值: click
+ * @method $this popoverTitle(string $title) 标题
+ * @method $this content(string $content) 显示的内容,也可以通过 slot 传入 DOM
+ * @method $this width(string $width) 宽度, 默认值: 最小宽度 150px
+ * @method $this placement(string $placement) 出现位置, 可选值: top/top-start/top-end/bottom/bottom-start/bottom-end/left/left-start/left-end/right/right-start/right-end, 默认值: bottom
+ * @method $this disabled(boolean $disabled) Popover 是否可用, 默认值: false
+ * @method $this offset(number $offset) 出现位置的偏移量
+ * @method $this transition(string $transition) 定义渐变动画, 默认值: fade-in-linear
+ * @method $this visibleArrow(boolean $visibleArrow) 是否显示 Tooltip 箭头,更多参数可见Vue-popper, 默认值: true
+ * @method $this popperOptions(object $popperOptions) popper.js 的参数, 可选值: 参考 popper.js 文档, 默认值: {boundariesElement: 'body', gpuAcceleration: false}
+ * @method $this popperClass(string $popperClass) 为 popper 添加类名
+ * @method $this openDelay(number $openDelay) 触发方式为 hover 时的显示延迟,单位为毫秒
+ * @method $this closeDelay(float $closeDelay) 触发方式为 hover 时的隐藏延迟,单位为毫秒, 默认值: 200
+ * @method $this tabindex(float $tabindex) Popover 组件的 tabindex
+ */
+class Popover extends CustomComponent
+{
+    protected static $propsRule = [
+        'trigger' => 'string',
+        'title' => 'string',
+        'content' => 'string',
+        'width' => 'string',
+        'placement' => 'string',
+        'disabled' => 'boolean',
+        'popoverTitle' => ['string', 'title'],
+        'offset' => 'number',
+        'transition' => 'string',
+        'visibleArrow' => 'boolean',
+        'popperOptions' => 'object',
+        'popperClass' => 'string',
+        'openDelay' => 'number',
+        'closeDelay' => 'float',
+        'tabindex' => 'float',
+    ];
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Components/Radio.php b/vendor/xaboy/form-builder/src/UI/Elm/Components/Radio.php
new file mode 100755
index 0000000..ee36519
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Components/Radio.php
@@ -0,0 +1,69 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Components;
+
+
+use FormBuilder\Driver\FormOptionsComponent;
+use FormBuilder\Factory\Elm;
+
+/**
+ * 单选框组件
+ * Class Radio
+ *
+ * @method $this size(string $size) 单选框组尺寸,仅对按钮形式的 Radio 或带有边框的 Radio 有效, 可选值: medium / small / mini
+ * @method $this disabled(bool $disabled) 是否禁用, 默认值: false
+ * @method $this textColor(string $textColor) 按钮形式的 Radio 激活时的文本颜色, 默认值: #ffffff
+ * @method $this fill(string $fill) 按钮形式的 Radio 激活时的填充色和边框色, 默认值: #409EFF
+ */
+class Radio extends FormOptionsComponent
+{
+    protected $selectComponent = true;
+
+    protected static $propsRule = [
+        'size' => 'string',
+        'disabled' => 'bool',
+        'textColor' => 'string',
+        'fill' => 'string',
+    ];
+
+    public function createValidate()
+    {
+        return Elm::validateStr();
+    }
+
+    public function createValidateNum()
+    {
+        return Elm::validateNum();
+    }
+
+    public function requiredNum($message = '')
+    {
+        if (is_null($message)) $message = $this->getPlaceHolder();
+        return $this->appendValidate($this->createValidateNum()->message($message)->required());
+    }
+
+    /**
+     * 按钮样式
+     *
+     * @param bool $button
+     * @return $this
+     */
+    public function button($button = true)
+    {
+        if ($button)
+            $this->props['type'] = 'button';
+        else
+            unset($this->props['type']);
+        return $this;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Components/Rate.php b/vendor/xaboy/form-builder/src/UI/Elm/Components/Rate.php
new file mode 100755
index 0000000..7a46075
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Components/Rate.php
@@ -0,0 +1,69 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Components;
+
+
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Factory\Elm;
+
+/**
+ * 评分组件
+ * Class Rate
+ *
+ * @method $this max(float $max) 最大分值, 默认值: 5
+ * @method $this disabled(bool $disabled) 是否为只读, 默认值: false
+ * @method $this allowHalf(bool $allowHalf) 是否允许半选, 默认值: false
+ * @method $this lowThreshold(float $lowThreshold) 低分和中等分数的界限值,值本身被划分在低分中, 默认值: 2
+ * @method $this highThreshold(float $highThreshold) 高分和中等分数的界限值,值本身被划分在高分中, 默认值: 4
+ * @method $this colors(array $colors) icon 的颜色。若传入数组,共有 3 个元素,为 3 个分段所对应的颜色;若传入对象,可自定义分段,键名为分段的界限值,键值为对应的颜色, 默认值: ['#F7BA2A', '#F7BA2A', '#F7BA2A']
+ * @method $this voidColor(string $voidColor) 未选中 icon 的颜色, 默认值: #C6D1DE
+ * @method $this disabledVoidColor(string $disabledVoidColor) 只读时未选中 icon 的颜色, 默认值: #EFF2F7
+ * @method $this iconClasses(array $iconClasses) icon 的类名。若传入数组,共有 3 个元素,为 3 个分段所对应的类名;若传入对象,可自定义分段,键名为分段的界限值,键值为对应的类名, 默认值: ['el-icon-star-on', 'el-icon-star-on', 'el-icon-star-on']
+ * @method $this voidIconClass(string $voidIconClass) 未选中 icon 的类名, 默认值: el-icon-star-off
+ * @method $this disabledVoidIconClass(string $disabledVoidIconClass) 只读时未选中 icon 的类名, 默认值: el-icon-star-on
+ * @method $this showText(bool $showText) 是否显示辅助文字,若为真,则会从 texts 数组中选取当前分数对应的文字内容, 默认值: false
+ * @method $this showScore(bool $showScore) 是否显示当前分数,show-score 和 show-text 不能同时为真, 默认值: false
+ * @method $this textColor(string $textColor) 辅助文字的颜色, 默认值: #1F2D3D
+ * @method $this texts(array $texts) 辅助文字数组, 默认值: ['极差', '失望', '一般', '满意', '惊喜']
+ * @method $this scoreTemplate(string $scoreTemplate) 分数显示模板, 默认值: {value}
+ *
+ */
+class Rate extends FormComponent
+{
+    protected $selectComponent = true;
+
+    protected static $propsRule = [
+        'max' => 'float',
+        'disabled' => 'bool',
+        'allowHalf' => 'bool',
+        'lowThreshold' => 'float',
+        'highThreshold' => 'float',
+        'colors' => 'array',
+        'voidColor' => 'string',
+        'disabledVoidColor' => 'string',
+        'iconClasses' => 'array',
+        'voidIconClass' => 'string',
+        'disabledVoidIconClass' => 'string',
+        'showText' => 'bool',
+        'showScore' => 'bool',
+        'textColor' => 'string',
+        'texts' => 'array',
+        'scoreTemplate' => 'string',
+    ];
+
+    public function createValidate()
+    {
+        return Elm::validateNum();
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Components/Select.php b/vendor/xaboy/form-builder/src/UI/Elm/Components/Select.php
new file mode 100755
index 0000000..8fdb437
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Components/Select.php
@@ -0,0 +1,99 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Components;
+
+
+use FormBuilder\Driver\FormOptionsComponent;
+use FormBuilder\Factory\Elm;
+
+/**
+ * 选择器组件
+ * Class Select
+ *
+ * @method $this multiple(bool $multiple) 是否多选, 默认值: false
+ * @method $this disabled(bool $disabled) 是否禁用, 默认值: false
+ * @method $this valueKey(string $valueKey) 作为 value 唯一标识的键名,绑定值为对象类型时必填, 默认值: value
+ * @method $this size(string $size) 输入框尺寸, 可选值: medium/small/mini
+ * @method $this clearable(bool $clearable) 是否可以清空选项, 默认值: false
+ * @method $this collapseTags(bool $collapseTags) 多选时是否将选中值按文字的形式展示, 默认值: false
+ * @method $this multipleLimit(float $multipleLimit) 多选时用户最多可以选择的项目数,为 0 则不限制
+ * @method $this name(string $name) select input 的 name 属性
+ * @method $this autocomplete(string $autocomplete) select input 的 autocomplete 属性, 默认值: off
+ * @method $this placeholder(string $placeholder) 占位符, 默认值: 请选择
+ * @method $this filterable(bool $filterable) 是否可搜索, 默认值: false
+ * @method $this allowCreate(bool $allowCreate) 是否允许用户创建新条目,需配合 filterable 使用, 默认值: false
+ * @method $this remote(bool $remote) 是否为远程搜索, 默认值: false
+ * @method $this loading(bool $loading) 是否正在从远程获取数据, 默认值: false
+ * @method $this loadingText(string $loadingText) 远程加载时显示的文字, 默认值: 加载中
+ * @method $this noMatchText(string $noMatchText) 搜索条件无匹配时显示的文字,也可以使用slot = "empty"设置, 默认值: 无匹配数据
+ * @method $this noDataText(string $noDataText) 选项为空时显示的文字,也可以使用slot = "empty"设置, 默认值: 无数据
+ * @method $this popperClass(string $popperClass) Select 下拉框的类名
+ * @method $this reserveKeyword(bool $reserveKeyword) 多选且可搜索时,是否在选中一个选项后保留当前的搜索关键词, 默认值: false
+ * @method $this defaultFirstOption(bool $defaultFirstOption) 在输入框按下回车,选择第一个匹配项。需配合 filterable 或 remote 使用, 默认值: false
+ * @method $this popperAppendToBody(bool $popperAppendToBody) 是否将弹出框插入至 body 元素。在弹出框的定位出现问题时,可将该属性设置为 false, 默认值: true
+ * @method $this automaticDropdown(bool $automaticDropdown) 对于不可搜索的 Select,是否在输入框获得焦点后自动弹出选项菜单, 默认值: false
+ *
+ */
+class Select extends FormOptionsComponent
+{
+    protected $selectComponent = true;
+
+    protected $defaultProps = [
+        'multiple' => false
+    ];
+
+    protected static $propsRule = [
+        'multiple' => 'bool',
+        'disabled' => 'bool',
+        'valueKey' => 'string',
+        'size' => 'string',
+        'clearable' => 'bool',
+        'collapseTags' => 'bool',
+        'multipleLimit' => 'float',
+        'name' => 'string',
+        'autocomplete' => 'string',
+        'placeholder' => 'string',
+        'filterable' => 'bool',
+        'allowCreate' => 'bool',
+        'remote' => 'bool',
+        'loading' => 'bool',
+        'loadingText' => 'string',
+        'noMatchText' => 'string',
+        'noDataText' => 'string',
+        'popperClass' => 'string',
+        'reserveKeyword' => 'bool',
+        'defaultFirstOption' => 'bool',
+        'popperAppendToBody' => 'bool',
+        'automaticDropdown' => 'bool',
+    ];
+
+    public function createValidate()
+    {
+        if ($this->props['multiple'] == true)
+            return Elm::validateArr();
+        else
+            return Elm::validateStr();
+    }
+
+    public function createValidateNum()
+    {
+        return Elm::validateNum();
+    }
+
+    public function requiredNum($message = null)
+    {
+        if (is_null($message)) $message = $this->getPlaceHolder();
+        return $this->appendValidate($this->createValidateNum()->message($message)->required());
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Components/Slider.php b/vendor/xaboy/form-builder/src/UI/Elm/Components/Slider.php
new file mode 100755
index 0000000..1181188
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Components/Slider.php
@@ -0,0 +1,75 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Components;
+
+
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Factory\Elm;
+
+/**
+ * 滑块组件
+ * Class Slider
+ *
+ * @method $this min(float $min) 最小值
+ * @method $this max(float $max) 最大值, 默认值: 100
+ * @method $this disabled(bool $disabled) 是否禁用, 默认值: false
+ * @method $this step(float $step) 步长, 默认值: 1
+ * @method $this showInput(bool $showInput) 是否显示输入框,仅在非范围选择时有效, 默认值: false
+ * @method $this showInputControls(bool $showInputControls) 在显示输入框的情况下,是否显示输入框的控制按钮, 默认值: true
+ * @method $this inputSize(string $inputSize) 输入框的尺寸, 可选值: large / medium / small / mini, 默认值: small
+ * @method $this showStops(bool $showStops) 是否显示间断点, 默认值: false
+ * @method $this showTooltip(bool $showTooltip) 是否显示 tooltip, 默认值: true
+ * @method $this range(bool $range) 是否为范围选择, 默认值: false
+ * @method $this vertical(bool $vertical) 是否竖向模式, 默认值: false
+ * @method $this height(string $height) Slider 高度,竖向模式时必填
+ * @method $this label(string $label) 屏幕阅读器标签
+ * @method $this debounce(float $debounce) 输入时的去抖延迟,毫秒,仅在show-input等于true时有效, 默认值: 300
+ * @method $this tooltipClass(string $tooltipClass) tooltip 的自定义类名
+ * @method $this marks(array $marks) 标记, key 的类型必须为 number 且取值在闭区间 [min, max] 内,每个标记可以单独设置样式
+ *
+ */
+class Slider extends FormComponent
+{
+    protected $selectComponent = true;
+
+    protected $defaultProps = [
+        'range' => false
+    ];
+
+    protected static $propsRule = [
+        'min' => 'float',
+        'max' => 'float',
+        'disabled' => 'bool',
+        'step' => 'float',
+        'showInput' => 'bool',
+        'showInputControls' => 'bool',
+        'inputSize' => 'string',
+        'showStops' => 'bool',
+        'showTooltip' => 'bool',
+        'range' => 'bool',
+        'vertical' => 'bool',
+        'height' => 'string',
+        'label' => 'string',
+        'debounce' => 'float',
+        'tooltipClass' => 'string',
+        'marks' => 'array',
+    ];
+
+    public function createValidate()
+    {
+        if ($this->props['range'] == true)
+            return Elm::validateArr();
+        else
+            return Elm::validateNum();
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Components/Switches.php b/vendor/xaboy/form-builder/src/UI/Elm/Components/Switches.php
new file mode 100755
index 0000000..5827b8f
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Components/Switches.php
@@ -0,0 +1,75 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Components;
+
+
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Factory\Elm;
+
+/**
+ * 开关组件
+ * Class Switches
+ *
+ * @method $this disabled(bool $disabled) 是否禁用, 默认值: false
+ * @method $this width(float $width) switch 的宽度(像素), 默认值: 40
+ * @method $this activeIconClass(string $activeIconClass) switch 打开时所显示图标的类名,设置此项会忽略 active-text
+ * @method $this inactiveIconClass(string $inactiveIconClass) switch 关闭时所显示图标的类名,设置此项会忽略 inactive-text
+ * @method $this activeText(string $activeText) switch 打开时的文字描述
+ * @method $this inactiveText(string $inactiveText) switch 关闭时的文字描述
+ * @method $this activeValue(mixed $activeValue) switch 打开时的值, 默认值: true
+ * @method $this inactiveValue(mixed $inactiveValue) switch 关闭时的值, 默认值: false
+ * @method $this activeColor(string $activeColor) switch 打开时的背景色, 默认值: #409EFF
+ * @method $this inactiveColor(string $inactiveColor) switch 关闭时的背景色, 默认值: #C0CCDA
+ * @method $this name(string $name) switch 对应的 name 属性
+ * @method $this validateEvent(bool $validateEvent) 改变 switch 状态时是否触发表单的校验, 默认值: true
+ */
+class Switches extends FormComponent
+{
+    protected $selectComponent = true;
+
+    protected $defaultProps = [
+        'activeValue' => '1',
+        'inactiveValue' => '0',
+    ];
+
+    protected static $propsRule = [
+        'disabled' => 'bool',
+        'width' => 'float',
+        'activeIconClass' => 'string',
+        'inactiveIconClass' => 'string',
+        'activeText' => 'string',
+        'inactiveText' => 'string',
+        'activeValue' => '',
+        'inactiveValue' => '',
+        'activeColor' => 'string',
+        'inactiveColor' => 'string',
+        'name' => 'string',
+        'validateEvent' => 'bool',
+    ];
+
+    public function getComponentName()
+    {
+        return 'switch';
+    }
+
+    public function createValidate()
+    {
+        return Elm::validateStr();
+    }
+
+    public function createValidateNum()
+    {
+        return Elm::validateNum();
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Components/TimePicker.php b/vendor/xaboy/form-builder/src/UI/Elm/Components/TimePicker.php
new file mode 100755
index 0000000..1acc74e
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Components/TimePicker.php
@@ -0,0 +1,111 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Components;
+
+
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Factory\Elm;
+
+/**
+ * 时间选择器组件
+ * Class TimePicker
+ *
+ * @method $this readonly(bool $readonly) 完全只读, 默认值: false
+ * @method $this disabled(bool $disabled) 禁用, 默认值: false
+ * @method $this editable(bool $editable) 文本框可输入, 默认值: true
+ * @method $this clearable(bool $clearable) 是否显示清除按钮, 默认值: true
+ * @method $this size(string $size) 输入框尺寸, 可选值: medium / small / mini
+ * @method $this placeholder(string $placeholder) 非范围选择时的占位内容
+ * @method $this startPlaceholder(string $startPlaceholder) 范围选择时开始日期的占位内容
+ * @method $this endPlaceholder(string $endPlaceholder) 范围选择时开始日期的占位内容
+ * @method $this isRange(bool $isRange) 是否为时间范围选择,仅对<el-time-picker>有效, 默认值: false
+ * @method $this arrowControl(bool $arrowControl) 是否使用箭头进行时间选择,仅对<el-time-picker>有效, 默认值: false
+ * @method $this align(string $align) 对齐方式, 可选值: left / center / right, 默认值: left
+ * @method $this popperClass(string $popperClass) TimePicker 下拉框的类名
+ * @method $this pickerOptions(array $pickerOptions) 当前时间日期选择器特有的选项参考下表, 默认值: {}
+ * @method $this rangeSeparator(string $rangeSeparator) 选择范围时的分隔符, 默认值: '-'
+ * @method $this valueFormat(string $valueFormat) 可选,仅TimePicker时可用,绑定值的格式。不指定则绑定值为 Date 对象, 可选值: 见日期格式
+ * @method $this prefixIcon(string $prefixIcon) 自定义头部图标的类名, 默认值: el-icon-time
+ * @method $this clearIcon(string $clearIcon) 自定义清空图标的类名, 默认值: el-icon-circle-close
+ *
+ */
+class TimePicker extends FormComponent
+{
+    protected $selectComponent = true;
+
+    protected $defaultProps = [
+        'isRange' => false,
+        'editable' => false,
+    ];
+
+    protected static $propsRule = [
+        'readonly' => 'bool',
+        'disabled' => 'bool',
+        'isRange' => 'bool',
+        'editable' => 'bool',
+        'clearable' => 'bool',
+        'size' => 'string',
+        'placeholder' => 'string',
+        'startPlaceholder' => 'string',
+        'endPlaceholder' => 'string',
+        'arrowControl' => 'bool',
+        'align' => 'string',
+        'popperClass' => 'string',
+        'pickerOptions' => 'array',
+        'rangeSeparator' => 'string',
+        'valueFormat' => 'string',
+        'prefixIcon' => 'string',
+        'clearIcon' => 'string',
+    ];
+
+    /**
+     * 下拉列表的时间间隔,数组的三项分别对应小时、分钟、秒。
+     * 例如设置为 [1, 15] 时,分钟会显示:00、15、30、45。
+     *
+     * @param     $h
+     * @param int $i
+     * @param int $s
+     * @return $this
+     */
+    public function steps($h, $i = 0, $s = 0)
+    {
+        $this->props['steps'] = [$h, $i, $s];
+        return $this;
+    }
+
+    public function createValidate()
+    {
+        if ($this->props['isRange'])
+            return Elm::validateArr();
+        else
+            return Elm::validateStr();
+    }
+
+    public function required($message = null)
+    {
+        if (is_null($message)) $message = $this->getPlaceHolder();
+        $validate = $this->createValidate();
+        if ($this->props['isRange']) {
+            $required = ['required' => true, 'message' => $message];
+            $validate->fields([
+                '0' => $required,
+                '1' => $required
+            ]);
+            return $this;
+        }
+
+        $this->appendValidate($validate->message($message)->required());
+        return $this;
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Components/Tooltip.php b/vendor/xaboy/form-builder/src/UI/Elm/Components/Tooltip.php
new file mode 100755
index 0000000..866c21c
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Components/Tooltip.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Components;
+
+
+use FormBuilder\Driver\CustomComponent;
+
+/**
+ * Class Tooltip
+ *
+ * @method $this effect(string $effect) 默认提供的主题, 可选值: dark/light, 默认值: dark
+ * @method $this placement(string $placement) Tooltip 的出现位置, 可选值: top/top-start/top-end/bottom/bottom-start/bottom-end/left/left-start/left-end/right/right-start/right-end, 默认值: bottom
+ * @method $this disabled(bool $disabled) Tooltip 是否可用, 默认值: false
+ * @method $this offset(float $offset) 出现位置的偏移量
+ * @method $this transition(string $transition) 定义渐变动画, 默认值: el-fade-in-linear
+ * @method $this visibleArrow(bool $visibleArrow) 是否显示 Tooltip 箭头,更多参数可见Vue-popper, 默认值: true
+ * @method $this popperOptions(array $popperOptions) popper.js 的参数, 可选值: 参考 popper.js 文档, 默认值: {boundariesElement: 'body', gpuAcceleration: false}
+ * @method $this openDelay(float $openDelay) 延迟出现,单位毫秒
+ * @method $this manual(bool $manual) 手动控制模式,设置为 true 后,mouseenter 和 mouseleave 事件将不会生效, 默认值: false
+ * @method $this popperClass(string $popperClass) 为 Tooltip 的 popper 添加类名
+ * @method $this enterable(bool $enterable) 鼠标是否可进入到 tooltip 中, 默认值: true
+ * @method $this hideAfter(float $hideAfter) Tooltip 出现后自动隐藏延时,单位毫秒,为 0 则不会自动隐藏
+ * @method $this tabindex(float $tabindex) Tooltip 组件的 tabindex
+ */
+class Tooltip extends CustomComponent
+{
+    protected static $propsRule = [
+        'effect' => 'string',
+        'content' => 'string',
+        'placement' => 'string',
+        'value / vModel' => 'bool',
+        'disabled' => 'bool',
+        'offset' => 'float',
+        'transition' => 'string',
+        'visibleArrow' => 'bool',
+        'popperOptions' => 'array',
+        'openDelay' => 'float',
+        'manual' => 'bool',
+        'popperClass' => 'string',
+        'enterable' => 'bool',
+        'hideAfter' => 'float',
+        'tabindex' => 'float',
+    ];
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Components/Tree.php b/vendor/xaboy/form-builder/src/UI/Elm/Components/Tree.php
new file mode 100755
index 0000000..8ddbc5a
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Components/Tree.php
@@ -0,0 +1,108 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Components;
+
+
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Factory\Elm;
+
+/**
+ * 树型组件
+ * Class Tree
+ *
+ * @method $this type(string $type) 类型,可选值为 checked、selected
+ * @method $this emptyText(string $emptyText) 内容为空的时候展示的文本
+ * @method $this nodeKey(string $nodeKey) 每个树节点用来作为唯一标识的属性,整棵树应该是唯一的
+ * @method $this props(array $props) 配置选项,具体看下表
+ * @method $this renderAfterExpand(bool $renderAfterExpand) 是否在第一次展开某个树节点后才渲染其子节点, 默认值: true
+ * @method $this highlightCurrent(bool $highlightCurrent) 是否高亮当前选中节点,默认值是 false。, 默认值: false
+ * @method $this defaultExpandAll(bool $defaultExpandAll) 是否默认展开所有节点, 默认值: false
+ * @method $this expandOnClickNode(bool $expandOnClickNode) 是否在点击节点的时候展开或者收缩节点, 默认值为 true,如果为 false,则只有点箭头图标的时候才会展开或者收缩节点。, 默认值: true
+ * @method $this checkOnClickNode(bool $checkOnClickNode) 是否在点击节点的时候选中节点,默认值为 false,即只有在点击复选框时才会选中节点。, 默认值: false
+ * @method $this autoExpandParent(bool $autoExpandParent) 展开子节点的时候是否自动展开父节点, 默认值: true
+ * @method $this showCheckbox(bool $showCheckbox) 节点是否可被选择, 默认值: false
+ * @method $this checkStrictly(bool $checkStrictly) 在显示复选框的情况下,是否严格的遵循父子不互相关联的做法,默认为 false, 默认值: false
+ * @method $this accordion(bool $accordion) 是否每次只打开一个同级树节点展开, 默认值: false
+ * @method $this indent(float $indent) 相邻级节点间的水平缩进,单位为像素, 默认值: 16
+ * @method $this iconClass(string $iconClass) 自定义树节点的图标
+ * @method $this draggable(bool $draggable) 是否开启拖拽节点功能, 默认值: false
+ */
+class Tree extends FormComponent
+{
+    /**
+     * 选中
+     */
+    const TYPE_SELECTED = 'selected';
+    /**
+     * 选择
+     */
+    const TYPE_CHECKED = 'checked';
+
+
+    protected $selectComponent = true;
+
+    protected $defaultProps = [
+        'type' => self::TYPE_CHECKED,
+        'showCheckbox' => true,
+        'data' => []
+    ];
+
+    protected static $propsRule = [
+        'type' => 'string',
+        'emptyText' => 'string',
+        'nodeKey' => 'string',
+        'props' => 'array',
+        'renderAfterExpand' => 'bool',
+        'highlightCurrent' => 'bool',
+        'defaultExpandAll' => 'bool',
+        'expandOnClickNode' => 'bool',
+        'checkOnClickNode' => 'bool',
+        'autoExpandParent' => 'bool',
+        'showCheckbox' => 'bool',
+        'checkStrictly' => 'bool',
+        'accordion' => 'bool',
+        'indent' => 'float',
+        'iconClass' => 'string',
+        'draggable' => 'bool',
+    ];
+
+    /**
+     * @param array $treeData
+     * @return $this
+     */
+    public function data(array $treeData)
+    {
+        $this->props['data'] = [];
+        foreach ($treeData as $child) {
+            $this->props['data'][] = $child instanceof TreeData
+                ? $child->getOption()
+                : $child;
+        }
+        return $this;
+    }
+
+    /**
+     * @param string $var
+     * @return $this
+     */
+    public function jsData($var)
+    {
+        $this->props['data'] = 'js.' . (string)$var;
+        return $this;
+    }
+
+    public function createValidate()
+    {
+        return Elm::validateArr();
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Components/TreeData.php b/vendor/xaboy/form-builder/src/UI/Elm/Components/TreeData.php
new file mode 100755
index 0000000..0301897
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Components/TreeData.php
@@ -0,0 +1,106 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Components;
+
+
+use FormBuilder\Contract\OptionComponentInterface;
+use FormBuilder\Rule\CallPropsRule;
+
+/**
+ * Class TreeData
+ *
+ * @method $this id(string $id) Id, 必须唯一
+ * @method $this title(string $title) 标题
+ * @method $this expand(bool $bool) 是否展开直子节点, 默认为false
+ * @method $this disabled(bool $bool) 禁掉响应, 默认为false
+ * @method $this disableCheckbox(bool $bool) 禁掉 checkbox
+ * @method $this selected(bool $bool) 是否选中子节点
+ * @method $this checked(bool $bool) 是否勾选(如果勾选,子节点也会全部勾选)
+ */
+class TreeData implements OptionComponentInterface
+{
+    use CallPropsRule;
+
+    /**
+     * @var array
+     */
+    protected $children = [];
+
+    /**
+     * @var array
+     */
+    protected $props = [];
+
+    /**
+     * @var array
+     */
+    protected static $propsRule = [
+        'id' => 'string',
+        'title' => 'string',
+        'expand' => 'bool',
+        'disabled' => 'bool',
+        'disableCheckbox' => 'bool',
+        'selected' => 'bool',
+        'checked' => 'bool',
+    ];
+
+    /**
+     * TreeData constructor.
+     *
+     * @param       $id
+     * @param       $title
+     * @param array $children
+     */
+    public function __construct($id, $title, array $children = [])
+    {
+        $this->props['id'] = $id;
+        $this->props['title'] = $title;
+        $this->children = $children;
+    }
+
+    /**
+     * @param array $children
+     * @return $this
+     */
+    public function children(array $children)
+    {
+        $this->children = array_merge($this->children, $children);
+        return $this;
+    }
+
+    /**
+     * @param TreeData $child
+     * @return $this
+     */
+    public function child(TreeData $child)
+    {
+        $this->children[] = $child;
+        return $this;
+    }
+
+    /**
+     * @return array
+     */
+    public function getOption()
+    {
+        $children = [];
+        foreach ($this->children as $child) {
+            $children[] = $child instanceof TreeData
+                ? $child->getOption()
+                : $child;
+        }
+        $this->props['children'] = $children;
+        return $this->props;
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Components/Upload.php b/vendor/xaboy/form-builder/src/UI/Elm/Components/Upload.php
new file mode 100755
index 0000000..42d94cb
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Components/Upload.php
@@ -0,0 +1,106 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Components;
+
+
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Factory\Elm;
+
+/**
+ * 上传组件
+ * Class Upload
+ *
+ * @method $this uploadType(string $uploadType) 上传文件类型,可选值为 image(图片上传),file(文件上传)
+ * @method $this action(string $action) 必选参数,上传的地址
+ * @method $this multiple(bool $multiple) 是否支持多选文件
+ * @method $this uploadName(string $name) 上传的文件字段名, 默认值: file
+ * @method $this withCredentials(bool $withCredentials) 支持发送 cookie 凭证信息, 默认值: false
+ * @method $this accept(string $accept) 接受上传的文件类型(thumbnail-mode 模式下此参数无效)
+ * @method $this listType(string $listType) 文件列表的类型, 可选值: text/picture/picture-card, 默认值: text
+ * @method $this autoUpload(bool $autoUpload) 是否在选取文件后立即进行上传, 默认值: true
+ * @method $this disabled(bool $disabled) 是否禁用, 默认值: false
+ * @method $this limit(float $limit) 最大允许上传个数
+ *
+ */
+class Upload extends FormComponent
+{
+    /**
+     * file类型
+     */
+    const TYPE_FILE = 'file';
+
+    /**
+     * image类型
+     */
+    const TYPE_IMAGE = 'image';
+
+
+    protected $defaultProps = [
+        'limit' => 0,
+        'uploadType' => self::TYPE_FILE,
+        'headers' => [],
+        'data' => []
+    ];
+
+    protected static $propsRule = [
+        'uploadType' => 'string',
+        'action' => 'string',
+        'multiple' => 'bool',
+        'uploadName' => ['string', 'name'],
+        'withCredentials' => 'bool',
+        'accept' => 'string',
+        'listType' => 'string',
+        'autoUpload' => 'bool',
+        'disabled' => 'bool',
+        'limit' => 'float',
+    ];
+
+    protected function init()
+    {
+        $this->name($this->field);
+    }
+
+    /**
+     * 设置上传的请求头部
+     *
+     * @param array $headers
+     * @return $this
+     */
+    public function headers(array $headers)
+    {
+        $this->props['headers'] = array_merge($this->props['headers'], $headers);
+        return $this;
+    }
+
+    /**
+     * 上传时附带的额外参数
+     *
+     * @param array $data
+     * @return $this
+     */
+    public function data(array $data)
+    {
+        $this->props['data'] = array_merge($this->props['data'], $data);
+        return $this;
+    }
+
+    protected function getPlaceHolder()
+    {
+        return '请上传' . $this->title;
+    }
+
+    public function createValidate()
+    {
+        return $this->props['limit'] == 1 ? Elm::validateStr() : Elm::validateArr();
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Config.php b/vendor/xaboy/form-builder/src/UI/Elm/Config.php
new file mode 100755
index 0000000..a32d187
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Config.php
@@ -0,0 +1,218 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm;
+
+
+use FormBuilder\Contract\ConfigInterface;
+use FormBuilder\UI\Elm\Components\Button;
+use FormBuilder\UI\Elm\Components\Popover;
+use FormBuilder\UI\Elm\Components\Tooltip;
+use FormBuilder\UI\Elm\Style\FormStyle;
+use FormBuilder\UI\Elm\Style\Row;
+
+class Config implements ConfigInterface
+{
+    const INFO_TYPE_POPOVER = 'popover';
+
+    const INFO_TYPE_TOOLTIP = 'tooltip';
+
+    protected $config;
+
+    public function __construct(array $config = [])
+    {
+        $this->config = $config;
+    }
+
+    public function info($type)
+    {
+        if (is_array($type) || $type instanceof Popover || $type instanceof Tooltip)
+            $this->config['info'] = $type;
+
+        return $this;
+    }
+
+    /**
+     * @param string $type tooltip,poptip
+     * @return Popover|Tooltip
+     */
+    public function createInfo($type = self::INFO_TYPE_POPOVER)
+    {
+        if (strtolower($type) === self::INFO_TYPE_TOOLTIP)
+            $info = new Tooltip();
+        else
+            $info = new Popover();
+        $this->info($info);
+        return $info;
+    }
+
+    /**
+     * 表单整体显示规则配置
+     *
+     * @param FormStyle|array $formStyle
+     * @return $this
+     */
+    public function formStyle($formStyle)
+    {
+        $this->config['form'] = $formStyle;
+        return $this;
+    }
+
+    public function createFormStyle(array $rule = [])
+    {
+        $formStyle = new FormStyle($rule);
+        $this->formStyle($formStyle);
+        return $formStyle;
+    }
+
+    /**
+     * 表单组件布局配置
+     *
+     * @param Row|array $row
+     * @return $this
+     */
+    public function row($row)
+    {
+        $this->config['row'] = $row;
+        return $this;
+    }
+
+    public function createRow(array $rule = [])
+    {
+        $row = new Row($rule);
+        $this->row($row);
+        return $row;
+    }
+
+    /**
+     * 提交按钮样式和布局配置
+     *
+     * @param Button|array|bool $btn
+     * @return $this
+     */
+    public function submitBtn($btn)
+    {
+        $this->config['submitBtn'] = $btn;
+        return $this;
+    }
+
+    public function createSubmitBtn()
+    {
+        $submitBtn = new Button();
+        $this->submitBtn($submitBtn);
+        return $submitBtn;
+    }
+
+    /**
+     * 开启事件注入
+     *
+     * @param bool $bool
+     * @return $this
+     */
+    public function injectEvent($bool)
+    {
+        $this->config['injectEvent'] = !!$bool;
+        return $this;
+    }
+
+    /**
+     * 重置按钮样式和布局配置
+     *
+     * @param Button|array|bool $btn
+     * @return $this
+     */
+    public function resetBtn($btn)
+    {
+        $this->config['resetBtn'] = $btn;
+        return $this;
+    }
+
+    /**
+     * @return Button
+     */
+    public function createResetBtn()
+    {
+        $resetBtn = new Button();
+        $this->resetBtn($resetBtn);
+        return $resetBtn;
+    }
+
+    /**
+     * @param Button $btn
+     * @return mixed
+     */
+    protected function parseButton(Button $btn)
+    {
+        $rule = $btn->build();
+        if (isset($rule['col']))
+            $rule['props']->col = $rule['col'];
+        return $rule['props'];
+    }
+
+    /**
+     * @param $info
+     * @return mixed
+     */
+    protected function parseInfo($info)
+    {
+        if ($info instanceof Popover || $info instanceof Tooltip) {
+            $rule = $info->build();
+            $info = $rule['props'];
+            $info->type = $rule['type'];
+        }
+
+        return $info;
+    }
+
+    /**
+     * @return array
+     */
+    public function getConfig()
+    {
+        $config = $this->config;
+        if (isset($config['form']) && ($form = $config['form']) instanceof FormStyle)
+            $config['form'] = $form->getStyle();
+        if (isset($config['row']) && ($row = $config['row']) instanceof Row)
+            $config['row'] = $row->getStyle();
+        if (isset($config['submitBtn']) && ($submitBtn = $config['submitBtn']) instanceof Button)
+            $config['submitBtn'] = $this->parseButton($submitBtn);
+        if (isset($config['resetBtn']) && ($resetBtn = $config['resetBtn']) instanceof Button)
+            $config['resetBtn'] = $this->parseButton($resetBtn);
+        if (isset($config['info']))
+            $config['info'] = $this->parseInfo($config['info']);
+
+        return $config;
+    }
+
+
+    /**
+     * @param string $componentName
+     * @param array $config
+     * @return $this
+     */
+    public function componentGlobalConfig($componentName, array $config)
+    {
+        if (!isset($this->config['global'])) $this->config['global'] = [];
+        $this->config['global'][$componentName] = $config;
+        return $this;
+    }
+
+
+    /**
+     * @param array $config
+     * @return $this
+     */
+    public function componentGlobalCommonConfig(array $config)
+    {
+        return $this->componentGlobalConfig('*', $config);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Style/Col.php b/vendor/xaboy/form-builder/src/UI/Elm/Style/Col.php
new file mode 100755
index 0000000..3373733
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Style/Col.php
@@ -0,0 +1,166 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Style;
+
+
+use FormBuilder\Contract\ColComponentInterface;
+
+/**
+ * col栅格规则
+ *
+ * Class Col
+ * @package FormBuilder\Style
+ */
+class Col implements ColComponentInterface
+{
+    protected $rule;
+
+    public function __construct(array $rule = [])
+    {
+        $this->rule = $rule;
+    }
+
+    /**
+     * 栅格的占位格数,可选值为0~24的整数,为 0 时,相当于display:none
+     *
+     * @param int|string $span
+     * @return $this
+     */
+    public function span($span)
+    {
+        $this->rule['span'] = $span;
+        return $this;
+    }
+
+    /**
+     * 设置表单域 label 的宽度
+     *
+     * @param int|string $labelWidth
+     * @return $this
+     */
+    public function labelWidth($labelWidth)
+    {
+        $this->rule['labelWidth'] = $labelWidth;
+        return $this;
+    }
+
+    /**
+     * 栅格左侧的间隔格数,间隔内不可以有栅格
+     *
+     * @param $offset
+     * @return $this
+     */
+    public function offset($offset)
+    {
+        $this->rule['offset'] = $offset;
+        return $this;
+    }
+
+    /**
+     * 栅格向右移动格数
+     *
+     * @param $push
+     * @return $this
+     */
+    public function push($push)
+    {
+        $this->rule['push'] = $push;
+        return $this;
+    }
+
+    /**
+     * 栅格向左移动格数
+     *
+     * @param $pull
+     * @return $this
+     */
+    public function pull($pull)
+    {
+        $this->rule['pull'] = $pull;
+        return $this;
+    }
+
+    /**
+     * 自定义元素标签
+     *
+     * @param string $tag
+     * @return $this
+     */
+    public function tag($tag)
+    {
+        $this->rule['tag'] = (string)$tag;
+        return $this;
+    }
+
+    /**
+     * <768px 响应式栅格,可为栅格数或一个包含其他属性的对象
+     *
+     * @param int|self $xs
+     * @return $this
+     */
+    public function xs($xs)
+    {
+        $this->rule['xs'] = $this->buildGrid($xs);
+        return $this;
+    }
+
+    /**
+     * ≥768px 响应式栅格,可为栅格数或一个包含其他属性的对象
+     *
+     * @param int|self $sm
+     * @return $this
+     */
+    public function sm($sm)
+    {
+        $this->rule['sm'] = $this->buildGrid($sm);
+        return $this;
+    }
+
+    /**
+     * ≥992px 响应式栅格,可为栅格数或一个包含其他属性的对象
+     *
+     * @param int|self $md
+     * @return $this
+     */
+    public function md($md)
+    {
+        $this->rule['md'] = $this->buildGrid($md);
+        return $this;
+    }
+
+    /**
+     * ≥1200px 响应式栅格,可为栅格数或一个包含其他属性的对象
+     *
+     * @param int|self $lg
+     * @return $this
+     */
+    public function lg($lg)
+    {
+        $this->rule['lg'] = $this->buildGrid($lg);
+        return $this;
+    }
+
+    protected function buildGrid($grid)
+    {
+        return $grid instanceof self ? $grid->getCol() : (int)$grid;
+    }
+
+    /**
+     * @return object
+     */
+    public function getCol()
+    {
+        return (object)$this->rule;
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Style/FormStyle.php b/vendor/xaboy/form-builder/src/UI/Elm/Style/FormStyle.php
new file mode 100755
index 0000000..a047b0a
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Style/FormStyle.php
@@ -0,0 +1,86 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Style;
+
+
+use FormBuilder\Contract\StyleInterface;
+use FormBuilder\Rule\CallPropsRule;
+
+/**
+ * form表单样式
+ * Class FormStyle
+ *
+ * @method $this inline(bool $inline) 行内表单模式, 默认值: false
+ * @method $this labelPosition(string $labelPosition) 表单域标签的位置,如果值为 left 或者 right 时,则需要设置 label-width, 可选值: right/left/top, 默认值: right
+ * @method $this labelWidth(string $labelWidth) 表单域标签的宽度,例如 '50px'。作为 Form 直接子元素的 form-item 会继承该值。支持 auto。
+ * @method $this labelSuffix(string $labelSuffix) 表单域标签的后缀
+ * @method $this hideRequiredAsterisk(bool $hideRequiredAsterisk) 是否显示必填字段的标签旁边的红色星号, 默认值: false
+ * @method $this showMessage(bool $showMessage) 是否显示校验错误信息, 默认值: true
+ * @method $this inlineMessage(bool $inlineMessage) 是否以行内形式展示校验信息, 默认值: false
+ * @method $this statusIcon(bool $statusIcon) 是否在输入框中显示校验结果反馈图标, 默认值: false
+ * @method $this validateOnRuleChange(bool $validateOnRuleChange) 是否在 rules 属性改变后立即触发一次验证, 默认值: true
+ * @method $this size(string $size) 用于控制该表单内组件的尺寸, 可选值: medium / small / mini
+ * @method $this disabled(bool $disabled) 是否禁用该表单内的所有组件。若设置为 true,则表单内组件上的 disabled 属性不再生效, 默认值: false
+ */
+class FormStyle implements StyleInterface
+{
+    use CallPropsRule;
+
+    /**
+     * @var array
+     */
+    protected $props;
+
+    protected static $propsRule = [
+        'inline' => 'bool',
+        'labelPosition' => 'string',
+        'labelWidth' => 'string',
+        'labelSuffix' => 'string',
+        'hideRequiredAsterisk' => 'bool',
+        'showMessage' => 'bool',
+        'inlineMessage' => 'bool',
+        'statusIcon' => 'bool',
+        'validateOnRuleChange' => 'bool',
+        'size' => 'string',
+        'disabled' => 'bool',
+    ];
+
+    /**
+     * FormStyle constructor.
+     * @param array $rule
+     */
+    public function __construct(array $rule = [])
+    {
+        $this->props = $rule;
+    }
+
+    /**
+     * 设置表单 class
+     * @param $class
+     * @return $this
+     */
+    public function className($class)
+    {
+        $this->props['className'] = $class;
+        return $this;
+    }
+
+    /**
+     * @return object
+     */
+    public function getStyle()
+    {
+        return (object)$this->props;
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Style/Row.php b/vendor/xaboy/form-builder/src/UI/Elm/Style/Row.php
new file mode 100755
index 0000000..d33358d
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Style/Row.php
@@ -0,0 +1,90 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Style;
+
+
+use FormBuilder\Contract\StyleInterface;
+
+/**
+ * row栅格规则
+ *
+ * Class Row
+ */
+class Row implements StyleInterface
+{
+    protected $rule;
+
+    public function __construct(array $rule = [])
+    {
+        $this->rule = $rule;
+    }
+
+    /**
+     * 栅格间距,单位 px,左右平分
+     *
+     * @param int $gutter
+     */
+    public function gutter($gutter)
+    {
+        $this->rule['gutter'] = (float)$gutter;
+    }
+
+    /**
+     * 布局模式,可选值为flex或不选,在现代浏览器下有效
+     *
+     * @param string $type
+     */
+    public function type($type)
+    {
+        $this->rule['type'] = $type;
+    }
+
+    /**
+     * flex 布局下的垂直对齐方式,可选值为top、middle、bottom
+     *
+     * @param string $align
+     */
+    public function align($align)
+    {
+        $this->rule['align'] = $align;
+    }
+
+    /**
+     * flex 布局下的水平排列方式,可选值为start、end、center、space-around、space-between
+     *
+     * @param string $justify
+     */
+    public function justify($justify)
+    {
+        $this->rule['justify'] = $justify;
+    }
+
+    /**
+     * 自定义元素标签
+     *
+     * @param string $tag
+     */
+    public function tag($tag)
+    {
+        $this->rule['tag'] = $tag;
+    }
+
+    /**
+     * @return object
+     */
+    public function getStyle()
+    {
+        return (object)$this->rule;
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Traits/CascaderFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Elm/Traits/CascaderFactoryTrait.php
new file mode 100755
index 0000000..f0346cb
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Traits/CascaderFactoryTrait.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Traits;
+
+
+use FormBuilder\UI\Elm\Components\Cascader;
+
+trait CascaderFactoryTrait
+{
+    /**
+     * 多级联动组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param array $value
+     * @param string $type
+     * @return Cascader
+     */
+    public static function cascader($field, $title, array $value = [], $type = Cascader::TYPE_OTHER)
+    {
+        $cascader = new Cascader($field, $title, $value);
+        $cascader->type($type);
+        return $cascader;
+    }
+
+
+    /**
+     * 省市二级联动
+     *
+     * @param string $field
+     * @param string $title
+     * @param array|string $province
+     * @param string $city
+     * @return Cascader
+     */
+    public static function city($field, $title, $province = [], $city = '')
+    {
+        if (is_array($province))
+            $value = $province;
+        else
+            $value = [(string)$province, (string)$city];
+
+        $cascader = self::cascader($field, $title, $value, Cascader::TYPE_CITY);
+        $cascader->jsOptions('province_city');
+        return $cascader;
+    }
+
+
+    /**
+     * 省市区三级联动
+     *
+     * @param string $field
+     * @param string $title
+     * @param array|string $province
+     * @param string $city
+     * @param string $area
+     * @return Cascader
+     */
+    public static function cityArea($field, $title, $province = [], $city = '', $area = '')
+    {
+        if (is_array($province))
+            $value = $province;
+        else
+            $value = [(string)$province, (string)$city, (string)$area];
+
+        $cascader = self::cascader($field, $title, $value, Cascader::TYPE_CITY_AREA);
+        $cascader->jsOptions('province_city_area');
+        return $cascader;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Traits/CheckBoxFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Elm/Traits/CheckBoxFactoryTrait.php
new file mode 100755
index 0000000..092e77e
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Traits/CheckBoxFactoryTrait.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Traits;
+
+use FormBuilder\UI\Elm\Components\Checkbox;
+
+trait CheckBoxFactoryTrait
+{
+    /**
+     * 多选框组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param array $value
+     * @return Checkbox
+     */
+    public static function checkbox($field, $title, array $value = [])
+    {
+        return new Checkbox($field, $title, $value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Traits/ColorPickerFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Elm/Traits/ColorPickerFactoryTrait.php
new file mode 100755
index 0000000..36dbc9d
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Traits/ColorPickerFactoryTrait.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Traits;
+
+
+use FormBuilder\UI\Elm\Components\ColorPicker;
+
+trait ColorPickerFactoryTrait
+{
+    /**
+     * 颜色选择组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $value
+     * @return ColorPicker
+     */
+    public static function color($field, $title, $value = '')
+    {
+        return new ColorPicker($field, $title, (string)$value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Traits/DatePickerFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Elm/Traits/DatePickerFactoryTrait.php
new file mode 100755
index 0000000..431c791
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Traits/DatePickerFactoryTrait.php
@@ -0,0 +1,141 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Traits;
+
+
+use FormBuilder\UI\Elm\Components\DatePicker;
+
+trait DatePickerFactoryTrait
+{
+    /**
+     * 日期组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $value
+     * @param string $type
+     * @return DatePicker
+     */
+    public static function datePicker($field, $title, $value = '', $type = DatePicker::TYPE_DATE)
+    {
+        $datePicker = new DatePicker($field, $title, $value);
+        return $datePicker->type($type);
+    }
+
+    /**
+     * 单选日期
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $value
+     * @return DatePicker
+     */
+    public static function date($field, $title, $value = '')
+    {
+        return self::datePicker($field, $title, (string)$value, DatePicker::TYPE_DATE);
+    }
+
+    /**
+     * 多选日期
+     *
+     * @param string $field
+     * @param string $title
+     * @param array $value
+     * @return DatePicker
+     */
+    public static function dates($field, $title, array $value)
+    {
+        return self::datePicker($field, $title, $value, DatePicker::TYPE_DATES);
+    }
+
+    /**
+     * 日期区间选择
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $startDate
+     * @param string $endDate
+     * @return DatePicker
+     */
+    public static function dateRange($field, $title, $startDate = '', $endDate = '')
+    {
+        return self::datePicker($field, $title, [(string)$startDate, (string)$endDate], DatePicker::TYPE_DATE_RANGE);
+    }
+
+    /**
+     * 单选日期时间
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $value
+     * @return DatePicker
+     */
+    public static function dateTime($field, $title, $value = '')
+    {
+        return self::datePicker($field, $title, (string)$value, DatePicker::TYPE_DATE_TIME);
+    }
+
+    /**
+     * 日期时间区间选择
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $startDate
+     * @param string $endDate
+     * @return DatePicker
+     */
+    public static function dateTimeRange($field, $title, $startDate = '', $endDate = '')
+    {
+        return self::datePicker($field, $title, [(string)$startDate, (string)$endDate], DatePicker::TYPE_DATE_TIME_RANGE);
+    }
+
+    /**
+     * 选择年
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $value
+     * @return DatePicker
+     */
+    public static function year($field, $title, $value = '')
+    {
+        return self::datePicker($field, $title, (string)$value, DatePicker::TYPE_YEAR);
+    }
+
+    /**
+     * 选择月
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $value
+     * @return DatePicker
+     */
+    public static function month($field, $title, $value = '')
+    {
+        return self::datePicker($field, $title, (string)$value, DatePicker::TYPE_MONTH);
+    }
+
+    /**
+     * 月区间选择
+     *
+     * @param $field
+     * @param $title
+     * @param string $startDate
+     * @param string $endDate
+     * @return DatePicker
+     */
+    public static function monthRange($field, $title, $startDate = '', $endDate = '')
+    {
+        return self::datePicker($field, $title, [(string)$startDate, (string)$endDate], DatePicker::TYPE_MONTH_RANGE);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Traits/FormStyleFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Elm/Traits/FormStyleFactoryTrait.php
new file mode 100755
index 0000000..31aee75
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Traits/FormStyleFactoryTrait.php
@@ -0,0 +1,55 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Traits;
+
+
+use FormBuilder\UI\Elm\Style\Col;
+use FormBuilder\UI\Elm\Style\FormStyle;
+use FormBuilder\UI\Elm\Style\Row;
+
+trait FormStyleFactoryTrait
+{
+
+    /**
+     * 组件布局规则类
+     *
+     * @param array $rule
+     * @return Col
+     */
+    public static function col(array $rule = [])
+    {
+        return new Col($rule);
+    }
+
+    /**
+     * 表格布局规则类
+     *
+     * @param $rule
+     * @return Row
+     */
+    public static function row(array $rule = [])
+    {
+        return new Row($rule);
+    }
+
+    /**
+     * 表格样式类
+     *
+     * @param array $rule
+     * @return FormStyle
+     */
+    public static function style(array $rule = [])
+    {
+        return new FormStyle($rule);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Traits/FrameFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Elm/Traits/FrameFactoryTrait.php
new file mode 100755
index 0000000..51d3a4d
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Traits/FrameFactoryTrait.php
@@ -0,0 +1,126 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Traits;
+
+
+use FormBuilder\UI\Elm\Components\Frame;
+
+trait FrameFactoryTrait
+{
+    /**
+     * 框架组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $src
+     * @param string|array $value
+     * @param string $type
+     * @return Frame
+     */
+    public static function frame($field, $title, $src, $value = [], $type = Frame::TYPE_INPUT)
+    {
+        $length = is_array($value) ? 0 : 1;
+        $frame = new Frame($field, $title, $value);
+        return $frame->maxLength($length)->src($src)->type($type);
+    }
+
+    /**
+     * 使用input类型显示,多选
+     * value为Array类型
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $src
+     * @param array $value
+     * @return Frame
+     */
+    public static function frameInputs($field, $title, $src, array $value = [])
+    {
+        return self::frame($field, $title, $src, $value);
+    }
+
+    /**
+     * 使用文件类型显示,多选
+     * value为Array类型
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $src
+     * @param array $value
+     * @return Frame
+     */
+    public static function frameFiles($field, $title, $src, array $value = [])
+    {
+        return self::frame($field, $title, $src, $value, Frame::TYPE_FILE);
+    }
+
+    /**
+     * 使用图片类型显示,多选
+     * value为Array类型
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $src
+     * @param array $value
+     * @return Frame
+     */
+    public static function frameImages($field, $title, $src, array $value = [])
+    {
+        return self::frame($field, $title, $src, $value, Frame::TYPE_IMAGE);
+    }
+
+    /**
+     * 使用input类型显示,单选
+     * value为string类型
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $src
+     * @param string $value
+     * @return Frame
+     */
+    public static function frameInput($field, $title, $src, $value = '')
+    {
+        return self::frame($field, $title, $src, (string)$value);
+    }
+
+    /**
+     * 使用文件类型显示,单选
+     * value为string类型
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $src
+     * @param string $value
+     * @return Frame
+     */
+    public static function frameFile($field, $title, $src, $value = '')
+    {
+        return self::frame($field, $title, $src, (string)$value, Frame::TYPE_FILE);
+    }
+
+    /**
+     * 使用图片类型显示,单选
+     * value为string类型
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $src
+     * @param string $value
+     * @return Frame
+     */
+    public static function frameImage($field, $title, $src, $value = '')
+    {
+        return self::frame($field, $title, $src, (string)$value, Frame::TYPE_IMAGE);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Traits/GroupFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Elm/Traits/GroupFactoryTrait.php
new file mode 100755
index 0000000..cd17eae
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Traits/GroupFactoryTrait.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Traits;
+
+
+use FormBuilder\UI\Elm\Components\Group;
+
+trait GroupFactoryTrait
+{
+
+    /**
+     * 数组组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param array $value
+     * @return Group
+     */
+    public static function group($field, $title, $value = [])
+    {
+        return new Group($field, $title, $value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Traits/HiddenFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Elm/Traits/HiddenFactoryTrait.php
new file mode 100755
index 0000000..33fa3c8
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Traits/HiddenFactoryTrait.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Traits;
+
+
+use FormBuilder\UI\Elm\Components\Hidden;
+
+trait HiddenFactoryTrait
+{
+    /**
+     * 隐藏组件
+     *
+     * @param string $field
+     * @param mixed $value
+     * @return Hidden
+     */
+    public static function hidden($field, $value)
+    {
+        return new Hidden($field, $value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Traits/InputFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Elm/Traits/InputFactoryTrait.php
new file mode 100755
index 0000000..2304c61
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Traits/InputFactoryTrait.php
@@ -0,0 +1,112 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Traits;
+
+
+use FormBuilder\UI\Elm\Components\Input;
+
+trait InputFactoryTrait
+{
+    /**
+     * input输入框组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $value
+     * @param string $type
+     * @return Input
+     */
+    public static function input($field, $title, $value = '', $type = Input::TYPE_TEXT)
+    {
+        $input = new Input($field, $title, (string)$value);
+        return $input->type($type);
+    }
+
+    /**
+     * text 类型输入框
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $value
+     * @return Input
+     */
+    public static function text($field, $title, $value = '')
+    {
+        return self::input($field, $title, $value);
+    }
+
+    /**
+     * password 类型输入框
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $value
+     * @return Input
+     */
+    public static function password($field, $title, $value = '')
+    {
+        return self::input($field, $title, $value, Input::TYPE_PASSWORD);
+    }
+
+    /**
+     * textarea 类型输入框
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $value
+     * @return Input
+     */
+    public static function textarea($field, $title, $value = '')
+    {
+        return self::input($field, $title, $value, Input::TYPE_TEXTAREA);
+    }
+
+    /**
+     * url 类型输入框
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $value
+     * @return Input
+     */
+    public static function url($field, $title, $value = '')
+    {
+        return self::input($field, $title, $value, Input::TYPE_URL);
+    }
+
+    /**
+     * email 类型输入框
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $value
+     * @return Input
+     */
+    public static function email($field, $title, $value = '')
+    {
+        return self::input($field, $title, $value, Input::TYPE_EMAIL);
+    }
+
+    /**
+     * date 类型输入框
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $value
+     * @return Input
+     */
+    public static function idate($field, $title, $value = '')
+    {
+        return self::input($field, $title, $value, Input::TYPE_DATE);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Traits/InputNumberFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Elm/Traits/InputNumberFactoryTrait.php
new file mode 100755
index 0000000..0918984
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Traits/InputNumberFactoryTrait.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Traits;
+
+
+use FormBuilder\UI\Elm\Components\InputNumber;
+
+trait InputNumberFactoryTrait
+{
+    /**
+     * 数字输入框组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param null|number $value
+     * @return InputNumber
+     */
+    public static function number($field, $title, $value = null)
+    {
+        return new InputNumber($field, $title, $value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Traits/RadioFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Elm/Traits/RadioFactoryTrait.php
new file mode 100755
index 0000000..6ce1591
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Traits/RadioFactoryTrait.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Traits;
+
+
+use FormBuilder\UI\Elm\Components\Radio;
+
+trait RadioFactoryTrait
+{
+    /**
+     * 单选框组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param mixed $value
+     * @return Radio
+     */
+    public static function radio($field, $title, $value = '')
+    {
+        return new Radio($field, $title, $value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Traits/RateFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Elm/Traits/RateFactoryTrait.php
new file mode 100755
index 0000000..c2e08b5
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Traits/RateFactoryTrait.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Traits;
+
+
+use FormBuilder\UI\Elm\Components\Rate;
+
+trait RateFactoryTrait
+{
+    /**
+     * 评分选择组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param int|float $value
+     * @return Rate
+     */
+    public static function rate($field, $title, $value = 0)
+    {
+        return new Rate($field, $title, (float)$value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Traits/SelectFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Elm/Traits/SelectFactoryTrait.php
new file mode 100755
index 0000000..27e8628
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Traits/SelectFactoryTrait.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Traits;
+
+
+use FormBuilder\UI\Elm\Components\Select;
+
+trait SelectFactoryTrait
+{
+    /**
+     * 下拉选择组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param mixed $value
+     * @return Select
+     */
+    public static function select($field, $title, $value = '')
+    {
+        $multiple = is_array($value) ? true : false;
+        $select = new Select($field, $title, $value);
+        $select->multiple($multiple);
+        return $select;
+    }
+
+    /**
+     * 多选
+     *
+     * @param string $field
+     * @param string $title
+     * @param array $value
+     * @return Select
+     */
+    public static function selectMultiple($field, $title, array $value = [])
+    {
+        return self::select($field, $title, $value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Traits/SliderFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Elm/Traits/SliderFactoryTrait.php
new file mode 100755
index 0000000..6f66599
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Traits/SliderFactoryTrait.php
@@ -0,0 +1,50 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Traits;
+
+
+use FormBuilder\UI\Elm\Components\Slider;
+
+trait SliderFactoryTrait
+{
+    /**
+     * 滑块组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param int|array $value
+     * @return Slider
+     */
+    public static function slider($field, $title, $value = 0)
+    {
+        $slider = new Slider($field, $title, $value);
+        if (is_array($value)) $slider->range(true);
+        return $slider;
+    }
+
+    /**
+     * 区间选择
+     *
+     * @param string $field
+     * @param string $title
+     * @param int $start
+     * @param int $end
+     * @return Slider
+     */
+    public static function sliderRange($field, $title, $start = 0, $end = 0)
+    {
+        $slider = self::slider($field, $title, [(int)$start, (int)$end]);
+        $slider->range(true);
+        return $slider;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Traits/SwitchesFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Elm/Traits/SwitchesFactoryTrait.php
new file mode 100755
index 0000000..7343083
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Traits/SwitchesFactoryTrait.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Traits;
+
+
+use FormBuilder\UI\Elm\Components\Switches;
+
+trait SwitchesFactoryTrait
+{
+    /**
+     * 开关组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param mixed $value
+     * @return Switches
+     */
+    public static function switches($field, $title, $value = '0')
+    {
+        return new Switches($field, $title, $value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Traits/TimePickerFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Elm/Traits/TimePickerFactoryTrait.php
new file mode 100755
index 0000000..921871f
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Traits/TimePickerFactoryTrait.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Traits;
+
+
+use FormBuilder\UI\Elm\Components\TimePicker;
+
+trait TimePickerFactoryTrait
+{
+    /**
+     * 时间选择组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $value
+     * @param bool $isRange
+     * @return TimePicker
+     */
+    public static function timePicker($field, $title, $value = '', $isRange = false)
+    {
+        $timePicker = new TimePicker($field, $title, $value);
+        return $timePicker->isRange(!!$isRange);
+    }
+
+    /**
+     * 时间选择
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $value
+     * @return TimePicker
+     */
+    public static function time($field, $title, $value = '')
+    {
+        return self::timePicker($field, $title, (string)$value);
+    }
+
+    /**
+     * 时间区间选择
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $startTime
+     * @param string $endTime
+     * @return TimePicker
+     */
+    public static function timeRange($field, $title, $startTime = '', $endTime = '')
+    {
+        return self::timePicker($field, $title, [(string)$startTime, (string)$endTime], true);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Traits/TreeFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Elm/Traits/TreeFactoryTrait.php
new file mode 100755
index 0000000..b979d00
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Traits/TreeFactoryTrait.php
@@ -0,0 +1,74 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Traits;
+
+
+use FormBuilder\UI\Elm\Components\Tree;
+use FormBuilder\UI\Elm\Components\TreeData;
+
+trait TreeFactoryTrait
+{
+    /**
+     * 树形组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param mixed $value
+     * @param string $type
+     * @return Tree
+     */
+    public static function tree($field, $title, $value = [], $type = Tree::TYPE_CHECKED)
+    {
+        $tree = new Tree($field, $title, $value);
+        return $tree->type($type);
+    }
+
+    /**
+     * 获取选中的值
+     *
+     * @param string $field
+     * @param string $title
+     * @param mixed $value
+     * @return Tree
+     */
+    public static function treeSelected($field, $title, $value = [])
+    {
+        return self::tree($field, $title, $value, Tree::TYPE_SELECTED);
+    }
+
+    /**
+     * 获取勾选的值
+     *
+     * @param string $field
+     * @param string $title
+     * @param mixed $value
+     * @return Tree
+     */
+    public static function treeChecked($field, $title, $value = [])
+    {
+        return self::tree($field, $title, $value)->showCheckbox(true);
+    }
+
+    /**
+     * 树形组件数据 date 类
+     *
+     * @param mixed $id
+     * @param string $title
+     * @param array $children
+     * @return TreeData
+     */
+    public static function treeData($id, $title, array $children = [])
+    {
+        return new TreeData($id, $title, $children);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Traits/UploadFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Elm/Traits/UploadFactoryTrait.php
new file mode 100755
index 0000000..b032c8f
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Traits/UploadFactoryTrait.php
@@ -0,0 +1,93 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\UI\Elm\Traits;
+
+
+use FormBuilder\UI\Elm\Components\Upload;
+
+trait UploadFactoryTrait
+{
+    /**
+     * 上传组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $action
+     * @param string|array $value
+     * @param string $type
+     * @return Upload
+     */
+    public static function upload($field, $title, $action, $value = '', $type = Upload::TYPE_FILE)
+    {
+        $upload = new Upload($field, $title, $value);
+        return $upload->uploadType($type)->action($action);
+    }
+
+    /**
+     * 图片上传
+     * value 为 Array类型
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $action
+     * @param array $value
+     * @return Upload
+     */
+    public static function uploadImages($field, $title, $action, array $value = [])
+    {
+        $upload = self::upload($field, $title, $action, $value, Upload::TYPE_IMAGE);
+        return $upload->accept('image/*');
+    }
+
+    /**
+     * 文件上传
+     * value 为 Array类型
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $action
+     * @param array $value
+     * @return Upload
+     */
+    public static function uploadFiles($field, $title, $action, array $value = [])
+    {
+        return self::upload($field, $title, $action, $value, Upload::TYPE_FILE);
+    }
+
+    /**
+     * 单图片上传
+     * value 为  string类型
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $action
+     * @param string $value
+     * @return Upload
+     */
+    public static function uploadImage($field, $title, $action, $value = '')
+    {
+        $upload = self::upload($field, $title, $action, (string)$value, Upload::TYPE_IMAGE);
+        return $upload->accept('image/*')->limit(1);
+    }
+
+    /**
+     * 单文件上传
+     * value 为  string类型
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $action
+     * @param string $value
+     * @return Upload
+     */
+    public static function uploadFile($field, $title, $action, $value = '')
+    {
+        $upload = self::upload($field, $title, $action, (string)$value, Upload::TYPE_FILE);
+        return $upload->limit(1);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Traits/ValidateFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Elm/Traits/ValidateFactoryTrait.php
new file mode 100755
index 0000000..35eb73a
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Traits/ValidateFactoryTrait.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm\Traits;
+
+use FormBuilder\UI\Elm\Validate;
+
+trait ValidateFactoryTrait
+{
+    public static function validateStr($trigger = Validate::TRIGGER_CHANGE)
+    {
+        return new Validate(Validate::TYPE_STRING, $trigger);
+    }
+
+    public static function validateArr($trigger = Validate::TRIGGER_CHANGE)
+    {
+        return new Validate(Validate::TYPE_ARRAY, $trigger);
+    }
+
+    public static function validateNum($trigger = Validate::TRIGGER_CHANGE)
+    {
+        return new Validate(Validate::TYPE_NUMBER, $trigger);
+    }
+
+    public static function validateDate($trigger = Validate::TRIGGER_CHANGE)
+    {
+        return new Validate(Validate::TYPE_DATE, $trigger);
+    }
+
+    public static function validateInt($trigger = Validate::TRIGGER_CHANGE)
+    {
+        return new Validate(Validate::TYPE_INTEGER, $trigger);
+    }
+
+    public static function validateFloat($trigger = Validate::TRIGGER_CHANGE)
+    {
+        return new Validate(Validate::TYPE_FLOAT, $trigger);
+    }
+
+    public static function validateObject($trigger = Validate::TRIGGER_CHANGE)
+    {
+        return new Validate(Validate::TYPE_OBJECT, $trigger);
+    }
+
+    public static function validateEmail($trigger = Validate::TRIGGER_CHANGE)
+    {
+        return new Validate(Validate::TYPE_EMAIL, $trigger);
+    }
+
+    public static function validateEnum($trigger = Validate::TRIGGER_CHANGE)
+    {
+        return new Validate(Validate::TYPE_ENUM, $trigger);
+    }
+
+    public static function validateUrl($trigger = Validate::TRIGGER_CHANGE)
+    {
+        return new Validate(Validate::TYPE_URL, $trigger);
+    }
+
+    public static function validateHex($trigger = Validate::TRIGGER_CHANGE)
+    {
+        return new Validate(Validate::TYPE_HEX, $trigger);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Elm/Validate.php b/vendor/xaboy/form-builder/src/UI/Elm/Validate.php
new file mode 100755
index 0000000..0f369cb
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Elm/Validate.php
@@ -0,0 +1,20 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Elm;
+
+
+use FormBuilder\UI\Iview\Validate as IViewValidate;
+
+class Validate extends IViewValidate
+{
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Bootstrap.php b/vendor/xaboy/form-builder/src/UI/Iview/Bootstrap.php
new file mode 100755
index 0000000..3e4ff90
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Bootstrap.php
@@ -0,0 +1,53 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview;
+
+use FormBuilder\Contract\BootstrapInterface;
+use FormBuilder\Form;
+
+class Bootstrap implements BootstrapInterface
+{
+
+    protected $version;
+
+    /**
+     * Bootstrap constructor.
+     * @param int $version
+     */
+    public function __construct($version = 3)
+    {
+        $this->version = $version;
+    }
+
+    public function init(Form $form)
+    {
+        $dependScript = $form->getDependScript();
+
+        if ($this->version != 4) {
+            array_splice($dependScript, 2, 0, [
+                '<link href="https://unpkg.com/iview@3.4.2/dist/styles/iview.css" rel="stylesheet">',
+                '<script src="https://unpkg.com/iview@3.4.2/dist/iview.min.js"></script>',
+                '<script src="https://unpkg.com/@form-create/iview@1.0.15/dist/form-create.min.js"></script>',
+            ]);
+        } else {
+            array_splice($dependScript, 2, 0, [
+                '<link href="https://unpkg.com/view-design@4.0.2/dist/styles/iview.css" rel="stylesheet">',
+                '<script src="https://unpkg.com/view-design@4.0.2/dist/iview.min.js"></script>',
+                '<script src="https://unpkg.com/@form-create/iview4@1.0.15/dist/form-create.min.js"></script>',
+            ]);
+        }
+
+
+        $form->setDependScript($dependScript);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Components/Button.php b/vendor/xaboy/form-builder/src/UI/Iview/Components/Button.php
new file mode 100755
index 0000000..c7e924c
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Components/Button.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Components;
+
+
+use FormBuilder\Driver\CustomComponent;
+
+/**
+ * Class Button
+ * @method $this type(string $type) 按钮类型,可选值为primary、ghost、dashed、text、info、success、warning、error或者不设置
+ * @method $this size(string $size) 按钮大小,可选值为large、small、default或者不设置
+ * @method $this long(bool $long) 开启后,按钮的长度为 100%
+ * @method $this htmlType(string $type) 设置button原生的type,可选值为button、submit、reset
+ * @method $this disabled(bool $disabled) 设置按钮为禁用状态
+ * @method $this icon(string $icon) 设置按钮的图标类型
+ * @method $this innerText(string $innerText) 按钮文字提示
+ * @method $this loading(bool $loading) 设置按钮为加载中状态
+ * @method $this show(bool $show) 是否显示, 默认显示
+ */
+class Button extends CustomComponent
+{
+    protected static $propsRule = [
+        'type' => 'string',
+        'size' => 'string',
+        'long' => 'bool',
+        'htmlType' => 'string',
+        'disabled' => 'bool',
+        'icon' => 'string',
+        'innerText' => 'string',
+        'loading' => 'bool',
+        'show' => 'bool'
+    ];
+
+    /**
+     * 按钮形状,可选值为circle或者不设置
+     *
+     * @param bool $isCircle
+     * @return $this
+     */
+    public function shape($isCircle = true)
+    {
+        if ($isCircle)
+            $this->props['shape'] = 'circle';
+        else
+            unset($this->props['shape']);
+        return $this;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Components/Cascader.php b/vendor/xaboy/form-builder/src/UI/Iview/Components/Cascader.php
new file mode 100755
index 0000000..eb791bc
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Components/Cascader.php
@@ -0,0 +1,122 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Components;
+
+
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Factory\Iview;
+
+/**
+ * 多级联动组件
+ * Class Cascader
+ *
+ * @method $this type(string $type) 数据类型, 支持 city_area(省市区三级联动), city (省市二级联动), other (自定义)
+ * @method $this disabled(bool $bool) 是否禁用选择器
+ * @method $this clearable(bool $bool) 是否支持清除
+ * @method $this placeholder(string $placeholder)
+ * @method $this trigger(string $trigger) 次级菜单展开方式,可选值为 click 或 hover
+ * @method $this changeOnSelect(bool $bool) 当此项为 true 时,点选每级菜单选项值都会发生变化, 默认为 false
+ * @method $this size(string $size) 输入框大小,可选值为large和small或者不填
+ * @method $this filterable(bool $bool) 是否支持搜索
+ * @method $this notFoundText(string $text) 当搜索列表为空时显示的内容
+ * @method $this transfer(bool $bool) /是否将弹层放置于 body 内,在 Tabs、带有 fixed 的 Table 列内使用时,建议添加此属性,它将不受父级样式影响,从而达到更好的效果
+ */
+class Cascader extends FormComponent
+{
+    /**
+     * 省市区三级联动数据
+     */
+    const TYPE_CITY_AREA = 'city_area';
+
+    /**
+     * 省市二级联动数据
+     */
+    const TYPE_CITY = 'city';
+
+    /**
+     * 自定义数据
+     */
+    const TYPE_OTHER = 'other';
+
+
+    protected $defaultValue = [];
+
+    protected $selectComponent = true;
+
+    protected $defaultProps = [
+        'type' => self::TYPE_OTHER,
+        'data' => []
+    ];
+
+    /**
+     * @var array
+     */
+    protected static $propsRule = [
+        'type' => 'string',
+        'disabled' => 'bool',
+        'clearable' => 'bool',
+        'changeOnSelect' => 'bool',
+        'filterable' => 'bool',
+        'transfer' => 'bool',
+        'placeholder' => 'string',
+        'trigger' => 'string',
+        'size' => 'string',
+        'notFoundText' => 'string',
+    ];
+
+    /**
+     * @param array $value
+     * @return $this
+     */
+    public function value($value)
+    {
+        $this->value = (array)$value;
+        return $this;
+    }
+
+    /**
+     * 可选项的数据源
+     * 例如:{
+     *    "value":"北京市", "label":"北京市", "children":[{
+     *        "value":"东城区", "label":"东城区"
+     *    }]
+     *  }
+     *
+     * @param array|callable $data
+     * @return $this
+     */
+    public function data($data)
+    {
+        $is_callable = is_callable($data);
+        if (!is_array($data) && !$is_callable) return $this;
+
+        $this->props['data'] = $is_callable ? $data($this) : $data;
+        return $this;
+    }
+
+    /**
+     * @param $var
+     * @return $this
+     */
+    public function jsData($var)
+    {
+        $this->props['data'] = 'js.' . (string)$var;
+        return $this;
+    }
+
+    public function createValidate()
+    {
+        return Iview::validateArr();
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Components/Checkbox.php b/vendor/xaboy/form-builder/src/UI/Iview/Components/Checkbox.php
new file mode 100755
index 0000000..e92b667
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Components/Checkbox.php
@@ -0,0 +1,49 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Components;
+
+
+use FormBuilder\Driver\FormOptionsComponent;
+use FormBuilder\Factory\Iview;
+
+/**
+ * 复选框组件
+ * Class Checkbox
+ *
+ * @method $this size(string $size) 多选框组的尺寸,可选值为 large、small、default 或者不设置
+ */
+class Checkbox extends FormOptionsComponent
+{
+    protected $defaultValue = [];
+
+    protected $selectComponent = true;
+
+    protected static $propsRule = [
+        'size' => 'string'
+    ];
+
+    /**
+     * @param array $value
+     * @return $this
+     */
+    public function value($value)
+    {
+        $this->value = (array)$value;
+        return $this;
+    }
+
+    public function createValidate()
+    {
+        return Iview::validateArr();
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Components/ColorPicker.php b/vendor/xaboy/form-builder/src/UI/Iview/Components/ColorPicker.php
new file mode 100755
index 0000000..a625710
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Components/ColorPicker.php
@@ -0,0 +1,49 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Components;
+
+
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Factory\Iview;
+
+/**
+ * 颜色选择器组件
+ * Class ColorPicker
+ *
+ * @method $this disabled(bool $bool) 是否禁用
+ * @method $this alpha(bool $bool) 是否支持透明度选择, 默认为false
+ * @method $this colors(array $colors) colors
+ * @method $this hue(bool $bool) 是否支持色彩选择, 默认为true
+ * @method $this recommend(bool $bool) 是否显示推荐的颜色预设, 默认为false
+ * @method $this size(string $size) 尺寸,可选值为large、small、default或者不设置
+ * @method $this format(string $format) 颜色的格式,可选值为 hsl、hsv、hex、rgb.开启 alpha 时为 rgb,其它为 hex
+ */
+class ColorPicker extends FormComponent
+{
+    protected $selectComponent = true;
+
+    protected static $propsRule = [
+        'disabled' => 'bool',
+        'alpha' => 'bool',
+        'hue' => 'bool',
+        'recommend' => 'bool',
+        'size' => 'string',
+        'colors' => 'array',
+        'format' => 'string',
+    ];
+
+    public function createValidate()
+    {
+        return Iview::validateStr();
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Components/DatePicker.php b/vendor/xaboy/form-builder/src/UI/Iview/Components/DatePicker.php
new file mode 100755
index 0000000..b81d5d6
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Components/DatePicker.php
@@ -0,0 +1,125 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Components;
+
+
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Factory\Iview;
+
+/**
+ * 日期选择器组件
+ * Class DatePicker
+ *
+ * @method $this type(string $type) 显示类型,可选值为 date、daterange、datetime、datetimerange、year、month
+ * @method $this format(string $format) 展示的日期格式, 默认为yyyy-MM-dd HH:mm:ss
+ * @method $this placement(string $placement) 日期选择器出现的位置,可选值为top, top-start, top-end, bottom, bottom-start, bottom-end, left, left-start, left-end, right, right-start, right-end, 默认为bottom-start
+ * @method $this placeholder(string $placeholder) 占位文本
+ * @method $this confirm(bool $bool) 是否显示底部控制栏,开启后,选择完日期,选择器不会主动关闭,需用户确认后才可关闭, 默认为false
+ * @method $this size(string $size) 尺寸,可选值为large、small、default或者不设置
+ * @method $this disabled(bool $bool) 是否禁用选择器
+ * @method $this clearable(bool $bool) 是否显示清除按钮
+ * @method $this readonly(bool $bool) 完全只读,开启后不会弹出选择器,只在没有设置 open 属性下生效
+ * @method $this editable(bool $bool) 文本框是否可以输入, 默认为false
+ * @method $this multiple(bool $bool) 开启后, 可以选择多个日期, 仅在 date 下可用, 默认为false
+ * @method $this transfer(bool $bool) 是否将弹层放置于 body 内,在 Tabs、带有 fixed 的 Table 列内使用时,建议添加此属性,它将不受父级样式影响,从而达到更好的效果, 默认为false
+ * @method $this splitPanels(bool $bool) 开启后,左右面板不联动,仅在 daterange 和 datetimerange 下可用。
+ * @method $this showWeekNumbers(bool $bool) 开启后,可以显示星期数。
+ *
+ */
+class DatePicker extends FormComponent
+{
+    /**
+     * 日期选择
+     */
+    const TYPE_DATE = 'date';
+    /**
+     * 日期区间选择
+     */
+    const TYPE_DATE_RANGE = 'daterange';
+    /**
+     * 日期+时间选择
+     */
+    const TYPE_DATE_TIME = 'datetime';
+    /**
+     * 日期+时间区间选择
+     */
+    const TYPE_DATE_TIME_RANGE = 'datetimerange';
+    /**
+     * 年份选择
+     */
+    const TYPE_YEAR = 'year';
+    /**
+     * 月份选择
+     */
+    const TYPE_MONTH = 'month';
+
+
+    protected $selectComponent = true;
+
+    protected $defaultProps = [
+        'type' => self::TYPE_DATE,
+        'editable' => false
+    ];
+
+    protected static $propsRule = [
+        'type' => 'string',
+        'format' => 'string',
+        'placement' => 'string',
+        'placeholder' => 'string',
+        'size' => 'string',
+        'confirm' => 'bool',
+        'disabled' => 'bool',
+        'clearable' => 'bool',
+        'readonly' => 'bool',
+        'editable' => 'bool',
+        'multiple' => 'bool',
+        'transfer' => 'bool',
+        'splitPanels' => 'bool',
+        'showWeekNumbers' => 'bool'
+    ];
+
+    protected function isRange()
+    {
+        return in_array(strtolower($this->props['type']), ['datetimerange', 'daterange']);
+    }
+
+    protected function isMultiple()
+    {
+        return isset($this->props['multiple']) && $this->props['multiple'];
+    }
+
+    public function createValidate()
+    {
+        if ($this->isRange() || $this->isMultiple())
+            return Iview::validateArr();
+        else
+            return Iview::validateDate();
+    }
+
+    public function required($message = null)
+    {
+        if (is_null($message)) $message = $this->getPlaceHolder();
+        $validate = $this->createValidate();
+
+        if ($this->isRange()) {
+            $dateRequired = Iview::validateDate()->message($message)->required();
+            $validate->fields([
+                '0' => $dateRequired,
+                '1' => $dateRequired
+            ]);
+        }
+
+        $this->appendValidate($validate->message($message)->required());
+        return $this;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Components/Frame.php b/vendor/xaboy/form-builder/src/UI/Iview/Components/Frame.php
new file mode 100755
index 0000000..e5824d4
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Components/Frame.php
@@ -0,0 +1,83 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Components;
+
+
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Factory\Iview;
+
+/**
+ * 框架组件
+ * Class Frame
+ *
+ * @method $this type(string $type) frame类型, 有input, file, image, 默认为input
+ * @method $this src(string $src) iframe地址
+ * @method $this maxLength(int $length) value的最大数量, 默认无限制
+ * @method $this icon(string $icon) 打开弹出框的按钮图标
+ * @method $this height(string $height) 弹出框高度
+ * @method $this width(string $width) 弹出框宽度
+ * @method $this spin(bool $bool) 是否显示加载动画, 默认为 true
+ * @method $this frameTitle(string $title) 弹出框标题
+ * @method $this modal(array $modalProps) 弹出框props
+ * @method $this handleIcon(bool $bool) 操作按钮的图标, 设置为false将不显示, 设置为true为默认的预览图标, 类型为file时默认为false, image类型默认为true
+ * @method $this allowRemove(bool $bool) 是否可删除, 设置为false是不显示删除按钮
+ * @method $this disabled(bool $bool) 是否禁用
+ *
+ */
+class Frame extends FormComponent
+{
+    /**
+     * 图片类型
+     */
+    const TYPE_IMAGE = 'image';
+    /**
+     * 文件类型
+     */
+    const TYPE_FILE = 'file';
+    /**
+     * input 类型
+     */
+    const TYPE_INPUT = 'input';
+
+
+    protected $selectComponent = true;
+
+    protected $defaultProps = [
+        'type' => self::TYPE_INPUT,
+        'maxLength' => 0
+    ];
+
+    protected static $propsRule = [
+        'type' => 'string',
+        'src' => 'string',
+        'maxLength' => 'int',
+        'icon' => 'string',
+        'height' => 'string',
+        'width' => 'string',
+        'spin' => 'bool',
+        'modal' => 'array',
+        'frameTitle' => ['string', 'title'],
+        'handleIcon' => 'bool',
+        'allowRemove' => 'bool',
+    ];
+
+    public function createValidate()
+    {
+        return Iview::validateArr();
+    }
+
+    protected function init()
+    {
+        $this->frameTitle($this->getPlaceHolder());
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Components/Group.php b/vendor/xaboy/form-builder/src/UI/Iview/Components/Group.php
new file mode 100755
index 0000000..397f802
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Components/Group.php
@@ -0,0 +1,77 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Components;
+
+
+use FormBuilder\Contract\ValidateInterface;
+use FormBuilder\Driver\CustomComponent;
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Factory\Iview;
+use FormBuilder\Util;
+
+/**
+ * 数组组件
+ *
+ * Class Group
+ * @method $this min(int $min) 最少几项
+ * @method $this max(int $max) 最多几项
+ * @method $this disabled(bool $bool) 是否禁用
+ */
+class Group extends FormComponent
+{
+    protected $defaultValue = [];
+
+    protected static $propsRule = [
+        'min' => 'string',
+        'max' => 'string',
+        'disabled' => 'int',
+    ];
+
+    /**
+     * @param array|CustomComponent $rule
+     * @return $this
+     */
+    public function rule($rule)
+    {
+        $this->props['rule'] = $this->tidyRule([$rule])[0];
+        return $this;
+    }
+
+    /**
+     * @param array $rules
+     * @return array
+     */
+    protected function tidyRule(array $rules)
+    {
+        foreach ($rules as $k => $rule) {
+            if (Util::isComponent($rule)) {
+                $rules[$k] = $rule->build();
+            }
+        }
+        return $rules;
+    }
+
+    public function rules(array $rules)
+    {
+        $this->props['rules'] = $this->tidyRule($rules);
+        return $this;
+    }
+
+    /**
+     * @return ValidateInterface
+     */
+    public function createValidate()
+    {
+        return Iview::validateArr();
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Components/Hidden.php b/vendor/xaboy/form-builder/src/UI/Iview/Components/Hidden.php
new file mode 100755
index 0000000..b2edaaf
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Components/Hidden.php
@@ -0,0 +1,57 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Components;
+
+
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Exception\FormBuilderException;
+
+/**
+ * hidden组件
+ * Class Hidden
+ *
+ */
+class Hidden extends FormComponent
+{
+    /**
+     * Hidden constructor.
+     *
+     * @param string $field
+     * @param string $value
+     */
+    public function __construct($field, $value)
+    {
+        parent::__construct($field, '', $value);
+    }
+
+    /**
+     * @return array
+     */
+    public function getRule()
+    {
+        return [
+            'type' => $this->type,
+            'field' => $this->field,
+            'value' => $this->value
+        ];
+    }
+
+    /**
+     * @return void
+     * @throws FormBuilderException
+     */
+    public function createValidate()
+    {
+        throw new FormBuilderException('hidden 组件不支持 createValidate 方法');
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Components/Input.php b/vendor/xaboy/form-builder/src/UI/Iview/Components/Input.php
new file mode 100755
index 0000000..3bd6bd1
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Components/Input.php
@@ -0,0 +1,91 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Components;
+
+
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Factory\Iview;
+
+/**
+ * Input组件,支持类型text、password、textarea、url、email、date
+ * Class Input
+ *
+ * @method $this type(string $type) 输入框类型,可选值为 text、password、textarea、url、email、date;
+ * @method $this size(string $size) 输入框尺寸,可选值为large、small、default或者不设置;
+ * @method $this placeholder(string $placeholder) 占位文本
+ * @method $this clearable(bool $bool) 是否显示清空按钮, 默认为false
+ * @method $this disabled(bool $bool) 设置输入框为禁用状态, 默认为false
+ * @method $this readonly(bool $bool) 设置输入框为只读, 默认为false
+ * @method $this maxlength(int $length) 最大输入长度
+ * @method $this icon(string $icon) 输入框尾部图标,仅在 text 类型下有效
+ * @method $this rows(int $rows) 文本域默认行数,仅在 textarea 类型下有效, 默认为2
+ * @method $this number(bool $bool) 将用户的输入转换为 Number 类型, 默认为false
+ * @method $this autofocus(bool $bool) 自动获取焦点, 默认为false
+ * @method $this autocomplete(bool $bool) 原生的自动完成功能, 默认为false
+ * @method $this spellcheck(bool $bool) 原生的 spellcheck 属性, 默认为false
+ * @method $this wrap(string $warp) 原生的 wrap 属性,可选值为 hard 和 soft, 默认为soft
+ */
+class Input extends FormComponent
+{
+    const TYPE_TEXT = 'text';
+
+    const TYPE_PASSWORD = 'password';
+
+    const TYPE_TEXTAREA = 'textarea';
+
+    const TYPE_URL = 'url';
+
+    const TYPE_EMAIL = 'email';
+
+    const TYPE_DATE = 'date';
+
+
+    protected $defaultProps = [
+        'type' => self::TYPE_TEXT
+    ];
+
+    protected static $propsRule = [
+        'type' => 'string',
+        'size' => 'string',
+        'placeholder' => 'string',
+        'clearable' => 'bool',
+        'disabled' => 'bool',
+        'readonly' => 'bool',
+        'maxlength' => 'int',
+        'icon' => 'string',
+        'rows' => 'int',
+        'number' => 'bool',
+        'autofocus' => 'bool',
+        'autocomplete' => 'bool',
+        'spellcheck' => 'bool',
+        'wrap' => 'string',
+    ];
+
+    /**
+     * 自适应内容高度,仅在 textarea 类型下有效
+     *
+     * @param Bool|Number $minRows
+     * @param null|Number $maxRows
+     * @return $this
+     */
+    public function autoSize($minRows = false, $maxRows = null)
+    {
+        $this->props['autosize'] = $maxRows === null ? boolval($minRows) : compact('minRows', 'maxRows');
+        return $this;
+    }
+
+    public function createValidate()
+    {
+        return Iview::validateStr();
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Components/InputNumber.php b/vendor/xaboy/form-builder/src/UI/Iview/Components/InputNumber.php
new file mode 100755
index 0000000..5ad52d3
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Components/InputNumber.php
@@ -0,0 +1,51 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Components;
+
+
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Factory\Iview;
+
+/**
+ * 数字输入框组件
+ * Class InputNumber
+ *
+ * @method $this max(float $max) 最大值
+ * @method $this min(float $min) 最小值
+ * @method $this step(float $step) 每次改变的步伐,可以是小数
+ * @method $this size(string $size) 输入框尺寸,可选值为large、small、default或者不填
+ * @method $this disabled(bool $bool) 设置禁用状态,默认为false
+ * @method $this placeholder(string $placeholder) 占位文本
+ * @method $this readonly(bool $bool) 是否设置为只读,默认为false
+ * @method $this editable(bool $bool) 是否可编辑,默认为true
+ * @method $this precision(int $precision) 数值精度
+ */
+class InputNumber extends FormComponent
+{
+    protected static $propsRule = [
+        'max' => 'float',
+        'min' => 'float',
+        'step' => 'float',
+        'disabled' => 'bool',
+        'size' => 'string',
+        'placeholder' => 'string',
+        'readonly' => 'bool',
+        'editable' => 'bool',
+        'precision' => 'int',
+    ];
+
+    public function createValidate()
+    {
+        return Iview::validateNum();
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Components/Option.php b/vendor/xaboy/form-builder/src/UI/Iview/Components/Option.php
new file mode 100755
index 0000000..73e1df5
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Components/Option.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Components;
+
+
+use FormBuilder\Contract\OptionComponentInterface;
+
+class Option implements OptionComponentInterface
+{
+    /**
+     * @var array
+     */
+    protected $rule;
+
+    /**
+     * Option constructor.
+     *
+     * @param string|number $value
+     * @param string $label
+     * @param bool $disabled
+     */
+    public function __construct($value, $label = '', $disabled = null)
+    {
+        $this->rule = compact('label', 'value');
+        if (!is_null($disabled))
+            $this->disabled($disabled);
+    }
+
+    /**
+     * @param bool $disabled
+     * @return $this
+     */
+    public function disabled($disabled = true)
+    {
+        $this->rule['disabled'] = !!$disabled;
+        return $this;
+    }
+
+    /**
+     * @return array
+     */
+    public function getOption()
+    {
+        return $this->rule;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Components/Poptip.php b/vendor/xaboy/form-builder/src/UI/Iview/Components/Poptip.php
new file mode 100755
index 0000000..1d93ac0
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Components/Poptip.php
@@ -0,0 +1,57 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Components;
+
+
+use FormBuilder\Driver\CustomComponent;
+
+/**
+ * Class Poptip
+ * @method $this trigger(string $trigger) 触发方式,可选值为hover(悬停)click(点击)focus(聚焦), 在 confirm 模式下,只有 click 有效
+ * @method $this popperTitle(string $title) 显示的标题
+ * @method $this placement(string $placement) 提示框出现的位置,可选值为top, top-start, top-end, bottom, bottom-start, bottom-endleft, left-start, left-end, right, right-start, right-end
+ * @method $this width(string $width) 宽度,最小宽度为 150px,在 confirm 模式下,默认最大宽度为 300px
+ * @method $this confirm(bool $confirm) 是否开启对话框模式
+ * @method $this disabled(bool $disabled) 是否禁用
+ * @method $this okText(string $okText) 确定按钮的文字,只在 confirm 模式下有效
+ * @method $this cancelText(string $cancelText) 取消按钮的文字,只在 confirm 模式下有效
+ * @method $this transfer(bool $transfer) 是否将弹层放置于 body 内,在 Tabs、带有 fixed 的 Table 列内使用时,建议添加此属性,它将不受父级样式影响,从而达到更好的效果
+ * @method $this popperClass(string $popperClass) 给 Poptip 设置 class-name,在使用 transfer 时会很有用
+ * @method $this wordWrap(bool $wordWrap) 开启后,超出指定宽度文本将自动换行,并两端对齐
+ * @method $this padding(string $padding) 自定义间距值
+ * @method $this offset(float $offset) 出现位置的偏移量
+ * @method $this options(array $options) 自定义 popper.js 的配置项
+ */
+class Poptip extends CustomComponent
+{
+    protected $defaultProps = [
+        'transfer' => true
+    ];
+
+    protected static $propsRule = [
+        'trigger' => 'string',
+        'placement' => 'string',
+        'width' => 'string',
+        'confirm' => 'bool',
+        'disabled ' => 'bool',
+        'okText ' => 'string',
+        'cancelText' => 'string',
+        'transfer' => 'bool',
+        'popperClass' => 'string',
+        'popperTitle' => ['string', 'title'],
+        'wordWrap' => 'bool',
+        'padding' => 'string',
+        'offset' => 'float',
+        'options' => 'array',
+    ];
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Components/Radio.php b/vendor/xaboy/form-builder/src/UI/Iview/Components/Radio.php
new file mode 100755
index 0000000..1c33bf9
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Components/Radio.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Components;
+
+
+use FormBuilder\Driver\FormOptionsComponent;
+use FormBuilder\Factory\Iview;
+
+/**
+ * 单选框组件
+ * Class Radio
+ *
+ * @method $this size(string $size) 单选框的尺寸,可选值为 large、small、default 或者不设置
+ * @method $this vertical(bool $bool) 是否垂直排列,按钮样式下无效
+ */
+class Radio extends FormOptionsComponent
+{
+    protected $selectComponent = true;
+
+    protected static $propsRule = [
+        'size' => 'string',
+        'vertical' => 'bool'
+    ];
+
+    public function createValidate()
+    {
+        return Iview::validateStr();
+    }
+
+    public function createValidateNum()
+    {
+        return Iview::validateNum();
+    }
+
+    public function requiredNum($message = '')
+    {
+        if (is_null($message)) $message = $this->getPlaceHolder();
+        return $this->appendValidate($this->createValidateNum()->message($message)->required());
+    }
+
+    /**
+     * 按钮样式
+     *
+     * @param bool $button
+     * @return $this
+     */
+    public function button($button = true)
+    {
+        if ($button)
+            $this->props['type'] = 'button';
+        else
+            unset($this->props['type']);
+        return $this;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Components/Rate.php b/vendor/xaboy/form-builder/src/UI/Iview/Components/Rate.php
new file mode 100755
index 0000000..2059ca9
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Components/Rate.php
@@ -0,0 +1,47 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Components;
+
+
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Factory\Iview;
+
+/**
+ * 评分组件
+ * Class Rate
+ *
+ * @method $this count(int $star) star 总数, 默认为 5
+ * @method $this allowHalf(bool $bool) 是否允许半选, 默认为 false
+ * @method $this disabled(bool $bool) 是否只读,无法进行交互, 默认为
+ * @method $this showText(bool $bool) 是否显示提示文字, 默认为 false
+ * @method $this clearable(bool $bool) 是否可以取消选择, 默认为 false
+ *
+ */
+class Rate extends FormComponent
+{
+    protected $selectComponent = true;
+
+    protected static $propsRule = [
+        'count' => 'float',
+        'allowHalf' => 'bool',
+        'disabled' => 'bool',
+        'showText' => 'bool',
+        'clearable' => 'bool',
+    ];
+
+    public function createValidate()
+    {
+        return Iview::validateNum();
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Components/Select.php b/vendor/xaboy/form-builder/src/UI/Iview/Components/Select.php
new file mode 100755
index 0000000..9a71ff3
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Components/Select.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Components;
+
+
+use FormBuilder\Driver\FormOptionsComponent;
+use FormBuilder\Factory\Iview;
+
+/**
+ * 选择器组件
+ * Class Select
+ *
+ * @method $this multiple(bool $bool) 是否支持多选, 默认为false
+ * @method $this disabled(bool $bool) 是否禁用, 默认为false
+ * @method $this clearable(bool $bool) 是否可以清空选项,只在单选时有效, 默认为false
+ * @method $this filterable(bool $bool) 是否支持搜索, 默认为false
+ * @method $this size(string $size) 选择框大小,可选值为large、small、default或者不填
+ * @method $this placeholder(string $placeholder) 选择框默认文字
+ * @method $this transfer(string $transfer) 是否将弹层放置于 body 内,在 Tabs、带有 fixed 的 Table 列内使用时,建议添加此属性,它将不受父级样式影响,从而达到更好的效果, 默认为false
+ * @method $this placement(string $placement) 弹窗的展开方向,可选值为 bottom 和 top, 默认为bottom
+ * @method $this notFoundText(string $text) 当下拉列表为空时显示的内容, 默认为 无匹配数据
+ *
+ */
+class Select extends FormOptionsComponent
+{
+    protected $selectComponent = true;
+
+    protected $defaultProps = [
+        'multiple' => false
+    ];
+
+    protected static $propsRule = [
+        'multiple' => 'bool',
+        'disabled' => 'bool',
+        'clearable' => 'bool',
+        'filterable' => 'bool',
+        'size' => 'string',
+        'placeholder' => 'string',
+        'transfer' => 'string',
+        'placement' => 'string',
+        'notFoundText' => 'string',
+    ];
+
+    public function createValidate()
+    {
+        if ($this->props['multiple'] == true)
+            return Iview::validateArr();
+        else
+            return Iview::validateStr();
+    }
+
+    public function createValidateNum()
+    {
+        return Iview::validateNum();
+    }
+
+    public function requiredNum($message = null)
+    {
+        if (is_null($message)) $message = $this->getPlaceHolder();
+        return $this->appendValidate($this->createValidateNum()->message($message)->required());
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Components/Slider.php b/vendor/xaboy/form-builder/src/UI/Iview/Components/Slider.php
new file mode 100755
index 0000000..545a19f
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Components/Slider.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Components;
+
+
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Factory\Iview;
+
+/**
+ * 滑块组件
+ * Class Slider
+ *
+ * @method $this min(float $min) 最小值, 默认 0
+ * @method $this max(float $max) 最大值, 默认 100
+ * @method $this step(float $step) 步长,取值建议能被(max - min)整除, 默认 1
+ * @method $this disabled(bool $bool) 是否禁用滑块, 默认 false
+ * @method $this range(bool $bool) 是否开启双滑块模式, 默认
+ * @method $this showInput(bool $bool) 是否显示数字输入框,仅在单滑块模式下有效, 默认 false
+ * @method $this showStops(bool $bool) 是否显示间断点,建议在 step 不密集时使用, 默认 false
+ * @method $this showTip(string $tip) 提示的显示控制,可选值为 hover(悬停,默认)、always(总是可见)、never(不可见)
+ * @method $this inputSize(string $size) 数字输入框的尺寸,可选值为large、small、default或者不填,仅在开启 show-input 时有效
+ *
+ */
+class Slider extends FormComponent
+{
+    protected $selectComponent = true;
+
+    protected $defaultProps = [
+        'range' => false
+    ];
+
+    protected static $propsRule = [
+        'min' => 'float',
+        'max' => 'float',
+        'step' => 'float',
+        'disabled' => 'bool',
+        'range' => 'bool',
+        'showInput' => 'bool',
+        'showStops' => 'bool',
+        'showTip' => 'string',
+        'inputSize' => 'string',
+    ];
+
+    public function createValidate()
+    {
+        if ($this->props['range'] == true)
+            return Iview::validateArr();
+        else
+            return Iview::validateNum();
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Components/Switches.php b/vendor/xaboy/form-builder/src/UI/Iview/Components/Switches.php
new file mode 100755
index 0000000..4deb1e8
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Components/Switches.php
@@ -0,0 +1,93 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Components;
+
+
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Factory\Iview;
+
+/**
+ * 开关组件
+ * Class Switches
+ *
+ * @method $this size(string $size) 开关的尺寸,可选值为large、small、default或者不写。建议开关如果使用了2个汉字的文字,使用 large。
+ * @method $this disabled(bool $bool) 禁用开关, 默认为false
+ * @method $this trueValue(string $value) 选中时的值,默认为1
+ * @method $this falseValue(string $value) 没有选中时的值,默认为0
+ */
+class Switches extends FormComponent
+{
+    protected $selectComponent = true;
+
+    protected $defaultProps = [
+        'trueValue' => '1',
+        'falseValue' => '0',
+        'slot' => []
+    ];
+
+    protected static $propsRule = [
+        'size' => 'string',
+        'disabled' => 'bool',
+        'trueValue' => '',
+        'falseValue' => ''
+    ];
+
+    public function getComponentName()
+    {
+        return 'switch';
+    }
+
+    /**
+     * 自定义显示打开时的内容
+     *
+     * @param string $open
+     * @return $this
+     */
+    public function openStr($open)
+    {
+        $this->props['slot']['open'] = (string)$open;
+        return $this;
+    }
+
+    /**
+     * 自定义显示关闭时的内容
+     *
+     * @param string $close
+     * @return $this
+     */
+    public function closeStr($close)
+    {
+        $this->props['slot']['close'] = (string)$close;
+        return $this;
+    }
+
+    /**
+     * @return array
+     */
+    public function getRule()
+    {
+        if (isset($this->props['slot']) && !count($this->props['slot'])) unset($this->props['slot']);
+        return parent::getRule();
+    }
+
+    public function createValidate()
+    {
+        return Iview::validateStr();
+    }
+
+    public function createValidateNum()
+    {
+        return Iview::validateNum();
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Components/TimePicker.php b/vendor/xaboy/form-builder/src/UI/Iview/Components/TimePicker.php
new file mode 100755
index 0000000..fa3c0da
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Components/TimePicker.php
@@ -0,0 +1,115 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Components;
+
+
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Factory\Iview;
+
+/**
+ * 时间选择器组件
+ * Class TimePicker
+ *
+ * @method $this type(string $type) 显示类型,可选值为 time、timerange
+ * @method $this format(string $format) 展示的时间格式, 默认为HH:mm:ss
+ * @method $this placement(string $placement) 时间选择器出现的位置,可选值为top, top-start, top-end, bottom, bottom-start, bottom-end, left, left-start, left-end, right, right-start, right-end, 默认为bottom-start
+ * @method $this placeholder(string $placeholder) 占位文本
+ * @method $this confirm(bool $bool) 是否显示底部控制栏, 默认为false
+ * @method $this size(string $size) 尺寸,可选值为large、small、default或者不设置
+ * @method $this disabled(bool $bool) 是否禁用选择器
+ * @method $this clearable(bool $bool) 是否显示清除按钮
+ * @method $this readonly(bool $bool) 完全只读,开启后不会弹出选择器,只在没有设置 open 属性下生效
+ * @method $this editable(bool $bool) 文本框是否可以输入, 默认为false
+ * @method $this transfer(bool $bool) 是否将弹层放置于 body 内,在 Tabs、带有 fixed 的 Table 列内使用时,建议添加此属性,它将不受父级样式影响,从而达到更好的效果, 默认为false
+ *
+ */
+class TimePicker extends FormComponent
+{
+    /**
+     * 时间选择
+     */
+    const TYPE_TIME = 'time';
+    /**
+     * 时间区间选择
+     */
+    const TYPE_TIME_RANGE = 'timerange';
+
+
+    protected $selectComponent = true;
+
+    protected $defaultProps = [
+        'type' => self::TYPE_TIME,
+        'editable' => false,
+        'confirm' => true
+    ];
+
+    protected static $propsRule = [
+        'type' => 'string',
+        'format' => 'string',
+        'placement' => 'string',
+        'placeholder' => 'string',
+        'size' => 'string',
+        'confirm' => 'bool',
+        'disabled' => 'bool',
+        'clearable' => 'bool',
+        'readonly' => 'bool',
+        'editable' => 'bool',
+        'transfer' => 'bool',
+    ];
+
+    /**
+     * 下拉列表的时间间隔,数组的三项分别对应小时、分钟、秒。
+     * 例如设置为 [1, 15] 时,分钟会显示:00、15、30、45。
+     *
+     * @param     $h
+     * @param int $i
+     * @param int $s
+     * @return $this
+     */
+    public function steps($h, $i = 0, $s = 0)
+    {
+        $this->props['steps'] = [$h, $i, $s];
+        return $this;
+    }
+
+    protected function isRange()
+    {
+        return strtolower($this->props['type']) === 'timerange';
+    }
+
+    public function createValidate()
+    {
+        if ($this->isRange())
+            return Iview::validateArr();
+        else
+            return Iview::validateStr();
+    }
+
+    public function required($message = null)
+    {
+        if (is_null($message)) $message = $this->getPlaceHolder();
+        $validate = $this->createValidate();
+        if ($this->isRange()) {
+            $required = ['required' => true, 'message' => $message];
+            $validate->fields([
+                '0' => $required,
+                '1' => $required
+            ]);
+            return $this;
+        }
+
+        $this->appendValidate($validate->message($message)->required());
+        return $this;
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Components/Tooltip.php b/vendor/xaboy/form-builder/src/UI/Iview/Components/Tooltip.php
new file mode 100755
index 0000000..362b775
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Components/Tooltip.php
@@ -0,0 +1,47 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Components;
+
+
+use FormBuilder\Driver\CustomComponent;
+
+/**
+ * Class Tooltip
+ * @method $this placement(string $placement) 提示框出现的位置,可选值为top, top-start, top-end, bottom, bottom-start, bottom-endleft, left-start, left-end, right, right-start, right-end
+ * @method $this disabled(bool $disabled) 是否禁用
+ * @method $this delay(float $delay) 延迟显示,单位毫秒
+ * @method $this always(bool $always) 是否总是可见
+ * @method $this theme(string $theme) 主题,可选值为 dark 或 light
+ * @method $this maxWidth(string $maxWidth) 最大宽度,超出最大值后,文本将自动换行,并两端对齐
+ * @method $this transfer(bool $transfer) 是否将弹层放置于 body 内,在 Tabs、带有 fixed 的 Table 列内使用时,建议添加此属性,它将不受父级样式影响,从而达到更好的效果
+ * @method $this offset(float $offset) 出现位置的偏移量
+ * @method $this options(array $options) 自定义 popper.js 的配置项
+ */
+class Tooltip extends CustomComponent
+{
+    protected $defaultProps = [
+        'transfer' => true
+    ];
+
+    protected static $propsRule = [
+        'placement' => 'string',
+        'disabled ' => 'bool',
+        'delay ' => 'float',
+        'theme' => 'string',
+        'maxWidth' => 'string',
+        'wordWrap' => 'bool',
+        'transfer' => 'bool',
+        'offset' => 'float',
+        'options' => 'array',
+    ];
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Components/Tree.php b/vendor/xaboy/form-builder/src/UI/Iview/Components/Tree.php
new file mode 100755
index 0000000..6a94f13
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Components/Tree.php
@@ -0,0 +1,87 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Components;
+
+
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Factory\Iview;
+
+/**
+ * 树型组件
+ * Class Tree
+ *
+ * @method $this type(string $type) 类型,可选值为 checked、selected
+ * @method $this multiple(bool $bool) 是否支持多选, 当`type=selected`并且`multiple=false`, 默认为false, 值为string或Number类型,其他情况为Array类型
+ * @method $this showCheckbox(bool $bool) 是否显示多选框, 默认为false
+ * @method $this emptyText(string $emptyText) 没有数据时的提示, 默认为'暂无数据'
+ */
+class Tree extends FormComponent
+{
+    /**
+     * 选中
+     */
+    const TYPE_SELECTED = 'selected';
+    /**
+     * 选择
+     */
+    const TYPE_CHECKED = 'checked';
+
+
+    protected $selectComponent = true;
+
+    protected $defaultProps = [
+        'type' => self::TYPE_CHECKED,
+        'data' => [],
+        'multiple' => true
+    ];
+
+    protected static $propsRule = [
+        'type' => 'string',
+        'multiple' => 'bool',
+        'showCheckbox' => 'bool',
+        'emptyText' => 'string',
+    ];
+
+    /**
+     * @param array $treeData
+     * @return $this
+     */
+    public function data(array $treeData)
+    {
+        $this->props['data'] = [];
+        foreach ($treeData as $child) {
+            $this->props['data'][] = $child instanceof TreeData
+                ? $child->getOption()
+                : $child;
+        }
+        return $this;
+    }
+
+    /**
+     * @param string $var
+     * @return $this
+     */
+    public function jsData($var)
+    {
+        $this->props['data'] = 'js.' . (string)$var;
+        return $this;
+    }
+
+    public function createValidate()
+    {
+        if ($this->props['multiple'])
+            return Iview::validateArr();
+        else
+            return Iview::validateStr();
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Components/TreeData.php b/vendor/xaboy/form-builder/src/UI/Iview/Components/TreeData.php
new file mode 100755
index 0000000..355e46e
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Components/TreeData.php
@@ -0,0 +1,106 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Components;
+
+
+use FormBuilder\Contract\OptionComponentInterface;
+use FormBuilder\Rule\CallPropsRule;
+
+/**
+ * Class TreeData
+ *
+ * @method $this id(string $id) Id, 必须唯一
+ * @method $this title(string $title) 标题
+ * @method $this expand(bool $bool) 是否展开直子节点, 默认为false
+ * @method $this disabled(bool $bool) 禁掉响应, 默认为false
+ * @method $this disableCheckbox(bool $bool) 禁掉 checkbox
+ * @method $this selected(bool $bool) 是否选中子节点
+ * @method $this checked(bool $bool) 是否勾选(如果勾选,子节点也会全部勾选)
+ */
+class TreeData implements OptionComponentInterface
+{
+    use CallPropsRule;
+
+    /**
+     * @var array
+     */
+    protected $children = [];
+
+    /**
+     * @var array
+     */
+    protected $props = [];
+
+    /**
+     * @var array
+     */
+    protected static $propsRule = [
+        'id' => 'string',
+        'title' => 'string',
+        'expand' => 'bool',
+        'disabled' => 'bool',
+        'disableCheckbox' => 'bool',
+        'selected' => 'bool',
+        'checked' => 'bool',
+    ];
+
+    /**
+     * TreeData constructor.
+     *
+     * @param       $id
+     * @param       $title
+     * @param array $children
+     */
+    public function __construct($id, $title, array $children = [])
+    {
+        $this->props['id'] = $id;
+        $this->props['title'] = $title;
+        $this->children = $children;
+    }
+
+    /**
+     * @param array $children
+     * @return $this
+     */
+    public function children(array $children)
+    {
+        $this->children = array_merge($this->children, $children);
+        return $this;
+    }
+
+    /**
+     * @param TreeData $child
+     * @return $this
+     */
+    public function child(TreeData $child)
+    {
+        $this->children[] = $child;
+        return $this;
+    }
+
+    /**
+     * @return array
+     */
+    public function getOption()
+    {
+        $children = [];
+        foreach ($this->children as $child) {
+            $children[] = $child instanceof TreeData
+                ? $child->getOption()
+                : $child;
+        }
+        $this->props['children'] = $children;
+        return $this->props;
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Components/Upload.php b/vendor/xaboy/form-builder/src/UI/Iview/Components/Upload.php
new file mode 100755
index 0000000..fcb1a58
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Components/Upload.php
@@ -0,0 +1,119 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Components;
+
+
+use FormBuilder\Driver\FormComponent;
+use FormBuilder\Factory\Iview;
+
+/**
+ * 上传组件
+ * Class Upload
+ *
+ * @method $this uploadType(string $uploadType) 上传文件类型,可选值为 image(图片上传),file(文件上传)
+ * @method $this action(string $action) 上传的地址
+ * @method $this multiple(bool $bool) 是否支持多选文件
+ * @method $this uploadName(string $name) 上传的文件字段名
+ * @method $this accept(string $accept) 接受上传的文件类型
+ * @method $this maxSize(int $size) 文件大小限制,单位 kb
+ * @method $this withCredentials(bool $bool) 支持发送 cookie 凭证信息, 默认为false
+ * @method $this maxLength(Int $length) 最大上传文件数, 0为无限
+ *
+ */
+class Upload extends FormComponent
+{
+    /**
+     * file类型
+     */
+    const TYPE_FILE = 'file';
+
+    /**
+     * image类型
+     */
+    const TYPE_IMAGE = 'image';
+
+
+    protected $defaultProps = [
+        'maxLength' => 0,
+        'type' => 'select',
+        'uploadType' => self::TYPE_FILE,
+        'headers' => [],
+        'data' => [],
+        'format' => [],
+        'show-upload-list' => false
+    ];
+
+    protected static $propsRule = [
+        'uploadType' => 'string',
+        'action' => 'string',
+        'multiple' => 'bool',
+        'uploadName' => ['string', 'name'],
+        'accept' => 'string',
+        'maxSize' => 'int',
+        'withCredentials' => 'bool',
+        'maxLength' => 'int'
+    ];
+
+    protected function init()
+    {
+        $this->name($this->field);
+    }
+
+    /**
+     * 设置上传的请求头部
+     *
+     * @param array $headers
+     * @return $this
+     */
+    public function headers(array $headers)
+    {
+        $this->props['headers'] = array_merge($this->props['headers'], $headers);
+        return $this;
+    }
+
+    /**
+     * 支持的文件类型,与 accept 不同的是,
+     * format 是识别文件的后缀名,accept 为 input 标签原生的 accept 属性,
+     * 会在选择文件时过滤,可以两者结合使用
+     *
+     * @param array $format
+     * @return $this
+     */
+    public function format(array $format)
+    {
+        $this->props['format'] = array_merge($this->props['format'], $format);
+        return $this;
+    }
+
+    /**
+     * 上传时附带的额外参数
+     *
+     * @param array $data
+     * @return $this
+     */
+    public function data(array $data)
+    {
+        $this->props['data'] = array_merge($this->props['data'], $data);
+        return $this;
+    }
+
+    protected function getPlaceHolder()
+    {
+        return '请上传' . $this->field;
+    }
+
+    public function createValidate()
+    {
+        return $this->props['maxLength'] == 1 ? Iview::validateStr() : Iview::validateArr();
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Config.php b/vendor/xaboy/form-builder/src/UI/Iview/Config.php
new file mode 100755
index 0000000..9015418
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Config.php
@@ -0,0 +1,218 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview;
+
+
+use FormBuilder\Contract\ConfigInterface;
+use FormBuilder\UI\Iview\Components\Button;
+use FormBuilder\UI\Iview\Components\Poptip;
+use FormBuilder\UI\Iview\Components\Tooltip;
+use FormBuilder\UI\Iview\Style\FormStyle;
+use FormBuilder\UI\Iview\Style\Row;
+
+class Config implements ConfigInterface
+{
+    const INFO_TYPE_POPTIP = 'poptip';
+
+    const INFO_TYPE_TOOLTIP = 'tooltip';
+
+    protected $config;
+
+    public function __construct(array $config = [])
+    {
+        $this->config = $config;
+    }
+
+    public function info($type)
+    {
+        if (is_array($type) || $type instanceof Poptip || $type instanceof Tooltip)
+            $this->config['info'] = $type;
+
+        return $this;
+    }
+
+    /**
+     * @param string $type tooltip,poptip
+     * @return Poptip|Tooltip
+     */
+    public function createInfo($type = self::INFO_TYPE_POPTIP)
+    {
+        if (strtolower($type) === self::INFO_TYPE_TOOLTIP)
+            $info = new Tooltip();
+        else
+            $info = new Poptip();
+        $this->info($info);
+        return $info;
+    }
+
+    /**
+     * 表单整体显示规则配置
+     *
+     * @param FormStyle|array $formStyle
+     * @return $this
+     */
+    public function formStyle($formStyle)
+    {
+        $this->config['form'] = $formStyle;
+        return $this;
+    }
+
+    public function createFormStyle(array $rule = [])
+    {
+        $formStyle = new FormStyle($rule);
+        $this->formStyle($formStyle);
+        return $formStyle;
+    }
+
+    /**
+     * 表单组件布局配置
+     *
+     * @param Row|array $row
+     * @return $this
+     */
+    public function row($row)
+    {
+        $this->config['row'] = $row;
+        return $this;
+    }
+
+    public function createRow(array $rule = [])
+    {
+        $row = new Row($rule);
+        $this->row($row);
+        return $row;
+    }
+
+    /**
+     * 提交按钮样式和布局配置
+     *
+     * @param Button|array|bool $btn
+     * @return $this
+     */
+    public function submitBtn($btn)
+    {
+        $this->config['submitBtn'] = $btn;
+        return $this;
+    }
+
+    public function createSubmitBtn()
+    {
+        $submitBtn = new Button();
+        $this->submitBtn($submitBtn);
+        return $submitBtn;
+    }
+
+    /**
+     * 开启事件注入
+     *
+     * @param bool $bool
+     * @return $this
+     */
+    public function injectEvent($bool)
+    {
+        $this->config['injectEvent'] = !!$bool;
+        return $this;
+    }
+
+    /**
+     * 重置按钮样式和布局配置
+     *
+     * @param Button|array|bool $btn
+     * @return $this
+     */
+    public function resetBtn($btn)
+    {
+        $this->config['resetBtn'] = $btn;
+        return $this;
+    }
+
+    /**
+     * @return Button
+     */
+    public function createResetBtn()
+    {
+        $resetBtn = new Button();
+        $this->resetBtn($resetBtn);
+        return $resetBtn;
+    }
+
+    /**
+     * @param Button $btn
+     * @return mixed
+     */
+    protected function parseButton(Button $btn)
+    {
+        $rule = $btn->build();
+        if (isset($rule['col']))
+            $rule['props']->col = $rule['col'];
+        return $rule['props'];
+    }
+
+    /**
+     * @param $info
+     * @return mixed
+     */
+    protected function parseInfo($info)
+    {
+        if ($info instanceof Poptip || $info instanceof Tooltip) {
+            $rule = $info->build();
+            $info = $rule['props'];
+            $info->type = $rule['type'];
+        }
+
+        return $info;
+    }
+
+    /**
+     * @return array
+     */
+    public function getConfig()
+    {
+        $config = $this->config;
+        if (isset($config['form']) && ($form = $config['form']) instanceof FormStyle)
+            $config['form'] = $form->getStyle();
+        if (isset($config['row']) && ($row = $config['row']) instanceof Row)
+            $config['row'] = $row->getStyle();
+        if (isset($config['submitBtn']) && ($submitBtn = $config['submitBtn']) instanceof Button)
+            $config['submitBtn'] = $this->parseButton($submitBtn);
+        if (isset($config['resetBtn']) && ($resetBtn = $config['resetBtn']) instanceof Button)
+            $config['resetBtn'] = $this->parseButton($resetBtn);
+        if (isset($config['info']))
+            $config['info'] = $this->parseInfo($config['info']);
+
+        return $config;
+    }
+
+
+    /**
+     * @param string $componentName
+     * @param array $config
+     * @return mixed
+     */
+    public function componentGlobalConfig($componentName, array $config)
+    {
+        if (!isset($this->config['global'])) $this->config['global'] = [];
+        $this->config['global'][$componentName] = $config;
+        return $this;
+    }
+
+
+    /**
+     * @param array $config
+     * @return $this
+     */
+    public function componentGlobalCommonConfig(array $config)
+    {
+        return $this->componentGlobalConfig('*', $config);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Style/Col.php b/vendor/xaboy/form-builder/src/UI/Iview/Style/Col.php
new file mode 100755
index 0000000..37db671
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Style/Col.php
@@ -0,0 +1,178 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Style;
+
+
+use FormBuilder\Contract\ColComponentInterface;
+
+/**
+ * col栅格规则
+ *
+ * Class Col
+ * @package FormBuilder\Style
+ */
+class Col implements ColComponentInterface
+{
+    protected $rule;
+
+    public function __construct(array $rule = [])
+    {
+        $this->rule = $rule;
+    }
+
+    /**
+     * 栅格的占位格数,可选值为0~24的整数,为 0 时,相当于display:none
+     *
+     * @param int|string $span
+     * @return $this
+     */
+    public function span($span)
+    {
+        $this->rule['span'] = $span;
+        return $this;
+    }
+
+    /**
+     * 栅格的顺序,在flex布局模式下有效
+     *
+     * @param int|string $order
+     * @return $this
+     */
+    public function order($order)
+    {
+        $this->rule['order'] = $order;
+        return $this;
+    }
+
+    /**
+     * 设置表单域 label 的宽度
+     *
+     * @param int|string $labelWidth
+     * @return $this
+     */
+    public function labelWidth($labelWidth)
+    {
+        $this->rule['labelWidth'] = $labelWidth;
+        return $this;
+    }
+
+    /**
+     * 栅格左侧的间隔格数,间隔内不可以有栅格
+     *
+     * @param int $offset
+     * @return $this
+     */
+    public function offset($offset)
+    {
+        $this->rule['offset'] = $offset;
+        return $this;
+    }
+
+    /**
+     * 栅格向右移动格数
+     *
+     * @param int $push
+     * @return $this
+     */
+    public function push($push)
+    {
+        $this->rule['push'] = $push;
+        return $this;
+    }
+
+    /**
+     * 栅格向左移动格数
+     *
+     * @param $pull
+     * @return $this
+     */
+    public function pull($pull)
+    {
+        $this->rule['pull'] = $pull;
+        return $this;
+    }
+
+    /**
+     * 组件的class
+     *
+     * @param $class
+     * @return $this
+     */
+    public function className($class)
+    {
+        $this->rule['className'] = (string)$class;
+        return $this;
+    }
+
+    /**
+     * <768px 响应式栅格,可为栅格数或一个包含其他属性的对象
+     *
+     * @param int|self $xs
+     * @return $this
+     */
+    public function xs($xs)
+    {
+        $this->rule['xs'] = $this->buildGrid($xs);
+        return $this;
+    }
+
+    /**
+     * ≥768px 响应式栅格,可为栅格数或一个包含其他属性的对象
+     *
+     * @param int|self $sm
+     * @return $this
+     */
+    public function sm($sm)
+    {
+        $this->rule['sm'] = $this->buildGrid($sm);
+        return $this;
+    }
+
+    /**
+     * ≥992px 响应式栅格,可为栅格数或一个包含其他属性的对象
+     *
+     * @param int|self $md
+     * @return $this
+     */
+    public function md($md)
+    {
+        $this->rule['md'] = $this->buildGrid($md);
+        return $this;
+    }
+
+    /**
+     * ≥1200px 响应式栅格,可为栅格数或一个包含其他属性的对象
+     *
+     * @param int|self $lg
+     * @return $this
+     */
+    public function lg($lg)
+    {
+        $this->rule['lg'] = $this->buildGrid($lg);
+        return $this;
+    }
+
+    protected function buildGrid($grid)
+    {
+        return $grid instanceof self ? $grid->getCol() : (int)$grid;
+    }
+
+    /**
+     * @return object
+     */
+    public function getCol()
+    {
+        return (object)$this->rule;
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Style/FormStyle.php b/vendor/xaboy/form-builder/src/UI/Iview/Style/FormStyle.php
new file mode 100755
index 0000000..e75be1e
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Style/FormStyle.php
@@ -0,0 +1,117 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Style;
+
+
+use FormBuilder\Contract\StyleInterface;
+
+/**
+ * form表单样式
+ * Class FormStyle
+ */
+class FormStyle implements StyleInterface
+{
+    /**
+     * @var array
+     */
+    protected $rule;
+
+    /**
+     * FormStyle constructor.
+     * @param array $rule
+     */
+    public function __construct(array $rule = [])
+    {
+        $this->rule = $rule;
+    }
+
+    /**
+     * 是否开启行内表单模式
+     *
+     * @param bool $bool
+     * @return $this
+     */
+    public function inline($bool)
+    {
+        $this->rule['inline'] = !!$bool;
+        return $this;
+    }
+
+    /**
+     * 设置表单 class
+     * @param $class
+     * @return $this
+     */
+    public function className($class)
+    {
+        $this->rule['className'] = $class;
+        return $this;
+    }
+
+    /**
+     * 表单域标签的位置,可选值为 left、right、top
+     *
+     * @param string $position
+     * @return $this
+     */
+    public function labelPosition($position)
+    {
+        $this->rule['labelPosition'] = $position;
+        return $this;
+    }
+
+    /**
+     * 表单域标签的宽度,所有的 FormItem 都会继承 Form 组件的 label-width 的值
+     *
+     * @param $labelWidth
+     * @return $this
+     */
+    public function labelWidth($labelWidth)
+    {
+        $this->rule['labelWidth'] = $labelWidth;
+        return $this;
+    }
+
+    /**
+     * 是否显示校验错误信息
+     *
+     * @param $showMessage
+     * @return $this
+     */
+    public function showMessage($showMessage)
+    {
+        $this->rule['showMessage'] = !!$showMessage;
+        return $this;
+    }
+
+    /**
+     * 原生的 autocomplete 属性,可选值为 true = off 或 false = on
+     *
+     * @param bool $bool
+     * @return $this
+     */
+    public function autocomplete($bool = false)
+    {
+        $this->rule['autocomplete'] = $bool === false ? 'on' : 'off';
+        return $this;
+    }
+
+    /**
+     * @return object
+     */
+    public function getStyle()
+    {
+        return (object)$this->rule;
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Style/Row.php b/vendor/xaboy/form-builder/src/UI/Iview/Style/Row.php
new file mode 100755
index 0000000..ce3d939
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Style/Row.php
@@ -0,0 +1,90 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Style;
+
+
+use FormBuilder\Contract\StyleInterface;
+
+/**
+ * row栅格规则
+ *
+ * Class Row
+ */
+class Row implements StyleInterface
+{
+    protected $rule;
+
+    public function __construct(array $rule = [])
+    {
+        $this->rule = $rule;
+    }
+
+    /**
+     * 栅格间距,单位 px,左右平分
+     *
+     * @param int $gutter
+     */
+    public function gutter($gutter)
+    {
+        $this->rule['gutter'] = (float)$gutter;
+    }
+
+    /**
+     * 布局模式,可选值为flex或不选,在现代浏览器下有效
+     *
+     * @param string $type
+     */
+    public function type($type)
+    {
+        $this->rule['type'] = $type;
+    }
+
+    /**
+     * flex 布局下的垂直对齐方式,可选值为top、middle、bottom
+     *
+     * @param string $align
+     */
+    public function align($align)
+    {
+        $this->rule['align'] = $align;
+    }
+
+    /**
+     * flex 布局下的水平排列方式,可选值为start、end、center、space-around、space-between
+     *
+     * @param string $justify
+     */
+    public function justify($justify)
+    {
+        $this->rule['justify'] = $justify;
+    }
+
+    /**
+     * 组件的class
+     *
+     * @param string $className
+     */
+    public function className($className)
+    {
+        $this->rule['className'] = $className;
+    }
+
+    /**
+     * @return object
+     */
+    public function getStyle()
+    {
+        return (object)$this->rule;
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Traits/CascaderFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Iview/Traits/CascaderFactoryTrait.php
new file mode 100755
index 0000000..43ea767
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Traits/CascaderFactoryTrait.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Traits;
+
+
+use FormBuilder\UI\Iview\Components\Cascader;
+
+trait CascaderFactoryTrait
+{
+    /**
+     * 多级联动组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param array $value
+     * @param string $type
+     * @return Cascader
+     */
+    public static function cascader($field, $title, array $value = [], $type = Cascader::TYPE_OTHER)
+    {
+        $cascader = new Cascader($field, $title, $value);
+        $cascader->type($type);
+        return $cascader;
+    }
+
+
+    /**
+     * 省市二级联动
+     *
+     * @param string $field
+     * @param string $title
+     * @param array|string $province
+     * @param string $city
+     * @return Cascader
+     */
+    public static function city($field, $title, $province = [], $city = '')
+    {
+        if (is_array($province))
+            $value = $province;
+        else
+            $value = [(string)$province, (string)$city];
+
+        $cascader = self::cascader($field, $title, $value, Cascader::TYPE_CITY);
+        $cascader->jsData('province_city');
+        return $cascader;
+    }
+
+
+    /**
+     * 省市区三级联动
+     *
+     * @param string $field
+     * @param string $title
+     * @param array|string $province
+     * @param string $city
+     * @param string $area
+     * @return Cascader
+     */
+    public static function cityArea($field, $title, $province = [], $city = '', $area = '')
+    {
+        if (is_array($province))
+            $value = $province;
+        else
+            $value = [(string)$province, (string)$city, (string)$area];
+
+        $cascader = self::cascader($field, $title, $value, Cascader::TYPE_CITY_AREA);
+        $cascader->jsData('province_city_area');
+        return $cascader;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Traits/CheckBoxFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Iview/Traits/CheckBoxFactoryTrait.php
new file mode 100755
index 0000000..9f8cd8a
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Traits/CheckBoxFactoryTrait.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Traits;
+
+use FormBuilder\UI\Iview\Components\Checkbox;
+
+trait CheckBoxFactoryTrait
+{
+    /**
+     * 多选框组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param array $value
+     * @return Checkbox
+     */
+    public static function checkbox($field, $title, array $value = [])
+    {
+        return new Checkbox($field, $title, $value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Traits/ColorPickerFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Iview/Traits/ColorPickerFactoryTrait.php
new file mode 100755
index 0000000..b2e8e81
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Traits/ColorPickerFactoryTrait.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Traits;
+
+
+use FormBuilder\UI\Iview\Components\ColorPicker;
+
+trait ColorPickerFactoryTrait
+{
+    /**
+     * 颜色选择组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $value
+     * @return ColorPicker
+     */
+    public static function color($field, $title, $value = '')
+    {
+        return new ColorPicker($field, $title, (string)$value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Traits/DatePickerFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Iview/Traits/DatePickerFactoryTrait.php
new file mode 100755
index 0000000..77aa1dd
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Traits/DatePickerFactoryTrait.php
@@ -0,0 +1,128 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Traits;
+
+
+use FormBuilder\UI\Iview\Components\DatePicker;
+
+trait DatePickerFactoryTrait
+{
+    /**
+     * 日期组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $value
+     * @param string $type
+     * @return DatePicker
+     */
+    public static function datePicker($field, $title, $value = '', $type = DatePicker::TYPE_DATE)
+    {
+        $datePicker = new DatePicker($field, $title, $value);
+        return $datePicker->type($type);
+    }
+
+    /**
+     * 单选日期
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $value
+     * @return DatePicker
+     */
+    public static function date($field, $title, $value = '')
+    {
+        return self::datePicker($field, $title, (string)$value, DatePicker::TYPE_DATE);
+    }
+
+    /**
+     * 多选日期
+     *
+     * @param string $field
+     * @param string $title
+     * @param array $value
+     * @return DatePicker
+     */
+    public static function dateMultiple($field, $title, array $value)
+    {
+        $date = self::datePicker($field, $title, $value, DatePicker::TYPE_DATE);
+        return $date->multiple(true);
+    }
+
+    /**
+     * 日期区间选择
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $startDate
+     * @param string $endDate
+     * @return DatePicker
+     */
+    public static function dateRange($field, $title, $startDate = '', $endDate = '')
+    {
+        return self::datePicker($field, $title, [(string)$startDate, (string)$endDate], DatePicker::TYPE_DATE_RANGE);
+    }
+
+    /**
+     * 单选日期时间
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $value
+     * @return DatePicker
+     */
+    public static function dateTime($field, $title, $value = '')
+    {
+        return self::datePicker($field, $title, (string)$value, DatePicker::TYPE_DATE_TIME);
+    }
+
+    /**
+     * 日期时间区间选择
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $startDate
+     * @param string $endDate
+     * @return DatePicker
+     */
+    public static function dateTimeRange($field, $title, $startDate = '', $endDate = '')
+    {
+        return self::datePicker($field, $title, [(string)$startDate, (string)$endDate], DatePicker::TYPE_DATE_TIME_RANGE);
+    }
+
+    /**
+     * 选择年
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $value
+     * @return DatePicker
+     */
+    public static function year($field, $title, $value = '')
+    {
+        return self::datePicker($field, $title, (string)$value, DatePicker::TYPE_YEAR);
+    }
+
+    /**
+     * 选择月
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $value
+     * @return DatePicker
+     */
+    public static function month($field, $title, $value = '')
+    {
+        return self::datePicker($field, $title, (string)$value, DatePicker::TYPE_MONTH);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Traits/FormStyleFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Iview/Traits/FormStyleFactoryTrait.php
new file mode 100755
index 0000000..3a9aaf9
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Traits/FormStyleFactoryTrait.php
@@ -0,0 +1,55 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Traits;
+
+
+use FormBuilder\UI\Iview\Style\Col;
+use FormBuilder\UI\Iview\Style\FormStyle;
+use FormBuilder\UI\Iview\Style\Row;
+
+trait FormStyleFactoryTrait
+{
+
+    /**
+     * 组件布局规则类
+     *
+     * @param array $rule
+     * @return Col
+     */
+    public static function col(array $rule = [])
+    {
+        return new Col($rule);
+    }
+
+    /**
+     * 表格布局规则类
+     *
+     * @param $rule
+     * @return Row
+     */
+    public static function row(array $rule = [])
+    {
+        return new Row($rule);
+    }
+
+    /**
+     * 表格样式类
+     *
+     * @param array $rule
+     * @return FormStyle
+     */
+    public static function style(array $rule = [])
+    {
+        return new FormStyle($rule);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Traits/FrameFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Iview/Traits/FrameFactoryTrait.php
new file mode 100755
index 0000000..eee734c
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Traits/FrameFactoryTrait.php
@@ -0,0 +1,126 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Traits;
+
+
+use FormBuilder\UI\Iview\Components\Frame;
+
+trait FrameFactoryTrait
+{
+    /**
+     * 框架组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $src
+     * @param string|array $value
+     * @param string $type
+     * @return Frame
+     */
+    public static function frame($field, $title, $src, $value = [], $type = Frame::TYPE_INPUT)
+    {
+        $length = is_array($value) ? 0 : 1;
+        $frame = new Frame($field, $title, $value);
+        return $frame->maxLength($length)->src($src)->type($type);
+    }
+
+    /**
+     * 使用input类型显示,多选
+     * value为Array类型
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $src
+     * @param array $value
+     * @return Frame
+     */
+    public static function frameInputs($field, $title, $src, array $value = [])
+    {
+        return self::frame($field, $title, $src, $value);
+    }
+
+    /**
+     * 使用文件类型显示,多选
+     * value为Array类型
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $src
+     * @param array $value
+     * @return Frame
+     */
+    public static function frameFiles($field, $title, $src, array $value = [])
+    {
+        return self::frame($field, $title, $src, $value, Frame::TYPE_FILE);
+    }
+
+    /**
+     * 使用文件类型显示,多选
+     * value为Array类型
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $src
+     * @param array $value
+     * @return Frame
+     */
+    public static function frameImages($field, $title, $src, array $value = [])
+    {
+        return self::frame($field, $title, $src, $value, Frame::TYPE_IMAGE);
+    }
+
+    /**
+     * 使用input类型显示,单选
+     * value为string类型
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $src
+     * @param string $value
+     * @return Frame
+     */
+    public static function frameInput($field, $title, $src, $value = '')
+    {
+        return self::frame($field, $title, $src, (string)$value);
+    }
+
+    /**
+     * 使用文件类型显示,单选
+     * value为string类型
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $src
+     * @param string $value
+     * @return Frame
+     */
+    public static function frameFile($field, $title, $src, $value = '')
+    {
+        return self::frame($field, $title, $src, (string)$value, Frame::TYPE_FILE);
+    }
+
+    /**
+     * 使用文件类型显示,单选
+     * value为string类型
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $src
+     * @param string $value
+     * @return Frame
+     */
+    public static function frameImage($field, $title, $src, $value = '')
+    {
+        return self::frame($field, $title, $src, (string)$value, Frame::TYPE_IMAGE);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Traits/GroupFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Iview/Traits/GroupFactoryTrait.php
new file mode 100755
index 0000000..6c7240d
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Traits/GroupFactoryTrait.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Traits;
+
+
+use FormBuilder\UI\Iview\Components\Group;
+
+trait GroupFactoryTrait
+{
+
+    /**
+     * 数组组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param array $value
+     * @return Group
+     */
+    public static function group($field, $title, $value = [])
+    {
+        return new Group($field, $title, $value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Traits/HiddenFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Iview/Traits/HiddenFactoryTrait.php
new file mode 100755
index 0000000..434c3f3
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Traits/HiddenFactoryTrait.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Traits;
+
+
+use FormBuilder\UI\Iview\Components\Hidden;
+
+trait HiddenFactoryTrait
+{
+    /**
+     * 隐藏组件
+     *
+     * @param string $field
+     * @param mixed $value
+     * @return Hidden
+     */
+    public static function hidden($field, $value)
+    {
+        return new Hidden($field, $value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Traits/InputFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Iview/Traits/InputFactoryTrait.php
new file mode 100755
index 0000000..02d6a32
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Traits/InputFactoryTrait.php
@@ -0,0 +1,112 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Traits;
+
+
+use FormBuilder\UI\Iview\Components\Input;
+
+trait InputFactoryTrait
+{
+    /**
+     * input输入框组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $value
+     * @param string $type
+     * @return Input
+     */
+    public static function input($field, $title, $value = '', $type = Input::TYPE_TEXT)
+    {
+        $input = new Input($field, $title, (string)$value);
+        return $input->type($type);
+    }
+
+    /**
+     * text 类型输入框
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $value
+     * @return Input
+     */
+    public static function text($field, $title, $value = '')
+    {
+        return self::input($field, $title, $value);
+    }
+
+    /**
+     * password 类型输入框
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $value
+     * @return Input
+     */
+    public static function password($field, $title, $value = '')
+    {
+        return self::input($field, $title, $value, Input::TYPE_PASSWORD);
+    }
+
+    /**
+     * textarea 类型输入框
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $value
+     * @return Input
+     */
+    public static function textarea($field, $title, $value = '')
+    {
+        return self::input($field, $title, $value, Input::TYPE_TEXTAREA);
+    }
+
+    /**
+     * url 类型输入框
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $value
+     * @return Input
+     */
+    public static function url($field, $title, $value = '')
+    {
+        return self::input($field, $title, $value, Input::TYPE_URL);
+    }
+
+    /**
+     * email 类型输入框
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $value
+     * @return Input
+     */
+    public static function email($field, $title, $value = '')
+    {
+        return self::input($field, $title, $value, Input::TYPE_EMAIL);
+    }
+
+    /**
+     * date 类型输入框
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $value
+     * @return Input
+     */
+    public static function idate($field, $title, $value = '')
+    {
+        return self::input($field, $title, $value, Input::TYPE_DATE);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Traits/InputNumberFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Iview/Traits/InputNumberFactoryTrait.php
new file mode 100755
index 0000000..f65a63c
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Traits/InputNumberFactoryTrait.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Traits;
+
+
+use FormBuilder\UI\Iview\Components\InputNumber;
+
+trait InputNumberFactoryTrait
+{
+    /**
+     * 数字输入框组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param null|number $value
+     * @return InputNumber
+     */
+    public static function number($field, $title, $value = null)
+    {
+        return new InputNumber($field, $title, $value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Traits/RadioFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Iview/Traits/RadioFactoryTrait.php
new file mode 100755
index 0000000..40f9407
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Traits/RadioFactoryTrait.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Traits;
+
+
+use FormBuilder\UI\Iview\Components\Radio;
+
+trait RadioFactoryTrait
+{
+    /**
+     * 单选框组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param string|number $value
+     * @return Radio
+     */
+    public static function radio($field, $title, $value = '')
+    {
+        return new Radio($field, $title, $value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Traits/RateFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Iview/Traits/RateFactoryTrait.php
new file mode 100755
index 0000000..6b94035
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Traits/RateFactoryTrait.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Traits;
+
+
+use FormBuilder\UI\Iview\Components\Rate;
+
+trait RateFactoryTrait
+{
+    /**
+     * 评分选择组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param int|float $value
+     * @return Rate
+     */
+    public static function rate($field, $title, $value = 0)
+    {
+        return new Rate($field, $title, (float)$value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Traits/SelectFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Iview/Traits/SelectFactoryTrait.php
new file mode 100755
index 0000000..cb57a8e
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Traits/SelectFactoryTrait.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Traits;
+
+
+use FormBuilder\UI\Iview\Components\Select;
+
+trait SelectFactoryTrait
+{
+    /**
+     * 下拉选择组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param mixed $value
+     * @return Select
+     */
+    public static function select($field, $title, $value = '')
+    {
+        $multiple = is_array($value) ? true : false;
+        $select = new Select($field, $title, $value);
+        $select->multiple($multiple);
+        return $select;
+    }
+
+    /**
+     * 多选
+     *
+     * @param string $field
+     * @param string $title
+     * @param array $value
+     * @return Select
+     */
+    public static function selectMultiple($field, $title, array $value = [])
+    {
+        return self::select($field, $title, $value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Traits/SliderFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Iview/Traits/SliderFactoryTrait.php
new file mode 100755
index 0000000..b3a0b70
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Traits/SliderFactoryTrait.php
@@ -0,0 +1,50 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Traits;
+
+
+use FormBuilder\UI\Iview\Components\Slider;
+
+trait SliderFactoryTrait
+{
+    /**
+     * 滑块组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param int|array $value
+     * @return Slider
+     */
+    public static function slider($field, $title, $value = 0)
+    {
+        $slider = new Slider($field, $title, $value);
+        if (is_array($value)) $slider->range(true);
+        return $slider;
+    }
+
+    /**
+     * 区间选择
+     *
+     * @param string $field
+     * @param string $title
+     * @param int $start
+     * @param int $end
+     * @return Slider
+     */
+    public static function sliderRange($field, $title, $start = 0, $end = 0)
+    {
+        $slider = self::slider($field, $title, [(int)$start, (int)$end]);
+        $slider->range(true);
+        return $slider;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Traits/SwitchesFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Iview/Traits/SwitchesFactoryTrait.php
new file mode 100755
index 0000000..bcfcde8
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Traits/SwitchesFactoryTrait.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Traits;
+
+
+use FormBuilder\UI\Iview\Components\Switches;
+
+trait SwitchesFactoryTrait
+{
+    /**
+     * 开关组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param mixed $value
+     * @return Switches
+     */
+    public static function switches($field, $title, $value = '0')
+    {
+        return new Switches($field, $title, $value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Traits/TimePickerFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Iview/Traits/TimePickerFactoryTrait.php
new file mode 100755
index 0000000..6d6327a
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Traits/TimePickerFactoryTrait.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Traits;
+
+
+use FormBuilder\UI\Iview\Components\TimePicker;
+
+trait TimePickerFactoryTrait
+{
+    /**
+     * 时间选择组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $value
+     * @param string|array $type
+     * @return TimePicker
+     */
+    public static function timePicker($field, $title, $value = '', $type = TimePicker::TYPE_TIME)
+    {
+        $timePicker = new TimePicker($field, $title, $value);
+        return $timePicker->type($type);
+    }
+
+    /**
+     * 时间选择
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $value
+     * @return TimePicker
+     */
+    public static function time($field, $title, $value = '')
+    {
+        return self::timePicker($field, $title, (string)$value);
+    }
+
+    /**
+     * 时间区间选择
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $startTime
+     * @param string $endTime
+     * @return TimePicker
+     */
+    public static function timeRange($field, $title, $startTime = '', $endTime = '')
+    {
+        return self::timePicker($field, $title, [(string)$startTime, (string)$endTime], TimePicker::TYPE_TIME_RANGE);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Traits/TreeFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Iview/Traits/TreeFactoryTrait.php
new file mode 100755
index 0000000..751066c
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Traits/TreeFactoryTrait.php
@@ -0,0 +1,74 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Traits;
+
+
+use FormBuilder\UI\Iview\Components\Tree;
+use FormBuilder\UI\Iview\Components\TreeData;
+
+trait TreeFactoryTrait
+{
+    /**
+     * 树形组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param mixed $value
+     * @param string $type
+     * @return Tree
+     */
+    public static function tree($field, $title, $value = [], $type = Tree::TYPE_CHECKED)
+    {
+        $tree = new Tree($field, $title, $value);
+        return $tree->type($type);
+    }
+
+    /**
+     * 获取选中的值
+     *
+     * @param string $field
+     * @param string $title
+     * @param mixed $value
+     * @return Tree
+     */
+    public static function treeSelected($field, $title, $value = [])
+    {
+        return self::tree($field, $title, $value, Tree::TYPE_SELECTED);
+    }
+
+    /**
+     * 获取勾选的值
+     *
+     * @param string $field
+     * @param string $title
+     * @param mixed $value
+     * @return Tree
+     */
+    public static function treeChecked($field, $title, $value = [])
+    {
+        return self::tree($field, $title, $value)->showCheckbox(true);
+    }
+
+    /**
+     * 树形组件数据 date 类
+     *
+     * @param mixed $id
+     * @param string $title
+     * @param array $children
+     * @return TreeData
+     */
+    public static function treeData($id, $title, array $children = [])
+    {
+        return new TreeData($id, $title, $children);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Traits/UploadFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Iview/Traits/UploadFactoryTrait.php
new file mode 100755
index 0000000..5f80e3e
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Traits/UploadFactoryTrait.php
@@ -0,0 +1,110 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\UI\Iview\Traits;
+
+
+use FormBuilder\UI\Iview\Components\Upload;
+
+trait UploadFactoryTrait
+{
+    /**
+     * 上传组件
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $action
+     * @param string|array $value
+     * @param string $type
+     * @return Upload
+     */
+    public static function upload($field, $title, $action, $value = '', $type = Upload::TYPE_FILE)
+    {
+        $upload = new Upload($field, $title, $value);
+        return $upload->uploadType($type)->action($action);
+    }
+
+    /**
+     * 图片上传
+     * value 为 Array类型
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $action
+     * @param array $value
+     * @return Upload
+     */
+    public static function uploadImages($field, $title, $action, array $value = [])
+    {
+        $upload = self::upload($field, $title, $action, $value, Upload::TYPE_IMAGE);
+        return $upload->format(['jpg', 'jpeg', 'png', 'gif'])->accept('image/*');
+    }
+
+    /**
+     * 文件上传
+     * value 为 Array类型
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $action
+     * @param array $value
+     * @return Upload
+     */
+    public static function uploadFiles($field, $title, $action, array $value = [])
+    {
+        return self::upload($field, $title, $action, $value, Upload::TYPE_FILE);
+    }
+
+    /**
+     * 单图片上传
+     * value 为string类型
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $action
+     * @param string $value
+     * @return Upload
+     * @deprecated
+     */
+    public static function uploadImageOne($field, $title, $action, $value = '')
+    {
+        $upload = self::upload($field, $title, $action, (string)$value, Upload::TYPE_IMAGE);
+        return $upload->format(['jpg', 'jpeg', 'png', 'gif'])->accept('image/*')->maxLength(1);
+    }
+
+    /**
+     * 单图片上传
+     * value 为string类型
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $action
+     * @param string $value
+     * @return Upload
+     */
+    public static function uploadImage($field, $title, $action, $value = '')
+    {
+        $upload = self::upload($field, $title, $action, (string)$value, Upload::TYPE_IMAGE);
+        return $upload->format(['jpg', 'jpeg', 'png', 'gif'])->accept('image/*')->maxLength(1);
+    }
+
+    /**
+     * 单文件上传
+     * value 为string类型
+     *
+     * @param string $field
+     * @param string $title
+     * @param string $action
+     * @param string $value
+     * @return Upload
+     */
+    public static function uploadFile($field, $title, $action, $value = '')
+    {
+        $upload = self::upload($field, $title, $action, (string)$value, Upload::TYPE_FILE);
+        return $upload->maxLength(1);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Traits/ValidateFactoryTrait.php b/vendor/xaboy/form-builder/src/UI/Iview/Traits/ValidateFactoryTrait.php
new file mode 100755
index 0000000..0dee74e
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Traits/ValidateFactoryTrait.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview\Traits;
+
+use FormBuilder\UI\Iview\Validate as IViewValidate;
+
+trait ValidateFactoryTrait
+{
+    public static function validateStr($trigger = IViewValidate::TRIGGER_CHANGE)
+    {
+        return new IViewValidate(IViewValidate::TYPE_STRING, $trigger);
+    }
+
+    public static function validateArr($trigger = IViewValidate::TRIGGER_CHANGE)
+    {
+        return new IViewValidate(IViewValidate::TYPE_ARRAY, $trigger);
+    }
+
+    public static function validateNum($trigger = IViewValidate::TRIGGER_CHANGE)
+    {
+        return new IViewValidate(IViewValidate::TYPE_NUMBER, $trigger);
+    }
+
+    public static function validateDate($trigger = IViewValidate::TRIGGER_CHANGE)
+    {
+        return new IViewValidate(IViewValidate::TYPE_DATE, $trigger);
+    }
+
+    public static function validateInt($trigger = IViewValidate::TRIGGER_CHANGE)
+    {
+        return new IViewValidate(IViewValidate::TYPE_INTEGER, $trigger);
+    }
+
+    public static function validateFloat($trigger = IViewValidate::TRIGGER_CHANGE)
+    {
+        return new IViewValidate(IViewValidate::TYPE_FLOAT, $trigger);
+    }
+
+    public static function validateObject($trigger = IViewValidate::TRIGGER_CHANGE)
+    {
+        return new IViewValidate(IViewValidate::TYPE_OBJECT, $trigger);
+    }
+
+    public static function validateEmail($trigger = IViewValidate::TRIGGER_CHANGE)
+    {
+        return new IViewValidate(IViewValidate::TYPE_EMAIL, $trigger);
+    }
+
+    public static function validateEnum($trigger = IViewValidate::TRIGGER_CHANGE)
+    {
+        return new IViewValidate(IViewValidate::TYPE_ENUM, $trigger);
+    }
+
+    public static function validateUrl($trigger = IViewValidate::TRIGGER_CHANGE)
+    {
+        return new IViewValidate(IViewValidate::TYPE_URL, $trigger);
+    }
+
+    public static function validateHex($trigger = IViewValidate::TRIGGER_CHANGE)
+    {
+        return new IViewValidate(IViewValidate::TYPE_HEX, $trigger);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/UI/Iview/Validate.php b/vendor/xaboy/form-builder/src/UI/Iview/Validate.php
new file mode 100755
index 0000000..d67fe36
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/UI/Iview/Validate.php
@@ -0,0 +1,225 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\UI\Iview;
+
+
+use FormBuilder\Contract\ValidateInterface;
+
+class Validate implements ValidateInterface
+{
+    const TYPE_STRING = 'string';
+
+    const TYPE_ARRAY = 'array';
+
+    const TYPE_NUMBER = 'number';
+
+    const TYPE_INTEGER = 'integer';
+
+    const TYPE_FLOAT = 'float';
+
+    const TYPE_OBJECT = 'object';
+
+    const TYPE_ENUM = 'enum';
+
+    const TYPE_URL = 'url';
+
+    const TYPE_HEX = 'hex';
+
+    const TYPE_EMAIL = 'email';
+
+    const TYPE_DATE = 'date';
+
+    const TRIGGER_CHANGE = 'change';
+
+    const TRIGGER_BLUR = 'blur';
+
+    const TRIGGER_SUBMIT = 'submit';
+
+    protected $validate = [
+        'fields' => []
+    ];
+
+    protected $type;
+
+    protected $trigger;
+
+    /**
+     * Validate constructor.
+     * @param string $type
+     * @param string $trigger
+     */
+    public function __construct($type, $trigger = self::TRIGGER_CHANGE)
+    {
+        $this->type($type);
+        $this->trigger($trigger);
+    }
+
+    /**
+     * @param string $trigger
+     * @return $this
+     */
+    public function trigger($trigger)
+    {
+        $this->trigger = (string)$trigger;
+        return $this;
+    }
+
+    /**
+     * @param $type
+     * @return $this
+     */
+    public function type($type)
+    {
+
+        $this->type = (string)$type;
+        return $this;
+    }
+
+
+    public function set($validate)
+    {
+        array_merge($this->validate, $validate);
+        if (!is_array($validate['fields'])) $validate['fields'] = [];
+
+        return $this;
+    }
+
+    public function fields(array $fields)
+    {
+        $this->validate['fields'] = array_merge($this->validate['fields'], $fields);
+        return $this;
+    }
+
+    public function field($field, $validate)
+    {
+        $this->validate['fields'][$field] = $validate;
+        return $this;
+    }
+
+    /**
+     * 必填
+     *
+     * @return $this
+     */
+    public function required()
+    {
+        $this->validate['required'] = true;
+        return $this;
+    }
+
+    /**
+     * 长度或值必须在这个范围内
+     *
+     * @param int|float $min
+     * @param int|float $max
+     * @return $this
+     */
+    public function range($min, $max)
+    {
+        $this->validate['min'] = (float)$min;
+        $this->validate['max'] = (float)$max;
+        return $this;
+    }
+
+    /**
+     * 长度或值必须大于这个值
+     *
+     * @param int|float $min
+     * @return $this
+     */
+    public function min($min)
+    {
+        $this->validate['min'] = (float)$min;
+        return $this;
+    }
+
+    /**
+     * 长度或值必须小于这个值
+     *
+     * @param int|float $max
+     * @return $this
+     */
+    public function max($max)
+    {
+        $this->validate['max'] = (float)$max;
+        return $this;
+    }
+
+    /**
+     * 长度或值必须等于这个值
+     *
+     * @param int $length
+     * @return $this
+     */
+    public function length($length)
+    {
+        $this->validate['len'] = (int)$length;
+        return $this;
+    }
+
+    /**
+     * 值必须在 list 中
+     *
+     * @param array $list
+     * @return $this
+     */
+    public function enum(array $list)
+    {
+        $this->validate['enum'] = $list;
+        return $this;
+    }
+
+    /**
+     * 错误信息
+     * @param $message
+     * @return $this
+     */
+    public function message($message)
+    {
+        $this->validate['message'] = (string)$message;
+        return $this;
+    }
+
+    /**
+     * 正则
+     *
+     * @param $pattern
+     * @return $this
+     */
+    public function pattern($pattern)
+    {
+        $this->validate['pattern'] = (string)$pattern;
+        return $this;
+    }
+
+    public function getValidate()
+    {
+        $validate = $this->validate;
+        $validate['type'] = $this->type;
+        $validate['trigger'] = $this->trigger;
+        $fields = $validate['fields'];
+
+        if (!($fieldCount = count($fields)) && count($validate) === 1)
+            return [];
+
+        if ($fieldCount) {
+            foreach ($fields as $k => $field) {
+                $fields[$k] = $field instanceof self ? $field->getValidate() : $field;
+            }
+            $validate['fields'] = (object)$fields;
+        } else {
+            unset($validate['fields']);
+        }
+        return $validate;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/Util.php b/vendor/xaboy/form-builder/src/Util.php
new file mode 100755
index 0000000..ff5cb01
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/Util.php
@@ -0,0 +1,25 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder;
+
+
+use FormBuilder\Contract\CustomComponentInterface;
+use FormBuilder\Contract\FormComponentInterface;
+
+class Util
+{
+    public static function isComponent($component)
+    {
+        return $component instanceof CustomComponentInterface || $component instanceof FormComponentInterface;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/components/Cascader.php b/vendor/xaboy/form-builder/src/components/Cascader.php
new file mode 100755
index 0000000..a5daf57
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/components/Cascader.php
@@ -0,0 +1,151 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\components;
+
+
+use FormBuilder\FormComponentDriver;
+use FormBuilder\Helper;
+
+/**
+ * 多级联动组件
+ * Class Cascader
+ *
+ * @package FormBuilder\components
+ * @method $this type(String $type) 数据类型, 支持 city_area(省市区三级联动), city (省市二级联动), other (自定义)
+ * @method $this disabled(Boolean $bool) 是否禁用选择器
+ * @method $this clearable(Boolean $bool) 是否支持清除
+ * @method $this placeholder(String $placeholder)
+ * @method $this trigger(String $trigger) 次级菜单展开方式,可选值为 click 或 hover
+ * @method $this changeOnSelect(Boolean $bool) 当此项为 true 时,点选每级菜单选项值都会发生变化, 默认为 false
+ * @method $this size(String $size) 输入框大小,可选值为large和small或者不填
+ * @method $this filterable(Boolean $bool) 是否支持搜索
+ * @method $this notFoundText(String $text) 当搜索列表为空时显示的内容
+ * @method $this transfer(Boolean $bool) /是否将弹层放置于 body 内,在 Tabs、带有 fixed 的 Table 列内使用时,建议添加此属性,它将不受父级样式影响,从而达到更好的效果
+ */
+class Cascader extends FormComponentDriver
+{
+    /**
+     * @var string
+     */
+    protected $name = 'cascader';
+
+    /**
+     *
+     */
+    const TYPE_CITY_AREA = 'city_area';
+    /**
+     *
+     */
+    const TYPE_CITY = 'city';
+    /**
+     *
+     */
+    const TYPE_OTHER = 'other';
+
+    /**
+     * @var array
+     */
+    protected $props = [
+        'type' => self::TYPE_OTHER,
+        'data' => []
+    ];
+
+    /**
+     * @var array
+     */
+    protected static $propsRule = [
+        'type' => 'string',
+        'disabled' => 'boolean',
+        'clearable' => 'boolean',
+        'changeOnSelect' => 'boolean',
+        'filterable' => 'boolean',
+        'transfer' => 'boolean',
+        'placeholder' => 'string',
+        'trigger' => 'string',
+        'size' => 'string',
+        'notFoundText' => 'string',
+    ];
+
+    protected function init()
+    {
+        $this->placeholder($this->getPlaceHolder());
+    }
+
+    /**
+     * @param array $value
+     * @return $this
+     */
+    public function value($value)
+    {
+        if (!is_array($value)) $value = [];
+        $this->value = $value;
+        return $this;
+    }
+
+    /**
+     * 可选项的数据源
+     * 例如:{
+     *    "value":"北京市", "label":"北京市", "children":[{
+     *        "value":"东城区", "label":"东城区"
+     *    }]
+     *  }
+     *
+     * @param array $data
+     * @return $this
+     */
+    public function data(array $data)
+    {
+        if (!is_array($this->props['data'])) $this->props['data'] = [];
+        $this->props['data'] = array_merge($this->props['data'], $data);
+        return $this;
+    }
+
+    /**
+     * @param $var
+     * @return $this
+     */
+    public function jsData($var)
+    {
+        $this->props['data'] = 'js.' . $var;
+        return $this;
+    }
+
+    /**
+     * 获取组件类型
+     *
+     * @return mixed
+     */
+    public function getType()
+    {
+        return $this->props['type'];
+    }
+
+    /**
+     * @return Validate
+     */
+    public function getValidateHandler()
+    {
+        return Validate::arr();
+    }
+
+    /**
+     * @return array
+     */
+    public function build()
+    {
+        return [
+            'type' => $this->name,
+            'field' => $this->field,
+            'title' => $this->title,
+            'value' => $this->value,
+            'props' => (object)$this->props,
+            'validate' => $this->validate,
+            'col' => $this->col
+        ];
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/components/Checkbox.php b/vendor/xaboy/form-builder/src/components/Checkbox.php
new file mode 100755
index 0000000..5af9ac7
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/components/Checkbox.php
@@ -0,0 +1,91 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\components;
+
+
+use FormBuilder\FormComponentDriver;
+use FormBuilder\Helper;
+use FormBuilder\traits\component\ComponentOptionsTrait;
+
+/**
+ * 复选框组件
+ * Class Checkbox
+ *
+ * @package FormBuilder\components
+ * @method $this size(String $size) 多选框组的尺寸,可选值为 large、small、default 或者不设置
+ */
+class Checkbox extends FormComponentDriver
+{
+    use ComponentOptionsTrait;
+
+    /**
+     * @var string
+     */
+    protected $name = 'checkbox';
+
+    /**
+     * @var array
+     */
+    protected $value = [];
+
+    /**
+     * @var array
+     */
+    protected static $propsRule = [
+        'size' => 'string'
+    ];
+
+    /**
+     * @param $value
+     * @return $this
+     */
+    public function value($value)
+    {
+        if ($value === null) return $this;
+        if (!is_array($value))
+            $this->value[] = (string)$value;
+        else {
+            foreach ($value as $v) {
+                $this->value[] = (string)$v;
+            }
+        }
+        return $this;
+    }
+
+    public function getValidateHandler()
+    {
+        return Validate::arr();
+    }
+
+    /**
+     * @return array
+     */
+    public function build()
+    {
+        $options = [];
+        foreach ($this->options as $option) {
+            if ($option instanceof Option)
+                $options[] = $option->build();
+        }
+        $value = array_unique($this->value);
+        foreach ($value as $k => $v) {
+            $value[$k] = (string)$v;
+        }
+        return [
+            'type' => $this->name,
+            'field' => $this->field,
+            'title' => $this->title,
+            'value' => $value,
+            'props' => (object)$this->props,
+            'options' => $options,
+            'validate' => $this->validate,
+            'col' => $this->col
+        ];
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/components/Col.php b/vendor/xaboy/form-builder/src/components/Col.php
new file mode 100755
index 0000000..9bbf9af
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/components/Col.php
@@ -0,0 +1,85 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\components;
+
+
+use FormBuilder\interfaces\FormComponentInterFace;
+use FormBuilder\traits\component\CallPropsTrait;
+
+/**
+ * col栅格规则
+ * Class Col
+ *
+ * @package FormBuilder\components
+ * @method $this span(Number $span) 栅格的占位格数,可选值为0~24的整数,为 0 时,相当于display:none
+ * @method $this order(Number $order) 栅格的顺序,在flex布局模式下有效
+ * @method $this offset(Number $offset) 栅格左侧的间隔格数,间隔内不可以有栅格
+ * @method $this push(Number $push) 栅格向右移动格数
+ * @method $this pull(Number $pull) 栅格向左移动格数
+ * @method $this labelWidth(Number $labelWidth) 表单域标签的的宽度, 默认150px
+ * @method $this className(String $className) 自定义的class名称
+ * @method $this xs(Number | Col $span) <768px 响应式栅格,可为栅格数或一个包含其他属性的对象
+ * @method $this sm(Number | Col $span) ≥768px 响应式栅格,可为栅格数或一个包含其他属性的对象
+ * @method $this md(Number | Col $span) ≥992px 响应式栅格,可为栅格数或一个包含其他属性的对象
+ * @method $this lg(Number | Col $span) ≥1200px 响应式栅格,可为栅格数或一个包含其他属性的对象
+ */
+class Col implements FormComponentInterFace
+{
+    use CallPropsTrait;
+
+    /**
+     * @var array
+     */
+    protected $props;
+
+    /**
+     * @var array
+     */
+    protected static $propsRule = [
+        'span' => 'number',
+        'order' => 'number',
+        'offset' => 'number',
+        'push' => 'number',
+        'pull' => 'number',
+        'labelWidth' => 'number',
+        'className' => 'number',
+        'xs' => '',
+        'sm' => '',
+        'md' => '',
+        'lg' => '',
+    ];
+
+    /**
+     * @var array
+     */
+    protected static $model = ['xs', 'sm', 'md', 'lg'];
+
+    /**
+     * Col constructor.
+     *
+     * @param int $span
+     */
+    public function __construct($span = 24)
+    {
+        $this->props['span'] = $span;
+    }
+
+    /**
+     * @return array
+     */
+    public function build()
+    {
+        foreach (self::$model as $m) {
+            if (isset($this->props[$m]) && $this->props[$m] instanceof Col) {
+                $this->props[$m] = $this->props[$m]->build();
+            }
+        }
+        return $this->props;
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/components/ColorPicker.php b/vendor/xaboy/form-builder/src/components/ColorPicker.php
new file mode 100755
index 0000000..bbcf85c
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/components/ColorPicker.php
@@ -0,0 +1,86 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\components;
+
+
+use FormBuilder\FormComponentDriver;
+use FormBuilder\Helper;
+
+/**
+ * 颜色选择器组件
+ * Class ColorPicker
+ *
+ * @package FormBuilder\components
+ * @method $this disabled(Boolean $bool) 是否禁用
+ * @method $this alpha(Boolean $bool) 是否支持透明度选择, 默认为false
+ * @method $this hue(Boolean $bool) 是否支持色彩选择, 默认为true
+ * @method $this recommend(Boolean $bool) 是否显示推荐的颜色预设, 默认为false
+ * @method $this size(String $size) 尺寸,可选值为large、small、default或者不设置
+ * @method $this format(String $format) 颜色的格式,可选值为 hsl、hsv、hex、rgb    String    开启 alpha 时为 rgb,其它为 hex
+ */
+class ColorPicker extends FormComponentDriver
+{
+    /**
+     * @var string
+     */
+    protected $name = 'colorPicker';
+
+    /**
+     * @var array
+     */
+    protected $props = [
+        'colors' => []
+    ];
+
+    /**
+     * @var array
+     */
+    protected static $propsRule = [
+        'disabled' => 'boolean',
+        'alpha' => 'boolean',
+        'hue' => 'boolean',
+        'recommend' => 'boolean',
+        'size' => 'string',
+        'format' => 'string',
+    ];
+
+    /**
+     * 自定义颜色预设
+     *
+     * @param $colors
+     * @return $this
+     */
+    public function colors($colors)
+    {
+        $colors = Helper::toType($colors, 'array');
+        $this->props['colors'] = array_merge($this->props['colors'], $colors);
+        return $this;
+    }
+
+    public function getValidateHandler()
+    {
+        return Validate::str();
+    }
+
+    /**
+     * @return array
+     */
+    public function build()
+    {
+        return [
+            'type' => $this->name,
+            'field' => $this->field,
+            'title' => $this->title,
+            'value' => $this->value,
+            'props' => (object)$this->props,
+            'validate' => $this->validate,
+            'col' => $this->col
+        ];
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/components/DatePicker.php b/vendor/xaboy/form-builder/src/components/DatePicker.php
new file mode 100755
index 0000000..9850151
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/components/DatePicker.php
@@ -0,0 +1,172 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\components;
+
+
+use FormBuilder\FormComponentDriver;
+use FormBuilder\Helper;
+
+/**
+ * 日期选择器组件
+ * Class DatePicker
+ *
+ * @package FormBuilder\components
+ * @method $this type(String $type) 显示类型,可选值为 date、daterange、datetime、datetimerange、year、month
+ * @method $this format(String $format) 展示的日期格式, 默认为yyyy-MM-dd HH:mm:ss
+ * @method $this placement(String $placement) 日期选择器出现的位置,可选值为top, top-start, top-end, bottom, bottom-start, bottom-end, left, left-start, left-end, right, right-start, right-end, 默认为bottom-start
+ * @method $this placeholder(String $placeholder) 占位文本
+ * @method $this confirm(Boolean $bool) 是否显示底部控制栏,开启后,选择完日期,选择器不会主动关闭,需用户确认后才可关闭, 默认为false
+ * @method $this size(String $size) 尺寸,可选值为large、small、default或者不设置
+ * @method $this disabled(Boolean $bool) 是否禁用选择器
+ * @method $this clearable(Boolean $bool) 是否显示清除按钮
+ * @method $this readonly(Boolean $bool) 完全只读,开启后不会弹出选择器,只在没有设置 open 属性下生效
+ * @method $this editable(Boolean $bool) 文本框是否可以输入, 默认为false
+ * @method $this transfer(Boolean $bool) 是否将弹层放置于 body 内,在 Tabs、带有 fixed 的 Table 列内使用时,建议添加此属性,它将不受父级样式影响,从而达到更好的效果, 默认为false
+ * @method $this splitPanels(Boolean $bool) 开启后,左右面板不联动,仅在 daterange 和 datetimerange 下可用。
+ * @method $this showWeekNumbers(Boolean $bool) 开启后,可以显示星期数。
+ *
+ */
+class DatePicker extends FormComponentDriver
+{
+    /**
+     * @var string
+     */
+    protected $name = 'datePicker';
+
+    /**
+     *
+     */
+    const TYPE_DATE = 'date';
+    /**
+     *
+     */
+    const TYPE_DATE_RANGE = 'daterange';
+    /**
+     *
+     */
+    const TYPE_DATE_TIME = 'datetime';
+    /**
+     *
+     */
+    const TYPE_DATE_TIME_RANGE = 'datetimerange';
+    /**
+     *
+     */
+    const TYPE_YEAR = 'year';
+    /**
+     *
+     */
+    const TYPE_MONTH = 'month';
+
+    /**
+     * @var array
+     */
+    protected $props = [
+        'type' => self::TYPE_DATE,
+        'editable' => false,
+        'multiple' => false
+    ];
+
+    /**
+     * @var array
+     */
+    protected static $propsRule = [
+        'type' => 'string',
+        'format' => 'string',
+        'placement' => 'string',
+        'placeholder' => 'string',
+        'size' => 'string',
+        'confirm' => 'boolean',
+        'disabled' => 'boolean',
+        'clearable' => 'boolean',
+        'readonly' => 'boolean',
+        'editable' => 'boolean',
+        'transfer' => 'boolean',
+        'splitPanels' => 'boolean',
+        'showWeekNumbers' => 'boolean'
+    ];
+
+    /**
+     *
+     */
+    protected function init()
+    {
+        $this->placeholder($this->getPlaceHolder());
+    }
+
+    /**
+     * 开启后, 可以选择多个日期, 仅在 date 下可用, 默认为false
+     *
+     * @param bool $bool
+     * @return $this
+     */
+    public function multiple($bool = true)
+    {
+        if ($this->props['type'] == 'date')
+            $this->props['multiple'] = (bool)$bool;
+        else
+            $this->props['multiple'] = false;
+
+        return $this;
+    }
+
+    /**
+     * @param $value
+     * @return $this
+     */
+    public function value($value)
+    {
+        if (is_array($value)) {
+            foreach ($value as $k => $v) {
+                $value[$k] = Helper::getDate($v);
+            }
+        } else {
+            $value = Helper::getDate($value);
+        }
+        $this->value = $value;
+        return $this;
+    }
+
+    public function getValidateHandler()
+    {
+        if (in_array($this->props['type'], ['datetimerange', 'daterange']) || $this->props['multiple'])
+            return Validate::arr();
+        else
+            return Validate::date();
+    }
+
+    public function required($message = null)
+    {
+        $message = $message ?: $this->getPlaceHolder();
+        if (in_array($this->props['type'], ['datetimerange', 'daterange'])) {
+            $this->validate()->fields([
+                '0' => ['required' => true, 'type' => 'date', 'message' => $message],
+                '1' => ['required' => true, 'type' => 'date', 'message' => $message]
+            ], true, $message);
+            return $this;
+        } else
+            return parent::required($message);
+    }
+
+    /**
+     * @return array
+     */
+    public function build()
+    {
+        return [
+            'type' => $this->name,
+            'field' => $this->field,
+            'title' => $this->title,
+            'value' => $this->value,
+            'props' => (object)$this->props,
+            'validate' => $this->validate,
+            'col' => $this->col
+        ];
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/components/FormStyle.php b/vendor/xaboy/form-builder/src/components/FormStyle.php
new file mode 100755
index 0000000..f281924
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/components/FormStyle.php
@@ -0,0 +1,78 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\components;
+
+
+use FormBuilder\interfaces\FormComponentInterFace;
+use FormBuilder\traits\component\CallPropsTrait;
+
+/**
+ * form表单样式
+ * Class FormStyle
+ *
+ * @package FormBuilder\components
+ * @method $this inline(Boolean $bool) 是否开启行内表单模式
+ * @method $this labelPosition(String $labelPosition) 表单域标签的位置,可选值为 left、right、top
+ * @method $this labelWidth(Number $labelWidth) 表单域标签的宽度,所有的 FormItem 都会继承 Form 组件的 label-width 的值
+ * @method $this showMessage(Boolean $bool) 是否显示校验错误信息
+ */
+class FormStyle implements FormComponentInterFace
+{
+    use CallPropsTrait;
+
+    /**
+     * @var array
+     */
+    protected $props;
+
+    /**
+     * @var array
+     */
+    protected static $propsRule = [
+        'inline' => 'boolean',
+        'labelPosition' => 'string',
+        'labelWidth' => 'number',
+        'showMessage' => 'boolean',
+    ];
+
+    /**
+     * FormStyle constructor.
+     *
+     * @param bool   $inline
+     * @param string $labelPosition
+     * @param int    $labelWidth
+     * @param bool   $showMessage
+     * @param string $autocomplete
+     */
+    public function __construct($inline = false, $labelPosition = 'right', $labelWidth = 125, $showMessage = true, $autocomplete = 'off')
+    {
+        $this->props = compact('inline', 'labelPosition', 'labelWidth', 'showMessage');
+        $this->autocomplete($autocomplete);
+    }
+
+    /**
+     * 原生的 autocomplete 属性,可选值为 true = off 或 false = on
+     *
+     * @param bool $bool
+     * @return $this
+     */
+    public function autocomplete($bool = false)
+    {
+        $this->props['autocomplete'] = $bool === false ? 'on' : 'off';
+        return $this;
+    }
+
+    /**
+     * @return array
+     */
+    public function build()
+    {
+        return $this->props;
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/components/Frame.php b/vendor/xaboy/form-builder/src/components/Frame.php
new file mode 100755
index 0000000..e80607c
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/components/Frame.php
@@ -0,0 +1,119 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\components;
+
+
+use FormBuilder\FormComponentDriver;
+use FormBuilder\Helper;
+
+/**
+ * 框架组件
+ * Class Frame
+ *
+ * @package FormBuilder\components
+ * @method $this type(String $type) frame类型, 有input, file, image, 默认为input
+ * @method $this src(String $src) iframe地址
+ * @method $this maxLength(int $length) value的最大数量, 默认无限制
+ * @method $this icon(String $icon) 打开弹出框的按钮图标
+ * @method $this height(String $height) 弹出框高度
+ * @method $this width(String $width) 弹出框宽度
+ * @method $this spin(Boolean $bool) 是否显示加载动画, 默认为 true
+ * @method $this frameTitle(String $title) 弹出框标题
+ * @method $this handleIcon(Boolean $bool) 操作按钮的图标, 设置为false将不显示, 设置为true为默认的预览图标, 类型为file时默认为false, image类型默认为true
+ * @method $this allowRemove(Boolean $bool) 是否可删除, 设置为false是不显示删除按钮
+ *
+ *
+ */
+class Frame extends FormComponentDriver
+{
+    /**
+     * @var string
+     */
+    protected $name = 'frame';
+
+    /**
+     *
+     */
+    const TYPE_IMAGE = 'image';
+    /**
+     *
+     */
+    const TYPE_FILE = 'file';
+    /**
+     *
+     */
+    const TYPE_INPUT = 'input';
+
+    /**
+     * @var array
+     */
+    protected $props = [
+        'type' => self::TYPE_INPUT,
+        'maxLength' => 0,
+        'modal' => ['footerHide' => true]
+    ];
+
+    /**
+     * @var array
+     */
+    protected static $propsRule = [
+        'type' => 'string',
+        'src' => 'string',
+        'maxLength' => 'int',
+        'icon' => 'string',
+        'height' => 'string',
+        'width' => 'string',
+        'spin' => 'boolean',
+        'frameTitle' => ['string', 'title'],
+        'handleIcon' => 'boolean',
+        'allowRemove' => 'boolean',
+    ];
+
+    /**
+     * @param string|array $value
+     * @return $this
+     */
+    public function value($value)
+    {
+        $this->value = $value;
+        return $this;
+    }
+
+    /**
+     *
+     */
+    protected function init()
+    {
+        $this->frameTitle($this->getPlaceHolder());
+    }
+
+    public function getValidateHandler()
+    {
+        return Validate::arr();
+    }
+
+    /**
+     * @return array
+     */
+    public function build()
+    {
+        $value = $this->value;
+        if ($this->props['maxLength'] == 1 && is_array($value))
+            $value = isset($value[0]) ? $value[0] : '';
+
+        return [
+            'type' => $this->name,
+            'field' => $this->field,
+            'title' => $this->title,
+            'value' => $value,
+            'props' => (object)$this->props,
+            'validate' => $this->validate,
+            'col' => $this->col
+        ];
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/components/Hidden.php b/vendor/xaboy/form-builder/src/components/Hidden.php
new file mode 100755
index 0000000..3c1388c
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/components/Hidden.php
@@ -0,0 +1,55 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\components;
+
+
+use FormBuilder\FormComponentDriver;
+
+/**
+ * hidden组件
+ * Class Hidden
+ *
+ * @package FormBuilder\components
+ */
+class Hidden extends FormComponentDriver
+{
+
+    /**
+     * @var string
+     */
+    protected $name = 'hidden';
+
+    /**
+     * Hidden constructor.
+     *
+     * @param String $field
+     * @param String $value
+     */
+    public function __construct($field, $value)
+    {
+        $this->field = (string)$field;
+        static::value($value);
+    }
+
+    protected function getValidateHandler()
+    {
+
+    }
+
+    /**
+     * @return array
+     */
+    public function build()
+    {
+        return [
+            'type' => $this->name,
+            'field' => $this->field,
+            'value' => $this->value
+        ];
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/components/Input.php b/vendor/xaboy/form-builder/src/components/Input.php
new file mode 100755
index 0000000..0ba73d4
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/components/Input.php
@@ -0,0 +1,143 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\components;
+
+
+use FormBuilder\FormComponentDriver;
+use FormBuilder\Helper;
+
+/**
+ * Input组件,支持类型text、password、textarea、url、email、date
+ * Class Input
+ *
+ * @package FormBuilder\components
+ * @method $this type(String $type) 输入框类型,可选值为 text、password、textarea、url、email、date;
+ * @method $this size(String $size) 输入框尺寸,可选值为large、small、default或者不设置;
+ * @method $this placeholder(String $placeholder) 占位文本
+ * @method $this clearable(Boolean $bool) 是否显示清空按钮, 默认为false
+ * @method $this disabled(Boolean $bool) 设置输入框为禁用状态, 默认为false
+ * @method $this readonly(Boolean $bool) 设置输入框为只读, 默认为false
+ * @method $this maxlength(int $length) 最大输入长度
+ * @method $this icon(String $icon) 输入框尾部图标,仅在 text 类型下有效
+ * @method $this rows(int $rows) 文本域默认行数,仅在 textarea 类型下有效, 默认为2
+ * @method $this number(Boolean $bool) 将用户的输入转换为 Number 类型, 默认为false
+ * @method $this autofocus(Boolean $bool) 自动获取焦点, 默认为false
+ * @method $this autocomplete(Boolean $bool) 原生的自动完成功能, 默认为false
+ * @method $this spellcheck(Boolean $bool) 原生的 spellcheck 属性, 默认为false
+ * @method $this wrap(String $warp) 原生的 wrap 属性,可选值为 hard 和 soft, 默认为soft
+ */
+class Input extends FormComponentDriver
+{
+    /**
+     * @var string
+     */
+    protected $name = 'input';
+
+    /**
+     * 组件类型
+     */
+    const TYPE_TEXT = 'text';
+    /**
+     *
+     */
+    const TYPE_PASSWORD = 'password';
+    /**
+     *
+     */
+    const TYPE_TEXTAREA = 'textarea';
+    /**
+     *
+     */
+    const TYPE_URL = 'url';
+    /**
+     *
+     */
+    const TYPE_EMAIL = 'email';
+    /**
+     *
+     */
+    const TYPE_DATE = 'date';
+
+    /**
+     * @var array
+     */
+    protected $props = [
+        'type' => self::TYPE_TEXT
+    ];
+
+    /**
+     * @var array
+     */
+    protected static $propsRule = [
+        'type' => 'string',
+        'size' => 'string',
+        'placeholder' => 'string',
+        'clearable' => 'boolean',
+        'disabled' => 'boolean',
+        'readonly' => 'boolean',
+        'maxlength' => 'int',
+        'icon' => 'string',
+        'rows' => 'int',
+        'number' => 'boolean',
+        'autofocus' => 'boolean',
+        'autocomplete' => 'boolean',
+        'spellcheck' => 'boolean',
+        'wrap' => 'string',
+    ];
+
+    /**
+     *
+     */
+    protected function init()
+    {
+        $this->placeholder($this->getPlaceHolder());
+    }
+
+    protected function getPlaceHolder($pre = '请输入')
+    {
+        return parent::getPlaceHolder($pre);
+    }
+
+    public function getValidateHandler()
+    {
+        return Validate::str(Validate::TRIGGER_BLUR);
+    }
+
+
+    /**
+     * 自适应内容高度,仅在 textarea 类型下有效
+     *
+     * @param Bool|Number $minRows
+     * @param null|Number $maxRows
+     * @return $this
+     */
+    public function autoSize($minRows = false, $maxRows = null)
+    {
+
+        $this->props['autosize'] = $maxRows === null ? boolval($minRows) : compact('minRows', 'maxRows');
+        return $this;
+    }
+
+    /**
+     * 生成表单规则
+     *
+     * @return array
+     */
+    public function build()
+    {
+        return [
+            'type' => $this->name,
+            'field' => $this->field,
+            'title' => $this->title,
+            'value' => $this->value,
+            'props' => (object)$this->props,
+            'validate' => $this->validate,
+            'col' => $this->col
+        ];
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/components/InputNumber.php b/vendor/xaboy/form-builder/src/components/InputNumber.php
new file mode 100755
index 0000000..6809539
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/components/InputNumber.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\components;
+
+
+use FormBuilder\FormComponentDriver;
+
+/**
+ * 数字输入框组件
+ * Class InputNumber
+ *
+ * @package FormBuilder\components
+ * @method $this max(float $max) 最大值
+ * @method $this min(float $min) 最小值
+ * @method $this step(float $step) 每次改变的步伐,可以是小数
+ * @method $this size(String $size) 输入框尺寸,可选值为large、small、default或者不填
+ * @method $this disabled(Boolean $bool) 设置禁用状态,默认为false
+ * @method $this placeholder(String $placeholder) 占位文本
+ * @method $this readonly(Boolean $bool) 是否设置为只读,默认为false
+ * @method $this editable(Boolean $bool) 是否可编辑,默认为true
+ * @method $this precision(int $precision) 数值精度
+ */
+class InputNumber extends FormComponentDriver
+{
+    /**
+     * @var string
+     */
+    protected $name = 'inputNumber';
+
+    /**
+     * @var array
+     */
+    protected static $propsRule = [
+        'max' => 'float',
+        'min' => 'float',
+        'step' => 'float',
+        'disabled' => 'boolean',
+        'size' => 'string',
+        'placeholder' => 'string',
+        'readonly' => 'boolean',
+        'editable' => 'boolean',
+        'precision' => 'int',
+    ];
+
+    /**
+     *
+     */
+    protected function init()
+    {
+        $this->placeholder($this->getPlaceHolder());
+    }
+
+    protected function getPlaceHolder($pre = '请输入')
+    {
+        return parent::getPlaceHolder($pre);
+    }
+
+    public function getValidateHandler()
+    {
+        return Validate::num(Validate::TRIGGER_BLUR);
+    }
+
+    /**
+     * @return array
+     */
+    public function build()
+    {
+        return [
+            'type' => $this->name,
+            'field' => $this->field,
+            'title' => $this->title,
+            'value' => $this->value === '' ? '' : (float)$this->value,
+            'props' => (object)$this->props,
+            'validate' => $this->validate,
+            'col' => $this->col
+        ];
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/components/Option.php b/vendor/xaboy/form-builder/src/components/Option.php
new file mode 100755
index 0000000..7229c1f
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/components/Option.php
@@ -0,0 +1,76 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\components;
+
+
+use FormBuilder\interfaces\FormComponentInterFace;
+use FormBuilder\Helper;
+
+/**
+ * Class Option
+ *
+ * @package FormBuilder\components
+ */
+class Option implements FormComponentInterFace
+{
+
+    /**
+     * @var array
+     */
+    protected $props;
+
+    /**
+     * Option constructor.
+     *
+     * @param        $value
+     * @param string $label
+     * @param bool   $disabled
+     * @throws \FormBuilder\exception\FormBuilderException
+     */
+    public function __construct($value, $label = '', $disabled = false)
+    {
+        self::verify($value, $value);
+        $value = (string)$value;
+        $this->props = compact('label', 'value');
+        $this->disabled($disabled);
+    }
+
+    /**
+     * @param $value
+     * @param $label
+     * @throws \FormBuilder\exception\FormBuilderException
+     */
+    public static function verify($value, $label)
+    {
+        Helper::verifyType($value, ['numeric', 'string', 'null'], 'options.value');
+        Helper::verifyType($label, ['numeric', 'string', 'null'], 'options.label');
+    }
+
+
+    /**
+     * @param bool $disabled
+     * @return $this
+     */
+    public function disabled($disabled = true)
+    {
+        $disabled = Helper::toType($disabled, 'boolean');
+        $this->props['disabled'] = $disabled;
+        return $this;
+    }
+
+    /**
+     * @return array
+     */
+    public function build()
+    {
+        $props = $this->props;
+        if ($props['disabled'] != true)
+            unset($props['disabled']);
+        return $props;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/components/Radio.php b/vendor/xaboy/form-builder/src/components/Radio.php
new file mode 100755
index 0000000..6df0049
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/components/Radio.php
@@ -0,0 +1,77 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\components;
+
+
+use FormBuilder\FormComponentDriver;
+use FormBuilder\Helper;
+use FormBuilder\traits\component\ComponentOptionsTrait;
+
+/**
+ * 单选框组件
+ * Class Radio
+ *
+ * @package FormBuilder\components
+ * @method $this size(String $size) 单选框的尺寸,可选值为 large、small、default 或者不设置
+ * @method $this vertical(Boolean $bool) 是否垂直排列,按钮样式下无效
+ */
+class Radio extends FormComponentDriver
+{
+    use ComponentOptionsTrait;
+
+    /**
+     * @var string
+     */
+    protected $name = 'radio';
+
+    /**
+     * @var array
+     */
+    protected static $propsRule = [
+        'size' => 'string',
+        'vertical' => 'boolean'
+    ];
+
+    /**
+     * 使用按钮样式
+     *
+     * @return $this
+     */
+    public function button()
+    {
+        $this->props['type'] = 'button';
+        return $this;
+    }
+
+    public function getValidateHandler()
+    {
+        return Validate::str();
+    }
+
+    /**
+     * @return array
+     */
+    public function build()
+    {
+        $options = [];
+        foreach ($this->options as $option) {
+            if ($option instanceof Option)
+                $options[] = $option->build();
+        }
+        return [
+            'type' => $this->name,
+            'field' => $this->field,
+            'title' => $this->title,
+            'value' => $this->value,
+            'props' => (object)$this->props,
+            'options' => $options,
+            'validate' => $this->validate,
+            'col' => $this->col
+        ];
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/components/Rate.php b/vendor/xaboy/form-builder/src/components/Rate.php
new file mode 100755
index 0000000..6ee7301
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/components/Rate.php
@@ -0,0 +1,63 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\components;
+
+
+use FormBuilder\FormComponentDriver;
+
+/**
+ * 评分组件
+ * Class Rate
+ *
+ * @package FormBuilder\components
+ * @method $this count(int $star) star 总数, 默认为 5
+ * @method $this allowHalf(Boolean $bool) 是否允许半选, 默认为 false
+ * @method $this disabled(Boolean $bool) 是否只读,无法进行交互, 默认为
+ * @method $this showText(Boolean $bool) 是否显示提示文字, 默认为 false
+ * @method $this clearable(Boolean $bool) 是否可以取消选择, 默认为 false
+ *
+ */
+class Rate extends FormComponentDriver
+{
+    /**
+     * @var string
+     */
+    protected $name = 'rate';
+
+    /**
+     * @var array
+     */
+    protected static $propsRule = [
+        'count' => 'float',
+        'allowHalf' => 'boolean',
+        'disabled' => 'boolean',
+        'showText' => 'boolean',
+        'clearable' => 'boolean',
+    ];
+
+    public function getValidateHandler()
+    {
+        return Validate::num();
+    }
+
+    /**
+     * @return array
+     */
+    public function build()
+    {
+        return [
+            'type' => $this->name,
+            'field' => $this->field,
+            'title' => $this->title,
+            'value' => (float)$this->value,
+            'props' => (object)$this->props,
+            'validate' => $this->validate,
+            'col' => $this->col
+        ];
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/components/Row.php b/vendor/xaboy/form-builder/src/components/Row.php
new file mode 100755
index 0000000..2649e6a
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/components/Row.php
@@ -0,0 +1,67 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\components;
+
+
+use FormBuilder\interfaces\FormComponentInterFace;
+use FormBuilder\traits\component\CallPropsTrait;
+
+/**
+ * row栅格规则
+ * Class Row
+ *
+ * @package FormBuilder\components
+ * @method $this gutter(Number $gutter) 栅格间距,单位 px,左右平分
+ * @method $this type(String $type) 栅格的顺序,在flex布局模式下有效
+ * @method $this align(String $align) flex 布局下的垂直对齐方式,可选值为top、middle、bottom
+ * @method $this justify(String $justify) flex 布局下的水平排列方式,可选值为start、end、center、space-around、space-between
+ * @method $this className(String $className) 自定义的class名称
+ */
+class Row implements FormComponentInterFace
+{
+    use CallPropsTrait;
+
+    /**
+     * @var array
+     */
+    protected $props;
+
+    /**
+     * @var array
+     */
+    protected static $propsRule = [
+        'gutter' => 'number',
+        'type' => 'string',
+        'align' => 'string',
+        'justify' => 'string',
+        'className' => 'string',
+    ];
+
+    /**
+     * Row constructor.
+     *
+     * @param int    $gutter
+     * @param string $type
+     * @param string $align
+     * @param string $justify
+     * @param string $className
+     */
+    public function __construct($gutter = 0, $type = '', $align = '', $justify = '', $className = '')
+    {
+        $this->props = compact('gutter', 'type', 'align', 'justify', 'className');
+    }
+
+    /**
+     * @return array
+     */
+    public function build()
+    {
+        return $this->props;
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/components/Select.php b/vendor/xaboy/form-builder/src/components/Select.php
new file mode 100755
index 0000000..589aa03
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/components/Select.php
@@ -0,0 +1,129 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\components;
+
+
+use FormBuilder\FormComponentDriver;
+use FormBuilder\Helper;
+use FormBuilder\traits\component\ComponentOptionsTrait;
+
+/**
+ * 选择器组件
+ * Class Select
+ *
+ * @package FormBuilder\components
+ * @method $this multiple(Boolean $bool) 是否支持多选, 默认为false
+ * @method $this disabled(Boolean $bool) 是否禁用, 默认为false
+ * @method $this clearable(Boolean $bool) 是否可以清空选项,只在单选时有效, 默认为false
+ * @method $this filterable(Boolean $bool) 是否支持搜索, 默认为false
+ * @method $this size(String $size) 选择框大小,可选值为large、small、default或者不填
+ * @method $this placeholder(String $placeholder) 选择框默认文字
+ * @method $this transfer(String $transfer) 是否将弹层放置于 body 内,在 Tabs、带有 fixed 的 Table 列内使用时,建议添加此属性,它将不受父级样式影响,从而达到更好的效果, 默认为false
+ * @method $this placement(String $placement) 弹窗的展开方向,可选值为 bottom 和 top, 默认为bottom
+ * @method $this notFoundText(String $text) 当下拉列表为空时显示的内容, 默认为 无匹配数据
+ *
+ */
+class Select extends FormComponentDriver
+{
+    use ComponentOptionsTrait;
+
+    /**
+     * @var string
+     */
+    protected $name = 'select';
+
+    /**
+     * @var array
+     */
+    protected $value = [];
+
+    /**
+     * @var array
+     */
+    protected $props = [
+        'multiple' => false
+    ];
+
+    /**
+     * @var array
+     */
+    protected static $propsRule = [
+        'multiple' => 'boolean',
+        'disabled' => 'boolean',
+        'clearable' => 'boolean',
+        'filterable' => 'boolean',
+        'size' => 'string',
+        'placeholder' => 'string',
+        'transfer' => 'string',
+        'placement' => 'string',
+        'notFoundText' => 'string',
+    ];
+
+    protected function init()
+    {
+        $this->placeholder($this->getPlaceHolder());
+    }
+
+    /**
+     * @param $value
+     * @return $this
+     */
+    public function value($value)
+    {
+        if ($value === null) return $this;
+        if (!is_array($value))
+            $this->value[] = $value;
+        else {
+            foreach ($value as $v) {
+                $this->value[] = (string)$v;
+            }
+        }
+        return $this;
+    }
+
+
+    public function getValidateHandler()
+    {
+        if ($this->props['multiple'] == true)
+            return Validate::arr();
+        else
+            return Validate::str();
+    }
+
+    /**
+     * @return array
+     */
+    public function build()
+    {
+        $options = [];
+        foreach ($this->options as $option) {
+            if ($option instanceof Option)
+                $options[] = $option->build();
+        }
+        $value = $this->value;
+        $isArr = is_array($value);
+        if ($this->props['multiple'] == false && $isArr)
+            $value = isset($value[0]) ? $value[0] : '';
+        else if ($isArr) {
+            $value = array_unique($value);
+            foreach ($value as $k => $v) {
+                $value[$k] = (string)$v;
+            }
+        }
+        return [
+            'type' => $this->name,
+            'field' => $this->field,
+            'title' => $this->title,
+            'value' => $value,
+            'props' => (object)$this->props,
+            'options' => $options,
+            'validate' => $this->validate,
+            'col' => $this->col
+        ];
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/components/Slider.php b/vendor/xaboy/form-builder/src/components/Slider.php
new file mode 100755
index 0000000..07025b3
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/components/Slider.php
@@ -0,0 +1,103 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\components;
+
+
+use FormBuilder\FormComponentDriver;
+use FormBuilder\Helper;
+
+/**
+ * 滑块组件
+ * Class Slider
+ *
+ * @package FormBuilder\components
+ * @method $this min(float $min) 最小值, 默认 0
+ * @method $this max(float $max) 最大值, 默认 100
+ * @method $this step(float $step) 步长,取值建议能被(max - min)整除, 默认 1
+ * @method $this disabled(Boolean $bool) 是否禁用滑块, 默认 false
+ * @method $this range(Boolean $bool) 是否开启双滑块模式, 默认
+ * @method $this showInput(Boolean $bool) 是否显示数字输入框,仅在单滑块模式下有效, 默认 false
+ * @method $this showStops(Boolean $bool) 是否显示间断点,建议在 step 不密集时使用, 默认 false
+ * @method $this showTip(String $tip) 提示的显示控制,可选值为 hover(悬停,默认)、always(总是可见)、never(不可见)
+ * @method $this inputSize(String $size) 数字输入框的尺寸,可选值为large、small、default或者不填,仅在开启 show-input 时有效
+ *
+ */
+class Slider extends FormComponentDriver
+{
+    /**
+     * @var string
+     */
+    protected $name = 'slider';
+
+    /**
+     * @var array
+     */
+    protected $props = [
+        'range' => false
+    ];
+
+    /**
+     * @var array
+     */
+    protected static $propsRule = [
+        'min' => 'float',
+        'max' => 'float',
+        'step' => 'float',
+        'disabled' => 'boolean',
+        'range' => 'boolean',
+        'showInput' => 'boolean',
+        'showStops' => 'boolean',
+        'showTip' => 'string',
+        'inputSize' => 'string',
+    ];
+
+    /**
+     * @param $value
+     * @return $this
+     */
+    public function value($value)
+    {
+        $this->value = $value;
+        return $this;
+    }
+
+    public function getValidateHandler()
+    {
+        if ($this->props['range'] == true)
+            return Validate::arr();
+        else
+            return Validate::num();
+    }
+
+    /**
+     * @return array
+     */
+    public function build()
+    {
+        $value = $this->value;
+        if ($this->props['range'] == true) {
+            $value = is_array($value) ? $value : [0, (int)$value];
+        } else {
+            $value = (int)(is_array($value)
+                ? (isset($value[0])
+                    ? $value[0]
+                    : 0)
+                : $value);
+        }
+
+        return [
+            'type' => $this->name,
+            'field' => $this->field,
+            'title' => $this->title,
+            'value' => $value,
+            'props' => (object)$this->props,
+            'validate' => $this->validate,
+            'col' => $this->col
+        ];
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/components/Switches.php b/vendor/xaboy/form-builder/src/components/Switches.php
new file mode 100755
index 0000000..175fd03
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/components/Switches.php
@@ -0,0 +1,98 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\components;
+
+use FormBuilder\FormComponentDriver;
+
+/**
+ * 开关组件
+ * Class Switches
+ *
+ * @package FormBuilder\components
+ * @method $this size(String $size) 开关的尺寸,可选值为large、small、default或者不写。建议开关如果使用了2个汉字的文字,使用 large。
+ * @method $this disabled(Boolean $bool) 禁用开关, 默认为false
+ * @method $this trueValue(String $value) 选中时的值,默认为1
+ * @method $this falseValue(String $value) 没有选中时的值,默认为0
+ */
+class Switches extends FormComponentDriver
+{
+    /**
+     * @var string
+     */
+    protected $name = 'switch';
+
+    /**
+     * @var array
+     */
+    protected $slot = [];
+
+    /**
+     * @var array
+     */
+    protected $props = [
+        'trueValue' => '1',
+        'falseValue' => '0'
+    ];
+
+    /**
+     * @var array
+     */
+    protected static $propsRule = [
+        'size' => 'string',
+        'disabled' => 'boolean',
+        'trueValue' => 'string',
+        'falseValue' => 'string'
+    ];
+
+    /**
+     * 自定义显示打开时的内容
+     *
+     * @param $open
+     * @return $this
+     */
+    public function openStr($open)
+    {
+        $this->slot['open'] = (string)$open;
+        return $this;
+    }
+
+    /**
+     * 自定义显示关闭时的内容
+     *
+     * @param $close
+     * @return $this
+     */
+    public function closeStr($close)
+    {
+        $this->slot['close'] = (string)$close;
+        return $this;
+    }
+
+    public function getValidateHandler()
+    {
+        return Validate::str();
+    }
+
+    /**
+     * @return array
+     */
+    public function build()
+    {
+        return [
+            'type' => $this->name,
+            'field' => $this->field,
+            'title' => $this->title,
+            'value' => $this->value,
+            'props' => (object)$this->props,
+            'slot' => (object)$this->slot,
+            'validate' => $this->validate,
+            'col' => $this->col
+        ];
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/components/TimePicker.php b/vendor/xaboy/form-builder/src/components/TimePicker.php
new file mode 100755
index 0000000..8a2ca7e
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/components/TimePicker.php
@@ -0,0 +1,155 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\components;
+
+
+use FormBuilder\FormComponentDriver;
+use FormBuilder\Helper;
+
+/**
+ * 时间选择器组件
+ * Class TimePicker
+ *
+ * @package FormBuilder\components
+ * @method $this type(String $type) 显示类型,可选值为 time、timerange
+ * @method $this format(String $format) 展示的时间格式, 默认为HH:mm:ss
+ * @method $this placement(String $placement) 时间选择器出现的位置,可选值为top, top-start, top-end, bottom, bottom-start, bottom-end, left, left-start, left-end, right, right-start, right-end, 默认为bottom-start
+ * @method $this placeholder(String $placeholder) 占位文本
+ * @method $this confirm(Boolean $bool) 是否显示底部控制栏, 默认为false
+ * @method $this size(String $size) 尺寸,可选值为large、small、default或者不设置
+ * @method $this disabled(Boolean $bool) 是否禁用选择器
+ * @method $this clearable(Boolean $bool) 是否显示清除按钮
+ * @method $this readonly(Boolean $bool) 完全只读,开启后不会弹出选择器,只在没有设置 open 属性下生效
+ * @method $this editable(Boolean $bool) 文本框是否可以输入, 默认为false
+ * @method $this transfer(Boolean $bool) 是否将弹层放置于 body 内,在 Tabs、带有 fixed 的 Table 列内使用时,建议添加此属性,它将不受父级样式影响,从而达到更好的效果, 默认为false
+ *
+ */
+class TimePicker extends FormComponentDriver
+{
+    /**
+     * @var string
+     */
+    protected $name = 'timePicker';
+
+    /**
+     *
+     */
+    const TYPE_TIME = 'time';
+    /**
+     *
+     */
+    const TYPE_TIME_RANGE = 'timerange';
+
+    /**
+     * @var array
+     */
+    protected $props = [
+        'type' => self::TYPE_TIME,
+        'editable' => false,
+        'confirm' => true
+    ];
+
+    /**
+     * @var array
+     */
+    protected static $propsRule = [
+        'type' => 'string',
+        'format' => 'string',
+        'placement' => 'string',
+        'placeholder' => 'string',
+        'size' => 'string',
+        'confirm' => 'boolean',
+        'disabled' => 'boolean',
+        'clearable' => 'boolean',
+        'readonly' => 'boolean',
+        'editable' => 'boolean',
+        'transfer' => 'boolean',
+    ];
+
+    /**
+     *
+     */
+    protected function init()
+    {
+        $this->placeholder($this->getPlaceHolder());
+    }
+
+    /**
+     * 下拉列表的时间间隔,数组的三项分别对应小时、分钟、秒。
+     * 例如设置为 [1, 15] 时,分钟会显示:00、15、30、45。
+     *
+     * @param     $h
+     * @param int $i
+     * @param int $s
+     * @return $this
+     */
+    public function steps($h, $i = 0, $s = 0)
+    {
+        $this->props['steps'] = [$h, $i, $s];
+        return $this;
+    }
+
+    /**
+     * @param $value
+     * @return $this
+     */
+    public function value($value)
+    {
+        if (is_array($value)) {
+            foreach ($value as $k => $v) {
+                $value[$k] = Helper::getDate($v);
+            }
+        } else {
+            $value = Helper::getDate($value);
+        }
+        $this->value = $value;
+        return $this;
+    }
+
+    public function getValidateHandler()
+    {
+        if ($this->props['type'] == 'timerange')
+            return Validate::arr();
+        else
+            return Validate::str();
+    }
+
+
+    public function required($message = null)
+    {
+        $message = $message ?: $this->getPlaceHolder();
+        if ($this->props['type'] == 'timerange') {
+            $this->validate()->fields([
+                '0' => ['required' => true, 'message' => $message],
+                '1' => ['required' => true, 'message' => $message]
+            ], true, $message);
+            return $this;
+        } else
+            return parent::required($message);
+    }
+
+    /**
+     * @return array
+     */
+    public function build()
+    {
+        $value = $this->value;
+        if (is_array($value) && count(array_filter($value)) == 0)
+            $value = ['', ''];
+        return [
+            'type' => $this->name,
+            'field' => $this->field,
+            'title' => $this->title,
+            'value' => $value,
+            'props' => (object)$this->props,
+            'validate' => $this->validate,
+            'col' => $this->col
+        ];
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/components/Tree.php b/vendor/xaboy/form-builder/src/components/Tree.php
new file mode 100755
index 0000000..03129d4
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/components/Tree.php
@@ -0,0 +1,124 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\components;
+
+
+use FormBuilder\FormComponentDriver;
+
+/**
+ * 树型组件
+ * Class Tree
+ *
+ * @package FormBuilder\components
+ * @method $this type(String $type) 类型,可选值为 checked、selected
+ * @method $this multiple(Boolean $bool) 是否支持多选, 当`type=selected`并且`multiple=false`, 默认为false, 值为String或Number类型,其他情况为Array类型
+ * @method $this showCheckbox(Boolean $bool) 是否显示多选框, 默认为false
+ * @method $this emptyText(String $emptyText) 没有数据时的提示, 默认为'暂无数据'
+ */
+class Tree extends FormComponentDriver
+{
+    /**
+     * @var string
+     */
+    protected $name = 'tree';
+
+    /**
+     *
+     */
+    const TYPE_SELECTED = 'selected';
+    /**
+     *
+     */
+    const TYPE_CHECKED = 'checked';
+
+    /**
+     * @var array
+     */
+    protected $props = [
+        'type' => self::TYPE_CHECKED,
+        'data' => [],
+        'multiple' => true
+    ];
+
+    /**
+     * @var array
+     */
+    protected static $propsRule = [
+        'type' => 'string',
+        'multiple' => 'boolean',
+        'showCheckbox' => 'boolean',
+        'emptyText' => 'string',
+    ];
+
+    /**
+     * @param array $treeData
+     * @return $this
+     */
+    public function data(array $treeData)
+    {
+        if (!is_array($this->props['data'])) $this->props['data'] = [];
+        foreach ($treeData as $child) {
+            $this->props['data'][] = $child instanceof TreeData
+                ? $child->build()
+                : $child;
+        }
+        return $this;
+    }
+
+    /**
+     * @param $var
+     * @return $this
+     */
+    public function jsData($var)
+    {
+        $this->props['data'] = 'js.' . $var;
+        return $this;
+    }
+
+    /**
+     * @param $value
+     * @return $this
+     */
+    public function value($value)
+    {
+        if (is_array($value)) {
+            foreach ($value as $k => $v) {
+                $value[$k] = (string)$v;
+            }
+        } else {
+            $value = (string)$value;
+        }
+        $this->value = $value;
+        return $this;
+    }
+
+    public function getValidateHandler()
+    {
+        if ($this->props['multiple'])
+            return Validate::arr();
+        else
+            return Validate::str();
+    }
+
+    /**
+     * @return array
+     */
+    public function build()
+    {
+        return [
+            'type' => $this->name,
+            'field' => $this->field,
+            'title' => $this->title,
+            'value' => $this->value,
+            'props' => (object)$this->props,
+            'validate' => $this->validate,
+            'col' => $this->col
+        ];
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/components/TreeData.php b/vendor/xaboy/form-builder/src/components/TreeData.php
new file mode 100755
index 0000000..46eaaf9
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/components/TreeData.php
@@ -0,0 +1,102 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\components;
+
+
+use FormBuilder\interfaces\FormComponentInterFace;
+use FormBuilder\traits\component\CallPropsTrait;
+
+/**
+ * Class TreeData
+ *
+ * @package FormBuilder\components
+ * @method $this id(String $id) Id, 必须唯一
+ * @method $this title(String $title) 标题
+ * @method $this expand(Boolean $bool) 是否展开直子节点, 默认为false
+ * @method $this disabled(Boolean $bool) 禁掉响应, 默认为false
+ * @method $this disableCheckbox(Boolean $bool) 禁掉 checkbox
+ * @method $this selected(Boolean $bool) 是否选中子节点
+ * @method $this checked(Boolean $bool) 是否勾选(如果勾选,子节点也会全部勾选)
+ */
+class TreeData implements FormComponentInterFace
+{
+    use CallPropsTrait;
+
+    /**
+     * @var array
+     */
+    protected $children = [];
+
+    /**
+     * @var array
+     */
+    protected $props = [];
+
+    /**
+     * @var array
+     */
+    protected static $propsRule = [
+        'id' => 'string',
+        'title' => 'string',
+        'expand' => 'boolean',
+        'disabled' => 'boolean',
+        'disableCheckbox' => 'boolean',
+        'selected' => 'boolean',
+        'checked' => 'boolean',
+    ];
+
+    /**
+     * TreeData constructor.
+     *
+     * @param       $id
+     * @param       $title
+     * @param array $children
+     */
+    public function __construct($id, $title, array $children = [])
+    {
+        $this->props['id'] = $id;
+        $this->props['title'] = $title;
+        $this->children = $children;
+    }
+
+    /**
+     * @param array $children
+     * @return $this
+     */
+    public function children(array $children)
+    {
+        $this->children = array_merge($this->children, $children);
+        return $this;
+    }
+
+    /**
+     * @param TreeData $child
+     * @return $this
+     */
+    public function child(TreeData $child)
+    {
+        $this->children[] = $child;
+        return $this;
+    }
+
+    /**
+     * @return array
+     */
+    public function build()
+    {
+        $children = [];
+        foreach ($this->children as $child) {
+            $children[] = $child instanceof TreeData
+                ? $child->build()
+                : $child;
+        }
+        $this->props['children'] = $children;
+        return $this->props;
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/components/Upload.php b/vendor/xaboy/form-builder/src/components/Upload.php
new file mode 100755
index 0000000..659f0e0
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/components/Upload.php
@@ -0,0 +1,156 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\components;
+
+
+use FormBuilder\FormComponentDriver;
+use FormBuilder\Helper;
+
+/**
+ * 上传组件
+ * Class Upload
+ *
+ * @package FormBuilder\components
+ * @method $this uploadType(String $uploadType) 上传文件类型,可选值为 image(图片上传),file(文件上传)
+ * @method $this action(String $action) 上传的地址
+ * @method $this multiple(Boolean $bool) 是否支持多选文件
+ * @method $this name(String $name) 上传的文件字段名
+ * @method $this accept(String $accept) 接受上传的文件类型
+ * @method $this maxSize(int $size) 文件大小限制,单位 kb
+ * @method $this withCredentials(Boolean $bool) 支持发送 cookie 凭证信息, 默认为false
+ * @method $this maxLength(Int $length) 最大上传文件数, 0为无限
+ *
+ */
+class Upload extends FormComponentDriver
+{
+    /**
+     * @var string
+     */
+    protected $name = 'upload';
+
+    /**
+     * file类型
+     */
+    const TYPE_FILE = 'file';
+
+    /**
+     * image类型
+     */
+    const TYPE_IMAGE = 'image';
+
+    /**
+     * @var array
+     */
+    protected $props = [
+        'maxLength' => 0,
+        'type' => 'select',
+        'uploadType' => self::TYPE_FILE,
+        'headers' => [],
+        'data' => [],
+        'format' => [],
+        'show-upload-list' => false
+    ];
+
+    /**
+     * @var array
+     */
+    protected static $propsRule = [
+        'uploadType' => 'string',
+        'action' => 'string',
+        'multiple' => 'boolean',
+        'name' => 'string',
+        'accept' => 'string',
+        'maxSize' => 'int',
+        'withCredentials' => 'boolean',
+        'maxLength' => 'int'
+    ];
+
+    /**
+     *
+     */
+    protected function init()
+    {
+        $this->name($this->field);
+    }
+
+    /**
+     * 设置上传的请求头部
+     *
+     * @param array $headers
+     * @return $this
+     */
+    public function headers(array $headers)
+    {
+        $this->props['headers'] = array_merge($this->props['headers'], $headers);
+        return $this;
+    }
+
+    /**
+     * 支持的文件类型,与 accept 不同的是,
+     * format 是识别文件的后缀名,accept 为 input 标签原生的 accept 属性,
+     * 会在选择文件时过滤,可以两者结合使用
+     *
+     * @param array $headers
+     * @return $this
+     */
+    public function format(array $format)
+    {
+        $this->props['format'] = array_merge($this->props['format'], $format);
+        return $this;
+    }
+
+    /**
+     * 上传时附带的额外参数
+     *
+     * @param array $headers
+     * @return $this
+     */
+    public function data(array $data)
+    {
+        $this->props['data'] = array_merge($this->props['data'], $data);
+        return $this;
+    }
+
+    public function getPlaceHolder($pre = '请上传')
+    {
+        return parent::getPlaceHolder($pre);
+    }
+
+    /**
+     * @param string|array $value
+     * @return $this
+     */
+    public function value($value)
+    {
+        $this->value = $value;
+        return $this;
+    }
+
+    protected function getValidateHandler()
+    {
+        return Validate::arr();
+    }
+
+    /**
+     * @return array
+     */
+    public function build()
+    {
+        $this->props['headers'] = (object)$this->props['headers'];
+        $this->props['data'] = (object)$this->props['data'];
+        return [
+            'type' => $this->name,
+            'field' => $this->field,
+            'title' => $this->title,
+            'value' => $this->value,
+            'props' => (object)$this->props,
+            'validate' => $this->validate,
+            'col' => $this->col
+        ];
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/components/Validate.php b/vendor/xaboy/form-builder/src/components/Validate.php
new file mode 100755
index 0000000..d624ceb
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/components/Validate.php
@@ -0,0 +1,207 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\components;
+
+use FormBuilder\interfaces\FormComponentInterFace;
+
+
+class Validate implements FormComponentInterFace
+{
+
+    const TYPE_STRING = 'string';
+
+    const TYPE_ARRAY = 'array';
+
+    const TYPE_NUMBER = 'number';
+
+    const TYPE_DATE = 'date';
+
+    const TRIGGER_CHANGE = 'change';
+
+    const TRIGGER_BLUR = 'blur';
+
+    protected $validate = [];
+
+    protected $type;
+
+    protected $trigger;
+
+    public function __construct($type, $trigger)
+    {
+        $this->type = $type;
+        $this->trigger = $trigger;
+    }
+
+    public static function str($trigger = self::TRIGGER_CHANGE)
+    {
+        return new self(self::TYPE_STRING, $trigger);
+    }
+
+    public static function arr($trigger = self::TRIGGER_CHANGE)
+    {
+        return new self(self::TYPE_ARRAY, $trigger);
+    }
+
+    public static function num($trigger = self::TRIGGER_CHANGE)
+    {
+        return new self(self::TYPE_NUMBER, $trigger);
+    }
+
+    public static function date($trigger = self::TRIGGER_CHANGE)
+    {
+        return new self(self::TYPE_DATE, $trigger);
+    }
+
+    public function set($validate, $message = null)
+    {
+        $this->validate[] = $validate + [
+                'trigger' => $this->trigger,
+                'type' => $this->type,
+                'message' => $message
+            ];
+
+        return $this;
+    }
+
+    public function fields(array $fields, $required = null, $message = null)
+    {
+        $data = [];
+        if (!is_null($required))
+            $data['required'] = $required;
+        if (is_null($message))
+            $data['message'] = $message;
+        $data['fields'] = (object)$fields;
+
+        return $this->set($data);
+    }
+
+    /**
+     * 必须为链接
+     *
+     * @param  string|null $message
+     * @return $this
+     */
+    public function url($message = null)
+    {
+        $this->set([
+            'type' => 'url'
+        ], $message);
+        return $this;
+    }
+
+    /**
+     * 必须为邮箱
+     *
+     * @param string|null $message
+     * @return $this
+     */
+    public function email($message = null)
+    {
+        $this->set([
+            'type' => 'email'
+        ], $message);
+        return $this;
+    }
+
+    /**
+     * 必填
+     *
+     * @param string|null $message
+     * @return $this
+     */
+    public function required($message = null)
+    {
+        $this->set([
+            'required' => true,
+        ], $message);
+        return $this;
+    }
+
+    /**
+     * 长度或值必须在这个范围内
+     *
+     * @param int         $min
+     * @param int         $max
+     * @param string|null $message
+     * @return $this
+     */
+    public function range($min, $max, $message = null)
+    {
+        $this->set([
+            'min' => (int)$min,
+            'max' => (int)$max,
+        ], $message);
+        return $this;
+    }
+
+    /**
+     * 长度或值必须大于这个值
+     *
+     * @param int         $min
+     * @param string|null $message
+     * @return $this
+     */
+    public function min($min, $message = null)
+    {
+        $this->set([
+            'min' => (int)$min,
+        ], $message);
+        return $this;
+    }
+
+    /**
+     * 长度或值必须小于这个值
+     *
+     * @param int         $max
+     * @param string|null $message
+     * @return $this
+     */
+    public function max($max, $message = null)
+    {
+        $this->set([
+            'max' => (int)$max,
+        ], $message);
+        return $this;
+    }
+
+    /**
+     * 长度或值必须等于这个值
+     *
+     * @param int         $length
+     * @param string|null $message
+     * @return $this
+     */
+    public function length($length, $message = null)
+    {
+        $this->set([
+            'len' => (int)$length
+        ], $message);
+        return $this;
+    }
+
+    /**
+     * 值必须在 list 中
+     *
+     * @param array       $list
+     * @param string|null $message
+     * @return $this
+     */
+    public function enum($list, $message = null)
+    {
+        $this->set([
+            'type' => 'enum',
+            'enum' => (array)$list
+        ], $message);
+        return $this;
+    }
+
+    public function build()
+    {
+        return $this->validate;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/exception/FormBuilderException.php b/vendor/xaboy/form-builder/src/exception/FormBuilderException.php
new file mode 100755
index 0000000..0b924ed
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/exception/FormBuilderException.php
@@ -0,0 +1,18 @@
+<?php
+/**
+ * PHP表单生成器
+ *
+ * @package  FormBuilder
+ * @author   xaboy <xaboy2005@qq.com>
+ * @version  2.0
+ * @license  MIT
+ * @link     https://github.com/xaboy/form-builder
+ * @document http://php.form-create.com
+ */
+
+namespace FormBuilder\Exception;
+
+class FormBuilderException extends \Exception
+{
+
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/interfaces/FormComponentInterFace.php b/vendor/xaboy/form-builder/src/interfaces/FormComponentInterFace.php
new file mode 100755
index 0000000..da8a132
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/interfaces/FormComponentInterFace.php
@@ -0,0 +1,25 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\interfaces;
+
+
+/**
+ * Interface FormComponentInterFace
+ *
+ * @package FormBuilder\interfaces
+ */
+interface FormComponentInterFace
+{
+
+    /**
+     * 获取组件的生成规则
+     *
+     * @return array
+     */
+    public function build();
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/traits/component/CallPropsTrait.php b/vendor/xaboy/form-builder/src/traits/component/CallPropsTrait.php
new file mode 100755
index 0000000..97b621f
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/traits/component/CallPropsTrait.php
@@ -0,0 +1,52 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\traits\component;
+
+
+use FormBuilder\exception\FormBuilderException;
+use FormBuilder\Helper;
+
+/**
+ * Class CallPropsTrait
+ *
+ * @package FormBuilder\traits\component
+ */
+trait CallPropsTrait
+{
+    /**
+     * 设置组件属性
+     *
+     * @param $name
+     * @param $arguments
+     * @return $this
+     * @throws FormBuilderException
+     */
+    public function __call($name, $arguments)
+    {
+        if (isset(static::$propsRule[$name])) {
+            if (static::$propsRule[$name] == '') {
+                $this->props[$name] = $arguments[0];
+            } else if (is_array(static::$propsRule[$name])) {
+                $this->props[static::$propsRule[$name][1]] = Helper::toType(
+                    $arguments[0],
+                    static::$propsRule[$name][0]
+                );
+            } else {
+                $this->props[$name] = Helper::toType(
+                    $arguments[0],
+                    static::$propsRule[$name]
+                );
+            }
+            return $this;
+        } else {
+            $this->props[$name] = $arguments[0];
+            return $this;
+//            throw new FormBuilderException($name . '方法不存在');
+        }
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/traits/component/ComponentOptionsTrait.php b/vendor/xaboy/form-builder/src/traits/component/ComponentOptionsTrait.php
new file mode 100755
index 0000000..bbd8b06
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/traits/component/ComponentOptionsTrait.php
@@ -0,0 +1,81 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\traits\component;
+
+
+use FormBuilder\components\Option;
+
+/**
+ * Class ComponentOptionsTrait
+ *
+ * @package FormBuilder\traits\component
+ */
+trait ComponentOptionsTrait
+{
+    /**
+     * @var array
+     */
+    protected $options = [];
+
+    /**
+     * 设置的选项
+     *
+     * @param      $value
+     * @param      $label
+     * @param bool $disabled
+     * @return $this
+     * @throws \FormBuilder\exception\FormBuilderException
+     */
+    public function option($value, $label, $disabled = false)
+    {
+        $this->options[] = new Option($value, $label, $disabled);
+        return $this;
+    }
+
+
+    /**
+     * 批量设置的选项
+     *
+     * @param array $options
+     * @param bool  $disabled
+     * @return $this
+     * @throws \FormBuilder\exception\FormBuilderException
+     */
+    public function options(array $options, $disabled = false)
+    {
+        $disabled = (bool)$disabled;
+        foreach ($options as $option) {
+            if ($option instanceof Option)
+                $this->options[] = $option;
+            else
+                $this->option(
+                    $option['value'],
+                    $option['label'],
+                    isset($option['disabled']) ? $option['disabled'] : $disabled
+                );
+        }
+        return $this;
+    }
+
+    /**
+     * 批量设置选项 支持匿名函数
+     *
+     * @param      $options
+     * @param bool $disabled
+     * @return $this
+     */
+    public function setOptions($options, $disabled = false)
+    {
+        if (is_callable($options))
+            return $this->setOptions($options($this), $disabled);
+        else if (is_array($options))
+            return $this->options($options, $disabled);
+        else
+            return $this;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/traits/form/FormCascaderTrait.php b/vendor/xaboy/form-builder/src/traits/form/FormCascaderTrait.php
new file mode 100755
index 0000000..1f27d73
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/traits/form/FormCascaderTrait.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\traits\form;
+
+
+use FormBuilder\components\Cascader;
+
+/**
+ * Class FormCascaderTrait
+ *
+ * @package FormBuilder\traits\form
+ */
+trait FormCascaderTrait
+{
+    /**
+     * 多级联动组件
+     *
+     * @param        $field
+     * @param        $title
+     * @param array  $value
+     * @param string $type
+     * @return Cascader
+     */
+    public static function cascader($field, $title, array $value = [], $type = Cascader::TYPE_OTHER)
+    {
+        $cascader = new Cascader($field, $title, $value);
+        $cascader->type($type);
+        return $cascader;
+    }
+
+
+    /**
+     * 省市二级联动
+     *
+     * @param              $field
+     * @param              $title
+     * @param array|string $province
+     * @param string       $city
+     * @return Cascader
+     */
+    public static function city($field, $title, $province = [], $city = '')
+    {
+        if (is_array($province))
+            $value = $province;
+        else
+            $value = [(string)$province, (string)$city];
+
+        $cascader = self::cascader($field, $title, $value, Cascader::TYPE_CITY);
+        $cascader->jsData('province_city');
+        return $cascader;
+    }
+
+
+    /**
+     * 省市区三级联动
+     *
+     * @param              $field
+     * @param              $title
+     * @param array|string $province
+     * @param string       $city
+     * @param string       $area
+     * @return Cascader
+     */
+    public static function cityArea($field, $title, $province = [], $city = '', $area = '')
+    {
+        if (is_array($province))
+            $value = $province;
+        else
+            $value = [(string)$province, (string)$city, (string)$area];
+
+        $cascader = self::cascader($field, $title, $value, Cascader::TYPE_CITY_AREA);
+        $cascader->jsData('province_city_area');
+        return $cascader;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/traits/form/FormCheckBoxTrait.php b/vendor/xaboy/form-builder/src/traits/form/FormCheckBoxTrait.php
new file mode 100755
index 0000000..fd0d4f6
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/traits/form/FormCheckBoxTrait.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\traits\form;
+
+use FormBuilder\components\Checkbox;
+
+/**
+ * Class FormCheckBoxTrait
+ *
+ * @package FormBuilder\traits\form
+ */
+trait FormCheckBoxTrait
+{
+    /**
+     * 多选框组件
+     *
+     * @param       $field
+     * @param       $title
+     * @param array $value
+     * @return Checkbox
+     */
+    public static function checkbox($field, $title, array $value = [])
+    {
+        return new Checkbox($field, $title, $value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/traits/form/FormColorPickerTrait.php b/vendor/xaboy/form-builder/src/traits/form/FormColorPickerTrait.php
new file mode 100755
index 0000000..1df3bc7
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/traits/form/FormColorPickerTrait.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\traits\form;
+
+
+use FormBuilder\components\ColorPicker;
+
+/**
+ * Class FormColorPickerTrait
+ *
+ * @package FormBuilder\traits\form
+ */
+trait FormColorPickerTrait
+{
+    /**
+     * 颜色选择组件
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $value
+     * @return ColorPicker
+     */
+    public static function color($field, $title, $value = '')
+    {
+        return new ColorPicker($field, $title, (string)$value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/traits/form/FormDatePickerTrait.php b/vendor/xaboy/form-builder/src/traits/form/FormDatePickerTrait.php
new file mode 100755
index 0000000..60d5228
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/traits/form/FormDatePickerTrait.php
@@ -0,0 +1,129 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\traits\form;
+
+
+use FormBuilder\components\DatePicker;
+
+/**
+ * Class FormDatePickerTrait
+ *
+ * @package FormBuilder\traits\form
+ */
+trait FormDatePickerTrait
+{
+    /**
+     * 日期组件
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $value
+     * @param string $type
+     * @return DatePicker
+     */
+    public static function datePicker($field, $title, $value = '', $type = DatePicker::TYPE_DATE)
+    {
+        $datePicker = new DatePicker($field, $title, $value);
+        $datePicker->type($type);
+        return $datePicker;
+    }
+
+    /**
+     * 单选日期
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $value
+     * @return DatePicker
+     */
+    public static function date($field, $title, $value = '')
+    {
+        return self::datePicker($field, $title, (string)$value, DatePicker::TYPE_DATE);
+    }
+
+    /**
+     * 多选日期
+     *
+     * @param       $field
+     * @param       $title
+     * @param array $value
+     * @return DatePicker
+     */
+    public static function dateMultiple($field, $title, array $value)
+    {
+        $date = self::datePicker($field, $title, $value, DatePicker::TYPE_DATE);
+        return $date->multiple();
+    }
+
+    /**
+     * 日期区间选择
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $startDate
+     * @param string $endDate
+     * @return DatePicker
+     */
+    public static function dateRange($field, $title, $startDate = '', $endDate = '')
+    {
+        return self::datePicker($field, $title, [(string)$startDate, (string)$endDate], DatePicker::TYPE_DATE_RANGE);
+    }
+
+    /**
+     * 单选日期时间
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $value
+     * @return DatePicker
+     */
+    public static function dateTime($field, $title, $value = '')
+    {
+        return self::datePicker($field, $title, (string)$value, DatePicker::TYPE_DATE_TIME);
+    }
+
+    /**
+     * 日期时间区间选择
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $startDate
+     * @param string $endDate
+     * @return DatePicker
+     */
+    public static function dateTimeRange($field, $title, $startDate = '', $endDate = '')
+    {
+        return self::datePicker($field, $title, [(string)$startDate, (string)$endDate], DatePicker::TYPE_DATE_TIME_RANGE);
+    }
+
+    /**
+     * 选择年
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $value
+     * @return DatePicker
+     */
+    public static function year($field, $title, $value = '')
+    {
+        return self::datePicker($field, $title, (string)$value, DatePicker::TYPE_YEAR);
+    }
+
+    /**
+     * 选择月
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $value
+     * @return DatePicker
+     */
+    public static function month($field, $title, $value = '')
+    {
+        return self::datePicker($field, $title, (string)$value, DatePicker::TYPE_MONTH);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/traits/form/FormFrameTrait.php b/vendor/xaboy/form-builder/src/traits/form/FormFrameTrait.php
new file mode 100755
index 0000000..1883b59
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/traits/form/FormFrameTrait.php
@@ -0,0 +1,127 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\traits\form;
+
+
+use FormBuilder\components\Frame;
+
+/**
+ * Class FormFrameTrait
+ *
+ * @package FormBuilder\traits\form
+ */
+trait FormFrameTrait
+{
+    /**
+     * 框架组件
+     *
+     * @param        $field
+     * @param        $title
+     * @param        $src
+     * @param string $value
+     * @param string $type
+     * @return Frame
+     */
+    public static function frame($field, $title, $src, $value = '', $type = Frame::TYPE_INPUT)
+    {
+        $length = is_array($value) ? 0 : 1;
+        $frame = new Frame($field, $title, $value);
+        $frame->maxLength($length)->src($src)->type($type);
+        return $frame;
+    }
+
+    /**
+     * 使用input  类型显示,多选
+     * value为Array类型
+     *
+     * @param       $field
+     * @param       $title
+     * @param       $src
+     * @param array $value
+     * @return Frame
+     */
+    public static function frameInputs($field, $title, $src, array $value = [])
+    {
+        return self::frame($field, $title, $src, $value);
+    }
+
+    /**
+     * 使用文件类型显示,多选
+     * value为Array类型
+     *
+     * @param       $field
+     * @param       $title
+     * @param       $src
+     * @param array $value
+     * @return Frame
+     */
+    public static function frameFiles($field, $title, $src, array $value = [])
+    {
+        return self::frame($field, $title, $src, $value, Frame::TYPE_FILE);
+    }
+
+    /**
+     * 使用文件类型显示,多选
+     * value为Array类型
+     *
+     * @param       $field
+     * @param       $title
+     * @param       $src
+     * @param array $value
+     * @return Frame
+     */
+    public static function frameImages($field, $title, $src, array $value = [])
+    {
+        return self::frame($field, $title, $src, $value, Frame::TYPE_IMAGE);
+    }
+
+    /**
+     * 使用input  类型显示,单选
+     * value为String类型
+     *
+     * @param        $field
+     * @param        $title
+     * @param        $src
+     * @param string $value
+     * @return Frame
+     */
+    public static function frameInputOne($field, $title, $src, $value = '')
+    {
+        return self::frame($field, $title, $src, (string)$value);
+    }
+
+    /**
+     * 使用文件类型显示,单选
+     * value为String类型
+     *
+     * @param        $field
+     * @param        $title
+     * @param        $src
+     * @param string $value
+     * @return Frame
+     */
+    public static function frameFileOne($field, $title, $src, $value = '')
+    {
+        return self::frame($field, $title, $src, (string)$value, Frame::TYPE_FILE);
+    }
+
+    /**
+     * 使用文件类型显示,单选
+     * value为String类型
+     *
+     * @param        $field
+     * @param        $title
+     * @param        $src
+     * @param string $value
+     * @return Frame
+     */
+    public static function frameImageOne($field, $title, $src, $value = '')
+    {
+        return self::frame($field, $title, $src, (string)$value, Frame::TYPE_IMAGE);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/traits/form/FormHiddenTrait.php b/vendor/xaboy/form-builder/src/traits/form/FormHiddenTrait.php
new file mode 100755
index 0000000..f02ccd7
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/traits/form/FormHiddenTrait.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\traits\form;
+
+
+use FormBuilder\components\Hidden;
+
+/**
+ * Class FormHiddenTrait
+ *
+ * @package FormBuilder\traits\form
+ */
+trait FormHiddenTrait
+{
+    /**
+     * 隐藏组件
+     *
+     * @param $field
+     * @param $value
+     * @return Hidden
+     */
+    public static function hidden($field, $value)
+    {
+        return new Hidden($field, $value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/traits/form/FormInputNumberTrait.php b/vendor/xaboy/form-builder/src/traits/form/FormInputNumberTrait.php
new file mode 100755
index 0000000..112785a
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/traits/form/FormInputNumberTrait.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\traits\form;
+
+
+use FormBuilder\components\InputNumber;
+
+/**
+ * Class FormInputNumberTrait
+ *
+ * @package FormBuilder\traits\form
+ */
+trait FormInputNumberTrait
+{
+
+    /**
+     * 数字输入框组件
+     *
+     * @param      $field
+     * @param      $title
+     * @param null $value
+     * @return InputNumber
+     */
+    public static function number($field, $title, $value = null)
+    {
+        return new InputNumber($field, $title, $value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/traits/form/FormInputTrait.php b/vendor/xaboy/form-builder/src/traits/form/FormInputTrait.php
new file mode 100755
index 0000000..5ba0027
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/traits/form/FormInputTrait.php
@@ -0,0 +1,113 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\traits\form;
+
+
+use FormBuilder\components\Input;
+
+/**
+ * Class FormInputTrait
+ *
+ * @package FormBuilder\traits\form
+ */
+trait FormInputTrait
+{
+    /**
+     * input输入框组件
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $value
+     * @param string $type
+     * @return Input
+     */
+    public static function input($field, $title, $value = '', $type = Input::TYPE_TEXT)
+    {
+        $input = new Input($field, $title, (string)$value);
+        $input->type($type);
+        return $input;
+    }
+
+    /**
+     * text 类型输入框
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $value
+     * @return Input
+     */
+    public static function text($field, $title, $value = '')
+    {
+        return self::input($field, $title, $value);
+    }
+
+    /**
+     * password 类型输入框
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $value
+     * @return Input
+     */
+    public static function password($field, $title, $value = '')
+    {
+        return self::input($field, $title, $value, Input::TYPE_PASSWORD);
+    }
+
+    /**
+     * textarea 类型输入框
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $value
+     * @return Input
+     */
+    public static function textarea($field, $title, $value = '')
+    {
+        return self::input($field, $title, $value, Input::TYPE_TEXTAREA);
+    }
+
+    /**
+     * url 类型输入框
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $value
+     * @return Input
+     */
+    public static function url($field, $title, $value = '')
+    {
+        return self::input($field, $title, $value, Input::TYPE_URL);
+    }
+
+    /**
+     * email 类型输入框
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $value
+     * @return Input
+     */
+    public static function email($field, $title, $value = '')
+    {
+        return self::input($field, $title, $value, Input::TYPE_EMAIL);
+    }
+
+    /**
+     * date 类型输入框
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $value
+     * @return Input
+     */
+    public static function idate($field, $title, $value = '')
+    {
+        return self::input($field, $title, $value, Input::TYPE_DATE);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/traits/form/FormOptionTrait.php b/vendor/xaboy/form-builder/src/traits/form/FormOptionTrait.php
new file mode 100755
index 0000000..aa229fd
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/traits/form/FormOptionTrait.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\traits\form;
+
+
+use FormBuilder\components\Option;
+
+/**
+ * Class FormOptionTrait
+ *
+ * @package FormBuilder\traits\form
+ */
+trait FormOptionTrait
+{
+    /**
+     * 获取选择类组件 option 类
+     *
+     * @param        $value
+     * @param string $label
+     * @param bool   $disabled
+     * @return Option
+     * @throws \FormBuilder\exception\FormBuilderException
+     */
+    public static function option($value, $label = '', $disabled = false)
+    {
+        return new Option($value, $label, $disabled);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/traits/form/FormRadioTrait.php b/vendor/xaboy/form-builder/src/traits/form/FormRadioTrait.php
new file mode 100755
index 0000000..8a8d19f
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/traits/form/FormRadioTrait.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\traits\form;
+
+
+use FormBuilder\components\Radio;
+
+/**
+ * Class FormRadioTrait
+ *
+ * @package FormBuilder\traits\form
+ */
+trait FormRadioTrait
+{
+    /**
+     * 单选框组件
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $value
+     * @return Radio
+     */
+    public static function radio($field, $title, $value = '')
+    {
+        return new Radio($field, $title, (string)$value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/traits/form/FormRateTrait.php b/vendor/xaboy/form-builder/src/traits/form/FormRateTrait.php
new file mode 100755
index 0000000..1b627ac
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/traits/form/FormRateTrait.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\traits\form;
+
+
+use FormBuilder\components\Rate;
+
+/**
+ * Class FormRateTrait
+ *
+ * @package FormBuilder\traits\form
+ */
+trait FormRateTrait
+{
+    /**
+     * 评分选择组件
+     *
+     * @param        $field
+     * @param        $title
+     * @param  float $value
+     * @return Rate
+     */
+    public static function rate($field, $title, $value = 0.0)
+    {
+        return new Rate($field, $title, (float)$value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/traits/form/FormSelectTrait.php b/vendor/xaboy/form-builder/src/traits/form/FormSelectTrait.php
new file mode 100755
index 0000000..3e77bdc
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/traits/form/FormSelectTrait.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\traits\form;
+
+
+use FormBuilder\components\Select;
+
+/**
+ * Class FormSelectTrait
+ *
+ * @package FormBuilder\traits\form
+ */
+trait FormSelectTrait
+{
+    /**
+     * 下拉选择组件
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $value
+     * @return Select
+     */
+    public static function select($field, $title, $value = '')
+    {
+        $multiple = is_array($value) ? true : false;
+        $select = new Select($field, $title, $value);
+        $select->multiple($multiple);
+        return $select;
+    }
+
+    /**
+     * 多选
+     *
+     * @param       $field
+     * @param       $title
+     * @param array $value
+     * @return Select
+     */
+    public static function selectMultiple($field, $title, array $value = [])
+    {
+        return self::select($field, $title, $value);
+    }
+
+    /**
+     * 单选
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $value
+     * @return Select
+     */
+    public static function selectOne($field, $title, $value = '')
+    {
+        return self::select($field, $title, (string)$value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/traits/form/FormSliderTrait.php b/vendor/xaboy/form-builder/src/traits/form/FormSliderTrait.php
new file mode 100755
index 0000000..1ee17e2
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/traits/form/FormSliderTrait.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\traits\form;
+
+
+use FormBuilder\components\Slider;
+
+/**
+ * Class FormSliderTrait
+ *
+ * @package FormBuilder\traits\form
+ */
+trait FormSliderTrait
+{
+    /**
+     * 滑块组件
+     *
+     * @param     $field
+     * @param     $title
+     * @param int $value
+     * @return Slider
+     */
+    public static function slider($field, $title, $value = 0)
+    {
+        return new Slider($field, $title, $value);
+    }
+
+    /**
+     * 区间选择
+     *
+     * @param     $field
+     * @param     $title
+     * @param int $start
+     * @param int $end
+     * @return Slider
+     */
+    public static function sliderRange($field, $title, $start = 0, $end = 0)
+    {
+        $slider = self::slider($field, $title, [(int)$start, (int)$end]);
+        $slider->range(true);
+        return $slider;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/traits/form/FormStyleTrait.php b/vendor/xaboy/form-builder/src/traits/form/FormStyleTrait.php
new file mode 100755
index 0000000..a6226c3
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/traits/form/FormStyleTrait.php
@@ -0,0 +1,63 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\traits\form;
+
+
+use FormBuilder\components\Col;
+use FormBuilder\components\FormStyle;
+use FormBuilder\components\Row;
+
+/**
+ * Class FormStyleTrait
+ *
+ * @package FormBuilder\traits\form
+ */
+trait FormStyleTrait
+{
+
+    /**
+     * 组件布局规则类
+     *
+     * @param int $span
+     * @return Col
+     */
+    public static function col($span = 24)
+    {
+        return new Col($span);
+    }
+
+    /**
+     * 表格布局规则类
+     *
+     * @param int    $gutter
+     * @param string $type
+     * @param string $align
+     * @param string $justify
+     * @param string $className
+     * @return Row
+     */
+    public static function row($gutter = 0, $type = '', $align = '', $justify = '', $className = '')
+    {
+        return new Row($gutter, $type, $align, $justify, $className);
+    }
+
+    /**
+     * 表格样式类
+     *
+     * @param bool   $inline
+     * @param string $labelPosition
+     * @param int    $labelWidth
+     * @param bool   $showMessage
+     * @param string $autocomplete
+     * @return FormStyle
+     */
+    public static function style($inline = false, $labelPosition = 'right', $labelWidth = 125, $showMessage = true, $autocomplete = 'off')
+    {
+        return new FormStyle($inline, $labelPosition, $labelWidth, $showMessage, $autocomplete);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/traits/form/FormSwitchesTrait.php b/vendor/xaboy/form-builder/src/traits/form/FormSwitchesTrait.php
new file mode 100755
index 0000000..254fb2b
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/traits/form/FormSwitchesTrait.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\traits\form;
+
+
+use FormBuilder\components\Switches;
+
+/**
+ * Class FormSwitchesTrait
+ *
+ * @package FormBuilder\traits\form
+ */
+trait FormSwitchesTrait
+{
+    /**
+     * 开关组件
+     *
+     * @param        $field
+     * @param        $title
+     * @param string $value
+     * @return Switches
+     */
+    public static function switches($field, $title, $value = '0')
+    {
+        return new Switches($field, $title, $value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/traits/form/FormTimePickerTrait.php b/vendor/xaboy/form-builder/src/traits/form/FormTimePickerTrait.php
new file mode 100755
index 0000000..bfb2d59
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/traits/form/FormTimePickerTrait.php
@@ -0,0 +1,60 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\traits\form;
+
+
+use FormBuilder\components\TimePicker;
+
+/**
+ * Class FormTimePickerTrait
+ *
+ * @package FormBuilder\traits\form
+ */
+trait FormTimePickerTrait
+{
+    /**
+     * 时间选择组件
+     *
+     * @param            $field
+     * @param            $title
+     * @param string|int $value
+     * @param string     $type
+     * @return TimePicker
+     */
+    public static function timePicker($field, $title, $value = '', $type = TimePicker::TYPE_TIME)
+    {
+        return (new TimePicker($field, $title, $value))->type($type);
+    }
+
+    /**
+     * 事件选择
+     *
+     * @param            $field
+     * @param            $title
+     * @param string|int $value
+     * @return TimePicker
+     */
+    public static function time($field, $title, $value = '')
+    {
+        return self::timePicker($field, $title, (string)$value);
+    }
+
+    /**
+     * 时间区间选择
+     *
+     * @param            $field
+     * @param            $title
+     * @param string|int $startTime
+     * @param string|int $endTime
+     * @return TimePicker
+     */
+    public static function timeRange($field, $title, $startTime = '', $endTime = '')
+    {
+        return self::timePicker($field, $title, [(string)$startTime, (string)$endTime], TimePicker::TYPE_TIME_RANGE);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/traits/form/FormTreeTrait.php b/vendor/xaboy/form-builder/src/traits/form/FormTreeTrait.php
new file mode 100755
index 0000000..c81affd
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/traits/form/FormTreeTrait.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\traits\form;
+
+
+use FormBuilder\components\Tree;
+use FormBuilder\components\TreeData;
+
+/**
+ * Class FormTreeTrait
+ *
+ * @package FormBuilder\traits\form
+ */
+trait FormTreeTrait
+{
+    /**
+     * 树形组件
+     *
+     * @param        $field
+     * @param        $title
+     * @param array  $value
+     * @param string $type
+     * @return Tree
+     */
+    public static function tree($field, $title, $value = [], $type = Tree::TYPE_CHECKED)
+    {
+        return (new Tree($field, $title, $value))->type($type);
+    }
+
+    /**
+     * 获取选中的值
+     *
+     * @param       $field
+     * @param       $title
+     * @param array $value
+     * @return Tree
+     */
+    public static function treeSelected($field, $title, $value = [])
+    {
+        return self::tree($field, $title, $value, Tree::TYPE_SELECTED);
+    }
+
+    /**
+     * 获取勾选的值
+     *
+     * @param       $field
+     * @param       $title
+     * @param array $value
+     * @return Tree
+     */
+    public static function treeChecked($field, $title, $value = [])
+    {
+        return self::tree($field, $title, $value)->showCheckbox(true);
+    }
+
+    /**
+     * 树形组件数据 date 类
+     *
+     * @param       $id
+     * @param       $title
+     * @param array $children
+     * @return TreeData
+     */
+    public static function treeData($id, $title, array $children = [])
+    {
+        return new TreeData($id, $title, $children);
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/traits/form/FormUploadTrait.php b/vendor/xaboy/form-builder/src/traits/form/FormUploadTrait.php
new file mode 100755
index 0000000..9c50e2f
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/traits/form/FormUploadTrait.php
@@ -0,0 +1,103 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\traits\form;
+
+
+use FormBuilder\components\Upload;
+
+/**
+ * Class FormUploadTrait
+ *
+ * @package FormBuilder\traits\form
+ */
+trait FormUploadTrait
+{
+    /**
+     * 上传组件
+     *
+     * @param        $field
+     * @param        $title
+     * @param        $action
+     * @param string $value
+     * @param string $type
+     * @return Upload
+     */
+    public static function upload($field, $title, $action, $value = '', $type = Upload::TYPE_FILE)
+    {
+        $upload = new Upload($field, $title, $value);
+        $upload->uploadType($type)->action($action);
+        return $upload;
+    }
+
+    /**
+     * 图片上传
+     * value 为 Array类型
+     *
+     * @param       $field
+     * @param       $title
+     * @param       $action
+     * @param array $value
+     * @return Upload
+     */
+    public static function uploadImages($field, $title, $action, array $value = [])
+    {
+        $upload = self::upload($field, $title, $action, $value, Upload::TYPE_IMAGE);
+        $upload->format(['jpg', 'jpeg', 'png', 'gif'])->accept('image/*');
+        return $upload;
+
+    }
+
+    /**
+     * 文件上传
+     * value 为 Array类型
+     *
+     * @param       $field
+     * @param       $title
+     * @param       $action
+     * @param array $value
+     * @return Upload
+     */
+    public static function uploadFiles($field, $title, $action, array $value = [])
+    {
+        return self::upload($field, $title, $action, $value, Upload::TYPE_FILE);
+    }
+
+    /**
+     * 图片上传
+     * value 为  String类型
+     *
+     * @param        $field
+     * @param        $title
+     * @param        $action
+     * @param string $value
+     * @return Upload
+     */
+    public static function uploadImageOne($field, $title, $action, $value = '')
+    {
+        $upload = self::upload($field, $title, $action, $value, Upload::TYPE_IMAGE);
+        $upload->format(['jpg', 'jpeg', 'png', 'gif'])->accept('image/*')->maxLength(1);
+        return $upload;
+    }
+
+    /**
+     * 文件上传
+     * value 为  String类型
+     *
+     * @param        $field
+     * @param        $title
+     * @param        $action
+     * @param string $value
+     * @return Upload
+     */
+    public static function uploadFileOne($field, $title, $action, $value = '')
+    {
+        $upload = self::upload($field, $title, $action, (string)$value, Upload::TYPE_FILE);
+        $upload->maxLength(1);
+        return $upload;
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/traits/form/FormValidateTrait.php b/vendor/xaboy/form-builder/src/traits/form/FormValidateTrait.php
new file mode 100755
index 0000000..8efa26e
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/traits/form/FormValidateTrait.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * FormBuilder表单生成器
+ * Author: xaboy
+ * Github: https://github.com/xaboy/form-builder
+ */
+
+namespace FormBuilder\traits\form;
+
+
+use FormBuilder\components\Validate;
+
+trait FormValidateTrait
+{
+    /**
+     * string 类型验证器
+     *
+     * @return Validate
+     */
+    public static function validateStr()
+    {
+        return Validate::str();
+    }
+
+    /**
+     * input 组件验证器
+     *
+     * @return Validate
+     */
+    public static function validateInput()
+    {
+        return Validate::str(Validate::TRIGGER_BLUR);
+    }
+
+    /**
+     * array 类型验证器
+     *
+     * @return Validate
+     */
+    public static function validateArr()
+    {
+        return Validate::arr();
+    }
+
+    /**
+     * number 类型验证器
+     *
+     * @return Validate
+     */
+    public static function validateNum()
+    {
+        return Validate::num();
+    }
+
+    /**
+     * inputNumber 组件验证器
+     *
+     * @return Validate
+     */
+    public static function validateNumInput()
+    {
+        return Validate::num(Validate::TRIGGER_BLUR);
+    }
+
+    /**
+     * date 类型验证器
+     *
+     * @return Validate
+     */
+    public static function validateDate()
+    {
+        return Validate::date();
+    }
+
+    /**
+     * frame 组件验证器
+     *
+     * @return Validate
+     */
+    public static function validateFrame()
+    {
+        return self::ValidateArr();
+    }
+}
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/view/form.php b/vendor/xaboy/form-builder/src/view/form.php
new file mode 100755
index 0000000..c7c5553
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/view/form.php
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+	<meta charset="UTF-8">
+	<title><?=$form->getTitle()?></title>
+	<?=implode("\r\n",$form->getScript())?>
+	<?=$form->getSuccessScript()?>
+</head>
+<body>
+<script>
+	(function () {
+		var create = <?=$form->formScript()?>
+        create();
+    })();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/vendor/xaboy/form-builder/src/view/formScript.php b/vendor/xaboy/form-builder/src/view/formScript.php
new file mode 100755
index 0000000..accff2b
--- /dev/null
+++ b/vendor/xaboy/form-builder/src/view/formScript.php
@@ -0,0 +1,72 @@
+(function () {
+    var getRule = function () {
+        var rule = <?=json_encode($form->getRules())?>;
+        rule.forEach(function (c) {
+            if ((c.type == 'cascader' || c.type == 'tree') && Object.prototype.toString.call(c.props.data) == '[object String]') {
+                if (c.props.data.indexOf('js.') === 0) {
+                    c.props.data = window[c.props.data.replace('js.', '')];
+                }
+            }
+        });
+        return rule;
+    }, vm = new Vue,name = 'formBuilderExec<?= !$form->getId() ? '' : '_'.$form->getId() ?>';
+	var _b = false;
+    window[name] =  function create(el, callback) {
+		if(_b) return ;
+		_b = true;
+        if (!el) el = document.body;
+        var $f = formCreate.create(getRule(), {
+            el: el,
+            form:<?=json_encode($form->getConfig('form'))?>,
+            row:<?=json_encode($form->getConfig('row'))?>,
+			submitBtn:<?=$form->isSubmitBtn() ? '{}' : 'false'?>,
+			resetBtn:<?=$form->isResetBtn() ? 'true' : '{}'?>,
+            upload: {
+                onExceededSize: function (file) {
+                    vm.$Message.error(file.name + '超出指定大小限制');
+                },
+                onFormatError: function () {
+                    vm.$Message.error(file.name + '格式验证失败');
+                },
+                onError: function (error) {
+                    vm.$Message.error(file.name + '上传失败,(' + error + ')');
+                },
+                onSuccess: function (res) {
+                    if (res.code == 200) {
+                        return res.data.filePath;
+                    } else {
+                        vm.$Message.error(res.msg);
+                    }
+                }
+            },
+            //表单提交事件
+            onSubmit: function (formData) {
+                $f.submitStatus({loading: true});
+                $.ajax({
+                    url: '<?=$form->getAction()?>',
+                    type: '<?=$form->getMethod()?>',
+                    dataType: 'json',
+                    data: formData,
+                    success: function (res) {
+                        if (res.code == 200) {
+                            vm.$Message.success(res.msg);
+                            callback && callback(0, res, $f, formData);
+                            //TODO 表单提交成功!
+                        } else {
+                            vm.$Message.error(res.msg || '表单提交失败');
+                            $f.btn.finish();
+                            callback && callback(1, res, $f, formData);
+                            //TODO 表单提交失败
+                        }
+                    },
+                    error: function () {
+                        vm.$Message.error('表单提交失败');
+                        $f.btn.finish();
+                    }
+                });
+            }
+        });
+        return $f;
+    };
+    return window[name];
+}());
diff --git a/workerman.bat b/workerman.bat
new file mode 100755
index 0000000..d5ffc05
--- /dev/null
+++ b/workerman.bat
@@ -0,0 +1,11 @@
+chcp 65001
+start "Chat Serve" /b php think workerman start chat
+start "Admin Serve" /b php think workerman start admin
+start "Time Task" /b php think timer start
+php think workerman start channel
+
+
+
+
+
+

--
Gitblit v1.10.0